aura-code 0.6.3 → 0.10.1
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/README.md +47 -164
- package/dist/agent/affect.d.ts +13 -0
- package/dist/agent/affect.js +34 -0
- package/dist/agent/affect.js.map +1 -0
- package/dist/agent/compactor.d.ts +27 -4
- package/dist/agent/compactor.js +185 -54
- package/dist/agent/compactor.js.map +1 -1
- package/dist/agent/confess.d.ts +35 -0
- package/dist/agent/confess.js +296 -0
- package/dist/agent/confess.js.map +1 -0
- package/dist/agent/context.d.ts +0 -4
- package/dist/agent/context.js +0 -38
- package/dist/agent/context.js.map +1 -1
- package/dist/agent/domain-expertise.d.ts +22 -0
- package/dist/agent/domain-expertise.js +110 -0
- package/dist/agent/domain-expertise.js.map +1 -0
- package/dist/agent/executive-queue.d.ts +26 -0
- package/dist/agent/executive-queue.js +50 -0
- package/dist/agent/executive-queue.js.map +1 -0
- package/dist/agent/generational-flush.d.ts +28 -0
- package/dist/agent/generational-flush.js +48 -0
- package/dist/agent/generational-flush.js.map +1 -0
- package/dist/agent/loop-profile.d.ts +50 -0
- package/dist/agent/loop-profile.js +88 -0
- package/dist/agent/loop-profile.js.map +1 -0
- package/dist/agent/loop.d.ts +14 -5
- package/dist/agent/loop.js +167 -39
- package/dist/agent/loop.js.map +1 -1
- package/dist/agent/memory-consolidate.d.ts +14 -0
- package/dist/agent/memory-consolidate.js +141 -0
- package/dist/agent/memory-consolidate.js.map +1 -0
- package/dist/agent/mixture.d.ts +33 -0
- package/dist/agent/mixture.js +138 -0
- package/dist/agent/mixture.js.map +1 -0
- package/dist/agent/spawner.js +2 -2
- package/dist/agent/spawner.js.map +1 -1
- package/dist/agent/system-prompt.d.ts +1 -1
- package/dist/agent/system-prompt.js +50 -22
- package/dist/agent/system-prompt.js.map +1 -1
- package/dist/agent/unified-memory.d.ts +15 -0
- package/dist/agent/unified-memory.js +164 -0
- package/dist/agent/unified-memory.js.map +1 -0
- package/dist/checkpoints/engine.d.ts +44 -0
- package/dist/checkpoints/engine.js +324 -0
- package/dist/checkpoints/engine.js.map +1 -0
- package/dist/cli/command-palette.d.ts +26 -0
- package/dist/cli/command-palette.js +133 -0
- package/dist/cli/command-palette.js.map +1 -0
- package/dist/cli/context-health.d.ts +51 -0
- package/dist/cli/context-health.js +187 -0
- package/dist/cli/context-health.js.map +1 -0
- package/dist/cli/diamond.d.ts +67 -0
- package/dist/cli/diamond.js +222 -12
- package/dist/cli/diamond.js.map +1 -1
- package/dist/cli/dic.d.ts +2 -0
- package/dist/cli/dic.js +106 -0
- package/dist/cli/dic.js.map +1 -0
- package/dist/cli/diff-view.d.ts +18 -0
- package/dist/cli/diff-view.js +114 -0
- package/dist/cli/diff-view.js.map +1 -0
- package/dist/cli/display.d.ts +18 -4
- package/dist/cli/display.js +74 -151
- package/dist/cli/display.js.map +1 -1
- package/dist/cli/help-data.d.ts +7 -0
- package/dist/cli/help-data.js +80 -0
- package/dist/cli/help-data.js.map +1 -0
- package/dist/cli/index.js +1002 -852
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/markdown.d.ts +5 -0
- package/dist/cli/markdown.js +128 -0
- package/dist/cli/markdown.js.map +1 -0
- package/dist/cli/model-select.d.ts +39 -0
- package/dist/cli/model-select.js +65 -0
- package/dist/cli/model-select.js.map +1 -0
- package/dist/cli/tui.d.ts +34 -0
- package/dist/cli/tui.js +1176 -0
- package/dist/cli/tui.js.map +1 -0
- package/dist/config/defaults.d.ts +6 -17
- package/dist/config/defaults.js +25 -46
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/project-config.d.ts +2 -6
- package/dist/config/project-config.js +2 -2
- package/dist/config/project-config.js.map +1 -1
- package/dist/doctor/checks.d.ts +18 -0
- package/dist/doctor/checks.js +524 -0
- package/dist/doctor/checks.js.map +1 -0
- package/dist/doctor/index.d.ts +4 -0
- package/dist/doctor/index.js +229 -0
- package/dist/doctor/index.js.map +1 -0
- package/dist/doctor/repair.d.ts +19 -0
- package/dist/doctor/repair.js +126 -0
- package/dist/doctor/repair.js.map +1 -0
- package/dist/doctor/types.d.ts +38 -0
- package/dist/doctor/types.js +10 -0
- package/dist/doctor/types.js.map +1 -0
- package/dist/dream/dream.d.ts +79 -59
- package/dist/dream/dream.js +324 -144
- package/dist/dream/dream.js.map +1 -1
- package/dist/dream/episode.d.ts +17 -0
- package/dist/dream/episode.js +116 -0
- package/dist/dream/episode.js.map +1 -0
- package/dist/kanban/engine.d.ts +26 -0
- package/dist/kanban/engine.js +208 -0
- package/dist/kanban/engine.js.map +1 -0
- package/dist/kanban/types.d.ts +19 -0
- package/dist/kanban/types.js +9 -1
- package/dist/kanban/types.js.map +1 -1
- package/dist/machina/render-html.js +4 -2
- package/dist/machina/render-html.js.map +1 -1
- package/dist/machina/render-terminal.js +11 -4
- package/dist/machina/render-terminal.js.map +1 -1
- package/dist/machina/repair.d.ts +23 -0
- package/dist/machina/repair.js +98 -0
- package/dist/machina/repair.js.map +1 -0
- package/dist/machina/spec.js +12 -12
- package/dist/machina/spec.js.map +1 -1
- package/dist/machina/verify.d.ts +13 -1
- package/dist/machina/verify.js +12 -2
- package/dist/machina/verify.js.map +1 -1
- package/dist/mining/extract.d.ts +34 -0
- package/dist/mining/extract.js +229 -0
- package/dist/mining/extract.js.map +1 -0
- package/dist/mining/refine.d.ts +28 -0
- package/dist/mining/refine.js +256 -0
- package/dist/mining/refine.js.map +1 -0
- package/dist/perception/extractor.js +5 -3
- package/dist/perception/extractor.js.map +1 -1
- package/dist/perception/graph-store.d.ts +0 -11
- package/dist/perception/graph-store.js +0 -33
- package/dist/perception/graph-store.js.map +1 -1
- package/dist/perception/index.d.ts +1 -1
- package/dist/perception/index.js +1 -2
- package/dist/perception/index.js.map +1 -1
- package/dist/plugins/commands.d.ts +38 -0
- package/dist/plugins/commands.js +134 -0
- package/dist/plugins/commands.js.map +1 -0
- package/dist/plugins/frontmatter.d.ts +14 -0
- package/dist/plugins/frontmatter.js +78 -0
- package/dist/plugins/frontmatter.js.map +1 -0
- package/dist/plugins/hooks.d.ts +12 -0
- package/dist/plugins/hooks.js +148 -0
- package/dist/plugins/hooks.js.map +1 -0
- package/dist/plugins/loader.d.ts +7 -0
- package/dist/plugins/loader.js +255 -0
- package/dist/plugins/loader.js.map +1 -0
- package/dist/plugins/market.d.ts +23 -0
- package/dist/plugins/market.js +281 -0
- package/dist/plugins/market.js.map +1 -0
- package/dist/plugins/types.d.ts +90 -0
- package/dist/plugins/types.js +20 -0
- package/dist/plugins/types.js.map +1 -0
- package/dist/providers/anthropic-oauth-draft.d.ts +119 -0
- package/dist/providers/anthropic-oauth-draft.js +414 -0
- package/dist/providers/anthropic-oauth-draft.js.map +1 -0
- package/dist/providers/anthropic.d.ts +4 -1
- package/dist/providers/anthropic.js +35 -7
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/factory.d.ts +43 -19
- package/dist/providers/factory.js +183 -129
- package/dist/providers/factory.js.map +1 -1
- package/dist/providers/google.js +9 -22
- package/dist/providers/google.js.map +1 -1
- package/dist/providers/live-models.d.ts +23 -0
- package/dist/providers/live-models.js +145 -0
- package/dist/providers/live-models.js.map +1 -0
- package/dist/providers/openai-compatible.d.ts +1 -12
- package/dist/providers/openai-compatible.js +71 -108
- package/dist/providers/openai-compatible.js.map +1 -1
- package/dist/providers/resilient-factory.js +1 -1
- package/dist/providers/resilient-factory.js.map +1 -1
- package/dist/repl/queue.d.ts +22 -0
- package/dist/repl/queue.js +165 -0
- package/dist/repl/queue.js.map +1 -0
- package/dist/repl/side-channel.d.ts +23 -0
- package/dist/repl/side-channel.js +63 -0
- package/dist/repl/side-channel.js.map +1 -0
- package/dist/research/council.d.ts +2 -0
- package/dist/research/council.js +29 -4
- package/dist/research/council.js.map +1 -1
- package/dist/ruby/alternator.d.ts +11 -30
- package/dist/ruby/alternator.js +59 -38
- package/dist/ruby/alternator.js.map +1 -1
- package/dist/ruby/episode-capture.d.ts +0 -10
- package/dist/ruby/episode-capture.js +0 -33
- package/dist/ruby/episode-capture.js.map +1 -1
- package/dist/ruby/types.js +1 -1
- package/dist/ruby/types.js.map +1 -1
- package/dist/safety/path-jail.d.ts +17 -0
- package/dist/safety/path-jail.js +100 -0
- package/dist/safety/path-jail.js.map +1 -0
- package/dist/safety/permissions.d.ts +12 -21
- package/dist/safety/permissions.js +81 -216
- package/dist/safety/permissions.js.map +1 -1
- package/dist/safety/ssrf.d.ts +29 -0
- package/dist/safety/ssrf.js +185 -0
- package/dist/safety/ssrf.js.map +1 -0
- package/dist/server/index.js +64 -10
- package/dist/server/index.js.map +1 -1
- package/dist/setup/first-run.d.ts +0 -24
- package/dist/setup/first-run.js +10 -344
- package/dist/setup/first-run.js.map +1 -1
- package/dist/setup/key-store.d.ts +12 -0
- package/dist/setup/key-store.js +108 -0
- package/dist/setup/key-store.js.map +1 -0
- package/dist/setup/provider-registry.js +31 -18
- package/dist/setup/provider-registry.js.map +1 -1
- package/dist/setup/provider-test.d.ts +7 -0
- package/dist/setup/provider-test.js +73 -10
- package/dist/setup/provider-test.js.map +1 -1
- package/dist/setup/provider-wizard.d.ts +4 -1
- package/dist/setup/provider-wizard.js +92 -26
- package/dist/setup/provider-wizard.js.map +1 -1
- package/dist/setup/xiaomi.d.ts +1 -1
- package/dist/setup/xiaomi.js +3 -3
- package/dist/setup/xiaomi.js.map +1 -1
- package/dist/tools/browser.js +14 -10
- package/dist/tools/browser.js.map +1 -1
- package/dist/tools/clipboard.js +7 -1
- package/dist/tools/clipboard.js.map +1 -1
- package/dist/tools/dictate.d.ts +60 -0
- package/dist/tools/dictate.js +983 -0
- package/dist/tools/dictate.js.map +1 -0
- package/dist/tools/dictate_patched.js +1057 -0
- package/dist/tools/edit-file.js +10 -2
- package/dist/tools/edit-file.js.map +1 -1
- package/dist/tools/http-request.js +14 -4
- package/dist/tools/http-request.js.map +1 -1
- package/dist/tools/image-read.js +34 -2
- package/dist/tools/image-read.js.map +1 -1
- package/dist/tools/index.d.ts +16 -1
- package/dist/tools/index.js +59 -8
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/mcp.js +12 -0
- package/dist/tools/mcp.js.map +1 -1
- package/dist/tools/read-file.js +10 -14
- package/dist/tools/read-file.js.map +1 -1
- package/dist/tools/telegram-audio-policy.d.ts +30 -0
- package/dist/tools/telegram-audio-policy.js +49 -0
- package/dist/tools/telegram-audio-policy.js.map +1 -0
- package/dist/tools/telegram-bot.js +1136 -537
- package/dist/tools/telegram-bot.js.map +1 -1
- package/dist/tools/telegram-voice.d.ts +18 -5
- package/dist/tools/telegram-voice.js +54 -12
- package/dist/tools/telegram-voice.js.map +1 -1
- package/dist/tools/telegram.d.ts +3 -3
- package/dist/tools/telegram.js +5 -84
- package/dist/tools/telegram.js.map +1 -1
- package/dist/tools/tools.d.ts +2 -14
- package/dist/tools/tools.js +79 -144
- package/dist/tools/tools.js.map +1 -1
- package/dist/tools/web-fetch.js +39 -7
- package/dist/tools/web-fetch.js.map +1 -1
- package/dist/tools/web-search.d.ts +0 -20
- package/dist/tools/web-search.js +40 -137
- package/dist/tools/web-search.js.map +1 -1
- package/dist/util/errors.js +1 -15
- package/dist/util/errors.js.map +1 -1
- package/dist/util/json-repair.d.ts +11 -0
- package/dist/util/json-repair.js +31 -0
- package/dist/util/json-repair.js.map +1 -0
- package/dist/util/rate-limiter.js +2 -8
- package/dist/util/rate-limiter.js.map +1 -1
- package/dist/viz/index.js +793 -792
- package/dist/viz/index.js.map +1 -1
- package/package.json +12 -32
package/dist/cli/index.js
CHANGED
|
@@ -43,17 +43,25 @@ const fs = __importStar(require("fs"));
|
|
|
43
43
|
const minimist_1 = __importDefault(require("minimist"));
|
|
44
44
|
const chalk_1 = __importDefault(require("chalk"));
|
|
45
45
|
const factory_js_1 = require("../providers/factory.js");
|
|
46
|
+
const live_models_js_1 = require("../providers/live-models.js");
|
|
47
|
+
void (0, live_models_js_1.refreshLiveModels)().catch(() => { }); // fire-and-forget at module load — see comment history for why this isn't awaited
|
|
48
|
+
process.on('unhandledRejection', (reason) => {
|
|
49
|
+
console.error(chalk_1.default.hex('#b15439')(' \u2717 Unhandled rejection: ' + String(reason)));
|
|
50
|
+
});
|
|
46
51
|
const resilient_factory_js_1 = require("../providers/resilient-factory.js");
|
|
47
52
|
const context_js_1 = require("../agent/context.js");
|
|
48
53
|
const index_js_1 = require("../viz/index.js");
|
|
49
54
|
const loop_js_1 = require("../agent/loop.js");
|
|
50
55
|
const permissions_js_1 = require("../safety/permissions.js");
|
|
51
56
|
const display_js_1 = require("./display.js");
|
|
57
|
+
const tui_js_1 = require("./tui.js");
|
|
52
58
|
const index_js_2 = require("../server/index.js");
|
|
53
59
|
const project_config_js_1 = require("../config/project-config.js");
|
|
60
|
+
const package_json_1 = __importDefault(require("../../package.json"));
|
|
54
61
|
const defaults_js_1 = require("../config/defaults.js");
|
|
55
62
|
const session_store_js_1 = require("../agent/session-store.js");
|
|
56
63
|
const global_config_js_1 = require("../setup/global-config.js");
|
|
64
|
+
const key_store_js_1 = require("../setup/key-store.js");
|
|
57
65
|
const first_run_js_1 = require("../setup/first-run.js");
|
|
58
66
|
const provider_wizard_js_1 = require("../setup/provider-wizard.js");
|
|
59
67
|
const index_js_3 = require("../orchestration/index.js");
|
|
@@ -63,21 +71,16 @@ const proposer_js_1 = require("../harness/proposer.js");
|
|
|
63
71
|
const engine_js_1 = require("../workflows/engine.js");
|
|
64
72
|
const engine_js_2 = require("../architect/engine.js");
|
|
65
73
|
const diamond_js_1 = require("./diamond.js");
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
const types_js_1 = require("../ruby/types.js");
|
|
71
|
-
const load_env_js_1 = require("../util/load-env.js");
|
|
72
|
-
const factory_js_2 = require("../providers/factory.js");
|
|
73
|
-
// Load API keys from ~/.hermes/.env, ~/.config/aura-code/.env, and project .env
|
|
74
|
-
(0, load_env_js_1.bootstrapAuraEnv)(process.cwd());
|
|
74
|
+
const model_select_js_1 = require("./model-select.js");
|
|
75
|
+
const context_health_js_1 = require("./context-health.js");
|
|
76
|
+
const index_js_5 = require("../doctor/index.js");
|
|
77
|
+
const help_data_js_1 = require("./help-data.js");
|
|
75
78
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
76
79
|
// Parse args
|
|
77
80
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
78
81
|
const argv = (0, minimist_1.default)(process.argv.slice(2), {
|
|
79
82
|
string: ['model', 'm', 'api-key', 'base-url', 'mode', 'cwd', 'rate-limit-rpm', 'rate-limit-tpm', 'max-retries', 'max-verify-retries', 'max-turns', 'fallback', 'resume', 'chat-id', 'profile', 'test-command', 'workflow', 'resume-workflow', 'workflow-name', 'apply-harness', 'blueprint', 'build'],
|
|
80
|
-
boolean: ['help', 'h', 'version', 'v', 'auto', 'readonly', 'models', 'no-session', 'no-setup', 'reset-setup', '
|
|
83
|
+
boolean: ['help', 'h', 'version', 'v', 'auto', 'readonly', 'models', 'no-session', 'no-setup', 'reset-setup', 'orchestrate', 'plan', 'architect', 'list-sessions', 'new-session', 'verify', 'analyze', 'workflows', 'propose-harness', 'blueprints', 'moa', 'doctor'],
|
|
81
84
|
alias: { m: 'model', h: 'help', v: 'version' },
|
|
82
85
|
default: {
|
|
83
86
|
model: process.env.AURA_MODEL,
|
|
@@ -94,6 +97,9 @@ const cliMaxRetries = num(argv['max-retries']) ?? num(process.env.AURA_MAX_RETRI
|
|
|
94
97
|
const cliMaxVerifyRetries = num(argv['max-verify-retries']);
|
|
95
98
|
const cliMaxTurns = num(argv['max-turns']);
|
|
96
99
|
const cliVerify = argv.verify === true;
|
|
100
|
+
// Voice output: speak task summaries aloud. Enabled by --speak or AURA_SPEAK=1;
|
|
101
|
+
// toggled at runtime in the REPL with :speak. Mutable so :speak can flip it.
|
|
102
|
+
let speakEnabled = argv.speak === true || process.env.AURA_SPEAK === '1';
|
|
97
103
|
const cliProfile = typeof argv.profile === 'string' ? argv.profile : undefined;
|
|
98
104
|
const cliTestCommand = typeof argv['test-command'] === 'string' ? argv['test-command'] : undefined;
|
|
99
105
|
const cliRpm = num(argv['rate-limit-rpm']) ?? num(process.env.AURA_API_RPM);
|
|
@@ -121,21 +127,21 @@ if (argv.models) {
|
|
|
121
127
|
return acc;
|
|
122
128
|
}, {});
|
|
123
129
|
for (const [provider, models] of Object.entries(byProvider)) {
|
|
124
|
-
console.log(chalk_1.default.hex(
|
|
130
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` ${provider}`));
|
|
125
131
|
for (const m of models) {
|
|
126
|
-
console.log(` ${chalk_1.default.hex('#cc785c')(m.id.padEnd(45))} ${chalk_1.default.hex(
|
|
132
|
+
console.log(` ${chalk_1.default.hex('#cc785c')(m.id.padEnd(45))} ${chalk_1.default.hex(diamond_js_1.FAINT_HEX)(m.speed)}`);
|
|
127
133
|
}
|
|
128
134
|
}
|
|
129
|
-
console.log(chalk_1.default.hex(
|
|
130
|
-
console.log(chalk_1.default.hex(
|
|
131
|
-
console.log(chalk_1.default.hex(
|
|
135
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)('\n Use --model <id> or set AURA_MODEL env var'));
|
|
136
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(' For Ollama: --model ollama/llama3.2'));
|
|
137
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(' For OpenRouter: --model openrouter/<provider>/<name>\n'));
|
|
132
138
|
process.exit(0);
|
|
133
139
|
}
|
|
134
140
|
if (argv['list-sessions']) {
|
|
135
141
|
const root = argv.cwd ? path.resolve(argv.cwd) : process.cwd();
|
|
136
142
|
const sessions = session_store_js_1.sessionStore.listSessions(root);
|
|
137
143
|
if (sessions.length === 0) {
|
|
138
|
-
console.log(chalk_1.default.hex(
|
|
144
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('\n No saved sessions for this project.\n'));
|
|
139
145
|
}
|
|
140
146
|
else {
|
|
141
147
|
console.log(chalk_1.default.hex('#cc785c').bold('\n Saved sessions:\n'));
|
|
@@ -143,8 +149,8 @@ if (argv['list-sessions']) {
|
|
|
143
149
|
const updated = new Date(s.updatedAt).toLocaleString();
|
|
144
150
|
const turns = Math.floor(s.history.length / 2);
|
|
145
151
|
console.log(` ${chalk_1.default.hex('#cc785c')(s.id.padEnd(20))} ` +
|
|
146
|
-
`${chalk_1.default.hex(
|
|
147
|
-
`${chalk_1.default.hex(
|
|
152
|
+
`${chalk_1.default.hex(diamond_js_1.TEXT_HEX)(s.title.slice(0, 45).padEnd(46))} ` +
|
|
153
|
+
`${chalk_1.default.hex(diamond_js_1.FAINT_HEX)(`${turns}t · ${updated}`)}`);
|
|
148
154
|
}
|
|
149
155
|
console.log();
|
|
150
156
|
}
|
|
@@ -154,53 +160,39 @@ if (argv.analyze) {
|
|
|
154
160
|
const report = (0, weakness_miner_js_1.mineWeaknesses)();
|
|
155
161
|
const outPath = (0, weakness_miner_js_1.saveReport)(report);
|
|
156
162
|
console.log(chalk_1.default.hex('#cc785c').bold('\n Weakness Analysis Report\n'));
|
|
157
|
-
console.log(chalk_1.default.hex(
|
|
158
|
-
console.log(chalk_1.default.hex(
|
|
163
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` Sessions analyzed: ${report.sessionsAnalyzed}`));
|
|
164
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` Report saved to: ${outPath}\n`));
|
|
159
165
|
if (report.patterns.length === 0) {
|
|
160
166
|
console.log(chalk_1.default.hex('#5a9e6e')(' ✓ No recurring weakness patterns detected. Agent behavior looks healthy.\n'));
|
|
161
167
|
}
|
|
162
168
|
else {
|
|
163
169
|
for (const p of report.patterns) {
|
|
164
170
|
console.log(chalk_1.default.hex('#b15439').bold(` ✗ ${p.pattern} (${p.frequency} occurrences)`));
|
|
165
|
-
console.log(chalk_1.default.hex(
|
|
171
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` ${p.description}`));
|
|
166
172
|
if (p.occurrences[0]) {
|
|
167
|
-
console.log(chalk_1.default.hex(
|
|
168
|
-
console.log(chalk_1.default.hex(
|
|
173
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(` Example task: "${p.occurrences[0].exampleTask.slice(0, 80)}"`));
|
|
174
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(` Example failure: ${p.occurrences[0].exampleFailure.slice(0, 100)}`));
|
|
169
175
|
}
|
|
170
176
|
console.log(chalk_1.default.hex('#cc785c')(` Suggestion: ${p.promptPatch.slice(0, 120)}...`));
|
|
171
177
|
console.log();
|
|
172
178
|
}
|
|
173
|
-
console.log(chalk_1.default.hex(
|
|
179
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` ${report.summary}\n`));
|
|
174
180
|
}
|
|
175
181
|
process.exit(0);
|
|
176
182
|
}
|
|
177
|
-
if (argv.
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
console.log(chalk_1.default.hex('#cc785c').bold(line));
|
|
186
|
-
}
|
|
187
|
-
else if (line.includes(':')) {
|
|
188
|
-
const colonIdx = line.indexOf(':');
|
|
189
|
-
const label = line.slice(0, colonIdx + 1);
|
|
190
|
-
const value = line.slice(colonIdx + 1);
|
|
191
|
-
console.log(chalk_1.default.hex('#8a7768')(label) + chalk_1.default.hex('#ede0cc')(value));
|
|
192
|
-
}
|
|
193
|
-
else {
|
|
194
|
-
console.log(line);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
process.exit(0);
|
|
198
|
-
})();
|
|
183
|
+
if (argv.doctor === true) {
|
|
184
|
+
const cwd = typeof argv.cwd === 'string' ? path.resolve(argv.cwd) : process.cwd();
|
|
185
|
+
const fix = process.argv.includes('--fix');
|
|
186
|
+
const offline = process.argv.includes('--offline');
|
|
187
|
+
(0, index_js_5.runDoctor)({ projectRoot: cwd, fix, offline }).then(report => {
|
|
188
|
+
console.log((0, index_js_5.formatDoctorReport)(report));
|
|
189
|
+
process.exit(report.summary.error > 0 ? 1 : 0);
|
|
190
|
+
}).catch(e => { console.error('Doctor error:', String(e)); process.exit(1); });
|
|
199
191
|
}
|
|
200
192
|
if (argv['propose-harness']) {
|
|
201
193
|
// Ensure a weakness report exists — mine if needed
|
|
202
194
|
if (!fs.existsSync((0, weakness_miner_js_1.reportPath)())) {
|
|
203
|
-
console.log(chalk_1.default.hex(
|
|
195
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('\n No weakness report found — mining sessions first...\n'));
|
|
204
196
|
const report = (0, weakness_miner_js_1.mineWeaknesses)();
|
|
205
197
|
(0, weakness_miner_js_1.saveReport)(report);
|
|
206
198
|
}
|
|
@@ -212,13 +204,13 @@ if (argv['propose-harness']) {
|
|
|
212
204
|
console.log(chalk_1.default.hex('#cc785c').bold('\n Harness Proposals\n'));
|
|
213
205
|
for (const p of proposals) {
|
|
214
206
|
console.log(chalk_1.default.hex('#cc785c')(` ${p.id}`));
|
|
215
|
-
console.log(chalk_1.default.hex(
|
|
216
|
-
console.log(chalk_1.default.hex(
|
|
217
|
-
console.log(chalk_1.default.hex(
|
|
207
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` Pattern: ${p.pattern} (${p.description.slice(0, 60)})`));
|
|
208
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` Section: ${p.targetSection}`));
|
|
209
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(` Patch: ${p.patchText.slice(0, 80)}...`));
|
|
218
210
|
console.log();
|
|
219
211
|
}
|
|
220
212
|
console.log(chalk_1.default.hex('#5a9e6e')(` ${proposals.length} proposal(s) saved to ~/.aura/harness/proposals/`));
|
|
221
|
-
console.log(chalk_1.default.hex(
|
|
213
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(' Apply with: ruby --apply-harness <id>\n'));
|
|
222
214
|
}
|
|
223
215
|
process.exit(0);
|
|
224
216
|
}
|
|
@@ -239,7 +231,7 @@ if (argv.workflows) {
|
|
|
239
231
|
(async () => {
|
|
240
232
|
const workflows = await (0, engine_js_1.listWorkflows)();
|
|
241
233
|
if (workflows.length === 0) {
|
|
242
|
-
console.log(chalk_1.default.hex(
|
|
234
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('\n No saved workflows.\n'));
|
|
243
235
|
}
|
|
244
236
|
else {
|
|
245
237
|
console.log(chalk_1.default.hex('#cc785c').bold('\n Saved workflows:\n'));
|
|
@@ -249,9 +241,9 @@ if (argv.workflows) {
|
|
|
249
241
|
const totalSteps = ws.definition.steps.length;
|
|
250
242
|
const statusColor = ws.status === 'done' ? '#5a9e6e' : ws.status === 'failed' ? '#b15439' : '#cc785c';
|
|
251
243
|
console.log(` ${chalk_1.default.hex('#cc785c')(ws.definition.id.padEnd(24))} ` +
|
|
252
|
-
`${chalk_1.default.hex(
|
|
244
|
+
`${chalk_1.default.hex(diamond_js_1.TEXT_HEX)(ws.definition.name.slice(0, 36).padEnd(37))} ` +
|
|
253
245
|
`${chalk_1.default.hex(statusColor)(ws.status.padEnd(8))} ` +
|
|
254
|
-
`${chalk_1.default.hex(
|
|
246
|
+
`${chalk_1.default.hex(diamond_js_1.FAINT_HEX)(`${doneSteps}/${totalSteps} steps · ${created}`)}`);
|
|
255
247
|
}
|
|
256
248
|
console.log();
|
|
257
249
|
}
|
|
@@ -263,7 +255,7 @@ if (argv.blueprints) {
|
|
|
263
255
|
(async () => {
|
|
264
256
|
const bps = await (0, engine_js_2.listBlueprints)();
|
|
265
257
|
if (bps.length === 0) {
|
|
266
|
-
console.log(chalk_1.default.hex(
|
|
258
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('\n No saved blueprints.\n'));
|
|
267
259
|
}
|
|
268
260
|
else {
|
|
269
261
|
console.log(chalk_1.default.hex('#cc785c').bold('\n Saved blueprints:\n'));
|
|
@@ -274,8 +266,8 @@ if (argv.blueprints) {
|
|
|
274
266
|
const statusColor = bp.status === 'complete' ? '#5a9e6e' : bp.status === 'building' ? '#cc9e5c' : '#cc785c';
|
|
275
267
|
console.log(` ${chalk_1.default.hex(statusColor)(bp.status.padEnd(10))} ` +
|
|
276
268
|
`${chalk_1.default.hex('#cc785c')(bp.id.slice(0, 16).padEnd(18))} ` +
|
|
277
|
-
`${chalk_1.default.hex(
|
|
278
|
-
`${chalk_1.default.hex(
|
|
269
|
+
`${chalk_1.default.hex(diamond_js_1.TEXT_HEX)(bp.task.slice(0, 40).padEnd(41))} ` +
|
|
270
|
+
`${chalk_1.default.hex(diamond_js_1.FAINT_HEX)(`${builtCount}/${totalFiles} files · ${created}`)}`);
|
|
279
271
|
}
|
|
280
272
|
console.log();
|
|
281
273
|
}
|
|
@@ -292,53 +284,53 @@ if (typeof argv.blueprint === 'string' && argv.blueprint) {
|
|
|
292
284
|
}
|
|
293
285
|
const statusColor = bp.status === 'complete' ? '#5a9e6e' : bp.status === 'building' ? '#cc9e5c' : '#cc785c';
|
|
294
286
|
console.log(chalk_1.default.hex('#cc785c').bold('\n Blueprint\n'));
|
|
295
|
-
console.log(` ${chalk_1.default.hex(
|
|
296
|
-
console.log(` ${chalk_1.default.hex(
|
|
297
|
-
console.log(` ${chalk_1.default.hex(
|
|
298
|
-
console.log(` ${chalk_1.default.hex(
|
|
299
|
-
console.log(` ${chalk_1.default.hex(
|
|
287
|
+
console.log(` ${chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('ID:')} ${chalk_1.default.hex('#cc785c')(bp.id)}`);
|
|
288
|
+
console.log(` ${chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('Task:')} ${chalk_1.default.hex(diamond_js_1.TEXT_HEX)(bp.task)}`);
|
|
289
|
+
console.log(` ${chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('Status:')} ${chalk_1.default.hex(statusColor)(bp.status)}`);
|
|
290
|
+
console.log(` ${chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('Steps:')} ${bp.estimatedSteps}`);
|
|
291
|
+
console.log(` ${chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('Created:')} ${new Date(bp.createdAt).toLocaleString()}`);
|
|
300
292
|
if (bp.builtAt)
|
|
301
|
-
console.log(` ${chalk_1.default.hex(
|
|
293
|
+
console.log(` ${chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('Built:')} ${new Date(bp.builtAt).toLocaleString()}`);
|
|
302
294
|
if (bp.files.length > 0) {
|
|
303
295
|
console.log(chalk_1.default.hex('#cc785c').bold('\n Files:\n'));
|
|
304
296
|
for (const f of bp.files) {
|
|
305
|
-
const fileStatusColor = f.status === 'built' ? '#5a9e6e' : f.status === 'skipped' ? '#
|
|
297
|
+
const fileStatusColor = f.status === 'built' ? '#5a9e6e' : f.status === 'skipped' ? '#a68a2a' : '#cc785c';
|
|
306
298
|
console.log(` ${chalk_1.default.hex(fileStatusColor)(f.status.padEnd(8))} ${chalk_1.default.hex('#cc785c')(f.path)}`);
|
|
307
|
-
console.log(` ${chalk_1.default.hex(
|
|
299
|
+
console.log(` ${chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(f.purpose)}`);
|
|
308
300
|
if (f.exports.length > 0) {
|
|
309
|
-
console.log(` ${chalk_1.default.hex(
|
|
301
|
+
console.log(` ${chalk_1.default.hex(diamond_js_1.FAINT_HEX)(`exports: ${f.exports.join(', ')}`)}`);
|
|
310
302
|
}
|
|
311
303
|
if (f.interfaces.length > 0) {
|
|
312
|
-
console.log(` ${chalk_1.default.hex(
|
|
304
|
+
console.log(` ${chalk_1.default.hex(diamond_js_1.FAINT_HEX)(`interfaces: ${f.interfaces.join(', ')}`)}`);
|
|
313
305
|
}
|
|
314
306
|
}
|
|
315
307
|
}
|
|
316
308
|
if (bp.dataModels.length > 0) {
|
|
317
309
|
console.log(chalk_1.default.hex('#cc785c').bold('\n Data Models:\n'));
|
|
318
310
|
for (const dm of bp.dataModels) {
|
|
319
|
-
console.log(` ${chalk_1.default.hex('#cc785c')(dm.name)} — ${chalk_1.default.hex(
|
|
311
|
+
console.log(` ${chalk_1.default.hex('#cc785c')(dm.name)} — ${chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(dm.description)}`);
|
|
320
312
|
for (const field of dm.fields) {
|
|
321
|
-
console.log(` ${chalk_1.default.hex(
|
|
313
|
+
console.log(` ${chalk_1.default.hex(diamond_js_1.FAINT_HEX)(field)}`);
|
|
322
314
|
}
|
|
323
315
|
}
|
|
324
316
|
}
|
|
325
317
|
if (bp.dependencies.length > 0) {
|
|
326
318
|
console.log(chalk_1.default.hex('#cc785c').bold('\n Dependencies:\n'));
|
|
327
319
|
for (const dep of bp.dependencies) {
|
|
328
|
-
console.log(` ${chalk_1.default.hex(
|
|
320
|
+
console.log(` ${chalk_1.default.hex(diamond_js_1.FAINT_HEX)(dep)}`);
|
|
329
321
|
}
|
|
330
322
|
}
|
|
331
323
|
if (bp.risks.length > 0) {
|
|
332
324
|
console.log(chalk_1.default.hex('#b15439').bold('\n Risks:\n'));
|
|
333
325
|
for (const risk of bp.risks) {
|
|
334
|
-
console.log(` ${chalk_1.default.hex('#b15439')('⚠')} ${chalk_1.default.hex(
|
|
326
|
+
console.log(` ${chalk_1.default.hex('#b15439')('⚠')} ${chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(risk)}`);
|
|
335
327
|
}
|
|
336
328
|
}
|
|
337
329
|
if (bp.deviations.length > 0) {
|
|
338
330
|
console.log(chalk_1.default.hex('#cc9e5c').bold('\n Deviations:\n'));
|
|
339
331
|
for (const dev of bp.deviations) {
|
|
340
332
|
const time = new Date(dev.recordedAt).toLocaleString();
|
|
341
|
-
console.log(` ${chalk_1.default.hex('#cc9e5c')('→')} ${chalk_1.default.hex(
|
|
333
|
+
console.log(` ${chalk_1.default.hex('#cc9e5c')('→')} ${chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(dev.description)} ${chalk_1.default.hex(diamond_js_1.FAINT_HEX)(`(${time})`)}`);
|
|
342
334
|
}
|
|
343
335
|
}
|
|
344
336
|
console.log();
|
|
@@ -353,7 +345,7 @@ if (argv.help) {
|
|
|
353
345
|
// When run with no args + a TTY or piped input, fall through to the REPL/wizard.
|
|
354
346
|
// Skip this gate when --reset-setup is set (the wizard should fire even if env
|
|
355
347
|
// vars make needsWizard() return false).
|
|
356
|
-
if (argv._.length === 0 && !argv.interactive && process.stdin.isTTY !== true && !argv['reset-setup']) {
|
|
348
|
+
if (argv._.length === 0 && !argv.interactive && !argv.doctor && process.stdin.isTTY !== true && !argv['reset-setup']) {
|
|
357
349
|
if (!(0, first_run_js_1.needsWizard)({})) {
|
|
358
350
|
printHelp();
|
|
359
351
|
process.exit(0);
|
|
@@ -364,21 +356,39 @@ if (argv._.length === 0 && !argv.interactive && process.stdin.isTTY !== true &&
|
|
|
364
356
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
365
357
|
const cwd = argv.cwd ? path.resolve(argv.cwd) : process.cwd();
|
|
366
358
|
const fileConfig = (0, project_config_js_1.loadProjectConfig)(cwd);
|
|
367
|
-
//
|
|
368
|
-
//
|
|
359
|
+
// No TLS patching needed — the DigiCert root CA used by the MiMo endpoint
|
|
360
|
+
// is already trusted by Node's built-in certificate store.
|
|
361
|
+
// Load persisted API keys (~/.aura/keys.json) into process.env before any
|
|
362
|
+
// provider is built — so keys set once with :apikey survive across sessions
|
|
363
|
+
// and every provider sees them, without depending on shell rc files. Real
|
|
364
|
+
// environment values are never overridden.
|
|
365
|
+
(0, key_store_js_1.loadKeysIntoEnv)();
|
|
366
|
+
// Pull global config (saved by the setup wizard) so the user doesn't have
|
|
367
|
+
// to re-set their provider on every run. provider.json is the fuller record
|
|
368
|
+
// from the same wizard save — it also carries the API key and base URL, so a
|
|
369
|
+
// choice made last session works next session without any env vars.
|
|
369
370
|
const globalCfg = (0, global_config_js_1.loadGlobalConfig)();
|
|
370
|
-
|
|
371
|
+
const savedProvider = (0, provider_wizard_js_1.loadProviderConfig)();
|
|
372
|
+
// Effective model = CLI > AURA_MODEL env > .aura.json > global config > undefined
|
|
371
373
|
const cliModel = typeof argv.model === 'string' ? argv.model : undefined;
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
//
|
|
374
|
+
const effectiveModel = cliModel ?? fileConfig.model ?? globalCfg?.defaultModel
|
|
375
|
+
?? savedProvider?.model ?? process.env.AURA_MODEL;
|
|
376
|
+
// The saved provider record only applies when we're actually running the
|
|
377
|
+
// model it was saved with — its baseUrl/apiKey belong to that provider.
|
|
378
|
+
const savedProviderApplies = !!savedProvider && effectiveModel === savedProvider.model;
|
|
379
|
+
// Effective base URL = CLI > .aura.json > global config > undefined.
|
|
380
|
+
// CRITICAL: the global config's baseUrl belongs to the provider the wizard
|
|
381
|
+
// configured — it must NOT be forced onto a DIFFERENT model. Otherwise picking
|
|
382
|
+
// `-m deepseek/...` while the global default is a GLM/Z.ai endpoint sends the
|
|
383
|
+
// DeepSeek key to Z.ai → 401. So only inherit globalCfg.baseUrl when the
|
|
384
|
+
// effective model is actually that global default model (same provider).
|
|
380
385
|
const cliBaseUrl = typeof argv['base-url'] === 'string' ? argv['base-url'] : undefined;
|
|
381
|
-
const
|
|
386
|
+
const globalBaseUrlApplies = !!globalCfg?.baseUrl &&
|
|
387
|
+
!!globalCfg?.defaultModel &&
|
|
388
|
+
effectiveModel === globalCfg.defaultModel;
|
|
389
|
+
const effectiveBaseUrl = cliBaseUrl ?? fileConfig.baseUrl
|
|
390
|
+
?? (globalBaseUrlApplies ? globalCfg.baseUrl : undefined)
|
|
391
|
+
?? (savedProviderApplies ? savedProvider.baseUrl : undefined);
|
|
382
392
|
const resolved = (0, project_config_js_1.resolveConfig)({ ...fileConfig, model: effectiveModel, baseUrl: effectiveBaseUrl }, {
|
|
383
393
|
model: cliModel,
|
|
384
394
|
baseUrl: cliBaseUrl,
|
|
@@ -397,7 +407,11 @@ const permissionLevel = resolved.mode;
|
|
|
397
407
|
const runtimeConfig = {
|
|
398
408
|
model: resolved.model,
|
|
399
409
|
baseUrl: resolved.baseUrl,
|
|
400
|
-
|
|
410
|
+
// --api-key wins; otherwise the key the wizard saved with this model last
|
|
411
|
+
// session. Env vars only apply when neither is present (factory fallback).
|
|
412
|
+
apiKey: typeof argv['api-key'] === 'string'
|
|
413
|
+
? argv['api-key']
|
|
414
|
+
: (savedProviderApplies ? savedProvider.apiKey : undefined),
|
|
401
415
|
};
|
|
402
416
|
// ── Profile: local → Ollama defaults ─────────────────────────────────────────
|
|
403
417
|
if (cliProfile === 'local') {
|
|
@@ -407,18 +421,12 @@ if (cliProfile === 'local') {
|
|
|
407
421
|
}
|
|
408
422
|
}
|
|
409
423
|
function buildProvider(display) {
|
|
410
|
-
//
|
|
411
|
-
const model =
|
|
412
|
-
const transport = (0, factory_js_2.resolveProviderTransport)(model, {
|
|
413
|
-
baseUrl: runtimeConfig.baseUrl ?? resolved.baseUrl ?? undefined,
|
|
414
|
-
apiKey: runtimeConfig.apiKey,
|
|
415
|
-
});
|
|
416
|
-
// Keep resolved in sync so external reads (e.g. header display) reflect the switch.
|
|
417
|
-
resolved.model = model;
|
|
424
|
+
// Caller guarantees resolved.model is set (guarded in main()).
|
|
425
|
+
const model = resolved.model;
|
|
418
426
|
return (0, resilient_factory_js_1.createResilientProvider)({
|
|
419
427
|
model,
|
|
420
|
-
apiKey:
|
|
421
|
-
baseUrl:
|
|
428
|
+
apiKey: runtimeConfig.apiKey,
|
|
429
|
+
baseUrl: runtimeConfig.baseUrl ?? undefined,
|
|
422
430
|
}, {
|
|
423
431
|
rpm: resolved.rateLimitRpm,
|
|
424
432
|
tpm: resolved.rateLimitTpm,
|
|
@@ -430,9 +438,6 @@ function buildProvider(display) {
|
|
|
430
438
|
// Main
|
|
431
439
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
432
440
|
async function main() {
|
|
433
|
-
(0, diamond_js_1.renderDiamond)();
|
|
434
|
-
await new Promise(r => setTimeout(r, 1500));
|
|
435
|
-
process.stdout.write('\x1b[2J\x1b[H');
|
|
436
441
|
const display = (0, display_js_1.createTerminalDisplay)();
|
|
437
442
|
// ── First-run wizard ───────────────────────────────────────────────────────
|
|
438
443
|
// Skip if: --no-setup flag, --api-key on CLI, env var set, or global config exists.
|
|
@@ -452,50 +457,45 @@ async function main() {
|
|
|
452
457
|
const cliModel = typeof argv.model === 'string' ? argv.model : undefined;
|
|
453
458
|
const skipSetup = argv['no-setup'] === true || argv.help === true || argv.h === true || argv.models === true || argv.version === true || argv.v === true;
|
|
454
459
|
const resetSetup = argv['reset-setup'] === true;
|
|
455
|
-
const cliSetup = argv['setup'] === true;
|
|
456
460
|
if (resetSetup) {
|
|
457
|
-
// Wipe
|
|
461
|
+
// Wipe both saved configs so the wizard fires unconditionally and no
|
|
462
|
+
// stale provider record (baseUrl/apiKey) survives the reset.
|
|
458
463
|
try {
|
|
459
464
|
fs.unlinkSync((0, global_config_js_1.globalConfigPath)());
|
|
460
465
|
}
|
|
461
466
|
catch { /* not present */ }
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
if (cliSetup && !skipSetup) {
|
|
465
|
-
const cfg = await (0, provider_wizard_js_1.runProviderWizard)();
|
|
466
|
-
if (cfg) {
|
|
467
|
-
resolved.model = cfg.model;
|
|
468
|
-
resolved.baseUrl = cfg.baseUrl;
|
|
469
|
-
runtimeConfig.model = cfg.model;
|
|
470
|
-
runtimeConfig.baseUrl = cfg.baseUrl;
|
|
471
|
-
runtimeConfig.apiKey = cfg.apiKey;
|
|
467
|
+
try {
|
|
468
|
+
fs.unlinkSync(path.join(path.dirname((0, global_config_js_1.globalConfigPath)()), 'provider.json'));
|
|
472
469
|
}
|
|
470
|
+
catch { /* not present */ }
|
|
473
471
|
}
|
|
474
472
|
// When --reset-setup is set, force the wizard to fire (overrides env-var
|
|
475
473
|
// detection — the user explicitly wants to reconfigure).
|
|
476
|
-
const shouldRunWizard = !skipSetup &&
|
|
474
|
+
const shouldRunWizard = !skipSetup && isInteractive && (resetSetup || (0, first_run_js_1.needsWizard)({ cliApiKey, cliModel }));
|
|
477
475
|
if (shouldRunWizard) {
|
|
478
476
|
// If stdin is not a TTY and there's nothing piped in, the wizard will
|
|
479
477
|
// hang. Skip with a helpful message instead.
|
|
480
478
|
if (process.stdin.isTTY !== true && !process.stdin.readable) {
|
|
481
479
|
console.error(chalk_1.default.hex('#b15439')('\n ✗ No interactive input available.'));
|
|
482
|
-
console.error(chalk_1.default.hex(
|
|
483
|
-
console.error(chalk_1.default.hex(
|
|
480
|
+
console.error(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(' Set an API key env var (e.g. export OPENAI_API_KEY=...)'));
|
|
481
|
+
console.error(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(' or pass --api-key <key> --model <id> on the command line,\n'));
|
|
484
482
|
process.exit(1);
|
|
485
483
|
}
|
|
486
|
-
|
|
484
|
+
// Full provider wizard: pick provider + model, detect/enter key, and
|
|
485
|
+
// TEST the connection (URL normalization + response validation) before
|
|
486
|
+
// saving. The choice is persisted and restored on every later run.
|
|
487
|
+
const cfg = await (0, provider_wizard_js_1.runProviderWizard)();
|
|
487
488
|
if (!cfg) {
|
|
488
489
|
console.error(chalk_1.default.hex('#b15439')('\n ✗ Setup cancelled. Set an API key env var (e.g. export OPENAI_API_KEY=...) or run with --api-key.\n'));
|
|
489
490
|
process.exit(1);
|
|
490
491
|
}
|
|
491
|
-
//
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
runtimeConfig.
|
|
498
|
-
}
|
|
492
|
+
// Apply the wizard's choice to this session (it already saved to disk).
|
|
493
|
+
resolved.model = cfg.model;
|
|
494
|
+
resolved.baseUrl = cfg.baseUrl || undefined;
|
|
495
|
+
runtimeConfig.model = cfg.model;
|
|
496
|
+
runtimeConfig.baseUrl = cfg.baseUrl || undefined;
|
|
497
|
+
if (cfg.apiKey)
|
|
498
|
+
runtimeConfig.apiKey = cfg.apiKey;
|
|
499
499
|
}
|
|
500
500
|
let ctx;
|
|
501
501
|
try {
|
|
@@ -508,9 +508,9 @@ async function main() {
|
|
|
508
508
|
// ── Guard: we need a model before we can build a provider ─────────────────
|
|
509
509
|
if (!resolved.model) {
|
|
510
510
|
console.error(chalk_1.default.hex('#b15439')('\n ✗ No model configured.'));
|
|
511
|
-
console.error(chalk_1.default.hex(
|
|
512
|
-
console.error(chalk_1.default.hex(
|
|
513
|
-
console.error(chalk_1.default.hex(
|
|
511
|
+
console.error(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(' Run `aura` with no args in a TTY to launch the setup wizard,'));
|
|
512
|
+
console.error(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(' or pass --model <id> --api-key <key> on the command line,'));
|
|
513
|
+
console.error(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(' or set the model in .aura.json (`"model": "..."`).'));
|
|
514
514
|
process.exit(1);
|
|
515
515
|
}
|
|
516
516
|
let provider;
|
|
@@ -573,32 +573,20 @@ async function main() {
|
|
|
573
573
|
}
|
|
574
574
|
// Legacy sessionPath kept for single-task one-shot mode
|
|
575
575
|
const sessionPath = noSession ? undefined : path.join(session_store_js_1.sessionStore.defaultDir(), projectRoot.replace(/[^a-zA-Z0-9_-]/g, '_').slice(0, 80), 'latest.json');
|
|
576
|
-
// ──
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
console.log(red1(' ██╔══██║██║ ██║██╔══██╗██╔══██║ ██║ ██║ ██║██║ ██║██╔══╝ '));
|
|
591
|
-
console.log(red2(' ██║ ██║╚██████╔╝██║ ██║██║ ██║ ╚██████╗╚██████╔╝██████╔╝███████╗'));
|
|
592
|
-
console.log(red2(' ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝'));
|
|
593
|
-
console.log('');
|
|
594
|
-
console.log(chalk_1.default.hex('#4e3d30')(' "I don\'t try. I verify."'));
|
|
595
|
-
console.log('');
|
|
596
|
-
}
|
|
597
|
-
console.log('');
|
|
598
|
-
await new Promise(r => setTimeout(r, 50));
|
|
599
|
-
display.header(`Aura — ${ctx.name}`, `${provider.name} · ${runtimeConfig.model} · ${ctx.language} · ${permissionLevel} mode` +
|
|
600
|
-
(fileConfig.model ? ` · .aura.json loaded` : '') +
|
|
601
|
-
(activeChatId ? ` · chat ${activeChatId}` : ''));
|
|
576
|
+
// ── Startup banner ──────────────────────────────────────────────────────────
|
|
577
|
+
(0, diamond_js_1.renderBanner)({
|
|
578
|
+
version: package_json_1.default.version,
|
|
579
|
+
title: ctx.name,
|
|
580
|
+
provider: provider.name,
|
|
581
|
+
model: runtimeConfig.model,
|
|
582
|
+
language: ctx.language,
|
|
583
|
+
mode: permissionLevel,
|
|
584
|
+
cwd: projectRoot,
|
|
585
|
+
extras: [
|
|
586
|
+
...(fileConfig.model ? ['.aura.json loaded'] : []),
|
|
587
|
+
...(activeChatId ? [`chat ${activeChatId}`] : []),
|
|
588
|
+
],
|
|
589
|
+
});
|
|
602
590
|
const cumulative = { turns: 0, toolCalls: 0, inputTokens: 0, outputTokens: 0, costUsd: 0 };
|
|
603
591
|
// ── --build <id>: build from a saved blueprint in dependency order ───────────
|
|
604
592
|
if (typeof argv.build === 'string' && argv.build) {
|
|
@@ -608,8 +596,8 @@ async function main() {
|
|
|
608
596
|
process.exit(1);
|
|
609
597
|
}
|
|
610
598
|
display.header('Architect Builder', `Building from blueprint: ${bp.id}`);
|
|
611
|
-
console.log(chalk_1.default.hex(
|
|
612
|
-
console.log(chalk_1.default.hex(
|
|
599
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` Task: ${bp.task}`));
|
|
600
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` Files: ${bp.files.filter(f => f.status === 'planned').length} to build\n`));
|
|
613
601
|
await (0, engine_js_2.updateBlueprintStatus)(bp.id, 'building');
|
|
614
602
|
// Build files in order — planned files only
|
|
615
603
|
const plannedFiles = bp.files.filter(f => f.status === 'planned');
|
|
@@ -668,7 +656,7 @@ async function main() {
|
|
|
668
656
|
const stepTasks = argv._.map(String);
|
|
669
657
|
if (stepTasks.length === 0) {
|
|
670
658
|
console.error(chalk_1.default.hex('#b15439')('\n ✗ No step tasks provided.'));
|
|
671
|
-
console.error(chalk_1.default.hex(
|
|
659
|
+
console.error(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(' Usage: ruby --workflow <name> "step 1" "step 2" ...\n'));
|
|
672
660
|
process.exit(1);
|
|
673
661
|
}
|
|
674
662
|
const steps = stepTasks.map((task, i) => ({
|
|
@@ -708,9 +696,9 @@ async function main() {
|
|
|
708
696
|
console.error(chalk_1.default.hex('#b15439').bold(`\n ✗ ${finalState.outcome}\n`));
|
|
709
697
|
}
|
|
710
698
|
const totalTokens = finalState.totalTokens ?? 0;
|
|
711
|
-
console.log(chalk_1.default.hex(
|
|
699
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(` ↳ ${totalTokens.toLocaleString()} tokens · ${finalState.stepStates.length} steps · status: ${finalState.status}`));
|
|
712
700
|
if (finalState.status === 'failed') {
|
|
713
|
-
console.log(chalk_1.default.hex(
|
|
701
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(`\n Resume with: ruby --resume-workflow ${finalState.definition.id}\n`));
|
|
714
702
|
process.exit(1);
|
|
715
703
|
}
|
|
716
704
|
return;
|
|
@@ -753,9 +741,9 @@ async function main() {
|
|
|
753
741
|
console.error(chalk_1.default.hex('#b15439').bold(`\n ✗ ${finalState.outcome}\n`));
|
|
754
742
|
}
|
|
755
743
|
const totalTokens = finalState.totalTokens ?? 0;
|
|
756
|
-
console.log(chalk_1.default.hex(
|
|
744
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(` ↳ ${totalTokens.toLocaleString()} tokens · ${finalState.stepStates.length} steps · status: ${finalState.status}`));
|
|
757
745
|
if (finalState.status === 'failed') {
|
|
758
|
-
console.log(chalk_1.default.hex(
|
|
746
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(`\n Resume with: ruby --resume-workflow ${finalState.definition.id}\n`));
|
|
759
747
|
process.exit(1);
|
|
760
748
|
}
|
|
761
749
|
return;
|
|
@@ -763,77 +751,60 @@ async function main() {
|
|
|
763
751
|
// ── Single task mode: aura "fix the bug" ──────────────────────────────────────
|
|
764
752
|
if (argv._.length > 0) {
|
|
765
753
|
const task = argv._.join(' ');
|
|
766
|
-
console.log(chalk_1.default.hex(
|
|
767
|
-
// --architect: plan-only — decompose and display
|
|
754
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(`\n Task: ${chalk_1.default.hex(diamond_js_1.TEXT_HEX)(task)}\n`));
|
|
755
|
+
// --architect: plan-only — decompose and display, then exit (no execution)
|
|
768
756
|
if (argv.architect === true) {
|
|
769
757
|
await runArchitectPlan(task, provider, ctx, display);
|
|
770
|
-
|
|
771
|
-
return;
|
|
772
|
-
return startRepl();
|
|
758
|
+
return;
|
|
773
759
|
}
|
|
774
760
|
// --build: full orchestrated build — decompose + execute all specialists
|
|
775
761
|
// When --build <id> is a string, build from a saved blueprint instead
|
|
776
762
|
const doOrchestrate = argv.orchestrate === true || argv.build === true;
|
|
777
763
|
if (doOrchestrate) {
|
|
778
764
|
await runOrchestratedTask(task, provider, ctx, display, doOrchestrate);
|
|
779
|
-
|
|
780
|
-
return;
|
|
781
|
-
return startRepl();
|
|
765
|
+
return;
|
|
782
766
|
}
|
|
783
767
|
try {
|
|
784
768
|
let perception = await (0, index_js_4.loadPerception)(ctx.root);
|
|
785
769
|
if (!perception || (0, index_js_4.isStale)(perception)) {
|
|
786
770
|
display.agentThinking();
|
|
787
771
|
perception = await (0, index_js_4.extractPerception)(ctx.root);
|
|
788
|
-
// Persist so the viz dashboard and context summary stay populated
|
|
789
|
-
(0, index_js_4.savePerception)(perception).catch(() => { });
|
|
790
|
-
(0, index_js_4.saveGraphForViz)(perception).catch(() => { });
|
|
791
772
|
}
|
|
792
773
|
const decision = await (0, index_js_3.routeTask)({ provider, context: ctx, task, perception: perception ?? undefined });
|
|
793
774
|
if (decision.shouldDecompose && decision.confidence > 0.8) {
|
|
794
775
|
await runOrchestratedTask(task, provider, ctx, display, false, perception ?? undefined);
|
|
795
|
-
|
|
796
|
-
return;
|
|
797
|
-
return startRepl();
|
|
776
|
+
return;
|
|
798
777
|
}
|
|
799
778
|
}
|
|
800
779
|
catch {
|
|
801
780
|
// Router failed — fall through to single agent
|
|
802
781
|
}
|
|
803
|
-
|
|
804
|
-
//
|
|
805
|
-
//
|
|
806
|
-
//
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
const
|
|
812
|
-
|
|
813
|
-
.
|
|
814
|
-
.filter(id => (0, factory_js_2.modelProviderFamily)(id) === (0, factory_js_2.modelProviderFamily)(runtimeConfig.model ?? resolved.model));
|
|
815
|
-
const suggested = await (0, model_selector_js_1.selectModel)(ctx.root, task, availableModelIds);
|
|
816
|
-
if (suggested && suggested !== runtimeConfig.model) {
|
|
817
|
-
console.log(chalk_1.default.hex('#4e3d30')(` Using ${suggested} (selected by competence history)\n`));
|
|
818
|
-
runtimeConfig.model = suggested;
|
|
819
|
-
runtimeConfig.baseUrl = undefined;
|
|
820
|
-
runtimeConfig.apiKey = undefined;
|
|
821
|
-
// Rebuild provider with the suggested model
|
|
822
|
-
provider = buildProvider(display);
|
|
782
|
+
const doVerify = cliVerify || !!fileConfig.verify;
|
|
783
|
+
// --moa: mixture-of-agents Phase 2 — only pays off for exploratory-shaped
|
|
784
|
+
// tasks (see docs/MIXTURE_OF_AGENTS.md), so it's gated on both the flag
|
|
785
|
+
// and the task shape. Other shapes fall through to the single-agent path.
|
|
786
|
+
if (argv.moa === true) {
|
|
787
|
+
const { classifyTask } = await Promise.resolve().then(() => __importStar(require('../agent/loop-profile.js')));
|
|
788
|
+
if (classifyTask(task) === 'exploratory') {
|
|
789
|
+
const { runMixtureOfAgents } = await Promise.resolve().then(() => __importStar(require('../agent/mixture.js')));
|
|
790
|
+
const moaResult = await runMixtureOfAgents({ provider, task, context: ctx, display });
|
|
791
|
+
if (activeChatId && !noSession) {
|
|
792
|
+
await session_store_js_1.sessionStore.upsertSession(projectRoot, activeChatId, moaResult.history, activeChatTitle);
|
|
823
793
|
}
|
|
794
|
+
if (moaResult.success) {
|
|
795
|
+
display.summary(moaResult.summary, moaResult.turns, moaResult.toolCallCount);
|
|
796
|
+
printUsageFooter(display, moaResult.usage, moaResult.costUsd);
|
|
797
|
+
}
|
|
798
|
+
else {
|
|
799
|
+
display.error(moaResult.summary);
|
|
800
|
+
process.exit(1);
|
|
801
|
+
}
|
|
802
|
+
return;
|
|
824
803
|
}
|
|
825
|
-
|
|
826
|
-
// Competence selection is best-effort; never block the user
|
|
827
|
-
}
|
|
804
|
+
display.warning('--moa only applies to exploratory tasks (explain/analyze/investigate…) — running the normal single-agent path.');
|
|
828
805
|
}
|
|
829
|
-
const doVerify = cliVerify || !!fileConfig.verify;
|
|
830
|
-
const rubyConfig = { ...types_js_1.DEFAULT_RUBY_CONFIG, ...fileConfig.ruby };
|
|
831
|
-
const episodeStart = Date.now();
|
|
832
806
|
let result;
|
|
833
|
-
let alternatorHandledEpisode = false;
|
|
834
807
|
if (doVerify) {
|
|
835
|
-
// Ruby-alternation doesn't support the verification wrapper yet —
|
|
836
|
-
// a verified task always runs directly against the selected model.
|
|
837
808
|
const { runWithVerification } = await Promise.resolve().then(() => __importStar(require('../verify/index.js')));
|
|
838
809
|
const maxRetries = cliMaxVerifyRetries ?? fileConfig.maxVerifyRetries ?? defaults_js_1.DEFAULTS.maxVerifyRetries;
|
|
839
810
|
const testCommand = cliTestCommand ?? fileConfig.testCommand;
|
|
@@ -854,20 +825,6 @@ async function main() {
|
|
|
854
825
|
});
|
|
855
826
|
result = wrapperResult.loopResult;
|
|
856
827
|
}
|
|
857
|
-
else if (rubyConfig.enabled) {
|
|
858
|
-
const alternator = new alternator_js_1.RubyAlternator({
|
|
859
|
-
rubyConfig,
|
|
860
|
-
largeModelProvider: provider,
|
|
861
|
-
projectRoot: ctx.root,
|
|
862
|
-
context: ctx,
|
|
863
|
-
display,
|
|
864
|
-
permissions,
|
|
865
|
-
initialHistory: activeChatHistory,
|
|
866
|
-
});
|
|
867
|
-
const altResult = await alternator.run(task);
|
|
868
|
-
result = altResult.loopResult;
|
|
869
|
-
alternatorHandledEpisode = true; // alternator.run() already saved its own episode
|
|
870
|
-
}
|
|
871
828
|
else {
|
|
872
829
|
result = await (0, loop_js_1.runAgentLoop)({
|
|
873
830
|
provider, task, context: ctx, permissions, display,
|
|
@@ -880,254 +837,302 @@ async function main() {
|
|
|
880
837
|
sessionPath,
|
|
881
838
|
});
|
|
882
839
|
}
|
|
883
|
-
// Persist episode for Ruby Principle training data — skip when
|
|
884
|
-
// RubyAlternator already saved its own (richer) episode above, to
|
|
885
|
-
// avoid double-counting every task in the stats/dashboard.
|
|
886
|
-
if (!alternatorHandledEpisode) {
|
|
887
|
-
try {
|
|
888
|
-
const { randomBytes } = await Promise.resolve().then(() => __importStar(require('crypto')));
|
|
889
|
-
await (0, episode_capture_js_1.saveEpisode)(ctx.root, {
|
|
890
|
-
id: randomBytes(4).toString('hex') + '-' + Date.now().toString(36),
|
|
891
|
-
timestamp: Date.now(),
|
|
892
|
-
task,
|
|
893
|
-
projectRoot: ctx.root,
|
|
894
|
-
rubyAttempted: false,
|
|
895
|
-
rubySucceeded: false,
|
|
896
|
-
largeModelUsed: runtimeConfig.model,
|
|
897
|
-
largeModelOutput: result.summary,
|
|
898
|
-
reviewerApproved: result.success,
|
|
899
|
-
tokensUsed: { largeModel: result.usage.totalTokens },
|
|
900
|
-
durationMs: Date.now() - episodeStart,
|
|
901
|
-
taskCategory: 'other',
|
|
902
|
-
});
|
|
903
|
-
}
|
|
904
|
-
catch {
|
|
905
|
-
// Episode recording is best-effort; never block the user
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
// Update history so REPL continues the conversation
|
|
909
|
-
activeChatHistory = result.history;
|
|
910
840
|
if (activeChatId && !noSession) {
|
|
911
841
|
await session_store_js_1.sessionStore.upsertSession(projectRoot, activeChatId, result.history, activeChatTitle);
|
|
912
842
|
}
|
|
843
|
+
{
|
|
844
|
+
const { recordEpisode } = await Promise.resolve().then(() => __importStar(require('../dream/episode.js')));
|
|
845
|
+
recordEpisode(ctx.root, {
|
|
846
|
+
task,
|
|
847
|
+
model: provider.model,
|
|
848
|
+
success: result.success,
|
|
849
|
+
tokens: (result.usage?.inputTokens ?? 0) + (result.usage?.outputTokens ?? 0),
|
|
850
|
+
durationMs: 0,
|
|
851
|
+
});
|
|
852
|
+
}
|
|
913
853
|
if (result.success) {
|
|
914
854
|
display.summary(result.summary, result.turns, result.toolCallCount);
|
|
915
|
-
printUsageFooter(display, result.usage, result.costUsd
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
history: result.history,
|
|
919
|
-
});
|
|
855
|
+
printUsageFooter(display, result.usage, result.costUsd);
|
|
856
|
+
if (speakEnabled)
|
|
857
|
+
await speakSummary(result.summary);
|
|
920
858
|
}
|
|
921
859
|
else {
|
|
922
860
|
display.error(result.summary);
|
|
861
|
+
process.exit(1);
|
|
923
862
|
}
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
//
|
|
929
|
-
|
|
930
|
-
//
|
|
931
|
-
//
|
|
932
|
-
//
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
provider: currentProvider, task: input,
|
|
1016
|
-
context: ctx, permissions, display,
|
|
1017
|
-
initialHistory: activeChatHistory,
|
|
1018
|
-
maxTurns: resolved.maxTurns,
|
|
1019
|
-
confirmFn: replConfirmFn,
|
|
1020
|
-
spawnConfig: {
|
|
1021
|
-
apiKey: runtimeConfig.apiKey,
|
|
1022
|
-
baseUrl: runtimeConfig.baseUrl ?? undefined,
|
|
1023
|
-
},
|
|
1024
|
-
sessionPath,
|
|
1025
|
-
},
|
|
1026
|
-
config: { enabled: true, maxRetries, testCommand },
|
|
1027
|
-
projectRoot: ctx.root,
|
|
1028
|
-
display,
|
|
1029
|
-
});
|
|
1030
|
-
result = wrapperResult.loopResult;
|
|
1031
|
-
}
|
|
1032
|
-
else if (rubyConfig.enabled) {
|
|
1033
|
-
const alternator = new alternator_js_1.RubyAlternator({
|
|
1034
|
-
rubyConfig,
|
|
1035
|
-
largeModelProvider: currentProvider,
|
|
1036
|
-
projectRoot: ctx.root,
|
|
1037
|
-
context: ctx,
|
|
1038
|
-
display,
|
|
1039
|
-
permissions,
|
|
1040
|
-
confirmFn: replConfirmFn,
|
|
1041
|
-
initialHistory: activeChatHistory,
|
|
1042
|
-
});
|
|
1043
|
-
const altResult = await alternator.run(input);
|
|
1044
|
-
result = altResult.loopResult;
|
|
1045
|
-
}
|
|
1046
|
-
else {
|
|
1047
|
-
result = await (0, loop_js_1.runAgentLoop)({
|
|
1048
|
-
provider: currentProvider, task: input,
|
|
1049
|
-
context: ctx, permissions, display,
|
|
1050
|
-
initialHistory: activeChatHistory,
|
|
1051
|
-
maxTurns: resolved.maxTurns,
|
|
1052
|
-
confirmFn: replConfirmFn,
|
|
1053
|
-
spawnConfig: {
|
|
1054
|
-
apiKey: runtimeConfig.apiKey,
|
|
1055
|
-
baseUrl: runtimeConfig.baseUrl ?? undefined,
|
|
1056
|
-
},
|
|
1057
|
-
sessionPath,
|
|
1058
|
-
});
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
catch (err) {
|
|
1062
|
-
const msg = err instanceof Error ? (err.stack || err.message) : String(err);
|
|
1063
|
-
console.error(chalk_1.default.hex('#b15439')(`\n ✗ Unhandled error: ${msg}\n`));
|
|
1064
|
-
// Suggest :provider on auth errors
|
|
1065
|
-
if (/\b(401|403|unauthorized|invalid.*api.?key|authentication)\b/i.test(msg)) {
|
|
1066
|
-
console.error(chalk_1.default.hex('#cc785c')(' 💡 Run :provider to reconfigure your API key\n'));
|
|
1067
|
-
}
|
|
1068
|
-
ask();
|
|
1069
|
-
return;
|
|
1070
|
-
}
|
|
1071
|
-
// Update stay-active history
|
|
1072
|
-
activeChatHistory = result.history;
|
|
1073
|
-
// Persist session
|
|
1074
|
-
if (activeChatId && !noSession) {
|
|
1075
|
-
await session_store_js_1.sessionStore.upsertSession(projectRoot, activeChatId, activeChatHistory, activeChatTitle);
|
|
1076
|
-
}
|
|
1077
|
-
cumulative.turns += result.turns;
|
|
1078
|
-
cumulative.toolCalls += result.toolCallCount;
|
|
1079
|
-
cumulative.inputTokens += result.usage.inputTokens;
|
|
1080
|
-
cumulative.outputTokens += result.usage.outputTokens;
|
|
1081
|
-
cumulative.costUsd += result.costUsd;
|
|
1082
|
-
if (result.success) {
|
|
1083
|
-
display.summary(result.summary, result.turns, result.toolCallCount);
|
|
1084
|
-
printUsageFooter(display, result.usage, result.costUsd, {
|
|
1085
|
-
model: runtimeConfig.model ?? undefined,
|
|
1086
|
-
cumulativeTokens: cumulative.inputTokens + cumulative.outputTokens,
|
|
1087
|
-
history: activeChatHistory,
|
|
1088
|
-
});
|
|
1089
|
-
}
|
|
1090
|
-
else {
|
|
1091
|
-
display.error(result.summary);
|
|
1092
|
-
}
|
|
1093
|
-
ask();
|
|
1094
|
-
});
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
// ── Interactive REPL mode (TUI — fixed bottom input) ────────────────────────
|
|
866
|
+
// Enter the alternate screen buffer for the whole interactive session —
|
|
867
|
+
// see enterAltScreen()'s doc comment in tui.ts for why: redrawing the
|
|
868
|
+
// header in place on the NORMAL screen (needed so it stays pinned while
|
|
869
|
+
// reflecting live typing) leaves every intermediate redraw frame
|
|
870
|
+
// permanently baked into the real terminal's scrollback the moment it
|
|
871
|
+
// scrolls past the top of the viewport — the live view was always
|
|
872
|
+
// correct, but scrolling up through history showed it stacked dozens of
|
|
873
|
+
// times. The alt screen is an isolated buffer with no persistent
|
|
874
|
+
// scrollback, so this can't happen; leaving it (destroyTui()/Ctrl+C)
|
|
875
|
+
// restores the terminal exactly as it was, same as quitting vim or less.
|
|
876
|
+
// The banner was already drawn once above (renderBanner() is shared with
|
|
877
|
+
// one-shot mode, which does NOT use the alt screen), so it has to be
|
|
878
|
+
// redrawn here — it rendered to the now-hidden normal screen, not this
|
|
879
|
+
// fresh alt-screen buffer.
|
|
880
|
+
(0, tui_js_1.enterAltScreen)();
|
|
881
|
+
const tuiBannerInfo = {
|
|
882
|
+
version: package_json_1.default.version,
|
|
883
|
+
title: ctx.name,
|
|
884
|
+
provider: provider.name,
|
|
885
|
+
model: runtimeConfig.model,
|
|
886
|
+
language: ctx.language,
|
|
887
|
+
mode: permissionLevel,
|
|
888
|
+
cwd: projectRoot,
|
|
889
|
+
extras: [
|
|
890
|
+
...(fileConfig.model ? ['.aura.json loaded'] : []),
|
|
891
|
+
...(activeChatId ? [`chat ${activeChatId}`] : []),
|
|
892
|
+
],
|
|
893
|
+
};
|
|
894
|
+
(0, diamond_js_1.renderBanner)(tuiBannerInfo);
|
|
895
|
+
// The TUI keeps its own copy of the banner rows: when scroll mode hands
|
|
896
|
+
// the screen back, the live view is rebuilt from scratch, and on the alt
|
|
897
|
+
// screen there's no scrollback to recover the banner from.
|
|
898
|
+
(0, tui_js_1.setBannerLines)((0, diamond_js_1.buildBannerLines)(tuiBannerInfo));
|
|
899
|
+
// Use the TUI display for output
|
|
900
|
+
const tuiDisplay = (0, tui_js_1.createTuiDisplay)();
|
|
901
|
+
// Shared context-health tracker: the loop records compaction events and
|
|
902
|
+
// per-turn snapshots into it; the /context command reads it back. The
|
|
903
|
+
// system-prompt source starts empty — the loop calls updateSystem() with
|
|
904
|
+
// the real (task-embedded) prompt each run.
|
|
905
|
+
const healthTracker = new context_health_js_1.ContextHealthTracker(() => '', () => activeChatHistory, resolved.model ?? 'gpt-4o', resolved.model ?? 'gpt-4o');
|
|
906
|
+
// ── Right-panel content: "Try" suggestions only ─────────────────────────
|
|
907
|
+
// The panel deliberately carries no Commands/Skills listings — the
|
|
908
|
+
// command list lives in :help and doesn't need permanent sidebar space
|
|
909
|
+
// next to the input.
|
|
910
|
+
const panelSuggestions = [
|
|
911
|
+
activeChatHistory.length === 0 ? ':help — see all commands' : ':doctor — scan for issues',
|
|
912
|
+
ctx.language ? `run: ${ctx.language === 'TypeScript' || ctx.language === 'JavaScript' ? 'npm test' : ':graph'}` : ':viz — memory dashboard',
|
|
913
|
+
];
|
|
914
|
+
(0, tui_js_1.setPanelContent)({ suggestions: panelSuggestions });
|
|
915
|
+
// Unboxed metadata line under the input box, mirroring the reference's
|
|
916
|
+
// "Build · <model> · <mode>" line under its own input box.
|
|
917
|
+
(0, tui_js_1.setStatusLine)([provider.name, runtimeConfig.model, permissionLevel].filter(Boolean).join(' · '));
|
|
918
|
+
(0, tui_js_1.initTui)();
|
|
919
|
+
if (activeChatId)
|
|
920
|
+
(0, tui_js_1.setChatId)(activeChatId);
|
|
921
|
+
(0, tui_js_1.startInput)();
|
|
922
|
+
// Route permission confirmations through the TUI's own raw-mode-safe
|
|
923
|
+
// prompt instead of a plain readline.Interface, which forces stdin out
|
|
924
|
+
// of raw mode and fights the TUI's own input handler (see tui.ts).
|
|
925
|
+
(0, permissions_js_1.setConfirmHandler)(tui_js_1.askConfirm);
|
|
926
|
+
// Must come after initTui() — writeOutput() assumes the "cursor at base
|
|
927
|
+
// row" invariant initTui() establishes; calling it any earlier corrupts
|
|
928
|
+
// that baseline.
|
|
929
|
+
if (activeChatHistory.length > 0) {
|
|
930
|
+
(0, tui_js_1.writeOutput)(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(' Continuing session with ' + Math.floor(activeChatHistory.length / 2) + ' prior turns.'));
|
|
931
|
+
}
|
|
932
|
+
// Buffer for :btw and :stop typed during the agent loop
|
|
933
|
+
let pendingBtw = null;
|
|
934
|
+
let abortController = null;
|
|
935
|
+
(0, tui_js_1.setCallbacks)({
|
|
936
|
+
onEnter(line) {
|
|
937
|
+
processLine(line);
|
|
938
|
+
},
|
|
939
|
+
onStop() {
|
|
940
|
+
if (abortController)
|
|
941
|
+
abortController?.abort();
|
|
942
|
+
},
|
|
943
|
+
});
|
|
944
|
+
let tuiInputHistory = [];
|
|
945
|
+
async function processLine(input) {
|
|
946
|
+
const replCtx = {
|
|
947
|
+
rl: null,
|
|
948
|
+
ctx, display: tuiDisplay,
|
|
949
|
+
providerConfig: { model: resolved.model, apiKey: runtimeConfig.apiKey, baseUrl: runtimeConfig.baseUrl ?? undefined },
|
|
950
|
+
permissions, cumulative,
|
|
951
|
+
chatState: { projectRoot, activeChatId, activeChatHistory, activeChatTitle, noSession },
|
|
952
|
+
sessionPath,
|
|
953
|
+
healthTracker,
|
|
1095
954
|
};
|
|
1096
|
-
//
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
955
|
+
// Check for REPL commands
|
|
956
|
+
const cmdResult = await handleReplCommand(input, replCtx);
|
|
957
|
+
if (cmdResult.handled) {
|
|
958
|
+
if (cmdResult.newChatId !== undefined)
|
|
959
|
+
activeChatId = cmdResult.newChatId;
|
|
960
|
+
if (cmdResult.newHistory !== undefined)
|
|
961
|
+
activeChatHistory = cmdResult.newHistory;
|
|
962
|
+
if (cmdResult.newTitle !== undefined)
|
|
963
|
+
activeChatTitle = cmdResult.newTitle;
|
|
964
|
+
if (activeChatId)
|
|
965
|
+
(0, tui_js_1.setChatId)(activeChatId);
|
|
966
|
+
return;
|
|
967
|
+
}
|
|
968
|
+
// Run task
|
|
969
|
+
let result;
|
|
970
|
+
abortController = new AbortController();
|
|
971
|
+
const abortSignal = abortController.signal;
|
|
972
|
+
try {
|
|
973
|
+
const currentProvider = buildProvider(tuiDisplay);
|
|
974
|
+
pendingBtw = null;
|
|
975
|
+
const doVerify = argv.verify === true || !!fileConfig.verify;
|
|
976
|
+
if (doVerify) {
|
|
977
|
+
const { runWithVerification } = await Promise.resolve().then(() => __importStar(require('../verify/index.js')));
|
|
978
|
+
const maxRetries = cliMaxVerifyRetries ?? fileConfig.maxVerifyRetries ?? defaults_js_1.DEFAULTS.maxVerifyRetries;
|
|
979
|
+
const testCommand = cliTestCommand ?? fileConfig.testCommand;
|
|
980
|
+
const wrapperResult = await runWithVerification({
|
|
981
|
+
loopOpts: {
|
|
982
|
+
provider: currentProvider, task: input,
|
|
983
|
+
context: ctx, permissions, display: tuiDisplay,
|
|
984
|
+
initialHistory: activeChatHistory,
|
|
985
|
+
maxTurns: resolved.maxTurns,
|
|
986
|
+
abortSignal,
|
|
987
|
+
spawnConfig: {
|
|
988
|
+
apiKey: runtimeConfig.apiKey,
|
|
989
|
+
baseUrl: runtimeConfig.baseUrl ?? undefined,
|
|
990
|
+
},
|
|
991
|
+
sessionPath,
|
|
992
|
+
healthTracker,
|
|
993
|
+
},
|
|
994
|
+
config: { enabled: true, maxRetries, testCommand },
|
|
995
|
+
projectRoot: ctx.root,
|
|
996
|
+
display: tuiDisplay,
|
|
997
|
+
});
|
|
998
|
+
result = wrapperResult.loopResult;
|
|
1103
999
|
}
|
|
1104
1000
|
else {
|
|
1105
|
-
|
|
1106
|
-
|
|
1001
|
+
result = await (0, loop_js_1.runAgentLoop)({
|
|
1002
|
+
provider: currentProvider, task: input,
|
|
1003
|
+
context: ctx, permissions, display: tuiDisplay,
|
|
1004
|
+
initialHistory: activeChatHistory,
|
|
1005
|
+
maxTurns: resolved.maxTurns,
|
|
1006
|
+
spawnConfig: {
|
|
1007
|
+
apiKey: runtimeConfig.apiKey,
|
|
1008
|
+
baseUrl: runtimeConfig.baseUrl ?? undefined,
|
|
1009
|
+
},
|
|
1010
|
+
sessionPath,
|
|
1011
|
+
abortSignal,
|
|
1012
|
+
healthTracker,
|
|
1013
|
+
});
|
|
1107
1014
|
}
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
}
|
|
1015
|
+
}
|
|
1016
|
+
catch (err) {
|
|
1017
|
+
const msg = err instanceof Error ? (err.stack || err.message) : String(err);
|
|
1018
|
+
(0, tui_js_1.writeOutput)(chalk_1.default.hex('#b15439')(' ✗ Unhandled error: ' + msg));
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
// Check if task was cancelled by user
|
|
1022
|
+
if (abortController?.signal.aborted && !result.success) {
|
|
1023
|
+
(0, tui_js_1.writeOutput)(chalk_1.default.hex('#d4903a')(' ⏹ Task cancelled.'));
|
|
1024
|
+
// Don't record episode for cancelled tasks
|
|
1025
|
+
return;
|
|
1026
|
+
}
|
|
1027
|
+
// Update stay-active history
|
|
1028
|
+
try {
|
|
1029
|
+
activeChatHistory = result.history;
|
|
1030
|
+
// Persist session
|
|
1031
|
+
if (activeChatId && !noSession) {
|
|
1032
|
+
await session_store_js_1.sessionStore.upsertSession(projectRoot, activeChatId, activeChatHistory, activeChatTitle);
|
|
1033
|
+
}
|
|
1034
|
+
{
|
|
1035
|
+
const { recordEpisode } = await Promise.resolve().then(() => __importStar(require('../dream/episode.js')));
|
|
1036
|
+
recordEpisode(ctx.root, {
|
|
1037
|
+
task: input,
|
|
1038
|
+
model: runtimeConfig.model ?? resolved.model ?? 'unknown',
|
|
1039
|
+
success: result.success,
|
|
1040
|
+
tokens: (result.usage?.inputTokens ?? 0) + (result.usage?.outputTokens ?? 0),
|
|
1041
|
+
durationMs: 0,
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
cumulative.turns += result.turns;
|
|
1045
|
+
cumulative.toolCalls += result.toolCallCount;
|
|
1046
|
+
cumulative.inputTokens += result.usage.inputTokens;
|
|
1047
|
+
cumulative.outputTokens += result.usage.outputTokens;
|
|
1048
|
+
cumulative.costUsd += result.costUsd;
|
|
1049
|
+
if (result.success) {
|
|
1050
|
+
tuiDisplay.summary(result.summary, result.turns, result.toolCallCount);
|
|
1051
|
+
printUsageFooter(tuiDisplay, result.usage, result.costUsd);
|
|
1052
|
+
if (speakEnabled)
|
|
1053
|
+
await speakSummary(result.summary);
|
|
1054
|
+
}
|
|
1055
|
+
else {
|
|
1056
|
+
tuiDisplay.error(result.summary);
|
|
1057
|
+
}
|
|
1058
|
+
// Process any :btw that was typed during the loop
|
|
1059
|
+
if (pendingBtw) {
|
|
1060
|
+
const q = pendingBtw;
|
|
1061
|
+
pendingBtw = null;
|
|
1062
|
+
abortController = new AbortController();
|
|
1063
|
+
const { runBtwQuery, renderBtwAnswer } = await Promise.resolve().then(() => __importStar(require('../repl/side-channel.js')));
|
|
1064
|
+
(0, tui_js_1.writeOutput)(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(' Side question: "' + q + '"'));
|
|
1065
|
+
const btwResult = await runBtwQuery(q, buildProvider(tuiDisplay), ctx);
|
|
1066
|
+
(0, tui_js_1.writeOutput)(renderBtwAnswer(btwResult.answer, btwResult.tokens));
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
catch (err) {
|
|
1070
|
+
const msg = err instanceof Error ? (err.stack || err.message) : String(err);
|
|
1071
|
+
(0, tui_js_1.writeOutput)(chalk_1.default.hex('#b15439')(' \u2717 Unhandled error after task completed: ' + msg));
|
|
1072
|
+
}
|
|
1114
1073
|
}
|
|
1074
|
+
(0, tui_js_1.writeOutput)(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(' Type a task, or :help for commands.'));
|
|
1075
|
+
}
|
|
1076
|
+
/**
|
|
1077
|
+
* Best-effort map from a model id to the env-var name its key lives under.
|
|
1078
|
+
* Delegates to the factory helper — custom providers from .aura.json are
|
|
1079
|
+
* covered because registerCustomProviders runs at startup.
|
|
1080
|
+
*/
|
|
1081
|
+
function envNameForModel(model) {
|
|
1082
|
+
return (0, factory_js_1.apiKeyEnvVarForModel)(model);
|
|
1115
1083
|
}
|
|
1116
1084
|
function trySetModel(c, newModel) {
|
|
1117
1085
|
const prevModel = runtimeConfig.model;
|
|
1086
|
+
const prevResolved = resolved.model;
|
|
1087
|
+
const prevApiKey = runtimeConfig.apiKey;
|
|
1118
1088
|
const prevBaseUrl = runtimeConfig.baseUrl;
|
|
1089
|
+
const prevResolvedBaseUrl = resolved.baseUrl;
|
|
1119
1090
|
runtimeConfig.model = newModel;
|
|
1120
|
-
|
|
1091
|
+
resolved.model = newModel; // buildProvider reads resolved.model — keep in sync
|
|
1092
|
+
const crossing = (0, model_select_js_1.isProviderChange)(prevModel ?? prevResolved, newModel);
|
|
1093
|
+
if (crossing) {
|
|
1094
|
+
// The old provider's key/baseUrl must not follow the model across a
|
|
1095
|
+
// provider boundary — createProvider gives config.apiKey priority over
|
|
1096
|
+
// the per-provider env fallback, so a stale key would be sent to the
|
|
1097
|
+
// NEW provider's endpoint. Clear both so the factory falls through to
|
|
1098
|
+
// the new provider's own getApiKey()/default-endpoint chain.
|
|
1099
|
+
runtimeConfig.apiKey = undefined;
|
|
1100
|
+
runtimeConfig.baseUrl = undefined;
|
|
1101
|
+
resolved.baseUrl = undefined;
|
|
1102
|
+
}
|
|
1121
1103
|
try {
|
|
1122
1104
|
const test = buildProvider(c.display);
|
|
1123
1105
|
c.providerConfig.model = newModel;
|
|
1124
|
-
|
|
1106
|
+
if (crossing) {
|
|
1107
|
+
c.providerConfig.apiKey = undefined;
|
|
1108
|
+
c.providerConfig.baseUrl = undefined;
|
|
1109
|
+
}
|
|
1125
1110
|
console.log(chalk_1.default.hex('#5a9e6e')(` ✓ Switched to ${test.name} · ${newModel}`));
|
|
1111
|
+
// Remember the choice for the next session. The saved baseUrl belongs to
|
|
1112
|
+
// the wizard-configured model — keep it only when switching back to that
|
|
1113
|
+
// model, otherwise the factory's per-provider default applies.
|
|
1114
|
+
try {
|
|
1115
|
+
(0, global_config_js_1.saveGlobalConfig)({
|
|
1116
|
+
provider: globalCfg?.provider ?? test.name,
|
|
1117
|
+
// loadGlobalConfig treats an empty apiKeyEnv as "not configured", so
|
|
1118
|
+
// resolve from the NEW model's provider — never inherit the old
|
|
1119
|
+
// provider's env name across a provider change (a silently wrong
|
|
1120
|
+
// apiKeyEnv on disk pairs the wrong key with the model on the next
|
|
1121
|
+
// startup).
|
|
1122
|
+
apiKeyEnv: (0, model_select_js_1.apiKeyEnvForModelSwitch)(newModel, prevModel ?? prevResolved, globalCfg?.apiKeyEnv),
|
|
1123
|
+
defaultModel: newModel,
|
|
1124
|
+
baseUrl: savedProvider && newModel === savedProvider.model ? savedProvider.baseUrl : undefined,
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
catch { /* persistence is best-effort; the switch itself succeeded */ }
|
|
1126
1128
|
return { ok: true };
|
|
1127
1129
|
}
|
|
1128
1130
|
catch (e) {
|
|
1129
1131
|
runtimeConfig.model = prevModel; // rollback on error
|
|
1132
|
+
resolved.model = prevResolved;
|
|
1133
|
+
runtimeConfig.apiKey = prevApiKey;
|
|
1130
1134
|
runtimeConfig.baseUrl = prevBaseUrl;
|
|
1135
|
+
resolved.baseUrl = prevResolvedBaseUrl;
|
|
1131
1136
|
return { ok: false, err: String(e) };
|
|
1132
1137
|
}
|
|
1133
1138
|
}
|
|
@@ -1136,134 +1141,443 @@ function trySetModel(c, newModel) {
|
|
|
1136
1141
|
* lets the user pick by number or type a custom model ID.
|
|
1137
1142
|
*/
|
|
1138
1143
|
async function showModelSelector(c) {
|
|
1139
|
-
|
|
1140
|
-
//
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
console.log(chalk_1.default.hex('#cc785c').bold('\n Model Selector\n'));
|
|
1155
|
-
for (const e of entries) {
|
|
1156
|
-
console.log(e.label);
|
|
1157
|
-
}
|
|
1158
|
-
console.log(chalk_1.default.hex('#4e3d30')(`\n Current: ${runtimeConfig.model}`));
|
|
1159
|
-
console.log(chalk_1.default.hex('#4e3d30')(' Type a number, model ID, or press Enter to cancel:\n'));
|
|
1160
|
-
return new Promise(resolve => {
|
|
1161
|
-
c.rl.question(chalk_1.default.hex('#cc785c')(' ▸ '), (answer) => {
|
|
1162
|
-
const choice = answer.trim();
|
|
1163
|
-
if (!choice) {
|
|
1164
|
-
console.log(chalk_1.default.hex('#4e3d30')(' Cancelled.\n'));
|
|
1165
|
-
resolve();
|
|
1166
|
-
return;
|
|
1144
|
+
// Same stdin-collision fix as :provider (see handleReplCommand): the TUI's
|
|
1145
|
+
// raw-mode stdin handler competes with any prompt for keystrokes, so stop
|
|
1146
|
+
// TUI input entirely (this also hides the main input box), let a plain
|
|
1147
|
+
// readline own stdin, then restart TUI input once the selector exits.
|
|
1148
|
+
const wasInputActive = tui_js_1.inputActive;
|
|
1149
|
+
if (wasInputActive)
|
|
1150
|
+
(0, tui_js_1.stopInput)();
|
|
1151
|
+
try {
|
|
1152
|
+
// Grouped list where section headers are display-only: they carry no
|
|
1153
|
+
// number, so numbering is gap-free and a header can never be selected.
|
|
1154
|
+
const rows = (0, model_select_js_1.buildModelRows)((0, factory_js_1.getAllModels)());
|
|
1155
|
+
console.log(chalk_1.default.hex('#cc785c').bold('\n Model Selector\n'));
|
|
1156
|
+
for (const r of rows) {
|
|
1157
|
+
if (r.kind === 'header') {
|
|
1158
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX).bold(` ── ${r.provider} ──`));
|
|
1167
1159
|
}
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1160
|
+
else {
|
|
1161
|
+
console.log(` ${chalk_1.default.hex('#cc785c')(String(r.num).padStart(2))}. ${chalk_1.default.hex(diamond_js_1.TEXT_HEX)(r.name.padEnd(30))} ${chalk_1.default.hex(diamond_js_1.FAINT_HEX)(r.speed)}`);
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(`\n Current: ${runtimeConfig.model}`));
|
|
1165
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(' Type a number, model ID, or press Enter to cancel:\n'));
|
|
1166
|
+
const answer = await new Promise(resolve => {
|
|
1167
|
+
const promptRl = c.rl;
|
|
1168
|
+
if (!promptRl) {
|
|
1169
|
+
// TUI mode: TUI input is stopped above, so a temporary readline can
|
|
1170
|
+
// own stdin without the two-readers-one-stream conflict.
|
|
1171
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1172
|
+
rl.question(chalk_1.default.hex('#cc785c')(' ▸ '), ans => { rl.close(); resolve(ans); });
|
|
1179
1173
|
return;
|
|
1180
1174
|
}
|
|
1181
|
-
|
|
1182
|
-
trySetModel(c, choice);
|
|
1183
|
-
resolve();
|
|
1175
|
+
promptRl.question(chalk_1.default.hex('#cc785c')(' ▸ '), resolve);
|
|
1184
1176
|
});
|
|
1185
|
-
|
|
1177
|
+
const choice = answer.trim();
|
|
1178
|
+
if (!choice) {
|
|
1179
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(' Cancelled.\n'));
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1182
|
+
// Try as a number — every listed number is a real, selectable model
|
|
1183
|
+
const num = parseInt(choice, 10);
|
|
1184
|
+
if (!isNaN(num) && num >= 1 && num <= (0, model_select_js_1.modelCount)(rows)) {
|
|
1185
|
+
trySetModel(c, (0, model_select_js_1.modelIdForNumber)(rows, num));
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
1188
|
+
// Treat as a raw model ID
|
|
1189
|
+
trySetModel(c, choice);
|
|
1190
|
+
}
|
|
1191
|
+
finally {
|
|
1192
|
+
if (wasInputActive)
|
|
1193
|
+
(0, tui_js_1.startInput)();
|
|
1194
|
+
}
|
|
1186
1195
|
}
|
|
1187
1196
|
async function handleReplCommand(input, c) {
|
|
1188
1197
|
const unhandled = { handled: false };
|
|
1198
|
+
// ── :q — Task queue (with subcommands, keep bare :q as quit) ─────────────
|
|
1199
|
+
if (input.startsWith(':q ')) {
|
|
1200
|
+
const sub = input.slice(3).trimStart();
|
|
1201
|
+
const { addToQueue, loadQueue, removeFromQueue, clearQueue, runQueueItem, formatQueue } = await Promise.resolve().then(() => __importStar(require('../repl/queue.js')));
|
|
1202
|
+
if (sub.startsWith('add ')) {
|
|
1203
|
+
const prompt = sub.slice(4).trim();
|
|
1204
|
+
if (!prompt) {
|
|
1205
|
+
c.display.warning('Usage: :q add <prompt> -- add a task to the queue.');
|
|
1206
|
+
return { handled: true };
|
|
1207
|
+
}
|
|
1208
|
+
const item = addToQueue(prompt);
|
|
1209
|
+
console.log(chalk_1.default.hex('#5a9e6e')(`\n ✓ Queued #${loadQueue().length}: "${prompt.slice(0, 60)}"\n`));
|
|
1210
|
+
return { handled: true };
|
|
1211
|
+
}
|
|
1212
|
+
if (sub === 'list') {
|
|
1213
|
+
const items = loadQueue();
|
|
1214
|
+
console.log(formatQueue(items));
|
|
1215
|
+
return { handled: true };
|
|
1216
|
+
}
|
|
1217
|
+
if (sub.startsWith('run ')) {
|
|
1218
|
+
const n = parseInt(sub.slice(4).trim(), 10);
|
|
1219
|
+
if (isNaN(n) || n < 1) {
|
|
1220
|
+
c.display.warning('Usage: :q run <number> — run the task at that position (see :q list).');
|
|
1221
|
+
return { handled: true };
|
|
1222
|
+
}
|
|
1223
|
+
const items = loadQueue();
|
|
1224
|
+
if (n > items.length) {
|
|
1225
|
+
c.display.warning(`Queue only has ${items.length} item(s).`);
|
|
1226
|
+
return { handled: true };
|
|
1227
|
+
}
|
|
1228
|
+
c.display.agentThinking();
|
|
1229
|
+
const result = await runQueueItem(n - 1, buildProvider(c.display), c.ctx, c.permissions, c.display);
|
|
1230
|
+
if (!result) {
|
|
1231
|
+
c.display.warning('Could not run that item.');
|
|
1232
|
+
return { handled: true };
|
|
1233
|
+
}
|
|
1234
|
+
c.display.success(`Queue item #${n}: ${result.success ? 'done' : 'failed'}`);
|
|
1235
|
+
if (result.output) {
|
|
1236
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_HEX)(` ${result.output.slice(0, 240)}`));
|
|
1237
|
+
}
|
|
1238
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(` ${result.turns} turn(s) · ${result.toolCalls} tool call(s).\n`));
|
|
1239
|
+
return { handled: true };
|
|
1240
|
+
}
|
|
1241
|
+
if (sub.startsWith('drop ')) {
|
|
1242
|
+
const n = parseInt(sub.slice(5).trim(), 10);
|
|
1243
|
+
if (isNaN(n) || n < 1) {
|
|
1244
|
+
c.display.warning('Usage: :q drop <number> — remove the task at that position.');
|
|
1245
|
+
return { handled: true };
|
|
1246
|
+
}
|
|
1247
|
+
const removed = removeFromQueue(n - 1);
|
|
1248
|
+
if (!removed) {
|
|
1249
|
+
c.display.warning(`No item at position ${n}.`);
|
|
1250
|
+
return { handled: true };
|
|
1251
|
+
}
|
|
1252
|
+
console.log(chalk_1.default.hex('#5a9e6e')(`\n ✓ Dropped #${n}: "${removed.prompt.slice(0, 60)}"\n`));
|
|
1253
|
+
return { handled: true };
|
|
1254
|
+
}
|
|
1255
|
+
if (sub === 'clear') {
|
|
1256
|
+
const count = loadQueue().length;
|
|
1257
|
+
if (count === 0) {
|
|
1258
|
+
c.display.warning('Queue is already empty.');
|
|
1259
|
+
return { handled: true };
|
|
1260
|
+
}
|
|
1261
|
+
clearQueue();
|
|
1262
|
+
console.log(chalk_1.default.hex('#5a9e6e')(`\n ✓ Queue cleared (${count} item(s) removed).\n`));
|
|
1263
|
+
return { handled: true };
|
|
1264
|
+
}
|
|
1265
|
+
c.display.warning('Usage: :q add <prompt> | :q list | :q run <n> | :q drop <n> | :q clear');
|
|
1266
|
+
return { handled: true };
|
|
1267
|
+
}
|
|
1189
1268
|
if (input === ':quit' || input === ':q' || input === '/exit') {
|
|
1190
1269
|
process.exit(0);
|
|
1191
1270
|
}
|
|
1271
|
+
if (input === ':speak') {
|
|
1272
|
+
speakEnabled = !speakEnabled;
|
|
1273
|
+
console.log(chalk_1.default.hex(speakEnabled ? '#5a9e6e' : '#a68a2a')(` 🔊 Voice replies ${speakEnabled ? 'ON — Aura will read its answers aloud' : 'OFF'}.\n`));
|
|
1274
|
+
return { handled: true };
|
|
1275
|
+
}
|
|
1276
|
+
// :approve — flip the session into auto-approve (no per-command y/N prompt).
|
|
1277
|
+
// :approve → toggle auto ⇄ normal
|
|
1278
|
+
// :approve all → auto (approve everything for this session)
|
|
1279
|
+
// :approve off → back to normal (confirm destructive commands again)
|
|
1280
|
+
// Dangerous commands are still blocked either way.
|
|
1281
|
+
if (input === ':approve' || input === ':approve all' || input === ':approve off') {
|
|
1282
|
+
const cur = c.permissions.getLevel();
|
|
1283
|
+
let next;
|
|
1284
|
+
if (input === ':approve off')
|
|
1285
|
+
next = 'normal';
|
|
1286
|
+
else if (input === ':approve all')
|
|
1287
|
+
next = 'auto';
|
|
1288
|
+
else
|
|
1289
|
+
next = cur === 'auto' ? 'normal' : 'auto';
|
|
1290
|
+
c.permissions.setLevel(next);
|
|
1291
|
+
if (next === 'auto') {
|
|
1292
|
+
console.log(chalk_1.default.hex('#d4903a')(' ✅ Auto-approve ON — commands run without asking (dangerous ones still blocked). `:approve off` to re-enable prompts.\n'));
|
|
1293
|
+
}
|
|
1294
|
+
else {
|
|
1295
|
+
console.log(chalk_1.default.hex('#5a9e6e')(' 🔒 Auto-approve OFF — destructive commands will ask for confirmation again.\n'));
|
|
1296
|
+
}
|
|
1297
|
+
return { handled: true };
|
|
1298
|
+
}
|
|
1299
|
+
if (input === ':dream' || input === ':dream full') {
|
|
1300
|
+
const full = input === ':dream full';
|
|
1301
|
+
const { runDream } = await Promise.resolve().then(() => __importStar(require('../dream/dream.js')));
|
|
1302
|
+
c.display.agentThinking();
|
|
1303
|
+
const res = await runDream({ projectRoot: c.ctx.root, provider: buildProvider(c.display), full });
|
|
1304
|
+
if (res.skipped) {
|
|
1305
|
+
c.display.warning(res.providerError
|
|
1306
|
+
? `Dream skipped (episodes preserved): ${res.providerError}`
|
|
1307
|
+
: (res.reason ?? 'Nothing to consolidate.'));
|
|
1308
|
+
}
|
|
1309
|
+
else {
|
|
1310
|
+
c.display.success(`Dream written: ${res.path} (${res.episodeCount} episodes${full ? ', full run' : ''})`);
|
|
1311
|
+
if (res.reconciled)
|
|
1312
|
+
c.display.success('Reconciliation also ran (>=3 dreams exist) -> dreams/.reconciled.md');
|
|
1313
|
+
}
|
|
1314
|
+
return { handled: true };
|
|
1315
|
+
}
|
|
1316
|
+
if (input.startsWith(':research ') || input === ':research') {
|
|
1317
|
+
const topic = input.slice(':research '.length).trim();
|
|
1318
|
+
if (!topic) {
|
|
1319
|
+
c.display.warning('Usage: :research <topic> -- runs a multi-step research pass and saves to research/*.md.');
|
|
1320
|
+
return { handled: true };
|
|
1321
|
+
}
|
|
1322
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(`\n Researching "${topic}"…\n`));
|
|
1323
|
+
try {
|
|
1324
|
+
const { runResearch } = await Promise.resolve().then(() => __importStar(require('../research/research.js')));
|
|
1325
|
+
const res = await runResearch({
|
|
1326
|
+
projectRoot: c.ctx.root,
|
|
1327
|
+
topic,
|
|
1328
|
+
provider: buildProvider(c.display),
|
|
1329
|
+
context: c.ctx,
|
|
1330
|
+
permissions: c.permissions,
|
|
1331
|
+
display: c.display,
|
|
1332
|
+
});
|
|
1333
|
+
console.log(chalk_1.default.hex('#5a9e6e')(` ✓ Research written: ${res.path}`));
|
|
1334
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` ${res.turns} turn(s) · ${res.toolCalls} tool call(s).\n`));
|
|
1335
|
+
}
|
|
1336
|
+
catch (e) {
|
|
1337
|
+
console.log(chalk_1.default.hex('#b15439')(` ✗ ${String(e)}\n`));
|
|
1338
|
+
}
|
|
1339
|
+
return { handled: true };
|
|
1340
|
+
}
|
|
1341
|
+
if (input === ':confessions') {
|
|
1342
|
+
const { listConfessions } = await Promise.resolve().then(() => __importStar(require('../agent/confess.js')));
|
|
1343
|
+
const confs = listConfessions();
|
|
1344
|
+
if (confs.length === 0) {
|
|
1345
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('\n No confessions yet. Run :confess after a high-token episode.\n'));
|
|
1346
|
+
}
|
|
1347
|
+
else {
|
|
1348
|
+
console.log(chalk_1.default.hex('#cc785c').bold(`\n ${confs.length} confession(s):\n`));
|
|
1349
|
+
for (const c of confs) {
|
|
1350
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` ${c.file}`));
|
|
1351
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(` ${c.tokens.toLocaleString()} tokens burned → ${c.lesson.slice(0, 100)}`));
|
|
1352
|
+
}
|
|
1353
|
+
console.log('');
|
|
1354
|
+
}
|
|
1355
|
+
return { handled: true };
|
|
1356
|
+
}
|
|
1357
|
+
if (input === ':confess') {
|
|
1358
|
+
const { runConfession, findEpisodeToConfess } = await Promise.resolve().then(() => __importStar(require('../agent/confess.js')));
|
|
1359
|
+
const targetEp = findEpisodeToConfess(c.ctx.root);
|
|
1360
|
+
if (!targetEp) {
|
|
1361
|
+
console.log(chalk_1.default.hex('#cc9e5c')('\n No anomalous episode found. Confession is fully automatic — the system alone decides what to confess.\n'));
|
|
1362
|
+
return { handled: true };
|
|
1363
|
+
}
|
|
1364
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(`\n 🙏 Confessing episode ${targetEp.id.slice(0, 8)}… — ${targetEp.task.slice(0, 60)} (${(targetEp.tokens / 1e6).toFixed(1)}M tok)\n`));
|
|
1365
|
+
try {
|
|
1366
|
+
// Use a different model than the one that made the mistake
|
|
1367
|
+
const confessorModel = targetEp.model.startsWith('deepseek') ? 'glm-5.2' : 'deepseek/deepseek-chat';
|
|
1368
|
+
const { createProvider } = await Promise.resolve().then(() => __importStar(require('../providers/factory.js')));
|
|
1369
|
+
const provider = createProvider({ model: confessorModel });
|
|
1370
|
+
const result = await runConfession({
|
|
1371
|
+
projectRoot: c.ctx.root,
|
|
1372
|
+
episodeId: targetEp.id,
|
|
1373
|
+
provider,
|
|
1374
|
+
});
|
|
1375
|
+
console.log(chalk_1.default.hex('#5a9e6e')(` ✓ Confession written: ${result.path}`));
|
|
1376
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` Tokens burned: ${result.tokensBurned.toLocaleString()} | Confession cost: ${result.tokensSpent.toLocaleString()} (${confessorModel})`));
|
|
1377
|
+
console.log(chalk_1.default.hex('#cc9e6c')(' Permanent lesson:'));
|
|
1378
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_HEX)(` "${result.lesson}"\n`));
|
|
1379
|
+
}
|
|
1380
|
+
catch (e) {
|
|
1381
|
+
console.log(chalk_1.default.hex('#b15439')(` ✗ ${String(e)}\n`));
|
|
1382
|
+
}
|
|
1383
|
+
return { handled: true };
|
|
1384
|
+
}
|
|
1385
|
+
if (input === ':rem') {
|
|
1386
|
+
const { getReconciledOrLatest } = await Promise.resolve().then(() => __importStar(require('../dream/dream.js')));
|
|
1387
|
+
const res = getReconciledOrLatest(c.ctx.root);
|
|
1388
|
+
if (!res) {
|
|
1389
|
+
c.display.warning('No dreams yet. Run :dream first.');
|
|
1390
|
+
}
|
|
1391
|
+
else {
|
|
1392
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(`\n ${res.isReconciled ? 'Reconciled projection' : 'Latest dream (not yet reconciled)'}:\n`));
|
|
1393
|
+
console.log(res.content);
|
|
1394
|
+
}
|
|
1395
|
+
return { handled: true };
|
|
1396
|
+
}
|
|
1397
|
+
// ── :mine — Baby Ruby experience mining (src/mining/). The base pass is
|
|
1398
|
+
// zero-LLM (pure clustering over episodes/*.json); --refine additionally
|
|
1399
|
+
// runs Papa Ruby, one local-model call per qualifying concept, appending
|
|
1400
|
+
// accepted lessons to training-data/<date>.jsonl.
|
|
1401
|
+
if (input === ':mine' || input === ':mine --refine') {
|
|
1402
|
+
const refine = input.endsWith('--refine');
|
|
1403
|
+
const { mineExperience } = await Promise.resolve().then(() => __importStar(require('../mining/extract.js')));
|
|
1404
|
+
const mined = await mineExperience(c.ctx.root);
|
|
1405
|
+
if (mined.episodeCount === 0) {
|
|
1406
|
+
c.display.warning('No episodes to mine yet — run some tasks first.');
|
|
1407
|
+
return { handled: true };
|
|
1408
|
+
}
|
|
1409
|
+
console.log(chalk_1.default.hex('#cc785c').bold(`\n Mined ${mined.concepts.length} concept(s) from ${mined.episodeCount} episode(s) (${mined.unclustered} unclustered):\n`));
|
|
1410
|
+
for (const con of mined.concepts.slice(0, 15)) {
|
|
1411
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` ${con.concept}`) + chalk_1.default.hex(diamond_js_1.FAINT_HEX)(` (${con.category} · ×${con.frequency} · conf ${con.confidence} · depth ${con.depth})`));
|
|
1412
|
+
if (con.keywords.length > 0)
|
|
1413
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(` keywords: ${con.keywords.join(', ')}`));
|
|
1414
|
+
}
|
|
1415
|
+
if (mined.concepts.length > 15) {
|
|
1416
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(` … and ${mined.concepts.length - 15} more.`));
|
|
1417
|
+
}
|
|
1418
|
+
if (refine) {
|
|
1419
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('\n Refining with the local Ruby model (Papa Ruby)…'));
|
|
1420
|
+
const { refineConcepts } = await Promise.resolve().then(() => __importStar(require('../mining/refine.js')));
|
|
1421
|
+
const res = await refineConcepts({ projectRoot: c.ctx.root, concepts: mined.concepts });
|
|
1422
|
+
if (res.accepted.length > 0) {
|
|
1423
|
+
console.log(chalk_1.default.hex('#5a9e6e')(` ✓ ${res.accepted.length} training example(s) appended: ${res.outputPath}`));
|
|
1424
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(` ${res.rejected} rejected, ${res.skipped} below the confidence/frequency gate.\n`));
|
|
1425
|
+
}
|
|
1426
|
+
else {
|
|
1427
|
+
c.display.warning(`No concepts survived refinement — ${res.rejected} rejected, ${res.skipped} below the confidence/frequency gate.`);
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
else if (mined.concepts.length > 0) {
|
|
1431
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)('\n :mine --refine judges these with the local Ruby model and writes training-data/*.jsonl\n'));
|
|
1432
|
+
}
|
|
1433
|
+
return { handled: true };
|
|
1434
|
+
}
|
|
1435
|
+
if (input === ':doctor' || input.startsWith(':doctor')) {
|
|
1436
|
+
const fix = input.includes('--fix');
|
|
1437
|
+
const offline = input.includes('--offline');
|
|
1438
|
+
c.display.agentThinking();
|
|
1439
|
+
const report = await (0, index_js_5.runDoctor)({ projectRoot: c.ctx.root, fix, offline });
|
|
1440
|
+
if (typeof tui_js_1.writeOutput === 'function') {
|
|
1441
|
+
(0, tui_js_1.writeOutput)((0, index_js_5.formatDoctorReport)(report));
|
|
1442
|
+
}
|
|
1443
|
+
else {
|
|
1444
|
+
console.log((0, index_js_5.formatDoctorReport)(report));
|
|
1445
|
+
}
|
|
1446
|
+
return { handled: true };
|
|
1447
|
+
}
|
|
1448
|
+
if (input.startsWith(':machina ') || input === ':machina') {
|
|
1449
|
+
const machinaTask = input.slice(':machina '.length).trim();
|
|
1450
|
+
if (!machinaTask) {
|
|
1451
|
+
c.display.warning('Usage: :machina <task> -- runs the task with self-verification (file/test checks + auto-retry).');
|
|
1452
|
+
return { handled: true };
|
|
1453
|
+
}
|
|
1454
|
+
const { runWithVerification } = await Promise.resolve().then(() => __importStar(require('../verify/index.js')));
|
|
1455
|
+
const maxRetries = cliMaxVerifyRetries ?? fileConfig.maxVerifyRetries ?? defaults_js_1.DEFAULTS.maxVerifyRetries;
|
|
1456
|
+
const testCommand = cliTestCommand ?? fileConfig.testCommand;
|
|
1457
|
+
const wrapperResult = await runWithVerification({
|
|
1458
|
+
loopOpts: {
|
|
1459
|
+
provider: buildProvider(c.display), task: machinaTask,
|
|
1460
|
+
context: c.ctx, permissions: c.permissions, display: c.display,
|
|
1461
|
+
initialHistory: c.chatState.activeChatHistory,
|
|
1462
|
+
maxTurns: resolved.maxTurns,
|
|
1463
|
+
spawnConfig: {
|
|
1464
|
+
apiKey: c.providerConfig.apiKey,
|
|
1465
|
+
baseUrl: c.providerConfig.baseUrl,
|
|
1466
|
+
},
|
|
1467
|
+
sessionPath: c.sessionPath,
|
|
1468
|
+
},
|
|
1469
|
+
config: { enabled: true, maxRetries, testCommand },
|
|
1470
|
+
projectRoot: c.ctx.root,
|
|
1471
|
+
display: c.display,
|
|
1472
|
+
});
|
|
1473
|
+
const mResult = wrapperResult.loopResult;
|
|
1474
|
+
if (mResult.success) {
|
|
1475
|
+
c.display.summary(mResult.summary, mResult.turns, mResult.toolCallCount);
|
|
1476
|
+
printUsageFooter(c.display, mResult.usage, mResult.costUsd);
|
|
1477
|
+
}
|
|
1478
|
+
else {
|
|
1479
|
+
c.display.error(mResult.summary);
|
|
1480
|
+
}
|
|
1481
|
+
return { handled: true, newHistory: mResult.history };
|
|
1482
|
+
}
|
|
1483
|
+
if (input.startsWith(':council ') || input === ':council') {
|
|
1484
|
+
const councilTask = input.slice(':council '.length).trim();
|
|
1485
|
+
if (!councilTask) {
|
|
1486
|
+
c.display.warning('Usage: :council <task> -- runs 2-3 read-only domain specialists in parallel, then synthesizes their reports.');
|
|
1487
|
+
return { handled: true };
|
|
1488
|
+
}
|
|
1489
|
+
const { runMixtureOfAgents } = await Promise.resolve().then(() => __importStar(require('../agent/mixture.js')));
|
|
1490
|
+
const councilResult = await runMixtureOfAgents({
|
|
1491
|
+
provider: buildProvider(c.display), task: councilTask, context: c.ctx, display: c.display,
|
|
1492
|
+
});
|
|
1493
|
+
if (councilResult.success) {
|
|
1494
|
+
c.display.summary(councilResult.summary, councilResult.turns, councilResult.toolCallCount);
|
|
1495
|
+
printUsageFooter(c.display, councilResult.usage, councilResult.costUsd);
|
|
1496
|
+
}
|
|
1497
|
+
else {
|
|
1498
|
+
c.display.error(councilResult.summary);
|
|
1499
|
+
}
|
|
1500
|
+
return { handled: true };
|
|
1501
|
+
}
|
|
1502
|
+
// ── :ecclesia — 5-agent independent research council (research/council.ts).
|
|
1503
|
+
// Distinct from :council above (mixture-of-agents over the current task):
|
|
1504
|
+
// the Ecclesia runs N agents that research a topic WITHOUT seeing each
|
|
1505
|
+
// other's findings, then one synthesis call reconciles them into a verdict.
|
|
1506
|
+
if (input.startsWith(':ecclesia ') || input === ':ecclesia') {
|
|
1507
|
+
let topic = input.slice(':ecclesia '.length).trim();
|
|
1508
|
+
if (!topic) {
|
|
1509
|
+
c.display.warning('Usage: :ecclesia <topic> [--panel <model>] [--seats <n>] -- N independent research agents (default 5) + a synthesis verdict, saved to council/*.md|.html.');
|
|
1510
|
+
return { handled: true };
|
|
1511
|
+
}
|
|
1512
|
+
let panelModel;
|
|
1513
|
+
let panelSize;
|
|
1514
|
+
const panelMatch = topic.match(/\s--panel\s+(\S+)/);
|
|
1515
|
+
if (panelMatch) {
|
|
1516
|
+
panelModel = panelMatch[1];
|
|
1517
|
+
topic = topic.replace(panelMatch[0], '').trim();
|
|
1518
|
+
}
|
|
1519
|
+
const seatsMatch = topic.match(/\s--seats\s+(\d+)/);
|
|
1520
|
+
if (seatsMatch) {
|
|
1521
|
+
panelSize = Number(seatsMatch[1]);
|
|
1522
|
+
topic = topic.replace(seatsMatch[0], '').trim();
|
|
1523
|
+
}
|
|
1524
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(`\n Convening the Ecclesia on "${topic}"…\n`));
|
|
1525
|
+
try {
|
|
1526
|
+
const { runCouncil } = await Promise.resolve().then(() => __importStar(require('../research/council.js')));
|
|
1527
|
+
const res = await runCouncil({
|
|
1528
|
+
projectRoot: c.ctx.root, topic,
|
|
1529
|
+
synthesisProvider: buildProvider(c.display),
|
|
1530
|
+
context: c.ctx, permissions: c.permissions, display: c.display,
|
|
1531
|
+
panelSize, panelModel,
|
|
1532
|
+
configuredModel: c.providerConfig.model,
|
|
1533
|
+
});
|
|
1534
|
+
console.log(chalk_1.default.hex('#5a9e6e')(` ✓ Ecclesia verdict written: ${res.path}`));
|
|
1535
|
+
console.log(chalk_1.default.hex('#5a9e6e')(` HTML: ${res.htmlPath}`));
|
|
1536
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` ${res.panelSize} seats on ${res.panelModel}.`));
|
|
1537
|
+
if (res.agentFailures > 0) {
|
|
1538
|
+
c.display.warning(`${res.agentFailures} of ${res.panelSize} panel agent(s) failed — verdict is based on the rest.`);
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
catch (e) {
|
|
1542
|
+
console.log(chalk_1.default.hex('#b15439')(` ✗ ${String(e)}\n`));
|
|
1543
|
+
}
|
|
1544
|
+
return { handled: true };
|
|
1545
|
+
}
|
|
1546
|
+
// ── :btw — Side channel question (read-only, no history) ────────────────
|
|
1547
|
+
if (input.startsWith(':btw ')) {
|
|
1548
|
+
const question = input.slice(5).trim();
|
|
1549
|
+
if (!question) {
|
|
1550
|
+
c.display.warning('Usage: :btw <question> — ask a quick side question without interrupting the current task.');
|
|
1551
|
+
return { handled: true };
|
|
1552
|
+
}
|
|
1553
|
+
const { runBtwQuery, renderBtwAnswer } = await Promise.resolve().then(() => __importStar(require('../repl/side-channel.js')));
|
|
1554
|
+
c.display.agentThinking();
|
|
1555
|
+
const result = await runBtwQuery(question, buildProvider(c.display), c.ctx);
|
|
1556
|
+
console.log(renderBtwAnswer(result.answer, result.tokens));
|
|
1557
|
+
return { handled: true };
|
|
1558
|
+
}
|
|
1192
1559
|
if (input === ':help' || input === '/help') {
|
|
1193
|
-
console.log(chalk_1.default.hex('
|
|
1194
|
-
'',
|
|
1195
|
-
' ── Session ──────────────────────────────────────',
|
|
1196
|
-
' :id Show current chat ID',
|
|
1197
|
-
' :sessions List all saved sessions',
|
|
1198
|
-
' :resume Resume the latest session',
|
|
1199
|
-
' :resume <id> Resume a specific session by ID',
|
|
1200
|
-
' :new Start a new session (fresh history)',
|
|
1201
|
-
' :history Show turn count in current session',
|
|
1202
|
-
' :clear-history Wipe conversation history (keep session ID)',
|
|
1203
|
-
' :save [title] Rename / save current session',
|
|
1204
|
-
' :delete <id> Delete a saved session',
|
|
1205
|
-
'',
|
|
1206
|
-
' ── Model / API ──────────────────────────────────',
|
|
1207
|
-
' :provider Provider setup wizard (configure provider, model, API key)',
|
|
1208
|
-
' :model Interactive model selector',
|
|
1209
|
-
' :model <id> Switch to a specific model',
|
|
1210
|
-
' :models List all available models',
|
|
1211
|
-
' :apikey <key> Set API key for current session',
|
|
1212
|
-
'',
|
|
1213
|
-
' ── Workflows ─────────────────────────────────────',
|
|
1214
|
-
' :workflows List all saved workflows',
|
|
1215
|
-
' :workflow Create & run a multi-step workflow',
|
|
1216
|
-
' <name> "step1" "step2" ...',
|
|
1217
|
-
' :resume-workflow <id> Resume a paused/failed workflow',
|
|
1218
|
-
'',
|
|
1219
|
-
' ── Context / Stats ──────────────────────────────',
|
|
1220
|
-
' :context Show loaded project context',
|
|
1221
|
-
' :graph Show codebase knowledge graph summary',
|
|
1222
|
-
' :graph refresh Extract + persist codebase graph',
|
|
1223
|
-
' :stats Show episode-level stats (completion rate, models, tokens)',
|
|
1224
|
-
' :plans List saved execution plans',
|
|
1225
|
-
' :viz, :dashboard Generate and open the memory dashboard',
|
|
1226
|
-
' :dream [experimental] Consolidate today\'s episodes into a dated dream (dreams/*.md)',
|
|
1227
|
-
' :dream full [experimental] Consolidate ALL episodes, ignoring the last-dream cutoff',
|
|
1228
|
-
' :rem [experimental] List dream files and open the most recent one',
|
|
1229
|
-
' :research <topic> Multi-step research pass, saved to research/*.md (inspired by DeerFlow)',
|
|
1230
|
-
' :council <topic> Ecclesia — 5 independent agents research the topic, synthesized into one verdict (council/*.md)',
|
|
1231
|
-
' :ruby [on|off] Toggle the Ruby Principle (default: off)',
|
|
1232
|
-
' ⚠ :machina The Abstract Agent Machine — formal model, verified against this source tree',
|
|
1233
|
-
' ⚠ :machina --html Also write the full writeup + diagram to docs/machina.html',
|
|
1234
|
-
' :design [slug|list|off] Set/list the design system auto-applied to UI builds',
|
|
1235
|
-
' /stats, /usage, :usage Show token + cost usage this session',
|
|
1236
|
-
' /clear, /reset Reset cumulative usage stats',
|
|
1237
|
-
'',
|
|
1238
|
-
' ── General ──────────────────────────────────────',
|
|
1239
|
-
' :approve all Auto-approve all tool calls (no confirmations)',
|
|
1240
|
-
' :approve normal Require confirmation for destructive ops',
|
|
1241
|
-
' :approve read-only Block all write operations',
|
|
1242
|
-
' :quit, :q, /exit Exit',
|
|
1243
|
-
'',
|
|
1244
|
-
' ⚠ = extremely high token usage — reads multiple source files and writes a careful long-form result',
|
|
1245
|
-
'',
|
|
1246
|
-
].join('\n')));
|
|
1560
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(help_data_js_1.HELP_TEXT.join('\n')));
|
|
1247
1561
|
return { handled: true };
|
|
1248
1562
|
}
|
|
1249
1563
|
// ── Session commands ─────────────────────────────────────────────────────
|
|
1250
1564
|
if (input === ':id') {
|
|
1251
1565
|
const cs = c.chatState;
|
|
1252
1566
|
if (cs.activeChatId) {
|
|
1253
|
-
console.log(chalk_1.default.hex(
|
|
1567
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(`\n Chat ID: ${chalk_1.default.hex('#cc785c')(cs.activeChatId)}`));
|
|
1254
1568
|
if (cs.activeChatTitle)
|
|
1255
|
-
console.log(chalk_1.default.hex(
|
|
1256
|
-
console.log(chalk_1.default.hex(
|
|
1569
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` Title: ${cs.activeChatTitle}`));
|
|
1570
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(` Turns: ${Math.floor(cs.activeChatHistory.length / 2)}\n`));
|
|
1257
1571
|
}
|
|
1258
1572
|
else {
|
|
1259
|
-
console.log(chalk_1.default.hex(
|
|
1573
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('\n No active session (--no-session mode).\n'));
|
|
1260
1574
|
}
|
|
1261
1575
|
return { handled: true };
|
|
1262
1576
|
}
|
|
1263
1577
|
if (input === ':sessions') {
|
|
1264
1578
|
const sessions = session_store_js_1.sessionStore.listSessions(c.chatState.projectRoot);
|
|
1265
1579
|
if (sessions.length === 0) {
|
|
1266
|
-
console.log(chalk_1.default.hex(
|
|
1580
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('\n No saved sessions.\n'));
|
|
1267
1581
|
}
|
|
1268
1582
|
else {
|
|
1269
1583
|
console.log(chalk_1.default.hex('#cc785c').bold('\n Saved sessions:\n'));
|
|
@@ -1272,8 +1586,8 @@ async function handleReplCommand(input, c) {
|
|
|
1272
1586
|
const turns = Math.floor(s.history.length / 2);
|
|
1273
1587
|
const marker = s.id === c.chatState.activeChatId ? chalk_1.default.hex('#5a9e6e')(' ← current') : '';
|
|
1274
1588
|
console.log(` ${chalk_1.default.hex('#cc785c')(s.id.padEnd(20))} ` +
|
|
1275
|
-
`${chalk_1.default.hex(
|
|
1276
|
-
`${chalk_1.default.hex(
|
|
1589
|
+
`${chalk_1.default.hex(diamond_js_1.TEXT_HEX)(s.title.slice(0, 40).padEnd(41))} ` +
|
|
1590
|
+
`${chalk_1.default.hex(diamond_js_1.FAINT_HEX)(`${turns}t · ${updated}`)}${marker}`);
|
|
1277
1591
|
}
|
|
1278
1592
|
console.log();
|
|
1279
1593
|
}
|
|
@@ -1282,7 +1596,7 @@ async function handleReplCommand(input, c) {
|
|
|
1282
1596
|
if (input === ':resume' || input === ':resume ') {
|
|
1283
1597
|
const latest = session_store_js_1.sessionStore.findLatestSession(c.chatState.projectRoot);
|
|
1284
1598
|
if (!latest) {
|
|
1285
|
-
console.log(chalk_1.default.hex(
|
|
1599
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('\n No saved sessions to resume.\n'));
|
|
1286
1600
|
return { handled: true };
|
|
1287
1601
|
}
|
|
1288
1602
|
console.log(chalk_1.default.hex('#5a9e6e')(`\n ↩ Resuming ${latest.id} — "${latest.title}" (${Math.floor(latest.history.length / 2)} turns)\n`));
|
|
@@ -1305,7 +1619,7 @@ async function handleReplCommand(input, c) {
|
|
|
1305
1619
|
}
|
|
1306
1620
|
if (input === ':history') {
|
|
1307
1621
|
const turns = Math.floor(c.chatState.activeChatHistory.length / 2);
|
|
1308
|
-
console.log(chalk_1.default.hex(
|
|
1622
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(`\n Current session: ${turns} turn${turns !== 1 ? 's' : ''} in history.\n`));
|
|
1309
1623
|
return { handled: true };
|
|
1310
1624
|
}
|
|
1311
1625
|
if (input === ':clear-history') {
|
|
@@ -1316,7 +1630,7 @@ async function handleReplCommand(input, c) {
|
|
|
1316
1630
|
const title = input.startsWith(':save ') ? input.slice(':save '.length).trim() : undefined;
|
|
1317
1631
|
const cs = c.chatState;
|
|
1318
1632
|
if (!cs.activeChatId) {
|
|
1319
|
-
console.log(chalk_1.default.hex(
|
|
1633
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('\n No active session to save (--no-session mode).\n'));
|
|
1320
1634
|
return { handled: true };
|
|
1321
1635
|
}
|
|
1322
1636
|
const session = await session_store_js_1.sessionStore.upsertSession(cs.projectRoot, cs.activeChatId, cs.activeChatHistory, title ?? cs.activeChatTitle);
|
|
@@ -1330,7 +1644,7 @@ async function handleReplCommand(input, c) {
|
|
|
1330
1644
|
console.log(chalk_1.default.hex('#5a9e6e')(`\n ✓ Deleted session ${id}\n`));
|
|
1331
1645
|
if (id === c.chatState.activeChatId) {
|
|
1332
1646
|
const newId = session_store_js_1.sessionStore.generateId();
|
|
1333
|
-
console.log(chalk_1.default.hex(
|
|
1647
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` Starting new session: ${newId}\n`));
|
|
1334
1648
|
return { handled: true, newChatId: newId, newHistory: [], newTitle: undefined };
|
|
1335
1649
|
}
|
|
1336
1650
|
}
|
|
@@ -1341,28 +1655,28 @@ async function handleReplCommand(input, c) {
|
|
|
1341
1655
|
}
|
|
1342
1656
|
// ── Model / API commands ─────────────────────────────────────────────────
|
|
1343
1657
|
if (input === ':context') {
|
|
1344
|
-
console.log(chalk_1.default.hex(
|
|
1345
|
-
console.log(chalk_1.default.hex(
|
|
1658
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(`\n Project: ${c.ctx.name} · ${c.ctx.language} · ${c.ctx.framework}`));
|
|
1659
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(` Root: ${c.ctx.root}\n`));
|
|
1346
1660
|
return { handled: true };
|
|
1347
1661
|
}
|
|
1348
1662
|
if (input === ':graph') {
|
|
1349
1663
|
const summary = (0, context_js_1.loadGraphSummary)(c.ctx.root);
|
|
1350
1664
|
if (!summary) {
|
|
1351
|
-
console.log(chalk_1.default.hex(
|
|
1665
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('\n No graph.json found. Run :graph refresh to extract.\n'));
|
|
1352
1666
|
}
|
|
1353
1667
|
else {
|
|
1354
1668
|
console.log(chalk_1.default.hex('#cc785c').bold('\n Codebase Knowledge Graph\n'));
|
|
1355
|
-
console.log(chalk_1.default.hex(
|
|
1669
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(summary));
|
|
1356
1670
|
console.log();
|
|
1357
1671
|
}
|
|
1358
1672
|
return { handled: true };
|
|
1359
1673
|
}
|
|
1360
1674
|
if (input === ':viz' || input === ':dashboard') {
|
|
1361
|
-
console.log(chalk_1.default.hex(
|
|
1675
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('\n Generating dashboard…\n'));
|
|
1362
1676
|
try {
|
|
1363
1677
|
const outPath = (0, index_js_1.generateDashboard)(c.ctx.root);
|
|
1364
1678
|
console.log(chalk_1.default.hex('#5a9e6e')(` ✓ Dashboard written to ${outPath}`));
|
|
1365
|
-
console.log(chalk_1.default.hex(
|
|
1679
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(' Opening in browser…\n'));
|
|
1366
1680
|
(0, index_js_1.openDashboard)(outPath);
|
|
1367
1681
|
}
|
|
1368
1682
|
catch (e) {
|
|
@@ -1370,185 +1684,11 @@ async function handleReplCommand(input, c) {
|
|
|
1370
1684
|
}
|
|
1371
1685
|
return { handled: true };
|
|
1372
1686
|
}
|
|
1373
|
-
if (input === ':dream' || input === ':dream full') {
|
|
1374
|
-
const full = input === ':dream full';
|
|
1375
|
-
console.log(chalk_1.default.hex('#8a7768')('\n Dreaming — consolidating episodes…\n'));
|
|
1376
|
-
try {
|
|
1377
|
-
const { runDream } = await Promise.resolve().then(() => __importStar(require('../dream/dream.js')));
|
|
1378
|
-
const { createResilientProvider } = await Promise.resolve().then(() => __importStar(require('../providers/resilient-factory.js')));
|
|
1379
|
-
const provider = createResilientProvider({ model: c.providerConfig.model, apiKey: c.providerConfig.apiKey, baseUrl: c.providerConfig.baseUrl }, {}, c.display);
|
|
1380
|
-
const res = await runDream({ projectRoot: c.ctx.root, provider, full });
|
|
1381
|
-
if (res.skipped) {
|
|
1382
|
-
console.log(chalk_1.default.hex('#cc9e5c')(` ⤳ Nothing to dream about — ${res.reason}.\n`));
|
|
1383
|
-
}
|
|
1384
|
-
else {
|
|
1385
|
-
console.log(chalk_1.default.hex('#5a9e6e')(` ✓ Dream written: ${res.path}`));
|
|
1386
|
-
console.log(chalk_1.default.hex('#8a7768')(` Consolidated ${res.episodeCount} episode(s).\n`));
|
|
1387
|
-
}
|
|
1388
|
-
}
|
|
1389
|
-
catch (e) {
|
|
1390
|
-
console.log(chalk_1.default.hex('#b15439')(` ✗ ${String(e)}\n`));
|
|
1391
|
-
}
|
|
1392
|
-
return { handled: true };
|
|
1393
|
-
}
|
|
1394
|
-
if (input.startsWith(':research ')) {
|
|
1395
|
-
const topic = input.slice(':research '.length).trim();
|
|
1396
|
-
if (!topic) {
|
|
1397
|
-
console.log(chalk_1.default.hex('#b15439')('\n Usage: :research <topic>\n'));
|
|
1398
|
-
return { handled: true };
|
|
1399
|
-
}
|
|
1400
|
-
console.log(chalk_1.default.hex('#8a7768')(`\n Researching "${topic}"…\n`));
|
|
1401
|
-
try {
|
|
1402
|
-
const { runResearch } = await Promise.resolve().then(() => __importStar(require('../research/research.js')));
|
|
1403
|
-
const { createResilientProvider } = await Promise.resolve().then(() => __importStar(require('../providers/resilient-factory.js')));
|
|
1404
|
-
const provider = createResilientProvider({ model: c.providerConfig.model, apiKey: c.providerConfig.apiKey, baseUrl: c.providerConfig.baseUrl }, {}, c.display);
|
|
1405
|
-
const res = await runResearch({
|
|
1406
|
-
projectRoot: c.ctx.root,
|
|
1407
|
-
topic,
|
|
1408
|
-
provider,
|
|
1409
|
-
context: c.ctx,
|
|
1410
|
-
permissions: c.permissions,
|
|
1411
|
-
display: c.display,
|
|
1412
|
-
});
|
|
1413
|
-
console.log(chalk_1.default.hex('#5a9e6e')(` ✓ Research written: ${res.path}`));
|
|
1414
|
-
console.log(chalk_1.default.hex('#8a7768')(` ${res.turns} turn(s) · ${res.toolCalls} tool call(s).\n`));
|
|
1415
|
-
}
|
|
1416
|
-
catch (e) {
|
|
1417
|
-
console.log(chalk_1.default.hex('#b15439')(` ✗ ${String(e)}\n`));
|
|
1418
|
-
}
|
|
1419
|
-
return { handled: true };
|
|
1420
|
-
}
|
|
1421
|
-
if (input.startsWith(':council ')) {
|
|
1422
|
-
let rest = input.slice(':council '.length).trim();
|
|
1423
|
-
const wantsReader = /(^|\s)--reader(\s|$)/.test(rest);
|
|
1424
|
-
rest = rest.replace(/(^|\s)--reader(\s|$)/g, ' ').trim();
|
|
1425
|
-
const stray = rest.match(/(^|\s)(--\S+)/);
|
|
1426
|
-
if (stray) {
|
|
1427
|
-
console.log(chalk_1.default.hex('#b15439')(`\n ✗ Unknown flag: ${stray[2]}. Did you mean --reader?\n`));
|
|
1428
|
-
return { handled: true };
|
|
1429
|
-
}
|
|
1430
|
-
const topic = rest;
|
|
1431
|
-
if (!topic) {
|
|
1432
|
-
console.log(chalk_1.default.hex('#b15439')('\n Usage: :council <topic> [--reader]\n'));
|
|
1433
|
-
return { handled: true };
|
|
1434
|
-
}
|
|
1435
|
-
console.log(chalk_1.default.hex('#8a7768')(`\n Convening Ecclesia on "${topic}" — 5 independent agents…\n`));
|
|
1436
|
-
try {
|
|
1437
|
-
const { runCouncil } = await Promise.resolve().then(() => __importStar(require('../research/council.js')));
|
|
1438
|
-
const { createResilientProvider } = await Promise.resolve().then(() => __importStar(require('../providers/resilient-factory.js')));
|
|
1439
|
-
const synthesisProvider = createResilientProvider({ model: c.providerConfig.model, apiKey: c.providerConfig.apiKey, baseUrl: c.providerConfig.baseUrl }, {}, c.display);
|
|
1440
|
-
const res = await runCouncil({
|
|
1441
|
-
projectRoot: c.ctx.root,
|
|
1442
|
-
topic,
|
|
1443
|
-
synthesisProvider,
|
|
1444
|
-
context: c.ctx,
|
|
1445
|
-
permissions: c.permissions,
|
|
1446
|
-
display: c.display,
|
|
1447
|
-
});
|
|
1448
|
-
console.log(chalk_1.default.hex('#5a9e6e')(` ✓ Ecclesia verdict: ${res.path}`));
|
|
1449
|
-
console.log(chalk_1.default.hex('#8a7768')(` ${res.panelSize} agents` + (res.agentFailures > 0 ? `, ${res.agentFailures} failed` : '') + `.\n`));
|
|
1450
|
-
if (wantsReader) {
|
|
1451
|
-
const { renderReaderFromMarkdown } = await Promise.resolve().then(() => __importStar(require('../viz/reader.js')));
|
|
1452
|
-
const readerPath = renderReaderFromMarkdown(res.path);
|
|
1453
|
-
console.log(chalk_1.default.hex('#5a9e6e')(` ✓ Reader: ${readerPath}`));
|
|
1454
|
-
console.log(chalk_1.default.hex('#8a7768')(' Opening in browser…\n'));
|
|
1455
|
-
(0, index_js_1.openDashboard)(readerPath);
|
|
1456
|
-
}
|
|
1457
|
-
}
|
|
1458
|
-
catch (e) {
|
|
1459
|
-
console.log(chalk_1.default.hex('#b15439')(` ✗ ${String(e)}\n`));
|
|
1460
|
-
}
|
|
1461
|
-
return { handled: true };
|
|
1462
|
-
}
|
|
1463
|
-
if (input === ':rem') {
|
|
1464
|
-
const dir = path.join(c.ctx.root, 'dreams');
|
|
1465
|
-
let files = [];
|
|
1466
|
-
try {
|
|
1467
|
-
files = fs.readdirSync(dir).filter(f => f.endsWith('.md') && !f.startsWith('.')).sort().reverse();
|
|
1468
|
-
}
|
|
1469
|
-
catch { /* no dreams dir */ }
|
|
1470
|
-
if (files.length === 0) {
|
|
1471
|
-
console.log(chalk_1.default.hex('#8a7768')('\n No dreams yet. Run :dream after some work.\n'));
|
|
1472
|
-
}
|
|
1473
|
-
else {
|
|
1474
|
-
console.log(chalk_1.default.hex('#cc785c').bold('\n Dreams\n'));
|
|
1475
|
-
for (const f of files.slice(0, 20))
|
|
1476
|
-
console.log(chalk_1.default.hex('#ede0cc')(` ${f}`));
|
|
1477
|
-
// Prefer reconciled view if it exists
|
|
1478
|
-
const reconciledPath = path.join(dir, '.reconciled.md');
|
|
1479
|
-
const hasReconciled = fs.existsSync(reconciledPath);
|
|
1480
|
-
if (hasReconciled) {
|
|
1481
|
-
console.log(chalk_1.default.hex('#5a9e6e')(`\n ✓ Reconciled memory (${files.length} dreams → projection):\n`));
|
|
1482
|
-
console.log(chalk_1.default.hex('#ede0cc')(fs.readFileSync(reconciledPath, 'utf8')));
|
|
1483
|
-
}
|
|
1484
|
-
else {
|
|
1485
|
-
const latest = path.join(dir, files[0]);
|
|
1486
|
-
console.log(chalk_1.default.hex('#8a7768')(`\n Most recent: ${latest}\n`));
|
|
1487
|
-
console.log(chalk_1.default.hex('#4e3d30')(fs.readFileSync(latest, 'utf8')));
|
|
1488
|
-
if (files.length >= 3) {
|
|
1489
|
-
console.log(chalk_1.default.hex('#8a7768')(' 💡 Run :dream to trigger memory reconciliation (≥3 dreams).\n'));
|
|
1490
|
-
}
|
|
1491
|
-
}
|
|
1492
|
-
}
|
|
1493
|
-
return { handled: true };
|
|
1494
|
-
}
|
|
1495
|
-
if (input === ':ruby' || input === ':ruby on' || input === ':ruby off') {
|
|
1496
|
-
if (input === ':ruby') {
|
|
1497
|
-
const state = c.rubyEnabled ? chalk_1.default.hex('#5a9e6e')('ON') : chalk_1.default.hex('#b15439')('OFF');
|
|
1498
|
-
console.log(chalk_1.default.hex('#8a7768')(`\n Ruby Principle is ${state}. Use :ruby on / :ruby off to change.\n`));
|
|
1499
|
-
return { handled: true };
|
|
1500
|
-
}
|
|
1501
|
-
const on = input === ':ruby on';
|
|
1502
|
-
c.rubyEnabled = on;
|
|
1503
|
-
const state = on ? chalk_1.default.hex('#5a9e6e')('ON') : chalk_1.default.hex('#b15439')('OFF');
|
|
1504
|
-
console.log(chalk_1.default.hex('#8a7768')(`\n Ruby Principle is now ${state} for this session.\n`));
|
|
1505
|
-
return { handled: true, rubyEnabled: on };
|
|
1506
|
-
}
|
|
1507
|
-
if (input === ':machina' || input === ':machina --html' || input === ':machina -html') {
|
|
1508
|
-
const wantsHtml = input !== ':machina';
|
|
1509
|
-
console.log(chalk_1.default.hex('#b15439')('\n ⚠ :machina reads several source files and writes a careful long-form result — extremely high token usage.\n'));
|
|
1510
|
-
const { runMachina } = await Promise.resolve().then(() => __importStar(require('../machina/index.js')));
|
|
1511
|
-
const res = runMachina({ outputRoot: c.ctx.root, writeHtml: wantsHtml });
|
|
1512
|
-
console.log(res.terminalOutput);
|
|
1513
|
-
if (res.htmlPath) {
|
|
1514
|
-
console.log(chalk_1.default.hex('#5a9e6e')(` ✓ Written: ${res.htmlPath}\n`));
|
|
1515
|
-
}
|
|
1516
|
-
return { handled: true };
|
|
1517
|
-
}
|
|
1518
|
-
if (input === ':design' || input.startsWith(':design ')) {
|
|
1519
|
-
const { listDesignSlugs } = await Promise.resolve().then(() => __importStar(require('../agent/design.js')));
|
|
1520
|
-
const slugs = listDesignSlugs(c.ctx.root);
|
|
1521
|
-
const arg = input.slice(':design'.length).trim();
|
|
1522
|
-
if (slugs.length === 0) {
|
|
1523
|
-
console.log(chalk_1.default.hex('#b15439')('\n No design-systems/ folder found in this project.\n'));
|
|
1524
|
-
return { handled: true };
|
|
1525
|
-
}
|
|
1526
|
-
if (arg === '' || arg === 'list') {
|
|
1527
|
-
const current = process.env.AURA_DESIGN || 'terracotta';
|
|
1528
|
-
console.log(chalk_1.default.hex('#cc785c').bold(`\n Design systems (${slugs.length}) — default: ${current}\n`));
|
|
1529
|
-
console.log(chalk_1.default.hex('#ede0cc')(' ' + slugs.join(' ')));
|
|
1530
|
-
console.log(chalk_1.default.hex('#8a7768')('\n :design <slug> sets the default · auto-applies on website/UI tasks.\n'));
|
|
1531
|
-
return { handled: true };
|
|
1532
|
-
}
|
|
1533
|
-
if (arg === 'off') {
|
|
1534
|
-
process.env.AURA_DESIGN = '__none__';
|
|
1535
|
-
console.log(chalk_1.default.hex('#8a7768')('\n Design auto-injection disabled for this session.\n'));
|
|
1536
|
-
return { handled: true };
|
|
1537
|
-
}
|
|
1538
|
-
if (slugs.includes(arg)) {
|
|
1539
|
-
process.env.AURA_DESIGN = arg;
|
|
1540
|
-
console.log(chalk_1.default.hex('#5a9e6e')(`\n Default design set to "${arg}" for this session.\n`));
|
|
1541
|
-
}
|
|
1542
|
-
else {
|
|
1543
|
-
console.log(chalk_1.default.hex('#b15439')(`\n Unknown design "${arg}". Run :design to list available slugs.\n`));
|
|
1544
|
-
}
|
|
1545
|
-
return { handled: true };
|
|
1546
|
-
}
|
|
1547
1687
|
if (input === ':plans') {
|
|
1548
1688
|
const { planStore } = await Promise.resolve().then(() => __importStar(require('../orchestration/plan-store.js')));
|
|
1549
1689
|
const plans = await planStore.list();
|
|
1550
1690
|
if (!plans.length) {
|
|
1551
|
-
console.log(chalk_1.default.hex(
|
|
1691
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('\n No execution plans found.\n'));
|
|
1552
1692
|
}
|
|
1553
1693
|
else {
|
|
1554
1694
|
console.log(chalk_1.default.hex('#cc785c').bold('\n Execution plans:\n'));
|
|
@@ -1558,51 +1698,62 @@ async function handleReplCommand(input, c) {
|
|
|
1558
1698
|
const statusColor = p.status === 'done' ? '#5a9e6e' : p.status === 'failed' ? '#b15439' : '#cc9e5c';
|
|
1559
1699
|
console.log(` ${chalk_1.default.hex(statusColor)(p.status.padEnd(8))} ` +
|
|
1560
1700
|
`${chalk_1.default.hex('#cc785c')(p.id.slice(0, 12).padEnd(14))} ` +
|
|
1561
|
-
`${chalk_1.default.hex(
|
|
1562
|
-
`${chalk_1.default.hex(
|
|
1701
|
+
`${chalk_1.default.hex(diamond_js_1.TEXT_HEX)(p.goal.slice(0, 50).padEnd(51))} ` +
|
|
1702
|
+
`${chalk_1.default.hex(diamond_js_1.FAINT_HEX)(`${p.steps.length}s · ${dur} · ${created}`)}`);
|
|
1563
1703
|
}
|
|
1564
1704
|
console.log();
|
|
1565
1705
|
}
|
|
1566
1706
|
return { handled: true };
|
|
1567
1707
|
}
|
|
1568
1708
|
if (input === ':graph refresh') {
|
|
1569
|
-
console.log(chalk_1.default.hex(
|
|
1709
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('\n Refreshing codebase graph...\n'));
|
|
1710
|
+
const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
|
|
1570
1711
|
try {
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
const edgeCount = perception.edges.length;
|
|
1576
|
-
console.log(chalk_1.default.hex('#5a9e6e')(` ✓ Extracted ${nodeCount} nodes, ${edgeCount} edges.\n`));
|
|
1577
|
-
}
|
|
1578
|
-
catch (err) {
|
|
1579
|
-
console.log(chalk_1.default.hex('#f85149')(` ✗ Extraction failed: ${err instanceof Error ? err.message : String(err)}\n`));
|
|
1712
|
+
execSync('python3 -c "' +
|
|
1713
|
+
'import json,os,re,glob; ' +
|
|
1714
|
+
'root=\\\"' + c.ctx.root + '/src\\\"; ' +
|
|
1715
|
+
'print(\\\"Scanning\\\", root)" ', { stdio: 'inherit' });
|
|
1580
1716
|
}
|
|
1717
|
+
catch { /* ignore */ }
|
|
1581
1718
|
// Reload context graph
|
|
1582
1719
|
c.ctx.graphSummary = (0, context_js_1.loadGraphSummary)(c.ctx.root);
|
|
1583
1720
|
if (c.ctx.graphSummary) {
|
|
1584
1721
|
console.log(chalk_1.default.hex('#5a9e6e')(' ✓ Graph loaded and injected into context.\n'));
|
|
1585
1722
|
}
|
|
1586
1723
|
else {
|
|
1587
|
-
console.log(chalk_1.default.hex(
|
|
1724
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(' No graph.json found after refresh. Run graphify extract first.\n'));
|
|
1588
1725
|
}
|
|
1589
1726
|
return { handled: true };
|
|
1590
1727
|
}
|
|
1591
1728
|
// ── Provider wizard command ────────────────────────────────────────────────
|
|
1592
1729
|
if (input === ':provider' || input === '/provider') {
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1730
|
+
// Fullscreen prompt approach was unreliable — wizard's readline and TUI's
|
|
1731
|
+
// stdin handler collide. Instead, stop TUI input, let readline own stdin
|
|
1732
|
+
// completely, then restart TUI input.
|
|
1733
|
+
const wasInputActive = tui_js_1.inputActive;
|
|
1734
|
+
if (wasInputActive)
|
|
1735
|
+
(0, tui_js_1.stopInput)();
|
|
1736
|
+
try {
|
|
1737
|
+
const cfg = await (0, provider_wizard_js_1.runProviderWizard)(undefined, undefined);
|
|
1738
|
+
if (cfg) {
|
|
1739
|
+
runtimeConfig.model = cfg.model;
|
|
1740
|
+
runtimeConfig.baseUrl = cfg.baseUrl;
|
|
1741
|
+
runtimeConfig.apiKey = cfg.apiKey;
|
|
1742
|
+
c.providerConfig.model = cfg.model;
|
|
1743
|
+
c.providerConfig.baseUrl = cfg.baseUrl;
|
|
1744
|
+
c.providerConfig.apiKey = cfg.apiKey;
|
|
1745
|
+
resolved.model = cfg.model;
|
|
1746
|
+
resolved.baseUrl = cfg.baseUrl;
|
|
1747
|
+
(0, tui_js_1.writeOutput)(chalk_1.default.hex('#5a9e6e')(` ✓ Now using ${cfg.provider} · ${cfg.model}`));
|
|
1748
|
+
if (fileConfig.model && fileConfig.model !== cfg.model) {
|
|
1749
|
+
(0, tui_js_1.writeOutput)(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` ⚠ .aura.json pins model "${fileConfig.model}" — next startup in this project will use it.\n` +
|
|
1750
|
+
` Remove the "model" field from .aura.json (or set it to ${cfg.model}) to keep this choice.`));
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
finally {
|
|
1755
|
+
if (wasInputActive)
|
|
1756
|
+
(0, tui_js_1.startInput)();
|
|
1606
1757
|
}
|
|
1607
1758
|
return { handled: true };
|
|
1608
1759
|
}
|
|
@@ -1613,9 +1764,9 @@ async function handleReplCommand(input, c) {
|
|
|
1613
1764
|
return acc;
|
|
1614
1765
|
}, {});
|
|
1615
1766
|
for (const [provider, models] of Object.entries(byProvider)) {
|
|
1616
|
-
console.log(chalk_1.default.hex(
|
|
1767
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(`\n ${provider}`));
|
|
1617
1768
|
for (const m of models) {
|
|
1618
|
-
console.log(` ${chalk_1.default.hex('#cc785c')(m.id.padEnd(45))} ${chalk_1.default.hex(
|
|
1769
|
+
console.log(` ${chalk_1.default.hex('#cc785c')(m.id.padEnd(45))} ${chalk_1.default.hex(diamond_js_1.FAINT_HEX)(m.speed)}`);
|
|
1619
1770
|
}
|
|
1620
1771
|
}
|
|
1621
1772
|
console.log();
|
|
@@ -1638,7 +1789,22 @@ async function handleReplCommand(input, c) {
|
|
|
1638
1789
|
const newKey = input.slice(sep.length).trim();
|
|
1639
1790
|
runtimeConfig.apiKey = newKey;
|
|
1640
1791
|
c.providerConfig.apiKey = newKey;
|
|
1641
|
-
|
|
1792
|
+
// Persist it so it survives across sessions (fixes "type the key every
|
|
1793
|
+
// time"). Save under the env-var name for the current provider, and also
|
|
1794
|
+
// set that env var live so getApiKey resolves it this session.
|
|
1795
|
+
const envName = globalCfg?.apiKeyEnv || envNameForModel(resolved.model ?? '');
|
|
1796
|
+
if (envName) {
|
|
1797
|
+
try {
|
|
1798
|
+
const p = (0, key_store_js_1.saveKey)(envName, newKey);
|
|
1799
|
+
console.log(chalk_1.default.hex('#5a9e6e')(` ✓ API key saved as ${envName} → ${p} (persists across sessions).`));
|
|
1800
|
+
}
|
|
1801
|
+
catch (e) {
|
|
1802
|
+
console.log(chalk_1.default.hex('#5a9e6e')(' ✓ API key set for current session (could not persist: ' + String(e) + ').'));
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
else {
|
|
1806
|
+
console.log(chalk_1.default.hex('#5a9e6e')(' ✓ API key set for current session.'));
|
|
1807
|
+
}
|
|
1642
1808
|
return { handled: true };
|
|
1643
1809
|
}
|
|
1644
1810
|
if (input === '/clear' || input === '/reset') {
|
|
@@ -1650,42 +1816,10 @@ async function handleReplCommand(input, c) {
|
|
|
1650
1816
|
console.log(chalk_1.default.hex('#5a9e6e')(' ✓ Session stats reset'));
|
|
1651
1817
|
return { handled: true };
|
|
1652
1818
|
}
|
|
1653
|
-
|
|
1654
|
-
if (input.startsWith(':approve ')) {
|
|
1655
|
-
const raw = input.slice(':approve '.length).trim();
|
|
1656
|
-
const mapped = raw === 'all' ? 'auto' : raw;
|
|
1657
|
-
if (mapped !== 'auto' && mapped !== 'normal' && mapped !== 'read-only') {
|
|
1658
|
-
console.log(chalk_1.default.hex('#b15439')(' ✗ Usage: :approve all | :approve normal | :approve read-only'));
|
|
1659
|
-
return { handled: true };
|
|
1660
|
-
}
|
|
1661
|
-
c.permissions.setLevel(mapped);
|
|
1662
|
-
console.log(chalk_1.default.hex('#5a9e6e')(` ✓ Permission level set to ${mapped}`));
|
|
1663
|
-
return { handled: true };
|
|
1664
|
-
}
|
|
1665
|
-
if (input === ':stats') {
|
|
1666
|
-
// Episode-level stats (same as --stats CLI flag)
|
|
1667
|
-
const episodes = await (0, episode_capture_js_1.loadEpisodes)(c.chatState.projectRoot);
|
|
1668
|
-
const output = (0, stats_js_1.formatStats)(episodes);
|
|
1669
|
-
for (const line of output.split('\n')) {
|
|
1670
|
-
if (line.trim() === 'Aura Stats') {
|
|
1671
|
-
console.log(chalk_1.default.hex('#cc785c').bold(line));
|
|
1672
|
-
}
|
|
1673
|
-
else if (line.includes(':')) {
|
|
1674
|
-
const colonIdx = line.indexOf(':');
|
|
1675
|
-
const label = line.slice(0, colonIdx + 1);
|
|
1676
|
-
const value = line.slice(colonIdx + 1);
|
|
1677
|
-
console.log(chalk_1.default.hex('#8a7768')(label) + chalk_1.default.hex('#ede0cc')(value));
|
|
1678
|
-
}
|
|
1679
|
-
else {
|
|
1680
|
-
console.log(line);
|
|
1681
|
-
}
|
|
1682
|
-
}
|
|
1683
|
-
return { handled: true };
|
|
1684
|
-
}
|
|
1685
|
-
if (input === '/stats' || input === '/usage' || input === ':usage') {
|
|
1819
|
+
if (input === '/stats' || input === '/usage') {
|
|
1686
1820
|
const u = c.cumulative;
|
|
1687
1821
|
const total = u.inputTokens + u.outputTokens;
|
|
1688
|
-
console.log(chalk_1.default.hex(
|
|
1822
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)([
|
|
1689
1823
|
'',
|
|
1690
1824
|
` Session usage:`,
|
|
1691
1825
|
` Turns: ${u.turns}`,
|
|
@@ -1698,11 +1832,19 @@ async function handleReplCommand(input, c) {
|
|
|
1698
1832
|
].join('\n')));
|
|
1699
1833
|
return { handled: true };
|
|
1700
1834
|
}
|
|
1835
|
+
if (input === '/context') {
|
|
1836
|
+
const u = c.cumulative;
|
|
1837
|
+
const h = c.healthTracker.snapshot(u.inputTokens, u.outputTokens);
|
|
1838
|
+
h.turnCount = u.turns;
|
|
1839
|
+
h.toolCallCount = u.toolCalls;
|
|
1840
|
+
c.display.contextDashboard?.(h);
|
|
1841
|
+
return { handled: true };
|
|
1842
|
+
}
|
|
1701
1843
|
// ── Workflow commands ──────────────────────────────────────────────────────
|
|
1702
1844
|
if (input === ':workflows') {
|
|
1703
1845
|
const workflows = await (0, engine_js_1.listWorkflows)();
|
|
1704
1846
|
if (workflows.length === 0) {
|
|
1705
|
-
console.log(chalk_1.default.hex(
|
|
1847
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('\n No saved workflows.\n'));
|
|
1706
1848
|
}
|
|
1707
1849
|
else {
|
|
1708
1850
|
console.log(chalk_1.default.hex('#cc785c').bold('\n Saved workflows:\n'));
|
|
@@ -1712,9 +1854,9 @@ async function handleReplCommand(input, c) {
|
|
|
1712
1854
|
const totalSteps = ws.definition.steps.length;
|
|
1713
1855
|
const statusColor = ws.status === 'done' ? '#5a9e6e' : ws.status === 'failed' ? '#b15439' : '#cc785c';
|
|
1714
1856
|
console.log(` ${chalk_1.default.hex('#cc785c')(ws.definition.id.padEnd(24))} ` +
|
|
1715
|
-
`${chalk_1.default.hex(
|
|
1857
|
+
`${chalk_1.default.hex(diamond_js_1.TEXT_HEX)(ws.definition.name.slice(0, 36).padEnd(37))} ` +
|
|
1716
1858
|
`${chalk_1.default.hex(statusColor)(ws.status.padEnd(8))} ` +
|
|
1717
|
-
`${chalk_1.default.hex(
|
|
1859
|
+
`${chalk_1.default.hex(diamond_js_1.FAINT_HEX)(`${doneSteps}/${totalSteps} steps · ${created}`)}`);
|
|
1718
1860
|
}
|
|
1719
1861
|
console.log();
|
|
1720
1862
|
}
|
|
@@ -1772,7 +1914,7 @@ async function handleReplCommand(input, c) {
|
|
|
1772
1914
|
}
|
|
1773
1915
|
else {
|
|
1774
1916
|
console.log(chalk_1.default.hex('#b15439').bold(`\n ✗ ${finalState.outcome}`));
|
|
1775
|
-
console.log(chalk_1.default.hex(
|
|
1917
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` Resume with: :resume-workflow ${finalState.definition.id}\n`));
|
|
1776
1918
|
}
|
|
1777
1919
|
return { handled: true };
|
|
1778
1920
|
}
|
|
@@ -1810,7 +1952,7 @@ async function handleReplCommand(input, c) {
|
|
|
1810
1952
|
}
|
|
1811
1953
|
else {
|
|
1812
1954
|
console.log(chalk_1.default.hex('#b15439').bold(`\n ✗ ${finalState.outcome}`));
|
|
1813
|
-
console.log(chalk_1.default.hex(
|
|
1955
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` Resume with: :resume-workflow ${finalState.definition.id}\n`));
|
|
1814
1956
|
}
|
|
1815
1957
|
return { handled: true };
|
|
1816
1958
|
}
|
|
@@ -1919,29 +2061,29 @@ async function runArchitectPlan(task, provider, ctx, display) {
|
|
|
1919
2061
|
});
|
|
1920
2062
|
// Display result
|
|
1921
2063
|
console.log(chalk_1.default.hex('#cc785c').bold('\n Blueprint\n'));
|
|
1922
|
-
console.log(chalk_1.default.hex(
|
|
1923
|
-
console.log(chalk_1.default.hex(
|
|
2064
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_HEX)(` Task: ${blueprint.task}`));
|
|
2065
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(` ID: ${blueprint.id}\n`));
|
|
1924
2066
|
if (blueprint.files.length > 0) {
|
|
1925
2067
|
console.log(chalk_1.default.hex('#cc785c').bold(' Files:\n'));
|
|
1926
2068
|
for (const f of blueprint.files) {
|
|
1927
2069
|
console.log(` ${chalk_1.default.hex('#cc785c')(f.path)}`);
|
|
1928
|
-
console.log(` ${chalk_1.default.hex(
|
|
2070
|
+
console.log(` ${chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(f.purpose)}`);
|
|
1929
2071
|
if (f.exports.length > 0)
|
|
1930
|
-
console.log(` ${chalk_1.default.hex(
|
|
2072
|
+
console.log(` ${chalk_1.default.hex(diamond_js_1.FAINT_HEX)(`exports: ${f.exports.join(', ')}`)}`);
|
|
1931
2073
|
if (f.interfaces.length > 0)
|
|
1932
|
-
console.log(` ${chalk_1.default.hex(
|
|
2074
|
+
console.log(` ${chalk_1.default.hex(diamond_js_1.FAINT_HEX)(`interfaces: ${f.interfaces.join(', ')}`)}`);
|
|
1933
2075
|
}
|
|
1934
2076
|
}
|
|
1935
2077
|
if (blueprint.dataModels.length > 0) {
|
|
1936
2078
|
console.log(chalk_1.default.hex('#cc785c').bold('\n Data Models:\n'));
|
|
1937
2079
|
for (const dm of blueprint.dataModels) {
|
|
1938
|
-
console.log(` ${chalk_1.default.hex('#cc785c')(dm.name)} — ${chalk_1.default.hex(
|
|
2080
|
+
console.log(` ${chalk_1.default.hex('#cc785c')(dm.name)} — ${chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(dm.description)}`);
|
|
1939
2081
|
}
|
|
1940
2082
|
}
|
|
1941
2083
|
if (blueprint.risks.length > 0) {
|
|
1942
2084
|
console.log(chalk_1.default.hex('#b15439').bold('\n Risks:\n'));
|
|
1943
2085
|
for (const risk of blueprint.risks) {
|
|
1944
|
-
console.log(` ${chalk_1.default.hex('#b15439')('⚠')} ${chalk_1.default.hex(
|
|
2086
|
+
console.log(` ${chalk_1.default.hex('#b15439')('⚠')} ${chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(risk)}`);
|
|
1945
2087
|
}
|
|
1946
2088
|
}
|
|
1947
2089
|
console.log(chalk_1.default.hex('#5a9e6e')('\n Blueprint saved. No files were modified.'));
|
|
@@ -1961,16 +2103,20 @@ async function runOrchestratedTask(task, provider, ctx, display, forceOrchestrat
|
|
|
1961
2103
|
// If --plan flag, show plan and ask for confirmation
|
|
1962
2104
|
if (argv.plan === true) {
|
|
1963
2105
|
display.showPlan?.(plan);
|
|
1964
|
-
// Use a simple readline prompt for confirmation
|
|
1965
|
-
|
|
2106
|
+
// Use a simple readline prompt for confirmation — reuse the REPL's
|
|
2107
|
+
// readline when one is active (second interface double-echoes and
|
|
2108
|
+
// pauses stdin on close).
|
|
2109
|
+
const sharedRl = (0, permissions_js_1.getSharedReadline)();
|
|
2110
|
+
const rl = sharedRl ?? readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1966
2111
|
const approved = await new Promise(resolve => {
|
|
1967
2112
|
rl.question(chalk_1.default.hex('#cc785c')('\n Run this plan? [y/N] '), answer => {
|
|
1968
|
-
|
|
2113
|
+
if (!sharedRl)
|
|
2114
|
+
rl.close();
|
|
1969
2115
|
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
1970
2116
|
});
|
|
1971
2117
|
});
|
|
1972
2118
|
if (!approved) {
|
|
1973
|
-
console.log(chalk_1.default.hex(
|
|
2119
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(' Plan cancelled.\n'));
|
|
1974
2120
|
process.exit(0);
|
|
1975
2121
|
}
|
|
1976
2122
|
}
|
|
@@ -1994,51 +2140,49 @@ async function runOrchestratedTask(task, provider, ctx, display, forceOrchestrat
|
|
|
1994
2140
|
display.summary(executedPlan.outcome, executedPlan.steps.length, 0);
|
|
1995
2141
|
}
|
|
1996
2142
|
const totalTokens = executedPlan.totalTokens ?? 0;
|
|
1997
|
-
console.log(chalk_1.default.hex(
|
|
2143
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(` ↳ ${totalTokens.toLocaleString()} tokens · ${executedPlan.steps.length} steps · status: ${executedPlan.status}`));
|
|
1998
2144
|
}
|
|
1999
|
-
function printUsageFooter(display, usage, costUsd
|
|
2145
|
+
function printUsageFooter(display, usage, costUsd) {
|
|
2000
2146
|
const total = usage.inputTokens + usage.outputTokens;
|
|
2001
|
-
console.log(chalk_1.default.hex(
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
console.log((0, display_js_1.formatContextBar)(used, ctxWin, estimated));
|
|
2027
|
-
}
|
|
2028
|
-
}
|
|
2147
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(` ↳ ${total.toLocaleString()} tokens (${usage.inputTokens.toLocaleString()} in / ${usage.outputTokens.toLocaleString()} out) · est. $${costUsd.toFixed(4)}`));
|
|
2148
|
+
}
|
|
2149
|
+
/**
|
|
2150
|
+
* Read a task summary aloud (the "Aura talks back" half of the voice loop).
|
|
2151
|
+
* Best-effort: strips code/markdown, caps length so a long report doesn't
|
|
2152
|
+
* monologue, and never throws into the caller (a TTS/network failure must
|
|
2153
|
+
* not break a successful task).
|
|
2154
|
+
*/
|
|
2155
|
+
async function speakSummary(text) {
|
|
2156
|
+
if (!text || !text.trim())
|
|
2157
|
+
return;
|
|
2158
|
+
// Strip fenced code blocks, inline code, markdown markers, and collapse
|
|
2159
|
+
// whitespace — TTS should read the prose, not backticks and hashes.
|
|
2160
|
+
const spoken = text
|
|
2161
|
+
.replace(/```[\s\S]*?```/g, ' (code omitted) ')
|
|
2162
|
+
.replace(/`[^`]*`/g, '')
|
|
2163
|
+
.replace(/[#*_>|]/g, ' ')
|
|
2164
|
+
.replace(/\s+/g, ' ')
|
|
2165
|
+
.trim()
|
|
2166
|
+
.slice(0, 600);
|
|
2167
|
+
if (!spoken)
|
|
2168
|
+
return;
|
|
2169
|
+
try {
|
|
2170
|
+
const { speakText } = await Promise.resolve().then(() => __importStar(require('../tools/dictate.js')));
|
|
2171
|
+
await speakText(spoken);
|
|
2029
2172
|
}
|
|
2173
|
+
catch { /* speech is best-effort — never break the task on TTS failure */ }
|
|
2030
2174
|
}
|
|
2031
2175
|
function printHelp() {
|
|
2032
2176
|
console.log(`
|
|
2033
|
-
${chalk_1.default.hex('#cc785c').bold(' aura')} ${chalk_1.default.hex(
|
|
2177
|
+
${chalk_1.default.hex('#cc785c').bold(' aura')} ${chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)("— Aura Code: model-agnostic AI coding agent")}
|
|
2034
2178
|
|
|
2035
|
-
${chalk_1.default.hex(
|
|
2036
|
-
aura ${chalk_1.default.hex(
|
|
2037
|
-
aura ${chalk_1.default.hex(
|
|
2038
|
-
aura
|
|
2039
|
-
aura ${chalk_1.default.hex(
|
|
2179
|
+
${chalk_1.default.hex(diamond_js_1.FAINT_HEX)('Usage:')}
|
|
2180
|
+
aura ${chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('"<task>"')} Run a single task
|
|
2181
|
+
aura ${chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('serve')} Start the HTTP API server
|
|
2182
|
+
aura ${chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('--interactive')} Start interactive REPL
|
|
2183
|
+
aura ${chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('--models')} List available models
|
|
2040
2184
|
|
|
2041
|
-
${chalk_1.default.hex(
|
|
2185
|
+
${chalk_1.default.hex(diamond_js_1.FAINT_HEX)('Options:')}
|
|
2042
2186
|
--model, -m <id> Model to use (default: from ~/.config/aura-code/config.json)
|
|
2043
2187
|
--api-key <key> API key (overrides env var)
|
|
2044
2188
|
--base-url <url> Custom API endpoint (for Ollama, proxies, etc.)
|
|
@@ -2047,12 +2191,10 @@ ${chalk_1.default.hex('#cc785c').bold(' aura')} ${chalk_1.default.hex('#8a7768'
|
|
|
2047
2191
|
--cwd <path> Working directory (default: current)
|
|
2048
2192
|
--models List all known model IDs
|
|
2049
2193
|
--no-session Disable conversation history persistence
|
|
2050
|
-
--once Run single task and exit (no REPL after)
|
|
2051
2194
|
--new-session Force a fresh session (ignore any prior history)
|
|
2052
2195
|
--resume [id] Resume latest session, or a specific session by ID
|
|
2053
2196
|
--chat-id <id> Attach to a specific chat ID (creates if missing)
|
|
2054
2197
|
--list-sessions List all saved sessions for this project
|
|
2055
|
-
--setup Run the provider setup wizard before entering REPL
|
|
2056
2198
|
--no-setup Skip the first-run setup wizard
|
|
2057
2199
|
--reset-setup Wipe saved config and re-run the setup wizard
|
|
2058
2200
|
--orchestrate Force multi-agent orchestration mode
|
|
@@ -2064,11 +2206,14 @@ ${chalk_1.default.hex('#cc785c').bold(' aura')} ${chalk_1.default.hex('#8a7768'
|
|
|
2064
2206
|
--verify Verify output after task; retry up to --max-verify-retries times
|
|
2065
2207
|
--max-verify-retries <n> Max verification retries (default: 3)
|
|
2066
2208
|
--test-command <cmd> Shell command run as part of verification (e.g. "npm test")
|
|
2067
|
-
--max-turns <n> Max agent loop turns before stopping (default:
|
|
2209
|
+
--max-turns <n> Max agent loop turns before stopping (default: sized by task shape)
|
|
2210
|
+
--moa Mixture of agents: parallel read-only domain perspectives + synthesis (exploratory tasks only)
|
|
2068
2211
|
--analyze Mine session history for weakness patterns; save report
|
|
2069
|
-
--stats Show episode stats — completion rate, avg duration, top model, tokens
|
|
2070
2212
|
--propose-harness Generate system-prompt patches from weakness report
|
|
2071
2213
|
--apply-harness <id> Apply a proposal patch; reverts if tests fail
|
|
2214
|
+
--doctor Scan Aura itself for issues (build, config, deps, env, git)
|
|
2215
|
+
--doctor --fix Scan and attempt auto-repairs (rebuild, restore, reinstall)
|
|
2216
|
+
--doctor --offline Skip the network version check
|
|
2072
2217
|
--workflow <name> ... Create and run a sequential workflow with named steps
|
|
2073
2218
|
--resume-workflow <id> Resume a paused/failed workflow from last completed step
|
|
2074
2219
|
--workflows List all persisted workflows
|
|
@@ -2080,7 +2225,7 @@ ${chalk_1.default.hex('#cc785c').bold(' aura')} ${chalk_1.default.hex('#8a7768'
|
|
|
2080
2225
|
--fallback <model> Fallback model if primary exhausts retries (repeatable)
|
|
2081
2226
|
--verify Enable post-task verification with automatic retries
|
|
2082
2227
|
|
|
2083
|
-
${chalk_1.default.hex(
|
|
2228
|
+
${chalk_1.default.hex(diamond_js_1.FAINT_HEX)('Resilience:')}
|
|
2084
2229
|
All API calls automatically:
|
|
2085
2230
|
1. Honour Retry-After / Google's retryDelay on 429s
|
|
2086
2231
|
2. Back off with exponential + jitter (capped at 60s)
|
|
@@ -2088,7 +2233,7 @@ ${chalk_1.default.hex('#cc785c').bold(' aura')} ${chalk_1.default.hex('#8a7768'
|
|
|
2088
2233
|
4. Fail over to the next --fallback model if retries exhaust
|
|
2089
2234
|
5. Pace requests when --rate-limit-rpm / --rate-limit-tpm is set
|
|
2090
2235
|
|
|
2091
|
-
${chalk_1.default.hex(
|
|
2236
|
+
${chalk_1.default.hex(diamond_js_1.FAINT_HEX)('Project config (.aura.json):')}
|
|
2092
2237
|
{
|
|
2093
2238
|
"model": "claude-sonnet-4-5-20251001",
|
|
2094
2239
|
"mode": "auto",
|
|
@@ -2099,8 +2244,8 @@ ${chalk_1.default.hex('#cc785c').bold(' aura')} ${chalk_1.default.hex('#8a7768'
|
|
|
2099
2244
|
"apiKeyEnv": "DEEPSEEK_API_KEY",
|
|
2100
2245
|
"prefixes": ["deepseek/"],
|
|
2101
2246
|
"models": [
|
|
2102
|
-
{ "id": "deepseek/deepseek-
|
|
2103
|
-
{ "id": "deepseek/deepseek-
|
|
2247
|
+
{ "id": "deepseek/deepseek-chat", "name": "DeepSeek Chat", "speed": "Fast" },
|
|
2248
|
+
{ "id": "deepseek/deepseek-reasoner", "name": "DeepSeek R1", "speed": "Reasoning" }
|
|
2104
2249
|
]
|
|
2105
2250
|
}
|
|
2106
2251
|
],
|
|
@@ -2114,19 +2259,21 @@ ${chalk_1.default.hex('#cc785c').bold(' aura')} ${chalk_1.default.hex('#8a7768'
|
|
|
2114
2259
|
CLI flags always override .aura.json.
|
|
2115
2260
|
Custom providers are OpenAI-compatible endpoints.
|
|
2116
2261
|
|
|
2117
|
-
${chalk_1.default.hex(
|
|
2262
|
+
${chalk_1.default.hex(diamond_js_1.FAINT_HEX)('Model examples:')}
|
|
2118
2263
|
aura -m claude-opus-4-5-20251001 "refactor auth"
|
|
2119
2264
|
aura -m gpt-4o "add unit tests"
|
|
2120
2265
|
aura -m gemini-2.5-pro --rate-limit-rpm 20 "explain this codebase"
|
|
2121
2266
|
aura -m ollama/llama3.2 "local model, no API key needed"
|
|
2122
2267
|
|
|
2123
|
-
${chalk_1.default.hex(
|
|
2268
|
+
${chalk_1.default.hex(diamond_js_1.FAINT_HEX)('API keys (set as env vars):')}
|
|
2124
2269
|
ANTHROPIC_API_KEY Claude models
|
|
2125
2270
|
OPENAI_API_KEY GPT models
|
|
2126
2271
|
GOOGLE_API_KEY Gemini models
|
|
2127
2272
|
XAI_API_KEY Grok models
|
|
2128
2273
|
OPENROUTER_API_KEY OpenRouter (access to all models)
|
|
2129
2274
|
XIAOMI_API_KEY Xiaomi MiMo
|
|
2275
|
+
ZHIPU_API_KEY Zhipu GLM (Z.ai) — glm-* general endpoint, zhipu-coding/<model> Coding Plan
|
|
2276
|
+
ZHIPU_BASE_URL Override Zhipu endpoint (default https://api.z.ai/api/paas/v4)
|
|
2130
2277
|
AURA_MODEL Default model (overridden by --model)
|
|
2131
2278
|
AURA_API_RPM Default request rate limit
|
|
2132
2279
|
AURA_API_TPM Default token rate limit (Gemini)
|
|
@@ -2134,7 +2281,10 @@ ${chalk_1.default.hex('#cc785c').bold(' aura')} ${chalk_1.default.hex('#8a7768'
|
|
|
2134
2281
|
AURA_FALLBACK_MODEL Comma-separated fallback models
|
|
2135
2282
|
`);
|
|
2136
2283
|
}
|
|
2137
|
-
if (argv.
|
|
2284
|
+
if (argv.doctor === true) {
|
|
2285
|
+
// --doctor runs async above and exits on completion; don't start main().
|
|
2286
|
+
}
|
|
2287
|
+
else if (argv._[0] === 'serve') {
|
|
2138
2288
|
const port = Number(argv.port ?? argv.p ?? 7337);
|
|
2139
2289
|
(0, index_js_2.startServer)({ port, cwd, model: argv.model, apiKey: argv['api-key'] ?? undefined, baseUrl: argv['base-url'] ?? undefined, open: argv.open !== false }).catch(e => { console.error('Fatal:', String(e)); process.exit(1); });
|
|
2140
2290
|
}
|