← Files ForgeMindARCHIVED FILE

src/project-documents.mjs

1.37 KB · Oct 2, 2026 · 00:31 UTC

↓ Download file

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