Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 17 additions & 1 deletion apps/marketing/src/content/docs/getting-started/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,23 @@ OpenCode uses `opencode.json` to load the plugin:
}
```

This registers the `submit_plan` tool. Slash commands (`/plannotator-review`, `/plannotator-annotate`) require the CLI to be installed separately via the install script.
This uses the default `plan-agent` workflow: `submit_plan` is registered for OpenCode's `plan` agent, while `build` and other primary agents do not see it.

To configure the workflow explicitly:

```json
{
"$schema": "https://opencode.ai/config.json",
"plugin": [
["@plannotator/opencode@latest", {
"workflow": "plan-agent",
"planningAgents": ["plan"]
}]
]
}
```

Use `workflow: "manual"` for commands-only mode, or `workflow: "all-agents"` to restore the legacy behavior where primary agents can call `submit_plan`. Slash commands (`/plannotator-review`, `/plannotator-annotate`, `/plannotator-last`) require the CLI to be installed separately via the install script.

## Plan saving

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ Add the plugin to your `opencode.json`:
}
```

Restart OpenCode. The `submit_plan` tool is now available.
Restart OpenCode. By default, `submit_plan` is available to OpenCode's `plan` agent only. Use the [OpenCode guide](/docs/guides/opencode/) if you want commands-only mode or the legacy all-agents behavior.

For slash commands (`/plannotator-review`, `/plannotator-annotate`), also run the install script:

Expand Down
58 changes: 53 additions & 5 deletions apps/marketing/src/content/docs/guides/opencode.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,66 @@ sidebar:
section: "Getting Started"
---

Plannotator integrates with OpenCode as an npm plugin that registers a `submit_plan` tool. When the agent calls `submit_plan`, Plannotator opens the review UI in your browser.
Plannotator integrates with OpenCode as an npm plugin. By default it makes `submit_plan` available to OpenCode's `plan` agent only, so OpenCode plan mode can use Plannotator without exposing the tool to `build`.

## How the plugin works

The OpenCode plugin (`@plannotator/opencode`) hooks into OpenCode's plugin system:

1. The plugin registers a `submit_plan` tool that the agent can call
1. The plugin registers a `submit_plan` tool for configured planning agents
2. When `submit_plan` is called with a plan, Plannotator starts a local server and opens the browser
3. The user reviews and annotates the plan
4. On approval, the plugin returns a success response to the agent
5. On denial, the plugin returns the feedback for the agent to revise

## Workflow modes

OpenCode support has three explicit modes:

- **`plan-agent`** (default): `submit_plan` is available to configured planning agents only. The default planning agent is `plan`.
- **`manual`**: `submit_plan` is not registered. Use `/plannotator-last`, `/plannotator-annotate`, `/plannotator-review`, and `/plannotator-archive` when you want Plannotator.
- **`all-agents`**: legacy broad behavior. Primary agents can see and call `submit_plan`.

Default config:

```json
{
"$schema": "https://opencode.ai/config.json",
"plugin": [
["@plannotator/opencode@latest", {
"workflow": "plan-agent",
"planningAgents": ["plan"]
}]
]
}
```

If you want the old broad behavior:

```json
{
"$schema": "https://opencode.ai/config.json",
"plugin": [
["@plannotator/opencode@latest", {
"workflow": "all-agents"
}]
]
}
```

If you want commands only:

```json
{
"$schema": "https://opencode.ai/config.json",
"plugin": [
["@plannotator/opencode@latest", {
"workflow": "manual"
}]
]
}
```

## Approve with annotations

Unlike Claude Code, OpenCode supports feedback on approval. This means:
Expand All @@ -40,7 +88,7 @@ If the configured agent isn't found in the current OpenCode session, Plannotator

## Slash commands

The plugin registers three slash commands:
The plugin registers slash commands that work in every workflow mode:

### `/plannotator-review`

