Skip to content

Commit 451cb6c

Browse files
Bump version to 0.10.17
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 8203083 commit 451cb6c

11 files changed

Lines changed: 1096 additions & 81 deletions

CHANGELOG.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,34 @@
11
# Changelog
22

3+
## 0.10.17 (Preview)
4+
5+
### Added
6+
- **Language Model Tools** — 5 LM-tool wrappers (`binlog_lm_overview`, `binlog_lm_errors`, `binlog_lm_search`, `binlog_lm_perf`, `binlog_lm_compare`) registered via `vscode.lm.registerTool` so any agent (@workspace, agent mode, custom chat modes) can analyze loaded binlogs — not just `@binlog`
7+
- **"Ask @binlog" CodeAction** — click a build error/warning squiggle in the editor → Quick Fix → "Ask @binlog about this error" or "Fix with @binlog"
8+
- **Auto-fix diagnostic** — right-click any error/warning in the Binlog Explorer → "Auto-fix with Copilot" opens agent mode to edit the source file directly
9+
- **Diagnostic context menu** — right-click error/warning tree items for "Ask @binlog about this issue" and "Auto-fix with Copilot"
10+
- **Fix All: before/after comparison** — "Fix All Issues" now writes to a new `{name}_fixed_{N}.binlog` (preserving the original) and auto-loads both for `/compare`
11+
- **`/timeline` command playbook**`/timeline` now has its own per-command instruction file
12+
- **esbuild bundling** — extension ships as a single `dist/extension.js` (~476 KB) instead of raw TypeScript output
13+
- **CI matrix** — GitHub Actions workflow runs on both Ubuntu and Windows
14+
15+
### Changed
16+
- **Prompt refactor** — SYSTEM_PROMPT and COMMAND_PROMPTS moved to lazy-loaded markdown playbooks (`resources/playbooks/`). Total prompt budget ~250 tokens baseline vs ~1500 before
17+
- **Auto-greeting** — loading a binlog now fires `@binlog /summary` automatically instead of asking "What would you like to analyze?"
18+
- **Multi-binlog support** — all MCP tool calls (tree view, document provider, timeline, extension commands) now auto-inject `binlog_file` for the primary binlog when multiple are loaded
19+
20+
### Fixed
21+
- **MCP startup race** — auto-greeting now waits for both MCP config AND tree client initialization before firing `/summary`
22+
- **LM-tool readiness** — wrappers wait up to 10s for MCP client to become ready instead of immediately returning "No binlog loaded"
23+
- **Cross-machine file navigation** — diagnostics and tree view items from binlogs built on other machines (CI, coworkers) no longer show "file not found" — paths are resolved against workspace folders
24+
- **Multi-root workspace path resolution**`resolveFilePath` now checks each workspace folder for file existence instead of blindly using the first
25+
- **MCP timer leak**`setTimeout` in `sendRequest` is now cleared on resolve/reject
26+
- **Spawn error handler** — missing `error` event listener on MCP subprocess no longer crashes the extension host
27+
- **processResponse error retry** — recursive tool-call retry now checks all 5 error patterns (was missing `role 'tool'`, `tool_call_id`, `400`)
28+
- **CodeAction cap** — max 5 diagnostics per `provideCodeActions` call to prevent unusable Quick Fix menus
29+
- **Cross-platform tests**`workspaceMatchesBinlog` and `getSourceLabel` no longer depend on `path.sep` (fixes CI on Ubuntu)
30+
- **Stale tool names in prompts**`analyzeInChat` prompts no longer reference non-existent tools (`binlog_search_targets`, etc.)
31+
332
## 0.10.16 (Preview)
433

