specweave 0.33.2 → 0.33.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/CLAUDE.md +133 -19
  2. package/dist/plugins/specweave-ado/lib/per-us-sync.d.ts +120 -0
  3. package/dist/plugins/specweave-ado/lib/per-us-sync.d.ts.map +1 -0
  4. package/dist/plugins/specweave-ado/lib/per-us-sync.js +276 -0
  5. package/dist/plugins/specweave-ado/lib/per-us-sync.js.map +1 -0
  6. package/dist/plugins/specweave-github/lib/github-client-v2.d.ts +4 -1
  7. package/dist/plugins/specweave-github/lib/github-client-v2.d.ts.map +1 -1
  8. package/dist/plugins/specweave-github/lib/github-client-v2.js +13 -3
  9. package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
  10. package/dist/plugins/specweave-github/lib/per-us-sync.d.ts +97 -0
  11. package/dist/plugins/specweave-github/lib/per-us-sync.d.ts.map +1 -0
  12. package/dist/plugins/specweave-github/lib/per-us-sync.js +274 -0
  13. package/dist/plugins/specweave-github/lib/per-us-sync.js.map +1 -0
  14. package/dist/plugins/specweave-jira/lib/per-us-sync.d.ts +113 -0
  15. package/dist/plugins/specweave-jira/lib/per-us-sync.d.ts.map +1 -0
  16. package/dist/plugins/specweave-jira/lib/per-us-sync.js +254 -0
  17. package/dist/plugins/specweave-jira/lib/per-us-sync.js.map +1 -0
  18. package/dist/src/cli/cleanup-zombies.js +8 -5
  19. package/dist/src/cli/cleanup-zombies.js.map +1 -1
  20. package/dist/src/config/types.d.ts +203 -1208
  21. package/dist/src/config/types.d.ts.map +1 -1
  22. package/dist/src/core/config/config-manager.d.ts.map +1 -1
  23. package/dist/src/core/config/config-manager.js +58 -0
  24. package/dist/src/core/config/config-manager.js.map +1 -1
  25. package/dist/src/core/config/types.d.ts +80 -0
  26. package/dist/src/core/config/types.d.ts.map +1 -1
  27. package/dist/src/core/config/types.js.map +1 -1
  28. package/dist/src/core/living-docs/cross-project-sync.d.ts +87 -15
  29. package/dist/src/core/living-docs/cross-project-sync.d.ts.map +1 -1
  30. package/dist/src/core/living-docs/cross-project-sync.js +147 -28
  31. package/dist/src/core/living-docs/cross-project-sync.js.map +1 -1
  32. package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
  33. package/dist/src/core/living-docs/living-docs-sync.js +26 -22
  34. package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
  35. package/dist/src/core/living-docs/types.d.ts +24 -3
  36. package/dist/src/core/living-docs/types.d.ts.map +1 -1
  37. package/dist/src/core/types/config.d.ts +79 -0
  38. package/dist/src/core/types/config.d.ts.map +1 -1
  39. package/dist/src/core/types/config.js.map +1 -1
  40. package/dist/src/importers/jira-importer.d.ts +10 -0
  41. package/dist/src/importers/jira-importer.d.ts.map +1 -1
  42. package/dist/src/importers/jira-importer.js +55 -5
  43. package/dist/src/importers/jira-importer.js.map +1 -1
  44. package/dist/src/init/architecture/types.d.ts +33 -140
  45. package/dist/src/init/architecture/types.d.ts.map +1 -1
  46. package/dist/src/init/compliance/types.d.ts +30 -27
  47. package/dist/src/init/compliance/types.d.ts.map +1 -1
  48. package/dist/src/init/repo/types.d.ts +11 -34
  49. package/dist/src/init/repo/types.d.ts.map +1 -1
  50. package/dist/src/init/research/src/config/types.d.ts +15 -82
  51. package/dist/src/init/research/src/config/types.d.ts.map +1 -1
  52. package/dist/src/init/research/types.d.ts +38 -93
  53. package/dist/src/init/research/types.d.ts.map +1 -1
  54. package/dist/src/init/team/types.d.ts +4 -42
  55. package/dist/src/init/team/types.d.ts.map +1 -1
  56. package/dist/src/sync/closure-metrics.d.ts +102 -0
  57. package/dist/src/sync/closure-metrics.d.ts.map +1 -0
  58. package/dist/src/sync/closure-metrics.js +267 -0
  59. package/dist/src/sync/closure-metrics.js.map +1 -0
  60. package/dist/src/sync/sync-coordinator.d.ts +49 -0
  61. package/dist/src/sync/sync-coordinator.d.ts.map +1 -1
  62. package/dist/src/sync/sync-coordinator.js +399 -37
  63. package/dist/src/sync/sync-coordinator.js.map +1 -1
  64. package/dist/src/utils/notification-constants.d.ts +85 -0
  65. package/dist/src/utils/notification-constants.d.ts.map +1 -0
  66. package/dist/src/utils/notification-constants.js +129 -0
  67. package/dist/src/utils/notification-constants.js.map +1 -0
  68. package/dist/src/utils/platform-utils.d.ts +13 -3
  69. package/dist/src/utils/platform-utils.d.ts.map +1 -1
  70. package/dist/src/utils/platform-utils.js +17 -6
  71. package/dist/src/utils/platform-utils.js.map +1 -1
  72. package/dist/src/utils/project-resolver.d.ts +156 -0
  73. package/dist/src/utils/project-resolver.d.ts.map +1 -0
  74. package/dist/src/utils/project-resolver.js +587 -0
  75. package/dist/src/utils/project-resolver.js.map +1 -0
  76. package/package.json +1 -1
  77. package/plugins/specweave/commands/specweave-increment.md +46 -0
  78. package/plugins/specweave/commands/specweave-jobs.md +153 -8
  79. package/plugins/specweave/hooks/hooks.json +10 -0
  80. package/plugins/specweave/hooks/spec-project-validator.sh +24 -2
  81. package/plugins/specweave/hooks/universal/hook-wrapper.cmd +26 -26
  82. package/plugins/specweave/hooks/universal/session-start.cmd +16 -16
  83. package/plugins/specweave/hooks/universal/session-start.ps1 +16 -16
  84. package/plugins/specweave/hooks/user-prompt-submit.sh +105 -3
  85. package/plugins/specweave/hooks/v2/guards/per-us-project-validator.sh +281 -0
  86. package/plugins/specweave/hooks/v2/handlers/living-specs-handler.sh +29 -0
  87. package/plugins/specweave/scripts/session-watchdog.sh +278 -130
  88. package/plugins/specweave/skills/increment-planner/SKILL.md +48 -18
  89. package/plugins/specweave/skills/increment-planner/templates/spec-multi-project.md +27 -14
  90. package/plugins/specweave/skills/increment-planner/templates/spec-single-project.md +16 -5
  91. package/plugins/specweave/skills/spec-generator/SKILL.md +74 -15
  92. package/plugins/specweave-ado/lib/per-us-sync.js +247 -0
  93. package/plugins/specweave-ado/lib/per-us-sync.ts +410 -0
  94. package/plugins/specweave-github/lib/github-client-v2.js +10 -3
  95. package/plugins/specweave-github/lib/github-client-v2.ts +15 -3
  96. package/plugins/specweave-github/lib/per-us-sync.js +241 -0
  97. package/plugins/specweave-github/lib/per-us-sync.ts +375 -0
  98. package/plugins/specweave-jira/lib/per-us-sync.js +224 -0
  99. package/plugins/specweave-jira/lib/per-us-sync.ts +366 -0
  100. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +0 -738
  101. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +0 -1107
