open-plan-annotator 1.1.1 → 1.1.3
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 +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/package.json +14 -9
- package/opencode/config.test.ts +0 -110
- package/opencode/index.test.ts +0 -54
- package/shared/cliHelp.test.ts +0 -29
- package/shared/cliMode.test.ts +0 -35
- package/shared/packageManager.test.ts +0 -33
- package/shared/runtimeResolver.test.ts +0 -36
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
},
|
|
6
6
|
"metadata": {
|
|
7
7
|
"description": "Interactive plan annotation plugin for Claude Code",
|
|
8
|
-
"version": "1.1.
|
|
8
|
+
"version": "1.1.3"
|
|
9
9
|
},
|
|
10
10
|
"plugins": [
|
|
11
11
|
{
|
|
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.1.
|
|
15
|
+
"version": "1.1.3",
|
|
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.1.
|
|
4
|
+
"version": "1.1.3",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "ndom91"
|
|
7
7
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "open-plan-annotator",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Fully local plugin for interactive plan annotation from your Agentic assistants",
|
|
6
6
|
"author": "ndom91",
|
|
@@ -26,9 +26,15 @@
|
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
28
|
"bin/open-plan-annotator.mjs",
|
|
29
|
-
"shared/",
|
|
29
|
+
"shared/cliHelp.mjs",
|
|
30
|
+
"shared/cliMode.mjs",
|
|
31
|
+
"shared/packageManager.mjs",
|
|
32
|
+
"shared/runtimeResolver.mjs",
|
|
33
|
+
"shared/updateHints.mjs",
|
|
30
34
|
".claude-plugin/",
|
|
31
|
-
"opencode/",
|
|
35
|
+
"opencode/bridge.js",
|
|
36
|
+
"opencode/config.js",
|
|
37
|
+
"opencode/index.js",
|
|
32
38
|
"hooks/",
|
|
33
39
|
"CLAUDE.md",
|
|
34
40
|
"README.md"
|
|
@@ -61,10 +67,9 @@
|
|
|
61
67
|
"@opencode-ai/plugin": "^1.2.14"
|
|
62
68
|
},
|
|
63
69
|
"optionalDependencies": {
|
|
64
|
-
"@open-plan-annotator/runtime-darwin-arm64": "1.1.
|
|
65
|
-
"@open-plan-annotator/runtime-darwin-x64": "1.1.
|
|
66
|
-
"@open-plan-annotator/runtime-linux-arm64": "1.1.
|
|
67
|
-
"@open-plan-annotator/runtime-linux-x64": "1.1.
|
|
68
|
-
}
|
|
69
|
-
"packageManager": "bun@1.3.9"
|
|
70
|
+
"@open-plan-annotator/runtime-darwin-arm64": "1.1.3",
|
|
71
|
+
"@open-plan-annotator/runtime-darwin-x64": "1.1.3",
|
|
72
|
+
"@open-plan-annotator/runtime-linux-arm64": "1.1.3",
|
|
73
|
+
"@open-plan-annotator/runtime-linux-x64": "1.1.3"
|
|
74
|
+
}
|
|
70
75
|
}
|
package/opencode/config.test.ts
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { resolveImplementationHandoff } from "./config.js";
|
|
6
|
-
|
|
7
|
-
function withEnv(key: string, value: string | undefined, fn: () => Promise<void>) {
|
|
8
|
-
const original = process.env[key];
|
|
9
|
-
if (value === undefined) {
|
|
10
|
-
delete process.env[key];
|
|
11
|
-
} else {
|
|
12
|
-
process.env[key] = value;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return fn().finally(() => {
|
|
16
|
-
if (original === undefined) {
|
|
17
|
-
delete process.env[key];
|
|
18
|
-
} else {
|
|
19
|
-
process.env[key] = original;
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
describe("resolveImplementationHandoff", () => {
|
|
25
|
-
test("returns defaults when no config files exist", async () => {
|
|
26
|
-
const tempRoot = mkdtempSync(join(tmpdir(), "open-plan-annotator-config-"));
|
|
27
|
-
|
|
28
|
-
try {
|
|
29
|
-
await withEnv("XDG_CONFIG_HOME", join(tempRoot, "xdg"), async () => {
|
|
30
|
-
const result = await resolveImplementationHandoff(join(tempRoot, "project"));
|
|
31
|
-
expect(result.enabled).toBe(true);
|
|
32
|
-
expect(result.agent).toBe("build");
|
|
33
|
-
});
|
|
34
|
-
} finally {
|
|
35
|
-
rmSync(tempRoot, { recursive: true, force: true });
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
test("uses global config when project config is missing", async () => {
|
|
40
|
-
const tempRoot = mkdtempSync(join(tmpdir(), "open-plan-annotator-config-"));
|
|
41
|
-
const xdgHome = join(tempRoot, "xdg");
|
|
42
|
-
const globalConfigDir = join(xdgHome, "opencode");
|
|
43
|
-
mkdirSync(globalConfigDir, { recursive: true });
|
|
44
|
-
writeFileSync(
|
|
45
|
-
join(globalConfigDir, "open-plan-annotator.json"),
|
|
46
|
-
JSON.stringify({ implementationHandoff: { enabled: false, agent: "explore" } }),
|
|
47
|
-
"utf8",
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
try {
|
|
51
|
-
await withEnv("XDG_CONFIG_HOME", xdgHome, async () => {
|
|
52
|
-
const result = await resolveImplementationHandoff(join(tempRoot, "project"));
|
|
53
|
-
expect(result.enabled).toBe(false);
|
|
54
|
-
expect(result.agent).toBe("explore");
|
|
55
|
-
});
|
|
56
|
-
} finally {
|
|
57
|
-
rmSync(tempRoot, { recursive: true, force: true });
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
test("project config overrides global config", async () => {
|
|
62
|
-
const tempRoot = mkdtempSync(join(tmpdir(), "open-plan-annotator-config-"));
|
|
63
|
-
const xdgHome = join(tempRoot, "xdg");
|
|
64
|
-
const globalConfigDir = join(xdgHome, "opencode");
|
|
65
|
-
const projectDir = join(tempRoot, "project");
|
|
66
|
-
const projectConfigDir = join(projectDir, ".opencode");
|
|
67
|
-
|
|
68
|
-
mkdirSync(globalConfigDir, { recursive: true });
|
|
69
|
-
mkdirSync(projectConfigDir, { recursive: true });
|
|
70
|
-
|
|
71
|
-
writeFileSync(
|
|
72
|
-
join(globalConfigDir, "open-plan-annotator.json"),
|
|
73
|
-
JSON.stringify({ implementationHandoff: { enabled: true, agent: "explore" } }),
|
|
74
|
-
"utf8",
|
|
75
|
-
);
|
|
76
|
-
writeFileSync(
|
|
77
|
-
join(projectConfigDir, "open-plan-annotator.json"),
|
|
78
|
-
JSON.stringify({ implementationHandoff: { enabled: false, agent: "build" } }),
|
|
79
|
-
"utf8",
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
try {
|
|
83
|
-
await withEnv("XDG_CONFIG_HOME", xdgHome, async () => {
|
|
84
|
-
const result = await resolveImplementationHandoff(projectDir);
|
|
85
|
-
expect(result.enabled).toBe(false);
|
|
86
|
-
expect(result.agent).toBe("build");
|
|
87
|
-
});
|
|
88
|
-
} finally {
|
|
89
|
-
rmSync(tempRoot, { recursive: true, force: true });
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
test("ignores malformed config and falls back", async () => {
|
|
94
|
-
const tempRoot = mkdtempSync(join(tmpdir(), "open-plan-annotator-config-"));
|
|
95
|
-
const xdgHome = join(tempRoot, "xdg");
|
|
96
|
-
const globalConfigDir = join(xdgHome, "opencode");
|
|
97
|
-
mkdirSync(globalConfigDir, { recursive: true });
|
|
98
|
-
writeFileSync(join(globalConfigDir, "open-plan-annotator.json"), "not-json", "utf8");
|
|
99
|
-
|
|
100
|
-
try {
|
|
101
|
-
await withEnv("XDG_CONFIG_HOME", xdgHome, async () => {
|
|
102
|
-
const result = await resolveImplementationHandoff(join(tempRoot, "project"));
|
|
103
|
-
expect(result.enabled).toBe(true);
|
|
104
|
-
expect(result.agent).toBe("build");
|
|
105
|
-
});
|
|
106
|
-
} finally {
|
|
107
|
-
rmSync(tempRoot, { recursive: true, force: true });
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
});
|
package/opencode/index.test.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
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 tool output", () => {
|
|
23
|
-
test("returns plain text execution instructions after approval", 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(typeof result).toBe("string");
|
|
34
|
-
expect(result).toContain("plan_status=approved");
|
|
35
|
-
expect(result).toContain("next_state=EXECUTION");
|
|
36
|
-
expect(result).toContain("Do not call `submit_plan` again");
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
test("returns plain text revision instructions after rejection", async () => {
|
|
40
|
-
mock.module("./bridge.js", () => ({
|
|
41
|
-
runPlanReview: async () => ({ approved: false, feedback: "Need rollback steps." }),
|
|
42
|
-
}));
|
|
43
|
-
|
|
44
|
-
const { OpenPlanAnnotatorPlugin } = await import(`./index.js?rejected-${Date.now()}`);
|
|
45
|
-
const plugin = await OpenPlanAnnotatorPlugin(createPluginContext());
|
|
46
|
-
|
|
47
|
-
const result = await plugin.tool.submit_plan.execute({ plan: "# Plan" }, { sessionID: "session-2" });
|
|
48
|
-
|
|
49
|
-
expect(typeof result).toBe("string");
|
|
50
|
-
expect(result).toContain("plan_status=rejected");
|
|
51
|
-
expect(result).toContain("next_state=PLAN_DRAFT");
|
|
52
|
-
expect(result).toContain("Need rollback steps.");
|
|
53
|
-
});
|
|
54
|
-
});
|
package/shared/cliHelp.test.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { buildCliHelpText, buildUnknownCommandPrefix } from "./cliHelp.mjs";
|
|
3
|
-
|
|
4
|
-
describe("buildCliHelpText", () => {
|
|
5
|
-
test("builds canonical help text for all entrypoints", () => {
|
|
6
|
-
expect(buildCliHelpText("1.2.3")).toBe(`open-plan-annotator v1.2.3
|
|
7
|
-
|
|
8
|
-
Usage:
|
|
9
|
-
open-plan-annotator Show this help
|
|
10
|
-
open-plan-annotator < event.json Run as a Claude Code hook (debug)
|
|
11
|
-
open-plan-annotator doctor Show resolved runtime details
|
|
12
|
-
open-plan-annotator update Show package-managed update guidance
|
|
13
|
-
open-plan-annotator upgrade Alias for update
|
|
14
|
-
open-plan-annotator --version Print version
|
|
15
|
-
open-plan-annotator --help Show this help
|
|
16
|
-
|
|
17
|
-
https://github.com/ndom91/open-plan-annotator`);
|
|
18
|
-
});
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
describe("buildUnknownCommandPrefix", () => {
|
|
22
|
-
test("formats unknown command prefix consistently", () => {
|
|
23
|
-
expect(buildUnknownCommandPrefix("wat")).toBe("open-plan-annotator: unknown command `wat`");
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
test("handles missing command safely", () => {
|
|
27
|
-
expect(buildUnknownCommandPrefix(undefined)).toBe("open-plan-annotator: unknown command ``");
|
|
28
|
-
});
|
|
29
|
-
});
|
package/shared/cliMode.test.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { resolveCliMode } from "./cliMode.mjs";
|
|
3
|
-
|
|
4
|
-
describe("resolveCliMode", () => {
|
|
5
|
-
test("returns help for no-arg interactive invocation", () => {
|
|
6
|
-
expect(resolveCliMode(undefined, { stdinIsTTY: true })).toBe("help");
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
test("returns hook for no-arg piped invocation", () => {
|
|
10
|
-
expect(resolveCliMode(undefined, { stdinIsTTY: false })).toBe("hook");
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
test("defaults no-arg invocation to hook mode", () => {
|
|
14
|
-
expect(resolveCliMode(undefined)).toBe("hook");
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
test("treats upgrade as update alias", () => {
|
|
18
|
-
expect(resolveCliMode("upgrade")).toBe("update");
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
test("recognizes doctor subcommand", () => {
|
|
22
|
-
expect(resolveCliMode("doctor")).toBe("doctor");
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
test("recognizes help and version flags", () => {
|
|
26
|
-
expect(resolveCliMode("--help")).toBe("help");
|
|
27
|
-
expect(resolveCliMode("-h")).toBe("help");
|
|
28
|
-
expect(resolveCliMode("--version")).toBe("version");
|
|
29
|
-
expect(resolveCliMode("-v")).toBe("version");
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test("marks unknown commands explicitly", () => {
|
|
33
|
-
expect(resolveCliMode("noop")).toBe("unknown");
|
|
34
|
-
});
|
|
35
|
-
});
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { detectPackageManager } from "./packageManager.mjs";
|
|
3
|
-
|
|
4
|
-
describe("detectPackageManager", () => {
|
|
5
|
-
test("prefers explicit OPEN_PLAN_PKG_MANAGER", () => {
|
|
6
|
-
expect(detectPackageManager({ env: { OPEN_PLAN_PKG_MANAGER: "pnpm" } })).toBe("pnpm");
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
test("detects package manager from npm user agent", () => {
|
|
10
|
-
expect(detectPackageManager({ env: { npm_config_user_agent: "pnpm/10.0.0 node/v22.0.0" } })).toBe("pnpm");
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
test("detects package manager from npm execpath", () => {
|
|
14
|
-
expect(detectPackageManager({ env: { npm_execpath: "/usr/local/lib/node_modules/pnpm/bin/pnpm.cjs" } })).toBe(
|
|
15
|
-
"pnpm",
|
|
16
|
-
);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
test("detects package manager from install path hints", () => {
|
|
20
|
-
expect(
|
|
21
|
-
detectPackageManager({
|
|
22
|
-
env: {},
|
|
23
|
-
installPath: "/Users/test/Library/pnpm/global/5/node_modules/open-plan-annotator/bin/open-plan-annotator.mjs",
|
|
24
|
-
}),
|
|
25
|
-
).toBe("pnpm");
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
test("falls back to npm", () => {
|
|
29
|
-
expect(detectPackageManager({ env: {}, installPath: "/tmp/open-plan-annotator/bin/open-plan-annotator.mjs" })).toBe(
|
|
30
|
-
"npm",
|
|
31
|
-
);
|
|
32
|
-
});
|
|
33
|
-
});
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import os from "node:os";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
import { pathToFileURL } from "node:url";
|
|
6
|
-
import { getRuntimePackageName, resolveRuntimeBinary } from "./runtimeResolver.mjs";
|
|
7
|
-
|
|
8
|
-
describe("runtimeResolver", () => {
|
|
9
|
-
test("maps supported platforms to runtime packages", () => {
|
|
10
|
-
expect(getRuntimePackageName("darwin", "arm64")).toBe("@open-plan-annotator/runtime-darwin-arm64");
|
|
11
|
-
expect(getRuntimePackageName("linux", "x64")).toBe("@open-plan-annotator/runtime-linux-x64");
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
test("throws for unsupported platforms", () => {
|
|
15
|
-
expect(() => resolveRuntimeBinary({ platform: "win32", arch: "x64" })).toThrow("Unsupported platform win32-x64");
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
test("resolves binary from installed runtime package", () => {
|
|
19
|
-
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "opa-runtime-"));
|
|
20
|
-
const packageRoot = path.join(tempRoot, "node_modules", "@open-plan-annotator", "runtime-linux-x64");
|
|
21
|
-
const binaryPath = path.join(packageRoot, "bin", "open-plan-annotator");
|
|
22
|
-
|
|
23
|
-
fs.mkdirSync(path.dirname(binaryPath), { recursive: true });
|
|
24
|
-
fs.writeFileSync(path.join(packageRoot, "package.json"), '{"name":"@open-plan-annotator/runtime-linux-x64"}');
|
|
25
|
-
fs.writeFileSync(binaryPath, "binary");
|
|
26
|
-
|
|
27
|
-
const resolved = resolveRuntimeBinary({
|
|
28
|
-
platform: "linux",
|
|
29
|
-
arch: "x64",
|
|
30
|
-
parentUrl: pathToFileURL(path.join(tempRoot, "index.mjs")).href,
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
expect(resolved.packageName).toBe("@open-plan-annotator/runtime-linux-x64");
|
|
34
|
-
expect(fs.realpathSync(resolved.binaryPath)).toBe(fs.realpathSync(binaryPath));
|
|
35
|
-
});
|
|
36
|
-
});
|