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.
- package/.github/FUNDING.yaml +15 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +32 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +22 -0
- package/.github/workflows/discord.yaml +25 -0
- package/.github/workflows/format-check.yaml +42 -0
- package/.github/workflows/manual-release.yaml +173 -0
- package/.husky/pre-commit +3 -2
- package/.vscode/settings.json +67 -74
- package/CHANGELOG.md +564 -19
- package/CONTRIBUTING.md +168 -5
- package/LICENSE +1 -1
- package/README.md +146 -218
- package/bmad-core/agent-teams/team-all.yaml +14 -0
- package/bmad-core/agent-teams/team-fullstack.yaml +18 -0
- package/bmad-core/agent-teams/team-ide-minimal.yaml +10 -0
- package/bmad-core/agent-teams/team-no-ui.yaml +13 -0
- package/bmad-core/agents/analyst.md +81 -0
- package/bmad-core/agents/architect.md +83 -0
- package/bmad-core/agents/bmad-master.md +107 -0
- package/bmad-core/agents/bmad-orchestrator.md +149 -0
- package/bmad-core/agents/dev.md +75 -0
- package/bmad-core/agents/pm.md +81 -0
- package/bmad-core/agents/po.md +76 -0
- package/bmad-core/agents/qa.md +88 -0
- package/bmad-core/agents/sm.md +62 -0
- package/bmad-core/agents/ux-expert.md +66 -0
- package/{.bmad-core → bmad-core}/checklists/architect-checklist.md +0 -5
- package/{.bmad-core → bmad-core}/checklists/change-checklist.md +2 -2
- package/{.bmad-core → bmad-core}/checklists/pm-checklist.md +0 -5
- package/{.bmad-core → bmad-core}/checklists/po-master-checklist.md +0 -9
- package/{.bmad-core → bmad-core}/checklists/story-dod-checklist.md +0 -7
- package/{.bmad-core → bmad-core}/checklists/story-draft-checklist.md +1 -4
- package/bmad-core/core-config.yaml +20 -0
- package/bmad-core/data/bmad-kb.md +806 -0
- package/bmad-core/data/brainstorming-techniques.md +36 -0
- package/bmad-core/data/elicitation-methods.md +154 -0
- package/bmad-core/data/test-levels-framework.md +146 -0
- package/bmad-core/data/test-priorities-matrix.md +172 -0
- package/bmad-core/tasks/advanced-elicitation.md +117 -0
- package/{.bmad-core → bmad-core}/tasks/correct-course.md +9 -12
- package/bmad-core/tasks/create-brownfield-story.md +312 -0
- package/{.bmad-core → bmad-core}/tasks/create-deep-research-prompt.md +4 -27
- package/bmad-core/tasks/create-next-story.md +112 -0
- package/bmad-core/tasks/document-project.md +343 -0
- package/bmad-core/tasks/facilitate-brainstorming-session.md +136 -0
- package/bmad-core/tasks/generate-ai-frontend-prompt.md +51 -0
- package/{.bmad-core → bmad-core}/tasks/index-docs.md +3 -13
- package/bmad-core/tasks/kb-mode-interaction.md +75 -0
- package/bmad-core/tasks/nfr-assess.md +343 -0
- package/bmad-core/tasks/qa-gate.md +159 -0
- package/bmad-core/tasks/review-story.md +314 -0
- package/bmad-core/tasks/risk-profile.md +353 -0
- package/{.bmad-core → bmad-core}/tasks/shard-doc.md +27 -15
- package/bmad-core/tasks/test-design.md +174 -0
- package/bmad-core/tasks/trace-requirements.md +264 -0
- package/bmad-core/tasks/validate-next-story.md +134 -0
- package/bmad-core/templates/architecture-tmpl.yaml +650 -0
- package/bmad-core/templates/brainstorming-output-tmpl.yaml +156 -0
- package/bmad-core/templates/brownfield-architecture-tmpl.yaml +476 -0
- package/bmad-core/templates/brownfield-prd-tmpl.yaml +280 -0
- package/bmad-core/templates/competitor-analysis-tmpl.yaml +306 -0
- package/bmad-core/templates/front-end-architecture-tmpl.yaml +218 -0
- package/bmad-core/templates/front-end-spec-tmpl.yaml +349 -0
- package/bmad-core/templates/fullstack-architecture-tmpl.yaml +823 -0
- package/bmad-core/templates/market-research-tmpl.yaml +252 -0
- package/bmad-core/templates/prd-tmpl.yaml +202 -0
- package/bmad-core/templates/project-brief-tmpl.yaml +221 -0
- package/bmad-core/templates/qa-gate-tmpl.yaml +102 -0
- package/bmad-core/templates/story-tmpl.yaml +137 -0
- package/bmad-core/workflows/brownfield-fullstack.yaml +297 -0
- package/bmad-core/workflows/brownfield-service.yaml +187 -0
- package/bmad-core/workflows/brownfield-ui.yaml +197 -0
- package/{.bmad-core/workflows/greenfield-fullstack.yml → bmad-core/workflows/greenfield-fullstack.yaml} +140 -77
- package/bmad-core/workflows/greenfield-service.yaml +206 -0
- package/bmad-core/workflows/greenfield-ui.yaml +235 -0
- package/common/tasks/create-doc.md +101 -0
- package/{.bmad-core → common}/tasks/execute-checklist.md +2 -13
- package/common/utils/bmad-doc-template.md +325 -0
- package/common/utils/workflow-management.md +69 -0
- package/dist/agents/analyst.txt +2889 -0
- package/dist/agents/architect.txt +3552 -0
- package/dist/agents/bmad-master.txt +8769 -0
- package/dist/agents/bmad-orchestrator.txt +1513 -0
- package/dist/agents/dev.txt +414 -0
- package/{.bmad-core/web-bundles → dist}/agents/pm.txt +668 -1119
- package/{.bmad-core/web-bundles → dist}/agents/po.txt +341 -484
- package/dist/agents/qa.txt +1987 -0
- package/dist/agents/sm.txt +658 -0
- package/dist/agents/ux-expert.txt +694 -0
- package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-designer.txt +2371 -0
- package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.txt +1620 -0
- package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.txt +815 -0
- package/dist/expansion-packs/bmad-2d-phaser-game-dev/teams/phaser-2d-nodejs-game-team.txt +10952 -0
- package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-architect.txt +4012 -0
- package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-designer.txt +3698 -0
- package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-developer.txt +450 -0
- package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-sm.txt +973 -0
- package/dist/expansion-packs/bmad-2d-unity-game-dev/teams/unity-2d-game-team.txt +15376 -0
- package/dist/expansion-packs/bmad-infrastructure-devops/agents/infra-devops-platform.txt +2075 -0
- package/dist/teams/team-all.txt +12682 -0
- package/dist/teams/team-fullstack.txt +10421 -0
- package/dist/teams/team-ide-minimal.txt +5103 -0
- package/dist/teams/team-no-ui.txt +8980 -0
- package/docs/GUIDING-PRINCIPLES.md +91 -0
- package/docs/core-architecture.md +219 -0
- package/docs/enhanced-ide-development-workflow.md +248 -0
- package/docs/expansion-packs.md +280 -0
- package/docs/how-to-contribute-with-pull-requests.md +158 -0
- package/docs/user-guide.md +504 -0
- package/docs/versioning-and-releases.md +115 -53
- package/docs/versions.md +4 -5
- package/docs/working-in-the-brownfield.md +597 -0
- package/eslint.config.mjs +119 -0
- package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/Complete AI Agent System - Flowchart.svg +102 -0
- 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
- 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
- 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
- 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
- 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
- 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
- package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/README.md +109 -0
- package/expansion-packs/README.md +2 -112
- package/expansion-packs/bmad-2d-phaser-game-dev/agent-teams/phaser-2d-nodejs-game-team.yaml +13 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-designer.md +71 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.md +78 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.md +64 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/checklists/game-design-checklist.md +201 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/checklists/game-story-dod-checklist.md +160 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/config.yaml +8 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/data/bmad-kb.md +250 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/data/development-guidelines.md +647 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/tasks/advanced-elicitation.md +110 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/tasks/create-game-story.md +216 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/tasks/game-design-brainstorming.md +290 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-architecture-tmpl.yaml +613 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-brief-tmpl.yaml +356 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-design-doc-tmpl.yaml +343 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-story-tmpl.yaml +253 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/templates/level-design-doc-tmpl.yaml +484 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/workflows/game-dev-greenfield.yaml +183 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/workflows/game-prototype.yaml +175 -0
- package/expansion-packs/bmad-2d-unity-game-dev/agent-teams/unity-2d-game-team.yaml +14 -0
- package/expansion-packs/bmad-2d-unity-game-dev/agents/game-architect.md +80 -0
- package/expansion-packs/bmad-2d-unity-game-dev/agents/game-designer.md +77 -0
- package/expansion-packs/bmad-2d-unity-game-dev/agents/game-developer.md +78 -0
- package/expansion-packs/bmad-2d-unity-game-dev/agents/game-sm.md +65 -0
- package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-architect-checklist.md +391 -0
- package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-change-checklist.md +203 -0
- package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-design-checklist.md +201 -0
- package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-story-dod-checklist.md +124 -0
- package/expansion-packs/bmad-2d-unity-game-dev/config.yaml +6 -0
- package/expansion-packs/bmad-2d-unity-game-dev/data/bmad-kb.md +769 -0
- package/expansion-packs/bmad-2d-unity-game-dev/data/development-guidelines.md +586 -0
- package/expansion-packs/bmad-2d-unity-game-dev/tasks/advanced-elicitation.md +110 -0
- package/expansion-packs/bmad-2d-unity-game-dev/tasks/correct-course-game.md +141 -0
- package/expansion-packs/bmad-2d-unity-game-dev/tasks/create-game-story.md +184 -0
- package/expansion-packs/bmad-2d-unity-game-dev/tasks/game-design-brainstorming.md +290 -0
- package/expansion-packs/bmad-2d-unity-game-dev/tasks/validate-game-story.md +200 -0
- package/expansion-packs/bmad-2d-unity-game-dev/templates/game-architecture-tmpl.yaml +1030 -0
- package/expansion-packs/bmad-2d-unity-game-dev/templates/game-brief-tmpl.yaml +356 -0
- package/expansion-packs/bmad-2d-unity-game-dev/templates/game-design-doc-tmpl.yaml +705 -0
- package/expansion-packs/bmad-2d-unity-game-dev/templates/game-story-tmpl.yaml +256 -0
- package/expansion-packs/bmad-2d-unity-game-dev/templates/level-design-doc-tmpl.yaml +484 -0
- package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-dev-greenfield.yaml +183 -0
- package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-prototype.yaml +175 -0
- package/expansion-packs/{infrastructure-devops → bmad-infrastructure-devops}/README.md +9 -9
- package/expansion-packs/{infrastructure-devops → bmad-infrastructure-devops}/agents/infra-devops-platform.md +30 -18
- package/expansion-packs/{infrastructure-devops → bmad-infrastructure-devops}/checklists/infrastructure-checklist.md +1 -1
- package/expansion-packs/bmad-infrastructure-devops/config.yaml +9 -0
- package/expansion-packs/bmad-infrastructure-devops/data/bmad-kb.md +305 -0
- package/expansion-packs/{infrastructure-devops → bmad-infrastructure-devops}/tasks/review-infrastructure.md +4 -5
- package/expansion-packs/{infrastructure-devops → bmad-infrastructure-devops}/tasks/validate-infrastructure.md +4 -5
- package/expansion-packs/bmad-infrastructure-devops/templates/infrastructure-architecture-tmpl.yaml +424 -0
- package/expansion-packs/bmad-infrastructure-devops/templates/infrastructure-platform-from-arch-tmpl.yaml +629 -0
- package/package.json +74 -42
- package/prettier.config.mjs +32 -0
- package/release_notes.md +30 -0
- package/tools/bmad-npx-wrapper.js +13 -15
- package/tools/builders/web-builder.js +544 -15
- package/tools/bump-all-versions.js +115 -0
- package/tools/bump-expansion-version.js +90 -0
- package/tools/cli.js +65 -32
- package/tools/flattener/aggregate.js +76 -0
- package/tools/flattener/binary.js +80 -0
- package/tools/flattener/discovery.js +71 -0
- package/tools/flattener/files.js +35 -0
- package/tools/flattener/ignoreRules.js +176 -0
- package/tools/flattener/main.js +573 -0
- package/tools/flattener/projectRoot.js +206 -0
- package/tools/flattener/prompts.js +44 -0
- package/tools/flattener/stats.helpers.js +395 -0
- package/tools/flattener/stats.js +80 -0
- package/tools/flattener/test-matrix.js +413 -0
- package/tools/flattener/xml.js +88 -0
- package/tools/installer/README.md +3 -53
- package/tools/installer/bin/bmad.js +475 -90
- package/tools/installer/config/ide-agent-config.yaml +58 -0
- package/tools/installer/config/install.config.yaml +123 -0
- package/tools/installer/lib/config-loader.js +208 -40
- package/tools/installer/lib/file-manager.js +258 -55
- package/tools/installer/lib/ide-base-setup.js +228 -0
- package/tools/installer/lib/ide-setup.js +1265 -253
- package/tools/installer/lib/installer.js +1651 -310
- package/tools/installer/lib/memory-profiler.js +225 -0
- package/tools/installer/lib/module-manager.js +114 -0
- package/tools/installer/lib/resource-locator.js +308 -0
- package/tools/installer/package.json +25 -24
- package/tools/lib/dependency-resolver.js +44 -48
- package/tools/lib/yaml-utils.js +29 -0
- package/tools/md-assets/web-agent-startup-instructions.md +39 -0
- package/tools/preview-release-notes.js +66 -0
- package/tools/shared/bannerArt.js +105 -0
- package/tools/sync-installer-version.js +7 -9
- package/tools/update-expansion-version.js +53 -0
- package/tools/upgraders/v3-to-v4-upgrader.js +221 -320
- package/tools/version-bump.js +42 -27
- package/tools/yaml-format.js +57 -44
- package/.bmad-core/agent-teams/team-all.yml +0 -16
- package/.bmad-core/agent-teams/team-fullstack.yml +0 -26
- package/.bmad-core/agent-teams/team-no-ui.yml +0 -15
- package/.bmad-core/agents/analyst.md +0 -59
- package/.bmad-core/agents/architect.md +0 -66
- package/.bmad-core/agents/bmad-master.md +0 -104
- package/.bmad-core/agents/bmad-orchestrator.md +0 -81
- package/.bmad-core/agents/dev.md +0 -70
- package/.bmad-core/agents/pm.md +0 -59
- package/.bmad-core/agents/po.md +0 -60
- package/.bmad-core/agents/qa.md +0 -52
- package/.bmad-core/agents/sm.md +0 -55
- package/.bmad-core/agents/ux-expert.md +0 -66
- package/.bmad-core/data/bmad-kb.md +0 -47
- package/.bmad-core/schemas/agent-team-schema.yml +0 -153
- package/.bmad-core/tasks/advanced-elicitation.md +0 -92
- package/.bmad-core/tasks/brainstorming-techniques.md +0 -238
- package/.bmad-core/tasks/core-dump.md +0 -74
- package/.bmad-core/tasks/create-agent.md +0 -202
- package/.bmad-core/tasks/create-doc.md +0 -74
- package/.bmad-core/tasks/create-expansion-pack.md +0 -425
- package/.bmad-core/tasks/create-next-story.md +0 -206
- package/.bmad-core/tasks/create-team.md +0 -229
- package/.bmad-core/tasks/doc-migration-task.md +0 -143
- package/.bmad-core/tasks/generate-ai-frontend-prompt.md +0 -58
- package/.bmad-core/templates/agent-tmpl.md +0 -58
- package/.bmad-core/templates/architecture-tmpl.md +0 -771
- package/.bmad-core/templates/brownfield-architecture-tmpl.md +0 -542
- package/.bmad-core/templates/brownfield-prd-tmpl.md +0 -240
- package/.bmad-core/templates/competitor-analysis-tmpl.md +0 -289
- package/.bmad-core/templates/expansion-pack-plan-tmpl.md +0 -91
- package/.bmad-core/templates/front-end-architecture-tmpl.md +0 -173
- package/.bmad-core/templates/front-end-spec-tmpl.md +0 -411
- package/.bmad-core/templates/fullstack-architecture-tmpl.md +0 -1016
- package/.bmad-core/templates/market-research-tmpl.md +0 -261
- package/.bmad-core/templates/prd-tmpl.md +0 -200
- package/.bmad-core/templates/project-brief-tmpl.md +0 -228
- package/.bmad-core/templates/simple-project-prd-tmpl.md +0 -461
- package/.bmad-core/templates/story-tmpl.md +0 -61
- package/.bmad-core/templates/web-agent-startup-instructions-template.md +0 -39
- package/.bmad-core/utils/agent-switcher.ide.md +0 -112
- package/.bmad-core/utils/template-format.md +0 -26
- package/.bmad-core/utils/workflow-management.md +0 -224
- package/.bmad-core/web-bundles/agents/analyst.txt +0 -1684
- package/.bmad-core/web-bundles/agents/architect.txt +0 -3584
- package/.bmad-core/web-bundles/agents/bmad-master.txt +0 -9491
- package/.bmad-core/web-bundles/agents/bmad-orchestrator.txt +0 -1466
- package/.bmad-core/web-bundles/agents/dev.txt +0 -316
- package/.bmad-core/web-bundles/agents/qa.txt +0 -129
- package/.bmad-core/web-bundles/agents/sm.txt +0 -658
- package/.bmad-core/web-bundles/agents/ux-expert.txt +0 -1099
- package/.bmad-core/web-bundles/teams/team-all.txt +0 -10757
- package/.bmad-core/web-bundles/teams/team-fullstack.txt +0 -10109
- package/.bmad-core/web-bundles/teams/team-no-ui.txt +0 -8950
- package/.bmad-core/workflows/brownfield-fullstack.yml +0 -116
- package/.bmad-core/workflows/brownfield-service.yml +0 -117
- package/.bmad-core/workflows/brownfield-ui.yml +0 -127
- package/.bmad-core/workflows/greenfield-service.yml +0 -143
- package/.bmad-core/workflows/greenfield-ui.yml +0 -172
- package/.claude/commands/analyst.md +0 -63
- package/.claude/commands/architect.md +0 -70
- package/.claude/commands/bmad-master.md +0 -108
- package/.claude/commands/bmad-orchestrator.md +0 -85
- package/.claude/commands/dev.md +0 -74
- package/.claude/commands/pm.md +0 -63
- package/.claude/commands/po.md +0 -64
- package/.claude/commands/qa.md +0 -56
- package/.claude/commands/sm.md +0 -59
- package/.claude/commands/ux-expert.md +0 -70
- package/.cursor/rules/analyst.mdc +0 -77
- package/.cursor/rules/architect.mdc +0 -84
- package/.cursor/rules/bmad-master.mdc +0 -122
- package/.cursor/rules/bmad-orchestrator.mdc +0 -99
- package/.cursor/rules/dev.mdc +0 -88
- package/.cursor/rules/pm.mdc +0 -77
- package/.cursor/rules/po.mdc +0 -78
- package/.cursor/rules/qa.mdc +0 -70
- package/.cursor/rules/sm.mdc +0 -73
- package/.cursor/rules/ux-expert.mdc +0 -84
- package/.github/workflows/release.yml +0 -59
- package/.releaserc.json +0 -18
- package/.roo/.roomodes +0 -95
- package/.roo/README.md +0 -38
- package/.vscode/extensions.json +0 -6
- package/.windsurf/rules/analyst.md +0 -71
- package/.windsurf/rules/architect.md +0 -78
- package/.windsurf/rules/bmad-master.md +0 -116
- package/.windsurf/rules/bmad-orchestrator.md +0 -93
- package/.windsurf/rules/dev.md +0 -82
- package/.windsurf/rules/pm.md +0 -71
- package/.windsurf/rules/po.md +0 -72
- package/.windsurf/rules/qa.md +0 -64
- package/.windsurf/rules/sm.md +0 -67
- package/.windsurf/rules/ux-expert.md +0 -78
- package/docs/bmad-workflow-guide.md +0 -161
- package/docs/claude-code-guide.md +0 -119
- package/docs/cursor-guide.md +0 -127
- package/docs/roo-code-guide.md +0 -140
- package/docs/sample-output/simple-fullstack-greenfield/prd.md +0 -42
- package/docs/windsurf-guide.md +0 -127
- package/expansion-packs/infrastructure-devops/manifest.yml +0 -38
- package/expansion-packs/infrastructure-devops/templates/infrastructure-architecture-tmpl.md +0 -415
- package/expansion-packs/infrastructure-devops/templates/infrastructure-platform-from-arch-tmpl.md +0 -0
- package/tools/installer/config/install.config.yml +0 -139
- package/tools/installer/package-lock.json +0 -906
- package/tools/installer/templates/claude-commands.md +0 -7
- package/tools/installer/templates/cursor-rules.md +0 -22
- package/tools/installer/templates/windsurf-rules.md +0 -22
- package/tools/semantic-release-sync-installer.js +0 -31
- /package/{.bmad-core → bmad-core}/data/technical-preferences.md +0 -0
- /package/{.bmad-core → bmad-core}/tasks/brownfield-create-epic.md +0 -0
- /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;
|