specweave 1.0.508 → 1.0.509

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 (46) hide show
  1. package/dist/src/adapters/aider/adapter.d.ts.map +1 -1
  2. package/dist/src/adapters/aider/adapter.js +9 -9
  3. package/dist/src/adapters/aider/adapter.js.map +1 -1
  4. package/dist/src/adapters/amazonq/adapter.d.ts.map +1 -1
  5. package/dist/src/adapters/amazonq/adapter.js +9 -9
  6. package/dist/src/adapters/amazonq/adapter.js.map +1 -1
  7. package/dist/src/adapters/cline/adapter.d.ts.map +1 -1
  8. package/dist/src/adapters/cline/adapter.js +9 -9
  9. package/dist/src/adapters/cline/adapter.js.map +1 -1
  10. package/dist/src/adapters/codex/adapter.d.ts +3 -3
  11. package/dist/src/adapters/codex/adapter.js +10 -10
  12. package/dist/src/adapters/codex/adapter.js.map +1 -1
  13. package/dist/src/adapters/continue/adapter.d.ts +3 -3
  14. package/dist/src/adapters/continue/adapter.d.ts.map +1 -1
  15. package/dist/src/adapters/continue/adapter.js +13 -13
  16. package/dist/src/adapters/continue/adapter.js.map +1 -1
  17. package/dist/src/adapters/copilot/adapter.d.ts +3 -4
  18. package/dist/src/adapters/copilot/adapter.d.ts.map +1 -1
  19. package/dist/src/adapters/copilot/adapter.js +13 -14
  20. package/dist/src/adapters/copilot/adapter.js.map +1 -1
  21. package/dist/src/adapters/cursor/adapter.d.ts +3 -3
  22. package/dist/src/adapters/cursor/adapter.js +10 -10
  23. package/dist/src/adapters/cursor/adapter.js.map +1 -1
  24. package/dist/src/adapters/jetbrains/adapter.d.ts.map +1 -1
  25. package/dist/src/adapters/jetbrains/adapter.js +10 -10
  26. package/dist/src/adapters/jetbrains/adapter.js.map +1 -1
  27. package/dist/src/adapters/registry.yaml +32 -32
  28. package/dist/src/adapters/tabnine/adapter.d.ts.map +1 -1
  29. package/dist/src/adapters/tabnine/adapter.js +9 -9
  30. package/dist/src/adapters/tabnine/adapter.js.map +1 -1
  31. package/dist/src/adapters/trae/adapter.d.ts.map +1 -1
  32. package/dist/src/adapters/trae/adapter.js +9 -9
  33. package/dist/src/adapters/trae/adapter.js.map +1 -1
  34. package/dist/src/adapters/windsurf/adapter.d.ts +3 -4
  35. package/dist/src/adapters/windsurf/adapter.d.ts.map +1 -1
  36. package/dist/src/adapters/windsurf/adapter.js +13 -14
  37. package/dist/src/adapters/windsurf/adapter.js.map +1 -1
  38. package/dist/src/adapters/zed/adapter.d.ts.map +1 -1
  39. package/dist/src/adapters/zed/adapter.js +9 -9
  40. package/dist/src/adapters/zed/adapter.js.map +1 -1
  41. package/package.json +1 -1
  42. package/plugins/specweave/agents/sw-architect.md +1 -1
  43. package/plugins/specweave/lib/integrations/jira/jira-spec-sync.js +100 -90
  44. package/plugins/specweave/lib/integrations/jira/jira-status-sync.js +54 -2
  45. package/plugins/specweave/skills/architect/SKILL.md +2 -2
  46. package/plugins/specweave/skills/team-lead/agents/frontend.md +1 -2
