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.
- package/LICENSE +21 -0
- package/README.ja.md +377 -0
- package/README.md +377 -0
- package/dist/bin/magi-benchmark.d.ts +14 -0
- package/dist/bin/magi-benchmark.js +93 -0
- package/dist/bin/magi-mcp.d.ts +8 -0
- package/dist/bin/magi-mcp.js +28 -0
- package/dist/bin/magi.d.ts +2 -0
- package/dist/bin/magi.js +634 -0
- package/dist/src/adapters/base.d.ts +34 -0
- package/dist/src/adapters/base.js +149 -0
- package/dist/src/adapters/claude.d.ts +29 -0
- package/dist/src/adapters/claude.js +65 -0
- package/dist/src/adapters/codex.d.ts +21 -0
- package/dist/src/adapters/codex.js +41 -0
- package/dist/src/adapters/gemini.d.ts +18 -0
- package/dist/src/adapters/gemini.js +31 -0
- package/dist/src/adapters/registry.d.ts +19 -0
- package/dist/src/adapters/registry.js +59 -0
- package/dist/src/audit/hash-chain.d.ts +21 -0
- package/dist/src/audit/hash-chain.js +70 -0
- package/dist/src/audit/types.d.ts +25 -0
- package/dist/src/audit/types.js +1 -0
- package/dist/src/audit/writer.d.ts +18 -0
- package/dist/src/audit/writer.js +100 -0
- package/dist/src/benchmark/golden-tasks.d.ts +9 -0
- package/dist/src/benchmark/golden-tasks.js +476 -0
- package/dist/src/benchmark/reporter.d.ts +5 -0
- package/dist/src/benchmark/reporter.js +107 -0
- package/dist/src/benchmark/runner.d.ts +30 -0
- package/dist/src/benchmark/runner.js +224 -0
- package/dist/src/benchmark/scorer.d.ts +12 -0
- package/dist/src/benchmark/scorer.js +124 -0
- package/dist/src/benchmark/types.d.ts +54 -0
- package/dist/src/benchmark/types.js +1 -0
- package/dist/src/cache/deliberation-cache.d.ts +49 -0
- package/dist/src/cache/deliberation-cache.js +127 -0
- package/dist/src/cli/commands/config-cmd.d.ts +11 -0
- package/dist/src/cli/commands/config-cmd.js +190 -0
- package/dist/src/cli/commands/demo.d.ts +12 -0
- package/dist/src/cli/commands/demo.js +66 -0
- package/dist/src/cli/commands/setup.d.ts +7 -0
- package/dist/src/cli/commands/setup.js +182 -0
- package/dist/src/cli/i18n.d.ts +89 -0
- package/dist/src/cli/i18n.js +176 -0
- package/dist/src/cli/interactive-select.d.ts +27 -0
- package/dist/src/cli/interactive-select.js +130 -0
- package/dist/src/cli/tui-setup.d.ts +24 -0
- package/dist/src/cli/tui-setup.js +42 -0
- package/dist/src/config/cli-detector.d.ts +37 -0
- package/dist/src/config/cli-detector.js +99 -0
- package/dist/src/config/user-config.d.ts +81 -0
- package/dist/src/config/user-config.js +134 -0
- package/dist/src/context/auto-collector.d.ts +43 -0
- package/dist/src/context/auto-collector.js +337 -0
- package/dist/src/context/manager.d.ts +35 -0
- package/dist/src/context/manager.js +162 -0
- package/dist/src/context/serializer.d.ts +20 -0
- package/dist/src/context/serializer.js +52 -0
- package/dist/src/demo/recorded-deliberation.d.ts +13 -0
- package/dist/src/demo/recorded-deliberation.js +277 -0
- package/dist/src/engine/angel-detector.d.ts +83 -0
- package/dist/src/engine/angel-detector.js +334 -0
- package/dist/src/engine/at-field.d.ts +40 -0
- package/dist/src/engine/at-field.js +195 -0
- package/dist/src/engine/berserk-orchestrator.d.ts +66 -0
- package/dist/src/engine/berserk-orchestrator.js +378 -0
- package/dist/src/engine/change-metrics.d.ts +56 -0
- package/dist/src/engine/change-metrics.js +214 -0
- package/dist/src/engine/consensus.d.ts +20 -0
- package/dist/src/engine/consensus.js +146 -0
- package/dist/src/engine/dead-sea-scrolls.d.ts +132 -0
- package/dist/src/engine/dead-sea-scrolls.js +610 -0
- package/dist/src/engine/drift-detector.d.ts +39 -0
- package/dist/src/engine/drift-detector.js +225 -0
- package/dist/src/engine/dummy-plug.d.ts +44 -0
- package/dist/src/engine/dummy-plug.js +190 -0
- package/dist/src/engine/engram-manager.d.ts +55 -0
- package/dist/src/engine/engram-manager.js +306 -0
- package/dist/src/engine/events.d.ts +130 -0
- package/dist/src/engine/events.js +44 -0
- package/dist/src/engine/gospel.d.ts +30 -0
- package/dist/src/engine/gospel.js +129 -0
- package/dist/src/engine/hallucination-detector.d.ts +33 -0
- package/dist/src/engine/hallucination-detector.js +215 -0
- package/dist/src/engine/human-resolver.d.ts +19 -0
- package/dist/src/engine/human-resolver.js +89 -0
- package/dist/src/engine/instrumentality.d.ts +64 -0
- package/dist/src/engine/instrumentality.js +297 -0
- package/dist/src/engine/iruel-battle.d.ts +79 -0
- package/dist/src/engine/iruel-battle.js +319 -0
- package/dist/src/engine/kernel/deliberation-kernel.d.ts +12 -0
- package/dist/src/engine/kernel/deliberation-kernel.js +303 -0
- package/dist/src/engine/kernel/index.d.ts +8 -0
- package/dist/src/engine/kernel/index.js +7 -0
- package/dist/src/engine/kernel/phase-runner.d.ts +10 -0
- package/dist/src/engine/kernel/phase-runner.js +155 -0
- package/dist/src/engine/kernel/post-processor.d.ts +17 -0
- package/dist/src/engine/kernel/post-processor.js +131 -0
- package/dist/src/engine/kernel/types.d.ts +107 -0
- package/dist/src/engine/kernel/types.js +1 -0
- package/dist/src/engine/kernel/unit-executor.d.ts +6 -0
- package/dist/src/engine/kernel/unit-executor.js +132 -0
- package/dist/src/engine/lcl-manager.d.ts +44 -0
- package/dist/src/engine/lcl-manager.js +143 -0
- package/dist/src/engine/middleware/cache.d.ts +7 -0
- package/dist/src/engine/middleware/cache.js +29 -0
- package/dist/src/engine/middleware/chain.d.ts +18 -0
- package/dist/src/engine/middleware/chain.js +45 -0
- package/dist/src/engine/middleware/firewall.d.ts +8 -0
- package/dist/src/engine/middleware/firewall.js +24 -0
- package/dist/src/engine/middleware/index.d.ts +4 -0
- package/dist/src/engine/middleware/index.js +3 -0
- package/dist/src/engine/middleware/types.d.ts +43 -0
- package/dist/src/engine/middleware/types.js +1 -0
- package/dist/src/engine/nebuchadnezzar-key.d.ts +61 -0
- package/dist/src/engine/nebuchadnezzar-key.js +203 -0
- package/dist/src/engine/neon-genesis.d.ts +52 -0
- package/dist/src/engine/neon-genesis.js +203 -0
- package/dist/src/engine/objective-judge.d.ts +53 -0
- package/dist/src/engine/objective-judge.js +214 -0
- package/dist/src/engine/offline-mode.d.ts +18 -0
- package/dist/src/engine/offline-mode.js +46 -0
- package/dist/src/engine/orchestrator.d.ts +79 -0
- package/dist/src/engine/orchestrator.js +58 -0
- package/dist/src/engine/secret-cipher.d.ts +26 -0
- package/dist/src/engine/secret-cipher.js +114 -0
- package/dist/src/engine/seele-council.d.ts +90 -0
- package/dist/src/engine/seele-council.js +482 -0
- package/dist/src/engine/self-destruct.d.ts +61 -0
- package/dist/src/engine/self-destruct.js +231 -0
- package/dist/src/engine/self-evolution.d.ts +64 -0
- package/dist/src/engine/self-evolution.js +368 -0
- package/dist/src/engine/sync-rate.d.ts +45 -0
- package/dist/src/engine/sync-rate.js +151 -0
- package/dist/src/engine/type666-firewall.d.ts +76 -0
- package/dist/src/engine/type666-firewall.js +343 -0
- package/dist/src/engine/umbilical-cable.d.ts +41 -0
- package/dist/src/engine/umbilical-cable.js +192 -0
- package/dist/src/index.d.ts +106 -0
- package/dist/src/index.js +426 -0
- package/dist/src/mcp/server.d.ts +38 -0
- package/dist/src/mcp/server.js +196 -0
- package/dist/src/metrics/token-tracker.d.ts +38 -0
- package/dist/src/metrics/token-tracker.js +112 -0
- package/dist/src/parsers/json-extractor.d.ts +9 -0
- package/dist/src/parsers/json-extractor.js +239 -0
- package/dist/src/parsers/opinion-schema.d.ts +81 -0
- package/dist/src/parsers/opinion-schema.js +147 -0
- package/dist/src/parsers/unstructured-parser.d.ts +20 -0
- package/dist/src/parsers/unstructured-parser.js +122 -0
- package/dist/src/pipelines/architecture.d.ts +10 -0
- package/dist/src/pipelines/architecture.js +9 -0
- package/dist/src/pipelines/bug-analysis.d.ts +9 -0
- package/dist/src/pipelines/bug-analysis.js +8 -0
- package/dist/src/pipelines/code-review.d.ts +10 -0
- package/dist/src/pipelines/code-review.js +30 -0
- package/dist/src/pipelines/custom.d.ts +14 -0
- package/dist/src/pipelines/custom.js +29 -0
- package/dist/src/pipelines/registry.d.ts +9 -0
- package/dist/src/pipelines/registry.js +20 -0
- package/dist/src/prompts/personas.d.ts +6 -0
- package/dist/src/prompts/personas.js +44 -0
- package/dist/src/prompts/schemas.d.ts +4 -0
- package/dist/src/prompts/schemas.js +24 -0
- package/dist/src/prompts/templates.d.ts +6 -0
- package/dist/src/prompts/templates.js +91 -0
- package/dist/src/repl/accessibility.d.ts +23 -0
- package/dist/src/repl/accessibility.js +46 -0
- package/dist/src/repl/banner.d.ts +4 -0
- package/dist/src/repl/banner.js +28 -0
- package/dist/src/repl/boot-animation.d.ts +13 -0
- package/dist/src/repl/boot-animation.js +143 -0
- package/dist/src/repl/completer.d.ts +21 -0
- package/dist/src/repl/completer.js +168 -0
- package/dist/src/repl/context.d.ts +24 -0
- package/dist/src/repl/context.js +42 -0
- package/dist/src/repl/display-utils.d.ts +13 -0
- package/dist/src/repl/display-utils.js +65 -0
- package/dist/src/repl/event-listener.d.ts +18 -0
- package/dist/src/repl/event-listener.js +112 -0
- package/dist/src/repl/export-formatter.d.ts +8 -0
- package/dist/src/repl/export-formatter.js +73 -0
- package/dist/src/repl/ghost-text.d.ts +31 -0
- package/dist/src/repl/ghost-text.js +119 -0
- package/dist/src/repl/handoff-animation.d.ts +15 -0
- package/dist/src/repl/handoff-animation.js +65 -0
- package/dist/src/repl/history.d.ts +16 -0
- package/dist/src/repl/history.js +130 -0
- package/dist/src/repl/job-registry.d.ts +26 -0
- package/dist/src/repl/job-registry.js +80 -0
- package/dist/src/repl/magi-repl.d.ts +72 -0
- package/dist/src/repl/magi-repl.js +1008 -0
- package/dist/src/repl/multiline-input.d.ts +45 -0
- package/dist/src/repl/multiline-input.js +78 -0
- package/dist/src/repl/prompt-builder.d.ts +19 -0
- package/dist/src/repl/prompt-builder.js +36 -0
- package/dist/src/repl/repl-state.d.ts +5 -0
- package/dist/src/repl/repl-state.js +19 -0
- package/dist/src/repl/result-display.d.ts +8 -0
- package/dist/src/repl/result-display.js +195 -0
- package/dist/src/repl/session-stats.d.ts +26 -0
- package/dist/src/repl/session-stats.js +119 -0
- package/dist/src/repl/slash-commands.d.ts +60 -0
- package/dist/src/repl/slash-commands.js +725 -0
- package/dist/src/repl/terminal-sanitize.d.ts +14 -0
- package/dist/src/repl/terminal-sanitize.js +19 -0
- package/dist/src/reporters/console.d.ts +7 -0
- package/dist/src/reporters/console.js +78 -0
- package/dist/src/reporters/json.d.ts +2 -0
- package/dist/src/reporters/json.js +3 -0
- package/dist/src/reporters/markdown.d.ts +2 -0
- package/dist/src/reporters/markdown.js +65 -0
- package/dist/src/reporters/streaming.d.ts +20 -0
- package/dist/src/reporters/streaming.js +178 -0
- package/dist/src/tui/activity-log.d.ts +23 -0
- package/dist/src/tui/activity-log.js +67 -0
- package/dist/src/tui/animations.d.ts +39 -0
- package/dist/src/tui/animations.js +167 -0
- package/dist/src/tui/ansi.d.ts +28 -0
- package/dist/src/tui/ansi.js +51 -0
- package/dist/src/tui/boot-sequence.d.ts +11 -0
- package/dist/src/tui/boot-sequence.js +98 -0
- package/dist/src/tui/colors.d.ts +101 -0
- package/dist/src/tui/colors.js +71 -0
- package/dist/src/tui/header.d.ts +24 -0
- package/dist/src/tui/header.js +122 -0
- package/dist/src/tui/index.d.ts +3 -0
- package/dist/src/tui/index.js +3 -0
- package/dist/src/tui/keypress.d.ts +25 -0
- package/dist/src/tui/keypress.js +95 -0
- package/dist/src/tui/layout.d.ts +74 -0
- package/dist/src/tui/layout.js +171 -0
- package/dist/src/tui/magi-tui.d.ts +101 -0
- package/dist/src/tui/magi-tui.js +754 -0
- package/dist/src/tui/panel.d.ts +45 -0
- package/dist/src/tui/panel.js +292 -0
- package/dist/src/tui/screen-buffer.d.ts +54 -0
- package/dist/src/tui/screen-buffer.js +262 -0
- package/dist/src/tui/status-bar.d.ts +25 -0
- package/dist/src/tui/status-bar.js +124 -0
- package/dist/src/tui/terminal-detect.d.ts +26 -0
- package/dist/src/tui/terminal-detect.js +44 -0
- package/dist/src/tui/tui-helpers.d.ts +12 -0
- package/dist/src/tui/tui-helpers.js +37 -0
- package/dist/src/types/adapter.d.ts +75 -0
- package/dist/src/types/adapter.js +36 -0
- package/dist/src/types/config.d.ts +108 -0
- package/dist/src/types/config.js +85 -0
- package/dist/src/types/consensus.d.ts +55 -0
- package/dist/src/types/consensus.js +17 -0
- package/dist/src/types/core.d.ts +178 -0
- package/dist/src/types/core.js +85 -0
- package/dist/src/types/magi-api.d.ts +62 -0
- package/dist/src/types/magi-api.js +7 -0
- package/dist/src/types/phase-h.d.ts +142 -0
- package/dist/src/types/phase-h.js +7 -0
- package/dist/src/types/phase-i.d.ts +186 -0
- package/dist/src/types/phase-i.js +6 -0
- package/dist/src/types/phase-k.d.ts +259 -0
- package/dist/src/types/phase-k.js +6 -0
- package/dist/src/types/phase-l.d.ts +199 -0
- package/dist/src/types/phase-l.js +6 -0
- package/dist/src/types/pipeline.d.ts +37 -0
- package/dist/src/types/pipeline.js +2 -0
- package/dist/src/utils/abstain-factory.d.ts +2 -0
- package/dist/src/utils/abstain-factory.js +18 -0
- package/dist/src/utils/errors.d.ts +34 -0
- package/dist/src/utils/errors.js +59 -0
- package/dist/src/utils/file-validator.d.ts +50 -0
- package/dist/src/utils/file-validator.js +124 -0
- package/dist/src/utils/fire-and-forget.d.ts +5 -0
- package/dist/src/utils/fire-and-forget.js +10 -0
- package/dist/src/utils/flag-validator.d.ts +21 -0
- package/dist/src/utils/flag-validator.js +79 -0
- package/dist/src/utils/freeze.d.ts +8 -0
- package/dist/src/utils/freeze.js +16 -0
- package/dist/src/utils/language-detector.d.ts +16 -0
- package/dist/src/utils/language-detector.js +159 -0
- package/dist/src/utils/latency-tracker.d.ts +45 -0
- package/dist/src/utils/latency-tracker.js +100 -0
- package/dist/src/utils/logger.d.ts +33 -0
- package/dist/src/utils/logger.js +112 -0
- package/dist/src/utils/process.d.ts +40 -0
- package/dist/src/utils/process.js +253 -0
- package/dist/src/utils/retry.d.ts +12 -0
- package/dist/src/utils/retry.js +30 -0
- package/dist/src/utils/safe-fs.d.ts +38 -0
- package/dist/src/utils/safe-fs.js +56 -0
- package/dist/src/utils/safe-json-parse.d.ts +15 -0
- package/dist/src/utils/safe-json-parse.js +49 -0
- package/dist/src/utils/sanitize.d.ts +14 -0
- package/dist/src/utils/sanitize.js +186 -0
- package/dist/src/utils/semaphore.d.ts +22 -0
- package/dist/src/utils/semaphore.js +57 -0
- package/dist/src/utils/shutdown.d.ts +6 -0
- package/dist/src/utils/shutdown.js +51 -0
- package/dist/src/utils/tty.d.ts +5 -0
- package/dist/src/utils/tty.js +7 -0
- package/package.json +82 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deliberation export formatters (JSON / Markdown).
|
|
3
|
+
*
|
|
4
|
+
* Pure functions — no side-effects, no chalk, no filesystem.
|
|
5
|
+
*/
|
|
6
|
+
export function formatDeliberationAsJson(d) {
|
|
7
|
+
return JSON.stringify(d, (_key, value) => {
|
|
8
|
+
if (value instanceof Date)
|
|
9
|
+
return value.toISOString();
|
|
10
|
+
if (value instanceof Error)
|
|
11
|
+
return { message: value.message };
|
|
12
|
+
return value;
|
|
13
|
+
}, 2);
|
|
14
|
+
}
|
|
15
|
+
export function formatDeliberationAsMarkdown(d) {
|
|
16
|
+
const lines = [];
|
|
17
|
+
const c = d.consensus;
|
|
18
|
+
lines.push(`## MAGI Deliberation: ${d.task.title}`);
|
|
19
|
+
lines.push('');
|
|
20
|
+
lines.push(`- **Decision:** ${c.decision}`);
|
|
21
|
+
lines.push(`- **Confidence:** ${(c.confidence * 100).toFixed(0)}%`);
|
|
22
|
+
lines.push(`- **Method:** ${c.method}`);
|
|
23
|
+
lines.push(`- **Duration:** ${(d.totalDurationMs / 1000).toFixed(1)}s`);
|
|
24
|
+
lines.push(`- **Rounds:** ${d.rounds.length}`);
|
|
25
|
+
lines.push('');
|
|
26
|
+
// Unit votes from final round
|
|
27
|
+
const finalRound = d.rounds[d.rounds.length - 1];
|
|
28
|
+
if (finalRound?.opinions && finalRound.opinions.length > 0) {
|
|
29
|
+
lines.push('### Unit Votes');
|
|
30
|
+
lines.push('');
|
|
31
|
+
for (const op of finalRound.opinions) {
|
|
32
|
+
const conf = (op.confidence * 100).toFixed(0);
|
|
33
|
+
lines.push(`- **${op.unit}:** ${op.vote} (${conf}%)`);
|
|
34
|
+
if (op.reasoning) {
|
|
35
|
+
lines.push(` > ${op.reasoning.replace(/\n/g, ' ').slice(0, 200)}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
lines.push('');
|
|
39
|
+
}
|
|
40
|
+
// Summary
|
|
41
|
+
if (c.summary) {
|
|
42
|
+
lines.push('### Summary');
|
|
43
|
+
lines.push('');
|
|
44
|
+
lines.push(c.summary);
|
|
45
|
+
lines.push('');
|
|
46
|
+
}
|
|
47
|
+
// Key points
|
|
48
|
+
const keyPoints = finalRound?.opinions
|
|
49
|
+
?.flatMap(o => o.keyPoints ?? [])
|
|
50
|
+
.filter((v, i, a) => a.indexOf(v) === i)
|
|
51
|
+
.slice(0, 5);
|
|
52
|
+
if (keyPoints && keyPoints.length > 0) {
|
|
53
|
+
lines.push('### Key Points');
|
|
54
|
+
lines.push('');
|
|
55
|
+
for (const kp of keyPoints) {
|
|
56
|
+
lines.push(`- ${kp}`);
|
|
57
|
+
}
|
|
58
|
+
lines.push('');
|
|
59
|
+
}
|
|
60
|
+
// Dissent
|
|
61
|
+
if (c.dissent && c.dissent.length > 0) {
|
|
62
|
+
lines.push('### Dissent');
|
|
63
|
+
lines.push('');
|
|
64
|
+
for (const entry of c.dissent) {
|
|
65
|
+
lines.push(`- **${entry.unit}:** ${entry.reasoning}`);
|
|
66
|
+
}
|
|
67
|
+
lines.push('');
|
|
68
|
+
}
|
|
69
|
+
lines.push(`---`);
|
|
70
|
+
lines.push(`*Exported by MAGI System at ${new Date().toISOString()}*`);
|
|
71
|
+
lines.push('');
|
|
72
|
+
return lines.join('\n');
|
|
73
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ghost text engine — inline dim gray completion suggestions.
|
|
3
|
+
*
|
|
4
|
+
* Renders predicted text after the cursor in dim gray.
|
|
5
|
+
* Tab accepts the suggestion. Any other key updates/clears it.
|
|
6
|
+
* Degrades gracefully: disabled in non-TTY / CI / MAGI_NO_GHOST=1.
|
|
7
|
+
*/
|
|
8
|
+
export interface GhostTextSource {
|
|
9
|
+
getSlashCommands(): string[];
|
|
10
|
+
getHistory(): string[];
|
|
11
|
+
getDeliberationTitles(): string[];
|
|
12
|
+
}
|
|
13
|
+
export interface GhostTextEngine {
|
|
14
|
+
/** Compute suggestion suffix for the current input line. */
|
|
15
|
+
suggest(currentLine: string): string;
|
|
16
|
+
/** Render ghost text at cursor position. */
|
|
17
|
+
render(currentLine: string, prompt: string, suggestion: string, write: (s: string) => void): void;
|
|
18
|
+
/** Clear previously rendered ghost text. */
|
|
19
|
+
clear(write: (s: string) => void): void;
|
|
20
|
+
/** Accept current suggestion — returns accepted text or undefined. */
|
|
21
|
+
accept(): string | undefined;
|
|
22
|
+
/** Check and reset the just-accepted flag (used to suppress Tab completion). */
|
|
23
|
+
consumeAccepted(): boolean;
|
|
24
|
+
/** Release resources. */
|
|
25
|
+
dispose(): void;
|
|
26
|
+
}
|
|
27
|
+
export declare function createGhostTextEngine(source: GhostTextSource, options?: {
|
|
28
|
+
enabled?: boolean;
|
|
29
|
+
}): GhostTextEngine;
|
|
30
|
+
/** Check if ghost text should be disabled based on environment. */
|
|
31
|
+
export declare function isGhostTextDisabled(): boolean;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ghost text engine — inline dim gray completion suggestions.
|
|
3
|
+
*
|
|
4
|
+
* Renders predicted text after the cursor in dim gray.
|
|
5
|
+
* Tab accepts the suggestion. Any other key updates/clears it.
|
|
6
|
+
* Degrades gracefully: disabled in non-TTY / CI / MAGI_NO_GHOST=1.
|
|
7
|
+
*/
|
|
8
|
+
import { SYNC_BEGIN, SYNC_END, RESET } from '../tui/ansi.js';
|
|
9
|
+
import { sanitizeForTerminal } from './terminal-sanitize.js';
|
|
10
|
+
import { visibleWidth } from './display-utils.js';
|
|
11
|
+
const GHOST_STYLE = '\x1b[2m\x1b[90m'; // dim + bright black (gray)
|
|
12
|
+
const ERASE_TO_EOL = '\x1b[K';
|
|
13
|
+
const ERASE_LINE = '\x1b[2K';
|
|
14
|
+
const SAVE_CURSOR = '\x1b7';
|
|
15
|
+
const RESTORE_CURSOR = '\x1b8';
|
|
16
|
+
const CURSOR_DOWN = '\x1b[B';
|
|
17
|
+
const CURSOR_LINE_START = '\r';
|
|
18
|
+
export function createGhostTextEngine(source, options) {
|
|
19
|
+
let lastSuggestion = '';
|
|
20
|
+
let rendered = false;
|
|
21
|
+
let renderedRows = 0;
|
|
22
|
+
let _justAccepted = false;
|
|
23
|
+
const enabled = options?.enabled ?? true;
|
|
24
|
+
function calculateRenderedRows(currentLine, prompt, suggestion) {
|
|
25
|
+
const columns = Math.max(1, process.stdout.columns ?? 80);
|
|
26
|
+
const startColumn = (visibleWidth(prompt) + visibleWidth(currentLine)) % columns;
|
|
27
|
+
const suggestionWidth = visibleWidth(suggestion);
|
|
28
|
+
if (suggestionWidth <= 0)
|
|
29
|
+
return 0;
|
|
30
|
+
return Math.floor((startColumn + suggestionWidth - 1) / columns) + 1;
|
|
31
|
+
}
|
|
32
|
+
function suggest(currentLine) {
|
|
33
|
+
if (!enabled || !currentLine) {
|
|
34
|
+
lastSuggestion = '';
|
|
35
|
+
return '';
|
|
36
|
+
}
|
|
37
|
+
// Priority 1: Slash command prefix completion (safe: hardcoded names)
|
|
38
|
+
if (currentLine.startsWith('/') && !currentLine.includes(' ')) {
|
|
39
|
+
const commands = source.getSlashCommands();
|
|
40
|
+
const match = commands.find(cmd => cmd.startsWith(currentLine) && cmd !== currentLine);
|
|
41
|
+
if (match) {
|
|
42
|
+
lastSuggestion = match.slice(currentLine.length);
|
|
43
|
+
return lastSuggestion;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Priority 2: History prefix match (most recent first)
|
|
47
|
+
// Sanitize: history/titles may contain ANSI escapes from crafted input
|
|
48
|
+
const history = source.getHistory();
|
|
49
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
50
|
+
const entry = history[i];
|
|
51
|
+
if (entry.startsWith(currentLine) && entry !== currentLine) {
|
|
52
|
+
lastSuggestion = sanitizeForTerminal(entry.slice(currentLine.length));
|
|
53
|
+
return lastSuggestion;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Priority 3: Deliberation title prefix match (most recent first)
|
|
57
|
+
const titles = source.getDeliberationTitles();
|
|
58
|
+
for (let i = titles.length - 1; i >= 0; i--) {
|
|
59
|
+
const title = titles[i];
|
|
60
|
+
if (title.startsWith(currentLine) && title !== currentLine) {
|
|
61
|
+
lastSuggestion = sanitizeForTerminal(title.slice(currentLine.length));
|
|
62
|
+
return lastSuggestion;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
lastSuggestion = '';
|
|
66
|
+
return '';
|
|
67
|
+
}
|
|
68
|
+
function render(currentLine, prompt, suggestion, write) {
|
|
69
|
+
if (!suggestion)
|
|
70
|
+
return;
|
|
71
|
+
renderedRows = calculateRenderedRows(currentLine, prompt, suggestion);
|
|
72
|
+
// Save cursor, write ghost text (dim gray), restore cursor
|
|
73
|
+
write(`${SYNC_BEGIN}${SAVE_CURSOR}${GHOST_STYLE}${suggestion}${RESET}${RESTORE_CURSOR}${SYNC_END}`);
|
|
74
|
+
rendered = true;
|
|
75
|
+
}
|
|
76
|
+
function clear(write) {
|
|
77
|
+
if (!rendered)
|
|
78
|
+
return;
|
|
79
|
+
let sequence = `${SAVE_CURSOR}${ERASE_TO_EOL}`;
|
|
80
|
+
for (let i = 1; i < renderedRows; i++) {
|
|
81
|
+
sequence += `${CURSOR_DOWN}${CURSOR_LINE_START}${ERASE_LINE}`;
|
|
82
|
+
}
|
|
83
|
+
write(`${sequence}${RESTORE_CURSOR}`);
|
|
84
|
+
rendered = false;
|
|
85
|
+
renderedRows = 0;
|
|
86
|
+
}
|
|
87
|
+
function accept() {
|
|
88
|
+
if (!lastSuggestion)
|
|
89
|
+
return undefined;
|
|
90
|
+
const accepted = lastSuggestion;
|
|
91
|
+
lastSuggestion = '';
|
|
92
|
+
rendered = false;
|
|
93
|
+
renderedRows = 0;
|
|
94
|
+
_justAccepted = true;
|
|
95
|
+
return accepted;
|
|
96
|
+
}
|
|
97
|
+
function consumeAccepted() {
|
|
98
|
+
if (_justAccepted) {
|
|
99
|
+
_justAccepted = false;
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
function dispose() {
|
|
105
|
+
lastSuggestion = '';
|
|
106
|
+
rendered = false;
|
|
107
|
+
renderedRows = 0;
|
|
108
|
+
_justAccepted = false;
|
|
109
|
+
}
|
|
110
|
+
return { suggest, render, clear, accept, consumeAccepted, dispose };
|
|
111
|
+
}
|
|
112
|
+
/** Check if ghost text should be disabled based on environment. */
|
|
113
|
+
export function isGhostTextDisabled() {
|
|
114
|
+
return (!process.stdin.isTTY ||
|
|
115
|
+
process.env.CI === 'true' ||
|
|
116
|
+
process.env.MAGI_NO_GHOST === '1' ||
|
|
117
|
+
process.env.MAGI_NO_ANIMATION === '1' ||
|
|
118
|
+
process.env.REDUCE_MOTION === '1');
|
|
119
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handoff animation — visual transition from REPL → TUI deliberation.
|
|
3
|
+
*
|
|
4
|
+
* Displays a 4-line NERV-style status readout before TUI takes over.
|
|
5
|
+
* Does NOT use raw mode or alt screen — Ctrl+C safe.
|
|
6
|
+
*/
|
|
7
|
+
export interface HandoffOptions {
|
|
8
|
+
write: (s: string) => void;
|
|
9
|
+
taskTitle: string;
|
|
10
|
+
berserk: boolean;
|
|
11
|
+
unitCount: number;
|
|
12
|
+
delayFn?: (ms: number) => Promise<void>;
|
|
13
|
+
signal?: AbortSignal;
|
|
14
|
+
}
|
|
15
|
+
export declare function runHandoffAnimation(opts: HandoffOptions): Promise<number>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handoff animation — visual transition from REPL → TUI deliberation.
|
|
3
|
+
*
|
|
4
|
+
* Displays a 4-line NERV-style status readout before TUI takes over.
|
|
5
|
+
* Does NOT use raw mode or alt screen — Ctrl+C safe.
|
|
6
|
+
*/
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import { EVA_PALETTE } from '../tui/colors.js';
|
|
9
|
+
import { truncateToDisplayWidth } from '../tui/screen-buffer.js';
|
|
10
|
+
import { shouldSkipAnimation } from './accessibility.js';
|
|
11
|
+
import { sanitizeForTerminal } from './terminal-sanitize.js';
|
|
12
|
+
const { scanline, frame, warning } = EVA_PALETTE;
|
|
13
|
+
const GREEN = chalk.rgb(scanline.r, scanline.g, scanline.b);
|
|
14
|
+
const ORANGE = chalk.rgb(frame.r, frame.g, frame.b);
|
|
15
|
+
const WARN = chalk.rgb(warning.r, warning.g, warning.b);
|
|
16
|
+
function defaultDelay(ms) {
|
|
17
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
18
|
+
}
|
|
19
|
+
function normalizeInlineTitle(text) {
|
|
20
|
+
return sanitizeForTerminal(text)
|
|
21
|
+
.replace(/\s+/g, ' ')
|
|
22
|
+
.trim();
|
|
23
|
+
}
|
|
24
|
+
function unitNames(count) {
|
|
25
|
+
const names = ['MELCHIOR', 'BALTHASAR', 'CASPER'];
|
|
26
|
+
if (count <= 3)
|
|
27
|
+
return names.slice(0, count).join('/');
|
|
28
|
+
return `${names.join('/')}+${count - 3}`;
|
|
29
|
+
}
|
|
30
|
+
export async function runHandoffAnimation(opts) {
|
|
31
|
+
if (shouldSkipAnimation())
|
|
32
|
+
return 0;
|
|
33
|
+
const { write, taskTitle, berserk, unitCount, signal } = opts;
|
|
34
|
+
const delay = opts.delayFn ?? defaultDelay;
|
|
35
|
+
// Sanitize and truncate title to terminal width (leave room for prefix)
|
|
36
|
+
const maxTitleLen = Math.min((process.stdout.columns ?? 80) - 30, 50);
|
|
37
|
+
const title = truncateToDisplayWidth(normalizeInlineTitle(taskTitle), maxTitleLen);
|
|
38
|
+
const lines = [
|
|
39
|
+
GREEN(` [TASK ] deliberation :: ${title}`),
|
|
40
|
+
GREEN(` [ROUTE] MAGI core link ................. LOCKED`),
|
|
41
|
+
GREEN(` [SYNC ] ${unitNames(unitCount)} ...... 100%`),
|
|
42
|
+
ORANGE(` [TUI ] Switching to tactical display...`),
|
|
43
|
+
];
|
|
44
|
+
let lineCount = 0;
|
|
45
|
+
write('\n');
|
|
46
|
+
lineCount++;
|
|
47
|
+
for (const line of lines) {
|
|
48
|
+
if (signal?.aborted)
|
|
49
|
+
return lineCount;
|
|
50
|
+
write(line + '\n');
|
|
51
|
+
lineCount++;
|
|
52
|
+
await delay(70);
|
|
53
|
+
}
|
|
54
|
+
if (berserk) {
|
|
55
|
+
if (signal?.aborted)
|
|
56
|
+
return lineCount;
|
|
57
|
+
await delay(60);
|
|
58
|
+
const bar = WARN.bgRed.bold(' ⚠ BERSERK MODE — ALL LIMITERS RELEASED ⚠ ');
|
|
59
|
+
write(bar + '\n');
|
|
60
|
+
lineCount++;
|
|
61
|
+
}
|
|
62
|
+
write('\n');
|
|
63
|
+
lineCount++;
|
|
64
|
+
return lineCount;
|
|
65
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REPL history persistence — ~/.magi/repl_history
|
|
3
|
+
*
|
|
4
|
+
* JSON format with atomic writes (tmp + rename), debounced saves,
|
|
5
|
+
* corrupt file recovery, and sensitive input filtering.
|
|
6
|
+
* No file locking: last-write-wins under concurrent sessions.
|
|
7
|
+
*/
|
|
8
|
+
export interface ReplHistory {
|
|
9
|
+
load(): Promise<string[]>;
|
|
10
|
+
addEntry(line: string): void;
|
|
11
|
+
getEntries(): string[];
|
|
12
|
+
flush(): Promise<void>;
|
|
13
|
+
dispose(): void;
|
|
14
|
+
}
|
|
15
|
+
export declare function looksSensitive(line: string): boolean;
|
|
16
|
+
export declare function createReplHistory(historyDir?: string): ReplHistory;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REPL history persistence — ~/.magi/repl_history
|
|
3
|
+
*
|
|
4
|
+
* JSON format with atomic writes (tmp + rename), debounced saves,
|
|
5
|
+
* corrupt file recovery, and sensitive input filtering.
|
|
6
|
+
* No file locking: last-write-wins under concurrent sessions.
|
|
7
|
+
*/
|
|
8
|
+
import { readFile, rename } from 'node:fs/promises';
|
|
9
|
+
import { randomBytes } from 'node:crypto';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { homedir } from 'node:os';
|
|
12
|
+
import { safeMkdir, safeWriteFile } from '../utils/safe-fs.js';
|
|
13
|
+
import { logger } from '../utils/logger.js';
|
|
14
|
+
import { safeJsonParse } from '../utils/safe-json-parse.js';
|
|
15
|
+
const MAX_ENTRIES = 1000;
|
|
16
|
+
const MAX_FILE_BYTES = 1_048_576; // 1 MiB
|
|
17
|
+
const DEBOUNCE_MS = 2_000;
|
|
18
|
+
const SENSITIVE_PATTERN = /password|secret|token|api[_-]?key|private[_ ]key|credential/i;
|
|
19
|
+
export function looksSensitive(line) {
|
|
20
|
+
return SENSITIVE_PATTERN.test(line);
|
|
21
|
+
}
|
|
22
|
+
export function createReplHistory(historyDir) {
|
|
23
|
+
const dir = historyDir ?? join(homedir(), '.magi');
|
|
24
|
+
const filePath = join(dir, 'repl_history');
|
|
25
|
+
let entries = [];
|
|
26
|
+
let dirty = false;
|
|
27
|
+
let debounceTimer;
|
|
28
|
+
let savePromise;
|
|
29
|
+
async function load() {
|
|
30
|
+
try {
|
|
31
|
+
const raw = await readFile(filePath, 'utf-8');
|
|
32
|
+
if (!raw.trim())
|
|
33
|
+
return (entries = []);
|
|
34
|
+
const data = safeJsonParse(raw);
|
|
35
|
+
if (data.version !== 1 || !Array.isArray(data.entries)) {
|
|
36
|
+
throw new Error('Invalid history format');
|
|
37
|
+
}
|
|
38
|
+
entries = data.entries.slice(-MAX_ENTRIES);
|
|
39
|
+
return [...entries];
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
const code = error.code;
|
|
43
|
+
if (code === 'ENOENT')
|
|
44
|
+
return (entries = []);
|
|
45
|
+
// Corrupt file — rename and start fresh
|
|
46
|
+
const ts = new Date().toISOString().replace(/[:.]/g, '').slice(0, 15);
|
|
47
|
+
try {
|
|
48
|
+
await rename(filePath, `${filePath}.corrupt-${ts}`);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
logger.debug('history: failed to rename corrupt file');
|
|
52
|
+
}
|
|
53
|
+
entries = [];
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function addEntry(line) {
|
|
58
|
+
const trimmed = line.trim();
|
|
59
|
+
if (!trimmed)
|
|
60
|
+
return;
|
|
61
|
+
if (looksSensitive(trimmed))
|
|
62
|
+
return;
|
|
63
|
+
// Skip consecutive duplicate
|
|
64
|
+
if (entries.length > 0 && entries[entries.length - 1] === trimmed)
|
|
65
|
+
return;
|
|
66
|
+
entries.push(trimmed);
|
|
67
|
+
if (entries.length > MAX_ENTRIES) {
|
|
68
|
+
entries = entries.slice(-MAX_ENTRIES);
|
|
69
|
+
}
|
|
70
|
+
dirty = true;
|
|
71
|
+
scheduleSave();
|
|
72
|
+
}
|
|
73
|
+
function scheduleSave() {
|
|
74
|
+
if (debounceTimer !== undefined)
|
|
75
|
+
clearTimeout(debounceTimer);
|
|
76
|
+
debounceTimer = setTimeout(() => { save().catch(() => { }); }, DEBOUNCE_MS);
|
|
77
|
+
}
|
|
78
|
+
async function save() {
|
|
79
|
+
if (!dirty || savePromise)
|
|
80
|
+
return;
|
|
81
|
+
// Snapshot dirty state before async I/O — if addEntry() sets dirty=true
|
|
82
|
+
// during our save, we must NOT clear it afterwards.
|
|
83
|
+
dirty = false;
|
|
84
|
+
const doSave = async () => {
|
|
85
|
+
try {
|
|
86
|
+
const data = { version: 1, entries };
|
|
87
|
+
let json = JSON.stringify(data, null, 2);
|
|
88
|
+
// Enforce 1 MiB limit — trim oldest entries until it fits
|
|
89
|
+
while (Buffer.byteLength(json, 'utf-8') > MAX_FILE_BYTES && entries.length > 0) {
|
|
90
|
+
entries.shift();
|
|
91
|
+
json = JSON.stringify({ version: 1, entries }, null, 2);
|
|
92
|
+
}
|
|
93
|
+
await safeMkdir(dir);
|
|
94
|
+
// Unique tmp name to avoid collision with concurrent sessions
|
|
95
|
+
const tmpPath = `${filePath}.tmp-${randomBytes(4).toString('hex')}`;
|
|
96
|
+
await safeWriteFile(tmpPath, json);
|
|
97
|
+
await rename(tmpPath, filePath);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// Save failed — mark dirty again so next attempt retries
|
|
101
|
+
dirty = true;
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
savePromise = undefined;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
savePromise = doSave();
|
|
108
|
+
await savePromise;
|
|
109
|
+
}
|
|
110
|
+
async function flush() {
|
|
111
|
+
if (debounceTimer !== undefined) {
|
|
112
|
+
clearTimeout(debounceTimer);
|
|
113
|
+
debounceTimer = undefined;
|
|
114
|
+
}
|
|
115
|
+
// Wait for in-flight save to complete, then re-save if dirty
|
|
116
|
+
if (savePromise)
|
|
117
|
+
await savePromise;
|
|
118
|
+
await save();
|
|
119
|
+
}
|
|
120
|
+
function dispose() {
|
|
121
|
+
if (debounceTimer !== undefined) {
|
|
122
|
+
clearTimeout(debounceTimer);
|
|
123
|
+
debounceTimer = undefined;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function getEntries() {
|
|
127
|
+
return [...entries];
|
|
128
|
+
}
|
|
129
|
+
return { load, addEntry, getEntries, flush, dispose };
|
|
130
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Background job registry for MAGI REPL.
|
|
3
|
+
* Manages long-running tasks (e.g., angel watch daemon).
|
|
4
|
+
*/
|
|
5
|
+
export interface BackgroundJob {
|
|
6
|
+
id: number;
|
|
7
|
+
name: string;
|
|
8
|
+
status: 'running' | 'done' | 'failed';
|
|
9
|
+
abort: AbortController;
|
|
10
|
+
startedAt: number;
|
|
11
|
+
error?: string;
|
|
12
|
+
}
|
|
13
|
+
interface AddJobOptions {
|
|
14
|
+
onSettled?: (job: Readonly<BackgroundJob>) => void;
|
|
15
|
+
}
|
|
16
|
+
export declare class JobRegistry {
|
|
17
|
+
private jobs;
|
|
18
|
+
private nextId;
|
|
19
|
+
add(name: string, run: (signal: AbortSignal) => Promise<void>, options?: AddJobOptions): BackgroundJob;
|
|
20
|
+
cancel(id: number): boolean;
|
|
21
|
+
list(): readonly BackgroundJob[];
|
|
22
|
+
get(id: number): Readonly<BackgroundJob> | undefined;
|
|
23
|
+
getRunningCount(): number;
|
|
24
|
+
dispose(): void;
|
|
25
|
+
}
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Background job registry for MAGI REPL.
|
|
3
|
+
* Manages long-running tasks (e.g., angel watch daemon).
|
|
4
|
+
*/
|
|
5
|
+
const MAX_CONCURRENT_JOBS = 10;
|
|
6
|
+
export class JobRegistry {
|
|
7
|
+
jobs = new Map();
|
|
8
|
+
nextId = 1;
|
|
9
|
+
add(name, run, options) {
|
|
10
|
+
if (this.getRunningCount() >= MAX_CONCURRENT_JOBS) {
|
|
11
|
+
throw new Error(`Maximum concurrent jobs (${MAX_CONCURRENT_JOBS}) reached. Cancel a job first.`);
|
|
12
|
+
}
|
|
13
|
+
const abort = new AbortController();
|
|
14
|
+
const job = {
|
|
15
|
+
id: this.nextId++,
|
|
16
|
+
name,
|
|
17
|
+
status: 'running',
|
|
18
|
+
abort,
|
|
19
|
+
startedAt: Date.now(),
|
|
20
|
+
};
|
|
21
|
+
this.jobs.set(job.id, job);
|
|
22
|
+
const notifySettled = () => {
|
|
23
|
+
options?.onSettled?.(job);
|
|
24
|
+
};
|
|
25
|
+
run(abort.signal)
|
|
26
|
+
.then(() => {
|
|
27
|
+
// Guard: cancel() may have already set status to 'done'
|
|
28
|
+
if (job.status === 'running')
|
|
29
|
+
job.status = 'done';
|
|
30
|
+
notifySettled();
|
|
31
|
+
})
|
|
32
|
+
.catch((err) => {
|
|
33
|
+
// Guard: cancel() may have already set status to 'done'
|
|
34
|
+
if (job.status !== 'running') {
|
|
35
|
+
notifySettled();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (abort.signal.aborted) {
|
|
39
|
+
job.status = 'done';
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
job.status = 'failed';
|
|
43
|
+
job.error = String(err);
|
|
44
|
+
}
|
|
45
|
+
notifySettled();
|
|
46
|
+
});
|
|
47
|
+
return job;
|
|
48
|
+
}
|
|
49
|
+
cancel(id) {
|
|
50
|
+
const job = this.jobs.get(id);
|
|
51
|
+
if (!job || job.status !== 'running')
|
|
52
|
+
return false;
|
|
53
|
+
job.status = 'done';
|
|
54
|
+
job.abort.abort();
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
list() {
|
|
58
|
+
return [...this.jobs.values()];
|
|
59
|
+
}
|
|
60
|
+
get(id) {
|
|
61
|
+
return this.jobs.get(id);
|
|
62
|
+
}
|
|
63
|
+
getRunningCount() {
|
|
64
|
+
let count = 0;
|
|
65
|
+
for (const j of this.jobs.values()) {
|
|
66
|
+
if (j.status === 'running')
|
|
67
|
+
count++;
|
|
68
|
+
}
|
|
69
|
+
return count;
|
|
70
|
+
}
|
|
71
|
+
dispose() {
|
|
72
|
+
for (const job of this.jobs.values()) {
|
|
73
|
+
if (job.status === 'running') {
|
|
74
|
+
job.abort.abort();
|
|
75
|
+
job.status = 'done';
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
this.jobs.clear();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MagiRepl — Interactive REPL for the MAGI System.
|
|
3
|
+
*
|
|
4
|
+
* Provides a Claude Code-like experience: type questions naturally,
|
|
5
|
+
* context persists across turns, and the full-screen Evangelion TUI
|
|
6
|
+
* takes over during deliberation.
|
|
7
|
+
*
|
|
8
|
+
* Key design decision (Gemini review):
|
|
9
|
+
* readline is **close → recreate** per deliberation rather than
|
|
10
|
+
* pause/resume, to avoid stdin state conflicts with the TUI's raw mode.
|
|
11
|
+
*/
|
|
12
|
+
import { type ReplState } from './repl-state.js';
|
|
13
|
+
import type { MagiConfig } from '../types/config.js';
|
|
14
|
+
export interface MagiReplOptions {
|
|
15
|
+
/** Magi engine configuration (passed through to Magi constructor) */
|
|
16
|
+
magiConfig?: Partial<MagiConfig>;
|
|
17
|
+
/** Skip boot animation (for testing / CI) */
|
|
18
|
+
skipBootAnimation?: boolean;
|
|
19
|
+
/** Initial sound state */
|
|
20
|
+
soundEnabled?: boolean;
|
|
21
|
+
/** TUI enabled (from user config; default true) */
|
|
22
|
+
tuiEnabled?: boolean;
|
|
23
|
+
/** History directory override (for testing) */
|
|
24
|
+
historyDir?: string;
|
|
25
|
+
}
|
|
26
|
+
export declare class MagiRepl {
|
|
27
|
+
private magi;
|
|
28
|
+
private context;
|
|
29
|
+
private slashCommands;
|
|
30
|
+
private soundEnabled;
|
|
31
|
+
private state;
|
|
32
|
+
private abortController?;
|
|
33
|
+
private sessionState;
|
|
34
|
+
private history;
|
|
35
|
+
private completerFn;
|
|
36
|
+
private shownEngramIds;
|
|
37
|
+
private stats;
|
|
38
|
+
private multilineCollector;
|
|
39
|
+
private lastSigintTime;
|
|
40
|
+
private lastDeliberation?;
|
|
41
|
+
private readonly options;
|
|
42
|
+
private ghostEngine;
|
|
43
|
+
private jobRegistry;
|
|
44
|
+
private currentRl?;
|
|
45
|
+
private watchJobId?;
|
|
46
|
+
private watchPollIntervalMs?;
|
|
47
|
+
constructor(options?: MagiReplOptions);
|
|
48
|
+
get currentState(): ReplState;
|
|
49
|
+
private get running();
|
|
50
|
+
private transition;
|
|
51
|
+
start(): Promise<void>;
|
|
52
|
+
private readInputLoop;
|
|
53
|
+
private createReadline;
|
|
54
|
+
private handleInput;
|
|
55
|
+
private handleSlashCommand;
|
|
56
|
+
private handleConfirmation;
|
|
57
|
+
private drainPendingInput;
|
|
58
|
+
private createConfirmationToken;
|
|
59
|
+
private handleWatchCommand;
|
|
60
|
+
private handleJobsCommand;
|
|
61
|
+
private writeAbovePrompt;
|
|
62
|
+
private injectEngramContext;
|
|
63
|
+
private runDeliberation;
|
|
64
|
+
private checkUnitsOnStartup;
|
|
65
|
+
/**
|
|
66
|
+
* Graceful resource cleanup — idempotent, callable from any state.
|
|
67
|
+
* Use for programmatic shutdown without process.exit().
|
|
68
|
+
*/
|
|
69
|
+
dispose(): Promise<void>;
|
|
70
|
+
private exportDeliberation;
|
|
71
|
+
private clearRecentLines;
|
|
72
|
+
}
|