Expand All @@ -54,7 +102,7 @@ Requires the CLI to be installed (the slash command runs `plannotator review` un

### `/plannotator-annotate <file.md>`

Opens a markdown file in the annotation UI. Also requires the CLI.
Opens a markdown file, directory, or URL in the annotation UI. Also requires the CLI.

### `/plannotator-last`

Expand All @@ -79,7 +127,7 @@ Add to your `opencode.json`:
}
```

Restart OpenCode. The `submit_plan` tool is now available. See the [installation guide](/docs/getting-started/installation/) for full details.
Restart OpenCode. With the default workflow, `submit_plan` is available to the `plan` agent. If you need `build` or another primary agent to call it, set `workflow` to `all-agents`. See the [installation guide](/docs/getting-started/installation/) for full details.

## Devcontainer / Docker

Expand Down
19 changes: 19 additions & 0 deletions apps/marketing/src/content/docs/guides/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,22 @@ If `ExitPlanMode` doesn't trigger Plannotator:
2. Restart Claude Code after installing (hooks load on startup)
3. Verify `plannotator` is on your PATH: `which plannotator`
4. Check that plan mode is enabled in your Claude Code session

## OpenCode build agent cannot call `submit_plan`

This is expected with the default OpenCode workflow. Plannotator now defaults to `plan-agent`, which keeps `submit_plan` available to OpenCode's `plan` agent and hides or denies it for `build` and other non-planning primary agents.

If you want the old broad behavior, opt in from `opencode.json`:

```json
{
"$schema": "https://opencode.ai/config.json",
"plugin": [
["@plannotator/opencode@latest", {
"workflow": "all-agents"
}]
]
}
```

If you do not want automatic plan review at all, use `workflow: "manual"` and run `/plannotator-last` or `/plannotator-annotate` when you want Plannotator.
53 changes: 51 additions & 2 deletions apps/opencode-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,65 @@ Add to your `opencode.json`:
}
```

Restart OpenCode. The `submit_plan` tool is now available.
Restart OpenCode. By default, the `submit_plan` tool is available to OpenCode's `plan` agent, not to `build` or other primary agents.

> **Slash commands:** Run the install script to get `/plannotator-review`, `/plannotator-annotate`, and `/plannotator-last`:
> ```bash
> curl -fsSL https://plannotator.ai/install.sh | bash
> ```
> This also clears any cached plugin versions.

## Workflow Modes

Plannotator supports three OpenCode workflows:

- **`plan-agent`** (default): `submit_plan` is available to configured planning agents only. This keeps Plannotator integrated with OpenCode plan mode without nudging `build` to call it.
- **`manual`**: `submit_plan` is not registered. Use `/plannotator-last`, `/plannotator-annotate`, `/plannotator-review`, and `/plannotator-archive` when you want Plannotator.
- **`all-agents`**: legacy broad behavior. Primary agents can see and call `submit_plan`.

Default config:

```json
{
"$schema": "https://opencode.ai/config.json",
"plugin": [
["@plannotator/opencode@latest", {
"workflow": "plan-agent",
"planningAgents": ["plan"]
}]
]
}
```

Restore the old broad behavior:

```json
{
"$schema": "https://opencode.ai/config.json",
"plugin": [
["@plannotator/opencode@latest", {
"workflow": "all-agents"
}]
]
}
```

Use commands only:

```json
{
"$schema": "https://opencode.ai/config.json",
"plugin": [
["@plannotator/opencode@latest", {
"workflow": "manual"
}]
]
}
```

## How It Works

1. Agent calls `submit_plan` → Plannotator opens in your browser
1. The configured planning agent calls `submit_plan` → Plannotator opens in your browser
2. Select text → annotate (delete, replace, comment)
3. **Approve** → Agent proceeds with implementation
4. **Request changes** → Annotations sent back as structured feedback
Expand All @@ -50,6 +98,7 @@ Restart OpenCode. The `submit_plan` tool is now available.
- **Private sharing**: Plans and annotations compress into the URL itself—share a link, no accounts or backend required
- **Plan Diff**: See what changed when the agent revises a plan after feedback
- **Annotate last message**: Run `/plannotator-last` to annotate the agent's most recent response
- **Annotate files, folders, and URLs**: Run `/plannotator-annotate` when you want manual review of an artifact
- **Obsidian integration**: Auto-save approved plans to your vault with frontmatter and tags

