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 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, checkFrameworkExists, fuzzyMatchFramework } = require('./validate');
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
- // Fuzzy match first - before registry check
137
- const matches = fuzzyMatchFramework(typed, block);
138
- if (matches.length > 0) {
139
- const didYouMeanOpts = [
140
- ...matches.map(m => `→ ${m.label || m}`),
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:`, didYouMeanOpts.map(o => ({ label: o })));
144
- if (choice < matches.length) return matches[choice]; // returns full framework object
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
- // Live registry check - only when no fuzzy match
149
- console.log(dim(' Verifying...'));
150
- const existence = await checkFrameworkExists(typed);
149
+ if (resolved.status === 'valid') return resolved.match;
151
150
 
152
- if (!existence.exists) {
153
- console.log('');
154
- const riskOpts = [
155
- `→ Proceed with "${typed}" - I accept the risks`,
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(`"${typed}" couldn't be verified.`, riskOpts.map(o => ({ label: o })));
159
- if (choice === 1) return handleFreeText(prompt, items, stepMachine, stepIndex, context, isOptional);
160
- // Show risk consequence block
161
- console.log('');
162
- console.log(` ${yellow('⚠ Proceeding with an unverified framework may result in:')}`);
163
- console.log(dim(' · Agent spending excessive tokens resolving unknown setup'));
164
- console.log(dim(' · Incorrect scaffold structure for this framework'));
165
- console.log(dim(' · Missing contracts and integration points'));
166
- console.log(dim(' · Potential mid-flow failures requiring full restart'));
167
- console.log('');
168
- const confirm = await arrowSelect('Confirm you understand and want to continue?', [
169
- { label: `→ Yes - proceed with "${typed}"` },
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 (confirm === 1) return handleFreeText(prompt, items, stepMachine, stepIndex, context, isOptional);
172
+ if (choice === 1) return handleFreeText(prompt, items, stepMachine, stepIndex, context, isOptional, block);
173
173
  return typed;
174
174
  }
175
175
 
176
- // Valid + verified - check compatibility
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
- module.exports = { validateCombination, checkFrameworkExists, fuzzyMatchFramework };
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "multi-agents-cli",
3
- "version": "1.1.70",
3
+ "version": "1.1.72",
4
4
  "description": "Multi-agent workflow orchestration for Claude Code — isolated git worktrees, structured state tracking, autonomous task chaining",
5
5
  "keywords": [
6
6
  "claude-code",