@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,234 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Validate Refactor
|
|
3
|
+
# Runs tests/lint/typecheck with structured pass/fail output.
|
|
4
|
+
# Replaces validate phase prose checklist with deterministic validation.
|
|
5
|
+
#
|
|
6
|
+
# Usage: validate-refactor.sh --repo-root <path> [--skip-lint] [--skip-typecheck]
|
|
7
|
+
#
|
|
8
|
+
# Exit codes:
|
|
9
|
+
# 0 = all checks pass
|
|
10
|
+
# 1 = one or more checks failed
|
|
11
|
+
# 2 = usage error (missing required args)
|
|
12
|
+
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
16
|
+
|
|
17
|
+
# Colors
|
|
18
|
+
RED='\033[0;31m'
|
|
19
|
+
GREEN='\033[0;32m'
|
|
20
|
+
YELLOW='\033[1;33m'
|
|
21
|
+
NC='\033[0m'
|
|
22
|
+
|
|
23
|
+
# ============================================================
|
|
24
|
+
# ARGUMENT PARSING
|
|
25
|
+
# ============================================================
|
|
26
|
+
|
|
27
|
+
REPO_ROOT=""
|
|
28
|
+
SKIP_LINT=false
|
|
29
|
+
SKIP_TYPECHECK=false
|
|
30
|
+
|
|
31
|
+
usage() {
|
|
32
|
+
cat << 'USAGE'
|
|
33
|
+
Usage: validate-refactor.sh --repo-root <path> [--skip-lint] [--skip-typecheck]
|
|
34
|
+
|
|
35
|
+
Required:
|
|
36
|
+
--repo-root <path> Path to the repository root (must contain package.json)
|
|
37
|
+
|
|
38
|
+
Optional:
|
|
39
|
+
--skip-lint Skip lint check
|
|
40
|
+
--skip-typecheck Skip typecheck
|
|
41
|
+
--help Show this help message
|
|
42
|
+
|
|
43
|
+
Exit codes:
|
|
44
|
+
0 All checks pass
|
|
45
|
+
1 One or more checks failed
|
|
46
|
+
2 Usage error (missing required args)
|
|
47
|
+
|
|
48
|
+
Checks performed:
|
|
49
|
+
- npm run test:run (required)
|
|
50
|
+
- npm run lint (skipped if missing or --skip-lint)
|
|
51
|
+
- npm run typecheck (skipped if missing or --skip-typecheck)
|
|
52
|
+
USAGE
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
while [[ $# -gt 0 ]]; do
|
|
56
|
+
case "$1" in
|
|
57
|
+
--repo-root)
|
|
58
|
+
if [[ -z "${2:-}" ]]; then
|
|
59
|
+
echo "Error: --repo-root requires a path argument" >&2
|
|
60
|
+
exit 2
|
|
61
|
+
fi
|
|
62
|
+
REPO_ROOT="$2"
|
|
63
|
+
shift 2
|
|
64
|
+
;;
|
|
65
|
+
--skip-lint)
|
|
66
|
+
SKIP_LINT=true
|
|
67
|
+
shift
|
|
68
|
+
;;
|
|
69
|
+
--skip-typecheck)
|
|
70
|
+
SKIP_TYPECHECK=true
|
|
71
|
+
shift
|
|
72
|
+
;;
|
|
73
|
+
--help)
|
|
74
|
+
usage
|
|
75
|
+
exit 0
|
|
76
|
+
;;
|
|
77
|
+
*)
|
|
78
|
+
echo "Error: Unknown argument '$1'" >&2
|
|
79
|
+
usage >&2
|
|
80
|
+
exit 2
|
|
81
|
+
;;
|
|
82
|
+
esac
|
|
83
|
+
done
|
|
84
|
+
|
|
85
|
+
if [[ -z "$REPO_ROOT" ]]; then
|
|
86
|
+
echo "Error: --repo-root is required" >&2
|
|
87
|
+
usage >&2
|
|
88
|
+
exit 2
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
# ============================================================
|
|
92
|
+
# CHECK FUNCTIONS
|
|
93
|
+
# ============================================================
|
|
94
|
+
|
|
95
|
+
CHECK_PASS=0
|
|
96
|
+
CHECK_FAIL=0
|
|
97
|
+
RESULTS=()
|
|
98
|
+
|
|
99
|
+
check_pass() {
|
|
100
|
+
local name="$1"
|
|
101
|
+
RESULTS+=("- **PASS**: $name")
|
|
102
|
+
CHECK_PASS=$((CHECK_PASS + 1))
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
check_fail() {
|
|
106
|
+
local name="$1"
|
|
107
|
+
local detail="${2:-}"
|
|
108
|
+
if [[ -n "$detail" ]]; then
|
|
109
|
+
RESULTS+=("- **FAIL**: $name — $detail")
|
|
110
|
+
else
|
|
111
|
+
RESULTS+=("- **FAIL**: $name")
|
|
112
|
+
fi
|
|
113
|
+
CHECK_FAIL=$((CHECK_FAIL + 1))
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
check_skip() {
|
|
117
|
+
local name="$1"
|
|
118
|
+
RESULTS+=("- **SKIP**: $name")
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# ============================================================
|
|
122
|
+
# HELPER: Check if npm script exists in package.json
|
|
123
|
+
# ============================================================
|
|
124
|
+
|
|
125
|
+
has_npm_script() {
|
|
126
|
+
local script_name="$1"
|
|
127
|
+
if [[ -f "$REPO_ROOT/package.json" ]]; then
|
|
128
|
+
# Use node or jq to check; fall back to grep
|
|
129
|
+
if command -v jq &>/dev/null; then
|
|
130
|
+
jq -e ".scripts[\"$script_name\"]" "$REPO_ROOT/package.json" &>/dev/null
|
|
131
|
+
return $?
|
|
132
|
+
else
|
|
133
|
+
grep -q "\"$script_name\"" "$REPO_ROOT/package.json" 2>/dev/null
|
|
134
|
+
return $?
|
|
135
|
+
fi
|
|
136
|
+
fi
|
|
137
|
+
return 1
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
# ============================================================
|
|
141
|
+
# CHECK 1: Tests (npm run test:run)
|
|
142
|
+
# ============================================================
|
|
143
|
+
|
|
144
|
+
check_tests() {
|
|
145
|
+
local output
|
|
146
|
+
if ! output="$(cd "$REPO_ROOT" && npm run test:run 2>&1)"; then
|
|
147
|
+
check_fail "Tests (npm run test:run)" "Tests failed"
|
|
148
|
+
return 1
|
|
149
|
+
fi
|
|
150
|
+
check_pass "Tests (npm run test:run)"
|
|
151
|
+
return 0
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
# ============================================================
|
|
155
|
+
# CHECK 2: Lint (npm run lint)
|
|
156
|
+
# ============================================================
|
|
157
|
+
|
|
158
|
+
check_lint() {
|
|
159
|
+
if [[ "$SKIP_LINT" == true ]]; then
|
|
160
|
+
check_skip "Lint (--skip-lint)"
|
|
161
|
+
return 0
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
if ! has_npm_script "lint"; then
|
|
165
|
+
check_skip "Lint (no lint script in package.json)"
|
|
166
|
+
return 0
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
local output
|
|
170
|
+
if ! output="$(cd "$REPO_ROOT" && npm run lint 2>&1)"; then
|
|
171
|
+
check_fail "Lint (npm run lint)" "Lint errors found"
|
|
172
|
+
return 1
|
|
173
|
+
fi
|
|
174
|
+
check_pass "Lint (npm run lint)"
|
|
175
|
+
return 0
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
# ============================================================
|
|
179
|
+
# CHECK 3: Typecheck (npm run typecheck)
|
|
180
|
+
# ============================================================
|
|
181
|
+
|
|
182
|
+
check_typecheck() {
|
|
183
|
+
if [[ "$SKIP_TYPECHECK" == true ]]; then
|
|
184
|
+
check_skip "Typecheck (--skip-typecheck)"
|
|
185
|
+
return 0
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
if ! has_npm_script "typecheck"; then
|
|
189
|
+
check_skip "Typecheck (no typecheck script in package.json)"
|
|
190
|
+
return 0
|
|
191
|
+
fi
|
|
192
|
+
|
|
193
|
+
local output
|
|
194
|
+
if ! output="$(cd "$REPO_ROOT" && npm run typecheck 2>&1)"; then
|
|
195
|
+
check_fail "Typecheck (npm run typecheck)" "Type errors found"
|
|
196
|
+
return 1
|
|
197
|
+
fi
|
|
198
|
+
check_pass "Typecheck (npm run typecheck)"
|
|
199
|
+
return 0
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
# ============================================================
|
|
203
|
+
# EXECUTE CHECKS
|
|
204
|
+
# ============================================================
|
|
205
|
+
|
|
206
|
+
check_tests || true
|
|
207
|
+
check_lint || true
|
|
208
|
+
check_typecheck || true
|
|
209
|
+
|
|
210
|
+
# ============================================================
|
|
211
|
+
# STRUCTURED OUTPUT
|
|
212
|
+
# ============================================================
|
|
213
|
+
|
|
214
|
+
echo "## Refactor Validation Report"
|
|
215
|
+
echo ""
|
|
216
|
+
echo "**Repository:** \`$REPO_ROOT\`"
|
|
217
|
+
echo ""
|
|
218
|
+
|
|
219
|
+
for result in "${RESULTS[@]}"; do
|
|
220
|
+
echo "$result"
|
|
221
|
+
done
|
|
222
|
+
|
|
223
|
+
echo ""
|
|
224
|
+
TOTAL=$((CHECK_PASS + CHECK_FAIL))
|
|
225
|
+
echo "---"
|
|
226
|
+
echo ""
|
|
227
|
+
|
|
228
|
+
if [[ $CHECK_FAIL -eq 0 ]]; then
|
|
229
|
+
echo "**Result: PASS** ($CHECK_PASS/$TOTAL checks passed)"
|
|
230
|
+
exit 0
|
|
231
|
+
else
|
|
232
|
+
echo "**Result: FAIL** ($CHECK_FAIL/$TOTAL checks failed)"
|
|
233
|
+
exit 1
|
|
234
|
+
fi
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Validates rm commands - blocks those targeting outside current directory
|
|
3
|
+
# Exit 0 = allow, Exit 2 = block with message to stderr
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
# Read tool input from stdin
|
|
8
|
+
INPUT=$(cat)
|
|
9
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
|
|
10
|
+
|
|
11
|
+
# If not an rm command, allow it
|
|
12
|
+
if [[ ! "$COMMAND" =~ ^[[:space:]]*(rm|/bin/rm|/usr/bin/rm)[[:space:]] ]]; then
|
|
13
|
+
exit 0
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
# Extract the working directory
|
|
17
|
+
CWD=$(echo "$INPUT" | jq -r '.cwd // empty')
|
|
18
|
+
[[ -z "$CWD" ]] && CWD="$PWD"
|
|
19
|
+
|
|
20
|
+
# Function to check if a path is inside CWD
|
|
21
|
+
is_inside_cwd() {
|
|
22
|
+
local target="$1"
|
|
23
|
+
local resolved
|
|
24
|
+
|
|
25
|
+
# Handle relative paths
|
|
26
|
+
if [[ "$target" != /* ]]; then
|
|
27
|
+
target="$CWD/$target"
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# Resolve to absolute path (handle .., symlinks, etc)
|
|
31
|
+
# Use realpath if target exists, otherwise normalize manually
|
|
32
|
+
if [[ -e "$target" ]]; then
|
|
33
|
+
resolved=$(realpath "$target" 2>/dev/null) || resolved="$target"
|
|
34
|
+
else
|
|
35
|
+
# For non-existent paths, normalize parent + basename
|
|
36
|
+
local parent=$(dirname "$target")
|
|
37
|
+
local base=$(basename "$target")
|
|
38
|
+
if [[ -d "$parent" ]]; then
|
|
39
|
+
resolved="$(realpath "$parent")/$base"
|
|
40
|
+
else
|
|
41
|
+
resolved="$target"
|
|
42
|
+
fi
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# Check if resolved path starts with CWD
|
|
46
|
+
local resolved_cwd
|
|
47
|
+
resolved_cwd=$(realpath "$CWD" 2>/dev/null) || resolved_cwd="$CWD"
|
|
48
|
+
|
|
49
|
+
[[ "$resolved" == "$resolved_cwd" || "$resolved" == "$resolved_cwd"/* ]]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# Quick sanity check for obviously catastrophic commands
|
|
53
|
+
# The path resolution below handles the full check
|
|
54
|
+
NORMALIZED_CMD=$(echo "$COMMAND" | tr -s ' ')
|
|
55
|
+
if [[ "$NORMALIZED_CMD" =~ rm[[:space:]]+-[rRf]*[[:space:]]+/[[:space:]]*$ ]] || \
|
|
56
|
+
[[ "$NORMALIZED_CMD" =~ rm[[:space:]]+-[rRf]*[[:space:]]+/\*[[:space:]]*$ ]]; then
|
|
57
|
+
echo "BLOCKED: rm targeting filesystem root" >&2
|
|
58
|
+
exit 2
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# Parse rm arguments to find target paths
|
|
62
|
+
# Strip rm command and flags, get remaining arguments
|
|
63
|
+
TARGETS=$(echo "$COMMAND" | sed -E 's/^[[:space:]]*(rm|\/bin\/rm|\/usr\/bin\/rm)[[:space:]]+//' | \
|
|
64
|
+
sed -E 's/-[rRfivI]+[[:space:]]*//g' | \
|
|
65
|
+
sed -E 's/--[a-z-]+[[:space:]]*//g' | \
|
|
66
|
+
xargs -n1 2>/dev/null || true)
|
|
67
|
+
|
|
68
|
+
# If no targets found, allow (rm with no args will fail anyway)
|
|
69
|
+
[[ -z "$TARGETS" ]] && exit 0
|
|
70
|
+
|
|
71
|
+
# Check each target
|
|
72
|
+
BLOCKED_PATHS=()
|
|
73
|
+
while IFS= read -r target; do
|
|
74
|
+
[[ -z "$target" ]] && continue
|
|
75
|
+
|
|
76
|
+
# Skip if target contains unexpanded variables (could be dangerous)
|
|
77
|
+
if [[ "$target" == *'$'* ]]; then
|
|
78
|
+
BLOCKED_PATHS+=("$target (contains unexpanded variable)")
|
|
79
|
+
continue
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
if ! is_inside_cwd "$target"; then
|
|
83
|
+
BLOCKED_PATHS+=("$target")
|
|
84
|
+
fi
|
|
85
|
+
done <<< "$TARGETS"
|
|
86
|
+
|
|
87
|
+
if [[ ${#BLOCKED_PATHS[@]} -gt 0 ]]; then
|
|
88
|
+
echo "BLOCKED: rm targets paths outside current directory ($CWD):" >&2
|
|
89
|
+
printf " - %s\n" "${BLOCKED_PATHS[@]}" >&2
|
|
90
|
+
exit 2
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
exit 0
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# verify-delegation-saga.sh — Verify saga step ordering in delegation event streams
|
|
3
|
+
# Reads a JSONL event file and validates that delegation saga events appear in correct order.
|
|
4
|
+
#
|
|
5
|
+
# Usage: verify-delegation-saga.sh --feature-id <id> [--state-dir <path>]
|
|
6
|
+
#
|
|
7
|
+
# Exit codes:
|
|
8
|
+
# 0 = valid saga (or no team events to validate)
|
|
9
|
+
# 1 = violations found
|
|
10
|
+
# 2 = usage error (missing args, no event file, empty stream)
|
|
11
|
+
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
|
|
14
|
+
# ============================================================
|
|
15
|
+
# ARGUMENT PARSING
|
|
16
|
+
# ============================================================
|
|
17
|
+
|
|
18
|
+
FEATURE_ID=""
|
|
19
|
+
STATE_DIR="${HOME}/.claude/workflow-state"
|
|
20
|
+
|
|
21
|
+
usage() {
|
|
22
|
+
cat >&2 << 'USAGE'
|
|
23
|
+
Usage: verify-delegation-saga.sh --feature-id <id> [--state-dir <path>]
|
|
24
|
+
|
|
25
|
+
Required:
|
|
26
|
+
--feature-id <id> Feature identifier (used to locate events JSONL file)
|
|
27
|
+
|
|
28
|
+
Optional:
|
|
29
|
+
--state-dir <path> Directory containing event files (default: ~/.claude/workflow-state/)
|
|
30
|
+
--help Show this help message
|
|
31
|
+
|
|
32
|
+
Exit codes:
|
|
33
|
+
0 Valid saga ordering (or no team events to validate)
|
|
34
|
+
1 Saga ordering violations found
|
|
35
|
+
2 Usage error (missing args, no event file, empty stream)
|
|
36
|
+
USAGE
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
while [[ $# -gt 0 ]]; do
|
|
40
|
+
case "$1" in
|
|
41
|
+
--feature-id)
|
|
42
|
+
if [[ -z "${2:-}" ]]; then
|
|
43
|
+
echo "Error: --feature-id requires a value" >&2
|
|
44
|
+
exit 2
|
|
45
|
+
fi
|
|
46
|
+
FEATURE_ID="$2"
|
|
47
|
+
shift 2
|
|
48
|
+
;;
|
|
49
|
+
--state-dir)
|
|
50
|
+
if [[ -z "${2:-}" ]]; then
|
|
51
|
+
echo "Error: --state-dir requires a path argument" >&2
|
|
52
|
+
exit 2
|
|
53
|
+
fi
|
|
54
|
+
STATE_DIR="$2"
|
|
55
|
+
shift 2
|
|
56
|
+
;;
|
|
57
|
+
--help)
|
|
58
|
+
usage
|
|
59
|
+
exit 0
|
|
60
|
+
;;
|
|
61
|
+
*)
|
|
62
|
+
echo "Error: Unknown argument '$1'" >&2
|
|
63
|
+
usage
|
|
64
|
+
exit 2
|
|
65
|
+
;;
|
|
66
|
+
esac
|
|
67
|
+
done
|
|
68
|
+
|
|
69
|
+
if [[ -z "$FEATURE_ID" ]]; then
|
|
70
|
+
echo "Error: --feature-id is required" >&2
|
|
71
|
+
usage
|
|
72
|
+
exit 2
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
# ============================================================
|
|
76
|
+
# DEPENDENCY CHECK
|
|
77
|
+
# ============================================================
|
|
78
|
+
|
|
79
|
+
if ! command -v jq &>/dev/null; then
|
|
80
|
+
echo "Error: jq is required but not installed" >&2
|
|
81
|
+
exit 2
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
# ============================================================
|
|
85
|
+
# LOCATE EVENT FILE
|
|
86
|
+
# ============================================================
|
|
87
|
+
|
|
88
|
+
EVENT_FILE="${STATE_DIR}/${FEATURE_ID}.events.jsonl"
|
|
89
|
+
|
|
90
|
+
if [[ ! -f "$EVENT_FILE" ]]; then
|
|
91
|
+
echo "Error: Event file not found: $EVENT_FILE" >&2
|
|
92
|
+
exit 2
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
# Check for empty file
|
|
96
|
+
if [[ ! -s "$EVENT_FILE" ]]; then
|
|
97
|
+
echo "Error: Event file is empty: $EVENT_FILE" >&2
|
|
98
|
+
exit 2
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
# ============================================================
|
|
102
|
+
# EXTRACT TEAM EVENTS
|
|
103
|
+
# ============================================================
|
|
104
|
+
|
|
105
|
+
# Filter to only team.* events, preserving order
|
|
106
|
+
TEAM_EVENTS="$(jq -c 'select(.type | startswith("team."))' "$EVENT_FILE")"
|
|
107
|
+
|
|
108
|
+
# If no team events exist, nothing to validate — exit clean
|
|
109
|
+
if [[ -z "$TEAM_EVENTS" ]]; then
|
|
110
|
+
echo "No team events found in event stream. Skipping saga validation."
|
|
111
|
+
exit 0
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
# ============================================================
|
|
115
|
+
# VALIDATION STATE
|
|
116
|
+
# ============================================================
|
|
117
|
+
|
|
118
|
+
VIOLATIONS=()
|
|
119
|
+
HAS_SPAWNED=false
|
|
120
|
+
HAS_PLANNED=false
|
|
121
|
+
HAS_DISBANDED=false
|
|
122
|
+
DISBANDED_SEQUENCE=0
|
|
123
|
+
|
|
124
|
+
# Track planned task IDs
|
|
125
|
+
declare -a PLANNED_TASK_IDS=()
|
|
126
|
+
|
|
127
|
+
# Track dispatched task IDs (from assignedTaskIds)
|
|
128
|
+
declare -a DISPATCHED_TASK_IDS=()
|
|
129
|
+
|
|
130
|
+
# ============================================================
|
|
131
|
+
# RULE VALIDATION — Process events in sequence order
|
|
132
|
+
# ============================================================
|
|
133
|
+
|
|
134
|
+
while IFS= read -r event; do
|
|
135
|
+
event_type="$(echo "$event" | jq -r '.type')"
|
|
136
|
+
event_seq="$(echo "$event" | jq -r '.sequence')"
|
|
137
|
+
|
|
138
|
+
case "$event_type" in
|
|
139
|
+
team.spawned)
|
|
140
|
+
HAS_SPAWNED=true
|
|
141
|
+
;;
|
|
142
|
+
|
|
143
|
+
team.task.planned)
|
|
144
|
+
# Rule 1: team.spawned must appear before any team.task.planned
|
|
145
|
+
if [[ "$HAS_SPAWNED" != true ]]; then
|
|
146
|
+
VIOLATIONS+=("VIOLATION: team.task.planned (seq $event_seq) appeared before team.spawned")
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
# Rule 4: team.disbanded must be the last team event
|
|
150
|
+
if [[ "$HAS_DISBANDED" == true ]]; then
|
|
151
|
+
VIOLATIONS+=("VIOLATION: team.task.planned (seq $event_seq) appeared after team.disbanded (seq $DISBANDED_SEQUENCE)")
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
# Track planned task IDs — support both single taskId and batched taskIds[]
|
|
155
|
+
while IFS= read -r tid; do
|
|
156
|
+
PLANNED_TASK_IDS+=("$tid")
|
|
157
|
+
done < <(echo "$event" | jq -r '(.data.taskIds // [])[] // empty, (.data.taskId // empty)')
|
|
158
|
+
HAS_PLANNED=true
|
|
159
|
+
;;
|
|
160
|
+
|
|
161
|
+
team.teammate.dispatched)
|
|
162
|
+
# Rule 1: team.spawned must appear before dispatch
|
|
163
|
+
if [[ "$HAS_SPAWNED" != true ]]; then
|
|
164
|
+
VIOLATIONS+=("VIOLATION: team.teammate.dispatched (seq $event_seq) appeared before team.spawned")
|
|
165
|
+
fi
|
|
166
|
+
|
|
167
|
+
# Rule 2: team.task.planned must appear before any team.teammate.dispatched
|
|
168
|
+
if [[ "$HAS_PLANNED" != true ]]; then
|
|
169
|
+
VIOLATIONS+=("VIOLATION: team.teammate.dispatched (seq $event_seq) appeared before any team.task.planned")
|
|
170
|
+
fi
|
|
171
|
+
|
|
172
|
+
# Rule 4: team.disbanded must be the last team event
|
|
173
|
+
if [[ "$HAS_DISBANDED" == true ]]; then
|
|
174
|
+
VIOLATIONS+=("VIOLATION: team.teammate.dispatched (seq $event_seq) appeared after team.disbanded (seq $DISBANDED_SEQUENCE)")
|
|
175
|
+
fi
|
|
176
|
+
|
|
177
|
+
# Track dispatched task IDs for coverage check (safe on missing/null field)
|
|
178
|
+
while IFS= read -r tid; do
|
|
179
|
+
DISPATCHED_TASK_IDS+=("$tid")
|
|
180
|
+
done < <(echo "$event" | jq -r '(.data.assignedTaskIds // [])[]')
|
|
181
|
+
;;
|
|
182
|
+
|
|
183
|
+
team.disbanded)
|
|
184
|
+
HAS_DISBANDED=true
|
|
185
|
+
DISBANDED_SEQUENCE="$event_seq"
|
|
186
|
+
;;
|
|
187
|
+
|
|
188
|
+
# Other team.* events — no specific rules yet, but check disbanded constraint
|
|
189
|
+
team.*)
|
|
190
|
+
if [[ "$HAS_DISBANDED" == true ]]; then
|
|
191
|
+
VIOLATIONS+=("VIOLATION: $event_type (seq $event_seq) appeared after team.disbanded (seq $DISBANDED_SEQUENCE)")
|
|
192
|
+
fi
|
|
193
|
+
;;
|
|
194
|
+
esac
|
|
195
|
+
done <<< "$TEAM_EVENTS"
|
|
196
|
+
|
|
197
|
+
# ============================================================
|
|
198
|
+
# RULE 3: All dispatched task IDs must have been planned
|
|
199
|
+
# ============================================================
|
|
200
|
+
|
|
201
|
+
if [[ ${#DISPATCHED_TASK_IDS[@]} -gt 0 ]]; then
|
|
202
|
+
for dispatched_id in "${DISPATCHED_TASK_IDS[@]}"; do
|
|
203
|
+
found=false
|
|
204
|
+
if [[ ${#PLANNED_TASK_IDS[@]} -gt 0 ]]; then
|
|
205
|
+
for planned_id in "${PLANNED_TASK_IDS[@]}"; do
|
|
206
|
+
if [[ "$dispatched_id" == "$planned_id" ]]; then
|
|
207
|
+
found=true
|
|
208
|
+
break
|
|
209
|
+
fi
|
|
210
|
+
done
|
|
211
|
+
fi
|
|
212
|
+
if [[ "$found" != true ]]; then
|
|
213
|
+
VIOLATIONS+=("VIOLATION: Dispatched task '$dispatched_id' was never planned (no team.task.planned event with this taskId)")
|
|
214
|
+
fi
|
|
215
|
+
done
|
|
216
|
+
fi
|
|
217
|
+
|
|
218
|
+
# ============================================================
|
|
219
|
+
# OUTPUT AND EXIT
|
|
220
|
+
# ============================================================
|
|
221
|
+
|
|
222
|
+
if [[ ${#VIOLATIONS[@]} -gt 0 ]]; then
|
|
223
|
+
echo "## Delegation Saga Validation"
|
|
224
|
+
echo ""
|
|
225
|
+
echo "**Status:** FAILED for feature \`$FEATURE_ID\`"
|
|
226
|
+
echo ""
|
|
227
|
+
echo "### Violations"
|
|
228
|
+
echo ""
|
|
229
|
+
for v in "${VIOLATIONS[@]}"; do
|
|
230
|
+
echo "- $v"
|
|
231
|
+
done
|
|
232
|
+
echo ""
|
|
233
|
+
echo "**Total:** ${#VIOLATIONS[@]} violation(s) found."
|
|
234
|
+
exit 1
|
|
235
|
+
fi
|
|
236
|
+
|
|
237
|
+
echo "## Delegation Saga Validation"
|
|
238
|
+
echo ""
|
|
239
|
+
echo "**Status:** PASSED for feature \`$FEATURE_ID\`"
|
|
240
|
+
exit 0
|