## Environment Variables
Expand Down
38 changes: 32 additions & 6 deletions apps/opencode-plugin/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import {
import { getGitContext, runGitDiffWithContext } from "@plannotator/server/git";
import { parsePRUrl, checkPRAuth, fetchPR, getCliName, getMRLabel, getMRNumberLabel, getDisplayRepo } from "@plannotator/server/pr";
import { loadConfig, resolveDefaultDiffType, resolveUseJina } from "@plannotator/shared/config";
import { resolveMarkdownFile } from "@plannotator/shared/resolve-file";
import { resolveMarkdownFile, hasMarkdownFiles } from "@plannotator/shared/resolve-file";
import { FILE_BROWSER_EXCLUDED } from "@plannotator/shared/reference-common";
import { htmlToMarkdown } from "@plannotator/shared/html-to-markdown";
import { urlToMarkdown } from "@plannotator/shared/url-to-markdown";
import { statSync } from "fs";
Expand Down Expand Up @@ -147,18 +148,24 @@ export async function handleAnnotateCommand(
event: any,
deps: CommandDeps
) {
const { client, htmlContent, getSharingEnabled, getShareBaseUrl } = deps;
const { client, htmlContent, getSharingEnabled, getShareBaseUrl, directory } = deps;

// @ts-ignore - Event properties contain arguments
const filePath = event.properties?.arguments || event.arguments || "";
let filePath = event.properties?.arguments || event.arguments || "";

if (!filePath) {
client.app.log({ level: "error", message: "Usage: /plannotator-annotate <file.md | file.html | https://...>" });
client.app.log({ level: "error", message: "Usage: /plannotator-annotate <file.md | file.html | https://... | folder/>" });
return;
}

if (filePath.startsWith("@")) {
filePath = filePath.slice(1);
}

let markdown: string;
let absolutePath: string;
let folderPath: string | undefined;
let annotateMode: "annotate" | "annotate-folder" = "annotate";
let sourceInfo: string | undefined;

// --- URL annotation ---
Expand All @@ -177,10 +184,27 @@ export async function handleAnnotateCommand(
absolutePath = filePath;
sourceInfo = filePath;
} else {
const projectRoot = process.cwd();
const projectRoot = directory || process.cwd();
const resolvedArg = path.resolve(projectRoot, filePath);

if (/\.html?$/i.test(resolvedArg)) {
let isFolder = false;
try {
isFolder = statSync(resolvedArg).isDirectory();
} catch {
// Not a directory, fall through to file resolution.
}

if (isFolder) {
if (!hasMarkdownFiles(resolvedArg, FILE_BROWSER_EXCLUDED, /\.(mdx?|html?)$/i)) {
client.app.log({ level: "error", message: `No markdown or HTML files found in ${resolvedArg}` });
return;
}
folderPath = resolvedArg;
absolutePath = resolvedArg;
markdown = "";
annotateMode = "annotate-folder";
client.app.log({ level: "info", message: `Opening annotation UI for folder ${resolvedArg}...` });
} else if (/\.html?$/i.test(resolvedArg)) {
// HTML file annotation — convert to markdown via Turndown
let fileSize: number;
try {
Expand Down Expand Up @@ -225,6 +249,8 @@ export async function handleAnnotateCommand(
markdown,
filePath: absolutePath,
origin: "opencode",
mode: annotateMode,
folderPath,
sourceInfo,
sharingEnabled: await getSharingEnabled(),
shareBaseUrl: getShareBaseUrl(),
Expand Down
Loading