@@ -364,6 +364,52 @@ Proceeding with hotfix 0006...
364
364
  - Hotfixes and bugs bypass context switching warnings (emergency work)
365
365
  - Use sparingly - discipline exists for a reason!
366
366
 
367
+ ### Step 0D: Get Project Context (MANDATORY - RUN FIRST!)
368
+
369
+ **⛔ THIS STEP MUST BE COMPLETED BEFORE ANY SPEC GENERATION!**
370
+
371
+ **🚨 YOU MUST USE THE BASH TOOL TO RUN THIS COMMAND:**
372
+
373
+ ```bash
374
+ specweave context projects
375
+ ```
376
+
377
+ **CAPTURE THE OUTPUT AND STORE IT:**
378
+
379
+ ```json
380
+ // Example 1-level output:
381
+ {
382
+ "level": 1,
383
+ "projects": [{"id": "frontend-app"}, {"id": "backend-api"}, {"id": "shared"}]
384
+ }
385
+
386
+ // Example 2-level output:
387
+ {
388
+ "level": 2,
389
+ "projects": [{"id": "acme-corp"}],
390
+ "boardsByProject": {
391
+ "acme-corp": [{"id": "digital-ops"}, {"id": "mobile-team"}]
392
+ }
393
+ }
394
+ ```
395
+
396
+ **STORE THESE VALUES FOR USE IN STEP 5:**
397
+ ```
398
+ STRUCTURE_LEVEL = 1 or 2
399
+ AVAILABLE_PROJECTS = ["frontend-app", "backend-api", "shared"]
400
+ AVAILABLE_BOARDS = {...} // for 2-level only
401
+ ```
402
+
403
+ **WHY THIS IS MANDATORY:**
404
+ - Without this data, spec.md will have `{{PROJECT_ID}}` placeholders
405
+ - Placeholders WILL BE BLOCKED by `spec-project-validator.sh` hook
406
+ - Living docs sync and external tool sync WILL FAIL
407
+ - User will get frustrated with blocked edits
408
+
409
+ **YOU MUST ACTUALLY RUN THE COMMAND** - reading documentation about it is NOT enough!
410
+
411
+ ---
412
+
367
413
  ### Step 1: Find next increment number
368
414
 
369
415
  - Scan `.specweave/increments/` directory (active increments)
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: specweave:jobs
3
3
  description: Show current work status (active increments, progress) and background jobs (imports, cloning). Even with no jobs, shows increment summary and helpful context.
4
- usage: /specweave:jobs [--all] [--id <job-id>] [--resume <job-id>] [--kill <job-id>] [--follow <job-id>] [--logs <job-id>]
4
+ usage: /specweave:jobs [--all] [--id <job-id>] [--resume <job-id>] [--kill <job-id>] [--follow <job-id>] [--logs <job-id>] [--diagnostics]
5
5
  ---
