Hide tool call rows so the transcript shows only the conversation. Toggle with /quiet.

Hides tool call rows, running ones included, so the transcript shows only the conversation. Only the drawing changes: the stored transcript and the model's context stay as they were.
/quiet toggles it. The choice is kept across sessionsquietctrl+o still shows the full transcript, background task rows included/plugin install quiet --marketplace kimjisub/agent-kit
Checked on Claude Code 2.1.304. It needs a Claude Code build with function hooks (hooks/hooks.json naming a modules entry).
Tests: claude plugin test plugins/quiet.
도구 호출 줄(실행 중인 것 포함)을 숨겨 대화만 보이게 합니다. 화면에 그리는 것만 바뀌고, 저장된 대화와 모델이 보는 맥락은 그대로입니다. /quiet 로 켜고 끄며, 고른 상태는 다음 세션에도 남습니다. 켜져 있으면 아래 모드 표시에 quiet 가 붙습니다. ctrl+o 로는 전체 기록을 그대로 볼 수 있습니다.
hooks/register.ts 74 lines1// quiet: hides tool call rows, running ones included, so the transcript shows only the conversation.
2// Only the drawing changes; the stored transcript and the model's context stay as they were.
3import type { EngineInterface, Register, RenderInput } from 'claude-code'
4
5const COMMAND = 'quiet'
6const STORE_KEY = 'enabled'
7
8// Module variables start over on every load, so the stored choice is read lazily.
9let quiet: boolean | undefined
10
11async function isQuiet($: EngineInterface): Promise<boolean> {
12 if (quiet === undefined) {
13 quiet = await $.store.get(STORE_KEY).then(
14 value => value === true,
15 () => false,
16 )
17 }
18 return quiet
19}
20
21function hiddenRow($: EngineInterface, e: RenderInput) {
22 const { Box } = $.ui.resolve(e)
23 return Box({ display: 'none' })
24}
25
26export const register: Register = on => {
27 on('session.start', async ($, e, next) => {
28 quiet = undefined
29 await isQuiet($)
30 await $.command.register({
31 name: COMMAND,
32 description: 'Toggle hiding tool call rows so only the conversation shows',
33 // Only a drawing flag changes, so it can run while a turn streams.
34 immediate: true,
35 })
36 return next(e)
37 })
38
39 on('command.run', { command: COMMAND }, async $ => {
40 const next = !(await isQuiet($))
41 try {
42 await $.store.set(STORE_KEY, next)
43 } catch (error) {
44 $.ui.toast(`Quiet unchanged: ${error instanceof Error ? error.message : String(error)}`)
45 return {}
46 }
47 quiet = next
48 $.ui.invalidate('ui.render')
49 $.ui.toast(next ? 'Quiet on: tool rows hidden' : 'Quiet off: tool rows shown')
50 return {}
51 })
52
53 // The footer's mode labels say Quiet is on, beside the engine's own (`focus`, ...).
54 on('ui.render', { component: 'SessionMode' }, async ($, e, next) =>
55 (await isQuiet($)) ? next({ ...e, props: { ...e.props, modes: [...e.props.modes, COMMAND] } }) : next(e),
56 )
57 on('ui.render', { component: 'ToolUse' }, async ($, e, next) =>
58 (await isQuiet($)) ? hiddenRow($, e) : next(e),
59 )
60 on('ui.render', { component: 'ToolResult' }, async ($, e, next) =>
61 (await isQuiet($)) ? hiddenRow($, e) : next(e),
62 )
63 on('ui.render', { component: 'ToolGroup' }, async ($, e, next) =>
64 (await isQuiet($)) ? hiddenRow($, e) : next(e),
65 )
66 // A background task's `finished` row is the tail of a tool call; ctrl+o still shows it.
67 on('ui.render', { component: 'UserMessage', props: { origin: { kind: 'task-notification' } } }, async ($, e, next) =>
68 (await isQuiet($)) && !e.props.isExpanded ? hiddenRow($, e) : next(e),
69 )
70 on('ui.render', { component: 'ToolProgress' }, async ($, e, next) =>
71 (await isQuiet($)) ? hiddenRow($, e) : next(e),
72 )
73}
74