specweave 1.0.447 → 1.0.450

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.
Files changed (106) hide show
  1. package/bin/specweave.js +18 -0
  2. package/dist/plugins/specweave-ado/lib/ado-client.d.ts +6 -0
  3. package/dist/plugins/specweave-ado/lib/ado-client.d.ts.map +1 -1
  4. package/dist/plugins/specweave-ado/lib/ado-client.js +22 -0
  5. package/dist/plugins/specweave-ado/lib/ado-client.js.map +1 -1
  6. package/dist/src/adapters/adapter-base.d.ts +15 -0
  7. package/dist/src/adapters/adapter-base.d.ts.map +1 -1
  8. package/dist/src/adapters/adapter-base.js +56 -0
  9. package/dist/src/adapters/adapter-base.js.map +1 -1
  10. package/dist/src/adapters/adapter-loader.d.ts.map +1 -1
  11. package/dist/src/adapters/adapter-loader.js +20 -2
  12. package/dist/src/adapters/adapter-loader.js.map +1 -1
  13. package/dist/src/adapters/aider/adapter.d.ts +21 -0
  14. package/dist/src/adapters/aider/adapter.d.ts.map +1 -0
  15. package/dist/src/adapters/aider/adapter.js +57 -0
  16. package/dist/src/adapters/aider/adapter.js.map +1 -0
  17. package/dist/src/adapters/amazonq/adapter.d.ts +21 -0
  18. package/dist/src/adapters/amazonq/adapter.d.ts.map +1 -0
  19. package/dist/src/adapters/amazonq/adapter.js +57 -0
  20. package/dist/src/adapters/amazonq/adapter.js.map +1 -0
  21. package/dist/src/adapters/cline/adapter.d.ts +21 -0
  22. package/dist/src/adapters/cline/adapter.d.ts.map +1 -0
  23. package/dist/src/adapters/cline/adapter.js +57 -0
  24. package/dist/src/adapters/cline/adapter.js.map +1 -0
  25. package/dist/src/adapters/codex/adapter.d.ts +18 -0
  26. package/dist/src/adapters/codex/adapter.d.ts.map +1 -1
  27. package/dist/src/adapters/codex/adapter.js +32 -0
  28. package/dist/src/adapters/codex/adapter.js.map +1 -1
  29. package/dist/src/adapters/continue/adapter.d.ts +63 -0
  30. package/dist/src/adapters/continue/adapter.d.ts.map +1 -0
  31. package/dist/src/adapters/continue/adapter.js +103 -0
  32. package/dist/src/adapters/continue/adapter.js.map +1 -0
  33. package/dist/src/adapters/copilot/adapter.d.ts +65 -0
  34. package/dist/src/adapters/copilot/adapter.d.ts.map +1 -0
  35. package/dist/src/adapters/copilot/adapter.js +106 -0
  36. package/dist/src/adapters/copilot/adapter.js.map +1 -0
  37. package/dist/src/adapters/cursor/adapter.d.ts +4 -20
  38. package/dist/src/adapters/cursor/adapter.d.ts.map +1 -1
  39. package/dist/src/adapters/cursor/adapter.js +12 -129
  40. package/dist/src/adapters/cursor/adapter.js.map +1 -1
  41. package/dist/src/adapters/gemini/adapter.d.ts +18 -0
  42. package/dist/src/adapters/gemini/adapter.d.ts.map +1 -1
  43. package/dist/src/adapters/gemini/adapter.js +32 -0
  44. package/dist/src/adapters/gemini/adapter.js.map +1 -1
  45. package/dist/src/adapters/jetbrains/adapter.d.ts +21 -0
  46. package/dist/src/adapters/jetbrains/adapter.d.ts.map +1 -0
  47. package/dist/src/adapters/jetbrains/adapter.js +57 -0
  48. package/dist/src/adapters/jetbrains/adapter.js.map +1 -0
  49. package/dist/src/adapters/registry.yaml +166 -20
  50. package/dist/src/adapters/tabnine/adapter.d.ts +21 -0
  51. package/dist/src/adapters/tabnine/adapter.d.ts.map +1 -0
  52. package/dist/src/adapters/tabnine/adapter.js +57 -0
  53. package/dist/src/adapters/tabnine/adapter.js.map +1 -0
  54. package/dist/src/adapters/windsurf/adapter.d.ts +64 -0
  55. package/dist/src/adapters/windsurf/adapter.d.ts.map +1 -0
  56. package/dist/src/adapters/windsurf/adapter.js +106 -0
  57. package/dist/src/adapters/windsurf/adapter.js.map +1 -0
  58. package/dist/src/adapters/zed/adapter.d.ts +21 -0
  59. package/dist/src/adapters/zed/adapter.d.ts.map +1 -0
  60. package/dist/src/adapters/zed/adapter.js +57 -0
  61. package/dist/src/adapters/zed/adapter.js.map +1 -0
  62. package/dist/src/cli/commands/get.d.ts +17 -1
  63. package/dist/src/cli/commands/get.d.ts.map +1 -1
  64. package/dist/src/cli/commands/get.js +51 -1
  65. package/dist/src/cli/commands/get.js.map +1 -1
  66. package/dist/src/cli/commands/link-pr.d.ts +17 -0
  67. package/dist/src/cli/commands/link-pr.d.ts.map +1 -0
  68. package/dist/src/cli/commands/link-pr.js +45 -0
  69. package/dist/src/cli/commands/link-pr.js.map +1 -0
  70. package/dist/src/cli/helpers/get/bulk-get.d.ts +41 -0
  71. package/dist/src/cli/helpers/get/bulk-get.d.ts.map +1 -0
  72. package/dist/src/cli/helpers/get/bulk-get.js +88 -0
  73. package/dist/src/cli/helpers/get/bulk-get.js.map +1 -0
  74. package/dist/src/cli/helpers/init/github-repo-cloning.d.ts +50 -0
  75. package/dist/src/cli/helpers/init/github-repo-cloning.d.ts.map +1 -1
  76. package/dist/src/cli/helpers/init/github-repo-cloning.js +1 -1
  77. package/dist/src/cli/helpers/init/github-repo-cloning.js.map +1 -1
  78. package/dist/src/core/skills/skill-judge.d.ts.map +1 -1
  79. package/dist/src/core/skills/skill-judge.js +2 -1
  80. package/dist/src/core/skills/skill-judge.js.map +1 -1
  81. package/dist/src/integrations/jira/jira-client.d.ts +12 -0
  82. package/dist/src/integrations/jira/jira-client.d.ts.map +1 -1
  83. package/dist/src/integrations/jira/jira-client.js +37 -0
  84. package/dist/src/integrations/jira/jira-client.js.map +1 -1
  85. package/dist/src/metrics/dora-calculator.d.ts.map +1 -1
  86. package/dist/src/metrics/dora-calculator.js +3 -2
  87. package/dist/src/metrics/dora-calculator.js.map +1 -1
  88. package/dist/src/sync/format-preservation-sync.d.ts +3 -0
  89. package/dist/src/sync/format-preservation-sync.d.ts.map +1 -1
  90. package/dist/src/sync/format-preservation-sync.js +9 -0
  91. package/dist/src/sync/format-preservation-sync.js.map +1 -1
  92. package/dist/src/sync/integration-health-check.d.ts +45 -0
  93. package/dist/src/sync/integration-health-check.d.ts.map +1 -0
  94. package/dist/src/sync/integration-health-check.js +186 -0
  95. package/dist/src/sync/integration-health-check.js.map +1 -0
  96. package/dist/src/sync/pr-linker.d.ts +38 -0
  97. package/dist/src/sync/pr-linker.d.ts.map +1 -0
  98. package/dist/src/sync/pr-linker.js +157 -0
  99. package/dist/src/sync/pr-linker.js.map +1 -0
  100. package/package.json +1 -1
  101. package/plugins/specweave/commands/sync-setup.md +24 -1
  102. package/plugins/specweave/skills/get/SKILL.md +38 -6
  103. package/plugins/specweave/skills/pr/SKILL.md +39 -2
  104. package/plugins/specweave-ado/lib/ado-client.js +22 -0
  105. package/plugins/specweave-ado/lib/ado-client.ts +25 -0
  106. package/src/templates/AGENTS.md.template +19 -5
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Integration Health Check
3
+ *
4
+ * Validates external integration chain beyond simple auth.
5
+ * Runs during sync-setup to catch misconfigurations early.
6
+ *
7
+ * @module sync/integration-health-check
8
+ */
9
+ /**
10
+ * Run health checks for JIRA integration
11
+ */
12
+ export async function checkJiraIntegration(config) {
13
+ const checks = [];
14
+ const authHeader = 'Basic ' + Buffer.from(`${config.email}:${config.apiToken}`).toString('base64');
15
+ const baseUrl = config.domain.startsWith('http') ? config.domain : `https://${config.domain}`;
16
+ // Check 1: Verify API auth (basic)
17
+ try {
18
+ const res = await fetch(`${baseUrl}/rest/api/3/myself`, {
19
+ headers: { 'Authorization': authHeader }
20
+ });
21
+ if (res.ok) {
22
+ checks.push({ name: 'API Authentication', status: 'pass', message: 'Authenticated successfully' });
23
+ }
24
+ else {
25
+ checks.push({ name: 'API Authentication', status: 'fail', message: `Auth failed: ${res.status}`, fix: 'Check JIRA_EMAIL and JIRA_API_TOKEN in .env' });
26
+ }
27
+ }
28
+ catch (err) {
29
+ checks.push({ name: 'API Authentication', status: 'fail', message: err.message, fix: 'Check JIRA_DOMAIN in .env' });
30
+ }
31
+ // Check 2: Verify project exists and has valid issue types
32
+ try {
33
+ const res = await fetch(`${baseUrl}/rest/api/3/project/${config.projectKey}`, {
34
+ headers: { 'Authorization': authHeader }
35
+ });
36
+ if (res.ok) {
37
+ checks.push({ name: 'Project Access', status: 'pass', message: `Project ${config.projectKey} accessible` });
38
+ }
39
+ else {
40
+ checks.push({ name: 'Project Access', status: 'fail', message: `Project ${config.projectKey} not found or no access`, fix: `Verify JIRA project key: ${config.projectKey}` });
41
+ }
42
+ }
43
+ catch (err) {
44
+ checks.push({ name: 'Project Access', status: 'fail', message: err.message });
45
+ }
46
+ // Check 3: Verify issue types exist in project (catches "The issue type selected is invalid")
47
+ try {
48
+ const res = await fetch(`${baseUrl}/rest/api/3/issue/createmeta?projectKeys=${config.projectKey}&expand=projects.issuetypes`, {
49
+ headers: { 'Authorization': authHeader }
50
+ });
51
+ if (res.ok) {
52
+ const data = await res.json();
53
+ const project = data.projects?.[0];
54
+ if (project) {
55
+ const typeNames = (project.issuetypes || []).map((t) => t.name);
56
+ const hasEpic = typeNames.some((n) => n.toLowerCase().includes('epic'));
57
+ const hasStory = typeNames.some((n) => n.toLowerCase().includes('story'));
58
+ if (hasEpic && hasStory) {
59
+ checks.push({ name: 'Issue Types', status: 'pass', message: `Epic and Story types available (${typeNames.join(', ')})` });
60
+ }
61
+ else {
62
+ const missing = [];
63
+ if (!hasEpic)
64
+ missing.push('Epic');
65
+ if (!hasStory)
66
+ missing.push('Story');
67
+ checks.push({
68
+ name: 'Issue Types',
69
+ status: 'warn',
70
+ message: `Missing issue types: ${missing.join(', ')}. Available: ${typeNames.join(', ')}`,
71
+ fix: 'Add missing issue types in JIRA project settings, or SpecWeave sync will fail at increment closure'
72
+ });
73
+ }
74
+ }
75
+ }
76
+ }
77
+ catch {
78
+ // Non-critical — createmeta API may have changed in newer JIRA versions
79
+ checks.push({ name: 'Issue Types', status: 'warn', message: 'Could not verify issue types (createmeta API unavailable)' });
80
+ }
81
+ // Check 4: Verify remote link permission
82
+ try {
83
+ const res = await fetch(`${baseUrl}/rest/api/3/mypermissions?permissions=EDIT_ISSUES&projectKey=${config.projectKey}`, {
84
+ headers: { 'Authorization': authHeader }
85
+ });
86
+ if (res.ok) {
87
+ const data = await res.json();
88
+ const canEdit = data.permissions?.EDIT_ISSUES?.havePermission;
89
+ if (canEdit) {
90
+ checks.push({ name: 'Edit Permission', status: 'pass', message: 'Can edit issues (required for remote links)' });
91
+ }
92
+ else {
93
+ checks.push({
94
+ name: 'Edit Permission',
95
+ status: 'warn',
96
+ message: 'Cannot edit issues — PR remote links will fail',
97
+ fix: 'Grant "Edit Issues" permission to the API user in JIRA project settings'
98
+ });
99
+ }
100
+ }
101
+ }
102
+ catch {
103
+ checks.push({ name: 'Edit Permission', status: 'warn', message: 'Could not verify permissions' });
104
+ }
105
+ return {
106
+ provider: 'jira',
107
+ checks,
108
+ healthy: checks.every(c => c.status !== 'fail')
109
+ };
110
+ }
111
+ /**
112
+ * Run health checks for ADO integration
113
+ */
114
+ export async function checkAdoIntegration(config) {
115
+ const checks = [];
116
+ const authHeader = 'Basic ' + Buffer.from(`:${config.pat}`).toString('base64');
117
+ const baseUrl = `https://dev.azure.com/${config.organization}/${config.project}`;
118
+ // Check 1: Auth + project access
119
+ try {
120
+ const res = await fetch(`${baseUrl}/_apis/wit/workitemtypes?api-version=7.1`, {
121
+ headers: { 'Authorization': authHeader }
122
+ });
123
+ if (res.ok) {
124
+ const data = await res.json();
125
+ const typeNames = (data.value || []).map((t) => t.name);
126
+ checks.push({ name: 'API Authentication', status: 'pass', message: 'Authenticated successfully' });
127
+ checks.push({ name: 'Work Item Types', status: 'pass', message: `Available: ${typeNames.slice(0, 5).join(', ')}` });
128
+ }
129
+ else {
130
+ checks.push({ name: 'API Authentication', status: 'fail', message: `Auth failed: ${res.status}`, fix: 'Check ADO_PAT and ADO_ORGANIZATION in .env' });
131
+ }
132
+ }
133
+ catch (err) {
134
+ checks.push({ name: 'API Authentication', status: 'fail', message: err.message });
135
+ }
136
+ return {
137
+ provider: 'ado',
138
+ checks,
139
+ healthy: checks.every(c => c.status !== 'fail')
140
+ };
141
+ }
142
+ /**
143
+ * Run health checks for GitHub integration
144
+ */
145
+ export async function checkGitHubIntegration() {
146
+ const checks = [];
147
+ // Check 1: gh CLI authenticated
148
+ try {
149
+ const { execSync } = await import('child_process');
150
+ const output = execSync('gh auth status 2>&1', { encoding: 'utf-8', timeout: 10000 });
151
+ if (output.includes('Logged in')) {
152
+ checks.push({ name: 'GitHub CLI', status: 'pass', message: 'gh CLI authenticated' });
153
+ }
154
+ else {
155
+ checks.push({ name: 'GitHub CLI', status: 'warn', message: 'gh CLI not authenticated', fix: 'Run: gh auth login' });
156
+ }
157
+ }
158
+ catch {
159
+ checks.push({ name: 'GitHub CLI', status: 'warn', message: 'gh CLI not available or not authenticated', fix: 'Install gh CLI and run: gh auth login' });
160
+ }
161
+ return {
162
+ provider: 'github',
163
+ checks,
164
+ healthy: checks.every(c => c.status !== 'fail')
165
+ };
166
+ }
167
+ /**
168
+ * Format health check results for display
169
+ */
170
+ export function formatHealthCheckResults(results) {
171
+ const lines = ['', '━━━ Integration Health Check ━━━', ''];
172
+ for (const result of results) {
173
+ const icon = result.healthy ? '✅' : '⚠️';
174
+ lines.push(`${icon} ${result.provider.toUpperCase()}`);
175
+ for (const check of result.checks) {
176
+ const statusIcon = check.status === 'pass' ? ' ✓' : check.status === 'warn' ? ' ⚠' : ' ✗';
177
+ lines.push(`${statusIcon} ${check.name}: ${check.message}`);
178
+ if (check.fix) {
179
+ lines.push(` Fix: ${check.fix}`);
180
+ }
181
+ }
182
+ lines.push('');
183
+ }
184
+ return lines.join('\n');
185
+ }
186
+ //# sourceMappingURL=integration-health-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration-health-check.js","sourceRoot":"","sources":["../../../src/sync/integration-health-check.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * PR Linker — Links pull requests to external tickets (JIRA, ADO)
3
+ *
4
+ * Called after PR creation to create remote links / hyperlinks
5
+ * so external tools show the PR in the issue's UI.
6
+ *
7
+ * @module sync/pr-linker
8
+ */
9
+ import { Logger } from '../utils/logger.js';
10
+ export interface PrLinkRequest {
11
+ projectRoot: string;
12
+ incrementId: string;
13
+ prUrl: string;
14
+ prNumber: number;
15
+ branch: string;
16
+ }
17
+ export interface PrLinkResult {
18
+ linked: {
19
+ provider: string;
20
+ key: string;
21
+ }[];
22
+ errors: {
23
+ provider: string;
24
+ key: string;
25
+ error: string;
26
+ }[];
27
+ }
28
+ /**
29
+ * Link a PR to all external tickets associated with this increment.
30
+ * Non-blocking: errors are collected, never thrown.
31
+ */
32
+ export declare function linkPrToExternalTickets(request: PrLinkRequest, logger?: Logger): Promise<PrLinkResult>;
33
+ /**
34
+ * Resolve the external ticket key for branch naming.
35
+ * Returns the JIRA key or ADO ID prefix if available.
36
+ */
37
+ export declare function resolveExternalBranchPrefix(metadata: any): string | null;
38
+ //# sourceMappingURL=pr-linker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr-linker.d.ts","sourceRoot":"","sources":["../../../src/sync/pr-linker.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAiB,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE3D,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5C,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC5D;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAqC5G;AAwHD;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,GAAG,GAAG,MAAM,GAAG,IAAI,CAUxE"}
@@ -0,0 +1,157 @@
1
+ /**
2
+ * PR Linker — Links pull requests to external tickets (JIRA, ADO)
3
+ *
4
+ * Called after PR creation to create remote links / hyperlinks
5
+ * so external tools show the PR in the issue's UI.
6
+ *
7
+ * @module sync/pr-linker
8
+ */
9
+ import { readFileSync, existsSync } from 'fs';
10
+ import { join } from 'path';
11
+ import { consoleLogger } from '../utils/logger.js';
12
+ /**
13
+ * Link a PR to all external tickets associated with this increment.
14
+ * Non-blocking: errors are collected, never thrown.
15
+ */
16
+ export async function linkPrToExternalTickets(request, logger) {
17
+ const log = logger || consoleLogger;
18
+ const result = { linked: [], errors: [] };
19
+ const configPath = join(request.projectRoot, '.specweave', 'config.json');
20
+ if (!existsSync(configPath)) {
21
+ log.warn('No .specweave/config.json found — skipping PR linking');
22
+ return result;
23
+ }
24
+ const config = JSON.parse(readFileSync(configPath, 'utf-8'));
25
+ const metadataPath = join(request.projectRoot, '.specweave', 'increments', request.incrementId, 'metadata.json');
26
+ if (!existsSync(metadataPath)) {
27
+ log.warn(`No metadata.json for increment ${request.incrementId}`);
28
+ return result;
29
+ }
30
+ const metadata = JSON.parse(readFileSync(metadataPath, 'utf-8'));
31
+ // Collect JIRA issue keys from metadata
32
+ const jiraKeys = collectJiraKeys(metadata, request.projectRoot, request.incrementId);
33
+ // Collect ADO work item IDs from metadata
34
+ const adoIds = collectAdoIds(metadata, request.projectRoot, request.incrementId);
35
+ // Link to JIRA
36
+ if (jiraKeys.length > 0 && config.sync?.jira?.enabled !== false) {
37
+ await linkToJira(jiraKeys, request, config, result, log);
38
+ }
39
+ // Link to ADO
40
+ if (adoIds.length > 0 && config.sync?.ado?.enabled !== false) {
41
+ await linkToAdo(adoIds, request, config, result, log);
42
+ }
43
+ return result;
44
+ }
45
+ function collectJiraKeys(metadata, projectRoot, incrementId) {
46
+ const keys = [];
47
+ // From metadata.json direct jira reference
48
+ if (metadata.jira?.issue)
49
+ keys.push(metadata.jira.issue);
50
+ if (metadata.jira?.issueKey)
51
+ keys.push(metadata.jira.issueKey);
52
+ // From externalLinks
53
+ if (metadata.externalLinks?.jira?.epicKey)
54
+ keys.push(metadata.externalLinks.jira.epicKey);
55
+ if (metadata.externalLinks?.jira?.issueKey)
56
+ keys.push(metadata.externalLinks.jira.issueKey);
57
+ // From per-user-story links
58
+ const usLinks = metadata.externalLinks?.jira?.userStories;
59
+ if (usLinks && typeof usLinks === 'object') {
60
+ for (const us of Object.values(usLinks)) {
61
+ if (us?.issueKey)
62
+ keys.push(us.issueKey);
63
+ }
64
+ }
65
+ // Deduplicate
66
+ return [...new Set(keys)];
67
+ }
68
+ function collectAdoIds(metadata, projectRoot, incrementId) {
69
+ const ids = [];
70
+ if (metadata.externalLinks?.ado?.featureId)
71
+ ids.push(Number(metadata.externalLinks.ado.featureId));
72
+ if (metadata.externalLinks?.ado?.workItemId)
73
+ ids.push(Number(metadata.externalLinks.ado.workItemId));
74
+ const usLinks = metadata.externalLinks?.ado?.userStories;
75
+ if (usLinks && typeof usLinks === 'object') {
76
+ for (const us of Object.values(usLinks)) {
77
+ if (us?.workItemId)
78
+ ids.push(Number(us.workItemId));
79
+ }
80
+ }
81
+ return [...new Set(ids.filter(id => !isNaN(id)))];
82
+ }
83
+ async function linkToJira(keys, request, config, result, log) {
84
+ try {
85
+ const { JiraClient } = await import('../integrations/jira/jira-client.js');
86
+ const jiraClient = new JiraClient();
87
+ for (const issueKey of keys) {
88
+ try {
89
+ await jiraClient.addRemoteLink(issueKey, {
90
+ url: request.prUrl,
91
+ title: `PR #${request.prNumber}: ${request.branch}`,
92
+ globalId: `specweave-pr-${request.prNumber}`,
93
+ });
94
+ result.linked.push({ provider: 'jira', key: issueKey });
95
+ log.info(`Linked PR #${request.prNumber} to JIRA ${issueKey}`);
96
+ }
97
+ catch (err) {
98
+ result.errors.push({ provider: 'jira', key: issueKey, error: err.message });
99
+ log.warn(`Failed to link PR to JIRA ${issueKey}: ${err.message}`);
100
+ }
101
+ }
102
+ }
103
+ catch (err) {
104
+ log.warn(`JIRA client initialization failed: ${err.message}`);
105
+ for (const key of keys) {
106
+ result.errors.push({ provider: 'jira', key, error: err.message });
107
+ }
108
+ }
109
+ }
110
+ async function linkToAdo(ids, request, config, result, log) {
111
+ try {
112
+ const { AdoClient } = await import('../../plugins/specweave-ado/lib/ado-client.js');
113
+ const adoConfig = {
114
+ organization: config.sync?.ado?.organization || '',
115
+ project: config.sync?.ado?.project || '',
116
+ personalAccessToken: process.env.ADO_PAT || process.env.AZURE_DEVOPS_PAT || '',
117
+ };
118
+ if (!adoConfig.organization || !adoConfig.personalAccessToken) {
119
+ log.warn('ADO not configured — skipping PR linking');
120
+ return;
121
+ }
122
+ const adoClient = new AdoClient(adoConfig);
123
+ for (const workItemId of ids) {
124
+ try {
125
+ await adoClient.addHyperlink(workItemId, request.prUrl, `PR #${request.prNumber}: ${request.branch}`);
126
+ result.linked.push({ provider: 'ado', key: String(workItemId) });
127
+ log.info(`Linked PR #${request.prNumber} to ADO #${workItemId}`);
128
+ }
129
+ catch (err) {
130
+ result.errors.push({ provider: 'ado', key: String(workItemId), error: err.message });
131
+ log.warn(`Failed to link PR to ADO #${workItemId}: ${err.message}`);
132
+ }
133
+ }
134
+ }
135
+ catch (err) {
136
+ log.warn(`ADO client initialization failed: ${err.message}`);
137
+ for (const id of ids) {
138
+ result.errors.push({ provider: 'ado', key: String(id), error: err.message });
139
+ }
140
+ }
141
+ }
142
+ /**
143
+ * Resolve the external ticket key for branch naming.
144
+ * Returns the JIRA key or ADO ID prefix if available.
145
+ */
146
+ export function resolveExternalBranchPrefix(metadata) {
147
+ // JIRA key takes priority
148
+ const jiraKeys = collectJiraKeys(metadata, '', '');
149
+ if (jiraKeys.length > 0)
150
+ return jiraKeys[0];
151
+ // ADO work item ID
152
+ const adoIds = collectAdoIds(metadata, '', '');
153
+ if (adoIds.length > 0)
154
+ return `AB#${adoIds[0]}`;
155
+ return null;
156
+ }
157
+ //# sourceMappingURL=pr-linker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr-linker.js","sourceRoot":"","sources":["../../../src/sync/pr-linker.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specweave",
3
- "version": "1.0.447",
3
+ "version": "1.0.450",
4
4
  "description": "100+ domain-expert AI skills — PM, Architect, Frontend, QA, Security and more. Skills learn your team's patterns permanently. Spec-first planning, autonomous execution, multi-agent teams, synced to GitHub/JIRA. Claude Code, Cursor, Copilot & more.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -119,7 +119,30 @@ Write the following:
