skyloom 1.5.0 → 1.5.1
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/package.json +1 -1
- package/src/cli/main.ts +46 -65
package/package.json
CHANGED
package/src/cli/main.ts
CHANGED
|
@@ -143,92 +143,73 @@ function render(text: string): string[] {
|
|
|
143
143
|
/* ═══════════════════════════════════════
|
|
144
144
|
Chat loop
|
|
145
145
|
═══════════════════════════════════════ */
|
|
146
|
+
/* Check for API key availability */
|
|
147
|
+
function checkApiKeys(): string | null {
|
|
148
|
+
const keys = ["DEEPSEEK_API_KEY","OPENAI_API_KEY","ANTHROPIC_API_KEY","GROQ_API_KEY","OPENROUTER_API_KEY"];
|
|
149
|
+
for (const k of keys) { if (process.env[k]) return k; }
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
|
|
146
153
|
async function chat(agentName: string, modelOverride?: string): Promise<void> {
|
|
154
|
+
const haveKey = checkApiKeys();
|
|
155
|
+
if (!haveKey) {
|
|
156
|
+
process.stdout.write("\n" + chalk.yellow(" ⚠ No API key configured.\n"));
|
|
157
|
+
process.stdout.write(chalk.dim(" Set one: $env:DEEPSEEK_API_KEY = \"sk-your-key\" (PowerShell)\n"));
|
|
158
|
+
process.stdout.write(chalk.dim(" export DEEPSEEK_API_KEY=sk-your-key (Bash)\n\n"));
|
|
159
|
+
process.stdout.write(chalk.dim(" Then run: sky\n\n"));
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
|
|
147
163
|
const ctx = createSystemContext();
|
|
148
164
|
let agent = ctx.agentMap.get(agentName);
|
|
149
|
-
if (!agent) { process.stdout.write(chalk.red(
|
|
165
|
+
if (!agent) { process.stdout.write(chalk.red("Unknown agent: " + agentName) + "\n"); return; }
|
|
150
166
|
await agent.init();
|
|
167
|
+
// eslint-disable-next-line prefer-const
|
|
168
|
+
let currentAgent = agent; // mutable for agent switching
|
|
151
169
|
welcome(agent);
|
|
152
170
|
|
|
153
|
-
|
|
154
|
-
const history: string[] = [];
|
|
171
|
+
process.stdout.write(chalk.dim(" Key: " + haveKey + "\n\n"));
|
|
155
172
|
|
|
156
|
-
|
|
157
|
-
const inp = line.trim();
|
|
158
|
-
if (!inp) continue;
|
|
173
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
159
174
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
175
|
+
function ask() { rl.question(chalk.cyan(" " + currentAgent.displayName + " ❯ "), handler); }
|
|
176
|
+
async function handler(inp: string) {
|
|
177
|
+
inp = inp.trim();
|
|
178
|
+
if (!inp) { ask(); return; }
|
|
164
179
|
|
|
165
|
-
const
|
|
166
|
-
let handled = false;
|
|
180
|
+
const cmdL = inp.toLowerCase();
|
|
167
181
|
|
|
168
182
|
// Agent switch
|
|
169
183
|
for (const n of AGENT_NAMES) {
|
|
170
|
-
if (
|
|
171
|
-
const a = ctx.agentMap.get(n);
|
|
172
|
-
if (a) { await a.init(); agent = a; process.stdout.write(chalk.dim(` ⟳ ${AGENT_DISPLAY[n]}\n`)); }
|
|
173
|
-
handled = true; break;
|
|
174
|
-
}
|
|
184
|
+
if (cmdL === "/" + n) { const a = ctx.agentMap.get(n); if (a) { await a.init(); currentAgent = a; } process.stdout.write(chalk.dim(" ⟳ " + AGENT_DISPLAY[n] + "\n")); ask(); return; }
|
|
175
185
|
}
|
|
176
186
|
|
|
177
|
-
if (
|
|
178
|
-
if (
|
|
179
|
-
if (
|
|
180
|
-
if (
|
|
181
|
-
if (
|
|
182
|
-
if (
|
|
183
|
-
if (
|
|
184
|
-
if (
|
|
185
|
-
if (
|
|
186
|
-
if (cmdLower === "/memory") { process.stdout.write(chalk.dim(` Short-term: ${agent.memory.shortTerm.length} msgs · Working: ${Object.keys(agent.memory.working).length} keys\n\n`)); handled = true; }
|
|
187
|
-
if (cmdLower === "/workspace") { process.stdout.write(chalk.dim(` ${ctx.workspacePath || "default"}\n\n`)); handled = true; }
|
|
188
|
-
if (cmdLower === "/mcp") { process.stdout.write(chalk.dim(` ${ctx.mcpStatus?.join(", ") || "none"}\n\n`)); handled = true; }
|
|
189
|
-
if (cmdLower === "/sessions") { const ss = await agent.memory.listSessions(); if (ss.length) { for (const s of ss.slice(0, 10)) process.stdout.write(chalk.dim(` ${s.id?.slice(0, 10)}... ${s.preview || ""} (${s.messageCount || 0} msgs)\n`)); } else process.stdout.write(chalk.dim(" No saved sessions\n")); process.stdout.write("\n"); handled = true; }
|
|
190
|
-
if (cmdLower.startsWith("/model")) { process.stdout.write(chalk.dim(" Configure in ~/.skyloom/config.yaml\n\n")); handled = true; }
|
|
191
|
-
|
|
192
|
-
if (handled) continue;
|
|
193
|
-
|
|
194
|
-
// Task orchestration
|
|
195
|
-
if (cmdLower.startsWith("/task ")) { const g = inp.slice(6).trim(); if (g) { process.stdout.write(chalk.cyan(`\n ✦ ${g}\n\n`)); await runTask(g); } continue; }
|
|
196
|
-
|
|
197
|
-
// Unknown slash → help
|
|
198
|
-
if (inp.startsWith("/")) { process.stdout.write(helpText()); continue; }
|
|
187
|
+
if (cmdL === "/quit" || cmdL === "/exit") { process.stdout.write(chalk.dim("\n Session ended\n")); rl.close(); await ctx.closeAll(); process.exit(0); return; }
|
|
188
|
+
if (cmdL === "/help") { process.stdout.write(helpText()); ask(); return; }
|
|
189
|
+
if (cmdL === "/clear") { console.clear(); welcome(agent); process.stdout.write(chalk.dim(" Key: " + haveKey + "\n\n")); ask(); return; }
|
|
190
|
+
if (cmdL === "/status") { process.stdout.write(chalk.bold("\n " + currentAgent.displayName + " (" + currentAgent.name + ")\n") + chalk.dim(" State: " + currentAgent.state + " · Memory: " + currentAgent.memory.shortTerm.length + " msgs\n\n")); ask(); return; }
|
|
191
|
+
if (cmdL === "/cost") { process.stdout.write(chalk.bold("\n Total: " + formatCost(ctx.llm.getTotalCost()) + "\n\n")); ask(); return; }
|
|
192
|
+
if (cmdL === "/compact") { const r = await currentAgent.compact(); process.stdout.write(chalk.green(" ✓ " + r + "\n\n")); ask(); return; }
|
|
193
|
+
if (cmdL === "/version") { process.stdout.write(" Skyloom v" + VERSION + "\n"); ask(); return; }
|
|
194
|
+
if (cmdL.startsWith("/task ")) { const g = inp.slice(6); process.stdout.write(chalk.cyan("\n ✦ " + g + "\n\n")); await runTask(g); ask(); return; }
|
|
195
|
+
if (inp.startsWith("/")) { process.stdout.write(helpText()); ask(); return; }
|
|
199
196
|
|
|
200
197
|
// ── Chat ──
|
|
201
|
-
process.stdout.write(chalk.dim(
|
|
202
|
-
|
|
198
|
+
process.stdout.write(chalk.dim(" " + currentAgent.displayName + " thinking...\r"));
|
|
203
199
|
try {
|
|
204
|
-
const response = await
|
|
205
|
-
process.stdout.write("\r" + " ".repeat(40) + "\r");
|
|
206
|
-
|
|
200
|
+
const response = await currentAgent.chat(inp);
|
|
201
|
+
process.stdout.write("\r" + " ".repeat(40) + "\r\n");
|
|
202
|
+
const lines = render(response);
|
|
203
|
+
for (const l of lines) process.stdout.write(l + "\n");
|
|
207
204
|
process.stdout.write("\n");
|
|
208
|
-
|
|
209
|
-
process.stdout.write("
|
|
210
|
-
|
|
211
|
-
process.stdout.write(chalk.red(`\n ✗ ${(e as Error).message || e}\n\n`));
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Auto-continue
|
|
215
|
-
if (MODE.current === InteractiveMode.AUTO && agent.memory.shortTerm.length) {
|
|
216
|
-
const last = agent.memory.shortTerm[agent.memory.shortTerm.length - 1];
|
|
217
|
-
if (last?.content && /(?:接下来|下一步|继续|next|let me|I'[vl]l)/i.test(last.content.split("\n").slice(-4).join("\n"))) {
|
|
218
|
-
process.stdout.write(chalk.yellow(" [auto]\n"));
|
|
219
|
-
try {
|
|
220
|
-
const r2 = await agent.chat("请继续完成");
|
|
221
|
-
process.stdout.write("\n");
|
|
222
|
-
for (const l of render(r2)) process.stdout.write(l + "\n");
|
|
223
|
-
process.stdout.write("\n");
|
|
224
|
-
} catch { /* ignore */ }
|
|
225
|
-
}
|
|
205
|
+
} catch (e: any) {
|
|
206
|
+
process.stdout.write("\r" + " ".repeat(40) + "\r");
|
|
207
|
+
process.stdout.write(chalk.red(" ✗ " + (e.message || e) + "\n\n"));
|
|
226
208
|
}
|
|
209
|
+
ask();
|
|
227
210
|
}
|
|
228
211
|
|
|
229
|
-
|
|
230
|
-
await ctx.closeAll();
|
|
231
|
-
process.exit(0);
|
|
212
|
+
ask();
|
|
232
213
|
}
|
|
233
214
|
|
|
234
215
|
/* ═══════════════════════════════════════
|