feat(config-audit): TOK flags skill description > 500 chars (v5 M2) [skip-docs]
- New Pattern F in TOK: low-severity finding when SKILL.md description > 500 chars - Scoped to discovery.files (project-local) — activeConfig.skills walk would pull in user/plugin skills out of project scope - New fixtures: skill-bloated (594-char desc) + skill-tight (46-char baseline) 574 → 576 tests, all green.
This commit is contained in:
parent
25ca6139b4
commit
9a44df22ac
4 changed files with 71 additions and 1 deletions
|
|
@ -26,7 +26,7 @@ import { stat } from 'node:fs/promises';
|
|||
import { readTextFile } from './lib/file-discovery.mjs';
|
||||
import { finding, scannerResult } from './lib/output.mjs';
|
||||
import { SEVERITY } from './lib/severity.mjs';
|
||||
import { findImports, parseJson } from './lib/yaml-parser.mjs';
|
||||
import { findImports, parseJson, parseFrontmatter } from './lib/yaml-parser.mjs';
|
||||
import { estimateTokens, readActiveConfig } from './lib/active-config-reader.mjs';
|
||||
|
||||
const SCANNER = 'TOK';
|
||||
|
|
@ -48,6 +48,10 @@ const MAX_IMPORT_DEPTH = 2;
|
|||
// any tool description loads. Heuristic for "context budget under pressure".
|
||||
const CASCADE_TOKEN_THRESHOLD = 10_000;
|
||||
|
||||
// v5 M2: SKILL.md `description` loads on every turn even when the body does
|
||||
// not. Anything past this hints the description is doing the body's job.
|
||||
const SKILL_DESCRIPTION_THRESHOLD = 500;
|
||||
|
||||
const HOTSPOTS_MAX = 10;
|
||||
|
||||
// v5 F7: shared evidence note appended to every TOK pattern finding.
|
||||
|
|
@ -334,6 +338,40 @@ export async function scan(targetPath, discovery) {
|
|||
}
|
||||
}
|
||||
|
||||
// ── Pattern F: SKILL.md description > 500 chars (v5 M2) ──
|
||||
// Scoped to discovery.files (project-local skill-md). The plan mentioned
|
||||
// walking activeConfig.skills, but that pulls in user's ~/.claude/skills
|
||||
// and installed plugin skills which are out-of-scope for a project audit
|
||||
// and add noise the user can't act on. Project-local discovery is what
|
||||
// /config-audit on a path is actually asking about.
|
||||
for (const f of discovery.files) {
|
||||
if (f.type !== 'skill-md') continue;
|
||||
const content = await readTextFile(f.absPath);
|
||||
if (!content) continue;
|
||||
filesScanned++;
|
||||
const fm = parseFrontmatter(content)?.frontmatter || null;
|
||||
const desc = (fm && typeof fm.description === 'string') ? fm.description : '';
|
||||
if (desc.length <= SKILL_DESCRIPTION_THRESHOLD) continue;
|
||||
const skillName = (fm && fm.name) || f.absPath.split('/').slice(-2, -1)[0] || f.absPath;
|
||||
findings.push(finding({
|
||||
scanner: SCANNER,
|
||||
severity: SEVERITY.low,
|
||||
title: 'Bloated skill description (loads on every turn)',
|
||||
description:
|
||||
`Skill "${skillName}" has a description of ${desc.length} characters ` +
|
||||
`(>${SKILL_DESCRIPTION_THRESHOLD}). The description block loads on every turn ` +
|
||||
'even when the skill body does not — long descriptions inflate per-turn cost.',
|
||||
file: f.absPath,
|
||||
evidence:
|
||||
`description_chars=${desc.length}; threshold=${SKILL_DESCRIPTION_THRESHOLD}; ` +
|
||||
`skill="${skillName}" — ${CALIBRATION_NOTE}`,
|
||||
recommendation:
|
||||
'Tighten the description to a single sentence (≤500 chars) covering trigger phrases ' +
|
||||
'only. Move detailed usage / examples into the SKILL.md body.',
|
||||
category: 'token-efficiency',
|
||||
}));
|
||||
}
|
||||
|
||||
// ── Pattern E: CLAUDE.md cascade > CASCADE_TOKEN_THRESHOLD (v5 M4) ──
|
||||
if (activeConfig?.claudeMd?.estimatedTokens > CASCADE_TOKEN_THRESHOLD) {
|
||||
const cascadeTokens = activeConfig.claudeMd.estimatedTokens;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue