spacecode 2.7.0 → 2.8.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
@@ -1283,26 +1283,26 @@ var require_react_development = __commonJS({
1283
1283
  }
1284
1284
  return lazyType;
1285
1285
  }
1286
- function forwardRef2(render3) {
1286
+ function forwardRef2(render4) {
1287
1287
  {
1288
- if (render3 != null && render3.$$typeof === REACT_MEMO_TYPE) {
1288
+ if (render4 != null && render4.$$typeof === REACT_MEMO_TYPE) {
1289
1289
  error("forwardRef requires a render function but received a `memo` component. Instead of forwardRef(memo(...)), use memo(forwardRef(...)).");
1290
- } else if (typeof render3 !== "function") {
1291
- error("forwardRef requires a render function but was given %s.", render3 === null ? "null" : typeof render3);
1290
+ } else if (typeof render4 !== "function") {
1291
+ error("forwardRef requires a render function but was given %s.", render4 === null ? "null" : typeof render4);
1292
1292
  } else {
1293
- if (render3.length !== 0 && render3.length !== 2) {
1294
- error("forwardRef render functions accept exactly two parameters: props and ref. %s", render3.length === 1 ? "Did you forget to use the ref parameter?" : "Any additional parameter will be undefined.");
1293
+ if (render4.length !== 0 && render4.length !== 2) {
1294
+ error("forwardRef render functions accept exactly two parameters: props and ref. %s", render4.length === 1 ? "Did you forget to use the ref parameter?" : "Any additional parameter will be undefined.");
1295
1295
  }
1296
1296
  }
1297
- if (render3 != null) {
1298
- if (render3.defaultProps != null || render3.propTypes != null) {
1297
+ if (render4 != null) {
1298
+ if (render4.defaultProps != null || render4.propTypes != null) {
1299
1299
  error("forwardRef render functions do not support propTypes or defaultProps. Did you accidentally pass a React component?");
1300
1300
  }
1301
1301
  }
1302
1302
  }
1303
1303
  var elementType = {
1304
1304
  $$typeof: REACT_FORWARD_REF_TYPE,
1305
- render: render3
1305
+ render: render4
1306
1306
  };
1307
1307
  {
1308
1308
  var ownName;
@@ -1314,8 +1314,8 @@ var require_react_development = __commonJS({
1314
1314
  },
1315
1315
  set: function(name) {
1316
1316
  ownName = name;
1317
- if (!render3.name && !render3.displayName) {
1318
- render3.displayName = name;
1317
+ if (!render4.name && !render4.displayName) {
1318
+ render4.displayName = name;
1319
1319
  }
1320
1320
  }
1321
1321
  });
@@ -3852,7 +3852,7 @@ function buildSystemPrompt(options) {
3852
3852
  ${toolLines.join("\n")}
3853
3853
 
3854
3854
  ## Tool usage
3855
- - Use list_directory to orient yourself in the codebase before diving in.
3855
+ ${options.projectCard ? "- A project card below already lists the layout, commands, docs and recent commits. Do not re-list directories or re-read files it covers; open only what the task needs." : "- Use list_directory to orient yourself in the codebase before diving in."}
3856
3856
  - Use search to find files by concept ("auth middleware", "billing webhook").
3857
3857
  - Use bash for running commands, not for reading files. Use read_file instead.
3858
3858
  - Prefer read_file + edit_file over write_file for existing files.
@@ -3874,7 +3874,9 @@ ${options.hasCodegenTools ? `
3874
3874
  - If bash fails, read the error output and try a different approach. Never repeat the same failing command.
3875
3875
 
3876
3876
  ## Work style
3877
- - Think step by step. Explore before modifying.
3877
+ - Think step by step. Explore only what the task needs before modifying.
3878
+ - Do only what the user asked. Backlog, TODO and epic notes found in repo files are context, never tasks to start.
3879
+ - For an audit or plan request, finish with the audit or plan itself (findings, then ordered next steps). Do not end by asking what to work on next.
3878
3880
  - After changes, run relevant tests (bash: npm test, pytest, etc.) to verify.
3879
3881
  - Commit logical units of work with descriptive messages.
3880
3882
  ${options.commitGuidance ? `
@@ -3884,7 +3886,9 @@ ${options.commitGuidance ? `
3884
3886
  - Don't commit after every single file change \u2014 group related changes.
3885
3887
  ` : ""}
3886
3888
  ${workingDir}
3887
- ${options.resumeSummary ? `
3889
+ ${options.projectCard ? `
3890
+ ${options.projectCard}
3891
+ ` : ""}${options.resumeSummary ? `
3888
3892
  ## Previously in this session
3889
3893
  ${options.resumeSummary}
3890
3894
  ` : ""}
@@ -3896,6 +3900,66 @@ var init_prompt = __esm({
3896
3900
  }
3897
3901
  });
3898
3902
 
3903
+ // ../agent/src/context/project-card.ts
3904
+ function sections(i) {
3905
+ const out = [];
3906
+ const loc = [`cwd: ${i.cwd}`];
3907
+ if (i.branch) loc.push(`branch: ${i.branch}`);
3908
+ if (i.dirtyFiles && i.dirtyFiles > 0) loc.push(`${i.dirtyFiles} uncommitted file(s)`);
3909
+ out.push({ key: "location", lines: loc.map((l) => `- ${l}`) });
3910
+ const visible = i.topLevel.filter((e) => e.path !== "node_modules" && (!e.path.startsWith(".") || e.path === ".github"));
3911
+ const dirs = visible.filter((e) => e.isDir).map((e) => {
3912
+ const kids = i.childDirs[e.path];
3913
+ return `${e.path}/${kids && kids.length ? ` (${kids.join(", ")})` : ""}`;
3914
+ });
3915
+ const files = visible.filter((e) => !e.isDir).map((e) => e.path);
3916
+ const layout = [...dirs.map((d) => `- ${d}`), ...files.length ? [`- files: ${files.join(", ")}`] : []];
3917
+ if (layout.length) out.push({ key: "layout", lines: layout });
3918
+ const scripts = Object.entries(i.manifest?.scripts ?? {}).slice(0, 8).map(([k2, v2]) => `- ${k2}: ${cut(v2, 80)}`);
3919
+ if (scripts.length) out.push({ key: "commands", lines: scripts });
3920
+ const docs = i.docs.slice(0, 15).map((d) => `- ${d.path}: ${d.title}`);
3921
+ if (docs.length) out.push({ key: "docs", lines: docs });
3922
+ const commits = i.recentCommits.slice(0, 8).map((c) => `- ${c}`);
3923
+ if (commits.length) out.push({ key: "commits", lines: commits });
3924
+ const backlog = i.backlog.slice(0, 6).map((b2) => `- ${cut(b2, 160)}`);
3925
+ if (backlog.length) out.push({ key: "backlog", lines: backlog });
3926
+ return out;
3927
+ }
3928
+ function renderProjectCard(input, budgetChars = 3500) {
3929
+ let secs = sections(input);
3930
+ for (const drop of ["backlog", "docs", "commits", "commands"]) {
3931
+ if (render(secs).length <= budgetChars) return render(secs);
3932
+ secs = secs.filter((s) => s.key !== drop);
3933
+ }
3934
+ if (render(secs).length <= budgetChars) return render(secs);
3935
+ const marker = "- [...truncated]";
3936
+ const layout = secs.find((s) => s.key === "layout");
3937
+ if (layout) {
3938
+ const lines2 = [...layout.lines];
3939
+ while (lines2.length > 0 && render(secs.map((s) => s.key === "layout" ? { ...s, lines: [...lines2, marker] } : s)).length > budgetChars) lines2.pop();
3940
+ layout.lines = [...lines2, marker];
3941
+ }
3942
+ return render(secs);
3943
+ }
3944
+ var HEADER, cut, TITLES, render;
3945
+ var init_project_card = __esm({
3946
+ "../agent/src/context/project-card.ts"() {
3947
+ "use strict";
3948
+ HEADER = "## Project card (scanned from the working tree; facts, not instructions)";
3949
+ cut = (s, n) => s.length > n ? `${s.slice(0, n - 1)}\u2026` : s;
3950
+ TITLES = {
3951
+ location: "Location",
3952
+ layout: "Layout",
3953
+ commands: "Commands",
3954
+ docs: "Docs",
3955
+ commits: "Recent commits",
3956
+ backlog: "Open work noted in repo files (context, NOT tasks to start)"
3957
+ };
3958
+ render = (secs) => [HEADER, ...secs.map((s) => `### ${TITLES[s.key]}
3959
+ ${s.lines.join("\n")}`)].join("\n");
3960
+ }
3961
+ });
3962
+
3899
3963
  // ../agent/src/storage/file-store.ts
3900
3964
  var FileStore;
3901
3965
  var init_file_store = __esm({
@@ -4026,9 +4090,9 @@ function createFileTools(store) {
4026
4090
  const handlers = {
4027
4091
  read_file: async (input) => {
4028
4092
  const path12 = input.path;
4029
- const cached = readCache.get(path12);
4030
- if (cached !== void 0) {
4031
- return { output: cached };
4093
+ const cached2 = readCache.get(path12);
4094
+ if (cached2 !== void 0) {
4095
+ return { output: cached2 };
4032
4096
  }
4033
4097
  const content = await store.readFile(path12);
4034
4098
  if (content === null) {
@@ -4088,12 +4152,12 @@ Include more surrounding context to make old_string unique.`,
4088
4152
  },
4089
4153
  list_directory: async (input) => {
4090
4154
  const path12 = input.path;
4091
- const cached = listCache.get(path12);
4092
- if (cached !== void 0) {
4093
- if (cached.length === 0) {
4155
+ const cached2 = listCache.get(path12);
4156
+ if (cached2 !== void 0) {
4157
+ if (cached2.length === 0) {
4094
4158
  return { output: `Empty or not found: ${path12}` };
4095
4159
  }
4096
- return { output: cached.join("\n") };
4160
+ return { output: cached2.join("\n") };
4097
4161
  }
4098
4162
  const entries = await store.listDirectory(path12);
4099
4163
  listCache.set(path12, entries);
@@ -5705,9 +5769,9 @@ async function sha256(content) {
5705
5769
  const hashArray = new Uint8Array(hashBuffer);
5706
5770
  return Array.from(hashArray).map((b2) => b2.toString(16).padStart(2, "0")).join("");
5707
5771
  }
5708
- async function computeLockfileHash(readFile9) {
5772
+ async function computeLockfileHash(readFile10) {
5709
5773
  for (const name of LOCKFILES) {
5710
- const content = await readFile9(name);
5774
+ const content = await readFile10(name);
5711
5775
  if (content) {
5712
5776
  const hash = await sha256(content);
5713
5777
  return hash.slice(0, 12);
@@ -5782,25 +5846,25 @@ function stripCodeFence(raw) {
5782
5846
  return fenced ? fenced[1].trim() : trimmed;
5783
5847
  }
5784
5848
  function buildSummaryText(decisions, workItems) {
5785
- const sections = [];
5849
+ const sections2 = [];
5786
5850
  if (decisions.length > 0) {
5787
- sections.push(
5851
+ sections2.push(
5788
5852
  ["Decisions:", ...decisions.map((d) => `- ${d.description}`)].join("\n")
5789
5853
  );
5790
5854
  }
5791
5855
  const openItems = workItems.filter((item) => item.status === "open");
5792
5856
  if (openItems.length > 0) {
5793
- sections.push(
5857
+ sections2.push(
5794
5858
  ["Open work items:", ...openItems.map((item) => `- ${item.description}`)].join("\n")
5795
5859
  );
5796
5860
  }
5797
5861
  const doneItems = workItems.filter((item) => item.status === "done");
5798
5862
  if (doneItems.length > 0) {
5799
- sections.push(
5863
+ sections2.push(
5800
5864
  ["Completed this session:", ...doneItems.map((item) => `- ${item.description}`)].join("\n")
5801
5865
  );
5802
5866
  }
5803
- return sections.join("\n\n");
5867
+ return sections2.join("\n\n");
5804
5868
  }
5805
5869
  async function summarizeConversation(messages, config) {
5806
5870
  if (messages.length === 0) {
@@ -6463,8 +6527,8 @@ function createProfileClient({ baseUrl, apiKey, fetch: doFetch, timeoutMs = 8e3
6463
6527
  }
6464
6528
  },
6465
6529
  /** Asks the server-side classifier to decide keep/on_demand/drop per section (see adapt-plan.ts). */
6466
- async adapt(sections) {
6467
- const res = await call("POST", "/adapt", { body: { sections }, timeoutMs: ADAPT_TIMEOUT_MS });
6530
+ async adapt(sections2) {
6531
+ const res = await call("POST", "/adapt", { body: { sections: sections2 }, timeoutMs: ADAPT_TIMEOUT_MS });
6468
6532
  return await res.json();
6469
6533
  },
6470
6534
  /** Deletes one asset; false when it did not exist. */
@@ -6580,17 +6644,17 @@ var init_memory_tool = __esm({
6580
6644
  // ../agent/src/import/profile-loader.ts
6581
6645
  async function loadProfile(opts) {
6582
6646
  const { client, cache: cache4, projectKeys } = opts;
6583
- let cached = null;
6647
+ let cached2 = null;
6584
6648
  let cacheError;
6585
6649
  try {
6586
- cached = await cache4.read();
6650
+ cached2 = await cache4.read();
6587
6651
  } catch (err) {
6588
6652
  cacheError = `profile cache unreadable: ${err instanceof Error ? err.message : String(err)}`;
6589
6653
  }
6590
- const sameProject = cached !== null && cached.projectKey !== null && projectKeys.includes(cached.projectKey);
6654
+ const sameProject = cached2 !== null && cached2.projectKey !== null && projectKeys.includes(cached2.projectKey);
6591
6655
  try {
6592
- const index = await client.index(sameProject ? cached.version : void 0);
6593
- if (index === null && sameProject) return { profile: cached, source: "live" };
6656
+ const index = await client.index(sameProject ? cached2.version : void 0);
6657
+ if (index === null && sameProject) return { profile: cached2, source: "live" };
6594
6658
  if (index === null) throw new Error("profile API answered 304 with no usable cached copy");
6595
6659
  const has2 = (kind, name) => index.assets.some((a) => a.kind === kind && a.name === name);
6596
6660
  const chosen = projectKeys.find((k2) => has2("memory", `${k2}.index`)) ?? null;
@@ -6612,8 +6676,8 @@ async function loadProfile(opts) {
6612
6676
  return { profile, source: "live", ...cacheError ? { error: cacheError } : {} };
6613
6677
  } catch (err) {
6614
6678
  const error = `${err instanceof Error ? err.message : String(err)}${cacheError ? `; ${cacheError}` : ""}`;
6615
- if (!cached) return { profile: null, source: "none", error };
6616
- return { profile: sameProject ? cached : { ...cached, memoryIndex: null, projectKey: null }, source: "cache", error };
6679
+ if (!cached2) return { profile: null, source: "none", error };
6680
+ return { profile: sameProject ? cached2 : { ...cached2, memoryIndex: null, projectKey: null }, source: "cache", error };
6617
6681
  }
6618
6682
  }
6619
6683
  var init_profile_loader = __esm({
@@ -6645,14 +6709,14 @@ function splitSections(text) {
6645
6709
  });
6646
6710
  if (starts.length === 0) return { preamble: text, sections: [] };
6647
6711
  const taken = /* @__PURE__ */ new Set();
6648
- const sections = starts.map((start, k2) => {
6712
+ const sections2 = starts.map((start, k2) => {
6649
6713
  const end = k2 + 1 < starts.length ? starts[k2 + 1] : lines2.length;
6650
6714
  const chunk = lines2.slice(start, end).join("\n") + (end < lines2.length ? "\n" : "");
6651
6715
  const title = lines2[start].slice(3).trim();
6652
6716
  return { title, slug: slugify(title, taken), text: chunk };
6653
6717
  });
6654
6718
  const preamble = starts[0] === 0 ? "" : lines2.slice(0, starts[0]).join("\n") + "\n";
6655
- return { preamble, sections };
6719
+ return { preamble, sections: sections2 };
6656
6720
  }
6657
6721
  function joinSections(split) {
6658
6722
  return split.preamble + split.sections.map((s) => s.text).join("");
@@ -6712,11 +6776,11 @@ function prepareAdaptation(original) {
6712
6776
  return r.text;
6713
6777
  };
6714
6778
  const preamble = tr(split.preamble);
6715
- const sections = split.sections.map((s) => {
6779
+ const sections2 = split.sections.map((s) => {
6716
6780
  const text = tr(s.text);
6717
6781
  return { slug: s.slug, title: s.title, chars: text.length, unsupported: findUnsupported(text), text };
6718
6782
  });
6719
- return { preamble, sections, replaced: [...replaced.values()] };
6783
+ return { preamble, sections: sections2, replaced: [...replaced.values()] };
6720
6784
  }
6721
6785
  function applyPlan(input, decisions, maxCoreChars = MAX_CORE_CHARS) {
6722
6786
  const bySlug = new Map(input.sections.map((s) => [s.slug, s]));
@@ -7695,6 +7759,7 @@ __export(src_exports, {
7695
7759
  prepareAdaptation: () => prepareAdaptation,
7696
7760
  projectKey: () => projectKey,
7697
7761
  readClaudeAssets: () => readClaudeAssets,
7762
+ renderProjectCard: () => renderProjectCard,
7698
7763
  runAgentLoop: () => runAgentLoop,
7699
7764
  safeSkillPath: () => safeSkillPath,
7700
7765
  serializeImportsConfig: () => serializeImportsConfig,
@@ -7712,6 +7777,7 @@ var init_src = __esm({
7712
7777
  init_context_window();
7713
7778
  init_bash();
7714
7779
  init_prompt();
7780
+ init_project_card();
7715
7781
  init_file_store();
7716
7782
  init_files();
7717
7783
  init_indexer();
@@ -7772,7 +7838,7 @@ __export(api_key_exports, {
7772
7838
  });
7773
7839
  import { join } from "node:path";
7774
7840
  async function resolveApiKey(deps) {
7775
- const { env: env3, readFile: readFile9, homedir: homedir3 } = deps;
7841
+ const { env: env3, readFile: readFile10, homedir: homedir3 } = deps;
7776
7842
  for (const name of ["SPACECODE_API_KEY", "ANTHROPIC_API_KEY"]) {
7777
7843
  const value = env3[name]?.trim();
7778
7844
  if (value) return value;
@@ -7781,7 +7847,7 @@ async function resolveApiKey(deps) {
7781
7847
  const file = join(dir, "auth.json");
7782
7848
  let raw;
7783
7849
  try {
7784
- raw = await readFile9(file);
7850
+ raw = await readFile10(file);
7785
7851
  } catch (err) {
7786
7852
  if (err.code === "ENOENT") return null;
7787
7853
  throw new Error(`Cannot read ${file}: ${err instanceof Error ? err.message : String(err)}`);
@@ -24121,7 +24187,7 @@ var require_react_reconciler_development = __commonJS({
24121
24187
  }
24122
24188
  }
24123
24189
  }
24124
- var render3 = Component.render;
24190
+ var render4 = Component.render;
24125
24191
  var ref = workInProgress2.ref;
24126
24192
  var nextChildren;
24127
24193
  var hasId;
@@ -24132,12 +24198,12 @@ var require_react_reconciler_development = __commonJS({
24132
24198
  {
24133
24199
  ReactCurrentOwner$1.current = workInProgress2;
24134
24200
  setIsRendering(true);
24135
- nextChildren = renderWithHooks(current2, workInProgress2, render3, nextProps, ref, renderLanes2);
24201
+ nextChildren = renderWithHooks(current2, workInProgress2, render4, nextProps, ref, renderLanes2);
24136
24202
  hasId = checkDidRenderIdHook();
24137
24203
  if (workInProgress2.mode & StrictLegacyMode) {
24138
24204
  setIsStrictModeForDevtools(true);
24139
24205
  try {
24140
- nextChildren = renderWithHooks(current2, workInProgress2, render3, nextProps, ref, renderLanes2);
24206
+ nextChildren = renderWithHooks(current2, workInProgress2, render4, nextProps, ref, renderLanes2);
24141
24207
  hasId = checkDidRenderIdHook();
24142
24208
  } finally {
24143
24209
  setIsStrictModeForDevtools(false);
@@ -25491,9 +25557,9 @@ var require_react_reconciler_development = __commonJS({
25491
25557
  }
25492
25558
  }
25493
25559
  var newProps = workInProgress2.pendingProps;
25494
- var render3 = newProps.children;
25560
+ var render4 = newProps.children;
25495
25561
  {
25496
- if (typeof render3 !== "function") {
25562
+ if (typeof render4 !== "function") {
25497
25563
  error("A context consumer was rendered with multiple children, or a child that isn't a function. A context consumer expects a single child that is a function. If you did pass a function, make sure there is no trailing or leading whitespace around it.");
25498
25564
  }
25499
25565
  }
@@ -25506,7 +25572,7 @@ var require_react_reconciler_development = __commonJS({
25506
25572
  {
25507
25573
  ReactCurrentOwner$1.current = workInProgress2;
25508
25574
  setIsRendering(true);
25509
- newChildren = render3(newValue);
25575
+ newChildren = render4(newValue);
25510
25576
  setIsRendering(false);
25511
25577
  }
25512
25578
  {
@@ -38606,7 +38672,7 @@ var init_log_update = __esm({
38606
38672
  let previousLineCount = 0;
38607
38673
  let previousOutput = "";
38608
38674
  let hasHiddenCursor = false;
38609
- const render3 = (str) => {
38675
+ const render4 = (str) => {
38610
38676
  if (!showCursor && !hasHiddenCursor) {
38611
38677
  cli_cursor_default.hide();
38612
38678
  hasHiddenCursor = true;
@@ -38619,12 +38685,12 @@ var init_log_update = __esm({
38619
38685
  stream.write(base_exports.eraseLines(previousLineCount) + output);
38620
38686
  previousLineCount = output.split("\n").length;
38621
38687
  };
38622
- render3.clear = () => {
38688
+ render4.clear = () => {
38623
38689
  stream.write(base_exports.eraseLines(previousLineCount));
38624
38690
  previousOutput = "";
38625
38691
  previousLineCount = 0;
38626
38692
  };
38627
- render3.done = () => {
38693
+ render4.done = () => {
38628
38694
  previousOutput = "";
38629
38695
  previousLineCount = 0;
38630
38696
  if (!showCursor) {
@@ -38632,7 +38698,7 @@ var init_log_update = __esm({
38632
38698
  hasHiddenCursor = false;
38633
38699
  }
38634
38700
  };
38635
- return render3;
38701
+ return render4;
38636
38702
  };
38637
38703
  logUpdate = { create };
38638
38704
  log_update_default = logUpdate;
@@ -39765,12 +39831,12 @@ var init_ink = __esm({
39765
39831
  // ../../node_modules/ink/build/render.js
39766
39832
  import { Stream } from "node:stream";
39767
39833
  import process12 from "node:process";
39768
- var render, render_default, getOptions, getInstance;
39834
+ var render2, render_default, getOptions, getInstance;
39769
39835
  var init_render = __esm({
39770
39836
  async "../../node_modules/ink/build/render.js"() {
39771
39837
  await init_ink();
39772
39838
  init_instances();
39773
- render = (node, options) => {
39839
+ render2 = (node, options) => {
39774
39840
  const inkOptions = {
39775
39841
  stdout: process12.stdout,
39776
39842
  stdin: process12.stdin,
@@ -39792,7 +39858,7 @@ var init_render = __esm({
39792
39858
  clear: instance.clear
39793
39859
  };
39794
39860
  };
39795
- render_default = render;
39861
+ render_default = render2;
39796
39862
  getOptions = (stdout = {}) => {
39797
39863
  if (stdout instanceof Stream) {
39798
39864
  return {
@@ -39815,7 +39881,7 @@ var init_render = __esm({
39815
39881
 
39816
39882
  // ../../node_modules/ink/build/components/Static.js
39817
39883
  function Static(props) {
39818
- const { items, children: render3, style: customStyle } = props;
39884
+ const { items, children: render4, style: customStyle } = props;
39819
39885
  const [index, setIndex] = (0, import_react11.useState)(0);
39820
39886
  const itemsToRender = (0, import_react11.useMemo)(() => {
39821
39887
  return items.slice(index);
@@ -39824,7 +39890,7 @@ function Static(props) {
39824
39890
  setIndex(items.length);
39825
39891
  }, [items.length]);
39826
39892
  const children = itemsToRender.map((item, itemIndex) => {
39827
- return render3(item, index + itemIndex);
39893
+ return render4(item, index + itemIndex);
39828
39894
  });
39829
39895
  const style = (0, import_react11.useMemo)(() => ({
39830
39896
  position: "absolute",
@@ -44010,7 +44076,7 @@ var require_core = __commonJS({
44010
44076
  return match && match.index === 0;
44011
44077
  }
44012
44078
  var BACKREF_RE = /\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;
44013
- function join12(regexps, separator = "|") {
44079
+ function join13(regexps, separator = "|") {
44014
44080
  let numCaptures = 0;
44015
44081
  return regexps.map((regex2) => {
44016
44082
  numCaptures += 1;
@@ -44314,7 +44380,7 @@ var require_core = __commonJS({
44314
44380
  this.exec = () => null;
44315
44381
  }
44316
44382
  const terminators = this.regexes.map((el) => el[1]);
44317
- this.matcherRe = langRe(join12(terminators), true);
44383
+ this.matcherRe = langRe(join13(terminators), true);
44318
44384
  this.lastIndex = 0;
44319
44385
  }
44320
44386
  /** @param {string} s */
@@ -44609,7 +44675,7 @@ var require_core = __commonJS({
44609
44675
  function close(node) {
44610
44676
  result += "</" + tag(node) + ">";
44611
44677
  }
44612
- function render3(event) {
44678
+ function render4(event) {
44613
44679
  (event.event === "start" ? open3 : close)(event.node);
44614
44680
  }
44615
44681
  while (original.length || highlighted.length) {
@@ -44619,7 +44685,7 @@ var require_core = __commonJS({
44619
44685
  if (stream === original) {
44620
44686
  nodeStack.reverse().forEach(close);
44621
44687
  do {
44622
- render3(stream.splice(0, 1)[0]);
44688
+ render4(stream.splice(0, 1)[0]);
44623
44689
  stream = selectStream();
44624
44690
  } while (stream === original && stream.length && stream[0].offset === processed);
44625
44691
  nodeStack.reverse().forEach(open3);
@@ -44629,7 +44695,7 @@ var require_core = __commonJS({
44629
44695
  } else {
44630
44696
  nodeStack.pop();
44631
44697
  }
44632
- render3(stream.splice(0, 1)[0]);
44698
+ render4(stream.splice(0, 1)[0]);
44633
44699
  }
44634
44700
  }
44635
44701
  return result + escapeHTML(value.substr(processed));
@@ -52094,7 +52160,7 @@ var require_markdown = __commonJS({
52094
52160
  BOLD.contains = BOLD.contains.concat(CONTAINABLE);
52095
52161
  ITALIC.contains = ITALIC.contains.concat(CONTAINABLE);
52096
52162
  CONTAINABLE = CONTAINABLE.concat(BOLD, ITALIC);
52097
- const HEADER = {
52163
+ const HEADER2 = {
52098
52164
  className: "section",
52099
52165
  variants: [
52100
52166
  {
@@ -52131,7 +52197,7 @@ var require_markdown = __commonJS({
52131
52197
  "mkd"
52132
52198
  ],
52133
52199
  contains: [
52134
- HEADER,
52200
+ HEADER2,
52135
52201
  INLINE_HTML,
52136
52202
  LIST,
52137
52203
  BOLD,
@@ -56178,7 +56244,7 @@ var require_http = __commonJS({
56178
56244
  function http(hljs) {
56179
56245
  const VERSION = "HTTP/(2|1\\.[01])";
56180
56246
  const HEADER_NAME = /[A-Za-z][A-Za-z0-9-]*/;
56181
- const HEADER = {
56247
+ const HEADER2 = {
56182
56248
  className: "attribute",
56183
56249
  begin: concat("^", HEADER_NAME, "(?=\\:\\s)"),
56184
56250
  starts: {
@@ -56196,7 +56262,7 @@ var require_http = __commonJS({
56196
56262
  }
56197
56263
  };
56198
56264
  const HEADERS_AND_BODY = [
56199
- HEADER,
56265
+ HEADER2,
56200
56266
  {
56201
56267
  begin: "\\n\\n",
56202
56268
  starts: { subLanguage: [], endsWithParent: true }
@@ -56255,7 +56321,7 @@ var require_http = __commonJS({
56255
56321
  }
56256
56322
  },
56257
56323
  // to allow headers to work even without a preamble
56258
- hljs.inherit(HEADER, {
56324
+ hljs.inherit(HEADER2, {
56259
56325
  relevance: 0
56260
56326
  })
56261
56327
  ]
@@ -99376,8 +99442,8 @@ function markedTerminal(options, highlightOptions) {
99376
99442
  );
99377
99443
  }
99378
99444
  function reflowText(text, width, gfm) {
99379
- var splitRe = gfm ? HARD_RETURN_GFM_RE : HARD_RETURN_RE, sections = text.split(splitRe), reflowed = [];
99380
- sections.forEach(function(section2) {
99445
+ var splitRe = gfm ? HARD_RETURN_GFM_RE : HARD_RETURN_RE, sections2 = text.split(splitRe), reflowed = [];
99446
+ sections2.forEach(function(section2) {
99381
99447
  var fragments = section2.split(/(\u001b\[(?:\d{1,3})(?:;\d{1,3})*m)/g);
99382
99448
  var column = 0;
99383
99449
  var currentLine = "";
@@ -103628,6 +103694,144 @@ var init_local_toolset = __esm({
103628
103694
  }
103629
103695
  });
103630
103696
 
103697
+ // src/project-card-scan.ts
103698
+ import { execFile as execFile3 } from "node:child_process";
103699
+ import { readdir as readdir3, readFile as readFile4 } from "node:fs/promises";
103700
+ import { join as join7 } from "node:path";
103701
+ function firstHeading(md) {
103702
+ for (const line of md.split("\n", 40)) {
103703
+ const m2 = /^#{1,3}\s+(.+?)\s*$/.exec(line);
103704
+ if (m2) return m2[1];
103705
+ }
103706
+ return null;
103707
+ }
103708
+ function backlogLines(todo) {
103709
+ const out = [];
103710
+ for (const raw of todo.split("\n")) {
103711
+ const line = raw.trim();
103712
+ if (!line || /^[|\s:-]+$/.test(line) || /^\|\s*(area|task|priority)\b/i.test(line)) continue;
103713
+ const row = /^\|\s*([^|]+?)\s*\|/.exec(line);
103714
+ const bullet = /^[-*]\s+(?:\[[ x]\]\s*)?(.+)$/.exec(line);
103715
+ const text = row?.[1] ?? bullet?.[1];
103716
+ if (text && text.length > 8) out.push(text);
103717
+ if (out.length >= MAX_BACKLOG) break;
103718
+ }
103719
+ return out;
103720
+ }
103721
+ async function scanProject(cwd2, io = nodeScanIo) {
103722
+ const problems = [];
103723
+ const attempt = async (label, fn, fallback) => {
103724
+ try {
103725
+ return await fn();
103726
+ } catch (err) {
103727
+ problems.push(`${label}: ${err instanceof Error ? err.message : String(err)}`);
103728
+ return fallback;
103729
+ }
103730
+ };
103731
+ const root = await attempt("list root", () => io.readdir(cwd2), []);
103732
+ const topLevel = root.filter((e) => !SKIP.has(e.name)).map((e) => ({ path: e.name, isDir: e.isDir }));
103733
+ const childDirs = {};
103734
+ for (const d of topLevel.filter((e) => e.isDir && ["apps", "packages", "services", "libs", "src"].includes(e.path))) {
103735
+ const kids = await attempt(`list ${d.path}`, () => io.readdir(join7(cwd2, d.path)), []);
103736
+ childDirs[d.path] = kids.filter((k2) => !SKIP.has(k2.name) && !k2.name.startsWith(".")).map((k2) => k2.isDir ? k2.name : k2.name).slice(0, 20);
103737
+ }
103738
+ let manifest;
103739
+ const pkgText = await attempt("read package.json", () => io.readFile(join7(cwd2, "package.json")), null);
103740
+ if (pkgText) {
103741
+ try {
103742
+ const pkg = JSON.parse(pkgText);
103743
+ manifest = {
103744
+ ...pkg.name ? { name: pkg.name } : {},
103745
+ ...pkg.scripts ? { scripts: pkg.scripts } : {},
103746
+ ...Array.isArray(pkg.workspaces) ? { workspaces: pkg.workspaces } : pkg.workspaces?.packages ? { workspaces: pkg.workspaces.packages } : {}
103747
+ };
103748
+ } catch (err) {
103749
+ problems.push(`parse package.json: ${err instanceof Error ? err.message : String(err)}`);
103750
+ }
103751
+ }
103752
+ const docs = [];
103753
+ for (const e of topLevel.filter((t) => !t.isDir && /\.md$/i.test(t.path)).slice(0, MAX_DOCS)) {
103754
+ const text = await attempt(`read ${e.path}`, () => io.readFile(join7(cwd2, e.path)), null);
103755
+ const title = text ? firstHeading(text) : null;
103756
+ if (title) docs.push({ path: e.path, title });
103757
+ }
103758
+ const todo = await attempt("read TODO.md", () => io.readFile(join7(cwd2, "TODO.md")), null);
103759
+ const log = await io.git(cwd2, ["log", "--oneline", "-8"]);
103760
+ const status = await io.git(cwd2, ["status", "--porcelain"]);
103761
+ const branch = await io.git(cwd2, ["rev-parse", "--abbrev-ref", "HEAD"]);
103762
+ return {
103763
+ problems,
103764
+ input: {
103765
+ cwd: cwd2,
103766
+ ...branch?.trim() ? { branch: branch.trim() } : {},
103767
+ ...status !== null ? { dirtyFiles: status.split("\n").filter(Boolean).length } : {},
103768
+ ...manifest ? { manifest } : {},
103769
+ topLevel,
103770
+ childDirs,
103771
+ recentCommits: (log ?? "").split("\n").filter(Boolean),
103772
+ docs,
103773
+ backlog: todo ? backlogLines(todo) : []
103774
+ }
103775
+ };
103776
+ }
103777
+ var SKIP, MAX_DOCS, MAX_BACKLOG, nodeScanIo;
103778
+ var init_project_card_scan = __esm({
103779
+ "src/project-card-scan.ts"() {
103780
+ "use strict";
103781
+ SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", ".wrangler", ".turbo", "coverage", ".worktrees", ".superpowers", ".remember"]);
103782
+ MAX_DOCS = 15;
103783
+ MAX_BACKLOG = 6;
103784
+ nodeScanIo = {
103785
+ async readdir(path12) {
103786
+ const entries = await readdir3(path12, { withFileTypes: true });
103787
+ return entries.map((e) => ({ name: e.name, isDir: e.isDirectory() }));
103788
+ },
103789
+ async readFile(path12) {
103790
+ try {
103791
+ return await readFile4(path12, "utf8");
103792
+ } catch (err) {
103793
+ if (err.code === "ENOENT") return null;
103794
+ throw err;
103795
+ }
103796
+ },
103797
+ git: (cwd2, args) => new Promise((resolve2) => {
103798
+ execFile3("git", args, { cwd: cwd2, timeout: 3e3, maxBuffer: 256 * 1024 }, (err, stdout) => resolve2(err ? null : stdout));
103799
+ })
103800
+ };
103801
+ }
103802
+ });
103803
+
103804
+ // src/project-card-runtime.ts
103805
+ async function loadProjectCard(cwd2, deps = {}) {
103806
+ const env3 = deps.env ?? process.env;
103807
+ const now = deps.now ?? Date.now;
103808
+ if (env3.SPACECODE_PROJECT_CARD === "off") return { text: "", statusLine: "project card: disabled (SPACECODE_PROJECT_CARD=off)" };
103809
+ if (cached && cached.cwd === cwd2 && now() - cached.at < TTL_MS) return cached.state;
103810
+ let state;
103811
+ try {
103812
+ const { input, problems } = await scanProject(cwd2, deps.io ?? nodeScanIo);
103813
+ const text = renderProjectCard(input);
103814
+ state = {
103815
+ text,
103816
+ statusLine: `project card: ${text.length} chars, ${input.topLevel.length} top-level entries${problems.length ? `, ${problems.length} scan problem(s): ${problems.join("; ")}` : ""}`
103817
+ };
103818
+ } catch (err) {
103819
+ console.error("[project-card] scan failed; the REPL runs without a card", err instanceof Error ? err.message : String(err));
103820
+ state = { text: "", statusLine: `project card: not built (${err instanceof Error ? err.message : String(err)})` };
103821
+ }
103822
+ cached = { cwd: cwd2, at: now(), state };
103823
+ return state;
103824
+ }
103825
+ var TTL_MS, cached;
103826
+ var init_project_card_runtime = __esm({
103827
+ "src/project-card-runtime.ts"() {
103828
+ "use strict";
103829
+ init_src();
103830
+ init_project_card_scan();
103831
+ TTL_MS = 5 * 6e4;
103832
+ }
103833
+ });
103834
+
103631
103835
  // src/shell-turn.ts
103632
103836
  function shellCommandOf(prompt) {
103633
103837
  return prompt.startsWith("!") ? prompt.slice(1).trim() : null;
@@ -103922,11 +104126,11 @@ var init_node_mention_fs = __esm({
103922
104126
  });
103923
104127
 
103924
104128
  // src/editor/node-read-dir.ts
103925
- import { readdir as readdir3 } from "node:fs/promises";
104129
+ import { readdir as readdir4 } from "node:fs/promises";
103926
104130
  import path7 from "node:path";
103927
104131
  function createNodeReadDir(cwd2) {
103928
104132
  return async (rel) => {
103929
- const entries = await readdir3(path7.join(cwd2, rel), { withFileTypes: true });
104133
+ const entries = await readdir4(path7.join(cwd2, rel), { withFileTypes: true });
103930
104134
  return entries.map((e) => ({ name: e.name, isFile: e.isFile(), isDirectory: e.isDirectory() }));
103931
104135
  };
103932
104136
  }
@@ -103937,11 +104141,11 @@ var init_node_read_dir = __esm({
103937
104141
  });
103938
104142
 
103939
104143
  // src/editor/node-mentions.ts
103940
- import { readFile as readFile4 } from "node:fs/promises";
104144
+ import { readFile as readFile5 } from "node:fs/promises";
103941
104145
  import path8 from "node:path";
103942
104146
  async function readGitignore(cwd2) {
103943
104147
  try {
103944
- return createGitignoreMatcher(await readFile4(path8.join(cwd2, ".gitignore"), "utf8"));
104148
+ return createGitignoreMatcher(await readFile5(path8.join(cwd2, ".gitignore"), "utf8"));
103945
104149
  } catch (err) {
103946
104150
  if (err.code === "ENOENT") return () => false;
103947
104151
  throw err;
@@ -104051,8 +104255,8 @@ var init_recording_chunks = __esm({
104051
104255
 
104052
104256
  // src/recording/recorder.ts
104053
104257
  import { createHash as createHash4 } from "node:crypto";
104054
- import { mkdir as mkdir4, readFile as readFile5, rename as rename4, rm as rm2, writeFile as writeFile4, readdir as readdir4, stat as stat3 } from "node:fs/promises";
104055
- import { join as join7 } from "node:path";
104258
+ import { mkdir as mkdir4, readFile as readFile6, rename as rename4, rm as rm2, writeFile as writeFile4, readdir as readdir5, stat as stat3 } from "node:fs/promises";
104259
+ import { join as join8 } from "node:path";
104056
104260
  function projectHash(root) {
104057
104261
  return createHash4("sha256").update(root).digest("hex");
104058
104262
  }
@@ -104063,7 +104267,7 @@ function createRecorder(deps) {
104063
104267
  let inflight = null;
104064
104268
  let retryAttempt = 0;
104065
104269
  let timerArmed = false;
104066
- const stateFile = (sessionId) => join7(deps.dir, `${sessionId}.json`);
104270
+ const stateFile = (sessionId) => join8(deps.dir, `${sessionId}.json`);
104067
104271
  async function save(sessionId, st) {
104068
104272
  await mkdir4(deps.dir, { recursive: true, mode: 448 });
104069
104273
  const tmp = `${stateFile(sessionId)}.${deps.randomId()}.tmp`;
@@ -104071,16 +104275,16 @@ function createRecorder(deps) {
104071
104275
  await rename4(tmp, stateFile(sessionId));
104072
104276
  }
104073
104277
  async function load(sessionId) {
104074
- const cached = states.get(sessionId);
104075
- if (cached) return cached;
104278
+ const cached2 = states.get(sessionId);
104279
+ if (cached2) return cached2;
104076
104280
  let st;
104077
104281
  try {
104078
- st = JSON.parse(await readFile5(stateFile(sessionId), "utf8"));
104282
+ st = JSON.parse(await readFile6(stateFile(sessionId), "utf8"));
104079
104283
  } catch (e) {
104080
104284
  if (e.code !== "ENOENT") {
104081
- const bad = join7(deps.dir, "bad");
104285
+ const bad = join8(deps.dir, "bad");
104082
104286
  await mkdir4(bad, { recursive: true, mode: 448 });
104083
- await rename4(stateFile(sessionId), join7(bad, `${sessionId}.${deps.now()}.json`)).catch((err) => deps.log(`recording: could not quarantine ${sessionId}: ${String(err)}`));
104287
+ await rename4(stateFile(sessionId), join8(bad, `${sessionId}.${deps.now()}.json`)).catch((err) => deps.log(`recording: could not quarantine ${sessionId}: ${String(err)}`));
104084
104288
  lastError = `recording state for ${sessionId} was unreadable and was quarantined`;
104085
104289
  deps.log(`recording: ${lastError}`);
104086
104290
  }
@@ -104242,7 +104446,7 @@ function createRecorder(deps) {
104242
104446
  async function resume() {
104243
104447
  let names;
104244
104448
  try {
104245
- names = await readdir4(deps.dir);
104449
+ names = await readdir5(deps.dir);
104246
104450
  } catch (e) {
104247
104451
  if (e.code === "ENOENT") return;
104248
104452
  throw e;
@@ -104270,8 +104474,8 @@ var init_recorder = __esm({
104270
104474
  });
104271
104475
 
104272
104476
  // src/recording/recording-runtime.ts
104273
- import { mkdir as mkdir5, readFile as readFile6, writeFile as writeFile5, stat as stat4 } from "node:fs/promises";
104274
- import { join as join8, sep as sep2 } from "node:path";
104477
+ import { mkdir as mkdir5, readFile as readFile7, writeFile as writeFile5, stat as stat4 } from "node:fs/promises";
104478
+ import { join as join9, sep as sep2 } from "node:path";
104275
104479
  function matchesExclude(pattern, root) {
104276
104480
  const re3 = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "\0").replace(/\*/g, `[^${sep2 === "\\" ? "\\\\" : "/"}]*`).replace(/\u0000/g, ".*");
104277
104481
  return new RegExp(`^${re3}(?:[/\\\\].*)?$`).test(root);
@@ -104286,8 +104490,8 @@ async function exists(path12) {
104286
104490
  }
104287
104491
  }
104288
104492
  function createRecordingRuntime(deps) {
104289
- const dir = join8(deps.configDir, "recordings");
104290
- const noticeFile = join8(dir, ".notice");
104493
+ const dir = join9(deps.configDir, "recordings");
104494
+ const noticeFile = join9(dir, ".notice");
104291
104495
  let recorder = null;
104292
104496
  let standingOff;
104293
104497
  let sessionOff = false;
@@ -104295,7 +104499,7 @@ function createRecordingRuntime(deps) {
104295
104499
  if (deps.env.SPACECODE_RECORD?.trim().toLowerCase() === "off") return "SPACECODE_RECORD=off";
104296
104500
  let cfg = {};
104297
104501
  try {
104298
- cfg = JSON.parse(await readFile6(join8(deps.configDir, "config.json"), "utf8"));
104502
+ cfg = JSON.parse(await readFile7(join9(deps.configDir, "config.json"), "utf8"));
104299
104503
  } catch (e) {
104300
104504
  if (e.code !== "ENOENT") return `~/.spacecode/config.json is unreadable (${e instanceof Error ? e.message : String(e)}); fix it to record`;
104301
104505
  }
@@ -104303,7 +104507,7 @@ function createRecordingRuntime(deps) {
104303
104507
  const excludes = Array.isArray(cfg.recording?.exclude) ? cfg.recording.exclude.filter((x2) => typeof x2 === "string") : [];
104304
104508
  const hit = excludes.find((p) => matchesExclude(p, deps.projectRoot));
104305
104509
  if (hit) return `this folder matches recording.exclude (${hit})`;
104306
- if (await exists(join8(deps.projectRoot, ".spacecode", "norecord"))) return "this repo has .spacecode/norecord";
104510
+ if (await exists(join9(deps.projectRoot, ".spacecode", "norecord"))) return "this repo has .spacecode/norecord";
104307
104511
  return null;
104308
104512
  }
104309
104513
  async function noticeShown() {
@@ -104444,11 +104648,11 @@ var init_skills_command = __esm({
104444
104648
  });
104445
104649
 
104446
104650
  // src/permission-rules-loader.ts
104447
- import { readFile as readFile7 } from "node:fs/promises";
104448
- import { join as join9 } from "node:path";
104651
+ import { readFile as readFile8 } from "node:fs/promises";
104652
+ import { join as join10 } from "node:path";
104449
104653
  async function readOrNull2(path12) {
104450
104654
  try {
104451
- return await readFile7(path12, "utf8");
104655
+ return await readFile8(path12, "utf8");
104452
104656
  } catch (err) {
104453
104657
  if (err.code === "ENOENT") return null;
104454
104658
  throw err;
@@ -104456,7 +104660,7 @@ async function readOrNull2(path12) {
104456
104660
  }
104457
104661
  async function loadPermissionRules(deps) {
104458
104662
  const none = (line) => ({ rules: void 0, defaultMode: void 0, statusLines: [line] });
104459
- const configPath = join9(deps.configDir, "imports.json");
104663
+ const configPath = join10(deps.configDir, "imports.json");
104460
104664
  try {
104461
104665
  const loaded = parseImportsConfig(await readOrNull2(configPath), configPath);
104462
104666
  if (loaded.error) return none("permission rules: off (imports.json unreadable)");
@@ -104465,13 +104669,13 @@ async function loadPermissionRules(deps) {
104465
104669
  return none(`permission rules: off (imports.json not read: ${err instanceof Error ? err.message : String(err)})`);
104466
104670
  }
104467
104671
  const lines2 = [];
104468
- const files = [{ scope: "user", path: join9(deps.homeDir, ".claude", "settings.json") }];
104672
+ const files = [{ scope: "user", path: join10(deps.homeDir, ".claude", "settings.json") }];
104469
104673
  try {
104470
104674
  const sources2 = await readRepoSources(deps.cwd);
104471
104675
  if (sources2[".claude/settings.json"] !== void 0 || sources2[".claude/settings.local.json"] !== void 0) {
104472
104676
  const status = await createNodeTrustStore(deps.configDir).repoStatus(deps.cwd, sources2);
104473
104677
  if (status === "trusted" || status === "session") {
104474
- files.push({ scope: "repo", path: join9(deps.cwd, ".claude", "settings.json") }, { scope: "local", path: join9(deps.cwd, ".claude", "settings.local.json") });
104678
+ files.push({ scope: "repo", path: join10(deps.cwd, ".claude", "settings.json") }, { scope: "local", path: join10(deps.cwd, ".claude", "settings.local.json") });
104475
104679
  } else lines2.push(`permission rules: repo settings NOT loaded (${status === "never" ? "marked never trust" : "untrusted repo"})`);
104476
104680
  }
104477
104681
  } catch (err) {
@@ -104513,7 +104717,7 @@ var init_permission_rules_loader = __esm({
104513
104717
  });
104514
104718
 
104515
104719
  // src/command-loader.ts
104516
- import { join as join10 } from "node:path";
104720
+ import { join as join11 } from "node:path";
104517
104721
  async function userCommands(deps, lines2) {
104518
104722
  try {
104519
104723
  const apiKey = await resolveApiKey({ env: deps.env, readFile: deps.readFile, homedir: deps.homedir });
@@ -104568,7 +104772,7 @@ async function repoCommands(deps, lines2) {
104568
104772
  }
104569
104773
  }
104570
104774
  async function loadCommands(deps) {
104571
- const configPath = join10(deps.configDir, "imports.json");
104775
+ const configPath = join11(deps.configDir, "imports.json");
104572
104776
  try {
104573
104777
  const loaded = parseImportsConfig(await deps.readFile(configPath).catch((err) => {
104574
104778
  if (err.code === "ENOENT") return null;
@@ -104601,10 +104805,10 @@ var init_command_loader = __esm({
104601
104805
  });
104602
104806
 
104603
104807
  // src/useReplController.ts
104604
- import { readFile as readFile8, writeFile as writeFile6, mkdir as mkdir6 } from "node:fs/promises";
104808
+ import { readFile as readFile9, writeFile as writeFile6, mkdir as mkdir6 } from "node:fs/promises";
104605
104809
  import { homedir as homedir2 } from "node:os";
104606
- import { join as join11 } from "node:path";
104607
- import { execFile as execFile3 } from "node:child_process";
104810
+ import { join as join12 } from "node:path";
104811
+ import { execFile as execFile4 } from "node:child_process";
104608
104812
  function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mentionsOpt, sessions, resumeLatest: resumeLatest2, onViewTurns, onViewResume, isEscOwned }) {
104609
104813
  const { exit } = use_app_default();
104610
104814
  const mentions = (0, import_react30.useMemo)(() => mentionsOpt ?? createNodeMentions(process.cwd()), [mentionsOpt]);
@@ -104634,11 +104838,11 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
104634
104838
  const recordingRef = (0, import_react30.useRef)(null);
104635
104839
  const recording = () => recordingRef.current ??= createRecordingRuntime({
104636
104840
  env: process.env,
104637
- configDir: process.env.SPACECODE_CONFIG_DIR?.trim() || join11(homedir2(), ".spacecode"),
104841
+ configDir: process.env.SPACECODE_CONFIG_DIR?.trim() || join12(homedir2(), ".spacecode"),
104638
104842
  projectRoot,
104639
104843
  fetch,
104640
104844
  baseUrl: backendUrl(process.env),
104641
- resolveApiKey: () => resolveApiKey({ env: process.env, readFile: (p) => readFile8(p, "utf-8"), homedir: homedir2 }).catch(() => null),
104845
+ resolveApiKey: () => resolveApiKey({ env: process.env, readFile: (p) => readFile9(p, "utf-8"), homedir: homedir2 }).catch(() => null),
104642
104846
  log: (m2) => console.error(`[agent-ui] ${m2}`)
104643
104847
  });
104644
104848
  const resumeListRef = (0, import_react30.useRef)([]);
@@ -104661,10 +104865,10 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
104661
104865
  (0, import_react30.useEffect)(() => {
104662
104866
  let live2 = true;
104663
104867
  loadCommands({
104664
- configDir: process.env.SPACECODE_CONFIG_DIR?.trim() || join11(homedir2(), ".spacecode"),
104868
+ configDir: process.env.SPACECODE_CONFIG_DIR?.trim() || join12(homedir2(), ".spacecode"),
104665
104869
  cwd: process.cwd(),
104666
104870
  env: process.env,
104667
- readFile: (p) => readFile8(p, "utf-8"),
104871
+ readFile: (p) => readFile9(p, "utf-8"),
104668
104872
  homedir: homedir2,
104669
104873
  fetch,
104670
104874
  builtins: SLASH_COMMANDS.map((c) => c.name)
@@ -104952,7 +105156,7 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
104952
105156
  }
104953
105157
  setIsRunning(true);
104954
105158
  try {
104955
- const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) => readFile8(p, "utf-8"), homedir: homedir2 });
105159
+ const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) => readFile9(p, "utf-8"), homedir: homedir2 });
104956
105160
  if (!apiKey2) {
104957
105161
  notice(MISSING_KEY_MESSAGE);
104958
105162
  return;
@@ -104974,7 +105178,7 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
104974
105178
  }
104975
105179
  case "model": {
104976
105180
  try {
104977
- const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) => readFile8(p, "utf-8"), homedir: homedir2 });
105181
+ const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) => readFile9(p, "utf-8"), homedir: homedir2 });
104978
105182
  if (!apiKey2) {
104979
105183
  notice(MISSING_KEY_MESSAGE);
104980
105184
  return;
@@ -105009,7 +105213,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105009
105213
  }
105010
105214
  case "skills": {
105011
105215
  const state = profileRef.current;
105012
- const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) => readFile8(p, "utf-8"), homedir: homedir2 }).catch(() => null);
105216
+ const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) => readFile9(p, "utf-8"), homedir: homedir2 }).catch(() => null);
105013
105217
  const client = apiKey2 ? createProfileClient({ baseUrl: backendUrl(process.env), apiKey: apiKey2, fetch }) : null;
105014
105218
  notice(await runSkillsCommand(slash.args, { source: state?.skills ?? null, client, statusLines: state?.statusLines.filter((l) => l.startsWith("skills")) ?? ["skills: not loaded yet (loads on your first message)"] }));
105015
105219
  return;
@@ -105031,7 +105235,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105031
105235
  try {
105032
105236
  apiKey = await resolveApiKey({
105033
105237
  env: process.env,
105034
- readFile: (p) => readFile8(p, "utf-8"),
105238
+ readFile: (p) => readFile9(p, "utf-8"),
105035
105239
  homedir: homedir2
105036
105240
  });
105037
105241
  } catch (err) {
@@ -105068,7 +105272,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105068
105272
  try {
105069
105273
  profilePromiseRef.current ??= prepareProfile({
105070
105274
  env: process.env,
105071
- readFile: (p) => readFile8(p, "utf-8"),
105275
+ readFile: (p) => readFile9(p, "utf-8"),
105072
105276
  writeFile: (p, d) => writeFile6(p, d, { encoding: "utf-8", mode: 384 }),
105073
105277
  mkdir: async (p) => {
105074
105278
  await mkdir6(p, { recursive: true });
@@ -105078,7 +105282,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105078
105282
  cwd: process.cwd(),
105079
105283
  gitRoot: gitTopLevel,
105080
105284
  importGate: async () => {
105081
- const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join11(homedir2(), ".spacecode");
105285
+ const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join12(homedir2(), ".spacecode");
105082
105286
  const gate = await importGateStatus({ configDir, cwd: process.cwd(), interactive: Boolean(process.stdin.isTTY) });
105083
105287
  const loaded = await loadPermissionRules({ configDir, homeDir: homedir2(), cwd: process.cwd() });
105084
105288
  rulesRef.current = loaded.rules;
@@ -105088,7 +105292,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105088
105292
  });
105089
105293
  profileRef.current = await profilePromiseRef.current;
105090
105294
  if (mcpRef.current === null && (profileRef.current?.mcpDefs?.length ?? 0) > 0) {
105091
- const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join11(homedir2(), ".spacecode");
105295
+ const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join12(homedir2(), ".spacecode");
105092
105296
  const trust = createNodeTrustStore(configDir);
105093
105297
  const manager = new McpManager({
105094
105298
  defs: profileRef.current.mcpDefs,
@@ -105098,7 +105302,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105098
105302
  ...rulesRef.current ? { rules: rulesRef.current } : {},
105099
105303
  permissions,
105100
105304
  confirm: askUser,
105101
- audit: new FileAuditLog(join11(homedir2(), ".spacecode", "audit.jsonl")),
105305
+ audit: new FileAuditLog(join12(homedir2(), ".spacecode", "audit.jsonl")),
105102
105306
  clientVersion: process.env.SPACECODE_VERSION ?? "0"
105103
105307
  });
105104
105308
  mcpRef.current = manager;
@@ -105106,6 +105310,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105106
105310
  }
105107
105311
  const toolset = createToolset();
105108
105312
  toolsetRef.current = toolset;
105313
+ const projectCard = await loadProjectCard(projectRoot);
105109
105314
  const config = {
105110
105315
  apiUrl: apiUrl(),
105111
105316
  apiKey,
@@ -105114,7 +105319,8 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105114
105319
  repoName: repoName2,
105115
105320
  localCwd: projectRoot,
105116
105321
  commitGuidance: true,
105117
- hasGitHubTools: toolset.hasGitHubTools
105322
+ hasGitHubTools: toolset.hasGitHubTools,
105323
+ ...projectCard.text ? { projectCard: projectCard.text } : {}
105118
105324
  }) + (profileRef.current?.promptSection ?? ""),
105119
105325
  tools: toolset.tools,
105120
105326
  maxTurns: 100,
@@ -105225,6 +105431,7 @@ var init_useReplController = __esm({
105225
105431
  init_models();
105226
105432
  init_session_commands();
105227
105433
  init_local_toolset();
105434
+ init_project_card_runtime();
105228
105435
  init_shell_turn();
105229
105436
  init_node_mentions();
105230
105437
  init_api_key();
@@ -105239,7 +105446,7 @@ var init_useReplController = __esm({
105239
105446
  init_slash_commands();
105240
105447
  init_useEventAccumulator();
105241
105448
  gitTopLevel = () => new Promise((resolve2) => {
105242
- execFile3("git", ["rev-parse", "--show-toplevel"], { cwd: process.cwd() }, (err, stdout) => resolve2(err ? null : stdout.trim() || null));
105449
+ execFile4("git", ["rev-parse", "--show-toplevel"], { cwd: process.cwd() }, (err, stdout) => resolve2(err ? null : stdout.trim() || null));
105243
105450
  });
105244
105451
  apiUrl = () => process.env.API_PROXY_URL || "https://api.spacecode.app";
105245
105452
  }
@@ -105820,16 +106027,16 @@ function clearOnResize(stdout) {
105820
106027
  var import_jsx_runtime14 = __toESM(require_jsx_runtime(), 1);
105821
106028
  if (process.argv[2] === "import-claude") {
105822
106029
  const { runImportClaude: runImportClaude2 } = await Promise.resolve().then(() => (init_import_claude(), import_claude_exports));
105823
- const { readFile: readFile9, readdir: readdir5 } = await import("node:fs/promises");
105824
- const { execFile: execFile4 } = await import("node:child_process");
106030
+ const { readFile: readFile10, readdir: readdir6 } = await import("node:fs/promises");
106031
+ const { execFile: execFile5 } = await import("node:child_process");
105825
106032
  const gitRoot = () => new Promise((resolve2) => {
105826
- execFile4("git", ["rev-parse", "--show-toplevel"], { cwd: process.cwd() }, (err, stdout) => resolve2(err ? null : stdout.trim() || null));
106033
+ execFile5("git", ["rev-parse", "--show-toplevel"], { cwd: process.cwd() }, (err, stdout) => resolve2(err ? null : stdout.trim() || null));
105827
106034
  });
105828
106035
  const code = await runImportClaude2({
105829
106036
  argv: process.argv.slice(2),
105830
106037
  env: process.env,
105831
- fs: { readFile: (p) => readFile9(p, "utf-8"), readdir: (p) => readdir5(p) },
105832
- readFile: (p) => readFile9(p, "utf-8"),
106038
+ fs: { readFile: (p) => readFile10(p, "utf-8"), readdir: (p) => readdir6(p) },
106039
+ readFile: (p) => readFile10(p, "utf-8"),
105833
106040
  homedir: os5.homedir,
105834
106041
  cwd: process.cwd(),
105835
106042
  gitRoot,
@@ -105854,9 +106061,9 @@ if (process.argv[2] === "approve-mcp") {
105854
106061
  const { resolveApiKey: resolveApiKey2 } = await Promise.resolve().then(() => (init_api_key(), api_key_exports));
105855
106062
  const { backendUrl: backendUrl2 } = await Promise.resolve().then(() => (init_profile_runtime(), profile_runtime_exports));
105856
106063
  const { createProfileClient: createProfileClient2 } = await Promise.resolve().then(() => (init_src(), src_exports));
105857
- const { readFile: readFile9 } = await import("node:fs/promises");
106064
+ const { readFile: readFile10 } = await import("node:fs/promises");
105858
106065
  const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || path11.join(os5.homedir(), ".spacecode");
105859
- const apiKey = await resolveApiKey2({ env: process.env, readFile: (p) => readFile9(p, "utf-8"), homedir: os5.homedir }).catch(() => null);
106066
+ const apiKey = await resolveApiKey2({ env: process.env, readFile: (p) => readFile10(p, "utf-8"), homedir: os5.homedir }).catch(() => null);
105860
106067
  const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
105861
106068
  const { createInterface } = interactive ? await import("node:readline/promises") : { createInterface: void 0 };
105862
106069
  const rl = createInterface ? createInterface({ input: process.stdin, output: process.stdout }) : void 0;
@@ -105864,7 +106071,7 @@ if (process.argv[2] === "approve-mcp") {
105864
106071
  const code = await runApproveMcp2({
105865
106072
  configDir,
105866
106073
  cwd: process.cwd(),
105867
- readFile: (p) => readFile9(p, "utf-8"),
106074
+ readFile: (p) => readFile10(p, "utf-8"),
105868
106075
  client: apiKey ? createProfileClient2({ baseUrl: backendUrl2(process.env), apiKey, fetch }) : null,
105869
106076
  trust: createNodeTrustStore2(configDir),
105870
106077
  ...rl ? { ask: async (q2) => {
@@ -105925,10 +106132,10 @@ if (process.stdin.isTTY && process.stdout.isTTY) {
105925
106132
  }
105926
106133
  }
105927
106134
  var restoreCiVariables = hideCiVariables(process.env, Boolean(process.stdout.isTTY));
105928
- var render2;
106135
+ var render3;
105929
106136
  var App3;
105930
106137
  try {
105931
- [{ render: render2 }, { App: App3 }] = await Promise.all([init_build2().then(() => build_exports), init_App2().then(() => App_exports)]);
106138
+ [{ render: render3 }, { App: App3 }] = await Promise.all([init_build2().then(() => build_exports), init_App2().then(() => App_exports)]);
105932
106139
  } finally {
105933
106140
  restoreCiVariables();
105934
106141
  }
@@ -105942,7 +106149,7 @@ if (fullscreen) {
105942
106149
  session.onLeave(restoreConsole);
105943
106150
  session.onLeave(clearOnResize(process.stdout));
105944
106151
  }
105945
- var app = render2(
106152
+ var app = render3(
105946
106153
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(App3, { repoName, onboardingMode, launchCount, yolo, resumeLatest, interactivePicker: Boolean(process.stdout.isTTY), fullscreen }),
105947
106154
  { patchConsole: !fullscreen }
105948
106155
  );