aura-code 0.6.3 → 0.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +15 -5
- package/dist/agent/loop.js +182 -45
- 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 +140 -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 +20 -4
- package/dist/cli/display.js +77 -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 +1018 -851
- 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 +1190 -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 +92 -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/providers/types.d.ts +1 -0
- 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,304 @@ 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
|
+
// Update the TUI status line so the model change is immediately visible.
|
|
1112
|
+
(0, tui_js_1.setStatusLine)([test.name, newModel, permissionLevel].filter(Boolean).join(' · '));
|
|
1113
|
+
// Remember the choice for the next session. The saved baseUrl belongs to
|
|
1114
|
+
// the wizard-configured model — keep it only when switching back to that
|
|
1115
|
+
// model, otherwise the factory's per-provider default applies.
|
|
1116
|
+
try {
|
|
1117
|
+
(0, global_config_js_1.saveGlobalConfig)({
|
|
1118
|
+
provider: globalCfg?.provider ?? test.name,
|
|
1119
|
+
// loadGlobalConfig treats an empty apiKeyEnv as "not configured", so
|
|
1120
|
+
// resolve from the NEW model's provider — never inherit the old
|
|
1121
|
+
// provider's env name across a provider change (a silently wrong
|
|
1122
|
+
// apiKeyEnv on disk pairs the wrong key with the model on the next
|
|
1123
|
+
// startup).
|
|
1124
|
+
apiKeyEnv: (0, model_select_js_1.apiKeyEnvForModelSwitch)(newModel, prevModel ?? prevResolved, globalCfg?.apiKeyEnv),
|
|
1125
|
+
defaultModel: newModel,
|
|
1126
|
+
baseUrl: savedProvider && newModel === savedProvider.model ? savedProvider.baseUrl : undefined,
|
|
1127
|
+
});
|
|
1128
|
+
}
|
|
1129
|
+
catch { /* persistence is best-effort; the switch itself succeeded */ }
|
|
1126
1130
|
return { ok: true };
|
|
1127
1131
|
}
|
|
1128
1132
|
catch (e) {
|
|
1129
1133
|
runtimeConfig.model = prevModel; // rollback on error
|
|
1134
|
+
resolved.model = prevResolved;
|
|
1135
|
+
runtimeConfig.apiKey = prevApiKey;
|
|
1130
1136
|
runtimeConfig.baseUrl = prevBaseUrl;
|
|
1137
|
+
resolved.baseUrl = prevResolvedBaseUrl;
|
|
1131
1138
|
return { ok: false, err: String(e) };
|
|
1132
1139
|
}
|
|
1133
1140
|
}
|
|
@@ -1136,134 +1143,450 @@ function trySetModel(c, newModel) {
|
|
|
1136
1143
|
* lets the user pick by number or type a custom model ID.
|
|
1137
1144
|
*/
|
|
1138
1145
|
async function showModelSelector(c) {
|
|
1139
|
-
|
|
1140
|
-
//
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
label: ` ${chalk_1.default.hex('#cc785c')(String(entries.length + 1).padStart(2))}. ${chalk_1.default.hex('#ede0cc')(m.name.padEnd(30))} ${chalk_1.default.hex('#4e3d30')(m.speed)}`,
|
|
1151
|
-
provider: m.provider,
|
|
1152
|
-
});
|
|
1146
|
+
// Same stdin-collision fix as :provider (see handleReplCommand): the TUI's
|
|
1147
|
+
// raw-mode stdin handler competes with any prompt for keystrokes, so stop
|
|
1148
|
+
// TUI input entirely (this also hides the main input box), let a plain
|
|
1149
|
+
// readline own stdin, then restart TUI input once the selector exits.
|
|
1150
|
+
// enterFullscreenPrompt resets the scroll region so the selector's output
|
|
1151
|
+
// isn't crammed into the small scroll area (numbers were invisible because
|
|
1152
|
+
// the bottom block was overwriting them).
|
|
1153
|
+
const wasInputActive = tui_js_1.inputActive;
|
|
1154
|
+
if (wasInputActive) {
|
|
1155
|
+
(0, tui_js_1.stopInput)();
|
|
1156
|
+
(0, tui_js_1.enterFullscreenPrompt)();
|
|
1153
1157
|
}
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
const choice = answer.trim();
|
|
1163
|
-
if (!choice) {
|
|
1164
|
-
console.log(chalk_1.default.hex('#4e3d30')(' Cancelled.\n'));
|
|
1165
|
-
resolve();
|
|
1166
|
-
return;
|
|
1158
|
+
try {
|
|
1159
|
+
// Grouped list where section headers are display-only: they carry no
|
|
1160
|
+
// number, so numbering is gap-free and a header can never be selected.
|
|
1161
|
+
const rows = (0, model_select_js_1.buildModelRows)((0, factory_js_1.getAllModels)());
|
|
1162
|
+
console.log(chalk_1.default.hex('#cc785c').bold('\n Model Selector\n'));
|
|
1163
|
+
for (const r of rows) {
|
|
1164
|
+
if (r.kind === 'header') {
|
|
1165
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX).bold(` ── ${r.provider} ──`));
|
|
1167
1166
|
}
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1167
|
+
else {
|
|
1168
|
+
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)}`);
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(`\n Current: ${runtimeConfig.model}`));
|
|
1172
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(' Type a number, model ID, or press Enter to cancel:\n'));
|
|
1173
|
+
const answer = await new Promise(resolve => {
|
|
1174
|
+
const promptRl = c.rl;
|
|
1175
|
+
if (!promptRl) {
|
|
1176
|
+
// TUI mode: TUI input is stopped above, so a temporary readline can
|
|
1177
|
+
// own stdin without the two-readers-one-stream conflict.
|
|
1178
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1179
|
+
rl.question(chalk_1.default.hex('#cc785c')(' ▸ '), ans => { rl.close(); resolve(ans); });
|
|
1179
1180
|
return;
|
|
1180
1181
|
}
|
|
1181
|
-
|
|
1182
|
-
trySetModel(c, choice);
|
|
1183
|
-
resolve();
|
|
1182
|
+
promptRl.question(chalk_1.default.hex('#cc785c')(' ▸ '), resolve);
|
|
1184
1183
|
});
|
|
1185
|
-
|
|
1184
|
+
const choice = answer.trim();
|
|
1185
|
+
if (!choice) {
|
|
1186
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(' Cancelled.\n'));
|
|
1187
|
+
return;
|
|
1188
|
+
}
|
|
1189
|
+
// Try as a number — every listed number is a real, selectable model
|
|
1190
|
+
const num = parseInt(choice, 10);
|
|
1191
|
+
if (!isNaN(num) && num >= 1 && num <= (0, model_select_js_1.modelCount)(rows)) {
|
|
1192
|
+
trySetModel(c, (0, model_select_js_1.modelIdForNumber)(rows, num));
|
|
1193
|
+
return;
|
|
1194
|
+
}
|
|
1195
|
+
// Treat as a raw model ID
|
|
1196
|
+
trySetModel(c, choice);
|
|
1197
|
+
}
|
|
1198
|
+
finally {
|
|
1199
|
+
if (wasInputActive) {
|
|
1200
|
+
(0, tui_js_1.exitFullscreenPrompt)();
|
|
1201
|
+
(0, tui_js_1.startInput)();
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1186
1204
|
}
|
|
1187
1205
|
async function handleReplCommand(input, c) {
|
|
1188
1206
|
const unhandled = { handled: false };
|
|
1207
|
+
// ── :q — Task queue (with subcommands, keep bare :q as quit) ─────────────
|
|
1208
|
+
if (input.startsWith(':q ')) {
|
|
1209
|
+
const sub = input.slice(3).trimStart();
|
|
1210
|
+
const { addToQueue, loadQueue, removeFromQueue, clearQueue, runQueueItem, formatQueue } = await Promise.resolve().then(() => __importStar(require('../repl/queue.js')));
|
|
1211
|
+
if (sub.startsWith('add ')) {
|
|
1212
|
+
const prompt = sub.slice(4).trim();
|
|
1213
|
+
if (!prompt) {
|
|
1214
|
+
c.display.warning('Usage: :q add <prompt> -- add a task to the queue.');
|
|
1215
|
+
return { handled: true };
|
|
1216
|
+
}
|
|
1217
|
+
const item = addToQueue(prompt);
|
|
1218
|
+
console.log(chalk_1.default.hex('#5a9e6e')(`\n ✓ Queued #${loadQueue().length}: "${prompt.slice(0, 60)}"\n`));
|
|
1219
|
+
return { handled: true };
|
|
1220
|
+
}
|
|
1221
|
+
if (sub === 'list') {
|
|
1222
|
+
const items = loadQueue();
|
|
1223
|
+
console.log(formatQueue(items));
|
|
1224
|
+
return { handled: true };
|
|
1225
|
+
}
|
|
1226
|
+
if (sub.startsWith('run ')) {
|
|
1227
|
+
const n = parseInt(sub.slice(4).trim(), 10);
|
|
1228
|
+
if (isNaN(n) || n < 1) {
|
|
1229
|
+
c.display.warning('Usage: :q run <number> — run the task at that position (see :q list).');
|
|
1230
|
+
return { handled: true };
|
|
1231
|
+
}
|
|
1232
|
+
const items = loadQueue();
|
|
1233
|
+
if (n > items.length) {
|
|
1234
|
+
c.display.warning(`Queue only has ${items.length} item(s).`);
|
|
1235
|
+
return { handled: true };
|
|
1236
|
+
}
|
|
1237
|
+
c.display.agentThinking();
|
|
1238
|
+
const result = await runQueueItem(n - 1, buildProvider(c.display), c.ctx, c.permissions, c.display);
|
|
1239
|
+
if (!result) {
|
|
1240
|
+
c.display.warning('Could not run that item.');
|
|
1241
|
+
return { handled: true };
|
|
1242
|
+
}
|
|
1243
|
+
c.display.success(`Queue item #${n}: ${result.success ? 'done' : 'failed'}`);
|
|
1244
|
+
if (result.output) {
|
|
1245
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_HEX)(` ${result.output.slice(0, 240)}`));
|
|
1246
|
+
}
|
|
1247
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(` ${result.turns} turn(s) · ${result.toolCalls} tool call(s).\n`));
|
|
1248
|
+
return { handled: true };
|
|
1249
|
+
}
|
|
1250
|
+
if (sub.startsWith('drop ')) {
|
|
1251
|
+
const n = parseInt(sub.slice(5).trim(), 10);
|
|
1252
|
+
if (isNaN(n) || n < 1) {
|
|
1253
|
+
c.display.warning('Usage: :q drop <number> — remove the task at that position.');
|
|
1254
|
+
return { handled: true };
|
|
1255
|
+
}
|
|
1256
|
+
const removed = removeFromQueue(n - 1);
|
|
1257
|
+
if (!removed) {
|
|
1258
|
+
c.display.warning(`No item at position ${n}.`);
|
|
1259
|
+
return { handled: true };
|
|
1260
|
+
}
|
|
1261
|
+
console.log(chalk_1.default.hex('#5a9e6e')(`\n ✓ Dropped #${n}: "${removed.prompt.slice(0, 60)}"\n`));
|
|
1262
|
+
return { handled: true };
|
|
1263
|
+
}
|
|
1264
|
+
if (sub === 'clear') {
|
|
1265
|
+
const count = loadQueue().length;
|
|
1266
|
+
if (count === 0) {
|
|
1267
|
+
c.display.warning('Queue is already empty.');
|
|
1268
|
+
return { handled: true };
|
|
1269
|
+
}
|
|
1270
|
+
clearQueue();
|
|
1271
|
+
console.log(chalk_1.default.hex('#5a9e6e')(`\n ✓ Queue cleared (${count} item(s) removed).\n`));
|
|
1272
|
+
return { handled: true };
|
|
1273
|
+
}
|
|
1274
|
+
c.display.warning('Usage: :q add <prompt> | :q list | :q run <n> | :q drop <n> | :q clear');
|
|
1275
|
+
return { handled: true };
|
|
1276
|
+
}
|
|
1189
1277
|
if (input === ':quit' || input === ':q' || input === '/exit') {
|
|
1190
1278
|
process.exit(0);
|
|
1191
1279
|
}
|
|
1280
|
+
if (input === ':speak') {
|
|
1281
|
+
speakEnabled = !speakEnabled;
|
|
1282
|
+
console.log(chalk_1.default.hex(speakEnabled ? '#5a9e6e' : '#a68a2a')(` 🔊 Voice replies ${speakEnabled ? 'ON — Aura will read its answers aloud' : 'OFF'}.\n`));
|
|
1283
|
+
return { handled: true };
|
|
1284
|
+
}
|
|
1285
|
+
// :approve — flip the session into auto-approve (no per-command y/N prompt).
|
|
1286
|
+
// :approve → toggle auto ⇄ normal
|
|
1287
|
+
// :approve all → auto (approve everything for this session)
|
|
1288
|
+
// :approve off → back to normal (confirm destructive commands again)
|
|
1289
|
+
// Dangerous commands are still blocked either way.
|
|
1290
|
+
if (input === ':approve' || input === ':approve all' || input === ':approve off') {
|
|
1291
|
+
const cur = c.permissions.getLevel();
|
|
1292
|
+
let next;
|
|
1293
|
+
if (input === ':approve off')
|
|
1294
|
+
next = 'normal';
|
|
1295
|
+
else if (input === ':approve all')
|
|
1296
|
+
next = 'auto';
|
|
1297
|
+
else
|
|
1298
|
+
next = cur === 'auto' ? 'normal' : 'auto';
|
|
1299
|
+
c.permissions.setLevel(next);
|
|
1300
|
+
if (next === 'auto') {
|
|
1301
|
+
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'));
|
|
1302
|
+
}
|
|
1303
|
+
else {
|
|
1304
|
+
console.log(chalk_1.default.hex('#5a9e6e')(' 🔒 Auto-approve OFF — destructive commands will ask for confirmation again.\n'));
|
|
1305
|
+
}
|
|
1306
|
+
return { handled: true };
|
|
1307
|
+
}
|
|
1308
|
+
if (input === ':dream' || input === ':dream full') {
|
|
1309
|
+
const full = input === ':dream full';
|
|
1310
|
+
const { runDream } = await Promise.resolve().then(() => __importStar(require('../dream/dream.js')));
|
|
1311
|
+
c.display.agentThinking();
|
|
1312
|
+
const res = await runDream({ projectRoot: c.ctx.root, provider: buildProvider(c.display), full });
|
|
1313
|
+
if (res.skipped) {
|
|
1314
|
+
c.display.warning(res.providerError
|
|
1315
|
+
? `Dream skipped (episodes preserved): ${res.providerError}`
|
|
1316
|
+
: (res.reason ?? 'Nothing to consolidate.'));
|
|
1317
|
+
}
|
|
1318
|
+
else {
|
|
1319
|
+
c.display.success(`Dream written: ${res.path} (${res.episodeCount} episodes${full ? ', full run' : ''})`);
|
|
1320
|
+
if (res.reconciled)
|
|
1321
|
+
c.display.success('Reconciliation also ran (>=3 dreams exist) -> dreams/.reconciled.md');
|
|
1322
|
+
}
|
|
1323
|
+
return { handled: true };
|
|
1324
|
+
}
|
|
1325
|
+
if (input.startsWith(':research ') || input === ':research') {
|
|
1326
|
+
const topic = input.slice(':research '.length).trim();
|
|
1327
|
+
if (!topic) {
|
|
1328
|
+
c.display.warning('Usage: :research <topic> -- runs a multi-step research pass and saves to research/*.md.');
|
|
1329
|
+
return { handled: true };
|
|
1330
|
+
}
|
|
1331
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(`\n Researching "${topic}"…\n`));
|
|
1332
|
+
try {
|
|
1333
|
+
const { runResearch } = await Promise.resolve().then(() => __importStar(require('../research/research.js')));
|
|
1334
|
+
const res = await runResearch({
|
|
1335
|
+
projectRoot: c.ctx.root,
|
|
1336
|
+
topic,
|
|
1337
|
+
provider: buildProvider(c.display),
|
|
1338
|
+
context: c.ctx,
|
|
1339
|
+
permissions: c.permissions,
|
|
1340
|
+
display: c.display,
|
|
1341
|
+
});
|
|
1342
|
+
console.log(chalk_1.default.hex('#5a9e6e')(` ✓ Research written: ${res.path}`));
|
|
1343
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` ${res.turns} turn(s) · ${res.toolCalls} tool call(s).\n`));
|
|
1344
|
+
}
|
|
1345
|
+
catch (e) {
|
|
1346
|
+
console.log(chalk_1.default.hex('#b15439')(` ✗ ${String(e)}\n`));
|
|
1347
|
+
}
|
|
1348
|
+
return { handled: true };
|
|
1349
|
+
}
|
|
1350
|
+
if (input === ':confessions') {
|
|
1351
|
+
const { listConfessions } = await Promise.resolve().then(() => __importStar(require('../agent/confess.js')));
|
|
1352
|
+
const confs = listConfessions();
|
|
1353
|
+
if (confs.length === 0) {
|
|
1354
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('\n No confessions yet. Run :confess after a high-token episode.\n'));
|
|
1355
|
+
}
|
|
1356
|
+
else {
|
|
1357
|
+
console.log(chalk_1.default.hex('#cc785c').bold(`\n ${confs.length} confession(s):\n`));
|
|
1358
|
+
for (const c of confs) {
|
|
1359
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` ${c.file}`));
|
|
1360
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(` ${c.tokens.toLocaleString()} tokens burned → ${c.lesson.slice(0, 100)}`));
|
|
1361
|
+
}
|
|
1362
|
+
console.log('');
|
|
1363
|
+
}
|
|
1364
|
+
return { handled: true };
|
|
1365
|
+
}
|
|
1366
|
+
if (input === ':confess') {
|
|
1367
|
+
const { runConfession, findEpisodeToConfess } = await Promise.resolve().then(() => __importStar(require('../agent/confess.js')));
|
|
1368
|
+
const targetEp = findEpisodeToConfess(c.ctx.root);
|
|
1369
|
+
if (!targetEp) {
|
|
1370
|
+
console.log(chalk_1.default.hex('#cc9e5c')('\n No anomalous episode found. Confession is fully automatic — the system alone decides what to confess.\n'));
|
|
1371
|
+
return { handled: true };
|
|
1372
|
+
}
|
|
1373
|
+
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`));
|
|
1374
|
+
try {
|
|
1375
|
+
// Use a different model than the one that made the mistake
|
|
1376
|
+
const confessorModel = targetEp.model.startsWith('deepseek') ? 'glm-5.2' : 'deepseek/deepseek-chat';
|
|
1377
|
+
const { createProvider } = await Promise.resolve().then(() => __importStar(require('../providers/factory.js')));
|
|
1378
|
+
const provider = createProvider({ model: confessorModel });
|
|
1379
|
+
const result = await runConfession({
|
|
1380
|
+
projectRoot: c.ctx.root,
|
|
1381
|
+
episodeId: targetEp.id,
|
|
1382
|
+
provider,
|
|
1383
|
+
});
|
|
1384
|
+
console.log(chalk_1.default.hex('#5a9e6e')(` ✓ Confession written: ${result.path}`));
|
|
1385
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` Tokens burned: ${result.tokensBurned.toLocaleString()} | Confession cost: ${result.tokensSpent.toLocaleString()} (${confessorModel})`));
|
|
1386
|
+
console.log(chalk_1.default.hex('#cc9e6c')(' Permanent lesson:'));
|
|
1387
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_HEX)(` "${result.lesson}"\n`));
|
|
1388
|
+
}
|
|
1389
|
+
catch (e) {
|
|
1390
|
+
console.log(chalk_1.default.hex('#b15439')(` ✗ ${String(e)}\n`));
|
|
1391
|
+
}
|
|
1392
|
+
return { handled: true };
|
|
1393
|
+
}
|
|
1394
|
+
if (input === ':rem') {
|
|
1395
|
+
const { getReconciledOrLatest } = await Promise.resolve().then(() => __importStar(require('../dream/dream.js')));
|
|
1396
|
+
const res = getReconciledOrLatest(c.ctx.root);
|
|
1397
|
+
if (!res) {
|
|
1398
|
+
c.display.warning('No dreams yet. Run :dream first.');
|
|
1399
|
+
}
|
|
1400
|
+
else {
|
|
1401
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(`\n ${res.isReconciled ? 'Reconciled projection' : 'Latest dream (not yet reconciled)'}:\n`));
|
|
1402
|
+
console.log(res.content);
|
|
1403
|
+
}
|
|
1404
|
+
return { handled: true };
|
|
1405
|
+
}
|
|
1406
|
+
// ── :mine — Baby Ruby experience mining (src/mining/). The base pass is
|
|
1407
|
+
// zero-LLM (pure clustering over episodes/*.json); --refine additionally
|
|
1408
|
+
// runs Papa Ruby, one local-model call per qualifying concept, appending
|
|
1409
|
+
// accepted lessons to training-data/<date>.jsonl.
|
|
1410
|
+
if (input === ':mine' || input === ':mine --refine') {
|
|
1411
|
+
const refine = input.endsWith('--refine');
|
|
1412
|
+
const { mineExperience } = await Promise.resolve().then(() => __importStar(require('../mining/extract.js')));
|
|
1413
|
+
const mined = await mineExperience(c.ctx.root);
|
|
1414
|
+
if (mined.episodeCount === 0) {
|
|
1415
|
+
c.display.warning('No episodes to mine yet — run some tasks first.');
|
|
1416
|
+
return { handled: true };
|
|
1417
|
+
}
|
|
1418
|
+
console.log(chalk_1.default.hex('#cc785c').bold(`\n Mined ${mined.concepts.length} concept(s) from ${mined.episodeCount} episode(s) (${mined.unclustered} unclustered):\n`));
|
|
1419
|
+
for (const con of mined.concepts.slice(0, 15)) {
|
|
1420
|
+
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})`));
|
|
1421
|
+
if (con.keywords.length > 0)
|
|
1422
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(` keywords: ${con.keywords.join(', ')}`));
|
|
1423
|
+
}
|
|
1424
|
+
if (mined.concepts.length > 15) {
|
|
1425
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(` … and ${mined.concepts.length - 15} more.`));
|
|
1426
|
+
}
|
|
1427
|
+
if (refine) {
|
|
1428
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('\n Refining with the local Ruby model (Papa Ruby)…'));
|
|
1429
|
+
const { refineConcepts } = await Promise.resolve().then(() => __importStar(require('../mining/refine.js')));
|
|
1430
|
+
const res = await refineConcepts({ projectRoot: c.ctx.root, concepts: mined.concepts });
|
|
1431
|
+
if (res.accepted.length > 0) {
|
|
1432
|
+
console.log(chalk_1.default.hex('#5a9e6e')(` ✓ ${res.accepted.length} training example(s) appended: ${res.outputPath}`));
|
|
1433
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(` ${res.rejected} rejected, ${res.skipped} below the confidence/frequency gate.\n`));
|
|
1434
|
+
}
|
|
1435
|
+
else {
|
|
1436
|
+
c.display.warning(`No concepts survived refinement — ${res.rejected} rejected, ${res.skipped} below the confidence/frequency gate.`);
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
else if (mined.concepts.length > 0) {
|
|
1440
|
+
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'));
|
|
1441
|
+
}
|
|
1442
|
+
return { handled: true };
|
|
1443
|
+
}
|
|
1444
|
+
if (input === ':doctor' || input.startsWith(':doctor')) {
|
|
1445
|
+
const fix = input.includes('--fix');
|
|
1446
|
+
const offline = input.includes('--offline');
|
|
1447
|
+
c.display.agentThinking();
|
|
1448
|
+
const report = await (0, index_js_5.runDoctor)({ projectRoot: c.ctx.root, fix, offline });
|
|
1449
|
+
if (typeof tui_js_1.writeOutput === 'function') {
|
|
1450
|
+
(0, tui_js_1.writeOutput)((0, index_js_5.formatDoctorReport)(report));
|
|
1451
|
+
}
|
|
1452
|
+
else {
|
|
1453
|
+
console.log((0, index_js_5.formatDoctorReport)(report));
|
|
1454
|
+
}
|
|
1455
|
+
return { handled: true };
|
|
1456
|
+
}
|
|
1457
|
+
if (input.startsWith(':machina ') || input === ':machina') {
|
|
1458
|
+
const machinaTask = input.slice(':machina '.length).trim();
|
|
1459
|
+
if (!machinaTask) {
|
|
1460
|
+
c.display.warning('Usage: :machina <task> -- runs the task with self-verification (file/test checks + auto-retry).');
|
|
1461
|
+
return { handled: true };
|
|
1462
|
+
}
|
|
1463
|
+
const { runWithVerification } = await Promise.resolve().then(() => __importStar(require('../verify/index.js')));
|
|
1464
|
+
const maxRetries = cliMaxVerifyRetries ?? fileConfig.maxVerifyRetries ?? defaults_js_1.DEFAULTS.maxVerifyRetries;
|
|
1465
|
+
const testCommand = cliTestCommand ?? fileConfig.testCommand;
|
|
1466
|
+
const wrapperResult = await runWithVerification({
|
|
1467
|
+
loopOpts: {
|
|
1468
|
+
provider: buildProvider(c.display), task: machinaTask,
|
|
1469
|
+
context: c.ctx, permissions: c.permissions, display: c.display,
|
|
1470
|
+
initialHistory: c.chatState.activeChatHistory,
|
|
1471
|
+
maxTurns: resolved.maxTurns,
|
|
1472
|
+
spawnConfig: {
|
|
1473
|
+
apiKey: c.providerConfig.apiKey,
|
|
1474
|
+
baseUrl: c.providerConfig.baseUrl,
|
|
1475
|
+
},
|
|
1476
|
+
sessionPath: c.sessionPath,
|
|
1477
|
+
},
|
|
1478
|
+
config: { enabled: true, maxRetries, testCommand },
|
|
1479
|
+
projectRoot: c.ctx.root,
|
|
1480
|
+
display: c.display,
|
|
1481
|
+
});
|
|
1482
|
+
const mResult = wrapperResult.loopResult;
|
|
1483
|
+
if (mResult.success) {
|
|
1484
|
+
c.display.summary(mResult.summary, mResult.turns, mResult.toolCallCount);
|
|
1485
|
+
printUsageFooter(c.display, mResult.usage, mResult.costUsd);
|
|
1486
|
+
}
|
|
1487
|
+
else {
|
|
1488
|
+
c.display.error(mResult.summary);
|
|
1489
|
+
}
|
|
1490
|
+
return { handled: true, newHistory: mResult.history };
|
|
1491
|
+
}
|
|
1492
|
+
if (input.startsWith(':council ') || input === ':council') {
|
|
1493
|
+
const councilTask = input.slice(':council '.length).trim();
|
|
1494
|
+
if (!councilTask) {
|
|
1495
|
+
c.display.warning('Usage: :council <task> -- runs 2-3 read-only domain specialists in parallel, then synthesizes their reports.');
|
|
1496
|
+
return { handled: true };
|
|
1497
|
+
}
|
|
1498
|
+
const { runMixtureOfAgents } = await Promise.resolve().then(() => __importStar(require('../agent/mixture.js')));
|
|
1499
|
+
const councilResult = await runMixtureOfAgents({
|
|
1500
|
+
provider: buildProvider(c.display), task: councilTask, context: c.ctx, display: c.display,
|
|
1501
|
+
});
|
|
1502
|
+
if (councilResult.success) {
|
|
1503
|
+
c.display.summary(councilResult.summary, councilResult.turns, councilResult.toolCallCount);
|
|
1504
|
+
printUsageFooter(c.display, councilResult.usage, councilResult.costUsd);
|
|
1505
|
+
}
|
|
1506
|
+
else {
|
|
1507
|
+
c.display.error(councilResult.summary);
|
|
1508
|
+
}
|
|
1509
|
+
return { handled: true };
|
|
1510
|
+
}
|
|
1511
|
+
// ── :ecclesia — 5-agent independent research council (research/council.ts).
|
|
1512
|
+
// Distinct from :council above (mixture-of-agents over the current task):
|
|
1513
|
+
// the Ecclesia runs N agents that research a topic WITHOUT seeing each
|
|
1514
|
+
// other's findings, then one synthesis call reconciles them into a verdict.
|
|
1515
|
+
if (input.startsWith(':ecclesia ') || input === ':ecclesia') {
|
|
1516
|
+
let topic = input.slice(':ecclesia '.length).trim();
|
|
1517
|
+
if (!topic) {
|
|
1518
|
+
c.display.warning('Usage: :ecclesia <topic> [--panel <model>] [--seats <n>] -- N independent research agents (default 5) + a synthesis verdict, saved to council/*.md|.html.');
|
|
1519
|
+
return { handled: true };
|
|
1520
|
+
}
|
|
1521
|
+
let panelModel;
|
|
1522
|
+
let panelSize;
|
|
1523
|
+
const panelMatch = topic.match(/\s--panel\s+(\S+)/);
|
|
1524
|
+
if (panelMatch) {
|
|
1525
|
+
panelModel = panelMatch[1];
|
|
1526
|
+
topic = topic.replace(panelMatch[0], '').trim();
|
|
1527
|
+
}
|
|
1528
|
+
const seatsMatch = topic.match(/\s--seats\s+(\d+)/);
|
|
1529
|
+
if (seatsMatch) {
|
|
1530
|
+
panelSize = Number(seatsMatch[1]);
|
|
1531
|
+
topic = topic.replace(seatsMatch[0], '').trim();
|
|
1532
|
+
}
|
|
1533
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(`\n Convening the Ecclesia on "${topic}"…\n`));
|
|
1534
|
+
try {
|
|
1535
|
+
const { runCouncil } = await Promise.resolve().then(() => __importStar(require('../research/council.js')));
|
|
1536
|
+
const res = await runCouncil({
|
|
1537
|
+
projectRoot: c.ctx.root, topic,
|
|
1538
|
+
synthesisProvider: buildProvider(c.display),
|
|
1539
|
+
context: c.ctx, permissions: c.permissions, display: c.display,
|
|
1540
|
+
panelSize, panelModel,
|
|
1541
|
+
configuredModel: c.providerConfig.model,
|
|
1542
|
+
});
|
|
1543
|
+
console.log(chalk_1.default.hex('#5a9e6e')(` ✓ Ecclesia verdict written: ${res.path}`));
|
|
1544
|
+
console.log(chalk_1.default.hex('#5a9e6e')(` HTML: ${res.htmlPath}`));
|
|
1545
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` ${res.panelSize} seats on ${res.panelModel}.`));
|
|
1546
|
+
if (res.agentFailures > 0) {
|
|
1547
|
+
c.display.warning(`${res.agentFailures} of ${res.panelSize} panel agent(s) failed — verdict is based on the rest.`);
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
catch (e) {
|
|
1551
|
+
console.log(chalk_1.default.hex('#b15439')(` ✗ ${String(e)}\n`));
|
|
1552
|
+
}
|
|
1553
|
+
return { handled: true };
|
|
1554
|
+
}
|
|
1555
|
+
// ── :btw — Side channel question (read-only, no history) ────────────────
|
|
1556
|
+
if (input.startsWith(':btw ')) {
|
|
1557
|
+
const question = input.slice(5).trim();
|
|
1558
|
+
if (!question) {
|
|
1559
|
+
c.display.warning('Usage: :btw <question> — ask a quick side question without interrupting the current task.');
|
|
1560
|
+
return { handled: true };
|
|
1561
|
+
}
|
|
1562
|
+
const { runBtwQuery, renderBtwAnswer } = await Promise.resolve().then(() => __importStar(require('../repl/side-channel.js')));
|
|
1563
|
+
c.display.agentThinking();
|
|
1564
|
+
const result = await runBtwQuery(question, buildProvider(c.display), c.ctx);
|
|
1565
|
+
console.log(renderBtwAnswer(result.answer, result.tokens));
|
|
1566
|
+
return { handled: true };
|
|
1567
|
+
}
|
|
1192
1568
|
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')));
|
|
1569
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(help_data_js_1.HELP_TEXT.join('\n')));
|
|
1247
1570
|
return { handled: true };
|
|
1248
1571
|
}
|
|
1249
1572
|
// ── Session commands ─────────────────────────────────────────────────────
|
|
1250
1573
|
if (input === ':id') {
|
|
1251
1574
|
const cs = c.chatState;
|
|
1252
1575
|
if (cs.activeChatId) {
|
|
1253
|
-
console.log(chalk_1.default.hex(
|
|
1576
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(`\n Chat ID: ${chalk_1.default.hex('#cc785c')(cs.activeChatId)}`));
|
|
1254
1577
|
if (cs.activeChatTitle)
|
|
1255
|
-
console.log(chalk_1.default.hex(
|
|
1256
|
-
console.log(chalk_1.default.hex(
|
|
1578
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` Title: ${cs.activeChatTitle}`));
|
|
1579
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(` Turns: ${Math.floor(cs.activeChatHistory.length / 2)}\n`));
|
|
1257
1580
|
}
|
|
1258
1581
|
else {
|
|
1259
|
-
console.log(chalk_1.default.hex(
|
|
1582
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('\n No active session (--no-session mode).\n'));
|
|
1260
1583
|
}
|
|
1261
1584
|
return { handled: true };
|
|
1262
1585
|
}
|
|
1263
1586
|
if (input === ':sessions') {
|
|
1264
1587
|
const sessions = session_store_js_1.sessionStore.listSessions(c.chatState.projectRoot);
|
|
1265
1588
|
if (sessions.length === 0) {
|
|
1266
|
-
console.log(chalk_1.default.hex(
|
|
1589
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('\n No saved sessions.\n'));
|
|
1267
1590
|
}
|
|
1268
1591
|
else {
|
|
1269
1592
|
console.log(chalk_1.default.hex('#cc785c').bold('\n Saved sessions:\n'));
|
|
@@ -1272,8 +1595,8 @@ async function handleReplCommand(input, c) {
|
|
|
1272
1595
|
const turns = Math.floor(s.history.length / 2);
|
|
1273
1596
|
const marker = s.id === c.chatState.activeChatId ? chalk_1.default.hex('#5a9e6e')(' ← current') : '';
|
|
1274
1597
|
console.log(` ${chalk_1.default.hex('#cc785c')(s.id.padEnd(20))} ` +
|
|
1275
|
-
`${chalk_1.default.hex(
|
|
1276
|
-
`${chalk_1.default.hex(
|
|
1598
|
+
`${chalk_1.default.hex(diamond_js_1.TEXT_HEX)(s.title.slice(0, 40).padEnd(41))} ` +
|
|
1599
|
+
`${chalk_1.default.hex(diamond_js_1.FAINT_HEX)(`${turns}t · ${updated}`)}${marker}`);
|
|
1277
1600
|
}
|
|
1278
1601
|
console.log();
|
|
1279
1602
|
}
|
|
@@ -1282,7 +1605,7 @@ async function handleReplCommand(input, c) {
|
|
|
1282
1605
|
if (input === ':resume' || input === ':resume ') {
|
|
1283
1606
|
const latest = session_store_js_1.sessionStore.findLatestSession(c.chatState.projectRoot);
|
|
1284
1607
|
if (!latest) {
|
|
1285
|
-
console.log(chalk_1.default.hex(
|
|
1608
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('\n No saved sessions to resume.\n'));
|
|
1286
1609
|
return { handled: true };
|
|
1287
1610
|
}
|
|
1288
1611
|
console.log(chalk_1.default.hex('#5a9e6e')(`\n ↩ Resuming ${latest.id} — "${latest.title}" (${Math.floor(latest.history.length / 2)} turns)\n`));
|
|
@@ -1305,7 +1628,7 @@ async function handleReplCommand(input, c) {
|
|
|
1305
1628
|
}
|
|
1306
1629
|
if (input === ':history') {
|
|
1307
1630
|
const turns = Math.floor(c.chatState.activeChatHistory.length / 2);
|
|
1308
|
-
console.log(chalk_1.default.hex(
|
|
1631
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(`\n Current session: ${turns} turn${turns !== 1 ? 's' : ''} in history.\n`));
|
|
1309
1632
|
return { handled: true };
|
|
1310
1633
|
}
|
|
1311
1634
|
if (input === ':clear-history') {
|
|
@@ -1316,7 +1639,7 @@ async function handleReplCommand(input, c) {
|
|
|
1316
1639
|
const title = input.startsWith(':save ') ? input.slice(':save '.length).trim() : undefined;
|
|
1317
1640
|
const cs = c.chatState;
|
|
1318
1641
|
if (!cs.activeChatId) {
|
|
1319
|
-
console.log(chalk_1.default.hex(
|
|
1642
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('\n No active session to save (--no-session mode).\n'));
|
|
1320
1643
|
return { handled: true };
|
|
1321
1644
|
}
|
|
1322
1645
|
const session = await session_store_js_1.sessionStore.upsertSession(cs.projectRoot, cs.activeChatId, cs.activeChatHistory, title ?? cs.activeChatTitle);
|
|
@@ -1330,7 +1653,7 @@ async function handleReplCommand(input, c) {
|
|
|
1330
1653
|
console.log(chalk_1.default.hex('#5a9e6e')(`\n ✓ Deleted session ${id}\n`));
|
|
1331
1654
|
if (id === c.chatState.activeChatId) {
|
|
1332
1655
|
const newId = session_store_js_1.sessionStore.generateId();
|
|
1333
|
-
console.log(chalk_1.default.hex(
|
|
1656
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` Starting new session: ${newId}\n`));
|
|
1334
1657
|
return { handled: true, newChatId: newId, newHistory: [], newTitle: undefined };
|
|
1335
1658
|
}
|
|
1336
1659
|
}
|
|
@@ -1341,28 +1664,28 @@ async function handleReplCommand(input, c) {
|
|
|
1341
1664
|
}
|
|
1342
1665
|
// ── Model / API commands ─────────────────────────────────────────────────
|
|
1343
1666
|
if (input === ':context') {
|
|
1344
|
-
console.log(chalk_1.default.hex(
|
|
1345
|
-
console.log(chalk_1.default.hex(
|
|
1667
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(`\n Project: ${c.ctx.name} · ${c.ctx.language} · ${c.ctx.framework}`));
|
|
1668
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(` Root: ${c.ctx.root}\n`));
|
|
1346
1669
|
return { handled: true };
|
|
1347
1670
|
}
|
|
1348
1671
|
if (input === ':graph') {
|
|
1349
1672
|
const summary = (0, context_js_1.loadGraphSummary)(c.ctx.root);
|
|
1350
1673
|
if (!summary) {
|
|
1351
|
-
console.log(chalk_1.default.hex(
|
|
1674
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('\n No graph.json found. Run :graph refresh to extract.\n'));
|
|
1352
1675
|
}
|
|
1353
1676
|
else {
|
|
1354
1677
|
console.log(chalk_1.default.hex('#cc785c').bold('\n Codebase Knowledge Graph\n'));
|
|
1355
|
-
console.log(chalk_1.default.hex(
|
|
1678
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(summary));
|
|
1356
1679
|
console.log();
|
|
1357
1680
|
}
|
|
1358
1681
|
return { handled: true };
|
|
1359
1682
|
}
|
|
1360
1683
|
if (input === ':viz' || input === ':dashboard') {
|
|
1361
|
-
console.log(chalk_1.default.hex(
|
|
1684
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('\n Generating dashboard…\n'));
|
|
1362
1685
|
try {
|
|
1363
1686
|
const outPath = (0, index_js_1.generateDashboard)(c.ctx.root);
|
|
1364
1687
|
console.log(chalk_1.default.hex('#5a9e6e')(` ✓ Dashboard written to ${outPath}`));
|
|
1365
|
-
console.log(chalk_1.default.hex(
|
|
1688
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(' Opening in browser…\n'));
|
|
1366
1689
|
(0, index_js_1.openDashboard)(outPath);
|
|
1367
1690
|
}
|
|
1368
1691
|
catch (e) {
|
|
@@ -1370,185 +1693,11 @@ async function handleReplCommand(input, c) {
|
|
|
1370
1693
|
}
|
|
1371
1694
|
return { handled: true };
|
|
1372
1695
|
}
|
|
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
1696
|
if (input === ':plans') {
|
|
1548
1697
|
const { planStore } = await Promise.resolve().then(() => __importStar(require('../orchestration/plan-store.js')));
|
|
1549
1698
|
const plans = await planStore.list();
|
|
1550
1699
|
if (!plans.length) {
|
|
1551
|
-
console.log(chalk_1.default.hex(
|
|
1700
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('\n No execution plans found.\n'));
|
|
1552
1701
|
}
|
|
1553
1702
|
else {
|
|
1554
1703
|
console.log(chalk_1.default.hex('#cc785c').bold('\n Execution plans:\n'));
|
|
@@ -1558,51 +1707,70 @@ async function handleReplCommand(input, c) {
|
|
|
1558
1707
|
const statusColor = p.status === 'done' ? '#5a9e6e' : p.status === 'failed' ? '#b15439' : '#cc9e5c';
|
|
1559
1708
|
console.log(` ${chalk_1.default.hex(statusColor)(p.status.padEnd(8))} ` +
|
|
1560
1709
|
`${chalk_1.default.hex('#cc785c')(p.id.slice(0, 12).padEnd(14))} ` +
|
|
1561
|
-
`${chalk_1.default.hex(
|
|
1562
|
-
`${chalk_1.default.hex(
|
|
1710
|
+
`${chalk_1.default.hex(diamond_js_1.TEXT_HEX)(p.goal.slice(0, 50).padEnd(51))} ` +
|
|
1711
|
+
`${chalk_1.default.hex(diamond_js_1.FAINT_HEX)(`${p.steps.length}s · ${dur} · ${created}`)}`);
|
|
1563
1712
|
}
|
|
1564
1713
|
console.log();
|
|
1565
1714
|
}
|
|
1566
1715
|
return { handled: true };
|
|
1567
1716
|
}
|
|
1568
1717
|
if (input === ':graph refresh') {
|
|
1569
|
-
console.log(chalk_1.default.hex(
|
|
1718
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('\n Refreshing codebase graph...\n'));
|
|
1719
|
+
const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
|
|
1570
1720
|
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`));
|
|
1721
|
+
execSync('python3 -c "' +
|
|
1722
|
+
'import json,os,re,glob; ' +
|
|
1723
|
+
'root=\\\"' + c.ctx.root + '/src\\\"; ' +
|
|
1724
|
+
'print(\\\"Scanning\\\", root)" ', { stdio: 'inherit' });
|
|
1580
1725
|
}
|
|
1726
|
+
catch { /* ignore */ }
|
|
1581
1727
|
// Reload context graph
|
|
1582
1728
|
c.ctx.graphSummary = (0, context_js_1.loadGraphSummary)(c.ctx.root);
|
|
1583
1729
|
if (c.ctx.graphSummary) {
|
|
1584
1730
|
console.log(chalk_1.default.hex('#5a9e6e')(' ✓ Graph loaded and injected into context.\n'));
|
|
1585
1731
|
}
|
|
1586
1732
|
else {
|
|
1587
|
-
console.log(chalk_1.default.hex(
|
|
1733
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(' No graph.json found after refresh. Run graphify extract first.\n'));
|
|
1588
1734
|
}
|
|
1589
1735
|
return { handled: true };
|
|
1590
1736
|
}
|
|
1591
1737
|
// ── Provider wizard command ────────────────────────────────────────────────
|
|
1592
1738
|
if (input === ':provider' || input === '/provider') {
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1739
|
+
// Fullscreen prompt approach was unreliable — wizard's readline and TUI's
|
|
1740
|
+
// stdin handler collide. Instead, stop TUI input, let readline own stdin
|
|
1741
|
+
// completely, then restart TUI input. enterFullscreenPrompt resets the
|
|
1742
|
+
// scroll region so the wizard's output isn't crammed into the small scroll
|
|
1743
|
+
// area (numbers were invisible because the bottom block was overwriting them).
|
|
1744
|
+
const wasInputActive = tui_js_1.inputActive;
|
|
1745
|
+
if (wasInputActive) {
|
|
1746
|
+
(0, tui_js_1.stopInput)();
|
|
1747
|
+
(0, tui_js_1.enterFullscreenPrompt)();
|
|
1748
|
+
}
|
|
1749
|
+
try {
|
|
1750
|
+
const cfg = await (0, provider_wizard_js_1.runProviderWizard)(undefined, undefined);
|
|
1751
|
+
if (cfg) {
|
|
1752
|
+
runtimeConfig.model = cfg.model;
|
|
1753
|
+
runtimeConfig.baseUrl = cfg.baseUrl;
|
|
1754
|
+
runtimeConfig.apiKey = cfg.apiKey;
|
|
1755
|
+
c.providerConfig.model = cfg.model;
|
|
1756
|
+
c.providerConfig.baseUrl = cfg.baseUrl;
|
|
1757
|
+
c.providerConfig.apiKey = cfg.apiKey;
|
|
1758
|
+
resolved.model = cfg.model;
|
|
1759
|
+
resolved.baseUrl = cfg.baseUrl;
|
|
1760
|
+
(0, tui_js_1.writeOutput)(chalk_1.default.hex('#5a9e6e')(` ✓ Now using ${cfg.provider} · ${cfg.model}`));
|
|
1761
|
+
// Update the TUI status line so the provider/model change is immediately visible.
|
|
1762
|
+
(0, tui_js_1.setStatusLine)([cfg.provider, cfg.model, permissionLevel].filter(Boolean).join(' · '));
|
|
1763
|
+
if (fileConfig.model && fileConfig.model !== cfg.model) {
|
|
1764
|
+
(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` +
|
|
1765
|
+
` Remove the "model" field from .aura.json (or set it to ${cfg.model}) to keep this choice.`));
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
finally {
|
|
1770
|
+
if (wasInputActive) {
|
|
1771
|
+
(0, tui_js_1.exitFullscreenPrompt)();
|
|
1772
|
+
(0, tui_js_1.startInput)();
|
|
1773
|
+
}
|
|
1606
1774
|
}
|
|
1607
1775
|
return { handled: true };
|
|
1608
1776
|
}
|
|
@@ -1613,9 +1781,9 @@ async function handleReplCommand(input, c) {
|
|
|
1613
1781
|
return acc;
|
|
1614
1782
|
}, {});
|
|
1615
1783
|
for (const [provider, models] of Object.entries(byProvider)) {
|
|
1616
|
-
console.log(chalk_1.default.hex(
|
|
1784
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(`\n ${provider}`));
|
|
1617
1785
|
for (const m of models) {
|
|
1618
|
-
console.log(` ${chalk_1.default.hex('#cc785c')(m.id.padEnd(45))} ${chalk_1.default.hex(
|
|
1786
|
+
console.log(` ${chalk_1.default.hex('#cc785c')(m.id.padEnd(45))} ${chalk_1.default.hex(diamond_js_1.FAINT_HEX)(m.speed)}`);
|
|
1619
1787
|
}
|
|
1620
1788
|
}
|
|
1621
1789
|
console.log();
|
|
@@ -1638,7 +1806,22 @@ async function handleReplCommand(input, c) {
|
|
|
1638
1806
|
const newKey = input.slice(sep.length).trim();
|
|
1639
1807
|
runtimeConfig.apiKey = newKey;
|
|
1640
1808
|
c.providerConfig.apiKey = newKey;
|
|
1641
|
-
|
|
1809
|
+
// Persist it so it survives across sessions (fixes "type the key every
|
|
1810
|
+
// time"). Save under the env-var name for the current provider, and also
|
|
1811
|
+
// set that env var live so getApiKey resolves it this session.
|
|
1812
|
+
const envName = globalCfg?.apiKeyEnv || envNameForModel(resolved.model ?? '');
|
|
1813
|
+
if (envName) {
|
|
1814
|
+
try {
|
|
1815
|
+
const p = (0, key_store_js_1.saveKey)(envName, newKey);
|
|
1816
|
+
console.log(chalk_1.default.hex('#5a9e6e')(` ✓ API key saved as ${envName} → ${p} (persists across sessions).`));
|
|
1817
|
+
}
|
|
1818
|
+
catch (e) {
|
|
1819
|
+
console.log(chalk_1.default.hex('#5a9e6e')(' ✓ API key set for current session (could not persist: ' + String(e) + ').'));
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
else {
|
|
1823
|
+
console.log(chalk_1.default.hex('#5a9e6e')(' ✓ API key set for current session.'));
|
|
1824
|
+
}
|
|
1642
1825
|
return { handled: true };
|
|
1643
1826
|
}
|
|
1644
1827
|
if (input === '/clear' || input === '/reset') {
|
|
@@ -1650,42 +1833,10 @@ async function handleReplCommand(input, c) {
|
|
|
1650
1833
|
console.log(chalk_1.default.hex('#5a9e6e')(' ✓ Session stats reset'));
|
|
1651
1834
|
return { handled: true };
|
|
1652
1835
|
}
|
|
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') {
|
|
1836
|
+
if (input === '/stats' || input === '/usage') {
|
|
1686
1837
|
const u = c.cumulative;
|
|
1687
1838
|
const total = u.inputTokens + u.outputTokens;
|
|
1688
|
-
console.log(chalk_1.default.hex(
|
|
1839
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)([
|
|
1689
1840
|
'',
|
|
1690
1841
|
` Session usage:`,
|
|
1691
1842
|
` Turns: ${u.turns}`,
|
|
@@ -1698,11 +1849,19 @@ async function handleReplCommand(input, c) {
|
|
|
1698
1849
|
].join('\n')));
|
|
1699
1850
|
return { handled: true };
|
|
1700
1851
|
}
|
|
1852
|
+
if (input === '/context') {
|
|
1853
|
+
const u = c.cumulative;
|
|
1854
|
+
const h = c.healthTracker.snapshot(u.inputTokens, u.outputTokens);
|
|
1855
|
+
h.turnCount = u.turns;
|
|
1856
|
+
h.toolCallCount = u.toolCalls;
|
|
1857
|
+
c.display.contextDashboard?.(h);
|
|
1858
|
+
return { handled: true };
|
|
1859
|
+
}
|
|
1701
1860
|
// ── Workflow commands ──────────────────────────────────────────────────────
|
|
1702
1861
|
if (input === ':workflows') {
|
|
1703
1862
|
const workflows = await (0, engine_js_1.listWorkflows)();
|
|
1704
1863
|
if (workflows.length === 0) {
|
|
1705
|
-
console.log(chalk_1.default.hex(
|
|
1864
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('\n No saved workflows.\n'));
|
|
1706
1865
|
}
|
|
1707
1866
|
else {
|
|
1708
1867
|
console.log(chalk_1.default.hex('#cc785c').bold('\n Saved workflows:\n'));
|
|
@@ -1712,9 +1871,9 @@ async function handleReplCommand(input, c) {
|
|
|
1712
1871
|
const totalSteps = ws.definition.steps.length;
|
|
1713
1872
|
const statusColor = ws.status === 'done' ? '#5a9e6e' : ws.status === 'failed' ? '#b15439' : '#cc785c';
|
|
1714
1873
|
console.log(` ${chalk_1.default.hex('#cc785c')(ws.definition.id.padEnd(24))} ` +
|
|
1715
|
-
`${chalk_1.default.hex(
|
|
1874
|
+
`${chalk_1.default.hex(diamond_js_1.TEXT_HEX)(ws.definition.name.slice(0, 36).padEnd(37))} ` +
|
|
1716
1875
|
`${chalk_1.default.hex(statusColor)(ws.status.padEnd(8))} ` +
|
|
1717
|
-
`${chalk_1.default.hex(
|
|
1876
|
+
`${chalk_1.default.hex(diamond_js_1.FAINT_HEX)(`${doneSteps}/${totalSteps} steps · ${created}`)}`);
|
|
1718
1877
|
}
|
|
1719
1878
|
console.log();
|
|
1720
1879
|
}
|
|
@@ -1772,7 +1931,7 @@ async function handleReplCommand(input, c) {
|
|
|
1772
1931
|
}
|
|
1773
1932
|
else {
|
|
1774
1933
|
console.log(chalk_1.default.hex('#b15439').bold(`\n ✗ ${finalState.outcome}`));
|
|
1775
|
-
console.log(chalk_1.default.hex(
|
|
1934
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` Resume with: :resume-workflow ${finalState.definition.id}\n`));
|
|
1776
1935
|
}
|
|
1777
1936
|
return { handled: true };
|
|
1778
1937
|
}
|
|
@@ -1810,7 +1969,7 @@ async function handleReplCommand(input, c) {
|
|
|
1810
1969
|
}
|
|
1811
1970
|
else {
|
|
1812
1971
|
console.log(chalk_1.default.hex('#b15439').bold(`\n ✗ ${finalState.outcome}`));
|
|
1813
|
-
console.log(chalk_1.default.hex(
|
|
1972
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(` Resume with: :resume-workflow ${finalState.definition.id}\n`));
|
|
1814
1973
|
}
|
|
1815
1974
|
return { handled: true };
|
|
1816
1975
|
}
|
|
@@ -1919,29 +2078,29 @@ async function runArchitectPlan(task, provider, ctx, display) {
|
|
|
1919
2078
|
});
|
|
1920
2079
|
// Display result
|
|
1921
2080
|
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(
|
|
2081
|
+
console.log(chalk_1.default.hex(diamond_js_1.TEXT_HEX)(` Task: ${blueprint.task}`));
|
|
2082
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(` ID: ${blueprint.id}\n`));
|
|
1924
2083
|
if (blueprint.files.length > 0) {
|
|
1925
2084
|
console.log(chalk_1.default.hex('#cc785c').bold(' Files:\n'));
|
|
1926
2085
|
for (const f of blueprint.files) {
|
|
1927
2086
|
console.log(` ${chalk_1.default.hex('#cc785c')(f.path)}`);
|
|
1928
|
-
console.log(` ${chalk_1.default.hex(
|
|
2087
|
+
console.log(` ${chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(f.purpose)}`);
|
|
1929
2088
|
if (f.exports.length > 0)
|
|
1930
|
-
console.log(` ${chalk_1.default.hex(
|
|
2089
|
+
console.log(` ${chalk_1.default.hex(diamond_js_1.FAINT_HEX)(`exports: ${f.exports.join(', ')}`)}`);
|
|
1931
2090
|
if (f.interfaces.length > 0)
|
|
1932
|
-
console.log(` ${chalk_1.default.hex(
|
|
2091
|
+
console.log(` ${chalk_1.default.hex(diamond_js_1.FAINT_HEX)(`interfaces: ${f.interfaces.join(', ')}`)}`);
|
|
1933
2092
|
}
|
|
1934
2093
|
}
|
|
1935
2094
|
if (blueprint.dataModels.length > 0) {
|
|
1936
2095
|
console.log(chalk_1.default.hex('#cc785c').bold('\n Data Models:\n'));
|
|
1937
2096
|
for (const dm of blueprint.dataModels) {
|
|
1938
|
-
console.log(` ${chalk_1.default.hex('#cc785c')(dm.name)} — ${chalk_1.default.hex(
|
|
2097
|
+
console.log(` ${chalk_1.default.hex('#cc785c')(dm.name)} — ${chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(dm.description)}`);
|
|
1939
2098
|
}
|
|
1940
2099
|
}
|
|
1941
2100
|
if (blueprint.risks.length > 0) {
|
|
1942
2101
|
console.log(chalk_1.default.hex('#b15439').bold('\n Risks:\n'));
|
|
1943
2102
|
for (const risk of blueprint.risks) {
|
|
1944
|
-
console.log(` ${chalk_1.default.hex('#b15439')('⚠')} ${chalk_1.default.hex(
|
|
2103
|
+
console.log(` ${chalk_1.default.hex('#b15439')('⚠')} ${chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)(risk)}`);
|
|
1945
2104
|
}
|
|
1946
2105
|
}
|
|
1947
2106
|
console.log(chalk_1.default.hex('#5a9e6e')('\n Blueprint saved. No files were modified.'));
|
|
@@ -1961,16 +2120,20 @@ async function runOrchestratedTask(task, provider, ctx, display, forceOrchestrat
|
|
|
1961
2120
|
// If --plan flag, show plan and ask for confirmation
|
|
1962
2121
|
if (argv.plan === true) {
|
|
1963
2122
|
display.showPlan?.(plan);
|
|
1964
|
-
// Use a simple readline prompt for confirmation
|
|
1965
|
-
|
|
2123
|
+
// Use a simple readline prompt for confirmation — reuse the REPL's
|
|
2124
|
+
// readline when one is active (second interface double-echoes and
|
|
2125
|
+
// pauses stdin on close).
|
|
2126
|
+
const sharedRl = (0, permissions_js_1.getSharedReadline)();
|
|
2127
|
+
const rl = sharedRl ?? readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1966
2128
|
const approved = await new Promise(resolve => {
|
|
1967
2129
|
rl.question(chalk_1.default.hex('#cc785c')('\n Run this plan? [y/N] '), answer => {
|
|
1968
|
-
|
|
2130
|
+
if (!sharedRl)
|
|
2131
|
+
rl.close();
|
|
1969
2132
|
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
1970
2133
|
});
|
|
1971
2134
|
});
|
|
1972
2135
|
if (!approved) {
|
|
1973
|
-
console.log(chalk_1.default.hex(
|
|
2136
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(' Plan cancelled.\n'));
|
|
1974
2137
|
process.exit(0);
|
|
1975
2138
|
}
|
|
1976
2139
|
}
|
|
@@ -1994,51 +2157,49 @@ async function runOrchestratedTask(task, provider, ctx, display, forceOrchestrat
|
|
|
1994
2157
|
display.summary(executedPlan.outcome, executedPlan.steps.length, 0);
|
|
1995
2158
|
}
|
|
1996
2159
|
const totalTokens = executedPlan.totalTokens ?? 0;
|
|
1997
|
-
console.log(chalk_1.default.hex(
|
|
2160
|
+
console.log(chalk_1.default.hex(diamond_js_1.FAINT_HEX)(` ↳ ${totalTokens.toLocaleString()} tokens · ${executedPlan.steps.length} steps · status: ${executedPlan.status}`));
|
|
1998
2161
|
}
|
|
1999
|
-
function printUsageFooter(display, usage, costUsd
|
|
2162
|
+
function printUsageFooter(display, usage, costUsd) {
|
|
2000
2163
|
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
|
-
}
|
|
2164
|
+
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)}`));
|
|
2165
|
+
}
|
|
2166
|
+
/**
|
|
2167
|
+
* Read a task summary aloud (the "Aura talks back" half of the voice loop).
|
|
2168
|
+
* Best-effort: strips code/markdown, caps length so a long report doesn't
|
|
2169
|
+
* monologue, and never throws into the caller (a TTS/network failure must
|
|
2170
|
+
* not break a successful task).
|
|
2171
|
+
*/
|
|
2172
|
+
async function speakSummary(text) {
|
|
2173
|
+
if (!text || !text.trim())
|
|
2174
|
+
return;
|
|
2175
|
+
// Strip fenced code blocks, inline code, markdown markers, and collapse
|
|
2176
|
+
// whitespace — TTS should read the prose, not backticks and hashes.
|
|
2177
|
+
const spoken = text
|
|
2178
|
+
.replace(/```[\s\S]*?```/g, ' (code omitted) ')
|
|
2179
|
+
.replace(/`[^`]*`/g, '')
|
|
2180
|
+
.replace(/[#*_>|]/g, ' ')
|
|
2181
|
+
.replace(/\s+/g, ' ')
|
|
2182
|
+
.trim()
|
|
2183
|
+
.slice(0, 600);
|
|
2184
|
+
if (!spoken)
|
|
2185
|
+
return;
|
|
2186
|
+
try {
|
|
2187
|
+
const { speakText } = await Promise.resolve().then(() => __importStar(require('../tools/dictate.js')));
|
|
2188
|
+
await speakText(spoken);
|
|
2029
2189
|
}
|
|
2190
|
+
catch { /* speech is best-effort — never break the task on TTS failure */ }
|
|
2030
2191
|
}
|
|
2031
2192
|
function printHelp() {
|
|
2032
2193
|
console.log(`
|
|
2033
|
-
${chalk_1.default.hex('#cc785c').bold(' aura')} ${chalk_1.default.hex(
|
|
2194
|
+
${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
2195
|
|
|
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(
|
|
2196
|
+
${chalk_1.default.hex(diamond_js_1.FAINT_HEX)('Usage:')}
|
|
2197
|
+
aura ${chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('"<task>"')} Run a single task
|
|
2198
|
+
aura ${chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('serve')} Start the HTTP API server
|
|
2199
|
+
aura ${chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('--interactive')} Start interactive REPL
|
|
2200
|
+
aura ${chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX)('--models')} List available models
|
|
2040
2201
|
|
|
2041
|
-
${chalk_1.default.hex(
|
|
2202
|
+
${chalk_1.default.hex(diamond_js_1.FAINT_HEX)('Options:')}
|
|
2042
2203
|
--model, -m <id> Model to use (default: from ~/.config/aura-code/config.json)
|
|
2043
2204
|
--api-key <key> API key (overrides env var)
|
|
2044
2205
|
--base-url <url> Custom API endpoint (for Ollama, proxies, etc.)
|
|
@@ -2047,12 +2208,10 @@ ${chalk_1.default.hex('#cc785c').bold(' aura')} ${chalk_1.default.hex('#8a7768'
|
|
|
2047
2208
|
--cwd <path> Working directory (default: current)
|
|
2048
2209
|
--models List all known model IDs
|
|
2049
2210
|
--no-session Disable conversation history persistence
|
|
2050
|
-
--once Run single task and exit (no REPL after)
|
|
2051
2211
|
--new-session Force a fresh session (ignore any prior history)
|
|
2052
2212
|
--resume [id] Resume latest session, or a specific session by ID
|
|
2053
2213
|
--chat-id <id> Attach to a specific chat ID (creates if missing)
|
|
2054
2214
|
--list-sessions List all saved sessions for this project
|
|
2055
|
-
--setup Run the provider setup wizard before entering REPL
|
|
2056
2215
|
--no-setup Skip the first-run setup wizard
|
|
2057
2216
|
--reset-setup Wipe saved config and re-run the setup wizard
|
|
2058
2217
|
--orchestrate Force multi-agent orchestration mode
|
|
@@ -2064,11 +2223,14 @@ ${chalk_1.default.hex('#cc785c').bold(' aura')} ${chalk_1.default.hex('#8a7768'
|
|
|
2064
2223
|
--verify Verify output after task; retry up to --max-verify-retries times
|
|
2065
2224
|
--max-verify-retries <n> Max verification retries (default: 3)
|
|
2066
2225
|
--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:
|
|
2226
|
+
--max-turns <n> Max agent loop turns before stopping (default: sized by task shape)
|
|
2227
|
+
--moa Mixture of agents: parallel read-only domain perspectives + synthesis (exploratory tasks only)
|
|
2068
2228
|
--analyze Mine session history for weakness patterns; save report
|
|
2069
|
-
--stats Show episode stats — completion rate, avg duration, top model, tokens
|
|
2070
2229
|
--propose-harness Generate system-prompt patches from weakness report
|
|
2071
2230
|
--apply-harness <id> Apply a proposal patch; reverts if tests fail
|
|
2231
|
+
--doctor Scan Aura itself for issues (build, config, deps, env, git)
|
|
2232
|
+
--doctor --fix Scan and attempt auto-repairs (rebuild, restore, reinstall)
|
|
2233
|
+
--doctor --offline Skip the network version check
|
|
2072
2234
|
--workflow <name> ... Create and run a sequential workflow with named steps
|
|
2073
2235
|
--resume-workflow <id> Resume a paused/failed workflow from last completed step
|
|
2074
2236
|
--workflows List all persisted workflows
|
|
@@ -2080,7 +2242,7 @@ ${chalk_1.default.hex('#cc785c').bold(' aura')} ${chalk_1.default.hex('#8a7768'
|
|
|
2080
2242
|
--fallback <model> Fallback model if primary exhausts retries (repeatable)
|
|
2081
2243
|
--verify Enable post-task verification with automatic retries
|
|
2082
2244
|
|
|
2083
|
-
${chalk_1.default.hex(
|
|
2245
|
+
${chalk_1.default.hex(diamond_js_1.FAINT_HEX)('Resilience:')}
|
|
2084
2246
|
All API calls automatically:
|
|
2085
2247
|
1. Honour Retry-After / Google's retryDelay on 429s
|
|
2086
2248
|
2. Back off with exponential + jitter (capped at 60s)
|
|
@@ -2088,7 +2250,7 @@ ${chalk_1.default.hex('#cc785c').bold(' aura')} ${chalk_1.default.hex('#8a7768'
|
|
|
2088
2250
|
4. Fail over to the next --fallback model if retries exhaust
|
|
2089
2251
|
5. Pace requests when --rate-limit-rpm / --rate-limit-tpm is set
|
|
2090
2252
|
|
|
2091
|
-
${chalk_1.default.hex(
|
|
2253
|
+
${chalk_1.default.hex(diamond_js_1.FAINT_HEX)('Project config (.aura.json):')}
|
|
2092
2254
|
{
|
|
2093
2255
|
"model": "claude-sonnet-4-5-20251001",
|
|
2094
2256
|
"mode": "auto",
|
|
@@ -2099,8 +2261,8 @@ ${chalk_1.default.hex('#cc785c').bold(' aura')} ${chalk_1.default.hex('#8a7768'
|
|
|
2099
2261
|
"apiKeyEnv": "DEEPSEEK_API_KEY",
|
|
2100
2262
|
"prefixes": ["deepseek/"],
|
|
2101
2263
|
"models": [
|
|
2102
|
-
{ "id": "deepseek/deepseek-
|
|
2103
|
-
{ "id": "deepseek/deepseek-
|
|
2264
|
+
{ "id": "deepseek/deepseek-chat", "name": "DeepSeek Chat", "speed": "Fast" },
|
|
2265
|
+
{ "id": "deepseek/deepseek-reasoner", "name": "DeepSeek R1", "speed": "Reasoning" }
|
|
2104
2266
|
]
|
|
2105
2267
|
}
|
|
2106
2268
|
],
|
|
@@ -2114,19 +2276,21 @@ ${chalk_1.default.hex('#cc785c').bold(' aura')} ${chalk_1.default.hex('#8a7768'
|
|
|
2114
2276
|
CLI flags always override .aura.json.
|
|
2115
2277
|
Custom providers are OpenAI-compatible endpoints.
|
|
2116
2278
|
|
|
2117
|
-
${chalk_1.default.hex(
|
|
2279
|
+
${chalk_1.default.hex(diamond_js_1.FAINT_HEX)('Model examples:')}
|
|
2118
2280
|
aura -m claude-opus-4-5-20251001 "refactor auth"
|
|
2119
2281
|
aura -m gpt-4o "add unit tests"
|
|
2120
2282
|
aura -m gemini-2.5-pro --rate-limit-rpm 20 "explain this codebase"
|
|
2121
2283
|
aura -m ollama/llama3.2 "local model, no API key needed"
|
|
2122
2284
|
|
|
2123
|
-
${chalk_1.default.hex(
|
|
2285
|
+
${chalk_1.default.hex(diamond_js_1.FAINT_HEX)('API keys (set as env vars):')}
|
|
2124
2286
|
ANTHROPIC_API_KEY Claude models
|
|
2125
2287
|
OPENAI_API_KEY GPT models
|
|
2126
2288
|
GOOGLE_API_KEY Gemini models
|
|
2127
2289
|
XAI_API_KEY Grok models
|
|
2128
2290
|
OPENROUTER_API_KEY OpenRouter (access to all models)
|
|
2129
2291
|
XIAOMI_API_KEY Xiaomi MiMo
|
|
2292
|
+
ZHIPU_API_KEY Zhipu GLM (Z.ai) — glm-* general endpoint, zhipu-coding/<model> Coding Plan
|
|
2293
|
+
ZHIPU_BASE_URL Override Zhipu endpoint (default https://api.z.ai/api/paas/v4)
|
|
2130
2294
|
AURA_MODEL Default model (overridden by --model)
|
|
2131
2295
|
AURA_API_RPM Default request rate limit
|
|
2132
2296
|
AURA_API_TPM Default token rate limit (Gemini)
|
|
@@ -2134,7 +2298,10 @@ ${chalk_1.default.hex('#cc785c').bold(' aura')} ${chalk_1.default.hex('#8a7768'
|
|
|
2134
2298
|
AURA_FALLBACK_MODEL Comma-separated fallback models
|
|
2135
2299
|
`);
|
|
2136
2300
|
}
|
|
2137
|
-
if (argv.
|
|
2301
|
+
if (argv.doctor === true) {
|
|
2302
|
+
// --doctor runs async above and exits on completion; don't start main().
|
|
2303
|
+
}
|
|
2304
|
+
else if (argv._[0] === 'serve') {
|
|
2138
2305
|
const port = Number(argv.port ?? argv.p ?? 7337);
|
|
2139
2306
|
(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
2307
|
}
|