duocode 1.2.1 → 1.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 (53) hide show
  1. package/dist/cli.js +245 -35
  2. package/dist/cli.js.map +1 -1
  3. package/dist/commands/duo.js +11 -8
  4. package/dist/commands/duo.js.map +1 -1
  5. package/dist/commands/repl_commands.d.ts +6 -0
  6. package/dist/commands/repl_commands.js +119 -25
  7. package/dist/commands/repl_commands.js.map +1 -1
  8. package/dist/commands/status.js +9 -0
  9. package/dist/commands/status.js.map +1 -1
  10. package/dist/duo/duo_orchestrator.js +67 -19
  11. package/dist/duo/duo_orchestrator.js.map +1 -1
  12. package/dist/duo/duo_types.d.ts +1 -1
  13. package/dist/duo/swarm_orchestrator.js +32 -12
  14. package/dist/duo/swarm_orchestrator.js.map +1 -1
  15. package/dist/providers/cost_tracker.js +22 -15
  16. package/dist/providers/cost_tracker.js.map +1 -1
  17. package/dist/providers/duo_pair_router.d.ts +3 -1
  18. package/dist/providers/duo_pair_router.js +18 -4
  19. package/dist/providers/duo_pair_router.js.map +1 -1
  20. package/dist/providers/factory.js +123 -118
  21. package/dist/providers/factory.js.map +1 -1
  22. package/dist/providers/role_scorer.d.ts +2 -0
  23. package/dist/providers/role_scorer.js +10 -7
  24. package/dist/providers/role_scorer.js.map +1 -1
  25. package/dist/providers/router.js +111 -63
  26. package/dist/providers/router.js.map +1 -1
  27. package/dist/tools/agent_loop.d.ts +5 -0
  28. package/dist/tools/agent_loop.js +2 -1
  29. package/dist/tools/agent_loop.js.map +1 -1
  30. package/dist/ui/autocomplete.d.ts +24 -0
  31. package/dist/ui/autocomplete.js +192 -0
  32. package/dist/ui/autocomplete.js.map +1 -0
  33. package/dist/ui/banner.d.ts +1 -1
  34. package/dist/ui/banner.js +71 -14
  35. package/dist/ui/banner.js.map +1 -1
  36. package/dist/ui/callbacks.js +5 -1
  37. package/dist/ui/callbacks.js.map +1 -1
  38. package/dist/ui/diff_panel.d.ts +22 -0
  39. package/dist/ui/diff_panel.js +171 -0
  40. package/dist/ui/diff_panel.js.map +1 -0
  41. package/dist/ui/neural_knot.d.ts +1 -0
  42. package/dist/ui/neural_knot.js +131 -0
  43. package/dist/ui/neural_knot.js.map +1 -0
  44. package/dist/ui/plan_viewer.d.ts +12 -0
  45. package/dist/ui/plan_viewer.js +117 -0
  46. package/dist/ui/plan_viewer.js.map +1 -0
  47. package/dist/ui/progress.d.ts +15 -0
  48. package/dist/ui/progress.js +103 -2
  49. package/dist/ui/progress.js.map +1 -1
  50. package/dist/ui/status_bar.d.ts +32 -0
  51. package/dist/ui/status_bar.js +107 -0
  52. package/dist/ui/status_bar.js.map +1 -0
  53. package/package.json +5 -1
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Live inline diff panel — renders a compact unified diff to the terminal
3
+ * immediately after a write_file or edit_file tool call, before the next
4
+ * model turn begins.
5
+ *
6
+ * Shows:
7
+ * ┌─ executor: edit src/auth/login.ts (+24 -3) ──────────────────┐
8
+ * │ + const token = jwt.sign(payload, secret, { expiresIn: '1h' })│
9
+ * │ - const token = createToken(payload) │
10
+ * └────────────────────────────────────────────────────────────────┘
11
+ */
12
+ import type { FileChange } from "../tools/tool_types.js";
13
+ /**
14
+ * Render a diff panel for a single file change and write it to stdout.
15
+ * Called from the agent loop's onToolCall hook.
16
+ */
17
+ export declare function renderFileDiff(change: FileChange): void;
18
+ /**
19
+ * Render a compact single-line tool action notification for non-write tools
20
+ * (read, search, command). Keeps the user informed without a full diff panel.
21
+ */
22
+ export declare function renderToolAction(tool: string, summary: string): void;
@@ -0,0 +1,171 @@
1
+ import { R, B, DM, GRN, RED, CYN, YLW, stripAnsi } from "./colors.js";
2
+ const MAX_PANEL_LINES = 18; // max diff lines shown per file change
3
+ const MAX_CONTEXT = 2; // unchanged context lines around each hunk
4
+ function computeDiff(oldText, newText) {
5
+ const oldLines = oldText.split("\n");
6
+ const newLines = newText.split("\n");
7
+ // Simple LCS-based diff (good enough for the size of edits we render)
8
+ const m = oldLines.length;
9
+ const n = newLines.length;
10
+ // Build LCS table
11
+ const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
12
+ for (let i = m - 1; i >= 0; i--) {
13
+ for (let j = n - 1; j >= 0; j--) {
14
+ if (oldLines[i] === newLines[j]) {
15
+ dp[i][j] = dp[i + 1][j + 1] + 1;
16
+ }
17
+ else {
18
+ dp[i][j] = Math.max(dp[i + 1][j], dp[i][j + 1]);
19
+ }
20
+ }
21
+ }
22
+ const result = [];
23
+ let i = 0;
24
+ let j = 0;
25
+ while (i < m || j < n) {
26
+ if (i < m && j < n && oldLines[i] === newLines[j]) {
27
+ result.push({ type: "ctx", text: oldLines[i] });
28
+ i++;
29
+ j++;
30
+ }
31
+ else if (j < n && (i >= m || dp[i][j + 1] >= dp[i + 1][j])) {
32
+ result.push({ type: "add", text: newLines[j] });
33
+ j++;
34
+ }
35
+ else {
36
+ result.push({ type: "del", text: oldLines[i] });
37
+ i++;
38
+ }
39
+ }
40
+ return result;
41
+ }
42
+ function extractHunks(diff, context) {
43
+ // Find indices of changed lines
44
+ const changed = new Set();
45
+ diff.forEach((line, i) => {
46
+ if (line.type !== "ctx")
47
+ changed.add(i);
48
+ });
49
+ // Expand with context window
50
+ const include = new Set();
51
+ for (const idx of changed) {
52
+ for (let k = Math.max(0, idx - context); k <= Math.min(diff.length - 1, idx + context); k++) {
53
+ include.add(k);
54
+ }
55
+ }
56
+ const result = [];
57
+ let prev = -2;
58
+ for (const idx of [...include].sort((a, b) => a - b)) {
59
+ if (idx > prev + 1)
60
+ result.push({ type: "ctx", text: "..." });
61
+ result.push(diff[idx]);
62
+ prev = idx;
63
+ }
64
+ return result;
65
+ }
66
+ // ── Rendering ────────────────────────────────────────────────────
67
+ function stats(diff) {
68
+ let added = 0;
69
+ let deleted = 0;
70
+ for (const line of diff) {
71
+ if (line.type === "add")
72
+ added++;
73
+ if (line.type === "del")
74
+ deleted++;
75
+ }
76
+ return { added, deleted };
77
+ }
78
+ function truncateText(text, max) {
79
+ if (text.length <= max)
80
+ return text;
81
+ return text.slice(0, max - 1) + "…";
82
+ }
83
+ function renderDiffLine(line, innerWidth) {
84
+ const text = truncateText(line.text, innerWidth - 2);
85
+ switch (line.type) {
86
+ case "add": return `${GRN}+${R} ${GRN}${text}${R}`;
87
+ case "del": return `${RED}-${R} ${RED}${text}${R}`;
88
+ case "ctx":
89
+ if (text === "...")
90
+ return `${DM} ···${R}`;
91
+ return `${DM} ${truncateText(text, innerWidth - 2)}${R}`;
92
+ }
93
+ }
94
+ /**
95
+ * Render a diff panel for a single file change and write it to stdout.
96
+ * Called from the agent loop's onToolCall hook.
97
+ */
98
+ export function renderFileDiff(change) {
99
+ if (!process.stdout.isTTY)
100
+ return;
101
+ const cols = process.stdout.columns ?? 80;
102
+ const innerWidth = cols - 4; // 2 chars border + 2 padding each side
103
+ const typeLabel = change.type === "create" ? "create" : "edit ";
104
+ const filePath = change.filePath;
105
+ const typeColor = change.type === "create" ? GRN : CYN;
106
+ // ── Compute diff ─────────────────────────────────────────────
107
+ let diffLines = [];
108
+ let addCount = 0;
109
+ let delCount = 0;
110
+ if (change.oldContent !== null && change.newContent !== null) {
111
+ const raw = computeDiff(change.oldContent, change.newContent);
112
+ const s = stats(raw);
113
+ addCount = s.added;
114
+ delCount = s.deleted;
115
+ diffLines = extractHunks(raw, MAX_CONTEXT);
116
+ }
117
+ else if (change.newContent !== null) {
118
+ // New file — show first few lines
119
+ const lines = change.newContent.split("\n");
120
+ addCount = lines.length;
121
+ diffLines = lines.slice(0, MAX_PANEL_LINES).map((t) => ({ type: "add", text: t }));
122
+ if (lines.length > MAX_PANEL_LINES) {
123
+ diffLines.push({ type: "ctx", text: `... +${lines.length - MAX_PANEL_LINES} more lines` });
124
+ }
125
+ }
126
+ // Trim to max panel lines
127
+ if (diffLines.length > MAX_PANEL_LINES) {
128
+ diffLines = diffLines.slice(0, MAX_PANEL_LINES);
129
+ diffLines.push({ type: "ctx", text: `... (diff truncated)` });
130
+ }
131
+ // ── Header ────────────────────────────────────────────────────
132
+ const statsStr = (addCount > 0 ? `${GRN}+${addCount}${R}` : "") +
133
+ (addCount > 0 && delCount > 0 ? " " : "") +
134
+ (delCount > 0 ? `${RED}-${delCount}${R}` : "");
135
+ const headerLabel = `${DM}executor: ${R}${typeColor}${B}${typeLabel}${R} ${B}${filePath}${R} ${statsStr}`;
136
+ const headerVisible = stripAnsi(headerLabel);
137
+ const dashCount = Math.max(0, cols - headerVisible.length - 5);
138
+ const header = `${DM}┌─${R} ${headerLabel} ${DM}${"─".repeat(dashCount)}┐${R}`;
139
+ // ── Body ──────────────────────────────────────────────────────
140
+ const bodyLines = diffLines.map((dl) => {
141
+ const rendered = renderDiffLine(dl, innerWidth);
142
+ const visible = stripAnsi(rendered);
143
+ const pad = Math.max(0, innerWidth - visible.length);
144
+ return `${DM}│${R} ${rendered}${" ".repeat(pad)} ${DM}│${R}`;
145
+ });
146
+ // ── Footer ────────────────────────────────────────────────────
147
+ const footer = `${DM}└${"─".repeat(cols - 2)}┘${R}`;
148
+ // ── Output ────────────────────────────────────────────────────
149
+ process.stdout.write("\n");
150
+ process.stdout.write(header + "\n");
151
+ for (const line of bodyLines) {
152
+ process.stdout.write(line + "\n");
153
+ }
154
+ process.stdout.write(footer + "\n\n");
155
+ }
156
+ /**
157
+ * Render a compact single-line tool action notification for non-write tools
158
+ * (read, search, command). Keeps the user informed without a full diff panel.
159
+ */
160
+ export function renderToolAction(tool, summary) {
161
+ if (!process.stdout.isTTY)
162
+ return;
163
+ const icon = tool === "run_command" ? `${YLW}⚙${R}` :
164
+ tool === "read_file" ? `${DM}↳${R}` :
165
+ tool === "list_directory" ? `${DM}↳${R}` :
166
+ tool === "search_files" ? `${CYN}⌕${R}` :
167
+ tool === "glob_files" ? `${CYN}⌕${R}` :
168
+ `${DM}·${R}`;
169
+ process.stdout.write(` ${icon} ${DM}${summary}${R}\n`);
170
+ }
171
+ //# sourceMappingURL=diff_panel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff_panel.js","sourceRoot":"","sources":["../../src/ui/diff_panel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function runNeuralKnotBoot(): Promise<void>;
@@ -0,0 +1,131 @@
1
+ import chalk from "chalk";
2
+ import { VERSION } from "./banner.js";
3
+ import { stripAnsi } from "./colors.js";
4
+ const HELIX_FRAMES = [
5
+ { top: " ◉══╲ ╱══◉ ", mid: " ╲ ╳ ╱ ", bot: " ◉══╱ ╲══◉ ", flat: "◉══╲ ╱══◉" },
6
+ { top: " ◉══╲ ╱══◉ ", mid: " ╲╱ ╲╱ ", bot: " ◉══╱ ╲══◉ ", flat: "◉═══╲ ╱═══◉" },
7
+ { top: " ◉══╲╱══◉ ", mid: " ╳ ╳ ", bot: " ◉══╱╲══◉ ", flat: "◉════╲╱════◉" },
8
+ { top: " ◉══╱ ╲══◉ ", mid: " ╱╲ ╱╲ ", bot: " ◉══╲ ╱══◉ ", flat: "◉═══╱ ╲═══◉" },
9
+ { top: " ◉══╱ ╲══◉ ", mid: " ╱ ╳ ╲ ", bot: " ◉══╲ ╱══◉ ", flat: "◉══╱ ╲══◉" },
10
+ { top: " ◉══╱ ╲══◉ ", mid: " ╱╲ ╱╲ ", bot: " ◉══╲ ╱══◉ ", flat: "◉═══╱ ╲═══◉" },
11
+ { top: " ◉══╱╲══◉ ", mid: " ╳ ╳ ", bot: " ◉══╲╱══◉ ", flat: "◉════╱╲════◉" },
12
+ { top: " ◉══╲ ╱══◉ ", mid: " ╲╱ ╲╱ ", bot: " ◉══╱ ╲══◉ ", flat: "◉═══╲ ╱═══◉" },
13
+ ];
14
+ const cyan = chalk.hex("#00F2FF");
15
+ const magenta = chalk.hex("#7000FF");
16
+ const mid = chalk.hex("#5e5e7a");
17
+ function sleep(ms) {
18
+ return new Promise((resolve) => setTimeout(resolve, ms));
19
+ }
20
+ class HelixSpinner {
21
+ plainMode;
22
+ intervalMs;
23
+ index = 0;
24
+ timer = null;
25
+ rendered = false;
26
+ lastPlainWidth = 0;
27
+ text = "";
28
+ constructor(plainMode, intervalMs) {
29
+ this.plainMode = plainMode;
30
+ this.intervalMs = intervalMs;
31
+ }
32
+ start(text) {
33
+ this.text = text;
34
+ if (!this.plainMode) {
35
+ process.stdout.write("\x1b[?25l");
36
+ }
37
+ this.render();
38
+ this.timer = setInterval(() => this.render(), this.intervalMs);
39
+ }
40
+ update(text) {
41
+ this.text = text;
42
+ this.render();
43
+ }
44
+ stop() {
45
+ if (this.timer) {
46
+ clearInterval(this.timer);
47
+ this.timer = null;
48
+ }
49
+ if (!this.plainMode) {
50
+ process.stdout.write("\x1b[?25h");
51
+ }
52
+ }
53
+ persistDone(message) {
54
+ if (this.plainMode) {
55
+ const line = `OK ${message}`;
56
+ const visible = stripAnsi(line).length;
57
+ const pad = Math.max(0, this.lastPlainWidth - visible);
58
+ process.stdout.write(`\r${line}${" ".repeat(pad)}\n`);
59
+ return;
60
+ }
61
+ const doneLine = `${chalk.hex("#00F2FF")("✔")} ${chalk.bold.white(message)}`;
62
+ if (this.rendered) {
63
+ process.stdout.write("\x1b[4F");
64
+ for (let i = 0; i < 3; i += 1) {
65
+ process.stdout.write("\x1b[2K\n");
66
+ }
67
+ process.stdout.write(`\x1b[2K${doneLine}\n`);
68
+ return;
69
+ }
70
+ process.stdout.write(`${doneLine}\n`);
71
+ }
72
+ render() {
73
+ const frame = HELIX_FRAMES[this.index % HELIX_FRAMES.length];
74
+ this.index += 1;
75
+ if (this.plainMode) {
76
+ const line = `${frame.flat} ${this.text}`;
77
+ const visible = stripAnsi(line).length;
78
+ const pad = Math.max(0, this.lastPlainWidth - visible);
79
+ process.stdout.write(`\r${line}${" ".repeat(pad)}`);
80
+ this.lastPlainWidth = visible;
81
+ return;
82
+ }
83
+ const lines = [
84
+ cyan(frame.top),
85
+ mid(frame.mid),
86
+ magenta(frame.bot),
87
+ chalk.white(this.text),
88
+ ];
89
+ if (this.rendered) {
90
+ process.stdout.write("\x1b[4F");
91
+ }
92
+ for (const line of lines) {
93
+ process.stdout.write(`\x1b[2K${line}\n`);
94
+ }
95
+ this.rendered = true;
96
+ }
97
+ }
98
+ export async function runNeuralKnotBoot() {
99
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
100
+ return;
101
+ }
102
+ if (process.env.DUOCODE_NO_ANIM === "1") {
103
+ return;
104
+ }
105
+ const plainMode = process.env.DUOCODE_PLAIN_ANIM === "1"
106
+ || process.env.NO_COLOR === "1"
107
+ || process.env.TERM === "dumb"
108
+ || chalk.level === 0;
109
+ const title = `DUOCODE\nv${VERSION}`;
110
+ const fastBoot = process.env.DUOCODE_FAST_BOOT === "1";
111
+ process.stdout.write("\n");
112
+ process.stdout.write(` ${title}\n\n`);
113
+ await sleep(fastBoot ? 120 : 280);
114
+ const spinner = new HelixSpinner(plainMode, 95);
115
+ try {
116
+ spinner.start("Agents synthesizing...");
117
+ await sleep(fastBoot ? 260 : 1300);
118
+ spinner.update(plainMode
119
+ ? "Intent Planner -> Code Builder"
120
+ : `${chalk.cyan("Intent Planner")} ${chalk.dim("->")} ${chalk.magenta("Code Builder")}`);
121
+ await sleep(fastBoot ? 280 : 1400);
122
+ spinner.update(plainMode ? "Merging reasoning paths..." : chalk.italic.gray("Merging reasoning paths..."));
123
+ await sleep(fastBoot ? 240 : 1000);
124
+ }
125
+ finally {
126
+ spinner.stop();
127
+ }
128
+ spinner.persistDone("Synthesis Complete.");
129
+ process.stdout.write("\n");
130
+ }
131
+ //# sourceMappingURL=neural_knot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"neural_knot.js","sourceRoot":"","sources":["../../src/ui/neural_knot.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import type { Interface as ReadlineInterface } from "node:readline/promises";
3
+ /**
4
+ * Render the architect plan as a collapsible section viewer.
5
+ * Prompts the user to expand sections or proceed.
6
+ *
7
+ * @returns the ReviewFeedback (same shape as architect review callback)
8
+ */
9
+ export declare function renderPlanWithFolding(plan: string, rl: ReadlineInterface | null, autoConfirm: boolean, log: (s: string) => void): Promise<{
10
+ action: "approve" | "revise" | "abort" | "discuss";
11
+ feedback?: string;
12
+ }>;
@@ -0,0 +1,117 @@
1
+ import { R, B, DM, CYN, BLU } from "./colors.js";
2
+ function parseSections(plan) {
3
+ const lines = plan.split("\n");
4
+ const sections = [];
5
+ let current = null;
6
+ for (const line of lines) {
7
+ if (/^#{1,3} /.test(line)) {
8
+ if (current)
9
+ sections.push(current);
10
+ current = { heading: line.replace(/^#+\s*/, ""), body: "" };
11
+ }
12
+ else if (current) {
13
+ current.body += line + "\n";
14
+ }
15
+ }
16
+ if (current)
17
+ sections.push(current);
18
+ return sections;
19
+ }
20
+ // ── Rendering ────────────────────────────────────────────────────
21
+ function renderSection(section, idx, expanded) {
22
+ const num = `${CYN}[${idx + 1}]${R}`;
23
+ const heading = `${B}${section.heading}${R}`;
24
+ const header = ` ${num} ${heading}`;
25
+ if (!expanded)
26
+ return header;
27
+ const bodyLines = section.body.trim().split("\n");
28
+ const body = bodyLines
29
+ .map((l) => ` ${DM}${l}${R}`)
30
+ .join("\n");
31
+ return `${header}\n${body}`;
32
+ }
33
+ function printSections(sections, expanded, log) {
34
+ log(`\n ${BLU}${B}Architect Plan${R} ${DM}(${sections.length} section${sections.length === 1 ? "" : "s"})${R}\n\n`);
35
+ for (let i = 0; i < sections.length; i++) {
36
+ log(renderSection(sections[i], i, expanded.has(i)) + "\n");
37
+ }
38
+ log("\n");
39
+ }
40
+ // ── Interactive folding viewer ───────────────────────────────────
41
+ /**
42
+ * Render the architect plan as a collapsible section viewer.
43
+ * Prompts the user to expand sections or proceed.
44
+ *
45
+ * @returns the ReviewFeedback (same shape as architect review callback)
46
+ */
47
+ export async function renderPlanWithFolding(plan, rl, autoConfirm, log) {
48
+ const sections = parseSections(plan);
49
+ // If no sections were found (no markdown headings), just show plan and ask
50
+ if (sections.length < 2) {
51
+ log(`\n${BLU}${B}Architect plan complete.${R}\n`);
52
+ log(`${DM}Review the plan above before proceeding to executor.${R}\n\n`);
53
+ if (autoConfirm || !rl)
54
+ return { action: "approve" };
55
+ const ans = await rl.question(`${CYN}Proceed: [Y]es / [v]iew / [r]evise / [d]iscuss / [n]o?${R} `);
56
+ return parseReviewAnswer(ans, plan, rl);
57
+ }
58
+ if (autoConfirm || !rl) {
59
+ return { action: "approve" };
60
+ }
61
+ const expanded = new Set();
62
+ printSections(sections, expanded, log);
63
+ log(` ${DM}Type a section number to expand it, or:${R}\n`);
64
+ log(` ${CYN}[Y]${R}${DM}es proceed ${CYN}[r]${R}${DM}evise ${CYN}[d]${R}${DM}iscuss ${CYN}[n]${R}${DM}o abort${R}\n\n`);
65
+ while (true) {
66
+ const ans = await rl.question(`${CYN}Section / action:${R} `);
67
+ const trimmed = ans.trim().toLowerCase();
68
+ // Expand section
69
+ const num = parseInt(trimmed, 10);
70
+ if (!isNaN(num) && num >= 1 && num <= sections.length) {
71
+ expanded.add(num - 1);
72
+ printSections(sections, expanded, log);
73
+ continue;
74
+ }
75
+ // Actions
76
+ if (trimmed === "" || trimmed === "y" || trimmed === "yes")
77
+ return { action: "approve" };
78
+ if (trimmed === "n" || trimmed === "no")
79
+ return { action: "abort" };
80
+ if (trimmed === "r" || trimmed === "revise") {
81
+ const fb = await rl.question(`${CYN}Revision feedback: ${R}`);
82
+ return { action: "revise", feedback: fb.trim() };
83
+ }
84
+ if (trimmed === "d" || trimmed === "discuss") {
85
+ const q = await rl.question(`${CYN}Your question: ${R}`);
86
+ return { action: "discuss", feedback: q.trim() };
87
+ }
88
+ if (trimmed === "all") {
89
+ for (let i = 0; i < sections.length; i++)
90
+ expanded.add(i);
91
+ printSections(sections, expanded, log);
92
+ continue;
93
+ }
94
+ log(`${DM}Unknown input — type a number (1-${sections.length}), Y, r, d, or n.${R}\n`);
95
+ }
96
+ }
97
+ async function parseReviewAnswer(ans, plan, rl) {
98
+ const trimmed = ans.trim().toLowerCase();
99
+ if (trimmed === "n" || trimmed === "no")
100
+ return { action: "abort" };
101
+ if (trimmed === "v" || trimmed === "view") {
102
+ // Just show the raw plan again
103
+ process.stdout.write(plan + "\n\n");
104
+ const ans2 = await rl.question(`${CYN}Proceed? [Y/r/n]${R} `);
105
+ return parseReviewAnswer(ans2, plan, rl);
106
+ }
107
+ if (trimmed === "r" || trimmed === "revise") {
108
+ const fb = await rl.question(`${CYN}Revision feedback: ${R}`);
109
+ return { action: "revise", feedback: fb.trim() };
110
+ }
111
+ if (trimmed === "d" || trimmed === "discuss") {
112
+ const q = await rl.question(`${CYN}Your question: ${R}`);
113
+ return { action: "discuss", feedback: q.trim() };
114
+ }
115
+ return { action: "approve" };
116
+ }
117
+ //# sourceMappingURL=plan_viewer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan_viewer.js","sourceRoot":"","sources":["../../src/ui/plan_viewer.ts"],"names":[],"mappings":"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"}
@@ -8,8 +8,23 @@ export declare class Spinner {
8
8
  constructor(message: string);
9
9
  start(): void;
10
10
  update(message: string): void;
11
+ stopAndPersist(symbol: string, text: string): void;
11
12
  stop(finalMessage?: string): void;
12
13
  }
14
+ export declare class NeuralKnotSpinner {
15
+ private readonly leftLabel;
16
+ private readonly rightLabel;
17
+ private intervalId;
18
+ private frameIndex;
19
+ private message;
20
+ private rendered;
21
+ constructor(message: string, leftLabel?: string, rightLabel?: string);
22
+ private draw;
23
+ start(): void;
24
+ update(message: string): void;
25
+ stopAndPersist(symbol: string, text: string): void;
26
+ stop(): void;
27
+ }
13
28
  export declare function phaseBanner(phaseNumber: number, totalPhases: number, label: string): string;
14
29
  export declare function liveTokenCounter(tokens: number, costUsd: number): string;
15
30
  export declare function formatElapsed(ms: number): string;
@@ -3,7 +3,14 @@
3
3
  */
4
4
  import { DM, R, B } from "./colors.js";
5
5
  // ── Spinner ─────────────────────────────────────────────────────
6
- const SPINNER_FRAMES = ["\u28CB", "\u28D9", "\u28F9", "\u28F8", "\u28FC", "\u28F4", "\u28E6", "\u28E7", "\u28C7", "\u28CF"];
6
+ const SPINNER_FRAMES = ["", "", "", "", "", "", "", ""];
7
+ function gradientChar(char, t) {
8
+ // Interpolate #00F2FF (cyan) → #7000FF (purple)
9
+ const r = Math.round(0x00 + t * (0x70 - 0x00));
10
+ const g = Math.round(0xF2 + t * (0x00 - 0xF2));
11
+ const b = 0xFF;
12
+ return `\x1b[38;2;${r};${g};${b}m${char}\x1b[0m`;
13
+ }
7
14
  export class Spinner {
8
15
  intervalId = null;
9
16
  frameIndex = 0;
@@ -17,13 +24,22 @@ export class Spinner {
17
24
  this.frameIndex = 0;
18
25
  this.intervalId = setInterval(() => {
19
26
  const frame = SPINNER_FRAMES[this.frameIndex % SPINNER_FRAMES.length];
20
- process.stdout.write(`\r\x1b[2K${DM}${frame} ${this.message}${R}`);
27
+ const t = (this.frameIndex % 20) / 20;
28
+ const colored = gradientChar(frame, t);
29
+ process.stdout.write(`\r\x1b[2K${colored} ${this.message}\x1b[0m`);
21
30
  this.frameIndex++;
22
31
  }, 80);
23
32
  }
24
33
  update(message) {
25
34
  this.message = message;
26
35
  }
36
+ stopAndPersist(symbol, text) {
37
+ if (this.intervalId) {
38
+ clearInterval(this.intervalId);
39
+ this.intervalId = null;
40
+ }
41
+ process.stdout.write(`\r\x1b[2K${symbol} ${text}\n`);
42
+ }
27
43
  stop(finalMessage) {
28
44
  if (this.intervalId) {
29
45
  clearInterval(this.intervalId);
@@ -35,6 +51,91 @@ export class Spinner {
35
51
  }
36
52
  }
37
53
  }
54
+ // ── Neural Knot Spinner ─────────────────────────────────────────
55
+ // Two colored strands weaving through each other — Architect (cyan)
56
+ // and Executor (purple) — forming a DNA-like braid as they hand off.
57
+ const NK_C = "\x1b[38;2;0;242;255m\x1b[1m"; // Architect strand: #00F2FF bold
58
+ const NK_P = "\x1b[38;2;112;0;255m\x1b[1m"; // Executor strand: #7000FF bold
59
+ // 8-row braid period. Each row is 7 visible chars wide.
60
+ // Reading top-to-bottom, the two strands cross twice per period.
61
+ const NK_PATTERN = [
62
+ /* 0 C-left parallel */ `${NK_C}│${R} ${NK_P}│${R}`,
63
+ /* 1 C-left converge */ ` ${NK_C}╲${R} ${NK_P}╱${R} `,
64
+ /* 2 crossing C→P */ ` ${NK_C}╳${R} `,
65
+ /* 3 P-left diverge */ ` ${NK_P}╱${R} ${NK_C}╲${R} `,
66
+ /* 4 P-left parallel */ `${NK_P}│${R} ${NK_C}│${R}`,
67
+ /* 5 P-left converge */ ` ${NK_P}╲${R} ${NK_C}╱${R} `,
68
+ /* 6 crossing P→C */ ` ${NK_P}╳${R} `,
69
+ /* 7 C-left diverge */ ` ${NK_C}╱${R} ${NK_P}╲${R} `,
70
+ ];
71
+ const NK_WINDOW = 5; // knot rows shown at once
72
+ const NK_LINES = NK_WINDOW + 2; // + role-label line + message line
73
+ export class NeuralKnotSpinner {
74
+ leftLabel;
75
+ rightLabel;
76
+ intervalId = null;
77
+ frameIndex = 0;
78
+ message;
79
+ rendered = false;
80
+ constructor(message, leftLabel = "Architect", rightLabel = "Executor") {
81
+ this.leftLabel = leftLabel;
82
+ this.rightLabel = rightLabel;
83
+ this.message = message;
84
+ }
85
+ draw() {
86
+ if (this.rendered) {
87
+ process.stdout.write(`\x1b[${NK_LINES}A`);
88
+ }
89
+ for (let i = 0; i < NK_WINDOW; i++) {
90
+ const row = NK_PATTERN[(this.frameIndex + i) % NK_PATTERN.length];
91
+ process.stdout.write(`\r\x1b[2K ${row}\n`);
92
+ }
93
+ process.stdout.write(`\r\x1b[2K ${NK_C}${this.leftLabel}${R} ${DM}↔${R} ${NK_P}${this.rightLabel}${R}\n`);
94
+ process.stdout.write(`\r\x1b[2K ${DM}${this.message}${R}\n`);
95
+ this.rendered = true;
96
+ }
97
+ start() {
98
+ if (this.intervalId)
99
+ return;
100
+ this.frameIndex = 0;
101
+ this.rendered = false;
102
+ this.intervalId = setInterval(() => {
103
+ this.draw();
104
+ this.frameIndex++;
105
+ }, 100);
106
+ }
107
+ update(message) {
108
+ this.message = message;
109
+ }
110
+ stopAndPersist(symbol, text) {
111
+ if (this.intervalId) {
112
+ clearInterval(this.intervalId);
113
+ this.intervalId = null;
114
+ }
115
+ if (this.rendered) {
116
+ // Erase all knot lines then write a single persistent line
117
+ process.stdout.write(`\x1b[${NK_LINES}A`);
118
+ for (let i = 0; i < NK_LINES; i++) {
119
+ process.stdout.write(`\r\x1b[2K\n`);
120
+ }
121
+ process.stdout.write(`\x1b[${NK_LINES}A`);
122
+ }
123
+ process.stdout.write(`\r\x1b[2K ${symbol} ${text}\n`);
124
+ }
125
+ stop() {
126
+ if (this.intervalId) {
127
+ clearInterval(this.intervalId);
128
+ this.intervalId = null;
129
+ }
130
+ if (this.rendered) {
131
+ process.stdout.write(`\x1b[${NK_LINES}A`);
132
+ for (let i = 0; i < NK_LINES; i++) {
133
+ process.stdout.write(`\r\x1b[2K\n`);
134
+ }
135
+ process.stdout.write(`\x1b[${NK_LINES}A\r\x1b[2K`);
136
+ }
137
+ }
138
+ }
38
139
  // ── Phase Banners ───────────────────────────────────────────────
39
140
  export function phaseBanner(phaseNumber, totalPhases, label) {
40
141
  return `${DM}[${phaseNumber}/${totalPhases}]${R} ${B}${label}${R}`;