119
119
  }
120
120
  ```
121
121
 
122
- ### Step 7: Dry Run
122
+ ### Step 7: Integration Health Check
123
+
124
+ After credentials are validated, run a deeper health check to catch misconfigurations early (e.g., wrong issue types, missing permissions, unavailable integrations):
125
+
126
+ ```typescript
127
+ // Import and run health checks for each enabled provider
128
+ import { checkJiraIntegration, checkAdoIntegration, checkGitHubIntegration, formatHealthCheckResults } from '../../src/sync/integration-health-check.js';
129
+
130
+ const results = [];
131
+ if (jiraEnabled) results.push(await checkJiraIntegration({ domain, projectKey, email, apiToken }));
132
+ if (adoEnabled) results.push(await checkAdoIntegration({ organization, project, pat }));
133
+ if (githubEnabled) results.push(await checkGitHubIntegration());
134
+
135
+ console.log(formatHealthCheckResults(results));
136
+ ```
137
+
138
+ This checks:
139
+ - **JIRA**: API auth, project access, issue type availability (catches "The issue type selected is invalid"), edit permissions for remote links
140
+ - **ADO**: API auth, work item type availability
141
+ - **GitHub**: gh CLI authentication
142
+
143
+ Warnings are advisory — they don't block setup but alert the user to fix issues before they hit them at increment closure.
144
+
145
+ ### Step 8: Dry Run
123
146
 
124
147
  Run a test sync (read-only) to verify configuration:
125
148
 
@@ -1,12 +1,13 @@
1
1
  ---
2
- version: 1.0.0
2
+ version: 1.1.0
3
3
  description: >
4
- Clone and register an existing repository into the SpecWeave workspace.
4
+ Clone and register an existing repository (or bulk-clone an entire org) into the SpecWeave workspace.
5
5
  Activate when the user wants to: add a repo, get a repo, clone a repo,
6
6
  register a repo, bring in a repo, pull in a repo, add a github repo to umbrella,
7
7
  clone and register, "get owner/repo", "add this github repo", "clone this repo
8
- into my workspace", "register this local repo". Also activate for "restore repos"
9
- or "clone all child repos" on a new machine.
8
+ into my workspace", "register this local repo". Also activate for "restore repos",
9
+ "clone all child repos" on a new machine, "clone all service-* repos", "bulk clone",
10
+ "clone entire org", "clone all repos matching pattern", "get all repos from org".
10
11
  Do NOT activate for: add a feature, add a task, add a story, add an increment,
11
12
  add a user story (those route to sw:increment).
12
13
  triggers:
@@ -20,6 +21,10 @@ triggers:
20
21
  - "pull in repo"
21
22
  - "restore repos"
22
23
  - "clone all child repos"
24
+ - "bulk clone"
25
+ - "clone entire org"
26
+ - "clone all repos"
27
+ - "clone all matching"
23
28
  negative_triggers:
24
29
  - "add a feature"
25
30
  - "add a task"
@@ -30,16 +35,21 @@ negative_triggers:
30
35
 
31
36
  # sw:get — Clone & Register Repository
32
37
 
33
- Use this skill when the user wants to add an existing repository to their SpecWeave workspace.
38
+ Use this skill when the user wants to add an existing repository (or many repositories) to their SpecWeave workspace.
34
39
 
35
40
  ## What it does
36
41
 
37
42
  Runs `specweave get <source>` which:
38
- 1. Parses the source (GitHub shorthand, full URL, SSH URL, or local path)
43
+ 1. Parses the source (GitHub shorthand, full URL, SSH URL, local path, or org glob pattern)
39
44
  2. Clones the repo into `repositories/{org}/{repo}/` (in umbrella context)
40
45
  3. Registers it in `.specweave/config.json` under `umbrella.childRepos`
41
46
  4. Runs `specweave init` inside the cloned repo
42
47
 
48
+ For **bulk mode** (glob pattern or `--all`):
49
+ - Fetches matching repos from the GitHub org via API
50
+ - Launches a background clone job (survives terminal close)
51
+ - Reports job ID; user monitors with `specweave jobs`
52
+
43
53
  ## Instructions
44
54
 
45
55
  1. Extract the repository source from the user's message:
@@ -47,16 +57,26 @@ Runs `specweave get <source>` which:
47
57
  - Full URL: `https://github.com/org/repo`
