mixdog 0.9.0 → 0.9.2

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 (240) hide show
  1. package/package.json +10 -3
  2. package/scripts/_bench-cwc.json +20 -0
  3. package/scripts/agent-loop-policy-test.mjs +37 -0
  4. package/scripts/agent-parallel-smoke.mjs +54 -10
  5. package/scripts/background-task-meta-smoke.mjs +1 -1
  6. package/scripts/bench-run.mjs +262 -0
  7. package/scripts/compact-smoke.mjs +12 -0
  8. package/scripts/compact-trigger-migration-smoke.mjs +67 -1
  9. package/scripts/ingest-pure-conversation-smoke.mjs +148 -0
  10. package/scripts/internal-comms-bench.mjs +727 -0
  11. package/scripts/internal-comms-smoke.mjs +75 -0
  12. package/scripts/lead-workflow-smoke.mjs +4 -4
  13. package/scripts/live-worker-smoke.mjs +9 -9
  14. package/scripts/output-style-bench.mjs +285 -0
  15. package/scripts/output-style-smoke.mjs +13 -10
  16. package/scripts/patch-replay.mjs +90 -0
  17. package/scripts/provider-stream-stall-test.mjs +276 -0
  18. package/scripts/provider-toolcall-test.mjs +599 -1
  19. package/scripts/routing-corpus.mjs +281 -0
  20. package/scripts/session-bench.mjs +1526 -0
  21. package/scripts/session-diag.mjs +595 -0
  22. package/scripts/session-ingest-smoke.mjs +2 -2
  23. package/scripts/task-bench.mjs +207 -0
  24. package/scripts/tool-failures.mjs +6 -6
  25. package/scripts/tool-smoke.mjs +306 -66
  26. package/scripts/toolcall-args-test.mjs +81 -0
  27. package/src/agents/debugger/AGENT.md +4 -4
  28. package/src/agents/heavy-worker/AGENT.md +4 -2
  29. package/src/agents/reviewer/AGENT.md +4 -4
  30. package/src/agents/worker/AGENT.md +4 -2
  31. package/src/app.mjs +10 -6
  32. package/src/defaults/{hidden-roles.json → agents.json} +7 -7
  33. package/src/examples/schedules/SCHEDULE.example.md +32 -0
  34. package/src/examples/webhooks/WEBHOOK.example.md +40 -0
  35. package/src/headless-role.mjs +14 -14
  36. package/src/help.mjs +1 -0
  37. package/src/lib/mixdog-debug.cjs +0 -22
  38. package/src/lib/plugin-paths.cjs +1 -7
  39. package/src/lib/rules-builder.cjs +34 -56
  40. package/src/mixdog-session-runtime.mjs +710 -319
  41. package/src/output-styles/default.md +12 -7
  42. package/src/output-styles/minimal.md +25 -0
  43. package/src/output-styles/oneline.md +21 -0
  44. package/src/output-styles/simple.md +10 -9
  45. package/src/repl.mjs +12 -4
  46. package/src/rules/agent/00-common.md +7 -5
  47. package/src/rules/agent/30-explorer.md +7 -8
  48. package/src/rules/lead/01-general.md +3 -1
  49. package/src/rules/lead/lead-tool.md +7 -0
  50. package/src/rules/shared/01-tool.md +17 -12
  51. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +90 -32
  52. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +32 -0
  53. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +18 -6
  54. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +23 -20
  55. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +48 -14
  56. package/src/runtime/agent/orchestrator/agent-trace.mjs +87 -12
  57. package/src/runtime/agent/orchestrator/config.mjs +3 -0
  58. package/src/runtime/agent/orchestrator/context/collect.mjs +131 -67
  59. package/src/runtime/agent/orchestrator/{internal-roles.mjs → internal-agents.mjs} +72 -72
  60. package/src/runtime/agent/orchestrator/internal-tools.mjs +13 -26
  61. package/src/runtime/agent/orchestrator/mcp/client.mjs +94 -16
  62. package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +7 -0
  63. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +188 -0
  64. package/src/runtime/agent/orchestrator/providers/anthropic-leaked-toolcall.mjs +444 -0
  65. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +359 -106
  66. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +63 -51
  67. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +27 -20
  68. package/src/runtime/agent/orchestrator/providers/gemini.mjs +184 -17
  69. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +8 -1
  70. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +18 -8
  71. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +210 -21
  72. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +86 -30
  73. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +254 -280
  74. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +191 -50
  75. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +18 -0
  76. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +11 -5
  77. package/src/runtime/agent/orchestrator/providers/registry.mjs +2 -1
  78. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +265 -1
  79. package/src/runtime/agent/orchestrator/session/compact.mjs +560 -51
  80. package/src/runtime/agent/orchestrator/session/context-utils.mjs +250 -3
  81. package/src/runtime/agent/orchestrator/session/loop.mjs +394 -132
  82. package/src/runtime/agent/orchestrator/session/manager.mjs +217 -170
  83. package/src/runtime/agent/orchestrator/session/store.mjs +4 -4
  84. package/src/runtime/agent/orchestrator/session/tool-envelope.mjs +61 -0
  85. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +5 -0
  86. package/src/runtime/agent/orchestrator/stall-policy.mjs +63 -15
  87. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +1 -1
  88. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +194 -32
  89. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +143 -0
  90. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +1 -44
  91. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +34 -18
  92. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +0 -0
  93. package/src/runtime/agent/orchestrator/tools/builtin/list-formatting.mjs +10 -0
  94. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +5 -4
  95. package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +15 -0
  96. package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +9 -44
  97. package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -1
  98. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +13 -4
  99. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +10 -17
  100. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +18 -2
  101. package/src/runtime/agent/orchestrator/tools/builtin/shell-output.mjs +3 -2
  102. package/src/runtime/agent/orchestrator/tools/builtin/tool-output-limit.mjs +10 -0
  103. package/src/runtime/agent/orchestrator/tools/builtin.mjs +59 -1
  104. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +5 -5
  105. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4076 -3985
  106. package/src/runtime/agent/orchestrator/tools/patch.mjs +116 -2
  107. package/src/runtime/channels/backends/discord.mjs +99 -9
  108. package/src/runtime/channels/backends/telegram.mjs +501 -0
  109. package/src/runtime/channels/index.mjs +441 -1254
  110. package/src/runtime/channels/lib/cli-worker-host.mjs +1 -8
  111. package/src/runtime/channels/lib/config.mjs +54 -3
  112. package/src/runtime/channels/lib/drop-trace.mjs +1 -1
  113. package/src/runtime/channels/lib/executor.mjs +0 -3
  114. package/src/runtime/channels/lib/format.mjs +4 -2
  115. package/src/runtime/channels/lib/memory-client.mjs +0 -38
  116. package/src/runtime/channels/lib/output-forwarder.mjs +77 -71
  117. package/src/runtime/channels/lib/runtime-paths.mjs +29 -6
  118. package/src/runtime/channels/lib/scheduler.mjs +1 -1
  119. package/src/runtime/channels/lib/session-discovery.mjs +0 -4
  120. package/src/runtime/channels/lib/telegram-format.mjs +283 -0
  121. package/src/runtime/channels/lib/tool-format.mjs +1 -2
  122. package/src/runtime/channels/lib/transcript-discovery.mjs +20 -11
  123. package/src/runtime/channels/lib/webhook.mjs +59 -31
  124. package/src/runtime/channels/tool-defs.mjs +1 -1
  125. package/src/runtime/lib/keychain-cjs.cjs +0 -1
  126. package/src/runtime/memory/data/runtime-manifest.json +6 -7
  127. package/src/runtime/memory/index.mjs +187 -43
  128. package/src/runtime/memory/lib/agent-ipc.mjs +2 -2
  129. package/src/runtime/memory/lib/core-memory-store.mjs +1 -1
  130. package/src/runtime/memory/lib/llm-worker-host.mjs +0 -4
  131. package/src/runtime/memory/lib/memory-cycle1.mjs +1 -1
  132. package/src/runtime/memory/lib/memory-cycle2.mjs +9 -6
  133. package/src/runtime/memory/lib/memory-cycle3.mjs +1 -1
  134. package/src/runtime/memory/lib/memory-ops-policy.mjs +0 -1
  135. package/src/runtime/memory/lib/memory.mjs +101 -4
  136. package/src/runtime/memory/lib/pg/adapter.mjs +139 -15
  137. package/src/runtime/memory/lib/runtime-fetcher.mjs +43 -18
  138. package/src/runtime/memory/lib/session-ingest.mjs +116 -7
  139. package/src/runtime/memory/lib/trace-store.mjs +69 -22
  140. package/src/runtime/memory/tool-defs.mjs +6 -3
  141. package/src/runtime/search/index.mjs +2 -7
  142. package/src/runtime/search/lib/config.mjs +0 -4
  143. package/src/runtime/search/lib/state.mjs +1 -15
  144. package/src/runtime/search/lib/web-tools.mjs +0 -1
  145. package/src/runtime/shared/channel-notification-routing.mjs +12 -0
  146. package/src/runtime/shared/channel-notification-routing.test.mjs +45 -0
  147. package/src/runtime/shared/child-spawn-gate.mjs +0 -6
  148. package/src/runtime/shared/config.mjs +9 -0
  149. package/src/runtime/shared/llm/http-agent.mjs +12 -5
  150. package/src/runtime/shared/schedules-store.mjs +21 -19
  151. package/src/runtime/shared/tool-surface.mjs +98 -13
  152. package/src/runtime/shared/transcript-writer.mjs +129 -0
  153. package/src/runtime/shared/update-checker.mjs +214 -0
  154. package/src/standalone/agent-tool.mjs +255 -109
  155. package/src/standalone/channel-admin.mjs +133 -40
  156. package/src/standalone/channel-worker.mjs +8 -291
  157. package/src/standalone/explore-tool.mjs +2 -2
  158. package/src/standalone/memory-runtime-proxy.mjs +3 -1
  159. package/src/standalone/provider-admin.mjs +11 -0
  160. package/src/standalone/seeds.mjs +1 -11
  161. package/src/standalone/usage-dashboard.mjs +1 -1
  162. package/src/tui/App.jsx +2137 -750
  163. package/src/tui/components/ConfirmBar.jsx +47 -0
  164. package/src/tui/components/ContextPanel.jsx +5 -3
  165. package/src/tui/components/ItemRightHintOverprint.jsx +54 -0
  166. package/src/tui/components/Markdown.jsx +22 -98
  167. package/src/tui/components/Message.jsx +14 -35
  168. package/src/tui/components/Picker.jsx +87 -12
  169. package/src/tui/components/PromptInput.jsx +146 -9
  170. package/src/tui/components/QueuedCommands.jsx +1 -1
  171. package/src/tui/components/SlashCommandPalette.jsx +8 -5
  172. package/src/tui/components/Spinner.jsx +7 -7
  173. package/src/tui/components/StatusLine.jsx +40 -21
  174. package/src/tui/components/TextEntryPanel.jsx +51 -7
  175. package/src/tui/components/ToolExecution.jsx +177 -100
  176. package/src/tui/components/TurnDone.jsx +4 -4
  177. package/src/tui/components/UsagePanel.jsx +1 -1
  178. package/src/tui/components/tool-output-format.mjs +312 -40
  179. package/src/tui/components/tool-output-format.test.mjs +180 -1
  180. package/src/tui/display-width.mjs +69 -0
  181. package/src/tui/display-width.test.mjs +35 -0
  182. package/src/tui/dist/index.mjs +7324 -2393
  183. package/src/tui/engine.mjs +287 -126
  184. package/src/tui/index.jsx +117 -7
  185. package/src/tui/keyboard-protocol.mjs +42 -0
  186. package/src/tui/lib/voice-recorder.mjs +453 -0
  187. package/src/tui/markdown/format-token.mjs +354 -142
  188. package/src/tui/markdown/format-token.test.mjs +155 -17
  189. package/src/tui/markdown/measure-rendered-rows.mjs +85 -0
  190. package/src/tui/markdown/render-ansi.test.mjs +1 -1
  191. package/src/tui/markdown/streaming-markdown.mjs +167 -0
  192. package/src/tui/markdown/streaming-markdown.test.mjs +70 -0
  193. package/src/tui/markdown/table-layout.mjs +9 -9
  194. package/src/tui/paste-attachments.mjs +0 -11
  195. package/src/tui/prompt-history-store.mjs +129 -0
  196. package/src/tui/prompt-history-store.test.mjs +52 -0
  197. package/src/tui/statusline-ansi-bridge.test.mjs +3 -3
  198. package/src/tui/theme.mjs +41 -647
  199. package/src/tui/themes/base.mjs +86 -0
  200. package/src/tui/themes/basic.mjs +85 -0
  201. package/src/tui/themes/catppuccin.mjs +72 -0
  202. package/src/tui/themes/dracula.mjs +70 -0
  203. package/src/tui/themes/everforest.mjs +71 -0
  204. package/src/tui/themes/gruvbox.mjs +71 -0
  205. package/src/tui/themes/index.mjs +71 -0
  206. package/src/tui/themes/indigo.mjs +78 -0
  207. package/src/tui/themes/kanagawa.mjs +80 -0
  208. package/src/tui/themes/light.mjs +81 -0
  209. package/src/tui/themes/nord.mjs +72 -0
  210. package/src/tui/themes/onedark.mjs +16 -0
  211. package/src/tui/themes/rosepine.mjs +70 -0
  212. package/src/tui/themes/teal.mjs +81 -0
  213. package/src/tui/themes/tokyonight.mjs +79 -0
  214. package/src/tui/themes/utils.mjs +106 -0
  215. package/src/tui/themes/warm.mjs +79 -0
  216. package/src/tui/transcript-tool-failures.mjs +13 -2
  217. package/src/ui/markdown.mjs +1 -1
  218. package/src/ui/model-display.mjs +2 -2
  219. package/src/ui/statusline.mjs +26 -27
  220. package/src/vendor/statusline/bin/statusline-lib.mjs +0 -623
  221. package/src/vendor/statusline/bin/statusline-route.mjs +5 -12
  222. package/src/vendor/statusline/src/gateway/claude-current.mjs +3 -3
  223. package/src/vendor/statusline/src/gateway/route-meta.mjs +30 -16
  224. package/src/workflows/default/WORKFLOW.md +39 -12
  225. package/src/workflows/sequential/WORKFLOW.md +46 -0
  226. package/src/workflows/solo/WORKFLOW.md +7 -0
  227. package/vendor/ink/build/display-width.js +62 -0
  228. package/vendor/ink/build/ink.js +154 -20
  229. package/vendor/ink/build/measure-text.js +4 -1
  230. package/vendor/ink/build/output.js +115 -9
  231. package/vendor/ink/build/render-node-to-output.js +4 -1
  232. package/vendor/ink/build/render.js +4 -0
  233. package/src/hooks/lib/permission-rules.cjs +0 -170
  234. package/src/hooks/lib/settings-loader.cjs +0 -112
  235. package/src/lib/hook-pipe-path.cjs +0 -10
  236. package/src/output-styles/extreme-simple.md +0 -20
  237. package/src/rules/lead/04-workflow.md +0 -51
  238. package/src/runtime/channels/lib/hook-pipe-server.mjs +0 -671
  239. package/src/workflows/default/workflow.json +0 -13
  240. package/src/workflows/solo/workflow.json +0 -7
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Persistent TUI prompt input history, scoped by project working directory.
3
+ * Stored under MIXDOG data dir (not the repo working tree).
4
+ */
5
+ import { createHash } from 'node:crypto';
6
+ import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
7
+ import { dirname, join, resolve } from 'node:path';
8
+ import { resolvePluginData } from '../runtime/shared/plugin-paths.mjs';
9
+
10
+ export const PROMPT_HISTORY_LIMIT = 50;
11
+
12
+ export function promptHistoryKey(value) {
13
+ return String(value || '').trim().replace(/\s+/g, ' ');
14
+ }
15
+
16
+ /** Stable map key for cwd-scoped prompt history buckets. */
17
+ export function promptHistoryCwdKey(rawPath) {
18
+ const text = String(rawPath || '').trim();
19
+ if (!text) return '';
20
+ const abs = resolve(text);
21
+ return process.platform === 'win32'
22
+ ? abs.replace(/[\\/]+$/, '').toLowerCase()
23
+ : abs.replace(/\/+$/, '');
24
+ }
25
+
26
+ function historyFilePath(cwd) {
27
+ const key = promptHistoryCwdKey(cwd);
28
+ if (!key) return '';
29
+ const digest = createHash('sha256').update(key, 'utf8').digest('hex').slice(0, 32);
30
+ return join(resolvePluginData(), 'tui-prompt-history', `${digest}.json`);
31
+ }
32
+
33
+ function readEntries(filePath) {
34
+ if (!filePath || !existsSync(filePath)) return [];
35
+ try {
36
+ const parsed = JSON.parse(readFileSync(filePath, 'utf8'));
37
+ const entries = Array.isArray(parsed?.entries) ? parsed.entries : [];
38
+ return entries
39
+ .map((entry) => String(entry || '').trim())
40
+ .filter((entry) => promptHistoryKey(entry));
41
+ } catch {
42
+ return [];
43
+ }
44
+ }
45
+
46
+ function writeEntries(filePath, entries) {
47
+ if (!filePath) return false;
48
+ try {
49
+ // Prompt text may be sensitive; restrict directory/file to the owning user.
50
+ mkdirSync(dirname(filePath), { recursive: true, mode: 0o700 });
51
+ const payload = { version: 1, entries };
52
+ const tmp = `${filePath}.${process.pid}.tmp`;
53
+ writeFileSync(tmp, `${JSON.stringify(payload, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
54
+ renameSync(tmp, filePath);
55
+ try {
56
+ chmodSync(filePath, 0o600);
57
+ } catch {
58
+ /* best-effort; Windows may ignore mode bits */
59
+ }
60
+ return true;
61
+ } catch {
62
+ return false;
63
+ }
64
+ }
65
+
66
+ /**
67
+ * In-memory session list (newest first). Dedupes by promptHistoryKey.
68
+ */
69
+ export function pushSessionPromptHistory(sessionTexts, value, limit = PROMPT_HISTORY_LIMIT) {
70
+ const text = String(value || '').trim();
71
+ const key = promptHistoryKey(text);
72
+ if (!key) return Array.isArray(sessionTexts) ? sessionTexts : [];
73
+ const base = Array.isArray(sessionTexts) ? sessionTexts : [];
74
+ const next = base.filter((entry) => promptHistoryKey(entry) !== key);
75
+ next.unshift(text);
76
+ return next.length > limit ? next.slice(0, limit) : next;
77
+ }
78
+
79
+ /**
80
+ * Merge session-derived prompts (newest first) with persisted prompts from
81
+ * older sessions. Dedupes by promptHistoryKey; session wins ordering.
82
+ */
83
+ export function buildMergedPromptHistory(sessionTexts, persistedTexts, limit = PROMPT_HISTORY_LIMIT) {
84
+ const seen = new Set();
85
+ const history = [];
86
+
87
+ const push = (raw) => {
88
+ if (history.length >= limit) return;
89
+ const text = String(raw || '').trim();
90
+ const key = promptHistoryKey(text);
91
+ if (!key || seen.has(key)) return;
92
+ seen.add(key);
93
+ history.push(text);
94
+ };
95
+
96
+ for (const text of sessionTexts) push(text);
97
+
98
+ const persisted = Array.isArray(persistedTexts) ? persistedTexts : [];
99
+ for (let i = persisted.length - 1; i >= 0; i -= 1) {
100
+ push(persisted[i]);
101
+ }
102
+
103
+ return history;
104
+ }
105
+
106
+ export function loadPromptHistory(cwd) {
107
+ const filePath = historyFilePath(cwd);
108
+ const entries = readEntries(filePath);
109
+ if (entries.length <= PROMPT_HISTORY_LIMIT) return entries;
110
+ return entries.slice(-PROMPT_HISTORY_LIMIT);
111
+ }
112
+
113
+ /**
114
+ * Append a submitted prompt for cwd. Skips empty keys; removes prior duplicate
115
+ * keys (promptHistoryKey); caps at PROMPT_HISTORY_LIMIT.
116
+ * Returns the saved entry list (oldest → newest) on success, or null on failure.
117
+ */
118
+ export function appendPromptHistory(cwd, value) {
119
+ const key = promptHistoryKey(value);
120
+ if (!key) return null;
121
+ const filePath = historyFilePath(cwd);
122
+ const entries = readEntries(filePath).filter((entry) => promptHistoryKey(entry) !== key);
123
+ entries.push(String(value).trim());
124
+ const trimmed = entries.length > PROMPT_HISTORY_LIMIT
125
+ ? entries.slice(-PROMPT_HISTORY_LIMIT)
126
+ : entries;
127
+ return writeEntries(filePath, trimmed) ? trimmed : null;
128
+ }
129
+
@@ -0,0 +1,52 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { mkdtempSync, rmSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { tmpdir } from 'node:os';
6
+ import {
7
+ appendPromptHistory,
8
+ buildMergedPromptHistory,
9
+ loadPromptHistory,
10
+ pushSessionPromptHistory,
11
+ promptHistoryKey,
12
+ } from './prompt-history-store.mjs';
13
+
14
+ test('promptHistoryKey normalizes whitespace', () => {
15
+ assert.equal(promptHistoryKey(' hello world '), 'hello world');
16
+ assert.equal(promptHistoryKey(' '), '');
17
+ });
18
+
19
+ test('buildMergedPromptHistory prefers session order and dedupes', () => {
20
+ const merged = buildMergedPromptHistory(
21
+ ['newest', 'older'],
22
+ ['stale', 'newest', 'from-disk'],
23
+ 50,
24
+ );
25
+ assert.deepEqual(merged, ['newest', 'older', 'from-disk', 'stale']);
26
+ });
27
+
28
+ test('appendPromptHistory persists per cwd across reload', () => {
29
+ const dataDir = mkdtempSync(join(tmpdir(), 'mixdog-prompt-history-'));
30
+ const previous = process.env.MIXDOG_DATA_DIR;
31
+ process.env.MIXDOG_DATA_DIR = dataDir;
32
+ try {
33
+ const cwd = join(dataDir, 'project-a');
34
+ assert.deepEqual(appendPromptHistory(cwd, 'first prompt'), ['first prompt']);
35
+ assert.deepEqual(appendPromptHistory(cwd, 'second prompt'), ['first prompt', 'second prompt']);
36
+ assert.deepEqual(appendPromptHistory(cwd, 'first prompt'), ['second prompt', 'first prompt']);
37
+ const loaded = loadPromptHistory(cwd);
38
+ assert.deepEqual(loaded, ['second prompt', 'first prompt']);
39
+ } finally {
40
+ if (previous === undefined) delete process.env.MIXDOG_DATA_DIR;
41
+ else process.env.MIXDOG_DATA_DIR = previous;
42
+ rmSync(dataDir, { recursive: true, force: true });
43
+ }
44
+ });
45
+
46
+ test('pushSessionPromptHistory is cwd-session newest-first with dedupe', () => {
47
+ let session = pushSessionPromptHistory([], 'alpha');
48
+ session = pushSessionPromptHistory(session, 'beta');
49
+ session = pushSessionPromptHistory(session, 'alpha');
50
+ assert.deepEqual(session, ['alpha', 'beta']);
51
+ });
52
+
@@ -143,17 +143,17 @@ test('stats reset transition is footer identity change (cache must not reuse on
143
143
  });
144
144
 
145
145
  test('theme switch changes normalized output when raw line is canonical', () => {
146
- setThemeSetting('mixdog', { persist: false });
146
+ setThemeSetting('basic', { persist: false });
147
147
  const colorsA = statusColorsFromTheme();
148
148
  const raw = `${sgr38(...STATUSLINE_CANONICAL_TRUECOLOR.success)}▓▓\x1b[0m ${sgr38(...STATUSLINE_CANONICAL_TRUECOLOR.subtle)}░░`;
149
149
  const tonedA = normalizeStatuslineAnsi(raw, colorsA);
150
150
 
151
- setThemeSetting('pi-dark', { persist: false });
151
+ setThemeSetting('teal', { persist: false });
152
152
  const colorsB = statusColorsFromTheme();
153
153
  const tonedB = normalizeStatuslineAnsi(raw, colorsB);
154
154
 
155
155
  assert.notEqual(tonedA, tonedB);
156
156
  assert.ok(tonedB.includes(colorsB.SUCCESS));
157
157
  assert.ok(tonedB.includes(colorsB.SUBTLE));
158
- setThemeSetting('mixdog', { persist: false });
158
+ setThemeSetting('basic', { persist: false });
159
159
  });