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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/cli/main.ts +46 -65
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skyloom",
3
- "version": "1.5.0",
3
+ "version": "1.5.1",
4
4
  "description": "天空织机 Skyloom — 6 weather-themed AI agents: Fog, Rain, Frost, Snow, Dew, Fair",
5
5
  "preferGlobal": true,
6
6
  "type": "commonjs",
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(`Unknown agent: ${agentName}\n`)); return; }
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
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
154
- const history: string[] = [];
171
+ process.stdout.write(chalk.dim(" Key: " + haveKey + "\n\n"));
155
172
 
156
- for await (const line of rl) {
157
- const inp = line.trim();
158
- if (!inp) continue;
173
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
159
174
 
160
- if (inp[0] !== "/" && !history.includes(inp)) {
161
- history.push(inp);
162
- if (history.length > 50) history.shift();
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 cmdLower = inp.toLowerCase();
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 (cmdLower === `/${n}`) {
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 (handled) continue;
178
- if (cmdLower === "/quit" || cmdLower === "/exit") break;
179
- if (cmdLower === "/help") { process.stdout.write(helpText()); handled = true; }
180
- if (cmdLower === "/clear") { console.clear(); welcome(agent); handled = true; }
181
- if (cmdLower === "/version") { process.stdout.write(` Skyloom v${VERSION}\n`); handled = true; }
182
- if (cmdLower === "/status") { process.stdout.write(chalk.bold(`\n ${agent.displayName} (${agent.name})\n`) + chalk.dim(` State: ${agent.state} · Memory: ${agent.memory.shortTerm.length} msgs\n\n`)); handled = true; }
183
- if (cmdLower === "/cost") { process.stdout.write(chalk.bold(`\n Total: ${formatCost(ctx.llm.getTotalCost())}\n\n`)); handled = true; }
184
- if (cmdLower === "/cost reset") { (ctx.llm as any).resetUsageStats?.(); process.stdout.write(chalk.dim(" Reset\n\n")); handled = true; }
185
- if (cmdLower === "/compact") { const r = await agent.compact(); process.stdout.write(chalk.green(` ✓ ${r}\n\n`)); handled = true; }
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(` ${agent.displayName} thinking...\r`));
202
-
198
+ process.stdout.write(chalk.dim(" " + currentAgent.displayName + " thinking...\r"));
203
199
  try {
204
- const response = await agent.chat(inp);
205
- process.stdout.write("\r" + " ".repeat(40) + "\r");
206
- for (const l of render(response)) process.stdout.write(l + "\n");
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
- // Status bar
209
- process.stdout.write(" " + statusBar(agent, ctx) + "\n");
210
- } catch (e) {
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
- process.stdout.write(chalk.dim("\n Session ended\n"));
230
- await ctx.closeAll();
231
- process.exit(0);
212
+ ask();
232
213
  }
233
214
 
234
215
  /* ═══════════════════════════════════════