@slats/claude-assets-sync 0.2.0 → 0.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.
Files changed (158) hide show
  1. package/README.md +47 -63
  2. package/bin/inject-claude-settings.mjs +4 -0
  3. package/dist/claude-hashes.json +9 -9
  4. package/dist/commands/index.d.ts +1 -1
  5. package/dist/commands/runCli/index.d.ts +1 -1
  6. package/dist/commands/runCli/runCli.d.ts +10 -6
  7. package/dist/commands/runCli/runCli.mjs +33 -6
  8. package/dist/commands/runCli/type.d.ts +4 -12
  9. package/dist/commands/runCli/utils/classifyTarget.d.ts +19 -0
  10. package/dist/commands/runCli/utils/classifyTarget.mjs +46 -0
  11. package/dist/commands/runCli/utils/renderOrFallback.d.ts +6 -0
  12. package/dist/commands/runCli/utils/renderOrFallback.mjs +12 -0
  13. package/dist/commands/runCli/utils/renderPlain.d.ts +11 -0
  14. package/dist/commands/runCli/utils/renderPlain.mjs +89 -0
  15. package/dist/commands/runCli/utils/resolvePackage.d.ts +16 -0
  16. package/dist/commands/runCli/utils/resolvePackage.mjs +74 -0
  17. package/dist/commands/runCli/utils/resolveScopeAlias.d.ts +2 -0
  18. package/dist/commands/runCli/utils/resolveScopeAlias.mjs +67 -0
  19. package/dist/commands/runCli/utils/resolveScopeFlag.d.ts +9 -1
  20. package/dist/commands/runCli/utils/resolveScopeFlag.mjs +5 -11
  21. package/dist/commands/runCli/utils/resolveTargets.d.ts +15 -0
  22. package/dist/commands/runCli/utils/resolveTargets.mjs +38 -0
  23. package/dist/commands/runCli/utils/toConsumerPackages.d.ts +9 -0
  24. package/dist/commands/runCli/utils/toConsumerPackages.mjs +26 -0
  25. package/dist/core/index.d.ts +2 -2
  26. package/dist/core/injectDocs/index.d.ts +3 -2
  27. package/dist/core/injectDocs/type.d.ts +0 -19
  28. package/dist/core/injectDocs/utils/applyAction.mjs +1 -1
  29. package/dist/core/scope/index.d.ts +1 -1
  30. package/dist/core/scope/scope.d.ts +0 -1
  31. package/dist/core/scope/scope.mjs +1 -4
  32. package/dist/index.d.ts +2 -2
  33. package/dist/index.mjs +2 -2
  34. package/dist/ui/InjectApp/InjectApp.d.ts +2 -0
  35. package/dist/ui/InjectApp/InjectApp.mjs +82 -0
  36. package/dist/ui/InjectApp/index.d.ts +2 -0
  37. package/dist/ui/InjectApp/utils/eventSelectors.d.ts +5 -0
  38. package/dist/ui/InjectApp/utils/eventSelectors.mjs +24 -0
  39. package/dist/ui/InjectApp/utils/phaseReducer.d.ts +2 -0
  40. package/dist/ui/InjectApp/utils/phaseReducer.mjs +130 -0
  41. package/dist/ui/InjectApp/utils/renderInjectApp.d.ts +2 -0
  42. package/dist/ui/InjectApp/utils/renderInjectApp.mjs +19 -0
  43. package/dist/ui/InjectApp/utils/type.d.ts +5 -0
  44. package/dist/ui/components/ActionRow.d.ts +7 -0
  45. package/dist/ui/components/ActionRow.mjs +45 -0
  46. package/dist/ui/components/Banner.d.ts +7 -0
  47. package/dist/ui/components/Banner.mjs +9 -0
  48. package/dist/ui/components/ConfirmForce.d.ts +8 -0
  49. package/dist/ui/components/ConfirmForce.mjs +35 -0
  50. package/dist/ui/components/ErrorPanel.d.ts +6 -0
  51. package/dist/ui/components/ErrorPanel.mjs +14 -0
  52. package/dist/ui/components/Footer.d.ts +8 -0
  53. package/dist/ui/components/Footer.mjs +27 -0
  54. package/dist/ui/components/PlanTable.d.ts +8 -0
  55. package/dist/ui/components/PlanTable.mjs +15 -0
  56. package/dist/ui/components/ProgressBar.d.ts +10 -0
  57. package/dist/ui/components/ProgressBar.mjs +28 -0
  58. package/dist/ui/components/ScopePicker.d.ts +7 -0
  59. package/dist/ui/components/ScopePicker.mjs +26 -0
  60. package/dist/ui/components/Spinner.d.ts +8 -0
  61. package/dist/ui/components/Spinner.mjs +10 -0
  62. package/dist/ui/components/StatusBadge.d.ts +8 -0
  63. package/dist/ui/components/StepTracker.d.ts +9 -0
  64. package/dist/ui/components/StepTracker.mjs +43 -0
  65. package/dist/ui/components/Summary.d.ts +9 -0
  66. package/dist/ui/components/Summary.mjs +30 -0
  67. package/dist/ui/components/TargetCard.d.ts +11 -0
  68. package/dist/ui/components/TargetCard.mjs +29 -0
  69. package/dist/ui/hooks/useApplyStep.d.ts +12 -0
  70. package/dist/ui/hooks/useApplyStep.mjs +30 -0
  71. package/dist/ui/hooks/useExitApp.d.ts +8 -0
  72. package/dist/ui/hooks/useExitApp.mjs +19 -0
  73. package/dist/ui/hooks/useForceConfirmStep.d.ts +9 -0
  74. package/dist/ui/hooks/useForceConfirmStep.mjs +24 -0
  75. package/dist/ui/hooks/useInjectSession.d.ts +10 -0
  76. package/dist/ui/hooks/useInjectSession.mjs +63 -0
  77. package/dist/ui/hooks/useInterval.d.ts +1 -0
  78. package/dist/ui/hooks/usePhase.d.ts +2 -0
  79. package/dist/ui/hooks/usePhase.mjs +9 -0
  80. package/dist/ui/hooks/usePlanStep.d.ts +13 -0
  81. package/dist/ui/hooks/usePlanStep.mjs +94 -0
  82. package/dist/ui/hooks/useResolveStep.d.ts +18 -0
  83. package/dist/ui/hooks/useResolveStep.mjs +21 -0
  84. package/dist/ui/hooks/useTerminalWidth.d.ts +1 -0
  85. package/dist/ui/index.d.ts +2 -0
  86. package/dist/ui/index.mjs +16 -0
  87. package/dist/ui/theme/colors.d.ts +12 -0
  88. package/dist/ui/theme/colors.mjs +9 -0
  89. package/dist/ui/theme/icons.d.ts +29 -0
  90. package/dist/ui/theme/icons.mjs +17 -0
  91. package/dist/ui/theme/layout.d.ts +20 -0
  92. package/dist/ui/theme/layout.mjs +9 -0
  93. package/dist/ui/types/event.d.ts +45 -0
  94. package/dist/ui/types/index.d.ts +4 -0
  95. package/dist/ui/types/phase.d.ts +44 -0
  96. package/dist/ui/types/render.d.ts +6 -0
  97. package/dist/ui/types/target.d.ts +25 -0
  98. package/dist/utils/version.d.ts +1 -1
  99. package/dist/utils/version.mjs +1 -1
  100. package/docs/claude/skills/claude-docs-asset-wiring/SKILL.md +159 -0
  101. package/docs/claude/skills/claude-docs-asset-wiring/knowledge/claude-md-template.md +78 -0
  102. package/docs/claude/skills/claude-docs-asset-wiring/knowledge/dependency-cruiser.md +54 -0
  103. package/docs/claude/skills/claude-docs-asset-wiring/knowledge/gotchas.md +125 -0
  104. package/docs/claude/skills/claude-docs-asset-wiring/knowledge/package-json-patches.md +150 -0
  105. package/docs/claude/skills/claude-docs-asset-wiring/knowledge/reference-files.md +37 -0
  106. package/docs/claude/skills/claude-docs-asset-wiring/knowledge/smoke-tests.md +111 -0
  107. package/docs/consumer-integration.md +43 -101
  108. package/package.json +13 -8
  109. package/scripts/dev-ui-fixtures.ts +288 -0
  110. package/scripts/dev-ui.tsx +289 -0
  111. package/bin/claude-sync.mjs +0 -24
  112. package/dist/commands/runCli/runCli.cjs +0 -31
  113. package/dist/commands/runCli/utils/injectOne.cjs +0 -48
  114. package/dist/commands/runCli/utils/injectOne.d.ts +0 -3
  115. package/dist/commands/runCli/utils/injectOne.mjs +0 -46
  116. package/dist/commands/runCli/utils/resolveScopeFlag.cjs +0 -28
  117. package/dist/commands/runCli/utils/runInject.cjs +0 -36
  118. package/dist/commands/runCli/utils/runInject.d.ts +0 -2
  119. package/dist/commands/runCli/utils/runInject.mjs +0 -34
  120. package/dist/core/buildPlan/buildPlan.cjs +0 -42
  121. package/dist/core/buildPlan/utils/toPosix.cjs +0 -9
  122. package/dist/core/buildPlan/utils/walkFiles.cjs +0 -25
  123. package/dist/core/hash/hash.cjs +0 -30
  124. package/dist/core/hashManifest/hashManifest.cjs +0 -27
  125. package/dist/core/injectDocs/injectDocs.cjs +0 -43
  126. package/dist/core/injectDocs/injectDocs.d.ts +0 -2
  127. package/dist/core/injectDocs/injectDocs.mjs +0 -41
  128. package/dist/core/injectDocs/utils/applyAction.cjs +0 -21
  129. package/dist/core/injectDocs/utils/emitCiForceList.cjs +0 -10
  130. package/dist/core/injectDocs/utils/emitCiForceList.d.ts +0 -2
  131. package/dist/core/injectDocs/utils/emitCiForceList.mjs +0 -8
  132. package/dist/core/injectDocs/utils/printPlan.cjs +0 -20
  133. package/dist/core/injectDocs/utils/printPlan.d.ts +0 -2
  134. package/dist/core/injectDocs/utils/printPlan.mjs +0 -18
  135. package/dist/core/injectDocs/utils/summarize.cjs +0 -27
  136. package/dist/core/scope/scope.cjs +0 -46
  137. package/dist/core/scope/utils/isDirectory.cjs +0 -14
  138. package/dist/index.cjs +0 -20
  139. package/dist/prompts/confirmForce.cjs +0 -27
  140. package/dist/prompts/confirmForce.d.ts +0 -1
  141. package/dist/prompts/confirmForce.mjs +0 -25
  142. package/dist/prompts/index.d.ts +0 -2
  143. package/dist/prompts/selectScope.cjs +0 -30
  144. package/dist/prompts/selectScope.d.ts +0 -2
  145. package/dist/prompts/selectScope.mjs +0 -28
  146. package/dist/utils/asyncPool.cjs +0 -26
  147. package/dist/utils/heartbeat.cjs +0 -25
  148. package/dist/utils/heartbeat.d.ts +0 -16
  149. package/dist/utils/heartbeat.mjs +0 -23
  150. package/dist/utils/logger.cjs +0 -74
  151. package/dist/utils/version.cjs +0 -5
  152. package/docs/claude/skills/claude-sync-applier/SKILL.md +0 -195
  153. package/docs/claude/skills/claude-sync-applier/knowledge/claude-md-template.md +0 -77
  154. package/docs/claude/skills/claude-sync-applier/knowledge/dependency-cruiser.md +0 -126
  155. package/docs/claude/skills/claude-sync-applier/knowledge/gotchas.md +0 -139
  156. package/docs/claude/skills/claude-sync-applier/knowledge/package-json-patches.md +0 -130
  157. package/docs/claude/skills/claude-sync-applier/knowledge/reference-files.md +0 -120
  158. package/docs/claude/skills/claude-sync-applier/knowledge/smoke-tests.md +0 -102