534
### Fixed

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,17 @@ The [BinlogInsights.Mcp](https://www.nuget.org/packages/BinlogInsights.Mcp) serv
2525

2626
| Feature | Description |
2727
|---------|-------------|
28-
| **@binlog Chat** | Ask Copilot about errors, performance, targets, imports, NuGet issues — with slash commands like `/errors`, `/perf`, `/timeline`, `/compare` |
28+
| **@binlog Chat** | Ask Copilot about errors, performance, targets, imports, NuGet issues — with slash commands like `/errors`, `/perf`, `/timeline`, `/compare`, `/summary`, `/incremental`, `/buildcheck`, `/search`, `/targets`, `/properties`, `/items`, `/propertyhistory` |
2929
| **Build & Collect** | Build a project and capture a `.binlog` in one step |
3030
| **Binlog Explorer** | Sidebar tree with project → target → task hierarchy, errors, warnings, performance |
3131
| **Build Timeline** | Visual bar charts of target/task durations with click-to-analyze in Copilot |
32+
| **Fix All Issues** | Copilot fixes all build errors/warnings, rebuilds, and loads before/after for comparison |
33+
| **Auto-fix Diagnostic** | Right-click any error/warning in the tree → "Auto-fix with Copilot" to fix it directly |
3234
| **Optimize Build** | Pick optimizations, Copilot applies changes, verify with A/B comparison |
35+
| **Build Analysis Mode** | Chat mode pre-configured with BinlogInsights MCP tools — works with any agent |
36+
| **Language Model Tools** | `binlog_lm_overview`, `binlog_lm_errors`, `binlog_lm_search`, `binlog_lm_perf`, `binlog_lm_compare` — available to @workspace, agent mode, and custom chat modes |
3337
| **CI/CD Integration** | Download binlogs from Azure DevOps Pipelines and GitHub Actions — filter by branch or PR |
34-
| **Problems Panel** | Build diagnostics as native VS Code errors/warnings with per-project CodeLens |
38+
| **Problems Panel** | Build diagnostics as native VS Code errors/warnings with per-project CodeLens and "Ask @binlog" CodeActions |
3539
| **Search** | Search across all build events — targets, tasks, messages, properties |
3640

3741
## Configuration

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "binlog-analyzer",
33
"displayName": "MSBuild Binlog Analyzer",
44
"description": "Analyze MSBuild binary logs with Copilot Chat and MCP tools",
5-
"version": "0.10.30",
5+
"version": "0.10.17",
66
"preview": true,
77
"publisher": "dotutils",
88
"license": "MIT",

src/binlogDocumentProvider.ts

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,20 @@ export class BinlogDocumentProvider implements vscode.TextDocumentContentProvide
2020
this.cache.clear();
2121
}
2222

