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
|
@@ -44,6 +44,20 @@ export class GitHubClientV2 {
|
|
|
44
44
|
return new GitHubClientV2(profile);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Get repository owner
|
|
49
|
+
*/
|
|
50
|
+
getOwner(): string {
|
|
51
|
+
return this.owner;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Get repository name
|
|
56
|
+
*/
|
|
57
|
+
getRepo(): string {
|
|
58
|
+
return this.repo;
|
|
59
|
+
}
|
|
60
|
+
|
|
47
61
|
// ==========================================================================
|
|
48
62
|
// Authentication & Setup
|
|
49
63
|
// ==========================================================================
|
|
@@ -272,6 +286,51 @@ export class GitHubClientV2 {
|
|
|
272
286
|
};
|
|
273
287
|
}
|
|
274
288
|
|
|
289
|
+
/**
|
|
290
|
+
* Search for issue by exact title match
|
|
291
|
+
*
|
|
292
|
+
* IDEMPOTENCY: Use this before creating issues to prevent duplicates
|
|
293
|
+
*/
|
|
294
|
+
async searchIssueByTitle(title: string): Promise<GitHubIssue | null> {
|
|
295
|
+
// Escape double quotes in title for gh search
|
|
296
|
+
const escapedTitle = title.replace(/"/g, '\\"');
|
|
297
|
+
|
|
298
|
+
const result = await execFileNoThrow('gh', [
|
|
299
|
+
'issue',
|
|
300
|
+
'list',
|
|
301
|
+
'--repo',
|
|
302
|
+
this.fullRepo,
|
|
303
|
+
'--search',
|
|
304
|
+
`"${escapedTitle}" in:title`,
|
|
305
|
+
'--json',
|
|
306
|
+
'number,title,state,url,labels',
|
|
307
|
+
'--limit',
|
|
308
|
+
'1',
|
|
309
|
+
]);
|
|
310
|
+
|
|
311
|
+
if (result.exitCode !== 0) {
|
|
312
|
+
// Search failed, return null (treat as not found)
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const issues = JSON.parse(result.stdout || '[]');
|
|
317
|
+
|
|
318
|
+
if (!issues || issues.length === 0) {
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Return first exact match
|
|
323
|
+
const issue = issues[0];
|
|
324
|
+
return {
|
|
325
|
+
number: issue.number,
|
|
326
|
+
title: issue.title,
|
|
327
|
+
body: '', // Body not included in list view
|
|
328
|
+
state: issue.state,
|
|
329
|
+
html_url: issue.url,
|
|
330
|
+
labels: issue.labels?.map((l: any) => l.name) || [],
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
275
334
|
/**
|
|
276
335
|
* Update issue body
|
|
277
336
|
*/
|
|
@@ -4,6 +4,7 @@ import * as path from "path";
|
|
|
4
4
|
import * as yaml from "yaml";
|
|
5
5
|
import { execFileNoThrow } from "../../../src/utils/execFileNoThrow.js";
|
|
6
6
|
import { DuplicateDetector } from "./duplicate-detector.js";
|
|
7
|
+
import { EpicContentBuilder } from "./epic-content-builder.js";
|
|
7
8
|
class GitHubEpicSync {
|
|
8
9
|
constructor(client, specsDir) {
|
|
9
10
|
this.client = client;
|
|
@@ -350,21 +351,20 @@ Status: ${epic.status}`;
|
|
|
350
351
|
}
|
|
351
352
|
}
|
|
352
353
|
/**
|
|
353
|
-
* Create GitHub Issue for
|
|
354
|
+
* Create GitHub Issue for Epic with hierarchical content (US → Tasks)
|
|
354
355
|
*/
|
|
355
356
|
async createIssue(epicId, increment, milestoneNumber) {
|
|
356
357
|
const title = `[${epicId}] ${increment.title}`;
|
|
357
|
-
const
|
|
358
|
-
|
|
359
|
-
${
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
\u{1F916} Auto-created by SpecWeave Epic Sync`;
|
|
358
|
+
const epicFolder = await this.findEpicFolder(epicId);
|
|
359
|
+
if (!epicFolder) {
|
|
360
|
+
throw new Error(`Epic folder not found for ${epicId}`);
|
|
361
|
+
}
|
|
362
|
+
const contentBuilder = new EpicContentBuilder(
|
|
363
|
+
epicFolder,
|
|
364
|
+
path.dirname(this.specsDir)
|
|
365
|
+
// Project root
|
|
366
|
+
);
|
|
367
|
+
const body = await contentBuilder.buildIssueBody();
|
|
368
368
|
try {
|
|
369
369
|
const result = await DuplicateDetector.createWithProtection({
|
|
370
370
|
title,
|
|
@@ -387,21 +387,20 @@ ${increment.overview}
|
|
|
387
387
|
}
|
|
388
388
|
}
|
|
389
389
|
/**
|
|
390
|
-
* Update GitHub Issue for
|
|
390
|
+
* Update GitHub Issue for Epic with hierarchical content (US → Tasks)
|
|
391
391
|
*/
|
|
392
392
|
async updateIssue(epicId, issueNumber, increment, milestoneNumber) {
|
|
393
393
|
const title = `[${epicId}] ${increment.title}`;
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
${
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
\u{1F916} Auto-updated by SpecWeave Epic Sync`;
|
|
394
|
+
const epicFolder = await this.findEpicFolder(epicId);
|
|
395
|
+
if (!epicFolder) {
|
|
396
|
+
throw new Error(`Epic folder not found for ${epicId}`);
|
|
397
|
+
}
|
|
398
|
+
const contentBuilder = new EpicContentBuilder(
|
|
399
|
+
epicFolder,
|
|
400
|
+
path.dirname(this.specsDir)
|
|
401
|
+
// Project root
|
|
402
|
+
);
|
|
403
|
+
const body = await contentBuilder.buildIssueBody();
|
|
405
404
|
const result = await execFileNoThrow("gh", [
|
|
406
405
|
"issue",
|
|
407
406
|
"edit",
|
|
@@ -15,6 +15,7 @@ import * as yaml from 'yaml';
|
|
|
15
15
|
import { GitHubClientV2 } from './github-client-v2.js';
|
|
16
16
|
import { execFileNoThrow } from '../../../src/utils/execFileNoThrow.js';
|
|
17
17
|
import { DuplicateDetector } from './duplicate-detector.js';
|
|
18
|
+
import { EpicContentBuilder } from './epic-content-builder.js';
|
|
18
19
|
|
|
19
20
|
interface EpicFrontmatter {
|
|
20
21
|
id: string;
|
|
@@ -317,7 +318,7 @@ export class GitHubEpicSync {
|
|
|
317
318
|
): Promise<number | null> {
|
|
318
319
|
try {
|
|
319
320
|
// Search GitHub for issues with Epic ID in title
|
|
320
|
-
// Pattern: "[FS-031] Title" or "[INC-0031] Title"
|
|
321
|
+
// Pattern: "[FS-031] Title" (new) or "[INC-0031] Title" (deprecated, legacy support only)
|
|
321
322
|
const titlePattern = `[${epicId}]`;
|
|
322
323
|
|
|
323
324
|
const result = await execFileNoThrow('gh', [
|
|
@@ -526,7 +527,7 @@ export class GitHubEpicSync {
|
|
|
526
527
|
}
|
|
527
528
|
|
|
528
529
|
/**
|
|
529
|
-
* Create GitHub Issue for
|
|
530
|
+
* Create GitHub Issue for Epic with hierarchical content (US → Tasks)
|
|
530
531
|
*/
|
|
531
532
|
private async createIssue(
|
|
532
533
|
epicId: string,
|
|
@@ -539,7 +540,19 @@ export class GitHubEpicSync {
|
|
|
539
540
|
milestoneNumber: number
|
|
540
541
|
): Promise<number> {
|
|
541
542
|
const title = `[${epicId}] ${increment.title}`;
|
|
542
|
-
|
|
543
|
+
|
|
544
|
+
// Build hierarchical issue body using EpicContentBuilder
|
|
545
|
+
const epicFolder = await this.findEpicFolder(epicId);
|
|
546
|
+
if (!epicFolder) {
|
|
547
|
+
throw new Error(`Epic folder not found for ${epicId}`);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
const contentBuilder = new EpicContentBuilder(
|
|
551
|
+
epicFolder,
|
|
552
|
+
path.dirname(this.specsDir) // Project root
|
|
553
|
+
);
|
|
554
|
+
|
|
555
|
+
const body = await contentBuilder.buildIssueBody();
|
|
543
556
|
|
|
544
557
|
try {
|
|
545
558
|
// Use DuplicateDetector for full 3-phase protection
|
|
@@ -568,7 +581,7 @@ export class GitHubEpicSync {
|
|
|
568
581
|
}
|
|
569
582
|
|
|
570
583
|
/**
|
|
571
|
-
* Update GitHub Issue for
|
|
584
|
+
* Update GitHub Issue for Epic with hierarchical content (US → Tasks)
|
|
572
585
|
*/
|
|
573
586
|
private async updateIssue(
|
|
574
587
|
epicId: string,
|
|
@@ -582,7 +595,19 @@ export class GitHubEpicSync {
|
|
|
582
595
|
milestoneNumber: number
|
|
583
596
|
): Promise<void> {
|
|
584
597
|
const title = `[${epicId}] ${increment.title}`;
|
|
585
|
-
|
|
598
|
+
|
|
599
|
+
// Build hierarchical issue body using EpicContentBuilder
|
|
600
|
+
const epicFolder = await this.findEpicFolder(epicId);
|
|
601
|
+
if (!epicFolder) {
|
|
602
|
+
throw new Error(`Epic folder not found for ${epicId}`);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
const contentBuilder = new EpicContentBuilder(
|
|
606
|
+
epicFolder,
|
|
607
|
+
path.dirname(this.specsDir) // Project root
|
|
608
|
+
);
|
|
609
|
+
|
|
610
|
+
const body = await contentBuilder.buildIssueBody();
|
|
586
611
|
|
|
587
612
|
const result = await execFileNoThrow('gh', [
|
|
588
613
|
'issue',
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
import { readdir, readFile, writeFile } from "fs/promises";
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import * as yaml from "yaml";
|
|
5
|
+
import { UserStoryIssueBuilder } from "./user-story-issue-builder.js";
|
|
6
|
+
import { CompletionCalculator } from "./completion-calculator.js";
|
|
7
|
+
import { execFileNoThrow } from "../../../src/utils/execFileNoThrow.js";
|
|
8
|
+
class GitHubFeatureSync {
|
|
9
|
+
constructor(client, specsDir, projectRoot) {
|
|
10
|
+
this.client = client;
|
|
11
|
+
this.specsDir = specsDir;
|
|
12
|
+
this.projectRoot = projectRoot;
|
|
13
|
+
this.calculator = new CompletionCalculator(projectRoot);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Sync Feature folder to GitHub (Milestone + User Story Issues)
|
|
17
|
+
*
|
|
18
|
+
* Process:
|
|
19
|
+
* 1. Create/update GitHub Milestone for Feature
|
|
20
|
+
* 2. Find all us-*.md files across all projects
|
|
21
|
+
* 3. Create/update GitHub Issue for EACH user story
|
|
22
|
+
* 4. Update frontmatter with GitHub issue links
|
|
23
|
+
*/
|
|
24
|
+
async syncFeatureToGitHub(featureId) {
|
|
25
|
+
console.log(`
|
|
26
|
+
\u{1F504} Syncing Feature ${featureId} to GitHub...`);
|
|
27
|
+
const featureFolder = await this.findFeatureFolder(featureId);
|
|
28
|
+
if (!featureFolder) {
|
|
29
|
+
throw new Error(`Feature ${featureId} not found in ${this.specsDir}`);
|
|
30
|
+
}
|
|
31
|
+
const featurePath = path.join(featureFolder, "FEATURE.md");
|
|
32
|
+
const featureData = await this.parseFeatureMd(featurePath);
|
|
33
|
+
console.log(` \u{1F4E6} Feature: ${featureData.title}`);
|
|
34
|
+
console.log(` \u{1F4CA} Status: ${featureData.status}`);
|
|
35
|
+
let milestoneNumber = featureData.external_tools?.github?.id;
|
|
36
|
+
let milestoneUrl = featureData.external_tools?.github?.url;
|
|
37
|
+
if (!milestoneNumber) {
|
|
38
|
+
console.log(` \u{1F680} Creating GitHub Milestone...`);
|
|
39
|
+
const milestone = await this.createMilestone(featureData);
|
|
40
|
+
milestoneNumber = milestone.number;
|
|
41
|
+
milestoneUrl = milestone.url;
|
|
42
|
+
console.log(` \u2705 Created Milestone #${milestoneNumber}`);
|
|
43
|
+
await this.updateFeatureMd(featurePath, {
|
|
44
|
+
type: "milestone",
|
|
45
|
+
id: milestoneNumber,
|
|
46
|
+
url: milestoneUrl
|
|
47
|
+
});
|
|
48
|
+
} else {
|
|
49
|
+
console.log(` \u267B\uFE0F Using existing Milestone #${milestoneNumber}`);
|
|
50
|
+
milestoneUrl = featureData.external_tools?.github?.url || milestoneUrl;
|
|
51
|
+
}
|
|
52
|
+
const userStories = await this.findUserStories(featureId);
|
|
53
|
+
console.log(`
|
|
54
|
+
\u{1F4DD} Found ${userStories.length} User Stories to sync...`);
|
|
55
|
+
let issuesCreated = 0;
|
|
56
|
+
let issuesUpdated = 0;
|
|
57
|
+
for (const userStory of userStories) {
|
|
58
|
+
console.log(`
|
|
59
|
+
\u{1F539} Processing ${userStory.id}: ${userStory.title}`);
|
|
60
|
+
const repoInfo = {
|
|
61
|
+
owner: this.client.getOwner(),
|
|
62
|
+
repo: this.client.getRepo(),
|
|
63
|
+
branch: "develop"
|
|
64
|
+
// TODO: detect from git
|
|
65
|
+
};
|
|
66
|
+
const builder = new UserStoryIssueBuilder(
|
|
67
|
+
userStory.filePath,
|
|
68
|
+
this.projectRoot,
|
|
69
|
+
featureId,
|
|
70
|
+
repoInfo
|
|
71
|
+
);
|
|
72
|
+
const issueContent = await builder.buildIssueBody();
|
|
73
|
+
issueContent.status = userStory.status;
|
|
74
|
+
if (userStory.existingIssue) {
|
|
75
|
+
console.log(` \u267B\uFE0F Issue #${userStory.existingIssue} exists in frontmatter`);
|
|
76
|
+
try {
|
|
77
|
+
await this.client.getIssue(userStory.existingIssue);
|
|
78
|
+
await this.updateUserStoryIssue(userStory.existingIssue, issueContent, userStory.filePath);
|
|
79
|
+
issuesUpdated++;
|
|
80
|
+
console.log(` \u2705 Updated Issue #${userStory.existingIssue}`);
|
|
81
|
+
continue;
|
|
82
|
+
} catch (err) {
|
|
83
|
+
console.log(` \u26A0\uFE0F Issue #${userStory.existingIssue} deleted on GitHub, creating new`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const existingByTitle = await this.client.searchIssueByTitle(issueContent.title);
|
|
87
|
+
if (existingByTitle) {
|
|
88
|
+
console.log(` \u267B\uFE0F Found existing issue by title: #${existingByTitle.number}`);
|
|
89
|
+
await this.updateUserStoryFrontmatter(userStory.filePath, existingByTitle.number);
|
|
90
|
+
await this.updateUserStoryIssue(existingByTitle.number, issueContent, userStory.filePath);
|
|
91
|
+
issuesUpdated++;
|
|
92
|
+
console.log(` \u2705 Linked and updated Issue #${existingByTitle.number}`);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
console.log(` \u{1F680} Creating new issue: ${issueContent.title}`);
|
|
96
|
+
const milestoneTitle = `${featureData.id}: ${featureData.title}`;
|
|
97
|
+
const issueNumber = await this.createUserStoryIssue(
|
|
98
|
+
issueContent,
|
|
99
|
+
milestoneTitle,
|
|
100
|
+
userStory.filePath
|
|
101
|
+
);
|
|
102
|
+
issuesCreated++;
|
|
103
|
+
console.log(` \u2705 Created Issue #${issueNumber}`);
|
|
104
|
+
await this.updateUserStoryFrontmatter(userStory.filePath, issueNumber);
|
|
105
|
+
}
|
|
106
|
+
console.log(`
|
|
107
|
+
\u2705 Feature sync complete!`);
|
|
108
|
+
console.log(` Milestone: ${milestoneUrl}`);
|
|
109
|
+
console.log(` User Stories: ${userStories.length}`);
|
|
110
|
+
console.log(` Issues created: ${issuesCreated}`);
|
|
111
|
+
console.log(` Issues updated: ${issuesUpdated}`);
|
|
112
|
+
return {
|
|
113
|
+
milestoneNumber,
|
|
114
|
+
milestoneUrl,
|
|
115
|
+
issuesCreated,
|
|
116
|
+
issuesUpdated,
|
|
117
|
+
userStoriesProcessed: userStories.length
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Find Feature folder in specs directory
|
|
122
|
+
*/
|
|
123
|
+
async findFeatureFolder(featureId) {
|
|
124
|
+
const featuresFolder = path.join(this.specsDir, "_features", featureId);
|
|
125
|
+
if (existsSync(featuresFolder)) {
|
|
126
|
+
return featuresFolder;
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Parse FEATURE.md frontmatter
|
|
132
|
+
*/
|
|
133
|
+
async parseFeatureMd(featurePath) {
|
|
134
|
+
const content = await readFile(featurePath, "utf-8");
|
|
135
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
136
|
+
if (!match) {
|
|
137
|
+
throw new Error(`${featurePath}: Missing YAML frontmatter`);
|
|
138
|
+
}
|
|
139
|
+
return yaml.parse(match[1]);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Find all User Story files for this feature across all projects
|
|
143
|
+
*/
|
|
144
|
+
async findUserStories(featureId) {
|
|
145
|
+
const userStories = [];
|
|
146
|
+
const projectFolders = await this.findProjectFolders();
|
|
147
|
+
for (const projectFolder of projectFolders) {
|
|
148
|
+
const featureSpecsFolder = path.join(projectFolder, featureId);
|
|
149
|
+
if (!existsSync(featureSpecsFolder)) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const files = await readdir(featureSpecsFolder);
|
|
153
|
+
const usFiles = files.filter((f) => f.startsWith("us-") && f.endsWith(".md"));
|
|
154
|
+
for (const file of usFiles.sort()) {
|
|
155
|
+
const filePath = path.join(featureSpecsFolder, file);
|
|
156
|
+
const content = await readFile(filePath, "utf-8");
|
|
157
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
158
|
+
if (!match) {
|
|
159
|
+
console.warn(` \u26A0\uFE0F ${file}: Missing frontmatter, skipping`);
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
const frontmatter = yaml.parse(match[1]);
|
|
163
|
+
const projectName = path.basename(projectFolder);
|
|
164
|
+
userStories.push({
|
|
165
|
+
id: frontmatter.id || file.match(/us-(\d+)/)?.[0]?.toUpperCase() || "UNKNOWN",
|
|
166
|
+
title: frontmatter.title || "Untitled User Story",
|
|
167
|
+
filePath,
|
|
168
|
+
project: projectName,
|
|
169
|
+
status: frontmatter.status || "not-started",
|
|
170
|
+
existingIssue: frontmatter.external?.github?.issue || null
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return userStories;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Find all project folders (default, backend, frontend, etc.)
|
|
178
|
+
*/
|
|
179
|
+
async findProjectFolders() {
|
|
180
|
+
const folders = [];
|
|
181
|
+
const specsRoot = this.specsDir;
|
|
182
|
+
const entries = await readdir(specsRoot, { withFileTypes: true });
|
|
183
|
+
for (const entry of entries) {
|
|
184
|
+
if (entry.isDirectory() && !entry.name.startsWith("_")) {
|
|
185
|
+
folders.push(path.join(specsRoot, entry.name));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return folders;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Create GitHub Milestone for Feature
|
|
192
|
+
*/
|
|
193
|
+
async createMilestone(featureData) {
|
|
194
|
+
const title = `${featureData.id}: ${featureData.title}`;
|
|
195
|
+
const description = `Feature ${featureData.id}
|
|
196
|
+
|
|
197
|
+
Status: ${featureData.status}
|
|
198
|
+
Created: ${featureData.created}`;
|
|
199
|
+
const result = await execFileNoThrow("gh", [
|
|
200
|
+
"api",
|
|
201
|
+
"repos/:owner/:repo/milestones",
|
|
202
|
+
"-X",
|
|
203
|
+
"POST",
|
|
204
|
+
"-f",
|
|
205
|
+
`title=${title}`,
|
|
206
|
+
"-f",
|
|
207
|
+
`description=${description}`,
|
|
208
|
+
"-f",
|
|
209
|
+
"state=open"
|
|
210
|
+
]);
|
|
211
|
+
if (result.exitCode !== 0) {
|
|
212
|
+
throw new Error(`Failed to create Milestone: ${result.stderr || result.stdout}`);
|
|
213
|
+
}
|
|
214
|
+
const milestone = JSON.parse(result.stdout);
|
|
215
|
+
return {
|
|
216
|
+
number: milestone.number,
|
|
217
|
+
url: milestone.html_url
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Create GitHub Issue for User Story with AC/Task Verification
|
|
222
|
+
*
|
|
223
|
+
* ✅ VERIFICATION GATE FIX:
|
|
224
|
+
* - Verifies actual completion before closing
|
|
225
|
+
* - Prevents premature closure on creation
|
|
226
|
+
*/
|
|
227
|
+
async createUserStoryIssue(issueContent, milestoneTitle, userStoryPath) {
|
|
228
|
+
const result = await execFileNoThrow("gh", [
|
|
229
|
+
"issue",
|
|
230
|
+
"create",
|
|
231
|
+
"--title",
|
|
232
|
+
issueContent.title,
|
|
233
|
+
"--body",
|
|
234
|
+
issueContent.body,
|
|
235
|
+
"--milestone",
|
|
236
|
+
milestoneTitle,
|
|
237
|
+
...issueContent.labels.flatMap((label) => ["--label", label])
|
|
238
|
+
]);
|
|
239
|
+
if (result.exitCode !== 0) {
|
|
240
|
+
throw new Error(`Failed to create GitHub Issue: ${result.stderr || result.stdout}`);
|
|
241
|
+
}
|
|
242
|
+
const match = result.stdout.match(/issues\/(\d+)/);
|
|
243
|
+
if (!match) {
|
|
244
|
+
throw new Error(`Failed to parse issue number from: ${result.stdout}`);
|
|
245
|
+
}
|
|
246
|
+
const issueNumber = parseInt(match[1], 10);
|
|
247
|
+
const completion = await this.calculator.calculateCompletion(userStoryPath);
|
|
248
|
+
if (completion.overallComplete) {
|
|
249
|
+
await execFileNoThrow("gh", [
|
|
250
|
+
"issue",
|
|
251
|
+
"close",
|
|
252
|
+
issueNumber.toString(),
|
|
253
|
+
"--comment",
|
|
254
|
+
this.calculator.buildCompletionComment(completion)
|
|
255
|
+
]);
|
|
256
|
+
console.log(
|
|
257
|
+
` \u2705 Created and verified complete: ${completion.acsCompleted}/${completion.acsTotal} ACs, ${completion.tasksCompleted}/${completion.tasksTotal} tasks`
|
|
258
|
+
);
|
|
259
|
+
} else {
|
|
260
|
+
await execFileNoThrow("gh", [
|
|
261
|
+
"issue",
|
|
262
|
+
"comment",
|
|
263
|
+
issueNumber.toString(),
|
|
264
|
+
"--body",
|
|
265
|
+
this.calculator.buildProgressComment(completion)
|
|
266
|
+
]);
|
|
267
|
+
console.log(
|
|
268
|
+
` \u{1F4CA} Created: ${completion.acsPercentage.toFixed(0)}% ACs, ${completion.tasksPercentage.toFixed(0)}% tasks`
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
return issueNumber;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Update existing GitHub Issue for User Story with AC/Task Verification
|
|
275
|
+
*
|
|
276
|
+
* ✅ VERIFICATION GATE FIX:
|
|
277
|
+
* - OLD: Closed based on frontmatter `status: complete`
|
|
278
|
+
* - NEW: Closes ONLY if all ACs and tasks are [x]
|
|
279
|
+
*
|
|
280
|
+
* This prevents Issue #574 type bugs (premature closure)
|
|
281
|
+
*/
|
|
282
|
+
async updateUserStoryIssue(issueNumber, issueContent, userStoryPath) {
|
|
283
|
+
await execFileNoThrow("gh", [
|
|
284
|
+
"issue",
|
|
285
|
+
"edit",
|
|
286
|
+
issueNumber.toString(),
|
|
287
|
+
"--title",
|
|
288
|
+
issueContent.title,
|
|
289
|
+
"--body",
|
|
290
|
+
issueContent.body
|
|
291
|
+
]);
|
|
292
|
+
const completion = await this.calculator.calculateCompletion(userStoryPath);
|
|
293
|
+
const issueData = await this.client.getIssue(issueNumber);
|
|
294
|
+
const currentlyClosed = issueData.state === "closed";
|
|
295
|
+
if (completion.overallComplete) {
|
|
296
|
+
if (!currentlyClosed) {
|
|
297
|
+
await execFileNoThrow("gh", [
|
|
298
|
+
"issue",
|
|
299
|
+
"close",
|
|
300
|
+
issueNumber.toString(),
|
|
301
|
+
"--comment",
|
|
302
|
+
this.calculator.buildCompletionComment(completion)
|
|
303
|
+
]);
|
|
304
|
+
console.log(
|
|
305
|
+
` \u2705 Verified complete: ${completion.acsCompleted}/${completion.acsTotal} ACs, ${completion.tasksCompleted}/${completion.tasksTotal} tasks`
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
} else {
|
|
309
|
+
if (currentlyClosed) {
|
|
310
|
+
await execFileNoThrow("gh", [
|
|
311
|
+
"issue",
|
|
312
|
+
"reopen",
|
|
313
|
+
issueNumber.toString(),
|
|
314
|
+
"--comment",
|
|
315
|
+
this.calculator.buildReopenComment(completion, "Work verification failed")
|
|
316
|
+
]);
|
|
317
|
+
console.log(
|
|
318
|
+
` \u26A0\uFE0F Reopened: ${completion.blockingAcs.length + completion.blockingTasks.length} items incomplete`
|
|
319
|
+
);
|
|
320
|
+
} else {
|
|
321
|
+
await execFileNoThrow("gh", [
|
|
322
|
+
"issue",
|
|
323
|
+
"comment",
|
|
324
|
+
issueNumber.toString(),
|
|
325
|
+
"--body",
|
|
326
|
+
this.calculator.buildProgressComment(completion)
|
|
327
|
+
]);
|
|
328
|
+
console.log(
|
|
329
|
+
` \u{1F4CA} Progress: ${completion.acsPercentage.toFixed(0)}% ACs, ${completion.tasksPercentage.toFixed(0)}% tasks`
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Update FEATURE.md with GitHub Milestone link
|
|
336
|
+
*/
|
|
337
|
+
async updateFeatureMd(featurePath, milestone) {
|
|
338
|
+
const content = await readFile(featurePath, "utf-8");
|
|
339
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
340
|
+
if (!match) {
|
|
341
|
+
throw new Error(`${featurePath}: Missing YAML frontmatter`);
|
|
342
|
+
}
|
|
343
|
+
const frontmatter = yaml.parse(match[1]);
|
|
344
|
+
if (!frontmatter.external_tools) {
|
|
345
|
+
frontmatter.external_tools = {};
|
|
346
|
+
}
|
|
347
|
+
frontmatter.external_tools.github = milestone;
|
|
348
|
+
const newFrontmatter = yaml.stringify(frontmatter);
|
|
349
|
+
const bodyContent = content.slice(match[0].length);
|
|
350
|
+
const newContent = `---
|
|
351
|
+
${newFrontmatter}---${bodyContent}`;
|
|
352
|
+
await writeFile(featurePath, newContent, "utf-8");
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Update User Story frontmatter with GitHub issue link
|
|
356
|
+
*/
|
|
357
|
+
async updateUserStoryFrontmatter(userStoryPath, issueNumber) {
|
|
358
|
+
const content = await readFile(userStoryPath, "utf-8");
|
|
359
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
360
|
+
if (!match) {
|
|
361
|
+
throw new Error(`${userStoryPath}: Missing YAML frontmatter`);
|
|
362
|
+
}
|
|
363
|
+
const frontmatter = yaml.parse(match[1]);
|
|
364
|
+
if (!frontmatter.external) {
|
|
365
|
+
frontmatter.external = {};
|
|
366
|
+
}
|
|
367
|
+
if (!frontmatter.external.github) {
|
|
368
|
+
frontmatter.external.github = {};
|
|
369
|
+
}
|
|
370
|
+
frontmatter.external.github.issue = issueNumber;
|
|
371
|
+
frontmatter.external.github.url = `https://github.com/anton-abyzov/specweave/issues/${issueNumber}`;
|
|
372
|
+
const newFrontmatter = yaml.stringify(frontmatter);
|
|
373
|
+
const bodyContent = content.slice(match[0].length);
|
|
374
|
+
const newContent = `---
|
|
375
|
+
${newFrontmatter}---${bodyContent}`;
|
|
376
|
+
await writeFile(userStoryPath, newContent, "utf-8");
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
export {
|
|
380
|
+
GitHubFeatureSync
|
|
381
|
+
};
|