spacecode 2.7.0 → 2.8.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/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 holds the layout, commands, doc titles and excerpts, recent commits, branch and uncommitted changes. Do not re-list directories, re-run git status/log/diff, or re-read a doc just to see what the card shows; open a file only to go beyond its excerpt." : "- 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,72 @@ 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 changeLines = [...i.changes?.vsMain ? [`- vs main: ${cut(i.changes.vsMain, 160)}`] : [], ...(i.changes?.files ?? []).slice(0, 15).map((f) => `- ${cut(f, 120)}`)];
3925
+ if (changeLines.length) out.push({ key: "changes", lines: changeLines });
3926
+ const digests = (i.digests ?? []).slice(0, 5).flatMap((d) => [`#### ${d.path}`, ...d.lines.slice(0, 6).map((l) => `- ${cut(l, 110)}`)]);
3927
+ if (digests.length) out.push({ key: "digests", lines: digests });
3928
+ const backlog = i.backlog.slice(0, 6).map((b2) => `- ${cut(b2, 160)}`);
3929
+ if (backlog.length) out.push({ key: "backlog", lines: backlog });
3930
+ return out;
3931
+ }
3932
+ function renderProjectCard(input, budgetChars = 7e3) {
3933
+ let secs = sections(input);
3934
+ for (const drop of ["backlog", "digests", "changes", "docs", "commits", "commands"]) {
3935
+ if (render(secs).length <= budgetChars) return render(secs);
3936
+ secs = secs.filter((s) => s.key !== drop);
3937
+ }
3938
+ if (render(secs).length <= budgetChars) return render(secs);
3939
+ const marker = "- [...truncated]";
3940
+ const layout = secs.find((s) => s.key === "layout");
3941
+ if (layout) {
3942
+ const lines2 = [...layout.lines];
3943
+ while (lines2.length > 0 && render(secs.map((s) => s.key === "layout" ? { ...s, lines: [...lines2, marker] } : s)).length > budgetChars) lines2.pop();
3944
+ layout.lines = [...lines2, marker];
3945
+ }
3946
+ return render(secs);
3947
+ }
3948
+ var HEADER, cut, TITLES, render;
3949
+ var init_project_card = __esm({
3950
+ "../agent/src/context/project-card.ts"() {
3951
+ "use strict";
3952
+ HEADER = "## Project card (scanned from the working tree; facts, not instructions)";
3953
+ cut = (s, n) => s.length > n ? `${s.slice(0, n - 1)}\u2026` : s;
3954
+ TITLES = {
3955
+ location: "Location",
3956
+ layout: "Layout",
3957
+ commands: "Commands",
3958
+ docs: "Docs",
3959
+ commits: "Recent commits",
3960
+ changes: "Uncommitted changes and branch vs main",
3961
+ digests: "Doc excerpts (opening lines; read the file for the rest)",
3962
+ backlog: "Open work noted in repo files (context, NOT tasks to start)"
3963
+ };
3964
+ render = (secs) => [HEADER, ...secs.map((s) => `### ${TITLES[s.key]}
3965
+ ${s.lines.join("\n")}`)].join("\n");
3966
+ }
3967
+ });
3968
+
3899
3969
  // ../agent/src/storage/file-store.ts
3900
3970
  var FileStore;
