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
|
dist-ssr
|
||||||
*.local
|
*.local
|
||||||
|
|
||||||
|
# npm pack output
|
||||||
|
*.tgz
|
||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
.vscode/*
|
.vscode/*
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
|
|||||||
@@ -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 |
@@ -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",
|
"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
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user