Guidance for agentic coding in this repo.
- We typically create a git worktree per feature.
- If you are in a feature worktree, the project root is the worktree root (paths may differ).
- When planning in a feature worktree, use
FEATURE_TODO.mdin the worktree root to track plan and progress. If you are building and this file is present, check whether it should be updated.
Issues are tracked in GitHub Issues for cashubtc/coco. External pull requests are not a
triage request surface. See docs/agents/issue-tracker.md.
Use the default mattpocock/skills triage label vocabulary. See docs/agents/triage-labels.md.
Use a single-context domain doc layout. See docs/agents/domain.md.
- Prefer Conventional Commit style messages.
- Prefer scoped commit and PR titles when the affected package or area is clear (for example
fix(core): ...orfeat(react): ...). - Use unscoped titles for repo-wide changes when a single package scope would be misleading.
- packages/core: core TS library (services, models, repositories, tests).
- packages/react: React hooks/providers for core (Vite build, ESLint).
- packages/sqlite3: SQLite3 adapter (bun tests).
- packages/indexeddb: IndexedDB adapter (bun + vitest browser tests).
- packages/expo-sqlite: Expo SQLite adapter.
- packages/adapter-tests: contract test helpers.
- packages/cocod: Cashu wallet CLI + daemon on the workspace packages (private, bun tests).
- packages/docs: VitePress docs site.
api/exposes public API wrappers.services/holds business logic and orchestration.operations/implements send/melt flows.infra/contains transport/request helpers and subscriptions.repositories/defines interfaces and memory adapters.models/andtypes.tshold domain types/errors.
- Use Bun workspaces; root scripts call
bun run --filter='pkg' .... tsdownbuilds most packages (ESM + CJS).- React package builds with
tsc -b+vite. - Docs use VitePress.
- No root ESLint config; only React package is linted.
bun install
- All packages:
bun run build - Core:
bun run --filter='@cashu/coco-core' build - Adapter tests:
bun run --filter='@cashu/coco-adapter-tests' build - IndexedDB:
bun run --filter='@cashu/coco-indexeddb' build - Expo SQLite:
bun run --filter='@cashu/coco-expo-sqlite' build - SQLite3:
bun run --filter='@cashu/coco-sqlite' build - React:
bun run --filter='@cashu/coco-react' build - Docs:
bun run docs:build
- All packages:
bun run typecheck - Core:
bun run --filter='@cashu/coco-core' typecheck - IndexedDB:
bun run --filter='@cashu/coco-indexeddb' typecheck - Expo SQLite:
bun run --filter='@cashu/coco-expo-sqlite' typecheck - SQLite3:
bun run --filter='@cashu/coco-sqlite' typecheck - React (project refs):
bun run --filter='@cashu/coco-react' typecheck - cocod:
bun run --filter='cocod' typecheck(build the workspace first; it resolves core through dist/)
- React only:
bun run --filter='@cashu/coco-react' lint
- Core all tests:
bun run --filter='@cashu/coco-core' test - Core unit:
bun run --filter='@cashu/coco-core' test:unit - Core integration:
bun run --filter='@cashu/coco-core' test:integration - SQLite3 adapter:
bun run --filter='@cashu/coco-sqlite' test - IndexedDB adapter:
bun run --filter='@cashu/coco-indexeddb' test - IndexedDB browser tests:
bun run --filter='@cashu/coco-indexeddb' test:browser - Expo SQLite tests (no script):
bun --cwd packages/expo-sqlite test - cocod:
bun run --filter='cocod' test(build the workspace first) - React package has no tests yet.
- Bun file:
bun run --filter='@cashu/coco-core' test -- test/unit/Manager.test.ts - Bun by name:
bun run --filter='@cashu/coco-core' test -- -t "initializeCoco" test/unit/Manager.test.ts - SQLite3 file:
bun run --filter='@cashu/coco-sqlite' test -- src/test/integration.test.ts - IndexedDB file:
bun run --filter='@cashu/coco-indexeddb' test -- src/test/integration.test.ts - Vitest browser file:
bun run --filter='@cashu/coco-indexeddb' test:browser -- src/test/integration.test.ts - Run all browsers locally:
CI=1 bun run --filter='@cashu/coco-indexeddb' test:browser
- Dev server:
bun run docs:dev - Preview build:
bun run docs:preview - Package-local:
bun --cwd packages/docs run docs:dev
- Prettier config in
.prettierrc: single quotes, 100 char width, trailing commas. - Indentation is 2 spaces, no tabs.
- Use semicolons (matches existing files).
- Keep lines <= 100 chars where practical.
- Packages are ESM (
"type": "module"); useimport/export. moduleResolution: "bundler"andverbatimModuleSyntaxare on.- Use
import typefor type-only imports. allowImportingTsExtensionsis enabled; keep.tsextensions on local imports where used.strict,noUncheckedIndexedAccess, andnoImplicitOverrideare enabled.- React package also enables
noUnusedLocals/noUnusedParameters; core adapters do not. - Avoid
any; if required, keep it localized and add an eslint disable comment only if needed.
- Order: external first, then internal/alias, then relative.
- Prefer named exports; default exports are rare (React hooks may default-export).
- Use path aliases in core (
@core/*) when already established. - Keep import ordering consistent within a file; don't churn order without reason.
- Classes and types:
PascalCase. - Functions/variables:
camelCase. - Constants:
SCREAMING_SNAKE_CASEwhen truly constant. - Repositories are
XxxRepositoryimplementations (e.g.,SqliteProofRepository). - React hooks:
useXand file namesuseX.ts. - React components:
PascalCasefile names matching component names. - Test files:
*.test.tsundertest/unitortest/integration.
- Validate inputs early; return empty arrays for no-op cases (common pattern).
- Prefer domain errors in
packages/core/models/Error.tsfor protocol/state failures. - Include
causewhen wrapping errors; preserve original error objects. - Log with context:
logger?.info('message', { mintUrl, ... }). - Avoid swallowing exceptions; either handle and log or rethrow.
- Use structured logging across services (
debug/info/warn/error). - Emit
EventBusevents when state changes in core services. - Avoid emitting events from adapters unless that interface requires it.
- Repositories are transactional; keep operations atomic.
- Pre-check invariants (existence, state, reservation) before mutating.
- Serialize JSON fields consistently and defensively parse.
- Normalize mint URLs with
normalizeMintUrl()before persistence.
- Public exports go through each package's
index.ts. - Update
index.tswhen adding new public types/services. - Keep adapter packages exporting repository classes from
src/index.ts. - Avoid exporting internal helpers from package roots.
- Use JSDoc on public APIs and non-obvious flows.
- Keep section dividers and headings consistent with existing style.
- Avoid inline comments for trivial code.
- ESLint config:
packages/react/eslint.config.js. - Keep hooks rules clean (
react-hooks). - Use
useCallback/useMemowhen a value is referenced in deps arrays. - When catching unknown errors in hooks, normalize via
e instanceof Error ? e : new Error(String(e)).
- Use
bun:test(describe,it,expect,mock). - Prefer Bun
mock()for test doubles and spies. - Assert mock usage with
toHaveBeenCalled*ormock.callsinstead of manual counters. - Keep async tests
asyncandawaitpromises; avoid racey timers unless needed. - Browser tests run via Playwright in Vitest; see
packages/indexeddb/vitest.config.ts.
- Build artifacts live in
dist/; do not edit generated files. tsdownbuilds ESM and CJS; keep entry points inindex.ts.
- No
.cursor/rules,.cursorrules, or.github/copilot-instructions.mdfound.