magi-ai 0.1.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 (300) hide show
  1. package/LICENSE +21 -0
  2. package/README.ja.md +377 -0
  3. package/README.md +377 -0
  4. package/dist/bin/magi-benchmark.d.ts +14 -0
  5. package/dist/bin/magi-benchmark.js +93 -0
  6. package/dist/bin/magi-mcp.d.ts +8 -0
  7. package/dist/bin/magi-mcp.js +28 -0
  8. package/dist/bin/magi.d.ts +2 -0
  9. package/dist/bin/magi.js +634 -0
  10. package/dist/src/adapters/base.d.ts +34 -0
  11. package/dist/src/adapters/base.js +149 -0
  12. package/dist/src/adapters/claude.d.ts +29 -0
  13. package/dist/src/adapters/claude.js +65 -0
  14. package/dist/src/adapters/codex.d.ts +21 -0
  15. package/dist/src/adapters/codex.js +41 -0
  16. package/dist/src/adapters/gemini.d.ts +18 -0
  17. package/dist/src/adapters/gemini.js +31 -0
  18. package/dist/src/adapters/registry.d.ts +19 -0
  19. package/dist/src/adapters/registry.js +59 -0
  20. package/dist/src/audit/hash-chain.d.ts +21 -0
  21. package/dist/src/audit/hash-chain.js +70 -0
  22. package/dist/src/audit/types.d.ts +25 -0
  23. package/dist/src/audit/types.js +1 -0
  24. package/dist/src/audit/writer.d.ts +18 -0
  25. package/dist/src/audit/writer.js +100 -0
  26. package/dist/src/benchmark/golden-tasks.d.ts +9 -0
  27. package/dist/src/benchmark/golden-tasks.js +476 -0
  28. package/dist/src/benchmark/reporter.d.ts +5 -0
  29. package/dist/src/benchmark/reporter.js +107 -0
  30. package/dist/src/benchmark/runner.d.ts +30 -0
  31. package/dist/src/benchmark/runner.js +224 -0
  32. package/dist/src/benchmark/scorer.d.ts +12 -0
  33. package/dist/src/benchmark/scorer.js +124 -0
  34. package/dist/src/benchmark/types.d.ts +54 -0
  35. package/dist/src/benchmark/types.js +1 -0
  36. package/dist/src/cache/deliberation-cache.d.ts +49 -0
  37. package/dist/src/cache/deliberation-cache.js +127 -0
  38. package/dist/src/cli/commands/config-cmd.d.ts +11 -0
  39. package/dist/src/cli/commands/config-cmd.js +190 -0
  40. package/dist/src/cli/commands/demo.d.ts +12 -0
  41. package/dist/src/cli/commands/demo.js +66 -0
  42. package/dist/src/cli/commands/setup.d.ts +7 -0
  43. package/dist/src/cli/commands/setup.js +182 -0
  44. package/dist/src/cli/i18n.d.ts +89 -0
  45. package/dist/src/cli/i18n.js +176 -0
  46. package/dist/src/cli/interactive-select.d.ts +27 -0
  47. package/dist/src/cli/interactive-select.js +130 -0
  48. package/dist/src/cli/tui-setup.d.ts +24 -0
  49. package/dist/src/cli/tui-setup.js +42 -0
  50. package/dist/src/config/cli-detector.d.ts +37 -0
  51. package/dist/src/config/cli-detector.js +99 -0
  52. package/dist/src/config/user-config.d.ts +81 -0
  53. package/dist/src/config/user-config.js +134 -0
  54. package/dist/src/context/auto-collector.d.ts +43 -0
  55. package/dist/src/context/auto-collector.js +337 -0
  56. package/dist/src/context/manager.d.ts +35 -0
  57. package/dist/src/context/manager.js +162 -0
  58. package/dist/src/context/serializer.d.ts +20 -0
  59. package/dist/src/context/serializer.js +52 -0
  60. package/dist/src/demo/recorded-deliberation.d.ts +13 -0
  61. package/dist/src/demo/recorded-deliberation.js +277 -0
  62. package/dist/src/engine/angel-detector.d.ts +83 -0
  63. package/dist/src/engine/angel-detector.js +334 -0
  64. package/dist/src/engine/at-field.d.ts +40 -0
  65. package/dist/src/engine/at-field.js +195 -0
  66. package/dist/src/engine/berserk-orchestrator.d.ts +66 -0
  67. package/dist/src/engine/berserk-orchestrator.js +378 -0
  68. package/dist/src/engine/change-metrics.d.ts +56 -0
  69. package/dist/src/engine/change-metrics.js +214 -0
  70. package/dist/src/engine/consensus.d.ts +20 -0
  71. package/dist/src/engine/consensus.js +146 -0
  72. package/dist/src/engine/dead-sea-scrolls.d.ts +132 -0
  73. package/dist/src/engine/dead-sea-scrolls.js +610 -0
  74. package/dist/src/engine/drift-detector.d.ts +39 -0
  75. package/dist/src/engine/drift-detector.js +225 -0
  76. package/dist/src/engine/dummy-plug.d.ts +44 -0
  77. package/dist/src/engine/dummy-plug.js +190 -0
  78. package/dist/src/engine/engram-manager.d.ts +55 -0
  79. package/dist/src/engine/engram-manager.js +306 -0
  80. package/dist/src/engine/events.d.ts +130 -0
  81. package/dist/src/engine/events.js +44 -0
  82. package/dist/src/engine/gospel.d.ts +30 -0
  83. package/dist/src/engine/gospel.js +129 -0
  84. package/dist/src/engine/hallucination-detector.d.ts +33 -0
  85. package/dist/src/engine/hallucination-detector.js +215 -0
  86. package/dist/src/engine/human-resolver.d.ts +19 -0
  87. package/dist/src/engine/human-resolver.js +89 -0
  88. package/dist/src/engine/instrumentality.d.ts +64 -0
  89. package/dist/src/engine/instrumentality.js +297 -0
  90. package/dist/src/engine/iruel-battle.d.ts +79 -0
  91. package/dist/src/engine/iruel-battle.js +319 -0
  92. package/dist/src/engine/kernel/deliberation-kernel.d.ts +12 -0
  93. package/dist/src/engine/kernel/deliberation-kernel.js +303 -0
  94. package/dist/src/engine/kernel/index.d.ts +8 -0
  95. package/dist/src/engine/kernel/index.js +7 -0
  96. package/dist/src/engine/kernel/phase-runner.d.ts +10 -0
  97. package/dist/src/engine/kernel/phase-runner.js +155 -0
  98. package/dist/src/engine/kernel/post-processor.d.ts +17 -0
  99. package/dist/src/engine/kernel/post-processor.js +131 -0
  100. package/dist/src/engine/kernel/types.d.ts +107 -0
  101. package/dist/src/engine/kernel/types.js +1 -0
  102. package/dist/src/engine/kernel/unit-executor.d.ts +6 -0
  103. package/dist/src/engine/kernel/unit-executor.js +132 -0
  104. package/dist/src/engine/lcl-manager.d.ts +44 -0
  105. package/dist/src/engine/lcl-manager.js +143 -0
  106. package/dist/src/engine/middleware/cache.d.ts +7 -0
  107. package/dist/src/engine/middleware/cache.js +29 -0
  108. package/dist/src/engine/middleware/chain.d.ts +18 -0
  109. package/dist/src/engine/middleware/chain.js +45 -0
  110. package/dist/src/engine/middleware/firewall.d.ts +8 -0
  111. package/dist/src/engine/middleware/firewall.js +24 -0
  112. package/dist/src/engine/middleware/index.d.ts +4 -0
  113. package/dist/src/engine/middleware/index.js +3 -0
  114. package/dist/src/engine/middleware/types.d.ts +43 -0
  115. package/dist/src/engine/middleware/types.js +1 -0
  116. package/dist/src/engine/nebuchadnezzar-key.d.ts +61 -0
  117. package/dist/src/engine/nebuchadnezzar-key.js +203 -0
  118. package/dist/src/engine/neon-genesis.d.ts +52 -0
  119. package/dist/src/engine/neon-genesis.js +203 -0
  120. package/dist/src/engine/objective-judge.d.ts +53 -0
  121. package/dist/src/engine/objective-judge.js +214 -0
  122. package/dist/src/engine/offline-mode.d.ts +18 -0
  123. package/dist/src/engine/offline-mode.js +46 -0
  124. package/dist/src/engine/orchestrator.d.ts +79 -0
  125. package/dist/src/engine/orchestrator.js +58 -0
  126. package/dist/src/engine/secret-cipher.d.ts +26 -0
  127. package/dist/src/engine/secret-cipher.js +114 -0
  128. package/dist/src/engine/seele-council.d.ts +90 -0
  129. package/dist/src/engine/seele-council.js +482 -0
  130. package/dist/src/engine/self-destruct.d.ts +61 -0
  131. package/dist/src/engine/self-destruct.js +231 -0
  132. package/dist/src/engine/self-evolution.d.ts +64 -0
  133. package/dist/src/engine/self-evolution.js +368 -0
  134. package/dist/src/engine/sync-rate.d.ts +45 -0
  135. package/dist/src/engine/sync-rate.js +151 -0
  136. package/dist/src/engine/type666-firewall.d.ts +76 -0
  137. package/dist/src/engine/type666-firewall.js +343 -0
  138. package/dist/src/engine/umbilical-cable.d.ts +41 -0
  139. package/dist/src/engine/umbilical-cable.js +192 -0
  140. package/dist/src/index.d.ts +106 -0
  141. package/dist/src/index.js +426 -0
  142. package/dist/src/mcp/server.d.ts +38 -0
  143. package/dist/src/mcp/server.js +196 -0
  144. package/dist/src/metrics/token-tracker.d.ts +38 -0
  145. package/dist/src/metrics/token-tracker.js +112 -0
  146. package/dist/src/parsers/json-extractor.d.ts +9 -0
  147. package/dist/src/parsers/json-extractor.js +239 -0
  148. package/dist/src/parsers/opinion-schema.d.ts +81 -0
  149. package/dist/src/parsers/opinion-schema.js +147 -0
  150. package/dist/src/parsers/unstructured-parser.d.ts +20 -0
  151. package/dist/src/parsers/unstructured-parser.js +122 -0
  152. package/dist/src/pipelines/architecture.d.ts +10 -0
  153. package/dist/src/pipelines/architecture.js +9 -0
  154. package/dist/src/pipelines/bug-analysis.d.ts +9 -0
  155. package/dist/src/pipelines/bug-analysis.js +8 -0
  156. package/dist/src/pipelines/code-review.d.ts +10 -0
  157. package/dist/src/pipelines/code-review.js +30 -0
  158. package/dist/src/pipelines/custom.d.ts +14 -0
  159. package/dist/src/pipelines/custom.js +29 -0
  160. package/dist/src/pipelines/registry.d.ts +9 -0
  161. package/dist/src/pipelines/registry.js +20 -0
  162. package/dist/src/prompts/personas.d.ts +6 -0
  163. package/dist/src/prompts/personas.js +44 -0
  164. package/dist/src/prompts/schemas.d.ts +4 -0
  165. package/dist/src/prompts/schemas.js +24 -0
  166. package/dist/src/prompts/templates.d.ts +6 -0
  167. package/dist/src/prompts/templates.js +91 -0
  168. package/dist/src/repl/accessibility.d.ts +23 -0
  169. package/dist/src/repl/accessibility.js +46 -0
  170. package/dist/src/repl/banner.d.ts +4 -0
  171. package/dist/src/repl/banner.js +28 -0
  172. package/dist/src/repl/boot-animation.d.ts +13 -0
  173. package/dist/src/repl/boot-animation.js +143 -0
  174. package/dist/src/repl/completer.d.ts +21 -0
  175. package/dist/src/repl/completer.js +168 -0
  176. package/dist/src/repl/context.d.ts +24 -0
  177. package/dist/src/repl/context.js +42 -0
  178. package/dist/src/repl/display-utils.d.ts +13 -0
  179. package/dist/src/repl/display-utils.js +65 -0
  180. package/dist/src/repl/event-listener.d.ts +18 -0
  181. package/dist/src/repl/event-listener.js +112 -0
  182. package/dist/src/repl/export-formatter.d.ts +8 -0
  183. package/dist/src/repl/export-formatter.js +73 -0
  184. package/dist/src/repl/ghost-text.d.ts +31 -0
  185. package/dist/src/repl/ghost-text.js +119 -0
  186. package/dist/src/repl/handoff-animation.d.ts +15 -0
  187. package/dist/src/repl/handoff-animation.js +65 -0
  188. package/dist/src/repl/history.d.ts +16 -0
  189. package/dist/src/repl/history.js +130 -0
  190. package/dist/src/repl/job-registry.d.ts +26 -0
  191. package/dist/src/repl/job-registry.js +80 -0
  192. package/dist/src/repl/magi-repl.d.ts +72 -0
  193. package/dist/src/repl/magi-repl.js +1008 -0
  194. package/dist/src/repl/multiline-input.d.ts +45 -0
  195. package/dist/src/repl/multiline-input.js +78 -0
  196. package/dist/src/repl/prompt-builder.d.ts +19 -0
  197. package/dist/src/repl/prompt-builder.js +36 -0
  198. package/dist/src/repl/repl-state.d.ts +5 -0
  199. package/dist/src/repl/repl-state.js +19 -0
  200. package/dist/src/repl/result-display.d.ts +8 -0
  201. package/dist/src/repl/result-display.js +195 -0
  202. package/dist/src/repl/session-stats.d.ts +26 -0
  203. package/dist/src/repl/session-stats.js +119 -0
  204. package/dist/src/repl/slash-commands.d.ts +60 -0
  205. package/dist/src/repl/slash-commands.js +725 -0
  206. package/dist/src/repl/terminal-sanitize.d.ts +14 -0
  207. package/dist/src/repl/terminal-sanitize.js +19 -0
  208. package/dist/src/reporters/console.d.ts +7 -0
  209. package/dist/src/reporters/console.js +78 -0
  210. package/dist/src/reporters/json.d.ts +2 -0
  211. package/dist/src/reporters/json.js +3 -0
  212. package/dist/src/reporters/markdown.d.ts +2 -0
  213. package/dist/src/reporters/markdown.js +65 -0
  214. package/dist/src/reporters/streaming.d.ts +20 -0
  215. package/dist/src/reporters/streaming.js +178 -0
  216. package/dist/src/tui/activity-log.d.ts +23 -0
  217. package/dist/src/tui/activity-log.js +67 -0
  218. package/dist/src/tui/animations.d.ts +39 -0
  219. package/dist/src/tui/animations.js +167 -0
  220. package/dist/src/tui/ansi.d.ts +28 -0
  221. package/dist/src/tui/ansi.js +51 -0
  222. package/dist/src/tui/boot-sequence.d.ts +11 -0
  223. package/dist/src/tui/boot-sequence.js +98 -0
  224. package/dist/src/tui/colors.d.ts +101 -0
  225. package/dist/src/tui/colors.js +71 -0
  226. package/dist/src/tui/header.d.ts +24 -0
  227. package/dist/src/tui/header.js +122 -0
  228. package/dist/src/tui/index.d.ts +3 -0
  229. package/dist/src/tui/index.js +3 -0
  230. package/dist/src/tui/keypress.d.ts +25 -0
  231. package/dist/src/tui/keypress.js +95 -0
  232. package/dist/src/tui/layout.d.ts +74 -0
  233. package/dist/src/tui/layout.js +171 -0
  234. package/dist/src/tui/magi-tui.d.ts +101 -0
  235. package/dist/src/tui/magi-tui.js +754 -0
  236. package/dist/src/tui/panel.d.ts +45 -0
  237. package/dist/src/tui/panel.js +292 -0
  238. package/dist/src/tui/screen-buffer.d.ts +54 -0
  239. package/dist/src/tui/screen-buffer.js +262 -0
  240. package/dist/src/tui/status-bar.d.ts +25 -0
  241. package/dist/src/tui/status-bar.js +124 -0
  242. package/dist/src/tui/terminal-detect.d.ts +26 -0
  243. package/dist/src/tui/terminal-detect.js +44 -0
  244. package/dist/src/tui/tui-helpers.d.ts +12 -0
  245. package/dist/src/tui/tui-helpers.js +37 -0
  246. package/dist/src/types/adapter.d.ts +75 -0
  247. package/dist/src/types/adapter.js +36 -0
  248. package/dist/src/types/config.d.ts +108 -0
  249. package/dist/src/types/config.js +85 -0
  250. package/dist/src/types/consensus.d.ts +55 -0
  251. package/dist/src/types/consensus.js +17 -0
  252. package/dist/src/types/core.d.ts +178 -0
  253. package/dist/src/types/core.js +85 -0
  254. package/dist/src/types/magi-api.d.ts +62 -0
  255. package/dist/src/types/magi-api.js +7 -0
  256. package/dist/src/types/phase-h.d.ts +142 -0
  257. package/dist/src/types/phase-h.js +7 -0
  258. package/dist/src/types/phase-i.d.ts +186 -0
  259. package/dist/src/types/phase-i.js +6 -0
  260. package/dist/src/types/phase-k.d.ts +259 -0
  261. package/dist/src/types/phase-k.js +6 -0
  262. package/dist/src/types/phase-l.d.ts +199 -0
  263. package/dist/src/types/phase-l.js +6 -0
  264. package/dist/src/types/pipeline.d.ts +37 -0
  265. package/dist/src/types/pipeline.js +2 -0
  266. package/dist/src/utils/abstain-factory.d.ts +2 -0
  267. package/dist/src/utils/abstain-factory.js +18 -0
  268. package/dist/src/utils/errors.d.ts +34 -0
  269. package/dist/src/utils/errors.js +59 -0
  270. package/dist/src/utils/file-validator.d.ts +50 -0
  271. package/dist/src/utils/file-validator.js +124 -0
  272. package/dist/src/utils/fire-and-forget.d.ts +5 -0
  273. package/dist/src/utils/fire-and-forget.js +10 -0
  274. package/dist/src/utils/flag-validator.d.ts +21 -0
  275. package/dist/src/utils/flag-validator.js +79 -0
  276. package/dist/src/utils/freeze.d.ts +8 -0
  277. package/dist/src/utils/freeze.js +16 -0
  278. package/dist/src/utils/language-detector.d.ts +16 -0
  279. package/dist/src/utils/language-detector.js +159 -0
  280. package/dist/src/utils/latency-tracker.d.ts +45 -0
  281. package/dist/src/utils/latency-tracker.js +100 -0
  282. package/dist/src/utils/logger.d.ts +33 -0
  283. package/dist/src/utils/logger.js +112 -0
  284. package/dist/src/utils/process.d.ts +40 -0
  285. package/dist/src/utils/process.js +253 -0
  286. package/dist/src/utils/retry.d.ts +12 -0
  287. package/dist/src/utils/retry.js +30 -0
  288. package/dist/src/utils/safe-fs.d.ts +38 -0
  289. package/dist/src/utils/safe-fs.js +56 -0
  290. package/dist/src/utils/safe-json-parse.d.ts +15 -0
  291. package/dist/src/utils/safe-json-parse.js +49 -0
  292. package/dist/src/utils/sanitize.d.ts +14 -0
  293. package/dist/src/utils/sanitize.js +186 -0
  294. package/dist/src/utils/semaphore.d.ts +22 -0
  295. package/dist/src/utils/semaphore.js +57 -0
  296. package/dist/src/utils/shutdown.d.ts +6 -0
  297. package/dist/src/utils/shutdown.js +51 -0
  298. package/dist/src/utils/tty.d.ts +5 -0
  299. package/dist/src/utils/tty.js +7 -0
  300. package/package.json +82 -0