48
58
  - SSH: `git@github.com:org/repo`
49
59
  - Local path: `./path/to/repo`
60
+ - Glob pattern: `org/service-*` or `org/*`
61
+ - All repos in org: `--all org`
50
62
 
51
63
  2. Check if the user mentioned optional flags:
52
64
  - `--prefix` — US prefix (e.g., "use prefix FE")
53
65
  - `--role` — repo role (e.g., "it's a backend service")
54
66
  - `--branch` — specific branch (e.g., "clone the develop branch")
55
67
  - `--no-init` — skip specweave init
68
+ - `--all` — clone all repos in org
69
+ - `--pattern` — glob filter when used with `--all`
70
+ - `--no-archived` — skip archived repos
71
+ - `--no-forks` — skip forks
72
+ - `--limit` — max repos to fetch (default 1000)
56
73
 
57
74
  3. Run the command:
58
75
  ```bash
59
76
  specweave get <source> [--prefix X] [--role Y] [--branch Z] [--no-init]
77
+ # or bulk:
78
+ specweave get "org/service-*"
79
+ specweave get --all org [--pattern "svc-*"] [--no-archived] [--no-forks]
60
80
  ```
61
81
 
62
82
  4. Report the result to the user.
@@ -70,8 +90,20 @@ Runs `specweave get <source>` which:
70
90
  | "get this repo: git@github.com:org/repo" | `specweave get git@github.com:org/repo` |
