This file provides guidance for agentic coding agents operating in this repository.
An OpenCode plugin (opencode-git-memory) that automatically stores AI conversation history as git notes metadata on each commit. Built as a Bun-native TypeScript project — Bun is the runtime, package manager, and test runner. No Node.js, npm, pnpm, Jest, or Webpack.
- Hooks into OpenCode's
tool.execute.afterevent - Detects successful
git commitbash commands - Fetches session messages since the last commit
- Renders them as a Markdown transcript (text, reasoning, and tool parts only)
- Attaches the transcript to the new commit via
git notes --ref=refs/notes/opencode
- Append mode: Multiple commits in one session concatenate notes with
---separators - Custom git notes ref:
refs/notes/opencode(keeps notes separate from default ref) - Tool output hidden: Tool parts record name and status only — no input/output to avoid leaking file paths
- Session-scoped: Only messages from the current session, since the previous commit
- Noise filtering: Skips
snapshot,patch,step-start,step-finish, andcompactionparts
src/
├── index.ts ← Plugin entry point (exports GitMemory)
├── index.test.ts ← Plugin hook tests
├── transcript.ts ← renderTranscript() + MessageWithParts type
├── notes-reader.ts ← Git notes reading logic (branch detection, commit scanning, system prompt)
└── notes-reader.test.ts ← Tests for notes reader functions
dist/ ← Build output (gitignored, included in npm package)
tsconfig.json ← Base config (noEmit, for type-checking)
tsconfig.build.json ← Build config (emits .js + .d.ts to dist/)
package.json ← npm package config
bun installbun run buildbunx tsc --noEmitbun testbun test src/index.test.tsbun test --test-name-pattern "pattern here"bun test --watchAlways use Bun's built-in APIs instead of third-party equivalents:
| Task | Use | Do NOT use |
|---|---|---|
| Run a file | bun <file> |
node <file>, ts-node <file> |
| HTTP server | Bun.serve() |
express, fastify |
| SQLite | bun:sqlite |
better-sqlite3 |
| Redis | Bun.redis |
ioredis |
| Postgres | Bun.sql |
pg, postgres.js |
| WebSocket | built-in WebSocket |
ws |
| File I/O | Bun.file() |
node:fs readFile/writeFile |
| Shell commands | Bun.$\cmd`` |
execa, child_process |
| Environment vars | automatic .env loading |
dotenv |
| Frontend | Bun.serve() with HTML imports |
vite, webpack, esbuild |
| Testing | bun test |
jest, vitest |
Key settings from tsconfig.json:
target/lib:ESNext— use modern JS, no polyfills needed.module: "Preserve"+moduleResolution: "bundler"— Bun bundler mode; preserves ESM as-is.allowImportingTsExtensions: true— import files with explicit.ts/.tsxextensions.verbatimModuleSyntax: true— type-only imports must useimport type.noEmit: true— TypeScript is for type-checking only; Bun handles transpilation.strict: true— all strict checks enabled.noUncheckedIndexedAccess: true— array/object index access returnsT | undefined.noImplicitOverride: true— class method overrides must use theoverridekeyword.noUnusedLocals/noUnusedParameters: disabled — unused variables are tolerated.jsx: "react-jsx"— automatic JSX transform (noReactimport needed in JSX files).
- Use explicit
.ts/.tsxfile extensions in all local imports:import { foo } from "./foo.ts"; import type { Bar } from "./bar.ts";
- Use
import typefor type-only imports (required byverbatimModuleSyntax):import type { MyType } from "./types.ts";
- No path aliases configured — use relative paths only.
- All files are ESM (
"type": "module"in package.json); do not userequire().
- No Prettier or ESLint is configured. Follow consistent style manually.
- Use 2-space indentation (Bun/TypeScript community default).
- Trailing commas in multi-line arrays/objects/params.
- Single quotes for strings preferred; template literals for interpolation.
- Prefer explicit type annotations for function parameters and return types.
- Avoid
any; useunknownwhen the type is truly unknown. - Use
noUncheckedIndexedAccessdiscipline: always handle theundefinedcase for array/object index access. - Mark overriding class methods with
override:class Derived extends Base { override method() { ... } }
- Use
satisfiesoperator to validate objects against a type without widening.
- Files:
kebab-case.tsfor modules,PascalCase.tsfor classes/components. - Variables/functions:
camelCase. - Types/interfaces/classes:
PascalCase. - Constants:
SCREAMING_SNAKE_CASEfor true module-level constants;camelCasefor otherconstbindings. - Test files:
*.test.tsor*.spec.tsalongside the source file they test.
- Prefer returning typed errors or
Result-style patterns over throwing where practical. - When catching, avoid swallowing errors silently — log or re-throw.
- Use
instanceofchecks for typed error handling:try { ... } catch (err) { if (err instanceof SomeError) { ... } throw err; }
-
Use Bun's built-in
bun:test— do not install Jest or Vitest.import { test, expect, describe, beforeEach } from "bun:test"; describe("MyModule", () => { test("does something", () => { expect(foo()).toBe("bar"); }); });
-
Place test files next to the code they test, e.g.,
foo.ts→foo.test.ts. -
Test file discovery:
**/*.test.ts,**/*.test.tsx,**/*.spec.ts,**/*.spec.tsx.
- Bun automatically loads
.env— nodotenvpackage needed. - Do not commit
.envfiles (covered by.gitignore).
The .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc rule applies to all *.ts, *.tsx, *.html, *.css, *.js, *.jsx, and package.json files:
Default to using Bun instead of Node.js.
Bun.serve()supports WebSockets, HTTPS, and routes — don't use Express. HTML imports withBun.serve()for frontend — don't use Vite.Bun.$instead of execa. Bun automatically loads.env— don't use dotenv.