mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-02-15 10:53:18 +08:00
fix: skip code blocks in command cross-reference validation
The validator was matching example/template content inside fenced code blocks as real cross-references, causing false positives for evolve.md (example /new-table command and debugger agent). - Strip ``` blocks before running cross-reference checks - Change evolve.md examples to use bold instead of backtick formatting for hypothetical outputs All 261 tests pass.
This commit is contained in:
@@ -47,7 +47,7 @@ Example:
|
||||
- `new-table-step2`: "when adding a database table, update schema"
|
||||
- `new-table-step3`: "when adding a database table, regenerate types"
|
||||
|
||||
→ Creates: `/new-table` command
|
||||
→ Creates: **new-table** command
|
||||
|
||||
### → Skill (Auto-Triggered)
|
||||
When instincts describe behaviors that should happen automatically:
|
||||
@@ -74,7 +74,7 @@ Example:
|
||||
- `debug-step3`: "when debugging, create minimal reproduction"
|
||||
- `debug-step4`: "when debugging, verify fix with test"
|
||||
|
||||
→ Creates: `debugger` agent
|
||||
→ Creates: **debugger** agent
|
||||
|
||||
## What to Do
|
||||
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Validate command markdown files are non-empty and readable
|
||||
* Validate command markdown files are non-empty, readable,
|
||||
* and have valid cross-references to other commands, agents, and skills.
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const COMMANDS_DIR = path.join(__dirname, '../../commands');
|
||||
const ROOT_DIR = path.join(__dirname, '../..');
|
||||
const COMMANDS_DIR = path.join(ROOT_DIR, 'commands');
|
||||
const AGENTS_DIR = path.join(ROOT_DIR, 'agents');
|
||||
const SKILLS_DIR = path.join(ROOT_DIR, 'skills');
|
||||
|
||||
function validateCommands() {
|
||||
if (!fs.existsSync(COMMANDS_DIR)) {
|
||||
@@ -16,6 +20,35 @@ function validateCommands() {
|
||||
|
||||
const files = fs.readdirSync(COMMANDS_DIR).filter(f => f.endsWith('.md'));
|
||||
let hasErrors = false;
|
||||
let warnCount = 0;
|
||||
|
||||
// Build set of valid command names (without .md extension)
|
||||
const validCommands = new Set(files.map(f => f.replace(/\.md$/, '')));
|
||||
|
||||
// Build set of valid agent names (without .md extension)
|
||||
const validAgents = new Set();
|
||||
if (fs.existsSync(AGENTS_DIR)) {
|
||||
for (const f of fs.readdirSync(AGENTS_DIR)) {
|
||||
if (f.endsWith('.md')) {
|
||||
validAgents.add(f.replace(/\.md$/, ''));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build set of valid skill directory names
|
||||
const validSkills = new Set();
|
||||
if (fs.existsSync(SKILLS_DIR)) {
|
||||
for (const f of fs.readdirSync(SKILLS_DIR)) {
|
||||
const skillPath = path.join(SKILLS_DIR, f);
|
||||
try {
|
||||
if (fs.statSync(skillPath).isDirectory()) {
|
||||
validSkills.add(f);
|
||||
}
|
||||
} catch {
|
||||
// skip unreadable entries
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = path.join(COMMANDS_DIR, file);
|
||||
@@ -32,6 +65,56 @@ function validateCommands() {
|
||||
if (content.trim().length === 0) {
|
||||
console.error(`ERROR: ${file} - Empty command file`);
|
||||
hasErrors = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Strip fenced code blocks before checking cross-references.
|
||||
// Examples/templates inside ``` blocks are not real references.
|
||||
const contentNoCodeBlocks = content.replace(/```[\s\S]*?```/g, '');
|
||||
|
||||
// Check cross-references to other commands (e.g., `/build-fix`)
|
||||
// Skip lines that describe hypothetical output (e.g., "→ Creates: `/new-table`")
|
||||
const cmdRefs = contentNoCodeBlocks.matchAll(/^.*`\/([a-z][-a-z0-9]*)`.*$/gm);
|
||||
for (const match of cmdRefs) {
|
||||
const line = match[0];
|
||||
if (/creates:|would create:/i.test(line)) continue;
|
||||
const refName = match[1];
|
||||
if (!validCommands.has(refName)) {
|
||||
console.error(`ERROR: ${file} - references non-existent command /${refName}`);
|
||||
hasErrors = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check agent references (e.g., "agents/planner.md" or "`planner` agent")
|
||||
const agentPathRefs = contentNoCodeBlocks.matchAll(/agents\/([a-z][-a-z0-9]*)\.md/g);
|
||||
for (const match of agentPathRefs) {
|
||||
const refName = match[1];
|
||||
if (!validAgents.has(refName)) {
|
||||
console.error(`ERROR: ${file} - references non-existent agent agents/${refName}.md`);
|
||||
hasErrors = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check skill directory references (e.g., "skills/tdd-workflow/")
|
||||
const skillRefs = contentNoCodeBlocks.matchAll(/skills\/([a-z][-a-z0-9]*)\//g);
|
||||
for (const match of skillRefs) {
|
||||
const refName = match[1];
|
||||
if (!validSkills.has(refName)) {
|
||||
console.warn(`WARN: ${file} - references skill directory skills/${refName}/ (not found locally)`);
|
||||
warnCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Check agent name references in workflow diagrams (e.g., "planner -> tdd-guide")
|
||||
const workflowLines = contentNoCodeBlocks.matchAll(/^([a-z][-a-z0-9]*(?:\s*->\s*[a-z][-a-z0-9]*)+)$/gm);
|
||||
for (const match of workflowLines) {
|
||||
const agents = match[1].split(/\s*->\s*/);
|
||||
for (const agent of agents) {
|
||||
if (!validAgents.has(agent)) {
|
||||
console.error(`ERROR: ${file} - workflow references non-existent agent "${agent}"`);
|
||||
hasErrors = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +122,11 @@ function validateCommands() {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`Validated ${files.length} command files`);
|
||||
let msg = `Validated ${files.length} command files`;
|
||||
if (warnCount > 0) {
|
||||
msg += ` (${warnCount} warnings)`;
|
||||
}
|
||||
console.log(msg);
|
||||
}
|
||||
|
||||
validateCommands();
|
||||
|
||||
Reference in New Issue
Block a user