Skip to content

Add VSCode extension for MariaDB Profiler Viewer#16

Merged
39ff merged 8 commits into
mainfrom
claude/plan-vscode-extension-K67WU
Feb 18, 2026
Merged

Add VSCode extension for MariaDB Profiler Viewer#16
39ff merged 8 commits into
mainfrom
claude/plan-vscode-extension-K67WU

Conversation

@39ff

@39ff 39ff commented Feb 18, 2026

Copy link
Copy Markdown
Owner

Summary

This PR introduces a complete VSCode extension implementation for visualizing and analyzing MariaDB/MySQL query profiling data generated by php-ext-mariadb-salvage. The extension provides feature parity with the existing JetBrains plugin but uses VSCode's native API exclusively (no Webview) for a lightweight, fast, and theme-integrated experience.

Key Changes

  • Extension Architecture: Complete TypeScript-based VSCode extension with modular service and provider layers

    • LogParserService: JSONL parser with offset-based incremental reading for efficient log handling
    • JobManagerService: Job lifecycle management and CLI integration
    • StatisticsService: Query statistics computation (by type, table, tag)
    • FileWatcherService: Polling-based file monitoring (Docker-compatible)
    • FrameResolverService: JavaScript-based backtrace frame resolution
  • UI Components: Three native TreeView providers (no Webview)

    • JobTreeProvider: Job list with active/completed status
    • QueryTreeProvider: Hierarchical query display with expandable details (tables, tags, backtrace)
    • StatisticsTreeProvider: Unicode bar chart statistics dashboard
    • QueryDocumentProvider: Virtual SQL documents with syntax highlighting
  • Commands & Interactions: 11 registered commands via command palette and TreeView menus

    • Job control: start/stop profiling
    • Filtering: by query type, tag, and text search (QuickPick-based)
    • Live tail: real-time query streaming to OutputChannel
    • Navigation: backtrace frame opening in editor
  • Configuration: VSCode Settings integration for

    • Log directory path
    • PHP executable and CLI script paths
    • Display limits and refresh intervals
    • Docker path mappings
    • Custom frame resolver scripts
  • Testing: Comprehensive unit test suite using Vitest

    • QueryEntry model tests (type detection, parameter binding, SQL formatting)
    • LogParserService tests (JSONL parsing, offset handling)
    • JobManagerService tests (jobs.json parsing, CLI execution)
    • StatisticsService tests (aggregation by type/table/tag)
    • JobInfo model tests (duration formatting)
  • Build & Packaging

    • esbuild configuration for fast bundling
    • TypeScript strict mode compilation
    • VSCode marketplace-ready manifest (package.json)
    • .vscodeignore for clean distribution

Implementation Details

  • No Webview dependency: Uses only VSCode TreeView, OutputChannel, and Virtual Documents APIs for minimal memory footprint and automatic theme integration
  • Offset-based log reading: Efficiently handles large JSONL files by tracking byte offsets
  • Polling file watcher: Compatible with Docker volumes and network filesystems
  • Docker path mapping: Configurable container-to-local path translation for backtrace navigation
  • Extensible frame resolution: JavaScript sandbox for custom backtrace filtering logic
  • Live tail buffering: Configurable output channel buffer for real-time query monitoring

https://claude.ai/code/session_01EabgSSMXSA25Pjf1aTEmB2

Summary by CodeRabbit

  • 新機能

    • VSCode拡張「MariaDB Profiler Viewer」を追加(ジョブ一覧・クエリツリー・統計ビュー、仮想SQL表示、バックトレース閲覧)
    • ジョブの開始/停止、ログの開放読み込み、ライブテール出力
    • クエリのタイプ/タグフィルタ、テキスト検索、テーブル/パラメータ表示
    • パスマッピングや詳細設定による環境適応
  • テスト

    • ユニットテスト一式を追加(ジョブ情報、ログ解析、クエリ処理、統計)
  • ドキュメント

    • 拡張設計仕様書を追加(導入/設定/機能概要)
  • Chores

    • ビルド・パッケージ設定、CIワークフロー、パッケージ配布用ignore/gitattributesを追加

Detailed plan for a VSCode extension equivalent to the existing JetBrains
plugin (jetbrains-plugin/). Covers feature mapping, tech stack, directory
structure, data models, services, UI layout, communication protocol,
implementation phases, and risk analysis.

https://claude.ai/code/session_01EabgSSMXSA25Pjf1aTEmB2
Replace all Webview-based UI with VSCode native APIs:
- QueryLogTable (Webview) -> QueryTreeProvider (TreeView with expandable details)
- StatisticsView (Webview) -> StatisticsTreeProvider (TreeView with Unicode bar charts)
- SQL detail (Webview) -> QueryDocumentProvider (Virtual Document .sql with syntax highlighting)
- LiveTailView (Webview) -> OutputChannel
- FilterBar (Webview) -> QuickPick commands + toolbar buttons

Benefits: zero dependencies, no HTML/CSS/JS build pipeline, lighter memory
footprint, full theme integration, better Remote SSH/WSL stability.

https://claude.ai/code/session_01EabgSSMXSA25Pjf1aTEmB2
All UI uses VSCode native APIs only - no Webview, no HTML/CSS/JS:
- TreeView for jobs list, query log (expandable details), and statistics
- Virtual Document (.sql) for full SQL display with syntax highlighting
- OutputChannel for live tail streaming
- QuickPick for filter/search interactions

Structure:
- src/model/: QueryEntry, JobInfo (matching PHP JSONL/JSON formats)
- src/service/: LogParser, JobManager, Statistics, FileWatcher, FrameResolver
- src/provider/: JobTree, QueryTree, StatisticsTree, QueryDocument
- src/command/: startJob, stopJob, openLog, filter, search, liveTail
- test/unit/: 56 tests passing (QueryEntry, JobInfo, Statistics, LogParser)

TypeScript compiles cleanly, esbuild production bundle works.

https://claude.ai/code/session_01EabgSSMXSA25Pjf1aTEmB2
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Feb 18, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@39ff has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 1 minutes and 5 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

Walkthrough

MariaDB Profiler Viewer の VSCode 拡張を追加します。設計仕様書、ビルド/テスト設定、拡張エントリポイント、ジョブ/クエリ用データモデル、ログ解析・ジョブ管理・ファイル監視・フレーム解決・統計の各サービス、ツリービュー・ドキュメントプロバイダ、フィルタ/Search/ライブテイル等のコマンド、ユーティリティ、ユニットテストを導入します。

Changes

