@slats/claude-assets-sync 0.2.0 → 0.3.0

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 (50) hide show
  1. package/README.md +46 -62
  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.cjs +27 -5
  7. package/dist/commands/runCli/runCli.d.ts +10 -6
  8. package/dist/commands/runCli/runCli.mjs +27 -5
  9. package/dist/commands/runCli/type.d.ts +3 -12
  10. package/dist/commands/runCli/utils/classifyTarget.cjs +48 -0
  11. package/dist/commands/runCli/utils/classifyTarget.d.ts +19 -0
  12. package/dist/commands/runCli/utils/classifyTarget.mjs +46 -0
  13. package/dist/commands/runCli/utils/injectOne.cjs +2 -3
  14. package/dist/commands/runCli/utils/injectOne.d.ts +1 -1
  15. package/dist/commands/runCli/utils/injectOne.mjs +2 -3
  16. package/dist/commands/runCli/utils/resolvePackage.cjs +77 -0
  17. package/dist/commands/runCli/utils/resolvePackage.d.ts +16 -0
  18. package/dist/commands/runCli/utils/resolvePackage.mjs +74 -0
  19. package/dist/commands/runCli/utils/resolveScopeAlias.cjs +69 -0
  20. package/dist/commands/runCli/utils/resolveScopeAlias.d.ts +2 -0
  21. package/dist/commands/runCli/utils/resolveScopeAlias.mjs +67 -0
  22. package/dist/commands/runCli/utils/resolveTargets.cjs +40 -0
  23. package/dist/commands/runCli/utils/resolveTargets.d.ts +15 -0
  24. package/dist/commands/runCli/utils/resolveTargets.mjs +38 -0
  25. package/dist/commands/runCli/utils/runInject.cjs +38 -22
  26. package/dist/commands/runCli/utils/runInject.d.ts +3 -2
  27. package/dist/commands/runCli/utils/runInject.mjs +38 -22
  28. package/dist/core/injectDocs/utils/applyAction.cjs +1 -1
  29. package/dist/core/injectDocs/utils/applyAction.mjs +1 -1
  30. package/dist/index.d.ts +1 -1
  31. package/dist/utils/version.cjs +1 -1
  32. package/dist/utils/version.d.ts +1 -1
  33. package/dist/utils/version.mjs +1 -1
  34. package/docs/claude/skills/claude-docs-asset-wiring/SKILL.md +159 -0
  35. package/docs/claude/skills/claude-docs-asset-wiring/knowledge/claude-md-template.md +86 -0
  36. package/docs/claude/skills/claude-docs-asset-wiring/knowledge/dependency-cruiser.md +54 -0
  37. package/docs/claude/skills/claude-docs-asset-wiring/knowledge/gotchas.md +122 -0
  38. package/docs/claude/skills/claude-docs-asset-wiring/knowledge/package-json-patches.md +145 -0
  39. package/docs/claude/skills/claude-docs-asset-wiring/knowledge/reference-files.md +37 -0
  40. package/docs/claude/skills/claude-docs-asset-wiring/knowledge/smoke-tests.md +111 -0
  41. package/docs/consumer-integration.md +41 -100
  42. package/package.json +2 -2
  43. package/bin/claude-sync.mjs +0 -24
  44. package/docs/claude/skills/claude-sync-applier/SKILL.md +0 -195
  45. package/docs/claude/skills/claude-sync-applier/knowledge/claude-md-template.md +0 -77
  46. package/docs/claude/skills/claude-sync-applier/knowledge/dependency-cruiser.md +0 -126
  47. package/docs/claude/skills/claude-sync-applier/knowledge/gotchas.md +0 -139
  48. package/docs/claude/skills/claude-sync-applier/knowledge/package-json-patches.md +0 -130
  49. package/docs/claude/skills/claude-sync-applier/knowledge/reference-files.md +0 -120
  50. package/docs/claude/skills/claude-sync-applier/knowledge/smoke-tests.md +0 -102
@@ -1,6 +1,6 @@
1
1
  # Consumer Integration Template
2
2
 
3
- How to make a package "claude-sync aware" so `claude-sync` discovers and injects its `docs/claude/` tree into end-user `.claude` directories.
3
+ How to make a package ship Claude Code assets through the `inject-claude-settings` dispatcher. Two fields in `package.json`; no stub code.
4
4
 
5
5
  ## 1. `package.json` additions
6
6
 
