@sporhq/spor 0.2.6 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,6 @@
2
2
  "name": "spor",
3
3
  "displayName": "Spor Context Compiler",
4
4
  "description": "Maintains a typed, versioned knowledge graph and compiles compact briefings from it: session-start injection, per-prompt relevance digests, capture at discovery, end-of-session distillation, decision queue.",
5
- "version": "0.2.6",
5
+ "version": "0.2.7",
6
6
  "author": { "name": "losthammer" }
7
7
  }
package/README.md CHANGED
@@ -24,21 +24,8 @@ Requires Node 20+ and nothing else — the client is zero-dependency. To run
24
24
  from a checkout instead (e.g. to hack on it), clone the repo and `npm link`
25
25
  from its root; that symlinks the same two commands onto your PATH.
26
26
 
27
- Then create the graph home (this is yours, kept outside any code repo):
28
-
29
- ```bash
30
- spor init # creates ~/.spor/nodes, git-inits it, writes .gitignore
31
- ```
32
-
33
- `spor init` is idempotent. `spor status` then tells you the resolved mode,
34
- graph, project, and (in remote mode) server health and identity — run it any
35
- time you're unsure whether Spor is active or which graph you're on. (Without
36
- the `spor` CLI on your PATH the equivalent is
37
- `mkdir -p ~/.spor/nodes && git -C ~/.spor init && printf 'journal/\n' > ~/.spor/.gitignore`.)
38
-
39
- Then install for your agent. One verb wires up any supported host — it
40
- resolves the adapter manifest to this checkout and drops it into the host's
41
- config:
27
+ Wire Spor into your agent. One verb resolves the adapter manifest to this
28
+ install and drops it into the host's config:
42
29
 
43
30
  ```bash
44
31
  spor install claude # Claude Code (via its plugin CLI — no marketplace browsing)
@@ -50,27 +37,33 @@ spor install # no host => list the hosts detected on this machine
50
37
  per-repo config. `--all` installs every detected host, `--print` is a dry run,
51
38
  and `--server <url> --token <tok>` also points the client at a team graph in
52
39
  the same step. Re-running is idempotent — it refreshes the path and never
53
- duplicates your other hooks.
40
+ duplicates your other hooks. (In Claude Code you can also install by hand:
41
+ `/plugin marketplace add sporhq/spor` then `/plugin install spor@spor`.)
54
42
 
55
- In Claude Code you can still install from the marketplace by hand if you
56
- prefer:
43
+ Then onboard a repo one command, from inside it:
57
44
 
45
+ ```bash
46
+ cd ~/my-repo && spor dispatch --backfill
58
47
  ```
59
- /plugin marketplace add sporhq/spor
60
- /plugin install spor@spor
61
- ```
48
+
49
+ That does the whole setup in one step: creates your graph home if it doesn't
50
+ exist yet (`~/.spor/nodes`, git-initialised), registers the repo so Spor knows
51
+ where it lives on this machine, makes sure Spor is enabled for it, and launches
52
+ the `/spor:backfill` agent in a Claude Code background session — it mines git
53
+ history, design docs, and issue trackers (edges first) and proposes how to
54
+ group your repos into projects. Watch or attach to it with `claude agents`.
55
+ Re-run it whenever you add a repo, or skip it entirely and just work —
56
+ distillation grows the graph one session at a time.
57
+
58
+ `spor status` tells you the resolved mode, graph, project, and (on a team
59
+ graph) server health and identity — run it any time you're unsure whether Spor
60
+ is active or which graph you're on. `spor init` does the graph-home setup on
61
+ its own if you'd rather not dispatch anything yet.
62
62
 
63
63
  For the per-host event mapping, fidelity notes, distiller backend, and the
64
64
  `AGENTS.md` fallback for hosts with no hook support, see
65
65
  [adapters/](adapters/).
66
66
 
67
- To start with a populated graph, run `/spor:backfill` — the onboarding door. It
68
- dispatches the heavy mining (git history, design docs, issue trackers, edges
69
- first) to the bundled `spor-backfill` **subagent**, which runs in its own
70
- context, and then proposes how to group your repos into projects (re-run it as
71
- you add repos). Or skip it and just work — distillation grows the graph one
72
- session at a time.
73
-
74
67
  ## Dispatching background agents
75
68
 
76
69
  `spor dispatch` hands a task to Claude Code's background-agent machinery
package/bin/spor.js CHANGED
@@ -101,7 +101,10 @@ function nodeCount(nodesDir) {
101
101
  }
102
102
  }
103
103
 
