bmad-method 5.0.0 → 5.0.1

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 (329) 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 +25 -0
  5. package/.github/workflows/format-check.yaml +42 -0
  6. package/.github/workflows/manual-release.yaml +173 -0
  7. package/.husky/pre-commit +3 -2
  8. package/.vscode/settings.json +67 -74
  9. package/CHANGELOG.md +564 -19
  10. package/CONTRIBUTING.md +168 -5
  11. package/LICENSE +1 -1
  12. package/README.md +146 -218
  13. package/bmad-core/agent-teams/team-all.yaml +14 -0
  14. package/bmad-core/agent-teams/team-fullstack.yaml +18 -0
  15. package/bmad-core/agent-teams/team-ide-minimal.yaml +10 -0
  16. package/bmad-core/agent-teams/team-no-ui.yaml +13 -0
  17. package/bmad-core/agents/analyst.md +81 -0
  18. package/bmad-core/agents/architect.md +83 -0
  19. package/bmad-core/agents/bmad-master.md +107 -0
  20. package/bmad-core/agents/bmad-orchestrator.md +149 -0
  21. package/bmad-core/agents/dev.md +75 -0
  22. package/bmad-core/agents/pm.md +81 -0
  23. package/bmad-core/agents/po.md +76 -0
  24. package/bmad-core/agents/qa.md +88 -0
  25. package/bmad-core/agents/sm.md +62 -0
  26. package/bmad-core/agents/ux-expert.md +66 -0
  27. package/{.bmad-core → bmad-core}/checklists/architect-checklist.md +0 -5
  28. package/{.bmad-core → bmad-core}/checklists/change-checklist.md +2 -2
  29. package/{.bmad-core → bmad-core}/checklists/pm-checklist.md +0 -5
  30. package/{.bmad-core → bmad-core}/checklists/po-master-checklist.md +0 -9
  31. package/{.bmad-core → bmad-core}/checklists/story-dod-checklist.md +0 -7
  32. package/{.bmad-core → bmad-core}/checklists/story-draft-checklist.md +1 -4
  33. package/bmad-core/core-config.yaml +20 -0
  34. package/bmad-core/data/bmad-kb.md +806 -0
  35. package/bmad-core/data/brainstorming-techniques.md +36 -0
  36. package/bmad-core/data/elicitation-methods.md +154 -0
  37. package/bmad-core/data/test-levels-framework.md +146 -0
  38. package/bmad-core/data/test-priorities-matrix.md +172 -0
  39. package/bmad-core/tasks/advanced-elicitation.md +117 -0
  40. package/{.bmad-core → bmad-core}/tasks/correct-course.md +9 -12
  41. package/bmad-core/tasks/create-brownfield-story.md +312 -0
  42. package/{.bmad-core → bmad-core}/tasks/create-deep-research-prompt.md +4 -27
  43. package/bmad-core/tasks/create-next-story.md +112 -0
  44. package/bmad-core/tasks/document-project.md +343 -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 → bmad-core}/tasks/index-docs.md +3 -13
  48. package/bmad-core/tasks/kb-mode-interaction.md +75 -0
  49. package/bmad-core/tasks/nfr-assess.md +343 -0
  50. package/bmad-core/tasks/qa-gate.md +159 -0
  51. package/bmad-core/tasks/review-story.md +314 -0
  52. package/bmad-core/tasks/risk-profile.md +353 -0
  53. package/{.bmad-core → bmad-core}/tasks/shard-doc.md +27 -15
  54. package/bmad-core/tasks/test-design.md +174 -0
  55. package/bmad-core/tasks/trace-requirements.md +264 -0
  56. package/bmad-core/tasks/validate-next-story.md +134 -0
  57. package/bmad-core/templates/architecture-tmpl.yaml +650 -0
  58. package/bmad-core/templates/brainstorming-output-tmpl.yaml +156 -0
  59. package/bmad-core/templates/brownfield-architecture-tmpl.yaml +476 -0
  60. package/bmad-core/templates/brownfield-prd-tmpl.yaml +280 -0
  61. package/bmad-core/templates/competitor-analysis-tmpl.yaml +306 -0
  62. package/bmad-core/templates/front-end-architecture-tmpl.yaml +218 -0
  63. package/bmad-core/templates/front-end-spec-tmpl.yaml +349 -0
  64. package/bmad-core/templates/fullstack-architecture-tmpl.yaml +823 -0
  65. package/bmad-core/templates/market-research-tmpl.yaml +252 -0
  66. package/bmad-core/templates/prd-tmpl.yaml +202 -0
  67. package/bmad-core/templates/project-brief-tmpl.yaml +221 -0
  68. package/bmad-core/templates/qa-gate-tmpl.yaml +102 -0
  69. package/bmad-core/templates/story-tmpl.yaml +137 -0
  70. package/bmad-core/workflows/brownfield-fullstack.yaml +297 -0
  71. package/bmad-core/workflows/brownfield-service.yaml +187 -0
  72. package/bmad-core/workflows/brownfield-ui.yaml +197 -0
  73. package/{.bmad-core/workflows/greenfield-fullstack.yml → bmad-core/workflows/greenfield-fullstack.yaml} +140 -77
  74. package/bmad-core/workflows/greenfield-service.yaml +206 -0
  75. package/bmad-core/workflows/greenfield-ui.yaml +235 -0
  76. package/common/tasks/create-doc.md +101 -0
  77. package/{.bmad-core → common}/tasks/execute-checklist.md +2 -13
  78. package/common/utils/bmad-doc-template.md +325 -0
  79. package/common/utils/workflow-management.md +69 -0
  80. package/dist/agents/analyst.txt +2889 -0
  81. package/dist/agents/architect.txt +3552 -0
  82. package/dist/agents/bmad-master.txt +8769 -0
  83. package/dist/agents/bmad-orchestrator.txt +1513 -0
  84. package/dist/agents/dev.txt +414 -0
  85. package/{.bmad-core/web-bundles → dist}/agents/pm.txt +668 -1119
  86. package/{.bmad-core/web-bundles → dist}/agents/po.txt +341 -484
  87. package/dist/agents/qa.txt +1987 -0
  88. package/dist/agents/sm.txt +658 -0
  89. package/dist/agents/ux-expert.txt +694 -0
  90. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-designer.txt +2371 -0
  91. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.txt +1620 -0
  92. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.txt +815 -0
  93. package/dist/expansion-packs/bmad-2d-phaser-game-dev/teams/phaser-2d-nodejs-game-team.txt +10952 -0
  94. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-architect.txt +4012 -0
  95. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-designer.txt +3698 -0
  96. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-developer.txt +450 -0
  97. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-sm.txt +973 -0
  98. package/dist/expansion-packs/bmad-2d-unity-game-dev/teams/unity-2d-game-team.txt +15376 -0
  99. package/dist/expansion-packs/bmad-infrastructure-devops/agents/infra-devops-platform.txt +2075 -0
  100. package/dist/teams/team-all.txt +12682 -0
  101. package/dist/teams/team-fullstack.txt +10421 -0
  102. package/dist/teams/team-ide-minimal.txt +5103 -0
  103. package/dist/teams/team-no-ui.txt +8980 -0
  104. package/docs/GUIDING-PRINCIPLES.md +91 -0
  105. package/docs/core-architecture.md +219 -0
  106. package/docs/enhanced-ide-development-workflow.md +248 -0
  107. package/docs/expansion-packs.md +280 -0
  108. package/docs/how-to-contribute-with-pull-requests.md +158 -0
  109. package/docs/user-guide.md +504 -0
  110. package/docs/versioning-and-releases.md +115 -53
  111. package/docs/versions.md +4 -5
  112. package/docs/working-in-the-brownfield.md +597 -0
  113. package/eslint.config.mjs +119 -0
  114. package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/Complete AI Agent System - Flowchart.svg +102 -0
  115. 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
  116. 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
  117. 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
  118. 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
  119. 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
  120. 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
  121. package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/README.md +109 -0
  122. package/expansion-packs/README.md +2 -112
  123. package/expansion-packs/bmad-2d-phaser-game-dev/agent-teams/phaser-2d-nodejs-game-team.yaml +13 -0
  124. package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-designer.md +71 -0
  125. package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.md +78 -0
  126. package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.md +64 -0
  127. package/expansion-packs/bmad-2d-phaser-game-dev/checklists/game-design-checklist.md +201 -0
  128. package/expansion-packs/bmad-2d-phaser-game-dev/checklists/game-story-dod-checklist.md +160 -0
  129. package/expansion-packs/bmad-2d-phaser-game-dev/config.yaml +8 -0
  130. package/expansion-packs/bmad-2d-phaser-game-dev/data/bmad-kb.md +250 -0
  131. package/expansion-packs/bmad-2d-phaser-game-dev/data/development-guidelines.md +647 -0
  132. package/expansion-packs/bmad-2d-phaser-game-dev/tasks/advanced-elicitation.md +110 -0
  133. package/expansion-packs/bmad-2d-phaser-game-dev/tasks/create-game-story.md +216 -0
  134. package/expansion-packs/bmad-2d-phaser-game-dev/tasks/game-design-brainstorming.md +290 -0
  135. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-architecture-tmpl.yaml +613 -0
  136. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-brief-tmpl.yaml +356 -0
  137. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-design-doc-tmpl.yaml +343 -0
  138. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-story-tmpl.yaml +253 -0
  139. package/expansion-packs/bmad-2d-phaser-game-dev/templates/level-design-doc-tmpl.yaml +484 -0
  140. package/expansion-packs/bmad-2d-phaser-game-dev/workflows/game-dev-greenfield.yaml +183 -0
  141. package/expansion-packs/bmad-2d-phaser-game-dev/workflows/game-prototype.yaml +175 -0
  142. package/expansion-packs/bmad-2d-unity-game-dev/agent-teams/unity-2d-game-team.yaml +14 -0
  143. package/expansion-packs/bmad-2d-unity-game-dev/agents/game-architect.md +80 -0
  144. package/expansion-packs/bmad-2d-unity-game-dev/agents/game-designer.md +77 -0
  145. package/expansion-packs/bmad-2d-unity-game-dev/agents/game-developer.md +78 -0
  146. package/expansion-packs/bmad-2d-unity-game-dev/agents/game-sm.md +65 -0
  147. package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-architect-checklist.md +391 -0
  148. package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-change-checklist.md +203 -0
  149. package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-design-checklist.md +201 -0
  150. package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-story-dod-checklist.md +124 -0
  151. package/expansion-packs/bmad-2d-unity-game-dev/config.yaml +6 -0
  152. package/expansion-packs/bmad-2d-unity-game-dev/data/bmad-kb.md +769 -0
  153. package/expansion-packs/bmad-2d-unity-game-dev/data/development-guidelines.md +586 -0
  154. package/expansion-packs/bmad-2d-unity-game-dev/tasks/advanced-elicitation.md +110 -0
  155. package/expansion-packs/bmad-2d-unity-game-dev/tasks/correct-course-game.md +141 -0
  156. package/expansion-packs/bmad-2d-unity-game-dev/tasks/create-game-story.md +184 -0
  157. package/expansion-packs/bmad-2d-unity-game-dev/tasks/game-design-brainstorming.md +290 -0
  158. package/expansion-packs/bmad-2d-unity-game-dev/tasks/validate-game-story.md +200 -0
  159. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-architecture-tmpl.yaml +1030 -0
  160. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-brief-tmpl.yaml +356 -0
  161. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-design-doc-tmpl.yaml +705 -0
  162. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-story-tmpl.yaml +256 -0
  163. package/expansion-packs/bmad-2d-unity-game-dev/templates/level-design-doc-tmpl.yaml +484 -0
  164. package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-dev-greenfield.yaml +183 -0
  165. package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-prototype.yaml +175 -0
  166. package/expansion-packs/{infrastructure-devops → bmad-infrastructure-devops}/README.md +9 -9
  167. package/expansion-packs/{infrastructure-devops → bmad-infrastructure-devops}/agents/infra-devops-platform.md +30 -18
  168. package/expansion-packs/{infrastructure-devops → bmad-infrastructure-devops}/checklists/infrastructure-checklist.md +1 -1
  169. package/expansion-packs/bmad-infrastructure-devops/config.yaml +9 -0
  170. package/expansion-packs/bmad-infrastructure-devops/data/bmad-kb.md +305 -0
  171. package/expansion-packs/{infrastructure-devops → bmad-infrastructure-devops}/tasks/review-infrastructure.md +4 -5
  172. package/expansion-packs/{infrastructure-devops → bmad-infrastructure-devops}/tasks/validate-infrastructure.md +4 -5
  173. package/expansion-packs/bmad-infrastructure-devops/templates/infrastructure-architecture-tmpl.yaml +424 -0
  174. package/expansion-packs/bmad-infrastructure-devops/templates/infrastructure-platform-from-arch-tmpl.yaml +629 -0
  175. package/package.json +74 -42
  176. package/prettier.config.mjs +32 -0
  177. package/release_notes.md +30 -0
  178. package/tools/bmad-npx-wrapper.js +13 -15
  179. package/tools/builders/web-builder.js +544 -15
  180. package/tools/bump-all-versions.js +115 -0
  181. package/tools/bump-expansion-version.js +90 -0
  182. package/tools/cli.js +65 -32
  183. package/tools/flattener/aggregate.js +76 -0
  184. package/tools/flattener/binary.js +80 -0
  185. package/tools/flattener/discovery.js +71 -0
  186. package/tools/flattener/files.js +35 -0
  187. package/tools/flattener/ignoreRules.js +176 -0
  188. package/tools/flattener/main.js +573 -0
  189. package/tools/flattener/projectRoot.js +206 -0
  190. package/tools/flattener/prompts.js +44 -0
  191. package/tools/flattener/stats.helpers.js +395 -0
  192. package/tools/flattener/stats.js +80 -0
  193. package/tools/flattener/test-matrix.js +413 -0
  194. package/tools/flattener/xml.js +88 -0
  195. package/tools/installer/README.md +3 -53
  196. package/tools/installer/bin/bmad.js +475 -90
  197. package/tools/installer/config/ide-agent-config.yaml +58 -0
  198. package/tools/installer/config/install.config.yaml +123 -0
  199. package/tools/installer/lib/config-loader.js +208 -40
  200. package/tools/installer/lib/file-manager.js +258 -55
  201. package/tools/installer/lib/ide-base-setup.js +228 -0
  202. package/tools/installer/lib/ide-setup.js +1265 -253
  203. package/tools/installer/lib/installer.js +1651 -310
  204. package/tools/installer/lib/memory-profiler.js +225 -0
  205. package/tools/installer/lib/module-manager.js +114 -0
  206. package/tools/installer/lib/resource-locator.js +308 -0
  207. package/tools/installer/package.json +25 -24
  208. package/tools/lib/dependency-resolver.js +44 -48
  209. package/tools/lib/yaml-utils.js +29 -0
  210. package/tools/md-assets/web-agent-startup-instructions.md +39 -0
  211. package/tools/preview-release-notes.js +66 -0
  212. package/tools/shared/bannerArt.js +105 -0
  213. package/tools/sync-installer-version.js +7 -9
  214. package/tools/update-expansion-version.js +53 -0
  215. package/tools/upgraders/v3-to-v4-upgrader.js +221 -320
  216. package/tools/version-bump.js +42 -27
  217. package/tools/yaml-format.js +57 -44
  218. package/.bmad-core/agent-teams/team-all.yml +0 -16
  219. package/.bmad-core/agent-teams/team-fullstack.yml +0 -26
  220. package/.bmad-core/agent-teams/team-no-ui.yml +0 -15
  221. package/.bmad-core/agents/analyst.md +0 -59
  222. package/.bmad-core/agents/architect.md +0 -66
  223. package/.bmad-core/agents/bmad-master.md +0 -104
  224. package/.bmad-core/agents/bmad-orchestrator.md +0 -81
  225. package/.bmad-core/agents/dev.md +0 -70
  226. package/.bmad-core/agents/pm.md +0 -59
  227. package/.bmad-core/agents/po.md +0 -60
  228. package/.bmad-core/agents/qa.md +0 -52
  229. package/.bmad-core/agents/sm.md +0 -55
  230. package/.bmad-core/agents/ux-expert.md +0 -66
  231. package/.bmad-core/data/bmad-kb.md +0 -47
  232. package/.bmad-core/schemas/agent-team-schema.yml +0 -153
  233. package/.bmad-core/tasks/advanced-elicitation.md +0 -92
  234. package/.bmad-core/tasks/brainstorming-techniques.md +0 -238
  235. package/.bmad-core/tasks/core-dump.md +0 -74
  236. package/.bmad-core/tasks/create-agent.md +0 -202
  237. package/.bmad-core/tasks/create-doc.md +0 -74
  238. package/.bmad-core/tasks/create-expansion-pack.md +0 -425
  239. package/.bmad-core/tasks/create-next-story.md +0 -206
  240. package/.bmad-core/tasks/create-team.md +0 -229
  241. package/.bmad-core/tasks/doc-migration-task.md +0 -143
  242. package/.bmad-core/tasks/generate-ai-frontend-prompt.md +0 -58
  243. package/.bmad-core/templates/agent-tmpl.md +0 -58
  244. package/.bmad-core/templates/architecture-tmpl.md +0 -771
  245. package/.bmad-core/templates/brownfield-architecture-tmpl.md +0 -542
  246. package/.bmad-core/templates/brownfield-prd-tmpl.md +0 -240
  247. package/.bmad-core/templates/competitor-analysis-tmpl.md +0 -289
  248. package/.bmad-core/templates/expansion-pack-plan-tmpl.md +0 -91
  249. package/.bmad-core/templates/front-end-architecture-tmpl.md +0 -173
  250. package/.bmad-core/templates/front-end-spec-tmpl.md +0 -411
  251. package/.bmad-core/templates/fullstack-architecture-tmpl.md +0 -1016
  252. package/.bmad-core/templates/market-research-tmpl.md +0 -261
  253. package/.bmad-core/templates/prd-tmpl.md +0 -200
  254. package/.bmad-core/templates/project-brief-tmpl.md +0 -228
  255. package/.bmad-core/templates/simple-project-prd-tmpl.md +0 -461
  256. package/.bmad-core/templates/story-tmpl.md +0 -61
  257. package/.bmad-core/templates/web-agent-startup-instructions-template.md +0 -39
  258. package/.bmad-core/utils/agent-switcher.ide.md +0 -112
  259. package/.bmad-core/utils/template-format.md +0 -26
  260. package/.bmad-core/utils/workflow-management.md +0 -224
  261. package/.bmad-core/web-bundles/agents/analyst.txt +0 -1684
  262. package/.bmad-core/web-bundles/agents/architect.txt +0 -3584
  263. package/.bmad-core/web-bundles/agents/bmad-master.txt +0 -9491
  264. package/.bmad-core/web-bundles/agents/bmad-orchestrator.txt +0 -1466
  265. package/.bmad-core/web-bundles/agents/dev.txt +0 -316
  266. package/.bmad-core/web-bundles/agents/qa.txt +0 -129
  267. package/.bmad-core/web-bundles/agents/sm.txt +0 -658
  268. package/.bmad-core/web-bundles/agents/ux-expert.txt +0 -1099
  269. package/.bmad-core/web-bundles/teams/team-all.txt +0 -10757
  270. package/.bmad-core/web-bundles/teams/team-fullstack.txt +0 -10109
  271. package/.bmad-core/web-bundles/teams/team-no-ui.txt +0 -8950
  272. package/.bmad-core/workflows/brownfield-fullstack.yml +0 -116
  273. package/.bmad-core/workflows/brownfield-service.yml +0 -117
  274. package/.bmad-core/workflows/brownfield-ui.yml +0 -127
  275. package/.bmad-core/workflows/greenfield-service.yml +0 -143
  276. package/.bmad-core/workflows/greenfield-ui.yml +0 -172
  277. package/.claude/commands/analyst.md +0 -63
  278. package/.claude/commands/architect.md +0 -70
  279. package/.claude/commands/bmad-master.md +0 -108
  280. package/.claude/commands/bmad-orchestrator.md +0 -85
  281. package/.claude/commands/dev.md +0 -74
  282. package/.claude/commands/pm.md +0 -63
  283. package/.claude/commands/po.md +0 -64
  284. package/.claude/commands/qa.md +0 -56
  285. package/.claude/commands/sm.md +0 -59
  286. package/.claude/commands/ux-expert.md +0 -70
  287. package/.cursor/rules/analyst.mdc +0 -77
  288. package/.cursor/rules/architect.mdc +0 -84
  289. package/.cursor/rules/bmad-master.mdc +0 -122
  290. package/.cursor/rules/bmad-orchestrator.mdc +0 -99
  291. package/.cursor/rules/dev.mdc +0 -88
  292. package/.cursor/rules/pm.mdc +0 -77
  293. package/.cursor/rules/po.mdc +0 -78
  294. package/.cursor/rules/qa.mdc +0 -70
  295. package/.cursor/rules/sm.mdc +0 -73
  296. package/.cursor/rules/ux-expert.mdc +0 -84
  297. package/.github/workflows/release.yml +0 -59
  298. package/.releaserc.json +0 -18
  299. package/.roo/.roomodes +0 -95
  300. package/.roo/README.md +0 -38
  301. package/.vscode/extensions.json +0 -6
  302. package/.windsurf/rules/analyst.md +0 -71
  303. package/.windsurf/rules/architect.md +0 -78
  304. package/.windsurf/rules/bmad-master.md +0 -116
  305. package/.windsurf/rules/bmad-orchestrator.md +0 -93
  306. package/.windsurf/rules/dev.md +0 -82
  307. package/.windsurf/rules/pm.md +0 -71
  308. package/.windsurf/rules/po.md +0 -72
  309. package/.windsurf/rules/qa.md +0 -64
  310. package/.windsurf/rules/sm.md +0 -67
  311. package/.windsurf/rules/ux-expert.md +0 -78
  312. package/docs/bmad-workflow-guide.md +0 -161
  313. package/docs/claude-code-guide.md +0 -119
  314. package/docs/cursor-guide.md +0 -127
  315. package/docs/roo-code-guide.md +0 -140
  316. package/docs/sample-output/simple-fullstack-greenfield/prd.md +0 -42
  317. package/docs/windsurf-guide.md +0 -127
  318. package/expansion-packs/infrastructure-devops/manifest.yml +0 -38
  319. package/expansion-packs/infrastructure-devops/templates/infrastructure-architecture-tmpl.md +0 -415
  320. package/expansion-packs/infrastructure-devops/templates/infrastructure-platform-from-arch-tmpl.md +0 -0
  321. package/tools/installer/config/install.config.yml +0 -139
  322. package/tools/installer/package-lock.json +0 -906
  323. package/tools/installer/templates/claude-commands.md +0 -7
  324. package/tools/installer/templates/cursor-rules.md +0 -22
  325. package/tools/installer/templates/windsurf-rules.md +0 -22
  326. package/tools/semantic-release-sync-installer.js +0 -31
  327. /package/{.bmad-core → bmad-core}/data/technical-preferences.md +0 -0
  328. /package/{.bmad-core → bmad-core}/tasks/brownfield-create-epic.md +0 -0
  329. /package/{.bmad-core → bmad-core}/tasks/brownfield-create-story.md +0 -0
