+**Preamble lag**: lines before first imperative
+**Inferred script language**:
+
+## Corpus inventory
+
+| File | Lines | ~Tokens | Role |
+|---|---|---|---|
+| SKILL.md | 89 | 1329 | entrypoint |
+| references/patterns.md | 200 | 2200 | reference |
+| scripts/preflight.js | 678 | 5450 | script (invoked, not loaded) |
+| ... | ... | ... | ... |
+
+## Summary
+
+**Workflow patterns** (agent-judgment):
+
+| Priority | P1 | P2 | P3 | P5 | P8 | P10 | P12 | Total |
+|---|---|---|---|---|---|---|---|---|
+| HIGH | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| MEDIUM | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| LOW | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+
+**Loading patterns** (script-emitted, agent-validated):
+
+| Priority | P4 | P6 | P7 | P9 | P11 | P13 | P14 | P15 | Total |
+|---|---|---|---|---|---|---|---|---|---|
+| HIGH | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| MEDIUM | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 2 |
+| LOW | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+
+Pattern legend: P1 deterministic ops · P2 complex workflow · P3 file edits · P4 large inline content · P5 read-then-grep chain · P6 verbose prose · P7 repeated boilerplate · P8 conditionals · P9 description bloat · P10 phase-gated content · P11 verbose tool prose · P12 meta prose · P13 cross-file duplication · P14 eager-load · P15 preamble lag.
+
+## Findings — HIGH
+
+### [HIGH] P1 — Deterministic ops → script
+
+**Location**: `SKILL.md:L52-L104`
+**Excerpt**: "Run `git rev-parse`, then `my-cli whoami`, then parse output and branch..."
+**Issue**: Five sequential commands with output-parsing prose loaded on every trigger.
+**Fix**: Extract to `scripts/preflight.js` returning `{version, user, context}`; SKILL.md calls it once.
+**Est. tokens saved**: ~520
+
+
+
+## Findings — MEDIUM
+
+
+
+## Findings — LOW
+
+
+
+## Top 3 wins
+
+Tackle in this order for maximum context reduction:
+
+1. **P4 at `SKILL.md:L197-L303`** — saves ~1,060 tokens / trigger by moving Examples 1–8 to `assets/examples.md`.
+2. **P1 at `SKILL.md:L52-L104`** — preflight script collapses 5 sequential commands into one Read.
+3. **P6 at `SKILL.md:L10-L22`** — move Context block to `references/context.md`.
+
+## Unreferenced files
+
+
+
+These files exist in the folder but are never mentioned in SKILL.md. Review for dead weight:
+
+- `references/old-notes.md` (84 lines)
+
+## Broken references
+
+
+
+These references appear in SKILL.md but point to files that do not exist. Fix or remove:
+
+- `references/missing.md` — cited at `SKILL.md:L145`
+````
+
+---
+
+## Field rules
+
+- **``** — pull from `skillName` in preflight output.
+- **``** — `skillPath` from preflight, ending with a `/`.
+- **``** — current date in ISO format.
+- **`/10`** — final efficiency score per `references/patterns.md` → "Score formula appendix" (start from `efficiencyScore.raw`, then apply HIGH/MEDIUM adjustments, floor 1, round). Justification is one short clause naming the dominant penalty (e.g. "eager footprint dominates", "5 HIGH findings", "near-optimal").
+- **`////`** — from `loadedFootprint`. ` = eagerTotal`.
+- **`/`** — from `referenceUsage.eager` / `referenceUsage.lazy`.
+- **`` and ``** — from `efficiencyScore.frequency.tier` and `.multiplier`. If you override the tier in Phase 4, state the override in the reason.
+- **``** — from `skillMdMetrics.preambleLines`.
+- **Priority section order** — always HIGH first, then MEDIUM, then LOW. Omit any section with zero findings.
+- **Finding heading format** — `### [PRIORITY] P — ` — exactly that shape.
+- **Excerpt rule** — ≤20 words, verbatim from the quoted line range. No paraphrasing.
+- **Fix rule** — one sentence. If the fix involves a script, include the inferred language (e.g. "a Node script", "a Bash one-liner").
+- **Est. tokens saved rule** — for extraction patterns (P4/P6/P10), use `span.tokenEstimate` from the candidate. For collapse-into-script patterns (P1/P3/P5), estimate 0.6 × `span.tokenEstimate`. For description bloat (P9), the saving is the full `tokenEstimate` × number-of-sessions-per-day (use 1× as a per-session estimate). For P11, estimate 5 tokens × number of occurrences.
+- **Do not include** — a preamble, an executive summary paragraph, or recommendations beyond the Top 3 wins section.
diff --git a/.claude/skills/audit-skill-context/references/scope.md b/.claude/skills/audit-skill-context/references/scope.md
new file mode 100644
index 000000000..a6124975d
--- /dev/null
+++ b/.claude/skills/audit-skill-context/references/scope.md
@@ -0,0 +1,12 @@
+# Scope
+
+Boundary notes for `audit-skill-context`. Not loaded at runtime — kept for skill authors deciding whether this skill matches their need.
+
+## What this skill does NOT do
+
+- Rewrite or edit the target skill. The audit produces a report; the user decides what to apply.
+- Compare multiple skills against each other. Run the audit per skill.
+- Generate the scripts it recommends (e.g. extracting a P1 finding into `scripts/release.js`). The report identifies where a script would help; authoring it is a separate task.
+- Run benchmarks or measure runtime token cost. The `Context efficiency` score is qualitative — based on file size, eager-load footprint, and finding counts — not a measured live token count.
+- Track audit history or diff against prior audits.
+- Detect cross-skill duplication (audit is single-skill scoped).
diff --git a/.claude/skills/audit-skill-context/scripts/preflight.js b/.claude/skills/audit-skill-context/scripts/preflight.js
new file mode 100644
index 000000000..472e14c0a
--- /dev/null
+++ b/.claude/skills/audit-skill-context/scripts/preflight.js
@@ -0,0 +1,799 @@
+#!/usr/bin/env node
+/*
+ * Preflight for `audit-skill-context`. Walks the target skill folder, collects
+ * inventory, classifies citations as eager vs lazy, pre-fires the mechanical
+ * detectors (P4, P6, P7, P9, P11, P13, P14, P15), and computes a preliminary
+ * efficiency score. The agent then validates candidates and writes the report.
+ *
+ * Output JSON: see SKILL.md "Phase 1 — Run the preflight" for the fields the
+ * agent consumes. Pattern definitions and the score formula are canonical in
+ * references/patterns.md.
+ *
+ * Citation forms recognized in SKILL.md (the only canonical list):
+ * references/foo.md — relative path in prose
+ * @references/foo.md — at-path reference
+ * `assets/template.md` — backticked inline path
+ * path=references/foo.md — code-fence annotation
+ */
+
+const fs = require('fs');
+const path = require('path');
+const crypto = require('crypto');
+
+const BINARY_EXTS = new Set([
+ '.png', '.jpg', '.jpeg', '.gif', '.ico', '.svg',
+ '.woff', '.woff2', '.ttf', '.otf', '.eot',
+ '.pdf', '.zip', '.tar', '.gz',
+ '.mp4', '.mp3', '.wav',
+]);
+
+const REFERENCE_REGEX = /(?:references|agents|steps|scripts|assets|inputs)\/[A-Za-z0-9_\-./]+\.(?:md|txt|json|ts|js|py|sh|html)/g;
+
+const ROLE_BY_DIR = {
+ references: 'reference',
+ agents: 'agent',
+ steps: 'step',
+ assets: 'asset',
+ scripts: 'script',
+ inputs: 'input',
+};
+
+const IGNORED_FILES = new Set(['.DS_Store']);
+
+const REDUNDANT_PHRASES = [
+ /\buse when\b/gi,
+ /\btriggers? on\b/gi,
+ /\balso triggers?\b/gi,
+ /\bshould be used\b/gi,
+ /\buse this skill\b/gi,
+];
+
+const CONDITIONAL_KEYWORDS = [
+ /\bif\s+\w+/i,
+ /\bonly when\b/i,
+ /\bbefore composing\b/i,
+ /\bon demand\b/i,
+ /\bif uncertain\b/i,
+ /\bwhen uncertain\b/i,
+ /\bto validate\b/i,
+ /\bfor calibration\b/i,
+ /\bsanity[- ]check\b/i,
+ /\blazy[- ]?load/i,
+];
+
+const IMPERATIVE_VERB = /^(?:[*\-]\s+|\d+\.\s+)?(run|read|write|call|update|create|check|verify|walk|fetch|parse|extract|grep|edit|apply|emit|move|delete|remove|list|build|configure|install|launch|invoke|trigger|generate|compute|scan|inspect|review|analyze|audit|load|open|append|prepend|set|configure|push|pull|tag|commit|deploy)\b/i;
+
+const TOKEN_RATIO = 4; // chars per token (approximation)
+
+function tokenEstimate(charCount) {
+ return Math.round(charCount / TOKEN_RATIO);
+}
+
+function truncateExcerpt(text, maxWords = 20) {
+ const cleaned = String(text || '').replace(/\s+/g, ' ').trim();
+ if (!cleaned) return '';
+ const words = cleaned.split(' ');
+ if (words.length <= maxWords) return cleaned;
+ return words.slice(0, maxWords).join(' ') + '…';
+}
+
+function walk(dir, base = dir) {
+ const out = [];
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
+ if (IGNORED_FILES.has(entry.name)) continue;
+ const full = path.join(dir, entry.name);
+ if (entry.isDirectory()) {
+ out.push(...walk(full, base));
+ } else if (entry.isFile()) {
+ out.push(path.relative(base, full));
+ }
+ }
+ return out;
+}
+
+function getRole(rel) {
+ if (rel === 'SKILL.md') return 'SKILL.md';
+ const first = rel.split('/')[0];
+ return ROLE_BY_DIR[first] || 'other';
+}
+
+function isBinary(rel) {
+ return BINARY_EXTS.has(path.extname(rel).toLowerCase());
+}
+
+function safeRead(abs) {
+ try {
+ return fs.readFileSync(abs, 'utf8');
+ } catch {
+ return '';
+ }
+}
+
+function countLines(content) {
+ return content.length === 0 ? 0 : content.split('\n').length;
+}
+
+function readSkillName(frontmatter) {
+ const name = frontmatter.match(/^name:\s*['"]?([^'"\n]+?)['"]?\s*$/m);
+ return name ? name[1].trim() : null;
+}
+
+function parseFrontmatter(skillMdContent) {
+ const fm = skillMdContent.match(/^---\n([\s\S]*?)\n---/);
+ if (!fm) return { block: '', endLine: 0 };
+ const endLine = fm[0].split('\n').length;
+ return { block: fm[1], endLine };
+}
+
+// Extract the SKILL.md `description:` field and compute the metrics P9 needs:
+// word count, character count, token estimate, and a count of redundant
+// trigger-clause phrases ("Use when …", "Triggers on …", etc.). Returns null
+// if no description is present.
+function parseDescription(frontmatterBlock) {
+ if (!frontmatterBlock) return null;
+ // Capture from "description:" up to the next ":" line or end.
+ const m = frontmatterBlock.match(/^description:\s*([\s\S]*?)(?=\n[a-zA-Z_][\w-]*:\s|$)/m);
+ if (!m) return null;
+ let value = m[1].trim();
+ if (value.startsWith("'") && value.endsWith("'")) {
+ value = value.slice(1, -1).replace(/''/g, "'");
+ } else if (value.startsWith('"') && value.endsWith('"')) {
+ value = value.slice(1, -1).replace(/\\"/g, '"');
+ }
+ value = value.replace(/\s+/g, ' ').trim();
+ if (!value) return null;
+ const wordCount = value.split(/\s+/).length;
+ const charCount = value.length;
+ let redundantPhraseCount = 0;
+ for (const re of REDUNDANT_PHRASES) {
+ const matches = value.match(re);
+ if (matches) redundantPhraseCount += matches.length;
+ }
+ return {
+ text: value,
+ wordCount,
+ charCount,
+ tokenEstimate: tokenEstimate(charCount),
+ redundantPhraseCount,
+ };
+}
+
+// Pair triple-backtick fences into [startLine, endLine] blocks (1-indexed).
+// Used by P4 (large inline blocks) and to mask code fences out of prose-only
+// detectors (P6, P11, P14).
+function findFencedBlocks(lines) {
+ const blocks = [];
+ let openLine = -1;
+ for (let i = 0; i < lines.length; i++) {
+ if (/^\s*```/.test(lines[i])) {
+ if (openLine === -1) openLine = i + 1;
+ else {
+ blocks.push({ startLine: openLine, endLine: i + 1 });
+ openLine = -1;
+ }
+ }
+ }
+ return blocks;
+}
+
+// Flatten a list of fenced blocks into the set of all line numbers they cover,
+// for O(1) "is this line inside a fence?" checks.
+function buildFenceLineSet(blocks) {
+ const set = new Set();
+ for (const b of blocks) {
+ for (let i = b.startLine; i <= b.endLine; i++) set.add(i);
+ }
+ return set;
+}
+
+// Returns true if `matchStart` falls inside a double-quoted span on `line`.
+// Used to drop citations that appear in illustrative quoted prose (e.g.
+// "references/foo.md is the path") rather than real instructions.
+function isInDoubleQuotes(line, matchStart) {
+ const before = line.slice(0, matchStart);
+ let inQuote = false;
+ for (let i = 0; i < before.length; i++) {
+ if (before[i] === '"' && before[i - 1] !== '\\') inQuote = !inQuote;
+ }
+ return inQuote;
+}
+
+function findRepoRoot(start) {
+ let cur = path.resolve(start);
+ while (cur !== path.dirname(cur)) {
+ if (fs.existsSync(path.join(cur, '.git'))) return cur;
+ cur = path.dirname(cur);
+ }
+ return process.cwd();
+}
+
+// Heuristic: pick the script flavour the host repo already uses, so a P1/P3/P5
+// "extract to a script" recommendation lands in something that matches local
+// tooling. Falls back to bash when no manifest is recognized.
+function inferScriptLanguage(repoRoot) {
+ if (fs.existsSync(path.join(repoRoot, 'package.json'))) return 'node';
+ if (
+ fs.existsSync(path.join(repoRoot, 'pyproject.toml')) ||
+ fs.existsSync(path.join(repoRoot, 'requirements.txt')) ||
+ fs.existsSync(path.join(repoRoot, 'setup.py'))
+ ) return 'python';
+ return 'bash';
+}
+
+// First non-decoration line below the frontmatter that begins with an action
+// verb. Drives the P15 "preamble lag" metric — how many lines the agent reads
+// before reaching anything actionable.
+function findFirstImperativeLine(lines, frontmatterEndLine) {
+ for (let i = frontmatterEndLine; i < lines.length; i++) {
+ const raw = lines[i];
+ const trimmed = raw.trim();
+ if (!trimmed) continue;
+ if (trimmed.startsWith('#')) continue;
+ if (trimmed.startsWith('```')) continue;
+ if (trimmed.startsWith('|')) continue;
+ if (trimmed.startsWith('>')) continue;
+ if (trimmed.startsWith('
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
No output files found
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
No benchmark data available. Run a benchmark to see quantitative results here.
+
+
+
+
+
+
+
+
Review Complete
+
Your feedback has been saved. Go back to your Claude Code session and tell Claude you're done reviewing.
+
+
+
+
+
+
+
+
+
+
+