71
91
  | "add ./my-local-service to the umbrella" | `specweave get ./my-local-service` |
72
92
  | "clone my-service with prefix MSV" | `specweave get owner/my-service --prefix MSV` |
93
+ | "clone all service-* repos from acme-corp" | `specweave get "acme-corp/service-*"` |
94
+ | "clone all repos in acme-corp org" | `specweave get --all acme-corp` |
95
+ | "clone all acme-corp repos, skip archived and forks" | `specweave get --all acme-corp --no-archived --no-forks` |
96
+ | "clone all api-* repos from myorg, max 200" | `specweave get "myorg/api-*" --limit 200` |
73
97
  | "restore all repos on this machine" | See note below |
74
98
 
99
+ ## Bulk Clone Notes
100
+
101
+ - Requires GitHub auth: `GH_TOKEN` env var or `gh auth login`
102
+ - Cloning runs in the **background** — terminal can be closed safely
103
+ - Monitor progress: `specweave jobs`
104
+ - Already-cloned repos are skipped automatically (idempotent)
105
+ - Failed repos are tracked and can be resumed by re-running the same command
106
+
75
107
  ## New Machine Restore
76
108
 
77
109
  If the user says "restore repos", "clone all child repos", or "set up on new machine":
@@ -39,9 +39,26 @@ If `prRefs` already has an entry with `state: "open"`, skip PR creation and repo
39
39
 
