multi-agents-cli 1.1.70 → 1.1.72
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/ui.js +54 -34
- package/lib/validate.js +44 -1
- package/package.json +1 -1
package/lib/ui.js
CHANGED
|
@@ -121,59 +121,79 @@ const stepHeader = (stepIndex, totalSteps) => {
|
|
|
121
121
|
// ── Step selection helpers ────────────────────────────────────────────────────
|
|
122
122
|
|
|
123
123
|
const { BACK, RESTART } = require('./steps');
|
|
124
|
-
const { validateCombination,
|
|
124
|
+
const { validateCombination, resolveFramework } = require('./validate');
|
|
125
125
|
const { BLOCKS } = require('./data-config');
|
|
126
126
|
|
|
127
127
|
const NONE_LABEL = '→ None of these - I\'ll specify my own';
|
|
128
128
|
|
|
129
129
|
// ── Free-text handler (shared by selectRequired + selectOptional) ────────────
|
|
130
130
|
|
|
131
|
-
const handleFreeText = async (prompt, items, stepMachine, stepIndex, context, isOptional) => {
|
|
131
|
+
const handleFreeText = async (prompt, items, stepMachine, stepIndex, context, isOptional, block = null) => {
|
|
132
132
|
const _res = await prompts({ type: 'text', name: 'value', message: 'Type your own value:' }, { onCancel: () => process.exit(0) });
|
|
133
133
|
const typed = (_res.value || '').trim();
|
|
134
|
-
if (!typed) return isOptional ? null : handleFreeText(prompt, items, stepMachine, stepIndex, context, isOptional);
|
|
134
|
+
if (!typed) return isOptional ? null : handleFreeText(prompt, items, stepMachine, stepIndex, context, isOptional, block);
|
|
135
135
|
|
|
136
|
-
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
136
|
+
console.log(dim(' Verifying...'));
|
|
137
|
+
const resolved = await resolveFramework(typed, block);
|
|
138
|
+
|
|
139
|
+
if (resolved.status === 'fuzzy') {
|
|
140
|
+
const opts = [
|
|
141
|
+
...resolved.matches.map(m => `→ ${m.label || m}`),
|
|
141
142
|
`→ Type a different value`,
|
|
142
143
|
];
|
|
143
|
-
const choice = await arrowSelect(`"${typed}" couldn't be verified. Did you mean:`,
|
|
144
|
-
if (choice < matches.length) return matches[choice];
|
|
145
|
-
return handleFreeText(prompt, items, stepMachine, stepIndex, context, isOptional);
|
|
144
|
+
const choice = await arrowSelect(`"${typed}" couldn't be verified. Did you mean:`, opts.map(o => ({ label: o })));
|
|
145
|
+
if (choice < resolved.matches.length) return resolved.matches[choice];
|
|
146
|
+
return handleFreeText(prompt, items, stepMachine, stepIndex, context, isOptional, block);
|
|
146
147
|
}
|
|
147
148
|
|
|
148
|
-
|
|
149
|
-
console.log(dim(' Verifying...'));
|
|
150
|
-
const existence = await checkFrameworkExists(typed);
|
|
149
|
+
if (resolved.status === 'valid') return resolved.match;
|
|
151
150
|
|
|
152
|
-
if (
|
|
153
|
-
console.log(
|
|
154
|
-
|
|
155
|
-
|
|
151
|
+
if (resolved.status === 'partial_cross') {
|
|
152
|
+
console.log(`
|
|
153
|
+
${yellow('⚠')} ${resolved.message}
|
|
154
|
+
`);
|
|
155
|
+
const opts = [
|
|
156
|
+
...resolved.matches.map(m => `→ ${m.label || m}`),
|
|
156
157
|
`→ Type a different value`,
|
|
157
158
|
];
|
|
158
|
-
const choice = await arrowSelect(
|
|
159
|
-
if (choice
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
console.log(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
{ label: `→
|
|
170
|
-
{ label: `→ No - let me pick something else` },
|
|
159
|
+
const choice = await arrowSelect('What would you like to do?', opts.map(o => ({ label: o })));
|
|
160
|
+
if (choice < resolved.matches.length) return resolved.matches[choice];
|
|
161
|
+
return handleFreeText(prompt, items, stepMachine, stepIndex, context, isOptional, block);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (resolved.status === 'cross_block') {
|
|
165
|
+
console.log(`
|
|
166
|
+
${yellow('⚠')} ${resolved.message}
|
|
167
|
+
`);
|
|
168
|
+
const choice = await arrowSelect('How would you like to proceed?', [
|
|
169
|
+
{ label: `→ Continue - I understand the tradeoff` },
|
|
170
|
+
{ label: `→ Type a different value` },
|
|
171
171
|
]);
|
|
172
|
-
if (
|
|
172
|
+
if (choice === 1) return handleFreeText(prompt, items, stepMachine, stepIndex, context, isOptional, block);
|
|
173
173
|
return typed;
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
//
|
|
176
|
+
// unverified - risk block
|
|
177
|
+
console.log('');
|
|
178
|
+
const riskOpts = [
|
|
179
|
+
`→ Proceed with "${typed}" - I accept the risks`,
|
|
180
|
+
`→ Type a different value`,
|
|
181
|
+
];
|
|
182
|
+
const riskChoice = await arrowSelect(`"${typed}" couldn't be verified.`, riskOpts.map(o => ({ label: o })));
|
|
183
|
+
if (riskChoice === 1) return handleFreeText(prompt, items, stepMachine, stepIndex, context, isOptional, block);
|
|
184
|
+
console.log('');
|
|
185
|
+
console.log(` ${yellow('⚠ Proceeding with an unverified framework may result in:')}`);
|
|
186
|
+
console.log(dim(' · Agent spending excessive tokens resolving unknown setup'));
|
|
187
|
+
console.log(dim(' · Incorrect scaffold structure for this framework'));
|
|
188
|
+
console.log(dim(' · Missing contracts and integration points'));
|
|
189
|
+
console.log(dim(' · Potential mid-flow failures requiring full restart'));
|
|
190
|
+
console.log('');
|
|
191
|
+
const confirm = await arrowSelect('Confirm you understand and want to continue?', [
|
|
192
|
+
{ label: `→ Yes - proceed with "${typed}"` },
|
|
193
|
+
{ label: `→ No - let me pick something else` },
|
|
194
|
+
]);
|
|
195
|
+
if (confirm === 1) return handleFreeText(prompt, items, stepMachine, stepIndex, context, isOptional, block);
|
|
196
|
+
|
|
177
197
|
const check = validateCombination(typed, context);
|
|
178
198
|
if (!check.valid) {
|
|
179
199
|
console.log(`
|
|
@@ -186,7 +206,7 @@ const handleFreeText = async (prompt, items, stepMachine, stepIndex, context, is
|
|
|
186
206
|
];
|
|
187
207
|
const choice = await arrowSelect('How would you like to proceed?', opts.map(o => ({ label: o })));
|
|
188
208
|
if (choice === 0) return typed;
|
|
189
|
-
if (choice === opts.length - 1) return handleFreeText(prompt, items, stepMachine, stepIndex, context, isOptional);
|
|
209
|
+
if (choice === opts.length - 1) return handleFreeText(prompt, items, stepMachine, stepIndex, context, isOptional, block);
|
|
190
210
|
return check.alternatives[choice - 1];
|
|
191
211
|
}
|
|
192
212
|
|
package/lib/validate.js
CHANGED
|
@@ -127,4 +127,47 @@ const validateCombination = (chosen, context = {}) => {
|
|
|
127
127
|
return { valid: true, reason: null, alternatives: [] };
|
|
128
128
|
};
|
|
129
129
|
|
|
130
|
-
|
|
130
|
+
|
|
131
|
+
// ── resolveFramework ──────────────────────────────────────────────────────────
|
|
132
|
+
// Full lookup order per block — returns { status, match, message }
|
|
133
|
+
// status: 'match' | 'cross_block' | 'partial_cross' | 'unverified' | 'valid'
|
|
134
|
+
|
|
135
|
+
const resolveFramework = async (typed, block = null) => {
|
|
136
|
+
const lower = typed.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
137
|
+
|
|
138
|
+
const primaryPool = block === BLOCKS.CLIENT ? CLIENT_FRAMEWORKS
|
|
139
|
+
: block === BLOCKS.BACKEND ? BACKEND_FRAMEWORKS
|
|
140
|
+
: ALL_FRAMEWORKS;
|
|
141
|
+
const secondaryPool = block === BLOCKS.CLIENT ? BACKEND_FRAMEWORKS
|
|
142
|
+
: block === BLOCKS.BACKEND ? CLIENT_FRAMEWORKS
|
|
143
|
+
: [];
|
|
144
|
+
const crossLabel = block === BLOCKS.CLIENT ? 'backend' : 'client';
|
|
145
|
+
|
|
146
|
+
// 1. Fuzzy match in primary pool
|
|
147
|
+
const primaryFuzzy = primaryPool.filter(f => {
|
|
148
|
+
const n = f.value.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
149
|
+
return n.includes(lower) || lower.includes(n) || (lower.length >= 3 && n.startsWith(lower.slice(0, 3)));
|
|
150
|
+
}).slice(0, 3);
|
|
151
|
+
if (primaryFuzzy.length > 0) return { status: 'fuzzy', matches: primaryFuzzy, message: null };
|
|
152
|
+
|
|
153
|
+
// 2. Exact match in primary pool
|
|
154
|
+
const primaryExact = primaryPool.find(f => f.value.toLowerCase() === typed.toLowerCase());
|
|
155
|
+
if (primaryExact) return { status: 'valid', match: primaryExact, message: null };
|
|
156
|
+
|
|
157
|
+
// 3. Partial match in secondary pool
|
|
158
|
+
const secondaryFuzzy = secondaryPool.filter(f => {
|
|
159
|
+
const n = f.value.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
160
|
+
return n.includes(lower) || lower.includes(n) || (lower.length >= 3 && n.startsWith(lower.slice(0, 3)));
|
|
161
|
+
});
|
|
162
|
+
if (secondaryFuzzy.length > 0) return { status: 'partial_cross', matches: secondaryFuzzy, message: `No such ${block} framework found. Did you mean a ${crossLabel} framework?` };
|
|
163
|
+
|
|
164
|
+
// 4. Exact match in secondary pool
|
|
165
|
+
const secondaryExact = secondaryPool.find(f => f.value.toLowerCase() === typed.toLowerCase());
|
|
166
|
+
if (secondaryExact) return { status: 'cross_block', match: secondaryExact, message: `"${typed}" is a ${crossLabel} framework, not a ${block} framework.` };
|
|
167
|
+
|
|
168
|
+
// 5. Registry check
|
|
169
|
+
const existence = await checkFrameworkExists(typed);
|
|
170
|
+
return { status: 'unverified', match: null, message: existence.exists ? `"${typed}" was found on ${existence.registry} but could not be confirmed as a ${block || ''} framework.` : `"${typed}" couldn't be verified on any registry.` };
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
module.exports = { validateCombination, checkFrameworkExists, fuzzyMatchFramework, resolveFramework };
|
package/package.json
CHANGED