Lift okr's reference okf-check.mjs into the catalog as the single cross-plugin acceptance gate for the OKF-compatible second-brain form (spec §3): - scripts/okf-check.mjs (+ vendored okf-frontmatter.mjs): verdict logic byte-identical to okr's reference impl, English output, zero deps, self-contained. - scripts/okf-check.test.mjs: 5 self-contained tests (temp-dir bundles). - spec §7 + §14: the shared checker now lives here; only TS/mjs reconciliation remains Stage-3, not required for the gate. log.md protocol §6: conformance is verified by the gate, not asserted — a plugin moves to 🟢 only after passing it. Verified: 33/33 catalog tests green (canonical glob form); verdict parity with okr's checker on okr fixtures (positive + negative missing-type); a scaffolded linkedin-studio brain/ validates clean (exit 0). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012GqEHp4uDiivfrAUjw4BdE
32 lines
1.2 KiB
JavaScript
32 lines
1.2 KiB
JavaScript
// okf-frontmatter.mjs
|
|
// Minimal flat-frontmatter reader for the shared OKF conformance checker.
|
|
// Vendored from okr's lib/frontmatter.mjs (the reference implementation) so the
|
|
// catalog-hosted checker is self-contained — zero npm dependencies, no cross-repo
|
|
// import. The checker only reads (never writes), so only parseFrontmatter().get is
|
|
// vendored. Parsing logic is kept byte-identical to okr's so verdicts stay in parity.
|
|
|
|
const FM_RE = /^---\n([\s\S]*?)\n---/;
|
|
|
|
export function parseFrontmatter(content) {
|
|
const match = String(content).match(FM_RE);
|
|
const raw = match ? match[1] : null;
|
|
|
|
const get = (key) => {
|
|
if (raw === null) return null;
|
|
const m = raw.match(new RegExp(`^\\s*${key}:\\s*(.*)$`, 'm'));
|
|
if (!m) return null;
|
|
let v = m[1].trim();
|
|
if (v === '') return null;
|
|
const q = v[0];
|
|
if (q === '"' || q === "'") {
|
|
const end = v.indexOf(q, 1);
|
|
if (end !== -1) return v.slice(1, end); // internal '#' preserved
|
|
v = v.slice(1); // unterminated quote: fall back to the rest
|
|
} else {
|
|
v = v.replace(/\s+#.*$/, '').trim(); // unquoted: strip trailing comment
|
|
}
|
|
return v === '' ? null : v;
|
|
};
|
|
|
|
return { raw, get };
|
|
}
|