aura-code 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +137 -0
- package/dist/agent/context.d.ts +13 -0
- package/dist/agent/context.js +183 -0
- package/dist/agent/context.js.map +1 -0
- package/dist/agent/loop.d.ts +60 -0
- package/dist/agent/loop.js +228 -0
- package/dist/agent/loop.js.map +1 -0
- package/dist/agent/session-store.d.ts +30 -0
- package/dist/agent/session-store.js +178 -0
- package/dist/agent/session-store.js.map +1 -0
- package/dist/agent/spawner.d.ts +57 -0
- package/dist/agent/spawner.js +112 -0
- package/dist/agent/spawner.js.map +1 -0
- package/dist/agent/system-prompt.d.ts +3 -0
- package/dist/agent/system-prompt.js +100 -0
- package/dist/agent/system-prompt.js.map +1 -0
- package/dist/architect/engine.d.ts +47 -0
- package/dist/architect/engine.js +217 -0
- package/dist/architect/engine.js.map +1 -0
- package/dist/architect/types.d.ts +61 -0
- package/dist/architect/types.js +6 -0
- package/dist/architect/types.js.map +1 -0
- package/dist/cli/diamond.d.ts +1 -0
- package/dist/cli/diamond.js +23 -0
- package/dist/cli/diamond.js.map +1 -0
- package/dist/cli/display.d.ts +44 -0
- package/dist/cli/display.js +234 -0
- package/dist/cli/display.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +1734 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config/defaults.d.ts +46 -0
- package/dist/config/defaults.js +106 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/project-config.d.ts +90 -0
- package/dist/config/project-config.js +141 -0
- package/dist/config/project-config.js.map +1 -0
- package/dist/harness/proposer.d.ts +26 -0
- package/dist/harness/proposer.js +204 -0
- package/dist/harness/proposer.js.map +1 -0
- package/dist/harness/weakness-miner.d.ts +28 -0
- package/dist/harness/weakness-miner.js +359 -0
- package/dist/harness/weakness-miner.js.map +1 -0
- package/dist/integrations/harnesses.d.ts +23 -0
- package/dist/integrations/harnesses.js +161 -0
- package/dist/integrations/harnesses.js.map +1 -0
- package/dist/orchestration/competence.d.ts +53 -0
- package/dist/orchestration/competence.js +181 -0
- package/dist/orchestration/competence.js.map +1 -0
- package/dist/orchestration/executor.d.ts +42 -0
- package/dist/orchestration/executor.js +206 -0
- package/dist/orchestration/executor.js.map +1 -0
- package/dist/orchestration/index.d.ts +20 -0
- package/dist/orchestration/index.js +39 -0
- package/dist/orchestration/index.js.map +1 -0
- package/dist/orchestration/orchestrator-prompts.d.ts +11 -0
- package/dist/orchestration/orchestrator-prompts.js +153 -0
- package/dist/orchestration/orchestrator-prompts.js.map +1 -0
- package/dist/orchestration/orchestrator.d.ts +27 -0
- package/dist/orchestration/orchestrator.js +169 -0
- package/dist/orchestration/orchestrator.js.map +1 -0
- package/dist/orchestration/plan-store.d.ts +53 -0
- package/dist/orchestration/plan-store.js +176 -0
- package/dist/orchestration/plan-store.js.map +1 -0
- package/dist/orchestration/router-prompts.d.ts +6 -0
- package/dist/orchestration/router-prompts.js +55 -0
- package/dist/orchestration/router-prompts.js.map +1 -0
- package/dist/orchestration/router.d.ts +23 -0
- package/dist/orchestration/router.js +70 -0
- package/dist/orchestration/router.js.map +1 -0
- package/dist/orchestration/ruby-detect.d.ts +6 -0
- package/dist/orchestration/ruby-detect.js +130 -0
- package/dist/orchestration/ruby-detect.js.map +1 -0
- package/dist/orchestration/ruby-types.d.ts +44 -0
- package/dist/orchestration/ruby-types.js +6 -0
- package/dist/orchestration/ruby-types.js.map +1 -0
- package/dist/orchestration/specialist-prompts.d.ts +19 -0
- package/dist/orchestration/specialist-prompts.js +143 -0
- package/dist/orchestration/specialist-prompts.js.map +1 -0
- package/dist/orchestration/specialists.d.ts +66 -0
- package/dist/orchestration/specialists.js +175 -0
- package/dist/orchestration/specialists.js.map +1 -0
- package/dist/orchestration/types.d.ts +82 -0
- package/dist/orchestration/types.js +6 -0
- package/dist/orchestration/types.js.map +1 -0
- package/dist/perception/extractor.d.ts +7 -0
- package/dist/perception/extractor.js +521 -0
- package/dist/perception/extractor.js.map +1 -0
- package/dist/perception/graph-store.d.ts +25 -0
- package/dist/perception/graph-store.js +117 -0
- package/dist/perception/graph-store.js.map +1 -0
- package/dist/perception/index.d.ts +4 -0
- package/dist/perception/index.js +18 -0
- package/dist/perception/index.js.map +1 -0
- package/dist/perception/queries.d.ts +29 -0
- package/dist/perception/queries.js +354 -0
- package/dist/perception/queries.js.map +1 -0
- package/dist/perception/types.d.ts +103 -0
- package/dist/perception/types.js +6 -0
- package/dist/perception/types.js.map +1 -0
- package/dist/providers/anthropic.d.ts +11 -0
- package/dist/providers/anthropic.js +172 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/factory.d.ts +58 -0
- package/dist/providers/factory.js +354 -0
- package/dist/providers/factory.js.map +1 -0
- package/dist/providers/fallback.d.ts +24 -0
- package/dist/providers/fallback.js +72 -0
- package/dist/providers/fallback.js.map +1 -0
- package/dist/providers/google.d.ts +11 -0
- package/dist/providers/google.js +129 -0
- package/dist/providers/google.js.map +1 -0
- package/dist/providers/openai-compatible.d.ts +24 -0
- package/dist/providers/openai-compatible.js +216 -0
- package/dist/providers/openai-compatible.js.map +1 -0
- package/dist/providers/resilient-factory.d.ts +24 -0
- package/dist/providers/resilient-factory.js +57 -0
- package/dist/providers/resilient-factory.js.map +1 -0
- package/dist/providers/resilient.d.ts +63 -0
- package/dist/providers/resilient.js +145 -0
- package/dist/providers/resilient.js.map +1 -0
- package/dist/providers/types.d.ts +84 -0
- package/dist/providers/types.js +6 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/ruby/alternator.d.ts +40 -0
- package/dist/ruby/alternator.js +233 -0
- package/dist/ruby/alternator.js.map +1 -0
- package/dist/ruby/competence.d.ts +34 -0
- package/dist/ruby/competence.js +264 -0
- package/dist/ruby/competence.js.map +1 -0
- package/dist/ruby/episode-capture.d.ts +56 -0
- package/dist/ruby/episode-capture.js +193 -0
- package/dist/ruby/episode-capture.js.map +1 -0
- package/dist/ruby/fine-tune.d.ts +16 -0
- package/dist/ruby/fine-tune.js +253 -0
- package/dist/ruby/fine-tune.js.map +1 -0
- package/dist/ruby/index.d.ts +10 -0
- package/dist/ruby/index.js +26 -0
- package/dist/ruby/index.js.map +1 -0
- package/dist/ruby/ruby-model.d.ts +36 -0
- package/dist/ruby/ruby-model.js +81 -0
- package/dist/ruby/ruby-model.js.map +1 -0
- package/dist/ruby/training-data.d.ts +11 -0
- package/dist/ruby/training-data.js +107 -0
- package/dist/ruby/training-data.js.map +1 -0
- package/dist/ruby/types.d.ts +136 -0
- package/dist/ruby/types.js +20 -0
- package/dist/ruby/types.js.map +1 -0
- package/dist/safety/permissions.d.ts +32 -0
- package/dist/safety/permissions.js +304 -0
- package/dist/safety/permissions.js.map +1 -0
- package/dist/server/index.d.ts +9 -0
- package/dist/server/index.js +342 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/session.d.ts +22 -0
- package/dist/server/session.js +16 -0
- package/dist/server/session.js.map +1 -0
- package/dist/setup/first-run.d.ts +41 -0
- package/dist/setup/first-run.js +386 -0
- package/dist/setup/first-run.js.map +1 -0
- package/dist/setup/global-config.d.ts +19 -0
- package/dist/setup/global-config.js +97 -0
- package/dist/setup/global-config.js.map +1 -0
- package/dist/tools/audio-transcribe.d.ts +8 -0
- package/dist/tools/audio-transcribe.js +156 -0
- package/dist/tools/audio-transcribe.js.map +1 -0
- package/dist/tools/browser.d.ts +15 -0
- package/dist/tools/browser.js +241 -0
- package/dist/tools/browser.js.map +1 -0
- package/dist/tools/calendar.d.ts +12 -0
- package/dist/tools/calendar.js +196 -0
- package/dist/tools/calendar.js.map +1 -0
- package/dist/tools/clipboard.d.ts +7 -0
- package/dist/tools/clipboard.js +90 -0
- package/dist/tools/clipboard.js.map +1 -0
- package/dist/tools/cron.d.ts +10 -0
- package/dist/tools/cron.js +167 -0
- package/dist/tools/cron.js.map +1 -0
- package/dist/tools/edit-file.d.ts +6 -0
- package/dist/tools/edit-file.js +98 -0
- package/dist/tools/edit-file.js.map +1 -0
- package/dist/tools/email.d.ts +11 -0
- package/dist/tools/email.js +153 -0
- package/dist/tools/email.js.map +1 -0
- package/dist/tools/git.d.ts +1 -0
- package/dist/tools/git.js +7 -0
- package/dist/tools/git.js.map +1 -0
- package/dist/tools/http-request.d.ts +12 -0
- package/dist/tools/http-request.js +91 -0
- package/dist/tools/http-request.js.map +1 -0
- package/dist/tools/image-read.d.ts +7 -0
- package/dist/tools/image-read.js +125 -0
- package/dist/tools/image-read.js.map +1 -0
- package/dist/tools/index.d.ts +3 -0
- package/dist/tools/index.js +201 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/list-dir.d.ts +1 -0
- package/dist/tools/list-dir.js +6 -0
- package/dist/tools/list-dir.js.map +1 -0
- package/dist/tools/mcp.d.ts +11 -0
- package/dist/tools/mcp.js +356 -0
- package/dist/tools/mcp.js.map +1 -0
- package/dist/tools/memory.d.ts +9 -0
- package/dist/tools/memory.js +124 -0
- package/dist/tools/memory.js.map +1 -0
- package/dist/tools/notify.d.ts +9 -0
- package/dist/tools/notify.js +40 -0
- package/dist/tools/notify.js.map +1 -0
- package/dist/tools/read-file.d.ts +6 -0
- package/dist/tools/read-file.js +76 -0
- package/dist/tools/read-file.js.map +1 -0
- package/dist/tools/run-shell.d.ts +1 -0
- package/dist/tools/run-shell.js +6 -0
- package/dist/tools/run-shell.js.map +1 -0
- package/dist/tools/run-tests.d.ts +1 -0
- package/dist/tools/run-tests.js +6 -0
- package/dist/tools/run-tests.js.map +1 -0
- package/dist/tools/search-code.d.ts +1 -0
- package/dist/tools/search-code.js +6 -0
- package/dist/tools/search-code.js.map +1 -0
- package/dist/tools/telegram-bot.d.ts +2 -0
- package/dist/tools/telegram-bot.js +693 -0
- package/dist/tools/telegram-bot.js.map +1 -0
- package/dist/tools/telegram-safety.d.ts +52 -0
- package/dist/tools/telegram-safety.js +189 -0
- package/dist/tools/telegram-safety.js.map +1 -0
- package/dist/tools/telegram.d.ts +16 -0
- package/dist/tools/telegram.js +227 -0
- package/dist/tools/telegram.js.map +1 -0
- package/dist/tools/tools.d.ts +48 -0
- package/dist/tools/tools.js +328 -0
- package/dist/tools/tools.js.map +1 -0
- package/dist/tools/web-fetch.d.ts +11 -0
- package/dist/tools/web-fetch.js +119 -0
- package/dist/tools/web-fetch.js.map +1 -0
- package/dist/tools/web-search.d.ts +8 -0
- package/dist/tools/web-search.js +81 -0
- package/dist/tools/web-search.js.map +1 -0
- package/dist/tools/whatsapp.d.ts +10 -0
- package/dist/tools/whatsapp.js +222 -0
- package/dist/tools/whatsapp.js.map +1 -0
- package/dist/tools/write-file.d.ts +1 -0
- package/dist/tools/write-file.js +6 -0
- package/dist/tools/write-file.js.map +1 -0
- package/dist/tools/youtube-transcript.d.ts +9 -0
- package/dist/tools/youtube-transcript.js +200 -0
- package/dist/tools/youtube-transcript.js.map +1 -0
- package/dist/util/circuit-breaker.d.ts +44 -0
- package/dist/util/circuit-breaker.js +78 -0
- package/dist/util/circuit-breaker.js.map +1 -0
- package/dist/util/env.d.ts +18 -0
- package/dist/util/env.js +39 -0
- package/dist/util/env.js.map +1 -0
- package/dist/util/errors.d.ts +42 -0
- package/dist/util/errors.js +121 -0
- package/dist/util/errors.js.map +1 -0
- package/dist/util/rate-limiter.d.ts +71 -0
- package/dist/util/rate-limiter.js +141 -0
- package/dist/util/rate-limiter.js.map +1 -0
- package/dist/util/retry.d.ts +30 -0
- package/dist/util/retry.js +57 -0
- package/dist/util/retry.js.map +1 -0
- package/dist/util/sanitize.d.ts +16 -0
- package/dist/util/sanitize.js +255 -0
- package/dist/util/sanitize.js.map +1 -0
- package/dist/verify/checks.d.ts +21 -0
- package/dist/verify/checks.js +311 -0
- package/dist/verify/checks.js.map +1 -0
- package/dist/verify/index.d.ts +33 -0
- package/dist/verify/index.js +127 -0
- package/dist/verify/index.js.map +1 -0
- package/dist/verify/types.d.ts +20 -0
- package/dist/verify/types.js +3 -0
- package/dist/verify/types.js.map +1 -0
- package/dist/versioncheck.d.ts +22 -0
- package/dist/versioncheck.js +203 -0
- package/dist/versioncheck.js.map +1 -0
- package/dist/viz/index.d.ts +2 -0
- package/dist/viz/index.js +733 -0
- package/dist/viz/index.js.map +1 -0
- package/dist/workflows/engine.d.ts +46 -0
- package/dist/workflows/engine.js +320 -0
- package/dist/workflows/engine.js.map +1 -0
- package/dist/workflows/types.d.ts +92 -0
- package/dist/workflows/types.js +6 -0
- package/dist/workflows/types.js.map +1 -0
- package/package.json +86 -0
|
@@ -0,0 +1,1734 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const readline = __importStar(require("readline"));
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const minimist_1 = __importDefault(require("minimist"));
|
|
44
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
45
|
+
const factory_js_1 = require("../providers/factory.js");
|
|
46
|
+
const resilient_factory_js_1 = require("../providers/resilient-factory.js");
|
|
47
|
+
const context_js_1 = require("../agent/context.js");
|
|
48
|
+
const index_js_1 = require("../viz/index.js");
|
|
49
|
+
const loop_js_1 = require("../agent/loop.js");
|
|
50
|
+
const permissions_js_1 = require("../safety/permissions.js");
|
|
51
|
+
const display_js_1 = require("./display.js");
|
|
52
|
+
const index_js_2 = require("../server/index.js");
|
|
53
|
+
const project_config_js_1 = require("../config/project-config.js");
|
|
54
|
+
const defaults_js_1 = require("../config/defaults.js");
|
|
55
|
+
const session_store_js_1 = require("../agent/session-store.js");
|
|
56
|
+
const global_config_js_1 = require("../setup/global-config.js");
|
|
57
|
+
const first_run_js_1 = require("../setup/first-run.js");
|
|
58
|
+
const index_js_3 = require("../orchestration/index.js");
|
|
59
|
+
const index_js_4 = require("../perception/index.js");
|
|
60
|
+
const weakness_miner_js_1 = require("../harness/weakness-miner.js");
|
|
61
|
+
const proposer_js_1 = require("../harness/proposer.js");
|
|
62
|
+
const engine_js_1 = require("../workflows/engine.js");
|
|
63
|
+
const engine_js_2 = require("../architect/engine.js");
|
|
64
|
+
const diamond_js_1 = require("./diamond.js");
|
|
65
|
+
const episode_capture_js_1 = require("../ruby/episode-capture.js");
|
|
66
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
67
|
+
// Parse args
|
|
68
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
69
|
+
const argv = (0, minimist_1.default)(process.argv.slice(2), {
|
|
70
|
+
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'],
|
|
71
|
+
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', 'once'],
|
|
72
|
+
alias: { m: 'model', h: 'help', v: 'version' },
|
|
73
|
+
default: {
|
|
74
|
+
model: process.env.AURA_MODEL,
|
|
75
|
+
mode: 'normal',
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
function num(s) {
|
|
79
|
+
if (s === undefined || s === null || s === '')
|
|
80
|
+
return undefined;
|
|
81
|
+
const n = Number(s);
|
|
82
|
+
return Number.isFinite(n) ? n : undefined;
|
|
83
|
+
}
|
|
84
|
+
const cliMaxRetries = num(argv['max-retries']) ?? num(process.env.AURA_MAX_RETRIES);
|
|
85
|
+
const cliMaxVerifyRetries = num(argv['max-verify-retries']);
|
|
86
|
+
const cliMaxTurns = num(argv['max-turns']);
|
|
87
|
+
const cliVerify = argv.verify === true;
|
|
88
|
+
const cliProfile = typeof argv.profile === 'string' ? argv.profile : undefined;
|
|
89
|
+
const cliTestCommand = typeof argv['test-command'] === 'string' ? argv['test-command'] : undefined;
|
|
90
|
+
const cliRpm = num(argv['rate-limit-rpm']) ?? num(process.env.AURA_API_RPM);
|
|
91
|
+
const cliTpm = num(argv['rate-limit-tpm']) ?? num(process.env.AURA_API_TPM);
|
|
92
|
+
const cliFallbacks = Array.isArray(argv.fallback)
|
|
93
|
+
? argv.fallback.map(String)
|
|
94
|
+
: typeof argv.fallback === 'string'
|
|
95
|
+
? [argv.fallback]
|
|
96
|
+
: process.env.AURA_FALLBACK_MODEL
|
|
97
|
+
? [process.env.AURA_FALLBACK_MODEL]
|
|
98
|
+
: [...defaults_js_1.FALLBACK_CHAIN];
|
|
99
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
100
|
+
// Help / version
|
|
101
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
102
|
+
if (argv.version) {
|
|
103
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '../../package.json'), 'utf8'));
|
|
104
|
+
console.log(`Aura v${pkg.version}`);
|
|
105
|
+
process.exit(0);
|
|
106
|
+
}
|
|
107
|
+
if (argv.models) {
|
|
108
|
+
console.log('\n' + chalk_1.default.hex('#cc785c').bold(' Supported models:\n'));
|
|
109
|
+
const allModels = (0, factory_js_1.getAllModels)();
|
|
110
|
+
const byProvider = allModels.reduce((acc, m) => {
|
|
111
|
+
(acc[m.provider] ??= []).push(m);
|
|
112
|
+
return acc;
|
|
113
|
+
}, {});
|
|
114
|
+
for (const [provider, models] of Object.entries(byProvider)) {
|
|
115
|
+
console.log(chalk_1.default.hex('#8a7768')(` ${provider}`));
|
|
116
|
+
for (const m of models) {
|
|
117
|
+
console.log(` ${chalk_1.default.hex('#cc785c')(m.id.padEnd(45))} ${chalk_1.default.hex('#4e3d30')(m.speed)}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
console.log(chalk_1.default.hex('#4e3d30')('\n Use --model <id> or set AURA_MODEL env var'));
|
|
121
|
+
console.log(chalk_1.default.hex('#4e3d30')(' For Ollama: --model ollama/llama3.2'));
|
|
122
|
+
console.log(chalk_1.default.hex('#4e3d30')(' For OpenRouter: --model openrouter/<provider>/<name>\n'));
|
|
123
|
+
process.exit(0);
|
|
124
|
+
}
|
|
125
|
+
if (argv['list-sessions']) {
|
|
126
|
+
const root = argv.cwd ? path.resolve(argv.cwd) : process.cwd();
|
|
127
|
+
const sessions = session_store_js_1.sessionStore.listSessions(root);
|
|
128
|
+
if (sessions.length === 0) {
|
|
129
|
+
console.log(chalk_1.default.hex('#8a7768')('\n No saved sessions for this project.\n'));
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
console.log(chalk_1.default.hex('#cc785c').bold('\n Saved sessions:\n'));
|
|
133
|
+
for (const s of sessions) {
|
|
134
|
+
const updated = new Date(s.updatedAt).toLocaleString();
|
|
135
|
+
const turns = Math.floor(s.history.length / 2);
|
|
136
|
+
console.log(` ${chalk_1.default.hex('#cc785c')(s.id.padEnd(20))} ` +
|
|
137
|
+
`${chalk_1.default.hex('#ede0cc')(s.title.slice(0, 45).padEnd(46))} ` +
|
|
138
|
+
`${chalk_1.default.hex('#4e3d30')(`${turns}t · ${updated}`)}`);
|
|
139
|
+
}
|
|
140
|
+
console.log();
|
|
141
|
+
}
|
|
142
|
+
process.exit(0);
|
|
143
|
+
}
|
|
144
|
+
if (argv.analyze) {
|
|
145
|
+
const report = (0, weakness_miner_js_1.mineWeaknesses)();
|
|
146
|
+
const outPath = (0, weakness_miner_js_1.saveReport)(report);
|
|
147
|
+
console.log(chalk_1.default.hex('#cc785c').bold('\n Weakness Analysis Report\n'));
|
|
148
|
+
console.log(chalk_1.default.hex('#8a7768')(` Sessions analyzed: ${report.sessionsAnalyzed}`));
|
|
149
|
+
console.log(chalk_1.default.hex('#8a7768')(` Report saved to: ${outPath}\n`));
|
|
150
|
+
if (report.patterns.length === 0) {
|
|
151
|
+
console.log(chalk_1.default.hex('#5a9e6e')(' ✓ No recurring weakness patterns detected. Agent behavior looks healthy.\n'));
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
for (const p of report.patterns) {
|
|
155
|
+
console.log(chalk_1.default.hex('#b15439').bold(` ✗ ${p.pattern} (${p.frequency} occurrences)`));
|
|
156
|
+
console.log(chalk_1.default.hex('#8a7768')(` ${p.description}`));
|
|
157
|
+
if (p.occurrences[0]) {
|
|
158
|
+
console.log(chalk_1.default.hex('#4e3d30')(` Example task: "${p.occurrences[0].exampleTask.slice(0, 80)}"`));
|
|
159
|
+
console.log(chalk_1.default.hex('#4e3d30')(` Example failure: ${p.occurrences[0].exampleFailure.slice(0, 100)}`));
|
|
160
|
+
}
|
|
161
|
+
console.log(chalk_1.default.hex('#cc785c')(` Suggestion: ${p.promptPatch.slice(0, 120)}...`));
|
|
162
|
+
console.log();
|
|
163
|
+
}
|
|
164
|
+
console.log(chalk_1.default.hex('#8a7768')(` ${report.summary}\n`));
|
|
165
|
+
}
|
|
166
|
+
process.exit(0);
|
|
167
|
+
}
|
|
168
|
+
if (argv['propose-harness']) {
|
|
169
|
+
// Ensure a weakness report exists — mine if needed
|
|
170
|
+
if (!fs.existsSync((0, weakness_miner_js_1.reportPath)())) {
|
|
171
|
+
console.log(chalk_1.default.hex('#8a7768')('\n No weakness report found — mining sessions first...\n'));
|
|
172
|
+
const report = (0, weakness_miner_js_1.mineWeaknesses)();
|
|
173
|
+
(0, weakness_miner_js_1.saveReport)(report);
|
|
174
|
+
}
|
|
175
|
+
const proposals = (0, proposer_js_1.generateProposals)();
|
|
176
|
+
if (proposals.length === 0) {
|
|
177
|
+
console.log(chalk_1.default.hex('#5a9e6e')('\n ✓ No proposals generated — no actionable weakness patterns found.\n'));
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
console.log(chalk_1.default.hex('#cc785c').bold('\n Harness Proposals\n'));
|
|
181
|
+
for (const p of proposals) {
|
|
182
|
+
console.log(chalk_1.default.hex('#cc785c')(` ${p.id}`));
|
|
183
|
+
console.log(chalk_1.default.hex('#8a7768')(` Pattern: ${p.pattern} (${p.description.slice(0, 60)})`));
|
|
184
|
+
console.log(chalk_1.default.hex('#8a7768')(` Section: ${p.targetSection}`));
|
|
185
|
+
console.log(chalk_1.default.hex('#4e3d30')(` Patch: ${p.patchText.slice(0, 80)}...`));
|
|
186
|
+
console.log();
|
|
187
|
+
}
|
|
188
|
+
console.log(chalk_1.default.hex('#5a9e6e')(` ${proposals.length} proposal(s) saved to ~/.aura/harness/proposals/`));
|
|
189
|
+
console.log(chalk_1.default.hex('#8a7768')(' Apply with: ruby --apply-harness <id>\n'));
|
|
190
|
+
}
|
|
191
|
+
process.exit(0);
|
|
192
|
+
}
|
|
193
|
+
if (typeof argv['apply-harness'] === 'string' && argv['apply-harness']) {
|
|
194
|
+
const proposalId = argv['apply-harness'];
|
|
195
|
+
console.log(chalk_1.default.hex('#cc785c').bold(`\n Applying harness proposal: ${proposalId}\n`));
|
|
196
|
+
const result = (0, proposer_js_1.applyHarnessProposal)(proposalId);
|
|
197
|
+
if (result.success) {
|
|
198
|
+
console.log(chalk_1.default.hex('#5a9e6e')(` ✓ ${result.message}\n`));
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
console.log(chalk_1.default.hex('#b15439')(` ✗ ${result.message}\n`));
|
|
202
|
+
}
|
|
203
|
+
process.exit(result.success ? 0 : 1);
|
|
204
|
+
}
|
|
205
|
+
// ── --workflows: list all persisted workflows ────────────────────────────────
|
|
206
|
+
if (argv.workflows) {
|
|
207
|
+
(async () => {
|
|
208
|
+
const workflows = await (0, engine_js_1.listWorkflows)();
|
|
209
|
+
if (workflows.length === 0) {
|
|
210
|
+
console.log(chalk_1.default.hex('#8a7768')('\n No saved workflows.\n'));
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
console.log(chalk_1.default.hex('#cc785c').bold('\n Saved workflows:\n'));
|
|
214
|
+
for (const ws of workflows) {
|
|
215
|
+
const created = new Date(ws.definition.createdAt).toLocaleString();
|
|
216
|
+
const doneSteps = ws.stepStates.filter(s => s.status === 'done').length;
|
|
217
|
+
const totalSteps = ws.definition.steps.length;
|
|
218
|
+
const statusColor = ws.status === 'done' ? '#5a9e6e' : ws.status === 'failed' ? '#b15439' : '#cc785c';
|
|
219
|
+
console.log(` ${chalk_1.default.hex('#cc785c')(ws.definition.id.padEnd(24))} ` +
|
|
220
|
+
`${chalk_1.default.hex('#ede0cc')(ws.definition.name.slice(0, 36).padEnd(37))} ` +
|
|
221
|
+
`${chalk_1.default.hex(statusColor)(ws.status.padEnd(8))} ` +
|
|
222
|
+
`${chalk_1.default.hex('#4e3d30')(`${doneSteps}/${totalSteps} steps · ${created}`)}`);
|
|
223
|
+
}
|
|
224
|
+
console.log();
|
|
225
|
+
}
|
|
226
|
+
process.exit(0);
|
|
227
|
+
})();
|
|
228
|
+
}
|
|
229
|
+
// ── --blueprints: list all saved blueprints ──────────────────────────────────
|
|
230
|
+
if (argv.blueprints) {
|
|
231
|
+
(async () => {
|
|
232
|
+
const bps = await (0, engine_js_2.listBlueprints)();
|
|
233
|
+
if (bps.length === 0) {
|
|
234
|
+
console.log(chalk_1.default.hex('#8a7768')('\n No saved blueprints.\n'));
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
console.log(chalk_1.default.hex('#cc785c').bold('\n Saved blueprints:\n'));
|
|
238
|
+
for (const bp of bps) {
|
|
239
|
+
const created = new Date(bp.createdAt).toLocaleString();
|
|
240
|
+
const builtCount = bp.files.filter(f => f.status === 'built').length;
|
|
241
|
+
const totalFiles = bp.files.length;
|
|
242
|
+
const statusColor = bp.status === 'complete' ? '#5a9e6e' : bp.status === 'building' ? '#cc9e5c' : '#cc785c';
|
|
243
|
+
console.log(` ${chalk_1.default.hex(statusColor)(bp.status.padEnd(10))} ` +
|
|
244
|
+
`${chalk_1.default.hex('#cc785c')(bp.id.slice(0, 16).padEnd(18))} ` +
|
|
245
|
+
`${chalk_1.default.hex('#ede0cc')(bp.task.slice(0, 40).padEnd(41))} ` +
|
|
246
|
+
`${chalk_1.default.hex('#4e3d30')(`${builtCount}/${totalFiles} files · ${created}`)}`);
|
|
247
|
+
}
|
|
248
|
+
console.log();
|
|
249
|
+
}
|
|
250
|
+
process.exit(0);
|
|
251
|
+
})();
|
|
252
|
+
}
|
|
253
|
+
// ── --blueprint <id>: show a saved blueprint ────────────────────────────────
|
|
254
|
+
if (typeof argv.blueprint === 'string' && argv.blueprint) {
|
|
255
|
+
(async () => {
|
|
256
|
+
const bp = await (0, engine_js_2.loadBlueprint)(argv.blueprint);
|
|
257
|
+
if (!bp) {
|
|
258
|
+
console.error(chalk_1.default.hex('#b15439')(`\n ✗ Blueprint not found: ${argv.blueprint}\n`));
|
|
259
|
+
process.exit(1);
|
|
260
|
+
}
|
|
261
|
+
const statusColor = bp.status === 'complete' ? '#5a9e6e' : bp.status === 'building' ? '#cc9e5c' : '#cc785c';
|
|
262
|
+
console.log(chalk_1.default.hex('#cc785c').bold('\n Blueprint\n'));
|
|
263
|
+
console.log(` ${chalk_1.default.hex('#8a7768')('ID:')} ${chalk_1.default.hex('#cc785c')(bp.id)}`);
|
|
264
|
+
console.log(` ${chalk_1.default.hex('#8a7768')('Task:')} ${chalk_1.default.hex('#ede0cc')(bp.task)}`);
|
|
265
|
+
console.log(` ${chalk_1.default.hex('#8a7768')('Status:')} ${chalk_1.default.hex(statusColor)(bp.status)}`);
|
|
266
|
+
console.log(` ${chalk_1.default.hex('#8a7768')('Steps:')} ${bp.estimatedSteps}`);
|
|
267
|
+
console.log(` ${chalk_1.default.hex('#8a7768')('Created:')} ${new Date(bp.createdAt).toLocaleString()}`);
|
|
268
|
+
if (bp.builtAt)
|
|
269
|
+
console.log(` ${chalk_1.default.hex('#8a7768')('Built:')} ${new Date(bp.builtAt).toLocaleString()}`);
|
|
270
|
+
if (bp.files.length > 0) {
|
|
271
|
+
console.log(chalk_1.default.hex('#cc785c').bold('\n Files:\n'));
|
|
272
|
+
for (const f of bp.files) {
|
|
273
|
+
const fileStatusColor = f.status === 'built' ? '#5a9e6e' : f.status === 'skipped' ? '#8a7768' : '#cc785c';
|
|
274
|
+
console.log(` ${chalk_1.default.hex(fileStatusColor)(f.status.padEnd(8))} ${chalk_1.default.hex('#cc785c')(f.path)}`);
|
|
275
|
+
console.log(` ${chalk_1.default.hex('#8a7768')(f.purpose)}`);
|
|
276
|
+
if (f.exports.length > 0) {
|
|
277
|
+
console.log(` ${chalk_1.default.hex('#4e3d30')(`exports: ${f.exports.join(', ')}`)}`);
|
|
278
|
+
}
|
|
279
|
+
if (f.interfaces.length > 0) {
|
|
280
|
+
console.log(` ${chalk_1.default.hex('#4e3d30')(`interfaces: ${f.interfaces.join(', ')}`)}`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (bp.dataModels.length > 0) {
|
|
285
|
+
console.log(chalk_1.default.hex('#cc785c').bold('\n Data Models:\n'));
|
|
286
|
+
for (const dm of bp.dataModels) {
|
|
287
|
+
console.log(` ${chalk_1.default.hex('#cc785c')(dm.name)} — ${chalk_1.default.hex('#8a7768')(dm.description)}`);
|
|
288
|
+
for (const field of dm.fields) {
|
|
289
|
+
console.log(` ${chalk_1.default.hex('#4e3d30')(field)}`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (bp.dependencies.length > 0) {
|
|
294
|
+
console.log(chalk_1.default.hex('#cc785c').bold('\n Dependencies:\n'));
|
|
295
|
+
for (const dep of bp.dependencies) {
|
|
296
|
+
console.log(` ${chalk_1.default.hex('#4e3d30')(dep)}`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
if (bp.risks.length > 0) {
|
|
300
|
+
console.log(chalk_1.default.hex('#b15439').bold('\n Risks:\n'));
|
|
301
|
+
for (const risk of bp.risks) {
|
|
302
|
+
console.log(` ${chalk_1.default.hex('#b15439')('⚠')} ${chalk_1.default.hex('#8a7768')(risk)}`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if (bp.deviations.length > 0) {
|
|
306
|
+
console.log(chalk_1.default.hex('#cc9e5c').bold('\n Deviations:\n'));
|
|
307
|
+
for (const dev of bp.deviations) {
|
|
308
|
+
const time = new Date(dev.recordedAt).toLocaleString();
|
|
309
|
+
console.log(` ${chalk_1.default.hex('#cc9e5c')('→')} ${chalk_1.default.hex('#8a7768')(dev.description)} ${chalk_1.default.hex('#4e3d30')(`(${time})`)}`);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
console.log();
|
|
313
|
+
process.exit(0);
|
|
314
|
+
})();
|
|
315
|
+
}
|
|
316
|
+
if (argv.help) {
|
|
317
|
+
printHelp();
|
|
318
|
+
process.exit(0);
|
|
319
|
+
}
|
|
320
|
+
// When run with no args and nothing in stdin, show help then exit.
|
|
321
|
+
// When run with no args + a TTY or piped input, fall through to the REPL/wizard.
|
|
322
|
+
// Skip this gate when --reset-setup is set (the wizard should fire even if env
|
|
323
|
+
// vars make needsWizard() return false).
|
|
324
|
+
if (argv._.length === 0 && !argv.interactive && process.stdin.isTTY !== true && !argv['reset-setup']) {
|
|
325
|
+
if (!(0, first_run_js_1.needsWizard)({})) {
|
|
326
|
+
printHelp();
|
|
327
|
+
process.exit(0);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
331
|
+
// Resolve config — CLI > .aura.json > global config > first-run wizard
|
|
332
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
333
|
+
const cwd = argv.cwd ? path.resolve(argv.cwd) : process.cwd();
|
|
334
|
+
const fileConfig = (0, project_config_js_1.loadProjectConfig)(cwd);
|
|
335
|
+
// Pull global config (saved by the first-run wizard) so the user doesn't have
|
|
336
|
+
// to re-set their provider on every run.
|
|
337
|
+
const globalCfg = (0, global_config_js_1.loadGlobalConfig)();
|
|
338
|
+
// Effective model = CLI > AURA_MODEL env > .aura.json > global config > undefined
|
|
339
|
+
const cliModel = typeof argv.model === 'string' ? argv.model : undefined;
|
|
340
|
+
const effectiveModel = cliModel ?? fileConfig.model ?? globalCfg?.defaultModel ?? process.env.AURA_MODEL;
|
|
341
|
+
// Effective base URL = CLI > .aura.json > global config > undefined
|
|
342
|
+
const cliBaseUrl = typeof argv['base-url'] === 'string' ? argv['base-url'] : undefined;
|
|
343
|
+
const effectiveBaseUrl = cliBaseUrl ?? fileConfig.baseUrl ?? globalCfg?.baseUrl;
|
|
344
|
+
const resolved = (0, project_config_js_1.resolveConfig)({ ...fileConfig, model: effectiveModel, baseUrl: effectiveBaseUrl }, {
|
|
345
|
+
model: cliModel,
|
|
346
|
+
baseUrl: cliBaseUrl,
|
|
347
|
+
auto: argv.auto === true,
|
|
348
|
+
readonly: argv.readonly === true,
|
|
349
|
+
maxTurns: cliMaxTurns,
|
|
350
|
+
rateLimitRpm: cliRpm,
|
|
351
|
+
rateLimitTpm: cliTpm,
|
|
352
|
+
maxRetries: cliMaxRetries,
|
|
353
|
+
fallbacks: cliFallbacks.length > 0 ? cliFallbacks : undefined,
|
|
354
|
+
}, { model: undefined, mode: 'normal', ignore: [] });
|
|
355
|
+
// Register custom providers from .aura.json
|
|
356
|
+
(0, factory_js_1.registerCustomProviders)(resolved.providers);
|
|
357
|
+
const permissionLevel = resolved.mode;
|
|
358
|
+
// Mutable runtime state — :model command updates this
|
|
359
|
+
const runtimeConfig = {
|
|
360
|
+
model: resolved.model,
|
|
361
|
+
baseUrl: resolved.baseUrl,
|
|
362
|
+
apiKey: typeof argv['api-key'] === 'string' ? argv['api-key'] : undefined,
|
|
363
|
+
};
|
|
364
|
+
// ── Profile: local → Ollama defaults ─────────────────────────────────────────
|
|
365
|
+
if (cliProfile === 'local') {
|
|
366
|
+
resolved.baseUrl = 'http://localhost:11434/v1';
|
|
367
|
+
if (!runtimeConfig.apiKey) {
|
|
368
|
+
runtimeConfig.apiKey = 'ollama';
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
function buildProvider(display) {
|
|
372
|
+
// Use runtimeConfig.model (mutable via :model command) falling back to resolved.model.
|
|
373
|
+
const model = runtimeConfig.model ?? resolved.model;
|
|
374
|
+
// Keep resolved in sync so external reads (e.g. header display) reflect the switch.
|
|
375
|
+
resolved.model = model;
|
|
376
|
+
return (0, resilient_factory_js_1.createResilientProvider)({
|
|
377
|
+
model,
|
|
378
|
+
apiKey: runtimeConfig.apiKey,
|
|
379
|
+
baseUrl: runtimeConfig.baseUrl ?? undefined,
|
|
380
|
+
}, {
|
|
381
|
+
rpm: resolved.rateLimitRpm,
|
|
382
|
+
tpm: resolved.rateLimitTpm,
|
|
383
|
+
maxRetries: resolved.maxRetries,
|
|
384
|
+
fallbacks: resolved.fallbacks,
|
|
385
|
+
}, display);
|
|
386
|
+
}
|
|
387
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
388
|
+
// Main
|
|
389
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
390
|
+
async function main() {
|
|
391
|
+
(0, diamond_js_1.renderDiamond)();
|
|
392
|
+
await new Promise(r => setTimeout(r, 1500));
|
|
393
|
+
process.stdout.write('\x1b[2J\x1b[H');
|
|
394
|
+
const display = (0, display_js_1.createTerminalDisplay)();
|
|
395
|
+
// ── First-run wizard ───────────────────────────────────────────────────────
|
|
396
|
+
// Skip if: --no-setup flag, --api-key on CLI, env var set, or global config exists.
|
|
397
|
+
// Non-interactive modes (one-shot, --models, --help) skip the wizard too.
|
|
398
|
+
// Wizard only fires when we're in an interactive terminal (TTY + no other args).
|
|
399
|
+
// Wizard is eligible when there are no other args (no one-shot task) AND
|
|
400
|
+
// we're not in a strict TTY-less script context. Accept both TTY and pipe:
|
|
401
|
+
// - TTY = real interactive use
|
|
402
|
+
// - pipe = test harnesses (and `aura | tee log`)
|
|
403
|
+
// The one-shot path (`aura "task"`) has argv._.length > 0 so the
|
|
404
|
+
// wizard can't fire there. --reset-setup alone is treated as interactive
|
|
405
|
+
// since the whole point is to launch the wizard.
|
|
406
|
+
const isInteractive = argv.interactive === true
|
|
407
|
+
|| process.argv.slice(2).length === 0
|
|
408
|
+
|| argv['reset-setup'] === true;
|
|
409
|
+
const cliApiKey = typeof argv['api-key'] === 'string' ? argv['api-key'] : undefined;
|
|
410
|
+
const cliModel = typeof argv.model === 'string' ? argv.model : undefined;
|
|
411
|
+
const skipSetup = argv['no-setup'] === true || argv.help === true || argv.h === true || argv.models === true || argv.version === true || argv.v === true;
|
|
412
|
+
const resetSetup = argv['reset-setup'] === true;
|
|
413
|
+
if (resetSetup) {
|
|
414
|
+
// Wipe global config so the wizard fires unconditionally.
|
|
415
|
+
try {
|
|
416
|
+
fs.unlinkSync((0, global_config_js_1.globalConfigPath)());
|
|
417
|
+
}
|
|
418
|
+
catch { /* not present */ }
|
|
419
|
+
}
|
|
420
|
+
// When --reset-setup is set, force the wizard to fire (overrides env-var
|
|
421
|
+
// detection — the user explicitly wants to reconfigure).
|
|
422
|
+
const shouldRunWizard = !skipSetup && isInteractive && (resetSetup || (0, first_run_js_1.needsWizard)({ cliApiKey, cliModel }));
|
|
423
|
+
if (shouldRunWizard) {
|
|
424
|
+
// If stdin is not a TTY and there's nothing piped in, the wizard will
|
|
425
|
+
// hang. Skip with a helpful message instead.
|
|
426
|
+
if (process.stdin.isTTY !== true && !process.stdin.readable) {
|
|
427
|
+
console.error(chalk_1.default.hex('#b15439')('\n ✗ No interactive input available.'));
|
|
428
|
+
console.error(chalk_1.default.hex('#8a7768')(' Set an API key env var (e.g. export OPENAI_API_KEY=...)'));
|
|
429
|
+
console.error(chalk_1.default.hex('#8a7768')(' or pass --api-key <key> --model <id> on the command line,\n'));
|
|
430
|
+
process.exit(1);
|
|
431
|
+
}
|
|
432
|
+
const cfg = await (0, first_run_js_1.runFirstRunWizard)();
|
|
433
|
+
if (!cfg) {
|
|
434
|
+
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'));
|
|
435
|
+
process.exit(1);
|
|
436
|
+
}
|
|
437
|
+
// Re-resolve with the new global config
|
|
438
|
+
const fresh = (0, global_config_js_1.loadGlobalConfig)();
|
|
439
|
+
if (fresh) {
|
|
440
|
+
resolved.model = fresh.defaultModel;
|
|
441
|
+
resolved.baseUrl = fresh.baseUrl;
|
|
442
|
+
runtimeConfig.model = fresh.defaultModel;
|
|
443
|
+
runtimeConfig.baseUrl = fresh.baseUrl;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
let ctx;
|
|
447
|
+
try {
|
|
448
|
+
ctx = await (0, context_js_1.loadProjectContext)(cwd);
|
|
449
|
+
}
|
|
450
|
+
catch (e) {
|
|
451
|
+
display.error(`Could not load project context: ${String(e)}`);
|
|
452
|
+
process.exit(1);
|
|
453
|
+
}
|
|
454
|
+
// ── Guard: we need a model before we can build a provider ─────────────────
|
|
455
|
+
if (!resolved.model) {
|
|
456
|
+
console.error(chalk_1.default.hex('#b15439')('\n ✗ No model configured.'));
|
|
457
|
+
console.error(chalk_1.default.hex('#8a7768')(' Run `aura` with no args in a TTY to launch the setup wizard,'));
|
|
458
|
+
console.error(chalk_1.default.hex('#8a7768')(' or pass --model <id> --api-key <key> on the command line,'));
|
|
459
|
+
console.error(chalk_1.default.hex('#8a7768')(' or set the model in .aura.json (`"model": "..."`).'));
|
|
460
|
+
process.exit(1);
|
|
461
|
+
}
|
|
462
|
+
let provider;
|
|
463
|
+
try {
|
|
464
|
+
provider = buildProvider(display);
|
|
465
|
+
}
|
|
466
|
+
catch (e) {
|
|
467
|
+
display.error(`Could not initialize provider: ${String(e)}`);
|
|
468
|
+
process.exit(1);
|
|
469
|
+
}
|
|
470
|
+
const permissions = new permissions_js_1.PermissionSystem(permissionLevel);
|
|
471
|
+
// ── Session / chat-ID resolution ───────────────────────────────────────────
|
|
472
|
+
const noSession = argv['no-session'] === true;
|
|
473
|
+
const projectRoot = ctx.root || cwd;
|
|
474
|
+
// Active session state (mutable — commands can swap it)
|
|
475
|
+
let activeChatId;
|
|
476
|
+
let activeChatHistory = [];
|
|
477
|
+
let activeChatTitle;
|
|
478
|
+
if (!noSession) {
|
|
479
|
+
if (argv['new-session']) {
|
|
480
|
+
// Force a brand-new session
|
|
481
|
+
activeChatId = session_store_js_1.sessionStore.generateId();
|
|
482
|
+
}
|
|
483
|
+
else if (typeof argv['resume'] === 'string' && argv['resume']) {
|
|
484
|
+
// --resume <id>
|
|
485
|
+
const loaded = await session_store_js_1.sessionStore.loadSession(projectRoot, argv['resume']);
|
|
486
|
+
if (!loaded) {
|
|
487
|
+
console.error(chalk_1.default.hex('#b15439')(`\n ✗ Session not found: ${argv['resume']}\n`));
|
|
488
|
+
process.exit(1);
|
|
489
|
+
}
|
|
490
|
+
activeChatId = loaded.id;
|
|
491
|
+
activeChatHistory = loaded.history;
|
|
492
|
+
activeChatTitle = loaded.title;
|
|
493
|
+
console.log(chalk_1.default.hex('#5a9e6e')(`\n ↩ Resuming session ${loaded.id} — "${loaded.title}" (${Math.floor(loaded.history.length / 2)} turns)\n`));
|
|
494
|
+
}
|
|
495
|
+
else if (argv['resume'] === true || argv['resume'] === '') {
|
|
496
|
+
// --resume with no value → resume latest
|
|
497
|
+
const latest = session_store_js_1.sessionStore.findLatestSession(projectRoot);
|
|
498
|
+
if (latest) {
|
|
499
|
+
activeChatId = latest.id;
|
|
500
|
+
activeChatHistory = latest.history;
|
|
501
|
+
activeChatTitle = latest.title;
|
|
502
|
+
console.log(chalk_1.default.hex('#5a9e6e')(`\n ↩ Resuming latest session ${latest.id} — "${latest.title}" (${Math.floor(latest.history.length / 2)} turns)\n`));
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
activeChatId = session_store_js_1.sessionStore.generateId();
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
else if (typeof argv['chat-id'] === 'string' && argv['chat-id']) {
|
|
509
|
+
activeChatId = argv['chat-id'];
|
|
510
|
+
const existing = await session_store_js_1.sessionStore.loadSession(projectRoot, activeChatId);
|
|
511
|
+
if (existing) {
|
|
512
|
+
activeChatHistory = existing.history;
|
|
513
|
+
activeChatTitle = existing.title;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
activeChatId = session_store_js_1.sessionStore.generateId();
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
// Legacy sessionPath kept for single-task one-shot mode
|
|
521
|
+
const sessionPath = noSession ? undefined : path.join(session_store_js_1.sessionStore.defaultDir(), projectRoot.replace(/[^a-zA-Z0-9_-]/g, '_').slice(0, 80), 'latest.json');
|
|
522
|
+
// ── ASCII art banner ────────────────────────────────────────────────────────
|
|
523
|
+
const orange = chalk_1.default.hex('#E8771A');
|
|
524
|
+
const pink1 = chalk_1.default.hex('#F5715B'); // Smooth transition step
|
|
525
|
+
const pink2 = chalk_1.default.hex('#FF6B9D');
|
|
526
|
+
const red1 = chalk_1.default.hex('#E64A69'); // Smooth transition step
|
|
527
|
+
const red2 = chalk_1.default.hex('#CC2936');
|
|
528
|
+
if ((process.stdout.columns ?? 80) < 80) {
|
|
529
|
+
console.log(chalk_1.default.hex('#CC2936').bold('AURA CODE'));
|
|
530
|
+
}
|
|
531
|
+
else {
|
|
532
|
+
console.log('');
|
|
533
|
+
console.log(orange(' █████╗ ██╗ ██╗██████╗ █████╗ ██████╗ ██████╗ ██████╗ ███████╗'));
|
|
534
|
+
console.log(pink1(' ██╔══██╗██║ ██║██╔══██╗██╔══██╗ ██╔════╝██╔═══██╗██╔══██╗██╔════╝'));
|
|
535
|
+
console.log(pink2(' ███████║██║ ██║██████╔╝███████║ ██║ ██║ ██║██║ ██║█████╗ '));
|
|
536
|
+
console.log(red1(' ██╔══██║██║ ██║██╔══██╗██╔══██║ ██║ ██║ ██║██║ ██║██╔══╝ '));
|
|
537
|
+
console.log(red2(' ██║ ██║╚██████╔╝██║ ██║██║ ██║ ╚██████╗╚██████╔╝██████╔╝███████╗'));
|
|
538
|
+
console.log(red2(' ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝'));
|
|
539
|
+
console.log('');
|
|
540
|
+
console.log(chalk_1.default.hex('#4e3d30')(' "I don\'t try. I verify."'));
|
|
541
|
+
console.log('');
|
|
542
|
+
}
|
|
543
|
+
console.log('');
|
|
544
|
+
await new Promise(r => setTimeout(r, 50));
|
|
545
|
+
display.header(`Aura — ${ctx.name}`, `${provider.name} · ${runtimeConfig.model} · ${ctx.language} · ${permissionLevel} mode` +
|
|
546
|
+
(fileConfig.model ? ` · .aura.json loaded` : '') +
|
|
547
|
+
(activeChatId ? ` · chat ${activeChatId}` : ''));
|
|
548
|
+
const cumulative = { turns: 0, toolCalls: 0, inputTokens: 0, outputTokens: 0, costUsd: 0 };
|
|
549
|
+
// ── --build <id>: build from a saved blueprint in dependency order ───────────
|
|
550
|
+
if (typeof argv.build === 'string' && argv.build) {
|
|
551
|
+
const bp = await (0, engine_js_2.loadBlueprint)(argv.build);
|
|
552
|
+
if (!bp) {
|
|
553
|
+
console.error(chalk_1.default.hex('#b15439')(`\n ✗ Blueprint not found: ${argv.build}\n`));
|
|
554
|
+
process.exit(1);
|
|
555
|
+
}
|
|
556
|
+
display.header('Architect Builder', `Building from blueprint: ${bp.id}`);
|
|
557
|
+
console.log(chalk_1.default.hex('#8a7768')(` Task: ${bp.task}`));
|
|
558
|
+
console.log(chalk_1.default.hex('#8a7768')(` Files: ${bp.files.filter(f => f.status === 'planned').length} to build\n`));
|
|
559
|
+
await (0, engine_js_2.updateBlueprintStatus)(bp.id, 'building');
|
|
560
|
+
// Build files in order — planned files only
|
|
561
|
+
const plannedFiles = bp.files.filter(f => f.status === 'planned');
|
|
562
|
+
for (const file of plannedFiles) {
|
|
563
|
+
console.log(chalk_1.default.hex('#cc785c')(` ▸ Building: ${file.path} — ${file.purpose}`));
|
|
564
|
+
const buildTask = [
|
|
565
|
+
`Create the file ${file.path}.`,
|
|
566
|
+
`Purpose: ${file.purpose}`,
|
|
567
|
+
file.exports.length > 0 ? `Exports: ${file.exports.join(', ')}` : '',
|
|
568
|
+
file.interfaces.length > 0 ? `Interfaces: ${file.interfaces.join(', ')}` : '',
|
|
569
|
+
`This file is part of a larger blueprint for: ${bp.task}`,
|
|
570
|
+
bp.dependencies.length > 0 ? `Dependencies: ${bp.dependencies.join(', ')}` : '',
|
|
571
|
+
'Follow the existing code style. Do not modify other files.',
|
|
572
|
+
].filter(Boolean).join('\n');
|
|
573
|
+
try {
|
|
574
|
+
const result = await (0, loop_js_1.runAgentLoop)({
|
|
575
|
+
provider, task: buildTask, context: ctx, permissions, display,
|
|
576
|
+
initialHistory: [],
|
|
577
|
+
maxTurns: Math.min(resolved.maxTurns ?? 50, 50),
|
|
578
|
+
spawnConfig: {
|
|
579
|
+
apiKey: runtimeConfig.apiKey,
|
|
580
|
+
baseUrl: runtimeConfig.baseUrl ?? undefined,
|
|
581
|
+
},
|
|
582
|
+
});
|
|
583
|
+
if (result.success) {
|
|
584
|
+
await (0, engine_js_2.markBuilt)(bp.id, file.path);
|
|
585
|
+
console.log(chalk_1.default.hex('#5a9e6e')(` ✓ ${file.path} built\n`));
|
|
586
|
+
}
|
|
587
|
+
else {
|
|
588
|
+
console.log(chalk_1.default.hex('#b15439')(` ✗ ${file.path} failed: ${result.summary}\n`));
|
|
589
|
+
await (0, engine_js_2.addDeviation)(bp.id, `Failed to build ${file.path}: ${result.summary}`);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
catch (e) {
|
|
593
|
+
console.log(chalk_1.default.hex('#b15439')(` ✗ ${file.path} error: ${String(e)}\n`));
|
|
594
|
+
await (0, engine_js_2.addDeviation)(bp.id, `Error building ${file.path}: ${String(e)}`);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
// Final status
|
|
598
|
+
const finalBp = await (0, engine_js_2.loadBlueprint)(bp.id);
|
|
599
|
+
if (finalBp) {
|
|
600
|
+
const allBuilt = finalBp.files.every(f => f.status !== 'planned');
|
|
601
|
+
if (allBuilt) {
|
|
602
|
+
await (0, engine_js_2.updateBlueprintStatus)(bp.id, 'complete');
|
|
603
|
+
console.log(chalk_1.default.hex('#5a9e6e').bold(`\n ✓ Blueprint complete: ${finalBp.files.filter(f => f.status === 'built').length} files built\n`));
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
console.log(chalk_1.default.hex('#cc9e5c').bold(`\n ⚠ Blueprint partially complete: ${finalBp.files.filter(f => f.status === 'built').length}/${finalBp.files.length} files built\n`));
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
// ── --workflow: create and run a new workflow ──────────────────────────────
|
|
612
|
+
if (typeof argv.workflow === 'string' && argv.workflow) {
|
|
613
|
+
const workflowName = argv.workflow;
|
|
614
|
+
const stepTasks = argv._.map(String);
|
|
615
|
+
if (stepTasks.length === 0) {
|
|
616
|
+
console.error(chalk_1.default.hex('#b15439')('\n ✗ No step tasks provided.'));
|
|
617
|
+
console.error(chalk_1.default.hex('#8a7768')(' Usage: ruby --workflow <name> "step 1" "step 2" ...\n'));
|
|
618
|
+
process.exit(1);
|
|
619
|
+
}
|
|
620
|
+
const steps = stepTasks.map((task, i) => ({
|
|
621
|
+
name: `step-${i + 1}`,
|
|
622
|
+
task,
|
|
623
|
+
}));
|
|
624
|
+
display.header('Workflow', `Creating workflow "${workflowName}" with ${steps.length} steps`);
|
|
625
|
+
const state = await (0, engine_js_1.createWorkflow)({ name: workflowName, steps });
|
|
626
|
+
console.log(chalk_1.default.hex('#5a9e6e')(`\n ✓ Workflow created: ${state.definition.id}\n`));
|
|
627
|
+
const makeRunStep = () => {
|
|
628
|
+
return async (task, stepIndex) => {
|
|
629
|
+
console.log(chalk_1.default.hex('#cc785c')(`\n ▸ Step ${stepIndex + 1}/${steps.length}: ${task}\n`));
|
|
630
|
+
const currentProvider = buildProvider(display);
|
|
631
|
+
const result = await (0, loop_js_1.runAgentLoop)({
|
|
632
|
+
provider: currentProvider, task, context: ctx, permissions, display,
|
|
633
|
+
initialHistory: [],
|
|
634
|
+
maxTurns: resolved.maxTurns,
|
|
635
|
+
spawnConfig: {
|
|
636
|
+
apiKey: runtimeConfig.apiKey,
|
|
637
|
+
baseUrl: runtimeConfig.baseUrl ?? undefined,
|
|
638
|
+
},
|
|
639
|
+
});
|
|
640
|
+
return {
|
|
641
|
+
success: result.success,
|
|
642
|
+
summary: result.summary,
|
|
643
|
+
turns: result.turns,
|
|
644
|
+
toolCallCount: result.toolCallCount,
|
|
645
|
+
tokensUsed: result.usage.inputTokens + result.usage.outputTokens,
|
|
646
|
+
};
|
|
647
|
+
};
|
|
648
|
+
};
|
|
649
|
+
const finalState = await (0, engine_js_1.runWorkflow)(state, makeRunStep());
|
|
650
|
+
if (finalState.status === 'done') {
|
|
651
|
+
console.log(chalk_1.default.hex('#5a9e6e').bold(`\n ✓ ${finalState.outcome}\n`));
|
|
652
|
+
}
|
|
653
|
+
else {
|
|
654
|
+
console.error(chalk_1.default.hex('#b15439').bold(`\n ✗ ${finalState.outcome}\n`));
|
|
655
|
+
}
|
|
656
|
+
const totalTokens = finalState.totalTokens ?? 0;
|
|
657
|
+
console.log(chalk_1.default.hex('#4e3d30')(` ↳ ${totalTokens.toLocaleString()} tokens · ${finalState.stepStates.length} steps · status: ${finalState.status}`));
|
|
658
|
+
if (finalState.status === 'failed') {
|
|
659
|
+
console.log(chalk_1.default.hex('#8a7768')(`\n Resume with: ruby --resume-workflow ${finalState.definition.id}\n`));
|
|
660
|
+
process.exit(1);
|
|
661
|
+
}
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
// ── --resume-workflow <id>: resume a persisted workflow ─────────────────────
|
|
665
|
+
if (typeof argv['resume-workflow'] === 'string' && argv['resume-workflow']) {
|
|
666
|
+
const workflowId = argv['resume-workflow'];
|
|
667
|
+
display.header('Workflow', `Resuming workflow ${workflowId}`);
|
|
668
|
+
const makeRunStep = () => {
|
|
669
|
+
return async (task, stepIndex) => {
|
|
670
|
+
console.log(chalk_1.default.hex('#cc785c')(`\n ▸ Step ${stepIndex + 1}: ${task}\n`));
|
|
671
|
+
const currentProvider = buildProvider(display);
|
|
672
|
+
const result = await (0, loop_js_1.runAgentLoop)({
|
|
673
|
+
provider: currentProvider, task, context: ctx, permissions, display,
|
|
674
|
+
initialHistory: [],
|
|
675
|
+
maxTurns: resolved.maxTurns,
|
|
676
|
+
spawnConfig: {
|
|
677
|
+
apiKey: runtimeConfig.apiKey,
|
|
678
|
+
baseUrl: runtimeConfig.baseUrl ?? undefined,
|
|
679
|
+
},
|
|
680
|
+
});
|
|
681
|
+
return {
|
|
682
|
+
success: result.success,
|
|
683
|
+
summary: result.summary,
|
|
684
|
+
turns: result.turns,
|
|
685
|
+
toolCallCount: result.toolCallCount,
|
|
686
|
+
tokensUsed: result.usage.inputTokens + result.usage.outputTokens,
|
|
687
|
+
};
|
|
688
|
+
};
|
|
689
|
+
};
|
|
690
|
+
const finalState = await (0, engine_js_1.resumeWorkflow)(workflowId, makeRunStep());
|
|
691
|
+
if (!finalState) {
|
|
692
|
+
console.error(chalk_1.default.hex('#b15439')(`\n ✗ Workflow not found: ${workflowId}\n`));
|
|
693
|
+
process.exit(1);
|
|
694
|
+
}
|
|
695
|
+
if (finalState.status === 'done') {
|
|
696
|
+
console.log(chalk_1.default.hex('#5a9e6e').bold(`\n ✓ ${finalState.outcome}\n`));
|
|
697
|
+
}
|
|
698
|
+
else {
|
|
699
|
+
console.error(chalk_1.default.hex('#b15439').bold(`\n ✗ ${finalState.outcome}\n`));
|
|
700
|
+
}
|
|
701
|
+
const totalTokens = finalState.totalTokens ?? 0;
|
|
702
|
+
console.log(chalk_1.default.hex('#4e3d30')(` ↳ ${totalTokens.toLocaleString()} tokens · ${finalState.stepStates.length} steps · status: ${finalState.status}`));
|
|
703
|
+
if (finalState.status === 'failed') {
|
|
704
|
+
console.log(chalk_1.default.hex('#8a7768')(`\n Resume with: ruby --resume-workflow ${finalState.definition.id}\n`));
|
|
705
|
+
process.exit(1);
|
|
706
|
+
}
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
// ── Single task mode: aura "fix the bug" ──────────────────────────────────────
|
|
710
|
+
if (argv._.length > 0) {
|
|
711
|
+
const task = argv._.join(' ');
|
|
712
|
+
console.log(chalk_1.default.hex('#8a7768')(`\n Task: ${chalk_1.default.hex('#ede0cc')(task)}\n`));
|
|
713
|
+
// --architect: plan-only — decompose and display
|
|
714
|
+
if (argv.architect === true) {
|
|
715
|
+
await runArchitectPlan(task, provider, ctx, display);
|
|
716
|
+
if (argv.once)
|
|
717
|
+
return;
|
|
718
|
+
return startRepl();
|
|
719
|
+
}
|
|
720
|
+
// --build: full orchestrated build — decompose + execute all specialists
|
|
721
|
+
// When --build <id> is a string, build from a saved blueprint instead
|
|
722
|
+
const doOrchestrate = argv.orchestrate === true || argv.build === true;
|
|
723
|
+
if (doOrchestrate) {
|
|
724
|
+
await runOrchestratedTask(task, provider, ctx, display, doOrchestrate);
|
|
725
|
+
if (argv.once)
|
|
726
|
+
return;
|
|
727
|
+
return startRepl();
|
|
728
|
+
}
|
|
729
|
+
try {
|
|
730
|
+
let perception = await (0, index_js_4.loadPerception)(ctx.root);
|
|
731
|
+
if (!perception || (0, index_js_4.isStale)(perception)) {
|
|
732
|
+
display.agentThinking();
|
|
733
|
+
perception = await (0, index_js_4.extractPerception)(ctx.root);
|
|
734
|
+
}
|
|
735
|
+
const decision = await (0, index_js_3.routeTask)({ provider, context: ctx, task, perception: perception ?? undefined });
|
|
736
|
+
if (decision.shouldDecompose && decision.confidence > 0.8) {
|
|
737
|
+
await runOrchestratedTask(task, provider, ctx, display, false, perception ?? undefined);
|
|
738
|
+
if (argv.once)
|
|
739
|
+
return;
|
|
740
|
+
return startRepl();
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
catch {
|
|
744
|
+
// Router failed — fall through to single agent
|
|
745
|
+
}
|
|
746
|
+
const doVerify = cliVerify || !!fileConfig.verify;
|
|
747
|
+
const episodeStart = Date.now();
|
|
748
|
+
let result;
|
|
749
|
+
if (doVerify) {
|
|
750
|
+
const { runWithVerification } = await Promise.resolve().then(() => __importStar(require('../verify/index.js')));
|
|
751
|
+
const maxRetries = cliMaxVerifyRetries ?? fileConfig.maxVerifyRetries ?? defaults_js_1.DEFAULTS.maxVerifyRetries;
|
|
752
|
+
const testCommand = cliTestCommand ?? fileConfig.testCommand;
|
|
753
|
+
const wrapperResult = await runWithVerification({
|
|
754
|
+
loopOpts: {
|
|
755
|
+
provider, task, context: ctx, permissions, display,
|
|
756
|
+
initialHistory: activeChatHistory,
|
|
757
|
+
maxTurns: resolved.maxTurns,
|
|
758
|
+
spawnConfig: {
|
|
759
|
+
apiKey: argv['api-key'] ?? undefined,
|
|
760
|
+
baseUrl: resolved.baseUrl ?? undefined,
|
|
761
|
+
},
|
|
762
|
+
sessionPath,
|
|
763
|
+
},
|
|
764
|
+
config: { enabled: true, maxRetries, testCommand },
|
|
765
|
+
projectRoot: ctx.root,
|
|
766
|
+
display,
|
|
767
|
+
});
|
|
768
|
+
result = wrapperResult.loopResult;
|
|
769
|
+
}
|
|
770
|
+
else {
|
|
771
|
+
result = await (0, loop_js_1.runAgentLoop)({
|
|
772
|
+
provider, task, context: ctx, permissions, display,
|
|
773
|
+
initialHistory: activeChatHistory,
|
|
774
|
+
maxTurns: resolved.maxTurns,
|
|
775
|
+
spawnConfig: {
|
|
776
|
+
apiKey: argv['api-key'] ?? undefined,
|
|
777
|
+
baseUrl: resolved.baseUrl ?? undefined,
|
|
778
|
+
},
|
|
779
|
+
sessionPath,
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
// Persist episode for Ruby Principle training data
|
|
783
|
+
try {
|
|
784
|
+
const { randomBytes } = await Promise.resolve().then(() => __importStar(require('crypto')));
|
|
785
|
+
await (0, episode_capture_js_1.saveEpisode)(ctx.root, {
|
|
786
|
+
id: randomBytes(4).toString('hex') + '-' + Date.now().toString(36),
|
|
787
|
+
timestamp: Date.now(),
|
|
788
|
+
task,
|
|
789
|
+
projectRoot: ctx.root,
|
|
790
|
+
rubyAttempted: false,
|
|
791
|
+
rubySucceeded: false,
|
|
792
|
+
largeModelUsed: runtimeConfig.model,
|
|
793
|
+
largeModelOutput: result.summary,
|
|
794
|
+
reviewerApproved: result.success,
|
|
795
|
+
tokensUsed: { largeModel: result.usage.totalTokens },
|
|
796
|
+
durationMs: Date.now() - episodeStart,
|
|
797
|
+
taskCategory: 'other',
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
catch {
|
|
801
|
+
// Episode recording is best-effort; never block the user
|
|
802
|
+
}
|
|
803
|
+
// Update history so REPL continues the conversation
|
|
804
|
+
activeChatHistory = result.history;
|
|
805
|
+
if (activeChatId && !noSession) {
|
|
806
|
+
await session_store_js_1.sessionStore.upsertSession(projectRoot, activeChatId, result.history, activeChatTitle);
|
|
807
|
+
}
|
|
808
|
+
if (result.success) {
|
|
809
|
+
display.summary(result.summary, result.turns, result.toolCallCount);
|
|
810
|
+
printUsageFooter(display, result.usage, result.costUsd, {
|
|
811
|
+
model: runtimeConfig.model ?? undefined,
|
|
812
|
+
cumulativeTokens: result.usage.inputTokens + result.usage.outputTokens,
|
|
813
|
+
history: result.history,
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
else {
|
|
817
|
+
display.error(result.summary);
|
|
818
|
+
}
|
|
819
|
+
if (argv.once)
|
|
820
|
+
return;
|
|
821
|
+
return startRepl();
|
|
822
|
+
}
|
|
823
|
+
// ── Interactive REPL mode (no task args) ────────────────────────────────────
|
|
824
|
+
return startRepl();
|
|
825
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
826
|
+
// startRepl — interactive readline loop, reused after single-task completion
|
|
827
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
828
|
+
async function startRepl() {
|
|
829
|
+
if (activeChatHistory.length > 0) {
|
|
830
|
+
console.log(chalk_1.default.hex('#8a7768')(` Continuing session with ${Math.floor(activeChatHistory.length / 2)} prior turns.`));
|
|
831
|
+
}
|
|
832
|
+
console.log(chalk_1.default.hex('#8a7768')(' Type a task, or :help for commands. Ctrl+C to exit.\n'));
|
|
833
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
834
|
+
const ask = () => {
|
|
835
|
+
const idTag = activeChatId ? chalk_1.default.hex('#4e3d30')(` [${activeChatId}]`) : '';
|
|
836
|
+
rl.question(chalk_1.default.hex('#cc785c')(' ▸ ') + idTag + (idTag ? ' ' : ''), async (line) => {
|
|
837
|
+
const input = line.trim();
|
|
838
|
+
if (!input) {
|
|
839
|
+
ask();
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
// Slash / colon commands
|
|
843
|
+
const replCtx = {
|
|
844
|
+
ctx, display,
|
|
845
|
+
providerConfig: { model: resolved.model, apiKey: runtimeConfig.apiKey, baseUrl: runtimeConfig.baseUrl ?? undefined },
|
|
846
|
+
permissions, cumulative,
|
|
847
|
+
chatState: { projectRoot, activeChatId, activeChatHistory, activeChatTitle, noSession },
|
|
848
|
+
};
|
|
849
|
+
const cmdResult = await handleReplCommand(input, replCtx);
|
|
850
|
+
if (cmdResult.handled) {
|
|
851
|
+
// Commands may update chat state
|
|
852
|
+
if (cmdResult.newChatId !== undefined)
|
|
853
|
+
activeChatId = cmdResult.newChatId;
|
|
854
|
+
if (cmdResult.newHistory !== undefined)
|
|
855
|
+
activeChatHistory = cmdResult.newHistory;
|
|
856
|
+
if (cmdResult.newTitle !== undefined)
|
|
857
|
+
activeChatTitle = cmdResult.newTitle;
|
|
858
|
+
ask();
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
// Run task — pass current conversation history for stay-active mode
|
|
862
|
+
let result;
|
|
863
|
+
try {
|
|
864
|
+
const currentProvider = buildProvider(display);
|
|
865
|
+
const doVerify = argv.verify === true || !!fileConfig.verify;
|
|
866
|
+
if (doVerify) {
|
|
867
|
+
const { runWithVerification } = await Promise.resolve().then(() => __importStar(require('../verify/index.js')));
|
|
868
|
+
const maxRetries = cliMaxVerifyRetries ?? fileConfig.maxVerifyRetries ?? defaults_js_1.DEFAULTS.maxVerifyRetries;
|
|
869
|
+
const testCommand = cliTestCommand ?? fileConfig.testCommand;
|
|
870
|
+
const wrapperResult = await runWithVerification({
|
|
871
|
+
loopOpts: {
|
|
872
|
+
provider: currentProvider, task: input,
|
|
873
|
+
context: ctx, permissions, display,
|
|
874
|
+
initialHistory: activeChatHistory,
|
|
875
|
+
maxTurns: resolved.maxTurns,
|
|
876
|
+
spawnConfig: {
|
|
877
|
+
apiKey: runtimeConfig.apiKey,
|
|
878
|
+
baseUrl: runtimeConfig.baseUrl ?? undefined,
|
|
879
|
+
},
|
|
880
|
+
sessionPath,
|
|
881
|
+
},
|
|
882
|
+
config: { enabled: true, maxRetries, testCommand },
|
|
883
|
+
projectRoot: ctx.root,
|
|
884
|
+
display,
|
|
885
|
+
});
|
|
886
|
+
result = wrapperResult.loopResult;
|
|
887
|
+
}
|
|
888
|
+
else {
|
|
889
|
+
result = await (0, loop_js_1.runAgentLoop)({
|
|
890
|
+
provider: currentProvider, task: input,
|
|
891
|
+
context: ctx, permissions, display,
|
|
892
|
+
initialHistory: activeChatHistory,
|
|
893
|
+
maxTurns: resolved.maxTurns,
|
|
894
|
+
spawnConfig: {
|
|
895
|
+
apiKey: runtimeConfig.apiKey,
|
|
896
|
+
baseUrl: runtimeConfig.baseUrl ?? undefined,
|
|
897
|
+
},
|
|
898
|
+
sessionPath,
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
catch (err) {
|
|
903
|
+
const msg = err instanceof Error ? (err.stack || err.message) : String(err);
|
|
904
|
+
console.error(chalk_1.default.hex('#b15439')(`\n ✗ Unhandled error: ${msg}\n`));
|
|
905
|
+
ask();
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
// Update stay-active history
|
|
909
|
+
activeChatHistory = result.history;
|
|
910
|
+
// Persist session
|
|
911
|
+
if (activeChatId && !noSession) {
|
|
912
|
+
await session_store_js_1.sessionStore.upsertSession(projectRoot, activeChatId, activeChatHistory, activeChatTitle);
|
|
913
|
+
}
|
|
914
|
+
cumulative.turns += result.turns;
|
|
915
|
+
cumulative.toolCalls += result.toolCallCount;
|
|
916
|
+
cumulative.inputTokens += result.usage.inputTokens;
|
|
917
|
+
cumulative.outputTokens += result.usage.outputTokens;
|
|
918
|
+
cumulative.costUsd += result.costUsd;
|
|
919
|
+
if (result.success) {
|
|
920
|
+
display.summary(result.summary, result.turns, result.toolCallCount);
|
|
921
|
+
printUsageFooter(display, result.usage, result.costUsd, {
|
|
922
|
+
model: runtimeConfig.model ?? undefined,
|
|
923
|
+
cumulativeTokens: cumulative.inputTokens + cumulative.outputTokens,
|
|
924
|
+
history: activeChatHistory,
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
else {
|
|
928
|
+
display.error(result.summary);
|
|
929
|
+
}
|
|
930
|
+
ask();
|
|
931
|
+
});
|
|
932
|
+
};
|
|
933
|
+
// Ctrl+C: if a task is running, prompt to force-quit; second Ctrl+C exits.
|
|
934
|
+
let ctrlC = 0;
|
|
935
|
+
rl.on('SIGINT', () => {
|
|
936
|
+
ctrlC++;
|
|
937
|
+
if (ctrlC === 1) {
|
|
938
|
+
console.log(chalk_1.default.hex('#cc785c')('\n ⏳ Press Ctrl+C again to exit (current task will keep running).'));
|
|
939
|
+
setTimeout(() => { ctrlC = 0; }, 3000);
|
|
940
|
+
}
|
|
941
|
+
else {
|
|
942
|
+
console.log(chalk_1.default.hex('#4e3d30')('\n Aura closed.\n'));
|
|
943
|
+
process.exit(0);
|
|
944
|
+
}
|
|
945
|
+
});
|
|
946
|
+
ask();
|
|
947
|
+
rl.on('close', () => {
|
|
948
|
+
console.log(chalk_1.default.hex('#4e3d30')('\n Aura closed.\n'));
|
|
949
|
+
process.exit(0);
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
function trySetModel(c, newModel) {
|
|
954
|
+
const prevModel = runtimeConfig.model;
|
|
955
|
+
const prevBaseUrl = runtimeConfig.baseUrl;
|
|
956
|
+
runtimeConfig.model = newModel;
|
|
957
|
+
runtimeConfig.baseUrl = undefined;
|
|
958
|
+
try {
|
|
959
|
+
const test = buildProvider(c.display);
|
|
960
|
+
c.providerConfig.model = newModel;
|
|
961
|
+
console.log(chalk_1.default.hex('#5a9e6e')(` ✓ Switched to ${test.name} · ${newModel}`));
|
|
962
|
+
return { ok: true };
|
|
963
|
+
}
|
|
964
|
+
catch (e) {
|
|
965
|
+
runtimeConfig.model = prevModel; // rollback on error
|
|
966
|
+
runtimeConfig.baseUrl = prevBaseUrl;
|
|
967
|
+
return { ok: false, err: String(e) };
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
/**
|
|
971
|
+
* Interactive model selector — shows all models grouped by provider,
|
|
972
|
+
* lets the user pick by number or type a custom model ID.
|
|
973
|
+
*/
|
|
974
|
+
function showModelSelector(c) {
|
|
975
|
+
const allModels = (0, factory_js_1.getAllModels)();
|
|
976
|
+
// Build flat numbered list grouped by provider
|
|
977
|
+
const entries = [];
|
|
978
|
+
let currentProvider = '';
|
|
979
|
+
for (const m of allModels) {
|
|
980
|
+
if (m.provider !== currentProvider) {
|
|
981
|
+
currentProvider = m.provider;
|
|
982
|
+
entries.push({ id: '', label: chalk_1.default.hex('#8a7768').bold(` ── ${currentProvider} ──`), provider: currentProvider });
|
|
983
|
+
}
|
|
984
|
+
entries.push({
|
|
985
|
+
id: m.id,
|
|
986
|
+
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)}`,
|
|
987
|
+
provider: m.provider,
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
console.log(chalk_1.default.hex('#cc785c').bold('\n Model Selector\n'));
|
|
991
|
+
for (const e of entries) {
|
|
992
|
+
console.log(e.label);
|
|
993
|
+
}
|
|
994
|
+
console.log(chalk_1.default.hex('#4e3d30')(`\n Current: ${runtimeConfig.model}`));
|
|
995
|
+
console.log(chalk_1.default.hex('#4e3d30')(' Type a number, model ID, or press Enter to cancel:\n'));
|
|
996
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
997
|
+
rl.question(chalk_1.default.hex('#cc785c')(' ▸ '), (answer) => {
|
|
998
|
+
const choice = answer.trim();
|
|
999
|
+
rl.close();
|
|
1000
|
+
if (!choice) {
|
|
1001
|
+
console.log(chalk_1.default.hex('#4e3d30')(' Cancelled.\n'));
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
1004
|
+
// Try as a number
|
|
1005
|
+
const num = parseInt(choice, 10);
|
|
1006
|
+
if (!isNaN(num) && num >= 1 && num <= entries.length) {
|
|
1007
|
+
const selected = entries[num - 1];
|
|
1008
|
+
if (selected.id) {
|
|
1009
|
+
trySetModel(c, selected.id);
|
|
1010
|
+
}
|
|
1011
|
+
else {
|
|
1012
|
+
console.log(chalk_1.default.hex('#b15439')(' ✗ That\'s a section header, pick a model number.'));
|
|
1013
|
+
}
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
// Treat as a raw model ID
|
|
1017
|
+
trySetModel(c, choice);
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
async function handleReplCommand(input, c) {
|
|
1021
|
+
const unhandled = { handled: false };
|
|
1022
|
+
if (input === ':quit' || input === ':q' || input === '/exit') {
|
|
1023
|
+
process.exit(0);
|
|
1024
|
+
}
|
|
1025
|
+
if (input === ':help' || input === '/help') {
|
|
1026
|
+
console.log(chalk_1.default.hex('#8a7768')([
|
|
1027
|
+
'',
|
|
1028
|
+
' ── Session ──────────────────────────────────────',
|
|
1029
|
+
' :id Show current chat ID',
|
|
1030
|
+
' :sessions List all saved sessions',
|
|
1031
|
+
' :resume Resume the latest session',
|
|
1032
|
+
' :resume <id> Resume a specific session by ID',
|
|
1033
|
+
' :new Start a new session (fresh history)',
|
|
1034
|
+
' :history Show turn count in current session',
|
|
1035
|
+
' :clear-history Wipe conversation history (keep session ID)',
|
|
1036
|
+
' :save [title] Rename / save current session',
|
|
1037
|
+
' :delete <id> Delete a saved session',
|
|
1038
|
+
'',
|
|
1039
|
+
' ── Model / API ──────────────────────────────────',
|
|
1040
|
+
' :model Interactive model selector',
|
|
1041
|
+
' :model <id> Switch to a specific model',
|
|
1042
|
+
' :models List all available models',
|
|
1043
|
+
' :apikey <key> Set API key for current session',
|
|
1044
|
+
'',
|
|
1045
|
+
' ── Workflows ─────────────────────────────────────',
|
|
1046
|
+
' :workflows List all saved workflows',
|
|
1047
|
+
' :workflow Create & run a multi-step workflow',
|
|
1048
|
+
' <name> "step1" "step2" ...',
|
|
1049
|
+
' :resume-workflow <id> Resume a paused/failed workflow',
|
|
1050
|
+
'',
|
|
1051
|
+
' ── Context / Stats ──────────────────────────────',
|
|
1052
|
+
' :context Show loaded project context',
|
|
1053
|
+
' :graph Show codebase knowledge graph summary',
|
|
1054
|
+
' :graph refresh Reload graph from graphify-out/graph.json',
|
|
1055
|
+
' :plans List saved execution plans',
|
|
1056
|
+
' :viz, :dashboard Generate and open the memory dashboard',
|
|
1057
|
+
' /stats, /usage Show token + cost usage this session',
|
|
1058
|
+
' /clear, /reset Reset cumulative usage stats',
|
|
1059
|
+
'',
|
|
1060
|
+
' ── General ──────────────────────────────────────',
|
|
1061
|
+
' :quit, :q, /exit Exit',
|
|
1062
|
+
'',
|
|
1063
|
+
].join('\n')));
|
|
1064
|
+
return { handled: true };
|
|
1065
|
+
}
|
|
1066
|
+
// ── Session commands ─────────────────────────────────────────────────────
|
|
1067
|
+
if (input === ':id') {
|
|
1068
|
+
const cs = c.chatState;
|
|
1069
|
+
if (cs.activeChatId) {
|
|
1070
|
+
console.log(chalk_1.default.hex('#8a7768')(`\n Chat ID: ${chalk_1.default.hex('#cc785c')(cs.activeChatId)}`));
|
|
1071
|
+
if (cs.activeChatTitle)
|
|
1072
|
+
console.log(chalk_1.default.hex('#8a7768')(` Title: ${cs.activeChatTitle}`));
|
|
1073
|
+
console.log(chalk_1.default.hex('#4e3d30')(` Turns: ${Math.floor(cs.activeChatHistory.length / 2)}\n`));
|
|
1074
|
+
}
|
|
1075
|
+
else {
|
|
1076
|
+
console.log(chalk_1.default.hex('#8a7768')('\n No active session (--no-session mode).\n'));
|
|
1077
|
+
}
|
|
1078
|
+
return { handled: true };
|
|
1079
|
+
}
|
|
1080
|
+
if (input === ':sessions') {
|
|
1081
|
+
const sessions = session_store_js_1.sessionStore.listSessions(c.chatState.projectRoot);
|
|
1082
|
+
if (sessions.length === 0) {
|
|
1083
|
+
console.log(chalk_1.default.hex('#8a7768')('\n No saved sessions.\n'));
|
|
1084
|
+
}
|
|
1085
|
+
else {
|
|
1086
|
+
console.log(chalk_1.default.hex('#cc785c').bold('\n Saved sessions:\n'));
|
|
1087
|
+
for (const s of sessions) {
|
|
1088
|
+
const updated = new Date(s.updatedAt).toLocaleString();
|
|
1089
|
+
const turns = Math.floor(s.history.length / 2);
|
|
1090
|
+
const marker = s.id === c.chatState.activeChatId ? chalk_1.default.hex('#5a9e6e')(' ← current') : '';
|
|
1091
|
+
console.log(` ${chalk_1.default.hex('#cc785c')(s.id.padEnd(20))} ` +
|
|
1092
|
+
`${chalk_1.default.hex('#ede0cc')(s.title.slice(0, 40).padEnd(41))} ` +
|
|
1093
|
+
`${chalk_1.default.hex('#4e3d30')(`${turns}t · ${updated}`)}${marker}`);
|
|
1094
|
+
}
|
|
1095
|
+
console.log();
|
|
1096
|
+
}
|
|
1097
|
+
return { handled: true };
|
|
1098
|
+
}
|
|
1099
|
+
if (input === ':resume' || input === ':resume ') {
|
|
1100
|
+
const latest = session_store_js_1.sessionStore.findLatestSession(c.chatState.projectRoot);
|
|
1101
|
+
if (!latest) {
|
|
1102
|
+
console.log(chalk_1.default.hex('#8a7768')('\n No saved sessions to resume.\n'));
|
|
1103
|
+
return { handled: true };
|
|
1104
|
+
}
|
|
1105
|
+
console.log(chalk_1.default.hex('#5a9e6e')(`\n ↩ Resuming ${latest.id} — "${latest.title}" (${Math.floor(latest.history.length / 2)} turns)\n`));
|
|
1106
|
+
return { handled: true, newChatId: latest.id, newHistory: latest.history, newTitle: latest.title };
|
|
1107
|
+
}
|
|
1108
|
+
if (input.startsWith(':resume ')) {
|
|
1109
|
+
const id = input.slice(':resume '.length).trim();
|
|
1110
|
+
const loaded = await session_store_js_1.sessionStore.loadSession(c.chatState.projectRoot, id);
|
|
1111
|
+
if (!loaded) {
|
|
1112
|
+
console.log(chalk_1.default.hex('#b15439')(`\n ✗ Session not found: ${id}\n`));
|
|
1113
|
+
return { handled: true };
|
|
1114
|
+
}
|
|
1115
|
+
console.log(chalk_1.default.hex('#5a9e6e')(`\n ↩ Resumed ${loaded.id} — "${loaded.title}" (${Math.floor(loaded.history.length / 2)} turns)\n`));
|
|
1116
|
+
return { handled: true, newChatId: loaded.id, newHistory: loaded.history, newTitle: loaded.title };
|
|
1117
|
+
}
|
|
1118
|
+
if (input === ':new') {
|
|
1119
|
+
const newId = session_store_js_1.sessionStore.generateId();
|
|
1120
|
+
console.log(chalk_1.default.hex('#5a9e6e')(`\n ✓ New session started: ${newId}\n`));
|
|
1121
|
+
return { handled: true, newChatId: newId, newHistory: [], newTitle: undefined };
|
|
1122
|
+
}
|
|
1123
|
+
if (input === ':history') {
|
|
1124
|
+
const turns = Math.floor(c.chatState.activeChatHistory.length / 2);
|
|
1125
|
+
console.log(chalk_1.default.hex('#8a7768')(`\n Current session: ${turns} turn${turns !== 1 ? 's' : ''} in history.\n`));
|
|
1126
|
+
return { handled: true };
|
|
1127
|
+
}
|
|
1128
|
+
if (input === ':clear-history') {
|
|
1129
|
+
console.log(chalk_1.default.hex('#5a9e6e')('\n ✓ Conversation history cleared.\n'));
|
|
1130
|
+
return { handled: true, newHistory: [] };
|
|
1131
|
+
}
|
|
1132
|
+
if (input === ':save' || input.startsWith(':save ')) {
|
|
1133
|
+
const title = input.startsWith(':save ') ? input.slice(':save '.length).trim() : undefined;
|
|
1134
|
+
const cs = c.chatState;
|
|
1135
|
+
if (!cs.activeChatId) {
|
|
1136
|
+
console.log(chalk_1.default.hex('#8a7768')('\n No active session to save (--no-session mode).\n'));
|
|
1137
|
+
return { handled: true };
|
|
1138
|
+
}
|
|
1139
|
+
const session = await session_store_js_1.sessionStore.upsertSession(cs.projectRoot, cs.activeChatId, cs.activeChatHistory, title ?? cs.activeChatTitle);
|
|
1140
|
+
console.log(chalk_1.default.hex('#5a9e6e')(`\n ✓ Saved as "${session.title}" (${cs.activeChatId})\n`));
|
|
1141
|
+
return { handled: true, newTitle: session.title };
|
|
1142
|
+
}
|
|
1143
|
+
if (input.startsWith(':delete ')) {
|
|
1144
|
+
const id = input.slice(':delete '.length).trim();
|
|
1145
|
+
const deleted = await session_store_js_1.sessionStore.deleteSession(c.chatState.projectRoot, id);
|
|
1146
|
+
if (deleted) {
|
|
1147
|
+
console.log(chalk_1.default.hex('#5a9e6e')(`\n ✓ Deleted session ${id}\n`));
|
|
1148
|
+
if (id === c.chatState.activeChatId) {
|
|
1149
|
+
const newId = session_store_js_1.sessionStore.generateId();
|
|
1150
|
+
console.log(chalk_1.default.hex('#8a7768')(` Starting new session: ${newId}\n`));
|
|
1151
|
+
return { handled: true, newChatId: newId, newHistory: [], newTitle: undefined };
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
else {
|
|
1155
|
+
console.log(chalk_1.default.hex('#b15439')(`\n ✗ Session not found: ${id}\n`));
|
|
1156
|
+
}
|
|
1157
|
+
return { handled: true };
|
|
1158
|
+
}
|
|
1159
|
+
// ── Model / API commands ─────────────────────────────────────────────────
|
|
1160
|
+
if (input === ':context') {
|
|
1161
|
+
console.log(chalk_1.default.hex('#8a7768')(`\n Project: ${c.ctx.name} · ${c.ctx.language} · ${c.ctx.framework}`));
|
|
1162
|
+
console.log(chalk_1.default.hex('#4e3d30')(` Root: ${c.ctx.root}\n`));
|
|
1163
|
+
return { handled: true };
|
|
1164
|
+
}
|
|
1165
|
+
if (input === ':graph') {
|
|
1166
|
+
const summary = (0, context_js_1.loadGraphSummary)(c.ctx.root);
|
|
1167
|
+
if (!summary) {
|
|
1168
|
+
console.log(chalk_1.default.hex('#8a7768')('\n No graph.json found. Run :graph refresh to extract.\n'));
|
|
1169
|
+
}
|
|
1170
|
+
else {
|
|
1171
|
+
console.log(chalk_1.default.hex('#cc785c').bold('\n Codebase Knowledge Graph\n'));
|
|
1172
|
+
console.log(chalk_1.default.hex('#8a7768')(summary));
|
|
1173
|
+
console.log();
|
|
1174
|
+
}
|
|
1175
|
+
return { handled: true };
|
|
1176
|
+
}
|
|
1177
|
+
if (input === ':viz' || input === ':dashboard') {
|
|
1178
|
+
console.log(chalk_1.default.hex('#8a7768')('\n Generating dashboard…\n'));
|
|
1179
|
+
try {
|
|
1180
|
+
const outPath = (0, index_js_1.generateDashboard)(c.ctx.root);
|
|
1181
|
+
console.log(chalk_1.default.hex('#5a9e6e')(` ✓ Dashboard written to ${outPath}`));
|
|
1182
|
+
console.log(chalk_1.default.hex('#8a7768')(' Opening in browser…\n'));
|
|
1183
|
+
(0, index_js_1.openDashboard)(outPath);
|
|
1184
|
+
}
|
|
1185
|
+
catch (e) {
|
|
1186
|
+
console.log(chalk_1.default.hex('#b15439')(` ✗ ${String(e)}\n`));
|
|
1187
|
+
}
|
|
1188
|
+
return { handled: true };
|
|
1189
|
+
}
|
|
1190
|
+
if (input === ':plans') {
|
|
1191
|
+
const { planStore } = await Promise.resolve().then(() => __importStar(require('../orchestration/plan-store.js')));
|
|
1192
|
+
const plans = await planStore.list();
|
|
1193
|
+
if (!plans.length) {
|
|
1194
|
+
console.log(chalk_1.default.hex('#8a7768')('\n No execution plans found.\n'));
|
|
1195
|
+
}
|
|
1196
|
+
else {
|
|
1197
|
+
console.log(chalk_1.default.hex('#cc785c').bold('\n Execution plans:\n'));
|
|
1198
|
+
for (const p of plans.slice(0, 15)) {
|
|
1199
|
+
const created = new Date(p.created).toLocaleString();
|
|
1200
|
+
const dur = p.completed ? `${Math.round((p.completed - p.created) / 1000)}s` : '—';
|
|
1201
|
+
const statusColor = p.status === 'done' ? '#5a9e6e' : p.status === 'failed' ? '#b15439' : '#cc9e5c';
|
|
1202
|
+
console.log(` ${chalk_1.default.hex(statusColor)(p.status.padEnd(8))} ` +
|
|
1203
|
+
`${chalk_1.default.hex('#cc785c')(p.id.slice(0, 12).padEnd(14))} ` +
|
|
1204
|
+
`${chalk_1.default.hex('#ede0cc')(p.goal.slice(0, 50).padEnd(51))} ` +
|
|
1205
|
+
`${chalk_1.default.hex('#4e3d30')(`${p.steps.length}s · ${dur} · ${created}`)}`);
|
|
1206
|
+
}
|
|
1207
|
+
console.log();
|
|
1208
|
+
}
|
|
1209
|
+
return { handled: true };
|
|
1210
|
+
}
|
|
1211
|
+
if (input === ':graph refresh') {
|
|
1212
|
+
console.log(chalk_1.default.hex('#8a7768')('\n Refreshing codebase graph...\n'));
|
|
1213
|
+
const { execFileSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
|
|
1214
|
+
try {
|
|
1215
|
+
// Pass root as sys.argv[1] to avoid shell interpolation
|
|
1216
|
+
execFileSync('python3', [
|
|
1217
|
+
'-c',
|
|
1218
|
+
'import sys,json,os,re,glob; root=sys.argv[1]+"/src"; print("Scanning", root)',
|
|
1219
|
+
c.ctx.root,
|
|
1220
|
+
], { stdio: 'inherit' });
|
|
1221
|
+
}
|
|
1222
|
+
catch { /* ignore */ }
|
|
1223
|
+
// Reload context graph
|
|
1224
|
+
c.ctx.graphSummary = (0, context_js_1.loadGraphSummary)(c.ctx.root);
|
|
1225
|
+
if (c.ctx.graphSummary) {
|
|
1226
|
+
console.log(chalk_1.default.hex('#5a9e6e')(' ✓ Graph loaded and injected into context.\n'));
|
|
1227
|
+
}
|
|
1228
|
+
else {
|
|
1229
|
+
console.log(chalk_1.default.hex('#8a7768')(' No graph.json found after refresh. Run graphify extract first.\n'));
|
|
1230
|
+
}
|
|
1231
|
+
return { handled: true };
|
|
1232
|
+
}
|
|
1233
|
+
if (input === ':models') {
|
|
1234
|
+
const allModels = (0, factory_js_1.getAllModels)();
|
|
1235
|
+
const byProvider = allModels.reduce((acc, m) => {
|
|
1236
|
+
(acc[m.provider] ??= []).push(m);
|
|
1237
|
+
return acc;
|
|
1238
|
+
}, {});
|
|
1239
|
+
for (const [provider, models] of Object.entries(byProvider)) {
|
|
1240
|
+
console.log(chalk_1.default.hex('#8a7768')(`\n ${provider}`));
|
|
1241
|
+
for (const m of models) {
|
|
1242
|
+
console.log(` ${chalk_1.default.hex('#cc785c')(m.id.padEnd(45))} ${chalk_1.default.hex('#4e3d30')(m.speed)}`);
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
console.log();
|
|
1246
|
+
return { handled: true };
|
|
1247
|
+
}
|
|
1248
|
+
if (input === ':model' || input === '/model') {
|
|
1249
|
+
showModelSelector(c);
|
|
1250
|
+
return { handled: true };
|
|
1251
|
+
}
|
|
1252
|
+
if (input.startsWith(':model ') || input.startsWith('/model ')) {
|
|
1253
|
+
const sep = input.startsWith(':model ') ? ':model ' : '/model ';
|
|
1254
|
+
const newModel = input.slice(sep.length).trim();
|
|
1255
|
+
const r = trySetModel(c, newModel);
|
|
1256
|
+
if (!r.ok)
|
|
1257
|
+
console.log(chalk_1.default.hex('#b15439')(` ✗ ${r.err}`));
|
|
1258
|
+
return { handled: true };
|
|
1259
|
+
}
|
|
1260
|
+
if (input.startsWith(':apikey ') || input.startsWith('/apikey ')) {
|
|
1261
|
+
const sep = input.startsWith(':apikey ') ? ':apikey ' : '/apikey ';
|
|
1262
|
+
const newKey = input.slice(sep.length).trim();
|
|
1263
|
+
runtimeConfig.apiKey = newKey;
|
|
1264
|
+
c.providerConfig.apiKey = newKey;
|
|
1265
|
+
console.log(chalk_1.default.hex('#5a9e6e')(' ✓ API key set for current session.'));
|
|
1266
|
+
return { handled: true };
|
|
1267
|
+
}
|
|
1268
|
+
if (input === '/clear' || input === '/reset') {
|
|
1269
|
+
c.cumulative.turns = 0;
|
|
1270
|
+
c.cumulative.toolCalls = 0;
|
|
1271
|
+
c.cumulative.inputTokens = 0;
|
|
1272
|
+
c.cumulative.outputTokens = 0;
|
|
1273
|
+
c.cumulative.costUsd = 0;
|
|
1274
|
+
console.log(chalk_1.default.hex('#5a9e6e')(' ✓ Session stats reset'));
|
|
1275
|
+
return { handled: true };
|
|
1276
|
+
}
|
|
1277
|
+
if (input === '/stats' || input === '/usage') {
|
|
1278
|
+
const u = c.cumulative;
|
|
1279
|
+
const total = u.inputTokens + u.outputTokens;
|
|
1280
|
+
console.log(chalk_1.default.hex('#8a7768')([
|
|
1281
|
+
'',
|
|
1282
|
+
` Session usage:`,
|
|
1283
|
+
` Turns: ${u.turns}`,
|
|
1284
|
+
` Tool calls: ${u.toolCalls}`,
|
|
1285
|
+
` Input tokens: ${u.inputTokens.toLocaleString()}`,
|
|
1286
|
+
` Output tokens:${u.outputTokens.toLocaleString()}`,
|
|
1287
|
+
` Total tokens: ${total.toLocaleString()}`,
|
|
1288
|
+
` Est. cost: ${u.costUsd.toFixed(4)}`,
|
|
1289
|
+
'',
|
|
1290
|
+
].join('\n')));
|
|
1291
|
+
return { handled: true };
|
|
1292
|
+
}
|
|
1293
|
+
// ── Workflow commands ──────────────────────────────────────────────────────
|
|
1294
|
+
if (input === ':workflows') {
|
|
1295
|
+
const workflows = await (0, engine_js_1.listWorkflows)();
|
|
1296
|
+
if (workflows.length === 0) {
|
|
1297
|
+
console.log(chalk_1.default.hex('#8a7768')('\n No saved workflows.\n'));
|
|
1298
|
+
}
|
|
1299
|
+
else {
|
|
1300
|
+
console.log(chalk_1.default.hex('#cc785c').bold('\n Saved workflows:\n'));
|
|
1301
|
+
for (const ws of workflows) {
|
|
1302
|
+
const created = new Date(ws.definition.createdAt).toLocaleString();
|
|
1303
|
+
const doneSteps = ws.stepStates.filter(s => s.status === 'done').length;
|
|
1304
|
+
const totalSteps = ws.definition.steps.length;
|
|
1305
|
+
const statusColor = ws.status === 'done' ? '#5a9e6e' : ws.status === 'failed' ? '#b15439' : '#cc785c';
|
|
1306
|
+
console.log(` ${chalk_1.default.hex('#cc785c')(ws.definition.id.padEnd(24))} ` +
|
|
1307
|
+
`${chalk_1.default.hex('#ede0cc')(ws.definition.name.slice(0, 36).padEnd(37))} ` +
|
|
1308
|
+
`${chalk_1.default.hex(statusColor)(ws.status.padEnd(8))} ` +
|
|
1309
|
+
`${chalk_1.default.hex('#4e3d30')(`${doneSteps}/${totalSteps} steps · ${created}`)}`);
|
|
1310
|
+
}
|
|
1311
|
+
console.log();
|
|
1312
|
+
}
|
|
1313
|
+
return { handled: true };
|
|
1314
|
+
}
|
|
1315
|
+
if (input.startsWith(':workflow ')) {
|
|
1316
|
+
const parts = input.slice(':workflow '.length).trim();
|
|
1317
|
+
// Parse: <name> "step1" "step2" ... or <name> step1 step2 ...
|
|
1318
|
+
const match = parts.match(/^(\S+)\s+(.+)$/);
|
|
1319
|
+
if (!match) {
|
|
1320
|
+
console.log(chalk_1.default.hex('#b15439')(' ✗ Usage: :workflow <name> "step 1" "step 2" ...'));
|
|
1321
|
+
return { handled: true };
|
|
1322
|
+
}
|
|
1323
|
+
const workflowName = match[1];
|
|
1324
|
+
// Split remaining by quoted strings or spaces
|
|
1325
|
+
const restStr = match[2];
|
|
1326
|
+
const stepTasks = [];
|
|
1327
|
+
const quotedRe = /"([^"]+)"|'([^']+)'|(\S+)/g;
|
|
1328
|
+
let m;
|
|
1329
|
+
while ((m = quotedRe.exec(restStr)) !== null) {
|
|
1330
|
+
stepTasks.push(m[1] ?? m[2] ?? m[3]);
|
|
1331
|
+
}
|
|
1332
|
+
if (stepTasks.length === 0) {
|
|
1333
|
+
console.log(chalk_1.default.hex('#b15439')(' ✗ At least one step task is required.'));
|
|
1334
|
+
return { handled: true };
|
|
1335
|
+
}
|
|
1336
|
+
const steps = stepTasks.map((task, i) => ({
|
|
1337
|
+
name: `step-${i + 1}`,
|
|
1338
|
+
task,
|
|
1339
|
+
}));
|
|
1340
|
+
console.log(chalk_1.default.hex('#cc785c').bold(`\n Creating workflow "${workflowName}" with ${steps.length} steps...\n`));
|
|
1341
|
+
const state = await (0, engine_js_1.createWorkflow)({ name: workflowName, steps });
|
|
1342
|
+
console.log(chalk_1.default.hex('#5a9e6e')(` ✓ Workflow created: ${state.definition.id}\n`));
|
|
1343
|
+
// Build the runStep callback using the REPL context's provider
|
|
1344
|
+
const runStep = async (task, stepIndex) => {
|
|
1345
|
+
console.log(chalk_1.default.hex('#cc785c')(` ▸ Step ${stepIndex + 1}/${steps.length}: ${task}\n`));
|
|
1346
|
+
const { createResilientProvider } = await Promise.resolve().then(() => __importStar(require('../providers/resilient-factory.js')));
|
|
1347
|
+
const currentProvider = createResilientProvider({ model: c.providerConfig.model, apiKey: c.providerConfig.apiKey, baseUrl: c.providerConfig.baseUrl }, {}, c.display);
|
|
1348
|
+
const result = await (0, loop_js_1.runAgentLoop)({
|
|
1349
|
+
provider: currentProvider, task, context: c.ctx, permissions: c.permissions,
|
|
1350
|
+
display: c.display, initialHistory: [], maxTurns: undefined,
|
|
1351
|
+
spawnConfig: { apiKey: c.providerConfig.apiKey, baseUrl: c.providerConfig.baseUrl },
|
|
1352
|
+
});
|
|
1353
|
+
return {
|
|
1354
|
+
success: result.success,
|
|
1355
|
+
summary: result.summary,
|
|
1356
|
+
turns: result.turns,
|
|
1357
|
+
toolCallCount: result.toolCallCount,
|
|
1358
|
+
tokensUsed: result.usage.inputTokens + result.usage.outputTokens,
|
|
1359
|
+
};
|
|
1360
|
+
};
|
|
1361
|
+
const finalState = await (0, engine_js_1.runWorkflow)(state, runStep);
|
|
1362
|
+
if (finalState.status === 'done') {
|
|
1363
|
+
console.log(chalk_1.default.hex('#5a9e6e').bold(`\n ✓ ${finalState.outcome}\n`));
|
|
1364
|
+
}
|
|
1365
|
+
else {
|
|
1366
|
+
console.log(chalk_1.default.hex('#b15439').bold(`\n ✗ ${finalState.outcome}`));
|
|
1367
|
+
console.log(chalk_1.default.hex('#8a7768')(` Resume with: :resume-workflow ${finalState.definition.id}\n`));
|
|
1368
|
+
}
|
|
1369
|
+
return { handled: true };
|
|
1370
|
+
}
|
|
1371
|
+
if (input.startsWith(':resume-workflow ')) {
|
|
1372
|
+
const workflowId = input.slice(':resume-workflow '.length).trim();
|
|
1373
|
+
if (!workflowId) {
|
|
1374
|
+
console.log(chalk_1.default.hex('#b15439')(' ✗ Usage: :resume-workflow <id>'));
|
|
1375
|
+
return { handled: true };
|
|
1376
|
+
}
|
|
1377
|
+
console.log(chalk_1.default.hex('#cc785c').bold(`\n Resuming workflow ${workflowId}...\n`));
|
|
1378
|
+
const runStep = async (task, stepIndex) => {
|
|
1379
|
+
console.log(chalk_1.default.hex('#cc785c')(` ▸ Step ${stepIndex + 1}: ${task}\n`));
|
|
1380
|
+
const { createResilientProvider } = await Promise.resolve().then(() => __importStar(require('../providers/resilient-factory.js')));
|
|
1381
|
+
const currentProvider = createResilientProvider({ model: c.providerConfig.model, apiKey: c.providerConfig.apiKey, baseUrl: c.providerConfig.baseUrl }, {}, c.display);
|
|
1382
|
+
const result = await (0, loop_js_1.runAgentLoop)({
|
|
1383
|
+
provider: currentProvider, task, context: c.ctx, permissions: c.permissions,
|
|
1384
|
+
display: c.display, initialHistory: [], maxTurns: undefined,
|
|
1385
|
+
spawnConfig: { apiKey: c.providerConfig.apiKey, baseUrl: c.providerConfig.baseUrl },
|
|
1386
|
+
});
|
|
1387
|
+
return {
|
|
1388
|
+
success: result.success,
|
|
1389
|
+
summary: result.summary,
|
|
1390
|
+
turns: result.turns,
|
|
1391
|
+
toolCallCount: result.toolCallCount,
|
|
1392
|
+
tokensUsed: result.usage.inputTokens + result.usage.outputTokens,
|
|
1393
|
+
};
|
|
1394
|
+
};
|
|
1395
|
+
const finalState = await (0, engine_js_1.resumeWorkflow)(workflowId, runStep);
|
|
1396
|
+
if (!finalState) {
|
|
1397
|
+
console.log(chalk_1.default.hex('#b15439')(` ✗ Workflow not found: ${workflowId}\n`));
|
|
1398
|
+
return { handled: true };
|
|
1399
|
+
}
|
|
1400
|
+
if (finalState.status === 'done') {
|
|
1401
|
+
console.log(chalk_1.default.hex('#5a9e6e').bold(`\n ✓ ${finalState.outcome}\n`));
|
|
1402
|
+
}
|
|
1403
|
+
else {
|
|
1404
|
+
console.log(chalk_1.default.hex('#b15439').bold(`\n ✗ ${finalState.outcome}`));
|
|
1405
|
+
console.log(chalk_1.default.hex('#8a7768')(` Resume with: :resume-workflow ${finalState.definition.id}\n`));
|
|
1406
|
+
}
|
|
1407
|
+
return { handled: true };
|
|
1408
|
+
}
|
|
1409
|
+
return unhandled;
|
|
1410
|
+
}
|
|
1411
|
+
/**
|
|
1412
|
+
* Architect mode: produce a blueprint without writing any code.
|
|
1413
|
+
* The agent analyses the task, proposes files/interfaces/data models,
|
|
1414
|
+
* and the blueprint is saved to ~/.aura/blueprints/<id>.json.
|
|
1415
|
+
*/
|
|
1416
|
+
async function runArchitectPlan(task, provider, ctx, display) {
|
|
1417
|
+
display.header('Architect', 'Analysing task and producing blueprint...');
|
|
1418
|
+
const architectPrompt = [
|
|
1419
|
+
`You are in architect mode. You are planning the implementation for: "${task}"`,
|
|
1420
|
+
'',
|
|
1421
|
+
'Project context:',
|
|
1422
|
+
` Language: ${ctx.language}`,
|
|
1423
|
+
` Framework: ${ctx.framework}`,
|
|
1424
|
+
` Root: ${ctx.root}`,
|
|
1425
|
+
'',
|
|
1426
|
+
'Rules for architect mode:',
|
|
1427
|
+
'1. Think about the FULL solution before proposing any file.',
|
|
1428
|
+
'2. Propose the MINIMUM number of files needed.',
|
|
1429
|
+
'3. Name files after what they DO, not what they ARE.',
|
|
1430
|
+
'4. Define interfaces before implementations.',
|
|
1431
|
+
'5. Flag any ambiguous parts of the task as risks.',
|
|
1432
|
+
'6. Do NOT write any code. Only plan.',
|
|
1433
|
+
'',
|
|
1434
|
+
'Output format — respond with ONLY this JSON object (no markdown fences, no extra text):',
|
|
1435
|
+
JSON.stringify({
|
|
1436
|
+
files: [
|
|
1437
|
+
{
|
|
1438
|
+
path: 'src/example.ts',
|
|
1439
|
+
purpose: 'What this file does (one sentence)',
|
|
1440
|
+
exports: ['exportedSymbol'],
|
|
1441
|
+
interfaces: ['InterfaceName'],
|
|
1442
|
+
},
|
|
1443
|
+
],
|
|
1444
|
+
dataModels: [
|
|
1445
|
+
{
|
|
1446
|
+
name: 'ModelName',
|
|
1447
|
+
fields: ['field: type'],
|
|
1448
|
+
description: 'What this model represents',
|
|
1449
|
+
},
|
|
1450
|
+
],
|
|
1451
|
+
dependencies: ['external-package-or-module'],
|
|
1452
|
+
risks: ['Ambiguous part or concern'],
|
|
1453
|
+
estimatedSteps: 0,
|
|
1454
|
+
}, null, 2),
|
|
1455
|
+
'',
|
|
1456
|
+
'Now produce the blueprint JSON for the task described above.',
|
|
1457
|
+
].join('\n');
|
|
1458
|
+
const permissions = new permissions_js_1.PermissionSystem('read-only');
|
|
1459
|
+
let result;
|
|
1460
|
+
try {
|
|
1461
|
+
result = await (0, loop_js_1.runAgentLoop)({
|
|
1462
|
+
provider, task: architectPrompt, context: ctx, permissions, display,
|
|
1463
|
+
initialHistory: [],
|
|
1464
|
+
maxTurns: 5,
|
|
1465
|
+
spawnConfig: {
|
|
1466
|
+
apiKey: runtimeConfig.apiKey,
|
|
1467
|
+
baseUrl: runtimeConfig.baseUrl ?? undefined,
|
|
1468
|
+
},
|
|
1469
|
+
});
|
|
1470
|
+
}
|
|
1471
|
+
catch (e) {
|
|
1472
|
+
display.error(`Architect analysis failed: ${String(e)}`);
|
|
1473
|
+
process.exit(1);
|
|
1474
|
+
}
|
|
1475
|
+
// Parse the agent's response for a JSON blueprint
|
|
1476
|
+
const responseText = result.summary;
|
|
1477
|
+
let parsed;
|
|
1478
|
+
try {
|
|
1479
|
+
// Try to extract JSON from the response (may have surrounding text)
|
|
1480
|
+
const jsonMatch = responseText.match(/\{[\s\S]*\}/);
|
|
1481
|
+
if (!jsonMatch)
|
|
1482
|
+
throw new Error('No JSON object found in response');
|
|
1483
|
+
parsed = JSON.parse(jsonMatch[0]);
|
|
1484
|
+
}
|
|
1485
|
+
catch (e) {
|
|
1486
|
+
// If the agent didn't produce valid JSON, create a minimal blueprint with the raw response
|
|
1487
|
+
parsed = {
|
|
1488
|
+
files: [],
|
|
1489
|
+
dataModels: [],
|
|
1490
|
+
dependencies: [],
|
|
1491
|
+
risks: [`Agent response was not valid JSON: ${responseText.slice(0, 200)}`],
|
|
1492
|
+
estimatedSteps: 0,
|
|
1493
|
+
};
|
|
1494
|
+
}
|
|
1495
|
+
const blueprint = await (0, engine_js_2.createBlueprint)(task, ctx.root, {
|
|
1496
|
+
files: (parsed.files ?? []).map(f => ({
|
|
1497
|
+
path: f.path,
|
|
1498
|
+
purpose: f.purpose,
|
|
1499
|
+
exports: f.exports ?? [],
|
|
1500
|
+
interfaces: f.interfaces ?? [],
|
|
1501
|
+
status: 'planned',
|
|
1502
|
+
})),
|
|
1503
|
+
dataModels: (parsed.dataModels ?? []).map(dm => ({
|
|
1504
|
+
name: dm.name,
|
|
1505
|
+
fields: dm.fields ?? [],
|
|
1506
|
+
description: dm.description ?? '',
|
|
1507
|
+
})),
|
|
1508
|
+
dependencies: parsed.dependencies ?? [],
|
|
1509
|
+
risks: parsed.risks ?? [],
|
|
1510
|
+
estimatedSteps: parsed.estimatedSteps ?? parsed.files?.length ?? 0,
|
|
1511
|
+
});
|
|
1512
|
+
// Display result
|
|
1513
|
+
console.log(chalk_1.default.hex('#cc785c').bold('\n Blueprint\n'));
|
|
1514
|
+
console.log(chalk_1.default.hex('#ede0cc')(` Task: ${blueprint.task}`));
|
|
1515
|
+
console.log(chalk_1.default.hex('#4e3d30')(` ID: ${blueprint.id}\n`));
|
|
1516
|
+
if (blueprint.files.length > 0) {
|
|
1517
|
+
console.log(chalk_1.default.hex('#cc785c').bold(' Files:\n'));
|
|
1518
|
+
for (const f of blueprint.files) {
|
|
1519
|
+
console.log(` ${chalk_1.default.hex('#cc785c')(f.path)}`);
|
|
1520
|
+
console.log(` ${chalk_1.default.hex('#8a7768')(f.purpose)}`);
|
|
1521
|
+
if (f.exports.length > 0)
|
|
1522
|
+
console.log(` ${chalk_1.default.hex('#4e3d30')(`exports: ${f.exports.join(', ')}`)}`);
|
|
1523
|
+
if (f.interfaces.length > 0)
|
|
1524
|
+
console.log(` ${chalk_1.default.hex('#4e3d30')(`interfaces: ${f.interfaces.join(', ')}`)}`);
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
if (blueprint.dataModels.length > 0) {
|
|
1528
|
+
console.log(chalk_1.default.hex('#cc785c').bold('\n Data Models:\n'));
|
|
1529
|
+
for (const dm of blueprint.dataModels) {
|
|
1530
|
+
console.log(` ${chalk_1.default.hex('#cc785c')(dm.name)} — ${chalk_1.default.hex('#8a7768')(dm.description)}`);
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
if (blueprint.risks.length > 0) {
|
|
1534
|
+
console.log(chalk_1.default.hex('#b15439').bold('\n Risks:\n'));
|
|
1535
|
+
for (const risk of blueprint.risks) {
|
|
1536
|
+
console.log(` ${chalk_1.default.hex('#b15439')('⚠')} ${chalk_1.default.hex('#8a7768')(risk)}`);
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
console.log(chalk_1.default.hex('#5a9e6e')('\n Blueprint saved. No files were modified.'));
|
|
1540
|
+
console.log(chalk_1.default.hex('#5a9e6e')(` Review with: ruby --blueprint ${blueprint.id}`));
|
|
1541
|
+
console.log(chalk_1.default.hex('#5a9e6e')(` Build with: ruby --build ${blueprint.id}\n`));
|
|
1542
|
+
}
|
|
1543
|
+
async function runOrchestratedTask(task, provider, ctx, display, forceOrchestrate, perception) {
|
|
1544
|
+
display.header('Orchestrator', 'Planning multi-agent execution...');
|
|
1545
|
+
let plan;
|
|
1546
|
+
try {
|
|
1547
|
+
plan = await (0, index_js_3.createPlan)({ provider, context: ctx, task, perception });
|
|
1548
|
+
}
|
|
1549
|
+
catch (e) {
|
|
1550
|
+
display.error(`Failed to create plan: ${String(e)}`);
|
|
1551
|
+
process.exit(1);
|
|
1552
|
+
}
|
|
1553
|
+
// If --plan flag, show plan and ask for confirmation
|
|
1554
|
+
if (argv.plan === true) {
|
|
1555
|
+
display.showPlan?.(plan);
|
|
1556
|
+
// Use a simple readline prompt for confirmation
|
|
1557
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1558
|
+
const approved = await new Promise(resolve => {
|
|
1559
|
+
rl.question(chalk_1.default.hex('#cc785c')('\n Run this plan? [y/N] '), answer => {
|
|
1560
|
+
rl.close();
|
|
1561
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
1562
|
+
});
|
|
1563
|
+
});
|
|
1564
|
+
if (!approved) {
|
|
1565
|
+
console.log(chalk_1.default.hex('#4e3d30')(' Plan cancelled.\n'));
|
|
1566
|
+
process.exit(0);
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
// Execute the plan
|
|
1570
|
+
let executedPlan;
|
|
1571
|
+
try {
|
|
1572
|
+
executedPlan = await (0, index_js_3.executePlan)({
|
|
1573
|
+
provider,
|
|
1574
|
+
context: ctx,
|
|
1575
|
+
perception,
|
|
1576
|
+
plan,
|
|
1577
|
+
display,
|
|
1578
|
+
});
|
|
1579
|
+
}
|
|
1580
|
+
catch (e) {
|
|
1581
|
+
display.error(`Plan execution error: ${String(e)}`);
|
|
1582
|
+
process.exit(1);
|
|
1583
|
+
}
|
|
1584
|
+
// Display outcome
|
|
1585
|
+
if (executedPlan.outcome) {
|
|
1586
|
+
display.summary(executedPlan.outcome, executedPlan.steps.length, 0);
|
|
1587
|
+
}
|
|
1588
|
+
const totalTokens = executedPlan.totalTokens ?? 0;
|
|
1589
|
+
console.log(chalk_1.default.hex('#4e3d30')(` ↳ ${totalTokens.toLocaleString()} tokens · ${executedPlan.steps.length} steps · status: ${executedPlan.status}`));
|
|
1590
|
+
}
|
|
1591
|
+
function printUsageFooter(display, usage, costUsd, opts) {
|
|
1592
|
+
const total = usage.inputTokens + usage.outputTokens;
|
|
1593
|
+
console.log(chalk_1.default.hex('#4e3d30')(` ↳ ${total.toLocaleString()} tokens (${usage.inputTokens.toLocaleString()} in / ${usage.outputTokens.toLocaleString()} out) · est. ${costUsd.toFixed(4)}`));
|
|
1594
|
+
// Context window bar
|
|
1595
|
+
if (opts?.model) {
|
|
1596
|
+
const ctxWin = (0, factory_js_1.getContextWindow)(opts.model);
|
|
1597
|
+
if (ctxWin) {
|
|
1598
|
+
const real = opts.cumulativeTokens ?? 0;
|
|
1599
|
+
let used;
|
|
1600
|
+
let estimated;
|
|
1601
|
+
if (real > 0) {
|
|
1602
|
+
used = real;
|
|
1603
|
+
estimated = false;
|
|
1604
|
+
}
|
|
1605
|
+
else {
|
|
1606
|
+
// Provider didn't report usage — estimate from history character count
|
|
1607
|
+
const histChars = (opts.history ?? []).reduce((n, m) => {
|
|
1608
|
+
if ('content' in m && typeof m.content === 'string')
|
|
1609
|
+
return n + m.content.length;
|
|
1610
|
+
if ('results' in m)
|
|
1611
|
+
return n + JSON.stringify(m.results).length;
|
|
1612
|
+
return n;
|
|
1613
|
+
}, 0);
|
|
1614
|
+
used = Math.ceil(histChars / 4);
|
|
1615
|
+
estimated = true;
|
|
1616
|
+
}
|
|
1617
|
+
if (used > 0) {
|
|
1618
|
+
console.log((0, display_js_1.formatContextBar)(used, ctxWin, estimated));
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
function printHelp() {
|
|
1624
|
+
console.log(`
|
|
1625
|
+
${chalk_1.default.hex('#cc785c').bold(' aura')} ${chalk_1.default.hex('#8a7768')("— Aura Code: model-agnostic AI coding agent")}
|
|
1626
|
+
|
|
1627
|
+
${chalk_1.default.hex('#4e3d30')('Usage:')}
|
|
1628
|
+
aura ${chalk_1.default.hex('#8a7768')('"<task>"')} Run a task, then drop into REPL
|
|
1629
|
+
aura ${chalk_1.default.hex('#8a7768')('"<task>" --once')} Run a single task and exit
|
|
1630
|
+
aura Start interactive REPL
|
|
1631
|
+
aura ${chalk_1.default.hex('#8a7768')('--models')} List available models
|
|
1632
|
+
|
|
1633
|
+
${chalk_1.default.hex('#4e3d30')('Options:')}
|
|
1634
|
+
--model, -m <id> Model to use (default: from ~/.config/aura-code/config.json)
|
|
1635
|
+
--api-key <key> API key (overrides env var)
|
|
1636
|
+
--base-url <url> Custom API endpoint (for Ollama, proxies, etc.)
|
|
1637
|
+
--auto Auto-approve all tool calls (no confirmation)
|
|
1638
|
+
--readonly Read-only mode (no file writes or shell commands)
|
|
1639
|
+
--cwd <path> Working directory (default: current)
|
|
1640
|
+
--models List all known model IDs
|
|
1641
|
+
--no-session Disable conversation history persistence
|
|
1642
|
+
--once Run single task and exit (no REPL after)
|
|
1643
|
+
--new-session Force a fresh session (ignore any prior history)
|
|
1644
|
+
--resume [id] Resume latest session, or a specific session by ID
|
|
1645
|
+
--chat-id <id> Attach to a specific chat ID (creates if missing)
|
|
1646
|
+
--list-sessions List all saved sessions for this project
|
|
1647
|
+
--no-setup Skip the first-run setup wizard
|
|
1648
|
+
--reset-setup Wipe saved config and re-run the setup wizard
|
|
1649
|
+
--orchestrate Force multi-agent orchestration mode
|
|
1650
|
+
--architect "task" Blueprint mode: plan-only, no code written, produces blueprint
|
|
1651
|
+
--blueprint <id> Show a saved blueprint by ID
|
|
1652
|
+
--blueprints List all saved blueprints
|
|
1653
|
+
--build [id] Full orchestrated build; --build <id> builds from blueprint
|
|
1654
|
+
--plan Preview execution plan before running
|
|
1655
|
+
--verify Verify output after task; retry up to --max-verify-retries times
|
|
1656
|
+
--max-verify-retries <n> Max verification retries (default: 3)
|
|
1657
|
+
--test-command <cmd> Shell command run as part of verification (e.g. "npm test")
|
|
1658
|
+
--max-turns <n> Max agent loop turns before stopping (default: 150)
|
|
1659
|
+
--analyze Mine session history for weakness patterns; save report
|
|
1660
|
+
--propose-harness Generate system-prompt patches from weakness report
|
|
1661
|
+
--apply-harness <id> Apply a proposal patch; reverts if tests fail
|
|
1662
|
+
--workflow <name> ... Create and run a sequential workflow with named steps
|
|
1663
|
+
--resume-workflow <id> Resume a paused/failed workflow from last completed step
|
|
1664
|
+
--workflows List all persisted workflows
|
|
1665
|
+
--profile local Use local Ollama model (no API key required)
|
|
1666
|
+
|
|
1667
|
+
--rate-limit-rpm <n> Cap requests per minute (default: 0=unlimited, Google: 30)
|
|
1668
|
+
--rate-limit-tpm <n> Cap tokens per minute (Google only; default: 0=unlimited)
|
|
1669
|
+
--max-retries <n> Max retry attempts on 429/5xx (default: 5, Google: 6)
|
|
1670
|
+
--fallback <model> Fallback model if primary exhausts retries (repeatable)
|
|
1671
|
+
--verify Enable post-task verification with automatic retries
|
|
1672
|
+
|
|
1673
|
+
${chalk_1.default.hex('#4e3d30')('Resilience:')}
|
|
1674
|
+
All API calls automatically:
|
|
1675
|
+
1. Honour Retry-After / Google's retryDelay on 429s
|
|
1676
|
+
2. Back off with exponential + jitter (capped at 60s)
|
|
1677
|
+
3. Trip a circuit breaker after 5 consecutive failures
|
|
1678
|
+
4. Fail over to the next --fallback model if retries exhaust
|
|
1679
|
+
5. Pace requests when --rate-limit-rpm / --rate-limit-tpm is set
|
|
1680
|
+
|
|
1681
|
+
${chalk_1.default.hex('#4e3d30')('Project config (.aura.json):')}
|
|
1682
|
+
{
|
|
1683
|
+
"model": "claude-sonnet-4-5-20251001",
|
|
1684
|
+
"mode": "auto",
|
|
1685
|
+
"providers": [
|
|
1686
|
+
{
|
|
1687
|
+
"name": "DeepSeek",
|
|
1688
|
+
"baseUrl": "https://api.deepseek.com/v1",
|
|
1689
|
+
"apiKeyEnv": "DEEPSEEK_API_KEY",
|
|
1690
|
+
"prefixes": ["deepseek/"],
|
|
1691
|
+
"models": [
|
|
1692
|
+
{ "id": "deepseek/deepseek-v4-flash", "name": "DeepSeek V4 Flash", "speed": "Fast · 1M context" },
|
|
1693
|
+
{ "id": "deepseek/deepseek-v4-pro", "name": "DeepSeek V4 Pro", "speed": "Powerful · 1M context" }
|
|
1694
|
+
]
|
|
1695
|
+
}
|
|
1696
|
+
],
|
|
1697
|
+
"rateLimitRpm": 30,
|
|
1698
|
+
"rateLimitTpm": 1000000,
|
|
1699
|
+
"maxTurns": 150,
|
|
1700
|
+
"maxRetries": 6,
|
|
1701
|
+
"fallbacks": ["gpt-4o-mini", "gemini-2.5-flash"],
|
|
1702
|
+
"ignore": ["dist/", "*.generated.ts"]
|
|
1703
|
+
}
|
|
1704
|
+
CLI flags always override .aura.json.
|
|
1705
|
+
Custom providers are OpenAI-compatible endpoints.
|
|
1706
|
+
|
|
1707
|
+
${chalk_1.default.hex('#4e3d30')('Model examples:')}
|
|
1708
|
+
aura -m claude-opus-4-5-20251001 "refactor auth"
|
|
1709
|
+
aura -m gpt-4o "add unit tests"
|
|
1710
|
+
aura -m gemini-2.5-pro --rate-limit-rpm 20 "explain this codebase"
|
|
1711
|
+
aura -m ollama/llama3.2 "local model, no API key needed"
|
|
1712
|
+
|
|
1713
|
+
${chalk_1.default.hex('#4e3d30')('API keys (set as env vars):')}
|
|
1714
|
+
ANTHROPIC_API_KEY Claude models
|
|
1715
|
+
OPENAI_API_KEY GPT models
|
|
1716
|
+
GOOGLE_API_KEY Gemini models
|
|
1717
|
+
XAI_API_KEY Grok models
|
|
1718
|
+
OPENROUTER_API_KEY OpenRouter (access to all models)
|
|
1719
|
+
XIAOMI_API_KEY Xiaomi MiMo
|
|
1720
|
+
AURA_MODEL Default model (overridden by --model)
|
|
1721
|
+
AURA_API_RPM Default request rate limit
|
|
1722
|
+
AURA_API_TPM Default token rate limit (Gemini)
|
|
1723
|
+
AURA_MAX_RETRIES Default max retry attempts
|
|
1724
|
+
AURA_FALLBACK_MODEL Comma-separated fallback models
|
|
1725
|
+
`);
|
|
1726
|
+
}
|
|
1727
|
+
if (argv._[0] === 'serve') {
|
|
1728
|
+
const port = Number(argv.port ?? argv.p ?? 7337);
|
|
1729
|
+
(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); });
|
|
1730
|
+
}
|
|
1731
|
+
else {
|
|
1732
|
+
main().catch(e => { console.error(chalk_1.default.hex('#b15439')(`\nFatal: ${String(e)}`)); process.exit(1); });
|
|
1733
|
+
}
|
|
1734
|
+
//# sourceMappingURL=index.js.map
|