autonomous-coding-toolkit 1.0.0
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 +13 -0
- package/LICENSE +21 -0
- package/Makefile +21 -0
- package/README.md +140 -0
- package/SECURITY.md +28 -0
- package/agents/bash-expert.md +113 -0
- package/agents/dependency-auditor.md +138 -0
- package/agents/integration-tester.md +120 -0
- package/agents/lesson-scanner.md +149 -0
- package/agents/python-expert.md +179 -0
- package/agents/service-monitor.md +141 -0
- package/agents/shell-expert.md +147 -0
- package/benchmarks/runner.sh +147 -0
- package/benchmarks/tasks/01-rest-endpoint/rubric.sh +29 -0
- package/benchmarks/tasks/01-rest-endpoint/task.md +17 -0
- package/benchmarks/tasks/02-refactor-module/task.md +8 -0
- package/benchmarks/tasks/03-fix-integration-bug/task.md +8 -0
- package/benchmarks/tasks/04-add-test-coverage/task.md +8 -0
- package/benchmarks/tasks/05-multi-file-feature/task.md +8 -0
- package/bin/act.js +238 -0
- package/commands/autocode.md +6 -0
- package/commands/cancel-ralph.md +18 -0
- package/commands/code-factory.md +53 -0
- package/commands/create-prd.md +55 -0
- package/commands/ralph-loop.md +18 -0
- package/commands/run-plan.md +117 -0
- package/commands/submit-lesson.md +122 -0
- package/docs/ARCHITECTURE.md +630 -0
- package/docs/CONTRIBUTING.md +125 -0
- package/docs/lessons/0001-bare-exception-swallowing.md +34 -0
- package/docs/lessons/0002-async-def-without-await.md +28 -0
- package/docs/lessons/0003-create-task-without-callback.md +28 -0
- package/docs/lessons/0004-hardcoded-test-counts.md +28 -0
- package/docs/lessons/0005-sqlite-without-closing.md +33 -0
- package/docs/lessons/0006-venv-pip-path.md +27 -0
- package/docs/lessons/0007-runner-state-self-rejection.md +35 -0
- package/docs/lessons/0008-quality-gate-blind-spot.md +33 -0
- package/docs/lessons/0009-parser-overcount-empty-batches.md +36 -0
- package/docs/lessons/0010-local-outside-function-bash.md +33 -0
- package/docs/lessons/0011-batch-tests-for-unimplemented-code.md +36 -0
- package/docs/lessons/0012-api-markdown-unescaped-chars.md +33 -0
- package/docs/lessons/0013-export-prefix-env-parsing.md +33 -0
- package/docs/lessons/0014-decorator-registry-import-side-effect.md +43 -0
- package/docs/lessons/0015-frontend-backend-schema-drift.md +43 -0
- package/docs/lessons/0016-event-driven-cold-start-seeding.md +44 -0
- package/docs/lessons/0017-copy-paste-logic-diverges.md +43 -0
- package/docs/lessons/0018-layer-passes-pipeline-broken.md +45 -0
- package/docs/lessons/0019-systemd-envfile-ignores-export.md +41 -0
- package/docs/lessons/0020-persist-state-incrementally.md +44 -0
- package/docs/lessons/0021-dual-axis-testing.md +48 -0
- package/docs/lessons/0022-jsx-factory-shadowing.md +43 -0
- package/docs/lessons/0023-static-analysis-spiral.md +51 -0
- package/docs/lessons/0024-shared-pipeline-implementation.md +55 -0
- package/docs/lessons/0025-defense-in-depth-all-entry-points.md +65 -0
- package/docs/lessons/0026-linter-no-rules-false-enforcement.md +54 -0
- package/docs/lessons/0027-jsx-silent-prop-drop.md +64 -0
- package/docs/lessons/0028-no-infrastructure-in-client-code.md +49 -0
- package/docs/lessons/0029-never-write-secrets-to-files.md +61 -0
- package/docs/lessons/0030-cache-merge-not-replace.md +62 -0
- package/docs/lessons/0031-verify-units-at-boundaries.md +66 -0
- package/docs/lessons/0032-module-lifecycle-subscribe-unsubscribe.md +89 -0
- package/docs/lessons/0033-async-iteration-mutable-snapshot.md +72 -0
- package/docs/lessons/0034-caller-missing-await-silent-discard.md +65 -0
- package/docs/lessons/0035-duplicate-registration-silent-overwrite.md +85 -0
- package/docs/lessons/0036-websocket-dirty-disconnect.md +33 -0
- package/docs/lessons/0037-parallel-agents-worktree-corruption.md +31 -0
- package/docs/lessons/0038-subscribe-no-stored-ref.md +36 -0
- package/docs/lessons/0039-fallback-or-default-hides-bugs.md +34 -0
- package/docs/lessons/0040-event-firehose-filter-first.md +36 -0
- package/docs/lessons/0041-ambiguous-base-dir-path-nesting.md +32 -0
- package/docs/lessons/0042-spec-compliance-insufficient.md +36 -0
- package/docs/lessons/0043-exact-count-extensible-collections.md +32 -0
- package/docs/lessons/0044-relative-file-deps-worktree.md +39 -0
- package/docs/lessons/0045-iterative-design-improvement.md +33 -0
- package/docs/lessons/0046-plan-assertion-math-bugs.md +38 -0
- package/docs/lessons/0047-pytest-single-threaded-default.md +37 -0
- package/docs/lessons/0048-integration-wiring-batch.md +40 -0
- package/docs/lessons/0049-ab-verification.md +41 -0
- package/docs/lessons/0050-editing-sourced-files-during-execution.md +33 -0
- package/docs/lessons/0051-infrastructure-fixes-cant-self-heal.md +30 -0
- package/docs/lessons/0052-uncommitted-changes-poison-quality-gates.md +31 -0
- package/docs/lessons/0053-jq-compact-flag-inconsistency.md +31 -0
- package/docs/lessons/0054-parser-matches-inside-code-blocks.md +30 -0
- package/docs/lessons/0055-agents-compensate-for-garbled-prompts.md +31 -0
- package/docs/lessons/0056-grep-count-exit-code-on-zero.md +42 -0
- package/docs/lessons/0057-new-artifacts-break-git-clean-gates.md +42 -0
- package/docs/lessons/0058-dead-config-keys-never-consumed.md +49 -0
- package/docs/lessons/0059-contract-test-shared-structures.md +53 -0
- package/docs/lessons/0060-set-e-silent-death-in-runners.md +53 -0
- package/docs/lessons/0061-context-injection-dirty-state.md +50 -0
- package/docs/lessons/0062-sibling-bug-neighborhood-scan.md +29 -0
- package/docs/lessons/0063-one-flag-two-lifetimes.md +31 -0
- package/docs/lessons/0064-test-passes-wrong-reason.md +31 -0
- package/docs/lessons/0065-pipefail-grep-count-double-output.md +39 -0
- package/docs/lessons/0066-local-keyword-outside-function.md +37 -0
- package/docs/lessons/0067-stdin-hang-non-interactive-shell.md +36 -0
- package/docs/lessons/0068-agent-builds-wrong-thing-correctly.md +31 -0
- package/docs/lessons/0069-plan-quality-dominates-execution.md +30 -0
- package/docs/lessons/0070-spec-echo-back-prevents-drift.md +31 -0
- package/docs/lessons/0071-positive-instructions-outperform-negative.md +30 -0
- package/docs/lessons/0072-lost-in-the-middle-context-placement.md +30 -0
- package/docs/lessons/0073-unscoped-lessons-cause-false-positives.md +30 -0
- package/docs/lessons/0074-stale-context-injection-wrong-batch.md +32 -0
- package/docs/lessons/0075-research-artifacts-must-persist.md +32 -0
- package/docs/lessons/0076-wrong-decomposition-contaminates-downstream.md +30 -0
- package/docs/lessons/0077-cherry-pick-merges-need-manual-resolution.md +30 -0
- package/docs/lessons/0078-static-review-without-live-test.md +30 -0
- package/docs/lessons/0079-integration-wiring-batch-required.md +32 -0
- package/docs/lessons/FRAMEWORK.md +161 -0
- package/docs/lessons/SUMMARY.md +201 -0
- package/docs/lessons/TEMPLATE.md +85 -0
- package/docs/plans/2026-02-21-code-factory-v2-design.md +204 -0
- package/docs/plans/2026-02-21-code-factory-v2-implementation-plan.md +2189 -0
- package/docs/plans/2026-02-21-code-factory-v2-phase4-design.md +537 -0
- package/docs/plans/2026-02-21-code-factory-v2-phase4-implementation-plan.md +2012 -0
- package/docs/plans/2026-02-21-hardening-pass-design.md +108 -0
- package/docs/plans/2026-02-21-hardening-pass-plan.md +1378 -0
- package/docs/plans/2026-02-21-mab-research-report.md +406 -0
- package/docs/plans/2026-02-21-marketplace-restructure-design.md +240 -0
- package/docs/plans/2026-02-21-marketplace-restructure-plan.md +832 -0
- package/docs/plans/2026-02-21-phase4-completion-plan.md +697 -0
- package/docs/plans/2026-02-21-validator-suite-design.md +148 -0
- package/docs/plans/2026-02-21-validator-suite-plan.md +540 -0
- package/docs/plans/2026-02-22-mab-research-round2.md +556 -0
- package/docs/plans/2026-02-22-mab-run-design.md +462 -0
- package/docs/plans/2026-02-22-mab-run-plan.md +2046 -0
- package/docs/plans/2026-02-22-operations-design-methodology-research.md +681 -0
- package/docs/plans/2026-02-22-research-agent-failure-taxonomy.md +532 -0
- package/docs/plans/2026-02-22-research-code-guideline-policies.md +886 -0
- package/docs/plans/2026-02-22-research-codebase-audit-refactoring.md +908 -0
- package/docs/plans/2026-02-22-research-coding-standards-documentation.md +541 -0
- package/docs/plans/2026-02-22-research-competitive-landscape.md +687 -0
- package/docs/plans/2026-02-22-research-comprehensive-testing.md +1076 -0
- package/docs/plans/2026-02-22-research-context-utilization.md +459 -0
- package/docs/plans/2026-02-22-research-cost-quality-tradeoff.md +548 -0
- package/docs/plans/2026-02-22-research-lesson-transferability.md +508 -0
- package/docs/plans/2026-02-22-research-multi-agent-coordination.md +312 -0
- package/docs/plans/2026-02-22-research-phase-integration.md +602 -0
- package/docs/plans/2026-02-22-research-plan-quality.md +428 -0
- package/docs/plans/2026-02-22-research-prompt-engineering.md +558 -0
- package/docs/plans/2026-02-22-research-unconventional-perspectives.md +528 -0
- package/docs/plans/2026-02-22-research-user-adoption.md +638 -0
- package/docs/plans/2026-02-22-research-verification-effectiveness.md +433 -0
- package/docs/plans/2026-02-23-agent-suite-design.md +299 -0
- package/docs/plans/2026-02-23-agent-suite-plan.md +578 -0
- package/docs/plans/2026-02-23-phase3-cost-infrastructure-design.md +148 -0
- package/docs/plans/2026-02-23-phase3-cost-infrastructure-plan.md +1062 -0
- package/docs/plans/2026-02-23-research-bash-expert-agent.md +543 -0
- package/docs/plans/2026-02-23-research-dependency-auditor-agent.md +564 -0
- package/docs/plans/2026-02-23-research-improving-existing-agents.md +503 -0
- package/docs/plans/2026-02-23-research-integration-tester-agent.md +454 -0
- package/docs/plans/2026-02-23-research-python-expert-agent.md +429 -0
- package/docs/plans/2026-02-23-research-service-monitor-agent.md +425 -0
- package/docs/plans/2026-02-23-research-shell-expert-agent.md +533 -0
- package/docs/plans/2026-02-23-roadmap-to-completion.md +530 -0
- package/docs/plans/2026-02-24-headless-module-split-design.md +98 -0
- package/docs/plans/2026-02-24-headless-module-split.md +443 -0
- package/docs/plans/2026-02-24-lesson-scope-metadata-design.md +228 -0
- package/docs/plans/2026-02-24-lesson-scope-metadata-plan.md +968 -0
- package/docs/plans/2026-02-24-npm-packaging-design.md +841 -0
- package/docs/plans/2026-02-24-npm-packaging-plan.md +1965 -0
- package/docs/plans/audit-findings.md +186 -0
- package/docs/telegram-notification-format.md +98 -0
- package/examples/example-plan.md +51 -0
- package/examples/example-prd.json +72 -0
- package/examples/example-roadmap.md +33 -0
- package/examples/quickstart-plan.md +63 -0
- package/hooks/hooks.json +26 -0
- package/hooks/setup-symlinks.sh +48 -0
- package/hooks/stop-hook.sh +135 -0
- package/package.json +47 -0
- package/policies/bash.md +71 -0
- package/policies/python.md +71 -0
- package/policies/testing.md +61 -0
- package/policies/universal.md +60 -0
- package/scripts/analyze-report.sh +97 -0
- package/scripts/architecture-map.sh +145 -0
- package/scripts/auto-compound.sh +273 -0
- package/scripts/batch-audit.sh +42 -0
- package/scripts/batch-test.sh +101 -0
- package/scripts/entropy-audit.sh +221 -0
- package/scripts/failure-digest.sh +51 -0
- package/scripts/generate-ast-rules.sh +96 -0
- package/scripts/init.sh +112 -0
- package/scripts/lesson-check.sh +428 -0
- package/scripts/lib/common.sh +61 -0
- package/scripts/lib/cost-tracking.sh +153 -0
- package/scripts/lib/ollama.sh +60 -0
- package/scripts/lib/progress-writer.sh +128 -0
- package/scripts/lib/run-plan-context.sh +215 -0
- package/scripts/lib/run-plan-echo-back.sh +231 -0
- package/scripts/lib/run-plan-headless.sh +396 -0
- package/scripts/lib/run-plan-notify.sh +57 -0
- package/scripts/lib/run-plan-parser.sh +81 -0
- package/scripts/lib/run-plan-prompt.sh +215 -0
- package/scripts/lib/run-plan-quality-gate.sh +132 -0
- package/scripts/lib/run-plan-routing.sh +315 -0
- package/scripts/lib/run-plan-sampling.sh +170 -0
- package/scripts/lib/run-plan-scoring.sh +146 -0
- package/scripts/lib/run-plan-state.sh +142 -0
- package/scripts/lib/run-plan-team.sh +199 -0
- package/scripts/lib/telegram.sh +54 -0
- package/scripts/lib/thompson-sampling.sh +176 -0
- package/scripts/license-check.sh +74 -0
- package/scripts/mab-run.sh +575 -0
- package/scripts/module-size-check.sh +146 -0
- package/scripts/patterns/async-no-await.yml +5 -0
- package/scripts/patterns/bare-except.yml +6 -0
- package/scripts/patterns/empty-catch.yml +6 -0
- package/scripts/patterns/hardcoded-localhost.yml +9 -0
- package/scripts/patterns/retry-loop-no-backoff.yml +12 -0
- package/scripts/pipeline-status.sh +197 -0
- package/scripts/policy-check.sh +226 -0
- package/scripts/prior-art-search.sh +133 -0
- package/scripts/promote-mab-lessons.sh +126 -0
- package/scripts/prompts/agent-a-superpowers.md +29 -0
- package/scripts/prompts/agent-b-ralph.md +29 -0
- package/scripts/prompts/judge-agent.md +61 -0
- package/scripts/prompts/planner-agent.md +44 -0
- package/scripts/pull-community-lessons.sh +90 -0
- package/scripts/quality-gate.sh +266 -0
- package/scripts/research-gate.sh +90 -0
- package/scripts/run-plan.sh +329 -0
- package/scripts/scope-infer.sh +159 -0
- package/scripts/setup-ralph-loop.sh +155 -0
- package/scripts/telemetry.sh +230 -0
- package/scripts/tests/run-all-tests.sh +52 -0
- package/scripts/tests/test-act-cli.sh +46 -0
- package/scripts/tests/test-agents-md.sh +87 -0
- package/scripts/tests/test-analyze-report.sh +114 -0
- package/scripts/tests/test-architecture-map.sh +89 -0
- package/scripts/tests/test-auto-compound.sh +169 -0
- package/scripts/tests/test-batch-test.sh +65 -0
- package/scripts/tests/test-benchmark-runner.sh +25 -0
- package/scripts/tests/test-common.sh +168 -0
- package/scripts/tests/test-cost-tracking.sh +158 -0
- package/scripts/tests/test-echo-back.sh +180 -0
- package/scripts/tests/test-entropy-audit.sh +146 -0
- package/scripts/tests/test-failure-digest.sh +66 -0
- package/scripts/tests/test-generate-ast-rules.sh +145 -0
- package/scripts/tests/test-helpers.sh +82 -0
- package/scripts/tests/test-init.sh +47 -0
- package/scripts/tests/test-lesson-check.sh +278 -0
- package/scripts/tests/test-lesson-local.sh +55 -0
- package/scripts/tests/test-license-check.sh +109 -0
- package/scripts/tests/test-mab-run.sh +182 -0
- package/scripts/tests/test-ollama-lib.sh +49 -0
- package/scripts/tests/test-ollama.sh +60 -0
- package/scripts/tests/test-pipeline-status.sh +198 -0
- package/scripts/tests/test-policy-check.sh +124 -0
- package/scripts/tests/test-prior-art-search.sh +96 -0
- package/scripts/tests/test-progress-writer.sh +140 -0
- package/scripts/tests/test-promote-mab-lessons.sh +110 -0
- package/scripts/tests/test-pull-community-lessons.sh +149 -0
- package/scripts/tests/test-quality-gate.sh +241 -0
- package/scripts/tests/test-research-gate.sh +132 -0
- package/scripts/tests/test-run-plan-cli.sh +86 -0
- package/scripts/tests/test-run-plan-context.sh +305 -0
- package/scripts/tests/test-run-plan-e2e.sh +153 -0
- package/scripts/tests/test-run-plan-headless.sh +424 -0
- package/scripts/tests/test-run-plan-notify.sh +124 -0
- package/scripts/tests/test-run-plan-parser.sh +217 -0
- package/scripts/tests/test-run-plan-prompt.sh +254 -0
- package/scripts/tests/test-run-plan-quality-gate.sh +222 -0
- package/scripts/tests/test-run-plan-routing.sh +178 -0
- package/scripts/tests/test-run-plan-scoring.sh +148 -0
- package/scripts/tests/test-run-plan-state.sh +261 -0
- package/scripts/tests/test-run-plan-team.sh +157 -0
- package/scripts/tests/test-scope-infer.sh +150 -0
- package/scripts/tests/test-setup-ralph-loop.sh +63 -0
- package/scripts/tests/test-telegram-env.sh +38 -0
- package/scripts/tests/test-telegram.sh +121 -0
- package/scripts/tests/test-telemetry.sh +46 -0
- package/scripts/tests/test-thompson-sampling.sh +139 -0
- package/scripts/tests/test-validate-all.sh +60 -0
- package/scripts/tests/test-validate-commands.sh +89 -0
- package/scripts/tests/test-validate-hooks.sh +98 -0
- package/scripts/tests/test-validate-lessons.sh +150 -0
- package/scripts/tests/test-validate-plan-quality.sh +235 -0
- package/scripts/tests/test-validate-plans.sh +187 -0
- package/scripts/tests/test-validate-plugin.sh +106 -0
- package/scripts/tests/test-validate-prd.sh +184 -0
- package/scripts/tests/test-validate-skills.sh +134 -0
- package/scripts/validate-all.sh +57 -0
- package/scripts/validate-commands.sh +67 -0
- package/scripts/validate-hooks.sh +89 -0
- package/scripts/validate-lessons.sh +98 -0
- package/scripts/validate-plan-quality.sh +369 -0
- package/scripts/validate-plans.sh +120 -0
- package/scripts/validate-plugin.sh +86 -0
- package/scripts/validate-policies.sh +42 -0
- package/scripts/validate-prd.sh +118 -0
- package/scripts/validate-skills.sh +96 -0
- package/skills/autocode/SKILL.md +285 -0
- package/skills/autocode/ab-verification.md +51 -0
- package/skills/autocode/code-quality-standards.md +37 -0
- package/skills/autocode/competitive-mode.md +364 -0
- package/skills/brainstorming/SKILL.md +97 -0
- package/skills/capture-lesson/SKILL.md +187 -0
- package/skills/check-lessons/SKILL.md +116 -0
- package/skills/dispatching-parallel-agents/SKILL.md +110 -0
- package/skills/executing-plans/SKILL.md +85 -0
- package/skills/finishing-a-development-branch/SKILL.md +201 -0
- package/skills/receiving-code-review/SKILL.md +72 -0
- package/skills/requesting-code-review/SKILL.md +59 -0
- package/skills/requesting-code-review/code-reviewer.md +82 -0
- package/skills/research/SKILL.md +145 -0
- package/skills/roadmap/SKILL.md +115 -0
- package/skills/subagent-driven-development/SKILL.md +98 -0
- package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +18 -0
- package/skills/subagent-driven-development/implementer-prompt.md +73 -0
- package/skills/subagent-driven-development/spec-reviewer-prompt.md +57 -0
- package/skills/systematic-debugging/SKILL.md +134 -0
- package/skills/systematic-debugging/condition-based-waiting.md +64 -0
- package/skills/systematic-debugging/defense-in-depth.md +32 -0
- package/skills/systematic-debugging/root-cause-tracing.md +55 -0
- package/skills/test-driven-development/SKILL.md +167 -0
- package/skills/using-git-worktrees/SKILL.md +219 -0
- package/skills/using-superpowers/SKILL.md +54 -0
- package/skills/verification-before-completion/SKILL.md +140 -0
- package/skills/verify/SKILL.md +82 -0
- package/skills/writing-plans/SKILL.md +128 -0
- package/skills/writing-skills/SKILL.md +93 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Test validate-prd.sh
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
+
source "$SCRIPT_DIR/test-helpers.sh"
|
|
7
|
+
|
|
8
|
+
VALIDATOR="$SCRIPT_DIR/../validate-prd.sh"
|
|
9
|
+
WORK=$(mktemp -d)
|
|
10
|
+
trap 'rm -rf "$WORK"' EXIT
|
|
11
|
+
|
|
12
|
+
# Helper: create a PRD file
|
|
13
|
+
create_prd() {
|
|
14
|
+
local name="$1" content="$2"
|
|
15
|
+
mkdir -p "$WORK/tasks"
|
|
16
|
+
printf '%s\n' "$content" > "$WORK/tasks/$name"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
# Helper: run validator against a temp PRD file
|
|
20
|
+
run_validator() {
|
|
21
|
+
local exit_code=0
|
|
22
|
+
PRD_FILE="$WORK/tasks/prd.json" bash "$VALIDATOR" "$@" 2>&1 || exit_code=$?
|
|
23
|
+
echo "EXIT:$exit_code"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
# === Test: Valid PRD passes ===
|
|
27
|
+
create_prd "prd.json" '[
|
|
28
|
+
{
|
|
29
|
+
"id": 1,
|
|
30
|
+
"title": "First task",
|
|
31
|
+
"acceptance_criteria": ["test -f foo.txt"],
|
|
32
|
+
"passes": false,
|
|
33
|
+
"blocked_by": []
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": 2,
|
|
37
|
+
"title": "Second task",
|
|
38
|
+
"acceptance_criteria": ["test -f bar.txt"],
|
|
39
|
+
"passes": false,
|
|
40
|
+
"blocked_by": [1]
|
|
41
|
+
}
|
|
42
|
+
]'
|
|
43
|
+
|
|
44
|
+
output=$(run_validator)
|
|
45
|
+
assert_contains "valid PRD: PASS" "validate-prd: PASS" "$output"
|
|
46
|
+
assert_contains "valid PRD: exit 0" "EXIT:0" "$output"
|
|
47
|
+
|
|
48
|
+
# === Test: Invalid JSON fails ===
|
|
49
|
+
create_prd "prd.json" 'this is not json at all {'
|
|
50
|
+
|
|
51
|
+
output=$(run_validator)
|
|
52
|
+
assert_contains "invalid JSON: reports violation" "Invalid JSON" "$output"
|
|
53
|
+
assert_contains "invalid JSON: exit 1" "EXIT:1" "$output"
|
|
54
|
+
|
|
55
|
+
# === Test: Not an array fails ===
|
|
56
|
+
create_prd "prd.json" '{"id": 1, "title": "Not an array"}'
|
|
57
|
+
|
|
58
|
+
output=$(run_validator)
|
|
59
|
+
assert_contains "not array: reports violation" "must be a JSON array" "$output"
|
|
60
|
+
assert_contains "not array: exit 1" "EXIT:1" "$output"
|
|
61
|
+
|
|
62
|
+
# === Test: Missing id field fails ===
|
|
63
|
+
create_prd "prd.json" '[
|
|
64
|
+
{
|
|
65
|
+
"title": "No ID",
|
|
66
|
+
"acceptance_criteria": ["test -f foo.txt"],
|
|
67
|
+
"blocked_by": []
|
|
68
|
+
}
|
|
69
|
+
]'
|
|
70
|
+
|
|
71
|
+
output=$(run_validator)
|
|
72
|
+
assert_contains "missing id: reports violation" "missing or non-numeric 'id'" "$output"
|
|
73
|
+
assert_contains "missing id: exit 1" "EXIT:1" "$output"
|
|
74
|
+
|
|
75
|
+
# === Test: Missing title field fails ===
|
|
76
|
+
create_prd "prd.json" '[
|
|
77
|
+
{
|
|
78
|
+
"id": 1,
|
|
79
|
+
"acceptance_criteria": ["test -f foo.txt"],
|
|
80
|
+
"blocked_by": []
|
|
81
|
+
}
|
|
82
|
+
]'
|
|
83
|
+
|
|
84
|
+
output=$(run_validator)
|
|
85
|
+
assert_contains "missing title: reports violation" "missing or empty 'title'" "$output"
|
|
86
|
+
assert_contains "missing title: exit 1" "EXIT:1" "$output"
|
|
87
|
+
|
|
88
|
+
# === Test: Missing acceptance_criteria fails ===
|
|
89
|
+
create_prd "prd.json" '[
|
|
90
|
+
{
|
|
91
|
+
"id": 1,
|
|
92
|
+
"title": "No criteria",
|
|
93
|
+
"blocked_by": []
|
|
94
|
+
}
|
|
95
|
+
]'
|
|
96
|
+
|
|
97
|
+
output=$(run_validator)
|
|
98
|
+
assert_contains "missing criteria: reports violation" "missing or empty 'acceptance_criteria'" "$output"
|
|
99
|
+
assert_contains "missing criteria: exit 1" "EXIT:1" "$output"
|
|
100
|
+
|
|
101
|
+
# === Test: Empty acceptance_criteria fails ===
|
|
102
|
+
create_prd "prd.json" '[
|
|
103
|
+
{
|
|
104
|
+
"id": 1,
|
|
105
|
+
"title": "Empty criteria",
|
|
106
|
+
"acceptance_criteria": [],
|
|
107
|
+
"blocked_by": []
|
|
108
|
+
}
|
|
109
|
+
]'
|
|
110
|
+
|
|
111
|
+
output=$(run_validator)
|
|
112
|
+
assert_contains "empty criteria: reports violation" "missing or empty 'acceptance_criteria'" "$output"
|
|
113
|
+
assert_contains "empty criteria: exit 1" "EXIT:1" "$output"
|
|
114
|
+
|
|
115
|
+
# === Test: blocked_by references non-existent ID fails ===
|
|
116
|
+
create_prd "prd.json" '[
|
|
117
|
+
{
|
|
118
|
+
"id": 1,
|
|
119
|
+
"title": "First",
|
|
120
|
+
"acceptance_criteria": ["true"],
|
|
121
|
+
"blocked_by": []
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"id": 2,
|
|
125
|
+
"title": "Second",
|
|
126
|
+
"acceptance_criteria": ["true"],
|
|
127
|
+
"blocked_by": [99]
|
|
128
|
+
}
|
|
129
|
+
]'
|
|
130
|
+
|
|
131
|
+
output=$(run_validator)
|
|
132
|
+
assert_contains "bad blocked_by: reports violation" "references non-existent ID 99" "$output"
|
|
133
|
+
assert_contains "bad blocked_by: exit 1" "EXIT:1" "$output"
|
|
134
|
+
|
|
135
|
+
# === Test: Self-referencing blocked_by fails ===
|
|
136
|
+
create_prd "prd.json" '[
|
|
137
|
+
{
|
|
138
|
+
"id": 1,
|
|
139
|
+
"title": "Self-blocking",
|
|
140
|
+
"acceptance_criteria": ["true"],
|
|
141
|
+
"blocked_by": [1]
|
|
142
|
+
}
|
|
143
|
+
]'
|
|
144
|
+
|
|
145
|
+
output=$(run_validator)
|
|
146
|
+
assert_contains "self-ref: reports violation" "blocks itself" "$output"
|
|
147
|
+
assert_contains "self-ref: exit 1" "EXIT:1" "$output"
|
|
148
|
+
|
|
149
|
+
# === Test: Single file argument ===
|
|
150
|
+
create_prd "custom.json" '[
|
|
151
|
+
{
|
|
152
|
+
"id": 1,
|
|
153
|
+
"title": "Custom file",
|
|
154
|
+
"acceptance_criteria": ["true"],
|
|
155
|
+
"blocked_by": []
|
|
156
|
+
}
|
|
157
|
+
]'
|
|
158
|
+
|
|
159
|
+
exit_code=0
|
|
160
|
+
output=$(bash "$VALIDATOR" "$WORK/tasks/custom.json" 2>&1) || exit_code=$?
|
|
161
|
+
output="${output}
|
|
162
|
+
EXIT:${exit_code}"
|
|
163
|
+
assert_contains "single file arg: PASS" "validate-prd: PASS" "$output"
|
|
164
|
+
assert_contains "single file arg: exit 0" "EXIT:0" "$output"
|
|
165
|
+
|
|
166
|
+
# === Test: --warn exits 0 even with violations ===
|
|
167
|
+
create_prd "prd.json" 'not json'
|
|
168
|
+
|
|
169
|
+
output=$(run_validator --warn)
|
|
170
|
+
assert_contains "--warn: still reports violation" "Invalid JSON" "$output"
|
|
171
|
+
assert_contains "--warn: exits 0" "EXIT:0" "$output"
|
|
172
|
+
|
|
173
|
+
# === Test: --help exits 0 ===
|
|
174
|
+
output=$(run_validator --help)
|
|
175
|
+
assert_contains "--help: shows usage" "Usage:" "$output"
|
|
176
|
+
assert_contains "--help: exits 0" "EXIT:0" "$output"
|
|
177
|
+
|
|
178
|
+
# === Test: Missing PRD file fails ===
|
|
179
|
+
rm -f "$WORK/tasks/prd.json"
|
|
180
|
+
output=$(PRD_FILE="$WORK/tasks/nonexistent.json" bash "$VALIDATOR" 2>&1 || echo "EXIT:$?")
|
|
181
|
+
assert_contains "missing file: error message" "PRD file not found" "$output"
|
|
182
|
+
assert_contains "missing file: exit 1" "EXIT:1" "$output"
|
|
183
|
+
|
|
184
|
+
report_results
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Test validate-skills.sh
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
+
source "$SCRIPT_DIR/test-helpers.sh"
|
|
7
|
+
|
|
8
|
+
VALIDATOR="$SCRIPT_DIR/../validate-skills.sh"
|
|
9
|
+
WORK=$(mktemp -d)
|
|
10
|
+
trap 'rm -rf "$WORK"' EXIT
|
|
11
|
+
|
|
12
|
+
# Helper: create a skill directory with SKILL.md
|
|
13
|
+
create_skill() {
|
|
14
|
+
local name="$1" content="$2"
|
|
15
|
+
mkdir -p "$WORK/skills/$name"
|
|
16
|
+
echo "$content" > "$WORK/skills/$name/SKILL.md"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
# Helper: create a companion file in a skill directory
|
|
20
|
+
create_companion() {
|
|
21
|
+
local skill="$1" file="$2"
|
|
22
|
+
echo "# Companion" > "$WORK/skills/$skill/$file"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
# Helper: run validator against temp skills dir
|
|
26
|
+
run_validator() {
|
|
27
|
+
local exit_code=0
|
|
28
|
+
SKILLS_DIR="$WORK/skills" bash "$VALIDATOR" "$@" 2>&1 || exit_code=$?
|
|
29
|
+
echo "EXIT:$exit_code"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# === Test: Valid skill passes ===
|
|
33
|
+
rm -rf "$WORK/skills"
|
|
34
|
+
create_skill "my-skill" '---
|
|
35
|
+
name: my-skill
|
|
36
|
+
description: "A test skill"
|
|
37
|
+
---
|
|
38
|
+
Body text here.'
|
|
39
|
+
|
|
40
|
+
output=$(run_validator)
|
|
41
|
+
assert_contains "valid skill: PASS" "validate-skills: PASS" "$output"
|
|
42
|
+
assert_contains "valid skill: exit 0" "EXIT:0" "$output"
|
|
43
|
+
|
|
44
|
+
# === Test: Missing name field fails ===
|
|
45
|
+
rm -rf "$WORK/skills"
|
|
46
|
+
create_skill "no-name" '---
|
|
47
|
+
description: "Missing name"
|
|
48
|
+
---
|
|
49
|
+
Body.'
|
|
50
|
+
|
|
51
|
+
output=$(run_validator)
|
|
52
|
+
assert_contains "missing name: reports violation" "Missing required field: name" "$output"
|
|
53
|
+
assert_contains "missing name: exit 1" "EXIT:1" "$output"
|
|
54
|
+
|
|
55
|
+
# === Test: Missing description field fails ===
|
|
56
|
+
rm -rf "$WORK/skills"
|
|
57
|
+
create_skill "no-desc" '---
|
|
58
|
+
name: no-desc
|
|
59
|
+
---
|
|
60
|
+
Body.'
|
|
61
|
+
|
|
62
|
+
output=$(run_validator)
|
|
63
|
+
assert_contains "missing description: reports violation" "Missing required field: description" "$output"
|
|
64
|
+
assert_contains "missing description: exit 1" "EXIT:1" "$output"
|
|
65
|
+
|
|
66
|
+
# === Test: Name mismatch with directory fails ===
|
|
67
|
+
rm -rf "$WORK/skills"
|
|
68
|
+
create_skill "actual-dir" '---
|
|
69
|
+
name: wrong-name
|
|
70
|
+
description: "Name does not match directory"
|
|
71
|
+
---
|
|
72
|
+
Body.'
|
|
73
|
+
|
|
74
|
+
output=$(run_validator)
|
|
75
|
+
assert_contains "name mismatch: reports violation" "name 'wrong-name' does not match directory 'actual-dir'" "$output"
|
|
76
|
+
assert_contains "name mismatch: exit 1" "EXIT:1" "$output"
|
|
77
|
+
|
|
78
|
+
# === Test: Referenced .md file missing fails ===
|
|
79
|
+
rm -rf "$WORK/skills"
|
|
80
|
+
create_skill "has-ref" '---
|
|
81
|
+
name: has-ref
|
|
82
|
+
description: "References a companion file"
|
|
83
|
+
---
|
|
84
|
+
See details in companion-doc.md for more.'
|
|
85
|
+
|
|
86
|
+
output=$(run_validator)
|
|
87
|
+
assert_contains "missing ref: reports violation" "Referenced file not found: companion-doc.md" "$output"
|
|
88
|
+
assert_contains "missing ref: exit 1" "EXIT:1" "$output"
|
|
89
|
+
|
|
90
|
+
# === Test: Referenced .md file exists passes ===
|
|
91
|
+
rm -rf "$WORK/skills"
|
|
92
|
+
create_skill "has-ref-ok" '---
|
|
93
|
+
name: has-ref-ok
|
|
94
|
+
description: "References a companion file that exists"
|
|
95
|
+
---
|
|
96
|
+
See details in companion-doc.md for more.'
|
|
97
|
+
create_companion "has-ref-ok" "companion-doc.md"
|
|
98
|
+
|
|
99
|
+
output=$(run_validator)
|
|
100
|
+
assert_contains "existing ref: PASS" "validate-skills: PASS" "$output"
|
|
101
|
+
assert_contains "existing ref: exit 0" "EXIT:0" "$output"
|
|
102
|
+
|
|
103
|
+
# === Test: Missing frontmatter start fails ===
|
|
104
|
+
rm -rf "$WORK/skills"
|
|
105
|
+
create_skill "no-front" 'name: no-front
|
|
106
|
+
description: "No frontmatter markers"'
|
|
107
|
+
|
|
108
|
+
output=$(run_validator)
|
|
109
|
+
assert_contains "missing ---: reports violation" "First line must be '---'" "$output"
|
|
110
|
+
assert_contains "missing ---: exit 1" "EXIT:1" "$output"
|
|
111
|
+
|
|
112
|
+
# === Test: --warn exits 0 even with violations ===
|
|
113
|
+
rm -rf "$WORK/skills"
|
|
114
|
+
create_skill "warn-test" '---
|
|
115
|
+
description: "Missing name"
|
|
116
|
+
---
|
|
117
|
+
Body.'
|
|
118
|
+
|
|
119
|
+
output=$(run_validator --warn)
|
|
120
|
+
assert_contains "--warn: still reports violation" "Missing required field: name" "$output"
|
|
121
|
+
assert_contains "--warn: exits 0" "EXIT:0" "$output"
|
|
122
|
+
|
|
123
|
+
# === Test: --help exits 0 ===
|
|
124
|
+
output=$(run_validator --help)
|
|
125
|
+
assert_contains "--help: shows usage" "Usage:" "$output"
|
|
126
|
+
assert_contains "--help: exits 0" "EXIT:0" "$output"
|
|
127
|
+
|
|
128
|
+
# === Test: Missing skills directory fails ===
|
|
129
|
+
rm -rf "$WORK/skills"
|
|
130
|
+
output=$(SKILLS_DIR="$WORK/nonexistent" bash "$VALIDATOR" 2>&1 || echo "EXIT:$?")
|
|
131
|
+
assert_contains "missing dir: error message" "skills directory not found" "$output"
|
|
132
|
+
assert_contains "missing dir: exit 1" "EXIT:1" "$output"
|
|
133
|
+
|
|
134
|
+
report_results
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# validate-all.sh — Run all repo-level validators and report summary
|
|
3
|
+
# Exit 0 if all pass, exit 1 if any fail. Use --warn to pass through to validators.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
+
PASS_ARGS=()
|
|
8
|
+
|
|
9
|
+
usage() {
|
|
10
|
+
echo "Usage: validate-all.sh [--warn] [--help]"
|
|
11
|
+
echo " Runs all repo-level validators (lessons, skills, commands, plugin, hooks, policies)"
|
|
12
|
+
echo " --warn Pass --warn to all validators (print violations but exit 0)"
|
|
13
|
+
exit 0
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
[[ "${1:-}" == "--help" || "${1:-}" == "-h" ]] && usage
|
|
17
|
+
[[ "${1:-}" == "--warn" ]] && PASS_ARGS=("--warn")
|
|
18
|
+
|
|
19
|
+
validators=(
|
|
20
|
+
validate-lessons
|
|
21
|
+
validate-skills
|
|
22
|
+
validate-commands
|
|
23
|
+
validate-plugin
|
|
24
|
+
validate-hooks
|
|
25
|
+
validate-policies
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
total=${#validators[@]}
|
|
29
|
+
passed=0
|
|
30
|
+
failed_names=()
|
|
31
|
+
|
|
32
|
+
for name in "${validators[@]}"; do
|
|
33
|
+
script="$SCRIPT_DIR/${name}.sh"
|
|
34
|
+
if [[ ! -f "$script" ]]; then
|
|
35
|
+
echo " $name: SKIP (not found)"
|
|
36
|
+
continue
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
exit_code=0
|
|
40
|
+
bash "$script" ${PASS_ARGS[@]+"${PASS_ARGS[@]}"} >/dev/null 2>&1 || exit_code=$?
|
|
41
|
+
|
|
42
|
+
if [[ $exit_code -eq 0 ]]; then
|
|
43
|
+
echo " $name: PASS"
|
|
44
|
+
((passed++)) || true
|
|
45
|
+
else
|
|
46
|
+
echo " $name: FAIL"
|
|
47
|
+
failed_names+=("$name")
|
|
48
|
+
fi
|
|
49
|
+
done
|
|
50
|
+
|
|
51
|
+
echo ""
|
|
52
|
+
echo "$passed/$total validators passed"
|
|
53
|
+
|
|
54
|
+
if [[ ${#failed_names[@]} -gt 0 ]]; then
|
|
55
|
+
echo "Failed: ${failed_names[*]}"
|
|
56
|
+
exit 1
|
|
57
|
+
fi
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# validate-commands.sh — Validate command file frontmatter
|
|
3
|
+
# Exit 0 if clean, exit 1 if violations found. Use --warn to print but exit 0.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
+
COMMANDS_DIR="${COMMANDS_DIR:-$SCRIPT_DIR/../commands}"
|
|
8
|
+
WARN_ONLY=false
|
|
9
|
+
violations=0
|
|
10
|
+
|
|
11
|
+
usage() {
|
|
12
|
+
echo "Usage: validate-commands.sh [--warn] [--help]"
|
|
13
|
+
echo " Validates all commands/*.md files"
|
|
14
|
+
echo " --warn Print violations but exit 0"
|
|
15
|
+
exit 0
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
report_violation() {
|
|
19
|
+
local file="$1" line="$2" msg="$3"
|
|
20
|
+
echo "${file}:${line}: ${msg}"
|
|
21
|
+
((violations++)) || true
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
[[ "${1:-}" == "--help" || "${1:-}" == "-h" ]] && usage
|
|
25
|
+
[[ "${1:-}" == "--warn" ]] && WARN_ONLY=true
|
|
26
|
+
|
|
27
|
+
if [[ ! -d "$COMMANDS_DIR" ]]; then
|
|
28
|
+
echo "validate-commands: commands directory not found: $COMMANDS_DIR" >&2
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
for cmd_file in "$COMMANDS_DIR"/*.md; do
|
|
33
|
+
[[ -f "$cmd_file" ]] || continue
|
|
34
|
+
fname="$(basename "$cmd_file")"
|
|
35
|
+
|
|
36
|
+
# Check 1: First line must be ---
|
|
37
|
+
first_line=$(head -1 "$cmd_file")
|
|
38
|
+
if [[ "$first_line" != "---" ]]; then
|
|
39
|
+
report_violation "$fname" 1 "First line must be '---', got '$first_line'"
|
|
40
|
+
continue
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# Check 2: Second --- delimiter must exist (frontmatter is closed)
|
|
44
|
+
closing_line=$(sed -n '2,$ { /^---$/= }' "$cmd_file" | head -1)
|
|
45
|
+
if [[ -z "$closing_line" ]]; then
|
|
46
|
+
report_violation "$fname" 0 "Frontmatter not closed (missing second '---')"
|
|
47
|
+
continue
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# Extract frontmatter (between first two --- lines)
|
|
51
|
+
frontmatter=$(sed -n '2,/^---$/{ /^---$/d; p; }' "$cmd_file")
|
|
52
|
+
|
|
53
|
+
# Check 3: Required fields
|
|
54
|
+
if ! echo "$frontmatter" | grep -q "^description:"; then
|
|
55
|
+
report_violation "$fname" 0 "Missing required field: description"
|
|
56
|
+
fi
|
|
57
|
+
done
|
|
58
|
+
|
|
59
|
+
if [[ $violations -gt 0 ]]; then
|
|
60
|
+
echo ""
|
|
61
|
+
echo "validate-commands: FAIL ($violations issues)"
|
|
62
|
+
[[ "$WARN_ONLY" == true ]] && exit 0
|
|
63
|
+
exit 1
|
|
64
|
+
else
|
|
65
|
+
echo "validate-commands: PASS"
|
|
66
|
+
exit 0
|
|
67
|
+
fi
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# validate-hooks.sh — Validate hooks/hooks.json and referenced scripts
|
|
3
|
+
# Exit 0 if clean, exit 1 if violations found. Use --warn to print but exit 0.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
+
HOOKS_DIR="${HOOKS_DIR:-$SCRIPT_DIR/../hooks}"
|
|
8
|
+
TOOLKIT_ROOT="${TOOLKIT_ROOT:-$SCRIPT_DIR/..}"
|
|
9
|
+
WARN_ONLY=false
|
|
10
|
+
violations=0
|
|
11
|
+
|
|
12
|
+
usage() {
|
|
13
|
+
echo "Usage: validate-hooks.sh [--warn] [--help]"
|
|
14
|
+
echo " Validates hooks/hooks.json and referenced scripts"
|
|
15
|
+
echo " --warn Print violations but exit 0"
|
|
16
|
+
exit 0
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
report_violation() {
|
|
20
|
+
local file="$1" msg="$2"
|
|
21
|
+
echo "${file}: ${msg}"
|
|
22
|
+
((violations++)) || true
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
[[ "${1:-}" == "--help" || "${1:-}" == "-h" ]] && usage
|
|
26
|
+
[[ "${1:-}" == "--warn" ]] && WARN_ONLY=true
|
|
27
|
+
|
|
28
|
+
if [[ ! -d "$HOOKS_DIR" ]]; then
|
|
29
|
+
echo "validate-hooks: hooks directory not found: $HOOKS_DIR" >&2
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
hooks_file="$HOOKS_DIR/hooks.json"
|
|
34
|
+
if [[ ! -f "$hooks_file" ]]; then
|
|
35
|
+
report_violation "hooks.json" "hooks.json not found"
|
|
36
|
+
echo ""
|
|
37
|
+
echo "validate-hooks: FAIL ($violations issues)"
|
|
38
|
+
[[ "$WARN_ONLY" == true ]] && exit 0
|
|
39
|
+
exit 1
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# Validate JSON
|
|
43
|
+
if ! jq empty "$hooks_file" 2>/dev/null; then
|
|
44
|
+
report_violation "hooks.json" "hooks.json is not valid JSON"
|
|
45
|
+
echo ""
|
|
46
|
+
echo "validate-hooks: FAIL ($violations issues)"
|
|
47
|
+
[[ "$WARN_ONLY" == true ]] && exit 0
|
|
48
|
+
exit 1
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# Extract all command paths from hooks — walks the entire JSON tree for "command" keys
|
|
52
|
+
commands=$(jq -r '.. | objects | select(.type == "command") | .command' "$hooks_file" 2>/dev/null || true)
|
|
53
|
+
|
|
54
|
+
while IFS= read -r cmd; do
|
|
55
|
+
[[ -z "$cmd" ]] && continue
|
|
56
|
+
# Resolve ${CLAUDE_PLUGIN_ROOT} to toolkit root
|
|
57
|
+
resolved="${cmd//\$\{CLAUDE_PLUGIN_ROOT\}/$TOOLKIT_ROOT}"
|
|
58
|
+
|
|
59
|
+
# Extract the script path (last token that looks like a file path)
|
|
60
|
+
# Handles "bash /path/to/script.sh" and "/path/to/script.sh" alike
|
|
61
|
+
script_path="$resolved"
|
|
62
|
+
for token in $resolved; do
|
|
63
|
+
if [[ "$token" == /* || "$token" == \$* ]]; then
|
|
64
|
+
script_path="$token"
|
|
65
|
+
break
|
|
66
|
+
fi
|
|
67
|
+
done
|
|
68
|
+
|
|
69
|
+
# Skip if the resolved path is just a bare command (e.g., "bash" — not a file check target)
|
|
70
|
+
if [[ "$script_path" != */* ]]; then
|
|
71
|
+
continue
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
if [[ ! -f "$script_path" ]]; then
|
|
75
|
+
report_violation "hooks.json" "script not found: $cmd (resolved: $script_path)"
|
|
76
|
+
elif [[ ! -x "$script_path" ]]; then
|
|
77
|
+
report_violation "hooks.json" "script not executable: $cmd (resolved: $script_path)"
|
|
78
|
+
fi
|
|
79
|
+
done <<< "$commands"
|
|
80
|
+
|
|
81
|
+
if [[ $violations -gt 0 ]]; then
|
|
82
|
+
echo ""
|
|
83
|
+
echo "validate-hooks: FAIL ($violations issues)"
|
|
84
|
+
[[ "$WARN_ONLY" == true ]] && exit 0
|
|
85
|
+
exit 1
|
|
86
|
+
else
|
|
87
|
+
echo "validate-hooks: PASS"
|
|
88
|
+
exit 0
|
|
89
|
+
fi
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# validate-lessons.sh — Validate lesson file format and frontmatter
|
|
3
|
+
# Exit 0 if clean, exit 1 if violations found. Use --warn to print but exit 0.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
+
LESSONS_DIR="${LESSONS_DIR:-$SCRIPT_DIR/../docs/lessons}"
|
|
8
|
+
WARN_ONLY=false
|
|
9
|
+
violations=0
|
|
10
|
+
|
|
11
|
+
usage() {
|
|
12
|
+
echo "Usage: validate-lessons.sh [--warn] [--help]"
|
|
13
|
+
echo " Validates all lesson files in docs/lessons/"
|
|
14
|
+
echo " --warn Print violations but exit 0"
|
|
15
|
+
exit 0
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
report_violation() {
|
|
19
|
+
local file="$1" line="$2" msg="$3"
|
|
20
|
+
echo "${file}:${line}: ${msg}"
|
|
21
|
+
((violations++)) || true
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
[[ "${1:-}" == "--help" || "${1:-}" == "-h" ]] && usage
|
|
25
|
+
[[ "${1:-}" == "--warn" ]] && WARN_ONLY=true
|
|
26
|
+
|
|
27
|
+
if [[ ! -d "$LESSONS_DIR" ]]; then
|
|
28
|
+
echo "validate-lessons: lessons directory not found: $LESSONS_DIR" >&2
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
seen_ids=()
|
|
33
|
+
|
|
34
|
+
for lesson in "$LESSONS_DIR"/[0-9]*.md; do
|
|
35
|
+
[[ -f "$lesson" ]] || continue
|
|
36
|
+
fname="$(basename "$lesson")"
|
|
37
|
+
|
|
38
|
+
# Check 1: First line must be ---
|
|
39
|
+
first_line=$(head -1 "$lesson")
|
|
40
|
+
if [[ "$first_line" != "---" ]]; then
|
|
41
|
+
report_violation "$fname" 1 "First line must be '---', got '$first_line' (code block wrapping?)"
|
|
42
|
+
continue # Can't parse frontmatter if start is wrong
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# Extract frontmatter (between first two --- lines)
|
|
46
|
+
frontmatter=$(sed -n '2,/^---$/{ /^---$/d; p; }' "$lesson")
|
|
47
|
+
|
|
48
|
+
# Check 2: Required fields
|
|
49
|
+
for field in id title severity languages; do
|
|
50
|
+
if ! echo "$frontmatter" | grep -q "^${field}:"; then
|
|
51
|
+
report_violation "$fname" 0 "Missing required field: $field"
|
|
52
|
+
fi
|
|
53
|
+
done
|
|
54
|
+
|
|
55
|
+
# Check 3: pattern.type must exist
|
|
56
|
+
if ! echo "$frontmatter" | grep -q "type:"; then
|
|
57
|
+
report_violation "$fname" 0 "Missing pattern.type field"
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# Check 4: Extract and validate ID
|
|
61
|
+
lesson_id=$(echo "$frontmatter" | sed -n 's/^id:[[:space:]]*\(.*\)/\1/p' | tr -d ' "'"'"'')
|
|
62
|
+
if [[ -n "$lesson_id" ]]; then
|
|
63
|
+
# Check for duplicate IDs
|
|
64
|
+
for seen in "${seen_ids[@]+"${seen_ids[@]}"}"; do
|
|
65
|
+
if [[ "$seen" == "$lesson_id" ]]; then
|
|
66
|
+
report_violation "$fname" 0 "Duplicate lesson ID: $lesson_id"
|
|
67
|
+
fi
|
|
68
|
+
done
|
|
69
|
+
seen_ids+=("$lesson_id")
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# Check 5: Severity must be valid
|
|
73
|
+
severity=$(echo "$frontmatter" | sed -n 's/^severity:[[:space:]]*\(.*\)/\1/p' | tr -d ' ')
|
|
74
|
+
if [[ -n "$severity" ]]; then
|
|
75
|
+
case "$severity" in
|
|
76
|
+
blocker|should-fix|nice-to-have) ;;
|
|
77
|
+
*) report_violation "$fname" 0 "Invalid severity '$severity' (must be blocker|should-fix|nice-to-have)" ;;
|
|
78
|
+
esac
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
# Check 6: Syntactic lessons must have regex
|
|
82
|
+
pattern_type=$(echo "$frontmatter" | grep "type:" | tail -1 | sed 's/.*type:[[:space:]]*//' | tr -d ' ')
|
|
83
|
+
if [[ "$pattern_type" == "syntactic" ]]; then
|
|
84
|
+
if ! echo "$frontmatter" | grep -q "regex:"; then
|
|
85
|
+
report_violation "$fname" 0 "Syntactic lesson missing regex field"
|
|
86
|
+
fi
|
|
87
|
+
fi
|
|
88
|
+
done
|
|
89
|
+
|
|
90
|
+
if [[ $violations -gt 0 ]]; then
|
|
91
|
+
echo ""
|
|
92
|
+
echo "validate-lessons: FAIL ($violations issues)"
|
|
93
|
+
[[ "$WARN_ONLY" == true ]] && exit 0
|
|
94
|
+
exit 1
|
|
95
|
+
else
|
|
96
|
+
echo "validate-lessons: PASS"
|
|
97
|
+
exit 0
|
|
98
|
+
fi
|