23+
/**
24+
* Wrapper around mcpClient.callTool that auto-injects binlog_file
25+
* for the primary (first) loaded binlog. Without this, multi-binlog
26+
* mode causes "requires explicit binlog_file" errors in every
27+
* document render call.
28+
*/
29+
private async call(tool: string, args: Record<string, unknown> = {}): Promise<{ text: string }> {
30+
if (!this.mcpClient) { throw new Error('MCP server not connected'); }
31+
if (!args.binlog_file && this.mcpClient.loadedBinlogs.length > 1) {
32+
args.binlog_file = this.mcpClient.loadedBinlogs[0];
33+
}
34+
return this.mcpClient.callTool(tool, args);
35+
}
36+
2337
invalidate(uri: vscode.Uri) {
2438
this.cache.delete(uri.toString());
2539
this._onDidChange.fire(uri);
@@ -82,7 +96,7 @@ export class BinlogDocumentProvider implements vscode.TextDocumentContentProvide
8296

8397
// Build overview
8498
try {
85-
const overviewResult = await this.mcpClient!.callTool('binlog_overview');
99+
const overviewResult = await this.call('binlog_overview');
86100
const ov = JSON.parse(overviewResult.text);
87101
const status = ov.succeeded ? '✅ BUILD SUCCEEDED' : '❌ BUILD FAILED';
88102
const dur = ov.duration || '';
@@ -110,7 +124,7 @@ export class BinlogDocumentProvider implements vscode.TextDocumentContentProvide
110124

111125
// Projects
112126
try {
113-
const projResult = await this.mcpClient!.callTool('binlog_projects');
127+
const projResult = await this.call('binlog_projects');
114128
const projData = JSON.parse(projResult.text);
115129

116130
// Handle both formats: array (BinlogInsights) and object (baronfel)
@@ -141,8 +155,8 @@ export class BinlogDocumentProvider implements vscode.TextDocumentContentProvide
141155
let diagsByProject: Map<string, { errors: number; warnings: number }> | undefined;
142156
try {
143157
const [errResult, warnResult] = await Promise.allSettled([
144-
this.mcpClient!.callTool('binlog_errors'),
145-
this.mcpClient!.callTool('binlog_warnings'),
158+
this.call('binlog_errors'),
159+
this.call('binlog_warnings'),
146160
]);
147161
diagsByProject = new Map();
148162
for (const result of [errResult, warnResult]) {
@@ -188,8 +202,8 @@ export class BinlogDocumentProvider implements vscode.TextDocumentContentProvide
188202
// Diagnostics
189203
try {
190204
const [errResult, warnResult] = await Promise.allSettled([
191-
this.mcpClient!.callTool('binlog_errors'),
192-
this.mcpClient!.callTool('binlog_warnings'),
205+
this.call('binlog_errors'),
206+
this.call('binlog_warnings'),
193207
]);
194208

195209
const parseItems = (r: PromiseSettledResult<any>) => {
@@ -237,7 +251,7 @@ export class BinlogDocumentProvider implements vscode.TextDocumentContentProvide
237251

238252
// Performance
239253
try {
240-
const targetsResult = await this.mcpClient!.callTool('binlog_expensive_targets', { top_number: 10 });
254+
const targetsResult = await this.call('binlog_expensive_targets', { top_number: 10 });
241255
const targetsData = JSON.parse(targetsResult.text);
242256
lines.push('🔥 SLOWEST TARGETS');
243257
lines.push('─────────────────────────────────────────────────────');
@@ -250,7 +264,7 @@ export class BinlogDocumentProvider implements vscode.TextDocumentContentProvide
250264
lines.push('');
251265

252266
try {
253-
const tasksResult = await this.mcpClient!.callTool('binlog_expensive_tasks', { top_number: 10 });
267+
const tasksResult = await this.call('binlog_expensive_tasks', { top_number: 10 });
254268
const tasksData = JSON.parse(tasksResult.text);
255269
lines.push('🔧 SLOWEST TASKS');
256270
lines.push('─────────────────────────────────────────────────────');
@@ -263,7 +277,7 @@ export class BinlogDocumentProvider implements vscode.TextDocumentContentProvide
263277

264278
// Analyzers
265279
try {
266-
const analyzersResult = await this.mcpClient!.callTool('binlog_expensive_analyzers', { limit: 10 });
280+
const analyzersResult = await this.call('binlog_expensive_analyzers', { limit: 10 });
267281
const analyzersData = JSON.parse(analyzersResult.text);
268282
const entries = this.parsePerfEntries(analyzersData);
269283
if (entries.length > 0) {
@@ -300,7 +314,7 @@ export class BinlogDocumentProvider implements vscode.TextDocumentContentProvide
300314

301315
// Get per-project target times
302316
try {
303-
const targetTimesResult = await this.mcpClient!.callTool('binlog_project_target_times', {
317+
const targetTimesResult = await this.call('binlog_project_target_times', {
304318
project: projectName,
305319
});
306320
const targetData = JSON.parse(targetTimesResult.text);
@@ -330,7 +344,7 @@ export class BinlogDocumentProvider implements vscode.TextDocumentContentProvide
330344
} catch {
331345
// Try fallback: binlog_expensive_projects for at least a total time
332346
try {
333-
const buildTimeResult = await this.mcpClient!.callTool('binlog_expensive_projects');
347+
const buildTimeResult = await this.call('binlog_expensive_projects');
334348
const data = JSON.parse(buildTimeResult.text);
335349
const projects = Array.isArray(data) ? data : [];
336350
const match = projects.find((p: any) =>
@@ -348,8 +362,8 @@ export class BinlogDocumentProvider implements vscode.TextDocumentContentProvide
348362
// Get diagnostics and filter for this project
349363
try {
350364
const [errResult, warnResult] = await Promise.allSettled([
351-
this.mcpClient!.callTool('binlog_errors'),
352-
this.mcpClient!.callTool('binlog_warnings'),
365+
this.call('binlog_errors'),
366+
this.call('binlog_warnings'),
353367
]);
354368

355369
const parseItems = (r: PromiseSettledResult<any>) => {
@@ -414,18 +428,18 @@ export class BinlogDocumentProvider implements vscode.TextDocumentContentProvide
414428
}
415429

416430
private async renderProjects(): Promise<string> {
417-
const result = await this.mcpClient!.callTool('binlog_projects');
431+
const result = await this.call('binlog_projects');
418432
return this.formatSection('PROJECTS', result.text);
419433
}
420434

421435
private async renderDiagnostics(type: 'errors' | 'warnings'): Promise<string> {
422436
const tool = type === 'errors' ? 'binlog_errors' : 'binlog_warnings';
423-
const result = await this.mcpClient!.callTool(tool);
437+
const result = await this.call(tool);
424438
return this.formatSection(type.toUpperCase(), result.text);
425439
}
426440

427441
private async renderExpensive(tool: string, title: string): Promise<string> {
428-
const result = await this.mcpClient!.callTool(tool, { top_number: 20 });
442+
const result = await this.call(tool, { top_number: 20 });
429443
return this.formatSection(title.toUpperCase(), result.text);
430444
}
431445

src/binlogTreeView.ts

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -825,7 +825,7 @@ export class BinlogTreeDataProvider implements vscode.TreeDataProvider<BinlogTre
825825

826826
// Try the dedicated MCP tool first
827827
try {
828-
const result = await this.mcpClient.callTool('binlog_expensive_analyzers', { limit: 20 });
828+
const result = await this.mcpCall('binlog_expensive_analyzers', { limit: 20 });
829829
const items = this.parsePerfItems(result.text, 'microscope');
830830
if (items.length > 0) {
831831
this.analyzersCache = items;
@@ -842,7 +842,7 @@ export class BinlogTreeDataProvider implements vscode.TreeDataProvider<BinlogTre
842842
// "363 ms Microsoft.CodeAnalysis.CSharp.NetAnalyzers ... = 341 ms"
843843
try {
844844
// First check if the binlog has analyzer data at all
845-
const checkResult = await this.mcpClient.callTool('binlog_search', {
845+
const checkResult = await this.mcpCall('binlog_search', {
846846
query: 'Total analyzer execution',
847847
limit: 5,
848848
});
@@ -877,7 +877,7 @@ export class BinlogTreeDataProvider implements vscode.TreeDataProvider<BinlogTre
877877
];
878878
for (const term of searchTerms) {
879879
try {
880-
const result = await this.mcpClient.callTool('binlog_search', {
880+
const result = await this.mcpCall('binlog_search', {
881881
query: term,
882882
limit: 200,
883883
});
@@ -1019,7 +1019,7 @@ export class BinlogTreeDataProvider implements vscode.TreeDataProvider<BinlogTre
10191019
}
10201020
try {
10211021
const projectName = this.extractFileName(projectFile).replace(/\.[^.]+$/, '');
1022-
const result = await this.mcpClient.callTool('binlog_project_targets', {
1022+
const result = await this.mcpCall('binlog_project_targets', {
10231023
project: projectName,
10241024
});
10251025
const data = this.tryParseJson(result.text);
@@ -1089,7 +1089,7 @@ export class BinlogTreeDataProvider implements vscode.TreeDataProvider<BinlogTre
10891089
target_name: targetName,
10901090
project: projectName,
10911091
};
1092-
const result = await this.mcpClient.callTool('binlog_tasks_in_target', args);
1092+
const result = await this.mcpCall('binlog_tasks_in_target', args);
10931093
const data = this.tryParseJson(result.text);
10941094
const items: BinlogTreeItem[] = [];
10951095
const entries = Array.isArray(data) ? data : (data && typeof data === 'object' ? Object.entries(data).map(([name, info]) => ({ name, ...(info as object) })) : []);
@@ -1152,7 +1152,7 @@ export class BinlogTreeDataProvider implements vscode.TreeDataProvider<BinlogTre
11521152
project: projectName,
11531153
target_name: element.targetName || '',
11541154
};
1155-
const result = await this.mcpClient.callTool('binlog_task_details', args);
1155+
const result = await this.mcpCall('binlog_task_details', args);
11561156
const items: BinlogTreeItem[] = [];
11571157

11581158
// Try to parse as JSON first
@@ -1224,7 +1224,7 @@ export class BinlogTreeDataProvider implements vscode.TreeDataProvider<BinlogTre
12241224
if (!this.mcpClient) {
12251225
throw new Error('MCP server not connected');
12261226
}
1227-
return this.mcpClient.callTool('binlog_search', {
1227+
return this.mcpCall('binlog_search', {
12281228
query,
12291229
limit: maxResults,
12301230
});
@@ -1405,7 +1405,7 @@ export class BinlogTreeDataProvider implements vscode.TreeDataProvider<BinlogTre
14051405
return [this.makeInfoItem('MCP server not connected', 'info')];
14061406
}
14071407
try {
1408-
const result = await this.mcpClient.callTool('binlog_evaluations');
1408+
const result = await this.mcpCall('binlog_evaluations');
14091409
const data = this.tryParseJson(result.text);
14101410
const entries = Array.isArray(data) ? data : [];
14111411

@@ -1456,7 +1456,7 @@ export class BinlogTreeDataProvider implements vscode.TreeDataProvider<BinlogTre
14561456
return [this.makeInfoItem('No evaluation ID', 'info')];
14571457
}
14581458
try {
1459-
const result = await this.mcpClient.callTool('binlog_evaluation_properties', {
1459+
const result = await this.mcpCall('binlog_evaluation_properties', {
14601460
evaluation_id: element.evaluationId,
14611461
});
14621462
const data = this.tryParseJson(result.text);
@@ -1502,7 +1502,7 @@ export class BinlogTreeDataProvider implements vscode.TreeDataProvider<BinlogTre
15021502
return [this.makeInfoItem('No evaluation ID', 'info')];
15031503
}
15041504
try {
1505-
const result = await this.mcpClient.callTool('binlog_evaluation_global_properties', {
1505+
const result = await this.mcpCall('binlog_evaluation_global_properties', {
15061506
evaluation_id: element.evaluationId,
15071507
});
15081508
const data = this.tryParseJson(result.text);
@@ -1559,9 +1559,15 @@ export class BinlogTreeDataProvider implements vscode.TreeDataProvider<BinlogTre
15591559
return [this.makeInfoItem('Loading...', 'loading')];
15601560
}
15611561

1562+
// Auto-inject binlog_file for multi-binlog to avoid
1563+
// "requires explicit binlog_file" errors.
1564+
if (!args.binlog_file && this.binlogPaths.length > 1) {
1565+
args.binlog_file = this.binlogPaths[0];
1566+
}
1567+
15621568
this.loadingSet.add(parentKind);
15631569
try {
1564-
const result = await this.mcpClient.callTool(toolName, args);
1570+
const result = await this.mcpCall(toolName, args);
15651571
this.loadingSet.delete(parentKind);
15661572
const data = parser(result.text);
15671573
if (data.length === 0) {
@@ -1575,6 +1581,18 @@ export class BinlogTreeDataProvider implements vscode.TreeDataProvider<BinlogTre
15751581
}
15761582
}
15771583

1584+
/**
1585+
* Call MCP tool with auto-injected binlog_file for multi-binlog mode.
1586+
* Used by direct callTool sites outside of the callMcpTool helper.
1587+
*/
1588+
private mcpCall(tool: string, args: Record<string, unknown> = {}): Promise<{ text: string }> {
1589+
if (!this.mcpClient) { throw new Error('MCP server not connected'); }
1590+
if (!args.binlog_file && this.binlogPaths.length > 1) {
1591+
args.binlog_file = this.binlogPaths[0];
1592+
}
1593+
return this.mcpClient.callTool(tool, args);
1594+
}
1595+
15781596
// --- Helpers ---
15791597

15801598
private dataToItem(data: TreeNodeData): BinlogTreeItem {

0 commit comments

Comments
 (0)