@@ -0,0 +1,54 @@
1
+ # Dependency-Cruiser Isolation Rule (optional)
2
+
3
+ Skip this step unless `${TARGET_PATH}/.dependency-cruiser.cjs`
4
+ already exists or the user explicitly asks. This guardrail is a
5
+ CI-time check that the consumer's `src/**` never reaches the
6
+ Claude assets tree.
7
+
8
+ The post-v0.3.0 layout removes the bin stub entirely, so the
9
+ former `src-no-bin` and `src-no-claude-assets-sync` rules are no
10
+ longer load-bearing — the consumer no longer imports any of that
11
+ from anywhere. The one remaining invariant is `src/**` must not
12
+ import from `docs/**`.
13
+
14
+ ## Rule
15
+
16
+ ```javascript
17
+ module.exports = {
18
+ forbidden: [
19
+ {
20
+ name: 'src-no-docs',
21
+ severity: 'error',
22
+ comment:
23
+ 'src/ must not import from docs/. docs/claude/** contains pure markdown ' +
24
+ 'assets meant only for the engine dispatcher, not for the library runtime.',
25
+ from: { path: '^src/' },
26
+ to: { path: '^docs/' },
27
+ },
28
+ ],
29
+ options: {
30
+ doNotFollow: { path: 'node_modules' },
31
+ includeOnly: '^(src|docs)',
32
+ },
33
+ };
34
+ ```
35
+
36
+ ## Optional script
37
+
38
+ ```json
39
+ "scripts": {
40
+ "depcheck": "depcruise src docs --config .dependency-cruiser.cjs --no-progress"
41
+ }
42
+ ```
43
+
44
+ Zero errors expected. Orphan warnings on `docs/**` are
45
+ acceptable — the docs tree never imports anything.
46
+
47
+ ## Legacy rules removed
48
+
49
+ Previous revisions of this skill had three forbidden rules
50
+ (`src-no-bin`, `src-no-docs`, `src-no-claude-assets-sync`), a
51
+ `no-orphans` adjustment excluding `^bin/`, and an `includeOnly`
52
+ covering `^src` and `^bin`. All of those assumed the consumer
53
+ owned a `bin/` directory. The new layout owns no `bin/`, so the
54
+ extra rules are dead. Do not reintroduce them.
@@ -0,0 +1,125 @@
1
+ # Invariants and Gotchas
2
+
3
+ Hard-earned rules. Each one reflects a previous incident or a design
4
+ constraint of the `@slats/claude-assets-sync` engine.
5
+
6
+ ---
7
+
8
+ ## The engine is the only CLI surface
9
+
10
+ `inject-claude-settings` lives in one place: the engine package. No
11
+ consumer has a bin. No consumer has a `bin/` directory. No consumer
12
+ has a `scripts/` directory. If you find yourself "adapting" a stub
13
+ for a new consumer, stop — wiring does not need code; it needs two
14
+ fields in `package.json`.
15
+
16
+ ---
17
+
18
+ ## `claude.assetPath` is the opt-in marker
19
+
20
+ The engine's `claude-build-hashes` bin silently no-ops when
21
+ `claude.assetPath` is missing or not a string. The dispatcher
22
+ (`inject-claude-settings`) exits 2 with a clear error when a target
23
+ lacks the field. Both behaviors are intentional: missing = opt-out.
24
+
25
+ Do not add "helpful" error messages at build time for the opt-out
26
+ case — it would break silently-disabled packages.
27
+
28
+ ---
29
+
30
+ ## `@slats/claude-assets-sync` must be in `devDependencies`
31
+
32
+ Not `dependencies`, not `peerDependencies`. Reasons:
33
+
34
+ 1. The engine is CLI-only. Declaring it in `dependencies` would
35
+ pull `commander`, `@inquirer/prompts`, and their transitive
36
+ trees into every end-user's production install even though the
37
+ consumer's runtime never imports the engine.
38
+ 2. The monorepo build chain still resolves `.bin/claude-build-hashes`
39
+ from `devDependencies` at `yarn install` time — yarn workspaces
40
+ link devDeps and deps identically for workspace-local builds.
41
+ 3. End users never rely on a hoisted `inject-claude-settings` bin.
42
+ The canonical invocation is `npx -p @slats/claude-assets-sync
43
+ inject-claude-settings --package=<THIS>`, which fetches the
44
+ engine on demand and caches it.
45
+ 4. Bundle isolation is enforced by the import graph (`src/**`
46
+ never references the engine), not by dependency-type.
47
+
48
+ Every consumer's CLAUDE.md documents the single `npx -p` path.
49
+
50
+ ---
51
+
52
+ ## Never add `./bin/*` or `./docs/*` to `exports`
53
+
54
+ The `exports` map in `package.json` controls which subpaths a
55
+ consumer's bundler can resolve. Keeping `./docs/*` out of
56
+ `exports` is what prevents a bundler from deep-importing the docs
57
+ tree into app bundles.
58
+
59
+ ---
60
+
61
+ ## Do not commit `dist/claude-hashes.json`
62
+
63
+ It is a build artifact. The `yarn build` chain regenerates it via
64
+ `build:hashes`. It should be in `.gitignore` (usually via a
65
+ catch-all `dist/` rule). If you see it in `git status`, stop —
66
+ something is misconfigured.
67
+
68
+ ---
69
+
70
+ ## `yarn workspace ${PACKAGE_NAME} build` can fail with `rollup: command not found`
71
+
72
+ Yarn v4 workspace dispatch does not always propagate the
73
+ workspace-local PATH. Prefer `yarn ${SHORTCUT} build` from the
74
+ monorepo root, where `${SHORTCUT}` is the root-level script alias
75
+ (e.g. `yarn schemaForm`, `yarn claudeAssetsSync`).
76
+
77
+ If no shortcut exists, the full form may still work depending on
78
+ yarn version and cache state — but if it fails with `rollup:
79
+ command not found`, add a shortcut to the root `package.json`
80
+ rather than debugging the nested call.
81
+
82
+ ---
83
+
84
+ ## `--scope=project` walks upward
85
+
86
+ `--scope=project` walks `process.cwd()` upward looking for an
87
+ existing `.claude` directory. The first one found is reused; if
88
+ none is found, the engine creates one at `cwd`.
89
+
90
+ Consequence: running the smoke tests from the monorepo root would
91
+ reuse the monorepo's real `.claude`, corrupting it. Always run
92
+ smoke tests from `/tmp/...` with a fresh directory.
93
+
94
+ ---
95
+
96
+ ## Dispatcher exception to the `src/core` purity rule
97
+
98
+ `src/core/**` never reads `package.json` or walks the filesystem.
99
+ The engine's `bin/inject-claude-settings.mjs` and
100
+ `src/commands/runCli/utils/resolvePackage.ts` are allowed to
101
+ `createRequire().resolve(`${name}/package.json`)` for exactly one
102
+ target — the one named in `--package=<name>`. The dispatcher never
103
+ enumerates, never walks `node_modules` for siblings. Preserve this
104
+ boundary: extensions like `--all` or workspace scan require
105
+ explicit re-architecture.
106
+
107
+ ---
108
+
109
+ ## Commit this change alone
110
+
111
+ The change set from this skill touches the consumer's
112
+ `package.json` and possibly its `CLAUDE.md`. It should land in a
113
+ single commit, with no unrelated changes interleaved.
114
+
115
+ Reasons:
116
+
117
+ - Easier to revert as a unit if an issue appears downstream.
118
+ - The CI signal (smoke tests) is bound to the state of these files
119
+ and nothing else.
120
+ - Reviewers can skim-verify against the reference consumer without
121
+ reviewing business logic.
122
+
123
+ If the user asks to bundle with other work, push back once:
124
+ recommend a separate commit. If they still want it bundled,
125
+ proceed but note it in the Step 6 report.
@@ -0,0 +1,150 @@
1
+ # `package.json` Patches
2
+
3
+ All edits below are **additive**. Existing non-conflicting values
4
+ remain untouched. On any conflicting existing value, stop and ask the
5
+ user — do not overwrite.
6
+
7
+ Reference: `packages/canard/schema-form/package.json`.
8
+
9
+ ---
10
+
11
+ ## 1. `claude.assetPath`
12
+
13
+ ```json
14
+ "claude": {
15
+ "assetPath": "docs/claude"
16
+ }
17
+ ```
18
+
19
+ Consumer-side convention — the engine does not enforce it. Relative
20
+ to the consumer's package root. If the field already exists with a
21
+ non-default value, preserve it.
22
+
23
+ A missing or non-string value is an intentional opt-out: the
24
+ dispatcher will exit 2 with a clear error, and `claude-build-hashes`
25
+ will silently no-op. Do not remove the opt-out path.
26
+
27
+ ---
28
+
29
+ ## 2. `scripts.build`
30
+
31
+ Ensure the build chain invokes `yarn build:hashes` at the end:
32
+
33
+ ```json
34
+ "scripts": {
35
+ "build": "rollup -c && yarn build:types && yarn build:hashes"
36
+ }
37
+ ```
38
+
39
+ **Guard against double-append.** If the existing value already
40
+ contains `build:hashes`, leave it alone.
41
+
42
+ ---
43
+
44
+ ## 3. `scripts.build:hashes`
45
+
46
+ Point to the engine's bin (NOT a local stub):
47
+
48
+ ```json
49
+ "scripts": {
50
+ "build:hashes": "claude-build-hashes"
51
+ }
52
+ ```
53
+
54
+ `claude-build-hashes` reads `process.cwd()/package.json` and picks
55
+ up `claude.assetPath`. Works because `@slats/claude-assets-sync` is
56
+ in `devDependencies`, so `node_modules/.bin/claude-build-hashes` is
57
+ linked at workspace install time (yarn workspaces link devDeps and
58
+ deps identically for workspace-local builds).
59
+
60
+ If a different `build:hashes` script exists, ask.
61
+
62
+ ---
63
+
64
+ ## 4. `scripts.prepublishOnly`
65
+
66
+ ```json
67
+ "scripts": {
68
+ "prepublishOnly": "yarn build"
69
+ }
70
+ ```
71
+
72
+ Guarantees `dist/claude-hashes.json` is regenerated before publish.
73
+ If the target already has a `prepublishOnly` that calls `yarn build`
74
+ (directly or transitively), leave it alone.
75
+
76
+ ---
77
+
78
+ ## 5. `devDependencies."@slats/claude-assets-sync"`
79
+
80
+ **Must be in `devDependencies`, never `dependencies` or
81
+ `peerDependencies`.**
82
+
83
+ ```json
84
+ "devDependencies": {
85
+ "@slats/claude-assets-sync": "workspace:^"
86
+ }
87
+ ```
88
+
89
+ Reasons:
90
+
91
+ - The engine is CLI-only. Declaring it in `dependencies` would pull
92
+ `commander`, `@inquirer/prompts`, and their transitive trees into
93
+ every end-user's production install even though the consumer's
94
+ runtime never imports the engine.
95
+ - The monorepo build chain still resolves `.bin/claude-build-hashes`
96
+ from `devDependencies` at `yarn install` time — yarn workspaces
97
+ link devDeps and deps identically for workspace-local builds.
98
+ - End users invoke the engine via `npx -p @slats/claude-assets-sync
99
+ inject-claude-settings --package=<THIS>`, which fetches the engine
100
+ on demand and caches it. No transitive bin hoist is required.
101
+ - Bundle isolation is enforced by the import graph (`src/**` never
102
+ references the engine), not by dependency-type.
103
+
104
+ If the target already has it in `dependencies` or
105
+ `peerDependencies`, move it to `devDependencies`. Do not duplicate.
106
+
107
+ ---
108
+
109
+ ## 6. `files`
110
+
111
+ Ship the published artifact surface. Keep `"dist"`, `"docs"`, and
112
+ `"README.md"` (plus whatever else the package needs). Do NOT
113
+ include `"bin"` or `"scripts"`:
114
+
115
+ ```json
116
+ "files": [
117
+ "dist",
118
+ "docs",
119
+ "README.md"
120
+ ]
121
+ ```
122
+
123
+ If `files` is absent, create it with at least `["dist", "docs", "README.md"]`.
124
+
125
+ ---
126
+
127
+ ## 7. `bin` — MUST be ABSENT
128
+
129
+ Never add a `bin` field. Bin names collide across consumers under
130
+ `node_modules/.bin/` and the engine is the sole CLI surface.
131
+
132
+ ---
133
+
134
+ ## 8. `exports` — never add `./bin/*` or `./docs/*`
135
+
136
+ Exports control which subpaths a consumer's bundler can resolve.
137
+ Keeping `./bin/*` and `./docs/*` out of `exports` is what prevents
138
+ consumer bundlers from pulling the CLI or the asset tree into app
139
+ bundles.
140
+
141
+ ---
142
+
143
+ ## Full Reference
144
+
145
+ See `packages/canard/schema-form/package.json` for the canonical
146
+ shape. The relevant keys are `scripts.build`,
147
+ `scripts.build:hashes`, `scripts.prepublishOnly`,
148
+ `dependencies."@slats/claude-assets-sync"`, `claude.assetPath`, and
149
+ `files`. Everything else in that file is schema-form-specific and
150
+ must not be copied.
@@ -0,0 +1,37 @@
1
+ # Reference Files
2
+
3
+ Consumers do **not** own any runtime files for the injector. The whole
4
+ CLI surface lives in `@slats/claude-assets-sync`. A consumer is wired
5
+ up by editing `package.json` and (optionally) `CLAUDE.md` — nothing
6
+ else.
7
+
8
+ Reference consumer: `packages/canard/schema-form`.
9
+
10
+ ## What the consumer MUST own
11
+
12
+ - `docs/claude/**` — the assets to ship (skills / rules / commands).
13
+ - `package.json.claude.assetPath` — string, usually `"docs/claude"`.
14
+
15
+ ## What the consumer MUST NOT own
16
+
17
+ - Any `bin/` directory or stub file. The engine owns the dispatcher.
18
+ - Any `scripts/build-hashes.mjs` wrapper. Use the engine's
19
+ `claude-build-hashes` bin directly in `scripts.build:hashes`.
20
+ - Any `"bin"` entry in `package.json`.
21
+ - `./bin/*` or `./docs/*` exposed in `exports`. Exposing them would
22
+ let bundlers pull CLI code or the docs tree into app bundles.
23
+
24
+ ## What the engine provides
25
+
26
+ - `inject-claude-settings` bin — dispatcher. Invoked as
27
+ `npx -p @slats/claude-assets-sync inject-claude-settings --package=<name> --scope=<scope>`.
28
+ The engine is a consumer-side `devDependency` only, so end users
29
+ never get a hoisted bin; the `npx -p` form pulls the engine on
30
+ demand and caches it.
31
+ - `claude-build-hashes` bin — reads `process.cwd()/package.json`,
32
+ picks up `claude.assetPath`, hashes every file beneath it, and
33
+ writes `dist/claude-hashes.json`. Run via `yarn build:hashes` in
34
+ the consumer build chain.
35
+ - `buildHashes()` + `injectDocs()` — headless programmatic APIs.
36
+
37
+ No content mirroring across consumers. No stub drift to manage.
@@ -0,0 +1,111 @@
1
+ # E2E Smoke Tests — 8-path matrix via engine dispatcher
2
+
3
+ **Run from `/tmp/...` — never from the monorepo root or `${TARGET_PATH}/`.**
4
+
5
+ `--scope=project` walks `cwd` upward looking for an existing `.claude`
6
+ directory. Running from the monorepo would reuse or mutate the real
7
+ repo's `.claude`, which is a destructive error.
8
+
9
+ No fake `node_modules` needed — the engine uses
10
+ `createRequire(import.meta.url).resolve(`${PACKAGE_NAME}/package.json`)`
11
+ from the engine's own installed location.
12
+
13
+ ---
14
+
15
+ ## Setup
16
+
17
+ ```bash
18
+ BIN="$PWD/packages/slats/claude-assets-sync/bin/inject-claude-settings.mjs"
19
+ DIR=/tmp/inject-smoke-${SHORTCUT:-target}
20
+ [ -d "$DIR" ] && find "$DIR" -mindepth 1 -delete
21
+ mkdir -p "$DIR" && cd "$DIR"
22
+ ```
23
+
24
+ `[ -d ... ] && find -delete` keeps the setup idempotent. **Never** use
25
+ `rm -rf` or unquoted `*` globs — too easy to nuke the wrong directory.
26
+
27
+ ---
28
+
29
+ ## Matrix
30
+
31
+ Execute sequentially. `EXIT=$?` after each so the value is captured
32
+ before the next command overwrites `$?`.
33
+
34
+ | # | Command | Expected exit | Purpose |
35
+ |----|--------------------------------------------------------------------------------------------------|---------------|--------------------------------------------------------------|
36
+ | 1 | `node "$BIN" --package=${PACKAGE_NAME} --scope=project --dry-run` | 0 | Dry run — previews actions, no writes. |
37
+ | 2 | `node "$BIN" --package=${PACKAGE_NAME} --scope=project` | 0 | First real install — writes `.claude/` under `$DIR`. |
38
+ | 3 | `node "$BIN" --package=${PACKAGE_NAME} --scope=project` | 0 | Re-run — no-op (idempotent). |
39
+ | 4 | (after tampering) `CI=true node "$BIN" --package=${PACKAGE_NAME} --scope=project` | **2** | CI + tampered content → refuse to overwrite. |
40
+ | 5 | `CI=true node "$BIN" --package=${PACKAGE_NAME} --scope=project --force` | 0 | `--force` overrides the refusal. |
41
+ | 6 | `CI=true node "$BIN" --package=${PACKAGE_NAME}` | **2** | Missing `--scope` in non-TTY context. |
42
+ | 7 | `node "$BIN"` | **2** | Missing `--package` (dispatcher-specific). |
43
+ | 8 | `node "$BIN" --package=@does/not-exist` | **2** | Unresolvable package (dispatcher-specific). |
44
+
45
+ ### Tamper step (between path 3 and path 4)
46
+
47
+ ```bash
48
+ find .claude -name SKILL.md -exec sh -c 'echo tampered >> "$1"' _ {} \;
49
+ ```
50
+
51
+ Appends `tampered` to every `SKILL.md` under the local `.claude/`.
52
+ Simulates a human edit that the CI-mode dispatcher must detect and
53
+ refuse to clobber.
54
+
55
+ ---
56
+
57
+ ## Execution Shape
58
+
59
+ Split into **two bash calls** because `cwd` resets between Bash tool
60
+ invocations.
61
+
62
+ **First call** — paths 1–3:
63
+
64
+ ```bash
65
+ BIN="$PWD/packages/slats/claude-assets-sync/bin/inject-claude-settings.mjs"
66
+ DIR=/tmp/inject-smoke-${SHORTCUT:-target}
67
+ [ -d "$DIR" ] && find "$DIR" -mindepth 1 -delete
68
+ mkdir -p "$DIR" && cd "$DIR"
69
+
70
+ node "$BIN" --package=${PACKAGE_NAME} --scope=project --dry-run; echo "EXIT=$?"
71
+ node "$BIN" --package=${PACKAGE_NAME} --scope=project; echo "EXIT=$?"
72
+ node "$BIN" --package=${PACKAGE_NAME} --scope=project; echo "EXIT=$?"
73
+ ```
74
+
75
+ **Second call** — paths 4–8:
76
+
77
+ ```bash
78
+ DIR=/tmp/inject-smoke-${SHORTCUT:-target}
79
+ BIN="$PWD/packages/slats/claude-assets-sync/bin/inject-claude-settings.mjs"
80
+ cd "$DIR"
81
+
82
+ find .claude -name SKILL.md -exec sh -c 'echo tampered >> "$1"' _ {} \;
83
+ CI=true node "$BIN" --package=${PACKAGE_NAME} --scope=project; echo "EXIT=$?"
84
+ CI=true node "$BIN" --package=${PACKAGE_NAME} --scope=project --force; echo "EXIT=$?"
85
+ CI=true node "$BIN" --package=${PACKAGE_NAME}; echo "EXIT=$?"
86
+ node "$BIN"; echo "EXIT=$?"
87
+ node "$BIN" --package=@does/not-exist; echo "EXIT=$?"
88
+ ```
89
+
90
+ Note: `$PWD` in the second call is the parent shell's cwd (monorepo
91
+ root), so `BIN` resolves correctly. `cd "$DIR"` then moves into the
92
+ smoke directory before invoking.
93
+
94
+ ---
95
+
96
+ ## Failure Handling
97
+
98
+ | Observed | Meaning | Action |
99
+ |----------|-------------------------------------------------------------------------|--------------------------------------------------------------------|
100
+ | 1 ≠ 0 | Dry-run crashed. Likely an engine bug or bad `claude.assetPath`. | Stop, capture stderr, report. |
101
+ | 2 ≠ 0 | First write failed. Permissions, engine bug, or manifest issue. | Stop, inspect `dist/claude-hashes.json`, report. |
102
+ | 3 ≠ 0 | Idempotency broken — re-run should be no-op. | Stop, diff `$DIR/.claude` before/after, report. |
103
+ | 4 = 0 | CI mode did not refuse tampered files. Safety regression. | Stop — the engine's CI gate is broken. |
104
+ | 5 ≠ 0 | `--force` failed to override. Check engine. | Stop, report. |
105
+ | 6 = 0 | Engine defaulted a scope in non-TTY context. Should require `--scope`. | Stop, report. |
106
+ | 7 = 0 | Dispatcher accepted no `--package`. Violates contract. | Stop — dispatcher bug. |
107
+ | 8 = 0 | Dispatcher succeeded on unresolvable package. Violates contract. | Stop — dispatcher bug. |
108
+
109
+ Do not attempt to "make the tests pass" by altering expectations. The
110
+ matrix encodes invariants of the engine — a mismatch is a real
111
+ regression upstream.