open-plan-annotator 1.0.9 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/opencode/index.js +62 -20
- package/opencode/index.test.ts +56 -0
- package/package.json +1 -1
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"name": "open-plan-annotator",
|
|
13
13
|
"source": "./",
|
|
14
14
|
"description": "Interactive plan annotation UI: review, strikethrough, and comment on Claude's plans before approving. Fully local, no external services.",
|
|
15
|
-
"version": "1.0.
|
|
15
|
+
"version": "1.0.11",
|
|
16
16
|
"author": {
|
|
17
17
|
"name": "ndom91"
|
|
18
18
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "open-plan-annotator",
|
|
3
3
|
"description": "Interactive plan annotation UI: review, strikethrough, and comment on Claude's plans before approving. Fully local, no external services.",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.11",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "ndom91"
|
|
7
7
|
},
|
package/opencode/index.js
CHANGED
|
@@ -4,15 +4,44 @@ import { resolveImplementationHandoff } from "./config.js";
|
|
|
4
4
|
|
|
5
5
|
const PLAN_REVIEW_INSTRUCTIONS = `## Plan Review Workflow
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
Track planning/execution using this state enum:
|
|
8
|
+
- \`DISCOVERY\`, \`PLAN_DRAFT\`, \`AWAITING_PLAN_DECISION\`, \`EXECUTION\`, \`DONE\`
|
|
9
|
+
|
|
10
|
+
State transitions:
|
|
11
|
+
- Start in \`DISCOVERY\`.
|
|
12
|
+
- Move to \`PLAN_DRAFT\` only when a plan is required.
|
|
13
|
+
- From \`PLAN_DRAFT\`, call \`submit_plan\` exactly once, then move to \`AWAITING_PLAN_DECISION\`.
|
|
14
|
+
- If user approves plan, set \`plan_status=approved\` and move to \`EXECUTION\`.
|
|
15
|
+
- If user rejects or requests plan changes, set \`plan_status=rejected\` and return to \`PLAN_DRAFT\`.
|
|
16
|
+
- When work is complete, move to \`DONE\`.
|
|
17
|
+
|
|
18
|
+
Required flags:
|
|
19
|
+
- \`plan_status\` in \`{none, submitted, approved, rejected}\`
|
|
20
|
+
- \`explicit_replan\` in \`{true,false}\` (default \`false\`)
|
|
21
|
+
- Set \`explicit_replan=true\` only when user clearly asks to replan (for example: revise/change/new/update plan).
|
|
22
|
+
|
|
23
|
+
Hard rules:
|
|
24
|
+
1) \`submit_plan\` is allowed only in \`PLAN_DRAFT\`.
|
|
25
|
+
2) If \`plan_status=approved\`, \`submit_plan\` is forbidden unless \`explicit_replan=true\`.
|
|
26
|
+
3) Call \`submit_plan\` at most once per plan draft/version. If rejected, revise and submit once for the new draft.
|
|
27
|
+
4) After approval, treat follow-up user messages as execution refinements by default, not planning triggers.
|
|
28
|
+
5) On conflict, prioritize the approved plan and execute immediately.
|
|
29
|
+
6) Do not ask permission to proceed after approval; execute and report progress/results.
|
|
30
|
+
7) When delegating to subagents, always pass current \`plan_status\` and \`explicit_replan\` values.
|
|
31
|
+
8) If \`plan_status=approved\` and \`explicit_replan=false\`, subagents must execute and must not call \`submit_plan\`.
|
|
32
|
+
|
|
33
|
+
Tool guard before calling \`submit_plan\`:
|
|
34
|
+
- assert \`state == PLAN_DRAFT\`
|
|
35
|
+
- assert \`plan_status != approved || explicit_replan == true\`
|
|
36
|
+
- if an assertion fails, continue execution without submitting a new plan.`;
|
|
37
|
+
|
|
38
|
+
const IMPLEMENTATION_PROMPT = [
|
|
39
|
+
"Plan review status: plan_status=approved.",
|
|
40
|
+
"State transition: next_state=EXECUTION.",
|
|
41
|
+
"Replan intent: explicit_replan=false unless the user explicitly asks to revise the plan.",
|
|
42
|
+
"Execute the approved plan directly now — write code, create files, and make changes.",
|
|
43
|
+
"Do not call `submit_plan` again unless the user explicitly requests re-planning.",
|
|
44
|
+
].join(" ");
|
|
16
45
|
|
|
17
46
|
function getErrorMessage(error) {
|
|
18
47
|
if (error instanceof Error && error.message) {
|
|
@@ -133,7 +162,7 @@ export const OpenPlanAnnotatorPlugin = async (ctx) => {
|
|
|
133
162
|
tool: {
|
|
134
163
|
submit_plan: tool({
|
|
135
164
|
description:
|
|
136
|
-
"Submit a markdown plan for interactive user review. Returns
|
|
165
|
+
"Submit a markdown plan for interactive user review. Returns a structured result with plan_status, next_state, approved, and feedback fields.",
|
|
137
166
|
|
|
138
167
|
args: {
|
|
139
168
|
plan: tool.schema.string().describe("The complete implementation plan in markdown format"),
|
|
@@ -147,6 +176,13 @@ export const OpenPlanAnnotatorPlugin = async (ctx) => {
|
|
|
147
176
|
cwd: ctx.directory,
|
|
148
177
|
});
|
|
149
178
|
|
|
179
|
+
const basePayload = {
|
|
180
|
+
plan_status: result.approved ? "approved" : "rejected",
|
|
181
|
+
next_state: result.approved ? "EXECUTION" : "PLAN_DRAFT",
|
|
182
|
+
approved: result.approved,
|
|
183
|
+
feedback: result.approved ? null : (result.feedback ?? "Plan changes requested."),
|
|
184
|
+
};
|
|
185
|
+
|
|
150
186
|
if (result.approved) {
|
|
151
187
|
const lines = [
|
|
152
188
|
"Plan approved by the user.",
|
|
@@ -167,18 +203,24 @@ export const OpenPlanAnnotatorPlugin = async (ctx) => {
|
|
|
167
203
|
}
|
|
168
204
|
|
|
169
205
|
lines.push("Begin implementing the approved plan now — write code and make changes.");
|
|
170
|
-
return
|
|
206
|
+
return {
|
|
207
|
+
...basePayload,
|
|
208
|
+
guidance: lines.join("\n\n"),
|
|
209
|
+
};
|
|
171
210
|
}
|
|
172
211
|
|
|
173
|
-
return
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
212
|
+
return {
|
|
213
|
+
...basePayload,
|
|
214
|
+
guidance: [
|
|
215
|
+
"Plan needs revision.",
|
|
216
|
+
"",
|
|
217
|
+
"## User feedback",
|
|
218
|
+
"",
|
|
219
|
+
basePayload.feedback,
|
|
220
|
+
"",
|
|
221
|
+
"Revise the plan using this feedback, then submit the revised draft once via `submit_plan`.",
|
|
222
|
+
].join("\n"),
|
|
223
|
+
};
|
|
182
224
|
},
|
|
183
225
|
}),
|
|
184
226
|
},
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { afterEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
afterEach(() => {
|
|
4
|
+
mock.restore();
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
function createPluginContext() {
|
|
8
|
+
return {
|
|
9
|
+
directory: process.cwd(),
|
|
10
|
+
client: {
|
|
11
|
+
session: {
|
|
12
|
+
messages: async () => ({ data: [] }),
|
|
13
|
+
prompt: async () => ({ data: null }),
|
|
14
|
+
},
|
|
15
|
+
app: {
|
|
16
|
+
agents: async () => ({ data: [] }),
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe("submit_plan return schema contract", () => {
|
|
23
|
+
test("approved decision maps to approved execution payload", async () => {
|
|
24
|
+
mock.module("./bridge.js", () => ({
|
|
25
|
+
runPlanReview: async () => ({ approved: true }),
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
const { OpenPlanAnnotatorPlugin } = await import(`./index.js?approved-${Date.now()}`);
|
|
29
|
+
const plugin = await OpenPlanAnnotatorPlugin(createPluginContext());
|
|
30
|
+
|
|
31
|
+
const result = await plugin.tool.submit_plan.execute({ plan: "# Plan" }, { sessionID: "session-1" });
|
|
32
|
+
|
|
33
|
+
expect(result.plan_status).toBe("approved");
|
|
34
|
+
expect(result.next_state).toBe("EXECUTION");
|
|
35
|
+
expect(result.approved).toBe(true);
|
|
36
|
+
expect(result.feedback).toBeNull();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("rejected decision maps to plan redraft payload with bridge feedback", async () => {
|
|
40
|
+
const bridgeFeedback = "Need to add rollback steps.";
|
|
41
|
+
|
|
42
|
+
mock.module("./bridge.js", () => ({
|
|
43
|
+
runPlanReview: async () => ({ approved: false, feedback: bridgeFeedback }),
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
const { OpenPlanAnnotatorPlugin } = await import(`./index.js?rejected-${Date.now()}`);
|
|
47
|
+
const plugin = await OpenPlanAnnotatorPlugin(createPluginContext());
|
|
48
|
+
|
|
49
|
+
const result = await plugin.tool.submit_plan.execute({ plan: "# Plan" }, { sessionID: "session-2" });
|
|
50
|
+
|
|
51
|
+
expect(result.plan_status).toBe("rejected");
|
|
52
|
+
expect(result.next_state).toBe("PLAN_DRAFT");
|
|
53
|
+
expect(result.approved).toBe(false);
|
|
54
|
+
expect(result.feedback).toBe(bridgeFeedback);
|
|
55
|
+
});
|
|
56
|
+
});
|