UNPKG

framework

Version:

The (AI) Framework: turnkey, zero-config AI orchestration that wraps a coding-agent CLI (Claude Code) as a black box and takes you from an idea to a running app. Vite for AI.

61 lines 2.8 kB
import { readFile, readdir } from 'node:fs/promises'; import { join } from 'node:path'; import { readDataFile } from '../data-branch.js'; import { FLAT_TODO_FILE } from '../tickets.js'; /** * The plan/backlog document categories the dashboard surfaces in its sidebar * (#319, part of the MVP UI #309), so the human can read them beside the agent. * * The Framework's system prompt writes these per session (#323/#326): * `PLAN_<SESSION>.agent.md` (the plan for now) and `TODO_<SESSION>.agent.md` (the * backlog), where SESSION is a git-branch slug. The flat fallbacks are `PLAN.md` * (root) and the flat backlog (`backlog: true` reads `TODO_AGENTS.md` off the data * branch, its one location since #1582). Scoped and flat-root names are matched * against a flat readdir of the root, never taken from user input, so there is no * path traversal to guard against. */ export const DOC_CATEGORIES = [ { flat: 'PLAN.md', scoped: /^PLAN_[a-z0-9-]+\.agent\.md$/ }, { flat: 'TODO_AGENTS.md', backlog: true, scoped: /^TODO_[a-z0-9-]+\.agent\.md$/ }, ]; /** Cap a single doc so a runaway file can't bloat the docs payload. */ const MAX_DOC_BYTES = 200_000; /** * The workspace-root filenames to surface, in sidebar order: per category the flat * file (if present) then its session-scoped files (sorted). Every name is a bare * readdir entry matched against a fixed pattern, so none can traverse. Returns * empty when the workspace is missing or unreadable. */ /** * Read the surfaced plan/backlog docs, in sidebar order: per category the flat file (if present) * then its session-scoped files (sorted). The flat backlog comes off the data branch (#1582); * everything else is a workspace-root file. Missing or blank files are skipped; a file over the * size cap is truncated. Never throws — a read error just omits that doc. */ export async function readDocs(cwd) { let entries; try { entries = await readdir(cwd); } catch { return []; } const present = new Set(entries); const docs = []; const push = (name, content) => { if (!content?.trim()) return; docs.push({ name, content: content.length > MAX_DOC_BYTES ? content.slice(0, MAX_DOC_BYTES) + '\n\n… (truncated)' : content }); }; for (const cat of DOC_CATEGORIES) { if ('backlog' in cat) push(FLAT_TODO_FILE, await readDataFile(cwd, FLAT_TODO_FILE)); else if (present.has(cat.flat)) push(cat.flat, await readFile(join(cwd, cat.flat), 'utf8').catch(() => undefined)); for (const name of entries.filter(e => cat.scoped.test(e)).sort()) { push(name, await readFile(join(cwd, name), 'utf8').catch(() => undefined)); } } return docs; } //# sourceMappingURL=docs.js.map