From 762d66e8192c85504ff9bcb5e3a93ad4cb9b8eb0 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Thu, 28 May 2026 09:52:20 +0200 Subject: [PATCH] shape permission-prompt input rendering per tool Bash, Write, and Edit/MultiEdit each get a tool-appropriate body in the permission card instead of the indented-JSON dump: Bash renders the command as a shell code block with a background/timeout footer, Write shows path + content syntax-highlighted from the extension, and Edit shows path + before/after blocks. Unknown tools keep the JSON fallback. Co-Authored-By: Claude --- CHANGELOG.md | 5 +++ lib/builtin/claude/src/prompt_card.dart | Bin 15166 -> 18181 bytes test/builtin/claude/prompt_card_test.dart | 46 ++++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6abd875..6617bf41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,11 @@ heading, and (b) bumping `pubspec.yaml` `version:` in the same commit. ### Changed +- Permission prompt cards render the tool input in the shape that fits the + tool โ€” Bash shows the command as a shell code block (with a footer for + `run_in_background` / `timeout`), Write shows the path plus the content + highlighted from its extension, Edit shows the path plus before/after + blocks. Unknown tools fall back to the indented-JSON dump. - Claude meta sidebar is now tabbed โ€” Activity / Team / Config (T-182). Activity shows usage stats plus the primary session's live runtime; Team holds the roster and auto-fronts when a team spawns; Config shows the diff --git a/lib/builtin/claude/src/prompt_card.dart b/lib/builtin/claude/src/prompt_card.dart index ade3db72339046b49e49db5f642b9611fd49bf5b..53078c51ec9cc48c0bf50e29a1c8b7a5287ea455 100644 GIT binary patch delta 1941 zcmb_cTW=dh6c%n(Qd1DAL((=;IYd!*t!&qT5K>*-L`ee@g^*sK@}2L@pAUch z`HkOBoT$kGS7f&l65dNmkG3NcA3Uk7yj%JBLp@hpn)3n{`b}sv9;a$m_yb#s9m=Hx zab5pvO&>g|{ZQF&z)i}1nt%!@C?P_~{PCMvWI$rH2Sh=KN(Eh}zB8PG5UPxur%q40JbgoN}`KVgdTUY0?kljvtr;(ek5SVDiUC|A&Dc+49s;svZ1 zg!s?USSNc(eds14%J6g-VUh{%0O11_m=rXW6uKk~C3J|lgDH(`5sqQ6n99HO<*jcv zz)xqQ*u|^6%*QUqxY9SA_x2sQjf8AsvREN$@CZEtBo^rfV8^bZYlB3zyaDhC=C%wg zRx@)pfP|_v;n41&`S2XvjO+T9!Zk=47e0H~!CnyK(`B5XI3gcfU}wE= zS)Pa@!hLHSh=f}z!4WRQ@-kRf-NjRM(+uA(ILZTXp{T?VpXKgXw+jAPcNjHEISL;s z_jY=TNKJeKtrn;x#c^3ls)v>i=>su*w*J3uf2)W z@&c!-|9IQcKP^nHtcA>{Yq$@qW*uxP(!`@pLxKb4?lw(DsF`|M41nn+>r+3Ny#AIF zaT7kxuJt`@_H1_JekT~WE%@tqu3hM+uH7#B*C_ z5`Hcjg@S26170u5=d}M~NFy0ccub>uW^w&lTEBQ{S|QWxZG2d5>)-Ex!B@X45~{c7 z=W=zO*8f~QrLTQ3qyMhWU;bn?j~sYZ=s8E@LozKGwBCK^%-OMYOF+7cCa;hSSwvco zua8}Q4D*wMhXcDKloC(find.byType(ClideCodeBlock)); + expect(block.language, 'bash'); + expect(block.source, 'cat ~/.claude/ide/97632.lock'); + // The raw `description` key shouldn't bleed into the body โ€” it's already + // shown above as the prompt's description line. + expect(find.textContaining('"description"'), findsNothing); + }); + + testWidgets('permission card: Bash shows background/timeout footer when set', (tester) async { + const prompt = ToolPrompt( + promptId: 'req-b2', + toolName: 'Bash', + displayName: 'Bash', + description: 'long task', + input: {'command': 'sleep 30', 'run_in_background': true, 'timeout': 60000}, + ); + await tester.pumpWidget(harness(f, ToolPromptCard(prompt: prompt, onResolve: (_, __) {}))); + await tester.pump(); + expect(find.text('background ยท timeout 60000ms'), findsOneWidget); + }); + + testWidgets('permission card: unknown tool falls back to JSON', (tester) async { + const prompt = ToolPrompt( + promptId: 'req-x', + toolName: 'NovelTool', + displayName: 'NovelTool', + input: {'foo': 'bar'}, + ); + await tester.pumpWidget(harness(f, ToolPromptCard(prompt: prompt, onResolve: (_, __) {}))); + await tester.pump(); + final block = tester.widget(find.byType(ClideCodeBlock)); + expect(block.language, 'json'); + expect(block.source, contains('"foo"')); + }); + testWidgets('permission card: Deny returns DenyTool with a message', (tester) async { ToolDecision? decision; await tester.pumpWidget(harness(