@oh-my-pi/pi-coding-agent 12.18.3 → 12.19.2

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 (233) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/package.json +35 -27
  3. package/src/async/index.ts +1 -0
  4. package/src/async/job-manager.ts +341 -0
  5. package/src/cli/file-processor.ts +3 -3
  6. package/src/cli/list-models.ts +3 -17
  7. package/src/cli/stats-cli.ts +3 -22
  8. package/src/cli/web-search-cli.ts +8 -16
  9. package/src/commit/agentic/agent.ts +6 -9
  10. package/src/commit/agentic/index.ts +44 -50
  11. package/src/commit/agentic/state.ts +0 -9
  12. package/src/commit/agentic/tools/propose-commit.ts +1 -30
  13. package/src/commit/agentic/tools/schemas.ts +31 -0
  14. package/src/commit/agentic/tools/split-commit.ts +1 -30
  15. package/src/commit/agentic/validation.ts +1 -18
  16. package/src/commit/analysis/conventional.ts +3 -50
  17. package/src/commit/analysis/summary.ts +2 -13
  18. package/src/commit/changelog/detect.ts +4 -1
  19. package/src/commit/changelog/generate.ts +2 -25
  20. package/src/commit/changelog/index.ts +1 -2
  21. package/src/commit/cli.ts +4 -12
  22. package/src/commit/map-reduce/reduce-phase.ts +2 -43
  23. package/src/commit/pipeline.ts +7 -15
  24. package/src/commit/utils.ts +44 -0
  25. package/src/config/prompt-templates.ts +1 -81
  26. package/src/config/settings-schema.ts +20 -1
  27. package/src/config.ts +2 -3
  28. package/src/debug/index.ts +1 -6
  29. package/src/debug/system-info.ts +2 -6
  30. package/src/discovery/builtin.ts +5 -9
  31. package/src/discovery/helpers.ts +0 -26
  32. package/src/discovery/ssh.ts +1 -8
  33. package/src/exa/company.ts +8 -39
  34. package/src/exa/factory.ts +64 -0
  35. package/src/exa/index.ts +0 -16
  36. package/src/exa/linkedin.ts +8 -39
  37. package/src/exa/mcp-client.ts +0 -64
  38. package/src/exa/researcher.ts +17 -59
  39. package/src/exa/search.ts +30 -154
  40. package/src/extensibility/custom-tools/loader.ts +3 -41
  41. package/src/extensibility/extensions/loader.ts +2 -9
  42. package/src/extensibility/hooks/loader.ts +3 -20
  43. package/src/extensibility/hooks/runner.ts +3 -19
  44. package/src/extensibility/plugins/installer.ts +2 -1
  45. package/src/extensibility/plugins/loader.ts +29 -117
  46. package/src/extensibility/skills.ts +2 -89
  47. package/src/extensibility/slash-commands.ts +1 -63
  48. package/src/extensibility/utils.ts +38 -0
  49. package/src/index.ts +9 -25
  50. package/src/internal-urls/index.ts +1 -0
  51. package/src/internal-urls/jobs-protocol.ts +118 -0
  52. package/src/ipy/kernel.ts +2 -0
  53. package/src/lsp/config.ts +1 -5
  54. package/src/lsp/lspmux.ts +0 -17
  55. package/src/lsp/utils.ts +2 -24
  56. package/src/main.ts +16 -24
  57. package/src/mcp/client.ts +1 -46
  58. package/src/mcp/render.ts +8 -1
  59. package/src/mcp/tool-cache.ts +1 -5
  60. package/src/mcp/transports/http.ts +2 -7
  61. package/src/mcp/transports/stdio.ts +2 -7
  62. package/src/modes/components/bash-execution.ts +2 -16
  63. package/src/modes/components/extensions/inspector-panel.ts +8 -18
  64. package/src/modes/components/footer.ts +10 -50
  65. package/src/modes/components/model-selector.ts +2 -21
  66. package/src/modes/components/python-execution.ts +2 -16
  67. package/src/modes/components/settings-selector.ts +1 -10
  68. package/src/modes/components/status-line/segments.ts +8 -25
  69. package/src/modes/components/status-line.ts +14 -31
  70. package/src/modes/components/tool-execution.ts +8 -2
  71. package/src/modes/controllers/command-controller.ts +71 -30
  72. package/src/modes/controllers/event-controller.ts +34 -4
  73. package/src/modes/controllers/mcp-command-controller.ts +3 -34
  74. package/src/modes/controllers/selector-controller.ts +2 -2
  75. package/src/modes/controllers/ssh-command-controller.ts +3 -34
  76. package/src/modes/interactive-mode.ts +6 -2
  77. package/src/modes/rpc/rpc-client.ts +1 -5
  78. package/src/modes/shared.ts +73 -0
  79. package/src/modes/types.ts +1 -0
  80. package/src/modes/utils/ui-helpers.ts +26 -2
  81. package/src/patch/hashline.ts +6 -286
  82. package/src/patch/index.ts +6 -57
  83. package/src/patch/normalize.ts +22 -65
  84. package/src/patch/shared.ts +16 -16
  85. package/src/prompts/system/custom-system-prompt.md +0 -10
  86. package/src/prompts/system/system-prompt.md +69 -89
  87. package/src/prompts/tools/async-result.md +5 -0
  88. package/src/prompts/tools/bash.md +5 -0
  89. package/src/prompts/tools/cancel-job.md +7 -0
  90. package/src/prompts/tools/hashline.md +0 -16
  91. package/src/prompts/tools/poll-jobs.md +7 -0
  92. package/src/prompts/tools/task.md +4 -0
  93. package/src/sdk.ts +70 -6
  94. package/src/session/agent-session.ts +43 -6
  95. package/src/session/agent-storage.ts +69 -278
  96. package/src/session/auth-storage.ts +14 -1430
  97. package/src/session/session-manager.ts +69 -5
  98. package/src/session/session-storage.ts +1 -5
  99. package/src/session/streaming-output.ts +637 -76
  100. package/src/slash-commands/builtin-registry.ts +8 -0
  101. package/src/ssh/connection-manager.ts +4 -12
  102. package/src/ssh/sshfs-mount.ts +3 -7
  103. package/src/ssh/utils.ts +8 -0
  104. package/src/system-prompt.ts +24 -90
  105. package/src/task/executor.ts +11 -1
  106. package/src/task/index.ts +258 -13
  107. package/src/task/parallel.ts +32 -0
  108. package/src/task/render.ts +15 -7
  109. package/src/task/types.ts +5 -0
  110. package/src/tools/ask.ts +4 -7
  111. package/src/tools/bash-interactive.ts +4 -5
  112. package/src/tools/bash.ts +125 -41
  113. package/src/tools/cancel-job.ts +93 -0
  114. package/src/tools/fetch.ts +7 -27
  115. package/src/tools/find.ts +3 -3
  116. package/src/tools/gemini-image.ts +15 -14
  117. package/src/tools/grep.ts +3 -3
  118. package/src/tools/index.ts +13 -29
  119. package/src/tools/json-tree.ts +12 -1
  120. package/src/tools/jtd-to-json-schema.ts +10 -74
  121. package/src/tools/jtd-to-typescript.ts +10 -72
  122. package/src/tools/jtd-utils.ts +102 -0
  123. package/src/tools/notebook.ts +4 -9
  124. package/src/tools/output-meta.ts +52 -26
  125. package/src/tools/path-utils.ts +13 -7
  126. package/src/tools/poll-jobs.ts +178 -0
  127. package/src/tools/python.ts +32 -35
  128. package/src/tools/read.ts +61 -82
  129. package/src/tools/render-utils.ts +8 -159
  130. package/src/tools/ssh.ts +7 -20
  131. package/src/tools/submit-result.ts +1 -1
  132. package/src/tools/tool-errors.ts +0 -30
  133. package/src/tools/tool-result.ts +1 -2
  134. package/src/tools/write.ts +8 -10
  135. package/src/tui/code-cell.ts +8 -3
  136. package/src/tui/status-line.ts +4 -4
  137. package/src/tui/types.ts +0 -1
  138. package/src/tui/utils.ts +1 -14
  139. package/src/utils/command-args.ts +76 -0
  140. package/src/utils/file-mentions.ts +15 -19
  141. package/src/utils/frontmatter.ts +5 -10
  142. package/src/utils/shell-snapshot.ts +0 -11
  143. package/src/utils/title-generator.ts +0 -12
  144. package/src/web/scrapers/artifacthub.ts +7 -16
  145. package/src/web/scrapers/arxiv.ts +3 -8
  146. package/src/web/scrapers/aur.ts +8 -22
  147. package/src/web/scrapers/biorxiv.ts +5 -14
  148. package/src/web/scrapers/bluesky.ts +13 -36
  149. package/src/web/scrapers/brew.ts +5 -10
  150. package/src/web/scrapers/cheatsh.ts +2 -12
  151. package/src/web/scrapers/chocolatey.ts +63 -26
  152. package/src/web/scrapers/choosealicense.ts +3 -18
  153. package/src/web/scrapers/cisa-kev.ts +4 -18
  154. package/src/web/scrapers/clojars.ts +6 -33
  155. package/src/web/scrapers/coingecko.ts +25 -33
  156. package/src/web/scrapers/crates-io.ts +7 -26
  157. package/src/web/scrapers/crossref.ts +4 -18
  158. package/src/web/scrapers/devto.ts +11 -41
  159. package/src/web/scrapers/discogs.ts +7 -10
  160. package/src/web/scrapers/discourse.ts +6 -31
  161. package/src/web/scrapers/dockerhub.ts +12 -35
  162. package/src/web/scrapers/fdroid.ts +8 -33
  163. package/src/web/scrapers/firefox-addons.ts +10 -34
  164. package/src/web/scrapers/flathub.ts +7 -24
  165. package/src/web/scrapers/github-gist.ts +2 -12
  166. package/src/web/scrapers/github.ts +9 -47
  167. package/src/web/scrapers/gitlab.ts +130 -185
  168. package/src/web/scrapers/go-pkg.ts +12 -22
  169. package/src/web/scrapers/hackage.ts +88 -43
  170. package/src/web/scrapers/hackernews.ts +25 -45
  171. package/src/web/scrapers/hex.ts +19 -36
  172. package/src/web/scrapers/huggingface.ts +26 -91
  173. package/src/web/scrapers/iacr.ts +3 -8
  174. package/src/web/scrapers/jetbrains-marketplace.ts +9 -20
  175. package/src/web/scrapers/lemmy.ts +5 -23
  176. package/src/web/scrapers/lobsters.ts +16 -28
  177. package/src/web/scrapers/mastodon.ts +24 -43
  178. package/src/web/scrapers/maven.ts +6 -21
  179. package/src/web/scrapers/mdn.ts +7 -11
  180. package/src/web/scrapers/metacpan.ts +9 -41
  181. package/src/web/scrapers/musicbrainz.ts +4 -28
  182. package/src/web/scrapers/npm.ts +8 -25
  183. package/src/web/scrapers/nuget.ts +14 -37
  184. package/src/web/scrapers/nvd.ts +6 -28
  185. package/src/web/scrapers/ollama.ts +7 -34
  186. package/src/web/scrapers/open-vsx.ts +5 -19
  187. package/src/web/scrapers/opencorporates.ts +30 -14
  188. package/src/web/scrapers/openlibrary.ts +49 -33
  189. package/src/web/scrapers/orcid.ts +4 -18
  190. package/src/web/scrapers/osv.ts +7 -24
  191. package/src/web/scrapers/packagist.ts +9 -24
  192. package/src/web/scrapers/pub-dev.ts +7 -50
  193. package/src/web/scrapers/pubmed.ts +54 -21
  194. package/src/web/scrapers/pypi.ts +8 -26
  195. package/src/web/scrapers/rawg.ts +11 -19
  196. package/src/web/scrapers/readthedocs.ts +4 -9
  197. package/src/web/scrapers/reddit.ts +5 -15
  198. package/src/web/scrapers/repology.ts +8 -20
  199. package/src/web/scrapers/rfc.ts +5 -14
  200. package/src/web/scrapers/rubygems.ts +6 -21
  201. package/src/web/scrapers/searchcode.ts +8 -36
  202. package/src/web/scrapers/sec-edgar.ts +4 -18
  203. package/src/web/scrapers/semantic-scholar.ts +15 -35
  204. package/src/web/scrapers/snapcraft.ts +5 -19
  205. package/src/web/scrapers/sourcegraph.ts +5 -43
  206. package/src/web/scrapers/spdx.ts +4 -18
  207. package/src/web/scrapers/spotify.ts +4 -23
  208. package/src/web/scrapers/stackoverflow.ts +8 -13
  209. package/src/web/scrapers/terraform.ts +9 -37
  210. package/src/web/scrapers/tldr.ts +3 -7
  211. package/src/web/scrapers/twitter.ts +3 -7
  212. package/src/web/scrapers/types.ts +105 -27
  213. package/src/web/scrapers/utils.ts +97 -103
  214. package/src/web/scrapers/vimeo.ts +7 -27
  215. package/src/web/scrapers/vscode-marketplace.ts +8 -17
  216. package/src/web/scrapers/w3c.ts +6 -14
  217. package/src/web/scrapers/wikidata.ts +5 -19
  218. package/src/web/scrapers/wikipedia.ts +2 -12
  219. package/src/web/scrapers/youtube.ts +5 -34
  220. package/src/web/search/index.ts +0 -9
  221. package/src/web/search/providers/anthropic.ts +3 -2
  222. package/src/web/search/providers/brave.ts +3 -18
  223. package/src/web/search/providers/exa.ts +1 -12
  224. package/src/web/search/providers/kimi.ts +5 -44
  225. package/src/web/search/providers/perplexity.ts +1 -12
  226. package/src/web/search/providers/synthetic.ts +3 -26
  227. package/src/web/search/providers/utils.ts +36 -0
  228. package/src/web/search/providers/zai.ts +9 -50
  229. package/src/web/search/types.ts +0 -28
  230. package/src/web/search/utils.ts +17 -0
  231. package/src/tools/output-utils.ts +0 -63
  232. package/src/tools/truncate.ts +0 -385
  233. package/src/web/search/auth.ts +0 -178
