grimoire-wizard 0.5.2 → 0.6.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 +66 -51
- package/dist/cli.js +528 -75
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +114 -12
- package/dist/index.js +527 -74
- package/dist/index.js.map +1 -1
- package/examples/yaml/demo.yaml +54 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -56,6 +56,11 @@ var separatorOptionSchema = z.object({
|
|
|
56
56
|
separator: z.string()
|
|
57
57
|
});
|
|
58
58
|
var selectChoiceSchema = z.union([selectOptionSchema, separatorOptionSchema]);
|
|
59
|
+
var stepReviewConfigSchema = z.object({
|
|
60
|
+
hide: z.boolean().optional(),
|
|
61
|
+
label: z.string().optional(),
|
|
62
|
+
format: z.enum(["none", "uppercase", "lowercase", "capitalize"]).optional()
|
|
63
|
+
}).optional();
|
|
59
64
|
var baseStepFields = {
|
|
60
65
|
id: z.string(),
|
|
61
66
|
message: z.string(),
|
|
@@ -64,7 +69,8 @@ var baseStepFields = {
|
|
|
64
69
|
when: conditionSchema.optional(),
|
|
65
70
|
keepValuesOnPrevious: z.boolean().optional(),
|
|
66
71
|
required: z.boolean().optional(),
|
|
67
|
-
group: z.string().optional()
|
|
72
|
+
group: z.string().optional(),
|
|
73
|
+
review: stepReviewConfigSchema
|
|
68
74
|
};
|
|
69
75
|
var textStepSchema = z.object({
|
|
70
76
|
...baseStepFields,
|
|
@@ -81,7 +87,8 @@ var selectStepSchema = z.object({
|
|
|
81
87
|
default: z.string().optional(),
|
|
82
88
|
routes: z.record(z.string(), z.string()).optional(),
|
|
83
89
|
pageSize: z.number().int().positive().optional(),
|
|
84
|
-
loop: z.boolean().optional()
|
|
90
|
+
loop: z.boolean().optional(),
|
|
91
|
+
columns: z.number().int().min(1).max(8).optional()
|
|
85
92
|
});
|
|
86
93
|
var multiSelectStepSchema = z.object({
|
|
87
94
|
...baseStepFields,
|
|
@@ -92,7 +99,8 @@ var multiSelectStepSchema = z.object({
|
|
|
92
99
|
min: z.number().int().nonnegative().optional(),
|
|
93
100
|
max: z.number().int().positive().optional(),
|
|
94
101
|
pageSize: z.number().int().positive().optional(),
|
|
95
|
-
loop: z.boolean().optional()
|
|
102
|
+
loop: z.boolean().optional(),
|
|
103
|
+
columns: z.number().int().min(1).max(8).optional()
|
|
96
104
|
});
|
|
97
105
|
var confirmStepSchema = z.object({
|
|
98
106
|
...baseStepFields,
|
|
@@ -120,7 +128,8 @@ var searchStepSchema = z.object({
|
|
|
120
128
|
default: z.string().optional(),
|
|
121
129
|
placeholder: z.string().optional(),
|
|
122
130
|
pageSize: z.number().int().positive().optional(),
|
|
123
|
-
loop: z.boolean().optional()
|
|
131
|
+
loop: z.boolean().optional(),
|
|
132
|
+
columns: z.number().int().min(1).max(8).optional()
|
|
124
133
|
});
|
|
125
134
|
var editorStepSchema = z.object({
|
|
126
135
|
...baseStepFields,
|
|
@@ -148,7 +157,14 @@ var messageStepSchema = z.object({
|
|
|
148
157
|
});
|
|
149
158
|
var noteStepSchema = z.object({
|
|
150
159
|
...baseStepFields,
|
|
151
|
-
type: z.literal("note")
|
|
160
|
+
type: z.literal("note"),
|
|
161
|
+
style: z.enum(["info", "warning", "error", "success", "code", "banner"]).optional()
|
|
162
|
+
});
|
|
163
|
+
var browserStepSchema = z.object({
|
|
164
|
+
...baseStepFields,
|
|
165
|
+
type: z.literal("browser"),
|
|
166
|
+
url: z.string(),
|
|
167
|
+
fallback: z.string().optional()
|
|
152
168
|
});
|
|
153
169
|
var stepConfigSchema = z.discriminatedUnion("type", [
|
|
154
170
|
textStepSchema,
|
|
@@ -162,12 +178,19 @@ var stepConfigSchema = z.discriminatedUnion("type", [
|
|
|
162
178
|
pathStepSchema,
|
|
163
179
|
toggleStepSchema,
|
|
164
180
|
messageStepSchema,
|
|
165
|
-
noteStepSchema
|
|
181
|
+
noteStepSchema,
|
|
182
|
+
browserStepSchema
|
|
166
183
|
]);
|
|
167
184
|
var hexColorSchema = z.string().regex(
|
|
168
185
|
/^#[0-9a-fA-F]{6}$/,
|
|
169
186
|
"Must be a 6-digit hex color (e.g., #FF0000)"
|
|
170
187
|
);
|
|
188
|
+
var progressBarConfigSchema = z.object({
|
|
189
|
+
width: z.number().int().positive().max(200).optional(),
|
|
190
|
+
filledColor: hexColorSchema.optional(),
|
|
191
|
+
emptyColor: hexColorSchema.optional(),
|
|
192
|
+
style: z.enum(["blocks", "line", "dots", "arrow"]).optional()
|
|
193
|
+
}).optional();
|
|
171
194
|
var themeConfigSchema = z.object({
|
|
172
195
|
preset: z.enum(["default", "catppuccin", "dracula", "nord", "tokyonight", "monokai"]).optional(),
|
|
173
196
|
tokens: z.object({
|
|
@@ -177,7 +200,12 @@ var themeConfigSchema = z.object({
|
|
|
177
200
|
warning: hexColorSchema.optional(),
|
|
178
201
|
info: hexColorSchema.optional(),
|
|
179
202
|
muted: hexColorSchema.optional(),
|
|
180
|
-
accent: hexColorSchema.optional()
|
|
203
|
+
accent: hexColorSchema.optional(),
|
|
204
|
+
highlight: hexColorSchema.optional(),
|
|
205
|
+
highlightBg: hexColorSchema.optional(),
|
|
206
|
+
pointer: hexColorSchema.optional(),
|
|
207
|
+
checked: hexColorSchema.optional(),
|
|
208
|
+
dimmed: hexColorSchema.optional()
|
|
181
209
|
}).optional(),
|
|
182
210
|
icons: z.object({
|
|
183
211
|
step: z.string().optional(),
|
|
@@ -191,12 +219,15 @@ var themeConfigSchema = z.object({
|
|
|
191
219
|
frames: z.array(z.string()).min(1),
|
|
192
220
|
interval: z.number().positive().optional()
|
|
193
221
|
})
|
|
194
|
-
]).optional()
|
|
222
|
+
]).optional(),
|
|
223
|
+
spinnerElapsed: z.boolean().optional(),
|
|
224
|
+
progressBar: progressBarConfigSchema
|
|
195
225
|
});
|
|
196
226
|
var preFlightCheckSchema = z.object({
|
|
197
227
|
name: z.string(),
|
|
198
228
|
run: z.string(),
|
|
199
|
-
message: z.string()
|
|
229
|
+
message: z.string(),
|
|
230
|
+
showOutput: z.boolean().optional()
|
|
200
231
|
});
|
|
201
232
|
var actionConfigSchema = z.object({
|
|
202
233
|
name: z.string().optional(),
|
|
@@ -209,7 +240,13 @@ var wizardConfigSchema = z.object({
|
|
|
209
240
|
version: z.string().optional(),
|
|
210
241
|
description: z.string().optional(),
|
|
211
242
|
review: z.boolean().optional(),
|
|
212
|
-
icon: z.string().optional()
|
|
243
|
+
icon: z.string().optional(),
|
|
244
|
+
iconSize: z.union([z.enum(["small", "medium", "large"]), z.number().int().positive()]).optional(),
|
|
245
|
+
font: z.string().min(1).optional(),
|
|
246
|
+
banner: z.union([z.string(), z.function()]).optional(),
|
|
247
|
+
subtitle: z.string().optional(),
|
|
248
|
+
clearBetweenSteps: z.boolean().optional(),
|
|
249
|
+
checksStyle: z.enum(["spinner", "tasklist"]).optional()
|
|
213
250
|
}),
|
|
214
251
|
theme: themeConfigSchema.optional(),
|
|
215
252
|
steps: z.array(stepConfigSchema).min(1),
|
|
@@ -490,11 +527,12 @@ async function loadWithInheritance(filePath, seen) {
|
|
|
490
527
|
async function loadWizardConfig(filePath) {
|
|
491
528
|
const config = await loadWithInheritance(filePath, /* @__PURE__ */ new Set());
|
|
492
529
|
detectCycles(config);
|
|
530
|
+
config._configFilePath = resolve(filePath);
|
|
493
531
|
return config;
|
|
494
532
|
}
|
|
495
533
|
|
|
496
534
|
// src/runner.ts
|
|
497
|
-
import { execSync } from "child_process";
|
|
535
|
+
import { execSync, execFileSync } from "child_process";
|
|
498
536
|
import { resolve as resolve2, dirname as dirname2 } from "path";
|
|
499
537
|
import { pathToFileURL } from "url";
|
|
500
538
|
|
|
@@ -671,7 +709,8 @@ function wizardReducer(state, transition, config) {
|
|
|
671
709
|
errors: { ...state.errors, [state.currentStepId]: validationError }
|
|
672
710
|
};
|
|
673
711
|
}
|
|
674
|
-
const
|
|
712
|
+
const isDisplayOnly = currentStep.type === "note" || currentStep.type === "message" || currentStep.type === "browser";
|
|
713
|
+
const updatedAnswers = isDisplayOnly ? { ...state.answers } : {
|
|
675
714
|
...state.answers,
|
|
676
715
|
[state.currentStepId]: transition.value
|
|
677
716
|
};
|
|
@@ -879,7 +918,12 @@ var DEFAULT_TOKENS = {
|
|
|
879
918
|
warning: "#FFD93D",
|
|
880
919
|
info: "#4D96FF",
|
|
881
920
|
muted: "#888888",
|
|
882
|
-
accent: "#C084FC"
|
|
921
|
+
accent: "#C084FC",
|
|
922
|
+
highlight: "#5B9BD5",
|
|
923
|
+
highlightBg: "#1E1E2E",
|
|
924
|
+
pointer: "#C084FC",
|
|
925
|
+
checked: "#6BCB77",
|
|
926
|
+
dimmed: "#555555"
|
|
883
927
|
};
|
|
884
928
|
var DEFAULT_ICONS = {
|
|
885
929
|
step: "\u25CF",
|
|
@@ -887,6 +931,25 @@ var DEFAULT_ICONS = {
|
|
|
887
931
|
stepPending: "\u25CB",
|
|
888
932
|
pointer: "\u203A"
|
|
889
933
|
};
|
|
934
|
+
var PROGRESS_BAR_CHARS = {
|
|
935
|
+
blocks: { filled: "\u2588", empty: "\u2591" },
|
|
936
|
+
line: { filled: "\u2500", empty: "\u2500" },
|
|
937
|
+
dots: { filled: "\u2022", empty: "\xB7" },
|
|
938
|
+
arrow: { filled: "\u2550", empty: "\u2500" }
|
|
939
|
+
};
|
|
940
|
+
function resolveProgressBar(config, tokens) {
|
|
941
|
+
const style = config?.style ?? "blocks";
|
|
942
|
+
const chars = PROGRESS_BAR_CHARS[style];
|
|
943
|
+
const filledHex = config?.filledColor ?? tokens?.success ?? DEFAULT_TOKENS.success;
|
|
944
|
+
const emptyHex = config?.emptyColor ?? tokens?.muted ?? DEFAULT_TOKENS.muted;
|
|
945
|
+
return {
|
|
946
|
+
width: config?.width ?? 20,
|
|
947
|
+
filledColor: chalk.hex(filledHex),
|
|
948
|
+
emptyColor: chalk.hex(emptyHex),
|
|
949
|
+
style,
|
|
950
|
+
chars
|
|
951
|
+
};
|
|
952
|
+
}
|
|
890
953
|
function resolveTheme(themeConfig) {
|
|
891
954
|
const presetTokens = themeConfig?.preset ? THEME_PRESETS[themeConfig.preset] : void 0;
|
|
892
955
|
const tokens = { ...DEFAULT_TOKENS, ...presetTokens, ...themeConfig?.tokens };
|
|
@@ -899,9 +962,16 @@ function resolveTheme(themeConfig) {
|
|
|
899
962
|
info: chalk.hex(tokens.info),
|
|
900
963
|
muted: chalk.hex(tokens.muted),
|
|
901
964
|
accent: chalk.hex(tokens.accent),
|
|
965
|
+
highlight: chalk.hex(tokens.highlight),
|
|
966
|
+
highlightBg: chalk.bgHex(tokens.highlightBg),
|
|
967
|
+
pointer: chalk.hex(tokens.pointer),
|
|
968
|
+
checked: chalk.hex(tokens.checked),
|
|
969
|
+
dimmed: chalk.hex(tokens.dimmed),
|
|
902
970
|
bold: chalk.bold,
|
|
903
971
|
icons,
|
|
904
|
-
spinner: resolveSpinner(themeConfig?.spinner)
|
|
972
|
+
spinner: resolveSpinner(themeConfig?.spinner),
|
|
973
|
+
spinnerElapsed: themeConfig?.spinnerElapsed ?? false,
|
|
974
|
+
progressBar: resolveProgressBar(themeConfig?.progressBar, tokens)
|
|
905
975
|
};
|
|
906
976
|
}
|
|
907
977
|
|
|
@@ -919,11 +989,11 @@ import {
|
|
|
919
989
|
} from "@inquirer/prompts";
|
|
920
990
|
var InquirerRenderer = class {
|
|
921
991
|
renderStepHeader(stepIndex, totalVisible, message, theme, description) {
|
|
922
|
-
const
|
|
923
|
-
const filledCount = totalVisible > 0 ? Math.round(stepIndex / totalVisible *
|
|
924
|
-
const remainingCount =
|
|
925
|
-
const filledBar =
|
|
926
|
-
const remainingBar =
|
|
992
|
+
const pb = theme.progressBar;
|
|
993
|
+
const filledCount = totalVisible > 0 ? Math.round(stepIndex / totalVisible * pb.width) : 0;
|
|
994
|
+
const remainingCount = pb.width - filledCount;
|
|
995
|
+
const filledBar = pb.filledColor(pb.chars.filled.repeat(filledCount));
|
|
996
|
+
const remainingBar = pb.emptyColor(pb.chars.empty.repeat(remainingCount));
|
|
927
997
|
const counter = theme.muted(`Step ${String(stepIndex + 1)}/${String(totalVisible)}`);
|
|
928
998
|
const stepMessage = theme.muted(`\u2014 ${message}`);
|
|
929
999
|
console.log(`
|
|
@@ -932,6 +1002,39 @@ var InquirerRenderer = class {
|
|
|
932
1002
|
console.log(` ${theme.muted(description)}`);
|
|
933
1003
|
}
|
|
934
1004
|
}
|
|
1005
|
+
renderNote(step, theme) {
|
|
1006
|
+
const title = step.message;
|
|
1007
|
+
const body = step.description ?? "";
|
|
1008
|
+
const style = step.style ?? "info";
|
|
1009
|
+
const { colorFn, icon } = getNoteStyleConfig(style, theme);
|
|
1010
|
+
if (style === "banner") {
|
|
1011
|
+
const width = Math.max(4, (process.stdout.columns ?? 60) - 4);
|
|
1012
|
+
const rule = colorFn("\u2500".repeat(width));
|
|
1013
|
+
console.log();
|
|
1014
|
+
console.log(` ${rule}`);
|
|
1015
|
+
console.log(` ${colorFn(icon)} ${theme.bold(title)}`);
|
|
1016
|
+
if (body) console.log(` ${theme.muted(body)}`);
|
|
1017
|
+
console.log(` ${rule}`);
|
|
1018
|
+
console.log();
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
const border = colorFn("\u2502");
|
|
1022
|
+
const topRule = colorFn(`\u250C${"\u2500".repeat(2)}`);
|
|
1023
|
+
const bottomRule = colorFn(`\u2514${"\u2500".repeat(2)}`);
|
|
1024
|
+
console.log();
|
|
1025
|
+
console.log(` ${topRule} ${colorFn(icon)} ${theme.bold(title)}`);
|
|
1026
|
+
if (body) {
|
|
1027
|
+
for (const line of body.split("\n")) {
|
|
1028
|
+
if (style === "code") {
|
|
1029
|
+
console.log(` ${border} ${theme.muted(line)}`);
|
|
1030
|
+
} else {
|
|
1031
|
+
console.log(` ${border} ${line}`);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
console.log(` ${bottomRule}`);
|
|
1036
|
+
console.log();
|
|
1037
|
+
}
|
|
935
1038
|
async renderText(step, state, theme) {
|
|
936
1039
|
const existingAnswer = state.answers[step.id];
|
|
937
1040
|
const defaultValue = typeof existingAnswer === "string" ? existingAnswer : step.default;
|
|
@@ -944,7 +1047,7 @@ var InquirerRenderer = class {
|
|
|
944
1047
|
async renderSelect(step, state, theme) {
|
|
945
1048
|
const existingAnswer = state.answers[step.id];
|
|
946
1049
|
const defaultValue = typeof existingAnswer === "string" ? existingAnswer : step.default;
|
|
947
|
-
const
|
|
1050
|
+
const rawChoices = step.options.map((opt) => {
|
|
948
1051
|
if ("separator" in opt) {
|
|
949
1052
|
return new Separator(opt.separator);
|
|
950
1053
|
}
|
|
@@ -955,19 +1058,27 @@ var InquirerRenderer = class {
|
|
|
955
1058
|
disabled: opt.disabled
|
|
956
1059
|
};
|
|
957
1060
|
});
|
|
1061
|
+
const itemChoices = rawChoices.filter((c) => !(c instanceof Separator));
|
|
1062
|
+
const columnedItems = step.columns ? applyColumns(itemChoices, step.columns) : itemChoices;
|
|
1063
|
+
let itemIdx = 0;
|
|
1064
|
+
const choices = rawChoices.map((c) => c instanceof Separator ? c : columnedItems[itemIdx++] ?? c);
|
|
958
1065
|
return select({
|
|
959
1066
|
message: step.message,
|
|
960
1067
|
choices,
|
|
961
1068
|
default: defaultValue,
|
|
962
1069
|
pageSize: step.pageSize,
|
|
963
1070
|
loop: step.loop,
|
|
964
|
-
theme: {
|
|
1071
|
+
theme: {
|
|
1072
|
+
prefix: { idle: theme.icons.pointer, done: theme.icons.stepDone },
|
|
1073
|
+
icon: { cursor: theme.icons.pointer },
|
|
1074
|
+
style: { disabled: theme.dimmed }
|
|
1075
|
+
}
|
|
965
1076
|
});
|
|
966
1077
|
}
|
|
967
1078
|
async renderMultiSelect(step, state, theme) {
|
|
968
1079
|
const existingAnswer = state.answers[step.id];
|
|
969
1080
|
const previousSelections = Array.isArray(existingAnswer) ? existingAnswer.filter((v) => typeof v === "string") : step.default;
|
|
970
|
-
const
|
|
1081
|
+
const rawChoices = step.options.map((opt) => {
|
|
971
1082
|
if ("separator" in opt) {
|
|
972
1083
|
return new Separator(opt.separator);
|
|
973
1084
|
}
|
|
@@ -978,12 +1089,20 @@ var InquirerRenderer = class {
|
|
|
978
1089
|
disabled: opt.disabled
|
|
979
1090
|
};
|
|
980
1091
|
});
|
|
1092
|
+
const itemChoices = rawChoices.filter((c) => !(c instanceof Separator));
|
|
1093
|
+
const columnedItems = step.columns ? applyColumns(itemChoices, step.columns) : itemChoices;
|
|
1094
|
+
let itemIdx = 0;
|
|
1095
|
+
const choices = rawChoices.map((c) => c instanceof Separator ? c : columnedItems[itemIdx++] ?? c);
|
|
981
1096
|
return checkbox({
|
|
982
1097
|
message: step.message,
|
|
983
1098
|
choices,
|
|
984
1099
|
pageSize: step.pageSize,
|
|
985
1100
|
loop: step.loop,
|
|
986
|
-
theme: {
|
|
1101
|
+
theme: {
|
|
1102
|
+
prefix: { idle: theme.icons.pointer, done: theme.icons.stepDone },
|
|
1103
|
+
icon: { cursor: theme.icons.pointer, checked: theme.checked("\u2714"), unchecked: theme.muted("\u25CB") },
|
|
1104
|
+
style: { disabledChoice: theme.dimmed }
|
|
1105
|
+
}
|
|
987
1106
|
});
|
|
988
1107
|
}
|
|
989
1108
|
async renderConfirm(step, state, theme) {
|
|
@@ -1015,15 +1134,17 @@ var InquirerRenderer = class {
|
|
|
1015
1134
|
return result ?? defaultValue ?? 0;
|
|
1016
1135
|
}
|
|
1017
1136
|
async renderSearch(step, _state, theme) {
|
|
1137
|
+
const message = step.placeholder ? `${step.message} ${theme.muted(`(${step.placeholder})`)}` : step.message;
|
|
1018
1138
|
return search({
|
|
1019
|
-
message
|
|
1139
|
+
message,
|
|
1020
1140
|
source: (input4) => {
|
|
1021
1141
|
const term = (input4 ?? "").toLowerCase();
|
|
1022
|
-
|
|
1142
|
+
const filtered = step.options.filter((opt) => "value" in opt).filter((opt) => !opt.disabled && opt.label.toLowerCase().includes(term)).map((opt) => ({
|
|
1023
1143
|
name: opt.label,
|
|
1024
1144
|
value: opt.value,
|
|
1025
1145
|
description: opt.hint
|
|
1026
1146
|
}));
|
|
1147
|
+
return step.columns ? applyColumns(filtered, step.columns) : filtered;
|
|
1027
1148
|
},
|
|
1028
1149
|
pageSize: step.pageSize,
|
|
1029
1150
|
theme: { prefix: { idle: theme.icons.pointer, done: theme.icons.stepDone } }
|
|
@@ -1091,6 +1212,42 @@ ${theme.muted("\u2500".repeat(40))}`);
|
|
|
1091
1212
|
process.stdout.write("\x1B[2J\x1B[0f");
|
|
1092
1213
|
}
|
|
1093
1214
|
};
|
|
1215
|
+
var NOTE_STYLE_MAP = {
|
|
1216
|
+
info: { tokenKey: "info", icon: "\u2139" },
|
|
1217
|
+
warning: { tokenKey: "warning", icon: "\u26A0" },
|
|
1218
|
+
error: { tokenKey: "error", icon: "\u2716" },
|
|
1219
|
+
success: { tokenKey: "success", icon: "\u2714" },
|
|
1220
|
+
code: { tokenKey: "muted", icon: "\u276F" },
|
|
1221
|
+
banner: { tokenKey: "primary", icon: "\u2605" }
|
|
1222
|
+
};
|
|
1223
|
+
function getNoteStyleConfig(style, theme) {
|
|
1224
|
+
const entry = NOTE_STYLE_MAP[style];
|
|
1225
|
+
return {
|
|
1226
|
+
colorFn: theme[entry.tokenKey],
|
|
1227
|
+
icon: entry.icon
|
|
1228
|
+
};
|
|
1229
|
+
}
|
|
1230
|
+
function applyColumns(items, columns) {
|
|
1231
|
+
const cols = Math.max(1, Math.floor(columns));
|
|
1232
|
+
if (cols <= 1) return items;
|
|
1233
|
+
if (items.length === 0) return items;
|
|
1234
|
+
const maxLabel = Math.max(...items.map((i) => i.name.length));
|
|
1235
|
+
const colWidth = maxLabel + 4;
|
|
1236
|
+
const grouped = [];
|
|
1237
|
+
for (let i = 0; i < items.length; i += cols) {
|
|
1238
|
+
grouped.push(items.slice(i, i + cols));
|
|
1239
|
+
}
|
|
1240
|
+
const result = [];
|
|
1241
|
+
for (const row of grouped) {
|
|
1242
|
+
for (let c = 0; c < row.length; c++) {
|
|
1243
|
+
const item = row[c];
|
|
1244
|
+
const isLast = c === row.length - 1;
|
|
1245
|
+
const paddedName = isLast ? item.name : item.name.padEnd(colWidth);
|
|
1246
|
+
result.push({ ...item, name: paddedName });
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
return result;
|
|
1250
|
+
}
|
|
1094
1251
|
|
|
1095
1252
|
// src/resolve.ts
|
|
1096
1253
|
function resolveEnvDefault(value) {
|
|
@@ -1145,23 +1302,71 @@ function renderBanner(name, theme, options) {
|
|
|
1145
1302
|
const icon = options?.icon;
|
|
1146
1303
|
const prefix = icon ? `${icon} ` : "";
|
|
1147
1304
|
if (options?.plain) {
|
|
1148
|
-
|
|
1305
|
+
const line = ` ${prefix}${theme.bold(name)}`;
|
|
1306
|
+
return options?.subtitle ? `${line}
|
|
1307
|
+
${theme.muted(options.subtitle)}` : line;
|
|
1308
|
+
}
|
|
1309
|
+
if (options?.banner) {
|
|
1310
|
+
const bannerStr = (typeof options.banner === "function" ? options.banner(theme) : options.banner).replace(/\n$/, "");
|
|
1311
|
+
const raw = bannerStr.split("\n");
|
|
1312
|
+
const mid = Math.floor(raw.length / 2);
|
|
1313
|
+
const bannerLines = raw.map((l, i) => {
|
|
1314
|
+
if (icon && i === mid) return ` ${icon} ${l}`;
|
|
1315
|
+
return icon ? ` ${l}` : ` ${l}`;
|
|
1316
|
+
}).join("\n");
|
|
1317
|
+
const colored = typeof options.banner === "function" ? bannerLines : GRIMOIRE_GRADIENT(bannerLines);
|
|
1318
|
+
return options?.subtitle ? `${colored}
|
|
1319
|
+
${theme.muted(options.subtitle)}` : colored;
|
|
1149
1320
|
}
|
|
1150
1321
|
try {
|
|
1151
|
-
const
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1322
|
+
const requestedFont = options?.font ?? "Small";
|
|
1323
|
+
let art;
|
|
1324
|
+
try {
|
|
1325
|
+
art = figlet.textSync(name, { font: requestedFont, horizontalLayout: "default" });
|
|
1326
|
+
} catch {
|
|
1327
|
+
art = figlet.textSync(name, { font: "Small", horizontalLayout: "default" });
|
|
1328
|
+
}
|
|
1155
1329
|
const artLines = art.split("\n");
|
|
1156
1330
|
const lines = artLines.map((line, i) => {
|
|
1157
|
-
|
|
1158
|
-
|
|
1331
|
+
const iconStr = getIconForLine(icon, options?.iconSize, i, artLines.length);
|
|
1332
|
+
if (iconStr !== void 0) {
|
|
1333
|
+
return ` ${iconStr}${line}`;
|
|
1159
1334
|
}
|
|
1160
1335
|
return icon ? ` ${line}` : ` ${line}`;
|
|
1161
1336
|
}).join("\n");
|
|
1162
|
-
|
|
1337
|
+
const colored = GRIMOIRE_GRADIENT(lines);
|
|
1338
|
+
return options?.subtitle ? `${colored}
|
|
1339
|
+
${theme.muted(options.subtitle)}` : colored;
|
|
1163
1340
|
} catch {
|
|
1164
|
-
|
|
1341
|
+
const line = ` ${prefix}${theme.bold(name)}`;
|
|
1342
|
+
return options?.subtitle ? `${line}
|
|
1343
|
+
${theme.muted(options.subtitle)}` : line;
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
function getIconForLine(icon, iconSize, lineIndex, totalLines) {
|
|
1347
|
+
if (!icon) return void 0;
|
|
1348
|
+
const mid = Math.floor(totalLines / 2);
|
|
1349
|
+
const span = resolveIconSpan(iconSize, totalLines);
|
|
1350
|
+
const halfSpan = Math.floor(span / 2);
|
|
1351
|
+
const start = Math.max(0, mid - halfSpan);
|
|
1352
|
+
const end = Math.min(totalLines - 1, start + span - 1);
|
|
1353
|
+
if (lineIndex >= start && lineIndex <= end) {
|
|
1354
|
+
return lineIndex === mid ? `${icon} ` : ` `;
|
|
1355
|
+
}
|
|
1356
|
+
return void 0;
|
|
1357
|
+
}
|
|
1358
|
+
function resolveIconSpan(iconSize, totalLines) {
|
|
1359
|
+
if (typeof iconSize === "number") {
|
|
1360
|
+
const clamped = Math.max(1, Math.floor(isFinite(iconSize) ? iconSize : 1));
|
|
1361
|
+
return Math.min(clamped, totalLines);
|
|
1362
|
+
}
|
|
1363
|
+
switch (iconSize ?? "small") {
|
|
1364
|
+
case "small":
|
|
1365
|
+
return 1;
|
|
1366
|
+
case "medium":
|
|
1367
|
+
return 3;
|
|
1368
|
+
case "large":
|
|
1369
|
+
return totalLines;
|
|
1165
1370
|
}
|
|
1166
1371
|
}
|
|
1167
1372
|
|
|
@@ -1252,7 +1457,7 @@ function clearCache(wizardName, customDir) {
|
|
|
1252
1457
|
}
|
|
1253
1458
|
|
|
1254
1459
|
// src/progress.ts
|
|
1255
|
-
import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
1460
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, readdirSync as readdirSync2, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
1256
1461
|
import { join as join2 } from "path";
|
|
1257
1462
|
import { homedir as homedir2 } from "os";
|
|
1258
1463
|
var DEFAULT_PROGRESS_DIR = join2(homedir2(), ".config", "grimoire", "progress");
|
|
@@ -1302,6 +1507,20 @@ function clearProgress(wizardName, customDir) {
|
|
|
1302
1507
|
} catch {
|
|
1303
1508
|
}
|
|
1304
1509
|
}
|
|
1510
|
+
function clearAllProgress(customDir) {
|
|
1511
|
+
try {
|
|
1512
|
+
const dir = customDir ?? DEFAULT_PROGRESS_DIR;
|
|
1513
|
+
for (const file of readdirSync2(dir)) {
|
|
1514
|
+
if (file.endsWith(".json")) {
|
|
1515
|
+
try {
|
|
1516
|
+
unlinkSync2(join2(dir, file));
|
|
1517
|
+
} catch {
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
} catch {
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1305
1524
|
|
|
1306
1525
|
// src/mru.ts
|
|
1307
1526
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync } from "fs";
|
|
@@ -1391,7 +1610,14 @@ function emitEvent(renderer, event, theme) {
|
|
|
1391
1610
|
renderer.onEvent(event, theme);
|
|
1392
1611
|
}
|
|
1393
1612
|
}
|
|
1394
|
-
function runPreFlightChecks(checks, theme, renderer) {
|
|
1613
|
+
function runPreFlightChecks(checks, theme, renderer, checksStyle) {
|
|
1614
|
+
if (checksStyle === "tasklist") {
|
|
1615
|
+
runPreFlightChecksTasklist(checks, theme, renderer);
|
|
1616
|
+
} else {
|
|
1617
|
+
runPreFlightChecksSpinner(checks, theme, renderer);
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
function runPreFlightChecksSpinner(checks, theme, renderer) {
|
|
1395
1621
|
if (renderer) emitEvent(renderer, { type: "checks:start", checks }, theme);
|
|
1396
1622
|
for (const check of checks) {
|
|
1397
1623
|
if (renderer) emitEvent(renderer, { type: "spinner:start", message: check.name }, theme);
|
|
@@ -1409,20 +1635,69 @@ function runPreFlightChecks(checks, theme, renderer) {
|
|
|
1409
1635
|
}
|
|
1410
1636
|
console.log();
|
|
1411
1637
|
}
|
|
1638
|
+
function runPreFlightChecksTasklist(checks, theme, renderer) {
|
|
1639
|
+
if (!process.stdout.isTTY) {
|
|
1640
|
+
runPreFlightChecksSpinner(checks, theme, renderer);
|
|
1641
|
+
return;
|
|
1642
|
+
}
|
|
1643
|
+
if (renderer) emitEvent(renderer, { type: "checks:start", checks, mode: "tasklist" }, theme);
|
|
1644
|
+
const statuses = checks.map(() => "pending");
|
|
1645
|
+
const icons = theme.icons;
|
|
1646
|
+
function renderTasklist() {
|
|
1647
|
+
process.stdout.write(`\x1B[${checks.length}A\x1B[0G`);
|
|
1648
|
+
for (let i = 0; i < checks.length; i++) {
|
|
1649
|
+
const check = checks[i];
|
|
1650
|
+
const s = statuses[i];
|
|
1651
|
+
let icon;
|
|
1652
|
+
if (s === "pass") icon = theme.success(icons.stepDone);
|
|
1653
|
+
else if (s === "fail") icon = theme.error("\u2717");
|
|
1654
|
+
else if (s === "running") icon = theme.info("\u280B");
|
|
1655
|
+
else icon = theme.muted(icons.stepPending);
|
|
1656
|
+
process.stdout.write(` ${icon} ${check.name}\x1B[K
|
|
1657
|
+
`);
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
for (let i = 0; i < checks.length; i++) {
|
|
1661
|
+
process.stdout.write(` ${theme.muted(icons.stepPending)} ${checks[i].name}
|
|
1662
|
+
`);
|
|
1663
|
+
}
|
|
1664
|
+
let failedCheck;
|
|
1665
|
+
for (let i = 0; i < checks.length; i++) {
|
|
1666
|
+
const check = checks[i];
|
|
1667
|
+
statuses[i] = "running";
|
|
1668
|
+
renderTasklist();
|
|
1669
|
+
try {
|
|
1670
|
+
execSync(check.run, { stdio: "pipe" });
|
|
1671
|
+
statuses[i] = "pass";
|
|
1672
|
+
renderTasklist();
|
|
1673
|
+
if (renderer) emitEvent(renderer, { type: "check:pass", name: check.name }, theme);
|
|
1674
|
+
} catch {
|
|
1675
|
+
statuses[i] = "fail";
|
|
1676
|
+
renderTasklist();
|
|
1677
|
+
failedCheck = check;
|
|
1678
|
+
if (renderer) emitEvent(renderer, { type: "check:fail", name: check.name, message: check.message }, theme);
|
|
1679
|
+
break;
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
renderTasklist();
|
|
1683
|
+
console.log();
|
|
1684
|
+
if (failedCheck) {
|
|
1685
|
+
throw new Error(`Pre-flight check failed: ${failedCheck.name} \u2014 ${failedCheck.message}`);
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
var MOCK_MISS = /* @__PURE__ */ Symbol("mock-miss");
|
|
1412
1689
|
function getMockValue(step, mockAnswers) {
|
|
1413
1690
|
if (step.id in mockAnswers) {
|
|
1414
1691
|
return mockAnswers[step.id];
|
|
1415
1692
|
}
|
|
1416
|
-
if (step.type === "message" || step.type === "note") {
|
|
1693
|
+
if (step.type === "message" || step.type === "note" || step.type === "browser") {
|
|
1417
1694
|
return true;
|
|
1418
1695
|
}
|
|
1419
1696
|
const defaultValue = getStepDefault(step);
|
|
1420
1697
|
if (defaultValue !== void 0) {
|
|
1421
1698
|
return defaultValue;
|
|
1422
1699
|
}
|
|
1423
|
-
|
|
1424
|
-
`Mock mode: no answer provided for step "${step.id}" and no default available`
|
|
1425
|
-
);
|
|
1700
|
+
return MOCK_MISS;
|
|
1426
1701
|
}
|
|
1427
1702
|
function getStepDefault(step) {
|
|
1428
1703
|
switch (step.type) {
|
|
@@ -1442,6 +1717,7 @@ function getStepDefault(step) {
|
|
|
1442
1717
|
case "password":
|
|
1443
1718
|
case "message":
|
|
1444
1719
|
case "note":
|
|
1720
|
+
case "browser":
|
|
1445
1721
|
return void 0;
|
|
1446
1722
|
}
|
|
1447
1723
|
}
|
|
@@ -1455,7 +1731,7 @@ async function runWizard(config, options) {
|
|
|
1455
1731
|
const cacheDir = typeof options?.cache === "object" ? options.cache.dir : void 0;
|
|
1456
1732
|
const mruEnabled = !isMock && options?.mru !== false;
|
|
1457
1733
|
let state = createWizardState(config);
|
|
1458
|
-
const resumeEnabled = !isMock && options?.resume !== false;
|
|
1734
|
+
const resumeEnabled = !isMock && options?.resume !== false && options?.cache !== false;
|
|
1459
1735
|
if (resumeEnabled) {
|
|
1460
1736
|
const saved = loadProgress(config.meta.name);
|
|
1461
1737
|
if (saved) {
|
|
@@ -1477,9 +1753,36 @@ async function runWizard(config, options) {
|
|
|
1477
1753
|
registerPlugin(plugin);
|
|
1478
1754
|
}
|
|
1479
1755
|
}
|
|
1756
|
+
let cancelFired = false;
|
|
1757
|
+
const performCancel = async () => {
|
|
1758
|
+
if (cancelFired) return;
|
|
1759
|
+
cancelFired = true;
|
|
1760
|
+
state = wizardReducer(state, { type: "CANCEL" }, config);
|
|
1761
|
+
if (resumeEnabled) {
|
|
1762
|
+
const passwordStepIds = config.steps.filter((s) => s.type === "password").map((s) => s.id);
|
|
1763
|
+
saveProgress(config.meta.name, {
|
|
1764
|
+
currentStepId: state.currentStepId,
|
|
1765
|
+
answers: state.answers,
|
|
1766
|
+
history: state.history
|
|
1767
|
+
}, void 0, passwordStepIds);
|
|
1768
|
+
}
|
|
1769
|
+
try {
|
|
1770
|
+
await options?.onCancel?.(state);
|
|
1771
|
+
} catch {
|
|
1772
|
+
}
|
|
1773
|
+
emitEvent(renderer, { type: "session:end", answers: state.answers, cancelled: true }, theme);
|
|
1774
|
+
if (!quiet) console.log(theme.warning("\n Wizard cancelled.\n"));
|
|
1775
|
+
};
|
|
1776
|
+
const signalHandler = !isMock ? () => {
|
|
1777
|
+
performCancel().finally(() => process.exit(130));
|
|
1778
|
+
} : void 0;
|
|
1779
|
+
if (signalHandler) {
|
|
1780
|
+
process.once("SIGINT", signalHandler);
|
|
1781
|
+
process.once("SIGTERM", signalHandler);
|
|
1782
|
+
}
|
|
1480
1783
|
try {
|
|
1481
1784
|
if (!isMock && config.checks && config.checks.length > 0) {
|
|
1482
|
-
runPreFlightChecks(config.checks, theme, renderer);
|
|
1785
|
+
runPreFlightChecks(config.checks, theme, renderer, config.meta.checksStyle);
|
|
1483
1786
|
}
|
|
1484
1787
|
if (!quiet) {
|
|
1485
1788
|
printWizardHeader(config, theme, options?.plain);
|
|
@@ -1489,7 +1792,62 @@ async function runWizard(config, options) {
|
|
|
1489
1792
|
let needsReview = true;
|
|
1490
1793
|
while (needsReview) {
|
|
1491
1794
|
let previousGroup;
|
|
1795
|
+
let stepsCompleted = 0;
|
|
1492
1796
|
while (state.status === "running") {
|
|
1797
|
+
if (!isMock && config.meta.clearBetweenSteps && stepsCompleted > 0) {
|
|
1798
|
+
renderer.clear();
|
|
1799
|
+
if (!quiet) {
|
|
1800
|
+
printWizardHeader(config, theme, options?.plain);
|
|
1801
|
+
}
|
|
1802
|
+
previousGroup = void 0;
|
|
1803
|
+
}
|
|
1804
|
+
let nextStepOverride;
|
|
1805
|
+
const createHookContext = (stateOverride) => ({
|
|
1806
|
+
answers: { ...(stateOverride ?? state).answers },
|
|
1807
|
+
state: stateOverride ?? state,
|
|
1808
|
+
showNote: (title, body) => {
|
|
1809
|
+
emitEvent(renderer, { type: "note", title, body }, theme);
|
|
1810
|
+
if (!("onEvent" in renderer) && process.stdout.isTTY) {
|
|
1811
|
+
console.log(`
|
|
1812
|
+
${theme.bold(title)}`);
|
|
1813
|
+
if (body) console.log(` ${theme.muted(body)}`);
|
|
1814
|
+
console.log();
|
|
1815
|
+
}
|
|
1816
|
+
},
|
|
1817
|
+
setNextStep: (stepId) => {
|
|
1818
|
+
nextStepOverride = stepId;
|
|
1819
|
+
},
|
|
1820
|
+
openBrowser: async (url) => {
|
|
1821
|
+
if (isMock) return;
|
|
1822
|
+
try {
|
|
1823
|
+
new URL(url);
|
|
1824
|
+
} catch {
|
|
1825
|
+
return;
|
|
1826
|
+
}
|
|
1827
|
+
if (process.platform === "darwin") {
|
|
1828
|
+
execFileSync("open", [url], { stdio: "ignore" });
|
|
1829
|
+
} else if (process.platform === "win32") {
|
|
1830
|
+
execFileSync("powershell", ["-NoProfile", "-Command", `Start-Process '${url.replace(/'/g, "''")}'`], { stdio: "ignore" });
|
|
1831
|
+
} else {
|
|
1832
|
+
execFileSync("xdg-open", [url], { stdio: "ignore" });
|
|
1833
|
+
}
|
|
1834
|
+
},
|
|
1835
|
+
prompt: async (promptConfig) => {
|
|
1836
|
+
if (isMock) {
|
|
1837
|
+
if (promptConfig.default !== void 0) return promptConfig.default;
|
|
1838
|
+
throw new Error("Mock mode: context.prompt() requires a default value");
|
|
1839
|
+
}
|
|
1840
|
+
const contextState = stateOverride ?? state;
|
|
1841
|
+
const tempStep = {
|
|
1842
|
+
id: "__hook_prompt__",
|
|
1843
|
+
message: promptConfig.message,
|
|
1844
|
+
type: promptConfig.type,
|
|
1845
|
+
...promptConfig.type === "select" ? { options: promptConfig.options } : {},
|
|
1846
|
+
default: promptConfig.default
|
|
1847
|
+
};
|
|
1848
|
+
return renderStep(renderer, tempStep, contextState, theme);
|
|
1849
|
+
}
|
|
1850
|
+
});
|
|
1493
1851
|
const visibleSteps = getVisibleSteps(config, state.answers);
|
|
1494
1852
|
const currentStep = config.steps.find((s) => s.id === state.currentStepId);
|
|
1495
1853
|
if (!currentStep) {
|
|
@@ -1512,9 +1870,13 @@ async function runWizard(config, options) {
|
|
|
1512
1870
|
emitEvent(renderer, { type: "step:start", stepId: currentStep.id, stepIndex, totalVisible: visibleSteps.length, step: currentStep }, theme);
|
|
1513
1871
|
if (currentStep.type === "note") {
|
|
1514
1872
|
emitEvent(renderer, { type: "note", title: resolvedMessage, body: resolvedDescription ?? "" }, theme);
|
|
1873
|
+
if (!isMock && "style" in currentStep && currentStep.style && "renderNote" in renderer && !("onEvent" in renderer)) {
|
|
1874
|
+
const resolvedStep2 = { ...currentStep, message: resolvedMessage, description: resolvedDescription };
|
|
1875
|
+
renderer.renderNote(resolvedStep2, theme);
|
|
1876
|
+
}
|
|
1515
1877
|
}
|
|
1516
1878
|
if (options?.onBeforeStep) {
|
|
1517
|
-
await options.onBeforeStep(currentStep.id, currentStep,
|
|
1879
|
+
await options.onBeforeStep(currentStep.id, currentStep, createHookContext());
|
|
1518
1880
|
}
|
|
1519
1881
|
const pluginStep = getPluginStep(currentStep.type);
|
|
1520
1882
|
const resolvedStep = pluginStep ? currentStep : resolveStepDefaults(currentStep, cachedAnswers);
|
|
@@ -1531,7 +1893,19 @@ async function runWizard(config, options) {
|
|
|
1531
1893
|
}
|
|
1532
1894
|
}
|
|
1533
1895
|
try {
|
|
1534
|
-
|
|
1896
|
+
let value;
|
|
1897
|
+
if (isMock) {
|
|
1898
|
+
const mockResult = getMockValue(finalStep, mockAnswers);
|
|
1899
|
+
if (mockResult === MOCK_MISS) {
|
|
1900
|
+
throw new Error(
|
|
1901
|
+
`Mock mode: no answer provided for step "${finalStep.id}" and no default available`
|
|
1902
|
+
);
|
|
1903
|
+
} else {
|
|
1904
|
+
value = mockResult;
|
|
1905
|
+
}
|
|
1906
|
+
} else {
|
|
1907
|
+
value = pluginStep ? await pluginStep.render(toStepRecord(finalStep), state, theme) : await renderStep(renderer, finalStep, state, theme);
|
|
1908
|
+
}
|
|
1535
1909
|
if (pluginStep?.validate) {
|
|
1536
1910
|
const pluginError = pluginStep.validate(value, toStepRecord(templatedStep));
|
|
1537
1911
|
if (pluginError) {
|
|
@@ -1572,28 +1946,31 @@ async function runWizard(config, options) {
|
|
|
1572
1946
|
}
|
|
1573
1947
|
}
|
|
1574
1948
|
if (options?.onAfterStep) {
|
|
1575
|
-
await options.onAfterStep(currentStep.id, value, nextState);
|
|
1949
|
+
await options.onAfterStep(currentStep.id, value, createHookContext(nextState));
|
|
1950
|
+
}
|
|
1951
|
+
if (nextStepOverride) {
|
|
1952
|
+
if (nextStepOverride === "__done__") {
|
|
1953
|
+
state = { ...nextState, status: "done" };
|
|
1954
|
+
} else {
|
|
1955
|
+
const targetExists = config.steps.some((s) => s.id === nextStepOverride);
|
|
1956
|
+
if (!targetExists) {
|
|
1957
|
+
throw new Error(`setNextStep: step "${nextStepOverride}" does not exist`);
|
|
1958
|
+
}
|
|
1959
|
+
state = { ...nextState, status: "running", currentStepId: nextStepOverride };
|
|
1960
|
+
}
|
|
1961
|
+
nextStepOverride = void 0;
|
|
1962
|
+
} else {
|
|
1963
|
+
state = nextState;
|
|
1576
1964
|
}
|
|
1577
|
-
state = nextState;
|
|
1578
1965
|
emitEvent(renderer, { type: "step:complete", stepId: currentStep.id, value, step: currentStep }, theme);
|
|
1579
1966
|
if (mruEnabled && isSelectLikeStep(currentStep.type)) {
|
|
1580
1967
|
recordSelection(config.meta.name, currentStep.id, value);
|
|
1581
1968
|
}
|
|
1582
1969
|
options?.onStepComplete?.(currentStep.id, value, state);
|
|
1970
|
+
stepsCompleted++;
|
|
1583
1971
|
} catch (error) {
|
|
1584
1972
|
if (!isMock && isUserCancel(error)) {
|
|
1585
|
-
|
|
1586
|
-
options?.onCancel?.(state);
|
|
1587
|
-
const passwordStepIds = config.steps.filter((s) => s.type === "password").map((s) => s.id);
|
|
1588
|
-
saveProgress(config.meta.name, {
|
|
1589
|
-
currentStepId: state.currentStepId,
|
|
1590
|
-
answers: state.answers,
|
|
1591
|
-
history: state.history
|
|
1592
|
-
}, void 0, passwordStepIds);
|
|
1593
|
-
emitEvent(renderer, { type: "session:end", answers: state.answers, cancelled: true }, theme);
|
|
1594
|
-
if (!quiet) {
|
|
1595
|
-
console.log(theme.warning("\n Wizard cancelled.\n"));
|
|
1596
|
-
}
|
|
1973
|
+
await performCancel();
|
|
1597
1974
|
return state.answers;
|
|
1598
1975
|
}
|
|
1599
1976
|
throw error;
|
|
@@ -1602,10 +1979,14 @@ async function runWizard(config, options) {
|
|
|
1602
1979
|
if (config.meta.review && !isMock && state.status === "done") {
|
|
1603
1980
|
const reviewLines = [];
|
|
1604
1981
|
for (const step of config.steps) {
|
|
1982
|
+
if (step.review?.hide) continue;
|
|
1983
|
+
if (step.type === "note" || step.type === "message") continue;
|
|
1605
1984
|
const answer = state.answers[step.id];
|
|
1606
1985
|
if (answer === void 0) continue;
|
|
1607
|
-
const
|
|
1608
|
-
|
|
1986
|
+
const label = step.review?.label ?? step.id;
|
|
1987
|
+
const raw = step.type === "password" ? "****" : Array.isArray(answer) ? answer.map(String).join(", ") : String(answer);
|
|
1988
|
+
const display = formatReviewValue(raw, step.review?.format);
|
|
1989
|
+
reviewLines.push(`${label}: ${display}`);
|
|
1609
1990
|
}
|
|
1610
1991
|
emitEvent(renderer, { type: "note", title: "Review your answers", body: reviewLines.join("\n") }, theme);
|
|
1611
1992
|
console.log(`
|
|
@@ -1625,12 +2006,16 @@ async function runWizard(config, options) {
|
|
|
1625
2006
|
} else {
|
|
1626
2007
|
const { select: selectPrompt } = await import("@inquirer/prompts");
|
|
1627
2008
|
const stepsWithAnswers = config.steps.filter(
|
|
1628
|
-
(s) => state.answers[s.id] !== void 0 && s.type !== "note" && s.type !== "message"
|
|
2009
|
+
(s) => state.answers[s.id] !== void 0 && s.type !== "note" && s.type !== "message" && !s.review?.hide
|
|
1629
2010
|
);
|
|
2011
|
+
if (stepsWithAnswers.length === 0) {
|
|
2012
|
+
needsReview = false;
|
|
2013
|
+
break;
|
|
2014
|
+
}
|
|
1630
2015
|
const stepToRevisit = await selectPrompt({
|
|
1631
2016
|
message: "Which step would you like to change?",
|
|
1632
2017
|
choices: stepsWithAnswers.map((s) => ({
|
|
1633
|
-
name: `${s.id}: ${s.type === "password" ? "****" : String(state.answers[s.id] ?? "")}`,
|
|
2018
|
+
name: `${s.review?.label ?? s.id}: ${s.type === "password" ? "****" : String(state.answers[s.id] ?? "")}`,
|
|
1634
2019
|
value: s.id
|
|
1635
2020
|
}))
|
|
1636
2021
|
});
|
|
@@ -1673,6 +2058,10 @@ async function runWizard(config, options) {
|
|
|
1673
2058
|
}
|
|
1674
2059
|
return state.answers;
|
|
1675
2060
|
} finally {
|
|
2061
|
+
if (signalHandler) {
|
|
2062
|
+
process.removeListener("SIGINT", signalHandler);
|
|
2063
|
+
process.removeListener("SIGTERM", signalHandler);
|
|
2064
|
+
}
|
|
1676
2065
|
if (userPlugins) {
|
|
1677
2066
|
clearPlugins();
|
|
1678
2067
|
}
|
|
@@ -1712,6 +2101,18 @@ function renderStep(renderer, step, state, theme) {
|
|
|
1712
2101
|
return Promise.resolve(true);
|
|
1713
2102
|
case "note":
|
|
1714
2103
|
return Promise.resolve(true);
|
|
2104
|
+
case "browser": {
|
|
2105
|
+
const resolvedUrl = resolveTemplate(step.url, state.answers);
|
|
2106
|
+
console.log(`
|
|
2107
|
+
${theme.info("\u2192")} ${step.message}`);
|
|
2108
|
+
console.log(` ${theme.muted(resolvedUrl)}`);
|
|
2109
|
+
openUrl(resolvedUrl);
|
|
2110
|
+
if (step.fallback) {
|
|
2111
|
+
console.log(` ${theme.muted(step.fallback)}`);
|
|
2112
|
+
}
|
|
2113
|
+
console.log();
|
|
2114
|
+
return Promise.resolve(true);
|
|
2115
|
+
}
|
|
1715
2116
|
}
|
|
1716
2117
|
}
|
|
1717
2118
|
function resolveStepDefaults(step, cachedAnswers) {
|
|
@@ -1763,6 +2164,7 @@ function resolveStepDefaults(step, cachedAnswers) {
|
|
|
1763
2164
|
case "password":
|
|
1764
2165
|
case "message":
|
|
1765
2166
|
case "note":
|
|
2167
|
+
case "browser":
|
|
1766
2168
|
return step;
|
|
1767
2169
|
}
|
|
1768
2170
|
}
|
|
@@ -1772,7 +2174,7 @@ function getCachedDefault(stepId, cachedAnswers) {
|
|
|
1772
2174
|
}
|
|
1773
2175
|
function applyTemplateDefaults(step, templateAnswers) {
|
|
1774
2176
|
if (!(step.id in templateAnswers)) return step;
|
|
1775
|
-
if (step.type === "password" || step.type === "message" || step.type === "note") return step;
|
|
2177
|
+
if (step.type === "password" || step.type === "message" || step.type === "note" || step.type === "browser") return step;
|
|
1776
2178
|
const value = templateAnswers[step.id];
|
|
1777
2179
|
switch (step.type) {
|
|
1778
2180
|
case "text":
|
|
@@ -1862,6 +2264,7 @@ function resolveStepTemplates(step, answers) {
|
|
|
1862
2264
|
case "toggle":
|
|
1863
2265
|
case "message":
|
|
1864
2266
|
case "note":
|
|
2267
|
+
case "browser":
|
|
1865
2268
|
return {
|
|
1866
2269
|
...step,
|
|
1867
2270
|
description: step.description ? resolveTemplate(step.description, answers) : void 0
|
|
@@ -1919,18 +2322,50 @@ async function executeActions(actions, answers, theme, renderer) {
|
|
|
1919
2322
|
}
|
|
1920
2323
|
function printWizardHeader(config, theme, plain) {
|
|
1921
2324
|
console.log();
|
|
1922
|
-
console.log(renderBanner(config.meta.name, theme, {
|
|
2325
|
+
console.log(renderBanner(config.meta.name, theme, {
|
|
2326
|
+
plain,
|
|
2327
|
+
icon: config.meta.icon,
|
|
2328
|
+
iconSize: config.meta.iconSize,
|
|
2329
|
+
font: config.meta.font,
|
|
2330
|
+
banner: config.meta.banner,
|
|
2331
|
+
subtitle: config.meta.subtitle
|
|
2332
|
+
}));
|
|
1923
2333
|
if (config.meta.description) {
|
|
1924
2334
|
console.log(` ${theme.muted(config.meta.description)}`);
|
|
1925
2335
|
}
|
|
1926
2336
|
console.log();
|
|
1927
2337
|
}
|
|
2338
|
+
function openUrl(url) {
|
|
2339
|
+
try {
|
|
2340
|
+
if (process.platform === "darwin") {
|
|
2341
|
+
execFileSync("open", [url], { stdio: "ignore" });
|
|
2342
|
+
} else if (process.platform === "win32") {
|
|
2343
|
+
const safeUrl = url.replace(/'/g, "''");
|
|
2344
|
+
execFileSync("powershell", ["-NoProfile", "-Command", `Start-Process '${safeUrl}'`], { stdio: "ignore" });
|
|
2345
|
+
} else {
|
|
2346
|
+
execFileSync("xdg-open", [url], { stdio: "ignore" });
|
|
2347
|
+
}
|
|
2348
|
+
} catch {
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
1928
2351
|
function isUserCancel(error) {
|
|
1929
2352
|
if (error instanceof Error) {
|
|
1930
2353
|
return error.message.includes("User force closed") || error.name === "ExitPromptError";
|
|
1931
2354
|
}
|
|
1932
2355
|
return false;
|
|
1933
2356
|
}
|
|
2357
|
+
function formatReviewValue(value, format) {
|
|
2358
|
+
switch (format) {
|
|
2359
|
+
case "uppercase":
|
|
2360
|
+
return value.toUpperCase();
|
|
2361
|
+
case "lowercase":
|
|
2362
|
+
return value.toLowerCase();
|
|
2363
|
+
case "capitalize":
|
|
2364
|
+
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
2365
|
+
default:
|
|
2366
|
+
return value;
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
1934
2369
|
|
|
1935
2370
|
// src/scaffolder.ts
|
|
1936
2371
|
import { input as input2, select as select2, confirm as confirm2, number as number2 } from "@inquirer/prompts";
|
|
@@ -2243,7 +2678,7 @@ complete -c grimoire -n "__fish_seen_subcommand_from completion" -f -a "fish" -d
|
|
|
2243
2678
|
}
|
|
2244
2679
|
|
|
2245
2680
|
// src/templates.ts
|
|
2246
|
-
import { mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync5, unlinkSync as unlinkSync3, readdirSync as
|
|
2681
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync5, unlinkSync as unlinkSync3, readdirSync as readdirSync3, existsSync as existsSync2 } from "fs";
|
|
2247
2682
|
import { join as join4 } from "path";
|
|
2248
2683
|
import { homedir as homedir4 } from "os";
|
|
2249
2684
|
var TEMPLATES_DIR = join4(homedir4(), ".config", "grimoire", "templates");
|
|
@@ -2270,7 +2705,7 @@ function listTemplates(wizardName) {
|
|
|
2270
2705
|
try {
|
|
2271
2706
|
const dir = getWizardTemplateDir(wizardName);
|
|
2272
2707
|
if (!existsSync2(dir)) return [];
|
|
2273
|
-
return
|
|
2708
|
+
return readdirSync3(dir).filter((f) => f.endsWith(".json")).map((f) => f.replace(/\.json$/, ""));
|
|
2274
2709
|
} catch {
|
|
2275
2710
|
return [];
|
|
2276
2711
|
}
|
|
@@ -2400,8 +2835,9 @@ var InkRenderer = class {
|
|
|
2400
2835
|
return result ?? defaultValue ?? 0;
|
|
2401
2836
|
}
|
|
2402
2837
|
async renderSearch(step, _state, theme) {
|
|
2838
|
+
const message = step.placeholder ? `${step.message} ${theme.muted(`(${step.placeholder})`)}` : step.message;
|
|
2403
2839
|
return search2({
|
|
2404
|
-
message
|
|
2840
|
+
message,
|
|
2405
2841
|
source: (term) => {
|
|
2406
2842
|
const query = (term ?? "").toLowerCase();
|
|
2407
2843
|
return step.options.filter((opt) => "value" in opt).filter((opt) => !opt.disabled && opt.label.toLowerCase().includes(query)).map((opt) => ({
|
|
@@ -2505,6 +2941,8 @@ var S_BAR_H = u("\u2500", "-");
|
|
|
2505
2941
|
var ClackRenderer = class extends InquirerRenderer {
|
|
2506
2942
|
spinnerInterval;
|
|
2507
2943
|
spinnerFrameIndex = 0;
|
|
2944
|
+
spinnerStartTime = 0;
|
|
2945
|
+
checksMode = void 0;
|
|
2508
2946
|
renderStepHeader() {
|
|
2509
2947
|
}
|
|
2510
2948
|
renderGroupHeader() {
|
|
@@ -2561,18 +2999,25 @@ var ClackRenderer = class extends InquirerRenderer {
|
|
|
2561
2999
|
this.stopSpinner(event.message, theme);
|
|
2562
3000
|
break;
|
|
2563
3001
|
case "checks:start":
|
|
2564
|
-
|
|
3002
|
+
this.checksMode = event.mode === "tasklist" ? "tasklist" : "spinner";
|
|
3003
|
+
if (this.checksMode !== "tasklist") {
|
|
3004
|
+
process.stdout.write(`${chalk2.gray(S_BAR)}
|
|
2565
3005
|
`);
|
|
2566
|
-
|
|
3006
|
+
process.stdout.write(`${chalk2.gray(S_BAR)} ${theme.bold("Running checks...")}
|
|
2567
3007
|
`);
|
|
3008
|
+
}
|
|
2568
3009
|
break;
|
|
2569
3010
|
case "check:pass":
|
|
2570
|
-
|
|
3011
|
+
if (this.checksMode !== "tasklist") {
|
|
3012
|
+
process.stdout.write(`${chalk2.gray(S_BAR)} ${theme.success(S_STEP_SUBMIT)} ${event.name}
|
|
2571
3013
|
`);
|
|
3014
|
+
}
|
|
2572
3015
|
break;
|
|
2573
3016
|
case "check:fail":
|
|
2574
|
-
|
|
3017
|
+
if (this.checksMode !== "tasklist") {
|
|
3018
|
+
process.stdout.write(`${chalk2.gray(S_BAR)} ${theme.error(S_STEP_ERROR)} ${event.name}: ${event.message}
|
|
2575
3019
|
`);
|
|
3020
|
+
}
|
|
2576
3021
|
break;
|
|
2577
3022
|
case "actions:start":
|
|
2578
3023
|
process.stdout.write(`${chalk2.gray(S_BAR)}
|
|
@@ -2639,11 +3084,17 @@ var ClackRenderer = class extends InquirerRenderer {
|
|
|
2639
3084
|
`);
|
|
2640
3085
|
}
|
|
2641
3086
|
startSpinner(message, theme) {
|
|
3087
|
+
if (this.spinnerInterval) {
|
|
3088
|
+
clearInterval(this.spinnerInterval);
|
|
3089
|
+
this.spinnerInterval = void 0;
|
|
3090
|
+
}
|
|
2642
3091
|
this.spinnerFrameIndex = 0;
|
|
3092
|
+
this.spinnerStartTime = Date.now();
|
|
2643
3093
|
const { frames, interval } = theme.spinner;
|
|
2644
3094
|
this.spinnerInterval = setInterval(() => {
|
|
2645
3095
|
const frame = frames[this.spinnerFrameIndex % frames.length];
|
|
2646
|
-
|
|
3096
|
+
const elapsed = theme.spinnerElapsed ? ` ${chalk2.gray(`(${((Date.now() - this.spinnerStartTime) / 1e3).toFixed(1)}s)`)}` : "";
|
|
3097
|
+
process.stdout.write(`\r${chalk2.gray(S_BAR)} ${chalk2.cyan(frame ?? "")} ${message}${elapsed}`);
|
|
2647
3098
|
this.spinnerFrameIndex++;
|
|
2648
3099
|
}, interval);
|
|
2649
3100
|
}
|
|
@@ -2653,7 +3104,7 @@ var ClackRenderer = class extends InquirerRenderer {
|
|
|
2653
3104
|
this.spinnerInterval = void 0;
|
|
2654
3105
|
}
|
|
2655
3106
|
const finalMessage = message ?? "Done";
|
|
2656
|
-
process.stdout.write(`\r${chalk2.gray(S_BAR)} ${theme.success(S_STEP_SUBMIT)} ${finalMessage}
|
|
3107
|
+
process.stdout.write(`\r${chalk2.gray(S_BAR)} ${theme.success(S_STEP_SUBMIT)} ${finalMessage}\x1B[K
|
|
2657
3108
|
`);
|
|
2658
3109
|
}
|
|
2659
3110
|
};
|
|
@@ -2821,11 +3272,13 @@ var cacheCommand = program.command("cache").description("Manage cached wizard an
|
|
|
2821
3272
|
cacheCommand.command("clear").description("Delete cached wizard answers").argument("[name]", "Wizard name to clear (clears all if omitted)").action((name) => {
|
|
2822
3273
|
clearCache(name);
|
|
2823
3274
|
if (name) {
|
|
3275
|
+
clearProgress(name);
|
|
2824
3276
|
console.log(`
|
|
2825
|
-
Cache cleared for "${name}".
|
|
3277
|
+
Cache and progress cleared for "${name}".
|
|
2826
3278
|
`);
|
|
2827
3279
|
} else {
|
|
2828
|
-
|
|
3280
|
+
clearAllProgress();
|
|
3281
|
+
console.log("\n All cached answers and progress cleared.\n");
|
|
2829
3282
|
}
|
|
2830
3283
|
});
|
|
2831
3284
|
var templateCommand = program.command("template").description("Manage saved wizard answer templates");
|