mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-07 17:53:32 +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-step2`: "when adding a database table, update schema"
|
||||||
- `new-table-step3`: "when adding a database table, regenerate types"
|
- `new-table-step3`: "when adding a database table, regenerate types"
|
||||||
|
|
||||||
→ Creates: `/new-table` command
|
→ Creates: **new-table** command
|
||||||
|
|
||||||
### → Skill (Auto-Triggered)
|
### → Skill (Auto-Triggered)
|
||||||
When instincts describe behaviors that should happen automatically:
|
When instincts describe behaviors that should happen automatically:
|
||||||
@@ -74,7 +74,7 @@ Example:
|
|||||||
- `debug-step3`: "when debugging, create minimal reproduction"
|
- `debug-step3`: "when debugging, create minimal reproduction"
|
||||||
- `debug-step4`: "when debugging, verify fix with test"
|
- `debug-step4`: "when debugging, verify fix with test"
|
||||||
|
|
||||||
→ Creates: `debugger` agent
|
→ Creates: **debugger** agent
|
||||||
|
|
||||||
## What to Do
|
## What to Do
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
#!/usr/bin/env node
|
#!/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 fs = require('fs');
|
||||||
const path = require('path');
|
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() {
|
function validateCommands() {
|
||||||
if (!fs.existsSync(COMMANDS_DIR)) {
|
if (!fs.existsSync(COMMANDS_DIR)) {
|
||||||
@@ -16,6 +20,35 @@ function validateCommands() {
|
|||||||
|
|
||||||
const files = fs.readdirSync(COMMANDS_DIR).filter(f => f.endsWith('.md'));
|
const files = fs.readdirSync(COMMANDS_DIR).filter(f => f.endsWith('.md'));
|
||||||
let hasErrors = false;
|
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) {
|
for (const file of files) {
|
||||||
const filePath = path.join(COMMANDS_DIR, file);
|
const filePath = path.join(COMMANDS_DIR, file);
|
||||||
@@ -32,6 +65,56 @@ function validateCommands() {
|
|||||||
if (content.trim().length === 0) {
|
if (content.trim().length === 0) {
|
||||||
console.error(`ERROR: ${file} - Empty command file`);
|
console.error(`ERROR: ${file} - Empty command file`);
|
||||||
hasErrors = true;
|
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);
|
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();
|
validateCommands();
|
||||||
|
|||||||
Reference in New Issue
Block a user