@oh-my-pi/pi-coding-agent 15.7.2 → 15.7.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.
- package/CHANGELOG.md +114 -6
- package/dist/types/cli/args.d.ts +1 -1
- package/dist/types/cli/extension-flags.d.ts +36 -0
- package/dist/types/config/config-file.d.ts +4 -0
- package/dist/types/config/file-lock.d.ts +23 -0
- package/dist/types/config/keybindings.d.ts +2 -1
- package/dist/types/config/model-registry.d.ts +6 -0
- package/dist/types/config/settings-schema.d.ts +69 -65
- package/dist/types/edit/hashline/diff.d.ts +3 -3
- package/dist/types/eval/__tests__/agent-bridge.test.d.ts +1 -0
- package/dist/types/eval/__tests__/budget-bridge.test.d.ts +1 -0
- package/dist/types/eval/__tests__/idle-timeout.test.d.ts +1 -0
- package/dist/types/eval/agent-bridge.d.ts +25 -0
- package/dist/types/eval/backend.d.ts +17 -2
- package/dist/types/eval/budget-bridge.d.ts +29 -0
- package/dist/types/eval/idle-timeout.d.ts +28 -0
- package/dist/types/eval/js/executor.d.ts +8 -0
- package/dist/types/eval/js/tool-bridge.d.ts +2 -1
- package/dist/types/eval/py/executor.d.ts +13 -0
- package/dist/types/exec/bash-executor.d.ts +1 -0
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -2
- package/dist/types/extensibility/extensions/runner.d.ts +7 -0
- package/dist/types/extensibility/plugins/git-url.d.ts +11 -1
- package/dist/types/extensibility/plugins/manager.d.ts +12 -1
- package/dist/types/extensibility/shared-events.d.ts +2 -2
- package/dist/types/internal-urls/local-protocol.d.ts +19 -9
- package/dist/types/internal-urls/types.d.ts +14 -0
- package/dist/types/lsp/client.d.ts +3 -0
- package/dist/types/mcp/manager.d.ts +14 -5
- package/dist/types/memory-backend/index.d.ts +1 -1
- package/dist/types/memory-backend/resolve.d.ts +1 -1
- package/dist/types/memory-backend/types.d.ts +3 -3
- package/dist/types/mnemopi/backend.d.ts +4 -0
- package/dist/types/mnemopi/config.d.ts +29 -0
- package/dist/types/mnemopi/state.d.ts +72 -0
- package/dist/types/modes/components/custom-editor.d.ts +2 -2
- package/dist/types/modes/components/omfg-panel.d.ts +19 -0
- package/dist/types/modes/controllers/command-controller.d.ts +6 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -3
- package/dist/types/modes/controllers/omfg-controller.d.ts +10 -0
- package/dist/types/modes/controllers/omfg-rule.d.ts +26 -0
- package/dist/types/modes/gradient-highlight.d.ts +5 -1
- package/dist/types/modes/interactive-mode.d.ts +7 -3
- package/dist/types/modes/magic-keywords.d.ts +14 -0
- package/dist/types/modes/markdown-prose.d.ts +27 -0
- package/dist/types/modes/orchestrate.d.ts +7 -2
- package/dist/types/modes/shared.d.ts +1 -1
- package/dist/types/modes/turn-budget.d.ts +18 -0
- package/dist/types/modes/types.d.ts +7 -3
- package/dist/types/modes/ultrathink.d.ts +7 -2
- package/dist/types/modes/workflow.d.ts +15 -0
- package/dist/types/sdk.d.ts +13 -3
- package/dist/types/session/agent-session.d.ts +36 -17
- package/dist/types/session/session-manager.d.ts +18 -0
- package/dist/types/session/session-storage.d.ts +6 -0
- package/dist/types/session/shake-types.d.ts +24 -0
- package/dist/types/task/executor.d.ts +2 -2
- package/dist/types/task/repair-args.d.ts +52 -0
- package/dist/types/tiny/models.d.ts +1 -1
- package/dist/types/tiny/title-client.d.ts +28 -2
- package/dist/types/tiny/title-protocol.d.ts +8 -5
- package/dist/types/tools/find.d.ts +1 -1
- package/dist/types/tools/index.d.ts +19 -3
- package/dist/types/tools/memory-edit.d.ts +1 -1
- package/dist/types/tools/path-utils.d.ts +7 -0
- package/dist/types/tui/output-block.d.ts +7 -7
- package/examples/sdk/09-api-keys-and-oauth.ts +2 -2
- package/package.json +10 -10
- package/scripts/build-binary.ts +0 -1
- package/src/autoresearch/tools/run-experiment.ts +45 -113
- package/src/cli/args.ts +39 -16
- package/src/cli/extension-flags.ts +48 -0
- package/src/cli/plugin-cli.ts +11 -2
- package/src/cli.ts +59 -0
- package/src/config/config-file.ts +98 -13
- package/src/config/file-lock.ts +60 -17
- package/src/config/keybindings.ts +78 -27
- package/src/config/model-registry.ts +7 -1
- package/src/config/settings-schema.ts +73 -67
- package/src/config/settings.ts +22 -0
- package/src/edit/hashline/diff.ts +81 -24
- package/src/edit/renderer.ts +16 -12
- package/src/eval/__tests__/agent-bridge.test.ts +433 -0
- package/src/eval/__tests__/budget-bridge.test.ts +69 -0
- package/src/eval/__tests__/idle-timeout.test.ts +66 -0
- package/src/eval/__tests__/shared-executors.test.ts +21 -0
- package/src/eval/agent-bridge.ts +295 -0
- package/src/eval/backend.ts +17 -2
- package/src/eval/budget-bridge.ts +48 -0
- package/src/eval/idle-timeout.ts +80 -0
- package/src/eval/js/executor.ts +35 -7
- package/src/eval/js/index.ts +2 -1
- package/src/eval/js/shared/prelude.txt +85 -1
- package/src/eval/js/tool-bridge.ts +9 -0
- package/src/eval/py/executor.ts +41 -14
- package/src/eval/py/index.ts +2 -1
- package/src/eval/py/prelude.py +132 -1
- package/src/exec/bash-executor.ts +2 -3
- package/src/extensibility/custom-tools/types.ts +2 -2
- package/src/extensibility/extensions/runner.ts +12 -2
- package/src/extensibility/plugins/git-url.ts +90 -4
- package/src/extensibility/plugins/manager.ts +103 -7
- package/src/extensibility/shared-events.ts +2 -2
- package/src/internal-urls/docs-index.generated.ts +88 -88
- package/src/internal-urls/local-protocol.ts +23 -11
- package/src/internal-urls/types.ts +15 -0
- package/src/lsp/client.ts +28 -5
- package/src/main.ts +44 -55
- package/src/mcp/manager.ts +87 -4
- package/src/memory-backend/index.ts +1 -1
- package/src/memory-backend/resolve.ts +3 -3
- package/src/memory-backend/types.ts +3 -3
- package/src/{mnemosyne → mnemopi}/backend.ts +70 -70
- package/src/{mnemosyne → mnemopi}/config.ts +36 -36
- package/src/{mnemosyne → mnemopi}/state.ts +67 -67
- package/src/modes/components/agent-dashboard.ts +6 -6
- package/src/modes/components/custom-editor.ts +4 -11
- package/src/modes/components/extensions/state-manager.ts +3 -4
- package/src/modes/components/footer.ts +8 -9
- package/src/modes/components/hook-selector.ts +86 -20
- package/src/modes/components/oauth-selector.ts +93 -21
- package/src/modes/components/omfg-panel.ts +141 -0
- package/src/modes/components/settings-defs.ts +2 -2
- package/src/modes/components/tips.txt +2 -1
- package/src/modes/components/tool-execution.ts +38 -19
- package/src/modes/components/tree-selector.ts +4 -3
- package/src/modes/components/user-message-selector.ts +94 -19
- package/src/modes/components/user-message.ts +8 -1
- package/src/modes/controllers/command-controller.ts +25 -0
- package/src/modes/controllers/event-controller.ts +68 -3
- package/src/modes/controllers/input-controller.ts +14 -11
- package/src/modes/controllers/mcp-command-controller.ts +1 -1
- package/src/modes/controllers/omfg-controller.ts +283 -0
- package/src/modes/controllers/omfg-rule.ts +647 -0
- package/src/modes/controllers/selector-controller.ts +1 -0
- package/src/modes/gradient-highlight.ts +23 -6
- package/src/modes/interactive-mode.ts +41 -7
- package/src/modes/magic-keywords.ts +20 -0
- package/src/modes/markdown-prose.ts +247 -0
- package/src/modes/orchestrate.ts +17 -11
- package/src/modes/shared.ts +3 -11
- package/src/modes/turn-budget.ts +31 -0
- package/src/modes/types.ts +7 -1
- package/src/modes/ultrathink.ts +16 -10
- package/src/modes/utils/hotkeys-markdown.ts +1 -1
- package/src/modes/workflow.ts +42 -0
- package/src/prompts/system/omfg-user.md +51 -0
- package/src/prompts/system/project-prompt.md +3 -2
- package/src/prompts/system/subagent-system-prompt.md +12 -8
- package/src/prompts/system/system-prompt.md +9 -6
- package/src/prompts/system/workflow-notice.md +70 -0
- package/src/prompts/tools/eval.md +13 -1
- package/src/prompts/tools/memory-edit.md +1 -1
- package/src/sdk.ts +63 -33
- package/src/session/agent-session.ts +290 -55
- package/src/session/session-manager.ts +32 -0
- package/src/session/session-storage.ts +68 -8
- package/src/session/shake-types.ts +43 -0
- package/src/slash-commands/builtin-registry.ts +39 -16
- package/src/task/executor.ts +17 -7
- package/src/task/index.ts +9 -8
- package/src/task/repair-args.ts +117 -0
- package/src/tiny/models.ts +2 -2
- package/src/tiny/title-client.ts +133 -43
- package/src/tiny/title-protocol.ts +10 -5
- package/src/tiny/worker.ts +3 -46
- package/src/tools/ast-edit.ts +3 -0
- package/src/tools/ast-grep.ts +3 -0
- package/src/tools/eval.ts +202 -26
- package/src/tools/find.ts +20 -6
- package/src/tools/gh.ts +1 -0
- package/src/tools/grouped-file-output.ts +9 -2
- package/src/tools/index.ts +17 -5
- package/src/tools/memory-edit.ts +4 -4
- package/src/tools/memory-recall.ts +5 -5
- package/src/tools/memory-reflect.ts +5 -5
- package/src/tools/memory-retain.ts +4 -4
- package/src/tools/path-utils.ts +13 -2
- package/src/tools/read.ts +1 -0
- package/src/tools/render-utils.ts +2 -1
- package/src/tools/search.ts +491 -76
- package/src/tui/output-block.ts +37 -75
- package/src/utils/git.ts +9 -3
- package/dist/types/mnemosyne/backend.d.ts +0 -4
- package/dist/types/mnemosyne/config.d.ts +0 -29
- package/dist/types/mnemosyne/state.d.ts +0 -72
- /package/dist/types/{mnemosyne → mnemopi}/index.d.ts +0 -0
- /package/src/{mnemosyne → mnemopi}/index.ts +0 -0
package/src/tools/search.ts
CHANGED
|
@@ -10,9 +10,12 @@ import { prompt, untilAborted } from "@oh-my-pi/pi-utils";
|
|
|
10
10
|
import * as z from "zod/v4";
|
|
11
11
|
import { recordFileSnapshot } from "../edit/file-snapshot-store";
|
|
12
12
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
13
|
+
import type { LocalProtocolOptions } from "../internal-urls/local-protocol";
|
|
14
|
+
import { InternalUrlRouter } from "../internal-urls/router";
|
|
15
|
+
import type { InternalResource, ResolveContext } from "../internal-urls/types";
|
|
13
16
|
import type { Theme } from "../modes/theme/theme";
|
|
14
17
|
import searchDescription from "../prompts/tools/search.md" with { type: "text" };
|
|
15
|
-
import { DEFAULT_MAX_COLUMN, type TruncationResult, truncateHead } from "../session/streaming-output";
|
|
18
|
+
import { DEFAULT_MAX_COLUMN, type TruncationResult, truncateHead, truncateLine } from "../session/streaming-output";
|
|
16
19
|
import { Ellipsis, fileHyperlink, renderStatusLine, renderTreeList, truncateToWidth } from "../tui";
|
|
17
20
|
import { resolveFileDisplayMode } from "../utils/file-display-mode";
|
|
18
21
|
import type { ToolSession } from ".";
|
|
@@ -31,6 +34,7 @@ import {
|
|
|
31
34
|
isLineInRanges,
|
|
32
35
|
type LineRange,
|
|
33
36
|
parseLineRanges,
|
|
37
|
+
type ResolvedSearchTarget,
|
|
34
38
|
resolveReadPath,
|
|
35
39
|
resolveToolSearchScope,
|
|
36
40
|
splitPathAndSel,
|
|
@@ -257,6 +261,343 @@ async function resolveArchiveSearchPaths(
|
|
|
257
261
|
return { resolvedPaths, displayMap, displaySet, unreadable, cleanup };
|
|
258
262
|
}
|
|
259
263
|
|
|
264
|
+
interface VirtualSearchResource {
|
|
265
|
+
path: string;
|
|
266
|
+
content: string;
|
|
267
|
+
ranges?: readonly LineRange[];
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
interface InternalSearchInputResolution {
|
|
271
|
+
paths: string[];
|
|
272
|
+
resolvedPathsByInput: string[];
|
|
273
|
+
virtualResources: VirtualSearchResource[];
|
|
274
|
+
virtualPathSet: Set<string>;
|
|
275
|
+
virtualInputIndexes: Set<number>;
|
|
276
|
+
immutableSourcePaths: Set<string>;
|
|
277
|
+
virtualScopePath?: string;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
interface IndexedContentLines {
|
|
281
|
+
lines: string[];
|
|
282
|
+
starts: number[];
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const INTERNAL_URL_DISPLAY_RE = /^[a-z][a-z0-9+.-]*:\/\//i;
|
|
286
|
+
const OMP_ROOT_URL_RE = /^omp:\/\/\/?$/i;
|
|
287
|
+
|
|
288
|
+
function normalizeSearchLine(line: string): string {
|
|
289
|
+
return line.endsWith("\r") ? line.slice(0, -1) : line;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function splitSearchLines(content: string): string[] {
|
|
293
|
+
const lines = content.split("\n");
|
|
294
|
+
if (lines.length > 0 && lines[lines.length - 1] === "") {
|
|
295
|
+
lines.pop();
|
|
296
|
+
}
|
|
297
|
+
return lines.map(normalizeSearchLine);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function indexSearchLines(content: string): IndexedContentLines {
|
|
301
|
+
const rawLines = content.split("\n");
|
|
302
|
+
if (rawLines.length > 0 && rawLines[rawLines.length - 1] === "") {
|
|
303
|
+
rawLines.pop();
|
|
304
|
+
}
|
|
305
|
+
const lines: string[] = [];
|
|
306
|
+
const starts: number[] = [];
|
|
307
|
+
let offset = 0;
|
|
308
|
+
for (const rawLine of rawLines) {
|
|
309
|
+
starts.push(offset);
|
|
310
|
+
lines.push(normalizeSearchLine(rawLine));
|
|
311
|
+
offset += rawLine.length + 1;
|
|
312
|
+
}
|
|
313
|
+
return { lines, starts };
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function findLineIndex(starts: readonly number[], offset: number): number {
|
|
317
|
+
if (starts.length === 0) return -1;
|
|
318
|
+
let low = 0;
|
|
319
|
+
let high = starts.length - 1;
|
|
320
|
+
while (low <= high) {
|
|
321
|
+
const mid = Math.floor((low + high) / 2);
|
|
322
|
+
if (starts[mid] <= offset) {
|
|
323
|
+
low = mid + 1;
|
|
324
|
+
} else {
|
|
325
|
+
high = mid - 1;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return Math.max(0, high);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function lineAllowed(lineNumber: number, ranges: readonly LineRange[] | undefined): boolean {
|
|
332
|
+
return !ranges || isLineInRanges(lineNumber, ranges);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function makeContextLine(lines: readonly string[], lineIndex: number): { lineNumber: number; line: string } {
|
|
336
|
+
const { text } = truncateLine(lines[lineIndex] ?? "", DEFAULT_MAX_COLUMN);
|
|
337
|
+
return { lineNumber: lineIndex + 1, line: text };
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function makeVirtualMatch(
|
|
341
|
+
resource: VirtualSearchResource,
|
|
342
|
+
lines: readonly string[],
|
|
343
|
+
lineIndex: number,
|
|
344
|
+
contextBefore: number,
|
|
345
|
+
contextAfter: number,
|
|
346
|
+
): GrepMatch {
|
|
347
|
+
const lineNumber = lineIndex + 1;
|
|
348
|
+
const { text, wasTruncated } = truncateLine(lines[lineIndex] ?? "", DEFAULT_MAX_COLUMN);
|
|
349
|
+
const match: GrepMatch = {
|
|
350
|
+
path: resource.path,
|
|
351
|
+
lineNumber,
|
|
352
|
+
line: text,
|
|
353
|
+
};
|
|
354
|
+
if (wasTruncated) match.truncated = true;
|
|
355
|
+
|
|
356
|
+
if (contextBefore > 0) {
|
|
357
|
+
const before: NonNullable<GrepMatch["contextBefore"]> = [];
|
|
358
|
+
const start = Math.max(0, lineIndex - contextBefore);
|
|
359
|
+
for (let idx = start; idx < lineIndex; idx++) {
|
|
360
|
+
const contextLineNumber = idx + 1;
|
|
361
|
+
if (lineAllowed(contextLineNumber, resource.ranges)) {
|
|
362
|
+
before.push(makeContextLine(lines, idx));
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
if (before.length > 0) match.contextBefore = before;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (contextAfter > 0) {
|
|
369
|
+
const after: NonNullable<GrepMatch["contextAfter"]> = [];
|
|
370
|
+
const end = Math.min(lines.length - 1, lineIndex + contextAfter);
|
|
371
|
+
for (let idx = lineIndex + 1; idx <= end; idx++) {
|
|
372
|
+
const contextLineNumber = idx + 1;
|
|
373
|
+
if (lineAllowed(contextLineNumber, resource.ranges)) {
|
|
374
|
+
after.push(makeContextLine(lines, idx));
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
if (after.length > 0) match.contextAfter = after;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return match;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function compileVirtualRegex(pattern: string, ignoreCase: boolean, multiline: boolean): RegExp {
|
|
384
|
+
const flags = `${ignoreCase ? "i" : ""}${multiline ? "gm" : ""}`;
|
|
385
|
+
try {
|
|
386
|
+
return new RegExp(pattern, flags);
|
|
387
|
+
} catch (err) {
|
|
388
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
389
|
+
throw new ToolError(`Invalid regex: ${message.replace(/^Invalid regular expression:\s*/i, "")}`);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function searchVirtualResourceLines(
|
|
394
|
+
resource: VirtualSearchResource,
|
|
395
|
+
regex: RegExp,
|
|
396
|
+
contextBefore: number,
|
|
397
|
+
contextAfter: number,
|
|
398
|
+
maxCount: number,
|
|
399
|
+
): { matches: GrepMatch[]; totalMatches: number; limitReached: boolean } {
|
|
400
|
+
const lines = splitSearchLines(resource.content);
|
|
401
|
+
const matches: GrepMatch[] = [];
|
|
402
|
+
let totalMatches = 0;
|
|
403
|
+
let limitReached = false;
|
|
404
|
+
|
|
405
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
|
406
|
+
const lineNumber = lineIndex + 1;
|
|
407
|
+
if (!lineAllowed(lineNumber, resource.ranges)) continue;
|
|
408
|
+
regex.lastIndex = 0;
|
|
409
|
+
if (!regex.test(lines[lineIndex] ?? "")) continue;
|
|
410
|
+
totalMatches++;
|
|
411
|
+
if (matches.length >= maxCount) {
|
|
412
|
+
limitReached = true;
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
matches.push(makeVirtualMatch(resource, lines, lineIndex, contextBefore, contextAfter));
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return { matches, totalMatches, limitReached };
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function searchVirtualResourceMultiline(
|
|
422
|
+
resource: VirtualSearchResource,
|
|
423
|
+
regex: RegExp,
|
|
424
|
+
contextBefore: number,
|
|
425
|
+
contextAfter: number,
|
|
426
|
+
maxCount: number,
|
|
427
|
+
): { matches: GrepMatch[]; totalMatches: number; limitReached: boolean } {
|
|
428
|
+
const indexed = indexSearchLines(resource.content);
|
|
429
|
+
const matches: GrepMatch[] = [];
|
|
430
|
+
const matchedLines = new Set<number>();
|
|
431
|
+
let totalMatches = 0;
|
|
432
|
+
let limitReached = false;
|
|
433
|
+
|
|
434
|
+
while (true) {
|
|
435
|
+
const match = regex.exec(resource.content);
|
|
436
|
+
if (match === null) break;
|
|
437
|
+
const lineIndex = findLineIndex(indexed.starts, match.index);
|
|
438
|
+
if (lineIndex >= 0) {
|
|
439
|
+
const lineNumber = lineIndex + 1;
|
|
440
|
+
if (!matchedLines.has(lineNumber) && lineAllowed(lineNumber, resource.ranges)) {
|
|
441
|
+
matchedLines.add(lineNumber);
|
|
442
|
+
totalMatches++;
|
|
443
|
+
if (matches.length >= maxCount) {
|
|
444
|
+
limitReached = true;
|
|
445
|
+
} else {
|
|
446
|
+
matches.push(makeVirtualMatch(resource, indexed.lines, lineIndex, contextBefore, contextAfter));
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
if (match[0].length === 0) {
|
|
451
|
+
regex.lastIndex++;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
return { matches, totalMatches, limitReached };
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function searchVirtualResources(
|
|
459
|
+
resources: readonly VirtualSearchResource[],
|
|
460
|
+
pattern: string,
|
|
461
|
+
ignoreCase: boolean,
|
|
462
|
+
multiline: boolean,
|
|
463
|
+
contextBefore: number,
|
|
464
|
+
contextAfter: number,
|
|
465
|
+
maxCount: number,
|
|
466
|
+
): GrepResult {
|
|
467
|
+
if (resources.length === 0) {
|
|
468
|
+
return { matches: [], totalMatches: 0, filesWithMatches: 0, filesSearched: 0, limitReached: false };
|
|
469
|
+
}
|
|
470
|
+
const regex = compileVirtualRegex(pattern, ignoreCase, multiline);
|
|
471
|
+
const matches: GrepMatch[] = [];
|
|
472
|
+
const filesWithMatches = new Set<string>();
|
|
473
|
+
let totalMatches = 0;
|
|
474
|
+
let limitReached = false;
|
|
475
|
+
|
|
476
|
+
for (const resource of resources) {
|
|
477
|
+
const remaining = Math.max(maxCount - matches.length, 0);
|
|
478
|
+
const resourceResult = multiline
|
|
479
|
+
? searchVirtualResourceMultiline(resource, regex, contextBefore, contextAfter, remaining)
|
|
480
|
+
: searchVirtualResourceLines(resource, regex, contextBefore, contextAfter, remaining);
|
|
481
|
+
if (resourceResult.totalMatches > 0) {
|
|
482
|
+
filesWithMatches.add(resource.path);
|
|
483
|
+
}
|
|
484
|
+
totalMatches += resourceResult.totalMatches;
|
|
485
|
+
limitReached = limitReached || resourceResult.limitReached;
|
|
486
|
+
matches.push(...resourceResult.matches);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return {
|
|
490
|
+
matches,
|
|
491
|
+
totalMatches,
|
|
492
|
+
filesWithMatches: filesWithMatches.size,
|
|
493
|
+
filesSearched: resources.length,
|
|
494
|
+
limitReached,
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function mergeGrepResults(left: GrepResult, right: GrepResult, maxCount: number): GrepResult {
|
|
499
|
+
if (left.matches.length === 0) return right;
|
|
500
|
+
if (right.matches.length === 0) return left;
|
|
501
|
+
const combinedMatches = [...left.matches, ...right.matches];
|
|
502
|
+
const matches = combinedMatches.length > maxCount ? combinedMatches.slice(0, maxCount) : combinedMatches;
|
|
503
|
+
return {
|
|
504
|
+
matches,
|
|
505
|
+
totalMatches: left.totalMatches + right.totalMatches,
|
|
506
|
+
filesWithMatches: new Set(matches.map(match => match.path)).size,
|
|
507
|
+
filesSearched: left.filesSearched + right.filesSearched,
|
|
508
|
+
limitReached: left.limitReached || right.limitReached || matches.length < combinedMatches.length,
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
async function expandVirtualInternalResource(
|
|
513
|
+
rawPath: string,
|
|
514
|
+
resource: InternalResource,
|
|
515
|
+
internalRouter: InternalUrlRouter,
|
|
516
|
+
context: ResolveContext,
|
|
517
|
+
ranges: readonly LineRange[] | undefined,
|
|
518
|
+
): Promise<VirtualSearchResource[]> {
|
|
519
|
+
if (OMP_ROOT_URL_RE.test(rawPath)) {
|
|
520
|
+
const completions = await internalRouter.complete("omp", "");
|
|
521
|
+
if (completions && completions.length > 0) {
|
|
522
|
+
const resources: VirtualSearchResource[] = [];
|
|
523
|
+
const seen = new Set<string>();
|
|
524
|
+
for (const completion of completions) {
|
|
525
|
+
if (seen.has(completion.value)) continue;
|
|
526
|
+
seen.add(completion.value);
|
|
527
|
+
const docUrl = `omp://${completion.value}`;
|
|
528
|
+
const doc = await internalRouter.resolve(docUrl, context);
|
|
529
|
+
if (!doc.sourcePath) {
|
|
530
|
+
resources.push({ path: docUrl, content: doc.content, ranges });
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
if (resources.length > 0) return resources;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
return [{ path: rawPath, content: resource.content, ranges }];
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
async function resolveInternalSearchInputs(opts: {
|
|
541
|
+
pathSpecs: readonly SearchPathSpec[];
|
|
542
|
+
resolvedPaths: string[];
|
|
543
|
+
cwd: string;
|
|
544
|
+
settings: unknown;
|
|
545
|
+
signal?: AbortSignal;
|
|
546
|
+
archiveDisplayMap: ReadonlyMap<string, string>;
|
|
547
|
+
localProtocolOptions?: LocalProtocolOptions;
|
|
548
|
+
}): Promise<InternalSearchInputResolution> {
|
|
549
|
+
const internalRouter = InternalUrlRouter.instance();
|
|
550
|
+
const paths = opts.resolvedPaths.slice();
|
|
551
|
+
const virtualResources: VirtualSearchResource[] = [];
|
|
552
|
+
const virtualPathSet = new Set<string>();
|
|
553
|
+
const virtualInputIndexes = new Set<number>();
|
|
554
|
+
const immutableSourcePaths = new Set<string>();
|
|
555
|
+
let virtualScopePath: string | undefined;
|
|
556
|
+
const context: ResolveContext = {
|
|
557
|
+
cwd: opts.cwd,
|
|
558
|
+
settings: opts.settings,
|
|
559
|
+
signal: opts.signal,
|
|
560
|
+
localProtocolOptions: opts.localProtocolOptions,
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
for (let idx = 0; idx < paths.length; idx++) {
|
|
564
|
+
const rawPath = paths[idx];
|
|
565
|
+
if (!rawPath || opts.archiveDisplayMap.has(rawPath) || !internalRouter.canHandle(rawPath)) {
|
|
566
|
+
continue;
|
|
567
|
+
}
|
|
568
|
+
if (hasGlobPathChars(rawPath)) {
|
|
569
|
+
throw new ToolError(`Glob patterns are not supported for internal URLs: ${rawPath}`);
|
|
570
|
+
}
|
|
571
|
+
const resource = await internalRouter.resolve(rawPath, context);
|
|
572
|
+
if (resource.sourcePath) {
|
|
573
|
+
paths[idx] = resource.sourcePath;
|
|
574
|
+
if (resource.immutable) {
|
|
575
|
+
immutableSourcePaths.add(path.resolve(resource.sourcePath));
|
|
576
|
+
}
|
|
577
|
+
continue;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
const ranges = opts.pathSpecs[idx]?.ranges;
|
|
581
|
+
const expanded = await expandVirtualInternalResource(rawPath, resource, internalRouter, context, ranges);
|
|
582
|
+
virtualInputIndexes.add(idx);
|
|
583
|
+
for (const virtual of expanded) {
|
|
584
|
+
virtualResources.push(virtual);
|
|
585
|
+
virtualPathSet.add(virtual.path);
|
|
586
|
+
}
|
|
587
|
+
virtualScopePath = virtualScopePath ? `${virtualScopePath}, ${rawPath}` : rawPath;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
return {
|
|
591
|
+
resolvedPathsByInput: paths,
|
|
592
|
+
paths: paths.filter((_, idx) => !virtualInputIndexes.has(idx)),
|
|
593
|
+
virtualResources,
|
|
594
|
+
virtualPathSet,
|
|
595
|
+
virtualInputIndexes,
|
|
596
|
+
immutableSourcePaths,
|
|
597
|
+
virtualScopePath,
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
|
|
260
601
|
export interface SearchToolDetails {
|
|
261
602
|
truncation?: TruncationResult;
|
|
262
603
|
fileLimitReached?: number;
|
|
@@ -333,6 +674,17 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
333
674
|
cleanup: cleanupArchiveScratch,
|
|
334
675
|
} = await resolveArchiveSearchPaths(paths, this.session.cwd);
|
|
335
676
|
try {
|
|
677
|
+
const internalResolution = await resolveInternalSearchInputs({
|
|
678
|
+
pathSpecs,
|
|
679
|
+
resolvedPaths,
|
|
680
|
+
cwd: this.session.cwd,
|
|
681
|
+
settings: this.session.settings,
|
|
682
|
+
signal,
|
|
683
|
+
archiveDisplayMap,
|
|
684
|
+
localProtocolOptions: this.session.localProtocolOptions,
|
|
685
|
+
});
|
|
686
|
+
const searchablePaths = internalResolution.paths;
|
|
687
|
+
const { virtualResources, virtualPathSet, virtualInputIndexes } = internalResolution;
|
|
336
688
|
// Build the per-file line-range filter (keyed by absolute path) now that
|
|
337
689
|
// archive entries have been materialized to scratch files. Plain entries
|
|
338
690
|
// resolve through `resolveReadPath`; archive entries are keyed by the
|
|
@@ -341,10 +693,12 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
341
693
|
for (let idx = 0; idx < pathSpecs.length; idx++) {
|
|
342
694
|
const spec = pathSpecs[idx];
|
|
343
695
|
if (!spec.ranges) continue;
|
|
344
|
-
|
|
696
|
+
if (virtualInputIndexes.has(idx)) continue;
|
|
697
|
+
const resolved = internalResolution.resolvedPathsByInput[idx];
|
|
698
|
+
if (!resolved) continue;
|
|
345
699
|
if (resolved === spec.clean && !archiveDisplayMap.has(resolved)) {
|
|
346
700
|
// Non-archive entry; ensure the cleaned path resolves to a regular file.
|
|
347
|
-
const absKey = path.resolve(resolveReadPath(
|
|
701
|
+
const absKey = path.resolve(resolveReadPath(resolved, this.session.cwd));
|
|
348
702
|
const stats = await stat(absKey).catch(() => null);
|
|
349
703
|
if (!stats) {
|
|
350
704
|
throw new ToolError(`Path not found for line-range selector: ${spec.original}`);
|
|
@@ -360,7 +714,7 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
360
714
|
}
|
|
361
715
|
}
|
|
362
716
|
|
|
363
|
-
if (archiveUnreadable.length > 0 &&
|
|
717
|
+
if (archiveUnreadable.length > 0 && searchablePaths.length === archiveUnreadable.length) {
|
|
364
718
|
// All inputs were archive selectors we couldn't materialize; surface the
|
|
365
719
|
// reason instead of a downstream "path not found" from the scope resolver.
|
|
366
720
|
throw new ToolError(
|
|
@@ -376,22 +730,58 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
376
730
|
const patternHasNewline = normalizedPattern.includes("\n") || normalizedPattern.includes("\\n");
|
|
377
731
|
const effectiveMultiline = patternHasNewline;
|
|
378
732
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
:
|
|
394
|
-
|
|
733
|
+
let searchPath: string;
|
|
734
|
+
let scopePath: string;
|
|
735
|
+
let globFilter: string | undefined;
|
|
736
|
+
let isDirectory: boolean;
|
|
737
|
+
let multiTargets: ResolvedSearchTarget[] | undefined;
|
|
738
|
+
let exactFilePaths: string[] | undefined;
|
|
739
|
+
let missingPaths: string[];
|
|
740
|
+
const immutableSourcePaths = new Set(internalResolution.immutableSourcePaths);
|
|
741
|
+
if (searchablePaths.length > 0) {
|
|
742
|
+
const scope = await resolveToolSearchScope({
|
|
743
|
+
rawPaths: searchablePaths,
|
|
744
|
+
cwd: this.session.cwd,
|
|
745
|
+
internalUrlAction: "search",
|
|
746
|
+
trackImmutableSources: true,
|
|
747
|
+
surfaceExactFilePaths: true,
|
|
748
|
+
multipathStatHint: " (`paths` entries must each exist relative to cwd)",
|
|
749
|
+
settings: this.session.settings,
|
|
750
|
+
signal,
|
|
751
|
+
localProtocolOptions: this.session.localProtocolOptions,
|
|
752
|
+
});
|
|
753
|
+
searchPath = scope.searchPath;
|
|
754
|
+
isDirectory = scope.isDirectory;
|
|
755
|
+
multiTargets = scope.multiTargets;
|
|
756
|
+
exactFilePaths = scope.exactFilePaths;
|
|
757
|
+
missingPaths = scope.missingPaths;
|
|
758
|
+
globFilter = scope.globFilter;
|
|
759
|
+
for (const immutablePath of scope.immutableSourcePaths) {
|
|
760
|
+
immutableSourcePaths.add(immutablePath);
|
|
761
|
+
}
|
|
762
|
+
// When the only input was an archive selector, surface that selector instead
|
|
763
|
+
// of the temp scratch path the resolver substituted in.
|
|
764
|
+
const physicalScopePath =
|
|
765
|
+
searchablePaths.length === 1 && archiveDisplayMap.get(searchPath)
|
|
766
|
+
? (archiveDisplayMap.get(searchPath) as string)
|
|
767
|
+
: scope.scopePath;
|
|
768
|
+
scopePath = internalResolution.virtualScopePath
|
|
769
|
+
? `${physicalScopePath}, ${internalResolution.virtualScopePath}`
|
|
770
|
+
: physicalScopePath;
|
|
771
|
+
} else {
|
|
772
|
+
searchPath = this.session.cwd;
|
|
773
|
+
scopePath = internalResolution.virtualScopePath ?? ".";
|
|
774
|
+
globFilter = undefined;
|
|
775
|
+
isDirectory = false;
|
|
776
|
+
multiTargets = undefined;
|
|
777
|
+
exactFilePaths = undefined;
|
|
778
|
+
missingPaths = [];
|
|
779
|
+
}
|
|
780
|
+
if (
|
|
781
|
+
missingPaths.length > 0 &&
|
|
782
|
+
missingPaths.length === searchablePaths.length &&
|
|
783
|
+
virtualResources.length === 0
|
|
784
|
+
) {
|
|
395
785
|
const archiveHint =
|
|
396
786
|
archiveUnreadable.length > 0
|
|
397
787
|
? ` (archive members were not searchable: ${archiveUnreadable.join(", ")})`
|
|
@@ -400,34 +790,78 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
400
790
|
`Path not found: ${missingPaths.join(", ")}; pass each path as its own array element${archiveHint}`,
|
|
401
791
|
);
|
|
402
792
|
}
|
|
403
|
-
const { globFilter } = scope;
|
|
404
793
|
const baseDisplayMode = resolveFileDisplayMode(this.session);
|
|
405
794
|
|
|
406
795
|
const effectiveOutputMode = GrepOutputMode.Content;
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
796
|
+
const isMultiScope =
|
|
797
|
+
isDirectory ||
|
|
798
|
+
Boolean(exactFilePaths) ||
|
|
799
|
+
Boolean(multiTargets) ||
|
|
800
|
+
(virtualResources.length > 0 && (virtualResources.length > 1 || searchablePaths.length > 0));
|
|
412
801
|
const perFileMatchCap = isMultiScope ? MULTI_FILE_PER_FILE_MATCHES : SINGLE_FILE_MATCHES;
|
|
413
802
|
|
|
414
803
|
// Run grep
|
|
415
|
-
let result: GrepResult
|
|
804
|
+
let result: GrepResult = {
|
|
805
|
+
matches: [],
|
|
806
|
+
totalMatches: 0,
|
|
807
|
+
filesWithMatches: 0,
|
|
808
|
+
filesSearched: 0,
|
|
809
|
+
limitReached: false,
|
|
810
|
+
};
|
|
416
811
|
try {
|
|
417
|
-
if (
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
812
|
+
if (searchablePaths.length > 0) {
|
|
813
|
+
if (exactFilePaths || multiTargets) {
|
|
814
|
+
const matches: GrepMatch[] = [];
|
|
815
|
+
let limitReached = false;
|
|
816
|
+
let totalMatches = 0;
|
|
817
|
+
let filesSearched = 0;
|
|
818
|
+
const targets = exactFilePaths
|
|
819
|
+
? exactFilePaths.map(filePath => ({
|
|
820
|
+
basePath: filePath,
|
|
821
|
+
glob: undefined as string | undefined,
|
|
822
|
+
}))
|
|
823
|
+
: (multiTargets ?? []);
|
|
824
|
+
for (const target of targets) {
|
|
825
|
+
const targetResult = await grep(
|
|
826
|
+
{
|
|
827
|
+
pattern: normalizedPattern,
|
|
828
|
+
path: target.basePath,
|
|
829
|
+
glob: target.glob,
|
|
830
|
+
ignoreCase,
|
|
831
|
+
multiline: effectiveMultiline,
|
|
832
|
+
hidden: true,
|
|
833
|
+
gitignore: useGitignore,
|
|
834
|
+
cache: false,
|
|
835
|
+
maxCount: INTERNAL_TOTAL_CAP,
|
|
836
|
+
contextBefore: normalizedContextBefore,
|
|
837
|
+
contextAfter: normalizedContextAfter,
|
|
838
|
+
maxColumns: DEFAULT_MAX_COLUMN,
|
|
839
|
+
mode: effectiveOutputMode,
|
|
840
|
+
},
|
|
841
|
+
undefined,
|
|
842
|
+
);
|
|
843
|
+
limitReached = limitReached || Boolean(targetResult.limitReached);
|
|
844
|
+
totalMatches += targetResult.totalMatches;
|
|
845
|
+
filesSearched += targetResult.filesSearched;
|
|
846
|
+
for (const match of targetResult.matches) {
|
|
847
|
+
const absolute = path.resolve(target.basePath, match.path);
|
|
848
|
+
const rebased = path.relative(searchPath, absolute).replace(/\\/g, "/");
|
|
849
|
+
matches.push({ ...match, path: rebased });
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
result = {
|
|
853
|
+
matches,
|
|
854
|
+
totalMatches: exactFilePaths ? matches.length : totalMatches,
|
|
855
|
+
filesWithMatches: new Set(matches.map(match => match.path)).size,
|
|
856
|
+
filesSearched: exactFilePaths ? exactFilePaths.length : filesSearched,
|
|
857
|
+
limitReached,
|
|
858
|
+
};
|
|
859
|
+
} else {
|
|
860
|
+
result = await grep(
|
|
427
861
|
{
|
|
428
862
|
pattern: normalizedPattern,
|
|
429
|
-
path:
|
|
430
|
-
glob:
|
|
863
|
+
path: searchPath,
|
|
864
|
+
glob: globFilter,
|
|
431
865
|
ignoreCase,
|
|
432
866
|
multiline: effectiveMultiline,
|
|
433
867
|
hidden: true,
|
|
@@ -441,41 +875,7 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
441
875
|
},
|
|
442
876
|
undefined,
|
|
443
877
|
);
|
|
444
|
-
limitReached = limitReached || Boolean(targetResult.limitReached);
|
|
445
|
-
totalMatches += targetResult.totalMatches;
|
|
446
|
-
filesSearched += targetResult.filesSearched;
|
|
447
|
-
for (const match of targetResult.matches) {
|
|
448
|
-
const absolute = path.resolve(target.basePath, match.path);
|
|
449
|
-
const rebased = path.relative(searchPath, absolute).replace(/\\/g, "/");
|
|
450
|
-
matches.push({ ...match, path: rebased });
|
|
451
|
-
}
|
|
452
878
|
}
|
|
453
|
-
result = {
|
|
454
|
-
matches,
|
|
455
|
-
totalMatches: exactFilePaths ? matches.length : totalMatches,
|
|
456
|
-
filesWithMatches: new Set(matches.map(match => match.path)).size,
|
|
457
|
-
filesSearched: exactFilePaths ? exactFilePaths.length : filesSearched,
|
|
458
|
-
limitReached,
|
|
459
|
-
};
|
|
460
|
-
} else {
|
|
461
|
-
result = await grep(
|
|
462
|
-
{
|
|
463
|
-
pattern: normalizedPattern,
|
|
464
|
-
path: searchPath,
|
|
465
|
-
glob: globFilter,
|
|
466
|
-
ignoreCase,
|
|
467
|
-
multiline: effectiveMultiline,
|
|
468
|
-
hidden: true,
|
|
469
|
-
gitignore: useGitignore,
|
|
470
|
-
cache: false,
|
|
471
|
-
maxCount: INTERNAL_TOTAL_CAP,
|
|
472
|
-
contextBefore: normalizedContextBefore,
|
|
473
|
-
contextAfter: normalizedContextAfter,
|
|
474
|
-
maxColumns: DEFAULT_MAX_COLUMN,
|
|
475
|
-
mode: effectiveOutputMode,
|
|
476
|
-
},
|
|
477
|
-
undefined,
|
|
478
|
-
);
|
|
479
879
|
}
|
|
480
880
|
} catch (err) {
|
|
481
881
|
if (err instanceof Error && /^regex(?: parse)? error/i.test(err.message)) {
|
|
@@ -483,6 +883,16 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
483
883
|
}
|
|
484
884
|
throw err;
|
|
485
885
|
}
|
|
886
|
+
const virtualResult = searchVirtualResources(
|
|
887
|
+
virtualResources,
|
|
888
|
+
normalizedPattern,
|
|
889
|
+
ignoreCase,
|
|
890
|
+
effectiveMultiline,
|
|
891
|
+
normalizedContextBefore,
|
|
892
|
+
normalizedContextAfter,
|
|
893
|
+
INTERNAL_TOTAL_CAP,
|
|
894
|
+
);
|
|
895
|
+
result = mergeGrepResults(result, virtualResult, INTERNAL_TOTAL_CAP);
|
|
486
896
|
if (rangesByAbsPath.size > 0) {
|
|
487
897
|
const filteredMatches: GrepMatch[] = [];
|
|
488
898
|
for (const match of result.matches) {
|
|
@@ -521,7 +931,7 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
521
931
|
}
|
|
522
932
|
|
|
523
933
|
const formatPath = (filePath: string): string =>
|
|
524
|
-
archiveDisplaySet.has(filePath)
|
|
934
|
+
archiveDisplaySet.has(filePath) || virtualPathSet.has(filePath)
|
|
525
935
|
? filePath
|
|
526
936
|
: formatResultPath(filePath, isDirectory, searchPath, this.session.cwd);
|
|
527
937
|
|
|
@@ -612,7 +1022,7 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
612
1022
|
const hashContexts = new Map<string, { tag: string }>();
|
|
613
1023
|
if (baseDisplayMode.hashLines) {
|
|
614
1024
|
for (const relativePath of fileList) {
|
|
615
|
-
if (archiveDisplaySet.has(relativePath)) continue;
|
|
1025
|
+
if (archiveDisplaySet.has(relativePath) || virtualPathSet.has(relativePath)) continue;
|
|
616
1026
|
const absoluteFilePath = path.resolve(this.session.cwd, relativePath);
|
|
617
1027
|
if (immutableSourcePaths.has(absoluteFilePath)) continue;
|
|
618
1028
|
// Mint a whole-file content tag so any anchor validates while the
|
|
@@ -668,7 +1078,8 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
668
1078
|
}
|
|
669
1079
|
return { model: modelOut, display: displayOut };
|
|
670
1080
|
};
|
|
671
|
-
|
|
1081
|
+
const useGroupedOutput = isDirectory || isMultiScope;
|
|
1082
|
+
if (useGroupedOutput) {
|
|
672
1083
|
const grouped = formatGroupedFiles(fileList, relativePath => {
|
|
673
1084
|
const rendered = renderMatchesForFile(relativePath);
|
|
674
1085
|
const hashContext = hashContexts.get(relativePath);
|
|
@@ -902,6 +1313,10 @@ export const searchToolRenderer = {
|
|
|
902
1313
|
.slice(2)
|
|
903
1314
|
.trimEnd()
|
|
904
1315
|
.replace(/\s+\([^)]*\)\s*$/, "");
|
|
1316
|
+
if (INTERNAL_URL_DISPLAY_RE.test(raw)) {
|
|
1317
|
+
contextDir = "";
|
|
1318
|
+
return uiTheme.fg("accent", line);
|
|
1319
|
+
}
|
|
905
1320
|
const isDirectory = raw.endsWith("/");
|
|
906
1321
|
const name = isDirectory ? raw.replace(/\/$/, "") : raw.replace(/#[0-9a-f]+$/, "");
|
|
907
1322
|
if (isDirectory) {
|