spacecode 2.9.0 → 2.10.0

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
@@ -7252,6 +7252,28 @@ var init_trust = __esm({
7252
7252
  }
7253
7253
  });
7254
7254
 
7255
+ // ../agent/src/import/source-changes.ts
7256
+ function fingerprintSources(files, hash) {
7257
+ const fingerprint = {};
7258
+ for (const [path12, content] of Object.entries(files)) fingerprint[path12] = hash(content);
7259
+ return fingerprint;
7260
+ }
7261
+ function diffSources(before, after) {
7262
+ const has2 = (o, k2) => Object.hasOwn(o, k2);
7263
+ const added = Object.keys(after).filter((k2) => !has2(before, k2)).sort();
7264
+ const removed = Object.keys(before).filter((k2) => !has2(after, k2)).sort();
7265
+ const modified = Object.keys(after).filter((k2) => has2(before, k2) && before[k2] !== after[k2]).sort();
7266
+ return { added, modified, removed, changed: added.length + modified.length + removed.length > 0 };
7267
+ }
7268
+ function formatSourceChanges(c) {
7269
+ return [...c.added.map((p) => `added: ${p}`), ...c.modified.map((p) => `modified: ${p}`), ...c.removed.map((p) => `removed: ${p}`)];
7270
+ }
7271
+ var init_source_changes = __esm({
7272
+ "../agent/src/import/source-changes.ts"() {
7273
+ "use strict";
7274
+ }
7275
+ });
7276
+
7255
7277
  // ../agent/src/import/imports-config.ts
