tau-coding-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 (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +42 -0
  3. package/extensions/answer.ts +601 -0
  4. package/extensions/branch-term.ts +405 -0
  5. package/extensions/btw.ts +444 -0
  6. package/extensions/ghostty.ts +301 -0
  7. package/extensions/git-diff-stats.ts +277 -0
  8. package/extensions/git-pr-status.ts +286 -0
  9. package/extensions/insights.ts +2367 -0
  10. package/extensions/interlude.ts +144 -0
  11. package/extensions/loop.ts +529 -0
  12. package/extensions/memory.ts +1889 -0
  13. package/extensions/notify.ts +161 -0
  14. package/extensions/openai-fast.ts +227 -0
  15. package/extensions/openai-verbosity.ts +223 -0
  16. package/extensions/review.ts +4347 -0
  17. package/extensions/sandbox/index.ts +2578 -0
  18. package/extensions/usage/anthropic.ts +198 -0
  19. package/extensions/usage/github-copilot.ts +204 -0
  20. package/extensions/usage/google-gemini-cli.ts +232 -0
  21. package/extensions/usage/index.ts +2310 -0
  22. package/extensions/usage/minimax.ts +208 -0
  23. package/extensions/usage/openai-codex.ts +180 -0
  24. package/extensions/usage/openrouter.ts +168 -0
  25. package/extensions/usage/providers.ts +26 -0
  26. package/extensions/usage/shared.ts +156 -0
  27. package/extensions/usage/types.ts +53 -0
  28. package/extensions/usage/zai.ts +186 -0
  29. package/extensions/websearch/README.md +68 -0
  30. package/extensions/websearch/browser/chromium.ts +330 -0
  31. package/extensions/websearch/browser/discovery.ts +75 -0
  32. package/extensions/websearch/browser/firefox.ts +150 -0
  33. package/extensions/websearch/browser/sqlite.ts +38 -0
  34. package/extensions/websearch/config.ts +76 -0
  35. package/extensions/websearch/index.ts +312 -0
  36. package/extensions/websearch/normalize.ts +155 -0
  37. package/extensions/websearch/providers/anthropic.pi.ts +139 -0
  38. package/extensions/websearch/providers/gemini.browser.ts +185 -0
  39. package/extensions/websearch/providers/gemini.pi.ts +108 -0
  40. package/extensions/websearch/providers/openai-codex.browser.ts +77 -0
  41. package/extensions/websearch/providers/openai-codex.pi.ts +49 -0
  42. package/extensions/websearch/providers/openai-codex.shared.ts +123 -0
  43. package/extensions/websearch/providers/pi-model.shared.ts +86 -0
  44. package/extensions/websearch/providers/search-prompt.shared.ts +17 -0
  45. package/extensions/websearch/providers/shared.ts +76 -0
  46. package/extensions/websearch/types.ts +52 -0
  47. package/extensions/worktree.ts +2223 -0
  48. package/package.json +72 -0
  49. package/skills/browser-tools/SKILL.md +253 -0
  50. package/skills/browser-tools/scripts/browser-content.js +100 -0
  51. package/skills/browser-tools/scripts/browser-cookies.js +33 -0
  52. package/skills/browser-tools/scripts/browser-dismiss-cookies.js +455 -0
  53. package/skills/browser-tools/scripts/browser-eval.js +75 -0
  54. package/skills/browser-tools/scripts/browser-logs-tail.js +89 -0
  55. package/skills/browser-tools/scripts/browser-nav.js +28 -0
  56. package/skills/browser-tools/scripts/browser-net-summary.js +115 -0
  57. package/skills/browser-tools/scripts/browser-pick.js +133 -0
  58. package/skills/browser-tools/scripts/browser-screenshot.js +18 -0
  59. package/skills/browser-tools/scripts/browser-start.js +281 -0
  60. package/skills/browser-tools/scripts/browser-watch.js +321 -0
  61. package/skills/browser-tools/scripts/utils.js +60 -0
  62. package/skills/git-clean-history/SKILL.md +45 -0
  63. package/skills/git-commit/SKILL.md +55 -0
  64. package/skills/oracle/SKILL.md +64 -0
  65. package/skills/oracle/scripts/oracle +249 -0
  66. package/skills/oracle/scripts/oracle-bundle +227 -0
  67. package/skills/sentry/SKILL.md +198 -0
  68. package/skills/sentry/lib/auth.js +99 -0
  69. package/skills/sentry/scripts/fetch-event.js +325 -0
  70. package/skills/sentry/scripts/fetch-issue.js +368 -0
  71. package/skills/sentry/scripts/list-issues.js +245 -0
  72. package/skills/sentry/scripts/search-events.js +291 -0
  73. package/skills/sentry/scripts/search-logs.js +234 -0
  74. package/skills/update-changelog/SKILL.md +135 -0
  75. package/skills/web-design/SKILL.md +117 -0
@@ -0,0 +1,99 @@
1
+ import { readFileSync, existsSync } from "fs";
2
+ import { homedir } from "os";
3
+ import { join } from "path";
4
+
5
+ export const SENTRY_API_BASE = "https://sentry.io/api/0";
6
+
7
+ // Cache for project slug -> ID resolution
8
+ const projectIdCache = new Map();
9
+
10
+ /**
11
+ * Get auth token from ~/.sentryclirc
12
+ * @returns {string} The auth token
13
+ */
14
+ export function getAuthToken() {
15
+ const rcPath = join(homedir(), ".sentryclirc");
16
+ if (!existsSync(rcPath)) {
17
+ console.error("Error: ~/.sentryclirc not found");
18
+ console.error("Provide a Sentry auth token via ~/.sentryclirc (user-managed).");
19
+ process.exit(1);
20
+ }
21
+
22
+ const content = readFileSync(rcPath, "utf-8");
23
+ const match = content.match(/token\s*=\s*(.+)/);
24
+ if (!match) {
25
+ console.error("Error: No token found in ~/.sentryclirc");
26
+ process.exit(1);
27
+ }
28
+ return match[1].trim();
29
+ }
30
+
31
+ /**
32
+ * Fetch JSON from a Sentry API endpoint
33
+ * @param {string} url - The full URL to fetch
34
+ * @param {string} token - The auth token
35
+ * @returns {Promise<any>} The parsed JSON response
36
+ */
37
+ export async function fetchJson(url, token) {
38
+ const res = await fetch(url, {
39
+ headers: {
40
+ Authorization: `Bearer ${token}`,
41
+ },
42
+ });
43
+
44
+ if (!res.ok) {
45
+ const text = await res.text();
46
+ throw new Error(`API error ${res.status}: ${text}`);
47
+ }
48
+
49
+ return res.json();
50
+ }
51
+
52
+ /**
53
+ * Format a timestamp for display
54
+ * @param {string|number} ts - Timestamp (ISO string or unix)
55
+ * @returns {string} Formatted timestamp
56
+ */
57
+ export function formatTimestamp(ts) {
58
+ if (!ts) return "N/A";
59
+ try {
60
+ const date = new Date(ts);
61
+ if (isNaN(date.getTime())) return ts;
62
+ return date.toLocaleString();
63
+ } catch {
64
+ return ts;
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Resolve a project slug to its numeric ID.
70
+ * If the input is already a numeric ID, returns it as-is.
71
+ * @param {string} org - Organization slug
72
+ * @param {string} project - Project slug or numeric ID
73
+ * @param {string} token - Auth token
74
+ * @returns {Promise<string>} The numeric project ID
75
+ */
76
+ export async function resolveProjectId(org, project, token) {
77
+ // If already numeric, return as-is
78
+ if (/^\d+$/.test(project)) {
79
+ return project;
80
+ }
81
+
82
+ // Check cache
83
+ const cacheKey = `${org}/${project}`;
84
+ if (projectIdCache.has(cacheKey)) {
85
+ return projectIdCache.get(cacheKey);
86
+ }
87
+
88
+ // Fetch project details to get the ID
89
+ const url = `${SENTRY_API_BASE}/projects/${encodeURIComponent(org)}/${encodeURIComponent(project)}/`;
90
+ const data = await fetchJson(url, token);
91
+
92
+ if (!data || !data.id) {
93
+ throw new Error(`Project '${project}' not found in organization '${org}'`);
94
+ }
95
+
96
+ const id = String(data.id);
97
+ projectIdCache.set(cacheKey, id);
98
+ return id;
99
+ }
@@ -0,0 +1,325 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { SENTRY_API_BASE, getAuthToken, fetchJson, formatTimestamp } from "../lib/auth.js";
4
+
5
+ const HELP = `Usage: fetch-event.js <event-id> [options]
6
+
7
+ Fetch a specific event by ID with full details.
8
+
9
+ Arguments:
10
+ event-id The event ID to fetch
11
+
12
+ Options:
13
+ --org, -o <org> Organization slug (required)
14
+ --project, -p <project> Project slug (required)
15
+ --json Output raw JSON
16
+ --breadcrumbs, -b Show all breadcrumbs (default: last 30)
17
+ --spans Show span tree (for transactions)
18
+ -h, --help Show this help
19
+
20
+ Examples:
21
+ # Fetch an event
22
+ fetch-event.js 571076d9728248739cecac2c9e96a24c --org myorg --project backend
23
+
24
+ # Get full breadcrumb history
25
+ fetch-event.js abc123 --org myorg --project backend --breadcrumbs
26
+
27
+ # Show spans for a transaction
28
+ fetch-event.js abc123 --org myorg --project backend --spans
29
+
30
+ # Get raw JSON for further analysis
31
+ fetch-event.js abc123 --org myorg --project backend --json
32
+ `;
33
+
34
+ function parseArgs(args) {
35
+ const options = {
36
+ eventId: null,
37
+ org: null,
38
+ project: null,
39
+ json: false,
40
+ allBreadcrumbs: false,
41
+ showSpans: false,
42
+ help: false,
43
+ };
44
+
45
+ for (let i = 0; i < args.length; i++) {
46
+ const arg = args[i];
47
+
48
+ switch (arg) {
49
+ case "--help":
50
+ case "-h":
51
+ options.help = true;
52
+ break;
53
+ case "--json":
54
+ options.json = true;
55
+ break;
56
+ case "--org":
57
+ case "-o":
58
+ options.org = args[++i];
59
+ break;
60
+ case "--project":
61
+ case "-p":
62
+ options.project = args[++i];
63
+ break;
64
+ case "--breadcrumbs":
65
+ case "-b":
66
+ options.allBreadcrumbs = true;
67
+ break;
68
+ case "--spans":
69
+ options.showSpans = true;
70
+ break;
71
+ default:
72
+ if (!arg.startsWith("-") && !options.eventId) {
73
+ options.eventId = arg;
74
+ }
75
+ }
76
+ }
77
+
78
+ return options;
79
+ }
80
+
81
+ function formatStacktrace(frames, { maxFrames = 20, showContext = true } = {}) {
82
+ if (!frames || frames.length === 0) return " (no frames)";
83
+
84
+ const reversed = frames.slice().reverse();
85
+ const appFrames = reversed.filter((f) => f.inApp !== false);
86
+ const framesToShow = appFrames.length > 0 ? appFrames : reversed;
87
+
88
+ return framesToShow
89
+ .slice(0, maxFrames)
90
+ .map((f, i) => {
91
+ const file = f.filename || f.absPath || f.module || "unknown";
92
+ const func = f.function || "(anonymous)";
93
+ const line = f.lineNo || f.lineno;
94
+ const col = f.colNo || f.colno;
95
+ const loc = line ? `:${line}${col ? `:${col}` : ""}` : "";
96
+
97
+ let out = ` ${i + 1}. ${file}${loc}\n → ${func}`;
98
+
99
+ if (showContext && f.context_line) {
100
+ out += `\n | ${f.context_line.trim()}`;
101
+ }
102
+
103
+ return out;
104
+ })
105
+ .join("\n\n");
106
+ }
107
+
108
+ function formatBreadcrumb(crumb) {
109
+ let ts = "??:??:??";
110
+ if (crumb.timestamp) {
111
+ try {
112
+ const date =
113
+ typeof crumb.timestamp === "number"
114
+ ? new Date(crumb.timestamp * 1000)
115
+ : new Date(crumb.timestamp);
116
+ if (!isNaN(date.getTime())) {
117
+ ts = date.toISOString().slice(11, 19);
118
+ }
119
+ } catch {}
120
+ }
121
+
122
+ const cat = crumb.category || crumb.type || "?";
123
+ const level = crumb.level && crumb.level !== "info" ? `[${crumb.level}] ` : "";
124
+ let msg = crumb.message || "";
125
+
126
+ if (!msg && crumb.data) {
127
+ if (crumb.data.url) msg = crumb.data.url;
128
+ else if (crumb.data.method) msg = `${crumb.data.method} ${crumb.data.url || ""}`;
129
+ else if (typeof crumb.data === "object") msg = JSON.stringify(crumb.data);
130
+ }
131
+
132
+ return ` [${ts}] ${level}${cat}: ${msg}`;
133
+ }
134
+
135
+ function formatSpan(span, indent = 0) {
136
+ const prefix = " ".repeat(indent);
137
+ const op = span.op || "?";
138
+ const desc = span.description || "(no description)";
139
+ const status = span.status || "?";
140
+ const duration = span.exclusive_time ? `${span.exclusive_time.toFixed(1)}ms` : "?";
141
+
142
+ return `${prefix}[${op}] ${desc}\n${prefix} status: ${status} | duration: ${duration}`;
143
+ }
144
+
145
+ function formatEvent(event, options) {
146
+ const lines = [];
147
+
148
+ lines.push(`# Event: ${event.eventID || event.id}`);
149
+ lines.push("");
150
+ lines.push(`**Timestamp:** ${formatTimestamp(event.dateCreated || event.timestamp)}`);
151
+ lines.push(`**Project:** ${event.projectSlug || event.projectID || "?"}`);
152
+
153
+ if (event.title) {
154
+ lines.push(`**Title:** ${event.title}`);
155
+ }
156
+
157
+ if (event.message) {
158
+ lines.push(`**Message:** ${event.message}`);
159
+ }
160
+
161
+ // Tags
162
+ if (event.tags && event.tags.length > 0) {
163
+ lines.push("");
164
+ lines.push("## Tags");
165
+ for (const tag of event.tags) {
166
+ lines.push(`- **${tag.key}:** ${tag.value}`);
167
+ }
168
+ }
169
+
170
+ // Contexts
171
+ if (event.contexts) {
172
+ const ctx = event.contexts;
173
+ const contextLines = [];
174
+
175
+ if (ctx.runtime) {
176
+ contextLines.push(`- **Runtime:** ${ctx.runtime.name || "?"} ${ctx.runtime.version || ""}`);
177
+ }
178
+ if (ctx.browser) {
179
+ contextLines.push(`- **Browser:** ${ctx.browser.name || "?"} ${ctx.browser.version || ""}`);
180
+ }
181
+ if (ctx.os) {
182
+ contextLines.push(`- **OS:** ${ctx.os.name || "?"} ${ctx.os.version || ""}`);
183
+ }
184
+ if (ctx.device && ctx.device.family) {
185
+ contextLines.push(`- **Device:** ${ctx.device.family}`);
186
+ }
187
+ if (ctx.trace) {
188
+ contextLines.push(`- **Trace ID:** ${ctx.trace.trace_id || "?"}`);
189
+ contextLines.push(`- **Span ID:** ${ctx.trace.span_id || "?"}`);
190
+ if (ctx.trace.op) {
191
+ contextLines.push(`- **Operation:** ${ctx.trace.op}`);
192
+ }
193
+ if (ctx.trace.status) {
194
+ contextLines.push(`- **Status:** ${ctx.trace.status}`);
195
+ }
196
+ }
197
+
198
+ if (contextLines.length > 0) {
199
+ lines.push("");
200
+ lines.push("## Context");
201
+ lines.push(...contextLines);
202
+ }
203
+ }
204
+
205
+ // Process entries
206
+ if (event.entries) {
207
+ // Request
208
+ for (const entry of event.entries) {
209
+ if (entry.type === "request" && entry.data) {
210
+ const req = entry.data;
211
+ lines.push("");
212
+ lines.push("## Request");
213
+ if (req.method && req.url) {
214
+ lines.push(`**${req.method}** ${req.url}`);
215
+ }
216
+ if (req.headers && req.headers.length > 0) {
217
+ const importantHeaders = ["User-Agent", "Content-Type", "Host"];
218
+ const headers = req.headers.filter(([k]) => importantHeaders.includes(k));
219
+ if (headers.length > 0) {
220
+ for (const [k, v] of headers) {
221
+ lines.push(` ${k}: ${v}`);
222
+ }
223
+ }
224
+ }
225
+ }
226
+ }
227
+
228
+ // Exceptions
229
+ for (const entry of event.entries) {
230
+ if (entry.type === "exception" && entry.data?.values) {
231
+ lines.push("");
232
+ lines.push("## Exception");
233
+ for (const exc of entry.data.values) {
234
+ lines.push("");
235
+ lines.push(`**${exc.type || "Error"}:** ${exc.value || "(no message)"}`);
236
+ if (exc.stacktrace?.frames) {
237
+ lines.push("");
238
+ lines.push(formatStacktrace(exc.stacktrace.frames));
239
+ }
240
+ }
241
+ }
242
+ }
243
+
244
+ // Breadcrumbs
245
+ for (const entry of event.entries) {
246
+ if (entry.type === "breadcrumbs" && entry.data?.values) {
247
+ const crumbs = options.allBreadcrumbs ? entry.data.values : entry.data.values.slice(-30);
248
+
249
+ if (crumbs.length > 0) {
250
+ lines.push("");
251
+ lines.push(
252
+ `## Breadcrumbs (${crumbs.length}${options.allBreadcrumbs ? "" : " most recent"})`,
253
+ );
254
+ for (const c of crumbs) {
255
+ lines.push(formatBreadcrumb(c));
256
+ }
257
+ }
258
+ }
259
+ }
260
+
261
+ // Spans (for transactions)
262
+ if (options.showSpans) {
263
+ for (const entry of event.entries) {
264
+ if (entry.type === "spans" && entry.data) {
265
+ lines.push("");
266
+ lines.push("## Spans");
267
+ const spans = Array.isArray(entry.data) ? entry.data : [entry.data];
268
+ for (const span of spans.slice(0, 50)) {
269
+ lines.push(formatSpan(span));
270
+ lines.push("");
271
+ }
272
+ }
273
+ }
274
+ }
275
+ }
276
+
277
+ return lines.join("\n");
278
+ }
279
+
280
+ async function main() {
281
+ const args = process.argv.slice(2);
282
+ const options = parseArgs(args);
283
+
284
+ if (options.help) {
285
+ console.log(HELP);
286
+ process.exit(0);
287
+ }
288
+
289
+ if (!options.eventId) {
290
+ console.error("Error: event-id is required");
291
+ console.error("Run with --help for usage information");
292
+ process.exit(1);
293
+ }
294
+
295
+ if (!options.org) {
296
+ console.error("Error: --org is required");
297
+ console.error("Run with --help for usage information");
298
+ process.exit(1);
299
+ }
300
+
301
+ if (!options.project) {
302
+ console.error("Error: --project is required");
303
+ console.error("Run with --help for usage information");
304
+ process.exit(1);
305
+ }
306
+
307
+ const token = getAuthToken();
308
+
309
+ const url = `${SENTRY_API_BASE}/projects/${encodeURIComponent(options.org)}/${encodeURIComponent(options.project)}/events/${encodeURIComponent(options.eventId)}/`;
310
+
311
+ try {
312
+ const event = await fetchJson(url, token);
313
+
314
+ if (options.json) {
315
+ console.log(JSON.stringify(event, null, 2));
316
+ } else {
317
+ console.log(formatEvent(event, options));
318
+ }
319
+ } catch (err) {
320
+ console.error("Error:", err.message);
321
+ process.exit(1);
322
+ }
323
+ }
324
+
325
+ main();