spexcode 0.2.3 → 0.2.4

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/README.md CHANGED
@@ -14,9 +14,9 @@
14
14
  Spec-driven development with AI agents in the loop. SpexCode keeps a versioned tree of specs inside
15
15
  your git repo, links every spec to the code it governs, and runs a session manager that dispatches
16
16
  coding agents into isolated worktrees. You review and merge; the tool keeps intent and
17
- implementation from drifting apart. (All screenshots below are this very repo on its own board.)
17
+ implementation from drifting apart.
18
18
 
19
- [![This repo's spec tree, grown from its own git history](docs/spec-tree-hero.gif)](https://spexcode.net/assets/spec-tree-growth.mp4)
19
+ [![Watch: this repo's spec tree grow from its git history](docs/spec-tree-poster.jpg)](https://spexcode.net/assets/spec-tree-growth.mp4)
20
20
 
21
21
  <sub>▶ This repo's own spec tree, replayed from its git history — 160 spec nodes growing over three weeks. Click for the [full video](https://spexcode.net/assets/spec-tree-growth.mp4).</sub>
22
22
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spexcode",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "type": "module",
5
5
  "description": "SpexCode — a spec-driven, self-developing dev tool. The `spex` CLI + spec server reads the .spec tree and its git history, and serves the dashboard.",
6
6
  "license": "MIT",
@@ -4,6 +4,7 @@ import { randomUUID } from 'node:crypto'
4
4
  import { readFileSync, writeFileSync, appendFileSync, existsSync, renameSync, mkdirSync, rmSync, readdirSync, realpathSync, statSync } from 'node:fs'
5
5
  import { join, dirname, relative, isAbsolute } from 'node:path'
6
6
  import { fileURLToPath } from 'node:url'
7
+ import { linkUntrackedSpecSources } from './worktree-sources.js'
7
8
  import { git, gitA, gitTry, repoRoot, mergeBaseDiff, mergeConflicts, type ReviewDiffFile } from './git.js'
8
9
  import { loadSpecs } from './specs.js'
9
10
  import { defaultHarness, defaultLauncher, harnessById, resolveLauncher, rvSock, rendezvousListening, type Harness, type DispatchResult, type PaneProbe, type ProcTable } from './harness.js'
@@ -1061,6 +1062,9 @@ export async function newSession(node: string | null, prompt: string, parent: st
1061
1062
  const branch = `node/${slug}`
1062
1063
  const path = join(mainRoot(), '.worktrees', slug)
1063
1064
  await gitA(['-C', mainRoot(), 'worktree', 'add', '-b', branch, path, mainBranch()])
1065
+ // a private-overlay repo keeps the spec sources out of git, so the checkout alone leaves this worktree
1066
+ // spec-blind and hook-dead — link them from main ([[private-overlay]]; no-op on a default-mode repo).
1067
+ linkUntrackedSpecSources(mainRoot(), path)
1064
1068
  // prepared but NOT launched: enters the queue as `queued`. drainQueue() below launches it at once when a
1065
1069
  // slot is free, else it waits — durable as a global record (+ its worktree), so it survives a backend
1066
1070
  // restart and is still findable. governed:true — this is a DASHBOARD/CLI-launched session, so it feeds the
@@ -0,0 +1,19 @@
1
+ import { existsSync, symlinkSync } from 'node:fs'
2
+ import { join } from 'node:path'
3
+
4
+ // @@@ worktree-sources ([[private-overlay]]) - a fresh session worktree checks out only TRACKED content, and
5
+ // private mode keeps `.spec` + `spexcode.json` untracked (spexcode.local.json is untracked in BOTH modes) —
6
+ // so git alone hands a dispatched agent a worktree with NO spec tree: every hook handler script is absent
7
+ // (the dispatcher silently runs nothing), spex sees zero nodes, and the gate re-renders per event over empty
8
+ // config roots. Link those sources from the main checkout instead. On a default-mode repo the checkout
9
+ // already carries them, so each link guard no-ops — one mechanism, never a mode branch. A failure degrades
10
+ // that worker (no hooks, no specs), so it is reported, not swallowed.
11
+ export function linkUntrackedSpecSources(main: string, wt: string): void {
12
+ for (const f of ['.spec', 'spexcode.json', 'spexcode.local.json']) {
13
+ try {
14
+ if (existsSync(join(main, f)) && !existsSync(join(wt, f))) symlinkSync(join(main, f), join(wt, f))
15
+ } catch (e) {
16
+ console.error(`spexcode: could not link ${f} from ${main} into worktree ${wt} — that worker runs without it (${e})`)
17
+ }
18
+ }
19
+ }