← Files ForgeMindARCHIVED FILE
src/project-documents.mjs
1.37 KB · Oct 2, 2026 · 00:31 UTC
import path from 'node:path';
import { writeTextAtomic } from './io.mjs';
import { assertContained } from './paths.mjs';
import { artifactMetadata } from './artifact-store.mjs';
export async function publishProjectDocument({ workspace, name, title, body }) {
if (!/^[a-z0-9-]+\.md$/.test(name)) throw new Error(`Invalid ForgeMind document name: ${name}`);
// `--artifacts none` promises one-shot output with no persisted state, including
// human-facing project documents. All other modes deliberately publish the
// concise decision record while keeping the detailed JSON state external.
if (artifactMetadata().artifactMode === 'none') return null;
const target = assertContained(workspace, path.join(workspace, 'docs', 'forgemind', name));
const content = `# ${title}\n\n> Generated by ForgeMind. Inputs and assumptions must be reviewed before a business decision.\n\n${body.trim()}\n`;
await writeTextAtomic(target, content);
return target;
}
export function markdownTable(rows) {
if (!rows.length) return '_No data available._';
const columns = Object.keys(rows[0]);
const cell = (value) => String(value ?? '—').replaceAll('|', '\\|').replaceAll('\n', ' ');
return [`| ${columns.join(' | ')} |`, `| ${columns.map(() => '---').join(' | ')} |`, ...rows.map((row) => `| ${columns.map((column) => cell(row[column])).join(' | ')} |`)].join('\n');
}
SHA-256: 63c1220d8b286af9231c6c9e61de0ce50c82d335ddadf5ea555c89b9e5233c9e