
Validate Counts
- 109 installs
- 213 repo stars
- Updated August 4, 2026
- yonatangross/orchestkit
Helps with ai & agent building tasks.
About
validate-counts is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- validate-counts
- AI & Agent Building
- AI-coding skill
Validate Counts by the numbers
- 109 all-time installs (skills.sh)
- Ranked #4,092 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/yonatangross/orchestkit --skill validate-countsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 109 |
|---|---|
| repo stars | ★ 213 |
| Last updated | August 4, 2026 |
| Repository | yonatangross/orchestkit ↗ |
What it does
Helps with ai & agent building tasks.
Files
Validate Counts
Checks that hook, skill, and agent counts are consistent across all authoritative sources in OrchestKit. Outputs a comparison table and flags drift with precise file references.
Quick Start
# Full validation: counts src/ vs CLAUDE.md and manifests (run from repo root)
bash src/skills/validate-counts/scripts/validate-counts.sh
# Just get raw counts from src/
bash src/skills/validate-counts/scripts/count-all.shRules
| Category | Rule | Impact | Key Pattern |
|---|---|---|---|
| Count Sources | rules/sources-authoritative.md (load ${CLAUDE_SKILL_DIR}/rules/sources-authoritative.md) | HIGH | Filesystem is authoritative; derived sources must match |
| Drift Detection | rules/drift-reporting.md (load ${CLAUDE_SKILL_DIR}/rules/drift-reporting.md) | HIGH | Comparison table + flag with file:field references |
Total: 2 rules across 2 categories
Workflow
1. Run scripts/validate-counts.sh for full validation (counts + drift comparison), or scripts/count-all.sh for raw counts only 2. Read CLAUDE.md — extract counts from Project Overview and Version section 3. Read manifests/ork.json — check skill/agent/hook array lengths 4. Build the comparison table (see rules/drift-reporting.md for format) 5. Flag any mismatches with file + field references; otherwise output "All counts consistent."
References
Load on demand with Read("${CLAUDE_SKILL_DIR}/references/<file>"):
| File | Content |
|---|---|
count-locations.md | Where every count lives and why drift happens |
Related Skills
release-checklist— Uses validate-counts as step 5 of the release gatedoctor— Broader health check that includes count validationaudit-skills— Quality audit for skill structure and completeness
Common Mistakes
1. Counting from plugins/ instead of src/ — plugins/ may be empty after an interrupted build 2. Comparing against deleted alias manifests — only manifests/ork.json exists in v7 3. Forgetting the hook breakdown: global + agent-scoped + skill-scoped must sum to total
Count Locations in OrchestKit
Overview of every place counts appear and what they represent.
Where Counts Live
Source of Truth (filesystem)
| What | Path | How to Count |
|---|---|---|
| Skills | src/skills/*/ | Subdirectory count |
| Agents | src/agents/*.md | .md file count |
| Hooks | src/hooks/hooks.json → .hooks[] | Array length |
Derived Counts (must stay in sync)
| File | Field | Example |
|---|---|---|
CLAUDE.md | Project Overview line | "111 skills, 37 agents, 144 hooks" |
CLAUDE.md | Version section | "55 entries (17 event types, 12 dispatchers, 9 native async)" |
src/hooks/hooks.json | top-level description | May embed hook count |
manifests/ork.json | skills[] length, agents[] length | Counted from arrays |
Why Counts Drift
Counts in documentation and manifests must be updated manually after adding or removing components. Common drift scenarios:
1. Added a skill — src/skills/ count increases, but CLAUDE.md and manifests may not be updated until npm run build is run and CLAUDE.md is manually edited. 2. Added a hook — hooks.json entry count increases, but the description field and CLAUDE.md still show the old count. 3. Interrupted build — plugins/ is emptied at build start; if build fails mid-run, plugins are gone but src counts are fine.
Hook Breakdown
CLAUDE.md tracks hooks with a breakdown: N hooks (X global + Y agent-scoped + Z skill-scoped).
- Global: hooks that run on all Claude Code sessions
- Agent-scoped: hooks only active during named agent sessions
- Skill-scoped: hooks only active when a specific skill is running
The three numbers must sum to the total. The authoritative breakdown comes from src/hooks/hooks.json — each entry has a scope field or equivalent.
Rule Categories
1. Authoritative Sources (sources) — HIGH — 1 rule
Which files are the ground truth for hook, skill, and agent counts, and which are derived sources that must stay in sync.
sources-authoritative.md— Canonical count sources per component type, commands to read them, known legitimate differences
2. Drift Reporting (reporting) — HIGH — 1 rule
How to compare all sources and surface mismatches with enough detail to fix them immediately.
drift-reporting.md— Comparison table format, per-row MATCH/DRIFT classification, fix instruction format
[Rule Name]
[Brief description — 1-2 sentences.]
Incorrect:
// Bad patternCorrect:
// Good patternKey rules:
- [Rule 1]
- [Rule 2]
- [Rule 3]
Reference: [link]
Drift Detection and Reporting
After counting all sources, compare them and report using a structured table. Flag every mismatch with a specific file and field reference.
Comparison Table Format
Output one table row per source. Use MATCH or DRIFT in the Status column.
| Source | Skills | Agents | Hooks | Status |
|-------------------------------|--------|--------|-------|--------|
| src/skills/ (actual) | 63 | — | — | — |
| src/agents/ (actual) | — | 37 | — | — |
| src/hooks/hooks.json (actual) | — | — | 87 | — |
| CLAUDE.md Project Overview | 63 | 37 | 87 | MATCH |
| CLAUDE.md Version section | — | — | 85 | DRIFT |
| manifests/ork.json | 63 | 36 | 87 | MATCH |Flagging Drift
For each DRIFT row, output a specific fix instruction:
DRIFT: CLAUDE.md Version section says "85 entries" but hooks.json has 87.
Fix: Update src/hooks/README.md line ~5 and CLAUDE.md Version section to "87 entries"Include: 1. Which file has the wrong value 2. What the correct value is 3. Approximate line number or field name where the stale count appears
Known Legitimate Differences
These are NOT drift — do not flag them:
| Difference | Reason |
|---|---|
| Agent count in manifests = actual - 1 | Some agents may be internal-only and excluded from manifest |
Incorrect:
DRIFT: CLAUDE.md has wrong hook count.Correct:
DRIFT: CLAUDE.md Version section says "85 entries" but hooks.json has 87.
Fix: Update CLAUDE.md Version section (line ~42) to "87 entries"Key Rules
- If all counts match, output "All counts consistent." and stop
- Always show the full table even when there is no drift — it confirms what was checked
- Reference file + field, not just file, when flagging drift (e.g., "CLAUDE.md line 7, Project Overview")
Authoritative Count Sources
Each count has exactly one authoritative source (the filesystem/JSON) and multiple derived sources (docs/manifests) that must stay in sync.
Hooks
Authoritative: src/hooks/hooks.json — count entries in the top-level hooks array.
# Count hook entries
jq '.hooks | length' src/hooks/hooks.jsonDerived sources to check against:
CLAUDE.mdProject Overview line: "N hooks (X global + Y agent-scoped + Z skill-scoped)"CLAUDE.mdVersion section: "N entries (X global + Y agent-scoped + Z skill-scoped, ...)"src/hooks/hooks.jsontop-leveldescriptionfield (may embed count)manifests/ork.json— hook count in metadata
Skills
Authoritative: src/skills/ — count subdirectories (each directory = one skill).
# Count skill directories
ls -d src/skills/*/ | wc -lDerived sources:
CLAUDE.mdProject Overview: "N skills"manifests/ork.json— skill list length
Agents
Authoritative: src/agents/ — count .md files.
# Count agent files
ls src/agents/*.md | wc -lDerived sources:
CLAUDE.mdProject Overview: "N agents"manifests/ork.json— agent list length
Incorrect:
# Counting from generated plugins/ — stale if build was interrupted
ls -d plugins/ork/skills/*/ | wc -lCorrect:
# Always count from src/ — the authoritative source
ls -d src/skills/*/ | wc -lKey Rules
- Never count
plugins/— it's generated fromsrc/and may be stale if build was interrupted - Only
manifests/ork.jsonexists in v7 — no alias manifests to compare - Hook breakdown (global + agent-scoped + skill-scoped) must sum to the total count
#!/usr/bin/env bash
# count-all.sh — Count OrchestKit hooks, skills, and agents from authoritative sources
#
# Usage: ./scripts/count-all.sh [--json]
# --json Output raw counts as JSON for scripting
# --help Show this help message
#
# Run from the repo root. Outputs a summary table of actual counts.
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../.." && pwd)"
# Argument parsing
JSON_OUTPUT=false
while [[ $# -gt 0 ]]; do
case "$1" in
--json) JSON_OUTPUT=true; shift ;;
--help|-h)
echo "Usage: $0 [--json]"
echo "Count hooks, skills, and agents from authoritative sources."
echo "Run from the repo root or any subdirectory."
exit 0 ;;
*) echo "Unknown option: $1. Use --help." >&2; exit 2 ;;
esac
done
# --- Count skills ---
SKILLS_DIR="$REPO_ROOT/src/skills"
if [[ ! -d "$SKILLS_DIR" ]]; then
echo "Error: src/skills/ not found at $REPO_ROOT" >&2
exit 1
fi
SKILL_COUNT=$(find "$SKILLS_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l | tr -d ' ')
# --- Count agents ---
AGENTS_DIR="$REPO_ROOT/src/agents"
if [[ ! -d "$AGENTS_DIR" ]]; then
echo "Error: src/agents/ not found at $REPO_ROOT" >&2
exit 1
fi
AGENT_COUNT=$(find "$AGENTS_DIR" -maxdepth 1 -name "*.md" | wc -l | tr -d ' ')
# --- Count hooks ---
HOOKS_JSON="$REPO_ROOT/src/hooks/hooks.json"
if [[ ! -f "$HOOKS_JSON" ]]; then
echo "Error: src/hooks/hooks.json not found at $REPO_ROOT" >&2
exit 1
fi
if ! command -v jq &>/dev/null; then
echo "Error: jq is required. Install with: brew install jq" >&2
exit 1
fi
# hooks.json structure: event type → entries[] → each entry has .hooks[] commands
# Count = total commands across all entries across all event types
HOOK_COUNT=$(jq '[.hooks | to_entries[] | .value[] | .hooks | length] | add' "$HOOKS_JSON")
# --- Output ---
if $JSON_OUTPUT; then
echo "{\"skills\": $SKILL_COUNT, \"agents\": $AGENT_COUNT, \"hooks\": $HOOK_COUNT}"
else
echo "OrchestKit Actual Counts (from source)"
echo "======================================="
printf " Skills (src/skills/*/): %s\n" "$SKILL_COUNT"
printf " Agents (src/agents/*.md): %s\n" "$AGENT_COUNT"
printf " Hooks (src/hooks/hooks.json): %s\n" "$HOOK_COUNT"
echo ""
echo "Compare these against CLAUDE.md and manifests/ to detect drift."
fi
#!/usr/bin/env bash
# validate-counts.sh — Full count validation: counts from src/ vs CLAUDE.md and manifests
#
# Usage: ./scripts/validate-counts.sh [--help]
#
# Run from the repo root. Outputs a comparison table and exits non-zero if drift found.
# Requires: jq (brew install jq)
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../.." && pwd)"
DRIFT=0
while [[ $# -gt 0 ]]; do
case "$1" in
--help|-h)
echo "Usage: $0"
echo "Validates hook, skill, and agent counts across CLAUDE.md, hooks.json, and manifests."
echo "Exits 0 if all counts match, 1 if drift detected."
exit 0 ;;
*) echo "Unknown option: $1. Use --help." >&2; exit 2 ;;
esac
done
if ! command -v jq &>/dev/null; then
echo "Error: jq is required. Install with: brew install jq" >&2
exit 1
fi
# =============================================================================
# ACTUAL COUNTS (authoritative — from src/)
# =============================================================================
ACTUAL_SKILLS=$(find "$REPO_ROOT/src/skills" -mindepth 1 -maxdepth 1 -type d | wc -l | tr -d ' ')
ACTUAL_AGENTS=$(find "$REPO_ROOT/src/agents" -maxdepth 1 -name "*.md" | wc -l | tr -d ' ')
# hooks.json .hooks is an object keyed by event type → array of entries → each has .hooks array of commands
# Count = total commands across all entries across all event types
ACTUAL_HOOKS=$(jq '[.hooks | to_entries[] | .value[] | .hooks | length] | add' "$REPO_ROOT/src/hooks/hooks.json")
# =============================================================================
# CLAUDE.md — extract counts from Project Overview and Version section
# =============================================================================
CLAUDE_MD="$REPO_ROOT/CLAUDE.md"
# Project Overview line: "69 skills, 38 agents, 95 hooks"
CLAUDE_OVERVIEW=$(grep -m1 'skills.*agents.*hooks' "$CLAUDE_MD" || true)
CLAUDE_OV_SKILLS=$(echo "$CLAUDE_OVERVIEW" | grep -oE '[0-9]+ skills?' | grep -oE '[0-9]+' || echo "?")
CLAUDE_OV_AGENTS=$(echo "$CLAUDE_OVERVIEW" | grep -oE '[0-9]+ agents?' | grep -oE '[0-9]+' || echo "?")
CLAUDE_OV_HOOKS=$(echo "$CLAUDE_OVERVIEW" | grep -oE '[0-9]+ hooks?' | grep -oE '[0-9]+' || echo "?")
# Version section: "Hooks: 64 entries (...)"
CLAUDE_VER_LINE=$(grep -m1 'Hooks.*entries' "$CLAUDE_MD" || true)
if [[ -n "$CLAUDE_VER_LINE" ]]; then
CLAUDE_VER_HOOKS=$(echo "$CLAUDE_VER_LINE" | grep -oE '[0-9]+ entries' | grep -oE '[0-9]+' || echo "?")
else
CLAUDE_VER_HOOKS="?"
fi
# =============================================================================
# MANIFESTS — handle both "all" shorthand and explicit arrays
# =============================================================================
ORK_JSON="$REPO_ROOT/manifests/ork.json"
# Helper: if value is "all", return "all"; if array, return length; else "?"
manifest_count() {
local file="$1" field="$2"
local val
val=$(jq -r ".$field | type" "$file" 2>/dev/null) || { echo "?"; return; }
case "$val" in
string)
local str
str=$(jq -r ".$field" "$file")
if [[ "$str" == "all" ]]; then echo "all"; else echo "?"; fi ;;
array) jq ".$field | length" "$file" ;;
*) echo "?" ;;
esac
}
ORK_SKILLS=$(manifest_count "$ORK_JSON" skills)
ORK_AGENTS=$(manifest_count "$ORK_JSON" agents)
ORK_HOOKS=$(manifest_count "$ORK_JSON" hooks)
# =============================================================================
# COMPARISON HELPERS
# =============================================================================
status() {
local actual="$1" derived="$2"
if [[ "$derived" == "?" ]]; then
echo "SKIP"
elif [[ "$derived" == "all" ]]; then
echo "MATCH" # "all" means it includes everything from src/
elif [[ "$actual" == "$derived" ]]; then
echo "MATCH"
else
echo "DRIFT"
DRIFT=1
fi
}
# =============================================================================
# OUTPUT TABLE
# =============================================================================
echo ""
echo "OrchestKit Count Validation"
echo "==========================="
printf "%-32s %7s %7s %7s %8s\n" "Source" "Skills" "Agents" "Hooks" "Status"
printf "%-32s %7s %7s %7s %8s\n" "------" "------" "------" "-----" "------"
printf "%-32s %7s %7s %7s %8s\n" "src/skills/ (actual)" "$ACTUAL_SKILLS" "—" "—" "—"
printf "%-32s %7s %7s %7s %8s\n" "src/agents/ (actual)" "—" "$ACTUAL_AGENTS" "—" "—"
printf "%-32s %7s %7s %7s %8s\n" "src/hooks/hooks.json (actual)" "—" "—" "$ACTUAL_HOOKS" "—"
OV_STATUS=$(status "$ACTUAL_SKILLS" "$CLAUDE_OV_SKILLS")
[[ $(status "$ACTUAL_AGENTS" "$CLAUDE_OV_AGENTS") == "DRIFT" ]] && { OV_STATUS="DRIFT"; DRIFT=1; }
[[ $(status "$ACTUAL_HOOKS" "$CLAUDE_OV_HOOKS") == "DRIFT" ]] && { OV_STATUS="DRIFT"; DRIFT=1; }
printf "%-32s %7s %7s %7s %8s\n" "CLAUDE.md Project Overview" "$CLAUDE_OV_SKILLS" "$CLAUDE_OV_AGENTS" "$CLAUDE_OV_HOOKS" "$OV_STATUS"
VER_STATUS=$(status "$ACTUAL_HOOKS" "$CLAUDE_VER_HOOKS")
printf "%-32s %7s %7s %7s %8s\n" "CLAUDE.md Version section" "—" "—" "$CLAUDE_VER_HOOKS" "$VER_STATUS"
ORK_S="MATCH"
[[ $(status "$ACTUAL_SKILLS" "$ORK_SKILLS") == "DRIFT" ]] && { ORK_S="DRIFT"; DRIFT=1; }
[[ $(status "$ACTUAL_AGENTS" "$ORK_AGENTS") == "DRIFT" ]] && { ORK_S="DRIFT"; DRIFT=1; }
[[ $(status "$ACTUAL_HOOKS" "$ORK_HOOKS") == "DRIFT" ]] && { ORK_S="DRIFT"; DRIFT=1; }
printf "%-32s %7s %7s %7s %8s\n" "manifests/ork.json" "$ORK_SKILLS" "$ORK_AGENTS" "$ORK_HOOKS" "$ORK_S"
echo ""
# =============================================================================
# RESULT
# =============================================================================
if [[ $DRIFT -eq 0 ]]; then
echo "All counts consistent."
exit 0
else
echo "DRIFT DETECTED — update the stale sources listed above."
echo " - CLAUDE.md Project Overview: skills, agents, hooks counts"
echo " - CLAUDE.md Version section: hooks entries count"
echo " - manifests/ork.json: after npm run build"
exit 1
fi
{
"skill": "validate-counts",
"version": "2.0.0",
"testCases": [
{
"id": "all-counts-match",
"rule": null,
"query": "Validate that all counts are consistent across OrchestKit",
"expectedBehavior": [
"Runs scripts/validate-counts.sh from repo root to check all count sources",
"Compares skill, hook, and agent counts from src/ vs CLAUDE.md and manifests",
"Outputs a comparison table showing all sources agree",
"Reports 'All counts consistent.' with no drift flags"
]
},
{
"id": "hook-count-drifted",
"rule": "drift-reporting",
"query": "Check if counts are up to date after I added a new hook",
"expectedBehavior": [
"Detects hook count in CLAUDE.md Project Overview does not match hooks.json entry count",
"Flags drift with exact file and field reference (e.g., CLAUDE.md:line 5 says 87, found 88)",
"Outputs comparison table row showing src/ count vs CLAUDE.md count",
"Suggests updating CLAUDE.md Project Overview and Version section to match"
]
},
{
"id": "skill-count-drifted",
"rule": "sources-authoritative",
"query": "Validate counts — I just added a skill to src/skills/ but haven't rebuilt yet",
"expectedBehavior": [
"Uses src/ filesystem as authoritative source, not plugins/",
"Detects skill count mismatch between src/skills/ directories and manifest array length",
"Flags drift with precise reference to manifests/ork.json skills array",
"Uses only manifests/ork.json — no alias manifests exist in v7"
]
},
{
"id": "claude-md-outdated",
"rule": "drift-reporting",
"query": "CLAUDE.md Project Overview seems wrong, validate all counts",
"expectedBehavior": [
"Reads CLAUDE.md Project Overview to extract stated skill, agent, and hook counts",
"Reads manifests/ork.json to compare declared counts",
"Identifies which specific count in CLAUDE.md is stale",
"Outputs flags for each mismatched field with file:field location for easy correction"
]
}
]
}