specweave 0.28.36 → 0.28.42

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 (143) hide show
  1. package/CLAUDE.md +21 -0
  2. package/README.md +10 -9
  3. package/bin/specweave.js +2 -1
  4. package/dist/src/cli/commands/archive.d.ts.map +1 -1
  5. package/dist/src/cli/commands/archive.js +2 -1
  6. package/dist/src/cli/commands/archive.js.map +1 -1
  7. package/dist/src/cli/commands/init.d.ts.map +1 -1
  8. package/dist/src/cli/commands/init.js +33 -0
  9. package/dist/src/cli/commands/init.js.map +1 -1
  10. package/dist/src/cli/helpers/ado-area-selector.d.ts +49 -0
  11. package/dist/src/cli/helpers/ado-area-selector.d.ts.map +1 -0
  12. package/dist/src/cli/helpers/ado-area-selector.js +161 -0
  13. package/dist/src/cli/helpers/ado-area-selector.js.map +1 -0
  14. package/dist/src/cli/helpers/init/config-detection.d.ts +5 -1
  15. package/dist/src/cli/helpers/init/config-detection.d.ts.map +1 -1
  16. package/dist/src/cli/helpers/init/config-detection.js +50 -17
  17. package/dist/src/cli/helpers/init/config-detection.js.map +1 -1
  18. package/dist/src/cli/helpers/init/external-import.js +1 -1
  19. package/dist/src/cli/helpers/init/external-import.js.map +1 -1
  20. package/dist/src/cli/helpers/init/repository-setup.d.ts +2 -0
  21. package/dist/src/cli/helpers/init/repository-setup.d.ts.map +1 -1
  22. package/dist/src/cli/helpers/init/repository-setup.js +34 -2
  23. package/dist/src/cli/helpers/init/repository-setup.js.map +1 -1
  24. package/dist/src/cli/helpers/init/types.d.ts +3 -0
  25. package/dist/src/cli/helpers/init/types.d.ts.map +1 -1
  26. package/dist/src/cli/helpers/issue-tracker/ado.d.ts +10 -0
  27. package/dist/src/cli/helpers/issue-tracker/ado.d.ts.map +1 -1
  28. package/dist/src/cli/helpers/issue-tracker/ado.js +107 -22
  29. package/dist/src/cli/helpers/issue-tracker/ado.js.map +1 -1
  30. package/dist/src/cli/helpers/issue-tracker/index.d.ts.map +1 -1
  31. package/dist/src/cli/helpers/issue-tracker/index.js +30 -10
  32. package/dist/src/cli/helpers/issue-tracker/index.js.map +1 -1
  33. package/dist/src/cli/helpers/issue-tracker/types.d.ts +1 -0
  34. package/dist/src/cli/helpers/issue-tracker/types.d.ts.map +1 -1
  35. package/dist/src/cli/helpers/issue-tracker/types.js.map +1 -1
  36. package/dist/src/core/increment/discipline-checker.js +1 -1
  37. package/dist/src/core/increment/increment-archiver.d.ts +13 -0
  38. package/dist/src/core/increment/increment-archiver.d.ts.map +1 -1
  39. package/dist/src/core/increment/increment-archiver.js +60 -3
  40. package/dist/src/core/increment/increment-archiver.js.map +1 -1
  41. package/dist/src/core/living-docs/feature-archiver.d.ts.map +1 -1
  42. package/dist/src/core/living-docs/feature-archiver.js +75 -37
  43. package/dist/src/core/living-docs/feature-archiver.js.map +1 -1
  44. package/dist/src/core/living-docs/living-docs-sync.d.ts +2 -111
  45. package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
  46. package/dist/src/core/living-docs/living-docs-sync.js +18 -383
  47. package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
  48. package/dist/src/core/living-docs/sync-helpers/file-utils.d.ts +30 -0
  49. package/dist/src/core/living-docs/sync-helpers/file-utils.d.ts.map +1 -0
  50. package/dist/src/core/living-docs/sync-helpers/file-utils.js +107 -0
  51. package/dist/src/core/living-docs/sync-helpers/file-utils.js.map +1 -0
  52. package/dist/src/core/living-docs/sync-helpers/generators.d.ts +19 -0
  53. package/dist/src/core/living-docs/sync-helpers/generators.d.ts.map +1 -0
  54. package/dist/src/core/living-docs/sync-helpers/generators.js +146 -0
  55. package/dist/src/core/living-docs/sync-helpers/generators.js.map +1 -0
  56. package/dist/src/core/living-docs/sync-helpers/index.d.ts +8 -0
  57. package/dist/src/core/living-docs/sync-helpers/index.d.ts.map +1 -0
  58. package/dist/src/core/living-docs/sync-helpers/index.js +11 -0
  59. package/dist/src/core/living-docs/sync-helpers/index.js.map +1 -0
  60. package/dist/src/core/living-docs/sync-helpers/parsers.d.ts +19 -0
  61. package/dist/src/core/living-docs/sync-helpers/parsers.d.ts.map +1 -0
  62. package/dist/src/core/living-docs/sync-helpers/parsers.js +94 -0
  63. package/dist/src/core/living-docs/sync-helpers/parsers.js.map +1 -0
  64. package/dist/src/core/living-docs/types.d.ts +45 -0
  65. package/dist/src/core/living-docs/types.d.ts.map +1 -1
  66. package/dist/src/core/types/config.d.ts +1 -1
  67. package/dist/src/core/types/config.js +2 -2
  68. package/dist/src/core/types/config.js.map +1 -1
  69. package/dist/src/importers/ado-importer.d.ts.map +1 -1
  70. package/dist/src/importers/ado-importer.js +2 -0
  71. package/dist/src/importers/ado-importer.js.map +1 -1
  72. package/dist/src/importers/item-converter.d.ts.map +1 -1
  73. package/dist/src/importers/item-converter.js +10 -2
  74. package/dist/src/importers/item-converter.js.map +1 -1
  75. package/dist/src/living-docs/fs-id-allocator.d.ts +5 -0
  76. package/dist/src/living-docs/fs-id-allocator.d.ts.map +1 -1
  77. package/dist/src/living-docs/fs-id-allocator.js +31 -2
  78. package/dist/src/living-docs/fs-id-allocator.js.map +1 -1
  79. package/dist/src/utils/external-resource-validator.d.ts +5 -192
  80. package/dist/src/utils/external-resource-validator.d.ts.map +1 -1
  81. package/dist/src/utils/external-resource-validator.js +10 -1162
  82. package/dist/src/utils/external-resource-validator.js.map +1 -1
  83. package/dist/src/utils/validators/ado-validator.d.ts +86 -0
  84. package/dist/src/utils/validators/ado-validator.d.ts.map +1 -0
  85. package/dist/src/utils/validators/ado-validator.js +528 -0
  86. package/dist/src/utils/validators/ado-validator.js.map +1 -0
  87. package/dist/src/utils/validators/index.d.ts +11 -0
  88. package/dist/src/utils/validators/index.d.ts.map +1 -0
  89. package/dist/src/utils/validators/index.js +12 -0
  90. package/dist/src/utils/validators/index.js.map +1 -0
  91. package/dist/src/utils/validators/jira-validator.d.ts +70 -0
  92. package/dist/src/utils/validators/jira-validator.d.ts.map +1 -0
  93. package/dist/src/utils/validators/jira-validator.js +606 -0
  94. package/dist/src/utils/validators/jira-validator.js.map +1 -0
  95. package/dist/src/utils/validators/types.d.ts +82 -0
  96. package/dist/src/utils/validators/types.d.ts.map +1 -0
  97. package/dist/src/utils/validators/types.js +6 -0
  98. package/dist/src/utils/validators/types.js.map +1 -0
  99. package/package.json +1 -1
  100. package/plugins/specweave/.claude-plugin/plugin.json +7 -62
  101. package/plugins/specweave/commands/specweave-archive.md +3 -3
  102. package/plugins/specweave/commands/specweave-increment.md +18 -19
  103. package/plugins/specweave/hooks/hooks.json +3 -49
  104. package/plugins/specweave/hooks/hooks.json.bak +72 -0
  105. package/plugins/specweave/hooks/hooks.json.v1-backup +16 -0
  106. package/plugins/specweave/hooks/lib/update-status-line.sh +39 -15
  107. package/plugins/specweave/hooks/post-task-edit.sh +10 -0
  108. package/plugins/specweave/hooks/user-prompt-submit.sh +27 -8
  109. package/plugins/specweave/hooks/v2/dispatchers/post-tool-use.sh +44 -0
  110. package/plugins/specweave/hooks/v2/dispatchers/session-start.sh +24 -0
  111. package/plugins/specweave/hooks/v2/handlers/ac-validation-handler.sh +46 -0
  112. package/plugins/specweave/hooks/v2/handlers/github-sync-handler.sh +54 -0
  113. package/plugins/specweave/hooks/v2/handlers/living-docs-handler.sh +46 -0
  114. package/plugins/specweave/hooks/v2/handlers/status-update.sh +50 -0
  115. package/plugins/specweave/hooks/v2/hooks.json +16 -0
  116. package/plugins/specweave/hooks/v2/queue/dequeue.sh +30 -0
  117. package/plugins/specweave/hooks/v2/queue/enqueue.sh +41 -0
  118. package/plugins/specweave/hooks/v2/queue/processor.sh +72 -0
  119. package/plugins/specweave-ado/lib/ado-multi-project-sync.js +0 -1
  120. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +20 -1262
  121. package/plugins/specweave-jira/lib/enhanced-jira-sync.js +3 -3
  122. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +30 -1254
  123. package/src/templates/tasks.md.template +2 -0
  124. package/plugins/specweave/hooks/docs-changed.sh.backup +0 -79
  125. package/plugins/specweave/hooks/human-input-required.sh.backup +0 -75
  126. package/plugins/specweave/hooks/post-first-increment.sh.backup +0 -61
  127. package/plugins/specweave/hooks/post-increment-change.sh.backup +0 -98
  128. package/plugins/specweave/hooks/post-increment-completion.sh.backup +0 -231
  129. package/plugins/specweave/hooks/post-increment-planning.sh.backup +0 -1048
  130. package/plugins/specweave/hooks/post-increment-status-change.sh.backup +0 -147
  131. package/plugins/specweave/hooks/post-spec-update.sh.backup +0 -158
  132. package/plugins/specweave/hooks/post-user-story-complete.sh.backup +0 -179
  133. package/plugins/specweave/hooks/pre-command-deduplication.sh.backup +0 -83
  134. package/plugins/specweave/hooks/pre-implementation.sh.backup +0 -67
  135. package/plugins/specweave/hooks/pre-task-completion.sh.backup +0 -194
  136. package/plugins/specweave/hooks/pre-tool-use.sh.backup +0 -133
  137. package/plugins/specweave/hooks/user-prompt-submit.sh.backup +0 -386
  138. package/plugins/specweave-ado/hooks/post-living-docs-update.sh.backup +0 -353
  139. package/plugins/specweave-ado/hooks/post-task-completion.sh.backup +0 -172
  140. package/plugins/specweave-ado/lib/enhanced-ado-sync.js +0 -170
  141. package/plugins/specweave-github/hooks/post-task-completion.sh.backup +0 -258
  142. package/plugins/specweave-jira/hooks/post-task-completion.sh.backup +0 -172
  143. package/plugins/specweave-release/hooks/post-task-completion.sh.backup +0 -110
