bmad-method 4.37.0 → 4.39.0
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
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Helps identify memory leaks and optimize resource usage
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
const v8 = require('v8');
|
|
6
|
+
const v8 = require('node:v8');
|
|
7
7
|
|
|
8
8
|
class MemoryProfiler {
|
|
9
9
|
constructor() {
|
|
@@ -19,7 +19,7 @@ class MemoryProfiler {
|
|
|
19
19
|
checkpoint(label) {
|
|
20
20
|
const memUsage = process.memoryUsage();
|
|
21
21
|
const heapStats = v8.getHeapStatistics();
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
const checkpoint = {
|
|
24
24
|
label,
|
|
25
25
|
timestamp: Date.now() - this.startTime,
|
|
@@ -28,18 +28,18 @@ class MemoryProfiler {
|
|
|
28
28
|
heapTotal: this.formatBytes(memUsage.heapTotal),
|
|
29
29
|
heapUsed: this.formatBytes(memUsage.heapUsed),
|
|
30
30
|
external: this.formatBytes(memUsage.external),
|
|
31
|
-
arrayBuffers: this.formatBytes(memUsage.arrayBuffers || 0)
|
|
31
|
+
arrayBuffers: this.formatBytes(memUsage.arrayBuffers || 0),
|
|
32
32
|
},
|
|
33
33
|
heap: {
|
|
34
34
|
totalHeapSize: this.formatBytes(heapStats.total_heap_size),
|
|
35
35
|
usedHeapSize: this.formatBytes(heapStats.used_heap_size),
|
|
36
36
|
heapSizeLimit: this.formatBytes(heapStats.heap_size_limit),
|
|
37
37
|
mallocedMemory: this.formatBytes(heapStats.malloced_memory),
|
|
38
|
-
externalMemory: this.formatBytes(heapStats.external_memory)
|
|
38
|
+
externalMemory: this.formatBytes(heapStats.external_memory),
|
|
39
39
|
},
|
|
40
40
|
raw: {
|
|
41
|
-
heapUsed: memUsage.heapUsed
|
|
42
|
-
}
|
|
41
|
+
heapUsed: memUsage.heapUsed,
|
|
42
|
+
},
|
|
43
43
|
};
|
|
44
44
|
|
|
45
45
|
// Track peak memory
|
|
@@ -55,8 +55,8 @@ class MemoryProfiler {
|
|
|
55
55
|
* Force garbage collection (requires --expose-gc flag)
|
|
56
56
|
*/
|
|
57
57
|
forceGC() {
|
|
58
|
-
if (
|
|
59
|
-
|
|
58
|
+
if (globalThis.gc) {
|
|
59
|
+
globalThis.gc();
|
|
60
60
|
return true;
|
|
61
61
|
}
|
|
62
62
|
return false;
|
|
@@ -67,16 +67,16 @@ class MemoryProfiler {
|
|
|
67
67
|
*/
|
|
68
68
|
getSummary() {
|
|
69
69
|
const currentMemory = process.memoryUsage();
|
|
70
|
-
|
|
70
|
+
|
|
71
71
|
return {
|
|
72
72
|
currentUsage: {
|
|
73
73
|
rss: this.formatBytes(currentMemory.rss),
|
|
74
74
|
heapTotal: this.formatBytes(currentMemory.heapTotal),
|
|
75
|
-
heapUsed: this.formatBytes(currentMemory.heapUsed)
|
|
75
|
+
heapUsed: this.formatBytes(currentMemory.heapUsed),
|
|
76
76
|
},
|
|
77
77
|
peakMemory: this.formatBytes(this.peakMemory),
|
|
78
78
|
totalCheckpoints: this.checkpoints.length,
|
|
79
|
-
runTime: `${((Date.now() - this.startTime) / 1000).toFixed(2)}s
|
|
79
|
+
runTime: `${((Date.now() - this.startTime) / 1000).toFixed(2)}s`,
|
|
80
80
|
};
|
|
81
81
|
}
|
|
82
82
|
|
|
@@ -86,12 +86,12 @@ class MemoryProfiler {
|
|
|
86
86
|
getDetailedReport() {
|
|
87
87
|
const summary = this.getSummary();
|
|
88
88
|
const memoryGrowth = this.calculateMemoryGrowth();
|
|
89
|
-
|
|
89
|
+
|
|
90
90
|
return {
|
|
91
91
|
summary,
|
|
92
92
|
memoryGrowth,
|
|
93
93
|
checkpoints: this.checkpoints,
|
|
94
|
-
recommendations: this.getRecommendations(memoryGrowth)
|
|
94
|
+
recommendations: this.getRecommendations(memoryGrowth),
|
|
95
95
|
};
|
|
96
96
|
}
|
|
97
97
|
|
|
@@ -100,23 +100,23 @@ class MemoryProfiler {
|
|
|
100
100
|
*/
|
|
101
101
|
calculateMemoryGrowth() {
|
|
102
102
|
if (this.checkpoints.length < 2) return [];
|
|
103
|
-
|
|
103
|
+
|
|
104
104
|
const growth = [];
|
|
105
|
-
for (let
|
|
106
|
-
const
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
const heapDiff =
|
|
110
|
-
|
|
105
|
+
for (let index = 1; index < this.checkpoints.length; index++) {
|
|
106
|
+
const previous = this.checkpoints[index - 1];
|
|
107
|
+
const current = this.checkpoints[index];
|
|
108
|
+
|
|
109
|
+
const heapDiff = current.raw.heapUsed - previous.raw.heapUsed;
|
|
110
|
+
|
|
111
111
|
growth.push({
|
|
112
|
-
from:
|
|
113
|
-
to:
|
|
112
|
+
from: previous.label,
|
|
113
|
+
to: current.label,
|
|
114
114
|
heapGrowth: this.formatBytes(Math.abs(heapDiff)),
|
|
115
115
|
isIncrease: heapDiff > 0,
|
|
116
|
-
timeDiff: `${((
|
|
116
|
+
timeDiff: `${((current.timestamp - previous.timestamp) / 1000).toFixed(2)}s`,
|
|
117
117
|
});
|
|
118
118
|
}
|
|
119
|
-
|
|
119
|
+
|
|
120
120
|
return growth;
|
|
121
121
|
}
|
|
122
122
|
|
|
@@ -125,40 +125,41 @@ class MemoryProfiler {
|
|
|
125
125
|
*/
|
|
126
126
|
getRecommendations(memoryGrowth) {
|
|
127
127
|
const recommendations = [];
|
|
128
|
-
|
|
128
|
+
|
|
129
129
|
// Check for large memory growth
|
|
130
|
-
const largeGrowths = memoryGrowth.filter(g => {
|
|
130
|
+
const largeGrowths = memoryGrowth.filter((g) => {
|
|
131
131
|
const bytes = this.parseBytes(g.heapGrowth);
|
|
132
132
|
return bytes > 50 * 1024 * 1024; // 50MB
|
|
133
133
|
});
|
|
134
|
-
|
|
134
|
+
|
|
135
135
|
if (largeGrowths.length > 0) {
|
|
136
136
|
recommendations.push({
|
|
137
137
|
type: 'warning',
|
|
138
138
|
message: `Large memory growth detected in ${largeGrowths.length} operations`,
|
|
139
|
-
details: largeGrowths.map(g => `${g.from} → ${g.to}: ${g.heapGrowth}`)
|
|
139
|
+
details: largeGrowths.map((g) => `${g.from} → ${g.to}: ${g.heapGrowth}`),
|
|
140
140
|
});
|
|
141
141
|
}
|
|
142
|
-
|
|
142
|
+
|
|
143
143
|
// Check peak memory
|
|
144
|
-
if (this.peakMemory > 500 * 1024 * 1024) {
|
|
144
|
+
if (this.peakMemory > 500 * 1024 * 1024) {
|
|
145
|
+
// 500MB
|
|
145
146
|
recommendations.push({
|
|
146
147
|
type: 'warning',
|
|
147
148
|
message: `High peak memory usage: ${this.formatBytes(this.peakMemory)}`,
|
|
148
|
-
suggestion: 'Consider processing files in smaller batches'
|
|
149
|
+
suggestion: 'Consider processing files in smaller batches',
|
|
149
150
|
});
|
|
150
151
|
}
|
|
151
|
-
|
|
152
|
+
|
|
152
153
|
// Check for potential memory leaks
|
|
153
154
|
const continuousGrowth = this.checkContinuousGrowth();
|
|
154
155
|
if (continuousGrowth) {
|
|
155
156
|
recommendations.push({
|
|
156
157
|
type: 'error',
|
|
157
158
|
message: 'Potential memory leak detected',
|
|
158
|
-
details: 'Memory usage continuously increases without significant decreases'
|
|
159
|
+
details: 'Memory usage continuously increases without significant decreases',
|
|
159
160
|
});
|
|
160
161
|
}
|
|
161
|
-
|
|
162
|
+
|
|
162
163
|
return recommendations;
|
|
163
164
|
}
|
|
164
165
|
|
|
@@ -167,14 +168,14 @@ class MemoryProfiler {
|
|
|
167
168
|
*/
|
|
168
169
|
checkContinuousGrowth() {
|
|
169
170
|
if (this.checkpoints.length < 5) return false;
|
|
170
|
-
|
|
171
|
+
|
|
171
172
|
let increasingCount = 0;
|
|
172
|
-
for (let
|
|
173
|
-
if (this.checkpoints[
|
|
173
|
+
for (let index = 1; index < this.checkpoints.length; index++) {
|
|
174
|
+
if (this.checkpoints[index].raw.heapUsed > this.checkpoints[index - 1].raw.heapUsed) {
|
|
174
175
|
increasingCount++;
|
|
175
176
|
}
|
|
176
177
|
}
|
|
177
|
-
|
|
178
|
+
|
|
178
179
|
// If memory increases in more than 80% of checkpoints, might be a leak
|
|
179
180
|
return increasingCount / (this.checkpoints.length - 1) > 0.8;
|
|
180
181
|
}
|
|
@@ -184,31 +185,31 @@ class MemoryProfiler {
|
|
|
184
185
|
*/
|
|
185
186
|
formatBytes(bytes) {
|
|
186
187
|
if (bytes === 0) return '0 B';
|
|
187
|
-
|
|
188
|
+
|
|
188
189
|
const k = 1024;
|
|
189
190
|
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
return parseFloat((bytes / Math.pow(k,
|
|
191
|
+
const index = Math.floor(Math.log(bytes) / Math.log(k));
|
|
192
|
+
|
|
193
|
+
return Number.parseFloat((bytes / Math.pow(k, index)).toFixed(2)) + ' ' + sizes[index];
|
|
193
194
|
}
|
|
194
195
|
|
|
195
196
|
/**
|
|
196
197
|
* Parse human-readable bytes back to number
|
|
197
198
|
*/
|
|
198
|
-
parseBytes(
|
|
199
|
-
const match =
|
|
199
|
+
parseBytes(string_) {
|
|
200
|
+
const match = string_.match(/^([\d.]+)\s*([KMGT]?B?)$/i);
|
|
200
201
|
if (!match) return 0;
|
|
201
|
-
|
|
202
|
-
const value = parseFloat(match[1]);
|
|
202
|
+
|
|
203
|
+
const value = Number.parseFloat(match[1]);
|
|
203
204
|
const unit = match[2].toUpperCase();
|
|
204
|
-
|
|
205
|
+
|
|
205
206
|
const multipliers = {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
207
|
+
B: 1,
|
|
208
|
+
KB: 1024,
|
|
209
|
+
MB: 1024 * 1024,
|
|
210
|
+
GB: 1024 * 1024 * 1024,
|
|
210
211
|
};
|
|
211
|
-
|
|
212
|
+
|
|
212
213
|
return value * (multipliers[unit] || 1);
|
|
213
214
|
}
|
|
214
215
|
|
|
@@ -221,4 +222,4 @@ class MemoryProfiler {
|
|
|
221
222
|
}
|
|
222
223
|
|
|
223
224
|
// Export singleton instance
|
|
224
|
-
module.exports = new MemoryProfiler();
|
|
225
|
+
module.exports = new MemoryProfiler();
|
|
@@ -17,13 +17,13 @@ class ModuleManager {
|
|
|
17
17
|
const modules = await Promise.all([
|
|
18
18
|
this.getModule('chalk'),
|
|
19
19
|
this.getModule('ora'),
|
|
20
|
-
this.getModule('inquirer')
|
|
20
|
+
this.getModule('inquirer'),
|
|
21
21
|
]);
|
|
22
22
|
|
|
23
23
|
return {
|
|
24
24
|
chalk: modules[0],
|
|
25
25
|
ora: modules[1],
|
|
26
|
-
inquirer: modules[2]
|
|
26
|
+
inquirer: modules[2],
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -64,18 +64,24 @@ class ModuleManager {
|
|
|
64
64
|
*/
|
|
65
65
|
async _loadModule(moduleName) {
|
|
66
66
|
switch (moduleName) {
|
|
67
|
-
case 'chalk':
|
|
67
|
+
case 'chalk': {
|
|
68
68
|
return (await import('chalk')).default;
|
|
69
|
-
|
|
69
|
+
}
|
|
70
|
+
case 'ora': {
|
|
70
71
|
return (await import('ora')).default;
|
|
71
|
-
|
|
72
|
+
}
|
|
73
|
+
case 'inquirer': {
|
|
72
74
|
return (await import('inquirer')).default;
|
|
73
|
-
|
|
75
|
+
}
|
|
76
|
+
case 'glob': {
|
|
74
77
|
return (await import('glob')).glob;
|
|
75
|
-
|
|
78
|
+
}
|
|
79
|
+
case 'globSync': {
|
|
76
80
|
return (await import('glob')).globSync;
|
|
77
|
-
|
|
81
|
+
}
|
|
82
|
+
default: {
|
|
78
83
|
throw new Error(`Unknown module: ${moduleName}`);
|
|
84
|
+
}
|
|
79
85
|
}
|
|
80
86
|
}
|
|
81
87
|
|
|
@@ -93,13 +99,11 @@ class ModuleManager {
|
|
|
93
99
|
* @returns {Promise<Object>} Object with module names as keys
|
|
94
100
|
*/
|
|
95
101
|
async getModules(moduleNames) {
|
|
96
|
-
const modules = await Promise.all(
|
|
97
|
-
moduleNames.map(name => this.getModule(name))
|
|
98
|
-
);
|
|
102
|
+
const modules = await Promise.all(moduleNames.map((name) => this.getModule(name)));
|
|
99
103
|
|
|
100
|
-
return moduleNames.reduce((
|
|
101
|
-
|
|
102
|
-
return
|
|
104
|
+
return moduleNames.reduce((accumulator, name, index) => {
|
|
105
|
+
accumulator[name] = modules[index];
|
|
106
|
+
return accumulator;
|
|
103
107
|
}, {});
|
|
104
108
|
}
|
|
105
109
|
}
|
|
@@ -107,4 +111,4 @@ class ModuleManager {
|
|
|
107
111
|
// Singleton instance
|
|
108
112
|
const moduleManager = new ModuleManager();
|
|
109
113
|
|
|
110
|
-
module.exports = moduleManager;
|
|
114
|
+
module.exports = moduleManager;
|
|
@@ -43,18 +43,18 @@ class ResourceLocator {
|
|
|
43
43
|
*/
|
|
44
44
|
async findFiles(pattern, options = {}) {
|
|
45
45
|
const cacheKey = `${pattern}:${JSON.stringify(options)}`;
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
if (this._globCache.has(cacheKey)) {
|
|
48
48
|
return this._globCache.get(cacheKey);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
const { glob } = await moduleManager.getModules(['glob']);
|
|
52
52
|
const files = await glob(pattern, options);
|
|
53
|
-
|
|
53
|
+
|
|
54
54
|
// Cache for 5 minutes
|
|
55
55
|
this._globCache.set(cacheKey, files);
|
|
56
56
|
setTimeout(() => this._globCache.delete(cacheKey), 5 * 60 * 1000);
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
return files;
|
|
59
59
|
}
|
|
60
60
|
|
|
@@ -65,7 +65,7 @@ class ResourceLocator {
|
|
|
65
65
|
*/
|
|
66
66
|
async getAgentPath(agentId) {
|
|
67
67
|
const cacheKey = `agent:${agentId}`;
|
|
68
|
-
|
|
68
|
+
|
|
69
69
|
if (this._pathCache.has(cacheKey)) {
|
|
70
70
|
return this._pathCache.get(cacheKey);
|
|
71
71
|
}
|
|
@@ -96,7 +96,7 @@ class ResourceLocator {
|
|
|
96
96
|
*/
|
|
97
97
|
async getAvailableAgents() {
|
|
98
98
|
const cacheKey = 'all-agents';
|
|
99
|
-
|
|
99
|
+
|
|
100
100
|
if (this._pathCache.has(cacheKey)) {
|
|
101
101
|
return this._pathCache.get(cacheKey);
|
|
102
102
|
}
|
|
@@ -107,14 +107,11 @@ class ResourceLocator {
|
|
|
107
107
|
|
|
108
108
|
// Get agents from bmad-core
|
|
109
109
|
const coreAgents = await this.findFiles('agents/*.md', {
|
|
110
|
-
cwd: this.getBmadCorePath()
|
|
110
|
+
cwd: this.getBmadCorePath(),
|
|
111
111
|
});
|
|
112
112
|
|
|
113
113
|
for (const agentFile of coreAgents) {
|
|
114
|
-
const content = await fs.readFile(
|
|
115
|
-
path.join(this.getBmadCorePath(), agentFile),
|
|
116
|
-
'utf8'
|
|
117
|
-
);
|
|
114
|
+
const content = await fs.readFile(path.join(this.getBmadCorePath(), agentFile), 'utf8');
|
|
118
115
|
const yamlContent = extractYamlFromAgent(content);
|
|
119
116
|
if (yamlContent) {
|
|
120
117
|
try {
|
|
@@ -123,9 +120,9 @@ class ResourceLocator {
|
|
|
123
120
|
id: path.basename(agentFile, '.md'),
|
|
124
121
|
name: metadata.agent_name || path.basename(agentFile, '.md'),
|
|
125
122
|
description: metadata.description || 'No description available',
|
|
126
|
-
source: 'core'
|
|
123
|
+
source: 'core',
|
|
127
124
|
});
|
|
128
|
-
} catch
|
|
125
|
+
} catch {
|
|
129
126
|
// Skip invalid agents
|
|
130
127
|
}
|
|
131
128
|
}
|
|
@@ -144,7 +141,7 @@ class ResourceLocator {
|
|
|
144
141
|
*/
|
|
145
142
|
async getExpansionPacks() {
|
|
146
143
|
const cacheKey = 'expansion-packs';
|
|
147
|
-
|
|
144
|
+
|
|
148
145
|
if (this._pathCache.has(cacheKey)) {
|
|
149
146
|
return this._pathCache.get(cacheKey);
|
|
150
147
|
}
|
|
@@ -154,7 +151,7 @@ class ResourceLocator {
|
|
|
154
151
|
|
|
155
152
|
if (await fs.pathExists(expansionPacksPath)) {
|
|
156
153
|
const entries = await fs.readdir(expansionPacksPath, { withFileTypes: true });
|
|
157
|
-
|
|
154
|
+
|
|
158
155
|
for (const entry of entries) {
|
|
159
156
|
if (entry.isDirectory()) {
|
|
160
157
|
const configPath = path.join(expansionPacksPath, entry.name, 'config.yaml');
|
|
@@ -167,11 +164,12 @@ class ResourceLocator {
|
|
|
167
164
|
name: config.name || entry.name,
|
|
168
165
|
version: config.version || '1.0.0',
|
|
169
166
|
description: config.description || 'No description available',
|
|
170
|
-
shortTitle:
|
|
167
|
+
shortTitle:
|
|
168
|
+
config['short-title'] || config.description || 'No description available',
|
|
171
169
|
author: config.author || 'Unknown',
|
|
172
|
-
path: path.join(expansionPacksPath, entry.name)
|
|
170
|
+
path: path.join(expansionPacksPath, entry.name),
|
|
173
171
|
});
|
|
174
|
-
} catch
|
|
172
|
+
} catch {
|
|
175
173
|
// Skip invalid packs
|
|
176
174
|
}
|
|
177
175
|
}
|
|
@@ -193,13 +191,13 @@ class ResourceLocator {
|
|
|
193
191
|
*/
|
|
194
192
|
async getTeamConfig(teamId) {
|
|
195
193
|
const cacheKey = `team:${teamId}`;
|
|
196
|
-
|
|
194
|
+
|
|
197
195
|
if (this._pathCache.has(cacheKey)) {
|
|
198
196
|
return this._pathCache.get(cacheKey);
|
|
199
197
|
}
|
|
200
198
|
|
|
201
199
|
const teamPath = path.join(this.getBmadCorePath(), 'agent-teams', `${teamId}.yaml`);
|
|
202
|
-
|
|
200
|
+
|
|
203
201
|
if (await fs.pathExists(teamPath)) {
|
|
204
202
|
try {
|
|
205
203
|
const yaml = require('js-yaml');
|
|
@@ -207,7 +205,7 @@ class ResourceLocator {
|
|
|
207
205
|
const config = yaml.load(content);
|
|
208
206
|
this._pathCache.set(cacheKey, config);
|
|
209
207
|
return config;
|
|
210
|
-
} catch
|
|
208
|
+
} catch {
|
|
211
209
|
return null;
|
|
212
210
|
}
|
|
213
211
|
}
|
|
@@ -222,7 +220,7 @@ class ResourceLocator {
|
|
|
222
220
|
*/
|
|
223
221
|
async getAgentDependencies(agentId) {
|
|
224
222
|
const cacheKey = `deps:${agentId}`;
|
|
225
|
-
|
|
223
|
+
|
|
226
224
|
if (this._pathCache.has(cacheKey)) {
|
|
227
225
|
return this._pathCache.get(cacheKey);
|
|
228
226
|
}
|
|
@@ -244,11 +242,11 @@ class ResourceLocator {
|
|
|
244
242
|
const yaml = require('js-yaml');
|
|
245
243
|
const metadata = yaml.load(yamlContent);
|
|
246
244
|
const dependencies = metadata.dependencies || {};
|
|
247
|
-
|
|
245
|
+
|
|
248
246
|
// Flatten dependencies
|
|
249
247
|
const allDeps = [];
|
|
250
248
|
const byType = {};
|
|
251
|
-
|
|
249
|
+
|
|
252
250
|
for (const [type, deps] of Object.entries(dependencies)) {
|
|
253
251
|
if (Array.isArray(deps)) {
|
|
254
252
|
byType[type] = deps;
|
|
@@ -261,7 +259,7 @@ class ResourceLocator {
|
|
|
261
259
|
const result = { all: allDeps, byType };
|
|
262
260
|
this._pathCache.set(cacheKey, result);
|
|
263
261
|
return result;
|
|
264
|
-
} catch
|
|
262
|
+
} catch {
|
|
265
263
|
return { all: [], byType: {} };
|
|
266
264
|
}
|
|
267
265
|
}
|
|
@@ -281,13 +279,13 @@ class ResourceLocator {
|
|
|
281
279
|
*/
|
|
282
280
|
async getIdeConfig(ideId) {
|
|
283
281
|
const cacheKey = `ide:${ideId}`;
|
|
284
|
-
|
|
282
|
+
|
|
285
283
|
if (this._pathCache.has(cacheKey)) {
|
|
286
284
|
return this._pathCache.get(cacheKey);
|
|
287
285
|
}
|
|
288
286
|
|
|
289
287
|
const idePath = path.join(this.getBmadCorePath(), 'ide-rules', `${ideId}.yaml`);
|
|
290
|
-
|
|
288
|
+
|
|
291
289
|
if (await fs.pathExists(idePath)) {
|
|
292
290
|
try {
|
|
293
291
|
const yaml = require('js-yaml');
|
|
@@ -295,7 +293,7 @@ class ResourceLocator {
|
|
|
295
293
|
const config = yaml.load(content);
|
|
296
294
|
this._pathCache.set(cacheKey, config);
|
|
297
295
|
return config;
|
|
298
|
-
} catch
|
|
296
|
+
} catch {
|
|
299
297
|
return null;
|
|
300
298
|
}
|
|
301
299
|
}
|
|
@@ -307,4 +305,4 @@ class ResourceLocator {
|
|
|
307
305
|
// Singleton instance
|
|
308
306
|
const resourceLocator = new ResourceLocator();
|
|
309
307
|
|
|
310
|
-
module.exports = resourceLocator;
|
|
308
|
+
module.exports = resourceLocator;
|
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bmad-method",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.1.3",
|
|
4
4
|
"description": "BMad Method installer - AI-powered Agile development framework",
|
|
5
|
-
"main": "lib/installer.js",
|
|
6
|
-
"bin": {
|
|
7
|
-
"bmad": "./bin/bmad.js",
|
|
8
|
-
"bmad-method": "./bin/bmad.js"
|
|
9
|
-
},
|
|
10
|
-
"scripts": {
|
|
11
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
12
|
-
},
|
|
13
5
|
"keywords": [
|
|
14
6
|
"bmad",
|
|
15
7
|
"agile",
|
|
@@ -19,8 +11,24 @@
|
|
|
19
11
|
"installer",
|
|
20
12
|
"agents"
|
|
21
13
|
],
|
|
22
|
-
"
|
|
14
|
+
"homepage": "https://github.com/bmad-team/bmad-method#readme",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/bmad-team/bmad-method/issues"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/bmad-team/bmad-method.git"
|
|
21
|
+
},
|
|
23
22
|
"license": "MIT",
|
|
23
|
+
"author": "BMad Team",
|
|
24
|
+
"main": "lib/installer.js",
|
|
25
|
+
"bin": {
|
|
26
|
+
"bmad": "./bin/bmad.js",
|
|
27
|
+
"bmad-method": "./bin/bmad.js"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
31
|
+
},
|
|
24
32
|
"dependencies": {
|
|
25
33
|
"chalk": "^4.1.2",
|
|
26
34
|
"commander": "^14.0.0",
|
|
@@ -32,13 +40,5 @@
|
|
|
32
40
|
},
|
|
33
41
|
"engines": {
|
|
34
42
|
"node": ">=20.0.0"
|
|
35
|
-
}
|
|
36
|
-
"repository": {
|
|
37
|
-
"type": "git",
|
|
38
|
-
"url": "https://github.com/bmad-team/bmad-method.git"
|
|
39
|
-
},
|
|
40
|
-
"bugs": {
|
|
41
|
-
"url": "https://github.com/bmad-team/bmad-method/issues"
|
|
42
|
-
},
|
|
43
|
-
"homepage": "https://github.com/bmad-team/bmad-method#readme"
|
|
43
|
+
}
|
|
44
44
|
}
|