Cohort / File(s) Summary
Design & Packaging
PLAN_VSCODE_EXTENSION.md, vscode-extension/.gitignore, vscode-extension/.vscodeignore, vscode-extension/esbuild.mjs, vscode-extension/tsconfig.json, vscode-extension/vitest.config.ts, .github/workflows/vscode-build.yml, .github/workflows/release.yml
VSCode 拡張の設計/spec、ビルド・パッケージング・CI ワークフロー、esbuild スクリプト、TypeScript/Vitest 設定とパッケージ除外ルールを追加。
Extension Manifest
vscode-extension/package.json
拡張の package.json を追加(ビュー、コマンド、設定、スクリプト、devDependencies を定義)。
Extension Bootstrap
vscode-extension/src/extension.ts
activate/deactivate 実装。サービス初期化、ツリービュー登録、コマンド登録、ジョブ/クエリの読み込み・更新ロジック、ライブ更新タイマー等を配線。
Commands
vscode-extension/src/command/...
filterQueries.ts, searchQueries.ts, liveTail.ts, startJob.ts, stopJob.ts, openLog.ts
フィルタ(型/タグ/クリア)、検索、ライブテイル管理(LiveTailManager + OutputChannel)、ジョブ開始/停止、ログファイル選択コマンドを追加。
Models
vscode-extension/src/model/...
JobInfo.ts, QueryEntry.ts
ジョブ/クエリの型定義とユーティリティ関数(ジョブ JSON 正規化、クエリ型判定、パラメータバインド、テーブル抽出、タイムスタンプ整形など)。
Services
vscode-extension/src/service/...
JobManagerService.ts, LogParserService.ts, FileWatcherService.ts, FrameResolverService.ts, StatisticsService.ts
ジョブ管理(CLI 実行含む)、JSONL パースとオフセット読み取り、ポーリングベースのファイル監視、ユーザースクリプトによるフレーム解決(VM 実行、キャッシュ)、統計集計を実装。
Providers / UI
vscode-extension/src/provider/...
JobTreeProvider.ts, QueryTreeProvider.ts, StatisticsTreeProvider.ts, QueryDocumentProvider.ts
ツリービュー実装(ジョブ、クエリ、統計)、仮想ドキュメントでのクエリ詳細表示、バックトレース開口・ファイルマッピング対応。
Utilities
vscode-extension/src/util/...
pathMapping.ts, queryUtils.ts
パスマッピング(コンテナ→ホスト)、統計用バー生成・割合整形・キー短縮ユーティリティを追加。
Tests
vscode-extension/test/unit/...
JobInfo.test.ts, QueryEntry.test.ts, LogParserService.test.ts, StatisticsService.test.ts
JobInfo、QueryEntry、LogParser、Statistics のユニットテストを追加(Vitest 使用、ファイルベースのシナリオ含む)。

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant VSCode as "VSCode Extension"
    participant JobMgr as "JobManagerService"
    participant LogParser as "LogParserService"
    participant QueryTree as "QueryTreeProvider"
    participant Stats as "StatisticsService"

    User->>VSCode: ジョブ読み込み/リフレッシュ要求
    VSCode->>JobMgr: loadJobs()
    JobMgr-->>VSCode: JobInfo[]
    VSCode->>QueryTree: refresh(entries)
    VSCode->>Stats: computeStats(entries)
    Stats-->>VSCode: QueryStats
    QueryTree-->>User: ツリービュー表示
Loading
sequenceDiagram
    actor User
    participant VSCode as "VSCode Extension"
    participant LiveTail as "LiveTailManager"
    participant FileWatch as "FileWatcherService"
    participant LogParser as "LogParserService"
    participant Output as "OutputChannel"

    User->>VSCode: ライブテイル開始(jobKey)
    VSCode->>LiveTail: start(jobKey)
    LiveTail->>LogParser: parseJsonlFile(path)
    LogParser-->>LiveTail: 既存エントリ
    LiveTail->>FileWatch: watchFile(jsonlPath)
    FileWatch-->>LiveTail: onChange()
    LiveTail->>LogParser: parseJsonlFileFromOffset(path, offset)
    LogParser-->>LiveTail: 新規エントリ
    LiveTail->>Output: フォーマットして出力
    Output-->>User: 表示
Loading
sequenceDiagram
    actor User
    participant VSCode as "VSCode Extension"
    participant Quick as "QuickPick"
    participant QueryTree as "QueryTreeProvider"
    participant Context as "VSCode Context"

    User->>VSCode: 型でフィルタ選択
    VSCode->>QueryTree: getAllTypes()
    QueryTree-->>VSCode: タイプ一覧+件数
    VSCode->>Quick: Show QuickPick
    Quick-->>User: 選択表示
    User->>Quick: 選択
    Quick->>VSCode: 選択結果
    VSCode->>QueryTree: setFilter(type)
    QueryTree->>Context: setContext(hasFilter)
    QueryTree-->>User: ツリー更新
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

no_run

Poem

🐇 小さな穴から覗けばコードの野原、
クエリが跳ねてジョブが歌う、
ログはそっと追いかけられ、出力は光る、
木の上のビューでフィルタをかけ、
うさぎは喜ぶ、ネイティブで描いた夜。

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.63% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding a VSCode extension for MariaDB Profiler Viewer. It accurately reflects the primary objective of the PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/plan-vscode-extension-K67WU

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Runs on pushes/PRs that touch vscode-extension/:
- TypeScript type check (tsc --noEmit)
- Unit tests (vitest)
- Production build (esbuild)

Mirrors the existing plugin-build.yml pattern for JetBrains plugin.

https://claude.ai/code/session_01EabgSSMXSA25Pjf1aTEmB2
- New build-vscode job: npm ci, tsc, vitest, esbuild, vsce package
- Produces .vsix artifact included in GitHub Release assets
- Release job now depends on build-vscode alongside existing jobs

https://claude.ai/code/session_01EabgSSMXSA25Pjf1aTEmB2

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 20

🧹 Nitpick comments (16)
vscode-extension/src/command/filterQueries.ts (1)

5-8: 3関数すべてで context: vscode.ExtensionContext パラメータが未使用

registerFilterByTypeCommandregisterFilterByTagCommandregisterClearFilterCommand の引数 context はどの関数本体でも参照されていません。Disposable を返す設計なので呼び出し側でライフサイクル管理を行うべきですが、そうであれば context を受け取る必要はありません。パラメータを削除するか、関数内で context.subscriptions.push(...) を行うかに統一することを推奨します。

