Opencode TypeScript
Code like the opencode core team. This skill contains real code extracted from the repo — complete implementations, not abstract rules. Follow the workflow below based on your task.
Implement (new code)
Follow these phases in order:
1. Orient — where does this go?
Load architecture.md. Find:
- Which module owns this behavior
- What the dependency direction allows
- What file naming convention to follow
- Whether this is a new module or an addition to an existing one
2. Gather — what already exists?
Load helpers-deep-dive.md. Before writing ANY utility:
- Check if it already exists in
util/,effect/,bus/,sync/ - Check the usage matrix to see how other modules use it
- If it exists, use it. If it doesn't, inline first — extract only when awkwardness repeats.
For quick lookups: primitives.md (shorter, import paths + signatures only)
3. Build — write the code
Load the reference that matches what you're building:
| Building... | Load this | |---|---| | Service module (namespace + Effect service + schemas + events) | service-module.md | | Tool or modifying tool behavior | tool-module.md | | Database tables, schemas, events, error types | schemas-and-state.md | | Server routes, config, plugins, project lifecycle | server-and-routes.md | | Tests | test-writing.md |
4. CHECK GATE — code MUST pass all of these before proceeding
Load style-dna.md. If ANY of the following fail, fix the code before proceeding to Review:
- [ ] Single-word variable names where clear
- [ ] No
try/catch, noelse, noany, no unnecessary destructuring - [ ]
const+ ternary overlet+ mutation - [ ] snake_case Drizzle fields,
.annotate({ identifier })on boundary schemas,.annotate({ description })on fields - [ ] Effect Schema (
Schema.Struct/Schema.Schema.Type) for DTOs, events, tool params — not Zod - [ ] Effect is used for services, not plain async classes
- [ ] Module ends with
export * as X from "."— no in-fileexport namespace X {} - [ ] No patterns from Section 7 ("Things That Compile But Get Rejected") present in the diff
DO NOT proceed if any check fails. Go back to phase 3 and fix.
5. REVIEW GATE — REJECT the diff if any of these apply
Load review-voice.md. The diff MUST NOT contain any of the following. If it does, fix before submitting:
- [ ] Changes to files outside the scope of the task
- [ ]
as anyoras unknown ascasts - [ ] Custom utilities that duplicate community primitives or
@/util/*helpers - [ ] Provider-specific code that should live in models.dev
- [ ] Code removal without a clear reason documented in the commit
- [ ] Unexplained variable renames or structural changes
- [ ] Abstraction the core team would ask to remove (check refactoring-patterns.md)
Refactor (changing existing code)
1. Orient — what touches what?
Load architecture.md. Map the blast radius before changing anything.
2. Study — which pattern applies here?
Load refactoring-patterns.md. Start with the Decision Matrix at the top — match the code smell you see to the correct pattern. Then read the specific pattern section for real before/after diffs:
- Simplification patterns (removing unnecessary abstraction)
- Consolidation patterns (Bun → Node migration)
- Extraction patterns (pulling reusable utilities)
- Migration patterns (moving to Effect services)
- Deletion patterns (removing dead code)
- Stabilization patterns (fixing ordering/race conditions)
- Variant elimination (removing special cases)
3. Gather — can an existing utility replace this code?
Load helpers-deep-dive.md. The best refactor often replaces 20 lines with one utility call.
4. Check + Review
Same as implement phases 4-5: style-dna.md then review-voice.md.
Key decisions (always apply)
- Effect is mandatory — all services use
Context.Service/Layer/makeRuntime. No plain async classes. - One module, one self-barrel — write flat top-level exports (schemas,
Interface,Service,layer,defaultLayer), then close the file withexport * as X from ".". Consumers still writeimport { X } from "@/x"→X.Service; the barrel is the namespace. opencode dropped in-fileexport namespace X {}. - Effect Schema everywhere —
Schema.Struct+Schema.Schema.Type<typeof X>for DTOs, events, and tool params;.annotate({ identifier })on boundary schemas,.annotate({ description })on fields.Schema.TaggedErrorClassfor Effect errors.Newtype<Self>()("Name", Schema.String.check(...))(from@opencode-ai/core/schema) for branded IDs. Zod is no longer the boundary tool. - Event-sourced writes — mutations go through
SyncEvent.run→ projectors → SQLite. Direct DB writes only for non-event-sourced features. - No mocks in tests — use
tmpdir+Instance.provide+ real services. Mocks only for external SDKs. - Single-word variables —
state,pending,info,row,cfg,tx. Multi-word only when genuinely ambiguous.
Reference index
| File | Size | What it contains | |------|------|-----------------| | style-dna.md | 18K | Mandatory style rules, naming, control flow, 14 review traps | | primitives.md | 22K | Quick-lookup: every utility with import path + signature | | helpers-deep-dive.md | ~40K | Full deep-dive: every utility, every usage site, when NOT to use | | architecture.md | ~30K | Module map, dependency graph, data flow, file conventions | | service-module.md | 27K | Complete Question + Permission implementations | | tool-module.md | 28K | Full tool implementations, registry, prompt loop | | test-writing.md | 42K | 5 complete test files with all fixture patterns | | schemas-and-state.md | 36K | SQL tables, Effect Schema (Struct/annotate/Newtype), SyncEvent flow, errors | | server-and-routes.md | 32K | Routes, config, plugins, project lifecycle | | review-voice.md | ~25K | Real PR review comments from Dax + Aiden | | refactoring-patterns.md | ~25K | Real before/after diffs from cleanup commits |