@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,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: synthesis-steps
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Synthesis Process
|
|
6
|
+
|
|
7
|
+
## Step 1: Verify Readiness
|
|
8
|
+
|
|
9
|
+
Run the pre-synthesis readiness check:
|
|
10
|
+
```bash
|
|
11
|
+
scripts/pre-synthesis-check.sh \
|
|
12
|
+
--state-file ~/.claude/workflow-state/<featureId>.state.json \
|
|
13
|
+
--repo-root <repo-root>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The script validates all readiness conditions:
|
|
17
|
+
- All delegated tasks complete (from state file)
|
|
18
|
+
- All reviews passed (from state file)
|
|
19
|
+
- No outstanding fix requests (from state file)
|
|
20
|
+
- Graphite stack branches exist (`gt log`)
|
|
21
|
+
- All tests pass (`npm run test:run && npm run typecheck`)
|
|
22
|
+
|
|
23
|
+
**On exit 0:** All checks passed -- proceed to Step 2.
|
|
24
|
+
**On exit 1:** Output identifies the failing check. Return to `/exarchos:review` or `/exarchos:delegate` as appropriate.
|
|
25
|
+
|
|
26
|
+
Use `--skip-tests` if tests were already verified in review phase. Use `--skip-stack` to defer stack check to Step 2.
|
|
27
|
+
|
|
28
|
+
## Step 2: Verify and Reconstruct Graphite Stack
|
|
29
|
+
|
|
30
|
+
Run the stack reconstruction script to detect and fix any broken stack state:
|
|
31
|
+
```bash
|
|
32
|
+
scripts/reconstruct-stack.sh \
|
|
33
|
+
--repo-root <repo-root> \
|
|
34
|
+
--state-file ~/.claude/workflow-state/<featureId>.state.json
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The script has three phases:
|
|
38
|
+
1. **Detection** -- Parses `gt log` for diverged branches, restack markers, or missing task branches
|
|
39
|
+
2. **Reconstruction** -- If issues detected: resets branch pointers, removes blocking worktrees, re-tracks with correct parent chain
|
|
40
|
+
3. **Validation** -- Confirms `gt log` shows a clean stack with correct parent chain
|
|
41
|
+
|
|
42
|
+
**On exit 0:** Stack is healthy (or was successfully reconstructed) -- proceed to Step 3.
|
|
43
|
+
**On exit 1:** Reconstruction failed validation. Manual intervention required -- inspect `gt log` output and resolve conflicts.
|
|
44
|
+
|
|
45
|
+
Use `--dry-run` to preview reconstruction actions without making changes.
|
|
46
|
+
|
|
47
|
+
## Step 3: Quick Test Verification
|
|
48
|
+
|
|
49
|
+
Run tests from the top of the Graphite stack to confirm everything works:
|
|
50
|
+
```bash
|
|
51
|
+
npm run test:run
|
|
52
|
+
npm run typecheck
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
If these fail, return to `/exarchos:review` or `/exarchos:delegate` to resolve.
|
|
56
|
+
|
|
57
|
+
## Step 4: Check CodeRabbit Review State
|
|
58
|
+
|
|
59
|
+
Get PR numbers from the Graphite stack, then run the CodeRabbit review check:
|
|
60
|
+
```bash
|
|
61
|
+
# Get PR numbers from gt log
|
|
62
|
+
PR_NUMBERS=$(gt log --short | grep -o '#[0-9]*' | sed 's/#//')
|
|
63
|
+
|
|
64
|
+
# Check CodeRabbit review state
|
|
65
|
+
scripts/check-coderabbit.sh \
|
|
66
|
+
--owner <owner> --repo <repo> \
|
|
67
|
+
$PR_NUMBERS
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The script queries GitHub's PR reviews API for each PR, filters for CodeRabbit reviews, and classifies the latest review state.
|
|
71
|
+
|
|
72
|
+
**On exit 0:** All PRs are APPROVED or have no CodeRabbit review -- proceed to Step 5.
|
|
73
|
+
**On exit 1:** At least one PR has CHANGES_REQUESTED or PENDING. The output identifies which PRs need attention. Route to fix cycle:
|
|
74
|
+
```typescript
|
|
75
|
+
Skill({ skill: "exarchos:delegate", args: "--pr-fixes [PR_URL]" })
|
|
76
|
+
```
|
|
77
|
+
After fixes are applied, return to Step 4 to re-check.
|
|
78
|
+
|
|
79
|
+
## Step 5: Submit Stack to Merge Queue
|
|
80
|
+
|
|
81
|
+
Enqueue the stack for merging (PRs already exist from delegation):
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
mcp__graphite__run_gt_cmd({
|
|
85
|
+
args: ["submit", "--no-interactive", "--publish", "--merge-when-ready"],
|
|
86
|
+
cwd: "<repo-root>",
|
|
87
|
+
why: "Enqueue stacked PRs in merge queue after review gates pass"
|
|
88
|
+
})
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
After submission, use `mcp__graphite__run_gt_cmd` with `["log", "--short"]` to get the PR URLs for each stack entry.
|
|
92
|
+
|
|
93
|
+
## Step 6: Cleanup After Merge
|
|
94
|
+
|
|
95
|
+
After PRs are merged, use Graphite to clean up:
|
|
96
|
+
```
|
|
97
|
+
mcp__graphite__run_gt_cmd({
|
|
98
|
+
args: ["sync"],
|
|
99
|
+
cwd: "<repo-root>",
|
|
100
|
+
why: "Pull latest trunk, clean up merged branches"
|
|
101
|
+
})
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Then remove worktrees if they exist:
|
|
105
|
+
```bash
|
|
106
|
+
# Remove worktrees used during delegation
|
|
107
|
+
git worktree list | grep ".worktrees/" | awk '{print $1}' | xargs -I{} git worktree remove {}
|
|
108
|
+
git worktree prune
|
|
109
|
+
```
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Troubleshooting
|
|
2
|
+
|
|
3
|
+
## Handling Failures
|
|
4
|
+
|
|
5
|
+
### Test Failure (Unexpected)
|
|
6
|
+
|
|
7
|
+
If tests fail during synthesis (they passed in review):
|
|
8
|
+
|
|
9
|
+
1. Return to review phase to investigate
|
|
10
|
+
2. Re-run `/exarchos:review` to diagnose
|
|
11
|
+
3. Dispatch fixes via `/exarchos:delegate --fixes`
|
|
12
|
+
4. Return to synthesis after review passes
|
|
13
|
+
|
|
14
|
+
### PR Checks Fail
|
|
15
|
+
|
|
16
|
+
1. Wait for CI feedback
|
|
17
|
+
2. Create fix task for failures
|
|
18
|
+
3. Push fixes to the stack branches
|
|
19
|
+
4. Re-run synthesis verification
|
|
20
|
+
|
|
21
|
+
### Merge Queue Rejection
|
|
22
|
+
|
|
23
|
+
If the merge queue rejects a PR:
|
|
24
|
+
1. Check CI status via `gh pr checks <number>` (or GitHub MCP `pull_request_read` with method `get_status` if available)
|
|
25
|
+
2. Fix failing checks
|
|
26
|
+
3. Push fixes and re-enqueue
|
|
27
|
+
|
|
28
|
+
## Handling PR Feedback
|
|
29
|
+
|
|
30
|
+
If the user receives PR review comments:
|
|
31
|
+
|
|
32
|
+
1. Offer to address feedback:
|
|
33
|
+
```typescript
|
|
34
|
+
Skill({ skill: "exarchos:delegate", args: "--pr-fixes [PR_URL]" })
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
2. Delegate reads PR comments via gh CLI:
|
|
38
|
+
```bash
|
|
39
|
+
gh pr view <number> --json reviews,comments
|
|
40
|
+
```
|
|
41
|
+
> Or use GitHub MCP `pull_request_read` if available.
|
|
42
|
+
|
|
43
|
+
3. Creates fix tasks from review comments
|
|
44
|
+
4. After fixes, amend the stack with `mcp__graphite__run_gt_cmd` using `["modify", "-m", "fix: <description>"]` and resubmit with `["submit", "--no-interactive", "--publish", "--merge-when-ready"]`
|
|
45
|
+
5. Return to merge confirmation
|
|
46
|
+
|
|
47
|
+
## Final Report Template
|
|
48
|
+
|
|
49
|
+
```markdown
|
|
50
|
+
## Synthesis Complete
|
|
51
|
+
|
|
52
|
+
### Pull Requests
|
|
53
|
+
[PR URLs from gt log --short]
|
|
54
|
+
|
|
55
|
+
### Stack Branches
|
|
56
|
+
- task/001-types
|
|
57
|
+
- task/002-api
|
|
58
|
+
- task/003-tests
|
|
59
|
+
|
|
60
|
+
### Test Results
|
|
61
|
+
- Unit tests: PASS
|
|
62
|
+
- Type check: PASS
|
|
63
|
+
- Lint: PASS
|
|
64
|
+
- Build: PASS
|
|
65
|
+
|
|
66
|
+
### Next Steps
|
|
67
|
+
1. Wait for CI/CD checks
|
|
68
|
+
2. Request code review (if required)
|
|
69
|
+
3. Merge when approved
|
|
70
|
+
4. Worktrees will be cleaned up after merge
|
|
71
|
+
|
|
72
|
+
### Documentation
|
|
73
|
+
- Design: docs/designs/YYYY-MM-DD-feature.md
|
|
74
|
+
- Plan: docs/plans/YYYY-MM-DD-feature.md
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## MCP Tool Call Failed
|
|
78
|
+
If an Exarchos MCP tool returns an error:
|
|
79
|
+
1. Check the error message -- it usually contains specific guidance
|
|
80
|
+
2. Verify the workflow state exists: call `mcp__exarchos__exarchos_workflow` with `action: "get"` and the featureId
|
|
81
|
+
3. If "version mismatch": another process updated state -- retry the operation
|
|
82
|
+
4. If state is corrupted: call `mcp__exarchos__exarchos_workflow` with `action: "cancel"` and `dryRun: true`
|
|
83
|
+
|
|
84
|
+
## State Desync
|
|
85
|
+
If workflow state doesn't match git reality:
|
|
86
|
+
1. The SessionStart hook runs reconciliation automatically on resume
|
|
87
|
+
2. If manual check needed: compare state file with `git log` and branch state
|
|
88
|
+
3. Update state via `mcp__exarchos__exarchos_workflow` with `action: "set"` to match git truth
|
|
89
|
+
|
|
90
|
+
## PR Creation Failed
|
|
91
|
+
If `gt submit` fails:
|
|
92
|
+
1. Check the error output for specific guidance
|
|
93
|
+
2. Run `gt log` to verify the stack state
|
|
94
|
+
3. If rebase conflict: run `gt restack` to resolve
|
|
95
|
+
4. If authentication issue: check GitHub token permissions
|
|
96
|
+
|
|
97
|
+
## Stack Rebase Conflict
|
|
98
|
+
If `gt restack` encounters conflicts:
|
|
99
|
+
1. Resolve conflicts manually in each affected file
|
|
100
|
+
2. Run `git add <resolved-files>` then `gt continue`
|
|
101
|
+
3. After resolution, re-run `gt submit --no-interactive --publish --merge-when-ready`
|
|
102
|
+
|
|
103
|
+
## Exarchos Integration
|
|
104
|
+
|
|
105
|
+
When Exarchos MCP tools are available:
|
|
106
|
+
|
|
107
|
+
1. **After stack submission:** Call `mcp__exarchos__exarchos_event` with `action: "append"` with event type `stack.enqueued` including PR numbers from `gt log --short`
|
|
108
|
+
2. **Monitor merge status:** Use `mcp__graphite__run_gt_cmd` with `["log", "--short"]` to check stack/PR status
|
|
109
|
+
3. **On successful merge:** Call `mcp__exarchos__exarchos_event` with `action: "append"` with event type `phase.transitioned` to mark workflow complete
|
|
110
|
+
|
|
111
|
+
## Performance Notes
|
|
112
|
+
|
|
113
|
+
- Complete each step fully before advancing -- quality over speed
|
|
114
|
+
- Do not skip validation checks even when the change appears trivial
|
|
115
|
+
- Verify all tests pass before creating PR. Do not skip the pre-submit validation step.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# validate-all-skills.sh — Run frontmatter validation on all SKILL.md files
|
|
3
|
+
#
|
|
4
|
+
# Usage: bash skills/validate-all-skills.sh
|
|
5
|
+
# Must be run from the repository root, or provide SKILLS_DIR env var.
|
|
6
|
+
#
|
|
7
|
+
# Iterates over skills/*/SKILL.md (excluding test-fixtures and shared),
|
|
8
|
+
# extracts folder name, and calls validate-frontmatter.sh for each.
|
|
9
|
+
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
|
+
VALIDATOR="${SCRIPT_DIR}/validate-frontmatter.sh"
|
|
14
|
+
SKILLS_DIR="${SCRIPT_DIR}"
|
|
15
|
+
shopt -s nullglob
|
|
16
|
+
|
|
17
|
+
# Colors
|
|
18
|
+
GREEN='\033[0;32m'
|
|
19
|
+
RED='\033[0;31m'
|
|
20
|
+
RESET='\033[0m'
|
|
21
|
+
|
|
22
|
+
PASS_COUNT=0
|
|
23
|
+
FAIL_COUNT=0
|
|
24
|
+
TOTAL=0
|
|
25
|
+
|
|
26
|
+
for skill_file in "${SKILLS_DIR}"/*/SKILL.md; do
|
|
27
|
+
folder_path=$(dirname "$skill_file")
|
|
28
|
+
folder_name=$(basename "$folder_path")
|
|
29
|
+
|
|
30
|
+
# Skip test fixtures and shared
|
|
31
|
+
if [[ "$folder_name" == "test-fixtures" || "$folder_name" == "shared" ]]; then
|
|
32
|
+
continue
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
TOTAL=$((TOTAL + 1))
|
|
36
|
+
|
|
37
|
+
validation_output=""
|
|
38
|
+
validation_output=$("$VALIDATOR" "$skill_file" "$folder_name" 2>&1) && validation_exit=0 || validation_exit=$?
|
|
39
|
+
|
|
40
|
+
if [[ "$validation_exit" -eq 0 ]]; then
|
|
41
|
+
PASS_COUNT=$((PASS_COUNT + 1))
|
|
42
|
+
printf "%b" "Validating $(printf '%-30s' "${folder_name}...") ${GREEN}PASS${RESET}\n"
|
|
43
|
+
else
|
|
44
|
+
FAIL_COUNT=$((FAIL_COUNT + 1))
|
|
45
|
+
# Extract the first error for the summary line
|
|
46
|
+
first_error=$(echo "$validation_output" | head -n 1 | sed 's/^ERROR: //')
|
|
47
|
+
printf "%b" "Validating $(printf '%-30s' "${folder_name}...") ${RED}FAIL${RESET} (${first_error})\n"
|
|
48
|
+
fi
|
|
49
|
+
done
|
|
50
|
+
|
|
51
|
+
echo ""
|
|
52
|
+
echo "=== Results: ${PASS_COUNT}/${TOTAL} skills passed ==="
|
|
53
|
+
|
|
54
|
+
if [[ "$FAIL_COUNT" -gt 0 ]]; then
|
|
55
|
+
exit 1
|
|
56
|
+
fi
|
|
57
|
+
exit 0
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# validate-frontmatter.sh — Validate YAML frontmatter in SKILL.md files
|
|
3
|
+
#
|
|
4
|
+
# Usage: validate-frontmatter.sh <path-to-SKILL.md> <expected-folder-name>
|
|
5
|
+
#
|
|
6
|
+
# Runs all checks, collects errors, exits 0 if clean or 1 if any errors found.
|
|
7
|
+
# Designed for macOS compatibility (no GNU-specific tools).
|
|
8
|
+
|
|
9
|
+
set -euo pipefail
|
|
10
|
+
|
|
11
|
+
SKILL_FILE="${1:-}"
|
|
12
|
+
EXPECTED_FOLDER="${2:-}"
|
|
13
|
+
|
|
14
|
+
if [[ -z "$SKILL_FILE" || -z "$EXPECTED_FOLDER" ]]; then
|
|
15
|
+
echo "Usage: validate-frontmatter.sh <path-to-SKILL.md> <expected-folder-name>"
|
|
16
|
+
exit 2
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
if [[ ! -f "$SKILL_FILE" ]]; then
|
|
20
|
+
echo "ERROR: File not found: $SKILL_FILE"
|
|
21
|
+
exit 2
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
ERRORS=()
|
|
25
|
+
FRONTMATTER=""
|
|
26
|
+
BODY=""
|
|
27
|
+
|
|
28
|
+
# ---------------------------------------------------------------------------
|
|
29
|
+
# check_frontmatter_exists
|
|
30
|
+
# Verify file starts with --- and has a closing ---.
|
|
31
|
+
# Extract frontmatter between delimiters and body after closing delimiter.
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
check_frontmatter_exists() {
|
|
34
|
+
local first_line
|
|
35
|
+
first_line=$(head -n 1 "$SKILL_FILE")
|
|
36
|
+
|
|
37
|
+
if [[ "$first_line" != "---" ]]; then
|
|
38
|
+
ERRORS+=("check_frontmatter_exists: Missing opening --- delimiter")
|
|
39
|
+
return
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# Find the closing --- (second occurrence, so line number > 1)
|
|
43
|
+
local closing_line=0
|
|
44
|
+
local line_num=0
|
|
45
|
+
while IFS= read -r line; do
|
|
46
|
+
line_num=$((line_num + 1))
|
|
47
|
+
if [[ "$line_num" -gt 1 && "$line" == "---" ]]; then
|
|
48
|
+
closing_line=$line_num
|
|
49
|
+
break
|
|
50
|
+
fi
|
|
51
|
+
done < "$SKILL_FILE"
|
|
52
|
+
|
|
53
|
+
if [[ "$closing_line" -eq 0 ]]; then
|
|
54
|
+
ERRORS+=("check_frontmatter_exists: Missing closing --- delimiter")
|
|
55
|
+
return
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# Extract frontmatter (between line 2 and closing_line - 1)
|
|
59
|
+
local fm_start=2
|
|
60
|
+
local fm_end=$((closing_line - 1))
|
|
61
|
+
FRONTMATTER=$(sed -n "${fm_start},${fm_end}p" "$SKILL_FILE")
|
|
62
|
+
|
|
63
|
+
# Extract body (everything after closing delimiter)
|
|
64
|
+
local body_start=$((closing_line + 1))
|
|
65
|
+
local total_lines
|
|
66
|
+
total_lines=$(awk 'END {print NR}' "$SKILL_FILE")
|
|
67
|
+
if [[ "$body_start" -le "$total_lines" ]]; then
|
|
68
|
+
BODY=$(tail -n +"$body_start" "$SKILL_FILE")
|
|
69
|
+
else
|
|
70
|
+
BODY=""
|
|
71
|
+
fi
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# ---------------------------------------------------------------------------
|
|
75
|
+
# check_required_fields
|
|
76
|
+
# Verify name: and description: are present in the frontmatter.
|
|
77
|
+
# ---------------------------------------------------------------------------
|
|
78
|
+
check_required_fields() {
|
|
79
|
+
if [[ -z "$FRONTMATTER" ]]; then
|
|
80
|
+
return
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
if ! echo "$FRONTMATTER" | grep -q '^name:'; then
|
|
84
|
+
ERRORS+=("check_required_fields: Missing required field 'name'")
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
if ! echo "$FRONTMATTER" | grep -q '^description:'; then
|
|
88
|
+
ERRORS+=("check_required_fields: Missing required field 'description'")
|
|
89
|
+
fi
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# ---------------------------------------------------------------------------
|
|
93
|
+
# check_name_matches_folder
|
|
94
|
+
# Verify the name: value matches the expected folder name (kebab-case).
|
|
95
|
+
# ---------------------------------------------------------------------------
|
|
96
|
+
check_name_matches_folder() {
|
|
97
|
+
if [[ -z "$FRONTMATTER" ]]; then
|
|
98
|
+
return
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
local name_value
|
|
102
|
+
name_value=$(echo "$FRONTMATTER" | grep '^name:' | sed 's/^name:[[:space:]]*//' | tr -d '"' | tr -d "'" || true)
|
|
103
|
+
|
|
104
|
+
if [[ -z "$name_value" ]]; then
|
|
105
|
+
return # Already caught by check_required_fields
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
if [[ "$name_value" != "$EXPECTED_FOLDER" ]]; then
|
|
109
|
+
ERRORS+=("check_name_matches_folder: Name '${name_value}' does not match folder '${EXPECTED_FOLDER}'")
|
|
110
|
+
fi
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
# ---------------------------------------------------------------------------
|
|
114
|
+
# check_no_xml_tags
|
|
115
|
+
# Verify the frontmatter section contains no < or > characters.
|
|
116
|
+
# ---------------------------------------------------------------------------
|
|
117
|
+
check_no_xml_tags() {
|
|
118
|
+
if [[ -z "$FRONTMATTER" ]]; then
|
|
119
|
+
return
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
if echo "$FRONTMATTER" | grep -q '[<>]'; then
|
|
123
|
+
ERRORS+=("check_no_xml_tags: Frontmatter contains '<' or '>' characters (breaks Claude Code parsing)")
|
|
124
|
+
fi
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
# ---------------------------------------------------------------------------
|
|
128
|
+
# check_word_count
|
|
129
|
+
# Count words in the body and verify <= 2000 words.
|
|
130
|
+
# ---------------------------------------------------------------------------
|
|
131
|
+
check_word_count() {
|
|
132
|
+
if [[ -z "$BODY" ]]; then
|
|
133
|
+
return
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
local word_count
|
|
137
|
+
word_count=$(echo "$BODY" | wc -w | tr -d ' ')
|
|
138
|
+
|
|
139
|
+
if [[ "$word_count" -gt 2000 ]]; then
|
|
140
|
+
ERRORS+=("check_word_count: Body has ${word_count} words (limit: 2000)")
|
|
141
|
+
fi
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
# ---------------------------------------------------------------------------
|
|
145
|
+
# check_references_exist
|
|
146
|
+
# Find all references/*.md patterns in the body and verify each file exists
|
|
147
|
+
# relative to the SKILL.md's parent directory.
|
|
148
|
+
# ---------------------------------------------------------------------------
|
|
149
|
+
check_references_exist() {
|
|
150
|
+
if [[ -z "$BODY" ]]; then
|
|
151
|
+
return
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
local skill_dir
|
|
155
|
+
skill_dir=$(dirname "$SKILL_FILE")
|
|
156
|
+
|
|
157
|
+
# Find all references/*.md patterns in the body
|
|
158
|
+
local refs
|
|
159
|
+
refs=$(echo "$BODY" | grep -oE 'references/[^)[:space:]"]+\.md' || true)
|
|
160
|
+
|
|
161
|
+
if [[ -z "$refs" ]]; then
|
|
162
|
+
return
|
|
163
|
+
fi
|
|
164
|
+
|
|
165
|
+
while IFS= read -r ref; do
|
|
166
|
+
if [[ ! -f "${skill_dir}/${ref}" ]]; then
|
|
167
|
+
ERRORS+=("check_references_exist: Referenced file '${ref}' not found relative to $(basename "$skill_dir")/")
|
|
168
|
+
fi
|
|
169
|
+
done <<< "$refs"
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
# ---------------------------------------------------------------------------
|
|
173
|
+
# check_negative_trigger
|
|
174
|
+
# Verify the description contains a negative trigger phrase
|
|
175
|
+
# (e.g., "Do NOT" or "Not for") to help Claude Code disambiguate skills.
|
|
176
|
+
# ---------------------------------------------------------------------------
|
|
177
|
+
check_negative_trigger() {
|
|
178
|
+
if [[ -z "$FRONTMATTER" ]]; then
|
|
179
|
+
return
|
|
180
|
+
fi
|
|
181
|
+
|
|
182
|
+
local desc_value
|
|
183
|
+
desc_value=$(echo "$FRONTMATTER" | grep '^description:' | sed 's/^description:[[:space:]]*//' | tr -d '"' | tr -d "'" || true)
|
|
184
|
+
|
|
185
|
+
if [[ -z "$desc_value" ]]; then
|
|
186
|
+
return # Already caught by check_required_fields
|
|
187
|
+
fi
|
|
188
|
+
|
|
189
|
+
if ! echo "$desc_value" | grep -qi 'Do NOT\|Not for'; then
|
|
190
|
+
ERRORS+=("check_negative_trigger: Description lacks a negative trigger phrase (e.g., 'Do NOT ...' or 'Not for ...')")
|
|
191
|
+
fi
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
# ---------------------------------------------------------------------------
|
|
195
|
+
# check_no_orphan_references
|
|
196
|
+
# Verify every .md file in references/ is actually referenced in the body.
|
|
197
|
+
# Only checks .md files (not .sh, .json, etc.).
|
|
198
|
+
# ---------------------------------------------------------------------------
|
|
199
|
+
check_no_orphan_references() {
|
|
200
|
+
local skill_dir
|
|
201
|
+
skill_dir=$(dirname "$SKILL_FILE")
|
|
202
|
+
local refs_dir="${skill_dir}/references"
|
|
203
|
+
|
|
204
|
+
if [[ ! -d "$refs_dir" ]]; then
|
|
205
|
+
return # No references directory — nothing to check
|
|
206
|
+
fi
|
|
207
|
+
|
|
208
|
+
for ref_file in "$refs_dir"/*.md; do
|
|
209
|
+
[[ -f "$ref_file" ]] || continue
|
|
210
|
+
local ref_name="references/$(basename "$ref_file")"
|
|
211
|
+
if ! echo "$BODY" | grep -qF "$ref_name"; then
|
|
212
|
+
ERRORS+=("check_no_orphan_references: File '${ref_name}' exists but is not referenced in SKILL.md")
|
|
213
|
+
fi
|
|
214
|
+
done
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
# ---------------------------------------------------------------------------
|
|
218
|
+
# Main — Run all checks, report errors, exit appropriately
|
|
219
|
+
# ---------------------------------------------------------------------------
|
|
220
|
+
|
|
221
|
+
check_frontmatter_exists
|
|
222
|
+
check_required_fields
|
|
223
|
+
check_name_matches_folder
|
|
224
|
+
check_no_xml_tags
|
|
225
|
+
check_word_count
|
|
226
|
+
check_references_exist
|
|
227
|
+
check_negative_trigger
|
|
228
|
+
check_no_orphan_references
|
|
229
|
+
|
|
230
|
+
if [[ ${#ERRORS[@]} -gt 0 ]]; then
|
|
231
|
+
for err in "${ERRORS[@]}"; do
|
|
232
|
+
echo "ERROR: $err"
|
|
233
|
+
done
|
|
234
|
+
exit 1
|
|
235
|
+
fi
|
|
236
|
+
|
|
237
|
+
exit 0
|