tau-all-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 (95) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +45 -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/telegram/README.md +81 -0
  19. package/extensions/telegram/daemon.mjs +1942 -0
  20. package/extensions/telegram/index.ts +1073 -0
  21. package/extensions/telegram/message-text.d.mts +1 -0
  22. package/extensions/telegram/message-text.mjs +16 -0
  23. package/extensions/usage/anthropic.ts +198 -0
  24. package/extensions/usage/github-copilot.ts +204 -0
  25. package/extensions/usage/google-gemini-cli.ts +232 -0
  26. package/extensions/usage/index.ts +2310 -0
  27. package/extensions/usage/minimax.ts +208 -0
  28. package/extensions/usage/openai-codex.ts +180 -0
  29. package/extensions/usage/openrouter.ts +168 -0
  30. package/extensions/usage/providers.ts +26 -0
  31. package/extensions/usage/shared.ts +156 -0
  32. package/extensions/usage/types.ts +53 -0
  33. package/extensions/usage/zai.ts +186 -0
  34. package/extensions/websearch/README.md +68 -0
  35. package/extensions/websearch/browser/chromium.ts +330 -0
  36. package/extensions/websearch/browser/discovery.ts +75 -0
  37. package/extensions/websearch/browser/firefox.ts +150 -0
  38. package/extensions/websearch/browser/sqlite.ts +38 -0
  39. package/extensions/websearch/config.ts +76 -0
  40. package/extensions/websearch/index.ts +312 -0
  41. package/extensions/websearch/normalize.ts +155 -0
  42. package/extensions/websearch/providers/anthropic.pi.ts +139 -0
  43. package/extensions/websearch/providers/gemini.browser.ts +185 -0
  44. package/extensions/websearch/providers/gemini.pi.ts +108 -0
  45. package/extensions/websearch/providers/openai-codex.browser.ts +77 -0
  46. package/extensions/websearch/providers/openai-codex.pi.ts +49 -0
  47. package/extensions/websearch/providers/openai-codex.shared.ts +123 -0
  48. package/extensions/websearch/providers/pi-model.shared.ts +86 -0
  49. package/extensions/websearch/providers/search-prompt.shared.ts +17 -0
  50. package/extensions/websearch/providers/shared.ts +76 -0
  51. package/extensions/websearch/types.ts +52 -0
  52. package/extensions/worktree.ts +2223 -0
  53. package/package.json +76 -0
  54. package/skills/browser-tools/SKILL.md +253 -0
  55. package/skills/browser-tools/scripts/browser-content.js +100 -0
  56. package/skills/browser-tools/scripts/browser-cookies.js +33 -0
  57. package/skills/browser-tools/scripts/browser-dismiss-cookies.js +455 -0
  58. package/skills/browser-tools/scripts/browser-eval.js +75 -0
  59. package/skills/browser-tools/scripts/browser-logs-tail.js +89 -0
  60. package/skills/browser-tools/scripts/browser-nav.js +28 -0
  61. package/skills/browser-tools/scripts/browser-net-summary.js +115 -0
  62. package/skills/browser-tools/scripts/browser-pick.js +133 -0
  63. package/skills/browser-tools/scripts/browser-screenshot.js +18 -0
  64. package/skills/browser-tools/scripts/browser-start.js +281 -0
  65. package/skills/browser-tools/scripts/browser-watch.js +321 -0
  66. package/skills/browser-tools/scripts/utils.js +60 -0
  67. package/skills/git-clean-history/SKILL.md +45 -0
  68. package/skills/git-commit/SKILL.md +55 -0
  69. package/skills/homeassistant-ops/SKILL.md +184 -0
  70. package/skills/homeassistant-ops/references/api.md +64 -0
  71. package/skills/homeassistant-ops/references/id_conventions.md +71 -0
  72. package/skills/homeassistant-ops/references/playbook.md +150 -0
  73. package/skills/homeassistant-ops/scripts/ha_ops.js +2942 -0
  74. package/skills/openscad/SKILL.md +149 -0
  75. package/skills/openscad/examples/parametric_box.scad +92 -0
  76. package/skills/openscad/examples/phone_stand.scad +95 -0
  77. package/skills/openscad/scripts/common.sh +49 -0
  78. package/skills/openscad/scripts/export-stl.sh +56 -0
  79. package/skills/openscad/scripts/extract-params.sh +147 -0
  80. package/skills/openscad/scripts/multi-preview.sh +68 -0
  81. package/skills/openscad/scripts/preview.sh +74 -0
  82. package/skills/openscad/scripts/render-with-params.sh +91 -0
  83. package/skills/openscad/scripts/validate.sh +46 -0
  84. package/skills/oracle/SKILL.md +64 -0
  85. package/skills/oracle/scripts/oracle +249 -0
  86. package/skills/oracle/scripts/oracle-bundle +227 -0
  87. package/skills/sentry/SKILL.md +198 -0
  88. package/skills/sentry/lib/auth.js +99 -0
  89. package/skills/sentry/scripts/fetch-event.js +325 -0
  90. package/skills/sentry/scripts/fetch-issue.js +368 -0
  91. package/skills/sentry/scripts/list-issues.js +245 -0
  92. package/skills/sentry/scripts/search-events.js +291 -0
  93. package/skills/sentry/scripts/search-logs.js +234 -0
  94. package/skills/update-changelog/SKILL.md +135 -0
  95. package/skills/web-design/SKILL.md +117 -0