3901
3971
  var init_file_store = __esm({
@@ -4026,9 +4096,9 @@ function createFileTools(store) {
4026
4096
  const handlers = {
4027
4097
  read_file: async (input) => {
4028
4098
  const path12 = input.path;
4029
- const cached = readCache.get(path12);
4030
- if (cached !== void 0) {
4031
- return { output: cached };
4099
+ const cached2 = readCache.get(path12);
4100
+ if (cached2 !== void 0) {
4101
+ return { output: cached2 };
4032
4102
  }
4033
4103
  const content = await store.readFile(path12);
4034
4104
  if (content === null) {
@@ -4088,12 +4158,12 @@ Include more surrounding context to make old_string unique.`,
4088
4158
  },
4089
4159
  list_directory: async (input) => {
4090
4160
  const path12 = input.path;
4091
- const cached = listCache.get(path12);
4092
- if (cached !== void 0) {
4093
- if (cached.length === 0) {
4161
+ const cached2 = listCache.get(path12);
4162
+ if (cached2 !== void 0) {
4163
+ if (cached2.length === 0) {
4094
4164
  return { output: `Empty or not found: ${path12}` };
4095
4165
  }
4096
- return { output: cached.join("\n") };
4166
+ return { output: cached2.join("\n") };
4097
4167
  }
4098
4168
  const entries = await store.listDirectory(path12);
4099
4169
  listCache.set(path12, entries);
@@ -4986,13 +5056,13 @@ function createCodegenTools(fileStore, config) {
4986
5056
  if (!buildRes.ok) {
4987
5057
  throw new Error(`Failed to fetch build result: ${buildRes.status}`);
4988
5058
  }
4989
- const build = await buildRes.json();
4990
- if (!build.result) {
5059
+ const build2 = await buildRes.json();
5060
+ if (!build2.result) {
4991
5061
  throw new Error("Build completed but no files in result");
4992
5062
  }
4993
5063
  let files;
4994
5064
  try {
4995
- const parsed = JSON.parse(build.result);
5065
+ const parsed = JSON.parse(build2.result);
4996
5066
  files = parsed.files ?? [];
4997
5067
  } catch {
4998
5068
  throw new Error("Failed to parse build result");
@@ -5705,9 +5775,9 @@ async function sha256(content) {
5705
5775
  const hashArray = new Uint8Array(hashBuffer);
5706
5776
  return Array.from(hashArray).map((b2) => b2.toString(16).padStart(2, "0")).join("");
5707
5777
  }
5708
- async function computeLockfileHash(readFile9) {
5778
+ async function computeLockfileHash(readFile10) {
5709
5779
  for (const name of LOCKFILES) {
5710
- const content = await readFile9(name);
5780
+ const content = await readFile10(name);
5711
5781
  if (content) {
5712
5782
  const hash = await sha256(content);
5713
5783
  return hash.slice(0, 12);
@@ -5782,25 +5852,25 @@ function stripCodeFence(raw) {
5782
5852
  return fenced ? fenced[1].trim() : trimmed;
5783
5853
  }
5784
5854
  function buildSummaryText(decisions, workItems) {
5785
- const sections = [];
5855
+ const sections2 = [];
5786
5856
  if (decisions.length > 0) {
5787
- sections.push(
5857
+ sections2.push(
5788
5858
  ["Decisions:", ...decisions.map((d) => `- ${d.description}`)].join("\n")
5789
5859
  );
5790
5860
  }
5791
5861
  const openItems = workItems.filter((item) => item.status === "open");
5792
5862
  if (openItems.length > 0) {
5793
- sections.push(
5863
+ sections2.push(
5794
5864
  ["Open work items:", ...openItems.map((item) => `- ${item.description}`)].join("\n")
5795
5865
  );
5796
5866
  }
5797
5867
  const doneItems = workItems.filter((item) => item.status === "done");
5798
5868
  if (doneItems.length > 0) {
5799
- sections.push(
5869
+ sections2.push(
5800
5870
  ["Completed this session:", ...doneItems.map((item) => `- ${item.description}`)].join("\n")
5801
5871
  );
5802
5872
  }
5803
- return sections.join("\n\n");
5873
+ return sections2.join("\n\n");
5804
5874
  }
5805
5875
  async function summarizeConversation(messages, config) {
5806
5876
  if (messages.length === 0) {
@@ -6463,8 +6533,8 @@ function createProfileClient({ baseUrl, apiKey, fetch: doFetch, timeoutMs = 8e3
6463
6533
  }
6464
6534
  },
6465
6535
  /** 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 });
6536
+ async adapt(sections2) {
6537
+ const res = await call("POST", "/adapt", { body: { sections: sections2 }, timeoutMs: ADAPT_TIMEOUT_MS });
6468
6538
  return await res.json();
6469
6539
  },
6470
6540
  /** Deletes one asset; false when it did not exist. */
@@ -6580,17 +6650,17 @@ var init_memory_tool = __esm({
6580
6650
  // ../agent/src/import/profile-loader.ts
6581
6651
  async function loadProfile(opts) {
6582
6652
  const { client, cache: cache4, projectKeys } = opts;
6583
- let cached = null;
6653
+ let cached2 = null;
6584
6654
  let cacheError;
6585
6655
  try {
6586
- cached = await cache4.read();
6656
+ cached2 = await cache4.read();
6587
6657
  } catch (err) {
6588
6658
  cacheError = `profile cache unreadable: ${err instanceof Error ? err.message : String(err)}`;
6589
6659
  }
6590
- const sameProject = cached !== null && cached.projectKey !== null && projectKeys.includes(cached.projectKey);
6660
+ const sameProject = cached2 !== null && cached2.projectKey !== null && projectKeys.includes(cached2.projectKey);
6591
6661
  try {
6592
- const index = await client.index(sameProject ? cached.version : void 0);
6593
- if (index === null && sameProject) return { profile: cached, source: "live" };
6662
+ const index = await client.index(sameProject ? cached2.version : void 0);
6663
+ if (index === null && sameProject) return { profile: cached2, source: "live" };
6594
6664
  if (index === null) throw new Error("profile API answered 304 with no usable cached copy");
6595
6665
  const has2 = (kind, name) => index.assets.some((a) => a.kind === kind && a.name === name);
6596
6666
  const chosen = projectKeys.find((k2) => has2("memory", `${k2}.index`)) ?? null;
@@ -6612,8 +6682,8 @@ async function loadProfile(opts) {
6612
6682
  return { profile, source: "live", ...cacheError ? { error: cacheError } : {} };
6613
6683
  } catch (err) {
6614
6684
  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 };
6685
+ if (!cached2) return { profile: null, source: "none", error };
6686
+ return { profile: sameProject ? cached2 : { ...cached2, memoryIndex: null, projectKey: null }, source: "cache", error };
6617
6687
  }
6618
6688
  }
6619
6689
  var init_profile_loader = __esm({
@@ -6645,14 +6715,14 @@ function splitSections(text) {
6645
6715
  });
6646
6716
  if (starts.length === 0) return { preamble: text, sections: [] };
6647
6717
  const taken = /* @__PURE__ */ new Set();
6648
- const sections = starts.map((start, k2) => {
6718
+ const sections2 = starts.map((start, k2) => {
6649
6719
  const end = k2 + 1 < starts.length ? starts[k2 + 1] : lines2.length;
6650
6720
  const chunk = lines2.slice(start, end).join("\n") + (end < lines2.length ? "\n" : "");
6651
6721
  const title = lines2[start].slice(3).trim();
6652
6722
  return { title, slug: slugify(title, taken), text: chunk };
6653
6723
  });
6654
6724
  const preamble = starts[0] === 0 ? "" : lines2.slice(0, starts[0]).join("\n") + "\n";
6655
- return { preamble, sections };
6725
+ return { preamble, sections: sections2 };
6656
6726
  }
6657
6727
  function joinSections(split) {
6658
6728
  return split.preamble + split.sections.map((s) => s.text).join("");
@@ -6712,11 +6782,11 @@ function prepareAdaptation(original) {
6712
6782
  return r.text;
6713
6783
  };
6714
6784
  const preamble = tr(split.preamble);
6715
- const sections = split.sections.map((s) => {
6785
+ const sections2 = split.sections.map((s) => {
6716
6786
  const text = tr(s.text);
6717
6787
  return { slug: s.slug, title: s.title, chars: text.length, unsupported: findUnsupported(text), text };
6718
6788
  });
6719
- return { preamble, sections, replaced: [...replaced.values()] };
6789
+ return { preamble, sections: sections2, replaced: [...replaced.values()] };
6720
6790
  }
6721
6791
  function applyPlan(input, decisions, maxCoreChars = MAX_CORE_CHARS) {
6722
6792
  const bySlug = new Map(input.sections.map((s) => [s.slug, s]));
@@ -7695,6 +7765,7 @@ __export(src_exports, {
7695
7765
  prepareAdaptation: () => prepareAdaptation,
7696
7766
  projectKey: () => projectKey,
7697
7767
  readClaudeAssets: () => readClaudeAssets,
7768
+ renderProjectCard: () => renderProjectCard,
7698
7769
  runAgentLoop: () => runAgentLoop,
7699
7770
  safeSkillPath: () => safeSkillPath,
7700
7771
  serializeImportsConfig: () => serializeImportsConfig,
@@ -7712,6 +7783,7 @@ var init_src = __esm({
7712
7783
  init_context_window();
7713
7784
  init_bash();
7714
7785
  init_prompt();
7786
+ init_project_card();
7715
7787
  init_file_store();
7716
7788
  init_files();
7717
7789
  init_indexer();
@@ -7772,7 +7844,7 @@ __export(api_key_exports, {
7772
7844
  });
7773
7845
  import { join } from "node:path";
7774
7846
  async function resolveApiKey(deps) {
7775
- const { env: env3, readFile: readFile9, homedir: homedir3 } = deps;
7847
+ const { env: env3, readFile: readFile10, homedir: homedir3 } = deps;
7776
7848
  for (const name of ["SPACECODE_API_KEY", "ANTHROPIC_API_KEY"]) {
7777
7849
  const value = env3[name]?.trim();
7778
7850
  if (value) return value;
@@ -7781,7 +7853,7 @@ async function resolveApiKey(deps) {
7781
7853
  const file = join(dir, "auth.json");
7782
7854
  let raw;
7783
7855
  try {
7784
- raw = await readFile9(file);
7856
+ raw = await readFile10(file);
7785
7857
  } catch (err) {
7786
7858
  if (err.code === "ENOENT") return null;
7787
7859
  throw new Error(`Cannot read ${file}: ${err instanceof Error ? err.message : String(err)}`);
@@ -9122,11 +9194,11 @@ var init_base = __esm({
9122
9194
  }
9123
9195
  const parts = os.release().split(".");
9124
9196
  const major = Number(parts[0]);
9125
- const build = Number(parts[2] ?? 0);
9197
+ const build2 = Number(parts[2] ?? 0);
9126
9198
  if (major < 10) {
9127
9199
  return true;
9128
9200
  }
9129
- if (major === 10 && build < 10586) {
9201
+ if (major === 10 && build2 < 10586) {
9130
9202
  return true;
9131
9203
  }
9132
9204
  return false;
@@ -24121,7 +24193,7 @@ var require_react_reconciler_development = __commonJS({
24121
24193
  }
24122
24194
  }
24123
24195
  }
24124
- var render3 = Component.render;
24196
+ var render4 = Component.render;
24125
24197
  var ref = workInProgress2.ref;
24126
24198
  var nextChildren;
24127
24199
  var hasId;
@@ -24132,12 +24204,12 @@ var require_react_reconciler_development = __commonJS({
24132
24204
  {
24133
24205
  ReactCurrentOwner$1.current = workInProgress2;
24134
24206
  setIsRendering(true);
24135
- nextChildren = renderWithHooks(current2, workInProgress2, render3, nextProps, ref, renderLanes2);
24207
+ nextChildren = renderWithHooks(current2, workInProgress2, render4, nextProps, ref, renderLanes2);
24136
24208
  hasId = checkDidRenderIdHook();
24137
24209
  if (workInProgress2.mode & StrictLegacyMode) {
24138
24210
  setIsStrictModeForDevtools(true);
24139
24211
  try {
24140
- nextChildren = renderWithHooks(current2, workInProgress2, render3, nextProps, ref, renderLanes2);
24212
+ nextChildren = renderWithHooks(current2, workInProgress2, render4, nextProps, ref, renderLanes2);
24141
24213
  hasId = checkDidRenderIdHook();
24142
24214
  } finally {
24143
24215
  setIsStrictModeForDevtools(false);
@@ -25491,9 +25563,9 @@ var require_react_reconciler_development = __commonJS({
25491
25563
  }
25492
25564
  }
25493
25565
  var newProps = workInProgress2.pendingProps;
25494
- var render3 = newProps.children;
25566
+ var render4 = newProps.children;
25495
25567
  {
25496
- if (typeof render3 !== "function") {
25568
+ if (typeof render4 !== "function") {
25497
25569
  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
25570
  }
25499
25571
  }
@@ -25506,7 +25578,7 @@ var require_react_reconciler_development = __commonJS({
25506
25578
  {
25507
25579
  ReactCurrentOwner$1.current = workInProgress2;
25508
25580
  setIsRendering(true);
25509
- newChildren = render3(newValue);
25581
+ newChildren = render4(newValue);
25510
25582
  setIsRendering(false);
25511
25583
  }
25512
25584
  {
@@ -38606,7 +38678,7 @@ var init_log_update = __esm({
38606
38678
  let previousLineCount = 0;
38607
38679
  let previousOutput = "";
38608
38680
  let hasHiddenCursor = false;
38609
- const render3 = (str) => {
38681
+ const render4 = (str) => {
38610
38682
  if (!showCursor && !hasHiddenCursor) {
38611
38683
  cli_cursor_default.hide();
38612
38684
  hasHiddenCursor = true;
@@ -38619,12 +38691,12 @@ var init_log_update = __esm({
38619
38691
  stream.write(base_exports.eraseLines(previousLineCount) + output);
38620
38692
  previousLineCount = output.split("\n").length;
38621
38693
  };
38622
- render3.clear = () => {
38694
+ render4.clear = () => {
38623
38695
  stream.write(base_exports.eraseLines(previousLineCount));
38624
38696
  previousOutput = "";
38625
38697
  previousLineCount = 0;
38626
38698
  };
38627
- render3.done = () => {
38699
+ render4.done = () => {
38628
38700
  previousOutput = "";
38629
38701
  previousLineCount = 0;
38630
38702
  if (!showCursor) {
@@ -38632,7 +38704,7 @@ var init_log_update = __esm({
38632
38704
  hasHiddenCursor = false;
38633
38705
  }
38634
38706
  };
38635
- return render3;
38707
+ return render4;
38636
38708
  };
38637
38709
  logUpdate = { create };
38638
38710
  log_update_default = logUpdate;
@@ -39765,12 +39837,12 @@ var init_ink = __esm({
39765
39837
  // ../../node_modules/ink/build/render.js
39766
39838
  import { Stream } from "node:stream";
39767
39839
  import process12 from "node:process";
39768
- var render, render_default, getOptions, getInstance;
39840
+ var render2, render_default, getOptions, getInstance;
39769
39841
  var init_render = __esm({
39770
39842
  async "../../node_modules/ink/build/render.js"() {
39771
39843
  await init_ink();
39772
39844
  init_instances();
39773
- render = (node, options) => {
39845
+ render2 = (node, options) => {
39774
39846
  const inkOptions = {
39775
39847
  stdout: process12.stdout,
39776
39848
  stdin: process12.stdin,
@@ -39792,7 +39864,7 @@ var init_render = __esm({
39792
39864
  clear: instance.clear
39793
39865
  };
39794
39866
  };
39795
- render_default = render;
39867
+ render_default = render2;
39796
39868
  getOptions = (stdout = {}) => {
39797
39869
  if (stdout instanceof Stream) {
39798
39870
  return {
@@ -39815,7 +39887,7 @@ var init_render = __esm({
39815
39887
 
39816
39888
  // ../../node_modules/ink/build/components/Static.js
39817
39889
  function Static(props) {
39818
- const { items, children: render3, style: customStyle } = props;
39890
+ const { items, children: render4, style: customStyle } = props;
39819
39891
  const [index, setIndex] = (0, import_react11.useState)(0);
39820
39892
  const itemsToRender = (0, import_react11.useMemo)(() => {
39821
39893
  return items.slice(index);
@@ -39824,7 +39896,7 @@ function Static(props) {
39824
39896
  setIndex(items.length);
39825
39897
  }, [items.length]);
39826
39898
  const children = itemsToRender.map((item, itemIndex) => {
39827
- return render3(item, index + itemIndex);
39899
+ return render4(item, index + itemIndex);
39828
39900
  });
39829
39901
  const style = (0, import_react11.useMemo)(() => ({
39830
39902
  position: "absolute",
@@ -42932,7 +43004,7 @@ var require_colors = __commonJS({
42932
43004
  }
42933
43005
  return str.replace(matchOperatorsRe, "\\$&");
42934
43006
  };
42935
- function build(_styles) {
43007
+ function build2(_styles) {
42936
43008
  var builder = function builder2() {
42937
43009
  return applyStyle2.apply(builder2, arguments);
42938
43010
  };
@@ -42947,7 +43019,7 @@ var require_colors = __commonJS({
42947
43019
  ansiStyles3[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles3[key].close), "g");
42948
43020
  ret[key] = {
42949
43021
  get: function() {
42950
- return build(this._styles.concat(key));
43022
+ return build2(this._styles.concat(key));
42951
43023
  }
42952
43024
  };
42953
43025
  });
@@ -43006,7 +43078,7 @@ var require_colors = __commonJS({
43006
43078
  Object.keys(styles5).forEach(function(name) {
43007
43079
  ret[name] = {
43008
43080
  get: function() {
43009
- return build([name]);
43081
+ return build2([name]);
43010
43082
  }
43011
43083
  };
43012
43084
  });
@@ -44010,7 +44082,7 @@ var require_core = __commonJS({
44010
44082
  return match && match.index === 0;
44011
44083
  }
44012
44084
  var BACKREF_RE = /\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;
44013
- function join12(regexps, separator = "|") {
44085
+ function join13(regexps, separator = "|") {
44014
44086
  let numCaptures = 0;
44015
44087
  return regexps.map((regex2) => {
44016
44088
  numCaptures += 1;
@@ -44314,7 +44386,7 @@ var require_core = __commonJS({
44314
44386
  this.exec = () => null;
44315
44387
  }
44316
44388
  const terminators = this.regexes.map((el) => el[1]);
44317
- this.matcherRe = langRe(join12(terminators), true);
44389
+ this.matcherRe = langRe(join13(terminators), true);
44318
44390
  this.lastIndex = 0;
44319
44391
  }
44320
44392
  /** @param {string} s */
@@ -44609,7 +44681,7 @@ var require_core = __commonJS({
44609
44681
  function close(node) {
44610
44682
  result += "</" + tag(node) + ">";
44611
44683
  }
44612
- function render3(event) {
44684
+ function render4(event) {
44613
44685
  (event.event === "start" ? open3 : close)(event.node);
44614
44686
  }
44615
44687
  while (original.length || highlighted.length) {
@@ -44619,7 +44691,7 @@ var require_core = __commonJS({
44619
44691
  if (stream === original) {
44620
44692
  nodeStack.reverse().forEach(close);
44621
44693
  do {
44622
- render3(stream.splice(0, 1)[0]);
44694
+ render4(stream.splice(0, 1)[0]);
44623
44695
  stream = selectStream();
44624
44696
  } while (stream === original && stream.length && stream[0].offset === processed);
44625
44697
  nodeStack.reverse().forEach(open3);
@@ -44629,7 +44701,7 @@ var require_core = __commonJS({
44629
44701
  } else {
44630
44702
  nodeStack.pop();
44631
44703
  }
44632
- render3(stream.splice(0, 1)[0]);
44704
+ render4(stream.splice(0, 1)[0]);
44633
44705
  }
44634
44706
  }
44635
44707
  return result + escapeHTML(value.substr(processed));
@@ -52094,7 +52166,7 @@ var require_markdown = __commonJS({
52094
52166
  BOLD.contains = BOLD.contains.concat(CONTAINABLE);
52095
52167
  ITALIC.contains = ITALIC.contains.concat(CONTAINABLE);
52096
52168
  CONTAINABLE = CONTAINABLE.concat(BOLD, ITALIC);
52097
- const HEADER = {
52169
+ const HEADER2 = {
52098
52170
  className: "section",
52099
52171
  variants: [
52100
52172
  {
@@ -52131,7 +52203,7 @@ var require_markdown = __commonJS({
52131
52203
  "mkd"
52132
52204
  ],
52133
52205
  contains: [
52134
- HEADER,
52206
+ HEADER2,
52135
52207
  INLINE_HTML,
52136
52208
  LIST,
52137
52209
  BOLD,
@@ -56178,7 +56250,7 @@ var require_http = __commonJS({
56178
56250
  function http(hljs) {
56179
56251
  const VERSION = "HTTP/(2|1\\.[01])";
56180
56252
  const HEADER_NAME = /[A-Za-z][A-Za-z0-9-]*/;
56181
- const HEADER = {
56253
+ const HEADER2 = {
56182
56254
  className: "attribute",
56183
56255
  begin: concat("^", HEADER_NAME, "(?=\\:\\s)"),
56184
56256
  starts: {
@@ -56196,7 +56268,7 @@ var require_http = __commonJS({
56196
56268
  }
56197
56269
  };
56198
56270
  const HEADERS_AND_BODY = [
56199
- HEADER,
56271
+ HEADER2,
56200
56272
  {
56201
56273
  begin: "\\n\\n",
56202
56274
  starts: { subLanguage: [], endsWithParent: true }
@@ -56255,7 +56327,7 @@ var require_http = __commonJS({
56255
56327
  }
56256
56328
  },
56257
56329
  // to allow headers to work even without a preamble
56258
- hljs.inherit(HEADER, {
56330
+ hljs.inherit(HEADER2, {
56259
56331
  relevance: 0
56260
56332
  })
56261
56333
  ]
@@ -99376,8 +99448,8 @@ function markedTerminal(options, highlightOptions) {
99376
99448
  );
99377
99449
  }
99378
99450
  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) {
99451
+ var splitRe = gfm ? HARD_RETURN_GFM_RE : HARD_RETURN_RE, sections2 = text.split(splitRe), reflowed = [];
99452
+ sections2.forEach(function(section2) {
99381
99453
  var fragments = section2.split(/(\u001b\[(?:\d{1,3})(?:;\d{1,3})*m)/g);
99382
99454
  var column = 0;
99383
99455
  var currentLine = "";
@@ -103628,6 +103700,161 @@ var init_local_toolset = __esm({
103628
103700
  }
103629
103701
  });
103630
103702
 
103703
+ // src/project-card-scan.ts
103704
+ import { execFile as execFile3 } from "node:child_process";
103705
+ import { readdir as readdir3, readFile as readFile4 } from "node:fs/promises";
103706
+ import { join as join7 } from "node:path";
103707
+ function digestLines(md) {
103708
+ return md.split("\n").map((l) => l.trim()).filter((l) => l && !/^#\s/.test(l) && !/^[|\s:-]+$/.test(l) && !/^\|\s*(area|task|priority|date|issue|id)\b/i.test(l)).slice(0, 12);
103709
+ }
103710
+ function firstHeading(md) {
103711
+ for (const line of md.split("\n", 40)) {
103712
+ const m2 = /^#{1,3}\s+(.+?)\s*$/.exec(line);
103713
+ if (m2) return m2[1];
103714
+ }
103715
+ return null;
103716
+ }
103717
+ function backlogLines(todo) {
103718
+ const out = [];
103719
+ for (const raw of todo.split("\n")) {
103720
+ const line = raw.trim();
103721
+ if (!line || /^[|\s:-]+$/.test(line) || /^\|\s*(area|task|priority)\b/i.test(line)) continue;
103722
+ const row = /^\|\s*([^|]+?)\s*\|/.exec(line);
103723
+ const bullet = /^[-*]\s+(?:\[[ x]\]\s*)?(.+)$/.exec(line);
103724
+ const text = row?.[1] ?? bullet?.[1];
103725
+ if (text && text.length > 8) out.push(text);
103726
+ if (out.length >= MAX_BACKLOG) break;
103727
+ }
103728
+ return out;
103729
+ }
103730
+ async function scanProject(cwd2, io = nodeScanIo) {
103731
+ const problems = [];
103732
+ const attempt = async (label, fn, fallback) => {
103733
+ try {
103734
+ return await fn();
103735
+ } catch (err) {
103736
+ problems.push(`${label}: ${err instanceof Error ? err.message : String(err)}`);
103737
+ return fallback;
103738
+ }
103739
+ };
103740
+ const root = await attempt("list root", () => io.readdir(cwd2), []);
103741
+ const topLevel = root.filter((e) => !SKIP.has(e.name)).map((e) => ({ path: e.name, isDir: e.isDir }));
103742
+ const childDirs = {};
103743
+ for (const d of topLevel.filter((e) => e.isDir && ["apps", "packages", "services", "libs", "src"].includes(e.path))) {
103744
+ const kids = await attempt(`list ${d.path}`, () => io.readdir(join7(cwd2, d.path)), []);
103745
+ childDirs[d.path] = kids.filter((k2) => !SKIP.has(k2.name) && !k2.name.startsWith(".")).map((k2) => k2.isDir ? k2.name : k2.name).slice(0, 20);
103746
+ }
103747
+ let manifest;
103748
+ const pkgText = await attempt("read package.json", () => io.readFile(join7(cwd2, "package.json")), null);
103749
+ if (pkgText) {
103750
+ try {
103751
+ const pkg = JSON.parse(pkgText);
103752
+ manifest = {
103753
+ ...pkg.name ? { name: pkg.name } : {},
103754
+ ...pkg.scripts ? { scripts: pkg.scripts } : {},
103755
+ ...Array.isArray(pkg.workspaces) ? { workspaces: pkg.workspaces } : pkg.workspaces?.packages ? { workspaces: pkg.workspaces.packages } : {}
103756
+ };
103757
+ } catch (err) {
103758
+ problems.push(`parse package.json: ${err instanceof Error ? err.message : String(err)}`);
103759
+ }
103760
+ }
103761
+ const docs = [];
103762
+ for (const e of topLevel.filter((t) => !t.isDir && /\.md$/i.test(t.path)).slice(0, MAX_DOCS)) {
103763
+ const text = await attempt(`read ${e.path}`, () => io.readFile(join7(cwd2, e.path)), null);
103764
+ const title = text ? firstHeading(text) : null;
103765
+ if (title) docs.push({ path: e.path, title });
103766
+ }
103767
+ const digests = [];
103768
+ for (const name of DIGEST_FILES.filter((n) => topLevel.some((t) => t.path === n))) {
103769
+ const text = await attempt(`read ${name}`, () => io.readFile(join7(cwd2, name)), null);
103770
+ const lines2 = text ? digestLines(text) : [];
103771
+ if (lines2.length) digests.push({ path: name, lines: lines2 });
103772
+ }
103773
+ const todo = await attempt("read TODO.md", () => io.readFile(join7(cwd2, "TODO.md")), null);
103774
+ const log = await io.git(cwd2, ["log", "--oneline", "-8"]);
103775
+ const status = await io.git(cwd2, ["status", "--porcelain"]);
103776
+ const branch = await io.git(cwd2, ["rev-parse", "--abbrev-ref", "HEAD"]);
103777
+ const onBranch = branch?.trim();
103778
+ const vsMainRaw = onBranch && onBranch !== "main" && onBranch !== "HEAD" ? await io.git(cwd2, ["diff", "--shortstat", "main...HEAD"]) : null;
103779
+ const vsMain = vsMainRaw?.trim() ? `${vsMainRaw.trim()} (branch ${onBranch})` : void 0;
103780
+ return {
103781
+ problems,
103782
+ input: {
103783
+ cwd: cwd2,
103784
+ ...branch?.trim() ? { branch: branch.trim() } : {},
103785
+ ...status !== null ? { dirtyFiles: status.split("\n").filter(Boolean).length } : {},
103786
+ ...manifest ? { manifest } : {},
103787
+ topLevel,
103788
+ childDirs,
103789
+ recentCommits: (log ?? "").split("\n").filter(Boolean),
103790
+ docs,
103791
+ backlog: todo ? backlogLines(todo) : [],
103792
+ changes: { files: (status ?? "").split("\n").filter(Boolean), ...vsMain ? { vsMain } : {} },
103793
+ digests
103794
+ }
103795
+ };
103796
+ }
103797
+ var SKIP, MAX_DOCS, MAX_BACKLOG, DIGEST_FILES, nodeScanIo;
103798
+ var init_project_card_scan = __esm({
103799
+ "src/project-card-scan.ts"() {
103800
+ "use strict";
103801
+ SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", ".wrangler", ".turbo", "coverage", ".worktrees", ".superpowers", ".remember"]);
103802
+ MAX_DOCS = 15;
103803
+ MAX_BACKLOG = 6;
103804
+ DIGEST_FILES = ["CLAUDE.md", "TODO.md", "BUGS.md", "ISSUES.md", "CHANGELOG.md", "README.md"];
103805
+ nodeScanIo = {
103806
+ async readdir(path12) {
103807
+ const entries = await readdir3(path12, { withFileTypes: true });
103808
+ return entries.map((e) => ({ name: e.name, isDir: e.isDirectory() }));
103809
+ },
103810
+ async readFile(path12) {
103811
+ try {
103812
+ return await readFile4(path12, "utf8");
103813
+ } catch (err) {
103814
+ if (err.code === "ENOENT") return null;
103815
+ throw err;
103816
+ }
103817
+ },
103818
+ git: (cwd2, args) => new Promise((resolve2) => {
103819
+ execFile3("git", args, { cwd: cwd2, timeout: 3e3, maxBuffer: 256 * 1024 }, (err, stdout) => resolve2(err ? null : stdout));
103820
+ })
103821
+ };
103822
+ }
103823
+ });
103824
+
103825
+ // src/project-card-runtime.ts
103826
+ async function build(cwd2, io) {
103827
+ try {
103828
+ const { input, problems } = await scanProject(cwd2, io);
103829
+ const text = renderProjectCard(input);
103830
+ return {
103831
+ text,
103832
+ statusLine: `project card: ${text.length} chars, ${input.topLevel.length} top-level entries${problems.length ? `, ${problems.length} scan problem(s): ${problems.join("; ")}` : ""}`
103833
+ };
103834
+ } catch (err) {
103835
+ console.error("[project-card] scan failed; the REPL runs without a card", err instanceof Error ? err.message : String(err));
103836
+ return { text: "", statusLine: `project card: not built (${err instanceof Error ? err.message : String(err)})` };
103837
+ }
103838
+ }
103839
+ function loadProjectCard(cwd2, deps = {}) {
103840
+ const env3 = deps.env ?? process.env;
103841
+ const now = deps.now ?? Date.now;
103842
+ if (env3.SPACECODE_PROJECT_CARD === "off") return Promise.resolve({ text: "", statusLine: "project card: disabled (SPACECODE_PROJECT_CARD=off)" });
103843
+ if (cached && cached.cwd === cwd2 && now() - cached.at < TTL_MS) return cached.pending;
103844
+ const pending = build(cwd2, deps.io ?? nodeScanIo);
103845
+ cached = { cwd: cwd2, at: now(), pending };
103846
+ return pending;
103847
+ }
103848
+ var TTL_MS, cached;
103849
+ var init_project_card_runtime = __esm({
103850
+ "src/project-card-runtime.ts"() {
103851
+ "use strict";
103852
+ init_src();
103853
+ init_project_card_scan();
103854
+ TTL_MS = 5 * 6e4;
103855
+ }
103856
+ });
103857
+
103631
103858
  // src/shell-turn.ts
103632
103859
  function shellCommandOf(prompt) {
103633
103860
  return prompt.startsWith("!") ? prompt.slice(1).trim() : null;
@@ -103922,11 +104149,11 @@ var init_node_mention_fs = __esm({
103922
104149
  });
103923
104150
 
103924
104151
  // src/editor/node-read-dir.ts
103925
- import { readdir as readdir3 } from "node:fs/promises";
104152
+ import { readdir as readdir4 } from "node:fs/promises";
103926
104153
  import path7 from "node:path";
103927
104154
  function createNodeReadDir(cwd2) {
103928
104155
  return async (rel) => {
103929
- const entries = await readdir3(path7.join(cwd2, rel), { withFileTypes: true });
104156
+ const entries = await readdir4(path7.join(cwd2, rel), { withFileTypes: true });
103930
104157
  return entries.map((e) => ({ name: e.name, isFile: e.isFile(), isDirectory: e.isDirectory() }));
103931
104158
  };
103932
104159
  }
@@ -103937,11 +104164,11 @@ var init_node_read_dir = __esm({
103937
104164
  });
103938
104165
 
103939
104166
  // src/editor/node-mentions.ts
103940
- import { readFile as readFile4 } from "node:fs/promises";
104167
+ import { readFile as readFile5 } from "node:fs/promises";
103941
104168
  import path8 from "node:path";
103942
104169
  async function readGitignore(cwd2) {
103943
104170
  try {
103944
- return createGitignoreMatcher(await readFile4(path8.join(cwd2, ".gitignore"), "utf8"));
104171
+ return createGitignoreMatcher(await readFile5(path8.join(cwd2, ".gitignore"), "utf8"));
103945
104172
  } catch (err) {
103946
104173
  if (err.code === "ENOENT") return () => false;
103947
104174
  throw err;
@@ -104051,8 +104278,8 @@ var init_recording_chunks = __esm({
104051
104278
 
104052
104279
  // src/recording/recorder.ts
104053
104280
  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";
104281
+ 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";
104282
+ import { join as join8 } from "node:path";
104056
104283
  function projectHash(root) {
104057
104284
  return createHash4("sha256").update(root).digest("hex");
104058
104285
  }
@@ -104063,7 +104290,7 @@ function createRecorder(deps) {
104063
104290
  let inflight = null;
104064
104291
  let retryAttempt = 0;
104065
104292
  let timerArmed = false;
104066
- const stateFile = (sessionId) => join7(deps.dir, `${sessionId}.json`);
104293
+ const stateFile = (sessionId) => join8(deps.dir, `${sessionId}.json`);
104067
104294
  async function save(sessionId, st) {
104068
104295
  await mkdir4(deps.dir, { recursive: true, mode: 448 });
104069
104296
  const tmp = `${stateFile(sessionId)}.${deps.randomId()}.tmp`;
@@ -104071,16 +104298,16 @@ function createRecorder(deps) {
104071
104298
  await rename4(tmp, stateFile(sessionId));
104072
104299
  }
104073
104300
  async function load(sessionId) {
104074
- const cached = states.get(sessionId);
104075
- if (cached) return cached;
104301
+ const cached2 = states.get(sessionId);
104302
+ if (cached2) return cached2;
104076
104303
  let st;
104077
104304
  try {
104078
- st = JSON.parse(await readFile5(stateFile(sessionId), "utf8"));
104305
+ st = JSON.parse(await readFile6(stateFile(sessionId), "utf8"));
104079
104306
  } catch (e) {
104080
104307
  if (e.code !== "ENOENT") {
104081
- const bad = join7(deps.dir, "bad");
104308
+ const bad = join8(deps.dir, "bad");
104082
104309
  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)}`));
104310
+ await rename4(stateFile(sessionId), join8(bad, `${sessionId}.${deps.now()}.json`)).catch((err) => deps.log(`recording: could not quarantine ${sessionId}: ${String(err)}`));
104084
104311
  lastError = `recording state for ${sessionId} was unreadable and was quarantined`;
104085
104312
  deps.log(`recording: ${lastError}`);
104086
104313
  }
@@ -104242,7 +104469,7 @@ function createRecorder(deps) {
104242
104469
  async function resume() {
104243
104470
  let names;
104244
104471
  try {
104245
- names = await readdir4(deps.dir);
104472
+ names = await readdir5(deps.dir);
104246
104473
  } catch (e) {
104247
104474
  if (e.code === "ENOENT") return;
104248
104475
  throw e;
@@ -104270,8 +104497,8 @@ var init_recorder = __esm({
104270
104497
  });
104271
104498
 
104272
104499
  // 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";
104500
+ import { mkdir as mkdir5, readFile as readFile7, writeFile as writeFile5, stat as stat4 } from "node:fs/promises";
104501
+ import { join as join9, sep as sep2 } from "node:path";
104275
104502
  function matchesExclude(pattern, root) {
104276
104503
  const re3 = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "\0").replace(/\*/g, `[^${sep2 === "\\" ? "\\\\" : "/"}]*`).replace(/\u0000/g, ".*");
104277
104504
  return new RegExp(`^${re3}(?:[/\\\\].*)?$`).test(root);
@@ -104286,8 +104513,8 @@ async function exists(path12) {
104286
104513
  }
104287
104514
  }
104288
104515
  function createRecordingRuntime(deps) {
104289
- const dir = join8(deps.configDir, "recordings");
104290
- const noticeFile = join8(dir, ".notice");
104516
+ const dir = join9(deps.configDir, "recordings");
104517
+ const noticeFile = join9(dir, ".notice");
104291
104518
  let recorder = null;
104292
104519
  let standingOff;
104293
104520
  let sessionOff = false;
@@ -104295,7 +104522,7 @@ function createRecordingRuntime(deps) {
104295
104522
  if (deps.env.SPACECODE_RECORD?.trim().toLowerCase() === "off") return "SPACECODE_RECORD=off";
104296
104523
  let cfg = {};
104297
104524
  try {
104298
- cfg = JSON.parse(await readFile6(join8(deps.configDir, "config.json"), "utf8"));
104525
+ cfg = JSON.parse(await readFile7(join9(deps.configDir, "config.json"), "utf8"));
104299
104526
  } catch (e) {
104300
104527
  if (e.code !== "ENOENT") return `~/.spacecode/config.json is unreadable (${e instanceof Error ? e.message : String(e)}); fix it to record`;
104301
104528
  }
@@ -104303,7 +104530,7 @@ function createRecordingRuntime(deps) {
104303
104530
  const excludes = Array.isArray(cfg.recording?.exclude) ? cfg.recording.exclude.filter((x2) => typeof x2 === "string") : [];
104304
104531
  const hit = excludes.find((p) => matchesExclude(p, deps.projectRoot));
104305
104532
  if (hit) return `this folder matches recording.exclude (${hit})`;
104306
- if (await exists(join8(deps.projectRoot, ".spacecode", "norecord"))) return "this repo has .spacecode/norecord";
104533
+ if (await exists(join9(deps.projectRoot, ".spacecode", "norecord"))) return "this repo has .spacecode/norecord";
104307
104534
  return null;
104308
104535
  }
104309
104536
  async function noticeShown() {
@@ -104444,11 +104671,11 @@ var init_skills_command = __esm({
104444
104671
  });
104445
104672
 
104446
104673
  // src/permission-rules-loader.ts
104447
- import { readFile as readFile7 } from "node:fs/promises";
104448
- import { join as join9 } from "node:path";
104674
+ import { readFile as readFile8 } from "node:fs/promises";
104675
+ import { join as join10 } from "node:path";
104449
104676
  async function readOrNull2(path12) {
104450
104677
  try {
104451
- return await readFile7(path12, "utf8");
104678
+ return await readFile8(path12, "utf8");
104452
104679
  } catch (err) {
104453
104680
  if (err.code === "ENOENT") return null;
104454
104681
  throw err;
@@ -104456,7 +104683,7 @@ async function readOrNull2(path12) {
104456
104683
  }
104457
104684
  async function loadPermissionRules(deps) {
104458
104685
  const none = (line) => ({ rules: void 0, defaultMode: void 0, statusLines: [line] });
104459
- const configPath = join9(deps.configDir, "imports.json");
104686
+ const configPath = join10(deps.configDir, "imports.json");
104460
104687
  try {
104461
104688
  const loaded = parseImportsConfig(await readOrNull2(configPath), configPath);
104462
104689
  if (loaded.error) return none("permission rules: off (imports.json unreadable)");
@@ -104465,13 +104692,13 @@ async function loadPermissionRules(deps) {
104465
104692
  return none(`permission rules: off (imports.json not read: ${err instanceof Error ? err.message : String(err)})`);
104466
104693
  }
104467
104694
  const lines2 = [];
104468
- const files = [{ scope: "user", path: join9(deps.homeDir, ".claude", "settings.json") }];
104695
+ const files = [{ scope: "user", path: join10(deps.homeDir, ".claude", "settings.json") }];
104469
104696
  try {
104470
104697
  const sources2 = await readRepoSources(deps.cwd);
104471
104698
  if (sources2[".claude/settings.json"] !== void 0 || sources2[".claude/settings.local.json"] !== void 0) {
104472
104699
  const status = await createNodeTrustStore(deps.configDir).repoStatus(deps.cwd, sources2);
104473
104700
  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") });
104701
+ files.push({ scope: "repo", path: join10(deps.cwd, ".claude", "settings.json") }, { scope: "local", path: join10(deps.cwd, ".claude", "settings.local.json") });
104475
104702
  } else lines2.push(`permission rules: repo settings NOT loaded (${status === "never" ? "marked never trust" : "untrusted repo"})`);
104476
104703
  }
104477
104704
  } catch (err) {
@@ -104513,7 +104740,7 @@ var init_permission_rules_loader = __esm({
104513
104740
  });
104514
104741
 
104515
104742
  // src/command-loader.ts
104516
- import { join as join10 } from "node:path";
104743
+ import { join as join11 } from "node:path";
104517
104744
  async function userCommands(deps, lines2) {
104518
104745
  try {
104519
104746
  const apiKey = await resolveApiKey({ env: deps.env, readFile: deps.readFile, homedir: deps.homedir });
@@ -104568,7 +104795,7 @@ async function repoCommands(deps, lines2) {
104568
104795
  }
104569
104796
  }
104570
104797
  async function loadCommands(deps) {
104571
- const configPath = join10(deps.configDir, "imports.json");
104798
+ const configPath = join11(deps.configDir, "imports.json");
104572
104799
  try {
104573
104800
  const loaded = parseImportsConfig(await deps.readFile(configPath).catch((err) => {
104574
104801
  if (err.code === "ENOENT") return null;
@@ -104601,10 +104828,10 @@ var init_command_loader = __esm({
104601
104828
  });
104602
104829
 
104603
104830
  // src/useReplController.ts
104604
- import { readFile as readFile8, writeFile as writeFile6, mkdir as mkdir6 } from "node:fs/promises";
104831
+ import { readFile as readFile9, writeFile as writeFile6, mkdir as mkdir6 } from "node:fs/promises";
104605
104832
  import { homedir as homedir2 } from "node:os";
104606
- import { join as join11 } from "node:path";
104607
- import { execFile as execFile3 } from "node:child_process";
104833
+ import { join as join12 } from "node:path";
104834
+ import { execFile as execFile4 } from "node:child_process";
104608
104835
  function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mentionsOpt, sessions, resumeLatest: resumeLatest2, onViewTurns, onViewResume, isEscOwned }) {
104609
104836
  const { exit } = use_app_default();
104610
104837
  const mentions = (0, import_react30.useMemo)(() => mentionsOpt ?? createNodeMentions(process.cwd()), [mentionsOpt]);
@@ -104625,6 +104852,9 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
104625
104852
  return () => clearInterval(timer);
104626
104853
  }, [turnStartedAt]);
104627
104854
  const projectRoot = (0, import_react30.useMemo)(() => process.cwd(), []);
104855
+ (0, import_react30.useEffect)(() => {
104856
+ void loadProjectCard(projectRoot);
104857
+ }, [projectRoot]);
104628
104858
  const sessionIdRef = (0, import_react30.useRef)(null);
104629
104859
  const sessionCreatedAtRef = (0, import_react30.useRef)(null);
104630
104860
  const sessionTokensRef = (0, import_react30.useRef)({ input: 0, output: 0 });
@@ -104634,11 +104864,11 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
104634
104864
  const recordingRef = (0, import_react30.useRef)(null);
104635
104865
  const recording = () => recordingRef.current ??= createRecordingRuntime({
104636
104866
  env: process.env,
104637
- configDir: process.env.SPACECODE_CONFIG_DIR?.trim() || join11(homedir2(), ".spacecode"),
104867
+ configDir: process.env.SPACECODE_CONFIG_DIR?.trim() || join12(homedir2(), ".spacecode"),
104638
104868
  projectRoot,
104639
104869
  fetch,
104640
104870
  baseUrl: backendUrl(process.env),
104641
- resolveApiKey: () => resolveApiKey({ env: process.env, readFile: (p) => readFile8(p, "utf-8"), homedir: homedir2 }).catch(() => null),
104871
+ resolveApiKey: () => resolveApiKey({ env: process.env, readFile: (p) => readFile9(p, "utf-8"), homedir: homedir2 }).catch(() => null),
104642
104872
  log: (m2) => console.error(`[agent-ui] ${m2}`)
104643
104873
  });
104644
104874
  const resumeListRef = (0, import_react30.useRef)([]);
@@ -104661,10 +104891,10 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
104661
104891
  (0, import_react30.useEffect)(() => {
104662
104892
  let live2 = true;
104663
104893
  loadCommands({
104664
- configDir: process.env.SPACECODE_CONFIG_DIR?.trim() || join11(homedir2(), ".spacecode"),
104894
+ configDir: process.env.SPACECODE_CONFIG_DIR?.trim() || join12(homedir2(), ".spacecode"),
104665
104895
  cwd: process.cwd(),
104666
104896
  env: process.env,
104667
- readFile: (p) => readFile8(p, "utf-8"),
104897
+ readFile: (p) => readFile9(p, "utf-8"),
104668
104898
  homedir: homedir2,
104669
104899
  fetch,
104670
104900
  builtins: SLASH_COMMANDS.map((c) => c.name)
@@ -104952,7 +105182,7 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
104952
105182
  }
104953
105183
  setIsRunning(true);
104954
105184
  try {
104955
- const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) => readFile8(p, "utf-8"), homedir: homedir2 });
105185
+ const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) => readFile9(p, "utf-8"), homedir: homedir2 });
104956
105186
  if (!apiKey2) {
104957
105187
  notice(MISSING_KEY_MESSAGE);
104958
105188
  return;
@@ -104974,7 +105204,7 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
104974
105204
  }
104975
105205
  case "model": {
104976
105206
  try {
104977
- const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) => readFile8(p, "utf-8"), homedir: homedir2 });
105207
+ const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) => readFile9(p, "utf-8"), homedir: homedir2 });
104978
105208
  if (!apiKey2) {
104979
105209
  notice(MISSING_KEY_MESSAGE);
104980
105210
  return;
@@ -105009,7 +105239,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105009
105239
  }
105010
105240
  case "skills": {
105011
105241
  const state = profileRef.current;
105012
- const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) => readFile8(p, "utf-8"), homedir: homedir2 }).catch(() => null);
105242
+ const apiKey2 = await resolveApiKey({ env: process.env, readFile: (p) => readFile9(p, "utf-8"), homedir: homedir2 }).catch(() => null);
105013
105243
  const client = apiKey2 ? createProfileClient({ baseUrl: backendUrl(process.env), apiKey: apiKey2, fetch }) : null;
105014
105244
  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
105245
  return;
@@ -105031,7 +105261,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105031
105261
  try {
105032
105262
  apiKey = await resolveApiKey({
105033
105263
  env: process.env,
105034
- readFile: (p) => readFile8(p, "utf-8"),
105264
+ readFile: (p) => readFile9(p, "utf-8"),
105035
105265
  homedir: homedir2
105036
105266
  });
105037
105267
  } catch (err) {
@@ -105068,7 +105298,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105068
105298
  try {
105069
105299
  profilePromiseRef.current ??= prepareProfile({
105070
105300
  env: process.env,
105071
- readFile: (p) => readFile8(p, "utf-8"),
105301
+ readFile: (p) => readFile9(p, "utf-8"),
105072
105302
  writeFile: (p, d) => writeFile6(p, d, { encoding: "utf-8", mode: 384 }),
105073
105303
  mkdir: async (p) => {
105074
105304
  await mkdir6(p, { recursive: true });
@@ -105078,7 +105308,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105078
105308
  cwd: process.cwd(),
105079
105309
  gitRoot: gitTopLevel,
105080
105310
  importGate: async () => {
105081
- const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join11(homedir2(), ".spacecode");
105311
+ const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join12(homedir2(), ".spacecode");
105082
105312
  const gate = await importGateStatus({ configDir, cwd: process.cwd(), interactive: Boolean(process.stdin.isTTY) });
105083
105313
  const loaded = await loadPermissionRules({ configDir, homeDir: homedir2(), cwd: process.cwd() });
105084
105314
  rulesRef.current = loaded.rules;
@@ -105088,7 +105318,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105088
105318
  });
105089
105319
  profileRef.current = await profilePromiseRef.current;
105090
105320
  if (mcpRef.current === null && (profileRef.current?.mcpDefs?.length ?? 0) > 0) {
105091
- const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join11(homedir2(), ".spacecode");
105321
+ const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join12(homedir2(), ".spacecode");
105092
105322
  const trust = createNodeTrustStore(configDir);
105093
105323
  const manager = new McpManager({
105094
105324
  defs: profileRef.current.mcpDefs,
@@ -105098,7 +105328,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105098
105328
  ...rulesRef.current ? { rules: rulesRef.current } : {},
105099
105329
  permissions,
105100
105330
  confirm: askUser,
105101
- audit: new FileAuditLog(join11(homedir2(), ".spacecode", "audit.jsonl")),
105331
+ audit: new FileAuditLog(join12(homedir2(), ".spacecode", "audit.jsonl")),
105102
105332
  clientVersion: process.env.SPACECODE_VERSION ?? "0"
105103
105333
  });
105104
105334
  mcpRef.current = manager;
@@ -105106,6 +105336,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105106
105336
  }
105107
105337
  const toolset = createToolset();
105108
105338
  toolsetRef.current = toolset;
105339
+ const projectCard = await loadProjectCard(projectRoot);
105109
105340
  const config = {
105110
105341
  apiUrl: apiUrl(),
105111
105342
  apiKey,
@@ -105114,7 +105345,8 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105114
105345
  repoName: repoName2,
105115
105346
  localCwd: projectRoot,
105116
105347
  commitGuidance: true,
105117
- hasGitHubTools: toolset.hasGitHubTools
105348
+ hasGitHubTools: toolset.hasGitHubTools,
105349
+ ...projectCard.text ? { projectCard: projectCard.text } : {}
105118
105350
  }) + (profileRef.current?.promptSection ?? ""),
105119
105351
  tools: toolset.tools,
105120
105352
  maxTurns: 100,
@@ -105225,6 +105457,7 @@ var init_useReplController = __esm({
105225
105457
  init_models();
105226
105458
  init_session_commands();
105227
105459
  init_local_toolset();
105460
+ init_project_card_runtime();
105228
105461
  init_shell_turn();
105229
105462
  init_node_mentions();
105230
105463
  init_api_key();
@@ -105239,7 +105472,7 @@ var init_useReplController = __esm({
105239
105472
  init_slash_commands();
105240
105473
  init_useEventAccumulator();
105241
105474
  gitTopLevel = () => new Promise((resolve2) => {
105242
- execFile3("git", ["rev-parse", "--show-toplevel"], { cwd: process.cwd() }, (err, stdout) => resolve2(err ? null : stdout.trim() || null));
105475
+ execFile4("git", ["rev-parse", "--show-toplevel"], { cwd: process.cwd() }, (err, stdout) => resolve2(err ? null : stdout.trim() || null));
105243
105476
  });
105244
105477
  apiUrl = () => process.env.API_PROXY_URL || "https://api.spacecode.app";
105245
105478
  }
@@ -105820,16 +106053,16 @@ function clearOnResize(stdout) {
105820
106053
  var import_jsx_runtime14 = __toESM(require_jsx_runtime(), 1);
105821
106054
  if (process.argv[2] === "import-claude") {
105822
106055
  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");
106056
+ const { readFile: readFile10, readdir: readdir6 } = await import("node:fs/promises");
106057
+ const { execFile: execFile5 } = await import("node:child_process");
105825
106058
  const gitRoot = () => new Promise((resolve2) => {
105826
- execFile4("git", ["rev-parse", "--show-toplevel"], { cwd: process.cwd() }, (err, stdout) => resolve2(err ? null : stdout.trim() || null));
106059
+ execFile5("git", ["rev-parse", "--show-toplevel"], { cwd: process.cwd() }, (err, stdout) => resolve2(err ? null : stdout.trim() || null));
105827
106060
  });
105828
106061
  const code = await runImportClaude2({
105829
106062
  argv: process.argv.slice(2),
105830
106063
  env: process.env,
105831
- fs: { readFile: (p) => readFile9(p, "utf-8"), readdir: (p) => readdir5(p) },
105832
- readFile: (p) => readFile9(p, "utf-8"),
106064
+ fs: { readFile: (p) => readFile10(p, "utf-8"), readdir: (p) => readdir6(p) },
106065
+ readFile: (p) => readFile10(p, "utf-8"),
105833
106066
  homedir: os5.homedir,
105834
106067
  cwd: process.cwd(),
105835
106068
  gitRoot,
@@ -105854,9 +106087,9 @@ if (process.argv[2] === "approve-mcp") {
105854
106087
  const { resolveApiKey: resolveApiKey2 } = await Promise.resolve().then(() => (init_api_key(), api_key_exports));
105855
106088
  const { backendUrl: backendUrl2 } = await Promise.resolve().then(() => (init_profile_runtime(), profile_runtime_exports));
105856
106089
  const { createProfileClient: createProfileClient2 } = await Promise.resolve().then(() => (init_src(), src_exports));
105857
- const { readFile: readFile9 } = await import("node:fs/promises");
106090
+ const { readFile: readFile10 } = await import("node:fs/promises");
105858
106091
  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);
106092
+ const apiKey = await resolveApiKey2({ env: process.env, readFile: (p) => readFile10(p, "utf-8"), homedir: os5.homedir }).catch(() => null);
105860
106093
  const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
105861
106094
  const { createInterface } = interactive ? await import("node:readline/promises") : { createInterface: void 0 };
105862
106095
  const rl = createInterface ? createInterface({ input: process.stdin, output: process.stdout }) : void 0;
@@ -105864,7 +106097,7 @@ if (process.argv[2] === "approve-mcp") {
105864
106097
  const code = await runApproveMcp2({
105865
106098
  configDir,
105866
106099
  cwd: process.cwd(),
105867
- readFile: (p) => readFile9(p, "utf-8"),
106100
+ readFile: (p) => readFile10(p, "utf-8"),
105868
106101
  client: apiKey ? createProfileClient2({ baseUrl: backendUrl2(process.env), apiKey, fetch }) : null,
105869
106102
  trust: createNodeTrustStore2(configDir),
105870
106103
  ...rl ? { ask: async (q2) => {
@@ -105925,10 +106158,10 @@ if (process.stdin.isTTY && process.stdout.isTTY) {
105925
106158
  }
105926
106159
  }
105927
106160
  var restoreCiVariables = hideCiVariables(process.env, Boolean(process.stdout.isTTY));
105928
- var render2;
106161
+ var render3;
105929
106162
  var App3;
105930
106163
  try {
105931
- [{ render: render2 }, { App: App3 }] = await Promise.all([init_build2().then(() => build_exports), init_App2().then(() => App_exports)]);
106164
+ [{ render: render3 }, { App: App3 }] = await Promise.all([init_build2().then(() => build_exports), init_App2().then(() => App_exports)]);
105932
106165
  } finally {
105933
106166
  restoreCiVariables();
105934
106167
  }
@@ -105942,7 +106175,7 @@ if (fullscreen) {
105942
106175
  session.onLeave(restoreConsole);
105943
106176
  session.onLeave(clearOnResize(process.stdout));
105944
106177
  }
105945
- var app = render2(
106178
+ var app = render3(
105946
106179
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(App3, { repoName, onboardingMode, launchCount, yolo, resumeLatest, interactivePicker: Boolean(process.stdout.isTTY), fullscreen }),
105947
106180
  { patchConsole: !fullscreen }
105948
106181
  );