Skip to content

Commit 99d017e

Browse files
authored
opencode plugin (#3)
* opencode plugin * opencode plugin * marketing copy updates * opencode plugin marketing * readme for open code * readme enhancement;
1 parent d7b160d commit 99d017e

10 files changed

Lines changed: 781 additions & 92 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ dist
1212
dist-ssr
1313
*.local
1414

15+
# npm pack output
16+
*.tgz
17+
1518
# Editor directories and files
1619
.vscode/*
1720
!.vscode/extensions.json

README.md

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,32 @@
44

55
# Plannotator
66

7-
Interactive Plan Review: Mark up and refine your plans using a UI, easily share for team collaboration, automatically integrates with Claude Code plan mode.
7+
Interactive Plan Review for AI Coding Agents. Mark up and refine your plans using a visual UI, share for team collaboration, and seamlessly integrate with **Claude Code** and **OpenCode**.
8+
9+
<table>
10+
<tr>
11+
<td align="center" width="50%">
12+
<h3>Claude Code</h3>
13+
<a href="https://www.youtube.com/watch?v=a_AT7cEN_9I">
14+
<img src="apps/marketing/public/youtube.png" alt="Claude Code Demo" width="100%" />
15+
</a>
16+
<p><a href="https://www.youtube.com/watch?v=a_AT7cEN_9I">Watch Demo</a></p>
17+
</td>
18+
<td align="center" width="50%">
19+
<h3>OpenCode</h3>
20+
<a href="https://youtu.be/_N7uo0EFI-U">
21+
<img src="apps/marketing/public/youtube-opencode.png" alt="OpenCode Demo" width="100%" />
22+
</a>
23+
<p><a href="https://youtu.be/_N7uo0EFI-U">Watch Demo</a></p>
24+
</td>
25+
</tr>
26+
</table>
827

9-
<p align="center">
10-
<a href="https://www.youtube.com/watch?v=a_AT7cEN_9I">Watch video</a>
11-
</p>
12-
<p align="center">
13-
<a href="https://www.youtube.com/watch?v=a_AT7cEN_9I">
14-
<img src="apps/marketing/public/youtube.png" alt="Watch the demo" width="600" />
15-
</a>
16-
</p>
28+
---
1729

18-
## Install
30+
## Install for Claude Code
1931

20-
**Install the `plannotator` command so Claude Code can use it:**
32+
**Install the `plannotator` command:**
2133

2234
**macOS / Linux / WSL:**
2335

@@ -42,14 +54,30 @@ irm https://plannotator.ai/install.ps1 | iex
4254

4355
See [apps/hook/README.md](apps/hook/README.md) for detailed installation instructions including a `manual hook` approach.
4456

57+
---
58+
59+
## Install for OpenCode
60+
61+
Add to your `opencode.json`:
62+
63+
```json
64+
{
65+
"plugin": ["@plannotator/opencode"]
66+
}
67+
```
68+
69+
That's it! Restart OpenCode and the `submit_plan` tool will be available.
70+
71+
---
72+
4573
## How It Works
4674

47-
When Claude Code calls `ExitPlanMode`, this hook intercepts and:
75+
When your AI agent finishes planning, Plannotator:
4876

49-
1. Opens Plannotator UI in your browser
50-
2. Lets you annotate the plan visually
51-
3. Approve → Claude proceeds with implementation
52-
4. Request changes → Your annotations are sent back to Claude
77+
1. Opens the Plannotator UI in your browser
78+
2. Lets you annotate the plan visually (delete, insert, replace, comment)
79+
3. **Approve**Agent proceeds with implementation
80+
4. **Request changes** → Your annotations are sent back as structured feedback
5381

5482
---
5583

700 KB
Loading

apps/opencode-plugin/README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# @plannotator/opencode
2+
3+
**Annotate plans. Not in the terminal.**
4+
5+
Interactive Plan Review for OpenCode. Select the exact parts of the plan you want to change—mark for deletion, add a comment, or suggest a replacement. Feedback flows back to your agent automatically.
6+
7+
<table>
8+
<tr>
9+
<td align="center">
10+
<strong>Watch Demo</strong><br><br>
11+
<a href="https://youtu.be/_N7uo0EFI-U">
12+
<img src="https://img.youtube.com/vi/_N7uo0EFI-U/maxresdefault.jpg" alt="Watch Demo" width="600" />
13+
</a>
14+
</td>
15+
</tr>
16+
</table>
17+
18+
## Install
19+
20+
Add to your `opencode.json`:
21+
22+
```json
23+
{
24+
"$schema": "https://opencode.ai/config.json",
25+
"plugin": ["@plannotator/opencode"]
26+
}
27+
```
28+
29+
Restart OpenCode. The `submit_plan` tool is now available.
30+
31+
## How It Works
32+
33+
1. Agent calls `submit_plan` → Plannotator opens in your browser
34+
2. Select text → annotate (delete, replace, comment)
35+
3. **Approve** → Agent proceeds with implementation
36+
4. **Request changes** → Annotations sent back as structured feedback
37+
38+
## Features
39+
40+
- **Visual annotations**: Select text, choose an action, see feedback in the sidebar
41+
- **Runs locally**: No network requests. Plans never leave your machine.
42+
- **Private sharing**: Plans and annotations compress into the URL itself—share a link, no accounts or backend required
43+
44+
## Links
45+
46+
- [Website](https://plannotator.ai)
47+
- [GitHub](https://github.com/backnotprop/plannotator)
48+
- [Claude Code Plugin](https://github.com/backnotprop/plannotator/tree/main/apps/hook)
49+
50+
## License
51+
52+
Copyright (c) 2025 backnotprop. Licensed under [BSL-1.1](https://github.com/backnotprop/plannotator/blob/main/LICENSE).

apps/opencode-plugin/index.ts

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/**
2+
* Plannotator Plugin for OpenCode
3+
*
4+
* Provides a Claude Code-style planning experience with interactive plan review.
5+
* When the agent calls submit_plan, the Plannotator UI opens for the user to
6+
* annotate, approve, or request changes to the plan.
7+
*
8+
* @packageDocumentation
9+
*/
10+
11+
import { type Plugin, tool } from "@opencode-ai/plugin";
12+
import { $ } from "bun";
13+
14+
// @ts-ignore - Bun import attribute for text
15+
import indexHtml from "./plannotator.html" with { type: "text" };
16+
const htmlContent = indexHtml as unknown as string;
17+
18+
interface ServerResult {
19+
port: number;
20+
url: string;
21+
waitForDecision: () => Promise<{ approved: boolean; feedback?: string }>;
22+
stop: () => void;
23+
}
24+
25+
async function startPlannotatorServer(planContent: string): Promise<ServerResult> {
26+
let resolveDecision: (result: { approved: boolean; feedback?: string }) => void;
27+
const decisionPromise = new Promise<{ approved: boolean; feedback?: string }>(
28+
(resolve) => { resolveDecision = resolve; }
29+
);
30+
31+
const server = Bun.serve({
32+
port: 0,
33+
async fetch(req) {
34+
const url = new URL(req.url);
35+
36+
if (url.pathname === "/api/plan") {
37+
return Response.json({ plan: planContent });
38+
}
39+
40+
if (url.pathname === "/api/approve" && req.method === "POST") {
41+
resolveDecision({ approved: true });
42+
return Response.json({ ok: true });
43+
}
44+
45+
if (url.pathname === "/api/deny" && req.method === "POST") {
46+
try {
47+
const body = await req.json() as { feedback?: string };
48+
resolveDecision({ approved: false, feedback: body.feedback || "Plan rejected by user" });
49+
} catch {
50+
resolveDecision({ approved: false, feedback: "Plan rejected by user" });
51+
}
52+
return Response.json({ ok: true });
53+
}
54+
55+
return new Response(htmlContent, {
56+
headers: { "Content-Type": "text/html" }
57+
});
58+
},
59+
});
60+
61+
return {
62+
port: server.port!,
63+
url: `http://localhost:${server.port}`,
64+
waitForDecision: () => decisionPromise,
65+
stop: () => server.stop(),
66+
};
67+
}
68+
69+
async function openBrowser(url: string): Promise<void> {
70+
try {
71+
if (process.platform === "win32") {
72+
await $`cmd /c start ${url}`.quiet();
73+
} else if (process.platform === "darwin") {
74+
await $`open ${url}`.quiet();
75+
} else {
76+
await $`xdg-open ${url}`.quiet();
77+
}
78+
} catch {
79+
// Silently fail - user can open manually if needed
80+
}
81+
}
82+
83+
export const PlannotatorPlugin: Plugin = async (ctx) => {
84+
return {
85+
"experimental.chat.system.transform": async (_input, output) => {
86+
output.system.push(`
87+
## Plan Submission
88+
89+
When you have completed your plan, you MUST call the \`submit_plan\` tool to submit it for user review.
90+
The user will be able to:
91+
- Review your plan visually in a dedicated UI
92+
- Annotate specific sections with feedback
93+
- Approve the plan to proceed with implementation
94+
- Request changes with detailed feedback
95+
96+
If your plan is rejected, you will receive the user's annotated feedback. Revise your plan
97+
based on their feedback and call submit_plan again.
98+
99+
Do NOT proceed with implementation until your plan is approved.
100+
`);
101+
},
102+
103+
tool: {
104+
submit_plan: tool({
105+
description:
106+
"Submit your completed plan for interactive user review. The user can annotate, approve, or request changes. Call this when you have finished creating your implementation plan.",
107+
args: {
108+
plan: tool.schema
109+
.string()
110+
.describe("The complete implementation plan in markdown format"),
111+
summary: tool.schema
112+
.string()
113+
.describe("A brief 1-2 sentence summary of what the plan accomplishes"),
114+
},
115+
116+
async execute(args, _context) {
117+
const server = await startPlannotatorServer(args.plan);
118+
await openBrowser(server.url);
119+
120+
const result = await server.waitForDecision();
121+
await Bun.sleep(1500);
122+
server.stop();
123+
124+
if (result.approved) {
125+
try {
126+
await ctx.client.tui.executeCommand({
127+
body: { command: "agent_cycle" },
128+
});
129+
} catch {
130+
// Silently fail - agent switching is optional
131+
}
132+
133+
return `Plan approved! Switching to build mode.
134+
135+
Your plan has been approved by the user. You may now proceed with implementation.
136+
137+
Plan Summary: ${args.summary}`;
138+
} else {
139+
return `Plan needs revision.
140+
141+
The user has requested changes to your plan. Please review their feedback below and revise your plan accordingly.
142+
143+
## User Feedback
144+
145+
${result.feedback}
146+
147+
---
148+
149+
Please revise your plan based on this feedback and call \`submit_plan\` again when ready.`;
150+
}
151+
},
152+
}),
153+
},
154+
};
155+
};
156+
157+
export default PlannotatorPlugin;

apps/opencode-plugin/package.json

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"name": "@plannotator/opencode",
3+
"version": "0.1.5",
4+
"description": "Plannotator plugin for OpenCode - interactive plan review with visual annotation",
5+
"author": "backnotprop",
6+
"license": "BSL-1.1",
7+
"repository": {
8+
"type": "git",
9+
"url": "git+https://github.com/backnotprop/plannotator.git",
10+
"directory": "apps/opencode-plugin"
11+
},
12+
"homepage": "https://github.com/backnotprop/plannotator",
13+
"bugs": {
14+
"url": "https://github.com/backnotprop/plannotator/issues"
15+
},
16+
"keywords": [
17+
"opencode",
18+
"opencode-plugin",
19+
"plannotator",
20+
"plan-review",
21+
"ai-agent",
22+
"coding-agent"
23+
],
24+
"main": "index.ts",
25+
"files": [
26+
"index.ts",
27+
"plannotator.html",
28+
"README.md"
29+
],
30+
"scripts": {
31+
"build": "cp ../hook/dist/index.html ./plannotator.html",
32+
"prepublishOnly": "bun run build"
33+
},
34+
"dependencies": {
35+
"@opencode-ai/plugin": "^1.0.218"
36+
},
37+
"peerDependencies": {
38+
"bun": ">=1.0.0"
39+
}
40+
}

0 commit comments

Comments
 (0)