opencode plugin (#3)

* opencode plugin

* opencode plugin

* marketing copy updates

* opencode plugin marketing

* readme for open code

* readme enhancement;
This commit is contained in:
Michael Ramos
2025-12-30 21:25:46 -08:00
committed by GitHub
parent c86671aead
commit 17ebb50b79
10 changed files with 783 additions and 94 deletions
+3
View File
@@ -12,6 +12,9 @@ dist
dist-ssr dist-ssr
*.local *.local
# npm pack output
*.tgz
# Editor directories and files # Editor directories and files
.vscode/* .vscode/*
!.vscode/extensions.json !.vscode/extensions.json
+42 -14
View File
@@ -4,20 +4,32 @@
# Plannotator # Plannotator
Interactive Plan Review: Mark up and refine your plans using a UI, easily share for team collaboration, automatically integrates with Claude Code plan mode. 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**.
<p align="center"> <table>
<a href="https://www.youtube.com/watch?v=a_AT7cEN_9I">Watch video</a> <tr>
</p> <td align="center" width="50%">
<p align="center"> <h3>Claude Code</h3>
<a href="https://www.youtube.com/watch?v=a_AT7cEN_9I"> <a href="https://www.youtube.com/watch?v=a_AT7cEN_9I">
<img src="apps/marketing/public/youtube.png" alt="Watch the demo" width="600" /> <img src="apps/marketing/public/youtube.png" alt="Claude Code Demo" width="100%" />
</a> </a>
</p> <p><a href="https://www.youtube.com/watch?v=a_AT7cEN_9I">Watch Demo</a></p>
</td>
<td align="center" width="50%">
<h3>OpenCode</h3>
<a href="https://youtu.be/_N7uo0EFI-U">
<img src="apps/marketing/public/youtube-opencode.png" alt="OpenCode Demo" width="100%" />
</a>
<p><a href="https://youtu.be/_N7uo0EFI-U">Watch Demo</a></p>
</td>
</tr>
</table>
## Install ---
**Install the `plannotator` command so Claude Code can use it:** ## Install for Claude Code
**Install the `plannotator` command:**
**macOS / Linux / WSL:** **macOS / Linux / WSL:**
@@ -42,14 +54,30 @@ irm https://plannotator.ai/install.ps1 | iex
See [apps/hook/README.md](apps/hook/README.md) for detailed installation instructions including a `manual hook` approach. See [apps/hook/README.md](apps/hook/README.md) for detailed installation instructions including a `manual hook` approach.
---
## Install for OpenCode
Add to your `opencode.json`:
```json
{
"plugin": ["@plannotator/opencode"]
}
```
That's it! Restart OpenCode and the `submit_plan` tool will be available.
---
## How It Works ## How It Works
When Claude Code calls `ExitPlanMode`, this hook intercepts and: When your AI agent finishes planning, Plannotator:
1. Opens Plannotator UI in your browser 1. Opens the Plannotator UI in your browser
2. Lets you annotate the plan visually 2. Lets you annotate the plan visually (delete, insert, replace, comment)
3. Approve → Claude proceeds with implementation 3. **Approve** → Agent proceeds with implementation
4. Request changes → Your annotations are sent back to Claude 4. **Request changes** → Your annotations are sent back as structured feedback
--- ---
Binary file not shown.

After

Width:  |  Height:  |  Size: 700 KiB

+52
View File
@@ -0,0 +1,52 @@
# @plannotator/opencode
**Annotate plans. Not in the terminal.**
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.
<table>
<tr>
<td align="center">
<strong>Watch Demo</strong><br><br>
<a href="https://youtu.be/_N7uo0EFI-U">
<img src="https://img.youtube.com/vi/_N7uo0EFI-U/maxresdefault.jpg" alt="Watch Demo" width="600" />
</a>
</td>
</tr>
</table>
## Install
Add to your `opencode.json`:
```json
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["@plannotator/opencode"]
}
```
Restart OpenCode. The `submit_plan` tool is now available.
## How It Works
1. 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
## Features
- **Visual annotations**: Select text, choose an action, see feedback in the sidebar
- **Runs locally**: No network requests. Plans never leave your machine.
- **Private sharing**: Plans and annotations compress into the URL itself—share a link, no accounts or backend required
## Links
- [Website](https://plannotator.ai)
- [GitHub](https://github.com/backnotprop/plannotator)
- [Claude Code Plugin](https://github.com/backnotprop/plannotator/tree/main/apps/hook)
## License
Copyright (c) 2025 backnotprop. Licensed under [BSL-1.1](https://github.com/backnotprop/plannotator/blob/main/LICENSE).
+157
View File
@@ -0,0 +1,157 @@
/**
* Plannotator Plugin for OpenCode
*
* Provides a Claude Code-style planning experience with interactive plan review.
* When the agent calls submit_plan, the Plannotator UI opens for the user to
* annotate, approve, or request changes to the plan.
*
* @packageDocumentation
*/
import { type Plugin, tool } from "@opencode-ai/plugin";
import { $ } from "bun";
// @ts-ignore - Bun import attribute for text
import indexHtml from "./plannotator.html" with { type: "text" };
const htmlContent = indexHtml as unknown as string;
interface ServerResult {
port: number;
url: string;
waitForDecision: () => Promise<{ approved: boolean; feedback?: string }>;
stop: () => void;
}
async function startPlannotatorServer(planContent: string): Promise<ServerResult> {
let resolveDecision: (result: { approved: boolean; feedback?: string }) => void;
const decisionPromise = new Promise<{ approved: boolean; feedback?: string }>(
(resolve) => { resolveDecision = resolve; }
);
const server = Bun.serve({
port: 0,
async fetch(req) {
const url = new URL(req.url);
if (url.pathname === "/api/plan") {
return Response.json({ plan: planContent });
}
if (url.pathname === "/api/approve" && req.method === "POST") {
resolveDecision({ approved: true });
return Response.json({ ok: true });
}
if (url.pathname === "/api/deny" && req.method === "POST") {
try {
const body = await req.json() as { feedback?: string };
resolveDecision({ approved: false, feedback: body.feedback || "Plan rejected by user" });
} catch {
resolveDecision({ approved: false, feedback: "Plan rejected by user" });
}
return Response.json({ ok: true });
}
return new Response(htmlContent, {
headers: { "Content-Type": "text/html" }
});
},
});
return {
port: server.port!,
url: `http://localhost:${server.port}`,
waitForDecision: () => decisionPromise,
stop: () => server.stop(),
};
}
async function openBrowser(url: string): Promise<void> {
try {
if (process.platform === "win32") {
await $`cmd /c start ${url}`.quiet();
} else if (process.platform === "darwin") {
await $`open ${url}`.quiet();
} else {
await $`xdg-open ${url}`.quiet();
}
} catch {
// Silently fail - user can open manually if needed
}
}
export const PlannotatorPlugin: Plugin = async (ctx) => {
return {
"experimental.chat.system.transform": async (_input, output) => {
output.system.push(`
## Plan Submission
When you have completed your plan, you MUST call the \`submit_plan\` tool to submit it for user review.
The user will be able to:
- Review your plan visually in a dedicated UI
- Annotate specific sections with feedback
- Approve the plan to proceed with implementation
- Request changes with detailed feedback
If your plan is rejected, you will receive the user's annotated feedback. Revise your plan
based on their feedback and call submit_plan again.
Do NOT proceed with implementation until your plan is approved.
`);
},
tool: {
submit_plan: tool({
description:
"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.",
args: {
plan: tool.schema
.string()
.describe("The complete implementation plan in markdown format"),
summary: tool.schema
.string()
.describe("A brief 1-2 sentence summary of what the plan accomplishes"),
},
async execute(args, _context) {
const server = await startPlannotatorServer(args.plan);
await openBrowser(server.url);
const result = await server.waitForDecision();
await Bun.sleep(1500);
server.stop();
if (result.approved) {
try {
await ctx.client.tui.executeCommand({
body: { command: "agent_cycle" },
});
} catch {
// Silently fail - agent switching is optional
}
return `Plan approved! Switching to build mode.
Your plan has been approved by the user. You may now proceed with implementation.
Plan Summary: ${args.summary}`;
} else {
return `Plan needs revision.
The user has requested changes to your plan. Please review their feedback below and revise your plan accordingly.
## User Feedback
${result.feedback}
---
Please revise your plan based on this feedback and call \`submit_plan\` again when ready.`;
}
},
}),
},
};
};
export default PlannotatorPlugin;
+40
View File
@@ -0,0 +1,40 @@
{
"name": "@plannotator/opencode",
"version": "0.1.5",
"description": "Plannotator plugin for OpenCode - interactive plan review with visual annotation",
"author": "backnotprop",
"license": "BSL-1.1",
"repository": {
"type": "git",
"url": "git+https://github.com/backnotprop/plannotator.git",
"directory": "apps/opencode-plugin"
},
"homepage": "https://github.com/backnotprop/plannotator",
"bugs": {
"url": "https://github.com/backnotprop/plannotator/issues"
},
"keywords": [
"opencode",
"opencode-plugin",
"plannotator",
"plan-review",
"ai-agent",
"coding-agent"
],
"main": "index.ts",
"files": [
"index.ts",
"plannotator.html",
"README.md"
],
"scripts": {
"build": "cp ../hook/dist/index.html ./plannotator.html",
"prepublishOnly": "bun run build"
},
"dependencies": {
"@opencode-ai/plugin": "^1.0.218"
},
"peerDependencies": {
"bun": ">=1.0.0"
}
}
File diff suppressed because one or more lines are too long
+27 -2
View File
@@ -4,7 +4,7 @@
"": { "": {
"name": "plannotator", "name": "plannotator",
}, },
"apps/hooks": { "apps/hook": {
"name": "@plannotator/hooks", "name": "@plannotator/hooks",
"version": "0.0.1", "version": "0.0.1",
"dependencies": { "dependencies": {
@@ -41,6 +41,18 @@
"vite": "^6.2.0", "vite": "^6.2.0",
}, },
}, },
"apps/opencode-plugin": {
"name": "@plannotator/opencode",
"version": "0.0.1",
"dependencies": {
"@opencode-ai/plugin": "latest",
},
"devDependencies": {
"@types/bun": "latest",
"@types/node": "^22.14.0",
"typescript": "~5.8.2",
},
},
"apps/portal": { "apps/portal": {
"name": "@plannotator/portal", "name": "@plannotator/portal",
"version": "0.0.1", "version": "0.0.1",
@@ -66,6 +78,7 @@
"@plannotator/ui": "workspace:*", "@plannotator/ui": "workspace:*",
"react": "^19.2.3", "react": "^19.2.3",
"react-dom": "^19.2.3", "react-dom": "^19.2.3",
"tailwindcss": "^4.1.18",
}, },
}, },
"packages/ui": { "packages/ui": {
@@ -181,12 +194,18 @@
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
"@opencode-ai/plugin": ["@opencode-ai/plugin@1.0.218", "", { "dependencies": { "@opencode-ai/sdk": "1.0.218", "zod": "4.1.8" } }, "sha512-7fsCx2SIu7TRNn3cdHiaIU7r1/XgnVTBufGnlezH+OZwHP+gILBjqtuMn+1L0e1npnl12CvS3FRNvg/3jr6yhA=="],
"@opencode-ai/sdk": ["@opencode-ai/sdk@1.0.218", "", {}, "sha512-c6ss6UPAMskSVQUecuhNvPLFngyVh2Os9o0kpVjoqJJ16HXhzjVSk5axgh3ueQrfP5aZfg5o6l6srmjuCTPNnQ=="],
"@plannotator/editor": ["@plannotator/editor@workspace:packages/editor"], "@plannotator/editor": ["@plannotator/editor@workspace:packages/editor"],
"@plannotator/hooks": ["@plannotator/hooks@workspace:apps/hooks"], "@plannotator/hooks": ["@plannotator/hooks@workspace:apps/hook"],
"@plannotator/marketing": ["@plannotator/marketing@workspace:apps/marketing"], "@plannotator/marketing": ["@plannotator/marketing@workspace:apps/marketing"],
"@plannotator/opencode": ["@plannotator/opencode@workspace:apps/opencode-plugin"],
"@plannotator/portal": ["@plannotator/portal@workspace:apps/portal"], "@plannotator/portal": ["@plannotator/portal@workspace:apps/portal"],
"@plannotator/ui": ["@plannotator/ui@workspace:packages/ui"], "@plannotator/ui": ["@plannotator/ui@workspace:packages/ui"],
@@ -275,6 +294,8 @@
"@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="], "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="],
"@types/bun": ["@types/bun@1.3.5", "", { "dependencies": { "bun-types": "1.3.5" } }, "sha512-RnygCqNrd3srIPEWBd5LFeUYG7plCoH2Yw9WaZGyNmdTEei+gWaHqydbaIRkIkcbXwhBT94q78QljxN0Sk838w=="],
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
"@types/node": ["@types/node@22.19.3", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA=="], "@types/node": ["@types/node@22.19.3", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA=="],
@@ -287,6 +308,8 @@
"browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], "browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="],
"bun-types": ["bun-types@1.3.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="],
"caniuse-lite": ["caniuse-lite@1.0.30001761", "", {}, "sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g=="], "caniuse-lite": ["caniuse-lite@1.0.30001761", "", {}, "sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g=="],
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
@@ -405,6 +428,8 @@
"yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
"zod": ["zod@4.1.8", "", {}, "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.7.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.7.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.7.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.7.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA=="],
+3 -1
View File
@@ -20,6 +20,8 @@
"dev:marketing": "bun run --cwd apps/marketing dev", "dev:marketing": "bun run --cwd apps/marketing dev",
"build:hook": "bun run --cwd apps/hook build", "build:hook": "bun run --cwd apps/hook build",
"build:portal": "bun run --cwd apps/portal build", "build:portal": "bun run --cwd apps/portal build",
"build:marketing": "bun run --cwd apps/marketing build" "build:marketing": "bun run --cwd apps/marketing build",
"build:opencode": "bun run --cwd apps/opencode-plugin build",
"build": "bun run build:hook && bun run build:opencode"
} }
} }
+36 -22
View File
@@ -47,7 +47,7 @@ export const Landing: React.FC<LandingProps> = ({ onEnter }) => {
<div className="max-w-2xl"> <div className="max-w-2xl">
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-primary/10 text-primary text-xs font-medium mb-6"> <div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-primary/10 text-primary text-xs font-medium mb-6">
<span className="w-1.5 h-1.5 rounded-full bg-primary animate-pulse" /> <span className="w-1.5 h-1.5 rounded-full bg-primary animate-pulse" />
For Claude Code For Claude Code & OpenCode
</div> </div>
<div className="flex items-center gap-6 mb-4"> <div className="flex items-center gap-6 mb-4">
@@ -66,13 +66,13 @@ export const Landing: React.FC<LandingProps> = ({ onEnter }) => {
</div> </div>
<p className="text-lg text-muted-foreground mb-8 max-w-lg"> <p className="text-lg text-muted-foreground mb-8 max-w-lg">
Interactive Plan Review: Mark up and refine your plans using a UI, Interactive Plan Review for coding agents. Mark up and refine plans visually,
easily share for team collaboration, automatically integrates with share for team collaboration. Works with Claude Code and OpenCode.
agent plan mode.
</p> </p>
<div className="flex flex-col gap-3">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
{/* YouTube button */} {/* YouTube button - Claude Code */}
<a <a
href="https://www.youtube.com/watch?v=a_AT7cEN_9I" href="https://www.youtube.com/watch?v=a_AT7cEN_9I"
target="_blank" target="_blank"
@@ -86,7 +86,7 @@ export const Landing: React.FC<LandingProps> = ({ onEnter }) => {
> >
<path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z" /> <path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z" />
</svg> </svg>
See it in action Watch for Claude Code
</a> </a>
{/* Open Demo button */} {/* Open Demo button */}
@@ -132,6 +132,17 @@ export const Landing: React.FC<LandingProps> = ({ onEnter }) => {
</a> </a>
)} )}
</div> </div>
{/* OpenCode video link */}
<a
href="https://youtu.be/_N7uo0EFI-U"
target="_blank"
rel="noopener noreferrer"
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
>
Also watch for OpenCode →
</a>
</div>
</div> </div>
</section> </section>
@@ -143,10 +154,9 @@ export const Landing: React.FC<LandingProps> = ({ onEnter }) => {
The Problem The Problem
</h3> </h3>
<p className="text-foreground/90"> <p className="text-foreground/90">
Claude Code shows plans in the terminal. You read them, approve Coding agents show plans in the terminal. You approve or deny, but
or deny, but giving specific feedback means typing everything giving specific feedback means typing everything out. Hard to
out. Hard to reference exact sections. Zero team collaboration reference exact sections. Zero collaboration features.
features.
</p> </p>
</div> </div>
<div> <div>
@@ -154,10 +164,9 @@ export const Landing: React.FC<LandingProps> = ({ onEnter }) => {
The Solution The Solution
</h3> </h3>
<p className="text-foreground/90"> <p className="text-foreground/90">
Select the exact parts of the plan you want to change. Mark it Select the exact parts of the plan you want to change. Mark for
for deletion, add a comment, or suggest a replacement. Share deletion, add a comment, or suggest a replacement. Share plans
plans and collect team member feedback. Automatically send with your team. Feedback flows back to your agent automatically.
feedback for Claude Code to act on.
</p> </p>
</div> </div>
</div> </div>
@@ -344,20 +353,21 @@ export const Landing: React.FC<LandingProps> = ({ onEnter }) => {
<h2 className="text-xl font-semibold mb-8">How it works</h2> <h2 className="text-xl font-semibold mb-8">How it works</h2>
<div className="space-y-6 max-w-2xl"> <div className="space-y-6 max-w-2xl">
<Step num={1} title="Hook into ExitPlanMode"> <Step num={1} title="Agent triggers Plannotator">
<code className="text-xs bg-muted px-2 py-1 rounded font-mono"> <span className="text-xs">
ExitPlanMode → opens Plannotator with plan content <strong>Claude Code:</strong> ExitPlanMode hook opens UI<br />
</code> <strong>OpenCode:</strong> Agent calls submit_plan tool
</span>
</Step> </Step>
<Step num={2} title="Annotate visually"> <Step num={2} title="Annotate visually">
Select text → choose action (delete, comment) → annotations appear Select text → choose action (delete, comment, replace) → annotations appear
in the sidebar in the sidebar
</Step> </Step>
<Step num={3} title="Approve or request changes"> <Step num={3} title="Approve or request changes">
Click approve to proceed, or deny with annotations. Feedback flows Click approve to proceed, or provide feedback with annotations.
back to Claude automatically via hooks. Feedback flows back to your agent automatically.
</Step> </Step>
</div> </div>
</section> </section>
@@ -372,7 +382,11 @@ export const Landing: React.FC<LandingProps> = ({ onEnter }) => {
</li> </li>
<li className="flex items-center gap-2"> <li className="flex items-center gap-2">
<span className="text-primary">•</span> <span className="text-primary">•</span>
Claude Code plugin with PermissionRequest hook on ExitPlanMode <strong>Claude Code:</strong> Binary + plugin with PermissionRequest hook
</li>
<li className="flex items-center gap-2">
<span className="text-primary">•</span>
<strong>OpenCode:</strong> npm package with submit_plan tool
</li> </li>
<li className="flex items-center gap-2"> <li className="flex items-center gap-2">
<span className="text-primary">•</span> <span className="text-primary">•</span>