@shanepadgett/tau-agent 0.27.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/extensions/context/sync.ts +1 -9
- package/extensions/context-pruning/prune.ts +2 -2
- package/extensions/explore/README.md +13 -21
- package/extensions/explore/ast/adapter.ts +97 -0
- package/extensions/explore/ast/engine.ts +470 -0
- package/extensions/explore/ast/format/ast-search.ts +97 -0
- package/extensions/explore/ast/format/composite.ts +26 -0
- package/extensions/explore/ast/format/context.ts +66 -0
- package/extensions/explore/ast/format/deps.ts +86 -0
- package/extensions/explore/ast/format/discover.ts +85 -0
- package/extensions/explore/ast/format/impact.ts +105 -0
- package/extensions/explore/ast/format/outline.ts +66 -0
- package/extensions/explore/ast/format/relationships.ts +81 -0
- package/extensions/explore/ast/format/show.ts +38 -0
- package/extensions/explore/ast/grammars/kotlin.wasm +0 -0
- package/extensions/explore/ast/grammars/manifest.json +41 -0
- package/extensions/explore/ast/grammars/manifest.ts +99 -0
- package/extensions/explore/ast/grammars/odin.wasm +0 -0
- package/extensions/explore/ast/grammars/swift.wasm +0 -0
- package/extensions/explore/ast/graph/file-graph.ts +360 -0
- package/extensions/explore/ast/graph/relationships.ts +597 -0
- package/extensions/explore/ast/identity.ts +119 -0
- package/extensions/explore/ast/ir.ts +130 -0
- package/extensions/explore/ast/languages/csharp-file-deps.ts +189 -0
- package/extensions/explore/ast/languages/csharp.ts +493 -0
- package/extensions/explore/ast/languages/file-dep-util.ts +120 -0
- package/extensions/explore/ast/languages/fixtures/sample.cs +17 -0
- package/extensions/explore/ast/languages/fixtures/sample.java +21 -0
- package/extensions/explore/ast/languages/fixtures/sample.kt +15 -0
- package/extensions/explore/ast/languages/fixtures/sample.odin +28 -0
- package/extensions/explore/ast/languages/fixtures/sample.rs +31 -0
- package/extensions/explore/ast/languages/fixtures/sample.swift +25 -0
- package/extensions/explore/ast/languages/go-file-deps.ts +139 -0
- package/extensions/explore/ast/languages/go.ts +545 -0
- package/extensions/explore/ast/languages/java.ts +412 -0
- package/extensions/explore/ast/languages/jvm-file-deps.ts +217 -0
- package/extensions/explore/ast/languages/kotlin.ts +611 -0
- package/extensions/explore/ast/languages/odin-file-deps.ts +175 -0
- package/extensions/explore/ast/languages/odin.ts +407 -0
- package/extensions/explore/ast/languages/rust-file-deps.ts +159 -0
- package/extensions/explore/ast/languages/rust.ts +506 -0
- package/extensions/explore/ast/languages/swift-file-deps.ts +59 -0
- package/extensions/explore/ast/languages/swift.ts +466 -0
- package/extensions/explore/ast/languages/tree.ts +220 -0
- package/extensions/explore/ast/languages/typescript-file-deps.ts +259 -0
- package/extensions/explore/ast/languages/typescript-package-surface.ts +440 -0
- package/extensions/explore/ast/languages/typescript.ts +716 -0
- package/extensions/explore/ast/markdown.ts +145 -0
- package/extensions/explore/ast/package-surface.ts +42 -0
- package/extensions/explore/ast/queries/ast-search.ts +306 -0
- package/extensions/explore/ast/queries/composite-target.ts +77 -0
- package/extensions/explore/ast/queries/context.ts +406 -0
- package/extensions/explore/ast/queries/discover.ts +427 -0
- package/extensions/explore/ast/queries/impact.ts +250 -0
- package/extensions/explore/ast/queries/outline.ts +203 -0
- package/extensions/explore/ast/queries/show.ts +262 -0
- package/extensions/explore/ast/query.ts +48 -0
- package/extensions/explore/ast/read/hook.ts +125 -0
- package/extensions/explore/ast/read/policy.ts +55 -0
- package/extensions/explore/ast/registry.ts +120 -0
- package/extensions/explore/ast/scan.ts +76 -0
- package/extensions/explore/ast/slice.ts +31 -0
- package/extensions/explore/ast/tools/ast-search.ts +138 -0
- package/extensions/explore/ast/tools/context.ts +126 -0
- package/extensions/explore/ast/tools/deps.ts +72 -0
- package/extensions/explore/ast/tools/discover.ts +208 -0
- package/extensions/explore/ast/tools/impact.ts +135 -0
- package/extensions/explore/ast/tools/outline.ts +200 -0
- package/extensions/explore/ast/tools/relationships.ts +160 -0
- package/extensions/explore/ast/tools/render.ts +121 -0
- package/extensions/explore/ast/tools/reverse-deps.ts +72 -0
- package/extensions/explore/ast/tools/show.ts +116 -0
- package/extensions/explore/guidance.ts +52 -0
- package/extensions/explore/index.ts +82 -92
- package/extensions/explore/read/autoread.ts +118 -0
- package/extensions/explore/settings.ts +49 -31
- package/extensions/explore/traverse.ts +254 -91
- package/extensions/handoff/index.ts +1 -1
- package/extensions/publish/index.ts +18 -85
- package/extensions/publish/publish-ui.ts +0 -4
- package/extensions/soul/README.md +7 -4
- package/extensions/soul/index.ts +11 -5
- package/extensions/soul/prompt.ts +31 -13
- package/extensions/soul/settings.ts +7 -2
- package/extensions/subagent/agents/context-sync.md +6 -10
- package/extensions/subagent/agents/review.md +24 -55
- package/extensions/subagent/agents/scout.md +38 -115
- package/extensions/subagent/run.ts +1 -1
- package/extensions/tau-help/help.md +2 -4
- package/package.json +6 -4
- package/schemas/tau.schema.json +35 -30
- package/{extensions/explore → shared}/autoread.ts +21 -7
- package/shared/events.ts +1 -1
- package/shared/full-file-knowledge.ts +49 -0
- package/extensions/explore/ast-guidance.ts +0 -67
- package/extensions/explore/ast-languages.ts +0 -61
- package/extensions/explore/ast-tools.ts +0 -1994
- package/extensions/explore/ast-worker.ts +0 -1025
- package/extensions/explore/find.ts +0 -147
- package/extensions/explore/full-file-knowledge.ts +0 -234
- package/extensions/explore/grep.ts +0 -749
- package/extensions/explore/limits.ts +0 -22
- package/extensions/explore/ls.ts +0 -113
- package/extensions/explore/orientation-state.ts +0 -280
- package/extensions/explore/path-display.ts +0 -41
- package/extensions/explore/path-tree.ts +0 -150
- package/extensions/explore/read-cache.ts +0 -273
- package/extensions/explore/read-snapshots.ts +0 -57
- package/extensions/explore/read-stats-panel.ts +0 -176
- package/extensions/explore/read-stats.ts +0 -138
- package/extensions/explore/read.ts +0 -372
- package/extensions/explore/result.ts +0 -29
- package/native-bin/darwin-arm64/tau-ast +0 -0
|
@@ -1,372 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createReadToolDefinition,
|
|
3
|
-
DEFAULT_MAX_BYTES,
|
|
4
|
-
formatSize,
|
|
5
|
-
truncateHead,
|
|
6
|
-
type ReadToolDetails,
|
|
7
|
-
type ToolDefinition,
|
|
8
|
-
} from "@earendil-works/pi-coding-agent";
|
|
9
|
-
import { Container, Text } from "@earendil-works/pi-tui";
|
|
10
|
-
import { createHash } from "node:crypto";
|
|
11
|
-
import { readFile, realpath } from "node:fs/promises";
|
|
12
|
-
import { isAbsolute, resolve } from "node:path";
|
|
13
|
-
import { Type, type Static } from "typebox";
|
|
14
|
-
import { formatToolRowTitle, type ToolRowStateStore } from "../../shared/tool-row-state.js";
|
|
15
|
-
import {
|
|
16
|
-
createCompleteFileMeta,
|
|
17
|
-
estimateTokens,
|
|
18
|
-
readScopeKey,
|
|
19
|
-
selectCompleteFileResponse,
|
|
20
|
-
} from "./full-file-knowledge.ts";
|
|
21
|
-
import { normalizeCountLimit } from "./limits.ts";
|
|
22
|
-
import type { OrientationState, ReadPermission } from "./orientation-state.ts";
|
|
23
|
-
import { stripLeadingAt } from "./path-display.ts";
|
|
24
|
-
import {
|
|
25
|
-
createReadCacheStore,
|
|
26
|
-
type ReadCacheMetaV1,
|
|
27
|
-
type ReadCachePresentation,
|
|
28
|
-
type ReadCacheStore,
|
|
29
|
-
} from "./read-cache.ts";
|
|
30
|
-
import { createReadSnapshotStore, type ReadSnapshotStore } from "./read-snapshots.ts";
|
|
31
|
-
import exploreSettings, { matchesExploreReadGate, type ExploreReadGateSettings } from "./settings.ts";
|
|
32
|
-
|
|
33
|
-
const readSchema = Type.Object({
|
|
34
|
-
path: Type.String({ description: "Path to the file to read (relative or absolute)" }),
|
|
35
|
-
offset: Type.Optional(Type.Number({ description: "Line number to start reading from (1-indexed)" })),
|
|
36
|
-
limit: Type.Optional(Type.Number({ description: "Maximum number of lines to read" })),
|
|
37
|
-
lineNumbers: Type.Optional(Type.Boolean({ description: "Prefix text lines with their 1-indexed file line number" })),
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
type ReadToolInput = Static<typeof readSchema>;
|
|
41
|
-
type BaseReadDefinition = ReturnType<typeof createReadToolDefinition>;
|
|
42
|
-
interface ExploreReadDetails extends ReadToolDetails {
|
|
43
|
-
readCache?: ReadCacheMetaV1;
|
|
44
|
-
}
|
|
45
|
-
type ReadDefinition = ToolDefinition<typeof readSchema, ExploreReadDetails | undefined>;
|
|
46
|
-
type ReadExecute = ReadDefinition["execute"];
|
|
47
|
-
type ReadRenderCall = NonNullable<ReadDefinition["renderCall"]>;
|
|
48
|
-
type ReadRenderResult = NonNullable<ReadDefinition["renderResult"]>;
|
|
49
|
-
|
|
50
|
-
interface BaselineTextResult {
|
|
51
|
-
text: string;
|
|
52
|
-
details: ReadToolDetails | undefined;
|
|
53
|
-
totalLines: number;
|
|
54
|
-
startLine: number;
|
|
55
|
-
endLine: number;
|
|
56
|
-
completeFile: boolean;
|
|
57
|
-
scopeKey: string;
|
|
58
|
-
presentation: ReadCachePresentation;
|
|
59
|
-
summary: string;
|
|
60
|
-
cacheable: boolean;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const readDefinitionByCwd = new Map<string, BaseReadDefinition>();
|
|
64
|
-
|
|
65
|
-
function readDefinitionForCwd(cwd: string): BaseReadDefinition {
|
|
66
|
-
const existing = readDefinitionByCwd.get(cwd);
|
|
67
|
-
if (existing) return existing;
|
|
68
|
-
const definition = createReadToolDefinition(cwd);
|
|
69
|
-
readDefinitionByCwd.set(cwd, definition);
|
|
70
|
-
return definition;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function normalizeReadParams(params: ReadToolInput): ReadToolInput {
|
|
74
|
-
return {
|
|
75
|
-
...params,
|
|
76
|
-
path: stripLeadingAt(params.path),
|
|
77
|
-
limit: params.limit === undefined ? undefined : normalizeCountLimit(params.limit, 1),
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function isSupportedImage(buffer: Buffer): boolean {
|
|
82
|
-
return (
|
|
83
|
-
(buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff) ||
|
|
84
|
-
buffer.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])) ||
|
|
85
|
-
buffer.subarray(0, 6).toString("ascii") === "GIF87a" ||
|
|
86
|
-
buffer.subarray(0, 6).toString("ascii") === "GIF89a" ||
|
|
87
|
-
(buffer.subarray(0, 4).toString("ascii") === "RIFF" && buffer.subarray(8, 12).toString("ascii") === "WEBP") ||
|
|
88
|
-
buffer.subarray(0, 2).toString("ascii") === "BM"
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function renderCallSummary(args: ReadToolInput | undefined): string {
|
|
93
|
-
if (!args) return "";
|
|
94
|
-
const path = stripLeadingAt(args.path);
|
|
95
|
-
if (args.offset === undefined && args.limit === undefined) return path;
|
|
96
|
-
const start = args.offset ?? 1;
|
|
97
|
-
const limit = args.limit === undefined ? undefined : normalizeCountLimit(args.limit, 1);
|
|
98
|
-
const end = limit === undefined ? "" : start + limit - 1;
|
|
99
|
-
return `${path}:${start}${end === "" ? "" : `-${end}`}`;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function baselineText(text: string, params: ReadToolInput): BaselineTextResult {
|
|
103
|
-
const allLines = text.split("\n");
|
|
104
|
-
const startIndex = params.offset ? Math.max(0, params.offset - 1) : 0;
|
|
105
|
-
const startLine = startIndex + 1;
|
|
106
|
-
if (startIndex >= allLines.length) {
|
|
107
|
-
throw new Error(`Offset ${params.offset} is beyond end of file (${allLines.length} lines total)`);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const selectedEnd =
|
|
111
|
-
params.limit === undefined ? allLines.length : Math.min(startIndex + params.limit, allLines.length);
|
|
112
|
-
const selectedLines = allLines.slice(startIndex, selectedEnd);
|
|
113
|
-
const selectedContent = selectedLines
|
|
114
|
-
.map((line, index) => (params.lineNumbers ? `${startLine + index}: ${line}` : line))
|
|
115
|
-
.join("\n");
|
|
116
|
-
const truncation = truncateHead(selectedContent);
|
|
117
|
-
let outputText: string;
|
|
118
|
-
let details: ReadToolDetails | undefined;
|
|
119
|
-
let outputLines = selectedLines.length;
|
|
120
|
-
let cacheable = true;
|
|
121
|
-
|
|
122
|
-
if (truncation.firstLineExceedsLimit) {
|
|
123
|
-
const firstLineSize = formatSize(Buffer.byteLength(selectedLines[0] ?? "", "utf-8"));
|
|
124
|
-
outputText = `[Line ${startLine} is ${firstLineSize}, exceeds ${formatSize(DEFAULT_MAX_BYTES)} limit. Use bash: sed -n '${startLine}p' ${params.path} | head -c ${DEFAULT_MAX_BYTES}]`;
|
|
125
|
-
details = { truncation };
|
|
126
|
-
outputLines = 0;
|
|
127
|
-
cacheable = false;
|
|
128
|
-
} else if (truncation.truncated) {
|
|
129
|
-
outputLines = truncation.outputLines;
|
|
130
|
-
const endLineDisplay = startLine + outputLines - 1;
|
|
131
|
-
const nextOffset = endLineDisplay + 1;
|
|
132
|
-
outputText = truncation.content;
|
|
133
|
-
outputText +=
|
|
134
|
-
truncation.truncatedBy === "lines"
|
|
135
|
-
? `\n\n[Showing lines ${startLine}-${endLineDisplay} of ${allLines.length}. Use offset=${nextOffset} to continue.]`
|
|
136
|
-
: `\n\n[Showing lines ${startLine}-${endLineDisplay} of ${allLines.length} (${formatSize(DEFAULT_MAX_BYTES)} limit). Use offset=${nextOffset} to continue.]`;
|
|
137
|
-
details = { truncation };
|
|
138
|
-
} else if (selectedEnd < allLines.length) {
|
|
139
|
-
outputText = `${truncation.content}\n\n[${allLines.length - selectedEnd} more lines in file. Use offset=${selectedEnd + 1} to continue.]`;
|
|
140
|
-
} else {
|
|
141
|
-
outputText = truncation.content;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const endLine = outputLines === 0 ? startLine : startLine + outputLines - 1;
|
|
145
|
-
const completeFile = startIndex === 0 && selectedEnd === allLines.length && !truncation.truncated;
|
|
146
|
-
const presentation: ReadCachePresentation = params.lineNumbers ? "line-numbered" : "plain";
|
|
147
|
-
const scopeKey = readScopeKey(completeFile, startLine, endLine, presentation);
|
|
148
|
-
const summary = completeFile ? `${allLines.length} lines` : `${outputLines} lines`;
|
|
149
|
-
return {
|
|
150
|
-
text: outputText,
|
|
151
|
-
details,
|
|
152
|
-
totalLines: allLines.length,
|
|
153
|
-
startLine,
|
|
154
|
-
endLine,
|
|
155
|
-
completeFile,
|
|
156
|
-
scopeKey,
|
|
157
|
-
presentation,
|
|
158
|
-
summary,
|
|
159
|
-
cacheable,
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function withMeta(baseline: BaselineTextResult, meta: ReadCacheMetaV1, text = baseline.text) {
|
|
164
|
-
return {
|
|
165
|
-
content: [{ type: "text" as const, text }],
|
|
166
|
-
details: { ...baseline.details, readCache: meta },
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function createMeta(
|
|
171
|
-
baseline: BaselineTextResult,
|
|
172
|
-
pathKey: string,
|
|
173
|
-
hash: string,
|
|
174
|
-
mode: ReadCacheMetaV1["mode"],
|
|
175
|
-
returnedText: string,
|
|
176
|
-
baseHash?: string,
|
|
177
|
-
summary = baseline.summary,
|
|
178
|
-
): ReadCacheMetaV1 {
|
|
179
|
-
if (baseline.completeFile) {
|
|
180
|
-
return createCompleteFileMeta({
|
|
181
|
-
pathKey,
|
|
182
|
-
presentation: baseline.presentation,
|
|
183
|
-
servedHash: hash,
|
|
184
|
-
mode,
|
|
185
|
-
sourceText: baseline.text,
|
|
186
|
-
returnedText,
|
|
187
|
-
totalLines: baseline.totalLines,
|
|
188
|
-
summary,
|
|
189
|
-
baseHash,
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
return {
|
|
193
|
-
v: 1,
|
|
194
|
-
pathKey,
|
|
195
|
-
scopeKey: baseline.scopeKey,
|
|
196
|
-
presentation: baseline.presentation,
|
|
197
|
-
servedHash: hash,
|
|
198
|
-
baseHash,
|
|
199
|
-
mode,
|
|
200
|
-
baselineTokens: estimateTokens(baseline.text),
|
|
201
|
-
returnedTokens: estimateTokens(returnedText),
|
|
202
|
-
totalLines: baseline.totalLines,
|
|
203
|
-
summary,
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
export function createExploreReadTool(
|
|
208
|
-
rowState: ToolRowStateStore,
|
|
209
|
-
orientation: OrientationState,
|
|
210
|
-
cache: ReadCacheStore = createReadCacheStore(),
|
|
211
|
-
snapshots: ReadSnapshotStore = createReadSnapshotStore(),
|
|
212
|
-
readGate: () => ExploreReadGateSettings = () => exploreSettings.defaults.readGate,
|
|
213
|
-
): ReadDefinition {
|
|
214
|
-
const baseDefinition = readDefinitionForCwd(process.cwd());
|
|
215
|
-
return {
|
|
216
|
-
...baseDefinition,
|
|
217
|
-
description:
|
|
218
|
-
"Read a text or image file with optional line ranges and line numbers. Configured supported source requires a current structural attempt first. Repeated complete-file reads avoid repeating unchanged content and can return a useful diff or current source after changes.",
|
|
219
|
-
parameters: readSchema,
|
|
220
|
-
async execute(
|
|
221
|
-
toolCallId: Parameters<ReadExecute>[0],
|
|
222
|
-
params: Parameters<ReadExecute>[1],
|
|
223
|
-
signal: Parameters<ReadExecute>[2],
|
|
224
|
-
onUpdate: Parameters<ReadExecute>[3],
|
|
225
|
-
ctx: Parameters<ReadExecute>[4],
|
|
226
|
-
) {
|
|
227
|
-
const definition = readDefinitionForCwd(ctx.cwd);
|
|
228
|
-
const normalized = normalizeReadParams(params);
|
|
229
|
-
const requestedPath = isAbsolute(normalized.path)
|
|
230
|
-
? resolve(normalized.path)
|
|
231
|
-
: resolve(ctx.cwd, normalized.path);
|
|
232
|
-
const path = await realpath(requestedPath);
|
|
233
|
-
const snapshotEpoch = snapshots.epoch();
|
|
234
|
-
const buffer = await readFile(path);
|
|
235
|
-
if (isSupportedImage(buffer)) {
|
|
236
|
-
return definition.execute(
|
|
237
|
-
toolCallId,
|
|
238
|
-
{ path, offset: normalized.offset, limit: normalized.limit },
|
|
239
|
-
signal,
|
|
240
|
-
onUpdate,
|
|
241
|
-
ctx,
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
if (signal?.aborted) throw new Error("Operation aborted");
|
|
246
|
-
let text: string;
|
|
247
|
-
try {
|
|
248
|
-
text = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(buffer);
|
|
249
|
-
} catch {
|
|
250
|
-
return definition.execute(toolCallId, { ...normalized, path }, signal, onUpdate, ctx);
|
|
251
|
-
}
|
|
252
|
-
const hash = createHash("sha256").update(buffer).digest("hex");
|
|
253
|
-
const baseline = baselineText(text, normalized);
|
|
254
|
-
const staleEpoch = !snapshots.isCurrent(snapshotEpoch);
|
|
255
|
-
const cacheDecision = staleEpoch
|
|
256
|
-
? { baseHash: undefined, baselineText: undefined, recovery: false }
|
|
257
|
-
: cache.decision(ctx, requestedPath, baseline.scopeKey);
|
|
258
|
-
let output = baseline.text;
|
|
259
|
-
let mode: ReadCacheMetaV1["mode"] = cacheDecision.recovery ? "recovery" : "baseline";
|
|
260
|
-
let summary = baseline.summary;
|
|
261
|
-
|
|
262
|
-
if (baseline.cacheable && baseline.completeFile) {
|
|
263
|
-
const selected = selectCompleteFileResponse({
|
|
264
|
-
displayPath: normalized.path,
|
|
265
|
-
currentText: text,
|
|
266
|
-
currentHash: hash,
|
|
267
|
-
fullText: baseline.text,
|
|
268
|
-
totalLines: baseline.totalLines,
|
|
269
|
-
recovery: cacheDecision.recovery,
|
|
270
|
-
baseHash: cacheDecision.baseHash,
|
|
271
|
-
baselineText:
|
|
272
|
-
cacheDecision.baselineText ??
|
|
273
|
-
(cacheDecision.baseHash === undefined ? undefined : snapshots.get(cacheDecision.baseHash)),
|
|
274
|
-
});
|
|
275
|
-
output = selected.text;
|
|
276
|
-
mode = selected.mode;
|
|
277
|
-
summary = selected.summary;
|
|
278
|
-
} else if (baseline.cacheable && !cacheDecision.recovery && cacheDecision.baseHash === hash) {
|
|
279
|
-
output = `unchanged, lines ${baseline.startLine}-${baseline.endLine} of ${baseline.totalLines}`;
|
|
280
|
-
mode = "unchanged";
|
|
281
|
-
summary = output;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
let permission: ReadPermission = "ungated";
|
|
285
|
-
if ((await orientation.supports(path)) && matchesExploreReadGate(path, ctx.cwd, readGate())) {
|
|
286
|
-
const fingerprint = `sha256:${hash}`;
|
|
287
|
-
const gateDecision = orientation.check(path, fingerprint);
|
|
288
|
-
if (gateDecision === "blocked") {
|
|
289
|
-
const patchDiffPermitted =
|
|
290
|
-
baseline.cacheable &&
|
|
291
|
-
baseline.completeFile &&
|
|
292
|
-
normalized.offset === undefined &&
|
|
293
|
-
normalized.limit === undefined &&
|
|
294
|
-
!cacheDecision.recovery &&
|
|
295
|
-
cacheDecision.baselineText !== undefined &&
|
|
296
|
-
(mode === "diff" || mode === "unchanged") &&
|
|
297
|
-
orientation.checkPatch(path, fingerprint);
|
|
298
|
-
if (!patchDiffPermitted) {
|
|
299
|
-
orientation.recordBlockedRead(toolCallId, path);
|
|
300
|
-
throw new Error(
|
|
301
|
-
`Read blocked for ${normalized.path}: configured source has no current structural attempt. Use exact-name outline, focused api_discover, file-scoped ast_search, or symbol with a fresh locator, then retry read.`,
|
|
302
|
-
);
|
|
303
|
-
}
|
|
304
|
-
permission = "postPatchDiff";
|
|
305
|
-
} else {
|
|
306
|
-
permission = gateDecision;
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
if (!baseline.cacheable) {
|
|
311
|
-
const result = { content: [{ type: "text" as const, text: baseline.text }], details: baseline.details };
|
|
312
|
-
orientation.recordRead(toolCallId, path, permission, Buffer.byteLength(baseline.text));
|
|
313
|
-
return result;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
if (signal?.aborted) throw new Error("Operation aborted");
|
|
317
|
-
snapshots.set(hash, text, buffer.byteLength, snapshotEpoch);
|
|
318
|
-
const meta = createMeta(baseline, requestedPath, hash, mode, output, cacheDecision.baseHash, summary);
|
|
319
|
-
const result = withMeta(baseline, meta, output);
|
|
320
|
-
orientation.recordRead(toolCallId, path, permission, Buffer.byteLength(output));
|
|
321
|
-
return result;
|
|
322
|
-
},
|
|
323
|
-
renderCall(
|
|
324
|
-
args: Parameters<ReadRenderCall>[0],
|
|
325
|
-
theme: Parameters<ReadRenderCall>[1],
|
|
326
|
-
context: Parameters<ReadRenderCall>[2],
|
|
327
|
-
) {
|
|
328
|
-
rowState.watch(context.toolCallId, context.invalidate);
|
|
329
|
-
const text = (context.lastComponent as Text | undefined) ?? new Text("", 0, 0);
|
|
330
|
-
if (context.executionStarted || !context.isPartial) {
|
|
331
|
-
text.setText("");
|
|
332
|
-
return text;
|
|
333
|
-
}
|
|
334
|
-
const title = formatToolRowTitle(rowState, context.toolCallId, "read", theme);
|
|
335
|
-
text.setText(`${title} ${theme.fg("muted", renderCallSummary(args))}`);
|
|
336
|
-
return text;
|
|
337
|
-
},
|
|
338
|
-
renderResult(
|
|
339
|
-
result: Parameters<ReadRenderResult>[0],
|
|
340
|
-
options: Parameters<ReadRenderResult>[1],
|
|
341
|
-
theme: Parameters<ReadRenderResult>[2],
|
|
342
|
-
context: Parameters<ReadRenderResult>[3],
|
|
343
|
-
) {
|
|
344
|
-
rowState.watch(context.toolCallId, context.invalidate);
|
|
345
|
-
const container = new Container();
|
|
346
|
-
const title = formatToolRowTitle(rowState, context.toolCallId, "read", theme);
|
|
347
|
-
const args = context.args as ReadToolInput | undefined;
|
|
348
|
-
const details = result.details as ExploreReadDetails | undefined;
|
|
349
|
-
const summary = details?.readCache?.summary;
|
|
350
|
-
const summaryText = context.isError
|
|
351
|
-
? theme.fg("error", "error")
|
|
352
|
-
: summary
|
|
353
|
-
? theme.fg(
|
|
354
|
-
details?.readCache?.mode === "unchanged"
|
|
355
|
-
? "success"
|
|
356
|
-
: details?.readCache?.mode === "diff"
|
|
357
|
-
? "accent"
|
|
358
|
-
: "muted",
|
|
359
|
-
summary,
|
|
360
|
-
)
|
|
361
|
-
: "";
|
|
362
|
-
const header = `${title} ${theme.fg("muted", renderCallSummary(args))}${summaryText ? ` ${summaryText}` : ""}`;
|
|
363
|
-
container.addChild(new Text(header, 0, 0));
|
|
364
|
-
if (options.expanded) {
|
|
365
|
-
const definition = readDefinitionForCwd(context.cwd);
|
|
366
|
-
const body = definition.renderResult?.(result, { ...options, expanded: true }, theme, context);
|
|
367
|
-
if (body) container.addChild(body);
|
|
368
|
-
}
|
|
369
|
-
return container;
|
|
370
|
-
},
|
|
371
|
-
};
|
|
372
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { AgentToolResult } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
|
|
3
|
-
export interface ExploreTextDetails {
|
|
4
|
-
humanText?: string;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export function createExploreTextResult(
|
|
8
|
-
agentText: string,
|
|
9
|
-
humanText?: string,
|
|
10
|
-
): AgentToolResult<ExploreTextDetails | undefined> {
|
|
11
|
-
return {
|
|
12
|
-
content: [{ type: "text", text: agentText }],
|
|
13
|
-
details: humanText !== undefined && humanText !== agentText ? { humanText } : undefined,
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function firstTextContent(result: { content: readonly { type: string; text?: string }[] }): string {
|
|
18
|
-
for (const item of result.content) {
|
|
19
|
-
if (item.type === "text" && typeof item.text === "string") return item.text;
|
|
20
|
-
}
|
|
21
|
-
return "";
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function expandedExploreText(result: {
|
|
25
|
-
content: readonly { type: string; text?: string }[];
|
|
26
|
-
details?: ExploreTextDetails;
|
|
27
|
-
}): string {
|
|
28
|
-
return result.details?.humanText ?? firstTextContent(result);
|
|
29
|
-
}
|
|
Binary file
|