#!/usr/bin/env node // compose-org-profile.mjs // Purpose: wrap the NESTED org-profile YAML body (organisasjon:/program: ..., // read from stdin) in an OKF-compatible frontmatter document, prepending the // three top-level OKF keys the second-brain "Documents/kb" layout expects on a // concept file: type / resource / timestamp. Emits the full document to stdout, // meant to be piped into write-org-profile.mjs (which writes it atomically to // the reinstall-surviving home path ~/.claude/okr/org/profil.md). // // Why a dedicated composer (not lib/frontmatter.mjs writeFrontmatter): the org // profile is NESTED (organisasjon: { navn, type }, program: { ... }). The flat // writeFrontmatter would flatten/destroy that structure. We instead keep the // caller's nested YAML verbatim and only PREPEND OKF keys above it. // // Why ONLY type/resource/timestamp top-level: the home profile is read by the // flat parser (lib/frontmatter.mjs) in inject-okr-context.mjs via // get('navn')/get('id')/get('domene')/... A NEW top-level key colliding with // any of those (navn/name/id/fase/domene/sektor) would be matched FIRST and // shadow the nested value, breaking org/cycle resolution (the Critical risk). // type/resource/timestamp are read by NO consumer, so they are safe to prepend. // The nested organisasjon.type ("offentlig") is untouched and still unread. // // Single-block invariant: the OKF keys and the nested body MUST live in ONE // frontmatter block. A stray intermediate '---' would truncate the block the // flat parser reads (dropping organisasjon: -> get('navn') === null -> no // injection), so any fences the caller already put around the body are stripped // before re-wrapping. // // Testable clock seam: OKR_NOW (ISO-8601) overrides the wall clock so the // emitted timestamp is deterministic under test. // // Zero npm dependencies (node: builtins only). ASCII-clean identifiers. import { readFileSync } from 'node:fs'; // OKF resource pointer: where the canonical org profile lives. A bare string // pointer (OKF: "URL/path to the source"); no consumer reads it. const RESOURCE = '~/.claude/okr/org/profil.md'; let body = ''; try { body = readFileSync(0, 'utf8'); } catch { body = ''; } // Strip a leading/trailing frontmatter fence if the caller already wrapped the // body, so the result is exactly one frontmatter block (see single-block note). body = body.replace(/^\s*---\s*\r?\n/, ''); body = body.replace(/\r?\n---\s*\r?\n?\s*$/, '\n'); body = body.replace(/\s+$/, ''); const timestamp = process.env.OKR_NOW || new Date().toISOString(); const lines = [ '---', 'type: Organisasjonsprofil', `resource: ${RESOURCE}`, `timestamp: '${timestamp}'`, ]; if (body) lines.push(body); lines.push('---', ''); process.stdout.write(lines.join('\n'));