@oh-my-pi/pi-coding-agent 17.2.10 → 17.2.12
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 +53 -0
- package/dist/{CHANGELOG-rhwzpexa.md → CHANGELOG-k9ghy5sn.md} +53 -0
- package/dist/cli.js +10775 -10762
- package/dist/types/advisor/runtime.d.ts +1 -1
- package/dist/types/capability/mcp.d.ts +11 -0
- package/dist/types/capability/skill.d.ts +6 -0
- package/dist/types/cli/command-help.d.ts +3 -0
- package/dist/types/commands/share.d.ts +25 -0
- package/dist/types/commit/agentic/index.d.ts +3 -1
- package/dist/types/commit/execute.d.ts +32 -0
- package/dist/types/commit/index.d.ts +2 -1
- package/dist/types/commit/pipeline.d.ts +7 -1
- package/dist/types/config/custom-models.d.ts +31 -0
- package/dist/types/config/model-config-values.d.ts +19 -0
- package/dist/types/config/model-patch.d.ts +93 -0
- package/dist/types/config/model-provider-discovery.d.ts +51 -0
- package/dist/types/config/model-registry.d.ts +9 -52
- package/dist/types/config/settings-schema.d.ts +1 -34
- package/dist/types/config/settings.d.ts +5 -3
- package/dist/types/debug/raw-sse-buffer.d.ts +9 -0
- package/dist/types/discovery/agent-plugin-format.d.ts +136 -0
- package/dist/types/discovery/agent-plugins.d.ts +1 -0
- package/dist/types/discovery/contained-path.d.ts +33 -0
- package/dist/types/discovery/index.d.ts +1 -0
- package/dist/types/eval/executor-base.d.ts +8 -2
- package/dist/types/eval/kernel-session-registry.d.ts +60 -0
- package/dist/types/export/share.d.ts +1 -1
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -2
- package/dist/types/extensibility/extensions/loader.d.ts +7 -0
- package/dist/types/extensibility/extensions/types.d.ts +11 -0
- package/dist/types/extensibility/shared-events.d.ts +3 -2
- package/dist/types/extensibility/skills.d.ts +5 -0
- package/dist/types/lsp/diagnostics.d.ts +96 -0
- package/dist/types/lsp/index.d.ts +7 -128
- package/dist/types/lsp/servers.d.ts +72 -0
- package/dist/types/lsp/tool.d.ts +42 -0
- package/dist/types/lsp/workspace-diagnostics.d.ts +12 -0
- package/dist/types/lsp/writethrough.d.ts +33 -0
- package/dist/types/mcp/transports/header-policy.d.ts +46 -0
- package/dist/types/mcp/types.d.ts +15 -0
- package/dist/types/modes/components/agent-hub-projection.d.ts +2 -7
- package/dist/types/modes/components/agent-hub-renderer.d.ts +0 -7
- package/dist/types/modes/components/agent-hub.d.ts +2 -0
- package/dist/types/modes/components/agent-transcript-viewer.d.ts +2 -0
- package/dist/types/modes/components/chat-transcript-builder.d.ts +2 -0
- package/dist/types/modes/components/custom-editor.d.ts +1 -2
- package/dist/types/modes/components/status-line/types.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +2 -0
- package/dist/types/modes/theme/color.d.ts +19 -0
- package/dist/types/modes/theme/loader.d.ts +19 -0
- package/dist/types/modes/theme/schema.d.ts +175 -0
- package/dist/types/modes/theme/symbols.d.ts +28 -0
- package/dist/types/modes/theme/theme-class.d.ts +244 -0
- package/dist/types/modes/theme/theme.d.ts +9 -280
- package/dist/types/modes/theme/tui-adapters.d.ts +13 -0
- package/dist/types/modes/utils/transcript-render-helpers.d.ts +3 -2
- package/dist/types/registry/agent-registry.d.ts +1 -3
- package/dist/types/secrets/index.d.ts +26 -2
- package/dist/types/secrets/message-transform.d.ts +40 -0
- package/dist/types/secrets/obfuscator.d.ts +0 -108
- package/dist/types/secrets/placeholder-scan.d.ts +95 -0
- package/dist/types/secrets/placeholder.d.ts +94 -0
- package/dist/types/secrets/replacement.d.ts +82 -0
- package/dist/types/session/agent-session-events.d.ts +2 -2
- package/dist/types/session/agent-session-types.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +2 -2
- package/dist/types/session/messages.d.ts +1 -0
- package/dist/types/session/prewalk.d.ts +3 -1
- package/dist/types/session/session-handoff.d.ts +3 -3
- package/dist/types/session/session-manager.d.ts +32 -0
- package/dist/types/session/session-provider-boundary.d.ts +1 -1
- package/dist/types/session/session-tools.d.ts +8 -0
- package/dist/types/session/turn-recovery.d.ts +9 -4
- package/dist/types/slash-commands/builtin-collaboration.d.ts +2 -0
- package/dist/types/slash-commands/builtin-completions.d.ts +36 -0
- package/dist/types/slash-commands/builtin-control.d.ts +2 -0
- package/dist/types/slash-commands/builtin-lifecycle.d.ts +3 -0
- package/dist/types/slash-commands/builtin-marketplace.d.ts +11 -0
- package/dist/types/slash-commands/builtin-modes.d.ts +5 -0
- package/dist/types/slash-commands/builtin-registry.d.ts +1 -1
- package/dist/types/slash-commands/builtin-session.d.ts +2 -0
- package/dist/types/task/index.d.ts +2 -0
- package/dist/types/tools/gh-common.d.ts +69 -0
- package/dist/types/tools/gh-pr-checkout.d.ts +84 -0
- package/dist/types/tools/gh-pr-diff.d.ts +102 -0
- package/dist/types/tools/gh-run-watch.d.ts +78 -0
- package/dist/types/tools/gh-search.d.ts +61 -0
- package/dist/types/tools/gh-types.d.ts +340 -0
- package/dist/types/tools/gh-view.d.ts +88 -0
- package/dist/types/tools/gh.d.ts +4 -201
- package/dist/types/tools/read-archive.d.ts +16 -0
- package/dist/types/tools/read-format.d.ts +104 -0
- package/dist/types/tools/read-path-resolution.d.ts +19 -0
- package/dist/types/tools/read-pdf-images.d.ts +12 -0
- package/dist/types/tools/read-renderer.d.ts +24 -0
- package/dist/types/tools/read-selector.d.ts +27 -0
- package/dist/types/tools/read-sqlite.d.ts +16 -0
- package/dist/types/tools/read-summary.d.ts +19 -0
- package/dist/types/tools/read.d.ts +1 -23
- package/dist/types/tools/shell-tokenize.d.ts +18 -1
- package/dist/types/utils/changelog.d.ts +0 -5
- package/package.json +13 -13
- package/scripts/legacy-pi-virtual-module.ts +4 -1
- package/src/advisor/runtime.ts +12 -7
- package/src/capability/mcp.ts +11 -0
- package/src/capability/skill.ts +6 -0
- package/src/cli/command-help.ts +4 -0
- package/src/cli/gallery-cli.ts +1 -1
- package/src/cli-commands.ts +5 -0
- package/src/commands/commit.ts +16 -3
- package/src/commands/share.ts +71 -0
- package/src/commit/agentic/index.ts +39 -21
- package/src/commit/execute.ts +56 -0
- package/src/commit/index.ts +2 -1
- package/src/commit/pipeline.ts +20 -7
- package/src/config/custom-models.ts +188 -0
- package/src/config/model-config-values.ts +128 -0
- package/src/config/model-discovery.ts +1 -1
- package/src/config/model-patch.ts +252 -0
- package/src/config/model-provider-discovery.ts +132 -0
- package/src/config/model-registry.ts +84 -704
- package/src/config/settings-schema.ts +2 -33
- package/src/config/settings.ts +53 -3
- package/src/debug/raw-sse-buffer.ts +20 -0
- package/src/discovery/agent-plugin-format.ts +551 -0
- package/src/discovery/agent-plugins.ts +341 -0
- package/src/discovery/claude-plugins.ts +21 -5
- package/src/discovery/contained-path.ts +75 -0
- package/src/discovery/helpers.ts +23 -9
- package/src/discovery/index.ts +1 -0
- package/src/discovery/omp-plugins.ts +20 -7
- package/src/edit/hashline/diff.ts +5 -1
- package/src/eval/executor-base.ts +39 -6
- package/src/eval/jl/executor.ts +82 -371
- package/src/eval/kernel-session-registry.ts +398 -0
- package/src/eval/py/executor.ts +53 -261
- package/src/eval/rb/executor.ts +36 -279
- package/src/exec/non-interactive-env.ts +1 -0
- package/src/export/share.ts +2 -1
- package/src/extensibility/custom-tools/types.ts +2 -2
- package/src/extensibility/extensions/loader.ts +78 -14
- package/src/extensibility/extensions/runner.ts +6 -0
- package/src/extensibility/extensions/types.ts +12 -0
- package/src/extensibility/plugins/marketplace/manager.ts +2 -1
- package/src/extensibility/shared-events.ts +3 -2
- package/src/extensibility/skills.ts +9 -0
- package/src/internal-urls/skill-protocol.ts +14 -0
- package/src/internal-urls/vault-protocol.ts +2 -2
- package/src/launch/client.ts +11 -1
- package/src/launch/protocol.ts +7 -2
- package/src/lsp/diagnostics.ts +516 -0
- package/src/lsp/index.ts +20 -2819
- package/src/lsp/servers.ts +296 -0
- package/src/lsp/tool.ts +1352 -0
- package/src/lsp/workspace-diagnostics.ts +170 -0
- package/src/lsp/writethrough.ts +561 -0
- package/src/main.ts +3 -2
- package/src/mcp/config.ts +3 -0
- package/src/mcp/manager.ts +12 -9
- package/src/mcp/transports/header-policy.ts +95 -0
- package/src/mcp/transports/http.ts +35 -52
- package/src/mcp/transports/sse.ts +20 -27
- package/src/mcp/types.ts +15 -0
- package/src/modes/acp/acp-agent.ts +15 -4
- package/src/modes/components/agent-dashboard.ts +2 -0
- package/src/modes/components/agent-hub-projection.ts +2 -2
- package/src/modes/components/agent-hub-renderer.ts +3 -7
- package/src/modes/components/agent-hub.ts +5 -0
- package/src/modes/components/agent-transcript-viewer.ts +3 -0
- package/src/modes/components/chat-transcript-builder.ts +3 -0
- package/src/modes/components/custom-editor.ts +0 -9
- package/src/modes/components/status-line/component.ts +1 -0
- package/src/modes/components/status-line/segments.ts +6 -4
- package/src/modes/components/status-line/types.ts +2 -0
- package/src/modes/components/tool-execution.ts +13 -16
- package/src/modes/components/tree-selector.ts +62 -3
- package/src/modes/controllers/command-controller.ts +8 -2
- package/src/modes/controllers/event-controller.ts +15 -15
- package/src/modes/controllers/input-controller.ts +20 -2
- package/src/modes/controllers/selector-controller.ts +1 -0
- package/src/modes/theme/color.ts +133 -0
- package/src/modes/theme/loader.ts +178 -0
- package/src/modes/theme/schema.ts +263 -0
- package/src/modes/theme/symbols.ts +1000 -0
- package/src/modes/theme/theme-class.ts +611 -0
- package/src/modes/theme/theme.ts +27 -2453
- package/src/modes/theme/tui-adapters.ts +279 -0
- package/src/modes/utils/transcript-render-helpers.ts +4 -2
- package/src/modes/utils/ui-helpers.ts +1 -0
- package/src/registry/agent-registry.ts +2 -2
- package/src/sdk.ts +6 -49
- package/src/secrets/index.ts +51 -6
- package/src/secrets/message-transform.ts +287 -0
- package/src/secrets/obfuscator.ts +39 -1303
- package/src/secrets/placeholder-scan.ts +506 -0
- package/src/secrets/placeholder.ts +309 -0
- package/src/secrets/replacement.ts +216 -0
- package/src/session/agent-session-events.ts +2 -2
- package/src/session/agent-session-types.ts +6 -0
- package/src/session/agent-session.ts +146 -21
- package/src/session/indexed-session-storage.ts +7 -2
- package/src/session/messages.ts +1 -0
- package/src/session/prewalk.ts +57 -17
- package/src/session/session-advisors.ts +32 -34
- package/src/session/session-context.ts +3 -5
- package/src/session/session-handoff.ts +39 -16
- package/src/session/session-manager.ts +67 -3
- package/src/session/session-provider-boundary.ts +3 -6
- package/src/session/session-tools.ts +37 -3
- package/src/session/todo-tracker.ts +11 -1
- package/src/session/turn-recovery.ts +115 -84
- package/src/slash-commands/builtin-collaboration.ts +504 -0
- package/src/slash-commands/builtin-completions.ts +291 -0
- package/src/slash-commands/builtin-control.ts +78 -0
- package/src/slash-commands/builtin-lifecycle.ts +506 -0
- package/src/slash-commands/builtin-marketplace.ts +567 -0
- package/src/slash-commands/builtin-modes.ts +518 -0
- package/src/slash-commands/builtin-registry.ts +21 -2996
- package/src/slash-commands/builtin-session.ts +592 -0
- package/src/task/executor.ts +17 -14
- package/src/task/index.ts +21 -6
- package/src/tools/bash-skill-urls.ts +15 -0
- package/src/tools/bash.ts +10 -11
- package/src/tools/debug.ts +1 -1
- package/src/tools/gh-common.ts +288 -0
- package/src/tools/gh-pr-checkout.ts +679 -0
- package/src/tools/gh-pr-diff.ts +473 -0
- package/src/tools/gh-run-watch.ts +1015 -0
- package/src/tools/gh-search.ts +500 -0
- package/src/tools/gh-types.ts +379 -0
- package/src/tools/gh-view.ts +612 -0
- package/src/tools/gh.ts +109 -3821
- package/src/tools/jtd-to-json-schema.ts +123 -21
- package/src/tools/read-archive.ts +209 -0
- package/src/tools/read-format.ts +593 -0
- package/src/tools/read-path-resolution.ts +36 -0
- package/src/tools/read-pdf-images.ts +250 -0
- package/src/tools/read-renderer.ts +289 -0
- package/src/tools/read-selector.ts +84 -0
- package/src/tools/read-sqlite.ts +215 -0
- package/src/tools/read-summary.ts +199 -0
- package/src/tools/read.ts +82 -1824
- package/src/tools/shell-tokenize.ts +99 -1
- package/src/utils/changelog.ts +1 -1
- package/src/utils/title-generator.ts +3 -1
- package/src/web/search/providers/exa.ts +1 -1
- package/src/web/search/providers/zai.ts +12 -3
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import type { AgentToolResult } from "@oh-my-pi/pi-agent-core";
|
|
3
|
+
import type { ToolSession } from "../sdk";
|
|
4
|
+
import { DEFAULT_MAX_LINES, truncateHead } from "../session/streaming-output";
|
|
5
|
+
import { applyListLimit } from "./list-limit";
|
|
6
|
+
import { resolveReadPath } from "./path-utils";
|
|
7
|
+
import type { ReadToolDetails } from "./read";
|
|
8
|
+
import { prependSuffixResolutionNotice } from "./read-format";
|
|
9
|
+
import {
|
|
10
|
+
findSuffixMatchCached,
|
|
11
|
+
isNotFoundError,
|
|
12
|
+
isRemoteMountPath,
|
|
13
|
+
type SuffixMatchCache,
|
|
14
|
+
} from "./read-path-resolution";
|
|
15
|
+
import {
|
|
16
|
+
executeReadQuery,
|
|
17
|
+
getRowByKey,
|
|
18
|
+
getRowByRowId,
|
|
19
|
+
getTableSchema,
|
|
20
|
+
isSqliteFile,
|
|
21
|
+
listTables,
|
|
22
|
+
MAX_RAW_QUERY_ROWS,
|
|
23
|
+
parseSqlitePathCandidates,
|
|
24
|
+
parseSqliteSelector,
|
|
25
|
+
queryRows,
|
|
26
|
+
renderRow,
|
|
27
|
+
renderSchema,
|
|
28
|
+
renderTable,
|
|
29
|
+
renderTableList,
|
|
30
|
+
resolveTableRowLookup,
|
|
31
|
+
} from "./sqlite-reader";
|
|
32
|
+
import { ToolError, throwIfAborted } from "./tool-errors";
|
|
33
|
+
import { toolResult } from "./tool-result";
|
|
34
|
+
|
|
35
|
+
interface ResolvedSqliteReadPath {
|
|
36
|
+
absolutePath: string;
|
|
37
|
+
sqliteSubPath: string;
|
|
38
|
+
queryString: string;
|
|
39
|
+
suffixResolution?: { from: string; to: string };
|
|
40
|
+
}
|
|
41
|
+
export async function resolveSqliteReadPath(
|
|
42
|
+
session: ToolSession,
|
|
43
|
+
readPath: string,
|
|
44
|
+
suffixCache: SuffixMatchCache,
|
|
45
|
+
signal?: AbortSignal,
|
|
46
|
+
): Promise<ResolvedSqliteReadPath | null> {
|
|
47
|
+
const candidates = parseSqlitePathCandidates(readPath);
|
|
48
|
+
for (const candidate of candidates) {
|
|
49
|
+
let absolutePath = resolveReadPath(candidate.sqlitePath, session.cwd);
|
|
50
|
+
let suffixResolution: { from: string; to: string } | undefined;
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const stat = await Bun.file(absolutePath).stat();
|
|
54
|
+
if (stat.isDirectory()) continue;
|
|
55
|
+
if (!(await isSqliteFile(absolutePath))) continue;
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
absolutePath,
|
|
59
|
+
sqliteSubPath: candidate.subPath,
|
|
60
|
+
queryString: candidate.queryString,
|
|
61
|
+
suffixResolution,
|
|
62
|
+
};
|
|
63
|
+
} catch (error) {
|
|
64
|
+
if (!isNotFoundError(error) || isRemoteMountPath(absolutePath)) continue;
|
|
65
|
+
|
|
66
|
+
const suffixMatch = await findSuffixMatchCached(session, suffixCache, candidate.sqlitePath, signal);
|
|
67
|
+
if (!suffixMatch) continue;
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const retryStat = await Bun.file(suffixMatch.absolutePath).stat();
|
|
71
|
+
if (retryStat.isDirectory()) continue;
|
|
72
|
+
if (!(await isSqliteFile(suffixMatch.absolutePath))) continue;
|
|
73
|
+
|
|
74
|
+
absolutePath = suffixMatch.absolutePath;
|
|
75
|
+
suffixResolution = { from: candidate.sqlitePath, to: suffixMatch.displayPath };
|
|
76
|
+
return {
|
|
77
|
+
absolutePath,
|
|
78
|
+
sqliteSubPath: candidate.subPath,
|
|
79
|
+
queryString: candidate.queryString,
|
|
80
|
+
suffixResolution,
|
|
81
|
+
};
|
|
82
|
+
} catch (retryError) {
|
|
83
|
+
if (!isNotFoundError(retryError)) {
|
|
84
|
+
throw retryError;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
export async function readSqlite(
|
|
93
|
+
resolvedSqlitePath: ResolvedSqliteReadPath,
|
|
94
|
+
signal?: AbortSignal,
|
|
95
|
+
): Promise<AgentToolResult<ReadToolDetails>> {
|
|
96
|
+
throwIfAborted(signal);
|
|
97
|
+
|
|
98
|
+
const selectorInput = {
|
|
99
|
+
subPath: resolvedSqlitePath.sqliteSubPath,
|
|
100
|
+
queryString: resolvedSqlitePath.queryString,
|
|
101
|
+
};
|
|
102
|
+
const selector = parseSqliteSelector(selectorInput.subPath, selectorInput.queryString);
|
|
103
|
+
const details: ReadToolDetails = {
|
|
104
|
+
resolvedPath: resolvedSqlitePath.absolutePath,
|
|
105
|
+
suffixResolution: resolvedSqlitePath.suffixResolution,
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
let db: Database | null = null;
|
|
109
|
+
try {
|
|
110
|
+
db = new Database(resolvedSqlitePath.absolutePath, { readonly: true, strict: true });
|
|
111
|
+
db.run("PRAGMA busy_timeout = 3000");
|
|
112
|
+
throwIfAborted(signal);
|
|
113
|
+
|
|
114
|
+
switch (selector.kind) {
|
|
115
|
+
case "list": {
|
|
116
|
+
const listLimit = applyListLimit(listTables(db), { limit: 500 });
|
|
117
|
+
const output = prependSuffixResolutionNotice(
|
|
118
|
+
renderTableList(listLimit.items),
|
|
119
|
+
resolvedSqlitePath.suffixResolution,
|
|
120
|
+
);
|
|
121
|
+
const truncation = truncateHead(output, { maxLines: Number.MAX_SAFE_INTEGER });
|
|
122
|
+
details.truncation = truncation.truncated ? truncation : undefined;
|
|
123
|
+
const resultBuilder = toolResult<ReadToolDetails>(details)
|
|
124
|
+
.text(truncation.content)
|
|
125
|
+
.sourcePath(resolvedSqlitePath.absolutePath)
|
|
126
|
+
.limits({ resultLimit: listLimit.meta.resultLimit?.reached });
|
|
127
|
+
if (truncation.truncated) {
|
|
128
|
+
resultBuilder.truncation(truncation, { direction: "head" });
|
|
129
|
+
}
|
|
130
|
+
return resultBuilder.done();
|
|
131
|
+
}
|
|
132
|
+
case "schema": {
|
|
133
|
+
const sampleRows = queryRows(db, selector.table, { limit: selector.sampleLimit, offset: 0 });
|
|
134
|
+
let output = renderSchema(getTableSchema(db, selector.table), {
|
|
135
|
+
columns: sampleRows.columns,
|
|
136
|
+
rows: sampleRows.rows,
|
|
137
|
+
});
|
|
138
|
+
if (sampleRows.rows.length < sampleRows.totalCount) {
|
|
139
|
+
const remaining = sampleRows.totalCount - sampleRows.rows.length;
|
|
140
|
+
output += `\n[${remaining} more rows; append :${selector.table}?limit=20&offset=${sampleRows.rows.length} to the database path to continue]`;
|
|
141
|
+
}
|
|
142
|
+
return toolResult<ReadToolDetails>(details)
|
|
143
|
+
.text(prependSuffixResolutionNotice(output, resolvedSqlitePath.suffixResolution))
|
|
144
|
+
.sourcePath(resolvedSqlitePath.absolutePath)
|
|
145
|
+
.done();
|
|
146
|
+
}
|
|
147
|
+
case "row": {
|
|
148
|
+
const lookup = resolveTableRowLookup(db, selector.table);
|
|
149
|
+
const row =
|
|
150
|
+
lookup.kind === "pk"
|
|
151
|
+
? getRowByKey(db, selector.table, lookup, selector.key)
|
|
152
|
+
: getRowByRowId(db, selector.table, selector.key);
|
|
153
|
+
if (!row) {
|
|
154
|
+
return toolResult<ReadToolDetails>(details)
|
|
155
|
+
.text(
|
|
156
|
+
prependSuffixResolutionNotice(
|
|
157
|
+
`No row found in table '${selector.table}' for key '${selector.key}'.`,
|
|
158
|
+
resolvedSqlitePath.suffixResolution,
|
|
159
|
+
),
|
|
160
|
+
)
|
|
161
|
+
.sourcePath(resolvedSqlitePath.absolutePath)
|
|
162
|
+
.done();
|
|
163
|
+
}
|
|
164
|
+
return toolResult<ReadToolDetails>(details)
|
|
165
|
+
.text(prependSuffixResolutionNotice(renderRow(row), resolvedSqlitePath.suffixResolution))
|
|
166
|
+
.sourcePath(resolvedSqlitePath.absolutePath)
|
|
167
|
+
.done();
|
|
168
|
+
}
|
|
169
|
+
case "query": {
|
|
170
|
+
const page = queryRows(db, selector.table, selector);
|
|
171
|
+
return toolResult<ReadToolDetails>(details)
|
|
172
|
+
.text(
|
|
173
|
+
prependSuffixResolutionNotice(
|
|
174
|
+
renderTable(page.columns, page.rows, {
|
|
175
|
+
totalCount: page.totalCount,
|
|
176
|
+
offset: selector.offset,
|
|
177
|
+
limit: selector.limit,
|
|
178
|
+
table: selector.table,
|
|
179
|
+
dbPath: resolvedSqlitePath.absolutePath,
|
|
180
|
+
}),
|
|
181
|
+
resolvedSqlitePath.suffixResolution,
|
|
182
|
+
),
|
|
183
|
+
)
|
|
184
|
+
.sourcePath(resolvedSqlitePath.absolutePath)
|
|
185
|
+
.done();
|
|
186
|
+
}
|
|
187
|
+
case "raw": {
|
|
188
|
+
const result = executeReadQuery(db, selector.sql);
|
|
189
|
+
let output = renderTable(result.columns, result.rows, {
|
|
190
|
+
totalCount: result.rows.length,
|
|
191
|
+
offset: 0,
|
|
192
|
+
limit: result.rows.length || DEFAULT_MAX_LINES,
|
|
193
|
+
table: "query",
|
|
194
|
+
dbPath: resolvedSqlitePath.absolutePath,
|
|
195
|
+
});
|
|
196
|
+
if (result.truncated) {
|
|
197
|
+
output += `\n[Output capped at ${MAX_RAW_QUERY_ROWS} rows; add a LIMIT/OFFSET clause to the query to page through more]`;
|
|
198
|
+
}
|
|
199
|
+
return toolResult<ReadToolDetails>(details)
|
|
200
|
+
.text(prependSuffixResolutionNotice(output, resolvedSqlitePath.suffixResolution))
|
|
201
|
+
.sourcePath(resolvedSqlitePath.absolutePath)
|
|
202
|
+
.done();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
throw new ToolError("Unsupported SQLite selector");
|
|
207
|
+
} catch (error) {
|
|
208
|
+
if (error instanceof ToolError) {
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
throw new ToolError(error instanceof Error ? error.message : String(error));
|
|
212
|
+
} finally {
|
|
213
|
+
db?.close();
|
|
214
|
+
}
|
|
215
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import { type SummaryResult, summarizeCode } from "@oh-my-pi/pi-natives";
|
|
3
|
+
import { LRUCache } from "@oh-my-pi/pi-utils/lru";
|
|
4
|
+
import { isMarkdownPath } from "../modes/theme/theme";
|
|
5
|
+
import type { ToolSession } from "../sdk";
|
|
6
|
+
import { resolveFileDisplayMode } from "../utils/file-display-mode";
|
|
7
|
+
import {
|
|
8
|
+
canMergeBracePair,
|
|
9
|
+
countTextLines,
|
|
10
|
+
type ElidedRange,
|
|
11
|
+
formatMergedBraceLine,
|
|
12
|
+
formatSingleLine,
|
|
13
|
+
} from "./read-format";
|
|
14
|
+
import { throwIfAborted } from "./tool-errors";
|
|
15
|
+
|
|
16
|
+
// Per-session memo for tree-sitter summaries. `summarizeCode` is a pure function
|
|
17
|
+
// of (code, path, fold settings) but costs ~12-18ms for a ~1500-line file, and a
|
|
18
|
+
// repeat summary read of the same unchanged file re-parses from scratch. Key on
|
|
19
|
+
// the content hash of the freshly-read bytes (+ path + fold settings): the file
|
|
20
|
+
// is still read fresh on every call, so a hit only reuses the deterministic
|
|
21
|
+
// parse — there is no staleness window and no stat guard is needed. Bounded LRU,
|
|
22
|
+
// aged out with the session via WeakMap.
|
|
23
|
+
// Unusable results (not parsed, or nothing elided) are memoized as `false`: the
|
|
24
|
+
// full SummaryResult embeds the whole source in kept segments, and the caller
|
|
25
|
+
// only ever renders `parsed && elided` summaries — caching the segments would
|
|
26
|
+
// retain up to 48 near-2MiB sources just to remember "no summary".
|
|
27
|
+
const SUMMARY_CACHE_MAX = 48;
|
|
28
|
+
const summaryParseCaches = new WeakMap<object, LRUCache<string, SummaryResult | false>>();
|
|
29
|
+
function getSummaryParseCache(session: object): LRUCache<string, SummaryResult | false> {
|
|
30
|
+
let cache = summaryParseCaches.get(session);
|
|
31
|
+
if (!cache) {
|
|
32
|
+
cache = new LRUCache<string, SummaryResult | false>({ max: SUMMARY_CACHE_MAX });
|
|
33
|
+
summaryParseCaches.set(session, cache);
|
|
34
|
+
}
|
|
35
|
+
return cache;
|
|
36
|
+
}
|
|
37
|
+
const MAX_SUMMARY_BYTES = 2 * 1024 * 1024;
|
|
38
|
+
const MAX_SUMMARY_LINES = 20_000;
|
|
39
|
+
/**
|
|
40
|
+
* Prose files (Markdown flavors and plain text) skip code-block summarization
|
|
41
|
+
* unless `read.summarize.prose` opts them in.
|
|
42
|
+
*/
|
|
43
|
+
export function isProseSummaryPath(filePath: string): boolean {
|
|
44
|
+
return isMarkdownPath(filePath) || path.extname(filePath).toLowerCase() === ".txt";
|
|
45
|
+
}
|
|
46
|
+
export function routeReadThroughBridge(
|
|
47
|
+
session: ToolSession,
|
|
48
|
+
absolutePath: string,
|
|
49
|
+
options?: { line?: number; limit?: number },
|
|
50
|
+
): Promise<string> | undefined {
|
|
51
|
+
const bridge = session.getClientBridge?.();
|
|
52
|
+
if (!bridge?.capabilities.readTextFile || !bridge.readTextFile) return undefined;
|
|
53
|
+
return bridge.readTextFile({ path: absolutePath, ...options });
|
|
54
|
+
}
|
|
55
|
+
export async function trySummarize(
|
|
56
|
+
session: ToolSession,
|
|
57
|
+
absolutePath: string,
|
|
58
|
+
fileSize: number,
|
|
59
|
+
signal?: AbortSignal,
|
|
60
|
+
): Promise<SummaryResult | null> {
|
|
61
|
+
if (fileSize > MAX_SUMMARY_BYTES) return null;
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
throwIfAborted(signal);
|
|
65
|
+
const bridgePromise = routeReadThroughBridge(session, absolutePath);
|
|
66
|
+
const code =
|
|
67
|
+
bridgePromise !== undefined
|
|
68
|
+
? await bridgePromise.catch(() => Bun.file(absolutePath).text())
|
|
69
|
+
: await Bun.file(absolutePath).text();
|
|
70
|
+
throwIfAborted(signal);
|
|
71
|
+
const lineCount = countTextLines(code);
|
|
72
|
+
if (lineCount > MAX_SUMMARY_LINES) return null;
|
|
73
|
+
if (lineCount < session.settings.get("read.summarize.minTotalLines")) return null;
|
|
74
|
+
|
|
75
|
+
const minBodyLines = session.settings.get("read.summarize.minBodyLines");
|
|
76
|
+
const minCommentLines = session.settings.get("read.summarize.minCommentLines");
|
|
77
|
+
const unfoldUntilLines = session.settings.get("read.summarize.unfoldUntil");
|
|
78
|
+
const unfoldLimitLines = session.settings.get("read.summarize.unfoldLimit");
|
|
79
|
+
const cache = getSummaryParseCache(session);
|
|
80
|
+
const cacheKey = `${absolutePath}\0${Bun.hash(code)}\0${minBodyLines},${minCommentLines},${unfoldUntilLines},${unfoldLimitLines}`;
|
|
81
|
+
const memoized = cache.get(cacheKey);
|
|
82
|
+
if (memoized !== undefined) return memoized || null;
|
|
83
|
+
const result = summarizeCode({
|
|
84
|
+
code,
|
|
85
|
+
path: absolutePath,
|
|
86
|
+
minBodyLines,
|
|
87
|
+
minCommentLines,
|
|
88
|
+
unfoldUntilLines,
|
|
89
|
+
unfoldLimitLines,
|
|
90
|
+
});
|
|
91
|
+
const usable = result.parsed && result.elided ? result : false;
|
|
92
|
+
cache.set(cacheKey, usable);
|
|
93
|
+
return usable || null;
|
|
94
|
+
} catch {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function renderSummary(
|
|
100
|
+
session: ToolSession,
|
|
101
|
+
summary: SummaryResult,
|
|
102
|
+
): {
|
|
103
|
+
text: string;
|
|
104
|
+
displayText: string;
|
|
105
|
+
elidedRanges: ElidedRange[];
|
|
106
|
+
elidedLines: number;
|
|
107
|
+
} {
|
|
108
|
+
const displayMode = resolveFileDisplayMode(session);
|
|
109
|
+
const shouldAddHashLines = displayMode.hashLines;
|
|
110
|
+
const shouldAddLineNumbers = shouldAddHashLines ? false : displayMode.lineNumbers;
|
|
111
|
+
|
|
112
|
+
// Flatten segments into per-line units so we can merge a kept-head /
|
|
113
|
+
// elided / kept-tail sandwich into a single brace-pair line when the
|
|
114
|
+
// boundary lines look like `… {` and `}` (or matching variants).
|
|
115
|
+
type Unit =
|
|
116
|
+
| { kind: "line"; line: number; text: string }
|
|
117
|
+
| { kind: "elided"; startLine: number; endLine: number }
|
|
118
|
+
| {
|
|
119
|
+
kind: "merged";
|
|
120
|
+
startLine: number;
|
|
121
|
+
endLine: number;
|
|
122
|
+
headText: string;
|
|
123
|
+
tailText: string;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const raw: Unit[] = [];
|
|
127
|
+
for (const segment of summary.segments) {
|
|
128
|
+
if (segment.kind === "elided") {
|
|
129
|
+
raw.push({ kind: "elided", startLine: segment.startLine, endLine: segment.endLine });
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const text = segment.text ?? "";
|
|
133
|
+
if (text.length === 0) continue;
|
|
134
|
+
const lines = text.split("\n");
|
|
135
|
+
for (let i = 0; i < lines.length; i++) {
|
|
136
|
+
raw.push({ kind: "line", line: segment.startLine + i, text: lines[i] });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const units: Unit[] = [];
|
|
141
|
+
let i = 0;
|
|
142
|
+
while (i < raw.length) {
|
|
143
|
+
const cur = raw[i];
|
|
144
|
+
if (cur.kind === "elided") {
|
|
145
|
+
const prev = units.length > 0 ? units[units.length - 1] : null;
|
|
146
|
+
const next = i + 1 < raw.length ? raw[i + 1] : null;
|
|
147
|
+
if (prev?.kind === "line" && next?.kind === "line" && canMergeBracePair(prev.text, next.text)) {
|
|
148
|
+
units.pop();
|
|
149
|
+
units.push({
|
|
150
|
+
kind: "merged",
|
|
151
|
+
startLine: prev.line,
|
|
152
|
+
endLine: next.line,
|
|
153
|
+
headText: prev.text,
|
|
154
|
+
tailText: next.text,
|
|
155
|
+
});
|
|
156
|
+
i += 2;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
units.push(cur);
|
|
161
|
+
i++;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const modelParts: string[] = [];
|
|
165
|
+
const displayParts: string[] = [];
|
|
166
|
+
const elidedRanges: ElidedRange[] = [];
|
|
167
|
+
let elidedLines = 0;
|
|
168
|
+
for (const unit of units) {
|
|
169
|
+
if (unit.kind === "elided") {
|
|
170
|
+
modelParts.push("…");
|
|
171
|
+
displayParts.push("…");
|
|
172
|
+
elidedRanges.push({ start: unit.startLine, end: unit.endLine });
|
|
173
|
+
elidedLines += unit.endLine - unit.startLine + 1;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (unit.kind === "merged") {
|
|
177
|
+
const formatted = formatMergedBraceLine(
|
|
178
|
+
unit.startLine,
|
|
179
|
+
unit.endLine,
|
|
180
|
+
unit.headText,
|
|
181
|
+
unit.tailText,
|
|
182
|
+
shouldAddHashLines,
|
|
183
|
+
shouldAddLineNumbers,
|
|
184
|
+
);
|
|
185
|
+
modelParts.push(formatted.model);
|
|
186
|
+
displayParts.push(formatted.display);
|
|
187
|
+
// Suggest the full brace range so re-reading shows both braces
|
|
188
|
+
// plus the elided body in one shot.
|
|
189
|
+
elidedRanges.push({ start: unit.startLine, end: unit.endLine });
|
|
190
|
+
// Merged brace pair encloses (start+1)..(end-1) as elided.
|
|
191
|
+
elidedLines += Math.max(0, unit.endLine - unit.startLine - 1);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
modelParts.push(formatSingleLine(unit.line, unit.text, shouldAddHashLines, shouldAddLineNumbers));
|
|
195
|
+
displayParts.push(unit.text);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return { text: modelParts.join("\n"), displayText: displayParts.join("\n"), elidedRanges, elidedLines };
|
|
199
|
+
}
|