skyloom 1.5.1 → 1.5.3

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.js CHANGED
@@ -39,7 +39,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
39
39
  Object.defineProperty(exports, "__esModule", { value: true });
40
40
  /**
41
41
  * 天空织机 CLI — Skyloom Terminal Interface
42
- * Raw-mode input + slash command popup + streaming display
43
42
  */
44
43
  const commander_1 = require("commander");
45
44
  const fs = __importStar(require("fs"));
@@ -47,663 +46,297 @@ const readline = __importStar(require("readline"));
47
46
  const chalk_1 = __importDefault(require("chalk"));
48
47
  const factory_1 = require("../core/factory");
49
48
  const config_1 = require("../core/config");
50
- const router_1 = require("../core/router");
51
49
  const mode_1 = require("./mode");
52
50
  const MODE = new mode_1.ModeController();
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
- };
51
+ const VERSION = (() => { try {
52
+ return require("../../package.json").version;
53
+ }
54
+ catch {
55
+ return "1.5.2";
56
+ } })();
59
57
  const AGENT_DISPLAY = {
60
58
  fog: "≋ 雾 Fog", rain: "⸽ 雨 Rain", frost: "✱ 霜 Frost",
61
59
  snow: "❉ 雪 Snow", dew: "∘ 露 Dew", fair: "☼ 晴 Fair",
62
60
  };
63
61
  const AGENT_NAMES = ["fog", "rain", "frost", "snow", "dew", "fair"];
64
62
  /* ═══════════════════════════════════════
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
63
+ Slash commands registry
93
64
  ═══════════════════════════════════════ */
94
65
  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, ""],
66
+ ["/help", "Show all commands"],
67
+ ["/clear", "Clear screen"],
68
+ ["/status", "Agent overview"],
69
+ ["/cost", "Usage & cost"],
70
+ ["/cost reset", "Reset usage stats"],
71
+ ["/compact", "Compress context"],
72
+ ["/retry", "Resend last msg"],
73
+ ["/memory", "Memory stats"],
74
+ ["/memory clear", "Clear short-term memory"],
75
+ ["/sessions", "Session list"],
76
+ ["/workspace", "Workspace info"],
77
+ ["/model", "Model info"],
78
+ ["/mcp", "MCP server status"],
79
+ ["/version", "Version info"],
80
+ ["/task <goal>", "Multi-agent orchestrate"],
81
+ ["/fog", "≋ Fog — research insight"],
82
+ ["/rain", "⸽ Rain — creation codegen"],
83
+ ["/frost", "✱ Frost — review quality"],
84
+ ["/snow", "❉ Snow — planning architect"],
85
+ ["/dew", "∘ Dew — devops reliability"],
86
+ ["/fair", "☼ Fair — companion warmth"],
87
+ ["/quit", "Exit chat"],
88
+ ["/exit", "Exit chat"],
118
89
  ];
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);
129
- try {
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;
135
- }
136
- catch (e) {
137
- clearInterval(spinner);
138
- throw e;
139
- }
140
- }
141
- /* ── Render response ── */
142
- function renderResponse(text) {
143
- const lines = [];
90
+ function showPopup(cmds, selIdx) {
144
91
  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
- }
92
+ const start = Math.max(0, Math.min(selIdx - 4, cmds.length - 8));
93
+ const end = Math.min(cmds.length, start + 8);
94
+ process.stdout.write(chalk_1.default.dim(" ┌─ commands (↑↓ pick · type letter to filter · tab/enter select) ─┐\n"));
95
+ for (let i = start; i < end; i++) {
96
+ const [cmd, desc] = cmds[i];
97
+ const marker = i === selIdx ? chalk_1.default.cyan(" ▶ ") : " ";
98
+ process.stdout.write(` │${marker}${chalk_1.default.cyan(cmd.padEnd(24))}${chalk_1.default.dim(desc)}${" ".repeat(Math.max(0, 50 - desc.length))}│\n`);
181
99
  }
182
- return lines;
100
+ process.stdout.write(chalk_1.default.dim(` └${"─".repeat(60)}┘\n`));
183
101
  }
