specweave 1.0.350 → 1.0.352
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/bin/specweave.js +9 -0
- package/dist/plugins/specweave-ado/lib/ado-client-v2.d.ts +5 -0
- package/dist/plugins/specweave-ado/lib/ado-client-v2.d.ts.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-client-v2.js +61 -23
- package/dist/plugins/specweave-ado/lib/ado-client-v2.js.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-duplicate-detector.d.ts.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-duplicate-detector.js +3 -2
- package/dist/plugins/specweave-ado/lib/ado-duplicate-detector.js.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-profile-resolver.d.ts.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-profile-resolver.js +2 -1
- package/dist/plugins/specweave-ado/lib/ado-profile-resolver.js.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-sync.d.ts +1 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-sync.js +25 -9
- package/dist/plugins/specweave-ado/lib/ado-spec-sync.js.map +1 -1
- package/dist/plugins/specweave-ado/lib/conflict-resolver.d.ts.map +1 -1
- package/dist/plugins/specweave-ado/lib/conflict-resolver.js +17 -1
- package/dist/plugins/specweave-ado/lib/conflict-resolver.js.map +1 -1
- package/dist/plugins/specweave-ado/lib/per-us-sync.d.ts +3 -0
- package/dist/plugins/specweave-ado/lib/per-us-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-ado/lib/per-us-sync.js +14 -1
- package/dist/plugins/specweave-ado/lib/per-us-sync.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-client-v2.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-client-v2.js +10 -7
- package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-client.d.ts +1 -1
- package/dist/plugins/specweave-github/lib/github-client.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-client.js +7 -5
- package/dist/plugins/specweave-github/lib/github-client.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-cross-repo-sync.js +13 -3
- package/dist/plugins/specweave-github/lib/github-cross-repo-sync.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-feature-sync-cli.d.ts +24 -1
- package/dist/plugins/specweave-github/lib/github-feature-sync-cli.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-feature-sync-cli.js +36 -20
- package/dist/plugins/specweave-github/lib/github-feature-sync-cli.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts +4 -2
- package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-feature-sync.js +38 -9
- package/dist/plugins/specweave-github/lib/github-feature-sync.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-graphql-client.d.ts +1 -0
- package/dist/plugins/specweave-github/lib/github-graphql-client.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-graphql-client.js +32 -22
- package/dist/plugins/specweave-github/lib/github-graphql-client.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-spec-frontmatter-updater.js +144 -8
- package/dist/plugins/specweave-github/lib/github-spec-frontmatter-updater.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-spec-sync.d.ts +8 -1
- package/dist/plugins/specweave-github/lib/github-spec-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-spec-sync.js +94 -24
- package/dist/plugins/specweave-github/lib/github-spec-sync.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-sync-orchestrator.d.ts +1 -0
- package/dist/plugins/specweave-github/lib/github-sync-orchestrator.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-sync-orchestrator.js +2 -1
- package/dist/plugins/specweave-github/lib/github-sync-orchestrator.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-us-auto-closer.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-us-auto-closer.js +25 -0
- package/dist/plugins/specweave-github/lib/github-us-auto-closer.js.map +1 -1
- package/dist/plugins/specweave-github/lib/per-us-sync.d.ts +3 -0
- package/dist/plugins/specweave-github/lib/per-us-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/per-us-sync.js +29 -9
- package/dist/plugins/specweave-github/lib/per-us-sync.js.map +1 -1
- package/dist/plugins/specweave-jira/lib/content-format-adapter.d.ts +59 -0
- package/dist/plugins/specweave-jira/lib/content-format-adapter.d.ts.map +1 -0
- package/dist/plugins/specweave-jira/lib/content-format-adapter.js +159 -0
- package/dist/plugins/specweave-jira/lib/content-format-adapter.js.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-deployment-detector.d.ts +45 -0
- package/dist/plugins/specweave-jira/lib/jira-deployment-detector.d.ts.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-deployment-detector.js +92 -0
- package/dist/plugins/specweave-jira/lib/jira-deployment-detector.js.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-duplicate-detector.d.ts.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-duplicate-detector.js +13 -28
- package/dist/plugins/specweave-jira/lib/jira-duplicate-detector.js.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-epic-sync.d.ts +2 -1
- package/dist/plugins/specweave-jira/lib/jira-epic-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-epic-sync.js +19 -7
- package/dist/plugins/specweave-jira/lib/jira-epic-sync.js.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-field-discovery.d.ts +47 -0
- package/dist/plugins/specweave-jira/lib/jira-field-discovery.d.ts.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-field-discovery.js +110 -0
- package/dist/plugins/specweave-jira/lib/jira-field-discovery.js.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-paginated-search.d.ts +26 -0
- package/dist/plugins/specweave-jira/lib/jira-paginated-search.d.ts.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-paginated-search.js +77 -0
- package/dist/plugins/specweave-jira/lib/jira-paginated-search.js.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-spec-commit-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-spec-commit-sync.js +5 -3
- package/dist/plugins/specweave-jira/lib/jira-spec-commit-sync.js.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-spec-sync.d.ts +17 -2
- package/dist/plugins/specweave-jira/lib/jira-spec-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-spec-sync.js +103 -33
- package/dist/plugins/specweave-jira/lib/jira-spec-sync.js.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-status-sync.d.ts +4 -0
- package/dist/plugins/specweave-jira/lib/jira-status-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-status-sync.js +19 -6
- package/dist/plugins/specweave-jira/lib/jira-status-sync.js.map +1 -1
- package/dist/plugins/specweave-jira/lib/metadata-paths.d.ts +29 -0
- package/dist/plugins/specweave-jira/lib/metadata-paths.d.ts.map +1 -0
- package/dist/plugins/specweave-jira/lib/metadata-paths.js +73 -0
- package/dist/plugins/specweave-jira/lib/metadata-paths.js.map +1 -0
- package/dist/plugins/specweave-jira/lib/reorganization-detector.d.ts +15 -2
- package/dist/plugins/specweave-jira/lib/reorganization-detector.d.ts.map +1 -1
- package/dist/plugins/specweave-jira/lib/reorganization-detector.js +121 -33
- package/dist/plugins/specweave-jira/lib/reorganization-detector.js.map +1 -1
- package/dist/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +23 -18
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/sync-progress.d.ts +6 -0
- package/dist/src/cli/commands/sync-progress.d.ts.map +1 -1
- package/dist/src/cli/commands/sync-progress.js +37 -0
- package/dist/src/cli/commands/sync-progress.js.map +1 -1
- package/dist/src/cli/commands/sync-task.d.ts +16 -0
- package/dist/src/cli/commands/sync-task.d.ts.map +1 -0
- package/dist/src/cli/commands/sync-task.js +42 -0
- package/dist/src/cli/commands/sync-task.js.map +1 -0
- package/dist/src/cli/helpers/init/instruction-file-merger.js +3 -3
- package/dist/src/cli/helpers/init/instruction-file-merger.js.map +1 -1
- package/dist/src/core/hooks/LifecycleHookDispatcher.d.ts +9 -1
- package/dist/src/core/hooks/LifecycleHookDispatcher.d.ts.map +1 -1
- package/dist/src/core/hooks/LifecycleHookDispatcher.js +26 -8
- package/dist/src/core/hooks/LifecycleHookDispatcher.js.map +1 -1
- package/dist/src/core/increment/metadata-manager.d.ts +13 -0
- package/dist/src/core/increment/metadata-manager.d.ts.map +1 -1
- package/dist/src/core/increment/metadata-manager.js +144 -17
- package/dist/src/core/increment/metadata-manager.js.map +1 -1
- package/dist/src/core/increment/status-change-sync-trigger.d.ts +1 -1
- package/dist/src/core/increment/status-change-sync-trigger.d.ts.map +1 -1
- package/dist/src/core/increment/status-change-sync-trigger.js +2 -1
- package/dist/src/core/increment/status-change-sync-trigger.js.map +1 -1
- package/dist/src/core/increment/status-commands.d.ts.map +1 -1
- package/dist/src/core/increment/status-commands.js +33 -11
- package/dist/src/core/increment/status-commands.js.map +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 +2 -1
- package/dist/src/core/repo-structure/repo-structure-manager.js.map +1 -1
- package/dist/src/locales/de/cli.json +252 -77
- package/dist/src/locales/en/cli.json +7 -0
- package/dist/src/locales/es/cli.json +245 -3
- package/dist/src/locales/fr/cli.json +259 -84
- package/dist/src/locales/ja/cli.json +253 -78
- package/dist/src/locales/ko/cli.json +253 -78
- package/dist/src/locales/pt/cli.json +252 -77
- package/dist/src/locales/ru/cli.json +17 -3
- package/dist/src/locales/zh/cli.json +258 -83
- package/dist/src/sync/ado-reconciler.d.ts.map +1 -1
- package/dist/src/sync/ado-reconciler.js +5 -1
- package/dist/src/sync/ado-reconciler.js.map +1 -1
- package/dist/src/sync/base-reconciler.d.ts.map +1 -1
- package/dist/src/sync/base-reconciler.js +6 -1
- package/dist/src/sync/base-reconciler.js.map +1 -1
- package/dist/src/sync/config.d.ts +4 -0
- package/dist/src/sync/config.d.ts.map +1 -1
- package/dist/src/sync/config.js +6 -4
- package/dist/src/sync/config.js.map +1 -1
- package/dist/src/sync/external-issue-auto-creator.d.ts +3 -0
- package/dist/src/sync/external-issue-auto-creator.d.ts.map +1 -1
- package/dist/src/sync/external-issue-auto-creator.js +53 -17
- package/dist/src/sync/external-issue-auto-creator.js.map +1 -1
- package/dist/src/sync/external-item-sync-service.d.ts +9 -0
- package/dist/src/sync/external-item-sync-service.d.ts.map +1 -1
- package/dist/src/sync/external-item-sync-service.js +210 -9
- package/dist/src/sync/external-item-sync-service.js.map +1 -1
- package/dist/src/sync/github-reconciler.d.ts +30 -0
- package/dist/src/sync/github-reconciler.d.ts.map +1 -1
- package/dist/src/sync/github-reconciler.js +242 -3
- package/dist/src/sync/github-reconciler.js.map +1 -1
- package/dist/src/sync/jira-reconciler.d.ts.map +1 -1
- package/dist/src/sync/jira-reconciler.js +5 -1
- package/dist/src/sync/jira-reconciler.js.map +1 -1
- package/dist/src/sync/provider-router.d.ts.map +1 -1
- package/dist/src/sync/provider-router.js +2 -1
- package/dist/src/sync/provider-router.js.map +1 -1
- package/dist/src/sync/providers/ado.d.ts +4 -0
- package/dist/src/sync/providers/ado.d.ts.map +1 -1
- package/dist/src/sync/providers/ado.js +36 -11
- package/dist/src/sync/providers/ado.js.map +1 -1
- package/dist/src/sync/providers/github.d.ts.map +1 -1
- package/dist/src/sync/providers/github.js +48 -35
- package/dist/src/sync/providers/github.js.map +1 -1
- package/dist/src/sync/providers/jira.d.ts.map +1 -1
- package/dist/src/sync/providers/jira.js +42 -26
- package/dist/src/sync/providers/jira.js.map +1 -1
- package/dist/src/sync/status-mapper.d.ts +3 -1
- package/dist/src/sync/status-mapper.d.ts.map +1 -1
- package/dist/src/sync/status-mapper.js +10 -2
- package/dist/src/sync/status-mapper.js.map +1 -1
- package/dist/src/sync/sync-coordinator.d.ts.map +1 -1
- package/dist/src/sync/sync-coordinator.js +29 -19
- package/dist/src/sync/sync-coordinator.js.map +1 -1
- package/package.json +1 -1
- package/plugins/specweave/hooks/v2/guards/task-ac-sync-guard.sh +31 -0
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.d.ts +13 -0
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js +144 -17
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js.map +1 -1
- package/plugins/specweave/lib/vendor/sync/github-reconciler.d.ts +30 -0
- package/plugins/specweave/lib/vendor/sync/github-reconciler.js +242 -3
- package/plugins/specweave/lib/vendor/sync/github-reconciler.js.map +1 -1
- package/plugins/specweave/skills/architect/SKILL.md +2 -0
- package/plugins/specweave/skills/grill/SKILL.md +2 -0
- package/plugins/specweave/skills/team-lead/SKILL.md +43 -320
- package/plugins/specweave/skills/team-lead/agents/backend.md +60 -0
- package/plugins/specweave/skills/team-lead/agents/database.md +51 -0
- package/plugins/specweave/skills/team-lead/agents/frontend.md +61 -0
- package/plugins/specweave/skills/team-lead/agents/security.md +52 -0
- package/plugins/specweave/skills/team-lead/agents/testing.md +57 -0
- package/plugins/specweave/skills/test-aware-planner/SKILL.md +2 -0
- package/plugins/specweave-ado/hooks/post-task-completion.sh +2 -2
- package/plugins/specweave-ado/lib/ado-client-v2.js +51 -21
- package/plugins/specweave-ado/lib/ado-client-v2.ts +62 -23
- package/plugins/specweave-ado/lib/ado-duplicate-detector.js +4 -4
- package/plugins/specweave-ado/lib/ado-duplicate-detector.ts +4 -3
- package/plugins/specweave-ado/lib/ado-hierarchical-sync.js +54 -12
- package/plugins/specweave-ado/lib/ado-hierarchical-sync.ts +88 -18
- package/plugins/specweave-ado/lib/ado-profile-resolver.js +1 -1
- package/plugins/specweave-ado/lib/ado-profile-resolver.ts +3 -1
- package/plugins/specweave-ado/lib/ado-spec-sync.js +22 -9
- package/plugins/specweave-ado/lib/ado-spec-sync.ts +27 -9
- package/plugins/specweave-ado/lib/conflict-resolver.js +17 -1
- package/plugins/specweave-ado/lib/conflict-resolver.ts +17 -1
- package/plugins/specweave-ado/lib/enhanced-ado-sync.js +11 -1
- package/plugins/specweave-ado/lib/per-us-sync.js +8 -1
- package/plugins/specweave-ado/lib/per-us-sync.ts +17 -2
- package/plugins/specweave-github/hooks/github-auto-create-handler.sh +28 -2
- package/plugins/specweave-github/hooks/post-task-completion.sh +6 -3
- package/plugins/specweave-github/lib/enhanced-github-sync.js +35 -6
- package/plugins/specweave-github/lib/github-board-resolver.js +4 -4
- package/plugins/specweave-github/lib/github-board-resolver.ts +4 -4
- package/plugins/specweave-github/lib/github-client-v2.js +6 -6
- package/plugins/specweave-github/lib/github-client-v2.ts +11 -7
- package/plugins/specweave-github/lib/github-client.js +5 -4
- package/plugins/specweave-github/lib/github-client.ts +7 -5
- package/plugins/specweave-github/lib/github-cross-repo-sync.js +17 -3
- package/plugins/specweave-github/lib/github-cross-repo-sync.ts +16 -3
- package/plugins/specweave-github/lib/github-feature-sync-cli.js +20 -11
- package/plugins/specweave-github/lib/github-feature-sync-cli.ts +42 -20
- package/plugins/specweave-github/lib/github-feature-sync.js +32 -8
- package/plugins/specweave-github/lib/github-feature-sync.ts +41 -9
- package/plugins/specweave-github/lib/github-graphql-client.js +29 -20
- package/plugins/specweave-github/lib/github-graphql-client.ts +34 -22
- package/plugins/specweave-github/lib/github-hierarchical-sync.js +2 -2
- package/plugins/specweave-github/lib/github-hierarchical-sync.ts +2 -2
- package/plugins/specweave-github/lib/github-multi-project-sync.js +23 -7
- package/plugins/specweave-github/lib/github-multi-project-sync.ts +26 -8
- package/plugins/specweave-github/lib/github-spec-frontmatter-updater.js +110 -5
- package/plugins/specweave-github/lib/github-spec-frontmatter-updater.ts +135 -9
- package/plugins/specweave-github/lib/github-spec-sync.js +85 -24
- package/plugins/specweave-github/lib/github-spec-sync.ts +100 -26
- package/plugins/specweave-github/lib/github-sync-orchestrator.js +2 -1
- package/plugins/specweave-github/lib/github-sync-orchestrator.ts +3 -1
- package/plugins/specweave-github/lib/github-us-auto-closer.js +25 -0
- package/plugins/specweave-github/lib/github-us-auto-closer.ts +43 -0
- package/plugins/specweave-github/lib/per-us-sync.js +26 -11
- package/plugins/specweave-github/lib/per-us-sync.ts +29 -11
- package/plugins/specweave-jira/hooks/post-task-completion.sh +2 -1
- package/plugins/specweave-jira/lib/content-format-adapter.js +116 -0
- package/plugins/specweave-jira/lib/content-format-adapter.ts +189 -0
- package/plugins/specweave-jira/lib/enhanced-jira-sync.js +21 -5
- package/plugins/specweave-jira/lib/jira-deployment-detector.js +63 -0
- package/plugins/specweave-jira/lib/jira-deployment-detector.ts +113 -0
- package/plugins/specweave-jira/lib/jira-duplicate-detector.js +12 -29
- package/plugins/specweave-jira/lib/jira-duplicate-detector.ts +13 -27
- package/plugins/specweave-jira/lib/jira-epic-sync.js +15 -5
- package/plugins/specweave-jira/lib/jira-epic-sync.ts +22 -7
- package/plugins/specweave-jira/lib/jira-field-discovery.js +76 -0
- package/plugins/specweave-jira/lib/jira-field-discovery.ts +139 -0
- package/plugins/specweave-jira/lib/jira-hierarchical-sync.js +10 -0
- package/plugins/specweave-jira/lib/jira-hierarchical-sync.ts +11 -0
- package/plugins/specweave-jira/lib/jira-multi-project-sync.js +19 -9
- package/plugins/specweave-jira/lib/jira-multi-project-sync.ts +25 -14
- package/plugins/specweave-jira/lib/jira-paginated-search.js +55 -0
- package/plugins/specweave-jira/lib/jira-paginated-search.ts +108 -0
- package/plugins/specweave-jira/lib/jira-spec-commit-sync.js +5 -3
- package/plugins/specweave-jira/lib/jira-spec-commit-sync.ts +5 -3
- package/plugins/specweave-jira/lib/jira-spec-sync.js +102 -31
- package/plugins/specweave-jira/lib/jira-spec-sync.ts +123 -45
- package/plugins/specweave-jira/lib/jira-status-sync.js +18 -5
- package/plugins/specweave-jira/lib/jira-status-sync.ts +21 -6
- package/plugins/specweave-jira/lib/metadata-paths.js +38 -0
- package/plugins/specweave-jira/lib/metadata-paths.ts +73 -0
- package/plugins/specweave-jira/lib/reorganization-detector.js +101 -23
- package/plugins/specweave-jira/lib/reorganization-detector.ts +125 -35
- package/plugins/specweave-jira/scripts/refresh-cache.js +1 -1
- package/plugins/specweave-jira/scripts/refresh-cache.ts +2 -2
- package/plugins/specweave-jira/skills/jira-resource-validator/SKILL.md +3 -5
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical Metadata Paths for JIRA Issue Keys
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for reading/writing JIRA issue keys in metadata.
|
|
5
|
+
* All plugin files MUST use these helpers instead of direct property access.
|
|
6
|
+
*
|
|
7
|
+
* Canonical path: external_sync.jira.issueKey
|
|
8
|
+
* Legacy paths (read-only fallback): jira.issue, jira.issueKey
|
|
9
|
+
*
|
|
10
|
+
* @module metadata-paths
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** Canonical dot-path for JIRA issue key in metadata */
|
|
14
|
+
export const CANONICAL_JIRA_KEY_PATH = 'external_sync.jira.issueKey';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Read JIRA issue key from metadata, checking canonical path first
|
|
18
|
+
* then falling back to legacy paths for backward compatibility.
|
|
19
|
+
*/
|
|
20
|
+
export function readIssueKey(metadata: any): string | null {
|
|
21
|
+
if (!metadata) return null;
|
|
22
|
+
|
|
23
|
+
// Canonical path (preferred)
|
|
24
|
+
const canonical = metadata?.external_sync?.jira?.issueKey;
|
|
25
|
+
if (canonical) return canonical;
|
|
26
|
+
|
|
27
|
+
// Legacy path: .jira.issue
|
|
28
|
+
const legacyIssue = metadata?.jira?.issue;
|
|
29
|
+
if (legacyIssue) return legacyIssue;
|
|
30
|
+
|
|
31
|
+
// Legacy path: .jira.issueKey
|
|
32
|
+
const legacyKey = metadata?.jira?.issueKey;
|
|
33
|
+
if (legacyKey) return legacyKey;
|
|
34
|
+
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Write JIRA issue key to the canonical metadata path.
|
|
40
|
+
* Creates intermediate objects if needed.
|
|
41
|
+
*/
|
|
42
|
+
export function writeIssueKey(metadata: any, key: string): any {
|
|
43
|
+
if (!metadata) metadata = {};
|
|
44
|
+
if (!metadata.external_sync) metadata.external_sync = {};
|
|
45
|
+
if (!metadata.external_sync.jira) metadata.external_sync.jira = {};
|
|
46
|
+
metadata.external_sync.jira.issueKey = key;
|
|
47
|
+
return metadata;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Migrate legacy metadata paths to canonical path.
|
|
52
|
+
* Reads from legacy, writes to canonical, optionally cleans up legacy keys.
|
|
53
|
+
*/
|
|
54
|
+
export function migrateToCanonical(metadata: any, cleanup: boolean = false): any {
|
|
55
|
+
if (!metadata) return metadata;
|
|
56
|
+
|
|
57
|
+
const existingKey = readIssueKey(metadata);
|
|
58
|
+
if (!existingKey) return metadata;
|
|
59
|
+
|
|
60
|
+
// Write to canonical
|
|
61
|
+
writeIssueKey(metadata, existingKey);
|
|
62
|
+
|
|
63
|
+
// Optionally remove legacy paths
|
|
64
|
+
if (cleanup) {
|
|
65
|
+
if (metadata.jira?.issue) delete metadata.jira.issue;
|
|
66
|
+
if (metadata.jira?.issueKey) delete metadata.jira.issueKey;
|
|
67
|
+
if (metadata.jira && Object.keys(metadata.jira).length === 0) {
|
|
68
|
+
delete metadata.jira;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return metadata;
|
|
73
|
+
}
|
|
@@ -1,6 +1,19 @@
|
|
|
1
|
+
import * as fs from "fs/promises";
|
|
2
|
+
import * as path from "path";
|
|
1
3
|
class JiraReorganizationDetector {
|
|
2
4
|
constructor(client) {
|
|
3
5
|
this.client = client;
|
|
6
|
+
/** Track known parent keys for reparent detection */
|
|
7
|
+
this.previousParents = /* @__PURE__ */ new Map();
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Set known parent keys from previous sync metadata.
|
|
11
|
+
* Call before detectReorganization() to enable accurate reparent detection.
|
|
12
|
+
*/
|
|
13
|
+
setKnownParents(parents) {
|
|
14
|
+
for (const [key, parent] of Object.entries(parents)) {
|
|
15
|
+
this.previousParents.set(key, parent);
|
|
16
|
+
}
|
|
4
17
|
}
|
|
5
18
|
/**
|
|
6
19
|
* Detect all reorganization events for tracked issues
|
|
@@ -117,22 +130,26 @@ class JiraReorganizationDetector {
|
|
|
117
130
|
return null;
|
|
118
131
|
}
|
|
119
132
|
/**
|
|
120
|
-
* Detect if issue was moved to different epic
|
|
133
|
+
* Detect if issue was moved to different epic.
|
|
134
|
+
*
|
|
135
|
+
* Requires previousParents map to track known parent keys.
|
|
136
|
+
* Only fires REPARENTED when the parent actually changed.
|
|
121
137
|
*/
|
|
122
138
|
detectReparent(originalKey, issue, lastSyncTimestamp) {
|
|
123
|
-
const currentParent = issue.fields.parent?.key;
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
139
|
+
const currentParent = issue.fields.parent?.key || null;
|
|
140
|
+
const previousParent = this.previousParents.get(originalKey) || null;
|
|
141
|
+
if (previousParent !== null && currentParent !== previousParent) {
|
|
142
|
+
return {
|
|
143
|
+
type: "REPARENTED",
|
|
144
|
+
timestamp: issue.fields.updated,
|
|
145
|
+
description: `Issue ${originalKey} reparented from ${previousParent} to ${currentParent || "none"}`,
|
|
146
|
+
originalKeys: [originalKey],
|
|
147
|
+
fromEpic: previousParent,
|
|
148
|
+
toEpic: currentParent || void 0
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
if (currentParent) {
|
|
152
|
+
this.previousParents.set(originalKey, currentParent);
|
|
136
153
|
}
|
|
137
154
|
return null;
|
|
138
155
|
}
|
|
@@ -190,24 +207,85 @@ async function handleReorganization(events, incrementId, projectRoot = process.c
|
|
|
190
207
|
console.log(`
|
|
191
208
|
\u{1F527} Handling ${events.length} reorganization events...
|
|
192
209
|
`);
|
|
210
|
+
const metadataPath = path.join(
|
|
211
|
+
projectRoot,
|
|
212
|
+
".specweave",
|
|
213
|
+
"increments",
|
|
214
|
+
incrementId,
|
|
215
|
+
"metadata.json"
|
|
216
|
+
);
|
|
217
|
+
let metadata = {};
|
|
218
|
+
try {
|
|
219
|
+
metadata = JSON.parse(await fs.readFile(metadataPath, "utf-8"));
|
|
220
|
+
} catch {
|
|
221
|
+
console.warn(` \u26A0\uFE0F Could not read metadata.json for ${incrementId}`);
|
|
222
|
+
}
|
|
223
|
+
if (!metadata.reorganization) {
|
|
224
|
+
metadata.reorganization = { events: [], lastHandled: null };
|
|
225
|
+
}
|
|
193
226
|
for (const event of events) {
|
|
194
227
|
switch (event.type) {
|
|
195
|
-
case "MOVED_PROJECT":
|
|
228
|
+
case "MOVED_PROJECT": {
|
|
229
|
+
if (event.newKeys?.[0]) {
|
|
230
|
+
if (!metadata.external_sync) metadata.external_sync = {};
|
|
231
|
+
if (!metadata.external_sync.jira) metadata.external_sync.jira = {};
|
|
232
|
+
metadata.external_sync.jira.issueKey = event.newKeys[0];
|
|
233
|
+
metadata.external_sync.jira.project = event.toProject;
|
|
234
|
+
}
|
|
196
235
|
console.log(` \u2713 Updated project mapping: ${event.fromProject} \u2192 ${event.toProject}`);
|
|
197
236
|
break;
|
|
198
|
-
|
|
199
|
-
|
|
237
|
+
}
|
|
238
|
+
case "SPLIT": {
|
|
239
|
+
if (!metadata.external_sync?.jira?.relatedKeys) {
|
|
240
|
+
if (!metadata.external_sync) metadata.external_sync = {};
|
|
241
|
+
if (!metadata.external_sync.jira) metadata.external_sync.jira = {};
|
|
242
|
+
metadata.external_sync.jira.relatedKeys = [];
|
|
243
|
+
}
|
|
244
|
+
if (event.newKeys) {
|
|
245
|
+
metadata.external_sync.jira.relatedKeys.push(...event.newKeys);
|
|
246
|
+
}
|
|
247
|
+
console.log(` \u2713 Recorded split: ${event.newKeys?.join(", ")}`);
|
|
200
248
|
break;
|
|
201
|
-
|
|
202
|
-
|
|
249
|
+
}
|
|
250
|
+
case "MERGED": {
|
|
251
|
+
if (event.newKeys?.[0]) {
|
|
252
|
+
if (!metadata.external_sync) metadata.external_sync = {};
|
|
253
|
+
if (!metadata.external_sync.jira) metadata.external_sync.jira = {};
|
|
254
|
+
metadata.external_sync.jira.issueKey = event.newKeys[0];
|
|
255
|
+
metadata.external_sync.jira.mergedFrom = event.originalKeys[0];
|
|
256
|
+
}
|
|
257
|
+
console.log(` \u2713 Updated merged issue: ${event.originalKeys[0]} \u2192 ${event.newKeys?.[0]}`);
|
|
203
258
|
break;
|
|
204
|
-
|
|
205
|
-
|
|
259
|
+
}
|
|
260
|
+
case "REPARENTED": {
|
|
261
|
+
if (!metadata.external_sync) metadata.external_sync = {};
|
|
262
|
+
if (!metadata.external_sync.jira) metadata.external_sync.jira = {};
|
|
263
|
+
metadata.external_sync.jira.epicKey = event.toEpic || null;
|
|
264
|
+
metadata.external_sync.jira.previousEpicKey = event.fromEpic || null;
|
|
265
|
+
console.log(` \u2713 Updated epic link: ${event.fromEpic || "none"} \u2192 ${event.toEpic || "none"}`);
|
|
206
266
|
break;
|
|
207
|
-
|
|
208
|
-
|
|
267
|
+
}
|
|
268
|
+
case "DELETED": {
|
|
269
|
+
if (!metadata.external_sync) metadata.external_sync = {};
|
|
270
|
+
if (!metadata.external_sync.jira) metadata.external_sync.jira = {};
|
|
271
|
+
metadata.external_sync.jira.deleted = true;
|
|
272
|
+
metadata.external_sync.jira.deletedAt = event.timestamp;
|
|
273
|
+
console.log(` \u26A0\uFE0F Marked as deleted: ${event.originalKeys[0]}`);
|
|
209
274
|
break;
|
|
275
|
+
}
|
|
210
276
|
}
|
|
277
|
+
metadata.reorganization.events.push({
|
|
278
|
+
type: event.type,
|
|
279
|
+
timestamp: event.timestamp,
|
|
280
|
+
description: event.description
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
metadata.reorganization.lastHandled = (/* @__PURE__ */ new Date()).toISOString();
|
|
284
|
+
try {
|
|
285
|
+
await fs.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
|
|
286
|
+
console.log(` \u{1F4C4} Updated metadata.json for ${incrementId}`);
|
|
287
|
+
} catch (err) {
|
|
288
|
+
console.warn(` \u26A0\uFE0F Failed to write metadata: ${err.message}`);
|
|
211
289
|
}
|
|
212
290
|
console.log("\n\u2705 Reorganization handled\n");
|
|
213
291
|
}
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { JiraClient, JiraIssue } from '../../../src/integrations/jira/jira-client.js';
|
|
15
|
+
import * as fs from 'fs/promises';
|
|
16
|
+
import * as path from 'path';
|
|
15
17
|
|
|
16
18
|
// ============================================================================
|
|
17
19
|
// Types
|
|
@@ -54,8 +56,21 @@ export interface ReorganizationDetectionResult {
|
|
|
54
56
|
// ============================================================================
|
|
55
57
|
|
|
56
58
|
export class JiraReorganizationDetector {
|
|
59
|
+
/** Track known parent keys for reparent detection */
|
|
60
|
+
private previousParents = new Map<string, string>();
|
|
61
|
+
|
|
57
62
|
constructor(private client: JiraClient) {}
|
|
58
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Set known parent keys from previous sync metadata.
|
|
66
|
+
* Call before detectReorganization() to enable accurate reparent detection.
|
|
67
|
+
*/
|
|
68
|
+
setKnownParents(parents: Record<string, string>): void {
|
|
69
|
+
for (const [key, parent] of Object.entries(parents)) {
|
|
70
|
+
this.previousParents.set(key, parent);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
59
74
|
/**
|
|
60
75
|
* Detect all reorganization events for tracked issues
|
|
61
76
|
*/
|
|
@@ -220,32 +235,34 @@ export class JiraReorganizationDetector {
|
|
|
220
235
|
}
|
|
221
236
|
|
|
222
237
|
/**
|
|
223
|
-
* Detect if issue was moved to different epic
|
|
238
|
+
* Detect if issue was moved to different epic.
|
|
239
|
+
*
|
|
240
|
+
* Requires previousParents map to track known parent keys.
|
|
241
|
+
* Only fires REPARENTED when the parent actually changed.
|
|
224
242
|
*/
|
|
225
243
|
private detectReparent(
|
|
226
244
|
originalKey: string,
|
|
227
245
|
issue: JiraIssue,
|
|
228
246
|
lastSyncTimestamp?: string
|
|
229
247
|
): ReorganizationEvent | null {
|
|
230
|
-
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
//
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
}
|
|
248
|
+
const currentParent = issue.fields.parent?.key || null;
|
|
249
|
+
const previousParent = this.previousParents.get(originalKey) || null;
|
|
250
|
+
|
|
251
|
+
// Only fire REPARENTED if we know the previous parent AND it differs
|
|
252
|
+
if (previousParent !== null && currentParent !== previousParent) {
|
|
253
|
+
return {
|
|
254
|
+
type: 'REPARENTED',
|
|
255
|
+
timestamp: issue.fields.updated,
|
|
256
|
+
description: `Issue ${originalKey} reparented from ${previousParent} to ${currentParent || 'none'}`,
|
|
257
|
+
originalKeys: [originalKey],
|
|
258
|
+
fromEpic: previousParent,
|
|
259
|
+
toEpic: currentParent || undefined,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Update tracked parent for future comparisons
|
|
264
|
+
if (currentParent) {
|
|
265
|
+
this.previousParents.set(originalKey, currentParent);
|
|
249
266
|
}
|
|
250
267
|
|
|
251
268
|
return null;
|
|
@@ -313,7 +330,10 @@ export class JiraReorganizationDetector {
|
|
|
313
330
|
// ============================================================================
|
|
314
331
|
|
|
315
332
|
/**
|
|
316
|
-
* Handle reorganization events by updating SpecWeave increment
|
|
333
|
+
* Handle reorganization events by updating SpecWeave increment metadata.
|
|
334
|
+
*
|
|
335
|
+
* For each event type, updates local metadata.json to reflect
|
|
336
|
+
* the reorganization that happened in JIRA.
|
|
317
337
|
*/
|
|
318
338
|
export async function handleReorganization(
|
|
319
339
|
events: ReorganizationEvent[],
|
|
@@ -326,33 +346,103 @@ export async function handleReorganization(
|
|
|
326
346
|
|
|
327
347
|
console.log(`\n🔧 Handling ${events.length} reorganization events...\n`);
|
|
328
348
|
|
|
349
|
+
const metadataPath = path.join(
|
|
350
|
+
projectRoot,
|
|
351
|
+
'.specweave',
|
|
352
|
+
'increments',
|
|
353
|
+
incrementId,
|
|
354
|
+
'metadata.json'
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
let metadata: any = {};
|
|
358
|
+
try {
|
|
359
|
+
metadata = JSON.parse(await fs.readFile(metadataPath, 'utf-8'));
|
|
360
|
+
} catch {
|
|
361
|
+
console.warn(` ⚠️ Could not read metadata.json for ${incrementId}`);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Ensure reorganization log exists
|
|
365
|
+
if (!metadata.reorganization) {
|
|
366
|
+
metadata.reorganization = { events: [], lastHandled: null };
|
|
367
|
+
}
|
|
368
|
+
|
|
329
369
|
for (const event of events) {
|
|
330
370
|
switch (event.type) {
|
|
331
|
-
case 'MOVED_PROJECT':
|
|
371
|
+
case 'MOVED_PROJECT': {
|
|
372
|
+
// Update tracked project key in metadata
|
|
373
|
+
if (event.newKeys?.[0]) {
|
|
374
|
+
if (!metadata.external_sync) metadata.external_sync = {};
|
|
375
|
+
if (!metadata.external_sync.jira) metadata.external_sync.jira = {};
|
|
376
|
+
metadata.external_sync.jira.issueKey = event.newKeys[0];
|
|
377
|
+
metadata.external_sync.jira.project = event.toProject;
|
|
378
|
+
}
|
|
332
379
|
console.log(` ✓ Updated project mapping: ${event.fromProject} → ${event.toProject}`);
|
|
333
|
-
// Update metadata with new project/key
|
|
334
380
|
break;
|
|
381
|
+
}
|
|
335
382
|
|
|
336
|
-
case 'SPLIT':
|
|
337
|
-
|
|
338
|
-
|
|
383
|
+
case 'SPLIT': {
|
|
384
|
+
// Record split — new keys added as related issues
|
|
385
|
+
if (!metadata.external_sync?.jira?.relatedKeys) {
|
|
386
|
+
if (!metadata.external_sync) metadata.external_sync = {};
|
|
387
|
+
if (!metadata.external_sync.jira) metadata.external_sync.jira = {};
|
|
388
|
+
metadata.external_sync.jira.relatedKeys = [];
|
|
389
|
+
}
|
|
390
|
+
if (event.newKeys) {
|
|
391
|
+
metadata.external_sync.jira.relatedKeys.push(...event.newKeys);
|
|
392
|
+
}
|
|
393
|
+
console.log(` ✓ Recorded split: ${event.newKeys?.join(', ')}`);
|
|
339
394
|
break;
|
|
395
|
+
}
|
|
340
396
|
|
|
341
|
-
case 'MERGED':
|
|
342
|
-
|
|
343
|
-
|
|
397
|
+
case 'MERGED': {
|
|
398
|
+
// Update issue key to the merge target
|
|
399
|
+
if (event.newKeys?.[0]) {
|
|
400
|
+
if (!metadata.external_sync) metadata.external_sync = {};
|
|
401
|
+
if (!metadata.external_sync.jira) metadata.external_sync.jira = {};
|
|
402
|
+
metadata.external_sync.jira.issueKey = event.newKeys[0];
|
|
403
|
+
metadata.external_sync.jira.mergedFrom = event.originalKeys[0];
|
|
404
|
+
}
|
|
405
|
+
console.log(` ✓ Updated merged issue: ${event.originalKeys[0]} → ${event.newKeys?.[0]}`);
|
|
344
406
|
break;
|
|
407
|
+
}
|
|
345
408
|
|
|
346
|
-
case 'REPARENTED':
|
|
347
|
-
|
|
348
|
-
|
|
409
|
+
case 'REPARENTED': {
|
|
410
|
+
// Update parent epic key in metadata
|
|
411
|
+
if (!metadata.external_sync) metadata.external_sync = {};
|
|
412
|
+
if (!metadata.external_sync.jira) metadata.external_sync.jira = {};
|
|
413
|
+
metadata.external_sync.jira.epicKey = event.toEpic || null;
|
|
414
|
+
metadata.external_sync.jira.previousEpicKey = event.fromEpic || null;
|
|
415
|
+
console.log(` ✓ Updated epic link: ${event.fromEpic || 'none'} → ${event.toEpic || 'none'}`);
|
|
349
416
|
break;
|
|
417
|
+
}
|
|
350
418
|
|
|
351
|
-
case 'DELETED':
|
|
352
|
-
|
|
353
|
-
|
|
419
|
+
case 'DELETED': {
|
|
420
|
+
// Mark issue as deleted (don't remove metadata, just flag)
|
|
421
|
+
if (!metadata.external_sync) metadata.external_sync = {};
|
|
422
|
+
if (!metadata.external_sync.jira) metadata.external_sync.jira = {};
|
|
423
|
+
metadata.external_sync.jira.deleted = true;
|
|
424
|
+
metadata.external_sync.jira.deletedAt = event.timestamp;
|
|
425
|
+
console.log(` ⚠️ Marked as deleted: ${event.originalKeys[0]}`);
|
|
354
426
|
break;
|
|
427
|
+
}
|
|
355
428
|
}
|
|
429
|
+
|
|
430
|
+
// Log event for audit trail
|
|
431
|
+
metadata.reorganization.events.push({
|
|
432
|
+
type: event.type,
|
|
433
|
+
timestamp: event.timestamp,
|
|
434
|
+
description: event.description,
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
metadata.reorganization.lastHandled = new Date().toISOString();
|
|
439
|
+
|
|
440
|
+
// Write updated metadata
|
|
441
|
+
try {
|
|
442
|
+
await fs.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
|
|
443
|
+
console.log(` 📄 Updated metadata.json for ${incrementId}`);
|
|
444
|
+
} catch (err) {
|
|
445
|
+
console.warn(` ⚠️ Failed to write metadata: ${(err as Error).message}`);
|
|
356
446
|
}
|
|
357
447
|
|
|
358
448
|
console.log('\n✅ Reorganization handled\n');
|
|
@@ -17,7 +17,7 @@ async function refreshJiraCache(projectRoot = process.cwd()) {
|
|
|
17
17
|
}
|
|
18
18
|
console.log(`\u2705 Cleared ${cleared} cache files`);
|
|
19
19
|
}
|
|
20
|
-
if (
|
|
20
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
21
21
|
refreshJiraCache().catch(console.error);
|
|
22
22
|
}
|
|
23
23
|
export {
|
|
@@ -34,7 +34,7 @@ export async function refreshJiraCache(projectRoot: string = process.cwd()): Pro
|
|
|
34
34
|
console.log(`✅ Cleared ${cleared} cache files`);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
// CLI entry
|
|
38
|
-
if (
|
|
37
|
+
// CLI entry (ESM-compatible)
|
|
38
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
39
39
|
refreshJiraCache().catch(console.error);
|
|
40
40
|
}
|
|
@@ -59,11 +59,9 @@ if [[ ! "$JIRA_DOMAIN" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9](
|
|
|
59
59
|
exit 1
|
|
60
60
|
fi
|
|
61
61
|
|
|
62
|
-
# Cloud
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
exit 1
|
|
66
|
-
fi
|
|
62
|
+
# JIRA Cloud and Server/DC are both supported.
|
|
63
|
+
# Cloud domains match <subdomain>.atlassian.net; self-hosted use custom domains.
|
|
64
|
+
# No hard-block — deployment type is auto-detected at runtime.
|
|
67
65
|
```
|
|
68
66
|
|
|
69
67
|
### API Call Pattern (HTTPS only, quoted variables)
|