open-plan-annotator 1.1.3 → 1.1.5

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.
@@ -5,14 +5,14 @@
5
5
  },
6
6
  "metadata": {
7
7
  "description": "Interactive plan annotation plugin for Claude Code",
8
- "version": "1.1.3"
8
+ "version": "1.1.5"
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.3",
15
+ "version": "1.1.5",
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.3",
4
+ "version": "1.1.5",
5
5
  "author": {
6
6
  "name": "ndom91"
7
7
  },
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- ![](.github/assets/header.jpg)
1
+ ![](.github/assets/header2.jpg)
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/open-plan-annotator?style=for-the-badge&labelColor=black&color=black)](https://www.npmjs.com/package/open-plan-annotator)
4
4
  [![License: MIT](https://img.shields.io/badge/license-MIT-orange.svg?style=for-the-badge&labelColor=black&color=black)](https://opensource.org/licenses/MIT)
@@ -8,7 +8,7 @@ import { buildCliHelpText, buildUnknownCommandPrefix } from "../shared/cliHelp.m
8
8
  import { resolveCliMode } from "../shared/cliMode.mjs";
9
9
  import { detectPackageManager } from "../shared/packageManager.mjs";
10
10
  import { resolveRuntimeBinary } from "../shared/runtimeResolver.mjs";
11
- import { buildUpdateInstructions } from "../shared/updateHints.mjs";
11
+ import { buildUpdateMessage } from "../shared/updateMessage.mjs";
12
12
 
13
13
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
14
  const VERSION = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf8")).version;
@@ -27,7 +27,7 @@ if (cliMode === "help") {
27
27
  }
28
28
 
29
29
  if (cliMode === "doctor") {
30
- printDoctor();
30
+ await printDoctor();
31
31
  process.exit(0);
32
32
  }
33
33
 
@@ -51,7 +51,12 @@ if (cliMode === "hook") {
51
51
  }
52
52
 
53
53
  if (cliMode === "update") {
54
- console.log(buildUpdateInstructions({ host: process.env.OPEN_PLAN_HOST, packageManager: detectPackageManager({ installPath: fileURLToPath(import.meta.url) }) }));
54
+ console.log(
55
+ await buildUpdateMessage({
56
+ currentVersion: VERSION,
57
+ packageManager: detectPackageManager({ installPath: fileURLToPath(import.meta.url) }),
58
+ }),
59
+ );
55
60
  process.exit(0);
56
61
  }
57
62
 
@@ -118,8 +123,10 @@ child.on("error", (err) => {
118
123
  process.exit(1);
119
124
  });
120
125
 
121
- function printDoctor() {
126
+ async function printDoctor() {
122
127
  const platformKey = `${process.platform}-${process.arch}`;
128
+ const packageManager = detectPackageManager({ installPath: fileURLToPath(import.meta.url) });
129
+ const latestVersionLine = `update: ${await buildUpdateMessage({ currentVersion: VERSION, packageManager })}`;
123
130
 
124
131
  try {
125
132
  const runtime = resolveRuntimeBinary({ parentUrl: import.meta.url });
@@ -128,7 +135,7 @@ function printDoctor() {
128
135
  `platform: ${platformKey}`,
129
136
  `runtime package: ${runtime.packageName}`,
130
137
  `runtime path: ${runtime.binaryPath}`,
131
- `update: ${buildUpdateInstructions({ host: process.env.OPEN_PLAN_HOST, packageManager: detectPackageManager({ installPath: fileURLToPath(import.meta.url) }) })}`,
138
+ latestVersionLine,
132
139
  ].join("\n"));
133
140
  } catch (error) {
134
141
  console.log([
@@ -136,7 +143,7 @@ function printDoctor() {
136
143
  `platform: ${platformKey}`,
137
144
  `runtime: missing`,
138
145
  `error: ${error instanceof Error ? error.message : String(error)}`,
139
- `update: ${buildUpdateInstructions({ host: process.env.OPEN_PLAN_HOST, packageManager: detectPackageManager({ installPath: fileURLToPath(import.meta.url) }) })}`,
146
+ latestVersionLine,
140
147
  ].join("\n"));
141
148
  }
142
149
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-plan-annotator",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "type": "module",
5
5
  "description": "Fully local plugin for interactive plan annotation from your Agentic assistants",
6
6
  "author": "ndom91",
@@ -29,8 +29,10 @@
29
29
  "shared/cliHelp.mjs",
30
30
  "shared/cliMode.mjs",
31
31
  "shared/packageManager.mjs",
32
+ "shared/updateMessage.mjs",
32
33
  "shared/runtimeResolver.mjs",
33
34
  "shared/updateHints.mjs",
35
+ "shared/versionInfo.mjs",
34
36
  ".claude-plugin/",
35
37
  "opencode/bridge.js",
36
38
  "opencode/config.js",
@@ -67,9 +69,9 @@
67
69
  "@opencode-ai/plugin": "^1.2.14"
68
70
  },
69
71
  "optionalDependencies": {
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"
72
+ "@open-plan-annotator/runtime-darwin-arm64": "1.1.5",
73
+ "@open-plan-annotator/runtime-darwin-x64": "1.1.5",
74
+ "@open-plan-annotator/runtime-linux-arm64": "1.1.5",
75
+ "@open-plan-annotator/runtime-linux-x64": "1.1.5"
74
76
  }
75
77
  }
@@ -1,14 +1,23 @@
1
1
  export function buildUpdateInstructions(options = {}) {
2
2
  const host = options.host ?? process.env.OPEN_PLAN_HOST;
3
3
  const packageManager = options.packageManager ?? "npm";
4
+ const version = options.version ?? "latest";
4
5
 
5
6
  if (host === "opencode") {
6
- return "Refresh the OpenCode plugin install, then restart OpenCode.";
7
+ return "Refresh the OpenCode plugin install and restart OpenCode.";
7
8
  }
8
9
 
9
10
  if (host === "claude-code") {
10
- return "Refresh the Claude Code plugin or marketplace install, then restart Claude Code.";
11
+ return "Refresh the Claude Code plugin or marketplace install and restart Claude Code.";
11
12
  }
12
13
 
13
- return `Update open-plan-annotator via ${packageManager}, then rerun it.`;
14
+ if (packageManager === "pnpm") {
15
+ return `Run \`pnpm i -g open-plan-annotator@${version}\`.`;
16
+ }
17
+
18
+ if (packageManager === "bun") {
19
+ return `Run \`bun add -g open-plan-annotator@${version}\`.`;
20
+ }
21
+
22
+ return `Run \`npm i -g open-plan-annotator@${version}\`.`;
14
23
  }
@@ -0,0 +1,25 @@
1
+ import { buildUpdateInstructions } from "./updateHints.mjs";
2
+ import { fetchLatestVersion, isNewerVersion } from "./versionInfo.mjs";
3
+
4
+ export async function buildUpdateMessage(options = {}) {
5
+ const currentVersion = options.currentVersion;
6
+ const packageManager = options.packageManager ?? "npm";
7
+ const host = options.host ?? process.env.OPEN_PLAN_HOST;
8
+
9
+ try {
10
+ const latestVersion = await fetchLatestVersion();
11
+ if (currentVersion && isNewerVersion(currentVersion, latestVersion)) {
12
+ return `latest v${latestVersion}; ${buildUpdateInstructions({ host, packageManager, version: latestVersion })}`;
13
+ }
14
+
15
+ if (currentVersion) {
16
+ return `latest v${latestVersion}; already up to date`;
17
+ }
18
+
19
+ return buildUpdateInstructions({ host, packageManager, version: latestVersion });
20
+ } catch {
21
+ return currentVersion
22
+ ? `latest unknown; ${buildUpdateInstructions({ host, packageManager })}`
23
+ : buildUpdateInstructions({ host, packageManager });
24
+ }
25
+ }
@@ -0,0 +1,85 @@
1
+ const NPM_REGISTRY_BASE_URL = "https://registry.npmjs.org";
2
+
3
+ export function normalizeVersion(version) {
4
+ return version.trim().replace(/^v/, "");
5
+ }
6
+
7
+ function parseSemver(version) {
8
+ const normalized = normalizeVersion(version);
9
+ const match = normalized.match(/^([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/);
10
+ if (!match) return null;
11
+
12
+ return {
13
+ major: Number.parseInt(match[1], 10),
14
+ minor: Number.parseInt(match[2], 10),
15
+ patch: Number.parseInt(match[3], 10),
16
+ prerelease: match[4] ? match[4].split(".") : [],
17
+ };
18
+ }
19
+
20
+ function comparePrereleaseIdentifier(a, b) {
21
+ const aIsNum = /^[0-9]+$/.test(a);
22
+ const bIsNum = /^[0-9]+$/.test(b);
23
+
24
+ if (aIsNum && bIsNum) {
25
+ const aNum = Number.parseInt(a, 10);
26
+ const bNum = Number.parseInt(b, 10);
27
+ return aNum === bNum ? 0 : aNum > bNum ? 1 : -1;
28
+ }
29
+
30
+ if (aIsNum) return -1;
31
+ if (bIsNum) return 1;
32
+ if (a === b) return 0;
33
+ return a > b ? 1 : -1;
34
+ }
35
+
36
+ function compareSemver(a, b) {
37
+ const parsedA = parseSemver(a);
38
+ const parsedB = parseSemver(b);
39
+ if (!parsedA || !parsedB) return 0;
40
+
41
+ if (parsedA.major !== parsedB.major) return parsedA.major > parsedB.major ? 1 : -1;
42
+ if (parsedA.minor !== parsedB.minor) return parsedA.minor > parsedB.minor ? 1 : -1;
43
+ if (parsedA.patch !== parsedB.patch) return parsedA.patch > parsedB.patch ? 1 : -1;
44
+
45
+ if (parsedA.prerelease.length === 0 && parsedB.prerelease.length === 0) return 0;
46
+ if (parsedA.prerelease.length === 0) return 1;
47
+ if (parsedB.prerelease.length === 0) return -1;
48
+
49
+ const maxLen = Math.max(parsedA.prerelease.length, parsedB.prerelease.length);
50
+ for (let i = 0; i < maxLen; i += 1) {
51
+ const aPart = parsedA.prerelease[i];
52
+ const bPart = parsedB.prerelease[i];
53
+ if (aPart === undefined) return -1;
54
+ if (bPart === undefined) return 1;
55
+ const diff = comparePrereleaseIdentifier(aPart, bPart);
56
+ if (diff !== 0) return diff;
57
+ }
58
+
59
+ return 0;
60
+ }
61
+
62
+ export function isNewerVersion(current, latest) {
63
+ const parsedCurrent = parseSemver(current);
64
+ const parsedLatest = parseSemver(latest);
65
+ if (!parsedCurrent || !parsedLatest) return false;
66
+ return compareSemver(latest, current) > 0;
67
+ }
68
+
69
+ export async function fetchLatestVersion(packageName = "open-plan-annotator") {
70
+ const response = await fetch(`${NPM_REGISTRY_BASE_URL}/${encodeURIComponent(packageName)}/latest`, {
71
+ headers: { "User-Agent": "open-plan-annotator-update-check", Accept: "application/json" },
72
+ signal: AbortSignal.timeout(10_000),
73
+ });
74
+
75
+ if (!response.ok) {
76
+ throw new Error(`npm registry responded with ${response.status}`);
77
+ }
78
+
79
+ const payload = await response.json();
80
+ if (!payload || typeof payload !== "object" || typeof payload.version !== "string") {
81
+ throw new Error("npm registry response did not include a version string");
82
+ }
83
+
84
+ return normalizeVersion(payload.version);
85
+ }