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
|
@@ -31,6 +31,8 @@ import {
|
|
|
31
31
|
formatForJira,
|
|
32
32
|
CommentContent,
|
|
33
33
|
} from '../../../src/core/comment-builder.js';
|
|
34
|
+
import { readIssueKey } from './metadata-paths.js';
|
|
35
|
+
import { getApiBaseUrl } from './jira-deployment-detector.js';
|
|
34
36
|
import path from 'path';
|
|
35
37
|
import fs from 'fs/promises';
|
|
36
38
|
|
|
@@ -84,7 +86,7 @@ export async function syncSpecCommitsToJira(
|
|
|
84
86
|
return result;
|
|
85
87
|
}
|
|
86
88
|
|
|
87
|
-
const jiraIssueKey = metadata
|
|
89
|
+
const jiraIssueKey = readIssueKey(metadata);
|
|
88
90
|
if (!jiraIssueKey) {
|
|
89
91
|
if (verbose) {
|
|
90
92
|
console.log('No JIRA issue linked to increment');
|
|
@@ -94,7 +96,7 @@ export async function syncSpecCommitsToJira(
|
|
|
94
96
|
|
|
95
97
|
// 2. Create JIRA client
|
|
96
98
|
const client = axios.create({
|
|
97
|
-
baseURL:
|
|
99
|
+
baseURL: getApiBaseUrl(config.domain),
|
|
98
100
|
auth: {
|
|
99
101
|
username: config.email,
|
|
100
102
|
password: config.apiToken,
|
|
@@ -285,7 +287,7 @@ export async function postCommitBatchUpdate(
|
|
|
285
287
|
): Promise<boolean> {
|
|
286
288
|
try {
|
|
287
289
|
const client = axios.create({
|
|
288
|
-
baseURL:
|
|
290
|
+
baseURL: getApiBaseUrl(config.domain),
|
|
289
291
|
auth: {
|
|
290
292
|
username: config.email,
|
|
291
293
|
password: config.apiToken,
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { SpecMetadataManager } from "../../../src/core/specs/spec-metadata-manager.js";
|
|
2
2
|
import { SpecParser } from "../../../src/core/specs/spec-parser.js";
|
|
3
|
+
import * as fs from "fs/promises";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
import { detectDeploymentType, getApiBaseUrl } from "./jira-deployment-detector.js";
|
|
6
|
+
import { toDescription } from "./content-format-adapter.js";
|
|
7
|
+
import { getEpicLinkFieldForProject } from "./jira-field-discovery.js";
|
|
8
|
+
import { searchAllIssues } from "./jira-paginated-search.js";
|
|
3
9
|
import axios from "axios";
|
|
4
10
|
class JiraSpecSync {
|
|
5
11
|
constructor(config, projectRoot = process.cwd()) {
|
|
6
12
|
this.specManager = new SpecMetadataManager(projectRoot);
|
|
7
13
|
this.config = config;
|
|
8
14
|
this.client = axios.create({
|
|
9
|
-
baseURL:
|
|
15
|
+
baseURL: getApiBaseUrl(config.domain),
|
|
10
16
|
auth: {
|
|
11
17
|
username: config.email,
|
|
12
18
|
password: config.apiToken
|
|
@@ -17,6 +23,16 @@ class JiraSpecSync {
|
|
|
17
23
|
}
|
|
18
24
|
});
|
|
19
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Initialize: detect deployment type and update client baseURL
|
|
28
|
+
*/
|
|
29
|
+
async init() {
|
|
30
|
+
const deployment = await detectDeploymentType(this.config.domain, {
|
|
31
|
+
email: this.config.email,
|
|
32
|
+
apiToken: this.config.apiToken
|
|
33
|
+
});
|
|
34
|
+
this.client.defaults.baseURL = deployment.baseUrl;
|
|
35
|
+
}
|
|
20
36
|
/**
|
|
21
37
|
* Sync spec to Jira Epic (CREATE or UPDATE)
|
|
22
38
|
*/
|
|
@@ -106,6 +122,7 @@ class JiraSpecSync {
|
|
|
106
122
|
};
|
|
107
123
|
}
|
|
108
124
|
console.log(`\u26A0\uFE0F Detected ${conflicts.length} conflict(s)`);
|
|
125
|
+
await this.writeConflictReport(specId, conflicts);
|
|
109
126
|
await this.resolveConflicts(spec, conflicts);
|
|
110
127
|
console.log("\u2705 Sync FROM Jira complete!");
|
|
111
128
|
return {
|
|
@@ -131,7 +148,7 @@ class JiraSpecSync {
|
|
|
131
148
|
*/
|
|
132
149
|
async createJiraEpic(spec) {
|
|
133
150
|
const epicSummary = `[${spec.metadata.id.toUpperCase()}] ${spec.metadata.title}`;
|
|
134
|
-
const epicDescription = this.generateEpicDescription(spec);
|
|
151
|
+
const epicDescription = toDescription(this.generateEpicDescription(spec), this.config.domain);
|
|
135
152
|
const issueType = this.mapTypeToJira(spec.metadata.type, "Epic");
|
|
136
153
|
const payload = {
|
|
137
154
|
fields: {
|
|
@@ -169,7 +186,7 @@ class JiraSpecSync {
|
|
|
169
186
|
*/
|
|
170
187
|
async updateJiraEpic(epicKey, spec) {
|
|
171
188
|
const epicSummary = `[${spec.metadata.id.toUpperCase()}] ${spec.metadata.title}`;
|
|
172
|
-
const epicDescription = this.generateEpicDescription(spec);
|
|
189
|
+
const epicDescription = toDescription(this.generateEpicDescription(spec), this.config.domain);
|
|
173
190
|
const payload = {
|
|
174
191
|
fields: {
|
|
175
192
|
summary: epicSummary,
|
|
@@ -295,20 +312,72 @@ ${acList}
|
|
|
295
312
|
return conflicts;
|
|
296
313
|
}
|
|
297
314
|
/**
|
|
298
|
-
* Resolve conflicts
|
|
315
|
+
* Resolve conflicts based on configurable strategy.
|
|
316
|
+
*
|
|
317
|
+
* Strategies:
|
|
318
|
+
* - 'manual' (default): Halt sync, report conflicts to user, no auto-resolve
|
|
319
|
+
* - 'remote-wins': Auto-resolve in favor of JIRA (remote)
|
|
320
|
+
* - 'local-wins': Auto-resolve in favor of spec (local)
|
|
321
|
+
* - 'report-only': Log conflicts, continue without resolving
|
|
299
322
|
*/
|
|
300
|
-
async resolveConflicts(spec, conflicts) {
|
|
323
|
+
async resolveConflicts(spec, conflicts, strategy = "manual") {
|
|
324
|
+
if (strategy === "manual") {
|
|
325
|
+
console.log(` \u26A0\uFE0F ${conflicts.length} conflict(s) require manual resolution.`);
|
|
326
|
+
for (const conflict of conflicts) {
|
|
327
|
+
console.log(` - ${conflict.field}: local="${conflict.localValue}" vs remote="${conflict.remoteValue}"`);
|
|
328
|
+
}
|
|
329
|
+
console.log(` Sync halted. Review conflicts and resolve manually.`);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
if (strategy === "report-only") {
|
|
333
|
+
console.log(` \u2139\uFE0F ${conflicts.length} conflict(s) detected (report-only mode):`);
|
|
334
|
+
for (const conflict of conflicts) {
|
|
335
|
+
console.log(` - ${conflict.field}: local="${conflict.localValue}" vs remote="${conflict.remoteValue}"`);
|
|
336
|
+
}
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
301
339
|
for (const conflict of conflicts) {
|
|
302
|
-
if (
|
|
340
|
+
if (strategy === "remote-wins") {
|
|
303
341
|
console.log(` \u{1F504} Resolving: ${conflict.description} (Jira wins)`);
|
|
304
342
|
if (conflict.field === "title") {
|
|
305
343
|
await this.specManager.saveMetadata(spec.metadata.id, {
|
|
306
344
|
title: conflict.remoteValue
|
|
307
345
|
});
|
|
308
346
|
}
|
|
347
|
+
} else if (strategy === "local-wins") {
|
|
348
|
+
console.log(` \u{1F504} Resolving: ${conflict.description} (local wins \u2014 no remote update)`);
|
|
309
349
|
}
|
|
310
350
|
}
|
|
311
351
|
}
|
|
352
|
+
/**
|
|
353
|
+
* Write conflict report JSON file for detected conflicts.
|
|
354
|
+
*/
|
|
355
|
+
async writeConflictReport(specId, conflicts) {
|
|
356
|
+
try {
|
|
357
|
+
const report = {
|
|
358
|
+
specId,
|
|
359
|
+
provider: "jira",
|
|
360
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
361
|
+
conflicts: conflicts.map((c) => ({
|
|
362
|
+
field: c.field,
|
|
363
|
+
localValue: c.localValue,
|
|
364
|
+
remoteValue: c.remoteValue,
|
|
365
|
+
description: c.description
|
|
366
|
+
}))
|
|
367
|
+
};
|
|
368
|
+
const reportsDir = path.join(
|
|
369
|
+
this.specManager.projectRoot || process.cwd(),
|
|
370
|
+
".specweave",
|
|
371
|
+
"reports"
|
|
372
|
+
);
|
|
373
|
+
await fs.mkdir(reportsDir, { recursive: true });
|
|
374
|
+
const reportPath = path.join(reportsDir, "conflict-report.json");
|
|
375
|
+
await fs.writeFile(reportPath, JSON.stringify(report, null, 2));
|
|
376
|
+
console.log(` \u{1F4C4} Conflict report written to ${reportPath}`);
|
|
377
|
+
} catch (err) {
|
|
378
|
+
console.warn(" \u26A0\uFE0F Failed to write conflict report:", err.message);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
312
381
|
/**
|
|
313
382
|
* Fetch Jira Epic details
|
|
314
383
|
*/
|
|
@@ -329,14 +398,11 @@ ${acList}
|
|
|
329
398
|
*/
|
|
330
399
|
async findStoryByTitle(usId) {
|
|
331
400
|
const jql = `project = ${this.config.projectKey} AND summary ~ "[${usId}]" AND issuetype = Story`;
|
|
332
|
-
const
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
fields: "summary,description,status,labels"
|
|
337
|
-
}
|
|
401
|
+
const issues = await searchAllIssues(this.client, {
|
|
402
|
+
jql,
|
|
403
|
+
fields: "summary,description,status,labels",
|
|
404
|
+
maxResults: 1
|
|
338
405
|
});
|
|
339
|
-
const issues = response.data.issues;
|
|
340
406
|
return issues.length > 0 ? {
|
|
341
407
|
id: issues[0].id,
|
|
342
408
|
key: issues[0].key,
|
|
@@ -351,26 +417,31 @@ ${acList}
|
|
|
351
417
|
*/
|
|
352
418
|
async createStory(story) {
|
|
353
419
|
const issueType = this.mapTypeToJira(story.type, "Story");
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
}
|
|
420
|
+
const { field: epicField, style } = await getEpicLinkFieldForProject(
|
|
421
|
+
this.config.domain,
|
|
422
|
+
this.config.projectKey,
|
|
423
|
+
{ email: this.config.email, apiToken: this.config.apiToken }
|
|
424
|
+
);
|
|
425
|
+
const fields = {
|
|
426
|
+
project: {
|
|
427
|
+
key: this.config.projectKey
|
|
428
|
+
},
|
|
429
|
+
summary: story.summary,
|
|
430
|
+
description: story.description,
|
|
431
|
+
issuetype: {
|
|
432
|
+
name: issueType
|
|
433
|
+
},
|
|
434
|
+
labels: story.labels,
|
|
435
|
+
priority: {
|
|
436
|
+
name: this.mapPriorityToJira(story.priority)
|
|
372
437
|
}
|
|
373
438
|
};
|
|
439
|
+
if (style === "next-gen") {
|
|
440
|
+
fields.parent = { key: story.epicLink };
|
|
441
|
+
} else {
|
|
442
|
+
fields[epicField] = story.epicLink;
|
|
443
|
+
}
|
|
444
|
+
const payload = { fields };
|
|
374
445
|
const response = await this.client.post("/issue", payload);
|
|
375
446
|
const storyData = response.data;
|
|
376
447
|
return {
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
|
|
16
16
|
import { SpecMetadataManager } from '../../../src/core/specs/spec-metadata-manager.js';
|
|
17
17
|
import { SpecParser } from '../../../src/core/specs/spec-parser.js';
|
|
18
|
+
import * as fs from 'fs/promises';
|
|
19
|
+
import * as path from 'path';
|
|
18
20
|
import {
|
|
19
21
|
SpecContent,
|
|
20
22
|
UserStory,
|
|
@@ -22,13 +24,17 @@ import {
|
|
|
22
24
|
SpecSyncConflict
|
|
23
25
|
} from '../../../src/core/types/spec-metadata.js';
|
|
24
26
|
import { execFileNoThrow } from '../../../src/utils/execFileNoThrow.js';
|
|
27
|
+
import { detectDeploymentType, getApiBaseUrl } from './jira-deployment-detector.js';
|
|
28
|
+
import { toDescription, AdfDocument } from './content-format-adapter.js';
|
|
29
|
+
import { getEpicLinkFieldForProject } from './jira-field-discovery.js';
|
|
30
|
+
import { searchAllIssues } from './jira-paginated-search.js';
|
|
25
31
|
import axios, { AxiosInstance } from 'axios';
|
|
26
32
|
|
|
27
33
|
export interface JiraEpic {
|
|
28
34
|
id: string;
|
|
29
35
|
key: string; // e.g., SPEC-1
|
|
30
36
|
summary: string;
|
|
31
|
-
description: string;
|
|
37
|
+
description: string | AdfDocument;
|
|
32
38
|
status: {
|
|
33
39
|
name: string; // To Do, In Progress, Done
|
|
34
40
|
};
|
|
@@ -63,9 +69,9 @@ export class JiraSpecSync {
|
|
|
63
69
|
this.specManager = new SpecMetadataManager(projectRoot);
|
|
64
70
|
this.config = config;
|
|
65
71
|
|
|
66
|
-
// Create Jira API client
|
|
72
|
+
// Create Jira API client — baseURL set dynamically via init()
|
|
67
73
|
this.client = axios.create({
|
|
68
|
-
baseURL:
|
|
74
|
+
baseURL: getApiBaseUrl(config.domain),
|
|
69
75
|
auth: {
|
|
70
76
|
username: config.email,
|
|
71
77
|
password: config.apiToken
|
|
@@ -77,6 +83,17 @@ export class JiraSpecSync {
|
|
|
77
83
|
});
|
|
78
84
|
}
|
|
79
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Initialize: detect deployment type and update client baseURL
|
|
88
|
+
*/
|
|
89
|
+
async init(): Promise<void> {
|
|
90
|
+
const deployment = await detectDeploymentType(this.config.domain, {
|
|
91
|
+
email: this.config.email,
|
|
92
|
+
apiToken: this.config.apiToken,
|
|
93
|
+
});
|
|
94
|
+
this.client.defaults.baseURL = deployment.baseUrl;
|
|
95
|
+
}
|
|
96
|
+
|
|
80
97
|
/**
|
|
81
98
|
* Sync spec to Jira Epic (CREATE or UPDATE)
|
|
82
99
|
*/
|
|
@@ -191,7 +208,10 @@ export class JiraSpecSync {
|
|
|
191
208
|
|
|
192
209
|
console.log(`⚠️ Detected ${conflicts.length} conflict(s)`);
|
|
193
210
|
|
|
194
|
-
// 5.
|
|
211
|
+
// 5. Write conflict report
|
|
212
|
+
await this.writeConflictReport(specId, conflicts);
|
|
213
|
+
|
|
214
|
+
// 6. Resolve conflicts using configurable strategy (default: manual)
|
|
195
215
|
await this.resolveConflicts(spec, conflicts);
|
|
196
216
|
|
|
197
217
|
console.log('✅ Sync FROM Jira complete!');
|
|
@@ -221,7 +241,7 @@ export class JiraSpecSync {
|
|
|
221
241
|
*/
|
|
222
242
|
private async createJiraEpic(spec: SpecContent): Promise<JiraEpic> {
|
|
223
243
|
const epicSummary = `[${spec.metadata.id.toUpperCase()}] ${spec.metadata.title}`;
|
|
224
|
-
const epicDescription = this.generateEpicDescription(spec);
|
|
244
|
+
const epicDescription = toDescription(this.generateEpicDescription(spec), this.config.domain);
|
|
225
245
|
|
|
226
246
|
// Determine issue type based on spec type (supports Bug for bug-type specs)
|
|
227
247
|
const issueType = this.mapTypeToJira(spec.metadata.type, 'Epic');
|
|
@@ -230,7 +250,7 @@ export class JiraSpecSync {
|
|
|
230
250
|
fields: {
|
|
231
251
|
project: { key: string };
|
|
232
252
|
summary: string;
|
|
233
|
-
description:
|
|
253
|
+
description: any;
|
|
234
254
|
issuetype: { name: string };
|
|
235
255
|
labels: string[];
|
|
236
256
|
priority?: { name: string };
|
|
@@ -276,7 +296,7 @@ export class JiraSpecSync {
|
|
|
276
296
|
*/
|
|
277
297
|
private async updateJiraEpic(epicKey: string, spec: SpecContent): Promise<JiraEpic> {
|
|
278
298
|
const epicSummary = `[${spec.metadata.id.toUpperCase()}] ${spec.metadata.title}`;
|
|
279
|
-
const epicDescription = this.generateEpicDescription(spec);
|
|
299
|
+
const epicDescription = toDescription(this.generateEpicDescription(spec), this.config.domain);
|
|
280
300
|
|
|
281
301
|
const payload = {
|
|
282
302
|
fields: {
|
|
@@ -442,25 +462,85 @@ ${acList}
|
|
|
442
462
|
}
|
|
443
463
|
|
|
444
464
|
/**
|
|
445
|
-
* Resolve conflicts
|
|
465
|
+
* Resolve conflicts based on configurable strategy.
|
|
466
|
+
*
|
|
467
|
+
* Strategies:
|
|
468
|
+
* - 'manual' (default): Halt sync, report conflicts to user, no auto-resolve
|
|
469
|
+
* - 'remote-wins': Auto-resolve in favor of JIRA (remote)
|
|
470
|
+
* - 'local-wins': Auto-resolve in favor of spec (local)
|
|
471
|
+
* - 'report-only': Log conflicts, continue without resolving
|
|
446
472
|
*/
|
|
447
473
|
private async resolveConflicts(
|
|
448
474
|
spec: SpecContent,
|
|
449
|
-
conflicts: SpecSyncConflict[]
|
|
475
|
+
conflicts: SpecSyncConflict[],
|
|
476
|
+
strategy: 'manual' | 'remote-wins' | 'local-wins' | 'report-only' = 'manual'
|
|
450
477
|
): Promise<void> {
|
|
478
|
+
if (strategy === 'manual') {
|
|
479
|
+
console.log(` ⚠️ ${conflicts.length} conflict(s) require manual resolution.`);
|
|
480
|
+
for (const conflict of conflicts) {
|
|
481
|
+
console.log(` - ${conflict.field}: local="${conflict.localValue}" vs remote="${conflict.remoteValue}"`);
|
|
482
|
+
}
|
|
483
|
+
console.log(` Sync halted. Review conflicts and resolve manually.`);
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (strategy === 'report-only') {
|
|
488
|
+
console.log(` ℹ️ ${conflicts.length} conflict(s) detected (report-only mode):`);
|
|
489
|
+
for (const conflict of conflicts) {
|
|
490
|
+
console.log(` - ${conflict.field}: local="${conflict.localValue}" vs remote="${conflict.remoteValue}"`);
|
|
491
|
+
}
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
|
|
451
495
|
for (const conflict of conflicts) {
|
|
452
|
-
if (
|
|
496
|
+
if (strategy === 'remote-wins') {
|
|
453
497
|
console.log(` 🔄 Resolving: ${conflict.description} (Jira wins)`);
|
|
454
|
-
// Update spec metadata from Jira
|
|
455
498
|
if (conflict.field === 'title') {
|
|
456
499
|
await this.specManager.saveMetadata(spec.metadata.id, {
|
|
457
500
|
title: conflict.remoteValue
|
|
458
501
|
});
|
|
459
502
|
}
|
|
503
|
+
} else if (strategy === 'local-wins') {
|
|
504
|
+
console.log(` 🔄 Resolving: ${conflict.description} (local wins — no remote update)`);
|
|
505
|
+
// Local wins: keep spec as-is, no action needed
|
|
460
506
|
}
|
|
461
507
|
}
|
|
462
508
|
}
|
|
463
509
|
|
|
510
|
+
/**
|
|
511
|
+
* Write conflict report JSON file for detected conflicts.
|
|
512
|
+
*/
|
|
513
|
+
private async writeConflictReport(
|
|
514
|
+
specId: string,
|
|
515
|
+
conflicts: SpecSyncConflict[]
|
|
516
|
+
): Promise<void> {
|
|
517
|
+
try {
|
|
518
|
+
const report = {
|
|
519
|
+
specId,
|
|
520
|
+
provider: 'jira',
|
|
521
|
+
timestamp: new Date().toISOString(),
|
|
522
|
+
conflicts: conflicts.map((c) => ({
|
|
523
|
+
field: c.field,
|
|
524
|
+
localValue: c.localValue,
|
|
525
|
+
remoteValue: c.remoteValue,
|
|
526
|
+
description: c.description,
|
|
527
|
+
})),
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
const reportsDir = path.join(
|
|
531
|
+
(this.specManager as any).projectRoot || process.cwd(),
|
|
532
|
+
'.specweave',
|
|
533
|
+
'reports'
|
|
534
|
+
);
|
|
535
|
+
await fs.mkdir(reportsDir, { recursive: true });
|
|
536
|
+
const reportPath = path.join(reportsDir, 'conflict-report.json');
|
|
537
|
+
await fs.writeFile(reportPath, JSON.stringify(report, null, 2));
|
|
538
|
+
console.log(` 📄 Conflict report written to ${reportPath}`);
|
|
539
|
+
} catch (err) {
|
|
540
|
+
console.warn(' ⚠️ Failed to write conflict report:', (err as Error).message);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
464
544
|
/**
|
|
465
545
|
* Fetch Jira Epic details
|
|
466
546
|
*/
|
|
@@ -484,15 +564,12 @@ ${acList}
|
|
|
484
564
|
private async findStoryByTitle(usId: string): Promise<JiraStory | null> {
|
|
485
565
|
const jql = `project = ${this.config.projectKey} AND summary ~ "[${usId}]" AND issuetype = Story`;
|
|
486
566
|
|
|
487
|
-
const
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
fields: 'summary,description,status,labels'
|
|
492
|
-
}
|
|
567
|
+
const issues = await searchAllIssues(this.client, {
|
|
568
|
+
jql,
|
|
569
|
+
fields: 'summary,description,status,labels',
|
|
570
|
+
maxResults: 1,
|
|
493
571
|
});
|
|
494
572
|
|
|
495
|
-
const issues = response.data.issues;
|
|
496
573
|
return issues.length > 0 ? {
|
|
497
574
|
id: issues[0].id,
|
|
498
575
|
key: issues[0].key,
|
|
@@ -517,36 +594,37 @@ ${acList}
|
|
|
517
594
|
// Determine issue type (supports Bug for bug-type stories)
|
|
518
595
|
const issueType = this.mapTypeToJira(story.type, 'Story');
|
|
519
596
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
},
|
|
540
|
-
labels: story.labels,
|
|
541
|
-
// Link to epic (field name may vary by Jira configuration)
|
|
542
|
-
customfield_10014: story.epicLink, // Epic Link field (adjust if needed)
|
|
543
|
-
// Set native JIRA priority field
|
|
544
|
-
priority: {
|
|
545
|
-
name: this.mapPriorityToJira(story.priority)
|
|
546
|
-
}
|
|
597
|
+
// Discover epic link field dynamically based on project style
|
|
598
|
+
const { field: epicField, style } = await getEpicLinkFieldForProject(
|
|
599
|
+
this.config.domain,
|
|
600
|
+
this.config.projectKey,
|
|
601
|
+
{ email: this.config.email, apiToken: this.config.apiToken }
|
|
602
|
+
);
|
|
603
|
+
|
|
604
|
+
const fields: any = {
|
|
605
|
+
project: {
|
|
606
|
+
key: this.config.projectKey
|
|
607
|
+
},
|
|
608
|
+
summary: story.summary,
|
|
609
|
+
description: story.description,
|
|
610
|
+
issuetype: {
|
|
611
|
+
name: issueType
|
|
612
|
+
},
|
|
613
|
+
labels: story.labels,
|
|
614
|
+
priority: {
|
|
615
|
+
name: this.mapPriorityToJira(story.priority)
|
|
547
616
|
}
|
|
548
617
|
};
|
|
549
618
|
|
|
619
|
+
// Link to epic using the correct field for project style
|
|
620
|
+
if (style === 'next-gen') {
|
|
621
|
+
fields.parent = { key: story.epicLink };
|
|
622
|
+
} else {
|
|
623
|
+
fields[epicField] = story.epicLink;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
const payload = { fields };
|
|
627
|
+
|
|
550
628
|
const response = await this.client.post('/issue', payload);
|
|
551
629
|
const storyData = response.data;
|
|
552
630
|
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import axios from "axios";
|
|
2
|
+
import { detectDeploymentType, getApiBaseUrl } from "./jira-deployment-detector.js";
|
|
3
|
+
import { toCommentBody } from "./content-format-adapter.js";
|
|
2
4
|
class JiraStatusSync {
|
|
3
5
|
constructor(domain, email, apiToken, projectKey) {
|
|
4
6
|
this.domain = domain;
|
|
5
7
|
this.projectKey = projectKey;
|
|
6
8
|
this.client = axios.create({
|
|
7
|
-
baseURL:
|
|
9
|
+
baseURL: getApiBaseUrl(domain),
|
|
8
10
|
auth: {
|
|
9
11
|
username: email,
|
|
10
12
|
password: apiToken
|
|
@@ -15,6 +17,16 @@ class JiraStatusSync {
|
|
|
15
17
|
}
|
|
16
18
|
});
|
|
17
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Initialize: detect deployment type and update client baseURL
|
|
22
|
+
*/
|
|
23
|
+
async init() {
|
|
24
|
+
const deployment = await detectDeploymentType(this.domain, {
|
|
25
|
+
email: this.client.defaults.auth?.username || "",
|
|
26
|
+
apiToken: this.client.defaults.auth?.password || ""
|
|
27
|
+
});
|
|
28
|
+
this.client.defaults.baseURL = deployment.baseUrl;
|
|
29
|
+
}
|
|
18
30
|
/**
|
|
19
31
|
* Get current status from JIRA issue
|
|
20
32
|
*
|
|
@@ -67,14 +79,15 @@ class JiraStatusSync {
|
|
|
67
79
|
* @param newStatus - New SpecWeave status
|
|
68
80
|
*/
|
|
69
81
|
async postStatusComment(issueKey, oldStatus, newStatus) {
|
|
70
|
-
const
|
|
82
|
+
const rawBody = `*Status Update*
|
|
71
83
|
|
|
72
84
|
SpecWeave status changed:
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
85
|
+
* *From*: ${oldStatus}
|
|
86
|
+
* *To*: ${newStatus}
|
|
87
|
+
* *When*: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
76
88
|
|
|
77
89
|
_Synced from SpecWeave_`;
|
|
90
|
+
const body = toCommentBody(rawBody, this.domain);
|
|
78
91
|
await this.client.post(`/issue/${issueKey}/comment`, {
|
|
79
92
|
body
|
|
80
93
|
});
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import axios, { AxiosInstance } from 'axios';
|
|
15
|
+
import { detectDeploymentType, getApiBaseUrl } from './jira-deployment-detector.js';
|
|
16
|
+
import { toCommentBody } from './content-format-adapter.js';
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* External status representation (JIRA-specific)
|
|
@@ -51,9 +53,9 @@ export class JiraStatusSync {
|
|
|
51
53
|
this.domain = domain;
|
|
52
54
|
this.projectKey = projectKey;
|
|
53
55
|
|
|
54
|
-
// Create JIRA API client
|
|
56
|
+
// Create JIRA API client — baseURL set dynamically via init()
|
|
55
57
|
this.client = axios.create({
|
|
56
|
-
baseURL:
|
|
58
|
+
baseURL: getApiBaseUrl(domain),
|
|
57
59
|
auth: {
|
|
58
60
|
username: email,
|
|
59
61
|
password: apiToken
|
|
@@ -65,6 +67,17 @@ export class JiraStatusSync {
|
|
|
65
67
|
});
|
|
66
68
|
}
|
|
67
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Initialize: detect deployment type and update client baseURL
|
|
72
|
+
*/
|
|
73
|
+
async init(): Promise<void> {
|
|
74
|
+
const deployment = await detectDeploymentType(this.domain, {
|
|
75
|
+
email: this.client.defaults.auth?.username || '',
|
|
76
|
+
apiToken: this.client.defaults.auth?.password || '',
|
|
77
|
+
});
|
|
78
|
+
this.client.defaults.baseURL = deployment.baseUrl;
|
|
79
|
+
}
|
|
80
|
+
|
|
68
81
|
/**
|
|
69
82
|
* Get current status from JIRA issue
|
|
70
83
|
*
|
|
@@ -134,13 +147,15 @@ export class JiraStatusSync {
|
|
|
134
147
|
oldStatus: string,
|
|
135
148
|
newStatus: string
|
|
136
149
|
): Promise<void> {
|
|
137
|
-
const
|
|
150
|
+
const rawBody = `*Status Update*\n\n` +
|
|
138
151
|
`SpecWeave status changed:\n` +
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
152
|
+
`* *From*: ${oldStatus}\n` +
|
|
153
|
+
`* *To*: ${newStatus}\n` +
|
|
154
|
+
`* *When*: ${new Date().toISOString()}\n\n` +
|
|
142
155
|
`_Synced from SpecWeave_`;
|
|
143
156
|
|
|
157
|
+
const body = toCommentBody(rawBody, this.domain);
|
|
158
|
+
|
|
144
159
|
await this.client.post(`/issue/${issueKey}/comment`, {
|
|
145
160
|
body
|
|
146
161
|
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const CANONICAL_JIRA_KEY_PATH = "external_sync.jira.issueKey";
|
|
2
|
+
function readIssueKey(metadata) {
|
|
3
|
+
if (!metadata) return null;
|
|
4
|
+
const canonical = metadata?.external_sync?.jira?.issueKey;
|
|
5
|
+
if (canonical) return canonical;
|
|
6
|
+
const legacyIssue = metadata?.jira?.issue;
|
|
7
|
+
if (legacyIssue) return legacyIssue;
|
|
8
|
+
const legacyKey = metadata?.jira?.issueKey;
|
|
9
|
+
if (legacyKey) return legacyKey;
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
function writeIssueKey(metadata, key) {
|
|
13
|
+
if (!metadata) metadata = {};
|
|
14
|
+
if (!metadata.external_sync) metadata.external_sync = {};
|
|
15
|
+
if (!metadata.external_sync.jira) metadata.external_sync.jira = {};
|
|
16
|
+
metadata.external_sync.jira.issueKey = key;
|
|
17
|
+
return metadata;
|
|
18
|
+
}
|
|
19
|
+
function migrateToCanonical(metadata, cleanup = false) {
|
|
20
|
+
if (!metadata) return metadata;
|
|
21
|
+
const existingKey = readIssueKey(metadata);
|
|
22
|
+
if (!existingKey) return metadata;
|
|
23
|
+
writeIssueKey(metadata, existingKey);
|
|
24
|
+
if (cleanup) {
|
|
25
|
+
if (metadata.jira?.issue) delete metadata.jira.issue;
|
|
26
|
+
if (metadata.jira?.issueKey) delete metadata.jira.issueKey;
|
|
27
|
+
if (metadata.jira && Object.keys(metadata.jira).length === 0) {
|
|
28
|
+
delete metadata.jira;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return metadata;
|
|
32
|
+
}
|
|
33
|
+
export {
|
|
34
|
+
CANONICAL_JIRA_KEY_PATH,
|
|
35
|
+
migrateToCanonical,
|
|
36
|
+
readIssueKey,
|
|
37
|
+
writeIssueKey
|
|
38
|
+
};
|