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,369 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# validate-plan-quality.sh — Score implementation plan quality on 8 dimensions
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# validate-plan-quality.sh <plan-file> [--min-score N] [--json]
|
|
6
|
+
#
|
|
7
|
+
# Returns score 0-100. Exit 0 if >= min-score (default: 60), exit 1 otherwise.
|
|
8
|
+
# Research basis: Plan quality has 3x the impact of execution quality (SWE-bench Pro, N=1865).
|
|
9
|
+
#
|
|
10
|
+
# Dimensions (weights):
|
|
11
|
+
# 1. Task granularity — each task < 100 lines estimated (15%)
|
|
12
|
+
# 2. Spec completeness — each task has verification command (20%)
|
|
13
|
+
# 3. Single outcome — no mixed task types per batch (10%)
|
|
14
|
+
# 4. Dependency ordering — no forward references (10%)
|
|
15
|
+
# 5. File path specificity — all tasks name exact files (15%)
|
|
16
|
+
# 6. Acceptance criteria — each batch has at least one assert (15%)
|
|
17
|
+
# 7. Batch size — 1-5 tasks per batch (10%)
|
|
18
|
+
# 8. TDD structure — test-before-implement pattern (5%)
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
22
|
+
|
|
23
|
+
# Source parser for plan parsing functions
|
|
24
|
+
source "$SCRIPT_DIR/lib/run-plan-parser.sh"
|
|
25
|
+
|
|
26
|
+
# --- Defaults ---
|
|
27
|
+
PLAN_FILE=""
|
|
28
|
+
MIN_SCORE=60
|
|
29
|
+
JSON_OUTPUT=false
|
|
30
|
+
|
|
31
|
+
# --- Usage ---
|
|
32
|
+
usage() {
|
|
33
|
+
cat <<'USAGE'
|
|
34
|
+
validate-plan-quality — Score implementation plan quality (0-100)
|
|
35
|
+
|
|
36
|
+
Usage:
|
|
37
|
+
validate-plan-quality.sh <plan-file> [--min-score N] [--json]
|
|
38
|
+
|
|
39
|
+
Options:
|
|
40
|
+
--min-score N Minimum passing score (default: 60)
|
|
41
|
+
--json Output JSON report instead of text
|
|
42
|
+
-h, --help Show this help message
|
|
43
|
+
|
|
44
|
+
Dimensions scored:
|
|
45
|
+
1. Task granularity (15%) — tasks should be small (<100 lines each)
|
|
46
|
+
2. Spec completeness (20%) — tasks have verification commands
|
|
47
|
+
3. Single outcome (10%) — batches don't mix create/refactor/test
|
|
48
|
+
4. Dependency ordering (10%) — no forward references to later batches
|
|
49
|
+
5. File path specificity (15%) — tasks name exact file paths
|
|
50
|
+
6. Acceptance criteria (15%) — batches have testable assertions
|
|
51
|
+
7. Batch size (10%) — 1-5 tasks per batch
|
|
52
|
+
8. TDD structure (5%) — test-before-implement ordering
|
|
53
|
+
USAGE
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# --- Argument parsing ---
|
|
57
|
+
while [[ $# -gt 0 ]]; do
|
|
58
|
+
case "$1" in
|
|
59
|
+
-h|--help) usage; exit 0 ;;
|
|
60
|
+
--min-score) MIN_SCORE="$2"; shift 2 ;;
|
|
61
|
+
--json) JSON_OUTPUT=true; shift ;;
|
|
62
|
+
-*) echo "ERROR: Unknown option: $1" >&2; exit 1 ;;
|
|
63
|
+
*) PLAN_FILE="$1"; shift ;;
|
|
64
|
+
esac
|
|
65
|
+
done
|
|
66
|
+
|
|
67
|
+
if [[ -z "$PLAN_FILE" ]]; then
|
|
68
|
+
echo "ERROR: Plan file required" >&2
|
|
69
|
+
usage >&2
|
|
70
|
+
exit 1
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
if ! [[ "$MIN_SCORE" =~ ^[0-9]+$ ]]; then
|
|
74
|
+
echo "ERROR: --min-score must be a number, got: $MIN_SCORE" >&2
|
|
75
|
+
exit 1
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
if [[ ! -f "$PLAN_FILE" ]]; then
|
|
79
|
+
echo "ERROR: Plan file not found: $PLAN_FILE" >&2
|
|
80
|
+
exit 1
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
# --- Helpers ---
|
|
84
|
+
# Safe grep -c that works with pipefail (avoids the 0\n0 double-output bug).
|
|
85
|
+
# Reads from stdin. Distinguishes "no matches" (exit 1) from "grep error" (exit 2+).
|
|
86
|
+
_count_matches() {
|
|
87
|
+
local pattern="$1"
|
|
88
|
+
local result exit_code=0
|
|
89
|
+
result=$(grep -ciE "$pattern" 2>&1) || exit_code=$?
|
|
90
|
+
if [[ $exit_code -le 1 ]]; then
|
|
91
|
+
# 0 = matches found, 1 = no matches (both normal)
|
|
92
|
+
echo "${result:-0}"
|
|
93
|
+
else
|
|
94
|
+
echo "WARNING: grep failed (exit $exit_code) for pattern: $pattern" >&2
|
|
95
|
+
echo "0"
|
|
96
|
+
fi
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# --- Scoring functions ---
|
|
100
|
+
# Each returns a score 0-100 for its dimension
|
|
101
|
+
|
|
102
|
+
score_task_granularity() {
|
|
103
|
+
local plan_file="$1"
|
|
104
|
+
local total_batches task_count total_tasks=0 batches_with_big_tasks=0
|
|
105
|
+
total_batches=$(count_batches "$plan_file")
|
|
106
|
+
[[ "$total_batches" -eq 0 ]] && { echo 0; return; }
|
|
107
|
+
|
|
108
|
+
for ((b = 1; b <= total_batches; b++)); do
|
|
109
|
+
local text
|
|
110
|
+
text=$(get_batch_text "$plan_file" "$b")
|
|
111
|
+
task_count=$(echo "$text" | _count_matches '^### Task [0-9]+')
|
|
112
|
+
total_tasks=$((total_tasks + task_count))
|
|
113
|
+
|
|
114
|
+
# Estimate: if batch text > 200 lines and has tasks, tasks are probably too big
|
|
115
|
+
local line_count
|
|
116
|
+
line_count=$(echo "$text" | wc -l)
|
|
117
|
+
if [[ "$task_count" -gt 0 ]]; then
|
|
118
|
+
local avg_lines=$(( line_count / task_count ))
|
|
119
|
+
if [[ "$avg_lines" -gt 100 ]]; then
|
|
120
|
+
batches_with_big_tasks=$((batches_with_big_tasks + 1))
|
|
121
|
+
fi
|
|
122
|
+
fi
|
|
123
|
+
done
|
|
124
|
+
|
|
125
|
+
if [[ "$total_batches" -eq 0 ]]; then
|
|
126
|
+
echo 0
|
|
127
|
+
elif [[ "$batches_with_big_tasks" -eq 0 ]]; then
|
|
128
|
+
echo 100
|
|
129
|
+
else
|
|
130
|
+
local pct=$(( 100 - (batches_with_big_tasks * 100 / total_batches) ))
|
|
131
|
+
echo "$pct"
|
|
132
|
+
fi
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
score_spec_completeness() {
|
|
136
|
+
local plan_file="$1"
|
|
137
|
+
local total_batches tasks_with_verify=0 total_tasks=0
|
|
138
|
+
total_batches=$(count_batches "$plan_file")
|
|
139
|
+
[[ "$total_batches" -eq 0 ]] && { echo 0; return; }
|
|
140
|
+
|
|
141
|
+
for ((b = 1; b <= total_batches; b++)); do
|
|
142
|
+
local text
|
|
143
|
+
text=$(get_batch_text "$plan_file" "$b")
|
|
144
|
+
local task_count
|
|
145
|
+
task_count=$(echo "$text" | _count_matches '^### Task [0-9]+')
|
|
146
|
+
total_tasks=$((total_tasks + task_count))
|
|
147
|
+
|
|
148
|
+
# Count tasks that mention verification patterns
|
|
149
|
+
local verify_count
|
|
150
|
+
verify_count=$(echo "$text" | _count_matches '(verify|assert|test|check|confirm|expect|should)')
|
|
151
|
+
# Approximate: if verify mentions >= task count, all tasks are specified
|
|
152
|
+
if [[ "$task_count" -gt 0 && "$verify_count" -ge "$task_count" ]]; then
|
|
153
|
+
tasks_with_verify=$((tasks_with_verify + task_count))
|
|
154
|
+
elif [[ "$task_count" -gt 0 ]]; then
|
|
155
|
+
# Partial credit
|
|
156
|
+
tasks_with_verify=$((tasks_with_verify + verify_count))
|
|
157
|
+
fi
|
|
158
|
+
done
|
|
159
|
+
|
|
160
|
+
if [[ "$total_tasks" -eq 0 ]]; then
|
|
161
|
+
echo 0
|
|
162
|
+
else
|
|
163
|
+
local pct=$(( tasks_with_verify * 100 / total_tasks ))
|
|
164
|
+
[[ "$pct" -gt 100 ]] && pct=100
|
|
165
|
+
echo "$pct"
|
|
166
|
+
fi
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
score_single_outcome() {
|
|
170
|
+
local plan_file="$1"
|
|
171
|
+
local total_batches mixed_batches=0
|
|
172
|
+
total_batches=$(count_batches "$plan_file")
|
|
173
|
+
[[ "$total_batches" -eq 0 ]] && { echo 0; return; }
|
|
174
|
+
|
|
175
|
+
for ((b = 1; b <= total_batches; b++)); do
|
|
176
|
+
local text
|
|
177
|
+
text=$(get_batch_text "$plan_file" "$b")
|
|
178
|
+
# Detect mixed types: creation + refactoring in same batch
|
|
179
|
+
local has_create has_refactor
|
|
180
|
+
has_create=$(echo "$text" | _count_matches '(create|add|new|implement)')
|
|
181
|
+
has_refactor=$(echo "$text" | _count_matches '(refactor|rename|move|extract|reorganize)')
|
|
182
|
+
|
|
183
|
+
# Mixed = has significant refactoring alongside significant creation
|
|
184
|
+
if [[ "$has_create" -ge 2 && "$has_refactor" -ge 2 ]]; then
|
|
185
|
+
mixed_batches=$((mixed_batches + 1))
|
|
186
|
+
fi
|
|
187
|
+
done
|
|
188
|
+
|
|
189
|
+
if [[ "$mixed_batches" -eq 0 ]]; then
|
|
190
|
+
echo 100
|
|
191
|
+
else
|
|
192
|
+
local pct=$(( 100 - (mixed_batches * 100 / total_batches) ))
|
|
193
|
+
[[ "$pct" -lt 0 ]] && pct=0
|
|
194
|
+
echo "$pct"
|
|
195
|
+
fi
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
score_dependency_ordering() {
|
|
199
|
+
local plan_file="$1"
|
|
200
|
+
local total_batches forward_refs=0
|
|
201
|
+
total_batches=$(count_batches "$plan_file")
|
|
202
|
+
[[ "$total_batches" -eq 0 ]] && { echo 0; return; }
|
|
203
|
+
|
|
204
|
+
for ((b = 1; b <= total_batches; b++)); do
|
|
205
|
+
local text
|
|
206
|
+
text=$(get_batch_text "$plan_file" "$b")
|
|
207
|
+
# Check for references to later batches
|
|
208
|
+
for ((future = b + 1; future <= total_batches; future++)); do
|
|
209
|
+
if echo "$text" | grep -qE "(Batch ${future}|batch ${future})" 2>/dev/null; then
|
|
210
|
+
forward_refs=$((forward_refs + 1))
|
|
211
|
+
break
|
|
212
|
+
fi
|
|
213
|
+
done
|
|
214
|
+
done
|
|
215
|
+
|
|
216
|
+
if [[ "$forward_refs" -eq 0 ]]; then
|
|
217
|
+
echo 100
|
|
218
|
+
else
|
|
219
|
+
local pct=$(( 100 - (forward_refs * 100 / total_batches) ))
|
|
220
|
+
[[ "$pct" -lt 0 ]] && pct=0
|
|
221
|
+
echo "$pct"
|
|
222
|
+
fi
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
score_file_path_specificity() {
|
|
226
|
+
local plan_file="$1"
|
|
227
|
+
local total_batches batches_with_paths=0
|
|
228
|
+
total_batches=$(count_batches "$plan_file")
|
|
229
|
+
[[ "$total_batches" -eq 0 ]] && { echo 0; return; }
|
|
230
|
+
|
|
231
|
+
for ((b = 1; b <= total_batches; b++)); do
|
|
232
|
+
local text
|
|
233
|
+
text=$(get_batch_text "$plan_file" "$b")
|
|
234
|
+
# Check for file path patterns (backtick-wrapped paths or **Files:** sections)
|
|
235
|
+
local path_count
|
|
236
|
+
path_count=$(echo "$text" | _count_matches '(`[a-zA-Z0-9_./-]+\.[a-zA-Z]+`|\*\*Files:\*\*)')
|
|
237
|
+
if [[ "$path_count" -gt 0 ]]; then
|
|
238
|
+
batches_with_paths=$((batches_with_paths + 1))
|
|
239
|
+
fi
|
|
240
|
+
done
|
|
241
|
+
|
|
242
|
+
echo $(( batches_with_paths * 100 / total_batches ))
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
score_acceptance_criteria() {
|
|
246
|
+
local plan_file="$1"
|
|
247
|
+
local total_batches batches_with_criteria=0
|
|
248
|
+
total_batches=$(count_batches "$plan_file")
|
|
249
|
+
[[ "$total_batches" -eq 0 ]] && { echo 0; return; }
|
|
250
|
+
|
|
251
|
+
for ((b = 1; b <= total_batches; b++)); do
|
|
252
|
+
local text
|
|
253
|
+
text=$(get_batch_text "$plan_file" "$b")
|
|
254
|
+
# Look for acceptance criteria patterns
|
|
255
|
+
local criteria_count
|
|
256
|
+
criteria_count=$(echo "$text" | _count_matches '(assert|expect|should|must|test.*pass|verify that|confirm)')
|
|
257
|
+
if [[ "$criteria_count" -gt 0 ]]; then
|
|
258
|
+
batches_with_criteria=$((batches_with_criteria + 1))
|
|
259
|
+
fi
|
|
260
|
+
done
|
|
261
|
+
|
|
262
|
+
echo $(( batches_with_criteria * 100 / total_batches ))
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
score_batch_size() {
|
|
266
|
+
local plan_file="$1"
|
|
267
|
+
local total_batches good_batches=0
|
|
268
|
+
total_batches=$(count_batches "$plan_file")
|
|
269
|
+
[[ "$total_batches" -eq 0 ]] && { echo 0; return; }
|
|
270
|
+
|
|
271
|
+
for ((b = 1; b <= total_batches; b++)); do
|
|
272
|
+
local task_count
|
|
273
|
+
task_count=$(get_batch_task_count "$plan_file" "$b")
|
|
274
|
+
# Ideal: 1-5 tasks per batch
|
|
275
|
+
if [[ "$task_count" -ge 1 && "$task_count" -le 5 ]]; then
|
|
276
|
+
good_batches=$((good_batches + 1))
|
|
277
|
+
fi
|
|
278
|
+
done
|
|
279
|
+
|
|
280
|
+
echo $(( good_batches * 100 / total_batches ))
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
score_tdd_structure() {
|
|
284
|
+
local plan_file="$1"
|
|
285
|
+
local total_batches tdd_batches=0
|
|
286
|
+
total_batches=$(count_batches "$plan_file")
|
|
287
|
+
[[ "$total_batches" -eq 0 ]] && { echo 0; return; }
|
|
288
|
+
|
|
289
|
+
for ((b = 1; b <= total_batches; b++)); do
|
|
290
|
+
local text
|
|
291
|
+
text=$(get_batch_text "$plan_file" "$b")
|
|
292
|
+
# Look for TDD patterns: explicit TDD language or test file references before source
|
|
293
|
+
if echo "$text" | grep -qiE '(write.*test.*before|test.*first|failing test|red.green|TDD)' 2>/dev/null; then
|
|
294
|
+
tdd_batches=$((tdd_batches + 1))
|
|
295
|
+
elif echo "$text" | grep -qiE 'Test:.*test.*\.(py|js|ts|sh)' 2>/dev/null; then
|
|
296
|
+
tdd_batches=$((tdd_batches + 1))
|
|
297
|
+
fi
|
|
298
|
+
done
|
|
299
|
+
|
|
300
|
+
echo $(( tdd_batches * 100 / total_batches ))
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
# --- Main scoring ---
|
|
304
|
+
total_batches=$(count_batches "$PLAN_FILE")
|
|
305
|
+
if [[ "$total_batches" -eq 0 ]]; then
|
|
306
|
+
echo "ERROR: No batches found in plan (expected '## Batch N:' headers)" >&2
|
|
307
|
+
exit 1
|
|
308
|
+
fi
|
|
309
|
+
|
|
310
|
+
# Score each dimension
|
|
311
|
+
s_granularity=$(score_task_granularity "$PLAN_FILE")
|
|
312
|
+
s_spec=$(score_spec_completeness "$PLAN_FILE")
|
|
313
|
+
s_single=$(score_single_outcome "$PLAN_FILE")
|
|
314
|
+
s_deps=$(score_dependency_ordering "$PLAN_FILE")
|
|
315
|
+
s_paths=$(score_file_path_specificity "$PLAN_FILE")
|
|
316
|
+
s_criteria=$(score_acceptance_criteria "$PLAN_FILE")
|
|
317
|
+
s_batch_size=$(score_batch_size "$PLAN_FILE")
|
|
318
|
+
s_tdd=$(score_tdd_structure "$PLAN_FILE")
|
|
319
|
+
|
|
320
|
+
# Weighted total (weights sum to 100)
|
|
321
|
+
total=$(( (s_granularity * 15 + s_spec * 20 + s_single * 10 + s_deps * 10 + s_paths * 15 + s_criteria * 15 + s_batch_size * 10 + s_tdd * 5) / 100 ))
|
|
322
|
+
|
|
323
|
+
if [[ "$JSON_OUTPUT" == true ]]; then
|
|
324
|
+
escaped_plan=$(printf '%s' "$PLAN_FILE" | jq -Rs '.')
|
|
325
|
+
cat <<JSONEOF
|
|
326
|
+
{
|
|
327
|
+
"plan_file": $escaped_plan,
|
|
328
|
+
"total_batches": $total_batches,
|
|
329
|
+
"score": $total,
|
|
330
|
+
"min_score": $MIN_SCORE,
|
|
331
|
+
"passed": $([ "$total" -ge "$MIN_SCORE" ] && echo "true" || echo "false"),
|
|
332
|
+
"dimensions": {
|
|
333
|
+
"task_granularity": {"score": $s_granularity, "weight": 15},
|
|
334
|
+
"spec_completeness": {"score": $s_spec, "weight": 20},
|
|
335
|
+
"single_outcome": {"score": $s_single, "weight": 10},
|
|
336
|
+
"dependency_ordering": {"score": $s_deps, "weight": 10},
|
|
337
|
+
"file_path_specificity": {"score": $s_paths, "weight": 15},
|
|
338
|
+
"acceptance_criteria": {"score": $s_criteria, "weight": 15},
|
|
339
|
+
"batch_size": {"score": $s_batch_size, "weight": 10},
|
|
340
|
+
"tdd_structure": {"score": $s_tdd, "weight": 5}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
JSONEOF
|
|
344
|
+
else
|
|
345
|
+
echo "Plan Quality Scorecard: $(basename "$PLAN_FILE")"
|
|
346
|
+
echo "═══════════════════════════════════════════"
|
|
347
|
+
printf " %-25s %3d/100 (15%%)\n" "Task granularity" "$s_granularity"
|
|
348
|
+
printf " %-25s %3d/100 (20%%)\n" "Spec completeness" "$s_spec"
|
|
349
|
+
printf " %-25s %3d/100 (10%%)\n" "Single outcome" "$s_single"
|
|
350
|
+
printf " %-25s %3d/100 (10%%)\n" "Dependency ordering" "$s_deps"
|
|
351
|
+
printf " %-25s %3d/100 (15%%)\n" "File path specificity" "$s_paths"
|
|
352
|
+
printf " %-25s %3d/100 (15%%)\n" "Acceptance criteria" "$s_criteria"
|
|
353
|
+
printf " %-25s %3d/100 (10%%)\n" "Batch size" "$s_batch_size"
|
|
354
|
+
printf " %-25s %3d/100 ( 5%%)\n" "TDD structure" "$s_tdd"
|
|
355
|
+
echo "═══════════════════════════════════════════"
|
|
356
|
+
printf " %-25s %3d/100 (min: %d)\n" "TOTAL" "$total" "$MIN_SCORE"
|
|
357
|
+
echo ""
|
|
358
|
+
if [[ "$total" -ge "$MIN_SCORE" ]]; then
|
|
359
|
+
echo "PASSED"
|
|
360
|
+
else
|
|
361
|
+
echo "FAILED (score $total < min $MIN_SCORE)"
|
|
362
|
+
fi
|
|
363
|
+
fi
|
|
364
|
+
|
|
365
|
+
if [[ "$total" -ge "$MIN_SCORE" ]]; then
|
|
366
|
+
exit 0
|
|
367
|
+
else
|
|
368
|
+
exit 1
|
|
369
|
+
fi
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# validate-plans.sh — Validate implementation plan structure
|
|
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
|
+
PLANS_DIR="${PLANS_DIR:-$SCRIPT_DIR/../docs/plans}"
|
|
8
|
+
WARN_ONLY=false
|
|
9
|
+
EXPLICIT_FILES=false
|
|
10
|
+
violations=0
|
|
11
|
+
FILES=()
|
|
12
|
+
|
|
13
|
+
usage() {
|
|
14
|
+
echo "Usage: validate-plans.sh [--warn] [--help] [file ...]"
|
|
15
|
+
echo " Validates plan files (files with ## Batch headers)"
|
|
16
|
+
echo " Without arguments, scans docs/plans/*.md"
|
|
17
|
+
echo " --warn Print violations but exit 0"
|
|
18
|
+
exit 0
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
report_violation() {
|
|
22
|
+
local file="$1" msg="$2"
|
|
23
|
+
echo "${file}: ${msg}"
|
|
24
|
+
((violations++)) || true
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# Parse arguments
|
|
28
|
+
while [[ $# -gt 0 ]]; do
|
|
29
|
+
case "$1" in
|
|
30
|
+
--help|-h) usage ;;
|
|
31
|
+
--warn) WARN_ONLY=true; shift ;;
|
|
32
|
+
*) FILES+=("$1"); EXPLICIT_FILES=true; shift ;;
|
|
33
|
+
esac
|
|
34
|
+
done
|
|
35
|
+
|
|
36
|
+
# If no files given, scan PLANS_DIR
|
|
37
|
+
if [[ ${#FILES[@]} -eq 0 ]]; then
|
|
38
|
+
if [[ ! -d "$PLANS_DIR" ]]; then
|
|
39
|
+
echo "validate-plans: plans directory not found: $PLANS_DIR" >&2
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
42
|
+
for f in "$PLANS_DIR"/*.md; do
|
|
43
|
+
[[ -f "$f" ]] || continue
|
|
44
|
+
FILES+=("$f")
|
|
45
|
+
done
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
validated=0
|
|
49
|
+
|
|
50
|
+
for plan in "${FILES[@]}"; do
|
|
51
|
+
[[ -f "$plan" ]] || continue
|
|
52
|
+
fname="$(basename "$plan")"
|
|
53
|
+
|
|
54
|
+
# Skip files without any Batch headers (design docs) — but only in scan mode
|
|
55
|
+
if ! grep -q '^## Batch [0-9]' "$plan"; then
|
|
56
|
+
if [[ "$EXPLICIT_FILES" == true ]]; then
|
|
57
|
+
report_violation "$fname" "No batches found (missing '## Batch N:' headers)"
|
|
58
|
+
fi
|
|
59
|
+
continue
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
validated=$((validated + 1))
|
|
63
|
+
|
|
64
|
+
# Extract batch numbers and line numbers
|
|
65
|
+
batch_numbers=()
|
|
66
|
+
batch_lines=()
|
|
67
|
+
while IFS=: read -r line_num line_content; do
|
|
68
|
+
num=$(echo "$line_content" | sed -n 's/^## Batch \([0-9][0-9]*\).*/\1/p')
|
|
69
|
+
if [[ -n "$num" ]]; then
|
|
70
|
+
batch_numbers+=("$num")
|
|
71
|
+
batch_lines+=("$line_num")
|
|
72
|
+
fi
|
|
73
|
+
done < <(grep -n '^## Batch [0-9]' "$plan")
|
|
74
|
+
|
|
75
|
+
# Check: at least one batch
|
|
76
|
+
if [[ ${#batch_numbers[@]} -eq 0 ]]; then
|
|
77
|
+
report_violation "$fname" "No batches found"
|
|
78
|
+
continue
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
# Check: sequential batch numbers starting from 1
|
|
82
|
+
expected=1
|
|
83
|
+
for num in "${batch_numbers[@]}"; do
|
|
84
|
+
if [[ "$num" -ne "$expected" ]]; then
|
|
85
|
+
report_violation "$fname" "Non-sequential batch numbering: found Batch $num, expected Batch $expected"
|
|
86
|
+
break
|
|
87
|
+
fi
|
|
88
|
+
expected=$((expected + 1))
|
|
89
|
+
done
|
|
90
|
+
|
|
91
|
+
# Check: each batch has at least one task
|
|
92
|
+
total_lines=$(wc -l < "$plan")
|
|
93
|
+
for i in "${!batch_numbers[@]}"; do
|
|
94
|
+
batch_num="${batch_numbers[$i]}"
|
|
95
|
+
start_line="${batch_lines[$i]}"
|
|
96
|
+
|
|
97
|
+
# End is line before next batch start, or EOF for last batch
|
|
98
|
+
if [[ $((i + 1)) -lt ${#batch_lines[@]} ]]; then
|
|
99
|
+
end_line=$(( ${batch_lines[$((i + 1))]} - 1 ))
|
|
100
|
+
else
|
|
101
|
+
end_line="$total_lines"
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
# Check for ### Task headers between start and end
|
|
105
|
+
task_count=$(sed -n "${start_line},${end_line}p" "$plan" | grep -c '^### Task ' || true)
|
|
106
|
+
if [[ "$task_count" -eq 0 ]]; then
|
|
107
|
+
report_violation "$fname" "Batch $batch_num has no tasks"
|
|
108
|
+
fi
|
|
109
|
+
done
|
|
110
|
+
done
|
|
111
|
+
|
|
112
|
+
if [[ $violations -gt 0 ]]; then
|
|
113
|
+
echo ""
|
|
114
|
+
echo "validate-plans: FAIL ($violations issues)"
|
|
115
|
+
[[ "$WARN_ONLY" == true ]] && exit 0
|
|
116
|
+
exit 1
|
|
117
|
+
else
|
|
118
|
+
echo "validate-plans: PASS"
|
|
119
|
+
exit 0
|
|
120
|
+
fi
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# validate-plugin.sh — Validate .claude-plugin/plugin.json and marketplace.json consistency
|
|
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
|
+
PLUGIN_DIR="${PLUGIN_DIR:-$SCRIPT_DIR/../.claude-plugin}"
|
|
8
|
+
WARN_ONLY=false
|
|
9
|
+
violations=0
|
|
10
|
+
|
|
11
|
+
usage() {
|
|
12
|
+
echo "Usage: validate-plugin.sh [--warn] [--help]"
|
|
13
|
+
echo " Validates .claude-plugin/plugin.json and marketplace.json"
|
|
14
|
+
echo " --warn Print violations but exit 0"
|
|
15
|
+
exit 0
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
report_violation() {
|
|
19
|
+
local file="$1" msg="$2"
|
|
20
|
+
echo "${file}: ${msg}"
|
|
21
|
+
((violations++)) || true
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
[[ "${1:-}" == "--help" || "${1:-}" == "-h" ]] && usage
|
|
25
|
+
[[ "${1:-}" == "--warn" ]] && WARN_ONLY=true
|
|
26
|
+
|
|
27
|
+
if [[ ! -d "$PLUGIN_DIR" ]]; then
|
|
28
|
+
echo "validate-plugin: plugin directory not found: $PLUGIN_DIR" >&2
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# Check files exist
|
|
33
|
+
if [[ ! -f "$PLUGIN_DIR/plugin.json" ]]; then
|
|
34
|
+
report_violation "plugin.json" "plugin.json not found"
|
|
35
|
+
fi
|
|
36
|
+
if [[ ! -f "$PLUGIN_DIR/marketplace.json" ]]; then
|
|
37
|
+
report_violation "marketplace.json" "marketplace.json not found"
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
if [[ $violations -gt 0 ]]; then
|
|
41
|
+
echo ""
|
|
42
|
+
echo "validate-plugin: FAIL ($violations issues)"
|
|
43
|
+
[[ "$WARN_ONLY" == true ]] && exit 0
|
|
44
|
+
exit 1
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Validate JSON
|
|
48
|
+
if ! jq empty "$PLUGIN_DIR/plugin.json" 2>/dev/null; then
|
|
49
|
+
report_violation "plugin.json" "plugin.json is not valid JSON"
|
|
50
|
+
fi
|
|
51
|
+
if ! jq empty "$PLUGIN_DIR/marketplace.json" 2>/dev/null; then
|
|
52
|
+
report_violation "marketplace.json" "marketplace.json is not valid JSON"
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
if [[ $violations -gt 0 ]]; then
|
|
56
|
+
echo ""
|
|
57
|
+
echo "validate-plugin: FAIL ($violations issues)"
|
|
58
|
+
[[ "$WARN_ONLY" == true ]] && exit 0
|
|
59
|
+
exit 1
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# Extract fields
|
|
63
|
+
plugin_name=$(jq -r '.name' "$PLUGIN_DIR/plugin.json")
|
|
64
|
+
plugin_version=$(jq -r '.version' "$PLUGIN_DIR/plugin.json")
|
|
65
|
+
market_name=$(jq -r '.name' "$PLUGIN_DIR/marketplace.json")
|
|
66
|
+
market_version=$(jq -r '.plugins[0].version' "$PLUGIN_DIR/marketplace.json")
|
|
67
|
+
|
|
68
|
+
# Check name match
|
|
69
|
+
if [[ "$plugin_name" != "$market_name" ]]; then
|
|
70
|
+
report_violation "plugin.json" "name mismatch: plugin.json='$plugin_name' marketplace.json='$market_name'"
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
# Check version match
|
|
74
|
+
if [[ "$plugin_version" != "$market_version" ]]; then
|
|
75
|
+
report_violation "plugin.json" "version mismatch: plugin.json='$plugin_version' marketplace.json='$market_version'"
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
if [[ $violations -gt 0 ]]; then
|
|
79
|
+
echo ""
|
|
80
|
+
echo "validate-plugin: FAIL ($violations issues)"
|
|
81
|
+
[[ "$WARN_ONLY" == true ]] && exit 0
|
|
82
|
+
exit 1
|
|
83
|
+
else
|
|
84
|
+
echo "validate-plugin: PASS"
|
|
85
|
+
exit 0
|
|
86
|
+
fi
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# validate-policies.sh — Validate policy files exist and policy-check runs clean
|
|
3
|
+
# Used by validate-all.sh. Pass --warn to use advisory mode.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
+
TOOLKIT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
8
|
+
POLICY_DIR="$TOOLKIT_ROOT/policies"
|
|
9
|
+
WARN=false
|
|
10
|
+
EXIT_CODE=0
|
|
11
|
+
|
|
12
|
+
[[ "${1:-}" == "--warn" ]] && WARN=true
|
|
13
|
+
[[ "${1:-}" == "--help" || "${1:-}" == "-h" ]] && { echo "Usage: validate-policies.sh [--warn]"; exit 0; }
|
|
14
|
+
|
|
15
|
+
# Check policies directory exists
|
|
16
|
+
if [[ ! -d "$POLICY_DIR" ]]; then
|
|
17
|
+
echo "FAIL: policies/ directory not found"
|
|
18
|
+
[[ "$WARN" == "true" ]] && exit 0 || exit 1
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
# Check required policy files exist
|
|
22
|
+
required=(universal python bash testing)
|
|
23
|
+
for name in "${required[@]}"; do
|
|
24
|
+
if [[ ! -f "$POLICY_DIR/$name.md" ]]; then
|
|
25
|
+
echo "FAIL: missing policy file: policies/$name.md"
|
|
26
|
+
EXIT_CODE=1
|
|
27
|
+
fi
|
|
28
|
+
done
|
|
29
|
+
|
|
30
|
+
# Check policy files are non-empty
|
|
31
|
+
for f in "$POLICY_DIR"/*.md; do
|
|
32
|
+
if [[ ! -s "$f" ]]; then
|
|
33
|
+
echo "FAIL: empty policy file: $f"
|
|
34
|
+
EXIT_CODE=1
|
|
35
|
+
fi
|
|
36
|
+
done
|
|
37
|
+
|
|
38
|
+
if [[ $EXIT_CODE -eq 0 ]]; then
|
|
39
|
+
echo "validate-policies: PASS"
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
[[ "$WARN" == "true" ]] && exit 0 || exit $EXIT_CODE
|