specweave 0.32.0 ā 0.32.3
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 +215 -2
- package/README.md +22 -0
- package/bin/specweave.js +52 -1
- package/dist/plugins/specweave-ado/lib/ado-duplicate-detector.d.ts +100 -0
- package/dist/plugins/specweave-ado/lib/ado-duplicate-detector.d.ts.map +1 -0
- package/dist/plugins/specweave-ado/lib/ado-duplicate-detector.js +291 -0
- package/dist/plugins/specweave-ado/lib/ado-duplicate-detector.js.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-duplicate-detector.d.ts +103 -0
- package/dist/plugins/specweave-jira/lib/jira-duplicate-detector.d.ts.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-duplicate-detector.js +310 -0
- package/dist/plugins/specweave-jira/lib/jira-duplicate-detector.js.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-permission-gate.d.ts +126 -0
- package/dist/plugins/specweave-jira/lib/jira-permission-gate.d.ts.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-permission-gate.js +207 -0
- package/dist/plugins/specweave-jira/lib/jira-permission-gate.js.map +1 -0
- package/dist/src/adapters/codex/README.md +1 -1
- package/dist/src/adapters/codex/adapter.js +1 -1
- package/dist/src/cli/commands/archive.d.ts +2 -0
- package/dist/src/cli/commands/archive.d.ts.map +1 -1
- package/dist/src/cli/commands/archive.js +33 -0
- package/dist/src/cli/commands/archive.js.map +1 -1
- package/dist/src/cli/commands/cache.d.ts +17 -0
- package/dist/src/cli/commands/cache.d.ts.map +1 -0
- package/dist/src/cli/commands/cache.js +126 -0
- package/dist/src/cli/commands/cache.js.map +1 -0
- package/dist/src/cli/commands/context.d.ts +92 -0
- package/dist/src/cli/commands/context.d.ts.map +1 -0
- package/dist/src/cli/commands/context.js +205 -0
- package/dist/src/cli/commands/context.js.map +1 -0
- package/dist/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +112 -70
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/plan/increment-detector.js +2 -2
- package/dist/src/cli/commands/plan/increment-detector.js.map +1 -1
- package/dist/src/cli/commands/sync-spec-commits.js +1 -1
- package/dist/src/cli/commands/sync-spec-commits.js.map +1 -1
- package/dist/src/cli/commands/sync-specs.js +2 -2
- package/dist/src/cli/commands/sync-specs.js.map +1 -1
- package/dist/src/cli/helpers/github/increment-profile-selector.js +1 -1
- package/dist/src/cli/helpers/github/increment-profile-selector.js.map +1 -1
- package/dist/src/cli/helpers/init/external-import.d.ts +3 -0
- package/dist/src/cli/helpers/init/external-import.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/external-import.js +17 -4
- package/dist/src/cli/helpers/init/external-import.js.map +1 -1
- package/dist/src/cli/helpers/init/index.d.ts +1 -0
- package/dist/src/cli/helpers/init/index.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/index.js +2 -0
- package/dist/src/cli/helpers/init/index.js.map +1 -1
- package/dist/src/cli/helpers/init/jira-ado-auto-detect.d.ts +70 -0
- package/dist/src/cli/helpers/init/jira-ado-auto-detect.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/jira-ado-auto-detect.js +214 -4
- package/dist/src/cli/helpers/init/jira-ado-auto-detect.js.map +1 -1
- package/dist/src/cli/helpers/init/living-docs-preflight.d.ts +4 -0
- package/dist/src/cli/helpers/init/living-docs-preflight.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/living-docs-preflight.js +34 -3
- package/dist/src/cli/helpers/init/living-docs-preflight.js.map +1 -1
- package/dist/src/cli/helpers/init/testing-config.d.ts +3 -0
- package/dist/src/cli/helpers/init/testing-config.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/testing-config.js +9 -2
- package/dist/src/cli/helpers/init/testing-config.js.map +1 -1
- package/dist/src/cli/helpers/init/translation-config.d.ts +3 -0
- package/dist/src/cli/helpers/init/translation-config.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/translation-config.js +21 -4
- package/dist/src/cli/helpers/init/translation-config.js.map +1 -1
- package/dist/src/cli/helpers/init/wizard-navigation.d.ts +45 -0
- package/dist/src/cli/helpers/init/wizard-navigation.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/wizard-navigation.js +97 -0
- package/dist/src/cli/helpers/init/wizard-navigation.js.map +1 -0
- package/dist/src/cli/workers/living-docs-worker.js +66 -1
- package/dist/src/cli/workers/living-docs-worker.js.map +1 -1
- package/dist/src/config/types.d.ts +203 -1208
- package/dist/src/config/types.d.ts.map +1 -1
- package/dist/src/core/discrepancy/increment-generator.d.ts.map +1 -1
- package/dist/src/core/discrepancy/increment-generator.js +5 -2
- package/dist/src/core/discrepancy/increment-generator.js.map +1 -1
- package/dist/src/core/increment/duplicate-detector.js +2 -2
- package/dist/src/core/increment/duplicate-detector.js.map +1 -1
- package/dist/src/core/increment/increment-archiver.d.ts +49 -4
- package/dist/src/core/increment/increment-archiver.d.ts.map +1 -1
- package/dist/src/core/increment/increment-archiver.js +123 -22
- package/dist/src/core/increment/increment-archiver.js.map +1 -1
- package/dist/src/core/increment/increment-status.js +2 -2
- package/dist/src/core/increment/increment-status.js.map +1 -1
- package/dist/src/core/increment/increment-utils.d.ts +150 -0
- package/dist/src/core/increment/increment-utils.d.ts.map +1 -1
- package/dist/src/core/increment/increment-utils.js +216 -4
- package/dist/src/core/increment/increment-utils.js.map +1 -1
- package/dist/src/core/increment/metadata-validator.js +1 -1
- package/dist/src/core/increment/metadata-validator.js.map +1 -1
- package/dist/src/core/living-docs/feature-archiver.d.ts +4 -0
- package/dist/src/core/living-docs/feature-archiver.d.ts.map +1 -1
- package/dist/src/core/living-docs/feature-archiver.js +32 -10
- package/dist/src/core/living-docs/feature-archiver.js.map +1 -1
- package/dist/src/core/living-docs/feature-id-manager.d.ts.map +1 -1
- package/dist/src/core/living-docs/feature-id-manager.js +8 -4
- package/dist/src/core/living-docs/feature-id-manager.js.map +1 -1
- package/dist/src/core/living-docs/governance/ecosystem-detector.d.ts +38 -0
- package/dist/src/core/living-docs/governance/ecosystem-detector.d.ts.map +1 -0
- package/dist/src/core/living-docs/governance/ecosystem-detector.js +325 -0
- package/dist/src/core/living-docs/governance/ecosystem-detector.js.map +1 -0
- package/dist/src/core/living-docs/governance/frontend-standards-parser.d.ts +74 -0
- package/dist/src/core/living-docs/governance/frontend-standards-parser.d.ts.map +1 -0
- package/dist/src/core/living-docs/governance/frontend-standards-parser.js +366 -0
- package/dist/src/core/living-docs/governance/frontend-standards-parser.js.map +1 -0
- package/dist/src/core/living-docs/governance/go-standards-parser.d.ts +64 -0
- package/dist/src/core/living-docs/governance/go-standards-parser.d.ts.map +1 -0
- package/dist/src/core/living-docs/governance/go-standards-parser.js +229 -0
- package/dist/src/core/living-docs/governance/go-standards-parser.js.map +1 -0
- package/dist/src/core/living-docs/governance/index.d.ts +50 -0
- package/dist/src/core/living-docs/governance/index.d.ts.map +1 -0
- package/dist/src/core/living-docs/governance/index.js +56 -0
- package/dist/src/core/living-docs/governance/index.js.map +1 -0
- package/dist/src/core/living-docs/governance/java-standards-parser.d.ts +89 -0
- package/dist/src/core/living-docs/governance/java-standards-parser.d.ts.map +1 -0
- package/dist/src/core/living-docs/governance/java-standards-parser.js +356 -0
- package/dist/src/core/living-docs/governance/java-standards-parser.js.map +1 -0
- package/dist/src/core/living-docs/governance/python-standards-parser.d.ts +83 -0
- package/dist/src/core/living-docs/governance/python-standards-parser.d.ts.map +1 -0
- package/dist/src/core/living-docs/governance/python-standards-parser.js +347 -0
- package/dist/src/core/living-docs/governance/python-standards-parser.js.map +1 -0
- package/dist/src/core/living-docs/governance/standards-generator.d.ts +38 -0
- package/dist/src/core/living-docs/governance/standards-generator.d.ts.map +1 -0
- package/dist/src/core/living-docs/governance/standards-generator.js +476 -0
- package/dist/src/core/living-docs/governance/standards-generator.js.map +1 -0
- package/dist/src/core/living-docs/hierarchy-mapper.js +3 -3
- package/dist/src/core/living-docs/hierarchy-mapper.js.map +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.d.ts +18 -0
- package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js +299 -0
- package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.d.ts +15 -0
- package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.js +138 -0
- package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.d.ts +24 -0
- package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.js +198 -0
- package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.d.ts +17 -0
- package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.js +241 -0
- package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/index.d.ts +28 -0
- package/dist/src/core/living-docs/intelligent-analyzer/index.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/index.js +197 -0
- package/dist/src/core/living-docs/intelligent-analyzer/index.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.d.ts +22 -0
- package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js +482 -0
- package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.d.ts +42 -0
- package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.js +343 -0
- package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts +190 -0
- package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/types.js +7 -0
- package/dist/src/core/living-docs/intelligent-analyzer/types.js.map +1 -0
- package/dist/src/core/living-docs/living-docs-sync.d.ts +11 -3
- package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.js +53 -10
- package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
- package/dist/src/core/living-docs/module-analyzer.d.ts +22 -0
- package/dist/src/core/living-docs/module-analyzer.d.ts.map +1 -1
- package/dist/src/core/living-docs/module-analyzer.js +123 -19
- package/dist/src/core/living-docs/module-analyzer.js.map +1 -1
- package/dist/src/core/llm/provider-factory.js +2 -2
- package/dist/src/core/llm/provider-factory.js.map +1 -1
- package/dist/src/core/llm/providers/anthropic-provider.js +1 -1
- package/dist/src/core/llm/providers/bedrock-provider.d.ts.map +1 -1
- package/dist/src/core/llm/providers/bedrock-provider.js +8 -4
- package/dist/src/core/llm/providers/bedrock-provider.js.map +1 -1
- package/dist/src/core/sync/spec-increment-mapper.js +3 -3
- package/dist/src/core/sync/spec-increment-mapper.js.map +1 -1
- package/dist/src/importers/item-converter.d.ts +25 -0
- package/dist/src/importers/item-converter.d.ts.map +1 -1
- package/dist/src/importers/item-converter.js +135 -5
- package/dist/src/importers/item-converter.js.map +1 -1
- package/dist/src/importers/jira-importer.d.ts +14 -0
- package/dist/src/importers/jira-importer.d.ts.map +1 -1
- package/dist/src/importers/jira-importer.js +75 -0
- package/dist/src/importers/jira-importer.js.map +1 -1
- package/dist/src/init/architecture/types.d.ts +33 -140
- package/dist/src/init/architecture/types.d.ts.map +1 -1
- package/dist/src/init/compliance/types.d.ts +30 -27
- package/dist/src/init/compliance/types.d.ts.map +1 -1
- package/dist/src/init/repo/types.d.ts +11 -34
- package/dist/src/init/repo/types.d.ts.map +1 -1
- package/dist/src/init/research/src/config/types.d.ts +15 -82
- package/dist/src/init/research/src/config/types.d.ts.map +1 -1
- package/dist/src/init/research/types.d.ts +38 -93
- package/dist/src/init/research/types.d.ts.map +1 -1
- package/dist/src/init/team/types.d.ts +4 -42
- package/dist/src/init/team/types.d.ts.map +1 -1
- package/dist/src/integrations/jira/jira-token-provider.d.ts +93 -0
- package/dist/src/integrations/jira/jira-token-provider.d.ts.map +1 -0
- package/dist/src/integrations/jira/jira-token-provider.js +160 -0
- package/dist/src/integrations/jira/jira-token-provider.js.map +1 -0
- package/dist/src/sync/ado-reconciler.d.ts +92 -0
- package/dist/src/sync/ado-reconciler.d.ts.map +1 -0
- package/dist/src/sync/ado-reconciler.js +335 -0
- package/dist/src/sync/ado-reconciler.js.map +1 -0
- package/dist/src/sync/jira-reconciler.d.ts +106 -0
- package/dist/src/sync/jira-reconciler.d.ts.map +1 -0
- package/dist/src/sync/jira-reconciler.js +405 -0
- package/dist/src/sync/jira-reconciler.js.map +1 -0
- package/dist/src/types/dashboard-cache.d.ts +181 -0
- package/dist/src/types/dashboard-cache.d.ts.map +1 -0
- package/dist/src/types/dashboard-cache.js +65 -0
- package/dist/src/types/dashboard-cache.js.map +1 -0
- package/dist/src/types/model-selection.d.ts +6 -4
- package/dist/src/types/model-selection.d.ts.map +1 -1
- package/dist/src/types/model-selection.js +3 -1
- package/dist/src/types/model-selection.js.map +1 -1
- package/dist/src/utils/docs-validator.d.ts +131 -0
- package/dist/src/utils/docs-validator.d.ts.map +1 -0
- package/dist/src/utils/docs-validator.js +529 -0
- package/dist/src/utils/docs-validator.js.map +1 -0
- package/dist/src/utils/external-tool-drift-detector.d.ts +1 -1
- package/dist/src/utils/external-tool-drift-detector.d.ts.map +1 -1
- package/dist/src/utils/external-tool-drift-detector.js +5 -4
- package/dist/src/utils/external-tool-drift-detector.js.map +1 -1
- package/dist/src/utils/feature-id-collision.js +1 -1
- package/dist/src/utils/feature-id-collision.js.map +1 -1
- package/dist/src/utils/feature-id-derivation.d.ts +8 -3
- package/dist/src/utils/feature-id-derivation.d.ts.map +1 -1
- package/dist/src/utils/feature-id-derivation.js +14 -6
- package/dist/src/utils/feature-id-derivation.js.map +1 -1
- package/dist/src/utils/html-to-mdx.d.ts +1 -0
- package/dist/src/utils/html-to-mdx.d.ts.map +1 -1
- package/dist/src/utils/html-to-mdx.js +43 -5
- package/dist/src/utils/html-to-mdx.js.map +1 -1
- package/dist/src/utils/model-selection.d.ts +3 -4
- package/dist/src/utils/model-selection.d.ts.map +1 -1
- package/dist/src/utils/model-selection.js +3 -4
- package/dist/src/utils/model-selection.js.map +1 -1
- package/package.json +1 -1
- package/plugins/specweave/agents/code-standards-detective/AGENT.md +48 -0
- package/plugins/specweave/agents/pm/AGENT.md +10 -7
- package/plugins/specweave/commands/specweave-archive-features.md +5 -7
- package/plugins/specweave/commands/specweave-archive.md +2 -1
- package/plugins/specweave/commands/specweave-costs.md +4 -4
- package/plugins/specweave/commands/specweave-do.md +44 -10
- package/plugins/specweave/commands/specweave-done.md +109 -0
- package/plugins/specweave/commands/specweave-import-external.md +45 -18
- package/plugins/specweave/commands/specweave-increment.md +331 -33
- package/plugins/specweave/commands/specweave-jobs.md +2 -2
- package/plugins/specweave/commands/specweave-progress.md +4 -4
- package/plugins/specweave/commands/specweave-restore-feature.md +5 -4
- package/plugins/specweave/commands/specweave-sync-docs.md +1 -1
- package/plugins/specweave/commands/specweave-sync-specs.md +216 -322
- package/plugins/specweave/commands/specweave-validate-features.md +13 -8
- package/plugins/specweave/commands/specweave-validate.md +27 -1
- package/plugins/specweave/hooks/docs-changed.sh.backup +79 -0
- package/plugins/specweave/hooks/hooks.json +43 -4
- package/plugins/specweave/hooks/human-input-required.sh.backup +75 -0
- package/plugins/specweave/hooks/lib/common-setup.sh +375 -0
- package/plugins/specweave/hooks/lib/crash-prevention.sh +336 -0
- package/plugins/specweave/hooks/post-first-increment.sh.backup +61 -0
- package/plugins/specweave/hooks/post-increment-change.sh.backup +98 -0
- package/plugins/specweave/hooks/post-increment-completion.sh.backup +231 -0
- package/plugins/specweave/hooks/post-increment-planning.sh.backup +1048 -0
- package/plugins/specweave/hooks/post-increment-status-change.sh.backup +147 -0
- package/plugins/specweave/hooks/post-spec-update.sh.backup +158 -0
- package/plugins/specweave/hooks/post-task-completion.sh +4 -23
- package/plugins/specweave/hooks/post-user-story-complete.sh.backup +179 -0
- package/plugins/specweave/hooks/pre-command-deduplication.sh +1 -6
- package/plugins/specweave/hooks/pre-command-deduplication.sh.backup +83 -0
- package/plugins/specweave/hooks/pre-implementation.sh.backup +67 -0
- package/plugins/specweave/hooks/pre-task-completion.sh +8 -37
- package/plugins/specweave/hooks/pre-task-completion.sh.backup +194 -0
- package/plugins/specweave/hooks/pre-tool-use.sh +2 -11
- package/plugins/specweave/hooks/pre-tool-use.sh.backup +133 -0
- package/plugins/specweave/hooks/spec-project-validator.sh +80 -25
- package/plugins/specweave/hooks/universal/dispatcher.mjs +135 -42
- package/plugins/specweave/hooks/universal/fail-fast-wrapper.sh +183 -0
- package/plugins/specweave/hooks/user-prompt-submit.sh +140 -38
- package/plugins/specweave/hooks/user-prompt-submit.sh.backup +386 -0
- package/plugins/specweave/hooks/v2/dispatchers/post-tool-use.sh +12 -0
- package/plugins/specweave/hooks/v2/dispatchers/session-start.sh +89 -0
- package/plugins/specweave/hooks/v2/guards/bash-file-guard.sh +211 -0
- package/plugins/specweave/hooks/v2/guards/bash-file-guard.test.sh +163 -0
- package/plugins/specweave/hooks/v2/guards/completion-guard.sh +26 -28
- package/plugins/specweave/hooks/v2/guards/features-folder-guard.sh +50 -0
- package/plugins/specweave/hooks/v2/guards/increment-duplicate-guard.sh +135 -0
- package/plugins/specweave/lib/vendor/core/increment/duplicate-detector.js +2 -2
- package/plugins/specweave/lib/vendor/core/increment/duplicate-detector.js.map +1 -1
- package/plugins/specweave/scripts/README.md +166 -0
- package/plugins/specweave/scripts/cleanup-state.sh +142 -0
- package/plugins/specweave/scripts/force-kill.sh +142 -0
- package/plugins/specweave/scripts/jobs.js +171 -0
- package/plugins/specweave/scripts/progress.js +170 -0
- package/plugins/specweave/scripts/read-costs.sh +132 -0
- package/plugins/specweave/scripts/read-jobs.sh +324 -0
- package/plugins/specweave/scripts/read-progress.sh +185 -0
- package/plugins/specweave/scripts/read-status.sh +146 -0
- package/plugins/specweave/scripts/read-workflow.sh +173 -0
- package/plugins/specweave/scripts/rebuild-dashboard-cache.sh +327 -0
- package/plugins/specweave/scripts/session-watchdog.sh +192 -0
- package/plugins/specweave/scripts/status.js +154 -0
- package/plugins/specweave/scripts/update-dashboard-cache.sh +281 -0
- package/plugins/specweave/skills/code-standards-analyzer/SKILL.md +58 -6
- package/plugins/specweave/skills/increment-planner/SKILL.md +388 -48
- package/plugins/specweave/skills/increment-planner/templates/spec-multi-project.md +17 -7
- package/plugins/specweave/skills/increment-planner/templates/spec-single-project.md +6 -1
- package/plugins/specweave/skills/increment-planner/templates/tasks-multi-project.md +1 -1
- package/plugins/specweave/skills/increment-planner/templates/tasks-single-project.md +1 -1
- package/plugins/specweave/skills/instant-status/SKILL.md +70 -0
- package/plugins/specweave-ado/commands/cleanup-duplicates.md +212 -0
- package/plugins/specweave-ado/commands/reconcile.md +120 -0
- package/plugins/specweave-ado/hooks/post-living-docs-update.sh.backup +353 -0
- package/plugins/specweave-ado/hooks/post-task-completion.sh.backup +172 -0
- package/plugins/specweave-ado/lib/ado-duplicate-detector.js +279 -0
- package/plugins/specweave-ado/lib/ado-duplicate-detector.ts +407 -0
- package/plugins/specweave-ado/lib/enhanced-ado-sync.js +170 -0
- package/plugins/specweave-docs/commands/build.md +32 -4
- package/plugins/specweave-docs/commands/preview.md +43 -1
- package/plugins/specweave-docs/commands/validate.md +250 -0
- package/plugins/specweave-github/agents/github-manager/AGENT.md +2 -2
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +1262 -0
- package/plugins/specweave-github/hooks/post-task-completion.sh.backup +258 -0
- package/plugins/specweave-github/lib/enhanced-github-sync.js +220 -0
- package/plugins/specweave-infrastructure/skills/hetzner-provisioner/README.md +1 -1
- package/plugins/specweave-jira/agents/jira-manager/AGENT.md +1 -1
- package/plugins/specweave-jira/agents/jira-multi-project-mapper/AGENT.md +530 -0
- package/plugins/specweave-jira/agents/jira-sync-judge/AGENT.md +438 -0
- package/plugins/specweave-jira/commands/cleanup-duplicates.md +219 -0
- package/plugins/specweave-jira/commands/close.md +297 -0
- package/plugins/specweave-jira/commands/create.md +198 -0
- package/plugins/specweave-jira/commands/reconcile.md +123 -0
- package/plugins/specweave-jira/commands/status.md +215 -0
- package/plugins/specweave-jira/hooks/post-task-completion.sh.backup +172 -0
- package/plugins/specweave-jira/lib/enhanced-jira-sync.js +134 -0
- package/plugins/specweave-jira/lib/jira-duplicate-detector.js +296 -0
- package/plugins/specweave-jira/lib/jira-duplicate-detector.ts +434 -0
- package/plugins/specweave-jira/lib/jira-permission-gate.js +160 -0
- package/plugins/specweave-jira/lib/jira-permission-gate.ts +276 -0
- package/plugins/specweave-jira/lib/jira-profile-resolver.js +222 -0
- package/plugins/specweave-jira/lib/jira-profile-resolver.ts +427 -0
- package/plugins/specweave-jira/reference/jira-specweave-mapping.md +16 -11
- package/plugins/specweave-release/commands/specweave-release-npm.md +140 -14
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +1254 -0
- package/plugins/specweave-release/hooks/post-task-completion.sh.backup +110 -0
- package/plugins/specweave/hooks/post-edit-spec.sh +0 -265
- package/plugins/specweave/hooks/post-write-spec.sh +0 -267
- package/plugins/specweave/hooks/pre-edit-spec.sh +0 -151
- package/plugins/specweave/hooks/pre-write-spec.sh +0 -151
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Azure DevOps Duplicate Detector (v0.33.0)
|
|
3
|
+
*
|
|
4
|
+
* Implements 3-phase duplicate protection for ADO work items:
|
|
5
|
+
* 1. Detection: Check before create
|
|
6
|
+
* 2. Verification: Count check after create
|
|
7
|
+
* 3. Reflection: Auto-close duplicates
|
|
8
|
+
*
|
|
9
|
+
* Mirrors the GitHub DuplicateDetector pattern for consistency.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { Logger, consoleLogger } from '../../../src/utils/logger.js';
|
|
13
|
+
|
|
14
|
+
export interface AdoWorkItem {
|
|
15
|
+
id: number;
|
|
16
|
+
title: string;
|
|
17
|
+
state: string;
|
|
18
|
+
createdDate: string;
|
|
19
|
+
url?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface DuplicateGroup {
|
|
23
|
+
title: string;
|
|
24
|
+
items: AdoWorkItem[];
|
|
25
|
+
keepItem: AdoWorkItem;
|
|
26
|
+
duplicates: AdoWorkItem[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface DetectionResult {
|
|
30
|
+
found: boolean;
|
|
31
|
+
existingItem?: AdoWorkItem;
|
|
32
|
+
count: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface VerificationResult {
|
|
36
|
+
success: boolean;
|
|
37
|
+
expectedCount: number;
|
|
38
|
+
actualCount: number;
|
|
39
|
+
duplicates: AdoWorkItem[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface CleanupResult {
|
|
43
|
+
closedCount: number;
|
|
44
|
+
keptCount: number;
|
|
45
|
+
errors: string[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export class AdoDuplicateDetector {
|
|
49
|
+
private org: string;
|
|
50
|
+
private project: string;
|
|
51
|
+
private pat: string;
|
|
52
|
+
private logger: Logger;
|
|
53
|
+
|
|
54
|
+
constructor(options: {
|
|
55
|
+
org?: string;
|
|
56
|
+
project?: string;
|
|
57
|
+
pat?: string;
|
|
58
|
+
logger?: Logger;
|
|
59
|
+
} = {}) {
|
|
60
|
+
this.org = options.org || process.env.AZURE_DEVOPS_ORG || '';
|
|
61
|
+
this.project = options.project || process.env.AZURE_DEVOPS_PROJECT || '';
|
|
62
|
+
this.pat = options.pat || process.env.AZURE_DEVOPS_PAT || process.env.AZURE_DEVOPS_TOKEN || '';
|
|
63
|
+
this.logger = options.logger || consoleLogger;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Phase 1: Check if work item exists before creating
|
|
68
|
+
*/
|
|
69
|
+
async checkBeforeCreate(
|
|
70
|
+
titlePattern: string,
|
|
71
|
+
incrementId?: string
|
|
72
|
+
): Promise<DetectionResult> {
|
|
73
|
+
try {
|
|
74
|
+
const items = await this.searchWorkItems(titlePattern);
|
|
75
|
+
|
|
76
|
+
if (items.length > 0) {
|
|
77
|
+
return {
|
|
78
|
+
found: true,
|
|
79
|
+
existingItem: items[0],
|
|
80
|
+
count: items.length,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return { found: false, count: 0 };
|
|
85
|
+
} catch (error: any) {
|
|
86
|
+
this.logger.log(`ā ļø Detection check failed: ${error.message}`);
|
|
87
|
+
// Graceful degradation - continue anyway
|
|
88
|
+
return { found: false, count: 0 };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Phase 2: Verify count after creation
|
|
94
|
+
*/
|
|
95
|
+
async verifyAfterCreate(
|
|
96
|
+
titlePattern: string,
|
|
97
|
+
expectedCount: number = 1
|
|
98
|
+
): Promise<VerificationResult> {
|
|
99
|
+
try {
|
|
100
|
+
const items = await this.searchWorkItems(titlePattern);
|
|
101
|
+
|
|
102
|
+
if (items.length > expectedCount) {
|
|
103
|
+
// Duplicates detected!
|
|
104
|
+
const sorted = items.sort(
|
|
105
|
+
(a, b) => a.id - b.id // Sort by ID (lowest = oldest)
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
success: false,
|
|
110
|
+
expectedCount,
|
|
111
|
+
actualCount: items.length,
|
|
112
|
+
duplicates: sorted.slice(expectedCount), // All items after expected count
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
success: true,
|
|
118
|
+
expectedCount,
|
|
119
|
+
actualCount: items.length,
|
|
120
|
+
duplicates: [],
|
|
121
|
+
};
|
|
122
|
+
} catch (error: any) {
|
|
123
|
+
this.logger.log(`ā ļø Verification check failed: ${error.message}`);
|
|
124
|
+
return {
|
|
125
|
+
success: true, // Assume success on error
|
|
126
|
+
expectedCount,
|
|
127
|
+
actualCount: expectedCount,
|
|
128
|
+
duplicates: [],
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Phase 3: Auto-close duplicates
|
|
135
|
+
*/
|
|
136
|
+
async closeDuplicates(
|
|
137
|
+
duplicates: AdoWorkItem[],
|
|
138
|
+
keepItemId: number
|
|
139
|
+
): Promise<CleanupResult> {
|
|
140
|
+
const result: CleanupResult = {
|
|
141
|
+
closedCount: 0,
|
|
142
|
+
keptCount: 1,
|
|
143
|
+
errors: [],
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
for (const item of duplicates) {
|
|
147
|
+
try {
|
|
148
|
+
await this.closeWorkItem(item.id, keepItemId);
|
|
149
|
+
result.closedCount++;
|
|
150
|
+
this.logger.log(` ā
Closed #${item.id} (duplicate of #${keepItemId})`);
|
|
151
|
+
} catch (error: any) {
|
|
152
|
+
result.errors.push(`#${item.id}: ${error.message}`);
|
|
153
|
+
this.logger.log(` ā Failed to close #${item.id}: ${error.message}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return result;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Full cleanup: Find and close all duplicates for a feature
|
|
162
|
+
*/
|
|
163
|
+
async cleanupFeatureDuplicates(
|
|
164
|
+
featureId: string,
|
|
165
|
+
dryRun: boolean = false
|
|
166
|
+
): Promise<{
|
|
167
|
+
groups: DuplicateGroup[];
|
|
168
|
+
totalItems: number;
|
|
169
|
+
duplicateCount: number;
|
|
170
|
+
closedCount: number;
|
|
171
|
+
}> {
|
|
172
|
+
// 1. Search for all work items with feature ID
|
|
173
|
+
const searchPattern = `[${featureId}]`;
|
|
174
|
+
const items = await this.searchWorkItems(searchPattern);
|
|
175
|
+
|
|
176
|
+
this.logger.log(`\nš Scanning for duplicates in Feature ${featureId}...`);
|
|
177
|
+
this.logger.log(` Found ${items.length} total work items`);
|
|
178
|
+
|
|
179
|
+
// 2. Group by title
|
|
180
|
+
const groups = this.groupByTitle(items);
|
|
181
|
+
const duplicateGroups = groups.filter(g => g.duplicates.length > 0);
|
|
182
|
+
|
|
183
|
+
if (duplicateGroups.length === 0) {
|
|
184
|
+
this.logger.log(` ā
No duplicates found!`);
|
|
185
|
+
return {
|
|
186
|
+
groups: [],
|
|
187
|
+
totalItems: items.length,
|
|
188
|
+
duplicateCount: 0,
|
|
189
|
+
closedCount: 0,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
this.logger.log(` Detected ${duplicateGroups.length} duplicate groups:\n`);
|
|
194
|
+
|
|
195
|
+
// 3. Display groups
|
|
196
|
+
for (let i = 0; i < duplicateGroups.length; i++) {
|
|
197
|
+
const group = duplicateGroups[i];
|
|
198
|
+
this.logger.log(` š Group ${i + 1}: "${group.title.substring(0, 50)}..."`);
|
|
199
|
+
this.logger.log(` - #${group.keepItem.id} (KEEP) - Created ${group.keepItem.createdDate.split('T')[0]}`);
|
|
200
|
+
for (const dup of group.duplicates) {
|
|
201
|
+
this.logger.log(` - #${dup.id} (CLOSE) - Created ${dup.createdDate.split('T')[0]} - DUPLICATE`);
|
|
202
|
+
}
|
|
203
|
+
this.logger.log('');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const totalDuplicates = duplicateGroups.reduce((sum, g) => sum + g.duplicates.length, 0);
|
|
207
|
+
|
|
208
|
+
if (dryRun) {
|
|
209
|
+
this.logger.log(`\nā
Dry run complete!`);
|
|
210
|
+
this.logger.log(` Total work items: ${items.length}`);
|
|
211
|
+
this.logger.log(` Duplicate groups: ${duplicateGroups.length}`);
|
|
212
|
+
this.logger.log(` Work items to close: ${totalDuplicates}`);
|
|
213
|
+
this.logger.log(`\nā ļø This was a DRY RUN - no changes made.`);
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
groups: duplicateGroups,
|
|
217
|
+
totalItems: items.length,
|
|
218
|
+
duplicateCount: totalDuplicates,
|
|
219
|
+
closedCount: 0,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// 4. Close duplicates
|
|
224
|
+
let closedCount = 0;
|
|
225
|
+
this.logger.log(`šļø Closing duplicates...`);
|
|
226
|
+
|
|
227
|
+
for (const group of duplicateGroups) {
|
|
228
|
+
const result = await this.closeDuplicates(group.duplicates, group.keepItem.id);
|
|
229
|
+
closedCount += result.closedCount;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
this.logger.log(`\nā
Cleanup complete!`);
|
|
233
|
+
this.logger.log(` Closed: ${closedCount} duplicates`);
|
|
234
|
+
this.logger.log(` Kept: ${duplicateGroups.length} original work items`);
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
groups: duplicateGroups,
|
|
238
|
+
totalItems: items.length,
|
|
239
|
+
duplicateCount: totalDuplicates,
|
|
240
|
+
closedCount,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Group work items by title
|
|
246
|
+
*/
|
|
247
|
+
private groupByTitle(items: AdoWorkItem[]): DuplicateGroup[] {
|
|
248
|
+
const titleMap = new Map<string, AdoWorkItem[]>();
|
|
249
|
+
|
|
250
|
+
for (const item of items) {
|
|
251
|
+
const existing = titleMap.get(item.title) || [];
|
|
252
|
+
existing.push(item);
|
|
253
|
+
titleMap.set(item.title, existing);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const groups: DuplicateGroup[] = [];
|
|
257
|
+
|
|
258
|
+
for (const [title, groupItems] of titleMap) {
|
|
259
|
+
// Sort by ID (lowest = oldest)
|
|
260
|
+
const sorted = groupItems.sort((a, b) => a.id - b.id);
|
|
261
|
+
|
|
262
|
+
groups.push({
|
|
263
|
+
title,
|
|
264
|
+
items: sorted,
|
|
265
|
+
keepItem: sorted[0],
|
|
266
|
+
duplicates: sorted.slice(1),
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return groups;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Search for work items using WIQL
|
|
275
|
+
*/
|
|
276
|
+
private async searchWorkItems(titlePattern: string): Promise<AdoWorkItem[]> {
|
|
277
|
+
if (!this.org || !this.pat) {
|
|
278
|
+
throw new Error('ADO credentials not configured');
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const wiql = {
|
|
282
|
+
query: `SELECT [System.Id], [System.Title], [System.State], [System.CreatedDate]
|
|
283
|
+
FROM WorkItems
|
|
284
|
+
WHERE [System.TeamProject] = '${this.project}'
|
|
285
|
+
AND [System.Title] CONTAINS '${titlePattern}'
|
|
286
|
+
ORDER BY [System.Id] ASC`,
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
const auth = Buffer.from(`:${this.pat}`).toString('base64');
|
|
290
|
+
const url = `https://dev.azure.com/${this.org}/${this.project}/_apis/wit/wiql?api-version=7.1`;
|
|
291
|
+
|
|
292
|
+
const response = await fetch(url, {
|
|
293
|
+
method: 'POST',
|
|
294
|
+
headers: {
|
|
295
|
+
Authorization: `Basic ${auth}`,
|
|
296
|
+
'Content-Type': 'application/json',
|
|
297
|
+
},
|
|
298
|
+
body: JSON.stringify(wiql),
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
if (!response.ok) {
|
|
302
|
+
throw new Error(`WIQL query failed: ${response.status}`);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const data = await response.json();
|
|
306
|
+
const workItemIds = data.workItems?.map((wi: any) => wi.id) || [];
|
|
307
|
+
|
|
308
|
+
if (workItemIds.length === 0) {
|
|
309
|
+
return [];
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Fetch full work item details
|
|
313
|
+
return this.getWorkItemDetails(workItemIds);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Get work item details by IDs
|
|
318
|
+
*/
|
|
319
|
+
private async getWorkItemDetails(ids: number[]): Promise<AdoWorkItem[]> {
|
|
320
|
+
if (ids.length === 0) return [];
|
|
321
|
+
|
|
322
|
+
const auth = Buffer.from(`:${this.pat}`).toString('base64');
|
|
323
|
+
const url = `https://dev.azure.com/${this.org}/_apis/wit/workitems?ids=${ids.join(',')}&api-version=7.1`;
|
|
324
|
+
|
|
325
|
+
const response = await fetch(url, {
|
|
326
|
+
headers: {
|
|
327
|
+
Authorization: `Basic ${auth}`,
|
|
328
|
+
'Content-Type': 'application/json',
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
if (!response.ok) {
|
|
333
|
+
throw new Error(`Failed to get work items: ${response.status}`);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const data = await response.json();
|
|
337
|
+
return (data.value || []).map((wi: any) => ({
|
|
338
|
+
id: wi.id,
|
|
339
|
+
title: wi.fields['System.Title'],
|
|
340
|
+
state: wi.fields['System.State'],
|
|
341
|
+
createdDate: wi.fields['System.CreatedDate'],
|
|
342
|
+
url: wi._links?.html?.href,
|
|
343
|
+
}));
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Close a work item with duplicate comment
|
|
348
|
+
*/
|
|
349
|
+
private async closeWorkItem(workItemId: number, originalId: number): Promise<void> {
|
|
350
|
+
const auth = Buffer.from(`:${this.pat}`).toString('base64');
|
|
351
|
+
const url = `https://dev.azure.com/${this.org}/_apis/wit/workitems/${workItemId}?api-version=7.1`;
|
|
352
|
+
|
|
353
|
+
const comment = `## Duplicate of #${originalId}
|
|
354
|
+
|
|
355
|
+
This work item was automatically closed by SpecWeave cleanup because it is a duplicate.
|
|
356
|
+
|
|
357
|
+
The original work item (#${originalId}) contains the same content and should be used for tracking instead.
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
š¤ Auto-closed by SpecWeave Duplicate Cleanup`;
|
|
361
|
+
|
|
362
|
+
const operations = [
|
|
363
|
+
{
|
|
364
|
+
op: 'add',
|
|
365
|
+
path: '/fields/System.State',
|
|
366
|
+
value: 'Closed',
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
op: 'add',
|
|
370
|
+
path: '/fields/System.History',
|
|
371
|
+
value: comment,
|
|
372
|
+
},
|
|
373
|
+
];
|
|
374
|
+
|
|
375
|
+
const response = await fetch(url, {
|
|
376
|
+
method: 'PATCH',
|
|
377
|
+
headers: {
|
|
378
|
+
Authorization: `Basic ${auth}`,
|
|
379
|
+
'Content-Type': 'application/json-patch+json',
|
|
380
|
+
},
|
|
381
|
+
body: JSON.stringify(operations),
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
if (!response.ok) {
|
|
385
|
+
const error = await response.text();
|
|
386
|
+
throw new Error(`Failed to close work item: ${response.status} - ${error}`);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Convenience function for quick duplicate cleanup
|
|
393
|
+
*/
|
|
394
|
+
export async function cleanupAdoDuplicates(
|
|
395
|
+
featureId: string,
|
|
396
|
+
dryRun: boolean = false
|
|
397
|
+
): Promise<{
|
|
398
|
+
groups: DuplicateGroup[];
|
|
399
|
+
totalItems: number;
|
|
400
|
+
duplicateCount: number;
|
|
401
|
+
closedCount: number;
|
|
402
|
+
}> {
|
|
403
|
+
const detector = new AdoDuplicateDetector();
|
|
404
|
+
return detector.cleanupFeatureDuplicates(featureId, dryRun);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
export default AdoDuplicateDetector;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { AdoClientV2 } from "./ado-client-v2.js";
|
|
2
|
+
import { EnhancedContentBuilder } from "../../../src/core/sync/enhanced-content-builder.js";
|
|
3
|
+
import { SpecIncrementMapper } from "../../../src/core/sync/spec-increment-mapper.js";
|
|
4
|
+
import { parseSpecContent } from "../../../src/core/spec-content-sync.js";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import fs from "fs/promises";
|
|
7
|
+
async function syncSpecToAdoWithEnhancedContent(options) {
|
|
8
|
+
const { specPath, organization, project, dryRun = false, verbose = false } = options;
|
|
9
|
+
try {
|
|
10
|
+
const baseSpec = await parseSpecContent(specPath);
|
|
11
|
+
if (!baseSpec) {
|
|
12
|
+
return {
|
|
13
|
+
success: false,
|
|
14
|
+
action: "error",
|
|
15
|
+
error: "Failed to parse spec content"
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
if (verbose) {
|
|
19
|
+
console.log(`\u{1F4C4} Parsed spec: ${baseSpec.identifier.compact}`);
|
|
20
|
+
}
|
|
21
|
+
const specId = baseSpec.identifier.full || baseSpec.identifier.compact;
|
|
22
|
+
const rootDir = await findSpecWeaveRoot(specPath);
|
|
23
|
+
const mapper = new SpecIncrementMapper(rootDir);
|
|
24
|
+
const mapping = await mapper.mapSpecToIncrements(specId);
|
|
25
|
+
if (verbose) {
|
|
26
|
+
console.log(`\u{1F517} Found ${mapping.increments.length} related increments`);
|
|
27
|
+
}
|
|
28
|
+
const taskMapping = buildTaskMapping(mapping.increments, organization, project);
|
|
29
|
+
const architectureDocs = await findArchitectureDocs(rootDir, specId);
|
|
30
|
+
const enhancedSpec = {
|
|
31
|
+
...baseSpec,
|
|
32
|
+
summary: baseSpec.description,
|
|
33
|
+
taskMapping,
|
|
34
|
+
architectureDocs
|
|
35
|
+
};
|
|
36
|
+
const builder = new EnhancedContentBuilder();
|
|
37
|
+
const description = builder.buildExternalDescription(enhancedSpec);
|
|
38
|
+
if (verbose) {
|
|
39
|
+
console.log(`\u{1F4DD} Generated description: ${description.length} characters`);
|
|
40
|
+
}
|
|
41
|
+
if (dryRun) {
|
|
42
|
+
console.log("\u{1F50D} DRY RUN - Would create/update feature with:");
|
|
43
|
+
console.log(` Title: ${baseSpec.title}`);
|
|
44
|
+
console.log(` Description length: ${description.length}`);
|
|
45
|
+
return {
|
|
46
|
+
success: true,
|
|
47
|
+
action: "no-change",
|
|
48
|
+
tasksLinked: taskMapping?.tasks.length || 0
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (!organization || !project) {
|
|
52
|
+
return {
|
|
53
|
+
success: false,
|
|
54
|
+
action: "error",
|
|
55
|
+
error: "Azure DevOps organization/project not specified"
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const profile = {
|
|
59
|
+
provider: "ado",
|
|
60
|
+
displayName: `${organization}/${project}`,
|
|
61
|
+
config: {
|
|
62
|
+
organization,
|
|
63
|
+
project
|
|
64
|
+
},
|
|
65
|
+
timeRange: { default: "1M", max: "6M" }
|
|
66
|
+
};
|
|
67
|
+
const pat = process.env.AZURE_DEVOPS_PAT || "";
|
|
68
|
+
const client = new AdoClientV2(profile, pat);
|
|
69
|
+
const existingFeature = await findExistingFeature(client, baseSpec.identifier.compact);
|
|
70
|
+
let result;
|
|
71
|
+
if (existingFeature) {
|
|
72
|
+
await client.updateWorkItem(existingFeature.id, {
|
|
73
|
+
title: `[${baseSpec.identifier.compact}] ${baseSpec.title}`,
|
|
74
|
+
description
|
|
75
|
+
});
|
|
76
|
+
result = {
|
|
77
|
+
success: true,
|
|
78
|
+
action: "updated",
|
|
79
|
+
featureId: existingFeature.id,
|
|
80
|
+
featureUrl: `https://dev.azure.com/${organization}/${project}/_workitems/edit/${existingFeature.id}`,
|
|
81
|
+
tasksLinked: taskMapping?.tasks.length || 0
|
|
82
|
+
};
|
|
83
|
+
} else {
|
|
84
|
+
const feature = await client.createEpic({
|
|
85
|
+
title: `[${baseSpec.identifier.compact}] ${baseSpec.title}`,
|
|
86
|
+
description,
|
|
87
|
+
tags: ["spec", "external-tool-sync"]
|
|
88
|
+
});
|
|
89
|
+
result = {
|
|
90
|
+
success: true,
|
|
91
|
+
action: "created",
|
|
92
|
+
featureId: feature.id,
|
|
93
|
+
featureUrl: `https://dev.azure.com/${organization}/${project}/_workitems/edit/${feature.id}`,
|
|
94
|
+
tasksLinked: taskMapping?.tasks.length || 0
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
if (verbose) {
|
|
98
|
+
console.log(`\u2705 ${result.action === "created" ? "Created" : "Updated"} feature #${result.featureId}`);
|
|
99
|
+
}
|
|
100
|
+
return result;
|
|
101
|
+
} catch (error) {
|
|
102
|
+
return {
|
|
103
|
+
success: false,
|
|
104
|
+
action: "error",
|
|
105
|
+
error: error.message
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async function findSpecWeaveRoot(specPath) {
|
|
110
|
+
let currentDir = path.dirname(specPath);
|
|
111
|
+
while (true) {
|
|
112
|
+
const specweaveDir = path.join(currentDir, ".specweave");
|
|
113
|
+
try {
|
|
114
|
+
await fs.access(specweaveDir);
|
|
115
|
+
return currentDir;
|
|
116
|
+
} catch {
|
|
117
|
+
const parentDir = path.dirname(currentDir);
|
|
118
|
+
if (parentDir === currentDir) {
|
|
119
|
+
throw new Error(".specweave directory not found");
|
|
120
|
+
}
|
|
121
|
+
currentDir = parentDir;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function buildTaskMapping(increments, organization, project) {
|
|
126
|
+
if (increments.length === 0) return void 0;
|
|
127
|
+
const firstIncrement = increments[0];
|
|
128
|
+
const tasks = firstIncrement.tasks.map((task) => ({
|
|
129
|
+
id: task.id,
|
|
130
|
+
title: task.title,
|
|
131
|
+
userStories: task.userStories
|
|
132
|
+
}));
|
|
133
|
+
return {
|
|
134
|
+
incrementId: firstIncrement.id,
|
|
135
|
+
tasks,
|
|
136
|
+
tasksUrl: `https://dev.azure.com/${organization}/${project}/_git/repo?path=/.specweave/increments/${firstIncrement.id}/tasks.md`
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
async function findArchitectureDocs(rootDir, specId) {
|
|
140
|
+
const docs = [];
|
|
141
|
+
const archDir = path.join(rootDir, ".specweave/docs/internal/architecture");
|
|
142
|
+
try {
|
|
143
|
+
const adrDir = path.join(archDir, "adr");
|
|
144
|
+
try {
|
|
145
|
+
const adrs = await fs.readdir(adrDir);
|
|
146
|
+
const relatedAdrs = adrs.filter((file) => file.includes(specId.replace("spec-", "")));
|
|
147
|
+
for (const adr of relatedAdrs) {
|
|
148
|
+
docs.push({
|
|
149
|
+
type: "adr",
|
|
150
|
+
path: path.join(adrDir, adr),
|
|
151
|
+
title: adr.replace(".md", "").replace(/-/g, " ")
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
} catch {
|
|
155
|
+
}
|
|
156
|
+
} catch {
|
|
157
|
+
}
|
|
158
|
+
return docs;
|
|
159
|
+
}
|
|
160
|
+
async function findExistingFeature(client, specId) {
|
|
161
|
+
try {
|
|
162
|
+
const features = await client.queryWorkItems(`[System.Title] Contains '[${specId}]' AND [System.WorkItemType] = 'Feature'`);
|
|
163
|
+
return features[0] || null;
|
|
164
|
+
} catch {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
export {
|
|
169
|
+
syncSpecToAdoWithEnhancedContent
|
|
170
|
+
};
|
|
@@ -1,19 +1,47 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: specweave-docs:build
|
|
3
|
-
description: Build static documentation site for deployment.
|
|
3
|
+
description: Build static documentation site for deployment. Validates docs first, auto-fixes issues, auto-setup on first run. Outputs production-ready HTML/CSS/JS.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Documentation Build Command
|
|
7
7
|
|
|
8
8
|
Build production-ready static documentation site for deployment to any static host.
|
|
9
9
|
|
|
10
|
+
**CRITICAL**: Runs pre-flight validation to catch issues BEFORE building.
|
|
11
|
+
|
|
10
12
|
## Your Task
|
|
11
13
|
|
|
12
14
|
**IMPORTANT**: This command must work in ANY SpecWeave user project, not just the SpecWeave repo itself.
|
|
13
15
|
|
|
14
|
-
### Step 1:
|
|
16
|
+
### Step 1: CRITICAL - Run Pre-Flight Validation
|
|
17
|
+
|
|
18
|
+
**ALWAYS validate BEFORE building to prevent cryptic webpack errors!**
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import { DocsValidator } from '../../../src/utils/docs-validator.js';
|
|
22
|
+
|
|
23
|
+
const validator = new DocsValidator({
|
|
24
|
+
docsPath: '.specweave/docs/internal',
|
|
25
|
+
autoFix: true, // Auto-fix common issues
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
console.log('\nš Running pre-build validation...\n');
|
|
29
|
+
const result = await validator.validate();
|
|
30
|
+
|
|
31
|
+
// Show summary
|
|
32
|
+
console.log(DocsValidator.formatResult(result));
|
|
33
|
+
|
|
34
|
+
// If errors remain after auto-fix, STOP and report
|
|
35
|
+
if (!result.valid) {
|
|
36
|
+
console.log('\nā Documentation has errors that must be fixed before build.');
|
|
37
|
+
console.log(' Fix the issues above, then try again.\n');
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
console.log('\nā
Validation passed! Proceeding with build...\n');
|
|
42
|
+
```
|
|
15
43
|
|
|
16
|
-
|
|
44
|
+
### Step 2: Ensure Docusaurus is Set Up
|
|
17
45
|
|
|
18
46
|
```bash
|
|
19
47
|
# Check if Docusaurus is set up
|
|
@@ -26,7 +54,7 @@ fi
|
|
|
26
54
|
|
|
27
55
|
If not set up, follow the same setup steps as `/specweave-docs:preview` (Step 3 in preview.md).
|
|
28
56
|
|
|
29
|
-
### Step
|
|
57
|
+
### Step 3: Run Build
|
|
30
58
|
|
|
31
59
|
```bash
|
|
32
60
|
cd .specweave/cache/docs-site && npm run build
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: specweave-docs:preview
|
|
3
|
-
description: Launch Docusaurus documentation server for internal living docs.
|
|
3
|
+
description: Launch Docusaurus documentation server for internal living docs. Validates docs first, auto-fixes issues, auto-setup on first run. Port 3015.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Documentation Preview Command
|
|
7
7
|
|
|
8
8
|
Launch Docusaurus development server with hot reload, Mermaid diagrams, and auto-generated sidebar.
|
|
9
9
|
|
|
10
|
+
**CRITICAL**: Runs pre-flight validation to catch issues BEFORE starting the server.
|
|
11
|
+
|
|
10
12
|
## Your Task
|
|
11
13
|
|
|
12
14
|
**IMPORTANT**: This command must work in ANY SpecWeave user project, not just the SpecWeave repo itself.
|
|
@@ -22,6 +24,46 @@ ls -la .specweave/docs/internal/
|
|
|
22
24
|
# Run 'specweave init' first or create the folder structure."
|
|
23
25
|
```
|
|
24
26
|
|
|
27
|
+
### Step 1.5: CRITICAL - Run Pre-Flight Validation
|
|
28
|
+
|
|
29
|
+
**ALWAYS run validation BEFORE starting the server!**
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { DocsValidator } from '../../../src/utils/docs-validator.js';
|
|
33
|
+
|
|
34
|
+
const validator = new DocsValidator({
|
|
35
|
+
docsPath: '.specweave/docs/internal',
|
|
36
|
+
autoFix: true, // Auto-fix common issues
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
console.log('\nš Running pre-flight validation...\n');
|
|
40
|
+
const result = await validator.validate();
|
|
41
|
+
|
|
42
|
+
// Show summary
|
|
43
|
+
console.log(DocsValidator.formatResult(result));
|
|
44
|
+
|
|
45
|
+
// If errors remain after auto-fix, STOP and report
|
|
46
|
+
if (!result.valid) {
|
|
47
|
+
console.log('\nā Documentation has errors that must be fixed before preview.');
|
|
48
|
+
console.log(' Fix the issues above, then try again.\n');
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
console.log('\nā
Validation passed! Starting server...\n');
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**What this catches:**
|
|
56
|
+
- YAML frontmatter errors (unquoted colons, tabs)
|
|
57
|
+
- MDX compatibility issues (unquoted attributes, unclosed tags)
|
|
58
|
+
- Duplicate routes
|
|
59
|
+
- Broken internal links
|
|
60
|
+
|
|
61
|
+
**Auto-fixes applied:**
|
|
62
|
+
- Wraps YAML values with colons in quotes
|
|
63
|
+
- Quotes HTML attributes
|
|
64
|
+
- Adds closing slashes to void elements (`<br>` ā `<br />`)
|
|
65
|
+
- Converts tabs to spaces in YAML
|
|
66
|
+
|
|
25
67
|
### Step 2: Check for Cached Installation
|
|
26
68
|
|
|
27
69
|
```bash
|