40
40
  ## Step 2: Determine Branch Name
41
41
 
42
+ **External ticket key prefix** — If the increment was imported from JIRA/ADO, prefix the branch with the external ticket key for native integration linking:
43
+
42
44
  ```bash
43
- BRANCH_NAME="${BRANCH_PREFIX}${INCREMENT_ID}"
44
- # e.g., sw/0520-pr-based-increment-closure
45
+ # Check metadata for external ticket key (JIRA or ADO)
46
+ JIRA_KEY=$(jq -r '.jira.issue // .jira.issueKey // .externalLinks.jira.epicKey // .externalLinks.jira.issueKey // empty' \
47
+ .specweave/increments/${INCREMENT_ID}/metadata.json 2>/dev/null)
48
+ ADO_ID=$(jq -r '.externalLinks.ado.featureId // .externalLinks.ado.workItemId // empty' \
49
+ .specweave/increments/${INCREMENT_ID}/metadata.json 2>/dev/null)
50
+ EXTERNAL_KEY_BRANCHING=$(jq -r '.cicd.git.externalKeyBranching // true' .specweave/config.json 2>/dev/null)
51
+
52
+ if [ "$EXTERNAL_KEY_BRANCHING" = "true" ] && [ -n "$JIRA_KEY" ]; then
53
+ BRANCH_NAME="${JIRA_KEY}/${INCREMENT_ID}"
54
+ # e.g., ID-300/0520-auth-gateway-otel
55
+ elif [ "$EXTERNAL_KEY_BRANCHING" = "true" ] && [ -n "$ADO_ID" ]; then
56
+ BRANCH_NAME="AB#${ADO_ID}/${INCREMENT_ID}"
57
+ # e.g., AB#4567/0520-auth-gateway-otel
58
+ else
59
+ BRANCH_NAME="${BRANCH_PREFIX}${INCREMENT_ID}"
60
+ # e.g., sw/0520-pr-based-increment-closure
61
+ fi
45
62
  ```
