From 04b8289debc4dfc1682276e9f17ef632b53dc02a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Feb 2026 18:36:36 +0000 Subject: [PATCH 1/3] Initial plan From 4d9378aafa0134818a5c284c8dfb53af94665cef Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Feb 2026 18:49:08 +0000 Subject: [PATCH 2/3] Add formatted JSONL file viewer feature Co-authored-by: rajbos <6085745+rajbos@users.noreply.github.com> --- src/extension.ts | 65 +++++++++++++++++++++++++++++++++ src/webview/diagnostics/main.ts | 17 ++++++++- 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 6f82f50..40d2386 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -3141,6 +3141,61 @@ class CopilotTokenTracker implements vscode.Disposable { }); } + /** + * Opens a JSONL file in a formatted view with array brackets and commas. + * Does not modify the original file. + */ + public async showFormattedJsonlFile(sessionFilePath: string): Promise { + try { + // Read the file content + const fileContent = await fs.promises.readFile(sessionFilePath, 'utf-8'); + + // Parse JSONL into array of objects + const lines = fileContent.trim().split('\n').filter(line => line.trim().length > 0); + const jsonObjects: any[] = []; + + for (const line of lines) { + try { + const obj = JSON.parse(line); + jsonObjects.push(obj); + } catch (e) { + // Skip malformed lines + this.warn(`Skipping malformed line in ${sessionFilePath}: ${e}`); + } + } + + // Format as JSON array + const formattedJson = JSON.stringify(jsonObjects, null, 2); + + // Create an untitled document with the formatted content + const fileName = path.basename(sessionFilePath, path.extname(sessionFilePath)); + const prettyUri = vscode.Uri.parse(`untitled:${fileName}-formatted.json`); + + // Check if this document is already open + const openDoc = vscode.workspace.textDocuments.find(d => d.uri.toString() === prettyUri.toString()); + if (openDoc) { + await vscode.window.showTextDocument(openDoc, { preview: true }); + return; + } + + // Create and open the document + const doc = await vscode.workspace.openTextDocument(prettyUri); + const editor = await vscode.window.showTextDocument(doc, { preview: true }); + + // Insert the formatted JSON + await editor.edit((editBuilder) => { + editBuilder.insert(new vscode.Position(0, 0), formattedJson); + }); + + // Set language mode to JSON for syntax highlighting + await vscode.languages.setTextDocumentLanguage(doc, 'json'); + + } catch (error) { + this.error(`Error formatting JSONL file ${sessionFilePath}:`, error); + throw error; + } + } + private getLogViewerHtml(webview: vscode.Webview, logData: SessionLogData): string { const nonce = this.getNonce(); const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this.extensionUri, 'dist', 'webview', 'logviewer.js')); @@ -3599,6 +3654,16 @@ class CopilotTokenTracker implements vscode.Disposable { } break; + case 'openFormattedJsonlFile': + if (message.file) { + try { + await this.showFormattedJsonlFile(message.file); + } catch (err) { + vscode.window.showErrorMessage('Could not open formatted file: ' + message.file); + } + } + break; + case 'revealPath': if (message.path) { try { diff --git a/src/webview/diagnostics/main.ts b/src/webview/diagnostics/main.ts index 6b46688..357c5cd 100644 --- a/src/webview/diagnostics/main.ts +++ b/src/webview/diagnostics/main.ts @@ -280,6 +280,7 @@ function renderSessionTable(detailedFiles: SessionFileDetails[], isLoading: bool Context Refs First Interaction${getSortIndicator('firstInteraction')} Last Interaction${getSortIndicator('lastInteraction')} + Actions @@ -295,6 +296,9 @@ function renderSessionTable(detailedFiles: SessionFileDetails[], isLoading: bool ${sanitizeNumber(getTotalContextRefs(sf.contextReferences))} ${formatDate(sf.firstInteraction)} ${formatDate(sf.lastInteraction)} + + 📄 View + `).join('')} @@ -710,8 +714,8 @@ function renderLayout(data: DiagnosticsData): void { color: #9aa0a6; margin-top: 4px; } - .session-file-link, .reveal-link { color: #4FC3F7; text-decoration: underline; cursor: pointer; } - .session-file-link:hover, .reveal-link:hover { color: #81D4FA; } + .session-file-link, .reveal-link, .view-formatted-link { color: #4FC3F7; text-decoration: underline; cursor: pointer; } + .session-file-link:hover, .reveal-link:hover, .view-formatted-link:hover { color: #81D4FA; } .empty-session-link { color: #999; } .empty-session-link:hover { color: #aaa; } .button-group { display: flex; gap: 12px; margin-top: 16px; flex-wrap: wrap; } @@ -1043,6 +1047,15 @@ function setupStorageLinkHandlers(): void { }); }); + // View formatted JSONL link handlers + document.querySelectorAll('.view-formatted-link').forEach(link => { + link.addEventListener('click', (e) => { + e.preventDefault(); + const file = decodeURIComponent((link as HTMLElement).getAttribute('data-file') || ''); + vscode.postMessage({ command: 'openFormattedJsonlFile', file }); + }); + }); + // Reveal link handlers document.querySelectorAll('.reveal-link').forEach(link => { link.addEventListener('click', (e) => { From 90bdd32ba76e9902c417cfdea4d23fa585541ec3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Feb 2026 18:51:08 +0000 Subject: [PATCH 3/3] Address code review feedback - improve type safety and error handling Co-authored-by: rajbos <6085745+rajbos@users.noreply.github.com> --- src/extension.ts | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 40d2386..c1ec145 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -3152,15 +3152,15 @@ class CopilotTokenTracker implements vscode.Disposable { // Parse JSONL into array of objects const lines = fileContent.trim().split('\n').filter(line => line.trim().length > 0); - const jsonObjects: any[] = []; + const jsonObjects: unknown[] = []; - for (const line of lines) { + for (let i = 0; i < lines.length; i++) { try { - const obj = JSON.parse(line); + const obj = JSON.parse(lines[i]); jsonObjects.push(obj); } catch (e) { - // Skip malformed lines - this.warn(`Skipping malformed line in ${sessionFilePath}: ${e}`); + // Skip malformed lines with detailed warning + this.warn(`Skipping malformed line ${i + 1} in ${sessionFilePath}: ${e}`); } } @@ -3171,11 +3171,15 @@ class CopilotTokenTracker implements vscode.Disposable { const fileName = path.basename(sessionFilePath, path.extname(sessionFilePath)); const prettyUri = vscode.Uri.parse(`untitled:${fileName}-formatted.json`); - // Check if this document is already open + // Check if this document is already open and close it to refresh const openDoc = vscode.workspace.textDocuments.find(d => d.uri.toString() === prettyUri.toString()); if (openDoc) { - await vscode.window.showTextDocument(openDoc, { preview: true }); - return; + // Close the existing document so we can create a fresh one with updated content + const editor = vscode.window.visibleTextEditors.find(e => e.document === openDoc); + if (editor) { + await vscode.window.showTextDocument(openDoc, editor.viewColumn); + await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + } } // Create and open the document @@ -3659,7 +3663,8 @@ class CopilotTokenTracker implements vscode.Disposable { try { await this.showFormattedJsonlFile(message.file); } catch (err) { - vscode.window.showErrorMessage('Could not open formatted file: ' + message.file); + const errorMsg = err instanceof Error ? err.message : String(err); + vscode.window.showErrorMessage('Could not open formatted file: ' + message.file + ' (' + errorMsg + ')'); } } break;