@shanepadgett/tau-agent 0.5.1 → 0.7.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/auto-name/index.ts +1 -1
- package/extensions/commit/commit-plan.ts +12 -5
- package/extensions/context/README.md +1 -1
- package/extensions/context/definitions.ts +2 -142
- package/extensions/context/index.ts +67 -387
- package/extensions/context/panel.ts +43 -108
- package/extensions/context/sync.ts +693 -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/image-gen/README.md +4 -4
- package/extensions/image-gen/client.ts +126 -107
- package/extensions/image-gen/index.ts +44 -35
- package/extensions/subagent/README.md +0 -1
- package/extensions/subagent/agents.ts +0 -4
- package/extensions/tau-help/help.md +7 -3
- package/extensions/xai/README.md +7 -0
- package/extensions/xai/auth.ts +40 -0
- package/extensions/xai/constants.ts +11 -0
- package/extensions/xai/index.ts +38 -0
- package/extensions/xai/oauth.ts +342 -0
- package/extensions/xai/payload.ts +68 -0
- package/package.json +2 -2
- package/shared/git.ts +2 -2
- package/shared/model-fallback/index.ts +17 -12
- package/extensions/subagent/agents/context-maintenance.md +0 -23
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { type Component, getKeybindings, Key, matchesKey, truncateToWidth, type TUI } from "@earendil-works/pi-tui";
|
|
3
3
|
import { bindingHint, rawHint, SelectableList, Tabs, ToolPanel, type ToolPanelConfig } from "@shanepadgett/tau-tui";
|
|
4
|
-
import type { ContextEntry
|
|
4
|
+
import type { ContextEntry } from "./definitions.ts";
|
|
5
5
|
|
|
6
6
|
type SelectableEntry = ContextEntry & { id: string };
|
|
7
7
|
|
|
@@ -15,6 +15,7 @@ export class ContextPanel implements Component {
|
|
|
15
15
|
private readonly selected = new Map<string, readonly SelectableEntry[]>();
|
|
16
16
|
private readonly done: (result: ContextEntry[] | undefined) => void;
|
|
17
17
|
private current: ContextEntry;
|
|
18
|
+
private fileOffset = 0;
|
|
18
19
|
|
|
19
20
|
constructor(
|
|
20
21
|
tui: TUI,
|
|
@@ -72,6 +73,17 @@ export class ContextPanel implements Component {
|
|
|
72
73
|
const list = this.activeList();
|
|
73
74
|
if (!list?.isFilterFocused()) {
|
|
74
75
|
const keys = getKeybindings();
|
|
76
|
+
const pageSize = this.filePageSize();
|
|
77
|
+
if (this.current.files.length > pageSize && matchesKey(data, Key.alt("up"))) {
|
|
78
|
+
this.fileOffset = Math.max(0, this.fileOffset - pageSize);
|
|
79
|
+
this.sync();
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (this.current.files.length > pageSize && matchesKey(data, Key.alt("down"))) {
|
|
83
|
+
this.fileOffset = Math.min(this.current.files.length - pageSize, this.fileOffset + pageSize);
|
|
84
|
+
this.sync();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
75
87
|
if (matchesKey(data, Key.ctrl("c"))) {
|
|
76
88
|
for (const [tab, tabList] of this.lists) {
|
|
77
89
|
tabList.setSelectedIds([]);
|
|
@@ -90,7 +102,9 @@ export class ContextPanel implements Component {
|
|
|
90
102
|
}
|
|
91
103
|
}
|
|
92
104
|
this.tabs.handleInput(data);
|
|
93
|
-
|
|
105
|
+
const current = this.activeList()?.getCurrentItem() ?? this.current;
|
|
106
|
+
if (current.id !== this.current.id) this.fileOffset = 0;
|
|
107
|
+
this.current = current;
|
|
94
108
|
this.sync();
|
|
95
109
|
}
|
|
96
110
|
|
|
@@ -106,14 +120,35 @@ export class ContextPanel implements Component {
|
|
|
106
120
|
}
|
|
107
121
|
private body(): Component {
|
|
108
122
|
return {
|
|
109
|
-
render: (width) =>
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
123
|
+
render: (width) => {
|
|
124
|
+
const tabs = this.tabs.render(width);
|
|
125
|
+
const pageSize = this.filePageSize(tabs.length);
|
|
126
|
+
const maxOffset = Math.max(0, this.current.files.length - pageSize);
|
|
127
|
+
this.fileOffset = Math.min(this.fileOffset, maxOffset);
|
|
128
|
+
const files = this.current.files.slice(this.fileOffset, this.fileOffset + pageSize);
|
|
129
|
+
const range =
|
|
130
|
+
this.current.files.length > pageSize
|
|
131
|
+
? this.theme.fg(
|
|
132
|
+
"dim",
|
|
133
|
+
`${this.fileOffset + 1}-${this.fileOffset + files.length} of ${this.current.files.length} files`,
|
|
134
|
+
)
|
|
135
|
+
: undefined;
|
|
136
|
+
return [
|
|
137
|
+
...tabs,
|
|
138
|
+
"",
|
|
139
|
+
...(range ? [truncateToWidth(range, width, "…")] : []),
|
|
140
|
+
...files.map((file) => truncateToWidth(this.theme.fg("muted", `• ${file}`), width, "…")),
|
|
141
|
+
];
|
|
142
|
+
},
|
|
114
143
|
invalidate: () => this.tabs.invalidate(),
|
|
115
144
|
};
|
|
116
145
|
}
|
|
146
|
+
private filePageSize(tabLines = this.tabs.render(this.tui.terminal.columns).length): number {
|
|
147
|
+
const overlayHeight = Math.floor(this.tui.terminal.rows * 0.8);
|
|
148
|
+
const available = Math.max(1, overlayHeight - tabLines - 7);
|
|
149
|
+
const pageSize = Math.min(8, available);
|
|
150
|
+
return this.current.files.length > pageSize ? Math.max(1, pageSize - 1) : pageSize;
|
|
151
|
+
}
|
|
117
152
|
private secondary(): string {
|
|
118
153
|
return `${[...this.selected.values()].reduce((sum, items) => sum + items.length, 0)} selected`;
|
|
119
154
|
}
|
|
@@ -123,6 +158,7 @@ export class ContextPanel implements Component {
|
|
|
123
158
|
? list.getKeyHints()
|
|
124
159
|
: [
|
|
125
160
|
...this.tabs.getKeyHints(),
|
|
161
|
+
...(this.current.files.length > this.filePageSize() ? [rawHint("option+↑/↓", "scroll files")] : []),
|
|
126
162
|
rawHint("ctrl+c", "clear all"),
|
|
127
163
|
bindingHint("tui.select.confirm", "inject"),
|
|
128
164
|
bindingHint("tui.select.cancel", "cancel"),
|
|
@@ -134,104 +170,3 @@ export class ContextPanel implements Component {
|
|
|
134
170
|
this.tui.requestRender();
|
|
135
171
|
}
|
|
136
172
|
}
|
|
137
|
-
|
|
138
|
-
export type ProposalReviewDecision =
|
|
139
|
-
| { kind: "approve"; operations: ContextOperation[] }
|
|
140
|
-
| { kind: "feedback"; scope: "batch"; selectedIds: string[] }
|
|
141
|
-
| { kind: "feedback"; scope: "operation"; operationId: string; selectedIds: string[] }
|
|
142
|
-
| { kind: "rejected" };
|
|
143
|
-
|
|
144
|
-
export class ProposalPanel implements Component {
|
|
145
|
-
private readonly list: SelectableList<ContextOperation>;
|
|
146
|
-
private readonly panel: ToolPanel;
|
|
147
|
-
private readonly done: (result: ProposalReviewDecision) => void;
|
|
148
|
-
private selected: readonly ContextOperation[] = [];
|
|
149
|
-
private current: ContextOperation;
|
|
150
|
-
constructor(
|
|
151
|
-
tui: TUI,
|
|
152
|
-
theme: Theme,
|
|
153
|
-
proposals: readonly ContextOperation[],
|
|
154
|
-
done: (result: ProposalReviewDecision) => void,
|
|
155
|
-
) {
|
|
156
|
-
const first = proposals[0];
|
|
157
|
-
if (!first) throw new Error("Proposal panel requires at least one operation");
|
|
158
|
-
this.done = done;
|
|
159
|
-
this.current = first;
|
|
160
|
-
const items = [...proposals];
|
|
161
|
-
this.list = new SelectableList(theme, {
|
|
162
|
-
items,
|
|
163
|
-
emptyMessage: "No proposals.",
|
|
164
|
-
selection: { kind: "multi", primaryLabel: "apply selected" },
|
|
165
|
-
actions: [],
|
|
166
|
-
cancelLabel: "cancel",
|
|
167
|
-
maxVisible: 12,
|
|
168
|
-
renderItem: (item, _state, width) => [
|
|
169
|
-
truncateToWidth(
|
|
170
|
-
`${theme.fg("accent", item.kind)} · ${item.tab}/${item.concept}/${item.entry} ${theme.fg("dim", item.reason)}`,
|
|
171
|
-
width,
|
|
172
|
-
"…",
|
|
173
|
-
),
|
|
174
|
-
],
|
|
175
|
-
onResult: (result) =>
|
|
176
|
-
done(
|
|
177
|
-
result.kind === "primary" && result.items.length > 0
|
|
178
|
-
? { kind: "approve", operations: [...result.items] }
|
|
179
|
-
: { kind: "rejected" },
|
|
180
|
-
),
|
|
181
|
-
onSelectionChange: (selected) => {
|
|
182
|
-
this.selected = selected;
|
|
183
|
-
tui.requestRender();
|
|
184
|
-
},
|
|
185
|
-
});
|
|
186
|
-
this.list.setSelectedIds(items.map((item) => item.id));
|
|
187
|
-
this.selected = items;
|
|
188
|
-
this.panel = new ToolPanel(theme, {
|
|
189
|
-
title: "Maintain contexts",
|
|
190
|
-
secondary: "Review proposed operations",
|
|
191
|
-
body: {
|
|
192
|
-
render: (width) => [
|
|
193
|
-
...this.list.render(width),
|
|
194
|
-
"",
|
|
195
|
-
...operationPaths(this.current).map((path) =>
|
|
196
|
-
truncateToWidth(theme.fg("muted", `• ${path}`), width, "…"),
|
|
197
|
-
),
|
|
198
|
-
],
|
|
199
|
-
invalidate: () => this.list.invalidate(),
|
|
200
|
-
},
|
|
201
|
-
footer: {
|
|
202
|
-
kind: "hints",
|
|
203
|
-
hints: [
|
|
204
|
-
...this.list.getKeyHints(),
|
|
205
|
-
rawHint("ctrl+b", "batch feedback"),
|
|
206
|
-
rawHint("ctrl+i", "item feedback"),
|
|
207
|
-
],
|
|
208
|
-
},
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
handleInput(data: string): void {
|
|
212
|
-
if (!this.list.isFilterFocused()) {
|
|
213
|
-
const selectedIds = this.selected.map((item) => item.id);
|
|
214
|
-
if (matchesKey(data, Key.ctrl("b"))) {
|
|
215
|
-
this.done({ kind: "feedback", scope: "batch", selectedIds });
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
if (matchesKey(data, Key.ctrl("i"))) {
|
|
219
|
-
const current = this.list.getCurrentItem();
|
|
220
|
-
if (current) this.done({ kind: "feedback", scope: "operation", operationId: current.id, selectedIds });
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
this.list.handleInput(data);
|
|
225
|
-
this.current = this.list.getCurrentItem() ?? this.current;
|
|
226
|
-
}
|
|
227
|
-
render(width: number): string[] {
|
|
228
|
-
return this.panel.render(width);
|
|
229
|
-
}
|
|
230
|
-
invalidate(): void {
|
|
231
|
-
this.panel.invalidate();
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
function operationPaths(operation: ContextOperation): string[] {
|
|
236
|
-
return operation.kind === "replace-file" ? [`${operation.from} → ${operation.to}`] : operation.files;
|
|
237
|
-
}
|