bmad-method 4.37.0 โ 4.39.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/ISSUE_TEMPLATE/bug_report.md +3 -3
- package/.github/ISSUE_TEMPLATE/feature_request.md +3 -3
- package/.github/workflows/discord.yaml +11 -2
- package/.github/workflows/format-check.yaml +42 -0
- package/.github/workflows/manual-release.yaml +173 -0
- package/.husky/pre-commit +3 -0
- package/.vscode/settings.json +26 -1
- package/CHANGELOG.md +2 -23
- package/README.md +2 -0
- package/bmad-core/agent-teams/team-all.yaml +1 -1
- package/bmad-core/agents/analyst.md +16 -15
- package/bmad-core/agents/architect.md +11 -11
- package/bmad-core/agents/bmad-master.md +23 -22
- package/bmad-core/agents/bmad-orchestrator.md +13 -17
- package/bmad-core/agents/dev.md +14 -11
- package/bmad-core/agents/pm.md +15 -14
- package/bmad-core/agents/po.md +9 -8
- package/bmad-core/agents/qa.md +42 -22
- package/bmad-core/agents/sm.md +7 -6
- package/bmad-core/agents/ux-expert.md +6 -5
- package/bmad-core/core-config.yaml +2 -0
- package/bmad-core/data/bmad-kb.md +1 -1
- package/bmad-core/data/test-levels-framework.md +146 -0
- package/bmad-core/data/test-priorities-matrix.md +172 -0
- package/bmad-core/tasks/apply-qa-fixes.md +148 -0
- package/bmad-core/tasks/facilitate-brainstorming-session.md +1 -1
- package/bmad-core/tasks/nfr-assess.md +343 -0
- package/bmad-core/tasks/qa-gate.md +161 -0
- package/bmad-core/tasks/review-story.md +234 -74
- package/bmad-core/tasks/risk-profile.md +353 -0
- package/bmad-core/tasks/test-design.md +174 -0
- package/bmad-core/tasks/trace-requirements.md +264 -0
- package/bmad-core/templates/architecture-tmpl.yaml +49 -49
- package/bmad-core/templates/brainstorming-output-tmpl.yaml +5 -5
- package/bmad-core/templates/brownfield-architecture-tmpl.yaml +31 -31
- package/bmad-core/templates/brownfield-prd-tmpl.yaml +13 -13
- package/bmad-core/templates/competitor-analysis-tmpl.yaml +19 -6
- package/bmad-core/templates/front-end-architecture-tmpl.yaml +21 -9
- package/bmad-core/templates/front-end-spec-tmpl.yaml +24 -24
- package/bmad-core/templates/fullstack-architecture-tmpl.yaml +122 -104
- package/bmad-core/templates/market-research-tmpl.yaml +2 -2
- package/bmad-core/templates/prd-tmpl.yaml +9 -9
- package/bmad-core/templates/project-brief-tmpl.yaml +4 -4
- package/bmad-core/templates/qa-gate-tmpl.yaml +102 -0
- package/bmad-core/templates/story-tmpl.yaml +12 -12
- package/bmad-core/workflows/brownfield-fullstack.yaml +9 -9
- package/bmad-core/workflows/brownfield-service.yaml +1 -1
- package/bmad-core/workflows/brownfield-ui.yaml +1 -1
- package/bmad-core/workflows/greenfield-fullstack.yaml +1 -1
- package/bmad-core/workflows/greenfield-service.yaml +1 -1
- package/bmad-core/workflows/greenfield-ui.yaml +1 -1
- package/common/utils/bmad-doc-template.md +5 -5
- package/dist/agents/analyst.txt +1086 -1079
- package/dist/agents/architect.txt +1534 -1526
- package/dist/agents/bmad-master.txt +646 -632
- package/dist/agents/bmad-orchestrator.txt +40 -18
- package/dist/agents/dev.txt +158 -19
- package/dist/agents/pm.txt +1082 -1107
- package/dist/agents/po.txt +314 -332
- package/dist/agents/qa.txt +1754 -151
- package/dist/agents/sm.txt +88 -98
- package/dist/agents/ux-expert.txt +80 -87
- package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-designer.txt +109 -146
- package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.txt +75 -86
- package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.txt +41 -48
- package/dist/expansion-packs/bmad-2d-phaser-game-dev/teams/phaser-2d-nodejs-game-team.txt +1903 -1941
- package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-architect.txt +15 -50
- package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-designer.txt +149 -195
- package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-developer.txt +0 -15
- package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-sm.txt +20 -37
- package/dist/expansion-packs/bmad-2d-unity-game-dev/teams/unity-2d-game-team.txt +2660 -2752
- package/dist/expansion-packs/bmad-creative-writing/agents/beta-reader.txt +871 -0
- package/dist/expansion-packs/bmad-creative-writing/agents/book-critic.txt +78 -0
- package/dist/expansion-packs/bmad-creative-writing/agents/character-psychologist.txt +839 -0
- package/dist/expansion-packs/bmad-creative-writing/agents/cover-designer.txt +85 -0
- package/dist/expansion-packs/bmad-creative-writing/agents/dialog-specialist.txt +861 -0
- package/dist/expansion-packs/bmad-creative-writing/agents/editor.txt +796 -0
- package/dist/expansion-packs/bmad-creative-writing/agents/genre-specialist.txt +927 -0
- package/dist/expansion-packs/bmad-creative-writing/agents/narrative-designer.txt +842 -0
- package/dist/expansion-packs/bmad-creative-writing/agents/plot-architect.txt +1126 -0
- package/dist/expansion-packs/bmad-creative-writing/agents/world-builder.txt +864 -0
- package/dist/expansion-packs/bmad-creative-writing/teams/agent-team.txt +5917 -0
- package/dist/expansion-packs/bmad-infrastructure-devops/agents/infra-devops-platform.txt +25 -27
- package/dist/teams/team-all.txt +5541 -3768
- package/dist/teams/team-fullstack.txt +3014 -2987
- package/dist/teams/team-ide-minimal.txt +2219 -469
- package/dist/teams/team-no-ui.txt +2993 -2966
- package/docs/enhanced-ide-development-workflow.md +220 -15
- package/docs/user-guide.md +271 -18
- package/docs/versioning-and-releases.md +122 -44
- package/docs/working-in-the-brownfield.md +264 -31
- package/eslint.config.mjs +119 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.md +4 -4
- package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.md +1 -1
- package/expansion-packs/bmad-2d-phaser-game-dev/config.yaml +1 -1
- package/expansion-packs/bmad-2d-phaser-game-dev/data/development-guidelines.md +26 -28
- package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-architecture-tmpl.yaml +50 -50
- package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-brief-tmpl.yaml +23 -23
- package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-design-doc-tmpl.yaml +24 -24
- package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-story-tmpl.yaml +42 -42
- package/expansion-packs/bmad-2d-phaser-game-dev/templates/level-design-doc-tmpl.yaml +65 -65
- package/expansion-packs/bmad-2d-phaser-game-dev/workflows/game-dev-greenfield.yaml +5 -5
- package/expansion-packs/bmad-2d-phaser-game-dev/workflows/game-prototype.yaml +1 -1
- package/expansion-packs/bmad-2d-unity-game-dev/agents/game-developer.md +3 -3
- package/expansion-packs/bmad-2d-unity-game-dev/config.yaml +1 -1
- package/expansion-packs/bmad-2d-unity-game-dev/data/bmad-kb.md +1 -1
- package/expansion-packs/bmad-2d-unity-game-dev/templates/game-brief-tmpl.yaml +23 -23
- package/expansion-packs/bmad-2d-unity-game-dev/templates/game-design-doc-tmpl.yaml +63 -63
- package/expansion-packs/bmad-2d-unity-game-dev/templates/game-story-tmpl.yaml +20 -20
- package/expansion-packs/bmad-2d-unity-game-dev/templates/level-design-doc-tmpl.yaml +65 -65
- package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-dev-greenfield.yaml +5 -5
- package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-prototype.yaml +1 -1
- package/expansion-packs/bmad-creative-writing/README.md +132 -0
- package/expansion-packs/bmad-creative-writing/agent-teams/agent-team.yaml +19 -0
- package/expansion-packs/bmad-creative-writing/agents/beta-reader.md +91 -0
- package/expansion-packs/bmad-creative-writing/agents/book-critic.md +35 -0
- package/expansion-packs/bmad-creative-writing/agents/character-psychologist.md +90 -0
- package/expansion-packs/bmad-creative-writing/agents/cover-designer.md +41 -0
- package/expansion-packs/bmad-creative-writing/agents/dialog-specialist.md +89 -0
- package/expansion-packs/bmad-creative-writing/agents/editor.md +90 -0
- package/expansion-packs/bmad-creative-writing/agents/genre-specialist.md +92 -0
- package/expansion-packs/bmad-creative-writing/agents/narrative-designer.md +90 -0
- package/expansion-packs/bmad-creative-writing/agents/plot-architect.md +92 -0
- package/expansion-packs/bmad-creative-writing/agents/world-builder.md +91 -0
- package/expansion-packs/bmad-creative-writing/checklists/beta-feedback-closure-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/character-consistency-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/comedic-timing-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/cyberpunk-aesthetic-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/ebook-formatting-checklist.md +15 -0
- package/expansion-packs/bmad-creative-writing/checklists/epic-poetry-meter-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/fantasy-magic-system-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/foreshadowing-payoff-checklist.md +15 -0
- package/expansion-packs/bmad-creative-writing/checklists/genre-tropes-checklist.md +15 -0
- package/expansion-packs/bmad-creative-writing/checklists/historical-accuracy-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/horror-suspense-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/kdp-cover-ready-checklist.md +18 -0
- package/expansion-packs/bmad-creative-writing/checklists/line-edit-quality-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/marketing-copy-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/mystery-clue-trail-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/orbital-mechanics-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/plot-structure-checklist.md +49 -0
- package/expansion-packs/bmad-creative-writing/checklists/publication-readiness-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/romance-emotional-beats-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/scene-quality-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/scifi-technology-plausibility-checklist.md +15 -0
- package/expansion-packs/bmad-creative-writing/checklists/sensitivity-representation-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/steampunk-gadget-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/thriller-pacing-stakes-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/timeline-continuity-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/world-building-continuity-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/checklists/ya-appropriateness-checklist.md +16 -0
- package/expansion-packs/bmad-creative-writing/config.yaml +11 -0
- package/expansion-packs/bmad-creative-writing/data/bmad-kb.md +197 -0
- package/expansion-packs/bmad-creative-writing/data/story-structures.md +58 -0
- package/expansion-packs/bmad-creative-writing/docs/brief.md +183 -0
- package/expansion-packs/bmad-creative-writing/tasks/advanced-elicitation.md +117 -0
- package/expansion-packs/bmad-creative-writing/tasks/analyze-reader-feedback.md +16 -0
- package/expansion-packs/bmad-creative-writing/tasks/analyze-story-structure.md +55 -0
- package/expansion-packs/bmad-creative-writing/tasks/assemble-kdp-package.md +22 -0
- package/expansion-packs/bmad-creative-writing/tasks/brainstorm-premise.md +16 -0
- package/expansion-packs/bmad-creative-writing/tasks/build-world.md +17 -0
- package/expansion-packs/bmad-creative-writing/tasks/character-depth-pass.md +15 -0
- package/expansion-packs/bmad-creative-writing/tasks/create-doc.md +101 -0
- package/expansion-packs/bmad-creative-writing/tasks/create-draft-section.md +19 -0
- package/expansion-packs/bmad-creative-writing/tasks/critical-review.md +19 -0
- package/expansion-packs/bmad-creative-writing/tasks/develop-character.md +17 -0
- package/expansion-packs/bmad-creative-writing/tasks/execute-checklist.md +93 -0
- package/expansion-packs/bmad-creative-writing/tasks/expand-premise.md +16 -0
- package/expansion-packs/bmad-creative-writing/tasks/expand-synopsis.md +16 -0
- package/expansion-packs/bmad-creative-writing/tasks/final-polish.md +16 -0
- package/expansion-packs/bmad-creative-writing/tasks/generate-cover-brief.md +18 -0
- package/expansion-packs/bmad-creative-writing/tasks/generate-cover-prompts.md +19 -0
- package/expansion-packs/bmad-creative-writing/tasks/generate-scene-list.md +16 -0
- package/expansion-packs/bmad-creative-writing/tasks/incorporate-feedback.md +18 -0
- package/expansion-packs/bmad-creative-writing/tasks/outline-scenes.md +16 -0
- package/expansion-packs/bmad-creative-writing/tasks/provide-feedback.md +17 -0
- package/expansion-packs/bmad-creative-writing/tasks/publish-chapter.md +16 -0
- package/expansion-packs/bmad-creative-writing/tasks/quick-feedback.md +15 -0
- package/expansion-packs/bmad-creative-writing/tasks/select-next-arc.md +16 -0
- package/expansion-packs/bmad-creative-writing/tasks/workshop-dialog.md +51 -0
- package/expansion-packs/bmad-creative-writing/templates/beta-feedback-form.yaml +96 -0
- package/expansion-packs/bmad-creative-writing/templates/chapter-draft-tmpl.yaml +81 -0
- package/expansion-packs/bmad-creative-writing/templates/character-profile-tmpl.yaml +92 -0
- package/expansion-packs/bmad-creative-writing/templates/cover-design-brief-tmpl.yaml +97 -0
- package/expansion-packs/bmad-creative-writing/templates/premise-brief-tmpl.yaml +77 -0
- package/expansion-packs/bmad-creative-writing/templates/scene-list-tmpl.yaml +54 -0
- package/expansion-packs/bmad-creative-writing/templates/story-outline-tmpl.yaml +96 -0
- package/expansion-packs/bmad-creative-writing/templates/world-guide-tmpl.yaml +88 -0
- package/expansion-packs/bmad-creative-writing/workflows/book-cover-design-workflow.md +176 -0
- package/expansion-packs/bmad-creative-writing/workflows/novel-greenfield-workflow.yaml +58 -0
- package/expansion-packs/bmad-creative-writing/workflows/novel-serial-workflow.yaml +51 -0
- package/expansion-packs/bmad-creative-writing/workflows/novel-snowflake-workflow.yaml +69 -0
- package/expansion-packs/bmad-creative-writing/workflows/novel-writing.yaml +92 -0
- package/expansion-packs/bmad-creative-writing/workflows/screenplay-development.yaml +86 -0
- package/expansion-packs/bmad-creative-writing/workflows/series-planning.yaml +79 -0
- package/expansion-packs/bmad-creative-writing/workflows/short-story-creation.yaml +65 -0
- package/expansion-packs/bmad-infrastructure-devops/config.yaml +1 -1
- package/expansion-packs/bmad-infrastructure-devops/templates/infrastructure-architecture-tmpl.yaml +20 -20
- package/expansion-packs/bmad-infrastructure-devops/templates/infrastructure-platform-from-arch-tmpl.yaml +7 -7
- package/package.json +62 -39
- package/prettier.config.mjs +32 -0
- package/sync-version.sh +23 -0
- package/tools/bmad-npx-wrapper.js +10 -10
- package/tools/builders/web-builder.js +124 -130
- package/tools/bump-all-versions.js +42 -33
- package/tools/bump-expansion-version.js +23 -16
- package/tools/cli.js +10 -12
- package/tools/flattener/aggregate.js +10 -10
- package/tools/flattener/binary.js +44 -17
- package/tools/flattener/discovery.js +19 -18
- package/tools/flattener/files.js +6 -6
- package/tools/flattener/ignoreRules.js +125 -125
- package/tools/flattener/main.js +426 -70
- package/tools/flattener/projectRoot.js +186 -25
- package/tools/flattener/prompts.js +9 -9
- package/tools/flattener/stats.helpers.js +395 -0
- package/tools/flattener/stats.js +64 -14
- package/tools/flattener/test-matrix.js +413 -0
- package/tools/flattener/xml.js +33 -31
- package/tools/installer/bin/bmad.js +156 -113
- package/tools/installer/config/ide-agent-config.yaml +1 -1
- package/tools/installer/config/install.config.yaml +13 -3
- package/tools/installer/lib/config-loader.js +46 -42
- package/tools/installer/lib/file-manager.js +91 -113
- package/tools/installer/lib/ide-base-setup.js +57 -56
- package/tools/installer/lib/ide-setup.js +545 -399
- package/tools/installer/lib/installer.js +875 -714
- package/tools/installer/lib/memory-profiler.js +54 -53
- package/tools/installer/lib/module-manager.js +19 -15
- package/tools/installer/lib/resource-locator.js +26 -28
- package/tools/installer/package.json +19 -19
- package/tools/lib/dependency-resolver.js +26 -30
- package/tools/lib/yaml-utils.js +7 -7
- package/tools/preview-release-notes.js +66 -0
- package/tools/shared/bannerArt.js +3 -3
- package/tools/sync-installer-version.js +7 -9
- package/tools/update-expansion-version.js +14 -15
- package/tools/upgraders/v3-to-v4-upgrader.js +203 -294
- package/tools/version-bump.js +41 -26
- package/tools/yaml-format.js +56 -43
- package/.github/workflows/release.yaml +0 -60
- package/.releaserc.json +0 -21
- package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/Complete AI Agent System - Flowchart.svg +0 -102
- 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 +0 -13
- 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 +0 -13
- 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 +0 -25
- 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 +0 -34
- 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 +0 -70
- 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 +0 -26
- package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/README.md +0 -109
- package/tools/semantic-release-sync-installer.js +0 -30
package/tools/flattener/main.js
CHANGED
|
@@ -1,20 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const path = require("node:path");
|
|
6
|
-
const process = require("node:process");
|
|
1
|
+
const { Command } = require('commander');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const process = require('node:process');
|
|
7
5
|
|
|
8
6
|
// Modularized components
|
|
9
|
-
const { findProjectRoot } = require(
|
|
10
|
-
const { promptYesNo, promptPath } = require(
|
|
11
|
-
const {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
aggregateFileContents,
|
|
15
|
-
} = require("./files.js");
|
|
16
|
-
const { generateXMLOutput } = require("./xml.js");
|
|
17
|
-
const { calculateStatistics } = require("./stats.js");
|
|
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');
|
|
18
12
|
|
|
19
13
|
/**
|
|
20
14
|
* Recursively discover all files in a directory
|
|
@@ -73,30 +67,30 @@ const { calculateStatistics } = require("./stats.js");
|
|
|
73
67
|
const program = new Command();
|
|
74
68
|
|
|
75
69
|
program
|
|
76
|
-
.name(
|
|
77
|
-
.description(
|
|
78
|
-
.version(
|
|
79
|
-
.option(
|
|
80
|
-
.option(
|
|
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')
|
|
81
75
|
.action(async (options) => {
|
|
82
76
|
let inputDir = path.resolve(options.input);
|
|
83
77
|
let outputPath = path.resolve(options.output);
|
|
84
78
|
|
|
85
79
|
// Detect if user explicitly provided -i/--input or -o/--output
|
|
86
80
|
const argv = process.argv.slice(2);
|
|
87
|
-
const userSpecifiedInput = argv.some(
|
|
88
|
-
a ===
|
|
81
|
+
const userSpecifiedInput = argv.some(
|
|
82
|
+
(a) => a === '-i' || a === '--input' || a.startsWith('--input='),
|
|
89
83
|
);
|
|
90
|
-
const userSpecifiedOutput = argv.some(
|
|
91
|
-
a ===
|
|
84
|
+
const userSpecifiedOutput = argv.some(
|
|
85
|
+
(a) => a === '-o' || a === '--output' || a.startsWith('--output='),
|
|
92
86
|
);
|
|
93
|
-
const
|
|
87
|
+
const noPathArguments = !userSpecifiedInput && !userSpecifiedOutput;
|
|
94
88
|
|
|
95
|
-
if (
|
|
89
|
+
if (noPathArguments) {
|
|
96
90
|
const detectedRoot = await findProjectRoot(process.cwd());
|
|
97
91
|
const suggestedOutput = detectedRoot
|
|
98
|
-
? path.join(detectedRoot,
|
|
99
|
-
: path.resolve(
|
|
92
|
+
? path.join(detectedRoot, 'flattened-codebase.xml')
|
|
93
|
+
: path.resolve('flattened-codebase.xml');
|
|
100
94
|
|
|
101
95
|
if (detectedRoot) {
|
|
102
96
|
const useDefaults = await promptYesNo(
|
|
@@ -107,29 +101,23 @@ program
|
|
|
107
101
|
inputDir = detectedRoot;
|
|
108
102
|
outputPath = suggestedOutput;
|
|
109
103
|
} else {
|
|
110
|
-
inputDir = await promptPath(
|
|
111
|
-
"Enter input directory path",
|
|
112
|
-
process.cwd(),
|
|
113
|
-
);
|
|
104
|
+
inputDir = await promptPath('Enter input directory path', process.cwd());
|
|
114
105
|
outputPath = await promptPath(
|
|
115
|
-
|
|
116
|
-
path.join(inputDir,
|
|
106
|
+
'Enter output file path',
|
|
107
|
+
path.join(inputDir, 'flattened-codebase.xml'),
|
|
117
108
|
);
|
|
118
109
|
}
|
|
119
110
|
} else {
|
|
120
|
-
console.log(
|
|
121
|
-
inputDir = await promptPath(
|
|
122
|
-
"Enter input directory path",
|
|
123
|
-
process.cwd(),
|
|
124
|
-
);
|
|
111
|
+
console.log('Could not auto-detect a project root.');
|
|
112
|
+
inputDir = await promptPath('Enter input directory path', process.cwd());
|
|
125
113
|
outputPath = await promptPath(
|
|
126
|
-
|
|
127
|
-
path.join(inputDir,
|
|
114
|
+
'Enter output file path',
|
|
115
|
+
path.join(inputDir, 'flattened-codebase.xml'),
|
|
128
116
|
);
|
|
129
117
|
}
|
|
130
118
|
} else {
|
|
131
119
|
console.error(
|
|
132
|
-
|
|
120
|
+
'Could not auto-detect a project root and no arguments were provided. Please specify -i/--input and -o/--output.',
|
|
133
121
|
);
|
|
134
122
|
process.exit(1);
|
|
135
123
|
}
|
|
@@ -137,30 +125,25 @@ program
|
|
|
137
125
|
// Ensure output directory exists
|
|
138
126
|
await fs.ensureDir(path.dirname(outputPath));
|
|
139
127
|
|
|
140
|
-
console.log(`Flattening codebase from: ${inputDir}`);
|
|
141
|
-
console.log(`Output file: ${outputPath}`);
|
|
142
|
-
|
|
143
128
|
try {
|
|
144
129
|
// Verify input directory exists
|
|
145
|
-
if (!await fs.pathExists(inputDir)) {
|
|
130
|
+
if (!(await fs.pathExists(inputDir))) {
|
|
146
131
|
console.error(`โ Error: Input directory does not exist: ${inputDir}`);
|
|
147
132
|
process.exit(1);
|
|
148
133
|
}
|
|
149
134
|
|
|
150
135
|
// Import ora dynamically
|
|
151
|
-
const { default: ora } = await import(
|
|
136
|
+
const { default: ora } = await import('ora');
|
|
152
137
|
|
|
153
138
|
// Start file discovery with spinner
|
|
154
|
-
const discoverySpinner = ora(
|
|
139
|
+
const discoverySpinner = ora('๐ Discovering files...').start();
|
|
155
140
|
const files = await discoverFiles(inputDir);
|
|
156
141
|
const filteredFiles = await filterFiles(files, inputDir);
|
|
157
|
-
discoverySpinner.succeed(
|
|
158
|
-
`๐ Found ${filteredFiles.length} files to include`,
|
|
159
|
-
);
|
|
142
|
+
discoverySpinner.succeed(`๐ Found ${filteredFiles.length} files to include`);
|
|
160
143
|
|
|
161
144
|
// Process files with progress tracking
|
|
162
|
-
console.log(
|
|
163
|
-
const processingSpinner = ora(
|
|
145
|
+
console.log('Reading file contents');
|
|
146
|
+
const processingSpinner = ora('๐ Processing files...').start();
|
|
164
147
|
const aggregatedContent = await aggregateFileContents(
|
|
165
148
|
filteredFiles,
|
|
166
149
|
inputDir,
|
|
@@ -172,40 +155,413 @@ program
|
|
|
172
155
|
if (aggregatedContent.errors.length > 0) {
|
|
173
156
|
console.log(`Errors: ${aggregatedContent.errors.length}`);
|
|
174
157
|
}
|
|
175
|
-
console.log(`Text files: ${aggregatedContent.textFiles.length}`);
|
|
176
|
-
if (aggregatedContent.binaryFiles.length > 0) {
|
|
177
|
-
console.log(`Binary files: ${aggregatedContent.binaryFiles.length}`);
|
|
178
|
-
}
|
|
179
158
|
|
|
180
159
|
// Generate XML output using streaming
|
|
181
|
-
const xmlSpinner = ora(
|
|
160
|
+
const xmlSpinner = ora('๐ง Generating XML output...').start();
|
|
182
161
|
await generateXMLOutput(aggregatedContent, outputPath);
|
|
183
|
-
xmlSpinner.succeed(
|
|
162
|
+
xmlSpinner.succeed('๐ XML generation completed');
|
|
184
163
|
|
|
185
164
|
// Calculate and display statistics
|
|
186
165
|
const outputStats = await fs.stat(outputPath);
|
|
187
|
-
const stats = calculateStatistics(aggregatedContent, outputStats.size);
|
|
166
|
+
const stats = await calculateStatistics(aggregatedContent, outputStats.size, inputDir);
|
|
188
167
|
|
|
189
168
|
// Display completion summary
|
|
190
|
-
console.log(
|
|
169
|
+
console.log('\n๐ Completion Summary:');
|
|
191
170
|
console.log(
|
|
192
|
-
`โ
Successfully processed ${filteredFiles.length} files into ${
|
|
193
|
-
path.basename(outputPath)
|
|
194
|
-
}`,
|
|
171
|
+
`โ
Successfully processed ${filteredFiles.length} files into ${path.basename(outputPath)}`,
|
|
195
172
|
);
|
|
196
173
|
console.log(`๐ Output file: ${outputPath}`);
|
|
197
174
|
console.log(`๐ Total source size: ${stats.totalSize}`);
|
|
198
175
|
console.log(`๐ Generated XML size: ${stats.xmlSize}`);
|
|
199
|
-
console.log(
|
|
200
|
-
`๐ Total lines of code: ${stats.totalLines.toLocaleString()}`,
|
|
201
|
-
);
|
|
176
|
+
console.log(`๐ Total lines of code: ${stats.totalLines.toLocaleString()}`);
|
|
202
177
|
console.log(`๐ข Estimated tokens: ${stats.estimatedTokens}`);
|
|
203
178
|
console.log(
|
|
204
|
-
`๐ File breakdown: ${stats.textFiles} text, ${stats.binaryFiles} binary, ${stats.errorFiles} errors`,
|
|
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,
|
|
205
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
|
+
}
|
|
206
562
|
} catch (error) {
|
|
207
|
-
console.error(
|
|
208
|
-
console.error(
|
|
563
|
+
console.error('โ Critical error:', error.message);
|
|
564
|
+
console.error('An unexpected error occurred.');
|
|
209
565
|
process.exit(1);
|
|
210
566
|
}
|
|
211
567
|
});
|