@usepipr/runtime 0.4.3 → 0.6.0
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/LICENSE +21 -0
- package/README.md +8 -5
- package/dist/{commands-DA0Ij4Di.d.mts → commands-BHXh7P28.d.mts} +2 -2
- package/dist/{commands-DA0Ij4Di.d.mts.map → commands-BHXh7P28.d.mts.map} +1 -1
- package/dist/{commands-B9qNW4pk.mjs → commands-GWHklzvq.mjs} +1612 -466
- package/dist/commands-GWHklzvq.mjs.map +1 -0
- package/dist/index.d.mts +17 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +117 -15
- package/dist/index.mjs.map +1 -1
- package/dist/internal/action-result.d.mts +4 -2
- package/dist/internal/action-result.d.mts.map +1 -1
- package/dist/internal/action-result.mjs +28 -12
- package/dist/internal/action-result.mjs.map +1 -1
- package/dist/internal/docs.d.mts +1 -1
- package/dist/internal/docs.d.mts.map +1 -1
- package/dist/internal/docs.mjs +1 -1
- package/dist/internal/pipr-result.d.mts +24 -0
- package/dist/internal/pipr-result.d.mts.map +1 -0
- package/dist/internal/pipr-result.mjs +126 -0
- package/dist/internal/pipr-result.mjs.map +1 -0
- package/dist/internal/testing.d.mts +12 -11
- package/dist/internal/testing.d.mts.map +1 -1
- package/dist/internal/testing.mjs +1 -1
- package/dist/main-comment-envelope-DFirHYhW.mjs +69 -0
- package/dist/main-comment-envelope-DFirHYhW.mjs.map +1 -0
- package/dist/{official-github-workflow-Bjlu-FL3.mjs → official-github-workflow-B3yeaCgq.mjs} +421 -114
- package/dist/official-github-workflow-B3yeaCgq.mjs.map +1 -0
- package/dist/pi/runtime-tools-extension.d.mts.map +1 -1
- package/dist/pi/runtime-tools-extension.mjs +128 -2
- package/dist/pi/runtime-tools-extension.mjs.map +1 -1
- package/dist/{recipes-w72EpAOi.d.mts → recipes-C1kxJCgQ.d.mts} +2 -1
- package/dist/recipes-C1kxJCgQ.d.mts.map +1 -0
- package/dist/runtime-tools-core-BF8rSeNS.mjs +476 -0
- package/dist/runtime-tools-core-BF8rSeNS.mjs.map +1 -0
- package/dist/{types-BAis_Lwm.d.mts → types-w22wbdo9.d.mts} +72 -19
- package/dist/types-w22wbdo9.d.mts.map +1 -0
- package/package.json +16 -8
- package/dist/commands-B9qNW4pk.mjs.map +0 -1
- package/dist/official-github-workflow-Bjlu-FL3.mjs.map +0 -1
- package/dist/recipes-w72EpAOi.d.mts.map +0 -1
- package/dist/runtime-tools-core-D8_WsbL_.mjs +0 -179
- package/dist/runtime-tools-core-D8_WsbL_.mjs.map +0 -1
- package/dist/types-BAis_Lwm.d.mts.map +0 -1
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
import { lstat } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
//#region src/shared/record.ts
|
|
6
|
+
function isRecord(value) {
|
|
7
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/diff/manifest-structure.ts
|
|
11
|
+
const maxSymbolsPerFile = 32;
|
|
12
|
+
const maxSymbolCharacters = 120;
|
|
13
|
+
const maxSummaryCharacters = 200;
|
|
14
|
+
const maxDerivedMetadataBytes = 32 * 1024;
|
|
15
|
+
function enrichDiffManifestWithStructure(manifest, analysis) {
|
|
16
|
+
if (!analysis.available) return manifest;
|
|
17
|
+
const budget = {
|
|
18
|
+
remaining: maxDerivedMetadataBytes,
|
|
19
|
+
exhausted: false
|
|
20
|
+
};
|
|
21
|
+
return {
|
|
22
|
+
...manifest,
|
|
23
|
+
files: manifest.files.map((file) => enrichFile(file, analysis, budget))
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function findEnclosingDeclaration(file, range, analysis) {
|
|
27
|
+
if (!analysis.available) return;
|
|
28
|
+
const ref = range.side === "LEFT" ? "base" : "head";
|
|
29
|
+
const sourcePath = ref === "base" ? file.previousPath ?? file.path : file.path;
|
|
30
|
+
const declaration = (ref === "base" ? analysis.baseFiles : analysis.headFiles).find((candidate) => candidate.path === sourcePath)?.declarations.filter((candidate) => candidate.startLine <= range.startLine && candidate.endLine >= range.endLine).sort(compareContainingDeclarations)[0];
|
|
31
|
+
return declaration ? {
|
|
32
|
+
declaration,
|
|
33
|
+
ref,
|
|
34
|
+
sourcePath
|
|
35
|
+
} : void 0;
|
|
36
|
+
}
|
|
37
|
+
function enrichFile(file, analysis, budget) {
|
|
38
|
+
const owners = /* @__PURE__ */ new Map();
|
|
39
|
+
const commentableRanges = file.commentableRanges.map((range) => {
|
|
40
|
+
const owner = findEnclosingDeclaration(file, range, analysis)?.declaration;
|
|
41
|
+
if (owner) owners.set(owner.qualifiedName, owner);
|
|
42
|
+
if (!owner || range.summary !== void 0 || budget.exhausted) return range;
|
|
43
|
+
const summary = truncate(`Enclosing declaration: ${owner.kind} ${owner.qualifiedName}`, maxSummaryCharacters);
|
|
44
|
+
return consumeDerivedMetadata(budget, summary) ? {
|
|
45
|
+
...range,
|
|
46
|
+
summary
|
|
47
|
+
} : range;
|
|
48
|
+
});
|
|
49
|
+
const existingSymbols = [...file.changedSymbols ?? []];
|
|
50
|
+
const symbolSet = new Set(existingSymbols);
|
|
51
|
+
const derivedSymbols = [];
|
|
52
|
+
for (const owner of [...owners.values()].sort(compareSourceOrder)) {
|
|
53
|
+
if (budget.exhausted || existingSymbols.length + derivedSymbols.length >= maxSymbolsPerFile) break;
|
|
54
|
+
const symbol = truncate(owner.qualifiedName, maxSymbolCharacters);
|
|
55
|
+
if (symbolSet.has(symbol)) continue;
|
|
56
|
+
if (!consumeDerivedMetadata(budget, symbol)) break;
|
|
57
|
+
symbolSet.add(symbol);
|
|
58
|
+
derivedSymbols.push(symbol);
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
...file,
|
|
62
|
+
...existingSymbols.length > 0 || derivedSymbols.length > 0 ? { changedSymbols: [...existingSymbols, ...derivedSymbols] } : {},
|
|
63
|
+
commentableRanges
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function compareContainingDeclarations(left, right) {
|
|
67
|
+
return left.endLine - left.startLine - (right.endLine - right.startLine) || right.startLine - left.startLine || left.qualifiedName.localeCompare(right.qualifiedName);
|
|
68
|
+
}
|
|
69
|
+
function compareSourceOrder(left, right) {
|
|
70
|
+
return left.startLine - right.startLine || left.endLine - right.endLine || left.qualifiedName.localeCompare(right.qualifiedName);
|
|
71
|
+
}
|
|
72
|
+
function consumeDerivedMetadata(budget, value) {
|
|
73
|
+
const bytes = Buffer.byteLength(value, "utf8");
|
|
74
|
+
if (bytes > budget.remaining) {
|
|
75
|
+
budget.exhausted = true;
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
budget.remaining -= bytes;
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
function truncate(value, maxCharacters) {
|
|
82
|
+
return value.length <= maxCharacters ? value : value.slice(0, maxCharacters);
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/diff/ranges.ts
|
|
86
|
+
function createDiffRangeIndex(manifest) {
|
|
87
|
+
const filesByPath = new Map(manifest.files.map((file) => [file.path, file]));
|
|
88
|
+
const rangesById = /* @__PURE__ */ new Map();
|
|
89
|
+
for (const file of manifest.files) for (const range of file.commentableRanges) rangesById.set(range.id, {
|
|
90
|
+
file,
|
|
91
|
+
range
|
|
92
|
+
});
|
|
93
|
+
return {
|
|
94
|
+
fileByPath(filePath) {
|
|
95
|
+
return filesByPath.get(filePath);
|
|
96
|
+
},
|
|
97
|
+
excludedReason(filePath) {
|
|
98
|
+
return filesByPath.get(filePath)?.excludedReason;
|
|
99
|
+
},
|
|
100
|
+
findRange(rangeId) {
|
|
101
|
+
return rangesById.get(rangeId);
|
|
102
|
+
},
|
|
103
|
+
rangeById(rangeId) {
|
|
104
|
+
return rangesById.get(rangeId)?.range;
|
|
105
|
+
},
|
|
106
|
+
requireFile(filePath) {
|
|
107
|
+
const file = filesByPath.get(filePath);
|
|
108
|
+
if (!file) throw new Error(`Path '${filePath}' is not in the Diff Manifest`);
|
|
109
|
+
return file;
|
|
110
|
+
},
|
|
111
|
+
requireRangeInFile(file, rangeId) {
|
|
112
|
+
const range = file.commentableRanges.find((item) => item.id === rangeId);
|
|
113
|
+
if (range) return range;
|
|
114
|
+
if (rangesById.has(rangeId)) throw new Error(`Diff Manifest range '${rangeId}' is not in path '${file.path}'`);
|
|
115
|
+
throw new Error(`Unknown Diff Manifest range '${rangeId}'`);
|
|
116
|
+
},
|
|
117
|
+
requireHunk(file, range) {
|
|
118
|
+
const hunk = file.hunks.find((item) => item.hunkIndex === range.hunkIndex && item.contentHash === range.hunkContentHash);
|
|
119
|
+
if (!hunk) throw new Error(`Diff Manifest range '${range.id}' has no matching hunk`);
|
|
120
|
+
return hunk;
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/pi/runtime-tools-core.ts
|
|
126
|
+
const readAtRefContextLines = 3;
|
|
127
|
+
const readDiffParamsSchema = z.preprocess((params) => {
|
|
128
|
+
const record = isRecord(params) ? params : {};
|
|
129
|
+
return {
|
|
130
|
+
path: typeof record.path === "string" ? record.path : void 0,
|
|
131
|
+
rangeId: typeof record.rangeId === "string" ? record.rangeId : void 0
|
|
132
|
+
};
|
|
133
|
+
}, z.object({
|
|
134
|
+
path: z.string().optional(),
|
|
135
|
+
rangeId: z.string().optional()
|
|
136
|
+
}));
|
|
137
|
+
const readAtRefParamsSchema = z.preprocess((params) => isRecord(params) ? params : {}, z.object({
|
|
138
|
+
path: z.unknown(),
|
|
139
|
+
ref: z.enum(["base", "head"], { error: (issue) => `Unsupported ref '${String(issue.input)}'` }),
|
|
140
|
+
rangeId: z.string({ error: "rangeId must be a string" })
|
|
141
|
+
}));
|
|
142
|
+
const astGrepSearchParamsSchema = z.strictObject({
|
|
143
|
+
pattern: z.string().min(1).max(4096),
|
|
144
|
+
language: z.string().min(1).max(64),
|
|
145
|
+
paths: z.array(z.string()).min(1).max(16)
|
|
146
|
+
});
|
|
147
|
+
const astGrepMatchSchema = z.looseObject({
|
|
148
|
+
text: z.string(),
|
|
149
|
+
file: z.string(),
|
|
150
|
+
range: z.object({
|
|
151
|
+
start: z.object({
|
|
152
|
+
line: z.number().int().nonnegative(),
|
|
153
|
+
column: z.number().int().nonnegative()
|
|
154
|
+
}),
|
|
155
|
+
end: z.object({
|
|
156
|
+
line: z.number().int().nonnegative(),
|
|
157
|
+
column: z.number().int().nonnegative()
|
|
158
|
+
})
|
|
159
|
+
})
|
|
160
|
+
});
|
|
161
|
+
const astGrepMatchesSchema = z.array(astGrepMatchSchema);
|
|
162
|
+
function readDiffFromRuntimeData(data, params) {
|
|
163
|
+
const { rangeId } = params;
|
|
164
|
+
const filePath = params.path === void 0 ? void 0 : parseManifestPath(params.path);
|
|
165
|
+
const ranges = createDiffRangeIndex(data.manifest);
|
|
166
|
+
if (filePath !== void 0) ranges.requireFile(filePath);
|
|
167
|
+
if (rangeId !== void 0 && !ranges.findRange(rangeId)) throw new Error(`Unknown Diff Manifest range '${rangeId}'`);
|
|
168
|
+
return boundedJson({ files: data.manifest.files.filter((file) => filePath === void 0 || file.path === filePath).map((file) => filterManifestFileRanges(file, rangeId)).filter((file) => rangeId === void 0 || file.commentableRanges.length > 0) }, data.toolResponseMaxBytes);
|
|
169
|
+
}
|
|
170
|
+
function boundedJson(value, maxBytes) {
|
|
171
|
+
const text = JSON.stringify(value, null, 2);
|
|
172
|
+
const bytes = Buffer.byteLength(text, "utf8");
|
|
173
|
+
if (bytes <= maxBytes) return {
|
|
174
|
+
truncated: false,
|
|
175
|
+
bytes,
|
|
176
|
+
value
|
|
177
|
+
};
|
|
178
|
+
return {
|
|
179
|
+
truncated: true,
|
|
180
|
+
bytes,
|
|
181
|
+
maxBytes,
|
|
182
|
+
text: Buffer.from(text, "utf8").subarray(0, maxBytes).toString("utf8")
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function boundedLineSlice(content, window, maxBytes) {
|
|
186
|
+
const lines = content.match(/[^\n]*(?:\n|$)/g) ?? [];
|
|
187
|
+
if (lines.at(-1) === "") lines.pop();
|
|
188
|
+
const slice = lines.slice(window.startLine - 1, window.endLine).join("");
|
|
189
|
+
const buffer = Buffer.from(slice, "utf8");
|
|
190
|
+
return {
|
|
191
|
+
available: true,
|
|
192
|
+
content: buffer.subarray(0, maxBytes).toString("utf8"),
|
|
193
|
+
bytes: buffer.byteLength,
|
|
194
|
+
truncated: buffer.byteLength > maxBytes
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function resolveReadAtRefRequest(manifest, params) {
|
|
198
|
+
const filePath = parseManifestPath(params.path);
|
|
199
|
+
const ranges = createDiffRangeIndex(manifest);
|
|
200
|
+
const file = ranges.requireFile(filePath);
|
|
201
|
+
const range = ranges.requireRangeInFile(file, params.rangeId);
|
|
202
|
+
const hunk = ranges.requireHunk(file, range);
|
|
203
|
+
const sourcePath = parseManifestPath(params.ref === "base" ? file.previousPath ?? file.path : file.path);
|
|
204
|
+
return {
|
|
205
|
+
file,
|
|
206
|
+
range,
|
|
207
|
+
hunk,
|
|
208
|
+
ref: params.ref,
|
|
209
|
+
sourcePath,
|
|
210
|
+
window: lineWindowForRange(range, hunk, params.ref)
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
function unavailableReadAtRefResult(request) {
|
|
214
|
+
return {
|
|
215
|
+
path: request.file.path,
|
|
216
|
+
ref: request.ref,
|
|
217
|
+
sourcePath: request.sourcePath,
|
|
218
|
+
rangeId: request.range.id,
|
|
219
|
+
startLine: 0,
|
|
220
|
+
endLine: 0,
|
|
221
|
+
available: false
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
function parseManifestPath(filePath) {
|
|
225
|
+
if (typeof filePath !== "string" || filePath.length === 0 || filePath.includes("\0") || path.isAbsolute(filePath) || filePath.split(/[\\/]/).some((part) => part === ".." || part === ".git" || part === "")) throw new Error(`Unsafe manifest path '${String(filePath)}'`);
|
|
226
|
+
return filePath;
|
|
227
|
+
}
|
|
228
|
+
function resolveAllowedPath(root, filePath) {
|
|
229
|
+
const resolved = path.resolve(root, filePath);
|
|
230
|
+
const relative = path.relative(root, resolved);
|
|
231
|
+
if (relative.startsWith("..") || path.isAbsolute(relative)) throw new Error(`Path '${filePath}' resolves outside the workspace`);
|
|
232
|
+
return resolved;
|
|
233
|
+
}
|
|
234
|
+
async function assertNoSymlinkPath(root, filePath) {
|
|
235
|
+
const parts = filePath.split(/[\\/]/);
|
|
236
|
+
let current = root;
|
|
237
|
+
for (const part of parts) {
|
|
238
|
+
current = path.join(current, part);
|
|
239
|
+
if ((await lstat(current)).isSymbolicLink()) throw new Error(`Path '${filePath}' crosses a symlink`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function readDiffParams(params) {
|
|
243
|
+
return readDiffParamsSchema.parse(params);
|
|
244
|
+
}
|
|
245
|
+
function readAtRefParams(params) {
|
|
246
|
+
const parsed = readAtRefParamsSchema.parse(params);
|
|
247
|
+
return {
|
|
248
|
+
path: parseManifestPath(parsed.path),
|
|
249
|
+
ref: parsed.ref,
|
|
250
|
+
rangeId: parsed.rangeId
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function readDeclarationParams(params) {
|
|
254
|
+
return readAtRefParams(params);
|
|
255
|
+
}
|
|
256
|
+
function astGrepSearchParams(params) {
|
|
257
|
+
const parsed = astGrepSearchParamsSchema.parse(params);
|
|
258
|
+
return {
|
|
259
|
+
pattern: parsed.pattern,
|
|
260
|
+
language: parsed.language,
|
|
261
|
+
paths: parsed.paths.map(parseSearchPath)
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
function resolveDeclarationRequest(data, params) {
|
|
265
|
+
const filePath = parseManifestPath(params.path);
|
|
266
|
+
const ranges = createDiffRangeIndex(data.manifest);
|
|
267
|
+
const file = ranges.requireFile(filePath);
|
|
268
|
+
const range = ranges.requireRangeInFile(file, params.rangeId);
|
|
269
|
+
const sourcePath = parseManifestPath(params.ref === "base" ? file.previousPath ?? file.path : file.path);
|
|
270
|
+
const expectedSide = params.ref === "base" ? "LEFT" : "RIGHT";
|
|
271
|
+
if (!data.structuralAnalysis || range.side !== expectedSide) return {
|
|
272
|
+
available: false,
|
|
273
|
+
path: file.path,
|
|
274
|
+
sourcePath,
|
|
275
|
+
ref: params.ref,
|
|
276
|
+
rangeId: range.id
|
|
277
|
+
};
|
|
278
|
+
const owner = findEnclosingDeclaration(file, range, data.structuralAnalysis);
|
|
279
|
+
if (!owner || owner.ref !== params.ref) return {
|
|
280
|
+
available: false,
|
|
281
|
+
path: file.path,
|
|
282
|
+
sourcePath,
|
|
283
|
+
ref: params.ref,
|
|
284
|
+
rangeId: range.id
|
|
285
|
+
};
|
|
286
|
+
return {
|
|
287
|
+
available: true,
|
|
288
|
+
path: file.path,
|
|
289
|
+
sourcePath: owner.sourcePath,
|
|
290
|
+
ref: params.ref,
|
|
291
|
+
rangeId: range.id,
|
|
292
|
+
declaration: owner.declaration
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
async function runAstGrepSearch(options) {
|
|
296
|
+
for (const searchPath of options.params.paths) if (searchPath !== ".") {
|
|
297
|
+
resolveAllowedPath(options.cwd, searchPath);
|
|
298
|
+
await assertNoSymlinkPath(options.cwd, searchPath);
|
|
299
|
+
}
|
|
300
|
+
const result = await runAstGrepProcess([
|
|
301
|
+
"ast-grep",
|
|
302
|
+
"run",
|
|
303
|
+
"--pattern",
|
|
304
|
+
options.params.pattern,
|
|
305
|
+
"--lang",
|
|
306
|
+
options.params.language,
|
|
307
|
+
"--json=compact",
|
|
308
|
+
"--color",
|
|
309
|
+
"never",
|
|
310
|
+
"--",
|
|
311
|
+
...options.params.paths
|
|
312
|
+
], {
|
|
313
|
+
cwd: options.cwd,
|
|
314
|
+
env: options.env,
|
|
315
|
+
timeoutMs: options.timeoutMs ?? 1e4
|
|
316
|
+
});
|
|
317
|
+
const output = result.stdout.trim();
|
|
318
|
+
if (result.exitCode === 1 && (output === "" || output === "[]")) return assertSerializedToolResponseFits({
|
|
319
|
+
available: true,
|
|
320
|
+
matches: [],
|
|
321
|
+
truncated: false
|
|
322
|
+
}, options.maxBytes, "pipr_ast_grep response limit is too small");
|
|
323
|
+
if (result.exitCode !== 0) throw new Error("pipr_ast_grep failed");
|
|
324
|
+
let json;
|
|
325
|
+
try {
|
|
326
|
+
json = JSON.parse(output);
|
|
327
|
+
} catch {
|
|
328
|
+
throw new Error("pipr_ast_grep returned invalid output");
|
|
329
|
+
}
|
|
330
|
+
const parsed = astGrepMatchesSchema.safeParse(json);
|
|
331
|
+
if (!parsed.success) throw new Error("pipr_ast_grep returned invalid output");
|
|
332
|
+
const matches = [];
|
|
333
|
+
let truncated = parsed.data.length > 100;
|
|
334
|
+
for (const match of parsed.data.slice(0, 100)) {
|
|
335
|
+
const normalized = {
|
|
336
|
+
path: parseSearchResultPath(match.file),
|
|
337
|
+
startLine: match.range.start.line + 1,
|
|
338
|
+
endLine: match.range.end.line + 1,
|
|
339
|
+
text: truncateUtf8(match.text, 2 * 1024)
|
|
340
|
+
};
|
|
341
|
+
if (serializedToolResponseBytes({
|
|
342
|
+
available: true,
|
|
343
|
+
matches: [...matches, normalized],
|
|
344
|
+
truncated
|
|
345
|
+
}) > options.maxBytes) {
|
|
346
|
+
truncated = true;
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
matches.push(normalized);
|
|
350
|
+
}
|
|
351
|
+
return assertSerializedToolResponseFits({
|
|
352
|
+
available: true,
|
|
353
|
+
matches,
|
|
354
|
+
truncated
|
|
355
|
+
}, options.maxBytes, "pipr_ast_grep response limit is too small");
|
|
356
|
+
}
|
|
357
|
+
function serializedToolResponseBytes(value) {
|
|
358
|
+
return Buffer.byteLength(JSON.stringify(value), "utf8");
|
|
359
|
+
}
|
|
360
|
+
function assertSerializedToolResponseFits(value, maxBytes, errorMessage) {
|
|
361
|
+
if (serializedToolResponseBytes(value) > maxBytes) throw new Error(errorMessage);
|
|
362
|
+
return value;
|
|
363
|
+
}
|
|
364
|
+
function boundToolResponseContent(value, maxBytes, errorMessage) {
|
|
365
|
+
if (serializedToolResponseBytes(value) <= maxBytes) return value;
|
|
366
|
+
const empty = {
|
|
367
|
+
...value,
|
|
368
|
+
content: "",
|
|
369
|
+
truncated: true
|
|
370
|
+
};
|
|
371
|
+
assertSerializedToolResponseFits(empty, maxBytes, errorMessage);
|
|
372
|
+
const originalBuffer = Buffer.from(value.content, "utf8");
|
|
373
|
+
let low = 0;
|
|
374
|
+
let high = originalBuffer.byteLength;
|
|
375
|
+
let best = empty;
|
|
376
|
+
while (low <= high) {
|
|
377
|
+
const middle = Math.floor((low + high) / 2);
|
|
378
|
+
const candidate = {
|
|
379
|
+
...value,
|
|
380
|
+
content: originalBuffer.subarray(0, middle).toString("utf8"),
|
|
381
|
+
truncated: true
|
|
382
|
+
};
|
|
383
|
+
if (serializedToolResponseBytes(candidate) <= maxBytes) {
|
|
384
|
+
best = candidate;
|
|
385
|
+
low = middle + 1;
|
|
386
|
+
} else high = middle - 1;
|
|
387
|
+
}
|
|
388
|
+
return best;
|
|
389
|
+
}
|
|
390
|
+
function filterManifestFileRanges(file, rangeId) {
|
|
391
|
+
if (rangeId === void 0) return file;
|
|
392
|
+
return {
|
|
393
|
+
...file,
|
|
394
|
+
commentableRanges: file.commentableRanges.filter((range) => range.id === rangeId)
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
function lineWindowForRange(range, hunk, ref) {
|
|
398
|
+
const targetSide = ref === "base" ? "LEFT" : "RIGHT";
|
|
399
|
+
if (range.side !== targetSide) return;
|
|
400
|
+
const hunkStart = ref === "base" ? hunk.oldStart : hunk.newStart;
|
|
401
|
+
const hunkLines = ref === "base" ? hunk.oldLines : hunk.newLines;
|
|
402
|
+
if (hunkLines === 0) return;
|
|
403
|
+
const hunkEnd = hunkStart + hunkLines - 1;
|
|
404
|
+
return {
|
|
405
|
+
startLine: Math.max(hunkStart, range.startLine - readAtRefContextLines),
|
|
406
|
+
endLine: Math.min(hunkEnd, range.endLine + readAtRefContextLines)
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
function parseSearchPath(value) {
|
|
410
|
+
if (value === ".") return value;
|
|
411
|
+
if (value.includes("\\") || /[*?[\]{}]/.test(value) || path.isAbsolute(value) || value.includes("\0") || value.split("/").some((part) => part === "" || part === "." || part === ".." || part === ".git")) throw new Error(`Unsafe structural search path '${value}'`);
|
|
412
|
+
return value;
|
|
413
|
+
}
|
|
414
|
+
function parseSearchResultPath(value) {
|
|
415
|
+
try {
|
|
416
|
+
return parseSearchPath(value);
|
|
417
|
+
} catch {
|
|
418
|
+
throw new Error("pipr_ast_grep returned an unsafe path");
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
function truncateUtf8(value, maxBytes) {
|
|
422
|
+
const buffer = Buffer.from(value, "utf8");
|
|
423
|
+
return buffer.byteLength <= maxBytes ? value : buffer.subarray(0, maxBytes).toString("utf8");
|
|
424
|
+
}
|
|
425
|
+
async function runAstGrepProcess(command, options) {
|
|
426
|
+
return await new Promise((resolve, reject) => {
|
|
427
|
+
const child = spawn(command[0], command.slice(1), {
|
|
428
|
+
cwd: options.cwd,
|
|
429
|
+
env: options.env ?? process.env,
|
|
430
|
+
shell: false,
|
|
431
|
+
stdio: [
|
|
432
|
+
"ignore",
|
|
433
|
+
"pipe",
|
|
434
|
+
"pipe"
|
|
435
|
+
]
|
|
436
|
+
});
|
|
437
|
+
const stdout = [];
|
|
438
|
+
let stdoutBytes = 0;
|
|
439
|
+
let stderrBytes = 0;
|
|
440
|
+
let settled = false;
|
|
441
|
+
const fail = (message) => {
|
|
442
|
+
if (settled) return;
|
|
443
|
+
settled = true;
|
|
444
|
+
clearTimeout(timer);
|
|
445
|
+
child.kill("SIGKILL");
|
|
446
|
+
reject(new Error(message));
|
|
447
|
+
};
|
|
448
|
+
const timer = setTimeout(() => fail("pipr_ast_grep timed out"), options.timeoutMs);
|
|
449
|
+
child.on("error", () => fail("pipr_ast_grep is unavailable"));
|
|
450
|
+
child.stdout.on("data", (chunk) => {
|
|
451
|
+
stdoutBytes += chunk.byteLength;
|
|
452
|
+
if (stdoutBytes > 16 * 1024 * 1024) {
|
|
453
|
+
fail("pipr_ast_grep exceeded its output limit");
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
stdout.push(chunk);
|
|
457
|
+
});
|
|
458
|
+
child.stderr.on("data", (chunk) => {
|
|
459
|
+
stderrBytes += chunk.byteLength;
|
|
460
|
+
if (stderrBytes > 1024 * 1024) fail("pipr_ast_grep exceeded its output limit");
|
|
461
|
+
});
|
|
462
|
+
child.on("close", (exitCode) => {
|
|
463
|
+
if (settled) return;
|
|
464
|
+
settled = true;
|
|
465
|
+
clearTimeout(timer);
|
|
466
|
+
resolve({
|
|
467
|
+
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
468
|
+
exitCode: exitCode ?? -1
|
|
469
|
+
});
|
|
470
|
+
});
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
//#endregion
|
|
474
|
+
export { enrichDiffManifestWithStructure as _, boundedLineSlice as a, readDeclarationParams as c, resolveAllowedPath as d, resolveDeclarationRequest as f, createDiffRangeIndex as g, unavailableReadAtRefResult as h, boundToolResponseContent as i, readDiffFromRuntimeData as l, runAstGrepSearch as m, assertSerializedToolResponseFits as n, parseManifestPath as o, resolveReadAtRefRequest as p, astGrepSearchParams as r, readAtRefParams as s, assertNoSymlinkPath as t, readDiffParams as u, findEnclosingDeclaration as v, isRecord as y };
|
|
475
|
+
|
|
476
|
+
//# sourceMappingURL=runtime-tools-core-BF8rSeNS.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-tools-core-BF8rSeNS.mjs","names":[],"sources":["../src/shared/record.ts","../src/diff/manifest-structure.ts","../src/diff/ranges.ts","../src/pi/runtime-tools-core.ts"],"sourcesContent":["export function isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n","import type { CommentableRange, DiffManifest, DiffManifestFile } from \"../types.js\";\nimport type { DiffStructuralAnalysis, StructuralDeclaration } from \"./structural-analysis.js\";\n\nexport type EnclosingDeclaration = {\n declaration: StructuralDeclaration;\n ref: \"base\" | \"head\";\n sourcePath: string;\n};\n\nconst maxSymbolsPerFile = 32;\nconst maxSymbolCharacters = 120;\nconst maxSummaryCharacters = 200;\nconst maxDerivedMetadataBytes = 32 * 1024;\n\nexport function enrichDiffManifestWithStructure(\n manifest: DiffManifest,\n analysis: DiffStructuralAnalysis,\n): DiffManifest {\n if (!analysis.available) {\n return manifest;\n }\n const budget = { remaining: maxDerivedMetadataBytes, exhausted: false };\n return {\n ...manifest,\n files: manifest.files.map((file) => enrichFile(file, analysis, budget)),\n };\n}\n\nexport function findEnclosingDeclaration(\n file: DiffManifestFile,\n range: CommentableRange,\n analysis: DiffStructuralAnalysis,\n): EnclosingDeclaration | undefined {\n if (!analysis.available) {\n return undefined;\n }\n const ref = range.side === \"LEFT\" ? \"base\" : \"head\";\n const sourcePath = ref === \"base\" ? (file.previousPath ?? file.path) : file.path;\n const structuralFile = (ref === \"base\" ? analysis.baseFiles : analysis.headFiles).find(\n (candidate) => candidate.path === sourcePath,\n );\n const declaration = structuralFile?.declarations\n .filter(\n (candidate) => candidate.startLine <= range.startLine && candidate.endLine >= range.endLine,\n )\n .sort(compareContainingDeclarations)[0];\n return declaration ? { declaration, ref, sourcePath } : undefined;\n}\n\nfunction enrichFile(\n file: DiffManifestFile,\n analysis: DiffStructuralAnalysis,\n budget: { remaining: number; exhausted: boolean },\n): DiffManifestFile {\n const owners = new Map<string, StructuralDeclaration>();\n const commentableRanges = file.commentableRanges.map((range) => {\n const owner = findEnclosingDeclaration(file, range, analysis)?.declaration;\n if (owner) {\n owners.set(owner.qualifiedName, owner);\n }\n if (!owner || range.summary !== undefined || budget.exhausted) {\n return range;\n }\n const summary = truncate(\n `Enclosing declaration: ${owner.kind} ${owner.qualifiedName}`,\n maxSummaryCharacters,\n );\n return consumeDerivedMetadata(budget, summary) ? { ...range, summary } : range;\n });\n\n const existingSymbols = [...(file.changedSymbols ?? [])];\n const symbolSet = new Set(existingSymbols);\n const derivedSymbols: string[] = [];\n for (const owner of [...owners.values()].sort(compareSourceOrder)) {\n if (budget.exhausted || existingSymbols.length + derivedSymbols.length >= maxSymbolsPerFile) {\n break;\n }\n const symbol = truncate(owner.qualifiedName, maxSymbolCharacters);\n if (symbolSet.has(symbol)) {\n continue;\n }\n if (!consumeDerivedMetadata(budget, symbol)) {\n break;\n }\n symbolSet.add(symbol);\n derivedSymbols.push(symbol);\n }\n\n return {\n ...file,\n ...(existingSymbols.length > 0 || derivedSymbols.length > 0\n ? { changedSymbols: [...existingSymbols, ...derivedSymbols] }\n : {}),\n commentableRanges,\n };\n}\n\nfunction compareContainingDeclarations(\n left: StructuralDeclaration,\n right: StructuralDeclaration,\n): number {\n return (\n left.endLine - left.startLine - (right.endLine - right.startLine) ||\n right.startLine - left.startLine ||\n left.qualifiedName.localeCompare(right.qualifiedName)\n );\n}\n\nfunction compareSourceOrder(left: StructuralDeclaration, right: StructuralDeclaration): number {\n return (\n left.startLine - right.startLine ||\n left.endLine - right.endLine ||\n left.qualifiedName.localeCompare(right.qualifiedName)\n );\n}\n\nfunction consumeDerivedMetadata(\n budget: { remaining: number; exhausted: boolean },\n value: string,\n): boolean {\n const bytes = Buffer.byteLength(value, \"utf8\");\n if (bytes > budget.remaining) {\n budget.exhausted = true;\n return false;\n }\n budget.remaining -= bytes;\n return true;\n}\n\nfunction truncate(value: string, maxCharacters: number): string {\n return value.length <= maxCharacters ? value : value.slice(0, maxCharacters);\n}\n","import type { CommentableRange, DiffHunk, DiffManifest, DiffManifestFile } from \"../types.js\";\n\nexport type DiffRangeMatch = {\n file: DiffManifestFile;\n range: CommentableRange;\n};\n\nexport type DiffRangeIndex = {\n fileByPath(filePath: string): DiffManifestFile | undefined;\n excludedReason(filePath: string): string | undefined;\n findRange(rangeId: string): DiffRangeMatch | undefined;\n rangeById(rangeId: string): CommentableRange | undefined;\n requireFile(filePath: string): DiffManifestFile;\n requireRangeInFile(file: DiffManifestFile, rangeId: string): CommentableRange;\n requireHunk(file: DiffManifestFile, range: CommentableRange): DiffHunk;\n};\n\nexport function createDiffRangeIndex(manifest: DiffManifest): DiffRangeIndex {\n const filesByPath = new Map(manifest.files.map((file) => [file.path, file]));\n const rangesById = new Map<string, DiffRangeMatch>();\n for (const file of manifest.files) {\n for (const range of file.commentableRanges) {\n rangesById.set(range.id, { file, range });\n }\n }\n\n return {\n fileByPath(filePath) {\n return filesByPath.get(filePath);\n },\n excludedReason(filePath) {\n return filesByPath.get(filePath)?.excludedReason;\n },\n findRange(rangeId) {\n return rangesById.get(rangeId);\n },\n rangeById(rangeId) {\n return rangesById.get(rangeId)?.range;\n },\n requireFile(filePath) {\n const file = filesByPath.get(filePath);\n if (!file) {\n throw new Error(`Path '${filePath}' is not in the Diff Manifest`);\n }\n return file;\n },\n requireRangeInFile(file, rangeId) {\n const range = file.commentableRanges.find((item) => item.id === rangeId);\n if (range) {\n return range;\n }\n if (rangesById.has(rangeId)) {\n throw new Error(`Diff Manifest range '${rangeId}' is not in path '${file.path}'`);\n }\n throw new Error(`Unknown Diff Manifest range '${rangeId}'`);\n },\n requireHunk(file, range) {\n const hunk = file.hunks.find(\n (item) => item.hunkIndex === range.hunkIndex && item.contentHash === range.hunkContentHash,\n );\n if (!hunk) {\n throw new Error(`Diff Manifest range '${range.id}' has no matching hunk`);\n }\n return hunk;\n },\n };\n}\n","import { spawn } from \"node:child_process\";\nimport { lstat } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\nimport { findEnclosingDeclaration } from \"../diff/manifest-structure.js\";\nimport { createDiffRangeIndex } from \"../diff/ranges.js\";\nimport type { DiffStructuralAnalysis, StructuralDeclaration } from \"../diff/structural-analysis.js\";\nimport { isRecord } from \"../shared/record.js\";\nimport type { CommentableRange, DiffHunk, DiffManifest, DiffManifestFile } from \"../types.js\";\n\nconst readAtRefContextLines = 3;\n\nexport type ReadDiffParams = {\n path?: string;\n rangeId?: string;\n};\n\nexport type ReadAtRefParams = {\n path: string;\n ref: \"base\" | \"head\";\n rangeId: string;\n};\n\nexport type RuntimeToolData = {\n manifest: DiffManifest;\n toolResponseMaxBytes: number;\n baseRanges: Record<string, BaseRangeSnapshot>;\n baseDeclarations?: Record<string, BaseDeclarationSnapshot>;\n structuralAnalysis?: Extract<DiffStructuralAnalysis, { available: true }>;\n};\n\nexport type BaseRangeSnapshot = {\n path: string;\n ref: \"base\" | \"head\";\n sourcePath: string;\n rangeId: string;\n startLine: number;\n endLine: number;\n available: boolean;\n relativePath?: string;\n bytes?: number;\n truncated?: boolean;\n};\n\nexport type BaseDeclarationSnapshot = {\n path: string;\n ref: \"base\";\n sourcePath: string;\n rangeId: string;\n declaration: StructuralDeclaration;\n available: boolean;\n relativePath?: string;\n bytes?: number;\n truncated?: boolean;\n};\n\nexport type ReadDeclarationParams = {\n path: string;\n ref: \"base\" | \"head\";\n rangeId: string;\n};\n\nexport type AstGrepSearchParams = {\n pattern: string;\n language: string;\n paths: string[];\n};\n\nexport type ReadAtRefRequest = {\n file: DiffManifestFile;\n range: CommentableRange;\n hunk: DiffHunk;\n ref: \"base\" | \"head\";\n sourcePath: string;\n window: LineWindow | undefined;\n};\n\nexport type LineWindow = {\n startLine: number;\n endLine: number;\n};\n\nexport type LineSliceResult = {\n available: true;\n content: string;\n bytes: number;\n truncated: boolean;\n};\n\nconst readDiffParamsSchema = z.preprocess(\n (params) => {\n const record = isRecord(params) ? params : {};\n return {\n path: typeof record.path === \"string\" ? record.path : undefined,\n rangeId: typeof record.rangeId === \"string\" ? record.rangeId : undefined,\n };\n },\n z.object({\n path: z.string().optional(),\n rangeId: z.string().optional(),\n }),\n);\n\nconst readAtRefParamsSchema = z.preprocess(\n (params) => (isRecord(params) ? params : {}),\n z.object({\n path: z.unknown(),\n ref: z.enum([\"base\", \"head\"], {\n error: (issue) => `Unsupported ref '${String(issue.input)}'`,\n }),\n rangeId: z.string({ error: \"rangeId must be a string\" }),\n }),\n);\nconst astGrepSearchParamsSchema = z.strictObject({\n pattern: z.string().min(1).max(4096),\n language: z.string().min(1).max(64),\n paths: z.array(z.string()).min(1).max(16),\n});\nconst astGrepMatchSchema = z.looseObject({\n text: z.string(),\n file: z.string(),\n range: z.object({\n start: z.object({\n line: z.number().int().nonnegative(),\n column: z.number().int().nonnegative(),\n }),\n end: z.object({\n line: z.number().int().nonnegative(),\n column: z.number().int().nonnegative(),\n }),\n }),\n});\nconst astGrepMatchesSchema = z.array(astGrepMatchSchema);\nexport function readDiffFromRuntimeData(data: RuntimeToolData, params: ReadDiffParams): unknown {\n const { rangeId } = params;\n const filePath = params.path === undefined ? undefined : parseManifestPath(params.path);\n const ranges = createDiffRangeIndex(data.manifest);\n if (filePath !== undefined) {\n ranges.requireFile(filePath);\n }\n if (rangeId !== undefined && !ranges.findRange(rangeId)) {\n throw new Error(`Unknown Diff Manifest range '${rangeId}'`);\n }\n const files = data.manifest.files\n .filter((file) => filePath === undefined || file.path === filePath)\n .map((file) => filterManifestFileRanges(file, rangeId))\n .filter((file) => rangeId === undefined || file.commentableRanges.length > 0);\n return boundedJson({ files }, data.toolResponseMaxBytes);\n}\n\nfunction boundedJson(value: unknown, maxBytes: number): unknown {\n const text = JSON.stringify(value, null, 2);\n const bytes = Buffer.byteLength(text, \"utf8\");\n if (bytes <= maxBytes) {\n return { truncated: false, bytes, value };\n }\n return {\n truncated: true,\n bytes,\n maxBytes,\n text: Buffer.from(text, \"utf8\").subarray(0, maxBytes).toString(\"utf8\"),\n };\n}\n\nexport function boundedLineSlice(\n content: string,\n window: LineWindow,\n maxBytes: number,\n): LineSliceResult {\n const lines = content.match(/[^\\n]*(?:\\n|$)/g) ?? [];\n if (lines.at(-1) === \"\") {\n lines.pop();\n }\n const slice = lines.slice(window.startLine - 1, window.endLine).join(\"\");\n const buffer = Buffer.from(slice, \"utf8\");\n return {\n available: true,\n content: buffer.subarray(0, maxBytes).toString(\"utf8\"),\n bytes: buffer.byteLength,\n truncated: buffer.byteLength > maxBytes,\n };\n}\n\nexport function resolveReadAtRefRequest(\n manifest: DiffManifest,\n params: ReadAtRefParams,\n): ReadAtRefRequest {\n const filePath = parseManifestPath(params.path);\n const ranges = createDiffRangeIndex(manifest);\n const file = ranges.requireFile(filePath);\n const range = ranges.requireRangeInFile(file, params.rangeId);\n const hunk = ranges.requireHunk(file, range);\n const sourcePath = parseManifestPath(\n params.ref === \"base\" ? (file.previousPath ?? file.path) : file.path,\n );\n return {\n file,\n range,\n hunk,\n ref: params.ref,\n sourcePath,\n window: lineWindowForRange(range, hunk, params.ref),\n };\n}\n\nexport function unavailableReadAtRefResult(request: ReadAtRefRequest): BaseRangeSnapshot {\n return {\n path: request.file.path,\n ref: request.ref,\n sourcePath: request.sourcePath,\n rangeId: request.range.id,\n startLine: 0,\n endLine: 0,\n available: false,\n };\n}\n\nexport function parseManifestPath(filePath: unknown): string {\n if (\n typeof filePath !== \"string\" ||\n filePath.length === 0 ||\n filePath.includes(\"\\0\") ||\n path.isAbsolute(filePath) ||\n filePath.split(/[\\\\/]/).some((part) => part === \"..\" || part === \".git\" || part === \"\")\n ) {\n throw new Error(`Unsafe manifest path '${String(filePath)}'`);\n }\n return filePath;\n}\n\nexport function resolveAllowedPath(root: string, filePath: string): string {\n const resolved = path.resolve(root, filePath);\n const relative = path.relative(root, resolved);\n if (relative.startsWith(\"..\") || path.isAbsolute(relative)) {\n throw new Error(`Path '${filePath}' resolves outside the workspace`);\n }\n return resolved;\n}\n\nexport async function assertNoSymlinkPath(root: string, filePath: string): Promise<void> {\n const parts = filePath.split(/[\\\\/]/);\n let current = root;\n for (const part of parts) {\n current = path.join(current, part);\n const stats = await lstat(current);\n if (stats.isSymbolicLink()) {\n throw new Error(`Path '${filePath}' crosses a symlink`);\n }\n }\n}\n\nexport function readDiffParams(params: unknown): ReadDiffParams {\n return readDiffParamsSchema.parse(params);\n}\n\nexport function readAtRefParams(params: unknown): ReadAtRefParams {\n const parsed = readAtRefParamsSchema.parse(params);\n return { path: parseManifestPath(parsed.path), ref: parsed.ref, rangeId: parsed.rangeId };\n}\n\nexport function readDeclarationParams(params: unknown): ReadDeclarationParams {\n return readAtRefParams(params);\n}\n\nexport function astGrepSearchParams(params: unknown): AstGrepSearchParams {\n const parsed = astGrepSearchParamsSchema.parse(params);\n return {\n pattern: parsed.pattern,\n language: parsed.language,\n paths: parsed.paths.map(parseSearchPath),\n };\n}\n\nexport function resolveDeclarationRequest(\n data: RuntimeToolData,\n params: ReadDeclarationParams,\n):\n | {\n available: true;\n path: string;\n sourcePath: string;\n ref: \"base\" | \"head\";\n rangeId: string;\n declaration: StructuralDeclaration;\n }\n | {\n available: false;\n path: string;\n sourcePath: string;\n ref: \"base\" | \"head\";\n rangeId: string;\n } {\n const filePath = parseManifestPath(params.path);\n const ranges = createDiffRangeIndex(data.manifest);\n const file = ranges.requireFile(filePath);\n const range = ranges.requireRangeInFile(file, params.rangeId);\n const sourcePath = parseManifestPath(\n params.ref === \"base\" ? (file.previousPath ?? file.path) : file.path,\n );\n const expectedSide = params.ref === \"base\" ? \"LEFT\" : \"RIGHT\";\n if (!data.structuralAnalysis || range.side !== expectedSide) {\n return {\n available: false,\n path: file.path,\n sourcePath,\n ref: params.ref,\n rangeId: range.id,\n };\n }\n const owner = findEnclosingDeclaration(file, range, data.structuralAnalysis);\n if (!owner || owner.ref !== params.ref) {\n return {\n available: false,\n path: file.path,\n sourcePath,\n ref: params.ref,\n rangeId: range.id,\n };\n }\n return {\n available: true,\n path: file.path,\n sourcePath: owner.sourcePath,\n ref: params.ref,\n rangeId: range.id,\n declaration: owner.declaration,\n };\n}\n\nexport async function runAstGrepSearch(options: {\n cwd: string;\n params: AstGrepSearchParams;\n maxBytes: number;\n env?: NodeJS.ProcessEnv;\n timeoutMs?: number;\n}): Promise<unknown> {\n for (const searchPath of options.params.paths) {\n if (searchPath !== \".\") {\n resolveAllowedPath(options.cwd, searchPath);\n await assertNoSymlinkPath(options.cwd, searchPath);\n }\n }\n const result = await runAstGrepProcess(\n [\n \"ast-grep\",\n \"run\",\n \"--pattern\",\n options.params.pattern,\n \"--lang\",\n options.params.language,\n \"--json=compact\",\n \"--color\",\n \"never\",\n \"--\",\n ...options.params.paths,\n ],\n {\n cwd: options.cwd,\n env: options.env,\n timeoutMs: options.timeoutMs ?? 10_000,\n },\n );\n const output = result.stdout.trim();\n if (result.exitCode === 1 && (output === \"\" || output === \"[]\")) {\n return assertSerializedToolResponseFits(\n { available: true, matches: [], truncated: false },\n options.maxBytes,\n \"pipr_ast_grep response limit is too small\",\n );\n }\n if (result.exitCode !== 0) {\n throw new Error(\"pipr_ast_grep failed\");\n }\n let json: unknown;\n try {\n json = JSON.parse(output);\n } catch {\n throw new Error(\"pipr_ast_grep returned invalid output\");\n }\n const parsed = astGrepMatchesSchema.safeParse(json);\n if (!parsed.success) {\n throw new Error(\"pipr_ast_grep returned invalid output\");\n }\n const matches: Array<{\n path: string;\n startLine: number;\n endLine: number;\n text: string;\n }> = [];\n let truncated = parsed.data.length > 100;\n for (const match of parsed.data.slice(0, 100)) {\n const normalized = {\n path: parseSearchResultPath(match.file),\n startLine: match.range.start.line + 1,\n endLine: match.range.end.line + 1,\n text: truncateUtf8(match.text, 2 * 1024),\n };\n const candidate = { available: true, matches: [...matches, normalized], truncated };\n if (serializedToolResponseBytes(candidate) > options.maxBytes) {\n truncated = true;\n break;\n }\n matches.push(normalized);\n }\n return assertSerializedToolResponseFits(\n { available: true, matches, truncated },\n options.maxBytes,\n \"pipr_ast_grep response limit is too small\",\n );\n}\n\nfunction serializedToolResponseBytes(value: unknown): number {\n return Buffer.byteLength(JSON.stringify(value), \"utf8\");\n}\n\nexport function assertSerializedToolResponseFits<T>(\n value: T,\n maxBytes: number,\n errorMessage: string,\n): T {\n if (serializedToolResponseBytes(value) > maxBytes) {\n throw new Error(errorMessage);\n }\n return value;\n}\n\nexport function boundToolResponseContent<T extends { content: string }>(\n value: T,\n maxBytes: number,\n errorMessage: string,\n): T & { truncated: boolean } {\n if (serializedToolResponseBytes(value) <= maxBytes) {\n return value as T & { truncated: boolean };\n }\n const empty = { ...value, content: \"\", truncated: true };\n assertSerializedToolResponseFits(empty, maxBytes, errorMessage);\n const originalBuffer = Buffer.from(value.content, \"utf8\");\n let low = 0;\n let high = originalBuffer.byteLength;\n let best = empty;\n while (low <= high) {\n const middle = Math.floor((low + high) / 2);\n const candidate = {\n ...value,\n content: originalBuffer.subarray(0, middle).toString(\"utf8\"),\n truncated: true,\n };\n if (serializedToolResponseBytes(candidate) <= maxBytes) {\n best = candidate;\n low = middle + 1;\n } else {\n high = middle - 1;\n }\n }\n return best;\n}\n\nfunction filterManifestFileRanges(\n file: DiffManifestFile,\n rangeId: string | undefined,\n): DiffManifestFile {\n if (rangeId === undefined) {\n return file;\n }\n return {\n ...file,\n commentableRanges: file.commentableRanges.filter((range) => range.id === rangeId),\n };\n}\n\nfunction lineWindowForRange(\n range: CommentableRange,\n hunk: DiffHunk,\n ref: \"base\" | \"head\",\n): LineWindow | undefined {\n const targetSide: CommentableRange[\"side\"] = ref === \"base\" ? \"LEFT\" : \"RIGHT\";\n if (range.side !== targetSide) {\n return undefined;\n }\n const hunkStart = ref === \"base\" ? hunk.oldStart : hunk.newStart;\n const hunkLines = ref === \"base\" ? hunk.oldLines : hunk.newLines;\n if (hunkLines === 0) {\n return undefined;\n }\n const hunkEnd = hunkStart + hunkLines - 1;\n return {\n startLine: Math.max(hunkStart, range.startLine - readAtRefContextLines),\n endLine: Math.min(hunkEnd, range.endLine + readAtRefContextLines),\n };\n}\n\nfunction parseSearchPath(value: string): string {\n if (value === \".\") {\n return value;\n }\n if (\n value.includes(\"\\\\\") ||\n /[*?[\\]{}]/.test(value) ||\n path.isAbsolute(value) ||\n value.includes(\"\\0\") ||\n value.split(\"/\").some((part) => part === \"\" || part === \".\" || part === \"..\" || part === \".git\")\n ) {\n throw new Error(`Unsafe structural search path '${value}'`);\n }\n return value;\n}\n\nfunction parseSearchResultPath(value: string): string {\n try {\n return parseSearchPath(value);\n } catch {\n throw new Error(\"pipr_ast_grep returned an unsafe path\");\n }\n}\n\nfunction truncateUtf8(value: string, maxBytes: number): string {\n const buffer = Buffer.from(value, \"utf8\");\n return buffer.byteLength <= maxBytes ? value : buffer.subarray(0, maxBytes).toString(\"utf8\");\n}\n\nasync function runAstGrepProcess(\n command: [string, ...string[]],\n options: {\n cwd: string;\n env?: NodeJS.ProcessEnv;\n timeoutMs: number;\n },\n): Promise<{ stdout: string; exitCode: number }> {\n return await new Promise((resolve, reject) => {\n const child = spawn(command[0], command.slice(1), {\n cwd: options.cwd,\n env: options.env ?? process.env,\n shell: false,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n const stdout: Buffer[] = [];\n let stdoutBytes = 0;\n let stderrBytes = 0;\n let settled = false;\n const fail = (message: string) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(timer);\n child.kill(\"SIGKILL\");\n reject(new Error(message));\n };\n const timer = setTimeout(() => fail(\"pipr_ast_grep timed out\"), options.timeoutMs);\n child.on(\"error\", () => fail(\"pipr_ast_grep is unavailable\"));\n child.stdout.on(\"data\", (chunk: Buffer) => {\n stdoutBytes += chunk.byteLength;\n if (stdoutBytes > 16 * 1024 * 1024) {\n fail(\"pipr_ast_grep exceeded its output limit\");\n return;\n }\n stdout.push(chunk);\n });\n child.stderr.on(\"data\", (chunk: Buffer) => {\n stderrBytes += chunk.byteLength;\n if (stderrBytes > 1024 * 1024) {\n fail(\"pipr_ast_grep exceeded its output limit\");\n }\n });\n child.on(\"close\", (exitCode) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(timer);\n resolve({\n stdout: Buffer.concat(stdout).toString(\"utf8\"),\n exitCode: exitCode ?? -1,\n });\n });\n });\n}\n"],"mappings":"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"}
|