@oh-my-pi/pi-coding-agent 15.9.67 → 15.10.1
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 +136 -0
- package/dist/types/cli/args.d.ts +1 -1
- package/dist/types/cli/dry-balance-cli.d.ts +15 -1
- package/dist/types/cli/gallery-cli.d.ts +43 -0
- package/dist/types/cli/gallery-fixtures/agentic.d.ts +2 -0
- package/dist/types/cli/gallery-fixtures/codeintel.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/edit.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/fs.d.ts +2 -0
- package/dist/types/cli/gallery-fixtures/index.d.ts +4 -0
- package/dist/types/cli/gallery-fixtures/interaction.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/memory.d.ts +2 -0
- package/dist/types/cli/gallery-fixtures/misc.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/search.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/shell.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/types.d.ts +44 -0
- package/dist/types/cli/gallery-fixtures/web.d.ts +2 -0
- package/dist/types/cli/gallery-screenshot.d.ts +35 -0
- package/dist/types/commands/gallery.d.ts +47 -0
- package/dist/types/commit/analysis/conventional.d.ts +2 -2
- package/dist/types/commit/analysis/summary.d.ts +2 -2
- package/dist/types/commit/changelog/generate.d.ts +2 -2
- package/dist/types/commit/changelog/index.d.ts +2 -2
- package/dist/types/commit/map-reduce/index.d.ts +3 -3
- package/dist/types/commit/map-reduce/map-phase.d.ts +2 -2
- package/dist/types/commit/map-reduce/reduce-phase.d.ts +2 -2
- package/dist/types/commit/model-selection.d.ts +10 -4
- package/dist/types/config/api-key-resolver.d.ts +34 -0
- package/dist/types/config/keybindings.d.ts +6 -1
- package/dist/types/config/model-id-affixes.d.ts +2 -0
- package/dist/types/config/model-registry.d.ts +25 -2
- package/dist/types/config/settings-schema.d.ts +41 -6
- package/dist/types/dap/config.d.ts +14 -1
- package/dist/types/dap/types.d.ts +10 -0
- package/dist/types/extensibility/plugins/marketplace-auto-update.d.ts +8 -0
- package/dist/types/lsp/types.d.ts +10 -0
- package/dist/types/lsp/utils.d.ts +3 -2
- package/dist/types/main.d.ts +3 -2
- package/dist/types/memory-backend/index.d.ts +2 -1
- package/dist/types/memory-backend/resolve.d.ts +1 -1
- package/dist/types/memory-backend/types.d.ts +1 -1
- package/dist/types/modes/components/chat-block.d.ts +64 -0
- package/dist/types/modes/components/custom-editor.d.ts +5 -1
- package/dist/types/modes/components/overlay-box.d.ts +17 -0
- package/dist/types/modes/components/plan-review-overlay.d.ts +59 -0
- package/dist/types/modes/components/plan-toc.d.ts +41 -0
- package/dist/types/modes/components/read-tool-group.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +18 -0
- package/dist/types/modes/components/transcript-container.d.ts +11 -0
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/controllers/event-controller.d.ts +0 -1
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +0 -1
- package/dist/types/modes/controllers/input-controller.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
- package/dist/types/modes/controllers/streaming-reveal.d.ts +22 -0
- package/dist/types/modes/controllers/tan-command-controller.d.ts +6 -0
- package/dist/types/modes/index.d.ts +5 -4
- package/dist/types/modes/interactive-mode.d.ts +16 -6
- package/dist/types/modes/setup-version.d.ts +11 -0
- package/dist/types/modes/setup-wizard/index.d.ts +2 -1
- package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +2 -1
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +19 -6
- package/dist/types/modes/utils/copy-targets.d.ts +21 -1
- package/dist/types/plan-mode/approved-plan.d.ts +27 -8
- package/dist/types/plan-mode/plan-protection.d.ts +4 -4
- package/dist/types/sdk.d.ts +3 -1
- package/dist/types/session/agent-session.d.ts +21 -0
- package/dist/types/session/messages.d.ts +12 -0
- package/dist/types/session/session-manager.d.ts +3 -1
- package/dist/types/slash-commands/types.d.ts +4 -6
- package/dist/types/task/executor.d.ts +14 -0
- package/dist/types/task/index.d.ts +1 -0
- package/dist/types/task/render.d.ts +3 -2
- package/dist/types/telemetry-export.d.ts +1 -1
- package/dist/types/tools/archive-reader.d.ts +5 -0
- package/dist/types/tools/ast-edit.d.ts +3 -0
- package/dist/types/tools/ast-grep.d.ts +3 -0
- package/dist/types/tools/bash.d.ts +1 -0
- package/dist/types/tools/eval-render.d.ts +1 -8
- package/dist/types/tools/fetch.d.ts +15 -7
- package/dist/types/tools/find.d.ts +8 -4
- package/dist/types/tools/grouped-file-output.d.ts +95 -12
- package/dist/types/tools/memory-render.d.ts +4 -1
- package/dist/types/tools/plan-mode-guard.d.ts +8 -9
- package/dist/types/tools/render-utils.d.ts +13 -9
- package/dist/types/tools/renderers.d.ts +16 -2
- package/dist/types/tools/search.d.ts +5 -1
- package/dist/types/tools/sqlite-reader.d.ts +1 -0
- package/dist/types/tools/todo.d.ts +3 -2
- package/dist/types/tools/write.d.ts +5 -0
- package/dist/types/tui/output-block.d.ts +16 -4
- package/dist/types/tui/status-line.d.ts +3 -0
- package/dist/types/utils/enhanced-paste.d.ts +20 -0
- package/dist/types/web/scrapers/github.d.ts +22 -0
- package/dist/types/web/search/providers/kimi.d.ts +1 -1
- package/dist/types/web/search/providers/perplexity.d.ts +8 -1
- package/dist/types/web/search/types.d.ts +1 -1
- package/package.json +9 -9
- package/scripts/dev-launch +42 -0
- package/scripts/dev-launch-preload.ts +19 -0
- package/src/auto-thinking/classifier.ts +5 -1
- package/src/cli/args.ts +2 -2
- package/src/cli/dry-balance-cli.ts +52 -17
- package/src/cli/gallery-cli.ts +226 -0
- package/src/cli/gallery-fixtures/agentic.ts +292 -0
- package/src/cli/gallery-fixtures/codeintel.ts +188 -0
- package/src/cli/gallery-fixtures/edit.ts +194 -0
- package/src/cli/gallery-fixtures/fs.ts +153 -0
- package/src/cli/gallery-fixtures/index.ts +40 -0
- package/src/cli/gallery-fixtures/interaction.ts +49 -0
- package/src/cli/gallery-fixtures/memory.ts +81 -0
- package/src/cli/gallery-fixtures/misc.ts +250 -0
- package/src/cli/gallery-fixtures/search.ts +213 -0
- package/src/cli/gallery-fixtures/shell.ts +167 -0
- package/src/cli/gallery-fixtures/types.ts +41 -0
- package/src/cli/gallery-fixtures/web.ts +158 -0
- package/src/cli/gallery-screenshot.ts +279 -0
- package/src/cli-commands.ts +1 -0
- package/src/commands/gallery.ts +52 -0
- package/src/commands/launch.ts +1 -1
- package/src/commit/analysis/conventional.ts +2 -2
- package/src/commit/analysis/summary.ts +2 -2
- package/src/commit/changelog/generate.ts +2 -2
- package/src/commit/changelog/index.ts +2 -2
- package/src/commit/map-reduce/index.ts +3 -3
- package/src/commit/map-reduce/map-phase.ts +2 -2
- package/src/commit/map-reduce/reduce-phase.ts +2 -2
- package/src/commit/model-selection.ts +33 -9
- package/src/commit/pipeline.ts +4 -4
- package/src/config/api-key-resolver.ts +58 -0
- package/src/config/keybindings.ts +15 -6
- package/src/config/model-equivalence.ts +35 -12
- package/src/config/model-id-affixes.ts +39 -22
- package/src/config/model-registry.ts +41 -18
- package/src/config/settings-schema.ts +28 -5
- package/src/config/settings.ts +31 -2
- package/src/dap/client.ts +14 -16
- package/src/dap/config.ts +41 -2
- package/src/dap/defaults.json +1 -0
- package/src/dap/session.ts +1 -0
- package/src/dap/types.ts +10 -0
- package/src/debug/index.ts +40 -54
- package/src/edit/renderer.ts +111 -119
- package/src/eval/__tests__/agent-bridge.test.ts +75 -32
- package/src/eval/__tests__/llm-bridge.test.ts +90 -31
- package/src/eval/agent-bridge.ts +34 -7
- package/src/eval/llm-bridge.ts +8 -3
- package/src/extensibility/extensions/runner.ts +1 -0
- package/src/extensibility/plugins/doctor.ts +0 -1
- package/src/extensibility/plugins/marketplace-auto-update.ts +49 -0
- package/src/goals/tools/goal-tool.ts +37 -27
- package/src/internal-urls/docs-index.generated.ts +10 -10
- package/src/lsp/client.ts +104 -55
- package/src/lsp/types.ts +10 -0
- package/src/lsp/utils.ts +3 -2
- package/src/main.ts +53 -56
- package/src/memories/index.ts +12 -5
- package/src/memory-backend/index.ts +13 -1
- package/src/memory-backend/resolve.ts +3 -5
- package/src/memory-backend/types.ts +1 -1
- package/src/mnemopi/backend.ts +5 -1
- package/src/modes/acp/acp-agent.ts +33 -26
- package/src/modes/components/assistant-message.ts +2 -9
- package/src/modes/components/chat-block.ts +111 -0
- package/src/modes/components/copy-selector.ts +1 -44
- package/src/modes/components/custom-editor.ts +33 -1
- package/src/modes/components/custom-message.ts +1 -3
- package/src/modes/components/execution-shared.ts +1 -2
- package/src/modes/components/hook-message.ts +1 -3
- package/src/modes/components/overlay-box.ts +108 -0
- package/src/modes/components/plan-review-overlay.ts +799 -0
- package/src/modes/components/plan-toc.ts +138 -0
- package/src/modes/components/read-tool-group.ts +20 -4
- package/src/modes/components/skill-message.ts +0 -1
- package/src/modes/components/status-line.ts +3 -5
- package/src/modes/components/tips.txt +1 -0
- package/src/modes/components/todo-reminder.ts +0 -2
- package/src/modes/components/tool-execution.ts +115 -90
- package/src/modes/components/transcript-container.ts +84 -24
- package/src/modes/components/user-message.ts +1 -2
- package/src/modes/controllers/command-controller-shared.ts +7 -6
- package/src/modes/controllers/command-controller.ts +70 -57
- package/src/modes/controllers/event-controller.ts +41 -40
- package/src/modes/controllers/extension-ui-controller.ts +10 -73
- package/src/modes/controllers/input-controller.ts +135 -122
- package/src/modes/controllers/mcp-command-controller.ts +69 -60
- package/src/modes/controllers/selector-controller.ts +25 -27
- package/src/modes/controllers/streaming-reveal.ts +212 -0
- package/src/modes/controllers/tan-command-controller.ts +173 -0
- package/src/modes/index.ts +5 -4
- package/src/modes/interactive-mode.ts +171 -82
- package/src/modes/setup-version.ts +11 -0
- package/src/modes/setup-wizard/index.ts +3 -2
- package/src/modes/setup-wizard/scenes/web-search.ts +3 -2
- package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
- package/src/modes/theme/theme-schema.json +1 -1
- package/src/modes/theme/theme.ts +8 -4
- package/src/modes/types.ts +19 -8
- package/src/modes/utils/context-usage.ts +10 -6
- package/src/modes/utils/copy-targets.ts +133 -27
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +44 -46
- package/src/plan-mode/approved-plan.ts +66 -43
- package/src/plan-mode/plan-protection.ts +4 -4
- package/src/prompts/system/background-tan-dispatch.md +8 -0
- package/src/prompts/system/plan-mode-active.md +67 -58
- package/src/prompts/system/plan-mode-approved.md +1 -1
- package/src/sdk.ts +32 -60
- package/src/session/agent-session.ts +89 -13
- package/src/session/messages.ts +26 -0
- package/src/session/session-manager.ts +13 -5
- package/src/slash-commands/builtin-registry.ts +37 -10
- package/src/slash-commands/helpers/usage-report.ts +2 -0
- package/src/slash-commands/types.ts +4 -6
- package/src/task/executor.ts +25 -4
- package/src/task/index.ts +4 -0
- package/src/task/render.ts +212 -148
- package/src/telemetry-export.ts +25 -7
- package/src/tools/archive-reader.ts +64 -0
- package/src/tools/ask.ts +119 -164
- package/src/tools/ast-edit.ts +98 -71
- package/src/tools/ast-grep.ts +37 -43
- package/src/tools/bash.ts +50 -6
- package/src/tools/debug.ts +20 -8
- package/src/tools/eval-backends.ts +6 -17
- package/src/tools/eval-render.ts +21 -18
- package/src/tools/eval.ts +5 -4
- package/src/tools/fetch.ts +391 -91
- package/src/tools/find.ts +44 -30
- package/src/tools/gh-renderer.ts +81 -42
- package/src/tools/grouped-file-output.ts +272 -48
- package/src/tools/image-gen.ts +150 -103
- package/src/tools/inspect-image-renderer.ts +63 -41
- package/src/tools/inspect-image.ts +8 -1
- package/src/tools/job.ts +3 -4
- package/src/tools/memory-render.ts +4 -1
- package/src/tools/plan-mode-guard.ts +21 -39
- package/src/tools/read.ts +23 -16
- package/src/tools/render-utils.ts +38 -40
- package/src/tools/renderers.ts +16 -1
- package/src/tools/report-tool-issue.ts +1 -1
- package/src/tools/resolve.ts +14 -0
- package/src/tools/search-tool-bm25.ts +36 -23
- package/src/tools/search.ts +189 -95
- package/src/tools/sqlite-reader.ts +9 -12
- package/src/tools/todo.ts +138 -59
- package/src/tools/write.ts +100 -60
- package/src/tui/output-block.ts +60 -13
- package/src/tui/status-line.ts +5 -1
- package/src/utils/commit-message-generator.ts +9 -1
- package/src/utils/enhanced-paste.ts +202 -0
- package/src/utils/title-generator.ts +2 -1
- package/src/web/scrapers/github.ts +255 -3
- package/src/web/scrapers/youtube.ts +3 -2
- package/src/web/search/providers/anthropic.ts +25 -19
- package/src/web/search/providers/exa.ts +11 -3
- package/src/web/search/providers/kimi.ts +28 -17
- package/src/web/search/providers/parallel.ts +35 -24
- package/src/web/search/providers/perplexity.ts +199 -51
- package/src/web/search/providers/synthetic.ts +8 -6
- package/src/web/search/providers/tavily.ts +9 -8
- package/src/web/search/providers/zai.ts +8 -6
- package/src/web/search/render.ts +39 -54
- package/src/web/search/types.ts +5 -1
- package/dist/types/eval/__tests__/shared-executors.test.d.ts +0 -1
- package/src/eval/__tests__/shared-executors.test.ts +0 -609
|
@@ -16,9 +16,9 @@ import {
|
|
|
16
16
|
searchDiscoverableTools,
|
|
17
17
|
summarizeDiscoverableTools,
|
|
18
18
|
} from "../tool-discovery/tool-index";
|
|
19
|
-
import {
|
|
19
|
+
import { framedBlock, renderStatusLine, truncateToWidth } from "../tui";
|
|
20
20
|
import type { ToolSession } from ".";
|
|
21
|
-
import { formatCount, replaceTabs, TRUNCATE_LENGTHS } from "./render-utils";
|
|
21
|
+
import { formatCount, formatExpandHint, formatMoreItems, replaceTabs, TRUNCATE_LENGTHS } from "./render-utils";
|
|
22
22
|
import { ToolError } from "./tool-errors";
|
|
23
23
|
|
|
24
24
|
const DEFAULT_LIMIT = 8;
|
|
@@ -171,6 +171,25 @@ function renderMatchLines(match: SearchToolBm25Match, theme: Theme): string[] {
|
|
|
171
171
|
return lines;
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
+
function renderMatchBullets(tools: SearchToolBm25Match[], expanded: boolean, theme: Theme): string[] {
|
|
175
|
+
const shown = expanded ? tools.length : Math.min(tools.length, COLLAPSED_MATCH_LIMIT);
|
|
176
|
+
const bullet = theme.fg("dim", theme.format.bullet);
|
|
177
|
+
const lines: string[] = [];
|
|
178
|
+
for (let i = 0; i < shown; i++) {
|
|
179
|
+
const itemLines = renderMatchLines(tools[i]!, theme);
|
|
180
|
+
lines.push(`${bullet} ${itemLines[0]}`);
|
|
181
|
+
for (let j = 1; j < itemLines.length; j++) {
|
|
182
|
+
lines.push(` ${itemLines[j]}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const remaining = tools.length - shown;
|
|
186
|
+
if (remaining > 0) {
|
|
187
|
+
const hint = formatExpandHint(theme, expanded, true);
|
|
188
|
+
lines.push(`${theme.fg("muted", formatMoreItems(remaining, "tool"))}${hint ? ` ${hint}` : ""}`);
|
|
189
|
+
}
|
|
190
|
+
return lines;
|
|
191
|
+
}
|
|
192
|
+
|
|
174
193
|
function renderFallbackResult(text: string, theme: Theme): Component {
|
|
175
194
|
const header = renderStatusLine({ icon: "warning", title: TOOL_DISCOVERY_TITLE }, theme);
|
|
176
195
|
const bodyLines = (text || "Tool discovery completed")
|
|
@@ -271,14 +290,11 @@ export const searchToolBm25Renderer = {
|
|
|
271
290
|
renderCall(args: SearchToolBm25Params, _options: RenderResultOptions, uiTheme: Theme): Component {
|
|
272
291
|
const query = typeof args.query === "string" ? replaceTabs(args.query.trim()) : "";
|
|
273
292
|
const meta = args.limit ? [`limit:${args.limit}`] : [];
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
uiTheme,
|
|
278
|
-
),
|
|
279
|
-
0,
|
|
280
|
-
0,
|
|
293
|
+
const header = renderStatusLine(
|
|
294
|
+
{ icon: "pending", title: TOOL_DISCOVERY_TITLE, description: query || "(empty query)", meta },
|
|
295
|
+
uiTheme,
|
|
281
296
|
);
|
|
297
|
+
return new Text(header, 0, 0);
|
|
282
298
|
},
|
|
283
299
|
|
|
284
300
|
renderResult(
|
|
@@ -305,7 +321,9 @@ export const searchToolBm25Renderer = {
|
|
|
305
321
|
const safeQuery = replaceTabs(details.query);
|
|
306
322
|
const header = renderStatusLine(
|
|
307
323
|
{
|
|
308
|
-
|
|
324
|
+
...(details.tools.length > 0
|
|
325
|
+
? { iconOverride: uiTheme.fg("accent", uiTheme.symbol("icon.search")) }
|
|
326
|
+
: { icon: "warning" as const }),
|
|
309
327
|
title: TOOL_DISCOVERY_TITLE,
|
|
310
328
|
description: truncateToWidth(safeQuery, MATCH_LABEL_LEN),
|
|
311
329
|
meta,
|
|
@@ -318,19 +336,14 @@ export const searchToolBm25Renderer = {
|
|
|
318
336
|
return new Text(`${header}\n${uiTheme.fg("muted", emptyMessage)}`, 0, 0);
|
|
319
337
|
}
|
|
320
338
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
{
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
},
|
|
330
|
-
uiTheme,
|
|
331
|
-
);
|
|
332
|
-
lines.push(...treeLines);
|
|
333
|
-
return new Text(lines.join("\n"), 0, 0);
|
|
339
|
+
return framedBlock(uiTheme, width => ({
|
|
340
|
+
header,
|
|
341
|
+
sections: [{ lines: renderMatchBullets(details.tools, options.expanded ?? false, uiTheme) }],
|
|
342
|
+
state: "success",
|
|
343
|
+
borderColor: "borderMuted",
|
|
344
|
+
applyBg: false,
|
|
345
|
+
width,
|
|
346
|
+
}));
|
|
334
347
|
},
|
|
335
348
|
|
|
336
349
|
mergeCallAndResult: true,
|
package/src/tools/search.ts
CHANGED
|
@@ -19,6 +19,8 @@ import { DEFAULT_MAX_COLUMN, type TruncationResult, truncateHead, truncateLine }
|
|
|
19
19
|
import {
|
|
20
20
|
Ellipsis,
|
|
21
21
|
fileHyperlink,
|
|
22
|
+
getTreeBranch,
|
|
23
|
+
getTreeContinuePrefix,
|
|
22
24
|
renderStatusLine,
|
|
23
25
|
renderTreeList,
|
|
24
26
|
truncateToWidth,
|
|
@@ -34,9 +36,9 @@ import {
|
|
|
34
36
|
parseArchivePathCandidates,
|
|
35
37
|
} from "./archive-reader";
|
|
36
38
|
import { createFileRecorder, formatResultPath } from "./file-recorder";
|
|
37
|
-
import { formatGroupedFiles } from "./grouped-file-output";
|
|
39
|
+
import { classifyGroupedLines, formatGroupedFiles, groupLineIndicesByBlank } from "./grouped-file-output";
|
|
38
40
|
import { formatMatchLine } from "./match-line-format";
|
|
39
|
-
import {
|
|
41
|
+
import type { OutputMeta } from "./output-meta";
|
|
40
42
|
import {
|
|
41
43
|
expandDelimitedPathEntries,
|
|
42
44
|
hasGlobPathChars,
|
|
@@ -56,8 +58,9 @@ import {
|
|
|
56
58
|
formatCount,
|
|
57
59
|
formatEmptyMessage,
|
|
58
60
|
formatErrorMessage,
|
|
61
|
+
formatMoreItems,
|
|
59
62
|
PREVIEW_LIMITS,
|
|
60
|
-
|
|
63
|
+
replaceTabs,
|
|
61
64
|
} from "./render-utils";
|
|
62
65
|
import { ToolError } from "./tool-errors";
|
|
63
66
|
import { toolResult } from "./tool-result";
|
|
@@ -283,7 +286,6 @@ interface IndexedContentLines {
|
|
|
283
286
|
starts: number[];
|
|
284
287
|
}
|
|
285
288
|
|
|
286
|
-
const INTERNAL_URL_DISPLAY_RE = /^[a-z][a-z0-9+.-]*:\/\//i;
|
|
287
289
|
const OMP_ROOT_URL_RE = /^omp:\/\/(?:\/?|docs\/?)$/i;
|
|
288
290
|
|
|
289
291
|
function normalizeSearchLine(line: string): string {
|
|
@@ -619,6 +621,10 @@ export interface SearchToolDetails {
|
|
|
619
621
|
/** Absolute base directory used during search. Used by the renderer to resolve
|
|
620
622
|
* display-relative paths to absolute paths for OSC 8 hyperlinks. */
|
|
621
623
|
searchPath?: string;
|
|
624
|
+
/** Session cwd at search time. The renderer resolves the display-relative
|
|
625
|
+
* (cwd-relative) header/match paths against this for OSC 8 hyperlinks;
|
|
626
|
+
* `searchPath` is the scope label target, not the display-path base. */
|
|
627
|
+
cwd?: string;
|
|
622
628
|
/** User-supplied paths whose base directory was missing on disk. The tool
|
|
623
629
|
* skipped these and continued with the surviving entries; surfaced as a
|
|
624
630
|
* non-fatal warning in the renderer and in the model-facing text. */
|
|
@@ -1001,6 +1007,7 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
1001
1007
|
const details: SearchToolDetails = {
|
|
1002
1008
|
scopePath,
|
|
1003
1009
|
searchPath,
|
|
1010
|
+
cwd: this.session.cwd,
|
|
1004
1011
|
matchCount: 0,
|
|
1005
1012
|
fileCount: 0,
|
|
1006
1013
|
files: [],
|
|
@@ -1127,6 +1134,7 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
1127
1134
|
const details: SearchToolDetails = {
|
|
1128
1135
|
scopePath,
|
|
1129
1136
|
searchPath,
|
|
1137
|
+
cwd: this.session.cwd,
|
|
1130
1138
|
matchCount: selectedMatches.length,
|
|
1131
1139
|
fileCount: fileList.length,
|
|
1132
1140
|
files: fileList,
|
|
@@ -1169,6 +1177,10 @@ interface SearchRenderArgs {
|
|
|
1169
1177
|
}
|
|
1170
1178
|
|
|
1171
1179
|
const COLLAPSED_TEXT_LIMIT = PREVIEW_LIMITS.COLLAPSED_LINES * 2;
|
|
1180
|
+
/** Line budget for the expanded view. Larger than collapsed so expanding
|
|
1181
|
+
* reveals more matches with context, but still bounded so a single hot file
|
|
1182
|
+
* whose matches span the whole file can't dump its entire length. */
|
|
1183
|
+
const EXPANDED_TEXT_LIMIT = PREVIEW_LIMITS.EXPANDED_LINES * 2;
|
|
1172
1184
|
|
|
1173
1185
|
const SEARCH_CODE_FRAME_LINE_RE = /^\s*\*?(\d+)│/;
|
|
1174
1186
|
|
|
@@ -1190,6 +1202,140 @@ function parseSearchDisplayLineNumber(line: string): number | undefined {
|
|
|
1190
1202
|
return Number.parseInt(match[1]!, 10);
|
|
1191
1203
|
}
|
|
1192
1204
|
|
|
1205
|
+
const SEARCH_MATCH_LINE_RE = /^\s*\*\d+(?:│|[:|])/;
|
|
1206
|
+
|
|
1207
|
+
interface RenderedSearchLine {
|
|
1208
|
+
raw: string;
|
|
1209
|
+
styled: string;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
function isSearchMatchLine(line: string): boolean {
|
|
1213
|
+
return SEARCH_MATCH_LINE_RE.test(line);
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
function isSearchHeaderLine(line: string): boolean {
|
|
1217
|
+
return /^#+ /.test(line);
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
const URL_HEADER_PREFIX_RE = /^#+\s+/;
|
|
1221
|
+
|
|
1222
|
+
function renderSearchDisplayLines(
|
|
1223
|
+
lines: readonly string[],
|
|
1224
|
+
headerBase: string | undefined,
|
|
1225
|
+
fileScope: string | undefined,
|
|
1226
|
+
uiTheme: Theme,
|
|
1227
|
+
): RenderedSearchLine[] {
|
|
1228
|
+
const contexts = classifyGroupedLines(lines, headerBase, fileScope);
|
|
1229
|
+
// `classifyGroupedLines` can't resolve internal URLs (TUI-only), so track the
|
|
1230
|
+
// resolved URL target here and use it for the body lines that follow.
|
|
1231
|
+
let urlFile: string | undefined;
|
|
1232
|
+
return lines.map((line, index) => {
|
|
1233
|
+
const ctx = contexts[index]!;
|
|
1234
|
+
if (ctx.kind === "dir") {
|
|
1235
|
+
urlFile = undefined;
|
|
1236
|
+
const styled = uiTheme.fg("accent", line);
|
|
1237
|
+
return { raw: line, styled: ctx.headerPath ? fileHyperlink(ctx.headerPath, styled) : styled };
|
|
1238
|
+
}
|
|
1239
|
+
if (ctx.kind === "file") {
|
|
1240
|
+
if (ctx.isUrl) {
|
|
1241
|
+
const raw = line
|
|
1242
|
+
.replace(URL_HEADER_PREFIX_RE, "")
|
|
1243
|
+
.trimEnd()
|
|
1244
|
+
.replace(/\s+\([^)]*\)\s*$/, "");
|
|
1245
|
+
const linked = linkUrlLikeSearchHeader(raw, uiTheme.fg("accent", line));
|
|
1246
|
+
urlFile = linked.absPath;
|
|
1247
|
+
return { raw: line, styled: linked.line };
|
|
1248
|
+
}
|
|
1249
|
+
urlFile = undefined;
|
|
1250
|
+
// Root-level files keep the bright accent; nested file headers are dimmed.
|
|
1251
|
+
const styled = uiTheme.fg(ctx.depth === 1 ? "accent" : "dim", line);
|
|
1252
|
+
return { raw: line, styled: ctx.headerPath ? fileHyperlink(ctx.headerPath, styled) : styled };
|
|
1253
|
+
}
|
|
1254
|
+
const styled = uiTheme.fg("toolOutput", line);
|
|
1255
|
+
const lineNumber = parseSearchDisplayLineNumber(line);
|
|
1256
|
+
const filePath = ctx.filePath ?? urlFile;
|
|
1257
|
+
return {
|
|
1258
|
+
raw: line,
|
|
1259
|
+
styled: filePath && lineNumber !== undefined ? fileHyperlink(filePath, styled, { line: lineNumber }) : styled,
|
|
1260
|
+
};
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
function compactSearchPreviewGroup(group: RenderedSearchLine[]): RenderedSearchLine[] {
|
|
1265
|
+
const compact = group.filter(line => isSearchHeaderLine(line.raw) || isSearchMatchLine(line.raw));
|
|
1266
|
+
return compact.length > 0 ? compact : group;
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
function countPreviewMatches(lines: readonly RenderedSearchLine[], hasMarkedMatches: boolean): number {
|
|
1270
|
+
if (hasMarkedMatches) return lines.reduce((count, line) => count + (isSearchMatchLine(line.raw) ? 1 : 0), 0);
|
|
1271
|
+
return lines.reduce((count, line) => count + (!isSearchHeaderLine(line.raw) && line.raw.length > 0 ? 1 : 0), 0);
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
function renderBudgetedSearchGroups(
|
|
1275
|
+
groups: RenderedSearchLine[][],
|
|
1276
|
+
maxLines: number,
|
|
1277
|
+
matchCount: number,
|
|
1278
|
+
uiTheme: Theme,
|
|
1279
|
+
compact: boolean,
|
|
1280
|
+
): string[] {
|
|
1281
|
+
if (maxLines <= 0) return [];
|
|
1282
|
+
const renderedGroups = groups
|
|
1283
|
+
.map(group => (compact ? compactSearchPreviewGroup(group) : group))
|
|
1284
|
+
.filter(group => group.length > 0);
|
|
1285
|
+
if (renderedGroups.length === 0) return [];
|
|
1286
|
+
|
|
1287
|
+
let totalLines = 0;
|
|
1288
|
+
let totalMarkedMatches = 0;
|
|
1289
|
+
let totalFallbackMatches = 0;
|
|
1290
|
+
for (const group of renderedGroups) {
|
|
1291
|
+
totalLines += group.length;
|
|
1292
|
+
totalMarkedMatches += countPreviewMatches(group, true);
|
|
1293
|
+
totalFallbackMatches += countPreviewMatches(group, false);
|
|
1294
|
+
}
|
|
1295
|
+
const hasMarkedMatches = totalMarkedMatches > 0;
|
|
1296
|
+
const needsSummary = totalLines > maxLines;
|
|
1297
|
+
const contentBudget = needsSummary ? Math.max(maxLines - 1, 0) : maxLines;
|
|
1298
|
+
const visibleGroups: RenderedSearchLine[][] = [];
|
|
1299
|
+
let visibleLineCount = 0;
|
|
1300
|
+
let visibleMatches = 0;
|
|
1301
|
+
for (const group of renderedGroups) {
|
|
1302
|
+
if (visibleLineCount >= contentBudget) break;
|
|
1303
|
+
const available = contentBudget - visibleLineCount;
|
|
1304
|
+
const take = Math.min(group.length, available);
|
|
1305
|
+
if (take <= 0) break;
|
|
1306
|
+
const visibleGroup = group.slice(0, take);
|
|
1307
|
+
visibleGroups.push(visibleGroup);
|
|
1308
|
+
visibleLineCount += visibleGroup.length;
|
|
1309
|
+
visibleMatches += countPreviewMatches(visibleGroup, hasMarkedMatches);
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
const totalMatches = hasMarkedMatches ? totalMarkedMatches : Math.max(matchCount, totalFallbackMatches);
|
|
1313
|
+
const hiddenMatches = Math.max(totalMatches - visibleMatches, 0);
|
|
1314
|
+
const hiddenLines = Math.max(totalLines - visibleLineCount, 0);
|
|
1315
|
+
const hasSummary = needsSummary && (hiddenMatches > 0 || hiddenLines > 0);
|
|
1316
|
+
const lines: string[] = [];
|
|
1317
|
+
for (let i = 0; i < visibleGroups.length; i++) {
|
|
1318
|
+
const group = visibleGroups[i]!;
|
|
1319
|
+
const isLast = !hasSummary && i === visibleGroups.length - 1;
|
|
1320
|
+
const prefix = `${uiTheme.fg("dim", getTreeBranch(isLast, uiTheme))} `;
|
|
1321
|
+
const continuePrefix = uiTheme.fg("dim", getTreeContinuePrefix(isLast, uiTheme));
|
|
1322
|
+
lines.push(`${prefix}${replaceTabs(group[0]!.styled)}`);
|
|
1323
|
+
for (let j = 1; j < group.length; j++) {
|
|
1324
|
+
lines.push(`${continuePrefix}${replaceTabs(group[j]!.styled)}`);
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
if (hasSummary) {
|
|
1328
|
+
const hiddenLabel =
|
|
1329
|
+
hiddenMatches > 0 ? formatMoreItems(hiddenMatches, "match") : formatMoreItems(hiddenLines, "line");
|
|
1330
|
+
lines.push(`${uiTheme.fg("dim", uiTheme.tree.last)} ${uiTheme.fg("muted", hiddenLabel)}`);
|
|
1331
|
+
}
|
|
1332
|
+
return lines;
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
function searchStatusIcon(uiTheme: Theme): string {
|
|
1336
|
+
return uiTheme.fg("toolTitle", uiTheme.symbol("icon.search"));
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1193
1339
|
export const searchToolRenderer = {
|
|
1194
1340
|
inline: true,
|
|
1195
1341
|
renderCall(args: SearchRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
|
|
@@ -1201,10 +1347,10 @@ export const searchToolRenderer = {
|
|
|
1201
1347
|
if (args.skip !== undefined && args.skip > 0) meta.push(`skip:${args.skip}`);
|
|
1202
1348
|
|
|
1203
1349
|
const text = renderStatusLine(
|
|
1204
|
-
{ icon: "pending", title: "Search", description: args.pattern || "?", meta },
|
|
1350
|
+
{ icon: "pending", title: "Search", titleColor: "toolTitle", description: args.pattern || "?", meta },
|
|
1205
1351
|
uiTheme,
|
|
1206
1352
|
);
|
|
1207
|
-
return new Text(text,
|
|
1353
|
+
return new Text(text, 1, 0);
|
|
1208
1354
|
},
|
|
1209
1355
|
|
|
1210
1356
|
renderResult(
|
|
@@ -1217,7 +1363,7 @@ export const searchToolRenderer = {
|
|
|
1217
1363
|
|
|
1218
1364
|
if (result.isError || details?.error) {
|
|
1219
1365
|
const errorText = details?.error || result.content?.find(c => c.type === "text")?.text || "Unknown error";
|
|
1220
|
-
return new Text(formatErrorMessage(errorText, uiTheme),
|
|
1366
|
+
return new Text(formatErrorMessage(errorText, uiTheme), 1, 0);
|
|
1221
1367
|
}
|
|
1222
1368
|
|
|
1223
1369
|
const hasDetailedData = details?.matchCount !== undefined || details?.fileCount !== undefined;
|
|
@@ -1225,12 +1371,18 @@ export const searchToolRenderer = {
|
|
|
1225
1371
|
if (!hasDetailedData) {
|
|
1226
1372
|
const textContent = result.details?.displayContent ?? result.content?.find(c => c.type === "text")?.text;
|
|
1227
1373
|
if (!textContent || textContent === "No matches found") {
|
|
1228
|
-
return new Text(formatEmptyMessage("No matches found", uiTheme),
|
|
1374
|
+
return new Text(formatEmptyMessage("No matches found", uiTheme), 1, 0);
|
|
1229
1375
|
}
|
|
1230
1376
|
const lines = textContent.split("\n").filter(line => line.trim() !== "");
|
|
1231
1377
|
const description = args?.pattern ?? undefined;
|
|
1232
1378
|
const header = renderStatusLine(
|
|
1233
|
-
{
|
|
1379
|
+
{
|
|
1380
|
+
iconOverride: searchStatusIcon(uiTheme),
|
|
1381
|
+
title: "Search",
|
|
1382
|
+
titleColor: "toolTitle",
|
|
1383
|
+
description,
|
|
1384
|
+
meta: [formatCount("item", lines.length)],
|
|
1385
|
+
},
|
|
1234
1386
|
uiTheme,
|
|
1235
1387
|
);
|
|
1236
1388
|
return createCachedComponent(
|
|
@@ -1249,6 +1401,7 @@ export const searchToolRenderer = {
|
|
|
1249
1401
|
);
|
|
1250
1402
|
return [header, ...listLines].map(l => truncateToWidth(l, width, Ellipsis.Omit));
|
|
1251
1403
|
},
|
|
1404
|
+
{ paddingX: 1 },
|
|
1252
1405
|
);
|
|
1253
1406
|
}
|
|
1254
1407
|
|
|
@@ -1269,12 +1422,12 @@ export const searchToolRenderer = {
|
|
|
1269
1422
|
const scopeMeta = searchScopeMeta(details);
|
|
1270
1423
|
if (scopeMeta) meta.push(scopeMeta);
|
|
1271
1424
|
const header = renderStatusLine(
|
|
1272
|
-
{ icon: "warning", title: "Search", description: args?.pattern, meta },
|
|
1425
|
+
{ icon: "warning", title: "Search", titleColor: "toolTitle", description: args?.pattern, meta },
|
|
1273
1426
|
uiTheme,
|
|
1274
1427
|
);
|
|
1275
1428
|
const lines = [header, formatEmptyMessage("No matches found", uiTheme)];
|
|
1276
1429
|
if (missingNote) lines.push(missingNote);
|
|
1277
|
-
return new Text(lines.join("\n"),
|
|
1430
|
+
return new Text(lines.join("\n"), 1, 0);
|
|
1278
1431
|
}
|
|
1279
1432
|
|
|
1280
1433
|
const summaryParts = [formatCount("match", matchCount), formatCount("file", fileCount)];
|
|
@@ -1284,104 +1437,45 @@ export const searchToolRenderer = {
|
|
|
1284
1437
|
if (truncated) meta.push(uiTheme.fg("warning", "truncated"));
|
|
1285
1438
|
const description = args?.pattern ?? undefined;
|
|
1286
1439
|
const header = renderStatusLine(
|
|
1287
|
-
{
|
|
1440
|
+
{
|
|
1441
|
+
...(truncated ? { icon: "warning" as const } : { iconOverride: searchStatusIcon(uiTheme) }),
|
|
1442
|
+
title: "Search",
|
|
1443
|
+
titleColor: "toolTitle",
|
|
1444
|
+
description,
|
|
1445
|
+
meta,
|
|
1446
|
+
},
|
|
1288
1447
|
uiTheme,
|
|
1289
1448
|
);
|
|
1290
1449
|
|
|
1291
1450
|
const textContent = result.details?.displayContent ?? result.content?.find(c => c.type === "text")?.text ?? "";
|
|
1292
|
-
const
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1451
|
+
const allLines = textContent.split("\n");
|
|
1452
|
+
// Resolve hyperlinks once over the whole output so a nested directory stack
|
|
1453
|
+
// reconstructs correctly across blank-line group boundaries.
|
|
1454
|
+
// Header/match display paths are cwd-relative, so resolve them against cwd
|
|
1455
|
+
// (falling back to searchPath for legacy results that predate `cwd`); the
|
|
1456
|
+
// scoped file's absolute path seeds body lines in single-file searches.
|
|
1457
|
+
const renderedLines = renderSearchDisplayLines(
|
|
1458
|
+
allLines,
|
|
1459
|
+
details?.cwd ?? details?.searchPath,
|
|
1460
|
+
details?.searchPath,
|
|
1461
|
+
uiTheme,
|
|
1462
|
+
);
|
|
1463
|
+
const matchGroups = groupLineIndicesByBlank(allLines).map(indices => indices.map(i => renderedLines[i]!));
|
|
1302
1464
|
|
|
1303
1465
|
const extraLines: string[] = [];
|
|
1304
|
-
if (truncationReasons.length > 0) {
|
|
1305
|
-
extraLines.push(uiTheme.fg("warning", `truncated: ${truncationReasons.join(", ")}`));
|
|
1306
|
-
}
|
|
1307
1466
|
if (missingNote) extraLines.push(missingNote);
|
|
1308
1467
|
|
|
1309
1468
|
return createCachedComponent(
|
|
1310
1469
|
() => options.expanded,
|
|
1311
1470
|
width => {
|
|
1312
|
-
const
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
{
|
|
1316
|
-
items: matchGroups,
|
|
1317
|
-
expanded: options.expanded,
|
|
1318
|
-
maxCollapsed: matchGroups.length,
|
|
1319
|
-
maxCollapsedLines: collapsedMatchLineBudget,
|
|
1320
|
-
itemType: "match",
|
|
1321
|
-
renderItem: group => {
|
|
1322
|
-
// Track directory/file context within a group so headers and code-frame
|
|
1323
|
-
// lines link to the backing file, with line-specific links for matches.
|
|
1324
|
-
let contextDir = searchBase ?? "";
|
|
1325
|
-
const hasFileHeader = group.some(line => line.startsWith("# "));
|
|
1326
|
-
let currentFilePath: string | undefined = hasFileHeader ? undefined : searchBase;
|
|
1327
|
-
return group.map(line => {
|
|
1328
|
-
if (line.startsWith("## ")) {
|
|
1329
|
-
// Strip optional ` (suffix)` and `#hash` before resolving.
|
|
1330
|
-
const fileName = line
|
|
1331
|
-
.slice(3)
|
|
1332
|
-
.trimEnd()
|
|
1333
|
-
.replace(/\s+\([^)]*\)\s*$/, "")
|
|
1334
|
-
.replace(/#[0-9a-f]+$/, "");
|
|
1335
|
-
const absPath = contextDir && fileName ? path.join(contextDir, fileName) : undefined;
|
|
1336
|
-
currentFilePath = absPath;
|
|
1337
|
-
const styled = uiTheme.fg("dim", line);
|
|
1338
|
-
return absPath ? fileHyperlink(absPath, styled) : styled;
|
|
1339
|
-
}
|
|
1340
|
-
if (line.startsWith("# ")) {
|
|
1341
|
-
const raw = line
|
|
1342
|
-
.slice(2)
|
|
1343
|
-
.trimEnd()
|
|
1344
|
-
.replace(/\s+\([^)]*\)\s*$/, "");
|
|
1345
|
-
if (INTERNAL_URL_DISPLAY_RE.test(raw)) {
|
|
1346
|
-
contextDir = "";
|
|
1347
|
-
const styled = uiTheme.fg("accent", line);
|
|
1348
|
-
const linked = linkUrlLikeSearchHeader(raw, styled);
|
|
1349
|
-
currentFilePath = linked.absPath;
|
|
1350
|
-
return linked.line;
|
|
1351
|
-
}
|
|
1352
|
-
const isDirectory = raw.endsWith("/");
|
|
1353
|
-
const name = isDirectory ? raw.replace(/\/$/, "") : raw.replace(/#[0-9a-f]+$/, "");
|
|
1354
|
-
if (isDirectory) {
|
|
1355
|
-
const absPath = searchBase
|
|
1356
|
-
? name === "."
|
|
1357
|
-
? searchBase
|
|
1358
|
-
: path.join(searchBase, name)
|
|
1359
|
-
: undefined;
|
|
1360
|
-
if (absPath) {
|
|
1361
|
-
contextDir = absPath;
|
|
1362
|
-
}
|
|
1363
|
-
currentFilePath = undefined;
|
|
1364
|
-
const styled = uiTheme.fg("accent", line);
|
|
1365
|
-
return absPath ? fileHyperlink(absPath, styled) : styled;
|
|
1366
|
-
}
|
|
1367
|
-
// Root-level file emitted by formatGroupedFiles when the directory is `.`.
|
|
1368
|
-
const absPath = searchBase && name ? path.join(searchBase, name) : undefined;
|
|
1369
|
-
currentFilePath = absPath;
|
|
1370
|
-
const styled = uiTheme.fg("accent", line);
|
|
1371
|
-
return absPath ? fileHyperlink(absPath, styled) : styled;
|
|
1372
|
-
}
|
|
1373
|
-
const styled = uiTheme.fg("toolOutput", line);
|
|
1374
|
-
const lineNumber = parseSearchDisplayLineNumber(line);
|
|
1375
|
-
return currentFilePath && lineNumber !== undefined
|
|
1376
|
-
? fileHyperlink(currentFilePath, styled, { line: lineNumber })
|
|
1377
|
-
: styled;
|
|
1378
|
-
});
|
|
1379
|
-
},
|
|
1380
|
-
},
|
|
1381
|
-
uiTheme,
|
|
1471
|
+
const budget = Math.max(
|
|
1472
|
+
(options.expanded ? EXPANDED_TEXT_LIMIT : COLLAPSED_TEXT_LIMIT) - extraLines.length,
|
|
1473
|
+
0,
|
|
1382
1474
|
);
|
|
1475
|
+
const matchLines = renderBudgetedSearchGroups(matchGroups, budget, matchCount, uiTheme, !options.expanded);
|
|
1383
1476
|
return [header, ...matchLines, ...extraLines].map(l => truncateToWidth(l, width, Ellipsis.Omit));
|
|
1384
1477
|
},
|
|
1478
|
+
{ paddingX: 1 },
|
|
1385
1479
|
);
|
|
1386
1480
|
},
|
|
1387
1481
|
mergeCallAndResult: true,
|
|
@@ -5,6 +5,14 @@ import { ToolError } from "./tool-errors";
|
|
|
5
5
|
const SQLITE_MAGIC = new Uint8Array([
|
|
6
6
|
0x53, 0x51, 0x4c, 0x69, 0x74, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x33, 0x00,
|
|
7
7
|
]);
|
|
8
|
+
|
|
9
|
+
export function looksLikeSqlite(bytes: Uint8Array): boolean {
|
|
10
|
+
if (bytes.byteLength < SQLITE_MAGIC.byteLength) return false;
|
|
11
|
+
for (const [index, byte] of SQLITE_MAGIC.entries()) {
|
|
12
|
+
if (bytes[index] !== byte) return false;
|
|
13
|
+
}
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
8
16
|
const SQLITE_PATH_PATTERN = /\.(?:sqlite3?|db3?)(?=(?::|\?|$))/gi;
|
|
9
17
|
const DEFAULT_QUERY_LIMIT = 20;
|
|
10
18
|
const DEFAULT_SCHEMA_SAMPLE_LIMIT = 5;
|
|
@@ -443,18 +451,7 @@ export function parseSqlitePathCandidates(filePath: string): SqlitePathCandidate
|
|
|
443
451
|
|
|
444
452
|
export async function isSqliteFile(absolutePath: string): Promise<boolean> {
|
|
445
453
|
try {
|
|
446
|
-
|
|
447
|
-
if (bytes.length !== SQLITE_MAGIC.byteLength) {
|
|
448
|
-
return false;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
for (const [index, byte] of SQLITE_MAGIC.entries()) {
|
|
452
|
-
if (bytes[index] !== byte) {
|
|
453
|
-
return false;
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
return true;
|
|
454
|
+
return looksLikeSqlite(await Bun.file(absolutePath).slice(0, SQLITE_MAGIC.byteLength).bytes());
|
|
458
455
|
} catch {
|
|
459
456
|
return false;
|
|
460
457
|
}
|