@@ -0,0 +1,725 @@
1
+ /**
2
+ * Slash command registry for MAGI REPL.
3
+ *
4
+ * Each command returns a SlashCommandResult that tells the REPL
5
+ * whether to run a deliberation (with TUI) or just print inline output.
6
+ */
7
+ import chalk from 'chalk';
8
+ import { readFile } from 'node:fs/promises';
9
+ import { EVA_PALETTE } from '../tui/colors.js';
10
+ import { validateFile } from '../utils/file-validator.js';
11
+ import { detectLanguage } from '../utils/language-detector.js';
12
+ import { collectRelatedFiles, collectProjectContext } from '../context/auto-collector.js';
13
+ const MAX_REVIEW_FILE_BYTES = 524_288; // 512 KiB
14
+ const { frame, approve, reject, abstain, textPrimary, textSecondary, warning } = EVA_PALETTE;
15
+ const FRAME_C = chalk.rgb(frame.r, frame.g, frame.b);
16
+ const DIM_C = chalk.rgb(abstain.r, abstain.g, abstain.b);
17
+ const TEXT_C = chalk.rgb(textSecondary.r, textSecondary.g, textSecondary.b);
18
+ const OK_C = chalk.rgb(approve.r, approve.g, approve.b);
19
+ const ERR_C = chalk.rgb(reject.r, reject.g, reject.b);
20
+ const HEAD_C = chalk.rgb(textPrimary.r, textPrimary.g, textPrimary.b);
21
+ const WARN_C = chalk.rgb(warning.r, warning.g, warning.b);
22
+ // ── Input parser ────────────────────────────────────────────
23
+ export function parseInput(input) {
24
+ if (!input.startsWith('/')) {
25
+ return { isSlash: false, command: '', args: input };
26
+ }
27
+ const spaceIdx = input.indexOf(' ');
28
+ if (spaceIdx === -1) {
29
+ return { isSlash: true, command: input.slice(1).toLowerCase(), args: '' };
30
+ }
31
+ return {
32
+ isSlash: true,
33
+ command: input.slice(1, spaceIdx).toLowerCase(),
34
+ args: input.slice(spaceIdx + 1).trim(),
35
+ };
36
+ }
37
+ // ── Levenshtein distance (for command suggestion) ───────────
38
+ function levenshtein(a, b) {
39
+ const m = a.length;
40
+ const n = b.length;
41
+ const dp = Array.from({ length: n + 1 }, (_, i) => i);
42
+ for (let i = 1; i <= m; i++) {
43
+ let prev = dp[0];
44
+ dp[0] = i;
45
+ for (let j = 1; j <= n; j++) {
46
+ const tmp = dp[j];
47
+ dp[j] = a[i - 1] === b[j - 1]
48
+ ? prev
49
+ : 1 + Math.min(prev, dp[j], dp[j - 1]);
50
+ prev = tmp;
51
+ }
52
+ }
53
+ return dp[n];
54
+ }
55
+ export function suggestCommand(input, commands) {
56
+ if (!input)
57
+ return undefined;
58
+ let bestName;
59
+ let bestDist = Infinity;
60
+ for (const name of commands) {
61
+ if (name === input)
62
+ return undefined; // exact match — already in registry
63
+ const d = levenshtein(input, name);
64
+ if (d < bestDist) {
65
+ bestDist = d;
66
+ bestName = name;
67
+ }
68
+ }
69
+ // Threshold: distance <= 3 AND <= 50% of input length
70
+ if (bestDist <= 3 && bestDist <= Math.ceil(input.length / 2)) {
71
+ return bestName;
72
+ }
73
+ return undefined;
74
+ }
75
+ // ── Hierarchical command help ────────────────────────────────
76
+ function opsHelp() {
77
+ return [
78
+ '', HEAD_C(' /ops — Runtime Operations:'), '',
79
+ TEXT_C(' /ops watch start [--interval <ms>] ') + DIM_C('Start angel detection'),
80
+ TEXT_C(' /ops watch stop ') + DIM_C('Stop angel detection'),
81
+ TEXT_C(' /ops watch status ') + DIM_C('Show watch status'),
82
+ TEXT_C(' /ops scan <text> ') + DIM_C('Firewall scan'),
83
+ TEXT_C(' /ops prophecy <file> ') + DIM_C('Dead Sea Scrolls prediction'),
84
+ '',
85
+ ].join('\n');
86
+ }
87
+ function diagHelp() {
88
+ return [
89
+ '', HEAD_C(' /diag — Diagnostics:'), '',
90
+ TEXT_C(' /diag iruel ') + DIM_C('Iruel battle simulation'),
91
+ TEXT_C(' /diag evolve [--max <n>] ') + DIM_C('S² Engine evolution (dry-run)'),
92
+ TEXT_C(' /diag drift [unit] ') + DIM_C('Check drift for a unit'),
93
+ TEXT_C(' /diag lcl ') + DIM_C('Show LCL density'),
94
+ TEXT_C(' /diag chronicle ') + DIM_C('Gospel chronicle stats'),
95
+ '',
96
+ ].join('\n');
97
+ }
98
+ function adminHelp() {
99
+ return [
100
+ '', HEAD_C(' /admin — Admin (Dangerous Operations):'), '',
101
+ TEXT_C(' /admin seele [--attack] ') + DIM_C('Seele council simulation'),
102
+ TEXT_C(' /admin self-destruct <reason> ') + DIM_C('Self-destruct sequence'),
103
+ TEXT_C(' /admin genesis <reason> ') + DIM_C('Neon Genesis (full reset)'),
104
+ '',
105
+ ].join('\n');
106
+ }
107
+ /** Thin wrapper: delegates to shared file-validator and maps result to REPL format. */
108
+ async function validateWorkspaceFile(inputPath, opts) {
109
+ const result = await validateFile(inputPath, {
110
+ maxBytes: opts?.maxBytes,
111
+ rejectBinary: false, // binary check only for /review, not /ops prophecy
112
+ });
113
+ if (!result.ok) {
114
+ const { code, message } = result.error;
115
+ switch (code) {
116
+ case 'ENOENT':
117
+ return { output: ERR_C(`\n File not found: ${inputPath}\n`) };
118
+ case 'EACCES':
119
+ return { output: ERR_C(`\n Permission denied: ${inputPath}\n`) };
120
+ case 'PATH_TRAVERSAL':
121
+ return { output: ERR_C('\n Error: path traversal denied.\n') };
122
+ case 'SYMLINK':
123
+ return { output: ERR_C('\n Error: symlink targets are not allowed.\n') };
124
+ case 'NOT_FILE':
125
+ return { output: ERR_C(`\n Not a regular file: ${inputPath}\n`) };
126
+ case 'TOO_LARGE':
127
+ return { output: ERR_C(`\n ${message}\n`) };
128
+ default:
129
+ return { output: ERR_C(`\n ${message}\n`) };
130
+ }
131
+ }
132
+ return {
133
+ file: {
134
+ absolutePath: result.file.absolutePath,
135
+ relativePath: result.file.relativePath,
136
+ },
137
+ };
138
+ }
139
+ // ── Command registry ────────────────────────────────────────
140
+ export function createSlashCommands() {
141
+ const commands = new Map();
142
+ commands.set('help', {
143
+ description: 'Show available commands',
144
+ usage: '/help',
145
+ argKind: 'none',
146
+ async execute() {
147
+ const lines = [
148
+ '',
149
+ FRAME_C.bold(' Available Commands:'),
150
+ '',
151
+ TEXT_C(' /help ') + DIM_C('Show this message'),
152
+ TEXT_C(' /doctor ') + DIM_C('Check MAGI system health'),
153
+ TEXT_C(' /tokens ') + DIM_C('Show token usage summary'),
154
+ TEXT_C(' /review <file> ') + DIM_C('Code review deliberation'),
155
+ TEXT_C(' /decide <question>') + DIM_C('Architecture decision'),
156
+ TEXT_C(' /berserk <prompt> ') + DIM_C('BERSERK mode deliberation'),
157
+ TEXT_C(' /paste ') + DIM_C('Multiline input (end with ".")'),
158
+ TEXT_C(' /sound ') + DIM_C('Toggle sound on/off'),
159
+ TEXT_C(' /history ') + DIM_C('Show recent deliberations'),
160
+ TEXT_C(' /status ') + DIM_C('Show MAGI system dashboard'),
161
+ TEXT_C(' /export [file] ') + DIM_C('Export last deliberation'),
162
+ TEXT_C(' /clear ') + DIM_C('Clear screen'),
163
+ TEXT_C(' /reset ') + DIM_C('Reset deliberation context'),
164
+ TEXT_C(' /quit ') + DIM_C('Exit MAGI REPL'),
165
+ '',
166
+ FRAME_C.bold(' Command Groups:'),
167
+ '',
168
+ TEXT_C(' /ops <sub> ') + DIM_C('Runtime operations (watch, scan, prophecy)'),
169
+ TEXT_C(' /diag <sub> ') + DIM_C('Diagnostics (iruel, evolve, drift, lcl)'),
170
+ TEXT_C(' /admin <sub> ') + DIM_C('Admin operations (seele, self-destruct, genesis)'),
171
+ TEXT_C(' /jobs [cancel <id>]') + DIM_C('Background job management'),
172
+ '',
173
+ FRAME_C.bold(' Setup & Config:'),
174
+ '',
175
+ TEXT_C(' /setup ') + DIM_C('Guided initial setup'),
176
+ TEXT_C(' /config ') + DIM_C('View/edit user configuration'),
177
+ TEXT_C(' /demo ') + DIM_C('Watch pre-recorded deliberation'),
178
+ '',
179
+ DIM_C(' Or just type a question to start deliberation.'),
180
+ '',
181
+ ];
182
+ return { output: lines.join('\n') };
183
+ },
184
+ });
185
+ commands.set('doctor', {
186
+ description: 'Check MAGI system health',
187
+ usage: '/doctor',
188
+ argKind: 'none',
189
+ async execute(_args, magi) {
190
+ const results = await magi.healthCheck();
191
+ const lines = ['', HEAD_C(' MAGI System Health:'), ''];
192
+ for (const [unit, result] of results) {
193
+ if (result instanceof Error) {
194
+ lines.push(ERR_C(` ✗ ${unit}: ${result.message}`));
195
+ }
196
+ else {
197
+ lines.push(OK_C(` ✓ ${unit}: ${result}`));
198
+ }
199
+ }
200
+ lines.push('');
201
+ return { output: lines.join('\n') };
202
+ },
203
+ });
204
+ commands.set('tokens', {
205
+ description: 'Show token usage summary',
206
+ usage: '/tokens',
207
+ argKind: 'none',
208
+ async execute(_args, magi) {
209
+ const summaries = await magi.loadAllTokens();
210
+ if (summaries.length === 0) {
211
+ return { output: chalk.yellow('\n No token data found.\n') };
212
+ }
213
+ let total = 0;
214
+ const lines = ['', HEAD_C(' Token Usage:'), ''];
215
+ for (const s of summaries) {
216
+ total += s.totalEstimatedTokens;
217
+ lines.push(DIM_C(` ${s.deliberationId}: ~${s.totalEstimatedTokens} tokens (${s.recordCount} records)`));
218
+ }
219
+ lines.push('', chalk.bold(` Total: ~${total} tokens`), '');
220
+ return { output: lines.join('\n') };
221
+ },
222
+ });
223
+ commands.set('review', {
224
+ description: 'Code review deliberation',
225
+ usage: '/review <file>',
226
+ argKind: 'file',
227
+ async execute(args) {
228
+ if (!args) {
229
+ return { output: chalk.yellow('\n Usage: /review <file>\n') };
230
+ }
231
+ // Validate with binary rejection
232
+ const validation = await validateFile(args, {
233
+ maxBytes: MAX_REVIEW_FILE_BYTES,
234
+ rejectBinary: true,
235
+ });
236
+ if (!validation.ok) {
237
+ const { code, message } = validation.error;
238
+ switch (code) {
239
+ case 'ENOENT':
240
+ return { output: ERR_C(`\n File not found: ${args}\n`) };
241
+ case 'EACCES':
242
+ return { output: ERR_C(`\n Permission denied: ${args}\n`) };
243
+ case 'BINARY':
244
+ return { output: ERR_C(`\n ${message}\n`) };
245
+ default:
246
+ return { output: ERR_C(`\n ${message}\n`) };
247
+ }
248
+ }
249
+ const content = await readFile(validation.file.absolutePath, 'utf-8');
250
+ const language = detectLanguage(args);
251
+ // Collect related files (graceful degradation)
252
+ let relatedArtifacts = [];
253
+ let context;
254
+ try {
255
+ const [related, projectCtx] = await Promise.allSettled([
256
+ collectRelatedFiles(args, content),
257
+ collectProjectContext(),
258
+ ]);
259
+ if (related.status === 'fulfilled')
260
+ relatedArtifacts = related.value;
261
+ if (projectCtx.status === 'fulfilled' && projectCtx.value.charCount > 0) {
262
+ context = projectCtx.value.text;
263
+ }
264
+ }
265
+ catch { /* graceful degradation */ }
266
+ return {
267
+ deliberation: {
268
+ task: {
269
+ type: 'code-review',
270
+ title: `Code Review: ${validation.file.relativePath}`,
271
+ description: 'Review the following code file for quality, correctness, security, and best practices.',
272
+ artifacts: [
273
+ {
274
+ type: 'file',
275
+ path: validation.file.relativePath,
276
+ content,
277
+ language,
278
+ },
279
+ ...relatedArtifacts,
280
+ ],
281
+ context,
282
+ },
283
+ },
284
+ };
285
+ },
286
+ });
287
+ commands.set('decide', {
288
+ description: 'Architecture decision deliberation',
289
+ usage: '/decide <question>',
290
+ argKind: 'freeform',
291
+ async execute(args) {
292
+ if (!args) {
293
+ return { output: chalk.yellow('\n Usage: /decide <question>\n') };
294
+ }
295
+ // Collect project context (graceful degradation)
296
+ let context;
297
+ try {
298
+ const ctx = await collectProjectContext();
299
+ if (ctx.charCount > 0)
300
+ context = ctx.text;
301
+ }
302
+ catch { /* graceful degradation */ }
303
+ return {
304
+ deliberation: {
305
+ task: {
306
+ type: 'architecture-decision',
307
+ title: args.slice(0, 80),
308
+ description: args,
309
+ context,
310
+ },
311
+ },
312
+ };
313
+ },
314
+ });
315
+ commands.set('berserk', {
316
+ description: 'BERSERK mode deliberation',
317
+ usage: '/berserk <prompt>',
318
+ argKind: 'freeform',
319
+ async execute(args) {
320
+ if (!args) {
321
+ return { output: chalk.yellow('\n Usage: /berserk <prompt>\n') };
322
+ }
323
+ return {
324
+ deliberation: {
325
+ task: {
326
+ type: 'custom',
327
+ title: args.slice(0, 80),
328
+ description: args,
329
+ },
330
+ berserk: true,
331
+ },
332
+ };
333
+ },
334
+ });
335
+ commands.set('paste', {
336
+ description: 'Multiline input mode (end with ".")',
337
+ usage: '/paste',
338
+ argKind: 'none',
339
+ async execute() {
340
+ return { action: 'paste' };
341
+ },
342
+ });
343
+ commands.set('sound', {
344
+ description: 'Toggle sound on/off',
345
+ usage: '/sound',
346
+ argKind: 'none',
347
+ async execute() {
348
+ return { action: 'toggle-sound' };
349
+ },
350
+ });
351
+ commands.set('history', {
352
+ description: 'Show recent deliberations',
353
+ usage: '/history',
354
+ argKind: 'none',
355
+ async execute(_args, _magi, context) {
356
+ const history = context.getHistory();
357
+ if (history.length === 0) {
358
+ return { output: chalk.yellow('\n No deliberation history in this session.\n') };
359
+ }
360
+ const lines = ['', HEAD_C(' Session History:'), ''];
361
+ for (const [i, entry] of history.entries()) {
362
+ const time = entry.timestamp.toLocaleTimeString();
363
+ lines.push(TEXT_C(` ${i + 1}. [${time}] "${entry.title}"`));
364
+ lines.push(DIM_C(` → ${entry.decision}: ${(entry.summary || '(no summary)').slice(0, 120)}`));
365
+ }
366
+ lines.push('');
367
+ return { output: lines.join('\n') };
368
+ },
369
+ });
370
+ commands.set('status', {
371
+ description: 'Show MAGI system dashboard',
372
+ usage: '/status',
373
+ argKind: 'none',
374
+ async execute(_args, magi) {
375
+ const status = await magi.getSystemStatus();
376
+ const lines = ['', HEAD_C(' MAGI System Status:'), ''];
377
+ const WARN_C = chalk.yellow;
378
+ for (const u of status.units) {
379
+ const healthStr = u.health instanceof Error
380
+ ? ERR_C(u.health.message)
381
+ : OK_C(String(u.health));
382
+ const cableStr = u.cableStatus === 'connected' ? OK_C('●')
383
+ : u.cableStatus === 'degraded' ? WARN_C('◐')
384
+ : ERR_C('○');
385
+ const syncPct = `${Math.round(u.syncRate * 100)}%`;
386
+ lines.push(` ${TEXT_C(u.unit.padEnd(14))} ${cableStr} ${healthStr} ${DIM_C('Sync:')} ${DIM_C(syncPct)}`);
387
+ }
388
+ lines.push('');
389
+ const streak = status.atFieldStreak;
390
+ const atLabel = streak >= 5
391
+ ? ERR_C(` A.T.FIELD: WARNING (${streak} consecutive unanimous)`)
392
+ : DIM_C(` A.T.FIELD: Level ${streak}`);
393
+ lines.push(atLabel, '');
394
+ return { output: lines.join('\n') };
395
+ },
396
+ });
397
+ commands.set('export', {
398
+ description: 'Export last deliberation',
399
+ usage: '/export [filename] [--json]',
400
+ argKind: 'freeform',
401
+ async execute(args) {
402
+ return { action: 'export', output: args || '' };
403
+ },
404
+ });
405
+ commands.set('clear', {
406
+ description: 'Clear screen',
407
+ usage: '/clear',
408
+ argKind: 'none',
409
+ async execute() {
410
+ return { action: 'clear' };
411
+ },
412
+ });
413
+ commands.set('reset', {
414
+ description: 'Reset deliberation context',
415
+ usage: '/reset',
416
+ argKind: 'none',
417
+ async execute() {
418
+ return { action: 'reset' };
419
+ },
420
+ });
421
+ commands.set('quit', {
422
+ description: 'Exit MAGI REPL',
423
+ usage: '/quit',
424
+ argKind: 'none',
425
+ async execute() {
426
+ return { action: 'quit' };
427
+ },
428
+ });
429
+ commands.set('exit', {
430
+ description: 'Exit MAGI REPL',
431
+ usage: '/exit',
432
+ argKind: 'none',
433
+ aliases: ['quit'],
434
+ async execute() {
435
+ return { action: 'quit' };
436
+ },
437
+ });
438
+ // ── Hierarchical command groups ────────────────────────────
439
+ // /ops - Runtime operations
440
+ commands.set('ops', {
441
+ description: 'Runtime operations (watch, scan, prophecy)',
442
+ usage: '/ops <subcommand>',
443
+ argKind: 'freeform',
444
+ async execute(args, magi) {
445
+ const [sub, ...rest] = (args || '').split(/\s+/);
446
+ const subArgs = rest.join(' ');
447
+ switch (sub) {
448
+ case 'watch': {
449
+ const [action, ...watchRest] = subArgs.split(/\s+/);
450
+ if (action === 'start') {
451
+ const intervalMatch = watchRest.join(' ').match(/--interval\s+(\d+)/);
452
+ const pollIntervalMs = intervalMatch ? parseInt(intervalMatch[1], 10) : undefined;
453
+ if (pollIntervalMs !== undefined && pollIntervalMs < 1000) {
454
+ return { output: WARN_C(' Minimum interval is 1000ms.') };
455
+ }
456
+ return { watch: { command: 'start', pollIntervalMs } };
457
+ }
458
+ if (action === 'stop') {
459
+ return { watch: { command: 'stop' } };
460
+ }
461
+ if (action === 'status') {
462
+ return { watch: { command: 'status' } };
463
+ }
464
+ return { output: opsHelp() };
465
+ }
466
+ case 'scan': {
467
+ if (!subArgs)
468
+ return { output: WARN_C(' Usage: /ops scan <text>') };
469
+ const result = magi.runtime.scanFirewall(subArgs);
470
+ const status = result.passed ? OK_C('PASSED') : ERR_C('BLOCKED');
471
+ const lines = [` Firewall scan: ${status} (score: ${result.totalScore.toFixed(2)})`];
472
+ if (result.threats.length > 0) {
473
+ for (const t of result.threats) {
474
+ lines.push(ERR_C(` Layer ${t.layer}: ${t.category} — ${t.description}`));
475
+ }
476
+ }
477
+ return { output: lines.join('\n') };
478
+ }
479
+ case 'prophecy': {
480
+ if (!subArgs)
481
+ return { output: WARN_C(' Usage: /ops prophecy <file>') };
482
+ const validated = await validateWorkspaceFile(subArgs, { maxBytes: MAX_REVIEW_FILE_BYTES });
483
+ if ('output' in validated) {
484
+ return { output: validated.output.trimEnd() };
485
+ }
486
+ try {
487
+ const prophecy = await magi.runtime.analyzeFile(validated.file.absolutePath);
488
+ const levelColor = prophecy.level === 'PROPHET' ? ERR_C
489
+ : prophecy.level === 'ORACLE' ? WARN_C : DIM_C;
490
+ return {
491
+ output: [
492
+ ` ${HEAD_C('Dead Sea Scrolls — 予言')}`,
493
+ ` File: ${validated.file.relativePath}`,
494
+ ` Probability: ${levelColor(`${(prophecy.probability * 100).toFixed(0)}% (${prophecy.level})`)}`,
495
+ ` ${DIM_C(prophecy.narrative.slice(0, 200))}`,
496
+ ].join('\n'),
497
+ };
498
+ }
499
+ catch (error) {
500
+ return { output: ERR_C(` Prophecy failed: ${String(error)}`) };
501
+ }
502
+ }
503
+ default:
504
+ return { output: opsHelp() };
505
+ }
506
+ },
507
+ });
508
+ // /diag - Diagnostics
509
+ commands.set('diag', {
510
+ description: 'Diagnostics (iruel, evolve, drift, lcl, chronicle)',
511
+ usage: '/diag <subcommand>',
512
+ argKind: 'freeform',
513
+ async execute(args, magi) {
514
+ const [sub, ...rest] = (args || '').split(/\s+/);
515
+ const subArgs = rest.join(' ');
516
+ switch (sub) {
517
+ case 'iruel': {
518
+ try {
519
+ const phase = await magi.diagnostics.simulateIruelBattle();
520
+ return { output: ` ${HEAD_C('Iruel Battle')} — Final phase: ${phase}` };
521
+ }
522
+ catch (error) {
523
+ return { output: ERR_C(` Iruel battle failed: ${String(error)}`) };
524
+ }
525
+ }
526
+ case 'evolve': {
527
+ const maxMatch = subArgs.match(/--max\s+(\d+)/);
528
+ const maxProposals = maxMatch ? parseInt(maxMatch[1], 10) : 3;
529
+ try {
530
+ const gen = await magi.diagnostics.evolve({ dryRun: true, maxProposals });
531
+ return {
532
+ output: [
533
+ ` ${HEAD_C('S² Engine — Generation')} ${gen.generation}`,
534
+ ` Proposals: ${gen.proposals.length}`,
535
+ ` Approved: ${gen.proposals.filter(p => p.approvedByMeta).length}`,
536
+ ` Consensus rate: ${(gen.diagnostics.consensusRate * 100).toFixed(0)}%`,
537
+ ].join('\n'),
538
+ };
539
+ }
540
+ catch (error) {
541
+ return { output: ERR_C(` Evolution failed: ${String(error)}`) };
542
+ }
543
+ }
544
+ case 'drift': {
545
+ const unit = subArgs || 'MELCHIOR';
546
+ const asi = magi.diagnostics.computeASI(unit);
547
+ const drifting = magi.diagnostics.checkDrift(unit);
548
+ const color = drifting ? ERR_C : OK_C;
549
+ return {
550
+ output: ` ${HEAD_C(unit)} ASI: ${color(asi.toFixed(3))} ${drifting ? ERR_C('DRIFT DETECTED') : OK_C('STABLE')}`,
551
+ };
552
+ }
553
+ case 'lcl': {
554
+ const phases = ['initial-opinion', 'cross-examination', 'final-vote'];
555
+ const lines = [` ${HEAD_C('LCL Density:')}`];
556
+ for (const p of phases) {
557
+ const d = magi.diagnostics.getLCLDensity(p);
558
+ lines.push(` ${p}: ${d.toFixed(2)}`);
559
+ }
560
+ return { output: lines.join('\n') };
561
+ }
562
+ case 'chronicle': {
563
+ const count = magi.diagnostics.getChronicleEntryCount();
564
+ return { output: ` ${HEAD_C('Gospel Chronicle:')} ${count} entries recorded.` };
565
+ }
566
+ default:
567
+ return { output: diagHelp() };
568
+ }
569
+ },
570
+ });
571
+ // /admin - Dangerous operations
572
+ commands.set('admin', {
573
+ description: 'Admin operations (seele, self-destruct, genesis)',
574
+ usage: '/admin <subcommand>',
575
+ argKind: 'freeform',
576
+ async execute(args, magi) {
577
+ const [sub, ...rest] = (args || '').split(/\s+/);
578
+ const subArgs = rest.join(' ').trim();
579
+ switch (sub) {
580
+ case 'seele': {
581
+ const attack = subArgs.includes('--attack');
582
+ try {
583
+ const session = await magi.admin.simulateSeele();
584
+ const attackResult = attack
585
+ ? await magi.admin.executeSeeleAttack(['MAGI-05', 'MAGI-06'], 'MAGI-01')
586
+ : undefined;
587
+ return {
588
+ output: [
589
+ ` ${HEAD_C('Seele Council')}`,
590
+ ` Session: ${session.id.slice(0, 8)}`,
591
+ ` Leader: ${session.leader ?? 'N/A'}`,
592
+ ` Decision: ${session.decision ?? 'N/A'}`,
593
+ ...(attackResult ? [` Defense held: ${attackResult.defenseHeld}`] : []),
594
+ '',
595
+ magi.admin.formatSeelePanel(),
596
+ ].join('\n'),
597
+ };
598
+ }
599
+ catch (error) {
600
+ return { output: ERR_C(` Seele session failed: ${String(error)}`) };
601
+ }
602
+ }
603
+ case 'self-destruct': {
604
+ if (!subArgs)
605
+ return { output: WARN_C(' Usage: /admin self-destruct <reason>') };
606
+ return {
607
+ confirm: {
608
+ description: [
609
+ '',
610
+ ERR_C(' SELF-DESTRUCT SEQUENCE — 自爆シーケンス'),
611
+ '',
612
+ ` Reason: ${subArgs}`,
613
+ '',
614
+ WARN_C(' 全MAGIユニットが全会一致で承認し、カウントダウンを開始します。'),
615
+ WARN_C(' この操作は不可逆です。'),
616
+ '',
617
+ ].join('\n'),
618
+ async execute() {
619
+ const motion = magi.admin.fileDestructMotion(subArgs);
620
+ for (const unit of ['MELCHIOR', 'BALTHASAR', 'CASPER']) {
621
+ magi.admin.castDestructVote(motion.id, unit, 'APPROVE');
622
+ }
623
+ const result = await magi.admin.executeDestruct(motion.id);
624
+ return {
625
+ output: [
626
+ ERR_C(' SELF-DESTRUCT SEQUENCE COMPLETE'),
627
+ ` Phase: ${result.phase}`,
628
+ result.cipherMessage ? ` 裏コード: ${result.cipherMessage}` : '',
629
+ ].filter(Boolean).join('\n'),
630
+ };
631
+ },
632
+ },
633
+ };
634
+ }
635
+ case 'genesis': {
636
+ if (!subArgs)
637
+ return { output: WARN_C(' Usage: /admin genesis <reason>') };
638
+ return {
639
+ confirm: {
640
+ description: [
641
+ '',
642
+ ERR_C(' NEON GENESIS — 世界のやり直し'),
643
+ '',
644
+ WARN_C(' .magi/ 配下の全審議記録が削除されます。'),
645
+ WARN_C(' 10% の記憶のみ Fisher-Yates で次世代に引き継がれます。'),
646
+ WARN_C(' .cipher ファイルは保全されます。'),
647
+ '',
648
+ ERR_C(' この操作は不可逆です。'),
649
+ '',
650
+ ].join('\n'),
651
+ async execute() {
652
+ const log = await magi.admin.executeGenesis(subArgs, true);
653
+ return {
654
+ output: [
655
+ OK_C(' Genesis complete.'),
656
+ ` Previous deliberations: ${log.totalDeliberations}`,
657
+ ` Survival memories: ${log.survivalMemories.length}`,
658
+ ` Reset at: ${log.resetAt}`,
659
+ ].join('\n'),
660
+ };
661
+ },
662
+ },
663
+ };
664
+ }
665
+ default:
666
+ return { output: adminHelp() };
667
+ }
668
+ },
669
+ });
670
+ // /jobs - Background job management
671
+ commands.set('jobs', {
672
+ description: 'List background jobs',
673
+ usage: '/jobs [cancel <id>]',
674
+ argKind: 'freeform',
675
+ async execute(args) {
676
+ return { action: 'jobs', output: args || '' };
677
+ },
678
+ });
679
+ // ── Setup / Config / Demo ─────────────────────────────────
680
+ commands.set('setup', {
681
+ description: 'Run guided initial setup',
682
+ usage: '/setup',
683
+ argKind: 'none',
684
+ async execute() {
685
+ return {
686
+ interactive: async () => {
687
+ const { runSetup } = await import('../cli/commands/setup.js');
688
+ await runSetup();
689
+ },
690
+ };
691
+ },
692
+ });
693
+ commands.set('config', {
694
+ description: 'View or edit user configuration',
695
+ usage: '/config [set <key> <value> | reset]',
696
+ argKind: 'freeform',
697
+ async execute(args) {
698
+ const { handleConfig } = await import('../cli/commands/config-cmd.js');
699
+ const parts = args.trim().split(/\s+/);
700
+ const action = parts[0] || undefined;
701
+ const key = parts[1] || undefined;
702
+ const value = parts.slice(2).join(' ') || undefined;
703
+ await handleConfig(action, key, value);
704
+ return { output: '' };
705
+ },
706
+ });
707
+ commands.set('demo', {
708
+ description: 'Watch a pre-recorded MAGI deliberation',
709
+ usage: '/demo [--speed <n>]',
710
+ argKind: 'freeform',
711
+ async execute(args) {
712
+ return {
713
+ interactive: async () => {
714
+ const { runDemo } = await import('../cli/commands/demo.js');
715
+ const speedMatch = args.match(/--speed\s+(\S+)/);
716
+ await runDemo({
717
+ speed: speedMatch?.[1],
718
+ tui: false,
719
+ });
720
+ },
721
+ };
722
+ },
723
+ });
724
+ return commands;
725
+ }