@plannotator/pi-extension 0.19.15 → 0.19.16

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.
@@ -1,4 +1,4 @@
1
- import { execSync } from "node:child_process";
1
+ import { execSync, spawn } from "node:child_process";
2
2
  import { readFileSync, existsSync } from "node:fs";
3
3
  import { createServer } from "node:http";
4
4
  import os from "node:os";
@@ -84,6 +84,13 @@ import {
84
84
  transformClaudeFindings,
85
85
  } from "../generated/claude-review.js";
86
86
  import { createTourSession, TOUR_EMPTY_OUTPUT_ERROR } from "../generated/tour-review.js";
87
+ import {
88
+ type CodeNavRequest,
89
+ type CodeNavRuntime,
90
+ resolveCodeNav,
91
+ validateCodeNavRequest,
92
+ extractChangedFiles,
93
+ } from "../generated/code-nav.js";
87
94
  import {
88
95
  canStageFiles,
89
96
  detectRemoteDefaultCompareTarget,
@@ -96,6 +103,37 @@ import {
96
103
  unstageFile,
97
104
  } from "./vcs.js";
98
105
 
106
+ const piCodeNavRuntime: CodeNavRuntime = {
107
+ runCommand(command, args, options) {
108
+ return new Promise((resolve) => {
109
+ const proc = spawn(command, args, {
110
+ cwd: options?.cwd,
111
+ stdio: ["ignore", "pipe", "pipe"],
112
+ });
113
+ let timer: ReturnType<typeof setTimeout> | undefined;
114
+ if (options?.timeoutMs) {
115
+ timer = setTimeout(() => proc.kill(), options.timeoutMs);
116
+ }
117
+ const stdoutChunks: Buffer[] = [];
118
+ const stderrChunks: Buffer[] = [];
119
+ proc.stdout!.on("data", (chunk: Buffer) => stdoutChunks.push(chunk));
120
+ proc.stderr!.on("data", (chunk: Buffer) => stderrChunks.push(chunk));
121
+ proc.on("close", (code: number | null) => {
122
+ if (timer) clearTimeout(timer);
123
+ resolve({
124
+ stdout: Buffer.concat(stdoutChunks).toString("utf-8"),
125
+ stderr: Buffer.concat(stderrChunks).toString("utf-8"),
126
+ exitCode: code ?? 1,
127
+ });
128
+ });
129
+ proc.on("error", () => {
130
+ if (timer) clearTimeout(timer);
131
+ resolve({ stdout: "", stderr: "command not found", exitCode: 1 });
132
+ });
133
+ });
134
+ },
135
+ };
136
+
99
137
  /** Detect if running inside WSL (Windows Subsystem for Linux) */
100
138
  function detectWSL(): boolean {
101
139
  if (process.platform !== "linux") return false;
@@ -965,6 +1003,48 @@ export async function startReviewServer(options: {
965
1003
  }
966
1004
 
967
1005
  json(res, { error: "No file access available" }, 400);
1006
+ } else if (url.pathname === "/api/code-nav/resolve" && req.method === "POST") {
1007
+ const hasCodeNavAccess = !!options.gitContext || !!options.agentCwd || !!options.worktreePool;
1008
+ if (!hasCodeNavAccess) {
1009
+ json(res, { error: "Code navigation requires local access" }, 400);
1010
+ return;
1011
+ }
1012
+ try {
1013
+ const body = (await parseBody(req)) as unknown as CodeNavRequest;
1014
+ const error = validateCodeNavRequest(body);
1015
+ if (error) {
1016
+ json(res, { error }, 400);
1017
+ return;
1018
+ }
1019
+ const navCwd = resolveAgentCwd();
1020
+ const changedFiles = extractChangedFiles(currentPatch);
1021
+ const result = await resolveCodeNav(piCodeNavRuntime, body, navCwd, changedFiles);
1022
+ json(res, result);
1023
+ } catch (err) {
1024
+ json(res, { error: err instanceof Error ? err.message : "Code navigation failed" }, 500);
1025
+ }
1026
+ } else if (url.pathname === "/api/code-nav/file" && req.method === "GET") {
1027
+ const hasCodeNavAccess = !!options.gitContext || !!options.agentCwd || !!options.worktreePool;
1028
+ if (!hasCodeNavAccess) {
1029
+ json(res, { error: "Code navigation requires local access" }, 400);
1030
+ return;
1031
+ }
1032
+ const filePath = url.searchParams.get("path");
1033
+ if (!filePath) {
1034
+ json(res, { error: "Missing path" }, 400);
1035
+ return;
1036
+ }
1037
+ try { validateFilePath(filePath); } catch {
1038
+ json(res, { error: "Invalid path" }, 400);
1039
+ return;
1040
+ }
1041
+ try {
1042
+ const navCwd = resolveAgentCwd();
1043
+ const content = readFileSync(`${navCwd}/${filePath}`, "utf-8");
1044
+ json(res, { content });
1045
+ } catch {
1046
+ json(res, { error: "File not found" }, 404);
1047
+ }
968
1048
  } else if (url.pathname === "/api/config" && req.method === "POST") {
969
1049
  try {
970
1050
  const body = (await parseBody(req)) as { displayName?: string; diffOptions?: Record<string, unknown>; conventionalComments?: boolean };