mirror of
https://github.com/backnotprop/plannotator.git
synced 2026-09-14 14:17:26 +08:00
opencode plugin (#3)
* opencode plugin * opencode plugin * marketing copy updates * opencode plugin marketing * readme for open code * readme enhancement;
This commit is contained in:
@@ -12,6 +12,9 @@ dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# npm pack output
|
||||
*.tgz
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
|
||||
@@ -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 |
@@ -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).
|
||||
@@ -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;
|
||||
@@ -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
@@ -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
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user