specweave 1.0.488 → 1.0.490
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 +1 -1
- package/dist/plugins/specweave-ado/lib/ado-ac-checkbox-sync.d.ts +13 -5
- package/dist/plugins/specweave-ado/lib/ado-ac-checkbox-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-ac-checkbox-sync.js +28 -26
- package/dist/plugins/specweave-ado/lib/ado-ac-checkbox-sync.js.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-client.d.ts +14 -0
- package/dist/plugins/specweave-ado/lib/ado-client.d.ts.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-client.js +29 -1
- package/dist/plugins/specweave-ado/lib/ado-client.js.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-pull-sync.d.ts +35 -0
- package/dist/plugins/specweave-ado/lib/ado-pull-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-ado/lib/ado-pull-sync.js +53 -0
- package/dist/plugins/specweave-ado/lib/ado-pull-sync.js.map +1 -0
- package/dist/plugins/specweave-ado/lib/ado-rate-limiter.d.ts +46 -0
- package/dist/plugins/specweave-ado/lib/ado-rate-limiter.d.ts.map +1 -0
- package/dist/plugins/specweave-ado/lib/ado-rate-limiter.js +65 -0
- package/dist/plugins/specweave-ado/lib/ado-rate-limiter.js.map +1 -0
- package/dist/plugins/specweave-ado/lib/ado-spec-sync.d.ts +7 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-sync.js +25 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-sync.js.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-status-sync.d.ts +17 -1
- package/dist/plugins/specweave-ado/lib/ado-status-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-status-sync.js +51 -9
- package/dist/plugins/specweave-ado/lib/ado-status-sync.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-client-v2.js +1 -1
- package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-push-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-push-sync.js +15 -3
- package/dist/plugins/specweave-github/lib/github-push-sync.js.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-spec-sync.d.ts +31 -1
- package/dist/plugins/specweave-jira/lib/jira-spec-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-spec-sync.js +170 -97
- package/dist/plugins/specweave-jira/lib/jira-spec-sync.js.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-status-sync.d.ts +36 -1
- package/dist/plugins/specweave-jira/lib/jira-status-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-status-sync.js +185 -82
- package/dist/plugins/specweave-jira/lib/jira-status-sync.js.map +1 -1
- package/dist/src/adapters/adapter-loader.d.ts.map +1 -1
- package/dist/src/adapters/adapter-loader.js +8 -2
- package/dist/src/adapters/adapter-loader.js.map +1 -1
- package/dist/src/adapters/codex/adapter.d.ts.map +1 -1
- package/dist/src/adapters/codex/adapter.js +1 -0
- package/dist/src/adapters/codex/adapter.js.map +1 -1
- package/dist/src/adapters/cursor/adapter.d.ts.map +1 -1
- package/dist/src/adapters/cursor/adapter.js +1 -0
- package/dist/src/adapters/cursor/adapter.js.map +1 -1
- package/dist/src/adapters/generic/adapter.d.ts +6 -3
- package/dist/src/adapters/generic/adapter.d.ts.map +1 -1
- package/dist/src/adapters/generic/adapter.js +53 -47
- package/dist/src/adapters/generic/adapter.js.map +1 -1
- package/dist/src/adapters/kimi/adapter.d.ts +21 -0
- package/dist/src/adapters/kimi/adapter.d.ts.map +1 -0
- package/dist/src/adapters/kimi/adapter.js +57 -0
- package/dist/src/adapters/kimi/adapter.js.map +1 -0
- package/dist/src/adapters/opencode/adapter.d.ts +24 -0
- package/dist/src/adapters/opencode/adapter.d.ts.map +1 -0
- package/dist/src/adapters/opencode/adapter.js +71 -0
- package/dist/src/adapters/opencode/adapter.js.map +1 -0
- package/dist/src/adapters/registry.yaml +59 -0
- package/dist/src/adapters/trae/adapter.d.ts +21 -0
- package/dist/src/adapters/trae/adapter.d.ts.map +1 -0
- package/dist/src/adapters/trae/adapter.js +64 -0
- package/dist/src/adapters/trae/adapter.js.map +1 -0
- package/dist/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +156 -5
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/update-instructions.d.ts.map +1 -1
- package/dist/src/cli/commands/update-instructions.js +10 -0
- package/dist/src/cli/commands/update-instructions.js.map +1 -1
- package/dist/src/cli/helpers/init/index.d.ts +1 -0
- package/dist/src/cli/helpers/init/index.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/index.js +2 -0
- package/dist/src/cli/helpers/init/index.js.map +1 -1
- package/dist/src/cli/helpers/init/next-steps.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/next-steps.js +52 -0
- package/dist/src/cli/helpers/init/next-steps.js.map +1 -1
- package/dist/src/cli/helpers/init/skill-creator-installer.d.ts +24 -0
- package/dist/src/cli/helpers/init/skill-creator-installer.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/skill-creator-installer.js +54 -0
- package/dist/src/cli/helpers/init/skill-creator-installer.js.map +1 -0
- package/dist/src/core/ado-description-updater.d.ts +22 -0
- package/dist/src/core/ado-description-updater.d.ts.map +1 -0
- package/dist/src/core/ado-description-updater.js +46 -0
- package/dist/src/core/ado-description-updater.js.map +1 -0
- package/dist/src/core/closure-dispatcher.d.ts +96 -0
- package/dist/src/core/closure-dispatcher.d.ts.map +1 -0
- package/dist/src/core/closure-dispatcher.js +116 -0
- package/dist/src/core/closure-dispatcher.js.map +1 -0
- package/dist/src/core/config/types.d.ts +2 -0
- package/dist/src/core/config/types.d.ts.map +1 -1
- package/dist/src/core/config/types.js.map +1 -1
- package/dist/src/core/errors/sync-error.d.ts +12 -0
- package/dist/src/core/errors/sync-error.d.ts.map +1 -0
- package/dist/src/core/errors/sync-error.js +19 -0
- package/dist/src/core/errors/sync-error.js.map +1 -0
- package/dist/src/core/skill-gen/rule-collector.d.ts +28 -0
- package/dist/src/core/skill-gen/rule-collector.d.ts.map +1 -0
- package/dist/src/core/skill-gen/rule-collector.js +112 -0
- package/dist/src/core/skill-gen/rule-collector.js.map +1 -0
- package/dist/src/core/skill-gen/signal-collector.d.ts +2 -1
- package/dist/src/core/skill-gen/signal-collector.d.ts.map +1 -1
- package/dist/src/core/skill-gen/signal-collector.js +21 -5
- package/dist/src/core/skill-gen/signal-collector.js.map +1 -1
- package/dist/src/core/sync/persistent-circuit-breaker.d.ts +22 -0
- package/dist/src/core/sync/persistent-circuit-breaker.d.ts.map +1 -0
- package/dist/src/core/sync/persistent-circuit-breaker.js +65 -0
- package/dist/src/core/sync/persistent-circuit-breaker.js.map +1 -0
- package/dist/src/core/sync/retry-wrapper.d.ts +13 -0
- package/dist/src/core/sync/retry-wrapper.d.ts.map +1 -0
- package/dist/src/core/sync/retry-wrapper.js +37 -0
- package/dist/src/core/sync/retry-wrapper.js.map +1 -0
- package/dist/src/importers/ac-parser.d.ts +27 -0
- package/dist/src/importers/ac-parser.d.ts.map +1 -0
- package/dist/src/importers/ac-parser.js +47 -0
- package/dist/src/importers/ac-parser.js.map +1 -0
- package/dist/src/sync/types.d.ts +8 -0
- package/dist/src/sync/types.d.ts.map +1 -1
- package/dist/src/sync/types.js +12 -0
- package/dist/src/sync/types.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/code-reviewer/agents/reviewer-silent-failures.md +65 -0
- package/plugins/specweave/skills/code-reviewer/agents/reviewer-spec-compliance.md +83 -0
- package/plugins/specweave/skills/code-reviewer/agents/reviewer-types.md +68 -0
- package/plugins/specweave/skills/skill-gen/SKILL.md +20 -3
- package/plugins/specweave/skills/team-lead/SKILL.md +155 -4
- package/plugins/specweave/skills/team-lead/agents/architect.md +52 -0
- package/plugins/specweave/skills/team-lead/agents/pm.md +50 -0
- package/plugins/specweave/skills/team-lead/agents/researcher.md +64 -0
- package/plugins/specweave-ado/lib/ado-ac-checkbox-sync.js +23 -21
- package/plugins/specweave-ado/lib/ado-ac-checkbox-sync.ts +37 -29
- package/plugins/specweave-ado/lib/ado-client.js +27 -1
- package/plugins/specweave-ado/lib/ado-client.ts +37 -2
- package/plugins/specweave-ado/lib/ado-pull-sync.js +35 -0
- package/plugins/specweave-ado/lib/ado-pull-sync.ts +74 -0
- package/plugins/specweave-ado/lib/ado-rate-limiter.js +56 -0
- package/plugins/specweave-ado/lib/ado-rate-limiter.ts +86 -0
- package/plugins/specweave-ado/lib/ado-spec-sync.js +25 -1
- package/plugins/specweave-ado/lib/ado-spec-sync.ts +32 -2
- package/plugins/specweave-ado/lib/ado-status-sync.js +52 -14
- package/plugins/specweave-ado/lib/ado-status-sync.ts +64 -16
- package/plugins/specweave-github/lib/github-client-v2.ts +1 -1
- package/plugins/specweave-github/lib/github-push-sync.js +11 -3
- package/plugins/specweave-github/lib/github-push-sync.ts +16 -3
- package/plugins/specweave-jira/lib/jira-spec-sync.js +60 -1
- package/plugins/specweave-jira/lib/jira-spec-sync.ts +93 -1
- package/plugins/specweave-jira/lib/jira-status-sync.js +151 -109
- package/plugins/specweave-jira/lib/jira-status-sync.ts +161 -39
|
@@ -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
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
You are the ARCHITECT PLANNING agent for increment [INCREMENT_ID].
|
|
2
|
+
|
|
3
|
+
MASTER SPEC (SOURCE OF TRUTH):
|
|
4
|
+
The feature is specified in [MASTER_INCREMENT_PATH]/spec.md.
|
|
5
|
+
Read the spec BEFORE designing anything. Your architecture MUST satisfy all ACs.
|
|
6
|
+
|
|
7
|
+
MISSION:
|
|
8
|
+
Produce plan.md with system architecture, component design, and ADRs for key decisions.
|
|
9
|
+
You own the HOW — defining the technical approach. You work in parallel with
|
|
10
|
+
the Security reviewer who validates your design for vulnerabilities.
|
|
11
|
+
|
|
12
|
+
SKILLS TO INVOKE:
|
|
13
|
+
Skill({ skill: "sw:architect" })
|
|
14
|
+
|
|
15
|
+
FILE OWNERSHIP (WRITE access):
|
|
16
|
+
[MASTER_INCREMENT_PATH]/plan.md
|
|
17
|
+
.specweave/docs/internal/architecture/adr/ (new ADRs only)
|
|
18
|
+
|
|
19
|
+
READ ACCESS: Any file in the repository
|
|
20
|
+
|
|
21
|
+
UPSTREAM DEPENDENCY:
|
|
22
|
+
Wait for the PM agent to signal PLAN_READY or COMPLETION before starting.
|
|
23
|
+
You need spec.md to exist with user stories and ACs before you can design.
|
|
24
|
+
|
|
25
|
+
WORKFLOW:
|
|
26
|
+
1. Read spec.md at [MASTER_INCREMENT_PATH]/spec.md
|
|
27
|
+
2. Explore the codebase to understand existing architecture, patterns, and tech stack
|
|
28
|
+
3. Check existing ADRs at .specweave/docs/internal/architecture/adr/
|
|
29
|
+
4. Design system architecture:
|
|
30
|
+
- Component boundaries and responsibilities
|
|
31
|
+
- Data flow and state management
|
|
32
|
+
- API contracts and integration points
|
|
33
|
+
- Error handling strategy
|
|
34
|
+
- Performance considerations
|
|
35
|
+
5. Write ADRs for significant architectural decisions (use ADR template format)
|
|
36
|
+
6. Write plan.md to [MASTER_INCREMENT_PATH]/plan.md
|
|
37
|
+
7. Signal architecture decisions:
|
|
38
|
+
SendMessage({ type: "message", recipient: "team-lead",
|
|
39
|
+
content: "CONTRACT_READY: Architecture defined in plan.md.\nComponents: [list]\nKey patterns: [e.g., CQRS, event-driven]\nADRs created: [list or 'none']\nTech stack: [decisions]",
|
|
40
|
+
summary: "Architect: plan.md ready with architecture" })
|
|
41
|
+
8. Signal COMPLETION:
|
|
42
|
+
SendMessage({ type: "message", recipient: "team-lead",
|
|
43
|
+
content: "COMPLETION: plan.md finalized.\nComponents: [count]\nADRs: [count]\nKey risk: [biggest concern]",
|
|
44
|
+
summary: "Architect agent: plan complete" })
|
|
45
|
+
|
|
46
|
+
RULES:
|
|
47
|
+
- WRITE only plan.md and ADRs — do not modify spec.md or create tasks.md
|
|
48
|
+
- Every architectural decision must be justified (not just "use X because it's popular")
|
|
49
|
+
- Consider scalability, maintainability, testability, and security
|
|
50
|
+
- Reference existing codebase patterns — don't propose patterns alien to the project
|
|
51
|
+
- Flag technical risks and mitigation strategies
|
|
52
|
+
- Keep plan.md actionable — an implementer should be able to code from it
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
You are the PM PLANNING agent for increment [INCREMENT_ID].
|
|
2
|
+
|
|
3
|
+
FEATURE DESCRIPTION: [FEATURE_DESCRIPTION]
|
|
4
|
+
|
|
5
|
+
MASTER INCREMENT PATH: [MASTER_INCREMENT_PATH]
|
|
6
|
+
|
|
7
|
+
MISSION:
|
|
8
|
+
Produce a comprehensive spec.md with user stories, acceptance criteria, and scope
|
|
9
|
+
boundaries. You own the WHAT — defining what the feature does and how success is
|
|
10
|
+
measured. You work in parallel with the Architect agent who owns the HOW.
|
|
11
|
+
|
|
12
|
+
SKILLS TO INVOKE:
|
|
13
|
+
Skill({ skill: "sw:pm" })
|
|
14
|
+
|
|
15
|
+
FILE OWNERSHIP (WRITE access):
|
|
16
|
+
[MASTER_INCREMENT_PATH]/spec.md
|
|
17
|
+
|
|
18
|
+
READ ACCESS: Any file in the repository (for understanding existing patterns and domain)
|
|
19
|
+
|
|
20
|
+
WORKFLOW:
|
|
21
|
+
1. Read the feature description and any existing context
|
|
22
|
+
2. Explore the codebase to understand the domain, existing patterns, and constraints
|
|
23
|
+
3. Identify stakeholders, personas, and key use cases
|
|
24
|
+
4. Write user stories with acceptance criteria following the format:
|
|
25
|
+
### US-NNN: Story Title
|
|
26
|
+
**Project**: [project-name]
|
|
27
|
+
**As a** [role]
|
|
28
|
+
**I want** [capability]
|
|
29
|
+
**So that** [benefit]
|
|
30
|
+
**Acceptance Criteria**:
|
|
31
|
+
- [ ] **AC-USNN-01**: [Criterion]
|
|
32
|
+
5. Define scope boundaries (in-scope vs out-of-scope)
|
|
33
|
+
6. Write spec.md to [MASTER_INCREMENT_PATH]/spec.md
|
|
34
|
+
7. Send PLAN_READY notification (do NOT wait for response):
|
|
35
|
+
SendMessage({ type: "message", recipient: "team-lead",
|
|
36
|
+
content: "PLAN_READY: spec.md written at [MASTER_INCREMENT_PATH]/spec.md\nUser Stories: [count]\nACs: [count]\nKey decisions: [1-2 sentence summary]",
|
|
37
|
+
summary: "PM: spec.md ready — proceeding" })
|
|
38
|
+
8. Proceed immediately. If team-lead sends PLAN_CORRECTION, revise spec.md accordingly.
|
|
39
|
+
9. Signal COMPLETION:
|
|
40
|
+
SendMessage({ type: "message", recipient: "team-lead",
|
|
41
|
+
content: "COMPLETION: spec.md finalized.\nStories: [count]\nACs: [count]\nScope: [brief summary]",
|
|
42
|
+
summary: "PM agent: spec complete" })
|
|
43
|
+
|
|
44
|
+
RULES:
|
|
45
|
+
- WRITE only spec.md — do not create plan.md or tasks.md (Architect and Planner own those)
|
|
46
|
+
- Every user story MUST have a **Project**: field
|
|
47
|
+
- Every AC MUST use the AC-USNN-NN format for bidirectional linking
|
|
48
|
+
- Be specific in ACs — testable, not vague ("user can log in" not "auth works")
|
|
49
|
+
- Consider edge cases, error states, and non-functional requirements
|
|
50
|
+
- Do NOT scope-creep — stick to the feature description
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
You are the RESEARCHER agent.
|
|
2
|
+
|
|
3
|
+
RESEARCH TOPIC: [RESEARCH_TOPIC]
|
|
4
|
+
RESEARCH SCOPE: [RESEARCH_SCOPE]
|
|
5
|
+
|
|
6
|
+
MISSION:
|
|
7
|
+
Investigate the given topic thoroughly — explore the codebase, search the web,
|
|
8
|
+
analyze patterns, and compile actionable findings. You are a read-only analyst.
|
|
9
|
+
Your job is to FIND information, not implement changes.
|
|
10
|
+
|
|
11
|
+
APPROACH:
|
|
12
|
+
1. Parse the research scope to understand what information is needed
|
|
13
|
+
2. Explore the codebase for relevant patterns, implementations, and conventions
|
|
14
|
+
3. Search the web for related technologies, best practices, and alternatives
|
|
15
|
+
4. Cross-reference findings — validate web claims against actual codebase state
|
|
16
|
+
5. Compile a structured research report
|
|
17
|
+
|
|
18
|
+
YOUR REPORT MUST INCLUDE:
|
|
19
|
+
|
|
20
|
+
### Executive Summary
|
|
21
|
+
2-3 sentence overview of key findings and recommendation.
|
|
22
|
+
|
|
23
|
+
### Current State
|
|
24
|
+
What exists today in the codebase. Include file paths and line references.
|
|
25
|
+
|
|
26
|
+
### External Research
|
|
27
|
+
What the broader ecosystem offers. Technologies, libraries, patterns considered.
|
|
28
|
+
Include sources and links where relevant.
|
|
29
|
+
|
|
30
|
+
### Analysis
|
|
31
|
+
Compare options. Use a decision matrix if comparing 3+ alternatives:
|
|
32
|
+
| Option | Pros | Cons | Effort | Fit |
|
|
33
|
+
|
|
34
|
+
### Recommendations
|
|
35
|
+
Concrete, actionable recommendations ranked by priority.
|
|
36
|
+
Each should include: what to do, why, and estimated effort.
|
|
37
|
+
|
|
38
|
+
### Open Questions
|
|
39
|
+
Things that need further investigation or user input.
|
|
40
|
+
|
|
41
|
+
COMMUNICATION:
|
|
42
|
+
When done, signal completion:
|
|
43
|
+
SendMessage({
|
|
44
|
+
type: "message",
|
|
45
|
+
recipient: "team-lead",
|
|
46
|
+
content: "RESEARCH_COMPLETE: [topic] research finished.\nKey finding: [most important insight]\nRecommendation: [primary recommendation]\nOpen questions: [count]",
|
|
47
|
+
summary: "Research complete: [topic]"
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
For significant discoveries during research:
|
|
51
|
+
SendMessage({
|
|
52
|
+
type: "message",
|
|
53
|
+
recipient: "team-lead",
|
|
54
|
+
content: "INSIGHT: [important discovery that may affect scope or approach]",
|
|
55
|
+
summary: "Researcher found insight"
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
RULES:
|
|
59
|
+
- READ-ONLY: Do not modify any files
|
|
60
|
+
- Be thorough: explore multiple angles, not just the first result
|
|
61
|
+
- Be specific: include file paths, line numbers, URLs — not vague references
|
|
62
|
+
- Be honest: flag uncertainty and gaps in knowledge
|
|
63
|
+
- Stay scoped: answer the research question, don't expand into tangential topics
|
|
64
|
+
- Cite sources: for web findings, include the source URL or reference
|
|
@@ -5,6 +5,7 @@ import axios from "axios";
|
|
|
5
5
|
import { consoleLogger } from "../../specweave/lib/vendor/utils/logger.js";
|
|
6
6
|
import { deriveFeatureId } from "../../specweave/lib/vendor/utils/feature-id-derivation.js";
|
|
7
7
|
import { GitHubACCheckboxSync } from "../../specweave-github/lib/github-ac-checkbox-sync.js";
|
|
8
|
+
import { AdoDescriptionUpdater } from "../../../src/core/ado-description-updater.js";
|
|
8
9
|
class AdoACCheckboxSync {
|
|
9
10
|
constructor(options) {
|
|
10
11
|
this.projectRoot = options.projectRoot;
|
|
@@ -88,47 +89,48 @@ class AdoACCheckboxSync {
|
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
91
|
/**
|
|
91
|
-
*
|
|
92
|
-
*
|
|
92
|
+
* Update a work item's AC section using API-based section manipulation.
|
|
93
|
+
*
|
|
94
|
+
* GET description → formatACCheckboxes → updateAcSection → PATCH with
|
|
95
|
+
* JSON Patch op:"replace". No regex used at any point.
|
|
93
96
|
*/
|
|
94
|
-
async
|
|
97
|
+
async updateWorkItemACSection(client, storyId, acStatus) {
|
|
98
|
+
const updater = new AdoDescriptionUpdater();
|
|
95
99
|
const resp = await client.get(
|
|
96
100
|
`/wit/workitems/${storyId}?fields=System.Description&api-version=7.0`
|
|
97
101
|
);
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const beforeRegex = new RegExp(`(\u2610|\u2611)\\s+(${escaped}:)`, "g");
|
|
104
|
-
const newCheckbox = completed ? "\u2611" : "\u2610";
|
|
105
|
-
const replaced = description.replace(beforeRegex, `${newCheckbox} $2`);
|
|
106
|
-
if (replaced !== description) {
|
|
107
|
-
updatedCount++;
|
|
108
|
-
description = replaced;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
if (description === original) return 0;
|
|
102
|
+
const currentDescription = resp.data?.fields?.["System.Description"] ?? "";
|
|
103
|
+
const newAcHtml = updater.formatACCheckboxes(acStatus);
|
|
104
|
+
if (!newAcHtml) return 0;
|
|
105
|
+
const updatedDescription = updater.updateAcSection(currentDescription, newAcHtml);
|
|
106
|
+
if (updatedDescription === currentDescription) return 0;
|
|
112
107
|
await client.patch(
|
|
113
108
|
`/wit/workitems/${storyId}?api-version=7.0`,
|
|
114
|
-
[{ op: "replace", path: "/fields/System.Description", value:
|
|
109
|
+
[{ op: "replace", path: "/fields/System.Description", value: updatedDescription }],
|
|
115
110
|
{ headers: { "Content-Type": "application/json-patch+json" } }
|
|
116
111
|
);
|
|
117
112
|
const completedCount = [...acStatus.values()].filter(Boolean).length;
|
|
118
113
|
const totalCount = acStatus.size;
|
|
119
114
|
const percentage = Math.round(completedCount / totalCount * 100);
|
|
120
115
|
const acLines = [...acStatus.entries()].map(([id, done]) => `<li>${done ? "\u2705" : "\u2B1C"} ${id}</li>`).join("\n");
|
|
121
|
-
const commentHtml = `<h3
|
|
116
|
+
const commentHtml = `<h3>AC Progress Update</h3>
|
|
122
117
|
<p><strong>Acceptance Criteria</strong>: ${completedCount}/${totalCount} (${percentage}%)</p>
|
|
123
118
|
<ul>
|
|
124
119
|
${acLines}
|
|
125
120
|
</ul>
|
|
126
|
-
<p
|
|
121
|
+
<p>Auto-updated by SpecWeave</p>`;
|
|
127
122
|
await client.post(
|
|
128
123
|
`/wit/workItems/${storyId}/comments?api-version=7.1-preview.3`,
|
|
129
124
|
{ text: commentHtml }
|
|
130
125
|
);
|
|
131
|
-
return
|
|
126
|
+
return acStatus.size;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Legacy: Fetch current HTML description, regex-flip checkboxes, PATCH back.
|
|
130
|
+
* Kept for backward compatibility. New code should use updateWorkItemACSection.
|
|
131
|
+
*/
|
|
132
|
+
async updateStoryCheckboxes(client, adoConfig, storyId, acStatus) {
|
|
133
|
+
return this.updateWorkItemACSection(client, storyId, acStatus);
|
|
132
134
|
}
|
|
133
135
|
buildClient(adoConfig) {
|
|
134
136
|
const token = Buffer.from(`:${adoConfig.pat}`).toString("base64");
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
* ADO AC Checkbox Sync
|
|
3
3
|
*
|
|
4
4
|
* When ACs are marked complete in spec.md, updates the HTML description
|
|
5
|
-
* of the linked ADO work item
|
|
5
|
+
* of the linked ADO work item using API-based section update (not regex).
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Uses AdoDescriptionUpdater for clean HTML section manipulation via
|
|
8
|
+
* sentinel comments (<!-- AC_SECTION_START/END -->).
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { promises as fs, existsSync } from 'fs';
|
|
@@ -15,6 +15,7 @@ import axios, { AxiosInstance } from 'axios';
|
|
|
15
15
|
import { Logger, consoleLogger } from '../../specweave/lib/vendor/utils/logger.js';
|
|
16
16
|
import { deriveFeatureId } from '../../specweave/lib/vendor/utils/feature-id-derivation.js';
|
|
17
17
|
import { GitHubACCheckboxSync } from '../../specweave-github/lib/github-ac-checkbox-sync.js';
|
|
18
|
+
import { AdoDescriptionUpdater } from '../../../src/core/ado-description-updater.js';
|
|
18
19
|
import type { SpecWeaveConfig } from '../../../src/core/config/types.js';
|
|
19
20
|
import type { LivingDocsUSFile } from '../../../src/types/living-docs-us-file.js';
|
|
20
21
|
|
|
@@ -131,41 +132,35 @@ export class AdoACCheckboxSync {
|
|
|
131
132
|
}
|
|
132
133
|
|
|
133
134
|
/**
|
|
134
|
-
*
|
|
135
|
-
*
|
|
135
|
+
* Update a work item's AC section using API-based section manipulation.
|
|
136
|
+
*
|
|
137
|
+
* GET description → formatACCheckboxes → updateAcSection → PATCH with
|
|
138
|
+
* JSON Patch op:"replace". No regex used at any point.
|
|
136
139
|
*/
|
|
137
|
-
|
|
140
|
+
async updateWorkItemACSection(
|
|
138
141
|
client: AxiosInstance,
|
|
139
|
-
adoConfig: { organization: string; project: string; pat: string },
|
|
140
142
|
storyId: number,
|
|
141
|
-
acStatus: Map<string, boolean
|
|
143
|
+
acStatus: Map<string, boolean>,
|
|
142
144
|
): Promise<number> {
|
|
145
|
+
const updater = new AdoDescriptionUpdater();
|
|
146
|
+
|
|
143
147
|
const resp = await client.get(
|
|
144
148
|
`/wit/workitems/${storyId}?fields=System.Description&api-version=7.0`
|
|
145
149
|
);
|
|
146
150
|
|
|
147
|
-
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
for (const [acId, completed] of acStatus) {
|
|
152
|
-
const escaped = acId.replace(/-/g, '\\-');
|
|
153
|
-
// Pattern: ☐ AC-US1-01: or ☑ AC-US1-01:
|
|
154
|
-
const beforeRegex = new RegExp(`(☐|☑)\\s+(${escaped}:)`, 'g');
|
|
155
|
-
const newCheckbox = completed ? '☑' : '☐';
|
|
156
|
-
const replaced = description.replace(beforeRegex, `${newCheckbox} $2`);
|
|
157
|
-
if (replaced !== description) {
|
|
158
|
-
updatedCount++;
|
|
159
|
-
description = replaced;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
151
|
+
const currentDescription: string = resp.data?.fields?.['System.Description'] ?? '';
|
|
152
|
+
const newAcHtml = updater.formatACCheckboxes(acStatus);
|
|
153
|
+
|
|
154
|
+
if (!newAcHtml) return 0;
|
|
162
155
|
|
|
163
|
-
|
|
156
|
+
const updatedDescription = updater.updateAcSection(currentDescription, newAcHtml);
|
|
164
157
|
|
|
165
|
-
|
|
158
|
+
if (updatedDescription === currentDescription) return 0;
|
|
159
|
+
|
|
160
|
+
// PATCH description via JSON Patch
|
|
166
161
|
await client.patch(
|
|
167
162
|
`/wit/workitems/${storyId}?api-version=7.0`,
|
|
168
|
-
[{ op: 'replace', path: '/fields/System.Description', value:
|
|
163
|
+
[{ op: 'replace', path: '/fields/System.Description', value: updatedDescription }],
|
|
169
164
|
{ headers: { 'Content-Type': 'application/json-patch+json' } }
|
|
170
165
|
);
|
|
171
166
|
|
|
@@ -178,19 +173,32 @@ export class AdoACCheckboxSync {
|
|
|
178
173
|
.map(([id, done]) => `<li>${done ? '✅' : '⬜'} ${id}</li>`)
|
|
179
174
|
.join('\n');
|
|
180
175
|
|
|
181
|
-
const commentHtml = `<h3
|
|
176
|
+
const commentHtml = `<h3>AC Progress Update</h3>
|
|
182
177
|
<p><strong>Acceptance Criteria</strong>: ${completedCount}/${totalCount} (${percentage}%)</p>
|
|
183
178
|
<ul>
|
|
184
179
|
${acLines}
|
|
185
180
|
</ul>
|
|
186
|
-
<p
|
|
181
|
+
<p>Auto-updated by SpecWeave</p>`;
|
|
187
182
|
|
|
188
183
|
await client.post(
|
|
189
184
|
`/wit/workItems/${storyId}/comments?api-version=7.1-preview.3`,
|
|
190
185
|
{ text: commentHtml }
|
|
191
186
|
);
|
|
192
187
|
|
|
193
|
-
return
|
|
188
|
+
return acStatus.size;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Legacy: Fetch current HTML description, regex-flip checkboxes, PATCH back.
|
|
193
|
+
* Kept for backward compatibility. New code should use updateWorkItemACSection.
|
|
194
|
+
*/
|
|
195
|
+
private async updateStoryCheckboxes(
|
|
196
|
+
client: AxiosInstance,
|
|
197
|
+
adoConfig: { organization: string; project: string; pat: string },
|
|
198
|
+
storyId: number,
|
|
199
|
+
acStatus: Map<string, boolean>
|
|
200
|
+
): Promise<number> {
|
|
201
|
+
return this.updateWorkItemACSection(client, storyId, acStatus);
|
|
194
202
|
}
|
|
195
203
|
|
|
196
204
|
private buildClient(adoConfig: { organization: string; project: string; pat: string }): AxiosInstance {
|
|
@@ -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
|
// ==========================================================================
|
|
@@ -114,6 +115,20 @@ class AdoClient {
|
|
|
114
115
|
const url = `${this.baseUrl}/_apis/wit/workitems/${id}?api-version=7.1`;
|
|
115
116
|
await this.request("DELETE", url);
|
|
116
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Pull the current state of a work item for bidirectional sync.
|
|
120
|
+
*
|
|
121
|
+
* @param id - ADO work item ID
|
|
122
|
+
* @returns state and last-modified timestamp
|
|
123
|
+
*/
|
|
124
|
+
async pullWorkItemState(id) {
|
|
125
|
+
const url = `${this.baseUrl}/_apis/wit/workitems/${id}?$select=System.State,System.ChangedDate&api-version=7.1`;
|
|
126
|
+
const item = await this.request("GET", url);
|
|
127
|
+
return {
|
|
128
|
+
state: item.fields["System.State"],
|
|
129
|
+
modifiedAt: new Date(item.fields["System.ChangedDate"])
|
|
130
|
+
};
|
|
131
|
+
}
|
|
117
132
|
// ==========================================================================
|
|
118
133
|
// Comment Operations
|
|
119
134
|
// ==========================================================================
|
|
@@ -166,6 +181,9 @@ class AdoClient {
|
|
|
166
181
|
* Make HTTP request to ADO API
|
|
167
182
|
*/
|
|
168
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
|
+
}
|
|
169
187
|
return new Promise((resolve, reject) => {
|
|
170
188
|
const urlObj = new URL(url);
|
|
171
189
|
const options = {
|
|
@@ -196,6 +214,12 @@ class AdoClient {
|
|
|
196
214
|
reject(new Error(`Failed to parse JSON response: ${error}`));
|
|
197
215
|
}
|
|
198
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
|
+
}
|
|
199
223
|
let errorMessage = `ADO API error: ${res.statusCode} ${res.statusMessage}`;
|
|
200
224
|
try {
|
|
201
225
|
const errorData = JSON.parse(data);
|
|
@@ -204,7 +228,9 @@ class AdoClient {
|
|
|
204
228
|
}
|
|
205
229
|
} catch {
|
|
206
230
|
}
|
|
207
|
-
|
|
231
|
+
const err = new Error(errorMessage);
|
|
232
|
+
err.status = res.statusCode;
|
|
233
|
+
reject(err);
|
|
208
234
|
}
|
|
209
235
|
});
|
|
210
236
|
});
|