46
63
 
47
64
  Check current branch:
@@ -147,6 +164,26 @@ Update increment metadata with PR reference. Edit `metadata.json` to add/update
147
164
 
148
165
  Use `jq` or direct JSON edit to update metadata.json.
149
166
 
167
+ ## Step 7b: Link PR to External Tickets
168
+
169
+ After PR creation, link the PR to any associated JIRA/ADO tickets. This creates remote links (JIRA) or hyperlinks (ADO) so the PR appears in the external tool's UI.
170
+
171
+ ```bash
172
+ specweave link-pr \
173
+ --increment "${INCREMENT_ID}" \
174
+ --pr-url "${PR_URL}" \
175
+ --pr-number "${PR_NUMBER}" \
176
+ --branch "${BRANCH_NAME}"
177
+ ```
178
+
179
+ This is **non-blocking** — if linking fails, the PR is still created successfully. Errors are logged as warnings.
180
+
181
+ The link-pr command:
182
+ - Reads metadata.json for JIRA issue keys and ADO work item IDs
183
+ - Creates JIRA remote links via `/rest/api/3/issue/{key}/remotelink` (idempotent via `globalId`)
184
+ - Creates ADO work item hyperlinks via JSON Patch on work item relations
185
+ - Reports linked tickets and any errors
186
+
150
187
  ## Step 8: Multi-Repo / Umbrella Mode
151
188
 
152
189
  Check if umbrella mode is enabled:
@@ -127,6 +127,28 @@ class AdoClient {
127
127
  const response = await this.request("POST", url, { text });
128
128
  return response;
129
129
  }
130
+ /**
131
+ * Add hyperlink relation to work item (for PR linking)
132
+ *
133
+ * @see https://learn.microsoft.com/en-us/rest/api/azure/devops/wit/work-items/update
134
+ */
135
+ async addHyperlink(workItemId, url, comment) {
136
+ const apiUrl = `${this.baseUrl}/_apis/wit/workitems/${workItemId}?api-version=7.1`;
137
+ const operations = [{
138
+ op: "add",
139
+ path: "/relations/-",
140
+ value: {
141
+ rel: "Hyperlink",
142
+ url,
143
+ attributes: {
144
+ comment
145
+ }
146
+ }
147
+ }];
148
+ await this.request("PATCH", apiUrl, operations, {
149
+ "Content-Type": "application/json-patch+json"
150
+ });
151
+ }
130
152
  /**
131
153
  * Get comments for work item
132
154
  *