@slats/claude-assets-sync 0.3.0 → 0.3.2
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 +15 -11
- package/dist/claude-hashes.json +7 -7
- package/dist/commands/runCli/runCli.d.ts +4 -2
- package/dist/commands/runCli/runCli.mjs +7 -2
- package/dist/commands/runCli/type.d.ts +1 -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/resolveScopeAlias.mjs +30 -33
- package/dist/commands/runCli/utils/resolveScopeFlag.d.ts +9 -1
- package/dist/commands/runCli/utils/resolveScopeFlag.mjs +5 -11
- 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/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 +1 -1
- 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 +133 -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 +95 -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 +1 -1
- package/docs/claude/skills/claude-docs-asset-wiring/knowledge/claude-md-template.md +4 -12
- package/docs/claude/skills/claude-docs-asset-wiring/knowledge/gotchas.md +17 -14
- package/docs/claude/skills/claude-docs-asset-wiring/knowledge/package-json-patches.md +18 -13
- package/docs/claude/skills/claude-docs-asset-wiring/knowledge/reference-files.md +4 -4
- package/docs/consumer-integration.md +9 -8
- package/package.json +12 -7
- package/scripts/dev-ui-fixtures.ts +288 -0
- package/scripts/dev-ui.tsx +288 -0
- package/dist/commands/runCli/runCli.cjs +0 -53
- package/dist/commands/runCli/utils/classifyTarget.cjs +0 -48
- package/dist/commands/runCli/utils/injectOne.cjs +0 -47
- package/dist/commands/runCli/utils/injectOne.d.ts +0 -3
- package/dist/commands/runCli/utils/injectOne.mjs +0 -45
- package/dist/commands/runCli/utils/resolvePackage.cjs +0 -77
- package/dist/commands/runCli/utils/resolveScopeAlias.cjs +0 -69
- package/dist/commands/runCli/utils/resolveScopeFlag.cjs +0 -28
- package/dist/commands/runCli/utils/resolveTargets.cjs +0 -40
- package/dist/commands/runCli/utils/runInject.cjs +0 -52
- package/dist/commands/runCli/utils/runInject.d.ts +0 -3
- package/dist/commands/runCli/utils/runInject.mjs +0 -50
- 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/README.md
CHANGED
|
@@ -4,9 +4,9 @@ Engine + dispatcher CLI that lets any npm package ship its own Claude Code docs
|
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
A consumer package declares `claude.assetPath` in `package.json` and runs `claude-build-hashes` during build to emit `dist/claude-hashes.json`. End users run `npx -p @slats/claude-assets-sync inject-claude-settings --package=<name>` and this engine resolves
|
|
7
|
+
A consumer package declares `claude.assetPath` in `package.json` and runs `claude-build-hashes` during build to emit `dist/claude-hashes.json`. End users run `npx -p @slats/claude-assets-sync inject-claude-settings --package=<name>` and this engine resolves each consumer's metadata, compares its hash manifest against the target `.claude/`, and copies only what is out of date.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
`--package` accepts a scoped name (`@scope/pkg`), an unscoped name (`pkg`), or a **scope alias** (`@scope` with no slash) that fans out to every installed `node_modules/@scope/*` package declaring `claude.assetPath`. Single-target resolution uses `createRequire`; scope-alias enumeration walks ancestor `node_modules/@<scope>/` directories from `cwd` upward and is isolated to `runCli/utils/resolveScopeAlias.ts`.
|
|
10
10
|
|
|
11
11
|
No GitHub fetch, no `.sync-meta.json`, no migrations — the consumer's `dist/claude-hashes.json` is the single source of truth.
|
|
12
12
|
|
|
@@ -27,17 +27,19 @@ claude-build-hashes
|
|
|
27
27
|
|
|
28
28
|
### End-user invocation
|
|
29
29
|
|
|
30
|
+
The engine is not shipped as a runtime dependency of consumers. Always invoke via `npx -p @slats/claude-assets-sync ...`; the package manager fetches and caches the engine on demand.
|
|
31
|
+
|
|
30
32
|
```bash
|
|
31
|
-
#
|
|
33
|
+
# Single consumer:
|
|
32
34
|
npx -p @slats/claude-assets-sync inject-claude-settings --package=@canard/schema-form --scope=user
|
|
33
35
|
|
|
34
|
-
#
|
|
35
|
-
npx inject-claude-settings --package=@
|
|
36
|
+
# Scope alias — every installed @winglet/* that declares claude.assetPath:
|
|
37
|
+
npx -p @slats/claude-assets-sync inject-claude-settings --package=@winglet --scope=user
|
|
36
38
|
```
|
|
37
39
|
|
|
38
40
|
| Flag | Meaning |
|
|
39
41
|
|---|---|
|
|
40
|
-
| `--package <name>` | **Required.**
|
|
42
|
+
| `--package <name>` | **Required.** Repeatable/comma-separable. Accepts `@scope/pkg`, `pkg`, or a scope alias `@scope` (fans out to every installed `node_modules/@scope/*` with `claude.assetPath`). |
|
|
41
43
|
| `--scope=user` | `~/.claude` (applies globally). |
|
|
42
44
|
| `--scope=project` | Nearest ancestor `.claude` directory, or `<cwd>/.claude` if none found. |
|
|
43
45
|
| `--dry-run` | Print the copy / skip / warn plan, no writes. |
|
|
@@ -59,7 +61,7 @@ For `--scope=project` the target `.claude` directory is resolved by walking up f
|
|
|
59
61
|
"build": "… && yarn build:hashes",
|
|
60
62
|
"build:hashes": "claude-build-hashes"
|
|
61
63
|
},
|
|
62
|
-
"
|
|
64
|
+
"devDependencies": {
|
|
63
65
|
"@slats/claude-assets-sync": "workspace:^"
|
|
64
66
|
},
|
|
65
67
|
"files": ["dist", "docs", "README.md"],
|
|
@@ -67,7 +69,7 @@ For `--scope=project` the target `.claude` directory is resolved by walking up f
|
|
|
67
69
|
}
|
|
68
70
|
```
|
|
69
71
|
|
|
70
|
-
- `@slats/claude-assets-sync` MUST be in `
|
|
72
|
+
- `@slats/claude-assets-sync` MUST be in `devDependencies` — the engine is a CLI-only tool and must not leak into end-user production installs. See Rationale below.
|
|
71
73
|
- Do **not** add any `bin` field. The engine is the sole CLI surface; per-consumer bins would collide under `node_modules/.bin/`.
|
|
72
74
|
- Do **not** expose `./bin/*` or `./docs/*` in `exports`. That would let consumer bundlers pull CLI code or the asset tree into app bundles.
|
|
73
75
|
- Do **not** create a `bin/` or `scripts/` directory in the consumer.
|
|
@@ -82,10 +84,12 @@ yarn build
|
|
|
82
84
|
|
|
83
85
|
Ship the resulting `dist/` (including `claude-hashes.json`) alongside `docs/` when you publish.
|
|
84
86
|
|
|
85
|
-
### Rationale: `
|
|
87
|
+
### Rationale: `devDependencies`, not `dependencies`
|
|
86
88
|
|
|
87
|
-
- The
|
|
88
|
-
-
|
|
89
|
+
- The engine is used at two moments only: (1) the consumer's own build, where `claude-build-hashes` produces `dist/claude-hashes.json`, and (2) the end user's one-off `inject-claude-settings` invocation. Neither is runtime behaviour of the consumer library.
|
|
90
|
+
- Putting the engine in `dependencies` would force every downstream installer of the consumer to pull `commander`, `@inquirer/prompts`, and their transitive trees into their production `node_modules` — dead weight for anyone who never sets up Claude Code assets.
|
|
91
|
+
- The workspace build chain still resolves `.bin/claude-build-hashes` from `devDependencies` at `yarn install` time; yarn workspaces link devDeps and deps identically for workspace-local builds.
|
|
92
|
+
- End users never rely on a hoisted `inject-claude-settings` bin. The canonical invocation is `npx -p @slats/claude-assets-sync inject-claude-settings --package=<THIS>`, which fetches the engine on demand and caches it.
|
|
89
93
|
- Bundle isolation is enforced by the import graph (`src/**` in the consumer never references the engine), not by dependency-type.
|
|
90
94
|
|
|
91
95
|
## Authoring `docs/claude/`
|
package/dist/claude-hashes.json
CHANGED
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"package": {
|
|
4
4
|
"name": "@slats/claude-assets-sync",
|
|
5
|
-
"version": "0.3.
|
|
5
|
+
"version": "0.3.2"
|
|
6
6
|
},
|
|
7
|
-
"generatedAt": "2026-04-
|
|
7
|
+
"generatedAt": "2026-04-24T17:08:31.012Z",
|
|
8
8
|
"algorithm": "sha256",
|
|
9
9
|
"assetRoot": "docs/claude",
|
|
10
10
|
"files": {
|
|
11
|
-
"skills/claude-docs-asset-wiring/SKILL.md": "
|
|
12
|
-
"skills/claude-docs-asset-wiring/knowledge/claude-md-template.md": "
|
|
11
|
+
"skills/claude-docs-asset-wiring/SKILL.md": "4864a5a1aba0813fb64ad19111f3db4369df6cc025e485d03d17cdc9d245bce5",
|
|
12
|
+
"skills/claude-docs-asset-wiring/knowledge/claude-md-template.md": "b0274c5a37076a7e80bd7beccd739b10cc91ca5395fe425be25d9129206c684a",
|
|
13
13
|
"skills/claude-docs-asset-wiring/knowledge/dependency-cruiser.md": "18e994bd16242f72a4dc6cfa508493f4b63853a7a16c4910c7b7abd0368bd103",
|
|
14
|
-
"skills/claude-docs-asset-wiring/knowledge/gotchas.md": "
|
|
15
|
-
"skills/claude-docs-asset-wiring/knowledge/package-json-patches.md": "
|
|
16
|
-
"skills/claude-docs-asset-wiring/knowledge/reference-files.md": "
|
|
14
|
+
"skills/claude-docs-asset-wiring/knowledge/gotchas.md": "70a6c75ea09816ec457615c060e6a87197bc5ed332a377452b3cacd974b44eee",
|
|
15
|
+
"skills/claude-docs-asset-wiring/knowledge/package-json-patches.md": "063469932191843a2f7f9156a513717947bcad584c4084a0c8300897683885e7",
|
|
16
|
+
"skills/claude-docs-asset-wiring/knowledge/reference-files.md": "1b5f76d262f47835c4d0150a48fa25dfc644609fd4ecad260273a4cab57e41a2",
|
|
17
17
|
"skills/claude-docs-asset-wiring/knowledge/smoke-tests.md": "c90fc82d46bf4b55d794c2ce463b4c1e199d13d6b1161028486fb4bbbd6a60fe"
|
|
18
18
|
},
|
|
19
19
|
"previousVersions": {}
|
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The `inject-claude-settings` dispatcher parses `--package <name...>`
|
|
5
5
|
* from argv and classifies each value:
|
|
6
|
-
* - `@<scope>` — enumerate every
|
|
6
|
+
* - `@<scope>` — enumerate every installed `node_modules/@<scope>/*`
|
|
7
|
+
* package that declares `claude.assetPath`
|
|
7
8
|
* - `@<scope>/<name>` — one scoped package
|
|
8
9
|
* - `<name>` — one unscoped package
|
|
9
10
|
*
|
|
10
11
|
* Targets are resolved via Node module resolution (`resolvePackage`)
|
|
11
12
|
* except for scope aliases, which are the only path allowed to walk
|
|
12
|
-
*
|
|
13
|
+
* `node_modules` siblings — that exception is isolated to
|
|
14
|
+
* `resolveScopeAlias.ts`.
|
|
13
15
|
*/
|
|
14
16
|
export declare function runCli(argv?: readonly string[]): Promise<void>;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { logger } from '../../utils/logger.mjs';
|
|
3
3
|
import { VERSION } from '../../utils/version.mjs';
|
|
4
|
+
import { renderOrFallback } from './utils/renderOrFallback.mjs';
|
|
4
5
|
import { resolveTargets } from './utils/resolveTargets.mjs';
|
|
5
|
-
import {
|
|
6
|
+
import { toConsumerPackages } from './utils/toConsumerPackages.mjs';
|
|
6
7
|
|
|
7
8
|
async function runCli(argv = process.argv) {
|
|
8
9
|
const cmd = new Command();
|
|
@@ -15,6 +16,7 @@ async function runCli(argv = process.argv) {
|
|
|
15
16
|
.option('--dry-run', 'Preview without writing', false)
|
|
16
17
|
.option('--force', 'Overwrite user modifications', false)
|
|
17
18
|
.option('--root <path>', 'Override scope resolution cwd (default: cwd)')
|
|
19
|
+
.option('--json', 'Emit structured JSON output (forces non-interactive legacy logger path)', false)
|
|
18
20
|
.action(async (flags) => {
|
|
19
21
|
const targets = flags.package ?? [];
|
|
20
22
|
if (targets.length === 0) {
|
|
@@ -27,7 +29,10 @@ async function runCli(argv = process.argv) {
|
|
|
27
29
|
logger.warn(`no packages resolved from --package target(s): ${targets.join(', ')}`);
|
|
28
30
|
return;
|
|
29
31
|
}
|
|
30
|
-
await
|
|
32
|
+
const consumerPackages = await toConsumerPackages(metadataList);
|
|
33
|
+
const exitCode = await renderOrFallback(consumerPackages, flags, originCwd);
|
|
34
|
+
if (exitCode !== 0)
|
|
35
|
+
process.exit(exitCode);
|
|
31
36
|
});
|
|
32
37
|
try {
|
|
33
38
|
await cmd.parseAsync([...argv]);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ConsumerPackage, DefaultFlags } from '../type.js';
|
|
2
|
+
interface RenderEnv {
|
|
3
|
+
readonly isTTY?: boolean;
|
|
4
|
+
}
|
|
5
|
+
export declare function renderOrFallback(targets: readonly ConsumerPackage[], flags: DefaultFlags, originCwd: string, env?: RenderEnv): Promise<number>;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { renderPlain } from './renderPlain.mjs';
|
|
2
|
+
|
|
3
|
+
async function renderOrFallback(targets, flags, originCwd, env = {}) {
|
|
4
|
+
const isTTY = env.isTTY ?? Boolean(process.stdout.isTTY);
|
|
5
|
+
if (flags.json || !isTTY) {
|
|
6
|
+
return renderPlain(targets, flags, originCwd);
|
|
7
|
+
}
|
|
8
|
+
const ui = (await import('../../../ui/index.mjs'));
|
|
9
|
+
return ui.renderInjectApp({ targets, flags, originCwd });
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export { renderOrFallback };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ConsumerPackage, DefaultFlags } from '../type.js';
|
|
2
|
+
/**
|
|
3
|
+
* Plain (picocolors) renderer for non-TTY / `--json` invocations.
|
|
4
|
+
*
|
|
5
|
+
* Composes the same `core/**` primitives that the Ink `useInjectSession`
|
|
6
|
+
* pipeline uses — no legacy `injectDocs` orchestrator in between.
|
|
7
|
+
* Missing `--scope` on non-TTY exits via `resolveScopeFlag` with code 2.
|
|
8
|
+
* Divergent/orphan files still respect `--force`: absence of `--force`
|
|
9
|
+
* returns 2; presence logs the list to stderr and proceeds.
|
|
10
|
+
*/
|
|
11
|
+
export declare function renderPlain(targets: readonly ConsumerPackage[], flags: DefaultFlags, originCwd: string): Promise<number>;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import 'node:crypto';
|
|
2
|
+
import 'node:fs/promises';
|
|
3
|
+
import { readHashManifest, computeNamespacePrefixes } from '../../../core/hashManifest/hashManifest.mjs';
|
|
4
|
+
import { applyAction } from '../../../core/injectDocs/utils/applyAction.mjs';
|
|
5
|
+
import { summarize } from '../../../core/injectDocs/utils/summarize.mjs';
|
|
6
|
+
import { buildPlan } from '../../../core/buildPlan/buildPlan.mjs';
|
|
7
|
+
import { resolveScope } from '../../../core/scope/scope.mjs';
|
|
8
|
+
import { asyncPool } from '../../../utils/asyncPool.mjs';
|
|
9
|
+
import { logger } from '../../../utils/logger.mjs';
|
|
10
|
+
import { resolveScopeFlag } from './resolveScopeFlag.mjs';
|
|
11
|
+
|
|
12
|
+
async function renderPlain(targets, flags, originCwd) {
|
|
13
|
+
if (targets.length === 0)
|
|
14
|
+
return 0;
|
|
15
|
+
const scope = resolveScopeFlag(flags.scope);
|
|
16
|
+
const fatalOnError = targets.length === 1;
|
|
17
|
+
let failureCount = 0;
|
|
18
|
+
for (const target of targets) {
|
|
19
|
+
if (!target.hashesPresent) {
|
|
20
|
+
logger.warn(`${target.name}: dist/claude-hashes.json missing — build the package (e.g. yarn build) to regenerate the hash manifest first.`);
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
logger.heading(`${target.name}@${target.version}`);
|
|
24
|
+
let exitCode;
|
|
25
|
+
try {
|
|
26
|
+
exitCode = await renderOneTarget(target, scope, flags, originCwd);
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
30
|
+
logger.error(`${target.name}: ${msg}`);
|
|
31
|
+
exitCode = 1;
|
|
32
|
+
}
|
|
33
|
+
if (exitCode !== 0) {
|
|
34
|
+
if (fatalOnError)
|
|
35
|
+
return exitCode;
|
|
36
|
+
failureCount += 1;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return failureCount > 0 ? 1 : 0;
|
|
40
|
+
}
|
|
41
|
+
async function renderOneTarget(target, scope, flags, originCwd) {
|
|
42
|
+
const manifest = await readHashManifest(target.packageRoot);
|
|
43
|
+
const resolution = resolveScope(scope, originCwd);
|
|
44
|
+
const plan = await buildPlan({
|
|
45
|
+
sourceHashes: manifest.files,
|
|
46
|
+
targetRoot: resolution.targetRoot,
|
|
47
|
+
namespacePrefixes: computeNamespacePrefixes(manifest),
|
|
48
|
+
force: flags.force ?? false,
|
|
49
|
+
});
|
|
50
|
+
logger.info(`${target.name}@${target.version} → ${resolution.description}`);
|
|
51
|
+
printPlan(plan);
|
|
52
|
+
if (plan.requiresForce && !flags.force) {
|
|
53
|
+
logger.error('Re-run with --force to proceed, or inspect with --dry-run.');
|
|
54
|
+
return 2;
|
|
55
|
+
}
|
|
56
|
+
if (flags.force &&
|
|
57
|
+
plan.actions.some((a) => a.kind === 'warn-diverged' || a.kind === 'warn-orphan')) {
|
|
58
|
+
emitForceList(plan);
|
|
59
|
+
}
|
|
60
|
+
if (flags.dryRun) {
|
|
61
|
+
logger.warn('[DRY RUN] No files will be created, overwritten, or deleted.');
|
|
62
|
+
return 0;
|
|
63
|
+
}
|
|
64
|
+
await asyncPool(8, plan.actions, (action) => applyAction(action, target.assetRoot));
|
|
65
|
+
const report = summarize(plan, 0);
|
|
66
|
+
return report.exitCode;
|
|
67
|
+
}
|
|
68
|
+
function printPlan(plan) {
|
|
69
|
+
for (const action of plan.actions) {
|
|
70
|
+
if (action.kind === 'copy')
|
|
71
|
+
logger.file('create', action.relPath);
|
|
72
|
+
else if (action.kind === 'skip-uptodate')
|
|
73
|
+
logger.file('skip', `${action.relPath} (up-to-date)`);
|
|
74
|
+
else if (action.kind === 'warn-diverged')
|
|
75
|
+
logger.warn(`${action.relPath} — local differs from source (user edit or version change)`);
|
|
76
|
+
else if (action.kind === 'warn-orphan')
|
|
77
|
+
logger.warn(`${action.relPath} — present locally, absent in source`);
|
|
78
|
+
else if (action.kind === 'delete')
|
|
79
|
+
logger.file('update', `${action.relPath} (deleting)`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function emitForceList(plan) {
|
|
83
|
+
const divergent = plan.actions.filter((action) => action.kind === 'warn-diverged' || action.kind === 'warn-orphan');
|
|
84
|
+
process.stderr.write(`[claude-assets-sync] --force overwriting ${divergent.length} file(s) in non-TTY mode:\n`);
|
|
85
|
+
for (const action of divergent)
|
|
86
|
+
process.stderr.write(` ${action.relPath}\n`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export { renderPlain };
|
|
@@ -1,27 +1,45 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { readdir, readFile } from 'node:fs/promises';
|
|
3
|
-
import {
|
|
3
|
+
import { resolve, join, dirname } from 'node:path';
|
|
4
4
|
import { logger } from '../../../utils/logger.mjs';
|
|
5
5
|
import { resolvePackage } from './resolvePackage.mjs';
|
|
6
6
|
|
|
7
7
|
async function resolveScopeAlias(scope, rootCwd) {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
const expectedPrefix = `@${scope}/`;
|
|
9
|
+
const seen = new Set();
|
|
10
|
+
const matchedNames = [];
|
|
11
|
+
let cur = resolve(rootCwd);
|
|
12
|
+
while (true) {
|
|
13
|
+
const scopeDir = join(cur, 'node_modules', `@${scope}`);
|
|
14
|
+
await collectScopeDir(scopeDir, expectedPrefix, seen, matchedNames);
|
|
15
|
+
const parent = dirname(cur);
|
|
16
|
+
if (parent === cur)
|
|
17
|
+
break;
|
|
18
|
+
cur = parent;
|
|
19
|
+
}
|
|
20
|
+
if (matchedNames.length === 0) {
|
|
21
|
+
logger.error(`scope alias "@${scope}" matched no installed packages in any ancestor "node_modules/@${scope}/" walking up from ${rootCwd}.`);
|
|
11
22
|
process.exit(2);
|
|
12
23
|
}
|
|
13
|
-
const
|
|
24
|
+
const resolved = [];
|
|
25
|
+
for (const name of matchedNames) {
|
|
26
|
+
const meta = await resolvePackage(name, { skipMissingAsset: true });
|
|
27
|
+
if (meta)
|
|
28
|
+
resolved.push(meta);
|
|
29
|
+
}
|
|
30
|
+
return resolved;
|
|
31
|
+
}
|
|
32
|
+
async function collectScopeDir(scopeDir, expectedPrefix, seen, matchedNames) {
|
|
14
33
|
let entries;
|
|
15
34
|
try {
|
|
16
35
|
entries = await readdir(scopeDir);
|
|
17
36
|
}
|
|
18
37
|
catch {
|
|
19
|
-
|
|
20
|
-
process.exit(2);
|
|
38
|
+
return;
|
|
21
39
|
}
|
|
22
|
-
const matchedNames = [];
|
|
23
|
-
const expectedPrefix = `@${scope}/`;
|
|
24
40
|
for (const entry of entries) {
|
|
41
|
+
if (entry.startsWith('.'))
|
|
42
|
+
continue;
|
|
25
43
|
const pkgJsonPath = join(scopeDir, entry, 'package.json');
|
|
26
44
|
if (!existsSync(pkgJsonPath))
|
|
27
45
|
continue;
|
|
@@ -35,33 +53,12 @@ async function resolveScopeAlias(scope, rootCwd) {
|
|
|
35
53
|
}
|
|
36
54
|
if (typeof parsed.name === 'string' &&
|
|
37
55
|
parsed.name.startsWith(expectedPrefix) &&
|
|
38
|
-
parsed.name.length > expectedPrefix.length
|
|
56
|
+
parsed.name.length > expectedPrefix.length &&
|
|
57
|
+
!seen.has(parsed.name)) {
|
|
58
|
+
seen.add(parsed.name);
|
|
39
59
|
matchedNames.push(parsed.name);
|
|
40
60
|
}
|
|
41
61
|
}
|
|
42
|
-
if (matchedNames.length === 0) {
|
|
43
|
-
logger.warn(`scope alias "@${scope}" matched no workspace packages under ${scopeDir}.`);
|
|
44
|
-
return [];
|
|
45
|
-
}
|
|
46
|
-
const resolved = [];
|
|
47
|
-
for (const name of matchedNames) {
|
|
48
|
-
const meta = await resolvePackage(name, { skipMissingAsset: true });
|
|
49
|
-
if (meta)
|
|
50
|
-
resolved.push(meta);
|
|
51
|
-
}
|
|
52
|
-
return resolved;
|
|
53
|
-
}
|
|
54
|
-
function findPackagesRoot(start) {
|
|
55
|
-
let cur = resolve(start);
|
|
56
|
-
while (true) {
|
|
57
|
-
if (existsSync(join(cur, 'package.json')) && existsSync(join(cur, 'packages'))) {
|
|
58
|
-
return cur;
|
|
59
|
-
}
|
|
60
|
-
const parent = dirname(cur);
|
|
61
|
-
if (parent === cur)
|
|
62
|
-
return null;
|
|
63
|
-
cur = parent;
|
|
64
|
-
}
|
|
65
62
|
}
|
|
66
63
|
|
|
67
64
|
export { resolveScopeAlias };
|
|
@@ -1,2 +1,10 @@
|
|
|
1
1
|
import { type Scope } from '../../../core/index.js';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Legacy (non-TTY / --json) scope resolver.
|
|
4
|
+
*
|
|
5
|
+
* The TTY Ink path owns its own scope picker via `ui/components/ScopePicker`.
|
|
6
|
+
* This helper runs only after `renderOrFallback` has chosen the legacy path,
|
|
7
|
+
* where prompting is not appropriate — either stdout is piped or the caller
|
|
8
|
+
* asked for structured `--json` output. Missing flag → exit 2.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolveScopeFlag(flag: string | undefined): Scope;
|
|
@@ -2,12 +2,9 @@ import 'node:crypto';
|
|
|
2
2
|
import 'node:fs/promises';
|
|
3
3
|
import 'node:path';
|
|
4
4
|
import { logger } from '../../../utils/logger.mjs';
|
|
5
|
-
import { isValidScope
|
|
6
|
-
import { selectScopeAsync } from '../../../prompts/selectScope.mjs';
|
|
7
|
-
import '@inquirer/prompts';
|
|
8
|
-
import 'picocolors';
|
|
5
|
+
import { isValidScope } from '../../../core/scope/scope.mjs';
|
|
9
6
|
|
|
10
|
-
|
|
7
|
+
function resolveScopeFlag(flag) {
|
|
11
8
|
if (flag) {
|
|
12
9
|
if (!isValidScope(flag)) {
|
|
13
10
|
logger.error(`Invalid --scope: ${flag}. Expected user | project.`);
|
|
@@ -15,12 +12,9 @@ async function resolveScopeFlag(flag) {
|
|
|
15
12
|
}
|
|
16
13
|
return flag;
|
|
17
14
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
process.exit(2);
|
|
22
|
-
}
|
|
23
|
-
return selectScopeAsync();
|
|
15
|
+
logger.error('--scope is required in non-interactive environments.');
|
|
16
|
+
logger.error(' Pass --scope=user or --scope=project.');
|
|
17
|
+
process.exit(2);
|
|
24
18
|
}
|
|
25
19
|
|
|
26
20
|
export { resolveScopeFlag };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ConsumerPackage } from '../type.js';
|
|
2
|
+
import type { ResolvedMetadata } from './resolvePackage.js';
|
|
3
|
+
/**
|
|
4
|
+
* Convert dispatcher `ResolvedMetadata` into runtime `ConsumerPackage`.
|
|
5
|
+
* Resolves the asset root against `packageRoot` and probes for
|
|
6
|
+
* `dist/claude-hashes.json` presence so both the Ink and legacy paths
|
|
7
|
+
* can treat the target uniformly.
|
|
8
|
+
*/
|
|
9
|
+
export declare function toConsumerPackages(metadataList: readonly ResolvedMetadata[]): Promise<ConsumerPackage[]>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { stat } from 'node:fs/promises';
|
|
2
|
+
import { isAbsolute, resolve, join } from 'node:path';
|
|
3
|
+
import { logger } from '../../../utils/logger.mjs';
|
|
4
|
+
|
|
5
|
+
async function toConsumerPackages(metadataList) {
|
|
6
|
+
const result = [];
|
|
7
|
+
for (const metadata of metadataList) {
|
|
8
|
+
if (!isAbsolute(metadata.packageRoot)) {
|
|
9
|
+
logger.error(`packageRoot must be an absolute path; received: ${metadata.packageRoot}`);
|
|
10
|
+
process.exit(2);
|
|
11
|
+
}
|
|
12
|
+
const assetRoot = resolve(metadata.packageRoot, metadata.assetPath);
|
|
13
|
+
const hashesPath = join(metadata.packageRoot, 'dist', 'claude-hashes.json');
|
|
14
|
+
const hashesPresent = await stat(hashesPath).then(() => true, () => false);
|
|
15
|
+
result.push({
|
|
16
|
+
name: metadata.packageName,
|
|
17
|
+
version: metadata.packageVersion,
|
|
18
|
+
packageRoot: metadata.packageRoot,
|
|
19
|
+
assetRoot,
|
|
20
|
+
hashesPresent,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { toConsumerPackages };
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { hashContent, hashEquals, hashFile, type Sha256Hex, } from './hash/index.js';
|
|
2
2
|
export { HASH_MANIFEST_FILENAME, computeNamespacePrefixes, readHashManifest, type HashManifest, } from './hashManifest/index.js';
|
|
3
|
-
export {
|
|
3
|
+
export { applyAction, summarize, type InjectReport, } from './injectDocs/index.js';
|
|
4
4
|
export { buildPlan, type Action, type InjectPlan, type PlanInput, } from './buildPlan/index.js';
|
|
5
|
-
export { findNearestDotClaudeAncestor,
|
|
5
|
+
export { findNearestDotClaudeAncestor, isValidScope, resolveScope, type Scope, type ScopeResolution, } from './scope/index.js';
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export
|
|
1
|
+
export { applyAction } from './utils/applyAction.js';
|
|
2
|
+
export { summarize } from './utils/summarize.js';
|
|
3
|
+
export type { InjectReport } from './type.js';
|
|
@@ -1,22 +1,3 @@
|
|
|
1
|
-
import type { InjectPlan } from '../buildPlan/index.js';
|
|
2
|
-
import type { Scope } from '../scope/index.js';
|
|
3
|
-
export interface InjectOptions {
|
|
4
|
-
packageName: string;
|
|
5
|
-
packageVersion: string;
|
|
6
|
-
packageRoot: string;
|
|
7
|
-
assetRoot: string;
|
|
8
|
-
scope: Scope;
|
|
9
|
-
dryRun: boolean;
|
|
10
|
-
force: boolean;
|
|
11
|
-
/**
|
|
12
|
-
* Origin directory used to resolve project/local scope targets. When set,
|
|
13
|
-
* `resolveScope` walks up from this path to find the nearest existing
|
|
14
|
-
* `.claude` ancestor. Defaults to `process.cwd()`.
|
|
15
|
-
*/
|
|
16
|
-
originCwd?: string;
|
|
17
|
-
/** Called AFTER plan is built but BEFORE apply. Return false to abort. */
|
|
18
|
-
confirmForce?: (plan: InjectPlan) => Promise<boolean>;
|
|
19
|
-
}
|
|
20
1
|
export interface InjectReport {
|
|
21
2
|
created: string[];
|
|
22
3
|
updated: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { findNearestDotClaudeAncestor,
|
|
1
|
+
export { findNearestDotClaudeAncestor, isValidScope, resolveScope, type Scope, type ScopeResolution, } from './scope.js';
|
|
@@ -13,4 +13,3 @@ export declare function isValidScope(value: unknown): value is Scope;
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function findNearestDotClaudeAncestor(start: string): string | null;
|
|
15
15
|
export declare function resolveScope(scope: Scope, cwd?: string): ScopeResolution;
|
|
16
|
-
export declare function isInteractive(): boolean;
|
|
@@ -34,8 +34,5 @@ function resolveScope(scope, cwd = process.cwd()) {
|
|
|
34
34
|
: `${base}/.claude (project)`,
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
|
-
function isInteractive() {
|
|
38
|
-
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
39
|
-
}
|
|
40
37
|
|
|
41
|
-
export { findNearestDotClaudeAncestor,
|
|
38
|
+
export { findNearestDotClaudeAncestor, isValidScope, resolveScope };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { runCli } from './commands/index.js';
|
|
2
|
-
export { HASH_MANIFEST_FILENAME, computeNamespacePrefixes,
|
|
2
|
+
export { HASH_MANIFEST_FILENAME, computeNamespacePrefixes, isValidScope, readHashManifest, resolveScope, type HashManifest, type InjectReport, type Scope, type ScopeResolution, } from './core/index.js';
|
|
3
3
|
export type { AssetType } from './utils/types.js';
|
package/dist/index.mjs
CHANGED
|
@@ -2,6 +2,6 @@ export { runCli } from './commands/runCli/runCli.mjs';
|
|
|
2
2
|
import 'node:crypto';
|
|
3
3
|
import 'node:fs/promises';
|
|
4
4
|
export { HASH_MANIFEST_FILENAME, computeNamespacePrefixes, readHashManifest } from './core/hashManifest/hashManifest.mjs';
|
|
5
|
-
export { injectDocs } from './core/injectDocs/injectDocs.mjs';
|
|
6
5
|
import 'node:path';
|
|
7
|
-
|
|
6
|
+
import 'picocolors';
|
|
7
|
+
export { isValidScope, resolveScope } from './core/scope/scope.mjs';
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { useCallback } from 'react';
|
|
4
|
+
import { VERSION } from '../../utils/version.mjs';
|
|
5
|
+
import { Banner } from '../components/Banner.mjs';
|
|
6
|
+
import { ConfirmForce } from '../components/ConfirmForce.mjs';
|
|
7
|
+
import { ErrorPanel } from '../components/ErrorPanel.mjs';
|
|
8
|
+
import { Footer } from '../components/Footer.mjs';
|
|
9
|
+
import { ProgressBar } from '../components/ProgressBar.mjs';
|
|
10
|
+
import { ScopePicker } from '../components/ScopePicker.mjs';
|
|
11
|
+
import { Spinner } from '../components/Spinner.mjs';
|
|
12
|
+
import { StepTracker } from '../components/StepTracker.mjs';
|
|
13
|
+
import { Summary } from '../components/Summary.mjs';
|
|
14
|
+
import { TargetCard } from '../components/TargetCard.mjs';
|
|
15
|
+
import { useExitApp } from '../hooks/useExitApp.mjs';
|
|
16
|
+
import { useInjectSession } from '../hooks/useInjectSession.mjs';
|
|
17
|
+
import { usePhase } from '../hooks/usePhase.mjs';
|
|
18
|
+
import { colors } from '../theme/colors.mjs';
|
|
19
|
+
import { icons } from '../theme/icons.mjs';
|
|
20
|
+
import { scopeLabel, etaSeconds } from './utils/eventSelectors.mjs';
|
|
21
|
+
|
|
22
|
+
function InjectApp(props) {
|
|
23
|
+
const { targets, flags, originCwd, onExit } = props;
|
|
24
|
+
const [phase, dispatch] = usePhase({ kind: 'resolving', targets });
|
|
25
|
+
useInjectSession({ targets, flags, originCwd, dispatch });
|
|
26
|
+
const handleExit = useCallback((code) => {
|
|
27
|
+
onExit(code);
|
|
28
|
+
}, [onExit]);
|
|
29
|
+
useExitApp({
|
|
30
|
+
enabled: phase.kind === 'summary',
|
|
31
|
+
exitCode: phase.kind === 'summary' ? phase.exitCode : 0,
|
|
32
|
+
onExit: handleExit,
|
|
33
|
+
delayMs: 100,
|
|
34
|
+
});
|
|
35
|
+
useExitApp({
|
|
36
|
+
enabled: phase.kind === 'error',
|
|
37
|
+
exitCode: 1,
|
|
38
|
+
onExit: handleExit,
|
|
39
|
+
delayMs: 100,
|
|
40
|
+
});
|
|
41
|
+
return (jsxs(Box, { flexDirection: "column", children: [jsx(Banner, { version: VERSION, scope: scopeLabel(phase) }), jsx(Box, { marginTop: 1, children: jsx(StepTracker, { phase: phase, targetCount: targets.length, scope: scopeLabel(phase) }) }), jsx(Box, { flexDirection: "column", marginTop: 1, children: renderPhaseBody(phase) }), jsx(Footer, { phase: phase, version: VERSION })] }));
|
|
42
|
+
}
|
|
43
|
+
function renderPhaseBody(phase) {
|
|
44
|
+
switch (phase.kind) {
|
|
45
|
+
case 'booting':
|
|
46
|
+
case 'resolving':
|
|
47
|
+
return jsx(Spinner, { label: "resolving targets\u2026" });
|
|
48
|
+
case 'scope-select':
|
|
49
|
+
return jsx(ScopePicker, { onSelect: phase.pending });
|
|
50
|
+
case 'planning':
|
|
51
|
+
return (jsxs(Box, { flexDirection: "column", children: [jsx(Spinner, { label: "building plans\u2026" }), jsx(Box, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: [...phase.progress.values()].map((step) => (jsxs(Box, { children: [jsxs(Text, { color: step.status === 'done'
|
|
52
|
+
? colors.success
|
|
53
|
+
: step.status === 'running'
|
|
54
|
+
? colors.warn
|
|
55
|
+
: step.status === 'failed'
|
|
56
|
+
? colors.danger
|
|
57
|
+
: colors.muted, bold: step.status === 'running', children: [step.status === 'done'
|
|
58
|
+
? icons.check
|
|
59
|
+
: step.status === 'failed'
|
|
60
|
+
? icons.cross
|
|
61
|
+
: step.status === 'running'
|
|
62
|
+
? icons.bulletActive
|
|
63
|
+
: icons.bulletPending, ' '] }), jsx(Text, { children: step.packageName }), step.error ? (jsxs(Text, { color: colors.danger, dimColor: true, children: [' ', "(", step.error, ")"] })) : null] }, step.packageName))) })] }));
|
|
64
|
+
case 'diff-review':
|
|
65
|
+
return (jsxs(Box, { flexDirection: "column", children: [phase.plans.map((tp, idx) => (jsx(TargetCard, { target: tp.target, plan: tp.plan, expanded: true, highlighted: idx === phase.focusedIndex }, tp.target.name))), jsx(Box, { marginTop: 1, children: jsxs(Text, { color: colors.muted, dimColor: true, children: ["Applying ", phase.plans.length, " plan(s)\u2026"] }) })] }));
|
|
66
|
+
case 'force-confirm':
|
|
67
|
+
return (jsx(ConfirmForce, { warnings: phase.warnings, onAnswer: phase.pending }));
|
|
68
|
+
case 'applying': {
|
|
69
|
+
const eta = etaSeconds(phase.progress.startedAt, phase.progress.done, phase.progress.total);
|
|
70
|
+
return (jsxs(Box, { flexDirection: "column", children: [jsx(ProgressBar, { done: phase.progress.done, total: phase.progress.total, etaSeconds: eta, label: phase.progress.current }), jsx(Box, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: phase.plans.map((tp) => (jsx(TargetCard, { target: tp.target, plan: tp.plan, expanded: false }, tp.target.name))) })] }));
|
|
71
|
+
}
|
|
72
|
+
case 'summary':
|
|
73
|
+
return (jsxs(Box, { flexDirection: "column", children: [phase.plans.map((tp) => (jsx(TargetCard, { target: tp.target, plan: tp.plan, expanded: false }, tp.target.name))), jsx(Summary, { reports: phase.reports, exitCode: phase.exitCode, dryRun: phase.dryRun })] }));
|
|
74
|
+
case 'error':
|
|
75
|
+
return jsx(ErrorPanel, { error: phase.error });
|
|
76
|
+
default: {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export { InjectApp };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ConsumerPackage } from '../../../commands/runCli/type.js';
|
|
2
|
+
import type { Phase } from '../../types/index.js';
|
|
3
|
+
export declare function scopeLabel(phase: Phase): string | undefined;
|
|
4
|
+
export declare function targetsOf(phase: Phase): readonly ConsumerPackage[];
|
|
5
|
+
export declare function etaSeconds(startedAt: number, done: number, total: number, now?: number): number | undefined;
|