@plannotator/pi-extension 0.28.38 → 0.28.40

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.
@@ -0,0 +1,217 @@
1
+ // @generated — DO NOT EDIT. Source: packages/ai/providers/ask-ai-env.ts
2
+ /**
3
+ * The environment of every agent process Ask AI starts (Claude, Codex,
4
+ * OpenCode, Pi, Qwen Code): the live env with Plannotator's own integration
5
+ * switched off inside it.
6
+ *
7
+ * Ask AI drives an agent headlessly, and that agent loads the person's
8
+ * configuration, Plannotator's integration included: the Inbox MCP server
9
+ * (`plannotator inbox mcp`), the agent tool, and the plan hooks (Claude Code's
10
+ * ExitPlanMode PermissionRequest, the Codex Stop hook, Qwen Code's
11
+ * exit_plan_mode). Left on, a question could post to the person's Inbox or
12
+ * block on a plan review nobody is waiting for. So the child gets:
13
+ *
14
+ * - `PLANNOTATOR_INBOX_TOOL=0` and `PLANNOTATOR_AGENT_TOOL=0`: the switches
15
+ * the Claude Code mod, the OpenCode plugin and the Pi extension already
16
+ * read, and which win over config.json (`resolveInboxTool` /
17
+ * `resolveAgentTool` in packages/shared/config.ts).
18
+ * - `PLANNOTATOR_ASK_AI_CHILD=1`: the marker the rest honours
19
+ * (`isAskAiChild` in packages/shared/child-env.ts): the Inbox MCP shim
20
+ * lists no tools, the plan-review hook (`plannotator` with no arguments)
21
+ * exits 0 at once, and the OpenCode plugin stands down.
22
+ *
23
+ * Codex does not pass the env to a stdio MCP server, so codex-app-server.ts
24
+ * also turns the Inbox server off by config (`codexAskAiConfigArgs`, below).
25
+ * Claude sessions disallow it by name (claude-agent-sdk.ts).
26
+ *
27
+ * The review server's agent jobs (code review, Code Tour, Guided Review,
28
+ * `agent-jobs.ts` in both runtimes) get the same env and, for `codex exec`,
29
+ * the same `-c` override: none of them uses Plannotator's integration (each
30
+ * returns its result on stdout or in its `-o` file).
31
+ *
32
+ * Bun-free and Node-safe: vendored into the Pi extension.
33
+ */
34
+
35
+ import { existsSync, readFileSync, statSync } from "node:fs";
36
+ import { homedir } from "node:os";
37
+ import { dirname, join, resolve } from "node:path";
38
+
39
+ /** The marker's name. Only the exact value "1" counts. */
40
+ export const ASK_AI_CHILD_ENV = "PLANNOTATOR_ASK_AI_CHILD";
41
+
42
+ /** What `askAiChildEnv` lays over the env it copies. */
43
+ export const ASK_AI_CHILD_SWITCHES: Readonly<Record<string, string>> = {
44
+ PLANNOTATOR_INBOX_TOOL: "0",
45
+ PLANNOTATOR_AGENT_TOOL: "0",
46
+ [ASK_AI_CHILD_ENV]: "1",
47
+ };
48
+
49
+ /** A copy of `env` (default: the LIVE process.env) with Plannotator's integration off. */
50
+ export function askAiChildEnv(
51
+ env: Readonly<Record<string, string | undefined>> = process.env,
52
+ ): Record<string, string | undefined> {
53
+ return { ...env, ...ASK_AI_CHILD_SWITCHES };
54
+ }
55
+
56
+ /**
57
+ * Run `start` with `ASK_AI_CHILD_SWITCHES` set on process.env, and put
58
+ * process.env back before returning. Only for a spawn API that takes no env
59
+ * and copies process.env synchronously inside the call: the OpenCode SDK's
60
+ * `createOpencodeServer` (its `cross-spawn` call with `{ ...process.env }`
61
+ * comes before its first await). JavaScript runs one call at a time, so no
62
+ * other code, a concurrent Ask AI start included, sees the change.
63
+ * `ask-ai-env.test.ts` runs the installed SDK against a fake `opencode` to
64
+ * keep that true.
65
+ */
66
+ export function withAskAiChildEnv<T>(start: () => T): T {
67
+ const saved = Object.keys(ASK_AI_CHILD_SWITCHES).map((key) => [key, process.env[key]] as const);
68
+ Object.assign(process.env, ASK_AI_CHILD_SWITCHES);
69
+ try {
70
+ return start();
71
+ } finally {
72
+ for (const [key, value] of saved) {
73
+ if (value === undefined) delete process.env[key];
74
+ else process.env[key] = value;
75
+ }
76
+ }
77
+ }
78
+
79
+ /** The Inbox MCP server Plannotator connects to Codex (`INBOX_MCP_SERVER_NAME` in packages/server/inbox-agents.ts). */
80
+ const CODEX_INBOX_SERVER = "plannotator-inbox";
81
+
82
+ /**
83
+ * The global `-c` override every `codex app-server` that Ask AI starts gets,
84
+ * and every `codex exec` agent job of the review server (code review, Code
85
+ * Tour, Guided Review). It turns off the Inbox MCP server. The marker in the
86
+ * agent's env (`askAiChildEnv`) cannot do this, because Codex starts a stdio MCP server with
87
+ * a cleared environment that holds only HOME, PATH and a few others
88
+ * (codex-rs/rmcp-client `create_env_for_mcp_server`). Codex hooks get the
89
+ * whole env, so the Stop hook does see the marker.
90
+ *
91
+ * Codex deep-merges `-c` tables over config.toml and then rejects an entry
92
+ * that has both `command` and `url`, or neither. So:
93
+ * - the person's entry has a `url` (the HTTP transport): only
94
+ * `enabled=false`, because the entry already has its transport;
95
+ * - otherwise: a whole disabled stdio server. It is merged over a stdio entry,
96
+ * or added when there is none. Its own `env` carries the marker, so a Codex
97
+ * that ignored `enabled` would still start a shim that lists no tools.
98
+ * `configTexts` is every config.toml Codex merges for the run
99
+ * (`readCodexConfigTexts`): a `url` in any of them, the person's or the
100
+ * project's, picks the first form. Gap, not handled (rare): Codex ignores the
101
+ * config of a project it does not trust, so a `url` entry only there, with no
102
+ * entry in `$CODEX_HOME`, leaves `enabled=false` with no transport, and Codex
103
+ * (without `--strict-config`) warns "Invalid configuration; using defaults."
104
+ * and runs that session or job on its default config. Literal strings and no
105
+ * spaces: the argument stays unquoted on the cmd.exe line of a Windows `.cmd`
106
+ * shim.
107
+ */
108
+ export function codexAskAiConfigArgs(configTexts: string | null | readonly string[]): string[] {
109
+ const texts = configTexts === null ? [] : typeof configTexts === "string" ? [configTexts] : configTexts;
110
+ if (texts.some((text) => codexInboxEntryHasUrl(text))) {
111
+ return ["-c", `mcp_servers.${CODEX_INBOX_SERVER}.enabled=false`];
112
+ }
113
+ return [
114
+ "-c",
115
+ `mcp_servers.${CODEX_INBOX_SERVER}={command='plannotator',args=['inbox','mcp'],env={PLANNOTATOR_ASK_AI_CHILD='1'},enabled=false}`,
116
+ ];
117
+ }
118
+
119
+ /**
120
+ * The config.toml files Codex reads for a run in `cwd`, read only, the
121
+ * unreadable ones left out: `$CODEX_HOME/config.toml` (a relative CODEX_HOME
122
+ * resolved against `cwd`, as Codex does; else `~/.codex`), then the
123
+ * `.codex/config.toml` of each folder from `cwd` up to the project root, the
124
+ * nearest folder with a `.git` (a folder holding `HEAD`, or a worktree's
125
+ * file). With no root, only `cwd`'s. The `.codex` that is CODEX_HOME is not
126
+ * read twice. This is Codex's own walk (codex-rs/config `discover_project_root`
127
+ * and `discover_project_layers`), with its default `project_root_markers`.
128
+ */
129
+ export function readCodexConfigTexts(cwd: string, env: Readonly<Record<string, string | undefined>> = process.env): string[] {
130
+ const start = resolve(cwd);
131
+ const home = env.CODEX_HOME?.trim();
132
+ const codexHome = home ? resolve(start, home) : join(homedir(), ".codex");
133
+ const dirs: string[] = [];
134
+ for (let dir = start; ; dir = dirname(dir)) {
135
+ dirs.push(dir);
136
+ if (isGitRoot(dir)) break;
137
+ if (dirname(dir) === dir) {
138
+ dirs.length = 1;
139
+ break;
140
+ }
141
+ }
142
+ const folders = [codexHome, ...dirs.map((dir) => join(dir, ".codex")).filter((dot) => dot !== codexHome)];
143
+ return folders.flatMap((folder) => {
144
+ try {
145
+ return [readFileSync(join(folder, "config.toml"), "utf8")];
146
+ } catch {
147
+ return [];
148
+ }
149
+ });
150
+ }
151
+
152
+ /** Whether `dir` holds a `.git` that Codex takes as the project root marker. */
153
+ function isGitRoot(dir: string): boolean {
154
+ try {
155
+ const git = statSync(join(dir, ".git"));
156
+ return git.isFile() || (git.isDirectory() && existsSync(join(dir, ".git", "HEAD")));
157
+ } catch {
158
+ return false;
159
+ }
160
+ }
161
+
162
+ /** A TOML key (`a."b-c".d`) as its parts; null when the text is not a plain key. */
163
+ function tomlKeyPath(text: string): string[] | null {
164
+ const part = /\s*(?:"((?:[^"\\]|\\.)*)"|'([^']*)'|([A-Za-z0-9_-]+))\s*(\.|$)/y;
165
+ const parts: string[] = [];
166
+ let at = 0;
167
+ while (at < text.length) {
168
+ part.lastIndex = at;
169
+ const match = part.exec(text);
170
+ if (!match) return null;
171
+ parts.push(match[1] ?? match[2] ?? match[3]!);
172
+ at = part.lastIndex;
173
+ if (match[4] !== ".") break;
174
+ }
175
+ return parts.length > 0 && at >= text.length ? parts : null;
176
+ }
177
+
178
+ /** The first key of each top-level member of an inline table (`{ url = "…", env = { … } }`). */
179
+ function inlineTableKeys(value: string): string[] {
180
+ let inner = value.replace(/"(?:[^"\\]|\\.)*"|'[^']*'/g, '""').trim();
181
+ if (!inner.startsWith("{")) return [];
182
+ const close = inner.lastIndexOf("}");
183
+ inner = inner.slice(1, close < 0 ? undefined : close);
184
+ // Drop nested tables and arrays, innermost first, so only top-level members remain.
185
+ const nested = /[{[][^{}[\]]*[}\]]/;
186
+ while (nested.test(inner)) inner = inner.replace(new RegExp(nested.source, "g"), "");
187
+ return inner
188
+ .split(",")
189
+ .map((member) => tomlKeyPath(member.slice(0, Math.max(0, member.indexOf("="))))?.[0])
190
+ .filter((key): key is string => key !== undefined);
191
+ }
192
+
193
+ /**
194
+ * Whether config.toml's `mcp_servers.plannotator-inbox` has a `url`, in any
195
+ * form: its `[mcp_servers.plannotator-inbox]` table, an inline table, or
196
+ * dotted keys. A line scan, not a full TOML parser: something it cannot read
197
+ * counts as no `url`.
198
+ */
199
+ export function codexInboxEntryHasUrl(configText: string): boolean {
200
+ let table: string[] = [];
201
+ for (const raw of configText.split(/\r?\n/)) {
202
+ const line = raw.trim();
203
+ if (!line || line.startsWith("#")) continue;
204
+ const header = /^\[\[?([^\]]*)\]\]?\s*(?:#.*)?$/.exec(line);
205
+ if (header) {
206
+ table = tomlKeyPath(header[1]!) ?? [""];
207
+ continue;
208
+ }
209
+ const eq = line.indexOf("=");
210
+ const key = eq > 0 ? tomlKeyPath(line.slice(0, eq)) : null;
211
+ if (!key) continue;
212
+ const path = [...table, ...key];
213
+ if (path[0] !== "mcp_servers" || path[1] !== CODEX_INBOX_SERVER) continue;
214
+ if (path.length > 2 ? path[2] === "url" : inlineTableKeys(line.slice(eq + 1)).includes("url")) return true;
215
+ }
216
+ return false;
217
+ }
@@ -30,6 +30,7 @@ import {
30
30
  type CatalogModel,
31
31
  type ClaudeSdkModelInfo,
32
32
  } from '../model-catalog.ts';