7256
7278
  function allOffConfig() {
7257
7279
  return {
@@ -7986,6 +8008,7 @@ __export(src_exports, {
7986
8008
  createSkillTool: () => createSkillTool,
7987
8009
  createTodoTool: () => createTodoTool,
7988
8010
  defaultModeFor: () => defaultModeFor,
8011
+ diffSources: () => diffSources,
7989
8012
  estimateTokens: () => estimateTokens,
7990
8013
  estimateTotalTokens: () => estimateTotalTokens,
7991
8014
  expandCommand: () => expandCommand,
@@ -7993,8 +8016,10 @@ __export(src_exports, {
7993
8016
  expandServerEnv: () => expandServerEnv,
7994
8017
  extractRuleLines: () => extractRuleLines,
7995
8018
  findUnsupported: () => findUnsupported,
8019
+ fingerprintSources: () => fingerprintSources,
7996
8020
  flattenMessageText: () => flattenMessageText,
7997
8021
  formatSkillBody: () => formatSkillBody,
8022
+ formatSourceChanges: () => formatSourceChanges,
7998
8023
  formatToolResult: () => formatToolResult,
7999
8024
  guardHost: () => guardHost,
8000
8025
  isReadOnlyCommand: () => isReadOnlyCommand,
@@ -8060,6 +8085,7 @@ var init_src = __esm({
8060
8085
  init_adapt_plan();
8061
8086
  init_instructions_tool();
8062
8087
  init_trust();
8088
+ init_source_changes();
8063
8089
  init_imports_config();
8064
8090
  init_permission_rules();
8065
8091
  init_command_args();
@@ -103444,6 +103470,7 @@ var init_slash_commands = __esm({
103444
103470
  { name: "turns", description: "Browse earlier turns and open their steps (shortcut: ctrl+o)" },
103445
103471
  { name: "commands", description: "List the slash commands imported from Claude Code" },
103446
103472
  { name: "skills", description: "List imported skills; /skills show <name> [file], /skills delete <name>" },
103473
+ { name: "import", description: "Re-check imported Claude Code sources: /import reload shows changes; /import reload trust applies them" },
103447
103474
  { name: "tools", description: "List the tools available to the agent" },
103448
103475
  { name: "exit", description: "Quit the REPL (alias: /quit)" },
103449
103476
  { name: "quit", description: "Quit the REPL" }
@@ -105043,6 +105070,41 @@ var init_permission_rules_loader = __esm({
105043
105070
  }
105044
105071
  });
105045
105072
 
105073
+ // src/import-reload.ts
105074
+ async function checkRepoSources(deps, loaded) {
105075
+ const files = await readRepoSources(deps.cwd);
105076
+ const current = fingerprintSources(files, sha2562);
105077
+ const status = Object.keys(files).length === 0 ? "untrusted" : await createNodeTrustStore(deps.configDir).repoStatus(deps.cwd, files);
105078
+ return { current, changes: diffSources(loaded, current), trusted: status === "trusted" || status === "session" };
105079
+ }
105080
+ async function describeReload(deps, loaded) {
105081
+ const check = await checkRepoSources(deps, loaded);
105082
+ if (!check.changes.changed) return ["No change in the repo import sources since they were loaded."];
105083
+ const files = await readRepoSources(deps.cwd);
105084
+ return [
105085
+ "Repo import sources changed since they were loaded:",
105086
+ ...formatSourceChanges(check.changes).map((l) => ` ${l}`),
105087
+ "",
105088
+ "Trusting the new state would load:",
105089
+ ...describeRepoSources(files).length ? describeRepoSources(files) : [" (nothing)"],
105090
+ "",
105091
+ "Run /import reload trust to trust it and apply the rules and commands. MCP servers and skills apply on the next start."
105092
+ ];
105093
+ }
105094
+ async function trustCurrentSources(deps) {
105095
+ const files = await readRepoSources(deps.cwd);
105096
+ await createNodeTrustStore(deps.configDir).setRepo(deps.cwd, files, "trusted");
105097
+ return fingerprintSources(files, sha2562);
105098
+ }
105099
+ var init_import_reload = __esm({
105100
+ "src/import-reload.ts"() {
105101
+ "use strict";
105102
+ init_src();
105103
+ init_import_gate();
105104
+ init_repo_trust_prompt();
105105
+ }
105106
+ });
105107
+
105046
105108
  // src/command-loader.ts
105047
105109
  import { join as join12 } from "node:path";
105048
105110
  async function userCommands(deps, lines2) {
@@ -105196,6 +105258,7 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
105196
105258
  const pendingRef = (0, import_react30.useRef)(null);
105197
105259
  const toolsetRef = (0, import_react30.useRef)(null);
105198
105260
  const rulesRef = (0, import_react30.useRef)(void 0);
105261
+ const loadedSourcesRef = (0, import_react30.useRef)(null);
105199
105262
  const [commandsLoaded, setCommandsLoaded] = (0, import_react30.useState)({ commands: [], statusLines: [] });
105200
105263
  (0, import_react30.useEffect)(() => {
105201
105264
  let live2 = true;
@@ -105420,6 +105483,29 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
105420
105483
  case "context":
105421
105484
  notice(formatContextReport({ contextTokens: estimateTotalTokens("", history), maxContextTokens: DEFAULT_MAX_CONTEXT_TOKENS }));
105422
105485
  return;
105486
+ case "import": {
105487
+ const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join13(homedir2(), ".spacecode");
105488
+ const deps = { configDir, cwd: process.cwd() };
105489
+ const [sub, ...rest] = slash.args.split(/\s+/).filter(Boolean);
105490
+ if (sub !== "reload" || rest.length > 1 || rest[0] !== void 0 && rest[0] !== "trust") {
105491
+ notice("Usage: /import reload (show changes to the repo import sources) or /import reload trust (trust the new state and apply it).");
105492
+ return;
105493
+ }
105494
+ try {
105495
+ const baseline = loadedSourcesRef.current ?? fingerprintSources(await readRepoSources(process.cwd()), sha2562);
105496
+ if (rest[0] === "trust") {
105497
+ loadedSourcesRef.current = await trustCurrentSources(deps);
105498
+ const loaded = await loadPermissionRules({ configDir, homeDir: homedir2(), cwd: process.cwd() });
105499
+ rulesRef.current = loaded.rules;
105500
+ const cmds = await loadCommands({ configDir, cwd: process.cwd(), env: process.env, readFile: (p) => readFile9(p, "utf-8"), homedir: homedir2, fetch, builtins: SLASH_COMMANDS.map((c) => c.name) });
105501
+ setCommandsLoaded(cmds);
105502
+ notice(["Trusted the repo import sources as they are now.", ...loaded.statusLines, ...cmds.statusLines, "MCP servers and skills apply on the next start."].join("\n"));
105503
+ } else notice((await describeReload(deps, baseline)).join("\n"));
105504
+ } catch (err) {
105505
+ notice(`/import reload failed (${err instanceof Error ? err.message : String(err)}); nothing was changed.`);
105506
+ }
105507
+ return;
105508
+ }
105423
105509
  case "record":
105424
105510
  notice(await recording().command(slash.args));
105425
105511
  return;
@@ -105605,6 +105691,22 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105605
105691
  let failed = false;
105606
105692
  let keepForRetry = false;
105607
105693
  try {
105694
+ if (loadedSourcesRef.current) {
105695
+ const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join13(homedir2(), ".spacecode");
105696
+ try {
105697
+ const check = await checkRepoSources({ configDir, cwd: process.cwd() }, loadedSourcesRef.current);
105698
+ if (check.changes.changed) {
105699
+ const loaded = await loadPermissionRules({ configDir, homeDir: homedir2(), cwd: process.cwd() });
105700
+ rulesRef.current = loaded.rules;
105701
+ loadedSourcesRef.current = check.current;
105702
+ const lead = check.trusted ? "Repo import sources changed since they were loaded; the new state is already trusted, so its permission rules now apply:" : "Repo import sources changed since they were loaded; repo permission rules are dropped until you trust the new state:";
105703
+ notice([lead, ...formatSourceChanges(check.changes).map((l) => ` ${l}`), ...check.trusted ? [] : ["Run /import reload to review, /import reload trust to apply."]].join("\n"));
105704
+ }
105705
+ } catch (err) {
105706
+ rulesRef.current = void 0;
105707
+ notice(`Repo import source check failed (${err instanceof Error ? err.message : String(err)}); repo permission rules are dropped.`);
105708
+ }
105709
+ }
105608
105710
  profilePromiseRef.current ??= prepareProfile({
105609
105711
  env: process.env,
105610
105712
  readFile: (p) => readFile9(p, "utf-8"),
@@ -105618,9 +105720,11 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105618
105720
  gitRoot: gitTopLevel,
105619
105721
  importGate: async () => {
105620
105722
  const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join13(homedir2(), ".spacecode");
105723
+ const fingerprint = fingerprintSources(await readRepoSources(process.cwd()), sha2562);
105621
105724
  const gate = await importGateStatus({ configDir, cwd: process.cwd(), interactive: Boolean(process.stdin.isTTY) });
105622
105725
  const loaded = await loadPermissionRules({ configDir, homeDir: homedir2(), cwd: process.cwd() });
105623
105726
  rulesRef.current = loaded.rules;
105727
+ loadedSourcesRef.current = fingerprint;
105624
105728
  if (loaded.defaultMode && !yolo2) permissions.set(loaded.defaultMode);
105625
105729
  return [...gate, ...loaded.statusLines];
105626
105730
  }
@@ -105780,6 +105884,8 @@ var init_useReplController = __esm({
105780
105884
  init_mcp_manager();
105781
105885
  init_import_gate();
105782
105886
  init_permission_rules_loader();
105887
+ init_import_reload();
105888
+ init_src();
105783
105889
  init_command_loader();
105784
105890
  init_slash_commands();
105785
105891
  init_useEventAccumulator();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spacecode",
3
- "version": "2.9.0",
3
+ "version": "2.10.0",
4
4
  "description": "SpaceCode - launch cloud dev environments with AI coding agents",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",