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
*.local
# npm pack output
*.tgz
# Editor directories and files
.vscode/*
!.vscode/extensions.json
+44 -16
View File
@@ -4,20 +4,32 @@
# 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">
<a href="https://www.youtube.com/watch?v=a_AT7cEN_9I">Watch video</a>
</p>
<p align="center">
<a href="https://www.youtube.com/watch?v=a_AT7cEN_9I">
<img src="apps/marketing/public/youtube.png" alt="Watch the demo" width="600" />
</a>
</p>
<table>
<tr>
<td align="center" width="50%">
<h3>Claude Code</h3>
<a href="https://www.youtube.com/watch?v=a_AT7cEN_9I">
<img src="apps/marketing/public/youtube.png" alt="Claude Code Demo" width="100%" />
</a>
<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:**
@@ -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.
---
## 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
When Claude Code calls `ExitPlanMode`, this hook intercepts and:
When your AI agent finishes planning, Plannotator:
1. Opens Plannotator UI in your browser
2. Lets you annotate the plan visually
3. Approve → Claude proceeds with implementation
4. Request changes → Your annotations are sent back to Claude
1. Opens the Plannotator UI in your browser
2. Lets you annotate the plan visually (delete, insert, replace, comment)
3. **Approve** → Agent proceeds with implementation
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",
},
"apps/hooks": {
"apps/hook": {
"name": "@plannotator/hooks",
"version": "0.0.1",
"dependencies": {
@@ -41,6 +41,18 @@
"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": {
"name": "@plannotator/portal",
"version": "0.0.1",
@@ -66,6 +78,7 @@
"@plannotator/ui": "workspace:*",
"react": "^19.2.3",
"react-dom": "^19.2.3",
"tailwindcss": "^4.1.18",
},
},
"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=="],
"@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/hooks": ["@plannotator/hooks@workspace:apps/hooks"],
"@plannotator/hooks": ["@plannotator/hooks@workspace:apps/hook"],
"@plannotator/marketing": ["@plannotator/marketing@workspace:apps/marketing"],
"@plannotator/opencode": ["@plannotator/opencode@workspace:apps/opencode-plugin"],
"@plannotator/portal": ["@plannotator/portal@workspace:apps/portal"],
"@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/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/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=="],
"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=="],
"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=="],
"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/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",
"build:hook": "bun run --cwd apps/hook 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"
}
}
+89 -75
View File
@@ -47,7 +47,7 @@ export const Landing: React.FC<LandingProps> = ({ onEnter }) => {
<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">
<span className="w-1.5 h-1.5 rounded-full bg-primary animate-pulse" />
For Claude Code
For Claude Code & OpenCode
</div>
<div className="flex items-center gap-6 mb-4">
@@ -66,71 +66,82 @@ export const Landing: React.FC<LandingProps> = ({ onEnter }) => {
</div>
<p className="text-lg text-muted-foreground mb-8 max-w-lg">
Interactive Plan Review: Mark up and refine your plans using a UI,
easily share for team collaboration, automatically integrates with
agent plan mode.
Interactive Plan Review for coding agents. Mark up and refine plans visually,
share for team collaboration. Works with Claude Code and OpenCode.
</p>
<div className="flex items-center gap-3">
{/* YouTube button */}
<div className="flex flex-col gap-3">
<div className="flex items-center gap-3">
{/* YouTube button - Claude Code */}
<a
href="https://www.youtube.com/watch?v=a_AT7cEN_9I"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-lg bg-secondary text-white font-medium hover:opacity-90 transition-opacity"
>
<svg
className="w-5 h-5"
viewBox="0 0 24 24"
fill="currentColor"
>
<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>
Watch for Claude Code
</a>
{/* Open Demo button */}
{onEnter ? (
<button
onClick={onEnter}
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-lg bg-primary text-primary-foreground font-medium hover:opacity-90 transition-opacity"
>
Open Demo
<svg
className="w-4 h-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M13 7l5 5m0 0l-5 5m5-5H6"
/>
</svg>
</button>
) : (
<a
href="https://share.plannotator.ai"
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-lg bg-primary text-primary-foreground font-medium hover:opacity-90 transition-opacity"
>
Open Demo
<svg
className="w-4 h-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M13 7l5 5m0 0l-5 5m5-5H6"
/>
</svg>
</a>
)}
</div>
{/* OpenCode video link */}
<a
href="https://www.youtube.com/watch?v=a_AT7cEN_9I"
href="https://youtu.be/_N7uo0EFI-U"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-lg bg-secondary text-white font-medium hover:opacity-90 transition-opacity"
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
>
<svg
className="w-5 h-5"
viewBox="0 0 24 24"
fill="currentColor"
>
<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>
See it in action
Also watch for OpenCode →
</a>
{/* Open Demo button */}
{onEnter ? (
<button
onClick={onEnter}
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-lg bg-primary text-primary-foreground font-medium hover:opacity-90 transition-opacity"
>
Open Demo
<svg
className="w-4 h-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M13 7l5 5m0 0l-5 5m5-5H6"
/>
</svg>
</button>
) : (
<a
href="https://share.plannotator.ai"
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-lg bg-primary text-primary-foreground font-medium hover:opacity-90 transition-opacity"
>
Open Demo
<svg
className="w-4 h-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M13 7l5 5m0 0l-5 5m5-5H6"
/>
</svg>
</a>
)}
</div>
</div>
</section>
@@ -143,10 +154,9 @@ export const Landing: React.FC<LandingProps> = ({ onEnter }) => {
The Problem
</h3>
<p className="text-foreground/90">
Claude Code shows plans in the terminal. You read them, approve
or deny, but giving specific feedback means typing everything
out. Hard to reference exact sections. Zero team collaboration
features.
Coding agents show plans in the terminal. You approve or deny, but
giving specific feedback means typing everything out. Hard to
reference exact sections. Zero collaboration features.
</p>
</div>
<div>
@@ -154,10 +164,9 @@ export const Landing: React.FC<LandingProps> = ({ onEnter }) => {
The Solution
</h3>
<p className="text-foreground/90">
Select the exact parts of the plan you want to change. Mark it
for deletion, add a comment, or suggest a replacement. Share
plans and collect team member feedback. Automatically send
feedback for Claude Code to act on.
Select the exact parts of the plan you want to change. Mark for
deletion, add a comment, or suggest a replacement. Share plans
with your team. Feedback flows back to your agent automatically.
</p>
</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>
<div className="space-y-6 max-w-2xl">
<Step num={1} title="Hook into ExitPlanMode">
<code className="text-xs bg-muted px-2 py-1 rounded font-mono">
ExitPlanMode → opens Plannotator with plan content
</code>
<Step num={1} title="Agent triggers Plannotator">
<span className="text-xs">
<strong>Claude Code:</strong> ExitPlanMode hook opens UI<br />
<strong>OpenCode:</strong> Agent calls submit_plan tool
</span>
</Step>
<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
</Step>
<Step num={3} title="Approve or request changes">
Click approve to proceed, or deny with annotations. Feedback flows
back to Claude automatically via hooks.
Click approve to proceed, or provide feedback with annotations.
Feedback flows back to your agent automatically.
</Step>
</div>
</section>
@@ -372,7 +382,11 @@ export const Landing: React.FC<LandingProps> = ({ onEnter }) => {
</li>
<li className="flex items-center gap-2">
<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 className="flex items-center gap-2">
<span className="text-primary">•</span>