@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,267 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Validate .NET Standards
|
|
3
|
+
# Checks .NET project structure compliance: required files, CPM configuration,
|
|
4
|
+
# editorconfig, global.json, and no inline package versions.
|
|
5
|
+
#
|
|
6
|
+
# Usage: validate-dotnet-standards.sh --project-root <path>
|
|
7
|
+
#
|
|
8
|
+
# Exit codes:
|
|
9
|
+
# 0 = project is compliant
|
|
10
|
+
# 1 = violations found
|
|
11
|
+
# 2 = usage error (missing required args)
|
|
12
|
+
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
# ============================================================
|
|
16
|
+
# ARGUMENT PARSING
|
|
17
|
+
# ============================================================
|
|
18
|
+
|
|
19
|
+
PROJECT_ROOT=""
|
|
20
|
+
|
|
21
|
+
usage() {
|
|
22
|
+
cat << 'USAGE'
|
|
23
|
+
Usage: validate-dotnet-standards.sh --project-root <path>
|
|
24
|
+
|
|
25
|
+
Required:
|
|
26
|
+
--project-root <path> Root directory of the .NET project
|
|
27
|
+
|
|
28
|
+
Optional:
|
|
29
|
+
--help Show this help message
|
|
30
|
+
|
|
31
|
+
Exit codes:
|
|
32
|
+
0 Project is compliant
|
|
33
|
+
1 Violations found
|
|
34
|
+
2 Usage error (missing required args)
|
|
35
|
+
USAGE
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
while [[ $# -gt 0 ]]; do
|
|
39
|
+
case "$1" in
|
|
40
|
+
--project-root)
|
|
41
|
+
if [[ -z "${2:-}" ]]; then
|
|
42
|
+
echo "Error: --project-root requires a path argument" >&2
|
|
43
|
+
exit 2
|
|
44
|
+
fi
|
|
45
|
+
PROJECT_ROOT="$2"
|
|
46
|
+
shift 2
|
|
47
|
+
;;
|
|
48
|
+
--help)
|
|
49
|
+
usage
|
|
50
|
+
exit 0
|
|
51
|
+
;;
|
|
52
|
+
*)
|
|
53
|
+
echo "Error: Unknown argument '$1'" >&2
|
|
54
|
+
usage >&2
|
|
55
|
+
exit 2
|
|
56
|
+
;;
|
|
57
|
+
esac
|
|
58
|
+
done
|
|
59
|
+
|
|
60
|
+
if [[ -z "$PROJECT_ROOT" ]]; then
|
|
61
|
+
echo "Error: --project-root is required" >&2
|
|
62
|
+
usage >&2
|
|
63
|
+
exit 2
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
# ============================================================
|
|
67
|
+
# CHECK FUNCTIONS
|
|
68
|
+
# ============================================================
|
|
69
|
+
|
|
70
|
+
CHECK_PASS=0
|
|
71
|
+
CHECK_FAIL=0
|
|
72
|
+
RESULTS=()
|
|
73
|
+
|
|
74
|
+
check_pass() {
|
|
75
|
+
local name="$1"
|
|
76
|
+
RESULTS+=("- **PASS**: $name")
|
|
77
|
+
CHECK_PASS=$((CHECK_PASS + 1))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
check_fail() {
|
|
81
|
+
local name="$1"
|
|
82
|
+
local detail="${2:-}"
|
|
83
|
+
if [[ -n "$detail" ]]; then
|
|
84
|
+
RESULTS+=("- **FAIL**: $name — $detail")
|
|
85
|
+
else
|
|
86
|
+
RESULTS+=("- **FAIL**: $name")
|
|
87
|
+
fi
|
|
88
|
+
CHECK_FAIL=$((CHECK_FAIL + 1))
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# ============================================================
|
|
92
|
+
# CHECK 1: Directory.Build.props exists
|
|
93
|
+
# ============================================================
|
|
94
|
+
|
|
95
|
+
check_build_props() {
|
|
96
|
+
local build_props="$PROJECT_ROOT/src/Directory.Build.props"
|
|
97
|
+
if [[ -f "$build_props" ]]; then
|
|
98
|
+
check_pass "Directory.Build.props exists"
|
|
99
|
+
return 0
|
|
100
|
+
else
|
|
101
|
+
check_fail "Directory.Build.props exists" "Not found at $build_props"
|
|
102
|
+
return 1
|
|
103
|
+
fi
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
# ============================================================
|
|
107
|
+
# CHECK 2: Directory.Packages.props exists
|
|
108
|
+
# ============================================================
|
|
109
|
+
|
|
110
|
+
check_packages_props_exists() {
|
|
111
|
+
local packages_props="$PROJECT_ROOT/src/Directory.Packages.props"
|
|
112
|
+
if [[ -f "$packages_props" ]]; then
|
|
113
|
+
check_pass "Directory.Packages.props exists"
|
|
114
|
+
return 0
|
|
115
|
+
else
|
|
116
|
+
check_fail "Directory.Packages.props exists" "Not found at $packages_props"
|
|
117
|
+
return 1
|
|
118
|
+
fi
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# ============================================================
|
|
122
|
+
# CHECK 3: CPM is enabled in Directory.Packages.props
|
|
123
|
+
# ============================================================
|
|
124
|
+
|
|
125
|
+
check_cpm_enabled() {
|
|
126
|
+
local packages_props="$PROJECT_ROOT/src/Directory.Packages.props"
|
|
127
|
+
|
|
128
|
+
if [[ ! -f "$packages_props" ]]; then
|
|
129
|
+
# Already reported by check_packages_props_exists
|
|
130
|
+
return 1
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
if grep -qE '<ManagePackageVersionsCentrally>[[:space:]]*true[[:space:]]*</ManagePackageVersionsCentrally>' "$packages_props"; then
|
|
134
|
+
check_pass "Central Package Management (CPM) enabled"
|
|
135
|
+
return 0
|
|
136
|
+
else
|
|
137
|
+
check_fail "Central Package Management (CPM) enabled" "ManagePackageVersionsCentrally is not set to true in Directory.Packages.props"
|
|
138
|
+
return 1
|
|
139
|
+
fi
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
# ============================================================
|
|
143
|
+
# CHECK 4: .editorconfig exists
|
|
144
|
+
# ============================================================
|
|
145
|
+
|
|
146
|
+
check_editorconfig() {
|
|
147
|
+
local editorconfig="$PROJECT_ROOT/src/.editorconfig"
|
|
148
|
+
if [[ -f "$editorconfig" ]]; then
|
|
149
|
+
check_pass ".editorconfig exists"
|
|
150
|
+
return 0
|
|
151
|
+
else
|
|
152
|
+
check_fail ".editorconfig exists" "Not found at $editorconfig"
|
|
153
|
+
return 1
|
|
154
|
+
fi
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
# ============================================================
|
|
158
|
+
# CHECK 5: global.json exists with SDK version
|
|
159
|
+
# ============================================================
|
|
160
|
+
|
|
161
|
+
check_global_json() {
|
|
162
|
+
local global_json="$PROJECT_ROOT/src/global.json"
|
|
163
|
+
|
|
164
|
+
if [[ ! -f "$global_json" ]]; then
|
|
165
|
+
check_fail "global.json exists" "Not found at $global_json"
|
|
166
|
+
return 1
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
# Check for sdk.version field
|
|
170
|
+
if command -v jq &>/dev/null; then
|
|
171
|
+
local sdk_version
|
|
172
|
+
if ! sdk_version="$(jq -r '.sdk.version // empty' "$global_json" 2>/dev/null)"; then
|
|
173
|
+
check_fail "global.json valid" "Invalid JSON in $global_json"
|
|
174
|
+
return 1
|
|
175
|
+
fi
|
|
176
|
+
if [[ -n "$sdk_version" ]]; then
|
|
177
|
+
check_pass "global.json exists (SDK $sdk_version)"
|
|
178
|
+
return 0
|
|
179
|
+
else
|
|
180
|
+
check_fail "global.json exists" "File exists but sdk.version is not specified"
|
|
181
|
+
return 1
|
|
182
|
+
fi
|
|
183
|
+
else
|
|
184
|
+
# Fallback: just check file contains "version"
|
|
185
|
+
if grep -q '"version"' "$global_json"; then
|
|
186
|
+
check_pass "global.json exists (with version)"
|
|
187
|
+
return 0
|
|
188
|
+
else
|
|
189
|
+
check_fail "global.json exists" "File exists but no version field found"
|
|
190
|
+
return 1
|
|
191
|
+
fi
|
|
192
|
+
fi
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
# ============================================================
|
|
196
|
+
# CHECK 6: No inline PackageReference with Version in .csproj
|
|
197
|
+
# ============================================================
|
|
198
|
+
|
|
199
|
+
check_no_inline_versions() {
|
|
200
|
+
# Find all .csproj files under src/
|
|
201
|
+
local src_dir="$PROJECT_ROOT/src"
|
|
202
|
+
if [[ ! -d "$src_dir" ]]; then
|
|
203
|
+
check_pass "No inline package versions (no src/ directory)"
|
|
204
|
+
return 0
|
|
205
|
+
fi
|
|
206
|
+
|
|
207
|
+
local violations=()
|
|
208
|
+
while IFS= read -r csproj_file; do
|
|
209
|
+
[[ -z "$csproj_file" ]] && continue
|
|
210
|
+
# Check for PackageReference with Version attribute (but not in Directory.Build.props)
|
|
211
|
+
local basename
|
|
212
|
+
basename="$(basename "$csproj_file")"
|
|
213
|
+
if [[ "$basename" == "Directory.Build.props" || "$basename" == "Directory.Packages.props" ]]; then
|
|
214
|
+
continue
|
|
215
|
+
fi
|
|
216
|
+
if grep -qE '<PackageReference\s+[^>]*Version\s*=' "$csproj_file"; then
|
|
217
|
+
violations+=("$csproj_file")
|
|
218
|
+
fi
|
|
219
|
+
done < <(find "$src_dir" -name '*.csproj' -type f 2>/dev/null)
|
|
220
|
+
|
|
221
|
+
if [[ ${#violations[@]} -eq 0 ]]; then
|
|
222
|
+
check_pass "No inline package versions in .csproj files"
|
|
223
|
+
return 0
|
|
224
|
+
else
|
|
225
|
+
local violation_list
|
|
226
|
+
violation_list="$(printf '%s\n' "${violations[@]}" | sed "s|$PROJECT_ROOT/||g" | tr '\n' ', ' | sed 's/, $//')"
|
|
227
|
+
check_fail "No inline package versions in .csproj files" "Inline Version found in: $violation_list"
|
|
228
|
+
return 1
|
|
229
|
+
fi
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
# ============================================================
|
|
233
|
+
# EXECUTE CHECKS
|
|
234
|
+
# ============================================================
|
|
235
|
+
|
|
236
|
+
check_build_props || true
|
|
237
|
+
check_packages_props_exists || true
|
|
238
|
+
check_cpm_enabled || true
|
|
239
|
+
check_editorconfig || true
|
|
240
|
+
check_global_json || true
|
|
241
|
+
check_no_inline_versions || true
|
|
242
|
+
|
|
243
|
+
# ============================================================
|
|
244
|
+
# STRUCTURED OUTPUT
|
|
245
|
+
# ============================================================
|
|
246
|
+
|
|
247
|
+
echo "## .NET Standards Compliance Report"
|
|
248
|
+
echo ""
|
|
249
|
+
echo "**Project root:** \`$PROJECT_ROOT\`"
|
|
250
|
+
echo ""
|
|
251
|
+
|
|
252
|
+
for result in "${RESULTS[@]}"; do
|
|
253
|
+
echo "$result"
|
|
254
|
+
done
|
|
255
|
+
|
|
256
|
+
echo ""
|
|
257
|
+
TOTAL=$((CHECK_PASS + CHECK_FAIL))
|
|
258
|
+
echo "---"
|
|
259
|
+
echo ""
|
|
260
|
+
|
|
261
|
+
if [[ $CHECK_FAIL -eq 0 ]]; then
|
|
262
|
+
echo "**Result: PASS** ($CHECK_PASS/$TOTAL checks passed)"
|
|
263
|
+
exit 0
|
|
264
|
+
else
|
|
265
|
+
echo "**Result: FAIL** ($CHECK_FAIL/$TOTAL checks failed)"
|
|
266
|
+
exit 1
|
|
267
|
+
fi
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# validate-installation.sh — Post-install verification for skills
|
|
3
|
+
#
|
|
4
|
+
# Usage: validate-installation.sh [target-skills-dir]
|
|
5
|
+
# Default target: ~/.claude/skills
|
|
6
|
+
#
|
|
7
|
+
# Verifies:
|
|
8
|
+
# 1. Each skill subdirectory has a SKILL.md file
|
|
9
|
+
# 2. Each SKILL.md passes frontmatter validation
|
|
10
|
+
# 3. references/ directories have matching file counts vs repo source
|
|
11
|
+
#
|
|
12
|
+
# Exit 0 if all pass, exit 1 with error list otherwise.
|
|
13
|
+
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
17
|
+
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
18
|
+
VALIDATOR="${REPO_ROOT}/skills/validate-frontmatter.sh"
|
|
19
|
+
TARGET_DIR="${1:-${HOME}/.claude/skills}"
|
|
20
|
+
|
|
21
|
+
if [[ ! -d "$TARGET_DIR" ]]; then
|
|
22
|
+
echo "ERROR: Target directory not found: $TARGET_DIR"
|
|
23
|
+
exit 2
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
if [[ ! -x "$VALIDATOR" ]]; then
|
|
27
|
+
echo "ERROR: Validator script not found or not executable: $VALIDATOR"
|
|
28
|
+
exit 2
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
ERRORS=()
|
|
32
|
+
PASS_COUNT=0
|
|
33
|
+
TOTAL=0
|
|
34
|
+
|
|
35
|
+
for skill_dir in "$TARGET_DIR"/*/; do
|
|
36
|
+
[[ -d "$skill_dir" ]] || continue
|
|
37
|
+
|
|
38
|
+
# Skip test-fixtures and trigger-tests directories
|
|
39
|
+
local_name=$(basename "$skill_dir")
|
|
40
|
+
if [[ "$local_name" == "test-fixtures" || "$local_name" == "trigger-tests" ]]; then
|
|
41
|
+
continue
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
TOTAL=$((TOTAL + 1))
|
|
45
|
+
skill_file="${skill_dir}SKILL.md"
|
|
46
|
+
|
|
47
|
+
# Check 1: SKILL.md exists
|
|
48
|
+
if [[ ! -f "$skill_file" ]]; then
|
|
49
|
+
ERRORS+=("${local_name}: Missing SKILL.md")
|
|
50
|
+
continue
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# Check 2: Frontmatter validation
|
|
54
|
+
result=""
|
|
55
|
+
rc=0
|
|
56
|
+
result=$("$VALIDATOR" "$skill_file" "$local_name" 2>&1) || rc=$?
|
|
57
|
+
|
|
58
|
+
if [[ $rc -ne 0 ]]; then
|
|
59
|
+
ERRORS+=("${local_name}: Frontmatter validation failed — ${result}")
|
|
60
|
+
continue
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
PASS_COUNT=$((PASS_COUNT + 1))
|
|
64
|
+
|
|
65
|
+
# Check 3: references/ directory file count matches repo source (if applicable)
|
|
66
|
+
repo_refs="${REPO_ROOT}/skills/${local_name}/references"
|
|
67
|
+
target_refs="${skill_dir}references"
|
|
68
|
+
|
|
69
|
+
if [[ -d "$repo_refs" ]]; then
|
|
70
|
+
if [[ ! -d "$target_refs" ]]; then
|
|
71
|
+
ERRORS+=("${local_name}: Missing references/ directory (repo has one)")
|
|
72
|
+
PASS_COUNT=$((PASS_COUNT - 1))
|
|
73
|
+
else
|
|
74
|
+
repo_count=$(find "$repo_refs" -type f | wc -l | tr -d ' ')
|
|
75
|
+
target_count=$(find "$target_refs" -type f | wc -l | tr -d ' ')
|
|
76
|
+
|
|
77
|
+
if [[ "$repo_count" != "$target_count" ]]; then
|
|
78
|
+
ERRORS+=("${local_name}: references/ file count mismatch (repo: ${repo_count}, installed: ${target_count})")
|
|
79
|
+
PASS_COUNT=$((PASS_COUNT - 1))
|
|
80
|
+
fi
|
|
81
|
+
fi
|
|
82
|
+
fi
|
|
83
|
+
done
|
|
84
|
+
|
|
85
|
+
if [[ $TOTAL -eq 0 ]]; then
|
|
86
|
+
echo "ERROR: No skills found in $TARGET_DIR"
|
|
87
|
+
exit 1
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
echo "=== Installation Validation: ${PASS_COUNT}/${TOTAL} skills passed ==="
|
|
91
|
+
|
|
92
|
+
if [[ ${#ERRORS[@]} -gt 0 ]]; then
|
|
93
|
+
echo ""
|
|
94
|
+
echo "Errors:"
|
|
95
|
+
for err in "${ERRORS[@]}"; do
|
|
96
|
+
echo " - $err"
|
|
97
|
+
done
|
|
98
|
+
exit 1
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
exit 0
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# validate-plugin.sh — Validate core plugin directory structure
|
|
3
|
+
#
|
|
4
|
+
# Checks:
|
|
5
|
+
# 1. .claude-plugin/plugin.json exists and valid JSON with required fields
|
|
6
|
+
# 2. Referenced directories exist: commands/, skills/
|
|
7
|
+
# 3. Referenced files exist: hooks/hooks.json, .mcp.json
|
|
8
|
+
# 4. .mcp.json is valid JSON containing exarchos and graphite server entries
|
|
9
|
+
# 5. hooks/hooks.json contains all 6 hook types
|
|
10
|
+
# 6. No {{CLI_PATH}} references in hooks (should use ${CLAUDE_PLUGIN_ROOT})
|
|
11
|
+
#
|
|
12
|
+
# Usage: validate-plugin.sh --repo-root <path>
|
|
13
|
+
#
|
|
14
|
+
# Exit codes:
|
|
15
|
+
# 0 = all checks pass
|
|
16
|
+
# 1 = one or more checks fail
|
|
17
|
+
# 2 = usage error
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
# ============================================================
|
|
22
|
+
# ARGUMENT PARSING
|
|
23
|
+
# ============================================================
|
|
24
|
+
|
|
25
|
+
REPO_ROOT="."
|
|
26
|
+
|
|
27
|
+
usage() {
|
|
28
|
+
cat << 'USAGE'
|
|
29
|
+
Usage: validate-plugin.sh --repo-root <path>
|
|
30
|
+
|
|
31
|
+
Validates the core Exarchos plugin directory structure.
|
|
32
|
+
|
|
33
|
+
Options:
|
|
34
|
+
--repo-root <path> Path to the plugin repository root (default: .)
|
|
35
|
+
--help Show this help message
|
|
36
|
+
|
|
37
|
+
Exit codes:
|
|
38
|
+
0 All checks pass
|
|
39
|
+
1 One or more checks fail
|
|
40
|
+
2 Usage error
|
|
41
|
+
USAGE
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
while [[ $# -gt 0 ]]; do
|
|
45
|
+
case "$1" in
|
|
46
|
+
--repo-root)
|
|
47
|
+
if [[ -z "${2:-}" ]]; then
|
|
48
|
+
echo "Error: --repo-root requires a path argument" >&2
|
|
49
|
+
exit 2
|
|
50
|
+
fi
|
|
51
|
+
REPO_ROOT="$2"
|
|
52
|
+
shift 2
|
|
53
|
+
;;
|
|
54
|
+
--help)
|
|
55
|
+
usage
|
|
56
|
+
exit 0
|
|
57
|
+
;;
|
|
58
|
+
*)
|
|
59
|
+
echo "Error: Unknown argument '$1'" >&2
|
|
60
|
+
usage >&2
|
|
61
|
+
exit 2
|
|
62
|
+
;;
|
|
63
|
+
esac
|
|
64
|
+
done
|
|
65
|
+
|
|
66
|
+
if [[ ! -d "$REPO_ROOT" ]]; then
|
|
67
|
+
echo "Error: Repository root not found: $REPO_ROOT" >&2
|
|
68
|
+
exit 2
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
# ============================================================
|
|
72
|
+
# DEPENDENCY CHECK
|
|
73
|
+
# ============================================================
|
|
74
|
+
|
|
75
|
+
if ! command -v jq &>/dev/null; then
|
|
76
|
+
echo "Error: jq is required but not installed" >&2
|
|
77
|
+
exit 2
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# ============================================================
|
|
81
|
+
# VALIDATION
|
|
82
|
+
# ============================================================
|
|
83
|
+
|
|
84
|
+
CHECK_PASS=0
|
|
85
|
+
CHECK_FAIL=0
|
|
86
|
+
TOTAL=0
|
|
87
|
+
RESULTS=()
|
|
88
|
+
|
|
89
|
+
check() {
|
|
90
|
+
local description="$1"
|
|
91
|
+
local passed="$2"
|
|
92
|
+
TOTAL=$((TOTAL + 1))
|
|
93
|
+
if [[ "$passed" == "true" ]]; then
|
|
94
|
+
RESULTS+=("- **PASS**: $description")
|
|
95
|
+
CHECK_PASS=$((CHECK_PASS + 1))
|
|
96
|
+
else
|
|
97
|
+
RESULTS+=("- **FAIL**: $description")
|
|
98
|
+
CHECK_FAIL=$((CHECK_FAIL + 1))
|
|
99
|
+
fi
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# --- Check 1: .claude-plugin/plugin.json exists and valid JSON with required fields ---
|
|
103
|
+
PLUGIN_JSON="$REPO_ROOT/.claude-plugin/plugin.json"
|
|
104
|
+
if [[ -f "$PLUGIN_JSON" ]] && jq empty "$PLUGIN_JSON" 2>/dev/null; then
|
|
105
|
+
# Verify required fields
|
|
106
|
+
REQUIRED_FIELDS=("name" "version" "commands" "skills" "hooks" "mcpServers")
|
|
107
|
+
ALL_FIELDS_PRESENT=true
|
|
108
|
+
MISSING_FIELDS=()
|
|
109
|
+
for field in "${REQUIRED_FIELDS[@]}"; do
|
|
110
|
+
if ! jq -e ".$field" "$PLUGIN_JSON" >/dev/null 2>&1; then
|
|
111
|
+
ALL_FIELDS_PRESENT=false
|
|
112
|
+
MISSING_FIELDS+=("$field")
|
|
113
|
+
fi
|
|
114
|
+
done
|
|
115
|
+
if [[ "$ALL_FIELDS_PRESENT" == "true" ]]; then
|
|
116
|
+
check ".claude-plugin/plugin.json exists and valid" "true"
|
|
117
|
+
else
|
|
118
|
+
check ".claude-plugin/plugin.json missing fields: ${MISSING_FIELDS[*]}" "false"
|
|
119
|
+
fi
|
|
120
|
+
else
|
|
121
|
+
check ".claude-plugin/plugin.json exists and valid" "false"
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
# --- Check 2: Referenced directories exist ---
|
|
125
|
+
COMMANDS_DIR="$REPO_ROOT/commands"
|
|
126
|
+
SKILLS_DIR="$REPO_ROOT/skills"
|
|
127
|
+
if [[ -d "$COMMANDS_DIR" ]]; then
|
|
128
|
+
check "commands/ directory exists" "true"
|
|
129
|
+
else
|
|
130
|
+
check "commands/ directory exists" "false"
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
if [[ -d "$SKILLS_DIR" ]]; then
|
|
134
|
+
check "skills/ directory exists" "true"
|
|
135
|
+
else
|
|
136
|
+
check "skills/ directory exists" "false"
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
# --- Check 3: Referenced files exist ---
|
|
140
|
+
HOOKS_FILE="$REPO_ROOT/hooks/hooks.json"
|
|
141
|
+
MCP_FILE="$REPO_ROOT/.mcp.json"
|
|
142
|
+
|
|
143
|
+
if [[ -f "$HOOKS_FILE" ]]; then
|
|
144
|
+
check "hooks/hooks.json exists" "true"
|
|
145
|
+
else
|
|
146
|
+
check "hooks/hooks.json exists" "false"
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
if [[ -f "$MCP_FILE" ]]; then
|
|
150
|
+
check ".mcp.json exists" "true"
|
|
151
|
+
else
|
|
152
|
+
check ".mcp.json exists" "false"
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
# --- Check 4: .mcp.json is valid JSON with exarchos and graphite entries ---
|
|
156
|
+
if [[ -f "$MCP_FILE" ]] && jq empty "$MCP_FILE" 2>/dev/null; then
|
|
157
|
+
HAS_EXARCHOS=$(jq -e '.mcpServers.exarchos' "$MCP_FILE" >/dev/null 2>&1 && echo "true" || echo "false")
|
|
158
|
+
HAS_GRAPHITE=$(jq -e '.mcpServers.graphite' "$MCP_FILE" >/dev/null 2>&1 && echo "true" || echo "false")
|
|
159
|
+
if [[ "$HAS_EXARCHOS" == "true" && "$HAS_GRAPHITE" == "true" ]]; then
|
|
160
|
+
check ".mcp.json contains exarchos and graphite entries" "true"
|
|
161
|
+
else
|
|
162
|
+
MISSING_SERVERS=()
|
|
163
|
+
[[ "$HAS_EXARCHOS" == "false" ]] && MISSING_SERVERS+=("exarchos")
|
|
164
|
+
[[ "$HAS_GRAPHITE" == "false" ]] && MISSING_SERVERS+=("graphite")
|
|
165
|
+
check ".mcp.json missing server entries: ${MISSING_SERVERS[*]}" "false"
|
|
166
|
+
fi
|
|
167
|
+
else
|
|
168
|
+
check ".mcp.json valid JSON with required entries" "false"
|
|
169
|
+
fi
|
|
170
|
+
|
|
171
|
+
# --- Check 5: hooks/hooks.json contains all 6 hook types ---
|
|
172
|
+
REQUIRED_HOOKS=("PreCompact" "SessionStart" "PreToolUse" "TaskCompleted" "TeammateIdle" "SubagentStart")
|
|
173
|
+
if [[ -f "$HOOKS_FILE" ]] && jq empty "$HOOKS_FILE" 2>/dev/null; then
|
|
174
|
+
ALL_HOOKS_PRESENT=true
|
|
175
|
+
MISSING_HOOKS=()
|
|
176
|
+
for hook in "${REQUIRED_HOOKS[@]}"; do
|
|
177
|
+
if ! jq -e ".hooks.$hook" "$HOOKS_FILE" >/dev/null 2>&1; then
|
|
178
|
+
ALL_HOOKS_PRESENT=false
|
|
179
|
+
MISSING_HOOKS+=("$hook")
|
|
180
|
+
fi
|
|
181
|
+
done
|
|
182
|
+
if [[ "$ALL_HOOKS_PRESENT" == "true" ]]; then
|
|
183
|
+
check "hooks/hooks.json contains all 6 hook types" "true"
|
|
184
|
+
else
|
|
185
|
+
check "hooks/hooks.json missing hook types: ${MISSING_HOOKS[*]}" "false"
|
|
186
|
+
fi
|
|
187
|
+
else
|
|
188
|
+
check "hooks/hooks.json valid JSON with all hook types" "false"
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
# --- Check 6: No {{CLI_PATH}} references in hooks ---
|
|
192
|
+
if [[ -f "$HOOKS_FILE" ]]; then
|
|
193
|
+
if grep -q '{{CLI_PATH}}' "$HOOKS_FILE" 2>/dev/null; then
|
|
194
|
+
check "No {{CLI_PATH}} references in hooks (should use \${CLAUDE_PLUGIN_ROOT})" "false"
|
|
195
|
+
else
|
|
196
|
+
check "No {{CLI_PATH}} references in hooks" "true"
|
|
197
|
+
fi
|
|
198
|
+
else
|
|
199
|
+
check "No {{CLI_PATH}} references in hooks (hooks file missing)" "false"
|
|
200
|
+
fi
|
|
201
|
+
|
|
202
|
+
# ============================================================
|
|
203
|
+
# STRUCTURED OUTPUT
|
|
204
|
+
# ============================================================
|
|
205
|
+
|
|
206
|
+
echo "## Plugin Validation Report"
|
|
207
|
+
echo ""
|
|
208
|
+
|
|
209
|
+
for result in "${RESULTS[@]}"; do
|
|
210
|
+
echo "$result"
|
|
211
|
+
done
|
|
212
|
+
|
|
213
|
+
echo ""
|
|
214
|
+
echo "---"
|
|
215
|
+
echo ""
|
|
216
|
+
|
|
217
|
+
if [[ $CHECK_FAIL -eq 0 ]]; then
|
|
218
|
+
echo "**Result: PASS** ($CHECK_PASS/$TOTAL checks passed)"
|
|
219
|
+
exit 0
|
|
220
|
+
else
|
|
221
|
+
echo "**Result: FAIL** ($CHECK_PASS/$TOTAL checks passed)"
|
|
222
|
+
exit 1
|
|
223
|
+
fi
|