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,634 @@
1
+ #!/usr/bin/env node
2
+ import { Command, Option } from 'commander';
3
+ import chalk from 'chalk';
4
+ import { randomBytes } from 'node:crypto';
5
+ import { createInterface } from 'node:readline/promises';
6
+ import { readFile } from 'node:fs/promises';
7
+ import { Magi } from '../src/index.js';
8
+ import { printDeliberation, printHeader } from '../src/reporters/console.js';
9
+ import { initializeTui } from '../src/cli/tui-setup.js';
10
+ import { formatDeliberationJson } from '../src/reporters/json.js';
11
+ import { formatDeliberationMarkdown } from '../src/reporters/markdown.js';
12
+ import { setupGracefulShutdown } from '../src/utils/shutdown.js';
13
+ import { loadUserConfigSafe, mergeWithDefaults } from '../src/config/user-config.js';
14
+ import { setLocale, detectLocale } from '../src/cli/i18n.js';
15
+ import { validateFile } from '../src/utils/file-validator.js';
16
+ import { detectLanguage } from '../src/utils/language-detector.js';
17
+ import { collectProjectContext, collectRelatedFiles } from '../src/context/auto-collector.js';
18
+ setupGracefulShutdown();
19
+ // ── CLI confirmation helper ──────────────────────────────────────
20
+ async function confirmDestructiveAction(description) {
21
+ if (!process.stdin.isTTY) {
22
+ console.error(chalk.red(' Error: TTY required for destructive operations. Use --force for non-interactive mode.'));
23
+ return false;
24
+ }
25
+ const token = `yes-${randomBytes(2).toString('hex')}`;
26
+ console.log(description);
27
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
28
+ const ac = new AbortController();
29
+ const timer = setTimeout(() => ac.abort(), 30_000);
30
+ try {
31
+ const answer = await rl.question(chalk.dim(` 続行するには "${token}" と入力してください (30秒でタイムアウト): `), { signal: ac.signal });
32
+ return answer.trim() === token;
33
+ }
34
+ catch {
35
+ console.log(chalk.dim(' タイムアウトまたはキャンセルされました。'));
36
+ return false;
37
+ }
38
+ finally {
39
+ clearTimeout(timer);
40
+ rl.close();
41
+ }
42
+ }
43
+ // ── User config loading ──────────────────────────────────────────
44
+ let cachedUserConfig;
45
+ let cachedMagiConfig;
46
+ /** Load raw user config (includes tui/language). Initializes locale on first load. */
47
+ async function getRawUserConfig() {
48
+ if (cachedUserConfig !== undefined)
49
+ return cachedUserConfig;
50
+ cachedUserConfig = (await loadUserConfigSafe()) ?? undefined;
51
+ // Apply language setting globally for i18n
52
+ setLocale(cachedUserConfig?.language ?? detectLocale());
53
+ return cachedUserConfig;
54
+ }
55
+ async function getUserConfig() {
56
+ if (cachedMagiConfig !== undefined)
57
+ return cachedMagiConfig;
58
+ const raw = await getRawUserConfig();
59
+ cachedMagiConfig = raw ? mergeWithDefaults(raw) : undefined;
60
+ return cachedMagiConfig;
61
+ }
62
+ /** Build Magi config by merging user config with command-level overrides. */
63
+ async function buildMagiConfig(overrides = {}) {
64
+ const base = await getUserConfig();
65
+ return { ...base, ...overrides };
66
+ }
67
+ /** Resolve TUI enabled: CLI flag overrides user config, default true. */
68
+ async function resolveTuiEnabled(cliFlag) {
69
+ // Commander sets opts.tui=false only when --no-tui is explicitly passed
70
+ if (!cliFlag)
71
+ return false;
72
+ const raw = await getRawUserConfig();
73
+ return raw?.tui?.enabled ?? true;
74
+ }
75
+ /** Resolve sound enabled: CLI flag overrides user config. */
76
+ async function resolveSoundEnabled(cliFlag) {
77
+ if (cliFlag !== undefined)
78
+ return cliFlag;
79
+ const raw = await getRawUserConfig();
80
+ return raw?.tui?.soundEnabled ?? false;
81
+ }
82
+ const program = new Command();
83
+ program
84
+ .name('magi')
85
+ .description('MAGI System - Multi-AI CLI Deliberation Framework')
86
+ .version('0.1.0');
87
+ program
88
+ .command('deliberate')
89
+ .description('Start a MAGI deliberation on a topic')
90
+ .argument('<prompt>', 'The topic or question to deliberate')
91
+ .addOption(new Option('-t, --type <type>', 'Task type').default('custom')
92
+ .choices(['code-review', 'architecture-decision', 'bug-analysis', 'implementation-plan', 'security-audit', 'custom']))
93
+ .option('-r, --rounds <n>', 'Maximum rounds', '3')
94
+ .option('-o, --output <format>', 'Output format: console, json, markdown', 'console')
95
+ .option('--no-cross-exam', 'Skip cross-examination phase')
96
+ .option('--no-cache', 'Disable result caching')
97
+ .addOption(new Option('--deadlock <strategy>', 'Deadlock strategy')
98
+ .choices(['escalate', 'melchior-tiebreak', 'highest-confidence', 'human-in-the-loop']))
99
+ .option('--no-tui', 'Disable Evangelion-themed full-screen TUI mode')
100
+ .option('--sound', 'Enable terminal bell for MAGI TUI events')
101
+ .option('--berserk', 'Enable BERSERK mode (5 strategies × 3 units, unanimous deathmatch)')
102
+ .option('--no-auto-context', 'Disable automatic project context collection')
103
+ .option('--file-access', 'Allow CLIs to read project files during deliberation')
104
+ .action(async (prompt, opts) => {
105
+ const fileAccess = opts.fileAccess ? { enabled: true } : undefined;
106
+ const magiConfig = await buildMagiConfig({ logLevel: 'warn', cacheEnabled: opts.cache, ...(fileAccess && { fileAccess }) });
107
+ const magi = new Magi(magiConfig);
108
+ const tuiEnabled = await resolveTuiEnabled(opts.tui);
109
+ const soundEnabled = await resolveSoundEnabled(opts.sound);
110
+ const { tuiInstance } = await initializeTui(magi.getEventBus(), tuiEnabled, { soundEnabled });
111
+ // Collect project context if enabled
112
+ let context;
113
+ if (opts.autoContext) {
114
+ try {
115
+ const ctx = await collectProjectContext();
116
+ if (ctx.charCount > 0)
117
+ context = ctx.text;
118
+ }
119
+ catch { /* graceful degradation */ }
120
+ }
121
+ try {
122
+ let result;
123
+ if (opts.berserk) {
124
+ // BERSERK mode: bypass normal orchestrator
125
+ result = await magi.deliberateBerserk({
126
+ type: opts.type,
127
+ title: prompt.slice(0, 80),
128
+ description: prompt,
129
+ context,
130
+ });
131
+ }
132
+ else {
133
+ const phases = opts.crossExam
134
+ ? ['initial-opinion', 'cross-examination', 'final-vote']
135
+ : ['initial-opinion', 'final-vote'];
136
+ const consensusOverride = opts.deadlock
137
+ ? { deadlockStrategy: opts.deadlock }
138
+ : undefined;
139
+ const maxRounds = parseInt(opts.rounds, 10);
140
+ if (Number.isNaN(maxRounds) || maxRounds < 1) {
141
+ console.error(chalk.red('\n Error: --rounds must be a positive integer'));
142
+ process.exit(1);
143
+ }
144
+ result = await magi.deliberate({
145
+ type: opts.type,
146
+ title: prompt.slice(0, 80),
147
+ description: prompt,
148
+ context,
149
+ config: {
150
+ maxRounds,
151
+ phases,
152
+ ...(consensusOverride && { consensus: consensusOverride }),
153
+ },
154
+ });
155
+ }
156
+ // Wait for user to see the result, then dispose TUI
157
+ if (tuiInstance)
158
+ await tuiInstance.waitForDismiss();
159
+ tuiInstance?.dispose();
160
+ switch (opts.output) {
161
+ case 'json':
162
+ console.log(formatDeliberationJson(result));
163
+ break;
164
+ case 'markdown':
165
+ console.log(formatDeliberationMarkdown(result));
166
+ break;
167
+ default:
168
+ printDeliberation(result);
169
+ }
170
+ }
171
+ catch (error) {
172
+ tuiInstance?.dispose();
173
+ console.error(chalk.red(`\n Deliberation failed: ${String(error)}`));
174
+ process.exit(1);
175
+ }
176
+ });
177
+ program
178
+ .command('review')
179
+ .description('Code review deliberation')
180
+ .argument('<file>', 'File to review')
181
+ .option('-o, --output <format>', 'Output format', 'console')
182
+ .option('--no-cache', 'Disable result caching')
183
+ .option('--deadlock <strategy>', 'Deadlock strategy')
184
+ .option('--no-tui', 'Disable Evangelion-themed full-screen TUI mode')
185
+ .option('--sound', 'Enable terminal bell for MAGI TUI events')
186
+ .option('--no-auto-context', 'Disable automatic related file collection')
187
+ .option('--file-access', 'Allow CLIs to read project files during deliberation')
188
+ .action(async (file, opts) => {
189
+ // Validate file using shared file-validator
190
+ const validation = await validateFile(file, {
191
+ maxBytes: 524_288, // 512 KiB
192
+ rejectBinary: true,
193
+ });
194
+ if (!validation.ok) {
195
+ console.error(chalk.red(`\n ${validation.error.message}`));
196
+ process.exit(1);
197
+ }
198
+ const content = await readFile(validation.file.absolutePath, 'utf-8');
199
+ const language = detectLanguage(file);
200
+ const fileAccess = opts.fileAccess ? { enabled: true } : undefined;
201
+ const magiConfig = await buildMagiConfig({ logLevel: 'warn', cacheEnabled: opts.cache, ...(fileAccess && { fileAccess }) });
202
+ const magi = new Magi(magiConfig);
203
+ const tuiEnabled = await resolveTuiEnabled(opts.tui);
204
+ const soundEnabled = await resolveSoundEnabled(opts.sound);
205
+ const { tuiInstance } = await initializeTui(magi.getEventBus(), tuiEnabled, { soundEnabled });
206
+ // Collect related files if auto-context enabled
207
+ let relatedArtifacts = [];
208
+ let context;
209
+ if (opts.autoContext) {
210
+ try {
211
+ const [related, projectCtx] = await Promise.allSettled([
212
+ collectRelatedFiles(file, content),
213
+ collectProjectContext(),
214
+ ]);
215
+ if (related.status === 'fulfilled')
216
+ relatedArtifacts = related.value;
217
+ if (projectCtx.status === 'fulfilled' && projectCtx.value.charCount > 0) {
218
+ context = projectCtx.value.text;
219
+ }
220
+ }
221
+ catch { /* graceful degradation */ }
222
+ }
223
+ try {
224
+ const consensusOverride = opts.deadlock
225
+ ? { deadlockStrategy: opts.deadlock }
226
+ : undefined;
227
+ const result = await magi.deliberate({
228
+ type: 'code-review',
229
+ title: `Code Review: ${file}`,
230
+ description: `Review the following code file for quality, correctness, security, and best practices.`,
231
+ artifacts: [
232
+ {
233
+ type: 'file',
234
+ path: validation.file.relativePath,
235
+ content,
236
+ language,
237
+ },
238
+ ...relatedArtifacts,
239
+ ],
240
+ context,
241
+ ...(consensusOverride && { config: { consensus: consensusOverride } }),
242
+ });
243
+ // Wait for user to see the result, then dispose TUI
244
+ if (tuiInstance)
245
+ await tuiInstance.waitForDismiss();
246
+ tuiInstance?.dispose();
247
+ switch (opts.output) {
248
+ case 'json':
249
+ console.log(formatDeliberationJson(result));
250
+ break;
251
+ case 'markdown':
252
+ console.log(formatDeliberationMarkdown(result));
253
+ break;
254
+ default:
255
+ printDeliberation(result);
256
+ }
257
+ }
258
+ catch (error) {
259
+ tuiInstance?.dispose();
260
+ console.error(chalk.red(`\n Review failed: ${String(error)}`));
261
+ process.exit(1);
262
+ }
263
+ });
264
+ program
265
+ .command('decide')
266
+ .description('Architecture decision deliberation')
267
+ .argument('<question>', 'The architecture question to decide')
268
+ .option('-o, --output <format>', 'Output format', 'console')
269
+ .option('--no-cache', 'Disable result caching')
270
+ .option('--deadlock <strategy>', 'Deadlock strategy')
271
+ .option('--no-tui', 'Disable Evangelion-themed full-screen TUI mode')
272
+ .option('--sound', 'Enable terminal bell for MAGI TUI events')
273
+ .option('--no-auto-context', 'Disable automatic project context collection')
274
+ .option('--file-access', 'Allow CLIs to read project files during deliberation')
275
+ .action(async (question, opts) => {
276
+ const fileAccess = opts.fileAccess ? { enabled: true } : undefined;
277
+ const magiConfig = await buildMagiConfig({ logLevel: 'warn', cacheEnabled: opts.cache, ...(fileAccess && { fileAccess }) });
278
+ const magi = new Magi(magiConfig);
279
+ const tuiEnabled = await resolveTuiEnabled(opts.tui);
280
+ const soundEnabled = await resolveSoundEnabled(opts.sound);
281
+ const { tuiInstance } = await initializeTui(magi.getEventBus(), tuiEnabled, { soundEnabled });
282
+ // Collect project context if enabled
283
+ let context;
284
+ if (opts.autoContext) {
285
+ try {
286
+ const ctx = await collectProjectContext();
287
+ if (ctx.charCount > 0)
288
+ context = ctx.text;
289
+ }
290
+ catch { /* graceful degradation */ }
291
+ }
292
+ try {
293
+ const consensusOverride = opts.deadlock
294
+ ? { deadlockStrategy: opts.deadlock }
295
+ : undefined;
296
+ const result = await magi.deliberate({
297
+ type: 'architecture-decision',
298
+ title: question.slice(0, 80),
299
+ description: question,
300
+ context,
301
+ ...(consensusOverride && { config: { consensus: consensusOverride } }),
302
+ });
303
+ // Wait for user to see the result, then dispose TUI
304
+ if (tuiInstance)
305
+ await tuiInstance.waitForDismiss();
306
+ tuiInstance?.dispose();
307
+ switch (opts.output) {
308
+ case 'json':
309
+ console.log(formatDeliberationJson(result));
310
+ break;
311
+ case 'markdown':
312
+ console.log(formatDeliberationMarkdown(result));
313
+ break;
314
+ default:
315
+ printDeliberation(result);
316
+ }
317
+ }
318
+ catch (error) {
319
+ tuiInstance?.dispose();
320
+ console.error(chalk.red(`\n Decision failed: ${String(error)}`));
321
+ process.exit(1);
322
+ }
323
+ });
324
+ program
325
+ .command('tokens')
326
+ .description('Show token usage summary for past deliberations')
327
+ .action(async () => {
328
+ printHeader();
329
+ const magi = new Magi(await buildMagiConfig({ logLevel: 'error' }));
330
+ const summaries = await magi.loadAllTokens();
331
+ if (summaries.length === 0) {
332
+ console.log(chalk.yellow(' No token data found.\n'));
333
+ return;
334
+ }
335
+ let grandTotal = 0;
336
+ for (const s of summaries) {
337
+ grandTotal += s.totalEstimatedTokens;
338
+ console.log(chalk.cyan(` ${s.deliberationId}:`));
339
+ console.log(` Tokens: ~${s.totalEstimatedTokens} Records: ${s.recordCount}`);
340
+ }
341
+ console.log(chalk.bold(`\n Total: ~${grandTotal} tokens\n`));
342
+ });
343
+ program
344
+ .command('watch')
345
+ .description('Start angel detection daemon (monitors git for suspicious changes)')
346
+ .option('-i, --interval <ms>', 'Polling interval in milliseconds', '30000')
347
+ .action(async (opts) => {
348
+ printHeader();
349
+ const pollIntervalMs = parseInt(opts.interval, 10);
350
+ if (Number.isNaN(pollIntervalMs) || pollIntervalMs < 1) {
351
+ console.error(chalk.red('\n Error: --interval must be a positive integer (milliseconds)'));
352
+ process.exit(1);
353
+ }
354
+ const magi = new Magi(await buildMagiConfig({ logLevel: 'warn' }));
355
+ magi.on('angel:detected', (event) => {
356
+ const levelColor = event.threatLevel === 'RED' ? chalk.red
357
+ : event.threatLevel === 'ORANGE' ? chalk.yellow
358
+ : chalk.cyan;
359
+ console.log(levelColor(`\n [ANGEL DETECTED] ${event.angel} — ${event.threatLevel}`));
360
+ console.log(` Commit: ${event.commitHash}`);
361
+ console.log(` Files: ${event.affectedFiles.join(', ')}\n`);
362
+ });
363
+ console.log(chalk.cyan(` Starting angel detection daemon (interval: ${opts.interval}ms)...`));
364
+ console.log(chalk.gray(' Press Ctrl+C to stop.\n'));
365
+ const handle = magi.runtime.startWatch({ pollIntervalMs });
366
+ process.on('SIGINT', () => {
367
+ handle.stop();
368
+ console.log(chalk.yellow('\n Angel detection daemon stopped.'));
369
+ process.exit(0);
370
+ });
371
+ });
372
+ program
373
+ .command('self-destruct')
374
+ .description('Initiate MAGI self-destruct sequence (requires unanimous approval)')
375
+ .argument('<reason>', 'Reason for self-destruct')
376
+ .option('--force', 'Skip interactive confirmation')
377
+ .action(async (reason, opts) => {
378
+ printHeader();
379
+ // Interactive confirmation unless --force
380
+ if (!opts.force) {
381
+ const confirmed = await confirmDestructiveAction(chalk.red.bold('\n ⚠ SELF-DESTRUCT SEQUENCE\n') +
382
+ chalk.gray(` Reason: ${reason}\n`) +
383
+ chalk.yellow(' This will permanently destroy all MAGI deliberation data.\n'));
384
+ if (!confirmed) {
385
+ console.log(chalk.green('\n Self-destruct ABORTED.\n'));
386
+ return;
387
+ }
388
+ }
389
+ const magi = new Magi(await buildMagiConfig({ logLevel: 'warn' }));
390
+ magi.on('selfdestruct:countdown', (event) => {
391
+ console.log(chalk.red(` ${event.depth.label} (${event.depth.depth}m)`));
392
+ });
393
+ console.log(chalk.red.bold('\n ⚠ SELF-DESTRUCT SEQUENCE INITIATED'));
394
+ console.log(chalk.gray(` Reason: ${reason}\n`));
395
+ const motion = magi.admin.fileDestructMotion(reason);
396
+ console.log(chalk.yellow(' Awaiting votes from all MAGI units...'));
397
+ console.log(chalk.yellow(' (This is a ceremony — actual file deletion is the caller\'s responsibility)\n'));
398
+ for (const unit of ['MELCHIOR', 'BALTHASAR', 'CASPER']) {
399
+ console.log(chalk.cyan(` ${unit}: APPROVE`));
400
+ magi.admin.castDestructVote(motion.id, unit, 'APPROVE');
401
+ }
402
+ if (magi.admin.getDestructPhase() === 'COUNTDOWN') {
403
+ console.log(chalk.red.bold('\n UNANIMOUS APPROVAL — COUNTDOWN INITIATED\n'));
404
+ const result = await magi.admin.executeDestruct(motion.id);
405
+ console.log(chalk.red.bold('\n SELF-DESTRUCT SEQUENCE COMPLETE'));
406
+ if (result.cipherMessage) {
407
+ console.log(chalk.magenta(`\n 裏コード: ${result.cipherMessage}`));
408
+ }
409
+ }
410
+ else {
411
+ console.log(chalk.green('\n Self-destruct ABORTED — sequence cancelled.'));
412
+ }
413
+ console.log('');
414
+ });
415
+ program
416
+ .command('doctor')
417
+ .description('Check MAGI system health')
418
+ .action(async () => {
419
+ printHeader();
420
+ console.log(chalk.cyan(' Checking MAGI system health...\n'));
421
+ const magi = new Magi(await buildMagiConfig({ logLevel: 'error' }));
422
+ const results = await magi.healthCheck();
423
+ for (const [unit, result] of results) {
424
+ if (result instanceof Error) {
425
+ console.log(chalk.red(` ✗ ${unit}: ${result.message}`));
426
+ }
427
+ else {
428
+ console.log(chalk.green(` ✓ ${unit}: ${result}`));
429
+ }
430
+ }
431
+ console.log('');
432
+ });
433
+ program
434
+ .command('neon-genesis')
435
+ .description('Execute Neon Genesis — full MAGI system reset')
436
+ .argument('<reason>', 'Reason for the reset')
437
+ .option('--force', 'Skip interactive confirmation')
438
+ .action(async (reason, opts) => {
439
+ printHeader();
440
+ // Interactive confirmation unless --force
441
+ if (!opts.force) {
442
+ const confirmed = await confirmDestructiveAction(chalk.red.bold('\n NEON GENESIS — 世界のやり直し\n') +
443
+ chalk.gray(` Reason: ${reason}\n`) +
444
+ chalk.yellow(' This will permanently delete ALL deliberation data in .magi/\n'));
445
+ if (!confirmed) {
446
+ console.log(chalk.green('\n Neon Genesis ABORTED.\n'));
447
+ return;
448
+ }
449
+ }
450
+ const magi = new Magi(await buildMagiConfig({ logLevel: 'warn' }));
451
+ magi.on('genesis:started', (e) => {
452
+ console.log(chalk.red.bold('\n NEON GENESIS — 世界のやり直し'));
453
+ console.log(chalk.gray(` Reset ID: ${e.resetId}`));
454
+ console.log(chalk.gray(` Reason: ${reason}\n`));
455
+ });
456
+ magi.on('genesis:complete', (e) => {
457
+ console.log(chalk.cyan(` Survival memories: ${e.survivalMemories}`));
458
+ console.log(chalk.cyan(` Cipher preserved: ${e.cipherPreserved}`));
459
+ });
460
+ try {
461
+ // Always pass force=true since we already confirmed interactively
462
+ const log = await magi.admin.executeGenesis(reason, true);
463
+ console.log(chalk.green.bold('\n Genesis complete.'));
464
+ console.log(chalk.gray(` Previous deliberations: ${log.totalDeliberations}`));
465
+ console.log(chalk.gray(` Reset at: ${log.resetAt}\n`));
466
+ }
467
+ catch (error) {
468
+ console.error(chalk.red(`\n Neon Genesis failed: ${String(error)}`));
469
+ process.exit(1);
470
+ }
471
+ });
472
+ program
473
+ .command('iruel-battle')
474
+ .description('Run Iruel battle simulation (Byzantine fault tolerance test)')
475
+ .option('--simulation', 'Run full battle simulation')
476
+ .action(async (opts) => {
477
+ printHeader();
478
+ const magi = new Magi(await buildMagiConfig({ logLevel: 'warn' }));
479
+ magi.on('iruel:infiltration', (e) => {
480
+ const color = e.corruptionLevel === 'COMPROMISED' ? chalk.red
481
+ : e.corruptionLevel === 'UNDER_ATTACK' ? chalk.yellow
482
+ : chalk.cyan;
483
+ console.log(color(` [IRUEL] ${e.unit} — ${e.corruptionLevel} (score: ${e.overallScore.toFixed(2)})`));
484
+ });
485
+ magi.on('iruel:counterhack', (e) => {
486
+ console.log(chalk.green(` [COUNTER-HACK] ${e.strategy.name} → ${e.strategy.targetUnit}`));
487
+ });
488
+ magi.on('iruel:neutralized', (e) => {
489
+ console.log(chalk.green.bold(`\n [NEUTRALIZED] Battle phase: ${e.phase}`));
490
+ console.log(chalk.gray(` Compromised units: ${e.compromisedUnits.join(', ')}\n`));
491
+ });
492
+ console.log(chalk.red.bold('\n IRUEL BATTLE — ビザンチン障害耐性テスト\n'));
493
+ if (opts.simulation) {
494
+ const result = await magi.diagnostics.simulateIruelBattle();
495
+ console.log(chalk.cyan(` Final phase: ${result}\n`));
496
+ }
497
+ else {
498
+ console.log(chalk.yellow(' Use --simulation to run full battle sequence.\n'));
499
+ console.log(chalk.gray(` Current phase: ${magi.diagnostics.getIruelPhase()}\n`));
500
+ }
501
+ });
502
+ program
503
+ .command('evolve')
504
+ .description('Run S² Engine self-evolution cycle')
505
+ .option('--dry-run', 'Generate proposals without applying (default)')
506
+ .option('--no-dry-run', 'Apply proposals for real')
507
+ .option('--no-meta', 'Skip meta-deliberation')
508
+ .option('--max-proposals <n>', 'Maximum proposals to generate', '3')
509
+ .action(async (opts) => {
510
+ printHeader();
511
+ const magi = new Magi(await buildMagiConfig({ logLevel: 'warn' }));
512
+ magi.on('evolution:started', (e) => {
513
+ console.log(chalk.cyan(`\n S² Engine — Generation ${e.generation}`));
514
+ });
515
+ magi.on('evolution:proposal', (e) => {
516
+ console.log(chalk.yellow(` [PROPOSAL] ${e.proposal.title} (${e.proposal.category}, confidence: ${(e.proposal.confidence * 100).toFixed(0)}%)`));
517
+ });
518
+ console.log(chalk.magenta.bold('\n S² ENGINE — 自己進化\n'));
519
+ try {
520
+ const maxProposals = parseInt(opts.maxProposals, 10);
521
+ if (Number.isNaN(maxProposals) || maxProposals < 1) {
522
+ console.error(chalk.red('\n Error: --max-proposals must be a positive integer'));
523
+ process.exit(1);
524
+ }
525
+ const gen = await magi.diagnostics.evolve({
526
+ dryRun: opts.dryRun !== false,
527
+ maxProposals,
528
+ });
529
+ console.log(chalk.green(`\n Evolution cycle complete (Generation ${gen.generation})`));
530
+ console.log(chalk.gray(` Proposals: ${gen.proposals.length}`));
531
+ console.log(chalk.gray(` Approved: ${gen.proposals.filter(p => p.approvedByMeta).length}`));
532
+ console.log(chalk.gray(` Diagnostics: ${gen.diagnostics.totalDeliberations} deliberations, ` +
533
+ `${(gen.diagnostics.consensusRate * 100).toFixed(0)}% consensus rate\n`));
534
+ }
535
+ catch (error) {
536
+ console.error(chalk.red(`\n Evolution failed: ${String(error)}`));
537
+ process.exit(1);
538
+ }
539
+ });
540
+ program
541
+ .command('seele')
542
+ .description('Start Seele council session (distributed MAGI consensus)')
543
+ .option('--node <id>', 'Local node ID', 'MAGI-01')
544
+ .option('--port <port>', 'Server port', '7701')
545
+ .option('--simulation', 'Run simulated session')
546
+ .option('--attack', 'Include attack phase in simulation')
547
+ .action(async (opts) => {
548
+ printHeader();
549
+ const magi = new Magi(await buildMagiConfig({ logLevel: 'warn' }));
550
+ magi.on('seele:gathering', (e) => {
551
+ console.log(chalk.cyan(` [GATHERING] ${e.nodes.length} nodes connected`));
552
+ });
553
+ magi.on('seele:attack', (e) => {
554
+ console.log(chalk.red(` [ATTACK] ${e.attackers.join(', ')} → ${e.target}`));
555
+ });
556
+ magi.on('seele:concluded', (e) => {
557
+ console.log(chalk.green(` [CONCLUDED] Decision: ${e.decision ?? 'N/A'}, Leader: ${e.leader ?? 'N/A'}`));
558
+ });
559
+ console.log(chalk.magenta.bold('\n SEELE — 分散MAGI合議\n'));
560
+ if (opts.simulation) {
561
+ const session = await magi.admin.simulateSeele();
562
+ if (opts.node !== 'MAGI-01' || opts.port !== '7701') {
563
+ console.log(chalk.yellow(' Note: simulation mode uses the built-in in-memory topology; --node/--port are retained for CLI compatibility.'));
564
+ }
565
+ if (opts.attack) {
566
+ console.log(chalk.red('\n Executing attack phase...'));
567
+ const result = await magi.admin.executeSeeleAttack(['MAGI-05', 'MAGI-06'], 'MAGI-01');
568
+ console.log(chalk.yellow(` Defense held: ${result.defenseHeld}`));
569
+ }
570
+ console.log(chalk.cyan(`\n Session: ${session.id.slice(0, 8)}`));
571
+ console.log(chalk.gray(` Leader: ${session.leader}`));
572
+ console.log(chalk.gray(` Decision: ${session.decision}`));
573
+ console.log(chalk.gray(` Messages: ${session.messages.length}`));
574
+ console.log('\n' + magi.admin.formatSeelePanel() + '\n');
575
+ }
576
+ else {
577
+ console.log(chalk.yellow(' Use --simulation to run a simulated session.\n'));
578
+ }
579
+ });
580
+ // ── Setup & Config commands ──────────────────────────────────────
581
+ program
582
+ .command('setup')
583
+ .description('Guided initial setup — detect CLIs and create .magi/config.json')
584
+ .action(async () => {
585
+ const { runSetup } = await import('../src/cli/commands/setup.js');
586
+ await runSetup();
587
+ });
588
+ program
589
+ .command('demo')
590
+ .description('Watch a pre-recorded MAGI deliberation')
591
+ .option('--speed <n>', 'Playback speed multiplier', '1')
592
+ .option('--no-tui', 'Disable TUI, use text mode')
593
+ .option('--sound', 'Enable terminal bell')
594
+ .action(async (opts) => {
595
+ const { runDemo } = await import('../src/cli/commands/demo.js');
596
+ const tuiEnabled = await resolveTuiEnabled(opts.tui ?? true);
597
+ const soundEnabled = await resolveSoundEnabled(opts.sound);
598
+ await runDemo({ ...opts, tui: tuiEnabled, sound: soundEnabled });
599
+ });
600
+ program
601
+ .command('config')
602
+ .description('View or edit user configuration')
603
+ .argument('[action]', 'show | set | reset')
604
+ .argument('[key]', 'config key (dot-path)')
605
+ .argument('[value]', 'new value')
606
+ .action(async (action, key, value) => {
607
+ const { handleConfig } = await import('../src/cli/commands/config-cmd.js');
608
+ await handleConfig(action, key, value);
609
+ });
610
+ // Default action: launch interactive REPL when no subcommand is provided
611
+ program
612
+ .addOption(new Option('--log-level <level>', 'Log level').choices(['debug', 'info', 'warn', 'error']))
613
+ .option('--no-cache', 'Disable deliberation cache')
614
+ .action(async () => {
615
+ if (!process.stdin.isTTY) {
616
+ console.error('MAGI REPL requires an interactive terminal (TTY).');
617
+ process.exit(1);
618
+ }
619
+ const opts = program.opts();
620
+ const cliOverrides = {
621
+ ...(opts.logLevel ? { logLevel: opts.logLevel } : {}),
622
+ ...(opts.cache === false ? { cacheEnabled: false } : {}),
623
+ };
624
+ const magiConfig = await buildMagiConfig(cliOverrides);
625
+ const raw = await getRawUserConfig();
626
+ const { MagiRepl } = await import('../src/repl/magi-repl.js');
627
+ const repl = new MagiRepl({
628
+ magiConfig,
629
+ tuiEnabled: raw?.tui?.enabled ?? true,
630
+ soundEnabled: raw?.tui?.soundEnabled ?? false,
631
+ });
632
+ await repl.start();
633
+ });
634
+ program.parse();
@@ -0,0 +1,34 @@
1
+ import type { ICliAdapter, AdapterConfig, AdapterRequest, AdapterResponse } from '../types/adapter.js';
2
+ import { AdapterError } from '../types/adapter.js';
3
+ import type { MagiUnit, CliTool } from '../types/core.js';
4
+ export declare abstract class AbstractCliAdapter implements ICliAdapter {
5
+ protected config: AdapterConfig;
6
+ abstract readonly unit: MagiUnit;
7
+ abstract readonly tool: CliTool;
8
+ abstract readonly displayName: string;
9
+ constructor(config: AdapterConfig);
10
+ abstract supportsStructuredOutput(): boolean;
11
+ abstract supportsSystemPrompt(): boolean;
12
+ abstract buildCommand(request: AdapterRequest): string[];
13
+ /** All adapters use stdin for prompt delivery (security: no shell injection via args). */
14
+ protected usesStdinInput(): boolean;
15
+ /**
16
+ * Build stdin data with JSON output instructions and optional system context.
17
+ *
18
+ * Default implementation embeds CRITICAL INSTRUCTION and few-shot examples
19
+ * for CLIs that don't support structured output natively (Codex, Gemini).
20
+ * Subclasses with native structured output (Claude) should override this.
21
+ */
22
+ protected getStdinData(request: AdapterRequest): string;
23
+ healthCheck(): Promise<string>;
24
+ execute(request: AdapterRequest): Promise<AdapterResponse>;
25
+ protected executeOnce(request: AdapterRequest, timeoutMs: number): Promise<AdapterResponse>;
26
+ /**
27
+ * Parse the CLI's raw output into structured form.
28
+ *
29
+ * Default implementation uses extractJson for heuristic JSON extraction.
30
+ * All three adapters (Claude, Codex, Gemini) share this same logic.
31
+ */
32
+ protected parseResponse(raw: string): Omit<AdapterResponse, 'meta'>;
33
+ protected isRetryable(error: AdapterError): boolean;
34
+ }