104
- function cmdInit(cfg) {
104
+ // Idempotently create the local graph home (nodes/, git, .gitignore). Returns
105
+ // { home, nodesDir, created } and prints nothing — callers do their own UX.
106
+ // Shared by `spor init` and the `spor dispatch --backfill` onboarding path.
107
+ function ensureGraphHome(cfg) {
105
108
  const home = cfg.graphHome();
106
109
  const nodesDir = path.join(home, "nodes");
107
110
  let created = false;
@@ -122,6 +125,11 @@ function cmdInit(cfg) {
122
125
  /* non-fatal */
123
126
  }
124
127
  }
128
+ return { home, nodesDir, created };
129
+ }
130
+
131
+ function cmdInit(cfg) {
132
+ const { home, nodesDir, created } = ensureGraphHome(cfg);
125
133
  out(`${created ? "Created" : "Graph already present at"} ${home}`);
126
134
  out(` nodes: ${nodesDir} (${nodeCount(nodesDir) ?? 0} nodes)`);
127
135
  out(` mode: ${cfg.mode()}`);
@@ -1004,6 +1012,45 @@ function shellQuote(s) {
1004
1012
  return /[^\w./:-]/.test(s) ? `'${String(s).replace(/'/g, "'\\''")}'` : s;
1005
1013
  }
1006
1014
 
1015
+ // Re-enable Spor for a repo by merging { enabled: true } into its committable
1016
+ // .spor.json (and clearing a `mode: off`, which also disables). Used by the
1017
+ // --backfill onboarding to repair a repo a prior `spor disable` turned off.
1018
+ function enableRepoAt(dir) {
1019
+ const file = path.join(dir, ".spor.json");
1020
+ let data = {};
1021
+ try {
1022
+ data = JSON.parse(fs.readFileSync(file, "utf8")) || {};
1023
+ } catch {
1024
+ /* absent or malformed — start fresh */
1025
+ }
1026
+ data.enabled = true;
1027
+ if (data.mode === "off") delete data.mode;
1028
+ try {
1029
+ fs.writeFileSync(file, JSON.stringify(data, null, 2) + "\n");
1030
+ } catch {
1031
+ /* non-fatal */
1032
+ }
1033
+ }
1034
+
1035
+ // `spor dispatch --backfill` is the onboarding door (task-spor-cli-dispatch-
1036
+ // background-agents): set the repo up before launching its backfill agent.
1037
+ // Idempotent; prints what it did. The dir-registration happens in cmdDispatch
1038
+ // (it applies to every dispatch), this adds the init + enable steps.
1039
+ function onboardRepo(cfg, dir) {
1040
+ // Init the local graph home — but only in local mode; remote mode keeps the
1041
+ // graph on the server, so there is nothing to create locally.
1042
+ if (cfg.mode() !== "remote") {
1043
+ const r = ensureGraphHome(cfg);
1044
+ out(r.created ? `initialized graph home at ${r.home}` : `graph home ready: ${r.home}`);
1045
+ }
1046
+ // Re-enable the repo if a prior `spor disable` turned it off, so onboarding a
1047
+ // disabled repo actually works instead of silently launching into a no-op.
1048
+ if (!cfg.enabled()) {
1049
+ enableRepoAt(dir);
1050
+ out(`re-enabled Spor for ${dir}`);
1051
+ }
1052
+ }
1053
+
1007
1054
  async function cmdDispatch(cfg, args) {
1008
1055
  const dryRun = args.includes("--print") || args.includes("--dry-run");
1009
1056
  const full = args.includes("--full");
@@ -1089,9 +1136,6 @@ async function cmdDispatch(cfg, args) {
1089
1136
  err(`target dir does not exist: ${res.dir}`);
1090
1137
  return 1;
1091
1138
  }
1092
- // Learn the mapping from this CLI use too.
1093
- u.registerRepo(cfg.graphHome(), res.slug, res.dir);
1094
-
1095
1139
  const prompt = brief
1096
1140
  ? `# Spor briefing (compiled for this task — your standing context)\n\n${brief}\n\n---\n\n# Task\n\n${instruction}\n`
1097
1141
  : instruction;
@@ -1106,12 +1150,26 @@ async function cmdDispatch(cfg, args) {
1106
1150
 
1107
1151
  if (dryRun) {
1108
1152
  out(`dir: ${res.dir} (slug: ${res.slug}, via ${res.source})`);
1153
+ if (backfill) {
1154
+ const steps = [];
1155
+ if (cfg.mode() !== "remote") steps.push(fs.existsSync(cfg.nodesDir()) ? "graph home ready" : "init graph home");
1156
+ steps.push(`register ${res.slug} → ${res.dir}`);
1157
+ if (!cfg.enabled()) steps.push("re-enable repo (currently disabled)");
1158
+ out(`onboard: ${steps.join("; ")}`);
1159
+ }
1109
1160
  out(`brief: ${brief ? `${brief.length} bytes` : "(none — graph had nothing relevant, or --no-brief/--backfill)"}`);
1110
1161
  out(`run: ${claudeBin} ${claudeArgs.slice(0, -1).map(shellQuote).join(" ")} <prompt>`);
1111
1162
  out(`\n--- prompt ---\n${prompt}`);
1112
1163
  return 0;
1113
1164
  }
1114
1165
 
1166
+ // Side effects (real run only — --print writes nothing). --backfill is the
1167
+ // onboarding door, so it sets the repo up (init + enable) first; every
1168
+ // dispatch self-registers the dir it resolved.
1169
+ if (backfill) onboardRepo(cfg, res.dir);
1170
+ u.registerRepo(cfg.graphHome(), res.slug, res.dir);
1171
+ if (backfill) out(`registered ${res.slug} → ${res.dir}; launching the backfill agent…`);
1172
+
1115
1173
  if (claudeBin === "claude" && !hasCmd("claude")) {
1116
1174
  err("claude CLI not on PATH — install Claude Code, then re-run (or 'spor dispatch … --print' to see the prompt).");
1117
1175
  return 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sporhq/spor",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "description": "Spor — a shared memory substrate for teams and agents. Decisions, their reasons, and the traces they leave. Knowledge-graph context compiler: session-start briefings, per-prompt digests, capture at discovery, end-of-session distillation, decision queue.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Anthony Allen",