184
- /* ── Welcome banner ── */
185
- function printWelcome(agent, model) {
102
+ /* ═══════════════════════════════════════
103
+ Commander
104
+ ═══════════════════════════════════════ */
105
+ const program = new commander_1.Command()
106
+ .name("sky").description("天空织机 Skyloom").version(VERSION);
107
+ program.command("chat").argument("[agent]", "agent name", "fog")
108
+ .option("-m,--model <m>", "model").action(async (a, o) => { await chat(a, o.model); });
109
+ program.command("task").argument("[goal]", "task goal")
110
+ .action(async (g) => { if (g)
111
+ await runTask(g); });
112
+ program.command("web").option("-p,--port <p>", "port", "3000")
113
+ .action((o) => { Promise.resolve().then(() => __importStar(require("../web/server"))).then(m => m.startWebServer(parseInt(o.port || "3000"))); });
114
+ program.command("mcp").action(() => { Promise.resolve().then(() => __importStar(require("../core/mcp_server"))).then(m => m.startMCPServer()); });
115
+ program.command("config").action(() => { const c = (0, config_1.loadConfig)(); process.stdout.write(chalk_1.default.cyan("\nConfig: ") + config_1.USER_CONFIG_DIR + "\n"); for (const [n, a] of Object.entries(c.agents || {}))
116
+ process.stdout.write(` ${chalk_1.default.bold(n)}: ${a.model || "default"}\n`); });
117
+ program.command("init").action(() => { if (!fs.existsSync(config_1.USER_CONFIG_DIR))
118
+ fs.mkdirSync(config_1.USER_CONFIG_DIR, { recursive: true }); process.stdout.write(chalk_1.default.green("✓ ") + config_1.USER_CONFIG_DIR + "\n"); });
119
+ program.command("version").action(() => { process.stdout.write(`Skyloom v${VERSION}\n`); });
120
+ /* ═══════════════════════════════════════
121
+ Welcome
122
+ ═══════════════════════════════════════ */
123
+ function welcome(agent) {
186
124
  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);
125
+ const pad = " ".repeat(Math.max(0, Math.floor((w - 34) / 2)));
126
+ process.stdout.write("\n" + pad + chalk_1.default.cyan("✦ 天 空 织 机 ✦\n"));
127
+ process.stdout.write(pad + chalk_1.default.dim("S K Y L O O M\n\n"));
128
+ const parts = [];
129
+ for (const n of AGENT_NAMES) {
130
+ const a = n === agent.name;
131
+ const s = `${AGENT_DISPLAY[n].split(" ")[0]} ${AGENT_DISPLAY[n].split(" ")[1]}`;
132
+ parts.push(a ? chalk_1.default.bold.cyan(s) : chalk_1.default.dim(s));
196
133
  }
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("");
134
+ process.stdout.write(" " + parts.join(chalk_1.default.dim(" · ")) + "\n\n");
135
+ process.stdout.write(chalk_1.default.dim(" /help for commands · /quit to exit\n\n"));
201
136
  }
202
- /* ── Status bar ── */
203
137
  function statusBar(agent, ctx) {
204
- let ctxStr = "";
205
- let costStr = "$0";
206
- let modelStr = "default";
207
138
  try {
208
139
  const cu = agent.contextUsage();
209
- modelStr = cu.model || "?";
210
140
  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());
141
+ const bar = pct < 50 ? chalk_1.default.green : pct < 80 ? chalk_1.default.yellow : chalk_1.default.red;
142
+ const filled = Math.round(pct / 10);
143
+ const ctxBar = `${bar("█".repeat(filled) + "░".repeat(10 - filled))} ${pct}%`;
144
+ const cost = formatCost(ctx.llm.getTotalCost());
145
+ return chalk_1.default.dim(`${ctxBar} · ${cost} · ${cu.model || "?"}`);
146
+ }
147
+ catch {
148
+ return "";
215
149
  }
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
150
  }
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)}¢`);
151
+ function formatCost(c) {
152
+ if (c >= 1)
153
+ return chalk_1.default.yellow(`$${c.toFixed(2)}`);
154
+ if (c >= 0.01)
155
+ return chalk_1.default.yellow(`$${c.toFixed(4)}`);
156
+ if (c > 0)
157
+ return chalk_1.default.green(`${(c * 100).toFixed(2)}¢`);
227
158
  return "$0";
228
159
  }
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
- });
236
- }
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"));
160
+ /* ═══════════════════════════════════════
161
+ Response render
162
+ ═══════════════════════════════════════ */
163
+ function render(text) {
164
+ const out = [];
165
+ for (const para of text.split("\n\n")) {
166
+ const t = para.trim();
167
+ if (!t)
168
+ continue;
169
+ if (t.startsWith("```")) {
170
+ const lines = t.split("\n");
171
+ out.push(chalk_1.default.dim(" ╭─ code ──"));
172
+ for (let i = 1; i < lines.length - 1; i++)
173
+ out.push(` ${chalk_1.default.dim("│")} ${chalk_1.default.gray(lines[i].slice(0, 72))}`);
174
+ out.push(chalk_1.default.dim(" ╰────────"));
291
175
  }
