specweave 0.30.14 → 0.30.17
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/CLAUDE.md +43 -0
- package/README.md +32 -0
- package/bin/specweave.js +28 -0
- package/dist/src/cli/commands/commits.d.ts +7 -0
- package/dist/src/cli/commands/commits.d.ts.map +1 -0
- package/dist/src/cli/commands/commits.js +42 -0
- package/dist/src/cli/commands/commits.js.map +1 -0
- package/dist/src/cli/commands/living-docs.d.ts +29 -0
- package/dist/src/cli/commands/living-docs.d.ts.map +1 -0
- package/dist/src/cli/commands/living-docs.js +350 -0
- package/dist/src/cli/commands/living-docs.js.map +1 -0
- package/dist/src/cli/helpers/ado-area-selector.js +1 -1
- package/dist/src/cli/helpers/ado-area-selector.js.map +1 -1
- package/dist/src/cli/workers/living-docs-worker.js +80 -44
- package/dist/src/cli/workers/living-docs-worker.js.map +1 -1
- package/dist/src/core/background/index.d.ts +2 -2
- package/dist/src/core/background/index.d.ts.map +1 -1
- package/dist/src/core/background/index.js +1 -1
- package/dist/src/core/background/index.js.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.d.ts +60 -24
- package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.js +360 -103
- package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
- package/dist/src/core/llm/index.d.ts +1 -0
- package/dist/src/core/llm/index.d.ts.map +1 -1
- package/dist/src/core/llm/index.js +2 -0
- package/dist/src/core/llm/index.js.map +1 -1
- package/dist/src/core/llm/providers/anthropic-provider.d.ts.map +1 -1
- package/dist/src/core/llm/providers/anthropic-provider.js +15 -26
- package/dist/src/core/llm/providers/anthropic-provider.js.map +1 -1
- package/dist/src/core/llm/providers/azure-openai-provider.d.ts.map +1 -1
- package/dist/src/core/llm/providers/azure-openai-provider.js +13 -5
- package/dist/src/core/llm/providers/azure-openai-provider.js.map +1 -1
- package/dist/src/core/llm/providers/bedrock-provider.d.ts.map +1 -1
- package/dist/src/core/llm/providers/bedrock-provider.js +12 -8
- package/dist/src/core/llm/providers/bedrock-provider.js.map +1 -1
- package/dist/src/core/llm/providers/claude-code-provider.d.ts.map +1 -1
- package/dist/src/core/llm/providers/claude-code-provider.js +15 -25
- package/dist/src/core/llm/providers/claude-code-provider.js.map +1 -1
- package/dist/src/core/llm/providers/ollama-provider.d.ts.map +1 -1
- package/dist/src/core/llm/providers/ollama-provider.js +12 -9
- package/dist/src/core/llm/providers/ollama-provider.js.map +1 -1
- package/dist/src/core/llm/providers/openai-provider.d.ts.map +1 -1
- package/dist/src/core/llm/providers/openai-provider.js +13 -6
- package/dist/src/core/llm/providers/openai-provider.js.map +1 -1
- package/dist/src/core/llm/providers/vertex-ai-provider.d.ts.map +1 -1
- package/dist/src/core/llm/providers/vertex-ai-provider.js +12 -8
- package/dist/src/core/llm/providers/vertex-ai-provider.js.map +1 -1
- package/dist/src/importers/ado-importer.js +2 -2
- package/dist/src/importers/ado-importer.js.map +1 -1
- package/dist/src/importers/item-converter.d.ts +6 -1
- package/dist/src/importers/item-converter.d.ts.map +1 -1
- package/dist/src/importers/item-converter.js +15 -2
- package/dist/src/importers/item-converter.js.map +1 -1
- package/dist/src/integrations/ado/ado-pat-provider.d.ts +3 -3
- package/dist/src/integrations/ado/ado-pat-provider.js +3 -3
- package/dist/src/living-docs/epic-id-allocator.d.ts +1 -1
- package/dist/src/living-docs/epic-id-allocator.js +1 -1
- package/dist/src/living-docs/fs-id-allocator.d.ts +1 -1
- package/dist/src/living-docs/fs-id-allocator.js +1 -1
- package/dist/src/utils/auth-helpers.d.ts +23 -0
- package/dist/src/utils/auth-helpers.d.ts.map +1 -1
- package/dist/src/utils/auth-helpers.js +51 -0
- package/dist/src/utils/auth-helpers.js.map +1 -1
- package/dist/src/utils/feature-id-collision.d.ts +48 -5
- package/dist/src/utils/feature-id-collision.d.ts.map +1 -1
- package/dist/src/utils/feature-id-collision.js +251 -19
- package/dist/src/utils/feature-id-collision.js.map +1 -1
- package/dist/src/utils/llm-json-extractor.d.ts +105 -0
- package/dist/src/utils/llm-json-extractor.d.ts.map +1 -0
- package/dist/src/utils/llm-json-extractor.js +336 -0
- package/dist/src/utils/llm-json-extractor.js.map +1 -0
- package/dist/src/utils/structure-level-detector.d.ts +105 -0
- package/dist/src/utils/structure-level-detector.d.ts.map +1 -0
- package/dist/src/utils/structure-level-detector.js +388 -0
- package/dist/src/utils/structure-level-detector.js.map +1 -0
- package/dist/src/utils/validators/ado-validator.js +2 -2
- package/dist/src/utils/validators/ado-validator.js.map +1 -1
- package/package.json +1 -2
- package/plugins/specweave/commands/specweave-increment.md +57 -9
- package/plugins/specweave/commands/specweave-living-docs.md +321 -0
- package/plugins/specweave/commands/specweave-sync-specs.md +37 -6
- package/plugins/specweave/hooks/hooks.json +10 -0
- package/plugins/specweave/hooks/spec-project-validator.sh +111 -0
- package/plugins/specweave/hooks/v2/handlers/github-sync-handler.sh +10 -1
- package/plugins/specweave/hooks/v2/handlers/living-docs-handler.sh +10 -1
- package/plugins/specweave/skills/increment-planner/SKILL.md +109 -10
- package/plugins/specweave/skills/increment-planner/templates/spec-multi-project.md +2 -0
- package/plugins/specweave/skills/increment-planner/templates/spec-single-project.md +1 -0
- package/plugins/specweave/skills/multi-project-spec-mapper/SKILL.md +24 -1
- package/plugins/specweave/skills/spec-generator/SKILL.md +18 -0
- package/plugins/specweave/skills/specweave-framework/SKILL.md +25 -0
- package/plugins/specweave-ado/agents/ado-manager/AGENT.md +58 -0
- package/plugins/specweave-ado/commands/pull.md +30 -0
- package/plugins/specweave-ado/commands/push.md +30 -0
- package/plugins/specweave-ado/commands/sync.md +31 -0
- package/plugins/specweave-github/agents/github-manager/AGENT.md +22 -0
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +14 -0
- package/plugins/specweave-jira/agents/jira-manager/AGENT.md +30 -0
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +21 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: specweave:living-docs
|
|
3
|
+
description: Launch or resume Living Docs Builder independently. Generates documentation from codebase analysis with AI-powered insights.
|
|
4
|
+
usage: /specweave:living-docs [--resume <jobId>] [--depth <level>] [--priority <modules>] [--sources <folders>] [--depends-on <jobIds>] [--foreground]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Living Docs Builder (Standalone)
|
|
8
|
+
|
|
9
|
+
**Usage**: `/specweave:living-docs [options]`
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Purpose
|
|
14
|
+
|
|
15
|
+
Launch the Living Docs Builder independently of `specweave init`. This is essential for:
|
|
16
|
+
- **Resuming after crash** - Claude Code crashed after init, need to restart living docs
|
|
17
|
+
- **On-demand analysis** - Re-analyze codebase after major changes
|
|
18
|
+
- **Large brownfield projects** - Run targeted analysis on specific modules
|
|
19
|
+
- **CI/CD integration** - Automate documentation generation
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Command Options
|
|
24
|
+
|
|
25
|
+
| Option | Description |
|
|
26
|
+
|--------|-------------|
|
|
27
|
+
| (none) | Interactive mode - prompts for configuration |
|
|
28
|
+
| `--resume <jobId>` | Resume orphaned/paused living-docs job |
|
|
29
|
+
| `--depth <level>` | Analysis depth: `quick`, `standard`, `deep-native`, `deep-api` |
|
|
30
|
+
| `--priority <modules>` | Priority modules (comma-separated): `auth,payments,api` |
|
|
31
|
+
| `--sources <folders>` | Additional doc folders (comma-separated): `docs/,wiki/` |
|
|
32
|
+
| `--depends-on <jobIds>` | Wait for jobs before starting (comma-separated) |
|
|
33
|
+
| `--foreground` | Run in current session instead of background |
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Quick Start
|
|
38
|
+
|
|
39
|
+
### Launch New Analysis (Interactive)
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
/specweave:living-docs
|
|
43
|
+
|
|
44
|
+
# Prompts for:
|
|
45
|
+
# 1. Analysis depth (quick/standard/deep-native/deep-api)
|
|
46
|
+
# 2. Priority modules to focus on
|
|
47
|
+
# 3. Additional documentation sources
|
|
48
|
+
# 4. Confirmation to launch
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Resume After Crash
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Check for orphaned jobs first
|
|
55
|
+
/specweave:jobs
|
|
56
|
+
|
|
57
|
+
# If you see an orphaned living-docs-builder job:
|
|
58
|
+
/specweave:living-docs --resume abc12345
|
|
59
|
+
|
|
60
|
+
# Or let it auto-detect:
|
|
61
|
+
/specweave:living-docs
|
|
62
|
+
# → "Found orphaned job abc12345. Resume? [Y/n]"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Quick Analysis (Non-Interactive)
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# Quick scan - 5-10 minutes
|
|
69
|
+
/specweave:living-docs --depth quick
|
|
70
|
+
|
|
71
|
+
# Standard analysis - 15-30 minutes
|
|
72
|
+
/specweave:living-docs --depth standard --priority auth,payments
|
|
73
|
+
|
|
74
|
+
# AI-powered deep analysis (FREE with MAX subscription)
|
|
75
|
+
/specweave:living-docs --depth deep-native --priority core,api
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Analysis Depths
|
|
81
|
+
|
|
82
|
+
| Depth | Duration | What It Does | Cost |
|
|
83
|
+
|-------|----------|--------------|------|
|
|
84
|
+
| `quick` | ~5-10 min | Structure scan, tech detection, imports map | Free |
|
|
85
|
+
| `standard` | ~15-30 min | Module analysis, exports, dependencies | Free |
|
|
86
|
+
| `deep-native` | Progress-based | AI analysis via Claude Code CLI | FREE (MAX) |
|
|
87
|
+
| `deep-api` | Progress-based | AI analysis via API key | API costs |
|
|
88
|
+
|
|
89
|
+
### Deep-Native (Recommended for MAX Users)
|
|
90
|
+
|
|
91
|
+
Uses your Claude MAX subscription via `claude --print`:
|
|
92
|
+
- **No extra cost** - included in MAX
|
|
93
|
+
- Runs in **background** - survives terminal close
|
|
94
|
+
- **Checkpoint/resume** - can resume from any phase
|
|
95
|
+
- Uses **Opus 4.5** for best quality
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
/specweave:living-docs --depth deep-native
|
|
99
|
+
|
|
100
|
+
# Monitor progress:
|
|
101
|
+
/specweave:jobs --follow <jobId>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Implementation Steps
|
|
107
|
+
|
|
108
|
+
When this command is invoked:
|
|
109
|
+
|
|
110
|
+
### Step 1: Check for Orphaned Jobs
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
import { getOrphanedJobs, getJobManager } from '../../../src/core/background/job-launcher.js';
|
|
114
|
+
|
|
115
|
+
const orphaned = getOrphanedJobs(projectPath).filter(j => j.type === 'living-docs-builder');
|
|
116
|
+
if (orphaned.length > 0) {
|
|
117
|
+
// Prompt: "Found orphaned job {id}. Resume? [Y/n]"
|
|
118
|
+
// If yes: resume job
|
|
119
|
+
// If no: ask if they want to start fresh
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Step 2: Collect Configuration (if not --resume)
|
|
124
|
+
|
|
125
|
+
If no `--resume` flag and no auto-resume:
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
import { collectLivingDocsInputs } from '../../../src/cli/helpers/init/living-docs-preflight.js';
|
|
129
|
+
|
|
130
|
+
const result = await collectLivingDocsInputs({
|
|
131
|
+
projectPath,
|
|
132
|
+
language: 'en',
|
|
133
|
+
isCi: hasFlags, // Skip prompts if flags provided
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Override with flags:
|
|
138
|
+
- `--depth` → `result.userInputs.analysisDepth`
|
|
139
|
+
- `--priority` → `result.userInputs.priorityAreas`
|
|
140
|
+
- `--sources` → `result.userInputs.additionalSources`
|
|
141
|
+
|
|
142
|
+
### Step 3: Launch Job
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
import { launchLivingDocsJob } from '../../../src/core/background/job-launcher.js';
|
|
146
|
+
|
|
147
|
+
const { job, pid, isBackground } = await launchLivingDocsJob({
|
|
148
|
+
projectPath,
|
|
149
|
+
userInputs: result.userInputs,
|
|
150
|
+
dependsOn: dependsOnJobIds,
|
|
151
|
+
foreground: hasForegroundFlag,
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Step 4: Display Status
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
✅ Living Docs Builder launched!
|
|
159
|
+
|
|
160
|
+
Job ID: ldb-abc12345
|
|
161
|
+
Depth: deep-native (Claude Code Opus 4.5)
|
|
162
|
+
Priority: auth, payments, api
|
|
163
|
+
PID: 45678
|
|
164
|
+
|
|
165
|
+
Monitor: /specweave:jobs --follow ldb-abc12345
|
|
166
|
+
Logs: /specweave:jobs --logs ldb-abc12345
|
|
167
|
+
|
|
168
|
+
💡 This job runs in background and survives terminal close.
|
|
169
|
+
Output will be saved to:
|
|
170
|
+
- .specweave/docs/SUGGESTIONS.md
|
|
171
|
+
- .specweave/docs/ENTERPRISE-HEALTH.md
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Resume Behavior
|
|
177
|
+
|
|
178
|
+
When resuming a job:
|
|
179
|
+
|
|
180
|
+
1. **Load checkpoint** from `.specweave/state/jobs/<jobId>/checkpoints/`
|
|
181
|
+
2. **Skip completed phases**:
|
|
182
|
+
- `waiting` → dependency waiting
|
|
183
|
+
- `discovery` → codebase scanning
|
|
184
|
+
- `foundation` → high-level docs
|
|
185
|
+
- `integration` → work item matching
|
|
186
|
+
- `deep-dive` → module analysis (per-module checkpoints)
|
|
187
|
+
- `suggestions` → recommendations
|
|
188
|
+
- `enterprise` → health report
|
|
189
|
+
3. **Continue from resume point**
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
# Example: Job crashed during deep-dive phase
|
|
193
|
+
/specweave:living-docs --resume abc12345
|
|
194
|
+
|
|
195
|
+
# Output:
|
|
196
|
+
# Resuming from checkpoint: phase=deep-dive, module=auth (5/18)
|
|
197
|
+
# ✓ Skipping completed phases: waiting, discovery, foundation, integration
|
|
198
|
+
# → Continuing deep-dive from module: payments
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Waiting for Dependencies
|
|
204
|
+
|
|
205
|
+
For umbrella projects with clone/import jobs:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
# Launch after clone completes
|
|
209
|
+
/specweave:living-docs --depends-on clone-xyz123 --depth standard
|
|
210
|
+
|
|
211
|
+
# Launch after both clone and import complete
|
|
212
|
+
/specweave:living-docs --depends-on clone-xyz123,import-abc456
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
The job will:
|
|
216
|
+
1. Enter `waiting` phase
|
|
217
|
+
2. Poll dependency status every 30 seconds
|
|
218
|
+
3. Start analysis once all dependencies complete
|
|
219
|
+
4. Warn if any dependency failed (proceeds with available data)
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Output Files
|
|
224
|
+
|
|
225
|
+
After completion:
|
|
226
|
+
|
|
227
|
+
| File | Description |
|
|
228
|
+
|------|-------------|
|
|
229
|
+
| `.specweave/docs/SUGGESTIONS.md` | Documentation recommendations by priority |
|
|
230
|
+
| `.specweave/docs/ENTERPRISE-HEALTH.md` | Health score, coverage, accuracy metrics |
|
|
231
|
+
| `.specweave/docs/overview/PROJECT-OVERVIEW.md` | Auto-generated project overview |
|
|
232
|
+
| `.specweave/docs/overview/TECH-STACK.md` | Detected technologies and frameworks |
|
|
233
|
+
| `.specweave/docs/modules/*.md` | Per-module documentation |
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Examples
|
|
238
|
+
|
|
239
|
+
### Example 1: Post-Crash Resume
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
# Claude crashed after init, living docs job orphaned
|
|
243
|
+
|
|
244
|
+
# Step 1: Check what's there
|
|
245
|
+
/specweave:jobs
|
|
246
|
+
# Shows: [ldb-abc123] living-docs-builder - ORPHANED (worker died)
|
|
247
|
+
|
|
248
|
+
# Step 2: Resume
|
|
249
|
+
/specweave:living-docs --resume ldb-abc123
|
|
250
|
+
|
|
251
|
+
# Output:
|
|
252
|
+
# ✅ Resuming Living Docs Builder (ldb-abc123)
|
|
253
|
+
# Last checkpoint: deep-dive phase, module 12/45
|
|
254
|
+
# Continuing from: payments-service
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Example 2: Large Brownfield (247 repos)
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
# Focus on critical modules first
|
|
261
|
+
/specweave:living-docs --depth deep-native \
|
|
262
|
+
--priority auth,payments,billing,core \
|
|
263
|
+
--depends-on clone-main123
|
|
264
|
+
|
|
265
|
+
# Monitor in another terminal
|
|
266
|
+
/specweave:jobs --follow ldb-xyz789
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Example 3: CI/CD Integration
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
# In CI pipeline (non-interactive)
|
|
273
|
+
specweave living-docs --depth quick --foreground
|
|
274
|
+
|
|
275
|
+
# Or background with polling
|
|
276
|
+
specweave living-docs --depth standard
|
|
277
|
+
specweave jobs --wait ldb-latest # Wait for completion
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Error Handling
|
|
283
|
+
|
|
284
|
+
### Worker Crashed
|
|
285
|
+
```
|
|
286
|
+
/specweave:jobs
|
|
287
|
+
# Shows: ORPHANED status
|
|
288
|
+
|
|
289
|
+
/specweave:living-docs --resume <jobId>
|
|
290
|
+
# Resumes from last checkpoint
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Dependency Failed
|
|
294
|
+
```
|
|
295
|
+
⚠️ Dependency clone-xyz123 failed
|
|
296
|
+
Reason: Network timeout
|
|
297
|
+
|
|
298
|
+
Proceeding with available data...
|
|
299
|
+
Some repositories may be missing from analysis.
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### No Brownfield Detected
|
|
303
|
+
```
|
|
304
|
+
ℹ️ No existing code detected (greenfield project)
|
|
305
|
+
Living docs will sync automatically as you create increments.
|
|
306
|
+
|
|
307
|
+
To force analysis anyway: /specweave:living-docs --force
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## See Also
|
|
313
|
+
|
|
314
|
+
- `/specweave:jobs` - Monitor all background jobs
|
|
315
|
+
- `/specweave:import-docs` - Import existing documentation
|
|
316
|
+
- `specweave:brownfield-analyzer` skill - Analyze doc gaps
|
|
317
|
+
- `specweave:brownfield-onboarder` skill - Merge existing docs
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
**Implementation**: `src/cli/commands/living-docs.ts`
|
|
@@ -103,22 +103,53 @@ await syncSpecs(args);
|
|
|
103
103
|
|
|
104
104
|
**This will**:
|
|
105
105
|
1. **Derive feature ID from increment number** (e.g., 0040 → FS-040, 0002 → FS-002)
|
|
106
|
-
2. **
|
|
107
|
-
-
|
|
106
|
+
2. **Read project/board from spec.md YAML frontmatter** (v0.31.0+ REQUIRED):
|
|
107
|
+
- For 1-level: `project:` field REQUIRED
|
|
108
|
+
- For 2-level: `project:` AND `board:` fields REQUIRED
|
|
109
|
+
- See [ADR-0190](/internal/architecture/adr/0190-spec-project-board-requirement.md)
|
|
110
|
+
3. **Smart project matching fallback** (only if YAML fields missing - deprecated):
|
|
111
|
+
- `**Project**:` field in spec.md body (legacy)
|
|
108
112
|
- `multiProject.activeProject` in config.json
|
|
109
113
|
- ADO area path / JIRA board mapping
|
|
110
114
|
- Git remote (repo name)
|
|
111
115
|
- **ASK USER if unsure** (multi-project mode)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
- `.specweave/docs/internal/specs/{project}/FS-XXX
|
|
115
|
-
- `.specweave/docs/internal/specs/{project}/FS-XXX
|
|
116
|
+
4. Parse spec.md for user stories and acceptance criteria
|
|
117
|
+
5. Create living docs structure:
|
|
118
|
+
- 1-level: `.specweave/docs/internal/specs/{project}/FS-XXX/`
|
|
119
|
+
- 2-level: `.specweave/docs/internal/specs/{project}/{board}/FS-XXX/`
|
|
116
120
|
|
|
117
121
|
**CRITICAL**: Feature ID is DERIVED from increment number (ADR-0187)
|
|
118
122
|
- Increment 0002-user-authentication → FS-002
|
|
119
123
|
- Increment 0040-some-feature → FS-040
|
|
120
124
|
- NO date-based patterns like FS-YY-MM-DD-name
|
|
121
125
|
|
|
126
|
+
### 3.2 Project/Board Validation (v0.31.0+)
|
|
127
|
+
|
|
128
|
+
**Structure Level Detection** (from `src/utils/structure-level-detector.ts`):
|
|
129
|
+
- Detects 1-level vs 2-level from config (ADO, JIRA, umbrella, multiProject, folders)
|
|
130
|
+
- Validates spec.md has required fields based on structure level
|
|
131
|
+
- For 2-level: **ERROR if project OR board missing** (cannot sync)
|
|
132
|
+
- For 1-level: **WARNING if project missing** (uses fallback, deprecated)
|
|
133
|
+
|
|
134
|
+
**Expected spec.md frontmatter**:
|
|
135
|
+
|
|
136
|
+
```yaml
|
|
137
|
+
# 1-level structure
|
|
138
|
+
---
|
|
139
|
+
increment: 0001-feature-name
|
|
140
|
+
project: my-project # REQUIRED for 1-level
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
# 2-level structure
|
|
144
|
+
---
|
|
145
|
+
increment: 0001-feature-name
|
|
146
|
+
project: acme-corp # REQUIRED for 2-level
|
|
147
|
+
board: clinical-insights # REQUIRED for 2-level
|
|
148
|
+
---
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Migration**: See [Migration Guide](/public/guides/migration-v031-project-fields.md)
|
|
152
|
+
|
|
122
153
|
---
|
|
123
154
|
|
|
124
155
|
## STEP 4: Report Distribution Results
|
|
@@ -20,6 +20,16 @@
|
|
|
20
20
|
"command": "bash -c 'F=\"${CLAUDE_PLUGIN_ROOT}/hooks/universal/dispatcher.mjs\"; [ -f \"$F\" ] && node \"$F\" completion-guard || printf \"{\\\"continue\\\":true}\"' 2>/dev/null || printf '{\"continue\":true}'"
|
|
21
21
|
}
|
|
22
22
|
]
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"matcher": "Write",
|
|
26
|
+
"matcher_content": "\\.specweave/increments/\\d{4}-[^/]+/spec\\.md",
|
|
27
|
+
"hooks": [
|
|
28
|
+
{
|
|
29
|
+
"type": "command",
|
|
30
|
+
"command": "bash -c 'F=\"${CLAUDE_PLUGIN_ROOT}/hooks/spec-project-validator.sh\"; [ -f \"$F\" ] && \"$F\" || printf \"{\\\"decision\\\":\\\"allow\\\"}\"' 2>/dev/null || printf '{\"decision\":\"allow\"}'"
|
|
31
|
+
}
|
|
32
|
+
]
|
|
23
33
|
}
|
|
24
34
|
],
|
|
25
35
|
"PostToolUse": [
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# spec-project-validator.sh
|
|
4
|
+
#
|
|
5
|
+
# Pre-tool-use hook that validates spec.md has required project/board fields
|
|
6
|
+
# before allowing Write tool to create/update spec.md files.
|
|
7
|
+
#
|
|
8
|
+
# Activation:
|
|
9
|
+
# - tool_name: Write
|
|
10
|
+
# - file_path matches: .specweave/increments/*/spec.md
|
|
11
|
+
#
|
|
12
|
+
# Rules:
|
|
13
|
+
# - 1-level structure: spec.md MUST have `project:` in YAML frontmatter
|
|
14
|
+
# - 2-level structure: spec.md MUST have BOTH `project:` AND `board:` in frontmatter
|
|
15
|
+
#
|
|
16
|
+
# Returns exit code 1 (block) if validation fails, 0 (allow) otherwise.
|
|
17
|
+
|
|
18
|
+
set -e
|
|
19
|
+
|
|
20
|
+
# Read tool input from stdin
|
|
21
|
+
INPUT=$(cat)
|
|
22
|
+
|
|
23
|
+
# Extract tool name
|
|
24
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""')
|
|
25
|
+
|
|
26
|
+
# Only validate Write tool calls
|
|
27
|
+
if [ "$TOOL_NAME" != "Write" ]; then
|
|
28
|
+
echo '{"decision": "allow"}'
|
|
29
|
+
exit 0
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# Extract file path
|
|
33
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
|
|
34
|
+
|
|
35
|
+
# Only validate spec.md files in increments folder
|
|
36
|
+
if [[ ! "$FILE_PATH" =~ \.specweave/increments/[0-9]{4}-[^/]+/spec\.md$ ]]; then
|
|
37
|
+
echo '{"decision": "allow"}'
|
|
38
|
+
exit 0
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# Extract file content
|
|
42
|
+
CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // ""')
|
|
43
|
+
|
|
44
|
+
# Check if content has YAML frontmatter
|
|
45
|
+
if [[ ! "$CONTENT" =~ ^---$'\n' ]]; then
|
|
46
|
+
echo '{"decision": "block", "reason": "spec.md must have YAML frontmatter (starting with ---)"}'
|
|
47
|
+
exit 0
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# Extract YAML frontmatter
|
|
51
|
+
FRONTMATTER=$(echo "$CONTENT" | sed -n '/^---$/,/^---$/p' | tail -n +2 | head -n -1)
|
|
52
|
+
|
|
53
|
+
# Check for unresolved project placeholder
|
|
54
|
+
if echo "$FRONTMATTER" | grep -q 'project:\s*{{PROJECT_ID}}'; then
|
|
55
|
+
echo '{"decision": "block", "reason": "spec.md has unresolved placeholder {{PROJECT_ID}}. Run STEP 0B to select project before creating spec.md."}'
|
|
56
|
+
exit 0
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# Check for unresolved board placeholder
|
|
60
|
+
if echo "$FRONTMATTER" | grep -q 'board:\s*{{BOARD_ID}}'; then
|
|
61
|
+
echo '{"decision": "block", "reason": "spec.md has unresolved placeholder {{BOARD_ID}}. Run STEP 0B to select board before creating spec.md."}'
|
|
62
|
+
exit 0
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
# Check for project field
|
|
66
|
+
PROJECT=$(echo "$FRONTMATTER" | grep -E '^project:\s*' | sed 's/^project:\s*//' | tr -d '"'"'" | tr -d '[:space:]')
|
|
67
|
+
|
|
68
|
+
# Detect structure level by checking config
|
|
69
|
+
PROJECT_ROOT="${FILE_PATH%%/.specweave/*}"
|
|
70
|
+
CONFIG_PATH="$PROJECT_ROOT/.specweave/config.json"
|
|
71
|
+
|
|
72
|
+
IS_2_LEVEL=false
|
|
73
|
+
|
|
74
|
+
if [ -f "$CONFIG_PATH" ]; then
|
|
75
|
+
# Check for ADO area path mapping (indicates 2-level)
|
|
76
|
+
if jq -e '.sync.profiles | to_entries[] | select(.value.provider == "ado") | .value.config.areaPathMapping.mappings | length > 0' "$CONFIG_PATH" >/dev/null 2>&1; then
|
|
77
|
+
IS_2_LEVEL=true
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# Check for ADO areaPaths (indicates 2-level)
|
|
81
|
+
if jq -e '.sync.profiles | to_entries[] | select(.value.provider == "ado") | .value.config.areaPaths | length > 0' "$CONFIG_PATH" >/dev/null 2>&1; then
|
|
82
|
+
IS_2_LEVEL=true
|
|
83
|
+
fi
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
# Validation based on structure level
|
|
87
|
+
if [ "$IS_2_LEVEL" = true ]; then
|
|
88
|
+
# 2-level: BOTH project AND board required
|
|
89
|
+
if [ -z "$PROJECT" ] || [ "$PROJECT" = "null" ]; then
|
|
90
|
+
echo '{"decision": "block", "reason": "spec.md missing required '\''project:'\'' field in YAML frontmatter. This is a 2-level structure - add '\''project: <project_name>'\'' to frontmatter."}'
|
|
91
|
+
exit 0
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
BOARD=$(echo "$FRONTMATTER" | grep -E '^board:\s*' | sed 's/^board:\s*//' | tr -d '"'"'" | tr -d '[:space:]')
|
|
95
|
+
|
|
96
|
+
if [ -z "$BOARD" ] || [ "$BOARD" = "null" ]; then
|
|
97
|
+
echo '{"decision": "block", "reason": "spec.md missing required '\''board:'\'' field in YAML frontmatter. This is a 2-level structure - add '\''board: <board_name>'\'' to frontmatter."}'
|
|
98
|
+
exit 0
|
|
99
|
+
fi
|
|
100
|
+
else
|
|
101
|
+
# 1-level: project is strongly recommended (warning, not block)
|
|
102
|
+
if [ -z "$PROJECT" ] || [ "$PROJECT" = "null" ]; then
|
|
103
|
+
# For 1-level, we warn but don't block (backward compatibility)
|
|
104
|
+
echo '{"decision": "allow", "message": "⚠️ spec.md should have '\''project:'\'' field in YAML frontmatter for explicit sync target."}'
|
|
105
|
+
exit 0
|
|
106
|
+
fi
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
# All validations passed
|
|
110
|
+
echo '{"decision": "allow"}'
|
|
111
|
+
exit 0
|
|
@@ -26,15 +26,24 @@ GITHUB_ENABLED=$(grep -o '"enabled"[[:space:]]*:[[:space:]]*true' "$CONFIG_FILE"
|
|
|
26
26
|
|
|
27
27
|
# Throttle: max once per 5 minutes per increment
|
|
28
28
|
THROTTLE_FILE="$PROJECT_ROOT/.specweave/state/.github-sync-$INC_ID"
|
|
29
|
+
THROTTLE_LOG="$PROJECT_ROOT/.specweave/logs/throttle.log"
|
|
30
|
+
THROTTLE_WINDOW=300 # 5 minutes
|
|
31
|
+
mkdir -p "$(dirname "$THROTTLE_LOG")" 2>/dev/null
|
|
32
|
+
|
|
29
33
|
if [[ -f "$THROTTLE_FILE" ]]; then
|
|
30
34
|
if [[ "$(uname)" == "Darwin" ]]; then
|
|
31
35
|
AGE=$(($(date +%s) - $(stat -f %m "$THROTTLE_FILE" 2>/dev/null || echo 0)))
|
|
32
36
|
else
|
|
33
37
|
AGE=$(($(date +%s) - $(stat -c %Y "$THROTTLE_FILE" 2>/dev/null || echo 0)))
|
|
34
38
|
fi
|
|
35
|
-
[[ $AGE -lt
|
|
39
|
+
if [[ $AGE -lt $THROTTLE_WINDOW ]]; then
|
|
40
|
+
REMAINING=$((THROTTLE_WINDOW - AGE))
|
|
41
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [github-sync] THROTTLED $INC_ID (wait ${REMAINING}s, use /specweave:sync-progress to bypass)" >> "$THROTTLE_LOG" 2>/dev/null
|
|
42
|
+
exit 0
|
|
43
|
+
fi
|
|
36
44
|
fi
|
|
37
45
|
touch "$THROTTLE_FILE"
|
|
46
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [github-sync] EXECUTING $INC_ID" >> "$THROTTLE_LOG" 2>/dev/null
|
|
38
47
|
|
|
39
48
|
# Cross-platform timeout wrapper
|
|
40
49
|
run_with_timeout() {
|
|
@@ -19,15 +19,24 @@ done
|
|
|
19
19
|
|
|
20
20
|
# Throttle: max once per minute per increment
|
|
21
21
|
THROTTLE_FILE="$PROJECT_ROOT/.specweave/state/.living-docs-$INC_ID"
|
|
22
|
+
THROTTLE_LOG="$PROJECT_ROOT/.specweave/logs/throttle.log"
|
|
23
|
+
THROTTLE_WINDOW=60 # 1 minute
|
|
24
|
+
mkdir -p "$(dirname "$THROTTLE_LOG")" 2>/dev/null
|
|
25
|
+
|
|
22
26
|
if [[ -f "$THROTTLE_FILE" ]]; then
|
|
23
27
|
if [[ "$(uname)" == "Darwin" ]]; then
|
|
24
28
|
AGE=$(($(date +%s) - $(stat -f %m "$THROTTLE_FILE" 2>/dev/null || echo 0)))
|
|
25
29
|
else
|
|
26
30
|
AGE=$(($(date +%s) - $(stat -c %Y "$THROTTLE_FILE" 2>/dev/null || echo 0)))
|
|
27
31
|
fi
|
|
28
|
-
[[ $AGE -lt
|
|
32
|
+
if [[ $AGE -lt $THROTTLE_WINDOW ]]; then
|
|
33
|
+
REMAINING=$((THROTTLE_WINDOW - AGE))
|
|
34
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [living-docs] THROTTLED $INC_ID (wait ${REMAINING}s)" >> "$THROTTLE_LOG" 2>/dev/null
|
|
35
|
+
exit 0
|
|
36
|
+
fi
|
|
29
37
|
fi
|
|
30
38
|
touch "$THROTTLE_FILE"
|
|
39
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [living-docs] EXECUTING $INC_ID" >> "$THROTTLE_LOG" 2>/dev/null
|
|
31
40
|
|
|
32
41
|
# Cross-platform timeout wrapper
|
|
33
42
|
run_with_timeout() {
|