ma-agents 3.4.7 → 3.4.9
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/.opencode/skills/.ma-agents.json +134 -134
- package/.opencode/skills/add-sprint/SKILL.md +207 -0
- package/.opencode/skills/add-sprint/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/add-to-sprint/SKILL.md +189 -0
- package/.opencode/skills/add-to-sprint/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/bmad-advanced-elicitation/SKILL.md +137 -0
- package/.opencode/skills/bmad-advanced-elicitation/methods.csv +51 -0
- package/.opencode/skills/bmad-agent-analyst/SKILL.md +56 -0
- package/.opencode/skills/bmad-agent-analyst/bmad-skill-manifest.yaml +11 -0
- package/.opencode/skills/bmad-agent-architect/SKILL.md +52 -0
- package/.opencode/skills/bmad-agent-architect/bmad-skill-manifest.yaml +11 -0
- package/.opencode/skills/bmad-agent-dev/SKILL.md +62 -0
- package/.opencode/skills/bmad-agent-dev/bmad-skill-manifest.yaml +11 -0
- package/.opencode/skills/bmad-agent-pm/SKILL.md +57 -0
- package/.opencode/skills/bmad-agent-pm/bmad-skill-manifest.yaml +11 -0
- package/.opencode/skills/bmad-agent-qa/SKILL.md +59 -0
- package/.opencode/skills/bmad-agent-qa/bmad-skill-manifest.yaml +11 -0
- package/.opencode/skills/bmad-agent-quick-flow-solo-dev/SKILL.md +51 -0
- package/.opencode/skills/bmad-agent-quick-flow-solo-dev/bmad-skill-manifest.yaml +11 -0
- package/.opencode/skills/bmad-agent-sm/SKILL.md +53 -0
- package/.opencode/skills/bmad-agent-sm/bmad-skill-manifest.yaml +11 -0
- package/.opencode/skills/bmad-agent-tech-writer/SKILL.md +55 -0
- package/.opencode/skills/bmad-agent-tech-writer/bmad-skill-manifest.yaml +11 -0
- package/.opencode/skills/bmad-agent-tech-writer/explain-concept.md +20 -0
- package/.opencode/skills/bmad-agent-tech-writer/mermaid-gen.md +20 -0
- package/.opencode/skills/bmad-agent-tech-writer/validate-doc.md +19 -0
- package/.opencode/skills/bmad-agent-tech-writer/write-document.md +20 -0
- package/.opencode/skills/bmad-agent-ux-designer/SKILL.md +53 -0
- package/.opencode/skills/bmad-agent-ux-designer/bmad-skill-manifest.yaml +11 -0
- package/.opencode/skills/bmad-brainstorming/SKILL.md +6 -0
- package/.opencode/skills/bmad-brainstorming/brain-methods.csv +62 -0
- package/.opencode/skills/bmad-brainstorming/steps/step-01-session-setup.md +214 -0
- package/.opencode/skills/bmad-brainstorming/steps/step-01b-continue.md +124 -0
- package/.opencode/skills/bmad-brainstorming/steps/step-02a-user-selected.md +229 -0
- package/.opencode/skills/bmad-brainstorming/steps/step-02b-ai-recommended.md +239 -0
- package/.opencode/skills/bmad-brainstorming/steps/step-02c-random-selection.md +211 -0
- package/.opencode/skills/bmad-brainstorming/steps/step-02d-progressive-flow.md +266 -0
- package/.opencode/skills/bmad-brainstorming/steps/step-03-technique-execution.md +401 -0
- package/.opencode/skills/bmad-brainstorming/steps/step-04-idea-organization.md +305 -0
- package/.opencode/skills/bmad-brainstorming/template.md +15 -0
- package/.opencode/skills/bmad-brainstorming/workflow.md +53 -0
- package/.opencode/skills/bmad-check-implementation-readiness/SKILL.md +6 -0
- package/.opencode/skills/bmad-check-implementation-readiness/steps/step-01-document-discovery.md +179 -0
- package/.opencode/skills/bmad-check-implementation-readiness/steps/step-02-prd-analysis.md +168 -0
- package/.opencode/skills/bmad-check-implementation-readiness/steps/step-03-epic-coverage-validation.md +169 -0
- package/.opencode/skills/bmad-check-implementation-readiness/steps/step-04-ux-alignment.md +129 -0
- package/.opencode/skills/bmad-check-implementation-readiness/steps/step-05-epic-quality-review.md +241 -0
- package/.opencode/skills/bmad-check-implementation-readiness/steps/step-06-final-assessment.md +126 -0
- package/.opencode/skills/bmad-check-implementation-readiness/templates/readiness-report-template.md +4 -0
- package/.opencode/skills/bmad-check-implementation-readiness/workflow.md +49 -0
- package/.opencode/skills/bmad-cis-design-thinking/SKILL.md +6 -0
- package/.opencode/skills/bmad-cis-design-thinking/bmad-skill-manifest.yaml +1 -0
- package/.opencode/skills/bmad-cis-design-thinking/design-methods.csv +31 -0
- package/.opencode/skills/bmad-cis-design-thinking/template.md +111 -0
- package/.opencode/skills/bmad-cis-design-thinking/workflow.md +242 -0
- package/.opencode/skills/bmad-cis-innovation-strategy/SKILL.md +6 -0
- package/.opencode/skills/bmad-cis-innovation-strategy/bmad-skill-manifest.yaml +1 -0
- package/.opencode/skills/bmad-cis-innovation-strategy/innovation-frameworks.csv +31 -0
- package/.opencode/skills/bmad-cis-innovation-strategy/template.md +189 -0
- package/.opencode/skills/bmad-cis-innovation-strategy/workflow.md +315 -0
- package/.opencode/skills/bmad-cis-problem-solving/SKILL.md +6 -0
- package/.opencode/skills/bmad-cis-problem-solving/bmad-skill-manifest.yaml +1 -0
- package/.opencode/skills/bmad-cis-problem-solving/solving-methods.csv +31 -0
- package/.opencode/skills/bmad-cis-problem-solving/template.md +165 -0
- package/.opencode/skills/bmad-cis-problem-solving/workflow.md +291 -0
- package/.opencode/skills/bmad-cis-storytelling/SKILL.md +6 -0
- package/.opencode/skills/bmad-cis-storytelling/bmad-skill-manifest.yaml +1 -0
- package/.opencode/skills/bmad-cis-storytelling/story-types.csv +26 -0
- package/.opencode/skills/bmad-cis-storytelling/template.md +113 -0
- package/.opencode/skills/bmad-cis-storytelling/workflow.md +321 -0
- package/.opencode/skills/bmad-code-review/SKILL.md +6 -0
- package/.opencode/skills/bmad-code-review/steps/step-01-gather-context.md +62 -0
- package/.opencode/skills/bmad-code-review/steps/step-02-review.md +34 -0
- package/.opencode/skills/bmad-code-review/steps/step-03-triage.md +49 -0
- package/.opencode/skills/bmad-code-review/steps/step-04-present.md +129 -0
- package/.opencode/skills/bmad-code-review/workflow.md +55 -0
- package/.opencode/skills/bmad-correct-course/SKILL.md +6 -0
- package/.opencode/skills/bmad-correct-course/checklist.md +288 -0
- package/.opencode/skills/bmad-correct-course/workflow.md +267 -0
- package/.opencode/skills/bmad-create-architecture/SKILL.md +6 -0
- package/.opencode/skills/bmad-create-architecture/architecture-decision-template.md +12 -0
- package/.opencode/skills/bmad-create-architecture/data/domain-complexity.csv +13 -0
- package/.opencode/skills/bmad-create-architecture/data/project-types.csv +7 -0
- package/.opencode/skills/bmad-create-architecture/steps/step-01-init.md +153 -0
- package/.opencode/skills/bmad-create-architecture/steps/step-01b-continue.md +173 -0
- package/.opencode/skills/bmad-create-architecture/steps/step-02-context.md +224 -0
- package/.opencode/skills/bmad-create-architecture/steps/step-03-starter.md +329 -0
- package/.opencode/skills/bmad-create-architecture/steps/step-04-decisions.md +318 -0
- package/.opencode/skills/bmad-create-architecture/steps/step-05-patterns.md +359 -0
- package/.opencode/skills/bmad-create-architecture/steps/step-06-structure.md +379 -0
- package/.opencode/skills/bmad-create-architecture/steps/step-07-validation.md +359 -0
- package/.opencode/skills/bmad-create-architecture/steps/step-08-complete.md +76 -0
- package/.opencode/skills/bmad-create-architecture/workflow.md +38 -0
- package/.opencode/skills/bmad-create-epics-and-stories/SKILL.md +6 -0
- package/.opencode/skills/bmad-create-epics-and-stories/steps/step-01-validate-prerequisites.md +255 -0
- package/.opencode/skills/bmad-create-epics-and-stories/steps/step-02-design-epics.md +212 -0
- package/.opencode/skills/bmad-create-epics-and-stories/steps/step-03-create-stories.md +255 -0
- package/.opencode/skills/bmad-create-epics-and-stories/steps/step-04-final-validation.md +131 -0
- package/.opencode/skills/bmad-create-epics-and-stories/templates/epics-template.md +61 -0
- package/.opencode/skills/bmad-create-epics-and-stories/workflow.md +53 -0
- package/.opencode/skills/bmad-create-prd/SKILL.md +6 -0
- package/.opencode/skills/bmad-create-prd/data/domain-complexity.csv +15 -0
- package/.opencode/skills/bmad-create-prd/data/prd-purpose.md +197 -0
- package/.opencode/skills/bmad-create-prd/data/project-types.csv +11 -0
- package/.opencode/skills/bmad-create-prd/steps-c/step-01-init.md +178 -0
- package/.opencode/skills/bmad-create-prd/steps-c/step-01b-continue.md +161 -0
- package/.opencode/skills/bmad-create-prd/steps-c/step-02-discovery.md +208 -0
- package/.opencode/skills/bmad-create-prd/steps-c/step-02b-vision.md +142 -0
- package/.opencode/skills/bmad-create-prd/steps-c/step-02c-executive-summary.md +158 -0
- package/.opencode/skills/bmad-create-prd/steps-c/step-03-success.md +214 -0
- package/.opencode/skills/bmad-create-prd/steps-c/step-04-journeys.md +201 -0
- package/.opencode/skills/bmad-create-prd/steps-c/step-05-domain.md +194 -0
- package/.opencode/skills/bmad-create-prd/steps-c/step-06-innovation.md +211 -0
- package/.opencode/skills/bmad-create-prd/steps-c/step-07-project-type.md +222 -0
- package/.opencode/skills/bmad-create-prd/steps-c/step-08-scoping.md +216 -0
- package/.opencode/skills/bmad-create-prd/steps-c/step-09-functional.md +219 -0
- package/.opencode/skills/bmad-create-prd/steps-c/step-10-nonfunctional.md +230 -0
- package/.opencode/skills/bmad-create-prd/steps-c/step-11-polish.md +221 -0
- package/.opencode/skills/bmad-create-prd/steps-c/step-12-complete.md +115 -0
- package/.opencode/skills/bmad-create-prd/templates/prd-template.md +10 -0
- package/.opencode/skills/bmad-create-prd/workflow.md +62 -0
- package/.opencode/skills/bmad-create-story/SKILL.md +6 -0
- package/.opencode/skills/bmad-create-story/checklist.md +357 -0
- package/.opencode/skills/bmad-create-story/discover-inputs.md +88 -0
- package/.opencode/skills/bmad-create-story/template.md +49 -0
- package/.opencode/skills/bmad-create-story/workflow.md +380 -0
- package/.opencode/skills/bmad-create-ux-design/SKILL.md +6 -0
- package/.opencode/skills/bmad-create-ux-design/steps/step-01-init.md +135 -0
- package/.opencode/skills/bmad-create-ux-design/steps/step-01b-continue.md +127 -0
- package/.opencode/skills/bmad-create-ux-design/steps/step-02-discovery.md +190 -0
- package/.opencode/skills/bmad-create-ux-design/steps/step-03-core-experience.md +217 -0
- package/.opencode/skills/bmad-create-ux-design/steps/step-04-emotional-response.md +220 -0
- package/.opencode/skills/bmad-create-ux-design/steps/step-05-inspiration.md +235 -0
- package/.opencode/skills/bmad-create-ux-design/steps/step-06-design-system.md +253 -0
- package/.opencode/skills/bmad-create-ux-design/steps/step-07-defining-experience.md +255 -0
- package/.opencode/skills/bmad-create-ux-design/steps/step-08-visual-foundation.md +225 -0
- package/.opencode/skills/bmad-create-ux-design/steps/step-09-design-directions.md +225 -0
- package/.opencode/skills/bmad-create-ux-design/steps/step-10-user-journeys.md +242 -0
- package/.opencode/skills/bmad-create-ux-design/steps/step-11-component-strategy.md +249 -0
- package/.opencode/skills/bmad-create-ux-design/steps/step-12-ux-patterns.md +238 -0
- package/.opencode/skills/bmad-create-ux-design/steps/step-13-responsive-accessibility.md +265 -0
- package/.opencode/skills/bmad-create-ux-design/steps/step-14-complete.md +171 -0
- package/.opencode/skills/bmad-create-ux-design/ux-design-template.md +13 -0
- package/.opencode/skills/bmad-create-ux-design/workflow.md +36 -0
- package/.opencode/skills/bmad-dev-story/SKILL.md +6 -0
- package/.opencode/skills/bmad-dev-story/checklist.md +80 -0
- package/.opencode/skills/bmad-dev-story/workflow.md +450 -0
- package/.opencode/skills/bmad-distillator/SKILL.md +178 -0
- package/.opencode/skills/bmad-distillator/agents/distillate-compressor.md +116 -0
- package/.opencode/skills/bmad-distillator/agents/round-trip-reconstructor.md +68 -0
- package/.opencode/skills/bmad-distillator/resources/compression-rules.md +51 -0
- package/.opencode/skills/bmad-distillator/resources/distillate-format-reference.md +227 -0
- package/.opencode/skills/bmad-distillator/resources/splitting-strategy.md +78 -0
- package/.opencode/skills/bmad-distillator/scripts/analyze_sources.py +300 -0
- package/.opencode/skills/bmad-distillator/scripts/tests/test_analyze_sources.py +204 -0
- package/.opencode/skills/bmad-document-project/SKILL.md +6 -0
- package/.opencode/skills/bmad-document-project/checklist.md +245 -0
- package/.opencode/skills/bmad-document-project/documentation-requirements.csv +12 -0
- package/.opencode/skills/bmad-document-project/instructions.md +128 -0
- package/.opencode/skills/bmad-document-project/templates/deep-dive-template.md +345 -0
- package/.opencode/skills/bmad-document-project/templates/index-template.md +169 -0
- package/.opencode/skills/bmad-document-project/templates/project-overview-template.md +103 -0
- package/.opencode/skills/bmad-document-project/templates/project-scan-report-schema.json +160 -0
- package/.opencode/skills/bmad-document-project/templates/source-tree-template.md +135 -0
- package/.opencode/skills/bmad-document-project/workflow.md +27 -0
- package/.opencode/skills/bmad-document-project/workflows/deep-dive-instructions.md +299 -0
- package/.opencode/skills/bmad-document-project/workflows/deep-dive-workflow.md +34 -0
- package/.opencode/skills/bmad-document-project/workflows/full-scan-instructions.md +1107 -0
- package/.opencode/skills/bmad-document-project/workflows/full-scan-workflow.md +34 -0
- package/.opencode/skills/bmad-domain-research/SKILL.md +6 -0
- package/.opencode/skills/bmad-domain-research/domain-steps/step-01-init.md +137 -0
- package/.opencode/skills/bmad-domain-research/domain-steps/step-02-domain-analysis.md +229 -0
- package/.opencode/skills/bmad-domain-research/domain-steps/step-03-competitive-landscape.md +238 -0
- package/.opencode/skills/bmad-domain-research/domain-steps/step-04-regulatory-focus.md +206 -0
- package/.opencode/skills/bmad-domain-research/domain-steps/step-05-technical-trends.md +234 -0
- package/.opencode/skills/bmad-domain-research/domain-steps/step-06-research-synthesis.md +444 -0
- package/.opencode/skills/bmad-domain-research/research.template.md +29 -0
- package/.opencode/skills/bmad-domain-research/workflow.md +49 -0
- package/.opencode/skills/bmad-edit-prd/SKILL.md +6 -0
- package/.opencode/skills/bmad-edit-prd/steps-e/step-e-01-discovery.md +242 -0
- package/.opencode/skills/bmad-edit-prd/steps-e/step-e-01b-legacy-conversion.md +204 -0
- package/.opencode/skills/bmad-edit-prd/steps-e/step-e-02-review.md +245 -0
- package/.opencode/skills/bmad-edit-prd/steps-e/step-e-03-edit.md +250 -0
- package/.opencode/skills/bmad-edit-prd/steps-e/step-e-04-complete.md +165 -0
- package/.opencode/skills/bmad-edit-prd/workflow.md +63 -0
- package/.opencode/skills/bmad-editorial-review-prose/SKILL.md +86 -0
- package/.opencode/skills/bmad-editorial-review-structure/SKILL.md +179 -0
- package/.opencode/skills/bmad-generate-project-context/SKILL.md +6 -0
- package/.opencode/skills/bmad-generate-project-context/project-context-template.md +21 -0
- package/.opencode/skills/bmad-generate-project-context/steps/step-01-discover.md +186 -0
- package/.opencode/skills/bmad-generate-project-context/steps/step-02-generate.md +321 -0
- package/.opencode/skills/bmad-generate-project-context/steps/step-03-complete.md +278 -0
- package/.opencode/skills/bmad-generate-project-context/workflow.md +43 -0
- package/.opencode/skills/bmad-help/SKILL.md +73 -0
- package/.opencode/skills/bmad-index-docs/SKILL.md +66 -0
- package/.opencode/skills/bmad-init/SKILL.md +100 -0
- package/.opencode/skills/bmad-init/resources/core-module.yaml +25 -0
- package/.opencode/skills/bmad-init/scripts/bmad_init.py +593 -0
- package/.opencode/skills/bmad-init/scripts/tests/test_bmad_init.py +329 -0
- package/.opencode/skills/bmad-ma-agent-cyber/SKILL.md +49 -0
- package/.opencode/skills/bmad-ma-agent-cyber/bmad-skill-manifest.yaml +11 -0
- package/.opencode/skills/bmad-ma-agent-devops/SKILL.md +49 -0
- package/.opencode/skills/bmad-ma-agent-devops/bmad-skill-manifest.yaml +11 -0
- package/.opencode/skills/bmad-ma-agent-mil498/.gitkeep +0 -0
- package/.opencode/skills/bmad-ma-agent-mil498/SKILL.md +53 -0
- package/.opencode/skills/bmad-ma-agent-mil498/bmad-skill-manifest.yaml +11 -0
- package/.opencode/skills/bmad-ma-agent-ml/.gitkeep +0 -0
- package/.opencode/skills/bmad-ma-agent-ml/SKILL.md +59 -0
- package/.opencode/skills/bmad-ma-agent-ml/bmad-skill-manifest.yaml +11 -0
- package/.opencode/skills/bmad-ma-agent-sre/.gitkeep +0 -0
- package/.opencode/skills/bmad-ma-agent-sre/SKILL.md +49 -0
- package/.opencode/skills/bmad-ma-agent-sre/bmad-skill-manifest.yaml +11 -0
- package/.opencode/skills/bmad-market-research/SKILL.md +6 -0
- package/.opencode/skills/bmad-market-research/research.template.md +29 -0
- package/.opencode/skills/bmad-market-research/steps/step-01-init.md +184 -0
- package/.opencode/skills/bmad-market-research/steps/step-02-customer-behavior.md +239 -0
- package/.opencode/skills/bmad-market-research/steps/step-03-customer-pain-points.md +251 -0
- package/.opencode/skills/bmad-market-research/steps/step-04-customer-decisions.md +261 -0
- package/.opencode/skills/bmad-market-research/steps/step-05-competitive-analysis.md +173 -0
- package/.opencode/skills/bmad-market-research/steps/step-06-research-completion.md +478 -0
- package/.opencode/skills/bmad-market-research/workflow.md +49 -0
- package/.opencode/skills/bmad-party-mode/SKILL.md +6 -0
- package/.opencode/skills/bmad-party-mode/steps/step-01-agent-loading.md +138 -0
- package/.opencode/skills/bmad-party-mode/steps/step-02-discussion-orchestration.md +187 -0
- package/.opencode/skills/bmad-party-mode/steps/step-03-graceful-exit.md +167 -0
- package/.opencode/skills/bmad-party-mode/workflow.md +190 -0
- package/.opencode/skills/bmad-product-brief/SKILL.md +87 -0
- package/.opencode/skills/bmad-product-brief/agents/artifact-analyzer.md +60 -0
- package/.opencode/skills/bmad-product-brief/agents/opportunity-reviewer.md +44 -0
- package/.opencode/skills/bmad-product-brief/agents/skeptic-reviewer.md +44 -0
- package/.opencode/skills/bmad-product-brief/agents/web-researcher.md +49 -0
- package/.opencode/skills/bmad-product-brief/bmad-manifest.json +17 -0
- package/.opencode/skills/bmad-product-brief/prompts/contextual-discovery.md +57 -0
- package/.opencode/skills/bmad-product-brief/prompts/draft-and-review.md +86 -0
- package/.opencode/skills/bmad-product-brief/prompts/finalize.md +75 -0
- package/.opencode/skills/bmad-product-brief/prompts/guided-elicitation.md +70 -0
- package/.opencode/skills/bmad-product-brief/resources/brief-template.md +60 -0
- package/.opencode/skills/bmad-qa-generate-e2e-tests/SKILL.md +6 -0
- package/.opencode/skills/bmad-qa-generate-e2e-tests/checklist.md +33 -0
- package/.opencode/skills/bmad-qa-generate-e2e-tests/workflow.md +136 -0
- package/.opencode/skills/bmad-quick-dev/SKILL.md +6 -0
- package/.opencode/skills/bmad-quick-dev/spec-template.md +88 -0
- package/.opencode/skills/bmad-quick-dev/step-01-clarify-and-route.md +64 -0
- package/.opencode/skills/bmad-quick-dev/step-02-plan.md +35 -0
- package/.opencode/skills/bmad-quick-dev/step-03-implement.md +37 -0
- package/.opencode/skills/bmad-quick-dev/step-04-review.md +49 -0
- package/.opencode/skills/bmad-quick-dev/step-05-present.md +63 -0
- package/.opencode/skills/bmad-quick-dev/step-oneshot.md +49 -0
- package/.opencode/skills/bmad-quick-dev/workflow.md +79 -0
- package/.opencode/skills/bmad-retrospective/SKILL.md +6 -0
- package/.opencode/skills/bmad-retrospective/workflow.md +1479 -0
- package/.opencode/skills/bmad-review-adversarial-general/SKILL.md +37 -0
- package/.opencode/skills/bmad-review-edge-case-hunter/SKILL.md +67 -0
- package/.opencode/skills/bmad-shard-doc/SKILL.md +105 -0
- package/.opencode/skills/bmad-sprint-planning/SKILL.md +6 -0
- package/.opencode/skills/bmad-sprint-planning/checklist.md +33 -0
- package/.opencode/skills/bmad-sprint-planning/sprint-status-template.yaml +56 -0
- package/.opencode/skills/bmad-sprint-planning/workflow.md +263 -0
- package/.opencode/skills/bmad-sprint-status/SKILL.md +6 -0
- package/.opencode/skills/bmad-sprint-status/workflow.md +261 -0
- package/.opencode/skills/bmad-technical-research/SKILL.md +6 -0
- package/.opencode/skills/bmad-technical-research/research.template.md +29 -0
- package/.opencode/skills/bmad-technical-research/technical-steps/step-01-init.md +137 -0
- package/.opencode/skills/bmad-technical-research/technical-steps/step-02-technical-overview.md +239 -0
- package/.opencode/skills/bmad-technical-research/technical-steps/step-03-integration-patterns.md +248 -0
- package/.opencode/skills/bmad-technical-research/technical-steps/step-04-architectural-patterns.md +202 -0
- package/.opencode/skills/bmad-technical-research/technical-steps/step-05-implementation-research.md +233 -0
- package/.opencode/skills/bmad-technical-research/technical-steps/step-06-research-synthesis.md +487 -0
- package/.opencode/skills/bmad-technical-research/workflow.md +50 -0
- package/.opencode/skills/bmad-validate-prd/SKILL.md +6 -0
- package/.opencode/skills/bmad-validate-prd/data/domain-complexity.csv +15 -0
- package/.opencode/skills/bmad-validate-prd/data/prd-purpose.md +197 -0
- package/.opencode/skills/bmad-validate-prd/data/project-types.csv +11 -0
- package/.opencode/skills/bmad-validate-prd/steps-v/step-v-01-discovery.md +221 -0
- package/.opencode/skills/bmad-validate-prd/steps-v/step-v-02-format-detection.md +188 -0
- package/.opencode/skills/bmad-validate-prd/steps-v/step-v-02b-parity-check.md +206 -0
- package/.opencode/skills/bmad-validate-prd/steps-v/step-v-03-density-validation.md +171 -0
- package/.opencode/skills/bmad-validate-prd/steps-v/step-v-04-brief-coverage-validation.md +211 -0
- package/.opencode/skills/bmad-validate-prd/steps-v/step-v-05-measurability-validation.md +225 -0
- package/.opencode/skills/bmad-validate-prd/steps-v/step-v-06-traceability-validation.md +214 -0
- package/.opencode/skills/bmad-validate-prd/steps-v/step-v-07-implementation-leakage-validation.md +202 -0
- package/.opencode/skills/bmad-validate-prd/steps-v/step-v-08-domain-compliance-validation.md +240 -0
- package/.opencode/skills/bmad-validate-prd/steps-v/step-v-09-project-type-validation.md +260 -0
- package/.opencode/skills/bmad-validate-prd/steps-v/step-v-10-smart-validation.md +206 -0
- package/.opencode/skills/bmad-validate-prd/steps-v/step-v-11-holistic-quality-validation.md +261 -0
- package/.opencode/skills/bmad-validate-prd/steps-v/step-v-12-completeness-validation.md +239 -0
- package/.opencode/skills/bmad-validate-prd/steps-v/step-v-13-report-complete.md +229 -0
- package/.opencode/skills/bmad-validate-prd/workflow.md +62 -0
- package/.opencode/skills/cleanup-done/.gitkeep +0 -0
- package/.opencode/skills/create-bug-story/.gitkeep +0 -0
- package/.opencode/skills/create-bug-story/SKILL.md +263 -0
- package/.opencode/skills/create-bug-story/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/cyber-generate-certs/.gitkeep +0 -0
- package/.opencode/skills/cyber-generate-certs/SKILL.md +27 -0
- package/.opencode/skills/cyber-generate-certs/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/cyber-immunity-estimation/.gitkeep +0 -0
- package/.opencode/skills/cyber-immunity-estimation/SKILL.md +29 -0
- package/.opencode/skills/cyber-immunity-estimation/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/cyber-security-audit/.gitkeep +0 -0
- package/.opencode/skills/cyber-security-audit/SKILL.md +27 -0
- package/.opencode/skills/cyber-security-audit/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/cyber-vault-secrets/.gitkeep +0 -0
- package/.opencode/skills/cyber-vault-secrets/SKILL.md +28 -0
- package/.opencode/skills/cyber-vault-secrets/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/cyber-verify-docker-users/.gitkeep +0 -0
- package/.opencode/skills/cyber-verify-docker-users/SKILL.md +23 -0
- package/.opencode/skills/cyber-verify-docker-users/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/cyber-verify-image-signature/.gitkeep +0 -0
- package/.opencode/skills/cyber-verify-image-signature/SKILL.md +22 -0
- package/.opencode/skills/cyber-verify-image-signature/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/cyber-vulnerability-scan/.gitkeep +0 -0
- package/.opencode/skills/cyber-vulnerability-scan/SKILL.md +28 -0
- package/.opencode/skills/cyber-vulnerability-scan/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/devops-configure-infrastructure/.gitkeep +0 -0
- package/.opencode/skills/devops-configure-infrastructure/SKILL.md +27 -0
- package/.opencode/skills/devops-configure-infrastructure/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/devops-disconnected-deployment/.gitkeep +0 -0
- package/.opencode/skills/devops-disconnected-deployment/SKILL.md +27 -0
- package/.opencode/skills/devops-disconnected-deployment/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/devops-docker-compose-setup/.gitkeep +0 -0
- package/.opencode/skills/devops-docker-compose-setup/SKILL.md +26 -0
- package/.opencode/skills/devops-docker-compose-setup/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/devops-manage-helm/.gitkeep +0 -0
- package/.opencode/skills/devops-manage-helm/SKILL.md +28 -0
- package/.opencode/skills/devops-manage-helm/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/devops-sign-docker-image/.gitkeep +0 -0
- package/.opencode/skills/devops-sign-docker-image/SKILL.md +24 -0
- package/.opencode/skills/devops-sign-docker-image/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/generate-backlog/.gitkeep +0 -0
- package/.opencode/skills/mil498-ocd/.gitkeep +0 -0
- package/.opencode/skills/mil498-ocd/SKILL.md +30 -0
- package/.opencode/skills/mil498-ocd/bmad-skill-manifest.yaml +5 -0
- package/.opencode/skills/mil498-ocd/prompts/01-discover-project-artifacts.md +26 -0
- package/.opencode/skills/mil498-ocd/prompts/02-load-template.md +10 -0
- package/.opencode/skills/mil498-ocd/prompts/03-generate-document.md +90 -0
- package/.opencode/skills/mil498-ocd/prompts/04-validate.md +14 -0
- package/.opencode/skills/mil498-ocd/prompts/05-review.md +15 -0
- package/.opencode/skills/mil498-ocd/prompts/06-save.md +15 -0
- package/.opencode/skills/mil498-ocd/template.md +169 -0
- package/.opencode/skills/mil498-sdd/.gitkeep +0 -0
- package/.opencode/skills/mil498-sdd/SKILL.md +30 -0
- package/.opencode/skills/mil498-sdd/bmad-skill-manifest.yaml +5 -0
- package/.opencode/skills/mil498-sdd/prompts/01-discover-project-artifacts.md +50 -0
- package/.opencode/skills/mil498-sdd/prompts/02-load-template.md +10 -0
- package/.opencode/skills/mil498-sdd/prompts/03-generate-document.md +98 -0
- package/.opencode/skills/mil498-sdd/prompts/04-validate.md +16 -0
- package/.opencode/skills/mil498-sdd/prompts/05-review.md +15 -0
- package/.opencode/skills/mil498-sdd/prompts/06-save.md +19 -0
- package/.opencode/skills/mil498-sdd/template.md +163 -0
- package/.opencode/skills/mil498-sdp/.gitkeep +0 -0
- package/.opencode/skills/mil498-sdp/SKILL.md +30 -0
- package/.opencode/skills/mil498-sdp/bmad-skill-manifest.yaml +5 -0
- package/.opencode/skills/mil498-sdp/prompts/01-discover-project-artifacts.md +32 -0
- package/.opencode/skills/mil498-sdp/prompts/02-load-template.md +10 -0
- package/.opencode/skills/mil498-sdp/prompts/03-generate-document.md +187 -0
- package/.opencode/skills/mil498-sdp/prompts/04-validate.md +13 -0
- package/.opencode/skills/mil498-sdp/prompts/05-review.md +15 -0
- package/.opencode/skills/mil498-sdp/prompts/06-save.md +14 -0
- package/.opencode/skills/mil498-sdp/template.md +307 -0
- package/.opencode/skills/mil498-srs/.gitkeep +0 -0
- package/.opencode/skills/mil498-srs/SKILL.md +30 -0
- package/.opencode/skills/mil498-srs/bmad-skill-manifest.yaml +5 -0
- package/.opencode/skills/mil498-srs/prompts/01-discover-project-artifacts.md +42 -0
- package/.opencode/skills/mil498-srs/prompts/02-load-template.md +10 -0
- package/.opencode/skills/mil498-srs/prompts/03-generate-document.md +100 -0
- package/.opencode/skills/mil498-srs/prompts/04-validate.md +16 -0
- package/.opencode/skills/mil498-srs/prompts/05-review.md +15 -0
- package/.opencode/skills/mil498-srs/prompts/06-save.md +18 -0
- package/.opencode/skills/mil498-srs/template.md +219 -0
- package/.opencode/skills/mil498-ssdd/.gitkeep +0 -0
- package/.opencode/skills/mil498-ssdd/SKILL.md +32 -0
- package/.opencode/skills/mil498-ssdd/bmad-skill-manifest.yaml +5 -0
- package/.opencode/skills/mil498-ssdd/prompts/01-discover-project-artifacts.md +32 -0
- package/.opencode/skills/mil498-ssdd/prompts/02-load-template.md +10 -0
- package/.opencode/skills/mil498-ssdd/prompts/03-csci-discovery-interview.md +43 -0
- package/.opencode/skills/mil498-ssdd/prompts/04-generate-document.md +96 -0
- package/.opencode/skills/mil498-ssdd/prompts/05-validate.md +14 -0
- package/.opencode/skills/mil498-ssdd/prompts/06-review.md +16 -0
- package/.opencode/skills/mil498-ssdd/prompts/07-save.md +16 -0
- package/.opencode/skills/mil498-ssdd/template.md +154 -0
- package/.opencode/skills/mil498-sss/.gitkeep +0 -0
- package/.opencode/skills/mil498-sss/SKILL.md +31 -0
- package/.opencode/skills/mil498-sss/bmad-skill-manifest.yaml +5 -0
- package/.opencode/skills/mil498-sss/prompts/01-discover-project-artifacts.md +31 -0
- package/.opencode/skills/mil498-sss/prompts/02-load-template.md +10 -0
- package/.opencode/skills/mil498-sss/prompts/03-generate-document.md +108 -0
- package/.opencode/skills/mil498-sss/prompts/04-validate.md +16 -0
- package/.opencode/skills/mil498-sss/prompts/05-review.md +15 -0
- package/.opencode/skills/mil498-sss/prompts/06-save.md +15 -0
- package/.opencode/skills/mil498-sss/template.md +225 -0
- package/.opencode/skills/mil498-std/.gitkeep +0 -0
- package/.opencode/skills/mil498-std/SKILL.md +30 -0
- package/.opencode/skills/mil498-std/bmad-skill-manifest.yaml +5 -0
- package/.opencode/skills/mil498-std/prompts/01-discover-project-artifacts.md +42 -0
- package/.opencode/skills/mil498-std/prompts/02-load-template.md +10 -0
- package/.opencode/skills/mil498-std/prompts/03-generate-document.md +117 -0
- package/.opencode/skills/mil498-std/prompts/04-validate.md +15 -0
- package/.opencode/skills/mil498-std/prompts/05-review.md +15 -0
- package/.opencode/skills/mil498-std/prompts/06-save.md +15 -0
- package/.opencode/skills/mil498-std/template.md +188 -0
- package/.opencode/skills/ml-advise/.gitkeep +0 -0
- package/.opencode/skills/ml-advise/SKILL.md +76 -0
- package/.opencode/skills/ml-advise/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/ml-advise/skill.json +7 -0
- package/.opencode/skills/ml-analysis/.gitkeep +0 -0
- package/.opencode/skills/ml-analysis/SKILL.md +60 -0
- package/.opencode/skills/ml-analysis/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/ml-analysis/skill.json +7 -0
- package/.opencode/skills/ml-architecture/.gitkeep +0 -0
- package/.opencode/skills/ml-architecture/SKILL.md +55 -0
- package/.opencode/skills/ml-architecture/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/ml-architecture/skill.json +7 -0
- package/.opencode/skills/ml-detailed-design/.gitkeep +0 -0
- package/.opencode/skills/ml-detailed-design/SKILL.md +67 -0
- package/.opencode/skills/ml-detailed-design/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/ml-detailed-design/skill.json +7 -0
- package/.opencode/skills/ml-eda/.gitkeep +0 -0
- package/.opencode/skills/ml-eda/SKILL.md +56 -0
- package/.opencode/skills/ml-eda/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/ml-eda/scripts/baseline_classifier.py +522 -0
- package/.opencode/skills/ml-eda/scripts/class_weights_calculator.py +295 -0
- package/.opencode/skills/ml-eda/scripts/clustering_explorer.py +383 -0
- package/.opencode/skills/ml-eda/scripts/eda_analyzer.py +654 -0
- package/.opencode/skills/ml-eda/skill.json +7 -0
- package/.opencode/skills/ml-experiment/.gitkeep +0 -0
- package/.opencode/skills/ml-experiment/SKILL.md +74 -0
- package/.opencode/skills/ml-experiment/assets/advanced_trainer_configs.py +430 -0
- package/.opencode/skills/ml-experiment/assets/quick_trainer_setup.py +233 -0
- package/.opencode/skills/ml-experiment/assets/template_datamodule.py +219 -0
- package/.opencode/skills/ml-experiment/assets/template_gnn_module.py +341 -0
- package/.opencode/skills/ml-experiment/assets/template_lightning_module.py +158 -0
- package/.opencode/skills/ml-experiment/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/ml-experiment/skill.json +7 -0
- package/.opencode/skills/ml-hparam/.gitkeep +0 -0
- package/.opencode/skills/ml-hparam/SKILL.md +81 -0
- package/.opencode/skills/ml-hparam/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/ml-hparam/skill.json +7 -0
- package/.opencode/skills/ml-ideation/.gitkeep +0 -0
- package/.opencode/skills/ml-ideation/SKILL.md +50 -0
- package/.opencode/skills/ml-ideation/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/ml-ideation/scripts/validate_ml_prd.py +287 -0
- package/.opencode/skills/ml-ideation/skill.json +7 -0
- package/.opencode/skills/ml-infra/.gitkeep +0 -0
- package/.opencode/skills/ml-infra/SKILL.md +58 -0
- package/.opencode/skills/ml-infra/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/ml-infra/skill.json +7 -0
- package/.opencode/skills/ml-retrospective/.gitkeep +0 -0
- package/.opencode/skills/ml-retrospective/SKILL.md +63 -0
- package/.opencode/skills/ml-retrospective/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/ml-retrospective/skill.json +7 -0
- package/.opencode/skills/ml-revision/.gitkeep +0 -0
- package/.opencode/skills/ml-revision/SKILL.md +82 -0
- package/.opencode/skills/ml-revision/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/ml-revision/skill.json +7 -0
- package/.opencode/skills/ml-techspec/.gitkeep +0 -0
- package/.opencode/skills/ml-techspec/SKILL.md +80 -0
- package/.opencode/skills/ml-techspec/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/ml-techspec/skill.json +7 -0
- package/.opencode/skills/modify-sprint/.gitkeep +0 -0
- package/.opencode/skills/modify-sprint/SKILL.md +322 -0
- package/.opencode/skills/modify-sprint/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/prioritize-backlog/.gitkeep +0 -0
- package/.opencode/skills/project-context-expansion/.gitkeep +0 -0
- package/.opencode/skills/project-context-expansion/SKILL.md +238 -0
- package/.opencode/skills/project-context-expansion/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/remove-from-sprint/.gitkeep +0 -0
- package/.opencode/skills/sprint-status-view/.gitkeep +0 -0
- package/.opencode/skills/sprint-status-view/SKILL.md +263 -0
- package/.opencode/skills/sprint-status-view/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/sre-check-deployment-status/.gitkeep +0 -0
- package/.opencode/skills/sre-check-deployment-status/SKILL.md +32 -0
- package/.opencode/skills/sre-check-deployment-status/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/sre-check-secrets/.gitkeep +0 -0
- package/.opencode/skills/sre-check-secrets/SKILL.md +23 -0
- package/.opencode/skills/sre-check-secrets/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/sre-check-system-status/.gitkeep +0 -0
- package/.opencode/skills/sre-check-system-status/SKILL.md +27 -0
- package/.opencode/skills/sre-check-system-status/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/sre-day-2-ops/.gitkeep +0 -0
- package/.opencode/skills/sre-day-2-ops/SKILL.md +26 -0
- package/.opencode/skills/sre-day-2-ops/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/sre-deployment-strategies/.gitkeep +0 -0
- package/.opencode/skills/sre-deployment-strategies/SKILL.md +28 -0
- package/.opencode/skills/sre-deployment-strategies/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/sre-fix-deployments/.gitkeep +0 -0
- package/.opencode/skills/sre-fix-deployments/SKILL.md +25 -0
- package/.opencode/skills/sre-fix-deployments/bmad-skill-manifest.yaml +3 -0
- package/.opencode/skills/sre-gitops-status/.gitkeep +0 -0
- package/.opencode/skills/sre-gitops-status/SKILL.md +25 -0
- package/.opencode/skills/sre-gitops-status/bmad-skill-manifest.yaml +3 -0
- package/.roo/skills/.ma-agents.json +134 -134
- package/bin/cli.js +25 -10
- package/lib/agents.js +21 -51
- package/lib/bmad-extension/module-help.csv +4 -21
- package/lib/bmad-extension/skills/add-sprint/SKILL.md +40 -126
- package/lib/bmad-extension/skills/add-to-sprint/SKILL.md +142 -116
- package/lib/bmad-extension/skills/create-bug-story/SKILL.md +7 -75
- package/lib/bmad-extension/skills/modify-sprint/SKILL.md +0 -63
- package/lib/bmad-extension/skills/sprint-status-view/SKILL.md +138 -199
- package/lib/bmad-extension/workflows/add-sprint/workflow.md +39 -129
- package/lib/bmad-extension/workflows/add-to-sprint/workflow.md +205 -3
- package/lib/bmad-extension/workflows/modify-sprint/workflow.md +0 -5
- package/lib/bmad-extension/workflows/sprint-status-view/workflow.md +192 -3
- package/lib/installer.js +13 -5
- package/package.json +1 -1
- /package/{lib/bmad-extension/skills/cleanup-done → .opencode/skills/add-sprint}/.gitkeep +0 -0
- /package/{lib/bmad-extension/skills/generate-backlog → .opencode/skills/add-to-sprint}/.gitkeep +0 -0
- /package/{lib/bmad-extension/skills/prioritize-backlog → .opencode/skills/bmad-ma-agent-cyber}/.gitkeep +0 -0
- /package/{lib/bmad-extension/skills/remove-from-sprint → .opencode/skills/bmad-ma-agent-devops}/.gitkeep +0 -0
- /package/{lib/bmad-extension → .opencode}/skills/cleanup-done/SKILL.md +0 -0
- /package/{lib/bmad-extension → .opencode}/skills/cleanup-done/bmad-skill-manifest.yaml +0 -0
- /package/{lib/bmad-extension → .opencode}/skills/generate-backlog/SKILL.md +0 -0
- /package/{lib/bmad-extension → .opencode}/skills/generate-backlog/bmad-skill-manifest.yaml +0 -0
- /package/{lib/bmad-extension → .opencode}/skills/prioritize-backlog/SKILL.md +0 -0
- /package/{lib/bmad-extension → .opencode}/skills/prioritize-backlog/bmad-skill-manifest.yaml +0 -0
- /package/{lib/bmad-extension → .opencode}/skills/remove-from-sprint/SKILL.md +0 -0
- /package/{lib/bmad-extension → .opencode}/skills/remove-from-sprint/bmad-skill-manifest.yaml +0 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Semantic Splitting Strategy
|
|
2
|
+
|
|
3
|
+
When the source content is large (exceeds ~15,000 tokens) or a token_budget requires it, split the distillate into semantically coherent sections rather than arbitrary size breaks.
|
|
4
|
+
|
|
5
|
+
## Why Semantic Over Size-Based
|
|
6
|
+
|
|
7
|
+
Arbitrary splits (every N tokens) break coherence. A downstream workflow loading "part 2 of 4" gets context fragments. Semantic splits produce self-contained topic clusters that a workflow can load selectively — "give me just the technical decisions section" — which is more useful and more token-efficient for the consumer.
|
|
8
|
+
|
|
9
|
+
## Splitting Process
|
|
10
|
+
|
|
11
|
+
### 1. Identify Natural Boundaries
|
|
12
|
+
|
|
13
|
+
After the initial extraction and deduplication (Steps 1-2 of the compression process), look for natural semantic boundaries:
|
|
14
|
+
- Distinct problem domains or functional areas
|
|
15
|
+
- Different stakeholder perspectives (users, technical, business)
|
|
16
|
+
- Temporal boundaries (current state vs future vision)
|
|
17
|
+
- Scope boundaries (in-scope vs out-of-scope vs deferred)
|
|
18
|
+
- Phase boundaries (analysis, design, implementation)
|
|
19
|
+
|
|
20
|
+
Choose boundaries that produce sections a downstream workflow might load independently.
|
|
21
|
+
|
|
22
|
+
### 2. Assign Items to Sections
|
|
23
|
+
|
|
24
|
+
For each extracted item, assign it to the most relevant section. Items that span multiple sections go in the root distillate.
|
|
25
|
+
|
|
26
|
+
Cross-cutting items (items relevant to multiple sections):
|
|
27
|
+
- Constraints that affect all areas → root distillate
|
|
28
|
+
- Decisions with broad impact → root distillate
|
|
29
|
+
- Section-specific decisions → section distillate
|
|
30
|
+
|
|
31
|
+
### 3. Produce Root Distillate
|
|
32
|
+
|
|
33
|
+
The root distillate contains:
|
|
34
|
+
- **Orientation** (3-5 bullets): what was distilled, from what sources, for what consumer, how many sections
|
|
35
|
+
- **Cross-references**: list of section distillates with 1-line descriptions
|
|
36
|
+
- **Cross-cutting items**: facts, decisions, and constraints that span multiple sections
|
|
37
|
+
- **Scope summary**: high-level in/out/deferred if applicable
|
|
38
|
+
|
|
39
|
+
### 4. Produce Section Distillates
|
|
40
|
+
|
|
41
|
+
Each section distillate must be self-sufficient — a reader loading only one section should understand it without the others.
|
|
42
|
+
|
|
43
|
+
Each section includes:
|
|
44
|
+
- **Context header** (1 line): "This section covers [topic]. Part N of M from [source document names]."
|
|
45
|
+
- **Section content**: thematically-grouped bullets following the same compression rules as a single distillate
|
|
46
|
+
- **Cross-references** (if needed): pointers to other sections for related content
|
|
47
|
+
|
|
48
|
+
### 5. Output Structure
|
|
49
|
+
|
|
50
|
+
Create a folder `{base-name}-distillate/` containing:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
{base-name}-distillate/
|
|
54
|
+
├── _index.md # Root distillate: orientation, cross-cutting items, section manifest
|
|
55
|
+
├── 01-{topic-slug}.md # Self-contained section
|
|
56
|
+
├── 02-{topic-slug}.md
|
|
57
|
+
└── 03-{topic-slug}.md
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Example:
|
|
61
|
+
```
|
|
62
|
+
product-brief-distillate/
|
|
63
|
+
├── _index.md
|
|
64
|
+
├── 01-problem-solution.md
|
|
65
|
+
├── 02-technical-decisions.md
|
|
66
|
+
└── 03-users-market.md
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Size Targets
|
|
70
|
+
|
|
71
|
+
When a token_budget is specified:
|
|
72
|
+
- Root distillate: ~20% of budget (orientation + cross-cutting items)
|
|
73
|
+
- Remaining budget split proportionally across sections based on content density
|
|
74
|
+
- If a section exceeds its proportional share, compress more aggressively or sub-split
|
|
75
|
+
|
|
76
|
+
When no token_budget but splitting is needed:
|
|
77
|
+
- Aim for sections of 3,000-5,000 tokens each
|
|
78
|
+
- Root distillate as small as possible while remaining useful standalone
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
# /// script
|
|
2
|
+
# /// requires-python = ">=3.10"
|
|
3
|
+
# /// dependencies = []
|
|
4
|
+
# ///
|
|
5
|
+
"""Analyze source documents for the distillation generator.
|
|
6
|
+
|
|
7
|
+
Enumerates files from paths/folders/globs, computes sizes and token estimates,
|
|
8
|
+
detects document types from naming conventions, and suggests groupings for
|
|
9
|
+
related documents (e.g., a brief paired with its discovery notes).
|
|
10
|
+
|
|
11
|
+
Accepts: file paths, folder paths (scans recursively for .md/.txt/.yaml/.yml/.json),
|
|
12
|
+
or glob patterns. Skips node_modules, .git, __pycache__, .venv, _bmad-output.
|
|
13
|
+
|
|
14
|
+
Output JSON structure:
|
|
15
|
+
status: "ok" | "error"
|
|
16
|
+
files[]: path, filename, size_bytes, estimated_tokens, doc_type
|
|
17
|
+
summary: total_files, total_size_bytes, total_estimated_tokens
|
|
18
|
+
groups[]: group_key, files[] with role (primary/companion/standalone)
|
|
19
|
+
- Groups related docs by naming convention (e.g., brief + discovery-notes)
|
|
20
|
+
routing: recommendation ("single" | "fan-out"), reason
|
|
21
|
+
- single: ≤3 files AND ≤15K estimated tokens
|
|
22
|
+
- fan-out: >3 files OR >15K estimated tokens
|
|
23
|
+
split_prediction: prediction ("likely" | "unlikely"), reason, estimated_distillate_tokens
|
|
24
|
+
- Estimates distillate at ~1/3 source size; splits if >5K tokens
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
import argparse
|
|
30
|
+
import glob
|
|
31
|
+
import json
|
|
32
|
+
import os
|
|
33
|
+
import re
|
|
34
|
+
import sys
|
|
35
|
+
from pathlib import Path
|
|
36
|
+
|
|
37
|
+
# Extensions to include when scanning folders
|
|
38
|
+
INCLUDE_EXTENSIONS = {".md", ".txt", ".yaml", ".yml", ".json"}
|
|
39
|
+
|
|
40
|
+
# Directories to skip when scanning folders
|
|
41
|
+
SKIP_DIRS = {
|
|
42
|
+
"node_modules", ".git", "__pycache__", ".venv", "venv",
|
|
43
|
+
".claude", "_bmad-output", ".cursor", ".vscode",
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# Approximate chars per token for estimation
|
|
47
|
+
CHARS_PER_TOKEN = 4
|
|
48
|
+
|
|
49
|
+
# Thresholds
|
|
50
|
+
SINGLE_COMPRESSOR_MAX_TOKENS = 15_000
|
|
51
|
+
SINGLE_DISTILLATE_MAX_TOKENS = 5_000
|
|
52
|
+
|
|
53
|
+
# Naming patterns for document type detection
|
|
54
|
+
DOC_TYPE_PATTERNS = [
|
|
55
|
+
(r"discovery[_-]notes", "discovery-notes"),
|
|
56
|
+
(r"product[_-]brief", "product-brief"),
|
|
57
|
+
(r"research[_-]report", "research-report"),
|
|
58
|
+
(r"architecture", "architecture-doc"),
|
|
59
|
+
(r"prd", "prd"),
|
|
60
|
+
(r"distillate", "distillate"),
|
|
61
|
+
(r"changelog", "changelog"),
|
|
62
|
+
(r"readme", "readme"),
|
|
63
|
+
(r"spec", "specification"),
|
|
64
|
+
(r"requirements", "requirements"),
|
|
65
|
+
(r"design[_-]doc", "design-doc"),
|
|
66
|
+
(r"meeting[_-]notes", "meeting-notes"),
|
|
67
|
+
(r"brainstorm", "brainstorming"),
|
|
68
|
+
(r"interview", "interview-notes"),
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
# Patterns for grouping related documents
|
|
72
|
+
GROUP_PATTERNS = [
|
|
73
|
+
# base document + discovery notes
|
|
74
|
+
(r"^(.+?)(?:-discovery-notes|-discovery_notes)\.(\w+)$", r"\1.\2"),
|
|
75
|
+
# base document + appendix
|
|
76
|
+
(r"^(.+?)(?:-appendix|-addendum)(?:-\w+)?\.(\w+)$", r"\1.\2"),
|
|
77
|
+
# base document + review/feedback
|
|
78
|
+
(r"^(.+?)(?:-review|-feedback)\.(\w+)$", r"\1.\2"),
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def resolve_inputs(inputs: list[str]) -> list[Path]:
|
|
83
|
+
"""Resolve input arguments to a flat list of file paths."""
|
|
84
|
+
files: list[Path] = []
|
|
85
|
+
for inp in inputs:
|
|
86
|
+
path = Path(inp)
|
|
87
|
+
if path.is_file():
|
|
88
|
+
files.append(path.resolve())
|
|
89
|
+
elif path.is_dir():
|
|
90
|
+
for root, dirs, filenames in os.walk(path):
|
|
91
|
+
dirs[:] = [d for d in dirs if d not in SKIP_DIRS]
|
|
92
|
+
for fn in sorted(filenames):
|
|
93
|
+
fp = Path(root) / fn
|
|
94
|
+
if fp.suffix.lower() in INCLUDE_EXTENSIONS:
|
|
95
|
+
files.append(fp.resolve())
|
|
96
|
+
else:
|
|
97
|
+
# Try as glob
|
|
98
|
+
matches = glob.glob(inp, recursive=True)
|
|
99
|
+
for m in sorted(matches):
|
|
100
|
+
mp = Path(m)
|
|
101
|
+
if mp.is_file() and mp.suffix.lower() in INCLUDE_EXTENSIONS:
|
|
102
|
+
files.append(mp.resolve())
|
|
103
|
+
# Deduplicate while preserving order
|
|
104
|
+
seen: set[Path] = set()
|
|
105
|
+
deduped: list[Path] = []
|
|
106
|
+
for f in files:
|
|
107
|
+
if f not in seen:
|
|
108
|
+
seen.add(f)
|
|
109
|
+
deduped.append(f)
|
|
110
|
+
return deduped
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def detect_doc_type(filename: str) -> str:
|
|
114
|
+
"""Detect document type from filename."""
|
|
115
|
+
name_lower = filename.lower()
|
|
116
|
+
for pattern, doc_type in DOC_TYPE_PATTERNS:
|
|
117
|
+
if re.search(pattern, name_lower):
|
|
118
|
+
return doc_type
|
|
119
|
+
return "unknown"
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def suggest_groups(files: list[Path]) -> list[dict]:
|
|
123
|
+
"""Suggest document groupings based on naming conventions."""
|
|
124
|
+
groups: dict[str, list[dict]] = {}
|
|
125
|
+
ungrouped: list[dict] = []
|
|
126
|
+
|
|
127
|
+
file_map = {f.name: f for f in files}
|
|
128
|
+
|
|
129
|
+
assigned: set[str] = set()
|
|
130
|
+
|
|
131
|
+
for f in files:
|
|
132
|
+
if f.name in assigned:
|
|
133
|
+
continue
|
|
134
|
+
|
|
135
|
+
matched = False
|
|
136
|
+
for pattern, base_pattern in GROUP_PATTERNS:
|
|
137
|
+
m = re.match(pattern, f.name, re.IGNORECASE)
|
|
138
|
+
if m:
|
|
139
|
+
# This file is a companion — find its base
|
|
140
|
+
base_name = re.sub(pattern, base_pattern, f.name, flags=re.IGNORECASE)
|
|
141
|
+
group_key = base_name
|
|
142
|
+
if group_key not in groups:
|
|
143
|
+
groups[group_key] = []
|
|
144
|
+
# Add the base file if it exists
|
|
145
|
+
if base_name in file_map and base_name not in assigned:
|
|
146
|
+
groups[group_key].append({
|
|
147
|
+
"path": str(file_map[base_name]),
|
|
148
|
+
"filename": base_name,
|
|
149
|
+
"role": "primary",
|
|
150
|
+
})
|
|
151
|
+
assigned.add(base_name)
|
|
152
|
+
groups[group_key].append({
|
|
153
|
+
"path": str(f),
|
|
154
|
+
"filename": f.name,
|
|
155
|
+
"role": "companion",
|
|
156
|
+
})
|
|
157
|
+
assigned.add(f.name)
|
|
158
|
+
matched = True
|
|
159
|
+
break
|
|
160
|
+
|
|
161
|
+
if not matched:
|
|
162
|
+
# Check if this file is a base that already has companions
|
|
163
|
+
if f.name in groups:
|
|
164
|
+
continue # Already added as primary
|
|
165
|
+
ungrouped.append({
|
|
166
|
+
"path": str(f),
|
|
167
|
+
"filename": f.name,
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
result = []
|
|
171
|
+
for group_key, members in groups.items():
|
|
172
|
+
result.append({
|
|
173
|
+
"group_key": group_key,
|
|
174
|
+
"files": members,
|
|
175
|
+
})
|
|
176
|
+
for ug in ungrouped:
|
|
177
|
+
if ug["filename"] not in assigned:
|
|
178
|
+
result.append({
|
|
179
|
+
"group_key": ug["filename"],
|
|
180
|
+
"files": [{"path": ug["path"], "filename": ug["filename"], "role": "standalone"}],
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
return result
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def analyze(inputs: list[str], output_path: str | None = None) -> None:
|
|
187
|
+
"""Main analysis function."""
|
|
188
|
+
files = resolve_inputs(inputs)
|
|
189
|
+
|
|
190
|
+
if not files:
|
|
191
|
+
result = {
|
|
192
|
+
"status": "error",
|
|
193
|
+
"error": "No readable files found from provided inputs",
|
|
194
|
+
"inputs": inputs,
|
|
195
|
+
}
|
|
196
|
+
output_json(result, output_path)
|
|
197
|
+
return
|
|
198
|
+
|
|
199
|
+
# Analyze each file
|
|
200
|
+
file_details = []
|
|
201
|
+
total_chars = 0
|
|
202
|
+
for f in files:
|
|
203
|
+
size = f.stat().st_size
|
|
204
|
+
total_chars += size
|
|
205
|
+
file_details.append({
|
|
206
|
+
"path": str(f),
|
|
207
|
+
"filename": f.name,
|
|
208
|
+
"size_bytes": size,
|
|
209
|
+
"estimated_tokens": size // CHARS_PER_TOKEN,
|
|
210
|
+
"doc_type": detect_doc_type(f.name),
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
total_tokens = total_chars // CHARS_PER_TOKEN
|
|
214
|
+
groups = suggest_groups(files)
|
|
215
|
+
|
|
216
|
+
# Routing recommendation
|
|
217
|
+
if len(files) <= 3 and total_tokens <= SINGLE_COMPRESSOR_MAX_TOKENS:
|
|
218
|
+
routing = "single"
|
|
219
|
+
routing_reason = (
|
|
220
|
+
f"{len(files)} file(s), ~{total_tokens:,} estimated tokens — "
|
|
221
|
+
f"within single compressor threshold"
|
|
222
|
+
)
|
|
223
|
+
else:
|
|
224
|
+
routing = "fan-out"
|
|
225
|
+
routing_reason = (
|
|
226
|
+
f"{len(files)} file(s), ~{total_tokens:,} estimated tokens — "
|
|
227
|
+
f"exceeds single compressor threshold "
|
|
228
|
+
f"({'>' + str(SINGLE_COMPRESSOR_MAX_TOKENS) + ' tokens' if total_tokens > SINGLE_COMPRESSOR_MAX_TOKENS else '> 3 files'})"
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
# Split prediction
|
|
232
|
+
estimated_distillate_tokens = total_tokens // 3 # rough: distillate is ~1/3 of source
|
|
233
|
+
if estimated_distillate_tokens > SINGLE_DISTILLATE_MAX_TOKENS:
|
|
234
|
+
split_prediction = "likely"
|
|
235
|
+
split_reason = (
|
|
236
|
+
f"Estimated distillate ~{estimated_distillate_tokens:,} tokens "
|
|
237
|
+
f"exceeds {SINGLE_DISTILLATE_MAX_TOKENS:,} threshold"
|
|
238
|
+
)
|
|
239
|
+
else:
|
|
240
|
+
split_prediction = "unlikely"
|
|
241
|
+
split_reason = (
|
|
242
|
+
f"Estimated distillate ~{estimated_distillate_tokens:,} tokens "
|
|
243
|
+
f"within {SINGLE_DISTILLATE_MAX_TOKENS:,} threshold"
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
result = {
|
|
247
|
+
"status": "ok",
|
|
248
|
+
"files": file_details,
|
|
249
|
+
"summary": {
|
|
250
|
+
"total_files": len(files),
|
|
251
|
+
"total_size_bytes": total_chars,
|
|
252
|
+
"total_estimated_tokens": total_tokens,
|
|
253
|
+
},
|
|
254
|
+
"groups": groups,
|
|
255
|
+
"routing": {
|
|
256
|
+
"recommendation": routing,
|
|
257
|
+
"reason": routing_reason,
|
|
258
|
+
},
|
|
259
|
+
"split_prediction": {
|
|
260
|
+
"prediction": split_prediction,
|
|
261
|
+
"reason": split_reason,
|
|
262
|
+
"estimated_distillate_tokens": estimated_distillate_tokens,
|
|
263
|
+
},
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
output_json(result, output_path)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def output_json(data: dict, output_path: str | None) -> None:
|
|
270
|
+
"""Write JSON to file or stdout."""
|
|
271
|
+
json_str = json.dumps(data, indent=2)
|
|
272
|
+
if output_path:
|
|
273
|
+
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
|
|
274
|
+
Path(output_path).write_text(json_str + "\n")
|
|
275
|
+
print(f"Results written to {output_path}", file=sys.stderr)
|
|
276
|
+
else:
|
|
277
|
+
print(json_str)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def main() -> None:
|
|
281
|
+
parser = argparse.ArgumentParser(
|
|
282
|
+
description=__doc__,
|
|
283
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
284
|
+
)
|
|
285
|
+
parser.add_argument(
|
|
286
|
+
"inputs",
|
|
287
|
+
nargs="+",
|
|
288
|
+
help="File paths, folder paths, or glob patterns to analyze",
|
|
289
|
+
)
|
|
290
|
+
parser.add_argument(
|
|
291
|
+
"-o", "--output",
|
|
292
|
+
help="Output JSON to file instead of stdout",
|
|
293
|
+
)
|
|
294
|
+
args = parser.parse_args()
|
|
295
|
+
analyze(args.inputs, args.output)
|
|
296
|
+
sys.exit(0)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
if __name__ == "__main__":
|
|
300
|
+
main()
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"""Tests for analyze_sources.py"""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import tempfile
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from unittest.mock import patch
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
# Add parent dir to path so we can import the script
|
|
12
|
+
import sys
|
|
13
|
+
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
14
|
+
|
|
15
|
+
from analyze_sources import (
|
|
16
|
+
resolve_inputs,
|
|
17
|
+
detect_doc_type,
|
|
18
|
+
suggest_groups,
|
|
19
|
+
analyze,
|
|
20
|
+
INCLUDE_EXTENSIONS,
|
|
21
|
+
SKIP_DIRS,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@pytest.fixture
|
|
26
|
+
def temp_dir():
|
|
27
|
+
"""Create a temp directory with sample files."""
|
|
28
|
+
with tempfile.TemporaryDirectory() as d:
|
|
29
|
+
# Create sample files
|
|
30
|
+
(Path(d) / "product-brief-foo.md").write_text("# Product Brief\nContent here")
|
|
31
|
+
(Path(d) / "product-brief-foo-discovery-notes.md").write_text("# Discovery\nNotes")
|
|
32
|
+
(Path(d) / "architecture-doc.md").write_text("# Architecture\nDesign here")
|
|
33
|
+
(Path(d) / "research-report.md").write_text("# Research\nFindings")
|
|
34
|
+
(Path(d) / "random.txt").write_text("Some text content")
|
|
35
|
+
(Path(d) / "image.png").write_bytes(b"\x89PNG")
|
|
36
|
+
# Create a subdirectory with more files
|
|
37
|
+
sub = Path(d) / "subdir"
|
|
38
|
+
sub.mkdir()
|
|
39
|
+
(sub / "prd-v2.md").write_text("# PRD\nRequirements")
|
|
40
|
+
# Create a skip directory
|
|
41
|
+
skip = Path(d) / "node_modules"
|
|
42
|
+
skip.mkdir()
|
|
43
|
+
(skip / "junk.md").write_text("Should be skipped")
|
|
44
|
+
yield d
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class TestResolveInputs:
|
|
48
|
+
def test_single_file(self, temp_dir):
|
|
49
|
+
f = str(Path(temp_dir) / "product-brief-foo.md")
|
|
50
|
+
result = resolve_inputs([f])
|
|
51
|
+
assert len(result) == 1
|
|
52
|
+
assert result[0].name == "product-brief-foo.md"
|
|
53
|
+
|
|
54
|
+
def test_folder_recursion(self, temp_dir):
|
|
55
|
+
result = resolve_inputs([temp_dir])
|
|
56
|
+
names = {f.name for f in result}
|
|
57
|
+
assert "product-brief-foo.md" in names
|
|
58
|
+
assert "prd-v2.md" in names
|
|
59
|
+
assert "random.txt" in names
|
|
60
|
+
|
|
61
|
+
def test_folder_skips_excluded_dirs(self, temp_dir):
|
|
62
|
+
result = resolve_inputs([temp_dir])
|
|
63
|
+
names = {f.name for f in result}
|
|
64
|
+
assert "junk.md" not in names
|
|
65
|
+
|
|
66
|
+
def test_folder_skips_non_text_files(self, temp_dir):
|
|
67
|
+
result = resolve_inputs([temp_dir])
|
|
68
|
+
names = {f.name for f in result}
|
|
69
|
+
assert "image.png" not in names
|
|
70
|
+
|
|
71
|
+
def test_glob_pattern(self, temp_dir):
|
|
72
|
+
pattern = str(Path(temp_dir) / "product-brief-*.md")
|
|
73
|
+
result = resolve_inputs([pattern])
|
|
74
|
+
assert len(result) == 2
|
|
75
|
+
names = {f.name for f in result}
|
|
76
|
+
assert "product-brief-foo.md" in names
|
|
77
|
+
assert "product-brief-foo-discovery-notes.md" in names
|
|
78
|
+
|
|
79
|
+
def test_deduplication(self, temp_dir):
|
|
80
|
+
f = str(Path(temp_dir) / "product-brief-foo.md")
|
|
81
|
+
result = resolve_inputs([f, f, f])
|
|
82
|
+
assert len(result) == 1
|
|
83
|
+
|
|
84
|
+
def test_mixed_inputs(self, temp_dir):
|
|
85
|
+
file_path = str(Path(temp_dir) / "architecture-doc.md")
|
|
86
|
+
folder_path = str(Path(temp_dir) / "subdir")
|
|
87
|
+
result = resolve_inputs([file_path, folder_path])
|
|
88
|
+
names = {f.name for f in result}
|
|
89
|
+
assert "architecture-doc.md" in names
|
|
90
|
+
assert "prd-v2.md" in names
|
|
91
|
+
|
|
92
|
+
def test_nonexistent_path(self):
|
|
93
|
+
result = resolve_inputs(["/nonexistent/path/file.md"])
|
|
94
|
+
assert len(result) == 0
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class TestDetectDocType:
|
|
98
|
+
@pytest.mark.parametrize("filename,expected", [
|
|
99
|
+
("product-brief-foo.md", "product-brief"),
|
|
100
|
+
("product_brief_bar.md", "product-brief"),
|
|
101
|
+
("foo-discovery-notes.md", "discovery-notes"),
|
|
102
|
+
("foo-discovery_notes.md", "discovery-notes"),
|
|
103
|
+
("architecture-overview.md", "architecture-doc"),
|
|
104
|
+
("my-prd.md", "prd"),
|
|
105
|
+
("research-report-q4.md", "research-report"),
|
|
106
|
+
("foo-distillate.md", "distillate"),
|
|
107
|
+
("changelog.md", "changelog"),
|
|
108
|
+
("readme.md", "readme"),
|
|
109
|
+
("api-spec.md", "specification"),
|
|
110
|
+
("design-doc-v2.md", "design-doc"),
|
|
111
|
+
("meeting-notes-2026.md", "meeting-notes"),
|
|
112
|
+
("brainstorm-session.md", "brainstorming"),
|
|
113
|
+
("user-interview-notes.md", "interview-notes"),
|
|
114
|
+
("random-file.md", "unknown"),
|
|
115
|
+
])
|
|
116
|
+
def test_detection(self, filename, expected):
|
|
117
|
+
assert detect_doc_type(filename) == expected
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class TestSuggestGroups:
|
|
121
|
+
def test_groups_brief_with_discovery_notes(self, temp_dir):
|
|
122
|
+
files = [
|
|
123
|
+
Path(temp_dir) / "product-brief-foo.md",
|
|
124
|
+
Path(temp_dir) / "product-brief-foo-discovery-notes.md",
|
|
125
|
+
]
|
|
126
|
+
groups = suggest_groups(files)
|
|
127
|
+
# Should produce one group with both files
|
|
128
|
+
paired = [g for g in groups if len(g["files"]) > 1]
|
|
129
|
+
assert len(paired) == 1
|
|
130
|
+
filenames = {f["filename"] for f in paired[0]["files"]}
|
|
131
|
+
assert "product-brief-foo.md" in filenames
|
|
132
|
+
assert "product-brief-foo-discovery-notes.md" in filenames
|
|
133
|
+
|
|
134
|
+
def test_standalone_files(self, temp_dir):
|
|
135
|
+
files = [
|
|
136
|
+
Path(temp_dir) / "architecture-doc.md",
|
|
137
|
+
Path(temp_dir) / "research-report.md",
|
|
138
|
+
]
|
|
139
|
+
groups = suggest_groups(files)
|
|
140
|
+
assert len(groups) == 2
|
|
141
|
+
for g in groups:
|
|
142
|
+
assert len(g["files"]) == 1
|
|
143
|
+
|
|
144
|
+
def test_mixed_grouped_and_standalone(self, temp_dir):
|
|
145
|
+
files = [
|
|
146
|
+
Path(temp_dir) / "product-brief-foo.md",
|
|
147
|
+
Path(temp_dir) / "product-brief-foo-discovery-notes.md",
|
|
148
|
+
Path(temp_dir) / "architecture-doc.md",
|
|
149
|
+
]
|
|
150
|
+
groups = suggest_groups(files)
|
|
151
|
+
paired = [g for g in groups if len(g["files"]) > 1]
|
|
152
|
+
standalone = [g for g in groups if len(g["files"]) == 1]
|
|
153
|
+
assert len(paired) == 1
|
|
154
|
+
assert len(standalone) == 1
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class TestAnalyze:
|
|
158
|
+
def test_basic_analysis(self, temp_dir):
|
|
159
|
+
f = str(Path(temp_dir) / "product-brief-foo.md")
|
|
160
|
+
output_file = str(Path(temp_dir) / "output.json")
|
|
161
|
+
analyze([f], output_file)
|
|
162
|
+
result = json.loads(Path(output_file).read_text())
|
|
163
|
+
assert result["status"] == "ok"
|
|
164
|
+
assert result["summary"]["total_files"] == 1
|
|
165
|
+
assert result["files"][0]["doc_type"] == "product-brief"
|
|
166
|
+
assert result["files"][0]["estimated_tokens"] > 0
|
|
167
|
+
|
|
168
|
+
def test_routing_single_small_input(self, temp_dir):
|
|
169
|
+
f = str(Path(temp_dir) / "product-brief-foo.md")
|
|
170
|
+
output_file = str(Path(temp_dir) / "output.json")
|
|
171
|
+
analyze([f], output_file)
|
|
172
|
+
result = json.loads(Path(output_file).read_text())
|
|
173
|
+
assert result["routing"]["recommendation"] == "single"
|
|
174
|
+
|
|
175
|
+
def test_routing_fanout_many_files(self, temp_dir):
|
|
176
|
+
# Create enough files to trigger fan-out (> 3 files)
|
|
177
|
+
for i in range(5):
|
|
178
|
+
(Path(temp_dir) / f"doc-{i}.md").write_text("x" * 1000)
|
|
179
|
+
output_file = str(Path(temp_dir) / "output.json")
|
|
180
|
+
analyze([temp_dir], output_file)
|
|
181
|
+
result = json.loads(Path(output_file).read_text())
|
|
182
|
+
assert result["routing"]["recommendation"] == "fan-out"
|
|
183
|
+
|
|
184
|
+
def test_folder_analysis(self, temp_dir):
|
|
185
|
+
output_file = str(Path(temp_dir) / "output.json")
|
|
186
|
+
analyze([temp_dir], output_file)
|
|
187
|
+
result = json.loads(Path(output_file).read_text())
|
|
188
|
+
assert result["status"] == "ok"
|
|
189
|
+
assert result["summary"]["total_files"] >= 4 # at least the base files
|
|
190
|
+
assert len(result["groups"]) > 0
|
|
191
|
+
|
|
192
|
+
def test_no_files_found(self):
|
|
193
|
+
output_file = "/tmp/test_analyze_empty.json"
|
|
194
|
+
analyze(["/nonexistent/path"], output_file)
|
|
195
|
+
result = json.loads(Path(output_file).read_text())
|
|
196
|
+
assert result["status"] == "error"
|
|
197
|
+
os.unlink(output_file)
|
|
198
|
+
|
|
199
|
+
def test_stdout_output(self, temp_dir, capsys):
|
|
200
|
+
f = str(Path(temp_dir) / "product-brief-foo.md")
|
|
201
|
+
analyze([f])
|
|
202
|
+
captured = capsys.readouterr()
|
|
203
|
+
result = json.loads(captured.out)
|
|
204
|
+
assert result["status"] == "ok"
|