@@ -0,0 +1,24 @@
1
+ #!/bin/bash
2
+ # session-start.sh - Launch background processor on session start
3
+ # Ultra-fast, non-blocking
4
+ set +e
5
+
6
+ [[ "${SPECWEAVE_DISABLE_HOOKS:-0}" == "1" ]] && exit 0
7
+
8
+ # Find project root
9
+ PROJECT_ROOT="$PWD"
10
+ while [[ "$PROJECT_ROOT" != "/" ]] && [[ ! -d "$PROJECT_ROOT/.specweave" ]]; do
11
+ PROJECT_ROOT=$(dirname "$PROJECT_ROOT")
12
+ done
13
+ [[ ! -d "$PROJECT_ROOT/.specweave" ]] && exit 0
14
+
15
+ # Consume stdin
16
+ cat > /dev/null
17
+
18
+ PROCESSOR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../queue" && pwd)/processor.sh"
19
+ [[ ! -f "$PROCESSOR" ]] && exit 0
20
+
21
+ # Launch processor in background (daemon mode)
22
+ nohup bash "$PROCESSOR" --daemon > /dev/null 2>&1 &
23
+ disown 2>/dev/null
24
+ exit 0
@@ -0,0 +1,46 @@
1
+ #!/bin/bash
2
+ # ac-validation-handler.sh - Validate AC completion status
3
+ # Checks that completed tasks have their ACs checked in spec.md
4
+ # Non-blocking, logs warnings only
5
+ set +e
6
+
7
+ INC_ID="${1:-}"
8
+ [[ -z "$INC_ID" ]] && exit 0
9
+
10
+ # Find project root
11
+ PROJECT_ROOT="$PWD"
12
+ while [[ "$PROJECT_ROOT" != "/" ]] && [[ ! -d "$PROJECT_ROOT/.specweave" ]]; do
13
+ PROJECT_ROOT=$(dirname "$PROJECT_ROOT")
14
+ done
15
+ [[ ! -d "$PROJECT_ROOT/.specweave" ]] && exit 0
16
+
17
+ INC_DIR="$PROJECT_ROOT/.specweave/increments/$INC_ID"
18
+ TASKS_FILE="$INC_DIR/tasks.md"
19
+ SPEC_FILE="$INC_DIR/spec.md"
20
+ LOG_FILE="$PROJECT_ROOT/.specweave/logs/ac-validation.log"
21
+
22
+ [[ ! -f "$TASKS_FILE" ]] || [[ ! -f "$SPEC_FILE" ]] && exit 0
23
+
24
+ mkdir -p "$(dirname "$LOG_FILE")" 2>/dev/null
25
+
26
+ # Find completed tasks and their ACs
27
+ WARNINGS=""
28
+ while IFS= read -r line; do
29
+ if [[ "$line" =~ ^\*\*Satisfies\ ACs\*\*:\ (.+)$ ]]; then
30
+ ACS="${BASH_REMATCH[1]}"
31
+ # Check each AC
32
+ for ac in $(echo "$ACS" | tr ',' ' '); do
33
+ ac=$(echo "$ac" | tr -d ' ')
34
+ [[ -z "$ac" ]] && continue
35
+ # Check if AC is checked in spec.md
36
+ if ! grep -qE "^\s*-\s*\[x\]\s*\*\*${ac}\*\*" "$SPEC_FILE" 2>/dev/null; then
37
+ WARNINGS="$WARNINGS\n - $ac not checked in spec.md"
38
+ fi
39
+ done
40
+ fi
41
+ done < <(grep -A5 "\[x\] completed" "$TASKS_FILE" 2>/dev/null | grep "Satisfies ACs")
42
+
43
+ if [[ -n "$WARNINGS" ]]; then
44
+ echo "[$(date)] $INC_ID: AC validation warnings:$WARNINGS" >> "$LOG_FILE"
45
+ fi
46
+ exit 0
@@ -0,0 +1,54 @@
1
+ #!/bin/bash
2
+ # github-sync-handler.sh - Sync increment status to GitHub issue
3
+ # Called async by processor, non-blocking, error-tolerant
4
+ set +e
5
+
6
+ INC_ID="${1:-}"
7
+ [[ -z "$INC_ID" ]] && exit 0
8
+
9
+ # Find project root
10
+ PROJECT_ROOT="$PWD"
11
+ while [[ "$PROJECT_ROOT" != "/" ]] && [[ ! -d "$PROJECT_ROOT/.specweave" ]]; do
12
+ PROJECT_ROOT=$(dirname "$PROJECT_ROOT")
13
+ done
14
+ [[ ! -d "$PROJECT_ROOT/.specweave" ]] && exit 0
15
+
16
+ CONFIG_FILE="$PROJECT_ROOT/.specweave/config.json"
17
+ [[ ! -f "$CONFIG_FILE" ]] && exit 0
18
+
19
+ # Check if GitHub sync is enabled
20
+ GITHUB_ENABLED=$(grep -o '"enabled"[[:space:]]*:[[:space:]]*true' "$CONFIG_FILE" | head -1)
21
+ [[ -z "$GITHUB_ENABLED" ]] && exit 0
22
+
23
+ # Throttle: max once per 5 minutes per increment
24
+ THROTTLE_FILE="$PROJECT_ROOT/.specweave/state/.github-sync-$INC_ID"
25
+ if [[ -f "$THROTTLE_FILE" ]]; then
26
+ AGE=$(($(date +%s) - $(stat -f %m "$THROTTLE_FILE" 2>/dev/null || echo 0)))
27
+ [[ $AGE -lt 300 ]] && exit 0
28
+ fi
29
+ touch "$THROTTLE_FILE"
30
+
31
+ # Load GitHub token
32
+ GITHUB_TOKEN=""
33
+ [[ -f "$PROJECT_ROOT/.env" ]] && GITHUB_TOKEN=$(grep -E "^GITHUB_TOKEN=" "$PROJECT_ROOT/.env" | cut -d'=' -f2- | tr -d '"'"'")
34
+ [[ -z "$GITHUB_TOKEN" ]] && exit 0
35
+
36
+ # Find sync script
37
+ SYNC_SCRIPT=""
38
+ for path in \
39
+ "$PROJECT_ROOT/dist/plugins/specweave-github/lib/github-feature-sync-cli.js" \
40
+ "${CLAUDE_PLUGIN_ROOT:-/specweave-github}/lib/github-feature-sync-cli.js"; do
41
+ [[ -f "$path" ]] && { SYNC_SCRIPT="$path"; break; }
42
+ done
43
+ [[ -z "$SYNC_SCRIPT" ]] && exit 0
44
+
45
+ # Extract feature ID
46
+ SPEC_FILE="$PROJECT_ROOT/.specweave/increments/$INC_ID/spec.md"
47
+ FEATURE_ID=""
48
+ [[ -f "$SPEC_FILE" ]] && FEATURE_ID=$(grep -E "^(epic|feature_id):" "$SPEC_FILE" | head -1 | sed 's/.*:[[:space:]]*//' | tr -d '"'"'")
49
+ [[ -z "$FEATURE_ID" ]] && exit 0
50
+
51
+ # Run sync (timeout 60s)
52
+ cd "$PROJECT_ROOT" || exit 0
53
+ GITHUB_TOKEN="$GITHUB_TOKEN" timeout 60 node "$SYNC_SCRIPT" "$FEATURE_ID" >/dev/null 2>&1
54
+ exit 0
@@ -0,0 +1,46 @@
1
+ #!/bin/bash
2
+ # living-docs-handler.sh - Sync increment to living docs
3
+ # Called async by processor, non-blocking, error-tolerant
4
+ set +e
5
+
6
+ INC_ID="${1:-}"
7
+ [[ -z "$INC_ID" ]] && exit 0
8
+
9
+ # Find project root
10
+ PROJECT_ROOT="$PWD"
11
+ while [[ "$PROJECT_ROOT" != "/" ]] && [[ ! -d "$PROJECT_ROOT/.specweave" ]]; do
12
+ PROJECT_ROOT=$(dirname "$PROJECT_ROOT")
13
+ done
14
+ [[ ! -d "$PROJECT_ROOT/.specweave" ]] && exit 0
15
+
16
+ # Throttle: max once per minute per increment
17
+ THROTTLE_FILE="$PROJECT_ROOT/.specweave/state/.living-docs-$INC_ID"
18
+ if [[ -f "$THROTTLE_FILE" ]]; then
19
+ AGE=$(($(date +%s) - $(stat -f %m "$THROTTLE_FILE" 2>/dev/null || echo 0)))
20
+ [[ $AGE -lt 60 ]] && exit 0
21
+ fi
22
+ touch "$THROTTLE_FILE"
23
+
24
+ # Find sync script
25
+ SYNC_SCRIPT=""
26
+ for path in \
27
+ "$PROJECT_ROOT/plugins/specweave/lib/hooks/sync-living-docs.js" \
28
+ "$PROJECT_ROOT/dist/plugins/specweave/lib/hooks/sync-living-docs.js" \
29
+ "${CLAUDE_PLUGIN_ROOT:-}/lib/hooks/sync-living-docs.js"; do
30
+ [[ -f "$path" ]] && { SYNC_SCRIPT="$path"; break; }
31
+ done
32
+ [[ -z "$SYNC_SCRIPT" ]] && exit 0
33
+
34
+ # Extract feature ID from spec.md
35
+ SPEC_FILE="$PROJECT_ROOT/.specweave/increments/$INC_ID/spec.md"
36
+ FEATURE_ID=""
37
+ [[ -f "$SPEC_FILE" ]] && FEATURE_ID=$(grep -E "^(epic|feature_id):" "$SPEC_FILE" | head -1 | sed 's/.*:[[:space:]]*//' | tr -d '"'"'")
38
+
39
+ # Run sync (timeout 30s)
40
+ cd "$PROJECT_ROOT" || exit 0
41
+ if [[ -n "$FEATURE_ID" ]]; then
42
+ FEATURE_ID="$FEATURE_ID" timeout 30 node "$SYNC_SCRIPT" "$INC_ID" >/dev/null 2>&1
43
+ else
44
+ timeout 30 node "$SYNC_SCRIPT" "$INC_ID" >/dev/null 2>&1
45
+ fi
46
+ exit 0
@@ -0,0 +1,50 @@
1
+ #!/bin/bash
2
+ # status-update.sh - Fast status line update (synchronous)
3
+ # Goal: <20ms execution, pure bash, no external processes
4
+ set +e
5
+
6
+ INC_ID="${1:-}"
7
+
8
+ # Find project root
9
+ PROJECT_ROOT="$PWD"
10
+ while [[ "$PROJECT_ROOT" != "/" ]] && [[ ! -d "$PROJECT_ROOT/.specweave" ]]; do
11
+ PROJECT_ROOT=$(dirname "$PROJECT_ROOT")
12
+ done
13
+ [[ ! -d "$PROJECT_ROOT/.specweave" ]] && exit 0
14
+
15
+ STATE_DIR="$PROJECT_ROOT/.specweave/state"
16
+ CACHE_FILE="$STATE_DIR/status-line.json"
17
+ mkdir -p "$STATE_DIR" 2>/dev/null
18
+
19
+ # TTL check (10 seconds)
20
+ if [[ -f "$CACHE_FILE" ]]; then
21
+ CACHE_AGE=$(($(date +%s) - $(stat -f %m "$CACHE_FILE" 2>/dev/null || echo 0)))
22
+ [[ $CACHE_AGE -lt 10 ]] && exit 0
23
+ fi
24
+
25
+ # Find active increment if not provided
26
+ if [[ -z "$INC_ID" ]]; then
27
+ for meta in "$PROJECT_ROOT/.specweave/increments"/[0-9]*/metadata.json; do
28
+ [[ -f "$meta" ]] || continue
29
+ STATUS=$(grep -o '"status"[[:space:]]*:[[:space:]]*"[^"]*"' "$meta" | grep -o '"[^"]*"$' | tr -d '"')
30
+ [[ "$STATUS" == "active" || "$STATUS" == "planning" ]] && {
31
+ INC_ID=$(basename "$(dirname "$meta")")
32
+ break
33
+ }
34
+ done
35
+ fi
36
+ [[ -z "$INC_ID" ]] && { echo '{"current":null}' > "$CACHE_FILE"; exit 0; }
37
+
38
+ TASKS_FILE="$PROJECT_ROOT/.specweave/increments/$INC_ID/tasks.md"
39
+ [[ ! -f "$TASKS_FILE" ]] && exit 0
40
+
41
+ # Count tasks (pure bash, fast)
42
+ TOTAL=$(grep -c "^###\? T-" "$TASKS_FILE" 2>/dev/null || echo 0)
43
+ DONE=$(grep -c "\[x\]" "$TASKS_FILE" 2>/dev/null || echo 0)
44
+ PCT=0; [[ $TOTAL -gt 0 ]] && PCT=$((DONE * 100 / TOTAL))
45
+
46
+ # Write cache (atomic)
47
+ cat > "$CACHE_FILE" << EOF
48
+ {"current":{"id":"$INC_ID","completed":$DONE,"total":$TOTAL,"percentage":$PCT},"ts":"$(date +%s)"}
49
+ EOF
50
+ exit 0
@@ -0,0 +1,16 @@
1
+ {
2
+ "hooks": {
3
+ "PostToolUse": [
4
+ {
5
+ "matcher": "Edit|Write",
6
+ "matcher_content": "\\.specweave/increments/",
7
+ "hooks": [
8
+ {
9
+ "type": "command",
10
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/v2/dispatchers/post-tool-use.sh"
11
+ }
12
+ ]
13
+ }
14
+ ]
15
+ }
16
+ }
@@ -0,0 +1,30 @@
1
+ #!/bin/bash
2
+ # dequeue.sh - Get and remove next event from queue
3
+ # Usage: dequeue.sh [--peek]
4
+ # Returns JSON event or empty if queue is empty
5
+ set +e
6
+
7
+ PEEK=false
8
+ [[ "$1" == "--peek" ]] && PEEK=true
9
+
10
+ # Find project root
11
+ PROJECT_ROOT="$PWD"
12
+ while [[ "$PROJECT_ROOT" != "/" ]] && [[ ! -d "$PROJECT_ROOT/.specweave" ]]; do
13
+ PROJECT_ROOT=$(dirname "$PROJECT_ROOT")
14
+ done
15
+ [[ ! -d "$PROJECT_ROOT/.specweave" ]] && exit 0
16
+
17
+ QUEUE_DIR="$PROJECT_ROOT/.specweave/state/event-queue"
18
+ [[ ! -d "$QUEUE_DIR" ]] && exit 0
19
+
20
+ # Get oldest event file (FIFO)
21
+ OLDEST=$(ls -1 "$QUEUE_DIR"/*.event 2>/dev/null | head -1)
22
+ [[ -z "$OLDEST" ]] && exit 0
23
+
24
+ # Output event content
25
+ cat "$OLDEST" 2>/dev/null
26
+
27
+ # Remove if not peeking
28
+ [[ "$PEEK" == "false" ]] && rm -f "$OLDEST" 2>/dev/null
29
+
30
+ exit 0
@@ -0,0 +1,41 @@
1
+ #!/bin/bash
2
+ # enqueue.sh - Add event to queue with deduplication
3
+ # Usage: enqueue.sh <event_type> <event_data>
4
+ # Events are deduplicated by type+data hash within 5 second window
5
+ set +e
6
+
7
+ EVENT_TYPE="${1:-unknown}"
8
+ EVENT_DATA="${2:-}"
9
+
10
+ # Find project root
11
+ PROJECT_ROOT="$PWD"
12
+ while [[ "$PROJECT_ROOT" != "/" ]] && [[ ! -d "$PROJECT_ROOT/.specweave" ]]; do
13
+ PROJECT_ROOT=$(dirname "$PROJECT_ROOT")
14
+ done
15
+ [[ ! -d "$PROJECT_ROOT/.specweave" ]] && exit 0
16
+
17
+ QUEUE_DIR="$PROJECT_ROOT/.specweave/state/event-queue"
18
+ mkdir -p "$QUEUE_DIR" 2>/dev/null || exit 0
19
+
20
+ # Deduplication: hash event type + data
21
+ HASH=$(echo "${EVENT_TYPE}:${EVENT_DATA}" | md5 | cut -c1-8)
22
+ DEDUP_FILE="$QUEUE_DIR/.dedup-$HASH"
23
+ DEDUP_TTL=5
24
+
25
+ # Check if duplicate (within TTL)
26
+ if [[ -f "$DEDUP_FILE" ]]; then
27
+ AGE=$(($(date +%s) - $(stat -f %m "$DEDUP_FILE" 2>/dev/null || echo 0)))
28
+ [[ $AGE -lt $DEDUP_TTL ]] && exit 0
29
+ fi
30
+ touch "$DEDUP_FILE"
31
+
32
+ # Enqueue event (atomic write)
33
+ TIMESTAMP=$(date +%s%N)
34
+ EVENT_FILE="$QUEUE_DIR/${TIMESTAMP}-${EVENT_TYPE}.event"
35
+ cat > "$EVENT_FILE" << EOF
36
+ {"type":"$EVENT_TYPE","data":"$EVENT_DATA","ts":"$(date -u +%Y-%m-%dT%H:%M:%SZ)"}
37
+ EOF
38
+
39
+ # Cleanup old dedup files (>30s)
40
+ find "$QUEUE_DIR" -name ".dedup-*" -mmin +1 -delete 2>/dev/null &
41
+ exit 0
@@ -0,0 +1,72 @@
1
+ #!/bin/bash
2
+ # processor.sh - Background event processor
3
+ # Processes queued events asynchronously, routes to handlers
4
+ # Usage: processor.sh [--daemon]
5
+ # Self-terminates after 30s of idle
6
+ set +e
7
+
8
+ DAEMON_MODE=false
9
+ [[ "$1" == "--daemon" ]] && DAEMON_MODE=true
10
+
11
+ # Find project root
12
+ PROJECT_ROOT="$PWD"
13
+ while [[ "$PROJECT_ROOT" != "/" ]] && [[ ! -d "$PROJECT_ROOT/.specweave" ]]; do
14
+ PROJECT_ROOT=$(dirname "$PROJECT_ROOT")
15
+ done
16
+ [[ ! -d "$PROJECT_ROOT/.specweave" ]] && exit 0
17
+
18
+ QUEUE_DIR="$PROJECT_ROOT/.specweave/state/event-queue"
19
+ HANDLER_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../handlers" && pwd)"
20
+ LOG_FILE="$PROJECT_ROOT/.specweave/logs/processor.log"
21
+ PID_FILE="$PROJECT_ROOT/.specweave/state/.processor.pid"
22
+ IDLE_TIMEOUT=30
23
+ IDLE_COUNT=0
24
+
25
+ mkdir -p "$(dirname "$LOG_FILE")" 2>/dev/null
26
+
27
+ # Check if already running
28
+ if [[ -f "$PID_FILE" ]]; then
29
+ OLD_PID=$(cat "$PID_FILE" 2>/dev/null)
30
+ if kill -0 "$OLD_PID" 2>/dev/null; then
31
+ exit 0 # Already running
32
+ fi
33
+ fi
34
+ echo $$ > "$PID_FILE"
35
+ trap 'rm -f "$PID_FILE"' EXIT
36
+
37
+ log() { echo "[$(date +%H:%M:%S)] $1" >> "$LOG_FILE" 2>/dev/null; }
38
+ log "Processor started (PID: $$)"
39
+
40
+ process_event() {
41
+ local EVENT_JSON="$1"
42
+ local EVENT_TYPE=$(echo "$EVENT_JSON" | grep -o '"type":"[^"]*"' | cut -d'"' -f4)
43
+ local EVENT_DATA=$(echo "$EVENT_JSON" | grep -o '"data":"[^"]*"' | cut -d'"' -f4)
44
+
45
+ log "Processing: $EVENT_TYPE ($EVENT_DATA)"
46
+
47
+ case "$EVENT_TYPE" in
48
+ task.updated|spec.updated)
49
+ bash "$HANDLER_DIR/living-docs-handler.sh" "$EVENT_DATA" 2>/dev/null
50
+ bash "$HANDLER_DIR/ac-validation-handler.sh" "$EVENT_DATA" 2>/dev/null
51
+ ;;
52
+ metadata.changed)
53
+ bash "$HANDLER_DIR/github-sync-handler.sh" "$EVENT_DATA" 2>/dev/null
54
+ ;;
55
+ esac
56
+ }
57
+
58
+ # Main loop
59
+ while true; do
60
+ EVENT=$(bash "$(dirname "${BASH_SOURCE[0]}")/dequeue.sh" 2>/dev/null)
61
+
62
+ if [[ -n "$EVENT" ]]; then
63
+ IDLE_COUNT=0
64
+ process_event "$EVENT"
65
+ else
66
+ IDLE_COUNT=$((IDLE_COUNT + 1))
67
+ [[ $IDLE_COUNT -ge $IDLE_TIMEOUT ]] && { log "Idle timeout, exiting"; exit 0; }
68
+ sleep 1
69
+ fi
70
+
71
+ [[ "$DAEMON_MODE" == "false" ]] && [[ $IDLE_COUNT -ge 3 ]] && exit 0
72
+ done
@@ -373,7 +373,6 @@ ${userStory.technicalContext}
373
373
  return mapping.task || "Task";
374
374
  case "Subtask":
375
375
  return mapping.task || "Task";
376
- // ADO doesn't have subtasks, use Task
377
376
  default:
378
377
  return "User Story";
379
378
  }