@@ -0,0 +1,227 @@
1
+ #!/usr/bin/env bash
2
+ set -eo pipefail
3
+
4
+ ROOT_DIR="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
5
+
6
+ MAX_BYTES=$((1024 * 1024))
7
+ DRY_RUN=0
8
+ PROMPT=""
9
+ FILE_SELECTORS=()
10
+
11
+ usage() {
12
+ cat <<'EOF'
13
+ Usage:
14
+ oracle-bundle -p "<task>" --file "path|dir|glob" [--file "..."] [--dry-run] [--max-bytes N]
15
+
16
+ Notes:
17
+ - Runs from the git repo root when inside a repo; otherwise uses $PWD.
18
+ - --file accepts paths, dirs, or glob-like patterns (string globs).
19
+ - Exclude patterns by prefixing with "!" (e.g. --file "!**/*.test.*").
20
+ EOF
21
+ }
22
+
23
+ trim() {
24
+ local s="$1"
25
+ s="${s#"${s%%[!$' \t\r\n']*}"}"
26
+ s="${s%"${s##*[!$' \t\r\n']}"}"
27
+ printf '%s' "$s"
28
+ }
29
+
30
+ has_glob_chars() {
31
+ case "$1" in
32
+ *'*'* | *'?'* | *'['* | *']'* ) return 0 ;;
33
+ * ) return 1 ;;
34
+ esac
35
+ }
36
+
37
+ file_size_bytes() {
38
+ local path="$1"
39
+ local size=""
40
+ size="$(stat -f%z "$path" 2>/dev/null || true)"
41
+ if [[ -n "$size" ]]; then
42
+ printf '%s' "$size"
43
+ return 0
44
+ fi
45
+
46
+ size="$(stat -c%s "$path" 2>/dev/null || true)"
47
+ if [[ -n "$size" ]]; then
48
+ printf '%s' "$size"
49
+ return 0
50
+ fi
51
+
52
+ wc -c <"$path" | tr -d ' '
53
+ }
54
+
55
+ path_has_ignored_dir() {
56
+ local rel="$1"
57
+ case "$rel" in
58
+ .git/* | */.git/*) return 0 ;;
59
+ node_modules/* | */node_modules/*) return 0 ;;
60
+ dist/* | */dist/*) return 0 ;;
61
+ build/* | */build/*) return 0 ;;
62
+ coverage/* | */coverage/*) return 0 ;;
63
+ .next/* | */.next/*) return 0 ;;
64
+ .turbo/* | */.turbo/*) return 0 ;;
65
+ tmp/* | */tmp/*) return 0 ;;
66
+ *) return 1 ;;
67
+ esac
68
+ }
69
+
70
+ matches_any_pattern() {
71
+ local rel="$1"
72
+ shift
73
+ local pat=""
74
+ for pat in "$@"; do
75
+ # shellcheck disable=SC2053
76
+ [[ "$rel" == $pat ]] && return 0
77
+ done
78
+ return 1
79
+ }
80
+
81
+ while [[ $# -gt 0 ]]; do
82
+ case "$1" in
83
+ -p | --prompt)
84
+ PROMPT="${2-}"
85
+ shift 2
86
+ ;;
87
+ --file | -f)
88
+ FILE_SELECTORS+=("${2-}")
89
+ shift 2
90
+ ;;
91
+ --max-bytes)
92
+ MAX_BYTES="${2-}"
93
+ shift 2
94
+ ;;
95
+ --dry-run)
96
+ DRY_RUN=1
97
+ shift
98
+ ;;
99
+ -h | --help)
100
+ usage
101
+ exit 0
102
+ ;;
103
+ *)
104
+ echo "error: unknown argument: $1" >&2
105
+ usage >&2
106
+ exit 2
107
+ ;;
108
+ esac
109
+ done
110
+
111
+ if [[ -z "$PROMPT" && ! -t 0 ]]; then
112
+ PROMPT="$(cat)"
113
+ fi
114
+
115
+ if [[ -z "$(trim "$PROMPT")" ]]; then
116
+ echo "error: prompt is empty (use -p/--prompt)" >&2
117
+ exit 2
118
+ fi
119
+
120
+ cd "${ROOT_DIR}"
121
+
122
+ selectors=()
123
+ for raw in "${FILE_SELECTORS[@]-}"; do
124
+ IFS=',' read -r -a parts <<<"$raw"
125
+ for part in "${parts[@]}"; do
126
+ part="$(trim "$part")"
127
+ [[ -n "$part" ]] && selectors+=("$part")
128
+ done
129
+ done
130
+
131
+ include_patterns=()
132
+ exclude_patterns=()
133
+
134
+ for sel in "${selectors[@]}"; do
135
+ is_exclude=0
136
+ if [[ "$sel" == !* ]]; then
137
+ is_exclude=1
138
+ sel="${sel:1}"
139
+ fi
140
+
141
+ sel="$(trim "$sel")"
142
+ sel="${sel%/}"
143
+ if [[ -z "$sel" ]]; then
144
+ continue
145
+ fi
146
+
147
+ if [[ "$sel" == /* ]]; then
148
+ if [[ "$sel" == "${ROOT_DIR}/"* ]]; then
149
+ sel="${sel#"${ROOT_DIR}/"}"
150
+ else
151
+ echo "error: absolute paths must be under repo root: ${sel}" >&2
152
+ exit 2
153
+ fi
154
+ fi
155
+
156
+ if [[ -e "$sel" && -d "$sel" ]] && ! has_glob_chars "$sel"; then
157
+ sel="${sel}/**"
158
+ fi
159
+
160
+ if [[ $is_exclude -eq 1 ]]; then
161
+ exclude_patterns+=("$sel")
162
+ else
163
+ include_patterns+=("$sel")
164
+ fi
165
+ done
166
+
167
+ if [[ "${#include_patterns[@]}" -eq 0 ]]; then
168
+ echo "error: no include patterns provided (need at least one non-! --file)" >&2
169
+ exit 2
170
+ fi
171
+
172
+ all_files=()
173
+ if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
174
+ while IFS= read -r f; do
175
+ [[ -n "$f" ]] && all_files+=("$f")
176
+ done < <(git ls-files -co --exclude-standard)
177
+ else
178
+ while IFS= read -r f; do
179
+ f="${f#./}"
180
+ [[ -n "$f" ]] && all_files+=("$f")
181
+ done < <(find . -type f -print)
182
+ fi
183
+
184
+ selected=()
185
+ skipped=()
186
+
187
+ for rel in "${all_files[@]}"; do
188
+ [[ -z "$rel" ]] && continue
189
+ path_has_ignored_dir "$rel" && continue
190
+
191
+ if ! matches_any_pattern "$rel" "${include_patterns[@]}"; then
192
+ continue
193
+ fi
194
+
195
+ if [[ "${#exclude_patterns[@]}" -gt 0 ]] && matches_any_pattern "$rel" "${exclude_patterns[@]}"; then
196
+ continue
197
+ fi
198
+
199
+ if [[ ! -f "$rel" ]]; then
200
+ continue
201
+ fi
202
+
203
+ size="$(file_size_bytes "$rel")"
204
+ if [[ "$size" -gt "$MAX_BYTES" ]]; then
205
+ skipped+=("${rel} (too large: ${size} bytes)")
206
+ continue
207
+ fi
208
+
209
+ selected+=("$rel")
210
+ done
211
+
212
+ if [[ "$DRY_RUN" -eq 1 ]]; then
213
+ printf '%s\n' "${selected[@]}" | LC_ALL=C sort
214
+ if [[ "${#skipped[@]}" -gt 0 ]]; then
215
+ printf '\nSkipped:\n' >&2
216
+ printf '%s\n' "${skipped[@]}" >&2
217
+ fi
218
+ exit 0
219
+ fi
220
+
221
+ printf '# Task\n\n%s\n\n# Files\n' "$(trim "$PROMPT")"
222
+ printf '%s\n' "${selected[@]}" | LC_ALL=C sort | while IFS= read -r rel; do
223
+ [[ -z "$rel" ]] && continue
224
+ printf '\n## %s\n\n```\n' "$rel"
225
+ cat "$rel"
226
+ printf '\n```\n'
227
+ done
@@ -0,0 +1,198 @@
1
+ ---
2
+ name: sentry
3
+ description: "Fetch and analyze Sentry issues, events, transactions, and logs. Helps agents debug errors, find root causes, and understand what happened at specific times."
4
+ ---
5
+
6
+ # Sentry
7
+
8
+ Access Sentry data via the Sentry API for debugging and investigation.
9
+
10
+ ## Requirements
11
+
12
+ A Sentry auth token available to the scripts (by default they read `~/.sentryclirc`).
13
+
14
+ ## Quick reference
15
+
16
+ Run these scripts from this skill directory.
17
+
18
+ | Task | Command |
19
+ | --------------------- | -------------------------------------------------------------------------------- |
20
+ | Find errors on a date | `"./scripts/search-events.js" --org X --start 2025-12-23T15:00:00 --level error` |
21
+ | List open issues | `"./scripts/list-issues.js" --org X --status unresolved` |
22
+ | Get issue details | `"./scripts/fetch-issue.js" <issue-id-or-url> --latest` |
23
+ | Get event details | `"./scripts/fetch-event.js" <event-id> --org X --project Y` |
24
+ | Search logs | `"./scripts/search-logs.js" --org X --project Y "level:error"` |
25
+
26
+ ## Common debugging workflows
27
+
28
+ ### “What went wrong at this time?”
29
+
30
+ Find events around a specific timestamp:
31
+
32
+ ```bash
33
+ # Find all events in a 2-hour window
34
+ "./scripts/search-events.js" --org myorg --project backend \
35
+ --start 2025-12-23T15:00:00 --end 2025-12-23T17:00:00
36
+
37
+ # Filter to just errors
38
+ "./scripts/search-events.js" --org myorg --start 2025-12-23T15:00:00 \
39
+ --level error
40
+
41
+ # Find a specific transaction type
42
+ "./scripts/search-events.js" --org myorg --start 2025-12-23T15:00:00 \
43
+ --transaction process-incoming-email
44
+ ```
45
+
46
+ ### “What errors have occurred recently?”
47
+
48
+ ```bash
49
+ # List unresolved errors from last 24 hours
50
+ "./scripts/list-issues.js" --org myorg --status unresolved --level error --period 24h
51
+
52
+ # Find high-frequency issues
53
+ "./scripts/list-issues.js" --org myorg --query "times_seen:>50" --sort freq
54
+
55
+ # Issues affecting users
56
+ "./scripts/list-issues.js" --org myorg --query "is:unresolved has:user" --sort user
57
+ ```
58
+
59
+ ### “Get details about a specific issue/event”
60
+
61
+ ```bash
62
+ # Get issue with latest stack trace
63
+ "./scripts/fetch-issue.js" 5765604106 --latest
64
+ "./scripts/fetch-issue.js" https://sentry.io/organizations/myorg/issues/123/ --latest
65
+ "./scripts/fetch-issue.js" MYPROJ-123 --org myorg --latest
66
+
67
+ # Get specific event with all breadcrumbs
68
+ "./scripts/fetch-event.js" abc123def456 --org myorg --project backend --breadcrumbs
69
+ ```
70
+
71
+ ### “Find events with a specific tag”
72
+
73
+ ```bash
74
+ # Find by custom tag (e.g., thread_id, user_id)
75
+ "./scripts/search-events.js" --org myorg --tag thread_id:th_abc123
76
+
77
+ # Find by user email
78
+ "./scripts/search-events.js" --org myorg --query "user.email:*@example.com"
79
+ ```
80
+
81
+ ---
82
+
83
+ ## Fetch issue
84
+
85
+ ```bash
86
+ "./scripts/fetch-issue.js" <issue-id-or-url> [options]
87
+ ```
88
+
89
+ Get details about a specific issue (grouped error).
90
+
91
+ **Accepts:**
92
+
93
+ - Issue ID: `5765604106`
94
+ - Issue URL: `https://sentry.io/organizations/sentry/issues/5765604106/`
95
+ - New URL format: `https://myorg.sentry.io/issues/5765604106/`
96
+ - Short ID: `JAVASCRIPT-ABC` (requires `--org`)
97
+
98
+ **Options:**
99
+
100
+ - `--latest` include the latest event with full stack trace
101
+ - `--org <org>` organization slug (for short IDs)
102
+ - `--json` output raw JSON
103
+
104
+ ---
105
+
106
+ ## Fetch event
107
+
108
+ ```bash
109
+ "./scripts/fetch-event.js" <event-id> --org <org> --project <project> [options]
110
+ ```
111
+
112
+ **Options:**
113
+
114
+ - `--org, -o <org>` organization slug (required)
115
+ - `--project, -p <project>` project slug (required)
116
+ - `--breadcrumbs, -b` show all breadcrumbs (default: last 30)
117
+ - `--spans` show span tree for transactions
118
+ - `--json` output raw JSON
119
+
120
+ ---
121
+
122
+ ## Search events (Discover)
123
+
124
+ ```bash
125
+ "./scripts/search-events.js" [options]
126
+ ```
127
+
128
+ **Time range options:**
129
+
130
+ - `--period, -t <period>` relative time (24h, 7d, 14d)
131
+ - `--start <datetime>` start time (ISO 8601: 2025-12-23T15:00:00)
132
+ - `--end <datetime>` end time (ISO 8601)
133
+
134
+ **Filter options:**
135
+
136
+ - `--org, -o <org>` organization slug (required)
137
+ - `--project, -p <project>` project slug or ID
138
+ - `--query, -q <query>` Discover search query
139
+ - `--transaction <name>` transaction name filter
140
+ - `--tag <key:value>` tag filter (repeatable)
141
+ - `--level <level>` level filter (error, warning, info)
142
+ - `--limit, -n <n>` max results (default: 25, max: 100)
143
+ - `--fields <fields>` comma-separated fields to include
144
+
145
+ **Query syntax (Discover):**
146
+
147
+ ```
148
+ transaction:process-* Wildcard transaction match
149
+ level:error Filter by level
150
+ user.email:foo@bar.com Filter by user
151
+ environment:production Filter by environment
152
+ has:stack.filename Has stack trace
153
+ ```
154
+
155
+ ---
156
+
157
+ ## List issues
158
+
159
+ ```bash
160
+ "./scripts/list-issues.js" [options]
161
+ ```
162
+
163
+ **Options:**
164
+
165
+ - `--org, -o <org>` organization slug (required)
166
+ - `--project, -p <project>` project slug (repeatable)
167
+ - `--query, -q <query>` issue search query
168
+ - `--status <status>` unresolved, resolved, ignored
169
+ - `--level <level>` error, warning, info, fatal
170
+ - `--period, -t <period>` time period (default: 14d)
171
+ - `--limit, -n <n>` max results (default: 25)
172
+ - `--sort <sort>` date, new, priority, freq, user
173
+ - `--json` output raw JSON
174
+
175
+ ---
176
+
177
+ ## Search logs (Logs Explorer)
178
+
179
+ ```bash
180
+ "./scripts/search-logs.js" [query|url] [options]
181
+ ```
182
+
183
+ **Options:**
184
+
185
+ - `--org, -o <org>` organization slug (required unless URL provided)
186
+ - `--project, -p <project>` filter by project slug or ID
187
+ - `--period, -t <period>` time period (default: 24h)
188
+ - `--limit, -n <n>` max results (default: 100, max: 1000)
189
+ - `--json` output raw JSON
190
+
191
+ ---
192
+
193
+ ## Tips
194
+
195
+ 1. Start broad (time window + simple query), then drill into a single event/issue.
196
+ 2. Use `--breadcrumbs` on `fetch-event.js` for the full lead-up to an error.
197
+ 3. Use `list-issues.js --sort freq` to find recurring problems.
198
+ 4. Use tags (`request_id`, `user_id`, etc.) to correlate events.
@@ -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
+ }