@skitterbyte/skitterspec 22.3.0 → 22.3.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.
@@ -188,6 +188,7 @@ through to a documented conservative default; see the field notes below.
188
188
  // deliberate: the file it names then belongs to some other spec, and the safe
189
189
  // failure is a refusal you clear with /commit, not a stranger's file swept
190
190
  // into your commit. Default: none — a spec owns only its own folder.
191
+
191
192
  "spec": {
192
193
  "companionPaths": []
193
194
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skitterbyte/skitterspec",
3
- "version": "22.3.0",
3
+ "version": "22.3.1",
4
4
  "description": "Spec-driven development for Claude Code \u2014 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
@@ -2576,18 +2576,58 @@ function specEnvReviewAllow(dir, config, tier, flags) {
2576
2576
  }
2577
2577
 
2578
2578
  // `review skip` — move on without a verdict, on the record.
2579
- function specEnvReviewSkip(dir, config, reason, flags) {
2579
+ /**
2580
+ * Split `skip`'s positionals into a spec and a reason.
2581
+ *
2582
+ * WHY IT IS NOT SIMPLY `positional[1]`. It was, and it made the gate's own
2583
+ * documented exit unreachable: a spec whose worktree is gone resolves neither
2584
+ * from the cwd nor from a sole provisioned spec, so the refusal told the
2585
+ * operator to "name the one you mean" — a thing the command could not accept —
2586
+ * while its worktree, the other half of that advice, no longer existed. The
2587
+ * gate was then permanent, and clearing it meant hand-editing a gitignored
2588
+ * sidecar. `review gate <spec>` and `review <spec> --claim-since` both take a
2589
+ * name; this was the one that did not.
2590
+ *
2591
+ * TWO POSITIONALS are unambiguous: spec, then reason.
2592
+ *
2593
+ * ONE IS DECIDED BY A POSITIVE SIGNAL (`.claude/rules/negative-checks.md`
2594
+ * rule 1) — it is a spec only where it RESOLVES to one. `findSpecFolder`
2595
+ * matches a bucket folder by exact basename, so nothing fuzzy gets in: a reason
2596
+ * has to be spelled exactly like a spec folder to be read as one, and a reason
2597
+ * that is spelled exactly like a spec folder is a person naming a spec. Every
2598
+ * cannot-tell — an unknown name, an unresolvable repo, a thrown resolver —
2599
+ * falls to the reason, which is the behaviour this command has always had and
2600
+ * the harmless branch.
2601
+ *
2602
+ * THE PROBE NEVER DECIDES THE OUTCOME, only which slot the word fills. What
2603
+ * happens next is the ordinary resolution, refusal text and all.
2604
+ */
2605
+ function splitSkipArgs(dir, config, rest) {
2606
+ const args = (rest || []).filter((a) => a !== undefined && a !== null)
2607
+ if (args.length >= 2) return { specArg: args[0], reason: args[1] }
2608
+ if (args.length === 1) {
2609
+ return gateTarget(dir, config, args[0]).spec
2610
+ ? { specArg: args[0], reason: null }
2611
+ : { specArg: null, reason: args[0] }
2612
+ }
2613
+ return { specArg: null, reason: null }
2614
+ }
2615
+
2616
+ function specEnvReviewSkip(dir, config, rest, flags) {
2617
+ const { specArg, reason } = splitSkipArgs(dir, config, rest)
2580
2618
  const said = String(reason || '').trim()
2581
2619
  if (!said) {
2582
2620
  // The reason IS the feature. A skip with no reason is the silence this
2583
- // whole gate exists to replace, so it is refused rather than defaulted.
2621
+ // whole gate exists to replace, so it is refused rather than defaulted —
2622
+ // and naming the spec is not giving one, which is why the probe above puts
2623
+ // a lone spec name in the spec slot and leaves this empty.
2584
2624
  process.stdout.write(
2585
- 'spec-env review skip: needs a reason — skitterspec spec-env review skip "<why>"\n',
2625
+ 'spec-env review skip: needs a reason — skitterspec spec-env review skip [<spec>] "<why>"\n',
2586
2626
  )
2587
2627
  process.exitCode = 1
2588
2628
  return
2589
2629
  }
2590
- const target = gateTarget(dir, config, null)
2630
+ const target = gateTarget(dir, config, specArg)
2591
2631
  if (!target.spec) {
2592
2632
  process.stdout.write(`spec-env review skip: cannot tell which spec — ${target.reason}\n`)
2593
2633
  process.exitCode = 1
@@ -5114,10 +5154,10 @@ async function specEnv(rest) {
5114
5154
  break
5115
5155
  }
5116
5156
  if (positional[0] === 'skip') {
5117
- // The one positional is the REASON, not a spec: the two are
5118
- // indistinguishable as free text, and the spec is the one thing this
5119
- // engine can already resolve from where you are standing.
5120
- specEnvReviewSkip(dir, config, positional[1], flags)
5157
+ // `skip [<spec>] "<why>"`. The spec is usually resolvable from where you
5158
+ // are standing, which is why it stays optional — but not always, and
5159
+ // `splitSkipArgs` carries why that mattered enough to change.
5160
+ specEnvReviewSkip(dir, config, positional.slice(1), flags)
5121
5161
  break
5122
5162
  }
5123
5163
  await specEnvReview(dir, config, positional[0], flags, invokedFrom)
@@ -5134,7 +5174,7 @@ async function specEnv(rest) {
5134
5174
  ' review arm [spec] [--phase <n>] a phase ended — its diff now owes a verdict\n' +
5135
5175
  ' review gate [spec] [--check] [--json] is one owed? --check exits non-zero if so\n' +
5136
5176
  ' [--for-command <cmdline>] ...but only when that command is a git commit\n' +
5137
- ' review skip "<reason>" move on without one, on the record\n' +
5177
+ ' review skip [spec] "<reason>" move on without one, on the record\n' +
5138
5178
  ' review gate [spec] --offered record that the bypass was offered (spends it)\n' +
5139
5179
  ' review wait [spec] --heartbeat <secs> proof-of-life cadence on stderr; 0 disables\n' +
5140
5180
  ' review wait [spec] --since <iso> block until a verdict arrives ([--timeout <s>])\n' +