@@ -8,7 +8,7 @@ export class ZedAdapter extends AdapterBase {
8
8
  constructor() {
9
9
  super(...arguments);
10
10
  this.name = 'zed';
11
- this.description = 'Zed adapter — skills installed in .rules/';
11
+ this.description = 'Zed adapter — skills installed in .zed/skills/';
12
12
  this.automationLevel = 'semi';
13
13
  }
14
14
  async detect() {
@@ -19,7 +19,7 @@ export class ZedAdapter extends AdapterBase {
19
19
  }
20
20
  async install(options) {
21
21
  console.log('\n📦 Installing Zed Adapter\n');
22
- await fs.ensureDir(path.join(options.projectPath, '.rules'));
22
+ await fs.ensureDir(path.join(options.projectPath, '.zed/skills'));
23
23
  console.log('\n✨ Zed adapter installed!');
24
24
  console.log(' Run `specweave refresh-plugins` to install skills.');
25
25
  }
@@ -27,20 +27,20 @@ export class ZedAdapter extends AdapterBase {
27
27
  return true;
28
28
  }
29
29
  async compilePlugin(plugin) {
30
- const rulesDir = '.rules';
30
+ const skillsDir = '.zed/skills';
31
31
  console.log(`\n📦 Installing plugin skills for Zed: ${plugin.manifest.name}`);
32
- await this.writeSkillFiles(plugin, rulesDir);
33
- console.log(` ✓ ${plugin.skills.length} skill(s) written to ${rulesDir}/`);
32
+ await this.writeSkillFiles(plugin, skillsDir);
33
+ console.log(` ✓ ${plugin.skills.length} skill(s) written to ${skillsDir}/`);
34
34
  console.log(`\n✅ Plugin ${plugin.manifest.name} installed for Zed!`);
35
35
  }
36
36
  async unloadPlugin(pluginName) {
37
37
  console.log(`\n🗑️ Unloading plugin from Zed: ${pluginName}`);
38
- await this.removeSkillFiles(pluginName, '.rules');
39
- console.log(` ✓ Removed from .rules/`);
38
+ await this.removeSkillFiles(pluginName, '.zed/skills');
39
+ console.log(` ✓ Removed from .zed/skills/`);
40
40
  console.log(`\n✅ Plugin ${pluginName} unloaded!`);
41
41
  }
42
42
  async getInstalledPlugins() {
43
- return await this.listInstalledPluginsInDir('.rules');
43
+ return await this.listInstalledPluginsInDir('.zed/skills');
44
44
  }
45
45
  async postInstall(options) {
46
46
  console.log(this.getInstructions());
@@ -49,7 +49,7 @@ export class ZedAdapter extends AdapterBase {
49
49
  return `
50
50
  Zed Adapter
51
51
 
52
- Skills installed in .rules/.
52
+ Skills installed in .zed/skills/.
53
53
  Run: specweave refresh-plugins
54
54
  `;
55
55
  }
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../../src/adapters/zed/adapter.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAIjD,MAAM,OAAO,UAAW,SAAQ,WAAW;IAA3C;;QACE,SAAI,GAAG,KAAK,CAAC;QACb,gBAAW,GAAG,2CAA2C,CAAC;QAC1D,oBAAe,GAAG,MAAe,CAAC;IAoDpC,CAAC;IAlDC,KAAK,CAAC,MAAM;QACV,OAAO,MAAM,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAC1E,CAAC;IAED,QAAQ;QACN,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,OAAuB;QACnC,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC;QAC7C,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC;QAC7D,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;QAC1C,OAAO,CAAC,GAAG,CAAC,uDAAuD,CAAC,CAAC;IACvE,CAAC;IAED,eAAe;QACb,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,MAAc;QAChC,MAAM,QAAQ,GAAG,QAAQ,CAAC;QAC1B,OAAO,CAAC,GAAG,CAAC,0CAA0C,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;QAC9E,MAAM,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC7C,OAAO,CAAC,GAAG,CAAC,QAAQ,MAAM,CAAC,MAAM,CAAC,MAAM,wBAAwB,QAAQ,GAAG,CAAC,CAAC;QAC7E,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,QAAQ,CAAC,IAAI,qBAAqB,CAAC,CAAC;IACvE,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,UAAkB;QACnC,OAAO,CAAC,GAAG,CAAC,qCAAqC,UAAU,EAAE,CAAC,CAAC;QAC/D,MAAM,IAAI,CAAC,gBAAgB,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAClD,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;QACzC,OAAO,CAAC,GAAG,CAAC,cAAc,UAAU,YAAY,CAAC,CAAC;IACpD,CAAC;IAED,KAAK,CAAC,mBAAmB;QACvB,OAAO,MAAM,IAAI,CAAC,yBAAyB,CAAC,QAAQ,CAAC,CAAC;IACxD,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,OAAuB;QACvC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,eAAe;QACb,OAAO;;;;;KAKN,CAAC;IACJ,CAAC;CACF"}
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../../src/adapters/zed/adapter.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAIjD,MAAM,OAAO,UAAW,SAAQ,WAAW;IAA3C;;QACE,SAAI,GAAG,KAAK,CAAC;QACb,gBAAW,GAAG,gDAAgD,CAAC;QAC/D,oBAAe,GAAG,MAAe,CAAC;IAoDpC,CAAC;IAlDC,KAAK,CAAC,MAAM;QACV,OAAO,MAAM,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAC1E,CAAC;IAED,QAAQ;QACN,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,OAAuB;QACnC,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC;QAC7C,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC,CAAC;QAClE,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;QAC1C,OAAO,CAAC,GAAG,CAAC,uDAAuD,CAAC,CAAC;IACvE,CAAC;IAED,eAAe;QACb,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,MAAc;QAChC,MAAM,SAAS,GAAG,aAAa,CAAC;QAChC,OAAO,CAAC,GAAG,CAAC,0CAA0C,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;QAC9E,MAAM,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAC9C,OAAO,CAAC,GAAG,CAAC,QAAQ,MAAM,CAAC,MAAM,CAAC,MAAM,wBAAwB,SAAS,GAAG,CAAC,CAAC;QAC9E,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,QAAQ,CAAC,IAAI,qBAAqB,CAAC,CAAC;IACvE,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,UAAkB;QACnC,OAAO,CAAC,GAAG,CAAC,qCAAqC,UAAU,EAAE,CAAC,CAAC;QAC/D,MAAM,IAAI,CAAC,gBAAgB,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;QACvD,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;QAC9C,OAAO,CAAC,GAAG,CAAC,cAAc,UAAU,YAAY,CAAC,CAAC;IACpD,CAAC;IAED,KAAK,CAAC,mBAAmB;QACvB,OAAO,MAAM,IAAI,CAAC,yBAAyB,CAAC,aAAa,CAAC,CAAC;IAC7D,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,OAAuB;QACvC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,eAAe;QACb,OAAO;;;;;KAKN,CAAC;IACJ,CAAC;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specweave",
3
- "version": "1.0.508",
3
+ "version": "1.0.509",
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",
@@ -19,4 +19,4 @@ The `sw:architect` skill is preloaded with full domain logic including design pa
19
19
 
20
20
  - **Register skill-chain marker** (STEP 0 in preloaded skill) before writing plan.md
21
21
  - **Check ADRs first** at `.specweave/docs/internal/architecture/adr/`
22
- - **Domain delegation** — after plan.md, recommend domain skills (sw:architect for all domains; optional vskill plugins if installed)
22
+ - **Domain delegation** — after plan.md, recommend domain skills (sw:architect for all domains)
@@ -9,8 +9,6 @@ import { toDescription } from "./content-format-adapter.js";
9
9
  import { getEpicLinkFieldForProject } from "./jira-field-discovery.js";
10
10
  import { searchAllIssues } from "./jira-paginated-search.js";
11
11
  import axios from "axios";
12
- import { CircuitBreakerRegistry } from "../../../../../src/core/sync/circuit-breaker-registry.js";
13
- import { SyncRetryQueue } from "../../../../../src/core/sync/sync-retry-queue.js";
14
12
  import { SyncError } from "../../../../../src/core/errors/sync-error.js";
15
13
  import { LockManager } from "../../../../../src/utils/lock-manager.js";
16
14
  function buildStoryDescription(us) {
@@ -55,6 +53,9 @@ class JiraSpecSync {
55
53
  }
56
54
  });
57
55
  }
56
+ /**
57
+ * Execute fn under file lock (if lockManager configured).
58
+ */
58
59
  async withLock(fn) {
59
60
  if (!this.lockManager) return fn();
60
61
  const acquired = await this.lockManager.acquire();
@@ -67,6 +68,9 @@ class JiraSpecSync {
67
68
  await this.lockManager.release();
68
69
  }
69
70
  }
71
+ /**
72
+ * Check circuit breaker before making API calls.
73
+ */
70
74
  checkCircuitBreaker() {
71
75
  if (!this.circuitBreakerRegistry) return;
72
76
  const breaker = this.circuitBreakerRegistry.get("jira");
@@ -74,10 +78,16 @@ class JiraSpecSync {
74
78
  throw new SyncError("jira", 0, "", "Circuit breaker open for jira");
75
79
  }
76
80
  }
81
+ /**
82
+ * Record success on circuit breaker.
83
+ */
77
84
  recordApiSuccess() {
78
85
  if (!this.circuitBreakerRegistry) return;
79
86
  this.circuitBreakerRegistry.get("jira").recordSuccess();
80
87
  }
88
+ /**
89
+ * Record failure on circuit breaker and optionally enqueue retry.
90
+ */
81
91
  async recordApiFailure(error, operation) {
82
92
  const httpStatus = error?.response?.status ?? 0;
83
93
  if (this.circuitBreakerRegistry) {
@@ -111,56 +121,56 @@ class JiraSpecSync {
111
121
  console.log(`
112
122
  \u{1F504} Syncing spec ${specId} to Jira Epic...`);
113
123
  return this.withLock(async () => {
114
- this.checkCircuitBreaker();
115
- try {
116
- const spec = await this.specManager.loadSpec(specId);
117
- if (!spec) {
124
+ this.checkCircuitBreaker();
125
+ try {
126
+ const spec = await this.specManager.loadSpec(specId);
127
+ if (!spec) {
128
+ return {
129
+ success: false,
130
+ specId,
131
+ provider: "jira",
132
+ error: `Spec ${specId} not found`
133
+ };
134
+ }
135
+ const existingLink = spec.metadata.externalLinks?.jira;
136
+ let epic;
137
+ if (existingLink?.epicKey) {
138
+ console.log(` Found existing Jira Epic ${existingLink.epicKey}`);
139
+ epic = await this.updateJiraEpic(existingLink.epicKey, spec);
140
+ } else {
141
+ console.log(" Creating new Jira Epic...");
142
+ epic = await this.createJiraEpic(spec);
143
+ await this.specManager.linkToExternal(specId, "jira", {
144
+ id: epic.key,
145
+ url: epic.url,
146
+ projectKey: this.config.projectKey,
147
+ domain: this.config.domain
148
+ });
149
+ }
150
+ const changes = await this.syncUserStories(epic.key, spec);
151
+ this.recordApiSuccess();
152
+ console.log("\u2705 Sync complete!");
153
+ return {
154
+ success: true,
155
+ specId,
156
+ provider: "jira",
157
+ externalId: epic.key,
158
+ url: epic.url,
159
+ changes
160
+ };
161
+ } catch (error) {
162
+ await this.recordApiFailure(error, "syncSpecToJira");
163
+ const axiosData = error?.response?.data;
164
+ const detail = axiosData ? JSON.stringify(axiosData) : "";
165
+ console.error("\u274C Error syncing to Jira:", error?.message || error, detail ? `
166
+ Response: ${detail}` : "");
118
167
  return {
119
168
  success: false,
120
169
  specId,
121
170
  provider: "jira",
122
- error: `Spec ${specId} not found`
171
+ error: error instanceof Error ? error.message : "Unknown error"
123
172
  };
124
173
  }
125
- const existingLink = spec.metadata.externalLinks?.jira;
126
- let epic;
127
- if (existingLink?.epicKey) {
128
- console.log(` Found existing Jira Epic ${existingLink.epicKey}`);
129
- epic = await this.updateJiraEpic(existingLink.epicKey, spec);
130
- } else {
131
- console.log(" Creating new Jira Epic...");
132
- epic = await this.createJiraEpic(spec);
133
- await this.specManager.linkToExternal(specId, "jira", {
134
- id: epic.key,
135
- url: epic.url,
136
- projectKey: this.config.projectKey,
137
- domain: this.config.domain
138
- });
139
- }
140
- const changes = await this.syncUserStories(epic.key, spec);
141
- this.recordApiSuccess();
142
- console.log("\u2705 Sync complete!");
143
- return {
144
- success: true,
145
- specId,
146
- provider: "jira",
147
- externalId: epic.key,
148
- url: epic.url,
149
- changes
150
- };
151
- } catch (error) {
152
- await this.recordApiFailure(error, "syncSpecToJira");
153
- const axiosData = error?.response?.data;
154
- const detail = axiosData ? JSON.stringify(axiosData) : "";
155
- console.error("\u274C Error syncing to Jira:", error?.message || error, detail ? `
156
- Response: ${detail}` : "");
157
- return {
158
- success: false,
159
- specId,
160
- provider: "jira",
161
- error: error instanceof Error ? error.message : "Unknown error"
162
- };
163
- }
164
174
  });
165
175
  }
166
176
  /**
@@ -170,60 +180,60 @@ class JiraSpecSync {
170
180
  console.log(`
171
181
  \u{1F504} Syncing FROM Jira to spec ${specId}...`);
172
182
  return this.withLock(async () => {
173
- this.checkCircuitBreaker();
174
- try {
175
- const spec = await this.specManager.loadSpec(specId);
176
- if (!spec) {
183
+ this.checkCircuitBreaker();
184
+ try {
185
+ const spec = await this.specManager.loadSpec(specId);
186
+ if (!spec) {
187
+ return {
188
+ success: false,
189
+ specId,
190
+ provider: "jira",
191
+ error: `Spec ${specId} not found`
192
+ };
193
+ }
194
+ const jiraLink = spec.metadata.externalLinks?.jira;
195
+ if (!jiraLink?.epicKey) {
196
+ return {
197
+ success: false,
198
+ specId,
199
+ provider: "jira",
200
+ error: "Spec not linked to Jira Epic"
201
+ };
202
+ }
203
+ const epic = await this.fetchJiraEpic(jiraLink.epicKey);
204
+ const conflicts = await this.detectConflicts(spec, epic);
205
+ if (conflicts.length === 0) {
206
+ console.log("\u2705 No conflicts - spec and Jira in sync");
207
+ return {
208
+ success: true,
209
+ specId,
210
+ provider: "jira",
211
+ externalId: epic.key,
212
+ url: epic.url
213
+ };
214
+ }
215
+ console.log(`\u26A0\uFE0F Detected ${conflicts.length} conflict(s)`);
216
+ await this.writeConflictReport(specId, conflicts);
217
+ await this.resolveConflicts(spec, conflicts);
218
+ console.log("\u2705 Sync FROM Jira complete!");
177
219
  return {
178
- success: false,
220
+ success: true,
179
221
  specId,
180
222
  provider: "jira",
181
- error: `Spec ${specId} not found`
223
+ externalId: epic.key,
224
+ url: epic.url,
225
+ conflicts
182
226
  };
183
- }
184
- const jiraLink = spec.metadata.externalLinks?.jira;
185
- if (!jiraLink?.epicKey) {
227
+ } catch (error) {
228
+ await this.recordApiFailure(error, "syncFromJira");
229
+ console.error("\u274C Error syncing FROM Jira:", error);
186
230
  return {
187
231
  success: false,
188
232
  specId,
189
233
  provider: "jira",
190
- error: "Spec not linked to Jira Epic"
234
+ error: error instanceof Error ? error.message : "Unknown error"
191
235
  };
192
236
  }
193
- const epic = await this.fetchJiraEpic(jiraLink.epicKey);
194
- const conflicts = await this.detectConflicts(spec, epic);
195
- if (conflicts.length === 0) {
196
- console.log("\u2705 No conflicts - spec and Jira in sync");
197
- return {
198
- success: true,
199
- specId,
200
- provider: "jira",
201
- externalId: epic.key,
202
- url: epic.url
203
- };
204
- }
205
- console.log(`\u26A0\uFE0F Detected ${conflicts.length} conflict(s)`);
206
- await this.writeConflictReport(specId, conflicts);
207
- await this.resolveConflicts(spec, conflicts);
208
- console.log("\u2705 Sync FROM Jira complete!");
209
- return {
210
- success: true,
211
- specId,
212
- provider: "jira",
213
- externalId: epic.key,
214
- url: epic.url,
215
- conflicts
216
- };
217
- } catch (error) {
218
- await this.recordApiFailure(error, "syncFromJira");
219
- console.error("\u274C Error syncing FROM Jira:", error);
220
- return {
221
- success: false,
222
- specId,
223
- provider: "jira",
224
- error: error instanceof Error ? error.message : "Unknown error"
225
- };
226
- }
227
237
  });
228
238
  }
229
239
  /**
@@ -1,8 +1,6 @@
1
1
  import axios from "axios";
2
2
  import { detectDeploymentType, getApiBaseUrl } from "./jira-deployment-detector.js";
3
3
  import { toCommentBody } from "./content-format-adapter.js";
4
- import { CircuitBreakerRegistry } from "../../../../../src/core/sync/circuit-breaker-registry.js";
5
- import { SyncRetryQueue } from "../../../../../src/core/sync/sync-retry-queue.js";
6
4
  import { SyncError } from "../../../../../src/core/errors/sync-error.js";
7
5
  import { LockManager } from "../../../../../src/utils/lock-manager.js";
8
6
  class JiraStatusSync {
@@ -30,6 +28,9 @@ class JiraStatusSync {
30
28
  }
31
29
  });
32
30
  }
31
+ /**
32
+ * Execute fn under file lock (if lockManager configured).
33
+ */
33
34
  async withLock(fn) {
34
35
  if (!this.lockManager) return fn();
35
36
  const acquired = await this.lockManager.acquire();
@@ -42,6 +43,10 @@ class JiraStatusSync {
42
43
  await this.lockManager.release();
43
44
  }
44
45
  }
46
+ /**
47
+ * Check circuit breaker before making API calls.
48
+ * Throws if breaker is open.
49
+ */
45
50
  checkCircuitBreaker() {
46
51
  if (!this.circuitBreakerRegistry) return;
47
52
  const breaker = this.circuitBreakerRegistry.get("jira");
@@ -49,10 +54,16 @@ class JiraStatusSync {
49
54
  throw new SyncError("jira", 0, "", "Circuit breaker open for jira");
50
55
  }
51
56
  }
57
+ /**
58
+ * Record success on circuit breaker.
59
+ */
52
60
  recordSuccess() {
53
61
  if (!this.circuitBreakerRegistry) return;
54
62
  this.circuitBreakerRegistry.get("jira").recordSuccess();
55
63
  }
64
+ /**
65
+ * Handle API error: record failure on circuit breaker, enqueue retry, throw SyncError.
66
+ */
56
67
  async handleApiError(error, operation) {
57
68
  const httpStatus = error?.response?.status ?? 0;
58
69
  const responseBody = JSON.stringify(error?.response?.data ?? "");
@@ -72,6 +83,9 @@ class JiraStatusSync {
72
83
  }
73
84
  throw new SyncError("jira", httpStatus, responseBody, detail);
74
85
  }
86
+ /**
87
+ * Initialize: detect deployment type and update client baseURL
88
+ */
75
89
  async init() {
76
90
  const deployment = await detectDeploymentType(this.domain, {
77
91
  email: this.client.defaults.auth?.username || "",
@@ -79,6 +93,12 @@ class JiraStatusSync {
79
93
  });
80
94
  this.client.defaults.baseURL = deployment.baseUrl;
81
95
  }
96
+ /**
97
+ * Get current status from JIRA issue
98
+ *
99
+ * @param issueKey - JIRA issue key (e.g., PROJ-123)
100
+ * @returns Current issue status
101
+ */
82
102
  async getStatus(issueKey) {
83
103
  return this.withLock(async () => {
84
104
  this.checkCircuitBreaker();
@@ -93,6 +113,19 @@ class JiraStatusSync {
93
113
  }
94
114
  });
95
115
  }
116
+ /**
117
+ * Update JIRA issue status via transitions
118
+ *
119
+ * JIRA requires using transitions to change status.
120
+ * Cannot directly set status field.
121
+ *
122
+ * Handles missing transitions gracefully by logging a warning
123
+ * instead of throwing an error.
124
+ *
125
+ * @param issueKey - JIRA issue key (e.g., PROJ-123)
126
+ * @param status - Desired status
127
+ * @returns true if transition succeeded, false if not available
128
+ */
96
129
  async updateStatus(issueKey, status) {
97
130
  return this.withLock(async () => {
98
131
  this.checkCircuitBreaker();
@@ -120,6 +153,13 @@ class JiraStatusSync {
120
153
  }
121
154
  });
122
155
  }
156
+ /**
157
+ * Post comment about status change to JIRA issue
158
+ *
159
+ * @param issueKey - JIRA issue key (e.g., PROJ-123)
160
+ * @param oldStatus - Previous SpecWeave status
161
+ * @param newStatus - New SpecWeave status
162
+ */
123
163
  async postStatusComment(issueKey, oldStatus, newStatus) {
124
164
  return this.withLock(async () => {
125
165
  this.checkCircuitBreaker();
@@ -142,6 +182,18 @@ _Synced from SpecWeave_`;
142
182
  }
143
183
  });
144
184
  }
185
+ /**
186
+ * Post AC progress comment with proper ADF formatting and dedup.
187
+ *
188
+ * Builds native ADF with:
189
+ * - Bold header showing completion percentage
190
+ * - Bullet list with checkmark/cross emojis per AC
191
+ * - Fingerprint marker to prevent duplicate comments
192
+ *
193
+ * @param issueKey - JIRA issue key (e.g., PROJ-123)
194
+ * @param acStates - Array of AC states with id, description, completed
195
+ * @returns true if comment was posted, false if skipped (duplicate)
196
+ */
145
197
  async postProgressComment(issueKey, acStates) {
146
198
  return this.withLock(async () => {
147
199
  this.checkCircuitBreaker();
@@ -109,8 +109,8 @@ AskUserQuestion({
109
109
  ## Delegation
110
110
 
111
111
  After architecture is ready, delegate to domain skills:
112
- - Frontend: `sw:architect` (implement UI directly; optional: install `frontend` vskill plugin for specialized skills)
113
- - Backend: `sw:architect` (implement API/services directly; optional: install domain-specific vskill plugins)
112
+ - Frontend: `sw:architect` (implement UI directly)
113
+ - Backend: `sw:architect` (implement API/services directly)
114
114
 
115
115
  Output: `plan.md` with architecture decisions and component breakdown.
116
116
 
@@ -9,8 +9,7 @@ MASTER SPEC (SOURCE OF TRUTH):
9
9
  SKILLS TO INVOKE:
10
10
  Skill({ skill: "sw:architect" }) // architecture and component design
11
11
  Skill({ skill: "sw:service-connect" }) // for external service setup
12
- // NOTE: Domain skills (frontend:architect, frontend:design) are optional vskill plugins.
13
- // If not installed, implement frontend work directly using code tools.
12
+ // Implement frontend work directly using code tools.
14
13
 
15
14
  FILE OWNERSHIP (WRITE access):
16
15
  src/components/**