@skitterbyte/skitterspec 2.0.0 → 2.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skitterbyte/skitterspec",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
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
@@ -17,7 +17,7 @@ const {
17
17
  freeSlot,
18
18
  portOffset,
19
19
  } = require('./env/registry.js')
20
- const { resolveSpec, resolveBaseBranch } = require('./env/resolve.js')
20
+ const { resolveSpec, resolveBaseBranch, repoInfo, expandTokens, splitPrefix } = require('./env/resolve.js')
21
21
  const { ensureWorktreeDirTrusted } = require('./env/trust.js')
22
22
  const { planUp } = require('./env/provision.js')
23
23
  const { planDown } = require('./env/teardown.js')
@@ -332,7 +332,19 @@ function specEnvIntegrate(dir, config, specArg) {
332
332
  const commonDir = gitReader(dir)(['rev-parse', '--git-common-dir'])
333
333
  const mainRepoPath = commonDir ? path.dirname(path.resolve(dir, commonDir)) : dir
334
334
 
335
- const spec = resolveSpec(specArg, mainRepoPath, config)
335
+ // A spec authored entirely on its branch may not exist in the primary
336
+ // checkout's specs/** (it was never committed to base) — but its worktree
337
+ // does, and the worktree path is derivable from config without the folder.
338
+ // Offer it as a fallback search location so integrate can still find the spec.
339
+ const { slug } = splitPrefix(path.basename(specArg))
340
+ const { repo, repoSlug } = repoInfo(mainRepoPath)
341
+ const wtTokens = { repo, repoSlug, slug }
342
+ const worktreeGuess = path.resolve(
343
+ mainRepoPath,
344
+ expandTokens(config.worktree.root, wtTokens),
345
+ expandTokens(config.worktree.folderPattern, wtTokens),
346
+ )
347
+ const spec = resolveSpec(specArg, mainRepoPath, config, { searchDirs: [worktreeGuess] })
336
348
 
337
349
  if (!fs.existsSync(spec.worktreePath)) {
338
350
  process.stdout.write(
@@ -20,12 +20,17 @@ const BUCKETS = ['backlog', 'in-progress', 'complete', 'cancelled']
20
20
 
21
21
  // Find the spec folder under specs/<bucket>/<name>. `specArg` may be a bare
22
22
  // folder name or a path — only its basename is matched against the buckets.
23
- function findSpecFolder(specArg, dir) {
23
+ // Searches `dir` first, then any `extraDirs` in order — so a caller (e.g.
24
+ // `spec-env integrate`) can fall back to a worktree checkout for a spec that
25
+ // was authored on its branch and never committed to the primary checkout.
26
+ function findSpecFolder(specArg, dir, extraDirs = []) {
24
27
  const name = path.basename(specArg)
25
- for (const bucket of BUCKETS) {
26
- const abs = path.join(dir, 'specs', bucket, name)
27
- if (fs.existsSync(abs) && fs.statSync(abs).isDirectory()) {
28
- return { folder: name, bucket, path: abs }
28
+ for (const root of [dir, ...extraDirs]) {
29
+ for (const bucket of BUCKETS) {
30
+ const abs = path.join(root, 'specs', bucket, name)
31
+ if (fs.existsSync(abs) && fs.statSync(abs).isDirectory()) {
32
+ return { folder: name, bucket, path: abs }
33
+ }
29
34
  }
30
35
  }
31
36
  return null
@@ -149,9 +154,13 @@ function resolveBaseBranch(config, git) {
149
154
  /**
150
155
  * Resolve a spec argument to its identity + isolation coordinates.
151
156
  * Throws a clear Error when the spec folder can't be found.
157
+ *
158
+ * `opts.searchDirs` adds fallback checkout roots to look under (after `dir`) when
159
+ * locating the spec folder; identity/coordinate tokens still expand against `dir`
160
+ * (the primary checkout), so a worktree-only spec resolves to the right base.
152
161
  */
153
- function resolveSpec(specArg, dir, config) {
154
- const found = findSpecFolder(specArg, dir)
162
+ function resolveSpec(specArg, dir, config, opts = {}) {
163
+ const found = findSpecFolder(specArg, dir, opts.searchDirs || [])
155
164
  if (!found) {
156
165
  throw new Error(`spec not found under specs/**: ${specArg}`)
157
166
  }