6
6
 
7
7
  # Background Jobs Monitor
@@ -17,6 +17,7 @@ Monitor and manage long-running background operations:
17
17
  - **Issue import** (10K+ items from GitHub/JIRA/ADO)
18
18
  - **External sync** operations
19
19
  - **Brownfield analysis** (codebase documentation gap detection)
20
+ - **Session health monitoring** (watchdog diagnostics)
20
21
 
21
22
  **ASYNC ARCHITECTURE (2025-12-01)**:
22
23
  - Jobs run as **detached processes** that survive terminal close
@@ -29,13 +30,14 @@ Monitor and manage long-running background operations:
29
30
 
30
31
  | Option | Description |
31
32
  |--------|-------------|
32
- | (none) | Show active jobs |
33
+ | (none) | Show active jobs + session health |
33
34
  | `--all` | Show all jobs (including completed) |
34
35
  | `--id <jobId>` | Show details for specific job |
35
36
  | `--follow <jobId>` | Follow job progress in real-time |
36
37
  | `--logs <jobId>` | Show worker log output |
37
38
  | `--resume <jobId>` | Resume paused job |
38
39
  | `--kill <jobId>` | Kill running background job |
40
+ | `--diagnostics` | Show detailed watchdog diagnostics |
39
41
 
40
42
  ---
41
43
 
@@ -57,7 +59,9 @@ STATE_FILE=".specweave/state/background-jobs.json"
57
59
  ### Display Format
58
60
 
59
61
  ```
60
- 📋 Background Jobs
62
+ 📋 Background Jobs & Session Health
63
+
64
+ 🩺 Session Health: ✅ healthy (last check: 30s ago)
61
65
 
62
66
  🔄 Running (2):
63
67
  [abc12345] import-issues (ADO)
@@ -87,6 +91,7 @@ STATE_FILE=".specweave/state/background-jobs.json"
87
91
  /specweave:jobs --logs abc12345 → View worker logs
88
92
  /specweave:jobs --resume def67890 → Resume paused job
89
93
  /specweave:jobs --kill abc12345 → Kill running job
94
+ /specweave:jobs --diagnostics → Show watchdog diagnostics
90
95
  /specweave:jobs --all → Show all jobs (including old)
91
96
  ```
92
97
 
