spacecode 2.12.5 → 2.12.7

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/repl.mjs CHANGED
@@ -8465,15 +8465,26 @@ var init_mcp_manager = __esm({
8465
8465
  });
8466
8466
 
8467
8467
  // src/mcp/approve-mcp.ts
8468
+ function unsetVars(d, env3) {
8469
+ const texts = d.transport === "http" ? [d.url, ...Object.values(d.env)] : [d.command, ...d.args, ...Object.values(d.env)];
8470
+ return [...new Set(texts.flatMap((t) => expandRefs(t, env3).missing))].sort();
8471
+ }
8468
8472
  async function runApproveMcp(deps) {
8469
8473
  const { defs, statusLines } = await loadMcpDefs(deps);
8470
8474
  if (!deps.quiet) for (const l of statusLines) deps.log(l);
8471
8475
  const stdio = defs;
8472
- const todo = [];
8476
+ let todo = [];
8473
8477
  for (const d of stdio) {
8474
8478
  const key = approvalKey(d);
8475
8479
  if (await deps.trust.stdioStatus(key) === "unapproved") todo.push(d);
8476
8480
  }
8481
+ if (deps.quiet && deps.procEnv && todo.length > 0) {
8482
+ const env3 = deps.procEnv;
8483
+ const ready = todo.filter((d) => unsetVars(d, env3).length === 0);
8484
+ const waiting = todo.length - ready.length;
8485
+ if (waiting > 0) deps.log(`${waiting} imported MCP server(s) wait for environment variables (see \`spacecode approve-mcp\`); they are not asked about until those are set.`);
8486
+ todo = ready;
8487
+ }
8477
8488
  if (todo.length === 0) {
8478
8489
  if (!deps.quiet) deps.log(stdio.length === 0 ? "No stdio MCP servers to approve." : "Every stdio MCP server is already approved on this machine.");
8479
8490
  return 0;
@@ -8482,6 +8493,13 @@ async function runApproveMcp(deps) {
8482
8493
  deps.log(`${todo.length} MCP server(s) need approval, but this is not an interactive terminal: nothing was approved.`);
8483
8494
  return 1;
8484
8495
  }
8496
+ if (deps.quiet && todo.length > STARTUP_PROMPT_LIMIT) {
8497
+ const answer = (await deps.ask(`${todo.length} imported MCP servers can be approved now. Review them one by one? [y/N] `) ?? "").trim().toLowerCase();
8498
+ if (answer !== "y" && answer !== "yes") {
8499
+ deps.log("Skipped. Run `spacecode approve-mcp` when you want to review them; nothing spawns until then.");
8500
+ return 0;
8501
+ }
8502
+ }
8485
8503
  if (deps.quiet) deps.log(`
8486
8504
  ${todo.length} imported MCP server(s) want to run a command on this machine. Nothing spawns until you approve it (you can also run \`spacecode approve-mcp\` later).`);
8487
8505
  let approved = 0;
@@ -8506,12 +8524,14 @@ MCP server ${show(d.name)} (${d.origin} definition) wants to run on this machine
8506
8524
  ${approved} of ${todo.length} approved.`);
8507
8525
  return 0;
8508
8526
  }
8509
- var show;
8527
+ var STARTUP_PROMPT_LIMIT, show;
8510
8528
  var init_approve_mcp = __esm({
8511
8529
  "src/mcp/approve-mcp.ts"() {
8512
8530
  "use strict";
8531
+ init_src();
8513
8532
  init_mcp_loader();
8514
8533
  init_mcp_manager();
8534
+ STARTUP_PROMPT_LIMIT = 3;
8515
8535
  show = (s) => JSON.stringify(s);
8516
8536
  }
8517
8537
  });
@@ -8545,7 +8565,8 @@ async function runApproveMcpOnNode(opts) {
8545
8565
  }
8546
8566
  } } : {},
8547
8567
  log: (line) => console.log(line),
8548
- quiet: opts.quiet
8568
+ quiet: opts.quiet,
8569
+ procEnv: process.env
8549
8570
  });
8550
8571
  } finally {
8551
8572
  rl?.close();
@@ -104580,12 +104601,18 @@ var init_import_reload = __esm({
104580
104601
 
104581
104602
  // src/command-loader.ts
104582
104603
  import { join as join14 } from "node:path";
104583
- async function userCommands(deps, lines2) {
104604
+ function skillCommandBody(name) {
104605
+ return `Run the imported skill "${name}": call the skill tool with name "${name}" and follow its instructions.
104606
+
104607
+ Arguments (may be empty): $ARGUMENTS`;
104608
+ }
104609
+ async function userCommands(deps, lines2, includeSkills) {
104610
+ const skillNames = /* @__PURE__ */ new Set();
104584
104611
  try {
104585
104612
  const apiKey = await resolveApiKey({ env: deps.env, readFile: deps.readFile, homedir: deps.homedir });
104586
104613
  if (!apiKey) {
104587
104614
  lines2.push("commands: user commands NOT loaded (not signed in)");
104588
- return [];
104615
+ return { defs: [], skillNames };
104589
104616
  }
104590
104617
  const client = createProfileClient({ baseUrl: backendUrl(deps.env), apiKey, fetch: deps.fetch, timeoutMs: 3e3 });
104591
104618
  const index = await client.index();
@@ -104601,10 +104628,24 @@ async function userCommands(deps, lines2) {
104601
104628
  }
104602
104629
  })
104603
104630
  );
104604
- return defs;
104631
+ if (includeSkills) {
104632
+ const taken = /* @__PURE__ */ new Set([...defs.map((d) => d.name.toLowerCase()), ...deps.builtins.map((b2) => b2.toLowerCase())]);
104633
+ let hidden = 0;
104634
+ for (const a of index?.assets ?? []) {
104635
+ if (a.kind !== "skill" || (a.description ?? "").startsWith("file:")) continue;
104636
+ if (taken.has(a.name.toLowerCase())) {
104637
+ hidden++;
104638
+ continue;
104639
+ }
104640
+ skillNames.add(a.name.toLowerCase());
104641
+ defs.push({ name: a.name, description: a.description ?? "", body: skillCommandBody(a.name) });
104642
+ }
104643
+ if (hidden > 0) lines2.push(`commands: ${hidden} skill(s) not offered as /name (a command or built-in has that name)`);
104644
+ }
104645
+ return { defs, skillNames };
104605
104646
  } catch (err) {
104606
104647
  lines2.push(`commands: user commands NOT loaded (${err instanceof Error ? err.message : String(err)})`);
104607
- return [];
104648
+ return { defs: [], skillNames };
104608
104649
  }
104609
104650
  }
104610
104651
  async function repoCommands(deps, lines2) {
@@ -104635,6 +104676,7 @@ async function repoCommands(deps, lines2) {
104635
104676
  }
104636
104677
  async function loadCommands(deps) {
104637
104678
  const configPath = join14(deps.configDir, "imports.json");
104679
+ let skillsOn = false;
104638
104680
  try {
104639
104681
  const loaded = parseImportsConfig(await deps.readFile(configPath).catch((err) => {
104640
104682
  if (err.code === "ENOENT") return null;
@@ -104642,15 +104684,17 @@ async function loadCommands(deps) {
104642
104684
  }), configPath);
104643
104685
  if (loaded.error) return off("commands: off (imports.json unreadable)");
104644
104686
  if (!loaded.config.categories.commands) return off("commands: off (imports.json has commands off)");
104687
+ skillsOn = loaded.config.categories.skills;
104645
104688
  } catch (err) {
104646
104689
  return off(`commands: off (imports.json not read: ${err instanceof Error ? err.message : String(err)})`);
104647
104690
  }
104648
104691
  const lines2 = [];
104649
- const [user, repo] = await Promise.all([userCommands(deps, lines2), repoCommands(deps, lines2)]);
104692
+ const [{ defs: user, skillNames }, repo] = await Promise.all([userCommands(deps, lines2, skillsOn), repoCommands(deps, lines2)]);
104650
104693
  const reg = buildCommandRegistry({ builtins: deps.builtins, user, repo });
104651
- const nUser = reg.commands.filter((c) => c.scope === "user").length;
104652
- const nRepo = reg.commands.length - nUser;
104653
- lines2.unshift(`commands: ${nUser} user, ${nRepo} repo${reg.shadowed.length ? `; repo shadows user: ${reg.shadowed.map((n) => "/" + n).join(", ")}` : ""}`);
104694
+ const nSkill = reg.commands.filter((c) => c.scope === "user" && skillNames.has(c.name)).length;
104695
+ const nUser = reg.commands.filter((c) => c.scope === "user").length - nSkill;
104696
+ const nRepo = reg.commands.length - nUser - nSkill;
104697
+ lines2.unshift(`commands: ${nUser} user, ${nRepo} repo${nSkill ? `, ${nSkill} from skills` : ""}${reg.shadowed.length ? `; repo shadows user: ${reg.shadowed.map((n) => "/" + n).join(", ")}` : ""}`);
104654
104698
  for (const s of reg.skipped) lines2.push(`commands: skipped [${s.scope}] /${s.name} (${s.reason})`);
104655
104699
  return { commands: reg.commands, statusLines: lines2 };
104656
104700
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spacecode",
3
- "version": "2.12.5",
3
+ "version": "2.12.7",
4
4
  "description": "SpaceCode - launch cloud dev environments with AI coding agents",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",