@pennyfarthing/core 6.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +293 -0
- package/package.json +58 -0
- package/packages/core/bin/pennyfarthing.js +14 -0
- package/packages/core/dist/bmad/context-reader.d.ts +71 -0
- package/packages/core/dist/bmad/context-reader.d.ts.map +1 -0
- package/packages/core/dist/bmad/context-reader.js +369 -0
- package/packages/core/dist/bmad/context-reader.js.map +1 -0
- package/packages/core/dist/bmad/context-reader.test.d.ts +71 -0
- package/packages/core/dist/bmad/context-reader.test.d.ts.map +1 -0
- package/packages/core/dist/bmad/context-reader.test.js +878 -0
- package/packages/core/dist/bmad/context-reader.test.js.map +1 -0
- package/packages/core/dist/bmad/epics-parser.d.ts +61 -0
- package/packages/core/dist/bmad/epics-parser.d.ts.map +1 -0
- package/packages/core/dist/bmad/epics-parser.js +331 -0
- package/packages/core/dist/bmad/epics-parser.js.map +1 -0
- package/packages/core/dist/bmad/epics-parser.test.d.ts +7 -0
- package/packages/core/dist/bmad/epics-parser.test.d.ts.map +1 -0
- package/packages/core/dist/bmad/epics-parser.test.js +449 -0
- package/packages/core/dist/bmad/epics-parser.test.js.map +1 -0
- package/packages/core/dist/bmad/index.d.ts +11 -0
- package/packages/core/dist/bmad/index.d.ts.map +1 -0
- package/packages/core/dist/bmad/index.js +24 -0
- package/packages/core/dist/bmad/index.js.map +1 -0
- package/packages/core/dist/bmad/status-sync.d.ts +173 -0
- package/packages/core/dist/bmad/status-sync.d.ts.map +1 -0
- package/packages/core/dist/bmad/status-sync.js +463 -0
- package/packages/core/dist/bmad/status-sync.js.map +1 -0
- package/packages/core/dist/bmad/status-sync.test.d.ts +7 -0
- package/packages/core/dist/bmad/status-sync.test.d.ts.map +1 -0
- package/packages/core/dist/bmad/status-sync.test.js +702 -0
- package/packages/core/dist/bmad/status-sync.test.js.map +1 -0
- package/packages/core/dist/bmad/story-exporter.d.ts +55 -0
- package/packages/core/dist/bmad/story-exporter.d.ts.map +1 -0
- package/packages/core/dist/bmad/story-exporter.js +170 -0
- package/packages/core/dist/bmad/story-exporter.js.map +1 -0
- package/packages/core/dist/bmad/story-exporter.test.d.ts +51 -0
- package/packages/core/dist/bmad/story-exporter.test.d.ts.map +1 -0
- package/packages/core/dist/bmad/story-exporter.test.js +603 -0
- package/packages/core/dist/bmad/story-exporter.test.js.map +1 -0
- package/packages/core/dist/bmad/story-parser.d.ts +44 -0
- package/packages/core/dist/bmad/story-parser.d.ts.map +1 -0
- package/packages/core/dist/bmad/story-parser.js +307 -0
- package/packages/core/dist/bmad/story-parser.js.map +1 -0
- package/packages/core/dist/bmad/story-parser.test.d.ts +44 -0
- package/packages/core/dist/bmad/story-parser.test.d.ts.map +1 -0
- package/packages/core/dist/bmad/story-parser.test.js +693 -0
- package/packages/core/dist/bmad/story-parser.test.js.map +1 -0
- package/packages/core/dist/cli/commands/command.d.ts +28 -0
- package/packages/core/dist/cli/commands/command.d.ts.map +1 -0
- package/packages/core/dist/cli/commands/command.js +399 -0
- package/packages/core/dist/cli/commands/command.js.map +1 -0
- package/packages/core/dist/cli/commands/cyclist.d.ts +46 -0
- package/packages/core/dist/cli/commands/cyclist.d.ts.map +1 -0
- package/packages/core/dist/cli/commands/cyclist.js +191 -0
- package/packages/core/dist/cli/commands/cyclist.js.map +1 -0
- package/packages/core/dist/cli/commands/cyclist.test.d.ts +13 -0
- package/packages/core/dist/cli/commands/cyclist.test.d.ts.map +1 -0
- package/packages/core/dist/cli/commands/cyclist.test.js +243 -0
- package/packages/core/dist/cli/commands/cyclist.test.js.map +1 -0
- package/packages/core/dist/cli/commands/doctor.d.ts +9 -0
- package/packages/core/dist/cli/commands/doctor.d.ts.map +1 -0
- package/packages/core/dist/cli/commands/doctor.js +652 -0
- package/packages/core/dist/cli/commands/doctor.js.map +1 -0
- package/packages/core/dist/cli/commands/init.d.ts +8 -0
- package/packages/core/dist/cli/commands/init.d.ts.map +1 -0
- package/packages/core/dist/cli/commands/init.js +524 -0
- package/packages/core/dist/cli/commands/init.js.map +1 -0
- package/packages/core/dist/cli/commands/skill.d.ts +28 -0
- package/packages/core/dist/cli/commands/skill.d.ts.map +1 -0
- package/packages/core/dist/cli/commands/skill.js +416 -0
- package/packages/core/dist/cli/commands/skill.js.map +1 -0
- package/packages/core/dist/cli/commands/theme.d.ts +21 -0
- package/packages/core/dist/cli/commands/theme.d.ts.map +1 -0
- package/packages/core/dist/cli/commands/theme.js +201 -0
- package/packages/core/dist/cli/commands/theme.js.map +1 -0
- package/packages/core/dist/cli/commands/uninstall.d.ts +8 -0
- package/packages/core/dist/cli/commands/uninstall.d.ts.map +1 -0
- package/packages/core/dist/cli/commands/uninstall.js +237 -0
- package/packages/core/dist/cli/commands/uninstall.js.map +1 -0
- package/packages/core/dist/cli/commands/update.d.ts +9 -0
- package/packages/core/dist/cli/commands/update.d.ts.map +1 -0
- package/packages/core/dist/cli/commands/update.js +545 -0
- package/packages/core/dist/cli/commands/update.js.map +1 -0
- package/packages/core/dist/cli/commands/version.d.ts +2 -0
- package/packages/core/dist/cli/commands/version.d.ts.map +1 -0
- package/packages/core/dist/cli/commands/version.js +28 -0
- package/packages/core/dist/cli/commands/version.js.map +1 -0
- package/packages/core/dist/cli/customization.test.d.ts +12 -0
- package/packages/core/dist/cli/customization.test.d.ts.map +1 -0
- package/packages/core/dist/cli/customization.test.js +84 -0
- package/packages/core/dist/cli/customization.test.js.map +1 -0
- package/packages/core/dist/cli/cyclist-migration.test.d.ts +16 -0
- package/packages/core/dist/cli/cyclist-migration.test.d.ts.map +1 -0
- package/packages/core/dist/cli/cyclist-migration.test.js +224 -0
- package/packages/core/dist/cli/cyclist-migration.test.js.map +1 -0
- package/packages/core/dist/cli/index.d.ts +3 -0
- package/packages/core/dist/cli/index.d.ts.map +1 -0
- package/packages/core/dist/cli/index.js +174 -0
- package/packages/core/dist/cli/index.js.map +1 -0
- package/packages/core/dist/cli/ocean-profiles.test.d.ts +13 -0
- package/packages/core/dist/cli/ocean-profiles.test.d.ts.map +1 -0
- package/packages/core/dist/cli/ocean-profiles.test.js +134 -0
- package/packages/core/dist/cli/ocean-profiles.test.js.map +1 -0
- package/packages/core/dist/cli/theme-maker.test.d.ts +11 -0
- package/packages/core/dist/cli/theme-maker.test.d.ts.map +1 -0
- package/packages/core/dist/cli/theme-maker.test.js +356 -0
- package/packages/core/dist/cli/theme-maker.test.js.map +1 -0
- package/packages/core/dist/cli/utils/constants.d.ts +60 -0
- package/packages/core/dist/cli/utils/constants.d.ts.map +1 -0
- package/packages/core/dist/cli/utils/constants.js +52 -0
- package/packages/core/dist/cli/utils/constants.js.map +1 -0
- package/packages/core/dist/cli/utils/files.d.ts +71 -0
- package/packages/core/dist/cli/utils/files.d.ts.map +1 -0
- package/packages/core/dist/cli/utils/files.js +162 -0
- package/packages/core/dist/cli/utils/files.js.map +1 -0
- package/packages/core/dist/cli/utils/logger.d.ts +26 -0
- package/packages/core/dist/cli/utils/logger.d.ts.map +1 -0
- package/packages/core/dist/cli/utils/logger.js +88 -0
- package/packages/core/dist/cli/utils/logger.js.map +1 -0
- package/packages/core/dist/cli/utils/manifest.d.ts +47 -0
- package/packages/core/dist/cli/utils/manifest.d.ts.map +1 -0
- package/packages/core/dist/cli/utils/manifest.js +90 -0
- package/packages/core/dist/cli/utils/manifest.js.map +1 -0
- package/packages/core/dist/cli/utils/node-modules.d.ts +6 -0
- package/packages/core/dist/cli/utils/node-modules.d.ts.map +1 -0
- package/packages/core/dist/cli/utils/node-modules.js +22 -0
- package/packages/core/dist/cli/utils/node-modules.js.map +1 -0
- package/packages/core/dist/cli/utils/prompts.d.ts +34 -0
- package/packages/core/dist/cli/utils/prompts.d.ts.map +1 -0
- package/packages/core/dist/cli/utils/prompts.js +93 -0
- package/packages/core/dist/cli/utils/prompts.js.map +1 -0
- package/packages/core/dist/cli/utils/symlinks.d.ts +29 -0
- package/packages/core/dist/cli/utils/symlinks.d.ts.map +1 -0
- package/packages/core/dist/cli/utils/symlinks.js +181 -0
- package/packages/core/dist/cli/utils/symlinks.js.map +1 -0
- package/packages/core/dist/cli/utils/themes.d.ts +104 -0
- package/packages/core/dist/cli/utils/themes.d.ts.map +1 -0
- package/packages/core/dist/cli/utils/themes.js +393 -0
- package/packages/core/dist/cli/utils/themes.js.map +1 -0
- package/packages/core/dist/cli/utils/themes.test.d.ts +12 -0
- package/packages/core/dist/cli/utils/themes.test.d.ts.map +1 -0
- package/packages/core/dist/cli/utils/themes.test.js +144 -0
- package/packages/core/dist/cli/utils/themes.test.js.map +1 -0
- package/packages/core/dist/cli/utils/version.d.ts +10 -0
- package/packages/core/dist/cli/utils/version.d.ts.map +1 -0
- package/packages/core/dist/cli/utils/version.js +63 -0
- package/packages/core/dist/cli/utils/version.js.map +1 -0
- package/packages/core/dist/cli/workspace.test.d.ts +8 -0
- package/packages/core/dist/cli/workspace.test.d.ts.map +1 -0
- package/packages/core/dist/cli/workspace.test.js +154 -0
- package/packages/core/dist/cli/workspace.test.js.map +1 -0
- package/packages/core/dist/index.d.ts +6 -0
- package/packages/core/dist/index.d.ts.map +1 -0
- package/packages/core/dist/index.js +10 -0
- package/packages/core/dist/index.js.map +1 -0
- package/packages/core/dist/permissions/index.d.ts +9 -0
- package/packages/core/dist/permissions/index.d.ts.map +1 -0
- package/packages/core/dist/permissions/index.js +13 -0
- package/packages/core/dist/permissions/index.js.map +1 -0
- package/packages/core/dist/permissions/permission-schema.d.ts +89 -0
- package/packages/core/dist/permissions/permission-schema.d.ts.map +1 -0
- package/packages/core/dist/permissions/permission-schema.js +120 -0
- package/packages/core/dist/permissions/permission-schema.js.map +1 -0
- package/packages/core/dist/permissions/permission-schema.test.d.ts +40 -0
- package/packages/core/dist/permissions/permission-schema.test.d.ts.map +1 -0
- package/packages/core/dist/permissions/permission-schema.test.js +367 -0
- package/packages/core/dist/permissions/permission-schema.test.js.map +1 -0
- package/packages/core/dist/scripts/add-ocean-profiles.d.ts +9 -0
- package/packages/core/dist/scripts/add-ocean-profiles.d.ts.map +1 -0
- package/packages/core/dist/scripts/add-ocean-profiles.js +695 -0
- package/packages/core/dist/scripts/add-ocean-profiles.js.map +1 -0
- package/packages/core/dist/scripts/benchmark-integration.d.ts +182 -0
- package/packages/core/dist/scripts/benchmark-integration.d.ts.map +1 -0
- package/packages/core/dist/scripts/benchmark-integration.js +691 -0
- package/packages/core/dist/scripts/benchmark-integration.js.map +1 -0
- package/packages/core/dist/scripts/benchmark-integration.test.d.ts +13 -0
- package/packages/core/dist/scripts/benchmark-integration.test.d.ts.map +1 -0
- package/packages/core/dist/scripts/benchmark-integration.test.js +680 -0
- package/packages/core/dist/scripts/benchmark-integration.test.js.map +1 -0
- package/packages/core/dist/scripts/debugging-scenarios.test.d.ts +18 -0
- package/packages/core/dist/scripts/debugging-scenarios.test.d.ts.map +1 -0
- package/packages/core/dist/scripts/debugging-scenarios.test.js +317 -0
- package/packages/core/dist/scripts/debugging-scenarios.test.js.map +1 -0
- package/packages/core/dist/scripts/generate-all-faces.d.ts +10 -0
- package/packages/core/dist/scripts/generate-all-faces.d.ts.map +1 -0
- package/packages/core/dist/scripts/generate-all-faces.js +256 -0
- package/packages/core/dist/scripts/generate-all-faces.js.map +1 -0
- package/packages/core/dist/scripts/generate-all-faces.test.d.ts +17 -0
- package/packages/core/dist/scripts/generate-all-faces.test.d.ts.map +1 -0
- package/packages/core/dist/scripts/generate-all-faces.test.js +372 -0
- package/packages/core/dist/scripts/generate-all-faces.test.js.map +1 -0
- package/packages/core/dist/scripts/generate-all-spiders.d.ts +10 -0
- package/packages/core/dist/scripts/generate-all-spiders.d.ts.map +1 -0
- package/packages/core/dist/scripts/generate-all-spiders.js +306 -0
- package/packages/core/dist/scripts/generate-all-spiders.js.map +1 -0
- package/packages/core/dist/scripts/generate-ascii-face.d.ts +52 -0
- package/packages/core/dist/scripts/generate-ascii-face.d.ts.map +1 -0
- package/packages/core/dist/scripts/generate-ascii-face.js +155 -0
- package/packages/core/dist/scripts/generate-ascii-face.js.map +1 -0
- package/packages/core/dist/scripts/generate-face.d.ts +52 -0
- package/packages/core/dist/scripts/generate-face.d.ts.map +1 -0
- package/packages/core/dist/scripts/generate-face.js +199 -0
- package/packages/core/dist/scripts/generate-face.js.map +1 -0
- package/packages/core/dist/scripts/generate-face.test.d.ts +13 -0
- package/packages/core/dist/scripts/generate-face.test.d.ts.map +1 -0
- package/packages/core/dist/scripts/generate-face.test.js +301 -0
- package/packages/core/dist/scripts/generate-face.test.js.map +1 -0
- package/packages/core/dist/scripts/generate-report.d.ts +65 -0
- package/packages/core/dist/scripts/generate-report.d.ts.map +1 -0
- package/packages/core/dist/scripts/generate-report.js +378 -0
- package/packages/core/dist/scripts/generate-report.js.map +1 -0
- package/packages/core/dist/scripts/generate-report.test.d.ts +13 -0
- package/packages/core/dist/scripts/generate-report.test.d.ts.map +1 -0
- package/packages/core/dist/scripts/generate-report.test.js +363 -0
- package/packages/core/dist/scripts/generate-report.test.js.map +1 -0
- package/packages/core/dist/scripts/generate-spider-report.d.ts +65 -0
- package/packages/core/dist/scripts/generate-spider-report.d.ts.map +1 -0
- package/packages/core/dist/scripts/generate-spider-report.js +366 -0
- package/packages/core/dist/scripts/generate-spider-report.js.map +1 -0
- package/packages/core/dist/scripts/generate-spider-report.test.d.ts +13 -0
- package/packages/core/dist/scripts/generate-spider-report.test.d.ts.map +1 -0
- package/packages/core/dist/scripts/generate-spider-report.test.js +367 -0
- package/packages/core/dist/scripts/generate-spider-report.test.js.map +1 -0
- package/packages/core/dist/scripts/generate-spider.d.ts +37 -0
- package/packages/core/dist/scripts/generate-spider.d.ts.map +1 -0
- package/packages/core/dist/scripts/generate-spider.js +315 -0
- package/packages/core/dist/scripts/generate-spider.js.map +1 -0
- package/packages/core/dist/scripts/generate-spider.test.d.ts +14 -0
- package/packages/core/dist/scripts/generate-spider.test.d.ts.map +1 -0
- package/packages/core/dist/scripts/generate-spider.test.js +269 -0
- package/packages/core/dist/scripts/generate-spider.test.js.map +1 -0
- package/packages/core/dist/scripts/job-fair-aggregator.d.ts +150 -0
- package/packages/core/dist/scripts/job-fair-aggregator.d.ts.map +1 -0
- package/packages/core/dist/scripts/job-fair-aggregator.js +547 -0
- package/packages/core/dist/scripts/job-fair-aggregator.js.map +1 -0
- package/packages/core/dist/scripts/job-fair-aggregator.test.d.ts +14 -0
- package/packages/core/dist/scripts/job-fair-aggregator.test.d.ts.map +1 -0
- package/packages/core/dist/scripts/job-fair-aggregator.test.js +616 -0
- package/packages/core/dist/scripts/job-fair-aggregator.test.js.map +1 -0
- package/packages/core/dist/scripts/run-ci.test.d.ts +20 -0
- package/packages/core/dist/scripts/run-ci.test.d.ts.map +1 -0
- package/packages/core/dist/scripts/run-ci.test.js +127 -0
- package/packages/core/dist/scripts/run-ci.test.js.map +1 -0
- package/packages/core/dist/scripts/theme-detail.test.d.ts +10 -0
- package/packages/core/dist/scripts/theme-detail.test.d.ts.map +1 -0
- package/packages/core/dist/scripts/theme-detail.test.js +199 -0
- package/packages/core/dist/scripts/theme-detail.test.js.map +1 -0
- package/packages/core/dist/scripts/validate-ocean-profiles.d.ts +9 -0
- package/packages/core/dist/scripts/validate-ocean-profiles.d.ts.map +1 -0
- package/packages/core/dist/scripts/validate-ocean-profiles.js +130 -0
- package/packages/core/dist/scripts/validate-ocean-profiles.js.map +1 -0
- package/packages/core/dist/workflow/generic-handoff.d.ts +235 -0
- package/packages/core/dist/workflow/generic-handoff.d.ts.map +1 -0
- package/packages/core/dist/workflow/generic-handoff.js +358 -0
- package/packages/core/dist/workflow/generic-handoff.js.map +1 -0
- package/packages/core/dist/workflow/generic-handoff.test.d.ts +21 -0
- package/packages/core/dist/workflow/generic-handoff.test.d.ts.map +1 -0
- package/packages/core/dist/workflow/generic-handoff.test.js +499 -0
- package/packages/core/dist/workflow/generic-handoff.test.js.map +1 -0
- package/packages/core/dist/workflow/generic-sm-finish.d.ts +89 -0
- package/packages/core/dist/workflow/generic-sm-finish.d.ts.map +1 -0
- package/packages/core/dist/workflow/generic-sm-finish.js +157 -0
- package/packages/core/dist/workflow/generic-sm-finish.js.map +1 -0
- package/packages/core/dist/workflow/generic-sm-setup.d.ts +138 -0
- package/packages/core/dist/workflow/generic-sm-setup.d.ts.map +1 -0
- package/packages/core/dist/workflow/generic-sm-setup.js +382 -0
- package/packages/core/dist/workflow/generic-sm-setup.js.map +1 -0
- package/packages/core/dist/workflow/sm-subagents.test.d.ts +23 -0
- package/packages/core/dist/workflow/sm-subagents.test.d.ts.map +1 -0
- package/packages/core/dist/workflow/sm-subagents.test.js +727 -0
- package/packages/core/dist/workflow/sm-subagents.test.js.map +1 -0
- package/packages/core/dist/workflow/story-workflow-routing.test.d.ts +17 -0
- package/packages/core/dist/workflow/story-workflow-routing.test.d.ts.map +1 -0
- package/packages/core/dist/workflow/story-workflow-routing.test.js +559 -0
- package/packages/core/dist/workflow/story-workflow-routing.test.js.map +1 -0
- package/packages/core/dist/workflow/test-cache.d.ts +131 -0
- package/packages/core/dist/workflow/test-cache.d.ts.map +1 -0
- package/packages/core/dist/workflow/test-cache.js +226 -0
- package/packages/core/dist/workflow/test-cache.js.map +1 -0
- package/packages/core/dist/workflow/test-cache.test.d.ts +17 -0
- package/packages/core/dist/workflow/test-cache.test.d.ts.map +1 -0
- package/packages/core/dist/workflow/test-cache.test.js +438 -0
- package/packages/core/dist/workflow/test-cache.test.js.map +1 -0
- package/packages/core/dist/workflow/workflow-loader.d.ts +76 -0
- package/packages/core/dist/workflow/workflow-loader.d.ts.map +1 -0
- package/packages/core/dist/workflow/workflow-loader.js +133 -0
- package/packages/core/dist/workflow/workflow-loader.js.map +1 -0
- package/packages/core/dist/workflow/workflow-loader.test.d.ts +15 -0
- package/packages/core/dist/workflow/workflow-loader.test.d.ts.map +1 -0
- package/packages/core/dist/workflow/workflow-loader.test.js +354 -0
- package/packages/core/dist/workflow/workflow-loader.test.js.map +1 -0
- package/packages/core/dist/workflow/workflow-migration.test.d.ts +17 -0
- package/packages/core/dist/workflow/workflow-migration.test.d.ts.map +1 -0
- package/packages/core/dist/workflow/workflow-migration.test.js +372 -0
- package/packages/core/dist/workflow/workflow-migration.test.js.map +1 -0
- package/packages/core/dist/workflow/workflow-router.d.ts +55 -0
- package/packages/core/dist/workflow/workflow-router.d.ts.map +1 -0
- package/packages/core/dist/workflow/workflow-router.js +245 -0
- package/packages/core/dist/workflow/workflow-router.js.map +1 -0
- package/packages/core/dist/workflow/workflow-router.test.d.ts +20 -0
- package/packages/core/dist/workflow/workflow-router.test.d.ts.map +1 -0
- package/packages/core/dist/workflow/workflow-router.test.js +607 -0
- package/packages/core/dist/workflow/workflow-router.test.js.map +1 -0
- package/packages/core/dist/workflow/workflow-schema.d.ts +98 -0
- package/packages/core/dist/workflow/workflow-schema.d.ts.map +1 -0
- package/packages/core/dist/workflow/workflow-schema.js +230 -0
- package/packages/core/dist/workflow/workflow-schema.js.map +1 -0
- package/packages/core/dist/workflow/workflow-schema.test.d.ts +45 -0
- package/packages/core/dist/workflow/workflow-schema.test.d.ts.map +1 -0
- package/packages/core/dist/workflow/workflow-schema.test.js +512 -0
- package/packages/core/dist/workflow/workflow-schema.test.js.map +1 -0
- package/pennyfarthing-dist/agents/README.md +397 -0
- package/pennyfarthing-dist/agents/architect.md +171 -0
- package/pennyfarthing-dist/agents/dev.md +225 -0
- package/pennyfarthing-dist/agents/devops.md +183 -0
- package/pennyfarthing-dist/agents/generic-handoff.md +451 -0
- package/pennyfarthing-dist/agents/generic-sm-finish.md +261 -0
- package/pennyfarthing-dist/agents/generic-sm-setup.md +214 -0
- package/pennyfarthing-dist/agents/orchestrator.md +316 -0
- package/pennyfarthing-dist/agents/pm.md +153 -0
- package/pennyfarthing-dist/agents/reviewer-preflight.md +224 -0
- package/pennyfarthing-dist/agents/reviewer.md +315 -0
- package/pennyfarthing-dist/agents/sm-file-summary.md +109 -0
- package/pennyfarthing-dist/agents/sm-handoff.md +97 -0
- package/pennyfarthing-dist/agents/sm.md +480 -0
- package/pennyfarthing-dist/agents/tea.md +191 -0
- package/pennyfarthing-dist/agents/tech-writer.md +148 -0
- package/pennyfarthing-dist/agents/testing-runner.md +420 -0
- package/pennyfarthing-dist/agents/ux-designer.md +158 -0
- package/pennyfarthing-dist/agents/workflow-status-check.md +332 -0
- package/pennyfarthing-dist/commands/architect.md +62 -0
- package/pennyfarthing-dist/commands/benchmark-control.md +69 -0
- package/pennyfarthing-dist/commands/benchmark.md +467 -0
- package/pennyfarthing-dist/commands/brainstorm.md +91 -0
- package/pennyfarthing-dist/commands/check.md +156 -0
- package/pennyfarthing-dist/commands/chore.md +178 -0
- package/pennyfarthing-dist/commands/close-epic.md +136 -0
- package/pennyfarthing-dist/commands/continue-session.md +184 -0
- package/pennyfarthing-dist/commands/create-branches-from-story.md +374 -0
- package/pennyfarthing-dist/commands/create-theme.md +29 -0
- package/pennyfarthing-dist/commands/dev.md +60 -0
- package/pennyfarthing-dist/commands/devops.md +59 -0
- package/pennyfarthing-dist/commands/git-cleanup.md +340 -0
- package/pennyfarthing-dist/commands/health-check.md +108 -0
- package/pennyfarthing-dist/commands/help.md +264 -0
- package/pennyfarthing-dist/commands/job-fair.md +102 -0
- package/pennyfarthing-dist/commands/list-themes.md +17 -0
- package/pennyfarthing-dist/commands/new-work.md +127 -0
- package/pennyfarthing-dist/commands/orchestrator.md +56 -0
- package/pennyfarthing-dist/commands/parallel-work.md +71 -0
- package/pennyfarthing-dist/commands/party-mode.md +67 -0
- package/pennyfarthing-dist/commands/permissions.md +193 -0
- package/pennyfarthing-dist/commands/pm.md +60 -0
- package/pennyfarthing-dist/commands/prime.md +140 -0
- package/pennyfarthing-dist/commands/release.md +58 -0
- package/pennyfarthing-dist/commands/repo-status.md +49 -0
- package/pennyfarthing-dist/commands/retro.md +200 -0
- package/pennyfarthing-dist/commands/reviewer.md +64 -0
- package/pennyfarthing-dist/commands/run-ci.md +116 -0
- package/pennyfarthing-dist/commands/set-theme.md +52 -0
- package/pennyfarthing-dist/commands/show-theme.md +21 -0
- package/pennyfarthing-dist/commands/sm.md +70 -0
- package/pennyfarthing-dist/commands/solo.md +411 -0
- package/pennyfarthing-dist/commands/sprint-planning.md +109 -0
- package/pennyfarthing-dist/commands/start-epic.md +156 -0
- package/pennyfarthing-dist/commands/sync-epic-to-jira.md +184 -0
- package/pennyfarthing-dist/commands/sync-work-with-sprint.md +376 -0
- package/pennyfarthing-dist/commands/tea.md +63 -0
- package/pennyfarthing-dist/commands/tech-writer.md +53 -0
- package/pennyfarthing-dist/commands/theme-maker.md +671 -0
- package/pennyfarthing-dist/commands/update-domain-docs.md +83 -0
- package/pennyfarthing-dist/commands/ux-designer.md +62 -0
- package/pennyfarthing-dist/commands/work.md +111 -0
- package/pennyfarthing-dist/guides/AGENT-COORDINATION.md +480 -0
- package/pennyfarthing-dist/guides/AGENT-SCOPES.md +201 -0
- package/pennyfarthing-dist/guides/HOOKS.md +230 -0
- package/pennyfarthing-dist/guides/PROMPT-PATTERNS.md +338 -0
- package/pennyfarthing-dist/guides/SESSION-ARTIFACTS.md +193 -0
- package/pennyfarthing-dist/guides/agent-template-strategic.md +148 -0
- package/pennyfarthing-dist/guides/agent-template-tactical.md +162 -0
- package/pennyfarthing-dist/guides/patterns/approval-gates-pattern.md +746 -0
- package/pennyfarthing-dist/guides/patterns/fan-out-fan-in-pattern.md +574 -0
- package/pennyfarthing-dist/guides/patterns/helper-delegation-pattern.md +488 -0
- package/pennyfarthing-dist/guides/patterns/tdd-flow-pattern.md +402 -0
- package/pennyfarthing-dist/guides/permission-protocol.md +188 -0
- package/pennyfarthing-dist/guides/persona-loading.md +46 -0
- package/pennyfarthing-dist/guides/persona-system.md +294 -0
- package/pennyfarthing-dist/guides/shared-agent-behavior.md +388 -0
- package/pennyfarthing-dist/guides/shared-context.md +147 -0
- package/pennyfarthing-dist/guides/strategic-agent-behavior.md +348 -0
- package/pennyfarthing-dist/guides/tactical-agent-behavior.md +1041 -0
- package/pennyfarthing-dist/guides/workflow-schema.md +195 -0
- package/pennyfarthing-dist/guides/worktree-mode.md +113 -0
- package/pennyfarthing-dist/output-styles/teaching.md +33 -0
- package/pennyfarthing-dist/output-styles/terse.md +20 -0
- package/pennyfarthing-dist/output-styles/verbose.md +28 -0
- package/pennyfarthing-dist/personas/themes/1984.yaml +312 -0
- package/pennyfarthing-dist/personas/themes/a-team.yaml +207 -0
- package/pennyfarthing-dist/personas/themes/agatha-christie.yaml +300 -0
- package/pennyfarthing-dist/personas/themes/alice-in-wonderland.yaml +330 -0
- package/pennyfarthing-dist/personas/themes/all-stars.yaml +332 -0
- package/pennyfarthing-dist/personas/themes/ancient-philosophers.yaml +320 -0
- package/pennyfarthing-dist/personas/themes/ancient-strategists.yaml +306 -0
- package/pennyfarthing-dist/personas/themes/arcane.yaml +288 -0
- package/pennyfarthing-dist/personas/themes/arthurian-mythos.yaml +331 -0
- package/pennyfarthing-dist/personas/themes/avatar-the-last-airbender.yaml +288 -0
- package/pennyfarthing-dist/personas/themes/babylon-5.yaml +288 -0
- package/pennyfarthing-dist/personas/themes/battlestar-galactica.yaml +288 -0
- package/pennyfarthing-dist/personas/themes/better-call-saul.yaml +288 -0
- package/pennyfarthing-dist/personas/themes/big-lebowski.yaml +300 -0
- package/pennyfarthing-dist/personas/themes/black-sails.yaml +300 -0
- package/pennyfarthing-dist/personas/themes/blade-runner.yaml +295 -0
- package/pennyfarthing-dist/personas/themes/bobiverse.yaml +288 -0
- package/pennyfarthing-dist/personas/themes/breaking-bad.yaml +327 -0
- package/pennyfarthing-dist/personas/themes/catch-22.yaml +316 -0
- package/pennyfarthing-dist/personas/themes/classical-composers.yaml +310 -0
- package/pennyfarthing-dist/personas/themes/control.yaml +197 -0
- package/pennyfarthing-dist/personas/themes/count-of-monte-cristo.yaml +320 -0
- package/pennyfarthing-dist/personas/themes/cowboy-bebop.yaml +323 -0
- package/pennyfarthing-dist/personas/themes/deadwood.yaml +300 -0
- package/pennyfarthing-dist/personas/themes/dickens.yaml +320 -0
- package/pennyfarthing-dist/personas/themes/discworld.yaml +332 -0
- package/pennyfarthing-dist/personas/themes/doctor-who.yaml +290 -0
- package/pennyfarthing-dist/personas/themes/don-quixote.yaml +320 -0
- package/pennyfarthing-dist/personas/themes/dune.yaml +307 -0
- package/pennyfarthing-dist/personas/themes/enlightenment-thinkers.yaml +320 -0
- package/pennyfarthing-dist/personas/themes/expeditionary-force.yaml +288 -0
- package/pennyfarthing-dist/personas/themes/fargo.yaml +330 -0
- package/pennyfarthing-dist/personas/themes/film-auteurs.yaml +312 -0
- package/pennyfarthing-dist/personas/themes/firefly.yaml +328 -0
- package/pennyfarthing-dist/personas/themes/foundation.yaml +290 -0
- package/pennyfarthing-dist/personas/themes/futurama.yaml +321 -0
- package/pennyfarthing-dist/personas/themes/game-of-thrones.yaml +290 -0
- package/pennyfarthing-dist/personas/themes/gilligans-island.yaml +243 -0
- package/pennyfarthing-dist/personas/themes/gothic-literature.yaml +308 -0
- package/pennyfarthing-dist/personas/themes/great-gatsby.yaml +308 -0
- package/pennyfarthing-dist/personas/themes/greek-mythology.yaml +330 -0
- package/pennyfarthing-dist/personas/themes/hannibal.yaml +300 -0
- package/pennyfarthing-dist/personas/themes/harry-potter.yaml +324 -0
- package/pennyfarthing-dist/personas/themes/his-dark-materials.yaml +291 -0
- package/pennyfarthing-dist/personas/themes/historical-figures.yaml +288 -0
- package/pennyfarthing-dist/personas/themes/hitchhikers-guide.yaml +331 -0
- package/pennyfarthing-dist/personas/themes/house-md.yaml +321 -0
- package/pennyfarthing-dist/personas/themes/imperial-radch.yaml +289 -0
- package/pennyfarthing-dist/personas/themes/inspector-morse.yaml +300 -0
- package/pennyfarthing-dist/personas/themes/jane-austen.yaml +287 -0
- package/pennyfarthing-dist/personas/themes/jazz-legends.yaml +320 -0
- package/pennyfarthing-dist/personas/themes/justified.yaml +300 -0
- package/pennyfarthing-dist/personas/themes/legion-of-doom.yaml +219 -0
- package/pennyfarthing-dist/personas/themes/les-miserables.yaml +299 -0
- package/pennyfarthing-dist/personas/themes/lord-of-the-rings.yaml +334 -0
- package/pennyfarthing-dist/personas/themes/lovecraft-mythos.yaml +334 -0
- package/pennyfarthing-dist/personas/themes/mad-max.yaml +355 -0
- package/pennyfarthing-dist/personas/themes/mad-men.yaml +289 -0
- package/pennyfarthing-dist/personas/themes/marvel-mcu.yaml +300 -0
- package/pennyfarthing-dist/personas/themes/mash.yaml +334 -0
- package/pennyfarthing-dist/personas/themes/mass-effect.yaml +289 -0
- package/pennyfarthing-dist/personas/themes/military-commanders.yaml +306 -0
- package/pennyfarthing-dist/personas/themes/moby-dick.yaml +320 -0
- package/pennyfarthing-dist/personas/themes/monty-python.yaml +303 -0
- package/pennyfarthing-dist/personas/themes/neuromancer.yaml +300 -0
- package/pennyfarthing-dist/personas/themes/norse-mythology.yaml +329 -0
- package/pennyfarthing-dist/personas/themes/parks-and-rec.yaml +242 -0
- package/pennyfarthing-dist/personas/themes/peaky-blinders.yaml +298 -0
- package/pennyfarthing-dist/personas/themes/princess-bride.yaml +220 -0
- package/pennyfarthing-dist/personas/themes/renaissance-masters.yaml +320 -0
- package/pennyfarthing-dist/personas/themes/rome.yaml +300 -0
- package/pennyfarthing-dist/personas/themes/russian-masters.yaml +318 -0
- package/pennyfarthing-dist/personas/themes/sandman.yaml +288 -0
- package/pennyfarthing-dist/personas/themes/scientific-revolutionaries.yaml +320 -0
- package/pennyfarthing-dist/personas/themes/shakespeare.yaml +301 -0
- package/pennyfarthing-dist/personas/themes/sherlock-holmes.yaml +289 -0
- package/pennyfarthing-dist/personas/themes/snow-crash.yaml +288 -0
- package/pennyfarthing-dist/personas/themes/software-pioneers.yaml +300 -0
- package/pennyfarthing-dist/personas/themes/star-trek-tng.yaml +230 -0
- package/pennyfarthing-dist/personas/themes/star-trek-tos.yaml +210 -0
- package/pennyfarthing-dist/personas/themes/star-wars.yaml +303 -0
- package/pennyfarthing-dist/personas/themes/succession.yaml +300 -0
- package/pennyfarthing-dist/personas/themes/superfriends.yaml +208 -0
- package/pennyfarthing-dist/personas/themes/ted-lasso.yaml +236 -0
- package/pennyfarthing-dist/personas/themes/the-americans.yaml +300 -0
- package/pennyfarthing-dist/personas/themes/the-crown.yaml +300 -0
- package/pennyfarthing-dist/personas/themes/the-expanse.yaml +213 -0
- package/pennyfarthing-dist/personas/themes/the-good-place.yaml +322 -0
- package/pennyfarthing-dist/personas/themes/the-matrix.yaml +353 -0
- package/pennyfarthing-dist/personas/themes/the-odyssey.yaml +300 -0
- package/pennyfarthing-dist/personas/themes/the-office.yaml +330 -0
- package/pennyfarthing-dist/personas/themes/the-simpsons.yaml +308 -0
- package/pennyfarthing-dist/personas/themes/the-sopranos.yaml +300 -0
- package/pennyfarthing-dist/personas/themes/the-wire.yaml +311 -0
- package/pennyfarthing-dist/personas/themes/the-witcher.yaml +300 -0
- package/pennyfarthing-dist/personas/themes/twin-peaks.yaml +302 -0
- package/pennyfarthing-dist/personas/themes/vorkosigan-saga.yaml +300 -0
- package/pennyfarthing-dist/personas/themes/watchmen.yaml +291 -0
- package/pennyfarthing-dist/personas/themes/west-wing.yaml +291 -0
- package/pennyfarthing-dist/personas/themes/world-explorers.yaml +320 -0
- package/pennyfarthing-dist/personas/themes/wwii-leaders.yaml +307 -0
- package/pennyfarthing-dist/personas/themes/x-files.yaml +302 -0
- package/pennyfarthing-dist/scripts/add-short-names.mjs +264 -0
- package/pennyfarthing-dist/scripts/agent-session.sh +367 -0
- package/pennyfarthing-dist/scripts/check-context.sh +187 -0
- package/pennyfarthing-dist/scripts/check.sh +497 -0
- package/pennyfarthing-dist/scripts/deploy.sh +284 -0
- package/pennyfarthing-dist/scripts/doctor-dogfood.sh +360 -0
- package/pennyfarthing-dist/scripts/hooks/context-circuit-breaker.sh +61 -0
- package/pennyfarthing-dist/scripts/hooks/context-warning.sh +66 -0
- package/pennyfarthing-dist/scripts/hooks/otel-auto-config.sh +35 -0
- package/pennyfarthing-dist/scripts/hooks/post-merge.sh +166 -0
- package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +50 -0
- package/pennyfarthing-dist/scripts/hooks/pre-edit-check.sh +71 -0
- package/pennyfarthing-dist/scripts/hooks/pre-push.sh +54 -0
- package/pennyfarthing-dist/scripts/hooks/session-start.sh +98 -0
- package/pennyfarthing-dist/scripts/hooks/session-stop.sh +59 -0
- package/pennyfarthing-dist/scripts/install-git-hooks.sh +91 -0
- package/pennyfarthing-dist/scripts/prime.sh +161 -0
- package/pennyfarthing-dist/scripts/release.sh +198 -0
- package/pennyfarthing-dist/scripts/repo-utils.sh +778 -0
- package/pennyfarthing-dist/scripts/run-ci.sh +219 -0
- package/pennyfarthing-dist/scripts/run.sh +65 -0
- package/pennyfarthing-dist/scripts/statusline.sh +264 -0
- package/pennyfarthing-dist/scripts/tests/check.test.sh +582 -0
- package/pennyfarthing-dist/scripts/tests/test-character-voice.sh +107 -0
- package/pennyfarthing-dist/scripts/tests/test-drift-detection.sh +597 -0
- package/pennyfarthing-dist/scripts/tests/test-post-merge-hook.sh +514 -0
- package/pennyfarthing-dist/scripts/tests/test-session-checkpoint.sh +517 -0
- package/pennyfarthing-dist/scripts/tests/test-solo-command.sh +331 -0
- package/pennyfarthing-dist/scripts/uninstall.sh +271 -0
- package/pennyfarthing-dist/scripts/utils/background-tasks.sh +177 -0
- package/pennyfarthing-dist/scripts/utils/check-status.sh +251 -0
- package/pennyfarthing-dist/scripts/utils/checkpoint.sh +136 -0
- package/pennyfarthing-dist/scripts/utils/common.sh +157 -0
- package/pennyfarthing-dist/scripts/utils/create-feature-branches.sh +230 -0
- package/pennyfarthing-dist/scripts/utils/file-lock.sh +269 -0
- package/pennyfarthing-dist/scripts/utils/find-related-work.sh +231 -0
- package/pennyfarthing-dist/scripts/utils/find-root.sh +33 -0
- package/pennyfarthing-dist/scripts/utils/generate-skill-docs.sh +110 -0
- package/pennyfarthing-dist/scripts/utils/git-status-all.sh +127 -0
- package/pennyfarthing-dist/scripts/utils/ground-truth-judge.py +289 -0
- package/pennyfarthing-dist/scripts/utils/jira/jira-lib.mjs +443 -0
- package/pennyfarthing-dist/scripts/utils/jira/jira-sync-story.mjs +208 -0
- package/pennyfarthing-dist/scripts/utils/jira/jira-sync.mjs +198 -0
- package/pennyfarthing-dist/scripts/utils/jira-claim-story.sh +162 -0
- package/pennyfarthing-dist/scripts/utils/jira-lib.sh +463 -0
- package/pennyfarthing-dist/scripts/utils/jira-sync-story.sh +8 -0
- package/pennyfarthing-dist/scripts/utils/jira-sync.sh +8 -0
- package/pennyfarthing-dist/scripts/utils/log-skill-usage.sh +74 -0
- package/pennyfarthing-dist/scripts/utils/logging.sh +186 -0
- package/pennyfarthing-dist/scripts/utils/repo-scan.sh +141 -0
- package/pennyfarthing-dist/scripts/utils/retry.sh +76 -0
- package/pennyfarthing-dist/scripts/utils/run-timestamp.sh +7 -0
- package/pennyfarthing-dist/scripts/utils/session-cleanup.sh +319 -0
- package/pennyfarthing-dist/scripts/utils/skill-usage-report.sh +193 -0
- package/pennyfarthing-dist/scripts/utils/sprint-common.sh +286 -0
- package/pennyfarthing-dist/scripts/utils/sprint-metrics.sh +241 -0
- package/pennyfarthing-dist/scripts/utils/swebench-judge.py +400 -0
- package/pennyfarthing-dist/scripts/utils/sync-epic-to-jira.sh +16 -0
- package/pennyfarthing-dist/scripts/utils/test-setup.sh +337 -0
- package/pennyfarthing-dist/scripts/utils/validate-subagent-frontmatter.sh +160 -0
- package/pennyfarthing-dist/scripts/worktree-manager.sh +498 -0
- package/pennyfarthing-dist/skills/agentic-patterns/SKILL.md +236 -0
- package/pennyfarthing-dist/skills/changelog/SKILL.md +367 -0
- package/pennyfarthing-dist/skills/code-review/SKILL.md +168 -0
- package/pennyfarthing-dist/skills/context-engineering/SKILL.md +268 -0
- package/pennyfarthing-dist/skills/cyclist/SKILL.md +117 -0
- package/pennyfarthing-dist/skills/dev-patterns/SKILL.md +421 -0
- package/pennyfarthing-dist/skills/finalize-run/SKILL.md +258 -0
- package/pennyfarthing-dist/skills/jira/SKILL.md +281 -0
- package/pennyfarthing-dist/skills/judge/SKILL.md +524 -0
- package/pennyfarthing-dist/skills/just/SKILL.md +160 -0
- package/pennyfarthing-dist/skills/mermaid/SKILL.md +240 -0
- package/pennyfarthing-dist/skills/otel/skill.md +222 -0
- package/pennyfarthing-dist/skills/permissions/skill.md +172 -0
- package/pennyfarthing-dist/skills/persona-benchmark/SKILL.md +173 -0
- package/pennyfarthing-dist/skills/skill-registry.schema.json +102 -0
- package/pennyfarthing-dist/skills/skill-registry.yaml +335 -0
- package/pennyfarthing-dist/skills/sprint-context/SKILL.md +120 -0
- package/pennyfarthing-dist/skills/story-management/SKILL.md +208 -0
- package/pennyfarthing-dist/skills/testing/SKILL.md +99 -0
- package/pennyfarthing-dist/skills/testing/references/troubleshooting.md +124 -0
- package/pennyfarthing-dist/skills/theme/skill.md +129 -0
- package/pennyfarthing-dist/skills/theme-creation/SKILL.md +169 -0
- package/pennyfarthing-dist/skills/workflow/SKILL.md +160 -0
- package/pennyfarthing-dist/skills/yq/SKILL.md +264 -0
- package/pennyfarthing-dist/templates/LEADERBOARD.schema.yaml +187 -0
- package/pennyfarthing-dist/templates/LEADERBOARD.template.md +59 -0
- package/pennyfarthing-dist/templates/agent-scopes.yaml.template +276 -0
- package/pennyfarthing-dist/templates/pennyfarthing-settings.yaml.template +61 -0
- package/pennyfarthing-dist/templates/persona-config.yaml.template +22 -0
- package/pennyfarthing-dist/templates/preferences.yaml.template +15 -0
- package/pennyfarthing-dist/templates/settings.local.json.template +90 -0
- package/pennyfarthing-dist/templates/setup-env.sh.template +18 -0
- package/pennyfarthing-dist/templates/shared-context.md.template +70 -0
- package/pennyfarthing-dist/templates/sidecar/decisions.md.template +40 -0
- package/pennyfarthing-dist/templates/sidecar/gotchas.md.template +37 -0
- package/pennyfarthing-dist/templates/sidecar/patterns.md.template +34 -0
- package/pennyfarthing-dist/workflows/agent-docs.yaml +70 -0
- package/pennyfarthing-dist/workflows/bdd.yaml +58 -0
- package/pennyfarthing-dist/workflows/tdd.yaml +50 -0
- package/pennyfarthing-dist/workflows/trivial.yaml +40 -0
|
@@ -0,0 +1,693 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Story 32-2: BMAD Story File Parser
|
|
3
|
+
*
|
|
4
|
+
* These tests define the contract for parsing BMAD story markdown files.
|
|
5
|
+
* Dev will implement parseBmadStory() to pass these tests.
|
|
6
|
+
*
|
|
7
|
+
* BMAD story format:
|
|
8
|
+
* - Required: # Story: [title], ## Status, ## Story, ## Acceptance Criteria
|
|
9
|
+
* - Optional: ## Tasks / Subtasks, ## Dev Notes, ## Dev Agent Record, ## File List
|
|
10
|
+
*
|
|
11
|
+
* Run with: npm test
|
|
12
|
+
*/
|
|
13
|
+
import { describe, it } from 'node:test';
|
|
14
|
+
import assert from 'node:assert';
|
|
15
|
+
// Import the parser function that Dev will implement
|
|
16
|
+
// This import will fail until the module is implemented
|
|
17
|
+
import { parseBmadStory, } from './story-parser.js';
|
|
18
|
+
// =============================================================================
|
|
19
|
+
// TEST DATA: Valid BMAD story examples
|
|
20
|
+
// =============================================================================
|
|
21
|
+
const MINIMAL_VALID_STORY = `# Story: Minimal Test Story
|
|
22
|
+
|
|
23
|
+
## Status
|
|
24
|
+
ready-for-dev
|
|
25
|
+
|
|
26
|
+
## Story
|
|
27
|
+
As a user, I want to test, so that I can verify
|
|
28
|
+
|
|
29
|
+
## Acceptance Criteria
|
|
30
|
+
- Given a test, When I run it, Then it passes
|
|
31
|
+
`;
|
|
32
|
+
const COMPLETE_STORY = `# Story: User Profile Photo Upload
|
|
33
|
+
|
|
34
|
+
## Status
|
|
35
|
+
in-progress
|
|
36
|
+
|
|
37
|
+
## Story
|
|
38
|
+
As a registered user, I want to upload a profile photo, so that other users can identify me visually
|
|
39
|
+
|
|
40
|
+
## Acceptance Criteria
|
|
41
|
+
- Given I am on my profile page, When I click "Upload Photo", Then I see a file picker dialog
|
|
42
|
+
- Given I select a valid image (JPG/PNG under 5MB), When I confirm upload, Then my photo appears on my profile
|
|
43
|
+
- Given I select an invalid file, When I try to upload, Then I see an appropriate error message
|
|
44
|
+
- Given I have an existing photo, When I upload a new one, Then the old photo is replaced
|
|
45
|
+
|
|
46
|
+
## Tasks / Subtasks
|
|
47
|
+
- [x] Create photo upload API endpoint
|
|
48
|
+
- [x] Add file validation (type, size)
|
|
49
|
+
- [x] Implement S3 storage integration
|
|
50
|
+
- [x] Generate thumbnail versions
|
|
51
|
+
- [ ] Build frontend upload component
|
|
52
|
+
- [ ] File picker with drag-and-drop
|
|
53
|
+
- [ ] Preview before upload
|
|
54
|
+
- [x] Progress indicator
|
|
55
|
+
- [ ] Write integration tests
|
|
56
|
+
|
|
57
|
+
## Dev Notes
|
|
58
|
+
2024-01-15: Started implementation. Using pre-signed S3 URLs for direct upload.
|
|
59
|
+
2024-01-16: Hit CORS issue with S3, resolved by updating bucket policy.
|
|
60
|
+
|
|
61
|
+
## Dev Agent Record
|
|
62
|
+
Session: abc123
|
|
63
|
+
Commands:
|
|
64
|
+
- npm run test:upload -- passed
|
|
65
|
+
- aws s3 cp test.jpg s3://bucket/test/ -- verified upload
|
|
66
|
+
|
|
67
|
+
## File List
|
|
68
|
+
- src/api/upload.ts - Created: Photo upload endpoint with S3 integration
|
|
69
|
+
- src/components/PhotoUpload.tsx - Created: React component for file selection
|
|
70
|
+
- src/utils/imageValidation.ts - Created: File type and size validation
|
|
71
|
+
- tests/upload.test.ts - Created: Integration tests for upload flow
|
|
72
|
+
`;
|
|
73
|
+
const STORY_WITH_NON_BDD_CRITERIA = `# Story: Simple Feature
|
|
74
|
+
|
|
75
|
+
## Status
|
|
76
|
+
ready-for-dev
|
|
77
|
+
|
|
78
|
+
## Story
|
|
79
|
+
As a developer, I want a simple feature, so that I can test non-BDD criteria
|
|
80
|
+
|
|
81
|
+
## Acceptance Criteria
|
|
82
|
+
- Users can log in with email and password
|
|
83
|
+
- Password must be at least 8 characters
|
|
84
|
+
- Failed login attempts are logged
|
|
85
|
+
`;
|
|
86
|
+
const STORY_WITH_MIXED_CRITERIA = `# Story: Mixed Criteria Feature
|
|
87
|
+
|
|
88
|
+
## Status
|
|
89
|
+
ready-for-dev
|
|
90
|
+
|
|
91
|
+
## Story
|
|
92
|
+
As a developer, I want mixed criteria, so that I can test both formats
|
|
93
|
+
|
|
94
|
+
## Acceptance Criteria
|
|
95
|
+
- Given valid credentials, When I login, Then I see the dashboard
|
|
96
|
+
- Users receive email notifications
|
|
97
|
+
- Given invalid password, When I login, Then I see an error
|
|
98
|
+
`;
|
|
99
|
+
// =============================================================================
|
|
100
|
+
// AC1: Parses BMAD story markdown format - extracts all sections
|
|
101
|
+
// =============================================================================
|
|
102
|
+
describe('BMAD Story Parser (32-2)', () => {
|
|
103
|
+
describe('AC1: Valid story parsing', () => {
|
|
104
|
+
it('should parse a minimal valid story', () => {
|
|
105
|
+
const result = parseBmadStory(MINIMAL_VALID_STORY);
|
|
106
|
+
assert.strictEqual(result.success, true, 'Should successfully parse minimal story');
|
|
107
|
+
assert.ok(result.story, 'Should return story object');
|
|
108
|
+
assert.strictEqual(result.story?.title, 'Minimal Test Story');
|
|
109
|
+
assert.strictEqual(result.story?.status, 'ready-for-dev');
|
|
110
|
+
});
|
|
111
|
+
it('should parse a complete story with all sections', () => {
|
|
112
|
+
const result = parseBmadStory(COMPLETE_STORY);
|
|
113
|
+
assert.strictEqual(result.success, true, 'Should successfully parse complete story');
|
|
114
|
+
assert.ok(result.story, 'Should return story object');
|
|
115
|
+
assert.strictEqual(result.story?.title, 'User Profile Photo Upload');
|
|
116
|
+
assert.strictEqual(result.story?.status, 'in-progress');
|
|
117
|
+
});
|
|
118
|
+
it('should extract title from H1 header', () => {
|
|
119
|
+
const result = parseBmadStory(MINIMAL_VALID_STORY);
|
|
120
|
+
assert.strictEqual(result.success, true);
|
|
121
|
+
assert.strictEqual(result.story?.title, 'Minimal Test Story');
|
|
122
|
+
});
|
|
123
|
+
it('should handle title with special characters', () => {
|
|
124
|
+
const story = `# Story: Feature: User Auth (OAuth 2.0)
|
|
125
|
+
|
|
126
|
+
## Status
|
|
127
|
+
ready-for-dev
|
|
128
|
+
|
|
129
|
+
## Story
|
|
130
|
+
As a user, I want OAuth, so that I can login
|
|
131
|
+
|
|
132
|
+
## Acceptance Criteria
|
|
133
|
+
- Given OAuth, When I login, Then I'm authenticated
|
|
134
|
+
`;
|
|
135
|
+
const result = parseBmadStory(story);
|
|
136
|
+
assert.strictEqual(result.success, true);
|
|
137
|
+
assert.strictEqual(result.story?.title, 'Feature: User Auth (OAuth 2.0)');
|
|
138
|
+
});
|
|
139
|
+
it('should preserve whitespace in content sections', () => {
|
|
140
|
+
const result = parseBmadStory(COMPLETE_STORY);
|
|
141
|
+
assert.strictEqual(result.success, true);
|
|
142
|
+
// Dev notes should preserve the timestamps and formatting
|
|
143
|
+
assert.ok(result.story?.devNotes?.includes('2024-01-15:'));
|
|
144
|
+
assert.ok(result.story?.devNotes?.includes('2024-01-16:'));
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
// ===========================================================================
|
|
148
|
+
// AC2: Extracts status, ACs, tasks, dev notes - all section content parsed
|
|
149
|
+
// ===========================================================================
|
|
150
|
+
describe('AC2: Section extraction', () => {
|
|
151
|
+
describe('Status section', () => {
|
|
152
|
+
it('should extract ready-for-dev status', () => {
|
|
153
|
+
const result = parseBmadStory(MINIMAL_VALID_STORY);
|
|
154
|
+
assert.strictEqual(result.success, true);
|
|
155
|
+
assert.strictEqual(result.story?.status, 'ready-for-dev');
|
|
156
|
+
});
|
|
157
|
+
it('should extract in-progress status', () => {
|
|
158
|
+
const result = parseBmadStory(COMPLETE_STORY);
|
|
159
|
+
assert.strictEqual(result.success, true);
|
|
160
|
+
assert.strictEqual(result.story?.status, 'in-progress');
|
|
161
|
+
});
|
|
162
|
+
it('should extract review status', () => {
|
|
163
|
+
const story = MINIMAL_VALID_STORY.replace('ready-for-dev', 'review');
|
|
164
|
+
const result = parseBmadStory(story);
|
|
165
|
+
assert.strictEqual(result.success, true);
|
|
166
|
+
assert.strictEqual(result.story?.status, 'review');
|
|
167
|
+
});
|
|
168
|
+
it('should extract done status', () => {
|
|
169
|
+
const story = MINIMAL_VALID_STORY.replace('ready-for-dev', 'done');
|
|
170
|
+
const result = parseBmadStory(story);
|
|
171
|
+
assert.strictEqual(result.success, true);
|
|
172
|
+
assert.strictEqual(result.story?.status, 'done');
|
|
173
|
+
});
|
|
174
|
+
it('should reject invalid status values', () => {
|
|
175
|
+
const story = MINIMAL_VALID_STORY.replace('ready-for-dev', 'invalid-status');
|
|
176
|
+
const result = parseBmadStory(story);
|
|
177
|
+
assert.strictEqual(result.success, false, 'Should reject invalid status');
|
|
178
|
+
assert.ok(result.errors?.some(e => e.section === 'Status'), 'Should report status error');
|
|
179
|
+
});
|
|
180
|
+
it('should trim whitespace from status', () => {
|
|
181
|
+
const story = MINIMAL_VALID_STORY.replace('ready-for-dev', ' in-progress \n');
|
|
182
|
+
const result = parseBmadStory(story);
|
|
183
|
+
assert.strictEqual(result.success, true);
|
|
184
|
+
assert.strictEqual(result.story?.status, 'in-progress');
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
describe('Story section (user story)', () => {
|
|
188
|
+
it('should extract user story text', () => {
|
|
189
|
+
const result = parseBmadStory(MINIMAL_VALID_STORY);
|
|
190
|
+
assert.strictEqual(result.success, true);
|
|
191
|
+
assert.strictEqual(result.story?.userStory, 'As a user, I want to test, so that I can verify');
|
|
192
|
+
});
|
|
193
|
+
it('should handle multi-line user stories', () => {
|
|
194
|
+
const story = `# Story: Multi-line Story
|
|
195
|
+
|
|
196
|
+
## Status
|
|
197
|
+
ready-for-dev
|
|
198
|
+
|
|
199
|
+
## Story
|
|
200
|
+
As a product manager,
|
|
201
|
+
I want to track feature requests,
|
|
202
|
+
so that I can prioritize development effectively
|
|
203
|
+
|
|
204
|
+
## Acceptance Criteria
|
|
205
|
+
- Given a feature, When I track it, Then I see progress
|
|
206
|
+
`;
|
|
207
|
+
const result = parseBmadStory(story);
|
|
208
|
+
assert.strictEqual(result.success, true);
|
|
209
|
+
assert.ok(result.story?.userStory.includes('product manager'));
|
|
210
|
+
assert.ok(result.story?.userStory.includes('prioritize development'));
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
describe('Acceptance Criteria section', () => {
|
|
214
|
+
it('should parse BDD-style criteria (Given/When/Then)', () => {
|
|
215
|
+
const result = parseBmadStory(MINIMAL_VALID_STORY);
|
|
216
|
+
assert.strictEqual(result.success, true);
|
|
217
|
+
assert.strictEqual(result.story?.acceptanceCriteria.length, 1);
|
|
218
|
+
const ac = result.story?.acceptanceCriteria[0];
|
|
219
|
+
assert.strictEqual(ac?.given, 'a test');
|
|
220
|
+
assert.strictEqual(ac?.when, 'I run it');
|
|
221
|
+
assert.strictEqual(ac?.then, 'it passes');
|
|
222
|
+
});
|
|
223
|
+
it('should parse multiple BDD criteria', () => {
|
|
224
|
+
const result = parseBmadStory(COMPLETE_STORY);
|
|
225
|
+
assert.strictEqual(result.success, true);
|
|
226
|
+
assert.strictEqual(result.story?.acceptanceCriteria.length, 4);
|
|
227
|
+
// First criterion
|
|
228
|
+
const ac1 = result.story?.acceptanceCriteria[0];
|
|
229
|
+
assert.strictEqual(ac1?.given, 'I am on my profile page');
|
|
230
|
+
assert.strictEqual(ac1?.when, 'I click "Upload Photo"');
|
|
231
|
+
assert.strictEqual(ac1?.then, 'I see a file picker dialog');
|
|
232
|
+
});
|
|
233
|
+
it('should handle non-BDD criteria with raw text', () => {
|
|
234
|
+
const result = parseBmadStory(STORY_WITH_NON_BDD_CRITERIA);
|
|
235
|
+
assert.strictEqual(result.success, true);
|
|
236
|
+
assert.strictEqual(result.story?.acceptanceCriteria.length, 3);
|
|
237
|
+
// Non-BDD should have empty given/when/then but populated raw
|
|
238
|
+
const ac1 = result.story?.acceptanceCriteria[0];
|
|
239
|
+
assert.strictEqual(ac1?.raw, 'Users can log in with email and password');
|
|
240
|
+
assert.strictEqual(ac1?.given, '');
|
|
241
|
+
assert.strictEqual(ac1?.when, '');
|
|
242
|
+
assert.strictEqual(ac1?.then, '');
|
|
243
|
+
});
|
|
244
|
+
it('should handle mixed BDD and non-BDD criteria', () => {
|
|
245
|
+
const result = parseBmadStory(STORY_WITH_MIXED_CRITERIA);
|
|
246
|
+
assert.strictEqual(result.success, true);
|
|
247
|
+
assert.strictEqual(result.story?.acceptanceCriteria.length, 3);
|
|
248
|
+
// First is BDD
|
|
249
|
+
assert.strictEqual(result.story?.acceptanceCriteria[0]?.given, 'valid credentials');
|
|
250
|
+
// Second is non-BDD
|
|
251
|
+
assert.strictEqual(result.story?.acceptanceCriteria[1]?.raw, 'Users receive email notifications');
|
|
252
|
+
assert.strictEqual(result.story?.acceptanceCriteria[1]?.given, '');
|
|
253
|
+
// Third is BDD
|
|
254
|
+
assert.strictEqual(result.story?.acceptanceCriteria[2]?.given, 'invalid password');
|
|
255
|
+
});
|
|
256
|
+
it('should preserve raw text for all criteria', () => {
|
|
257
|
+
const result = parseBmadStory(MINIMAL_VALID_STORY);
|
|
258
|
+
assert.strictEqual(result.success, true);
|
|
259
|
+
const ac = result.story?.acceptanceCriteria[0];
|
|
260
|
+
assert.strictEqual(ac?.raw, 'Given a test, When I run it, Then it passes');
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
describe('Dev Notes section', () => {
|
|
264
|
+
it('should extract dev notes when present', () => {
|
|
265
|
+
const result = parseBmadStory(COMPLETE_STORY);
|
|
266
|
+
assert.strictEqual(result.success, true);
|
|
267
|
+
assert.ok(result.story?.devNotes);
|
|
268
|
+
assert.ok(result.story?.devNotes?.includes('Started implementation'));
|
|
269
|
+
assert.ok(result.story?.devNotes?.includes('CORS issue'));
|
|
270
|
+
});
|
|
271
|
+
it('should return null when dev notes missing', () => {
|
|
272
|
+
const result = parseBmadStory(MINIMAL_VALID_STORY);
|
|
273
|
+
assert.strictEqual(result.success, true);
|
|
274
|
+
assert.strictEqual(result.story?.devNotes, null);
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
describe('Dev Agent Record section', () => {
|
|
278
|
+
it('should extract dev agent record when present', () => {
|
|
279
|
+
const result = parseBmadStory(COMPLETE_STORY);
|
|
280
|
+
assert.strictEqual(result.success, true);
|
|
281
|
+
assert.ok(result.story?.devAgentRecord);
|
|
282
|
+
assert.ok(result.story?.devAgentRecord?.includes('Session: abc123'));
|
|
283
|
+
assert.ok(result.story?.devAgentRecord?.includes('npm run test:upload'));
|
|
284
|
+
});
|
|
285
|
+
it('should return null when dev agent record missing', () => {
|
|
286
|
+
const result = parseBmadStory(MINIMAL_VALID_STORY);
|
|
287
|
+
assert.strictEqual(result.success, true);
|
|
288
|
+
assert.strictEqual(result.story?.devAgentRecord, null);
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
describe('File List section', () => {
|
|
292
|
+
it('should extract file list when present', () => {
|
|
293
|
+
const result = parseBmadStory(COMPLETE_STORY);
|
|
294
|
+
assert.strictEqual(result.success, true);
|
|
295
|
+
assert.strictEqual(result.story?.fileList.length, 4);
|
|
296
|
+
assert.ok(result.story?.fileList.includes('src/api/upload.ts - Created: Photo upload endpoint with S3 integration'));
|
|
297
|
+
});
|
|
298
|
+
it('should return empty array when file list missing', () => {
|
|
299
|
+
const result = parseBmadStory(MINIMAL_VALID_STORY);
|
|
300
|
+
assert.strictEqual(result.success, true);
|
|
301
|
+
assert.deepStrictEqual(result.story?.fileList, []);
|
|
302
|
+
});
|
|
303
|
+
it('should handle file list with various formats', () => {
|
|
304
|
+
const story = `# Story: File List Test
|
|
305
|
+
|
|
306
|
+
## Status
|
|
307
|
+
ready-for-dev
|
|
308
|
+
|
|
309
|
+
## Story
|
|
310
|
+
As a user, I want files, so that I can track them
|
|
311
|
+
|
|
312
|
+
## Acceptance Criteria
|
|
313
|
+
- Given files, When I list them, Then I see them
|
|
314
|
+
|
|
315
|
+
## File List
|
|
316
|
+
- src/simple.ts
|
|
317
|
+
- src/with-description.ts - Modified: Updated logic
|
|
318
|
+
- path/to/nested/file.tsx - Created: New component
|
|
319
|
+
`;
|
|
320
|
+
const result = parseBmadStory(story);
|
|
321
|
+
assert.strictEqual(result.success, true);
|
|
322
|
+
assert.strictEqual(result.story?.fileList.length, 3);
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
// ===========================================================================
|
|
327
|
+
// AC3: Converts to Pennyfarthing session structure - BmadStory interface
|
|
328
|
+
// ===========================================================================
|
|
329
|
+
describe('AC3: BmadStory interface population', () => {
|
|
330
|
+
it('should return success: true for valid story', () => {
|
|
331
|
+
const result = parseBmadStory(COMPLETE_STORY);
|
|
332
|
+
assert.strictEqual(result.success, true);
|
|
333
|
+
assert.ok(result.story);
|
|
334
|
+
assert.strictEqual(result.errors, undefined);
|
|
335
|
+
});
|
|
336
|
+
it('should return success: false with errors for invalid story', () => {
|
|
337
|
+
const story = `# Story: Missing Sections
|
|
338
|
+
|
|
339
|
+
## Status
|
|
340
|
+
invalid-status
|
|
341
|
+
`;
|
|
342
|
+
const result = parseBmadStory(story);
|
|
343
|
+
assert.strictEqual(result.success, false);
|
|
344
|
+
assert.strictEqual(result.story, undefined);
|
|
345
|
+
assert.ok(result.errors && result.errors.length > 0);
|
|
346
|
+
});
|
|
347
|
+
it('should populate all BmadStory fields for complete story', () => {
|
|
348
|
+
const result = parseBmadStory(COMPLETE_STORY);
|
|
349
|
+
assert.strictEqual(result.success, true);
|
|
350
|
+
const story = result.story;
|
|
351
|
+
// All required fields
|
|
352
|
+
assert.strictEqual(typeof story.title, 'string');
|
|
353
|
+
assert.ok(['ready-for-dev', 'in-progress', 'review', 'done'].includes(story.status));
|
|
354
|
+
assert.strictEqual(typeof story.userStory, 'string');
|
|
355
|
+
assert.ok(Array.isArray(story.acceptanceCriteria));
|
|
356
|
+
// All optional fields
|
|
357
|
+
assert.ok(Array.isArray(story.tasks));
|
|
358
|
+
assert.ok(story.devNotes === null || typeof story.devNotes === 'string');
|
|
359
|
+
assert.ok(story.devAgentRecord === null || typeof story.devAgentRecord === 'string');
|
|
360
|
+
assert.ok(Array.isArray(story.fileList));
|
|
361
|
+
});
|
|
362
|
+
it('should have correct types for acceptance criteria', () => {
|
|
363
|
+
const result = parseBmadStory(COMPLETE_STORY);
|
|
364
|
+
assert.strictEqual(result.success, true);
|
|
365
|
+
const ac = result.story?.acceptanceCriteria[0];
|
|
366
|
+
assert.strictEqual(typeof ac.given, 'string');
|
|
367
|
+
assert.strictEqual(typeof ac.when, 'string');
|
|
368
|
+
assert.strictEqual(typeof ac.then, 'string');
|
|
369
|
+
assert.strictEqual(typeof ac.raw, 'string');
|
|
370
|
+
});
|
|
371
|
+
it('should have correct types for tasks', () => {
|
|
372
|
+
const result = parseBmadStory(COMPLETE_STORY);
|
|
373
|
+
assert.strictEqual(result.success, true);
|
|
374
|
+
const task = result.story?.tasks[0];
|
|
375
|
+
assert.strictEqual(typeof task.text, 'string');
|
|
376
|
+
assert.strictEqual(typeof task.completed, 'boolean');
|
|
377
|
+
// subtasks is optional
|
|
378
|
+
assert.ok(task.subtasks === undefined || Array.isArray(task.subtasks));
|
|
379
|
+
});
|
|
380
|
+
});
|
|
381
|
+
// ===========================================================================
|
|
382
|
+
// AC4: Preserves task checkbox state - [ ] incomplete, [x] complete
|
|
383
|
+
// ===========================================================================
|
|
384
|
+
describe('AC4: Task checkbox parsing', () => {
|
|
385
|
+
it('should parse completed tasks ([x])', () => {
|
|
386
|
+
const result = parseBmadStory(COMPLETE_STORY);
|
|
387
|
+
assert.strictEqual(result.success, true);
|
|
388
|
+
const firstTask = result.story?.tasks[0];
|
|
389
|
+
assert.strictEqual(firstTask?.text, 'Create photo upload API endpoint');
|
|
390
|
+
assert.strictEqual(firstTask?.completed, true);
|
|
391
|
+
});
|
|
392
|
+
it('should parse incomplete tasks ([ ])', () => {
|
|
393
|
+
const result = parseBmadStory(COMPLETE_STORY);
|
|
394
|
+
assert.strictEqual(result.success, true);
|
|
395
|
+
const secondTask = result.story?.tasks[1];
|
|
396
|
+
assert.strictEqual(secondTask?.text, 'Build frontend upload component');
|
|
397
|
+
assert.strictEqual(secondTask?.completed, false);
|
|
398
|
+
});
|
|
399
|
+
it('should parse nested subtasks', () => {
|
|
400
|
+
const result = parseBmadStory(COMPLETE_STORY);
|
|
401
|
+
assert.strictEqual(result.success, true);
|
|
402
|
+
const firstTask = result.story?.tasks[0];
|
|
403
|
+
assert.ok(firstTask?.subtasks);
|
|
404
|
+
assert.strictEqual(firstTask?.subtasks?.length, 3);
|
|
405
|
+
assert.strictEqual(firstTask?.subtasks?.[0].text, 'Add file validation (type, size)');
|
|
406
|
+
assert.strictEqual(firstTask?.subtasks?.[0].completed, true);
|
|
407
|
+
});
|
|
408
|
+
it('should handle mixed completed/incomplete subtasks', () => {
|
|
409
|
+
const result = parseBmadStory(COMPLETE_STORY);
|
|
410
|
+
assert.strictEqual(result.success, true);
|
|
411
|
+
const secondTask = result.story?.tasks[1];
|
|
412
|
+
assert.ok(secondTask?.subtasks);
|
|
413
|
+
// "File picker with drag-and-drop" - incomplete
|
|
414
|
+
assert.strictEqual(secondTask?.subtasks?.[0].completed, false);
|
|
415
|
+
// "Preview before upload" - incomplete
|
|
416
|
+
assert.strictEqual(secondTask?.subtasks?.[1].completed, false);
|
|
417
|
+
// "Progress indicator" - complete
|
|
418
|
+
assert.strictEqual(secondTask?.subtasks?.[2].completed, true);
|
|
419
|
+
});
|
|
420
|
+
it('should return empty tasks array when no tasks section', () => {
|
|
421
|
+
const result = parseBmadStory(MINIMAL_VALID_STORY);
|
|
422
|
+
assert.strictEqual(result.success, true);
|
|
423
|
+
assert.deepStrictEqual(result.story?.tasks, []);
|
|
424
|
+
});
|
|
425
|
+
it('should handle tasks without subtasks', () => {
|
|
426
|
+
const story = `# Story: Simple Tasks
|
|
427
|
+
|
|
428
|
+
## Status
|
|
429
|
+
ready-for-dev
|
|
430
|
+
|
|
431
|
+
## Story
|
|
432
|
+
As a user, I want tasks, so that I can track work
|
|
433
|
+
|
|
434
|
+
## Acceptance Criteria
|
|
435
|
+
- Given tasks, When I check them, Then I see status
|
|
436
|
+
|
|
437
|
+
## Tasks / Subtasks
|
|
438
|
+
- [x] First task done
|
|
439
|
+
- [ ] Second task pending
|
|
440
|
+
- [x] Third task done
|
|
441
|
+
`;
|
|
442
|
+
const result = parseBmadStory(story);
|
|
443
|
+
assert.strictEqual(result.success, true);
|
|
444
|
+
assert.strictEqual(result.story?.tasks.length, 3);
|
|
445
|
+
assert.strictEqual(result.story?.tasks[0].completed, true);
|
|
446
|
+
assert.strictEqual(result.story?.tasks[1].completed, false);
|
|
447
|
+
assert.strictEqual(result.story?.tasks[2].completed, true);
|
|
448
|
+
// No subtasks
|
|
449
|
+
assert.strictEqual(result.story?.tasks[0].subtasks, undefined);
|
|
450
|
+
});
|
|
451
|
+
it('should handle deeply nested subtasks (2-space indent)', () => {
|
|
452
|
+
const story = `# Story: Deep Nesting
|
|
453
|
+
|
|
454
|
+
## Status
|
|
455
|
+
ready-for-dev
|
|
456
|
+
|
|
457
|
+
## Story
|
|
458
|
+
As a user, I want nested tasks, so that I can organize work
|
|
459
|
+
|
|
460
|
+
## Acceptance Criteria
|
|
461
|
+
- Given nesting, When I parse it, Then hierarchy preserved
|
|
462
|
+
|
|
463
|
+
## Tasks / Subtasks
|
|
464
|
+
- [ ] Top level task
|
|
465
|
+
- [ ] First level subtask
|
|
466
|
+
- [x] Another first level
|
|
467
|
+
`;
|
|
468
|
+
const result = parseBmadStory(story);
|
|
469
|
+
assert.strictEqual(result.success, true);
|
|
470
|
+
assert.strictEqual(result.story?.tasks.length, 1);
|
|
471
|
+
assert.strictEqual(result.story?.tasks[0].subtasks?.length, 2);
|
|
472
|
+
assert.strictEqual(result.story?.tasks[0].subtasks?.[0].text, 'First level subtask');
|
|
473
|
+
assert.strictEqual(result.story?.tasks[0].subtasks?.[1].completed, true);
|
|
474
|
+
});
|
|
475
|
+
});
|
|
476
|
+
// ===========================================================================
|
|
477
|
+
// AC5: Unit tests - error accumulation and edge cases
|
|
478
|
+
// ===========================================================================
|
|
479
|
+
describe('AC5: Error handling and edge cases', () => {
|
|
480
|
+
describe('Required section validation', () => {
|
|
481
|
+
it('should reject story missing title', () => {
|
|
482
|
+
const story = `## Status
|
|
483
|
+
ready-for-dev
|
|
484
|
+
|
|
485
|
+
## Story
|
|
486
|
+
As a user, I want something
|
|
487
|
+
|
|
488
|
+
## Acceptance Criteria
|
|
489
|
+
- Given test, When I run, Then pass
|
|
490
|
+
`;
|
|
491
|
+
const result = parseBmadStory(story);
|
|
492
|
+
assert.strictEqual(result.success, false);
|
|
493
|
+
assert.ok(result.errors?.some(e => e.section === 'Title'));
|
|
494
|
+
});
|
|
495
|
+
it('should reject story missing status section', () => {
|
|
496
|
+
const story = `# Story: No Status
|
|
497
|
+
|
|
498
|
+
## Story
|
|
499
|
+
As a user, I want something
|
|
500
|
+
|
|
501
|
+
## Acceptance Criteria
|
|
502
|
+
- Given test, When I run, Then pass
|
|
503
|
+
`;
|
|
504
|
+
const result = parseBmadStory(story);
|
|
505
|
+
assert.strictEqual(result.success, false);
|
|
506
|
+
assert.ok(result.errors?.some(e => e.section === 'Status'));
|
|
507
|
+
});
|
|
508
|
+
it('should reject story missing story section', () => {
|
|
509
|
+
const story = `# Story: No User Story
|
|
510
|
+
|
|
511
|
+
## Status
|
|
512
|
+
ready-for-dev
|
|
513
|
+
|
|
514
|
+
## Acceptance Criteria
|
|
515
|
+
- Given test, When I run, Then pass
|
|
516
|
+
`;
|
|
517
|
+
const result = parseBmadStory(story);
|
|
518
|
+
assert.strictEqual(result.success, false);
|
|
519
|
+
assert.ok(result.errors?.some(e => e.section === 'Story'));
|
|
520
|
+
});
|
|
521
|
+
it('should reject story missing acceptance criteria', () => {
|
|
522
|
+
const story = `# Story: No ACs
|
|
523
|
+
|
|
524
|
+
## Status
|
|
525
|
+
ready-for-dev
|
|
526
|
+
|
|
527
|
+
## Story
|
|
528
|
+
As a user, I want something
|
|
529
|
+
`;
|
|
530
|
+
const result = parseBmadStory(story);
|
|
531
|
+
assert.strictEqual(result.success, false);
|
|
532
|
+
assert.ok(result.errors?.some(e => e.section === 'Acceptance Criteria'));
|
|
533
|
+
});
|
|
534
|
+
});
|
|
535
|
+
describe('Error accumulation', () => {
|
|
536
|
+
it('should report multiple errors at once', () => {
|
|
537
|
+
const story = `# Story: Multiple Errors
|
|
538
|
+
|
|
539
|
+
## Status
|
|
540
|
+
invalid-status
|
|
541
|
+
`;
|
|
542
|
+
// Missing: valid status, Story section, Acceptance Criteria
|
|
543
|
+
const result = parseBmadStory(story);
|
|
544
|
+
assert.strictEqual(result.success, false);
|
|
545
|
+
assert.ok(result.errors && result.errors.length >= 2, 'Should report multiple errors');
|
|
546
|
+
});
|
|
547
|
+
it('should include section name in error', () => {
|
|
548
|
+
const story = `# Story: Bad Status
|
|
549
|
+
|
|
550
|
+
## Status
|
|
551
|
+
not-a-valid-status
|
|
552
|
+
|
|
553
|
+
## Story
|
|
554
|
+
As a user, I want something
|
|
555
|
+
|
|
556
|
+
## Acceptance Criteria
|
|
557
|
+
- Given test, When I run, Then pass
|
|
558
|
+
`;
|
|
559
|
+
const result = parseBmadStory(story);
|
|
560
|
+
assert.strictEqual(result.success, false);
|
|
561
|
+
const statusError = result.errors?.find(e => e.section === 'Status');
|
|
562
|
+
assert.ok(statusError, 'Should have error for Status section');
|
|
563
|
+
});
|
|
564
|
+
it('should include human-readable error message', () => {
|
|
565
|
+
const story = `# Story: Bad Story
|
|
566
|
+
|
|
567
|
+
## Status
|
|
568
|
+
invalid
|
|
569
|
+
`;
|
|
570
|
+
const result = parseBmadStory(story);
|
|
571
|
+
assert.strictEqual(result.success, false);
|
|
572
|
+
const error = result.errors?.[0];
|
|
573
|
+
assert.ok(error?.message, 'Error should have message');
|
|
574
|
+
assert.ok(error?.message.length > 0, 'Message should not be empty');
|
|
575
|
+
});
|
|
576
|
+
});
|
|
577
|
+
describe('Edge cases', () => {
|
|
578
|
+
it('should handle empty content', () => {
|
|
579
|
+
const result = parseBmadStory('');
|
|
580
|
+
assert.strictEqual(result.success, false);
|
|
581
|
+
assert.ok(result.errors && result.errors.length > 0);
|
|
582
|
+
});
|
|
583
|
+
it('should handle content with only whitespace', () => {
|
|
584
|
+
const result = parseBmadStory(' \n\n \t ');
|
|
585
|
+
assert.strictEqual(result.success, false);
|
|
586
|
+
});
|
|
587
|
+
it('should handle extra blank lines between sections', () => {
|
|
588
|
+
const story = `# Story: Extra Whitespace
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
## Status
|
|
592
|
+
|
|
593
|
+
ready-for-dev
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
## Story
|
|
597
|
+
|
|
598
|
+
As a user, I want to test
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
## Acceptance Criteria
|
|
602
|
+
|
|
603
|
+
- Given test, When I run, Then pass
|
|
604
|
+
|
|
605
|
+
`;
|
|
606
|
+
const result = parseBmadStory(story);
|
|
607
|
+
assert.strictEqual(result.success, true);
|
|
608
|
+
assert.strictEqual(result.story?.status, 'ready-for-dev');
|
|
609
|
+
});
|
|
610
|
+
it('should handle section headers with extra spaces', () => {
|
|
611
|
+
const story = `# Story: Spaced Title
|
|
612
|
+
|
|
613
|
+
## Status
|
|
614
|
+
ready-for-dev
|
|
615
|
+
|
|
616
|
+
## Story
|
|
617
|
+
As a user, I want to test
|
|
618
|
+
|
|
619
|
+
## Acceptance Criteria
|
|
620
|
+
- Given test, When I run, Then pass
|
|
621
|
+
`;
|
|
622
|
+
const result = parseBmadStory(story);
|
|
623
|
+
assert.strictEqual(result.success, true);
|
|
624
|
+
assert.strictEqual(result.story?.title, 'Spaced Title');
|
|
625
|
+
});
|
|
626
|
+
it('should handle acceptance criteria with inline code', () => {
|
|
627
|
+
const story = `# Story: Code in ACs
|
|
628
|
+
|
|
629
|
+
## Status
|
|
630
|
+
ready-for-dev
|
|
631
|
+
|
|
632
|
+
## Story
|
|
633
|
+
As a developer, I want to test code references
|
|
634
|
+
|
|
635
|
+
## Acceptance Criteria
|
|
636
|
+
- Given a \`user_id\` parameter, When I call \`getUser()\`, Then I get the user object
|
|
637
|
+
`;
|
|
638
|
+
const result = parseBmadStory(story);
|
|
639
|
+
assert.strictEqual(result.success, true);
|
|
640
|
+
assert.ok(result.story?.acceptanceCriteria[0].raw.includes('`user_id`'));
|
|
641
|
+
});
|
|
642
|
+
it('should handle Windows line endings (CRLF)', () => {
|
|
643
|
+
const story = '# Story: Windows\r\n\r\n## Status\r\nready-for-dev\r\n\r\n## Story\r\nAs a user, I want Windows support\r\n\r\n## Acceptance Criteria\r\n- Given CRLF, When I parse, Then it works\r\n';
|
|
644
|
+
const result = parseBmadStory(story);
|
|
645
|
+
assert.strictEqual(result.success, true);
|
|
646
|
+
assert.strictEqual(result.story?.title, 'Windows');
|
|
647
|
+
});
|
|
648
|
+
it('should handle Tasks section with alternative header name', () => {
|
|
649
|
+
const story = `# Story: Alt Tasks Header
|
|
650
|
+
|
|
651
|
+
## Status
|
|
652
|
+
ready-for-dev
|
|
653
|
+
|
|
654
|
+
## Story
|
|
655
|
+
As a user, I want flexibility
|
|
656
|
+
|
|
657
|
+
## Acceptance Criteria
|
|
658
|
+
- Given alt header, When I parse, Then tasks found
|
|
659
|
+
|
|
660
|
+
## Tasks
|
|
661
|
+
- [ ] Simple task without "/ Subtasks" suffix
|
|
662
|
+
`;
|
|
663
|
+
const result = parseBmadStory(story);
|
|
664
|
+
assert.strictEqual(result.success, true);
|
|
665
|
+
assert.strictEqual(result.story?.tasks.length, 1);
|
|
666
|
+
});
|
|
667
|
+
it('should handle empty acceptance criteria section', () => {
|
|
668
|
+
const story = `# Story: Empty ACs
|
|
669
|
+
|
|
670
|
+
## Status
|
|
671
|
+
ready-for-dev
|
|
672
|
+
|
|
673
|
+
## Story
|
|
674
|
+
As a user, I want to test
|
|
675
|
+
|
|
676
|
+
## Acceptance Criteria
|
|
677
|
+
`;
|
|
678
|
+
const result = parseBmadStory(story);
|
|
679
|
+
assert.strictEqual(result.success, false);
|
|
680
|
+
assert.ok(result.errors?.some(e => e.section === 'Acceptance Criteria' &&
|
|
681
|
+
e.message.toLowerCase().includes('empty')));
|
|
682
|
+
});
|
|
683
|
+
it('should handle uppercase status values', () => {
|
|
684
|
+
const story = MINIMAL_VALID_STORY.replace('ready-for-dev', 'READY-FOR-DEV');
|
|
685
|
+
const result = parseBmadStory(story);
|
|
686
|
+
// Should normalize to lowercase
|
|
687
|
+
assert.strictEqual(result.success, true);
|
|
688
|
+
assert.strictEqual(result.story?.status, 'ready-for-dev');
|
|
689
|
+
});
|
|
690
|
+
});
|
|
691
|
+
});
|
|
692
|
+
});
|
|
693
|
+
//# sourceMappingURL=story-parser.test.js.map
|