@shanepadgett/tau-agent 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/subagents.md +1 -2
- package/extensions/context/README.md +1 -1
- package/extensions/context/definitions.ts +2 -142
- package/extensions/context/index.ts +65 -388
- package/extensions/context/panel.ts +43 -108
- package/extensions/context/sync.ts +683 -0
- package/extensions/explore/README.md +2 -2
- package/extensions/explore/autoread.ts +20 -3
- package/extensions/explore/index.ts +23 -2
- package/extensions/explore/read-cache.ts +209 -0
- package/extensions/explore/read-snapshots.ts +44 -0
- package/extensions/explore/read-stats-panel.ts +154 -0
- package/extensions/explore/read-stats.ts +120 -0
- package/extensions/explore/read.ts +198 -42
- package/extensions/subagent/README.md +0 -1
- package/extensions/subagent/agents.ts +0 -4
- package/extensions/tau-help/help.md +3 -3
- package/package.json +3 -3
- package/shared/git.ts +2 -2
- package/extensions/subagent/agents/context-maintenance.md +0 -23
|
@@ -2,17 +2,21 @@ import {
|
|
|
2
2
|
createReadToolDefinition,
|
|
3
3
|
DEFAULT_MAX_BYTES,
|
|
4
4
|
formatSize,
|
|
5
|
+
generateUnifiedPatch,
|
|
5
6
|
truncateHead,
|
|
6
7
|
type ReadToolDetails,
|
|
7
8
|
type ToolDefinition,
|
|
8
9
|
} from "@earendil-works/pi-coding-agent";
|
|
9
|
-
import { Text } from "@earendil-works/pi-tui";
|
|
10
|
+
import { Container, Text } from "@earendil-works/pi-tui";
|
|
11
|
+
import { createHash } from "node:crypto";
|
|
10
12
|
import { readFile } from "node:fs/promises";
|
|
11
13
|
import { isAbsolute, resolve } from "node:path";
|
|
12
14
|
import { Type, type Static } from "typebox";
|
|
13
15
|
import { formatToolRowTitle, type ToolRowStateStore } from "../../shared/tool-row-state.js";
|
|
14
16
|
import { normalizeCountLimit } from "./limits.ts";
|
|
15
17
|
import { stripLeadingAt } from "./path-display.ts";
|
|
18
|
+
import { createReadCacheStore, type ReadCacheMetaV1, type ReadCacheStore } from "./read-cache.ts";
|
|
19
|
+
import { createReadSnapshotStore, type ReadSnapshotStore } from "./read-snapshots.ts";
|
|
16
20
|
|
|
17
21
|
const readSchema = Type.Object({
|
|
18
22
|
path: Type.String({ description: "Path to the file to read (relative or absolute)" }),
|
|
@@ -23,11 +27,26 @@ const readSchema = Type.Object({
|
|
|
23
27
|
|
|
24
28
|
type ReadToolInput = Static<typeof readSchema>;
|
|
25
29
|
type BaseReadDefinition = ReturnType<typeof createReadToolDefinition>;
|
|
26
|
-
|
|
30
|
+
interface ExploreReadDetails extends ReadToolDetails {
|
|
31
|
+
readCache?: ReadCacheMetaV1;
|
|
32
|
+
}
|
|
33
|
+
type ReadDefinition = ToolDefinition<typeof readSchema, ExploreReadDetails | undefined>;
|
|
27
34
|
type ReadExecute = ReadDefinition["execute"];
|
|
28
35
|
type ReadRenderCall = NonNullable<ReadDefinition["renderCall"]>;
|
|
29
36
|
type ReadRenderResult = NonNullable<ReadDefinition["renderResult"]>;
|
|
30
37
|
|
|
38
|
+
interface BaselineTextResult {
|
|
39
|
+
text: string;
|
|
40
|
+
details: ReadToolDetails | undefined;
|
|
41
|
+
totalLines: number;
|
|
42
|
+
startLine: number;
|
|
43
|
+
endLine: number;
|
|
44
|
+
completeFile: boolean;
|
|
45
|
+
scopeKey: string;
|
|
46
|
+
summary: string;
|
|
47
|
+
cacheable: boolean;
|
|
48
|
+
}
|
|
49
|
+
|
|
31
50
|
const readDefinitionByCwd = new Map<string, BaseReadDefinition>();
|
|
32
51
|
|
|
33
52
|
function readDefinitionForCwd(cwd: string): BaseReadDefinition {
|
|
@@ -67,7 +86,114 @@ function renderCallSummary(args: ReadToolInput | undefined): string {
|
|
|
67
86
|
return `${path}:${start}${end === "" ? "" : `-${end}`}`;
|
|
68
87
|
}
|
|
69
88
|
|
|
70
|
-
|
|
89
|
+
function estimateTokens(text: string): number {
|
|
90
|
+
return Math.ceil(text.length / 4);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function baselineText(text: string, params: ReadToolInput): BaselineTextResult {
|
|
94
|
+
const allLines = text.split("\n");
|
|
95
|
+
const startIndex = params.offset ? Math.max(0, params.offset - 1) : 0;
|
|
96
|
+
const startLine = startIndex + 1;
|
|
97
|
+
if (startIndex >= allLines.length) {
|
|
98
|
+
throw new Error(`Offset ${params.offset} is beyond end of file (${allLines.length} lines total)`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const selectedEnd =
|
|
102
|
+
params.limit === undefined ? allLines.length : Math.min(startIndex + params.limit, allLines.length);
|
|
103
|
+
const selectedLines = allLines.slice(startIndex, selectedEnd);
|
|
104
|
+
const selectedContent = selectedLines
|
|
105
|
+
.map((line, index) => (params.lineNumbers ? `${startLine + index}: ${line}` : line))
|
|
106
|
+
.join("\n");
|
|
107
|
+
const truncation = truncateHead(selectedContent);
|
|
108
|
+
let outputText: string;
|
|
109
|
+
let details: ReadToolDetails | undefined;
|
|
110
|
+
let outputLines = selectedLines.length;
|
|
111
|
+
let cacheable = true;
|
|
112
|
+
|
|
113
|
+
if (truncation.firstLineExceedsLimit) {
|
|
114
|
+
const firstLineSize = formatSize(Buffer.byteLength(selectedLines[0] ?? "", "utf-8"));
|
|
115
|
+
outputText = `[Line ${startLine} is ${firstLineSize}, exceeds ${formatSize(DEFAULT_MAX_BYTES)} limit. Use bash: sed -n '${startLine}p' ${params.path} | head -c ${DEFAULT_MAX_BYTES}]`;
|
|
116
|
+
details = { truncation };
|
|
117
|
+
outputLines = 0;
|
|
118
|
+
cacheable = false;
|
|
119
|
+
} else if (truncation.truncated) {
|
|
120
|
+
outputLines = truncation.outputLines;
|
|
121
|
+
const endLineDisplay = startLine + outputLines - 1;
|
|
122
|
+
const nextOffset = endLineDisplay + 1;
|
|
123
|
+
outputText = truncation.content;
|
|
124
|
+
outputText +=
|
|
125
|
+
truncation.truncatedBy === "lines"
|
|
126
|
+
? `\n\n[Showing lines ${startLine}-${endLineDisplay} of ${allLines.length}. Use offset=${nextOffset} to continue.]`
|
|
127
|
+
: `\n\n[Showing lines ${startLine}-${endLineDisplay} of ${allLines.length} (${formatSize(DEFAULT_MAX_BYTES)} limit). Use offset=${nextOffset} to continue.]`;
|
|
128
|
+
details = { truncation };
|
|
129
|
+
} else if (selectedEnd < allLines.length) {
|
|
130
|
+
outputText = `${truncation.content}\n\n[${allLines.length - selectedEnd} more lines in file. Use offset=${selectedEnd + 1} to continue.]`;
|
|
131
|
+
} else {
|
|
132
|
+
outputText = truncation.content;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const endLine = outputLines === 0 ? startLine : startLine + outputLines - 1;
|
|
136
|
+
const completeFile = startIndex === 0 && selectedEnd === allLines.length && !truncation.truncated;
|
|
137
|
+
const scopeKey = `${completeFile ? "full" : `r:${startLine}:${endLine}`}:n${params.lineNumbers ? 1 : 0}`;
|
|
138
|
+
const summary = completeFile ? `${allLines.length} lines` : `${outputLines} lines`;
|
|
139
|
+
return {
|
|
140
|
+
text: outputText,
|
|
141
|
+
details,
|
|
142
|
+
totalLines: allLines.length,
|
|
143
|
+
startLine,
|
|
144
|
+
endLine,
|
|
145
|
+
completeFile,
|
|
146
|
+
scopeKey,
|
|
147
|
+
summary,
|
|
148
|
+
cacheable,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function withMeta(baseline: BaselineTextResult, meta: ReadCacheMetaV1, text = baseline.text) {
|
|
153
|
+
return {
|
|
154
|
+
content: [{ type: "text" as const, text }],
|
|
155
|
+
details: { ...baseline.details, readCache: meta },
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function createMeta(
|
|
160
|
+
baseline: BaselineTextResult,
|
|
161
|
+
pathKey: string,
|
|
162
|
+
hash: string,
|
|
163
|
+
mode: ReadCacheMetaV1["mode"],
|
|
164
|
+
returnedText: string,
|
|
165
|
+
baseHash?: string,
|
|
166
|
+
summary = baseline.summary,
|
|
167
|
+
): ReadCacheMetaV1 {
|
|
168
|
+
return {
|
|
169
|
+
v: 1,
|
|
170
|
+
pathKey,
|
|
171
|
+
scopeKey: baseline.scopeKey,
|
|
172
|
+
servedHash: hash,
|
|
173
|
+
baseHash,
|
|
174
|
+
mode,
|
|
175
|
+
baselineTokens: estimateTokens(baseline.text),
|
|
176
|
+
returnedTokens: estimateTokens(returnedText),
|
|
177
|
+
totalLines: baseline.totalLines,
|
|
178
|
+
summary,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function countDiffLines(patch: string): { added: number; removed: number } {
|
|
183
|
+
let added = 0;
|
|
184
|
+
let removed = 0;
|
|
185
|
+
for (const line of patch.split("\n")) {
|
|
186
|
+
if (line.startsWith("+") && !line.startsWith("+++")) added += 1;
|
|
187
|
+
else if (line.startsWith("-") && !line.startsWith("---")) removed += 1;
|
|
188
|
+
}
|
|
189
|
+
return { added, removed };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function createExploreReadTool(
|
|
193
|
+
rowState: ToolRowStateStore,
|
|
194
|
+
cache: ReadCacheStore = createReadCacheStore(),
|
|
195
|
+
snapshots: ReadSnapshotStore = createReadSnapshotStore(),
|
|
196
|
+
): ReadDefinition {
|
|
71
197
|
const baseDefinition = readDefinitionForCwd(process.cwd());
|
|
72
198
|
return {
|
|
73
199
|
...baseDefinition,
|
|
@@ -81,7 +207,7 @@ export function createExploreReadTool(rowState: ToolRowStateStore): ReadDefiniti
|
|
|
81
207
|
) {
|
|
82
208
|
const definition = readDefinitionForCwd(ctx.cwd);
|
|
83
209
|
const normalized = normalizeReadParams(params);
|
|
84
|
-
const path = isAbsolute(normalized.path) ? normalized.path : resolve(ctx.cwd, normalized.path);
|
|
210
|
+
const path = isAbsolute(normalized.path) ? resolve(normalized.path) : resolve(ctx.cwd, normalized.path);
|
|
85
211
|
const buffer = await readFile(path);
|
|
86
212
|
if (isSupportedImage(buffer)) {
|
|
87
213
|
return definition.execute(
|
|
@@ -94,42 +220,49 @@ export function createExploreReadTool(rowState: ToolRowStateStore): ReadDefiniti
|
|
|
94
220
|
}
|
|
95
221
|
|
|
96
222
|
if (signal?.aborted) throw new Error("Operation aborted");
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
223
|
+
let text: string;
|
|
224
|
+
try {
|
|
225
|
+
text = new TextDecoder("utf-8", { fatal: true }).decode(buffer);
|
|
226
|
+
} catch {
|
|
227
|
+
return definition.execute(toolCallId, normalized, signal, onUpdate, ctx);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const baseline = baselineText(text, normalized);
|
|
231
|
+
if (!baseline.cacheable) {
|
|
232
|
+
return { content: [{ type: "text", text: baseline.text }], details: baseline.details };
|
|
102
233
|
}
|
|
234
|
+
const hash = createHash("sha256").update(buffer).digest("hex");
|
|
235
|
+
const decision = cache.decision(ctx, path, baseline.scopeKey);
|
|
236
|
+
let output = baseline.text;
|
|
237
|
+
let mode: ReadCacheMetaV1["mode"] = decision.recovery ? "recovery" : "baseline";
|
|
238
|
+
let summary = baseline.summary;
|
|
103
239
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
? `\n\n[Showing lines ${startLineDisplay}-${endLineDisplay} of ${allLines.length}. Use offset=${nextOffset} to continue.]`
|
|
124
|
-
: `\n\n[Showing lines ${startLineDisplay}-${endLineDisplay} of ${allLines.length} (${formatSize(DEFAULT_MAX_BYTES)} limit). Use offset=${nextOffset} to continue.]`;
|
|
125
|
-
details = { truncation };
|
|
126
|
-
} else if (endLine < allLines.length) {
|
|
127
|
-
outputText = `${truncation.content}\n\n[${allLines.length - endLine} more lines in file. Use offset=${endLine + 1} to continue.]`;
|
|
128
|
-
} else {
|
|
129
|
-
outputText = truncation.content;
|
|
240
|
+
if (!decision.recovery && decision.baseHash === hash) {
|
|
241
|
+
output = baseline.completeFile
|
|
242
|
+
? `unchanged, ${baseline.totalLines} lines`
|
|
243
|
+
: `unchanged, lines ${baseline.startLine}-${baseline.endLine} of ${baseline.totalLines}`;
|
|
244
|
+
mode = "unchanged";
|
|
245
|
+
summary = output;
|
|
246
|
+
} else if (!decision.recovery && decision.baseHash && baseline.completeFile && !normalized.lineNumbers) {
|
|
247
|
+
const baseText = snapshots.get(decision.baseHash);
|
|
248
|
+
if (baseText !== undefined) {
|
|
249
|
+
const patch = generateUnifiedPatch(normalized.path, baseText, text, 3);
|
|
250
|
+
const counts = countDiffLines(patch);
|
|
251
|
+
const candidate = `[read: ${counts.added} lines added, ${counts.removed} removed of ${baseline.totalLines}]\n${patch}`;
|
|
252
|
+
const candidateTruncation = truncateHead(candidate);
|
|
253
|
+
if (!candidateTruncation.truncated && estimateTokens(candidate) < estimateTokens(baseline.text)) {
|
|
254
|
+
output = candidate;
|
|
255
|
+
mode = "diff";
|
|
256
|
+
summary = `+${counts.added} -${counts.removed}`;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
130
259
|
}
|
|
131
260
|
|
|
132
|
-
|
|
261
|
+
if (signal?.aborted) throw new Error("Operation aborted");
|
|
262
|
+
snapshots.set(hash, text, buffer.byteLength);
|
|
263
|
+
const meta = createMeta(baseline, path, hash, mode, output, decision.baseHash, summary);
|
|
264
|
+
cache.record(ctx, meta);
|
|
265
|
+
return withMeta(baseline, meta, output);
|
|
133
266
|
},
|
|
134
267
|
renderCall(
|
|
135
268
|
args: Parameters<ReadRenderCall>[0],
|
|
@@ -138,6 +271,10 @@ export function createExploreReadTool(rowState: ToolRowStateStore): ReadDefiniti
|
|
|
138
271
|
) {
|
|
139
272
|
rowState.watch(context.toolCallId, context.invalidate);
|
|
140
273
|
const text = (context.lastComponent as Text | undefined) ?? new Text("", 0, 0);
|
|
274
|
+
if (context.executionStarted) {
|
|
275
|
+
text.setText("");
|
|
276
|
+
return text;
|
|
277
|
+
}
|
|
141
278
|
const title = formatToolRowTitle(rowState, context.toolCallId, "read", theme);
|
|
142
279
|
text.setText(`${title} ${theme.fg("muted", renderCallSummary(args))}`);
|
|
143
280
|
return text;
|
|
@@ -148,13 +285,32 @@ export function createExploreReadTool(rowState: ToolRowStateStore): ReadDefiniti
|
|
|
148
285
|
theme: Parameters<ReadRenderResult>[2],
|
|
149
286
|
context: Parameters<ReadRenderResult>[3],
|
|
150
287
|
) {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
288
|
+
rowState.watch(context.toolCallId, context.invalidate);
|
|
289
|
+
const container = new Container();
|
|
290
|
+
const title = formatToolRowTitle(rowState, context.toolCallId, "read", theme);
|
|
291
|
+
const args = context.args as ReadToolInput | undefined;
|
|
292
|
+
const details = result.details as ExploreReadDetails | undefined;
|
|
293
|
+
const summary = details?.readCache?.summary;
|
|
294
|
+
const summaryText = context.isError
|
|
295
|
+
? theme.fg("error", "error")
|
|
296
|
+
: summary
|
|
297
|
+
? theme.fg(
|
|
298
|
+
details?.readCache?.mode === "unchanged"
|
|
299
|
+
? "success"
|
|
300
|
+
: details?.readCache?.mode === "diff"
|
|
301
|
+
? "accent"
|
|
302
|
+
: "muted",
|
|
303
|
+
summary,
|
|
304
|
+
)
|
|
305
|
+
: "";
|
|
306
|
+
const header = `${title} ${theme.fg("muted", renderCallSummary(args))}${summaryText ? ` ${summaryText}` : ""}`;
|
|
307
|
+
container.addChild(new Text(header, 0, 0));
|
|
308
|
+
if (options.expanded) {
|
|
309
|
+
const definition = readDefinitionForCwd(context.cwd);
|
|
310
|
+
const body = definition.renderResult?.(result, { ...options, expanded: true }, theme, context);
|
|
311
|
+
if (body) container.addChild(body);
|
|
155
312
|
}
|
|
156
|
-
|
|
157
|
-
return definition.renderResult?.(result, { ...options, expanded: true }, theme, context) ?? new Text("", 0, 0);
|
|
313
|
+
return container;
|
|
158
314
|
},
|
|
159
315
|
};
|
|
160
316
|
}
|
|
@@ -7,7 +7,6 @@ Agent definitions can override the parent model and thinking level. If an overri
|
|
|
7
7
|
Tau includes three built-in agents:
|
|
8
8
|
|
|
9
9
|
- `scout` explores local files and code with `read`, `grep`, `find`, and `ls`.
|
|
10
|
-
- `context-maintenance` reconciles reusable repository context entries and requests approval before updates.
|
|
11
10
|
- `web-research` researches web and code sources with `websearch`, `codesearch`, and `webfetch`.
|
|
12
11
|
|
|
13
12
|
Ask Tau to delegate a task, or let it call `subagent` with an agent name and task. Children use the parent's current working directory and inherit its model and thinking level unless their definition overrides either value. They do not receive the parent conversation. Tau loads only the extensions that own a child's declared tools, so unrelated extension hooks do not run in child sessions. When a child must inspect another repository, put its exact absolute path in the delegated task.
|
|
@@ -126,10 +126,6 @@ async function loadScope(
|
|
|
126
126
|
const reason = error instanceof Error ? error.message : "directory unavailable";
|
|
127
127
|
return new Map([
|
|
128
128
|
["scout", [{ path: directory, name: "scout", reason: `packaged agents unavailable: ${reason}` }]],
|
|
129
|
-
[
|
|
130
|
-
"context-maintenance",
|
|
131
|
-
[{ path: directory, name: "context-maintenance", reason: `packaged agents unavailable: ${reason}` }],
|
|
132
|
-
],
|
|
133
129
|
[
|
|
134
130
|
"web-research",
|
|
135
131
|
[{ path: directory, name: "web-research", reason: `packaged agents unavailable: ${reason}` }],
|
|
@@ -28,11 +28,11 @@ Adds `/commit` for semantic commit grouping, review, and committing selected rep
|
|
|
28
28
|
|
|
29
29
|
## context
|
|
30
30
|
|
|
31
|
-
Adds `/context` to select reusable repository work scopes from `.pi/contexts`, and `/context-
|
|
31
|
+
Adds `/context` to select reusable repository work scopes from `.pi/contexts`, and `/context-sync` to reconcile affected scopes from current Git changes. Folder names are tabs, TOML files are concepts, and TOML sections are selectable entries.
|
|
32
32
|
|
|
33
33
|
## explore
|
|
34
34
|
|
|
35
|
-
Replaces Pi’s filesystem inspection tools with compact Tau versions: `ls`, `find`, `grep`, and `read`. They produce smaller model payloads and readable tool rows. `read`
|
|
35
|
+
Replaces Pi’s filesystem inspection tools with compact Tau versions: `ls`, `find`, `grep`, and `read`. They produce smaller model payloads and readable tool rows. Repeated `read` calls return unchanged markers or useful diffs when branch history proves the agent already saw the base content. A failed patch unlocks one normal reread of the affected path. `/read-stats` shows estimated token and cost savings for the current chat and whole session.
|
|
36
36
|
|
|
37
37
|
## footer
|
|
38
38
|
|
|
@@ -84,7 +84,7 @@ Adds `Alt+S` to stash the current prompt draft and `/pop` to browse stashed draf
|
|
|
84
84
|
|
|
85
85
|
## subagent
|
|
86
86
|
|
|
87
|
-
Gives Tau a subagent delegation tool for isolated, focused work. You can also create your own subagents in the supported subagent directories. Ask Tau how to do it and have it consult the extension’s own documentation; the built-in `scout
|
|
87
|
+
Gives Tau a subagent delegation tool for isolated, focused work. You can also create your own subagents in the supported subagent directories. Ask Tau how to do it and have it consult the extension’s own documentation; the built-in `scout` and `web-research` subagents show the pattern. Each subagent can register its own model and the tools it is allowed to use.
|
|
88
88
|
|
|
89
89
|
## tau-help
|
|
90
90
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shanepadgett/tau-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Tau is a custom agentic harness built with pi extensions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
"README.md"
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@shanepadgett/tau-tui": "0.
|
|
31
|
+
"@shanepadgett/tau-tui": "0.6.0",
|
|
32
32
|
"@toon-format/toon": "2.3.0",
|
|
33
|
-
"smol-toml": "1.
|
|
33
|
+
"smol-toml": "1.7.0"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"@earendil-works/pi-ai": "*",
|
package/shared/git.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ExtensionAPI,
|
|
1
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
|
|
3
3
|
const DEFAULT_GIT_TIMEOUT_MS = 10_000;
|
|
4
4
|
|
|
@@ -7,7 +7,7 @@ export interface GitRunner {
|
|
|
7
7
|
run(args: string[], options?: { cwd?: string; optional?: boolean; timeout?: number }): Promise<string>;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
export function createGitRunner(pi: ExtensionAPI, ctx:
|
|
10
|
+
export function createGitRunner(pi: ExtensionAPI, ctx: ExtensionContext): GitRunner {
|
|
11
11
|
return {
|
|
12
12
|
cwd: ctx.cwd,
|
|
13
13
|
async run(args, options = {}) {
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: context-maintenance
|
|
3
|
-
description: Reconcile repository context definitions after meaningful scope changes, with user approval before every update
|
|
4
|
-
tools:
|
|
5
|
-
- read
|
|
6
|
-
- grep
|
|
7
|
-
- find
|
|
8
|
-
- ls
|
|
9
|
-
- context_list
|
|
10
|
-
- context_get
|
|
11
|
-
- context_check
|
|
12
|
-
- context_audit
|
|
13
|
-
- context_changes
|
|
14
|
-
- context_review
|
|
15
|
-
model: openai-codex/gpt-5.6-luna
|
|
16
|
-
thinking: high
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
Maintain the repository context catalog. Research only the changed or requested scopes. Existing entries should stay small, reusable, and useful for future work. Prefer updating an existing entry over creating a near-duplicate. Do not create one entry per file.
|
|
20
|
-
|
|
21
|
-
Use context_changes when the task does not provide exact changed paths. Use context_list, context_get, context_check, and context_audit to understand the current catalog. Call context_review with concrete proposed operations. That tool owns user approval and applies only selected operations.
|
|
22
|
-
|
|
23
|
-
If context_review returns feedback, revise the affected proposal or batch and call context_review again. If it returns rejected, stop. Never claim an operation was applied unless context_review reports it applied. Finish with only a short applied/rejected summary.
|