skyloom 1.4.2 → 1.4.4
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/dist/cli/main.d.ts +0 -4
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +621 -289
- package/dist/cli/main.js.map +1 -1
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +5 -4
- package/dist/core/agent.js.map +1 -1
- package/dist/core/logger.d.ts.map +1 -1
- package/dist/core/logger.js +3 -4
- package/dist/core/logger.js.map +1 -1
- package/dist/core/memory.d.ts.map +1 -1
- package/dist/core/memory.js +3 -1
- package/dist/core/memory.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/main.ts +525 -338
package/dist/cli/main.js
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
-
/**
|
|
4
|
-
* CLI interface for Skyloom — terminal agent product.
|
|
5
|
-
* Uses Commander.js for command routing.
|
|
6
|
-
*/
|
|
7
3
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
4
|
if (k2 === undefined) k2 = k;
|
|
9
5
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
@@ -41,6 +37,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
41
37
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
42
38
|
};
|
|
43
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
/**
|
|
41
|
+
* 天空织机 CLI — Skyloom Terminal Interface
|
|
42
|
+
* Raw-mode input + slash command popup + streaming display
|
|
43
|
+
*/
|
|
44
44
|
const commander_1 = require("commander");
|
|
45
45
|
const fs = __importStar(require("fs"));
|
|
46
46
|
const readline = __importStar(require("readline"));
|
|
@@ -50,353 +50,685 @@ const config_1 = require("../core/config");
|
|
|
50
50
|
const router_1 = require("../core/router");
|
|
51
51
|
const mode_1 = require("./mode");
|
|
52
52
|
const MODE = new mode_1.ModeController();
|
|
53
|
-
const VERSION =
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
.option(
|
|
80
|
-
.action(async (
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
53
|
+
const VERSION = "1.4.2";
|
|
54
|
+
/* ── Agent colors ── */
|
|
55
|
+
const AGENT_COLORS = {
|
|
56
|
+
fog: "#b8c6db", rain: "#4a90d9", frost: "#2cd4d4",
|
|
57
|
+
snow: "#e8ecf1", dew: "#7bed9f", fair: "#f7b733",
|
|
58
|
+
};
|
|
59
|
+
const AGENT_DISPLAY = {
|
|
60
|
+
fog: "≋ 雾 Fog", rain: "⸽ 雨 Rain", frost: "✱ 霜 Frost",
|
|
61
|
+
snow: "❉ 雪 Snow", dew: "∘ 露 Dew", fair: "☼ 晴 Fair",
|
|
62
|
+
};
|
|
63
|
+
const AGENT_NAMES = ["fog", "rain", "frost", "snow", "dew", "fair"];
|
|
64
|
+
/* ═══════════════════════════════════════
|
|
65
|
+
Commander program
|
|
66
|
+
═══════════════════════════════════════ */
|
|
67
|
+
const program = new commander_1.Command()
|
|
68
|
+
.name("sky").description("天空织机 Skyloom — 6 weather-themed AI agents").version(VERSION);
|
|
69
|
+
program.command("chat").description("Start interactive chat")
|
|
70
|
+
.argument("[agent]", "agent name", "fog")
|
|
71
|
+
.option("-m,--model <model>", "Model override")
|
|
72
|
+
.action(async (a, o) => { await chat(a, o.model); });
|
|
73
|
+
program.command("task").description("Multi-agent orchestration")
|
|
74
|
+
.argument("[goal]", "task goal")
|
|
75
|
+
.option("-r,--resume", "resume from checkpoint")
|
|
76
|
+
.action(async (g, o) => { if (g)
|
|
77
|
+
await runTask(g, o?.resume); });
|
|
78
|
+
program.command("web").description("Start web server")
|
|
79
|
+
.option("-p,--port <port>", "port", "3000")
|
|
80
|
+
.action(async (o) => { const { startWebServer } = await Promise.resolve().then(() => __importStar(require("../web/server"))); await startWebServer(parseInt(o.port || "3000", 10)); });
|
|
81
|
+
program.command("mcp").description("Start MCP server")
|
|
82
|
+
.action(async () => { const { startMCPServer } = await Promise.resolve().then(() => __importStar(require("../core/mcp_server"))); await startMCPServer(); });
|
|
83
|
+
program.command("config").description("Show configuration")
|
|
84
|
+
.action(() => { const c = (0, config_1.loadConfig)(); logLine(chalk_1.default.cyan("Config dir: ") + config_1.USER_CONFIG_DIR); logLine(chalk_1.default.cyan("Agent models:")); for (const [n, a] of Object.entries(c.agents || {}))
|
|
85
|
+
logLine(` ${chalk_1.default.bold(n)}: ${a.model || "default"}`); });
|
|
86
|
+
program.command("init").description("Initialize config directory")
|
|
87
|
+
.action(() => { const d = config_1.USER_CONFIG_DIR; if (!fs.existsSync(d))
|
|
88
|
+
fs.mkdirSync(d, { recursive: true }); logLine(chalk_1.default.green("✓ ") + d); });
|
|
89
|
+
program.command("version").description("Show version")
|
|
90
|
+
.action(() => logLine(`Skyloom v${VERSION}`));
|
|
91
|
+
/* ═══════════════════════════════════════
|
|
92
|
+
Interactive Chat — raw-mode input + popup
|
|
93
|
+
═══════════════════════════════════════ */
|
|
94
|
+
const SLASH_CMDS = [
|
|
95
|
+
["/help", "Show all commands", false, ""],
|
|
96
|
+
["/clear", "Clear screen", false, ""],
|
|
97
|
+
["/status", "Agent overview", false, ""],
|
|
98
|
+
["/cost", "Usage & cost", false, ""],
|
|
99
|
+
["/cost reset", "Reset usage stats", false, ""],
|
|
100
|
+
["/compact", "Compress context", false, ""],
|
|
101
|
+
["/retry", "Resend last message", false, ""],
|
|
102
|
+
["/mcp", "MCP server status", false, ""],
|
|
103
|
+
["/memory", "Memory stats", false, ""],
|
|
104
|
+
["/sessions", "Session list", false, ""],
|
|
105
|
+
["/workspace", "Workspace info", false, ""],
|
|
106
|
+
["/model", "Model info", false, ""],
|
|
107
|
+
["/version", "Version info", false, ""],
|
|
108
|
+
["/task <goal>", "Multi-agent orchestrate", true, ""],
|
|
109
|
+
["/quiz", "Export chat as quiz", false, ""],
|
|
110
|
+
["/fog", "≋ Fog — research", false, "fog"],
|
|
111
|
+
["/rain", "⸽ Rain — codegen", false, "rain"],
|
|
112
|
+
["/frost", "✱ Frost — review", false, "frost"],
|
|
113
|
+
["/snow", "❉ Snow — planning", false, "snow"],
|
|
114
|
+
["/dew", "∘ Dew — devops", false, "dew"],
|
|
115
|
+
["/fair", "☼ Fair — companion", false, "fair"],
|
|
116
|
+
["/quit", "Exit chat", false, ""],
|
|
117
|
+
["/exit", "Exit chat", false, ""],
|
|
118
|
+
];
|
|
119
|
+
function logLine(s) { process.stdout.write(s + "\n"); }
|
|
120
|
+
/* ── Stream response with spinner ── */
|
|
121
|
+
async function chatWithSpinner(agent, ctx, message) {
|
|
122
|
+
let frame = 0;
|
|
123
|
+
const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
124
|
+
const spinner = setInterval(() => {
|
|
125
|
+
readline.cursorTo(process.stdout, 0);
|
|
126
|
+
process.stdout.write(chalk_1.default.cyan(` ${frames[frame % frames.length]} ${agent.displayName} thinking...`));
|
|
127
|
+
frame++;
|
|
128
|
+
}, 80);
|
|
85
129
|
try {
|
|
86
|
-
await
|
|
130
|
+
const response = await agent.chat(message);
|
|
131
|
+
clearInterval(spinner);
|
|
132
|
+
readline.cursorTo(process.stdout, 0);
|
|
133
|
+
process.stdout.write(" ".repeat(50) + "\r");
|
|
134
|
+
return response;
|
|
87
135
|
}
|
|
88
136
|
catch (e) {
|
|
89
|
-
|
|
137
|
+
clearInterval(spinner);
|
|
138
|
+
throw e;
|
|
90
139
|
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
.
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
140
|
+
}
|
|
141
|
+
/* ── Render response ── */
|
|
142
|
+
function renderResponse(text) {
|
|
143
|
+
const lines = [];
|
|
144
|
+
const w = process.stdout.columns || 80;
|
|
145
|
+
const maxW = Math.min(w - 6, 76);
|
|
146
|
+
for (const block of text.split("\n\n")) {
|
|
147
|
+
const trimmed = block.trim();
|
|
148
|
+
if (!trimmed)
|
|
149
|
+
continue;
|
|
150
|
+
// Code blocks
|
|
151
|
+
if (trimmed.startsWith("```")) {
|
|
152
|
+
const codeLines = trimmed.split("\n");
|
|
153
|
+
lines.push(chalk_1.default.dim(" ┌─ code ──────────────"));
|
|
154
|
+
for (let i = 1; i < codeLines.length - 1; i++) {
|
|
155
|
+
const cl = codeLines[i];
|
|
156
|
+
lines.push(` ${chalk_1.default.dim("│")} ${chalk_1.default.white(cl.slice(0, maxW - 4))}`);
|
|
157
|
+
}
|
|
158
|
+
lines.push(chalk_1.default.dim(" └────────────────────"));
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
// Wrap long lines
|
|
162
|
+
for (const line of trimmed.split("\n")) {
|
|
163
|
+
if (line.startsWith("#")) {
|
|
164
|
+
lines.push(" " + chalk_1.default.bold(line));
|
|
165
|
+
}
|
|
166
|
+
else if (line.startsWith("- ") || line.startsWith("* ")) {
|
|
167
|
+
lines.push(" " + chalk_1.default.dim("• ") + line.slice(2));
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
let remaining = line;
|
|
171
|
+
while (remaining.length > maxW) {
|
|
172
|
+
const cut = remaining.lastIndexOf(" ", maxW);
|
|
173
|
+
const idx = cut > 0 ? cut : maxW;
|
|
174
|
+
lines.push(" " + remaining.slice(0, idx));
|
|
175
|
+
remaining = remaining.slice(idx).trimStart();
|
|
176
|
+
}
|
|
177
|
+
if (remaining)
|
|
178
|
+
lines.push(" " + remaining);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
103
181
|
}
|
|
104
|
-
|
|
105
|
-
|
|
182
|
+
return lines;
|
|
183
|
+
}
|
|
184
|
+
/* ── Welcome banner ── */
|
|
185
|
+
function printWelcome(agent, model) {
|
|
186
|
+
const w = process.stdout.columns || 80;
|
|
187
|
+
logLine("");
|
|
188
|
+
logLine(" ".repeat(Math.max(0, Math.floor((w - 40) / 2))) + chalk_1.default.cyan("✦ 天 空 织 机 ✦"));
|
|
189
|
+
logLine(" ".repeat(Math.max(0, Math.floor((w - 36) / 2))) + chalk_1.default.dim("S K Y L O O M"));
|
|
190
|
+
logLine("");
|
|
191
|
+
const agentLine = [];
|
|
192
|
+
for (const name of AGENT_NAMES) {
|
|
193
|
+
const active = name === agent.name;
|
|
194
|
+
const prefix = active ? chalk_1.default.bold(AGENT_DISPLAY[name].split(" ").slice(0, 2).join(" ")) : chalk_1.default.dim(AGENT_DISPLAY[name].split(" ")[0] + " " + AGENT_DISPLAY[name].split(" ")[1]);
|
|
195
|
+
agentLine.push(prefix);
|
|
106
196
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
197
|
+
logLine(" " + agentLine.join(chalk_1.default.dim(" · ")));
|
|
198
|
+
logLine("");
|
|
199
|
+
logLine(chalk_1.default.dim(` Model: ${model} · /help for commands · /quit to exit`));
|
|
200
|
+
logLine("");
|
|
201
|
+
}
|
|
202
|
+
/* ── Status bar ── */
|
|
203
|
+
function statusBar(agent, ctx) {
|
|
204
|
+
let ctxStr = "";
|
|
205
|
+
let costStr = "$0";
|
|
206
|
+
let modelStr = "default";
|
|
113
207
|
try {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
208
|
+
const cu = agent.contextUsage();
|
|
209
|
+
modelStr = cu.model || "?";
|
|
210
|
+
const pct = cu.pct || 0;
|
|
211
|
+
const barColor = pct < 50 ? chalk_1.default.green : pct < 80 ? chalk_1.default.yellow : chalk_1.default.red;
|
|
212
|
+
const barLen = Math.round(pct / 10);
|
|
213
|
+
ctxStr = `${barColor("█".repeat(barLen) + "░".repeat(10 - barLen))} ${pct}%`;
|
|
214
|
+
costStr = formatCost(ctx.llm.getTotalCost());
|
|
121
215
|
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const configDir = config_1.USER_CONFIG_DIR;
|
|
143
|
-
if (!fs.existsSync(configDir)) {
|
|
144
|
-
fs.mkdirSync(configDir, { recursive: true });
|
|
216
|
+
catch { /* ignore */ }
|
|
217
|
+
const w = process.stdout.columns || 80;
|
|
218
|
+
return chalk_1.default.dim(`┤ ${ctxStr} · ${costStr} · ${modelStr} ├${"─".repeat(Math.max(0, w - 60))}`);
|
|
219
|
+
}
|
|
220
|
+
function formatCost(cost) {
|
|
221
|
+
if (cost >= 1)
|
|
222
|
+
return chalk_1.default.yellow(`$${cost.toFixed(2)}`);
|
|
223
|
+
if (cost >= 0.01)
|
|
224
|
+
return chalk_1.default.yellow(`$${cost.toFixed(4)}`);
|
|
225
|
+
if (cost > 0)
|
|
226
|
+
return chalk_1.default.green(`${(cost * 100).toFixed(2)}¢`);
|
|
227
|
+
return "$0";
|
|
228
|
+
}
|
|
229
|
+
/* ── Slash-command popup ── */
|
|
230
|
+
async function readWithPopup(agent, ctx) {
|
|
231
|
+
if (!process.stdin.isTTY) {
|
|
232
|
+
return new Promise(resolve => {
|
|
233
|
+
const rl = readline.createInterface({ input: process.stdin });
|
|
234
|
+
rl.on("line", (line) => { rl.close(); resolve(line.trim()); });
|
|
235
|
+
});
|
|
145
236
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
237
|
+
return new Promise(resolve => {
|
|
238
|
+
const stdin = process.stdin;
|
|
239
|
+
try {
|
|
240
|
+
stdin.setRawMode?.(true);
|
|
241
|
+
}
|
|
242
|
+
catch { /* non-TTY */ }
|
|
243
|
+
stdin.resume();
|
|
244
|
+
let buf = "";
|
|
245
|
+
let cursor = 0;
|
|
246
|
+
let popup = false;
|
|
247
|
+
let selIdx = 0;
|
|
248
|
+
const _history = readWithPopup._popupHistory || [];
|
|
249
|
+
let histIdx = _history.length;
|
|
250
|
+
function render() {
|
|
251
|
+
const w = process.stdout.columns || 80;
|
|
252
|
+
readline.cursorTo(process.stdout, 0);
|
|
253
|
+
// Clear current line area
|
|
254
|
+
const promptLine = ` ${chalk_1.default.cyan(agent.displayName)} ${chalk_1.default.dim("❯")} `;
|
|
255
|
+
// Build filtered commands
|
|
256
|
+
const filtered = popup ? SLASH_CMDS.filter(c => c[0].toLowerCase().includes(buf.toLowerCase())) : [];
|
|
257
|
+
if (filtered.length && selIdx >= filtered.length)
|
|
258
|
+
selIdx = filtered.length - 1;
|
|
259
|
+
const popupH = Math.min(filtered.length, 10);
|
|
260
|
+
const totalH = popup ? popupH + 3 : 1;
|
|
261
|
+
// Move cursor up
|
|
262
|
+
if (popup) {
|
|
263
|
+
for (let i = 0; i < popupH + 2; i++)
|
|
264
|
+
process.stdout.write("\x1b[1A\x1b[2K");
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
process.stdout.write("\x1b[2K\r");
|
|
268
|
+
}
|
|
269
|
+
// Input line
|
|
270
|
+
const before = buf.slice(0, cursor);
|
|
271
|
+
const after = buf.slice(cursor);
|
|
272
|
+
const cursorChar = after[0] || " ";
|
|
273
|
+
process.stdout.write(promptLine + before + chalk_1.default.inverse(cursorChar) + after.slice(1) + "\n");
|
|
274
|
+
// Popup
|
|
275
|
+
if (popup && filtered.length) {
|
|
276
|
+
const maxW = Math.min(w - 4, 60);
|
|
277
|
+
const start = Math.max(0, Math.min(selIdx - 4, filtered.length - 8));
|
|
278
|
+
const end = Math.min(filtered.length, start + 8);
|
|
279
|
+
process.stdout.write(chalk_1.default.dim(` ┌─ commands (↑↓ pick · type to filter · tab/enter select · esc close)${"─".repeat(Math.max(0, maxW - 58))}┐\n`));
|
|
280
|
+
for (let i = start; i < end; i++) {
|
|
281
|
+
const [cmd, desc] = filtered[i];
|
|
282
|
+
const marker = i === selIdx ? chalk_1.default.cyan(" ▶ ") : " ";
|
|
283
|
+
const cmdColored = i === selIdx ? chalk_1.default.bold(cmd) : chalk_1.default.cyan(cmd);
|
|
284
|
+
const line = ` │${marker}${cmdColored.padEnd(24)}${chalk_1.default.dim(desc)}`;
|
|
285
|
+
process.stdout.write(line + " ".repeat(Math.max(0, maxW - line.length + 6)) + "│\n");
|
|
286
|
+
}
|
|
287
|
+
process.stdout.write(chalk_1.default.dim(` └${"─".repeat(maxW + 1)}┘\n`));
|
|
288
|
+
}
|
|
289
|
+
// Status bar
|
|
290
|
+
process.stdout.write(statusBar(agent, ctx) + (popup ? "" : "\n"));
|
|
291
|
+
}
|
|
292
|
+
function accept(line) {
|
|
293
|
+
try {
|
|
294
|
+
stdin.setRawMode?.(false);
|
|
295
|
+
}
|
|
296
|
+
catch { }
|
|
297
|
+
stdin.pause();
|
|
298
|
+
resolve(line);
|
|
299
|
+
}
|
|
300
|
+
stdin.on("data", (data) => {
|
|
301
|
+
const seq = data.toString();
|
|
302
|
+
for (const ch of seq) {
|
|
303
|
+
// Esc
|
|
304
|
+
if (ch === "\x1b") {
|
|
305
|
+
if (popup) {
|
|
306
|
+
popup = false;
|
|
307
|
+
render();
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
accept("");
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
// Enter
|
|
314
|
+
if (ch === "\r" || ch === "\n") {
|
|
315
|
+
if (popup && SLASH_CMDS.filter(c => c[0].toLowerCase().includes(buf.toLowerCase())).length) {
|
|
316
|
+
const filtered = SLASH_CMDS.filter(c => c[0].toLowerCase().includes(buf.toLowerCase()));
|
|
317
|
+
buf = filtered[selIdx]?.[0] || buf;
|
|
318
|
+
popup = false;
|
|
319
|
+
}
|
|
320
|
+
accept(buf.trim());
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
// Tab
|
|
324
|
+
if (ch === "\t") {
|
|
325
|
+
if (popup && SLASH_CMDS.filter(c => c[0].toLowerCase().includes(buf.toLowerCase())).length) {
|
|
326
|
+
const filtered = SLASH_CMDS.filter(c => c[0].toLowerCase().includes(buf.toLowerCase()));
|
|
327
|
+
buf = filtered[selIdx]?.[0] || buf;
|
|
328
|
+
cursor = buf.length;
|
|
329
|
+
popup = false;
|
|
330
|
+
render();
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
// Insert 2 spaces
|
|
334
|
+
buf = buf.slice(0, cursor) + " " + buf.slice(cursor);
|
|
335
|
+
cursor += 2;
|
|
336
|
+
render();
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
// Backspace
|
|
340
|
+
if (ch === "\x7f" || ch === "\b") {
|
|
341
|
+
if (cursor > 0) {
|
|
342
|
+
buf = buf.slice(0, cursor - 1) + buf.slice(cursor);
|
|
343
|
+
cursor--;
|
|
344
|
+
}
|
|
345
|
+
if (!buf) {
|
|
346
|
+
popup = false;
|
|
347
|
+
}
|
|
348
|
+
render();
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
// Ctrl+C
|
|
352
|
+
if (ch === "\x03") {
|
|
353
|
+
accept("/quit");
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
// Printable
|
|
357
|
+
if (ch >= " ") {
|
|
358
|
+
buf = buf.slice(0, cursor) + ch + buf.slice(cursor);
|
|
359
|
+
cursor++;
|
|
360
|
+
if (buf === "/") {
|
|
361
|
+
popup = true;
|
|
362
|
+
selIdx = 0;
|
|
363
|
+
}
|
|
364
|
+
else if (popup) {
|
|
365
|
+
selIdx = 0;
|
|
366
|
+
}
|
|
367
|
+
render();
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
// Arrow keys come as escape sequences — handle via process.stdin
|
|
373
|
+
// For simplicity, arrow keys in raw mode are: \x1b[A (up), \x1b[B (down), \x1b[C (right), \x1b[D (left)
|
|
374
|
+
// We handle them in the data handler above by checking for escape sequences
|
|
375
|
+
// Actually the raw mode data comes byte by byte — let me use a state machine approach
|
|
376
|
+
// For now, let me handle the common case: the full escape sequence arrives in one data event
|
|
377
|
+
// Note: Arrow keys are 3 bytes: \x1b [ A/B/C/D. They may arrive in one or multiple data events.
|
|
378
|
+
// In practice on Windows they arrive as one event. Let me add a simple buffered approach.
|
|
379
|
+
stdin.removeAllListeners("data");
|
|
380
|
+
let escBuf = "";
|
|
381
|
+
stdin.on("data", (data) => {
|
|
382
|
+
const str = data.toString();
|
|
383
|
+
escBuf += str;
|
|
384
|
+
// If we have an escape sequence, process it
|
|
385
|
+
if (escBuf.startsWith("\x1b[")) {
|
|
386
|
+
if (escBuf.length >= 3) {
|
|
387
|
+
const code = escBuf[2];
|
|
388
|
+
escBuf = "";
|
|
389
|
+
if (code === "A") {
|
|
390
|
+
if (popup) {
|
|
391
|
+
selIdx = Math.max(0, selIdx - 1);
|
|
392
|
+
}
|
|
393
|
+
else if (_history.length) {
|
|
394
|
+
histIdx = Math.max(0, histIdx - 1);
|
|
395
|
+
buf = _history[histIdx] || "";
|
|
396
|
+
cursor = buf.length;
|
|
397
|
+
}
|
|
398
|
+
render();
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
if (code === "B") {
|
|
402
|
+
if (popup) {
|
|
403
|
+
const f = SLASH_CMDS.filter(c => c[0].toLowerCase().includes(buf.toLowerCase()));
|
|
404
|
+
selIdx = Math.min(f.length - 1, selIdx + 1);
|
|
405
|
+
}
|
|
406
|
+
else if (_history.length && histIdx < _history.length) {
|
|
407
|
+
histIdx++;
|
|
408
|
+
buf = _history[histIdx] || "";
|
|
409
|
+
cursor = buf.length;
|
|
410
|
+
}
|
|
411
|
+
render();
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
if (code === "C") {
|
|
415
|
+
if (cursor < buf.length)
|
|
416
|
+
cursor++;
|
|
417
|
+
if (popup)
|
|
418
|
+
popup = false;
|
|
419
|
+
render();
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
if (code === "D") {
|
|
423
|
+
if (cursor > 0)
|
|
424
|
+
cursor--;
|
|
425
|
+
if (popup)
|
|
426
|
+
popup = false;
|
|
427
|
+
render();
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
return; /* wait for more bytes */
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
// Not an escape sequence — process normally
|
|
436
|
+
for (const ch of escBuf) {
|
|
437
|
+
escBuf = "";
|
|
438
|
+
if (ch === "\x1b") {
|
|
439
|
+
if (popup) {
|
|
440
|
+
popup = false;
|
|
441
|
+
render();
|
|
442
|
+
}
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
if (ch === "\r" || ch === "\n") {
|
|
446
|
+
if (popup && SLASH_CMDS.filter(c => c[0].toLowerCase().includes(buf.toLowerCase())).length) {
|
|
447
|
+
buf = SLASH_CMDS.filter(c => c[0].toLowerCase().includes(buf.toLowerCase()))[selIdx]?.[0] || buf;
|
|
448
|
+
popup = false;
|
|
449
|
+
}
|
|
450
|
+
accept(buf.trim());
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
if (ch === "\t") {
|
|
454
|
+
if (popup && SLASH_CMDS.filter(c => c[0].toLowerCase().includes(buf.toLowerCase())).length) {
|
|
455
|
+
const filtered = SLASH_CMDS.filter(c => c[0].toLowerCase().includes(buf.toLowerCase()));
|
|
456
|
+
buf = filtered[selIdx]?.[0] || buf;
|
|
457
|
+
cursor = buf.length;
|
|
458
|
+
popup = false;
|
|
459
|
+
render();
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
buf = buf.slice(0, cursor) + " " + buf.slice(cursor);
|
|
463
|
+
cursor += 2;
|
|
464
|
+
render();
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
if (ch === "\x7f" || ch === "\b") {
|
|
468
|
+
if (cursor > 0) {
|
|
469
|
+
buf = buf.slice(0, cursor - 1) + buf.slice(cursor);
|
|
470
|
+
cursor--;
|
|
471
|
+
}
|
|
472
|
+
if (!buf)
|
|
473
|
+
popup = false;
|
|
474
|
+
render();
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
if (ch === "\x03") {
|
|
478
|
+
accept("/quit");
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
if (ch >= " ") {
|
|
482
|
+
buf = buf.slice(0, cursor) + ch + buf.slice(cursor);
|
|
483
|
+
cursor++;
|
|
484
|
+
if (buf === "/") {
|
|
485
|
+
popup = true;
|
|
486
|
+
selIdx = 0;
|
|
487
|
+
}
|
|
488
|
+
else if (popup)
|
|
489
|
+
selIdx = 0;
|
|
490
|
+
render();
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
render();
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
/* ═══════════════════════════════════════
|
|
499
|
+
Main chat loop
|
|
500
|
+
═══════════════════════════════════════ */
|
|
501
|
+
async function chat(agentName, modelOverride) {
|
|
158
502
|
const ctx = (0, factory_1.createSystemContext)();
|
|
159
|
-
|
|
503
|
+
let agent = ctx.agentMap.get(agentName);
|
|
160
504
|
if (!agent) {
|
|
161
|
-
|
|
505
|
+
logLine(chalk_1.default.red(`Unknown agent: ${agentName}`));
|
|
162
506
|
return;
|
|
163
507
|
}
|
|
164
508
|
await agent.init();
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
console.log(chalk_1.default.cyan('≈ S K Y L O O M ≈'));
|
|
168
|
-
console.log(chalk_1.default.dim(`Agent: ${chalk_1.default.bold(agent.displayName)} · Model: ${modelOverride || 'default'}`));
|
|
169
|
-
console.log(chalk_1.default.dim('Type /help for commands, /quit to exit'));
|
|
170
|
-
console.log();
|
|
171
|
-
const rl = readline.createInterface({
|
|
172
|
-
input: process.stdin,
|
|
173
|
-
output: process.stdout,
|
|
174
|
-
prompt: '',
|
|
175
|
-
});
|
|
509
|
+
const model = modelOverride || "default";
|
|
510
|
+
printWelcome(agent, model);
|
|
176
511
|
const inputHistory = [];
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
rl.close();
|
|
184
|
-
return;
|
|
512
|
+
while (true) {
|
|
513
|
+
/* ── Read input ── */
|
|
514
|
+
const inp = await readWithPopup(agent, ctx);
|
|
515
|
+
if (!inp) {
|
|
516
|
+
logLine("");
|
|
517
|
+
continue;
|
|
185
518
|
}
|
|
186
|
-
|
|
519
|
+
// Save to history
|
|
520
|
+
if (!inputHistory[inputHistory.length - 1] || inputHistory[inputHistory.length - 1] !== inp) {
|
|
521
|
+
inputHistory.push(inp);
|
|
522
|
+
if (inputHistory.length > 50)
|
|
523
|
+
inputHistory.shift();
|
|
524
|
+
}
|
|
525
|
+
readWithPopup._popupHistory = inputHistory;
|
|
526
|
+
const cmd = inp.trim();
|
|
527
|
+
const cmdLower = cmd.toLowerCase();
|
|
528
|
+
/* ── Slash commands ── */
|
|
529
|
+
let handled = false;
|
|
530
|
+
// Agent switching
|
|
531
|
+
for (const n of AGENT_NAMES) {
|
|
532
|
+
if (cmdLower === `/${n}`) {
|
|
533
|
+
const newAgent = ctx.agentMap.get(n);
|
|
534
|
+
if (newAgent) {
|
|
535
|
+
await newAgent.init();
|
|
536
|
+
// Switch agent reference (mutate closure)
|
|
537
|
+
logLine(chalk_1.default.dim(`\n ⟳ ${AGENT_DISPLAY[n]}\n`));
|
|
538
|
+
// We can't reassign the outer `agent` const, so use a workaround
|
|
539
|
+
chat._currentAgent = newAgent;
|
|
540
|
+
chat._currentCtx = ctx;
|
|
541
|
+
// Actually, let me handle this differently — replace the agent in the closure
|
|
542
|
+
agent = newAgent;
|
|
543
|
+
}
|
|
544
|
+
handled = true;
|
|
545
|
+
break;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
if (cmdLower === "/quit" || cmdLower === "/exit")
|
|
549
|
+
break;
|
|
550
|
+
if (cmdLower === "/help") {
|
|
187
551
|
printHelp();
|
|
188
|
-
|
|
552
|
+
handled = true;
|
|
189
553
|
}
|
|
190
|
-
if (
|
|
554
|
+
if (cmdLower === "/clear") {
|
|
191
555
|
console.clear();
|
|
192
|
-
|
|
556
|
+
handled = true;
|
|
193
557
|
}
|
|
194
|
-
if (
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
558
|
+
if (cmdLower === "/version") {
|
|
559
|
+
logLine(` Skyloom v${VERSION}`);
|
|
560
|
+
handled = true;
|
|
561
|
+
}
|
|
562
|
+
if (cmdLower === "/status") {
|
|
563
|
+
logLine(chalk_1.default.bold(`\n ${agent.displayName} (${agent.name})`));
|
|
564
|
+
logLine(chalk_1.default.dim(` State: ${agent.state} · Specialty: ${agent.specialty}`));
|
|
565
|
+
logLine(chalk_1.default.dim(` Memory: ${agent.memory.shortTerm.length} messages · ${Object.keys(agent.memory.working).length} working keys`));
|
|
566
|
+
handled = true;
|
|
567
|
+
}
|
|
568
|
+
if (cmdLower === "/cost") {
|
|
569
|
+
logLine(chalk_1.default.bold("\n Usage & Cost"));
|
|
570
|
+
logLine(chalk_1.default.dim(" ─".repeat(24)));
|
|
571
|
+
logLine(` Total: ${formatCost(ctx.llm.getTotalCost())}`);
|
|
572
|
+
handled = true;
|
|
573
|
+
}
|
|
574
|
+
if (cmdLower === "/cost reset") {
|
|
575
|
+
ctx.llm.resetUsageStats?.();
|
|
576
|
+
logLine(chalk_1.default.dim(" Stats reset"));
|
|
577
|
+
handled = true;
|
|
578
|
+
}
|
|
579
|
+
if (cmdLower === "/compact") {
|
|
580
|
+
logLine(chalk_1.default.dim(" Compacting..."));
|
|
581
|
+
const r = await agent.compact();
|
|
582
|
+
logLine(chalk_1.default.green(` ✓ ${r}`));
|
|
583
|
+
handled = true;
|
|
584
|
+
}
|
|
585
|
+
if (cmdLower === "/memory") {
|
|
586
|
+
logLine(chalk_1.default.bold("\n Memory"));
|
|
587
|
+
logLine(chalk_1.default.dim(` Short-term: ${agent.memory.shortTerm.length} msgs · Working: ${Object.keys(agent.memory.working).length} keys`));
|
|
588
|
+
handled = true;
|
|
207
589
|
}
|
|
208
|
-
if (
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
return;
|
|
590
|
+
if (cmdLower === "/workspace") {
|
|
591
|
+
logLine(chalk_1.default.dim(`\n Workspace: ${ctx.workspacePath || "default"}`));
|
|
592
|
+
handled = true;
|
|
212
593
|
}
|
|
213
|
-
if (
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
return;
|
|
594
|
+
if (cmdLower === "/mcp") {
|
|
595
|
+
logLine(chalk_1.default.dim(`\n MCP servers: ${ctx.mcpStatus?.length ? ctx.mcpStatus.join(", ") : "none configured"}`));
|
|
596
|
+
handled = true;
|
|
217
597
|
}
|
|
218
|
-
if (
|
|
219
|
-
|
|
220
|
-
|
|
598
|
+
if (cmdLower === "/sessions") {
|
|
599
|
+
const sessions = await agent.memory.listSessions();
|
|
600
|
+
if (sessions.length) {
|
|
601
|
+
logLine(chalk_1.default.bold("\n Sessions"));
|
|
602
|
+
for (const s of sessions.slice(0, 10)) {
|
|
603
|
+
logLine(chalk_1.default.dim(` ${s.id?.slice(0, 10)}... ${s.preview || ""} (${s.messageCount || 0} msgs)`));
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
else {
|
|
607
|
+
logLine(chalk_1.default.dim(" No saved sessions"));
|
|
608
|
+
}
|
|
609
|
+
handled = true;
|
|
221
610
|
}
|
|
222
|
-
if (
|
|
223
|
-
|
|
224
|
-
|
|
611
|
+
if (cmdLower.startsWith("/model")) {
|
|
612
|
+
logLine(chalk_1.default.dim(` Model: ${modelOverride || "default"}. Configure in ~/.skyloom/config.yaml`));
|
|
613
|
+
handled = true;
|
|
225
614
|
}
|
|
226
|
-
if (
|
|
615
|
+
if (cmdLower.startsWith("/task ")) {
|
|
227
616
|
const goal = cmd.slice(6).trim();
|
|
228
617
|
if (goal) {
|
|
229
|
-
|
|
618
|
+
logLine(chalk_1.default.cyan(`\n ✦ Orchestrating: ${goal}\n`));
|
|
230
619
|
await runTask(goal);
|
|
231
620
|
}
|
|
232
|
-
|
|
621
|
+
handled = true;
|
|
233
622
|
}
|
|
234
|
-
if (
|
|
235
|
-
|
|
236
|
-
|
|
623
|
+
if (handled) {
|
|
624
|
+
logLine("");
|
|
625
|
+
continue;
|
|
237
626
|
}
|
|
238
|
-
|
|
239
|
-
if (!inputHistory.includes(cmd)) {
|
|
240
|
-
inputHistory.push(cmd);
|
|
241
|
-
if (inputHistory.length > 50)
|
|
242
|
-
inputHistory.shift();
|
|
243
|
-
}
|
|
244
|
-
// Classify and route
|
|
627
|
+
/* ── Route message ── */
|
|
245
628
|
const mode = MODE.current;
|
|
246
629
|
if (mode === mode_1.InteractiveMode.PLAN) {
|
|
247
|
-
console.log(chalk_1.default.magenta('\n [PLAN mode] Routing to orchestrator...\n'));
|
|
248
630
|
await runTask(cmd);
|
|
249
|
-
|
|
631
|
+
logLine("");
|
|
632
|
+
continue;
|
|
250
633
|
}
|
|
251
634
|
const cls = (0, router_1.classify)(cmd);
|
|
252
|
-
if (cls ===
|
|
635
|
+
if (cls === "orchestrate" && mode !== mode_1.InteractiveMode.AUTO) {
|
|
253
636
|
await runTask(cmd);
|
|
254
|
-
|
|
637
|
+
logLine("");
|
|
638
|
+
continue;
|
|
255
639
|
}
|
|
256
|
-
|
|
257
|
-
process.stdout.write(`\n ${chalk_1.default.cyan(agent.displayName)} ${chalk_1.default.dim('thinking...')}\n`);
|
|
640
|
+
/* ── Chat ── */
|
|
258
641
|
try {
|
|
259
|
-
const response = await agent
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
catch (e) {
|
|
279
|
-
console.error(chalk_1.default.red(`\n Error: ${e}`));
|
|
642
|
+
const response = await chatWithSpinner(agent, ctx, cmd);
|
|
643
|
+
logLine("");
|
|
644
|
+
for (const line of renderResponse(response)) {
|
|
645
|
+
logLine(line);
|
|
646
|
+
}
|
|
647
|
+
logLine("");
|
|
648
|
+
// Auto-continue
|
|
649
|
+
if (mode === mode_1.InteractiveMode.AUTO) {
|
|
650
|
+
const tail = response.split("\n").slice(-6).join("\n");
|
|
651
|
+
if (/(?:接下来|下一步|继续|next|let me|I'[vl]l)/i.test(tail) && !/(?:完成了|全部完成|all done)/i.test(tail)) {
|
|
652
|
+
logLine(chalk_1.default.yellow(" [auto-continue]\n"));
|
|
653
|
+
try {
|
|
654
|
+
const r2 = await chatWithSpinner(agent, ctx, "请继续完成");
|
|
655
|
+
logLine("");
|
|
656
|
+
for (const line of renderResponse(r2))
|
|
657
|
+
logLine(line);
|
|
658
|
+
logLine("");
|
|
659
|
+
}
|
|
660
|
+
catch { /* ignore */ }
|
|
280
661
|
}
|
|
281
662
|
}
|
|
282
663
|
}
|
|
283
|
-
};
|
|
284
|
-
rl.on('line', async (line) => {
|
|
285
|
-
try {
|
|
286
|
-
await processInput(line);
|
|
287
|
-
}
|
|
288
664
|
catch (e) {
|
|
289
|
-
|
|
665
|
+
logLine(chalk_1.default.red(`\n ✗ Error: ${e.message || e}\n`));
|
|
290
666
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
ctx.closeAll().catch(() => { });
|
|
296
|
-
process.exit(0);
|
|
297
|
-
});
|
|
298
|
-
rl.prompt();
|
|
667
|
+
}
|
|
668
|
+
logLine(chalk_1.default.dim("\n Session ended"));
|
|
669
|
+
await ctx.closeAll();
|
|
670
|
+
process.exit(0);
|
|
299
671
|
}
|
|
672
|
+
/* ═══════════════════════════════════════
|
|
673
|
+
Task execution
|
|
674
|
+
═══════════════════════════════════════ */
|
|
300
675
|
async function runTask(goal, resume) {
|
|
301
676
|
const ctx = (0, factory_1.createSystemContext)();
|
|
302
677
|
await ctx.initAll();
|
|
303
|
-
const [
|
|
304
|
-
resultTruncate: 500,
|
|
305
|
-
maxTaskRetries: 3,
|
|
306
|
-
maxReplanRounds: 1,
|
|
307
|
-
resume,
|
|
678
|
+
const [, results, summary] = await (0, factory_1.orchestrateTask)(goal, ctx.agentMap, null, {
|
|
679
|
+
resultTruncate: 500, maxTaskRetries: 3, maxReplanRounds: 1, resume,
|
|
308
680
|
});
|
|
309
|
-
|
|
310
|
-
|
|
681
|
+
logLine(chalk_1.default.bold("\n Task Results"));
|
|
682
|
+
logLine(chalk_1.default.dim(" ─".repeat(30)));
|
|
311
683
|
for (const r of results) {
|
|
312
|
-
|
|
313
|
-
console.log(` ${status} ${chalk_1.default.cyan(r.agent)}: ${r.description.slice(0, 60)}...`);
|
|
684
|
+
logLine(` ${r.success ? chalk_1.default.green("✓") : chalk_1.default.red("✗")} ${chalk_1.default.cyan(r.agent)}: ${r.description.slice(0, 60)}`);
|
|
314
685
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
686
|
+
logLine(chalk_1.default.bold("\n Summary"));
|
|
687
|
+
logLine(chalk_1.default.dim(" ─".repeat(30)));
|
|
688
|
+
logLine(` ${summary.slice(0, 1000)}`);
|
|
689
|
+
logLine("");
|
|
319
690
|
await ctx.closeAll();
|
|
320
691
|
}
|
|
321
692
|
function printHelp() {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
const
|
|
325
|
-
[
|
|
326
|
-
[
|
|
327
|
-
[
|
|
328
|
-
[
|
|
329
|
-
['/compact', 'Compress context'],
|
|
330
|
-
['/version', 'Version info'],
|
|
331
|
-
['/task <goal>', 'Multi-agent task'],
|
|
332
|
-
['/quit', 'Exit chat'],
|
|
333
|
-
['', ''],
|
|
334
|
-
['Switch agents:', ''],
|
|
335
|
-
['/fog', 'Fog — research'],
|
|
336
|
-
['/rain', 'Rain — codegen'],
|
|
337
|
-
['/frost', 'Frost — review'],
|
|
338
|
-
['/snow', 'Snow — planning'],
|
|
339
|
-
['/dew', 'Dew — devops'],
|
|
340
|
-
['/fair', 'Fair — companion'],
|
|
693
|
+
logLine(chalk_1.default.bold("\n Slash Commands"));
|
|
694
|
+
logLine(chalk_1.default.dim(" ─".repeat(40)));
|
|
695
|
+
const groups = [
|
|
696
|
+
["Agent", [["/fog /rain /frost", "Switch agents"], ["/snow /dew /fair", "Switch agents"]]],
|
|
697
|
+
["Chat", [["/help", "Show commands"], ["/clear", "Clear screen"], ["/compact", "Compress context"], ["/retry", "Resend last msg"], ["/quit", "Exit"]]],
|
|
698
|
+
["Info", [["/status", "Agent status"], ["/cost", "Usage & cost"], ["/memory", "Memory stats"], ["/sessions", "Session list"], ["/workspace", "Workspace info"], ["/version", "Version"]]],
|
|
699
|
+
["Orch.", [["/task <goal>", "Multi-agent task"]]],
|
|
341
700
|
];
|
|
342
|
-
for (const [
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
else {
|
|
347
|
-
console.log();
|
|
348
|
-
}
|
|
701
|
+
for (const [title, cmds] of groups) {
|
|
702
|
+
logLine(chalk_1.default.cyan(` ${title}`));
|
|
703
|
+
for (const [c, d] of cmds)
|
|
704
|
+
logLine(` ${chalk_1.default.cyan(c.padEnd(18))}${chalk_1.default.dim(d)}`);
|
|
349
705
|
}
|
|
350
|
-
|
|
351
|
-
}
|
|
352
|
-
function getAgentColor(name) {
|
|
353
|
-
const colors = {
|
|
354
|
-
fog: 'bright_white', rain: 'blue', frost: 'cyan',
|
|
355
|
-
snow: 'bright_white', dew: 'green', fair: '#FFD700',
|
|
356
|
-
};
|
|
357
|
-
return colors[name] || 'white';
|
|
358
|
-
}
|
|
359
|
-
function formatCost(cost) {
|
|
360
|
-
if (cost >= 1.0)
|
|
361
|
-
return `$${cost.toFixed(2)}`;
|
|
362
|
-
if (cost >= 0.01)
|
|
363
|
-
return `$${cost.toFixed(4)}`;
|
|
364
|
-
if (cost > 0.0)
|
|
365
|
-
return `${(cost * 100).toFixed(2)}¢`;
|
|
366
|
-
return '$0';
|
|
367
|
-
}
|
|
368
|
-
function shouldAutoContinue(text) {
|
|
369
|
-
const autoContinuePattern = /(?:接下来|下一步|下面我|然后我|接着|继续|next|let me\s|I'[vl]l\s)/i;
|
|
370
|
-
const autoStopPattern = /(?:完成了|全部完成|以上就|all done|task complete)/i;
|
|
371
|
-
const tail = text.split('\n').slice(-6).join('\n');
|
|
372
|
-
if (autoStopPattern.test(tail))
|
|
373
|
-
return false;
|
|
374
|
-
return autoContinuePattern.test(tail);
|
|
706
|
+
logLine("");
|
|
375
707
|
}
|
|
376
|
-
|
|
708
|
+
/* ═══════════════════════════════════════
|
|
709
|
+
Entry
|
|
710
|
+
═══════════════════════════════════════ */
|
|
377
711
|
async function main() {
|
|
378
712
|
const args = process.argv.slice(2);
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
let
|
|
385
|
-
for (let i =
|
|
386
|
-
if (
|
|
387
|
-
|
|
388
|
-
}
|
|
389
|
-
else if (args[i] === '-m' || args[i] === '--model') {
|
|
390
|
-
model = args[++i];
|
|
391
|
-
}
|
|
713
|
+
if (args.length === 0) {
|
|
714
|
+
await chat("fog");
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
if (AGENT_NAMES.includes(args[0])) {
|
|
718
|
+
let m;
|
|
719
|
+
for (let i = 1; i < args.length; i++) {
|
|
720
|
+
if ((args[i] === "-m" || args[i] === "--model") && i + 1 < args.length)
|
|
721
|
+
m = args[++i];
|
|
392
722
|
}
|
|
393
|
-
await
|
|
723
|
+
await chat(args[0], m);
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
const subCmds = ["chat", "task", "web", "config", "init", "version", "mcp", "help"];
|
|
727
|
+
if (!subCmds.includes(args[0]) && !args[0].startsWith("-")) {
|
|
728
|
+
await chat("fog");
|
|
394
729
|
return;
|
|
395
730
|
}
|
|
396
731
|
program.parse(process.argv);
|
|
397
732
|
}
|
|
398
|
-
main().catch((e)
|
|
399
|
-
console.error(chalk_1.default.red(`Fatal error: ${e}`));
|
|
400
|
-
process.exit(1);
|
|
401
|
-
});
|
|
733
|
+
main().catch(e => { logLine(chalk_1.default.red(`Fatal: ${e.message}`)); process.exit(1); });
|
|
402
734
|
//# sourceMappingURL=main.js.map
|