@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.
- package/README.md +47 -63
- 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.d.ts +10 -6
- package/dist/commands/runCli/runCli.mjs +33 -6
- package/dist/commands/runCli/type.d.ts +4 -12
- package/dist/commands/runCli/utils/classifyTarget.d.ts +19 -0
- package/dist/commands/runCli/utils/classifyTarget.mjs +46 -0
- package/dist/commands/runCli/utils/renderOrFallback.d.ts +6 -0
- package/dist/commands/runCli/utils/renderOrFallback.mjs +12 -0
- package/dist/commands/runCli/utils/renderPlain.d.ts +11 -0
- package/dist/commands/runCli/utils/renderPlain.mjs +89 -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.d.ts +2 -0
- package/dist/commands/runCli/utils/resolveScopeAlias.mjs +67 -0
- package/dist/commands/runCli/utils/resolveScopeFlag.d.ts +9 -1
- package/dist/commands/runCli/utils/resolveScopeFlag.mjs +5 -11
- package/dist/commands/runCli/utils/resolveTargets.d.ts +15 -0
- package/dist/commands/runCli/utils/resolveTargets.mjs +38 -0
- package/dist/commands/runCli/utils/toConsumerPackages.d.ts +9 -0
- package/dist/commands/runCli/utils/toConsumerPackages.mjs +26 -0
- package/dist/core/index.d.ts +2 -2
- package/dist/core/injectDocs/index.d.ts +3 -2
- package/dist/core/injectDocs/type.d.ts +0 -19
- package/dist/core/injectDocs/utils/applyAction.mjs +1 -1
- package/dist/core/scope/index.d.ts +1 -1
- package/dist/core/scope/scope.d.ts +0 -1
- package/dist/core/scope/scope.mjs +1 -4
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/ui/InjectApp/InjectApp.d.ts +2 -0
- package/dist/ui/InjectApp/InjectApp.mjs +82 -0
- package/dist/ui/InjectApp/index.d.ts +2 -0
- package/dist/ui/InjectApp/utils/eventSelectors.d.ts +5 -0
- package/dist/ui/InjectApp/utils/eventSelectors.mjs +24 -0
- package/dist/ui/InjectApp/utils/phaseReducer.d.ts +2 -0
- package/dist/ui/InjectApp/utils/phaseReducer.mjs +130 -0
- package/dist/ui/InjectApp/utils/renderInjectApp.d.ts +2 -0
- package/dist/ui/InjectApp/utils/renderInjectApp.mjs +19 -0
- package/dist/ui/InjectApp/utils/type.d.ts +5 -0
- package/dist/ui/components/ActionRow.d.ts +7 -0
- package/dist/ui/components/ActionRow.mjs +45 -0
- package/dist/ui/components/Banner.d.ts +7 -0
- package/dist/ui/components/Banner.mjs +9 -0
- package/dist/ui/components/ConfirmForce.d.ts +8 -0
- package/dist/ui/components/ConfirmForce.mjs +35 -0
- package/dist/ui/components/ErrorPanel.d.ts +6 -0
- package/dist/ui/components/ErrorPanel.mjs +14 -0
- package/dist/ui/components/Footer.d.ts +8 -0
- package/dist/ui/components/Footer.mjs +27 -0
- package/dist/ui/components/PlanTable.d.ts +8 -0
- package/dist/ui/components/PlanTable.mjs +15 -0
- package/dist/ui/components/ProgressBar.d.ts +10 -0
- package/dist/ui/components/ProgressBar.mjs +28 -0
- package/dist/ui/components/ScopePicker.d.ts +7 -0
- package/dist/ui/components/ScopePicker.mjs +26 -0
- package/dist/ui/components/Spinner.d.ts +8 -0
- package/dist/ui/components/Spinner.mjs +10 -0
- package/dist/ui/components/StatusBadge.d.ts +8 -0
- package/dist/ui/components/StepTracker.d.ts +9 -0
- package/dist/ui/components/StepTracker.mjs +43 -0
- package/dist/ui/components/Summary.d.ts +9 -0
- package/dist/ui/components/Summary.mjs +30 -0
- package/dist/ui/components/TargetCard.d.ts +11 -0
- package/dist/ui/components/TargetCard.mjs +29 -0
- package/dist/ui/hooks/useApplyStep.d.ts +12 -0
- package/dist/ui/hooks/useApplyStep.mjs +30 -0
- package/dist/ui/hooks/useExitApp.d.ts +8 -0
- package/dist/ui/hooks/useExitApp.mjs +19 -0
- package/dist/ui/hooks/useForceConfirmStep.d.ts +9 -0
- package/dist/ui/hooks/useForceConfirmStep.mjs +24 -0
- package/dist/ui/hooks/useInjectSession.d.ts +10 -0
- package/dist/ui/hooks/useInjectSession.mjs +63 -0
- package/dist/ui/hooks/useInterval.d.ts +1 -0
- package/dist/ui/hooks/usePhase.d.ts +2 -0
- package/dist/ui/hooks/usePhase.mjs +9 -0
- package/dist/ui/hooks/usePlanStep.d.ts +13 -0
- package/dist/ui/hooks/usePlanStep.mjs +94 -0
- package/dist/ui/hooks/useResolveStep.d.ts +18 -0
- package/dist/ui/hooks/useResolveStep.mjs +21 -0
- package/dist/ui/hooks/useTerminalWidth.d.ts +1 -0
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.mjs +16 -0
- package/dist/ui/theme/colors.d.ts +12 -0
- package/dist/ui/theme/colors.mjs +9 -0
- package/dist/ui/theme/icons.d.ts +29 -0
- package/dist/ui/theme/icons.mjs +17 -0
- package/dist/ui/theme/layout.d.ts +20 -0
- package/dist/ui/theme/layout.mjs +9 -0
- package/dist/ui/types/event.d.ts +45 -0
- package/dist/ui/types/index.d.ts +4 -0
- package/dist/ui/types/phase.d.ts +44 -0
- package/dist/ui/types/render.d.ts +6 -0
- package/dist/ui/types/target.d.ts +25 -0
- 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 +78 -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 +125 -0
- package/docs/claude/skills/claude-docs-asset-wiring/knowledge/package-json-patches.md +150 -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 +43 -101
- package/package.json +13 -8
- package/scripts/dev-ui-fixtures.ts +288 -0
- package/scripts/dev-ui.tsx +289 -0
- package/bin/claude-sync.mjs +0 -24
- package/dist/commands/runCli/runCli.cjs +0 -31
- package/dist/commands/runCli/utils/injectOne.cjs +0 -48
- package/dist/commands/runCli/utils/injectOne.d.ts +0 -3
- package/dist/commands/runCli/utils/injectOne.mjs +0 -46
- package/dist/commands/runCli/utils/resolveScopeFlag.cjs +0 -28
- package/dist/commands/runCli/utils/runInject.cjs +0 -36
- package/dist/commands/runCli/utils/runInject.d.ts +0 -2
- package/dist/commands/runCli/utils/runInject.mjs +0 -34
- package/dist/core/buildPlan/buildPlan.cjs +0 -42
- package/dist/core/buildPlan/utils/toPosix.cjs +0 -9
- package/dist/core/buildPlan/utils/walkFiles.cjs +0 -25
- package/dist/core/hash/hash.cjs +0 -30
- package/dist/core/hashManifest/hashManifest.cjs +0 -27
- package/dist/core/injectDocs/injectDocs.cjs +0 -43
- package/dist/core/injectDocs/injectDocs.d.ts +0 -2
- package/dist/core/injectDocs/injectDocs.mjs +0 -41
- package/dist/core/injectDocs/utils/applyAction.cjs +0 -21
- package/dist/core/injectDocs/utils/emitCiForceList.cjs +0 -10
- package/dist/core/injectDocs/utils/emitCiForceList.d.ts +0 -2
- package/dist/core/injectDocs/utils/emitCiForceList.mjs +0 -8
- package/dist/core/injectDocs/utils/printPlan.cjs +0 -20
- package/dist/core/injectDocs/utils/printPlan.d.ts +0 -2
- package/dist/core/injectDocs/utils/printPlan.mjs +0 -18
- package/dist/core/injectDocs/utils/summarize.cjs +0 -27
- package/dist/core/scope/scope.cjs +0 -46
- package/dist/core/scope/utils/isDirectory.cjs +0 -14
- package/dist/index.cjs +0 -20
- package/dist/prompts/confirmForce.cjs +0 -27
- package/dist/prompts/confirmForce.d.ts +0 -1
- package/dist/prompts/confirmForce.mjs +0 -25
- package/dist/prompts/index.d.ts +0 -2
- package/dist/prompts/selectScope.cjs +0 -30
- package/dist/prompts/selectScope.d.ts +0 -2
- package/dist/prompts/selectScope.mjs +0 -28
- package/dist/utils/asyncPool.cjs +0 -26
- package/dist/utils/heartbeat.cjs +0 -25
- package/dist/utils/heartbeat.d.ts +0 -16
- package/dist/utils/heartbeat.mjs +0 -23
- package/dist/utils/logger.cjs +0 -74
- package/dist/utils/version.cjs +0 -5
- 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,88 @@ 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
|
-
"
|
|
26
|
-
"@slats/claude-assets-sync": "workspace:^"
|
|
15
|
+
"devDependencies": {
|
|
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 `devDependencies`. The engine is a CLI-only tool; declaring it in `dependencies` would pull `commander`, `@inquirer/prompts`, and their transitive trees into every end-user's production install even though the consumer's runtime never imports the engine.
|
|
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/` from `devDependencies` at workspace install time) 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.
|
|
63
|
+
|
|
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.
|
|
105
65
|
|
|
106
|
-
##
|
|
66
|
+
## 4. End-user invocations
|
|
107
67
|
|
|
108
|
-
|
|
68
|
+
The engine is not shipped as a runtime dependency of any consumer, so end users never get a hoisted `inject-claude-settings` bin. Always invoke via `npx -p @slats/claude-assets-sync ...`; the package manager fetches and caches the engine on demand.
|
|
69
|
+
|
|
70
|
+
| Scenario | Invocation |
|
|
109
71
|
|---|---|
|
|
110
|
-
|
|
|
111
|
-
|
|
|
112
|
-
|
|
|
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` |
|
|
72
|
+
| Single consumer target | `npx -p @slats/claude-assets-sync inject-claude-settings --package=@your-scope/your-package --scope=user` |
|
|
73
|
+
| All packages under one npm scope | `npx -p @slats/claude-assets-sync inject-claude-settings --package=@your-scope --scope=user` (scope alias — no slash) |
|
|
74
|
+
| Multiple specific targets | Repeat `--package` or comma-separate: `--package=@scope-a --package=@scope-b/pkg`. There is no `--all`. |
|
|
115
75
|
|
|
116
76
|
### Scope resolution (project)
|
|
117
77
|
|
|
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`.
|
|
78
|
+
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
79
|
|
|
120
80
|
```
|
|
121
81
|
workspace/
|
|
122
|
-
.claude/
|
|
82
|
+
.claude/ ← reused target (auto-located)
|
|
123
83
|
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/...
|
|
84
|
+
@your-scope/your-package/ ← cd here and run inject-claude-settings
|
|
142
85
|
```
|
|
143
86
|
|
|
144
|
-
|
|
87
|
+
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
88
|
|
|
146
|
-
##
|
|
89
|
+
## 5. Verification checklist
|
|
147
90
|
|
|
148
91
|
- [ ] `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`).
|
|
92
|
+
- [ ] `node packages/slats/claude-assets-sync/bin/inject-claude-settings.mjs --help` prints the dispatcher usage.
|
|
93
|
+
- [ ] `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/...`.
|
|
94
|
+
- [ ] If you enabled Section 3's depcheck, `yarn depcheck` reports zero violations.
|
|
95
|
+
- [ ] 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.1",
|
|
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",
|
|
@@ -27,19 +27,17 @@
|
|
|
27
27
|
".": {
|
|
28
28
|
"types": "./dist/index.d.ts",
|
|
29
29
|
"source": "./src/index.ts",
|
|
30
|
-
"import": "./dist/index.mjs"
|
|
31
|
-
"require": "./dist/index.cjs"
|
|
30
|
+
"import": "./dist/index.mjs"
|
|
32
31
|
},
|
|
33
32
|
"./buildHashes": {
|
|
34
33
|
"import": "./scripts/buildHashes.mjs"
|
|
35
34
|
}
|
|
36
35
|
},
|
|
37
|
-
"main": "dist/index.cjs",
|
|
38
36
|
"module": "dist/index.mjs",
|
|
39
37
|
"types": "dist/index.d.ts",
|
|
40
38
|
"bin": {
|
|
41
39
|
"claude-build-hashes": "./scripts/claude-build-hashes.mjs",
|
|
42
|
-
"claude-
|
|
40
|
+
"inject-claude-settings": "./bin/inject-claude-settings.mjs"
|
|
43
41
|
},
|
|
44
42
|
"files": [
|
|
45
43
|
"dist",
|
|
@@ -54,6 +52,8 @@
|
|
|
54
52
|
"build:publish:npm": "yarn build && yarn publish:npm",
|
|
55
53
|
"build:types": "node ../../aileron/script/build/buildTypes.mjs",
|
|
56
54
|
"dev": "node scripts/inject-version.js && tsx src/main.ts",
|
|
55
|
+
"dev:ui": "tsx scripts/dev-ui.tsx",
|
|
56
|
+
"dev:cli": "tsx bin/inject-claude-settings.mjs",
|
|
57
57
|
"format": "prettier --write \"src/**/*.ts\"",
|
|
58
58
|
"lint": "eslint \"src/**/*.ts\"",
|
|
59
59
|
"prepublishOnly": "yarn build",
|
|
@@ -64,12 +64,17 @@
|
|
|
64
64
|
"version:patch": "yarn version patch"
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
-
"@inquirer/prompts": "^8.4.2",
|
|
68
67
|
"commander": "^12.1.0",
|
|
69
|
-
"
|
|
68
|
+
"ink": "^7.0.0",
|
|
69
|
+
"ink-gradient": "^4.0.0",
|
|
70
|
+
"ink-select-input": "^6.2.0",
|
|
71
|
+
"ink-spinner": "^5.0.0",
|
|
72
|
+
"picocolors": "^1.1.1",
|
|
73
|
+
"react": "^19.2.0"
|
|
70
74
|
},
|
|
71
75
|
"devDependencies": {
|
|
72
|
-
"@
|
|
76
|
+
"@types/react": "^19.2.0",
|
|
77
|
+
"ink-testing-library": "^4.0.0"
|
|
73
78
|
},
|
|
74
79
|
"claude": {
|
|
75
80
|
"assetPath": "docs/claude"
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
|
|
4
|
+
import type { ConsumerPackage } from '../src/commands/runCli/type.js';
|
|
5
|
+
import type { InjectPlan, Action } from '../src/core/buildPlan/index.js';
|
|
6
|
+
import type { InjectReport, ScopeResolution } from '../src/core/index.js';
|
|
7
|
+
import type {
|
|
8
|
+
ApplyProgress,
|
|
9
|
+
InjectEvent,
|
|
10
|
+
Phase,
|
|
11
|
+
PlanStepState,
|
|
12
|
+
TargetPlan,
|
|
13
|
+
Warning,
|
|
14
|
+
} from '../src/ui/types/index.js';
|
|
15
|
+
|
|
16
|
+
export const PHASES = [
|
|
17
|
+
'resolving',
|
|
18
|
+
'scope-select',
|
|
19
|
+
'planning',
|
|
20
|
+
'diff-review',
|
|
21
|
+
'force-confirm',
|
|
22
|
+
'applying',
|
|
23
|
+
'summary',
|
|
24
|
+
'summary-dry',
|
|
25
|
+
'error',
|
|
26
|
+
] as const;
|
|
27
|
+
|
|
28
|
+
export type PhaseKey = (typeof PHASES)[number];
|
|
29
|
+
|
|
30
|
+
const MOCK_TARGETS: ConsumerPackage[] = [
|
|
31
|
+
{
|
|
32
|
+
name: '@canard/schema-form',
|
|
33
|
+
version: '0.12.1',
|
|
34
|
+
packageRoot: '/workspace/packages/canard/schema-form',
|
|
35
|
+
assetRoot: '/workspace/packages/canard/schema-form/docs/claude',
|
|
36
|
+
hashesPresent: true,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: '@canard/schema-form-antd-plugin',
|
|
40
|
+
version: '0.12.1',
|
|
41
|
+
packageRoot: '/workspace/packages/canard/schema-form-antd-plugin',
|
|
42
|
+
assetRoot: '/workspace/packages/canard/schema-form-antd-plugin/docs/claude',
|
|
43
|
+
hashesPresent: true,
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: '@winglet/common-utils',
|
|
47
|
+
version: '0.12.1',
|
|
48
|
+
packageRoot: '/workspace/packages/winglet/common-utils',
|
|
49
|
+
assetRoot: '/workspace/packages/winglet/common-utils/docs/claude',
|
|
50
|
+
hashesPresent: true,
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
const scopeFixture: ScopeResolution = {
|
|
55
|
+
scope: 'user',
|
|
56
|
+
targetRoot: join(homedir(), '.claude'),
|
|
57
|
+
description: '~/.claude (user)',
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
function makeActions(
|
|
61
|
+
baseDstRoot: string,
|
|
62
|
+
options: {
|
|
63
|
+
copy?: number;
|
|
64
|
+
skip?: number;
|
|
65
|
+
diverged?: number;
|
|
66
|
+
orphan?: number;
|
|
67
|
+
del?: number;
|
|
68
|
+
},
|
|
69
|
+
): Action[] {
|
|
70
|
+
const actions: Action[] = [];
|
|
71
|
+
let fileIdx = 0;
|
|
72
|
+
for (let i = 0; i < (options.copy ?? 0); i += 1) {
|
|
73
|
+
fileIdx += 1;
|
|
74
|
+
actions.push({
|
|
75
|
+
kind: 'copy',
|
|
76
|
+
relPath: `skills/expert/doc-${fileIdx}.md`,
|
|
77
|
+
dstAbs: `${baseDstRoot}/skills/expert/doc-${fileIdx}.md`,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
for (let i = 0; i < (options.skip ?? 0); i += 1) {
|
|
81
|
+
fileIdx += 1;
|
|
82
|
+
actions.push({
|
|
83
|
+
kind: 'skip-uptodate',
|
|
84
|
+
relPath: `skills/expert/existing-${fileIdx}.md`,
|
|
85
|
+
dstAbs: `${baseDstRoot}/skills/expert/existing-${fileIdx}.md`,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
for (let i = 0; i < (options.diverged ?? 0); i += 1) {
|
|
89
|
+
fileIdx += 1;
|
|
90
|
+
actions.push({
|
|
91
|
+
kind: 'warn-diverged',
|
|
92
|
+
relPath: `skills/expert/edited-${fileIdx}.md`,
|
|
93
|
+
dstAbs: `${baseDstRoot}/skills/expert/edited-${fileIdx}.md`,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
for (let i = 0; i < (options.orphan ?? 0); i += 1) {
|
|
97
|
+
fileIdx += 1;
|
|
98
|
+
actions.push({
|
|
99
|
+
kind: 'warn-orphan',
|
|
100
|
+
relPath: `skills/expert/ghost-${fileIdx}.md`,
|
|
101
|
+
dstAbs: `${baseDstRoot}/skills/expert/ghost-${fileIdx}.md`,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
for (let i = 0; i < (options.del ?? 0); i += 1) {
|
|
105
|
+
fileIdx += 1;
|
|
106
|
+
actions.push({
|
|
107
|
+
kind: 'delete',
|
|
108
|
+
relPath: `skills/expert/stale-${fileIdx}.md`,
|
|
109
|
+
dstAbs: `${baseDstRoot}/skills/expert/stale-${fileIdx}.md`,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
return actions;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function makeTargetPlan(
|
|
116
|
+
target: ConsumerPackage,
|
|
117
|
+
actions: Action[],
|
|
118
|
+
requiresForce = false,
|
|
119
|
+
): TargetPlan {
|
|
120
|
+
const plan: InjectPlan = { actions, requiresForce };
|
|
121
|
+
return { target, scope: scopeFixture, plan };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const TP_CLEAN = makeTargetPlan(
|
|
125
|
+
MOCK_TARGETS[0],
|
|
126
|
+
makeActions(scopeFixture.targetRoot, { copy: 4, skip: 2 }),
|
|
127
|
+
false,
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const TP_WARN = makeTargetPlan(
|
|
131
|
+
MOCK_TARGETS[1],
|
|
132
|
+
makeActions(scopeFixture.targetRoot, {
|
|
133
|
+
copy: 2,
|
|
134
|
+
skip: 1,
|
|
135
|
+
diverged: 1,
|
|
136
|
+
orphan: 1,
|
|
137
|
+
}),
|
|
138
|
+
true,
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const TP_THIRD = makeTargetPlan(
|
|
142
|
+
MOCK_TARGETS[2],
|
|
143
|
+
makeActions(scopeFixture.targetRoot, { copy: 3, skip: 5 }),
|
|
144
|
+
false,
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
const PLAN_SET: readonly TargetPlan[] = [TP_CLEAN, TP_WARN, TP_THIRD];
|
|
148
|
+
|
|
149
|
+
const MOCK_WARNINGS: Warning[] = [
|
|
150
|
+
{
|
|
151
|
+
packageName: MOCK_TARGETS[1].name,
|
|
152
|
+
kind: 'warn-diverged',
|
|
153
|
+
relPath: 'skills/expert/edited-4.md',
|
|
154
|
+
description: 'local differs from source',
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
packageName: MOCK_TARGETS[1].name,
|
|
158
|
+
kind: 'warn-orphan',
|
|
159
|
+
relPath: 'skills/expert/ghost-5.md',
|
|
160
|
+
description: 'exists locally but not in manifest',
|
|
161
|
+
},
|
|
162
|
+
];
|
|
163
|
+
|
|
164
|
+
const MOCK_REPORTS: InjectReport[] = PLAN_SET.map((tp) => {
|
|
165
|
+
const report: InjectReport = {
|
|
166
|
+
created: [],
|
|
167
|
+
updated: [],
|
|
168
|
+
skipped: [],
|
|
169
|
+
warnings: [],
|
|
170
|
+
deleted: [],
|
|
171
|
+
exitCode: 0,
|
|
172
|
+
};
|
|
173
|
+
for (const action of tp.plan.actions) {
|
|
174
|
+
if (action.kind === 'copy') report.created.push(action.relPath);
|
|
175
|
+
else if (action.kind === 'skip-uptodate') report.skipped.push(action.relPath);
|
|
176
|
+
else if (action.kind === 'warn-diverged')
|
|
177
|
+
report.warnings.push({ relPath: action.relPath, reason: 'diverged' });
|
|
178
|
+
else if (action.kind === 'warn-orphan')
|
|
179
|
+
report.warnings.push({ relPath: action.relPath, reason: 'orphan' });
|
|
180
|
+
else if (action.kind === 'delete') report.deleted.push(action.relPath);
|
|
181
|
+
}
|
|
182
|
+
return report;
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const MOCK_APPLY_PROGRESS: ApplyProgress = {
|
|
186
|
+
total: 18,
|
|
187
|
+
done: 7,
|
|
188
|
+
current: 'skills/expert/doc-8.md',
|
|
189
|
+
startedAt: Date.now() - 2500,
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
function makePlanningProgress(): ReadonlyMap<string, PlanStepState> {
|
|
193
|
+
return new Map([
|
|
194
|
+
[
|
|
195
|
+
MOCK_TARGETS[0].name,
|
|
196
|
+
{ packageName: MOCK_TARGETS[0].name, status: 'done' },
|
|
197
|
+
],
|
|
198
|
+
[
|
|
199
|
+
MOCK_TARGETS[1].name,
|
|
200
|
+
{ packageName: MOCK_TARGETS[1].name, status: 'running' },
|
|
201
|
+
],
|
|
202
|
+
[
|
|
203
|
+
MOCK_TARGETS[2].name,
|
|
204
|
+
{ packageName: MOCK_TARGETS[2].name, status: 'pending' },
|
|
205
|
+
],
|
|
206
|
+
]);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function buildPhase(kind: PhaseKey): Phase {
|
|
210
|
+
switch (kind) {
|
|
211
|
+
case 'resolving':
|
|
212
|
+
return { kind: 'resolving', targets: MOCK_TARGETS };
|
|
213
|
+
case 'scope-select':
|
|
214
|
+
return {
|
|
215
|
+
kind: 'scope-select',
|
|
216
|
+
targets: MOCK_TARGETS,
|
|
217
|
+
pending: () => {
|
|
218
|
+
/* noop */
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
case 'planning':
|
|
222
|
+
return {
|
|
223
|
+
kind: 'planning',
|
|
224
|
+
targets: MOCK_TARGETS,
|
|
225
|
+
scope: 'user',
|
|
226
|
+
progress: makePlanningProgress(),
|
|
227
|
+
};
|
|
228
|
+
case 'diff-review':
|
|
229
|
+
return {
|
|
230
|
+
kind: 'diff-review',
|
|
231
|
+
plans: PLAN_SET,
|
|
232
|
+
focusedIndex: 0,
|
|
233
|
+
scope: 'user',
|
|
234
|
+
};
|
|
235
|
+
case 'force-confirm':
|
|
236
|
+
return {
|
|
237
|
+
kind: 'force-confirm',
|
|
238
|
+
plans: PLAN_SET,
|
|
239
|
+
warnings: MOCK_WARNINGS,
|
|
240
|
+
pending: () => {
|
|
241
|
+
/* noop */
|
|
242
|
+
},
|
|
243
|
+
scope: 'user',
|
|
244
|
+
};
|
|
245
|
+
case 'applying':
|
|
246
|
+
return {
|
|
247
|
+
kind: 'applying',
|
|
248
|
+
plans: PLAN_SET,
|
|
249
|
+
progress: MOCK_APPLY_PROGRESS,
|
|
250
|
+
scope: 'user',
|
|
251
|
+
};
|
|
252
|
+
case 'summary':
|
|
253
|
+
return {
|
|
254
|
+
kind: 'summary',
|
|
255
|
+
reports: MOCK_REPORTS,
|
|
256
|
+
plans: PLAN_SET,
|
|
257
|
+
exitCode: 0,
|
|
258
|
+
scope: 'user',
|
|
259
|
+
dryRun: false,
|
|
260
|
+
};
|
|
261
|
+
case 'summary-dry':
|
|
262
|
+
return {
|
|
263
|
+
kind: 'summary',
|
|
264
|
+
reports: MOCK_REPORTS,
|
|
265
|
+
plans: PLAN_SET,
|
|
266
|
+
exitCode: 0,
|
|
267
|
+
scope: 'user',
|
|
268
|
+
dryRun: true,
|
|
269
|
+
};
|
|
270
|
+
case 'error':
|
|
271
|
+
return {
|
|
272
|
+
kind: 'error',
|
|
273
|
+
error: new Error(
|
|
274
|
+
'Sample fatal: dist/claude-hashes.json missing at /workspace/packages/canard/schema-form',
|
|
275
|
+
),
|
|
276
|
+
};
|
|
277
|
+
default: {
|
|
278
|
+
const _exhaustive: never = kind;
|
|
279
|
+
void _exhaustive;
|
|
280
|
+
throw new Error('unknown phase');
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export function fixtureEvents(kind: PhaseKey): InjectEvent[] {
|
|
286
|
+
void kind;
|
|
287
|
+
return [];
|
|
288
|
+
}
|