bmad-method 6.6.1-next.8 → 6.6.1-next.9

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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Version](https://img.shields.io/npm/v/bmad-method?color=blue&label=version)](https://www.npmjs.com/package/bmad-method)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
5
- [![Node.js Version](https://img.shields.io/badge/node-%3E%3D20.0.0-brightgreen)](https://nodejs.org)
5
+ [![Node.js Version](https://img.shields.io/badge/node-%3E%3D20.12.0-brightgreen)](https://nodejs.org)
6
6
  [![Python Version](https://img.shields.io/badge/python-%3E%3D3.10-blue?logo=python&logoColor=white)](https://www.python.org)
7
7
  [![uv](https://img.shields.io/badge/uv-package%20manager-blueviolet?logo=uv)](https://docs.astral.sh/uv/)
8
8
  [![Discord](https://img.shields.io/badge/Discord-Join%20Community-7289da?logo=discord&logoColor=white)](https://discord.gg/gk8jAdXWmj)
@@ -36,7 +36,7 @@ Traditional AI tools do the thinking for you, producing average results. BMad ag
36
36
 
37
37
  ## Quick Start
38
38
 
39
- **Prerequisites**: [Node.js](https://nodejs.org) v20+ · [Python](https://www.python.org) 3.10+ · [uv](https://docs.astral.sh/uv/)
39
+ **Prerequisites**: [Node.js](https://nodejs.org) v20.12+ · [Python](https://www.python.org) 3.10+ · [uv](https://docs.astral.sh/uv/)
40
40
 
41
41
  ```bash
42
42
  npx bmad-method install
@@ -82,11 +82,11 @@ BMad Method extends with official modules for specialized domains. Available dur
82
82
  [BMad Method Docs Site](https://docs.bmad-method.org) — Tutorials, guides, concepts, and reference
83
83
 
84
84
  **Quick links:**
85
+
85
86
  - [Getting Started Tutorial](https://docs.bmad-method.org/tutorials/getting-started/)
86
87
  - [Upgrading from Previous Versions](https://docs.bmad-method.org/how-to/upgrade-to-v6/)
87
88
  - [Test Architect Documentation](https://bmad-code-org.github.io/bmad-method-test-architecture-enterprise/)
88
89
 
89
-
90
90
  ## Community
91
91
 
92
92
  - [Discord](https://discord.gg/gk8jAdXWmj) — Get help, share ideas, collaborate
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "bmad-method",
4
- "version": "6.6.1-next.8",
4
+ "version": "6.6.1-next.9",
5
5
  "description": "Breakthrough Method of Agile AI-driven Development",
6
6
  "keywords": [
7
7
  "agile",
@@ -66,8 +66,8 @@
66
66
  ]
67
67
  },
68
68
  "dependencies": {
69
- "@clack/core": "^1.0.0",
70
- "@clack/prompts": "^1.0.0",
69
+ "@clack/core": "^1.3.1",
70
+ "@clack/prompts": "^1.4.0",
71
71
  "@kayvan/markdown-tree-parser": "^1.6.1",
72
72
  "chalk": "^4.1.2",
73
73
  "commander": "^14.0.0",
@@ -103,7 +103,7 @@
103
103
  "yaml-lint": "^1.7.0"
104
104
  },
105
105
  "engines": {
106
- "node": ">=20.0.0"
106
+ "node": ">=20.12.0"
107
107
  },
108
108
  "publishConfig": {
109
109
  "access": "public"
@@ -10,6 +10,9 @@
10
10
  let _clack = null;
11
11
  let _clackCore = null;
12
12
  let _picocolors = null;
13
+ const fs = require('node:fs');
14
+ const os = require('node:os');
15
+ const path = require('node:path');
13
16
 
14
17
  /**
15
18
  * Lazy-load @clack/prompts (ESM module)
@@ -575,6 +578,151 @@ async function autocomplete(options) {
575
578
  return result;
576
579
  }
577
580
 
581
+ function hasPathSeparator(value) {
582
+ return value.endsWith('/') || value.endsWith('\\');
583
+ }
584
+
585
+ function expandHome(input) {
586
+ if (!input) return input;
587
+ if (input === '~') return os.homedir();
588
+ if (input.startsWith('~/') || input.startsWith('~\\')) {
589
+ return path.join(os.homedir(), input.slice(2));
590
+ }
591
+ return input;
592
+ }
593
+
594
+ function toDirectoryOption(value, label = value, synthetic = false) {
595
+ return { value, label, synthetic };
596
+ }
597
+
598
+ function isExistingDirectory(value) {
599
+ try {
600
+ return fs.existsSync(value) && fs.statSync(value).isDirectory();
601
+ } catch {
602
+ return false;
603
+ }
604
+ }
605
+
606
+ function listDirectoryOptions(input, options) {
607
+ const cwd = options.cwd || process.cwd();
608
+ const rawInput = input.trim();
609
+ const expandedInput = expandHome(rawInput);
610
+ const trailingSep = hasPathSeparator(rawInput) || hasPathSeparator(expandedInput);
611
+ const resolvedInput = expandedInput ? path.resolve(cwd, expandedInput) : cwd;
612
+ const browseDir = expandedInput && !trailingSep && !isExistingDirectory(resolvedInput) ? path.dirname(resolvedInput) : resolvedInput;
613
+ const prefix = expandedInput && browseDir !== resolvedInput ? path.basename(resolvedInput).toLowerCase() : '';
614
+ const results = [];
615
+
616
+ if (!trailingSep && isExistingDirectory(resolvedInput)) {
617
+ results.push(toDirectoryOption(resolvedInput, `. (use this directory)`));
618
+ }
619
+
620
+ if (isExistingDirectory(browseDir)) {
621
+ try {
622
+ for (const entry of fs.readdirSync(browseDir, { withFileTypes: true })) {
623
+ if (!entry.isDirectory()) continue;
624
+ if (prefix && !entry.name.toLowerCase().startsWith(prefix)) continue;
625
+ const fullPath = path.join(browseDir, entry.name);
626
+ if (!results.some((option) => option.value === fullPath)) {
627
+ results.push(toDirectoryOption(fullPath));
628
+ }
629
+ }
630
+ } catch {
631
+ // Skip unreadable directories; validation still reports path issues.
632
+ }
633
+ }
634
+
635
+ const validation = options.validate?.(rawInput);
636
+ const hasMatchingOption = results.some((option) => option.value === resolvedInput);
637
+ if (expandedInput && !validation && !hasMatchingOption) {
638
+ results.unshift(toDirectoryOption(resolvedInput, `Create/use: ${resolvedInput}`, true));
639
+ }
640
+
641
+ return results;
642
+ }
643
+
644
+ /**
645
+ * Directory prompt with autocomplete candidates and create-directory support.
646
+ * Uses @clack/core directly so typed paths that do not exist yet can still be
647
+ * submitted when validation allows creating them.
648
+ * @param {Object} options - Prompt options
649
+ * @param {string} options.message - Prompt message
650
+ * @param {string} [options.default] - Default directory
651
+ * @param {string} [options.placeholder] - Placeholder text
652
+ * @param {Function} [options.validate] - Sync validation function
653
+ * @returns {Promise<string>} Selected or typed directory path
654
+ */
655
+ async function directory(options) {
656
+ const core = await getClackCore();
657
+ const color = await getPicocolors();
658
+ const tabCompletion = {
659
+ prefix: '',
660
+ index: -1,
661
+ options: [],
662
+ lastValue: '',
663
+ };
664
+
665
+ let prompt;
666
+ prompt = new core.AutocompletePrompt({
667
+ initialValue: options.default,
668
+ options: () => listDirectoryOptions(prompt?.userInput || '', options),
669
+ filter: () => true,
670
+ validate: (value) => options.validate?.(value ?? prompt.userInput),
671
+ render() {
672
+ const title = `${color.gray('◆')} ${options.message}`;
673
+ const bar = color.gray('│');
674
+ const barEnd = color.gray('└');
675
+ const userInput = this.userInput;
676
+ const placeholder = options.placeholder || options.default;
677
+ const inputDisplay = userInput ? this.userInputWithCursor : `${color.inverse(color.hidden('_'))}${color.dim(placeholder || '')}`;
678
+ const errorLine = this.state === 'error' ? [`${color.yellow('│')} ${color.yellow(this.error)}`] : [];
679
+
680
+ switch (this.state) {
681
+ case 'submit': {
682
+ return `${color.gray('◇')} ${options.message}\n${bar} ${color.dim(this.value || '')}`;
683
+ }
684
+ case 'cancel': {
685
+ return `${color.gray('◇')} ${options.message}\n${bar} ${color.strikethrough(color.dim(userInput || ''))}`;
686
+ }
687
+ default: {
688
+ return [title, `${bar} ${inputDisplay}`, ...errorLine, barEnd].join('\n');
689
+ }
690
+ }
691
+ },
692
+ });
693
+
694
+ const hasSetUserInput = typeof prompt._setUserInput === 'function';
695
+ const hasClearUserInput = typeof prompt._clearUserInput === 'function';
696
+
697
+ prompt.on('key', (_, key) => {
698
+ if (key?.name !== 'tab') return;
699
+ if (!hasSetUserInput) return; // @clack/core API surface changed — skip Tab silently.
700
+ const currentInput = prompt.userInput;
701
+ const isContinuingCycle = tabCompletion.lastValue && currentInput === tabCompletion.lastValue;
702
+ const completionOptions = isContinuingCycle ? tabCompletion.options : prompt.filteredOptions.filter((option) => !option.synthetic);
703
+ if (completionOptions.length === 0) return;
704
+
705
+ if (isContinuingCycle) {
706
+ tabCompletion.index = (tabCompletion.index + 1) % completionOptions.length;
707
+ } else {
708
+ tabCompletion.prefix = currentInput;
709
+ tabCompletion.options = completionOptions;
710
+ tabCompletion.index = 0;
711
+ }
712
+
713
+ const focusedOption = completionOptions[tabCompletion.index];
714
+ if (!focusedOption) return;
715
+ const completedValue = focusedOption.value;
716
+ tabCompletion.lastValue = completedValue;
717
+ if (hasClearUserInput) prompt._clearUserInput();
718
+ prompt._setUserInput(completedValue, true);
719
+ });
720
+
721
+ const result = await prompt.prompt();
722
+ await handleCancel(result);
723
+ return result;
724
+ }
725
+
578
726
  /**
579
727
  * Get the color utility (picocolors instance from @clack/prompts)
580
728
  * @returns {Promise<Object>} The color utility (picocolors)
@@ -694,6 +842,7 @@ module.exports = {
694
842
  multiselect,
695
843
  autocompleteMultiselect,
696
844
  autocomplete,
845
+ directory,
697
846
  confirm,
698
847
  text,
699
848
  password,
@@ -1436,7 +1436,7 @@ class UI {
1436
1436
  */
1437
1437
  async promptForDirectory() {
1438
1438
  // Use sync validation because @clack/prompts doesn't support async validate
1439
- const directory = await prompts.text({
1439
+ const directory = await prompts.directory({
1440
1440
  message: 'Installation directory:',
1441
1441
  default: process.cwd(),
1442
1442
  placeholder: process.cwd(),