# PROJECT KNOWLEDGE BASE **Generated:** 2026-02-12 **Commit:** acf2f08 **Branch:** feat/tests ## OVERVIEW OpenPrism — OpenCode plugin providing 3-tier drawing: Mermaid diagrams (Tier 1), Matplotlib plots (Tier 2), AIGC image generation (Tier 3). TypeScript, ESM-only, `@opencode-ai/plugin` compatible. ## STRUCTURE ``` OpenPrism/ ├── src/ │ ├── index.ts # Plugin entry — wires tools + hooks, creates FileManager │ ├── types.ts # All shared types + DEFAULT_CONFIG │ ├── hooks/ │ │ ├── mermaid-renderer.ts # tool.execute.after — auto-renders ```mermaid blocks │ │ ├── system-prompt.ts # chat.system.transform — injects tier guidance │ │ └── session-compaction.ts # session.compacting — preserves asset summaries │ ├── renderers/ │ │ ├── mermaid-ssr.ts # mmdc wrapper: renderMermaid, validateMermaidSyntax, extractMermaidBlocks │ │ └── matplotlib-bridge.ts # Python subprocess: renderMatplotlib, detectPythonEnvironment, wrapScript │ ├── tools/ │ │ ├── render-mermaid.ts # Tier 1: explicit Mermaid render │ │ ├── analyze-structure.ts # Tier 1: project dir → Mermaid diagram │ │ ├── plot-data.ts # Tier 2: Matplotlib execution │ │ └── generate-image.ts # Tier 3: AIGC via MCP guidance │ └── utils/ │ ├── file-manager.ts # FileManager class — manifest-based asset CRUD, cleanup, size monitoring │ └── terminal-image.ts # Kitty/iTerm2/Sixel image display protocols ├── tests/ # Vitest — 40 tests, mirrors src/ structure ├── docs/plan.md # Original project plan (Chinese) ├── .opencode/plugins/ # Local plugin loader for E2E testing └── dist/ # tsc output — do not edit ``` ## WHERE TO LOOK | Task | Location | Notes | |------|----------|-------| | Add new tool | `src/tools/` + register in `src/index.ts` | Follow `createXxxTool(fileManager)` pattern | | Add new hook | `src/hooks/` + register in `src/index.ts` | Hook type from `Hooks` in `@opencode-ai/plugin` | | Change output format | `src/renderers/` | Mermaid: mmdc args. Matplotlib: wrapScript preamble | | Asset management | `src/utils/file-manager.ts` | Manifest at `{outputDir}/openprism-manifest.json` | | Config defaults | `src/types.ts` → `DEFAULT_CONFIG` | outputDir, tier toggles, cleanup thresholds | | Type definitions | `src/types.ts` | All render options, results, asset records | | Test a module | `tests/{module}.test.ts` | Real subprocess calls, temp dirs, no mocks | ## CODE MAP | Symbol | Type | Location | Role | |--------|------|----------|------| | `OpenPrismPlugin` | Plugin (async fn) | `src/index.ts:15` | Entry point — returns tools + hooks | | `FileManager` | Class | `src/utils/file-manager.ts:25` | Central asset tracker, used by all tools/hooks | | `renderMermaid` | Function | `src/renderers/mermaid-ssr.ts:44` | Calls mmdc, returns `{filePath, format, size}` | | `renderMatplotlib` | Function | `src/renderers/matplotlib-bridge.ts:57` | Python subprocess, Agg backend injection | | `extractMermaidBlocks` | Function | `src/renderers/mermaid-ssr.ts:99` | Regex extraction from markdown | | `validateMermaidSyntax` | Function | `src/renderers/mermaid-ssr.ts:112` | Checks first line against valid diagram types | | `detectPythonEnvironment` | Function | `src/renderers/matplotlib-bridge.ts:101` | Finds venv/conda/system Python | | `wrapScript` | Function | `src/renderers/matplotlib-bridge.ts:36` | Injects Agg backend + savefig, strips plt.show() | | `DEFAULT_CONFIG` | Const | `src/types.ts:186` | `.opencode/plots`, all tiers on, 500MB limit, 30d cleanup | ## CONVENTIONS - **ESM only** — `"type": "module"`, all imports use `.js` extensions in compiled output - **`import type`** — Use type-only imports for `@opencode-ai/plugin` types (avoids runtime ESM resolution bug in that package) - **Factory pattern** — Tools: `createXxxTool(fileManager)`. Hooks: `createXxxHook(fileManager | config)` - **No mocks in tests** — Tests use real mmdc, real Python, real temp directories. `beforeEach` creates tmpdir, `afterEach` cleans up - **Strict TypeScript** — `noUnusedLocals`, `noUnusedParameters`, `strict: true`. No `as any` or `@ts-ignore` - **Vitest inline dep** — `@opencode-ai/plugin` must be inlined in vitest config (broken internal `./tool` ESM import) ## ANTI-PATTERNS (THIS PROJECT) - No `as any`, `@ts-ignore`, `@ts-expect-error` — type safety is non-negotiable - No mocking in tests — real subprocess calls or skip the test - No `plt.show()` or `matplotlib.use()` in user scripts — `wrapScript` handles both - Do not edit `dist/` — generated by `tsc` ## COMMANDS ```bash npm run build # tsc → dist/ npm run typecheck # tsc --noEmit npm test # vitest run (40 tests, ~5s) npm run test:watch # vitest (watch mode) ``` ## NOTES - **`@opencode-ai/plugin` ESM bug**: Package's `dist/index.js` does `export * from "./tool"` without `.js` extension. Works at runtime in OpenCode (Bun), but fails in Node/vitest ESM. Workaround: `server.deps.inline` in vitest config. - **mmdc requires Chromium**: First run of mermaid-cli downloads Puppeteer's Chromium. Tests may be slow on first execution. - **Python detection order**: venv → conda (`CONDA_PREFIX`) → system. `detectPythonEnvironment` checks `{projectDir}/.venv/bin/python3` first. - **GitLab `main` is protected**: Push to feature branches, not main. Remote: `git@gitlab.mondorobotics.com:BrainholeCenter/openprism.git` - **OpenCode local plugin**: Place `.ts` file in `.opencode/plugins/` to load. Already configured at `.opencode/plugins/openprism.ts`. - **Asset manifest**: JSON at `.opencode/plots/openprism-manifest.json`. FileManager auto-persists on `recordAsset`, lazy-loads on `getAssets`.