mixdog 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (285) hide show
  1. package/README.md +47 -23
  2. package/package.json +33 -27
  3. package/scripts/_test-folder-dialog.mjs +30 -0
  4. package/scripts/agent-parallel-smoke.mjs +388 -0
  5. package/scripts/agent-tag-reuse-smoke.mjs +183 -0
  6. package/scripts/background-task-meta-smoke.mjs +38 -0
  7. package/scripts/boot-smoke.mjs +52 -9
  8. package/scripts/build-runtime-linux.sh +348 -0
  9. package/scripts/build-runtime-macos.sh +217 -0
  10. package/scripts/build-runtime-windows.ps1 +242 -0
  11. package/scripts/compact-active-turn-test.mjs +68 -0
  12. package/scripts/compact-smoke.mjs +859 -129
  13. package/scripts/compact-trigger-migration-smoke.mjs +187 -0
  14. package/scripts/fix-brief-fn.mjs +35 -0
  15. package/scripts/fix-format-tool-surface.mjs +24 -0
  16. package/scripts/fix-tool-exec-visible.mjs +42 -0
  17. package/scripts/generate-runtime-manifest.mjs +166 -0
  18. package/scripts/hook-bus-test.mjs +330 -0
  19. package/scripts/lead-workflow-smoke.mjs +33 -39
  20. package/scripts/live-worker-smoke.mjs +43 -37
  21. package/scripts/llm-trace-summary.mjs +315 -0
  22. package/scripts/memory-meta-concurrency-test.mjs +20 -0
  23. package/scripts/output-style-smoke.mjs +56 -15
  24. package/scripts/parent-abort-link-test.mjs +44 -0
  25. package/scripts/patch-agent-brief.mjs +48 -0
  26. package/scripts/patch-app.mjs +21 -0
  27. package/scripts/patch-app2.mjs +18 -0
  28. package/scripts/patch-dist-brief.mjs +96 -0
  29. package/scripts/patch-tool-exec.mjs +70 -0
  30. package/scripts/pretool-ask-runtime-test.mjs +54 -0
  31. package/scripts/provider-toolcall-test.mjs +376 -0
  32. package/scripts/reactive-compact-persist-smoke.mjs +124 -0
  33. package/scripts/sanitize-tool-pairs-test.mjs +260 -0
  34. package/scripts/session-context-bench.mjs +344 -0
  35. package/scripts/session-ingest-smoke.mjs +177 -0
  36. package/scripts/set-effort-config-test.mjs +41 -0
  37. package/scripts/smoke-runtime-negative.ps1 +106 -0
  38. package/scripts/smoke-runtime-negative.sh +97 -0
  39. package/scripts/smoke.mjs +25 -0
  40. package/scripts/tool-result-hook-test.mjs +48 -0
  41. package/scripts/tool-smoke.mjs +1223 -95
  42. package/scripts/toolcall-args-test.mjs +150 -0
  43. package/scripts/tui-background-failure-smoke.mjs +73 -0
  44. package/scripts/usage-metrics-epoch-smoke.mjs +114 -0
  45. package/src/agents/debugger/AGENT.md +8 -0
  46. package/src/agents/explore/AGENT.md +4 -0
  47. package/src/agents/heavy-worker/AGENT.md +9 -3
  48. package/src/agents/maintainer/AGENT.md +4 -0
  49. package/src/agents/reviewer/AGENT.md +8 -0
  50. package/src/agents/scheduler-task/AGENT.md +12 -0
  51. package/src/agents/scheduler-task/agent.json +6 -0
  52. package/src/agents/webhook-handler/AGENT.md +12 -0
  53. package/src/agents/webhook-handler/agent.json +6 -0
  54. package/src/agents/worker/AGENT.md +9 -3
  55. package/src/app.mjs +77 -3
  56. package/src/defaults/hidden-roles.json +17 -12
  57. package/src/headless-role.mjs +117 -0
  58. package/src/help.mjs +30 -0
  59. package/src/hooks/lib/permission-evaluator.cjs +11 -475
  60. package/src/lib/keychain-cjs.cjs +9 -1
  61. package/src/lib/mixdog-debug.cjs +0 -7
  62. package/src/lib/rules-builder.cjs +240 -96
  63. package/src/lib/text-utils.cjs +1 -1
  64. package/src/mixdog-session-runtime.mjs +2325 -450
  65. package/src/output-styles/default.md +12 -28
  66. package/src/output-styles/extreme-simple.md +9 -6
  67. package/src/output-styles/simple.md +22 -9
  68. package/src/repl.mjs +118 -59
  69. package/src/rules/agent/00-common.md +15 -0
  70. package/src/rules/{bridge → agent}/20-skip-protocol.md +1 -2
  71. package/src/rules/agent/30-explorer.md +22 -0
  72. package/src/rules/{bridge → agent}/40-cycle1-agent.md +7 -0
  73. package/src/rules/{bridge → agent}/41-cycle2-agent.md +7 -0
  74. package/src/rules/{bridge → agent}/42-cycle3-agent.md +7 -0
  75. package/src/rules/lead/01-general.md +9 -5
  76. package/src/rules/lead/04-workflow.md +51 -12
  77. package/src/rules/lead/lead-tool.md +6 -0
  78. package/src/rules/shared/01-tool.md +12 -1
  79. package/src/runtime/agent/orchestrator/activity-bus.mjs +7 -18
  80. package/src/runtime/agent/orchestrator/agent-owner.mjs +11 -0
  81. package/src/runtime/agent/orchestrator/{smart-bridge/bridge-llm.mjs → agent-runtime/agent-dispatch.mjs} +138 -111
  82. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +94 -0
  83. package/src/runtime/agent/orchestrator/{smart-bridge → agent-runtime}/cache-strategy.mjs +32 -23
  84. package/src/runtime/agent/orchestrator/{smart-bridge → agent-runtime}/session-builder.mjs +33 -27
  85. package/src/runtime/agent/orchestrator/{bridge-trace.mjs → agent-trace.mjs} +132 -81
  86. package/src/runtime/agent/orchestrator/cache-mtime.mjs +0 -21
  87. package/src/runtime/agent/orchestrator/config.mjs +174 -55
  88. package/src/runtime/agent/orchestrator/context/collect.mjs +195 -487
  89. package/src/runtime/agent/orchestrator/dispatch-persist.mjs +1 -1
  90. package/src/runtime/agent/orchestrator/internal-roles.mjs +77 -29
  91. package/src/runtime/agent/orchestrator/internal-tools.mjs +5 -6
  92. package/src/runtime/agent/orchestrator/mcp/client.mjs +15 -9
  93. package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +7 -1
  94. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +377 -243
  95. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +146 -93
  96. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +236 -4
  97. package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +49 -0
  98. package/src/runtime/agent/orchestrator/providers/gemini.mjs +58 -13
  99. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +67 -149
  100. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +132 -2
  101. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +4 -1
  102. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +45 -0
  103. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +61 -116
  104. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +25 -0
  105. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +79 -255
  106. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +221 -70
  107. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +477 -147
  108. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +343 -496
  109. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +6 -6
  110. package/src/runtime/agent/orchestrator/providers/registry.mjs +88 -51
  111. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +31 -11
  112. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +41 -8
  113. package/src/runtime/agent/orchestrator/session/cache/post-edit-marks.mjs +4 -4
  114. package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +1 -1
  115. package/src/runtime/agent/orchestrator/session/compact.mjs +1173 -267
  116. package/src/runtime/agent/orchestrator/session/context-utils.mjs +199 -36
  117. package/src/runtime/agent/orchestrator/session/loop.mjs +851 -674
  118. package/src/runtime/agent/orchestrator/session/manager.mjs +1593 -466
  119. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +107 -0
  120. package/src/runtime/agent/orchestrator/session/store.mjs +291 -46
  121. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +2 -2
  122. package/src/runtime/agent/orchestrator/stall-policy.mjs +31 -16
  123. package/src/runtime/agent/orchestrator/tool-loop-guard.mjs +3 -219
  124. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +34 -7
  125. package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +1 -1
  126. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +19 -0
  127. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +9 -9
  128. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +60 -37
  129. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +21 -2
  130. package/src/runtime/agent/orchestrator/tools/builtin/device-paths.mjs +1 -1
  131. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -7
  132. package/src/runtime/agent/orchestrator/tools/builtin/glob-walk.mjs +1 -3
  133. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +36 -12
  134. package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +2 -0
  135. package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -2
  136. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +5 -12
  137. package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +1 -1
  138. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +4 -36
  139. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +2 -40
  140. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +148 -27
  141. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +2 -2
  142. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +43 -75
  143. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +90 -20
  144. package/src/runtime/agent/orchestrator/tools/builtin/snapshot-store.mjs +1 -1
  145. package/src/runtime/agent/orchestrator/tools/builtin.mjs +59 -5
  146. package/src/runtime/agent/orchestrator/tools/code-graph-state.mjs +86 -0
  147. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +11 -11
  148. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4106 -4019
  149. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +33 -4
  150. package/src/runtime/agent/orchestrator/tools/patch.mjs +90 -6
  151. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +6 -4
  152. package/src/runtime/agent/orchestrator/tools/result-compression.mjs +4 -4
  153. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +8 -1
  154. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -4
  155. package/src/runtime/channels/index.mjs +152 -24
  156. package/src/runtime/channels/lib/scheduler.mjs +18 -14
  157. package/src/runtime/channels/lib/session-discovery.mjs +3 -2
  158. package/src/runtime/channels/lib/tool-format.mjs +0 -2
  159. package/src/runtime/channels/lib/transcript-discovery.mjs +3 -2
  160. package/src/runtime/channels/lib/webhook.mjs +1 -1
  161. package/src/runtime/channels/tool-defs.mjs +29 -29
  162. package/src/runtime/memory/index.mjs +635 -107
  163. package/src/runtime/memory/lib/agent-ipc.mjs +29 -12
  164. package/src/runtime/memory/lib/core-memory-store.mjs +2 -2
  165. package/src/runtime/memory/lib/embedding-model-config.mjs +55 -0
  166. package/src/runtime/memory/lib/embedding-provider.mjs +31 -4
  167. package/src/runtime/memory/lib/embedding-worker.mjs +19 -10
  168. package/src/runtime/memory/lib/memory-cycle1.mjs +38 -17
  169. package/src/runtime/memory/lib/memory-cycle2.mjs +6 -7
  170. package/src/runtime/memory/lib/memory-cycle3.mjs +4 -4
  171. package/src/runtime/memory/lib/memory-ops-policy.mjs +2 -1
  172. package/src/runtime/memory/lib/memory-session-merge.mjs +38 -0
  173. package/src/runtime/memory/lib/memory.mjs +88 -9
  174. package/src/runtime/memory/lib/model-profile.mjs +1 -1
  175. package/src/runtime/memory/lib/pg/adapter.mjs +15 -1
  176. package/src/runtime/memory/lib/pg/supervisor.mjs +12 -0
  177. package/src/runtime/memory/lib/runtime-fetcher.mjs +37 -3
  178. package/src/runtime/memory/lib/session-ingest.mjs +194 -0
  179. package/src/runtime/memory/lib/trace-store.mjs +96 -51
  180. package/src/runtime/memory/tool-defs.mjs +46 -37
  181. package/src/runtime/search/index.mjs +102 -466
  182. package/src/runtime/search/lib/web-tools.mjs +45 -25
  183. package/src/runtime/search/tool-defs.mjs +16 -23
  184. package/src/runtime/shared/abort-controller.mjs +1 -1
  185. package/src/runtime/shared/atomic-file.mjs +4 -3
  186. package/src/runtime/shared/background-tasks.mjs +122 -11
  187. package/src/runtime/shared/child-spawn-gate.mjs +145 -0
  188. package/src/runtime/shared/config.mjs +7 -4
  189. package/src/runtime/shared/err-text.mjs +131 -4
  190. package/src/runtime/shared/llm/cost.mjs +2 -2
  191. package/src/runtime/shared/llm/http-agent.mjs +23 -7
  192. package/src/runtime/shared/llm/index.mjs +34 -11
  193. package/src/runtime/shared/llm/usage-log.mjs +4 -4
  194. package/src/runtime/shared/markdown-frontmatter.mjs +56 -0
  195. package/src/runtime/shared/singleton-owner.mjs +104 -0
  196. package/src/runtime/shared/tool-execution-contract.mjs +199 -20
  197. package/src/runtime/shared/tool-execution-contract.test.mjs +183 -0
  198. package/src/runtime/shared/tool-surface.mjs +624 -98
  199. package/src/runtime/shared/user-data-guard.mjs +0 -2
  200. package/src/standalone/agent-task-status.mjs +203 -0
  201. package/src/standalone/agent-task-status.test.mjs +76 -0
  202. package/src/standalone/agent-tool.mjs +1913 -0
  203. package/src/standalone/channel-worker.mjs +370 -14
  204. package/src/standalone/explore-tool.mjs +165 -70
  205. package/src/standalone/folder-dialog.mjs +314 -0
  206. package/src/standalone/hook-bus.mjs +898 -22
  207. package/src/standalone/memory-runtime-proxy.mjs +320 -0
  208. package/src/standalone/projects.mjs +226 -0
  209. package/src/standalone/provider-admin.mjs +41 -24
  210. package/src/standalone/seeds.mjs +2 -69
  211. package/src/standalone/usage-dashboard.mjs +96 -8
  212. package/src/tui/App.jsx +4798 -2153
  213. package/src/tui/components/AnsiText.jsx +39 -28
  214. package/src/tui/components/ContextPanel.jsx +87 -29
  215. package/src/tui/components/Markdown.jsx +43 -77
  216. package/src/tui/components/MarkdownTable.jsx +9 -184
  217. package/src/tui/components/Message.jsx +28 -11
  218. package/src/tui/components/Picker.jsx +95 -56
  219. package/src/tui/components/PromptInput.jsx +367 -239
  220. package/src/tui/components/QueuedCommands.jsx +1 -1
  221. package/src/tui/components/SlashCommandPalette.jsx +27 -21
  222. package/src/tui/components/Spinner.jsx +67 -38
  223. package/src/tui/components/StatusLine.jsx +606 -38
  224. package/src/tui/components/TextEntryPanel.jsx +128 -9
  225. package/src/tui/components/ToolExecution.jsx +617 -368
  226. package/src/tui/components/TurnDone.jsx +3 -3
  227. package/src/tui/components/UsagePanel.jsx +3 -5
  228. package/src/tui/components/tool-output-format.mjs +365 -0
  229. package/src/tui/components/tool-output-format.test.mjs +220 -0
  230. package/src/tui/dist/index.mjs +8915 -2418
  231. package/src/tui/engine-runtime-notification.test.mjs +115 -0
  232. package/src/tui/engine-tool-result-text.test.mjs +75 -0
  233. package/src/tui/engine.mjs +1455 -279
  234. package/src/tui/figures.mjs +21 -40
  235. package/src/tui/index.jsx +75 -31
  236. package/src/tui/input-editing.mjs +25 -0
  237. package/src/tui/markdown/format-token.mjs +511 -68
  238. package/src/tui/markdown/format-token.test.mjs +216 -0
  239. package/src/tui/markdown/render-ansi.mjs +94 -0
  240. package/src/tui/markdown/render-ansi.test.mjs +108 -0
  241. package/src/tui/markdown/stream-fence.mjs +34 -0
  242. package/src/tui/markdown/stream-fence.test.mjs +26 -0
  243. package/src/tui/markdown/table-layout.mjs +250 -0
  244. package/src/tui/paste-attachments.mjs +0 -7
  245. package/src/tui/spinner-verbs.mjs +1 -2
  246. package/src/tui/statusline-ansi-bridge.mjs +172 -0
  247. package/src/tui/statusline-ansi-bridge.test.mjs +159 -0
  248. package/src/tui/theme.mjs +746 -24
  249. package/src/tui/time-format.mjs +1 -1
  250. package/src/tui/transcript-tool-failures.mjs +67 -0
  251. package/src/tui/transcript-tool-failures.test.mjs +111 -0
  252. package/src/ui/ansi.mjs +1 -2
  253. package/src/ui/markdown.mjs +85 -26
  254. package/src/ui/markdown.test.mjs +70 -0
  255. package/src/ui/model-display.mjs +121 -0
  256. package/src/ui/session-stats.mjs +44 -0
  257. package/src/ui/statusline-context-label.test.mjs +15 -0
  258. package/src/ui/statusline.mjs +386 -178
  259. package/src/ui/tool-card.mjs +3 -16
  260. package/src/vendor/statusline/bin/statusline-lib.mjs +8 -4
  261. package/src/vendor/statusline/bin/statusline-route.mjs +169 -37
  262. package/src/vendor/statusline/bin/statusline-route.test.mjs +80 -0
  263. package/src/vendor/statusline/scripts/lib/gateway-settings.mjs +3 -3
  264. package/src/vendor/statusline/src/gateway/claude-current.mjs +1 -1
  265. package/src/vendor/statusline/src/gateway/route-meta.mjs +44 -6
  266. package/src/vendor/statusline/src/gateway/session-routes.mjs +1 -1
  267. package/src/workflows/default/WORKFLOW.md +12 -5
  268. package/src/workflows/default/workflow.json +0 -1
  269. package/src/workflows/solo/WORKFLOW.md +15 -0
  270. package/src/workflows/solo/workflow.json +7 -0
  271. package/vendor/ink/build/output.js +6 -1
  272. package/src/agents/scheduler-task.md +0 -3
  273. package/src/agents/web-researcher/AGENT.md +0 -3
  274. package/src/agents/web-researcher/agent.json +0 -6
  275. package/src/agents/webhook-handler.md +0 -3
  276. package/src/rules/bridge/00-common.md +0 -5
  277. package/src/rules/bridge/30-explorer.md +0 -4
  278. package/src/rules/lead/00-tool-lead.md +0 -5
  279. package/src/rules/shared/00-language.md +0 -3
  280. package/src/runtime/agent/orchestrator/tools/builtin/native-edit-runner.mjs +0 -110
  281. package/src/runtime/agent/orchestrator/tools/mutation-content-cache.mjs +0 -67
  282. package/src/runtime/memory/lib/bridge-trace-queries.mjs +0 -120
  283. package/src/runtime/shared/llm/pid-cleanup.mjs +0 -27
  284. package/src/standalone/bridge-tool.mjs +0 -1414
  285. package/src/tui/runtime/shared/process-shutdown.mjs +0 -1
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * components/QueuedCommands.jsx — queued steering prompts.
3
3
  *
