bmad-method 4.37.0-beta.4 → 4.37.0-beta.6

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.
Files changed (206) hide show
  1. package/.github/FUNDING.yaml +15 -0
  2. package/.github/ISSUE_TEMPLATE/bug_report.md +32 -0
  3. package/.github/ISSUE_TEMPLATE/feature_request.md +22 -0
  4. package/.github/workflows/discord.yaml +16 -0
  5. package/.github/workflows/release.yaml +60 -0
  6. package/.releaserc.json +21 -0
  7. package/.vscode/settings.json +44 -0
  8. package/CHANGELOG.md +689 -0
  9. package/CONTRIBUTING.md +209 -0
  10. package/LICENSE +21 -0
  11. package/README.md +214 -3
  12. package/bmad-core/agent-teams/team-all.yaml +14 -0
  13. package/bmad-core/agent-teams/team-fullstack.yaml +18 -0
  14. package/bmad-core/agent-teams/team-ide-minimal.yaml +10 -0
  15. package/bmad-core/agent-teams/team-no-ui.yaml +13 -0
  16. package/bmad-core/agents/analyst.md +81 -0
  17. package/bmad-core/agents/architect.md +84 -0
  18. package/bmad-core/agents/bmad-master.md +108 -0
  19. package/bmad-core/agents/bmad-orchestrator.md +150 -0
  20. package/bmad-core/agents/dev.md +76 -0
  21. package/bmad-core/agents/pm.md +81 -0
  22. package/bmad-core/agents/po.md +76 -0
  23. package/bmad-core/agents/qa.md +69 -0
  24. package/bmad-core/agents/sm.md +62 -0
  25. package/bmad-core/agents/ux-expert.md +66 -0
  26. package/bmad-core/checklists/architect-checklist.md +443 -0
  27. package/bmad-core/checklists/change-checklist.md +182 -0
  28. package/bmad-core/checklists/pm-checklist.md +375 -0
  29. package/bmad-core/checklists/po-master-checklist.md +441 -0
  30. package/bmad-core/checklists/story-dod-checklist.md +101 -0
  31. package/bmad-core/checklists/story-draft-checklist.md +156 -0
  32. package/bmad-core/core-config.yaml +20 -0
  33. package/bmad-core/data/bmad-kb.md +803 -0
  34. package/bmad-core/data/brainstorming-techniques.md +36 -0
  35. package/bmad-core/data/elicitation-methods.md +134 -0
  36. package/bmad-core/data/technical-preferences.md +3 -0
  37. package/bmad-core/tasks/advanced-elicitation.md +117 -0
  38. package/bmad-core/tasks/brownfield-create-epic.md +160 -0
  39. package/bmad-core/tasks/brownfield-create-story.md +147 -0
  40. package/bmad-core/tasks/correct-course.md +70 -0
  41. package/bmad-core/tasks/create-brownfield-story.md +304 -0
  42. package/bmad-core/tasks/create-deep-research-prompt.md +289 -0
  43. package/bmad-core/tasks/create-next-story.md +112 -0
  44. package/bmad-core/tasks/document-project.md +341 -0
  45. package/bmad-core/tasks/facilitate-brainstorming-session.md +136 -0
  46. package/bmad-core/tasks/generate-ai-frontend-prompt.md +51 -0
  47. package/bmad-core/tasks/index-docs.md +179 -0
  48. package/bmad-core/tasks/kb-mode-interaction.md +75 -0
  49. package/bmad-core/tasks/review-story.md +145 -0
  50. package/bmad-core/tasks/shard-doc.md +187 -0
  51. package/bmad-core/tasks/validate-next-story.md +134 -0
  52. package/bmad-core/templates/architecture-tmpl.yaml +650 -0
  53. package/bmad-core/templates/brainstorming-output-tmpl.yaml +156 -0
  54. package/bmad-core/templates/brownfield-architecture-tmpl.yaml +476 -0
  55. package/bmad-core/templates/brownfield-prd-tmpl.yaml +280 -0
  56. package/bmad-core/templates/competitor-analysis-tmpl.yaml +293 -0
  57. package/bmad-core/templates/front-end-architecture-tmpl.yaml +206 -0
  58. package/bmad-core/templates/front-end-spec-tmpl.yaml +349 -0
  59. package/bmad-core/templates/fullstack-architecture-tmpl.yaml +805 -0
  60. package/bmad-core/templates/market-research-tmpl.yaml +252 -0
  61. package/bmad-core/templates/prd-tmpl.yaml +202 -0
  62. package/bmad-core/templates/project-brief-tmpl.yaml +221 -0
  63. package/bmad-core/templates/story-tmpl.yaml +137 -0
  64. package/bmad-core/workflows/brownfield-fullstack.yaml +297 -0
  65. package/bmad-core/workflows/brownfield-service.yaml +187 -0
  66. package/bmad-core/workflows/brownfield-ui.yaml +197 -0
  67. package/bmad-core/workflows/greenfield-fullstack.yaml +240 -0
  68. package/bmad-core/workflows/greenfield-service.yaml +206 -0
  69. package/bmad-core/workflows/greenfield-ui.yaml +235 -0
  70. package/common/tasks/create-doc.md +101 -0
  71. package/common/tasks/execute-checklist.md +93 -0
  72. package/common/utils/bmad-doc-template.md +325 -0
  73. package/common/utils/workflow-management.md +69 -0
  74. package/dist/agents/analyst.txt +2882 -0
  75. package/dist/agents/architect.txt +3543 -0
  76. package/dist/agents/bmad-master.txt +8756 -0
  77. package/dist/agents/bmad-orchestrator.txt +1490 -0
  78. package/dist/agents/dev.txt +428 -0
  79. package/dist/agents/pm.txt +2229 -0
  80. package/dist/agents/po.txt +1364 -0
  81. package/dist/agents/qa.txt +386 -0
  82. package/dist/agents/sm.txt +668 -0
  83. package/dist/agents/ux-expert.txt +701 -0
  84. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-designer.txt +2408 -0
  85. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.txt +1631 -0
  86. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.txt +822 -0
  87. package/dist/expansion-packs/bmad-2d-phaser-game-dev/teams/phaser-2d-nodejs-game-team.txt +10989 -0
  88. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-architect.txt +4047 -0
  89. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-designer.txt +3744 -0
  90. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-developer.txt +465 -0
  91. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-sm.txt +990 -0
  92. package/dist/expansion-packs/bmad-2d-unity-game-dev/teams/unity-2d-game-team.txt +15467 -0
  93. package/dist/expansion-packs/bmad-infrastructure-devops/agents/infra-devops-platform.txt +2077 -0
  94. package/dist/teams/team-all.txt +11062 -0
  95. package/dist/teams/team-fullstack.txt +10392 -0
  96. package/dist/teams/team-ide-minimal.txt +3507 -0
  97. package/dist/teams/team-no-ui.txt +8951 -0
  98. package/docs/GUIDING-PRINCIPLES.md +91 -0
  99. package/docs/core-architecture.md +219 -0
  100. package/docs/enhanced-ide-development-workflow.md +43 -0
  101. package/docs/expansion-packs.md +280 -0
  102. package/docs/how-to-contribute-with-pull-requests.md +158 -0
  103. package/docs/user-guide.md +251 -0
  104. package/docs/versioning-and-releases.md +77 -0
  105. package/docs/versions.md +48 -0
  106. package/docs/working-in-the-brownfield.md +364 -0
  107. package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/Complete AI Agent System - Flowchart.svg +102 -0
  108. package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/PART 1 - Google Cloud Vertex AI Setup Documentation/1.1 Google Cloud Project Setup/1.1.1 - Initial Project Configuration - bash copy.txt +13 -0
  109. package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/PART 1 - Google Cloud Vertex AI Setup Documentation/1.1 Google Cloud Project Setup/1.1.1 - Initial Project Configuration - bash.txt +13 -0
  110. package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/PART 1 - Google Cloud Vertex AI Setup Documentation/1.2 Agent Development Kit Installation/1.2.2 - Basic Project Structure - txt.txt +25 -0
  111. package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/PART 1 - Google Cloud Vertex AI Setup Documentation/1.3 Core Configuration Files/1.3.1 - settings.py +34 -0
  112. package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/PART 1 - Google Cloud Vertex AI Setup Documentation/1.3 Core Configuration Files/1.3.2 - main.py - Base Application.py +70 -0
  113. package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/PART 1 - Google Cloud Vertex AI Setup Documentation/1.4 Deployment Configuration/1.4.2 - cloudbuild.yaml +26 -0
  114. package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/README.md +109 -0
  115. package/expansion-packs/README.md +3 -0
  116. package/expansion-packs/bmad-2d-phaser-game-dev/agent-teams/phaser-2d-nodejs-game-team.yaml +13 -0
  117. package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-designer.md +71 -0
  118. package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.md +78 -0
  119. package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.md +64 -0
  120. package/expansion-packs/bmad-2d-phaser-game-dev/checklists/game-design-checklist.md +201 -0
  121. package/expansion-packs/bmad-2d-phaser-game-dev/checklists/game-story-dod-checklist.md +160 -0
  122. package/expansion-packs/bmad-2d-phaser-game-dev/config.yaml +8 -0
  123. package/expansion-packs/bmad-2d-phaser-game-dev/data/bmad-kb.md +254 -0
  124. package/expansion-packs/bmad-2d-phaser-game-dev/data/development-guidelines.md +651 -0
  125. package/expansion-packs/bmad-2d-phaser-game-dev/tasks/advanced-elicitation.md +111 -0
  126. package/expansion-packs/bmad-2d-phaser-game-dev/tasks/create-game-story.md +216 -0
  127. package/expansion-packs/bmad-2d-phaser-game-dev/tasks/game-design-brainstorming.md +308 -0
  128. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-architecture-tmpl.yaml +613 -0
  129. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-brief-tmpl.yaml +356 -0
  130. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-design-doc-tmpl.yaml +343 -0
  131. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-story-tmpl.yaml +253 -0
  132. package/expansion-packs/bmad-2d-phaser-game-dev/templates/level-design-doc-tmpl.yaml +484 -0
  133. package/expansion-packs/bmad-2d-phaser-game-dev/workflows/game-dev-greenfield.yaml +183 -0
  134. package/expansion-packs/bmad-2d-phaser-game-dev/workflows/game-prototype.yaml +175 -0
  135. package/expansion-packs/bmad-2d-unity-game-dev/agent-teams/unity-2d-game-team.yaml +14 -0
  136. package/expansion-packs/bmad-2d-unity-game-dev/agents/game-architect.md +80 -0
  137. package/expansion-packs/bmad-2d-unity-game-dev/agents/game-designer.md +77 -0
  138. package/expansion-packs/bmad-2d-unity-game-dev/agents/game-developer.md +78 -0
  139. package/expansion-packs/bmad-2d-unity-game-dev/agents/game-sm.md +65 -0
  140. package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-architect-checklist.md +396 -0
  141. package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-change-checklist.md +203 -0
  142. package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-design-checklist.md +201 -0
  143. package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-story-dod-checklist.md +132 -0
  144. package/expansion-packs/bmad-2d-unity-game-dev/config.yaml +6 -0
  145. package/expansion-packs/bmad-2d-unity-game-dev/data/bmad-kb.md +776 -0
  146. package/expansion-packs/bmad-2d-unity-game-dev/data/development-guidelines.md +590 -0
  147. package/expansion-packs/bmad-2d-unity-game-dev/tasks/advanced-elicitation.md +111 -0
  148. package/expansion-packs/bmad-2d-unity-game-dev/tasks/correct-course-game.md +151 -0
  149. package/expansion-packs/bmad-2d-unity-game-dev/tasks/create-game-story.md +184 -0
  150. package/expansion-packs/bmad-2d-unity-game-dev/tasks/game-design-brainstorming.md +308 -0
  151. package/expansion-packs/bmad-2d-unity-game-dev/tasks/validate-game-story.md +200 -0
  152. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-architecture-tmpl.yaml +1030 -0
  153. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-brief-tmpl.yaml +356 -0
  154. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-design-doc-tmpl.yaml +705 -0
  155. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-story-tmpl.yaml +256 -0
  156. package/expansion-packs/bmad-2d-unity-game-dev/templates/level-design-doc-tmpl.yaml +484 -0
  157. package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-dev-greenfield.yaml +183 -0
  158. package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-prototype.yaml +175 -0
  159. package/expansion-packs/bmad-infrastructure-devops/README.md +147 -0
  160. package/expansion-packs/bmad-infrastructure-devops/agents/infra-devops-platform.md +71 -0
  161. package/expansion-packs/bmad-infrastructure-devops/checklists/infrastructure-checklist.md +484 -0
  162. package/expansion-packs/bmad-infrastructure-devops/config.yaml +9 -0
  163. package/expansion-packs/bmad-infrastructure-devops/data/bmad-kb.md +308 -0
  164. package/expansion-packs/bmad-infrastructure-devops/tasks/review-infrastructure.md +160 -0
  165. package/expansion-packs/bmad-infrastructure-devops/tasks/validate-infrastructure.md +154 -0
  166. package/expansion-packs/bmad-infrastructure-devops/templates/infrastructure-architecture-tmpl.yaml +424 -0
  167. package/expansion-packs/bmad-infrastructure-devops/templates/infrastructure-platform-from-arch-tmpl.yaml +629 -0
  168. package/package.json +62 -24
  169. package/tools/bmad-npx-wrapper.js +39 -0
  170. package/tools/builders/web-builder.js +681 -0
  171. package/tools/bump-all-versions.js +106 -0
  172. package/tools/bump-expansion-version.js +83 -0
  173. package/tools/cli.js +154 -0
  174. package/tools/flattener/aggregate.js +76 -0
  175. package/tools/flattener/binary.js +53 -0
  176. package/tools/flattener/discovery.js +70 -0
  177. package/tools/flattener/files.js +35 -0
  178. package/tools/flattener/ignoreRules.js +176 -0
  179. package/tools/flattener/main.js +217 -0
  180. package/tools/flattener/projectRoot.js +45 -0
  181. package/tools/flattener/prompts.js +44 -0
  182. package/tools/flattener/stats.js +30 -0
  183. package/tools/flattener/xml.js +86 -0
  184. package/tools/installer/README.md +8 -0
  185. package/tools/installer/package.json +44 -0
  186. package/tools/lib/dependency-resolver.js +179 -0
  187. package/tools/lib/yaml-utils.js +29 -0
  188. package/tools/md-assets/web-agent-startup-instructions.md +39 -0
  189. package/tools/semantic-release-sync-installer.js +30 -0
  190. package/tools/shared/bannerArt.js +105 -0
  191. package/tools/sync-installer-version.js +34 -0
  192. package/tools/update-expansion-version.js +54 -0
  193. package/tools/upgraders/v3-to-v4-upgrader.js +763 -0
  194. package/tools/version-bump.js +79 -0
  195. package/tools/yaml-format.js +240 -0
  196. /package/{bin → tools/installer/bin}/bmad.js +0 -0
  197. /package/{config → tools/installer/config}/ide-agent-config.yaml +0 -0
  198. /package/{config → tools/installer/config}/install.config.yaml +0 -0
  199. /package/{lib → tools/installer/lib}/config-loader.js +0 -0
  200. /package/{lib → tools/installer/lib}/file-manager.js +0 -0
  201. /package/{lib → tools/installer/lib}/ide-base-setup.js +0 -0
  202. /package/{lib → tools/installer/lib}/ide-setup.js +0 -0
  203. /package/{lib → tools/installer/lib}/installer.js +0 -0
  204. /package/{lib → tools/installer/lib}/memory-profiler.js +0 -0
  205. /package/{lib → tools/installer/lib}/module-manager.js +0 -0
  206. /package/{lib → tools/installer/lib}/resource-locator.js +0 -0
