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.

56 lines 2.79 kB
import { errorMessage } from './error-message.js'; /** * The `${{ ... }}` markdown-fragment layer (#350): the built-in system (#326) prompt embeds * JS expressions (e.g. a ternary on `tf.params.autopilot`), so the verbatim * template renders against a context at run time. Each fragment is a real JS * expression evaluated with `new Function` — that is arbitrary code execution, * so only ever render trusted templates (the built-in system prompt), never * user- or repo-supplied text. */ /** * One `${{ ... }}` fragment. Non-greedy, so it ends at the first *adjacent* `}}`: an expression * may nest braces, but never let two of them close together — `{a:{b:1}}` ends the fragment on the * inner pair, and what gets evaluated is not what was written. A space between them (`{b:1} }`) is * the whole fix. * * A rule of this language, not a bug waiting to be fixed. Replacing the language outright was * considered and declined, so the limit is permanent, and two places already bend around it (a * block of the system prompt that cannot ship verbatim, and the `maintenance` preset). Written * down here so the next one bends on purpose rather than debugging a truncated prompt. */ const FRAGMENT = /\$\{\{([\s\S]*?)\}\}/g; /** Thrown when a `${{ ... }}` fragment fails to evaluate or evaluates to `undefined`. */ export class TemplateFragmentError extends Error { /** The fragment's expression, as written in the template. */ fragment; constructor(fragment, detail) { super(`[framework] template fragment \${{${fragment}}} ${detail}`); this.name = 'TemplateFragmentError'; this.fragment = fragment; } } /** * Render a template by evaluating every `${{ <expression> }}` fragment against * `context` (each key becomes a variable the expression can read, e.g. `tf`). * The result is stringified in place; text outside fragments passes through * byte-identical. A fragment that throws or evaluates to `undefined` (almost * always a typo) throws a {@link TemplateFragmentError} rather than silently * degrading the prompt. */ export function renderTemplate(template, context) { const names = Object.keys(context); const values = names.map(name => context[name]); return template.replace(FRAGMENT, (_, expression) => { let result; try { result = new Function(...names, `'use strict'; return (${expression});`)(...values); } catch (err) { throw new TemplateFragmentError(expression, `failed to evaluate: ${errorMessage(err)}`); } if (result === undefined) throw new TemplateFragmentError(expression, 'evaluated to undefined (typo in the expression?)'); return String(result); }); } //# sourceMappingURL=prompt-template.js.map