From bd088ec71c831a7c750c35004d2eea221020312d Mon Sep 17 00:00:00 2001 From: matt Date: Tue, 17 Feb 2026 20:31:06 +0900 Subject: [PATCH 1/4] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0899f470..d47e53a4 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@

- claude-devtools - See everything Claude Code hides from your terminal | Product Hunt + claude-devtools - See everything Claude Code hides from your terminal | Product Hunt

From 94f722d993dca4e8aa6cd2f3197cbe7d3bdcba9c Mon Sep 17 00:00:00 2001 From: Sanath Samarasinghe Date: Thu, 19 Feb 2026 06:03:16 +0100 Subject: [PATCH 2/4] feat: add markdown preview toggle for Write tool (#21) --- .../chat/items/linkedTool/WriteToolViewer.tsx | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/renderer/components/chat/items/linkedTool/WriteToolViewer.tsx b/src/renderer/components/chat/items/linkedTool/WriteToolViewer.tsx index 22cd90b1..d08d7005 100644 --- a/src/renderer/components/chat/items/linkedTool/WriteToolViewer.tsx +++ b/src/renderer/components/chat/items/linkedTool/WriteToolViewer.tsx @@ -6,7 +6,7 @@ import React from 'react'; -import { CodeBlockViewer } from '@renderer/components/chat/viewers'; +import { CodeBlockViewer, MarkdownViewer } from '@renderer/components/chat/viewers'; import type { LinkedToolItem } from '@renderer/types/groups'; @@ -20,13 +20,47 @@ export const WriteToolViewer: React.FC = ({ linkedTool }) const filePath = (toolUseResult?.filePath as string) || (linkedTool.input.file_path as string); const content = (toolUseResult?.content as string) || (linkedTool.input.content as string) || ''; const isCreate = toolUseResult?.type === 'create'; + const isMarkdownFile = /\.mdx?$/i.test(filePath); + const [viewMode, setViewMode] = React.useState<'code' | 'preview'>('code'); return (
{isCreate ? 'Created file' : 'Wrote to file'}
- + {isMarkdownFile && ( +
+ + +
+ )} + {isMarkdownFile && viewMode === 'preview' ? ( + + ) : ( + + )}
); }; From 4ec272758cfddf65dcaa8f427f0e974f89adf96f Mon Sep 17 00:00:00 2001 From: Cesar Augusto Fonseca <38327047+cesarafonseca@users.noreply.github.com> Date: Thu, 19 Feb 2026 02:07:30 -0300 Subject: [PATCH 3/4] fix: collect tool results from subagent messages with absent isMeta field (#23) User messages in subagent JSONLs lack the isMeta field, defaulting to false. An unconditional `continue` in the !isMeta branch skipped tool result collection for these messages, causing all subagent tools to show "No result received". Now we check for tool_result blocks before continuing, allowing them to fall through to the result collection logic. --- src/renderer/utils/displayItemBuilder.ts | 10 +- .../renderer/utils/displayItemBuilder.test.ts | 99 +++++++++++++++++++ 2 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 test/renderer/utils/displayItemBuilder.test.ts diff --git a/src/renderer/utils/displayItemBuilder.ts b/src/renderer/utils/displayItemBuilder.ts index 953dc645..07b4e786 100644 --- a/src/renderer/utils/displayItemBuilder.ts +++ b/src/renderer/utils/displayItemBuilder.ts @@ -423,16 +423,20 @@ export function buildDisplayItemsFromMessages( } continue; } - // Plain-text user message (subagent input prompt) - if (rawText.trim()) { + // Only treat as subagent input if there are NO tool_result blocks in this message + const hasToolResults = + Array.isArray(msg.content) && + msg.content.some((b) => b.type === 'tool_result'); + if (rawText.trim() && !hasToolResults) { displayItems.push({ type: 'subagent_input', content: rawText.trim(), timestamp: msgTimestamp, tokenCount: estimateTokens(rawText), }); + continue; } - continue; + // Fall through to tool result processing below if message has tool_results } if (msg.type === 'assistant' && Array.isArray(msg.content)) { diff --git a/test/renderer/utils/displayItemBuilder.test.ts b/test/renderer/utils/displayItemBuilder.test.ts new file mode 100644 index 00000000..1a843713 --- /dev/null +++ b/test/renderer/utils/displayItemBuilder.test.ts @@ -0,0 +1,99 @@ +import { describe, expect, it } from 'vitest'; +import { buildDisplayItemsFromMessages } from '../../../src/renderer/utils/displayItemBuilder'; +import type { ParsedMessage } from '../../../src/main/types/messages'; + +/** + * Helper to create a minimal ParsedMessage for testing. + */ +function makeMessage(overrides: Partial & Pick): ParsedMessage { + return { + uuid: `msg-${Math.random().toString(36).slice(2, 8)}`, + parentUuid: null, + timestamp: new Date('2025-01-01T00:00:00Z'), + isMeta: false, + isSidechain: false, + toolCalls: [], + toolResults: [], + ...overrides, + } as ParsedMessage; +} + +describe('buildDisplayItemsFromMessages', () => { + describe('subagent tool results with isMeta=false', () => { + it('should collect tool results from user messages without isMeta field', () => { + // Simulates real subagent JSONL where user messages with tool_result + // blocks have isMeta absent (defaults to false after parsing). + const toolUseId = 'toolu_test123'; + + const assistantMsg = makeMessage({ + uuid: 'assistant-1', + type: 'assistant', + content: [ + { + type: 'tool_use', + id: toolUseId, + name: 'Bash', + input: { command: 'echo hello' }, + }, + ], + timestamp: new Date('2025-01-01T00:00:00Z'), + }); + + // This is the key scenario: user message with tool_result but isMeta: false + // (simulating subagent JSONL where isMeta field is absent) + const toolResultMsg = makeMessage({ + uuid: 'user-result-1', + type: 'user', + isMeta: false, + content: [ + { + type: 'tool_result', + tool_use_id: toolUseId, + content: 'hello\n', + is_error: false, + }, + ], + toolResults: [ + { + toolUseId: toolUseId, + content: 'hello\n', + isError: false, + }, + ], + timestamp: new Date('2025-01-01T00:00:01Z'), + }); + + const items = buildDisplayItemsFromMessages([assistantMsg, toolResultMsg], []); + + const toolItems = items.filter((item) => item.type === 'tool'); + expect(toolItems).toHaveLength(1); + + const tool = toolItems[0]; + if (tool.type !== 'tool') throw new Error('Expected tool item'); + + // The critical assertion: result must be present, not orphaned + expect(tool.tool.isOrphaned).toBe(false); + expect(tool.tool.result).toBeDefined(); + expect(tool.tool.result?.content).toBe('hello\n'); + expect(tool.tool.name).toBe('Bash'); + }); + + it('should still render subagent_input for plain text user messages without tool results', () => { + const userMsg = makeMessage({ + uuid: 'user-input-1', + type: 'user', + isMeta: false, + content: 'Please run the tests', + toolResults: [], + timestamp: new Date('2025-01-01T00:00:00Z'), + }); + + const items = buildDisplayItemsFromMessages([userMsg], []); + + const inputItems = items.filter((item) => item.type === 'subagent_input'); + expect(inputItems).toHaveLength(1); + if (inputItems[0].type !== 'subagent_input') throw new Error('Expected subagent_input'); + expect(inputItems[0].content).toBe('Please run the tests'); + }); + }); +}); From e570bbebde6971c8c28003afaeaf9051d2bc136b Mon Sep 17 00:00:00 2001 From: matt Date: Fri, 20 Feb 2026 01:27:10 +0900 Subject: [PATCH 4/4] feat(package): add afterInstall script for chrome-sandbox permissions (#27) (#28) - Updated package.json to include an afterInstall script that adjusts permissions for the chrome-sandbox on Linux. - Added new afterInstall.sh script to ensure proper ownership and permissions for the sandbox file, enhancing security and functionality. --- package.json | 3 ++- resources/afterInstall.sh | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100755 resources/afterInstall.sh diff --git a/package.json b/package.json index ac50ab09..4a9093b0 100644 --- a/package.json +++ b/package.json @@ -161,7 +161,8 @@ "pacman" ], "icon": "resources/icons/png", - "category": "Development" + "category": "Development", + "afterInstall": "resources/afterInstall.sh" }, "nsis": { "oneClick": false, diff --git a/resources/afterInstall.sh b/resources/afterInstall.sh new file mode 100755 index 00000000..4f472b3a --- /dev/null +++ b/resources/afterInstall.sh @@ -0,0 +1,10 @@ +#!/bin/bash +# Fix chrome-sandbox permissions for SUID sandbox on Linux +# See: https://github.com/electron/electron/issues/17972 + +SANDBOX_PATH="/opt/${productFilename}/chrome-sandbox" + +if [ -f "$SANDBOX_PATH" ]; then + chown root:root "$SANDBOX_PATH" + chmod 4755 "$SANDBOX_PATH" +fi