@@ -0,0 +1,176 @@
1
+ const fs = require("fs-extra");
2
+ const path = require("node:path");
3
+ const ignore = require("ignore");
4
+
5
+ // Central default ignore patterns for discovery and filtering.
6
+ // These complement .gitignore and are applied regardless of VCS presence.
7
+ const DEFAULT_PATTERNS = [
8
+ // Project/VCS
9
+ "**/.bmad-core/**",
10
+ "**/.git/**",
11
+ "**/.svn/**",
12
+ "**/.hg/**",
13
+ "**/.bzr/**",
14
+ // Package/build outputs
15
+ "**/node_modules/**",
16
+ "**/bower_components/**",
17
+ "**/vendor/**",
18
+ "**/packages/**",
19
+ "**/build/**",
20
+ "**/dist/**",
21
+ "**/out/**",
22
+ "**/target/**",
23
+ "**/bin/**",
24
+ "**/obj/**",
25
+ "**/release/**",
26
+ "**/debug/**",
27
+ // Environments
28
+ "**/.venv/**",
29
+ "**/venv/**",
30
+ "**/.virtualenv/**",
31
+ "**/virtualenv/**",
32
+ "**/env/**",
33
+ // Logs & coverage
34
+ "**/*.log",
35
+ "**/npm-debug.log*",
36
+ "**/yarn-debug.log*",
37
+ "**/yarn-error.log*",
38
+ "**/lerna-debug.log*",
39
+ "**/coverage/**",
40
+ "**/.nyc_output/**",
41
+ "**/.coverage/**",
42
+ "**/test-results/**",
43
+ // Caches & temp
44
+ "**/.cache/**",
45
+ "**/.tmp/**",
46
+ "**/.temp/**",
47
+ "**/tmp/**",
48
+ "**/temp/**",
49
+ "**/.sass-cache/**",
50
+ // IDE/editor
51
+ "**/.vscode/**",
52
+ "**/.idea/**",
53
+ "**/*.swp",
54
+ "**/*.swo",
55
+ "**/*~",
56
+ "**/.project",
57
+ "**/.classpath",
58
+ "**/.settings/**",
59
+ "**/*.sublime-project",
60
+ "**/*.sublime-workspace",
61
+ // Lockfiles
62
+ "**/package-lock.json",
63
+ "**/yarn.lock",
64
+ "**/pnpm-lock.yaml",
65
+ "**/composer.lock",
66
+ "**/Pipfile.lock",
67
+ // Python/Java/compiled artifacts
68
+ "**/*.pyc",
69
+ "**/*.pyo",
70
+ "**/*.pyd",
71
+ "**/__pycache__/**",
72
+ "**/*.class",
73
+ "**/*.jar",
74
+ "**/*.war",
75
+ "**/*.ear",
76
+ "**/*.o",
77
+ "**/*.so",
78
+ "**/*.dll",
79
+ "**/*.exe",
80
+ // System junk
81
+ "**/lib64/**",
82
+ "**/.venv/lib64/**",
83
+ "**/venv/lib64/**",
84
+ "**/_site/**",
85
+ "**/.jekyll-cache/**",
86
+ "**/.jekyll-metadata",
87
+ "**/.DS_Store",
88
+ "**/.DS_Store?",
89
+ "**/._*",
90
+ "**/.Spotlight-V100/**",
91
+ "**/.Trashes/**",
92
+ "**/ehthumbs.db",
93
+ "**/Thumbs.db",
94
+ "**/desktop.ini",
95
+ // XML outputs
96
+ "**/flattened-codebase.xml",
97
+ "**/repomix-output.xml",
98
+ // Images, media, fonts, archives, docs, dylibs
99
+ "**/*.jpg",
100
+ "**/*.jpeg",
101
+ "**/*.png",
102
+ "**/*.gif",
103
+ "**/*.bmp",
104
+ "**/*.ico",
105
+ "**/*.svg",
106
+ "**/*.pdf",
107
+ "**/*.doc",
108
+ "**/*.docx",
109
+ "**/*.xls",
110
+ "**/*.xlsx",
111
+ "**/*.ppt",
112
+ "**/*.pptx",
113
+ "**/*.zip",
114
+ "**/*.tar",
115
+ "**/*.gz",
116
+ "**/*.rar",
117
+ "**/*.7z",
118
+ "**/*.dylib",
119
+ "**/*.mp3",
120
+ "**/*.mp4",
121
+ "**/*.avi",
122
+ "**/*.mov",
123
+ "**/*.wav",
124
+ "**/*.ttf",
125
+ "**/*.otf",
126
+ "**/*.woff",
127
+ "**/*.woff2",
128
+ // Env files
129
+ "**/.env",
130
+ "**/.env.*",
131
+ "**/*.env",
132
+ // Misc
133
+ "**/junit.xml",
134
+ ];
135
+
136
+ async function readIgnoreFile(filePath) {
137
+ try {
138
+ if (!await fs.pathExists(filePath)) return [];
139
+ const content = await fs.readFile(filePath, "utf8");
140
+ return content
141
+ .split("\n")
142
+ .map((l) => l.trim())
143
+ .filter((l) => l && !l.startsWith("#"));
144
+ } catch (err) {
145
+ return [];
146
+ }
147
+ }
148
+
149
+ // Backward compatible export matching previous signature
150
+ async function parseGitignore(gitignorePath) {
151
+ return readIgnoreFile(gitignorePath);
152
+ }
153
+
154
+ async function loadIgnore(rootDir, extraPatterns = []) {
155
+ const ig = ignore();
156
+ const gitignorePath = path.join(rootDir, ".gitignore");
157
+ const patterns = [
158
+ ...await readIgnoreFile(gitignorePath),
159
+ ...DEFAULT_PATTERNS,
160
+ ...extraPatterns,
161
+ ];
162
+ // De-duplicate
163
+ const unique = Array.from(new Set(patterns.map((p) => String(p))));
164
+ ig.add(unique);
165
+
166
+ // Include-only filter: return true if path should be included
167
+ const filter = (relativePath) => !ig.ignores(relativePath.replace(/\\/g, "/"));
168
+
169
+ return { ig, filter, patterns: unique };
170
+ }
171
+
172
+ module.exports = {
173
+ DEFAULT_PATTERNS,
174
+ parseGitignore,
175
+ loadIgnore,
176
+ };
@@ -0,0 +1,217 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { Command } = require("commander");
4
+ const fs = require("fs-extra");
5
+ const path = require("node:path");
6
+ const process = require("node:process");
7
+
8
+ // Modularized components
9
+ const { findProjectRoot } = require("./projectRoot.js");
10
+ const { promptYesNo, promptPath } = require("./prompts.js");
11
+ const {
12
+ discoverFiles,
13
+ filterFiles,
14
+ aggregateFileContents,
15
+ } = require("./files.js");
16
+ const { generateXMLOutput } = require("./xml.js");
17
+ const { calculateStatistics } = require("./stats.js");
18
+
19
+ /**
20
+ * Recursively discover all files in a directory
21
+ * @param {string} rootDir - The root directory to scan
22
+ * @returns {Promise<string[]>} Array of file paths
23
+ */
24
+
25
+ /**
26
+ * Parse .gitignore file and return ignore patterns
27
+ * @param {string} gitignorePath - Path to .gitignore file
28
+ * @returns {Promise<string[]>} Array of ignore patterns
29
+ */
30
+
31
+ /**
32
+ * Check if a file is binary using file command and heuristics
33
+ * @param {string} filePath - Path to the file
34
+ * @returns {Promise<boolean>} True if file is binary
35
+ */
36
+
37
+ /**
38
+ * Read and aggregate content from text files
39
+ * @param {string[]} files - Array of file paths
40
+ * @param {string} rootDir - The root directory
41
+ * @param {Object} spinner - Optional spinner instance for progress display
42
+ * @returns {Promise<Object>} Object containing file contents and metadata
43
+ */
44
+
45
+ /**
46
+ * Generate XML output with aggregated file contents using streaming
47
+ * @param {Object} aggregatedContent - The aggregated content object
48
+ * @param {string} outputPath - The output file path
49
+ * @returns {Promise<void>} Promise that resolves when writing is complete
50
+ */
51
+
52
+ /**
53
+ * Calculate statistics for the processed files
54
+ * @param {Object} aggregatedContent - The aggregated content object
55
+ * @param {number} xmlFileSize - The size of the generated XML file in bytes
56
+ * @returns {Object} Statistics object
57
+ */
58
+
59
+ /**
60
+ * Filter files based on .gitignore patterns
61
+ * @param {string[]} files - Array of file paths
62
+ * @param {string} rootDir - The root directory
63
+ * @returns {Promise<string[]>} Filtered array of file paths
64
+ */
65
+
66
+ /**
67
+ * Attempt to find the project root by walking up from startDir
68
+ * Looks for common project markers like .git, package.json, pyproject.toml, etc.
69
+ * @param {string} startDir
70
+ * @returns {Promise<string|null>} project root directory or null if not found
71
+ */
72
+
73
+ const program = new Command();
74
+
75
+ program
76
+ .name("bmad-flatten")
77
+ .description("BMad-Method codebase flattener tool")
78
+ .version("1.0.0")
79
+ .option("-i, --input <path>", "Input directory to flatten", process.cwd())
80
+ .option("-o, --output <path>", "Output file path", "flattened-codebase.xml")
81
+ .action(async (options) => {
82
+ let inputDir = path.resolve(options.input);
83
+ let outputPath = path.resolve(options.output);
84
+
85
+ // Detect if user explicitly provided -i/--input or -o/--output
86
+ const argv = process.argv.slice(2);
87
+ const userSpecifiedInput = argv.some((a) =>
88
+ a === "-i" || a === "--input" || a.startsWith("--input=")
89
+ );
90
+ const userSpecifiedOutput = argv.some((a) =>
91
+ a === "-o" || a === "--output" || a.startsWith("--output=")
92
+ );
93
+ const noPathArgs = !userSpecifiedInput && !userSpecifiedOutput;
94
+
95
+ if (noPathArgs) {
96
+ const detectedRoot = await findProjectRoot(process.cwd());
97
+ const suggestedOutput = detectedRoot
98
+ ? path.join(detectedRoot, "flattened-codebase.xml")
99
+ : path.resolve("flattened-codebase.xml");
100
+
101
+ if (detectedRoot) {
102
+ const useDefaults = await promptYesNo(
103
+ `Detected project root at "${detectedRoot}". Use it as input and write output to "${suggestedOutput}"?`,
104
+ true,
105
+ );
106
+ if (useDefaults) {
107
+ inputDir = detectedRoot;
108
+ outputPath = suggestedOutput;
109
+ } else {
110
+ inputDir = await promptPath(
111
+ "Enter input directory path",
112
+ process.cwd(),
113
+ );
114
+ outputPath = await promptPath(
115
+ "Enter output file path",
116
+ path.join(inputDir, "flattened-codebase.xml"),
117
+ );
118
+ }
119
+ } else {
120
+ console.log("Could not auto-detect a project root.");
121
+ inputDir = await promptPath(
122
+ "Enter input directory path",
123
+ process.cwd(),
124
+ );
125
+ outputPath = await promptPath(
126
+ "Enter output file path",
127
+ path.join(inputDir, "flattened-codebase.xml"),
128
+ );
129
+ }
130
+ } else {
131
+ console.error(
132
+ "Could not auto-detect a project root and no arguments were provided. Please specify -i/--input and -o/--output.",
133
+ );
134
+ process.exit(1);
135
+ }
136
+
137
+ // Ensure output directory exists
138
+ await fs.ensureDir(path.dirname(outputPath));
139
+
140
+ console.log(`Flattening codebase from: ${inputDir}`);
141
+ console.log(`Output file: ${outputPath}`);
142
+
143
+ try {
144
+ // Verify input directory exists
145
+ if (!await fs.pathExists(inputDir)) {
146
+ console.error(`❌ Error: Input directory does not exist: ${inputDir}`);
147
+ process.exit(1);
148
+ }
149
+
150
+ // Import ora dynamically
151
+ const { default: ora } = await import("ora");
152
+
153
+ // Start file discovery with spinner
154
+ const discoverySpinner = ora("🔍 Discovering files...").start();
155
+ const files = await discoverFiles(inputDir);
156
+ const filteredFiles = await filterFiles(files, inputDir);
157
+ discoverySpinner.succeed(
158
+ `📁 Found ${filteredFiles.length} files to include`,
159
+ );
160
+
161
+ // Process files with progress tracking
162
+ console.log("Reading file contents");
163
+ const processingSpinner = ora("📄 Processing files...").start();
164
+ const aggregatedContent = await aggregateFileContents(
165
+ filteredFiles,
166
+ inputDir,
167
+ processingSpinner,
168
+ );
169
+ processingSpinner.succeed(
170
+ `✅ Processed ${aggregatedContent.processedFiles}/${filteredFiles.length} files`,
171
+ );
172
+ if (aggregatedContent.errors.length > 0) {
173
+ console.log(`Errors: ${aggregatedContent.errors.length}`);
174
+ }
175
+ console.log(`Text files: ${aggregatedContent.textFiles.length}`);
176
+ if (aggregatedContent.binaryFiles.length > 0) {
177
+ console.log(`Binary files: ${aggregatedContent.binaryFiles.length}`);
178
+ }
179
+
180
+ // Generate XML output using streaming
181
+ const xmlSpinner = ora("🔧 Generating XML output...").start();
182
+ await generateXMLOutput(aggregatedContent, outputPath);
183
+ xmlSpinner.succeed("📝 XML generation completed");
184
+
185
+ // Calculate and display statistics
186
+ const outputStats = await fs.stat(outputPath);
187
+ const stats = calculateStatistics(aggregatedContent, outputStats.size);
188
+
189
+ // Display completion summary
190
+ console.log("\n📊 Completion Summary:");
191
+ console.log(
192
+ `✅ Successfully processed ${filteredFiles.length} files into ${
193
+ path.basename(outputPath)
194
+ }`,
195
+ );
196
+ console.log(`📁 Output file: ${outputPath}`);
197
+ console.log(`📏 Total source size: ${stats.totalSize}`);
198
+ console.log(`📄 Generated XML size: ${stats.xmlSize}`);
199
+ console.log(
200
+ `📝 Total lines of code: ${stats.totalLines.toLocaleString()}`,
201
+ );
202
+ console.log(`🔢 Estimated tokens: ${stats.estimatedTokens}`);
203
+ console.log(
204
+ `📊 File breakdown: ${stats.textFiles} text, ${stats.binaryFiles} binary, ${stats.errorFiles} errors`,
205
+ );
206
+ } catch (error) {
207
+ console.error("❌ Critical error:", error.message);
208
+ console.error("An unexpected error occurred.");
209
+ process.exit(1);
210
+ }
211
+ });
212
+
213
+ if (require.main === module) {
214
+ program.parse();
215
+ }
216
+
217
+ module.exports = program;
@@ -0,0 +1,45 @@
1
+ const fs = require("fs-extra");
2
+ const path = require("node:path");
3
+
4
+ /**
5
+ * Attempt to find the project root by walking up from startDir
6
+ * Looks for common project markers like .git, package.json, pyproject.toml, etc.
7
+ * @param {string} startDir
8
+ * @returns {Promise<string|null>} project root directory or null if not found
9
+ */
10
+ async function findProjectRoot(startDir) {
11
+ try {
12
+ let dir = path.resolve(startDir);
13
+ const root = path.parse(dir).root;
14
+ const markers = [
15
+ ".git",
16
+ "package.json",
17
+ "pnpm-workspace.yaml",
18
+ "yarn.lock",
19
+ "pnpm-lock.yaml",
20
+ "pyproject.toml",
21
+ "requirements.txt",
22
+ "go.mod",
23
+ "Cargo.toml",
24
+ "composer.json",
25
+ ".hg",
26
+ ".svn",
27
+ ];
28
+
29
+ while (true) {
30
+ const exists = await Promise.all(
31
+ markers.map((m) => fs.pathExists(path.join(dir, m))),
32
+ );
33
+ if (exists.some(Boolean)) {
34
+ return dir;
35
+ }
36
+ if (dir === root) break;
37
+ dir = path.dirname(dir);
38
+ }
39
+ return null;
40
+ } catch {
41
+ return null;
42
+ }
43
+ }
44
+
45
+ module.exports = { findProjectRoot };
@@ -0,0 +1,44 @@
1
+ const os = require("node:os");
2
+ const path = require("node:path");
3
+ const readline = require("node:readline");
4
+ const process = require("node:process");
5
+
6
+ function expandHome(p) {
7
+ if (!p) return p;
8
+ if (p.startsWith("~")) return path.join(os.homedir(), p.slice(1));
9
+ return p;
10
+ }
11
+
12
+ function createRl() {
13
+ return readline.createInterface({
14
+ input: process.stdin,
15
+ output: process.stdout,
16
+ });
17
+ }
18
+
19
+ function promptQuestion(question) {
20
+ return new Promise((resolve) => {
21
+ const rl = createRl();
22
+ rl.question(question, (answer) => {
23
+ rl.close();
24
+ resolve(answer);
25
+ });
26
+ });
27
+ }
28
+
29
+ async function promptYesNo(question, defaultYes = true) {
30
+ const suffix = defaultYes ? " [Y/n] " : " [y/N] ";
31
+ const ans = (await promptQuestion(`${question}${suffix}`)).trim().toLowerCase();
32
+ if (!ans) return defaultYes;
33
+ if (["y", "yes"].includes(ans)) return true;
34
+ if (["n", "no"].includes(ans)) return false;
35
+ return promptYesNo(question, defaultYes);
36
+ }
37
+
38
+ async function promptPath(question, defaultValue) {
39
+ const prompt = `${question}${defaultValue ? ` (default: ${defaultValue})` : ""}: `;
40
+ const ans = (await promptQuestion(prompt)).trim();
41
+ return expandHome(ans || defaultValue);
42
+ }
43
+
44
+ module.exports = { promptYesNo, promptPath, promptQuestion, expandHome };
@@ -0,0 +1,30 @@
1
+ function calculateStatistics(aggregatedContent, xmlFileSize) {
2
+ const { textFiles, binaryFiles, errors } = aggregatedContent;
3
+
4
+ const totalTextSize = textFiles.reduce((sum, file) => sum + file.size, 0);
5
+ const totalBinarySize = binaryFiles.reduce((sum, file) => sum + file.size, 0);
6
+ const totalSize = totalTextSize + totalBinarySize;
7
+
8
+ const totalLines = textFiles.reduce((sum, file) => sum + file.lines, 0);
9
+
10
+ const estimatedTokens = Math.ceil(xmlFileSize / 4);
11
+
12
+ const formatSize = (bytes) => {
13
+ if (bytes < 1024) return `${bytes} B`;
14
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
15
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
16
+ };
17
+
18
+ return {
19
+ totalFiles: textFiles.length + binaryFiles.length,
20
+ textFiles: textFiles.length,
21
+ binaryFiles: binaryFiles.length,
22
+ errorFiles: errors.length,
23
+ totalSize: formatSize(totalSize),
24
+ xmlSize: formatSize(xmlFileSize),
25
+ totalLines,
26
+ estimatedTokens: estimatedTokens.toLocaleString(),
27
+ };
28
+ }
29
+
30
+ module.exports = { calculateStatistics };
@@ -0,0 +1,86 @@
1
+ const fs = require("fs-extra");
2
+
3
+ function escapeXml(str) {
4
+ if (typeof str !== "string") {
5
+ return String(str);
6
+ }
7
+ return str
8
+ .replace(/&/g, "&amp;")
9
+ .replace(/</g, "&lt;")
10
+ .replace(/'/g, "&apos;");
11
+ }
12
+
13
+ function indentFileContent(content) {
14
+ if (typeof content !== "string") {
15
+ return String(content);
16
+ }
17
+ return content.split("\n").map((line) => ` ${line}`);
18
+ }
19
+
20
+ function generateXMLOutput(aggregatedContent, outputPath) {
21
+ const { textFiles } = aggregatedContent;
22
+ const writeStream = fs.createWriteStream(outputPath, { encoding: "utf8" });
23
+
24
+ return new Promise((resolve, reject) => {
25
+ writeStream.on("error", reject);
26
+ writeStream.on("finish", resolve);
27
+
28
+ writeStream.write('<?xml version="1.0" encoding="UTF-8"?>\n');
29
+ writeStream.write("<files>\n");
30
+
31
+ // Sort files by path for deterministic order
32
+ const filesSorted = [...textFiles].sort((a, b) =>
33
+ a.path.localeCompare(b.path)
34
+ );
35
+ let index = 0;
36
+
37
+ const writeNext = () => {
38
+ if (index >= filesSorted.length) {
39
+ writeStream.write("</files>\n");
40
+ writeStream.end();
41
+ return;
42
+ }
43
+
44
+ const file = filesSorted[index++];
45
+ const p = escapeXml(file.path);
46
+ const content = typeof file.content === "string" ? file.content : "";
47
+
48
+ if (content.length === 0) {
49
+ writeStream.write(`\t<file path='${p}'/>\n`);
50
+ setTimeout(writeNext, 0);
51
+ return;
52
+ }
53
+
54
+ const needsCdata = content.includes("<") || content.includes("&") ||
55
+ content.includes("]]>");
56
+ if (needsCdata) {
57
+ // Open tag and CDATA on their own line with tab indent; content lines indented with two tabs
58
+ writeStream.write(`\t<file path='${p}'><![CDATA[\n`);
59
+ // Safely split any occurrences of "]]>" inside content, trim trailing newlines, indent each line with two tabs
60
+ const safe = content.replace(/]]>/g, "]]]]><![CDATA[>");
61
+ const trimmed = safe.replace(/[\r\n]+$/, "");
62
+ const indented = trimmed.length > 0
63
+ ? trimmed.split("\n").map((line) => `\t\t${line}`).join("\n")
64
+ : "";
65
+ writeStream.write(indented);
66
+ // Close CDATA and attach closing tag directly after the last content line
67
+ writeStream.write("]]></file>\n");
68
+ } else {
69
+ // Write opening tag then newline; indent content with two tabs; attach closing tag directly after last content char
70
+ writeStream.write(`\t<file path='${p}'>\n`);
71
+ const trimmed = content.replace(/[\r\n]+$/, "");
72
+ const indented = trimmed.length > 0
73
+ ? trimmed.split("\n").map((line) => `\t\t${line}`).join("\n")
74
+ : "";
75
+ writeStream.write(indented);
76
+ writeStream.write(`</file>\n`);
77
+ }
78
+
79
+ setTimeout(writeNext, 0);
80
+ };
81
+
82
+ writeNext();
83
+ });
84
+ }
85
+
86
+ module.exports = { generateXMLOutput };
@@ -0,0 +1,8 @@
1
+ # BMad Method Installer
2
+
3
+ ## Usage
4
+
5
+ ```bash
6
+ # Interactive installation
7
+ npx bmad-method install
8
+ ```
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "bmad-method",
3
+ "version": "4.37.0-beta.6",
4
+ "description": "BMad Method installer - AI-powered Agile development framework",
5
+ "main": "lib/installer.js",
6
+ "bin": {
7
+ "bmad": "./bin/bmad.js",
8
+ "bmad-method": "./bin/bmad.js"
9
+ },
10
+ "scripts": {
11
+ "test": "echo \"Error: no test specified\" && exit 1"
12
+ },
13
+ "keywords": [
14
+ "bmad",
15
+ "agile",
16
+ "ai",
17
+ "development",
18
+ "framework",
19
+ "installer",
20
+ "agents"
21
+ ],
22
+ "author": "BMad Team",
23
+ "license": "MIT",
24
+ "dependencies": {
25
+ "chalk": "^4.1.2",
26
+ "commander": "^14.0.0",
27
+ "fs-extra": "^11.3.0",
28
+ "inquirer": "^8.2.6",
29
+ "js-yaml": "^4.1.0",
30
+ "ora": "^5.4.1",
31
+ "semver": "^7.6.3"
32
+ },
33
+ "engines": {
34
+ "node": ">=20.0.0"
35
+ },
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "https://github.com/bmad-team/bmad-method.git"
39
+ },
40
+ "bugs": {
41
+ "url": "https://github.com/bmad-team/bmad-method/issues"
42
+ },
43
+ "homepage": "https://github.com/bmad-team/bmad-method#readme"
44
+ }