@skitterbyte/skitterspec 8.0.0 → 8.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 +1 -1
- package/src/cli.js +25 -13
- package/src/env/resolve.js +15 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skitterbyte/skitterspec",
|
|
3
|
-
"version": "8.0.
|
|
3
|
+
"version": "8.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,14 @@ const {
|
|
|
17
17
|
freeSlot,
|
|
18
18
|
portOffset,
|
|
19
19
|
} = require('./env/registry.js')
|
|
20
|
-
const {
|
|
20
|
+
const {
|
|
21
|
+
resolveSpec,
|
|
22
|
+
resolveBaseBranch,
|
|
23
|
+
resolvePrimaryCheckout,
|
|
24
|
+
repoInfo,
|
|
25
|
+
expandTokens,
|
|
26
|
+
splitPrefix,
|
|
27
|
+
} = require('./env/resolve.js')
|
|
21
28
|
const { ensureWorktreeDirTrusted } = require('./env/trust.js')
|
|
22
29
|
const { planUp } = require('./env/provision.js')
|
|
23
30
|
const { planDown } = require('./env/teardown.js')
|
|
@@ -336,26 +343,23 @@ function specEnvIntegrate(dir, config, specArg) {
|
|
|
336
343
|
return
|
|
337
344
|
}
|
|
338
345
|
|
|
339
|
-
//
|
|
340
|
-
//
|
|
341
|
-
//
|
|
342
|
-
//
|
|
343
|
-
const commonDir = gitReader(dir)(['rev-parse', '--git-common-dir'])
|
|
344
|
-
const mainRepoPath = commonDir ? path.dirname(path.resolve(dir, commonDir)) : dir
|
|
345
|
-
|
|
346
|
+
// `dir` is already anchored on the primary checkout by the dispatch, so it is
|
|
347
|
+
// both where the spec resolves and the target of the fast-forward — /spec-complete
|
|
348
|
+
// can run this from inside the worktree and still land on main.
|
|
349
|
+
//
|
|
346
350
|
// A spec authored entirely on its branch may not exist in the primary
|
|
347
351
|
// checkout's specs/** (it was never committed to base) — but its worktree
|
|
348
352
|
// does, and the worktree path is derivable from config without the folder.
|
|
349
353
|
// Offer it as a fallback search location so integrate can still find the spec.
|
|
350
354
|
const { slug } = splitPrefix(path.basename(specArg))
|
|
351
|
-
const { repo, repoSlug } = repoInfo(
|
|
355
|
+
const { repo, repoSlug } = repoInfo(dir)
|
|
352
356
|
const wtTokens = { repo, repoSlug, slug }
|
|
353
357
|
const worktreeGuess = path.resolve(
|
|
354
|
-
|
|
358
|
+
dir,
|
|
355
359
|
expandTokens(config.worktree.root, wtTokens),
|
|
356
360
|
expandTokens(config.worktree.folderPattern, wtTokens),
|
|
357
361
|
)
|
|
358
|
-
const spec = resolveSpec(specArg,
|
|
362
|
+
const spec = resolveSpec(specArg, dir, config, { searchDirs: [worktreeGuess] })
|
|
359
363
|
|
|
360
364
|
if (!fs.existsSync(spec.worktreePath)) {
|
|
361
365
|
process.stdout.write(
|
|
@@ -364,14 +368,19 @@ function specEnvIntegrate(dir, config, specArg) {
|
|
|
364
368
|
return
|
|
365
369
|
}
|
|
366
370
|
|
|
367
|
-
const base = resolveBaseBranch(config, gitReader(
|
|
371
|
+
const base = resolveBaseBranch(config, gitReader(dir))
|
|
368
372
|
const wtGit = gitReader(spec.worktreePath)
|
|
369
373
|
const status = wtGit(['status', '--porcelain'])
|
|
370
374
|
const dirty = status !== null && status.length > 0
|
|
371
375
|
const ahead = wtGit(['rev-list', '--count', `${base}..HEAD`])
|
|
372
376
|
const aheadOfBase = ahead !== null && Number(ahead) > 0
|
|
373
377
|
|
|
374
|
-
const plan = planIntegrate(spec, config, {
|
|
378
|
+
const plan = planIntegrate(spec, config, {
|
|
379
|
+
worktreeState: { dirty },
|
|
380
|
+
base,
|
|
381
|
+
aheadOfBase,
|
|
382
|
+
mainRepoPath: dir,
|
|
383
|
+
})
|
|
375
384
|
|
|
376
385
|
if (plan.blocked) {
|
|
377
386
|
process.stdout.write(`spec-env integrate: blocked — ${plan.reason}.\n`)
|
|
@@ -588,6 +597,9 @@ async function specEnv(rest) {
|
|
|
588
597
|
else positional.push(args[i])
|
|
589
598
|
}
|
|
590
599
|
dir = path.resolve(dir)
|
|
600
|
+
// Anchor on the primary checkout so every subcommand resolves {repo}, worktree
|
|
601
|
+
// paths, and the registry identically whether run from main or a worktree.
|
|
602
|
+
dir = resolvePrimaryCheckout(dir, gitReader(dir))
|
|
591
603
|
|
|
592
604
|
const { config, present } = loadEnvConfig(dir)
|
|
593
605
|
if (!present) {
|
package/src/env/resolve.js
CHANGED
|
@@ -151,6 +151,20 @@ function resolveBaseBranch(config, git) {
|
|
|
151
151
|
return 'main'
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Resolve `dir` to the primary checkout root — the parent of the shared git dir.
|
|
156
|
+
* From the primary checkout `git rev-parse --git-common-dir` is `.git` (relative),
|
|
157
|
+
* so the parent is `dir`; from a linked worktree it's the absolute `<main>/.git`,
|
|
158
|
+
* so the parent is `<main>`. Anchoring every `spec-env` command here means they
|
|
159
|
+
* resolve `{repo}` / worktree paths / the registry identically whether run from
|
|
160
|
+
* `main` or a worktree. `git(args)` returns trimmed stdout or `null` (not a repo)
|
|
161
|
+
* — injected for testability; a `null` degrades to `dir` (today's behaviour).
|
|
162
|
+
*/
|
|
163
|
+
function resolvePrimaryCheckout(dir, git) {
|
|
164
|
+
const common = git(['rev-parse', '--git-common-dir'])
|
|
165
|
+
return common ? path.dirname(path.resolve(dir, common)) : dir
|
|
166
|
+
}
|
|
167
|
+
|
|
154
168
|
/**
|
|
155
169
|
* Resolve a spec argument to its identity + isolation coordinates.
|
|
156
170
|
* Throws a clear Error when the spec folder can't be found.
|
|
@@ -193,6 +207,7 @@ function resolveSpec(specArg, dir, config, opts = {}) {
|
|
|
193
207
|
module.exports = {
|
|
194
208
|
resolveSpec,
|
|
195
209
|
resolveBaseBranch,
|
|
210
|
+
resolvePrimaryCheckout,
|
|
196
211
|
branchFor,
|
|
197
212
|
splitPrefix,
|
|
198
213
|
repoInfo,
|