@emeryld/manager 0.6.6 → 0.7.0
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/dist/helper-cli/prompts.js +131 -13
- package/dist/helper-cli.js +5 -3
- package/dist/menu/script-helpers.js +12 -14
- package/dist/menu.js +41 -58
- package/package.json +1 -1
|
@@ -53,9 +53,16 @@ async function promptWithReadline(entries, title) {
|
|
|
53
53
|
rl.close();
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
-
function formatInteractiveLines(state, selectedIndex, title) {
|
|
56
|
+
function formatInteractiveLines(state, selectedIndex, title, searchState) {
|
|
57
57
|
const heading = pageHeading(title, state.page, state.pageCount);
|
|
58
58
|
const lines = [heading];
|
|
59
|
+
if (searchState?.active) {
|
|
60
|
+
const queryLabel = searchState.query || '(type to filter)';
|
|
61
|
+
const statusLabel = searchState.hasResults
|
|
62
|
+
? '(enter to run top match)'
|
|
63
|
+
: '(no matches yet)';
|
|
64
|
+
lines.push(colors.dim(`Search: ${queryLabel} ${statusLabel}`));
|
|
65
|
+
}
|
|
59
66
|
state.options.forEach((option, index) => {
|
|
60
67
|
const isSelected = index === selectedIndex;
|
|
61
68
|
const pointer = isSelected ? `${colors.green('➤')} ` : '';
|
|
@@ -69,7 +76,12 @@ function formatInteractiveLines(state, selectedIndex, title) {
|
|
|
69
76
|
: isSelected
|
|
70
77
|
? colors.green(option.entry.displayName)
|
|
71
78
|
: option.entry.displayName;
|
|
72
|
-
|
|
79
|
+
const runHint = searchState?.active &&
|
|
80
|
+
searchState.hasResults &&
|
|
81
|
+
index === 0
|
|
82
|
+
? colors.dim(' (enter to run)')
|
|
83
|
+
: '';
|
|
84
|
+
lines.push(`${pointer}${numberLabel}. ${option.entry.emoji} ${label} ${colors.dim(option.entry.metaLabel)}${runHint}`);
|
|
73
85
|
return;
|
|
74
86
|
}
|
|
75
87
|
const icon = option.action === 'back'
|
|
@@ -85,8 +97,17 @@ function formatInteractiveLines(state, selectedIndex, title) {
|
|
|
85
97
|
const navLabel = option.enabled ? baseLabel : colors.dim(baseLabel);
|
|
86
98
|
lines.push(`${pointer}${numberLabel}. ${icon} ${navLabel}`);
|
|
87
99
|
});
|
|
100
|
+
if (searchState?.active && !searchState.hasResults) {
|
|
101
|
+
lines.push(colors.yellow('No scripts match your search.'));
|
|
102
|
+
}
|
|
88
103
|
lines.push('');
|
|
89
|
-
lines.push(colors.dim(`Use ↑/↓ (or j/k) to move, 1-${PAGE_SIZE} to run, ${PREVIOUS_KEY} prev page (when shown), ${NEXT_KEY} next page (when shown), ${BACK_KEY} back, Enter to
|
|
104
|
+
lines.push(colors.dim(`Use ↑/↓ (or j/k) to move, 1-${PAGE_SIZE} to run, ${PREVIOUS_KEY} prev page (when shown), ${NEXT_KEY} next page (when shown), ${BACK_KEY} back, Enter to run, Space to filter, Esc/Ctrl+C to exit.`));
|
|
105
|
+
if (searchState?.active) {
|
|
106
|
+
lines.push(colors.dim('Search mode: type to filter, Backspace edits the query, Esc returns to navigation, Enter runs the highlighted entry.'));
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
lines.push(colors.dim('Press Space to start typing a filter.'));
|
|
110
|
+
}
|
|
90
111
|
return lines;
|
|
91
112
|
}
|
|
92
113
|
function renderInteractiveList(lines, previousLineCount) {
|
|
@@ -109,9 +130,15 @@ export async function promptForScript(entries, title) {
|
|
|
109
130
|
}
|
|
110
131
|
process.stdout.write('\x1b[?25l');
|
|
111
132
|
return new Promise((resolve) => {
|
|
133
|
+
const baseEntries = entries;
|
|
134
|
+
let filteredEntries = baseEntries;
|
|
112
135
|
let selectedIndex = 0;
|
|
113
136
|
let renderedLines = 0;
|
|
114
|
-
let state = buildVisibleOptions(
|
|
137
|
+
let state = buildVisibleOptions(filteredEntries, 0);
|
|
138
|
+
let searchActive = false;
|
|
139
|
+
let searchQuery = '';
|
|
140
|
+
let pageBeforeSearch = 0;
|
|
141
|
+
let selectionBeforeSearch = 0;
|
|
115
142
|
const cleanup = () => {
|
|
116
143
|
if (renderedLines > 0) {
|
|
117
144
|
process.stdout.write(`\x1b[${renderedLines}A`);
|
|
@@ -135,10 +162,24 @@ export async function promptForScript(entries, title) {
|
|
|
135
162
|
console.log();
|
|
136
163
|
resolve(undefined);
|
|
137
164
|
};
|
|
138
|
-
const
|
|
139
|
-
state = buildVisibleOptions(
|
|
165
|
+
const rebuildState = (page) => {
|
|
166
|
+
state = buildVisibleOptions(filteredEntries, page);
|
|
140
167
|
selectedIndex = Math.min(selectedIndex, state.options.length - 1);
|
|
141
168
|
selectedIndex = Math.max(0, selectedIndex);
|
|
169
|
+
};
|
|
170
|
+
const render = () => {
|
|
171
|
+
const searchState = searchActive
|
|
172
|
+
? {
|
|
173
|
+
active: true,
|
|
174
|
+
query: searchQuery.trim(),
|
|
175
|
+
hasResults: filteredEntries.length > 0,
|
|
176
|
+
}
|
|
177
|
+
: undefined;
|
|
178
|
+
const lines = formatInteractiveLines(state, selectedIndex, title, searchState);
|
|
179
|
+
renderedLines = renderInteractiveList(lines, renderedLines);
|
|
180
|
+
};
|
|
181
|
+
const setPage = (page) => {
|
|
182
|
+
rebuildState(page);
|
|
142
183
|
render();
|
|
143
184
|
};
|
|
144
185
|
const handleNav = (option) => {
|
|
@@ -167,9 +208,47 @@ export async function promptForScript(entries, title) {
|
|
|
167
208
|
}
|
|
168
209
|
handleNav(option);
|
|
169
210
|
};
|
|
170
|
-
const
|
|
171
|
-
const
|
|
172
|
-
|
|
211
|
+
const matchesSearchTerm = (entry, term) => {
|
|
212
|
+
const haystack = [
|
|
213
|
+
entry.displayName,
|
|
214
|
+
entry.metaLabel,
|
|
215
|
+
entry.script ?? '',
|
|
216
|
+
entry.description ?? '',
|
|
217
|
+
]
|
|
218
|
+
.join(' ')
|
|
219
|
+
.toLowerCase();
|
|
220
|
+
return haystack.includes(term);
|
|
221
|
+
};
|
|
222
|
+
const applySearch = () => {
|
|
223
|
+
const normalized = searchQuery.trim().toLowerCase();
|
|
224
|
+
if (normalized.length > 0) {
|
|
225
|
+
filteredEntries = baseEntries.filter((entry) => matchesSearchTerm(entry, normalized));
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
filteredEntries = baseEntries;
|
|
229
|
+
}
|
|
230
|
+
rebuildState(0);
|
|
231
|
+
selectedIndex = 0;
|
|
232
|
+
render();
|
|
233
|
+
};
|
|
234
|
+
const enterSearchMode = () => {
|
|
235
|
+
if (searchActive)
|
|
236
|
+
return;
|
|
237
|
+
pageBeforeSearch = state.page;
|
|
238
|
+
selectionBeforeSearch = selectedIndex;
|
|
239
|
+
searchActive = true;
|
|
240
|
+
searchQuery = '';
|
|
241
|
+
applySearch();
|
|
242
|
+
};
|
|
243
|
+
const exitSearchMode = () => {
|
|
244
|
+
if (!searchActive)
|
|
245
|
+
return;
|
|
246
|
+
searchActive = false;
|
|
247
|
+
searchQuery = '';
|
|
248
|
+
filteredEntries = baseEntries;
|
|
249
|
+
state = buildVisibleOptions(filteredEntries, pageBeforeSearch);
|
|
250
|
+
selectedIndex = Math.min(selectionBeforeSearch, state.options.length - 1);
|
|
251
|
+
render();
|
|
173
252
|
};
|
|
174
253
|
const onData = (buffer) => {
|
|
175
254
|
const isArrowUp = buffer.equals(Buffer.from([0x1b, 0x5b, 0x41]));
|
|
@@ -177,10 +256,25 @@ export async function promptForScript(entries, title) {
|
|
|
177
256
|
const isCtrlC = buffer.length === 1 && buffer[0] === 0x03;
|
|
178
257
|
const isEnter = buffer.length === 1 && (buffer[0] === 0x0d || buffer[0] === 0x0a);
|
|
179
258
|
const isEscape = buffer.length === 1 && buffer[0] === 0x1b;
|
|
180
|
-
|
|
259
|
+
const isBackspace = buffer.length === 1 && (buffer[0] === 0x7f || buffer[0] === 0x08);
|
|
260
|
+
const isPrintable = buffer.length === 1 && buffer[0] >= 0x20 && buffer[0] <= 0x7e;
|
|
261
|
+
const isSpace = buffer.length === 1 && buffer[0] === 0x20;
|
|
262
|
+
if (isCtrlC) {
|
|
181
263
|
cleanup();
|
|
182
264
|
process.exit(1);
|
|
183
265
|
}
|
|
266
|
+
if (isEscape) {
|
|
267
|
+
if (searchActive) {
|
|
268
|
+
exitSearchMode();
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
cleanup();
|
|
272
|
+
process.exit(1);
|
|
273
|
+
}
|
|
274
|
+
if (!searchActive && isSpace) {
|
|
275
|
+
enterSearchMode();
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
184
278
|
if (isArrowUp ||
|
|
185
279
|
(buffer.length === 1 && (buffer[0] === 0x6b || buffer[0] === 0x4b))) {
|
|
186
280
|
selectedIndex =
|
|
@@ -195,7 +289,29 @@ export async function promptForScript(entries, title) {
|
|
|
195
289
|
return;
|
|
196
290
|
}
|
|
197
291
|
if (isEnter) {
|
|
198
|
-
|
|
292
|
+
const option = state.options[selectedIndex];
|
|
293
|
+
if (option)
|
|
294
|
+
activateOption(option);
|
|
295
|
+
else
|
|
296
|
+
process.stdout.write('\x07');
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
if (searchActive) {
|
|
300
|
+
if (isBackspace) {
|
|
301
|
+
if (searchQuery.length > 0) {
|
|
302
|
+
searchQuery = searchQuery.slice(0, -1);
|
|
303
|
+
applySearch();
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
//exitSearchMode()
|
|
307
|
+
}
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
if (isPrintable) {
|
|
311
|
+
searchQuery += String.fromCharCode(buffer[0]);
|
|
312
|
+
applySearch();
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
199
315
|
return;
|
|
200
316
|
}
|
|
201
317
|
if (buffer.length === 1 && buffer[0] >= 0x30 && buffer[0] <= 0x39) {
|
|
@@ -211,17 +327,19 @@ export async function promptForScript(entries, title) {
|
|
|
211
327
|
((buffer[0] >= 0x41 && buffer[0] <= 0x5a) ||
|
|
212
328
|
(buffer[0] >= 0x61 && buffer[0] <= 0x7a))) {
|
|
213
329
|
const char = String.fromCharCode(buffer[0]).toLowerCase();
|
|
214
|
-
const foundIndex =
|
|
330
|
+
const foundIndex = baseEntries.findIndex((entry) => entry.displayName.toLowerCase().startsWith(char));
|
|
215
331
|
if (foundIndex !== -1) {
|
|
216
332
|
const page = Math.floor(foundIndex / PAGE_SIZE);
|
|
217
|
-
state = buildVisibleOptions(
|
|
333
|
+
state = buildVisibleOptions(filteredEntries, page);
|
|
218
334
|
selectedIndex = foundIndex % PAGE_SIZE;
|
|
219
335
|
render();
|
|
220
336
|
}
|
|
221
337
|
else {
|
|
222
338
|
process.stdout.write('\x07');
|
|
223
339
|
}
|
|
340
|
+
return;
|
|
224
341
|
}
|
|
342
|
+
process.stdout.write('\x07');
|
|
225
343
|
};
|
|
226
344
|
input.on('data', onData);
|
|
227
345
|
render();
|
package/dist/helper-cli.js
CHANGED
|
@@ -14,7 +14,7 @@ export async function runHelperCli({ scripts, title = 'Helper CLI', argv = proce
|
|
|
14
14
|
const [firstArg, ...restArgs] = args;
|
|
15
15
|
if (firstArg === '--list' || firstArg === '-l') {
|
|
16
16
|
printScriptList(normalized, title);
|
|
17
|
-
return;
|
|
17
|
+
return false;
|
|
18
18
|
}
|
|
19
19
|
if (firstArg === '--help' || firstArg === '-h') {
|
|
20
20
|
console.log(colors.magenta('Usage:'));
|
|
@@ -22,19 +22,21 @@ export async function runHelperCli({ scripts, title = 'Helper CLI', argv = proce
|
|
|
22
22
|
console.log('\nFlags:');
|
|
23
23
|
console.log(' --list Show available scripts');
|
|
24
24
|
console.log(' --help Show this information');
|
|
25
|
-
return;
|
|
25
|
+
return false;
|
|
26
26
|
}
|
|
27
27
|
const argLooksLikeScript = firstArg && !firstArg.startsWith('-');
|
|
28
28
|
if (argLooksLikeScript) {
|
|
29
29
|
const requested = findScriptEntry(normalized, firstArg);
|
|
30
30
|
if (requested) {
|
|
31
31
|
await runEntry(requested, restArgs);
|
|
32
|
-
return;
|
|
32
|
+
return true;
|
|
33
33
|
}
|
|
34
34
|
console.log(colors.yellow(`Unknown script "${firstArg}". Falling back to interactive selection…`));
|
|
35
35
|
}
|
|
36
36
|
const selection = await promptForScript(normalized, title);
|
|
37
37
|
if (selection) {
|
|
38
38
|
await runEntry(selection, []);
|
|
39
|
+
return true;
|
|
39
40
|
}
|
|
41
|
+
return false;
|
|
40
42
|
}
|
|
@@ -191,26 +191,24 @@ export function makeBaseScriptEntries(pkg) {
|
|
|
191
191
|
const scripts = pkg.json?.scripts ?? {};
|
|
192
192
|
const dependencies = collectDependencies(pkg);
|
|
193
193
|
const marker = getPackageMarker(pkg, dependencies);
|
|
194
|
-
return BASE_SCRIPT_KEYS.
|
|
194
|
+
return BASE_SCRIPT_KEYS.reduce((entries, key) => {
|
|
195
195
|
const resolution = resolveBaseScript(pkg, key, scripts, dependencies, marker.kind);
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
196
|
+
const args = resolution.args;
|
|
197
|
+
if (!resolution.available || !args)
|
|
198
|
+
return entries;
|
|
199
|
+
const description = resolution.isDefault
|
|
200
|
+
? `${resolution.description} (default)`
|
|
201
|
+
: resolution.description;
|
|
202
|
+
entries.push({
|
|
202
203
|
name: key,
|
|
203
204
|
emoji: '⚡️',
|
|
204
205
|
description,
|
|
205
206
|
handler: async () => {
|
|
206
|
-
|
|
207
|
-
console.log(colors.yellow(`No ${key} command found for ${pkg.name}.`));
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
await run('pnpm', resolution.args, { cwd: pkg.path });
|
|
207
|
+
await run('pnpm', args, { cwd: pkg.path });
|
|
211
208
|
},
|
|
212
|
-
};
|
|
213
|
-
|
|
209
|
+
});
|
|
210
|
+
return entries;
|
|
211
|
+
}, []);
|
|
214
212
|
}
|
|
215
213
|
export function getPackageMarker(pkg, dependencies = collectDependencies(pkg)) {
|
|
216
214
|
const normalizedDeps = new Set([...dependencies].map((dep) => String(dep).toLowerCase().trim()));
|
package/dist/menu.js
CHANGED
|
@@ -14,8 +14,7 @@ function formatKindLabel(kind) {
|
|
|
14
14
|
return 'CLI';
|
|
15
15
|
return `${kind.charAt(0).toUpperCase()}${kind.slice(1)}`;
|
|
16
16
|
}
|
|
17
|
-
function makeManagerStepEntries(targets, packages, state
|
|
18
|
-
const includeBack = options?.includeBack ?? true;
|
|
17
|
+
function makeManagerStepEntries(targets, packages, state) {
|
|
19
18
|
return [
|
|
20
19
|
{
|
|
21
20
|
name: 'update dependencies',
|
|
@@ -103,18 +102,6 @@ function makeManagerStepEntries(targets, packages, state, options) {
|
|
|
103
102
|
state.lastStep = 'full';
|
|
104
103
|
},
|
|
105
104
|
},
|
|
106
|
-
...(includeBack
|
|
107
|
-
? [
|
|
108
|
-
{
|
|
109
|
-
name: 'back',
|
|
110
|
-
emoji: '↩️',
|
|
111
|
-
description: 'Pick packages again',
|
|
112
|
-
handler: async () => {
|
|
113
|
-
state.lastStep = 'back';
|
|
114
|
-
},
|
|
115
|
-
},
|
|
116
|
-
]
|
|
117
|
-
: []),
|
|
118
105
|
];
|
|
119
106
|
}
|
|
120
107
|
function makeDockerEntry(pkg) {
|
|
@@ -158,17 +145,37 @@ export function buildPackageSelectionMenu(packages, onStepComplete) {
|
|
|
158
145
|
const ordered = getOrderedPackages(packages);
|
|
159
146
|
const entries = ordered.map((pkg) => {
|
|
160
147
|
const marker = getPackageMarker(pkg);
|
|
161
|
-
const
|
|
162
|
-
const
|
|
163
|
-
const
|
|
148
|
+
const primaryName = pkg.name ?? pkg.substitute ?? pkg.dirName;
|
|
149
|
+
const packageColorizer = colors[pkg.color ?? 'cyan'] ?? colors.cyan;
|
|
150
|
+
const highlightedName = packageColorizer(primaryName);
|
|
151
|
+
const tagParts = [];
|
|
152
|
+
const addTag = (value, colorizer) => {
|
|
153
|
+
if (!value)
|
|
154
|
+
return;
|
|
155
|
+
tagParts.push(colorizer(`[${value}]`));
|
|
156
|
+
};
|
|
157
|
+
const relativePath = pkg.relativeDir ?? pkg.dirName;
|
|
158
|
+
if (relativePath && relativePath !== '.') {
|
|
159
|
+
addTag(relativePath, colors.gray);
|
|
160
|
+
}
|
|
161
|
+
if (pkg.version) {
|
|
162
|
+
addTag(`v${pkg.version}`, colors.yellow);
|
|
163
|
+
}
|
|
164
|
+
if (pkg.substitute && pkg.substitute !== primaryName) {
|
|
165
|
+
const aliasTag = pkg.name ? `alias:${pkg.substitute}` : pkg.substitute;
|
|
166
|
+
addTag(aliasTag, colors.magenta);
|
|
167
|
+
}
|
|
168
|
+
if (pkg.dockerfilePath) {
|
|
169
|
+
addTag('docker', colors.green);
|
|
170
|
+
}
|
|
164
171
|
const kindLabel = formatKindLabel(marker.kind);
|
|
165
|
-
|
|
166
|
-
const
|
|
172
|
+
addTag(kindLabel, marker.kindColorize);
|
|
173
|
+
const labelWord = marker.label ? ` ${marker.colorize(marker.label)}` : '';
|
|
174
|
+
const tags = tagParts.length ? ` ${tagParts.join(' ')}` : '';
|
|
167
175
|
return {
|
|
168
|
-
name:
|
|
169
|
-
emoji:
|
|
170
|
-
description:
|
|
171
|
-
color: pkgColor,
|
|
176
|
+
name: `${highlightedName}${tags}${labelWord}`,
|
|
177
|
+
emoji: '',
|
|
178
|
+
description: pkg.json?.description ?? pkg.relativeDir ?? pkg.dirName,
|
|
172
179
|
handler: async () => {
|
|
173
180
|
const step = await runStepLoop([pkg], packages);
|
|
174
181
|
onStepComplete?.(step);
|
|
@@ -179,7 +186,6 @@ export function buildPackageSelectionMenu(packages, onStepComplete) {
|
|
|
179
186
|
return entries;
|
|
180
187
|
entries.push({
|
|
181
188
|
name: 'All packages',
|
|
182
|
-
color: 'gray',
|
|
183
189
|
emoji: globalEmoji,
|
|
184
190
|
description: 'Select all packages',
|
|
185
191
|
handler: async () => {
|
|
@@ -207,19 +213,7 @@ export async function runStepLoop(targets, packages) {
|
|
|
207
213
|
emoji: globalEmoji,
|
|
208
214
|
description: 'update/test/build/publish',
|
|
209
215
|
handler: async () => {
|
|
210
|
-
const managerEntries =
|
|
211
|
-
...makeManagerStepEntries(targets, packages, state, {
|
|
212
|
-
includeBack: false,
|
|
213
|
-
}),
|
|
214
|
-
{
|
|
215
|
-
name: 'back',
|
|
216
|
-
emoji: '↩️',
|
|
217
|
-
description: 'Return to package menu',
|
|
218
|
-
handler: () => {
|
|
219
|
-
state.lastStep = undefined;
|
|
220
|
-
},
|
|
221
|
-
},
|
|
222
|
-
];
|
|
216
|
+
const managerEntries = makeManagerStepEntries(targets, packages, state);
|
|
223
217
|
await runHelperCli({
|
|
224
218
|
title: `Manager actions for ${pkg.name}`,
|
|
225
219
|
scripts: managerEntries,
|
|
@@ -236,38 +230,23 @@ export async function runStepLoop(targets, packages) {
|
|
|
236
230
|
console.log(colors.yellow(`No package.json scripts found for ${pkg.name}.`));
|
|
237
231
|
return;
|
|
238
232
|
}
|
|
239
|
-
const scriptsMenu = [
|
|
240
|
-
...scriptEntries,
|
|
241
|
-
{
|
|
242
|
-
name: 'back',
|
|
243
|
-
emoji: '↩️',
|
|
244
|
-
description: 'Return to package menu',
|
|
245
|
-
handler: () => { },
|
|
246
|
-
},
|
|
247
|
-
];
|
|
248
233
|
await runHelperCli({
|
|
249
234
|
title: `${pkg.name} scripts`,
|
|
250
|
-
scripts:
|
|
235
|
+
scripts: scriptEntries,
|
|
251
236
|
argv: [],
|
|
252
237
|
});
|
|
253
238
|
},
|
|
254
239
|
},
|
|
255
|
-
{
|
|
256
|
-
name: 'back',
|
|
257
|
-
emoji: '↩️',
|
|
258
|
-
description: 'Pick packages again',
|
|
259
|
-
handler: () => {
|
|
260
|
-
state.lastStep = 'back';
|
|
261
|
-
},
|
|
262
|
-
},
|
|
263
240
|
];
|
|
264
|
-
await runHelperCli({
|
|
241
|
+
const ranScript = await runHelperCli({
|
|
265
242
|
title: `Actions for ${pkg.name}`,
|
|
266
243
|
scripts: entries,
|
|
267
244
|
argv: [],
|
|
268
245
|
});
|
|
269
|
-
if (
|
|
246
|
+
if (!ranScript) {
|
|
247
|
+
state.lastStep = 'back';
|
|
270
248
|
return state.lastStep;
|
|
249
|
+
}
|
|
271
250
|
// loop to keep showing menu
|
|
272
251
|
}
|
|
273
252
|
}
|
|
@@ -279,11 +258,15 @@ export async function runStepLoop(targets, packages) {
|
|
|
279
258
|
while (true) {
|
|
280
259
|
state.lastStep = undefined;
|
|
281
260
|
const entries = makeManagerStepEntries(targets, packages, state);
|
|
282
|
-
await runHelperCli({
|
|
261
|
+
const ranScript = await runHelperCli({
|
|
283
262
|
title: `Actions for ${targets.length === 1 ? targets[0].name : 'selected packages'}`,
|
|
284
263
|
scripts: entries,
|
|
285
264
|
argv: [], // <- key change
|
|
286
265
|
});
|
|
266
|
+
if (!ranScript) {
|
|
267
|
+
state.lastStep = 'back';
|
|
268
|
+
return state.lastStep;
|
|
269
|
+
}
|
|
287
270
|
if (state.lastStep === 'back')
|
|
288
271
|
return state.lastStep;
|
|
289
272
|
// keep looping to show menu again
|