@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
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { render, Box, Text, useApp, useInput } from 'ink';
|
|
3
|
+
import React, { useEffect, useState } from 'react';
|
|
4
|
+
|
|
5
|
+
import { Banner } from '../src/ui/components/Banner.js';
|
|
6
|
+
import { ConfirmForce } from '../src/ui/components/ConfirmForce.js';
|
|
7
|
+
import { ErrorPanel } from '../src/ui/components/ErrorPanel.js';
|
|
8
|
+
import { Footer } from '../src/ui/components/Footer.js';
|
|
9
|
+
import { ProgressBar } from '../src/ui/components/ProgressBar.js';
|
|
10
|
+
import { ScopePicker } from '../src/ui/components/ScopePicker.js';
|
|
11
|
+
import { Spinner } from '../src/ui/components/Spinner.js';
|
|
12
|
+
import { StepTracker } from '../src/ui/components/StepTracker.js';
|
|
13
|
+
import { Summary } from '../src/ui/components/Summary.js';
|
|
14
|
+
import { TargetCard } from '../src/ui/components/TargetCard.js';
|
|
15
|
+
import { colors } from '../src/ui/theme/colors.js';
|
|
16
|
+
import { icons } from '../src/ui/theme/icons.js';
|
|
17
|
+
import type { Phase } from '../src/ui/types/index.js';
|
|
18
|
+
|
|
19
|
+
import { buildPhase, PHASES, type PhaseKey } from './dev-ui-fixtures.js';
|
|
20
|
+
|
|
21
|
+
const VERSION = '0.3.0-dev';
|
|
22
|
+
|
|
23
|
+
interface CliArgs {
|
|
24
|
+
mode: 'usage' | 'phase' | 'tour';
|
|
25
|
+
phase?: PhaseKey;
|
|
26
|
+
tourMs?: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function parseArgs(argv: readonly string[]): CliArgs {
|
|
30
|
+
if (argv.includes('--tour')) {
|
|
31
|
+
const msArg = argv.find((a) => a.startsWith('--ms='));
|
|
32
|
+
const tourMs = msArg ? Number(msArg.slice(5)) : 2500;
|
|
33
|
+
return { mode: 'tour', tourMs };
|
|
34
|
+
}
|
|
35
|
+
const phaseArg = argv.find((a) => a.startsWith('--phase='));
|
|
36
|
+
if (phaseArg) {
|
|
37
|
+
const phaseKey = phaseArg.slice(8);
|
|
38
|
+
if (!PHASES.includes(phaseKey as PhaseKey)) {
|
|
39
|
+
console.error(
|
|
40
|
+
`Unknown phase: ${phaseKey}. Available: ${PHASES.join(', ')}`,
|
|
41
|
+
);
|
|
42
|
+
process.exit(2);
|
|
43
|
+
}
|
|
44
|
+
return { mode: 'phase', phase: phaseKey as PhaseKey };
|
|
45
|
+
}
|
|
46
|
+
return { mode: 'usage' };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function Usage(): React.ReactElement {
|
|
50
|
+
return (
|
|
51
|
+
<Box flexDirection="column">
|
|
52
|
+
<Text bold color={colors.primary}>
|
|
53
|
+
{icons.triangleRight} claude-assets-sync — dev UI runner
|
|
54
|
+
</Text>
|
|
55
|
+
<Box marginLeft={2} marginTop={1} flexDirection="column">
|
|
56
|
+
<Text color={colors.muted}>Preview individual Ink screens or tour them all.</Text>
|
|
57
|
+
<Box marginTop={1} flexDirection="column">
|
|
58
|
+
<Text>
|
|
59
|
+
<Text color={colors.success} bold>yarn dev:ui</Text>
|
|
60
|
+
<Text color={colors.muted}>{' --phase=<name> '}</Text>
|
|
61
|
+
<Text dimColor>single phase preview</Text>
|
|
62
|
+
</Text>
|
|
63
|
+
<Text>
|
|
64
|
+
<Text color={colors.success} bold>yarn dev:ui</Text>
|
|
65
|
+
<Text color={colors.muted}>{' --tour [--ms=2500] '}</Text>
|
|
66
|
+
<Text dimColor>cycle through all phases</Text>
|
|
67
|
+
</Text>
|
|
68
|
+
<Text>
|
|
69
|
+
<Text color={colors.success} bold>yarn dev:cli</Text>
|
|
70
|
+
<Text color={colors.muted}>{' <args> '}</Text>
|
|
71
|
+
<Text dimColor>run the actual CLI against real packages</Text>
|
|
72
|
+
</Text>
|
|
73
|
+
</Box>
|
|
74
|
+
<Box marginTop={1} flexDirection="column">
|
|
75
|
+
<Text bold>Available phases:</Text>
|
|
76
|
+
{PHASES.map((p) => (
|
|
77
|
+
<Text key={p} color={colors.muted}>
|
|
78
|
+
{' '}·{' '}
|
|
79
|
+
<Text color={colors.accent}>{p}</Text>
|
|
80
|
+
</Text>
|
|
81
|
+
))}
|
|
82
|
+
</Box>
|
|
83
|
+
<Box marginTop={1} flexDirection="column">
|
|
84
|
+
<Text bold>Examples:</Text>
|
|
85
|
+
<Text color={colors.muted}>
|
|
86
|
+
{' '}yarn dev:ui --phase=diff-review
|
|
87
|
+
</Text>
|
|
88
|
+
<Text color={colors.muted}>
|
|
89
|
+
{' '}yarn dev:ui --phase=applying
|
|
90
|
+
</Text>
|
|
91
|
+
<Text color={colors.muted}>
|
|
92
|
+
{' '}yarn dev:ui --tour --ms=1800
|
|
93
|
+
</Text>
|
|
94
|
+
<Text color={colors.muted}>
|
|
95
|
+
{' '}yarn dev:cli --package @canard/schema-form --scope=project --dry-run
|
|
96
|
+
</Text>
|
|
97
|
+
</Box>
|
|
98
|
+
</Box>
|
|
99
|
+
</Box>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
interface FixtureViewProps {
|
|
104
|
+
readonly phase: Phase;
|
|
105
|
+
readonly targetCount: number;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function FixtureView({ phase, targetCount }: FixtureViewProps): React.ReactElement {
|
|
109
|
+
return (
|
|
110
|
+
<Box flexDirection="column">
|
|
111
|
+
<Banner version={VERSION} scope="user" />
|
|
112
|
+
<Box marginTop={1}>
|
|
113
|
+
<StepTracker phase={phase} targetCount={targetCount} scope="user" />
|
|
114
|
+
</Box>
|
|
115
|
+
<Box flexDirection="column" marginTop={1}>
|
|
116
|
+
{renderPhaseBody(phase)}
|
|
117
|
+
</Box>
|
|
118
|
+
<Footer phase={phase} version={VERSION} />
|
|
119
|
+
</Box>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function renderPhaseBody(phase: Phase): React.ReactNode {
|
|
124
|
+
switch (phase.kind) {
|
|
125
|
+
case 'booting':
|
|
126
|
+
case 'resolving':
|
|
127
|
+
return <Spinner label="resolving targets…" />;
|
|
128
|
+
case 'scope-select':
|
|
129
|
+
return <ScopePicker onSelect={phase.pending} />;
|
|
130
|
+
case 'planning':
|
|
131
|
+
return (
|
|
132
|
+
<Box flexDirection="column">
|
|
133
|
+
<Spinner label="building plans…" />
|
|
134
|
+
<Box flexDirection="column" marginLeft={2} marginTop={1}>
|
|
135
|
+
{[...phase.progress.values()].map((step) => (
|
|
136
|
+
<Box key={step.packageName}>
|
|
137
|
+
<Text
|
|
138
|
+
color={
|
|
139
|
+
step.status === 'done'
|
|
140
|
+
? colors.success
|
|
141
|
+
: step.status === 'running'
|
|
142
|
+
? colors.warn
|
|
143
|
+
: step.status === 'failed'
|
|
144
|
+
? colors.danger
|
|
145
|
+
: colors.muted
|
|
146
|
+
}
|
|
147
|
+
bold={step.status === 'running'}
|
|
148
|
+
>
|
|
149
|
+
{step.status === 'done'
|
|
150
|
+
? icons.check
|
|
151
|
+
: step.status === 'failed'
|
|
152
|
+
? icons.cross
|
|
153
|
+
: step.status === 'running'
|
|
154
|
+
? icons.bulletActive
|
|
155
|
+
: icons.bulletPending}{' '}
|
|
156
|
+
</Text>
|
|
157
|
+
<Text>{step.packageName}</Text>
|
|
158
|
+
</Box>
|
|
159
|
+
))}
|
|
160
|
+
</Box>
|
|
161
|
+
</Box>
|
|
162
|
+
);
|
|
163
|
+
case 'diff-review':
|
|
164
|
+
return (
|
|
165
|
+
<Box flexDirection="column">
|
|
166
|
+
{phase.plans.map((tp, idx) => (
|
|
167
|
+
<TargetCard
|
|
168
|
+
key={tp.target.name}
|
|
169
|
+
target={tp.target}
|
|
170
|
+
plan={tp.plan}
|
|
171
|
+
expanded
|
|
172
|
+
highlighted={idx === phase.focusedIndex}
|
|
173
|
+
/>
|
|
174
|
+
))}
|
|
175
|
+
</Box>
|
|
176
|
+
);
|
|
177
|
+
case 'force-confirm':
|
|
178
|
+
return <ConfirmForce warnings={phase.warnings} onAnswer={phase.pending} />;
|
|
179
|
+
case 'applying': {
|
|
180
|
+
const elapsedMs = Math.max(Date.now() - phase.progress.startedAt, 1);
|
|
181
|
+
const rate = phase.progress.done / elapsedMs;
|
|
182
|
+
const remaining = phase.progress.total - phase.progress.done;
|
|
183
|
+
const eta = rate > 0 ? remaining / rate / 1000 : undefined;
|
|
184
|
+
return (
|
|
185
|
+
<Box flexDirection="column">
|
|
186
|
+
<ProgressBar
|
|
187
|
+
done={phase.progress.done}
|
|
188
|
+
total={phase.progress.total}
|
|
189
|
+
etaSeconds={eta}
|
|
190
|
+
label={phase.progress.current}
|
|
191
|
+
/>
|
|
192
|
+
<Box flexDirection="column" marginLeft={2} marginTop={1}>
|
|
193
|
+
{phase.plans.map((tp) => (
|
|
194
|
+
<TargetCard
|
|
195
|
+
key={tp.target.name}
|
|
196
|
+
target={tp.target}
|
|
197
|
+
plan={tp.plan}
|
|
198
|
+
expanded={false}
|
|
199
|
+
/>
|
|
200
|
+
))}
|
|
201
|
+
</Box>
|
|
202
|
+
</Box>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
case 'summary':
|
|
206
|
+
return (
|
|
207
|
+
<Box flexDirection="column">
|
|
208
|
+
{phase.plans.map((tp) => (
|
|
209
|
+
<TargetCard
|
|
210
|
+
key={tp.target.name}
|
|
211
|
+
target={tp.target}
|
|
212
|
+
plan={tp.plan}
|
|
213
|
+
expanded={false}
|
|
214
|
+
/>
|
|
215
|
+
))}
|
|
216
|
+
<Summary
|
|
217
|
+
reports={phase.reports}
|
|
218
|
+
exitCode={phase.exitCode}
|
|
219
|
+
dryRun={phase.dryRun}
|
|
220
|
+
/>
|
|
221
|
+
</Box>
|
|
222
|
+
);
|
|
223
|
+
case 'error':
|
|
224
|
+
return <ErrorPanel error={phase.error} />;
|
|
225
|
+
default: {
|
|
226
|
+
const _exhaustive: never = phase;
|
|
227
|
+
void _exhaustive;
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function PhaseApp({ phaseKey }: { phaseKey: PhaseKey }): React.ReactElement {
|
|
234
|
+
const { exit } = useApp();
|
|
235
|
+
const phase = buildPhase(phaseKey);
|
|
236
|
+
useInput((input) => {
|
|
237
|
+
if (input === 'q' || input === '') exit();
|
|
238
|
+
});
|
|
239
|
+
return <FixtureView phase={phase} targetCount={3} />;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function TourApp({ intervalMs }: { intervalMs: number }): React.ReactElement {
|
|
243
|
+
const { exit } = useApp();
|
|
244
|
+
const [idx, setIdx] = useState(0);
|
|
245
|
+
useEffect(() => {
|
|
246
|
+
const handle = setInterval(() => {
|
|
247
|
+
setIdx((current) => {
|
|
248
|
+
if (current + 1 >= PHASES.length) {
|
|
249
|
+
setTimeout(() => exit(), 300);
|
|
250
|
+
return current;
|
|
251
|
+
}
|
|
252
|
+
return current + 1;
|
|
253
|
+
});
|
|
254
|
+
}, intervalMs);
|
|
255
|
+
return () => clearInterval(handle);
|
|
256
|
+
}, [intervalMs, exit]);
|
|
257
|
+
useInput((input) => {
|
|
258
|
+
if (input === 'q' || input === '') exit();
|
|
259
|
+
if (input === ' ')
|
|
260
|
+
setIdx((current) =>
|
|
261
|
+
current + 1 >= PHASES.length ? current : current + 1,
|
|
262
|
+
);
|
|
263
|
+
});
|
|
264
|
+
const phaseKey = PHASES[idx];
|
|
265
|
+
const phase = buildPhase(phaseKey);
|
|
266
|
+
return (
|
|
267
|
+
<Box flexDirection="column">
|
|
268
|
+
<Box>
|
|
269
|
+
<Text color={colors.accent} bold>
|
|
270
|
+
[tour {idx + 1}/{PHASES.length}]
|
|
271
|
+
</Text>
|
|
272
|
+
<Text color={colors.muted} dimColor>
|
|
273
|
+
{' '}
|
|
274
|
+
{phaseKey} · space:next · q/esc:exit
|
|
275
|
+
</Text>
|
|
276
|
+
</Box>
|
|
277
|
+
<FixtureView phase={phase} targetCount={3} />
|
|
278
|
+
</Box>
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const args = parseArgs(process.argv.slice(2));
|
|
283
|
+
if (args.mode === 'usage') {
|
|
284
|
+
render(<Usage />);
|
|
285
|
+
} else if (args.mode === 'phase') {
|
|
286
|
+
render(<PhaseApp phaseKey={args.phase!} />);
|
|
287
|
+
} else if (args.mode === 'tour') {
|
|
288
|
+
render(<TourApp intervalMs={args.tourMs ?? 2500} />);
|
|
289
|
+
}
|
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,31 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var commander = require('commander');
|
|
4
|
-
var logger = require('../../utils/logger.cjs');
|
|
5
|
-
var version = require('../../utils/version.cjs');
|
|
6
|
-
var runInject = require('./utils/runInject.cjs');
|
|
7
|
-
|
|
8
|
-
async function runCli(argv = process.argv, options) {
|
|
9
|
-
const cmd = new commander.Command();
|
|
10
|
-
cmd
|
|
11
|
-
.name('claude-sync')
|
|
12
|
-
.description("Inject this package's assets into the target .claude directory")
|
|
13
|
-
.version(options.version ?? version.VERSION)
|
|
14
|
-
.option('--scope <scope>', 'Target scope: user (~/.claude) | project (nearest ancestor .claude or <cwd>/.claude)')
|
|
15
|
-
.option('--dry-run', 'Preview without writing', false)
|
|
16
|
-
.option('--force', 'Overwrite user modifications', false)
|
|
17
|
-
.option('--root <path>', 'Override scope resolution cwd (default: cwd)')
|
|
18
|
-
.action(async (flags) => {
|
|
19
|
-
await runInject.runInject(flags, options);
|
|
20
|
-
});
|
|
21
|
-
try {
|
|
22
|
-
await cmd.parseAsync([...argv]);
|
|
23
|
-
}
|
|
24
|
-
catch (err) {
|
|
25
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
26
|
-
logger.logger.error(msg);
|
|
27
|
-
process.exit(1);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
exports.runCli = runCli;
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
require('node:crypto');
|
|
4
|
-
require('node:fs/promises');
|
|
5
|
-
require('node:path');
|
|
6
|
-
var injectDocs = require('../../../core/injectDocs/injectDocs.cjs');
|
|
7
|
-
require('node:os');
|
|
8
|
-
require('node:fs');
|
|
9
|
-
require('@inquirer/prompts');
|
|
10
|
-
require('picocolors');
|
|
11
|
-
var confirmForce = require('../../../prompts/confirmForce.cjs');
|
|
12
|
-
var heartbeat = require('../../../utils/heartbeat.cjs');
|
|
13
|
-
var logger = require('../../../utils/logger.cjs');
|
|
14
|
-
|
|
15
|
-
async function injectOne(target, scope, flags, originCwd) {
|
|
16
|
-
if (!target.hashesPresent) {
|
|
17
|
-
logger.logger.warn(`${target.name}: dist/claude-hashes.json missing — build the package (e.g. yarn build) to regenerate the hash manifest first.`);
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
logger.logger.heading(`${target.name}@${target.version}`);
|
|
21
|
-
const stopHeartbeat = heartbeat.startHeartbeat({
|
|
22
|
-
label: `injecting ${target.name}`,
|
|
23
|
-
});
|
|
24
|
-
try {
|
|
25
|
-
const report = await injectDocs.injectDocs({
|
|
26
|
-
packageName: target.name,
|
|
27
|
-
packageVersion: target.version,
|
|
28
|
-
packageRoot: target.packageRoot,
|
|
29
|
-
assetRoot: target.assetRoot,
|
|
30
|
-
scope,
|
|
31
|
-
originCwd,
|
|
32
|
-
dryRun: flags.dryRun ?? false,
|
|
33
|
-
force: flags.force ?? false,
|
|
34
|
-
confirmForce: async (plan) => {
|
|
35
|
-
const diverged = plan.actions.filter((a) => a.kind === 'warn-diverged');
|
|
36
|
-
const orphans = plan.actions.filter((a) => a.kind === 'warn-orphan');
|
|
37
|
-
return confirmForce.confirmForceAsync(diverged.length, orphans.length, [...diverged, ...orphans].map((a) => a.relPath).slice(0, 3));
|
|
38
|
-
},
|
|
39
|
-
});
|
|
40
|
-
if (report.exitCode !== 0)
|
|
41
|
-
process.exit(report.exitCode);
|
|
42
|
-
}
|
|
43
|
-
finally {
|
|
44
|
-
stopHeartbeat();
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
exports.injectOne = injectOne;
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import 'node:crypto';
|
|
2
|
-
import 'node:fs/promises';
|
|
3
|
-
import 'node:path';
|
|
4
|
-
import { injectDocs } from '../../../core/injectDocs/injectDocs.mjs';
|
|
5
|
-
import 'node:os';
|
|
6
|
-
import 'node:fs';
|
|
7
|
-
import '@inquirer/prompts';
|
|
8
|
-
import 'picocolors';
|
|
9
|
-
import { confirmForceAsync } from '../../../prompts/confirmForce.mjs';
|
|
10
|
-
import { startHeartbeat } from '../../../utils/heartbeat.mjs';
|
|
11
|
-
import { logger } from '../../../utils/logger.mjs';
|
|
12
|
-
|
|
13
|
-
async function injectOne(target, scope, flags, originCwd) {
|
|
14
|
-
if (!target.hashesPresent) {
|
|
15
|
-
logger.warn(`${target.name}: dist/claude-hashes.json missing — build the package (e.g. yarn build) to regenerate the hash manifest first.`);
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
logger.heading(`${target.name}@${target.version}`);
|
|
19
|
-
const stopHeartbeat = startHeartbeat({
|
|
20
|
-
label: `injecting ${target.name}`,
|
|
21
|
-
});
|
|
22
|
-
try {
|
|
23
|
-
const report = await injectDocs({
|
|
24
|
-
packageName: target.name,
|
|
25
|
-
packageVersion: target.version,
|
|
26
|
-
packageRoot: target.packageRoot,
|
|
27
|
-
assetRoot: target.assetRoot,
|
|
28
|
-
scope,
|
|
29
|
-
originCwd,
|
|
30
|
-
dryRun: flags.dryRun ?? false,
|
|
31
|
-
force: flags.force ?? false,
|
|
32
|
-
confirmForce: async (plan) => {
|
|
33
|
-
const diverged = plan.actions.filter((a) => a.kind === 'warn-diverged');
|
|
34
|
-
const orphans = plan.actions.filter((a) => a.kind === 'warn-orphan');
|
|
35
|
-
return confirmForceAsync(diverged.length, orphans.length, [...diverged, ...orphans].map((a) => a.relPath).slice(0, 3));
|
|
36
|
-
},
|
|
37
|
-
});
|
|
38
|
-
if (report.exitCode !== 0)
|
|
39
|
-
process.exit(report.exitCode);
|
|
40
|
-
}
|
|
41
|
-
finally {
|
|
42
|
-
stopHeartbeat();
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export { injectOne };
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
require('node:crypto');
|
|
4
|
-
require('node:fs/promises');
|
|
5
|
-
require('node:path');
|
|
6
|
-
var logger = require('../../../utils/logger.cjs');
|
|
7
|
-
var scope = require('../../../core/scope/scope.cjs');
|
|
8
|
-
var selectScope = require('../../../prompts/selectScope.cjs');
|
|
9
|
-
require('@inquirer/prompts');
|
|
10
|
-
require('picocolors');
|
|
11
|
-
|
|
12
|
-
async function resolveScopeFlag(flag) {
|
|
13
|
-
if (flag) {
|
|
14
|
-
if (!scope.isValidScope(flag)) {
|
|
15
|
-
logger.logger.error(`Invalid --scope: ${flag}. Expected user | project.`);
|
|
16
|
-
process.exit(2);
|
|
17
|
-
}
|
|
18
|
-
return flag;
|
|
19
|
-
}
|
|
20
|
-
if (!scope.isInteractive()) {
|
|
21
|
-
logger.logger.error('--scope is required in non-interactive environments.');
|
|
22
|
-
logger.logger.error(' Pass --scope=user | --scope=project.');
|
|
23
|
-
process.exit(2);
|
|
24
|
-
}
|
|
25
|
-
return selectScope.selectScopeAsync();
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
exports.resolveScopeFlag = resolveScopeFlag;
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var promises = require('node:fs/promises');
|
|
4
|
-
var node_path = require('node:path');
|
|
5
|
-
var logger = require('../../../utils/logger.cjs');
|
|
6
|
-
var injectOne = require('./injectOne.cjs');
|
|
7
|
-
var resolveScopeFlag = require('./resolveScopeFlag.cjs');
|
|
8
|
-
|
|
9
|
-
async function runInject(flags, options) {
|
|
10
|
-
if (!options.packageRoot ||
|
|
11
|
-
!options.packageName ||
|
|
12
|
-
!options.packageVersion ||
|
|
13
|
-
!options.assetPath) {
|
|
14
|
-
logger.logger.error('runCli requires { packageRoot, packageName, packageVersion, assetPath }.');
|
|
15
|
-
process.exit(2);
|
|
16
|
-
}
|
|
17
|
-
if (!node_path.isAbsolute(options.packageRoot)) {
|
|
18
|
-
logger.logger.error(`packageRoot must be an absolute path; received: ${options.packageRoot}`);
|
|
19
|
-
process.exit(2);
|
|
20
|
-
}
|
|
21
|
-
const assetRoot = node_path.resolve(options.packageRoot, options.assetPath);
|
|
22
|
-
const hashesPath = node_path.join(options.packageRoot, 'dist', 'claude-hashes.json');
|
|
23
|
-
const hashesPresent = await promises.stat(hashesPath).then(() => true, () => false);
|
|
24
|
-
const target = {
|
|
25
|
-
name: options.packageName,
|
|
26
|
-
version: options.packageVersion,
|
|
27
|
-
packageRoot: options.packageRoot,
|
|
28
|
-
assetRoot,
|
|
29
|
-
hashesPresent,
|
|
30
|
-
};
|
|
31
|
-
const originCwd = flags.root ?? process.cwd();
|
|
32
|
-
const scope = await resolveScopeFlag.resolveScopeFlag(flags.scope);
|
|
33
|
-
await injectOne.injectOne(target, scope, flags, originCwd);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
exports.runInject = runInject;
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { stat } from 'node:fs/promises';
|
|
2
|
-
import { isAbsolute, resolve, join } from 'node:path';
|
|
3
|
-
import { logger } from '../../../utils/logger.mjs';
|
|
4
|
-
import { injectOne } from './injectOne.mjs';
|
|
5
|
-
import { resolveScopeFlag } from './resolveScopeFlag.mjs';
|
|
6
|
-
|
|
7
|
-
async function runInject(flags, options) {
|
|
8
|
-
if (!options.packageRoot ||
|
|
9
|
-
!options.packageName ||
|
|
10
|
-
!options.packageVersion ||
|
|
11
|
-
!options.assetPath) {
|
|
12
|
-
logger.error('runCli requires { packageRoot, packageName, packageVersion, assetPath }.');
|
|
13
|
-
process.exit(2);
|
|
14
|
-
}
|
|
15
|
-
if (!isAbsolute(options.packageRoot)) {
|
|
16
|
-
logger.error(`packageRoot must be an absolute path; received: ${options.packageRoot}`);
|
|
17
|
-
process.exit(2);
|
|
18
|
-
}
|
|
19
|
-
const assetRoot = resolve(options.packageRoot, options.assetPath);
|
|
20
|
-
const hashesPath = join(options.packageRoot, 'dist', 'claude-hashes.json');
|
|
21
|
-
const hashesPresent = await stat(hashesPath).then(() => true, () => false);
|
|
22
|
-
const target = {
|
|
23
|
-
name: options.packageName,
|
|
24
|
-
version: options.packageVersion,
|
|
25
|
-
packageRoot: options.packageRoot,
|
|
26
|
-
assetRoot,
|
|
27
|
-
hashesPresent,
|
|
28
|
-
};
|
|
29
|
-
const originCwd = flags.root ?? process.cwd();
|
|
30
|
-
const scope = await resolveScopeFlag(flags.scope);
|
|
31
|
-
await injectOne(target, scope, flags, originCwd);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export { runInject };
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var node_path = require('node:path');
|
|
4
|
-
var hash = require('../hash/hash.cjs');
|
|
5
|
-
var toPosix = require('./utils/toPosix.cjs');
|
|
6
|
-
var walkFiles = require('./utils/walkFiles.cjs');
|
|
7
|
-
|
|
8
|
-
async function buildPlan(input) {
|
|
9
|
-
const { sourceHashes, targetRoot, namespacePrefixes, force } = input;
|
|
10
|
-
const actions = [];
|
|
11
|
-
let requiresForce = false;
|
|
12
|
-
for (const [relPath, srcHash] of Object.entries(sourceHashes)) {
|
|
13
|
-
const dstAbs = node_path.join(targetRoot, relPath);
|
|
14
|
-
const dstHash = await hash.hashFile(dstAbs);
|
|
15
|
-
if (dstHash === null)
|
|
16
|
-
actions.push({ kind: 'copy', relPath, dstAbs });
|
|
17
|
-
else if (hash.hashEquals(dstHash, srcHash))
|
|
18
|
-
actions.push({ kind: 'skip-uptodate', relPath, dstAbs });
|
|
19
|
-
else {
|
|
20
|
-
actions.push({ kind: 'warn-diverged', relPath, dstAbs });
|
|
21
|
-
requiresForce = true;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
const known = new Set(Object.keys(sourceHashes));
|
|
25
|
-
for (const prefix of namespacePrefixes) {
|
|
26
|
-
const prefixRoot = node_path.join(targetRoot, prefix);
|
|
27
|
-
for await (const abs of walkFiles.walkFiles(prefixRoot)) {
|
|
28
|
-
const relPath = toPosix.toPosix(node_path.relative(targetRoot, abs));
|
|
29
|
-
if (known.has(relPath))
|
|
30
|
-
continue;
|
|
31
|
-
if (force)
|
|
32
|
-
actions.push({ kind: 'delete', relPath, dstAbs: abs });
|
|
33
|
-
else {
|
|
34
|
-
actions.push({ kind: 'warn-orphan', relPath, dstAbs: abs });
|
|
35
|
-
requiresForce = true;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return { actions, requiresForce };
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
exports.buildPlan = buildPlan;
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var promises = require('node:fs/promises');
|
|
4
|
-
var node_path = require('node:path');
|
|
5
|
-
|
|
6
|
-
async function* walkFiles(root) {
|
|
7
|
-
let entries;
|
|
8
|
-
try {
|
|
9
|
-
entries = await promises.readdir(root, { withFileTypes: true });
|
|
10
|
-
}
|
|
11
|
-
catch (err) {
|
|
12
|
-
if (err.code === 'ENOENT')
|
|
13
|
-
return;
|
|
14
|
-
throw err;
|
|
15
|
-
}
|
|
16
|
-
for (const entry of entries) {
|
|
17
|
-
const abs = node_path.join(root, entry.name);
|
|
18
|
-
if (entry.isDirectory())
|
|
19
|
-
yield* walkFiles(abs);
|
|
20
|
-
else if (entry.isFile())
|
|
21
|
-
yield abs;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
exports.walkFiles = walkFiles;
|
package/dist/core/hash/hash.cjs
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var node_crypto = require('node:crypto');
|
|
4
|
-
var promises = require('node:fs/promises');
|
|
5
|
-
|
|
6
|
-
function hashContent(buffer) {
|
|
7
|
-
return node_crypto.createHash('sha256').update(buffer).digest('hex');
|
|
8
|
-
}
|
|
9
|
-
async function hashFile(absPath) {
|
|
10
|
-
try {
|
|
11
|
-
const buf = await promises.readFile(absPath);
|
|
12
|
-
return hashContent(buf);
|
|
13
|
-
}
|
|
14
|
-
catch (err) {
|
|
15
|
-
if (err.code === 'ENOENT')
|
|
16
|
-
return null;
|
|
17
|
-
throw err;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
function hashEquals(a, b) {
|
|
21
|
-
if (a === null || b === null)
|
|
22
|
-
return false;
|
|
23
|
-
if (a.length !== b.length)
|
|
24
|
-
return false;
|
|
25
|
-
return a.toLowerCase() === b.toLowerCase();
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
exports.hashContent = hashContent;
|
|
29
|
-
exports.hashEquals = hashEquals;
|
|
30
|
-
exports.hashFile = hashFile;
|