@@ -221,14 +226,57 @@ const result = await launchImportJob({
221
226
  ## Implementation
222
227
 
223
228
  1. Read `.specweave/state/background-jobs.json`
224
- 2. Parse job entries
225
- 3. Display formatted status
226
- 4. For --resume, update job status and continue operation
229
+ 2. Read `.specweave/state/.watchdog-diagnostics.json` for session health
230
+ 3. Parse job entries
231
+ 4. Display formatted status with health indicator
232
+ 5. For --resume, update job status and continue operation
233
+ 6. For --diagnostics, show detailed watchdog checks
227
234
 
228
- ### State File Location
235
+ ### State File Locations
229
236
 
230
237
  ```
231
- .specweave/state/background-jobs.json
238
+ .specweave/state/background-jobs.json - Job status and progress
239
+ .specweave/state/.watchdog-diagnostics.json - Session health checks
240
+ .specweave/state/jobs/<jobId>/config.json - Job configuration
241
+ .specweave/state/jobs/<jobId>/worker.pid - Worker process ID
242
+ .specweave/state/jobs/<jobId>/worker.log - Worker output log
243
+ .specweave/logs/watchdog.log - Watchdog history
244
+ ```
245
+
246
+ ### Reading Session Health
247
+
248
+ ```typescript
249
+ import * as fs from 'fs';
250
+ import * as path from 'path';
251
+
252
+ function getSessionHealth(projectPath: string): SessionHealth | null {
253
+ const diagnosticsPath = path.join(projectPath, '.specweave/state/.watchdog-diagnostics.json');
254
+ if (!fs.existsSync(diagnosticsPath)) {
255
+ return null; // Watchdog not running
256
+ }
257
+
258
+ try {
259
+ const content = fs.readFileSync(diagnosticsPath, 'utf-8');
260
+ return JSON.parse(content);
261
+ } catch {
262
+ return null;
263
+ }
264
+ }
265
+
266
+ interface SessionHealth {
267
+ timestamp: string;
268
+ severity: 0 | 1 | 2; // INFO | WARNING | CRITICAL
269
+ status: 'healthy' | 'warning' | 'critical';
270
+ checks: {
271
+ lock: { severity: number; message: string };
272
+ zombies: { count: number; message: string };
273
+ mcp: { drops: number; message: string };
274
+ orphanedJobs: { count: number; message: string };
275
+ };
276
+ consecutiveWarnings: number;
277
+ thresholdSeconds: number;
278
+ checkIntervalSeconds: number;
279
+ }
232
280
  ```
233
281
 
234
282
  ### Job Types
@@ -299,9 +347,106 @@ If job failed:
299
347
 
300
348
  ---
301
349
 
350
+ ## Session Health & Watchdog Diagnostics (v2.0)
351
+
352
+ The `/specweave:jobs` command now includes session health monitoring. The watchdog runs in the background and writes diagnostics that help explain any alerts you may have received.
353
+
354
+ ### Display Format (with health status)
355
+
356
+ ```
357
+ 📋 Background Jobs & Session Health
358
+
359
+ 🩺 Session Health: ✅ healthy
360
+ Last check: 30 seconds ago
361
+ Consecutive warnings: 0/3
362
+
363
+ 🔄 Running (1):
364
+ [abc12345] import-issues (ADO)
365
+ Progress: 2,500/10,000 (25%)
366
+ ...
367
+ ```
368
+
369
+ ### Diagnostics File
370
+
371
+ The watchdog writes diagnostics to `.specweave/state/.watchdog-diagnostics.json`:
372
+
373
+ ```json
374
+ {
375
+ "timestamp": "2025-12-10T10:30:00Z",
376
+ "severity": 0,
377
+ "status": "healthy",
378
+ "checks": {
379
+ "lock": { "severity": 0, "message": "ok" },
380
+ "zombies": { "count": 0, "message": "none" },
381
+ "mcp": { "drops": 2, "message": "minor instability" },
382
+ "orphanedJobs": { "count": 0, "message": "none" }
383
+ },
384
+ "consecutiveWarnings": 0,
385
+ "thresholdSeconds": 300,
386
+ "checkIntervalSeconds": 60
387
+ }
388
+ ```
389
+
390
+ ### View Detailed Diagnostics
391
+
392
+ ```
393
+ /specweave:jobs --diagnostics
394
+
395
+ 🩺 Watchdog Diagnostics
396
+
397
+ Overall Status: ✅ healthy
398
+ Last Check: 2025-12-10 10:30:00
399
+ Severity Level: INFO (0)
400
+
401
+ Checks:
402
+ 📁 Lock File: ✅ ok
403
+ 💀 Zombie Procs: ✅ none (0)
404
+ 🔌 MCP Connection: ⚠️ 2 drops detected (minor instability)
405
+ 📦 Orphaned Jobs: ✅ none (0)
406
+
407
+ Alert Threshold: 3 consecutive warnings
408
+ Current Warnings: 0/3
409
+
410
+ Severity Levels:
411
+ INFO (0) - Everything healthy, no action needed
412
+ WARNING (1) - Minor issue detected, monitoring (NO notification)
413
+ CRITICAL (2) - Real stuck condition detected → NOTIFICATION SENT
414
+
415
+ 💡 The watchdog only sends notifications for CRITICAL issues
416
+ that persist across 3+ consecutive checks (prevents false positives).
417
+ ```
418
+
419
+ ### Why You Got a Notification
420
+
421
+ If you received a notification but your job completed successfully, here's what happened:
422
+
423
+ 1. **Old behavior (v1)**: Watchdog triggered on stale lock files even if no process was stuck
424
+ 2. **New behavior (v2)**: Watchdog verifies actual process state before alerting
425
+
426
+ Common false positive causes (now fixed):
427
+ - Stale `.processor.lock` file from completed job
428
+ - Missing heartbeat file (never written in normal operation)
429
+ - MCP connection drops (warning only, not critical)
430
+
431
+ **The v2 watchdog now requires**:
432
+ 1. **Actual stuck process** (zombie heredoc, hung worker)
433
+ 2. **3 consecutive checks** showing the same issue
434
+ 3. **CRITICAL severity** (not just warnings)
435
+
436
+ ### Watchdog Log
437
+
438
+ View historical watchdog checks:
439
+
440
+ ```bash
441
+ cat .specweave/logs/watchdog.log
442
+ ```
443
+
444
+ ---
445
+
302
446
  ## Notes
303
447
 
304
448
  - Jobs persist across Claude sessions
305
449
  - Paused jobs can be resumed later
306
450
  - Completed jobs cleaned up after 10 entries
307
451
  - Rate limiting auto-pauses and notifies
452
+ - Watchdog diagnostics available via `--diagnostics` flag
@@ -50,6 +50,16 @@
50
50
  }
51
51
  ]
52
52
  },