@@ -1,7 +1,5 @@
1
- import type { RenderResult, SpecialHandler } from "./types";
2
- import { finalizeOutput, formatCount, htmlToBasicMarkdown, loadPage } from "./types";
3
-
4
- type LocalizedText = string | Record<string, string | null | undefined> | null | undefined;
1
+ import type { LocalizedText, RenderResult, SpecialHandler } from "./types";
2
+ import { buildResult, formatNumber, getLocalizedText, htmlToBasicMarkdown, loadPage, tryParseJson } from "./types";
5
3
 
6
4
  type AddonFile = {
7
5
  permissions?: string[];
@@ -42,21 +40,6 @@ type AddonData = {
42
40
  url?: string;
43
41
  };
44
42
 
45
- function getLocalizedText(value: LocalizedText, defaultLocale?: string): string | undefined {
46
- if (!value) return undefined;
47
- if (typeof value === "string") return value;
48
-
49
- const localized = value as Record<string, string | null | undefined>;
50
- if (defaultLocale && localized[defaultLocale]) return localized[defaultLocale] ?? undefined;
51
- if (localized["en-US"]) return localized["en-US"] ?? undefined;
52
-
53
- for (const entry of Object.values(localized)) {
54
- if (entry) return entry;
55
- }
56
-
57
- return undefined;
58
- }
59
-
60
43
  function normalizeCategories(categories?: string[] | Record<string, string[]>): string[] {
61
44
  if (!categories) return [];
62
45
  if (Array.isArray(categories)) return categories.filter(Boolean);
@@ -119,12 +102,8 @@ export const handleFirefoxAddons: SpecialHandler = async (
119
102
  const result = await loadPage(apiUrl, { timeout, headers: { Accept: "application/json" }, signal });
120
103
  if (!result.ok) return null;
121
104
 
122
- let data: AddonData;
123
- try {
124
- data = JSON.parse(result.content) as AddonData;
125
- } catch {
126
- return null;
127
- }
105
+ const data = tryParseJson<AddonData>(result.content);
106
+ if (!data) return null;
128
107
 
129
108
  const fetchedAt = new Date().toISOString();
130
109
  const defaultLocale = data.default_locale || "en-US";
@@ -164,11 +143,11 @@ export const handleFirefoxAddons: SpecialHandler = async (
164
143
 
165
144
  if (ratingAverage !== undefined) {
166
145
  md += `**Rating:** ${ratingAverage.toFixed(2)}`;
167
- if (ratingCount !== undefined) md += ` (${formatCount(ratingCount)} reviews)`;
146
+ if (ratingCount !== undefined) md += ` (${formatNumber(ratingCount)} reviews)`;
168
147
  md += "\n";
169
148
  }
170
149
 
171
- if (users !== undefined) md += `**Users:** ${formatCount(users)}\n`;
150
+ if (users !== undefined) md += `**Users:** ${formatNumber(users)}\n`;
172
151
  if (version) md += `**Version:** ${version}\n`;
173
152
  if (categories.length > 0) md += `**Categories:** ${categories.join(", ")}\n`;
174
153
 
@@ -197,17 +176,14 @@ export const handleFirefoxAddons: SpecialHandler = async (
197
176
  }
198
177
  }
199
178
 
200
- const output = finalizeOutput(md);
201
- return {
179
+ const finalUrl = data.url ?? result.finalUrl ?? url;
180
+ return buildResult(md, {
202
181
  url,
203
- finalUrl: data.url ?? result.finalUrl ?? url,
204
- contentType: "text/markdown",
182
+ finalUrl,
205
183
  method: "firefox-addons",
206
- content: output.content,
207
184
  fetchedAt,
208
- truncated: output.truncated,
209
185
  notes: ["Fetched via Firefox Add-ons API"],
210
- };
186
+ });
211
187
  } catch {}
212
188
 
213
189
  return null;
@@ -1,5 +1,5 @@
1
1
  import type { RenderResult, SpecialHandler } from "./types";
2
- import { finalizeOutput, formatCount, htmlToBasicMarkdown, loadPage } from "./types";
2
+ import { buildResult, formatIsoDate, formatNumber, htmlToBasicMarkdown, loadPage, tryParseJson } from "./types";
3
3
 
4
4
  interface FlathubScreenshotSize {
5
5
  src?: string;
@@ -129,15 +129,6 @@ function bestScreenshotUrl(sizes?: FlathubScreenshotSize[]): string | null {
129
129
  return best.src ?? sizes[0].src ?? null;
130
130
  }
131
131
 
132
- function formatReleaseDate(timestamp?: string | null): string | null {
133
- if (!timestamp) return null;
134
- const seconds = Number(timestamp);
135
- if (!Number.isFinite(seconds)) return null;
136
- const date = new Date(seconds * 1000);
137
- if (Number.isNaN(date.getTime())) return null;
138
- return date.toISOString().split("T")[0] ?? null;
139
- }
140
-
141
132
  export const handleFlathub: SpecialHandler = async (
142
133
  url: string,
143
134
  timeout: number,
@@ -154,12 +145,8 @@ export const handleFlathub: SpecialHandler = async (
154
145
  const result = await loadPage(apiUrl, { timeout, signal, headers: { Accept: "application/json" } });
155
146
  if (!result.ok) return null;
156
147
 
157
- let app: FlathubAppStream;
158
- try {
159
- app = JSON.parse(result.content) as FlathubAppStream;
160
- } catch {
161
- return null;
162
- }
148
+ const app = tryParseJson<FlathubAppStream>(result.content);
149
+ if (!app) return null;
163
150
 
164
151
  const fetchedAt = new Date().toISOString();
165
152
  const name = app.name ?? app.id ?? appId;
@@ -172,7 +159,7 @@ export const handleFlathub: SpecialHandler = async (
172
159
  if (app.developer_name) md += `**Developer:** ${app.developer_name}\n`;
173
160
 
174
161
  const installs = extractInstalls(app);
175
- if (installs !== null) md += `**Installs:** ${formatCount(installs)}\n`;
162
+ if (installs !== null) md += `**Installs:** ${formatNumber(installs)}\n`;
176
163
 
177
164
  if (app.categories?.length) {
178
165
  md += "\n## Categories\n\n";
@@ -209,7 +196,7 @@ export const handleFlathub: SpecialHandler = async (
209
196
  for (const release of app.releases.slice(0, 5)) {
210
197
  const version = release.version ?? "unknown";
211
198
  let line = `- **${version}**`;
212
- const date = formatReleaseDate(release.timestamp);
199
+ const date = release.timestamp ? formatIsoDate(Number(release.timestamp) * 1000) : "";
213
200
  if (date) line += ` (${date})`;
214
201
  if (release.type) line += ` · ${release.type}`;
215
202
  if (release.url) line += ` · ${release.url}`;
@@ -222,17 +209,13 @@ export const handleFlathub: SpecialHandler = async (
222
209
  }
223
210
  }
224
211
 
225
- const output = finalizeOutput(md);
226
- return {
212
+ return buildResult(md, {
227
213
  url,
228
214
  finalUrl: result.finalUrl,
229
- contentType: "text/markdown",
230
215
  method: "flathub-appstream",
231
- content: output.content,
232
216
  fetchedAt,
233
- truncated: output.truncated,
234
217
  notes: ["Fetched via Flathub Appstream API"],
235
- };
218
+ });
236
219
  } catch {}
237
220
 
238
221
  return null;
@@ -1,6 +1,6 @@
1
1
  import { fetchGitHubApi } from "./github";
2
2
  import type { RenderResult, SpecialHandler } from "./types";
3
- import { finalizeOutput } from "./types";
3
+ import { buildResult } from "./types";
4
4
 
5
5
  /**
6
6
  * Handle GitHub Gist URLs via GitHub API
@@ -51,17 +51,7 @@ export const handleGitHubGist: SpecialHandler = async (
51
51
  md += `\`\`\`${lang}\n${file.content}\n\`\`\`\n\n`;
52
52
  }
53
53
 
54
- const output = finalizeOutput(md);
55
- return {
56
- url,
57
- finalUrl: url,
58
- contentType: "text/markdown",
59
- method: "github-gist",
60
- content: output.content,
61
- fetchedAt,
62
- truncated: output.truncated,
63
- notes: ["Fetched via GitHub API"],
64
- };
54
+ return buildResult(md, { url, method: "github-gist", fetchedAt, notes: ["Fetched via GitHub API"] });
65
55
  } catch {}
66
56
 
67
57
  return null;
@@ -1,6 +1,6 @@
1
1
  import { $env, ptree } from "@oh-my-pi/pi-utils";
2
2
  import type { RenderResult, SpecialHandler } from "./types";
3
- import { finalizeOutput, loadPage } from "./types";
3
+ import { buildResult, loadPage } from "./types";
4
4
 
5
5
  interface GitHubUrl {
6
6
  type: "blob" | "tree" | "repo" | "issue" | "issues" | "pull" | "pulls" | "discussion" | "discussions" | "other";
@@ -392,17 +392,14 @@ export const handleGitHub: SpecialHandler = async (
392
392
  notes.push(`Fetched raw: ${rawUrl}`);
393
393
  const result = await loadPage(rawUrl, { timeout, signal });
394
394
  if (result.ok) {
395
- const output = finalizeOutput(result.content);
396
- return {
395
+ return buildResult(result.content, {
397
396
  url,
398
397
  finalUrl: rawUrl,
399
- contentType: "text/plain",
400
398
  method: "github-raw",
401
- content: output.content,
402
399
  fetchedAt,
403
- truncated: output.truncated,
404
400
  notes,
405
- };
401
+ contentType: "text/plain",
402
+ });
406
403
  }
407
404
  break;
408
405
  }
@@ -411,17 +408,7 @@ export const handleGitHub: SpecialHandler = async (
411
408
  notes.push(`Fetched via GitHub API`);
412
409
  const result = await renderGitHubTree(gh, timeout, signal);
413
410
  if (result.ok) {
414
- const output = finalizeOutput(result.content);
415
- return {
416
- url,
417
- finalUrl: url,
418
- contentType: "text/markdown",
419
- method: "github-tree",
420
- content: output.content,
421
- fetchedAt,
422
- truncated: output.truncated,
423
- notes,
424
- };
411
+ return buildResult(result.content, { url, method: "github-tree", fetchedAt, notes });
425
412
  }
426
413
  break;
427
414
  }
@@ -431,17 +418,12 @@ export const handleGitHub: SpecialHandler = async (
431
418
  notes.push(`Fetched via GitHub API`);
432
419
  const result = await renderGitHubIssue(gh, timeout, signal);
433
420
  if (result.ok) {
434
- const output = finalizeOutput(result.content);
435
- return {
421
+ return buildResult(result.content, {
436
422
  url,
437
- finalUrl: url,
438
- contentType: "text/markdown",
439
423
  method: gh.type === "pull" ? "github-pr" : "github-issue",
440
- content: output.content,
441
424
  fetchedAt,
442
- truncated: output.truncated,
443
425
  notes,
444
- };
426
+ });
445
427
  }
446
428
  break;
447
429
  }
@@ -450,17 +432,7 @@ export const handleGitHub: SpecialHandler = async (
450
432
  notes.push(`Fetched via GitHub API`);
451
433
  const result = await renderGitHubIssuesList(gh, timeout, signal);
452
434
  if (result.ok) {
453
- const output = finalizeOutput(result.content);
454
- return {
455
- url,
456
- finalUrl: url,
457
- contentType: "text/markdown",
458
- method: "github-issues",
459
- content: output.content,
460
- fetchedAt,
461
- truncated: output.truncated,
462
- notes,
463
- };
435
+ return buildResult(result.content, { url, method: "github-issues", fetchedAt, notes });
464
436
  }
465
437
  break;
466
438
  }
@@ -469,17 +441,7 @@ export const handleGitHub: SpecialHandler = async (
469
441
  notes.push(`Fetched via GitHub API`);
470
442
  const result = await renderGitHubRepo(gh, timeout, signal);
471
443
  if (result.ok) {
472
- const output = finalizeOutput(result.content);
473
- return {
474
- url,
475
- finalUrl: url,
476
- contentType: "text/markdown",
477
- method: "github-repo",
478
- content: output.content,
479
- fetchedAt,
480
- truncated: output.truncated,
481
- notes,
482
- };
444
+ return buildResult(result.content, { url, method: "github-repo", fetchedAt, notes });
483
445
  }
484
446
  break;
485
447
  }