@@ -0,0 +1,573 @@
1
+ const { Command } = require('commander');
2
+ const fs = require('fs-extra');
3
+ const path = require('node:path');
4
+ const process = require('node:process');
5
+
6
+ // Modularized components
7
+ const { findProjectRoot } = require('./projectRoot.js');
8
+ const { promptYesNo, promptPath } = require('./prompts.js');
9
+ const { discoverFiles, filterFiles, aggregateFileContents } = require('./files.js');
10
+ const { generateXMLOutput } = require('./xml.js');
11
+ const { calculateStatistics } = require('./stats.js');
12
+
13
+ /**
14
+ * Recursively discover all files in a directory
15
+ * @param {string} rootDir - The root directory to scan
16
+ * @returns {Promise<string[]>} Array of file paths
17
+ */
18
+
19
+ /**
20
+ * Parse .gitignore file and return ignore patterns
21
+ * @param {string} gitignorePath - Path to .gitignore file
22
+ * @returns {Promise<string[]>} Array of ignore patterns
23
+ */
24
+
25
+ /**
26
+ * Check if a file is binary using file command and heuristics
27
+ * @param {string} filePath - Path to the file
28
+ * @returns {Promise<boolean>} True if file is binary
29
+ */
30
+
31
+ /**
32
+ * Read and aggregate content from text files
33
+ * @param {string[]} files - Array of file paths
34
+ * @param {string} rootDir - The root directory
35
+ * @param {Object} spinner - Optional spinner instance for progress display
36
+ * @returns {Promise<Object>} Object containing file contents and metadata
37
+ */
38
+
39
+ /**
40
+ * Generate XML output with aggregated file contents using streaming
41
+ * @param {Object} aggregatedContent - The aggregated content object
42
+ * @param {string} outputPath - The output file path
43
+ * @returns {Promise<void>} Promise that resolves when writing is complete
44
+ */
45
+
46
+ /**
47
+ * Calculate statistics for the processed files
48
+ * @param {Object} aggregatedContent - The aggregated content object
49
+ * @param {number} xmlFileSize - The size of the generated XML file in bytes
50
+ * @returns {Object} Statistics object
51
+ */
52
+
53
+ /**
54
+ * Filter files based on .gitignore patterns
55
+ * @param {string[]} files - Array of file paths
56
+ * @param {string} rootDir - The root directory
57
+ * @returns {Promise<string[]>} Filtered array of file paths
58
+ */
59
+
60
+ /**
61
+ * Attempt to find the project root by walking up from startDir
62
+ * Looks for common project markers like .git, package.json, pyproject.toml, etc.
63
+ * @param {string} startDir
64
+ * @returns {Promise<string|null>} project root directory or null if not found
65
+ */
66
+
67
+ const program = new Command();
68
+
69
+ program
70
+ .name('bmad-flatten')
71
+ .description('BMad-Method codebase flattener tool')
72
+ .version('1.0.0')
73
+ .option('-i, --input <path>', 'Input directory to flatten', process.cwd())
74
+ .option('-o, --output <path>', 'Output file path', 'flattened-codebase.xml')
75
+ .action(async (options) => {
76
+ let inputDir = path.resolve(options.input);
77
+ let outputPath = path.resolve(options.output);
78
+
79
+ // Detect if user explicitly provided -i/--input or -o/--output
80
+ const argv = process.argv.slice(2);
81
+ const userSpecifiedInput = argv.some(
82
+ (a) => a === '-i' || a === '--input' || a.startsWith('--input='),
83
+ );
84
+ const userSpecifiedOutput = argv.some(
85
+ (a) => a === '-o' || a === '--output' || a.startsWith('--output='),
86
+ );
87
+ const noPathArguments = !userSpecifiedInput && !userSpecifiedOutput;
88
+
89
+ if (noPathArguments) {
90
+ const detectedRoot = await findProjectRoot(process.cwd());
91
+ const suggestedOutput = detectedRoot
92
+ ? path.join(detectedRoot, 'flattened-codebase.xml')
93
+ : path.resolve('flattened-codebase.xml');
94
+
95
+ if (detectedRoot) {
96
+ const useDefaults = await promptYesNo(
97
+ `Detected project root at "${detectedRoot}". Use it as input and write output to "${suggestedOutput}"?`,
98
+ true,
99
+ );
100
+ if (useDefaults) {
101
+ inputDir = detectedRoot;
102
+ outputPath = suggestedOutput;
103
+ } else {
104
+ inputDir = await promptPath('Enter input directory path', process.cwd());
105
+ outputPath = await promptPath(
106
+ 'Enter output file path',
107
+ path.join(inputDir, 'flattened-codebase.xml'),
108
+ );
109
+ }
110
+ } else {
111
+ console.log('Could not auto-detect a project root.');
112
+ inputDir = await promptPath('Enter input directory path', process.cwd());
113
+ outputPath = await promptPath(
114
+ 'Enter output file path',
115
+ path.join(inputDir, 'flattened-codebase.xml'),
116
+ );
117
+ }
118
+ } else {
119
+ console.error(
120
+ 'Could not auto-detect a project root and no arguments were provided. Please specify -i/--input and -o/--output.',
121
+ );
122
+ process.exit(1);
123
+ }
124
+
125
+ // Ensure output directory exists
126
+ await fs.ensureDir(path.dirname(outputPath));
127
+
128
+ try {
129
+ // Verify input directory exists
130
+ if (!(await fs.pathExists(inputDir))) {
131
+ console.error(`❌ Error: Input directory does not exist: ${inputDir}`);
132
+ process.exit(1);
133
+ }
134
+
135
+ // Import ora dynamically
136
+ const { default: ora } = await import('ora');
137
+
138
+ // Start file discovery with spinner
139
+ const discoverySpinner = ora('🔍 Discovering files...').start();
140
+ const files = await discoverFiles(inputDir);
141
+ const filteredFiles = await filterFiles(files, inputDir);
142
+ discoverySpinner.succeed(`📁 Found ${filteredFiles.length} files to include`);
143
+
144
+ // Process files with progress tracking
145
+ console.log('Reading file contents');
146
+ const processingSpinner = ora('📄 Processing files...').start();
147
+ const aggregatedContent = await aggregateFileContents(
148
+ filteredFiles,
149
+ inputDir,
150
+ processingSpinner,
151
+ );
152
+ processingSpinner.succeed(
153
+ `✅ Processed ${aggregatedContent.processedFiles}/${filteredFiles.length} files`,
154
+ );
155
+ if (aggregatedContent.errors.length > 0) {
156
+ console.log(`Errors: ${aggregatedContent.errors.length}`);
157
+ }
158
+
159
+ // Generate XML output using streaming
160
+ const xmlSpinner = ora('🔧 Generating XML output...').start();
161
+ await generateXMLOutput(aggregatedContent, outputPath);
162
+ xmlSpinner.succeed('📝 XML generation completed');
163
+
164
+ // Calculate and display statistics
165
+ const outputStats = await fs.stat(outputPath);
166
+ const stats = await calculateStatistics(aggregatedContent, outputStats.size, inputDir);
167
+
168
+ // Display completion summary
169
+ console.log('\n📊 Completion Summary:');
170
+ console.log(
171
+ `✅ Successfully processed ${filteredFiles.length} files into ${path.basename(outputPath)}`,
172
+ );
173
+ console.log(`📁 Output file: ${outputPath}`);
174
+ console.log(`📏 Total source size: ${stats.totalSize}`);
175
+ console.log(`📄 Generated XML size: ${stats.xmlSize}`);
176
+ console.log(`📝 Total lines of code: ${stats.totalLines.toLocaleString()}`);
177
+ console.log(`🔢 Estimated tokens: ${stats.estimatedTokens}`);
178
+ console.log(
179
+ `📊 File breakdown: ${stats.textFiles} text, ${stats.binaryFiles} binary, ${stats.errorFiles} errors\n`,
180
+ );
181
+
182
+ // Ask user if they want detailed stats + markdown report
183
+ const generateDetailed = await promptYesNo(
184
+ 'Generate detailed stats (console + markdown) now?',
185
+ true,
186
+ );
187
+
188
+ if (generateDetailed) {
189
+ // Additional detailed stats
190
+ console.log('\n📈 Size Percentiles:');
191
+ console.log(
192
+ ` Avg: ${Math.round(stats.avgFileSize).toLocaleString()} B, Median: ${Math.round(
193
+ stats.medianFileSize,
194
+ ).toLocaleString()} B, p90: ${stats.p90.toLocaleString()} B, p95: ${stats.p95.toLocaleString()} B, p99: ${stats.p99.toLocaleString()} B`,
195
+ );
196
+
197
+ if (Array.isArray(stats.histogram) && stats.histogram.length > 0) {
198
+ console.log('\n🧮 Size Histogram:');
199
+ for (const b of stats.histogram.slice(0, 2)) {
200
+ console.log(` ${b.label}: ${b.count} files, ${b.bytes.toLocaleString()} bytes`);
201
+ }
202
+ if (stats.histogram.length > 2) {
203
+ console.log(` … and ${stats.histogram.length - 2} more buckets`);
204
+ }
205
+ }
206
+
207
+ if (Array.isArray(stats.byExtension) && stats.byExtension.length > 0) {
208
+ const topExt = stats.byExtension.slice(0, 2);
209
+ console.log('\n📦 Top Extensions:');
210
+ for (const e of topExt) {
211
+ const pct = stats.totalBytes ? (e.bytes / stats.totalBytes) * 100 : 0;
212
+ console.log(
213
+ ` ${e.ext}: ${e.count} files, ${e.bytes.toLocaleString()} bytes (${pct.toFixed(
214
+ 2,
215
+ )}%)`,
216
+ );
217
+ }
218
+ if (stats.byExtension.length > 2) {
219
+ console.log(` … and ${stats.byExtension.length - 2} more extensions`);
220
+ }
221
+ }
222
+
223
+ if (Array.isArray(stats.byDirectory) && stats.byDirectory.length > 0) {
224
+ const topDir = stats.byDirectory.slice(0, 2);
225
+ console.log('\n📂 Top Directories:');
226
+ for (const d of topDir) {
227
+ const pct = stats.totalBytes ? (d.bytes / stats.totalBytes) * 100 : 0;
228
+ console.log(
229
+ ` ${d.dir}: ${d.count} files, ${d.bytes.toLocaleString()} bytes (${pct.toFixed(
230
+ 2,
231
+ )}%)`,
232
+ );
233
+ }
234
+ if (stats.byDirectory.length > 2) {
235
+ console.log(` … and ${stats.byDirectory.length - 2} more directories`);
236
+ }
237
+ }
238
+
239
+ if (Array.isArray(stats.depthDistribution) && stats.depthDistribution.length > 0) {
240
+ console.log('\n🌳 Depth Distribution:');
241
+ const dd = stats.depthDistribution.slice(0, 2);
242
+ let line = ' ' + dd.map((d) => `${d.depth}:${d.count}`).join(' ');
243
+ if (stats.depthDistribution.length > 2) {
244
+ line += ` … +${stats.depthDistribution.length - 2} more`;
245
+ }
246
+ console.log(line);
247
+ }
248
+
249
+ if (Array.isArray(stats.longestPaths) && stats.longestPaths.length > 0) {
250
+ console.log('\n🧵 Longest Paths:');
251
+ for (const p of stats.longestPaths.slice(0, 2)) {
252
+ console.log(` ${p.path} (${p.length} chars, ${p.size.toLocaleString()} bytes)`);
253
+ }
254
+ if (stats.longestPaths.length > 2) {
255
+ console.log(` … and ${stats.longestPaths.length - 2} more paths`);
256
+ }
257
+ }
258
+
259
+ if (stats.temporal) {
260
+ console.log('\n⏱️ Temporal:');
261
+ if (stats.temporal.oldest) {
262
+ console.log(
263
+ ` Oldest: ${stats.temporal.oldest.path} (${stats.temporal.oldest.mtime})`,
264
+ );
265
+ }
266
+ if (stats.temporal.newest) {
267
+ console.log(
268
+ ` Newest: ${stats.temporal.newest.path} (${stats.temporal.newest.mtime})`,
269
+ );
270
+ }
271
+ if (Array.isArray(stats.temporal.ageBuckets)) {
272
+ console.log(' Age buckets:');
273
+ for (const b of stats.temporal.ageBuckets.slice(0, 2)) {
274
+ console.log(` ${b.label}: ${b.count} files, ${b.bytes.toLocaleString()} bytes`);
275
+ }
276
+ if (stats.temporal.ageBuckets.length > 2) {
277
+ console.log(` … and ${stats.temporal.ageBuckets.length - 2} more buckets`);
278
+ }
279
+ }
280
+ }
281
+
282
+ if (stats.quality) {
283
+ console.log('\n✅ Quality Signals:');
284
+ console.log(` Zero-byte files: ${stats.quality.zeroByteFiles}`);
285
+ console.log(` Empty text files: ${stats.quality.emptyTextFiles}`);
286
+ console.log(` Hidden files: ${stats.quality.hiddenFiles}`);
287
+ console.log(` Symlinks: ${stats.quality.symlinks}`);
288
+ console.log(
289
+ ` Large files (>= ${(stats.quality.largeThreshold / (1024 * 1024)).toFixed(
290
+ 0,
291
+ )} MB): ${stats.quality.largeFilesCount}`,
292
+ );
293
+ console.log(
294
+ ` Suspiciously large files (>= 100 MB): ${stats.quality.suspiciousLargeFilesCount}`,
295
+ );
296
+ }
297
+
298
+ if (Array.isArray(stats.duplicateCandidates) && stats.duplicateCandidates.length > 0) {
299
+ console.log('\n🧬 Duplicate Candidates:');
300
+ for (const d of stats.duplicateCandidates.slice(0, 2)) {
301
+ console.log(` ${d.reason}: ${d.count} files @ ${d.size.toLocaleString()} bytes`);
302
+ }
303
+ if (stats.duplicateCandidates.length > 2) {
304
+ console.log(` … and ${stats.duplicateCandidates.length - 2} more groups`);
305
+ }
306
+ }
307
+
308
+ if (typeof stats.compressibilityRatio === 'number') {
309
+ console.log(
310
+ `\n🗜️ Compressibility ratio (sampled): ${(stats.compressibilityRatio * 100).toFixed(
311
+ 2,
312
+ )}%`,
313
+ );
314
+ }
315
+
316
+ if (stats.git && stats.git.isRepo) {
317
+ console.log('\n🔧 Git:');
318
+ console.log(
319
+ ` Tracked: ${stats.git.trackedCount} files, ${stats.git.trackedBytes.toLocaleString()} bytes`,
320
+ );
321
+ console.log(
322
+ ` Untracked: ${stats.git.untrackedCount} files, ${stats.git.untrackedBytes.toLocaleString()} bytes`,
323
+ );
324
+ if (Array.isArray(stats.git.lfsCandidates) && stats.git.lfsCandidates.length > 0) {
325
+ console.log(' LFS candidates (top 2):');
326
+ for (const f of stats.git.lfsCandidates.slice(0, 2)) {
327
+ console.log(` ${f.path} (${f.size.toLocaleString()} bytes)`);
328
+ }
329
+ if (stats.git.lfsCandidates.length > 2) {
330
+ console.log(` … and ${stats.git.lfsCandidates.length - 2} more`);
331
+ }
332
+ }
333
+ }
334
+
335
+ if (Array.isArray(stats.largestFiles) && stats.largestFiles.length > 0) {
336
+ console.log('\n📚 Largest Files (top 2):');
337
+ for (const f of stats.largestFiles.slice(0, 2)) {
338
+ // Show LOC for text files when available; omit ext and mtime
339
+ let locStr = '';
340
+ if (!f.isBinary && Array.isArray(aggregatedContent?.textFiles)) {
341
+ const tf = aggregatedContent.textFiles.find((t) => t.path === f.path);
342
+ if (tf && typeof tf.lines === 'number') {
343
+ locStr = `, LOC: ${tf.lines.toLocaleString()}`;
344
+ }
345
+ }
346
+ console.log(
347
+ ` ${f.path} – ${f.sizeFormatted} (${f.percentOfTotal.toFixed(2)}%)${locStr}`,
348
+ );
349
+ }
350
+ if (stats.largestFiles.length > 2) {
351
+ console.log(` … and ${stats.largestFiles.length - 2} more files`);
352
+ }
353
+ }
354
+
355
+ // Write a comprehensive markdown report next to the XML
356
+ {
357
+ const mdPath = outputPath.endsWith('.xml')
358
+ ? outputPath.replace(/\.xml$/i, '.stats.md')
359
+ : outputPath + '.stats.md';
360
+ try {
361
+ const pct = (num, den) => (den ? (num / den) * 100 : 0);
362
+ const md = [];
363
+ md.push(
364
+ `# 🧾 Flatten Stats for ${path.basename(outputPath)}`,
365
+ '',
366
+ '## 📊 Summary',
367
+ `- Total source size: ${stats.totalSize}`,
368
+ `- Generated XML size: ${stats.xmlSize}`,
369
+ `- Total lines of code: ${stats.totalLines.toLocaleString()}`,
370
+ `- Estimated tokens: ${stats.estimatedTokens}`,
371
+ `- File breakdown: ${stats.textFiles} text, ${stats.binaryFiles} binary, ${stats.errorFiles} errors`,
372
+ '',
373
+ '## 📈 Size Percentiles',
374
+ `Avg: ${Math.round(stats.avgFileSize).toLocaleString()} B, Median: ${Math.round(
375
+ stats.medianFileSize,
376
+ ).toLocaleString()} B, p90: ${stats.p90.toLocaleString()} B, p95: ${stats.p95.toLocaleString()} B, p99: ${stats.p99.toLocaleString()} B`,
377
+ '',
378
+ );
379
+
380
+ // Histogram
381
+ if (Array.isArray(stats.histogram) && stats.histogram.length > 0) {
382
+ md.push(
383
+ '## 🧮 Size Histogram',
384
+ '| Bucket | Files | Bytes |',
385
+ '| --- | ---: | ---: |',
386
+ );
387
+ for (const b of stats.histogram) {
388
+ md.push(`| ${b.label} | ${b.count} | ${b.bytes.toLocaleString()} |`);
389
+ }
390
+ md.push('');
391
+ }
392
+
393
+ // Top Extensions
394
+ if (Array.isArray(stats.byExtension) && stats.byExtension.length > 0) {
395
+ md.push(
396
+ '## 📦 Top Extensions by Bytes (Top 20)',
397
+ '| Ext | Files | Bytes | % of total |',
398
+ '| --- | ---: | ---: | ---: |',
399
+ );
400
+ for (const e of stats.byExtension.slice(0, 20)) {
401
+ const p = pct(e.bytes, stats.totalBytes);
402
+ md.push(
403
+ `| ${e.ext} | ${e.count} | ${e.bytes.toLocaleString()} | ${p.toFixed(2)}% |`,
404
+ );
405
+ }
406
+ md.push('');
407
+ }
408
+
409
+ // Top Directories
410
+ if (Array.isArray(stats.byDirectory) && stats.byDirectory.length > 0) {
411
+ md.push(
412
+ '## 📂 Top Directories by Bytes (Top 20)',
413
+ '| Directory | Files | Bytes | % of total |',
414
+ '| --- | ---: | ---: | ---: |',
415
+ );
416
+ for (const d of stats.byDirectory.slice(0, 20)) {
417
+ const p = pct(d.bytes, stats.totalBytes);
418
+ md.push(
419
+ `| ${d.dir} | ${d.count} | ${d.bytes.toLocaleString()} | ${p.toFixed(2)}% |`,
420
+ );
421
+ }
422
+ md.push('');
423
+ }
424
+
425
+ // Depth distribution
426
+ if (Array.isArray(stats.depthDistribution) && stats.depthDistribution.length > 0) {
427
+ md.push('## 🌳 Depth Distribution', '| Depth | Count |', '| ---: | ---: |');
428
+ for (const d of stats.depthDistribution) {
429
+ md.push(`| ${d.depth} | ${d.count} |`);
430
+ }
431
+ md.push('');
432
+ }
433
+
434
+ // Longest paths
435
+ if (Array.isArray(stats.longestPaths) && stats.longestPaths.length > 0) {
436
+ md.push(
437
+ '## 🧵 Longest Paths (Top 25)',
438
+ '| Path | Length | Bytes |',
439
+ '| --- | ---: | ---: |',
440
+ );
441
+ for (const pth of stats.longestPaths) {
442
+ md.push(`| ${pth.path} | ${pth.length} | ${pth.size.toLocaleString()} |`);
443
+ }
444
+ md.push('');
445
+ }
446
+
447
+ // Temporal
448
+ if (stats.temporal) {
449
+ md.push('## ⏱️ Temporal');
450
+ if (stats.temporal.oldest) {
451
+ md.push(`- Oldest: ${stats.temporal.oldest.path} (${stats.temporal.oldest.mtime})`);
452
+ }
453
+ if (stats.temporal.newest) {
454
+ md.push(`- Newest: ${stats.temporal.newest.path} (${stats.temporal.newest.mtime})`);
455
+ }
456
+ if (Array.isArray(stats.temporal.ageBuckets)) {
457
+ md.push('', '| Age | Files | Bytes |', '| --- | ---: | ---: |');
458
+ for (const b of stats.temporal.ageBuckets) {
459
+ md.push(`| ${b.label} | ${b.count} | ${b.bytes.toLocaleString()} |`);
460
+ }
461
+ }
462
+ md.push('');
463
+ }
464
+
465
+ // Quality signals
466
+ if (stats.quality) {
467
+ md.push(
468
+ '## ✅ Quality Signals',
469
+ `- Zero-byte files: ${stats.quality.zeroByteFiles}`,
470
+ `- Empty text files: ${stats.quality.emptyTextFiles}`,
471
+ `- Hidden files: ${stats.quality.hiddenFiles}`,
472
+ `- Symlinks: ${stats.quality.symlinks}`,
473
+ `- Large files (>= ${(stats.quality.largeThreshold / (1024 * 1024)).toFixed(0)} MB): ${stats.quality.largeFilesCount}`,
474
+ `- Suspiciously large files (>= 100 MB): ${stats.quality.suspiciousLargeFilesCount}`,
475
+ '',
476
+ );
477
+ }
478
+
479
+ // Duplicates
480
+ if (Array.isArray(stats.duplicateCandidates) && stats.duplicateCandidates.length > 0) {
481
+ md.push(
482
+ '## 🧬 Duplicate Candidates',
483
+ '| Reason | Files | Size (bytes) |',
484
+ '| --- | ---: | ---: |',
485
+ );
486
+ for (const d of stats.duplicateCandidates) {
487
+ md.push(`| ${d.reason} | ${d.count} | ${d.size.toLocaleString()} |`);
488
+ }
489
+ md.push('', '### 🧬 Duplicate Groups Details');
490
+ let dupIndex = 1;
491
+ for (const d of stats.duplicateCandidates) {
492
+ md.push(
493
+ `#### Group ${dupIndex}: ${d.count} files @ ${d.size.toLocaleString()} bytes (${d.reason})`,
494
+ );
495
+ if (Array.isArray(d.files) && d.files.length > 0) {
496
+ for (const fp of d.files) {
497
+ md.push(`- ${fp}`);
498
+ }
499
+ } else {
500
+ md.push('- (file list unavailable)');
501
+ }
502
+ md.push('');
503
+ dupIndex++;
504
+ }
505
+ md.push('');
506
+ }
507
+
508
+ // Compressibility
509
+ if (typeof stats.compressibilityRatio === 'number') {
510
+ md.push(
511
+ '## 🗜️ Compressibility',
512
+ `Sampled compressibility ratio: ${(stats.compressibilityRatio * 100).toFixed(2)}%`,
513
+ '',
514
+ );
515
+ }
516
+
517
+ // Git
518
+ if (stats.git && stats.git.isRepo) {
519
+ md.push(
520
+ '## 🔧 Git',
521
+ `- Tracked: ${stats.git.trackedCount} files, ${stats.git.trackedBytes.toLocaleString()} bytes`,
522
+ `- Untracked: ${stats.git.untrackedCount} files, ${stats.git.untrackedBytes.toLocaleString()} bytes`,
523
+ );
524
+ if (Array.isArray(stats.git.lfsCandidates) && stats.git.lfsCandidates.length > 0) {
525
+ md.push('', '### 📦 LFS Candidates (Top 20)', '| Path | Bytes |', '| --- | ---: |');
526
+ for (const f of stats.git.lfsCandidates.slice(0, 20)) {
527
+ md.push(`| ${f.path} | ${f.size.toLocaleString()} |`);
528
+ }
529
+ }
530
+ md.push('');
531
+ }
532
+
533
+ // Largest Files
534
+ if (Array.isArray(stats.largestFiles) && stats.largestFiles.length > 0) {
535
+ md.push(
536
+ '## 📚 Largest Files (Top 50)',
537
+ '| Path | Size | % of total | LOC |',
538
+ '| --- | ---: | ---: | ---: |',
539
+ );
540
+ for (const f of stats.largestFiles) {
541
+ let loc = '';
542
+ if (!f.isBinary && Array.isArray(aggregatedContent?.textFiles)) {
543
+ const tf = aggregatedContent.textFiles.find((t) => t.path === f.path);
544
+ if (tf && typeof tf.lines === 'number') {
545
+ loc = tf.lines.toLocaleString();
546
+ }
547
+ }
548
+ md.push(
549
+ `| ${f.path} | ${f.sizeFormatted} | ${f.percentOfTotal.toFixed(2)}% | ${loc} |`,
550
+ );
551
+ }
552
+ md.push('');
553
+ }
554
+
555
+ await fs.writeFile(mdPath, md.join('\n'));
556
+ console.log(`\n🧾 Detailed stats report written to: ${mdPath}`);
557
+ } catch (error) {
558
+ console.warn(`⚠️ Failed to write stats markdown: ${error.message}`);
559
+ }
560
+ }
561
+ }
562
+ } catch (error) {
563
+ console.error('❌ Critical error:', error.message);
564
+ console.error('An unexpected error occurred.');
565
+ process.exit(1);
566
+ }
567
+ });
568
+
569
+ if (require.main === module) {
570
+ program.parse();
571
+ }
572
+
573
+ module.exports = program;