specweave 0.18.0 → 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/locales/de/.gitkeep +0 -0
- package/dist/locales/de/cli.json +108 -0
- package/dist/locales/en/cli.json +287 -0
- package/dist/locales/en/errors.json +7 -0
- package/dist/locales/en/templates.json +6 -0
- package/dist/locales/es/.gitkeep +0 -0
- package/dist/locales/es/cli.json +41 -0
- package/dist/locales/fr/.gitkeep +0 -0
- package/dist/locales/fr/cli.json +108 -0
- package/dist/locales/ja/.gitkeep +0 -0
- package/dist/locales/ja/cli.json +108 -0
- package/dist/locales/ko/.gitkeep +0 -0
- package/dist/locales/ko/cli.json +108 -0
- package/dist/locales/pt/.gitkeep +0 -0
- package/dist/locales/pt/cli.json +108 -0
- package/dist/locales/ru/.gitkeep +0 -0
- package/dist/locales/ru/cli.json +269 -0
- package/dist/locales/zh/.gitkeep +0 -0
- package/dist/locales/zh/cli.json +108 -0
- 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-ado/lib/enhanced-ado-sync.d.ts +25 -0
- package/dist/plugins/specweave-ado/lib/enhanced-ado-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-ado/lib/enhanced-ado-sync.js +191 -0
- package/dist/plugins/specweave-ado/lib/enhanced-ado-sync.js.map +1 -0
- 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 +70 -0
- package/dist/plugins/specweave-github/lib/epic-content-builder.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/epic-content-builder.js +258 -0
- package/dist/plugins/specweave-github/lib/epic-content-builder.js.map +1 -0
- 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.d.ts +2 -2
- package/dist/plugins/specweave-github/lib/github-epic-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-epic-sync.js +20 -5
- 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 +28 -0
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.js +156 -0
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.js.map +1 -0
- 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/spec-parser.js +629 -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/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +107 -3
- package/dist/src/cli/commands/init.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 +32 -54
- package/dist/src/core/sync/enhanced-content-builder.d.ts.map +1 -1
- package/dist/src/core/sync/enhanced-content-builder.js +142 -138
- 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/sync/spec-content-sync.d.ts +88 -0
- package/dist/src/core/sync/spec-content-sync.d.ts.map +1 -0
- package/dist/src/core/sync/spec-content-sync.js +5 -0
- package/dist/src/core/sync/spec-content-sync.js.map +1 -0
- package/dist/src/core/sync/types.d.ts +52 -0
- package/dist/src/core/sync/types.d.ts.map +1 -0
- package/dist/src/core/sync/types.js +5 -0
- package/dist/src/core/sync/types.js.map +1 -0
- package/dist/src/core/types/config.d.ts +125 -0
- package/dist/src/core/types/config.d.ts.map +1 -1
- package/dist/src/core/types/config.js +25 -0
- package/dist/src/core/types/config.js.map +1 -1
- package/dist/src/core/types/increment-metadata.d.ts +10 -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/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +1 -1
- package/plugins/specweave/COMMANDS.md +13 -4
- package/plugins/specweave/agents/pm/AGENT.md +159 -12
- 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/commands/specweave.md +70 -405
- package/plugins/specweave/hooks/hooks.json +4 -0
- package/plugins/specweave/hooks/lib/sync-spec-content.sh +2 -2
- package/plugins/specweave/hooks/post-increment-planning.sh +26 -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-ado/lib/enhanced-ado-sync.js +170 -0
- 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 +42 -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 +265 -0
- package/plugins/specweave-github/lib/epic-content-builder.ts +376 -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.js +23 -24
- package/plugins/specweave-github/lib/github-epic-sync.ts +30 -5
- 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,568 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Feature Sync - Universal Hierarchy Implementation
|
|
3
|
+
*
|
|
4
|
+
* Architecture (CORRECT):
|
|
5
|
+
* - Feature (FS-033) → GitHub Milestone (Container)
|
|
6
|
+
* - User Story (US-001, US-002, etc.) → GitHub Issue (Trackable item)
|
|
7
|
+
* - Tasks (T-001, T-002, etc.) → Checkboxes in User Story issue body
|
|
8
|
+
*
|
|
9
|
+
* This implements the TRUE Universal Hierarchy architecture for GitHub.
|
|
10
|
+
*
|
|
11
|
+
* Key Differences from old github-epic-sync.ts:
|
|
12
|
+
* - ❌ OLD: Feature/Increment → GitHub Issue (WRONG!)
|
|
13
|
+
* - ✅ NEW: User Story → GitHub Issue (CORRECT!)
|
|
14
|
+
* - ✅ Creates ONE issue PER user story file (not one per increment)
|
|
15
|
+
* - ✅ Reads us-*.md files from specs/{project}/FS-XXX/
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { readdir, readFile, writeFile } from 'fs/promises';
|
|
19
|
+
import { existsSync } from 'fs';
|
|
20
|
+
import * as path from 'path';
|
|
21
|
+
import * as yaml from 'yaml';
|
|
22
|
+
import { GitHubClientV2 } from './github-client-v2.js';
|
|
23
|
+
import { UserStoryIssueBuilder } from './user-story-issue-builder.js';
|
|
24
|
+
import { CompletionCalculator } from './completion-calculator.js';
|
|
25
|
+
import { execFileNoThrow } from '../../../src/utils/execFileNoThrow.js';
|
|
26
|
+
|
|
27
|
+
interface FeatureFrontmatter {
|
|
28
|
+
id: string;
|
|
29
|
+
title: string;
|
|
30
|
+
type: 'feature' | 'epic';
|
|
31
|
+
status: 'complete' | 'active' | 'planning' | 'archived';
|
|
32
|
+
projects?: string[];
|
|
33
|
+
created: string;
|
|
34
|
+
last_updated: string;
|
|
35
|
+
external_tools?: {
|
|
36
|
+
github?: {
|
|
37
|
+
type: 'milestone';
|
|
38
|
+
id: number | null;
|
|
39
|
+
url: string | null;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface UserStoryInfo {
|
|
45
|
+
id: string; // e.g., "US-001"
|
|
46
|
+
title: string;
|
|
47
|
+
filePath: string;
|
|
48
|
+
project: string;
|
|
49
|
+
status: string;
|
|
50
|
+
existingIssue?: number | null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export class GitHubFeatureSync {
|
|
54
|
+
private client: GitHubClientV2;
|
|
55
|
+
private specsDir: string;
|
|
56
|
+
private projectRoot: string;
|
|
57
|
+
private calculator: CompletionCalculator;
|
|
58
|
+
|
|
59
|
+
constructor(client: GitHubClientV2, specsDir: string, projectRoot: string) {
|
|
60
|
+
this.client = client;
|
|
61
|
+
this.specsDir = specsDir;
|
|
62
|
+
this.projectRoot = projectRoot;
|
|
63
|
+
this.calculator = new CompletionCalculator(projectRoot);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Sync Feature folder to GitHub (Milestone + User Story Issues)
|
|
68
|
+
*
|
|
69
|
+
* Process:
|
|
70
|
+
* 1. Create/update GitHub Milestone for Feature
|
|
71
|
+
* 2. Find all us-*.md files across all projects
|
|
72
|
+
* 3. Create/update GitHub Issue for EACH user story
|
|
73
|
+
* 4. Update frontmatter with GitHub issue links
|
|
74
|
+
*/
|
|
75
|
+
async syncFeatureToGitHub(featureId: string): Promise<{
|
|
76
|
+
milestoneNumber: number;
|
|
77
|
+
milestoneUrl: string;
|
|
78
|
+
issuesCreated: number;
|
|
79
|
+
issuesUpdated: number;
|
|
80
|
+
userStoriesProcessed: number;
|
|
81
|
+
}> {
|
|
82
|
+
console.log(`\n🔄 Syncing Feature ${featureId} to GitHub...`);
|
|
83
|
+
|
|
84
|
+
// 1. Load Feature FEATURE.md
|
|
85
|
+
const featureFolder = await this.findFeatureFolder(featureId);
|
|
86
|
+
if (!featureFolder) {
|
|
87
|
+
throw new Error(`Feature ${featureId} not found in ${this.specsDir}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const featurePath = path.join(featureFolder, 'FEATURE.md');
|
|
91
|
+
const featureData = await this.parseFeatureMd(featurePath);
|
|
92
|
+
|
|
93
|
+
console.log(` 📦 Feature: ${featureData.title}`);
|
|
94
|
+
console.log(` 📊 Status: ${featureData.status}`);
|
|
95
|
+
|
|
96
|
+
// 2. Create or update GitHub Milestone
|
|
97
|
+
let milestoneNumber = featureData.external_tools?.github?.id;
|
|
98
|
+
let milestoneUrl = featureData.external_tools?.github?.url;
|
|
99
|
+
|
|
100
|
+
if (!milestoneNumber) {
|
|
101
|
+
console.log(` 🚀 Creating GitHub Milestone...`);
|
|
102
|
+
const milestone = await this.createMilestone(featureData);
|
|
103
|
+
milestoneNumber = milestone.number;
|
|
104
|
+
milestoneUrl = milestone.url;
|
|
105
|
+
console.log(` ✅ Created Milestone #${milestoneNumber}`);
|
|
106
|
+
|
|
107
|
+
// Update FEATURE.md with Milestone ID
|
|
108
|
+
await this.updateFeatureMd(featurePath, {
|
|
109
|
+
type: 'milestone',
|
|
110
|
+
id: milestoneNumber,
|
|
111
|
+
url: milestoneUrl,
|
|
112
|
+
});
|
|
113
|
+
} else {
|
|
114
|
+
console.log(` ♻️ Using existing Milestone #${milestoneNumber}`);
|
|
115
|
+
milestoneUrl = featureData.external_tools?.github?.url || milestoneUrl;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// 3. Find all User Story files across all projects
|
|
119
|
+
const userStories = await this.findUserStories(featureId);
|
|
120
|
+
console.log(`\n 📝 Found ${userStories.length} User Stories to sync...`);
|
|
121
|
+
|
|
122
|
+
// 4. Sync each User Story as GitHub Issue
|
|
123
|
+
let issuesCreated = 0;
|
|
124
|
+
let issuesUpdated = 0;
|
|
125
|
+
|
|
126
|
+
for (const userStory of userStories) {
|
|
127
|
+
console.log(`\n 🔹 Processing ${userStory.id}: ${userStory.title}`);
|
|
128
|
+
|
|
129
|
+
// Build issue content using UserStoryIssueBuilder
|
|
130
|
+
const repoInfo = {
|
|
131
|
+
owner: this.client.getOwner(),
|
|
132
|
+
repo: this.client.getRepo(),
|
|
133
|
+
branch: 'develop' // TODO: detect from git
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const builder = new UserStoryIssueBuilder(
|
|
137
|
+
userStory.filePath,
|
|
138
|
+
this.projectRoot,
|
|
139
|
+
featureId,
|
|
140
|
+
repoInfo
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
const issueContent = await builder.buildIssueBody();
|
|
144
|
+
|
|
145
|
+
// ✅ FIX: Add status to issue content for sync
|
|
146
|
+
issueContent.status = userStory.status;
|
|
147
|
+
|
|
148
|
+
// ✅ TRIPLE IDEMPOTENCY CHECK
|
|
149
|
+
// This ensures 100% safety when running sync multiple times
|
|
150
|
+
|
|
151
|
+
// Check 1: User Story frontmatter has issue number
|
|
152
|
+
if (userStory.existingIssue) {
|
|
153
|
+
console.log(` ♻️ Issue #${userStory.existingIssue} exists in frontmatter`);
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
// Verify issue still exists on GitHub
|
|
157
|
+
await this.client.getIssue(userStory.existingIssue);
|
|
158
|
+
|
|
159
|
+
// Issue exists, update it with verification
|
|
160
|
+
await this.updateUserStoryIssue(userStory.existingIssue, issueContent, userStory.filePath);
|
|
161
|
+
issuesUpdated++;
|
|
162
|
+
console.log(` ✅ Updated Issue #${userStory.existingIssue}`);
|
|
163
|
+
continue;
|
|
164
|
+
} catch (err) {
|
|
165
|
+
// Issue deleted on GitHub, fall through to create new
|
|
166
|
+
console.log(` ⚠️ Issue #${userStory.existingIssue} deleted on GitHub, creating new`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Check 2: Search GitHub for issue by title
|
|
171
|
+
const existingByTitle = await this.client.searchIssueByTitle(issueContent.title);
|
|
172
|
+
if (existingByTitle) {
|
|
173
|
+
console.log(` ♻️ Found existing issue by title: #${existingByTitle.number}`);
|
|
174
|
+
|
|
175
|
+
// Link it in frontmatter
|
|
176
|
+
await this.updateUserStoryFrontmatter(userStory.filePath, existingByTitle.number);
|
|
177
|
+
|
|
178
|
+
// Update content with verification
|
|
179
|
+
await this.updateUserStoryIssue(existingByTitle.number, issueContent, userStory.filePath);
|
|
180
|
+
issuesUpdated++;
|
|
181
|
+
console.log(` ✅ Linked and updated Issue #${existingByTitle.number}`);
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Check 3: No existing issue found, create new
|
|
186
|
+
console.log(` 🚀 Creating new issue: ${issueContent.title}`);
|
|
187
|
+
const milestoneTitle = `${featureData.id}: ${featureData.title}`;
|
|
188
|
+
const issueNumber = await this.createUserStoryIssue(
|
|
189
|
+
issueContent,
|
|
190
|
+
milestoneTitle,
|
|
191
|
+
userStory.filePath
|
|
192
|
+
);
|
|
193
|
+
issuesCreated++;
|
|
194
|
+
console.log(` ✅ Created Issue #${issueNumber}`);
|
|
195
|
+
|
|
196
|
+
// Update User Story frontmatter
|
|
197
|
+
await this.updateUserStoryFrontmatter(userStory.filePath, issueNumber);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
console.log(`\n✅ Feature sync complete!`);
|
|
201
|
+
console.log(` Milestone: ${milestoneUrl}`);
|
|
202
|
+
console.log(` User Stories: ${userStories.length}`);
|
|
203
|
+
console.log(` Issues created: ${issuesCreated}`);
|
|
204
|
+
console.log(` Issues updated: ${issuesUpdated}`);
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
milestoneNumber: milestoneNumber!,
|
|
208
|
+
milestoneUrl: milestoneUrl!,
|
|
209
|
+
issuesCreated,
|
|
210
|
+
issuesUpdated,
|
|
211
|
+
userStoriesProcessed: userStories.length,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Find Feature folder in specs directory
|
|
217
|
+
*/
|
|
218
|
+
private async findFeatureFolder(featureId: string): Promise<string | null> {
|
|
219
|
+
// Check _features folder first
|
|
220
|
+
const featuresFolder = path.join(this.specsDir, '_features', featureId);
|
|
221
|
+
if (existsSync(featuresFolder)) {
|
|
222
|
+
return featuresFolder;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Parse FEATURE.md frontmatter
|
|
230
|
+
*/
|
|
231
|
+
private async parseFeatureMd(featurePath: string): Promise<FeatureFrontmatter> {
|
|
232
|
+
const content = await readFile(featurePath, 'utf-8');
|
|
233
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
234
|
+
|
|
235
|
+
if (!match) {
|
|
236
|
+
throw new Error(`${featurePath}: Missing YAML frontmatter`);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return yaml.parse(match[1]) as FeatureFrontmatter;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Find all User Story files for this feature across all projects
|
|
244
|
+
*/
|
|
245
|
+
private async findUserStories(featureId: string): Promise<UserStoryInfo[]> {
|
|
246
|
+
const userStories: UserStoryInfo[] = [];
|
|
247
|
+
|
|
248
|
+
// Find all project folders
|
|
249
|
+
const projectFolders = await this.findProjectFolders();
|
|
250
|
+
|
|
251
|
+
for (const projectFolder of projectFolders) {
|
|
252
|
+
const featureSpecsFolder = path.join(projectFolder, featureId);
|
|
253
|
+
|
|
254
|
+
if (!existsSync(featureSpecsFolder)) {
|
|
255
|
+
continue; // Feature not present in this project
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Read all us-*.md files
|
|
259
|
+
const files = await readdir(featureSpecsFolder);
|
|
260
|
+
const usFiles = files.filter((f) => f.startsWith('us-') && f.endsWith('.md'));
|
|
261
|
+
|
|
262
|
+
for (const file of usFiles.sort()) {
|
|
263
|
+
const filePath = path.join(featureSpecsFolder, file);
|
|
264
|
+
const content = await readFile(filePath, 'utf-8');
|
|
265
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
266
|
+
|
|
267
|
+
if (!match) {
|
|
268
|
+
console.warn(` ⚠️ ${file}: Missing frontmatter, skipping`);
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const frontmatter = yaml.parse(match[1]);
|
|
273
|
+
const projectName = path.basename(projectFolder);
|
|
274
|
+
|
|
275
|
+
userStories.push({
|
|
276
|
+
id: frontmatter.id || file.match(/us-(\d+)/)?.[0]?.toUpperCase() || 'UNKNOWN',
|
|
277
|
+
title: frontmatter.title || 'Untitled User Story',
|
|
278
|
+
filePath,
|
|
279
|
+
project: projectName,
|
|
280
|
+
status: frontmatter.status || 'not-started',
|
|
281
|
+
existingIssue: frontmatter.external?.github?.issue || null,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return userStories;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Find all project folders (default, backend, frontend, etc.)
|
|
291
|
+
*/
|
|
292
|
+
private async findProjectFolders(): Promise<string[]> {
|
|
293
|
+
const folders: string[] = [];
|
|
294
|
+
const specsRoot = this.specsDir;
|
|
295
|
+
|
|
296
|
+
// Read all directories in specs root
|
|
297
|
+
const entries = await readdir(specsRoot, { withFileTypes: true });
|
|
298
|
+
|
|
299
|
+
for (const entry of entries) {
|
|
300
|
+
if (entry.isDirectory() && !entry.name.startsWith('_')) {
|
|
301
|
+
folders.push(path.join(specsRoot, entry.name));
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return folders;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Create GitHub Milestone for Feature
|
|
310
|
+
*/
|
|
311
|
+
private async createMilestone(featureData: FeatureFrontmatter): Promise<{
|
|
312
|
+
number: number;
|
|
313
|
+
url: string;
|
|
314
|
+
}> {
|
|
315
|
+
const title = `${featureData.id}: ${featureData.title}`;
|
|
316
|
+
const description = `Feature ${featureData.id}\n\nStatus: ${featureData.status}\nCreated: ${featureData.created}`;
|
|
317
|
+
|
|
318
|
+
const result = await execFileNoThrow('gh', [
|
|
319
|
+
'api',
|
|
320
|
+
'repos/:owner/:repo/milestones',
|
|
321
|
+
'-X',
|
|
322
|
+
'POST',
|
|
323
|
+
'-f',
|
|
324
|
+
`title=${title}`,
|
|
325
|
+
'-f',
|
|
326
|
+
`description=${description}`,
|
|
327
|
+
'-f',
|
|
328
|
+
'state=open',
|
|
329
|
+
]);
|
|
330
|
+
|
|
331
|
+
if (result.exitCode !== 0) {
|
|
332
|
+
throw new Error(`Failed to create Milestone: ${result.stderr || result.stdout}`);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const milestone = JSON.parse(result.stdout);
|
|
336
|
+
return {
|
|
337
|
+
number: milestone.number,
|
|
338
|
+
url: milestone.html_url,
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Create GitHub Issue for User Story with AC/Task Verification
|
|
344
|
+
*
|
|
345
|
+
* ✅ VERIFICATION GATE FIX:
|
|
346
|
+
* - Verifies actual completion before closing
|
|
347
|
+
* - Prevents premature closure on creation
|
|
348
|
+
*/
|
|
349
|
+
private async createUserStoryIssue(
|
|
350
|
+
issueContent: {
|
|
351
|
+
title: string;
|
|
352
|
+
body: string;
|
|
353
|
+
labels: string[];
|
|
354
|
+
status?: string;
|
|
355
|
+
},
|
|
356
|
+
milestoneTitle: string,
|
|
357
|
+
userStoryPath: string
|
|
358
|
+
): Promise<number> {
|
|
359
|
+
// Step 1: Create issue (always open initially - gh CLI limitation)
|
|
360
|
+
const result = await execFileNoThrow('gh', [
|
|
361
|
+
'issue',
|
|
362
|
+
'create',
|
|
363
|
+
'--title',
|
|
364
|
+
issueContent.title,
|
|
365
|
+
'--body',
|
|
366
|
+
issueContent.body,
|
|
367
|
+
'--milestone',
|
|
368
|
+
milestoneTitle,
|
|
369
|
+
...issueContent.labels.flatMap((label) => ['--label', label]),
|
|
370
|
+
]);
|
|
371
|
+
|
|
372
|
+
if (result.exitCode !== 0) {
|
|
373
|
+
throw new Error(`Failed to create GitHub Issue: ${result.stderr || result.stdout}`);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Parse issue number from output
|
|
377
|
+
// Format: "https://github.com/owner/repo/issues/123"
|
|
378
|
+
const match = result.stdout.match(/issues\/(\d+)/);
|
|
379
|
+
if (!match) {
|
|
380
|
+
throw new Error(`Failed to parse issue number from: ${result.stdout}`);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const issueNumber = parseInt(match[1], 10);
|
|
384
|
+
|
|
385
|
+
// Step 2: VERIFICATION GATE - Close only if ACs/tasks verified
|
|
386
|
+
const completion = await this.calculator.calculateCompletion(userStoryPath);
|
|
387
|
+
|
|
388
|
+
if (completion.overallComplete) {
|
|
389
|
+
// ✅ SAFE TO CLOSE - All ACs and tasks verified [x]
|
|
390
|
+
await execFileNoThrow('gh', [
|
|
391
|
+
'issue',
|
|
392
|
+
'close',
|
|
393
|
+
issueNumber.toString(),
|
|
394
|
+
'--comment',
|
|
395
|
+
this.calculator.buildCompletionComment(completion),
|
|
396
|
+
]);
|
|
397
|
+
console.log(
|
|
398
|
+
` ✅ Created and verified complete: ${completion.acsCompleted}/${completion.acsTotal} ACs, ${completion.tasksCompleted}/${completion.tasksTotal} tasks`
|
|
399
|
+
);
|
|
400
|
+
} else {
|
|
401
|
+
// ⚠️ INCOMPLETE - Leave open with progress comment
|
|
402
|
+
await execFileNoThrow('gh', [
|
|
403
|
+
'issue',
|
|
404
|
+
'comment',
|
|
405
|
+
issueNumber.toString(),
|
|
406
|
+
'--body',
|
|
407
|
+
this.calculator.buildProgressComment(completion),
|
|
408
|
+
]);
|
|
409
|
+
console.log(
|
|
410
|
+
` 📊 Created: ${completion.acsPercentage.toFixed(0)}% ACs, ${completion.tasksPercentage.toFixed(0)}% tasks`
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return issueNumber;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Update existing GitHub Issue for User Story with AC/Task Verification
|
|
419
|
+
*
|
|
420
|
+
* ✅ VERIFICATION GATE FIX:
|
|
421
|
+
* - OLD: Closed based on frontmatter `status: complete`
|
|
422
|
+
* - NEW: Closes ONLY if all ACs and tasks are [x]
|
|
423
|
+
*
|
|
424
|
+
* This prevents Issue #574 type bugs (premature closure)
|
|
425
|
+
*/
|
|
426
|
+
private async updateUserStoryIssue(
|
|
427
|
+
issueNumber: number,
|
|
428
|
+
issueContent: {
|
|
429
|
+
title: string;
|
|
430
|
+
body: string;
|
|
431
|
+
labels: string[];
|
|
432
|
+
status?: string;
|
|
433
|
+
},
|
|
434
|
+
userStoryPath: string
|
|
435
|
+
): Promise<void> {
|
|
436
|
+
// Update issue body
|
|
437
|
+
await execFileNoThrow('gh', [
|
|
438
|
+
'issue',
|
|
439
|
+
'edit',
|
|
440
|
+
issueNumber.toString(),
|
|
441
|
+
'--title',
|
|
442
|
+
issueContent.title,
|
|
443
|
+
'--body',
|
|
444
|
+
issueContent.body,
|
|
445
|
+
]);
|
|
446
|
+
|
|
447
|
+
// ✅ VERIFICATION GATE: Calculate ACTUAL completion from checkboxes
|
|
448
|
+
const completion = await this.calculator.calculateCompletion(userStoryPath);
|
|
449
|
+
|
|
450
|
+
// Get current issue state
|
|
451
|
+
const issueData = await this.client.getIssue(issueNumber);
|
|
452
|
+
const currentlyClosed = issueData.state === 'closed';
|
|
453
|
+
|
|
454
|
+
// DECISION LOGIC: Close/Reopen/Update based on VERIFIED completion
|
|
455
|
+
if (completion.overallComplete) {
|
|
456
|
+
// ✅ SAFE TO CLOSE - All ACs and tasks verified [x]
|
|
457
|
+
if (!currentlyClosed) {
|
|
458
|
+
await execFileNoThrow('gh', [
|
|
459
|
+
'issue',
|
|
460
|
+
'close',
|
|
461
|
+
issueNumber.toString(),
|
|
462
|
+
'--comment',
|
|
463
|
+
this.calculator.buildCompletionComment(completion),
|
|
464
|
+
]);
|
|
465
|
+
console.log(
|
|
466
|
+
` ✅ Verified complete: ${completion.acsCompleted}/${completion.acsTotal} ACs, ${completion.tasksCompleted}/${completion.tasksTotal} tasks`
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
} else {
|
|
470
|
+
// ⚠️ INCOMPLETE - Keep open or reopen if needed
|
|
471
|
+
if (currentlyClosed) {
|
|
472
|
+
// Issue was closed prematurely - REOPEN
|
|
473
|
+
await execFileNoThrow('gh', [
|
|
474
|
+
'issue',
|
|
475
|
+
'reopen',
|
|
476
|
+
issueNumber.toString(),
|
|
477
|
+
'--comment',
|
|
478
|
+
this.calculator.buildReopenComment(completion, 'Work verification failed'),
|
|
479
|
+
]);
|
|
480
|
+
console.log(
|
|
481
|
+
` ⚠️ Reopened: ${completion.blockingAcs.length + completion.blockingTasks.length} items incomplete`
|
|
482
|
+
);
|
|
483
|
+
} else {
|
|
484
|
+
// Update progress comment
|
|
485
|
+
await execFileNoThrow('gh', [
|
|
486
|
+
'issue',
|
|
487
|
+
'comment',
|
|
488
|
+
issueNumber.toString(),
|
|
489
|
+
'--body',
|
|
490
|
+
this.calculator.buildProgressComment(completion),
|
|
491
|
+
]);
|
|
492
|
+
console.log(
|
|
493
|
+
` 📊 Progress: ${completion.acsPercentage.toFixed(0)}% ACs, ${completion.tasksPercentage.toFixed(0)}% tasks`
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// Note: Labels are not updated to avoid overwriting manually added labels
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Update FEATURE.md with GitHub Milestone link
|
|
503
|
+
*/
|
|
504
|
+
private async updateFeatureMd(
|
|
505
|
+
featurePath: string,
|
|
506
|
+
milestone: {
|
|
507
|
+
type: 'milestone';
|
|
508
|
+
id: number;
|
|
509
|
+
url: string;
|
|
510
|
+
}
|
|
511
|
+
): Promise<void> {
|
|
512
|
+
const content = await readFile(featurePath, 'utf-8');
|
|
513
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
514
|
+
|
|
515
|
+
if (!match) {
|
|
516
|
+
throw new Error(`${featurePath}: Missing YAML frontmatter`);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const frontmatter = yaml.parse(match[1]);
|
|
520
|
+
|
|
521
|
+
// Update external_tools.github
|
|
522
|
+
if (!frontmatter.external_tools) {
|
|
523
|
+
frontmatter.external_tools = {};
|
|
524
|
+
}
|
|
525
|
+
frontmatter.external_tools.github = milestone;
|
|
526
|
+
|
|
527
|
+
// Rebuild content
|
|
528
|
+
const newFrontmatter = yaml.stringify(frontmatter);
|
|
529
|
+
const bodyContent = content.slice(match[0].length);
|
|
530
|
+
const newContent = `---\n${newFrontmatter}---${bodyContent}`;
|
|
531
|
+
|
|
532
|
+
await writeFile(featurePath, newContent, 'utf-8');
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Update User Story frontmatter with GitHub issue link
|
|
537
|
+
*/
|
|
538
|
+
private async updateUserStoryFrontmatter(
|
|
539
|
+
userStoryPath: string,
|
|
540
|
+
issueNumber: number
|
|
541
|
+
): Promise<void> {
|
|
542
|
+
const content = await readFile(userStoryPath, 'utf-8');
|
|
543
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
544
|
+
|
|
545
|
+
if (!match) {
|
|
546
|
+
throw new Error(`${userStoryPath}: Missing YAML frontmatter`);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const frontmatter = yaml.parse(match[1]);
|
|
550
|
+
|
|
551
|
+
// Update external.github
|
|
552
|
+
if (!frontmatter.external) {
|
|
553
|
+
frontmatter.external = {};
|
|
554
|
+
}
|
|
555
|
+
if (!frontmatter.external.github) {
|
|
556
|
+
frontmatter.external.github = {};
|
|
557
|
+
}
|
|
558
|
+
frontmatter.external.github.issue = issueNumber;
|
|
559
|
+
frontmatter.external.github.url = `https://github.com/anton-abyzov/specweave/issues/${issueNumber}`;
|
|
560
|
+
|
|
561
|
+
// Rebuild content
|
|
562
|
+
const newFrontmatter = yaml.stringify(frontmatter);
|
|
563
|
+
const bodyContent = content.slice(match[0].length);
|
|
564
|
+
const newContent = `---\n${newFrontmatter}---${bodyContent}`;
|
|
565
|
+
|
|
566
|
+
await writeFile(userStoryPath, newContent, 'utf-8');
|
|
567
|
+
}
|
|
568
|
+
}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
hasExternalLink,
|
|
7
7
|
updateSpecWithExternalLink
|
|
8
8
|
} from "../../../src/core/spec-content-sync.js";
|
|
9
|
+
import { ProgressCommentBuilder } from "./progress-comment-builder.js";
|
|
9
10
|
import path from "path";
|
|
10
11
|
import fs from "fs/promises";
|
|
11
12
|
async function getGitHubRepoForProject(project, specPath) {
|
|
@@ -167,27 +168,35 @@ async function updateGitHubIssue(client, spec, issueNumber, options) {
|
|
|
167
168
|
console.log(` - ${change}`);
|
|
168
169
|
}
|
|
169
170
|
}
|
|
170
|
-
const newTitle = `[${spec.identifier.compact}] ${spec.title}`;
|
|
171
|
-
const newBody = buildExternalDescription(spec);
|
|
172
171
|
if (dryRun) {
|
|
173
|
-
console.log("\n\u{1F50D} Dry run - would
|
|
174
|
-
console.log(` Title: ${newTitle}`);
|
|
175
|
-
console.log(` Body:
|
|
176
|
-
${newBody}`);
|
|
172
|
+
console.log("\n\u{1F50D} Dry run - would post progress comment");
|
|
177
173
|
return {
|
|
178
174
|
success: true,
|
|
179
|
-
action: "updated",
|
|
175
|
+
action: "updated-via-comment",
|
|
180
176
|
externalId: issueNumber.toString(),
|
|
181
177
|
externalUrl: issue.html_url
|
|
182
178
|
};
|
|
183
179
|
}
|
|
184
|
-
|
|
180
|
+
const labels = [
|
|
181
|
+
"specweave",
|
|
182
|
+
"spec",
|
|
183
|
+
spec.project,
|
|
184
|
+
spec.metadata.priority || "P2"
|
|
185
|
+
].filter(Boolean);
|
|
186
|
+
await client.addLabels(issueNumber, labels);
|
|
187
|
+
await postProgressComment(
|
|
188
|
+
client,
|
|
189
|
+
specPath,
|
|
190
|
+
issueNumber,
|
|
191
|
+
spec,
|
|
192
|
+
verbose
|
|
193
|
+
);
|
|
185
194
|
if (verbose) {
|
|
186
|
-
console.log(`\u2705
|
|
195
|
+
console.log(`\u2705 Posted progress comment to issue #${issueNumber}`);
|
|
187
196
|
}
|
|
188
197
|
return {
|
|
189
198
|
success: true,
|
|
190
|
-
action: "updated",
|
|
199
|
+
action: "updated-via-comment",
|
|
191
200
|
externalId: issueNumber.toString(),
|
|
192
201
|
externalUrl: issue.html_url
|
|
193
202
|
};
|
|
@@ -199,6 +208,27 @@ ${newBody}`);
|
|
|
199
208
|
};
|
|
200
209
|
}
|
|
201
210
|
}
|
|
211
|
+
async function postProgressComment(client, specPath, issueNumber, spec, verbose = false) {
|
|
212
|
+
try {
|
|
213
|
+
let incrementId = "unknown";
|
|
214
|
+
const incrementMatch = specPath.match(/increments\/([^/]+)/);
|
|
215
|
+
if (incrementMatch) {
|
|
216
|
+
incrementId = incrementMatch[1];
|
|
217
|
+
} else {
|
|
218
|
+
incrementId = spec.identifier.compact;
|
|
219
|
+
}
|
|
220
|
+
const builder = new ProgressCommentBuilder(specPath);
|
|
221
|
+
const comment = await builder.buildProgressComment(incrementId);
|
|
222
|
+
await client.addComment(issueNumber, comment);
|
|
223
|
+
if (verbose) {
|
|
224
|
+
console.log(` \u{1F4CA} Progress comment posted (${comment.length} chars)`);
|
|
225
|
+
}
|
|
226
|
+
} catch (error) {
|
|
227
|
+
if (verbose) {
|
|
228
|
+
console.error(` \u26A0\uFE0F Failed to post progress comment: ${error.message}`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
202
232
|
function countUserStoriesInBody(body) {
|
|
203
233
|
const matches = body.match(/###\s+US-\d+:/g);
|
|
204
234
|
return matches ? matches.length : 0;
|