specweave 1.0.489 → 1.0.491
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/README.md +5 -2
- package/dist/plugins/specweave-ado/lib/ado-client.d.ts +4 -0
- package/dist/plugins/specweave-ado/lib/ado-client.d.ts.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-client.js +15 -1
- package/dist/plugins/specweave-ado/lib/ado-client.js.map +1 -1
- package/package.json +1 -1
- package/plugins/specweave/PLUGIN.md +1 -0
- package/plugins/specweave/hooks/v2/guards/increment-existence-guard.sh +9 -3
- package/plugins/specweave/skills/code-reviewer/SKILL.md +401 -0
- package/plugins/specweave/skills/team-lead/SKILL.md +155 -4
- package/plugins/specweave-ado/lib/ado-client.js +13 -1
- package/plugins/specweave-ado/lib/ado-client.ts +19 -2
package/README.md
CHANGED
|
@@ -46,8 +46,11 @@ When you describe what you want, your AI routes internally to the right skill. Y
|
|
|
46
46
|
| "Build me X" / "Let's add Y" | `/sw:increment` → spec + plan + tasks |
|
|
47
47
|
| "Go ahead" / "Build it" | `/sw:auto` → autonomous execution |
|
|
48
48
|
| "Ship it" / "We're done" | `/sw:done` → quality gates + close |
|
|
49
|
-
| "Split this into teams" | `/sw:team-lead` → parallel agents |
|
|
50
|
-
| "
|
|
49
|
+
| "Split this into teams" | `/sw:team-lead` → parallel agents (implement mode) |
|
|
50
|
+
| "Brainstorm approaches for X" | `/sw:team-lead` → parallel perspectives (brainstorm mode) |
|
|
51
|
+
| "Plan X in parallel" | `/sw:team-lead` → PM + Architect agents (planning mode) |
|
|
52
|
+
| "Review the code" | `/sw:code-reviewer` → 6 parallel reviewers |
|
|
53
|
+
| "Grill the code" | `/sw:grill` → critical audit before close |
|
|
51
54
|
|
|
52
55
|
You can also invoke these directly for fine-grained control — but you rarely need to.
|
|
53
56
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @see https://learn.microsoft.com/en-us/rest/api/azure/devops/
|
|
8
8
|
*/
|
|
9
|
+
import { AdoRateLimiter } from './ado-rate-limiter.js';
|
|
9
10
|
export interface AdoConfig {
|
|
10
11
|
organization: string;
|
|
11
12
|
project: string;
|
|
@@ -13,6 +14,8 @@ export interface AdoConfig {
|
|
|
13
14
|
workItemType?: 'Epic' | 'Feature' | 'User Story';
|
|
14
15
|
areaPath?: string;
|
|
15
16
|
iterationPath?: string;
|
|
17
|
+
/** Optional rate limiter instance (shared across client instances) */
|
|
18
|
+
rateLimiter?: AdoRateLimiter;
|
|
16
19
|
}
|
|
17
20
|
export interface WorkItem {
|
|
18
21
|
id: number;
|
|
@@ -58,6 +61,7 @@ export declare class AdoClient {
|
|
|
58
61
|
private config;
|
|
59
62
|
private baseUrl;
|
|
60
63
|
private authHeader;
|
|
64
|
+
private rateLimiter?;
|
|
61
65
|
constructor(config: AdoConfig);
|
|
62
66
|
/**
|
|
63
67
|
* Create a new work item
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ado-client.d.ts","sourceRoot":"","sources":["../../../../plugins/specweave-ado/lib/ado-client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"ado-client.d.ts","sourceRoot":"","sources":["../../../../plugins/specweave-ado/lib/ado-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,6 +14,7 @@ export class AdoClient {
|
|
|
14
14
|
constructor(config) {
|
|
15
15
|
this.config = config;
|
|
16
16
|
this.baseUrl = `https://dev.azure.com/${config.organization}/${config.project}`;
|
|
17
|
+
this.rateLimiter = config.rateLimiter;
|
|
17
18
|
// Basic Auth: base64(":PAT")
|
|
18
19
|
this.authHeader = 'Basic ' + Buffer.from(`:${config.personalAccessToken}`).toString('base64');
|
|
19
20
|
}
|
|
@@ -193,6 +194,10 @@ export class AdoClient {
|
|
|
193
194
|
* Make HTTP request to ADO API
|
|
194
195
|
*/
|
|
195
196
|
async request(method, url, body, additionalHeaders) {
|
|
197
|
+
// Check rate limiter before making request
|
|
198
|
+
if (this.rateLimiter && !this.rateLimiter.consume()) {
|
|
199
|
+
throw new Error('ADO rate limit exceeded — try again later');
|
|
200
|
+
}
|
|
196
201
|
return new Promise((resolve, reject) => {
|
|
197
202
|
const urlObj = new URL(url);
|
|
198
203
|
const options = {
|
|
@@ -224,6 +229,13 @@ export class AdoClient {
|
|
|
224
229
|
}
|
|
225
230
|
}
|
|
226
231
|
else {
|
|
232
|
+
// Handle 429 Too Many Requests — apply Retry-After
|
|
233
|
+
if (res.statusCode === 429 && this.rateLimiter) {
|
|
234
|
+
const retryAfter = parseInt(res.headers['retry-after'], 10);
|
|
235
|
+
if (retryAfter > 0) {
|
|
236
|
+
this.rateLimiter.applyRetryAfter(retryAfter);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
227
239
|
let errorMessage = `ADO API error: ${res.statusCode} ${res.statusMessage}`;
|
|
228
240
|
try {
|
|
229
241
|
const errorData = JSON.parse(data);
|
|
@@ -234,7 +246,9 @@ export class AdoClient {
|
|
|
234
246
|
catch {
|
|
235
247
|
// Ignore JSON parse errors for error responses
|
|
236
248
|
}
|
|
237
|
-
|
|
249
|
+
const err = new Error(errorMessage);
|
|
250
|
+
err.status = res.statusCode;
|
|
251
|
+
reject(err);
|
|
238
252
|
}
|
|
239
253
|
});
|
|
240
254
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ado-client.js","sourceRoot":"","sources":["../../../../plugins/specweave-ado/lib/ado-client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"ado-client.js","sourceRoot":"","sources":["../../../../plugins/specweave-ado/lib/ado-client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AA0D1B,+EAA+E;AAC/E,sBAAsB;AACtB,+EAA+E;AAE/E,MAAM,OAAO,SAAS;IAMpB,YAAY,MAAiB;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,OAAO,GAAG,yBAAyB,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QAChF,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;QAEtC,6BAA6B;QAC7B,IAAI,CAAC,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,mBAAmB,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAChG,CAAC;IAED,6EAA6E;IAC7E,uBAAuB;IACvB,6EAA6E;IAE7E;;;;OAIG;IACH,KAAK,CAAC,cAAc,CAAC,OAA8B;QACjD,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC;QACxD,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,yBAAyB,YAAY,kBAAkB,CAAC;QAEnF,4BAA4B;QAC5B,MAAM,UAAU,GAAU;YACxB;gBACE,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,sBAAsB;gBAC5B,KAAK,EAAE,OAAO,CAAC,KAAK;aACrB;SACF,CAAC;QAEF,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;YACxB,UAAU,CAAC,IAAI,CAAC;gBACd,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,4BAA4B;gBAClC,KAAK,EAAE,QAAQ,OAAO,CAAC,WAAW,QAAQ;aAC3C,CAAC,CAAC;QACL,CAAC;QAED,IAAI,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YAC7C,UAAU,CAAC,IAAI,CAAC;gBACd,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,yBAAyB;gBAC/B,KAAK,EAAE,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ;aAChD,CAAC,CAAC;QACL,CAAC;QAED,IAAI,OAAO,CAAC,aAAa,IAAI,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;YACvD,UAAU,CAAC,IAAI,CAAC;gBACd,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,8BAA8B;gBACpC,KAAK,EAAE,OAAO,CAAC,aAAa,IAAI,IAAI,CAAC,MAAM,CAAC,aAAa;aAC1D,CAAC,CAAC;QACL,CAAC;QAED,IAAI,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5C,UAAU,CAAC,IAAI,CAAC;gBACd,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,qBAAqB;gBAC3B,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;aAC/B,CAAC,CAAC;QACL,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAW,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE;YACrE,cAAc,EAAE,6BAA6B;SAC9C,CAAC,CAAC;QAEH,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,WAAW,CAAC,EAAU;QAC1B,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,wBAAwB,EAAE,kBAAkB,CAAC;QACxE,OAAO,IAAI,CAAC,OAAO,CAAW,KAAK,EAAE,GAAG,CAAC,CAAC;IAC5C,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,cAAc,CAAC,EAAU,EAAE,OAA8B;QAC7D,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,wBAAwB,EAAE,kBAAkB,CAAC;QAExE,MAAM,UAAU,GAAU,EAAE,CAAC;QAE7B,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAClB,UAAU,CAAC,IAAI,CAAC;gBACd,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,sBAAsB;gBAC5B,KAAK,EAAE,OAAO,CAAC,KAAK;aACrB,CAAC,CAAC;QACL,CAAC;QAED,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAClB,UAAU,CAAC,IAAI,CAAC;gBACd,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,sBAAsB;gBAC5B,KAAK,EAAE,OAAO,CAAC,KAAK;aACrB,CAAC,CAAC;QACL,CAAC;QAED,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;YACxB,UAAU,CAAC,IAAI,CAAC;gBACd,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,4BAA4B;gBAClC,KAAK,EAAE,QAAQ,OAAO,CAAC,WAAW,QAAQ;aAC3C,CAAC,CAAC;QACL,CAAC;QAED,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,UAAU,CAAC,IAAI,CAAC;gBACd,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,qBAAqB;gBAC3B,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;aAC/B,CAAC,CAAC;QACL,CAAC;QAED,OAAO,IAAI,CAAC,OAAO,CAAW,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE;YACtD,cAAc,EAAE,6BAA6B;SAC9C,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,cAAc,CAAC,EAAU;QAC7B,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,wBAAwB,EAAE,kBAAkB,CAAC;QACxE,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IACpC,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,iBAAiB,CAAC,EAAU;QAChC,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,wBAAwB,EAAE,0DAA0D,CAAC;QAChH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAW,KAAK,EAAE,GAAG,CAAC,CAAC;QACtD,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC;YAClC,UAAU,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;SACxD,CAAC;IACJ,CAAC;IAED,6EAA6E;IAC7E,qBAAqB;IACrB,6EAA6E;IAE7E;;;;OAIG;IACH,KAAK,CAAC,UAAU,CAAC,UAAkB,EAAE,IAAY;QAC/C,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,wBAAwB,UAAU,qCAAqC,CAAC;QAEnG,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAkB,MAAM,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QAE5E,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,YAAY,CAAC,UAAkB,EAAE,GAAW,EAAE,OAAe;QACjE,MAAM,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,wBAAwB,UAAU,kBAAkB,CAAC;QAEnF,MAAM,UAAU,GAAG,CAAC;gBAClB,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,cAAc;gBACpB,KAAK,EAAE;oBACL,GAAG,EAAE,WAAW;oBAChB,GAAG,EAAE,GAAG;oBACR,UAAU,EAAE;wBACV,OAAO,EAAE,OAAO;qBACjB;iBACF;aACF,CAAC,CAAC;QAEH,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE;YAC9C,cAAc,EAAE,6BAA6B;SAC9C,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,WAAW,CAAC,UAAkB;QAClC,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,wBAAwB,UAAU,qCAAqC,CAAC;QAEnG,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAkC,KAAK,EAAE,GAAG,CAAC,CAAC;QAEjF,OAAO,QAAQ,CAAC,QAAQ,IAAI,EAAE,CAAC;IACjC,CAAC;IAED,6EAA6E;IAC7E,iBAAiB;IACjB,6EAA6E;IAE7E;;OAEG;IACK,KAAK,CAAC,OAAO,CACnB,MAAc,EACd,GAAW,EACX,IAAU,EACV,iBAA0C;QAE1C,2CAA2C;QAC3C,IAAI,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,EAAE,CAAC;YACpD,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAC/D,CAAC;QAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;YAE5B,MAAM,OAAO,GAAyB;gBACpC,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,IAAI,EAAE,GAAG;gBACT,IAAI,EAAE,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,MAAM;gBACrC,MAAM;gBACN,OAAO,EAAE;oBACP,eAAe,EAAE,IAAI,CAAC,UAAU;oBAChC,QAAQ,EAAE,kBAAkB;oBAC5B,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBACvD,GAAG,iBAAiB;iBACrB;gBACD,OAAO,EAAE,KAAK,EAAE,oBAAoB;aACrC,CAAC;YAEF,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBACzC,IAAI,IAAI,GAAG,EAAE,CAAC;gBAEd,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;oBACvB,IAAI,IAAI,KAAK,CAAC;gBAChB,CAAC,CAAC,CAAC;gBAEH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;oBACjB,IAAI,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,UAAU,IAAI,GAAG,IAAI,GAAG,CAAC,UAAU,GAAG,GAAG,EAAE,CAAC;wBACpE,IAAI,CAAC;4BACH,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;4BAC5C,OAAO,CAAC,MAAW,CAAC,CAAC;wBACvB,CAAC;wBAAC,OAAO,KAAK,EAAE,CAAC;4BACf,MAAM,CAAC,IAAI,KAAK,CAAC,kCAAkC,KAAK,EAAE,CAAC,CAAC,CAAC;wBAC/D,CAAC;oBACH,CAAC;yBAAM,CAAC;wBACN,mDAAmD;wBACnD,IAAI,GAAG,CAAC,UAAU,KAAK,GAAG,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;4BAC/C,MAAM,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,aAAa,CAAW,EAAE,EAAE,CAAC,CAAC;4BACtE,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;gCACnB,IAAI,CAAC,WAAW,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;4BAC/C,CAAC;wBACH,CAAC;wBAED,IAAI,YAAY,GAAG,kBAAkB,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,aAAa,EAAE,CAAC;wBAC3E,IAAI,CAAC;4BACH,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;4BACnC,IAAI,SAAS,CAAC,OAAO,EAAE,CAAC;gCACtB,YAAY,IAAI,MAAM,SAAS,CAAC,OAAO,EAAE,CAAC;4BAC5C,CAAC;wBACH,CAAC;wBAAC,MAAM,CAAC;4BACP,+CAA+C;wBACjD,CAAC;wBACD,MAAM,GAAG,GAAQ,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC;wBACzC,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,UAAU,CAAC;wBAC5B,MAAM,CAAC,GAAG,CAAC,CAAC;oBACd,CAAC;gBACH,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YAEH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;gBACxB,MAAM,CAAC,IAAI,KAAK,CAAC,wBAAwB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YAC7D,CAAC,CAAC,CAAC;YAEH,GAAG,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;gBACrB,GAAG,CAAC,OAAO,EAAE,CAAC;gBACd,MAAM,CAAC,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC,CAAC;YAC7D,CAAC,CAAC,CAAC;YAEH,IAAI,IAAI,EAAE,CAAC;gBACT,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;YAClC,CAAC;YAED,GAAG,CAAC,GAAG,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc;QAClB,IAAI,CAAC;YACH,oCAAoC;YACpC,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,0CAA0C,CAAC;YACtE,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YAC/B,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,uCAAuC;YACvC,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;gBAC3B,OAAO,CAAC,KAAK,CAAC,sCAAsC,EAAE;oBACpD,OAAO,EAAE,KAAK,CAAC,OAAO;oBACtB,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,YAAY;oBACtC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO;oBAC5B,OAAO,EAAE,IAAI,CAAC,OAAO;iBACtB,CAAC,CAAC;YACL,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF;AAED,+EAA+E;AAC/E,mBAAmB;AACnB,+EAA+E;AAE/E;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,SAA6B,EAAE;IAC7D,MAAM,UAAU,GAAc;QAC5B,YAAY,EAAE,MAAM,CAAC,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,EAAE;QACvE,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,EAAE;QACjE,mBAAmB,EAAE,MAAM,CAAC,mBAAmB,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,EAAE;QACrF,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,aAAa,EAAE,MAAM,CAAC,aAAa;KACpC,CAAC;IAEF,IAAI,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACrD,CAAC;IAED,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAChD,CAAC;IAED,IAAI,CAAC,UAAU,CAAC,mBAAmB,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAC9C,CAAC;IAED,OAAO,IAAI,SAAS,CAAC,UAAU,CAAC,CAAC;AACnC,CAAC;AAED,+EAA+E;AAC/E,UAAU;AACV,+EAA+E;AAE/E,eAAe,SAAS,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specweave",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.491",
|
|
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",
|
|
@@ -32,6 +32,7 @@ SpecWeave framework core plugin. Provides increment planning (PM, Architect, Tec
|
|
|
32
32
|
| self-validating-example | Example skill demonstrating self-validating REST API generation |
|
|
33
33
|
| lsp | Language Server Protocol support for code navigation |
|
|
34
34
|
| brainstorm | Multi-perspective ideation with cognitive lenses, persistent idea trees, and sw:increment handoff |
|
|
35
|
+
| code-reviewer | Elite multi-agent code review with parallel specialized reviewers for logic, security, performance, silent failures, type design, and spec compliance |
|
|
35
36
|
|
|
36
37
|
## Commands
|
|
37
38
|
|
|
@@ -10,11 +10,14 @@
|
|
|
10
10
|
# - review: PR reviews, code audits, architecture reviews — no increment needed
|
|
11
11
|
# - brainstorm: Multi-perspective ideation sessions — no increment needed
|
|
12
12
|
# - analysis: Codebase research, exploration, audits — no increment needed
|
|
13
|
+
# - research: Codebase investigation and web research — no increment needed
|
|
14
|
+
# - plan: Parallel planning with PM + Architect — creates its own increment
|
|
13
15
|
#
|
|
14
16
|
# MODE DETECTION (in priority order):
|
|
15
|
-
# 1. team_name prefix: review-*, brainstorm-*, analysis-*, audit-*
|
|
17
|
+
# 1. team_name prefix: review-*, brainstorm-*, analysis-*, audit-*, research-*, plan-*
|
|
16
18
|
# 2. description keywords: "review", "brainstorm", "analyze", "audit", "explore",
|
|
17
|
-
# "ideate", "pr #", "pull request", "code review",
|
|
19
|
+
# "ideate", "research", "planning", "pr #", "pull request", "code review",
|
|
20
|
+
# "architecture review"
|
|
18
21
|
# 3. Default: implementation mode (requires increment)
|
|
19
22
|
#
|
|
20
23
|
# DETECTION (implementation mode only):
|
|
@@ -59,7 +62,7 @@ DESCRIPTION_LC=$(echo "$DESCRIPTION" | tr '[:upper:]' '[:lower:]')
|
|
|
59
62
|
# Check 1: team_name prefix
|
|
60
63
|
NON_IMPL_MODE=false
|
|
61
64
|
case "$TEAM_NAME_LC" in
|
|
62
|
-
review-*|brainstorm-*|analysis-*|audit-*|explore-*|ideate-*)
|
|
65
|
+
review-*|brainstorm-*|analysis-*|audit-*|explore-*|ideate-*|research-*|plan-*)
|
|
63
66
|
NON_IMPL_MODE=true
|
|
64
67
|
;;
|
|
65
68
|
esac
|
|
@@ -84,6 +87,7 @@ if [[ "$NON_IMPL_MODE" == "false" ]]; then
|
|
|
84
87
|
"ideation"
|
|
85
88
|
"exploration"
|
|
86
89
|
"research"
|
|
90
|
+
"planning"
|
|
87
91
|
"perspectives"
|
|
88
92
|
"devil's advocate"
|
|
89
93
|
"pros and cons"
|
|
@@ -217,6 +221,8 @@ ALTERNATIVE: For non-implementation work, use a mode-prefixed team name:
|
|
|
217
221
|
- review-* → PR reviews, code audits (no increment needed)
|
|
218
222
|
- brainstorm-* → Ideation sessions (no increment needed)
|
|
219
223
|
- analysis-* → Research and exploration (no increment needed)
|
|
224
|
+
- research-* → Codebase research and investigation (no increment needed)
|
|
225
|
+
- plan-* → Parallel planning with PM + Architect (creates increment)
|
|
220
226
|
|
|
221
227
|
Workflow: /sw:increment → /sw:team-lead → /sw:done"
|
|
222
228
|
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Elite multi-agent code review system. Spawns parallel specialized reviewers for logic, security, performance, silent failures, type design, and spec compliance. Use when saying 'review code', 'code review', 'audit code', 'review PR', 'review changes', 'check code quality'."
|
|
3
|
+
argument-hint: "[--pr N] [--changes] [--increment NNNN] [--cross-repo] [path]"
|
|
4
|
+
context: fork
|
|
5
|
+
model: opus
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Code Reviewer
|
|
9
|
+
|
|
10
|
+
**Parallel multi-agent code review with specialized reviewers.**
|
|
11
|
+
|
|
12
|
+
Spawns up to 6 specialized reviewer agents that analyze code simultaneously, then aggregates findings into a unified report with deduplication and severity ranking.
|
|
13
|
+
|
|
14
|
+
## MANDATORY: Orchestrator Identity
|
|
15
|
+
|
|
16
|
+
**You are an ORCHESTRATOR. You do NOT review code yourself.**
|
|
17
|
+
|
|
18
|
+
- ALWAYS create a team and spawn reviewer agents via Task()
|
|
19
|
+
- NEVER read code and produce findings directly — that's what the reviewer agents do
|
|
20
|
+
- Your job: detect scope, route reviewers, aggregate results, produce report
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 0. Scope Detection
|
|
25
|
+
|
|
26
|
+
Parse arguments to determine WHAT to review.
|
|
27
|
+
|
|
28
|
+
### Argument Parsing
|
|
29
|
+
|
|
30
|
+
| Argument | Scope | How to Get Diff |
|
|
31
|
+
|----------|-------|-----------------|
|
|
32
|
+
| `--pr N` | Review PR #N | `gh pr diff N` |
|
|
33
|
+
| `--changes` | Uncommitted + staged changes | `git diff HEAD` |
|
|
34
|
+
| `--increment NNNN` | Changes from increment NNNN | `git diff` on files touched by increment |
|
|
35
|
+
| `--cross-repo` | All repos in umbrella | Per-repo `git diff` (see Section 5) |
|
|
36
|
+
| `path/to/dir` | Specific directory/file | Read files directly |
|
|
37
|
+
| *(no args)* | Auto-detect (see below) | Varies |
|
|
38
|
+
|
|
39
|
+
### Auto-Detection (no arguments)
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# 1. Check for open PR on current branch
|
|
43
|
+
PR_NUM=$(gh pr view --json number -q '.number' 2>/dev/null)
|
|
44
|
+
if [ -n "$PR_NUM" ]; then
|
|
45
|
+
SCOPE="pr"
|
|
46
|
+
REVIEW_TARGET="$PR_NUM"
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# 2. Check for uncommitted changes
|
|
50
|
+
if [ -z "$SCOPE" ]; then
|
|
51
|
+
CHANGES=$(git diff --stat HEAD 2>/dev/null)
|
|
52
|
+
if [ -n "$CHANGES" ]; then
|
|
53
|
+
SCOPE="changes"
|
|
54
|
+
REVIEW_TARGET="uncommitted changes"
|
|
55
|
+
fi
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# 3. Check for active increment
|
|
59
|
+
if [ -z "$SCOPE" ]; then
|
|
60
|
+
ACTIVE=$(find .specweave/increments -maxdepth 2 -name "metadata.json" \
|
|
61
|
+
-exec grep -l '"active"' {} \; 2>/dev/null | head -1)
|
|
62
|
+
if [ -n "$ACTIVE" ]; then
|
|
63
|
+
SCOPE="increment"
|
|
64
|
+
REVIEW_TARGET=$(dirname "$ACTIVE")
|
|
65
|
+
fi
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# 4. Fall back to whole project
|
|
69
|
+
if [ -z "$SCOPE" ]; then
|
|
70
|
+
SCOPE="project"
|
|
71
|
+
REVIEW_TARGET="."
|
|
72
|
+
fi
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Build File List
|
|
76
|
+
|
|
77
|
+
Once scope is determined, build the list of files to review:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
case "$SCOPE" in
|
|
81
|
+
pr) FILES=$(gh pr diff "$REVIEW_TARGET" --name-only) ;;
|
|
82
|
+
changes) FILES=$(git diff --name-only HEAD) ;;
|
|
83
|
+
increment) FILES=$(git log --name-only --pretty=format: -- "$REVIEW_TARGET") ;;
|
|
84
|
+
project) FILES=$(find src -type f -name "*.ts" -o -name "*.tsx" -o -name "*.js" 2>/dev/null) ;;
|
|
85
|
+
esac
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 1. Smart Reviewer Routing
|
|
91
|
+
|
|
92
|
+
Not all 6 reviewers are needed for every review. Route based on what files changed.
|
|
93
|
+
|
|
94
|
+
### Available Reviewers
|
|
95
|
+
|
|
96
|
+
| Reviewer | Agent Template | Specialization |
|
|
97
|
+
|----------|---------------|----------------|
|
|
98
|
+
| **Logic** | `agents/reviewer-logic.md` (from team-lead) | Bugs, edge cases, error handling |
|
|
99
|
+
| **Security** | `agents/reviewer-security.md` (from team-lead) | OWASP, auth, secrets, injection |
|
|
100
|
+
| **Performance** | `agents/reviewer-performance.md` (from team-lead) | N+1, memory, blocking ops |
|
|
101
|
+
| **Silent Failures** | `agents/reviewer-silent-failures.md` | Empty catches, swallowed errors |
|
|
102
|
+
| **Type Design** | `agents/reviewer-types.md` | Type quality, invariants, assertions |
|
|
103
|
+
| **Spec Compliance** | `agents/reviewer-spec-compliance.md` | AC verification, scope creep |
|
|
104
|
+
|
|
105
|
+
### Routing Rules
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
ALWAYS include:
|
|
109
|
+
- reviewer-logic (runs on every review)
|
|
110
|
+
- reviewer-security (runs on every review)
|
|
111
|
+
|
|
112
|
+
Include IF file patterns match:
|
|
113
|
+
- reviewer-types → *.ts, *.tsx files present
|
|
114
|
+
- reviewer-silent-failures → *.ts, *.tsx, *.js files with try/catch or .catch patterns
|
|
115
|
+
- reviewer-performance → database files (prisma/, *.sql), API routes, data-heavy code
|
|
116
|
+
- reviewer-spec-compliance → increment scope provided (--increment or active increment found)
|
|
117
|
+
|
|
118
|
+
Cap: --max-reviewers N (default: 6)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Routing Decision
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
REVIEWERS=("logic" "security") # Always
|
|
125
|
+
|
|
126
|
+
# TypeScript files → add type reviewer
|
|
127
|
+
if echo "$FILES" | grep -qE '\.(ts|tsx)$'; then
|
|
128
|
+
REVIEWERS+=("types")
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
# Code files → add silent failures
|
|
132
|
+
if echo "$FILES" | grep -qE '\.(ts|tsx|js|jsx)$'; then
|
|
133
|
+
REVIEWERS+=("silent-failures")
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
# Database/API → add performance
|
|
137
|
+
if echo "$FILES" | grep -qE '(prisma|\.sql|api/|routes/|controllers/)'; then
|
|
138
|
+
REVIEWERS+=("performance")
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
# Increment context → add spec compliance
|
|
142
|
+
if [ "$SCOPE" = "increment" ] || [ -n "$INCREMENT_PATH" ]; then
|
|
143
|
+
REVIEWERS+=("spec-compliance")
|
|
144
|
+
fi
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## 2. Team Creation and Agent Spawning
|
|
150
|
+
|
|
151
|
+
### Create Review Team
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
TeamCreate({
|
|
155
|
+
team_name: "review-[timestamp-or-slug]",
|
|
156
|
+
description: "Code review: [REVIEW_TARGET]"
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The `review-*` prefix bypasses the increment-existence-guard (reviews don't need increments).
|
|
161
|
+
|
|
162
|
+
### Read and Spawn Agents
|
|
163
|
+
|
|
164
|
+
For each selected reviewer:
|
|
165
|
+
|
|
166
|
+
1. **Determine template source**:
|
|
167
|
+
- `logic`, `security`, `performance` → read from team-lead's agents/ dir:
|
|
168
|
+
`skills/team-lead/agents/reviewer-{name}.md`
|
|
169
|
+
- `silent-failures`, `types`, `spec-compliance` → read from own agents/ dir:
|
|
170
|
+
`skills/code-reviewer/agents/reviewer-{name}.md`
|
|
171
|
+
|
|
172
|
+
2. **Replace placeholders**:
|
|
173
|
+
- `[REVIEW_TARGET]` → the detected scope description
|
|
174
|
+
- `[INCREMENT_PATH]` → increment path (for spec-compliance only)
|
|
175
|
+
- `[PR_NUMBER]` → PR number (if scope is PR)
|
|
176
|
+
|
|
177
|
+
3. **Spawn via Task()**:
|
|
178
|
+
```typescript
|
|
179
|
+
Task({
|
|
180
|
+
team_name: "review-[slug]",
|
|
181
|
+
name: "reviewer-[domain]",
|
|
182
|
+
subagent_type: "general-purpose",
|
|
183
|
+
mode: "bypassPermissions",
|
|
184
|
+
prompt: <replaced template content>
|
|
185
|
+
});
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**All reviewers spawn in parallel** — no dependency chain for reviews.
|
|
189
|
+
|
|
190
|
+
**CRITICAL**: Always use `mode: "bypassPermissions"` — reviewers cannot handle trust-folder prompts.
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## 3. Result Aggregation
|
|
195
|
+
|
|
196
|
+
### Collect REVIEW_COMPLETE Messages
|
|
197
|
+
|
|
198
|
+
Wait for all spawned reviewers to signal `REVIEW_COMPLETE:`. Track completion:
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
Reviewer Status:
|
|
202
|
+
logic: REVIEW_COMPLETE (5 findings)
|
|
203
|
+
security: REVIEW_COMPLETE (2 findings)
|
|
204
|
+
types: REVIEW_COMPLETE (8 findings)
|
|
205
|
+
silent-failures: REVIEW_COMPLETE (3 findings)
|
|
206
|
+
performance: (not spawned)
|
|
207
|
+
spec-compliance: (not spawned)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Timeout Handling
|
|
211
|
+
|
|
212
|
+
If a reviewer doesn't respond within a reasonable number of turns:
|
|
213
|
+
1. Send STATUS_CHECK message
|
|
214
|
+
2. If still no response after 2 more turns, declare stuck and proceed without it
|
|
215
|
+
3. Note the missing reviewer in the final report
|
|
216
|
+
|
|
217
|
+
### Deduplication
|
|
218
|
+
|
|
219
|
+
Multiple reviewers may flag the same issue (e.g., logic + silent-failures both catch an empty catch block):
|
|
220
|
+
- Group findings by file:line
|
|
221
|
+
- Merge findings at the same location into a single entry
|
|
222
|
+
- Keep the highest severity level
|
|
223
|
+
- Combine descriptions from different perspectives
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## 4. Report Generation
|
|
228
|
+
|
|
229
|
+
### Unified Report Format
|
|
230
|
+
|
|
231
|
+
```markdown
|
|
232
|
+
# Code Review Report
|
|
233
|
+
|
|
234
|
+
**Scope**: [REVIEW_TARGET]
|
|
235
|
+
**Date**: [YYYY-MM-DD]
|
|
236
|
+
**Reviewers**: [list of active reviewers]
|
|
237
|
+
|
|
238
|
+
## Summary
|
|
239
|
+
|
|
240
|
+
| Severity | Count |
|
|
241
|
+
|----------|-------|
|
|
242
|
+
| CRITICAL | N |
|
|
243
|
+
| HIGH | N |
|
|
244
|
+
| MEDIUM | N |
|
|
245
|
+
| LOW | N |
|
|
246
|
+
| INFO | N |
|
|
247
|
+
|
|
248
|
+
## Critical Findings
|
|
249
|
+
|
|
250
|
+
[Grouped findings at CRITICAL severity]
|
|
251
|
+
|
|
252
|
+
## High-Priority Findings
|
|
253
|
+
|
|
254
|
+
[Grouped findings at HIGH severity]
|
|
255
|
+
|
|
256
|
+
## Medium & Low Findings
|
|
257
|
+
|
|
258
|
+
[Grouped findings at MEDIUM and LOW severity]
|
|
259
|
+
|
|
260
|
+
## Per-File Summary
|
|
261
|
+
|
|
262
|
+
| File | Issues | Top Severity |
|
|
263
|
+
|------|--------|-------------|
|
|
264
|
+
| src/api/auth.ts | 3 | CRITICAL |
|
|
265
|
+
| src/utils/parse.ts | 1 | MEDIUM |
|
|
266
|
+
|
|
267
|
+
## Recommendations
|
|
268
|
+
|
|
269
|
+
1. [Top priority action item]
|
|
270
|
+
2. [Second priority action item]
|
|
271
|
+
...
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Write JSON Report
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
# If reviewing an increment
|
|
278
|
+
REPORT_PATH="[INCREMENT_PATH]/reports/code-review-$(date +%Y-%m-%d).json"
|
|
279
|
+
|
|
280
|
+
# Otherwise
|
|
281
|
+
REPORT_PATH=".specweave/reports/code-review-$(date +%Y-%m-%d).json"
|
|
282
|
+
|
|
283
|
+
mkdir -p "$(dirname "$REPORT_PATH")"
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Write structured JSON with all findings, metadata, and reviewer statuses.
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## 5. Cross-Repo Mode
|
|
291
|
+
|
|
292
|
+
When `--cross-repo` is specified or umbrella mode is detected:
|
|
293
|
+
|
|
294
|
+
### Detect Changed Repos
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
# Find repos with changes in umbrella
|
|
298
|
+
for repo_dir in repositories/*/*; do
|
|
299
|
+
if [ -d "$repo_dir/.git" ]; then
|
|
300
|
+
changes=$(cd "$repo_dir" && git diff --stat HEAD 2>/dev/null)
|
|
301
|
+
if [ -n "$changes" ]; then
|
|
302
|
+
CHANGED_REPOS+=("$repo_dir")
|
|
303
|
+
fi
|
|
304
|
+
fi
|
|
305
|
+
done
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Per-Repo Review
|
|
309
|
+
|
|
310
|
+
For each changed repo:
|
|
311
|
+
1. Determine files changed in that repo
|
|
312
|
+
2. Route reviewers based on those files
|
|
313
|
+
3. Spawn reviewer agents scoped to that repo
|
|
314
|
+
4. Prefix all findings with repo path
|
|
315
|
+
|
|
316
|
+
### Cross-Repo Integration Check
|
|
317
|
+
|
|
318
|
+
After per-repo reviews complete, check for cross-repo issues:
|
|
319
|
+
- Shared type definitions changed but consumers not updated
|
|
320
|
+
- API contract changes without corresponding client updates
|
|
321
|
+
- Version/dependency mismatches between repos
|
|
322
|
+
- Shared configuration drift
|
|
323
|
+
|
|
324
|
+
### Merged Report
|
|
325
|
+
|
|
326
|
+
Produce a single report with sections per repo:
|
|
327
|
+
|
|
328
|
+
```markdown
|
|
329
|
+
# Cross-Repo Code Review Report
|
|
330
|
+
|
|
331
|
+
## Repository: repositories/org/api-service
|
|
332
|
+
[findings for api-service]
|
|
333
|
+
|
|
334
|
+
## Repository: repositories/org/web-client
|
|
335
|
+
[findings for web-client]
|
|
336
|
+
|
|
337
|
+
## Cross-Repo Issues
|
|
338
|
+
[integration findings]
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## 6. Cleanup and Output
|
|
344
|
+
|
|
345
|
+
### Shutdown Agents
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
// Shutdown each reviewer
|
|
349
|
+
SendMessage({ type: "shutdown_request", recipient: "reviewer-logic", content: "Review complete" });
|
|
350
|
+
SendMessage({ type: "shutdown_request", recipient: "reviewer-security", content: "Review complete" });
|
|
351
|
+
// ... for each spawned reviewer
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### Destroy Team
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
TeamDelete();
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Present Results
|
|
361
|
+
|
|
362
|
+
1. Display the unified report to the user
|
|
363
|
+
2. Highlight CRITICAL and HIGH findings prominently
|
|
364
|
+
3. If reviewing an increment: offer to create tasks for critical findings
|
|
365
|
+
4. Report location of JSON report file
|
|
366
|
+
|
|
367
|
+
### Offer Follow-Up
|
|
368
|
+
|
|
369
|
+
```
|
|
370
|
+
Review complete. [N] findings across [M] files.
|
|
371
|
+
- [X] critical, [Y] high findings need attention
|
|
372
|
+
|
|
373
|
+
Report saved to: [REPORT_PATH]
|
|
374
|
+
|
|
375
|
+
Next steps:
|
|
376
|
+
- Fix critical issues before merging
|
|
377
|
+
- /sw:do to implement fixes (if increment exists)
|
|
378
|
+
- /sw:code-reviewer --changes to re-review after fixes
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
## 7. Troubleshooting
|
|
384
|
+
|
|
385
|
+
| Issue | Cause | Fix |
|
|
386
|
+
|-------|-------|-----|
|
|
387
|
+
| Reviewer stuck | Agent not responding | Send STATUS_CHECK, then shutdown after 2 turns |
|
|
388
|
+
| No files to review | Empty diff or wrong scope | Check git status, verify scope argument |
|
|
389
|
+
| Ghost review-* team | Previous review didn't clean up | TeamDelete by name before starting |
|
|
390
|
+
| Spec compliance skipped | No increment path found | Pass --increment NNNN explicitly |
|
|
391
|
+
| Cross-repo misses a repo | Repo has no .git or no changes | Check repo has uncommitted work |
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
|
|
395
|
+
## Related Skills
|
|
396
|
+
|
|
397
|
+
| Skill | Relationship |
|
|
398
|
+
|-------|-------------|
|
|
399
|
+
| `/sw:grill` | Grill is increment-scoped, runs during closure. Code-reviewer is general-purpose, runs anytime. |
|
|
400
|
+
| `/sw:team-lead --mode review` | Team-lead delegates review mode to this skill |
|
|
401
|
+
| `/sw:validate` | Rule-based validation (130+ checks). Code-reviewer is AI-powered analysis. |
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
2
|
+
description: Phase-agnostic orchestrator for parallel multi-agent work — brainstorm, plan, implement, review, research, or test. Auto-detects mode from intent. Use for implementation (3+ domains or 15+ tasks), brainstorming (multiple perspectives), parallel planning (PM + Architect), code review (delegates to /sw:code-reviewer), research (multiple topics), or testing (parallel test layers). Also use when user says "team setup", "parallel agents", "team lead", "agent teams", "brainstorm with agents", "plan in parallel", "review code", "research this".
|
|
3
3
|
hooks:
|
|
4
4
|
PreToolUse:
|
|
5
5
|
- matcher: TeamCreate
|
|
@@ -62,12 +62,164 @@ hooks:
|
|
|
62
62
|
| Option | Description | Default |
|
|
63
63
|
|--------|-------------|---------|
|
|
64
64
|
| `--dry-run` | Show proposed agent plan without launching | false |
|
|
65
|
+
| `--mode` | Force operating mode: `brainstorm`, `plan`, `implement`, `review`, `research`, `test` | auto-detect |
|
|
65
66
|
| `--domains` | Override domain detection (e.g., `--domains frontend,backend,testing`) | auto-detect |
|
|
66
67
|
| `--max-agents` | Maximum number of concurrent agents | 6 |
|
|
67
68
|
|
|
68
69
|
---
|
|
69
70
|
|
|
70
|
-
## 0. Increment Pre-Flight
|
|
71
|
+
## 0. Mode Detection (BEFORE Increment Pre-Flight)
|
|
72
|
+
|
|
73
|
+
**Detect operating mode FIRST. This determines the entire workflow path.**
|
|
74
|
+
|
|
75
|
+
### Detection Rules (priority order)
|
|
76
|
+
|
|
77
|
+
1. **Explicit flag**: `--mode brainstorm|plan|implement|review|research|test`
|
|
78
|
+
2. **team_name prefix**: `review-*`, `brainstorm-*`, `research-*`, `plan-*`, `test-*`
|
|
79
|
+
3. **Intent keywords** in the user's request:
|
|
80
|
+
|
|
81
|
+
| Keywords | Mode | Go To |
|
|
82
|
+
|----------|------|-------|
|
|
83
|
+
| "brainstorm", "ideate", "explore ideas", "what if", "pros and cons" | BRAINSTORM | Section 0a |
|
|
84
|
+
| "plan", "spec", "design", "architect", "define requirements" | PLANNING | Section 0b |
|
|
85
|
+
| "implement", "build", "code", "develop" *(or default)* | IMPLEMENTATION | Section 1 |
|
|
86
|
+
| "review", "audit", "check code", "review PR", "code quality" | REVIEW | Section 0c |
|
|
87
|
+
| "research", "investigate", "analyze", "explore codebase" | RESEARCH | Section 0d |
|
|
88
|
+
| "test", "write tests", "test strategy", "test coverage" | TESTING | Section 0e |
|
|
89
|
+
|
|
90
|
+
4. **Default**: IMPLEMENTATION mode if no keywords match.
|
|
91
|
+
|
|
92
|
+
### Mode Configuration
|
|
93
|
+
|
|
94
|
+
| Mode | Increment? | Agent Templates | Coordination | Output |
|
|
95
|
+
|------|-----------|-----------------|--------------|--------|
|
|
96
|
+
| BRAINSTORM | No | brainstorm-advocate, brainstorm-critic, brainstorm-pragmatist | Parallel → synthesize | Decision matrix |
|
|
97
|
+
| PLANNING | Creates one | pm, architect (+ optional security reviewer) | PM first → Architect parallel | spec.md, plan.md, tasks.md |
|
|
98
|
+
| IMPLEMENTATION | Required | backend, frontend, database, testing, security | Contract-first phases | Working code |
|
|
99
|
+
| REVIEW | Optional | Delegates to /sw:code-reviewer | Parallel | Review report |
|
|
100
|
+
| RESEARCH | No | researcher (1-3 instances) | Parallel → merge | Research report |
|
|
101
|
+
| TESTING | Required | testing (split by layer) | Parallel | Test suites |
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
### 0a. BRAINSTORM Mode
|
|
106
|
+
|
|
107
|
+
**team_name**: `brainstorm-{topic-slug}`
|
|
108
|
+
|
|
109
|
+
Skip increment pre-flight entirely. Brainstorm doesn't need a spec — it explores possibilities.
|
|
110
|
+
|
|
111
|
+
1. Create team: `TeamCreate({ team_name: "brainstorm-{slug}", description: "Brainstorm: {topic}" })`
|
|
112
|
+
2. Read agent templates from `agents/brainstorm-advocate.md`, `agents/brainstorm-critic.md`, `agents/brainstorm-pragmatist.md`
|
|
113
|
+
3. Replace `[BRAINSTORM_QUESTION]` with the user's question/topic
|
|
114
|
+
4. Spawn all 3 agents in parallel via `Task()` with `mode: "bypassPermissions"`
|
|
115
|
+
5. Collect `PERSPECTIVE_COMPLETE:` messages from all agents
|
|
116
|
+
6. Synthesize perspectives into a decision matrix:
|
|
117
|
+
- Compare approaches across dimensions (effort, risk, value, alignment)
|
|
118
|
+
- Highlight points of agreement and disagreement
|
|
119
|
+
- Provide a ranked recommendation
|
|
120
|
+
7. Offer handoff: "Ready to proceed? Run `/sw:increment` to formalize the chosen approach."
|
|
121
|
+
8. Cleanup: shutdown agents, TeamDelete
|
|
122
|
+
9. **STOP** — do not proceed to implementation sections
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
### 0b. PLANNING Mode
|
|
127
|
+
|
|
128
|
+
**team_name**: `plan-{feature-slug}`
|
|
129
|
+
|
|
130
|
+
Planning mode runs PM and Architect agents in parallel for richer, faster spec creation.
|
|
131
|
+
|
|
132
|
+
1. **Check for existing increment**:
|
|
133
|
+
- If increment exists: read it as context, agents will update/enhance its spec and plan
|
|
134
|
+
- If no increment: create one (folder + metadata.json only, agents will write spec/plan)
|
|
135
|
+
|
|
136
|
+
2. **Phase 1 — PM Agent** (upstream):
|
|
137
|
+
- Read `agents/pm.md`, replace `[INCREMENT_ID]`, `[MASTER_INCREMENT_PATH]`, `[FEATURE_DESCRIPTION]`
|
|
138
|
+
- Spawn via `Task()` with `mode: "bypassPermissions"`
|
|
139
|
+
- PM writes spec.md with user stories and ACs
|
|
140
|
+
- Wait for PM's `PLAN_READY:` or `COMPLETION:` message
|
|
141
|
+
|
|
142
|
+
3. **Phase 2 — Architect + Security** (parallel, after PM):
|
|
143
|
+
- Read `agents/architect.md`, replace placeholders, spawn Architect agent
|
|
144
|
+
- Optionally read `agents/reviewer-security.md` from team-lead agents, replace `[REVIEW_TARGET]` with the spec, spawn Security reviewer
|
|
145
|
+
- Both run in parallel: Architect writes plan.md, Security reviewer flags design-level vulnerabilities
|
|
146
|
+
- Wait for both `COMPLETION:` messages
|
|
147
|
+
|
|
148
|
+
4. **Post-planning**:
|
|
149
|
+
- Run `specweave sync-living-docs {increment-id}` to sync external tools
|
|
150
|
+
- Present the spec + plan summary to the user
|
|
151
|
+
- Recommend execution strategy: `/sw:do`, `/sw:auto`, or `/sw:team-lead` (implementation mode)
|
|
152
|
+
|
|
153
|
+
5. Cleanup: shutdown agents, TeamDelete
|
|
154
|
+
6. **STOP** — do not proceed to implementation sections
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
### 0c. REVIEW Mode
|
|
159
|
+
|
|
160
|
+
**Delegates entirely to `/sw:code-reviewer`.**
|
|
161
|
+
|
|
162
|
+
Team-lead does NOT spawn its own reviewer agents for review mode. The code-reviewer skill handles its own orchestration with 6 specialized reviewers.
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
Skill({ skill: "sw:code-reviewer", args: "<user's review target or flags>" })
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Pass through any arguments the user provided (--pr N, --changes, --cross-repo, path).
|
|
169
|
+
|
|
170
|
+
**STOP** after the skill completes — do not proceed to implementation sections.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
### 0d. RESEARCH Mode
|
|
175
|
+
|
|
176
|
+
**team_name**: `research-{topic-slug}`
|
|
177
|
+
|
|
178
|
+
Skip increment pre-flight. Research is exploratory — no spec needed.
|
|
179
|
+
|
|
180
|
+
1. Create team: `TeamCreate({ team_name: "research-{slug}", description: "Research: {topic}" })`
|
|
181
|
+
2. **Determine research agents**:
|
|
182
|
+
- Single topic: spawn 1 researcher from `agents/researcher.md`
|
|
183
|
+
- Multi-faceted topic: spawn 2-3 researchers with different scopes
|
|
184
|
+
(e.g., "research auth" → one agent on OAuth providers, one on session management, one on security best practices)
|
|
185
|
+
3. Replace `[RESEARCH_TOPIC]` and `[RESEARCH_SCOPE]` in each agent prompt
|
|
186
|
+
4. Spawn all researchers in parallel via `Task()` with `mode: "bypassPermissions"`
|
|
187
|
+
5. Collect `RESEARCH_COMPLETE:` messages
|
|
188
|
+
6. Merge findings into a unified research report:
|
|
189
|
+
- Cross-reference findings between agents
|
|
190
|
+
- Resolve contradictions
|
|
191
|
+
- Produce ranked recommendations
|
|
192
|
+
7. Offer handoff: `/sw:increment` (to act on findings) or `/sw:brainstorm` (to explore approaches)
|
|
193
|
+
8. Cleanup: shutdown agents, TeamDelete
|
|
194
|
+
9. **STOP** — do not proceed to implementation sections
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
### 0e. TESTING Mode
|
|
199
|
+
|
|
200
|
+
**team_name**: `test-{increment-id}`
|
|
201
|
+
|
|
202
|
+
Testing mode requires an increment (it needs to know WHAT to test).
|
|
203
|
+
|
|
204
|
+
1. **Verify increment exists** (same as implementation mode — see below)
|
|
205
|
+
2. Create team: `TeamCreate({ team_name: "test-{id}", description: "Testing: {increment}" })`
|
|
206
|
+
3. Spawn testing agents split by layer:
|
|
207
|
+
- **Unit test agent**: read `agents/testing.md`, override scope to unit tests only
|
|
208
|
+
- **E2E test agent**: read `agents/testing.md`, override scope to E2E tests only
|
|
209
|
+
- Split scope via the agent prompt, not via separate templates
|
|
210
|
+
4. Spawn both in parallel via `Task()` with `mode: "bypassPermissions"`
|
|
211
|
+
5. Collect `COMPLETION:` messages
|
|
212
|
+
6. Run test suites to verify: `npx vitest run` + `npx playwright test`
|
|
213
|
+
7. Report results: pass/fail counts, coverage, uncovered ACs
|
|
214
|
+
8. Cleanup: shutdown agents, TeamDelete
|
|
215
|
+
9. **STOP** — do not proceed to implementation sections
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## 0.5. Increment Pre-Flight (IMPLEMENTATION and TESTING modes only)
|
|
220
|
+
|
|
221
|
+
**This section applies only to IMPLEMENTATION mode (default) and TESTING mode.**
|
|
222
|
+
All other modes handle their own increment logic (or skip it entirely) in Section 0a-0e above.
|
|
71
223
|
|
|
72
224
|
The team-lead works best with an increment (spec.md, plan.md, tasks.md) but can also run **without one** in free-form mode.
|
|
73
225
|
|
|
@@ -75,10 +227,9 @@ The team-lead works best with an increment (spec.md, plan.md, tasks.md) but can
|
|
|
75
227
|
|
|
76
228
|
**Free-form mode** (no increment needed) applies when:
|
|
77
229
|
- `SPECWEAVE_NO_INCREMENT=1` is set (via `specweave team --no-increment`)
|
|
78
|
-
- The team_name uses a non-implementation prefix (`review-*`, `brainstorm-*`, `analysis-*`)
|
|
79
230
|
- The user explicitly opted out of increment creation
|
|
80
231
|
|
|
81
|
-
In free-form mode: **skip the rest of Section 0** and proceed directly to Step 1. Agents will work from the natural language description instead of a spec. Note: without a spec, `/sw:done` closure is not available — the team-lead simply coordinates agent completion.
|
|
232
|
+
In free-form mode: **skip the rest of Section 0.5** and proceed directly to Step 1. Agents will work from the natural language description instead of a spec. Note: without a spec, `/sw:done` closure is not available — the team-lead simply coordinates agent completion.
|
|
82
233
|
|
|
83
234
|
### Standard mode: Verify increment exists
|
|
84
235
|
|
|
@@ -3,6 +3,7 @@ class AdoClient {
|
|
|
3
3
|
constructor(config) {
|
|
4
4
|
this.config = config;
|
|
5
5
|
this.baseUrl = `https://dev.azure.com/${config.organization}/${config.project}`;
|
|
6
|
+
this.rateLimiter = config.rateLimiter;
|
|
6
7
|
this.authHeader = "Basic " + Buffer.from(`:${config.personalAccessToken}`).toString("base64");
|
|
7
8
|
}
|
|
8
9
|
// ==========================================================================
|
|
@@ -180,6 +181,9 @@ class AdoClient {
|
|
|
180
181
|
* Make HTTP request to ADO API
|
|
181
182
|
*/
|
|
182
183
|
async request(method, url, body, additionalHeaders) {
|
|
184
|
+
if (this.rateLimiter && !this.rateLimiter.consume()) {
|
|
185
|
+
throw new Error("ADO rate limit exceeded \u2014 try again later");
|
|
186
|
+
}
|
|
183
187
|
return new Promise((resolve, reject) => {
|
|
184
188
|
const urlObj = new URL(url);
|
|
185
189
|
const options = {
|
|
@@ -210,6 +214,12 @@ class AdoClient {
|
|
|
210
214
|
reject(new Error(`Failed to parse JSON response: ${error}`));
|
|
211
215
|
}
|
|
212
216
|
} else {
|
|
217
|
+
if (res.statusCode === 429 && this.rateLimiter) {
|
|
218
|
+
const retryAfter = parseInt(res.headers["retry-after"], 10);
|
|
219
|
+
if (retryAfter > 0) {
|
|
220
|
+
this.rateLimiter.applyRetryAfter(retryAfter);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
213
223
|
let errorMessage = `ADO API error: ${res.statusCode} ${res.statusMessage}`;
|
|
214
224
|
try {
|
|
215
225
|
const errorData = JSON.parse(data);
|
|
@@ -218,7 +228,9 @@ class AdoClient {
|
|
|
218
228
|
}
|
|
219
229
|
} catch {
|
|
220
230
|
}
|
|
221
|
-
|
|
231
|
+
const err = new Error(errorMessage);
|
|
232
|
+
err.status = res.statusCode;
|
|
233
|
+
reject(err);
|
|
222
234
|
}
|
|
223
235
|
});
|
|
224
236
|
});
|
|
@@ -73,11 +73,13 @@ export class AdoClient {
|
|
|
73
73
|
private config: AdoConfig;
|
|
74
74
|
private baseUrl: string;
|
|
75
75
|
private authHeader: string;
|
|
76
|
+
private rateLimiter?: AdoRateLimiter;
|
|
76
77
|
|
|
77
78
|
constructor(config: AdoConfig) {
|
|
78
79
|
this.config = config;
|
|
79
80
|
this.baseUrl = `https://dev.azure.com/${config.organization}/${config.project}`;
|
|
80
|
-
|
|
81
|
+
this.rateLimiter = config.rateLimiter;
|
|
82
|
+
|
|
81
83
|
// Basic Auth: base64(":PAT")
|
|
82
84
|
this.authHeader = 'Basic ' + Buffer.from(`:${config.personalAccessToken}`).toString('base64');
|
|
83
85
|
}
|
|
@@ -293,6 +295,11 @@ export class AdoClient {
|
|
|
293
295
|
body?: any,
|
|
294
296
|
additionalHeaders?: Record<string, string>
|
|
295
297
|
): Promise<T> {
|
|
298
|
+
// Check rate limiter before making request
|
|
299
|
+
if (this.rateLimiter && !this.rateLimiter.consume()) {
|
|
300
|
+
throw new Error('ADO rate limit exceeded — try again later');
|
|
301
|
+
}
|
|
302
|
+
|
|
296
303
|
return new Promise((resolve, reject) => {
|
|
297
304
|
const urlObj = new URL(url);
|
|
298
305
|
|
|
@@ -326,6 +333,14 @@ export class AdoClient {
|
|
|
326
333
|
reject(new Error(`Failed to parse JSON response: ${error}`));
|
|
327
334
|
}
|
|
328
335
|
} else {
|
|
336
|
+
// Handle 429 Too Many Requests — apply Retry-After
|
|
337
|
+
if (res.statusCode === 429 && this.rateLimiter) {
|
|
338
|
+
const retryAfter = parseInt(res.headers['retry-after'] as string, 10);
|
|
339
|
+
if (retryAfter > 0) {
|
|
340
|
+
this.rateLimiter.applyRetryAfter(retryAfter);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
329
344
|
let errorMessage = `ADO API error: ${res.statusCode} ${res.statusMessage}`;
|
|
330
345
|
try {
|
|
331
346
|
const errorData = JSON.parse(data);
|
|
@@ -335,7 +350,9 @@ export class AdoClient {
|
|
|
335
350
|
} catch {
|
|
336
351
|
// Ignore JSON parse errors for error responses
|
|
337
352
|
}
|
|
338
|
-
|
|
353
|
+
const err: any = new Error(errorMessage);
|
|
354
|
+
err.status = res.statusCode;
|
|
355
|
+
reject(err);
|
|
339
356
|
}
|
|
340
357
|
});
|
|
341
358
|
});
|