prompt-architecturelisted
Install: claude install-skill nikzlabs/shipit
# Prompt architecture
LLM prompts — agent system instructions, voice cleanup, session naming — are **content, not logic**. Keep the two separated.
## Prompt *text* is data: it lives in `.md` files
Co-locate the `.md` with the code that composes it. Prose reviews as prose, diffs cleanly, and needs no backtick or `${}` escaping.
Load it with `loadPrompt(import.meta.url, "./x.md")` (`orchestrator/load-prompt.ts`), **at module top level** — once at init, never per call. A missing file then throws at boot rather than mid-turn.
**Not** a bundler `?raw` import: production runs TS via tsx with no bundler, so `fs.readFileSync(new URL(...))` is what actually works.
Examples: `agents/<id>/system-prompt.md`, `voice/cleanup-prompt.md`, `orchestrator/prompts/*.md`.
**A per-call hole is still data.** `session-namer.ts` loads `session-naming-prompt.md` at module level and fills its `{{MESSAGE}}` per call through `buildSessionNamePrompt`. The file read happens once; only the substitution is per call, which is composition and belongs in TypeScript. A prompt whose text varies per request is not a reason to inline it.
## Prompt *composition* is code
Axis branching and fragment selection stay in TypeScript. In `agent-instructions.ts`, `renderInstructions` fills `{{TOKEN}}` holes in `prompts/skeleton.md` via `fillPromptTokens`, which **throws on an unfilled token** — that's the guard ensuring no literal `{{FOO}}` ever reaches the model.
There are **two axes**: `agentId` (Parallel-session