specweave 1.0.550 → 1.0.552
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +1 -1
- package/bin/specweave.js +23 -1
- package/dist/src/cli/commands/hook.d.ts +15 -0
- package/dist/src/cli/commands/hook.d.ts.map +1 -0
- package/dist/src/cli/commands/hook.js +61 -0
- package/dist/src/cli/commands/hook.js.map +1 -0
- package/dist/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +5 -0
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/refresh-plugins.d.ts.map +1 -1
- package/dist/src/cli/commands/refresh-plugins.js +11 -1
- package/dist/src/cli/commands/refresh-plugins.js.map +1 -1
- package/dist/src/cli/commands/sync-setup.d.ts.map +1 -1
- package/dist/src/cli/commands/sync-setup.js +7 -3
- package/dist/src/cli/commands/sync-setup.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/project-mapping-wizard.d.ts +9 -0
- package/dist/src/cli/helpers/issue-tracker/project-mapping-wizard.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/project-mapping-wizard.js +9 -3
- package/dist/src/cli/helpers/issue-tracker/project-mapping-wizard.js.map +1 -1
- package/dist/src/config/types.d.ts +2 -2
- package/dist/src/core/config/types.d.ts +18 -2
- package/dist/src/core/config/types.d.ts.map +1 -1
- package/dist/src/core/config/types.js.map +1 -1
- package/dist/src/core/hooks/handlers/hook-router.d.ts +19 -0
- package/dist/src/core/hooks/handlers/hook-router.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/hook-router.js +75 -0
- package/dist/src/core/hooks/handlers/hook-router.js.map +1 -0
- package/dist/src/core/hooks/handlers/index.d.ts +10 -0
- package/dist/src/core/hooks/handlers/index.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/index.js +9 -0
- package/dist/src/core/hooks/handlers/index.js.map +1 -0
- package/dist/src/core/hooks/handlers/post-tool-use-analytics.d.ts +11 -0
- package/dist/src/core/hooks/handlers/post-tool-use-analytics.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/post-tool-use-analytics.js +73 -0
- package/dist/src/core/hooks/handlers/post-tool-use-analytics.js.map +1 -0
- package/dist/src/core/hooks/handlers/post-tool-use.d.ts +11 -0
- package/dist/src/core/hooks/handlers/post-tool-use.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/post-tool-use.js +76 -0
- package/dist/src/core/hooks/handlers/post-tool-use.js.map +1 -0
- package/dist/src/core/hooks/handlers/pre-compact.d.ts +11 -0
- package/dist/src/core/hooks/handlers/pre-compact.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/pre-compact.js +77 -0
- package/dist/src/core/hooks/handlers/pre-compact.js.map +1 -0
- package/dist/src/core/hooks/handlers/pre-tool-use.d.ts +11 -0
- package/dist/src/core/hooks/handlers/pre-tool-use.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/pre-tool-use.js +318 -0
- package/dist/src/core/hooks/handlers/pre-tool-use.js.map +1 -0
- package/dist/src/core/hooks/handlers/session-start.d.ts +9 -0
- package/dist/src/core/hooks/handlers/session-start.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/session-start.js +111 -0
- package/dist/src/core/hooks/handlers/session-start.js.map +1 -0
- package/dist/src/core/hooks/handlers/stop-auto.d.ts +16 -0
- package/dist/src/core/hooks/handlers/stop-auto.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/stop-auto.js +122 -0
- package/dist/src/core/hooks/handlers/stop-auto.js.map +1 -0
- package/dist/src/core/hooks/handlers/stop-reflect.d.ts +14 -0
- package/dist/src/core/hooks/handlers/stop-reflect.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/stop-reflect.js +43 -0
- package/dist/src/core/hooks/handlers/stop-reflect.js.map +1 -0
- package/dist/src/core/hooks/handlers/stop-sync.d.ts +15 -0
- package/dist/src/core/hooks/handlers/stop-sync.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/stop-sync.js +68 -0
- package/dist/src/core/hooks/handlers/stop-sync.js.map +1 -0
- package/dist/src/core/hooks/handlers/types.d.ts +63 -0
- package/dist/src/core/hooks/handlers/types.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/types.js +27 -0
- package/dist/src/core/hooks/handlers/types.js.map +1 -0
- package/dist/src/core/hooks/handlers/user-prompt-submit.d.ts +14 -0
- package/dist/src/core/hooks/handlers/user-prompt-submit.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/user-prompt-submit.js +173 -0
- package/dist/src/core/hooks/handlers/user-prompt-submit.js.map +1 -0
- package/dist/src/core/hooks/handlers/utils.d.ts +25 -0
- package/dist/src/core/hooks/handlers/utils.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/utils.js +64 -0
- package/dist/src/core/hooks/handlers/utils.js.map +1 -0
- package/dist/src/core/increment/completion-validator.d.ts.map +1 -1
- package/dist/src/core/increment/completion-validator.js +32 -0
- package/dist/src/core/increment/completion-validator.js.map +1 -1
- package/dist/src/init/research/types.d.ts +1 -1
- package/dist/src/sync/sync-target-resolver.js.map +1 -1
- package/dist/src/utils/lock-manager.d.ts.map +1 -1
- package/dist/src/utils/lock-manager.js +5 -0
- package/dist/src/utils/lock-manager.js.map +1 -1
- package/dist/src/utils/plugin-copier.d.ts +10 -0
- package/dist/src/utils/plugin-copier.d.ts.map +1 -1
- package/dist/src/utils/plugin-copier.js +63 -35
- package/dist/src/utils/plugin-copier.js.map +1 -1
- package/package.json +1 -1
- package/plugins/specweave/agents/sw-closer.md +3 -2
- package/plugins/specweave/hooks/hooks.json +10 -10
- package/plugins/specweave/skills/code-reviewer/SKILL.md +180 -16
- package/plugins/specweave/skills/code-reviewer/agents/reviewer-comments.md +83 -0
- package/plugins/specweave/skills/code-reviewer/agents/reviewer-silent-failures.md +19 -0
- package/plugins/specweave/skills/code-reviewer/agents/reviewer-spec-compliance.md +19 -0
- package/plugins/specweave/skills/code-reviewer/agents/reviewer-tests.md +101 -0
- package/plugins/specweave/skills/code-reviewer/agents/reviewer-types.md +20 -0
- package/plugins/specweave/skills/done/SKILL.md +56 -21
- package/plugins/specweave/skills/grill/SKILL.md +1 -1
- package/plugins/specweave/skills/team-lead/agents/reviewer-logic.md +19 -0
- package/plugins/specweave/skills/team-lead/agents/reviewer-performance.md +20 -0
- package/plugins/specweave/skills/team-lead/agents/reviewer-security.md +20 -0
- package/src/templates/CLAUDE.md.template +7 -4
- package/plugins/specweave/hooks/README.md +0 -493
- package/plugins/specweave/hooks/_archive/stop-auto-v4-legacy.sh +0 -1319
- package/plugins/specweave/hooks/lib/common-setup.sh +0 -144
- package/plugins/specweave/hooks/lib/hook-errors.sh +0 -414
- package/plugins/specweave/hooks/lib/migrate-increment-work.sh +0 -245
- package/plugins/specweave/hooks/lib/resolve-package.sh +0 -146
- package/plugins/specweave/hooks/lib/scheduler-startup.sh +0 -135
- package/plugins/specweave/hooks/lib/score-increment.sh +0 -87
- package/plugins/specweave/hooks/lib/sync-spec-content.sh +0 -193
- package/plugins/specweave/hooks/lib/update-active-increment.sh +0 -95
- package/plugins/specweave/hooks/lib/update-status-line.sh +0 -233
- package/plugins/specweave/hooks/lib/validate-spec-status.sh +0 -171
- package/plugins/specweave/hooks/llm-judge-validator.sh +0 -219
- package/plugins/specweave/hooks/log-decision.sh +0 -168
- package/plugins/specweave/hooks/pre-compact.sh +0 -64
- package/plugins/specweave/hooks/startup-health-check.sh +0 -64
- package/plugins/specweave/hooks/stop-auto-v5.sh +0 -276
- package/plugins/specweave/hooks/stop-reflect.sh +0 -336
- package/plugins/specweave/hooks/stop-sync.sh +0 -283
- package/plugins/specweave/hooks/tests/test-auto-context-integration.sh +0 -126
- package/plugins/specweave/hooks/tests/test-stop-auto-enriched.sh +0 -128
- package/plugins/specweave/hooks/universal/dispatcher.mjs +0 -336
- package/plugins/specweave/hooks/universal/fail-fast-wrapper.sh +0 -325
- package/plugins/specweave/hooks/universal/hook-wrapper.cmd +0 -26
- package/plugins/specweave/hooks/universal/hook-wrapper.sh +0 -69
- package/plugins/specweave/hooks/universal/run-hook.sh +0 -20
- package/plugins/specweave/hooks/universal/session-start.cmd +0 -16
- package/plugins/specweave/hooks/universal/session-start.ps1 +0 -16
- package/plugins/specweave/hooks/user-prompt-submit.sh +0 -2550
- package/plugins/specweave/hooks/v2/detectors/lifecycle-detector.sh +0 -87
- package/plugins/specweave/hooks/v2/detectors/us-completion-detector.sh +0 -186
- package/plugins/specweave/hooks/v2/dispatchers/post-tool-use-analytics.sh +0 -83
- package/plugins/specweave/hooks/v2/dispatchers/post-tool-use.sh +0 -447
- package/plugins/specweave/hooks/v2/dispatchers/pre-tool-use.sh +0 -104
- package/plugins/specweave/hooks/v2/dispatchers/session-start.sh +0 -270
- package/plugins/specweave/hooks/v2/guards/completion-guard.sh +0 -14
- package/plugins/specweave/hooks/v2/guards/increment-duplicate-guard.sh +0 -14
- package/plugins/specweave/hooks/v2/guards/increment-existence-guard.sh +0 -240
- package/plugins/specweave/hooks/v2/guards/interview-enforcement-guard.sh +0 -171
- package/plugins/specweave/hooks/v2/guards/metadata-json-guard.sh +0 -14
- package/plugins/specweave/hooks/v2/guards/skill-chain-enforcement-guard.sh +0 -222
- package/plugins/specweave/hooks/v2/guards/spec-template-enforcement-guard.sh +0 -21
- package/plugins/specweave/hooks/v2/guards/spec-validation-guard.sh +0 -14
- package/plugins/specweave/hooks/v2/guards/status-completion-guard.sh +0 -84
- package/plugins/specweave/hooks/v2/guards/task-ac-sync-guard.sh +0 -475
- package/plugins/specweave/hooks/v2/guards/tdd-enforcement-guard.sh +0 -268
- package/plugins/specweave/hooks/v2/handlers/ac-sync-dispatcher.sh +0 -332
- package/plugins/specweave/hooks/v2/handlers/ac-validation-handler.sh +0 -50
- package/plugins/specweave/hooks/v2/handlers/github-sync-handler.sh +0 -347
- package/plugins/specweave/hooks/v2/handlers/living-docs-handler.sh +0 -83
- package/plugins/specweave/hooks/v2/handlers/living-specs-handler.sh +0 -268
- package/plugins/specweave/hooks/v2/handlers/project-bridge-handler.sh +0 -104
- package/plugins/specweave/hooks/v2/handlers/status-line-handler.sh +0 -165
- package/plugins/specweave/hooks/v2/handlers/status-update.sh +0 -61
- package/plugins/specweave/hooks/v2/handlers/universal-auto-create-dispatcher.sh +0 -270
- package/plugins/specweave/hooks/v2/integrations/ado-post-living-docs-update.sh +0 -367
- package/plugins/specweave/hooks/v2/integrations/ado-post-task.sh +0 -179
- package/plugins/specweave/hooks/v2/integrations/github-auto-create-handler.sh +0 -553
- package/plugins/specweave/hooks/v2/integrations/github-post-task.sh +0 -345
- package/plugins/specweave/hooks/v2/integrations/jira-post-task.sh +0 -180
- package/plugins/specweave/hooks/v2/lib/check-provider-enabled.sh +0 -52
- package/plugins/specweave/hooks/v2/queue/enqueue.sh +0 -81
- package/plugins/specweave/hooks/v2/session-end.sh +0 -139
- package/plugins/specweave/hooks/validate-skill-activations.sh +0 -227
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
#
|
|
3
|
-
# Sync Spec Content to External Tools
|
|
4
|
-
#
|
|
5
|
-
# This script automatically syncs spec CONTENT (title, description, user stories)
|
|
6
|
-
# to external tools (GitHub Issues, JIRA Epics, Azure DevOps Features).
|
|
7
|
-
#
|
|
8
|
-
# CRITICAL: This does NOT sync STATUS - that's managed by external tools.
|
|
9
|
-
#
|
|
10
|
-
# Sync Direction:
|
|
11
|
-
# - Title/Description/User Stories: SpecWeave → External Tool (we update)
|
|
12
|
-
# - Status/State: External Tool → SpecWeave (we read)
|
|
13
|
-
#
|
|
14
|
-
# Usage:
|
|
15
|
-
# sync-spec-content.sh <spec-path>
|
|
16
|
-
#
|
|
17
|
-
# v1.0.71 - Fixed: Use specweave package location, not PROJECT_ROOT/dist
|
|
18
|
-
|
|
19
|
-
set +e # EMERGENCY FIX: Changed from set -euo pipefail to prevent Claude Code crashes
|
|
20
|
-
|
|
21
|
-
# Get script directory
|
|
22
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
23
|
-
|
|
24
|
-
# =============================================================================
|
|
25
|
-
# CRITICAL FIX: Resolve specweave package location for CLI scripts
|
|
26
|
-
# =============================================================================
|
|
27
|
-
resolve_specweave_package() {
|
|
28
|
-
local script_dir="$1"
|
|
29
|
-
|
|
30
|
-
# From hooks/lib/ go up to package root (lib -> hooks -> specweave -> plugins -> root)
|
|
31
|
-
local package_root="$(cd "$script_dir/../../.." 2>/dev/null && pwd)"
|
|
32
|
-
|
|
33
|
-
if [[ -d "$package_root/dist/src/cli" ]]; then
|
|
34
|
-
echo "$package_root"
|
|
35
|
-
return 0
|
|
36
|
-
fi
|
|
37
|
-
|
|
38
|
-
# Fallback: Try global npm installation
|
|
39
|
-
local npm_root
|
|
40
|
-
npm_root="$(npm root -g 2>/dev/null)"
|
|
41
|
-
if [[ -d "$npm_root/specweave/dist/src/cli" ]]; then
|
|
42
|
-
echo "$npm_root/specweave"
|
|
43
|
-
return 0
|
|
44
|
-
fi
|
|
45
|
-
|
|
46
|
-
# Fallback: Try which specweave
|
|
47
|
-
local npx_path
|
|
48
|
-
npx_path="$(which specweave 2>/dev/null)"
|
|
49
|
-
if [[ -n "$npx_path" ]]; then
|
|
50
|
-
local bin_dir="$(dirname "$npx_path")"
|
|
51
|
-
local pkg_dir="$(cd "$bin_dir/.." 2>/dev/null && pwd)"
|
|
52
|
-
if [[ -d "$pkg_dir/dist/src/cli" ]]; then
|
|
53
|
-
echo "$pkg_dir"
|
|
54
|
-
return 0
|
|
55
|
-
fi
|
|
56
|
-
fi
|
|
57
|
-
|
|
58
|
-
return 1
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
SPECWEAVE_PKG="$(resolve_specweave_package "$SCRIPT_DIR")"
|
|
62
|
-
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../../.." && pwd)"
|
|
63
|
-
|
|
64
|
-
# Input validation - exit 0 for safety (never exit 1 in hooks)
|
|
65
|
-
if [ $# -lt 1 ]; then
|
|
66
|
-
echo "Usage: $0 <spec-path>" >&2
|
|
67
|
-
exit 0 # SAFETY: Never use exit 1 in hooks
|
|
68
|
-
fi
|
|
69
|
-
|
|
70
|
-
SPEC_PATH="$1"
|
|
71
|
-
|
|
72
|
-
# Validate spec file exists
|
|
73
|
-
if [ ! -f "$SPEC_PATH" ]; then
|
|
74
|
-
echo "❌ Spec file not found: $SPEC_PATH" >&2
|
|
75
|
-
exit 0 # SAFETY: Never use exit 1 in hooks
|
|
76
|
-
fi
|
|
77
|
-
|
|
78
|
-
# Check if sync is enabled
|
|
79
|
-
CONFIG_FILE="$PROJECT_ROOT/.specweave/config.json"
|
|
80
|
-
if [ ! -f "$CONFIG_FILE" ]; then
|
|
81
|
-
echo "ℹ️ No config file found, skipping spec content sync" >&2
|
|
82
|
-
exit 0
|
|
83
|
-
fi
|
|
84
|
-
|
|
85
|
-
# Check if sync is enabled in config
|
|
86
|
-
SYNC_ENABLED=$(node -p "
|
|
87
|
-
try {
|
|
88
|
-
const config = require('$CONFIG_FILE');
|
|
89
|
-
config.sync?.enabled ?? false;
|
|
90
|
-
} catch {
|
|
91
|
-
false;
|
|
92
|
-
}
|
|
93
|
-
" 2>/dev/null || echo "false")
|
|
94
|
-
|
|
95
|
-
if [ "$SYNC_ENABLED" != "true" ]; then
|
|
96
|
-
echo "ℹ️ Sync disabled in config, skipping spec content sync" >&2
|
|
97
|
-
exit 0
|
|
98
|
-
fi
|
|
99
|
-
|
|
100
|
-
# Check if spec content sync is enabled
|
|
101
|
-
SPEC_SYNC_ENABLED=$(node -p "
|
|
102
|
-
try {
|
|
103
|
-
const config = require('$CONFIG_FILE');
|
|
104
|
-
config.sync?.settings?.syncSpecContent ?? true;
|
|
105
|
-
} catch {
|
|
106
|
-
true;
|
|
107
|
-
}
|
|
108
|
-
" 2>/dev/null || echo "true")
|
|
109
|
-
|
|
110
|
-
if [ "$SPEC_SYNC_ENABLED" != "true" ]; then
|
|
111
|
-
echo "ℹ️ Spec content sync disabled in config" >&2
|
|
112
|
-
exit 0
|
|
113
|
-
fi
|
|
114
|
-
|
|
115
|
-
# Get default profile
|
|
116
|
-
DEFAULT_PROFILE=$(node -p "
|
|
117
|
-
try {
|
|
118
|
-
const config = require('$CONFIG_FILE');
|
|
119
|
-
config.sync?.defaultProfile ?? null;
|
|
120
|
-
} catch {
|
|
121
|
-
null;
|
|
122
|
-
}
|
|
123
|
-
" 2>/dev/null || echo "null")
|
|
124
|
-
|
|
125
|
-
if [ "$DEFAULT_PROFILE" = "null" ]; then
|
|
126
|
-
echo "ℹ️ No default sync profile, skipping spec content sync" >&2
|
|
127
|
-
exit 0
|
|
128
|
-
fi
|
|
129
|
-
|
|
130
|
-
# Get provider from default profile
|
|
131
|
-
PROVIDER=$(node -p "
|
|
132
|
-
try {
|
|
133
|
-
const config = require('$CONFIG_FILE');
|
|
134
|
-
config.sync?.profiles?.['"$DEFAULT_PROFILE"']?.provider ?? null;
|
|
135
|
-
} catch {
|
|
136
|
-
null;
|
|
137
|
-
}
|
|
138
|
-
" 2>/dev/null || echo "null")
|
|
139
|
-
|
|
140
|
-
if [ "$PROVIDER" = "null" ]; then
|
|
141
|
-
echo "ℹ️ No provider configured for profile '$DEFAULT_PROFILE'" >&2
|
|
142
|
-
exit 0
|
|
143
|
-
fi
|
|
144
|
-
|
|
145
|
-
# Validate provider
|
|
146
|
-
if [[ ! "$PROVIDER" =~ ^(github|jira|ado)$ ]]; then
|
|
147
|
-
echo "❌ Invalid provider: $PROVIDER (must be github, jira, or ado)" >&2
|
|
148
|
-
exit 0 # SAFETY: Never use exit 1 in hooks
|
|
149
|
-
fi
|
|
150
|
-
|
|
151
|
-
# Check if sync CLI command exists
|
|
152
|
-
if [[ -z "$SPECWEAVE_PKG" ]]; then
|
|
153
|
-
echo "ℹ️ SpecWeave package not found, skipping spec content sync" >&2
|
|
154
|
-
exit 0
|
|
155
|
-
fi
|
|
156
|
-
|
|
157
|
-
SYNC_CLI="$SPECWEAVE_PKG/dist/src/cli/commands/sync-spec-content.js"
|
|
158
|
-
if [ ! -f "$SYNC_CLI" ]; then
|
|
159
|
-
echo "ℹ️ Sync CLI not found at $SYNC_CLI, skipping spec content sync" >&2
|
|
160
|
-
exit 0
|
|
161
|
-
fi
|
|
162
|
-
|
|
163
|
-
# Check if Node.js is available
|
|
164
|
-
if ! command -v node &> /dev/null; then
|
|
165
|
-
echo "❌ Node.js not found, cannot sync spec content" >&2
|
|
166
|
-
exit 0 # SAFETY: Never use exit 1 in hooks
|
|
167
|
-
fi
|
|
168
|
-
|
|
169
|
-
echo ""
|
|
170
|
-
echo "🔄 Syncing spec content to $PROVIDER..."
|
|
171
|
-
echo " Spec: $(basename "$SPEC_PATH")"
|
|
172
|
-
echo ""
|
|
173
|
-
|
|
174
|
-
# Run sync
|
|
175
|
-
set +e
|
|
176
|
-
node "$SYNC_CLI" --spec "$SPEC_PATH" --provider "$PROVIDER"
|
|
177
|
-
SYNC_EXIT_CODE=$?
|
|
178
|
-
set +e # EMERGENCY FIX: Changed from set -e to prevent Claude Code crashes
|
|
179
|
-
|
|
180
|
-
if [ $SYNC_EXIT_CODE -eq 0 ]; then
|
|
181
|
-
echo ""
|
|
182
|
-
echo "✅ Spec content synced successfully"
|
|
183
|
-
echo ""
|
|
184
|
-
else
|
|
185
|
-
echo ""
|
|
186
|
-
echo "⚠️ Spec content sync failed (exit code: $SYNC_EXIT_CODE)" >&2
|
|
187
|
-
echo " This is non-blocking. You can sync manually later:" >&2
|
|
188
|
-
echo " node dist/src/cli/commands/sync-spec-content.js --spec \"$SPEC_PATH\" --provider $PROVIDER" >&2
|
|
189
|
-
echo ""
|
|
190
|
-
# Non-blocking: continue execution even if sync fails
|
|
191
|
-
fi
|
|
192
|
-
|
|
193
|
-
exit 0
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
# update-active-increment.sh (v0.26.15)
|
|
4
|
-
#
|
|
5
|
-
# Maintains .specweave/state/active-increment.json in sync with metadata.json
|
|
6
|
-
#
|
|
7
|
-
# CRITICAL FIX: Hooks depend on active-increment.json to know which increments to sync.
|
|
8
|
-
# Without this, post-task-completion.sh sees empty array and skips ALL sync work!
|
|
9
|
-
#
|
|
10
|
-
# See: Root cause analysis in increment 0059
|
|
11
|
-
#
|
|
12
|
-
set +e
|
|
13
|
-
|
|
14
|
-
# ============================================================================
|
|
15
|
-
# PROJECT ROOT DETECTION (CRITICAL - must NOT fallback to pwd!)
|
|
16
|
-
# ============================================================================
|
|
17
|
-
if [[ -n "$SPECWEAVE_PROJECT_ROOT" ]] && [[ -f "$SPECWEAVE_PROJECT_ROOT/.specweave/config.json" ]]; then
|
|
18
|
-
PROJECT_ROOT="$SPECWEAVE_PROJECT_ROOT"
|
|
19
|
-
else
|
|
20
|
-
PROJECT_ROOT="$PWD"
|
|
21
|
-
while [[ "$PROJECT_ROOT" != "/" ]] && [[ ! -f "$PROJECT_ROOT/.specweave/config.json" ]]; do
|
|
22
|
-
PROJECT_ROOT=$(dirname "$PROJECT_ROOT")
|
|
23
|
-
done
|
|
24
|
-
fi
|
|
25
|
-
|
|
26
|
-
# No .specweave/config.json? Exit BEFORE any mkdir (prevents .specweave pollution)
|
|
27
|
-
[[ ! -f "$PROJECT_ROOT/.specweave/config.json" ]] && exit 0
|
|
28
|
-
|
|
29
|
-
STATE_DIR="$PROJECT_ROOT/.specweave/state"
|
|
30
|
-
ACTIVE_FILE="$STATE_DIR/active-increment.json"
|
|
31
|
-
INCREMENTS_DIR="$PROJECT_ROOT/.specweave/increments"
|
|
32
|
-
DEBUG_LOG="$PROJECT_ROOT/.specweave/logs/hooks-debug.log"
|
|
33
|
-
|
|
34
|
-
mkdir -p "$STATE_DIR" 2>/dev/null || true
|
|
35
|
-
|
|
36
|
-
# ============================================================================
|
|
37
|
-
# SCAN ALL INCREMENTS FOR ACTIVE STATUS
|
|
38
|
-
# ============================================================================
|
|
39
|
-
# Strategy: Scan metadata.json files for status=active|planning|backlog|ready_for_review
|
|
40
|
-
# This matches what ActiveIncrementManager.smartUpdate() does in TypeScript
|
|
41
|
-
|
|
42
|
-
ACTIVE_IDS=()
|
|
43
|
-
|
|
44
|
-
while IFS= read -r metadata_file; do
|
|
45
|
-
[[ -z "$metadata_file" ]] && continue
|
|
46
|
-
|
|
47
|
-
# Extract status from metadata.json
|
|
48
|
-
if command -v jq >/dev/null 2>&1; then
|
|
49
|
-
status=$(jq -r '.status // "unknown"' "$metadata_file" 2>/dev/null || echo "unknown")
|
|
50
|
-
else
|
|
51
|
-
# Fallback: sed parsing (portable - works on macOS BSD and Linux GNU)
|
|
52
|
-
status=$(sed -n 's/.*"status"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$metadata_file" 2>/dev/null || echo "unknown")
|
|
53
|
-
fi
|
|
54
|
-
|
|
55
|
-
# Check if status is active-like
|
|
56
|
-
if [[ "$status" == "active" ]] || [[ "$status" == "planning" ]] || [[ "$status" == "backlog" ]] || [[ "$status" == "ready_for_review" ]]; then
|
|
57
|
-
increment_id=$(basename "$(dirname "$metadata_file")")
|
|
58
|
-
ACTIVE_IDS+=("$increment_id")
|
|
59
|
-
echo "[$(date)] update-active-increment: Found active: $increment_id (status: $status)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
60
|
-
fi
|
|
61
|
-
done < <(find "$INCREMENTS_DIR" -maxdepth 2 -name "metadata.json" -not -path "*/_archive/*" 2>/dev/null)
|
|
62
|
-
|
|
63
|
-
# ============================================================================
|
|
64
|
-
# WRITE ACTIVE INCREMENT STATE (max 2)
|
|
65
|
-
# ============================================================================
|
|
66
|
-
# Take first 2 active increments (matches ActiveIncrementManager behavior)
|
|
67
|
-
ACTIVE_IDS=("${ACTIVE_IDS[@]:0:2}")
|
|
68
|
-
|
|
69
|
-
# Build JSON array
|
|
70
|
-
if [[ ${#ACTIVE_IDS[@]} -eq 0 ]]; then
|
|
71
|
-
IDS_JSON="[]"
|
|
72
|
-
else
|
|
73
|
-
# Build JSON array manually (no jq dependency)
|
|
74
|
-
IDS_JSON="["
|
|
75
|
-
for i in "${!ACTIVE_IDS[@]}"; do
|
|
76
|
-
[[ $i -gt 0 ]] && IDS_JSON+=","
|
|
77
|
-
IDS_JSON+="\"${ACTIVE_IDS[$i]}\""
|
|
78
|
-
done
|
|
79
|
-
IDS_JSON+="]"
|
|
80
|
-
fi
|
|
81
|
-
|
|
82
|
-
# Atomic write
|
|
83
|
-
TEMP_FILE="$ACTIVE_FILE.tmp"
|
|
84
|
-
cat > "$TEMP_FILE" << EOF
|
|
85
|
-
{
|
|
86
|
-
"ids": $IDS_JSON,
|
|
87
|
-
"lastUpdated": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
88
|
-
}
|
|
89
|
-
EOF
|
|
90
|
-
|
|
91
|
-
mv "$TEMP_FILE" "$ACTIVE_FILE"
|
|
92
|
-
|
|
93
|
-
echo "[$(date)] update-active-increment: Updated with ${#ACTIVE_IDS[@]} active increment(s): ${ACTIVE_IDS[*]}" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
94
|
-
|
|
95
|
-
exit 0
|
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
# update-status-line.sh (v0.26.13 - ULTRA-OPTIMIZED for crash prevention)
|
|
4
|
-
#
|
|
5
|
-
# Updates status line cache with current increment progress.
|
|
6
|
-
# Shows: [increment-name] ████░░░░ X/Y tasks | A/B ACs (Z open)
|
|
7
|
-
#
|
|
8
|
-
# OPTIMIZATIONS (v0.26.13):
|
|
9
|
-
# 1. TTL-based throttling (10s) - longer cache = fewer runs
|
|
10
|
-
# 2. Mtime checking via find -newer (no stat loops!)
|
|
11
|
-
# 3. Pure bash counting + JSON generation (NO jq!)
|
|
12
|
-
# 4. Single-pass awk for all counting (1 process vs 5 greps)
|
|
13
|
-
# 5. Exclude _archive/ with find -not -path
|
|
14
|
-
# 6. Lock file to prevent concurrent runs
|
|
15
|
-
#
|
|
16
|
-
# Performance: <5ms (cached) / 15-25ms (full scan)
|
|
17
|
-
#
|
|
18
|
-
set +e
|
|
19
|
-
|
|
20
|
-
# ============================================================================
|
|
21
|
-
# PROJECT ROOT (FAST - cached in env if available)
|
|
22
|
-
# CRITICAL: Must NOT fallback to pwd (prevents .specweave pollution)
|
|
23
|
-
# ============================================================================
|
|
24
|
-
if [[ -n "$SPECWEAVE_PROJECT_ROOT" ]] && [[ -f "$SPECWEAVE_PROJECT_ROOT/.specweave/config.json" ]]; then
|
|
25
|
-
PROJECT_ROOT="$SPECWEAVE_PROJECT_ROOT"
|
|
26
|
-
else
|
|
27
|
-
PROJECT_ROOT="$PWD"
|
|
28
|
-
while [[ "$PROJECT_ROOT" != "/" ]] && [[ ! -f "$PROJECT_ROOT/.specweave/config.json" ]]; do
|
|
29
|
-
PROJECT_ROOT=$(dirname "$PROJECT_ROOT")
|
|
30
|
-
done
|
|
31
|
-
fi
|
|
32
|
-
|
|
33
|
-
# No .specweave/config.json? Exit immediately BEFORE any variable init (prevents pollution)
|
|
34
|
-
[[ ! -f "$PROJECT_ROOT/.specweave/config.json" ]] && exit 0
|
|
35
|
-
|
|
36
|
-
# ============================================================================
|
|
37
|
-
# ULTRA-FAST EXITS
|
|
38
|
-
# ============================================================================
|
|
39
|
-
STATE_DIR="$PROJECT_ROOT/.specweave/state"
|
|
40
|
-
CACHE_FILE="$STATE_DIR/status-line.json"
|
|
41
|
-
INCREMENTS_DIR="$PROJECT_ROOT/.specweave/increments"
|
|
42
|
-
LOCK_FILE="$STATE_DIR/.status-update.lock"
|
|
43
|
-
|
|
44
|
-
# Recursion guard
|
|
45
|
-
[[ -f "$STATE_DIR/.hook-recursion-guard" ]] && exit 0
|
|
46
|
-
|
|
47
|
-
# Lock check (prevent concurrent runs - causes crashes!)
|
|
48
|
-
if [[ -f "$LOCK_FILE" ]]; then
|
|
49
|
-
# Check if lock is stale (>30s)
|
|
50
|
-
if [[ "$(uname)" == "Darwin" ]]; then
|
|
51
|
-
LOCK_AGE=$(( $(date +%s) - $(stat -f %m "$LOCK_FILE" 2>/dev/null || echo 0) ))
|
|
52
|
-
else
|
|
53
|
-
LOCK_AGE=$(( $(date +%s) - $(stat -c %Y "$LOCK_FILE" 2>/dev/null || echo 0) ))
|
|
54
|
-
fi
|
|
55
|
-
[[ $LOCK_AGE -lt 30 ]] && exit 0
|
|
56
|
-
fi
|
|
57
|
-
|
|
58
|
-
# ============================================================================
|
|
59
|
-
# TTL CHECK (10 seconds - balanced for UX vs performance)
|
|
60
|
-
# ============================================================================
|
|
61
|
-
# TTL check (skip if --force flag provided or SPECWEAVE_FORCE_STATUS_UPDATE=1)
|
|
62
|
-
# ============================================================================
|
|
63
|
-
TTL_SECONDS=10
|
|
64
|
-
FORCE_UPDATE=0
|
|
65
|
-
|
|
66
|
-
# Check for --force flag
|
|
67
|
-
if [[ "${1:-}" == "--force" ]] || [[ "${SPECWEAVE_FORCE_STATUS_UPDATE:-0}" == "1" ]]; then
|
|
68
|
-
FORCE_UPDATE=1
|
|
69
|
-
fi
|
|
70
|
-
|
|
71
|
-
if [[ "$FORCE_UPDATE" -eq 0 ]] && [[ -f "$CACHE_FILE" ]]; then
|
|
72
|
-
if [[ "$(uname)" == "Darwin" ]]; then
|
|
73
|
-
CACHE_AGE=$(( $(date +%s) - $(stat -f %m "$CACHE_FILE" 2>/dev/null || echo 0) ))
|
|
74
|
-
else
|
|
75
|
-
CACHE_AGE=$(( $(date +%s) - $(stat -c %Y "$CACHE_FILE" 2>/dev/null || echo 0) ))
|
|
76
|
-
fi
|
|
77
|
-
[[ $CACHE_AGE -lt $TTL_SECONDS ]] && exit 0
|
|
78
|
-
fi
|
|
79
|
-
|
|
80
|
-
# ============================================================================
|
|
81
|
-
# NO INCREMENTS? Write empty cache and exit
|
|
82
|
-
# ============================================================================
|
|
83
|
-
if [[ ! -d "$INCREMENTS_DIR" ]]; then
|
|
84
|
-
mkdir -p "$STATE_DIR"
|
|
85
|
-
printf '{"current":null,"openCount":0,"lastUpdate":"%s"}' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$CACHE_FILE"
|
|
86
|
-
exit 0
|
|
87
|
-
fi
|
|
88
|
-
|
|
89
|
-
# ============================================================================
|
|
90
|
-
# ACQUIRE LOCK (ATOMIC - mkdir pattern prevents TOCTOU race!)
|
|
91
|
-
# ============================================================================
|
|
92
|
-
mkdir -p "$STATE_DIR"
|
|
93
|
-
LOCK_DIR="$STATE_DIR/.status-update.lockdir"
|
|
94
|
-
if ! mkdir "$LOCK_DIR" 2>/dev/null; then
|
|
95
|
-
# Lock exists - check if stale (>30s)
|
|
96
|
-
if [[ "$(uname)" == "Darwin" ]]; then
|
|
97
|
-
LOCK_AGE=$(( $(date +%s) - $(stat -f %m "$LOCK_DIR" 2>/dev/null || echo 0) ))
|
|
98
|
-
else
|
|
99
|
-
LOCK_AGE=$(( $(date +%s) - $(stat -c %Y "$LOCK_DIR" 2>/dev/null || echo 0) ))
|
|
100
|
-
fi
|
|
101
|
-
if [[ $LOCK_AGE -lt 30 ]]; then
|
|
102
|
-
exit 0 # Another process holds lock
|
|
103
|
-
fi
|
|
104
|
-
# Stale lock - remove and retry
|
|
105
|
-
rm -rf "$LOCK_DIR"
|
|
106
|
-
mkdir "$LOCK_DIR" 2>/dev/null || exit 0
|
|
107
|
-
fi
|
|
108
|
-
echo $$ > "$LOCK_DIR/pid"
|
|
109
|
-
trap 'rm -rf "$LOCK_DIR"' EXIT
|
|
110
|
-
|
|
111
|
-
# ============================================================================
|
|
112
|
-
# FIND ACTIVE INCREMENTS (uses metadata.json - SOURCE OF TRUTH!)
|
|
113
|
-
# ============================================================================
|
|
114
|
-
ACTIVE_FILES=""
|
|
115
|
-
OPEN_COUNT=0
|
|
116
|
-
OLDEST_DATE="9999-99-99"
|
|
117
|
-
CURRENT_INCREMENT=""
|
|
118
|
-
|
|
119
|
-
while IFS= read -r metadata_file; do
|
|
120
|
-
[[ -z "$metadata_file" ]] && continue
|
|
121
|
-
|
|
122
|
-
# Extract status from metadata.json (jq if available, grep fallback)
|
|
123
|
-
if command -v jq >/dev/null 2>&1; then
|
|
124
|
-
status=$(jq -r '.status // "unknown"' "$metadata_file" 2>/dev/null || echo "unknown")
|
|
125
|
-
created=$(jq -r '.created // "9999-99-99"' "$metadata_file" 2>/dev/null || echo "9999-99-99")
|
|
126
|
-
else
|
|
127
|
-
status=$(sed -n 's/.*"status"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$metadata_file" 2>/dev/null || echo "unknown")
|
|
128
|
-
created=$(sed -n 's/.*"created"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$metadata_file" 2>/dev/null || echo "9999-99-99")
|
|
129
|
-
fi
|
|
130
|
-
|
|
131
|
-
# Check if active
|
|
132
|
-
if [[ "$status" == "active" || "$status" == "planning" || "$status" == "backlog" || "$status" == "ready_for_review" ]]; then
|
|
133
|
-
OPEN_COUNT=$((OPEN_COUNT + 1))
|
|
134
|
-
increment_id=$(basename "$(dirname "$metadata_file")")
|
|
135
|
-
|
|
136
|
-
if [[ "$created" < "$OLDEST_DATE" ]]; then
|
|
137
|
-
OLDEST_DATE="$created"
|
|
138
|
-
CURRENT_INCREMENT="$increment_id"
|
|
139
|
-
fi
|
|
140
|
-
|
|
141
|
-
spec_file="$(dirname "$metadata_file")/spec.md"
|
|
142
|
-
ACTIVE_FILES="$ACTIVE_FILES $spec_file"
|
|
143
|
-
fi
|
|
144
|
-
done < <(find "$INCREMENTS_DIR" -maxdepth 2 -name "metadata.json" -not -path "*/_archive/*" 2>/dev/null)
|
|
145
|
-
|
|
146
|
-
# No active increments?
|
|
147
|
-
if [[ -z "$CURRENT_INCREMENT" ]]; then
|
|
148
|
-
printf '{"current":null,"openCount":0,"lastUpdate":"%s"}' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$CACHE_FILE"
|
|
149
|
-
exit 0
|
|
150
|
-
fi
|
|
151
|
-
|
|
152
|
-
# ============================================================================
|
|
153
|
-
# MTIME CHECK (using find -newer - single syscall!)
|
|
154
|
-
# ============================================================================
|
|
155
|
-
MTIME_FILE="$STATE_DIR/.status-mtime-$CURRENT_INCREMENT"
|
|
156
|
-
|
|
157
|
-
if [[ -f "$MTIME_FILE" ]] && [[ -f "$CACHE_FILE" ]]; then
|
|
158
|
-
# Check if any relevant files are newer than our marker
|
|
159
|
-
TASKS_FILE="$INCREMENTS_DIR/$CURRENT_INCREMENT/tasks.md"
|
|
160
|
-
SPEC_FILE="$INCREMENTS_DIR/$CURRENT_INCREMENT/spec.md"
|
|
161
|
-
|
|
162
|
-
NEWER_FILES=$(find "$SPEC_FILE" "$TASKS_FILE" -newer "$MTIME_FILE" 2>/dev/null | head -1)
|
|
163
|
-
|
|
164
|
-
if [[ -z "$NEWER_FILES" ]]; then
|
|
165
|
-
# No changes - just touch cache to reset TTL
|
|
166
|
-
touch "$CACHE_FILE"
|
|
167
|
-
exit 0
|
|
168
|
-
fi
|
|
169
|
-
fi
|
|
170
|
-
|
|
171
|
-
# ============================================================================
|
|
172
|
-
# SINGLE-PASS COUNTING WITH AWK (replaces 5 grep calls!)
|
|
173
|
-
# ============================================================================
|
|
174
|
-
TASKS_FILE="$INCREMENTS_DIR/$CURRENT_INCREMENT/tasks.md"
|
|
175
|
-
SPEC_FILE="$INCREMENTS_DIR/$CURRENT_INCREMENT/spec.md"
|
|
176
|
-
|
|
177
|
-
# Count tasks with single awk call (per-task completion tracking!)
|
|
178
|
-
# Bug fix: Each task counts as 0 or 1 completed, regardless of marker count
|
|
179
|
-
read -r TOTAL_TASKS COMPLETED_TASKS < <(
|
|
180
|
-
awk '
|
|
181
|
-
BEGIN { total=0; completed=0; in_task=0; task_complete=0 }
|
|
182
|
-
/^###? T-/ {
|
|
183
|
-
if (in_task && task_complete) completed++
|
|
184
|
-
total++
|
|
185
|
-
in_task=1
|
|
186
|
-
task_complete=0
|
|
187
|
-
}
|
|
188
|
-
/\*\*Completed\*\*:|\*\*Status\*\*:[ \t]*\[x\]|^\[x\]/ {
|
|
189
|
-
if (in_task) task_complete=1
|
|
190
|
-
}
|
|
191
|
-
END {
|
|
192
|
-
if (in_task && task_complete) completed++
|
|
193
|
-
print total, completed
|
|
194
|
-
}
|
|
195
|
-
' "$TASKS_FILE" 2>/dev/null || echo "0 0"
|
|
196
|
-
)
|
|
197
|
-
|
|
198
|
-
# Count ACs with single awk call
|
|
199
|
-
# Supports both formats: "- [ ] AC-US1-01:" and "- [ ] **AC-US1-01**:"
|
|
200
|
-
read -r TOTAL_ACS COMPLETED_ACS < <(
|
|
201
|
-
awk '
|
|
202
|
-
/^- \[(x| )\] (\*\*)?AC-/ { total++ }
|
|
203
|
-
/^- \[x\] (\*\*)?AC-/ { completed++ }
|
|
204
|
-
END { print total+0, completed+0 }
|
|
205
|
-
' "$SPEC_FILE" 2>/dev/null || echo "0 0"
|
|
206
|
-
)
|
|
207
|
-
|
|
208
|
-
# Calculate percentage (pure bash)
|
|
209
|
-
PERCENTAGE=0
|
|
210
|
-
[[ ${TOTAL_TASKS:-0} -gt 0 ]] && PERCENTAGE=$((${COMPLETED_TASKS:-0} * 100 / TOTAL_TASKS))
|
|
211
|
-
|
|
212
|
-
# ============================================================================
|
|
213
|
-
# WRITE CACHE (PURE BASH - NO jq!)
|
|
214
|
-
# ============================================================================
|
|
215
|
-
# Sanitize values
|
|
216
|
-
TOTAL_TASKS=${TOTAL_TASKS:-0}
|
|
217
|
-
COMPLETED_TASKS=${COMPLETED_TASKS:-0}
|
|
218
|
-
TOTAL_ACS=${TOTAL_ACS:-0}
|
|
219
|
-
COMPLETED_ACS=${COMPLETED_ACS:-0}
|
|
220
|
-
OPEN_COUNT=${OPEN_COUNT:-0}
|
|
221
|
-
PERCENTAGE=${PERCENTAGE:-0}
|
|
222
|
-
|
|
223
|
-
# Generate JSON directly (ATOMIC: write temp then mv to prevent partial reads!)
|
|
224
|
-
TEMP_CACHE="$CACHE_FILE.tmp.$$"
|
|
225
|
-
cat > "$TEMP_CACHE" << EOF
|
|
226
|
-
{"current":{"id":"$CURRENT_INCREMENT","name":"$CURRENT_INCREMENT","completed":$COMPLETED_TASKS,"total":$TOTAL_TASKS,"percentage":$PERCENTAGE,"acsCompleted":$COMPLETED_ACS,"acsTotal":$TOTAL_ACS},"openCount":$OPEN_COUNT,"lastUpdate":"$(date -u +%Y-%m-%dT%H:%M:%SZ)"}
|
|
227
|
-
EOF
|
|
228
|
-
mv "$TEMP_CACHE" "$CACHE_FILE"
|
|
229
|
-
|
|
230
|
-
# Update mtime marker
|
|
231
|
-
touch "$MTIME_FILE"
|
|
232
|
-
|
|
233
|
-
exit 0
|
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
#
|
|
3
|
-
# validate-spec-status.sh
|
|
4
|
-
#
|
|
5
|
-
# Validates that spec.md status values match IncrementStatus enum.
|
|
6
|
-
# Prevents vocabulary drift and ensures status line hook compatibility.
|
|
7
|
-
#
|
|
8
|
-
# Usage:
|
|
9
|
-
# bash validate-spec-status.sh <increment-id>
|
|
10
|
-
# bash validate-spec-status.sh --all # Validate all increments
|
|
11
|
-
#
|
|
12
|
-
# Exit codes:
|
|
13
|
-
# 0 = Valid
|
|
14
|
-
# 1 = Invalid status found
|
|
15
|
-
# 2 = Error (file not found, etc.)
|
|
16
|
-
#
|
|
17
|
-
# Requires: bash 4.0+ (for associative arrays)
|
|
18
|
-
|
|
19
|
-
set +e # EMERGENCY FIX: Changed from set -euo pipefail to prevent Claude Code crashes
|
|
20
|
-
|
|
21
|
-
# Find project root (CRITICAL: must NOT fallback to pwd!)
|
|
22
|
-
find_project_root() {
|
|
23
|
-
local dir="$PWD"
|
|
24
|
-
while [[ "$dir" != "/" ]]; do
|
|
25
|
-
if [[ -f "$dir/.specweave/config.json" ]]; then
|
|
26
|
-
echo "$dir"
|
|
27
|
-
return 0
|
|
28
|
-
fi
|
|
29
|
-
dir=$(dirname "$dir")
|
|
30
|
-
done
|
|
31
|
-
# Return empty - NOT pwd (prevents .specweave pollution)
|
|
32
|
-
return 1
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
PROJECT_ROOT=$(find_project_root)
|
|
36
|
-
|
|
37
|
-
# Exit early if not a SpecWeave project (prevents .specweave pollution)
|
|
38
|
-
if [[ -z "$PROJECT_ROOT" ]] || [[ ! -f "$PROJECT_ROOT/.specweave/config.json" ]]; then
|
|
39
|
-
echo "Not a SpecWeave project - skipping validation"
|
|
40
|
-
exit 0
|
|
41
|
-
fi
|
|
42
|
-
|
|
43
|
-
INCREMENTS_DIR="$PROJECT_ROOT/.specweave/increments"
|
|
44
|
-
|
|
45
|
-
# Valid IncrementStatus enum values (from src/core/types/increment-metadata.ts)
|
|
46
|
-
VALID_STATUSES=("planning" "active" "backlog" "paused" "completed" "abandoned")
|
|
47
|
-
|
|
48
|
-
# Get suggested correction for invalid status
|
|
49
|
-
get_correction() {
|
|
50
|
-
local status="$1"
|
|
51
|
-
case "$status" in
|
|
52
|
-
"planned") echo "planning" ;;
|
|
53
|
-
"in-progress"|"in_progress") echo "active" ;;
|
|
54
|
-
"done") echo "completed" ;;
|
|
55
|
-
"cancelled"|"canceled") echo "abandoned" ;;
|
|
56
|
-
*) echo "" ;;
|
|
57
|
-
esac
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
# Validate a single spec file
|
|
61
|
-
validate_spec() {
|
|
62
|
-
local spec_file="$1"
|
|
63
|
-
local increment_id=$(basename "$(dirname "$spec_file")")
|
|
64
|
-
|
|
65
|
-
if [[ ! -f "$spec_file" ]]; then
|
|
66
|
-
echo "❌ Error: spec.md not found for increment $increment_id"
|
|
67
|
-
return 2
|
|
68
|
-
fi
|
|
69
|
-
|
|
70
|
-
# Extract status from YAML frontmatter
|
|
71
|
-
local status=$(grep -m1 "^status:" "$spec_file" 2>/dev/null | cut -d: -f2 | tr -d ' "' || echo "")
|
|
72
|
-
|
|
73
|
-
if [[ -z "$status" ]]; then
|
|
74
|
-
echo "⚠️ Warning: No status field in $increment_id/spec.md"
|
|
75
|
-
return 0 # Not an error, just a warning
|
|
76
|
-
fi
|
|
77
|
-
|
|
78
|
-
# Check if status is valid
|
|
79
|
-
local is_valid=0
|
|
80
|
-
for valid_status in "${VALID_STATUSES[@]}"; do
|
|
81
|
-
if [[ "$status" == "$valid_status" ]]; then
|
|
82
|
-
is_valid=1
|
|
83
|
-
break
|
|
84
|
-
fi
|
|
85
|
-
done
|
|
86
|
-
|
|
87
|
-
if [[ $is_valid -eq 1 ]]; then
|
|
88
|
-
echo "✅ $increment_id: status '$status' is valid"
|
|
89
|
-
return 0
|
|
90
|
-
else
|
|
91
|
-
# Invalid status found - suggest correction
|
|
92
|
-
echo ""
|
|
93
|
-
echo "❌ Invalid status in $increment_id/spec.md"
|
|
94
|
-
echo " Found: '$status'"
|
|
95
|
-
echo ""
|
|
96
|
-
|
|
97
|
-
# Check if we have a suggested correction
|
|
98
|
-
local correction=$(get_correction "$status")
|
|
99
|
-
if [[ -n "$correction" ]]; then
|
|
100
|
-
echo " 💡 Did you mean: '$correction'?"
|
|
101
|
-
echo ""
|
|
102
|
-
echo " Fix:"
|
|
103
|
-
echo " sed -i '' 's/^status: $status/status: $correction/' $spec_file"
|
|
104
|
-
else
|
|
105
|
-
echo " Valid statuses: ${VALID_STATUSES[*]}"
|
|
106
|
-
fi
|
|
107
|
-
echo ""
|
|
108
|
-
|
|
109
|
-
return 1
|
|
110
|
-
fi
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
# Validate all increments
|
|
114
|
-
validate_all() {
|
|
115
|
-
local exit_code=0
|
|
116
|
-
local total=0
|
|
117
|
-
local valid=0
|
|
118
|
-
local invalid=0
|
|
119
|
-
|
|
120
|
-
echo "Validating all increments in $INCREMENTS_DIR"
|
|
121
|
-
echo ""
|
|
122
|
-
|
|
123
|
-
for spec_file in "$INCREMENTS_DIR"/*/spec.md; do
|
|
124
|
-
if [[ -f "$spec_file" ]]; then
|
|
125
|
-
total=$((total + 1))
|
|
126
|
-
if validate_spec "$spec_file"; then
|
|
127
|
-
valid=$((valid + 1))
|
|
128
|
-
else
|
|
129
|
-
invalid=$((invalid + 1))
|
|
130
|
-
exit_code=1
|
|
131
|
-
fi
|
|
132
|
-
fi
|
|
133
|
-
done
|
|
134
|
-
|
|
135
|
-
echo ""
|
|
136
|
-
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
137
|
-
echo "Summary: $total increments checked"
|
|
138
|
-
echo " ✅ Valid: $valid"
|
|
139
|
-
echo " ❌ Invalid: $invalid"
|
|
140
|
-
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
141
|
-
|
|
142
|
-
if [[ $invalid -gt 0 ]]; then
|
|
143
|
-
echo ""
|
|
144
|
-
echo "⚠️ Please fix invalid status values to use official IncrementStatus enum."
|
|
145
|
-
echo ""
|
|
146
|
-
echo "Valid statuses:"
|
|
147
|
-
for status in "${VALID_STATUSES[@]}"; do
|
|
148
|
-
echo " - $status"
|
|
149
|
-
done
|
|
150
|
-
fi
|
|
151
|
-
|
|
152
|
-
return $exit_code
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
# Main logic
|
|
156
|
-
if [[ $# -eq 0 ]]; then
|
|
157
|
-
echo "Usage: $0 <increment-id> | --all"
|
|
158
|
-
echo ""
|
|
159
|
-
echo "Examples:"
|
|
160
|
-
echo " $0 0042-test-infrastructure-cleanup"
|
|
161
|
-
echo " $0 --all"
|
|
162
|
-
exit 2
|
|
163
|
-
fi
|
|
164
|
-
|
|
165
|
-
if [[ "$1" == "--all" ]]; then
|
|
166
|
-
validate_all
|
|
167
|
-
else
|
|
168
|
-
INCREMENT_ID="$1"
|
|
169
|
-
SPEC_FILE="$INCREMENTS_DIR/$INCREMENT_ID/spec.md"
|
|
170
|
-
validate_spec "$SPEC_FILE"
|
|
171
|
-
fi
|