@cdoing/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cdoing/permissions.json +8 -0
- package/dist/callbacks.d.ts +17 -0
- package/dist/callbacks.d.ts.map +1 -0
- package/dist/callbacks.js +265 -0
- package/dist/callbacks.js.map +1 -0
- package/dist/chat.d.ts +27 -0
- package/dist/chat.d.ts.map +1 -0
- package/dist/chat.js +57 -0
- package/dist/chat.js.map +1 -0
- package/dist/commands.d.ts +22 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +452 -0
- package/dist/commands.js.map +1 -0
- package/dist/config.d.ts +84 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +427 -0
- package/dist/config.js.map +1 -0
- package/dist/help.d.ts +9 -0
- package/dist/help.d.ts.map +1 -0
- package/dist/help.js +167 -0
- package/dist/help.js.map +1 -0
- package/dist/history.d.ts +51 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +207 -0
- package/dist/history.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +220 -0
- package/dist/index.js.map +1 -0
- package/dist/oauth.d.ts +13 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +182 -0
- package/dist/oauth.js.map +1 -0
- package/dist/review.d.ts +26 -0
- package/dist/review.d.ts.map +1 -0
- package/dist/review.js +198 -0
- package/dist/review.js.map +1 -0
- package/dist/serve.d.ts +23 -0
- package/dist/serve.d.ts.map +1 -0
- package/dist/serve.js +293 -0
- package/dist/serve.js.map +1 -0
- package/dist/tools.d.ts +14 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +57 -0
- package/dist/tools.js.map +1 -0
- package/dist/ui/App.d.ts +24 -0
- package/dist/ui/App.d.ts.map +1 -0
- package/dist/ui/App.js +321 -0
- package/dist/ui/App.js.map +1 -0
- package/dist/ui/MessageList.d.ts +14 -0
- package/dist/ui/MessageList.d.ts.map +1 -0
- package/dist/ui/MessageList.js +147 -0
- package/dist/ui/MessageList.js.map +1 -0
- package/dist/ui/SessionBrowser.d.ts +18 -0
- package/dist/ui/SessionBrowser.d.ts.map +1 -0
- package/dist/ui/SessionBrowser.js +149 -0
- package/dist/ui/SessionBrowser.js.map +1 -0
- package/dist/ui/SetupWizard.d.ts +23 -0
- package/dist/ui/SetupWizard.d.ts.map +1 -0
- package/dist/ui/SetupWizard.js +402 -0
- package/dist/ui/SetupWizard.js.map +1 -0
- package/dist/ui/Spinner.d.ts +15 -0
- package/dist/ui/Spinner.d.ts.map +1 -0
- package/dist/ui/Spinner.js +111 -0
- package/dist/ui/Spinner.js.map +1 -0
- package/dist/ui/StatusBar.d.ts +16 -0
- package/dist/ui/StatusBar.d.ts.map +1 -0
- package/dist/ui/StatusBar.js +56 -0
- package/dist/ui/StatusBar.js.map +1 -0
- package/dist/ui/UserInput.d.ts +13 -0
- package/dist/ui/UserInput.d.ts.map +1 -0
- package/dist/ui/UserInput.js +872 -0
- package/dist/ui/UserInput.js.map +1 -0
- package/dist/ui/hooks/helpers.d.ts +55 -0
- package/dist/ui/hooks/helpers.d.ts.map +1 -0
- package/dist/ui/hooks/helpers.js +304 -0
- package/dist/ui/hooks/helpers.js.map +1 -0
- package/dist/ui/hooks/useAgent.d.ts +60 -0
- package/dist/ui/hooks/useAgent.d.ts.map +1 -0
- package/dist/ui/hooks/useAgent.js +213 -0
- package/dist/ui/hooks/useAgent.js.map +1 -0
- package/dist/ui/hooks/useChat.d.ts +74 -0
- package/dist/ui/hooks/useChat.d.ts.map +1 -0
- package/dist/ui/hooks/useChat.js +819 -0
- package/dist/ui/hooks/useChat.js.map +1 -0
- package/dist/ui/theme.d.ts +73 -0
- package/dist/ui/theme.d.ts.map +1 -0
- package/dist/ui/theme.js +214 -0
- package/dist/ui/theme.js.map +1 -0
- package/dist/ui/types.d.ts +37 -0
- package/dist/ui/types.d.ts.map +1 -0
- package/dist/ui/types.js +3 -0
- package/dist/ui/types.js.map +1 -0
- package/package.json +33 -0
- package/src/callbacks.ts +294 -0
- package/src/chat.ts +72 -0
- package/src/commands.ts +425 -0
- package/src/config.ts +462 -0
- package/src/help.ts +182 -0
- package/src/history.ts +205 -0
- package/src/index.ts +248 -0
- package/src/oauth.ts +164 -0
- package/src/review.ts +233 -0
- package/src/serve.ts +290 -0
- package/src/tools.ts +104 -0
- package/src/ui/App.tsx +426 -0
- package/src/ui/MessageList.tsx +222 -0
- package/src/ui/SessionBrowser.tsx +161 -0
- package/src/ui/SetupWizard.tsx +412 -0
- package/src/ui/Spinner.tsx +103 -0
- package/src/ui/StatusBar.tsx +106 -0
- package/src/ui/UserInput.tsx +954 -0
- package/src/ui/hooks/helpers.ts +271 -0
- package/src/ui/hooks/useAgent.ts +270 -0
- package/src/ui/hooks/useChat.ts +943 -0
- package/src/ui/theme.ts +326 -0
- package/src/ui/types.ts +41 -0
- package/tsconfig.json +18 -0
package/src/callbacks.ts
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Callbacks — formatted console output for interactive and one-shot modes.
|
|
3
|
+
* Includes token usage display and basic markdown rendering.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import type { Ora } from "ora";
|
|
8
|
+
import type { AgentCallbacks } from "@cdoing/ai";
|
|
9
|
+
import type { TurnUsage } from "@cdoing/ai";
|
|
10
|
+
|
|
11
|
+
// Tool categories with icons and colors
|
|
12
|
+
const TOOL_STYLES: Record<string, { icon: string; color: (s: string) => string }> = {
|
|
13
|
+
// File operations
|
|
14
|
+
file_read: { icon: "📖", color: chalk.hex("#4FC3F7") },
|
|
15
|
+
file_write: { icon: "✏️ ", color: chalk.hex("#81C784") },
|
|
16
|
+
file_edit: { icon: "🔧", color: chalk.hex("#FFB74D") },
|
|
17
|
+
// Search
|
|
18
|
+
glob_search: { icon: "🔍", color: chalk.hex("#BA68C8") },
|
|
19
|
+
grep_search: { icon: "🔎", color: chalk.hex("#9575CD") },
|
|
20
|
+
// Execution
|
|
21
|
+
shell_exec: { icon: "💻", color: chalk.hex("#4DD0E1") },
|
|
22
|
+
file_run: { icon: "▶️ ", color: chalk.hex("#4DB6AC") },
|
|
23
|
+
code_verify: { icon: "✅", color: chalk.hex("#AED581") },
|
|
24
|
+
// Web
|
|
25
|
+
web_fetch: { icon: "🌐", color: chalk.hex("#64B5F6") },
|
|
26
|
+
web_search: { icon: "🔮", color: chalk.hex("#7986CB") },
|
|
27
|
+
// Agent
|
|
28
|
+
sub_agent: { icon: "🤖", color: chalk.hex("#F06292") },
|
|
29
|
+
// Tasks
|
|
30
|
+
todo: { icon: "📋", color: chalk.hex("#FF8A65") },
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/** Get tool style or default */
|
|
34
|
+
function getToolStyle(name: string): { icon: string; color: (s: string) => string } {
|
|
35
|
+
return TOOL_STYLES[name] || { icon: "⚡", color: chalk.hex("#FFC107") };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Basic terminal markdown rendering */
|
|
39
|
+
function renderMarkdown(text: string): string {
|
|
40
|
+
let result = text;
|
|
41
|
+
|
|
42
|
+
// Code blocks with language (```lang\n...\n```)
|
|
43
|
+
result = result.replace(/```(\w+)?\n([\s\S]*?)```/g, (_match, lang, code) => {
|
|
44
|
+
const langColor = chalk.hex("#82AAFF");
|
|
45
|
+
const header = lang ? chalk.dim(" ┌─ ") + langColor(lang) + chalk.dim(" ─┐") + "\n" : "";
|
|
46
|
+
const formatted = code
|
|
47
|
+
.split("\n")
|
|
48
|
+
.map((line: string) => chalk.hex("#C3E88D")(` │ ${line}`))
|
|
49
|
+
.join("\n");
|
|
50
|
+
return `\n${header}${formatted}\n` + chalk.dim(" └────────┘\n");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Inline code
|
|
54
|
+
result = result.replace(/`([^`]+)`/g, (_m: string, code: string) =>
|
|
55
|
+
chalk.bgHex("#2D2D2D").hex("#FFD54F")(` ${code} `)
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// Bold
|
|
59
|
+
result = result.replace(/\*\*([^*]+)\*\*/g, (_m: string, t: string) => chalk.bold.hex("#FFFFFF")(t));
|
|
60
|
+
|
|
61
|
+
// Italic
|
|
62
|
+
result = result.replace(/(?<!\*)\*([^*]+)\*(?!\*)/g, (_m: string, t: string) => chalk.italic.hex("#B0BEC5")(t));
|
|
63
|
+
|
|
64
|
+
// Headers with gradient effect
|
|
65
|
+
result = result.replace(/^### (.+)$/gm, (_m: string, t: string) =>
|
|
66
|
+
chalk.hex("#4FC3F7")(" ▸ ") + chalk.bold.hex("#4FC3F7")(t)
|
|
67
|
+
);
|
|
68
|
+
result = result.replace(/^## (.+)$/gm, (_m: string, t: string) =>
|
|
69
|
+
chalk.hex("#29B6F6")(" ▸▸ ") + chalk.bold.hex("#29B6F6")(t)
|
|
70
|
+
);
|
|
71
|
+
result = result.replace(/^# (.+)$/gm, (_m: string, t: string) =>
|
|
72
|
+
chalk.hex("#03A9F4")(" ▸▸▸ ") + chalk.bold.hex("#03A9F4")(t)
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
// Bullet lists with colorful bullets
|
|
76
|
+
result = result.replace(/^(\s*)[-*] (.+)$/gm, (_m: string, indent: string, t: string) =>
|
|
77
|
+
`${indent} ${chalk.hex("#FF7043")("●")} ${t}`
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
// Numbered lists
|
|
81
|
+
result = result.replace(/^(\s*)(\d+)\. (.+)$/gm, (_m: string, indent: string, n: string, t: string) =>
|
|
82
|
+
`${indent} ${chalk.hex("#AB47BC")(n + ".")} ${t}`
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// Horizontal rules
|
|
86
|
+
result = result.replace(/^---+$/gm, chalk.hex("#546E7A")(" ═══════════════════════════════"));
|
|
87
|
+
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Format token usage for display with colors */
|
|
92
|
+
function formatUsage(usage: TurnUsage): string {
|
|
93
|
+
const parts: string[] = [];
|
|
94
|
+
if (usage.inputTokens > 0 || usage.outputTokens > 0) {
|
|
95
|
+
parts.push(
|
|
96
|
+
chalk.hex("#4FC3F7")(usage.inputTokens.toLocaleString()) +
|
|
97
|
+
chalk.hex("#78909C")(" → ") +
|
|
98
|
+
chalk.hex("#81C784")(usage.outputTokens.toLocaleString())
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
parts.push(chalk.hex("#B0BEC5")(`${usage.totalTokens.toLocaleString()} tokens`));
|
|
102
|
+
if (usage.cost !== undefined) {
|
|
103
|
+
parts.push(chalk.hex("#FFD54F")(`$${usage.cost.toFixed(4)}`));
|
|
104
|
+
}
|
|
105
|
+
return parts.join(chalk.hex("#546E7A")(" · "));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Interactive mode: spinner + colors + markdown */
|
|
109
|
+
export function createInteractiveCallbacks(spinner: Ora): AgentCallbacks {
|
|
110
|
+
let isFirstToken = true;
|
|
111
|
+
let buffer = "";
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
onToken: (token) => {
|
|
115
|
+
if (isFirstToken) {
|
|
116
|
+
spinner.stop();
|
|
117
|
+
process.stdout.write(chalk.hex("#E0E0E0")(" "));
|
|
118
|
+
isFirstToken = false;
|
|
119
|
+
}
|
|
120
|
+
// Buffer tokens and render markdown on newlines
|
|
121
|
+
buffer += token;
|
|
122
|
+
const lines = buffer.split("\n");
|
|
123
|
+
// Write all complete lines with markdown rendering
|
|
124
|
+
for (let i = 0; i < lines.length - 1; i++) {
|
|
125
|
+
const rendered = renderMarkdown(lines[i]);
|
|
126
|
+
process.stdout.write(rendered + "\n");
|
|
127
|
+
}
|
|
128
|
+
// Keep the last incomplete line in the buffer
|
|
129
|
+
buffer = lines[lines.length - 1];
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
onToolCall: (name, input) => {
|
|
133
|
+
// Flush buffer
|
|
134
|
+
if (buffer) {
|
|
135
|
+
process.stdout.write(renderMarkdown(buffer));
|
|
136
|
+
buffer = "";
|
|
137
|
+
}
|
|
138
|
+
spinner.stop();
|
|
139
|
+
|
|
140
|
+
const style = getToolStyle(name);
|
|
141
|
+
|
|
142
|
+
// Special formatting for todo tool
|
|
143
|
+
if (name === "todo") {
|
|
144
|
+
const action = (input as Record<string, unknown>).action;
|
|
145
|
+
const subject = (input as Record<string, unknown>).subject;
|
|
146
|
+
const status = (input as Record<string, unknown>).status;
|
|
147
|
+
const id = (input as Record<string, unknown>).id;
|
|
148
|
+
|
|
149
|
+
if (action === "create" && subject) {
|
|
150
|
+
console.log(chalk.hex("#FF8A65")(`\n 📋 Creating task: `) + chalk.bold.white(String(subject)));
|
|
151
|
+
} else if (action === "update" && id && status) {
|
|
152
|
+
const statusStyle = status === "completed"
|
|
153
|
+
? { icon: "✅", color: chalk.hex("#81C784") }
|
|
154
|
+
: status === "in_progress"
|
|
155
|
+
? { icon: "🔄", color: chalk.hex("#FFB74D") }
|
|
156
|
+
: { icon: "📌", color: chalk.hex("#90A4AE") };
|
|
157
|
+
console.log(statusStyle.color(`\n ${statusStyle.icon} Task #${id}: `) + chalk.bold.white(String(status)));
|
|
158
|
+
} else if (action === "list") {
|
|
159
|
+
console.log(chalk.hex("#FF8A65")(`\n 📋 Listing tasks`));
|
|
160
|
+
} else {
|
|
161
|
+
const preview = JSON.stringify(input).substring(0, 60);
|
|
162
|
+
console.log(`\n ${style.icon} ` + style.color(name) + chalk.hex("#78909C")(` ${preview}`));
|
|
163
|
+
}
|
|
164
|
+
} else {
|
|
165
|
+
// Colorful tool call display
|
|
166
|
+
const preview = formatToolPreview(name, input);
|
|
167
|
+
console.log(`\n ${style.icon} ` + style.color(name) + chalk.hex("#78909C")(` ${preview}`));
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
onToolResult: (name, result, isError) => {
|
|
172
|
+
const style = getToolStyle(name);
|
|
173
|
+
|
|
174
|
+
if (isError) {
|
|
175
|
+
console.log(chalk.hex("#EF5350")(` ✗ `) + chalk.hex("#EF5350").dim(name) + chalk.hex("#EF5350")(" failed"));
|
|
176
|
+
} else if (name === "todo") {
|
|
177
|
+
console.log(chalk.hex("#81C784")(` ✓ `) + chalk.hex("#A5D6A7")(result.split("\n")[0]));
|
|
178
|
+
} else {
|
|
179
|
+
const preview = result.substring(0, 100).replace(/\n/g, " ");
|
|
180
|
+
console.log(chalk.hex("#81C784")(` ✓ `) + style.color(name) + chalk.hex("#90A4AE")(` ${preview}`));
|
|
181
|
+
}
|
|
182
|
+
spinner.start(chalk.hex("#B0BEC5")(" 🧠 Thinking..."));
|
|
183
|
+
isFirstToken = true;
|
|
184
|
+
},
|
|
185
|
+
|
|
186
|
+
onComplete: () => {
|
|
187
|
+
// Flush remaining buffer
|
|
188
|
+
if (buffer) {
|
|
189
|
+
process.stdout.write(renderMarkdown(buffer));
|
|
190
|
+
buffer = "";
|
|
191
|
+
}
|
|
192
|
+
spinner.stop();
|
|
193
|
+
console.log("\n");
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
onError: (error) => {
|
|
197
|
+
if (buffer) {
|
|
198
|
+
process.stdout.write(renderMarkdown(buffer));
|
|
199
|
+
buffer = "";
|
|
200
|
+
}
|
|
201
|
+
spinner.stop();
|
|
202
|
+
console.log(chalk.hex("#EF5350")(`\n ❌ Error: `) + chalk.hex("#FFCDD2")(error.message) + "\n");
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
onUsage: (usage) => {
|
|
206
|
+
// Show usage with nice formatting
|
|
207
|
+
const formatted = formatUsage(usage);
|
|
208
|
+
console.log(chalk.hex("#546E7A")(` ╭─ `) + formatted + chalk.hex("#546E7A")(` ─╮`));
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** Format tool input preview based on tool type */
|
|
214
|
+
function formatToolPreview(name: string, input: Record<string, unknown>): string {
|
|
215
|
+
switch (name) {
|
|
216
|
+
case "file_read":
|
|
217
|
+
case "file_write":
|
|
218
|
+
case "file_edit":
|
|
219
|
+
return String(input.path || input.file_path || "").split("/").pop() || "";
|
|
220
|
+
case "shell_exec":
|
|
221
|
+
const cmd = String(input.command || "").substring(0, 50);
|
|
222
|
+
return cmd.length < String(input.command || "").length ? cmd + "..." : cmd;
|
|
223
|
+
case "glob_search":
|
|
224
|
+
return String(input.pattern || "");
|
|
225
|
+
case "grep_search":
|
|
226
|
+
return `"${String(input.pattern || "").substring(0, 30)}"`;
|
|
227
|
+
case "web_fetch":
|
|
228
|
+
case "web_search":
|
|
229
|
+
return String(input.url || input.query || "").substring(0, 40);
|
|
230
|
+
default:
|
|
231
|
+
return JSON.stringify(input).substring(0, 60);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** One-shot mode: colorful minimal output */
|
|
236
|
+
export function createOneShotCallbacks(): AgentCallbacks {
|
|
237
|
+
return {
|
|
238
|
+
onToken: (token) => process.stdout.write(token),
|
|
239
|
+
onToolCall: (name) => {
|
|
240
|
+
const style = getToolStyle(name);
|
|
241
|
+
console.log(`\n${style.icon} ` + style.color(name));
|
|
242
|
+
},
|
|
243
|
+
onToolResult: (name, _r, isErr) => {
|
|
244
|
+
if (isErr) {
|
|
245
|
+
console.log(chalk.hex("#EF5350")(`✗ ${name} failed`));
|
|
246
|
+
} else {
|
|
247
|
+
const style = getToolStyle(name);
|
|
248
|
+
console.log(chalk.hex("#81C784")(`✓ `) + style.color(name));
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
onComplete: () => console.log(),
|
|
252
|
+
onError: (err) => console.error(chalk.hex("#EF5350")(`❌ Error: `) + chalk.hex("#FFCDD2")(err.message)),
|
|
253
|
+
onUsage: (usage) => {
|
|
254
|
+
console.error(chalk.hex("#546E7A")(`╭─ `) + formatUsage(usage) + chalk.hex("#546E7A")(` ─╮`));
|
|
255
|
+
},
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** Print mode: simple text output (for --print flag) */
|
|
260
|
+
export function createPrintCallbacks(): AgentCallbacks {
|
|
261
|
+
return {
|
|
262
|
+
onToken: (token) => process.stdout.write(token),
|
|
263
|
+
onToolCall: () => {},
|
|
264
|
+
onToolResult: () => {},
|
|
265
|
+
onComplete: () => console.log(),
|
|
266
|
+
onError: (e) => console.error(e.message),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** JSON mode: structured JSON output (for --output-format json) */
|
|
271
|
+
export function createJsonCallbacks(): AgentCallbacks {
|
|
272
|
+
const result: { response: string; tools: Array<{ name: string; input: Record<string, unknown> }> } = {
|
|
273
|
+
response: "",
|
|
274
|
+
tools: [],
|
|
275
|
+
};
|
|
276
|
+
return {
|
|
277
|
+
onToken: (token) => { result.response += token; },
|
|
278
|
+
onToolCall: (name, input) => { result.tools.push({ name, input }); },
|
|
279
|
+
onToolResult: () => {},
|
|
280
|
+
onComplete: () => console.log(JSON.stringify(result, null, 2)),
|
|
281
|
+
onError: (e) => console.error(JSON.stringify({ error: e.message })),
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** Stream JSON mode: line-delimited JSON events (for --output-format stream-json) */
|
|
286
|
+
export function createStreamJsonCallbacks(): AgentCallbacks {
|
|
287
|
+
return {
|
|
288
|
+
onToken: (token) => console.log(JSON.stringify({ type: "token", data: token })),
|
|
289
|
+
onToolCall: (name, input) => console.log(JSON.stringify({ type: "tool_call", name, input })),
|
|
290
|
+
onToolResult: (name, result) => console.log(JSON.stringify({ type: "tool_result", name, result })),
|
|
291
|
+
onComplete: () => console.log(JSON.stringify({ type: "complete" })),
|
|
292
|
+
onError: (e) => console.log(JSON.stringify({ type: "error", message: e.message })),
|
|
293
|
+
};
|
|
294
|
+
}
|
package/src/chat.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChatInterface — launches the Ink-based TUI.
|
|
3
|
+
*
|
|
4
|
+
* This replaces the raw readline REPL with a React/Ink component tree,
|
|
5
|
+
* modelled on how Continue (continuedev/continue) builds its CLI.
|
|
6
|
+
*
|
|
7
|
+
* Structure:
|
|
8
|
+
* App.tsx — root Ink component
|
|
9
|
+
* MessageList — committed messages (via <Static>)
|
|
10
|
+
* StreamingMessage — live token stream
|
|
11
|
+
* UserInput — keyboard input + slash-command suggestions
|
|
12
|
+
* StatusBar — provider / model / mode / cost
|
|
13
|
+
* hooks/useChat — all agent state + slash-command logic
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import React from "react";
|
|
17
|
+
import { render } from "ink";
|
|
18
|
+
import type { ModelConfig } from "@cdoing/ai";
|
|
19
|
+
import type {
|
|
20
|
+
ToolRegistry,
|
|
21
|
+
PermissionManager,
|
|
22
|
+
HookManager,
|
|
23
|
+
MemoryStore,
|
|
24
|
+
TodoStore,
|
|
25
|
+
} from "@cdoing/core";
|
|
26
|
+
import { App } from "./ui/App";
|
|
27
|
+
import { printWelcome } from "./help";
|
|
28
|
+
import { initTheme } from "./ui/theme";
|
|
29
|
+
|
|
30
|
+
export class ChatInterface {
|
|
31
|
+
private modelConfig: Partial<ModelConfig>;
|
|
32
|
+
private toolRegistry: ToolRegistry;
|
|
33
|
+
private permissionManager: PermissionManager;
|
|
34
|
+
private hookManager: HookManager;
|
|
35
|
+
private memoryStore: MemoryStore;
|
|
36
|
+
private todoStore: TodoStore | null;
|
|
37
|
+
|
|
38
|
+
constructor(
|
|
39
|
+
modelConfig: Partial<ModelConfig>,
|
|
40
|
+
toolRegistry: ToolRegistry,
|
|
41
|
+
permissionManager: PermissionManager,
|
|
42
|
+
hookManager: HookManager,
|
|
43
|
+
memoryStore: MemoryStore,
|
|
44
|
+
todoStore?: TodoStore,
|
|
45
|
+
) {
|
|
46
|
+
this.modelConfig = modelConfig;
|
|
47
|
+
this.toolRegistry = toolRegistry;
|
|
48
|
+
this.permissionManager = permissionManager;
|
|
49
|
+
this.hookManager = hookManager;
|
|
50
|
+
this.memoryStore = memoryStore;
|
|
51
|
+
this.todoStore = todoStore || null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async start(initialPrompt?: string): Promise<void> {
|
|
55
|
+
initTheme();
|
|
56
|
+
printWelcome();
|
|
57
|
+
|
|
58
|
+
const { waitUntilExit } = render(
|
|
59
|
+
React.createElement(App, {
|
|
60
|
+
modelConfig: this.modelConfig,
|
|
61
|
+
toolRegistry: this.toolRegistry,
|
|
62
|
+
permissionManager: this.permissionManager,
|
|
63
|
+
hookManager: this.hookManager,
|
|
64
|
+
memoryStore: this.memoryStore,
|
|
65
|
+
todoStore: this.todoStore || undefined,
|
|
66
|
+
initialPrompt,
|
|
67
|
+
}),
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
await waitUntilExit();
|
|
71
|
+
}
|
|
72
|
+
}
|