@thebassclef/lite 1.0.4 → 1.1.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.
Files changed (275) hide show
  1. package/dist/cli.cjs +298 -55
  2. package/dist/cli.js +298 -55
  3. package/dist/index.cjs +1 -1
  4. package/dist/index.d.ts +1 -1
  5. package/dist/index.js +1 -1
  6. package/dist/lite/.claude/agents/architect.md +70 -0
  7. package/dist/lite/.claude/agents/builder.md +114 -0
  8. package/dist/lite/.claude/agents/designer.md +156 -0
  9. package/dist/lite/.claude/agents/reviewer.md +88 -0
  10. package/dist/lite/.claude/hooks/bassclef-sync.sh +85 -27
  11. package/dist/lite/.claude/hooks/longrun-prep-compounding-axis-check.sh +492 -0
  12. package/dist/lite/.claude/hooks/longrun-prep-render-check.sh +236 -0
  13. package/dist/lite/.claude/hooks/persona-name-scrub-check.sh +105 -0
  14. package/dist/lite/.claude/hooks/skill-step-list-check.sh +171 -0
  15. package/dist/lite/.claude/luminaries/alan-cooper.md +170 -0
  16. package/dist/lite/.claude/luminaries/alistair-cockburn.md +140 -0
  17. package/dist/lite/.claude/luminaries/amazon-pr-faq.md +34 -0
  18. package/dist/lite/.claude/luminaries/andreas-zeller.md +74 -0
  19. package/dist/lite/.claude/luminaries/ash-maurya.md +121 -0
  20. package/dist/lite/.claude/luminaries/bill-buxton.md +210 -0
  21. package/dist/lite/.claude/luminaries/charles-sanders-peirce.md +150 -0
  22. package/dist/lite/.claude/luminaries/david-ogilvy.md +192 -0
  23. package/dist/lite/.claude/luminaries/don-norman.md +173 -0
  24. package/dist/lite/.claude/luminaries/edward-tufte.md +179 -0
  25. package/dist/lite/.claude/luminaries/eric-evans.md +160 -0
  26. package/dist/lite/.claude/luminaries/frederick-brooks.md +50 -0
  27. package/dist/lite/.claude/luminaries/gang-of-four.md +157 -0
  28. package/dist/lite/.claude/luminaries/glenford-myers.md +230 -0
  29. package/dist/lite/.claude/luminaries/hunt-thomas.md +115 -0
  30. package/dist/lite/.claude/luminaries/hyrum-wright.md +94 -0
  31. package/dist/lite/.claude/luminaries/jason-fried-dhh.md +46 -0
  32. package/dist/lite/.claude/luminaries/jesse-james-garrett.md +154 -0
  33. package/dist/lite/.claude/luminaries/john-ousterhout.md +94 -0
  34. package/dist/lite/.claude/luminaries/karl-popper.md +132 -0
  35. package/dist/lite/.claude/luminaries/kent-beck.md +168 -0
  36. package/dist/lite/.claude/luminaries/linus-torvalds.md +218 -0
  37. package/dist/lite/.claude/luminaries/martin-fowler.md +164 -0
  38. package/dist/lite/.claude/luminaries/michael-feathers.md +125 -0
  39. package/dist/lite/.claude/luminaries/michael-nygard.md +118 -0
  40. package/dist/lite/.claude/luminaries/robert-c-martin.md +164 -0
  41. package/dist/lite/.claude/luminaries/saltzer-schroeder.md +194 -0
  42. package/dist/lite/.claude/luminaries/sophia-prater.md +193 -0
  43. package/dist/lite/.claude/luminaries/stephen-toulmin.md +131 -0
  44. package/dist/lite/.claude/luminaries/tony-hoare.md +170 -0
  45. package/dist/lite/.claude/luminaries/vaughn-vernon.md +50 -0
  46. package/dist/lite/.claude/luminaries/w-edwards-deming.md +158 -0
  47. package/dist/lite/.claude/rules/accessor-library-discipline.md +110 -0
  48. package/dist/lite/.claude/rules/adr-discipline.md +96 -0
  49. package/dist/lite/.claude/rules/api-conventions.md +115 -0
  50. package/dist/lite/.claude/rules/artifact-ingestion.md +137 -0
  51. package/dist/lite/.claude/rules/assert-only-after-verify.md +111 -0
  52. package/dist/lite/.claude/rules/blocked-items.md +139 -0
  53. package/dist/lite/.claude/rules/bootstrap-pair-discipline.md +106 -0
  54. package/dist/lite/.claude/rules/branching.md +28 -0
  55. package/dist/lite/.claude/rules/cold-adopter-harness-discipline.md +93 -0
  56. package/dist/lite/.claude/rules/commit-conventions.md +22 -0
  57. package/dist/lite/.claude/rules/compounding-axis-fresh-analysis.md +173 -0
  58. package/dist/lite/.claude/rules/compounding-sequence-fresh-analysis.md +173 -0
  59. package/dist/lite/.claude/rules/context-engineering.md +155 -0
  60. package/dist/lite/.claude/rules/context-management.md +85 -0
  61. package/dist/lite/.claude/rules/defensive-bash.md +52 -0
  62. package/dist/lite/.claude/rules/deferred-actions.md +233 -0
  63. package/dist/lite/.claude/rules/destructive-operations.md +69 -0
  64. package/dist/lite/.claude/rules/diagnosis.md +38 -0
  65. package/dist/lite/.claude/rules/github-issue-flash-tweet.md +114 -0
  66. package/dist/lite/.claude/rules/guardrails.md +73 -0
  67. package/dist/lite/.claude/rules/hook-wire-on-author.md +77 -0
  68. package/dist/lite/.claude/rules/identifier-leak-prevention.md +73 -0
  69. package/dist/lite/.claude/rules/iteration-bet-brief-completeness.md +43 -0
  70. package/dist/lite/.claude/rules/lite-manifest-schema-change-discipline.md +79 -0
  71. package/dist/lite/.claude/rules/longrun-prep-plan-doc-compression.md +77 -0
  72. package/dist/lite/.claude/rules/loop-discipline.md +71 -0
  73. package/dist/lite/.claude/rules/manual-prod-approval.md +100 -0
  74. package/dist/lite/.claude/rules/marker-enrichment-discipline.md +72 -0
  75. package/dist/lite/.claude/rules/mobile-ephemeral-session.md +109 -0
  76. package/dist/lite/.claude/rules/new-dependency-check.md +44 -0
  77. package/dist/lite/.claude/rules/oo-ad-entry-point.md +95 -0
  78. package/dist/lite/.claude/rules/operator-facing-prose.md +177 -0
  79. package/dist/lite/.claude/rules/option-label-discipline.md +82 -0
  80. package/dist/lite/.claude/rules/pattern-annotation.md +83 -0
  81. package/dist/lite/.claude/rules/persona-name-leak-scrub.md +90 -0
  82. package/dist/lite/.claude/rules/plain-english-discipline.md +132 -0
  83. package/dist/lite/.claude/rules/plan-enumeration-needs-value-props.md +177 -0
  84. package/dist/lite/.claude/rules/pr-body-shape.md +195 -0
  85. package/dist/lite/.claude/rules/pr-strategy.md +142 -0
  86. package/dist/lite/.claude/rules/pr-title-shape.md +90 -0
  87. package/dist/lite/.claude/rules/prototype-workflow.md +53 -0
  88. package/dist/lite/.claude/rules/reserved-skill-names.md +103 -0
  89. package/dist/lite/.claude/rules/schema-management.md +42 -0
  90. package/dist/lite/.claude/rules/sdlc-gates.md +149 -0
  91. package/dist/lite/.claude/rules/security.md +37 -0
  92. package/dist/lite/.claude/rules/session-artifacts.md +236 -0
  93. package/dist/lite/.claude/rules/skill-composition-declarations.md +105 -0
  94. package/dist/lite/.claude/rules/skill-description-clarity.md +196 -0
  95. package/dist/lite/.claude/rules/skill-procedure-step-list.md +119 -0
  96. package/dist/lite/.claude/rules/sprint-prep-preset-picker.md +76 -0
  97. package/dist/lite/.claude/rules/state-schema-validation.md +156 -0
  98. package/dist/lite/.claude/rules/stuck-signal-diagnostic.md +111 -0
  99. package/dist/lite/.claude/rules/substrate-config-schema.md +85 -0
  100. package/dist/lite/.claude/rules/test-list-discipline.md +129 -0
  101. package/dist/lite/.claude/rules/test-sufficiency.md +186 -0
  102. package/dist/lite/.claude/rules/testing-tier-config.md +125 -0
  103. package/dist/lite/.claude/rules/testing.md +38 -0
  104. package/dist/lite/.claude/rules/turn-estimate-grounding.md +82 -0
  105. package/dist/lite/.claude/rules/visual-hierarchy.md +427 -0
  106. package/dist/lite/.claude/rules/we-dont-break-adopters.md +103 -0
  107. package/dist/lite/.claude/rules/whereami-load-bearing.md +184 -0
  108. package/dist/lite/.claude/rules/writing-craft-discipline.md +72 -0
  109. package/dist/lite/.claude/rules/wu-sequencing-compounds.md +119 -0
  110. package/dist/lite/.claude/skills/build/SKILL.md +640 -0
  111. package/dist/lite/.claude/skills/chronicle/SKILL.md +55 -0
  112. package/dist/lite/.claude/skills/clean-artifacts/SKILL.md +249 -0
  113. package/dist/lite/.claude/skills/decompose/SKILL.md +280 -0
  114. package/dist/lite/.claude/skills/diagnose/SKILL.md +297 -0
  115. package/dist/lite/.claude/skills/feynman/SKILL.md +90 -0
  116. package/dist/lite/.claude/skills/howdoi/SKILL.md +105 -0
  117. package/dist/lite/.claude/skills/ia-model/SKILL.md +108 -0
  118. package/dist/lite/.claude/skills/interaction-design/SKILL.md +112 -0
  119. package/dist/lite/.claude/skills/interpret-input/SKILL.md +180 -0
  120. package/dist/lite/.claude/skills/journal/SKILL.md +209 -0
  121. package/dist/lite/.claude/skills/kiss/SKILL.md +449 -0
  122. package/dist/lite/.claude/skills/launch/SKILL.md +915 -0
  123. package/dist/lite/.claude/skills/lean-canvas/SKILL.md +332 -0
  124. package/dist/lite/.claude/skills/longrun/SKILL.md +512 -0
  125. package/dist/lite/.claude/skills/luminary/SKILL.md +481 -0
  126. package/dist/lite/.claude/skills/ogilvy-writing-audit/SKILL.md +177 -0
  127. package/dist/lite/.claude/skills/onboard-repo/SKILL.md +1640 -0
  128. package/dist/lite/.claude/skills/pattern-review/SKILL.md +99 -0
  129. package/dist/lite/.claude/skills/personas/SKILL.md +207 -0
  130. package/dist/lite/.claude/skills/promote/SKILL.md +283 -0
  131. package/dist/lite/.claude/skills/requirement/SKILL.md +98 -0
  132. package/dist/lite/.claude/skills/retro/SKILL.md +117 -0
  133. package/dist/lite/.claude/skills/riff/SKILL.md +114 -0
  134. package/dist/lite/.claude/skills/roadmap-reconcile/SKILL.md +163 -0
  135. package/dist/lite/.claude/skills/session-end/SKILL.md +309 -0
  136. package/dist/lite/.claude/skills/session-log/SKILL.md +299 -0
  137. package/dist/lite/.claude/skills/skills/SKILL.md +228 -0
  138. package/dist/lite/.claude/skills/spec/SKILL.md +105 -0
  139. package/dist/lite/.claude/skills/sprint/SKILL.md +418 -0
  140. package/dist/lite/.claude/skills/stage/SKILL.md +384 -0
  141. package/dist/lite/.claude/skills/state-a-problem/SKILL.md +185 -0
  142. package/dist/lite/.claude/skills/temperance/SKILL.md +108 -0
  143. package/dist/lite/.claude/skills/use-case/SKILL.md +417 -0
  144. package/dist/lite/.claude/skills/user-stories/SKILL.md +268 -0
  145. package/dist/lite/.claude/skills/value-prop/SKILL.md +251 -0
  146. package/dist/lite/.claude/skills/verify/SKILL.md +160 -0
  147. package/dist/lite/.claude/skills/visual-review/SKILL.md +503 -0
  148. package/dist/lite/.claude/skills/whats-the-plan/SKILL.md +202 -0
  149. package/dist/lite/.claude/skills/whereami/SKILL.md +307 -0
  150. package/dist/lite/AGENTS.md +79 -0
  151. package/dist/lite/CLAUDE-lite.md +85 -0
  152. package/dist/lite/CODE_OF_CONDUCT.md +28 -0
  153. package/dist/lite/CONTRIBUTING.md +177 -0
  154. package/dist/lite/README.md +173 -0
  155. package/dist/lite/SECURITY.md +19 -0
  156. package/dist/lite/architecture/decisions/ADR-029-release-pipeline.md +79 -0
  157. package/dist/lite/architecture/decisions/ADR-031-non-breaking-changes-adopter-discipline.md +139 -0
  158. package/dist/lite/architecture/decisions/ADR-032-adopter-sync-dispatcher-architecture.md +192 -0
  159. package/dist/lite/architecture/decisions/ADR-039-release-tagging-scheme.md +145 -0
  160. package/dist/lite/architecture/decisions/ADR-040-planning-skill-vocabulary-and-lite-profile.md +155 -0
  161. package/dist/lite/architecture/decisions/ADR-044-unified-skill-body-template.md +162 -0
  162. package/dist/lite/lib/clean-artifacts-sweep.sh +112 -0
  163. package/dist/lite/lib/code-comment-discipline.sh +144 -0
  164. package/dist/lite/lib/composer-preflight.sh +459 -0
  165. package/dist/lite/lib/deferred-actions.sh +146 -0
  166. package/dist/lite/lib/hook-header-parser.sh +61 -0
  167. package/dist/lite/lib/hook-heartbeat.sh +152 -0
  168. package/dist/lite/lib/hook-inject.sh +255 -0
  169. package/dist/lite/lib/hook-installer.sh +195 -0
  170. package/dist/lite/lib/luminary-pick.sh +96 -0
  171. package/dist/lite/lib/output-discipline.sh +143 -0
  172. package/dist/lite/lib/prose-scan-boundary.sh +171 -0
  173. package/dist/lite/lib/rewrite-check.sh +214 -0
  174. package/dist/lite/lib/session-rescue.sh +151 -0
  175. package/dist/lite/lib/source-graph-walker.sh +277 -0
  176. package/dist/lite/lib/state.sh +1372 -0
  177. package/dist/lite/lib/telemetry.sh +205 -0
  178. package/dist/lite/lib/tier-check.sh +187 -0
  179. package/dist/lite/lib/tier-dependency-audit.sh +1088 -0
  180. package/dist/lite/presence/install/bassclef-hook-connect.sh +178 -0
  181. package/dist/lite/presence/install/bassclef-sync.dispatcher.template.sh +841 -0
  182. package/dist/lite/presence/install/bassclef-sync.template.sh +2278 -0
  183. package/dist/lite/presence/install/schedule-auto-save.cron.sh +88 -0
  184. package/dist/lite/presence/install/schedule-auto-save.taskscheduler.md +122 -0
  185. package/dist/lite/scripts/aggregate-telemetry.sh +217 -0
  186. package/dist/lite/scripts/analyze-tier-dependencies.sh +239 -0
  187. package/dist/lite/scripts/generate-lite-manifest.sh +505 -0
  188. package/dist/lite/scripts/generate-tier-manifest.sh +28 -0
  189. package/dist/lite/scripts/intent-drift-check.sh +456 -0
  190. package/dist/lite/scripts/lite-manifest-drift-check.sh +146 -0
  191. package/dist/lite/scripts/render-lite-manifest-doc.sh +150 -0
  192. package/dist/lite/standards/adr-template.md +86 -0
  193. package/dist/lite/standards/api-conventions/nextjs.md +84 -0
  194. package/dist/lite/standards/artifact-composition.md +209 -0
  195. package/dist/lite/standards/bash-hook-safety.md +246 -0
  196. package/dist/lite/standards/bassclef-configs-schema.md +232 -0
  197. package/dist/lite/standards/bassclef-evolution.md +143 -0
  198. package/dist/lite/standards/bassclef-internal-jargon.md +244 -0
  199. package/dist/lite/standards/bassclef-managed-sentinel.md +96 -0
  200. package/dist/lite/standards/bassclef-source-config.md +228 -0
  201. package/dist/lite/standards/branch-stacking.md +408 -0
  202. package/dist/lite/standards/code-safety-principles.md +176 -0
  203. package/dist/lite/standards/composer-prerequisites.md +155 -0
  204. package/dist/lite/standards/deferred-actions-schema.md +204 -0
  205. package/dist/lite/standards/dependency-discipline/cargo.md +39 -0
  206. package/dist/lite/standards/dependency-discipline/gem.md +43 -0
  207. package/dist/lite/standards/dependency-discipline/go-mod.md +41 -0
  208. package/dist/lite/standards/dependency-discipline/npm.md +42 -0
  209. package/dist/lite/standards/dependency-discipline/pip.md +42 -0
  210. package/dist/lite/standards/deployment-topology/ec2-tailscale.md +225 -0
  211. package/dist/lite/standards/deployment-topology.md +69 -0
  212. package/dist/lite/standards/docs-sync-allowlist.md +76 -0
  213. package/dist/lite/standards/domain-and-dns.md +145 -0
  214. package/dist/lite/standards/frontend-stack.md +67 -0
  215. package/dist/lite/standards/frontmatter-schema.md +154 -0
  216. package/dist/lite/standards/graceful-exit.md +227 -0
  217. package/dist/lite/standards/hook-idempotency.md +102 -0
  218. package/dist/lite/standards/hook-injection-discipline.md +202 -0
  219. package/dist/lite/standards/hook-install-class.md +215 -0
  220. package/dist/lite/standards/input-handler-interface.md +152 -0
  221. package/dist/lite/standards/lite-manifest-schema-changes.md +180 -0
  222. package/dist/lite/standards/lite-manifest.json +2797 -0
  223. package/dist/lite/standards/luminary-matching.md +105 -0
  224. package/dist/lite/standards/luminary-problem-patterns.md +481 -0
  225. package/dist/lite/standards/migration-discipline/active-record.md +50 -0
  226. package/dist/lite/standards/migration-discipline/alembic.md +43 -0
  227. package/dist/lite/standards/migration-discipline/gorm.md +50 -0
  228. package/dist/lite/standards/migration-discipline/prisma.md +53 -0
  229. package/dist/lite/standards/migration-discipline/sqlalchemy.md +51 -0
  230. package/dist/lite/standards/mobile-ephemeral-session.md +167 -0
  231. package/dist/lite/standards/model-routing-discipline.md +160 -0
  232. package/dist/lite/standards/ogilvy-writing-rules.md +225 -0
  233. package/dist/lite/standards/opener-discipline.md +96 -0
  234. package/dist/lite/standards/operator-facing-prose-discipline.md +201 -0
  235. package/dist/lite/standards/persona-schema.md +229 -0
  236. package/dist/lite/standards/pluggable-luminaries.md +323 -0
  237. package/dist/lite/standards/pr-body-discipline.md +115 -0
  238. package/dist/lite/standards/preview-state-schema.md +189 -0
  239. package/dist/lite/standards/project-directory-layout.md +276 -0
  240. package/dist/lite/standards/release-tagging.md +137 -0
  241. package/dist/lite/standards/reserved-skill-names.md +120 -0
  242. package/dist/lite/standards/scannable-multi-option-output.md +265 -0
  243. package/dist/lite/standards/sdlc-compliance.md +286 -0
  244. package/dist/lite/standards/sdlc-gates/typescript.md +57 -0
  245. package/dist/lite/standards/secrets-lifecycle.md +210 -0
  246. package/dist/lite/standards/security-scanner-adapter.md +145 -0
  247. package/dist/lite/standards/session-board.md +256 -0
  248. package/dist/lite/standards/skill-output-discipline.md +121 -0
  249. package/dist/lite/standards/state-spine-contract.md +255 -0
  250. package/dist/lite/standards/state-spine.md +511 -0
  251. package/dist/lite/standards/steering-hints/kiss-words.md +11 -0
  252. package/dist/lite/standards/substrate-config-schema.md +267 -0
  253. package/dist/lite/standards/tech-stack-config.md +109 -0
  254. package/dist/lite/standards/tier-dependency-analysis.md +167 -0
  255. package/dist/lite/standards/tier-runtime-deps/lite.md +57 -0
  256. package/dist/lite/standards/tier-tag-schema.md +155 -0
  257. package/dist/lite/standards/two-layer-config.md +99 -0
  258. package/dist/lite/standards/use-case-format.md +292 -0
  259. package/dist/lite/standards/user-story-invest.md +268 -0
  260. package/dist/lite/standards/velocity-and-appetite.md +229 -0
  261. package/dist/lite/standards/voice-input-pattern.md +119 -0
  262. package/dist/lite/standards/whereami-schema.md +301 -0
  263. package/dist/lite/standards/worktree-management.md +211 -0
  264. package/dist/lite/standards/writing-guide.md +213 -0
  265. package/dist/lite/templates/chronicle-template.md +75 -0
  266. package/dist/lite/templates/deferred-action-template.md +45 -0
  267. package/dist/lite/templates/memory-proposal-template.md +77 -0
  268. package/dist/lite/templates/persona-template.md +200 -0
  269. package/dist/lite/templates/pr-faq.md +45 -0
  270. package/dist/lite/templates/secret-rotation-template.md +162 -0
  271. package/dist/lite/templates/spec-template.md +131 -0
  272. package/dist/lite/templates/use-case-template.md +194 -0
  273. package/dist/lite/templates/user-story-template.md +107 -0
  274. package/dist/lite/templates/whereami-template.md +101 -0
  275. package/package.json +12 -5
