open-plan-annotator 1.1.0 → 1.1.2
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 +3 -8
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +12 -7
- package/package.json +21 -14
- 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,19 +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.2"
|
|
9
9
|
},
|
|
10
10
|
"plugins": [
|
|
11
11
|
{
|
|
12
12
|
"name": "open-plan-annotator",
|
|
13
|
-
"source":
|
|
14
|
-
"npm": {
|
|
15
|
-
"package": "open-plan-annotator",
|
|
16
|
-
"version": "1.1.0"
|
|
17
|
-
}
|
|
18
|
-
},
|
|
13
|
+
"source": "./",
|
|
19
14
|
"description": "Interactive plan annotation UI: review, strikethrough, and comment on Claude's plans before approving. Fully local, no external services.",
|
|
20
|
-
"version": "1.1.
|
|
15
|
+
"version": "1.1.2",
|
|
21
16
|
"author": {
|
|
22
17
|
"name": "ndom91"
|
|
23
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.2",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "ndom91"
|
|
7
7
|
},
|
package/README.md
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+

|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/open-plan-annotator)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[]()
|
|
6
6
|
|
|
7
7
|
A fully local agentic coding plugin that intercepts plan mode and opens an annotation UI in your browser. Mark up the plan, send structured feedback to the agent, and receive a revised version — iterate as many times as you need until you're ready to approve.
|
|
8
8
|
|
|
9
9
|
Select text to <code>strikethrough</code>, <code>replace</code>, <code>insert</code>, or <code>comment</code> — then approve the plan or request changes
|
|
10
10
|
|
|
11
|
-

|
|
12
11
|
|
|
13
12
|
## How It Works
|
|
14
13
|
|
|
@@ -20,6 +19,8 @@ Select text to <code>strikethrough</code>, <code>replace</code>, <code>insert</c
|
|
|
20
19
|
|
|
21
20
|
Everything runs locally. Nothing leaves your machine.
|
|
22
21
|
|
|
22
|
+

|
|
23
|
+
|
|
23
24
|
## Install
|
|
24
25
|
|
|
25
26
|
> [!NOTE]
|
|
@@ -36,7 +37,7 @@ From within Claude Code, add the marketplace and install the plugin:
|
|
|
36
37
|
/plugin install open-plan-annotator@ndom91-open-plan-annotator
|
|
37
38
|
```
|
|
38
39
|
|
|
39
|
-
This installs the npm-backed plugin and registers the `ExitPlanMode` hook that launches the annotation UI.
|
|
40
|
+
This installs the npm-backed plugin and registers the `ExitPlanMode` hook that launches the annotation UI. In Claude Code, third-party marketplaces have auto-update disabled by default, so also enable auto-update for the `ndom91-open-plan-annotator` marketplace in the Marketplace UI.
|
|
40
41
|
|
|
41
42
|
### OpenCode
|
|
42
43
|
|
|
@@ -44,7 +45,7 @@ Add `open-plan-annotator` to the `plugin` array in your OpenCode config (`openco
|
|
|
44
45
|
|
|
45
46
|
```json
|
|
46
47
|
{
|
|
47
|
-
"plugin": ["open-plan-annotator"]
|
|
48
|
+
"plugin": ["open-plan-annotator@latest"]
|
|
48
49
|
}
|
|
49
50
|
```
|
|
50
51
|
|
|
@@ -57,6 +58,9 @@ OpenCode will install the package and load it automatically. The plugin:
|
|
|
57
58
|
|
|
58
59
|
To update, refresh the plugin through OpenCode and restart the app so it reloads the latest package-managed runtime.
|
|
59
60
|
|
|
61
|
+
> [!NOTE]
|
|
62
|
+
> The update mechanism changed significantly in `1.0.20+`: OpenCode now loads the npm package plus a platform runtime package instead of using the old in-place binary updater. If OpenCode appears to be stuck on an older plugin build, clear the cached `open-plan-annotator` entries under `~/.cache/opencode/node_modules/` and restart OpenCode.
|
|
63
|
+
|
|
60
64
|
#### Implementation Handoff
|
|
61
65
|
|
|
62
66
|
By default, after a plan is approved the plugin sends "Proceed with implementation." to a `build` agent. To customize or disable this, create `open-plan-annotator.json` in your project's `.opencode/` directory or globally in `~/.config/opencode/`:
|
|
@@ -77,6 +81,7 @@ Set `enabled` to `false` to disable auto-handoff. Project config overrides globa
|
|
|
77
81
|
If you want to run the CLI standalone or install the package globally:
|
|
78
82
|
|
|
79
83
|
```sh
|
|
84
|
+
pnpm add -g open-plan-annotator
|
|
80
85
|
npm install -g open-plan-annotator
|
|
81
86
|
```
|
|
82
87
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "open-plan-annotator",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
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"
|
|
@@ -37,10 +43,10 @@
|
|
|
37
43
|
"test": "bun test",
|
|
38
44
|
"typecheck": "tsgo --noEmit --project ui/tsconfig.json && tsgo --noEmit --project server/tsconfig.json",
|
|
39
45
|
"build:ui": "cd ui && bun run vite build",
|
|
40
|
-
"build:platforms": "
|
|
41
|
-
"build": "bun run build:ui &&
|
|
46
|
+
"build:platforms": "bun scripts/build-platforms.mjs",
|
|
47
|
+
"build": "bun run build:ui && bun run build:platforms",
|
|
42
48
|
"release": "bun run build",
|
|
43
|
-
"pack:check": "
|
|
49
|
+
"pack:check": "bun scripts/check-package-files.mjs",
|
|
44
50
|
"dev:ui": "cd ui && bun run vite --port 5173",
|
|
45
51
|
"dev:server": "NODE_ENV=development bun run server/index.ts",
|
|
46
52
|
"dev": "NODE_ENV=development bun run server/index.ts & cd ui && bun run vite --port 5173",
|
|
@@ -48,11 +54,11 @@
|
|
|
48
54
|
"lint:fix": "biome check --write .",
|
|
49
55
|
"format": "biome format --write .",
|
|
50
56
|
"do-release": "./scripts/release.sh",
|
|
51
|
-
"prepack": "
|
|
52
|
-
"postpack": "
|
|
57
|
+
"prepack": "bun scripts/claude-pack-docs.mjs prepack",
|
|
58
|
+
"postpack": "bun scripts/claude-pack-docs.mjs postpack"
|
|
53
59
|
},
|
|
54
60
|
"devDependencies": {
|
|
55
|
-
"@biomejs/biome": "^2.4.
|
|
61
|
+
"@biomejs/biome": "^2.4.6",
|
|
56
62
|
"@types/node": "^25.3.0",
|
|
57
63
|
"@types/bun": "^1.3.9",
|
|
58
64
|
"@typescript/native-preview": "^7.0.0-dev.20260224.1"
|
|
@@ -61,9 +67,10 @@
|
|
|
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
|
-
}
|
|
70
|
+
"@open-plan-annotator/runtime-darwin-arm64": "1.1.2",
|
|
71
|
+
"@open-plan-annotator/runtime-darwin-x64": "1.1.2",
|
|
72
|
+
"@open-plan-annotator/runtime-linux-arm64": "1.1.2",
|
|
73
|
+
"@open-plan-annotator/runtime-linux-x64": "1.1.2"
|
|
74
|
+
},
|
|
75
|
+
"packageManager": "bun@1.3.9"
|
|
69
76
|
}
|
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
|
-
});
|