* test: guard ARCHITECTURE.md component counts against drift (#2258) Add tests/architecture-counts.test.cjs — 3 tests that dynamically verify the "Total commands/workflows/agents" counts in docs/ARCHITECTURE.md match the actual *.md file counts on disk. Both sides computed at runtime; zero hardcoded numbers. Also corrects the stale counts in ARCHITECTURE.md: - commands: 69 → 74 - workflows: 68 → 71 - agents: 24 → 31 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(init): remove literal ~/.claude/ from deprecated root identifiers to pass Cline path-leak test The cline-install.test.cjs scans installed engine files for literal ~/.claude/(get-shit-done|commands|...) strings that should have been substituted during install. Two deprecated-legacy entries added by #2261 used tilde-notation string literals for their root identifier, which triggered this scan. root is only a display/sort key — filesystem scanning always uses the path property (already dynamic via path.join). Switching root to the relative form '.claude/get-shit-done/skills' and '.claude/commands/gsd' satisfies the Cline path-leak guard without changing runtime behaviour. Update skill-manifest.test.cjs assertion to match the new root format. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
60 lines
1.9 KiB
JavaScript
60 lines
1.9 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* Guards ARCHITECTURE.md component counts against drift.
|
|
*
|
|
* Both sides are computed at test runtime — no hardcoded numbers.
|
|
* Parsing ARCHITECTURE.md: regex extracts the documented count.
|
|
* Filesystem count: readdirSync filters to *.md files.
|
|
*
|
|
* To add a new component: append a row to COMPONENTS below and update
|
|
* docs/ARCHITECTURE.md with a matching "**Total <label>:** N" line.
|
|
*/
|
|
|
|
const { describe, test } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const ROOT = path.join(__dirname, '..');
|
|
const ARCH_MD = path.join(ROOT, 'docs', 'ARCHITECTURE.md');
|
|
const ARCH_CONTENT = fs.readFileSync(ARCH_MD, 'utf-8');
|
|
|
|
/** Components whose counts must stay in sync with ARCHITECTURE.md. */
|
|
const COMPONENTS = [
|
|
{ label: 'commands', dir: 'commands/gsd' },
|
|
{ label: 'workflows', dir: 'get-shit-done/workflows' },
|
|
{ label: 'agents', dir: 'agents' },
|
|
];
|
|
|
|
/**
|
|
* Parse "**Total <label>:** N" from ARCHITECTURE.md.
|
|
* Returns the integer N, or throws if the pattern is missing.
|
|
*/
|
|
function parseDocCount(label) {
|
|
const match = ARCH_CONTENT.match(new RegExp(`\\*\\*Total ${label}:\\*\\*\\s+(\\d+)`));
|
|
assert.ok(match, `ARCHITECTURE.md is missing "**Total ${label}:** N" — add it`);
|
|
return parseInt(match[1], 10);
|
|
}
|
|
|
|
/**
|
|
* Count *.md files in a directory (non-recursive).
|
|
*/
|
|
function countMdFiles(relDir) {
|
|
return fs.readdirSync(path.join(ROOT, relDir)).filter((f) => f.endsWith('.md')).length;
|
|
}
|
|
|
|
describe('ARCHITECTURE.md component counts', () => {
|
|
for (const { label, dir } of COMPONENTS) {
|
|
test(`Total ${label} matches ${dir}/*.md file count`, () => {
|
|
const documented = parseDocCount(label);
|
|
const actual = countMdFiles(dir);
|
|
assert.strictEqual(
|
|
documented,
|
|
actual,
|
|
`docs/ARCHITECTURE.md says "Total ${label}: ${documented}" but ${dir}/ has ${actual} .md files — update ARCHITECTURE.md`
|
|
);
|
|
});
|
|
}
|
|
});
|