4
- * Forked from Claude Code's PromptInputQueuedCommands: lines the user typed
4
+ * Queued steering prompts: lines the user typed
5
5
  * while a turn is busy wait here as dim rows, pinned just above the input box
6
6
  * (part of the input cluster) instead of polluting the transcript. Each queued
7
7
  * line is promoted to a real transcript user row only when it starts executing
@@ -1,18 +1,19 @@
1
- /**
2
- * components/SlashCommandPalette.jsx — live slash command palette.
3
- *
4
- * This is intentionally display-only: PromptInput owns keyboard focus and
5
- * forwards arrow / enter / escape decisions to App, so the prompt caret stays
6
- * anchored while the palette updates above it.
7
- */
1
+ /**
2
+ * components/SlashCommandPalette.jsx — live slash command palette.
3
+ *
4
+ * This is intentionally display-only: PromptInput owns keyboard focus and
5
+ * forwards arrow / enter / escape decisions to App, so the prompt caret stays
6
+ * anchored while the palette updates above it.
7
+ */
8
8
  import React from 'react';
9
9
  import { Box, Text } from 'ink';
10
10
  import stringWidth from 'string-width';
11
11
  import { theme } from '../theme.mjs';
12
-
12
+
13
13
  const MAX_VISIBLE = 8;
14
14
  const COMMAND_LABEL_WIDTH = 18;
15
15
  const SLASH_HELP = '↑/↓ select · ←/→ change · Enter run · Esc cancel';
16
+ const SLASH_DESCRIPTION = 'Type to filter · Enter runs the highlighted command.';
16
17
  const ACRONYM_LABELS = new Map([
17
18
  ['mcp', 'MCP'],
18
19
  ]);
@@ -43,7 +44,7 @@ function queryMatchesAlias(command, query) {
43
44
 
44
45
  function commandDisplayLabel(command, query) {
45
46
  const usage = String(command?.usage || command?.name || '').replace(/^\/+/, '');
46
- const aliasText = queryMatchesAlias(command, query) && Array.isArray(command?.aliasUsage) && command.aliasUsage.length > 0
47
+ const aliasText = command?.showAliasUsage !== false && queryMatchesAlias(command, query) && Array.isArray(command?.aliasUsage) && command.aliasUsage.length > 0
47
48
  ? ` (${command.aliasUsage.join(', ')})`
48
49
  : '';
49
50
  const label = ACRONYM_LABELS.get(usage.toLowerCase()) || (usage ? `${usage.charAt(0).toUpperCase()}${usage.slice(1)}` : usage);
@@ -62,20 +63,25 @@ export function SlashCommandPalette({ commands, selectedIndex = 0, title = 'Comm
62
63
  const blankRows = Math.max(0, MAX_VISIBLE - visible.length);
63
64
  const labelWidth = Math.max(12, Math.min(COMMAND_LABEL_WIDTH, Math.max(12, Math.floor(columns * 0.45))));
64
65
  const descriptionWidth = Math.max(0, columns - labelWidth - 12);
65
-
66
- return (
67
- <Box flexDirection="column" flexShrink={0} width="100%">
68
- <Box
69
- flexDirection="column"
70
- borderStyle="round"
71
- borderColor={theme.promptBorder}
72
- paddingX={1}
73
- width="100%"
74
- >
75
- <Box flexDirection="row" justifyContent="space-between" marginBottom={1}>
66
+ // Standard panel rhythm: title row, blank, description/hint row, blank, content.
67
+ const description = truncateText(SLASH_DESCRIPTION, Math.max(0, columns - 4));
68
+
69
+ return (
70
+ <Box flexDirection="column" flexShrink={0} width="100%">
71
+ <Box
72
+ flexDirection="column"
73
+ borderStyle="round"
74
+ borderColor={theme.promptBorder}
75
+ paddingX={1}
76
+ width="100%"
77
+ >
78
+ <Box flexDirection="row" justifyContent="space-between">
76
79
  <Text color={theme.panelTitle}>{title}</Text>
77
80
  <Text color={theme.subtle}>{SLASH_HELP}</Text>
78
81
  </Box>
82
+ <Text> </Text>
83
+ <Text color={theme.subtle}>{description || ' '}</Text>
84
+ <Text> </Text>
79
85
  {visible.map((item, index) => (
80
86
  <CommandRow
81
87
  key={item.name}
@@ -93,7 +99,7 @@ export function SlashCommandPalette({ commands, selectedIndex = 0, title = 'Comm
93
99
  </Box>
94
100
  );
95
101
  }
96
-
102
+
97
103
  const CommandRow = React.memo(function CommandRow({ command, isSelected, labelWidth, descriptionWidth, query }) {
98
104
  const label = truncateText(commandDisplayLabel(command, query), labelWidth);
99
105
  const description = truncateText(command.description, descriptionWidth);
@@ -1,23 +1,19 @@
1
1
  /**
2
- * components/Spinner.jsx — the "thinking" indicator (Claude Code shape).
2
+ * components/Spinner.jsx — the "thinking" indicator.
3
3
  *
4
- * Ported from Claude Code's SpinnerAnimationRow + SpinnerGlyph + GlimmerMessage:
5
- * - frames = [...DEFAULT_CHARACTERS, ...reverse] so it sweeps forward then
6
- * back (CC SpinnerGlyph SPINNER_FRAMES).
7
- * - the glyph sits in a `width={2}` box (CC's <Box height={1} width={2}>).
8
- * - verb… verb shimmer: traveling highlight in claudeShimmer with
9
- * mode-aware glimmer speed (CC GlimmerMessage).
4
+ * Spinner row layout:
5
+ * - frames sweep forward then back.
6
+ * - the glyph sits in a `width={2}` box.
7
+ * - verb… verb shimmer: traveling highlight with mode-aware glimmer speed.
10
8
  * - stall detection with exponential smoothing: intensity fades in/out over
11
- * 2s (CC useStalledAnimation).
12
- * - thinking shimmer: left-to-right "thinking" label after delay (CC
13
- * ThinkingShimmerText, inlined from useAnimationFrame).
9
+ * 2s fade.
10
+ * - thinking shimmer: left-to-right "thinking" label after delay.
14
11
  * - progressive width gating: timer/tokens/thinking shown left→right only if
15
- * they fit after the previous segments (CC SpinnerAnimationRow).
12
+ * they fit after the previous segments.
16
13
  * - token counter animation: smooth increment toward the current turn's
17
- * output token count, shown Claude Code style as a single "<glyph> N
18
- * tokens" segment (CC SpinnerAnimationRow + SpinnerModeGlyph). The glyph
14
+ * output token count as a single "<glyph> N tokens" segment. The glyph
19
15
  * is mode-driven: up while requesting, down otherwise. Input totals hidden.
20
- * - elided duration formatting (CC formatDuration: "0:25" after 60s).
16
+ * - elided duration formatting ("0:25" after 60s).
21
17
  * - mode prop: 'responding' | 'thinking' | 'tool-use' | 'tool-input' |
22
18
  * 'requesting' | 'compacting' | 'resuming' (default 'responding').
23
19
  */
@@ -29,21 +25,21 @@ import { DOWN_ARROW, UP_ARROW } from '../figures.mjs';
29
25
  import { formatDuration } from '../time-format.mjs';
30
26
 
31
27
  const FRAME_MS = 130;
32
- // CC plays the frames forward, then in reverse — a smooth there-and-back sweep.
28
+ // Play frames forward, then in reverse — a smooth there-and-back sweep.
33
29
  const FRAMES = [...SPINNER_FRAMES, ...[...SPINNER_FRAMES].reverse()];
34
30
 
35
31
  // Stall: response must grow within this window or the glyph reddens.
36
32
  const STALL_TIMEOUT_MS = 3000;
37
- const STALL_FADE_MS = 2000; // CC fades red over 2s
38
- // Claude Code hides elapsed/token meta on short turns unless verbose/teammates
33
+ const STALL_FADE_MS = 2000; // fade red over 2s
34
+ // Hide elapsed/token meta on short turns unless verbose/teammates
39
35
  // are active. Mixdog has no spinner verbose/teammate row here, so mirror the
40
36
  // default 30s threshold.
41
37
  const SHOW_TOKENS_AFTER_MS = 30_000;
42
- // Thinking shimmer starts after this delay (CC THINKING_DELAY_MS).
38
+ // Thinking shimmer starts after this delay.
43
39
  const THINKING_DELAY_MS = 3000;
44
40
 
45
41
  // One-way shimmer. The tail runs past the final character before restarting.
46
- const GLIMMER_SPEED_MS = { requesting: 70, compacting: 120, resuming: 120, 'tool-use': 120, responding: 120, thinking: 120, 'tool-input': 120 };
42
+ const GLIMMER_SPEED_MS = { requesting: 70, compacting: 120, 'auto-clear': 120, resuming: 120, 'tool-use': 120, responding: 120, thinking: 120, 'tool-input': 120 };
47
43
  const GLIMMER_TRAIL = 4;
48
44
  const THINKING_GLIMMER_SPEED_MS = 120;
49
45
  const THINKING_GLIMMER_TRAIL = 4;
@@ -54,6 +50,7 @@ const VERB_CHANGE_PROBABILITY = 0.65;
54
50
  const MODE_VERBS = {
55
51
  requesting: ['Requesting', 'Preparing', 'Routing'],
56
52
  compacting: ['Compacting conversation'],
53
+ 'auto-clear': ['Auto-clearing conversation'],
57
54
  resuming: ['Resuming conversation'],
58
55
  thinking: ['Thinking', 'Reasoning', 'Mapping'],
59
56
  'tool-use': ['Using tools', 'Checking files', 'Running tools', 'Reading output'],
@@ -109,11 +106,19 @@ function renderShimmerText(text, head, trail, baseRgb, shimmerRgb, baseColor, ke
109
106
  );
110
107
  }
111
108
 
112
- const TEXT_RGB = parseRgb(theme.spinnerText) ?? parseRgb(theme.text);
113
- const SHIMMER_RGB = parseRgb(theme.spinnerShimmer) ?? parseRgb(theme.claudeShimmer);
114
- const SPINNER_GLYPH_RGB = parseRgb(theme.spinnerGlyph) ?? { r: 240, g: 240, b: 240 };
115
- const THINKING_INACTIVE = parseRgb(theme.thinkingBase) ?? parseRgb(theme.thinkingAccent) ?? { r: 153, g: 153, b: 153 };
116
- const THINKING_SHIMMER = parseRgb(theme.thinkingGlow) ?? { r: 255, g: 205, b: 175 };
109
+ // Parsed RGB tuples are derived from the active theme. They are resolved per
110
+ // render (cheap regex parse) so a live `/theme` switch — which mutates `theme`
111
+ // in-place and re-renders the tree picks up the new shimmer/glyph colors
112
+ // instead of the colors captured at module load.
113
+ function spinnerRgb() {
114
+ return {
115
+ TEXT_RGB: parseRgb(theme.spinnerText) ?? parseRgb(theme.text),
116
+ SHIMMER_RGB: parseRgb(theme.spinnerShimmer) ?? parseRgb(theme.claudeShimmer),
117
+ SPINNER_GLYPH_RGB: parseRgb(theme.spinnerGlyph) ?? { r: 240, g: 240, b: 240 },
118
+ THINKING_INACTIVE: parseRgb(theme.thinkingBase) ?? parseRgb(theme.thinkingAccent) ?? { r: 153, g: 153, b: 153 },
119
+ THINKING_SHIMMER: parseRgb(theme.thinkingGlow) ?? { r: 255, g: 205, b: 175 },
120
+ };
121
+ }
117
122
 
118
123
  const compactNumberFormatter = new Intl.NumberFormat('en-US', {
119
124
  notation: 'compact',
@@ -147,8 +152,27 @@ function chooseNextVerb(mode, fallback, current) {
147
152
  return candidates[Math.floor(Math.random() * candidates.length)];
148
153
  }
149
154
 
155
+ // Token-direction glyph by mode:
156
+ // up while uploading the request, down while receiving a response, and no
157
+ // glyph at all for non-streaming modes (compacting/resuming/auto-clear) where
158
+ // the arrow direction would be meaningless.
159
+ function tokenModeGlyph(mode) {
160
+ switch (mode) {
161
+ case 'tool-input':
162
+ case 'tool-use':
163
+ case 'responding':
164
+ case 'thinking':
165
+ return DOWN_ARROW;
166
+ case 'requesting':
167
+ return UP_ARROW;
168
+ default:
169
+ return '';
170
+ }
171
+ }
172
+
150
173
  export function Spinner({ verb = 'Working', startedAt, outputTokens = 0, tokens = 0, thinking = false, thinkingActiveSince = 0, mode = 'responding', columns = 80, marginTop = 1 }) {
151
174
  useAnimation({ interval: FRAME_MS });
175
+ const { TEXT_RGB, SHIMMER_RGB, SPINNER_GLYPH_RGB, THINKING_INACTIVE, THINKING_SHIMMER } = spinnerRgb();
152
176
  const now = Date.now();
153
177
  const elapsedMs = startedAt ? Math.max(0, now - startedAt) : 0;
154
178
  const frame = Math.floor(elapsedMs / FRAME_MS);
@@ -158,7 +182,7 @@ export function Spinner({ verb = 'Working', startedAt, outputTokens = 0, tokens
158
182
  const displayVerbRef = useRef('');
159
183
  const displayVerbModeRef = useRef('');
160
184
  const nextVerbCheckRef = useRef(0);
161
- // Stall smoothing refs (CC useStalledAnimation exponential fade)
185
+ // Stall smoothing refs (exponential fade)
162
186
  const stallSmoothRef = useRef(0);
163
187
  const lastStallTickRef = useRef(0);
164
188
 
@@ -173,7 +197,7 @@ export function Spinner({ verb = 'Working', startedAt, outputTokens = 0, tokens
173
197
 
174
198
  const stallMs = now - lastGrowRef.current;
175
199
  const isStalled = targetOutputTokens > 0 && stallMs > STALL_TIMEOUT_MS;
176
- // Stall smoothing: exponential fade toward target (CC useStalledAnimation)
200
+ // Stall smoothing: exponential fade toward target
177
201
  const rawIntensity = isStalled
178
202
  ? Math.min(1, (stallMs - STALL_TIMEOUT_MS) / STALL_FADE_MS)
179
203
  : 0;
@@ -194,7 +218,7 @@ export function Spinner({ verb = 'Working', startedAt, outputTokens = 0, tokens
194
218
 
195
219
  const glyph = FRAMES[frame % FRAMES.length];
196
220
 
197
- // Glyph color — interpolate toward red when stalled (CC SpinnerGlyph).
221
+ // Glyph color — interpolate toward red when stalled.
198
222
  const glyphColor = stalledIntensity > 0
199
223
  ? toRgbString(interpolateColor(
200
224
  SPINNER_GLYPH_RGB,
@@ -203,7 +227,7 @@ export function Spinner({ verb = 'Working', startedAt, outputTokens = 0, tokens
203
227
  ))
204
228
  : theme.spinnerGlyph;
205
229
 
206
- // --- Verb shimmer (CC GlimmerMessage traveling highlight) ---
230
+ // --- Verb shimmer (traveling highlight) ---
207
231
  if (displayVerbModeRef.current !== mode || !displayVerbRef.current) {
208
232
  displayVerbModeRef.current = mode;
209
233
  displayVerbRef.current = stableModeVerb(mode, verb);
@@ -242,18 +266,20 @@ export function Spinner({ verb = 'Working', startedAt, outputTokens = 0, tokens
242
266
  return Math.round(ref.current);
243
267
  };
244
268
 
245
- // Token counter animation — Claude Code shows a single "<glyph> N tokens"
246
- // segment (SpinnerAnimationRow + SpinnerModeGlyph). N is the output/response
269
+ // Token counter animation — single "<glyph> N tokens" segment. N is the output/response
247
270
  // token count, smoothly incremented toward the current turn's value. The
248
271
  // glyph is mode-driven: up while requesting, down otherwise (responding,
249
272
  // thinking, tool-use, tool-input). Input token totals are not shown.
250
273
  const displayedOutputTokens = advanceCounter(displayedOutputRef, targetOutputTokens);
251
274
 
252
- const tokenGlyph = mode === 'requesting' ? UP_ARROW : DOWN_ARROW;
253
- const tokenText = displayedOutputTokens > 0 ? `${tokenGlyph} ${formatNumber(displayedOutputTokens)} tokens` : '';
275
+ const tokenGlyph = tokenModeGlyph(mode);
276
+ const tokenCountText = formatNumber(displayedOutputTokens);
277
+ const tokenText = displayedOutputTokens > 0
278
+ ? (tokenGlyph ? `${tokenGlyph} ${tokenCountText} tokens` : `${tokenCountText} tokens`)
279
+ : '';
254
280
  const tokenW = tokenText.length;
255
281
 
256
- // Progressive width gating (CC SpinnerAnimationRow): show status parts
282
+ // Progressive width gating: show status parts
257
283
  // left→right, each only if it fits after the previous ones. Timer/tokens are
258
284
  // hidden for short turns by default; thinking status can still show alone.
259
285
  const avail = columns - messageLen - 5; // glyph(2) + ' (' + ')'
@@ -266,17 +292,20 @@ export function Spinner({ verb = 'Working', startedAt, outputTokens = 0, tokens
266
292
  ? 'thinking'
267
293
  : '';
268
294
  const thinkingStatusW = thinkingStatusText.length;
269
- const wantsTimerAndTokens = elapsedMs > SHOW_TOKENS_AFTER_MS;
295
+ // Turn elapsed time is the headline metric here, not a thinking sub-stat, so
296
+ // it shows from 1s onward (formatDuration returns '' below 1s). Tokens keep
297
+ // Short-turn gate: tokens only appear once the turn runs long.
298
+ const wantsTokens = elapsedMs > SHOW_TOKENS_AFTER_MS;
270
299
 
271
- // Claude Code gives thinking display priority for narrow widths, but renders
300
+ // Thinking display priority for narrow widths, but renders
272
301
  // it after timer/tokens in the final byline.
273
302
  const showThinkingStatus = Boolean(thinkingStatusText) && avail > thinkingStatusW;
274
303
  const usedAfterThinking = showThinkingStatus ? thinkingStatusW + SEP_WIDTH : 0;
275
- const showTimer = wantsTimerAndTokens && Boolean(timerLabel) && avail > usedAfterThinking + timerW;
304
+ const showTimer = Boolean(timerLabel) && avail > usedAfterThinking + timerW;
276
305
  const usedAfterTimer = usedAfterThinking + (showTimer ? timerW + SEP_WIDTH : 0);
277
- const showTokens = wantsTimerAndTokens && tokenText && avail > usedAfterTimer + tokenW;
306
+ const showTokens = wantsTokens && tokenText && avail > usedAfterTimer + tokenW;
278
307
 
279
- // Build meta line segments — elapsed, tokens, thinking (Claude Code order).
308
+ // Build meta line segments — elapsed, tokens, thinking.
280
309
  const segments = [];
281
310
  if (showTimer) {
282
311
  segments.push(