create-quiver 0.14.1 → 0.15.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/CHANGELOG.md +22 -0
- package/README.md +35 -0
- package/README_FOR_AI.md +15 -4
- package/ROADMAP.md +13 -2
- package/docs/AI_CONTEXT.md.template +4 -0
- package/docs/AI_ONBOARDING_PROMPT.md.template +8 -0
- package/docs/CLI_UX_GUIDE.md +79 -4
- package/docs/COMMANDS.md.template +21 -5
- package/docs/TROUBLESHOOTING.md +70 -0
- package/docs/TROUBLESHOOTING.md.template +20 -0
- package/docs/getting-started/installation.md +86 -0
- package/docs/reference/commands.md +33 -1
- package/package.json +1 -1
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/EVIDENCE_REPORT.md +213 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/EXECUTION_PLAN.md +85 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/SPEC.md +213 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/STATUS.md +31 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/pr.md +103 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-00-spec-foundation/slice.json +71 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-01-cli-ux-runtime-progress-engine/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-01-cli-ux-runtime-progress-engine/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-01-cli-ux-runtime-progress-engine/slice.json +69 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-02-agent-profile-selection-selectors/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-02-agent-profile-selection-selectors/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-02-agent-profile-selection-selectors/slice.json +81 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-03-provider-model-selection-contract/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-03-provider-model-selection-contract/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-03-provider-model-selection-contract/slice.json +75 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-04-planner-ia-progress-flows/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-04-planner-ia-progress-flows/EXECUTION_BRIEF.md +57 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-04-planner-ia-progress-flows/slice.json +85 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-05-executor-pr-progress-flows/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-05-executor-pr-progress-flows/EXECUTION_BRIEF.md +57 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-05-executor-pr-progress-flows/slice.json +85 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-06-doctor-visual-json-contract/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-06-doctor-visual-json-contract/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-06-doctor-visual-json-contract/slice.json +81 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-07-interactive-init-spec-create/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-07-interactive-init-spec-create/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-07-interactive-init-spec-create/slice.json +85 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-08-tests-docs-cross-platform-release/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-08-tests-docs-cross-platform-release/EXECUTION_BRIEF.md +59 -0
- package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-08-tests-docs-cross-platform-release/slice.json +95 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/EVIDENCE_REPORT.md +185 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/EXECUTION_PLAN.md +85 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/SPEC.md +337 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/STATUS.md +30 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/pr.md +98 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +30 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +51 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/slices/slice-00-spec-foundation/slice.json +62 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/slices/slice-01-model-catalog-alias-normalization/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/slices/slice-01-model-catalog-alias-normalization/EXECUTION_BRIEF.md +53 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/slices/slice-01-model-catalog-alias-normalization/slice.json +72 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/slices/slice-02-interactive-agent-set-selectors/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/slices/slice-02-interactive-agent-set-selectors/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/slices/slice-02-interactive-agent-set-selectors/slice.json +78 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/slices/slice-03-agent-doctor-repair/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/slices/slice-03-agent-doctor-repair/EXECUTION_BRIEF.md +57 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/slices/slice-03-agent-doctor-repair/slice.json +77 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/slices/slice-04-shared-preflight-provider-errors/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/slices/slice-04-shared-preflight-provider-errors/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/slices/slice-04-shared-preflight-provider-errors/slice.json +84 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/slices/slice-05-ai-models-list/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/slices/slice-05-ai-models-list/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/slices/slice-05-ai-models-list/slice.json +72 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/slices/slice-06-docs-templates-alignment/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/slices/slice-06-docs-templates-alignment/EXECUTION_BRIEF.md +58 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/slices/slice-06-docs-templates-alignment/slice.json +84 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/slices/slice-07-tests-smokes-release-readiness/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/slices/slice-07-tests-smokes-release-readiness/EXECUTION_BRIEF.md +59 -0
- package/specs/quiver-v31-ai-model-catalog-agent-selection/slices/slice-07-tests-smokes-release-readiness/slice.json +94 -0
- package/specs/quiver-v32-npx-installation-guidance/EVIDENCE_REPORT.md +26 -0
- package/specs/quiver-v32-npx-installation-guidance/SPEC.md +55 -0
- package/specs/quiver-v32-npx-installation-guidance/STATUS.md +23 -0
- package/specs/quiver-v32-npx-installation-guidance/pr.md +72 -0
- package/specs/quiver-v32-npx-installation-guidance/slices/slice-00-installation-docs/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v32-npx-installation-guidance/slices/slice-00-installation-docs/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v32-npx-installation-guidance/slices/slice-00-installation-docs/slice.json +75 -0
- package/src/create-quiver/commands/ai.js +797 -99
- package/src/create-quiver/commands/flow.js +2 -2
- package/src/create-quiver/commands/spec.js +106 -6
- package/src/create-quiver/index.js +557 -78
- package/src/create-quiver/lib/agent-profiles.js +440 -11
- package/src/create-quiver/lib/ai/execution-plan.js +111 -8
- package/src/create-quiver/lib/ai/executor.js +291 -28
- package/src/create-quiver/lib/ai/model-catalog.js +333 -0
- package/src/create-quiver/lib/ai/providers.js +211 -2
- package/src/create-quiver/lib/cli/selectors.js +160 -0
- package/src/create-quiver/lib/cli/theme.js +5 -2
- package/src/create-quiver/lib/cli/ux-flags.js +18 -0
- package/src/create-quiver/lib/cli/ux.js +100 -5
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
const { resolveUxMode } = require('./ux');
|
|
2
|
+
|
|
3
|
+
function formatError(message) {
|
|
4
|
+
return `create-quiver: ${message}`;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function normalizeSelectorOptions(options = []) {
|
|
8
|
+
return options.map((option) => {
|
|
9
|
+
if (typeof option === 'string') {
|
|
10
|
+
return {
|
|
11
|
+
label: option,
|
|
12
|
+
value: option,
|
|
13
|
+
hint: '',
|
|
14
|
+
default: false,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
label: String(option.label || option.name || option.id || option.value || ''),
|
|
19
|
+
value: String(option.value || option.id || option.path || option.label || ''),
|
|
20
|
+
hint: option.hint || option.description || '',
|
|
21
|
+
default: option.default === true || option.recommended === true,
|
|
22
|
+
raw: option.raw || option,
|
|
23
|
+
};
|
|
24
|
+
}).filter((option) => option.label && option.value);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function findOption(options, value) {
|
|
28
|
+
const normalized = String(value || '').trim();
|
|
29
|
+
if (!normalized) return null;
|
|
30
|
+
return options.find((option) => option.value === normalized || option.label === normalized) || null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function resolveDefaultOption(options, defaultValue) {
|
|
34
|
+
return findOption(options, defaultValue) || options.find((option) => option.default) || (options.length === 1 ? options[0] : null);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function loadClack(injected) {
|
|
38
|
+
if (injected) return injected;
|
|
39
|
+
return import('@clack/prompts');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function selectOption(message, rawOptions, options = {}) {
|
|
43
|
+
const normalizedOptions = normalizeSelectorOptions(rawOptions);
|
|
44
|
+
if (normalizedOptions.length === 0) {
|
|
45
|
+
throw new Error(formatError(`${options.name || 'selection'} has no available options.`));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const explicit = findOption(normalizedOptions, options.value);
|
|
49
|
+
if (explicit) return explicit;
|
|
50
|
+
if (options.value) {
|
|
51
|
+
throw new Error(formatError(`${options.name || 'selection'} option '${options.value}' was not found.`));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const mode = resolveUxMode({
|
|
55
|
+
interactive: options.interactive === true,
|
|
56
|
+
json: options.json,
|
|
57
|
+
noColor: options.noColor,
|
|
58
|
+
stdinIsTTY: options.stdinIsTTY,
|
|
59
|
+
stdoutIsTTY: options.stdoutIsTTY,
|
|
60
|
+
stderrIsTTY: options.stderrIsTTY,
|
|
61
|
+
}, options.env || process.env, {
|
|
62
|
+
input: options.input || process.stdin,
|
|
63
|
+
output: options.output || process.stdout,
|
|
64
|
+
error: options.error || process.stderr,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
if (mode.usePrompts) {
|
|
68
|
+
if (typeof options.promptSelect === 'function') {
|
|
69
|
+
const selected = await options.promptSelect(message, normalizedOptions);
|
|
70
|
+
const match = findOption(normalizedOptions, selected);
|
|
71
|
+
if (!match) {
|
|
72
|
+
throw new Error(formatError(`${options.name || 'selection'} prompt returned unknown option '${selected}'.`));
|
|
73
|
+
}
|
|
74
|
+
return match;
|
|
75
|
+
}
|
|
76
|
+
const clack = await loadClack(options.prompts);
|
|
77
|
+
const selected = await clack.select({
|
|
78
|
+
message,
|
|
79
|
+
options: normalizedOptions.map((option) => ({
|
|
80
|
+
label: option.label,
|
|
81
|
+
value: option.value,
|
|
82
|
+
hint: option.hint || undefined,
|
|
83
|
+
})),
|
|
84
|
+
initialValue: resolveDefaultOption(normalizedOptions, options.defaultValue)?.value,
|
|
85
|
+
});
|
|
86
|
+
if (clack.isCancel && clack.isCancel(selected)) {
|
|
87
|
+
throw new Error(formatError(`${options.name || 'selection'} was canceled.`));
|
|
88
|
+
}
|
|
89
|
+
return findOption(normalizedOptions, selected);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const fallback = resolveDefaultOption(normalizedOptions, options.defaultValue);
|
|
93
|
+
if (fallback) return fallback;
|
|
94
|
+
|
|
95
|
+
const flag = options.flag || '--value';
|
|
96
|
+
const available = normalizedOptions.map((option) => option.value).join(', ');
|
|
97
|
+
throw new Error(formatError([
|
|
98
|
+
`${options.name || 'selection'} requires an explicit choice.`,
|
|
99
|
+
`Available options: ${available}.`,
|
|
100
|
+
`Use ${flag} <value> or rerun with --interactive.`,
|
|
101
|
+
].join('\n')));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function promptText(message, options = {}) {
|
|
105
|
+
const explicit = String(options.value || '').trim();
|
|
106
|
+
if (explicit) return explicit;
|
|
107
|
+
|
|
108
|
+
const mode = resolveUxMode({
|
|
109
|
+
interactive: options.interactive === true,
|
|
110
|
+
json: options.json,
|
|
111
|
+
noColor: options.noColor,
|
|
112
|
+
stdinIsTTY: options.stdinIsTTY,
|
|
113
|
+
stdoutIsTTY: options.stdoutIsTTY,
|
|
114
|
+
stderrIsTTY: options.stderrIsTTY,
|
|
115
|
+
}, options.env || process.env, {
|
|
116
|
+
input: options.input || process.stdin,
|
|
117
|
+
output: options.output || process.stdout,
|
|
118
|
+
error: options.error || process.stderr,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
if (mode.usePrompts) {
|
|
122
|
+
if (typeof options.promptText === 'function') {
|
|
123
|
+
const value = await options.promptText(message, options);
|
|
124
|
+
const normalized = String(value || '').trim();
|
|
125
|
+
if (!normalized && options.required !== false) {
|
|
126
|
+
throw new Error(formatError(`${options.name || 'input'} is required.`));
|
|
127
|
+
}
|
|
128
|
+
return normalized;
|
|
129
|
+
}
|
|
130
|
+
const clack = await loadClack(options.prompts);
|
|
131
|
+
const value = await clack.text({
|
|
132
|
+
message,
|
|
133
|
+
placeholder: options.placeholder,
|
|
134
|
+
initialValue: options.initialValue,
|
|
135
|
+
validate: options.required === false
|
|
136
|
+
? undefined
|
|
137
|
+
: (input) => (String(input || '').trim() ? undefined : 'Required'),
|
|
138
|
+
});
|
|
139
|
+
if (clack.isCancel && clack.isCancel(value)) {
|
|
140
|
+
throw new Error(formatError(`${options.name || 'input'} was canceled.`));
|
|
141
|
+
}
|
|
142
|
+
return String(value || '').trim();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (options.defaultValue !== undefined && options.defaultValue !== null) {
|
|
146
|
+
return String(options.defaultValue).trim();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const flag = options.flag || '--value';
|
|
150
|
+
throw new Error(formatError([
|
|
151
|
+
`${options.name || 'input'} requires an explicit value.`,
|
|
152
|
+
`Use ${flag} <value> or rerun with --interactive.`,
|
|
153
|
+
].join('\n')));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
module.exports = {
|
|
157
|
+
normalizeSelectorOptions,
|
|
158
|
+
promptText,
|
|
159
|
+
selectOption,
|
|
160
|
+
};
|
|
@@ -9,8 +9,11 @@ const QUIVER_COLORS = Object.freeze({
|
|
|
9
9
|
const STATUS_COLORS = Object.freeze({
|
|
10
10
|
info: 'sky',
|
|
11
11
|
command: 'blue',
|
|
12
|
+
success: 'blue',
|
|
12
13
|
planner: 'violet',
|
|
13
14
|
review: 'magenta',
|
|
15
|
+
warning: 'magenta',
|
|
16
|
+
error: 'magenta',
|
|
14
17
|
risk: 'magenta',
|
|
15
18
|
muted: 'periwinkle',
|
|
16
19
|
});
|
|
@@ -70,8 +73,8 @@ function resolveTheme(options = {}, env = process.env, tty = {}) {
|
|
|
70
73
|
const useColor = shouldUseColor(options, env, tty);
|
|
71
74
|
const useUnicode = shouldUseUnicode(options, env, tty);
|
|
72
75
|
const symbols = useUnicode
|
|
73
|
-
? { start: '◇', success: '✓', warning: '!', error: '✖', bullet: '•' }
|
|
74
|
-
: { start: '*', success: 'OK', warning: 'WARN', error: 'ERR', bullet: '-' };
|
|
76
|
+
? { start: '◇', section: '◆', success: '✓', warning: '!', error: '✖', bullet: '•' }
|
|
77
|
+
: { start: '*', section: '*', success: 'OK', warning: 'WARN', error: 'ERR', bullet: '-' };
|
|
75
78
|
const theme = {
|
|
76
79
|
colors: QUIVER_COLORS,
|
|
77
80
|
statusColors: STATUS_COLORS,
|
|
@@ -5,6 +5,12 @@ const UX_FLAG_OPTIONS = Object.freeze({
|
|
|
5
5
|
});
|
|
6
6
|
|
|
7
7
|
const UX_FLAG_MATRIX = Object.freeze({
|
|
8
|
+
init: Object.freeze({
|
|
9
|
+
withPlanner: false,
|
|
10
|
+
interactive: true,
|
|
11
|
+
review: false,
|
|
12
|
+
note: 'guided project onboarding choices',
|
|
13
|
+
}),
|
|
8
14
|
'ai prepare-context': Object.freeze({
|
|
9
15
|
withPlanner: true,
|
|
10
16
|
interactive: true,
|
|
@@ -29,6 +35,18 @@ const UX_FLAG_MATRIX = Object.freeze({
|
|
|
29
35
|
review: true,
|
|
30
36
|
note: 'PR body review and interactive PR inputs',
|
|
31
37
|
}),
|
|
38
|
+
'ai execute-slice': Object.freeze({
|
|
39
|
+
withPlanner: false,
|
|
40
|
+
interactive: true,
|
|
41
|
+
review: false,
|
|
42
|
+
note: 'executor profile and ready-slice selection',
|
|
43
|
+
}),
|
|
44
|
+
'ai execute-plan': Object.freeze({
|
|
45
|
+
withPlanner: false,
|
|
46
|
+
interactive: true,
|
|
47
|
+
review: false,
|
|
48
|
+
note: 'execution strategy confirmation and future selectors',
|
|
49
|
+
}),
|
|
32
50
|
});
|
|
33
51
|
|
|
34
52
|
function formatError(message) {
|
|
@@ -49,6 +49,11 @@ function createUx(options = {}) {
|
|
|
49
49
|
write(`${text}\n`);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
function formatStatus(status, text) {
|
|
53
|
+
const symbol = mode.theme.symbols[status] || mode.theme.symbols.bullet;
|
|
54
|
+
return mode.theme.status(status, `${symbol} ${text}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
52
57
|
function heading(text) {
|
|
53
58
|
if (mode.json) return;
|
|
54
59
|
if (!mode.decoration) {
|
|
@@ -59,15 +64,65 @@ function createUx(options = {}) {
|
|
|
59
64
|
line(mode.theme.status('planner', `${prefix} ${text}`));
|
|
60
65
|
}
|
|
61
66
|
|
|
67
|
+
function section(text) {
|
|
68
|
+
if (mode.json) return;
|
|
69
|
+
if (!mode.decoration) {
|
|
70
|
+
line(text);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const prefix = mode.theme.symbols.section || mode.theme.symbols.start;
|
|
74
|
+
line(mode.theme.status('command', `${prefix} ${text}`));
|
|
75
|
+
}
|
|
76
|
+
|
|
62
77
|
function writeStatus(status, text) {
|
|
63
78
|
if (mode.json) return;
|
|
64
79
|
if (!mode.decoration) {
|
|
65
80
|
line(text);
|
|
66
81
|
return;
|
|
67
82
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
83
|
+
line(formatStatus(status, text));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function check(text) {
|
|
87
|
+
writeStatus('success', text);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function warning(text) {
|
|
91
|
+
writeStatus('warning', text);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function error(text) {
|
|
95
|
+
writeStatus('error', text);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function info(text) {
|
|
99
|
+
writeStatus('info', text);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function summary(items = [], summaryOptions = {}) {
|
|
103
|
+
if (mode.json) return;
|
|
104
|
+
const title = summaryOptions.title || 'Summary';
|
|
105
|
+
section(title);
|
|
106
|
+
for (const item of items) {
|
|
107
|
+
if (typeof item === 'string') {
|
|
108
|
+
line(mode.decoration ? mode.theme.status('muted', ` ${mode.theme.symbols.bullet} ${item}`) : `- ${item}`);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (item && typeof item === 'object') {
|
|
112
|
+
const label = item.label || item.key || '';
|
|
113
|
+
const value = item.value == null ? '' : String(item.value);
|
|
114
|
+
const text = label ? `${label}: ${value}` : value;
|
|
115
|
+
line(mode.decoration ? mode.theme.status(item.status || 'muted', ` ${mode.theme.symbols.bullet} ${text}`) : `- ${text}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function nextSteps(steps = [], stepOptions = {}) {
|
|
121
|
+
if (mode.json || !Array.isArray(steps) || steps.length === 0) return;
|
|
122
|
+
section(stepOptions.title || 'Next steps');
|
|
123
|
+
for (const step of steps) {
|
|
124
|
+
line(mode.decoration ? mode.theme.status('command', ` ${mode.theme.symbols.bullet} ${step}`) : `- ${step}`);
|
|
125
|
+
}
|
|
71
126
|
}
|
|
72
127
|
|
|
73
128
|
async function withSpinner(message, task, spinnerOptions = {}) {
|
|
@@ -84,17 +139,49 @@ function createUx(options = {}) {
|
|
|
84
139
|
|
|
85
140
|
const clack = await loadClack(options.prompts);
|
|
86
141
|
const spinner = clack.spinner();
|
|
142
|
+
let stopped = false;
|
|
143
|
+
function stop(message, code) {
|
|
144
|
+
if (stopped) return;
|
|
145
|
+
stopped = true;
|
|
146
|
+
spinner.stop(message, code);
|
|
147
|
+
}
|
|
87
148
|
spinner.start(message);
|
|
88
149
|
try {
|
|
89
150
|
const result = await task();
|
|
90
|
-
|
|
151
|
+
stop(spinnerOptions.successMessage || message);
|
|
91
152
|
return result;
|
|
92
153
|
} catch (error) {
|
|
93
|
-
|
|
154
|
+
stop(spinnerOptions.failureMessage || `Failed: ${message}`, 1);
|
|
94
155
|
throw error;
|
|
95
156
|
}
|
|
96
157
|
}
|
|
97
158
|
|
|
159
|
+
async function taskGroup(title, stages = []) {
|
|
160
|
+
heading(title);
|
|
161
|
+
const results = [];
|
|
162
|
+
for (const stage of stages) {
|
|
163
|
+
const run = typeof stage === 'function' ? stage : stage.run;
|
|
164
|
+
const message = typeof stage === 'function' ? stage.name || 'Running task' : stage.message;
|
|
165
|
+
const successMessage = stage.successMessage || message;
|
|
166
|
+
const useSpinner = stage.spinner === true;
|
|
167
|
+
if (typeof run !== 'function') {
|
|
168
|
+
throw new Error('create-quiver: taskGroup stage requires a run function.');
|
|
169
|
+
}
|
|
170
|
+
const result = useSpinner
|
|
171
|
+
? await withSpinner(message, run, {
|
|
172
|
+
successMessage,
|
|
173
|
+
failureMessage: stage.failureMessage,
|
|
174
|
+
echo: stage.echo,
|
|
175
|
+
})
|
|
176
|
+
: await run();
|
|
177
|
+
if (!useSpinner) {
|
|
178
|
+
check(successMessage);
|
|
179
|
+
}
|
|
180
|
+
results.push(result);
|
|
181
|
+
}
|
|
182
|
+
return results;
|
|
183
|
+
}
|
|
184
|
+
|
|
98
185
|
async function promptConfirm(message, promptOptions = {}) {
|
|
99
186
|
if (!mode.usePrompts) {
|
|
100
187
|
throw new Error('create-quiver: interactive confirmation requires an interactive TTY. Pass the equivalent flags or rerun with --interactive.');
|
|
@@ -114,11 +201,19 @@ function createUx(options = {}) {
|
|
|
114
201
|
}
|
|
115
202
|
|
|
116
203
|
return {
|
|
204
|
+
check,
|
|
205
|
+
error,
|
|
117
206
|
heading,
|
|
207
|
+
info,
|
|
118
208
|
line,
|
|
119
209
|
mode,
|
|
210
|
+
nextSteps,
|
|
120
211
|
promptConfirm,
|
|
212
|
+
section,
|
|
213
|
+
summary,
|
|
214
|
+
taskGroup,
|
|
121
215
|
theme: mode.theme,
|
|
216
|
+
warning,
|
|
122
217
|
withSpinner,
|
|
123
218
|
writeStatus,
|
|
124
219
|
};
|