Validate OKF bundle on edit
Catch a malformed knowledge entry the instant you save it
Whenever a file under okf/ changes, runs OKF v0.1 strict validation (frontmatter, required type, broken links, recommended fields) and surfaces errors immediately as context — instead of waiting for a manual validate pass. Silent when the bundle is conformant or when okf.mjs is absent.
What does the Validate OKF bundle on edit hook do?
Validate OKF bundle on edit is a Claude Code FileChanged hook. It fires automatically at that lifecycle event — outside the model, so it can't be skipped or forgotten. Catch a malformed knowledge entry the instant you save it.
Because it is a deterministic Node.js script, it executes on every matching event without relying on the model to remember — the guarantee that makes agentic workflows safe to automate.
Use cases
- Instant feedback while authoring OKF concepts
- Guarantee the knowledge bundle stays conformant to OKF v0.1
Tags
settings.json fragment
{
"hooks": {
"FileChanged": [
{
"hooks": [
{
"type": "command",
"command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/okf-validate-on-change.mjs"
}
]
}
]
}
}Script · .claude/hooks/okf-validate-on-change.mjs
#!/usr/bin/env node
// @hookstack okf-validate-on-change
// Valide le bundle OKF (frontmatter notamment) dès qu'un fichier okf/**/*.md
// change (FileChanged) — feedback immédiat au lieu d'attendre node scripts/okf.mjs validate.
import { execSync } from "node:child_process";
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
function defaultExec(projectDir) {
return execSync("node scripts/okf.mjs validate --strict --json", {
timeout: 15_000,
cwd: projectDir,
encoding: "utf8",
stdio: "pipe",
});
}
export function run(
input,
{ exec = defaultExec, projectDir = process.env.CLAUDE_PROJECT_DIR } = {},
) {
const filePath = input.file_path ?? "";
if (!/(^|\/)okf\/.*\.md$/.test(filePath) || !projectDir) return null;
let out;
try {
out = exec(projectDir);
} catch (e) {
out = e?.stdout ?? "";
}
let report;
try {
report = JSON.parse(out);
} catch {
return null; // sortie inattendue → ne pas bloquer sur un souci d'outillage
}
if (report.passed) return null; // conforme → silencieux
return {
hookSpecificOutput: {
hookEventName: "FileChanged",
additionalContext:
`okf/ bundle is INVALID against OKF v0.1 (strict mode):\n` +
report.errors.map((e) => `✗ ERROR ${e}`).join("\n") +
(report.errors.length && report.warnings.length ? "\n" : "") +
report.warnings.map((w) => `! warn ${w}`).join("\n") +
`\nFix the entries above (frontmatter, broken links) before continuing.`,
},
};
}
/* v8 ignore next 5 */
if (process.argv[1] === fileURLToPath(import.meta.url)) {
const input = JSON.parse(readFileSync(0, "utf8"));
const result = run(input);
if (result) process.stdout.write(JSON.stringify(result));
}