@shanepadgett/tau-agent 0.39.1 → 0.40.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/auto-compact/README.md +1 -1
- package/extensions/auto-compact/index.ts +20 -0
- package/extensions/cost-report/README.md +32 -0
- package/extensions/cost-report/analyze.ts +423 -0
- package/extensions/cost-report/html.ts +474 -0
- package/extensions/cost-report/index.ts +181 -0
- package/extensions/cost-report/panel.ts +64 -0
- package/extensions/cost-report/range.ts +145 -0
- package/extensions/cost-report/types.ts +93 -0
- package/extensions/silent-command-runner/README.md +2 -0
- package/extensions/silent-command-runner/index.ts +50 -11
- package/extensions/tau-help/help.md +4 -0
- package/package.json +2 -2
- package/shared/events.ts +9 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { getKeybindings, type Component, type TUI, truncateToWidth } from "@earendil-works/pi-tui";
|
|
3
|
+
import { bindingHint, ToolPanel, type ToolPanelConfig } from "@shanepadgett/tau-tui";
|
|
4
|
+
|
|
5
|
+
export class CostReportStatusPanel implements Component {
|
|
6
|
+
private readonly tui: TUI;
|
|
7
|
+
private readonly panel: ToolPanel;
|
|
8
|
+
private readonly config: ToolPanelConfig;
|
|
9
|
+
private readonly controller = new AbortController();
|
|
10
|
+
private readonly startedAt = Date.now();
|
|
11
|
+
private readonly timer: ReturnType<typeof setInterval>;
|
|
12
|
+
private status = "Starting…";
|
|
13
|
+
private readonly secondary: string;
|
|
14
|
+
|
|
15
|
+
constructor(tui: TUI, theme: Theme, secondary: string) {
|
|
16
|
+
this.tui = tui;
|
|
17
|
+
this.secondary = secondary;
|
|
18
|
+
this.config = {
|
|
19
|
+
title: "Cost report",
|
|
20
|
+
secondary,
|
|
21
|
+
body: {
|
|
22
|
+
render: (width) => [truncateToWidth(theme.fg("muted", this.status), width, "…")],
|
|
23
|
+
invalidate: () => {},
|
|
24
|
+
},
|
|
25
|
+
footer: { kind: "hints", hints: [bindingHint("tui.select.cancel", "cancel")] },
|
|
26
|
+
border: "box",
|
|
27
|
+
};
|
|
28
|
+
this.panel = new ToolPanel(theme, this.config);
|
|
29
|
+
this.timer = setInterval(() => this.tui.requestRender(), 1_000);
|
|
30
|
+
this.timer.unref();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get signal(): AbortSignal {
|
|
34
|
+
return this.controller.signal;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
handleInput(data: string): void {
|
|
38
|
+
if (!getKeybindings().matches(data, "tui.select.cancel")) return;
|
|
39
|
+
if (this.controller.signal.aborted) return;
|
|
40
|
+
this.update("Cancelling…");
|
|
41
|
+
this.controller.abort();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
update(status: string): void {
|
|
45
|
+
this.status = status.trim() || this.status;
|
|
46
|
+
this.tui.requestRender();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
render(width: number): string[] {
|
|
50
|
+
const elapsed = Math.floor((Date.now() - this.startedAt) / 1_000);
|
|
51
|
+
const clock = elapsed < 60 ? `${elapsed}s` : `${Math.floor(elapsed / 60)}m ${elapsed % 60}s`;
|
|
52
|
+
this.config.title = `Cost report · ${clock}`;
|
|
53
|
+
this.config.secondary = this.secondary;
|
|
54
|
+
return this.panel.render(width);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
invalidate(): void {
|
|
58
|
+
this.panel.invalidate();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
dispose(): void {
|
|
62
|
+
clearInterval(this.timer);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import type { LiveRangeKind, ReportRange } from "./types.ts";
|
|
2
|
+
|
|
3
|
+
const MONTH_LABELS = [
|
|
4
|
+
"January",
|
|
5
|
+
"February",
|
|
6
|
+
"March",
|
|
7
|
+
"April",
|
|
8
|
+
"May",
|
|
9
|
+
"June",
|
|
10
|
+
"July",
|
|
11
|
+
"August",
|
|
12
|
+
"September",
|
|
13
|
+
"October",
|
|
14
|
+
"November",
|
|
15
|
+
"December",
|
|
16
|
+
] as const;
|
|
17
|
+
|
|
18
|
+
export const LIVE_RANGE_CHOICES: ReadonlyArray<{ id: LiveRangeKind | "specific-month"; label: string }> = [
|
|
19
|
+
{ id: "past-7-days", label: "Past 7 days" },
|
|
20
|
+
{ id: "current-week", label: "Current week" },
|
|
21
|
+
{ id: "current-month", label: "Current month" },
|
|
22
|
+
{ id: "year-to-date", label: "Year to date" },
|
|
23
|
+
{ id: "specific-month", label: "Specific month…" },
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
export const MONTH_CHOICES: ReadonlyArray<{ month: number; label: string }> = MONTH_LABELS.map((label, month) => ({
|
|
27
|
+
month,
|
|
28
|
+
label,
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
export function yearChoices(now = new Date()): number[] {
|
|
32
|
+
const current = now.getFullYear();
|
|
33
|
+
return Array.from({ length: 6 }, (_, index) => current - index);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function liveRange(kind: LiveRangeKind, now = new Date()): ReportRange {
|
|
37
|
+
const endMs = now.getTime();
|
|
38
|
+
if (kind === "past-7-days") {
|
|
39
|
+
const start = startOfLocalDay(now);
|
|
40
|
+
start.setDate(start.getDate() - 6);
|
|
41
|
+
// Slides daily; slug pins the window end day so same-day reopens match.
|
|
42
|
+
return {
|
|
43
|
+
kind,
|
|
44
|
+
label: "Past 7 days",
|
|
45
|
+
slug: `7d-${localDateKey(endMs)}`,
|
|
46
|
+
startMs: start.getTime(),
|
|
47
|
+
endMs,
|
|
48
|
+
layout: "row",
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (kind === "current-week") {
|
|
52
|
+
const start = startOfLocalDay(now);
|
|
53
|
+
const day = start.getDay(); // 0 Sun … 6 Sat
|
|
54
|
+
const mondayOffset = day === 0 ? -6 : 1 - day;
|
|
55
|
+
start.setDate(start.getDate() + mondayOffset);
|
|
56
|
+
return {
|
|
57
|
+
kind,
|
|
58
|
+
label: "Current week",
|
|
59
|
+
slug: `week-${localDateKey(start.getTime())}`,
|
|
60
|
+
startMs: start.getTime(),
|
|
61
|
+
endMs,
|
|
62
|
+
layout: "row",
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
if (kind === "current-month") {
|
|
66
|
+
const start = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
67
|
+
const ym = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;
|
|
68
|
+
return {
|
|
69
|
+
kind,
|
|
70
|
+
label: "Current month",
|
|
71
|
+
slug: `month-${ym}`,
|
|
72
|
+
startMs: start.getTime(),
|
|
73
|
+
endMs,
|
|
74
|
+
layout: "row",
|
|
75
|
+
year: now.getFullYear(),
|
|
76
|
+
month: now.getMonth(),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const start = new Date(now.getFullYear(), 0, 1);
|
|
80
|
+
return {
|
|
81
|
+
kind,
|
|
82
|
+
label: "Year to date",
|
|
83
|
+
slug: `ytd-${now.getFullYear()}`,
|
|
84
|
+
startMs: start.getTime(),
|
|
85
|
+
endMs,
|
|
86
|
+
layout: "ytd",
|
|
87
|
+
year: now.getFullYear(),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function specificMonthRange(year: number, month: number, now = new Date()): ReportRange {
|
|
92
|
+
const start = new Date(year, month, 1);
|
|
93
|
+
const endExclusive = new Date(year, month + 1, 1);
|
|
94
|
+
const endMs = Math.min(now.getTime(), endExclusive.getTime() - 1);
|
|
95
|
+
const monthLabel = MONTH_LABELS[month] ?? `Month ${month + 1}`;
|
|
96
|
+
const ym = `${year}-${String(month + 1).padStart(2, "0")}`;
|
|
97
|
+
return {
|
|
98
|
+
kind: "specific-month",
|
|
99
|
+
label: `${monthLabel} ${year}`,
|
|
100
|
+
slug: ym,
|
|
101
|
+
startMs: start.getTime(),
|
|
102
|
+
endMs,
|
|
103
|
+
layout: "row",
|
|
104
|
+
year,
|
|
105
|
+
month,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function startOfLocalDay(date: Date): Date {
|
|
110
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function localDateKey(timestampMs: number): string {
|
|
114
|
+
const date = new Date(timestampMs);
|
|
115
|
+
const y = date.getFullYear();
|
|
116
|
+
const m = String(date.getMonth() + 1).padStart(2, "0");
|
|
117
|
+
const d = String(date.getDate()).padStart(2, "0");
|
|
118
|
+
return `${y}-${m}-${d}`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function shortDateLabel(timestampMs: number): string {
|
|
122
|
+
const date = new Date(timestampMs);
|
|
123
|
+
const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
124
|
+
return `${date.getDate()} ${months[date.getMonth()]}`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function isWeekend(timestampMs: number): boolean {
|
|
128
|
+
const day = new Date(timestampMs).getDay();
|
|
129
|
+
return day === 0 || day === 6;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function eachLocalDay(startMs: number, endMs: number): number[] {
|
|
133
|
+
const days: number[] = [];
|
|
134
|
+
const cursor = startOfLocalDay(new Date(startMs));
|
|
135
|
+
const end = startOfLocalDay(new Date(endMs));
|
|
136
|
+
while (cursor.getTime() <= end.getTime()) {
|
|
137
|
+
days.push(cursor.getTime());
|
|
138
|
+
cursor.setDate(cursor.getDate() + 1);
|
|
139
|
+
}
|
|
140
|
+
return days;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function daysInMonth(year: number, month: number): number {
|
|
144
|
+
return new Date(year, month + 1, 0).getDate();
|
|
145
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
export type ReportScope = "project" | "all";
|
|
2
|
+
|
|
3
|
+
export type LiveRangeKind = "past-7-days" | "current-week" | "current-month" | "year-to-date";
|
|
4
|
+
|
|
5
|
+
export type RangeKind = LiveRangeKind | "specific-month";
|
|
6
|
+
|
|
7
|
+
export interface ReportRange {
|
|
8
|
+
kind: RangeKind;
|
|
9
|
+
label: string;
|
|
10
|
+
slug: string;
|
|
11
|
+
startMs: number;
|
|
12
|
+
endMs: number;
|
|
13
|
+
/** Single full-width day row vs one row per month (YTD). */
|
|
14
|
+
layout: "row" | "ytd";
|
|
15
|
+
year?: number;
|
|
16
|
+
month?: number; // 0-11
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface CostTotals {
|
|
20
|
+
input: number;
|
|
21
|
+
output: number;
|
|
22
|
+
cacheRead: number;
|
|
23
|
+
cacheWrite: number;
|
|
24
|
+
totalTokens: number;
|
|
25
|
+
cost: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ModelCost {
|
|
29
|
+
key: string;
|
|
30
|
+
provider: string;
|
|
31
|
+
model: string;
|
|
32
|
+
cost: number;
|
|
33
|
+
tokens: number;
|
|
34
|
+
sessions: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface SubagentCost {
|
|
38
|
+
agent: string;
|
|
39
|
+
cost: number;
|
|
40
|
+
calls: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ProjectCost {
|
|
44
|
+
key: string;
|
|
45
|
+
label: string;
|
|
46
|
+
cost: number;
|
|
47
|
+
sessions: number;
|
|
48
|
+
tokens: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface SessionModelCost {
|
|
52
|
+
key: string;
|
|
53
|
+
label: string;
|
|
54
|
+
cost: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface SessionCost {
|
|
58
|
+
id: string;
|
|
59
|
+
path: string;
|
|
60
|
+
name: string;
|
|
61
|
+
projectKey: string;
|
|
62
|
+
projectLabel: string;
|
|
63
|
+
startedAtMs: number;
|
|
64
|
+
cost: number;
|
|
65
|
+
tokens: number;
|
|
66
|
+
models: SessionModelCost[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface DayCost {
|
|
70
|
+
dateKey: string; // YYYY-MM-DD local
|
|
71
|
+
label: string;
|
|
72
|
+
timestampMs: number;
|
|
73
|
+
cost: number;
|
|
74
|
+
weekend: boolean;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface CostReport {
|
|
78
|
+
generatedAtMs: number;
|
|
79
|
+
cwd: string;
|
|
80
|
+
scope: ReportScope;
|
|
81
|
+
range: ReportRange;
|
|
82
|
+
directCost: number;
|
|
83
|
+
subagentCost: number;
|
|
84
|
+
totalCost: number;
|
|
85
|
+
totalTokens: number;
|
|
86
|
+
sessionCount: number;
|
|
87
|
+
projectCount: number;
|
|
88
|
+
days: DayCost[];
|
|
89
|
+
models: ModelCost[];
|
|
90
|
+
subagents: SubagentCost[];
|
|
91
|
+
projects: ProjectCost[];
|
|
92
|
+
sessions: SessionCost[];
|
|
93
|
+
}
|
|
@@ -23,3 +23,5 @@ Configure it in Tau settings under `extensions.silentCommandRunner`:
|
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
Passes are notifications only. Failures are shown in chat and sent to the agent. Tau's ready-for-input notification waits for these checks and stays quiet when a failure starts another agent turn.
|
|
26
|
+
|
|
27
|
+
When auto-compact pauses a long run to compact context, checks wait until that work chain finishes. They do not run on the compact abort, and file changes from before the compact still count.
|
|
@@ -2,7 +2,7 @@ import { readdir, stat } from "node:fs/promises";
|
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { type ExecResult, type ExtensionAPI, keyText, type Theme } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
import { Box, Text } from "@earendil-works/pi-tui";
|
|
5
|
-
import { emitTauEvent } from "../../shared/events.ts";
|
|
5
|
+
import { emitTauEvent, onTauEvent } from "../../shared/events.ts";
|
|
6
6
|
import { matchGlob, posixPath } from "../../shared/glob.ts";
|
|
7
7
|
import { loadTauExtensionSettings } from "../../shared/settings/load.ts";
|
|
8
8
|
import { resolveProjectRoot } from "../../shared/settings/paths.ts";
|
|
@@ -84,11 +84,40 @@ export default function silentCommandRunnerExtension(pi: ExtensionAPI): void {
|
|
|
84
84
|
let chainActive = false;
|
|
85
85
|
let attentionHoldSequence = 0;
|
|
86
86
|
let attentionHoldId: string | undefined;
|
|
87
|
+
const checkPauses = new Set<string>();
|
|
88
|
+
let settleDeferred = false;
|
|
89
|
+
|
|
90
|
+
function checksPaused(): boolean {
|
|
91
|
+
return checkPauses.size > 0;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function finalizeChain(): void {
|
|
95
|
+
settleDeferred = false;
|
|
96
|
+
chainActive = false;
|
|
97
|
+
const holdId = attentionHoldId;
|
|
98
|
+
attentionHoldId = undefined;
|
|
99
|
+
if (holdId) emitTauEvent(pi, "tau:attention.hold.release", { id: holdId, disposition: "notify" });
|
|
100
|
+
}
|
|
87
101
|
|
|
88
102
|
pi.registerMessageRenderer<FailureDetails>(MESSAGE_TYPE, (message, { expanded }, theme) =>
|
|
89
103
|
renderFailure(asFailureDetails(message.details), expanded, theme),
|
|
90
104
|
);
|
|
91
105
|
|
|
106
|
+
onTauEvent(pi, "silent-command-runner.pause", "tau:silent-command-runner.pause", ({ id }) => {
|
|
107
|
+
checkPauses.add(id);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
onTauEvent(pi, "silent-command-runner.resume", "tau:silent-command-runner.resume", ({ id, disposition }) => {
|
|
111
|
+
checkPauses.delete(id);
|
|
112
|
+
if (checksPaused()) return;
|
|
113
|
+
if (disposition === "continue") {
|
|
114
|
+
// Compact will start a new agent run on the same work chain; keep baseline + hold.
|
|
115
|
+
settleDeferred = false;
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (settleDeferred || chainActive) finalizeChain();
|
|
119
|
+
});
|
|
120
|
+
|
|
92
121
|
pi.on("session_start", async (_event, ctx) => {
|
|
93
122
|
settings = normalizeSettings(await loadTauExtensionSettings(ctx, silentCommandRunnerSettings));
|
|
94
123
|
sessionActive = true;
|
|
@@ -97,6 +126,8 @@ export default function silentCommandRunnerExtension(pi: ExtensionAPI): void {
|
|
|
97
126
|
chainActive = false;
|
|
98
127
|
attentionHoldSequence = 0;
|
|
99
128
|
attentionHoldId = undefined;
|
|
129
|
+
checkPauses.clear();
|
|
130
|
+
settleDeferred = false;
|
|
100
131
|
});
|
|
101
132
|
|
|
102
133
|
pi.on("before_agent_start", async (event, ctx) => {
|
|
@@ -108,20 +139,24 @@ export default function silentCommandRunnerExtension(pi: ExtensionAPI): void {
|
|
|
108
139
|
pi.on("agent_start", async (_event, ctx) => {
|
|
109
140
|
const startingChain = !chainActive;
|
|
110
141
|
chainActive = true;
|
|
111
|
-
|
|
142
|
+
settleDeferred = false;
|
|
112
143
|
if (!settings.enabled || settings.commands.length === 0) {
|
|
113
|
-
|
|
144
|
+
if (startingChain) {
|
|
145
|
+
turnStart = Date.now();
|
|
146
|
+
turnPaths = new Set();
|
|
147
|
+
}
|
|
114
148
|
return;
|
|
115
149
|
}
|
|
116
|
-
if (startingChain)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
150
|
+
if (!startingChain) return;
|
|
151
|
+
attentionHoldId = `silent-command-runner:${++attentionHoldSequence}`;
|
|
152
|
+
emitTauEvent(pi, "tau:attention.hold.acquire", { id: attentionHoldId });
|
|
153
|
+
turnStart = Date.now();
|
|
120
154
|
const projectRoot = await resolveProjectRoot(ctx.cwd);
|
|
121
155
|
turnPaths = new Set(await walkFiles(projectRoot));
|
|
122
156
|
});
|
|
123
157
|
|
|
124
158
|
pi.on("agent_end", async (event, ctx) => {
|
|
159
|
+
if (checksPaused()) return;
|
|
125
160
|
if (hasAbortedAssistantMessage(event.messages)) return;
|
|
126
161
|
try {
|
|
127
162
|
await runChangedCommands(ctx.cwd, turnStart, ctx.ui.notify);
|
|
@@ -131,10 +166,12 @@ export default function silentCommandRunnerExtension(pi: ExtensionAPI): void {
|
|
|
131
166
|
});
|
|
132
167
|
|
|
133
168
|
pi.on("agent_settled", () => {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
169
|
+
if (checksPaused()) {
|
|
170
|
+
// Compact aborted this run; keep chain + file baseline until resume.
|
|
171
|
+
settleDeferred = true;
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
finalizeChain();
|
|
138
175
|
});
|
|
139
176
|
|
|
140
177
|
pi.on("session_shutdown", () => {
|
|
@@ -144,6 +181,8 @@ export default function silentCommandRunnerExtension(pi: ExtensionAPI): void {
|
|
|
144
181
|
turnPaths = new Set();
|
|
145
182
|
chainActive = false;
|
|
146
183
|
attentionHoldId = undefined;
|
|
184
|
+
checkPauses.clear();
|
|
185
|
+
settleDeferred = false;
|
|
147
186
|
});
|
|
148
187
|
|
|
149
188
|
async function collectCommandFailures(
|
|
@@ -40,6 +40,10 @@ Adds `/clear-screen` to clear terminal output without changing the session.
|
|
|
40
40
|
|
|
41
41
|
Adds `/commit` for semantic commit grouping, review, and committing selected repository changes.
|
|
42
42
|
|
|
43
|
+
## cost-report
|
|
44
|
+
|
|
45
|
+
Adds `/cost-report` to build an HTML spend report from local session usage. Pick a time frame (past 7 days, current week, current month, year to date, or a specific month) and scope (current project or all sessions). Tau scans sessions, writes under `~/.pi/tau/cost-reports/`, opens the file, and notifies with the path. Empty windows warn without writing a file.
|
|
46
|
+
|
|
43
47
|
## context
|
|
44
48
|
|
|
45
49
|
Adds `/context` to inject reusable repository work scopes from `.pi/contexts`, and `/context-sync` or `/context-sync <nudge>` for human-driven catalog sync. Selecting entries injects them once into the conversation: `read` paths as complete files, `show` targets as current declaration slices, `outline` paths as Explore structures, and one hidden note listing `references` plus instructions to treat the injected material as current. Run `/context` again to inject more. Manual sync replaces the editor with a status panel; Escape or Ctrl+C cancels. When `sync.automation` is on, coding agent can also run `context-sync` after meaningful uncommitted work. Sync catalogs durable code and long-lived documentation; recurring scratch, planning, interview, and rough-idea paths belong in `validation.ignoreGlobs`. `sync.enabled` is master switch for command, automation, and validation auto-run. Context validation is off by default; when on (and sync enabled), Tau auto-runs context-sync on failure. Domain folders are `NN_slug` tabs (ordered by the two-digit prefix; UI shows the slug), TOML files are concepts, and TOML sections are selectable entries.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shanepadgett/tau-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.0",
|
|
4
4
|
"description": "Tau is a custom agentic harness built with pi extensions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@ast-grep/wasm": "0.45.1",
|
|
38
|
-
"@shanepadgett/tau-tui": "0.
|
|
38
|
+
"@shanepadgett/tau-tui": "0.40.0",
|
|
39
39
|
"@vscode/tree-sitter-wasm": "0.3.1",
|
|
40
40
|
"image-size": "2.0.2",
|
|
41
41
|
"smol-toml": "1.8.0",
|
package/shared/events.ts
CHANGED
|
@@ -16,6 +16,15 @@ export type TauAgentEvents = {
|
|
|
16
16
|
id: string;
|
|
17
17
|
disposition: "notify" | "discard";
|
|
18
18
|
};
|
|
19
|
+
/** Pause silent post-edit checks across a compact that will resume the same work chain. */
|
|
20
|
+
"tau:silent-command-runner.pause": {
|
|
21
|
+
id: string;
|
|
22
|
+
};
|
|
23
|
+
/** End a silent-check pause. continue keeps the chain open; settle finalizes a deferred settle. */
|
|
24
|
+
"tau:silent-command-runner.resume": {
|
|
25
|
+
id: string;
|
|
26
|
+
disposition: "continue" | "settle";
|
|
27
|
+
};
|
|
19
28
|
"tau:file-mutation.applied": {
|
|
20
29
|
source: "patch";
|
|
21
30
|
toolCallId: string;
|