observer-ggboy-bmad-method 6.0.0-alpha.23
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/.coderabbit.yaml +40 -0
- package/.github/CODE_OF_CONDUCT.md +128 -0
- package/.github/FUNDING.yaml +15 -0
- package/.github/ISSUE_TEMPLATE/config.yaml +8 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +22 -0
- package/.github/ISSUE_TEMPLATE/issue.md +32 -0
- package/.github/scripts/discord-helpers.sh +34 -0
- package/.github/workflows/bundle-latest.yaml +330 -0
- package/.github/workflows/discord.yaml +90 -0
- package/.github/workflows/docs.yaml +63 -0
- package/.github/workflows/manual-release.yaml +190 -0
- package/.github/workflows/quality.yaml +115 -0
- package/.health-status.json +7 -0
- package/.husky/pre-commit +20 -0
- package/.markdownlint-cli2.yaml +41 -0
- package/.nvmrc +1 -0
- package/.prettierignore +9 -0
- package/.vscode/settings.json +97 -0
- package/CHANGELOG.md +1394 -0
- package/CNAME +1 -0
- package/CONTRIBUTING.md +167 -0
- package/CONTRIBUTORS.md +32 -0
- package/LICENSE +30 -0
- package/README.md +100 -0
- package/SECURITY.md +85 -0
- package/TRADEMARK.md +55 -0
- package/Wordmark.png +0 -0
- package/banner-bmad-method.png +0 -0
- package/bmad-method-6.0.0-alpha.23.tgz +0 -0
- package/docs/404.md +9 -0
- package/docs/_README_WORKFLOW_DIAGRAMS.md +40 -0
- package/docs/_STYLE_GUIDE.md +367 -0
- package/docs/_archive/customize-workflows.md +30 -0
- package/docs/_archive/getting-started-bmadv4.md +247 -0
- package/docs/_archive/vendor-workflows.md +52 -0
- package/docs/downloads.md +72 -0
- package/docs/explanation/agents/barry-quick-flow.md +328 -0
- package/docs/explanation/agents/index.md +19 -0
- package/docs/explanation/architecture/four-phases.md +107 -0
- package/docs/explanation/architecture/preventing-agent-conflicts.md +111 -0
- package/docs/explanation/architecture/why-solutioning-matters.md +75 -0
- package/docs/explanation/bmm/index.md +131 -0
- package/docs/explanation/core/index.md +18 -0
- package/docs/explanation/core-concepts/agent-roles.md +179 -0
- package/docs/explanation/core-concepts/index.md +35 -0
- package/docs/explanation/core-concepts/what-are-agents.md +97 -0
- package/docs/explanation/core-concepts/what-are-modules.md +85 -0
- package/docs/explanation/core-concepts/what-are-workflows.md +204 -0
- package/docs/explanation/faq/brownfield-faq.md +73 -0
- package/docs/explanation/faq/getting-started-faq.md +67 -0
- package/docs/explanation/faq/implementation-faq.md +52 -0
- package/docs/explanation/faq/index.md +16 -0
- package/docs/explanation/faq/levels-and-tracks-faq.md +52 -0
- package/docs/explanation/faq/planning-faq.md +41 -0
- package/docs/explanation/faq/tools-faq.md +277 -0
- package/docs/explanation/faq/workflows-faq.md +61 -0
- package/docs/explanation/features/advanced-elicitation.md +95 -0
- package/docs/explanation/features/brainstorming-techniques.md +92 -0
- package/docs/explanation/features/party-mode.md +95 -0
- package/docs/explanation/features/quick-flow.md +149 -0
- package/docs/explanation/features/tea-overview.md +410 -0
- package/docs/explanation/features/web-bundles.md +34 -0
- package/docs/explanation/philosophy/facilitation-over-generation.md +333 -0
- package/docs/explanation/philosophy/testing-as-engineering.md +112 -0
- package/docs/explanation/tea/engagement-models.md +710 -0
- package/docs/explanation/tea/fixture-architecture.md +457 -0
- package/docs/explanation/tea/knowledge-base-system.md +554 -0
- package/docs/explanation/tea/network-first-patterns.md +853 -0
- package/docs/explanation/tea/risk-based-testing.md +586 -0
- package/docs/explanation/tea/test-quality-standards.md +907 -0
- package/docs/how-to/brownfield/add-feature-to-existing.md +74 -0
- package/docs/how-to/brownfield/document-existing-project.md +66 -0
- package/docs/how-to/brownfield/index.md +84 -0
- package/docs/how-to/brownfield/quick-fix-in-brownfield.md +77 -0
- package/docs/how-to/brownfield/use-tea-for-enterprise.md +525 -0
- package/docs/how-to/brownfield/use-tea-with-existing-tests.md +577 -0
- package/docs/how-to/customization/customize-agents.md +212 -0
- package/docs/how-to/customization/enable-tea-mcp-enhancements.md +424 -0
- package/docs/how-to/customization/index.md +23 -0
- package/docs/how-to/customization/integrate-playwright-utils.md +813 -0
- package/docs/how-to/customization/shard-large-documents.md +101 -0
- package/docs/how-to/get-answers-about-bmad.md +102 -0
- package/docs/how-to/installation/index.md +12 -0
- package/docs/how-to/installation/install-bmad.md +111 -0
- package/docs/how-to/installation/install-custom-modules.md +118 -0
- package/docs/how-to/installation/upgrade-to-v6.md +131 -0
- package/docs/how-to/workflows/bmgd-quick-flow.md +156 -0
- package/docs/how-to/workflows/conduct-research.md +97 -0
- package/docs/how-to/workflows/create-architecture.md +119 -0
- package/docs/how-to/workflows/create-epics-and-stories.md +109 -0
- package/docs/how-to/workflows/create-prd.md +91 -0
- package/docs/how-to/workflows/create-product-brief.md +94 -0
- package/docs/how-to/workflows/create-story.md +102 -0
- package/docs/how-to/workflows/create-ux-design.md +100 -0
- package/docs/how-to/workflows/implement-story.md +97 -0
- package/docs/how-to/workflows/quick-spec.md +122 -0
- package/docs/how-to/workflows/run-atdd.md +436 -0
- package/docs/how-to/workflows/run-automate.md +653 -0
- package/docs/how-to/workflows/run-brainstorming-session.md +73 -0
- package/docs/how-to/workflows/run-code-review.md +89 -0
- package/docs/how-to/workflows/run-implementation-readiness.md +125 -0
- package/docs/how-to/workflows/run-nfr-assess.md +679 -0
- package/docs/how-to/workflows/run-sprint-planning.md +94 -0
- package/docs/how-to/workflows/run-test-design.md +135 -0
- package/docs/how-to/workflows/run-test-review.md +605 -0
- package/docs/how-to/workflows/run-trace.md +883 -0
- package/docs/how-to/workflows/setup-ci.md +712 -0
- package/docs/how-to/workflows/setup-party-mode.md +89 -0
- package/docs/how-to/workflows/setup-test-framework.md +98 -0
- package/docs/index.md +63 -0
- package/docs/reference/agents/index.md +109 -0
- package/docs/reference/configuration/core-tasks.md +67 -0
- package/docs/reference/configuration/global-config.md +28 -0
- package/docs/reference/glossary/index.md +159 -0
- package/docs/reference/tea/commands.md +276 -0
- package/docs/reference/tea/configuration.md +678 -0
- package/docs/reference/tea/knowledge-base.md +340 -0
- package/docs/reference/workflows/core-workflows.md +32 -0
- package/docs/reference/workflows/document-project.md +73 -0
- package/docs/reference/workflows/index.md +12 -0
- package/docs/tutorials/getting-started/getting-started-bmadv6.md +246 -0
- package/docs/tutorials/getting-started/images/workflow-method-greenfield.excalidraw +5034 -0
- package/docs/tutorials/getting-started/images/workflow-method-greenfield.svg +4 -0
- package/docs/tutorials/getting-started/images/workflow-overview.jpg +0 -0
- package/docs/tutorials/getting-started/tea-lite-quickstart.md +444 -0
- package/docs/tutorials/getting-started/workflow-overview.jpg +0 -0
- package/eslint.config.mjs +152 -0
- package/package.json +117 -0
- package/prettier.config.mjs +32 -0
- package/src/bmm/_module-installer/installer.js +48 -0
- package/src/bmm/_module-installer/platform-specifics/claude-code.js +35 -0
- package/src/bmm/_module-installer/platform-specifics/windsurf.js +32 -0
- package/src/bmm/agents/analyst.agent.yaml +36 -0
- package/src/bmm/agents/architect.agent.yaml +28 -0
- package/src/bmm/agents/dev.agent.yaml +38 -0
- package/src/bmm/agents/pm.agent.yaml +46 -0
- package/src/bmm/agents/quick-flow-solo-dev.agent.yaml +32 -0
- package/src/bmm/agents/sm.agent.yaml +36 -0
- package/src/bmm/agents/tea.agent.yaml +63 -0
- package/src/bmm/agents/tech-writer/tech-writer-sidecar/documentation-standards.md +224 -0
- package/src/bmm/agents/tech-writer/tech-writer.agent.yaml +45 -0
- package/src/bmm/agents/ux-designer.agent.yaml +26 -0
- package/src/bmm/data/project-context-template.md +26 -0
- package/src/bmm/module-help.csv +32 -0
- package/src/bmm/module.yaml +44 -0
- package/src/bmm/sub-modules/claude-code/config.yaml +4 -0
- package/src/bmm/sub-modules/claude-code/injections.yaml +242 -0
- package/src/bmm/sub-modules/claude-code/readme.md +87 -0
- package/src/bmm/teams/default-party.csv +21 -0
- package/src/bmm/teams/team-fullstack.yaml +12 -0
- package/src/bmm/testarch/knowledge/adr-quality-readiness-checklist.md +350 -0
- package/src/bmm/testarch/knowledge/api-request.md +442 -0
- package/src/bmm/testarch/knowledge/api-testing-patterns.md +843 -0
- package/src/bmm/testarch/knowledge/auth-session.md +552 -0
- package/src/bmm/testarch/knowledge/burn-in.md +273 -0
- package/src/bmm/testarch/knowledge/ci-burn-in.md +675 -0
- package/src/bmm/testarch/knowledge/component-tdd.md +486 -0
- package/src/bmm/testarch/knowledge/contract-testing.md +957 -0
- package/src/bmm/testarch/knowledge/data-factories.md +500 -0
- package/src/bmm/testarch/knowledge/email-auth.md +721 -0
- package/src/bmm/testarch/knowledge/error-handling.md +725 -0
- package/src/bmm/testarch/knowledge/feature-flags.md +750 -0
- package/src/bmm/testarch/knowledge/file-utils.md +463 -0
- package/src/bmm/testarch/knowledge/fixture-architecture.md +401 -0
- package/src/bmm/testarch/knowledge/fixtures-composition.md +382 -0
- package/src/bmm/testarch/knowledge/intercept-network-call.md +430 -0
- package/src/bmm/testarch/knowledge/log.md +429 -0
- package/src/bmm/testarch/knowledge/network-error-monitor.md +405 -0
- package/src/bmm/testarch/knowledge/network-first.md +486 -0
- package/src/bmm/testarch/knowledge/network-recorder.md +527 -0
- package/src/bmm/testarch/knowledge/nfr-criteria.md +670 -0
- package/src/bmm/testarch/knowledge/overview.md +286 -0
- package/src/bmm/testarch/knowledge/playwright-config.md +730 -0
- package/src/bmm/testarch/knowledge/probability-impact.md +601 -0
- package/src/bmm/testarch/knowledge/recurse.md +421 -0
- package/src/bmm/testarch/knowledge/risk-governance.md +615 -0
- package/src/bmm/testarch/knowledge/selective-testing.md +732 -0
- package/src/bmm/testarch/knowledge/selector-resilience.md +527 -0
- package/src/bmm/testarch/knowledge/test-healing-patterns.md +644 -0
- package/src/bmm/testarch/knowledge/test-levels-framework.md +473 -0
- package/src/bmm/testarch/knowledge/test-priorities-matrix.md +373 -0
- package/src/bmm/testarch/knowledge/test-quality.md +664 -0
- package/src/bmm/testarch/knowledge/timing-debugging.md +372 -0
- package/src/bmm/testarch/knowledge/visual-debugging.md +524 -0
- package/src/bmm/testarch/tea-index.csv +35 -0
- package/src/bmm/workflows/1-analysis/create-product-brief/product-brief.template.md +10 -0
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-01-init.md +177 -0
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-01b-continue.md +161 -0
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-02-vision.md +199 -0
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-03-users.md +202 -0
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-04-metrics.md +205 -0
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-05-scope.md +219 -0
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-06-complete.md +162 -0
- package/src/bmm/workflows/1-analysis/create-product-brief/workflow.md +58 -0
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-01-init.md +137 -0
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-02-domain-analysis.md +229 -0
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-03-competitive-landscape.md +238 -0
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-04-regulatory-focus.md +206 -0
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-05-technical-trends.md +234 -0
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-06-research-synthesis.md +443 -0
- package/src/bmm/workflows/1-analysis/research/market-steps/step-01-init.md +182 -0
- package/src/bmm/workflows/1-analysis/research/market-steps/step-02-customer-behavior.md +237 -0
- package/src/bmm/workflows/1-analysis/research/market-steps/step-02-customer-insights.md +200 -0
- package/src/bmm/workflows/1-analysis/research/market-steps/step-03-customer-pain-points.md +249 -0
- package/src/bmm/workflows/1-analysis/research/market-steps/step-04-customer-decisions.md +259 -0
- package/src/bmm/workflows/1-analysis/research/market-steps/step-05-competitive-analysis.md +177 -0
- package/src/bmm/workflows/1-analysis/research/market-steps/step-06-research-completion.md +475 -0
- package/src/bmm/workflows/1-analysis/research/research.template.md +29 -0
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-01-init.md +137 -0
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-02-technical-overview.md +239 -0
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-03-integration-patterns.md +248 -0
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-04-architectural-patterns.md +202 -0
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-05-implementation-research.md +239 -0
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-06-research-synthesis.md +486 -0
- package/src/bmm/workflows/1-analysis/research/workflow.md +173 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-01-init.md +135 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-01b-continue.md +127 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-02-discovery.md +190 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-03-core-experience.md +216 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-04-emotional-response.md +219 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-05-inspiration.md +234 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-06-design-system.md +252 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-07-defining-experience.md +254 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-08-visual-foundation.md +224 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-09-design-directions.md +224 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-10-user-journeys.md +241 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-11-component-strategy.md +248 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-12-ux-patterns.md +237 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-13-responsive-accessibility.md +264 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-14-complete.md +171 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/ux-design-template.md +13 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md +43 -0
- package/src/bmm/workflows/2-plan-workflows/prd/data/domain-complexity.csv +13 -0
- package/src/bmm/workflows/2-plan-workflows/prd/data/prd-purpose.md +197 -0
- package/src/bmm/workflows/2-plan-workflows/prd/data/project-types.csv +11 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-c/step-01-init.md +191 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-c/step-01b-continue.md +153 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-c/step-02-discovery.md +224 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-c/step-03-success.md +226 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-c/step-04-journeys.md +213 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-c/step-05-domain.md +207 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-c/step-06-innovation.md +226 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-c/step-07-project-type.md +237 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-c/step-08-scoping.md +228 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-c/step-09-functional.md +231 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-c/step-10-nonfunctional.md +242 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-c/step-11-polish.md +217 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-c/step-12-complete.md +124 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-e/step-e-01-discovery.md +247 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-e/step-e-01b-legacy-conversion.md +208 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-e/step-e-02-review.md +249 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-e/step-e-03-edit.md +253 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-e/step-e-04-complete.md +168 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-01-discovery.md +218 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-02-format-detection.md +191 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-02b-parity-check.md +209 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-03-density-validation.md +174 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-04-brief-coverage-validation.md +214 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-05-measurability-validation.md +228 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-06-traceability-validation.md +217 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-07-implementation-leakage-validation.md +205 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-08-domain-compliance-validation.md +243 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-09-project-type-validation.md +263 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-10-smart-validation.md +209 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-11-holistic-quality-validation.md +264 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-12-completeness-validation.md +242 -0
- package/src/bmm/workflows/2-plan-workflows/prd/steps-v/step-v-13-report-complete.md +231 -0
- package/src/bmm/workflows/2-plan-workflows/prd/templates/prd-template.md +10 -0
- package/src/bmm/workflows/2-plan-workflows/prd/validation-report-prd-workflow.md +433 -0
- package/src/bmm/workflows/2-plan-workflows/prd/workflow.md +150 -0
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-01-document-discovery.md +190 -0
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-02-prd-analysis.md +178 -0
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-03-epic-coverage-validation.md +179 -0
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-04-ux-alignment.md +139 -0
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-05-epic-quality-review.md +252 -0
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-06-final-assessment.md +135 -0
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/templates/readiness-report-template.md +4 -0
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md +55 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/architecture-decision-template.md +12 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/data/domain-complexity.csv +11 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/data/project-types.csv +7 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-01-init.md +153 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-01b-continue.md +164 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-02-context.md +224 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-03-starter.md +331 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-04-decisions.md +318 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-05-patterns.md +359 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-06-structure.md +379 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-07-validation.md +359 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-08-complete.md +76 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/workflow.md +50 -0
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-01-validate-prerequisites.md +259 -0
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-02-design-epics.md +233 -0
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-03-create-stories.md +272 -0
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-04-final-validation.md +149 -0
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/templates/epics-template.md +57 -0
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md +59 -0
- package/src/bmm/workflows/4-implementation/code-review/checklist.md +23 -0
- package/src/bmm/workflows/4-implementation/code-review/instructions.xml +227 -0
- package/src/bmm/workflows/4-implementation/code-review/workflow.yaml +51 -0
- package/src/bmm/workflows/4-implementation/correct-course/checklist.md +288 -0
- package/src/bmm/workflows/4-implementation/correct-course/instructions.md +206 -0
- package/src/bmm/workflows/4-implementation/correct-course/workflow.yaml +60 -0
- package/src/bmm/workflows/4-implementation/create-story/checklist.md +358 -0
- package/src/bmm/workflows/4-implementation/create-story/instructions.xml +345 -0
- package/src/bmm/workflows/4-implementation/create-story/template.md +49 -0
- package/src/bmm/workflows/4-implementation/create-story/workflow.yaml +61 -0
- package/src/bmm/workflows/4-implementation/dev-story/checklist.md +80 -0
- package/src/bmm/workflows/4-implementation/dev-story/instructions.xml +410 -0
- package/src/bmm/workflows/4-implementation/dev-story/workflow.yaml +27 -0
- package/src/bmm/workflows/4-implementation/retrospective/instructions.md +1443 -0
- package/src/bmm/workflows/4-implementation/retrospective/workflow.yaml +58 -0
- package/src/bmm/workflows/4-implementation/sprint-planning/checklist.md +33 -0
- package/src/bmm/workflows/4-implementation/sprint-planning/instructions.md +225 -0
- package/src/bmm/workflows/4-implementation/sprint-planning/sprint-status-template.yaml +55 -0
- package/src/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +54 -0
- package/src/bmm/workflows/4-implementation/sprint-status/instructions.md +229 -0
- package/src/bmm/workflows/4-implementation/sprint-status/workflow.yaml +36 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/data/project-levels.yaml +59 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-01-mode-detection.md +156 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-02-context-gathering.md +120 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-03-execute.md +113 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-04-self-check.md +113 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-05-adversarial-review.md +106 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-06-resolve-findings.md +140 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/workflow.md +50 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-01-understand.md +189 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-02-investigate.md +144 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-03-generate.md +128 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-04-review.md +191 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/tech-spec-template.md +74 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md +79 -0
- package/src/bmm/workflows/document-project/checklist.md +245 -0
- package/src/bmm/workflows/document-project/documentation-requirements.csv +12 -0
- package/src/bmm/workflows/document-project/instructions.md +221 -0
- package/src/bmm/workflows/document-project/templates/deep-dive-template.md +345 -0
- package/src/bmm/workflows/document-project/templates/index-template.md +169 -0
- package/src/bmm/workflows/document-project/templates/project-overview-template.md +103 -0
- package/src/bmm/workflows/document-project/templates/project-scan-report-schema.json +160 -0
- package/src/bmm/workflows/document-project/templates/source-tree-template.md +135 -0
- package/src/bmm/workflows/document-project/workflow.yaml +30 -0
- package/src/bmm/workflows/document-project/workflows/deep-dive-instructions.md +298 -0
- package/src/bmm/workflows/document-project/workflows/deep-dive.yaml +31 -0
- package/src/bmm/workflows/document-project/workflows/full-scan-instructions.md +1106 -0
- package/src/bmm/workflows/document-project/workflows/full-scan.yaml +31 -0
- package/src/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-library.json +90 -0
- package/src/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-templates.yaml +127 -0
- package/src/bmm/workflows/excalidraw-diagrams/create-dataflow/checklist.md +39 -0
- package/src/bmm/workflows/excalidraw-diagrams/create-dataflow/instructions.md +130 -0
- package/src/bmm/workflows/excalidraw-diagrams/create-dataflow/workflow.yaml +27 -0
- package/src/bmm/workflows/excalidraw-diagrams/create-diagram/checklist.md +43 -0
- package/src/bmm/workflows/excalidraw-diagrams/create-diagram/instructions.md +141 -0
- package/src/bmm/workflows/excalidraw-diagrams/create-diagram/workflow.yaml +27 -0
- package/src/bmm/workflows/excalidraw-diagrams/create-flowchart/checklist.md +49 -0
- package/src/bmm/workflows/excalidraw-diagrams/create-flowchart/instructions.md +241 -0
- package/src/bmm/workflows/excalidraw-diagrams/create-flowchart/workflow.yaml +27 -0
- package/src/bmm/workflows/excalidraw-diagrams/create-wireframe/checklist.md +38 -0
- package/src/bmm/workflows/excalidraw-diagrams/create-wireframe/instructions.md +133 -0
- package/src/bmm/workflows/excalidraw-diagrams/create-wireframe/workflow.yaml +27 -0
- package/src/bmm/workflows/testarch/atdd/atdd-checklist-template.md +363 -0
- package/src/bmm/workflows/testarch/atdd/checklist.md +374 -0
- package/src/bmm/workflows/testarch/atdd/instructions.md +806 -0
- package/src/bmm/workflows/testarch/atdd/workflow.yaml +47 -0
- package/src/bmm/workflows/testarch/automate/checklist.md +582 -0
- package/src/bmm/workflows/testarch/automate/instructions.md +1324 -0
- package/src/bmm/workflows/testarch/automate/workflow.yaml +54 -0
- package/src/bmm/workflows/testarch/ci/checklist.md +247 -0
- package/src/bmm/workflows/testarch/ci/github-actions-template.yaml +198 -0
- package/src/bmm/workflows/testarch/ci/gitlab-ci-template.yaml +149 -0
- package/src/bmm/workflows/testarch/ci/instructions.md +536 -0
- package/src/bmm/workflows/testarch/ci/workflow.yaml +47 -0
- package/src/bmm/workflows/testarch/framework/checklist.md +320 -0
- package/src/bmm/workflows/testarch/framework/instructions.md +481 -0
- package/src/bmm/workflows/testarch/framework/workflow.yaml +49 -0
- package/src/bmm/workflows/testarch/nfr-assess/checklist.md +407 -0
- package/src/bmm/workflows/testarch/nfr-assess/instructions.md +726 -0
- package/src/bmm/workflows/testarch/nfr-assess/nfr-report-template.md +461 -0
- package/src/bmm/workflows/testarch/nfr-assess/workflow.yaml +49 -0
- package/src/bmm/workflows/testarch/test-design/checklist.md +407 -0
- package/src/bmm/workflows/testarch/test-design/instructions.md +1158 -0
- package/src/bmm/workflows/testarch/test-design/test-design-architecture-template.md +213 -0
- package/src/bmm/workflows/testarch/test-design/test-design-qa-template.md +286 -0
- package/src/bmm/workflows/testarch/test-design/test-design-template.md +294 -0
- package/src/bmm/workflows/testarch/test-design/workflow.yaml +71 -0
- package/src/bmm/workflows/testarch/test-review/checklist.md +472 -0
- package/src/bmm/workflows/testarch/test-review/instructions.md +628 -0
- package/src/bmm/workflows/testarch/test-review/test-review-template.md +390 -0
- package/src/bmm/workflows/testarch/test-review/workflow.yaml +48 -0
- package/src/bmm/workflows/testarch/trace/checklist.md +642 -0
- package/src/bmm/workflows/testarch/trace/instructions.md +1030 -0
- package/src/bmm/workflows/testarch/trace/trace-template.md +675 -0
- package/src/bmm/workflows/testarch/trace/workflow.yaml +57 -0
- package/src/core/_module-installer/installer.js +60 -0
- package/src/core/agents/bmad-master.agent.yaml +29 -0
- package/src/core/module-help.csv +9 -0
- package/src/core/module.yaml +25 -0
- package/src/core/resources/excalidraw/README.md +160 -0
- package/src/core/resources/excalidraw/excalidraw-helpers.md +127 -0
- package/src/core/resources/excalidraw/library-loader.md +50 -0
- package/src/core/resources/excalidraw/validate-json-instructions.md +79 -0
- package/src/core/tasks/editorial-review-prose.xml +100 -0
- package/src/core/tasks/editorial-review-structure.xml +209 -0
- package/src/core/tasks/help.md +62 -0
- package/src/core/tasks/index-docs.xml +65 -0
- package/src/core/tasks/review-adversarial-general.xml +48 -0
- package/src/core/tasks/shard-doc.xml +109 -0
- package/src/core/tasks/workflow.xml +235 -0
- package/src/core/workflows/advanced-elicitation/methods.csv +51 -0
- package/src/core/workflows/advanced-elicitation/workflow.xml +117 -0
- package/src/core/workflows/brainstorming/brain-methods.csv +62 -0
- package/src/core/workflows/brainstorming/steps/step-01-session-setup.md +197 -0
- package/src/core/workflows/brainstorming/steps/step-01b-continue.md +122 -0
- package/src/core/workflows/brainstorming/steps/step-02a-user-selected.md +225 -0
- package/src/core/workflows/brainstorming/steps/step-02b-ai-recommended.md +237 -0
- package/src/core/workflows/brainstorming/steps/step-02c-random-selection.md +209 -0
- package/src/core/workflows/brainstorming/steps/step-02d-progressive-flow.md +264 -0
- package/src/core/workflows/brainstorming/steps/step-03-technique-execution.md +399 -0
- package/src/core/workflows/brainstorming/steps/step-04-idea-organization.md +303 -0
- package/src/core/workflows/brainstorming/template.md +15 -0
- package/src/core/workflows/brainstorming/workflow.md +58 -0
- package/src/core/workflows/party-mode/steps/step-01-agent-loading.md +138 -0
- package/src/core/workflows/party-mode/steps/step-02-discussion-orchestration.md +187 -0
- package/src/core/workflows/party-mode/steps/step-03-graceful-exit.md +157 -0
- package/src/core/workflows/party-mode/workflow.md +194 -0
- package/src/utility/agent-components/activation-rules.txt +6 -0
- package/src/utility/agent-components/activation-steps.txt +14 -0
- package/src/utility/agent-components/agent-command-header.md +1 -0
- package/src/utility/agent-components/agent.customize.template.yaml +41 -0
- package/src/utility/agent-components/handler-action.txt +4 -0
- package/src/utility/agent-components/handler-data.txt +5 -0
- package/src/utility/agent-components/handler-exec.txt +6 -0
- package/src/utility/agent-components/handler-multi.txt +14 -0
- package/src/utility/agent-components/handler-tmpl.txt +5 -0
- package/src/utility/agent-components/handler-validate-workflow.txt +7 -0
- package/src/utility/agent-components/handler-workflow.txt +10 -0
- package/src/utility/agent-components/menu-handlers.txt +6 -0
- package/test/README.md +295 -0
- package/test/adversarial-review-tests/README.md +56 -0
- package/test/adversarial-review-tests/sample-content.md +46 -0
- package/test/adversarial-review-tests/test-cases.yaml +103 -0
- package/test/fixtures/agent-schema/invalid/critical-actions/actions-as-string.agent.yaml +27 -0
- package/test/fixtures/agent-schema/invalid/critical-actions/empty-string-in-actions.agent.yaml +30 -0
- package/test/fixtures/agent-schema/invalid/menu/empty-menu.agent.yaml +22 -0
- package/test/fixtures/agent-schema/invalid/menu/missing-menu.agent.yaml +20 -0
- package/test/fixtures/agent-schema/invalid/menu-commands/empty-command-target.agent.yaml +25 -0
- package/test/fixtures/agent-schema/invalid/menu-commands/no-command-target.agent.yaml +24 -0
- package/test/fixtures/agent-schema/invalid/menu-triggers/camel-case.agent.yaml +25 -0
- package/test/fixtures/agent-schema/invalid/menu-triggers/compound-invalid-format.agent.yaml +25 -0
- package/test/fixtures/agent-schema/invalid/menu-triggers/compound-mismatched-kebab.agent.yaml +25 -0
- package/test/fixtures/agent-schema/invalid/menu-triggers/duplicate-triggers.agent.yaml +31 -0
- package/test/fixtures/agent-schema/invalid/menu-triggers/empty-trigger.agent.yaml +25 -0
- package/test/fixtures/agent-schema/invalid/menu-triggers/leading-asterisk.agent.yaml +25 -0
- package/test/fixtures/agent-schema/invalid/menu-triggers/snake-case.agent.yaml +25 -0
- package/test/fixtures/agent-schema/invalid/menu-triggers/trigger-with-spaces.agent.yaml +25 -0
- package/test/fixtures/agent-schema/invalid/metadata/empty-module-string.agent.yaml +26 -0
- package/test/fixtures/agent-schema/invalid/metadata/empty-name.agent.yaml +24 -0
- package/test/fixtures/agent-schema/invalid/metadata/extra-metadata-fields.agent.yaml +27 -0
- package/test/fixtures/agent-schema/invalid/metadata/missing-id.agent.yaml +23 -0
- package/test/fixtures/agent-schema/invalid/persona/empty-principles-array.agent.yaml +24 -0
- package/test/fixtures/agent-schema/invalid/persona/empty-string-in-principles.agent.yaml +27 -0
- package/test/fixtures/agent-schema/invalid/persona/extra-persona-fields.agent.yaml +27 -0
- package/test/fixtures/agent-schema/invalid/persona/missing-role.agent.yaml +24 -0
- package/test/fixtures/agent-schema/invalid/prompts/empty-content.agent.yaml +29 -0
- package/test/fixtures/agent-schema/invalid/prompts/extra-prompt-fields.agent.yaml +31 -0
- package/test/fixtures/agent-schema/invalid/prompts/missing-content.agent.yaml +28 -0
- package/test/fixtures/agent-schema/invalid/prompts/missing-id.agent.yaml +28 -0
- package/test/fixtures/agent-schema/invalid/top-level/empty-file.agent.yaml +5 -0
- package/test/fixtures/agent-schema/invalid/top-level/extra-top-level-keys.agent.yaml +28 -0
- package/test/fixtures/agent-schema/invalid/top-level/missing-agent-key.agent.yaml +11 -0
- package/test/fixtures/agent-schema/invalid/yaml-errors/invalid-indentation.agent.yaml +19 -0
- package/test/fixtures/agent-schema/invalid/yaml-errors/malformed-yaml.agent.yaml +18 -0
- package/test/fixtures/agent-schema/valid/critical-actions/empty-critical-actions.agent.yaml +24 -0
- package/test/fixtures/agent-schema/valid/critical-actions/no-critical-actions.agent.yaml +22 -0
- package/test/fixtures/agent-schema/valid/critical-actions/valid-critical-actions.agent.yaml +27 -0
- package/test/fixtures/agent-schema/valid/menu/multiple-menu-items.agent.yaml +31 -0
- package/test/fixtures/agent-schema/valid/menu/single-menu-item.agent.yaml +22 -0
- package/test/fixtures/agent-schema/valid/menu-commands/all-command-types.agent.yaml +38 -0
- package/test/fixtures/agent-schema/valid/menu-commands/multiple-commands.agent.yaml +24 -0
- package/test/fixtures/agent-schema/valid/menu-triggers/compound-triggers.agent.yaml +31 -0
- package/test/fixtures/agent-schema/valid/menu-triggers/kebab-case-triggers.agent.yaml +34 -0
- package/test/fixtures/agent-schema/valid/metadata/core-agent-with-module.agent.yaml +24 -0
- package/test/fixtures/agent-schema/valid/metadata/empty-module-name-in-path.agent.yaml +24 -0
- package/test/fixtures/agent-schema/valid/metadata/malformed-path-treated-as-core.agent.yaml +24 -0
- package/test/fixtures/agent-schema/valid/metadata/module-agent-correct.agent.yaml +24 -0
- package/test/fixtures/agent-schema/valid/metadata/module-agent-missing-module.agent.yaml +23 -0
- package/test/fixtures/agent-schema/valid/metadata/wrong-module-value.agent.yaml +24 -0
- package/test/fixtures/agent-schema/valid/persona/complete-persona.agent.yaml +24 -0
- package/test/fixtures/agent-schema/valid/prompts/empty-prompts.agent.yaml +24 -0
- package/test/fixtures/agent-schema/valid/prompts/no-prompts.agent.yaml +22 -0
- package/test/fixtures/agent-schema/valid/prompts/valid-prompts-minimal.agent.yaml +28 -0
- package/test/fixtures/agent-schema/valid/prompts/valid-prompts-with-description.agent.yaml +30 -0
- package/test/fixtures/agent-schema/valid/top-level/minimal-core-agent.agent.yaml +24 -0
- package/test/test-agent-schema.js +387 -0
- package/test/test-cli-integration.sh +159 -0
- package/test/test-installation-components.js +214 -0
- package/test/unit-test-schema.js +133 -0
- package/tools/bmad-npx-wrapper.js +38 -0
- package/tools/build-docs.js +577 -0
- package/tools/cli/README.md +7 -0
- package/tools/cli/bmad-cli.js +58 -0
- package/tools/cli/commands/install.js +87 -0
- package/tools/cli/commands/status.js +65 -0
- package/tools/cli/external-official-modules.yaml +56 -0
- package/tools/cli/installers/install-messages.yaml +58 -0
- package/tools/cli/installers/lib/core/config-collector.js +1079 -0
- package/tools/cli/installers/lib/core/custom-module-cache.js +259 -0
- package/tools/cli/installers/lib/core/dependency-resolver.js +739 -0
- package/tools/cli/installers/lib/core/detector.js +223 -0
- package/tools/cli/installers/lib/core/ide-config-manager.js +156 -0
- package/tools/cli/installers/lib/core/installer.js +2826 -0
- package/tools/cli/installers/lib/core/manifest-generator.js +1054 -0
- package/tools/cli/installers/lib/core/manifest.js +1036 -0
- package/tools/cli/installers/lib/custom/handler.js +363 -0
- package/tools/cli/installers/lib/ide/STANDARDIZATION_PLAN.md +208 -0
- package/tools/cli/installers/lib/ide/_base-ide.js +655 -0
- package/tools/cli/installers/lib/ide/antigravity.js +474 -0
- package/tools/cli/installers/lib/ide/auggie.js +244 -0
- package/tools/cli/installers/lib/ide/claude-code.js +506 -0
- package/tools/cli/installers/lib/ide/cline.js +272 -0
- package/tools/cli/installers/lib/ide/codex.js +412 -0
- package/tools/cli/installers/lib/ide/crush.js +149 -0
- package/tools/cli/installers/lib/ide/cursor.js +160 -0
- package/tools/cli/installers/lib/ide/gemini.js +301 -0
- package/tools/cli/installers/lib/ide/github-copilot.js +383 -0
- package/tools/cli/installers/lib/ide/iflow.js +191 -0
- package/tools/cli/installers/lib/ide/kilo.js +250 -0
- package/tools/cli/installers/lib/ide/kiro-cli.js +326 -0
- package/tools/cli/installers/lib/ide/manager.js +244 -0
- package/tools/cli/installers/lib/ide/opencode.js +257 -0
- package/tools/cli/installers/lib/ide/qwen.js +372 -0
- package/tools/cli/installers/lib/ide/roo.js +273 -0
- package/tools/cli/installers/lib/ide/rovo-dev.js +290 -0
- package/tools/cli/installers/lib/ide/shared/agent-command-generator.js +165 -0
- package/tools/cli/installers/lib/ide/shared/bmad-artifacts.js +158 -0
- package/tools/cli/installers/lib/ide/shared/module-injections.js +136 -0
- package/tools/cli/installers/lib/ide/shared/path-utils.js +165 -0
- package/tools/cli/installers/lib/ide/shared/task-tool-command-generator.js +268 -0
- package/tools/cli/installers/lib/ide/shared/workflow-command-generator.js +293 -0
- package/tools/cli/installers/lib/ide/templates/agent-command-template.md +14 -0
- package/tools/cli/installers/lib/ide/templates/gemini-agent-command.toml +14 -0
- package/tools/cli/installers/lib/ide/templates/gemini-task-command.toml +12 -0
- package/tools/cli/installers/lib/ide/templates/workflow-command-template.md +13 -0
- package/tools/cli/installers/lib/ide/templates/workflow-commander.md +5 -0
- package/tools/cli/installers/lib/ide/trae.js +313 -0
- package/tools/cli/installers/lib/ide/windsurf.js +258 -0
- package/tools/cli/installers/lib/message-loader.js +85 -0
- package/tools/cli/installers/lib/modules/external-manager.js +135 -0
- package/tools/cli/installers/lib/modules/manager.js +1375 -0
- package/tools/cli/lib/activation-builder.js +163 -0
- package/tools/cli/lib/agent/compiler.js +522 -0
- package/tools/cli/lib/agent/installer.js +716 -0
- package/tools/cli/lib/agent/template-engine.js +152 -0
- package/tools/cli/lib/agent-analyzer.js +109 -0
- package/tools/cli/lib/agent-party-generator.js +194 -0
- package/tools/cli/lib/cli-utils.js +227 -0
- package/tools/cli/lib/config.js +213 -0
- package/tools/cli/lib/file-ops.js +204 -0
- package/tools/cli/lib/platform-codes.js +116 -0
- package/tools/cli/lib/project-root.js +77 -0
- package/tools/cli/lib/prompts.js +433 -0
- package/tools/cli/lib/ui.js +1716 -0
- package/tools/cli/lib/xml-handler.js +177 -0
- package/tools/cli/lib/xml-to-markdown.js +82 -0
- package/tools/cli/lib/yaml-format.js +245 -0
- package/tools/cli/lib/yaml-xml-builder.js +587 -0
- package/tools/docs/BUNDLE_DISTRIBUTION_SETUP.md +95 -0
- package/tools/docs/index.md +2 -0
- package/tools/fix-doc-links.js +288 -0
- 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 +172 -0
- package/tools/flattener/main.js +483 -0
- package/tools/flattener/projectRoot.js +201 -0
- package/tools/flattener/prompts.js +44 -0
- package/tools/flattener/stats.helpers.js +368 -0
- package/tools/flattener/stats.js +75 -0
- package/tools/flattener/test-matrix.js +409 -0
- package/tools/flattener/xml.js +82 -0
- package/tools/format-workflow-md.js +263 -0
- package/tools/lib/xml-utils.js +13 -0
- package/tools/maintainer/review-pr-README.md +55 -0
- package/tools/maintainer/review-pr.md +242 -0
- package/tools/migrate-custom-module-paths.js +124 -0
- package/tools/platform-codes.yaml +157 -0
- package/tools/schema/agent.js +491 -0
- package/tools/validate-agent-schema.js +110 -0
- package/tools/validate-doc-links.js +363 -0
- package/tools/validate-svg-changes.sh +356 -0
- package/website/README.md +76 -0
- package/website/astro.config.mjs +228 -0
- package/website/public/favicon.ico +0 -0
- package/website/public/img/bmad-dark.png +0 -0
- package/website/public/img/bmad-light.png +0 -0
- package/website/public/img/logo.svg +4 -0
- package/website/public/robots.txt +37 -0
- package/website/src/components/Banner.astro +59 -0
- package/website/src/components/Header.astro +121 -0
- package/website/src/components/MobileMenuFooter.astro +53 -0
- package/website/src/content/config.ts +6 -0
- package/website/src/lib/site-url.js +25 -0
- package/website/src/rehype-markdown-links.js +102 -0
- package/website/src/styles/custom.css +485 -0
|
@@ -0,0 +1,1036 @@
|
|
|
1
|
+
const path = require('node:path');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const crypto = require('node:crypto');
|
|
4
|
+
const { getProjectRoot } = require('../../../lib/project-root');
|
|
5
|
+
|
|
6
|
+
class Manifest {
|
|
7
|
+
/**
|
|
8
|
+
* Create a new manifest
|
|
9
|
+
* @param {string} bmadDir - Path to bmad directory
|
|
10
|
+
* @param {Object} data - Manifest data
|
|
11
|
+
* @param {Array} installedFiles - List of installed files (no longer used, files tracked in files-manifest.csv)
|
|
12
|
+
*/
|
|
13
|
+
async create(bmadDir, data, installedFiles = []) {
|
|
14
|
+
const manifestPath = path.join(bmadDir, '_config', 'manifest.yaml');
|
|
15
|
+
const yaml = require('yaml');
|
|
16
|
+
|
|
17
|
+
// Ensure _config directory exists
|
|
18
|
+
await fs.ensureDir(path.dirname(manifestPath));
|
|
19
|
+
|
|
20
|
+
// Get the BMad version from package.json
|
|
21
|
+
const bmadVersion = data.version || require(path.join(process.cwd(), 'package.json')).version;
|
|
22
|
+
|
|
23
|
+
// Convert module list to new detailed format
|
|
24
|
+
const moduleDetails = [];
|
|
25
|
+
if (data.modules && Array.isArray(data.modules)) {
|
|
26
|
+
for (const moduleName of data.modules) {
|
|
27
|
+
// Core and BMM modules use the BMad version
|
|
28
|
+
const moduleVersion = moduleName === 'core' || moduleName === 'bmm' ? bmadVersion : null;
|
|
29
|
+
const now = data.installDate || new Date().toISOString();
|
|
30
|
+
|
|
31
|
+
moduleDetails.push({
|
|
32
|
+
name: moduleName,
|
|
33
|
+
version: moduleVersion,
|
|
34
|
+
installDate: now,
|
|
35
|
+
lastUpdated: now,
|
|
36
|
+
source: moduleName === 'core' || moduleName === 'bmm' ? 'built-in' : 'unknown',
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Structure the manifest data
|
|
42
|
+
const manifestData = {
|
|
43
|
+
installation: {
|
|
44
|
+
version: bmadVersion,
|
|
45
|
+
installDate: data.installDate || new Date().toISOString(),
|
|
46
|
+
lastUpdated: data.lastUpdated || new Date().toISOString(),
|
|
47
|
+
},
|
|
48
|
+
modules: moduleDetails,
|
|
49
|
+
ides: data.ides || [],
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Write YAML manifest
|
|
53
|
+
// Clean the manifest data to remove any non-serializable values
|
|
54
|
+
const cleanManifestData = structuredClone(manifestData);
|
|
55
|
+
|
|
56
|
+
const yamlContent = yaml.stringify(cleanManifestData, {
|
|
57
|
+
indent: 2,
|
|
58
|
+
lineWidth: 0,
|
|
59
|
+
sortKeys: false,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Ensure POSIX-compliant final newline
|
|
63
|
+
const content = yamlContent.endsWith('\n') ? yamlContent : yamlContent + '\n';
|
|
64
|
+
await fs.writeFile(manifestPath, content, 'utf8');
|
|
65
|
+
return { success: true, path: manifestPath, filesTracked: 0 };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Read existing manifest
|
|
70
|
+
* @param {string} bmadDir - Path to bmad directory
|
|
71
|
+
* @returns {Object|null} Manifest data or null if not found
|
|
72
|
+
*/
|
|
73
|
+
async read(bmadDir) {
|
|
74
|
+
const yamlPath = path.join(bmadDir, '_config', 'manifest.yaml');
|
|
75
|
+
const yaml = require('yaml');
|
|
76
|
+
|
|
77
|
+
if (await fs.pathExists(yamlPath)) {
|
|
78
|
+
try {
|
|
79
|
+
const content = await fs.readFile(yamlPath, 'utf8');
|
|
80
|
+
const manifestData = yaml.parse(content);
|
|
81
|
+
|
|
82
|
+
// Handle new detailed module format
|
|
83
|
+
const modules = manifestData.modules || [];
|
|
84
|
+
|
|
85
|
+
// For backward compatibility: if modules is an array of strings (old format),
|
|
86
|
+
// the calling code may need the array of names
|
|
87
|
+
const moduleNames = modules.map((m) => (typeof m === 'string' ? m : m.name));
|
|
88
|
+
|
|
89
|
+
// Check if we have the new detailed format
|
|
90
|
+
const hasDetailedModules = modules.length > 0 && typeof modules[0] === 'object';
|
|
91
|
+
|
|
92
|
+
// Flatten the structure for compatibility with existing code
|
|
93
|
+
return {
|
|
94
|
+
version: manifestData.installation?.version,
|
|
95
|
+
installDate: manifestData.installation?.installDate,
|
|
96
|
+
lastUpdated: manifestData.installation?.lastUpdated,
|
|
97
|
+
modules: moduleNames, // Simple array of module names for backward compatibility
|
|
98
|
+
modulesDetailed: hasDetailedModules ? modules : null, // New detailed format
|
|
99
|
+
customModules: manifestData.customModules || [], // Keep for backward compatibility
|
|
100
|
+
ides: manifestData.ides || [],
|
|
101
|
+
};
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.error('Failed to read YAML manifest:', error.message);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Update existing manifest
|
|
112
|
+
* @param {string} bmadDir - Path to bmad directory
|
|
113
|
+
* @param {Object} updates - Fields to update
|
|
114
|
+
* @param {Array} installedFiles - Updated list of installed files
|
|
115
|
+
*/
|
|
116
|
+
async update(bmadDir, updates, installedFiles = null) {
|
|
117
|
+
const yaml = require('yaml');
|
|
118
|
+
const manifest = (await this._readRaw(bmadDir)) || {
|
|
119
|
+
installation: {},
|
|
120
|
+
modules: [],
|
|
121
|
+
ides: [],
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// Handle module updates
|
|
125
|
+
if (updates.modules) {
|
|
126
|
+
// If modules is being updated, we need to preserve detailed module info
|
|
127
|
+
const existingDetailed = manifest.modules || [];
|
|
128
|
+
const incomingNames = updates.modules;
|
|
129
|
+
|
|
130
|
+
// Build updated modules array
|
|
131
|
+
const updatedModules = [];
|
|
132
|
+
for (const name of incomingNames) {
|
|
133
|
+
const existing = existingDetailed.find((m) => m.name === name);
|
|
134
|
+
if (existing) {
|
|
135
|
+
// Preserve existing details, update lastUpdated if this module is being updated
|
|
136
|
+
updatedModules.push({
|
|
137
|
+
...existing,
|
|
138
|
+
lastUpdated: new Date().toISOString(),
|
|
139
|
+
});
|
|
140
|
+
} else {
|
|
141
|
+
// New module - add with minimal details
|
|
142
|
+
updatedModules.push({
|
|
143
|
+
name,
|
|
144
|
+
version: null,
|
|
145
|
+
installDate: new Date().toISOString(),
|
|
146
|
+
lastUpdated: new Date().toISOString(),
|
|
147
|
+
source: 'unknown',
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
manifest.modules = updatedModules;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Merge other updates
|
|
156
|
+
if (updates.version) {
|
|
157
|
+
manifest.installation.version = updates.version;
|
|
158
|
+
}
|
|
159
|
+
if (updates.installDate) {
|
|
160
|
+
manifest.installation.installDate = updates.installDate;
|
|
161
|
+
}
|
|
162
|
+
manifest.installation.lastUpdated = new Date().toISOString();
|
|
163
|
+
|
|
164
|
+
if (updates.ides) {
|
|
165
|
+
manifest.ides = updates.ides;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Handle per-module version updates
|
|
169
|
+
if (updates.moduleVersions) {
|
|
170
|
+
for (const [moduleName, versionInfo] of Object.entries(updates.moduleVersions)) {
|
|
171
|
+
const moduleIndex = manifest.modules.findIndex((m) => m.name === moduleName);
|
|
172
|
+
if (moduleIndex !== -1) {
|
|
173
|
+
manifest.modules[moduleIndex] = {
|
|
174
|
+
...manifest.modules[moduleIndex],
|
|
175
|
+
...versionInfo,
|
|
176
|
+
lastUpdated: new Date().toISOString(),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Handle adding a new module with version info
|
|
183
|
+
if (updates.addModule) {
|
|
184
|
+
const { name, version, source, npmPackage, repoUrl } = updates.addModule;
|
|
185
|
+
const existing = manifest.modules.find((m) => m.name === name);
|
|
186
|
+
if (!existing) {
|
|
187
|
+
manifest.modules.push({
|
|
188
|
+
name,
|
|
189
|
+
version: version || null,
|
|
190
|
+
installDate: new Date().toISOString(),
|
|
191
|
+
lastUpdated: new Date().toISOString(),
|
|
192
|
+
source: source || 'external',
|
|
193
|
+
npmPackage: npmPackage || null,
|
|
194
|
+
repoUrl: repoUrl || null,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const manifestPath = path.join(bmadDir, '_config', 'manifest.yaml');
|
|
200
|
+
await fs.ensureDir(path.dirname(manifestPath));
|
|
201
|
+
|
|
202
|
+
// Clean the manifest data to remove any non-serializable values
|
|
203
|
+
const cleanManifestData = structuredClone(manifest);
|
|
204
|
+
|
|
205
|
+
const yamlContent = yaml.stringify(cleanManifestData, {
|
|
206
|
+
indent: 2,
|
|
207
|
+
lineWidth: 0,
|
|
208
|
+
sortKeys: false,
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// Ensure POSIX-compliant final newline
|
|
212
|
+
const content = yamlContent.endsWith('\n') ? yamlContent : yamlContent + '\n';
|
|
213
|
+
await fs.writeFile(manifestPath, content, 'utf8');
|
|
214
|
+
|
|
215
|
+
// Return the flattened format for compatibility
|
|
216
|
+
return this._flattenManifest(manifest);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Read raw manifest data without flattening
|
|
221
|
+
* @param {string} bmadDir - Path to bmad directory
|
|
222
|
+
* @returns {Object|null} Raw manifest data or null if not found
|
|
223
|
+
*/
|
|
224
|
+
async _readRaw(bmadDir) {
|
|
225
|
+
const yamlPath = path.join(bmadDir, '_config', 'manifest.yaml');
|
|
226
|
+
const yaml = require('yaml');
|
|
227
|
+
|
|
228
|
+
if (await fs.pathExists(yamlPath)) {
|
|
229
|
+
try {
|
|
230
|
+
const content = await fs.readFile(yamlPath, 'utf8');
|
|
231
|
+
return yaml.parse(content);
|
|
232
|
+
} catch (error) {
|
|
233
|
+
console.error('Failed to read YAML manifest:', error.message);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Flatten manifest for backward compatibility
|
|
242
|
+
* @param {Object} manifest - Raw manifest data
|
|
243
|
+
* @returns {Object} Flattened manifest
|
|
244
|
+
*/
|
|
245
|
+
_flattenManifest(manifest) {
|
|
246
|
+
const modules = manifest.modules || [];
|
|
247
|
+
const moduleNames = modules.map((m) => (typeof m === 'string' ? m : m.name));
|
|
248
|
+
const hasDetailedModules = modules.length > 0 && typeof modules[0] === 'object';
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
version: manifest.installation?.version,
|
|
252
|
+
installDate: manifest.installation?.installDate,
|
|
253
|
+
lastUpdated: manifest.installation?.lastUpdated,
|
|
254
|
+
modules: moduleNames,
|
|
255
|
+
modulesDetailed: hasDetailedModules ? modules : null,
|
|
256
|
+
customModules: manifest.customModules || [],
|
|
257
|
+
ides: manifest.ides || [],
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Add a module to the manifest with optional version info
|
|
263
|
+
* If module already exists, update its version info
|
|
264
|
+
* @param {string} bmadDir - Path to bmad directory
|
|
265
|
+
* @param {string} moduleName - Module name to add
|
|
266
|
+
* @param {Object} options - Optional version info
|
|
267
|
+
*/
|
|
268
|
+
async addModule(bmadDir, moduleName, options = {}) {
|
|
269
|
+
const manifest = await this._readRaw(bmadDir);
|
|
270
|
+
if (!manifest) {
|
|
271
|
+
throw new Error('No manifest found');
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (!manifest.modules) {
|
|
275
|
+
manifest.modules = [];
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const existingIndex = manifest.modules.findIndex((m) => m.name === moduleName);
|
|
279
|
+
|
|
280
|
+
if (existingIndex === -1) {
|
|
281
|
+
// Module doesn't exist, add it
|
|
282
|
+
manifest.modules.push({
|
|
283
|
+
name: moduleName,
|
|
284
|
+
version: options.version || null,
|
|
285
|
+
installDate: new Date().toISOString(),
|
|
286
|
+
lastUpdated: new Date().toISOString(),
|
|
287
|
+
source: options.source || 'unknown',
|
|
288
|
+
npmPackage: options.npmPackage || null,
|
|
289
|
+
repoUrl: options.repoUrl || null,
|
|
290
|
+
});
|
|
291
|
+
} else {
|
|
292
|
+
// Module exists, update its version info
|
|
293
|
+
const existing = manifest.modules[existingIndex];
|
|
294
|
+
manifest.modules[existingIndex] = {
|
|
295
|
+
...existing,
|
|
296
|
+
version: options.version === undefined ? existing.version : options.version,
|
|
297
|
+
source: options.source || existing.source,
|
|
298
|
+
npmPackage: options.npmPackage === undefined ? existing.npmPackage : options.npmPackage,
|
|
299
|
+
repoUrl: options.repoUrl === undefined ? existing.repoUrl : options.repoUrl,
|
|
300
|
+
lastUpdated: new Date().toISOString(),
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
await this._writeRaw(bmadDir, manifest);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Remove a module from the manifest
|
|
309
|
+
* @param {string} bmadDir - Path to bmad directory
|
|
310
|
+
* @param {string} moduleName - Module name to remove
|
|
311
|
+
*/
|
|
312
|
+
async removeModule(bmadDir, moduleName) {
|
|
313
|
+
const manifest = await this._readRaw(bmadDir);
|
|
314
|
+
if (!manifest || !manifest.modules) {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const index = manifest.modules.findIndex((m) => m.name === moduleName);
|
|
319
|
+
if (index !== -1) {
|
|
320
|
+
manifest.modules.splice(index, 1);
|
|
321
|
+
await this._writeRaw(bmadDir, manifest);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Update a single module's version info
|
|
327
|
+
* @param {string} bmadDir - Path to bmad directory
|
|
328
|
+
* @param {string} moduleName - Module name
|
|
329
|
+
* @param {Object} versionInfo - Version info to update
|
|
330
|
+
*/
|
|
331
|
+
async updateModuleVersion(bmadDir, moduleName, versionInfo) {
|
|
332
|
+
const manifest = await this._readRaw(bmadDir);
|
|
333
|
+
if (!manifest || !manifest.modules) {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const index = manifest.modules.findIndex((m) => m.name === moduleName);
|
|
338
|
+
if (index !== -1) {
|
|
339
|
+
manifest.modules[index] = {
|
|
340
|
+
...manifest.modules[index],
|
|
341
|
+
...versionInfo,
|
|
342
|
+
lastUpdated: new Date().toISOString(),
|
|
343
|
+
};
|
|
344
|
+
await this._writeRaw(bmadDir, manifest);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Get version info for a specific module
|
|
350
|
+
* @param {string} bmadDir - Path to bmad directory
|
|
351
|
+
* @param {string} moduleName - Module name
|
|
352
|
+
* @returns {Object|null} Module version info or null
|
|
353
|
+
*/
|
|
354
|
+
async getModuleVersion(bmadDir, moduleName) {
|
|
355
|
+
const manifest = await this._readRaw(bmadDir);
|
|
356
|
+
if (!manifest || !manifest.modules) {
|
|
357
|
+
return null;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return manifest.modules.find((m) => m.name === moduleName) || null;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Get all modules with their version info
|
|
365
|
+
* @param {string} bmadDir - Path to bmad directory
|
|
366
|
+
* @returns {Array} Array of module info objects
|
|
367
|
+
*/
|
|
368
|
+
async getAllModuleVersions(bmadDir) {
|
|
369
|
+
const manifest = await this._readRaw(bmadDir);
|
|
370
|
+
if (!manifest || !manifest.modules) {
|
|
371
|
+
return [];
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return manifest.modules;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Write raw manifest data to file
|
|
379
|
+
* @param {string} bmadDir - Path to bmad directory
|
|
380
|
+
* @param {Object} manifestData - Raw manifest data to write
|
|
381
|
+
*/
|
|
382
|
+
async _writeRaw(bmadDir, manifestData) {
|
|
383
|
+
const yaml = require('yaml');
|
|
384
|
+
const manifestPath = path.join(bmadDir, '_config', 'manifest.yaml');
|
|
385
|
+
|
|
386
|
+
await fs.ensureDir(path.dirname(manifestPath));
|
|
387
|
+
|
|
388
|
+
const cleanManifestData = structuredClone(manifestData);
|
|
389
|
+
|
|
390
|
+
const yamlContent = yaml.stringify(cleanManifestData, {
|
|
391
|
+
indent: 2,
|
|
392
|
+
lineWidth: 0,
|
|
393
|
+
sortKeys: false,
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
const content = yamlContent.endsWith('\n') ? yamlContent : yamlContent + '\n';
|
|
397
|
+
await fs.writeFile(manifestPath, content, 'utf8');
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Add an IDE configuration to the manifest
|
|
402
|
+
* @param {string} bmadDir - Path to bmad directory
|
|
403
|
+
* @param {string} ideName - IDE name to add
|
|
404
|
+
*/
|
|
405
|
+
async addIde(bmadDir, ideName) {
|
|
406
|
+
const manifest = await this.read(bmadDir);
|
|
407
|
+
if (!manifest) {
|
|
408
|
+
throw new Error('No manifest found');
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (!manifest.ides) {
|
|
412
|
+
manifest.ides = [];
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (!manifest.ides.includes(ideName)) {
|
|
416
|
+
manifest.ides.push(ideName);
|
|
417
|
+
await this.update(bmadDir, { ides: manifest.ides });
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Calculate SHA256 hash of a file
|
|
423
|
+
* @param {string} filePath - Path to file
|
|
424
|
+
* @returns {string} SHA256 hash
|
|
425
|
+
*/
|
|
426
|
+
async calculateFileHash(filePath) {
|
|
427
|
+
try {
|
|
428
|
+
const content = await fs.readFile(filePath);
|
|
429
|
+
return crypto.createHash('sha256').update(content).digest('hex');
|
|
430
|
+
} catch {
|
|
431
|
+
return null;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Parse installed files to extract metadata
|
|
437
|
+
* @param {Array} installedFiles - List of installed file paths
|
|
438
|
+
* @param {string} bmadDir - Path to bmad directory for relative paths
|
|
439
|
+
* @returns {Array} Array of file metadata objects
|
|
440
|
+
*/
|
|
441
|
+
async parseInstalledFiles(installedFiles, bmadDir) {
|
|
442
|
+
const fileMetadata = [];
|
|
443
|
+
|
|
444
|
+
for (const filePath of installedFiles) {
|
|
445
|
+
const fileExt = path.extname(filePath).toLowerCase();
|
|
446
|
+
// Make path relative to parent of bmad directory, starting with 'bmad/'
|
|
447
|
+
const relativePath = 'bmad' + filePath.replace(bmadDir, '').replaceAll('\\', '/');
|
|
448
|
+
|
|
449
|
+
// Calculate file hash
|
|
450
|
+
const hash = await this.calculateFileHash(filePath);
|
|
451
|
+
|
|
452
|
+
// Handle markdown files - extract XML metadata if present
|
|
453
|
+
if (fileExt === '.md') {
|
|
454
|
+
try {
|
|
455
|
+
if (await fs.pathExists(filePath)) {
|
|
456
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
457
|
+
const metadata = this.extractXmlNodeAttributes(content, filePath, relativePath);
|
|
458
|
+
|
|
459
|
+
if (metadata) {
|
|
460
|
+
// Has XML metadata
|
|
461
|
+
metadata.hash = hash;
|
|
462
|
+
fileMetadata.push(metadata);
|
|
463
|
+
} else {
|
|
464
|
+
// No XML metadata - still track the file
|
|
465
|
+
fileMetadata.push({
|
|
466
|
+
file: relativePath,
|
|
467
|
+
type: 'md',
|
|
468
|
+
name: path.basename(filePath, fileExt),
|
|
469
|
+
title: null,
|
|
470
|
+
hash: hash,
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
} catch (error) {
|
|
475
|
+
console.warn(`Warning: Could not parse ${filePath}:`, error.message);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
// Handle other file types (CSV, JSON, YAML, etc.)
|
|
479
|
+
else {
|
|
480
|
+
fileMetadata.push({
|
|
481
|
+
file: relativePath,
|
|
482
|
+
type: fileExt.slice(1), // Remove the dot
|
|
483
|
+
name: path.basename(filePath, fileExt),
|
|
484
|
+
title: null,
|
|
485
|
+
hash: hash,
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
return fileMetadata;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Extract XML node attributes from MD file content
|
|
495
|
+
* @param {string} content - File content
|
|
496
|
+
* @param {string} filePath - File path for context
|
|
497
|
+
* @param {string} relativePath - Relative path starting with 'bmad/'
|
|
498
|
+
* @returns {Object|null} Extracted metadata or null
|
|
499
|
+
*/
|
|
500
|
+
extractXmlNodeAttributes(content, filePath, relativePath) {
|
|
501
|
+
// Look for XML blocks in code fences
|
|
502
|
+
const xmlBlockMatch = content.match(/```xml\s*([\s\S]*?)```/);
|
|
503
|
+
if (!xmlBlockMatch) {
|
|
504
|
+
return null;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
const xmlContent = xmlBlockMatch[1];
|
|
508
|
+
|
|
509
|
+
// Extract root XML node (agent, task, template, etc.)
|
|
510
|
+
const rootNodeMatch = xmlContent.match(/<(\w+)([^>]*)>/);
|
|
511
|
+
if (!rootNodeMatch) {
|
|
512
|
+
return null;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
const nodeType = rootNodeMatch[1];
|
|
516
|
+
const attributes = rootNodeMatch[2];
|
|
517
|
+
|
|
518
|
+
// Extract name and title attributes (id not needed since we have path)
|
|
519
|
+
const nameMatch = attributes.match(/name="([^"]*)"/);
|
|
520
|
+
const titleMatch = attributes.match(/title="([^"]*)"/);
|
|
521
|
+
|
|
522
|
+
return {
|
|
523
|
+
file: relativePath,
|
|
524
|
+
type: nodeType,
|
|
525
|
+
name: nameMatch ? nameMatch[1] : null,
|
|
526
|
+
title: titleMatch ? titleMatch[1] : null,
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Generate CSV manifest content
|
|
532
|
+
* @param {Object} data - Manifest data
|
|
533
|
+
* @param {Array} fileMetadata - File metadata array
|
|
534
|
+
* @param {Object} moduleConfigs - Module configuration data
|
|
535
|
+
* @returns {string} CSV content
|
|
536
|
+
*/
|
|
537
|
+
generateManifestCsv(data, fileMetadata, moduleConfigs = {}) {
|
|
538
|
+
const timestamp = new Date().toISOString();
|
|
539
|
+
let csv = [];
|
|
540
|
+
|
|
541
|
+
// Header section
|
|
542
|
+
csv.push(
|
|
543
|
+
'# BMAD Manifest',
|
|
544
|
+
`# Generated: ${timestamp}`,
|
|
545
|
+
'',
|
|
546
|
+
'## Installation Info',
|
|
547
|
+
'Property,Value',
|
|
548
|
+
`Version,${data.version}`,
|
|
549
|
+
`InstallDate,${data.installDate || timestamp}`,
|
|
550
|
+
`LastUpdated,${data.lastUpdated || timestamp}`,
|
|
551
|
+
);
|
|
552
|
+
if (data.language) {
|
|
553
|
+
csv.push(`Language,${data.language}`);
|
|
554
|
+
}
|
|
555
|
+
csv.push('');
|
|
556
|
+
|
|
557
|
+
// Modules section
|
|
558
|
+
if (data.modules && data.modules.length > 0) {
|
|
559
|
+
csv.push('## Modules', 'Name,Version,ShortTitle');
|
|
560
|
+
for (const moduleName of data.modules) {
|
|
561
|
+
const config = moduleConfigs[moduleName] || {};
|
|
562
|
+
csv.push([moduleName, config.version || '', config['short-title'] || ''].map((v) => this.escapeCsv(v)).join(','));
|
|
563
|
+
}
|
|
564
|
+
csv.push('');
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// IDEs section
|
|
568
|
+
if (data.ides && data.ides.length > 0) {
|
|
569
|
+
csv.push('## IDEs', 'IDE');
|
|
570
|
+
for (const ide of data.ides) {
|
|
571
|
+
csv.push(this.escapeCsv(ide));
|
|
572
|
+
}
|
|
573
|
+
csv.push('');
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// Files section - NO LONGER USED
|
|
577
|
+
// Files are now tracked in files-manifest.csv by ManifestGenerator
|
|
578
|
+
|
|
579
|
+
return csv.join('\n');
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* Parse CSV manifest content back to object
|
|
584
|
+
* @param {string} csvContent - CSV content to parse
|
|
585
|
+
* @returns {Object} Parsed manifest data
|
|
586
|
+
*/
|
|
587
|
+
parseManifestCsv(csvContent) {
|
|
588
|
+
const result = {
|
|
589
|
+
modules: [],
|
|
590
|
+
ides: [],
|
|
591
|
+
files: [],
|
|
592
|
+
};
|
|
593
|
+
|
|
594
|
+
const lines = csvContent.split('\n');
|
|
595
|
+
let section = '';
|
|
596
|
+
|
|
597
|
+
for (const line_ of lines) {
|
|
598
|
+
const line = line_.trim();
|
|
599
|
+
|
|
600
|
+
// Skip empty lines and comments
|
|
601
|
+
if (!line || line.startsWith('#')) {
|
|
602
|
+
// Check for section headers
|
|
603
|
+
if (line.startsWith('## ')) {
|
|
604
|
+
section = line.slice(3).toLowerCase();
|
|
605
|
+
}
|
|
606
|
+
continue;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// Parse based on current section
|
|
610
|
+
switch (section) {
|
|
611
|
+
case 'installation info': {
|
|
612
|
+
// Skip header row
|
|
613
|
+
if (line === 'Property,Value') continue;
|
|
614
|
+
|
|
615
|
+
const [property, ...valueParts] = line.split(',');
|
|
616
|
+
const value = this.unescapeCsv(valueParts.join(','));
|
|
617
|
+
|
|
618
|
+
switch (property) {
|
|
619
|
+
// Path no longer stored in manifest
|
|
620
|
+
case 'Version': {
|
|
621
|
+
result.version = value;
|
|
622
|
+
break;
|
|
623
|
+
}
|
|
624
|
+
case 'InstallDate': {
|
|
625
|
+
result.installDate = value;
|
|
626
|
+
break;
|
|
627
|
+
}
|
|
628
|
+
case 'LastUpdated': {
|
|
629
|
+
result.lastUpdated = value;
|
|
630
|
+
break;
|
|
631
|
+
}
|
|
632
|
+
case 'Language': {
|
|
633
|
+
result.language = value;
|
|
634
|
+
break;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
break;
|
|
639
|
+
}
|
|
640
|
+
case 'modules': {
|
|
641
|
+
// Skip header row
|
|
642
|
+
if (line === 'Name,Version,ShortTitle') continue;
|
|
643
|
+
|
|
644
|
+
const parts = this.parseCsvLine(line);
|
|
645
|
+
if (parts[0]) {
|
|
646
|
+
result.modules.push(parts[0]);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
break;
|
|
650
|
+
}
|
|
651
|
+
case 'ides': {
|
|
652
|
+
// Skip header row
|
|
653
|
+
if (line === 'IDE') continue;
|
|
654
|
+
|
|
655
|
+
result.ides.push(this.unescapeCsv(line));
|
|
656
|
+
|
|
657
|
+
break;
|
|
658
|
+
}
|
|
659
|
+
case 'files': {
|
|
660
|
+
// Skip header rows (support both old and new format)
|
|
661
|
+
if (line === 'Type,Path,Name,Title' || line === 'Type,Path,Name,Title,Hash') continue;
|
|
662
|
+
|
|
663
|
+
const parts = this.parseCsvLine(line);
|
|
664
|
+
if (parts.length >= 2) {
|
|
665
|
+
result.files.push({
|
|
666
|
+
type: parts[0] || '',
|
|
667
|
+
file: parts[1] || '',
|
|
668
|
+
name: parts[2] || null,
|
|
669
|
+
title: parts[3] || null,
|
|
670
|
+
hash: parts[4] || null, // Hash column (may not exist in old manifests)
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
break;
|
|
675
|
+
}
|
|
676
|
+
// No default
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
return result;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Parse a CSV line handling quotes and commas
|
|
685
|
+
* @param {string} line - CSV line to parse
|
|
686
|
+
* @returns {Array} Array of values
|
|
687
|
+
*/
|
|
688
|
+
parseCsvLine(line) {
|
|
689
|
+
const result = [];
|
|
690
|
+
let current = '';
|
|
691
|
+
let inQuotes = false;
|
|
692
|
+
|
|
693
|
+
for (let i = 0; i < line.length; i++) {
|
|
694
|
+
const char = line[i];
|
|
695
|
+
|
|
696
|
+
if (char === '"') {
|
|
697
|
+
if (inQuotes && line[i + 1] === '"') {
|
|
698
|
+
// Escaped quote
|
|
699
|
+
current += '"';
|
|
700
|
+
i++;
|
|
701
|
+
} else {
|
|
702
|
+
// Toggle quote state
|
|
703
|
+
inQuotes = !inQuotes;
|
|
704
|
+
}
|
|
705
|
+
} else if (char === ',' && !inQuotes) {
|
|
706
|
+
// Field separator
|
|
707
|
+
result.push(this.unescapeCsv(current));
|
|
708
|
+
current = '';
|
|
709
|
+
} else {
|
|
710
|
+
current += char;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// Add the last field
|
|
715
|
+
result.push(this.unescapeCsv(current));
|
|
716
|
+
|
|
717
|
+
return result;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* Escape CSV special characters
|
|
722
|
+
* @param {string} text - Text to escape
|
|
723
|
+
* @returns {string} Escaped text
|
|
724
|
+
*/
|
|
725
|
+
escapeCsv(text) {
|
|
726
|
+
if (!text) return '';
|
|
727
|
+
const str = String(text);
|
|
728
|
+
|
|
729
|
+
// If contains comma, newline, or quote, wrap in quotes and escape quotes
|
|
730
|
+
if (str.includes(',') || str.includes('\n') || str.includes('"')) {
|
|
731
|
+
return '"' + str.replaceAll('"', '""') + '"';
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
return str;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Unescape CSV field
|
|
739
|
+
* @param {string} text - Text to unescape
|
|
740
|
+
* @returns {string} Unescaped text
|
|
741
|
+
*/
|
|
742
|
+
unescapeCsv(text) {
|
|
743
|
+
if (!text) return '';
|
|
744
|
+
|
|
745
|
+
// Remove surrounding quotes if present
|
|
746
|
+
if (text.startsWith('"') && text.endsWith('"')) {
|
|
747
|
+
text = text.slice(1, -1);
|
|
748
|
+
// Unescape doubled quotes
|
|
749
|
+
text = text.replaceAll('""', '"');
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
return text;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Load module configuration files
|
|
757
|
+
* @param {Array} modules - List of module names
|
|
758
|
+
* @returns {Object} Module configurations indexed by name
|
|
759
|
+
*/
|
|
760
|
+
async loadModuleConfigs(modules) {
|
|
761
|
+
const configs = {};
|
|
762
|
+
|
|
763
|
+
for (const moduleName of modules) {
|
|
764
|
+
// Handle core module differently - it's in src/core not src/modules/core
|
|
765
|
+
const configPath =
|
|
766
|
+
moduleName === 'core'
|
|
767
|
+
? path.join(process.cwd(), 'src', 'core', 'config.yaml')
|
|
768
|
+
: path.join(process.cwd(), 'src', 'modules', moduleName, 'config.yaml');
|
|
769
|
+
|
|
770
|
+
try {
|
|
771
|
+
if (await fs.pathExists(configPath)) {
|
|
772
|
+
const yaml = require('yaml');
|
|
773
|
+
const content = await fs.readFile(configPath, 'utf8');
|
|
774
|
+
configs[moduleName] = yaml.parse(content);
|
|
775
|
+
}
|
|
776
|
+
} catch (error) {
|
|
777
|
+
console.warn(`Could not load config for module ${moduleName}:`, error.message);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
return configs;
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Add a custom module to the manifest with its source path
|
|
785
|
+
* @param {string} bmadDir - Path to bmad directory
|
|
786
|
+
* @param {Object} customModule - Custom module info
|
|
787
|
+
*/
|
|
788
|
+
async addCustomModule(bmadDir, customModule) {
|
|
789
|
+
const manifest = await this.read(bmadDir);
|
|
790
|
+
if (!manifest) {
|
|
791
|
+
throw new Error('No manifest found');
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
if (!manifest.customModules) {
|
|
795
|
+
manifest.customModules = [];
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// Check if custom module already exists
|
|
799
|
+
const existingIndex = manifest.customModules.findIndex((m) => m.id === customModule.id);
|
|
800
|
+
if (existingIndex === -1) {
|
|
801
|
+
// Add new entry
|
|
802
|
+
manifest.customModules.push(customModule);
|
|
803
|
+
} else {
|
|
804
|
+
// Update existing entry
|
|
805
|
+
manifest.customModules[existingIndex] = customModule;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
await this.update(bmadDir, { customModules: manifest.customModules });
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* Remove a custom module from the manifest
|
|
813
|
+
* @param {string} bmadDir - Path to bmad directory
|
|
814
|
+
* @param {string} moduleId - Module ID to remove
|
|
815
|
+
*/
|
|
816
|
+
async removeCustomModule(bmadDir, moduleId) {
|
|
817
|
+
const manifest = await this.read(bmadDir);
|
|
818
|
+
if (!manifest || !manifest.customModules) {
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
const index = manifest.customModules.findIndex((m) => m.id === moduleId);
|
|
823
|
+
if (index !== -1) {
|
|
824
|
+
manifest.customModules.splice(index, 1);
|
|
825
|
+
await this.update(bmadDir, { customModules: manifest.customModules });
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
/**
|
|
830
|
+
* Get module version info from source
|
|
831
|
+
* @param {string} moduleName - Module name/code
|
|
832
|
+
* @param {string} bmadDir - Path to bmad directory
|
|
833
|
+
* @param {string} moduleSourcePath - Optional source path for custom modules
|
|
834
|
+
* @returns {Object} Version info object with version, source, npmPackage, repoUrl
|
|
835
|
+
*/
|
|
836
|
+
async getModuleVersionInfo(moduleName, bmadDir, moduleSourcePath = null) {
|
|
837
|
+
const os = require('node:os');
|
|
838
|
+
|
|
839
|
+
// Built-in modules use BMad version (only core and bmm are in BMAD-METHOD repo)
|
|
840
|
+
if (['core', 'bmm'].includes(moduleName)) {
|
|
841
|
+
const bmadVersion = require(path.join(getProjectRoot(), 'package.json')).version;
|
|
842
|
+
return {
|
|
843
|
+
version: bmadVersion,
|
|
844
|
+
source: 'built-in',
|
|
845
|
+
npmPackage: null,
|
|
846
|
+
repoUrl: null,
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// Check if this is an external official module
|
|
851
|
+
const { ExternalModuleManager } = require('../modules/external-manager');
|
|
852
|
+
const extMgr = new ExternalModuleManager();
|
|
853
|
+
const moduleInfo = await extMgr.getModuleByCode(moduleName);
|
|
854
|
+
|
|
855
|
+
if (moduleInfo) {
|
|
856
|
+
// External module - try to get version from npm registry first, then fall back to cache
|
|
857
|
+
let version = null;
|
|
858
|
+
|
|
859
|
+
if (moduleInfo.npmPackage) {
|
|
860
|
+
// Fetch version from npm registry
|
|
861
|
+
try {
|
|
862
|
+
version = await this.fetchNpmVersion(moduleInfo.npmPackage);
|
|
863
|
+
} catch {
|
|
864
|
+
// npm fetch failed, try cache as fallback
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
// If npm didn't work, try reading from cached repo's package.json
|
|
869
|
+
if (!version) {
|
|
870
|
+
const cacheDir = path.join(os.homedir(), '.bmad', 'cache', 'external-modules', moduleName);
|
|
871
|
+
const packageJsonPath = path.join(cacheDir, 'package.json');
|
|
872
|
+
|
|
873
|
+
if (await fs.pathExists(packageJsonPath)) {
|
|
874
|
+
try {
|
|
875
|
+
const pkg = require(packageJsonPath);
|
|
876
|
+
version = pkg.version;
|
|
877
|
+
} catch (error) {
|
|
878
|
+
console.warn(`Failed to read package.json for ${moduleName}: ${error.message}`);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
return {
|
|
884
|
+
version: version,
|
|
885
|
+
source: 'external',
|
|
886
|
+
npmPackage: moduleInfo.npmPackage || null,
|
|
887
|
+
repoUrl: moduleInfo.url || null,
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
// Custom module - check cache directory
|
|
892
|
+
const cacheDir = path.join(bmadDir, '_config', 'custom', moduleName);
|
|
893
|
+
const moduleYamlPath = path.join(cacheDir, 'module.yaml');
|
|
894
|
+
|
|
895
|
+
if (await fs.pathExists(moduleYamlPath)) {
|
|
896
|
+
try {
|
|
897
|
+
const yamlContent = await fs.readFile(moduleYamlPath, 'utf8');
|
|
898
|
+
const moduleConfig = yaml.parse(yamlContent);
|
|
899
|
+
return {
|
|
900
|
+
version: moduleConfig.version || null,
|
|
901
|
+
source: 'custom',
|
|
902
|
+
npmPackage: moduleConfig.npmPackage || null,
|
|
903
|
+
repoUrl: moduleConfig.repoUrl || null,
|
|
904
|
+
};
|
|
905
|
+
} catch (error) {
|
|
906
|
+
console.warn(`Failed to read module.yaml for ${moduleName}: ${error.message}`);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
// Unknown module
|
|
911
|
+
return {
|
|
912
|
+
version: null,
|
|
913
|
+
source: 'unknown',
|
|
914
|
+
npmPackage: null,
|
|
915
|
+
repoUrl: null,
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
/**
|
|
920
|
+
* Fetch latest version from npm for a package
|
|
921
|
+
* @param {string} packageName - npm package name
|
|
922
|
+
* @returns {string|null} Latest version or null
|
|
923
|
+
*/
|
|
924
|
+
async fetchNpmVersion(packageName) {
|
|
925
|
+
try {
|
|
926
|
+
const https = require('node:https');
|
|
927
|
+
const { execSync } = require('node:child_process');
|
|
928
|
+
|
|
929
|
+
// Try using npm view first (more reliable)
|
|
930
|
+
try {
|
|
931
|
+
const result = execSync(`npm view ${packageName} version`, {
|
|
932
|
+
encoding: 'utf8',
|
|
933
|
+
stdio: 'pipe',
|
|
934
|
+
timeout: 10_000,
|
|
935
|
+
});
|
|
936
|
+
return result.trim();
|
|
937
|
+
} catch {
|
|
938
|
+
// Fallback to npm registry API
|
|
939
|
+
return new Promise((resolve, reject) => {
|
|
940
|
+
https
|
|
941
|
+
.get(`https://registry.npmjs.org/${packageName}`, (res) => {
|
|
942
|
+
let data = '';
|
|
943
|
+
res.on('data', (chunk) => (data += chunk));
|
|
944
|
+
res.on('end', () => {
|
|
945
|
+
try {
|
|
946
|
+
const pkg = JSON.parse(data);
|
|
947
|
+
resolve(pkg['dist-tags']?.latest || pkg.version || null);
|
|
948
|
+
} catch {
|
|
949
|
+
resolve(null);
|
|
950
|
+
}
|
|
951
|
+
});
|
|
952
|
+
})
|
|
953
|
+
.on('error', () => resolve(null));
|
|
954
|
+
});
|
|
955
|
+
}
|
|
956
|
+
} catch {
|
|
957
|
+
return null;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
/**
|
|
962
|
+
* Check for available updates for installed modules
|
|
963
|
+
* @param {string} bmadDir - Path to bmad directory
|
|
964
|
+
* @returns {Array} Array of update info objects
|
|
965
|
+
*/
|
|
966
|
+
async checkForUpdates(bmadDir) {
|
|
967
|
+
const modules = await this.getAllModuleVersions(bmadDir);
|
|
968
|
+
const updates = [];
|
|
969
|
+
|
|
970
|
+
for (const module of modules) {
|
|
971
|
+
if (!module.npmPackage) {
|
|
972
|
+
continue; // Skip modules without npm package (built-in)
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
const latestVersion = await this.fetchNpmVersion(module.npmPackage);
|
|
976
|
+
if (!latestVersion) {
|
|
977
|
+
continue;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
if (module.version !== latestVersion) {
|
|
981
|
+
updates.push({
|
|
982
|
+
name: module.name,
|
|
983
|
+
installedVersion: module.version,
|
|
984
|
+
latestVersion: latestVersion,
|
|
985
|
+
npmPackage: module.npmPackage,
|
|
986
|
+
updateAvailable: true,
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
return updates;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
/**
|
|
995
|
+
* Compare two semantic versions
|
|
996
|
+
* @param {string} v1 - First version
|
|
997
|
+
* @param {string} v2 - Second version
|
|
998
|
+
* @returns {number} -1 if v1 < v2, 0 if v1 == v2, 1 if v1 > v2
|
|
999
|
+
*/
|
|
1000
|
+
compareVersions(v1, v2) {
|
|
1001
|
+
if (!v1 || !v2) return 0;
|
|
1002
|
+
|
|
1003
|
+
const normalize = (v) => {
|
|
1004
|
+
// Remove leading 'v' if present
|
|
1005
|
+
v = v.replace(/^v/, '');
|
|
1006
|
+
// Handle prerelease tags
|
|
1007
|
+
const parts = v.split('-');
|
|
1008
|
+
const main = parts[0].split('.');
|
|
1009
|
+
const prerelease = parts[1];
|
|
1010
|
+
return { main, prerelease };
|
|
1011
|
+
};
|
|
1012
|
+
|
|
1013
|
+
const n1 = normalize(v1);
|
|
1014
|
+
const n2 = normalize(v2);
|
|
1015
|
+
|
|
1016
|
+
// Compare main version parts
|
|
1017
|
+
for (let i = 0; i < 3; i++) {
|
|
1018
|
+
const num1 = parseInt(n1.main[i] || '0', 10);
|
|
1019
|
+
const num2 = parseInt(n2.main[i] || '0', 10);
|
|
1020
|
+
if (num1 !== num2) {
|
|
1021
|
+
return num1 < num2 ? -1 : 1;
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
// If main versions are equal, compare prerelease
|
|
1026
|
+
if (n1.prerelease && n2.prerelease) {
|
|
1027
|
+
return n1.prerelease < n2.prerelease ? -1 : n1.prerelease > n2.prerelease ? 1 : 0;
|
|
1028
|
+
}
|
|
1029
|
+
if (n1.prerelease) return -1; // Prerelease is older than stable
|
|
1030
|
+
if (n2.prerelease) return 1; // Stable is newer than prerelease
|
|
1031
|
+
|
|
1032
|
+
return 0;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
module.exports = { Manifest };
|