spacecode 2.8.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
@@ -3852,7 +3852,7 @@ function buildSystemPrompt(options) {
3852
3852
  ${toolLines.join("\n")}
3853
3853
 
3854
3854
  ## Tool usage
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."}
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.
@@ -3921,13 +3921,17 @@ function sections(i) {
3921
3921
  if (docs.length) out.push({ key: "docs", lines: docs });
3922
3922
  const commits = i.recentCommits.slice(0, 8).map((c) => `- ${c}`);
3923
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 });
3924
3928
  const backlog = i.backlog.slice(0, 6).map((b2) => `- ${cut(b2, 160)}`);
3925
3929
  if (backlog.length) out.push({ key: "backlog", lines: backlog });
3926
3930
  return out;
3927
3931
  }
3928
- function renderProjectCard(input, budgetChars = 3500) {
3932
+ function renderProjectCard(input, budgetChars = 7e3) {
3929
3933
  let secs = sections(input);
3930
- for (const drop of ["backlog", "docs", "commits", "commands"]) {
3934
+ for (const drop of ["backlog", "digests", "changes", "docs", "commits", "commands"]) {
3931
3935
  if (render(secs).length <= budgetChars) return render(secs);
3932
3936
  secs = secs.filter((s) => s.key !== drop);
3933
3937
  }
@@ -3953,6 +3957,8 @@ var init_project_card = __esm({
3953
3957
  commands: "Commands",
3954
3958
  docs: "Docs",
3955
3959
  commits: "Recent commits",
3960
+ changes: "Uncommitted changes and branch vs main",
3961
+ digests: "Doc excerpts (opening lines; read the file for the rest)",
3956
3962
  backlog: "Open work noted in repo files (context, NOT tasks to start)"
3957
3963
  };
3958
3964
  render = (secs) => [HEADER, ...secs.map((s) => `### ${TITLES[s.key]}
@@ -5050,13 +5056,13 @@ function createCodegenTools(fileStore, config) {
5050
5056
  if (!buildRes.ok) {
5051
5057
  throw new Error(`Failed to fetch build result: ${buildRes.status}`);
5052
5058
  }
5053
- const build = await buildRes.json();
5054
- if (!build.result) {
5059
+ const build2 = await buildRes.json();
5060
+ if (!build2.result) {
5055
5061
  throw new Error("Build completed but no files in result");
5056
5062
  }
5057
5063
  let files;
5058
5064
  try {
5059
- const parsed = JSON.parse(build.result);
5065
+ const parsed = JSON.parse(build2.result);
5060
5066
  files = parsed.files ?? [];
5061
5067
  } catch {
5062
5068
  throw new Error("Failed to parse build result");
@@ -9188,11 +9194,11 @@ var init_base = __esm({
9188
9194
  }
9189
9195
  const parts = os.release().split(".");
9190
9196
  const major = Number(parts[0]);
9191
- const build = Number(parts[2] ?? 0);
9197
+ const build2 = Number(parts[2] ?? 0);
9192
9198
  if (major < 10) {
9193
9199
  return true;
9194
9200
  }
9195
- if (major === 10 && build < 10586) {
9201
+ if (major === 10 && build2 < 10586) {
9196
9202
  return true;
9197
9203
  }
9198
9204
  return false;
@@ -42998,7 +43004,7 @@ var require_colors = __commonJS({
42998
43004
  }
42999
43005
  return str.replace(matchOperatorsRe, "\\$&");
43000
43006
  };
43001
- function build(_styles) {
43007
+ function build2(_styles) {
43002
43008
  var builder = function builder2() {
43003
43009
  return applyStyle2.apply(builder2, arguments);
43004
43010
  };
@@ -43013,7 +43019,7 @@ var require_colors = __commonJS({
43013
43019
  ansiStyles3[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles3[key].close), "g");
43014
43020
  ret[key] = {
43015
43021
  get: function() {
43016
- return build(this._styles.concat(key));
43022
+ return build2(this._styles.concat(key));
43017
43023
  }
43018
43024
  };
43019
43025
  });
@@ -43072,7 +43078,7 @@ var require_colors = __commonJS({
43072
43078
  Object.keys(styles5).forEach(function(name) {
43073
43079
  ret[name] = {
43074
43080
  get: function() {
43075
- return build([name]);
43081
+ return build2([name]);
43076
43082
  }
43077
43083
  };
43078
43084
  });
@@ -103698,6 +103704,9 @@ var init_local_toolset = __esm({
103698
103704
  import { execFile as execFile3 } from "node:child_process";
103699
103705
  import { readdir as readdir3, readFile as readFile4 } from "node:fs/promises";
103700
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
+ }
103701
103710
  function firstHeading(md) {
103702
103711
  for (const line of md.split("\n", 40)) {
103703
103712
  const m2 = /^#{1,3}\s+(.+?)\s*$/.exec(line);
@@ -103755,10 +103764,19 @@ async function scanProject(cwd2, io = nodeScanIo) {
103755
103764
  const title = text ? firstHeading(text) : null;
103756
103765
  if (title) docs.push({ path: e.path, title });
103757
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
+ }
103758
103773
  const todo = await attempt("read TODO.md", () => io.readFile(join7(cwd2, "TODO.md")), null);
103759
103774
  const log = await io.git(cwd2, ["log", "--oneline", "-8"]);
103760
103775
  const status = await io.git(cwd2, ["status", "--porcelain"]);
103761
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;
103762
103780
  return {
103763
103781
  problems,
103764
103782
  input: {
@@ -103770,17 +103788,20 @@ async function scanProject(cwd2, io = nodeScanIo) {
103770
103788
  childDirs,
103771
103789
  recentCommits: (log ?? "").split("\n").filter(Boolean),
103772
103790
  docs,
103773
- backlog: todo ? backlogLines(todo) : []
103791
+ backlog: todo ? backlogLines(todo) : [],
103792
+ changes: { files: (status ?? "").split("\n").filter(Boolean), ...vsMain ? { vsMain } : {} },
103793
+ digests
103774
103794
  }
103775
103795
  };
103776
103796
  }
103777
- var SKIP, MAX_DOCS, MAX_BACKLOG, nodeScanIo;
103797
+ var SKIP, MAX_DOCS, MAX_BACKLOG, DIGEST_FILES, nodeScanIo;
103778
103798
  var init_project_card_scan = __esm({
103779
103799
  "src/project-card-scan.ts"() {
103780
103800
  "use strict";
103781
103801
  SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", ".wrangler", ".turbo", "coverage", ".worktrees", ".superpowers", ".remember"]);
103782
103802
  MAX_DOCS = 15;
103783
103803
  MAX_BACKLOG = 6;
103804
+ DIGEST_FILES = ["CLAUDE.md", "TODO.md", "BUGS.md", "ISSUES.md", "CHANGELOG.md", "README.md"];
103784
103805
  nodeScanIo = {
103785
103806
  async readdir(path12) {
103786
103807
  const entries = await readdir3(path12, { withFileTypes: true });
@@ -103802,25 +103823,27 @@ var init_project_card_scan = __esm({
103802
103823
  });
103803
103824
 
103804
103825
  // 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;
103826
+ async function build(cwd2, io) {
103811
103827
  try {
103812
- const { input, problems } = await scanProject(cwd2, deps.io ?? nodeScanIo);
103828
+ const { input, problems } = await scanProject(cwd2, io);
103813
103829
  const text = renderProjectCard(input);
103814
- state = {
103830
+ return {
103815
103831
  text,
103816
103832
  statusLine: `project card: ${text.length} chars, ${input.topLevel.length} top-level entries${problems.length ? `, ${problems.length} scan problem(s): ${problems.join("; ")}` : ""}`
103817
103833
  };
103818
103834
  } catch (err) {
103819
103835
  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)})` };
103836
+ return { text: "", statusLine: `project card: not built (${err instanceof Error ? err.message : String(err)})` };
103821
103837
  }
103822
- cached = { cwd: cwd2, at: now(), state };
103823
- return state;
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;
103824
103847
  }
103825
103848
  var TTL_MS, cached;
103826
103849
  var init_project_card_runtime = __esm({
@@ -104829,6 +104852,9 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
104829
104852
  return () => clearInterval(timer);
104830
104853
  }, [turnStartedAt]);
104831
104854
  const projectRoot = (0, import_react30.useMemo)(() => process.cwd(), []);
104855
+ (0, import_react30.useEffect)(() => {
104856
+ void loadProjectCard(projectRoot);
104857
+ }, [projectRoot]);
104832
104858
  const sessionIdRef = (0, import_react30.useRef)(null);
104833
104859
  const sessionCreatedAtRef = (0, import_react30.useRef)(null);
104834
104860
  const sessionTokensRef = (0, import_react30.useRef)({ input: 0, output: 0 });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spacecode",
3
- "version": "2.8.0",
3
+ "version": "2.8.1",
4
4
  "description": "SpaceCode - launch cloud dev environments with AI coding agents",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",