@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,9 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { colors } from '../theme/colors.mjs';
|
|
4
|
+
|
|
5
|
+
function Banner({ version, scope, }) {
|
|
6
|
+
return (jsxs(Box, { borderStyle: "round", borderColor: colors.primary, paddingX: 1, alignSelf: "flex-start", children: [jsx(Text, { bold: true, color: colors.primary, children: "claude-sync" }), jsxs(Text, { color: colors.muted, children: [' ', "v", version] }), scope ? (jsxs(Text, { children: [jsx(Text, { color: colors.muted, children: ' → ' }), jsx(Text, { color: colors.accent, bold: true, children: scope })] })) : null] }));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export { Banner };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Warning } from '../types/index.js';
|
|
3
|
+
interface ConfirmForceProps {
|
|
4
|
+
readonly warnings: readonly Warning[];
|
|
5
|
+
readonly onAnswer: (ok: boolean) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function ConfirmForce({ warnings, onAnswer, }: ConfirmForceProps): React.ReactElement;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { Box, Text } from 'ink';
|
|
4
|
+
import SelectInput from 'ink-select-input';
|
|
5
|
+
import { colors } from '../theme/colors.mjs';
|
|
6
|
+
import { icons } from '../theme/icons.mjs';
|
|
7
|
+
import { limits } from '../theme/layout.mjs';
|
|
8
|
+
|
|
9
|
+
function ConfirmForce({ warnings, onAnswer, }) {
|
|
10
|
+
const [expanded, setExpanded] = useState(false);
|
|
11
|
+
const showAll = expanded || warnings.length <= limits.maxWarningsBeforeCollapse;
|
|
12
|
+
const shown = showAll
|
|
13
|
+
? warnings
|
|
14
|
+
: warnings.slice(0, limits.maxWarningsBeforeCollapse);
|
|
15
|
+
const hidden = warnings.length - shown.length;
|
|
16
|
+
const diverged = warnings.filter((w) => w.kind === 'warn-diverged').length;
|
|
17
|
+
const orphans = warnings.filter((w) => w.kind === 'warn-orphan').length;
|
|
18
|
+
const items = [
|
|
19
|
+
{ label: `Yes, overwrite ${warnings.length} path(s)`, value: 'accept' },
|
|
20
|
+
{ label: 'No, cancel', value: 'cancel' },
|
|
21
|
+
];
|
|
22
|
+
if (!showAll) {
|
|
23
|
+
items.push({ label: `Show all (${hidden} hidden)`, value: 'expand' });
|
|
24
|
+
}
|
|
25
|
+
return (jsxs(Box, { flexDirection: "column", marginTop: 1, children: [jsxs(Text, { color: colors.warn, bold: true, children: [icons.warning, " ", warnings.length, " path(s) need --force confirmation"] }), jsx(Box, { marginLeft: 2, children: jsxs(Text, { color: colors.muted, dimColor: true, children: [diverged, " diverged \u00B7 ", orphans, " orphan"] }) }), jsxs(Box, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: [shown.map((warning) => (jsxs(Box, { children: [jsxs(Text, { color: warning.kind === 'warn-diverged' ? colors.warn : colors.accent, bold: true, children: [warning.kind === 'warn-diverged' ? icons.warning : icons.question, ' '] }), jsxs(Text, { color: colors.muted, dimColor: true, children: ["[", warning.packageName, "]", ' '] }), jsx(Text, { children: warning.relPath })] }, `${warning.packageName}:${warning.relPath}`))), !showAll ? (jsxs(Text, { color: colors.muted, dimColor: true, children: ["\u2026 ", hidden, " more hidden"] })) : null] }), jsx(Box, { marginLeft: 2, marginTop: 1, children: jsx(SelectInput, { items: items, onSelect: (item) => {
|
|
26
|
+
if (item.value === 'accept')
|
|
27
|
+
onAnswer(true);
|
|
28
|
+
else if (item.value === 'cancel')
|
|
29
|
+
onAnswer(false);
|
|
30
|
+
else
|
|
31
|
+
setExpanded(true);
|
|
32
|
+
} }) })] }));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export { ConfirmForce };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { colors } from '../theme/colors.mjs';
|
|
4
|
+
import { icons } from '../theme/icons.mjs';
|
|
5
|
+
|
|
6
|
+
function ErrorPanel({ error, }) {
|
|
7
|
+
return (jsxs(Box, { flexDirection: "column", marginTop: 1, marginBottom: 1, children: [jsxs(Text, { color: colors.danger, bold: true, children: [icons.cross, " ", error.name ?? 'Error'] }), jsx(Box, { marginLeft: 2, marginTop: 1, children: jsx(Text, { color: colors.danger, children: error.message }) }), error.stack ? (jsx(Box, { marginLeft: 2, marginTop: 1, children: jsx(Text, { color: colors.muted, dimColor: true, children: error.stack
|
|
8
|
+
.split('\n')
|
|
9
|
+
.slice(1, 4)
|
|
10
|
+
.map((l) => l.trim())
|
|
11
|
+
.join('\n') }) })) : null] }));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export { ErrorPanel };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { colors } from '../theme/colors.mjs';
|
|
4
|
+
|
|
5
|
+
function hintsFor(kind) {
|
|
6
|
+
switch (kind) {
|
|
7
|
+
case 'scope-select':
|
|
8
|
+
return '↑↓:select · enter:confirm · ctrl-c:cancel';
|
|
9
|
+
case 'diff-review':
|
|
10
|
+
return 'enter:apply · a:accept all warn · esc:cancel';
|
|
11
|
+
case 'force-confirm':
|
|
12
|
+
return 'y:accept · n:cancel';
|
|
13
|
+
case 'applying':
|
|
14
|
+
return 'ctrl-c:abort';
|
|
15
|
+
case 'summary':
|
|
16
|
+
return 'enter:exit';
|
|
17
|
+
case 'error':
|
|
18
|
+
return 'enter:exit';
|
|
19
|
+
default:
|
|
20
|
+
return 'ctrl-c:cancel';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function Footer({ phase, version, }) {
|
|
24
|
+
return (jsxs(Box, { marginTop: 1, children: [jsx(Text, { color: colors.muted, dimColor: true, children: hintsFor(phase.kind) }), jsxs(Text, { color: colors.muted, dimColor: true, children: [' v', version] }), jsxs(Text, { color: colors.muted, dimColor: true, children: [' phase: ', phase.kind] })] }));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { Footer };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { InjectPlan } from '../../core/buildPlan/index.js';
|
|
3
|
+
interface PlanTableProps {
|
|
4
|
+
readonly plan: InjectPlan;
|
|
5
|
+
readonly maxRows?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function PlanTable({ plan, maxRows, }: PlanTableProps): React.ReactElement;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { colors } from '../theme/colors.mjs';
|
|
4
|
+
import { limits } from '../theme/layout.mjs';
|
|
5
|
+
import { ActionRow } from './ActionRow.mjs';
|
|
6
|
+
|
|
7
|
+
function PlanTable({ plan, maxRows = limits.maxPlanRowsBeforeTruncate, }) {
|
|
8
|
+
const actions = plan.actions;
|
|
9
|
+
const overflow = actions.length > maxRows;
|
|
10
|
+
const shown = overflow ? actions.slice(0, limits.planTruncatedReveal) : actions;
|
|
11
|
+
const hidden = actions.length - shown.length;
|
|
12
|
+
return (jsxs(Box, { flexDirection: "column", children: [shown.map((action) => (jsx(ActionRow, { action: action }, `${action.kind}-${action.relPath}`))), overflow ? (jsx(Box, { marginLeft: 2, children: jsxs(Text, { color: colors.muted, dimColor: true, children: ["\u2026 ", hidden, " more action(s) hidden"] }) })) : null] }));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export { PlanTable };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface ProgressBarProps {
|
|
3
|
+
readonly done: number;
|
|
4
|
+
readonly total: number;
|
|
5
|
+
readonly width?: number;
|
|
6
|
+
readonly etaSeconds?: number;
|
|
7
|
+
readonly label?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function ProgressBar({ done, total, width, etaSeconds, label, }: ProgressBarProps): React.ReactElement;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { colors } from '../theme/colors.mjs';
|
|
4
|
+
import { icons } from '../theme/icons.mjs';
|
|
5
|
+
import { widths } from '../theme/layout.mjs';
|
|
6
|
+
|
|
7
|
+
function formatEta(seconds) {
|
|
8
|
+
if (seconds === undefined || !Number.isFinite(seconds))
|
|
9
|
+
return null;
|
|
10
|
+
if (seconds < 1)
|
|
11
|
+
return '<1s';
|
|
12
|
+
if (seconds < 60)
|
|
13
|
+
return `${Math.round(seconds)}s`;
|
|
14
|
+
const m = Math.floor(seconds / 60);
|
|
15
|
+
const s = Math.round(seconds % 60);
|
|
16
|
+
return `${m}m${s}s`;
|
|
17
|
+
}
|
|
18
|
+
function ProgressBar({ done, total, width = widths.progressBarDefault, etaSeconds, label, }) {
|
|
19
|
+
const safeTotal = Math.max(total, 1);
|
|
20
|
+
const ratio = Math.min(Math.max(done / safeTotal, 0), 1);
|
|
21
|
+
const filled = Math.round(ratio * width);
|
|
22
|
+
const empty = width - filled;
|
|
23
|
+
const percent = Math.round(ratio * 100);
|
|
24
|
+
const eta = formatEta(etaSeconds);
|
|
25
|
+
return (jsxs(Box, { children: [jsx(Text, { color: colors.success, children: icons.blockFull.repeat(filled) }), jsx(Text, { color: colors.muted, dimColor: true, children: icons.blockEmpty.repeat(empty) }), jsxs(Text, { color: colors.muted, children: [' ', String(percent).padStart(3), "%"] }), jsxs(Text, { color: colors.muted, dimColor: true, children: [' ', "(", done, "/", total, eta ? ` · ETA ${eta}` : '', ")"] }), label ? (jsxs(Text, { color: colors.muted, dimColor: true, children: [' ', label] })) : null] }));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { ProgressBar };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import SelectInput from 'ink-select-input';
|
|
4
|
+
import { colors } from '../theme/colors.mjs';
|
|
5
|
+
import { icons } from '../theme/icons.mjs';
|
|
6
|
+
|
|
7
|
+
const ITEMS = [
|
|
8
|
+
{
|
|
9
|
+
label: 'user',
|
|
10
|
+
value: 'user',
|
|
11
|
+
hint: '~/.claude (global)',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
label: 'project',
|
|
15
|
+
value: 'project',
|
|
16
|
+
hint: 'nearest ancestor .claude (or cwd)',
|
|
17
|
+
},
|
|
18
|
+
];
|
|
19
|
+
function ScopePicker({ onSelect, }) {
|
|
20
|
+
return (jsxs(Box, { flexDirection: "column", marginTop: 1, children: [jsxs(Text, { bold: true, color: colors.primary, children: [icons.triangleRight, " Select the target scope"] }), jsx(Box, { marginLeft: 2, marginTop: 1, children: jsx(SelectInput, { items: ITEMS.map((it) => ({ label: it.label, value: it.value })), onSelect: (item) => onSelect(item.value), itemComponent: ({ isSelected, label }) => {
|
|
21
|
+
const entry = ITEMS.find((it) => it.label === label);
|
|
22
|
+
return (jsxs(Box, { children: [jsxs(Text, { color: isSelected ? colors.accent : colors.muted, bold: true, children: [isSelected ? icons.triangleRight : ' ', ' '] }), jsx(Text, { color: isSelected ? colors.primary : colors.muted, bold: isSelected, children: label.padEnd(8) }), entry ? (jsx(Text, { color: colors.muted, dimColor: true, children: entry.hint })) : null] }));
|
|
23
|
+
} }) })] }));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { ScopePicker };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface SpinnerProps {
|
|
3
|
+
readonly label?: string;
|
|
4
|
+
readonly color?: string;
|
|
5
|
+
readonly variant?: 'dots' | 'line' | 'arc';
|
|
6
|
+
}
|
|
7
|
+
export declare function Spinner({ label, color, variant, }: SpinnerProps): React.ReactElement;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import InkSpinner from 'ink-spinner';
|
|
4
|
+
import { colors } from '../theme/colors.mjs';
|
|
5
|
+
|
|
6
|
+
function Spinner({ label, color = colors.primary, variant = 'dots', }) {
|
|
7
|
+
return (jsxs(Box, { children: [jsx(Text, { color: color, children: jsx(InkSpinner, { type: variant }) }), label ? (jsxs(Text, { color: colors.muted, children: [' ', label] })) : null] }));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export { Spinner };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export type StatusKind = 'created' | 'updated' | 'skipped' | 'warn' | 'delete' | 'pending' | 'running' | 'done' | 'failed';
|
|
3
|
+
interface StatusBadgeProps {
|
|
4
|
+
readonly kind: StatusKind;
|
|
5
|
+
readonly label?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function StatusBadge({ kind, label, }: StatusBadgeProps): React.ReactElement;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Phase } from '../types/index.js';
|
|
3
|
+
interface StepTrackerProps {
|
|
4
|
+
readonly phase: Phase;
|
|
5
|
+
readonly targetCount?: number;
|
|
6
|
+
readonly scope?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function StepTracker({ phase, targetCount, scope, }: StepTrackerProps): React.ReactElement;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Box, Text } from 'ink';
|
|
4
|
+
import { colors } from '../theme/colors.mjs';
|
|
5
|
+
import { icons } from '../theme/icons.mjs';
|
|
6
|
+
import { Spinner } from './Spinner.mjs';
|
|
7
|
+
|
|
8
|
+
const STEPS = [
|
|
9
|
+
{ key: 'resolve', label: 'resolve', phases: ['booting', 'resolving'] },
|
|
10
|
+
{ key: 'scope', label: 'scope', phases: ['scope-select'] },
|
|
11
|
+
{ key: 'plan', label: 'plan', phases: ['planning', 'diff-review', 'force-confirm'] },
|
|
12
|
+
{ key: 'apply', label: 'apply', phases: ['applying'] },
|
|
13
|
+
{ key: 'done', label: 'done', phases: ['summary', 'error'] },
|
|
14
|
+
];
|
|
15
|
+
function stepState(phaseIndex, stepIndex) {
|
|
16
|
+
if (stepIndex < phaseIndex)
|
|
17
|
+
return 'done';
|
|
18
|
+
if (stepIndex === phaseIndex)
|
|
19
|
+
return 'active';
|
|
20
|
+
return 'pending';
|
|
21
|
+
}
|
|
22
|
+
function phaseToIndex(kind) {
|
|
23
|
+
for (let i = 0; i < STEPS.length; i += 1) {
|
|
24
|
+
const step = STEPS[i];
|
|
25
|
+
if (step.phases.includes(kind))
|
|
26
|
+
return i;
|
|
27
|
+
}
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
|
30
|
+
function StepTracker({ phase, targetCount, scope, }) {
|
|
31
|
+
const phaseIndex = phaseToIndex(phase.kind);
|
|
32
|
+
return (jsxs(Box, { children: [STEPS.map((step, idx) => {
|
|
33
|
+
const state = stepState(phaseIndex, idx);
|
|
34
|
+
const bulletColor = state === 'done'
|
|
35
|
+
? colors.success
|
|
36
|
+
: state === 'active'
|
|
37
|
+
? colors.warn
|
|
38
|
+
: colors.muted;
|
|
39
|
+
return (jsxs(React.Fragment, { children: [idx > 0 ? (jsxs(Text, { color: colors.muted, dimColor: true, children: [' ', icons.divider, ' '] })) : null, state === 'active' ? (jsx(Spinner, { color: bulletColor })) : (jsx(Text, { color: bulletColor, children: state === 'done' ? icons.bulletDone : icons.bulletPending })), jsxs(Text, { color: bulletColor, bold: state === 'active', dimColor: state === 'pending', children: [' ', step.label] })] }, step.key));
|
|
40
|
+
}), typeof targetCount === 'number' || scope ? (jsxs(Text, { color: colors.muted, dimColor: true, children: [' ', typeof targetCount === 'number' ? `${targetCount} target${targetCount === 1 ? '' : 's'}` : '', scope ? ` · scope ${scope}` : ''] })) : null] }));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export { StepTracker };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { InjectReport } from '../../core/index.js';
|
|
3
|
+
interface SummaryProps {
|
|
4
|
+
readonly reports: readonly InjectReport[];
|
|
5
|
+
readonly exitCode: 0 | 1 | 2;
|
|
6
|
+
readonly dryRun: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function Summary({ reports, exitCode, dryRun, }: SummaryProps): React.ReactElement;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { colors } from '../theme/colors.mjs';
|
|
4
|
+
|
|
5
|
+
function aggregate(reports) {
|
|
6
|
+
return reports.reduce((acc, report) => ({
|
|
7
|
+
created: acc.created + report.created.length,
|
|
8
|
+
updated: acc.updated + report.updated.length,
|
|
9
|
+
skipped: acc.skipped + report.skipped.length,
|
|
10
|
+
warnings: acc.warnings + report.warnings.length,
|
|
11
|
+
deleted: acc.deleted + report.deleted.length,
|
|
12
|
+
}), { created: 0, updated: 0, skipped: 0, warnings: 0, deleted: 0 });
|
|
13
|
+
}
|
|
14
|
+
function exitColor(code) {
|
|
15
|
+
if (code === 0)
|
|
16
|
+
return colors.success;
|
|
17
|
+
if (code === 2)
|
|
18
|
+
return colors.warn;
|
|
19
|
+
return colors.danger;
|
|
20
|
+
}
|
|
21
|
+
function Row({ label, value, labelColor, valueColor, labelBold, valueBold, }) {
|
|
22
|
+
return (jsxs(Box, { children: [jsx(Box, { width: 10, children: jsx(Text, { color: labelColor, bold: labelBold, children: label }) }), jsx(Text, { color: valueColor ?? colors.muted, bold: valueBold, children: String(value).padStart(3) })] }));
|
|
23
|
+
}
|
|
24
|
+
function Summary({ reports, exitCode, dryRun, }) {
|
|
25
|
+
const totals = aggregate(reports);
|
|
26
|
+
const exitTone = exitColor(exitCode);
|
|
27
|
+
return (jsxs(Box, { flexDirection: "column", borderStyle: "double", borderColor: colors.primary, paddingX: 2, paddingY: 0, marginTop: 1, alignSelf: "flex-start", children: [jsx(Box, { children: jsxs(Text, { bold: true, color: colors.primary, children: ["Summary", dryRun ? ' (dry-run)' : ''] }) }), jsxs(Box, { marginTop: 1, children: [jsxs(Box, { flexDirection: "column", marginRight: 4, children: [jsx(Row, { label: "created", value: totals.created, labelColor: colors.success, labelBold: true }), jsx(Row, { label: "skipped", value: totals.skipped, labelColor: colors.muted }), jsx(Row, { label: "deleted", value: totals.deleted, labelColor: colors.danger })] }), jsxs(Box, { flexDirection: "column", children: [jsx(Row, { label: "updated", value: totals.updated, labelColor: colors.success }), jsx(Row, { label: "warnings", value: totals.warnings, labelColor: colors.warn, labelBold: true }), jsx(Row, { label: "exit", value: exitCode, labelColor: exitTone, labelBold: true, valueColor: exitTone, valueBold: true })] })] })] }));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { Summary };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { InjectPlan } from '../../core/buildPlan/index.js';
|
|
3
|
+
import type { ConsumerPackage } from '../../commands/runCli/type.js';
|
|
4
|
+
interface TargetCardProps {
|
|
5
|
+
readonly target: ConsumerPackage;
|
|
6
|
+
readonly plan?: InjectPlan;
|
|
7
|
+
readonly expanded?: boolean;
|
|
8
|
+
readonly highlighted?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function TargetCard({ target, plan, expanded, highlighted, }: TargetCardProps): React.ReactElement;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { colors } from '../theme/colors.mjs';
|
|
4
|
+
import { icons } from '../theme/icons.mjs';
|
|
5
|
+
import { PlanTable } from './PlanTable.mjs';
|
|
6
|
+
|
|
7
|
+
function countActions(actions) {
|
|
8
|
+
let copy = 0;
|
|
9
|
+
let skip = 0;
|
|
10
|
+
let warn = 0;
|
|
11
|
+
let del = 0;
|
|
12
|
+
for (const action of actions) {
|
|
13
|
+
if (action.kind === 'copy')
|
|
14
|
+
copy += 1;
|
|
15
|
+
else if (action.kind === 'skip-uptodate')
|
|
16
|
+
skip += 1;
|
|
17
|
+
else if (action.kind === 'warn-diverged' || action.kind === 'warn-orphan')
|
|
18
|
+
warn += 1;
|
|
19
|
+
else if (action.kind === 'delete')
|
|
20
|
+
del += 1;
|
|
21
|
+
}
|
|
22
|
+
return { copy, skip, warn, del };
|
|
23
|
+
}
|
|
24
|
+
function TargetCard({ target, plan, expanded = true, highlighted = false, }) {
|
|
25
|
+
const counts = plan ? countActions(plan.actions) : null;
|
|
26
|
+
return (jsxs(Box, { flexDirection: "column", marginTop: 1, children: [jsxs(Box, { children: [jsxs(Text, { color: highlighted ? colors.accent : colors.primary, bold: true, children: [icons.triangleRight, ' '] }), jsx(Text, { bold: true, children: target.name }), jsxs(Text, { color: colors.muted, dimColor: true, children: ["@", target.version] }), counts ? (jsxs(Text, { color: colors.muted, children: [' [', jsxs(Text, { color: colors.success, bold: true, children: [counts.copy, " copy"] }), ' · ', jsxs(Text, { color: colors.muted, dimColor: true, children: [counts.skip, " skip"] }), counts.warn > 0 ? (jsxs(Fragment, { children: [' · ', jsxs(Text, { color: colors.warn, bold: true, children: [counts.warn, " ", icons.warning] })] })) : null, counts.del > 0 ? (jsxs(Fragment, { children: [' · ', jsxs(Text, { color: colors.danger, bold: true, children: [counts.del, " ", icons.minus] })] })) : null, ']'] })) : null] }), expanded && plan ? (jsx(Box, { marginLeft: 2, children: jsx(PlanTable, { plan: plan }) })) : null] }));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export { TargetCard };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { InjectReport } from '../../core/index.js';
|
|
2
|
+
import type { InjectEvent, TargetPlan } from '../types/index.js';
|
|
3
|
+
interface ApplyStepInput {
|
|
4
|
+
readonly plans: readonly TargetPlan[];
|
|
5
|
+
readonly dryRun: boolean;
|
|
6
|
+
readonly dispatch: (event: InjectEvent) => void;
|
|
7
|
+
}
|
|
8
|
+
export declare function applyAllPlans({ plans, dryRun, dispatch, }: ApplyStepInput): Promise<{
|
|
9
|
+
reports: InjectReport[];
|
|
10
|
+
exitCode: 0 | 1 | 2;
|
|
11
|
+
}>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { applyAction } from '../../core/injectDocs/utils/applyAction.mjs';
|
|
2
|
+
import { summarize } from '../../core/injectDocs/utils/summarize.mjs';
|
|
3
|
+
import { asyncPool } from '../../utils/asyncPool.mjs';
|
|
4
|
+
|
|
5
|
+
const CONCURRENCY = 8;
|
|
6
|
+
async function applyAllPlans({ plans, dryRun, dispatch, }) {
|
|
7
|
+
const total = plans.reduce((acc, tp) => acc + tp.plan.actions.length, 0);
|
|
8
|
+
let done = 0;
|
|
9
|
+
dispatch({ type: 'apply-start', total });
|
|
10
|
+
if (dryRun) {
|
|
11
|
+
const reports = plans.map((tp) => summarize(tp.plan, 0));
|
|
12
|
+
dispatch({ type: 'done', reports, exitCode: 0, dryRun: true });
|
|
13
|
+
return { reports, exitCode: 0 };
|
|
14
|
+
}
|
|
15
|
+
const reports = [];
|
|
16
|
+
for (const tp of plans) {
|
|
17
|
+
await asyncPool(CONCURRENCY, tp.plan.actions, async (action) => {
|
|
18
|
+
await applyAction(action, tp.target.assetRoot);
|
|
19
|
+
done += 1;
|
|
20
|
+
dispatch({ type: 'apply-progress', done, current: action.relPath });
|
|
21
|
+
});
|
|
22
|
+
reports.push(summarize(tp.plan, 0));
|
|
23
|
+
}
|
|
24
|
+
const hasFailure = reports.some((r) => r.exitCode !== 0);
|
|
25
|
+
const exitCode = hasFailure ? 1 : 0;
|
|
26
|
+
dispatch({ type: 'done', reports, exitCode, dryRun: false });
|
|
27
|
+
return { reports, exitCode };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { applyAllPlans };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
interface UseExitAppOptions {
|
|
2
|
+
readonly enabled: boolean;
|
|
3
|
+
readonly exitCode: 0 | 1 | 2;
|
|
4
|
+
readonly onExit: (code: 0 | 1 | 2) => void;
|
|
5
|
+
readonly delayMs?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function useExitApp({ enabled, exitCode, onExit, delayMs, }: UseExitAppOptions): void;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useApp } from 'ink';
|
|
3
|
+
|
|
4
|
+
function useExitApp({ enabled, exitCode, onExit, delayMs = 0, }) {
|
|
5
|
+
const { exit } = useApp();
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
if (!enabled)
|
|
8
|
+
return;
|
|
9
|
+
const handle = setTimeout(() => {
|
|
10
|
+
onExit(exitCode);
|
|
11
|
+
exit();
|
|
12
|
+
}, delayMs);
|
|
13
|
+
return () => {
|
|
14
|
+
clearTimeout(handle);
|
|
15
|
+
};
|
|
16
|
+
}, [enabled, exitCode, delayMs, exit, onExit]);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { useExitApp };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { InjectEvent, TargetPlan, Warning } from '../types/index.js';
|
|
2
|
+
interface RequestForceConfirmInput {
|
|
3
|
+
readonly plans: readonly TargetPlan[];
|
|
4
|
+
readonly warnings: readonly Warning[];
|
|
5
|
+
readonly force: boolean;
|
|
6
|
+
readonly dispatch: (event: InjectEvent) => void;
|
|
7
|
+
}
|
|
8
|
+
export declare function requestForceConfirm({ plans, warnings, force, dispatch, }: RequestForceConfirmInput): Promise<boolean>;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
async function requestForceConfirm({ plans, warnings, force, dispatch, }) {
|
|
2
|
+
const hasWarnings = plans.some((p) => p.plan.requiresForce);
|
|
3
|
+
if (!hasWarnings)
|
|
4
|
+
return true;
|
|
5
|
+
if (!force) {
|
|
6
|
+
dispatch({
|
|
7
|
+
type: 'fail',
|
|
8
|
+
error: new Error('Re-run with --force to proceed, or inspect with --dry-run.'),
|
|
9
|
+
});
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
return new Promise((resolve) => {
|
|
13
|
+
dispatch({
|
|
14
|
+
type: 'force-confirm-required',
|
|
15
|
+
warnings,
|
|
16
|
+
pending: (ok) => {
|
|
17
|
+
dispatch({ type: 'force-answer', ok });
|
|
18
|
+
resolve(ok);
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export { requestForceConfirm };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ConsumerPackage, DefaultFlags } from '../../commands/runCli/type.js';
|
|
2
|
+
import type { InjectEvent } from '../types/index.js';
|
|
3
|
+
interface UseInjectSessionOptions {
|
|
4
|
+
readonly targets: readonly ConsumerPackage[];
|
|
5
|
+
readonly flags: DefaultFlags;
|
|
6
|
+
readonly originCwd: string;
|
|
7
|
+
readonly dispatch: (event: InjectEvent) => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function useInjectSession({ targets, flags, originCwd, dispatch, }: UseInjectSessionOptions): void;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { useState, useRef, useCallback, useEffect } from 'react';
|
|
2
|
+
import { applyAllPlans } from './useApplyStep.mjs';
|
|
3
|
+
import { requestForceConfirm } from './useForceConfirmStep.mjs';
|
|
4
|
+
import { usePlanStep } from './usePlanStep.mjs';
|
|
5
|
+
import { useResolveStep } from './useResolveStep.mjs';
|
|
6
|
+
|
|
7
|
+
function useInjectSession({ targets, flags, originCwd, dispatch, }) {
|
|
8
|
+
const [scope, setScope] = useState(null);
|
|
9
|
+
const [plansReady, setPlansReady] = useState(null);
|
|
10
|
+
const pipelineStarted = useRef(false);
|
|
11
|
+
const onScopeResolved = useCallback((resolved) => {
|
|
12
|
+
setScope(resolved);
|
|
13
|
+
}, []);
|
|
14
|
+
const onPlansReady = useCallback((plans, warnings) => {
|
|
15
|
+
setPlansReady({ plans, warnings });
|
|
16
|
+
}, []);
|
|
17
|
+
useResolveStep({ targets, flags, dispatch, onScopeResolved });
|
|
18
|
+
usePlanStep({
|
|
19
|
+
targets,
|
|
20
|
+
scope,
|
|
21
|
+
originCwd,
|
|
22
|
+
force: Boolean(flags.force),
|
|
23
|
+
dispatch,
|
|
24
|
+
onPlansReady,
|
|
25
|
+
});
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (!plansReady || pipelineStarted.current)
|
|
28
|
+
return;
|
|
29
|
+
pipelineStarted.current = true;
|
|
30
|
+
(async () => {
|
|
31
|
+
const { plans, warnings } = plansReady;
|
|
32
|
+
if (plans.length === 0) {
|
|
33
|
+
dispatch({
|
|
34
|
+
type: 'done',
|
|
35
|
+
reports: [],
|
|
36
|
+
exitCode: 2,
|
|
37
|
+
dryRun: Boolean(flags.dryRun),
|
|
38
|
+
});
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const ok = await requestForceConfirm({
|
|
42
|
+
plans,
|
|
43
|
+
warnings,
|
|
44
|
+
force: Boolean(flags.force),
|
|
45
|
+
dispatch,
|
|
46
|
+
});
|
|
47
|
+
if (!ok)
|
|
48
|
+
return;
|
|
49
|
+
await applyAllPlans({
|
|
50
|
+
plans,
|
|
51
|
+
dryRun: Boolean(flags.dryRun),
|
|
52
|
+
dispatch,
|
|
53
|
+
});
|
|
54
|
+
})().catch((error) => {
|
|
55
|
+
dispatch({
|
|
56
|
+
type: 'fail',
|
|
57
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
}, [plansReady, flags.force, flags.dryRun, dispatch]);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export { useInjectSession };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useInterval(callback: () => void, delayMs: number | null): void;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ConsumerPackage } from '../../commands/runCli/type.js';
|
|
2
|
+
import { type Scope } from '../../core/index.js';
|
|
3
|
+
import type { InjectEvent, TargetPlan, Warning } from '../types/index.js';
|
|
4
|
+
interface UsePlanStepOptions {
|
|
5
|
+
readonly targets: readonly ConsumerPackage[];
|
|
6
|
+
readonly scope: Scope | null;
|
|
7
|
+
readonly originCwd: string;
|
|
8
|
+
readonly force: boolean;
|
|
9
|
+
readonly dispatch: (event: InjectEvent) => void;
|
|
10
|
+
readonly onPlansReady: (plans: readonly TargetPlan[], warnings: readonly Warning[]) => void;
|
|
11
|
+
}
|
|
12
|
+
export declare function usePlanStep({ targets, scope, originCwd, force, dispatch, onPlansReady, }: UsePlanStepOptions): void;
|
|
13
|
+
export {};
|