@skitterbyte/skitterspec 3.0.0 → 8.0.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.
@@ -12,6 +12,7 @@
12
12
  "envFile": ".env",
13
13
  "backupCommand": ""
14
14
  },
15
+ "setup": [],
15
16
  "dev": [],
16
17
  "proxy": {
17
18
  "enabled": true,
@@ -52,6 +52,16 @@ no live `env.config.json` was found.
52
52
  // empty = no backup, volumes dropped directly.
53
53
  },
54
54
 
55
+ // Bootstrap commands `spec-env up <spec>` runs IN the worktree, right after
56
+ // `git worktree add` (before Docker/dev), on every provision including
57
+ // re-attach — so a fresh worktree's dependencies exist and git hooks,
58
+ // typechecks, builds and tests work immediately instead of failing on a
59
+ // missing node_modules. An array, run in order; [] = none. Each string is a
60
+ // shell command; {slug}/{branch}/{worktreePath}/{projectName}/{portOffset}
61
+ // expand (the cwd is already the worktree, so {worktreePath} is usually
62
+ // redundant). Example: ["pnpm install --frozen-lockfile"].
63
+ "setup": [],
64
+
55
65
  // Host dev servers `spec-env dev up <spec>` starts on the spec's port block
56
66
  // (for apps that run via `pnpm dev` on the host, not inside the Docker stack).
57
67
  // An array so UI + API (or more) are supervised independently; [] = none.
@@ -30,6 +30,13 @@ housekeeping below lands on the spec's branch and never on `main`:
30
30
  worktree on a branch forked from `main`, and — only when the spec's
31
31
  `> **Stack:**` header is `worktree + docker` — also brings up its Docker stack.
32
32
  Print the worktree path and the opener command it emits.
33
+ - **Bootstrap the worktree's dependencies.** A fresh worktree has an empty
34
+ working tree — no installed dependencies — so git hooks, typechecks, builds and
35
+ tests fail until they're installed. `spec-env up` prints the project's
36
+ configured **`in the worktree, run:`** commands (from `env.config.json` →
37
+ `setup`, e.g. an install command) — run them in the worktree before doing
38
+ anything else. With no `setup` configured there's nothing to run; set one up if
39
+ agents keep stalling on missing dependencies.
33
40
  - **Trust the worktree for this session.** The engine wrote the printed
34
41
  `trusted:` root into `.claude/settings.local.json` (gitignored) so future
35
42
  sessions trust it automatically — but that file likely won't hot-reload now,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skitterbyte/skitterspec",
3
- "version": "3.0.0",
3
+ "version": "8.0.0",
4
4
  "description": "Spec-driven development for Claude Code — a tracker-free filesystem workflow: lifecycle skills and per-spec isolation. For Linear sync, install @skitterbyte/skitterspec-linear instead.",
