@shanepadgett/tau-agent 0.33.0 → 0.34.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/context.md +29 -20
- package/docs/extending-tau-agent.md +63 -24
- package/extensions/appshot/index.ts +112 -111
- package/extensions/aside/README.md +7 -0
- package/extensions/aside/index.ts +168 -0
- package/extensions/aside/panel.ts +83 -0
- package/extensions/branch/index.ts +38 -21
- package/extensions/cache-diagnostics/index.ts +70 -58
- package/extensions/checkpoint/README.md +9 -0
- package/extensions/checkpoint/checkpoint-budget.ts +79 -0
- package/extensions/checkpoint/checkpoint.ts +277 -0
- package/extensions/checkpoint/index.ts +102 -0
- package/extensions/checkpoint/messages.ts +169 -0
- package/extensions/checkpoint/prompt.ts +24 -0
- package/extensions/checkpoint/settings.ts +27 -0
- package/extensions/commit/commit-effort.ts +3 -3
- package/extensions/context/README.md +12 -8
- package/extensions/context/definitions.ts +178 -55
- package/extensions/context/index.ts +192 -160
- package/extensions/context/panel.ts +57 -37
- package/extensions/context/sync.ts +139 -89
- package/extensions/context/validation.ts +37 -25
- package/extensions/effort/README.md +1 -1
- package/extensions/effort/index.ts +3 -3
- package/extensions/effort/state.ts +3 -18
- package/extensions/explore/guidance.ts +6 -47
- package/extensions/explore/index.ts +38 -53
- package/extensions/explore/{ast/read → read}/hook.ts +12 -7
- package/extensions/explore/{ast/tools → tools}/ast-search.ts +15 -19
- package/extensions/explore/{ast/tools → tools}/context.ts +16 -23
- package/extensions/explore/{ast/tools → tools}/deps.ts +11 -15
- package/extensions/explore/{ast/tools → tools}/discover.ts +15 -17
- package/extensions/explore/{ast/tools → tools}/impact.ts +20 -23
- package/extensions/explore/{ast/tools → tools}/outline.ts +21 -24
- package/extensions/explore/{ast/tools → tools}/relationships.ts +23 -32
- package/extensions/explore/{ast/tools → tools}/render.ts +1 -1
- package/extensions/explore/{ast/tools → tools}/reverse-deps.ts +11 -15
- package/extensions/explore/{ast/tools → tools}/show.ts +16 -19
- package/extensions/explore/tools/target-params.ts +9 -0
- package/extensions/footer/index.ts +75 -56
- package/extensions/handoff/index.ts +8 -30
- package/extensions/image-gen/index.ts +46 -40
- package/extensions/patch/executor.ts +190 -135
- package/extensions/patch/matcher.ts +82 -48
- package/extensions/patch/parser.ts +149 -105
- package/extensions/patch/render.ts +32 -39
- package/extensions/patch/summary.ts +30 -26
- package/extensions/ready/README.md +7 -0
- package/extensions/ready/index.ts +58 -0
- package/extensions/ready/model.ts +128 -0
- package/extensions/ready/packs.ts +272 -0
- package/extensions/ready/render.ts +445 -0
- package/extensions/ready/scan.ts +760 -0
- package/extensions/reference/panel.ts +28 -16
- package/extensions/review/index.ts +63 -51
- package/extensions/review/panel.ts +5 -34
- package/extensions/runtime-context/context.ts +30 -26
- package/extensions/script-runner/README.md +2 -2
- package/extensions/script-runner/index.ts +130 -93
- package/extensions/silent-command-runner/index.ts +38 -23
- package/extensions/subagent/agents/context-sync.md +185 -33
- package/extensions/subagent/agents/scout.md +2 -2
- package/extensions/subagent/agents.ts +137 -84
- package/extensions/subagent/cmux-dashboard.ts +39 -30
- package/extensions/subagent/index.ts +79 -59
- package/extensions/subagent/resume.ts +1 -1
- package/extensions/subagent/run.ts +304 -193
- package/extensions/subagent/runtime.ts +781 -343
- package/extensions/tau/index.ts +47 -38
- package/extensions/tau-help/help.md +14 -6
- package/extensions/tool-loader/README.md +4 -2
- package/extensions/tool-loader/index.ts +77 -52
- package/extensions/web/index.ts +6 -3
- package/extensions/web/webfetch.ts +114 -86
- package/package.json +3 -3
- package/schemas/tau.schema.json +20 -33
- package/shared/bounded-text-result.ts +1 -0
- package/shared/checkpoint-visibility.ts +9 -0
- package/shared/events.ts +28 -10
- package/shared/isolated-session.ts +71 -29
- package/shared/model-effort.ts +9 -19
- package/shared/model-fallback/index.ts +66 -41
- package/shared/settings/load.ts +45 -38
- package/shared/temporary-output-store.ts +16 -10
- package/{extensions/explore → src}/ast/adapter.ts +11 -2
- package/{extensions/explore → src}/ast/engine.ts +54 -38
- package/{extensions/explore → src}/ast/format/ast-search.ts +15 -6
- package/{extensions/explore → src}/ast/format/context.ts +3 -6
- package/src/ast/format/deps.ts +94 -0
- package/{extensions/explore → src}/ast/format/discover.ts +2 -2
- package/{extensions/explore → src}/ast/format/impact.ts +9 -11
- package/{extensions/explore → src}/ast/format/outline.ts +2 -2
- package/{extensions/explore → src}/ast/format/relationships.ts +11 -7
- package/{extensions/explore → src}/ast/format/show.ts +1 -1
- package/src/ast/format/targeted-result.ts +20 -0
- package/src/ast/grammars/c_sharp.wasm +0 -0
- package/{extensions/explore → src}/ast/grammars/manifest.json +9 -12
- package/{extensions/explore → src}/ast/grammars/manifest.ts +0 -4
- package/{extensions/explore → src}/ast/graph/file-graph.ts +210 -98
- package/{extensions/explore → src}/ast/graph/relationships.ts +285 -72
- package/{extensions/explore → src}/ast/ir.ts +6 -0
- package/src/ast/languages/csharp-file-deps.ts +90 -0
- package/{extensions/explore → src}/ast/languages/csharp.ts +9 -40
- package/src/ast/languages/dotted-index.ts +131 -0
- package/{extensions/explore → src}/ast/languages/file-dep-util.ts +22 -13
- package/src/ast/languages/fixtures/sample.cs +53 -0
- package/src/ast/languages/fixtures/sample.cs.expected.json +156 -0
- package/src/ast/languages/fixtures/sample.go +37 -0
- package/src/ast/languages/fixtures/sample.go.expected.json +96 -0
- package/src/ast/languages/fixtures/sample.java +40 -0
- package/src/ast/languages/fixtures/sample.java.expected.json +126 -0
- package/src/ast/languages/fixtures/sample.kt +31 -0
- package/src/ast/languages/fixtures/sample.kt.expected.json +134 -0
- package/src/ast/languages/fixtures/sample.md +23 -0
- package/src/ast/languages/fixtures/sample.md.expected.json +44 -0
- package/src/ast/languages/fixtures/sample.rs +54 -0
- package/src/ast/languages/fixtures/sample.rs.expected.json +178 -0
- package/src/ast/languages/fixtures/sample.swift +45 -0
- package/src/ast/languages/fixtures/sample.swift.expected.json +147 -0
- package/src/ast/languages/fixtures/sample.ts +45 -0
- package/src/ast/languages/fixtures/sample.ts.expected.json +141 -0
- package/src/ast/languages/fixtures/sample.tsx +36 -0
- package/src/ast/languages/fixtures/sample.tsx.expected.json +59 -0
- package/{extensions/explore → src}/ast/languages/go-file-deps.ts +6 -0
- package/{extensions/explore → src}/ast/languages/go.ts +21 -3
- package/{extensions/explore → src}/ast/languages/java.ts +5 -38
- package/src/ast/languages/jvm-file-deps.ts +135 -0
- package/{extensions/explore → src}/ast/languages/kotlin.ts +5 -39
- package/{extensions/explore → src}/ast/languages/rust-file-deps.ts +3 -3
- package/{extensions/explore → src}/ast/languages/rust.ts +21 -13
- package/{extensions/explore → src}/ast/languages/swift-file-deps.ts +22 -15
- package/{extensions/explore → src}/ast/languages/swift.ts +9 -39
- package/{extensions/explore → src}/ast/languages/tree.ts +47 -0
- package/{extensions/explore → src}/ast/languages/typescript-file-deps.ts +108 -0
- package/{extensions/explore → src}/ast/languages/typescript-package-surface.ts +4 -3
- package/{extensions/explore → src}/ast/languages/typescript.ts +3 -5
- package/{extensions/explore → src}/ast/markdown.ts +72 -47
- package/{extensions/explore → src}/ast/queries/ast-search.ts +164 -146
- package/{extensions/explore → src}/ast/queries/composite-target.ts +1 -1
- package/{extensions/explore → src}/ast/queries/context.ts +116 -80
- package/{extensions/explore → src}/ast/queries/discover.ts +154 -105
- package/src/ast/queries/impact.ts +291 -0
- package/{extensions/explore → src}/ast/queries/outline.ts +4 -2
- package/{extensions/explore → src}/ast/queries/show.ts +46 -42
- package/{extensions/explore → src}/ast/query.ts +1 -1
- package/{extensions/explore → src}/ast/registry.ts +1 -2
- package/{extensions/explore → src}/ast/scan.ts +1 -1
- package/src/ast/session.ts +50 -0
- package/{extensions/explore → src}/ast/slice.ts +8 -0
- package/{extensions/explore → src/ast}/traverse.ts +32 -20
- package/src/file-injection/index.ts +547 -0
- package/src/image-generation/client.ts +50 -31
- package/src/image-generation/index.ts +177 -134
- package/src/index.ts +2 -0
- package/src/tool-loading/index.ts +58 -0
- package/extensions/context/evidence.ts +0 -517
- package/extensions/context/projection.ts +0 -141
- package/extensions/context/state.ts +0 -30
- package/extensions/context/write-scope.ts +0 -109
- package/extensions/explore/ast/format/deps.ts +0 -86
- package/extensions/explore/ast/grammars/odin.wasm +0 -0
- package/extensions/explore/ast/languages/csharp-file-deps.ts +0 -189
- package/extensions/explore/ast/languages/fixtures/sample.cs +0 -17
- package/extensions/explore/ast/languages/fixtures/sample.java +0 -21
- package/extensions/explore/ast/languages/fixtures/sample.kt +0 -15
- package/extensions/explore/ast/languages/fixtures/sample.odin +0 -28
- package/extensions/explore/ast/languages/fixtures/sample.rs +0 -31
- package/extensions/explore/ast/languages/fixtures/sample.swift +0 -25
- package/extensions/explore/ast/languages/jvm-file-deps.ts +0 -217
- package/extensions/explore/ast/languages/odin-file-deps.ts +0 -175
- package/extensions/explore/ast/languages/odin.ts +0 -407
- package/extensions/explore/ast/queries/impact.ts +0 -250
- package/extensions/explore/outline-injection.ts +0 -151
- package/extensions/explore/read/autoread.ts +0 -118
- package/extensions/working-memory/README.md +0 -17
- package/extensions/working-memory/checkpoint.ts +0 -242
- package/extensions/working-memory/index.ts +0 -341
- package/extensions/working-memory/memory.ts +0 -235
- package/extensions/working-memory/render.ts +0 -185
- package/extensions/working-memory/settings.ts +0 -38
- package/extensions/working-memory/state.ts +0 -158
- package/shared/autoread.ts +0 -234
- package/shared/context-messages.ts +0 -19
- package/shared/outline-injection.ts +0 -56
- /package/{extensions/explore → src}/ast/format/composite.ts +0 -0
- /package/{extensions/explore → src}/ast/grammars/kotlin.wasm +0 -0
- /package/{extensions/explore → src}/ast/grammars/swift.wasm +0 -0
- /package/{extensions/explore → src}/ast/identity.ts +0 -0
- /package/{extensions/explore → src}/ast/package-surface.ts +0 -0
- /package/{extensions/explore/ast/read/policy.ts → src/ast/read-policy.ts} +0 -0
|
@@ -257,6 +257,25 @@ class ReferencePanel implements Component {
|
|
|
257
257
|
];
|
|
258
258
|
}
|
|
259
259
|
|
|
260
|
+
private branchSuffix(branch: { isCurrent: boolean; updatedAt?: number }): string {
|
|
261
|
+
if (branch.isCurrent) return "current";
|
|
262
|
+
if (branch.updatedAt === undefined) return "remote";
|
|
263
|
+
return formatAge(branch.updatedAt);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
private renderBranchRow(
|
|
267
|
+
branch: { name: string; isCurrent: boolean; updatedAt?: number },
|
|
268
|
+
active: boolean,
|
|
269
|
+
renderWidth: number,
|
|
270
|
+
): string {
|
|
271
|
+
const pointer = active ? this.theme.fg("accent", "› ") : " ";
|
|
272
|
+
const labelText = `${branch.name} ${this.branchSuffix(branch)}`;
|
|
273
|
+
const label = active
|
|
274
|
+
? this.theme.fg("accent", labelText)
|
|
275
|
+
: this.theme.fg(branch.isCurrent ? "accent" : "text", labelText);
|
|
276
|
+
return truncateToWidth(`${pointer}${label}`, renderWidth, "");
|
|
277
|
+
}
|
|
278
|
+
|
|
260
279
|
private renderBranchPicker(width: number): string[] {
|
|
261
280
|
const picker = this.branchPicker;
|
|
262
281
|
if (!picker) return [];
|
|
@@ -279,18 +298,7 @@ class ReferencePanel implements Component {
|
|
|
279
298
|
for (let index = window.start; index < window.end; index++) {
|
|
280
299
|
const branch = picker.branches[index];
|
|
281
300
|
if (!branch) continue;
|
|
282
|
-
|
|
283
|
-
const pointer = active ? this.theme.fg("accent", "› ") : " ";
|
|
284
|
-
const suffix = branch.isCurrent
|
|
285
|
-
? "current"
|
|
286
|
-
: branch.updatedAt === undefined
|
|
287
|
-
? "remote"
|
|
288
|
-
: formatAge(branch.updatedAt);
|
|
289
|
-
const labelText = `${branch.name} ${suffix}`;
|
|
290
|
-
const label = active
|
|
291
|
-
? this.theme.fg("accent", labelText)
|
|
292
|
-
: this.theme.fg(branch.isCurrent ? "accent" : "text", labelText);
|
|
293
|
-
lines.push(truncateToWidth(`${pointer}${label}`, renderWidth, ""));
|
|
301
|
+
lines.push(this.renderBranchRow(branch, index === picker.cursor, renderWidth));
|
|
294
302
|
}
|
|
295
303
|
if (window.start > 0 || window.end < picker.branches.length) {
|
|
296
304
|
lines.push(
|
|
@@ -303,6 +311,12 @@ class ReferencePanel implements Component {
|
|
|
303
311
|
return lines;
|
|
304
312
|
}
|
|
305
313
|
|
|
314
|
+
private referenceStatusMark(state: NonNullable<ReferenceListItem["state"]>): string {
|
|
315
|
+
if (state === "failed") return this.theme.fg("error", "!");
|
|
316
|
+
if (state === "updated") return this.theme.fg("success", "✓");
|
|
317
|
+
return this.theme.fg("muted", "…");
|
|
318
|
+
}
|
|
319
|
+
|
|
306
320
|
private renderReferenceRow(item: ReferenceListItem, active: boolean, width: number): string[] {
|
|
307
321
|
const separator = item.displayName.lastIndexOf("/");
|
|
308
322
|
const repository = separator < 0 ? item.displayName : item.displayName.slice(separator + 1);
|
|
@@ -310,9 +324,7 @@ class ReferencePanel implements Component {
|
|
|
310
324
|
const name = item.dirty ? `${repository} *` : repository;
|
|
311
325
|
const label = active ? this.theme.fg("accent", name) : this.theme.fg(item.dirty ? "warning" : "text", name);
|
|
312
326
|
const branch = item.branch ? this.theme.fg("muted", ` (${item.branch})`) : "";
|
|
313
|
-
const status = item.state
|
|
314
|
-
? ` ${this.theme.fg(item.state === "failed" ? "error" : item.state === "updated" ? "success" : "muted", item.state === "failed" ? "!" : item.state === "updated" ? "✓" : "…")}`
|
|
315
|
-
: "";
|
|
327
|
+
const status = item.state ? ` ${this.referenceStatusMark(item.state)}` : "";
|
|
316
328
|
return [truncateToWidth(`${label}${owner}${branch}${status}`, width, "")];
|
|
317
329
|
}
|
|
318
330
|
|
|
@@ -871,7 +883,7 @@ function referenceNameFromUrl(url: string): string {
|
|
|
871
883
|
return name;
|
|
872
884
|
}
|
|
873
885
|
|
|
874
|
-
|
|
886
|
+
function referenceDisplayName(remoteUrl: string, fallback: string): string {
|
|
875
887
|
const remote = remoteUrl
|
|
876
888
|
.trim()
|
|
877
889
|
.replace(/[?#].*$/, "")
|
|
@@ -26,30 +26,11 @@ export default function reviewExtension(pi: ExtensionAPI): void {
|
|
|
26
26
|
await ctx.waitForIdle();
|
|
27
27
|
const requested = args.trim().toLowerCase();
|
|
28
28
|
if (requested === "show") {
|
|
29
|
-
|
|
30
|
-
if (!latest) {
|
|
31
|
-
ctx.ui.notify("No review exists on this session branch", "warning");
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
await showReview(pi, ctx, latest);
|
|
29
|
+
await showLatestReview(pi, ctx);
|
|
35
30
|
return;
|
|
36
31
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (requested) {
|
|
40
|
-
if (!isReviewMode(requested)) {
|
|
41
|
-
ctx.ui.notify("Usage: /review [simplify|architecture|correctness|show]", "warning");
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
mode = requested;
|
|
45
|
-
} else {
|
|
46
|
-
const selected = await ctx.ui.select("Review mode", ["Simplify", "Architecture", "Correctness"]);
|
|
47
|
-
if (!selected) return;
|
|
48
|
-
const normalized = selected.toLowerCase();
|
|
49
|
-
if (!isReviewMode(normalized)) return;
|
|
50
|
-
mode = normalized;
|
|
51
|
-
}
|
|
52
|
-
|
|
32
|
+
const mode = await resolveReviewMode(requested, ctx);
|
|
33
|
+
if (!mode) return;
|
|
53
34
|
const status = await loadRepoStatus(createGitRunner(pi, ctx));
|
|
54
35
|
if (!status) {
|
|
55
36
|
ctx.ui.notify("/review requires a Git repository", "warning");
|
|
@@ -59,41 +40,15 @@ export default function reviewExtension(pi: ExtensionAPI): void {
|
|
|
59
40
|
ctx.ui.notify("Nothing to review: working tree is clean", "info");
|
|
60
41
|
return;
|
|
61
42
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const signal = ctx.signal ? AbortSignal.any([controller.signal, ctx.signal]) : controller.signal;
|
|
65
|
-
let failure: string | undefined;
|
|
66
|
-
const output = await ctx.ui.custom<ReviewRecord | undefined>((tui, theme, keys, done) => {
|
|
67
|
-
const panel = new ReviewProgressPanel(tui, theme, mode, keys, () => controller.abort());
|
|
68
|
-
void runReview({
|
|
69
|
-
ctx,
|
|
70
|
-
root: status.root,
|
|
71
|
-
mode,
|
|
72
|
-
parentThinkingLevel: pi.getThinkingLevel(),
|
|
73
|
-
signal,
|
|
74
|
-
onProgress: (line) => panel.update(line),
|
|
75
|
-
})
|
|
76
|
-
.then((result) => done({ ...result, mode, root: status.root, createdAt: new Date().toISOString() }))
|
|
77
|
-
.catch((error: unknown) => {
|
|
78
|
-
failure = errorText(error);
|
|
79
|
-
done(undefined);
|
|
80
|
-
});
|
|
81
|
-
return panel;
|
|
82
|
-
});
|
|
83
|
-
if (!output) {
|
|
84
|
-
ctx.ui.notify(
|
|
85
|
-
signal.aborted ? "Review cancelled" : `Review failed: ${failure ?? "unknown error"}`,
|
|
86
|
-
signal.aborted ? "info" : "error",
|
|
87
|
-
);
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
43
|
+
const output = await runReviewWithPanel(pi, ctx, mode, status.root);
|
|
44
|
+
if (!output) return;
|
|
90
45
|
pi.appendEntry(REVIEW_ENTRY_TYPE, output);
|
|
91
46
|
await showReview(pi, ctx, output);
|
|
92
47
|
},
|
|
93
48
|
});
|
|
94
49
|
}
|
|
95
50
|
|
|
96
|
-
|
|
51
|
+
function latestReview(entries: readonly SessionEntry[]): ReviewRecord | undefined {
|
|
97
52
|
for (let index = entries.length - 1; index >= 0; index -= 1) {
|
|
98
53
|
const entry = entries[index];
|
|
99
54
|
if (entry?.type !== "custom" || entry.customType !== REVIEW_ENTRY_TYPE) continue;
|
|
@@ -102,6 +57,63 @@ export function latestReview(entries: readonly SessionEntry[]): ReviewRecord | u
|
|
|
102
57
|
return undefined;
|
|
103
58
|
}
|
|
104
59
|
|
|
60
|
+
async function showLatestReview(pi: ExtensionAPI, ctx: ExtensionContext): Promise<void> {
|
|
61
|
+
const latest = latestReview(ctx.sessionManager.getBranch());
|
|
62
|
+
if (!latest) {
|
|
63
|
+
ctx.ui.notify("No review exists on this session branch", "warning");
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
await showReview(pi, ctx, latest);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function resolveReviewMode(requested: string, ctx: ExtensionContext): Promise<ReviewMode | undefined> {
|
|
70
|
+
if (requested) {
|
|
71
|
+
if (!isReviewMode(requested)) {
|
|
72
|
+
ctx.ui.notify("Usage: /review [simplify|architecture|correctness|show]", "warning");
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
return requested;
|
|
76
|
+
}
|
|
77
|
+
const selected = await ctx.ui.select("Review mode", ["Simplify", "Architecture", "Correctness"]);
|
|
78
|
+
if (!selected) return undefined;
|
|
79
|
+
const normalized = selected.toLowerCase();
|
|
80
|
+
return isReviewMode(normalized) ? normalized : undefined;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function runReviewWithPanel(
|
|
84
|
+
pi: ExtensionAPI,
|
|
85
|
+
ctx: ExtensionContext,
|
|
86
|
+
mode: ReviewMode,
|
|
87
|
+
root: string,
|
|
88
|
+
): Promise<ReviewRecord | undefined> {
|
|
89
|
+
const controller = new AbortController();
|
|
90
|
+
const signal = ctx.signal ? AbortSignal.any([controller.signal, ctx.signal]) : controller.signal;
|
|
91
|
+
let failure: string | undefined;
|
|
92
|
+
const output = await ctx.ui.custom<ReviewRecord | undefined>((tui, theme, keys, done) => {
|
|
93
|
+
const panel = new ReviewProgressPanel(tui, theme, mode, keys, () => controller.abort());
|
|
94
|
+
void runReview({
|
|
95
|
+
ctx,
|
|
96
|
+
root,
|
|
97
|
+
mode,
|
|
98
|
+
parentThinkingLevel: pi.getThinkingLevel(),
|
|
99
|
+
signal,
|
|
100
|
+
onProgress: (line) => panel.update(line),
|
|
101
|
+
})
|
|
102
|
+
.then((result) => done({ ...result, mode, root, createdAt: new Date().toISOString() }))
|
|
103
|
+
.catch((error: unknown) => {
|
|
104
|
+
failure = errorText(error);
|
|
105
|
+
done(undefined);
|
|
106
|
+
});
|
|
107
|
+
return panel;
|
|
108
|
+
});
|
|
109
|
+
if (output) return output;
|
|
110
|
+
ctx.ui.notify(
|
|
111
|
+
signal.aborted ? "Review cancelled" : `Review failed: ${failure ?? "unknown error"}`,
|
|
112
|
+
signal.aborted ? "info" : "error",
|
|
113
|
+
);
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
|
|
105
117
|
async function showReview(pi: ExtensionAPI, ctx: ExtensionContext, review: ReviewRecord): Promise<void> {
|
|
106
118
|
const action = await ctx.ui.custom<ReviewResultAction>(
|
|
107
119
|
(tui, theme, keys, done) => new ReviewResultPanel(tui, theme, keys, review, done),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { bindingHint, bindingsHint, rawHint, ToolPanel } from "@shanepadgett/tau-tui";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { bindingHint, bindingsHint, rawHint, ScrollableMarkdown, ToolPanel } from "@shanepadgett/tau-tui";
|
|
2
|
+
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { type Component, type KeybindingsManager, truncateToWidth, type TUI } from "@earendil-works/pi-tui";
|
|
4
4
|
import { formatReviewMarkdown, reviewModeLabel, type ReviewMode, type ReviewRecord } from "./model.ts";
|
|
5
5
|
|
|
6
6
|
export class ReviewProgressPanel implements Component {
|
|
@@ -53,7 +53,7 @@ export type ReviewResultAction = "send" | "export" | "close";
|
|
|
53
53
|
|
|
54
54
|
export class ReviewResultPanel implements Component {
|
|
55
55
|
private readonly panel: ToolPanel;
|
|
56
|
-
private readonly body:
|
|
56
|
+
private readonly body: ScrollableMarkdown;
|
|
57
57
|
|
|
58
58
|
constructor(
|
|
59
59
|
tui: TUI,
|
|
@@ -62,7 +62,7 @@ export class ReviewResultPanel implements Component {
|
|
|
62
62
|
review: ReviewRecord,
|
|
63
63
|
done: (action: ReviewResultAction) => void,
|
|
64
64
|
) {
|
|
65
|
-
this.body = new
|
|
65
|
+
this.body = new ScrollableMarkdown(tui, formatReviewMarkdown(review), 9);
|
|
66
66
|
this.panel = new ToolPanel(theme, {
|
|
67
67
|
title: `${reviewModeLabel(review.mode)} review · ${review.verdict}`,
|
|
68
68
|
secondary: `${review.findings.length} finding${review.findings.length === 1 ? "" : "s"} · not in parent context`,
|
|
@@ -97,32 +97,3 @@ export class ReviewResultPanel implements Component {
|
|
|
97
97
|
this.panel.invalidate();
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
|
-
|
|
101
|
-
class ReviewMarkdownBody implements Component {
|
|
102
|
-
private readonly tui: TUI;
|
|
103
|
-
private readonly markdown: Markdown;
|
|
104
|
-
private offset = 0;
|
|
105
|
-
private maxOffset = 0;
|
|
106
|
-
|
|
107
|
-
constructor(tui: TUI, content: string) {
|
|
108
|
-
this.tui = tui;
|
|
109
|
-
this.markdown = new Markdown(content, 0, 0, getMarkdownTheme());
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
scroll(delta: number): void {
|
|
113
|
-
this.offset = Math.max(0, Math.min(this.maxOffset, this.offset + delta));
|
|
114
|
-
this.tui.requestRender();
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
render(width: number): string[] {
|
|
118
|
-
const lines = this.markdown.render(width);
|
|
119
|
-
const height = Math.max(4, Math.floor(this.tui.terminal.rows * 0.9) - 9);
|
|
120
|
-
this.maxOffset = Math.max(0, lines.length - height);
|
|
121
|
-
this.offset = Math.min(this.offset, this.maxOffset);
|
|
122
|
-
return lines.slice(this.offset, this.offset + height);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
invalidate(): void {
|
|
126
|
-
this.markdown.invalidate();
|
|
127
|
-
}
|
|
128
|
-
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
|
-
import { createHash } from "node:crypto";
|
|
3
2
|
import { type Dirent, readdirSync } from "node:fs";
|
|
4
3
|
import { join, resolve } from "node:path";
|
|
5
4
|
|
|
@@ -19,13 +18,6 @@ export function freezeRuntimeContext(cwd: string): RuntimeContext {
|
|
|
19
18
|
return { cwd: cwd.replace(/\\/g, "/"), rootSnapshot: listRootSnapshot(cwd) };
|
|
20
19
|
}
|
|
21
20
|
|
|
22
|
-
export function formatLocalDateKey(date: Date): string {
|
|
23
|
-
const year = date.getFullYear();
|
|
24
|
-
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
25
|
-
const day = String(date.getDate()).padStart(2, "0");
|
|
26
|
-
return `${year}-${month}-${day}`;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
21
|
export function formatLocalDisplayDate(date: Date): string {
|
|
30
22
|
const months = [
|
|
31
23
|
"January",
|
|
@@ -44,12 +36,6 @@ export function formatLocalDisplayDate(date: Date): string {
|
|
|
44
36
|
return `${date.getDate()} ${months[date.getMonth()]} ${date.getFullYear()}`;
|
|
45
37
|
}
|
|
46
38
|
|
|
47
|
-
export function fingerprintRuntimeSnapshot(context: RuntimeContext): string {
|
|
48
|
-
return createHash("sha256")
|
|
49
|
-
.update(JSON.stringify({ version: 1, cwd: context.cwd, paths: context.rootSnapshot }))
|
|
50
|
-
.digest("hex");
|
|
51
|
-
}
|
|
52
|
-
|
|
53
39
|
export function formatRuntimeContextMessage(displayDate: string, rootSnapshot: readonly string[] | undefined): string {
|
|
54
40
|
const blocks = [`Current local date: ${displayDate}`];
|
|
55
41
|
if (rootSnapshot?.length) {
|
|
@@ -58,42 +44,60 @@ export function formatRuntimeContextMessage(displayDate: string, rootSnapshot: r
|
|
|
58
44
|
return blocks.join("\n");
|
|
59
45
|
}
|
|
60
46
|
|
|
61
|
-
function
|
|
62
|
-
const root = resolve(cwd);
|
|
47
|
+
function visibleRootEntries(root: string): SnapshotEntry[] {
|
|
63
48
|
const rootEntries = listSnapshotEntries(root, ".");
|
|
64
49
|
const ignoredRootPaths = gitIgnoredPaths(
|
|
65
50
|
root,
|
|
66
51
|
rootEntries.map((entry) => entry.path),
|
|
67
52
|
);
|
|
68
|
-
|
|
53
|
+
return rootEntries.filter(
|
|
69
54
|
(entry) => !isAlwaysHiddenFromSnapshot(entry.dirent.name) && !ignoredRootPaths.has(entry.path),
|
|
70
55
|
);
|
|
71
|
-
|
|
72
|
-
const childPaths: string[] = [];
|
|
56
|
+
}
|
|
73
57
|
|
|
74
|
-
|
|
58
|
+
function childEntriesByParent(
|
|
59
|
+
root: string,
|
|
60
|
+
roots: readonly SnapshotEntry[],
|
|
61
|
+
): {
|
|
62
|
+
byParent: Map<string, SnapshotEntry[]>;
|
|
63
|
+
ignoredChildPaths: Set<string>;
|
|
64
|
+
} {
|
|
65
|
+
const byParent = new Map<string, SnapshotEntry[]>();
|
|
66
|
+
const childPaths: string[] = [];
|
|
67
|
+
for (const entry of roots) {
|
|
75
68
|
if (!entry.dirent.isDirectory()) continue;
|
|
76
69
|
const childEntries = listSnapshotEntries(root, entry.path).filter(
|
|
77
70
|
(childEntry) => !isAlwaysHiddenFromSnapshot(childEntry.dirent.name),
|
|
78
71
|
);
|
|
79
|
-
|
|
72
|
+
byParent.set(entry.path, childEntries);
|
|
80
73
|
childPaths.push(...childEntries.map((childEntry) => childEntry.path));
|
|
81
74
|
}
|
|
75
|
+
return { byParent, ignoredChildPaths: gitIgnoredPaths(root, childPaths) };
|
|
76
|
+
}
|
|
82
77
|
|
|
83
|
-
|
|
78
|
+
function collectSnapshotPaths(
|
|
79
|
+
roots: readonly SnapshotEntry[],
|
|
80
|
+
byParent: Map<string, SnapshotEntry[]>,
|
|
81
|
+
ignoredChildPaths: ReadonlySet<string>,
|
|
82
|
+
): string[] {
|
|
84
83
|
const paths: string[] = [];
|
|
85
|
-
|
|
86
|
-
for (const entry of visibleRootEntries) {
|
|
84
|
+
for (const entry of roots) {
|
|
87
85
|
pushSnapshotPath(paths, entry);
|
|
88
86
|
if (paths.length >= ROOT_SNAPSHOT_MAX_PATHS) break;
|
|
89
|
-
|
|
90
|
-
for (const childEntry of childEntriesByParent.get(entry.path) ?? []) {
|
|
87
|
+
for (const childEntry of byParent.get(entry.path) ?? []) {
|
|
91
88
|
if (ignoredChildPaths.has(childEntry.path)) continue;
|
|
92
89
|
pushSnapshotPath(paths, childEntry);
|
|
93
90
|
if (paths.length >= ROOT_SNAPSHOT_MAX_PATHS) break;
|
|
94
91
|
}
|
|
95
92
|
}
|
|
93
|
+
return paths;
|
|
94
|
+
}
|
|
96
95
|
|
|
96
|
+
function listRootSnapshot(cwd: string): string[] {
|
|
97
|
+
const root = resolve(cwd);
|
|
98
|
+
const roots = visibleRootEntries(root);
|
|
99
|
+
const children = childEntriesByParent(root, roots);
|
|
100
|
+
const paths = collectSnapshotPaths(roots, children.byParent, children.ignoredChildPaths);
|
|
97
101
|
return paths.length === ROOT_SNAPSHOT_MAX_PATHS ? [...paths, "..."] : paths;
|
|
98
102
|
}
|
|
99
103
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Script Runner
|
|
2
2
|
|
|
3
|
-
Gives the agent a first-class `script_runner` tool for running Python and
|
|
3
|
+
Gives the agent a first-class `script_runner` tool for running Python 3, Node.js, and Deno scripts instead of falling back to bash. The agent picks whichever runtime is more efficient for the task.
|
|
4
4
|
|
|
5
5
|
When a run fails, the tool keeps the script and returns a `scriptId`. The agent retries with targeted `{oldText, newText}` edits against what it just wrote instead of resending the whole script, saving output tokens and keeping duplicate scripts out of context. Only the source the agent already sent is referenced; no file path is exposed.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Runtimes are detected from the environment: Python 3 via `python3`, Node via the current process when Node is 22.6 or newer (`node --experimental-strip-types`), Deno via `deno` (`deno run -A`). The `node` language is the local Node.js runtime with full Node APIs; scripts may be TypeScript with erasable syntax or plain JavaScript. The `deno` language is the local Deno runtime with full permissions and native TypeScript/JavaScript. The tool registers only the runtimes actually available and is hidden from the prompt entirely when none are present.
|