specweave 0.22.13 → 0.23.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-plugin/README.md +2 -2
- package/CLAUDE.md +447 -52
- package/README.md +33 -10
- package/dist/plugins/specweave-github/lib/ThreeLayerSyncManager.d.ts +1 -1
- package/dist/plugins/specweave-github/lib/ThreeLayerSyncManager.js +1 -1
- package/dist/plugins/specweave-github/lib/enhanced-github-sync.js +1 -1
- package/dist/plugins/specweave-github/lib/enhanced-github-sync.js.map +1 -1
- 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 +4 -1
- package/dist/plugins/specweave-github/lib/github-spec-content-sync.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-spec-sync.d.ts +1 -1
- package/dist/plugins/specweave-github/lib/github-spec-sync.js +1 -1
- package/dist/plugins/specweave-github/lib/github-sync-bidirectional.d.ts +9 -0
- package/dist/plugins/specweave-github/lib/github-sync-bidirectional.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-sync-bidirectional.js +10 -1
- package/dist/plugins/specweave-github/lib/github-sync-bidirectional.js.map +1 -1
- package/dist/plugins/specweave-github/lib/progress-comment-builder.js +2 -2
- package/dist/plugins/specweave-github/lib/progress-comment-builder.js.map +1 -1
- package/dist/plugins/specweave-github/lib/types.d.ts +1 -1
- package/dist/src/cli/commands/import-external.d.ts +22 -0
- package/dist/src/cli/commands/import-external.d.ts.map +1 -0
- package/dist/src/cli/commands/import-external.js +282 -0
- package/dist/src/cli/commands/import-external.js.map +1 -0
- package/dist/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +359 -1
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/helpers/github-repo-selector.d.ts +59 -0
- package/dist/src/cli/helpers/github-repo-selector.d.ts.map +1 -0
- package/dist/src/cli/helpers/github-repo-selector.js +265 -0
- package/dist/src/cli/helpers/github-repo-selector.js.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/index.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/index.js +41 -24
- package/dist/src/cli/helpers/issue-tracker/index.js.map +1 -1
- package/dist/src/config/import-config.d.ts +69 -0
- package/dist/src/config/import-config.d.ts.map +1 -0
- package/dist/src/config/import-config.js +136 -0
- package/dist/src/config/import-config.js.map +1 -0
- package/dist/src/config/types.d.ts +26 -26
- package/dist/src/core/increment/ac-status-manager.d.ts.map +1 -1
- package/dist/src/core/increment/ac-status-manager.js +4 -2
- package/dist/src/core/increment/ac-status-manager.js.map +1 -1
- package/dist/src/core/increment/completion-validator.d.ts +30 -1
- package/dist/src/core/increment/completion-validator.d.ts.map +1 -1
- package/dist/src/core/increment/completion-validator.js +151 -3
- package/dist/src/core/increment/completion-validator.js.map +1 -1
- package/dist/src/core/increment/increment-archiver.d.ts +25 -0
- package/dist/src/core/increment/increment-archiver.d.ts.map +1 -1
- package/dist/src/core/increment/increment-archiver.js +130 -3
- package/dist/src/core/increment/increment-archiver.js.map +1 -1
- package/dist/src/core/living-docs/feature-archiver.d.ts +37 -0
- package/dist/src/core/living-docs/feature-archiver.d.ts.map +1 -1
- package/dist/src/core/living-docs/feature-archiver.js +262 -18
- package/dist/src/core/living-docs/feature-archiver.js.map +1 -1
- package/dist/src/core/living-docs/feature-id-manager.d.ts +17 -0
- package/dist/src/core/living-docs/feature-id-manager.d.ts.map +1 -1
- package/dist/src/core/living-docs/feature-id-manager.js +25 -0
- package/dist/src/core/living-docs/feature-id-manager.js.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.d.ts +16 -0
- package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.js +56 -1
- package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
- package/dist/src/core/living-docs/task-project-specific-generator.d.ts +2 -2
- package/dist/src/core/living-docs/task-project-specific-generator.js +2 -2
- package/dist/src/core/repo-structure/prompt-consolidator.d.ts +2 -2
- package/dist/src/core/repo-structure/prompt-consolidator.d.ts.map +1 -1
- package/dist/src/core/repo-structure/prompt-consolidator.js +3 -15
- package/dist/src/core/repo-structure/prompt-consolidator.js.map +1 -1
- package/dist/src/core/repo-structure/repo-structure-manager.d.ts +1 -1
- package/dist/src/core/repo-structure/repo-structure-manager.d.ts.map +1 -1
- package/dist/src/core/repo-structure/repo-structure-manager.js +3 -6
- package/dist/src/core/repo-structure/repo-structure-manager.js.map +1 -1
- package/dist/src/core/spec-content-sync.d.ts +4 -1
- package/dist/src/core/spec-content-sync.d.ts.map +1 -1
- package/dist/src/core/spec-content-sync.js +139 -4
- package/dist/src/core/spec-content-sync.js.map +1 -1
- package/dist/src/core/spec-task-mapper.d.ts.map +1 -1
- package/dist/src/core/spec-task-mapper.js +9 -8
- package/dist/src/core/spec-task-mapper.js.map +1 -1
- package/dist/src/core/status-line-validator.d.ts +63 -0
- package/dist/src/core/status-line-validator.d.ts.map +1 -0
- package/dist/src/core/status-line-validator.js +253 -0
- package/dist/src/core/status-line-validator.js.map +1 -0
- package/dist/src/core/sync/bidirectional-engine.d.ts +10 -1
- package/dist/src/core/sync/bidirectional-engine.d.ts.map +1 -1
- package/dist/src/core/sync/bidirectional-engine.js +10 -1
- package/dist/src/core/sync/bidirectional-engine.js.map +1 -1
- package/dist/src/core/sync/profile-manager.d.ts.map +1 -1
- package/dist/src/core/sync/profile-manager.js +3 -0
- package/dist/src/core/sync/profile-manager.js.map +1 -1
- package/dist/src/core/sync/project-context.d.ts.map +1 -1
- package/dist/src/core/sync/project-context.js +3 -0
- package/dist/src/core/sync/project-context.js.map +1 -1
- package/dist/src/core/sync/status-sync-engine.d.ts +1 -1
- package/dist/src/core/sync/status-sync-engine.js +1 -1
- package/dist/src/core/sync/sync-event-logger.d.ts +15 -1
- package/dist/src/core/sync/sync-event-logger.d.ts.map +1 -1
- package/dist/src/core/sync/sync-event-logger.js +39 -1
- package/dist/src/core/sync/sync-event-logger.js.map +1 -1
- package/dist/src/core/types/origin-metadata.d.ts +153 -0
- package/dist/src/core/types/origin-metadata.d.ts.map +1 -0
- package/dist/src/core/types/origin-metadata.js +166 -0
- package/dist/src/core/types/origin-metadata.js.map +1 -0
- package/dist/src/core/types/sync-config-validator.d.ts +57 -0
- package/dist/src/core/types/sync-config-validator.d.ts.map +1 -0
- package/dist/src/core/types/sync-config-validator.js +116 -0
- package/dist/src/core/types/sync-config-validator.js.map +1 -0
- package/dist/src/core/types/sync-profile.d.ts +8 -2
- package/dist/src/core/types/sync-profile.d.ts.map +1 -1
- package/dist/src/core/types/sync-profile.js.map +1 -1
- package/dist/src/core/types/sync-settings.d.ts +73 -0
- package/dist/src/core/types/sync-settings.d.ts.map +1 -0
- package/dist/src/core/types/sync-settings.js +90 -0
- package/dist/src/core/types/sync-settings.js.map +1 -0
- package/dist/src/core/utils/permission-checker.d.ts +100 -0
- package/dist/src/core/utils/permission-checker.d.ts.map +1 -0
- package/dist/src/core/utils/permission-checker.js +166 -0
- package/dist/src/core/utils/permission-checker.js.map +1 -0
- package/dist/src/generators/spec/spec-parser.js +3 -3
- package/dist/src/generators/spec/spec-parser.js.map +1 -1
- package/dist/src/generators/spec/task-parser.js +4 -4
- package/dist/src/generators/spec/task-parser.js.map +1 -1
- package/dist/src/id-generators/task-id-generator.d.ts +96 -0
- package/dist/src/id-generators/task-id-generator.d.ts.map +1 -0
- package/dist/src/id-generators/task-id-generator.js +143 -0
- package/dist/src/id-generators/task-id-generator.js.map +1 -0
- package/dist/src/id-generators/us-id-generator.d.ts +96 -0
- package/dist/src/id-generators/us-id-generator.d.ts.map +1 -0
- package/dist/src/id-generators/us-id-generator.js +143 -0
- package/dist/src/id-generators/us-id-generator.js.map +1 -0
- package/dist/src/importers/ado-importer.d.ts +43 -0
- package/dist/src/importers/ado-importer.d.ts.map +1 -0
- package/dist/src/importers/ado-importer.js +234 -0
- package/dist/src/importers/ado-importer.js.map +1 -0
- package/dist/src/importers/duplicate-detector.d.ts +107 -0
- package/dist/src/importers/duplicate-detector.d.ts.map +1 -0
- package/dist/src/importers/duplicate-detector.js +189 -0
- package/dist/src/importers/duplicate-detector.js.map +1 -0
- package/dist/src/importers/external-importer.d.ts +96 -0
- package/dist/src/importers/external-importer.d.ts.map +1 -0
- package/dist/src/importers/external-importer.js +13 -0
- package/dist/src/importers/external-importer.js.map +1 -0
- package/dist/src/importers/github-importer.d.ts +37 -0
- package/dist/src/importers/github-importer.d.ts.map +1 -0
- package/dist/src/importers/github-importer.js +161 -0
- package/dist/src/importers/github-importer.js.map +1 -0
- package/dist/src/importers/import-coordinator.d.ts +105 -0
- package/dist/src/importers/import-coordinator.d.ts.map +1 -0
- package/dist/src/importers/import-coordinator.js +224 -0
- package/dist/src/importers/import-coordinator.js.map +1 -0
- package/dist/src/importers/item-converter.d.ts +96 -0
- package/dist/src/importers/item-converter.d.ts.map +1 -0
- package/dist/src/importers/item-converter.js +246 -0
- package/dist/src/importers/item-converter.js.map +1 -0
- package/dist/src/importers/jira-importer.d.ts +42 -0
- package/dist/src/importers/jira-importer.d.ts.map +1 -0
- package/dist/src/importers/jira-importer.js +221 -0
- package/dist/src/importers/jira-importer.js.map +1 -0
- package/dist/src/importers/rate-limiter.d.ts +128 -0
- package/dist/src/importers/rate-limiter.d.ts.map +1 -0
- package/dist/src/importers/rate-limiter.js +200 -0
- package/dist/src/importers/rate-limiter.js.map +1 -0
- package/dist/src/init/compliance/types.d.ts +2 -2
- package/dist/src/init/repo/types.d.ts +2 -2
- package/dist/src/integrations/ado/ado-client.d.ts +6 -0
- package/dist/src/integrations/ado/ado-client.d.ts.map +1 -1
- package/dist/src/integrations/ado/ado-client.js +23 -0
- package/dist/src/integrations/ado/ado-client.js.map +1 -1
- package/dist/src/integrations/jira/jira-client.d.ts +6 -0
- package/dist/src/integrations/jira/jira-client.d.ts.map +1 -1
- package/dist/src/integrations/jira/jira-client.js +38 -0
- package/dist/src/integrations/jira/jira-client.js.map +1 -1
- package/dist/src/integrations/jira/jira-mapper.d.ts +1 -1
- package/dist/src/integrations/jira/jira-mapper.js +1 -1
- package/dist/src/living-docs/fs-id-allocator.d.ts +149 -0
- package/dist/src/living-docs/fs-id-allocator.d.ts.map +1 -0
- package/dist/src/living-docs/fs-id-allocator.js +325 -0
- package/dist/src/living-docs/fs-id-allocator.js.map +1 -0
- package/dist/src/living-docs/id-registry.d.ts +124 -0
- package/dist/src/living-docs/id-registry.d.ts.map +1 -0
- package/dist/src/living-docs/id-registry.js +230 -0
- package/dist/src/living-docs/id-registry.js.map +1 -0
- package/dist/src/progress/us-progress-tracker.d.ts +68 -0
- package/dist/src/progress/us-progress-tracker.d.ts.map +1 -0
- package/dist/src/progress/us-progress-tracker.js +120 -0
- package/dist/src/progress/us-progress-tracker.js.map +1 -0
- package/dist/src/sync/external-item-sync-service.d.ts +150 -0
- package/dist/src/sync/external-item-sync-service.d.ts.map +1 -0
- package/dist/src/sync/external-item-sync-service.js +241 -0
- package/dist/src/sync/external-item-sync-service.js.map +1 -0
- package/dist/src/sync/format-preservation-sync.d.ts +90 -0
- package/dist/src/sync/format-preservation-sync.d.ts.map +1 -0
- package/dist/src/sync/format-preservation-sync.js +173 -0
- package/dist/src/sync/format-preservation-sync.js.map +1 -0
- package/dist/src/sync/index.d.ts +8 -0
- package/dist/src/sync/index.d.ts.map +1 -0
- package/dist/src/sync/index.js +6 -0
- package/dist/src/sync/index.js.map +1 -0
- package/dist/src/sync/sync-coordinator.d.ts +49 -0
- package/dist/src/sync/sync-coordinator.d.ts.map +1 -0
- package/dist/src/sync/sync-coordinator.js +248 -0
- package/dist/src/sync/sync-coordinator.js.map +1 -0
- package/dist/src/sync/sync-metadata.d.ts +75 -0
- package/dist/src/sync/sync-metadata.d.ts.map +1 -0
- package/dist/src/sync/sync-metadata.js +100 -0
- package/dist/src/sync/sync-metadata.js.map +1 -0
- package/dist/src/types/living-docs-us-file.d.ts +63 -0
- package/dist/src/types/living-docs-us-file.d.ts.map +1 -0
- package/dist/src/types/living-docs-us-file.js +27 -0
- package/dist/src/types/living-docs-us-file.js.map +1 -0
- package/dist/src/validators/format-preservation-validator.d.ts +127 -0
- package/dist/src/validators/format-preservation-validator.d.ts.map +1 -0
- package/dist/src/validators/format-preservation-validator.js +187 -0
- package/dist/src/validators/format-preservation-validator.js.map +1 -0
- package/package.json +3 -2
- package/plugins/specweave/.claude-plugin/plugin.json +36 -2
- package/plugins/specweave/agents/architect/AGENT.md +11 -2
- package/plugins/specweave/agents/test-aware-planner/AGENT.md +81 -25
- package/plugins/specweave/commands/specweave-archive-features.md +11 -1
- package/plugins/specweave/commands/specweave-import-external.md +407 -0
- package/plugins/specweave/commands/specweave-progress.md +45 -97
- package/plugins/specweave/hooks/post-edit-spec.sh +41 -0
- package/plugins/specweave/hooks/post-increment-completion.sh +168 -26
- package/plugins/specweave/hooks/post-increment-planning.sh +148 -4
- package/plugins/specweave/hooks/post-spec-update.sh +0 -0
- package/plugins/specweave/hooks/post-task-completion.sh +75 -5
- package/plugins/specweave/hooks/post-write-spec.sh +37 -0
- package/plugins/specweave/lib/hooks/auto-transition.js +1 -1
- package/plugins/specweave/lib/hooks/auto-transition.ts +1 -1
- package/plugins/specweave/lib/hooks/invoke-translator-skill.js +1 -1
- package/plugins/specweave/lib/hooks/invoke-translator-skill.ts +1 -1
- package/plugins/specweave/lib/hooks/sync-cache.js +294 -0
- package/plugins/specweave/lib/hooks/sync-living-docs.js +67 -2
- package/plugins/specweave/lib/hooks/sync-us-tasks.js +200 -14
- package/plugins/specweave/lib/hooks/translate-file.js +1 -1
- package/plugins/specweave/lib/hooks/translate-file.ts +1 -1
- package/plugins/specweave/lib/hooks/update-ac-status.js +1 -1
- package/plugins/specweave/lib/hooks/update-ac-status.ts +1 -1
- package/plugins/specweave/lib/vendor/core/increment/ac-status-manager.d.ts +115 -0
- package/plugins/specweave/lib/vendor/core/increment/ac-status-manager.js +345 -0
- package/plugins/specweave/lib/vendor/core/increment/ac-status-manager.js.map +1 -0
- package/plugins/specweave/lib/vendor/core/increment/active-increment-manager.d.ts +106 -0
- package/plugins/specweave/lib/vendor/core/increment/active-increment-manager.js +220 -0
- package/plugins/specweave/lib/vendor/core/increment/active-increment-manager.js.map +1 -0
- package/plugins/specweave/lib/vendor/core/increment/auto-transition-manager.d.ts +60 -0
- package/plugins/specweave/lib/vendor/core/increment/auto-transition-manager.js +192 -0
- package/plugins/specweave/lib/vendor/core/increment/auto-transition-manager.js.map +1 -0
- package/plugins/specweave/lib/vendor/core/increment/duplicate-detector.d.ts +52 -0
- package/plugins/specweave/lib/vendor/core/increment/duplicate-detector.js +276 -0
- package/plugins/specweave/lib/vendor/core/increment/duplicate-detector.js.map +1 -0
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.d.ts +163 -0
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js +541 -0
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js.map +1 -0
- package/plugins/specweave/lib/vendor/core/types/increment-metadata.d.ts +157 -0
- package/plugins/specweave/lib/vendor/core/types/increment-metadata.js +191 -0
- package/plugins/specweave/lib/vendor/core/types/increment-metadata.js.map +1 -0
- package/plugins/specweave/lib/vendor/generators/spec/task-parser.d.ts +95 -0
- package/plugins/specweave/lib/vendor/generators/spec/task-parser.js +301 -0
- package/plugins/specweave/lib/vendor/generators/spec/task-parser.js.map +1 -0
- package/plugins/specweave/lib/vendor/utils/logger.d.ts +48 -0
- package/plugins/specweave/lib/vendor/utils/logger.js +53 -0
- package/plugins/specweave/lib/vendor/utils/logger.js.map +1 -0
- package/plugins/specweave/lib/vendor/utils/translation.d.ts +187 -0
- package/plugins/specweave/lib/vendor/utils/translation.js +414 -0
- package/plugins/specweave/lib/vendor/utils/translation.js.map +1 -0
- package/plugins/specweave-ado/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-ado/lib/ado-multi-project-sync.js +0 -1
- package/plugins/specweave-ado/lib/conflict-resolver.ts +1 -1
- package/plugins/specweave-alternatives/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-backend/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-confluent/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-cost-optimizer/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-diagrams/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-docs/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-docs-preview/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-figma/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-frontend/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-github/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-github/commands/specweave-github-update-user-story.md +1 -1
- package/plugins/specweave-github/hooks/post-task-completion.sh +37 -22
- package/plugins/specweave-github/lib/ThreeLayerSyncManager.ts +1 -1
- package/plugins/specweave-github/lib/enhanced-github-sync.js +1 -1
- package/plugins/specweave-github/lib/enhanced-github-sync.ts +1 -1
- package/plugins/specweave-github/lib/github-spec-content-sync.js +2 -1
- package/plugins/specweave-github/lib/github-spec-content-sync.ts +4 -1
- package/plugins/specweave-github/lib/github-spec-sync.js +1 -1
- package/plugins/specweave-github/lib/github-spec-sync.ts +1 -1
- package/plugins/specweave-github/lib/github-sync-bidirectional.js +1 -1
- package/plugins/specweave-github/lib/github-sync-bidirectional.ts +10 -1
- package/plugins/specweave-github/lib/progress-comment-builder.js +1 -1
- package/plugins/specweave-github/lib/progress-comment-builder.ts +2 -2
- package/plugins/specweave-github/lib/types.ts +1 -1
- package/plugins/specweave-github/skills/github-issue-standard/SKILL.md +1 -1
- package/plugins/specweave-infrastructure/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-jira/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-jira/lib/enhanced-jira-sync.js +3 -3
- package/plugins/specweave-kafka/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-kafka-streams/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-kubernetes/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-ml/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-mobile/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-n8n/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-payments/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-release/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-testing/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-tooling/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-ui/.claude-plugin/plugin.json +1 -1
- package/src/templates/.env.example +5 -0
- package/src/templates/config-permissions-guide.md +413 -0
- package/src/templates/config.json.template +68 -0
- package/src/templates/tasks.md.template +180 -201
- package/plugins/specweave-ado/lib/enhanced-ado-sync.js +0 -170
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +0 -5442
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Duplicate Detector - Scan filesystem and detect duplicate increments
|
|
3
|
+
*
|
|
4
|
+
* Detects increments that exist in multiple locations (active, archive, abandoned)
|
|
5
|
+
* or have the same increment number with different names.
|
|
6
|
+
*
|
|
7
|
+
* Part of increment 0033: Duplicate Increment Prevention System
|
|
8
|
+
*/
|
|
9
|
+
import fs from 'fs-extra';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
import { glob } from 'glob';
|
|
12
|
+
/**
|
|
13
|
+
* Scan all increment folders and detect duplicates
|
|
14
|
+
*/
|
|
15
|
+
export async function detectAllDuplicates(rootDir) {
|
|
16
|
+
const incrementsDir = path.join(rootDir, '.specweave', 'increments');
|
|
17
|
+
// Check if increments directory exists
|
|
18
|
+
if (!await fs.pathExists(incrementsDir)) {
|
|
19
|
+
return {
|
|
20
|
+
duplicates: [],
|
|
21
|
+
totalChecked: 0,
|
|
22
|
+
duplicateCount: 0
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
// Scan all locations in parallel
|
|
26
|
+
const [active, archived, abandoned] = await Promise.all([
|
|
27
|
+
scanDirectory(incrementsDir, false),
|
|
28
|
+
scanDirectory(path.join(incrementsDir, '_archive'), false),
|
|
29
|
+
scanDirectory(path.join(incrementsDir, '_abandoned'), false)
|
|
30
|
+
]);
|
|
31
|
+
// Group by increment number
|
|
32
|
+
const byNumber = new Map();
|
|
33
|
+
const allIncrements = [...active, ...archived, ...abandoned];
|
|
34
|
+
for (const inc of allIncrements) {
|
|
35
|
+
const number = extractIncrementNumber(inc.name);
|
|
36
|
+
if (!number)
|
|
37
|
+
continue; // Skip non-increment folders
|
|
38
|
+
if (!byNumber.has(number)) {
|
|
39
|
+
byNumber.set(number, []);
|
|
40
|
+
}
|
|
41
|
+
byNumber.get(number).push(inc);
|
|
42
|
+
}
|
|
43
|
+
// Find duplicates (increment number exists in >1 location or >1 name)
|
|
44
|
+
const duplicates = [];
|
|
45
|
+
byNumber.forEach((locations, number) => {
|
|
46
|
+
if (locations.length > 1) {
|
|
47
|
+
// Duplicate found!
|
|
48
|
+
const winner = selectWinner(locations);
|
|
49
|
+
duplicates.push({
|
|
50
|
+
incrementNumber: number,
|
|
51
|
+
locations,
|
|
52
|
+
recommendedWinner: winner,
|
|
53
|
+
losingVersions: locations.filter(l => l !== winner),
|
|
54
|
+
resolutionReason: explainWinner(winner, locations)
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
return {
|
|
59
|
+
duplicates,
|
|
60
|
+
totalChecked: allIncrements.length,
|
|
61
|
+
duplicateCount: duplicates.length
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Detect duplicates for a specific increment number
|
|
66
|
+
*
|
|
67
|
+
* Returns ALL increments that have the given number, even if there's only one.
|
|
68
|
+
* This is used for validation before creating a new increment.
|
|
69
|
+
*/
|
|
70
|
+
export async function detectDuplicatesByNumber(incrementNumber, rootDir) {
|
|
71
|
+
const incrementsDir = path.join(rootDir, '.specweave', 'increments');
|
|
72
|
+
// Check if increments directory exists
|
|
73
|
+
if (!await fs.pathExists(incrementsDir)) {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
// Normalize increment number (pad to 4 digits)
|
|
77
|
+
const normalizedNumber = incrementNumber.padStart(4, '0');
|
|
78
|
+
// Scan all locations in parallel
|
|
79
|
+
const [active, archived, abandoned] = await Promise.all([
|
|
80
|
+
scanDirectory(incrementsDir, false),
|
|
81
|
+
scanDirectory(path.join(incrementsDir, '_archive'), false),
|
|
82
|
+
scanDirectory(path.join(incrementsDir, '_abandoned'), false)
|
|
83
|
+
]);
|
|
84
|
+
// Combine all increments and filter by number
|
|
85
|
+
const allIncrements = [...active, ...archived, ...abandoned];
|
|
86
|
+
const matchingIncrements = allIncrements.filter(inc => {
|
|
87
|
+
const number = extractIncrementNumber(inc.name);
|
|
88
|
+
return number === normalizedNumber;
|
|
89
|
+
});
|
|
90
|
+
return matchingIncrements;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Scan a directory for increment folders
|
|
94
|
+
*/
|
|
95
|
+
async function scanDirectory(dir, throwOnError = false) {
|
|
96
|
+
try {
|
|
97
|
+
// Check if directory exists
|
|
98
|
+
if (!await fs.pathExists(dir)) {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
// Find all directories matching increment pattern (####-*)
|
|
102
|
+
const pattern = path.join(dir, '[0-9][0-9][0-9][0-9]-*');
|
|
103
|
+
const allPaths = await glob(pattern);
|
|
104
|
+
const locations = [];
|
|
105
|
+
for (const incPath of allPaths) {
|
|
106
|
+
try {
|
|
107
|
+
const stats = await fs.stat(incPath);
|
|
108
|
+
if (!stats.isDirectory())
|
|
109
|
+
continue;
|
|
110
|
+
const name = path.basename(incPath);
|
|
111
|
+
// Skip nested .specweave folders
|
|
112
|
+
if (incPath.includes('.specweave/increments/.specweave')) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
// Read metadata
|
|
116
|
+
const metadataPath = path.join(incPath, 'metadata.json');
|
|
117
|
+
let metadata = {
|
|
118
|
+
status: 'unknown',
|
|
119
|
+
lastActivity: new Date(stats.mtime).toISOString()
|
|
120
|
+
};
|
|
121
|
+
if (await fs.pathExists(metadataPath)) {
|
|
122
|
+
try {
|
|
123
|
+
metadata = await fs.readJson(metadataPath);
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
// Skip corrupted metadata (will be caught by validation)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Count files recursively
|
|
130
|
+
const allFiles = await glob(path.join(incPath, '**/*'));
|
|
131
|
+
const fileCount = allFiles.filter(async (f) => {
|
|
132
|
+
try {
|
|
133
|
+
const fstats = await fs.stat(f);
|
|
134
|
+
return fstats.isFile();
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}).length;
|
|
140
|
+
// Calculate total size
|
|
141
|
+
let totalSize = 0;
|
|
142
|
+
for (const file of allFiles) {
|
|
143
|
+
try {
|
|
144
|
+
const fstats = await fs.stat(file);
|
|
145
|
+
if (fstats.isFile()) {
|
|
146
|
+
totalSize += fstats.size;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// Skip files we can't read
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Check for reports folder
|
|
154
|
+
const reportsDir = path.join(incPath, 'reports');
|
|
155
|
+
const hasReports = await fs.pathExists(reportsDir);
|
|
156
|
+
// Check for GitHub link in metadata
|
|
157
|
+
const hasGitHubLink = metadata.github && metadata.github.issue;
|
|
158
|
+
locations.push({
|
|
159
|
+
path: incPath,
|
|
160
|
+
name,
|
|
161
|
+
status: metadata.status || 'unknown',
|
|
162
|
+
lastActivity: metadata.lastActivity || new Date(stats.mtime).toISOString(),
|
|
163
|
+
fileCount,
|
|
164
|
+
totalSize,
|
|
165
|
+
hasReports,
|
|
166
|
+
hasGitHubLink
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
// Skip increments we can't read
|
|
171
|
+
if (throwOnError)
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return locations;
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
if (throwOnError)
|
|
179
|
+
throw error;
|
|
180
|
+
return [];
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Extract increment number from increment name (e.g., "0001-feature" → "0001")
|
|
185
|
+
*/
|
|
186
|
+
function extractIncrementNumber(name) {
|
|
187
|
+
const match = name.match(/^(\d{4})-/);
|
|
188
|
+
return match ? match[1] : null;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Select winning version based on priority rules
|
|
192
|
+
*/
|
|
193
|
+
function selectWinner(locations) {
|
|
194
|
+
// Priority 1: Active status > Completed > Paused > Backlog > Abandoned
|
|
195
|
+
const statusPriority = {
|
|
196
|
+
active: 5,
|
|
197
|
+
completed: 4,
|
|
198
|
+
paused: 3,
|
|
199
|
+
backlog: 2,
|
|
200
|
+
abandoned: 1,
|
|
201
|
+
unknown: 0
|
|
202
|
+
};
|
|
203
|
+
// Sort by priority
|
|
204
|
+
const sorted = [...locations].sort((a, b) => {
|
|
205
|
+
// 1. Status priority
|
|
206
|
+
const aPriority = statusPriority[a.status] || 0;
|
|
207
|
+
const bPriority = statusPriority[b.status] || 0;
|
|
208
|
+
if (bPriority !== aPriority)
|
|
209
|
+
return bPriority - aPriority;
|
|
210
|
+
// 2. Most recent activity
|
|
211
|
+
const aTime = new Date(a.lastActivity).getTime();
|
|
212
|
+
const bTime = new Date(b.lastActivity).getTime();
|
|
213
|
+
if (bTime !== aTime)
|
|
214
|
+
return bTime - aTime;
|
|
215
|
+
// 3. Most complete (more files)
|
|
216
|
+
if (b.fileCount !== a.fileCount)
|
|
217
|
+
return b.fileCount - a.fileCount;
|
|
218
|
+
// 4. Location preference (active > _archive > _abandoned)
|
|
219
|
+
const locationScore = (loc) => {
|
|
220
|
+
if (loc.path.includes('_abandoned'))
|
|
221
|
+
return 1;
|
|
222
|
+
if (loc.path.includes('_archive'))
|
|
223
|
+
return 2;
|
|
224
|
+
return 3; // active
|
|
225
|
+
};
|
|
226
|
+
return locationScore(b) - locationScore(a);
|
|
227
|
+
});
|
|
228
|
+
return sorted[0];
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Explain why this version won
|
|
232
|
+
*/
|
|
233
|
+
function explainWinner(winner, all) {
|
|
234
|
+
const reasons = [];
|
|
235
|
+
const statusPriority = {
|
|
236
|
+
active: 5,
|
|
237
|
+
completed: 4,
|
|
238
|
+
paused: 3,
|
|
239
|
+
backlog: 2,
|
|
240
|
+
abandoned: 1,
|
|
241
|
+
unknown: 0
|
|
242
|
+
};
|
|
243
|
+
// Check status
|
|
244
|
+
const winnerStatusPriority = statusPriority[winner.status] || 0;
|
|
245
|
+
const hasLowerStatus = all.some(loc => loc !== winner && (statusPriority[loc.status] || 0) < winnerStatusPriority);
|
|
246
|
+
if (hasLowerStatus) {
|
|
247
|
+
reasons.push(`Higher status (${winner.status})`);
|
|
248
|
+
}
|
|
249
|
+
// Check recency
|
|
250
|
+
const winnerTime = new Date(winner.lastActivity).getTime();
|
|
251
|
+
const hasOlderActivity = all.some(loc => loc !== winner && new Date(loc.lastActivity).getTime() < winnerTime);
|
|
252
|
+
if (hasOlderActivity) {
|
|
253
|
+
const date = new Date(winner.lastActivity).toISOString().split('T')[0];
|
|
254
|
+
reasons.push(`Most recent activity (${date})`);
|
|
255
|
+
}
|
|
256
|
+
// Check completeness
|
|
257
|
+
const hasFewerFiles = all.some(loc => loc !== winner && loc.fileCount < winner.fileCount);
|
|
258
|
+
if (hasFewerFiles) {
|
|
259
|
+
reasons.push(`Most complete (${winner.fileCount} files)`);
|
|
260
|
+
}
|
|
261
|
+
// Check location
|
|
262
|
+
const winnerLocation = winner.path.includes('_abandoned') ? 'abandoned' :
|
|
263
|
+
winner.path.includes('_archive') ? 'archive' : 'active';
|
|
264
|
+
const hasWorseLocation = all.some(loc => {
|
|
265
|
+
const locLocation = loc.path.includes('_abandoned') ? 'abandoned' :
|
|
266
|
+
loc.path.includes('_archive') ? 'archive' : 'active';
|
|
267
|
+
const locationPriority = { active: 3, archive: 2, abandoned: 1 };
|
|
268
|
+
return locationPriority[locLocation] <
|
|
269
|
+
locationPriority[winnerLocation];
|
|
270
|
+
});
|
|
271
|
+
if (hasWorseLocation) {
|
|
272
|
+
reasons.push(`In ${winnerLocation} location`);
|
|
273
|
+
}
|
|
274
|
+
return reasons.length > 0 ? reasons.join(', ') : 'Default selection';
|
|
275
|
+
}
|
|
276
|
+
//# sourceMappingURL=duplicate-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duplicate-detector.js","sourceRoot":"","sources":["../../../../src/core/increment/duplicate-detector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metadata Manager
|
|
3
|
+
*
|
|
4
|
+
* Handles CRUD operations for increment metadata (status, type, timestamps).
|
|
5
|
+
* Part of increment 0007: Smart Status Management
|
|
6
|
+
*/
|
|
7
|
+
import { IncrementMetadata, IncrementMetadataExtended, IncrementStatus, IncrementType } from '../types/increment-metadata.js';
|
|
8
|
+
import { Logger } from '../../utils/logger.js';
|
|
9
|
+
/**
|
|
10
|
+
* Error thrown when metadata operations fail
|
|
11
|
+
*/
|
|
12
|
+
export declare class MetadataError extends Error {
|
|
13
|
+
incrementId: string;
|
|
14
|
+
cause?: Error;
|
|
15
|
+
constructor(message: string, incrementId: string, cause?: Error);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Metadata Manager
|
|
19
|
+
*
|
|
20
|
+
* Provides CRUD operations and queries for increment metadata
|
|
21
|
+
*/
|
|
22
|
+
export declare class MetadataManager {
|
|
23
|
+
/**
|
|
24
|
+
* Logger instance (injectable for testing)
|
|
25
|
+
*/
|
|
26
|
+
private static logger;
|
|
27
|
+
/**
|
|
28
|
+
* Set logger instance (primarily for testing with silentLogger)
|
|
29
|
+
*
|
|
30
|
+
* @param logger - Logger instance to use
|
|
31
|
+
*/
|
|
32
|
+
static setLogger(logger: Logger): void;
|
|
33
|
+
/**
|
|
34
|
+
* Get metadata file path for increment
|
|
35
|
+
*/
|
|
36
|
+
private static getMetadataPath;
|
|
37
|
+
/**
|
|
38
|
+
* Get increment directory path
|
|
39
|
+
*/
|
|
40
|
+
private static getIncrementPath;
|
|
41
|
+
/**
|
|
42
|
+
* Check if metadata file exists
|
|
43
|
+
*/
|
|
44
|
+
static exists(incrementId: string): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Read metadata from file
|
|
47
|
+
* Creates default metadata if file doesn't exist (lazy initialization)
|
|
48
|
+
*/
|
|
49
|
+
static read(incrementId: string): IncrementMetadata;
|
|
50
|
+
/**
|
|
51
|
+
* Reserved increment IDs that cannot be used
|
|
52
|
+
* These are status values, special folders, and state files
|
|
53
|
+
*/
|
|
54
|
+
private static readonly RESERVED_INCREMENT_IDS;
|
|
55
|
+
/**
|
|
56
|
+
* Validate increment ID is not a reserved name
|
|
57
|
+
* Throws if ID is reserved
|
|
58
|
+
*/
|
|
59
|
+
private static validateNotReserved;
|
|
60
|
+
/**
|
|
61
|
+
* Validate increment before creation (check for duplicates and reserved names)
|
|
62
|
+
* Throws if duplicates exist in other locations or ID is reserved
|
|
63
|
+
*/
|
|
64
|
+
static validateBeforeCreate(incrementId: string, rootDir?: string): Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* Write metadata to file
|
|
67
|
+
* Uses atomic write (temp file → rename)
|
|
68
|
+
*/
|
|
69
|
+
static write(incrementId: string, metadata: IncrementMetadata): void;
|
|
70
|
+
/**
|
|
71
|
+
* Delete metadata file
|
|
72
|
+
*/
|
|
73
|
+
static delete(incrementId: string): void;
|
|
74
|
+
/**
|
|
75
|
+
* Update increment status
|
|
76
|
+
* Validates transition and updates timestamps
|
|
77
|
+
*
|
|
78
|
+
* **CRITICAL**: Also updates active increment state automatically!
|
|
79
|
+
*
|
|
80
|
+
* NOTE: This method is now SYNCHRONOUS to ensure spec.md is updated
|
|
81
|
+
* before returning. This prevents race conditions in tests and ensures
|
|
82
|
+
* data consistency.
|
|
83
|
+
*/
|
|
84
|
+
static updateStatus(incrementId: string, newStatus: IncrementStatus, reason?: string): IncrementMetadata;
|
|
85
|
+
/**
|
|
86
|
+
* Update spec.md status synchronously (used by updateStatus)
|
|
87
|
+
*
|
|
88
|
+
* This is a private helper to avoid async/await in updateStatus() which would
|
|
89
|
+
* break backward compatibility with callers expecting sync behavior.
|
|
90
|
+
*/
|
|
91
|
+
private static updateSpecMdStatusSync;
|
|
92
|
+
/**
|
|
93
|
+
* Update increment type
|
|
94
|
+
*/
|
|
95
|
+
static updateType(incrementId: string, type: IncrementType): IncrementMetadata;
|
|
96
|
+
/**
|
|
97
|
+
* Touch increment (update lastActivity)
|
|
98
|
+
*/
|
|
99
|
+
static touch(incrementId: string): IncrementMetadata;
|
|
100
|
+
/**
|
|
101
|
+
* Get all increments
|
|
102
|
+
*/
|
|
103
|
+
static getAll(): IncrementMetadata[];
|
|
104
|
+
/**
|
|
105
|
+
* Get increments by status
|
|
106
|
+
*/
|
|
107
|
+
static getByStatus(status: IncrementStatus): IncrementMetadata[];
|
|
108
|
+
/**
|
|
109
|
+
* Get active increments (FAST: cache-first strategy)
|
|
110
|
+
*
|
|
111
|
+
* **PERFORMANCE UPGRADE**: Uses ActiveIncrementManager cache instead of scanning all increments
|
|
112
|
+
* - Old: Scan 31 metadata files (~50ms)
|
|
113
|
+
* - New: Read 1 cache file + 1-2 metadata files (~5ms) = **10x faster**
|
|
114
|
+
*
|
|
115
|
+
* Fallback to full scan if cache is stale or missing
|
|
116
|
+
*/
|
|
117
|
+
static getActive(): IncrementMetadata[];
|
|
118
|
+
/**
|
|
119
|
+
* Get backlog increments
|
|
120
|
+
*/
|
|
121
|
+
static getBacklog(): IncrementMetadata[];
|
|
122
|
+
/**
|
|
123
|
+
* Get paused increments
|
|
124
|
+
*/
|
|
125
|
+
static getPaused(): IncrementMetadata[];
|
|
126
|
+
/**
|
|
127
|
+
* Get completed increments
|
|
128
|
+
*/
|
|
129
|
+
static getCompleted(): IncrementMetadata[];
|
|
130
|
+
/**
|
|
131
|
+
* Get abandoned increments
|
|
132
|
+
*/
|
|
133
|
+
static getAbandoned(): IncrementMetadata[];
|
|
134
|
+
/**
|
|
135
|
+
* Get increments by type
|
|
136
|
+
*/
|
|
137
|
+
static getByType(type: IncrementType): IncrementMetadata[];
|
|
138
|
+
/**
|
|
139
|
+
* Get stale increments (paused >7 days or active >30 days)
|
|
140
|
+
*/
|
|
141
|
+
static getStale(): IncrementMetadata[];
|
|
142
|
+
/**
|
|
143
|
+
* Get increments that should be auto-abandoned (experiments inactive >14 days)
|
|
144
|
+
*/
|
|
145
|
+
static getShouldAutoAbandon(): IncrementMetadata[];
|
|
146
|
+
/**
|
|
147
|
+
* Get extended metadata with computed fields (progress, age, etc.)
|
|
148
|
+
*/
|
|
149
|
+
static getExtended(incrementId: string): IncrementMetadataExtended;
|
|
150
|
+
/**
|
|
151
|
+
* Validate metadata schema
|
|
152
|
+
*/
|
|
153
|
+
static validate(metadata: IncrementMetadata): boolean;
|
|
154
|
+
/**
|
|
155
|
+
* Check if status transition is allowed
|
|
156
|
+
*/
|
|
157
|
+
static canTransition(from: IncrementStatus, to: IncrementStatus): boolean;
|
|
158
|
+
/**
|
|
159
|
+
* Get human-readable status transition error message
|
|
160
|
+
*/
|
|
161
|
+
static getTransitionError(from: IncrementStatus, to: IncrementStatus): string;
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=metadata-manager.d.ts.map
|