@lvlup-sw/exarchos 2.0.1
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 +22 -0
- package/.claude-plugin/plugin.json +17 -0
- package/.mcp.json +17 -0
- package/AGENTS.md +59 -0
- package/CLAUDE.md.template +62 -0
- package/LICENSE +202 -0
- package/README.md +258 -0
- package/commands/autocompact.md +37 -0
- package/commands/checkpoint.md +85 -0
- package/commands/cleanup.md +99 -0
- package/commands/debug.md +145 -0
- package/commands/delegate.md +56 -0
- package/commands/ideate.md +82 -0
- package/commands/plan.md +150 -0
- package/commands/refactor.md +139 -0
- package/commands/reload.md +37 -0
- package/commands/resume.md +130 -0
- package/commands/review.md +51 -0
- package/commands/sync-schemas.md +74 -0
- package/commands/synthesize.md +122 -0
- package/commands/tdd.md +58 -0
- package/dist/exarchos-cli.js +8828 -0
- package/dist/exarchos-mcp.js +50 -0
- package/hooks/hooks.json +53 -0
- package/package.json +59 -0
- package/rules/coding-standards.md +46 -0
- package/rules/mcp-tool-guidance.md +26 -0
- package/rules/pr-descriptions.md +12 -0
- package/rules/rm-safety.md +9 -0
- package/rules/skill-path-resolution.md +10 -0
- package/rules/tdd.md +41 -0
- package/rules/telemetry-awareness.md +9 -0
- package/scripts/assess-refactor-scope.sh +239 -0
- package/scripts/check-benchmark-regression.sh +229 -0
- package/scripts/check-coderabbit.sh +288 -0
- package/scripts/check-coverage-thresholds.sh +194 -0
- package/scripts/check-polish-scope.sh +245 -0
- package/scripts/check-property-tests.sh +167 -0
- package/scripts/check-tdd-compliance.sh +265 -0
- package/scripts/coderabbit-review-gate.sh +518 -0
- package/scripts/debug-review-gate.sh +201 -0
- package/scripts/extract-fix-tasks.sh +179 -0
- package/scripts/extract-task.sh +67 -0
- package/scripts/generate-traceability.sh +209 -0
- package/scripts/investigation-timer.sh +171 -0
- package/scripts/needs-schema-sync.sh +174 -0
- package/scripts/new-project.sh +103 -0
- package/scripts/post-delegation-check.sh +317 -0
- package/scripts/pre-synthesis-check.sh +440 -0
- package/scripts/reconcile-state.sh +346 -0
- package/scripts/reconstruct-stack.sh +432 -0
- package/scripts/review-diff.sh +63 -0
- package/scripts/review-verdict.sh +169 -0
- package/scripts/security-scan.sh +248 -0
- package/scripts/select-debug-track.sh +186 -0
- package/scripts/setup-worktree.sh +323 -0
- package/scripts/spec-coverage-check.sh +230 -0
- package/scripts/static-analysis-gate.sh +236 -0
- package/scripts/sync-labels.sh +122 -0
- package/scripts/validate-companion.sh +161 -0
- package/scripts/validate-dotnet-standards.sh +267 -0
- package/scripts/validate-installation.sh +101 -0
- package/scripts/validate-plugin.sh +223 -0
- package/scripts/validate-refactor.sh +234 -0
- package/scripts/validate-rm.sh +93 -0
- package/scripts/verify-delegation-saga.sh +240 -0
- package/scripts/verify-doc-links.sh +211 -0
- package/scripts/verify-ideate-artifacts.sh +296 -0
- package/scripts/verify-plan-coverage.sh +228 -0
- package/scripts/verify-review-triage.sh +219 -0
- package/scripts/verify-worktree-baseline.sh +159 -0
- package/scripts/verify-worktree.sh +84 -0
- package/settings.json +47 -0
- package/skills/brainstorming/SKILL.md +127 -0
- package/skills/brainstorming/references/design-template.md +65 -0
- package/skills/cleanup/SKILL.md +147 -0
- package/skills/cleanup/references/merge-verification.md +40 -0
- package/skills/debug/SKILL.md +204 -0
- package/skills/debug/references/hotfix-track.md +134 -0
- package/skills/debug/references/investigation-checklist.md +217 -0
- package/skills/debug/references/rca-template.md +150 -0
- package/skills/debug/references/state-schema.md +294 -0
- package/skills/debug/references/thorough-track.md +194 -0
- package/skills/debug/references/triage-questions.md +155 -0
- package/skills/debug/references/troubleshooting.md +47 -0
- package/skills/delegation/SKILL.md +150 -0
- package/skills/delegation/references/adaptive-orchestration.md +31 -0
- package/skills/delegation/references/agent-teams-saga.md +248 -0
- package/skills/delegation/references/fix-mode.md +74 -0
- package/skills/delegation/references/fixer-prompt.md +162 -0
- package/skills/delegation/references/implementer-prompt.md +322 -0
- package/skills/delegation/references/parallel-strategy.md +124 -0
- package/skills/delegation/references/pbt-patterns.md +172 -0
- package/skills/delegation/references/pr-fixes-mode.md +154 -0
- package/skills/delegation/references/state-management.md +51 -0
- package/skills/delegation/references/testing-patterns.md +129 -0
- package/skills/delegation/references/troubleshooting.md +33 -0
- package/skills/delegation/references/workflow-steps.md +127 -0
- package/skills/delegation/references/worktree-enforcement.md +64 -0
- package/skills/dotnet-standards/SKILL.md +269 -0
- package/skills/dotnet-standards/references/csharp-standards.md +120 -0
- package/skills/dotnet-standards/templates/.editorconfig +366 -0
- package/skills/dotnet-standards/templates/Directory.Build.props +56 -0
- package/skills/dotnet-standards/templates/Directory.Packages.props +69 -0
- package/skills/dotnet-standards/templates/global.json +6 -0
- package/skills/dotnet-standards/templates/nuget.config +9 -0
- package/skills/dotnet-standards/templates/stylecop.json +37 -0
- package/skills/git-worktrees/SKILL.md +255 -0
- package/skills/implementation-planning/SKILL.md +233 -0
- package/skills/implementation-planning/references/plan-document-template.md +42 -0
- package/skills/implementation-planning/references/spec-tracing-guide.md +51 -0
- package/skills/implementation-planning/references/task-template.md +43 -0
- package/skills/implementation-planning/references/testing-strategy-guide.md +88 -0
- package/skills/quality-review/SKILL.md +278 -0
- package/skills/quality-review/references/code-quality-checklist.md +159 -0
- package/skills/quality-review/references/review-report-template.md +65 -0
- package/skills/quality-review/references/security-checklist.md +79 -0
- package/skills/quality-review/references/typescript-standards.md +24 -0
- package/skills/refactor/COMMAND.md +67 -0
- package/skills/refactor/SKILL.md +198 -0
- package/skills/refactor/phases/auto-chain.md +262 -0
- package/skills/refactor/phases/brief.md +176 -0
- package/skills/refactor/phases/explore.md +132 -0
- package/skills/refactor/phases/overhaul-delegate.md +136 -0
- package/skills/refactor/phases/overhaul-plan.md +312 -0
- package/skills/refactor/phases/overhaul-review.md +304 -0
- package/skills/refactor/phases/polish-implement.md +349 -0
- package/skills/refactor/phases/polish-validate.md +218 -0
- package/skills/refactor/phases/update-docs.md +234 -0
- package/skills/refactor/references/brief-template.md +81 -0
- package/skills/refactor/references/doc-update-checklist.md +110 -0
- package/skills/refactor/references/explore-checklist.md +73 -0
- package/skills/refactor/references/overhaul-track.md +215 -0
- package/skills/refactor/references/polish-track.md +170 -0
- package/skills/shared/prompts/context-reading.md +58 -0
- package/skills/shared/prompts/report-format.md +54 -0
- package/skills/shared/prompts/tdd-requirements.md +39 -0
- package/skills/shepherd/SKILL.md +264 -0
- package/skills/shepherd/references/assess-checklist.md +124 -0
- package/skills/shepherd/references/fix-strategies.md +191 -0
- package/skills/spec-review/SKILL.md +229 -0
- package/skills/spec-review/references/review-checklist.md +60 -0
- package/skills/sync-schemas/SKILL.md +114 -0
- package/skills/sync-schemas/references/configuration.md +73 -0
- package/skills/synthesis/SKILL.md +129 -0
- package/skills/synthesis/references/pr-descriptions.md +87 -0
- package/skills/synthesis/references/synthesis-steps.md +109 -0
- package/skills/synthesis/references/troubleshooting.md +115 -0
- package/skills/validate-all-skills.sh +57 -0
- package/skills/validate-frontmatter.sh +237 -0
- package/skills/workflow-state/SKILL.md +210 -0
- package/skills/workflow-state/references/mcp-tool-reference.md +111 -0
- package/skills/workflow-state/references/phase-transitions.md +141 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# setup-worktree.sh — Atomic worktree creation with validation
|
|
3
|
+
# Replaces the "Pre-Dispatch Checklist" prose in delegation SKILL.md.
|
|
4
|
+
#
|
|
5
|
+
# Usage: setup-worktree.sh --repo-root <path> --task-id <id> --task-name <name> [--base-branch main] [--skip-tests]
|
|
6
|
+
#
|
|
7
|
+
# Exit codes:
|
|
8
|
+
# 0 = worktree ready
|
|
9
|
+
# 1 = setup failed
|
|
10
|
+
# 2 = usage error (missing required args)
|
|
11
|
+
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
|
|
14
|
+
# ============================================================
|
|
15
|
+
# ARGUMENT PARSING
|
|
16
|
+
# ============================================================
|
|
17
|
+
|
|
18
|
+
REPO_ROOT=""
|
|
19
|
+
TASK_ID=""
|
|
20
|
+
TASK_NAME=""
|
|
21
|
+
BASE_BRANCH="main"
|
|
22
|
+
SKIP_TESTS=false
|
|
23
|
+
|
|
24
|
+
usage() {
|
|
25
|
+
cat << 'USAGE'
|
|
26
|
+
Usage: setup-worktree.sh --repo-root <path> --task-id <id> --task-name <name> [--base-branch main] [--skip-tests]
|
|
27
|
+
|
|
28
|
+
Required:
|
|
29
|
+
--repo-root <path> Repository root directory
|
|
30
|
+
--task-id <id> Task identifier (e.g., task-001)
|
|
31
|
+
--task-name <name> Task name slug (e.g., user-model)
|
|
32
|
+
|
|
33
|
+
Optional:
|
|
34
|
+
--base-branch <name> Base branch to create from (default: main)
|
|
35
|
+
--skip-tests Skip baseline test verification
|
|
36
|
+
--help Show this help message
|
|
37
|
+
|
|
38
|
+
Exit codes:
|
|
39
|
+
0 Worktree ready
|
|
40
|
+
1 Setup failed
|
|
41
|
+
2 Usage error (missing required args)
|
|
42
|
+
USAGE
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
while [[ $# -gt 0 ]]; do
|
|
46
|
+
case "$1" in
|
|
47
|
+
--repo-root)
|
|
48
|
+
if [[ -z "${2:-}" ]]; then
|
|
49
|
+
echo "Error: --repo-root requires a path argument" >&2
|
|
50
|
+
exit 2
|
|
51
|
+
fi
|
|
52
|
+
REPO_ROOT="$2"
|
|
53
|
+
shift 2
|
|
54
|
+
;;
|
|
55
|
+
--task-id)
|
|
56
|
+
if [[ -z "${2:-}" ]]; then
|
|
57
|
+
echo "Error: --task-id requires an argument" >&2
|
|
58
|
+
exit 2
|
|
59
|
+
fi
|
|
60
|
+
TASK_ID="$2"
|
|
61
|
+
shift 2
|
|
62
|
+
;;
|
|
63
|
+
--task-name)
|
|
64
|
+
if [[ -z "${2:-}" ]]; then
|
|
65
|
+
echo "Error: --task-name requires an argument" >&2
|
|
66
|
+
exit 2
|
|
67
|
+
fi
|
|
68
|
+
TASK_NAME="$2"
|
|
69
|
+
shift 2
|
|
70
|
+
;;
|
|
71
|
+
--base-branch)
|
|
72
|
+
if [[ -z "${2:-}" ]]; then
|
|
73
|
+
echo "Error: --base-branch requires an argument" >&2
|
|
74
|
+
exit 2
|
|
75
|
+
fi
|
|
76
|
+
BASE_BRANCH="$2"
|
|
77
|
+
shift 2
|
|
78
|
+
;;
|
|
79
|
+
--skip-tests)
|
|
80
|
+
SKIP_TESTS=true
|
|
81
|
+
shift
|
|
82
|
+
;;
|
|
83
|
+
--help)
|
|
84
|
+
usage
|
|
85
|
+
exit 0
|
|
86
|
+
;;
|
|
87
|
+
*)
|
|
88
|
+
echo "Error: Unknown argument '$1'" >&2
|
|
89
|
+
usage >&2
|
|
90
|
+
exit 2
|
|
91
|
+
;;
|
|
92
|
+
esac
|
|
93
|
+
done
|
|
94
|
+
|
|
95
|
+
if [[ -z "$REPO_ROOT" ]]; then
|
|
96
|
+
echo "Error: --repo-root is required" >&2
|
|
97
|
+
usage >&2
|
|
98
|
+
exit 2
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
if [[ -z "$TASK_ID" ]]; then
|
|
102
|
+
echo "Error: --task-id is required" >&2
|
|
103
|
+
usage >&2
|
|
104
|
+
exit 2
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
if [[ -z "$TASK_NAME" ]]; then
|
|
108
|
+
echo "Error: --task-name is required" >&2
|
|
109
|
+
usage >&2
|
|
110
|
+
exit 2
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
# ============================================================
|
|
114
|
+
# DERIVED VALUES
|
|
115
|
+
# ============================================================
|
|
116
|
+
|
|
117
|
+
WORKTREE_NAME="${TASK_ID}-${TASK_NAME}"
|
|
118
|
+
BRANCH_NAME="feature/${WORKTREE_NAME}"
|
|
119
|
+
WORKTREE_PATH="$REPO_ROOT/.worktrees/$WORKTREE_NAME"
|
|
120
|
+
|
|
121
|
+
# ============================================================
|
|
122
|
+
# CHECK FUNCTIONS
|
|
123
|
+
# ============================================================
|
|
124
|
+
|
|
125
|
+
CHECK_PASS=0
|
|
126
|
+
CHECK_FAIL=0
|
|
127
|
+
RESULTS=()
|
|
128
|
+
|
|
129
|
+
check_pass() {
|
|
130
|
+
local name="$1"
|
|
131
|
+
RESULTS+=("- **PASS**: $name")
|
|
132
|
+
CHECK_PASS=$((CHECK_PASS + 1))
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
check_fail() {
|
|
136
|
+
local name="$1"
|
|
137
|
+
local detail="${2:-}"
|
|
138
|
+
if [[ -n "$detail" ]]; then
|
|
139
|
+
RESULTS+=("- **FAIL**: $name — $detail")
|
|
140
|
+
else
|
|
141
|
+
RESULTS+=("- **FAIL**: $name")
|
|
142
|
+
fi
|
|
143
|
+
CHECK_FAIL=$((CHECK_FAIL + 1))
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
check_skip() {
|
|
147
|
+
local name="$1"
|
|
148
|
+
RESULTS+=("- **SKIP**: $name")
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
# ============================================================
|
|
152
|
+
# STEP 1: Ensure .worktrees is gitignored
|
|
153
|
+
# ============================================================
|
|
154
|
+
|
|
155
|
+
ensure_gitignored() {
|
|
156
|
+
# git check-ignore requires trailing slash for directory patterns
|
|
157
|
+
if (cd "$REPO_ROOT" && git check-ignore -q .worktrees/) 2>/dev/null; then
|
|
158
|
+
check_pass ".worktrees is gitignored"
|
|
159
|
+
return 0
|
|
160
|
+
fi
|
|
161
|
+
|
|
162
|
+
# Add to .gitignore
|
|
163
|
+
local gitignore="$REPO_ROOT/.gitignore"
|
|
164
|
+
if [[ -f "$gitignore" ]]; then
|
|
165
|
+
echo ".worktrees/" >> "$gitignore"
|
|
166
|
+
else
|
|
167
|
+
echo ".worktrees/" > "$gitignore"
|
|
168
|
+
fi
|
|
169
|
+
|
|
170
|
+
# Verify it worked
|
|
171
|
+
if (cd "$REPO_ROOT" && git check-ignore -q .worktrees/) 2>/dev/null; then
|
|
172
|
+
check_pass ".worktrees is gitignored (added to .gitignore)"
|
|
173
|
+
return 0
|
|
174
|
+
else
|
|
175
|
+
check_fail ".worktrees is gitignored" "Failed to add to .gitignore"
|
|
176
|
+
return 1
|
|
177
|
+
fi
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
# ============================================================
|
|
181
|
+
# STEP 2: Create feature branch
|
|
182
|
+
# ============================================================
|
|
183
|
+
|
|
184
|
+
create_branch() {
|
|
185
|
+
# Check if branch already exists
|
|
186
|
+
if git -C "$REPO_ROOT" show-ref --verify --quiet "refs/heads/$BRANCH_NAME" 2>/dev/null; then
|
|
187
|
+
check_pass "Branch created ($BRANCH_NAME already exists)"
|
|
188
|
+
return 0
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
if git -C "$REPO_ROOT" branch "$BRANCH_NAME" "$BASE_BRANCH" 2>/dev/null; then
|
|
192
|
+
check_pass "Branch created ($BRANCH_NAME from $BASE_BRANCH)"
|
|
193
|
+
return 0
|
|
194
|
+
else
|
|
195
|
+
check_fail "Branch created" "Failed to create $BRANCH_NAME from $BASE_BRANCH"
|
|
196
|
+
return 1
|
|
197
|
+
fi
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
# ============================================================
|
|
201
|
+
# STEP 3: Create worktree
|
|
202
|
+
# ============================================================
|
|
203
|
+
|
|
204
|
+
create_worktree() {
|
|
205
|
+
# Check if worktree already exists
|
|
206
|
+
if [[ -d "$WORKTREE_PATH" ]]; then
|
|
207
|
+
# Verify it's a valid worktree
|
|
208
|
+
if git -C "$WORKTREE_PATH" rev-parse --git-dir &>/dev/null; then
|
|
209
|
+
check_pass "Worktree created ($WORKTREE_PATH already exists)"
|
|
210
|
+
return 0
|
|
211
|
+
else
|
|
212
|
+
check_fail "Worktree created" "$WORKTREE_PATH exists but is not a valid worktree"
|
|
213
|
+
return 1
|
|
214
|
+
fi
|
|
215
|
+
fi
|
|
216
|
+
|
|
217
|
+
if git -C "$REPO_ROOT" worktree add "$WORKTREE_PATH" "$BRANCH_NAME" 2>/dev/null; then
|
|
218
|
+
check_pass "Worktree created ($WORKTREE_PATH)"
|
|
219
|
+
return 0
|
|
220
|
+
else
|
|
221
|
+
check_fail "Worktree created" "git worktree add failed for $WORKTREE_PATH"
|
|
222
|
+
return 1
|
|
223
|
+
fi
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
# ============================================================
|
|
227
|
+
# STEP 4: Run npm install
|
|
228
|
+
# ============================================================
|
|
229
|
+
|
|
230
|
+
run_npm_install() {
|
|
231
|
+
# Only run if package.json exists in worktree
|
|
232
|
+
if [[ ! -f "$WORKTREE_PATH/package.json" ]]; then
|
|
233
|
+
check_skip "npm install (no package.json in worktree)"
|
|
234
|
+
return 0
|
|
235
|
+
fi
|
|
236
|
+
|
|
237
|
+
if (cd "$WORKTREE_PATH" && npm install --silent 2>/dev/null); then
|
|
238
|
+
check_pass "npm install completed"
|
|
239
|
+
return 0
|
|
240
|
+
else
|
|
241
|
+
check_fail "npm install completed" "npm install failed in $WORKTREE_PATH"
|
|
242
|
+
return 1
|
|
243
|
+
fi
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
# ============================================================
|
|
247
|
+
# STEP 5: Baseline tests
|
|
248
|
+
# ============================================================
|
|
249
|
+
|
|
250
|
+
run_baseline_tests() {
|
|
251
|
+
if [[ "$SKIP_TESTS" == true ]]; then
|
|
252
|
+
check_skip "Baseline tests pass (--skip-tests)"
|
|
253
|
+
return 0
|
|
254
|
+
fi
|
|
255
|
+
|
|
256
|
+
if [[ ! -f "$WORKTREE_PATH/package.json" ]]; then
|
|
257
|
+
check_skip "Baseline tests pass (no package.json in worktree)"
|
|
258
|
+
return 0
|
|
259
|
+
fi
|
|
260
|
+
|
|
261
|
+
if (cd "$WORKTREE_PATH" && npm run test:run 2>/dev/null); then
|
|
262
|
+
check_pass "Baseline tests pass"
|
|
263
|
+
return 0
|
|
264
|
+
else
|
|
265
|
+
check_fail "Baseline tests pass" "npm run test:run failed in $WORKTREE_PATH"
|
|
266
|
+
return 1
|
|
267
|
+
fi
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
# ============================================================
|
|
271
|
+
# EXECUTE STEPS
|
|
272
|
+
# ============================================================
|
|
273
|
+
|
|
274
|
+
# Step 1: Gitignore
|
|
275
|
+
ensure_gitignored || true
|
|
276
|
+
|
|
277
|
+
# Step 2: Branch (depends on step 1 not fatally failing)
|
|
278
|
+
create_branch || true
|
|
279
|
+
|
|
280
|
+
# Step 3: Worktree (depends on branch existing)
|
|
281
|
+
create_worktree || true
|
|
282
|
+
|
|
283
|
+
# Step 4: npm install (depends on worktree existing)
|
|
284
|
+
if [[ -d "$WORKTREE_PATH" ]]; then
|
|
285
|
+
run_npm_install || true
|
|
286
|
+
else
|
|
287
|
+
check_skip "npm install (worktree not available)"
|
|
288
|
+
fi
|
|
289
|
+
|
|
290
|
+
# Step 5: Baseline tests (depends on npm install)
|
|
291
|
+
if [[ -d "$WORKTREE_PATH" ]]; then
|
|
292
|
+
run_baseline_tests || true
|
|
293
|
+
else
|
|
294
|
+
check_skip "Baseline tests pass (worktree not available)"
|
|
295
|
+
fi
|
|
296
|
+
|
|
297
|
+
# ============================================================
|
|
298
|
+
# STRUCTURED OUTPUT
|
|
299
|
+
# ============================================================
|
|
300
|
+
|
|
301
|
+
echo "## Worktree Setup Report"
|
|
302
|
+
echo ""
|
|
303
|
+
echo "**Task:** \`$TASK_ID\` — $TASK_NAME"
|
|
304
|
+
echo "**Branch:** \`$BRANCH_NAME\`"
|
|
305
|
+
echo "**Worktree:** \`$WORKTREE_PATH\`"
|
|
306
|
+
echo ""
|
|
307
|
+
|
|
308
|
+
for result in "${RESULTS[@]}"; do
|
|
309
|
+
echo "$result"
|
|
310
|
+
done
|
|
311
|
+
|
|
312
|
+
echo ""
|
|
313
|
+
TOTAL=$((CHECK_PASS + CHECK_FAIL))
|
|
314
|
+
echo "---"
|
|
315
|
+
echo ""
|
|
316
|
+
|
|
317
|
+
if [[ $CHECK_FAIL -eq 0 ]]; then
|
|
318
|
+
echo "**Result: PASS** ($CHECK_PASS/$TOTAL checks passed)"
|
|
319
|
+
exit 0
|
|
320
|
+
else
|
|
321
|
+
echo "**Result: FAIL** ($CHECK_FAIL/$TOTAL checks failed)"
|
|
322
|
+
exit 1
|
|
323
|
+
fi
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Spec Coverage Check
|
|
3
|
+
# Verify test coverage for spec compliance. Replaces spec-review "Compare to Spec" prose.
|
|
4
|
+
#
|
|
5
|
+
# Usage: spec-coverage-check.sh --plan-file <path> --repo-root <path> [--skip-run]
|
|
6
|
+
#
|
|
7
|
+
# Exit codes:
|
|
8
|
+
# 0 = coverage met (all planned tests exist and pass)
|
|
9
|
+
# 1 = gaps found (missing test files or test failures)
|
|
10
|
+
# 2 = usage error (missing required args)
|
|
11
|
+
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
|
|
14
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
15
|
+
|
|
16
|
+
# Colors
|
|
17
|
+
RED='\033[0;31m'
|
|
18
|
+
GREEN='\033[0;32m'
|
|
19
|
+
YELLOW='\033[1;33m'
|
|
20
|
+
NC='\033[0m'
|
|
21
|
+
|
|
22
|
+
# ============================================================
|
|
23
|
+
# ARGUMENT PARSING
|
|
24
|
+
# ============================================================
|
|
25
|
+
|
|
26
|
+
PLAN_FILE=""
|
|
27
|
+
REPO_ROOT=""
|
|
28
|
+
SKIP_RUN=false
|
|
29
|
+
|
|
30
|
+
usage() {
|
|
31
|
+
cat << 'USAGE'
|
|
32
|
+
Usage: spec-coverage-check.sh --plan-file <path> --repo-root <path> [--skip-run]
|
|
33
|
+
|
|
34
|
+
Required:
|
|
35
|
+
--plan-file <path> Path to the implementation plan markdown file
|
|
36
|
+
--repo-root <path> Repository root directory
|
|
37
|
+
|
|
38
|
+
Optional:
|
|
39
|
+
--skip-run Skip running tests (only check file existence)
|
|
40
|
+
--help Show this help message
|
|
41
|
+
|
|
42
|
+
Exit codes:
|
|
43
|
+
0 Coverage met (all planned tests exist and pass)
|
|
44
|
+
1 Gaps found (missing test files or test failures)
|
|
45
|
+
2 Usage error (missing required args)
|
|
46
|
+
USAGE
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
while [[ $# -gt 0 ]]; do
|
|
50
|
+
case "$1" in
|
|
51
|
+
--plan-file)
|
|
52
|
+
if [[ -z "${2:-}" ]]; then
|
|
53
|
+
echo "Error: --plan-file requires a path argument" >&2
|
|
54
|
+
exit 2
|
|
55
|
+
fi
|
|
56
|
+
PLAN_FILE="$2"
|
|
57
|
+
shift 2
|
|
58
|
+
;;
|
|
59
|
+
--repo-root)
|
|
60
|
+
if [[ -z "${2:-}" ]]; then
|
|
61
|
+
echo "Error: --repo-root requires a path argument" >&2
|
|
62
|
+
exit 2
|
|
63
|
+
fi
|
|
64
|
+
REPO_ROOT="$2"
|
|
65
|
+
shift 2
|
|
66
|
+
;;
|
|
67
|
+
--skip-run)
|
|
68
|
+
SKIP_RUN=true
|
|
69
|
+
shift
|
|
70
|
+
;;
|
|
71
|
+
--help)
|
|
72
|
+
usage
|
|
73
|
+
exit 0
|
|
74
|
+
;;
|
|
75
|
+
*)
|
|
76
|
+
echo "Error: Unknown argument '$1'" >&2
|
|
77
|
+
usage >&2
|
|
78
|
+
exit 2
|
|
79
|
+
;;
|
|
80
|
+
esac
|
|
81
|
+
done
|
|
82
|
+
|
|
83
|
+
if [[ -z "$PLAN_FILE" || -z "$REPO_ROOT" ]]; then
|
|
84
|
+
echo "Error: --plan-file and --repo-root are required" >&2
|
|
85
|
+
usage >&2
|
|
86
|
+
exit 2
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
if [[ ! -f "$PLAN_FILE" ]]; then
|
|
90
|
+
echo "Error: Plan file not found: $PLAN_FILE" >&2
|
|
91
|
+
exit 2
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
if [[ ! -d "$REPO_ROOT" ]]; then
|
|
95
|
+
echo "Error: Repo root directory not found: $REPO_ROOT" >&2
|
|
96
|
+
exit 2
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
# ============================================================
|
|
100
|
+
# CHECK FUNCTIONS
|
|
101
|
+
# ============================================================
|
|
102
|
+
|
|
103
|
+
CHECK_PASS=0
|
|
104
|
+
CHECK_FAIL=0
|
|
105
|
+
RESULTS=()
|
|
106
|
+
|
|
107
|
+
check_pass() {
|
|
108
|
+
local name="$1"
|
|
109
|
+
RESULTS+=("- **PASS**: $name")
|
|
110
|
+
CHECK_PASS=$((CHECK_PASS + 1))
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
check_fail() {
|
|
114
|
+
local name="$1"
|
|
115
|
+
local detail="${2:-}"
|
|
116
|
+
if [[ -n "$detail" ]]; then
|
|
117
|
+
RESULTS+=("- **FAIL**: $name — $detail")
|
|
118
|
+
else
|
|
119
|
+
RESULTS+=("- **FAIL**: $name")
|
|
120
|
+
fi
|
|
121
|
+
CHECK_FAIL=$((CHECK_FAIL + 1))
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
check_skip() {
|
|
125
|
+
local name="$1"
|
|
126
|
+
RESULTS+=("- **SKIP**: $name")
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
# ============================================================
|
|
130
|
+
# EXTRACT TEST FILES FROM PLAN
|
|
131
|
+
# ============================================================
|
|
132
|
+
|
|
133
|
+
# Extract test file paths from **Test file:** lines in the plan
|
|
134
|
+
TEST_FILES=()
|
|
135
|
+
while IFS= read -r line; do
|
|
136
|
+
# Match lines like: **Test file:** `src/widget.test.ts`
|
|
137
|
+
if [[ "$line" =~ \*\*Test\ file:\*\*[[:space:]]*\`([^\`]+)\` ]]; then
|
|
138
|
+
TEST_FILES+=("${BASH_REMATCH[1]}")
|
|
139
|
+
fi
|
|
140
|
+
done < "$PLAN_FILE"
|
|
141
|
+
|
|
142
|
+
# ============================================================
|
|
143
|
+
# CHECK: Test files referenced in plan
|
|
144
|
+
# ============================================================
|
|
145
|
+
|
|
146
|
+
if [[ ${#TEST_FILES[@]} -eq 0 ]]; then
|
|
147
|
+
check_fail "Test files in plan" "No test files referenced in plan document"
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
# ============================================================
|
|
151
|
+
# CHECK: Each test file exists on disk
|
|
152
|
+
# ============================================================
|
|
153
|
+
|
|
154
|
+
FOUND=0
|
|
155
|
+
MISSING=0
|
|
156
|
+
MISSING_LIST=()
|
|
157
|
+
|
|
158
|
+
for test_file in "${TEST_FILES[@]}"; do
|
|
159
|
+
full_path="$REPO_ROOT/$test_file"
|
|
160
|
+
if [[ -f "$full_path" ]]; then
|
|
161
|
+
check_pass "Test file exists: $test_file"
|
|
162
|
+
FOUND=$((FOUND + 1))
|
|
163
|
+
else
|
|
164
|
+
check_fail "Test file exists: $test_file" "Not found at $full_path"
|
|
165
|
+
MISSING=$((MISSING + 1))
|
|
166
|
+
MISSING_LIST+=("$test_file")
|
|
167
|
+
fi
|
|
168
|
+
done
|
|
169
|
+
|
|
170
|
+
# ============================================================
|
|
171
|
+
# CHECK: Tests pass (unless --skip-run)
|
|
172
|
+
# ============================================================
|
|
173
|
+
|
|
174
|
+
if [[ "$SKIP_RUN" == true ]]; then
|
|
175
|
+
check_skip "Test execution (--skip-run)"
|
|
176
|
+
elif [[ ${#TEST_FILES[@]} -gt 0 && $MISSING -eq 0 ]]; then
|
|
177
|
+
for test_file in "${TEST_FILES[@]}"; do
|
|
178
|
+
if ! npx vitest run --root "$REPO_ROOT" "$test_file" >/dev/null 2>&1; then
|
|
179
|
+
check_fail "Test passes: $test_file"
|
|
180
|
+
else
|
|
181
|
+
check_pass "Test passes: $test_file"
|
|
182
|
+
fi
|
|
183
|
+
done
|
|
184
|
+
fi
|
|
185
|
+
|
|
186
|
+
# ============================================================
|
|
187
|
+
# STRUCTURED OUTPUT
|
|
188
|
+
# ============================================================
|
|
189
|
+
|
|
190
|
+
echo "## Spec Coverage Report"
|
|
191
|
+
echo ""
|
|
192
|
+
echo "**Plan file:** \`$PLAN_FILE\`"
|
|
193
|
+
echo "**Repo root:** \`$REPO_ROOT\`"
|
|
194
|
+
echo ""
|
|
195
|
+
|
|
196
|
+
TOTAL_TESTS=${#TEST_FILES[@]}
|
|
197
|
+
echo "### Coverage Summary"
|
|
198
|
+
echo ""
|
|
199
|
+
echo "- Planned test files: $TOTAL_TESTS"
|
|
200
|
+
echo "- Found on disk: $FOUND"
|
|
201
|
+
echo "- Missing: $MISSING"
|
|
202
|
+
echo ""
|
|
203
|
+
|
|
204
|
+
if [[ ${#MISSING_LIST[@]} -gt 0 ]]; then
|
|
205
|
+
echo "### Missing Test Files"
|
|
206
|
+
echo ""
|
|
207
|
+
for f in "${MISSING_LIST[@]}"; do
|
|
208
|
+
echo "- \`$f\`"
|
|
209
|
+
done
|
|
210
|
+
echo ""
|
|
211
|
+
fi
|
|
212
|
+
|
|
213
|
+
echo "### Check Results"
|
|
214
|
+
echo ""
|
|
215
|
+
for result in "${RESULTS[@]}"; do
|
|
216
|
+
echo "$result"
|
|
217
|
+
done
|
|
218
|
+
|
|
219
|
+
echo ""
|
|
220
|
+
TOTAL=$((CHECK_PASS + CHECK_FAIL))
|
|
221
|
+
echo "---"
|
|
222
|
+
echo ""
|
|
223
|
+
|
|
224
|
+
if [[ $CHECK_FAIL -eq 0 && $TOTAL_TESTS -gt 0 ]]; then
|
|
225
|
+
echo "**Result: PASS** ($CHECK_PASS/$TOTAL checks passed)"
|
|
226
|
+
exit 0
|
|
227
|
+
else
|
|
228
|
+
echo "**Result: FAIL** ($CHECK_FAIL/$TOTAL checks failed)"
|
|
229
|
+
exit 1
|
|
230
|
+
fi
|