33
+ import { askAiChildEnv } from "./ask-ai-env.ts";
33
34
 
34
35
  // ---------------------------------------------------------------------------
35
36
  // Constants
@@ -162,8 +163,20 @@ interface ClaudeSDKQueryOptions {
162
163
  settingSources?: string[];
163
164
  effort?: string;
164
165
  strictMcpConfig?: boolean;
166
+ /** The whole env of the `claude` process (the SDK does not merge it with process.env). */
167
+ env: Record<string, string | undefined>;
168
+ disallowedTools: string[];
165
169
  }
166
170
 
171
+ /**
172
+ * Removed from every Ask AI session whatever the person's settings allow: the
173
+ * Plannotator Inbox MCP server, by its whole-server name (`mcp__<server>`, a
174
+ * server-level spec the SDK's `disallowedTools` takes). This covers an
175
+ * HTTP-transport `plannotator-inbox` entry too, which the env marker
176
+ * (ask-ai-env.ts) cannot reach.
177
+ */
178
+ const ASK_AI_DISALLOWED_TOOLS = ["mcp__plannotator-inbox"];
179
+
167
180
  // ---------------------------------------------------------------------------
168
181
  // Provider
169
182
  // ---------------------------------------------------------------------------
@@ -281,6 +294,7 @@ export class ClaudeAgentSDKProvider implements AIProvider {
281
294
  // option out, so the SDK loads every source, like the CLI.
282
295
  settingSources: [],
283
296
  strictMcpConfig: true,
297
+ env: askAiChildEnv(),
284
298
  ...(this.config.claudeExecutablePath && {
285
299
  pathToClaudeCodeExecutable: this.config.claudeExecutablePath,
286
300
  }),
@@ -462,10 +476,15 @@ class ClaudeAgentSDKSession extends BaseSession {
462
476
  ...(this.config.effort && { effort: this.config.effort }),
463
477
  maxTurns: this.config.maxTurns,
464
478
  allowedTools: this.config.allowedTools,
479
+ disallowedTools: ASK_AI_DISALLOWED_TOOLS,
465
480
  cwd: this.config.cwd,
466
481
  abortController: this._currentAbort!,
467
482
  includePartialMessages: true,
468
483
  persistSession: true,
484
+ // Plannotator's integration off in this `claude` (ask-ai-env.ts): the
485
+ // plugin's ExitPlanMode hook exits at once instead of opening a review.
486
+ // The SDK replaces the child's env with this, so it is the full live env.
487
+ env: askAiChildEnv(),
469
488
  ...(this.config.claudeExecutablePath && {
470
489
  pathToClaudeCodeExecutable: this.config.claudeExecutablePath,
471
490
  }),
@@ -45,6 +45,7 @@ import {
45
45
  resolveWindowsCommandShim,
46
46
  } from "./command-path.ts";
47
47
  import { guardChildStreams, writeChildLine } from "./child-io.ts";
48
+ import { askAiChildEnv, codexAskAiConfigArgs, readCodexConfigTexts } from "./ask-ai-env.ts";
48
49
  import { CODEX_FALLBACK_MODELS, cliVersionFrom, effortList, type CatalogModel } from '../model-catalog.ts';
49
50
 
50
51
  const PROVIDER_NAME = "codex-sdk";
@@ -68,6 +69,10 @@ const FILE_APPROVAL_METHOD = "item/fileChange/requestApproval";
68
69
  // response shape from the two above ({permissions} grant, not {decision}).
69
70
  const PERMISSIONS_APPROVAL_METHOD = "item/permissions/requestApproval";
70
71
 
72
+ // The `-c` override that turns the Inbox MCP server off lives in
73
+ // ask-ai-env.ts, so the review server's `codex exec` jobs use it too.
74
+ export { codexAskAiConfigArgs, codexInboxEntryHasUrl, readCodexConfigTexts } from "./ask-ai-env.ts";
75
+
71
76
  // ---------------------------------------------------------------------------
72
77
  // Pure helpers (exported for testing)
73
78
  // ---------------------------------------------------------------------------
@@ -292,7 +297,7 @@ export function mapApprovalRequest(
292
297
  // JSON-RPC over stdio (bidirectional: responses, notifications, server requests)
293
298
  // ---------------------------------------------------------------------------
294
299
 
295
- type NotificationListener = (notification: { method: string; params: RpcMessage }) => void;
300
+ type NotificationListener =(notification: { method: string; params: RpcMessage }) => void;
296
301
  type RequestHandler = (method: string, id: string | number, params: RpcMessage) => void;
297
302
 
298
303
  class CodexAppServerProcess {
@@ -327,18 +332,21 @@ class CodexAppServerProcess {
327
332
 
328
333
  private async doStart(codexPath: string, cwd: string, initTimeoutMs?: number): Promise<void> {
329
334
  const commandPath = resolveWindowsCommandShim(codexPath);
335
+ const appServerArgs = [...codexAskAiConfigArgs(readCodexConfigTexts(cwd)), "app-server"];
330
336
  const command =
331
- buildWindowsCommandScriptSpawnCommand(commandPath, ["app-server"]) ?? [
337
+ buildWindowsCommandScriptSpawnCommand(commandPath, appServerArgs) ?? [
332
338
  commandPath,
333
- "app-server",
339
+ ...appServerArgs,
334
340
  ];
335
341
 
336
342
  let proc: ChildProcess;
337
343
  try {
338
344
  const [file, ...args] = command;
339
345
  // stderr is "ignore", not "pipe": we never read it, and an un-drained
340
- // stderr pipe deadlocks the child once its buffer fills.
341
- proc = spawn(file, args, { cwd, stdio: ["pipe", "pipe", "ignore"] });
346
+ // stderr pipe deadlocks the child once its buffer fills. The env turns
347
+ // Plannotator's integration off in this Codex (ask-ai-env.ts): its hooks
348
+ // read the marker; the Inbox MCP server is off by the `-c` overrides.
349
+ proc = spawn(file, args, { cwd, env: askAiChildEnv(), stdio: ["pipe", "pipe", "ignore"] });
342
350
  } catch (err) {
343
351
  const error = err instanceof Error ? err : new Error(String(err));
344
352
  this.handleProcessEnd(error);
@@ -14,6 +14,7 @@
14
14
  import type { OpencodeClient } from "@opencode-ai/sdk";
15
15
  import { BaseSession } from "../base-session.ts";
16
16
  import { buildSystemPrompt } from "../context.ts";
17
+ import { withAskAiChildEnv } from "./ask-ai-env.ts";
17
18
  import type {
18
19
  AIMessage,
19
20
  AIProvider,
@@ -93,11 +94,18 @@ export class OpenCodeProvider implements AIProvider {
93
94
  // port. An explicitly configured port is still honored verbatim.
94
95
  let server: { url: string; close: () => void };
95
96
  try {
96
- server = await createOpencodeServer({
97
- hostname: this.config.hostname ?? "127.0.0.1",
98
- port: this.config.port ?? 0,
99
- timeout: 15_000,
100
- });
97
+ // Plannotator's integration off in this `opencode serve`
98
+ // (ask-ai-env.ts): the Plannotator OpenCode plugin it loads reads
99
+ // process.env and stands down. The SDK takes no env, so the switches
100
+ // are set on process.env only for the synchronous part of the call,
101
+ // where the SDK copies it into the spawn.
102
+ server = await withAskAiChildEnv(() =>
103
+ createOpencodeServer({
104
+ hostname: this.config.hostname ?? "127.0.0.1",
105
+ port: this.config.port ?? 0,
106
+ timeout: 15_000,
107
+ }),
108
+ );
101
109
  } catch (err: unknown) {
102
110
  // A missing CLI is a spawn error with code ENOENT in every runtime
103
111
  // (Node: "spawn opencode ENOENT"; Bun: "Executable not found in
@@ -2,7 +2,7 @@
2
2
  /**
3
3
  * Pi event mapping — shared between Bun and Node.js Pi providers.
4
4
  *
5
- * Pure function, no runtime-specific dependencies.
5
+ * Pure functions, no runtime-specific dependencies.
6
6
  */
7
7
 
8
8
  import type { AIMessage } from "../types.ts";
@@ -33,6 +33,7 @@ import {
33
33
  } from "./command-path.ts";
34
34
  import { createPiVersionProbe } from "./pi-version.ts";
35
35
  import { guardChildStreams, writeChildLine } from "./child-io.ts";
36
+ import { askAiChildEnv } from "./ask-ai-env.ts";
36
37
 
37
38
  // Re-export mapPiEvent from shared (runtime-agnostic)
38
39
  export { mapPiEvent } from "./pi-events.ts";
@@ -77,6 +78,10 @@ export class PiProcessNode {
77
78
  // (same reasoning as codex-app-server.ts).
78
79
  proc = spawn(file, args, {
79
80
  cwd,
81
+ // Plannotator's integration off in this throwaway Pi: Pi's RPC
82
+ // mode reports `ctx.hasUI`, so the Plannotator Pi extension would
83
+ // otherwise take it for a person's session (ask-ai-env.ts).
84
+ env: askAiChildEnv(),
80
85
  stdio: ["pipe", "pipe", "ignore"],
81
86
  });
82
87
  } catch (err) {
@@ -29,6 +29,7 @@ import {
29
29
  resolveWindowsCommandShim,
30
30
  } from "./command-path.ts";
31
31
  import { createPiVersionProbe } from "./pi-version.ts";
32
+ import { askAiChildEnv } from "./ask-ai-env.ts";
32
33
 
33
34
  // ---------------------------------------------------------------------------
34
35
  // Constants
@@ -72,8 +73,11 @@ class PiProcess {
72
73
  cwd,
73
74
  // The LIVE env, never Bun's startup env (which still holds the
74
75
  // variables the CLI scrubbed, like the bridge token): see
75
- // packages/shared/child-env.ts.
76
- env: { ...process.env },
76
+ // packages/shared/child-env.ts. Plannotator's integration off
77
+ // (ask-ai-env.ts): Pi's RPC mode reports `ctx.hasUI`, so the
78
+ // Plannotator Pi extension would otherwise take this throwaway
79
+ // process for a person's session and connect it to the Inbox.
80
+ env: askAiChildEnv(),
77
81
  stdin: "pipe",
78
82
  stdout: "pipe",
79
83
  stderr: "ignore",
@@ -51,6 +51,7 @@ import {
51
51
  resolveWindowsCommandShim,
52
52
  } from "./command-path.ts";
53
53
  import { guardChildStreams, toChildError, writeChildLine } from "./child-io.ts";
54
+ import { askAiChildEnv } from "./ask-ai-env.ts";
54
55
  import { mapQwenLine, newQwenAskAISessionId } from "./qwen-events.ts";
55
56
 
56
57
  export { mapQwenLine, QWEN_ASK_AI_SESSION_PREFIX } from "./qwen-events.ts";
@@ -213,7 +214,10 @@ export class QwenProcess {
213
214
  try {
214
215
  proc = spawn(file!, rest, {
215
216
  cwd,
216
- env: { ...process.env },
217
+ // Plannotator's integration off in this Qwen (ask-ai-env.ts):
218
+ // the Inbox MCP server Plannotator adds to Qwen's settings.json
219
+ // inherits the env, and so lists no tools.
220
+ env: askAiChildEnv(),
217
221
  stdio: ["pipe", "pipe", "pipe"],
218
222
  windowsHide: true,
219
223
  });
@@ -41,7 +41,9 @@ export interface CallFlowLanguageDefinition {
41
41
  const MB = 1024 * 1024;
42
42
 
43
43
  /**
44
- * CallDiff 0.4.1's supported extensions and pinned grammar packages.
44
+ * CallDiff 0.6.0's supported extensions and pinned grammar packages. Perl,
45
+ * new upstream in 0.6.0, is left out: tree-sitter-perl ships no prebuilt
46
+ * binaries, so a pack would need a C compiler on every machine.
45
47
  *
46
48
  * Size estimates describe Plannotator's pruned managed artifacts, not the
47
49
  * much larger npm tarball expansion. They are deliberately rounded up and
@@ -55,7 +57,7 @@ export const CALL_FLOW_LANGUAGES = [
55
57
  { id: "java", label: "Java", extensions: [".java"], kind: "pack", packageName: "tree-sitter-java", packageVersion: "0.23.5", installSizeBytes: 1 * MB },
56
58
  { id: "ruby", label: "Ruby", extensions: [".rb"], kind: "pack", packageName: "tree-sitter-ruby", packageVersion: "0.23.1", installSizeBytes: 3 * MB },
57
59
  { id: "c", label: "C", extensions: [".c", ".h"], kind: "pack", packageName: "tree-sitter-c", packageVersion: "0.24.1", installSizeBytes: 1 * MB },
58
- { id: "cpp", label: "C++", extensions: [".cc", ".cpp", ".cxx", ".hpp", ".hh"], kind: "pack", packageName: "tree-sitter-cpp", packageVersion: "0.23.4", installSizeBytes: 4 * MB },
60
+ { id: "cpp", label: "C++", extensions: [".cc", ".cpp", ".cu", ".cuh", ".cxx", ".hpp", ".hh"], kind: "pack", packageName: "tree-sitter-cpp", packageVersion: "0.23.4", installSizeBytes: 4 * MB },
59
61
  { id: "csharp", label: "C#", extensions: [".cs"], kind: "pack", packageName: "tree-sitter-c-sharp", packageVersion: "0.23.1", installSizeBytes: 6 * MB },
60
62
  { id: "php", label: "PHP", extensions: [".php"], kind: "pack", packageName: "tree-sitter-php", packageVersion: "0.24.2", installSizeBytes: 17 * MB },
61
63
  { id: "kotlin", label: "Kotlin", extensions: [".kt", ".kts"], kind: "pack", packageName: "tree-sitter-kotlin", packageVersion: "0.3.8", installSizeBytes: 4 * MB },
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "plannotator-call-flow-runtime",
3
- "version": "0.4.1-plannotator.2",
3
+ "version": "0.6.0-plannotator.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "plannotator-call-flow-runtime",
9
- "version": "0.4.1-plannotator.2",
9
+ "version": "0.6.0-plannotator.1",
10
10
  "dependencies": {
11
11
  "@types/node": "24.2.0",
12
- "calldiff": "file:calldiff-a3194d20ca91ef6a314273d634e9b9c0db1c2707.tar.gz",
12
+ "calldiff": "file:calldiff-2086f6e4e9ed3c410bba7c94ca3dde77791b1956.tar.gz",
13
13
  "tree-sitter": "0.25.1",
14
14
  "tree-sitter-javascript": "0.25.0",
15
15
  "tree-sitter-typescript": "0.23.2",
@@ -64,9 +64,9 @@
64
64
  }
65
65
  },
66
66
  "node_modules/calldiff": {
67
- "version": "0.4.1",
68
- "resolved": "file:calldiff-a3194d20ca91ef6a314273d634e9b9c0db1c2707.tar.gz",
69
- "integrity": "sha512-5y6tjre5UE00qPrVFPurnk9RBdk8WlRok+0w41lxLt1MyPLOlNUEaAT2/j/nz2xWlhDm3DfDYvW/5KBD2b9TLg==",
67
+ "version": "0.6.0",
68
+ "resolved": "file:calldiff-2086f6e4e9ed3c410bba7c94ca3dde77791b1956.tar.gz",
69
+ "integrity": "sha512-t+YCPz0WkETiwSOznwx3mLxVkEUe7DVACV+it2HJER7q/ha+K0IavN9IfBvP+7/ijzPXoUHgJIkAM/kg1O5S6A==",
70
70
  "license": "MIT",
71
71
  "dependencies": {
72
72
  "incur": "^0.4.26",
@@ -78,7 +78,7 @@
78
78
  "calldiff": "dist/cli.js"
79
79
  },
80
80
  "engines": {
81
- "node": ">=22"
81
+ "node": ">=22.18.0"
82
82
  }
83
83
  },
84
84
  "node_modules/incur": {
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "plannotator-call-flow-runtime",
3
- "version": "0.4.1-plannotator.2",
3
+ "version": "0.6.0-plannotator.1",
4
4
  "private": true,
5
5
  "description": "Managed offline runtime for Plannotator call-flow analysis",
6
6
  "dependencies": {
7
7
  "@types/node": "24.2.0",
8
- "calldiff": "file:calldiff-a3194d20ca91ef6a314273d634e9b9c0db1c2707.tar.gz",
8
+ "calldiff": "file:calldiff-2086f6e4e9ed3c410bba7c94ca3dde77791b1956.tar.gz",
9
9
  "tree-sitter": "0.25.1",
10
10
  "tree-sitter-javascript": "0.25.0",
11
11
  "tree-sitter-typescript": "0.23.2",