♻️ リファクタ案(パラメータ削除)
 export function registerFilterByTypeCommand(
-  context: vscode.ExtensionContext,
   queryTreeProvider: QueryTreeProvider,
 ): vscode.Disposable {

 export function registerFilterByTagCommand(
-  context: vscode.ExtensionContext,
   queryTreeProvider: QueryTreeProvider,
 ): vscode.Disposable {

 export function registerClearFilterCommand(
-  context: vscode.ExtensionContext,
   queryTreeProvider: QueryTreeProvider,
 ): vscode.Disposable {

Also applies to: 43-46, 83-85

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vscode-extension/src/command/filterQueries.ts` around lines 5 - 8, The three
exported functions registerFilterByTypeCommand, registerFilterByTagCommand, and
registerClearFilterCommand take an unused context: vscode.ExtensionContext
parameter; remove that parameter from each function signature and any internal
references, update any call sites to stop passing context, and keep each
function returning a vscode.Disposable as before; alternatively, if you prefer
lifecycle registration inside these functions, instead accept context and call
context.subscriptions.push(theDisposable) inside each function—pick one approach
and apply it consistently across the three functions and their callers.
vscode-extension/test/unit/QueryEntry.test.ts (1)

81-146: シングルクォートを含むパラメータ値のテストケース追加を推奨

getBoundQuery のテスト群は充実していますが、パラメータ値にシングルクォートが含まれる場合(例: "O'Brien")のテストが不足しています。これは src/model/QueryEntry.ts の実装上の問題(後述)と対になるケースです。

➕ 追加テスト案
+  it('should escape single quotes in parameter values', () => {
+    const entry: QueryEntry = {
+      jobKey: '', query: 'SELECT * FROM users WHERE name = ?', timestamp: 0,
+      params: ["O'Brien"],
+    };
+    expect(getBoundQuery(entry)).toBe("SELECT * FROM users WHERE name = 'O''Brien'");
+  });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vscode-extension/test/unit/QueryEntry.test.ts` around lines 81 - 146, Add a
unit test to cover parameters containing single quotes (e.g., "O'Brien") to
ensure getBoundQuery properly escapes or formats such values; update
vscode-extension/test/unit/QueryEntry.test.ts by adding a case that uses
getBoundQuery with a query containing a ? and a param value with a single quote,
then assert the returned SQL contains the parameter safely quoted/escaped (and
mirror the expected escaping behavior implemented in src/model/QueryEntry.ts) so
this edge case is validated.
vscode-extension/src/command/startJob.ts (1)

4-8: context パラメーターが未使用

context: vscode.ExtensionContext を受け取っていますが、関数内で一切使用されていません。戻り値の Disposable を呼び出し元が context.subscriptions.push(...) で管理する設計なら、このパラメーターは削除するか、内部で context.subscriptions.push するかを統一した方がすっきりします。

♻️ パラメーター削除案
 export function registerStartJobCommand(
-  context: vscode.ExtensionContext,
   jobManager: JobManagerService,
   onJobsChanged: () => void,
 ): vscode.Disposable {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vscode-extension/src/command/startJob.ts` around lines 4 - 8, The function
registerStartJobCommand currently accepts an unused context:
vscode.ExtensionContext; either remove that parameter from the signature and all
call sites, or use it to register the returned Disposable into
context.subscriptions (call context.subscriptions.push(...) inside
registerStartJobCommand) so ownership is consistent; update the function
signature and any callers if removing the parameter, or keep the signature and
add context.subscriptions.push(disposable) before returning to ensure the
parameter is used (refer to registerStartJobCommand and any call sites that pass
context).
vscode-extension/tsconfig.json (1)

13-14: バンドル型 VSCode 拡張では declaration / declarationMap は不要

esbuild で dist/extension.js に単一バンドルするため、.d.ts ファイルは VSCode ランタイムに消費されません。これらを有効にしておくとビルドで不要な成果物が生成されます。

♻️ 修正提案
-    "declaration": true,
-    "declarationMap": true,
     "sourceMap": true
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vscode-extension/tsconfig.json` around lines 13 - 14, tsconfig の
"declaration" と "declarationMap" が有効になっているためビルドで不要な .d.ts が生成されていますので、VSCode 拡張を
esbuild で単一バンドルする前提なら tsconfig.json の "declaration" と "declarationMap" を削除するか
false に設定してください(対象の設定キー: "declaration", "declarationMap" を編集、ファイル: tsconfig.json
を更新)。
vscode-extension/src/service/LogParserService.ts (1)

42-48: readRawLogTail がファイル全体をメモリに読み込む

ファイル全体を読んでから末尾 N 行を切り出しています。大きなログファイルでは不要なメモリを消費します。ファイル末尾から逆方向に読む方式か、tail 相当のオフセット計算を使うことで改善できます。

-  readRawLogTail(filePath: string, maxLines: number = 500): string {
-    if (!fs.existsSync(filePath)) { return ''; }
-
-    const content = fs.readFileSync(filePath, 'utf-8');
-    const lines = content.split('\n');
-    const tail = lines.slice(-maxLines);
-    return tail.join('\n');
-  }
+  readRawLogTail(filePath: string, maxLines: number = 500): string {
+    if (!fs.existsSync(filePath)) { return ''; }
+
+    const fd = fs.openSync(filePath, 'r');
+    try {
+      const stat = fs.fstatSync(fd);
+      // 末尾から最大 maxLines * 平均行長(256バイト)分だけ読む
+      const chunkSize = Math.min(stat.size, maxLines * 256);
+      const buffer = Buffer.alloc(chunkSize);
+      const bytesRead = fs.readSync(fd, buffer, 0, chunkSize, stat.size - chunkSize);
+      const lines = buffer.slice(0, bytesRead).toString('utf-8').split('\n');
+      return lines.slice(-maxLines).join('\n');
+    } finally {
+      fs.closeSync(fd);
+    }
+  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vscode-extension/src/service/LogParserService.ts` around lines 42 - 48,
readRawLogTail currently reads the whole file into memory; change it to read
only the tail by opening the file (fs.openSync), using fs.statSync to get file
size, and reading backwards in fixed-size chunks with fs.readSync until you have
collected maxLines (or hit file start), then extract and return the last N
lines—this avoids loading the entire file for large logs; update the
implementation in readRawLogTail to perform chunked backward reads (or compute
an appropriate start offset and createReadStream from that offset) and ensure
proper fd closing and error handling.
vscode-extension/test/unit/JobInfo.test.ts (1)

94-106: LGTM — formatDuration の分単位での小数秒ケースを追加すると更に堅牢

現在のテストは整数秒のケース (225-100=125s → 2m5s) のみカバーしています。secs % 60 が小数になるケース(例: formatDuration(100, 161.5)secs=1.5, toFixed(0)="2""1m2s")を追加すると toFixed(0) の丸め挙動を確認できます。任意の改善です。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vscode-extension/test/unit/JobInfo.test.ts` around lines 94 - 106, Add a unit
test to JobInfo.test.ts inside the describe('formatDuration') block that covers
fractional seconds: call formatDuration(100, 161.5) and assert the expected
string for fractional-second output (so the test will catch rounding issues in
formatDuration); this ensures formatDuration (the function under test) correctly
handles secs%60 being a non-integer and will surface any toFixed/rounding bugs.
vscode-extension/src/extension.ts (1)

176-183: インライン import() 型の使用

Line 178 でインラインの import('./model/QueryEntry').QueryEntry を使用しています。ファイル先頭で QueryEntry は既にモデルとして利用可能なはずなので、通常のインポートに統一した方が可読性が向上します。

♻️ 修正案

ファイル先頭に追加(Line 22 付近の JobInfo インポートの近くに):

 import { JobInfo } from './model/JobInfo';
+import { QueryEntry } from './model/QueryEntry';

そしてLine 178を:

-    (item: { entry?: import('./model/QueryEntry').QueryEntry; entryIndex?: number }) => {
+    (item: { entry?: QueryEntry; entryIndex?: number }) => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vscode-extension/src/extension.ts` around lines 176 - 183,
ファイル先頭の既存インポート群(JobInfo の近く)に QueryEntry 型を通常の import 文で追加し、registerCommand
のコールバック引数で使っているインライン型 import('./model/QueryEntry').QueryEntry を置き換えてください。具体的には
showSqlCmd を定義している箇所のシグネチャを QueryEntry を参照する形に変更し、既存の
queryDocumentProvider.showQueryDetail(item.entry, item.entryIndex ?? 0)
呼び出しはそのまま動作するように型だけ修正してください。
vscode-extension/src/service/FileWatcherService.ts (2)

19-25: 同一パスへの watchFile 再呼び出しで既存のウォッチャーが上書きされる

同じ filePathwatchFile を複数回呼ぶと、前のコールバックが警告なくサイレントに破棄されます。これが意図的であれば問題ありませんが、呼び出し元がこの動作を認識していない場合にバグの原因になり得ます。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vscode-extension/src/service/FileWatcherService.ts` around lines 19 - 25,
watchFile currently overwrites an existing watcher entry for the same filePath
(this.watchers.set) which silently drops the previous callback; modify watchFile
to detect an existing watcher via this.watchers.has(filePath) and handle it
explicitly: either (a) reject/throw or return an error when duplicate
registrations occur, or (b) change the watcher value shape to hold an array of
callbacks and push the new onChange into that array (and update any code that
invokes callback to iterate the array), or (c) log a clear warning before
overwriting; update the watcher structure and any usages of the callback (and
related fields lastSize/lastMtime) accordingly so duplicate registrations are
handled deterministically.

39-54: ポーリングごとに fs.statSync が2回呼ばれている

getFileSizegetFileMtime がそれぞれ個別に fs.statSync を呼び出しており、ウォッチ対象ファイルごとにポーリングサイクルあたり2回のシステムコールが発生しています。1回の statSync で両方の値を取得できます。

♻️ 修正案
   private poll(): void {
     for (const [filePath, entry] of this.watchers) {
-      const size = this.getFileSize(filePath);
-      const mtime = this.getFileMtime(filePath);
+      const stat = this.getFileStat(filePath);
+      const size = stat?.size ?? -1;
+      const mtime = stat?.mtimeMs ?? -1;

       if (size !== entry.lastSize || mtime !== entry.lastMtime) {
         entry.lastSize = size;
         entry.lastMtime = mtime;
         try {
           entry.callback();
         } catch (e) {
           // Ignore callback errors
         }
       }
     }
   }

-  private getFileSize(filePath: string): number {
-    try {
-      return fs.statSync(filePath).size;
-    } catch {
-      return -1;
-    }
-  }
-
-  private getFileMtime(filePath: string): number {
-    try {
-      return fs.statSync(filePath).mtimeMs;
-    } catch {
-      return -1;
-    }
-  }
+  private getFileStat(filePath: string): fs.Stats | null {
+    try {
+      return fs.statSync(filePath);
+    } catch {
+      return null;
+    }
+  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vscode-extension/src/service/FileWatcherService.ts` around lines 39 - 54,
poll() currently calls getFileSize(filePath) and getFileMtime(filePath) which
each call fs.statSync, causing two stat syscalls per file per poll; change
poll() to call fs.statSync once (or a single helper that returns both size and
mtime) for each filePath, extract size and mtime from that single stat result,
compare with entry.lastSize and entry.lastMtime, update both, and invoke
entry.callback() as before (ensure to keep the try/catch around entry.callback);
update or remove getFileSize/getFileMtime accordingly so they no longer perform
duplicate stat calls.
vscode-extension/src/command/openLog.ts (1)

6-7: 未使用の context パラメータ

context は関数本体で使用されていません。他のコマンド登録関数との署名の一貫性のためと思われますが、将来的に不要であれば削除も検討してください。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vscode-extension/src/command/openLog.ts` around lines 6 - 7, The parameter
`context` in the function `registerOpenLogCommand` is unused; either remove it
from the function signature and update any callers to match, or mark it as
intentionally unused (rename to `_context` or add an ESLint ignore for unused
vars) to keep the signature consistent with other command registration
functions; update `registerOpenLogCommand` accordingly and ensure exported
signature and all imports/call sites remain consistent.
vscode-extension/package.json (1)

226-229: linttypecheck スクリプトが重複

両方とも tsc --noEmit を実行しています。片方を削除するか、lint で ESLint 等のリンターを実行することを検討してください。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vscode-extension/package.json` around lines 226 - 229, The package.json
defines duplicate scripts: "typecheck" and "lint" both run "tsc --noEmit";
update package.json scripts to remove the duplication by either deleting the
"lint" script or changing "lint" to run a real linter (e.g., ESLint) instead of
"tsc --noEmit"; edit the "lint" entry so it invokes your linter command (for
example eslint with your project file extensions and config) or remove "lint" so
only "typecheck" remains.
vscode-extension/src/util/queryUtils.ts (1)

12-15: shortKey で切り詰め表示であることが分からない

切り詰められたキーに省略記号がないため、ユーザーが完全なキーだと誤解する可能性があります。

♻️ 提案
 export function shortKey(key: string, maxLen: number = 12): string {
   if (key.length <= maxLen) { return key; }
-  return key.substring(0, maxLen);
+  return key.substring(0, maxLen - 1) + '…';
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vscode-extension/src/util/queryUtils.ts` around lines 12 - 15, shortKey
currently returns a truncated substring without indicating truncation; update
the shortKey function so that when key.length > maxLen it returns a visible
truncated form (e.g., include an ellipsis "…" or "..." ) and ensure the total
returned length respects maxLen (adjust substring length to maxLen minus the
ellipsis length); handle small maxLen values gracefully (if maxLen is too small
to include an ellipsis, fall back to the raw substring).
vscode-extension/src/provider/StatisticsTreeProvider.ts (1)

7-21: EventEmitterdispose が実装されていません。

_onDidChangeTreeDataEventEmitterDisposable を実装しており、TreeProvider が不要になった際に dispose() を呼ぶべきです。TreeProviderがextension全体のライフサイクルで存在する場合は問題になりにくいですが、ベストプラクティスとして Disposable の実装を検討してください。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vscode-extension/src/provider/StatisticsTreeProvider.ts` around lines 7 - 21,
StatisticsTreeProvider currently creates an EventEmitter (_onDidChangeTreeData)
but doesn't dispose it; implement Disposable by adding a dispose() method on
StatisticsTreeProvider that calls this._onDidChangeTreeData.dispose() (and any
other cleanup if added), and ensure the provider's consumer (where it's
registered) calls provider.dispose() when tearing down; reference the class
StatisticsTreeProvider and its _onDidChangeTreeData, updateStats, clear methods
to locate where to add the dispose implementation.
vscode-extension/src/provider/QueryTreeProvider.ts (1)

95-102: allEntries.indexOf(entry) が O(n) のため、大量エントリ時にパフォーマンスが劣化します。

filteredEntries の各要素に対して allEntries.indexOf(entry) を実行すると、最大 maxQueries(デフォルト10,000)× allEntries.length の計算量になります。フィルタ適用時にインデックスを保持することで解消できます。

♻️ 修正案: フィルタリング時にインデックスを保持
-  private allEntries: QueryEntry[] = [];
-  private filteredEntries: QueryEntry[] = [];
+  private allEntries: QueryEntry[] = [];
+  private filteredEntries: { entry: QueryEntry; globalIndex: number }[] = [];

applyFiltersgetChildren を対応させ、filteredEntriesglobalIndex を持たせることで indexOf を回避します。変更範囲が広いため、別のアプローチとして Map<QueryEntry, number>allEntries 更新時に構築する方法もあります。

+  private entryIndexMap = new Map<QueryEntry, number>();
+
   loadEntries(entries: QueryEntry[]): void {
     this.allEntries = entries;
+    this.entryIndexMap = new Map(entries.map((e, i) => [e, i]));
     this.applyFilters();
   }

   appendEntries(entries: QueryEntry[]): void {
+    const startIdx = this.allEntries.length;
     this.allEntries.push(...entries);
+    entries.forEach((e, i) => this.entryIndexMap.set(e, startIdx + i));
     this.applyFilters();
   }
   getChildren(element?: QueryTreeItem): QueryTreeItem[] {
     if (!element) {
       return this.filteredEntries.map((entry, index) => {
-        const globalIndex = this.allEntries.indexOf(entry);
+        const globalIndex = this.entryIndexMap.get(entry) ?? -1;
         return new QueryEntryItem(entry, globalIndex);
       });
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vscode-extension/src/provider/QueryTreeProvider.ts` around lines 95 - 102,
getChildren currently calls this.allEntries.indexOf(entry) for each filtered
entry which is O(n); avoid this by preserving the global index during filtering
or by building a Map from entry->index when allEntries changes. Modify
applyFilters (or the allEntries update path) to produce filteredEntries entries
that include a globalIndex (or construct a Map<QueryEntry, number> once), then
update getChildren to read that precomputed globalIndex and pass it into new
QueryEntryItem(entry, globalIndex) instead of calling indexOf; update types for
filteredEntries as needed (or keep a parallel array of indices) and ensure
QueryEntryItem construction uses the stored index.
vscode-extension/src/service/JobManagerService.ts (1)

28-34: getActiveJobs()getCompletedJobs() がそれぞれ loadJobs() を呼び出し、ファイルを二重に読み込みます。

両方のメソッドが連続して呼ばれる場合、jobs.json の読み込みとパースが2回実行されます。現時点で大きな問題ではありませんが、呼び出し側でキャッシュする設計も検討に値します。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vscode-extension/src/service/JobManagerService.ts` around lines 28 - 34, Both
getActiveJobs and getCompletedJobs call loadJobs separately causing jobs.json to
be read/parsing twice; change loadJobs to return a cached in-memory array (e.g.,
add a private cachedJobs: JobInfo[] | null) and have loadJobs populate
cachedJobs on first call and return it on subsequent calls, and ensure any
mutating methods (those that add/update/delete jobs) clear or refresh cachedJobs
so callers get fresh data; update references to loadJobs, getActiveJobs, and
getCompletedJobs accordingly.
vscode-extension/src/command/liveTail.ts (1)

110-150: context パラメータが未使用です。

registerLiveTailCommandscontext: vscode.ExtensionContext パラメータが関数内で使用されていません。不要であれば削除、将来的に使用する予定であれば _context にリネームしてください。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vscode-extension/src/command/liveTail.ts` around lines 110 - 150, The context
parameter of registerLiveTailCommands is unused; either remove it from the
function signature and all its call sites, or rename it to _context to indicate
intentional unused status. Update the declaration of registerLiveTailCommands
and any places that call it (e.g., where it's passed an ExtensionContext) to
match your chosen change, keeping the other parameters (liveTailManager,
jobManager) and preserved behavior of start/stop command registration.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@PLAN_VSCODE_EXTENSION.md`:
- Around line 410-426: The design doc types/interfaces are out of sync with the
implementation: update the doc so JobInfo.startedAt uses number (not string),
JobsFile uses active_jobs and completed_jobs (not active/completed), and
BacktraceFrame uses class_name (optional) instead of class; locate the
interfaces named JobInfo, JobsFile and BacktraceFrame in the doc and change the
field names and types to match the implementation precisely (startedAt: number,
active_jobs/completed_jobs, class_name?) so the document reflects the actual
code.
- Around line 382-408: Update the documented QueryEntry interface to match the
implementation: change timestamp from string to number and change params from
string[] to (string | null)[] (keep it optional), e.g. adjust the QueryEntry
declaration so timestamp: number and params?: (string | null)[]; leave other
fields and the helper function signatures as-is (referencing the QueryEntry and
params/timestamp symbols to locate the code).

In `@vscode-extension/package.json`:
- Around line 232-239: Update the devDependencies in package.json: bump
"esbuild" from "^0.19.0" to "^0.27.3" and "vitest" from "^1.2.0" to "^4.0.18"
(entries inside the "devDependencies" object), then run your package manager to
refresh lockfile and verify by running the build/test scripts to ensure
compatibility with the new versions.

In `@vscode-extension/src/command/liveTail.ts`:
- Around line 85-106: You compute qtype via getQueryType(entry) but never print
it and you use entry.trace.indexOf(frame) which can give wrong indices for
duplicate frames; update the output in the loop that calls
this.outputChannel.appendLine(`[${time}] ... ${shortSql}`) to include qtype
(e.g., after status or before shortSql) and change the trace printing loop to
use an indexed loop (for (let i = 0; i < entry.trace.slice(0,3).length; i++) or
for (const [i, frame] of entry.trace.slice(0,3).entries())) so you can print the
correct frame index instead of entry.trace.indexOf(frame); keep the existing use
of getShortSql, formatTimestamp and the trailing "... (n more frames)" behavior.

In `@vscode-extension/src/command/openLog.ts`:
- Around line 22-27: The code in openLog.ts should (1) guard against
parseJsonlFile throwing by wrapping the call to
logParser.parseJsonlFile(filePath) in a try/catch and displaying a
vscode.window.showErrorMessage with the error if it fails (and aborting further
steps), and (2) after successfully calling
queryTreeProvider.loadEntries(entries) call
updateFilterContext(queryTreeProvider) and statisticsService.computeStats() (the
same functions used in loadJobQueries) so the filter context and statistics are
refreshed; ensure the needed symbols (updateFilterContext, statisticsService)
are imported or otherwise available before calling them and only show the
success vscode.window.showInformationMessage when all steps complete.

In `@vscode-extension/src/extension.ts`:
- Around line 96-100: Replace the inline require and unsafe statSync call by
importing fs at the top (add "import * as fs from 'fs'") and change the
jsonlOffset computation around queryTreeProvider.loadEntries(entries) to safely
handle missing files: check existence with fs.existsSync(jsonlPath) or wrap
fs.statSync in a try/catch, and set jsonlOffset = 0 when the file does not exist
or stat fails; reference symbols: queryTreeProvider.loadEntries, jsonlOffset,
jsonlPath.
- Around line 207-211: jobsJsonPath is computed once at activation via
jobManager.getJobsJsonPath() so when mariadbProfiler.logDirectory changes the
watcher keeps watching the old path; change jobsJsonPath from const to let,
compute it with jobManager.getJobsJsonPath(), then in the existing
settings-change handler (the handler that reacts to mariadbProfiler changes)
detect when logDirectory changes, unwatch the old path (e.g.
fileWatcher.unwatchFile(jobsJsonPath) or dispose the previous watcher) and
reassign jobsJsonPath = jobManager.getJobsJsonPath() and call
fileWatcher.watchFile(jobsJsonPath, () => refreshJobs()) to start watching the
updated file path.

In `@vscode-extension/src/model/JobInfo.ts`:
- Around line 75-81: formatDuration currently computes secs as a floating-point
remainder which can round to 60 ("1m60s"); change formatDuration to first treat
an active job (endedAt undefined) by returning "running...", then compute
totalSeconds = Math.round(end - startedAt) (or Math.floor if you prefer
truncation), derive minutes = Math.floor(totalSeconds / 60) and secs =
totalSeconds % 60 to avoid "60" seconds, and for durations under 1s return
milliseconds (e.g., `${Math.round(seconds*1000)} ms`) so the function
(formatDuration) matches JetBrains behavior and never yields "1m60s".

In `@vscode-extension/src/model/QueryEntry.ts`:
- Around line 131-137: TABLE_PATTERNS currently uses (\w+) which fails to
capture schema-qualified names like database.table; update TABLE_PATTERNS in
QueryEntry.ts to allow dot-separated identifiers (for example replace \w+ with a
pattern that permits dots in addition to word chars, and also handle backticked
qualifiers such as `db`.`table`) so schema.table and backticked variants are
captured correctly by the existing extraction logic in QueryEntry (ensure all
entries in TABLE_PATTERNS are changed consistently).
- Around line 117-123: q, params, paramIndex, result
のプレースホルダ置換でシングルクォートを含む文字列パラメータが適切にエスケープされていません。params[paramIndex] をそのまま
`'${param}'` で包むのではなく、param が null の場合は 'NULL'、文字列の場合は内部のシングルクォートを SQL
標準の2重シングルクォートに置換してから `'...`で囲むように置換処理を修正してください(例: param.replace(/'/g, "''")
を適用)。これにより result に挿入される表示用 SQL が安全にエスケープされます。

In `@vscode-extension/src/provider/JobTreeProvider.ts`:
- Around line 39-44: The ThemeColor used for non-active jobs is invalid: replace
the unsupported 'charts.gray' with a standard UI color (e.g.,
'descriptionForeground' or 'disabledForeground') when constructing this.iconPath
based on job.isActive; update the conditional that sets new
vscode.ThemeColor(...) so non-active branches use one of those valid color IDs
to ensure the gray/disabled appearance renders correctly.

In `@vscode-extension/src/provider/QueryDocumentProvider.ts`:
- Line 10: The documents Map (private documents: Map<string, QueryEntry>) grows
unbounded because showQueryDetail adds entries but never evicts them; add a
simple FIFO cap to prevent memory leaks by tracking insertion order (or using an
array/queue of keys) and, when adding in showQueryDetail, if size >=
MAX_DOCUMENTS remove the oldest key and its QueryEntry from documents before
inserting the new one; choose a sensible MAX_DOCUMENTS constant, update any
tests/uses of QueryEntry if needed, and ensure eviction logic runs atomically
with insertion to avoid races.
- Around line 19-20: In showQueryDetail remove the unused local variable qtype
(the declaration "const qtype = getQueryType(entry);") since getQueryType is
called again inside formatQueryDocument; either delete that line or pass qtype
into formatQueryDocument and use it there—locate the showQueryDetail method and
the formatQueryDocument/getQueryType usages to make the change.

In `@vscode-extension/src/service/FrameResolverService.ts`:
- Around line 41-43: FrameResolverService currently accepts any numeric result
(variable result) including floating-point values, which can later be used as an
invalid index into entry.trace; update the validation to only allow integers by
replacing the typeof check with an integer check (e.g., use
Number.isInteger(result)) and keep the bounds checks (result >= 0 && result <
entry.trace.length) so only whole-number indices are returned; modify the code
around the result validation in FrameResolverService (the branch that returns
result based on entry.trace.length) to enforce integer-only indices.
- Around line 38-39: このコードは vm.createContext / script.runInContext で外部提供の
frameResolverScript を直接実行して RCE リスクがあるため、実行前に VS Code の Workspace Trust API
を使ってワークスペースが信頼済みかを確認し、信頼されていない場合は実行を中止してエラーを返すように修正してください(該当箇所:
FrameResolverService.ts の vm.createContext と script.runInContext を呼び出している箇所、及び
frameResolverScript を受け取るロジック)。併せて package.json の contributes に
capabilities.untrustedWorkspaces.supported を false
に設定して拡張機能のワークスペーストラスト要件を宣言する変更を追加してください。
- Around line 57-71: getScript currently sets cachedScript = null and
cachedScriptText = scriptText on compile error, which makes the first condition
(this.cachedScriptText === scriptText && this.cachedScript) false and causes
repeated re-compilation attempts; fix by recording failed compilations and
short-circuiting: add a new member (e.g., failedScriptText or failedScripts:
Set<string>) and in getScript first check if the scriptText is in the failure
marker and return null immediately, and in the catch block add the scriptText to
that failure marker instead of relying on cachedScriptText alone; keep
references to getScript, cachedScript, cachedScriptText and update their logic
accordingly.

In `@vscode-extension/src/service/JobManagerService.ts`:
- Around line 36-47: startJob may return raw stdout on fallback which can
contain unexpected chars; update startJob (which calls runCli) so that when
output.match(/Job '([^']+)' started/) fails and jobKey is undefined you sanitize
and validate the output before returning: trim it, then extract a safe job
identifier using a strict regex (e.g. allow only [A-Za-z0-9_-]+) and return that
if present, otherwise throw or return a clear error/empty value instead of the
raw stdout to avoid leaking arbitrary strings into callers.

In `@vscode-extension/src/service/LogParserService.ts`:
- Around line 24-36: The code has a TOCTOU race: statSync is called before
openSync and readSync's return value is ignored, which can cause zero-padded
buffers and JSON parse errors when files rotate or shrink; fix
parseJsonlFileFromOffset and tailRawLog by opening the file first (openSync),
calling fs.fstatSync(fd) to get the current size, allocate/read only the needed
bytes, and check the bytesRead returned by fs.readSync—use bytesRead to slice
the buffer/string before passing to parseJsonlContent and compute the newOffset
based on the fstat size (or handle truncation appropriately).

In `@vscode-extension/src/util/pathMapping.ts`:
- Line 9: The current use of containerPath.replace(from, to) treats the
replacement string `to` as a replacement pattern (so `$&`, `$'`, etc. are
processed); change the logic in the path-mapping function that uses
containerPath.replace(from, to) to perform a safe prefix swap instead: check if
containerPath.startsWith(from) and if so return to +
containerPath.slice(from.length), otherwise return containerPath unchanged;
update the function that references containerPath.replace (the mapping routine
in pathMapping.ts) to use this startsWith + slice approach to avoid
special-character interpretation.

In `@vscode-extension/src/util/queryUtils.ts`:
- Around line 1-5: The generateBar function can throw a RangeError when value >
max because filled can exceed barWidth, making barWidth - filled negative; clamp
the computed filled value to the range [0, barWidth] after computing filled
(using value, max, barWidth) so the repeats never receive a negative count—keep
the existing max === 0 guard and then use the clamped filled when building the
string.

---

Nitpick comments:
In `@vscode-extension/package.json`:
- Around line 226-229: The package.json defines duplicate scripts: "typecheck"
and "lint" both run "tsc --noEmit"; update package.json scripts to remove the
duplication by either deleting the "lint" script or changing "lint" to run a
real linter (e.g., ESLint) instead of "tsc --noEmit"; edit the "lint" entry so
it invokes your linter command (for example eslint with your project file
extensions and config) or remove "lint" so only "typecheck" remains.

In `@vscode-extension/src/command/filterQueries.ts`:
- Around line 5-8: The three exported functions registerFilterByTypeCommand,
registerFilterByTagCommand, and registerClearFilterCommand take an unused
context: vscode.ExtensionContext parameter; remove that parameter from each
function signature and any internal references, update any call sites to stop
passing context, and keep each function returning a vscode.Disposable as before;
alternatively, if you prefer lifecycle registration inside these functions,
instead accept context and call context.subscriptions.push(theDisposable) inside
each function—pick one approach and apply it consistently across the three
functions and their callers.

In `@vscode-extension/src/command/liveTail.ts`:
- Around line 110-150: The context parameter of registerLiveTailCommands is
unused; either remove it from the function signature and all its call sites, or
rename it to _context to indicate intentional unused status. Update the
declaration of registerLiveTailCommands and any places that call it (e.g., where
it's passed an ExtensionContext) to match your chosen change, keeping the other
parameters (liveTailManager, jobManager) and preserved behavior of start/stop
command registration.

In `@vscode-extension/src/command/openLog.ts`:
- Around line 6-7: The parameter `context` in the function
`registerOpenLogCommand` is unused; either remove it from the function signature
and update any callers to match, or mark it as intentionally unused (rename to
`_context` or add an ESLint ignore for unused vars) to keep the signature
consistent with other command registration functions; update
`registerOpenLogCommand` accordingly and ensure exported signature and all
imports/call sites remain consistent.

In `@vscode-extension/src/command/startJob.ts`:
- Around line 4-8: The function registerStartJobCommand currently accepts an
unused context: vscode.ExtensionContext; either remove that parameter from the
signature and all call sites, or use it to register the returned Disposable into
context.subscriptions (call context.subscriptions.push(...) inside
registerStartJobCommand) so ownership is consistent; update the function
signature and any callers if removing the parameter, or keep the signature and
add context.subscriptions.push(disposable) before returning to ensure the
parameter is used (refer to registerStartJobCommand and any call sites that pass
context).

In `@vscode-extension/src/extension.ts`:
- Around line 176-183: ファイル先頭の既存インポート群(JobInfo の近く)に QueryEntry 型を通常の import
文で追加し、registerCommand のコールバック引数で使っているインライン型
import('./model/QueryEntry').QueryEntry を置き換えてください。具体的には showSqlCmd
を定義している箇所のシグネチャを QueryEntry を参照する形に変更し、既存の
queryDocumentProvider.showQueryDetail(item.entry, item.entryIndex ?? 0)
呼び出しはそのまま動作するように型だけ修正してください。

In `@vscode-extension/src/provider/QueryTreeProvider.ts`:
- Around line 95-102: getChildren currently calls this.allEntries.indexOf(entry)
for each filtered entry which is O(n); avoid this by preserving the global index
during filtering or by building a Map from entry->index when allEntries changes.
Modify applyFilters (or the allEntries update path) to produce filteredEntries
entries that include a globalIndex (or construct a Map<QueryEntry, number>
once), then update getChildren to read that precomputed globalIndex and pass it
into new QueryEntryItem(entry, globalIndex) instead of calling indexOf; update
types for filteredEntries as needed (or keep a parallel array of indices) and
ensure QueryEntryItem construction uses the stored index.

In `@vscode-extension/src/provider/StatisticsTreeProvider.ts`:
- Around line 7-21: StatisticsTreeProvider currently creates an EventEmitter
(_onDidChangeTreeData) but doesn't dispose it; implement Disposable by adding a
dispose() method on StatisticsTreeProvider that calls
this._onDidChangeTreeData.dispose() (and any other cleanup if added), and ensure
the provider's consumer (where it's registered) calls provider.dispose() when
tearing down; reference the class StatisticsTreeProvider and its
_onDidChangeTreeData, updateStats, clear methods to locate where to add the
dispose implementation.

In `@vscode-extension/src/service/FileWatcherService.ts`:
- Around line 19-25: watchFile currently overwrites an existing watcher entry
for the same filePath (this.watchers.set) which silently drops the previous
callback; modify watchFile to detect an existing watcher via
this.watchers.has(filePath) and handle it explicitly: either (a) reject/throw or
return an error when duplicate registrations occur, or (b) change the watcher
value shape to hold an array of callbacks and push the new onChange into that
array (and update any code that invokes callback to iterate the array), or (c)
log a clear warning before overwriting; update the watcher structure and any
usages of the callback (and related fields lastSize/lastMtime) accordingly so
duplicate registrations are handled deterministically.
- Around line 39-54: poll() currently calls getFileSize(filePath) and
getFileMtime(filePath) which each call fs.statSync, causing two stat syscalls
per file per poll; change poll() to call fs.statSync once (or a single helper
that returns both size and mtime) for each filePath, extract size and mtime from
that single stat result, compare with entry.lastSize and entry.lastMtime, update
both, and invoke entry.callback() as before (ensure to keep the try/catch around
entry.callback); update or remove getFileSize/getFileMtime accordingly so they
no longer perform duplicate stat calls.

In `@vscode-extension/src/service/JobManagerService.ts`:
- Around line 28-34: Both getActiveJobs and getCompletedJobs call loadJobs
separately causing jobs.json to be read/parsing twice; change loadJobs to return
a cached in-memory array (e.g., add a private cachedJobs: JobInfo[] | null) and
have loadJobs populate cachedJobs on first call and return it on subsequent
calls, and ensure any mutating methods (those that add/update/delete jobs) clear
or refresh cachedJobs so callers get fresh data; update references to loadJobs,
getActiveJobs, and getCompletedJobs accordingly.

In `@vscode-extension/src/service/LogParserService.ts`:
- Around line 42-48: readRawLogTail currently reads the whole file into memory;
change it to read only the tail by opening the file (fs.openSync), using
fs.statSync to get file size, and reading backwards in fixed-size chunks with
fs.readSync until you have collected maxLines (or hit file start), then extract
and return the last N lines—this avoids loading the entire file for large logs;
update the implementation in readRawLogTail to perform chunked backward reads
(or compute an appropriate start offset and createReadStream from that offset)
and ensure proper fd closing and error handling.

In `@vscode-extension/src/util/queryUtils.ts`:
- Around line 12-15: shortKey currently returns a truncated substring without
indicating truncation; update the shortKey function so that when key.length >
maxLen it returns a visible truncated form (e.g., include an ellipsis "…" or
"..." ) and ensure the total returned length respects maxLen (adjust substring
length to maxLen minus the ellipsis length); handle small maxLen values
gracefully (if maxLen is too small to include an ellipsis, fall back to the raw
substring).

In `@vscode-extension/test/unit/JobInfo.test.ts`:
- Around line 94-106: Add a unit test to JobInfo.test.ts inside the
describe('formatDuration') block that covers fractional seconds: call
formatDuration(100, 161.5) and assert the expected string for fractional-second
output (so the test will catch rounding issues in formatDuration); this ensures
formatDuration (the function under test) correctly handles secs%60 being a
non-integer and will surface any toFixed/rounding bugs.

In `@vscode-extension/test/unit/QueryEntry.test.ts`:
- Around line 81-146: Add a unit test to cover parameters containing single
quotes (e.g., "O'Brien") to ensure getBoundQuery properly escapes or formats
such values; update vscode-extension/test/unit/QueryEntry.test.ts by adding a
case that uses getBoundQuery with a query containing a ? and a param value with
a single quote, then assert the returned SQL contains the parameter safely
quoted/escaped (and mirror the expected escaping behavior implemented in
src/model/QueryEntry.ts) so this edge case is validated.

In `@vscode-extension/tsconfig.json`:
- Around line 13-14: tsconfig の "declaration" と "declarationMap"
が有効になっているためビルドで不要な .d.ts が生成されていますので、VSCode 拡張を esbuild で単一バンドルする前提なら
tsconfig.json の "declaration" と "declarationMap" を削除するか false に設定してください(対象の設定キー:
"declaration", "declarationMap" を編集、ファイル: tsconfig.json を更新)。

Comment thread PLAN_VSCODE_EXTENSION.md
Comment thread PLAN_VSCODE_EXTENSION.md
Comment thread vscode-extension/package.json
Comment thread vscode-extension/src/command/liveTail.ts
Comment thread vscode-extension/src/command/openLog.ts
Comment thread vscode-extension/src/service/FrameResolverService.ts
Comment thread vscode-extension/src/service/JobManagerService.ts
Comment thread vscode-extension/src/service/LogParserService.ts Outdated
Comment thread vscode-extension/src/util/pathMapping.ts Outdated
Comment thread vscode-extension/src/util/queryUtils.ts
Package and upload .vsix on every push/PR, matching plugin-build.yml
which uploads the JetBrains plugin .zip.

https://claude.ai/code/session_01EabgSSMXSA25Pjf1aTEmB2

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
.github/workflows/release.yml (1)

196-238: ジョブにタイムアウトを設定することを推奨します

timeout-minutes が指定されていないため、vitest や esbuild がハングした場合にジョブが GitHub のデフォルト上限(6時間)まで実行され続けます。

⚙️ タイムアウト設定の提案
  build-vscode:
    name: VSCode Extension
    runs-on: ubuntu-latest
+   timeout-minutes: 15
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 196 - 238, The build-vscode job
lacks a timeout and can hang indefinitely; add a timeout-minutes entry (e.g.,
timeout-minutes: 30) to the build-vscode job definition so the whole job
(including steps running npx vitest run, node esbuild.mjs, and the packaging
step using npx `@vscode/vsce`) is automatically cancelled after the configured
time; place the timeout-minutes field alongside name: and runs-on: under the
build-vscode job.
.github/workflows/vscode-build.yml (2)

19-23: permissions ブロックの明示的な宣言を推奨します。

ワークフローおよびジョブレベルに permissions が宣言されていないため、GITHUB_TOKEN はリポジトリのデフォルト権限(多くの場合 contents: write 等)を持ちます。このワークフローはアーティファクトのアップロードのみ行うため、最小権限原則に従い権限を明示することが望ましいです。

♻️ 修正案: 最小権限の明示
+permissions:
+  contents: read
+
 jobs:
   build:
     if: github.event_name != 'pull_request' || !contains(github.event.pull_request.labels.*.name, 'no_run')
     name: Build & Test Extension
     runs-on: ubuntu-latest
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/vscode-build.yml around lines 19 - 23, Add an explicit
permissions block to the workflow to scope GITHUB_TOKEN to the minimum needed
(e.g., artifacts: write and id-token if used), by adding a top-level
"permissions" entry and/or a job-level "permissions" under the "build" job;
ensure the "GITHUB_TOKEN" permission does not default to broad scopes like
contents: write and instead lists only the required permissions for artifact
upload (reference the workflow's top-level permissions key and the build job's
permissions override if present).

3-13: 内部PRで重複CIランが発生します。

同一リポジトリの内部PRでは、ブランチへの push イベントと pull_request イベントが同時に発火します。両者の github.ref は異なる(例: refs/heads/feature vs refs/pull/16/merge)ため、concurrency グループが別々になり、同じコミットに対して2回ビルドが走ります。

push トリガーを保護ブランチのみに限定するか、PRが存在しない場合のみ push ビルドを実行する条件を追加することを推奨します。

♻️ 修正案: pushトリガーをメインブランチのみに限定する
 on:
   push:
-    branches: ['**']
+    branches:
+      - 'main'
     paths:
       - 'vscode-extension/**'
       - '.github/workflows/vscode-build.yml'
   pull_request:
     branches: ['**']
     paths:
       - 'vscode-extension/**'
       - '.github/workflows/vscode-build.yml'

または、push イベント時にPRが存在しない場合のみ実行する条件を build ジョブに追加することもできます。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/vscode-build.yml around lines 3 - 13, 同一コミットで push と
pull_request の両イベントが発火して重複CIが走っているので、.github/workflows/vscode-build.yml の on:
push と on: pull_request トリガーを調整してください。簡単な修正は on.push.branches を保護ブランチ(例: main,
release/*)のみに限定すること、あるいは build ジョブ(ジョブ名 build)に実行条件を追加して push イベント時は PR
が存在しない場合のみ実行するようにすること(結果的に pull_request と重複しないようにする)。該当箇所はファイル内の on.push /
on.pull_request セクションと build ジョブの if 条件を編集してください。
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/vscode-build.yml:
- Around line 51-53: パッケージングで npx を使っているためロックされたバージョンが使われず再現性が失われています;
ワークフローステップ "Package .vsix" (working-directory: vscode-extension) の run コマンドで現在の
"npx `@vscode/vsce` package --no-dependencies -o mariadb-profiler-viewer.vsix"
を、ロックされた devDependency を確実に使うために "npm exec `@vscode/vsce` -- package
--no-dependencies -o mariadb-profiler-viewer.vsix" に置き換えるか、あるいは既存の npm
スクリプトを利用する場合は "npm run package" を使うように修正してください.

---

Nitpick comments:
In @.github/workflows/release.yml:
- Around line 196-238: The build-vscode job lacks a timeout and can hang
indefinitely; add a timeout-minutes entry (e.g., timeout-minutes: 30) to the
build-vscode job definition so the whole job (including steps running npx vitest
run, node esbuild.mjs, and the packaging step using npx `@vscode/vsce`) is
automatically cancelled after the configured time; place the timeout-minutes
field alongside name: and runs-on: under the build-vscode job.

In @.github/workflows/vscode-build.yml:
- Around line 19-23: Add an explicit permissions block to the workflow to scope
GITHUB_TOKEN to the minimum needed (e.g., artifacts: write and id-token if
used), by adding a top-level "permissions" entry and/or a job-level
"permissions" under the "build" job; ensure the "GITHUB_TOKEN" permission does
not default to broad scopes like contents: write and instead lists only the
required permissions for artifact upload (reference the workflow's top-level
permissions key and the build job's permissions override if present).
- Around line 3-13: 同一コミットで push と pull_request
の両イベントが発火して重複CIが走っているので、.github/workflows/vscode-build.yml の on: push と on:
pull_request トリガーを調整してください。簡単な修正は on.push.branches を保護ブランチ(例: main,
release/*)のみに限定すること、あるいは build ジョブ(ジョブ名 build)に実行条件を追加して push イベント時は PR
が存在しない場合のみ実行するようにすること(結果的に pull_request と重複しないようにする)。該当箇所はファイル内の on.push /
on.pull_request セクションと build ジョブの if 条件を編集してください。

Comment thread .github/workflows/vscode-build.yml Outdated
- liveTail.ts: include qtype in output, fix trace frame indexing for duplicates
- package.json: bump esbuild ^0.27.3 / vitest ^4.0.18, add untrustedWorkspaces capability
- openLog.ts: add try/catch for parseJsonlFile, refresh filter context and statistics
- extension.ts: replace require('fs') with proper import, safe statSync, re-watch jobsJsonPath on logDirectory change
- JobInfo.ts: fix formatDuration "1m60s" rounding bug, show "running..." for active jobs, ms for <1s
- QueryEntry.ts: escape single quotes in bound params, support schema.table in TABLE_PATTERNS
- JobTreeProvider.ts: replace unsupported 'charts.gray' ThemeColor with 'disabledForeground'
- QueryDocumentProvider.ts: add FIFO cap (50) to documents Map, remove unused qtype variable
- FrameResolverService.ts: add Workspace Trust guard, enforce integer-only indices, cache failed compilations
- JobManagerService.ts: sanitize fallback startJob output with strict regex
- LogParserService.ts: fix TOCTOU race by using fstatSync on opened fd, use bytesRead from readSync
- pathMapping.ts: use startsWith+slice instead of replace to avoid replacement pattern injection
- queryUtils.ts: clamp generateBar filled value to prevent RangeError
- vscode-build.yml: use npm exec instead of npx for reproducible packaging

https://claude.ai/code/session_01EabgSSMXSA25Pjf1aTEmB2
Use getBoundQuery() instead of raw SQL for the tree view label and
live tail output so users see actual parameter values (e.g.
'SELECT * FROM users WHERE id = 42') instead of placeholders
('SELECT * FROM users WHERE id = ?').

https://claude.ai/code/session_01EabgSSMXSA25Pjf1aTEmB2
@39ff 39ff merged commit 6db524a into main Feb 18, 2026
35 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants