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.
Files changed (288) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +137 -0
  3. package/dist/agent/context.d.ts +13 -0
  4. package/dist/agent/context.js +183 -0
  5. package/dist/agent/context.js.map +1 -0
  6. package/dist/agent/loop.d.ts +60 -0
  7. package/dist/agent/loop.js +228 -0
  8. package/dist/agent/loop.js.map +1 -0
  9. package/dist/agent/session-store.d.ts +30 -0
  10. package/dist/agent/session-store.js +178 -0
  11. package/dist/agent/session-store.js.map +1 -0
  12. package/dist/agent/spawner.d.ts +57 -0
  13. package/dist/agent/spawner.js +112 -0
  14. package/dist/agent/spawner.js.map +1 -0
  15. package/dist/agent/system-prompt.d.ts +3 -0
  16. package/dist/agent/system-prompt.js +100 -0
  17. package/dist/agent/system-prompt.js.map +1 -0
  18. package/dist/architect/engine.d.ts +47 -0
  19. package/dist/architect/engine.js +217 -0
  20. package/dist/architect/engine.js.map +1 -0
  21. package/dist/architect/types.d.ts +61 -0
  22. package/dist/architect/types.js +6 -0
  23. package/dist/architect/types.js.map +1 -0
  24. package/dist/cli/diamond.d.ts +1 -0
  25. package/dist/cli/diamond.js +23 -0
  26. package/dist/cli/diamond.js.map +1 -0
  27. package/dist/cli/display.d.ts +44 -0
  28. package/dist/cli/display.js +234 -0
  29. package/dist/cli/display.js.map +1 -0
  30. package/dist/cli/index.d.ts +2 -0
  31. package/dist/cli/index.js +1734 -0
  32. package/dist/cli/index.js.map +1 -0
  33. package/dist/config/defaults.d.ts +46 -0
  34. package/dist/config/defaults.js +106 -0
  35. package/dist/config/defaults.js.map +1 -0
  36. package/dist/config/project-config.d.ts +90 -0
  37. package/dist/config/project-config.js +141 -0
  38. package/dist/config/project-config.js.map +1 -0
  39. package/dist/harness/proposer.d.ts +26 -0
  40. package/dist/harness/proposer.js +204 -0
  41. package/dist/harness/proposer.js.map +1 -0
  42. package/dist/harness/weakness-miner.d.ts +28 -0
  43. package/dist/harness/weakness-miner.js +359 -0
  44. package/dist/harness/weakness-miner.js.map +1 -0
  45. package/dist/integrations/harnesses.d.ts +23 -0
  46. package/dist/integrations/harnesses.js +161 -0
  47. package/dist/integrations/harnesses.js.map +1 -0
  48. package/dist/orchestration/competence.d.ts +53 -0
  49. package/dist/orchestration/competence.js +181 -0
  50. package/dist/orchestration/competence.js.map +1 -0
  51. package/dist/orchestration/executor.d.ts +42 -0
  52. package/dist/orchestration/executor.js +206 -0
  53. package/dist/orchestration/executor.js.map +1 -0
  54. package/dist/orchestration/index.d.ts +20 -0
  55. package/dist/orchestration/index.js +39 -0
  56. package/dist/orchestration/index.js.map +1 -0
  57. package/dist/orchestration/orchestrator-prompts.d.ts +11 -0
  58. package/dist/orchestration/orchestrator-prompts.js +153 -0
  59. package/dist/orchestration/orchestrator-prompts.js.map +1 -0
  60. package/dist/orchestration/orchestrator.d.ts +27 -0
  61. package/dist/orchestration/orchestrator.js +169 -0
  62. package/dist/orchestration/orchestrator.js.map +1 -0
  63. package/dist/orchestration/plan-store.d.ts +53 -0
  64. package/dist/orchestration/plan-store.js +176 -0
  65. package/dist/orchestration/plan-store.js.map +1 -0
  66. package/dist/orchestration/router-prompts.d.ts +6 -0
  67. package/dist/orchestration/router-prompts.js +55 -0
  68. package/dist/orchestration/router-prompts.js.map +1 -0
  69. package/dist/orchestration/router.d.ts +23 -0
  70. package/dist/orchestration/router.js +70 -0
  71. package/dist/orchestration/router.js.map +1 -0
  72. package/dist/orchestration/ruby-detect.d.ts +6 -0
  73. package/dist/orchestration/ruby-detect.js +130 -0
  74. package/dist/orchestration/ruby-detect.js.map +1 -0
  75. package/dist/orchestration/ruby-types.d.ts +44 -0
  76. package/dist/orchestration/ruby-types.js +6 -0
  77. package/dist/orchestration/ruby-types.js.map +1 -0
  78. package/dist/orchestration/specialist-prompts.d.ts +19 -0
  79. package/dist/orchestration/specialist-prompts.js +143 -0
  80. package/dist/orchestration/specialist-prompts.js.map +1 -0
  81. package/dist/orchestration/specialists.d.ts +66 -0
  82. package/dist/orchestration/specialists.js +175 -0
  83. package/dist/orchestration/specialists.js.map +1 -0
  84. package/dist/orchestration/types.d.ts +82 -0
  85. package/dist/orchestration/types.js +6 -0
  86. package/dist/orchestration/types.js.map +1 -0
  87. package/dist/perception/extractor.d.ts +7 -0
  88. package/dist/perception/extractor.js +521 -0
  89. package/dist/perception/extractor.js.map +1 -0
  90. package/dist/perception/graph-store.d.ts +25 -0
  91. package/dist/perception/graph-store.js +117 -0
  92. package/dist/perception/graph-store.js.map +1 -0
  93. package/dist/perception/index.d.ts +4 -0
  94. package/dist/perception/index.js +18 -0
  95. package/dist/perception/index.js.map +1 -0
  96. package/dist/perception/queries.d.ts +29 -0
  97. package/dist/perception/queries.js +354 -0
  98. package/dist/perception/queries.js.map +1 -0
  99. package/dist/perception/types.d.ts +103 -0
  100. package/dist/perception/types.js +6 -0
  101. package/dist/perception/types.js.map +1 -0
  102. package/dist/providers/anthropic.d.ts +11 -0
  103. package/dist/providers/anthropic.js +172 -0
  104. package/dist/providers/anthropic.js.map +1 -0
  105. package/dist/providers/factory.d.ts +58 -0
  106. package/dist/providers/factory.js +354 -0
  107. package/dist/providers/factory.js.map +1 -0
  108. package/dist/providers/fallback.d.ts +24 -0
  109. package/dist/providers/fallback.js +72 -0
  110. package/dist/providers/fallback.js.map +1 -0
  111. package/dist/providers/google.d.ts +11 -0
  112. package/dist/providers/google.js +129 -0
  113. package/dist/providers/google.js.map +1 -0
  114. package/dist/providers/openai-compatible.d.ts +24 -0
  115. package/dist/providers/openai-compatible.js +216 -0
  116. package/dist/providers/openai-compatible.js.map +1 -0
  117. package/dist/providers/resilient-factory.d.ts +24 -0
  118. package/dist/providers/resilient-factory.js +57 -0
  119. package/dist/providers/resilient-factory.js.map +1 -0
  120. package/dist/providers/resilient.d.ts +63 -0
  121. package/dist/providers/resilient.js +145 -0
  122. package/dist/providers/resilient.js.map +1 -0
  123. package/dist/providers/types.d.ts +84 -0
  124. package/dist/providers/types.js +6 -0
  125. package/dist/providers/types.js.map +1 -0
  126. package/dist/ruby/alternator.d.ts +40 -0
  127. package/dist/ruby/alternator.js +233 -0
  128. package/dist/ruby/alternator.js.map +1 -0
  129. package/dist/ruby/competence.d.ts +34 -0
  130. package/dist/ruby/competence.js +264 -0
  131. package/dist/ruby/competence.js.map +1 -0
  132. package/dist/ruby/episode-capture.d.ts +56 -0
  133. package/dist/ruby/episode-capture.js +193 -0
  134. package/dist/ruby/episode-capture.js.map +1 -0
  135. package/dist/ruby/fine-tune.d.ts +16 -0
  136. package/dist/ruby/fine-tune.js +253 -0
  137. package/dist/ruby/fine-tune.js.map +1 -0
  138. package/dist/ruby/index.d.ts +10 -0
  139. package/dist/ruby/index.js +26 -0
  140. package/dist/ruby/index.js.map +1 -0
  141. package/dist/ruby/ruby-model.d.ts +36 -0
  142. package/dist/ruby/ruby-model.js +81 -0
  143. package/dist/ruby/ruby-model.js.map +1 -0
  144. package/dist/ruby/training-data.d.ts +11 -0
  145. package/dist/ruby/training-data.js +107 -0
  146. package/dist/ruby/training-data.js.map +1 -0
  147. package/dist/ruby/types.d.ts +136 -0
  148. package/dist/ruby/types.js +20 -0
  149. package/dist/ruby/types.js.map +1 -0
  150. package/dist/safety/permissions.d.ts +32 -0
  151. package/dist/safety/permissions.js +304 -0
  152. package/dist/safety/permissions.js.map +1 -0
  153. package/dist/server/index.d.ts +9 -0
  154. package/dist/server/index.js +342 -0
  155. package/dist/server/index.js.map +1 -0
  156. package/dist/server/session.d.ts +22 -0
  157. package/dist/server/session.js +16 -0
  158. package/dist/server/session.js.map +1 -0
  159. package/dist/setup/first-run.d.ts +41 -0
  160. package/dist/setup/first-run.js +386 -0
  161. package/dist/setup/first-run.js.map +1 -0
  162. package/dist/setup/global-config.d.ts +19 -0
  163. package/dist/setup/global-config.js +97 -0
  164. package/dist/setup/global-config.js.map +1 -0
  165. package/dist/tools/audio-transcribe.d.ts +8 -0
  166. package/dist/tools/audio-transcribe.js +156 -0
  167. package/dist/tools/audio-transcribe.js.map +1 -0
  168. package/dist/tools/browser.d.ts +15 -0
  169. package/dist/tools/browser.js +241 -0
  170. package/dist/tools/browser.js.map +1 -0
  171. package/dist/tools/calendar.d.ts +12 -0
  172. package/dist/tools/calendar.js +196 -0
  173. package/dist/tools/calendar.js.map +1 -0
  174. package/dist/tools/clipboard.d.ts +7 -0
  175. package/dist/tools/clipboard.js +90 -0
  176. package/dist/tools/clipboard.js.map +1 -0
  177. package/dist/tools/cron.d.ts +10 -0
  178. package/dist/tools/cron.js +167 -0
  179. package/dist/tools/cron.js.map +1 -0
  180. package/dist/tools/edit-file.d.ts +6 -0
  181. package/dist/tools/edit-file.js +98 -0
  182. package/dist/tools/edit-file.js.map +1 -0
  183. package/dist/tools/email.d.ts +11 -0
  184. package/dist/tools/email.js +153 -0
  185. package/dist/tools/email.js.map +1 -0
  186. package/dist/tools/git.d.ts +1 -0
  187. package/dist/tools/git.js +7 -0
  188. package/dist/tools/git.js.map +1 -0
  189. package/dist/tools/http-request.d.ts +12 -0
  190. package/dist/tools/http-request.js +91 -0
  191. package/dist/tools/http-request.js.map +1 -0
  192. package/dist/tools/image-read.d.ts +7 -0
  193. package/dist/tools/image-read.js +125 -0
  194. package/dist/tools/image-read.js.map +1 -0
  195. package/dist/tools/index.d.ts +3 -0
  196. package/dist/tools/index.js +201 -0
  197. package/dist/tools/index.js.map +1 -0
  198. package/dist/tools/list-dir.d.ts +1 -0
  199. package/dist/tools/list-dir.js +6 -0
  200. package/dist/tools/list-dir.js.map +1 -0
  201. package/dist/tools/mcp.d.ts +11 -0
  202. package/dist/tools/mcp.js +356 -0
  203. package/dist/tools/mcp.js.map +1 -0
  204. package/dist/tools/memory.d.ts +9 -0
  205. package/dist/tools/memory.js +124 -0
  206. package/dist/tools/memory.js.map +1 -0
  207. package/dist/tools/notify.d.ts +9 -0
  208. package/dist/tools/notify.js +40 -0
  209. package/dist/tools/notify.js.map +1 -0
  210. package/dist/tools/read-file.d.ts +6 -0
  211. package/dist/tools/read-file.js +76 -0
  212. package/dist/tools/read-file.js.map +1 -0
  213. package/dist/tools/run-shell.d.ts +1 -0
  214. package/dist/tools/run-shell.js +6 -0
  215. package/dist/tools/run-shell.js.map +1 -0
  216. package/dist/tools/run-tests.d.ts +1 -0
  217. package/dist/tools/run-tests.js +6 -0
  218. package/dist/tools/run-tests.js.map +1 -0
  219. package/dist/tools/search-code.d.ts +1 -0
  220. package/dist/tools/search-code.js +6 -0
  221. package/dist/tools/search-code.js.map +1 -0
  222. package/dist/tools/telegram-bot.d.ts +2 -0
  223. package/dist/tools/telegram-bot.js +693 -0
  224. package/dist/tools/telegram-bot.js.map +1 -0
  225. package/dist/tools/telegram-safety.d.ts +52 -0
  226. package/dist/tools/telegram-safety.js +189 -0
  227. package/dist/tools/telegram-safety.js.map +1 -0
  228. package/dist/tools/telegram.d.ts +16 -0
  229. package/dist/tools/telegram.js +227 -0
  230. package/dist/tools/telegram.js.map +1 -0
  231. package/dist/tools/tools.d.ts +48 -0
  232. package/dist/tools/tools.js +328 -0
  233. package/dist/tools/tools.js.map +1 -0
  234. package/dist/tools/web-fetch.d.ts +11 -0
  235. package/dist/tools/web-fetch.js +119 -0
  236. package/dist/tools/web-fetch.js.map +1 -0
  237. package/dist/tools/web-search.d.ts +8 -0
  238. package/dist/tools/web-search.js +81 -0
  239. package/dist/tools/web-search.js.map +1 -0
  240. package/dist/tools/whatsapp.d.ts +10 -0
  241. package/dist/tools/whatsapp.js +222 -0
  242. package/dist/tools/whatsapp.js.map +1 -0
  243. package/dist/tools/write-file.d.ts +1 -0
  244. package/dist/tools/write-file.js +6 -0
  245. package/dist/tools/write-file.js.map +1 -0
  246. package/dist/tools/youtube-transcript.d.ts +9 -0
  247. package/dist/tools/youtube-transcript.js +200 -0
  248. package/dist/tools/youtube-transcript.js.map +1 -0
  249. package/dist/util/circuit-breaker.d.ts +44 -0
  250. package/dist/util/circuit-breaker.js +78 -0
  251. package/dist/util/circuit-breaker.js.map +1 -0
  252. package/dist/util/env.d.ts +18 -0
  253. package/dist/util/env.js +39 -0
  254. package/dist/util/env.js.map +1 -0
  255. package/dist/util/errors.d.ts +42 -0
  256. package/dist/util/errors.js +121 -0
  257. package/dist/util/errors.js.map +1 -0
  258. package/dist/util/rate-limiter.d.ts +71 -0
  259. package/dist/util/rate-limiter.js +141 -0
  260. package/dist/util/rate-limiter.js.map +1 -0
  261. package/dist/util/retry.d.ts +30 -0
  262. package/dist/util/retry.js +57 -0
  263. package/dist/util/retry.js.map +1 -0
  264. package/dist/util/sanitize.d.ts +16 -0
  265. package/dist/util/sanitize.js +255 -0
  266. package/dist/util/sanitize.js.map +1 -0
  267. package/dist/verify/checks.d.ts +21 -0
  268. package/dist/verify/checks.js +311 -0
  269. package/dist/verify/checks.js.map +1 -0
  270. package/dist/verify/index.d.ts +33 -0
  271. package/dist/verify/index.js +127 -0
  272. package/dist/verify/index.js.map +1 -0
  273. package/dist/verify/types.d.ts +20 -0
  274. package/dist/verify/types.js +3 -0
  275. package/dist/verify/types.js.map +1 -0
  276. package/dist/versioncheck.d.ts +22 -0
  277. package/dist/versioncheck.js +203 -0
  278. package/dist/versioncheck.js.map +1 -0
  279. package/dist/viz/index.d.ts +2 -0
  280. package/dist/viz/index.js +733 -0
  281. package/dist/viz/index.js.map +1 -0
  282. package/dist/workflows/engine.d.ts +46 -0
  283. package/dist/workflows/engine.js +320 -0
  284. package/dist/workflows/engine.js.map +1 -0
  285. package/dist/workflows/types.d.ts +92 -0
  286. package/dist/workflows/types.js +6 -0
  287. package/dist/workflows/types.js.map +1 -0
  288. 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