@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,226 @@
|
|
|
1
|
+
import type { Stats } from "node:fs";
|
|
2
|
+
import { lstat, readdir, readFile } from "node:fs/promises";
|
|
3
|
+
import { dirname, join, matchesGlob, resolve, sep } from "node:path";
|
|
4
|
+
import { formatPathForDisplay, isWithinPath, relativeSlash, toSlashPath } from "./path-display.ts";
|
|
5
|
+
|
|
6
|
+
export type PathKind = "file" | "dir";
|
|
7
|
+
|
|
8
|
+
export interface TraversalEntry {
|
|
9
|
+
absolutePath: string;
|
|
10
|
+
displayPath: string;
|
|
11
|
+
type: PathKind;
|
|
12
|
+
stats: Stats;
|
|
13
|
+
depth: number;
|
|
14
|
+
empty?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface CollectPathOptions {
|
|
18
|
+
cwd: string;
|
|
19
|
+
root: string;
|
|
20
|
+
maxDepth?: number;
|
|
21
|
+
includeRoot: boolean;
|
|
22
|
+
includeHidden: boolean;
|
|
23
|
+
includeIgnored: boolean;
|
|
24
|
+
includeNoise: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface IgnoreRule {
|
|
28
|
+
baseDir: string;
|
|
29
|
+
pattern: string;
|
|
30
|
+
negated: boolean;
|
|
31
|
+
dirOnly: boolean;
|
|
32
|
+
hasSlash: boolean;
|
|
33
|
+
anchored: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const NOISE_PATH_NAMES = new Set([
|
|
37
|
+
".git",
|
|
38
|
+
"node_modules",
|
|
39
|
+
"dist",
|
|
40
|
+
"build",
|
|
41
|
+
"coverage",
|
|
42
|
+
".cache",
|
|
43
|
+
".next",
|
|
44
|
+
".turbo",
|
|
45
|
+
".parcel-cache",
|
|
46
|
+
"out",
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
function entryKind(stats: Stats): PathKind {
|
|
50
|
+
return stats.isDirectory() ? "dir" : "file";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function pathSegments(displayPath: string): string[] {
|
|
54
|
+
return displayPath.split("/").filter((segment) => segment.length > 0 && segment !== ".");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function hasHiddenPathSegment(displayPath: string): boolean {
|
|
58
|
+
return pathSegments(displayPath).some((segment) => segment.startsWith("."));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function hasNoisePathSegment(displayPath: string): boolean {
|
|
62
|
+
return pathSegments(displayPath).some((segment) => NOISE_PATH_NAMES.has(segment));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function parseIgnoreLine(baseDir: string, rawLine: string): IgnoreRule | undefined {
|
|
66
|
+
let line = rawLine.trim();
|
|
67
|
+
if (!line || line.startsWith("#")) return undefined;
|
|
68
|
+
const negated = line.startsWith("!");
|
|
69
|
+
if (negated) line = line.slice(1);
|
|
70
|
+
if (!line) return undefined;
|
|
71
|
+
const dirOnly = line.endsWith("/");
|
|
72
|
+
if (dirOnly) line = line.slice(0, -1);
|
|
73
|
+
const anchored = line.startsWith("/");
|
|
74
|
+
if (anchored) line = line.slice(1);
|
|
75
|
+
line = toSlashPath(line);
|
|
76
|
+
if (!line) return undefined;
|
|
77
|
+
return { baseDir, pattern: line, negated, dirOnly, hasSlash: line.includes("/"), anchored };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function readIgnoreRules(baseDir: string): Promise<IgnoreRule[]> {
|
|
81
|
+
try {
|
|
82
|
+
const content = await readFile(join(baseDir, ".gitignore"), "utf8");
|
|
83
|
+
return content
|
|
84
|
+
.split(/\r?\n/)
|
|
85
|
+
.map((line) => parseIgnoreLine(baseDir, line))
|
|
86
|
+
.filter((rule): rule is IgnoreRule => rule !== undefined);
|
|
87
|
+
} catch {
|
|
88
|
+
return [];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function appendIgnoreRulesForDirectory(
|
|
93
|
+
directory: string,
|
|
94
|
+
inherited: readonly IgnoreRule[],
|
|
95
|
+
): Promise<IgnoreRule[]> {
|
|
96
|
+
const rules = await readIgnoreRules(directory);
|
|
97
|
+
return rules.length === 0 ? [...inherited] : [...inherited, ...rules];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function collectIgnoreRulesToDirectory(cwd: string, directory: string): Promise<IgnoreRule[]> {
|
|
101
|
+
const resolvedCwd = resolve(cwd);
|
|
102
|
+
const resolvedDirectory = resolve(directory);
|
|
103
|
+
if (!isWithinPath(resolvedCwd, resolvedDirectory)) return [];
|
|
104
|
+
|
|
105
|
+
let current = resolvedCwd;
|
|
106
|
+
let rules: IgnoreRule[] = await appendIgnoreRulesForDirectory(current, []);
|
|
107
|
+
const rel = relativeSlash(resolvedCwd, resolvedDirectory);
|
|
108
|
+
if (rel === ".") return rules;
|
|
109
|
+
|
|
110
|
+
for (const segment of rel.split("/")) {
|
|
111
|
+
current = join(current, segment.split("/").join(sep));
|
|
112
|
+
rules = await appendIgnoreRulesForDirectory(current, rules);
|
|
113
|
+
}
|
|
114
|
+
return rules;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function ignoreRuleMatches(rule: IgnoreRule, absolutePath: string, kind: PathKind): boolean {
|
|
118
|
+
if (rule.dirOnly && kind !== "dir") return false;
|
|
119
|
+
const rel = relativeSlash(rule.baseDir, absolutePath);
|
|
120
|
+
if (rel === "." || rel.startsWith("..")) return false;
|
|
121
|
+
if (rule.hasSlash || rule.anchored) return matchesGlob(rel, rule.pattern);
|
|
122
|
+
return rel.split("/").some((segment) => matchesGlob(segment, rule.pattern));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function isIgnored(absolutePath: string, kind: PathKind, rules: readonly IgnoreRule[]): boolean {
|
|
126
|
+
let ignored = false;
|
|
127
|
+
for (const rule of rules) {
|
|
128
|
+
if (ignoreRuleMatches(rule, absolutePath, kind)) ignored = !rule.negated;
|
|
129
|
+
}
|
|
130
|
+
return ignored;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function shouldSkipPath(
|
|
134
|
+
absolutePath: string,
|
|
135
|
+
kind: PathKind,
|
|
136
|
+
rules: readonly IgnoreRule[],
|
|
137
|
+
options: CollectPathOptions,
|
|
138
|
+
): boolean {
|
|
139
|
+
const displayPath = formatPathForDisplay(absolutePath, options.cwd);
|
|
140
|
+
if (!options.includeHidden && hasHiddenPathSegment(displayPath)) return true;
|
|
141
|
+
if (!options.includeNoise && hasNoisePathSegment(displayPath)) return true;
|
|
142
|
+
return !options.includeIgnored && isIgnored(absolutePath, kind, rules);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function sortEntries(entries: TraversalEntry[]): TraversalEntry[] {
|
|
146
|
+
return entries.sort((a, b) => {
|
|
147
|
+
if (a.type !== b.type) return a.type === "dir" ? -1 : 1;
|
|
148
|
+
return a.displayPath.localeCompare(b.displayPath);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function readDirectoryNames(directory: string, cwd: string): Promise<string[]> {
|
|
153
|
+
try {
|
|
154
|
+
return await readdir(directory);
|
|
155
|
+
} catch {
|
|
156
|
+
throw new Error(`Cannot read directory: ${formatPathForDisplay(directory, cwd)}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export async function collectPaths(options: CollectPathOptions): Promise<TraversalEntry[]> {
|
|
161
|
+
const entries: TraversalEntry[] = [];
|
|
162
|
+
const entryByPath = new Map<string, TraversalEntry>();
|
|
163
|
+
const rootStats = await lstat(options.root);
|
|
164
|
+
const rootKind = entryKind(rootStats);
|
|
165
|
+
const rootDirectory = rootKind === "dir" ? options.root : dirname(options.root);
|
|
166
|
+
const rootRules = options.includeIgnored ? [] : await collectIgnoreRulesToDirectory(options.cwd, rootDirectory);
|
|
167
|
+
|
|
168
|
+
function pushEntry(absolutePath: string, stats: Stats, depth: number): TraversalEntry {
|
|
169
|
+
const entry = {
|
|
170
|
+
absolutePath,
|
|
171
|
+
displayPath: formatPathForDisplay(absolutePath, options.cwd),
|
|
172
|
+
type: entryKind(stats),
|
|
173
|
+
stats,
|
|
174
|
+
depth,
|
|
175
|
+
};
|
|
176
|
+
entries.push(entry);
|
|
177
|
+
entryByPath.set(absolutePath, entry);
|
|
178
|
+
return entry;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (options.includeRoot) pushEntry(options.root, rootStats, 0);
|
|
182
|
+
if (rootKind !== "dir") return entries;
|
|
183
|
+
|
|
184
|
+
async function walkDirectory(directory: string, depth: number, rules: readonly IgnoreRule[]): Promise<number> {
|
|
185
|
+
if (options.maxDepth !== undefined && depth >= options.maxDepth) return 0;
|
|
186
|
+
|
|
187
|
+
const names = await readDirectoryNames(directory, options.cwd);
|
|
188
|
+
|
|
189
|
+
const children: TraversalEntry[] = [];
|
|
190
|
+
for (const name of names) {
|
|
191
|
+
const childPath = `${directory}/${name}`;
|
|
192
|
+
const stats = await lstat(childPath);
|
|
193
|
+
const kind = entryKind(stats);
|
|
194
|
+
if (shouldSkipPath(childPath, kind, rules, options)) continue;
|
|
195
|
+
children.push({
|
|
196
|
+
absolutePath: childPath,
|
|
197
|
+
displayPath: formatPathForDisplay(childPath, options.cwd),
|
|
198
|
+
type: kind,
|
|
199
|
+
stats,
|
|
200
|
+
depth: depth + 1,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
for (const child of sortEntries(children)) {
|
|
205
|
+
entries.push(child);
|
|
206
|
+
entryByPath.set(child.absolutePath, child);
|
|
207
|
+
if (child.type !== "dir") continue;
|
|
208
|
+
const childRules = options.includeIgnored
|
|
209
|
+
? rules
|
|
210
|
+
: await appendIgnoreRulesForDirectory(child.absolutePath, rules);
|
|
211
|
+
const visibleChildren = await walkDirectory(child.absolutePath, child.depth, childRules);
|
|
212
|
+
if (visibleChildren === 0 && (options.maxDepth === undefined || child.depth < options.maxDepth)) {
|
|
213
|
+
child.empty = true;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return children.length;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const visibleChildren = await walkDirectory(options.root, 0, rootRules);
|
|
221
|
+
const rootEntry = entryByPath.get(options.root);
|
|
222
|
+
if (rootEntry && visibleChildren === 0 && (options.maxDepth === undefined || options.maxDepth > 0)) {
|
|
223
|
+
rootEntry.empty = true;
|
|
224
|
+
}
|
|
225
|
+
return entries;
|
|
226
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Footer Extension
|
|
2
|
+
|
|
3
|
+
Replaces Pi’s default footer with Tau’s compact two-line footer.
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
/footer
|
|
9
|
+
/footer on
|
|
10
|
+
/footer off
|
|
11
|
+
/footer refresh
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Layout
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
git • model • thinking S $0.18 · D $2.43
|
|
18
|
+
cwd • session footer items
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Other Tau extensions can publish bottom-right items with `setTauFooterItem()` from `shared/events.ts (or public event bus docs)`.
|
|
22
|
+
|
|
23
|
+
No polling. Git and daily cost refresh on lifecycle events or `/footer refresh`.
|