bmad-method 6.0.0-Beta.5 → 6.0.0-Beta.7
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/.claude/skills/changelog-social/SKILL.md +178 -0
- package/.claude/skills/changelog-social/examples/discord-example.md +53 -0
- package/.claude/skills/changelog-social/examples/linkedin-example.md +49 -0
- package/.claude/skills/changelog-social/examples/twitter-example.md +55 -0
- package/.claude/skills/changelog-social.skill +0 -0
- package/.claude/skills/draft-changelog/SKILL.md +2 -2
- package/.claude/skills/draft-changelog/prompts/instructions.md +25 -1
- package/.claude/skills/gh-triage/SKILL.md +1 -1
- package/.claude/skills/release-module/SKILL.md +1 -1
- package/.claude/skills/release-module/prompts/instructions.md +0 -4
- package/.github/workflows/quality.yaml +3 -0
- package/.vscode/settings.json +1 -2
- package/CHANGELOG.md +65 -1
- package/docs/bmgd/game-types.md +1 -0
- package/docs/bmgd/index.md +1 -1
- package/docs/bmgd/quick-flow-workflows.md +1 -0
- package/docs/reference/agents.md +22 -0
- package/docs/reference/commands.md +34 -0
- package/docs/reference/testing.md +21 -0
- package/docs/reference/workflow-map.md +1 -1
- package/package.json +5 -2
- package/src/bmm/agents/analyst.agent.yaml +11 -5
- package/src/bmm/agents/dev.agent.yaml +0 -1
- package/src/bmm/agents/pm.agent.yaml +3 -6
- package/src/bmm/agents/{quinn.agent.yaml → qa.agent.yaml} +3 -3
- package/src/bmm/module-help.csv +13 -20
- package/src/bmm/workflows/1-analysis/create-product-brief/workflow.md +0 -1
- package/src/bmm/workflows/1-analysis/research/workflow-domain-research.md +54 -0
- package/src/bmm/workflows/1-analysis/research/workflow-market-research.md +54 -0
- package/src/bmm/workflows/1-analysis/research/workflow-technical-research.md +54 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/data/domain-complexity.csv +2 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-create-prd.md +63 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-edit-prd.md +65 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-validate-prd.md +65 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md +0 -1
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md +0 -1
- package/src/bmm/workflows/3-solutioning/create-architecture/data/domain-complexity.csv +2 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/workflow.md +0 -1
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md +0 -1
- package/src/bmm/workflows/4-implementation/code-review/workflow.yaml +0 -3
- package/src/bmm/workflows/4-implementation/correct-course/workflow.yaml +0 -4
- package/src/bmm/workflows/4-implementation/create-story/workflow.yaml +0 -4
- package/src/bmm/workflows/4-implementation/dev-story/workflow.yaml +0 -4
- package/src/bmm/workflows/4-implementation/retrospective/workflow.yaml +0 -3
- package/src/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +0 -4
- package/src/bmm/workflows/4-implementation/sprint-status/workflow.yaml +0 -6
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md +0 -1
- package/src/bmm/workflows/document-project/workflow.yaml +0 -8
- package/src/bmm/workflows/qa/automate/workflow.yaml +0 -2
- package/src/core/tasks/editorial-review-prose.xml +14 -12
- package/src/core/tasks/editorial-review-structure.xml +28 -28
- package/src/core/tasks/help.md +36 -16
- package/src/core/tasks/index-docs.xml +1 -1
- package/src/core/tasks/review-adversarial-general.xml +2 -2
- package/src/core/tasks/shard-doc.xml +1 -2
- package/src/core/tasks/workflow.xml +1 -1
- package/src/core/workflows/advanced-elicitation/workflow.xml +1 -1
- package/test/test-installation-components.js +1 -1
- package/tools/build-docs.js +21 -1
- package/tools/cli/bmad-cli.js +42 -3
- package/tools/cli/installers/lib/core/config-collector.js +5 -1
- package/tools/cli/installers/lib/core/dependency-resolver.js +1 -1
- package/tools/cli/installers/lib/core/installer.js +1 -6
- package/tools/cli/installers/lib/core/manifest-generator.js +149 -125
- package/tools/cli/installers/lib/custom/handler.js +1 -1
- package/tools/cli/installers/lib/ide/_base-ide.js +25 -15
- package/tools/cli/installers/lib/ide/_config-driven.js +79 -9
- package/tools/cli/installers/lib/ide/codex.js +7 -1
- package/tools/cli/installers/lib/ide/manager.js +8 -2
- package/tools/cli/installers/lib/ide/platform-codes.yaml +0 -3
- package/tools/cli/installers/lib/ide/shared/agent-command-generator.js +2 -2
- package/tools/cli/installers/lib/ide/shared/bmad-artifacts.js +14 -3
- package/tools/cli/installers/lib/ide/shared/path-utils.js +9 -2
- package/tools/cli/installers/lib/ide/shared/task-tool-command-generator.js +126 -29
- package/tools/cli/installers/lib/ide/shared/workflow-command-generator.js +2 -2
- package/tools/cli/installers/lib/ide/templates/combined/default-task.md +10 -0
- package/tools/cli/installers/lib/ide/templates/combined/default-tool.md +10 -0
- package/tools/cli/installers/lib/ide/templates/combined/gemini-task.toml +11 -0
- package/tools/cli/installers/lib/ide/templates/combined/gemini-tool.toml +11 -0
- package/tools/cli/installers/lib/modules/manager.js +3 -6
- package/tools/cli/lib/agent/installer.js +1 -1
- package/tools/cli/lib/prompts.js +192 -6
- package/tools/cli/lib/ui.js +124 -72
- package/tools/schema/agent.js +0 -1
- package/tools/validate-file-refs.js +480 -0
- package/website/astro.config.mjs +1 -35
- package/src/bmm/workflows/1-analysis/research/workflow.md +0 -173
- package/src/bmm/workflows/2-plan-workflows/create-prd/validation-report-prd-workflow.md +0 -433
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow.md +0 -150
- package/src/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-library.json +0 -90
- package/src/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-templates.yaml +0 -127
- package/src/bmm/workflows/excalidraw-diagrams/create-dataflow/checklist.md +0 -39
- package/src/bmm/workflows/excalidraw-diagrams/create-dataflow/instructions.md +0 -130
- package/src/bmm/workflows/excalidraw-diagrams/create-dataflow/workflow.yaml +0 -27
- package/src/bmm/workflows/excalidraw-diagrams/create-diagram/checklist.md +0 -43
- package/src/bmm/workflows/excalidraw-diagrams/create-diagram/instructions.md +0 -141
- package/src/bmm/workflows/excalidraw-diagrams/create-diagram/workflow.yaml +0 -27
- package/src/bmm/workflows/excalidraw-diagrams/create-flowchart/checklist.md +0 -49
- package/src/bmm/workflows/excalidraw-diagrams/create-flowchart/instructions.md +0 -241
- package/src/bmm/workflows/excalidraw-diagrams/create-flowchart/workflow.yaml +0 -27
- package/src/bmm/workflows/excalidraw-diagrams/create-wireframe/checklist.md +0 -38
- package/src/bmm/workflows/excalidraw-diagrams/create-wireframe/instructions.md +0 -133
- package/src/bmm/workflows/excalidraw-diagrams/create-wireframe/workflow.yaml +0 -27
- package/src/core/resources/excalidraw/README.md +0 -160
- package/src/core/resources/excalidraw/excalidraw-helpers.md +0 -127
- package/src/core/resources/excalidraw/library-loader.md +0 -50
- package/src/core/resources/excalidraw/validate-json-instructions.md +0 -79
- package/tools/flattener/aggregate.js +0 -76
- package/tools/flattener/binary.js +0 -80
- package/tools/flattener/discovery.js +0 -71
- package/tools/flattener/files.js +0 -35
- package/tools/flattener/ignoreRules.js +0 -172
- package/tools/flattener/main.js +0 -483
- package/tools/flattener/projectRoot.js +0 -201
- package/tools/flattener/prompts.js +0 -44
- package/tools/flattener/stats.helpers.js +0 -368
- package/tools/flattener/stats.js +0 -75
- package/tools/flattener/test-matrix.js +0 -409
- package/tools/flattener/xml.js +0 -82
package/tools/cli/lib/prompts.js
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
let _clack = null;
|
|
11
|
+
let _clackCore = null;
|
|
12
|
+
let _picocolors = null;
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* Lazy-load @clack/prompts (ESM module)
|
|
@@ -20,6 +22,28 @@ async function getClack() {
|
|
|
20
22
|
return _clack;
|
|
21
23
|
}
|
|
22
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Lazy-load @clack/core (ESM module)
|
|
27
|
+
* @returns {Promise<Object>} The clack core module
|
|
28
|
+
*/
|
|
29
|
+
async function getClackCore() {
|
|
30
|
+
if (!_clackCore) {
|
|
31
|
+
_clackCore = await import('@clack/core');
|
|
32
|
+
}
|
|
33
|
+
return _clackCore;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Lazy-load picocolors
|
|
38
|
+
* @returns {Promise<Object>} The picocolors module
|
|
39
|
+
*/
|
|
40
|
+
async function getPicocolors() {
|
|
41
|
+
if (!_picocolors) {
|
|
42
|
+
_picocolors = (await import('picocolors')).default;
|
|
43
|
+
}
|
|
44
|
+
return _picocolors;
|
|
45
|
+
}
|
|
46
|
+
|
|
23
47
|
/**
|
|
24
48
|
* Handle user cancellation gracefully
|
|
25
49
|
* @param {any} value - The value to check
|
|
@@ -191,6 +215,118 @@ async function groupMultiselect(options) {
|
|
|
191
215
|
return result;
|
|
192
216
|
}
|
|
193
217
|
|
|
218
|
+
/**
|
|
219
|
+
* Default filter function for autocomplete - case-insensitive label matching
|
|
220
|
+
* @param {string} search - Search string
|
|
221
|
+
* @param {Object} option - Option object with label
|
|
222
|
+
* @returns {boolean} Whether the option matches
|
|
223
|
+
*/
|
|
224
|
+
function defaultAutocompleteFilter(search, option) {
|
|
225
|
+
const label = option.label ?? String(option.value ?? '');
|
|
226
|
+
return label.toLowerCase().includes(search.toLowerCase());
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Autocomplete multi-select prompt with type-ahead filtering
|
|
231
|
+
* Custom implementation that always shows "Space/Tab:" in the hint
|
|
232
|
+
* @param {Object} options - Prompt options
|
|
233
|
+
* @param {string} options.message - The question to ask
|
|
234
|
+
* @param {Array} options.options - Array of choices [{label, value, hint?}]
|
|
235
|
+
* @param {string} [options.placeholder] - Placeholder text for search input
|
|
236
|
+
* @param {Array} [options.initialValues] - Array of initially selected values
|
|
237
|
+
* @param {boolean} [options.required=false] - Whether at least one must be selected
|
|
238
|
+
* @param {number} [options.maxItems=5] - Maximum visible items in scrollable list
|
|
239
|
+
* @param {Function} [options.filter] - Custom filter function (search, option) => boolean
|
|
240
|
+
* @returns {Promise<Array>} Array of selected values
|
|
241
|
+
*/
|
|
242
|
+
async function autocompleteMultiselect(options) {
|
|
243
|
+
const core = await getClackCore();
|
|
244
|
+
const clack = await getClack();
|
|
245
|
+
const color = await getPicocolors();
|
|
246
|
+
|
|
247
|
+
const filterFn = options.filter ?? defaultAutocompleteFilter;
|
|
248
|
+
|
|
249
|
+
const prompt = new core.AutocompletePrompt({
|
|
250
|
+
options: options.options,
|
|
251
|
+
multiple: true,
|
|
252
|
+
filter: filterFn,
|
|
253
|
+
validate: () => {
|
|
254
|
+
if (options.required && prompt.selectedValues.length === 0) {
|
|
255
|
+
return 'Please select at least one item';
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
initialValue: options.initialValues,
|
|
259
|
+
render() {
|
|
260
|
+
const barColor = this.state === 'error' ? color.yellow : color.cyan;
|
|
261
|
+
const bar = barColor(clack.S_BAR);
|
|
262
|
+
const barEnd = barColor(clack.S_BAR_END);
|
|
263
|
+
|
|
264
|
+
const title = `${color.gray(clack.S_BAR)}\n${clack.symbol(this.state)} ${options.message}\n`;
|
|
265
|
+
|
|
266
|
+
const userInput = this.userInput;
|
|
267
|
+
const placeholder = options.placeholder || 'Type to search...';
|
|
268
|
+
const hasPlaceholder = userInput === '' && placeholder !== undefined;
|
|
269
|
+
|
|
270
|
+
// Show placeholder or user input with cursor
|
|
271
|
+
const searchDisplay =
|
|
272
|
+
this.isNavigating || hasPlaceholder ? color.dim(hasPlaceholder ? placeholder : userInput) : this.userInputWithCursor;
|
|
273
|
+
|
|
274
|
+
const allOptions = this.options;
|
|
275
|
+
const matchCount =
|
|
276
|
+
this.filteredOptions.length === allOptions.length
|
|
277
|
+
? ''
|
|
278
|
+
: color.dim(` (${this.filteredOptions.length} match${this.filteredOptions.length === 1 ? '' : 'es'})`);
|
|
279
|
+
|
|
280
|
+
// Render option with checkbox
|
|
281
|
+
const renderOption = (opt, isHighlighted) => {
|
|
282
|
+
const isSelected = this.selectedValues.includes(opt.value);
|
|
283
|
+
const label = opt.label ?? String(opt.value ?? '');
|
|
284
|
+
const hintText = opt.hint && opt.value === this.focusedValue ? color.dim(` (${opt.hint})`) : '';
|
|
285
|
+
const checkbox = isSelected ? color.green(clack.S_CHECKBOX_SELECTED) : color.dim(clack.S_CHECKBOX_INACTIVE);
|
|
286
|
+
return isHighlighted ? `${checkbox} ${label}${hintText}` : `${checkbox} ${color.dim(label)}`;
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
switch (this.state) {
|
|
290
|
+
case 'submit': {
|
|
291
|
+
return `${title}${color.gray(clack.S_BAR)} ${color.dim(`${this.selectedValues.length} items selected`)}`;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
case 'cancel': {
|
|
295
|
+
return `${title}${color.gray(clack.S_BAR)} ${color.strikethrough(color.dim(userInput))}`;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
default: {
|
|
299
|
+
// Always show "SPACE:" regardless of isNavigating state
|
|
300
|
+
const hints = [`${color.dim('↑/↓')} to navigate`, `${color.dim('TAB/SPACE:')} select`, `${color.dim('ENTER:')} confirm`];
|
|
301
|
+
|
|
302
|
+
const noMatchesLine = this.filteredOptions.length === 0 && userInput ? [`${bar} ${color.yellow('No matches found')}`] : [];
|
|
303
|
+
|
|
304
|
+
const errorLine = this.state === 'error' ? [`${bar} ${color.yellow(this.error)}`] : [];
|
|
305
|
+
|
|
306
|
+
const headerLines = [...`${title}${bar}`.split('\n'), `${bar} ${searchDisplay}${matchCount}`, ...noMatchesLine, ...errorLine];
|
|
307
|
+
|
|
308
|
+
const footerLines = [`${bar} ${color.dim(hints.join(' • '))}`, `${barEnd}`];
|
|
309
|
+
|
|
310
|
+
const optionLines = clack.limitOptions({
|
|
311
|
+
cursor: this.cursor,
|
|
312
|
+
options: this.filteredOptions,
|
|
313
|
+
style: renderOption,
|
|
314
|
+
maxItems: options.maxItems || 5,
|
|
315
|
+
output: options.output,
|
|
316
|
+
rowPadding: headerLines.length + footerLines.length,
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
return [...headerLines, ...optionLines.map((line) => `${bar} ${line}`), ...footerLines].join('\n');
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
const result = await prompt.prompt();
|
|
326
|
+
await handleCancel(result);
|
|
327
|
+
return result;
|
|
328
|
+
}
|
|
329
|
+
|
|
194
330
|
/**
|
|
195
331
|
* Confirm prompt (replaces Inquirer 'confirm' type)
|
|
196
332
|
* @param {Object} options - Prompt options
|
|
@@ -211,7 +347,12 @@ async function confirm(options) {
|
|
|
211
347
|
}
|
|
212
348
|
|
|
213
349
|
/**
|
|
214
|
-
* Text input prompt (replaces Inquirer 'input' type)
|
|
350
|
+
* Text input prompt with Tab-to-fill-placeholder support (replaces Inquirer 'input' type)
|
|
351
|
+
*
|
|
352
|
+
* This custom implementation restores the Tab-to-fill-placeholder behavior that was
|
|
353
|
+
* intentionally removed in @clack/prompts v1.0.0 (placeholder became purely visual).
|
|
354
|
+
* Uses @clack/core's TextPrompt primitive with custom key handling.
|
|
355
|
+
*
|
|
215
356
|
* @param {Object} options - Prompt options
|
|
216
357
|
* @param {string} options.message - The question to ask
|
|
217
358
|
* @param {string} [options.default] - Default value
|
|
@@ -220,20 +361,64 @@ async function confirm(options) {
|
|
|
220
361
|
* @returns {Promise<string>} User's input
|
|
221
362
|
*/
|
|
222
363
|
async function text(options) {
|
|
223
|
-
const
|
|
364
|
+
const core = await getClackCore();
|
|
365
|
+
const color = await getPicocolors();
|
|
224
366
|
|
|
225
367
|
// Use default as placeholder if placeholder not explicitly provided
|
|
226
368
|
// This shows the default value as grayed-out hint text
|
|
227
369
|
const placeholder = options.placeholder === undefined ? options.default : options.placeholder;
|
|
370
|
+
const defaultValue = options.default;
|
|
228
371
|
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
defaultValue: options.default,
|
|
232
|
-
placeholder: typeof placeholder === 'string' ? placeholder : undefined,
|
|
372
|
+
const prompt = new core.TextPrompt({
|
|
373
|
+
defaultValue,
|
|
233
374
|
validate: options.validate,
|
|
375
|
+
render() {
|
|
376
|
+
const title = `${color.gray('◆')} ${options.message}`;
|
|
377
|
+
|
|
378
|
+
// Show placeholder as dim text when input is empty
|
|
379
|
+
let valueDisplay;
|
|
380
|
+
if (this.state === 'error') {
|
|
381
|
+
valueDisplay = color.yellow(this.userInputWithCursor);
|
|
382
|
+
} else if (this.userInput) {
|
|
383
|
+
valueDisplay = this.userInputWithCursor;
|
|
384
|
+
} else if (placeholder) {
|
|
385
|
+
// Show placeholder with cursor indicator when empty
|
|
386
|
+
valueDisplay = `${color.inverse(color.hidden('_'))}${color.dim(placeholder)}`;
|
|
387
|
+
} else {
|
|
388
|
+
valueDisplay = color.inverse(color.hidden('_'));
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const bar = color.gray('│');
|
|
392
|
+
|
|
393
|
+
// Handle different states
|
|
394
|
+
if (this.state === 'submit') {
|
|
395
|
+
return `${color.gray('◇')} ${options.message}\n${bar} ${color.dim(this.value || defaultValue || '')}`;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (this.state === 'cancel') {
|
|
399
|
+
return `${color.gray('◇')} ${options.message}\n${bar} ${color.strikethrough(color.dim(this.userInput || ''))}`;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (this.state === 'error') {
|
|
403
|
+
return `${color.yellow('▲')} ${options.message}\n${bar} ${valueDisplay}\n${color.yellow('│')} ${color.yellow(this.error)}`;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return `${title}\n${bar} ${valueDisplay}\n${bar}`;
|
|
407
|
+
},
|
|
234
408
|
});
|
|
235
409
|
|
|
410
|
+
// Add Tab key handler to fill placeholder into input
|
|
411
|
+
prompt.on('key', (char) => {
|
|
412
|
+
if (char === '\t' && placeholder && !prompt.userInput) {
|
|
413
|
+
// Use _setUserInput with write=true to populate the readline and update internal state
|
|
414
|
+
prompt._setUserInput(placeholder, true);
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
const result = await prompt.prompt();
|
|
236
419
|
await handleCancel(result);
|
|
420
|
+
|
|
421
|
+
// TextPrompt's finalize handler already applies defaultValue for empty input
|
|
237
422
|
return result;
|
|
238
423
|
}
|
|
239
424
|
|
|
@@ -423,6 +608,7 @@ module.exports = {
|
|
|
423
608
|
select,
|
|
424
609
|
multiselect,
|
|
425
610
|
groupMultiselect,
|
|
611
|
+
autocompleteMultiselect,
|
|
426
612
|
confirm,
|
|
427
613
|
text,
|
|
428
614
|
password,
|
package/tools/cli/lib/ui.js
CHANGED
|
@@ -344,6 +344,9 @@ class UI {
|
|
|
344
344
|
|
|
345
345
|
/**
|
|
346
346
|
* Prompt for tool/IDE selection (called after module configuration)
|
|
347
|
+
* Uses a split prompt approach:
|
|
348
|
+
* 1. Recommended tools - standard multiselect for 3 preferred tools
|
|
349
|
+
* 2. Additional tools - autocompleteMultiselect with search capability
|
|
347
350
|
* @param {string} projectDir - Project directory to check for existing IDEs
|
|
348
351
|
* @returns {Object} Tool configuration
|
|
349
352
|
*/
|
|
@@ -366,95 +369,123 @@ class UI {
|
|
|
366
369
|
const preferredIdes = ideManager.getPreferredIdes();
|
|
367
370
|
const otherIdes = ideManager.getOtherIdes();
|
|
368
371
|
|
|
369
|
-
//
|
|
370
|
-
const
|
|
371
|
-
const
|
|
372
|
-
|
|
372
|
+
// Determine which configured IDEs are in "preferred" vs "other" categories
|
|
373
|
+
const configuredPreferred = configuredIdes.filter((id) => preferredIdes.some((ide) => ide.value === id));
|
|
374
|
+
const configuredOther = configuredIdes.filter((id) => otherIdes.some((ide) => ide.value === id));
|
|
375
|
+
|
|
376
|
+
// Warn about previously configured tools that are no longer available
|
|
377
|
+
const allKnownValues = new Set([...preferredIdes, ...otherIdes].map((ide) => ide.value));
|
|
378
|
+
const unknownTools = configuredIdes.filter((id) => id && typeof id === 'string' && !allKnownValues.has(id));
|
|
379
|
+
if (unknownTools.length > 0) {
|
|
380
|
+
console.log(chalk.yellow(`⚠️ Previously configured tools are no longer available: ${unknownTools.join(', ')}`));
|
|
381
|
+
}
|
|
373
382
|
|
|
374
|
-
//
|
|
383
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
384
|
+
// UPGRADE PATH: If tools already configured, show all tools with configured at top
|
|
385
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
375
386
|
if (configuredIdes.length > 0) {
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
387
|
+
const allTools = [...preferredIdes, ...otherIdes];
|
|
388
|
+
|
|
389
|
+
// Sort: configured tools first, then preferred, then others
|
|
390
|
+
const sortedTools = [
|
|
391
|
+
...allTools.filter((ide) => configuredIdes.includes(ide.value)),
|
|
392
|
+
...allTools.filter((ide) => !configuredIdes.includes(ide.value)),
|
|
393
|
+
];
|
|
394
|
+
|
|
395
|
+
const upgradeOptions = sortedTools.map((ide) => {
|
|
396
|
+
const isConfigured = configuredIdes.includes(ide.value);
|
|
397
|
+
const isPreferred = preferredIdes.some((p) => p.value === ide.value);
|
|
398
|
+
let label = ide.name;
|
|
399
|
+
if (isPreferred) label += ' ⭐';
|
|
400
|
+
if (isConfigured) label += ' ✅';
|
|
401
|
+
return { label, value: ide.value };
|
|
402
|
+
});
|
|
382
403
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
const otherIde = otherIdes.find((ide) => ide.value === ideValue);
|
|
386
|
-
const ide = preferredIde || otherIde;
|
|
404
|
+
// Sort initialValues to match display order
|
|
405
|
+
const sortedInitialValues = sortedTools.filter((ide) => configuredIdes.includes(ide.value)).map((ide) => ide.value);
|
|
387
406
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
407
|
+
const upgradeSelected = await prompts.autocompleteMultiselect({
|
|
408
|
+
message: 'Integrate with',
|
|
409
|
+
options: upgradeOptions,
|
|
410
|
+
initialValues: sortedInitialValues,
|
|
411
|
+
required: false,
|
|
412
|
+
maxItems: 8,
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
const selectedIdes = upgradeSelected || [];
|
|
416
|
+
|
|
417
|
+
if (selectedIdes.length === 0) {
|
|
418
|
+
console.log('');
|
|
419
|
+
const confirmNoTools = await prompts.confirm({
|
|
420
|
+
message: 'No tools selected. Continue without installing any tools?',
|
|
421
|
+
default: false,
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
if (!confirmNoTools) {
|
|
425
|
+
return this.promptToolSelection(projectDir);
|
|
398
426
|
}
|
|
427
|
+
|
|
428
|
+
return { ides: [], skipIde: true };
|
|
399
429
|
}
|
|
400
|
-
if (configuredGroup.length > 0) {
|
|
401
|
-
groupedOptions['Previously Configured'] = configuredGroup;
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
430
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
if (remainingPreferred.length > 0) {
|
|
408
|
-
groupedOptions['Recommended Tools'] = remainingPreferred.map((ide) => {
|
|
409
|
-
processedIdes.add(ide.value);
|
|
410
|
-
return {
|
|
411
|
-
label: `${ide.name} ⭐`,
|
|
412
|
-
value: ide.value,
|
|
413
|
-
};
|
|
414
|
-
});
|
|
415
|
-
}
|
|
431
|
+
// Display selected tools
|
|
432
|
+
this.displaySelectedTools(selectedIdes, preferredIdes, allTools);
|
|
416
433
|
|
|
417
|
-
|
|
418
|
-
const remainingOther = otherIdes.filter((ide) => !processedIdes.has(ide.value));
|
|
419
|
-
if (remainingOther.length > 0) {
|
|
420
|
-
groupedOptions['Additional Tools'] = remainingOther.map((ide) => ({
|
|
421
|
-
label: ide.name,
|
|
422
|
-
value: ide.value,
|
|
423
|
-
}));
|
|
434
|
+
return { ides: selectedIdes, skipIde: false };
|
|
424
435
|
}
|
|
425
436
|
|
|
426
|
-
//
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
value: '__NONE__',
|
|
431
|
-
},
|
|
432
|
-
];
|
|
437
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
438
|
+
// NEW INSTALL: Show all tools with search
|
|
439
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
440
|
+
const allTools = [...preferredIdes, ...otherIdes];
|
|
433
441
|
|
|
434
|
-
|
|
442
|
+
const allToolOptions = allTools.map((ide) => {
|
|
443
|
+
const isPreferred = preferredIdes.some((p) => p.value === ide.value);
|
|
444
|
+
let label = ide.name;
|
|
445
|
+
if (isPreferred) label += ' ⭐';
|
|
446
|
+
return {
|
|
447
|
+
label,
|
|
448
|
+
value: ide.value,
|
|
449
|
+
};
|
|
450
|
+
});
|
|
435
451
|
|
|
436
|
-
selectedIdes = await prompts.
|
|
437
|
-
message:
|
|
438
|
-
options:
|
|
439
|
-
initialValues:
|
|
440
|
-
required:
|
|
441
|
-
|
|
452
|
+
const selectedIdes = await prompts.autocompleteMultiselect({
|
|
453
|
+
message: 'Select tools:',
|
|
454
|
+
options: allToolOptions,
|
|
455
|
+
initialValues: configuredIdes.length > 0 ? configuredIdes : undefined,
|
|
456
|
+
required: false,
|
|
457
|
+
maxItems: 8,
|
|
442
458
|
});
|
|
443
459
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
460
|
+
const allSelectedIdes = selectedIdes || [];
|
|
461
|
+
|
|
462
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
463
|
+
// STEP 3: Confirm if no tools selected
|
|
464
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
465
|
+
if (allSelectedIdes.length === 0) {
|
|
466
|
+
console.log('');
|
|
467
|
+
const confirmNoTools = await prompts.confirm({
|
|
468
|
+
message: 'No tools selected. Continue without installing any tools?',
|
|
469
|
+
default: false,
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
if (!confirmNoTools) {
|
|
473
|
+
// User wants to select tools - recurse
|
|
474
|
+
return this.promptToolSelection(projectDir);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return {
|
|
478
|
+
ides: [],
|
|
479
|
+
skipIde: true,
|
|
480
|
+
};
|
|
453
481
|
}
|
|
454
482
|
|
|
483
|
+
// Display selected tools
|
|
484
|
+
this.displaySelectedTools(allSelectedIdes, preferredIdes, allTools);
|
|
485
|
+
|
|
455
486
|
return {
|
|
456
|
-
ides:
|
|
457
|
-
skipIde:
|
|
487
|
+
ides: allSelectedIdes,
|
|
488
|
+
skipIde: allSelectedIdes.length === 0,
|
|
458
489
|
};
|
|
459
490
|
}
|
|
460
491
|
|
|
@@ -1655,6 +1686,27 @@ class UI {
|
|
|
1655
1686
|
|
|
1656
1687
|
console.log('');
|
|
1657
1688
|
}
|
|
1689
|
+
|
|
1690
|
+
/**
|
|
1691
|
+
* Display list of selected tools after IDE selection
|
|
1692
|
+
* @param {Array} selectedIdes - Array of selected IDE values
|
|
1693
|
+
* @param {Array} preferredIdes - Array of preferred IDE objects
|
|
1694
|
+
* @param {Array} allTools - Array of all tool objects
|
|
1695
|
+
*/
|
|
1696
|
+
displaySelectedTools(selectedIdes, preferredIdes, allTools) {
|
|
1697
|
+
if (selectedIdes.length === 0) return;
|
|
1698
|
+
|
|
1699
|
+
const preferredValues = new Set(preferredIdes.map((ide) => ide.value));
|
|
1700
|
+
|
|
1701
|
+
console.log('');
|
|
1702
|
+
console.log(chalk.dim(' Selected tools:'));
|
|
1703
|
+
for (const ideValue of selectedIdes) {
|
|
1704
|
+
const tool = allTools.find((t) => t.value === ideValue);
|
|
1705
|
+
const name = tool?.name || ideValue;
|
|
1706
|
+
const marker = preferredValues.has(ideValue) ? ' ⭐' : '';
|
|
1707
|
+
console.log(chalk.dim(` • ${name}${marker}`));
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1658
1710
|
}
|
|
1659
1711
|
|
|
1660
1712
|
module.exports = { UI };
|
package/tools/schema/agent.js
CHANGED
|
@@ -210,7 +210,6 @@ function buildAgentSchema(expectedModule) {
|
|
|
210
210
|
critical_actions: z.array(createNonEmptyString('agent.critical_actions[]')).optional(),
|
|
211
211
|
menu: z.array(buildMenuItemSchema()).min(1, { message: 'agent.menu must include at least one entry' }),
|
|
212
212
|
prompts: z.array(buildPromptSchema()).optional(),
|
|
213
|
-
webskip: z.boolean().optional(),
|
|
214
213
|
discussion: z.boolean().optional(),
|
|
215
214
|
conversational_knowledge: z.array(z.object({}).passthrough()).min(1).optional(),
|
|
216
215
|
})
|