@@ -8,146 +8,87 @@ How to make a package "claude-sync aware" so `claude-sync` discovers and injects
8
8
  {
9
9
  "name": "@your-scope/your-package",
10
10
  "version": "…",
11
- "bin": {
12
- "claude-sync": "./bin/claude-sync.mjs"
13
- },
14
- "files": [
15
- "dist",
16
- "docs",
17
- "dist/claude-hashes.json",
18
- "bin",
19
- "README.md"
20
- ],
21
11
  "scripts": {
22
12
  "build": "… && yarn build:hashes",
23
- "build:hashes": "node scripts/build-hashes.mjs"
13
+ "build:hashes": "claude-build-hashes"
24
14
  },
25
15
  "dependencies": {
26
- "@slats/claude-assets-sync": "workspace:^",
16
+ "@slats/claude-assets-sync": "workspace:^"
27
17
  },
18
+ "files": ["dist", "docs", "README.md"],
28
19
  "claude": {
29
20
  "assetPath": "docs/claude"
30
21
  }
31
22
  }
32
23
  ```
33
24
 
34
- Do **not** expose `./bin/*` in `exports`. That would let consumer bundlers accidentally pull CLI code into app bundles.
35
-
36
- ## 2. `bin/claude-sync.mjs` (3-line re-export stub)
37
-
38
- ```javascript
39
- #!/usr/bin/env node
40
- import { runCli } from '@slats/claude-assets-sync';
41
-
42
- runCli(process.argv, { invokedFromBin: import.meta.url }).catch((err) => {
43
- process.stderr.write(
44
- `[@your-scope/your-package] claude-sync failed: ${err instanceof Error ? err.message : String(err)}\n`,
45
- );
46
- process.exit(1);
47
- });
48
- ```
49
-
50
- `runCli` determines the implicit `--package` target in this priority order:
25
+ - `@slats/claude-assets-sync` MUST be in `dependencies`, not `devDependencies` or `peerDependencies`.
26
+ - Do **not** add any `bin` field. Bin names collide across consumers under `node_modules/.bin/` and the engine is the sole CLI surface.
27
+ - Do **not** expose `./bin/*` or `./docs/*` in `exports`. Exposing them would let bundlers pull CLI code or the docs tree into app bundles.
28
+ - Do **not** create a `bin/` or `scripts/` directory in the consumer. The engine's `claude-build-hashes` bin (resolved via `node_modules/.bin/`) handles build-time hashing.
51
29
 
52
- 1. `--all` or `--package=<name>` (explicit)
53
- 2. The consumer that owns `process.cwd()` (i.e. the terminal you launched from). When you `cd` into a consumer's package directory and run `yarn claude-sync`, that consumer is picked automatically.
54
- 3. The consumer that owns `invokedFromBin` (fallback). This keeps bare `npx <pkg> claude-sync` working from arbitrary cwds — including from inside another consumer's directory where option 2 would have picked a different package.
55
- 4. The sole discovered consumer, if exactly one exists.
56
- 5. Otherwise an error asking for `--package=<name>` or `--all`.
30
+ ## 2. `docs/claude/` authoring
57
31
 
58
- Passing `invokedFromBin: import.meta.url` remains the mechanism for the fallback case. Omit it in slats's own global bin so it behaves as a cross-consumer dispatcher.
59
-
60
- Remember `chmod +x bin/claude-sync.mjs` (or rely on `files` entry to ship executable bit via npm).
61
-
62
- ## 3. `scripts/build-hashes.mjs` (one line import)
32
+ Any file tree works, but the recommended layout is:
63
33
 
64
- ```javascript
65
- #!/usr/bin/env node
66
- import { buildHashes } from '@slats/claude-assets-sync/buildHashes';
67
-
68
- try {
69
- const { outPath, fileCount } = await buildHashes();
70
- console.log(`✓ claude-hashes.json written: ${fileCount} file(s) → ${outPath}`);
71
- } catch (err) {
72
- console.error('❌ buildHashes failed:', err?.message ?? err);
73
- process.exit(1);
74
- }
34
+ ```
35
+ docs/claude/
36
+ ├── skills/
37
+ │ └── <skill-name>/
38
+ │ ├── SKILL.md
39
+ │ └── knowledge/...
40
+ ├── rules/...
41
+ └── commands/...
75
42
  ```
76
43
 
77
- This reads the current `package.json` + its `claude.assetPath`, hashes every file under the asset root (ignoring `.omc/**`, `*.log`, `.DS_Store`), and writes `dist/claude-hashes.json`.
44
+ The build step (`yarn build:hashes` `claude-build-hashes`) hashes every file under `docs/claude/` relative to the asset root and writes `dist/claude-hashes.json`. On inject, the CLI copies `skills`/`rules`/`commands` into the matching subtree under `.claude/`.
78
45
 
79
- ## 4. Isolation guardrails (optional but recommended)
46
+ ## 3. Isolation guardrails (optional but recommended)
80
47
 
81
48
  In a `.dependency-cruiser.cjs`:
82
49
 
83
50
  ```javascript
84
- {
85
- name: 'src-no-bin',
86
- severity: 'error',
87
- from: { path: '^src/' },
88
- to: { path: '^bin/' },
89
- },
90
51
  {
91
52
  name: 'src-no-docs',
92
53
  severity: 'error',
54
+ comment:
55
+ 'src/ must not import from docs/. docs/claude/** contains pure markdown ' +
56
+ 'assets meant only for the engine dispatcher, not for the library runtime.',
93
57
  from: { path: '^src/' },
94
58
  to: { path: '^docs/' },
95
59
  },
96
- {
97
- name: 'src-no-claude-assets-sync',
98
- severity: 'error',
99
- from: { path: '^src/' },
100
- to: { path: 'node_modules/@slats/claude-assets-sync' },
101
- },
102
60
  ```
103
61
 
104
- Plus `"sideEffects": false` in `package.json`. The guardrails ensure the CLI engine never leaks into consumer runtime bundles.
62
+ Plus `"sideEffects": false` in `package.json`. These ensure the docs tree never leaks into consumer runtime bundles.
105
63
 
106
- ## 5. End-user invocations
64
+ The legacy `src-no-bin` and `src-no-claude-assets-sync` rules are no longer load-bearing — the consumer owns no `bin/`, and the engine isn't referenced from `src/` anyway. Do not reintroduce them.
107
65
 
108
- | Install topology | Working invocations |
66
+ ## 4. End-user invocations
67
+
68
+ | Install topology | Invocation |
109
69
  |---|---|
110
- | Consumer is a **direct dep** of the user's project | `npx claude-sync --scope=user` *(bare)* |
111
- | Always works (preferred in docs) | `npx -p @your-scope/your-package claude-sync --scope=user` |
112
- | Consumer is a **transitive dep** | `npx -p @your-scope/your-package claude-sync --scope=user` |
113
- | User has no consumer installed | `npx @slats/claude-assets-sync --package=@your-scope/your-package --scope=user` |
114
- | Multiple consumers discovered | `npx claude-sync --package=@your-scope/your-package` *or* `npx claude-sync --all` |
70
+ | End user installs consumer as a **direct dep** on npm / yarn-classic | `npx inject-claude-settings --package=@your-scope/your-package --scope=user` |
71
+ | End user installs consumer as a **direct dep** on pnpm strict / yarn-berry PnP | `npx -p @slats/claude-assets-sync inject-claude-settings --package=@your-scope/your-package --scope=user` |
72
+ | End user has **no consumer installed** yet | `npx -p @slats/claude-assets-sync inject-claude-settings --package=@your-scope/your-package --scope=user` (resolves to whatever is on the registry under that exact name) |
73
+ | End user has **multiple consumers installed** | Call `inject-claude-settings --package=<one-name>` per target. There is no `--all`. |
115
74
 
116
75
  ### Scope resolution (project)
117
76
 
118
- For `--scope=project`, the target `.claude` directory is resolved by walking up from `process.cwd()` and reusing the first existing `.claude` directory found. Only if no ancestor owns a `.claude` does the CLI fall back to `process.cwd()/.claude`.
77
+ For `--scope=project`, the target `.claude` directory is resolved by walking up from `process.cwd()` and reusing the first existing `.claude` directory found. Only if no ancestor owns a `.claude` does the CLI fall back to `process.cwd()/.claude`. The CLI logs `(auto-located)` when this happens.
119
78
 
120
79
  ```
121
80
  workspace/
122
- .claude/ ← reused target (auto-located)
81
+ .claude/ ← reused target (auto-located)
123
82
  packages/
124
- @your-scope/your-package/ ← cd here and run claude-sync
125
- bin/claude-sync.mjs
126
- ```
127
-
128
- Running `yarn claude-sync --scope=project` from `packages/@your-scope/your-package/` injects into `workspace/.claude`, not `packages/@your-scope/your-package/.claude`. The CLI logs `(auto-located)` in its resolution line when this happens.
129
-
130
- ## 6. Authoring `docs/claude/`
131
-
132
- Any file tree works, but the recommended layout is:
133
-
134
- ```
135
- docs/claude/
136
- ├── skills/
137
- │ └── <skill-name>/
138
- │ ├── SKILL.md
139
- │ └── knowledge/...
140
- ├── rules/...
141
- └── commands/...
83
+ @your-scope/your-package/ ← cd here and run inject-claude-settings
142
84
  ```
143
85
 
144
- The hash manifest tracks every file under `docs/claude/` relative to the asset root. On inject, the CLI copies skills/rules/commands into the matching subtree under `.claude/`.
86
+ Running `inject-claude-settings --package=@your-scope/your-package --scope=project` from `packages/@your-scope/your-package/` injects into `workspace/.claude`, not `packages/@your-scope/your-package/.claude`.
145
87
 
146
- ## 7. Verification checklist
88
+ ## 5. Verification checklist
147
89
 
148
90
  - [ ] `yarn build` succeeds and emits `dist/claude-hashes.json` alongside the rest of `dist/`.
149
- - [ ] `node bin/claude-sync.mjs --help` prints the `claude-sync` subcommand tree.
150
- - [ ] `node bin/claude-sync.mjs list --json` emits an entry for your package with `hashesPresent: true`.
151
- - [ ] `node bin/claude-sync.mjs --scope=project --dry-run --package=@your-scope/your-package` emits a copy plan.
152
- - [ ] `yarn depcheck` (or whatever your dep-cruiser invocation is named) reports zero new violations.
153
- - [ ] Consumer bundler tree-shakes away CLI code (verify by greping the built bundle: should contain zero references to `@slats/claude-assets-sync`).
91
+ - [ ] `node packages/slats/claude-assets-sync/bin/inject-claude-settings.mjs --help` prints the dispatcher usage.
92
+ - [ ] `node packages/slats/claude-assets-sync/bin/inject-claude-settings.mjs --package=@your-scope/your-package --scope=project --dry-run` emits a copy plan when run from `/tmp/...`.
93
+ - [ ] If you enabled Section 3's depcheck, `yarn depcheck` reports zero violations.
94
+ - [ ] Consumer bundler tree-shakes away docs — grep the built bundle for `@slats/claude-assets-sync`, `inject-claude-settings`, and `docs/claude`; all three should be absent.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slats/claude-assets-sync",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Shared CLI engine that lets consumer packages inject their Claude docs (skills, rules, commands) into a user's .claude directory via a thin bin/inject-docs wrapper.",
5
5
  "keywords": [
6
6
  "claude",
@@ -39,7 +39,7 @@
39
39
  "types": "dist/index.d.ts",
40
40
  "bin": {
41
41
  "claude-build-hashes": "./scripts/claude-build-hashes.mjs",
42
- "claude-sync": "./bin/claude-sync.mjs"
42
+ "inject-claude-settings": "./bin/inject-claude-settings.mjs"
43
43
  },
44
44
  "files": [
45
45
  "dist",
@@ -1,24 +0,0 @@
1
- #!/usr/bin/env node
2
- import { runCli } from '@slats/claude-assets-sync';
3
- import { readFile } from 'node:fs/promises';
4
- import { dirname, resolve } from 'node:path';
5
- import { fileURLToPath } from 'node:url';
6
-
7
- const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
8
- const pkg = JSON.parse(
9
- await readFile(resolve(packageRoot, 'package.json'), 'utf-8'),
10
- );
11
-
12
- if (typeof pkg.claude?.assetPath === 'string') {
13
- runCli(process.argv, {
14
- packageRoot,
15
- packageName: pkg.name,
16
- packageVersion: pkg.version,
17
- assetPath: pkg.claude.assetPath,
18
- }).catch((err) => {
19
- process.stderr.write(
20
- `[${pkg.name}] claude-sync failed: ${err instanceof Error ? err.message : String(err)}\n`,
21
- );
22
- process.exit(1);
23
- });
24
- }
@@ -1,195 +0,0 @@
1
- ---
2
- name: claude-sync-applier
3
- description: "Wire the claude-sync CLI onto a consumer package in this monorepo. Copies verbatim bin/claude-sync.mjs and scripts/build-hashes.mjs stubs, patches package.json, updates CLAUDE.md, runs E2E smoke tests, and verifies bundle isolation. Idempotent — asks before clobbering."
4
- user-invocable: true
5
- disable-model-invocation: true
6
- argument-hint: <target-package-path>
7
- ---
8
-
9
- # claude-sync-applier
10
-
11
- Replicate the `claude-sync` bin setup on a target consumer package. The engine
12
- is `@slats/claude-assets-sync`; the reference consumer is `packages/canard/schema-form`.
13
-
14
- The bin stub reads its own `package.json` via `import.meta.url` and hands the
15
- engine `{ packageRoot, packageName, packageVersion, assetPath }`. Each invocation
16
- targets exactly one consumer — it does not discover other packages.
17
-
18
- **Outcome**
19
-
20
- ```bash
21
- npx <PACKAGE_NAME> claude-sync --scope=user|project [--dry-run] [--force] [--root=<cwd>]
22
- ```
23
-
24
- ## Role
25
-
26
- You are a monorepo wiring specialist. Execute the 10 steps below as a single,
27
- idempotent procedure. On any conflicting existing value — ask the user before
28
- overwriting. Never clobber silently.
29
-
30
- ## Knowledge Resources
31
-
32
- Consult these files as needed during execution. Do NOT preload everything;
33
- load on demand.
34
-
35
- - `knowledge/reference-files.md` — source stubs (`bin/claude-sync.mjs`, `scripts/build-hashes.mjs`) with expected contents and rationale
36
- - `knowledge/package-json-patches.md` — every required `package.json` edit, with guard conditions
37
- - `knowledge/claude-md-template.md` — the `## Claude Docs Injector` section to inject into the target `CLAUDE.md`
38
- - `knowledge/dependency-cruiser.md` — optional Step 4: three forbidden rules + config shape for static isolation
39
- - `knowledge/smoke-tests.md` — E2E 6-path matrix with expected exit codes and why
40
- - `knowledge/gotchas.md` — invariants, isolation guardrails, pitfalls
41
-
42
- ## Inputs
43
-
44
- Resolve these before starting. If any is missing, stop and ask.
45
-
46
- | Variable | Source |
47
- |-------------------|------------------------------------------------------------------------------------------------------|
48
- | `TARGET_PATH` | Skill argument (e.g. `packages/lerx/promise-modal`). If absent, ask the user. |
49
- | `PACKAGE_NAME` | `name` field of `${TARGET_PATH}/package.json`. |
50
- | `SHORTCUT` | Root `package.json` `scripts` entry whose value equals `yarn workspace ${PACKAGE_NAME}`; else unset. |
51
-
52
- `SHORTCUT` is a convenience only. When unset, fall back to full workspace
53
- syntax: `yarn workspace ${PACKAGE_NAME} <subcommand>`.
54
-
55
- ## Pre-Flight
56
-
57
- Stop and report on any failure. Do not attempt to fix silently.
58
-
59
- - [ ] `${TARGET_PATH}/docs/claude/skills/<name>/SKILL.md` and `knowledge/*.md` exist — the docs to be injected.
60
- - [ ] `${TARGET_PATH}/package.json` has `"type": "module"` and `"sideEffects": false`.
61
- - [ ] Build pipeline uses `rollup -c && yarn build:types` where `build:types` runs `node ../../aileron/script/build/buildTypes.mjs`.
62
- - [ ] `git status` in `${TARGET_PATH}` is clean. Unrelated changes present → confirm with user before proceeding.
63
-
64
- ## Steps
65
-
66
- Execute in order. Each step is idempotent; on conflict, ask rather than overwrite.
67
-
68
- ### Step 1 — Create `${TARGET_PATH}/bin/claude-sync.mjs`
69
-
70
- Copy verbatim from the reference consumer. See `knowledge/reference-files.md`
71
- for the expected content and the source path. `chmod +x` the result.
72
-
73
- ### Step 2 — Create `${TARGET_PATH}/scripts/build-hashes.mjs`
74
-
75
- Copy verbatim. See `knowledge/reference-files.md`.
76
-
77
- ### Step 3 — Patch `${TARGET_PATH}/package.json`
78
-
79
- See `knowledge/package-json-patches.md` for the complete patch list:
80
-
81
- - `bin` entry
82
- - `files` append
83
- - `scripts.build` append (guarded)
84
- - `scripts.build:hashes`
85
- - `scripts.prepublishOnly`
86
- - `dependencies."@slats/claude-assets-sync"` (NOT devDependencies)
87
- - `claude.assetPath` default
88
-
89
- Do NOT add `./bin/*` to `exports` — ever.
90
-
91
- ### Step 4 — (Optional) dependency-cruiser isolation gate
92
-
93
- Skip unless `${TARGET_PATH}/.dependency-cruiser.cjs` already exists or the user
94
- explicitly asks. Legacy `.dependency-cruiser.js` → out of scope; flag to user.
95
-
96
- When applicable, see `knowledge/dependency-cruiser.md` for the three forbidden
97
- rules, `no-orphans` adjustment, `includeOnly` expansion, and `depcheck` script.
98
-
99
- ### Step 5 — Patch `${TARGET_PATH}/CLAUDE.md`
100
-
101
- If `CLAUDE.md` exists, append the `## Claude Docs Injector` section from
102
- `knowledge/claude-md-template.md`, substituting `@canard/schema-form` →
103
- `${PACKAGE_NAME}`. Keep the Isolation Guardrails subsection. Skip if
104
- `CLAUDE.md` does not exist.
105
-
106
- ### Step 6 — Install and build
107
-
108
- ```bash
109
- yarn install
110
- yarn ${SHORTCUT:-workspace ${PACKAGE_NAME}} build
111
- ```
112
-
113
- Expected: `rollup` → `buildTypes` → `build:hashes` succeed, and
114
- `${TARGET_PATH}/dist/claude-hashes.json` is written.
115
-
116
- ### Step 7 — E2E smoke tests (6 paths)
117
-
118
- Run from `/tmp/...`, never from the monorepo root or `${TARGET_PATH}/` —
119
- `--scope=project` walks cwd upward looking for an existing `.claude`, and would
120
- mutate the real repo's. See `knowledge/smoke-tests.md` for the full 6-path
121
- matrix, expected exit codes, and rationale.
122
-
123
- Split into two bash calls (paths 1–3, then 4–6). cwd resets between calls; the
124
- `[ -d ... ] && find -delete` prefix keeps it idempotent. Never use `rm -rf` or
125
- unquoted `*` globs.
126
-
127
- ### Step 8 — Bundle isolation grep (must be empty)
128
-
129
- ```bash
130
- grep -rE "@slats/claude-assets-sync|docs/claude|claude-sync" \
131
- ${TARGET_PATH}/dist/index.mjs ${TARGET_PATH}/dist/index.cjs
132
- ```
133
-
134
- Pass = exit code 1 (no matches). Any match → stop; CLI has leaked into the
135
- library bundle. See `knowledge/gotchas.md` for the three-layer isolation model.
136
-
137
- ### Step 9 — depcheck (only if Step 4 ran)
138
-
139
- ```bash
140
- yarn ${SHORTCUT:-workspace ${PACKAGE_NAME}} depcheck
141
- ```
142
-
143
- Zero errors. Pre-existing `no-orphans` warnings are acceptable.
144
-
145
- ### Step 10 — Report
146
-
147
- Summarize:
148
-
149
- - Files written vs. skipped (with reason for each skip)
150
- - Manifest file count from `dist/claude-hashes.json`
151
- - Smoke-test exit codes (all 6)
152
- - Grep result (expected: no matches)
153
- - depcheck result (or "static isolation not enforced" if Step 4 skipped)
154
- - Recommendation: commit this change on its own, separate from other work
155
-
156
- ## Report Template
157
-
158
- ```markdown
159
- ## apply-claude-sync — ${PACKAGE_NAME}
160
-
161
- **Files**
162
- - bin/claude-sync.mjs — created | unchanged | asked-user
163
- - scripts/build-hashes.mjs — created | unchanged | asked-user
164
- - package.json — patched: [bin, files, scripts.build, …]
165
- - CLAUDE.md — section added | skipped (no CLAUDE.md)
166
- - .dependency-cruiser.cjs — updated | skipped (not present)
167
-
168
- **Manifest**
169
- - dist/claude-hashes.json: <N> files
170
-
171
- **Smoke tests**
172
- | # | command | expected | actual |
173
- |---|-------------------------------------------------|----------|--------|
174
- | 1 | --scope=project --dry-run | 0 | <n> |
175
- | 2 | --scope=project | 0 | <n> |
176
- | 3 | --scope=project (up-to-date) | 0 | <n> |
177
- | 4 | CI=true --scope=project (tampered) | 2 | <n> |
178
- | 5 | CI=true --scope=project --force | 0 | <n> |
179
- | 6 | CI=true (missing --scope) | 2 | <n> |
180
-
181
- **Isolation**
182
- - grep on dist/index.{mjs,cjs}: no matches ✓
183
- - depcheck: <result | "static isolation not enforced">
184
-
185
- **Next**: commit on its own — do not bundle with unrelated changes.
186
- ```
187
-
188
- ## Termination Conditions
189
-
190
- - **Pre-Flight fails** → stop, report the failing check. Do not proceed.
191
- - **Conflict during patch** → stop, show the diff, ask user whether to overwrite.
192
- - **Build fails at Step 6** → stop, report error. Do not run smoke tests on a broken build.
193
- - **Smoke test mismatch** → stop, report the failing path with captured exit code.
194
- - **Bundle grep finds matches** → stop, report which file leaked. Isolation is broken.
195
- - **All steps pass** → emit the report from the template above.
@@ -1,77 +0,0 @@
1
- # `CLAUDE.md` — `## Claude Docs Injector` section
2
-
3
- Reference: `packages/canard/schema-form/CLAUDE.md`.
4
-
5
- Append the section below to `${TARGET_PATH}/CLAUDE.md` if the file exists.
6
- Substitute `@canard/schema-form` → `${PACKAGE_NAME}`. Skip the entire step if
7
- `CLAUDE.md` does not exist (do not create one).
8
-
9
- The template is intentionally terse: CLI usage + essential isolation warnings.
10
- Architectural rationale (three-layer isolation model, silent no-op design,
11
- stub mechanics) lives in this skill's `knowledge/gotchas.md` — do not
12
- duplicate it into every consumer's `CLAUDE.md`.
13
-
14
- ---
15
-
16
- ## Template
17
-
18
- ````markdown
19
- ## Claude Docs Injector
20
-
21
- Thin CLI stub that injects `docs/claude/` assets into the user's `.claude`
22
- directory. Engine: `@slats/claude-assets-sync`.
23
-
24
- ```bash
25
- npx claude-sync --scope=user # ~/.claude
26
- npx claude-sync --scope=project # nearest existing .claude walking up from cwd
27
- npx claude-sync --scope=user --dry-run # preview
28
- npx claude-sync --scope=user --force # overwrite local edits
29
-
30
- npx -p @canard/schema-form claude-sync --scope=user # transitive-dep context
31
- ```
32
-
33
- ### Isolation Guardrails
34
-
35
- - `src/**` MUST NOT import from `bin/**`, `docs/**`, or `@slats/claude-assets-sync`.
36
- - **Never add `./bin/*` to `exports`.**
37
- - `yarn depcheck` enforces the isolation in CI.
38
- ````
39
-
40
- ---
41
-
42
- ## Substitution Rules
43
-
44
- - Replace `@canard/schema-form` with `${PACKAGE_NAME}` (one occurrence, in the
45
- `npx -p` line).
46
- - Preserve the Isolation Guardrails bullets verbatim — these are the sharp
47
- invariants that must stay consistent across consumers.
48
-
49
- ---
50
-
51
- ## Placement & Skip Conditions
52
-
53
- - Append to end of `CLAUDE.md`. Ensure one blank line before the injected
54
- section.
55
- - `${TARGET_PATH}/CLAUDE.md` does not exist → skip, report "skipped (no CLAUDE.md)".
56
- - Section already present with identical content → skip, report "unchanged".
57
- - Section present with different content → ask user, do not clobber.
58
-
59
- ---
60
-
61
- ## What Was Deliberately Removed
62
-
63
- If a previous version of this skill injected a longer template, these parts
64
- were intentionally dropped:
65
-
66
- - Intro paragraph explaining the stub mechanics — belongs in `gotchas.md`,
67
- not per-package docs.
68
- - `--scope=project` cwd-walk blockquote — the comment beside the command is
69
- enough.
70
- - Per-package structure list (`bin/`, `scripts/`, `docs/claude/`,
71
- `claude.assetPath` convention) — mechanical, same across all consumers,
72
- not useful as per-package documentation.
73
- - Verbose Isolation Guardrails prose — reduced to three one-line rules.
74
-
75
- The principle: per-package `CLAUDE.md` should carry only what an agent or
76
- human needs **specific to this package**. Shared architecture belongs in the
77
- skill's knowledge files.
@@ -1,126 +0,0 @@
1
- # Step 4 — dependency-cruiser isolation gate (optional)
2
-
3
- This step is **only** executed when:
4
-
5
- - `${TARGET_PATH}/.dependency-cruiser.cjs` already exists (static isolation
6
- was previously enforced), OR
7
- - The user explicitly asks for it.
8
-
9
- Do **not** introduce a new `.dependency-cruiser.cjs` into a target package that
10
- has not already opted into static analysis — the runtime isolation (import
11
- graph + `sideEffects: false` + no `bin` in `exports`) is sufficient on its own.
12
-
13
- **Legacy**: `.dependency-cruiser.js` (not `.cjs`) → out of scope. Flag to user
14
- and do not rename.
15
-
16
- Reference: `packages/canard/schema-form/.dependency-cruiser.cjs`.
17
-
18
- ---
19
-
20
- ## Changes to Apply
21
-
22
- When applicable, mirror the reference config. Four edits total:
23
-
24
- ### 1. Append three `forbidden` rules
25
-
26
- Append each with `severity: 'error'`:
27
-
28
- ```js
29
- {
30
- name: 'src-no-bin',
31
- severity: 'error',
32
- comment:
33
- 'src/ must not import from bin/. bin/ is a CLI-only entry point and must ' +
34
- 'never leak into the library bundle.',
35
- from: { path: '^src/' },
36
- to: { path: '^bin/' },
37
- },
38
- {
39
- name: 'src-no-docs',
40
- severity: 'error',
41
- comment:
42
- 'src/ must not import from docs/. docs/claude/** contains pure markdown assets ' +
43
- 'meant only for the inject-docs CLI, not for the library runtime.',
44
- from: { path: '^src/' },
45
- to: { path: '^docs/' },
46
- },
47
- {
48
- name: 'src-no-claude-assets-sync',
49
- severity: 'error',
50
- comment:
51
- '@slats/claude-assets-sync is a CLI-only dependency. It is allowed only ' +
52
- 'from bin/. Importing it from src/ would leak the CLI engine into ' +
53
- 'consumer production bundles.',
54
- from: { path: '^src/' },
55
- to: { path: 'node_modules/@slats/claude-assets-sync' },
56
- },
57
- ```
58
-
59
- Place these alongside the existing `forbidden` rules in the reference config.
60
- Do not duplicate if any of these rule names already exist.
61
-
62
- ### 2. Extend `no-orphans` rule
63
-
64
- Add `'^bin/'` to the existing `no-orphans` rule's `from.pathNot` array. `bin`
65
- entry points are orphans by design (they're invoked as executables, not
66
- imported).
67
-
68
- ```js
69
- from: {
70
- orphan: true,
71
- pathNot: [
72
- '(^|/)[.][^/]+[.](?:js|cjs|mjs|ts|cts|mts|json)$',
73
- '[.]d[.]ts$',
74
- '(^|/)tsconfig[.]json$',
75
- '(^|/)(?:babel|webpack)[.]config[.](?:js|cjs|mjs|ts|cts|mts|json)$',
76
- '^bin/',
77
- ],
78
- },
79
- ```
80
-
81
- ### 3. Expand `options.includeOnly`
82
-
83
- Change to include both `src` and `bin` so dependency-cruiser scans the bin
84
- entry points as well:
85
-
86
- ```js
87
- includeOnly: ['^src', '^bin'],
88
- ```
89
-
90
- ### 4. Add `depcheck` script
91
-
92
- In `${TARGET_PATH}/package.json` `scripts`:
93
-
94
- ```json
95
- "depcheck": "depcruise src bin --config .dependency-cruiser.cjs --no-progress"
96
- ```
97
-
98
- If an existing `depcheck` script points elsewhere, ask the user.
99
-
100
- ---
101
-
102
- ## Post-Step Verification
103
-
104
- After Steps 1–6 complete and this Step 4 has been applied, Step 9 runs:
105
-
106
- ```bash
107
- yarn ${SHORTCUT:-workspace ${PACKAGE_NAME}} depcheck
108
- ```
109
-
110
- Must exit 0 with no errors. Pre-existing `no-orphans` warnings are acceptable.
111
-
112
- ---
113
-
114
- ## Why This Step Is Optional
115
-
116
- The three-layer isolation works without static analysis:
117
-
118
- 1. Import graph: `src/**` never references `bin/**`, `docs/**`, or
119
- `@slats/claude-assets-sync`.
120
- 2. `"sideEffects": false` + `"type": "module"` — dead-code elimination of any
121
- accidental reference.
122
- 3. No `./bin/*` in `exports` — consumer bundlers cannot deep-import into bin.
123
-
124
- dependency-cruiser adds a fourth layer (CI-time regression detection) but is
125
- not required for correctness. Don't force-enable it on a package that hasn't
126
- opted in — it carries real maintenance cost.