Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
d96bec0
docs(domain): ADR-0012 v2-native-only; superseding notes on 0010/0011…
diegohb Oct 6, 2026
0aad337
docs(agents): repo AGENTS.md consistent with v2 — plugin-registered t…
diegohb Oct 6, 2026
f1606bf
docs(readme): v2-consistent surface — v2 docs badge link, Effect-firs…
diegohb Oct 6, 2026
451ba8a
docs(contributing): add CONTRIBUTING.md — v2 fact policy, gates, Effe…
diegohb Oct 6, 2026
af4fe4f
docs(references): rewrite plugins reference to v2 — Effect-first defi…
diegohb Oct 6, 2026
3a10a36
docs(references): rewrite config reference to v2 — key table from fac…
diegohb Oct 6, 2026
8b669ee
docs(references): rewrite agents reference to v2 — frontmatter mappin…
diegohb Oct 6, 2026
a7fe690
docs(references): rewrite commands reference to v2 — command fields, …
diegohb Oct 6, 2026
fe80de8
docs(references): rewrite tools reference to v2 — plugin-registered t…
diegohb Oct 6, 2026
fd9a67d
docs(references): rewrite skills reference to v2 — frontmatter fate, …
diegohb Oct 6, 2026
9af5971
docs(references): rewrite mcp-servers reference to v2 — snake_case oa…
diegohb Oct 6, 2026
8c02459
docs(references): live-knowledge fallback follows the source-of-truth…
diegohb Oct 6, 2026
e64d6fc
docs(references): rewrite oneshots to v2 — Effect-first plugin snippe…
diegohb Oct 6, 2026
063694f
docs(references): conformance checklist inverted for v2 — plugins key…
diegohb Oct 6, 2026
2b2f71a
test(gate): docs-fact gate — ban known-false v1 claims in guidance, r…
diegohb Oct 6, 2026
1bb0ff2
refactor(gate): class module per coding standards — no comments, help…
diegohb Oct 6, 2026
cb5c59e
docs(references): review fixes — facts §13 row 10 for legacy plugin t…
diegohb Oct 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ When encountering file references (e.g., @references/workflow.md), use Read tool
<identity>opencode-architect plugin</identity>
<capabilities>Agent orchestration, plugin registration, command/tool exposure</capabilities>
<scope>Creating and distributing OpenCode extensions (skills, commands, agents, plugins, tools)</scope>
<constraints>Copy transparency for skills/commands/agents; TypeScript plugins/tools in package</constraints>
<constraints>Copy transparency for skills/commands/agents; TypeScript plugins and plugin-registered tools in package</constraints>
</role>

<product_overview>
Expand All @@ -35,15 +35,15 @@ When encountering file references (e.g., @references/workflow.md), use Read tool
<stage name="Create">
- User creates extensions via delegation to specialist agents
- skill-creator, command-crafter, agent-designer, plugin-engineer, tool-builder, mcp-integrator
- Default outputs: `.opencode/skills/<name>/SKILL.md`, `.opencode/commands/<name>.md`, `.opencode/agents/<name>.md`, `.opencode/plugins/<name>.ts`, `.opencode/tools/<name>.ts`
- Default outputs: `.opencode/skills/<name>/SKILL.md`, `.opencode/commands/<name>.md`, `.opencode/agents/<name>.md`; custom tools and plugins are Effect-first plugin code in `.opencode/plugin/<name>.ts` (or `plugins/`) — v2 has no file-based tool definition
</stage>
<stage name="Iterate">
- User refines extensions based on usage feedback
- Project-local extensions stop here: no packaging required
</stage>
<stage name="Package" trigger="cross-project reuse (local file:/// package)">
- Delegate to `opencode-packager`
- Extracts `.opencode/` content to package-root `skills/`/`commands/`/`agents/` (no `assets/` wrapper), placed in this workspace (default) or a sibling `../opencode-<name>/` directory; creates `plugin.ts`, `package.json`, `tsconfig.json`
- Extracts `.opencode/` content to package-root `skills/`/`commands/`/`agents/` (no `assets/` wrapper), placed in this workspace (default) or a sibling `../opencode-<name>/` directory; creates a default-only `index.ts`, the Effect-first plugin definition in `src/plugin.ts`, `package.json` (declaring `exports["./server"]`), `tsconfig.json`
</stage>
<stage name="Publish" trigger="public sharing (npm registry)">
- Delegate to `opencode-publisher`, fed by the packager's output
Expand Down Expand Up @@ -74,10 +74,10 @@ When encountering file references (e.g., @references/workflow.md), use Read tool
<principle name="Copying_for_Transparency">
- Skills, commands, agents COPIED to consumer's `.opencode/` (not via config.skills.paths)
- End-users can see, read, modify extension content locally
- Plugins and tools remain as TypeScript in package
- Plugins and plugin-registered tools remain as TypeScript in package
</principle>
<principle name="Merger_Complexity">
- Custom plugins/tools in `.opencode/plugins/` or `.opencode/tools/` require merge decisions during packaging
- Custom plugin code in `.opencode/plugin/` or `.opencode/plugins/` requires merge decisions during packaging
- Packager delegates to `opencode-plugin-engineer` for guidance
</principle>
</design_principles>
Expand Down
54 changes: 40 additions & 14 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,38 @@ A user-invoked slash command defined as markdown with a prompt template.
Distinct from a tool: the user triggers it, not an agent.

**Tool**:
A function with a typed schema and execute logic that agents call. Distinct
from a command: agents invoke it, not the user.
A function with a typed argument schema and execute logic that agents call.
Tools are registered by plugins (see tool registration); there is no
file-based tool definition. Distinct from a command: agents invoke it, not
the user.

**Plugin**:
A TypeScript module registering config, tools, and event hooks with OpenCode.
A package registered under the plugins config key whose entry default-exports
a plugin definition that registers agents, tools, hooks, and MCP changes
through context domains at activation.
_Avoid_: extension (a plugin is one kind of extension)

**Plugin registration key**:
The `plugins` array in `opencode.json`/`opencode.jsonc` — the config key that
registers plugin packages. Entries are package specs (`name@latest`) or
`{ package, options }` objects; `-target` entries remove. Replaces the v1
singular `plugin` key, which is tolerated read-only with an upgrade advisory.
_Avoid_: plugin key (singular), plugin array

**Plugin definition shape**:
The default-export contract for a plugin entry module: `{ id, effect }`
(Effect-first) or `{ id, setup }` (Promise fallback), with a stable `id` that
scopes storage and diagnostics. Anything else fails loading. The suite's
house style is the Effect shape.
_Avoid_: plugin factory, hooks object

**Tool registration**:
How custom tools come to exist in v2: a plugin's tool-domain transform adds
`{ name, input, description, execute }` tool definitions whose `input` is a
JSON Schema (or Effect Schema/Standard Schema codec). The v1 file-based
`.opencode/tools/` convention has no v2 equivalent.
_Avoid_: tool file, file-based tool (for new work)

### The suite

**Architect**:
Expand Down Expand Up @@ -129,7 +154,7 @@ package.json, derived by the packager from its asset inventory and verified
by the publisher. It is how installers read the deployment plan.

**Plugin install**:
The mode where the package is listed in a config file's `plugin` array and
The mode where the package is listed in a config file's `plugins` array and
everything registers from the package at load time; the CLI copies nothing.
Mandatory for code-backed packages; the always-fresh opt-in for assets-only
packages.
Expand Down Expand Up @@ -159,7 +184,7 @@ and commands.

