specweave 0.18.1 → 0.20.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/CLAUDE.md +229 -1817
- package/README.md +68 -0
- package/bin/specweave.js +62 -6
- package/dist/plugins/specweave/lib/hooks/sync-living-docs.d.ts.map +1 -1
- package/dist/plugins/specweave/lib/hooks/sync-living-docs.js +3 -0
- package/dist/plugins/specweave/lib/hooks/sync-living-docs.js.map +1 -1
- package/dist/plugins/specweave/lib/hooks/update-ac-status.d.ts +21 -0
- package/dist/plugins/specweave/lib/hooks/update-ac-status.d.ts.map +1 -0
- package/dist/plugins/specweave/lib/hooks/update-ac-status.js +162 -0
- package/dist/plugins/specweave/lib/hooks/update-ac-status.js.map +1 -0
- package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.js +65 -6
- package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.js.map +1 -1
- package/dist/plugins/specweave-github/lib/completion-calculator.d.ts +112 -0
- package/dist/plugins/specweave-github/lib/completion-calculator.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/completion-calculator.js +301 -0
- package/dist/plugins/specweave-github/lib/completion-calculator.js.map +1 -0
- package/dist/plugins/specweave-github/lib/duplicate-detector.d.ts +3 -3
- package/dist/plugins/specweave-github/lib/duplicate-detector.js +3 -3
- package/dist/plugins/specweave-github/lib/epic-content-builder.d.ts +7 -0
- package/dist/plugins/specweave-github/lib/epic-content-builder.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/epic-content-builder.js +42 -0
- package/dist/plugins/specweave-github/lib/epic-content-builder.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-client-v2.d.ts +14 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-client-v2.js +51 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-epic-sync.js +1 -1
- package/dist/plugins/specweave-github/lib/github-epic-sync.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts +87 -0
- package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/github-feature-sync.js +412 -0
- package/dist/plugins/specweave-github/lib/github-feature-sync.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-spec-content-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-spec-content-sync.js +64 -13
- package/dist/plugins/specweave-github/lib/github-spec-content-sync.js.map +1 -1
- package/dist/plugins/specweave-github/lib/progress-comment-builder.d.ts +78 -0
- package/dist/plugins/specweave-github/lib/progress-comment-builder.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/progress-comment-builder.js +237 -0
- package/dist/plugins/specweave-github/lib/progress-comment-builder.js.map +1 -0
- package/dist/plugins/specweave-github/lib/user-story-content-builder.d.ts +97 -0
- package/dist/plugins/specweave-github/lib/user-story-content-builder.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/user-story-content-builder.js +301 -0
- package/dist/plugins/specweave-github/lib/user-story-content-builder.js.map +1 -0
- package/dist/plugins/specweave-github/lib/user-story-issue-builder.d.ts +83 -0
- package/dist/plugins/specweave-github/lib/user-story-issue-builder.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/user-story-issue-builder.js +386 -0
- package/dist/plugins/specweave-github/lib/user-story-issue-builder.js.map +1 -0
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.d.ts +8 -6
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.js +78 -117
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.js.map +1 -1
- package/dist/plugins/specweave-kafka/lib/cli/kcat-wrapper.d.ts +57 -0
- package/dist/plugins/specweave-kafka/lib/cli/kcat-wrapper.d.ts.map +1 -0
- package/dist/plugins/specweave-kafka/lib/cli/kcat-wrapper.js +248 -0
- package/dist/plugins/specweave-kafka/lib/cli/kcat-wrapper.js.map +1 -0
- package/dist/plugins/specweave-kafka/lib/cli/types.d.ts +82 -0
- package/dist/plugins/specweave-kafka/lib/cli/types.d.ts.map +1 -0
- package/dist/plugins/specweave-kafka/lib/cli/types.js +13 -0
- package/dist/plugins/specweave-kafka/lib/cli/types.js.map +1 -0
- package/dist/plugins/specweave-kafka/lib/mcp/detector.d.ts +49 -0
- package/dist/plugins/specweave-kafka/lib/mcp/detector.d.ts.map +1 -0
- package/dist/plugins/specweave-kafka/lib/mcp/detector.js +316 -0
- package/dist/plugins/specweave-kafka/lib/mcp/detector.js.map +1 -0
- package/dist/plugins/specweave-kafka/lib/mcp/types.d.ts +70 -0
- package/dist/plugins/specweave-kafka/lib/mcp/types.d.ts.map +1 -0
- package/dist/plugins/specweave-kafka/lib/mcp/types.js +23 -0
- package/dist/plugins/specweave-kafka/lib/mcp/types.js.map +1 -0
- package/dist/plugins/specweave-kafka/lib/utils/partitioning.d.ts +85 -0
- package/dist/plugins/specweave-kafka/lib/utils/partitioning.d.ts.map +1 -0
- package/dist/plugins/specweave-kafka/lib/utils/partitioning.js +281 -0
- package/dist/plugins/specweave-kafka/lib/utils/partitioning.js.map +1 -0
- package/dist/plugins/specweave-kafka/lib/utils/sizing.d.ts +75 -0
- package/dist/plugins/specweave-kafka/lib/utils/sizing.d.ts.map +1 -0
- package/dist/plugins/specweave-kafka/lib/utils/sizing.js +238 -0
- package/dist/plugins/specweave-kafka/lib/utils/sizing.js.map +1 -0
- package/dist/src/cli/commands/import-docs.js +4 -4
- package/dist/src/cli/commands/import-docs.js.map +1 -1
- package/dist/src/cli/commands/init-multiproject.d.ts.map +1 -1
- package/dist/src/cli/commands/init-multiproject.js +17 -18
- package/dist/src/cli/commands/init-multiproject.js.map +1 -1
- package/dist/src/cli/commands/migrate-to-multiproject.d.ts.map +1 -1
- package/dist/src/cli/commands/migrate-to-multiproject.js +8 -4
- package/dist/src/cli/commands/migrate-to-multiproject.js.map +1 -1
- package/dist/src/cli/commands/switch-project.d.ts.map +1 -1
- package/dist/src/cli/commands/switch-project.js +9 -26
- package/dist/src/cli/commands/switch-project.js.map +1 -1
- package/dist/src/cli/commands/sync-spec-content.js +3 -0
- package/dist/src/cli/commands/sync-spec-content.js.map +1 -1
- package/dist/src/core/deduplication/command-deduplicator.d.ts +166 -0
- package/dist/src/core/deduplication/command-deduplicator.d.ts.map +1 -0
- package/dist/src/core/deduplication/command-deduplicator.js +254 -0
- package/dist/src/core/deduplication/command-deduplicator.js.map +1 -0
- package/dist/src/core/increment/active-increment-manager.d.ts +42 -15
- package/dist/src/core/increment/active-increment-manager.d.ts.map +1 -1
- package/dist/src/core/increment/active-increment-manager.js +113 -46
- package/dist/src/core/increment/active-increment-manager.js.map +1 -1
- package/dist/src/core/increment/conflict-resolver.d.ts +40 -0
- package/dist/src/core/increment/conflict-resolver.d.ts.map +1 -0
- package/dist/src/core/increment/conflict-resolver.js +219 -0
- package/dist/src/core/increment/conflict-resolver.js.map +1 -0
- package/dist/src/core/increment/discipline-checker.d.ts.map +1 -1
- package/dist/src/core/increment/discipline-checker.js +7 -1
- package/dist/src/core/increment/discipline-checker.js.map +1 -1
- package/dist/src/core/increment/duplicate-detector.d.ts +52 -0
- package/dist/src/core/increment/duplicate-detector.d.ts.map +1 -0
- package/dist/src/core/increment/duplicate-detector.js +276 -0
- package/dist/src/core/increment/duplicate-detector.js.map +1 -0
- package/dist/src/core/increment/increment-archiver.d.ts +90 -0
- package/dist/src/core/increment/increment-archiver.d.ts.map +1 -0
- package/dist/src/core/increment/increment-archiver.js +368 -0
- package/dist/src/core/increment/increment-archiver.js.map +1 -0
- package/dist/src/core/increment/increment-reopener.d.ts +165 -0
- package/dist/src/core/increment/increment-reopener.d.ts.map +1 -0
- package/dist/src/core/increment/increment-reopener.js +390 -0
- package/dist/src/core/increment/increment-reopener.js.map +1 -0
- package/dist/src/core/increment/metadata-manager.d.ts +26 -1
- package/dist/src/core/increment/metadata-manager.d.ts.map +1 -1
- package/dist/src/core/increment/metadata-manager.js +143 -5
- package/dist/src/core/increment/metadata-manager.js.map +1 -1
- package/dist/src/core/increment/recent-work-scanner.d.ts +121 -0
- package/dist/src/core/increment/recent-work-scanner.d.ts.map +1 -0
- package/dist/src/core/increment/recent-work-scanner.js +303 -0
- package/dist/src/core/increment/recent-work-scanner.js.map +1 -0
- package/dist/src/core/increment/types.d.ts +1 -0
- package/dist/src/core/increment/types.d.ts.map +1 -1
- package/dist/src/core/increment-utils.d.ts +112 -0
- package/dist/src/core/increment-utils.d.ts.map +1 -0
- package/dist/src/core/increment-utils.js +210 -0
- package/dist/src/core/increment-utils.js.map +1 -0
- package/dist/src/core/living-docs/ac-project-specific-generator.d.ts +65 -0
- package/dist/src/core/living-docs/ac-project-specific-generator.d.ts.map +1 -0
- package/dist/src/core/living-docs/ac-project-specific-generator.js +175 -0
- package/dist/src/core/living-docs/ac-project-specific-generator.js.map +1 -0
- package/dist/src/core/living-docs/feature-archiver.d.ts +130 -0
- package/dist/src/core/living-docs/feature-archiver.d.ts.map +1 -0
- package/dist/src/core/living-docs/feature-archiver.js +549 -0
- package/dist/src/core/living-docs/feature-archiver.js.map +1 -0
- package/dist/src/core/living-docs/feature-id-manager.d.ts +81 -0
- package/dist/src/core/living-docs/feature-id-manager.d.ts.map +1 -0
- package/dist/src/core/living-docs/feature-id-manager.js +339 -0
- package/dist/src/core/living-docs/feature-id-manager.js.map +1 -0
- package/dist/src/core/living-docs/hierarchy-mapper.d.ts +144 -83
- package/dist/src/core/living-docs/hierarchy-mapper.d.ts.map +1 -1
- package/dist/src/core/living-docs/hierarchy-mapper.js +488 -270
- package/dist/src/core/living-docs/hierarchy-mapper.js.map +1 -1
- package/dist/src/core/living-docs/index.d.ts +6 -0
- package/dist/src/core/living-docs/index.d.ts.map +1 -1
- package/dist/src/core/living-docs/index.js +6 -0
- package/dist/src/core/living-docs/index.js.map +1 -1
- package/dist/src/core/living-docs/project-detector.d.ts +6 -0
- package/dist/src/core/living-docs/project-detector.d.ts.map +1 -1
- package/dist/src/core/living-docs/project-detector.js +35 -1
- package/dist/src/core/living-docs/project-detector.js.map +1 -1
- package/dist/src/core/living-docs/spec-distributor.d.ts +100 -26
- package/dist/src/core/living-docs/spec-distributor.d.ts.map +1 -1
- package/dist/src/core/living-docs/spec-distributor.js +1275 -258
- package/dist/src/core/living-docs/spec-distributor.js.map +1 -1
- package/dist/src/core/living-docs/task-project-specific-generator.d.ts +109 -0
- package/dist/src/core/living-docs/task-project-specific-generator.d.ts.map +1 -0
- package/dist/src/core/living-docs/task-project-specific-generator.js +221 -0
- package/dist/src/core/living-docs/task-project-specific-generator.js.map +1 -0
- package/dist/src/core/living-docs/types.d.ts +143 -0
- package/dist/src/core/living-docs/types.d.ts.map +1 -1
- package/dist/src/core/project-manager.d.ts +2 -17
- package/dist/src/core/project-manager.d.ts.map +1 -1
- package/dist/src/core/project-manager.js +68 -48
- package/dist/src/core/project-manager.js.map +1 -1
- package/dist/src/core/spec-content-sync.d.ts +1 -1
- package/dist/src/core/spec-content-sync.d.ts.map +1 -1
- package/dist/src/core/sync/enhanced-content-builder.d.ts.map +1 -1
- package/dist/src/core/sync/enhanced-content-builder.js +2 -1
- package/dist/src/core/sync/enhanced-content-builder.js.map +1 -1
- package/dist/src/core/sync/performance-optimizer.d.ts +153 -0
- package/dist/src/core/sync/performance-optimizer.d.ts.map +1 -0
- package/dist/src/core/sync/performance-optimizer.js +220 -0
- package/dist/src/core/sync/performance-optimizer.js.map +1 -0
- package/dist/src/core/sync/retry-handler.d.ts +98 -0
- package/dist/src/core/sync/retry-handler.d.ts.map +1 -0
- package/dist/src/core/sync/retry-handler.js +196 -0
- package/dist/src/core/sync/retry-handler.js.map +1 -0
- package/dist/src/core/types/config.d.ts +94 -0
- package/dist/src/core/types/config.d.ts.map +1 -1
- package/dist/src/core/types/config.js +16 -0
- package/dist/src/core/types/config.js.map +1 -1
- package/dist/src/core/types/increment-metadata.d.ts +6 -0
- package/dist/src/core/types/increment-metadata.d.ts.map +1 -1
- package/dist/src/core/types/increment-metadata.js +10 -1
- package/dist/src/core/types/increment-metadata.js.map +1 -1
- package/dist/src/integrations/jira/jira-incremental-mapper.d.ts.map +1 -1
- package/dist/src/integrations/jira/jira-incremental-mapper.js +4 -8
- package/dist/src/integrations/jira/jira-incremental-mapper.js.map +1 -1
- package/dist/src/integrations/jira/jira-mapper.d.ts.map +1 -1
- package/dist/src/integrations/jira/jira-mapper.js +4 -8
- package/dist/src/integrations/jira/jira-mapper.js.map +1 -1
- package/package.json +1 -1
- package/plugins/specweave/COMMANDS.md +13 -4
- package/plugins/specweave/commands/specweave-abandon.md +22 -20
- package/plugins/specweave/commands/specweave-archive-features.md +121 -0
- package/plugins/specweave/commands/specweave-archive-increments.md +82 -0
- package/plugins/specweave/commands/specweave-archive.md +363 -0
- package/plugins/specweave/commands/specweave-backlog.md +211 -0
- package/plugins/specweave/commands/specweave-fix-duplicates.md +517 -0
- package/plugins/specweave/commands/specweave-increment.md +4 -3
- package/plugins/specweave/commands/specweave-progress.md +176 -27
- package/plugins/specweave/commands/specweave-reopen.md +391 -0
- package/plugins/specweave/commands/specweave-restore-feature.md +90 -0
- package/plugins/specweave/commands/specweave-restore.md +309 -0
- package/plugins/specweave/commands/specweave-resume.md +51 -23
- package/plugins/specweave/commands/specweave-status.md +41 -7
- package/plugins/specweave/commands/specweave-sync-specs.md +425 -0
- package/plugins/specweave/hooks/hooks.json +4 -0
- package/plugins/specweave/hooks/lib/sync-spec-content.sh +2 -2
- package/plugins/specweave/hooks/post-task-completion.sh +39 -0
- package/plugins/specweave/hooks/pre-command-deduplication.sh +83 -0
- package/plugins/specweave/hooks/user-prompt-submit.sh +1 -1
- package/plugins/specweave/lib/hooks/sync-living-docs.js +2 -0
- package/plugins/specweave/lib/hooks/sync-living-docs.ts +4 -0
- package/plugins/specweave/lib/hooks/update-ac-status.js +102 -0
- package/plugins/specweave/lib/hooks/update-ac-status.ts +192 -0
- package/plugins/specweave/skills/archive-increments/SKILL.md +198 -0
- package/plugins/specweave/skills/increment-planner/scripts/feature-utils.js +14 -0
- package/plugins/specweave/skills/smart-reopen-detector/SKILL.md +244 -0
- package/plugins/specweave-ado/lib/ado-spec-content-sync.js +49 -5
- package/plugins/specweave-ado/lib/ado-spec-content-sync.ts +72 -6
- package/plugins/specweave-confluent/.claude-plugin/plugin.json +23 -0
- package/plugins/specweave-confluent/README.md +375 -0
- package/plugins/specweave-confluent/agents/confluent-architect/AGENT.md +306 -0
- package/plugins/specweave-confluent/skills/confluent-kafka-connect/SKILL.md +453 -0
- package/plugins/specweave-confluent/skills/confluent-ksqldb/SKILL.md +470 -0
- package/plugins/specweave-confluent/skills/confluent-schema-registry/SKILL.md +316 -0
- package/plugins/specweave-github/agents/github-task-splitter/AGENT.md +2 -2
- package/plugins/specweave-github/agents/user-story-updater/AGENT.md +148 -0
- package/plugins/specweave-github/commands/specweave-github-cleanup-duplicates.md +1 -1
- package/plugins/specweave-github/commands/specweave-github-update-user-story.md +156 -0
- package/plugins/specweave-github/hooks/post-task-completion.sh +10 -9
- package/plugins/specweave-github/lib/completion-calculator.js +262 -0
- package/plugins/specweave-github/lib/completion-calculator.ts +434 -0
- package/plugins/specweave-github/lib/duplicate-detector.js +3 -3
- package/plugins/specweave-github/lib/duplicate-detector.ts +4 -4
- package/plugins/specweave-github/lib/epic-content-builder.js +38 -0
- package/plugins/specweave-github/lib/epic-content-builder.ts +59 -0
- package/plugins/specweave-github/lib/github-client-v2.js +49 -0
- package/plugins/specweave-github/lib/github-client-v2.ts +59 -0
- package/plugins/specweave-github/lib/github-epic-sync.ts +1 -1
- package/plugins/specweave-github/lib/github-feature-sync.js +381 -0
- package/plugins/specweave-github/lib/github-feature-sync.ts +568 -0
- package/plugins/specweave-github/lib/github-spec-content-sync.js +40 -10
- package/plugins/specweave-github/lib/github-spec-content-sync.ts +82 -14
- package/plugins/specweave-github/lib/progress-comment-builder.js +229 -0
- package/plugins/specweave-github/lib/progress-comment-builder.ts +324 -0
- package/plugins/specweave-github/lib/user-story-content-builder.js +299 -0
- package/plugins/specweave-github/lib/user-story-content-builder.ts +413 -0
- package/plugins/specweave-github/lib/user-story-issue-builder.js +344 -0
- package/plugins/specweave-github/lib/user-story-issue-builder.ts +543 -0
- package/plugins/specweave-github/skills/github-issue-standard/SKILL.md +189 -0
- package/plugins/specweave-jira/lib/enhanced-jira-sync.js +134 -0
- package/plugins/specweave-jira/lib/{enhanced-jira-sync.ts.disabled → enhanced-jira-sync.ts} +26 -52
- package/plugins/specweave-kafka/.claude-plugin/plugin.json +26 -0
- package/plugins/specweave-kafka/IMPLEMENTATION-COMPLETE.md +483 -0
- package/plugins/specweave-kafka/README.md +242 -0
- package/plugins/specweave-kafka/agents/kafka-architect/AGENT.md +235 -0
- package/plugins/specweave-kafka/agents/kafka-devops/AGENT.md +209 -0
- package/plugins/specweave-kafka/agents/kafka-observability/AGENT.md +266 -0
- package/plugins/specweave-kafka/commands/deploy.md +99 -0
- package/plugins/specweave-kafka/commands/dev-env.md +176 -0
- package/plugins/specweave-kafka/commands/mcp-configure.md +101 -0
- package/plugins/specweave-kafka/commands/monitor-setup.md +96 -0
- package/plugins/specweave-kafka/docker/kafka-local/docker-compose.yml +187 -0
- package/plugins/specweave-kafka/docker/redpanda/docker-compose.yml +199 -0
- package/plugins/specweave-kafka/docker/templates/consumer-nodejs.js +225 -0
- package/plugins/specweave-kafka/docker/templates/consumer-python.py +220 -0
- package/plugins/specweave-kafka/docker/templates/producer-nodejs.js +168 -0
- package/plugins/specweave-kafka/docker/templates/producer-python.py +167 -0
- package/plugins/specweave-kafka/lib/adapters/apache-kafka-adapter.js +438 -0
- package/plugins/specweave-kafka/lib/adapters/apache-kafka-adapter.ts +541 -0
- package/plugins/specweave-kafka/lib/adapters/platform-adapter.js +47 -0
- package/plugins/specweave-kafka/lib/adapters/platform-adapter.ts +343 -0
- package/plugins/specweave-kafka/lib/cli/kcat-wrapper.js +258 -0
- package/plugins/specweave-kafka/lib/cli/kcat-wrapper.ts +298 -0
- package/plugins/specweave-kafka/lib/cli/types.js +10 -0
- package/plugins/specweave-kafka/lib/cli/types.ts +92 -0
- package/plugins/specweave-kafka/lib/connectors/connector-catalog.js +305 -0
- package/plugins/specweave-kafka/lib/connectors/connector-catalog.ts +528 -0
- package/plugins/specweave-kafka/lib/documentation/diagram-generator.js +114 -0
- package/plugins/specweave-kafka/lib/documentation/diagram-generator.ts +195 -0
- package/plugins/specweave-kafka/lib/documentation/exporter.js +210 -0
- package/plugins/specweave-kafka/lib/documentation/exporter.ts +338 -0
- package/plugins/specweave-kafka/lib/documentation/schema-catalog-generator.js +60 -0
- package/plugins/specweave-kafka/lib/documentation/schema-catalog-generator.ts +130 -0
- package/plugins/specweave-kafka/lib/documentation/topology-generator.js +143 -0
- package/plugins/specweave-kafka/lib/documentation/topology-generator.ts +290 -0
- package/plugins/specweave-kafka/lib/mcp/detector.js +298 -0
- package/plugins/specweave-kafka/lib/mcp/detector.ts +352 -0
- package/plugins/specweave-kafka/lib/mcp/types.js +21 -0
- package/plugins/specweave-kafka/lib/mcp/types.ts +77 -0
- package/plugins/specweave-kafka/lib/multi-cluster/cluster-config-manager.js +193 -0
- package/plugins/specweave-kafka/lib/multi-cluster/cluster-config-manager.ts +362 -0
- package/plugins/specweave-kafka/lib/multi-cluster/cluster-switcher.js +188 -0
- package/plugins/specweave-kafka/lib/multi-cluster/cluster-switcher.ts +359 -0
- package/plugins/specweave-kafka/lib/multi-cluster/health-aggregator.js +195 -0
- package/plugins/specweave-kafka/lib/multi-cluster/health-aggregator.ts +380 -0
- package/plugins/specweave-kafka/lib/observability/opentelemetry-kafka.js +209 -0
- package/plugins/specweave-kafka/lib/observability/opentelemetry-kafka.ts +358 -0
- package/plugins/specweave-kafka/lib/patterns/advanced-ksqldb-patterns.js +354 -0
- package/plugins/specweave-kafka/lib/patterns/advanced-ksqldb-patterns.ts +563 -0
- package/plugins/specweave-kafka/lib/patterns/circuit-breaker-resilience.js +259 -0
- package/plugins/specweave-kafka/lib/patterns/circuit-breaker-resilience.ts +516 -0
- package/plugins/specweave-kafka/lib/patterns/dead-letter-queue.js +233 -0
- package/plugins/specweave-kafka/lib/patterns/dead-letter-queue.ts +423 -0
- package/plugins/specweave-kafka/lib/patterns/exactly-once-semantics.js +266 -0
- package/plugins/specweave-kafka/lib/patterns/exactly-once-semantics.ts +445 -0
- package/plugins/specweave-kafka/lib/patterns/flink-kafka-integration.js +312 -0
- package/plugins/specweave-kafka/lib/patterns/flink-kafka-integration.ts +561 -0
- package/plugins/specweave-kafka/lib/patterns/multi-dc-replication.js +289 -0
- package/plugins/specweave-kafka/lib/patterns/multi-dc-replication.ts +607 -0
- package/plugins/specweave-kafka/lib/patterns/rate-limiting-backpressure.js +264 -0
- package/plugins/specweave-kafka/lib/patterns/rate-limiting-backpressure.ts +498 -0
- package/plugins/specweave-kafka/lib/patterns/stream-processing-optimization.js +263 -0
- package/plugins/specweave-kafka/lib/patterns/stream-processing-optimization.ts +549 -0
- package/plugins/specweave-kafka/lib/patterns/tiered-storage-compaction.js +205 -0
- package/plugins/specweave-kafka/lib/patterns/tiered-storage-compaction.ts +399 -0
- package/plugins/specweave-kafka/lib/performance/performance-optimizer.js +249 -0
- package/plugins/specweave-kafka/lib/performance/performance-optimizer.ts +427 -0
- package/plugins/specweave-kafka/lib/security/kafka-security.js +252 -0
- package/plugins/specweave-kafka/lib/security/kafka-security.ts +494 -0
- package/plugins/specweave-kafka/lib/utils/capacity-planner.js +203 -0
- package/plugins/specweave-kafka/lib/utils/capacity-planner.ts +469 -0
- package/plugins/specweave-kafka/lib/utils/config-validator.js +419 -0
- package/plugins/specweave-kafka/lib/utils/config-validator.ts +564 -0
- package/plugins/specweave-kafka/lib/utils/partitioning.js +329 -0
- package/plugins/specweave-kafka/lib/utils/partitioning.ts +473 -0
- package/plugins/specweave-kafka/lib/utils/sizing.js +221 -0
- package/plugins/specweave-kafka/lib/utils/sizing.ts +374 -0
- package/plugins/specweave-kafka/monitoring/grafana/dashboards/kafka-broker-metrics.json +628 -0
- package/plugins/specweave-kafka/monitoring/grafana/dashboards/kafka-cluster-overview.json +564 -0
- package/plugins/specweave-kafka/monitoring/grafana/dashboards/kafka-consumer-lag.json +509 -0
- package/plugins/specweave-kafka/monitoring/grafana/dashboards/kafka-jvm-metrics.json +674 -0
- package/plugins/specweave-kafka/monitoring/grafana/dashboards/kafka-topic-metrics.json +578 -0
- package/plugins/specweave-kafka/monitoring/grafana/provisioning/dashboards/kafka.yml +17 -0
- package/plugins/specweave-kafka/monitoring/grafana/provisioning/datasources/prometheus.yml +17 -0
- package/plugins/specweave-kafka/monitoring/prometheus/kafka-alerts.yml +415 -0
- package/plugins/specweave-kafka/monitoring/prometheus/kafka-jmx-exporter.yml +256 -0
- package/plugins/specweave-kafka/package.json +41 -0
- package/plugins/specweave-kafka/skills/kafka-architecture/SKILL.md +647 -0
- package/plugins/specweave-kafka/skills/kafka-cli-tools/SKILL.md +433 -0
- package/plugins/specweave-kafka/skills/kafka-iac-deployment/SKILL.md +449 -0
- package/plugins/specweave-kafka/skills/kafka-kubernetes/SKILL.md +667 -0
- package/plugins/specweave-kafka/skills/kafka-mcp-integration/SKILL.md +273 -0
- package/plugins/specweave-kafka/skills/kafka-observability/SKILL.md +576 -0
- package/plugins/specweave-kafka/templates/config/broker-production.properties +254 -0
- package/plugins/specweave-kafka/templates/config/consumer-low-latency.properties +112 -0
- package/plugins/specweave-kafka/templates/config/producer-high-throughput.properties +120 -0
- package/plugins/specweave-kafka/templates/migration/mirrormaker2-config.properties +234 -0
- package/plugins/specweave-kafka/templates/monitoring/grafana/multi-cluster-dashboard.json +686 -0
- package/plugins/specweave-kafka/terraform/apache-kafka/main.tf +347 -0
- package/plugins/specweave-kafka/terraform/apache-kafka/outputs.tf +107 -0
- package/plugins/specweave-kafka/terraform/apache-kafka/templates/kafka-broker-init.sh.tpl +216 -0
- package/plugins/specweave-kafka/terraform/apache-kafka/variables.tf +156 -0
- package/plugins/specweave-kafka/terraform/aws-msk/main.tf +362 -0
- package/plugins/specweave-kafka/terraform/aws-msk/outputs.tf +93 -0
- package/plugins/specweave-kafka/terraform/aws-msk/templates/server.properties.tpl +32 -0
- package/plugins/specweave-kafka/terraform/aws-msk/variables.tf +235 -0
- package/plugins/specweave-kafka/terraform/azure-event-hubs/main.tf +281 -0
- package/plugins/specweave-kafka/terraform/azure-event-hubs/outputs.tf +118 -0
- package/plugins/specweave-kafka/terraform/azure-event-hubs/variables.tf +148 -0
- package/plugins/specweave-kafka/tsconfig.json +21 -0
- package/plugins/specweave-kafka-streams/.claude-plugin/plugin.json +23 -0
- package/plugins/specweave-kafka-streams/README.md +310 -0
- package/plugins/specweave-kafka-streams/skills/kafka-streams-topology/SKILL.md +539 -0
- package/plugins/specweave-n8n/.claude-plugin/plugin.json +22 -0
- package/plugins/specweave-n8n/README.md +354 -0
- package/plugins/specweave-n8n/skills/n8n-kafka-workflows/SKILL.md +504 -0
- package/plugins/specweave-release/commands/specweave-release-platform.md +1 -1
- package/plugins/specweave-release/hooks/post-task-completion.sh +2 -2
- package/src/templates/AGENTS.md.template +601 -7
- package/src/templates/CLAUDE.md.template +188 -88
- package/plugins/specweave-ado/commands/specweave-ado-sync-spec.md +0 -255
- package/plugins/specweave-github/commands/specweave-github-sync-epic.md +0 -248
- package/plugins/specweave-github/commands/specweave-github-sync-from.md +0 -147
- package/plugins/specweave-github/commands/specweave-github-sync-spec.md +0 -208
- package/plugins/specweave-github/commands/specweave-github-sync-tasks.md +0 -530
- package/plugins/specweave-jira/commands/specweave-jira-sync-epic.md +0 -267
- package/plugins/specweave-jira/commands/specweave-jira-sync-spec.md +0 -240
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { readFile } from "fs/promises";
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import * as yaml from "yaml";
|
|
5
|
+
class CompletionCalculator {
|
|
6
|
+
constructor(projectRoot) {
|
|
7
|
+
this.projectRoot = projectRoot;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Calculate ACTUAL completion from markdown checkboxes
|
|
11
|
+
*
|
|
12
|
+
* Returns true only if:
|
|
13
|
+
* - All ACs have [x] (not [ ])
|
|
14
|
+
* - All Tasks have **Status**: [x] (not [ ])
|
|
15
|
+
* - At least 1 AC exists (no empty user stories)
|
|
16
|
+
*
|
|
17
|
+
* @param userStoryPath - Path to us-*.md file
|
|
18
|
+
* @returns Completion status with detailed metrics
|
|
19
|
+
*/
|
|
20
|
+
async calculateCompletion(userStoryPath) {
|
|
21
|
+
const content = await readFile(userStoryPath, "utf-8");
|
|
22
|
+
const frontmatter = this.parseUserStoryFrontmatter(content);
|
|
23
|
+
const acs = this.extractAcceptanceCriteria(content);
|
|
24
|
+
const tasks = await this.extractTasks(content, frontmatter.id);
|
|
25
|
+
const acsCompleted = acs.filter((ac) => ac.completed).length;
|
|
26
|
+
const tasksCompleted = tasks.filter((t) => t.completed).length;
|
|
27
|
+
const overallComplete = acs.length > 0 && acsCompleted === acs.length && (tasks.length === 0 || tasksCompleted === tasks.length);
|
|
28
|
+
return {
|
|
29
|
+
acsTotal: acs.length,
|
|
30
|
+
acsCompleted,
|
|
31
|
+
acsPercentage: acs.length > 0 ? acsCompleted / acs.length * 100 : 0,
|
|
32
|
+
tasksTotal: tasks.length,
|
|
33
|
+
tasksCompleted,
|
|
34
|
+
tasksPercentage: tasks.length > 0 ? tasksCompleted / tasks.length * 100 : 0,
|
|
35
|
+
overallComplete,
|
|
36
|
+
blockingAcs: acs.filter((ac) => !ac.completed).map((ac) => ac.id),
|
|
37
|
+
blockingTasks: tasks.filter((t) => !t.completed).map((t) => t.id)
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Parse User Story frontmatter
|
|
42
|
+
*/
|
|
43
|
+
parseUserStoryFrontmatter(content) {
|
|
44
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
45
|
+
if (!match) {
|
|
46
|
+
throw new Error("Missing YAML frontmatter in user story");
|
|
47
|
+
}
|
|
48
|
+
return yaml.parse(match[1]);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Extract Acceptance Criteria with checkbox state
|
|
52
|
+
*
|
|
53
|
+
* Supports TWO formats:
|
|
54
|
+
* - Format 1 (preferred): AC-US1-01, AC-US1-02 (project-specific)
|
|
55
|
+
* - Format 2 (legacy): AC-001, AC-002, AC-020 (global)
|
|
56
|
+
*
|
|
57
|
+
* Patterns:
|
|
58
|
+
* - [x] **AC-US1-01**: Description (completed)
|
|
59
|
+
* - [ ] **AC-US1-01**: Description (not completed)
|
|
60
|
+
* - **AC-US1-01**: Description (no checkbox, default to not completed)
|
|
61
|
+
*/
|
|
62
|
+
extractAcceptanceCriteria(content) {
|
|
63
|
+
const criteria = [];
|
|
64
|
+
const acMatch = content.match(/##\s*Acceptance Criteria\s*\n+([\s\S]*?)(?=\n##|$)/i);
|
|
65
|
+
if (!acMatch) {
|
|
66
|
+
return criteria;
|
|
67
|
+
}
|
|
68
|
+
const acSection = acMatch[1];
|
|
69
|
+
const acPatternWithCheckbox = /(?:^|\n)\s*[-*]\s+\[([x ])\]\s+\*\*([A-Z]+-(?:[A-Z]+\d+-)?(\d+))\*\*:\s*([^\n]+)/g;
|
|
70
|
+
const acPatternNoCheckbox = /(?:^|\n)\s*[-*]?\s*\*\*([A-Z]+-(?:[A-Z]+\d+-)?(\d+))\*\*:\s*([^\n]+)/g;
|
|
71
|
+
let match;
|
|
72
|
+
let foundAny = false;
|
|
73
|
+
while ((match = acPatternWithCheckbox.exec(acSection)) !== null) {
|
|
74
|
+
foundAny = true;
|
|
75
|
+
criteria.push({
|
|
76
|
+
id: match[2],
|
|
77
|
+
// e.g., "AC-US1-01" or "AC-020"
|
|
78
|
+
description: match[4].trim(),
|
|
79
|
+
completed: match[1] === "x"
|
|
80
|
+
// ✅ Read checkbox state from source!
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
if (!foundAny) {
|
|
84
|
+
while ((match = acPatternNoCheckbox.exec(acSection)) !== null) {
|
|
85
|
+
criteria.push({
|
|
86
|
+
id: match[1],
|
|
87
|
+
// e.g., "AC-US1-01" or "AC-020"
|
|
88
|
+
description: match[3].trim(),
|
|
89
|
+
completed: false
|
|
90
|
+
// Default to not completed
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return criteria;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Extract tasks from increment's tasks.md that map to this User Story
|
|
98
|
+
*
|
|
99
|
+
* Process:
|
|
100
|
+
* 1. Find increment link in user story's "Implementation" section
|
|
101
|
+
* 2. Read increment's tasks.md
|
|
102
|
+
* 3. Filter tasks that reference this user story's ACs
|
|
103
|
+
* 4. Extract completion status from **Status**: [x] or [ ]
|
|
104
|
+
*/
|
|
105
|
+
async extractTasks(userStoryContent, userStoryId) {
|
|
106
|
+
const tasks = [];
|
|
107
|
+
const implMatch = userStoryContent.match(/##\s*Implementation\s*\n+([\s\S]*?)(?=\n##|$)/i);
|
|
108
|
+
if (!implMatch) {
|
|
109
|
+
return tasks;
|
|
110
|
+
}
|
|
111
|
+
const implSection = implMatch[1];
|
|
112
|
+
const incrementMatch = implSection.match(/\*\*Increment\*\*:\s*\[([^\]]+)\]/);
|
|
113
|
+
if (!incrementMatch) {
|
|
114
|
+
return tasks;
|
|
115
|
+
}
|
|
116
|
+
const incrementId = incrementMatch[1];
|
|
117
|
+
const tasksPath = path.join(
|
|
118
|
+
this.projectRoot,
|
|
119
|
+
".specweave",
|
|
120
|
+
"increments",
|
|
121
|
+
incrementId,
|
|
122
|
+
"tasks.md"
|
|
123
|
+
);
|
|
124
|
+
if (!existsSync(tasksPath)) {
|
|
125
|
+
return tasks;
|
|
126
|
+
}
|
|
127
|
+
const tasksContent = await readFile(tasksPath, "utf-8");
|
|
128
|
+
const taskPattern = /###?\s+(T-\d+):\s*([^\n]+)\n([\s\S]*?)(?=\n###?\s+T-\d+:|$)/g;
|
|
129
|
+
let match;
|
|
130
|
+
while ((match = taskPattern.exec(tasksContent)) !== null) {
|
|
131
|
+
const taskId = match[1];
|
|
132
|
+
const taskTitle = match[2].trim();
|
|
133
|
+
const taskBody = match[3];
|
|
134
|
+
const acMatch = taskBody.match(/\*\*AC\*\*:\s*([^\n]+)/);
|
|
135
|
+
if (!acMatch) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
const acList = acMatch[1].trim();
|
|
139
|
+
const acIds = acList.split(",").map((ac) => ac.trim());
|
|
140
|
+
const belongsToThisUS = acIds.some((acId) => {
|
|
141
|
+
const usMatch = acId.match(/AC-([A-Z]+\d+)-/);
|
|
142
|
+
if (!usMatch) return false;
|
|
143
|
+
const extractedUsId = usMatch[1];
|
|
144
|
+
const extractedNum = extractedUsId.replace(/^US/, "");
|
|
145
|
+
const normalizedExtracted = `US-${extractedNum.padStart(3, "0")}`;
|
|
146
|
+
const currentNum = userStoryId.replace(/^US-?/, "");
|
|
147
|
+
const normalizedCurrent = `US-${currentNum.padStart(3, "0")}`;
|
|
148
|
+
return normalizedExtracted === normalizedCurrent;
|
|
149
|
+
});
|
|
150
|
+
if (!belongsToThisUS) {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
const statusMatch = taskBody.match(/\*\*Status\*\*:\s*\[([x ])\]/);
|
|
154
|
+
const completed = statusMatch ? statusMatch[1] === "x" : false;
|
|
155
|
+
tasks.push({
|
|
156
|
+
id: taskId,
|
|
157
|
+
title: taskTitle,
|
|
158
|
+
completed,
|
|
159
|
+
// ✅ Now reads actual completion status!
|
|
160
|
+
userStories: acIds
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
return tasks;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Build completion comment for GitHub issue closure
|
|
167
|
+
*
|
|
168
|
+
* Used when closing issue after verification
|
|
169
|
+
*/
|
|
170
|
+
buildCompletionComment(completion) {
|
|
171
|
+
return `\u2705 **User Story Verified Complete**
|
|
172
|
+
|
|
173
|
+
**Completion Status**:
|
|
174
|
+
- \u2705 Acceptance Criteria: ${completion.acsCompleted}/${completion.acsTotal} (100%)
|
|
175
|
+
- \u2705 Implementation Tasks: ${completion.tasksCompleted}/${completion.tasksTotal} (100%)
|
|
176
|
+
|
|
177
|
+
All work has been verified and completed. This issue is now closed.
|
|
178
|
+
|
|
179
|
+
\u{1F916} Auto-verified by SpecWeave AC Completion Gate`;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Build progress comment for GitHub issue update
|
|
183
|
+
*
|
|
184
|
+
* Used when issue stays open (not 100% complete)
|
|
185
|
+
*/
|
|
186
|
+
buildProgressComment(completion) {
|
|
187
|
+
const sections = [];
|
|
188
|
+
sections.push("\u{1F4CA} **Progress Update**");
|
|
189
|
+
sections.push("");
|
|
190
|
+
const acIcon = completion.acsPercentage === 100 ? "\u2705" : "\u{1F504}";
|
|
191
|
+
sections.push(
|
|
192
|
+
`${acIcon} **Acceptance Criteria**: ${completion.acsCompleted}/${completion.acsTotal} (${completion.acsPercentage.toFixed(0)}%)`
|
|
193
|
+
);
|
|
194
|
+
if (completion.blockingAcs.length > 0) {
|
|
195
|
+
sections.push("");
|
|
196
|
+
sections.push("**Incomplete ACs**:");
|
|
197
|
+
for (const acId of completion.blockingAcs) {
|
|
198
|
+
sections.push(`- [ ] ${acId}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
sections.push("");
|
|
202
|
+
const taskIcon = completion.tasksPercentage === 100 ? "\u2705" : "\u{1F504}";
|
|
203
|
+
sections.push(
|
|
204
|
+
`${taskIcon} **Implementation Tasks**: ${completion.tasksCompleted}/${completion.tasksTotal} (${completion.tasksPercentage.toFixed(0)}%)`
|
|
205
|
+
);
|
|
206
|
+
if (completion.blockingTasks.length > 0) {
|
|
207
|
+
sections.push("");
|
|
208
|
+
sections.push("**Incomplete Tasks**:");
|
|
209
|
+
for (const taskId of completion.blockingTasks) {
|
|
210
|
+
sections.push(`- [ ] ${taskId}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
sections.push("");
|
|
214
|
+
sections.push("---");
|
|
215
|
+
sections.push("\u{1F916} Auto-updated by SpecWeave AC Completion Gate");
|
|
216
|
+
return sections.join("\n");
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Build reopen comment for GitHub issue
|
|
220
|
+
*
|
|
221
|
+
* Used when reopening prematurely closed issue
|
|
222
|
+
*/
|
|
223
|
+
buildReopenComment(completion, reason = "Work verification failed") {
|
|
224
|
+
const sections = [];
|
|
225
|
+
sections.push(`\u{1F504} **Reopening Issue - ${reason}**`);
|
|
226
|
+
sections.push("");
|
|
227
|
+
sections.push("**Current Status**:");
|
|
228
|
+
sections.push(
|
|
229
|
+
`- Acceptance Criteria: ${completion.acsCompleted}/${completion.acsTotal} (${completion.acsPercentage.toFixed(0)}%)`
|
|
230
|
+
);
|
|
231
|
+
sections.push(
|
|
232
|
+
`- Implementation Tasks: ${completion.tasksCompleted}/${completion.tasksTotal} (${completion.tasksPercentage.toFixed(0)}%)`
|
|
233
|
+
);
|
|
234
|
+
const totalBlocking = completion.blockingAcs.length + completion.blockingTasks.length;
|
|
235
|
+
if (totalBlocking > 0) {
|
|
236
|
+
sections.push("");
|
|
237
|
+
sections.push(`**Blocking Items** (${totalBlocking}):`);
|
|
238
|
+
if (completion.blockingAcs.length > 0) {
|
|
239
|
+
sections.push("");
|
|
240
|
+
sections.push("**Acceptance Criteria**:");
|
|
241
|
+
for (const acId of completion.blockingAcs) {
|
|
242
|
+
sections.push(`- [ ] ${acId}`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (completion.blockingTasks.length > 0) {
|
|
246
|
+
sections.push("");
|
|
247
|
+
sections.push("**Implementation Tasks**:");
|
|
248
|
+
for (const taskId of completion.blockingTasks) {
|
|
249
|
+
sections.push(`- [ ] ${taskId}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
sections.push("");
|
|
254
|
+
sections.push("\u26A0\uFE0F This issue cannot be closed until all ACs and tasks are verified complete.");
|
|
255
|
+
sections.push("");
|
|
256
|
+
sections.push("\u{1F916} Auto-reopened by SpecWeave AC Completion Gate");
|
|
257
|
+
return sections.join("\n");
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
export {
|
|
261
|
+
CompletionCalculator
|
|
262
|
+
};
|
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Completion Calculator - Verifies actual work completion from markdown checkboxes
|
|
3
|
+
*
|
|
4
|
+
* CRITICAL FIX: Prevents premature GitHub issue closure by verifying actual
|
|
5
|
+
* AC and Task completion state, not just frontmatter status.
|
|
6
|
+
*
|
|
7
|
+
* Problem:
|
|
8
|
+
* - OLD: Issues closed based on frontmatter `status: complete`
|
|
9
|
+
* - Issue #574: Closed with 0/5 ACs complete!
|
|
10
|
+
*
|
|
11
|
+
* Solution:
|
|
12
|
+
* - NEW: Parse actual checkbox states ([x] vs [ ])
|
|
13
|
+
* - Close ONLY when ALL ACs and ALL tasks are verified [x]
|
|
14
|
+
*
|
|
15
|
+
* Architecture:
|
|
16
|
+
* - Acceptance Criteria: Read from us-*.md files
|
|
17
|
+
* - Tasks: Read from increment's tasks.md
|
|
18
|
+
* - Verification Gate: 100% completion required
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { readFile } from 'fs/promises';
|
|
22
|
+
import { existsSync } from 'fs';
|
|
23
|
+
import * as path from 'path';
|
|
24
|
+
import * as yaml from 'yaml';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Acceptance Criteria with completion status
|
|
28
|
+
*/
|
|
29
|
+
export interface AcceptanceCriteria {
|
|
30
|
+
id: string; // e.g., "AC-US1-01" or "AC-020"
|
|
31
|
+
description: string;
|
|
32
|
+
completed: boolean; // ✅ Read from [x] or [ ] checkbox
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Implementation Task with completion status
|
|
37
|
+
*/
|
|
38
|
+
export interface Task {
|
|
39
|
+
id: string; // e.g., "T-001"
|
|
40
|
+
title: string;
|
|
41
|
+
completed: boolean; // ✅ Read from **Status**: [x] or [ ]
|
|
42
|
+
userStories: string[]; // ACs this task implements (e.g., ["AC-US1-01", "AC-US1-02"])
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Complete verification status
|
|
47
|
+
*/
|
|
48
|
+
export interface CompletionStatus {
|
|
49
|
+
// Acceptance Criteria metrics
|
|
50
|
+
acsTotal: number;
|
|
51
|
+
acsCompleted: number;
|
|
52
|
+
acsPercentage: number;
|
|
53
|
+
|
|
54
|
+
// Task metrics
|
|
55
|
+
tasksTotal: number;
|
|
56
|
+
tasksCompleted: number;
|
|
57
|
+
tasksPercentage: number;
|
|
58
|
+
|
|
59
|
+
// Overall completion gate
|
|
60
|
+
overallComplete: boolean; // true ONLY if ALL ACs AND tasks are [x]
|
|
61
|
+
|
|
62
|
+
// Blocking items (for reopen)
|
|
63
|
+
blockingAcs: string[]; // List of incomplete AC-IDs
|
|
64
|
+
blockingTasks: string[]; // List of incomplete Task-IDs
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* User Story Frontmatter
|
|
69
|
+
*/
|
|
70
|
+
interface UserStoryFrontmatter {
|
|
71
|
+
id: string;
|
|
72
|
+
feature: string;
|
|
73
|
+
title: string;
|
|
74
|
+
status: 'complete' | 'active' | 'planning' | 'not-started';
|
|
75
|
+
project?: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export class CompletionCalculator {
|
|
79
|
+
private projectRoot: string;
|
|
80
|
+
|
|
81
|
+
constructor(projectRoot: string) {
|
|
82
|
+
this.projectRoot = projectRoot;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Calculate ACTUAL completion from markdown checkboxes
|
|
87
|
+
*
|
|
88
|
+
* Returns true only if:
|
|
89
|
+
* - All ACs have [x] (not [ ])
|
|
90
|
+
* - All Tasks have **Status**: [x] (not [ ])
|
|
91
|
+
* - At least 1 AC exists (no empty user stories)
|
|
92
|
+
*
|
|
93
|
+
* @param userStoryPath - Path to us-*.md file
|
|
94
|
+
* @returns Completion status with detailed metrics
|
|
95
|
+
*/
|
|
96
|
+
async calculateCompletion(userStoryPath: string): Promise<CompletionStatus> {
|
|
97
|
+
// Step 1: Read and parse user story
|
|
98
|
+
const content = await readFile(userStoryPath, 'utf-8');
|
|
99
|
+
const frontmatter = this.parseUserStoryFrontmatter(content);
|
|
100
|
+
|
|
101
|
+
// Step 2: Extract Acceptance Criteria
|
|
102
|
+
const acs = this.extractAcceptanceCriteria(content);
|
|
103
|
+
|
|
104
|
+
// Step 3: Extract Tasks from increment (if linked)
|
|
105
|
+
const tasks = await this.extractTasks(content, frontmatter.id);
|
|
106
|
+
|
|
107
|
+
// Step 4: Calculate metrics
|
|
108
|
+
const acsCompleted = acs.filter((ac) => ac.completed).length;
|
|
109
|
+
const tasksCompleted = tasks.filter((t) => t.completed).length;
|
|
110
|
+
|
|
111
|
+
// Step 5: Determine overall completion
|
|
112
|
+
// MUST have:
|
|
113
|
+
// - At least 1 AC (no empty user stories)
|
|
114
|
+
// - ALL ACs completed
|
|
115
|
+
// - ALL Tasks completed (or no tasks if not implemented yet)
|
|
116
|
+
const overallComplete =
|
|
117
|
+
acs.length > 0 &&
|
|
118
|
+
acsCompleted === acs.length &&
|
|
119
|
+
(tasks.length === 0 || tasksCompleted === tasks.length);
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
acsTotal: acs.length,
|
|
123
|
+
acsCompleted,
|
|
124
|
+
acsPercentage: acs.length > 0 ? (acsCompleted / acs.length) * 100 : 0,
|
|
125
|
+
tasksTotal: tasks.length,
|
|
126
|
+
tasksCompleted,
|
|
127
|
+
tasksPercentage: tasks.length > 0 ? (tasksCompleted / tasks.length) * 100 : 0,
|
|
128
|
+
overallComplete,
|
|
129
|
+
blockingAcs: acs.filter((ac) => !ac.completed).map((ac) => ac.id),
|
|
130
|
+
blockingTasks: tasks.filter((t) => !t.completed).map((t) => t.id),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Parse User Story frontmatter
|
|
136
|
+
*/
|
|
137
|
+
private parseUserStoryFrontmatter(content: string): UserStoryFrontmatter {
|
|
138
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
139
|
+
|
|
140
|
+
if (!match) {
|
|
141
|
+
throw new Error('Missing YAML frontmatter in user story');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return yaml.parse(match[1]) as UserStoryFrontmatter;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Extract Acceptance Criteria with checkbox state
|
|
149
|
+
*
|
|
150
|
+
* Supports TWO formats:
|
|
151
|
+
* - Format 1 (preferred): AC-US1-01, AC-US1-02 (project-specific)
|
|
152
|
+
* - Format 2 (legacy): AC-001, AC-002, AC-020 (global)
|
|
153
|
+
*
|
|
154
|
+
* Patterns:
|
|
155
|
+
* - [x] **AC-US1-01**: Description (completed)
|
|
156
|
+
* - [ ] **AC-US1-01**: Description (not completed)
|
|
157
|
+
* - **AC-US1-01**: Description (no checkbox, default to not completed)
|
|
158
|
+
*/
|
|
159
|
+
private extractAcceptanceCriteria(content: string): AcceptanceCriteria[] {
|
|
160
|
+
const criteria: AcceptanceCriteria[] = [];
|
|
161
|
+
|
|
162
|
+
// Look for "Acceptance Criteria" section
|
|
163
|
+
const acMatch = content.match(/##\s*Acceptance Criteria\s*\n+([\s\S]*?)(?=\n##|$)/i);
|
|
164
|
+
|
|
165
|
+
if (!acMatch) {
|
|
166
|
+
return criteria;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const acSection = acMatch[1];
|
|
170
|
+
|
|
171
|
+
// Pattern with checkboxes (PREFERRED)
|
|
172
|
+
// Matches: - [x] **AC-US1-01**: Description
|
|
173
|
+
// - [ ] **AC-020**: Description
|
|
174
|
+
const acPatternWithCheckbox =
|
|
175
|
+
/(?:^|\n)\s*[-*]\s+\[([x ])\]\s+\*\*([A-Z]+-(?:[A-Z]+\d+-)?(\d+))\*\*:\s*([^\n]+)/g;
|
|
176
|
+
|
|
177
|
+
// Pattern without checkboxes (FALLBACK)
|
|
178
|
+
// Matches: - **AC-US1-01**: Description
|
|
179
|
+
const acPatternNoCheckbox =
|
|
180
|
+
/(?:^|\n)\s*[-*]?\s*\*\*([A-Z]+-(?:[A-Z]+\d+-)?(\d+))\*\*:\s*([^\n]+)/g;
|
|
181
|
+
|
|
182
|
+
// First try pattern with checkboxes
|
|
183
|
+
let match;
|
|
184
|
+
let foundAny = false;
|
|
185
|
+
|
|
186
|
+
while ((match = acPatternWithCheckbox.exec(acSection)) !== null) {
|
|
187
|
+
foundAny = true;
|
|
188
|
+
criteria.push({
|
|
189
|
+
id: match[2], // e.g., "AC-US1-01" or "AC-020"
|
|
190
|
+
description: match[4].trim(),
|
|
191
|
+
completed: match[1] === 'x', // ✅ Read checkbox state from source!
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// If no checkboxes found, try pattern without checkboxes
|
|
196
|
+
if (!foundAny) {
|
|
197
|
+
while ((match = acPatternNoCheckbox.exec(acSection)) !== null) {
|
|
198
|
+
criteria.push({
|
|
199
|
+
id: match[1], // e.g., "AC-US1-01" or "AC-020"
|
|
200
|
+
description: match[3].trim(),
|
|
201
|
+
completed: false, // Default to not completed
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return criteria;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Extract tasks from increment's tasks.md that map to this User Story
|
|
211
|
+
*
|
|
212
|
+
* Process:
|
|
213
|
+
* 1. Find increment link in user story's "Implementation" section
|
|
214
|
+
* 2. Read increment's tasks.md
|
|
215
|
+
* 3. Filter tasks that reference this user story's ACs
|
|
216
|
+
* 4. Extract completion status from **Status**: [x] or [ ]
|
|
217
|
+
*/
|
|
218
|
+
private async extractTasks(userStoryContent: string, userStoryId: string): Promise<Task[]> {
|
|
219
|
+
const tasks: Task[] = [];
|
|
220
|
+
|
|
221
|
+
// Look for "Implementation" section with increment link
|
|
222
|
+
const implMatch = userStoryContent.match(/##\s*Implementation\s*\n+([\s\S]*?)(?=\n##|$)/i);
|
|
223
|
+
|
|
224
|
+
if (!implMatch) {
|
|
225
|
+
return tasks; // No implementation yet
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const implSection = implMatch[1];
|
|
229
|
+
|
|
230
|
+
// Extract increment ID from Implementation section
|
|
231
|
+
// Pattern: **Increment**: [0031-external-tool-status-sync](...)
|
|
232
|
+
const incrementMatch = implSection.match(/\*\*Increment\*\*:\s*\[([^\]]+)\]/);
|
|
233
|
+
|
|
234
|
+
if (!incrementMatch) {
|
|
235
|
+
return tasks; // No increment linked
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const incrementId = incrementMatch[1];
|
|
239
|
+
|
|
240
|
+
// Try to read increment's tasks.md
|
|
241
|
+
const tasksPath = path.join(
|
|
242
|
+
this.projectRoot,
|
|
243
|
+
'.specweave',
|
|
244
|
+
'increments',
|
|
245
|
+
incrementId,
|
|
246
|
+
'tasks.md'
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
if (!existsSync(tasksPath)) {
|
|
250
|
+
return tasks; // Increment has no tasks.md yet
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const tasksContent = await readFile(tasksPath, 'utf-8');
|
|
254
|
+
|
|
255
|
+
// Extract tasks that reference this User Story via AC-IDs
|
|
256
|
+
// Pattern:
|
|
257
|
+
// ### T-001: Task Title
|
|
258
|
+
// **User Story**: ...
|
|
259
|
+
// **Status**: [x] (100% - Completed) or [ ] (0% - Not started)
|
|
260
|
+
// **AC**: AC-US1-01, AC-US1-02
|
|
261
|
+
const taskPattern = /###?\s+(T-\d+):\s*([^\n]+)\n([\s\S]*?)(?=\n###?\s+T-\d+:|$)/g;
|
|
262
|
+
let match;
|
|
263
|
+
|
|
264
|
+
while ((match = taskPattern.exec(tasksContent)) !== null) {
|
|
265
|
+
const taskId = match[1];
|
|
266
|
+
const taskTitle = match[2].trim();
|
|
267
|
+
const taskBody = match[3];
|
|
268
|
+
|
|
269
|
+
// Extract AC list
|
|
270
|
+
const acMatch = taskBody.match(/\*\*AC\*\*:\s*([^\n]+)/);
|
|
271
|
+
if (!acMatch) {
|
|
272
|
+
continue; // Skip tasks without AC field
|
|
273
|
+
}
|
|
274
|
+
const acList = acMatch[1].trim();
|
|
275
|
+
|
|
276
|
+
// Check if any AC in this task belongs to current User Story
|
|
277
|
+
// AC-US1-01 → US-001
|
|
278
|
+
// AC-US001-01 → US-001
|
|
279
|
+
const acIds = acList.split(',').map((ac) => ac.trim());
|
|
280
|
+
const belongsToThisUS = acIds.some((acId) => {
|
|
281
|
+
// Extract US ID from AC-ID
|
|
282
|
+
// AC-US1-01 → US1 → US-001
|
|
283
|
+
// AC-US001-01 → US001 → US-001
|
|
284
|
+
const usMatch = acId.match(/AC-([A-Z]+\d+)-/);
|
|
285
|
+
if (!usMatch) return false;
|
|
286
|
+
|
|
287
|
+
// Normalize to US-XXX format (pad with zeros)
|
|
288
|
+
const extractedUsId = usMatch[1]; // e.g., "US1" or "US001"
|
|
289
|
+
const extractedNum = extractedUsId.replace(/^US/, ''); // "1" or "001"
|
|
290
|
+
const normalizedExtracted = `US-${extractedNum.padStart(3, '0')}`; // "US-001"
|
|
291
|
+
|
|
292
|
+
const currentNum = userStoryId.replace(/^US-?/, ''); // "001" or "1"
|
|
293
|
+
const normalizedCurrent = `US-${currentNum.padStart(3, '0')}`; // "US-001"
|
|
294
|
+
|
|
295
|
+
return normalizedExtracted === normalizedCurrent;
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
if (!belongsToThisUS) {
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// ✅ Extract completion status from **Status**: [x] or [ ]
|
|
303
|
+
const statusMatch = taskBody.match(/\*\*Status\*\*:\s*\[([x ])\]/);
|
|
304
|
+
const completed = statusMatch ? statusMatch[1] === 'x' : false;
|
|
305
|
+
|
|
306
|
+
tasks.push({
|
|
307
|
+
id: taskId,
|
|
308
|
+
title: taskTitle,
|
|
309
|
+
completed, // ✅ Now reads actual completion status!
|
|
310
|
+
userStories: acIds,
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return tasks;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Build completion comment for GitHub issue closure
|
|
319
|
+
*
|
|
320
|
+
* Used when closing issue after verification
|
|
321
|
+
*/
|
|
322
|
+
buildCompletionComment(completion: CompletionStatus): string {
|
|
323
|
+
return `✅ **User Story Verified Complete**
|
|
324
|
+
|
|
325
|
+
**Completion Status**:
|
|
326
|
+
- ✅ Acceptance Criteria: ${completion.acsCompleted}/${completion.acsTotal} (100%)
|
|
327
|
+
- ✅ Implementation Tasks: ${completion.tasksCompleted}/${completion.tasksTotal} (100%)
|
|
328
|
+
|
|
329
|
+
All work has been verified and completed. This issue is now closed.
|
|
330
|
+
|
|
331
|
+
🤖 Auto-verified by SpecWeave AC Completion Gate`;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Build progress comment for GitHub issue update
|
|
336
|
+
*
|
|
337
|
+
* Used when issue stays open (not 100% complete)
|
|
338
|
+
*/
|
|
339
|
+
buildProgressComment(completion: CompletionStatus): string {
|
|
340
|
+
const sections: string[] = [];
|
|
341
|
+
|
|
342
|
+
sections.push('📊 **Progress Update**');
|
|
343
|
+
sections.push('');
|
|
344
|
+
|
|
345
|
+
// AC progress
|
|
346
|
+
const acIcon = completion.acsPercentage === 100 ? '✅' : '🔄';
|
|
347
|
+
sections.push(
|
|
348
|
+
`${acIcon} **Acceptance Criteria**: ${completion.acsCompleted}/${completion.acsTotal} (${completion.acsPercentage.toFixed(0)}%)`
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
if (completion.blockingAcs.length > 0) {
|
|
352
|
+
sections.push('');
|
|
353
|
+
sections.push('**Incomplete ACs**:');
|
|
354
|
+
for (const acId of completion.blockingAcs) {
|
|
355
|
+
sections.push(`- [ ] ${acId}`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
sections.push('');
|
|
360
|
+
|
|
361
|
+
// Task progress
|
|
362
|
+
const taskIcon = completion.tasksPercentage === 100 ? '✅' : '🔄';
|
|
363
|
+
sections.push(
|
|
364
|
+
`${taskIcon} **Implementation Tasks**: ${completion.tasksCompleted}/${completion.tasksTotal} (${completion.tasksPercentage.toFixed(0)}%)`
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
if (completion.blockingTasks.length > 0) {
|
|
368
|
+
sections.push('');
|
|
369
|
+
sections.push('**Incomplete Tasks**:');
|
|
370
|
+
for (const taskId of completion.blockingTasks) {
|
|
371
|
+
sections.push(`- [ ] ${taskId}`);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
sections.push('');
|
|
376
|
+
sections.push('---');
|
|
377
|
+
sections.push('🤖 Auto-updated by SpecWeave AC Completion Gate');
|
|
378
|
+
|
|
379
|
+
return sections.join('\n');
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Build reopen comment for GitHub issue
|
|
384
|
+
*
|
|
385
|
+
* Used when reopening prematurely closed issue
|
|
386
|
+
*/
|
|
387
|
+
buildReopenComment(
|
|
388
|
+
completion: CompletionStatus,
|
|
389
|
+
reason: string = 'Work verification failed'
|
|
390
|
+
): string {
|
|
391
|
+
const sections: string[] = [];
|
|
392
|
+
|
|
393
|
+
sections.push(`🔄 **Reopening Issue - ${reason}**`);
|
|
394
|
+
sections.push('');
|
|
395
|
+
|
|
396
|
+
sections.push('**Current Status**:');
|
|
397
|
+
sections.push(
|
|
398
|
+
`- Acceptance Criteria: ${completion.acsCompleted}/${completion.acsTotal} (${completion.acsPercentage.toFixed(0)}%)`
|
|
399
|
+
);
|
|
400
|
+
sections.push(
|
|
401
|
+
`- Implementation Tasks: ${completion.tasksCompleted}/${completion.tasksTotal} (${completion.tasksPercentage.toFixed(0)}%)`
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
const totalBlocking = completion.blockingAcs.length + completion.blockingTasks.length;
|
|
405
|
+
|
|
406
|
+
if (totalBlocking > 0) {
|
|
407
|
+
sections.push('');
|
|
408
|
+
sections.push(`**Blocking Items** (${totalBlocking}):`);
|
|
409
|
+
|
|
410
|
+
if (completion.blockingAcs.length > 0) {
|
|
411
|
+
sections.push('');
|
|
412
|
+
sections.push('**Acceptance Criteria**:');
|
|
413
|
+
for (const acId of completion.blockingAcs) {
|
|
414
|
+
sections.push(`- [ ] ${acId}`);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (completion.blockingTasks.length > 0) {
|
|
419
|
+
sections.push('');
|
|
420
|
+
sections.push('**Implementation Tasks**:');
|
|
421
|
+
for (const taskId of completion.blockingTasks) {
|
|
422
|
+
sections.push(`- [ ] ${taskId}`);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
sections.push('');
|
|
428
|
+
sections.push('⚠️ This issue cannot be closed until all ACs and tasks are verified complete.');
|
|
429
|
+
sections.push('');
|
|
430
|
+
sections.push('🤖 Auto-reopened by SpecWeave AC Completion Gate');
|
|
431
|
+
|
|
432
|
+
return sections.join('\n');
|
|
433
|
+
}
|
|
434
|
+
}
|