specweave 0.23.1 → 0.23.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.
- package/.claude-plugin/marketplace.json +0 -88
- package/CLAUDE.md +368 -0
- package/bin/fix-marketplace-errors.sh +8 -8
- package/dist/plugins/specweave/lib/utils/fs-native.d.ts +133 -0
- package/dist/plugins/specweave/lib/utils/fs-native.d.ts.map +1 -0
- package/dist/plugins/specweave/lib/utils/fs-native.js +224 -0
- package/dist/plugins/specweave/lib/utils/fs-native.js.map +1 -0
- 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-feature-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-feature-sync.js +52 -20
- package/dist/plugins/specweave-github/lib/github-feature-sync.js.map +1 -1
- package/dist/src/cli/helpers/init/initial-increment-generator.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/initial-increment-generator.js +2 -1
- package/dist/src/cli/helpers/init/initial-increment-generator.js.map +1 -1
- package/dist/src/core/ac-test-validator-cli.d.ts +16 -0
- package/dist/src/core/ac-test-validator-cli.d.ts.map +1 -0
- package/dist/src/core/ac-test-validator-cli.js +118 -0
- package/dist/src/core/ac-test-validator-cli.js.map +1 -0
- package/dist/src/core/ac-test-validator.d.ts +111 -0
- package/dist/src/core/ac-test-validator.d.ts.map +1 -0
- package/dist/src/core/ac-test-validator.js +292 -0
- package/dist/src/core/ac-test-validator.js.map +1 -0
- package/dist/src/core/increment/desync-detector.d.ts +142 -0
- package/dist/src/core/increment/desync-detector.d.ts.map +1 -0
- package/dist/src/core/increment/desync-detector.js +270 -0
- package/dist/src/core/increment/desync-detector.js.map +1 -0
- package/dist/src/core/increment/metadata-manager.d.ts +8 -4
- package/dist/src/core/increment/metadata-manager.d.ts.map +1 -1
- package/dist/src/core/increment/metadata-manager.js +45 -21
- package/dist/src/core/increment/metadata-manager.js.map +1 -1
- package/dist/src/core/qa/qa-runner.js +9 -2
- package/dist/src/core/qa/qa-runner.js.map +1 -1
- package/dist/src/sync/sync-coordinator.d.ts +1 -1
- package/dist/src/sync/sync-coordinator.d.ts.map +1 -1
- package/dist/src/sync/sync-coordinator.js +40 -2
- package/dist/src/sync/sync-coordinator.js.map +1 -1
- package/dist/src/utils/fs-native.d.ts +133 -0
- package/dist/src/utils/fs-native.d.ts.map +1 -0
- package/dist/src/utils/fs-native.js +224 -0
- package/dist/src/utils/fs-native.js.map +1 -0
- package/package.json +1 -1
- package/plugins/specweave/.claude-plugin/plugin.json +22 -0
- package/plugins/specweave/agents/AGENTS-INDEX.md +216 -0
- package/plugins/specweave/agents/architect/AGENT.md +17 -0
- package/plugins/specweave/agents/code-standards-detective/AGENT.md +16 -0
- package/plugins/specweave/agents/docs-writer/AGENT.md +16 -0
- package/plugins/specweave/agents/increment-quality-judge-v2/AGENT.md +704 -0
- package/plugins/specweave/agents/infrastructure/AGENT.md +16 -0
- package/plugins/specweave/agents/performance/AGENT.md +16 -0
- package/plugins/specweave/agents/pm/AGENT.md +17 -0
- package/plugins/specweave/agents/qa-lead/AGENT.md +15 -0
- package/plugins/specweave/agents/reflective-reviewer/AGENT.md +16 -0
- package/plugins/specweave/agents/security/AGENT.md +16 -0
- package/plugins/specweave/agents/tdd-orchestrator/AGENT.md +16 -0
- package/plugins/specweave/agents/tech-lead/AGENT.md +16 -0
- package/plugins/specweave/agents/test-aware-planner/AGENT.md +16 -0
- package/plugins/specweave/agents/translator/AGENT.md +13 -0
- package/plugins/specweave/commands/specweave-done.md +14 -0
- package/plugins/specweave/commands/specweave-qa.md +11 -1
- package/plugins/specweave/commands/specweave-sync-status.md +356 -0
- package/plugins/specweave/commands/specweave-validate.md +10 -1
- package/plugins/specweave/hooks/post-metadata-change.sh +160 -0
- package/plugins/specweave/hooks/pre-task-completion.sh +196 -0
- package/plugins/specweave/lib/hooks/git-diff-analyzer.js +3 -3
- package/plugins/specweave/lib/hooks/git-diff-analyzer.ts +3 -3
- package/plugins/specweave/lib/hooks/invoke-translator-skill.js +3 -2
- package/plugins/specweave/lib/hooks/invoke-translator-skill.ts +3 -2
- package/plugins/specweave/lib/hooks/prepare-reflection-context.js +3 -3
- package/plugins/specweave/lib/hooks/prepare-reflection-context.ts +3 -3
- package/plugins/specweave/lib/hooks/reflection-config-loader.js +4 -4
- package/plugins/specweave/lib/hooks/reflection-config-loader.ts +4 -4
- package/plugins/specweave/lib/hooks/reflection-storage.js +9 -9
- package/plugins/specweave/lib/hooks/reflection-storage.ts +9 -9
- package/plugins/specweave/lib/hooks/sync-cache.js +9 -8
- package/plugins/specweave/lib/hooks/sync-living-docs.js +57 -6
- package/plugins/specweave/lib/hooks/sync-us-tasks.js +6 -6
- package/plugins/specweave/lib/hooks/translate-file.js +3 -2
- package/plugins/specweave/lib/hooks/translate-file.ts +3 -2
- package/plugins/specweave/lib/hooks/translate-living-docs.js +4 -3
- package/plugins/specweave/lib/hooks/translate-living-docs.ts +4 -3
- package/plugins/specweave/lib/utils/fs-native.js +182 -0
- package/plugins/specweave/lib/utils/fs-native.ts +283 -0
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.d.ts +8 -4
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js +45 -21
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js.map +1 -1
- package/plugins/specweave/skills/SKILLS-INDEX.md +26 -2
- package/plugins/specweave/skills/increment-planner/SKILL.md +2 -2
- package/plugins/specweave-ado/commands/specweave-ado-close-workitem.md +1 -1
- package/plugins/specweave-ado/commands/specweave-ado-create-workitem.md +1 -1
- package/plugins/specweave-ado/commands/specweave-ado-status.md +1 -1
- package/plugins/specweave-ado/commands/specweave-ado-sync.md +1 -1
- package/plugins/specweave-diagrams/agents/diagrams-architect/AGENT.md +1 -1
- package/plugins/specweave-diagrams/skills/diagrams-generator/SKILL.md +4 -4
- package/plugins/specweave-github/lib/github-client-v2.js +2 -1
- package/plugins/specweave-github/lib/github-client-v2.ts +1 -1
- package/plugins/specweave-github/lib/github-feature-sync.js +30 -17
- package/plugins/specweave-github/lib/github-feature-sync.ts +54 -24
- package/plugins/specweave-mobile/README.md +1 -1
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +72 -0
- package/plugins/specweave/skills/task-builder/README.md +0 -84
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# Post-Metadata-Change Hook: Dispatcher for Increment Lifecycle Events
|
|
4
|
+
#
|
|
5
|
+
# Triggers: After Write/Edit modifies metadata.json
|
|
6
|
+
# Purpose: Detect WHAT changed in metadata and call appropriate lifecycle hook
|
|
7
|
+
#
|
|
8
|
+
# Architecture:
|
|
9
|
+
# - metadata.json is the source of truth for increment state
|
|
10
|
+
# - Different state changes require different actions:
|
|
11
|
+
# * status: "completed" → Call post-increment-completion.sh
|
|
12
|
+
# * status: "paused"|"resumed"|"abandoned" → Call post-increment-status-change.sh
|
|
13
|
+
# * other changes → Update status line only
|
|
14
|
+
#
|
|
15
|
+
# This fixes the critical bug where status line never updates on increment closure
|
|
16
|
+
# because post-increment-completion.sh was orphaned (never registered or called).
|
|
17
|
+
#
|
|
18
|
+
# Related Incident: 2025-11-20 - Increment 0047 completed but status line still shows active
|
|
19
|
+
# Root Cause: metadata.json writes don't trigger status line refresh
|
|
20
|
+
# Fix: This hook dispatches to post-increment-completion.sh which updates status line
|
|
21
|
+
|
|
22
|
+
set -e
|
|
23
|
+
|
|
24
|
+
# Find project root
|
|
25
|
+
find_project_root() {
|
|
26
|
+
local dir="$PWD"
|
|
27
|
+
while [[ "$dir" != "/" ]]; do
|
|
28
|
+
if [[ -d "$dir/.specweave" ]]; then
|
|
29
|
+
echo "$dir"
|
|
30
|
+
return 0
|
|
31
|
+
fi
|
|
32
|
+
dir=$(dirname "$dir")
|
|
33
|
+
done
|
|
34
|
+
echo "$PWD"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
PROJECT_ROOT=$(find_project_root)
|
|
38
|
+
LOGS_DIR="$PROJECT_ROOT/.specweave/logs"
|
|
39
|
+
DEBUG_LOG="$LOGS_DIR/hooks-debug.log"
|
|
40
|
+
HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
41
|
+
|
|
42
|
+
# Ensure logs directory exists
|
|
43
|
+
mkdir -p "$LOGS_DIR" 2>/dev/null || true
|
|
44
|
+
|
|
45
|
+
# Extract modified file from environment variables (Claude Code provides this)
|
|
46
|
+
MODIFIED_FILE=""
|
|
47
|
+
|
|
48
|
+
# Method 1: TOOL_USE_CONTENT environment variable (primary for Write)
|
|
49
|
+
if [[ -n "${TOOL_USE_CONTENT:-}" ]]; then
|
|
50
|
+
MODIFIED_FILE="$TOOL_USE_CONTENT"
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# Method 2: TOOL_RESULT environment variable (fallback)
|
|
54
|
+
if [[ -z "$MODIFIED_FILE" ]] && [[ -n "${TOOL_RESULT:-}" ]]; then
|
|
55
|
+
MODIFIED_FILE=$(echo "$TOOL_RESULT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)".*/\1/' || echo "")
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# Method 3: Parse tool use arguments (last resort for Edit)
|
|
59
|
+
if [[ -z "$MODIFIED_FILE" ]] && [[ -n "${TOOL_USE_ARGS:-}" ]]; then
|
|
60
|
+
MODIFIED_FILE=$(echo "$TOOL_USE_ARGS" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)".*/\1/' || echo "")
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
echo "[$(date)] post-metadata-change: Detected file: ${MODIFIED_FILE:-<none>}" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
64
|
+
|
|
65
|
+
# Check if this is a metadata.json change in an increment folder
|
|
66
|
+
if [[ -z "$MODIFIED_FILE" ]] || [[ "$MODIFIED_FILE" != *"/metadata.json" ]] || [[ "$MODIFIED_FILE" != *"/.specweave/increments/"* ]]; then
|
|
67
|
+
# Not a metadata.json change in increments folder - exit silently
|
|
68
|
+
exit 0
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
# Exclude archived increments (shouldn't affect status line)
|
|
72
|
+
if [[ "$MODIFIED_FILE" == *"/_archive/"* ]]; then
|
|
73
|
+
echo "[$(date)] post-metadata-change: Archived increment - skipping" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
74
|
+
exit 0
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
echo "[$(date)] post-metadata-change: metadata.json changed - analyzing..." >> "$DEBUG_LOG" 2>/dev/null || true
|
|
78
|
+
|
|
79
|
+
# Extract increment ID from path
|
|
80
|
+
# Path format: /path/to/project/.specweave/increments/0047-name/metadata.json
|
|
81
|
+
INCREMENT_ID=$(echo "$MODIFIED_FILE" | grep -o '\.specweave/increments/[^/]*' | sed 's/\.specweave\/increments\///')
|
|
82
|
+
|
|
83
|
+
if [[ -z "$INCREMENT_ID" ]]; then
|
|
84
|
+
echo "[$(date)] post-metadata-change: Could not extract increment ID from path: $MODIFIED_FILE" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
85
|
+
exit 0
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
echo "[$(date)] post-metadata-change: Increment ID: $INCREMENT_ID" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
89
|
+
|
|
90
|
+
# Read the metadata.json to detect what changed
|
|
91
|
+
METADATA_PATH="$PROJECT_ROOT/.specweave/increments/$INCREMENT_ID/metadata.json"
|
|
92
|
+
|
|
93
|
+
if [[ ! -f "$METADATA_PATH" ]]; then
|
|
94
|
+
echo "[$(date)] post-metadata-change: metadata.json not found at $METADATA_PATH" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
95
|
+
exit 0
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
# Check if jq is available for parsing JSON
|
|
99
|
+
if ! command -v jq &> /dev/null; then
|
|
100
|
+
echo "[$(date)] post-metadata-change: jq not found - updating status line as fallback" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
101
|
+
bash "$HOOK_DIR/lib/update-status-line.sh" 2>/dev/null || true
|
|
102
|
+
exit 0
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
# Extract current status
|
|
106
|
+
CURRENT_STATUS=$(jq -r '.status // "unknown"' "$METADATA_PATH" 2>/dev/null)
|
|
107
|
+
|
|
108
|
+
echo "[$(date)] post-metadata-change: Current status: $CURRENT_STATUS" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
109
|
+
|
|
110
|
+
# Dispatch to appropriate lifecycle hook based on status
|
|
111
|
+
case "$CURRENT_STATUS" in
|
|
112
|
+
completed)
|
|
113
|
+
# Increment completed - call post-increment-completion.sh
|
|
114
|
+
# This hook handles:
|
|
115
|
+
# - Closing GitHub issues
|
|
116
|
+
# - Syncing living docs
|
|
117
|
+
# - Updating status line
|
|
118
|
+
echo "[$(date)] post-metadata-change: Increment completed - calling post-increment-completion.sh" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
119
|
+
|
|
120
|
+
if [[ -x "$HOOK_DIR/post-increment-completion.sh" ]]; then
|
|
121
|
+
bash "$HOOK_DIR/post-increment-completion.sh" "$INCREMENT_ID" 2>&1 | tee -a "$DEBUG_LOG" >/dev/null || {
|
|
122
|
+
echo "[$(date)] post-metadata-change: post-increment-completion.sh failed (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
123
|
+
}
|
|
124
|
+
else
|
|
125
|
+
echo "[$(date)] post-metadata-change: post-increment-completion.sh not found or not executable" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
126
|
+
# Fallback: Update status line directly
|
|
127
|
+
bash "$HOOK_DIR/lib/update-status-line.sh" 2>/dev/null || true
|
|
128
|
+
fi
|
|
129
|
+
;;
|
|
130
|
+
|
|
131
|
+
paused|resumed|abandoned)
|
|
132
|
+
# Status change - call post-increment-status-change.sh
|
|
133
|
+
# Note: This typically gets called manually by /specweave:pause commands
|
|
134
|
+
# But we handle it here for completeness
|
|
135
|
+
echo "[$(date)] post-metadata-change: Status changed to $CURRENT_STATUS - calling post-increment-status-change.sh" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
136
|
+
|
|
137
|
+
if [[ -x "$HOOK_DIR/post-increment-status-change.sh" ]]; then
|
|
138
|
+
# Extract reason if available
|
|
139
|
+
REASON=$(jq -r '.statusReason // "Not specified"' "$METADATA_PATH" 2>/dev/null)
|
|
140
|
+
bash "$HOOK_DIR/post-increment-status-change.sh" "$INCREMENT_ID" "$CURRENT_STATUS" "$REASON" 2>&1 | tee -a "$DEBUG_LOG" >/dev/null || {
|
|
141
|
+
echo "[$(date)] post-metadata-change: post-increment-status-change.sh failed (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
142
|
+
}
|
|
143
|
+
else
|
|
144
|
+
echo "[$(date)] post-metadata-change: post-increment-status-change.sh not found" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
145
|
+
# Fallback: Update status line directly
|
|
146
|
+
bash "$HOOK_DIR/lib/update-status-line.sh" 2>/dev/null || true
|
|
147
|
+
fi
|
|
148
|
+
;;
|
|
149
|
+
|
|
150
|
+
*)
|
|
151
|
+
# Other metadata changes (e.g., task completion count, AC count)
|
|
152
|
+
# Just update status line to reflect new progress
|
|
153
|
+
echo "[$(date)] post-metadata-change: Status is $CURRENT_STATUS - updating status line only" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
154
|
+
bash "$HOOK_DIR/lib/update-status-line.sh" 2>/dev/null || true
|
|
155
|
+
;;
|
|
156
|
+
esac
|
|
157
|
+
|
|
158
|
+
echo "[$(date)] post-metadata-change: Complete" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
159
|
+
|
|
160
|
+
exit 0
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# SpecWeave Pre-Task-Completion Hook
|
|
4
|
+
# CRITICAL QUALITY GATE: Validates AC tests before allowing task completion
|
|
5
|
+
#
|
|
6
|
+
# Runs automatically BEFORE any task is marked complete via TodoWrite
|
|
7
|
+
#
|
|
8
|
+
# WORKFLOW:
|
|
9
|
+
# =========
|
|
10
|
+
# 1. TodoWrite called with status="completed"
|
|
11
|
+
# 2. This hook fires (pre-completion validation)
|
|
12
|
+
# 3. Extract task ID from TodoWrite input
|
|
13
|
+
# 4. Find task in tasks.md
|
|
14
|
+
# 5. Run AC test validator
|
|
15
|
+
# 6. If tests PASS → Allow completion (continue: true)
|
|
16
|
+
# 7. If tests FAIL → Block completion (continue: false, show error)
|
|
17
|
+
#
|
|
18
|
+
# ENFORCEMENT:
|
|
19
|
+
# ============
|
|
20
|
+
# This is the ONLY way to mark tasks complete in SpecWeave.
|
|
21
|
+
# Manual edits to tasks.md are detected and flagged by pre-commit hooks.
|
|
22
|
+
|
|
23
|
+
set -e
|
|
24
|
+
|
|
25
|
+
# Find project root
|
|
26
|
+
find_project_root() {
|
|
27
|
+
local dir="$1"
|
|
28
|
+
while [ "$dir" != "/" ]; do
|
|
29
|
+
if [ -d "$dir/.specweave" ]; then
|
|
30
|
+
echo "$dir"
|
|
31
|
+
return 0
|
|
32
|
+
fi
|
|
33
|
+
dir="$(dirname "$dir")"
|
|
34
|
+
done
|
|
35
|
+
pwd
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
PROJECT_ROOT="$(find_project_root "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")"
|
|
39
|
+
cd "$PROJECT_ROOT" 2>/dev/null || true
|
|
40
|
+
|
|
41
|
+
# ============================================================================
|
|
42
|
+
# CONFIGURATION
|
|
43
|
+
# ============================================================================
|
|
44
|
+
|
|
45
|
+
LOGS_DIR=".specweave/logs"
|
|
46
|
+
DEBUG_LOG="$LOGS_DIR/hooks-debug.log"
|
|
47
|
+
|
|
48
|
+
mkdir -p "$LOGS_DIR" 2>/dev/null || true
|
|
49
|
+
|
|
50
|
+
echo "[$(date)] 🔒 Pre-task-completion hook fired" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
51
|
+
|
|
52
|
+
# ============================================================================
|
|
53
|
+
# CAPTURE INPUT
|
|
54
|
+
# ============================================================================
|
|
55
|
+
|
|
56
|
+
STDIN_DATA=$(mktemp)
|
|
57
|
+
cat > "$STDIN_DATA"
|
|
58
|
+
|
|
59
|
+
echo "[$(date)] Input JSON:" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
60
|
+
cat "$STDIN_DATA" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
61
|
+
|
|
62
|
+
# ============================================================================
|
|
63
|
+
# CHECK FOR TASK COMPLETION
|
|
64
|
+
# ============================================================================
|
|
65
|
+
|
|
66
|
+
# Only validate if a task is being marked complete
|
|
67
|
+
COMPLETING_TASK=false
|
|
68
|
+
|
|
69
|
+
if command -v jq >/dev/null 2>&1; then
|
|
70
|
+
# Check if any task is transitioning to "completed" status
|
|
71
|
+
COMPLETED_COUNT=$(jq -r '.tool_input.todos // [] | map(select(.status == "completed")) | length' "$STDIN_DATA" 2>/dev/null || echo "0")
|
|
72
|
+
|
|
73
|
+
if [ "$COMPLETED_COUNT" != "0" ]; then
|
|
74
|
+
COMPLETING_TASK=true
|
|
75
|
+
echo "[$(date)] ✓ Detected task completion (${COMPLETED_COUNT} tasks)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
76
|
+
fi
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
# If no tasks being completed, allow without validation
|
|
80
|
+
if [ "$COMPLETING_TASK" = "false" ]; then
|
|
81
|
+
echo "[$(date)] ⏭️ No tasks being completed, skipping validation" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
82
|
+
rm -f "$STDIN_DATA"
|
|
83
|
+
cat <<EOF
|
|
84
|
+
{
|
|
85
|
+
"continue": true
|
|
86
|
+
}
|
|
87
|
+
EOF
|
|
88
|
+
exit 0
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
# ============================================================================
|
|
92
|
+
# DETECT CURRENT INCREMENT
|
|
93
|
+
# ============================================================================
|
|
94
|
+
|
|
95
|
+
CURRENT_INCREMENT=$(ls -t .specweave/increments/ 2>/dev/null | grep -v "_backlog" | grep -v "_archive" | head -1)
|
|
96
|
+
|
|
97
|
+
if [ -z "$CURRENT_INCREMENT" ]; then
|
|
98
|
+
echo "[$(date)] ⚠️ No active increment found, skipping validation" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
99
|
+
rm -f "$STDIN_DATA"
|
|
100
|
+
cat <<EOF
|
|
101
|
+
{
|
|
102
|
+
"continue": true,
|
|
103
|
+
"systemMessage": "⚠️ Warning: No active increment found. Task completion validation skipped."
|
|
104
|
+
}
|
|
105
|
+
EOF
|
|
106
|
+
exit 0
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
TASKS_MD=".specweave/increments/$CURRENT_INCREMENT/tasks.md"
|
|
110
|
+
|
|
111
|
+
if [ ! -f "$TASKS_MD" ]; then
|
|
112
|
+
echo "[$(date)] ⚠️ tasks.md not found for $CURRENT_INCREMENT" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
113
|
+
rm -f "$STDIN_DATA"
|
|
114
|
+
cat <<EOF
|
|
115
|
+
{
|
|
116
|
+
"continue": true,
|
|
117
|
+
"systemMessage": "⚠️ Warning: tasks.md not found. Task completion validation skipped."
|
|
118
|
+
}
|
|
119
|
+
EOF
|
|
120
|
+
exit 0
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
# ============================================================================
|
|
124
|
+
# RUN AC TEST VALIDATION
|
|
125
|
+
# ============================================================================
|
|
126
|
+
|
|
127
|
+
echo "[$(date)] 🧪 Running AC test validation for $CURRENT_INCREMENT" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
128
|
+
|
|
129
|
+
# Determine which validation script to use
|
|
130
|
+
VALIDATOR_SCRIPT=""
|
|
131
|
+
if [ -f "$PROJECT_ROOT/dist/src/core/ac-test-validator-cli.js" ]; then
|
|
132
|
+
VALIDATOR_SCRIPT="$PROJECT_ROOT/dist/src/core/ac-test-validator-cli.js"
|
|
133
|
+
elif [ -f "$PROJECT_ROOT/node_modules/specweave/dist/src/core/ac-test-validator-cli.js" ]; then
|
|
134
|
+
VALIDATOR_SCRIPT="$PROJECT_ROOT/node_modules/specweave/dist/src/core/ac-test-validator-cli.js"
|
|
135
|
+
elif [ -n "${CLAUDE_PLUGIN_ROOT}" ] && [ -f "${CLAUDE_PLUGIN_ROOT}/dist/src/core/ac-test-validator-cli.js" ]; then
|
|
136
|
+
VALIDATOR_SCRIPT="${CLAUDE_PLUGIN_ROOT}/dist/src/core/ac-test-validator-cli.js"
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
if [ -z "$VALIDATOR_SCRIPT" ] || ! command -v node &> /dev/null; then
|
|
140
|
+
echo "[$(date)] ⚠️ AC test validator not found or Node.js missing" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
141
|
+
rm -f "$STDIN_DATA"
|
|
142
|
+
cat <<EOF
|
|
143
|
+
{
|
|
144
|
+
"continue": true,
|
|
145
|
+
"systemMessage": "⚠️ Warning: AC test validator not available. Task completion validation skipped. Install Node.js and rebuild SpecWeave to enable validation."
|
|
146
|
+
}
|
|
147
|
+
EOF
|
|
148
|
+
exit 0
|
|
149
|
+
fi
|
|
150
|
+
|
|
151
|
+
# Run validator (captures exit code)
|
|
152
|
+
VALIDATION_OUTPUT=$(mktemp)
|
|
153
|
+
VALIDATION_EXIT_CODE=0
|
|
154
|
+
|
|
155
|
+
(cd "$PROJECT_ROOT" && node "$VALIDATOR_SCRIPT" "$CURRENT_INCREMENT") > "$VALIDATION_OUTPUT" 2>&1 || VALIDATION_EXIT_CODE=$?
|
|
156
|
+
|
|
157
|
+
echo "[$(date)] Validator exit code: $VALIDATION_EXIT_CODE" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
158
|
+
cat "$VALIDATION_OUTPUT" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
159
|
+
|
|
160
|
+
rm -f "$STDIN_DATA"
|
|
161
|
+
|
|
162
|
+
# ============================================================================
|
|
163
|
+
# DECISION LOGIC
|
|
164
|
+
# ============================================================================
|
|
165
|
+
|
|
166
|
+
if [ "$VALIDATION_EXIT_CODE" = "0" ]; then
|
|
167
|
+
# Validation passed - allow completion
|
|
168
|
+
echo "[$(date)] ✅ AC test validation passed" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
169
|
+
|
|
170
|
+
VALIDATION_SUMMARY=$(cat "$VALIDATION_OUTPUT" | tail -5 | tr '\n' ' ')
|
|
171
|
+
|
|
172
|
+
rm -f "$VALIDATION_OUTPUT"
|
|
173
|
+
|
|
174
|
+
cat <<EOF
|
|
175
|
+
{
|
|
176
|
+
"continue": true,
|
|
177
|
+
"systemMessage": "✅ AC Test Validation Passed: All acceptance criteria have passing tests. Task completion allowed. ${VALIDATION_SUMMARY}"
|
|
178
|
+
}
|
|
179
|
+
EOF
|
|
180
|
+
else
|
|
181
|
+
# Validation failed - block completion
|
|
182
|
+
echo "[$(date)] ❌ AC test validation failed" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
183
|
+
|
|
184
|
+
VALIDATION_ERROR=$(cat "$VALIDATION_OUTPUT" | grep -A 10 "VALIDATION FAILED" | tr '\n' ' ' | cut -c 1-300)
|
|
185
|
+
|
|
186
|
+
rm -f "$VALIDATION_OUTPUT"
|
|
187
|
+
|
|
188
|
+
cat <<EOF
|
|
189
|
+
{
|
|
190
|
+
"continue": false,
|
|
191
|
+
"systemMessage": "❌ AC TEST VALIDATION FAILED: Cannot mark task as complete until all acceptance criteria have passing tests. ${VALIDATION_ERROR}
|
|
192
|
+
|
|
193
|
+
Fix the failing tests and try again. Run tests manually: npm test"
|
|
194
|
+
}
|
|
195
|
+
EOF
|
|
196
|
+
fi
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execSync } from "child_process";
|
|
2
|
-
import
|
|
2
|
+
import { existsSync, readFileSync } from "fs";
|
|
3
3
|
import path from "path";
|
|
4
4
|
function executeGitCommand(command, cwd) {
|
|
5
5
|
try {
|
|
@@ -65,10 +65,10 @@ function getFileContent(file, cwd) {
|
|
|
65
65
|
try {
|
|
66
66
|
const workingDir = cwd || process.cwd();
|
|
67
67
|
const absolutePath = path.isAbsolute(file) ? file : path.join(workingDir, file);
|
|
68
|
-
if (!
|
|
68
|
+
if (!existsSync(absolutePath)) {
|
|
69
69
|
return "";
|
|
70
70
|
}
|
|
71
|
-
return
|
|
71
|
+
return readFileSync(absolutePath, "utf-8");
|
|
72
72
|
} catch {
|
|
73
73
|
return "";
|
|
74
74
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { execSync } from 'child_process';
|
|
11
|
-
import
|
|
11
|
+
import { existsSync, readFileSync } from 'fs';
|
|
12
12
|
import path from 'path';
|
|
13
13
|
import { GitDiffInfo } from './types/reflection-types';
|
|
14
14
|
|
|
@@ -136,11 +136,11 @@ export function getFileContent(file: string, cwd?: string): string {
|
|
|
136
136
|
const workingDir = cwd || process.cwd();
|
|
137
137
|
const absolutePath = path.isAbsolute(file) ? file : path.join(workingDir, file);
|
|
138
138
|
|
|
139
|
-
if (!
|
|
139
|
+
if (!existsSync(absolutePath)) {
|
|
140
140
|
return '';
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
return
|
|
143
|
+
return readFileSync(absolutePath, 'utf-8');
|
|
144
144
|
} catch {
|
|
145
145
|
return '';
|
|
146
146
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { existsSync } from "fs";
|
|
2
|
+
import { promises as fs } from "fs";
|
|
2
3
|
import {
|
|
3
4
|
detectLanguage,
|
|
4
5
|
prepareTranslation,
|
|
@@ -94,7 +95,7 @@ ${contentToTranslate}`,
|
|
|
94
95
|
}
|
|
95
96
|
}
|
|
96
97
|
async function translateFile(filePath, targetLang = "en") {
|
|
97
|
-
if (!
|
|
98
|
+
if (!existsSync(filePath)) {
|
|
98
99
|
throw new Error(`File not found: ${filePath}`);
|
|
99
100
|
}
|
|
100
101
|
const content = await fs.readFile(filePath, "utf-8");
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
* @see plugins/specweave/commands/translate.md
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import
|
|
11
|
+
import { existsSync } from 'fs';
|
|
12
|
+
import { promises as fs } from 'fs';
|
|
12
13
|
import path from 'path';
|
|
13
14
|
import {
|
|
14
15
|
detectLanguage,
|
|
@@ -186,7 +187,7 @@ export async function translateFile(
|
|
|
186
187
|
targetLang: SupportedLanguage = 'en'
|
|
187
188
|
): Promise<TranslationResult & { filePath: string }> {
|
|
188
189
|
// Read file
|
|
189
|
-
if (!
|
|
190
|
+
if (!existsSync(filePath)) {
|
|
190
191
|
throw new Error(`File not found: ${filePath}`);
|
|
191
192
|
}
|
|
192
193
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { mkdirpSync, writeJsonSync } from "../utils/fs-native.js";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { createReflectionContext } from "./run-self-reflection";
|
|
4
4
|
import { getModifiedFilesSummary } from "./git-diff-analyzer";
|
|
@@ -10,7 +10,7 @@ function prepareReflectionContext(incrementId, taskId, projectRoot) {
|
|
|
10
10
|
}
|
|
11
11
|
const rootDir = projectRoot || process.cwd();
|
|
12
12
|
const tempDir = path.join(rootDir, ".specweave", "increments", incrementId, "logs", "reflections", ".temp");
|
|
13
|
-
|
|
13
|
+
mkdirpSync(tempDir);
|
|
14
14
|
const contextFile = path.join(tempDir, "reflection-context.json");
|
|
15
15
|
const fileStats = getModifiedFilesSummary(context.modifiedFiles);
|
|
16
16
|
const contextData = {
|
|
@@ -30,7 +30,7 @@ function prepareReflectionContext(incrementId, taskId, projectRoot) {
|
|
|
30
30
|
config: context.config,
|
|
31
31
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
32
32
|
};
|
|
33
|
-
|
|
33
|
+
writeJsonSync(contextFile, contextData, { spaces: 2 });
|
|
34
34
|
return contextFile;
|
|
35
35
|
} catch (error) {
|
|
36
36
|
console.error(`Failed to prepare reflection context: ${error.message}`);
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* @module prepare-reflection-context
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import
|
|
10
|
+
import { mkdirpSync, writeJsonSync } from '../utils/fs-native.js';
|
|
11
11
|
import path from 'path';
|
|
12
12
|
import { createReflectionContext } from './run-self-reflection';
|
|
13
13
|
import { getModifiedFilesSummary } from './git-diff-analyzer';
|
|
@@ -37,7 +37,7 @@ export function prepareReflectionContext(
|
|
|
37
37
|
// Create temp directory for reflection context
|
|
38
38
|
const rootDir = projectRoot || process.cwd();
|
|
39
39
|
const tempDir = path.join(rootDir, '.specweave', 'increments', incrementId, 'logs', 'reflections', '.temp');
|
|
40
|
-
|
|
40
|
+
mkdirpSync(tempDir);
|
|
41
41
|
|
|
42
42
|
// Save context to temp file
|
|
43
43
|
const contextFile = path.join(tempDir, 'reflection-context.json');
|
|
@@ -62,7 +62,7 @@ export function prepareReflectionContext(
|
|
|
62
62
|
timestamp: new Date().toISOString()
|
|
63
63
|
};
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
writeJsonSync(contextFile, contextData, { spaces: 2 });
|
|
66
66
|
|
|
67
67
|
return contextFile;
|
|
68
68
|
} catch (error: any) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { existsSync, readFileSync } from "fs";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { DEFAULT_REFLECTION_CONFIG } from "./types/reflection-types";
|
|
4
4
|
function findSpecweaveRoot(startDir = process.cwd()) {
|
|
@@ -6,7 +6,7 @@ function findSpecweaveRoot(startDir = process.cwd()) {
|
|
|
6
6
|
const root = path.parse(currentDir).root;
|
|
7
7
|
while (currentDir !== root) {
|
|
8
8
|
const specweavePath = path.join(currentDir, ".specweave");
|
|
9
|
-
if (
|
|
9
|
+
if (existsSync(specweavePath)) {
|
|
10
10
|
return currentDir;
|
|
11
11
|
}
|
|
12
12
|
currentDir = path.dirname(currentDir);
|
|
@@ -19,11 +19,11 @@ function loadReflectionConfig(projectRoot) {
|
|
|
19
19
|
return { ...DEFAULT_REFLECTION_CONFIG };
|
|
20
20
|
}
|
|
21
21
|
const configPath = path.join(rootDir, ".specweave", "config.json");
|
|
22
|
-
if (!
|
|
22
|
+
if (!existsSync(configPath)) {
|
|
23
23
|
return { ...DEFAULT_REFLECTION_CONFIG };
|
|
24
24
|
}
|
|
25
25
|
try {
|
|
26
|
-
const configContent =
|
|
26
|
+
const configContent = readFileSync(configPath, "utf-8");
|
|
27
27
|
const config = JSON.parse(configContent);
|
|
28
28
|
const userReflectionConfig = config.reflection || {};
|
|
29
29
|
const mergedConfig = {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* @module reflection-config-loader
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import
|
|
10
|
+
import { existsSync, readFileSync } from 'fs';
|
|
11
11
|
import path from 'path';
|
|
12
12
|
import { DEFAULT_REFLECTION_CONFIG, ReflectionConfig } from './types/reflection-types';
|
|
13
13
|
|
|
@@ -22,7 +22,7 @@ export function findSpecweaveRoot(startDir: string = process.cwd()): string | nu
|
|
|
22
22
|
|
|
23
23
|
while (currentDir !== root) {
|
|
24
24
|
const specweavePath = path.join(currentDir, '.specweave');
|
|
25
|
-
if (
|
|
25
|
+
if (existsSync(specweavePath)) {
|
|
26
26
|
return currentDir;
|
|
27
27
|
}
|
|
28
28
|
currentDir = path.dirname(currentDir);
|
|
@@ -51,13 +51,13 @@ export function loadReflectionConfig(projectRoot?: string): ReflectionConfig {
|
|
|
51
51
|
const configPath = path.join(rootDir, '.specweave', 'config.json');
|
|
52
52
|
|
|
53
53
|
// Config file doesn't exist, return defaults
|
|
54
|
-
if (!
|
|
54
|
+
if (!existsSync(configPath)) {
|
|
55
55
|
return { ...DEFAULT_REFLECTION_CONFIG };
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
try {
|
|
59
59
|
// Read and parse config file
|
|
60
|
-
const configContent =
|
|
60
|
+
const configContent = readFileSync(configPath, 'utf-8');
|
|
61
61
|
const config = JSON.parse(configContent);
|
|
62
62
|
|
|
63
63
|
// Extract reflection section (may be undefined)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { mkdirpSync, writeFileSync, existsSync, readdirSync, statSync, readFileSync, removeSync } from "../utils/fs-native.js";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { IssueSeverity } from "./types/reflection-types";
|
|
4
4
|
function generateReflectionMarkdown(result) {
|
|
@@ -183,12 +183,12 @@ function getReflectionFilename(taskId, timestamp) {
|
|
|
183
183
|
}
|
|
184
184
|
function saveReflection(result, incrementId, taskId, projectRoot) {
|
|
185
185
|
const logDir = getReflectionLogDir(incrementId, projectRoot);
|
|
186
|
-
|
|
186
|
+
mkdirpSync(logDir);
|
|
187
187
|
const filename = getReflectionFilename(taskId);
|
|
188
188
|
const filepath = path.join(logDir, filename);
|
|
189
189
|
const markdown = generateReflectionMarkdown(result);
|
|
190
190
|
try {
|
|
191
|
-
|
|
191
|
+
writeFileSync(filepath, markdown, "utf-8");
|
|
192
192
|
return filepath;
|
|
193
193
|
} catch (error) {
|
|
194
194
|
throw new Error(`Failed to save reflection: ${error.message}`);
|
|
@@ -196,26 +196,26 @@ function saveReflection(result, incrementId, taskId, projectRoot) {
|
|
|
196
196
|
}
|
|
197
197
|
function listReflections(incrementId, projectRoot) {
|
|
198
198
|
const logDir = getReflectionLogDir(incrementId, projectRoot);
|
|
199
|
-
if (!
|
|
199
|
+
if (!existsSync(logDir)) {
|
|
200
200
|
return [];
|
|
201
201
|
}
|
|
202
|
-
const files =
|
|
203
|
-
const statA =
|
|
204
|
-
const statB =
|
|
202
|
+
const files = readdirSync(logDir).filter((file) => file.endsWith(".md")).map((file) => path.join(logDir, file)).sort((a, b) => {
|
|
203
|
+
const statA = statSync(a);
|
|
204
|
+
const statB = statSync(b);
|
|
205
205
|
return statB.mtime.getTime() - statA.mtime.getTime();
|
|
206
206
|
});
|
|
207
207
|
return files;
|
|
208
208
|
}
|
|
209
209
|
function readReflection(filepath) {
|
|
210
210
|
try {
|
|
211
|
-
return
|
|
211
|
+
return readFileSync(filepath, "utf-8");
|
|
212
212
|
} catch (error) {
|
|
213
213
|
throw new Error(`Failed to read reflection: ${error.message}`);
|
|
214
214
|
}
|
|
215
215
|
}
|
|
216
216
|
function deleteReflection(filepath) {
|
|
217
217
|
try {
|
|
218
|
-
|
|
218
|
+
removeSync(filepath);
|
|
219
219
|
} catch (error) {
|
|
220
220
|
throw new Error(`Failed to delete reflection: ${error.message}`);
|
|
221
221
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* @module reflection-storage
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import
|
|
10
|
+
import { mkdirpSync, writeFileSync, existsSync, readdirSync, statSync, readFileSync, removeSync } from '../utils/fs-native.js';
|
|
11
11
|
import path from 'path';
|
|
12
12
|
import { ReflectionResult, IssueSeverity } from './types/reflection-types';
|
|
13
13
|
|
|
@@ -294,7 +294,7 @@ export function saveReflection(
|
|
|
294
294
|
): string {
|
|
295
295
|
// Ensure reflection logs directory exists
|
|
296
296
|
const logDir = getReflectionLogDir(incrementId, projectRoot);
|
|
297
|
-
|
|
297
|
+
mkdirpSync(logDir);
|
|
298
298
|
|
|
299
299
|
// Generate filename
|
|
300
300
|
const filename = getReflectionFilename(taskId);
|
|
@@ -305,7 +305,7 @@ export function saveReflection(
|
|
|
305
305
|
|
|
306
306
|
// Write to file
|
|
307
307
|
try {
|
|
308
|
-
|
|
308
|
+
writeFileSync(filepath, markdown, 'utf-8');
|
|
309
309
|
return filepath;
|
|
310
310
|
} catch (error: any) {
|
|
311
311
|
throw new Error(`Failed to save reflection: ${error.message}`);
|
|
@@ -324,17 +324,17 @@ export function listReflections(
|
|
|
324
324
|
): string[] {
|
|
325
325
|
const logDir = getReflectionLogDir(incrementId, projectRoot);
|
|
326
326
|
|
|
327
|
-
if (!
|
|
327
|
+
if (!existsSync(logDir)) {
|
|
328
328
|
return [];
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
-
const files =
|
|
331
|
+
const files = readdirSync(logDir)
|
|
332
332
|
.filter(file => file.endsWith('.md'))
|
|
333
333
|
.map(file => path.join(logDir, file))
|
|
334
334
|
.sort((a, b) => {
|
|
335
335
|
// Sort by modification time (newest first)
|
|
336
|
-
const statA =
|
|
337
|
-
const statB =
|
|
336
|
+
const statA = statSync(a);
|
|
337
|
+
const statB = statSync(b);
|
|
338
338
|
return statB.mtime.getTime() - statA.mtime.getTime();
|
|
339
339
|
});
|
|
340
340
|
|
|
@@ -349,7 +349,7 @@ export function listReflections(
|
|
|
349
349
|
*/
|
|
350
350
|
export function readReflection(filepath: string): string {
|
|
351
351
|
try {
|
|
352
|
-
return
|
|
352
|
+
return readFileSync(filepath, 'utf-8');
|
|
353
353
|
} catch (error: any) {
|
|
354
354
|
throw new Error(`Failed to read reflection: ${error.message}`);
|
|
355
355
|
}
|
|
@@ -362,7 +362,7 @@ export function readReflection(filepath: string): string {
|
|
|
362
362
|
*/
|
|
363
363
|
export function deleteReflection(filepath: string): void {
|
|
364
364
|
try {
|
|
365
|
-
|
|
365
|
+
removeSync(filepath);
|
|
366
366
|
} catch (error: any) {
|
|
367
367
|
throw new Error(`Failed to delete reflection: ${error.message}`);
|
|
368
368
|
}
|