292
- function accept(line) {
293
- try {
294
- stdin.setRawMode?.(false);
176
+ else {
177
+ for (const line of t.split("\n")) {
178
+ if (line.startsWith("# "))
179
+ out.push(" " + chalk_1.default.bold(line));
180
+ else if (line.startsWith("- ") || line.startsWith("* "))
181
+ out.push(" " + chalk_1.default.dim("• ") + line.slice(2));
182
+ else
183
+ out.push(" " + line);
295
184
  }
296
- catch { }
297
- stdin.pause();
298
- resolve(line);
299
185
  }
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
- });
186
+ }
187
+ return out;
497
188
  }
498
189
  /* ═══════════════════════════════════════
499
- Main chat loop
190
+ Chat loop
500
191
  ═══════════════════════════════════════ */
192
+ /* Check for API key availability */
193
+ function checkApiKeys() {
194
+ const keys = ["DEEPSEEK_API_KEY", "OPENAI_API_KEY", "ANTHROPIC_API_KEY", "GROQ_API_KEY", "OPENROUTER_API_KEY"];
195
+ for (const k of keys) {
196
+ if (process.env[k])
197
+ return k;
198
+ }
199
+ return null;
200
+ }
501
201
  async function chat(agentName, modelOverride) {
202
+ const haveKey = checkApiKeys();
203
+ if (!haveKey) {
204
+ process.stdout.write("\n" + chalk_1.default.yellow(" ⚠ No API key configured.\n"));
205
+ process.stdout.write(chalk_1.default.dim(" Set one: $env:DEEPSEEK_API_KEY = \"sk-your-key\" (PowerShell)\n"));
206
+ process.stdout.write(chalk_1.default.dim(" export DEEPSEEK_API_KEY=sk-your-key (Bash)\n\n"));
207
+ process.stdout.write(chalk_1.default.dim(" Then run: sky\n\n"));
208
+ process.exit(1);
209
+ }
502
210
  const ctx = (0, factory_1.createSystemContext)();
503
211
  let agent = ctx.agentMap.get(agentName);
504
212
  if (!agent) {
505
- logLine(chalk_1.default.red(`Unknown agent: ${agentName}`));
213
+ process.stdout.write(chalk_1.default.red("Unknown agent: " + agentName) + "\n");
506
214
  return;
507
215
  }
508
216
  await agent.init();
509
- const model = modelOverride || "default";
510
- printWelcome(agent, model);
511
- const inputHistory = [];
512
- while (true) {
513
- /* ── Read input ── */
514
- const inp = await readWithPopup(agent, ctx);
217
+ // eslint-disable-next-line prefer-const
218
+ let currentAgent = agent; // mutable for agent switching
219
+ welcome(agent);
220
+ process.stdout.write(chalk_1.default.dim(" Key: " + haveKey + "\n\n"));
221
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
222
+ function ask() { rl.question(chalk_1.default.cyan(" " + currentAgent.displayName + " ❯ "), handler); }
223
+ async function handler(inp) {
224
+ inp = inp.trim();
515
225
  if (!inp) {
516
- logLine("");
517
- continue;
518
- }
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();
226
+ ask();
227
+ return;
524
228
  }
525
- readWithPopup._popupHistory = inputHistory;
526
- const cmd = inp.trim();
527
- const cmdLower = cmd.toLowerCase();
528
- /* ── Slash commands ── */
529
- let handled = false;
530
- // Agent switching
229
+ const cmdL = inp.toLowerCase();
230
+ // Agent switch
531
231
  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;
232
+ if (cmdL === "/" + n) {
233
+ const a = ctx.agentMap.get(n);
234
+ if (a) {
235
+ await a.init();
236
+ currentAgent = a;
543
237
  }
544
- handled = true;
545
- break;
238
+ process.stdout.write(chalk_1.default.dim(" ⟳ " + AGENT_DISPLAY[n] + "\n"));
239
+ ask();
240
+ return;
546
241
  }
547
242
  }
548
- if (cmdLower === "/quit" || cmdLower === "/exit")
549
- break;
550
- if (cmdLower === "/help") {
551
- printHelp();
552
- handled = true;
553
- }
554
- if (cmdLower === "/clear") {
555
- console.clear();
556
- handled = true;
557
- }
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;
243
+ if (cmdL === "/quit" || cmdL === "/exit") {
244
+ process.stdout.write(chalk_1.default.dim("\n Session ended\n"));
245
+ rl.close();
246
+ await ctx.closeAll();
247
+ process.exit(0);
248
+ return;
584
249
  }
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;
250
+ if (cmdL === "/help") {
251
+ process.stdout.write(helpText());
252
+ ask();
253
+ return;
589
254
  }
590
- if (cmdLower === "/workspace") {
591
- logLine(chalk_1.default.dim(`\n Workspace: ${ctx.workspacePath || "default"}`));
592
- handled = true;
593
- }
594
- if (cmdLower === "/mcp") {
595
- logLine(chalk_1.default.dim(`\n MCP servers: ${ctx.mcpStatus?.length ? ctx.mcpStatus.join(", ") : "none configured"}`));
596
- handled = true;
255
+ if (cmdL === "/clear") {
256
+ console.clear();
257
+ welcome(agent);
258
+ process.stdout.write(chalk_1.default.dim(" Key: " + haveKey + "\n\n"));
259
+ ask();
260
+ return;
597
261
  }
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;
262
+ if (cmdL === "/status") {
263
+ process.stdout.write(chalk_1.default.bold("\n " + currentAgent.displayName + " (" + currentAgent.name + ")\n") + chalk_1.default.dim(" State: " + currentAgent.state + " · Memory: " + currentAgent.memory.shortTerm.length + " msgs\n\n"));
264
+ ask();
265
+ return;
610
266
  }
611
- if (cmdLower.startsWith("/model")) {
612
- logLine(chalk_1.default.dim(` Model: ${modelOverride || "default"}. Configure in ~/.skyloom/config.yaml`));
613
- handled = true;
267
+ if (cmdL === "/cost") {
268
+ process.stdout.write(chalk_1.default.bold("\n Total: " + formatCost(ctx.llm.getTotalCost()) + "\n\n"));
269
+ ask();
270
+ return;
614
271
  }
615
- if (cmdLower.startsWith("/task ")) {
616
- const goal = cmd.slice(6).trim();
617
- if (goal) {
618
- logLine(chalk_1.default.cyan(`\n ✦ Orchestrating: ${goal}\n`));
619
- await runTask(goal);
620
- }
621
- handled = true;
272
+ if (cmdL === "/compact") {
273
+ const r = await currentAgent.compact();
274
+ process.stdout.write(chalk_1.default.green(" ✓ " + r + "\n\n"));
275
+ ask();
276
+ return;
622
277
  }
623
- if (handled) {
624
- logLine("");
625
- continue;
278
+ if (cmdL === "/version") {
279
+ process.stdout.write(" Skyloom v" + VERSION + "\n");
280
+ ask();
281
+ return;
626
282
  }
627
- /* ── Route message ── */
628
- const mode = MODE.current;
629
- if (mode === mode_1.InteractiveMode.PLAN) {
630
- await runTask(cmd);
631
- logLine("");
632
- continue;
283
+ if (cmdL.startsWith("/task ")) {
284
+ const g = inp.slice(6);
285
+ process.stdout.write(chalk_1.default.cyan("\n ✦ " + g + "\n\n"));
286
+ await runTask(g);
287
+ ask();
288
+ return;
633
289
  }
634
- const cls = (0, router_1.classify)(cmd);
635
- if (cls === "orchestrate" && mode !== mode_1.InteractiveMode.AUTO) {
636
- await runTask(cmd);
637
- logLine("");
638
- continue;
290
+ if (inp.startsWith("/")) {
291
+ process.stdout.write(helpText());
292
+ ask();
293
+ return;
639
294
  }
640
- /* ── Chat ── */
295
+ // ── Chat ──
296
+ process.stdout.write(chalk_1.default.dim(" " + currentAgent.displayName + " thinking...\r"));
641
297
  try {
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 */ }
661
- }
662
- }
298
+ const response = await currentAgent.chat(inp);
299
+ process.stdout.write("\r" + " ".repeat(40) + "\r\n");
300
+ const lines = render(response);
301
+ for (const l of lines)
302
+ process.stdout.write(l + "\n");
303
+ process.stdout.write("\n");
663
304
  }
664
305
  catch (e) {
665
- logLine(chalk_1.default.red(`\n ✗ Error: ${e.message || e}\n`));
306
+ process.stdout.write("\r" + " ".repeat(40) + "\r");
307
+ process.stdout.write(chalk_1.default.red(" ✗ " + (e.message || e) + "\n\n"));
666
308
  }
309
+ ask();
667
310
  }
668
- logLine(chalk_1.default.dim("\n Session ended"));
669
- await ctx.closeAll();
670
- process.exit(0);
311
+ ask();
671
312
  }
672
313
  /* ═══════════════════════════════════════
673
- Task execution
314
+ Task
674
315
  ═══════════════════════════════════════ */
675
- async function runTask(goal, resume) {
316
+ async function runTask(goal) {
676
317
  const ctx = (0, factory_1.createSystemContext)();
677
318
  await ctx.initAll();
678
- const [, results, summary] = await (0, factory_1.orchestrateTask)(goal, ctx.agentMap, null, {
679
- resultTruncate: 500, maxTaskRetries: 3, maxReplanRounds: 1, resume,
680
- });
681
- logLine(chalk_1.default.bold("\n Task Results"));
682
- logLine(chalk_1.default.dim(" ─".repeat(30)));
683
- for (const r of results) {
684
- logLine(` ${r.success ? chalk_1.default.green("✓") : chalk_1.default.red("✗")} ${chalk_1.default.cyan(r.agent)}: ${r.description.slice(0, 60)}`);
685
- }
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
+ const [, results, summary] = await (0, factory_1.orchestrateTask)(goal, ctx.agentMap);
320
+ for (const r of results)
321
+ process.stdout.write(` ${r.success ? chalk_1.default.green("✓") : chalk_1.default.red("✗")} ${chalk_1.default.cyan(r.agent)}: ${r.description.slice(0, 60)}\n`);
322
+ process.stdout.write(chalk_1.default.bold("\n " + summary.slice(0, 800) + "\n\n"));
690
323
  await ctx.closeAll();
691
324
  }
692
- function printHelp() {
693
- logLine(chalk_1.default.bold("\n Slash Commands"));
694
- logLine(chalk_1.default.dim(" ─".repeat(40)));
325
+ function helpText() {
695
326
  const groups = [
696
327
  ["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"]]],
328
+ ["Chat", [["/help", "Commands"], ["/clear", "Clear"], ["/compact", "Compress"], ["/retry", "Resend"]]],
329
+ ["Info", [["/status", "Status"], ["/cost", "Cost"], ["/memory", "Memory"], ["/sessions", "Sessions"], ["/workspace", "Workspace"], ["/version", "Version"]]],
330
+ ["Orch.", [["/task <goal>", "Multi-agent"]]],
700
331
  ];
332
+ let s = "";
701
333
  for (const [title, cmds] of groups) {
702
- logLine(chalk_1.default.cyan(` ${title}`));
334
+ s += chalk_1.default.cyan(` ${title}\n`);
703
335
  for (const [c, d] of cmds)
704
- logLine(` ${chalk_1.default.cyan(c.padEnd(18))}${chalk_1.default.dim(d)}`);
336
+ s += ` ${chalk_1.default.cyan(c.padEnd(18))}${chalk_1.default.dim(d)}\n`;
705
337
  }
706
- logLine("");
338
+ s += "\n";
339
+ return s;
707
340
  }
708
341
  /* ═══════════════════════════════════════
709
342
  Entry
@@ -716,19 +349,17 @@ async function main() {
716
349
  }
717
350
  if (AGENT_NAMES.includes(args[0])) {
718
351
  let m;
719
- for (let i = 1; i < args.length; i++) {
352
+ for (let i = 1; i < args.length; i++)
720
353
  if ((args[i] === "-m" || args[i] === "--model") && i + 1 < args.length)
721
354
  m = args[++i];
722
- }
723
355
  await chat(args[0], m);
724
356
  return;
725
357
  }
726
- const subCmds = ["chat", "task", "web", "config", "init", "version", "mcp", "help"];
727
- if (!subCmds.includes(args[0]) && !args[0].startsWith("-")) {
358
+ if (!["chat", "task", "web", "config", "init", "version", "mcp", "help"].includes(args[0]) && !args[0].startsWith("-")) {
728
359
  await chat("fog");
729
360
  return;
730
361
  }
731
362
  program.parse(process.argv);
732
363
  }
733
- main().catch(e => { logLine(chalk_1.default.red(`Fatal: ${e.message}`)); process.exit(1); });
364
+ main().catch(e => { process.stderr.write(chalk_1.default.red(`Fatal: ${e.message}\n`)); process.exit(1); });
734
365
  //# sourceMappingURL=main.js.map