@shanepadgett/tau-agent 0.1.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/README.md +30 -0
- package/docs/extending-tau-agent.md +119 -0
- package/docs/subagents.md +57 -0
- package/docs/tui.md +107 -0
- package/extensions/appshot/README.md +11 -0
- package/extensions/appshot/capture.swift +191 -0
- package/extensions/appshot/index.ts +245 -0
- package/extensions/appshot/native-helper.ts +102 -0
- package/extensions/attention/README.md +23 -0
- package/extensions/attention/index.ts +71 -0
- package/extensions/auto-name/README.md +26 -0
- package/extensions/auto-name/index.ts +163 -0
- package/extensions/branch/README.md +22 -0
- package/extensions/branch/index.ts +145 -0
- package/extensions/branch/panel.ts +148 -0
- package/extensions/clear-screen/README.md +29 -0
- package/extensions/clear-screen/index.ts +75 -0
- package/extensions/commit/README.md +15 -0
- package/extensions/commit/commit-plan.ts +328 -0
- package/extensions/commit/git-change-set.ts +268 -0
- package/extensions/commit/index.ts +138 -0
- package/extensions/commit/review-ui.ts +745 -0
- package/extensions/explore/README.md +7 -0
- package/extensions/explore/autoread.ts +140 -0
- package/extensions/explore/find.ts +147 -0
- package/extensions/explore/grep.ts +749 -0
- package/extensions/explore/index.ts +17 -0
- package/extensions/explore/limits.ts +22 -0
- package/extensions/explore/ls.ts +113 -0
- package/extensions/explore/path-display.ts +41 -0
- package/extensions/explore/path-tree.ts +150 -0
- package/extensions/explore/read.ts +160 -0
- package/extensions/explore/result.ts +29 -0
- package/extensions/explore/traverse.ts +226 -0
- package/extensions/footer/README.md +23 -0
- package/extensions/footer/index.ts +534 -0
- package/extensions/footer/settings.ts +13 -0
- package/extensions/ideas/README.md +10 -0
- package/extensions/ideas/browser.ts +66 -0
- package/extensions/ideas/index.ts +31 -0
- package/extensions/ideas/store.ts +32 -0
- package/extensions/image-gen/README.md +11 -0
- package/extensions/image-gen/client.ts +186 -0
- package/extensions/image-gen/index.ts +152 -0
- package/extensions/manage-sessions/README.md +12 -0
- package/extensions/manage-sessions/index.ts +58 -0
- package/extensions/manage-sessions/manager-ui.ts +323 -0
- package/extensions/manage-sessions/sessions.ts +126 -0
- package/extensions/patch/README.md +48 -0
- package/extensions/patch/executor.ts +355 -0
- package/extensions/patch/index.ts +170 -0
- package/extensions/patch/matcher.ts +236 -0
- package/extensions/patch/parser.ts +347 -0
- package/extensions/patch/render.ts +247 -0
- package/extensions/patch/summary.ts +35 -0
- package/extensions/publish/README.md +11 -0
- package/extensions/publish/index.ts +225 -0
- package/extensions/qna/README.md +45 -0
- package/extensions/qna/additional-context-body.ts +51 -0
- package/extensions/qna/body-render.ts +6 -0
- package/extensions/qna/choice-question-body.ts +322 -0
- package/extensions/qna/index.ts +245 -0
- package/extensions/qna/inline-editor-row.ts +56 -0
- package/extensions/qna/input-question-body.ts +89 -0
- package/extensions/qna/model.ts +317 -0
- package/extensions/qna/panel.ts +240 -0
- package/extensions/qna/ui.ts +16 -0
- package/extensions/reference/README.md +47 -0
- package/extensions/reference/index.ts +76 -0
- package/extensions/reference/panel.ts +874 -0
- package/extensions/reference/settings.ts +29 -0
- package/extensions/run-summary/README.md +5 -0
- package/extensions/run-summary/index.ts +98 -0
- package/extensions/silent-command-runner/README.md +25 -0
- package/extensions/silent-command-runner/index.ts +468 -0
- package/extensions/silent-command-runner/settings.ts +65 -0
- package/extensions/soul/README.md +19 -0
- package/extensions/soul/index.ts +20 -0
- package/extensions/soul/prompt.ts +254 -0
- package/extensions/soul/settings.ts +13 -0
- package/extensions/stash/README.md +48 -0
- package/extensions/stash/browser.ts +51 -0
- package/extensions/stash/index.ts +55 -0
- package/extensions/stash/store.ts +36 -0
- package/extensions/subagent/README.md +34 -0
- package/extensions/subagent/agents/scout.md +85 -0
- package/extensions/subagent/agents/web-research.md +95 -0
- package/extensions/subagent/agents.ts +196 -0
- package/extensions/subagent/index.ts +214 -0
- package/extensions/subagent/render.ts +89 -0
- package/extensions/subagent/run.ts +325 -0
- package/extensions/tau/README.md +17 -0
- package/extensions/tau/index.ts +122 -0
- package/extensions/tau-help/README.md +3 -0
- package/extensions/tau-help/help.md +125 -0
- package/extensions/tau-help/index.ts +68 -0
- package/extensions/turn-budget/README.md +14 -0
- package/extensions/turn-budget/index.ts +150 -0
- package/extensions/turn-budget/settings.ts +35 -0
- package/extensions/web/README.md +13 -0
- package/extensions/web/codesearch.ts +79 -0
- package/extensions/web/exa.ts +101 -0
- package/extensions/web/html.ts +67 -0
- package/extensions/web/index.ts +13 -0
- package/extensions/web/limits.ts +11 -0
- package/extensions/web/tool-output.ts +54 -0
- package/extensions/web/webfetch.ts +177 -0
- package/extensions/web/websearch.ts +100 -0
- package/package.json +57 -0
- package/prompts/.gitkeep +1 -0
- package/prompts/cavemanify.md +25 -0
- package/prompts/implement.md +25 -0
- package/prompts/interview.md +48 -0
- package/prompts/plan-feature.md +30 -0
- package/prompts/plan-implementation.md +50 -0
- package/schemas/tau.schema.json +205 -0
- package/shared/agent-blocked.ts +12 -0
- package/shared/description.ts +28 -0
- package/shared/events.ts +135 -0
- package/shared/git.ts +41 -0
- package/shared/injected-context.ts +69 -0
- package/shared/jsonl-store.ts +108 -0
- package/shared/model-fallback/index.ts +278 -0
- package/shared/model-fallback/settings.ts +18 -0
- package/shared/model-fallback/types.ts +8 -0
- package/shared/ranges.ts +21 -0
- package/shared/settings/define.ts +15 -0
- package/shared/settings/files.ts +20 -0
- package/shared/settings/json.ts +41 -0
- package/shared/settings/load.ts +59 -0
- package/shared/settings/merge.ts +21 -0
- package/shared/settings/paths.ts +41 -0
- package/shared/settings/schema.ts +25 -0
- package/shared/settings/specs.ts +23 -0
- package/shared/text.ts +11 -0
- package/shared/tool-row-state.ts +43 -0
- package/skills/writing-preferences/SKILL.md +136 -0
- package/themes/.gitkeep +1 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { StringEnum } from "@earendil-works/pi-ai";
|
|
2
|
+
import { defineTool, type TruncationResult } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { Text } from "@earendil-works/pi-tui";
|
|
4
|
+
import { type Static, Type } from "typebox";
|
|
5
|
+
import { formatToolRowTitle, type ToolRowStateStore } from "../../shared/tool-row-state.js";
|
|
6
|
+
import { htmlToMarkdown, htmlToText } from "./html.ts";
|
|
7
|
+
import { normalizeTimeout } from "./limits.ts";
|
|
8
|
+
import { renderWebToolResult, truncateCallSummary, truncateToolOutput } from "./tool-output.ts";
|
|
9
|
+
|
|
10
|
+
const MAX_RESPONSE_BYTES = 5 * 1024 * 1024;
|
|
11
|
+
interface FetchResponse {
|
|
12
|
+
ok: boolean;
|
|
13
|
+
status: number;
|
|
14
|
+
headers: { get(name: string): string | null };
|
|
15
|
+
body: ReadableStream<Uint8Array> | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const webFetchParams = Type.Object(
|
|
19
|
+
{
|
|
20
|
+
url: Type.String({ description: "URL to fetch (http:// or https://)" }),
|
|
21
|
+
format: Type.Optional(
|
|
22
|
+
StringEnum(["markdown", "text", "html"] as const, { description: "Output format (default: markdown)" }),
|
|
23
|
+
),
|
|
24
|
+
timeout: Type.Optional(Type.Number({ description: "Timeout in seconds (default: 30, max: 600)" })),
|
|
25
|
+
},
|
|
26
|
+
{ additionalProperties: false },
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
type WebFetchParams = Static<typeof webFetchParams>;
|
|
30
|
+
interface WebFetchDetails {
|
|
31
|
+
url: string;
|
|
32
|
+
format: "markdown" | "text" | "html";
|
|
33
|
+
mime: string;
|
|
34
|
+
bytes: number;
|
|
35
|
+
truncation?: TruncationResult;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function readResponseBody(response: FetchResponse): Promise<Uint8Array> {
|
|
39
|
+
if (!response.body) return new Uint8Array();
|
|
40
|
+
const reader = response.body.getReader();
|
|
41
|
+
const chunks: Uint8Array[] = [];
|
|
42
|
+
let bytes = 0;
|
|
43
|
+
try {
|
|
44
|
+
while (true) {
|
|
45
|
+
const next = await reader.read();
|
|
46
|
+
if (next.done) break;
|
|
47
|
+
bytes += next.value.byteLength;
|
|
48
|
+
if (bytes > MAX_RESPONSE_BYTES) {
|
|
49
|
+
await reader.cancel().catch(() => undefined);
|
|
50
|
+
throw new Error("Response too large (limit is 5MB)");
|
|
51
|
+
}
|
|
52
|
+
chunks.push(next.value);
|
|
53
|
+
}
|
|
54
|
+
} finally {
|
|
55
|
+
reader.releaseLock();
|
|
56
|
+
}
|
|
57
|
+
return Buffer.concat(chunks, bytes);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function renderCallSummary(args: WebFetchParams): string {
|
|
61
|
+
return truncateCallSummary((args.url ?? "").trim());
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function createWebFetchTool(rowState: ToolRowStateStore) {
|
|
65
|
+
return defineTool<typeof webFetchParams, WebFetchDetails | undefined>({
|
|
66
|
+
name: "webfetch",
|
|
67
|
+
label: "Web Fetch",
|
|
68
|
+
description:
|
|
69
|
+
"Fetch a known HTTP(S) URL as Markdown, text, or HTML. Supports inline images, limits response bodies to 5 MB, and truncates text to 2,000 lines or 50 KB.",
|
|
70
|
+
promptSnippet: "Fetch a specific URL and extract readable content",
|
|
71
|
+
promptGuidelines: [
|
|
72
|
+
"Use webfetch when you already have a URL and need its content.",
|
|
73
|
+
"Use websearch for broad discovery and codesearch for implementation-oriented lookups.",
|
|
74
|
+
"Use a separate research workflow instead of webfetch when several searches, fetches, and synthesis are needed.",
|
|
75
|
+
],
|
|
76
|
+
parameters: webFetchParams,
|
|
77
|
+
async execute(_toolCallId, params, signal, onUpdate) {
|
|
78
|
+
let url: URL;
|
|
79
|
+
try {
|
|
80
|
+
url = new URL(params.url);
|
|
81
|
+
} catch {
|
|
82
|
+
throw new Error(`Invalid URL: ${params.url}`);
|
|
83
|
+
}
|
|
84
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
85
|
+
throw new Error("URL must use http:// or https://");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const format = params.format ?? "markdown";
|
|
89
|
+
const timeout = normalizeTimeout(params.timeout, 30);
|
|
90
|
+
await onUpdate?.({ content: [{ type: "text", text: "Fetching page..." }], details: undefined });
|
|
91
|
+
const timeoutSignal = AbortSignal.timeout(timeout * 1000);
|
|
92
|
+
const requestSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
const accept =
|
|
96
|
+
format === "markdown"
|
|
97
|
+
? "text/markdown;q=1.0, text/plain;q=0.8, text/html;q=0.7, */*;q=0.1"
|
|
98
|
+
: format === "text"
|
|
99
|
+
? "text/plain;q=1.0, text/markdown;q=0.9, text/html;q=0.8, */*;q=0.1"
|
|
100
|
+
: "text/html;q=1.0, application/xhtml+xml;q=0.9, */*;q=0.1";
|
|
101
|
+
const headers = {
|
|
102
|
+
"User-Agent":
|
|
103
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36",
|
|
104
|
+
Accept: accept,
|
|
105
|
+
"Accept-Language": "en-US,en;q=0.9",
|
|
106
|
+
};
|
|
107
|
+
const first = (await fetch(url.toString(), {
|
|
108
|
+
method: "GET",
|
|
109
|
+
headers,
|
|
110
|
+
signal: requestSignal,
|
|
111
|
+
})) as FetchResponse;
|
|
112
|
+
let response = first;
|
|
113
|
+
if (first.status === 403 && first.headers.get("cf-mitigated")?.toLowerCase() === "challenge") {
|
|
114
|
+
await first.body?.cancel().catch(() => undefined);
|
|
115
|
+
response = (await fetch(url.toString(), {
|
|
116
|
+
method: "GET",
|
|
117
|
+
headers: { ...headers, "User-Agent": "pi" },
|
|
118
|
+
signal: requestSignal,
|
|
119
|
+
})) as FetchResponse;
|
|
120
|
+
}
|
|
121
|
+
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
|
|
122
|
+
|
|
123
|
+
const declaredLength = response.headers.get("content-length");
|
|
124
|
+
if (declaredLength !== null) {
|
|
125
|
+
const bytes = Number.parseInt(declaredLength, 10);
|
|
126
|
+
if (Number.isFinite(bytes) && bytes > MAX_RESPONSE_BYTES) {
|
|
127
|
+
await response.body?.cancel().catch(() => undefined);
|
|
128
|
+
throw new Error("Response too large (limit is 5MB)");
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const body = await readResponseBody(response);
|
|
133
|
+
const mime = (response.headers.get("content-type")?.split(";", 1)[0] ?? "").trim().toLowerCase();
|
|
134
|
+
const details = { url: url.toString(), format, mime, bytes: body.byteLength } satisfies WebFetchDetails;
|
|
135
|
+
if (mime.startsWith("image/") && mime !== "image/svg+xml") {
|
|
136
|
+
return {
|
|
137
|
+
content: [
|
|
138
|
+
{ type: "text", text: `Fetched image from ${url.toString()} (${mime})` },
|
|
139
|
+
{ type: "image", data: Buffer.from(body).toString("base64"), mimeType: mime },
|
|
140
|
+
],
|
|
141
|
+
details,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const raw = new TextDecoder().decode(body);
|
|
146
|
+
const isHtml = mime === "text/html" || mime === "application/xhtml+xml";
|
|
147
|
+
const output =
|
|
148
|
+
format === "html" ? raw : isHtml ? (format === "text" ? htmlToText(raw) : htmlToMarkdown(raw)) : raw;
|
|
149
|
+
const truncated = truncateToolOutput(output);
|
|
150
|
+
return {
|
|
151
|
+
content: [{ type: "text", text: truncated.text }],
|
|
152
|
+
details: {
|
|
153
|
+
...details,
|
|
154
|
+
...(truncated.truncation ? { truncation: truncated.truncation } : {}),
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
} catch (error) {
|
|
158
|
+
if (timeoutSignal.aborted && signal?.aborted !== true) {
|
|
159
|
+
throw new Error(`Web fetch timed out after ${timeout}s`);
|
|
160
|
+
}
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
renderCall(args, theme, context) {
|
|
165
|
+
rowState.watch(context.toolCallId, context.invalidate);
|
|
166
|
+
const text = (context.lastComponent as Text | undefined) ?? new Text("", 0, 0);
|
|
167
|
+
const title = formatToolRowTitle(rowState, context.toolCallId, "webfetch", theme);
|
|
168
|
+
text.setText(
|
|
169
|
+
`${title} ${theme.fg("accent", renderCallSummary(args) || "…")} ${theme.fg("muted", `(${args.format ?? "markdown"})`)}`,
|
|
170
|
+
);
|
|
171
|
+
return text;
|
|
172
|
+
},
|
|
173
|
+
renderResult(result, options, theme, context) {
|
|
174
|
+
return renderWebToolResult(result, options, theme, context);
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { StringEnum } from "@earendil-works/pi-ai";
|
|
2
|
+
import { defineTool, type TruncationResult } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { Text } from "@earendil-works/pi-tui";
|
|
4
|
+
import { type Static, Type } from "typebox";
|
|
5
|
+
import { formatToolRowTitle, type ToolRowStateStore } from "../../shared/tool-row-state.js";
|
|
6
|
+
import { callExa } from "./exa.ts";
|
|
7
|
+
import { clampInteger, normalizeTimeout } from "./limits.ts";
|
|
8
|
+
import { renderWebToolResult, truncateCallSummary, truncateToolOutput } from "./tool-output.ts";
|
|
9
|
+
|
|
10
|
+
const webSearchParams = Type.Object(
|
|
11
|
+
{
|
|
12
|
+
query: Type.String({ description: "Web search query" }),
|
|
13
|
+
numResults: Type.Optional(Type.Number({ description: "Number of results (default: 8, max: 12)" })),
|
|
14
|
+
livecrawl: Type.Optional(
|
|
15
|
+
StringEnum(["fallback", "preferred"] as const, { description: "Live-crawl mode (default: fallback)" }),
|
|
16
|
+
),
|
|
17
|
+
type: Type.Optional(StringEnum(["auto", "fast"] as const, { description: "Search type (default: auto)" })),
|
|
18
|
+
contextMaxCharacters: Type.Optional(Type.Number({ description: "Context character budget (500-30000)" })),
|
|
19
|
+
timeout: Type.Optional(Type.Number({ description: "Timeout in seconds (default: 25, max: 600)" })),
|
|
20
|
+
},
|
|
21
|
+
{ additionalProperties: false },
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
type WebSearchParams = Static<typeof webSearchParams>;
|
|
25
|
+
interface WebSearchDetails {
|
|
26
|
+
query: string;
|
|
27
|
+
numResults: number;
|
|
28
|
+
livecrawl: "fallback" | "preferred";
|
|
29
|
+
type: "auto" | "fast";
|
|
30
|
+
contextMaxCharacters?: number;
|
|
31
|
+
truncation?: TruncationResult;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function createWebSearchTool(rowState: ToolRowStateStore) {
|
|
35
|
+
return defineTool<typeof webSearchParams, WebSearchDetails | undefined>({
|
|
36
|
+
name: "websearch",
|
|
37
|
+
label: "Web Search",
|
|
38
|
+
description:
|
|
39
|
+
"Search the public web through Exa for current information and relevant pages. Output is truncated to 2,000 lines or 50 KB.",
|
|
40
|
+
promptSnippet: "Search the public web for current or external information",
|
|
41
|
+
promptGuidelines: [
|
|
42
|
+
"Use websearch for broad discovery, then webfetch when you have a specific URL.",
|
|
43
|
+
"Use codesearch for implementation-oriented code and documentation context.",
|
|
44
|
+
"Use a separate research workflow instead of websearch when several searches, fetches, and synthesis are needed.",
|
|
45
|
+
],
|
|
46
|
+
parameters: webSearchParams,
|
|
47
|
+
async execute(_toolCallId, params, signal, onUpdate) {
|
|
48
|
+
const timeout = normalizeTimeout(params.timeout, 25);
|
|
49
|
+
const numResults = clampInteger(params.numResults, 8, 1, 12);
|
|
50
|
+
const livecrawl = params.livecrawl ?? "fallback";
|
|
51
|
+
const type = params.type ?? "auto";
|
|
52
|
+
const contextMaxCharacters =
|
|
53
|
+
params.contextMaxCharacters === undefined
|
|
54
|
+
? undefined
|
|
55
|
+
: clampInteger(params.contextMaxCharacters, 500, 500, 30_000);
|
|
56
|
+
await onUpdate?.({ content: [{ type: "text", text: "Searching web..." }], details: undefined });
|
|
57
|
+
try {
|
|
58
|
+
const output =
|
|
59
|
+
(await callExa(
|
|
60
|
+
{
|
|
61
|
+
toolName: "web_search_exa",
|
|
62
|
+
arguments: { query: params.query, type, numResults, livecrawl, contextMaxCharacters },
|
|
63
|
+
},
|
|
64
|
+
signal,
|
|
65
|
+
timeout,
|
|
66
|
+
)) ?? "No search results found. Try a more specific query.";
|
|
67
|
+
const truncated = truncateToolOutput(output);
|
|
68
|
+
return {
|
|
69
|
+
content: [{ type: "text", text: truncated.text }],
|
|
70
|
+
details: {
|
|
71
|
+
query: params.query,
|
|
72
|
+
numResults,
|
|
73
|
+
livecrawl,
|
|
74
|
+
type,
|
|
75
|
+
...(contextMaxCharacters === undefined ? {} : { contextMaxCharacters }),
|
|
76
|
+
...(truncated.truncation ? { truncation: truncated.truncation } : {}),
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
} catch (error) {
|
|
80
|
+
if (error instanceof Error && error.name === "TimeoutError") {
|
|
81
|
+
throw new Error(`Web search timed out after ${timeout}s`);
|
|
82
|
+
}
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
renderCall(args: WebSearchParams, theme, context) {
|
|
87
|
+
rowState.watch(context.toolCallId, context.invalidate);
|
|
88
|
+
const text = (context.lastComponent as Text | undefined) ?? new Text("", 0, 0);
|
|
89
|
+
const title = formatToolRowTitle(rowState, context.toolCallId, "websearch", theme);
|
|
90
|
+
const summary = truncateCallSummary((args.query ?? "").trim()) || "…";
|
|
91
|
+
text.setText(
|
|
92
|
+
`${title} ${theme.fg("accent", summary)} ${theme.fg("muted", `(${args.type ?? "auto"}, n=${clampInteger(args.numResults, 8, 1, 12)})`)}`,
|
|
93
|
+
);
|
|
94
|
+
return text;
|
|
95
|
+
},
|
|
96
|
+
renderResult(result, options, theme, context) {
|
|
97
|
+
return renderWebToolResult(result, options, theme, context);
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@shanepadgett/tau-agent",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Tau is a custom agentic harness built with pi extensions",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"pi-package",
|
|
9
|
+
"tau",
|
|
10
|
+
"coding-agent"
|
|
11
|
+
],
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/shanepadgett/tau-agent.git",
|
|
15
|
+
"directory": "packages/agent"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"extensions/**/*",
|
|
22
|
+
"shared/**/*",
|
|
23
|
+
"prompts/**/*",
|
|
24
|
+
"skills/**/*",
|
|
25
|
+
"themes/**/*",
|
|
26
|
+
"docs/**/*",
|
|
27
|
+
"schemas/**/*",
|
|
28
|
+
"README.md"
|
|
29
|
+
],
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@shanepadgett/tau-tui": "0.1.0",
|
|
32
|
+
"@toon-format/toon": "2.3.0"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@earendil-works/pi-ai": "*",
|
|
36
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
37
|
+
"@earendil-works/pi-tui": "*",
|
|
38
|
+
"typebox": "*"
|
|
39
|
+
},
|
|
40
|
+
"pi": {
|
|
41
|
+
"extensions": [
|
|
42
|
+
"./extensions/*/index.ts"
|
|
43
|
+
],
|
|
44
|
+
"skills": [
|
|
45
|
+
"./skills"
|
|
46
|
+
],
|
|
47
|
+
"prompts": [
|
|
48
|
+
"./prompts"
|
|
49
|
+
],
|
|
50
|
+
"themes": [
|
|
51
|
+
"./themes"
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
"engines": {
|
|
55
|
+
"node": ">=22.19.0"
|
|
56
|
+
}
|
|
57
|
+
}
|
package/prompts/.gitkeep
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Compress prose while preserving meaning
|
|
3
|
+
argument-hint: "<target>"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Cavemanify $ARGUMENTS.
|
|
7
|
+
|
|
8
|
+
Compress prose. Preserve meaning.
|
|
9
|
+
|
|
10
|
+
Target is required. If missing, ask for target.
|
|
11
|
+
|
|
12
|
+
If target is file or directory, edit in place. If target is pasted text, return rewritten text in chat.
|
|
13
|
+
|
|
14
|
+
Rules:
|
|
15
|
+
|
|
16
|
+
- Drop filler, hedging, pleasantries, throat clearing, repeated examples.
|
|
17
|
+
- Keep technical terms, paths, commands, APIs, flags, regexes, constraints, quoted errors exact.
|
|
18
|
+
- Keep code blocks unchanged unless user explicitly asks to rewrite code.
|
|
19
|
+
- Keep decision logic and branching conditions.
|
|
20
|
+
- Keep frontmatter keys. Rewrite values only when meaning stays same.
|
|
21
|
+
- Long sentence -> short sentence or fragment.
|
|
22
|
+
- Paragraphs -> bullets when clearer.
|
|
23
|
+
- If editing scripts, change comments only unless user asks for code changes.
|
|
24
|
+
|
|
25
|
+
After file edits, report changed paths only.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Execute an approved simple or full implementation plan
|
|
3
|
+
argument-hint: "[slug-or-artifact-path]"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Implement: $ARGUMENTS
|
|
7
|
+
|
|
8
|
+
Resolve the slug or artifact path from the invocation or conversation. Ask one practical question when ambiguous.
|
|
9
|
+
|
|
10
|
+
Require either:
|
|
11
|
+
|
|
12
|
+
- One `docs/plans/<slug>.plan.md`; or
|
|
13
|
+
- Both `docs/plans/<slug>.spec.md` and `docs/plans/<slug>.technical.md`.
|
|
14
|
+
|
|
15
|
+
This invocation authorizes execution of the identified artifacts. Read them in full. For a full plan, read the spec before the technical plan.
|
|
16
|
+
|
|
17
|
+
Stop on conflicting artifacts, unresolved decisions, or code evidence that invalidates the planned design. Do not invent public behavior or silently redesign. State the conflict and ask for a decision.
|
|
18
|
+
|
|
19
|
+
Implement only the planned scope. Follow the technical plan's boundaries and order. Read additional code only when a named owner, direct dependency, or contradiction requires it.
|
|
20
|
+
|
|
21
|
+
Choose the lowest-token accurate mutation path. Use the available patch tool for hand-authored changes. Use standard-library scripts, native tools, or shell commands for mechanical bulk changes when clearer and safer. Inspect generated or scripted results before further hand edits.
|
|
22
|
+
|
|
23
|
+
Remove replaced code, stale references, unused exports, and obsolete resources in the same change. Preserve validation, data safety, security, accessibility, explicit requirements, and hardware calibration.
|
|
24
|
+
|
|
25
|
+
After implementation, report only non-obvious caveats. Do not restate the plan.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Interview the user one question at a time until shared understanding is complete
|
|
3
|
+
argument-hint: "<topic>"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Run a structured interview about: $ARGUMENTS
|
|
7
|
+
|
|
8
|
+
The argument is the interview subject. If it is empty, ask for the subject before setup.
|
|
9
|
+
|
|
10
|
+
Ask only one question at a time.
|
|
11
|
+
|
|
12
|
+
First search `.agents/interviews/` for a matching interview. If one exists, ask whether to resume it or start fresh. For a resume, read `decisions.md`, `open-questions.md`, and `scratchpad.md`; briefly restate the goal, exit condition, and current state; then continue at the next open question.
|
|
13
|
+
|
|
14
|
+
For a fresh interview:
|
|
15
|
+
|
|
16
|
+
1. Confirm the interview goal: what user and agent must agree on.
|
|
17
|
+
2. Confirm the exit condition: which resolved decisions mean the interview is done. Keep it separate from the goal.
|
|
18
|
+
3. Derive a concise kebab-case slug only after the goal and exit condition are confirmed. Create `.agents/interviews/<slug>/` with:
|
|
19
|
+
|
|
20
|
+
```markdown
|
|
21
|
+
# decisions.md
|
|
22
|
+
|
|
23
|
+
## Goal
|
|
24
|
+
|
|
25
|
+
<what must be understood>
|
|
26
|
+
|
|
27
|
+
## Exit Condition
|
|
28
|
+
|
|
29
|
+
<what must be resolved for the interview to end>
|
|
30
|
+
|
|
31
|
+
## Decisions
|
|
32
|
+
|
|
33
|
+
- None yet.
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Create `open-questions.md` for unresolved questions that surface and `scratchpad.md` for useful ideas, observations, and things to revisit that fit neither decisions nor open questions. `decisions.md` contains concrete, confirmed, fully reconciled decisions only. It is never a Q&A log.
|
|
37
|
+
|
|
38
|
+
Question loop:
|
|
39
|
+
|
|
40
|
+
1. Pick the most important open question or unresolved point toward the exit condition. Inspect relevant material first when it informs the question. Findings do not settle a decision without user agreement.
|
|
41
|
+
2. Ask exactly one question. Use yes/no for binary decisions. Use multiple choice only for a known complete set of valid alternatives: list every genuinely valid option, whether that means two or ten. Do not pad the list, add decoys, or omit a valid path. Use open-ended questions when the user's framing matters.
|
|
42
|
+
3. Give a recommendation after the question or options. Explain the tradeoff in one or two sentences. Compare real tradeoffs when several choices are good.
|
|
43
|
+
4. The user may think aloud, explore, or raise another topic. Respond and explore when useful. Capture legitimate new questions in `open-questions.md` and useful ideas in `scratchpad.md`, then return to the current question.
|
|
44
|
+
5. Update a record only when new information belongs in it: concrete confirmed decisions in `decisions.md`; unresolved questions in `open-questions.md`; useful remainder in `scratchpad.md`. Do not edit records merely because a response arrived.
|
|
45
|
+
6. If a response contradicts a decision, surface and reconcile the conflict before recording it. Revise or remove stale decisions, or reopen the point. If the correct resolution is unclear, leave it open.
|
|
46
|
+
7. If the goal or exit condition shifts, confirm the shift with the user and update `decisions.md`.
|
|
47
|
+
|
|
48
|
+
Finish only when the exit condition is met and `open-questions.md` is empty. Sweep `scratchpad.md` for final questions or decisions, summarize `decisions.md`, and confirm shared understanding. Do not chase unrelated edge cases after that.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Plan a feature as a brief implementation plan or full behavioral spec
|
|
3
|
+
argument-hint: "[topic-or-plan]"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Plan the feature described by: $ARGUMENTS
|
|
7
|
+
|
|
8
|
+
Begin with rough conversation. Establish what the user means before searching plan files or asking pointed questions. If continuation status remains unclear after framing, ask whether this is new work or an existing plan. Read only the plan the user identifies.
|
|
9
|
+
|
|
10
|
+
Confirm these separately:
|
|
11
|
+
|
|
12
|
+
1. Goal: the feature outcome.
|
|
13
|
+
2. Exit condition: what must be resolved and documented before feature planning ends.
|
|
14
|
+
|
|
15
|
+
Inspect only current behavior, constraints, and user-facing seams relevant to that boundary. Confirm the feature needs to exist. Cut speculative scope.
|
|
16
|
+
|
|
17
|
+
Choose the smallest sufficient planning path with the user:
|
|
18
|
+
|
|
19
|
+
- Simple work: write `docs/plans/<slug>.plan.md`. Keep it brief and self-contained. Include the goal, behavior, scope, relevant code, implementation direction, edge cases, and completion criteria. This artifact goes directly to `/implement`.
|
|
20
|
+
- Full work: use `docs/plans/<slug>.scratch.md` for open questions, temporary research, and unsettled material. Write `docs/plans/<slug>.spec.md` as EARS-style system-behavior statements. This artifact goes to `/plan-implementation`.
|
|
21
|
+
|
|
22
|
+
Derive a concise kebab-case slug after the work is framed. Planning state belongs only in those `docs/plans/` artifacts.
|
|
23
|
+
|
|
24
|
+
Once the problem boundary is clear enough, ask one pointed question at a time. Inspect relevant material before asking when it can sharpen the question. Planning is not a Q&A transcript. Promote only reconciled truth into the plan or spec. Keep unresolved questions and temporary research in scratch. Continuously prune scratch as material is settled.
|
|
25
|
+
|
|
26
|
+
The full spec defines required system behavior. Exclude PRD material, implementation design, unresolved choices, transcripts, and stale research.
|
|
27
|
+
|
|
28
|
+
Finish only when feature outcomes, boundaries, and completion criteria are clear. For full planning, every scratch item must be promoted, discarded with user agreement, or moved to an agreed destination. Leave no unresolved or leftover scratch material.
|
|
29
|
+
|
|
30
|
+
Write substantive content into the artifacts. Return only a short chat summary and the handoff command.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Turn a feature spec into an executable technical design
|
|
3
|
+
argument-hint: "[slug-or-spec-path]"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Plan implementation for: $ARGUMENTS
|
|
7
|
+
|
|
8
|
+
Begin with rough conversational framing. Establish what the user means before searching plan files. If continuation status remains unclear after framing, ask which work to continue. Read only the plan the user identifies.
|
|
9
|
+
|
|
10
|
+
Require one `docs/plans/<slug>.spec.md`. Stop if it is missing, contradictory, or leaves feature behavior undecided. Simple `docs/plans/<slug>.plan.md` artifacts go directly to `/implement`.
|
|
11
|
+
|
|
12
|
+
Confirm these separately:
|
|
13
|
+
|
|
14
|
+
1. Goal: the technical design to produce.
|
|
15
|
+
2. Exit condition: what must be resolved before a fresh implementation chat can execute it.
|
|
16
|
+
|
|
17
|
+
Reuse `docs/plans/<slug>.scratch.md` for open questions, repository findings, and temporary design work. Research only the framed feature's code ownership, callers where needed, repository patterns, dependencies, refactor opportunities, and concrete implementation paths. Ask one pointed question at a time after the boundary is clear. Continuously promote settled material and prune scratch.
|
|
18
|
+
|
|
19
|
+
Climb the Code Ladder. Stop at the first rung that satisfies the spec cleanly:
|
|
20
|
+
|
|
21
|
+
1. Reuse code or a repository pattern that already exists.
|
|
22
|
+
2. Make a small refactor first when it removes special cases or makes the change safer and clearer.
|
|
23
|
+
3. Use the standard library.
|
|
24
|
+
4. Use the native platform.
|
|
25
|
+
5. Use an existing dependency.
|
|
26
|
+
6. Use one line when one line is clear and correct.
|
|
27
|
+
7. Otherwise, write the smallest code that works.
|
|
28
|
+
|
|
29
|
+
For non-trivial structure, design it twice. Compare the current or first viable shape with at least one serious alternative. Choose by concrete ownership, control flow, change surface, and failure behavior.
|
|
30
|
+
|
|
31
|
+
Keep implementation shape plain. Every file, function, class, interface, setting, command, and abstraction must earn its boundary through naming, reuse, or a simpler caller. Avoid one-implementation interfaces, one-product factories, empty wrappers, fixed-value configuration, and scaffolding for hypothetical work. Prefer focused files whose names make repository search useful. Refactor only where it reduces the planned change or removes special cases.
|
|
32
|
+
|
|
33
|
+
Write `docs/plans/<slug>.technical.md` with:
|
|
34
|
+
|
|
35
|
+
- Goal and completion condition.
|
|
36
|
+
- Chosen design plus serious alternatives and tradeoffs.
|
|
37
|
+
- Exact files and owners.
|
|
38
|
+
- Data and control flow.
|
|
39
|
+
- Existing code to reuse and any required refactors.
|
|
40
|
+
- Replaced code and resources to delete.
|
|
41
|
+
- Implementation order.
|
|
42
|
+
- Edge cases and boundaries.
|
|
43
|
+
- Validation.
|
|
44
|
+
- Exact code references where useful.
|
|
45
|
+
|
|
46
|
+
Make it self-contained and precise enough for a less-capable agent without prescribing every line of code.
|
|
47
|
+
|
|
48
|
+
Finish only when no basic design question remains, `technical.md` can guide a fresh implementation chat, and every scratch item has been promoted, discarded with user agreement, or moved to an agreed destination. Leave no unresolved or leftover scratch material.
|
|
49
|
+
|
|
50
|
+
Write substantive content into the artifacts. Return only a short chat summary and the `/implement` handoff.
|