**Manifest**:
The JSON file an install writes at the scope base, recording the installed
version, the install mode, the plugin entry and its target config file
version, the install mode, the registration entry and its target config file
(plugin mode), and the installed files with per-file content hashes (copy
mode); source of truth for status, no-op detection, uninstall, and
migration. Every install keeps one (a generated package's lives at
Expand All @@ -173,9 +198,9 @@ installation may touch.
_Avoid_: scope (ambiguous with the scope base, which is a write target)

**Load-time installation**:
The plugin hook that ensures a package's assets in the registered scopes
when OpenCode starts. Manifest-gated; never edits config registrations;
never writes outside the detected registration scopes.
The plugin work at startup that ensures a package's assets in the registered
scopes when OpenCode starts. Manifest-gated; never edits config
registrations; never writes outside the detected registration scopes.
_Avoid_: auto-install, install on load

**Startup non-interference**:
Expand All @@ -185,15 +210,16 @@ may fail hard. A hook that throws can stall startup with no escape, so
hooks never throw.

**Partial cache artifact**:
An npm cache install of a package left incomplete (bundled assets missing)
by an interrupted install, which OpenCode reuses indefinitely without
repair. Packages must detect this state at load and advise removing the
specific cache directory; it is not a consumer-setup error.
An npm cache generation of a package left incomplete (bundled assets missing)
by an interrupted install, in OpenCode's npm plugin cache
(`<cache>/npm/<key>/<generation>`), which the host can load without repair.
Packages must detect this state at activation and advise removing the
specific cache key; it is not a consumer-setup error.

**Zero-write no-op**:
The state where the manifest matches reality — same version, file hashes
intact, plugin entry already present — so an install or start performs no
writes at all.
intact, registration entry already present in the `plugins` array — so an
install or start performs no writes at all.

**Consumer modification**:
An installed file the consumer edited after install, detected by hash
Expand Down
65 changes: 65 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Contributing to opencode-architect

Thanks for helping build the suite. This repo has one non-negotiable rule:
**every OpenCode API fact we teach must be true of opencode v2** — the
package targets the v2 line only (ADR-0012), and our product is teaching, so
a stale fact is a shipped bug.

## Setup

```bash
bun install # Bun is the runtime and test runner
bun run check # tsc --noEmit
bun test # full suite
```

CI runs the same three gates plus the v2 verification harness
(`bun test tests/v2-host.test.ts tests/fixture-config-schema.test.ts`), which
loads the built plugin the way a v2 host does and validates every config
fixture against the pinned `@opencode/schema`.

## The guidance-fact policy

`docs/reference/opencode-v2-facts.md` is the single source of truth for
every OpenCode API fact asserted anywhere in this repo (references,
oneshots, conformance checklist, README, agent definitions, templates):

- Assert a fact only if it is **settled** or **hedged** in that record.
Open-harness items stay behind an explicit "pending verification" note.
- Cite by section number — e.g. "per opencode-v2-facts §8" — instead of
restating fragile details.
- New contradictions are adjudicated by extending §13 of that record, never
resolved locally.
- Consumer-facing guidance names packages with `@latest` (e.g.
`"@opencode/plugin": "latest"`); only the runtime pins what it executes.

The docs-fact gate (`tests/docs-fact-gate.test.ts`) enforces this in CI: it
fails on known-false v1 claims re-entering `references/` and on
API-teaching references that lack a verified-facts citation.

## Style

- **Effect-first**: our plugin, templates, examples, and generated packages
use the Effect-based v2 plugin API (`@opencode/plugin/effect`,
`Plugin.define({ id, effect })`); the Promise API is the documented
fallback for trivial plugins. General Effect-TS teaching is out of scope
here — it defers to the repo's effect-ts skill.
- No v1 archive: v1 names appear only inside explicit v1→v2 migration
mappings. Git history is the archive.
- TypeScript style rules: see `docs/coding-standards.md`.

## Domain docs

Root `CONTEXT.md` is the glossary — use its vocabulary and `_Avoid:` lines.
Decisions land as ADRs in `docs/adr/`. See `docs/agents/domain.md`.

## Tests

Tests assert external behavior only — what a consumer or a v2 host can
observe, never internal structure. Existing prior art lives in `tests/`;
migrate it, don't reinvent it.

## Release

Ship via tag push only — GitHub Actions publishes to npm. Never run
`npm publish` locally. See `docs/agents/release.md`.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# opencode-architect
[![npm version](https://img.shields.io/npm/v/opencode-architect?color=cb3837&label=npm)](https://www.npmjs.com/package/opencode-architect) [![Bun](https://img.shields.io/badge/Runtime-Bun-f9f1e1?logo=bun&logoColor=black)](https://bun.sh) [![License: MIT](https://img.shields.io/badge/License-MIT-22c55e)](LICENSE.md) [![Platforms](https://img.shields.io/badge/Platforms-Linux-6366f1)](#quick-start-install-the-opencode-plugin-suite) [![OpenCode plugin](https://img.shields.io/badge/opencode-plugin-blueviolet)](https://opencode.ai/docs/plugins)
[![npm version](https://img.shields.io/npm/v/opencode-architect?color=cb3837&label=npm)](https://www.npmjs.com/package/opencode-architect) [![Bun](https://img.shields.io/badge/Runtime-Bun-f9f1e1?logo=bun&logoColor=black)](https://bun.sh) [![License: MIT](https://img.shields.io/badge/License-MIT-22c55e)](LICENSE.md) [![Platforms](https://img.shields.io/badge/Platforms-Linux-6366f1)](#quick-start-install-the-opencode-plugin-suite) [![OpenCode plugin](https://img.shields.io/badge/opencode-plugin-blueviolet)](https://opencode.ai/v2/docs/plugins)

**Ten specialist agents that design, build, and package OpenCode extensions — agent skills, slash commands, custom tools, plugins, and MCP server integrations — right inside your AI coding assistant.**

opencode-architect is an [OpenCode](https://opencode.ai) plugin and CLI that ships a suite of AI agent experts for OpenCode work: designing agents, creating skills and slash commands, building plugins and custom tools, integrating MCP servers, and packaging extensions for npm. Install it once and every specialist agent is available in your coding sessions.
opencode-architect is an [OpenCode](https://opencode.ai) plugin and CLI that ships a suite of AI agent experts for OpenCode work: designing agents, creating skills and slash commands, building plugins and custom tools, integrating MCP servers, and packaging extensions for npm. It targets the OpenCode **v2** line and registers through the Effect-first plugin API (`@opencode/plugin/effect`). Install it once and every specialist agent is available in your coding sessions.

## Quick start: install the OpenCode plugin suite

Expand Down Expand Up @@ -63,8 +63,8 @@ Ten specialist agents, one router:
| `opencode-agent-designer` | Designs OpenCode agents and orchestrator subagents — roles, constraints, tools, permissions |
| `opencode-skill-creator` | Creates OpenCode skills in `.opencode/skills` — SKILL.md, frontmatter, progressive disclosure |
| `opencode-command-crafter` | Creates OpenCode slash commands in `.opencode/commands` — prompt templates, `$ARGUMENTS`, frontmatter |
| `opencode-tool-builder` | Creates OpenCode custom tools in `.opencode/tools` — Zod schemas and execute logic |
| `opencode-plugin-engineer` | Builds OpenCode plugins in `.opencode/plugins` — event hooks, custom tools, TypeScript |
| `opencode-tool-builder` | Registers OpenCode custom tools from plugins — JSON-Schema argument schemas and Effect execute logic |
| `opencode-plugin-engineer` | Builds Effect-first OpenCode plugins — plugin definitions, context domains, hooks, tool registration |
| `opencode-mcp-integrator` | Configures MCP servers and tool scoping in `opencode.json` — local/remote servers, permissions |
| `opencode-packager` | Packages OpenCode extensions for local sharing across projects — `file:///` plugin packages |
| `opencode-publisher` | Publishes OpenCode extensions to npm — transforms local packages into distributable ones |
Expand All @@ -87,7 +87,7 @@ Reach for opencode-architect whenever you want to:

## Requirements

- [OpenCode](https://opencode.ai) — the AI coding assistant the plugin extends
- [OpenCode](https://opencode.ai) — the AI coding assistant the plugin extends (v2 line)
- [Bun](https://bun.sh) — runs the plugin and the CLI (`bunx`); if you use `npx`, Bun must still be on your `PATH` because the CLI ships as TypeScript

## Development
Expand Down
11 changes: 11 additions & 0 deletions docs/adr/0010-plugin-entry-module-default-only.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Plugin entry module default-only

> **Superseded mechanism (ADR-0012, v2-native).** The loader behavior
> described below — every function-valued export invoked as a factory,
> `Cannot call a class constructor … without |new|`, `Plugin export is not a
> function` — is the **v1** loader and no longer applies. Under the v2
> contract the entry must default-export a plugin definition
> `{ id, effect }` / `{ id, setup }`; anything else fails loading with
> "Plugin must export a default definition with an id and an effect or setup
> function" (per opencode-v2-facts §2). The default-only rule stands — and is
> now schema-enforced rather than convention-enforced; checklist item E3 is
> rewritten accordingly.

OpenCode's plugin loader treats every function-valued export of a plugin's
entry module as a plugin factory and invokes it as a plain call:
`hooks.push(await server(input, options))` over `Object.values(mod)`. A
Expand Down
10 changes: 10 additions & 0 deletions docs/adr/0011-package-root-holds-only-the-entry.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Package root holds only the entry

> **Superseded mechanism (ADR-0012, v2-native).** The resolution chain below
> ("`exports["./server"]`, `main`, or the directory-index fallback") is
> v1-era shorthand. Under v2 the entrypoint resolves from
> `exports["./server"]` with a package-root index fallback — but the harness
> proved that fallback runtime-dependent (dead on Bun 1.3.x, working on Bun
> ≥ 1.4.2 and Node), so **every distributed package must declare
> `exports["./server"]`** and never rely on the root index (per
> opencode-v2-facts §13 row 9, §14.6). The root-holds-only-the-entry rule
> stands unchanged.

OpenCode's loader needs exactly one thing from a plugin package's root: the
entry module (`index.ts`, resolved via `exports["./server"]`, `main`, or the
directory-index fallback). Every other code file at the root is convention
Expand Down
65 changes: 65 additions & 0 deletions docs/adr/0012-v2-native-only.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# V2-native only — drop v1 hosts

opencode v2 replaced the extension SDK: v1 plugin implementations do not run
under a v2 host, and every API fact our suite teaches changed shape (config
keys, hook families, registration mechanics — all adjudicated in the
verified-facts record, `docs/reference/opencode-v2-facts.md`). Maintaining a
dual-target package would keep a dead code path alive for consumers who never
launch a v1 host, while the teaching surface — our actual product — cannot
serve both vocabularies at once. The decision: **the released package targets
the opencode v2 line only.** No dual entrypoint, no v1 host support; released
as 1.0.0.

Concretely, the plugin registers through the v2 Effect-first API: the entry
default-exports a plugin definition `{ id, effect }`
(`@opencode/plugin/effect`, `Plugin.define`), agents are injected via the
agent-domain transform, and the external-directory permission ask is
re-derived as a permission `evaluate` hook (facts §1, §2, §3, §4; confirmed
by the verification harness, `tests/v2-host.test.ts`). Consumer registrations
are written to the v2 `plugins` config key; legacy v1 `plugin` entries are
tolerated read-only with an upgrade advisory — never migrated silently, never
written back (facts §7). Generated packages must declare
`exports["./server"]`: the root-index fallback is runtime-dependent and dead
on Bun 1.3.x, so an undeclared entry disables the plugin at the entry stage
(facts §13 row 9, §14.6; `harness: tests/v2-host.test.ts`).

The guidance suite is rewritten in place — references, oneshots, conformance
checklist — with no v1 archive: git history is the archive. Guidance may
teach v1 names only inside explicit v1→v2 migration mappings (facts §9), so
consumers upgrading get a translation table, not a second dialect. Every
changed fact traces to the verified-facts record; the docs-fact gate
(`tests/docs-fact-gate.test.ts`) enforces this in CI.

Supersedes the v1-loader mechanism claims in ADR-0010 and the entry-resolution
claims in ADR-0011 (both carry superseding notes; their structural rules
survive under the v2 contract).

## Considered Options

- Dual v1/v2 entrypoints or dual-key config writes (rejected): the v1 host
and its SDK are dead ends; the cost is two teaching surfaces and two
installers for one product
- Silent migration of legacy `plugin` entries (rejected): rewriting consumer
configs behind their back violates the surgical-writer invariants (B5);
read-only tolerance plus an advisory keeps the consumer in control
- Keeping v1 guidance pages as an archive (rejected): stale pages get cited;
git history is the archive
- Pinning consumer installs to the adjudication-time package versions
(rejected, facts §15): consumer-facing guidance and templates use `@latest`
so generated packages resolve current versions at time of use; only the
runtime pins what it executes

## Consequences

- The package declares a v2-only host contract; consumers on v1 hosts must
stay on a pre-1.0 release
- ADR-0010/0011 keep their structural decisions but lose their v1 loader
justifications; the v2 default-export contract (`{ id, effect|setup }`,
facts §2) and the mandatory `exports["./server"]` declaration replace them
- The docs-fact gate blocks known-false v1 claims from re-entering
`references/`; new contradictions are adjudicated by extending §13 of the
verified-facts record, not resolved locally
- Effect-first is the house style for our plugin, templates, examples, and
the upgrade path's default port target; the Promise API is documented as
the fallback for trivial plugins, and general Effect-TS teaching defers to
the repo's effect-ts skill
Loading
Loading