@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.
Files changed (138) hide show
  1. package/README.md +30 -0
  2. package/docs/extending-tau-agent.md +119 -0
  3. package/docs/subagents.md +57 -0
  4. package/docs/tui.md +107 -0
  5. package/extensions/appshot/README.md +11 -0
  6. package/extensions/appshot/capture.swift +191 -0
  7. package/extensions/appshot/index.ts +245 -0
  8. package/extensions/appshot/native-helper.ts +102 -0
  9. package/extensions/attention/README.md +23 -0
  10. package/extensions/attention/index.ts +71 -0
  11. package/extensions/auto-name/README.md +26 -0
  12. package/extensions/auto-name/index.ts +163 -0
  13. package/extensions/branch/README.md +22 -0
  14. package/extensions/branch/index.ts +145 -0
  15. package/extensions/branch/panel.ts +148 -0
  16. package/extensions/clear-screen/README.md +29 -0
  17. package/extensions/clear-screen/index.ts +75 -0
  18. package/extensions/commit/README.md +15 -0
  19. package/extensions/commit/commit-plan.ts +328 -0
  20. package/extensions/commit/git-change-set.ts +268 -0
  21. package/extensions/commit/index.ts +138 -0
  22. package/extensions/commit/review-ui.ts +745 -0
  23. package/extensions/explore/README.md +7 -0
  24. package/extensions/explore/autoread.ts +140 -0
  25. package/extensions/explore/find.ts +147 -0
  26. package/extensions/explore/grep.ts +749 -0
  27. package/extensions/explore/index.ts +17 -0
  28. package/extensions/explore/limits.ts +22 -0
  29. package/extensions/explore/ls.ts +113 -0
  30. package/extensions/explore/path-display.ts +41 -0
  31. package/extensions/explore/path-tree.ts +150 -0
  32. package/extensions/explore/read.ts +160 -0
  33. package/extensions/explore/result.ts +29 -0
  34. package/extensions/explore/traverse.ts +226 -0
  35. package/extensions/footer/README.md +23 -0
  36. package/extensions/footer/index.ts +534 -0
  37. package/extensions/footer/settings.ts +13 -0
  38. package/extensions/ideas/README.md +10 -0
  39. package/extensions/ideas/browser.ts +66 -0
  40. package/extensions/ideas/index.ts +31 -0
  41. package/extensions/ideas/store.ts +32 -0
  42. package/extensions/image-gen/README.md +11 -0
  43. package/extensions/image-gen/client.ts +186 -0
  44. package/extensions/image-gen/index.ts +152 -0
  45. package/extensions/manage-sessions/README.md +12 -0
  46. package/extensions/manage-sessions/index.ts +58 -0
  47. package/extensions/manage-sessions/manager-ui.ts +323 -0
  48. package/extensions/manage-sessions/sessions.ts +126 -0
  49. package/extensions/patch/README.md +48 -0
  50. package/extensions/patch/executor.ts +355 -0
  51. package/extensions/patch/index.ts +170 -0
  52. package/extensions/patch/matcher.ts +236 -0
  53. package/extensions/patch/parser.ts +347 -0
  54. package/extensions/patch/render.ts +247 -0
  55. package/extensions/patch/summary.ts +35 -0
  56. package/extensions/publish/README.md +11 -0
  57. package/extensions/publish/index.ts +225 -0
  58. package/extensions/qna/README.md +45 -0
  59. package/extensions/qna/additional-context-body.ts +51 -0
  60. package/extensions/qna/body-render.ts +6 -0
  61. package/extensions/qna/choice-question-body.ts +322 -0
  62. package/extensions/qna/index.ts +245 -0
  63. package/extensions/qna/inline-editor-row.ts +56 -0
  64. package/extensions/qna/input-question-body.ts +89 -0
  65. package/extensions/qna/model.ts +317 -0
  66. package/extensions/qna/panel.ts +240 -0
  67. package/extensions/qna/ui.ts +16 -0
  68. package/extensions/reference/README.md +47 -0
  69. package/extensions/reference/index.ts +76 -0
  70. package/extensions/reference/panel.ts +874 -0
  71. package/extensions/reference/settings.ts +29 -0
  72. package/extensions/run-summary/README.md +5 -0
  73. package/extensions/run-summary/index.ts +98 -0
  74. package/extensions/silent-command-runner/README.md +25 -0
  75. package/extensions/silent-command-runner/index.ts +468 -0
  76. package/extensions/silent-command-runner/settings.ts +65 -0
  77. package/extensions/soul/README.md +19 -0
  78. package/extensions/soul/index.ts +20 -0
  79. package/extensions/soul/prompt.ts +254 -0
  80. package/extensions/soul/settings.ts +13 -0
  81. package/extensions/stash/README.md +48 -0
  82. package/extensions/stash/browser.ts +51 -0
  83. package/extensions/stash/index.ts +55 -0
  84. package/extensions/stash/store.ts +36 -0
  85. package/extensions/subagent/README.md +34 -0
  86. package/extensions/subagent/agents/scout.md +85 -0
  87. package/extensions/subagent/agents/web-research.md +95 -0
  88. package/extensions/subagent/agents.ts +196 -0
  89. package/extensions/subagent/index.ts +214 -0
  90. package/extensions/subagent/render.ts +89 -0
  91. package/extensions/subagent/run.ts +325 -0
  92. package/extensions/tau/README.md +17 -0
  93. package/extensions/tau/index.ts +122 -0
  94. package/extensions/tau-help/README.md +3 -0
  95. package/extensions/tau-help/help.md +125 -0
  96. package/extensions/tau-help/index.ts +68 -0
  97. package/extensions/turn-budget/README.md +14 -0
  98. package/extensions/turn-budget/index.ts +150 -0
  99. package/extensions/turn-budget/settings.ts +35 -0
  100. package/extensions/web/README.md +13 -0
  101. package/extensions/web/codesearch.ts +79 -0
  102. package/extensions/web/exa.ts +101 -0
  103. package/extensions/web/html.ts +67 -0
  104. package/extensions/web/index.ts +13 -0
  105. package/extensions/web/limits.ts +11 -0
  106. package/extensions/web/tool-output.ts +54 -0
  107. package/extensions/web/webfetch.ts +177 -0
  108. package/extensions/web/websearch.ts +100 -0
  109. package/package.json +57 -0
  110. package/prompts/.gitkeep +1 -0
  111. package/prompts/cavemanify.md +25 -0
  112. package/prompts/implement.md +25 -0
  113. package/prompts/interview.md +48 -0
  114. package/prompts/plan-feature.md +30 -0
  115. package/prompts/plan-implementation.md +50 -0
  116. package/schemas/tau.schema.json +205 -0
  117. package/shared/agent-blocked.ts +12 -0
  118. package/shared/description.ts +28 -0
  119. package/shared/events.ts +135 -0
  120. package/shared/git.ts +41 -0
  121. package/shared/injected-context.ts +69 -0
  122. package/shared/jsonl-store.ts +108 -0
  123. package/shared/model-fallback/index.ts +278 -0
  124. package/shared/model-fallback/settings.ts +18 -0
  125. package/shared/model-fallback/types.ts +8 -0
  126. package/shared/ranges.ts +21 -0
  127. package/shared/settings/define.ts +15 -0
  128. package/shared/settings/files.ts +20 -0
  129. package/shared/settings/json.ts +41 -0
  130. package/shared/settings/load.ts +59 -0
  131. package/shared/settings/merge.ts +21 -0
  132. package/shared/settings/paths.ts +41 -0
  133. package/shared/settings/schema.ts +25 -0
  134. package/shared/settings/specs.ts +23 -0
  135. package/shared/text.ts +11 -0
  136. package/shared/tool-row-state.ts +43 -0
  137. package/skills/writing-preferences/SKILL.md +136 -0
  138. 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`.