@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.
- package/README.md +46 -62
- package/bin/inject-claude-settings.mjs +4 -0
- package/dist/claude-hashes.json +9 -9
- package/dist/commands/index.d.ts +1 -1
- package/dist/commands/runCli/index.d.ts +1 -1
- package/dist/commands/runCli/runCli.cjs +27 -5
- package/dist/commands/runCli/runCli.d.ts +10 -6
- package/dist/commands/runCli/runCli.mjs +27 -5
- package/dist/commands/runCli/type.d.ts +3 -12
- package/dist/commands/runCli/utils/classifyTarget.cjs +48 -0
- package/dist/commands/runCli/utils/classifyTarget.d.ts +19 -0
- package/dist/commands/runCli/utils/classifyTarget.mjs +46 -0
- package/dist/commands/runCli/utils/injectOne.cjs +2 -3
- package/dist/commands/runCli/utils/injectOne.d.ts +1 -1
- package/dist/commands/runCli/utils/injectOne.mjs +2 -3
- package/dist/commands/runCli/utils/resolvePackage.cjs +77 -0
- package/dist/commands/runCli/utils/resolvePackage.d.ts +16 -0
- package/dist/commands/runCli/utils/resolvePackage.mjs +74 -0
- package/dist/commands/runCli/utils/resolveScopeAlias.cjs +69 -0
- package/dist/commands/runCli/utils/resolveScopeAlias.d.ts +2 -0
- package/dist/commands/runCli/utils/resolveScopeAlias.mjs +67 -0
- package/dist/commands/runCli/utils/resolveTargets.cjs +40 -0
- package/dist/commands/runCli/utils/resolveTargets.d.ts +15 -0
- package/dist/commands/runCli/utils/resolveTargets.mjs +38 -0
- package/dist/commands/runCli/utils/runInject.cjs +38 -22
- package/dist/commands/runCli/utils/runInject.d.ts +3 -2
- package/dist/commands/runCli/utils/runInject.mjs +38 -22
- package/dist/core/injectDocs/utils/applyAction.cjs +1 -1
- package/dist/core/injectDocs/utils/applyAction.mjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/utils/version.cjs +1 -1
- package/dist/utils/version.d.ts +1 -1
- package/dist/utils/version.mjs +1 -1
- package/docs/claude/skills/claude-docs-asset-wiring/SKILL.md +159 -0
- package/docs/claude/skills/claude-docs-asset-wiring/knowledge/claude-md-template.md +86 -0
- package/docs/claude/skills/claude-docs-asset-wiring/knowledge/dependency-cruiser.md +54 -0
- package/docs/claude/skills/claude-docs-asset-wiring/knowledge/gotchas.md +122 -0
- package/docs/claude/skills/claude-docs-asset-wiring/knowledge/package-json-patches.md +145 -0
- package/docs/claude/skills/claude-docs-asset-wiring/knowledge/reference-files.md +37 -0
- package/docs/claude/skills/claude-docs-asset-wiring/knowledge/smoke-tests.md +111 -0
- package/docs/consumer-integration.md +41 -100
- package/package.json +2 -2
- package/bin/claude-sync.mjs +0 -24
- package/docs/claude/skills/claude-sync-applier/SKILL.md +0 -195
- package/docs/claude/skills/claude-sync-applier/knowledge/claude-md-template.md +0 -77
- package/docs/claude/skills/claude-sync-applier/knowledge/dependency-cruiser.md +0 -126
- package/docs/claude/skills/claude-sync-applier/knowledge/gotchas.md +0 -139
- package/docs/claude/skills/claude-sync-applier/knowledge/package-json-patches.md +0 -130
- package/docs/claude/skills/claude-sync-applier/knowledge/reference-files.md +0 -120
- 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
|
|
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": "
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
```
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
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`.
|
|
62
|
+
Plus `"sideEffects": false` in `package.json`. These ensure the docs tree never leaks into consumer runtime bundles.
|
|
105
63
|
|
|
106
|
-
|
|
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
|
-
|
|
66
|
+
## 4. End-user invocations
|
|
67
|
+
|
|
68
|
+
| Install topology | Invocation |
|
|
109
69
|
|---|---|
|
|
110
|
-
|
|
|
111
|
-
|
|
|
112
|
-
|
|
|
113
|
-
|
|
|
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/
|
|
81
|
+
.claude/ ← reused target (auto-located)
|
|
123
82
|
packages/
|
|
124
|
-
@your-scope/your-package/
|
|
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
|
-
|
|
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
|
-
##
|
|
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-
|
|
150
|
-
- [ ] `node bin/claude-
|
|
151
|
-
- [ ]
|
|
152
|
-
- [ ]
|
|
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.
|
|
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-
|
|
42
|
+
"inject-claude-settings": "./bin/inject-claude-settings.mjs"
|
|
43
43
|
},
|
|
44
44
|
"files": [
|
|
45
45
|
"dist",
|
package/bin/claude-sync.mjs
DELETED
|
@@ -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.
|