5
5
  "keywords": [
6
6
  "claude",
package/src/cli.js CHANGED
@@ -205,6 +205,11 @@ function specEnvUp(dir, config, specArg) {
205
205
  out.push(' run these:')
206
206
  for (const cmd of plan.commands) out.push(` ${cmd}`)
207
207
  if (plan.openCommand) out.push(` ${plan.openCommand}`)
208
+ if (plan.setupCommands.length) {
209
+ out.push('')
210
+ out.push(' in the worktree, run:')
211
+ for (const cmd of plan.setupCommands) out.push(` ${cmd}`)
212
+ }
208
213
  if (plan.envContents) {
209
214
  out.push('')
210
215
  out.push(` write ${config.docker.envFile} in the worktree:`)
package/src/env/config.js CHANGED
@@ -16,6 +16,8 @@
16
16
  * worktree: { root, folderPattern },
17
17
  * docker: { enabled, composeFile, projectNamePattern, portBase,
18
18
  * portsPerSpec, envFile, backupCommand },
19
+ * setup: [ "cmd", ... ], // bootstrap commands run in the worktree right
20
+ * // after `git worktree add` (e.g. install deps); empty = none
19
21
  * dev: [ { name, command, portVar, health?, frontPort? } ], // host dev
20
22
  * // servers started on the spec's port block (empty = none)
21
23
  * proxy: { enabled, host }, // bundled front-door proxy (spec-env connect)
@@ -43,6 +45,10 @@ const DEFAULT_CONFIG = Object.freeze({
43
45
  envFile: '.env',
44
46
  backupCommand: '',
45
47
  }),
48
+ // Bootstrap commands run in the worktree by `spec-env up`, right after
49
+ // `git worktree add` (before Docker/dev), on every provision. Array of shell
50
+ // strings (e.g. "pnpm install"); {slug}/{branch}/… expand. Default: none.
51
+ setup: Object.freeze([]),
46
52
  // Host dev servers started on the spec's port block by `spec-env dev up`.
47
53
  // Each: { name, command, portVar, health?, frontPort? }. Default: none.
48
54
  dev: Object.freeze([]),
@@ -70,6 +76,7 @@ function defaults() {
70
76
  return {
71
77
  worktree: { ...DEFAULT_CONFIG.worktree },
72
78
  docker: { ...DEFAULT_CONFIG.docker },
79
+ setup: [],
73
80
  dev: [],
74
81
  proxy: { ...DEFAULT_CONFIG.proxy },
75
82
  open: { ...DEFAULT_CONFIG.open },
@@ -120,6 +127,21 @@ function normalizeDev(parsed) {
120
127
  return out
121
128
  }
122
129
 
130
+ /**
131
+ * Normalise a parsed `setup` array into bootstrap commands: keep only trimmed,
132
+ * non-empty strings, drop everything else (lenient, like `normalizeDev`) so a
133
+ * stray entry can't crash provisioning.
134
+ */
135
+ function normalizeSetup(parsed) {
136
+ const out = []
137
+ for (const raw of parsed) {
138
+ if (typeof raw !== 'string') continue
139
+ const cmd = raw.trim()
140
+ if (cmd) out.push(cmd)
141
+ }
142
+ return out
143
+ }
144
+
123
145
  /**
124
146
  * Merge a parsed config over the defaults. Only known keys are copied (unknown
125
147
  * keys ignored for forward-compat). Nested objects are merged field-by-field.
@@ -142,6 +164,10 @@ function mergeConfig(base, parsed) {
142
164
  assign(base.docker, parsed.docker, 'backupCommand', 'string?')
143
165
  }
144
166
 
167
+ if (Array.isArray(parsed.setup)) {
168
+ base.setup = normalizeSetup(parsed.setup)
169
+ }
170
+
145
171
  if (Array.isArray(parsed.dev)) {
146
172
  base.dev = normalizeDev(parsed.dev)
147
173
  }
@@ -13,6 +13,7 @@
13
13
 
14
14
  const { portOffset } = require('./registry.js')
15
15
  const { renderEnvFile, expandOpenCommand } = require('./render.js')
16
+ const { expandTokens } = require('./resolve.js')
16
17
 
17
18
  /**
18
19
  * Plan a provisioning run.
@@ -23,7 +24,8 @@ const { renderEnvFile, expandOpenCommand } = require('./render.js')
23
24
  * existed in the registry (re-run → attach, don't clobber).
24
25
  * @param {object} config normalised env config.
25
26
  * @returns {object} { worktreePath, branch, projectName, slot, portOffset,
26
- * envContents, openCommand, commands, attached }
27
+ * envContents, openCommand, commands, setupCommands,
28
+ * attached }
27
29
  */
28
30
  function planUp(spec, alloc, config) {
29
31
  const { slot, attached } = alloc
@@ -41,13 +43,21 @@ function planUp(spec, alloc, config) {
41
43
  ? renderEnvFile({ projectName: spec.projectName, portOffset: offset })
42
44
  : null
43
45
 
44
- const openCommand = expandOpenCommand(config.open.command, {
46
+ const tokens = {
45
47
  worktreePath: spec.worktreePath,
46
48
  slug: spec.slug,
47
49
  branch: spec.branch,
48
50
  projectName: spec.projectName,
49
51
  portOffset: offset === null ? '' : String(offset),
50
- })
52
+ }
53
+
54
+ const openCommand = expandOpenCommand(config.open.command, tokens)
55
+
56
+ // Bootstrap commands run *in the worktree* after `git worktree add` (before
57
+ // Docker/dev), on every provision including re-attach — deps must exist for
58
+ // the worktree to be usable. Kept separate from `commands` (run from the
59
+ // primary checkout root); the CLI prints them under an "in the worktree" head.
60
+ const setupCommands = (config.setup || []).map((cmd) => expandTokens(cmd, tokens))
51
61
 
52
62
  const commands = []
53
63
  // Fresh branch → -b; attach an existing branch/slot → plain form (never clobber).
@@ -69,6 +79,7 @@ function planUp(spec, alloc, config) {
69
79
  envContents,
70
80
  openCommand,
71
81
  commands,
82
+ setupCommands,
72
83
  attached,
73
84
  }
74
85
  }