multi-agents-cli 1.1.69 → 1.1.70
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/lib/data-config.js +9 -0
- package/lib/questions-flow.js +9 -8
- package/lib/ui.js +16 -12
- package/lib/validate.js +8 -4
- package/package.json +1 -1
package/lib/data-config.js
CHANGED
|
@@ -250,7 +250,16 @@ const IDE_CANDIDATES = [
|
|
|
250
250
|
|
|
251
251
|
// ── Exports ───────────────────────────────────────────────────────────────────
|
|
252
252
|
|
|
253
|
+
// ── Block identifiers ────────────────────────────────────────────────────────
|
|
254
|
+
|
|
255
|
+
const BLOCKS = {
|
|
256
|
+
CLIENT: 'client',
|
|
257
|
+
BACKEND: 'backend',
|
|
258
|
+
SHARED: 'shared',
|
|
259
|
+
};
|
|
260
|
+
|
|
253
261
|
module.exports = {
|
|
262
|
+
BLOCKS,
|
|
254
263
|
FRAMEWORK_CONVENTIONS,
|
|
255
264
|
CLIENT_FRAMEWORKS,
|
|
256
265
|
BACKEND_FRAMEWORKS,
|
package/lib/questions-flow.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// ── Imports ───────────────────────────────────────────────────────────────────
|
|
4
4
|
|
|
5
5
|
const { dim, bold, blue, green, cyan, separator, arrowSelect, arrowConfirm, selectRequired, selectOptional, stepHeader } = require('./ui');
|
|
6
|
+
const { BLOCKS } = require('./data-config');
|
|
6
7
|
const { BACK, RESTART } = require('./steps');
|
|
7
8
|
const {
|
|
8
9
|
CLIENT_FRAMEWORKS, BACKEND_FRAMEWORKS, FRAMEWORK_VERSION_FALLBACK,
|
|
@@ -23,7 +24,7 @@ const buildStepDefs = (IDE_CANDIDATES) => [
|
|
|
23
24
|
run: async (machine, answers) => {
|
|
24
25
|
stepHeader(1, 12);
|
|
25
26
|
console.log(`\n${bold(blue('Client configuration'))}`);
|
|
26
|
-
return await selectRequired('* Client framework (required):', CLIENT_FRAMEWORKS, machine, 0, answers);
|
|
27
|
+
return await selectRequired('* Client framework (required):', CLIENT_FRAMEWORKS, machine, 0, answers, BLOCKS.CLIENT);
|
|
27
28
|
},
|
|
28
29
|
},
|
|
29
30
|
|
|
@@ -60,7 +61,7 @@ const buildStepDefs = (IDE_CANDIDATES) => [
|
|
|
60
61
|
run: async (machine, answers) => {
|
|
61
62
|
stepHeader(3, 12);
|
|
62
63
|
const opts = STATE_OPTIONS[answers.clientFw.value] || [];
|
|
63
|
-
return await selectOptional('State management:', opts, machine, 2, answers);
|
|
64
|
+
return await selectOptional('State management:', opts, machine, 2, answers, BLOCKS.CLIENT);
|
|
64
65
|
},
|
|
65
66
|
},
|
|
66
67
|
|
|
@@ -70,7 +71,7 @@ const buildStepDefs = (IDE_CANDIDATES) => [
|
|
|
70
71
|
run: async (machine, answers) => {
|
|
71
72
|
stepHeader(4, 12);
|
|
72
73
|
const opts = UI_OPTIONS[answers.clientFw.value] || [];
|
|
73
|
-
return await selectOptional('UI library:', opts, machine, 3, answers);
|
|
74
|
+
return await selectOptional('UI library:', opts, machine, 3, answers, BLOCKS.CLIENT);
|
|
74
75
|
},
|
|
75
76
|
},
|
|
76
77
|
|
|
@@ -79,7 +80,7 @@ const buildStepDefs = (IDE_CANDIDATES) => [
|
|
|
79
80
|
name: 'clientStyle',
|
|
80
81
|
run: async (machine, answers) => {
|
|
81
82
|
stepHeader(5, 12);
|
|
82
|
-
return await selectOptional('Styling:', STYLING_OPTIONS, machine, 4, answers);
|
|
83
|
+
return await selectOptional('Styling:', STYLING_OPTIONS, machine, 4, answers, BLOCKS.CLIENT);
|
|
83
84
|
},
|
|
84
85
|
},
|
|
85
86
|
|
|
@@ -157,7 +158,7 @@ const buildStepDefs = (IDE_CANDIDATES) => [
|
|
|
157
158
|
run: async (machine, answers) => {
|
|
158
159
|
stepHeader(9, 12);
|
|
159
160
|
if (!answers.backendFwObj) return null;
|
|
160
|
-
return await selectOptional('Database type:', DB_OPTIONS, machine, 8, answers);
|
|
161
|
+
return await selectOptional('Database type:', DB_OPTIONS, machine, 8, answers, BLOCKS.BACKEND);
|
|
161
162
|
},
|
|
162
163
|
},
|
|
163
164
|
|
|
@@ -171,7 +172,7 @@ const buildStepDefs = (IDE_CANDIDATES) => [
|
|
|
171
172
|
const byDb = ORM_OPTIONS_BY_DB[backendDb] || [];
|
|
172
173
|
const byFw = ORM_OPTIONS[backendFwObj.value] || [];
|
|
173
174
|
const ormChoices = byDb.length && byFw.length ? byDb.filter(o => byFw.includes(o)) : byDb.length ? byDb : byFw;
|
|
174
|
-
return await selectOptional('ORM / query layer:', ormChoices, machine, 9, answers);
|
|
175
|
+
return await selectOptional('ORM / query layer:', ormChoices, machine, 9, answers, BLOCKS.BACKEND);
|
|
175
176
|
},
|
|
176
177
|
},
|
|
177
178
|
|
|
@@ -182,7 +183,7 @@ const buildStepDefs = (IDE_CANDIDATES) => [
|
|
|
182
183
|
stepHeader(11, 12);
|
|
183
184
|
const { backendFwObj } = answers;
|
|
184
185
|
if (!backendFwObj) return null;
|
|
185
|
-
return await selectOptional('Auth strategy:', AUTH_OPTIONS[backendFwObj.value] || [], machine, 10, answers);
|
|
186
|
+
return await selectOptional('Auth strategy:', AUTH_OPTIONS[backendFwObj.value] || [], machine, 10, answers, BLOCKS.BACKEND);
|
|
186
187
|
},
|
|
187
188
|
},
|
|
188
189
|
|
|
@@ -208,7 +209,7 @@ const buildStepDefs = (IDE_CANDIDATES) => [
|
|
|
208
209
|
else console.log(`\n ${'\x1b[33m'}No IDEs detected.${'\x1b[0m'}\n`);
|
|
209
210
|
|
|
210
211
|
while (true) {
|
|
211
|
-
const ideChoice = await selectRequired('* IDE / editor (required):', sorted, machine, 11, answers);
|
|
212
|
+
const ideChoice = await selectRequired('* IDE / editor (required):', sorted, machine, 11, answers, null);
|
|
212
213
|
if (ideChoice === RESTART || ideChoice === BACK) return ideChoice;
|
|
213
214
|
|
|
214
215
|
if (ideChoice.cmd && !ideChoice.detected) {
|
package/lib/ui.js
CHANGED
|
@@ -122,6 +122,7 @@ const stepHeader = (stepIndex, totalSteps) => {
|
|
|
122
122
|
|
|
123
123
|
const { BACK, RESTART } = require('./steps');
|
|
124
124
|
const { validateCombination, checkFrameworkExists, fuzzyMatchFramework } = require('./validate');
|
|
125
|
+
const { BLOCKS } = require('./data-config');
|
|
125
126
|
|
|
126
127
|
const NONE_LABEL = '→ None of these - I\'ll specify my own';
|
|
127
128
|
|
|
@@ -133,7 +134,7 @@ const handleFreeText = async (prompt, items, stepMachine, stepIndex, context, is
|
|
|
133
134
|
if (!typed) return isOptional ? null : handleFreeText(prompt, items, stepMachine, stepIndex, context, isOptional);
|
|
134
135
|
|
|
135
136
|
// Fuzzy match first - before registry check
|
|
136
|
-
const matches = fuzzyMatchFramework(typed);
|
|
137
|
+
const matches = fuzzyMatchFramework(typed, block);
|
|
137
138
|
if (matches.length > 0) {
|
|
138
139
|
const didYouMeanOpts = [
|
|
139
140
|
...matches.map(m => `→ ${m.label || m}`),
|
|
@@ -193,41 +194,44 @@ const handleFreeText = async (prompt, items, stepMachine, stepIndex, context, is
|
|
|
193
194
|
return typed;
|
|
194
195
|
};
|
|
195
196
|
|
|
196
|
-
const selectRequired = async (prompt, items, stepMachine, stepIndex, context = {}) => {
|
|
197
|
+
const selectRequired = async (prompt, items, stepMachine, stepIndex, context = {}, block = null) => {
|
|
197
198
|
const isFirstStep = stepIndex <= 1;
|
|
198
199
|
const navOpts = stepMachine ? stepMachine.navOptions(stepIndex, isFirstStep) : [];
|
|
199
|
-
const
|
|
200
|
-
const allItems = [...items, noneOpt, ...navOpts];
|
|
200
|
+
const allItems = block !== null ? [...items, { label: NONE_LABEL }, ...navOpts] : [...items, ...navOpts];
|
|
201
201
|
|
|
202
202
|
const idx = await arrowSelect(prompt, allItems.map(i => ({ label: typeof i === 'string' ? i : i.label })));
|
|
203
203
|
|
|
204
|
-
if (idx === items.length) return handleFreeText(prompt, items, stepMachine, stepIndex, context, false);
|
|
204
|
+
if (block !== null && idx === items.length) return handleFreeText(prompt, items, stepMachine, stepIndex, context, false, block);
|
|
205
205
|
|
|
206
|
-
|
|
207
|
-
|
|
206
|
+
const navStart = block !== null ? items.length + 1 : items.length;
|
|
207
|
+
if (idx >= navStart) {
|
|
208
|
+
const nav = navOpts[idx - navStart];
|
|
208
209
|
return nav ? nav.value : RESTART;
|
|
209
210
|
}
|
|
210
211
|
return items[idx];
|
|
211
212
|
};
|
|
212
213
|
|
|
213
|
-
const selectOptional = async (prompt, items, stepMachine, stepIndex, context = {}) => {
|
|
214
|
+
const selectOptional = async (prompt, items, stepMachine, stepIndex, context = {}, block = null) => {
|
|
214
215
|
if (!items || items.length === 0) return null;
|
|
215
216
|
const isFirstStep = stepIndex <= 1;
|
|
216
217
|
const navOpts = stepMachine ? stepMachine.navOptions(stepIndex, isFirstStep) : [];
|
|
217
218
|
const choices = [
|
|
218
219
|
...items.map(i => ({ label: typeof i === 'string' ? i : i.label })),
|
|
219
220
|
{ label: dim('Skip (agent will propose when needed)') },
|
|
220
|
-
{ label: NONE_LABEL },
|
|
221
|
+
...(block !== null ? [{ label: NONE_LABEL }] : []),
|
|
221
222
|
...navOpts.map(n => ({ label: n.label })),
|
|
222
223
|
];
|
|
223
224
|
|
|
224
225
|
const idx = await arrowSelect(prompt, choices);
|
|
225
226
|
|
|
226
227
|
if (idx === items.length) return null; // skip
|
|
227
|
-
if (idx === items.length + 1) return handleFreeText(prompt, items, stepMachine, stepIndex, context, true);
|
|
228
228
|
|
|
229
|
-
|
|
230
|
-
|
|
229
|
+
const noneIdx = block !== null ? items.length + 1 : -1;
|
|
230
|
+
if (block !== null && idx === noneIdx) return handleFreeText(prompt, items, stepMachine, stepIndex, context, true, block);
|
|
231
|
+
|
|
232
|
+
const navStart = block !== null ? items.length + 2 : items.length + 1;
|
|
233
|
+
if (idx >= navStart) {
|
|
234
|
+
const nav = navOpts[idx - navStart];
|
|
231
235
|
return nav ? nav.value : RESTART;
|
|
232
236
|
}
|
|
233
237
|
return typeof items[idx] === 'string' ? items[idx] : items[idx].value;
|
package/lib/validate.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { FRAMEWORK_REGISTRY, CLIENT_FRAMEWORKS, BACKEND_FRAMEWORKS } = require('./data-config');
|
|
3
|
+
const { FRAMEWORK_REGISTRY, CLIENT_FRAMEWORKS, BACKEND_FRAMEWORKS, BLOCKS } = require('./data-config');
|
|
4
4
|
|
|
5
5
|
const ALL_FRAMEWORKS = [...CLIENT_FRAMEWORKS, ...BACKEND_FRAMEWORKS];
|
|
6
6
|
|
|
@@ -17,13 +17,17 @@ const MONGO_DBS = ['MongoDB'];
|
|
|
17
17
|
// ── fuzzyMatchFramework ───────────────────────────────────────────────────────
|
|
18
18
|
// Returns up to 3 known framework names that loosely match the typed value
|
|
19
19
|
|
|
20
|
-
const fuzzyMatchFramework = (typed) => {
|
|
20
|
+
const fuzzyMatchFramework = (typed, block = null) => {
|
|
21
|
+
const pool = block === BLOCKS.CLIENT ? CLIENT_FRAMEWORKS
|
|
22
|
+
: block === BLOCKS.BACKEND ? BACKEND_FRAMEWORKS
|
|
23
|
+
: ALL_FRAMEWORKS;
|
|
21
24
|
const lower = typed.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
22
25
|
const matchedNames = Object.keys(FRAMEWORK_REGISTRY).filter(name => {
|
|
23
26
|
const n = name.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
24
|
-
return
|
|
27
|
+
return pool.some(f => f.value === name) &&
|
|
28
|
+
(n.includes(lower) || lower.includes(n) || (lower.length >= 3 && n.startsWith(lower.slice(0, 3))));
|
|
25
29
|
}).slice(0, 3);
|
|
26
|
-
return matchedNames.map(name =>
|
|
30
|
+
return matchedNames.map(name => pool.find(f => f.value === name) || { label: name, value: name });
|
|
27
31
|
};
|
|
28
32
|
|
|
29
33
|
// ── checkFrameworkExists ──────────────────────────────────────────────────────
|
package/package.json
CHANGED