AGENTS.md@plugins/prismantis · git:20261022.7f1445e · 2026-10-22 · sha256 c8854aab247505b2
AGENTS.md@plugins/prismantis git:20261022.7f1445eA
Immutable. This exact content never changes and is served at /api/v1/blob/c8854aab247505b2.
# AGENTS.md
Prismantis is a Claude Code mod that redraws assistant replies with themeable colors: tables, headings, lists, quotes, code, numbers, paths, links and mermaid diagrams. It ships as a single plugin from this repo, which is also its own marketplace.
## Layout
- `hooks/register.tsx` holds every hook and all code that calls `$`. Every other `hooks/*.ts(x)` file is pure.
- `.claude-plugin/plugin.json` is the manifest and every user option (`userConfig`); `types/index.d.ts` is the `$.prismantis` contract other mods type against.
- `hooks/vendor/` is generated by `npm --prefix scripts run build:vendor`, never edited by hand. The build patches beautiful-mermaid four times: adjustable chart size, a bar chart scale measured from the axis line, with bars drawn to the half cell in half blocks so they reach exactly the gridlines at or below their value and lines passing behind them, the edge-label fix from upstream PR 157, to drop once it merges, and flowchart node names that stop before an arrow's dashes, to drop once upstream issue 140 is fixed.
- `codex/`, `.codex-plugin/` and `.agents/plugins/marketplace.json` are the Codex plugin and its marketplace, generated from the Claude Code source by `npm --prefix scripts run build:codex`, never edited by hand: the hint from `HINT` and `FIGURE_HINT`, `$prismantis` from `showcaseText`, the `.tmTheme` files from `PRESETS` through `PRISM_COLORS`. PRs leave them alone. The release commit regenerates them, and CI checks them only while `plugin.json` holds a version with no tag yet.
## Verify
Run all of these before every commit:
```
claude plugin validate .
claude plugin test .
tsc -p .
```
`tsc -p .` needs `.claude-plugin/types/`, which appears after running `claude --plugin-dir .` once. CI gates live in `.github/workflows/ci.yml`.
Tests prove the tree; only a real render proves the look. Any change to what gets drawn needs a `live-check` render of `/prismantis demo` (and `/prismantis` when the help changed), looked at before the PR opens and attached to it.
Render and performance regressions:
```
npm --prefix scripts run snapshot # demo.md drawn in 3 themes vs tests/snapshots, color and structure
npm --prefix scripts run bench:check # timings vs docs/bench-baseline.json (x1.6, normalized by a calibration loop) and node counts (+10%)
```
CI gates snapshots and node counts but only prints timings (`bench:check -- --no-time`), because the calibration ratio differs between an M-series laptop and a Linux runner. Run the full check locally before a release. After an intended visual change run `snapshot:update` and review the diff; after an intended speed or size change run `npm --prefix scripts run bench -- --update`.
CI also fails when a file's cognitive complexity grows: the sum of how far each function goes past 15, as Biome measures it. Split the function instead. When a change lowers it, run `npm --prefix scripts run complexity:update` so the lower number becomes the new limit.
Claude Code refuses any tree over 20000 nodes and silently draws its own text, so a node-count rise lowers that ceiling (about 500 highlighted code lines, 1000 table rows).
## Rules
Code:
- No code comments. The why goes in the commit message or PR description.
- Never copy code from other projects, including other mods. Read them to learn the API, then write our own.
- Full customization is the product. When someone asks to hide, toggle or customize part of the drawing, the answer is yes unless there is a concrete reason to object (security, a mod-API limit, a real cost); treat such issues as accepted. Otherwise add no feature flags or config knobs. The exception is looks: a feature with more than one plausible look ships its top 3 or 4 styles behind one option (like `taskStyle`, `promptStyle`), the maintainer picks the default, every style is a step in `/prismantis setup`, and each style has a real screenshot in the README next to the option.
- Every user-visible feature ships with a sample in `/prismantis demo` (`showcaseText` in `hooks/help.ts`), and in the one-screen `/prismantis` help (`helpText`) when it fits. Extend the help tests in `tests/regressions.test.tsx` so dropping the sample fails.
Dependencies and licenses:
- Bundled code and copied palettes must be MIT. Check the license at the source repo, not a port or fork: GitHub's `license.spdx_id` misses licenses declared only in a README (Gruvbox), and ports can relicense (Tokyo Night's Neovim port is Apache-2.0, the original VS Code theme is MIT).
- Add a dependency only when writing it ourselves is unreasonable. Use its latest release, prefer well-starred maintained projects, and record it in docs/THIRD_PARTY_NOTICES.md. The bundled runtime dependencies today are beautiful-mermaid and Prism. esbuild is build-time only.
- Two external programs can run: the HTML table copy action runs macOS's built-in `osascript`, Windows' built-in `powershell.exe`, or an installed and running CopyQ on Linux, and LaTeX math runs RaTeX's `ratex-render`, installed by the person and off until they set `latex`. Nothing else runs.
- Plugin names cannot start with `claude-`, `anthropic-` or `cc-plugin-`, and must not use other products' trademarks (no "Codex" in names).
- Feature and fix PRs leave `docs/CHANGELOG.md` and the version alone, and say what changes for the user in the PR description's "What changes for the user" section. The release commit writes the version's CHANGELOG section from those, so parallel PRs never conflict over it.
## Mod API facts that bite
- `$` cannot cross an import. Any function that takes `$` must be declared in `hooks/register.tsx`. `claude plugin validate` does not catch this; `claude plugin test` does ("hooks module did not load").
- A render hook may not write `$.state`. Schedule work with `$.clock.after(0, ...)` and write from there; `read($, atom)` in the render subscribes and redraws when the value lands.
- The built-in `Markdown` element has no theming props, which is why we parse and draw markdown ourselves.
- `TextProps` has color, background, bold, italic, underline, strikethrough, dimColor and inverse. There is no font size on any surface.
- `Image` draws only in terminals with the kitty graphics protocol (kitty, Ghostty); elsewhere it shows its `alt` text.
- Installed (user-tier) plugins cannot change the system prompt: the built-in `sec-default` plugin skips their `prompt.compose`, `prompt.section` and `prompt.context` hooks. Add model-only context through `prompt.submit` instead. Tests do not run `sec-default`, so prove prompt changes in a live `claude --debug` session.
- `userConfig` values are flat primitives. A `string` field with `options` becomes a picker in `/config`.
- Hooks run without Node: no `require`, no bare package imports, no dynamic `import()`. Vendor libraries as one ESM file.
- The authoritative API is the `.d.ts` Claude Code lays in `.claude-plugin/types/` after `claude --plugin-dir .`. Grep it before guessing a prop or event.
## Test-kit gotchas
- `ui.find({ text: 'x' })` matches substrings, so it returns the parent line. Anchor it: `text: /^x$/`.
- A test whose plugin calls `next(e)` needs an engine stand-in: `on('ui.render', ($, e) => <Text>engine</Text>)`, returning a tree, not `null`.
- Loop visual tests over `['terminal', 'desktop'] as const`.
## Adding an option
1. Add the field to `userConfig` in `plugin.json` with a `title`, a `description` and a `default`.
2. Read it in `resolveStyle` in `hooks/theme.ts`, falling back safely on bad input.
3. Add a row to the README options table.
4. Add a test using `test(name, { options: {...} }, ...)`.
## Skills
| Skill | When |
| --- | --- |
| `.claude/skills/add-theme` | adding or renaming a color preset |
| `.claude/skills/demo` | printing the demo reply to eyeball every feature |
| `.claude/skills/live-check` | seeing a change render in a real session |
| `.claude/skills/drive-check` | pressing keys in a pane or button before merging, in a throwaway config |
| `.claude/skills/release` | cutting a version |