@oh-my-pi/pi-coding-agent 3.25.0 → 3.30.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/CHANGELOG.md +19 -0
- package/package.json +4 -4
- package/src/core/tools/complete.ts +2 -4
- package/src/core/tools/jtd-to-json-schema.ts +174 -196
- package/src/core/tools/read.ts +4 -4
- package/src/core/tools/task/executor.ts +146 -20
- package/src/core/tools/task/name-generator.ts +1544 -214
- package/src/core/tools/task/types.ts +19 -5
- package/src/core/tools/task/worker.ts +103 -13
- package/src/core/tools/web-fetch-handlers/academic.test.ts +239 -0
- package/src/core/tools/web-fetch-handlers/artifacthub.ts +210 -0
- package/src/core/tools/web-fetch-handlers/arxiv.ts +84 -0
- package/src/core/tools/web-fetch-handlers/aur.ts +171 -0
- package/src/core/tools/web-fetch-handlers/biorxiv.ts +136 -0
- package/src/core/tools/web-fetch-handlers/bluesky.ts +277 -0
- package/src/core/tools/web-fetch-handlers/brew.ts +173 -0
- package/src/core/tools/web-fetch-handlers/business.test.ts +82 -0
- package/src/core/tools/web-fetch-handlers/cheatsh.ts +73 -0
- package/src/core/tools/web-fetch-handlers/chocolatey.ts +153 -0
- package/src/core/tools/web-fetch-handlers/coingecko.ts +179 -0
- package/src/core/tools/web-fetch-handlers/crates-io.ts +123 -0
- package/src/core/tools/web-fetch-handlers/dev-platforms.test.ts +254 -0
- package/src/core/tools/web-fetch-handlers/devto.ts +173 -0
- package/src/core/tools/web-fetch-handlers/discogs.ts +303 -0
- package/src/core/tools/web-fetch-handlers/dockerhub.ts +156 -0
- package/src/core/tools/web-fetch-handlers/documentation.test.ts +85 -0
- package/src/core/tools/web-fetch-handlers/finance-media.test.ts +144 -0
- package/src/core/tools/web-fetch-handlers/git-hosting.test.ts +272 -0
- package/src/core/tools/web-fetch-handlers/github-gist.ts +64 -0
- package/src/core/tools/web-fetch-handlers/github.ts +424 -0
- package/src/core/tools/web-fetch-handlers/gitlab.ts +444 -0
- package/src/core/tools/web-fetch-handlers/go-pkg.ts +271 -0
- package/src/core/tools/web-fetch-handlers/hackage.ts +89 -0
- package/src/core/tools/web-fetch-handlers/hackernews.ts +208 -0
- package/src/core/tools/web-fetch-handlers/hex.ts +121 -0
- package/src/core/tools/web-fetch-handlers/huggingface.ts +385 -0
- package/src/core/tools/web-fetch-handlers/iacr.ts +82 -0
- package/src/core/tools/web-fetch-handlers/index.ts +69 -0
- package/src/core/tools/web-fetch-handlers/lobsters.ts +186 -0
- package/src/core/tools/web-fetch-handlers/mastodon.ts +302 -0
- package/src/core/tools/web-fetch-handlers/maven.ts +147 -0
- package/src/core/tools/web-fetch-handlers/mdn.ts +174 -0
- package/src/core/tools/web-fetch-handlers/media.test.ts +138 -0
- package/src/core/tools/web-fetch-handlers/metacpan.ts +247 -0
- package/src/core/tools/web-fetch-handlers/npm.ts +107 -0
- package/src/core/tools/web-fetch-handlers/nuget.ts +201 -0
- package/src/core/tools/web-fetch-handlers/nvd.ts +238 -0
- package/src/core/tools/web-fetch-handlers/opencorporates.ts +273 -0
- package/src/core/tools/web-fetch-handlers/openlibrary.ts +313 -0
- package/src/core/tools/web-fetch-handlers/osv.ts +184 -0
- package/src/core/tools/web-fetch-handlers/package-managers-2.test.ts +199 -0
- package/src/core/tools/web-fetch-handlers/package-managers.test.ts +171 -0
- package/src/core/tools/web-fetch-handlers/package-registries.test.ts +259 -0
- package/src/core/tools/web-fetch-handlers/packagist.ts +170 -0
- package/src/core/tools/web-fetch-handlers/pub-dev.ts +185 -0
- package/src/core/tools/web-fetch-handlers/pubmed.ts +174 -0
- package/src/core/tools/web-fetch-handlers/pypi.ts +125 -0
- package/src/core/tools/web-fetch-handlers/readthedocs.ts +122 -0
- package/src/core/tools/web-fetch-handlers/reddit.ts +100 -0
- package/src/core/tools/web-fetch-handlers/repology.ts +257 -0
- package/src/core/tools/web-fetch-handlers/research.test.ts +107 -0
- package/src/core/tools/web-fetch-handlers/rfc.ts +205 -0
- package/src/core/tools/web-fetch-handlers/rubygems.ts +112 -0
- package/src/core/tools/web-fetch-handlers/sec-edgar.ts +269 -0
- package/src/core/tools/web-fetch-handlers/security.test.ts +103 -0
- package/src/core/tools/web-fetch-handlers/semantic-scholar.ts +190 -0
- package/src/core/tools/web-fetch-handlers/social-extended.test.ts +192 -0
- package/src/core/tools/web-fetch-handlers/social.test.ts +259 -0
- package/src/core/tools/web-fetch-handlers/spotify.ts +218 -0
- package/src/core/tools/web-fetch-handlers/stackexchange.test.ts +120 -0
- package/src/core/tools/web-fetch-handlers/stackoverflow.ts +123 -0
- package/src/core/tools/web-fetch-handlers/standards.test.ts +122 -0
- package/src/core/tools/web-fetch-handlers/terraform.ts +296 -0
- package/src/core/tools/web-fetch-handlers/tldr.ts +47 -0
- package/src/core/tools/web-fetch-handlers/twitter.ts +84 -0
- package/src/core/tools/web-fetch-handlers/types.ts +163 -0
- package/src/core/tools/web-fetch-handlers/utils.ts +91 -0
- package/src/core/tools/web-fetch-handlers/vimeo.ts +152 -0
- package/src/core/tools/web-fetch-handlers/wikidata.ts +349 -0
- package/src/core/tools/web-fetch-handlers/wikipedia.test.ts +73 -0
- package/src/core/tools/web-fetch-handlers/wikipedia.ts +91 -0
- package/src/core/tools/web-fetch-handlers/youtube.test.ts +198 -0
- package/src/core/tools/web-fetch-handlers/youtube.ts +319 -0
- package/src/core/tools/web-fetch.ts +152 -1324
- package/src/utils/tools-manager.ts +110 -8
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
import type { RenderResult, SpecialHandler } from "./types";
|
|
2
|
+
import { finalizeOutput, loadPage } from "./types";
|
|
3
|
+
|
|
4
|
+
interface GitHubUrl {
|
|
5
|
+
type: "blob" | "tree" | "repo" | "issue" | "issues" | "pull" | "pulls" | "discussion" | "discussions" | "other";
|
|
6
|
+
owner: string;
|
|
7
|
+
repo: string;
|
|
8
|
+
ref?: string;
|
|
9
|
+
path?: string;
|
|
10
|
+
number?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Parse GitHub URL into components
|
|
15
|
+
*/
|
|
16
|
+
function parseGitHubUrl(url: string): GitHubUrl | null {
|
|
17
|
+
try {
|
|
18
|
+
const parsed = new URL(url);
|
|
19
|
+
if (parsed.hostname !== "github.com") return null;
|
|
20
|
+
|
|
21
|
+
const parts = parsed.pathname.split("/").filter(Boolean);
|
|
22
|
+
if (parts.length < 2) return null;
|
|
23
|
+
|
|
24
|
+
const [owner, repo, ...rest] = parts;
|
|
25
|
+
|
|
26
|
+
if (rest.length === 0) {
|
|
27
|
+
return { type: "repo", owner, repo };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const [section, ...subParts] = rest;
|
|
31
|
+
|
|
32
|
+
switch (section) {
|
|
33
|
+
case "blob":
|
|
34
|
+
case "tree": {
|
|
35
|
+
const [ref, ...pathParts] = subParts;
|
|
36
|
+
return { type: section, owner, repo, ref, path: pathParts.join("/") };
|
|
37
|
+
}
|
|
38
|
+
case "issues":
|
|
39
|
+
if (subParts.length > 0 && /^\d+$/.test(subParts[0])) {
|
|
40
|
+
return { type: "issue", owner, repo, number: parseInt(subParts[0], 10) };
|
|
41
|
+
}
|
|
42
|
+
return { type: "issues", owner, repo };
|
|
43
|
+
case "pull":
|
|
44
|
+
if (subParts.length > 0 && /^\d+$/.test(subParts[0])) {
|
|
45
|
+
return { type: "pull", owner, repo, number: parseInt(subParts[0], 10) };
|
|
46
|
+
}
|
|
47
|
+
return { type: "pulls", owner, repo };
|
|
48
|
+
case "pulls":
|
|
49
|
+
return { type: "pulls", owner, repo };
|
|
50
|
+
case "discussions":
|
|
51
|
+
if (subParts.length > 0 && /^\d+$/.test(subParts[0])) {
|
|
52
|
+
return { type: "discussion", owner, repo, number: parseInt(subParts[0], 10) };
|
|
53
|
+
}
|
|
54
|
+
return { type: "discussions", owner, repo };
|
|
55
|
+
default:
|
|
56
|
+
return { type: "other", owner, repo };
|
|
57
|
+
}
|
|
58
|
+
} catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Convert GitHub blob URL to raw URL
|
|
65
|
+
*/
|
|
66
|
+
function toRawGitHubUrl(gh: GitHubUrl): string {
|
|
67
|
+
return `https://raw.githubusercontent.com/${gh.owner}/${gh.repo}/refs/heads/${gh.ref}/${gh.path}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Fetch from GitHub API
|
|
72
|
+
*/
|
|
73
|
+
export async function fetchGitHubApi(endpoint: string, timeout: number): Promise<{ data: unknown; ok: boolean }> {
|
|
74
|
+
try {
|
|
75
|
+
const controller = new AbortController();
|
|
76
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout * 1000);
|
|
77
|
+
|
|
78
|
+
const headers: Record<string, string> = {
|
|
79
|
+
Accept: "application/vnd.github.v3+json",
|
|
80
|
+
"User-Agent": "omp-web-fetch/1.0",
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// Use GITHUB_TOKEN if available
|
|
84
|
+
const token = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
|
|
85
|
+
if (token) {
|
|
86
|
+
headers.Authorization = `Bearer ${token}`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const response = await fetch(`https://api.github.com${endpoint}`, {
|
|
90
|
+
signal: controller.signal,
|
|
91
|
+
headers,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
clearTimeout(timeoutId);
|
|
95
|
+
|
|
96
|
+
if (!response.ok) {
|
|
97
|
+
return { data: null, ok: false };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return { data: await response.json(), ok: true };
|
|
101
|
+
} catch {
|
|
102
|
+
return { data: null, ok: false };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Render GitHub issue/PR to markdown
|
|
108
|
+
*/
|
|
109
|
+
async function renderGitHubIssue(gh: GitHubUrl, timeout: number): Promise<{ content: string; ok: boolean }> {
|
|
110
|
+
const endpoint =
|
|
111
|
+
gh.type === "pull"
|
|
112
|
+
? `/repos/${gh.owner}/${gh.repo}/pulls/${gh.number}`
|
|
113
|
+
: `/repos/${gh.owner}/${gh.repo}/issues/${gh.number}`;
|
|
114
|
+
|
|
115
|
+
const result = await fetchGitHubApi(endpoint, timeout);
|
|
116
|
+
if (!result.ok || !result.data) return { content: "", ok: false };
|
|
117
|
+
|
|
118
|
+
const issue = result.data as {
|
|
119
|
+
title: string;
|
|
120
|
+
number: number;
|
|
121
|
+
state: string;
|
|
122
|
+
user: { login: string };
|
|
123
|
+
created_at: string;
|
|
124
|
+
updated_at: string;
|
|
125
|
+
body: string | null;
|
|
126
|
+
labels: Array<{ name: string }>;
|
|
127
|
+
comments: number;
|
|
128
|
+
html_url: string;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
let md = `# ${issue.title}\n\n`;
|
|
132
|
+
md += `**#${issue.number}** · ${issue.state} · opened by @${issue.user.login}\n`;
|
|
133
|
+
md += `Created: ${issue.created_at} · Updated: ${issue.updated_at}\n`;
|
|
134
|
+
if (issue.labels.length > 0) {
|
|
135
|
+
md += `Labels: ${issue.labels.map((l) => l.name).join(", ")}\n`;
|
|
136
|
+
}
|
|
137
|
+
md += `\n---\n\n`;
|
|
138
|
+
md += issue.body || "*No description provided.*";
|
|
139
|
+
md += `\n\n---\n\n`;
|
|
140
|
+
|
|
141
|
+
// Fetch comments if any
|
|
142
|
+
if (issue.comments > 0) {
|
|
143
|
+
const commentsResult = await fetchGitHubApi(
|
|
144
|
+
`/repos/${gh.owner}/${gh.repo}/issues/${gh.number}/comments?per_page=50`,
|
|
145
|
+
timeout,
|
|
146
|
+
);
|
|
147
|
+
if (commentsResult.ok && Array.isArray(commentsResult.data)) {
|
|
148
|
+
md += `## Comments (${issue.comments})\n\n`;
|
|
149
|
+
for (const comment of commentsResult.data as Array<{
|
|
150
|
+
user: { login: string };
|
|
151
|
+
created_at: string;
|
|
152
|
+
body: string;
|
|
153
|
+
}>) {
|
|
154
|
+
md += `### @${comment.user.login} · ${comment.created_at}\n\n`;
|
|
155
|
+
md += `${comment.body}\n\n---\n\n`;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return { content: md, ok: true };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Render GitHub issues list to markdown
|
|
165
|
+
*/
|
|
166
|
+
async function renderGitHubIssuesList(gh: GitHubUrl, timeout: number): Promise<{ content: string; ok: boolean }> {
|
|
167
|
+
const result = await fetchGitHubApi(`/repos/${gh.owner}/${gh.repo}/issues?state=open&per_page=30`, timeout);
|
|
168
|
+
if (!result.ok || !Array.isArray(result.data)) return { content: "", ok: false };
|
|
169
|
+
|
|
170
|
+
const issues = result.data as Array<{
|
|
171
|
+
number: number;
|
|
172
|
+
title: string;
|
|
173
|
+
state: string;
|
|
174
|
+
user: { login: string };
|
|
175
|
+
created_at: string;
|
|
176
|
+
comments: number;
|
|
177
|
+
labels: Array<{ name: string }>;
|
|
178
|
+
pull_request?: unknown;
|
|
179
|
+
}>;
|
|
180
|
+
|
|
181
|
+
let md = `# ${gh.owner}/${gh.repo} - Open Issues\n\n`;
|
|
182
|
+
|
|
183
|
+
for (const issue of issues) {
|
|
184
|
+
if (issue.pull_request) continue; // Skip PRs in issues list
|
|
185
|
+
const labels = issue.labels.length > 0 ? ` [${issue.labels.map((l) => l.name).join(", ")}]` : "";
|
|
186
|
+
md += `- **#${issue.number}** ${issue.title}${labels}\n`;
|
|
187
|
+
md += ` by @${issue.user.login} · ${issue.comments} comments · ${issue.created_at}\n\n`;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return { content: md, ok: true };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Render GitHub tree (directory) to markdown
|
|
195
|
+
*/
|
|
196
|
+
async function renderGitHubTree(gh: GitHubUrl, timeout: number): Promise<{ content: string; ok: boolean }> {
|
|
197
|
+
// Fetch repo info first to get default branch if ref not specified
|
|
198
|
+
const repoResult = await fetchGitHubApi(`/repos/${gh.owner}/${gh.repo}`, timeout);
|
|
199
|
+
if (!repoResult.ok) return { content: "", ok: false };
|
|
200
|
+
|
|
201
|
+
const repo = repoResult.data as {
|
|
202
|
+
full_name: string;
|
|
203
|
+
default_branch: string;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const ref = gh.ref || repo.default_branch;
|
|
207
|
+
const dirPath = gh.path || "";
|
|
208
|
+
|
|
209
|
+
let md = `# ${repo.full_name}/${dirPath || "(root)"}\n\n`;
|
|
210
|
+
md += `**Branch:** ${ref}\n\n`;
|
|
211
|
+
|
|
212
|
+
// Fetch directory contents
|
|
213
|
+
const contentsResult = await fetchGitHubApi(`/repos/${gh.owner}/${gh.repo}/contents/${dirPath}?ref=${ref}`, timeout);
|
|
214
|
+
|
|
215
|
+
if (contentsResult.ok && Array.isArray(contentsResult.data)) {
|
|
216
|
+
const items = contentsResult.data as Array<{
|
|
217
|
+
name: string;
|
|
218
|
+
type: "file" | "dir" | "symlink" | "submodule";
|
|
219
|
+
size?: number;
|
|
220
|
+
path: string;
|
|
221
|
+
}>;
|
|
222
|
+
|
|
223
|
+
// Sort: directories first, then files, alphabetically
|
|
224
|
+
items.sort((a, b) => {
|
|
225
|
+
if (a.type === "dir" && b.type !== "dir") return -1;
|
|
226
|
+
if (a.type !== "dir" && b.type === "dir") return 1;
|
|
227
|
+
return a.name.localeCompare(b.name);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
md += `## Contents\n\n`;
|
|
231
|
+
md += "```\n";
|
|
232
|
+
for (const item of items) {
|
|
233
|
+
const prefix = item.type === "dir" ? "[dir] " : " ";
|
|
234
|
+
const size = item.size ? ` (${item.size} bytes)` : "";
|
|
235
|
+
md += `${prefix}${item.name}${item.type === "file" ? size : ""}\n`;
|
|
236
|
+
}
|
|
237
|
+
md += "```\n\n";
|
|
238
|
+
|
|
239
|
+
// Look for README in this directory
|
|
240
|
+
const readmeFile = items.find((item) => item.type === "file" && /^readme\.md$/i.test(item.name));
|
|
241
|
+
if (readmeFile) {
|
|
242
|
+
const readmePath = dirPath ? `${dirPath}/${readmeFile.name}` : readmeFile.name;
|
|
243
|
+
const rawUrl = `https://raw.githubusercontent.com/${gh.owner}/${gh.repo}/refs/heads/${ref}/${readmePath}`;
|
|
244
|
+
const readmeResult = await loadPage(rawUrl, { timeout });
|
|
245
|
+
if (readmeResult.ok) {
|
|
246
|
+
md += `---\n\n## README\n\n${readmeResult.content}`;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return { content: md, ok: true };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Render GitHub repo to markdown (file list + README)
|
|
256
|
+
*/
|
|
257
|
+
async function renderGitHubRepo(gh: GitHubUrl, timeout: number): Promise<{ content: string; ok: boolean }> {
|
|
258
|
+
// Fetch repo info
|
|
259
|
+
const repoResult = await fetchGitHubApi(`/repos/${gh.owner}/${gh.repo}`, timeout);
|
|
260
|
+
if (!repoResult.ok) return { content: "", ok: false };
|
|
261
|
+
|
|
262
|
+
const repo = repoResult.data as {
|
|
263
|
+
full_name: string;
|
|
264
|
+
description: string | null;
|
|
265
|
+
stargazers_count: number;
|
|
266
|
+
forks_count: number;
|
|
267
|
+
open_issues_count: number;
|
|
268
|
+
default_branch: string;
|
|
269
|
+
language: string | null;
|
|
270
|
+
license: { name: string } | null;
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
let md = `# ${repo.full_name}\n\n`;
|
|
274
|
+
if (repo.description) md += `${repo.description}\n\n`;
|
|
275
|
+
md += `Stars: ${repo.stargazers_count} · Forks: ${repo.forks_count} · Issues: ${repo.open_issues_count}\n`;
|
|
276
|
+
if (repo.language) md += `Language: ${repo.language}\n`;
|
|
277
|
+
if (repo.license) md += `License: ${repo.license.name}\n`;
|
|
278
|
+
md += `\n---\n\n`;
|
|
279
|
+
|
|
280
|
+
// Fetch file tree
|
|
281
|
+
const treeResult = await fetchGitHubApi(
|
|
282
|
+
`/repos/${gh.owner}/${gh.repo}/git/trees/${repo.default_branch}?recursive=1`,
|
|
283
|
+
timeout,
|
|
284
|
+
);
|
|
285
|
+
if (treeResult.ok && treeResult.data) {
|
|
286
|
+
const tree = (treeResult.data as { tree: Array<{ path: string; type: string }> }).tree;
|
|
287
|
+
md += `## Files\n\n`;
|
|
288
|
+
md += "```\n";
|
|
289
|
+
for (const item of tree.slice(0, 100)) {
|
|
290
|
+
const prefix = item.type === "tree" ? "[dir] " : " ";
|
|
291
|
+
md += `${prefix}${item.path}\n`;
|
|
292
|
+
}
|
|
293
|
+
if (tree.length > 100) {
|
|
294
|
+
md += `... and ${tree.length - 100} more files\n`;
|
|
295
|
+
}
|
|
296
|
+
md += "```\n\n";
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Fetch README
|
|
300
|
+
const readmeResult = await fetchGitHubApi(`/repos/${gh.owner}/${gh.repo}/readme`, timeout);
|
|
301
|
+
if (readmeResult.ok && readmeResult.data) {
|
|
302
|
+
const readme = readmeResult.data as { content: string; encoding: string };
|
|
303
|
+
if (readme.encoding === "base64") {
|
|
304
|
+
const decoded = Buffer.from(readme.content, "base64").toString("utf-8");
|
|
305
|
+
md += `## README\n\n${decoded}`;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return { content: md, ok: true };
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Handle GitHub URLs specially
|
|
314
|
+
*/
|
|
315
|
+
export const handleGitHub: SpecialHandler = async (url: string, timeout: number): Promise<RenderResult | null> => {
|
|
316
|
+
const gh = parseGitHubUrl(url);
|
|
317
|
+
if (!gh) return null;
|
|
318
|
+
|
|
319
|
+
const fetchedAt = new Date().toISOString();
|
|
320
|
+
const notes: string[] = [];
|
|
321
|
+
|
|
322
|
+
switch (gh.type) {
|
|
323
|
+
case "blob": {
|
|
324
|
+
// Convert to raw URL and fetch
|
|
325
|
+
const rawUrl = toRawGitHubUrl(gh);
|
|
326
|
+
notes.push(`Fetched raw: ${rawUrl}`);
|
|
327
|
+
const result = await loadPage(rawUrl, { timeout });
|
|
328
|
+
if (result.ok) {
|
|
329
|
+
const output = finalizeOutput(result.content);
|
|
330
|
+
return {
|
|
331
|
+
url,
|
|
332
|
+
finalUrl: rawUrl,
|
|
333
|
+
contentType: "text/plain",
|
|
334
|
+
method: "github-raw",
|
|
335
|
+
content: output.content,
|
|
336
|
+
fetchedAt,
|
|
337
|
+
truncated: output.truncated,
|
|
338
|
+
notes,
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
case "tree": {
|
|
345
|
+
notes.push(`Fetched via GitHub API`);
|
|
346
|
+
const result = await renderGitHubTree(gh, timeout);
|
|
347
|
+
if (result.ok) {
|
|
348
|
+
const output = finalizeOutput(result.content);
|
|
349
|
+
return {
|
|
350
|
+
url,
|
|
351
|
+
finalUrl: url,
|
|
352
|
+
contentType: "text/markdown",
|
|
353
|
+
method: "github-tree",
|
|
354
|
+
content: output.content,
|
|
355
|
+
fetchedAt,
|
|
356
|
+
truncated: output.truncated,
|
|
357
|
+
notes,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
break;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
case "issue":
|
|
364
|
+
case "pull": {
|
|
365
|
+
notes.push(`Fetched via GitHub API`);
|
|
366
|
+
const result = await renderGitHubIssue(gh, timeout);
|
|
367
|
+
if (result.ok) {
|
|
368
|
+
const output = finalizeOutput(result.content);
|
|
369
|
+
return {
|
|
370
|
+
url,
|
|
371
|
+
finalUrl: url,
|
|
372
|
+
contentType: "text/markdown",
|
|
373
|
+
method: gh.type === "pull" ? "github-pr" : "github-issue",
|
|
374
|
+
content: output.content,
|
|
375
|
+
fetchedAt,
|
|
376
|
+
truncated: output.truncated,
|
|
377
|
+
notes,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
break;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
case "issues": {
|
|
384
|
+
notes.push(`Fetched via GitHub API`);
|
|
385
|
+
const result = await renderGitHubIssuesList(gh, timeout);
|
|
386
|
+
if (result.ok) {
|
|
387
|
+
const output = finalizeOutput(result.content);
|
|
388
|
+
return {
|
|
389
|
+
url,
|
|
390
|
+
finalUrl: url,
|
|
391
|
+
contentType: "text/markdown",
|
|
392
|
+
method: "github-issues",
|
|
393
|
+
content: output.content,
|
|
394
|
+
fetchedAt,
|
|
395
|
+
truncated: output.truncated,
|
|
396
|
+
notes,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
break;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
case "repo": {
|
|
403
|
+
notes.push(`Fetched via GitHub API`);
|
|
404
|
+
const result = await renderGitHubRepo(gh, timeout);
|
|
405
|
+
if (result.ok) {
|
|
406
|
+
const output = finalizeOutput(result.content);
|
|
407
|
+
return {
|
|
408
|
+
url,
|
|
409
|
+
finalUrl: url,
|
|
410
|
+
contentType: "text/markdown",
|
|
411
|
+
method: "github-repo",
|
|
412
|
+
content: output.content,
|
|
413
|
+
fetchedAt,
|
|
414
|
+
truncated: output.truncated,
|
|
415
|
+
notes,
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
break;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Fall back to null (let normal rendering handle it)
|
|
423
|
+
return null;
|
|
424
|
+
};
|