package/dist/cli.cjs CHANGED
@@ -313,14 +313,15 @@ function substrateConfigMdTemplate(pkgVersion) {
313
313
  ].join("\n");
314
314
  }
315
315
  const MANIFEST_SCHEMA_VERSION = "0.1.0";
316
- const MANIFEST_SHAPE_VERSION = 2;
316
+ const MANIFEST_SHAPE_VERSION = 3;
317
+ const GENERATED_BY = "@thebassclef/lite";
317
318
  function manifestTemplate(input) {
318
319
  const value = {
319
320
  schema_version: MANIFEST_SHAPE_VERSION,
320
321
  $bassclef: {
321
322
  template: "init.manifest.json",
322
323
  manifest_schema_version: MANIFEST_SCHEMA_VERSION,
323
- generated_by: "@thebassclef/core",
324
+ generated_by: GENERATED_BY,
324
325
  generated_by_version: input.pkgVersion
325
326
  },
326
327
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
@@ -388,7 +389,7 @@ function readManifest(targetDir) {
388
389
  if (compareSchemaVersion(schema, MANIFEST_SCHEMA_VERSION) > 0) {
389
390
  throw new ManifestReadError(
390
391
  "SchemaTooNew",
391
- `manifest schema version ${schema} is newer than this package understands (${MANIFEST_SCHEMA_VERSION}). Upgrade @thebassclef/core.`
392
+ `manifest schema version ${schema} is newer than this package understands (${MANIFEST_SCHEMA_VERSION}). Upgrade @thebassclef/lite.`
392
393
  );
393
394
  }
394
395
  const obj = parsed;
@@ -518,6 +519,7 @@ function setExecutable(targetPath) {
518
519
  }
519
520
  node_fs.chmodSync(targetPath, HOOK_EXECUTABLE_MODE);
520
521
  }
522
+ const CLAUDE_TARGET_ROOT = ".claude";
521
523
  const HOOKS_SUBPATH = ".claude/hooks/";
522
524
  const SETTINGS_SUBPATH = ".claude/settings.json";
523
525
  const CONFIG_FILES = [
@@ -540,7 +542,10 @@ function copySubstrate(targetDir, options = {}) {
540
542
  hookCount: 0,
541
543
  wiringVersion: manifest.version
542
544
  };
543
- if (options.dryRun) result.wouldCopy = [];
545
+ if (options.dryRun) {
546
+ result.wouldCopy = [];
547
+ result.wouldCopyEntries = [];
548
+ }
544
549
  const files = walkDistTree(bundleRoot);
545
550
  const groups = groupByTopDirectory(files);
546
551
  const bundleHookRelPaths = new Set(files.filter(isHookFile));
@@ -696,6 +701,11 @@ function copyOne(relPath, bundleRoot, options, result, scopeDecision) {
696
701
  const outputContent = options.transform ? options.transform(adopterRelPath, content) : content;
697
702
  if (options.dryRun) {
698
703
  result.wouldCopy?.push(adopterRelPath);
704
+ result.wouldCopyEntries?.push({
705
+ path: adopterRelPath,
706
+ scope,
707
+ content_hash_sha256: hashContent(outputContent)
708
+ });
699
709
  return "wouldCopy";
700
710
  }
701
711
  try {
@@ -705,7 +715,11 @@ function copyOne(relPath, bundleRoot, options, result, scopeDecision) {
705
715
  setExecutable(targetPath);
706
716
  }
707
717
  result.copied.push(adopterRelPath);
708
- result.copiedEntries.push({ path: adopterRelPath, scope });
718
+ result.copiedEntries.push({
719
+ path: adopterRelPath,
720
+ scope,
721
+ content_hash_sha256: hashContent(outputContent)
722
+ });
709
723
  return "copied";
710
724
  } catch (e) {
711
725
  if (e instanceof WriteError) {
@@ -782,7 +796,92 @@ function dirExists(p) {
782
796
  return false;
783
797
  }
784
798
  }
799
+ const FAMILIES = [
800
+ "skills",
801
+ "rules",
802
+ "agents",
803
+ "luminaries",
804
+ "hooks",
805
+ "libs",
806
+ "adrs",
807
+ "standards",
808
+ "templates",
809
+ "presence-templates",
810
+ "scripts",
811
+ "root-docs",
812
+ "other"
813
+ ];
814
+ const UNDER_CLAUDE = `${CLAUDE_TARGET_ROOT}/`;
815
+ const PREFIX_RULES = [
816
+ [`${UNDER_CLAUDE}skills/`, "skills"],
817
+ [`${UNDER_CLAUDE}rules/`, "rules"],
818
+ [`${UNDER_CLAUDE}agents/`, "agents"],
819
+ [`${UNDER_CLAUDE}luminaries/`, "luminaries"],
820
+ [`${UNDER_CLAUDE}hooks/`, "hooks"],
821
+ ["lib/", "libs"],
822
+ ["architecture/decisions/", "adrs"],
823
+ ["standards/", "standards"],
824
+ ["templates/", "templates"],
825
+ ["presence/install/", "presence-templates"],
826
+ ["scripts/", "scripts"]
827
+ ];
828
+ function classifyEntry(path) {
829
+ for (const [prefix, family] of PREFIX_RULES) {
830
+ if (path.startsWith(prefix)) return family;
831
+ }
832
+ if (!path.includes("/") && /^[A-Z]/.test(path)) return "root-docs";
833
+ return "other";
834
+ }
835
+ function emptyCatalogCounts() {
836
+ const out = {};
837
+ for (const f of FAMILIES) out[f] = 0;
838
+ return out;
839
+ }
840
+ function buildInitReport(input) {
841
+ const catalog = emptyCatalogCounts();
842
+ let project = 0;
843
+ let user = 0;
844
+ for (const e of input.entries) {
845
+ catalog[classifyEntry(e.path)] += 1;
846
+ if (e.scope === "user") user += 1;
847
+ else project += 1;
848
+ }
849
+ const refused = input.refused.length;
850
+ const errored = input.errored.length;
851
+ const written = input.configs.length + input.entries.length;
852
+ return {
853
+ schema_version: 3,
854
+ tier: input.tier,
855
+ totals: {
856
+ files: written + refused + errored,
857
+ written,
858
+ project,
859
+ user,
860
+ configs: input.configs.length
861
+ },
862
+ catalog,
863
+ hooks: {
864
+ declared: input.hookCount,
865
+ copied: input.declaredHooksCopied,
866
+ files: catalog.hooks
867
+ },
868
+ refused,
869
+ errored,
870
+ failed: refused + errored
871
+ };
872
+ }
873
+ function renderJsonReport(report, write) {
874
+ write(JSON.stringify(report) + "\n");
875
+ }
785
876
  const RESOLVED_TIER = "lite";
877
+ const SAY_HUMAN = (s) => {
878
+ process.stdout.write(s);
879
+ };
880
+ const SAY_QUIET = () => {
881
+ };
882
+ function makeSay(json) {
883
+ return json ? SAY_QUIET : SAY_HUMAN;
884
+ }
786
885
  const PLACEHOLDER_FILES = /* @__PURE__ */ new Set([
787
886
  "CLAUDE.md",
788
887
  "docs/whereami.md",
@@ -824,7 +923,7 @@ function runInit(argv) {
824
923
  }
825
924
  throw e;
826
925
  }
827
- const advisoryOutcome = maybeEmitUpgradeAdvisory(targetDir, args.yes);
926
+ const advisoryOutcome = maybeEmitUpgradeAdvisory(targetDir, args.yes, args.json);
828
927
  if (advisoryOutcome === "refused") return 1;
829
928
  const upgradeApproved = advisoryOutcome === "upgrade-approved";
830
929
  if (!args.force && !args.dryRun && !upgradeApproved) {
@@ -846,26 +945,55 @@ function runInit(argv) {
846
945
  templateVersion: SUBSTRATE_CONFIG_TEMPLATE_VERSION
847
946
  }
848
947
  ];
948
+ const say = makeSay(args.json);
849
949
  if (args.dryRun) {
850
- runDryRun$1(plans);
851
- return dispatchSubstrateCopy(targetDir, args.force || upgradeApproved, args.verbose, true, args.allowRoot, args.json);
852
- }
853
- return runReal$1(plans, args.force || upgradeApproved, args.verbose, targetDir, args.allowRoot, args.json);
854
- }
855
- function maybeEmitUpgradeAdvisory(targetDir, yes) {
950
+ runDryRun$1(plans, say);
951
+ const outcome = dispatchSubstrateCopy(
952
+ targetDir,
953
+ args.force || upgradeApproved,
954
+ args.verbose,
955
+ true,
956
+ args.allowRoot,
957
+ say
958
+ );
959
+ if (args.json) {
960
+ const report = buildInitReport({
961
+ entries: outcome.result?.wouldCopyEntries ?? [],
962
+ configs: plans.map((pl) => ({ path: pl.relativePath })),
963
+ refused: outcome.result?.refused ?? [],
964
+ // A dry run still reads every source file, so it can still fail
965
+ // to read one. Hard-coding this empty hid those failures from
966
+ // the JSON (RFC-0005 A-3).
967
+ errored: outcome.result?.errored ?? [],
968
+ hookCount: outcome.result?.hookCount ?? 0,
969
+ declaredHooksCopied: 0,
970
+ tier: RESOLVED_TIER
971
+ });
972
+ renderJsonReport(report, (t) => {
973
+ process.stdout.write(t);
974
+ });
975
+ }
976
+ return outcome.code;
977
+ }
978
+ return runReal$1(plans, args.force || upgradeApproved, args.verbose, targetDir, args.allowRoot, args.json, say);
979
+ }
980
+ function maybeEmitUpgradeAdvisory(targetDir, yes, json) {
981
+ const prompt = json ? (t) => {
982
+ process.stderr.write(t);
983
+ } : SAY_HUMAN;
856
984
  const manifestPath = node_path.join(targetDir, MANIFEST_RELATIVE_PATH);
857
985
  if (!node_fs.existsSync(manifestPath)) return "ok";
858
986
  const version = readManifestShapeVersion(targetDir);
859
987
  if (version !== null && version >= 2) return "ok";
860
- process.stdout.write(
988
+ prompt(
861
989
  `bassclef init: cli 1.0.1 introduces user-scope hook installation at ~/${HOOKS_SUBPATH}. cli 1.0.0 did not write there.
862
990
  `
863
991
  );
864
992
  if (yes) return "upgrade-approved";
865
- process.stdout.write("bassclef init: continue? (y/N) ");
993
+ prompt("bassclef init: continue? (y/N) ");
866
994
  const answer = readOneLineFromStdin();
867
995
  if (answer === "" || answer === "y" || answer === "Y") return "upgrade-approved";
868
- process.stdout.write("bassclef init: aborted by adopter.\n");
996
+ prompt("bassclef init: aborted by adopter.\n");
869
997
  return "refused";
870
998
  }
871
999
  function readOneLineFromStdin() {
@@ -877,7 +1005,7 @@ function readOneLineFromStdin() {
877
1005
  return "";
878
1006
  }
879
1007
  }
880
- function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, json) {
1008
+ function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, say) {
881
1009
  const substitute = makePlaceholderTransform(targetDir);
882
1010
  let result;
883
1011
  try {
@@ -891,9 +1019,9 @@ function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, jso
891
1019
  if (e instanceof CopyFailure) {
892
1020
  process.stderr.write(`bassclef init: ${e.message}
893
1021
  `);
894
- if (e.kind === "ManifestMissing") return 4;
895
- if (e.kind === "SchemaIncompatible") return 5;
896
- return 2;
1022
+ if (e.kind === "ManifestMissing") return { code: 4 };
1023
+ if (e.kind === "SchemaIncompatible") return { code: 5 };
1024
+ return { code: 2 };
897
1025
  }
898
1026
  throw e;
899
1027
  }
@@ -901,23 +1029,23 @@ function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, jso
901
1029
  const wouldCopy = result.wouldCopy ?? [];
902
1030
  for (const relativePath of wouldCopy) {
903
1031
  const targetPath = node_path.join(targetDir, relativePath);
904
- process.stdout.write(` ${"would create".padEnd(14)} ${targetPath}
1032
+ say(` ${"would create".padEnd(14)} ${targetPath}
905
1033
  `);
906
1034
  }
907
1035
  if (wouldCopy.length > 0) {
908
- process.stdout.write(
1036
+ say(
909
1037
  `bassclef init: ${wouldCopy.length} substrate files would be copied.
910
1038
  `
911
1039
  );
912
1040
  }
913
- process.stdout.write(
1041
+ say(
914
1042
  `bassclef init: ${result.hookCount} hooks armed (${RESOLVED_TIER} tier).
915
1043
  `
916
1044
  );
917
- return 0;
1045
+ return { code: 0, result };
918
1046
  }
919
1047
  if (result.copied.length === 0 && result.refused.length === 0 && result.errored.length === 0) {
920
- return 0;
1048
+ return { code: 0, result };
921
1049
  }
922
1050
  const groupCounts = /* @__PURE__ */ new Map();
923
1051
  for (const path of result.copied) {
@@ -926,17 +1054,17 @@ function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, jso
926
1054
  groupCounts.set(top, (groupCounts.get(top) ?? 0) + 1);
927
1055
  }
928
1056
  for (const [directory, count] of groupCounts) {
929
- process.stdout.write(` ${directory}: ${count} files copied
1057
+ say(` ${directory}: ${count} files copied
930
1058
  `);
931
1059
  }
932
1060
  const parts = [];
933
1061
  if (result.copied.length > 0) parts.push(`${result.copied.length} substrate files copied`);
934
1062
  if (result.refused.length > 0) parts.push(`${result.refused.length} refused`);
935
1063
  if (result.errored.length > 0) parts.push(`${result.errored.length} error(s)`);
936
- process.stdout.write(`bassclef init: ${parts.join(", ")}.
1064
+ say(`bassclef init: ${parts.join(", ")}.
937
1065
  `);
938
1066
  const grandTotal = 1 + result.copied.length;
939
- process.stdout.write(
1067
+ say(
940
1068
  `bassclef init: ${grandTotal} files total (1 config + ${result.copied.length} substrate).
941
1069
  `
942
1070
  );
@@ -953,33 +1081,87 @@ function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, jso
953
1081
  const projectScope = copiedHookEntries.filter((e) => e.scope === "project").length;
954
1082
  const scopeSuffix = userScope + projectScope > 0 ? ` ${projectScope} in <repo>/${HOOKS_SUBPATH.replace(/\/$/, "")}, ${userScope} in ~/${HOOKS_SUBPATH.replace(/\/$/, "")}.` : "";
955
1083
  if (failedCount > 0) {
956
- process.stdout.write(
1084
+ say(
957
1085
  `bassclef init: Installed ${copiedCount} of ${declaredCount} hooks (${RESOLVED_TIER} tier).${scopeSuffix} ${failedCount} failed — see errors above. Rerun bassclef init to retry.
958
1086
  `
959
1087
  );
960
1088
  } else {
961
- process.stdout.write(
1089
+ say(
962
1090
  `bassclef init: Installed ${copiedCount} of ${declaredCount} hooks (${RESOLVED_TIER} tier).${scopeSuffix}
963
1091
  `
964
1092
  );
965
1093
  }
966
- if (json) {
967
- const report = {
968
- copied: copiedCount,
969
- declared: declaredCount,
970
- failed: failedCount,
971
- scope_counts: { user: userScope, project: projectScope },
972
- tier: RESOLVED_TIER
973
- };
974
- process.stderr.write(JSON.stringify(report) + "\n");
1094
+ const catalogCounts = {
1095
+ skills: 0,
1096
+ rules: 0,
1097
+ agents: 0,
1098
+ luminaries: 0,
1099
+ libs: 0,
1100
+ adrs: 0,
1101
+ standards: 0,
1102
+ templates: 0,
1103
+ "presence-templates": 0,
1104
+ scripts: 0,
1105
+ "root-docs": 0
1106
+ };
1107
+ const SKILLS_PREFIX = `${CLAUDE_TARGET_ROOT}/skills/`;
1108
+ const RULES_PREFIX = `${CLAUDE_TARGET_ROOT}/rules/`;
1109
+ const AGENTS_PREFIX = `${CLAUDE_TARGET_ROOT}/agents/`;
1110
+ const LUMINARIES_PREFIX = `${CLAUDE_TARGET_ROOT}/luminaries/`;
1111
+ const CLAUDE_ROOT_PREFIX = `${CLAUDE_TARGET_ROOT}/`;
1112
+ for (const entry of result.copiedEntries) {
1113
+ if (entry.path.startsWith(SKILLS_PREFIX)) catalogCounts.skills += 1;
1114
+ else if (entry.path.startsWith(RULES_PREFIX)) catalogCounts.rules += 1;
1115
+ else if (entry.path.startsWith(AGENTS_PREFIX)) catalogCounts.agents += 1;
1116
+ else if (entry.path.startsWith(LUMINARIES_PREFIX)) catalogCounts.luminaries += 1;
1117
+ else if (entry.path.startsWith("lib/")) catalogCounts.libs += 1;
1118
+ else if (entry.path.startsWith("architecture/decisions/")) catalogCounts.adrs += 1;
1119
+ else if (entry.path.startsWith("standards/")) catalogCounts.standards += 1;
1120
+ else if (entry.path.startsWith("templates/")) catalogCounts.templates += 1;
1121
+ else if (entry.path.startsWith("presence/install/")) catalogCounts["presence-templates"] += 1;
1122
+ else if (entry.path.startsWith("scripts/")) catalogCounts.scripts += 1;
1123
+ else if (!entry.path.startsWith(CLAUDE_ROOT_PREFIX) && !entry.path.includes("/") && /^[A-Z]/.test(entry.path)) {
1124
+ catalogCounts["root-docs"] += 1;
1125
+ }
1126
+ }
1127
+ const claudeCounts = [
1128
+ catalogCounts.skills > 0 ? `${catalogCounts.skills} skills` : null,
1129
+ catalogCounts.rules > 0 ? `${catalogCounts.rules} rules` : null,
1130
+ catalogCounts.agents > 0 ? `${catalogCounts.agents} agents` : null,
1131
+ catalogCounts.luminaries > 0 ? `${catalogCounts.luminaries} luminaries` : null
1132
+ ].filter((s) => s !== null);
1133
+ if (claudeCounts.length > 0) {
1134
+ say(
1135
+ `bassclef init: Installed ${claudeCounts.join(", ")} under <repo>/.claude/.
1136
+ `
1137
+ );
975
1138
  }
1139
+ const otherCounts = [
1140
+ catalogCounts.libs > 0 ? `${catalogCounts.libs} libs` : null,
1141
+ catalogCounts.adrs > 0 ? `${catalogCounts.adrs} ADRs` : null,
1142
+ catalogCounts.templates > 0 ? `${catalogCounts.templates} templates` : null,
1143
+ catalogCounts["presence-templates"] > 0 ? `${catalogCounts["presence-templates"]} presence-templates` : null,
1144
+ catalogCounts.standards > 0 ? `${catalogCounts.standards} standards` : null,
1145
+ catalogCounts["root-docs"] > 0 ? `${catalogCounts["root-docs"]} root-docs` : null,
1146
+ catalogCounts.scripts > 0 ? `${catalogCounts.scripts} scripts` : null
1147
+ ].filter((s) => s !== null);
1148
+ if (otherCounts.length > 0) {
1149
+ say(
1150
+ `bassclef init: Installed ${otherCounts.join(", ")} under <repo>/.
1151
+ `
1152
+ );
1153
+ }
1154
+ say(
1155
+ `bassclef init: ${result.refused.length} files refused (path collision).` + (result.refused.length > 0 ? ` Use --force to overwrite existing files.` : ``) + `
1156
+ `
1157
+ );
976
1158
  if (verbose && result.erroredMessages) {
977
1159
  for (const msg of result.erroredMessages) {
978
1160
  process.stderr.write(` substrate: ${msg}
979
1161
  `);
980
1162
  }
981
1163
  }
982
- return result.errored.length > 0 ? 2 : 0;
1164
+ return { code: result.errored.length > 0 ? 2 : 0, result };
983
1165
  }
984
1166
  function makePlaceholderTransform(targetDir) {
985
1167
  const repoName = node_path.basename(targetDir);
@@ -989,7 +1171,7 @@ function makePlaceholderTransform(targetDir) {
989
1171
  return content.replace(/\[REPO_NAME\]/g, repoName).replace(/\[ISO_TIMESTAMP\]/g, timestamp).replace(/\[TIER\]/g, RESOLVED_TIER).replace(/\[Repo name\]/g, repoName).replace(/<tier>/g, RESOLVED_TIER);
990
1172
  };
991
1173
  }
992
- function runDryRun$1(plans) {
1174
+ function runDryRun$1(plans, say) {
993
1175
  for (const p of plans) {
994
1176
  let planned;
995
1177
  let extra = "";
@@ -1009,12 +1191,12 @@ function runDryRun$1(plans) {
1009
1191
  planned = "would skip";
1010
1192
  }
1011
1193
  }
1012
- process.stdout.write(` ${planned.padEnd(14)} ${p.fullPath}${extra}
1194
+ say(` ${planned.padEnd(14)} ${p.fullPath}${extra}
1013
1195
  `);
1014
1196
  }
1015
1197
  return 0;
1016
1198
  }
1017
- function runReal$1(plans, force, verbose, targetDir, allowRoot, json) {
1199
+ function runReal$1(plans, force, verbose, targetDir, allowRoot, json, say) {
1018
1200
  const results = [];
1019
1201
  let anyRefused = false;
1020
1202
  let anyError = false;
@@ -1062,7 +1244,7 @@ function runReal$1(plans, force, verbose, targetDir, allowRoot, json) {
1062
1244
  if (r.outcome === "error") label = `error (${r.message ?? "unknown"})`;
1063
1245
  else if (r.outcome === "refused") label = "refused";
1064
1246
  else label = r.outcome;
1065
- process.stdout.write(` ${label.padEnd(10)} ${r.plan.fullPath}
1247
+ say(` ${label.padEnd(10)} ${r.plan.fullPath}
1066
1248
  `);
1067
1249
  }
1068
1250
  }
@@ -1084,38 +1266,59 @@ function runReal$1(plans, force, verbose, targetDir, allowRoot, json) {
1084
1266
  return 2;
1085
1267
  }
1086
1268
  if (anyRefused && created > 0) {
1087
- process.stdout.write(
1269
+ say(
1088
1270
  `bassclef init: ${created} config files created, ${unchanged} unchanged. Pass --force to overwrite.
1089
1271
  `
1090
1272
  );
1091
1273
  } else if (created === 0 && unchanged === plans.length) {
1092
- process.stdout.write("bassclef init: already initialized. No changes.\n");
1274
+ say("bassclef init: already initialized. No changes.\n");
1093
1275
  } else {
1094
- process.stdout.write(`bassclef init: ${created} config files created, ${unchanged} unchanged.
1276
+ say(`bassclef init: ${created} config files created, ${unchanged} unchanged.
1095
1277
  `);
1096
1278
  }
1097
- const walkerExit = dispatchSubstrateCopy(targetDir, force, verbose, false, allowRoot, json);
1098
- writeManifest(targetDir, results);
1099
- if (walkerExit === 0) {
1100
- process.stdout.write(
1279
+ const walker = dispatchSubstrateCopy(targetDir, force, verbose, false, allowRoot, say);
1280
+ const declaredLeaves = readDeclaredCommandLeaves(
1281
+ node_path.join(targetDir, ".claude", "settings.json")
1282
+ );
1283
+ const declaredHooksCopied = (walker.result?.copiedEntries ?? []).filter(
1284
+ (e) => e.path.startsWith(HOOKS_SUBPATH) && e.path.endsWith(".sh") && declaredLeaves.has(node_path.basename(e.path))
1285
+ ).length;
1286
+ const report = buildInitReport({
1287
+ entries: walker.result?.copiedEntries ?? [],
1288
+ configs: results.map((r) => ({ path: r.plan.relativePath })),
1289
+ refused: walker.result?.refused ?? [],
1290
+ errored: walker.result?.errored ?? [],
1291
+ hookCount: walker.result?.hookCount ?? 0,
1292
+ declaredHooksCopied,
1293
+ tier: RESOLVED_TIER
1294
+ });
1295
+ writeManifest(targetDir, results, walker.result);
1296
+ if (walker.code === 0) {
1297
+ say(
1101
1298
  `bassclef init: your substrate lives under .claude/. Add .claude/ to .gitignore if you have not.
1102
1299
  `
1103
1300
  );
1104
1301
  }
1105
- return walkerExit;
1302
+ if (json) {
1303
+ renderJsonReport(report, (t) => {
1304
+ process.stdout.write(t);
1305
+ });
1306
+ }
1307
+ return walker.code;
1106
1308
  }
1107
1309
  function shouldRefuseRoot(currentUid, allowRoot) {
1108
1310
  if (currentUid === void 0) return false;
1109
1311
  if (currentUid !== 0) return false;
1110
1312
  return !allowRoot;
1111
1313
  }
1112
- function writeManifest(targetDir, results) {
1314
+ function writeManifest(targetDir, results, walkerResult) {
1113
1315
  const entries = results.map((r) => {
1114
1316
  const entry = {
1115
1317
  path: r.plan.relativePath,
1116
1318
  template: r.plan.templateName,
1117
1319
  template_version: r.plan.templateVersion,
1118
- outcome: r.outcome
1320
+ outcome: r.outcome,
1321
+ source: "config-composer"
1119
1322
  };
1120
1323
  if (r.outcome === "created") {
1121
1324
  entry.content_hash_sha256 = hashContent(r.plan.content);
@@ -1123,6 +1326,38 @@ function writeManifest(targetDir, results) {
1123
1326
  }
1124
1327
  return entry;
1125
1328
  });
1329
+ if (walkerResult) {
1330
+ for (const copied of walkerResult.copiedEntries) {
1331
+ entries.push({
1332
+ path: copied.path,
1333
+ template: `bundle:${copied.path}`,
1334
+ template_version: index.version,
1335
+ outcome: "created",
1336
+ source: "bundle",
1337
+ scope: copied.scope,
1338
+ ...copied.content_hash_sha256 ? { content_hash_sha256: copied.content_hash_sha256 } : {},
1339
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
1340
+ });
1341
+ }
1342
+ for (const path of walkerResult.refused) {
1343
+ entries.push({
1344
+ path,
1345
+ template: `bundle:${path}`,
1346
+ template_version: index.version,
1347
+ outcome: "refused",
1348
+ source: "bundle"
1349
+ });
1350
+ }
1351
+ for (const path of walkerResult.errored) {
1352
+ entries.push({
1353
+ path,
1354
+ template: `bundle:${path}`,
1355
+ template_version: index.version,
1356
+ outcome: "error",
1357
+ source: "bundle"
1358
+ });
1359
+ }
1360
+ }
1126
1361
  const manifestDir = node_path.join(targetDir, ".bassclef");
1127
1362
  const manifestPath = node_path.join(manifestDir, "init.manifest.json");
1128
1363
  const content = manifestTemplate({
@@ -1133,7 +1368,12 @@ function writeManifest(targetDir, results) {
1133
1368
  try {
1134
1369
  mkdirSafely(manifestDir);
1135
1370
  writeSafely(manifestPath, content, { force: true });
1136
- } catch {
1371
+ } catch (e) {
1372
+ const reason = e instanceof Error ? e.message : String(e);
1373
+ process.stderr.write(
1374
+ `bassclef init: could not write ${manifestPath} (${reason}). The files were written. Run \`bassclef init --force\` to rebuild the record.
1375
+ `
1376
+ );
1137
1377
  }
1138
1378
  }
1139
1379
  function usage$2() {
@@ -1584,7 +1824,10 @@ function emitL2Output(decisions, verbose) {
1584
1824
  }
1585
1825
  }
1586
1826
  function updateManifestEntry(manifest, path, patch) {
1587
- const idx = manifest.files.findIndex((f) => f.path === path);
1827
+ const wantScope = patch.scope;
1828
+ const idx = manifest.files.findIndex(
1829
+ (f) => f.path === path && (wantScope === void 0 || f.scope === wantScope)
1830
+ );
1588
1831
  if (idx < 0) {
1589
1832
  manifest.files.push({
1590
1833
  path,