@skitterbyte/skitterspec 14.0.3 → 15.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/README.md CHANGED
@@ -29,7 +29,7 @@ Ticketing sync is a **separate superset you install instead of this one**:
29
29
  | Install | You get |
30
30
  |---------|---------|
31
31
  | `@skitterbyte/skitterspec` | The base filesystem workflow. No tracker. |
32
- | `@skitterbyte/skitterspec-linear` | Everything here **plus** Linear hybrid-sync (`/spec-status` · `/spec-pull` · `/spec-push`, the `spec-sync` CLI). |
32
+ | `@skitterbyte/skitterspec-linear` | Everything here **plus** one-way Linear sync (`/spec-status` · `/spec-push`, the `spec-sync` CLI) — repo canonical, Linear a generated mirror. |
33
33
 
34
34
  Install exactly one — the superset is a strict superset of this package.
35
35
 
@@ -68,13 +68,15 @@ Beneath it, `skitterspec spec-env live <take|release|abort|status>` is the engin
68
68
  tracker-free: it knows nothing about any specific ticketing system. A
69
69
  ticketing provider is installed as its own distribution that plugs into two named
70
70
  **seams** in the shared skills (`/spec` Phase E, `/spec-go` step 3b) and fulfils a
71
- skill-name + CLI contract it ships `/spec-status` (read-only per-field
72
- divergence), `/spec-pull` (tracker→repo), and `/spec-push` (repo→tracker), backed
73
- by a `spec-sync` CLI, three-way merged against a committed base sidecar. When a
74
- provider is present, `/spec` also links the spec to the tracker and `/spec-go`
75
- pulls first. With no provider installed the seams are empty and every skill
76
- behaves as a plain filesystem workflow. See the provider package's own docs for
77
- its config and field reference.
71
+ skill-name + CLI contract. Sync is **one-way**: the repo is the source of truth
72
+ and the tracker is a **generated mirror**. It ships `/spec-push` (repo→tracker;
73
+ computes a create/update plan against a committed last-pushed snapshot and applies
74
+ it) and `/spec-status` (read-only drift report what would push, and whether the
75
+ tracker's workflow-state drifted), backed by a `spec-sync` CLI. There is no
76
+ content pull the tracker is never read back or merged. When a provider is
77
+ present, `/spec` also links the spec to the tracker. With no provider installed
78
+ the seams are empty and every skill behaves as a plain filesystem workflow. See
79
+ the provider package's own docs for its config and field reference.
78
80
 
79
81
  ## Project conventions (fill this in)
80
82
 
@@ -217,11 +217,11 @@ finish as above.
217
217
  ## Phase E — link to a ticketing provider (only if one is installed)
218
218
 
219
219
  **Only when a ticketing provider is installed and configured** (it ships the
220
- `/spec-push` · `/spec-pull` · `/spec-status` skills and a provider config under
220
+ `/spec-push` · `/spec-status` skills and a provider config under
221
221
  `specs/.core/`). If none is present, skip this phase entirely — the spec stays
222
222
  local-only and `/spec` behaves exactly as above. When a provider is present, link
223
223
  the spec to the tracker after writing it, so status and discussion live there
224
- while the repo stays the co-authoring surface — follow the provider's link steps
224
+ while the repo stays the source of truth — follow the provider's link steps
225
225
  below (nothing to do here without one).
226
226
 
227
227
 
@@ -129,12 +129,11 @@ Before writing any code for this phase, get the workspace clean:
129
129
  next phase on top of an uncommitted one. (Skip if this is the first phase —
130
130
  there's nothing prior to commit.)
131
131
 
132
- ## 3b. Pull from the tracker first (only if a provider is installed)
132
+ ## 3b. Sync with the tracker (only if a provider is installed)
133
133
 
134
- **Only when a ticketing provider with a `/spec-pull` skill is installed** and the
135
- spec is linked to the tracker. Otherwise skip this step — no provider means zero
136
- change to the flow below. Follow the provider's pull steps below (nothing to do
137
- here without one).
134
+ **Only when a ticketing provider is installed** and the spec is linked to the
135
+ tracker. Otherwise skip this step — no provider means zero change to the flow
136
+ below. Follow the provider's steps below (nothing to do here without one).
138
137
 
139
138
 
140
139
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skitterbyte/skitterspec",
3
- "version": "14.0.3",
3
+ "version": "15.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/init.js CHANGED
@@ -259,6 +259,30 @@ function removeRetiredFiles(dir) {
259
259
  }
260
260
  }
261
261
 
262
+ // Prune a file the manifest records as managed but the current package no longer
263
+ // ships — a retired skill (e.g. `spec-pull` after the one-way switch), rule, or
264
+ // template. Without this, upgrading leaves a live, model-visible skill on disk
265
+ // whose instructions invoke a command that no longer exists. Delete it only when
266
+ // PRISTINE (still matches the hash we last wrote) so a user edit is never lost; a
267
+ // customized retired file is kept with a warning. An emptied skill folder is
268
+ // removed. Takes the pre-flush manifest (which still holds the retired entries).
269
+ function pruneRetiredManaged(dir, manifest) {
270
+ const managedRel = new Set(managedTargets(dir).map((t) => t.relPath))
271
+ for (const relPath of Object.keys(manifest.files || {})) {
272
+ if (managedRel.has(relPath)) continue // still shipped by this version
273
+ const abs = path.join(dir, relPath)
274
+ if (!fs.existsSync(abs)) continue // already gone
275
+ if (managedState(dir, relPath, manifest) === 'customized') {
276
+ report.warnings.push(`retired but kept (you edited it): ${relPath} — delete manually if unused`)
277
+ continue
278
+ }
279
+ fs.unlinkSync(abs)
280
+ report.removed.push(rel(dir, abs))
281
+ const folder = path.dirname(abs)
282
+ if (fs.existsSync(folder) && !fs.readdirSync(folder).length) fs.rmdirSync(folder)
283
+ }
284
+ }
285
+
262
286
  // Scaffold the opt-in isolation templates into specs/.core/ (the example config
263
287
  // + its field docs). Copied, not activated: the feature stays off until the
264
288
  // consumer copies env.config.json.example → env.config.json.
@@ -409,6 +433,7 @@ function resync(dir, { force = false, claudeMd = true } = {}) {
409
433
  for (const t of managedTargets(dir)) resyncManagedFile(dir, t, manifest, force)
410
434
  installFolders(dir)
411
435
  removeRetiredFiles(dir)
436
+ pruneRetiredManaged(dir, manifest)
412
437
  if (claudeMd) installClaudeMd(dir, { mode: 'update' })
413
438
  flushManifest(dir)
414
439
  printReport(dir, 'resync')