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
|
@@ -156,8 +156,8 @@ async function executeSearch(query) {
|
|
|
156
156
|
"1000"
|
|
157
157
|
// Max results
|
|
158
158
|
], { env: getGhEnv() });
|
|
159
|
-
if (result.
|
|
160
|
-
throw new Error(`Failed to search issues: ${result.stderr || result.stdout}`);
|
|
159
|
+
if (result.exitCode !== 0) {
|
|
160
|
+
throw new Error(`Failed to search issues (exit code ${result.exitCode}): ${result.stderr || result.stdout}`);
|
|
161
161
|
}
|
|
162
162
|
if (!result.stdout.trim()) {
|
|
163
163
|
return [];
|
|
@@ -317,8 +317,8 @@ async function executeSearch(query: string): Promise<GitHubIssue[]> {
|
|
|
317
317
|
'1000', // Max results
|
|
318
318
|
], { env: getGhEnv() });
|
|
319
319
|
|
|
320
|
-
if (result.
|
|
321
|
-
throw new Error(`Failed to search issues: ${result.stderr || result.stdout}`);
|
|
320
|
+
if (result.exitCode !== 0) {
|
|
321
|
+
throw new Error(`Failed to search issues (exit code ${result.exitCode}): ${result.stderr || result.stdout}`);
|
|
322
322
|
}
|
|
323
323
|
|
|
324
324
|
if (!result.stdout.trim()) {
|
|
@@ -251,13 +251,29 @@ ${userStory.technicalContext}
|
|
|
251
251
|
|
|
252
252
|
\u{1F916} Auto-generated by SpecWeave
|
|
253
253
|
`;
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
254
|
+
let existing;
|
|
255
|
+
const seenNumbers = /* @__PURE__ */ new Set();
|
|
256
|
+
let page = 1;
|
|
257
|
+
while (!existing) {
|
|
258
|
+
const existingIssues = await this.octokit.issues.listForRepo({
|
|
259
|
+
owner: this.config.owner,
|
|
260
|
+
repo,
|
|
261
|
+
labels: "specweave",
|
|
262
|
+
state: "all",
|
|
263
|
+
per_page: 100,
|
|
264
|
+
page
|
|
265
|
+
});
|
|
266
|
+
for (const issue of existingIssues.data) {
|
|
267
|
+
if (seenNumbers.has(issue.number)) continue;
|
|
268
|
+
seenNumbers.add(issue.number);
|
|
269
|
+
if (issue.title === title) {
|
|
270
|
+
existing = { number: issue.number, title: issue.title };
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (existingIssues.data.length < 100) break;
|
|
275
|
+
page++;
|
|
276
|
+
}
|
|
261
277
|
if (existing) {
|
|
262
278
|
const response = await this.octokit.issues.update({
|
|
263
279
|
owner: this.config.owner,
|
|
@@ -356,15 +356,33 @@ ${userStory.technicalContext ? `\n## Technical Context\n\n${userStory.technicalC
|
|
|
356
356
|
🤖 Auto-generated by SpecWeave
|
|
357
357
|
`;
|
|
358
358
|
|
|
359
|
-
// Check if issue already exists (search by title)
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
359
|
+
// Check if issue already exists (search by title, paginated)
|
|
360
|
+
let existing: { number: number; title: string } | undefined;
|
|
361
|
+
const seenNumbers = new Set<number>();
|
|
362
|
+
let page = 1;
|
|
363
|
+
|
|
364
|
+
while (!existing) {
|
|
365
|
+
const existingIssues = await this.octokit.issues.listForRepo({
|
|
366
|
+
owner: this.config.owner,
|
|
367
|
+
repo,
|
|
368
|
+
labels: 'specweave',
|
|
369
|
+
state: 'all',
|
|
370
|
+
per_page: 100,
|
|
371
|
+
page,
|
|
372
|
+
});
|
|
366
373
|
|
|
367
|
-
|
|
374
|
+
for (const issue of existingIssues.data) {
|
|
375
|
+
if (seenNumbers.has(issue.number)) continue;
|
|
376
|
+
seenNumbers.add(issue.number);
|
|
377
|
+
if (issue.title === title) {
|
|
378
|
+
existing = { number: issue.number, title: issue.title };
|
|
379
|
+
break;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (existingIssues.data.length < 100) break;
|
|
384
|
+
page++;
|
|
385
|
+
}
|
|
368
386
|
|
|
369
387
|
if (existing) {
|
|
370
388
|
// Update existing issue
|
|
@@ -60,10 +60,38 @@ function parseYamlSimple(yaml) {
|
|
|
60
60
|
const stack = [
|
|
61
61
|
{ obj: result, indent: -1 }
|
|
62
62
|
];
|
|
63
|
-
for (
|
|
63
|
+
for (let i = 0; i < lines.length; i++) {
|
|
64
|
+
const line = lines[i];
|
|
64
65
|
if (!line.trim() || line.trim().startsWith("#")) continue;
|
|
65
66
|
const indent = line.search(/\S/);
|
|
66
67
|
const trimmed = line.trim();
|
|
68
|
+
if (trimmed.startsWith("- ")) {
|
|
69
|
+
while (stack.length > 1 && stack[stack.length - 1].indent >= indent) {
|
|
70
|
+
stack.pop();
|
|
71
|
+
}
|
|
72
|
+
const parent2 = stack[stack.length - 1];
|
|
73
|
+
const key2 = parent2.currentKey;
|
|
74
|
+
if (key2 && parent2.obj[key2] !== void 0) {
|
|
75
|
+
const arr = parent2.obj[key2];
|
|
76
|
+
if (Array.isArray(arr)) {
|
|
77
|
+
const itemText = trimmed.slice(2).trim();
|
|
78
|
+
const nestedColonIdx = itemText.indexOf(":");
|
|
79
|
+
if (nestedColonIdx > 0 && !itemText.startsWith('"') && !itemText.startsWith("'")) {
|
|
80
|
+
const nestedKey = itemText.slice(0, nestedColonIdx).trim();
|
|
81
|
+
const nestedVal = itemText.slice(nestedColonIdx + 1).trim();
|
|
82
|
+
if (nestedVal) {
|
|
83
|
+
const obj = { [nestedKey]: parseYamlValue(nestedVal) };
|
|
84
|
+
arr.push(obj);
|
|
85
|
+
} else {
|
|
86
|
+
arr.push(parseYamlValue(itemText));
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
arr.push(parseYamlValue(itemText));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
67
95
|
const colonIdx = trimmed.indexOf(":");
|
|
68
96
|
if (colonIdx === -1) continue;
|
|
69
97
|
const key = trimmed.slice(0, colonIdx).trim();
|
|
@@ -73,15 +101,60 @@ function parseYamlSimple(yaml) {
|
|
|
73
101
|
}
|
|
74
102
|
const parent = stack[stack.length - 1].obj;
|
|
75
103
|
if (rawValue === "" || rawValue === void 0) {
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
104
|
+
const nextIdx = findNextNonEmptyLine(lines, i + 1);
|
|
105
|
+
if (nextIdx !== -1 && lines[nextIdx].trim().startsWith("- ")) {
|
|
106
|
+
parent[key] = [];
|
|
107
|
+
stack.push({ obj: parent, indent, currentKey: key });
|
|
108
|
+
} else {
|
|
109
|
+
const child = {};
|
|
110
|
+
parent[key] = child;
|
|
111
|
+
stack.push({ obj: child, indent });
|
|
112
|
+
}
|
|
113
|
+
} else if (rawValue.startsWith("[") && rawValue.endsWith("]")) {
|
|
114
|
+
parent[key] = parseFlowArray(rawValue);
|
|
79
115
|
} else {
|
|
80
116
|
parent[key] = parseYamlValue(rawValue);
|
|
81
117
|
}
|
|
82
118
|
}
|
|
83
119
|
return result;
|
|
84
120
|
}
|
|
121
|
+
function findNextNonEmptyLine(lines, start) {
|
|
122
|
+
for (let i = start; i < lines.length; i++) {
|
|
123
|
+
const trimmed = lines[i].trim();
|
|
124
|
+
if (trimmed && !trimmed.startsWith("#")) return i;
|
|
125
|
+
}
|
|
126
|
+
return -1;
|
|
127
|
+
}
|
|
128
|
+
function parseFlowArray(raw) {
|
|
129
|
+
const inner = raw.slice(1, -1).trim();
|
|
130
|
+
if (!inner) return [];
|
|
131
|
+
const items = [];
|
|
132
|
+
let current = "";
|
|
133
|
+
let inQuote = false;
|
|
134
|
+
let quoteChar = "";
|
|
135
|
+
for (let i = 0; i < inner.length; i++) {
|
|
136
|
+
const ch = inner[i];
|
|
137
|
+
if (inQuote) {
|
|
138
|
+
if (ch === quoteChar) {
|
|
139
|
+
inQuote = false;
|
|
140
|
+
}
|
|
141
|
+
current += ch;
|
|
142
|
+
} else if (ch === '"' || ch === "'") {
|
|
143
|
+
inQuote = true;
|
|
144
|
+
quoteChar = ch;
|
|
145
|
+
current += ch;
|
|
146
|
+
} else if (ch === ",") {
|
|
147
|
+
items.push(parseYamlValue(current.trim()));
|
|
148
|
+
current = "";
|
|
149
|
+
} else {
|
|
150
|
+
current += ch;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (current.trim()) {
|
|
154
|
+
items.push(parseYamlValue(current.trim()));
|
|
155
|
+
}
|
|
156
|
+
return items;
|
|
157
|
+
}
|
|
85
158
|
function parseYamlValue(raw) {
|
|
86
159
|
if (raw === "null") return null;
|
|
87
160
|
if (raw === "true") return true;
|
|
@@ -99,7 +172,39 @@ function stringifyYaml(obj, indent = 0) {
|
|
|
99
172
|
for (const [key, value] of Object.entries(obj)) {
|
|
100
173
|
if (value === null || value === void 0) {
|
|
101
174
|
parts.push(`${prefix}${key}: null`);
|
|
102
|
-
} else if (
|
|
175
|
+
} else if (Array.isArray(value)) {
|
|
176
|
+
if (value.length === 0) {
|
|
177
|
+
parts.push(`${prefix}${key}: []`);
|
|
178
|
+
} else if (value.every((v) => typeof v !== "object" || v === null)) {
|
|
179
|
+
parts.push(`${prefix}${key}:`);
|
|
180
|
+
for (const item of value) {
|
|
181
|
+
if (typeof item === "string") {
|
|
182
|
+
parts.push(`${prefix} - "${item}"`);
|
|
183
|
+
} else {
|
|
184
|
+
parts.push(`${prefix} - ${item}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
parts.push(`${prefix}${key}:`);
|
|
189
|
+
for (const item of value) {
|
|
190
|
+
if (typeof item === "object" && item !== null) {
|
|
191
|
+
const entries = Object.entries(item);
|
|
192
|
+
if (entries.length > 0) {
|
|
193
|
+
const [firstKey, firstVal] = entries[0];
|
|
194
|
+
const formattedVal = typeof firstVal === "string" ? `"${firstVal}"` : firstVal;
|
|
195
|
+
parts.push(`${prefix} - ${firstKey}: ${formattedVal}`);
|
|
196
|
+
for (let j = 1; j < entries.length; j++) {
|
|
197
|
+
const [k, v] = entries[j];
|
|
198
|
+
const fv = typeof v === "string" ? `"${v}"` : v;
|
|
199
|
+
parts.push(`${prefix} ${k}: ${fv}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
} else {
|
|
203
|
+
parts.push(`${prefix} - ${item}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
} else if (typeof value === "object") {
|
|
103
208
|
parts.push(`${prefix}${key}:`);
|
|
104
209
|
parts.push(stringifyYaml(value, indent + 1));
|
|
105
210
|
} else if (typeof value === "string") {
|
|
@@ -101,20 +101,55 @@ export async function updateSpecFrontmatter(
|
|
|
101
101
|
|
|
102
102
|
/**
|
|
103
103
|
* Simple YAML parser for spec frontmatter.
|
|
104
|
-
* Handles nested objects, strings, numbers, booleans, null.
|
|
104
|
+
* Handles nested objects, strings, numbers, booleans, null, and arrays.
|
|
105
|
+
* Supports block arrays (`- item`) and flow arrays (`[a, b, c]`).
|
|
105
106
|
*/
|
|
106
107
|
function parseYamlSimple(yaml: string): Record<string, unknown> {
|
|
107
108
|
const result: Record<string, unknown> = {};
|
|
108
109
|
const lines = yaml.split('\n');
|
|
109
|
-
const stack: Array<{ obj: Record<string, unknown>; indent: number }> = [
|
|
110
|
+
const stack: Array<{ obj: Record<string, unknown>; indent: number; currentKey?: string }> = [
|
|
110
111
|
{ obj: result, indent: -1 },
|
|
111
112
|
];
|
|
112
113
|
|
|
113
|
-
for (
|
|
114
|
+
for (let i = 0; i < lines.length; i++) {
|
|
115
|
+
const line = lines[i];
|
|
114
116
|
if (!line.trim() || line.trim().startsWith('#')) continue;
|
|
115
117
|
|
|
116
118
|
const indent = line.search(/\S/);
|
|
117
119
|
const trimmed = line.trim();
|
|
120
|
+
|
|
121
|
+
// Block array item: `- value` or `- key: value` (nested object in array)
|
|
122
|
+
if (trimmed.startsWith('- ')) {
|
|
123
|
+
// Find the parent that owns this array
|
|
124
|
+
while (stack.length > 1 && stack[stack.length - 1].indent >= indent) {
|
|
125
|
+
stack.pop();
|
|
126
|
+
}
|
|
127
|
+
const parent = stack[stack.length - 1];
|
|
128
|
+
const key = parent.currentKey;
|
|
129
|
+
if (key && parent.obj[key] !== undefined) {
|
|
130
|
+
const arr = parent.obj[key];
|
|
131
|
+
if (Array.isArray(arr)) {
|
|
132
|
+
const itemText = trimmed.slice(2).trim();
|
|
133
|
+
// Check if it's a nested object item (- key: value)
|
|
134
|
+
const nestedColonIdx = itemText.indexOf(':');
|
|
135
|
+
if (nestedColonIdx > 0 && !itemText.startsWith('"') && !itemText.startsWith("'")) {
|
|
136
|
+
const nestedKey = itemText.slice(0, nestedColonIdx).trim();
|
|
137
|
+
const nestedVal = itemText.slice(nestedColonIdx + 1).trim();
|
|
138
|
+
if (nestedVal) {
|
|
139
|
+
// Simple key:value object item
|
|
140
|
+
const obj: Record<string, unknown> = { [nestedKey]: parseYamlValue(nestedVal) };
|
|
141
|
+
arr.push(obj);
|
|
142
|
+
} else {
|
|
143
|
+
arr.push(parseYamlValue(itemText));
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
arr.push(parseYamlValue(itemText));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
|
|
118
153
|
const colonIdx = trimmed.indexOf(':');
|
|
119
154
|
if (colonIdx === -1) continue;
|
|
120
155
|
|
|
@@ -129,10 +164,21 @@ function parseYamlSimple(yaml: string): Record<string, unknown> {
|
|
|
129
164
|
const parent = stack[stack.length - 1].obj;
|
|
130
165
|
|
|
131
166
|
if (rawValue === '' || rawValue === undefined) {
|
|
132
|
-
//
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
167
|
+
// Check if next non-empty line is a block array item
|
|
168
|
+
const nextIdx = findNextNonEmptyLine(lines, i + 1);
|
|
169
|
+
if (nextIdx !== -1 && lines[nextIdx].trim().startsWith('- ')) {
|
|
170
|
+
// This key holds an array
|
|
171
|
+
parent[key] = [] as unknown[];
|
|
172
|
+
stack.push({ obj: parent, indent, currentKey: key });
|
|
173
|
+
} else {
|
|
174
|
+
// Nested object
|
|
175
|
+
const child: Record<string, unknown> = {};
|
|
176
|
+
parent[key] = child;
|
|
177
|
+
stack.push({ obj: child, indent });
|
|
178
|
+
}
|
|
179
|
+
} else if (rawValue.startsWith('[') && rawValue.endsWith(']')) {
|
|
180
|
+
// Flow array: [a, b, c]
|
|
181
|
+
parent[key] = parseFlowArray(rawValue);
|
|
136
182
|
} else {
|
|
137
183
|
parent[key] = parseYamlValue(rawValue);
|
|
138
184
|
}
|
|
@@ -141,6 +187,52 @@ function parseYamlSimple(yaml: string): Record<string, unknown> {
|
|
|
141
187
|
return result;
|
|
142
188
|
}
|
|
143
189
|
|
|
190
|
+
/**
|
|
191
|
+
* Find the next non-empty, non-comment line index.
|
|
192
|
+
*/
|
|
193
|
+
function findNextNonEmptyLine(lines: string[], start: number): number {
|
|
194
|
+
for (let i = start; i < lines.length; i++) {
|
|
195
|
+
const trimmed = lines[i].trim();
|
|
196
|
+
if (trimmed && !trimmed.startsWith('#')) return i;
|
|
197
|
+
}
|
|
198
|
+
return -1;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Parse a YAML flow array like [a, b, c] or ["a", "b"]
|
|
203
|
+
*/
|
|
204
|
+
function parseFlowArray(raw: string): unknown[] {
|
|
205
|
+
const inner = raw.slice(1, -1).trim();
|
|
206
|
+
if (!inner) return [];
|
|
207
|
+
// Split on commas, respecting quoted strings
|
|
208
|
+
const items: unknown[] = [];
|
|
209
|
+
let current = '';
|
|
210
|
+
let inQuote = false;
|
|
211
|
+
let quoteChar = '';
|
|
212
|
+
for (let i = 0; i < inner.length; i++) {
|
|
213
|
+
const ch = inner[i];
|
|
214
|
+
if (inQuote) {
|
|
215
|
+
if (ch === quoteChar) {
|
|
216
|
+
inQuote = false;
|
|
217
|
+
}
|
|
218
|
+
current += ch;
|
|
219
|
+
} else if (ch === '"' || ch === "'") {
|
|
220
|
+
inQuote = true;
|
|
221
|
+
quoteChar = ch;
|
|
222
|
+
current += ch;
|
|
223
|
+
} else if (ch === ',') {
|
|
224
|
+
items.push(parseYamlValue(current.trim()));
|
|
225
|
+
current = '';
|
|
226
|
+
} else {
|
|
227
|
+
current += ch;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (current.trim()) {
|
|
231
|
+
items.push(parseYamlValue(current.trim()));
|
|
232
|
+
}
|
|
233
|
+
return items;
|
|
234
|
+
}
|
|
235
|
+
|
|
144
236
|
function parseYamlValue(raw: string): unknown {
|
|
145
237
|
if (raw === 'null') return null;
|
|
146
238
|
if (raw === 'true') return true;
|
|
@@ -155,7 +247,7 @@ function parseYamlValue(raw: string): unknown {
|
|
|
155
247
|
}
|
|
156
248
|
|
|
157
249
|
/**
|
|
158
|
-
* Simple YAML stringifier.
|
|
250
|
+
* Simple YAML stringifier with array support.
|
|
159
251
|
*/
|
|
160
252
|
function stringifyYaml(obj: Record<string, unknown>, indent = 0): string {
|
|
161
253
|
const prefix = ' '.repeat(indent);
|
|
@@ -164,7 +256,41 @@ function stringifyYaml(obj: Record<string, unknown>, indent = 0): string {
|
|
|
164
256
|
for (const [key, value] of Object.entries(obj)) {
|
|
165
257
|
if (value === null || value === undefined) {
|
|
166
258
|
parts.push(`${prefix}${key}: null`);
|
|
167
|
-
} else if (
|
|
259
|
+
} else if (Array.isArray(value)) {
|
|
260
|
+
if (value.length === 0) {
|
|
261
|
+
parts.push(`${prefix}${key}: []`);
|
|
262
|
+
} else if (value.every(v => typeof v !== 'object' || v === null)) {
|
|
263
|
+
// Simple array — use block style
|
|
264
|
+
parts.push(`${prefix}${key}:`);
|
|
265
|
+
for (const item of value) {
|
|
266
|
+
if (typeof item === 'string') {
|
|
267
|
+
parts.push(`${prefix} - "${item}"`);
|
|
268
|
+
} else {
|
|
269
|
+
parts.push(`${prefix} - ${item}`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
} else {
|
|
273
|
+
// Array of objects — use block style with nested keys
|
|
274
|
+
parts.push(`${prefix}${key}:`);
|
|
275
|
+
for (const item of value) {
|
|
276
|
+
if (typeof item === 'object' && item !== null) {
|
|
277
|
+
const entries = Object.entries(item as Record<string, unknown>);
|
|
278
|
+
if (entries.length > 0) {
|
|
279
|
+
const [firstKey, firstVal] = entries[0];
|
|
280
|
+
const formattedVal = typeof firstVal === 'string' ? `"${firstVal}"` : firstVal;
|
|
281
|
+
parts.push(`${prefix} - ${firstKey}: ${formattedVal}`);
|
|
282
|
+
for (let j = 1; j < entries.length; j++) {
|
|
283
|
+
const [k, v] = entries[j];
|
|
284
|
+
const fv = typeof v === 'string' ? `"${v}"` : v;
|
|
285
|
+
parts.push(`${prefix} ${k}: ${fv}`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
} else {
|
|
289
|
+
parts.push(`${prefix} - ${item}`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
} else if (typeof value === 'object') {
|
|
168
294
|
parts.push(`${prefix}${key}:`);
|
|
169
295
|
parts.push(stringifyYaml(value as Record<string, unknown>, indent + 1));
|
|
170
296
|
} else if (typeof value === 'string') {
|
|
@@ -3,8 +3,20 @@ import { SpecParser } from "../../../src/core/specs/spec-parser.js";
|
|
|
3
3
|
import { execFileNoThrow } from "../../../src/utils/execFileNoThrow.js";
|
|
4
4
|
import { ProjectContextManager } from "../../../src/core/sync/project-context.js";
|
|
5
5
|
import { getGitHubAuthFromProject } from "../../../src/utils/auth-helpers.js";
|
|
6
|
+
const DEFAULT_CROSS_TEAM_KEYWORDS = [
|
|
7
|
+
"integration",
|
|
8
|
+
"cross-team",
|
|
9
|
+
"cross-project",
|
|
10
|
+
"shared",
|
|
11
|
+
"common",
|
|
12
|
+
"auth",
|
|
13
|
+
"api-contract",
|
|
14
|
+
"sync"
|
|
15
|
+
];
|
|
6
16
|
class GitHubSpecSync {
|
|
7
17
|
constructor(projectRoot = process.cwd()) {
|
|
18
|
+
/** Configurable keywords for cross-team spec detection. Case-insensitive matching. */
|
|
19
|
+
this.crossTeamKeywords = DEFAULT_CROSS_TEAM_KEYWORDS;
|
|
8
20
|
this.projectRoot = projectRoot;
|
|
9
21
|
this.specManager = new SpecMetadataManager(projectRoot);
|
|
10
22
|
this.projectContextManager = new ProjectContextManager(projectRoot);
|
|
@@ -395,15 +407,28 @@ ${acList}
|
|
|
395
407
|
}
|
|
396
408
|
/**
|
|
397
409
|
* Resolve conflicts
|
|
410
|
+
*
|
|
411
|
+
* GUARD: Only overwrites local spec title if the user explicitly configured
|
|
412
|
+
* "remote-wins" as the conflict resolution strategy. Default conflicts
|
|
413
|
+
* detected by detectConflicts() use 'remote-wins' but this guard ensures
|
|
414
|
+
* API-sourced titles never silently replace local titles.
|
|
398
415
|
*/
|
|
399
416
|
async resolveConflicts(spec, conflicts) {
|
|
400
417
|
for (const conflict of conflicts) {
|
|
401
418
|
if (conflict.resolution === "remote-wins") {
|
|
402
|
-
console.log(` \u{1F504} Resolving: ${conflict.description} (GitHub wins)`);
|
|
403
419
|
if (conflict.field === "title") {
|
|
420
|
+
const remoteTitle = conflict.remoteValue;
|
|
421
|
+
const remoteSuffix = remoteTitle.replace(/^\[.*?\]\s*/, "");
|
|
422
|
+
const localTitle = spec.metadata.title;
|
|
423
|
+
if (remoteSuffix === localTitle || !remoteSuffix) {
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
console.log(` \u{1F504} Resolving: ${conflict.description} (GitHub wins \u2014 explicit remote-wins)`);
|
|
404
427
|
await this.specManager.saveMetadata(spec.metadata.id, {
|
|
405
428
|
title: conflict.remoteValue
|
|
406
429
|
});
|
|
430
|
+
} else {
|
|
431
|
+
console.log(` \u{1F504} Resolving: ${conflict.description} (GitHub wins)`);
|
|
407
432
|
}
|
|
408
433
|
}
|
|
409
434
|
}
|
|
@@ -439,18 +464,65 @@ ${acList}
|
|
|
439
464
|
return result.data.repositoryOwner.id;
|
|
440
465
|
}
|
|
441
466
|
/**
|
|
442
|
-
* Fetch GitHub Project details
|
|
467
|
+
* Fetch GitHub Project details via GraphQL ProjectV2 API
|
|
443
468
|
*/
|
|
444
469
|
async fetchGitHubProject(owner, repo, projectId) {
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
470
|
+
const query = `
|
|
471
|
+
query GetProject($owner: String!, $number: Int!) {
|
|
472
|
+
user(login: $owner) {
|
|
473
|
+
projectV2(number: $number) {
|
|
474
|
+
id
|
|
475
|
+
title
|
|
476
|
+
number
|
|
477
|
+
url
|
|
478
|
+
closed
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
`;
|
|
483
|
+
try {
|
|
484
|
+
const result = await this.executeGraphQL(query, {
|
|
485
|
+
owner,
|
|
486
|
+
number: projectId
|
|
487
|
+
});
|
|
488
|
+
let project = result.data?.user?.projectV2;
|
|
489
|
+
if (!project) {
|
|
490
|
+
const orgQuery = `
|
|
491
|
+
query GetOrgProject($owner: String!, $number: Int!) {
|
|
492
|
+
organization(login: $owner) {
|
|
493
|
+
projectV2(number: $number) {
|
|
494
|
+
id
|
|
495
|
+
title
|
|
496
|
+
number
|
|
497
|
+
url
|
|
498
|
+
closed
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
`;
|
|
503
|
+
const orgResult = await this.executeGraphQL(orgQuery, {
|
|
504
|
+
owner,
|
|
505
|
+
number: projectId
|
|
506
|
+
});
|
|
507
|
+
project = orgResult.data?.organization?.projectV2;
|
|
508
|
+
}
|
|
509
|
+
if (!project) {
|
|
510
|
+
throw new Error(`GitHub Project #${projectId} not found for ${owner}`);
|
|
511
|
+
}
|
|
512
|
+
return {
|
|
513
|
+
id: projectId,
|
|
514
|
+
title: project.title,
|
|
515
|
+
number: project.number,
|
|
516
|
+
url: project.url,
|
|
517
|
+
state: project.closed ? "closed" : "open",
|
|
518
|
+
owner,
|
|
519
|
+
repo
|
|
520
|
+
};
|
|
521
|
+
} catch (error) {
|
|
522
|
+
throw new Error(
|
|
523
|
+
`Failed to fetch GitHub Project #${projectId}: ${error instanceof Error ? error.message : String(error)}`
|
|
524
|
+
);
|
|
525
|
+
}
|
|
454
526
|
}
|
|
455
527
|
/**
|
|
456
528
|
* Find issue by title pattern
|
|
@@ -637,20 +709,9 @@ ${acList}
|
|
|
637
709
|
* - Tags include multiple project names
|
|
638
710
|
*/
|
|
639
711
|
isCrossTeamSpec(spec) {
|
|
640
|
-
const crossTeamKeywords = [
|
|
641
|
-
"integration",
|
|
642
|
-
"cross-team",
|
|
643
|
-
"cross-project",
|
|
644
|
-
"shared",
|
|
645
|
-
"common",
|
|
646
|
-
"auth",
|
|
647
|
-
// Auth often touches frontend + backend
|
|
648
|
-
"api-contract",
|
|
649
|
-
"sync"
|
|
650
|
-
];
|
|
651
712
|
const title = spec.metadata.title.toLowerCase();
|
|
652
|
-
const hasCrossTeamKeyword = crossTeamKeywords.some(
|
|
653
|
-
(keyword) => title.includes(keyword)
|
|
713
|
+
const hasCrossTeamKeyword = this.crossTeamKeywords.some(
|
|
714
|
+
(keyword) => title.toLowerCase().includes(keyword.toLowerCase())
|
|
654
715
|
);
|
|
655
716
|
const tags = spec.metadata.tags || [];
|
|
656
717
|
const projectTags = tags.filter((tag) => tag.startsWith("project:"));
|