53
+ {
54
+ "matcher": "Write",
55
+ "matcher_content": "\\.specweave/increments/\\d{3,4}E?-[^/]+/spec\\.md",
56
+ "hooks": [
57
+ {
58
+ "type": "command",
59
+ "command": "bash -c 'W=\"${CLAUDE_PLUGIN_ROOT}/hooks/universal/fail-fast-wrapper.sh\"; S=\"${CLAUDE_PLUGIN_ROOT}/hooks/v2/guards/per-us-project-validator.sh\"; [[ -x \"$W\" ]] && exec \"$W\" \"$S\" || (cat >/dev/null && printf \"{\\\"decision\\\":\\\"allow\\\"}\")'"
60
+ }
61
+ ]
62
+ },
53
63
  {
54
64
  "matcher": "Write|Edit",
55
65
  "matcher_content": "\\.specweave/docs/internal/specs/_features/",
@@ -61,18 +61,40 @@ fi
61
61
  # Extract YAML frontmatter
62
62
  FRONTMATTER=$(echo "$CONTENT" | sed -n '/^---$/,/^---$/p' | tail -n +2 | head -n -1)
63
63
 
64
- # Check for unresolved project placeholder
64
+ # Check for unresolved project placeholder (legacy {{PROJECT_ID}})
65
65
  if echo "$FRONTMATTER" | grep -q 'project:\s*{{PROJECT_ID}}'; then
66
66
  echo '{"decision": "block", "reason": "spec.md has unresolved placeholder {{PROJECT_ID}}. Run '\''specweave context projects'\'' to get available projects, then select one."}'
67
67
  exit 0
68
68
  fi
69
69
 
70
- # Check for unresolved board placeholder
70
+ # Check for unresolved board placeholder (legacy {{BOARD_ID}})
71
71
  if echo "$FRONTMATTER" | grep -q 'board:\s*{{BOARD_ID}}'; then
72
72
  echo '{"decision": "block", "reason": "spec.md has unresolved placeholder {{BOARD_ID}}. Run '\''specweave context boards --project=<id>'\'' to get available boards, then select one."}'
73
73
  exit 0
74
74
  fi
75
75
 
76
+ # Check for ANY unresolved {{...}} placeholder in frontmatter (v0.34.0+)
77
+ # This catches {{RESOLVED_PROJECT}}, {{RESOLVED_BOARD}}, and any other placeholders
78
+ if echo "$FRONTMATTER" | grep -qE '\{\{[A-Z_]+\}\}'; then
79
+ FOUND_PLACEHOLDERS=$(echo "$FRONTMATTER" | grep -oE '\{\{[A-Z_]+\}\}' | tr '\n' ', ' | sed 's/,$//')
80
+ echo "{\"decision\": \"block\", \"reason\": \"spec.md has unresolved placeholders: ${FOUND_PLACEHOLDERS}\\n\\nYOU MUST RESOLVE these BEFORE creating spec.md:\\n1. Run: specweave context projects\\n2. Parse the JSON output to get valid project/board IDs\\n3. Replace placeholders with actual values from step 2\\n\\n❌ FORBIDDEN: Using placeholder templates directly\\n✅ REQUIRED: Resolve ALL placeholders to actual values\"}"
81
+ exit 0
82
+ fi
83
+
84
+ # Check for ANY unresolved {{...}} placeholder in full content (catches per-US **Project**: {{...}})
85
+ if echo "$CONTENT" | grep -qE '\*\*Project\*\*:\s*\{\{[A-Z_]+\}\}'; then
86
+ FOUND_PLACEHOLDERS=$(echo "$CONTENT" | grep -oE '\*\*Project\*\*:\s*\{\{[A-Z_]+\}\}' | head -1)
87
+ echo "{\"decision\": \"block\", \"reason\": \"spec.md has unresolved **Project**: placeholder\\n\\nFound: ${FOUND_PLACEHOLDERS}\\n\\nEach user story MUST have a resolved **Project**: field.\\n\\n1. Run: specweave context projects\\n2. Get valid project IDs from the JSON output\\n3. Replace the placeholder with an actual project ID\"}"
88
+ exit 0
89
+ fi
90
+
91
+ # Check for unresolved **Board**: placeholders in full content (2-level structures)
92
+ if echo "$CONTENT" | grep -qE '\*\*Board\*\*:\s*\{\{[A-Z_]+\}\}'; then
93
+ FOUND_PLACEHOLDERS=$(echo "$CONTENT" | grep -oE '\*\*Board\*\*:\s*\{\{[A-Z_]+\}\}' | head -1)
94
+ echo "{\"decision\": \"block\", \"reason\": \"spec.md has unresolved **Board**: placeholder\\n\\nFound: ${FOUND_PLACEHOLDERS}\\n\\nEach user story MUST have a resolved **Board**: field (for 2-level structures).\\n\\n1. Run: specweave context projects\\n2. Get valid board IDs from boardsByProject in the JSON output\\n3. Replace the placeholder with an actual board ID\"}"
95
+ exit 0
96
+ fi
97
+
76
98
  # Extract project and board from frontmatter
77
99
  PROJECT=$(echo "$FRONTMATTER" | grep -E '^project:\s*' | sed 's/^project:\s*//' | tr -d '"'"'" | tr -d '[:space:]')
78
100
  BOARD=$(echo "$FRONTMATTER" | grep -E '^board:\s*' | sed 's/^board:\s*//' | tr -d '"'"'" | tr -d '[:space:]')
@@ -1,26 +1,26 @@
1
- @echo off
2
- REM hook-wrapper.cmd - Windows resilient hook launcher
3
- REM Prevents crashes when dispatcher.mjs is temporarily unavailable
4
-
5
- setlocal enabledelayedexpansion
6
-
7
- set "HOOK_TYPE=%~1"
8
- if "%HOOK_TYPE%"=="" set "HOOK_TYPE=unknown"
9
-
10
- set "SCRIPT_DIR=%~dp0"
11
- set "DISPATCHER=%SCRIPT_DIR%dispatcher.mjs"
12
-
13
- REM Check if dispatcher exists
14
- if not exist "%DISPATCHER%" (
15
- echo {"continue":true,"systemMessage":"Hook skipped: dispatcher.mjs not found"}
16
- exit /b 0
17
- )
18
-
19
- REM Run dispatcher with error suppression
20
- node "%DISPATCHER%" "%HOOK_TYPE%" 2>nul
21
- if errorlevel 1 (
22
- echo {"continue":true,"systemMessage":"Hook error, continuing"}
23
- exit /b 0
24
- )
25
-
26
- exit /b 0
1
+ @echo off
2
+ REM hook-wrapper.cmd - Windows resilient hook launcher
3
+ REM Prevents crashes when dispatcher.mjs is temporarily unavailable
4
+
5
+ setlocal enabledelayedexpansion
6
+
7
+ set "HOOK_TYPE=%~1"
8
+ if "%HOOK_TYPE%"=="" set "HOOK_TYPE=unknown"
9
+
10
+ set "SCRIPT_DIR=%~dp0"
11
+ set "DISPATCHER=%SCRIPT_DIR%dispatcher.mjs"
12
+
13
+ REM Check if dispatcher exists
14
+ if not exist "%DISPATCHER%" (
15
+ echo {"continue":true,"systemMessage":"Hook skipped: dispatcher.mjs not found"}
16
+ exit /b 0
17
+ )
18
+
19
+ REM Run dispatcher with error suppression
20
+ node "%DISPATCHER%" "%HOOK_TYPE%" 2>nul
21
+ if errorlevel 1 (
22
+ echo {"continue":true,"systemMessage":"Hook error, continuing"}
23
+ exit /b 0
24
+ )
25
+
26
+ exit /b 0
@@ -1,16 +1,16 @@
1
- @echo off
2
- :: Universal Session Start Hook for Windows
3
- :: Calls the Node.js dispatcher
4
-
5
- :: Find node.exe
6
- where node >nul 2>&1
7
- if %ERRORLEVEL% neq 0 (
8
- echo {"continue": true, "error": "Node.js not found"}
9
- exit /b 0
10
- )
11
-
12
- :: Get the directory of this script
13
- set "SCRIPT_DIR=%~dp0"
14
-
15
- :: Run the dispatcher
16
- node "%SCRIPT_DIR%dispatcher.mjs" session-start
1
+ @echo off
2
+ :: Universal Session Start Hook for Windows
3
+ :: Calls the Node.js dispatcher
4
+
5
+ :: Find node.exe
6
+ where node >nul 2>&1
7
+ if %ERRORLEVEL% neq 0 (
8
+ echo {"continue": true, "error": "Node.js not found"}
9
+ exit /b 0
10
+ )
11
+
12
+ :: Get the directory of this script
13
+ set "SCRIPT_DIR=%~dp0"
14
+
15
+ :: Run the dispatcher
16
+ node "%SCRIPT_DIR%dispatcher.mjs" session-start
@@ -1,16 +1,16 @@
1
- # Universal Session Start Hook for Windows PowerShell
2
- # Calls the Node.js dispatcher for cross-platform compatibility
3
-
4
- # Find node.exe
5
- $nodePath = Get-Command node -ErrorAction SilentlyContinue
6
-
7
- if (-not $nodePath) {
8
- Write-Host '{"continue": true, "error": "Node.js not found"}'
9
- exit 0
10
- }
11
-
12
- # Get script directory
13
- $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
14
-
15
- # Run the dispatcher
16
- & node "$scriptDir\dispatcher.mjs" session-start
1
+ # Universal Session Start Hook for Windows PowerShell
2
+ # Calls the Node.js dispatcher for cross-platform compatibility
3
+
4
+ # Find node.exe
5
+ $nodePath = Get-Command node -ErrorAction SilentlyContinue
6
+
7
+ if (-not $nodePath) {
8
+ Write-Host '{"continue": true, "error": "Node.js not found"}'
9
+ exit 0
10
+ }
11
+
12
+ # Get script directory
13
+ $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
14
+
15
+ # Run the dispatcher
16
+ & node "$scriptDir\dispatcher.mjs" session-start
@@ -204,7 +204,91 @@ fi
204
204
  # DISCIPLINE VALIDATION: Warn about WIP limits (configurable, not hard block!)
205
205
  # ==============================================================================
206
206
 
207
- if echo "$PROMPT" | grep -q "/specweave:increment"; then
207
+ # ==============================================================================
208
+ # PROJECT CONTEXT + WIP LIMITS FOR /specweave:increment (v0.34.0)
209
+ # ==============================================================================
210
+ # CRITICAL: Inject project/board context BEFORE Claude generates spec.md
211
+ # This ensures Claude knows available projects and uses correct IDs
212
+ # ALSO: Check WIP limits in same block to avoid duplicate command detection
213
+
214
+ if echo "$PROMPT" | grep -qE "^/specweave:increment"; then
215
+ # Get project context (uses specweave CLI if available)
216
+ PROJECT_CONTEXT=""
217
+
218
+ if command -v specweave >/dev/null 2>&1; then
219
+ # Use CLI for accurate project/board detection
220
+ CONTEXT_JSON=$(specweave context projects 2>/dev/null || echo '{}')
221
+
222
+ # Validate JSON before parsing (defensive coding)
223
+ if [[ -n "$CONTEXT_JSON" ]] && [[ "$CONTEXT_JSON" != "{}" ]]; then
224
+ if command -v jq >/dev/null 2>&1; then
225
+ # Verify JSON is parseable before extracting fields
226
+ if ! echo "$CONTEXT_JSON" | jq empty 2>/dev/null; then
227
+ CONTEXT_JSON='{}' # Invalid JSON - reset to empty
228
+ fi
229
+ fi
230
+ fi
231
+
232
+ if [[ -n "$CONTEXT_JSON" ]] && [[ "$CONTEXT_JSON" != "{}" ]]; then
233
+ # Parse JSON with jq
234
+ if command -v jq >/dev/null 2>&1; then
235
+ LEVEL=$(echo "$CONTEXT_JSON" | jq -r '.level // 1')
236
+ PROJECTS=$(echo "$CONTEXT_JSON" | jq -r '.projects | map(.id) | join(", ")' 2>/dev/null || echo "")
237
+
238
+ if [[ "$LEVEL" == "2" ]]; then
239
+ # 2-level structure: include boards
240
+ BOARDS_JSON=$(echo "$CONTEXT_JSON" | jq -r '.boardsByProject // {}' 2>/dev/null)
241
+ if [[ -n "$BOARDS_JSON" ]] && [[ "$BOARDS_JSON" != "{}" ]]; then
242
+ PROJECT_CONTEXT="\\n\\n📦 PROJECT CONTEXT (2-LEVEL STRUCTURE)\\n\\n"
243
+ PROJECT_CONTEXT="${PROJECT_CONTEXT}⚠️ MANDATORY: Each User Story MUST have both:\\n"
244
+ PROJECT_CONTEXT="${PROJECT_CONTEXT} - **Project**: <project_id>\\n"
245
+ PROJECT_CONTEXT="${PROJECT_CONTEXT} - **Board**: <board_id>\\n\\n"
246
+ PROJECT_CONTEXT="${PROJECT_CONTEXT}Available projects: ${PROJECTS}\\n"
247
+ PROJECT_CONTEXT="${PROJECT_CONTEXT}Boards by project:\\n"
248
+
249
+ # Format boards
250
+ for proj in $(echo "$CONTEXT_JSON" | jq -r '.projects[].id' 2>/dev/null); do
251
+ PROJ_BOARDS=$(echo "$CONTEXT_JSON" | jq -r ".boardsByProject[\"$proj\"] | map(.id) | join(\", \")" 2>/dev/null || echo "")
252
+ [[ -n "$PROJ_BOARDS" ]] && PROJECT_CONTEXT="${PROJECT_CONTEXT} - ${proj}: ${PROJ_BOARDS}\\n"
253
+ done
254
+
255
+ PROJECT_CONTEXT="${PROJECT_CONTEXT}\\n❌ FORBIDDEN: Comma-separated values (e.g., **Project**: fe, be)\\n"
256
+ PROJECT_CONTEXT="${PROJECT_CONTEXT}✅ REQUIRED: One project + one board per User Story"
257
+ fi
258
+ elif [[ -n "$PROJECTS" ]]; then
259
+ # 1-level structure: projects only
260
+ PROJECT_COUNT=$(echo "$CONTEXT_JSON" | jq '.projects | length' 2>/dev/null || echo "0")
261
+
262
+ if [[ "$PROJECT_COUNT" -gt 1 ]]; then
263
+ PROJECT_CONTEXT="\\n\\n📦 PROJECT CONTEXT (MULTI-PROJECT)\\n\\n"
264
+ PROJECT_CONTEXT="${PROJECT_CONTEXT}⚠️ MANDATORY: Each User Story MUST have:\\n"
265
+ PROJECT_CONTEXT="${PROJECT_CONTEXT} - **Project**: <project_id>\\n\\n"
266
+ PROJECT_CONTEXT="${PROJECT_CONTEXT}Available projects: ${PROJECTS}\\n"
267
+ PROJECT_CONTEXT="${PROJECT_CONTEXT}\\n❌ FORBIDDEN: Comma-separated values\\n"
268
+ PROJECT_CONTEXT="${PROJECT_CONTEXT}✅ REQUIRED: One project per User Story"
269
+ elif [[ "$PROJECT_COUNT" -eq 1 ]]; then
270
+ # Single project: auto-select
271
+ SINGLE_PROJECT=$(echo "$CONTEXT_JSON" | jq -r '.projects[0].id' 2>/dev/null)
272
+ PROJECT_CONTEXT="\\n\\n📦 PROJECT CONTEXT\\n"
273
+ PROJECT_CONTEXT="${PROJECT_CONTEXT}Single project detected: ${SINGLE_PROJECT} (auto-selected)"
274
+ fi
275
+ fi
276
+ fi
277
+ fi
278
+ else
279
+ # Fallback: Check for multi-project folders
280
+ if [[ -d "$SPECWEAVE_DIR/docs/internal/specs" ]]; then
281
+ PROJ_COUNT=$(find "$SPECWEAVE_DIR/docs/internal/specs" -maxdepth 1 -type d | wc -l)
282
+ if [[ "$PROJ_COUNT" -gt 2 ]]; then
283
+ PROJ_LIST=$(ls -1 "$SPECWEAVE_DIR/docs/internal/specs" 2>/dev/null | grep -v "_" | tr '\n' ', ' | sed 's/,$//')
284
+ PROJECT_CONTEXT="\\n\\n📦 PROJECT CONTEXT (MULTI-PROJECT)\\n"
285
+ PROJECT_CONTEXT="${PROJECT_CONTEXT}⚠️ MANDATORY: Each User Story MUST have **Project**: field\\n"
286
+ PROJECT_CONTEXT="${PROJECT_CONTEXT}Available folders: ${PROJ_LIST}"
287
+ fi
288
+ fi
289
+ fi
290
+
291
+ # WIP LIMITS CHECK (inside same block - no duplicate command detection)
208
292
  # Read limits from config.json (respect user's settings!)
209
293
  CONFIG_FILE="$SPECWEAVE_DIR/config.json"
210
294
  SOFT_LIMIT=1
@@ -226,13 +310,31 @@ if echo "$PROMPT" | grep -q "/specweave:increment"; then
226
310
 
227
311
  # Above hard cap: strong warning but NOT a block (user decides!)
228
312
  if [[ "$ACTIVE_COUNT" -ge "$HARD_CAP" ]]; then
229
- printf '{"decision":"approve","systemMessage":"⚠️ WIP LIMIT EXCEEDED (%s/%s)\\n\\nYou have %s active increments (configured maximum: %s)\\n\\nActive increments:\\n%s\\n\\n🧠 Research shows 3+ concurrent tasks = 40%% slower + more bugs\\n\\n💡 Options:\\n 1️⃣ Complete an increment: /specweave:done <id>\\n 2️⃣ Pause an increment: /specweave:pause <id>\\n 3️⃣ Increase limit: Edit .specweave/config.json limits.hardCap\\n 4️⃣ Continue anyway (not recommended)\\n\\n📝 To proceed anyway, just confirm your intent."}\n' "$ACTIVE_COUNT" "$HARD_CAP" "$ACTIVE_COUNT" "$HARD_CAP" "$ACTIVE_LIST"
313
+ WIP_MSG="⚠️ WIP LIMIT EXCEEDED (${ACTIVE_COUNT}/${HARD_CAP})\\n\\nYou have ${ACTIVE_COUNT} active increments (configured maximum: ${HARD_CAP})\\n\\nActive increments:\\n${ACTIVE_LIST}\\n\\n🧠 Research shows 3+ concurrent tasks = 40%% slower + more bugs\\n\\n💡 Options:\\n 1️⃣ Complete an increment: /specweave:done <id>\\n 2️⃣ Pause an increment: /specweave:pause <id>\\n 3️⃣ Increase limit: Edit .specweave/config.json limits.hardCap\\n 4️⃣ Continue anyway (not recommended)\\n\\n📝 To proceed anyway, just confirm your intent."
314
+ # Prepend project context if available
315
+ if [[ -n "$PROJECT_CONTEXT" ]]; then
316
+ printf '{"decision":"approve","systemMessage":"%s%s"}\n' "$PROJECT_CONTEXT" "$WIP_MSG"
317
+ else
318
+ printf '{"decision":"approve","systemMessage":"%s"}\n' "$WIP_MSG"
319
+ fi
230
320
  exit 0
231
321
  fi
232
322
 
233
323
  # At soft limit: mild warning, approve
234
324
  if [[ "$ACTIVE_COUNT" -ge "$SOFT_LIMIT" ]]; then
235
- printf '{"decision":"approve","systemMessage":"⚠️ WIP LIMIT REACHED (%s/%s)\\n\\nYou have %s active increment(s) (recommended limit: %s)\\n\\nActive increments:\\n%s\\n\\n🧠 Focus Principle: Fewer active increments = maximum productivity\\n\\n💡 Consider:\\n 1️⃣ Complete current work (recommended)\\n 2️⃣ Pause current work (/specweave:pause)\\n 3️⃣ Continue anyway\\n\\n⚠️ Emergency hotfix/bug? Use --type=hotfix or --type=bug"}\n' "$ACTIVE_COUNT" "$SOFT_LIMIT" "$ACTIVE_COUNT" "$SOFT_LIMIT" "$ACTIVE_LIST"
325
+ WIP_MSG="⚠️ WIP LIMIT REACHED (${ACTIVE_COUNT}/${SOFT_LIMIT})\\n\\nYou have ${ACTIVE_COUNT} active increment(s) (recommended limit: ${SOFT_LIMIT})\\n\\nActive increments:\\n${ACTIVE_LIST}\\n\\n🧠 Focus Principle: Fewer active increments = maximum productivity\\n\\n💡 Consider:\\n 1️⃣ Complete current work (recommended)\\n 2️⃣ Pause current work (/specweave:pause)\\n 3️⃣ Continue anyway\\n\\n⚠️ Emergency hotfix/bug? Use --type=hotfix or --type=bug"
326
+ # Prepend project context if available
327
+ if [[ -n "$PROJECT_CONTEXT" ]]; then
328
+ printf '{"decision":"approve","systemMessage":"%s%s"}\n' "$PROJECT_CONTEXT" "$WIP_MSG"
329
+ else
330
+ printf '{"decision":"approve","systemMessage":"%s"}\n' "$WIP_MSG"
331
+ fi
332
+ exit 0
333
+ fi
334
+
335
+ # No WIP limit warning, but we may have project context to inject
336
+ if [[ -n "$PROJECT_CONTEXT" ]]; then
337
+ printf '{"decision":"approve","systemMessage":"%s"}\n' "$PROJECT_CONTEXT"
236
338
  exit 0
237
339
  fi
238
340
  fi