@oh-my-pi/pi-coding-agent 8.0.20 → 8.2.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 (421) hide show
  1. package/CHANGELOG.md +125 -0
  2. package/docs/session.md +111 -46
  3. package/examples/custom-tools/hello/index.ts +1 -1
  4. package/examples/custom-tools/todo/index.ts +3 -4
  5. package/examples/extensions/api-demo.ts +0 -1
  6. package/examples/extensions/chalk-logger.ts +2 -3
  7. package/examples/extensions/hello.ts +0 -1
  8. package/examples/extensions/pirate.ts +0 -1
  9. package/examples/extensions/plan-mode.ts +15 -16
  10. package/examples/extensions/todo.ts +3 -4
  11. package/examples/extensions/tools.ts +1 -2
  12. package/examples/extensions/with-deps/index.ts +0 -1
  13. package/examples/hooks/auto-commit-on-exit.ts +1 -2
  14. package/examples/hooks/confirm-destructive.ts +0 -1
  15. package/examples/hooks/custom-compaction.ts +1 -2
  16. package/examples/hooks/dirty-repo-guard.ts +0 -1
  17. package/examples/hooks/file-trigger.ts +3 -4
  18. package/examples/hooks/git-checkpoint.ts +0 -1
  19. package/examples/hooks/handoff.ts +3 -4
  20. package/examples/hooks/permission-gate.ts +1 -2
  21. package/examples/hooks/protected-paths.ts +1 -2
  22. package/examples/hooks/qna.ts +2 -3
  23. package/examples/hooks/snake.ts +4 -5
  24. package/examples/hooks/status-line.ts +0 -1
  25. package/examples/sdk/01-minimal.ts +2 -3
  26. package/examples/sdk/02-custom-model.ts +2 -3
  27. package/examples/sdk/03-custom-prompt.ts +3 -4
  28. package/examples/sdk/04-skills.ts +2 -3
  29. package/examples/sdk/06-extensions.ts +1 -2
  30. package/examples/sdk/06-hooks.ts +6 -7
  31. package/examples/sdk/07-context-files.ts +0 -1
  32. package/examples/sdk/08-prompt-templates.ts +0 -1
  33. package/examples/sdk/08-slash-commands.ts +0 -1
  34. package/examples/sdk/09-api-keys-and-oauth.ts +0 -1
  35. package/examples/sdk/10-settings.ts +0 -1
  36. package/examples/sdk/11-sessions.ts +0 -1
  37. package/package.json +54 -23
  38. package/scripts/format-prompts.ts +0 -1
  39. package/src/capability/context-file.ts +3 -4
  40. package/src/capability/extension-module.ts +3 -4
  41. package/src/capability/extension.ts +3 -4
  42. package/src/capability/fs.ts +20 -21
  43. package/src/capability/hook.ts +3 -4
  44. package/src/capability/index.ts +15 -16
  45. package/src/capability/instruction.ts +3 -4
  46. package/src/capability/mcp.ts +3 -4
  47. package/src/capability/prompt.ts +3 -4
  48. package/src/capability/rule.ts +3 -4
  49. package/src/capability/settings.ts +2 -3
  50. package/src/capability/skill.ts +3 -4
  51. package/src/capability/slash-command.ts +3 -4
  52. package/src/capability/ssh.ts +3 -4
  53. package/src/capability/system-prompt.ts +3 -4
  54. package/src/capability/tool.ts +3 -4
  55. package/src/cli/args.ts +5 -6
  56. package/src/cli/config-cli.ts +6 -7
  57. package/src/cli/file-processor.ts +19 -17
  58. package/src/cli/jupyter-cli.ts +105 -0
  59. package/src/cli/list-models.ts +10 -11
  60. package/src/cli/plugin-cli.ts +20 -25
  61. package/src/cli/session-picker.ts +2 -3
  62. package/src/cli/setup-cli.ts +2 -3
  63. package/src/cli/stats-cli.ts +2 -3
  64. package/src/cli/update-cli.ts +25 -22
  65. package/src/commit/agentic/agent.ts +307 -0
  66. package/src/commit/agentic/fallback.ts +96 -0
  67. package/src/commit/agentic/index.ts +351 -0
  68. package/src/commit/agentic/prompts/analyze-file.md +22 -0
  69. package/src/commit/agentic/prompts/session-user.md +26 -0
  70. package/src/commit/agentic/prompts/split-confirm.md +1 -0
  71. package/src/commit/agentic/prompts/system.md +40 -0
  72. package/src/commit/agentic/state.ts +69 -0
  73. package/src/commit/agentic/tools/analyze-file.ts +131 -0
  74. package/src/commit/agentic/tools/git-file-diff.ts +194 -0
  75. package/src/commit/agentic/tools/git-hunk.ts +50 -0
  76. package/src/commit/agentic/tools/git-overview.ts +84 -0
  77. package/src/commit/agentic/tools/index.ts +56 -0
  78. package/src/commit/agentic/tools/propose-changelog.ts +128 -0
  79. package/src/commit/agentic/tools/propose-commit.ts +154 -0
  80. package/src/commit/agentic/tools/recent-commits.ts +81 -0
  81. package/src/commit/agentic/tools/split-commit.ts +280 -0
  82. package/src/commit/agentic/topo-sort.ts +44 -0
  83. package/src/commit/agentic/trivial.ts +51 -0
  84. package/src/commit/agentic/validation.ts +200 -0
  85. package/src/commit/analysis/conventional.ts +165 -0
  86. package/src/commit/analysis/index.ts +4 -0
  87. package/src/commit/analysis/scope.ts +242 -0
  88. package/src/commit/analysis/summary.ts +112 -0
  89. package/src/commit/analysis/validation.ts +66 -0
  90. package/src/commit/changelog/detect.ts +36 -0
  91. package/src/commit/changelog/generate.ts +110 -0
  92. package/src/commit/changelog/index.ts +233 -0
  93. package/src/commit/changelog/parse.ts +44 -0
  94. package/src/commit/cli.ts +93 -0
  95. package/src/commit/git/diff.ts +148 -0
  96. package/src/commit/git/errors.ts +11 -0
  97. package/src/commit/git/index.ts +212 -0
  98. package/src/commit/git/operations.ts +53 -0
  99. package/src/commit/index.ts +5 -0
  100. package/src/commit/map-reduce/index.ts +63 -0
  101. package/src/commit/map-reduce/map-phase.ts +178 -0
  102. package/src/commit/map-reduce/reduce-phase.ts +145 -0
  103. package/src/commit/map-reduce/utils.ts +9 -0
  104. package/src/commit/message.ts +11 -0
  105. package/src/commit/model-selection.ts +80 -0
  106. package/src/commit/pipeline.ts +240 -0
  107. package/src/commit/prompts/analysis-system.md +155 -0
  108. package/src/commit/prompts/analysis-user.md +41 -0
  109. package/src/commit/prompts/changelog-system.md +56 -0
  110. package/src/commit/prompts/changelog-user.md +19 -0
  111. package/src/commit/prompts/file-observer-system.md +26 -0
  112. package/src/commit/prompts/file-observer-user.md +9 -0
  113. package/src/commit/prompts/reduce-system.md +60 -0
  114. package/src/commit/prompts/reduce-user.md +17 -0
  115. package/src/commit/prompts/summary-retry.md +4 -0
  116. package/src/commit/prompts/summary-system.md +52 -0
  117. package/src/commit/prompts/summary-user.md +13 -0
  118. package/src/commit/prompts/types-description.md +2 -0
  119. package/src/commit/types.ts +109 -0
  120. package/src/commit/utils/exclusions.ts +42 -0
  121. package/src/config/file-lock.ts +121 -0
  122. package/src/config/keybindings.ts +6 -8
  123. package/src/config/model-registry.ts +65 -38
  124. package/src/config/model-resolver.ts +18 -19
  125. package/src/config/prompt-templates.ts +11 -11
  126. package/src/config/settings-manager.ts +141 -50
  127. package/src/config.ts +64 -66
  128. package/src/cursor.ts +11 -9
  129. package/src/discovery/agents-md.ts +11 -12
  130. package/src/discovery/builtin.ts +68 -73
  131. package/src/discovery/claude.ts +41 -42
  132. package/src/discovery/cline.ts +11 -12
  133. package/src/discovery/codex.ts +52 -53
  134. package/src/discovery/cursor.ts +9 -10
  135. package/src/discovery/gemini.ts +17 -22
  136. package/src/discovery/github.ts +13 -14
  137. package/src/discovery/helpers.ts +35 -34
  138. package/src/discovery/index.ts +22 -24
  139. package/src/discovery/mcp-json.ts +8 -9
  140. package/src/discovery/ssh.ts +8 -9
  141. package/src/discovery/vscode.ts +4 -5
  142. package/src/discovery/windsurf.ts +6 -7
  143. package/src/exa/company.ts +1 -2
  144. package/src/exa/index.ts +2 -3
  145. package/src/exa/linkedin.ts +1 -2
  146. package/src/exa/mcp-client.ts +14 -16
  147. package/src/exa/render.ts +10 -11
  148. package/src/exa/researcher.ts +1 -2
  149. package/src/exa/search.ts +1 -2
  150. package/src/exa/types.ts +0 -1
  151. package/src/exa/websets.ts +1 -2
  152. package/src/exec/bash-executor.ts +3 -4
  153. package/src/exec/exec.ts +0 -1
  154. package/src/export/custom-share.ts +5 -6
  155. package/src/export/html/index.ts +24 -21
  156. package/src/export/ttsr.ts +2 -3
  157. package/src/extensibility/custom-commands/bundled/review/index.ts +7 -8
  158. package/src/extensibility/custom-commands/loader.ts +18 -15
  159. package/src/extensibility/custom-commands/types.ts +2 -3
  160. package/src/extensibility/custom-tools/loader.ts +11 -12
  161. package/src/extensibility/custom-tools/types.ts +7 -8
  162. package/src/extensibility/custom-tools/wrapper.ts +2 -3
  163. package/src/extensibility/extensions/loader.ts +76 -54
  164. package/src/extensibility/extensions/runner.ts +11 -12
  165. package/src/extensibility/extensions/types.ts +20 -27
  166. package/src/extensibility/extensions/wrapper.ts +3 -4
  167. package/src/extensibility/hooks/index.ts +1 -1
  168. package/src/extensibility/hooks/loader.ts +9 -10
  169. package/src/extensibility/hooks/runner.ts +7 -8
  170. package/src/extensibility/hooks/tool-wrapper.ts +0 -1
  171. package/src/extensibility/hooks/types.ts +11 -18
  172. package/src/extensibility/plugins/doctor.ts +3 -3
  173. package/src/extensibility/plugins/installer.ts +27 -27
  174. package/src/extensibility/plugins/loader.ts +59 -56
  175. package/src/extensibility/plugins/manager.ts +211 -171
  176. package/src/extensibility/plugins/parser.ts +1 -1
  177. package/src/extensibility/plugins/paths.ts +8 -8
  178. package/src/extensibility/skills.ts +63 -60
  179. package/src/extensibility/slash-commands.ts +10 -10
  180. package/src/index.ts +54 -54
  181. package/src/internal-urls/agent-protocol.ts +21 -11
  182. package/src/internal-urls/artifact-protocol.ts +17 -13
  183. package/src/internal-urls/router.ts +1 -2
  184. package/src/internal-urls/rule-protocol.ts +3 -4
  185. package/src/internal-urls/skill-protocol.ts +3 -4
  186. package/src/ipy/executor.ts +109 -9
  187. package/src/ipy/gateway-coordinator.ts +79 -90
  188. package/src/ipy/kernel.ts +32 -30
  189. package/src/ipy/modules.ts +13 -13
  190. package/src/lsp/client.ts +21 -10
  191. package/src/lsp/clients/biome-client.ts +1 -2
  192. package/src/lsp/clients/index.ts +3 -3
  193. package/src/lsp/clients/lsp-linter-client.ts +4 -5
  194. package/src/lsp/config.ts +15 -15
  195. package/src/lsp/edits.ts +4 -5
  196. package/src/lsp/index.ts +43 -44
  197. package/src/lsp/lspmux.ts +8 -8
  198. package/src/lsp/render.ts +99 -61
  199. package/src/lsp/utils.ts +3 -3
  200. package/src/main.ts +71 -37
  201. package/src/mcp/client.ts +2 -3
  202. package/src/mcp/config.ts +5 -6
  203. package/src/mcp/json-rpc.ts +0 -1
  204. package/src/mcp/loader.ts +6 -7
  205. package/src/mcp/manager.ts +17 -18
  206. package/src/mcp/tool-bridge.ts +4 -9
  207. package/src/mcp/tool-cache.ts +2 -3
  208. package/src/mcp/transports/http.ts +2 -4
  209. package/src/mcp/transports/stdio.ts +1 -2
  210. package/src/migrations.ts +63 -52
  211. package/src/modes/components/armin.ts +4 -5
  212. package/src/modes/components/assistant-message.ts +33 -5
  213. package/src/modes/components/bash-execution.ts +7 -8
  214. package/src/modes/components/bordered-loader.ts +3 -3
  215. package/src/modes/components/branch-summary-message.ts +3 -3
  216. package/src/modes/components/compaction-summary-message.ts +3 -3
  217. package/src/modes/components/countdown-timer.ts +0 -1
  218. package/src/modes/components/custom-message.ts +5 -5
  219. package/src/modes/components/diff.ts +1 -1
  220. package/src/modes/components/dynamic-border.ts +2 -2
  221. package/src/modes/components/extensions/extension-dashboard.ts +6 -7
  222. package/src/modes/components/extensions/extension-list.ts +2 -3
  223. package/src/modes/components/extensions/inspector-panel.ts +3 -4
  224. package/src/modes/components/extensions/state-manager.ts +25 -26
  225. package/src/modes/components/extensions/types.ts +1 -2
  226. package/src/modes/components/footer.ts +47 -43
  227. package/src/modes/components/history-search.ts +2 -2
  228. package/src/modes/components/hook-editor.ts +3 -4
  229. package/src/modes/components/hook-input.ts +2 -3
  230. package/src/modes/components/hook-message.ts +5 -5
  231. package/src/modes/components/hook-selector.ts +2 -3
  232. package/src/modes/components/keybinding-hints.ts +2 -3
  233. package/src/modes/components/login-dialog.ts +2 -2
  234. package/src/modes/components/model-selector.ts +12 -12
  235. package/src/modes/components/oauth-selector.ts +2 -2
  236. package/src/modes/components/plugin-settings.ts +20 -20
  237. package/src/modes/components/python-execution.ts +7 -8
  238. package/src/modes/components/queue-mode-selector.ts +3 -3
  239. package/src/modes/components/read-tool-group.ts +2 -2
  240. package/src/modes/components/session-selector.ts +4 -4
  241. package/src/modes/components/settings-defs.ts +77 -69
  242. package/src/modes/components/settings-selector.ts +16 -16
  243. package/src/modes/components/show-images-selector.ts +2 -2
  244. package/src/modes/components/status-line/segments.ts +4 -4
  245. package/src/modes/components/status-line/separators.ts +1 -1
  246. package/src/modes/components/status-line/types.ts +2 -2
  247. package/src/modes/components/status-line-segment-editor.ts +7 -8
  248. package/src/modes/components/status-line.ts +12 -12
  249. package/src/modes/components/theme-selector.ts +8 -7
  250. package/src/modes/components/thinking-selector.ts +4 -4
  251. package/src/modes/components/todo-display.ts +2 -2
  252. package/src/modes/components/todo-reminder.ts +4 -4
  253. package/src/modes/components/tool-execution.ts +16 -19
  254. package/src/modes/components/tree-selector.ts +12 -12
  255. package/src/modes/components/ttsr-notification.ts +5 -5
  256. package/src/modes/components/user-message-selector.ts +1 -1
  257. package/src/modes/components/user-message.ts +1 -1
  258. package/src/modes/components/visual-truncate.ts +0 -1
  259. package/src/modes/components/welcome.ts +4 -4
  260. package/src/modes/controllers/command-controller.ts +46 -47
  261. package/src/modes/controllers/event-controller.ts +16 -20
  262. package/src/modes/controllers/extension-ui-controller.ts +40 -46
  263. package/src/modes/controllers/input-controller.ts +17 -18
  264. package/src/modes/controllers/selector-controller.ts +103 -91
  265. package/src/modes/index.ts +3 -3
  266. package/src/modes/interactive-mode.ts +31 -31
  267. package/src/modes/print-mode.ts +12 -13
  268. package/src/modes/rpc/rpc-client.ts +7 -8
  269. package/src/modes/rpc/rpc-mode.ts +24 -28
  270. package/src/modes/rpc/rpc-types.ts +3 -4
  271. package/src/modes/theme/mermaid-cache.ts +89 -0
  272. package/src/modes/theme/theme.ts +130 -53
  273. package/src/modes/types.ts +10 -10
  274. package/src/modes/utils/ui-helpers.ts +17 -17
  275. package/src/patch/applicator.ts +18 -19
  276. package/src/patch/diff.ts +1 -2
  277. package/src/patch/fuzzy.ts +1 -2
  278. package/src/patch/index.ts +11 -18
  279. package/src/patch/normalize.ts +4 -4
  280. package/src/patch/normative.ts +1 -2
  281. package/src/patch/parser.ts +8 -9
  282. package/src/patch/shared.ts +43 -16
  283. package/src/prompts/tools/task.md +2 -0
  284. package/src/sdk.ts +100 -65
  285. package/src/session/agent-session.ts +84 -85
  286. package/src/session/agent-storage.ts +43 -39
  287. package/src/session/artifacts.ts +32 -10
  288. package/src/session/auth-storage.ts +50 -39
  289. package/src/session/compaction/branch-summarization.ts +7 -10
  290. package/src/session/compaction/compaction.ts +8 -19
  291. package/src/session/compaction/utils.ts +6 -9
  292. package/src/session/history-storage.ts +10 -10
  293. package/src/session/messages.ts +4 -5
  294. package/src/session/session-manager.ts +76 -65
  295. package/src/session/session-storage.ts +57 -69
  296. package/src/session/storage-migration.ts +14 -56
  297. package/src/session/streaming-output.ts +2 -2
  298. package/src/ssh/connection-manager.ts +43 -50
  299. package/src/ssh/ssh-executor.ts +2 -2
  300. package/src/ssh/sshfs-mount.ts +11 -18
  301. package/src/system-prompt.ts +28 -35
  302. package/src/task/agents.ts +45 -30
  303. package/src/task/commands.ts +6 -7
  304. package/src/task/discovery.ts +39 -76
  305. package/src/task/executor.ts +14 -15
  306. package/src/task/index.ts +40 -34
  307. package/src/task/output-manager.ts +93 -0
  308. package/src/task/parallel.ts +0 -1
  309. package/src/task/render.ts +24 -30
  310. package/src/task/subprocess-tool-registry.ts +1 -2
  311. package/src/task/worker-protocol.ts +3 -3
  312. package/src/task/worker.ts +33 -39
  313. package/src/task/worktree.ts +19 -19
  314. package/src/tools/ask.ts +41 -20
  315. package/src/tools/bash-interceptor.ts +1 -5
  316. package/src/tools/bash.ts +91 -97
  317. package/src/tools/calculator.ts +49 -47
  318. package/src/tools/complete.ts +4 -5
  319. package/src/tools/context.ts +2 -2
  320. package/src/tools/fetch.ts +84 -124
  321. package/src/tools/find.ts +94 -98
  322. package/src/tools/gemini-image.ts +14 -14
  323. package/src/tools/grep.ts +100 -116
  324. package/src/tools/index.ts +80 -55
  325. package/src/tools/list-limit.ts +1 -1
  326. package/src/tools/ls.ts +44 -70
  327. package/src/tools/notebook.ts +51 -67
  328. package/src/tools/output-meta.ts +3 -4
  329. package/src/tools/output-utils.ts +2 -2
  330. package/src/tools/path-utils.ts +5 -5
  331. package/src/tools/python.ts +104 -217
  332. package/src/tools/read.ts +92 -33
  333. package/src/tools/render-utils.ts +8 -23
  334. package/src/tools/renderers.ts +6 -7
  335. package/src/tools/review.ts +8 -11
  336. package/src/tools/ssh.ts +69 -49
  337. package/src/tools/todo-write.ts +37 -25
  338. package/src/tools/tool-errors.ts +3 -3
  339. package/src/tools/tool-result.ts +3 -8
  340. package/src/tools/write.ts +99 -75
  341. package/src/tui/code-cell.ts +109 -0
  342. package/src/tui/file-list.ts +47 -0
  343. package/src/tui/index.ts +11 -0
  344. package/src/tui/output-block.ts +72 -0
  345. package/src/tui/status-line.ts +39 -0
  346. package/src/tui/tree-list.ts +55 -0
  347. package/src/tui/types.ts +16 -0
  348. package/src/tui/utils.ts +48 -0
  349. package/src/utils/changelog.ts +9 -10
  350. package/src/utils/clipboard.ts +11 -11
  351. package/src/utils/file-mentions.ts +4 -10
  352. package/src/utils/frontmatter.ts +6 -3
  353. package/src/utils/fuzzy.ts +2 -2
  354. package/src/utils/image-convert.ts +1 -1
  355. package/src/utils/image-resize.ts +1 -1
  356. package/src/utils/mime.ts +2 -2
  357. package/src/utils/shell-snapshot.ts +11 -13
  358. package/src/utils/shell.ts +4 -5
  359. package/src/utils/title-generator.ts +8 -9
  360. package/src/utils/tools-manager.ts +23 -23
  361. package/src/vendor/photon/index.js +1099 -1059
  362. package/src/vendor/photon/photon_rs_bg.wasm +0 -0
  363. package/src/web/scrapers/artifacthub.ts +1 -1
  364. package/src/web/scrapers/arxiv.ts +2 -2
  365. package/src/web/scrapers/bluesky.ts +2 -2
  366. package/src/web/scrapers/cheatsh.ts +1 -1
  367. package/src/web/scrapers/chocolatey.ts +2 -2
  368. package/src/web/scrapers/choosealicense.ts +5 -5
  369. package/src/web/scrapers/cisa-kev.ts +1 -1
  370. package/src/web/scrapers/crossref.ts +2 -2
  371. package/src/web/scrapers/devto.ts +3 -3
  372. package/src/web/scrapers/discogs.ts +3 -4
  373. package/src/web/scrapers/discourse.ts +1 -1
  374. package/src/web/scrapers/dockerhub.ts +1 -1
  375. package/src/web/scrapers/fdroid.ts +2 -2
  376. package/src/web/scrapers/firefox-addons.ts +3 -3
  377. package/src/web/scrapers/flathub.ts +1 -1
  378. package/src/web/scrapers/github.ts +3 -3
  379. package/src/web/scrapers/gitlab.ts +4 -4
  380. package/src/web/scrapers/hackernews.ts +2 -2
  381. package/src/web/scrapers/huggingface.ts +1 -1
  382. package/src/web/scrapers/iacr.ts +2 -2
  383. package/src/web/scrapers/index.ts +0 -1
  384. package/src/web/scrapers/jetbrains-marketplace.ts +1 -1
  385. package/src/web/scrapers/lemmy.ts +2 -2
  386. package/src/web/scrapers/maven.ts +2 -2
  387. package/src/web/scrapers/mdn.ts +2 -4
  388. package/src/web/scrapers/metacpan.ts +2 -2
  389. package/src/web/scrapers/musicbrainz.ts +1 -2
  390. package/src/web/scrapers/npm.ts +1 -1
  391. package/src/web/scrapers/nuget.ts +2 -2
  392. package/src/web/scrapers/nvd.ts +3 -3
  393. package/src/web/scrapers/ollama.ts +7 -9
  394. package/src/web/scrapers/opencorporates.ts +2 -2
  395. package/src/web/scrapers/openlibrary.ts +6 -6
  396. package/src/web/scrapers/orcid.ts +0 -1
  397. package/src/web/scrapers/osv.ts +2 -2
  398. package/src/web/scrapers/packagist.ts +1 -1
  399. package/src/web/scrapers/pubmed.ts +1 -2
  400. package/src/web/scrapers/rawg.ts +2 -2
  401. package/src/web/scrapers/readthedocs.ts +1 -2
  402. package/src/web/scrapers/repology.ts +2 -2
  403. package/src/web/scrapers/rfc.ts +1 -1
  404. package/src/web/scrapers/searchcode.ts +2 -2
  405. package/src/web/scrapers/semantic-scholar.ts +1 -1
  406. package/src/web/scrapers/snapcraft.ts +2 -2
  407. package/src/web/scrapers/sourcegraph.ts +1 -1
  408. package/src/web/scrapers/spdx.ts +3 -3
  409. package/src/web/scrapers/spotify.ts +0 -1
  410. package/src/web/scrapers/twitter.ts +1 -1
  411. package/src/web/scrapers/types.ts +1 -2
  412. package/src/web/scrapers/utils.ts +5 -5
  413. package/src/web/scrapers/wikidata.ts +3 -3
  414. package/src/web/scrapers/youtube.ts +9 -14
  415. package/src/web/search/auth.ts +5 -10
  416. package/src/web/search/index.ts +11 -21
  417. package/src/web/search/providers/anthropic.ts +3 -9
  418. package/src/web/search/providers/exa.ts +6 -10
  419. package/src/web/search/providers/perplexity.ts +5 -5
  420. package/src/web/search/render.ts +129 -175
  421. package/tsconfig.json +0 -42
Binary file
@@ -119,7 +119,7 @@ export const handleArtifactHub: SpecialHandler = async (
119
119
 
120
120
  // Maintainers
121
121
  if (pkg.maintainers?.length) {
122
- const maintainerNames = pkg.maintainers.map((m) => m.name).join(", ");
122
+ const maintainerNames = pkg.maintainers.map(m => m.name).join(", ");
123
123
  md += `**Maintainers:** ${maintainerNames}\n`;
124
124
  }
125
125
 
@@ -40,12 +40,12 @@ export const handleArxiv: SpecialHandler = async (
40
40
  const summary = entry.querySelector("summary")?.text?.trim();
41
41
  const authors = entry
42
42
  .querySelectorAll("author name")
43
- .map((n) => n.text?.trim())
43
+ .map(n => n.text?.trim())
44
44
  .filter(Boolean);
45
45
  const published = entry.querySelector("published")?.text?.trim()?.split("T")[0];
46
46
  const categories = entry
47
47
  .querySelectorAll("category")
48
- .map((c) => c.getAttribute("term"))
48
+ .map(c => c.getAttribute("term"))
49
49
  .filter(Boolean);
50
50
  const pdfLink = entry.querySelector('link[title="pdf"]')?.getAttribute("href");
51
51
 
@@ -93,7 +93,7 @@ function formatPost(post: BlueskyPost, isQuote = false): string {
93
93
  md += `> **${name}** (${handle}) - ${date}\n>\n`;
94
94
  md += post.record.text
95
95
  .split("\n")
96
- .map((line) => `> ${line}`)
96
+ .map(line => `> ${line}`)
97
97
  .join("\n");
98
98
  md += "\n";
99
99
  } else {
@@ -126,7 +126,7 @@ function formatPost(post: BlueskyPost, isQuote = false): string {
126
126
  md += `> **${rec.author.displayName || rec.author.handle}** (@${rec.author.handle})\n`;
127
127
  md += rec.value.text
128
128
  .split("\n")
129
- .map((line) => `> ${line}`)
129
+ .map(line => `> ${line}`)
130
130
  .join("\n");
131
131
  md += "\n";
132
132
  }
@@ -43,7 +43,7 @@ export const handleCheatSh: SpecialHandler = async (
43
43
  const content = result.content.trim();
44
44
  const lines = content.split("\n");
45
45
  const hasCodeIndicators = lines.some(
46
- (line) =>
46
+ line =>
47
47
  line.startsWith("$") ||
48
48
  line.startsWith("#") ||
49
49
  line.includes("()") ||
@@ -110,7 +110,7 @@ export const handleChocolatey: SpecialHandler = async (
110
110
  if (pkg.LicenseUrl) md += `**License:** ${pkg.LicenseUrl}\n`;
111
111
 
112
112
  if (pkg.Tags) {
113
- const tags = pkg.Tags.split(/\s+/).filter((t) => t.length > 0);
113
+ const tags = pkg.Tags.split(/\s+/).filter(t => t.length > 0);
114
114
  if (tags.length > 0) {
115
115
  md += `**Tags:** ${tags.join(", ")}\n`;
116
116
  }
@@ -127,7 +127,7 @@ export const handleChocolatey: SpecialHandler = async (
127
127
 
128
128
  if (pkg.Dependencies) {
129
129
  // Dependencies format: "id:version|id:version"
130
- const deps = pkg.Dependencies.split("|").filter((d) => d.trim().length > 0);
130
+ const deps = pkg.Dependencies.split("|").filter(d => d.trim().length > 0);
131
131
  if (deps.length > 0) {
132
132
  md += `\n## Dependencies\n\n`;
133
133
  for (const dep of deps) {
@@ -1,4 +1,4 @@
1
- import { parseFrontmatter } from "@oh-my-pi/pi-coding-agent/utils/frontmatter";
1
+ import { parseFrontmatter } from "../../utils/frontmatter";
2
2
  import type { RenderResult, SpecialHandler } from "./types";
3
3
  import { finalizeOutput, loadPage } from "./types";
4
4
 
@@ -16,14 +16,14 @@ function normalizeList(value: unknown): string[] {
16
16
  if (Array.isArray(value)) {
17
17
  return value
18
18
  .filter((item): item is string => typeof item === "string")
19
- .map((item) => item.trim())
20
- .filter((item) => item.length > 0);
19
+ .map(item => item.trim())
20
+ .filter(item => item.length > 0);
21
21
  }
22
22
  if (typeof value === "string") {
23
23
  return value
24
24
  .split(",")
25
- .map((item) => item.trim())
26
- .filter((item) => item.length > 0);
25
+ .map(item => item.trim())
26
+ .filter(item => item.length > 0);
27
27
  }
28
28
  return [];
29
29
  }
@@ -60,7 +60,7 @@ export const handleCisaKev: SpecialHandler = async (
60
60
  return null;
61
61
  }
62
62
 
63
- const entry = data.vulnerabilities?.find((item) => item.cveID?.toUpperCase() === cveId);
63
+ const entry = data.vulnerabilities?.find(item => item.cveID?.toUpperCase() === cveId);
64
64
  if (!entry) return null;
65
65
 
66
66
  let md = `# ${entry.cveID}\n\n`;
@@ -42,7 +42,7 @@ function extractDoi(pathname: string): string | null {
42
42
  function formatAuthors(authors?: CrossrefAuthor[]): string | null {
43
43
  if (!authors || authors.length === 0) return null;
44
44
  const names = authors
45
- .map((author) => {
45
+ .map(author => {
46
46
  if (author.name) return author.name;
47
47
  const parts = [author.given, author.family].filter(Boolean);
48
48
  return parts.length > 0 ? parts.join(" ") : null;
@@ -67,7 +67,7 @@ function formatDate(date?: CrossrefDate): string | null {
67
67
 
68
68
  function formatAbstract(abstract?: string): string | null {
69
69
  if (!abstract) return null;
70
- const normalized = abstract.replace(/<\/?jats:p[^>]*>/g, (match) => (match.startsWith("</") ? "</p>" : "<p>"));
70
+ const normalized = abstract.replace(/<\/?jats:p[^>]*>/g, match => (match.startsWith("</") ? "</p>" : "<p>"));
71
71
  const markdown = htmlToBasicMarkdown(normalized);
72
72
  return markdown.trim().length > 0 ? markdown : null;
73
73
  }
@@ -62,7 +62,7 @@ export const handleDevTo: SpecialHandler = async (
62
62
  md += `by **${article.user?.name || "Unknown"}** (@${article.user?.username || "unknown"})`;
63
63
  md += `${readTime}${reactStr}\n`;
64
64
  md += `*${new Date(article.published_at || article.published_timestamp || "").toISOString().split("T")[0]}*\n`;
65
- if (tags.length > 0) md += `Tags: ${tags.map((t) => `#${t}`).join(", ")}\n`;
65
+ if (tags.length > 0) md += `Tags: ${tags.map(t => `#${t}`).join(", ")}\n`;
66
66
  if (article.description) md += `\n${article.description}\n`;
67
67
  md += `\n---\n\n`;
68
68
  }
@@ -104,7 +104,7 @@ export const handleDevTo: SpecialHandler = async (
104
104
  md += `### ${article.title}\n\n`;
105
105
  md += `${readTime.substring(3)}${reactStr}\n`;
106
106
  md += `*${new Date(article.published_at || article.published_timestamp || "").toISOString().split("T")[0]}*\n`;
107
- if (tags.length > 0) md += `Tags: ${tags.map((t) => `#${t}`).join(", ")}\n`;
107
+ if (tags.length > 0) md += `Tags: ${tags.map(t => `#${t}`).join(", ")}\n`;
108
108
  if (article.description) md += `\n${article.description}\n`;
109
109
  md += `\n---\n\n`;
110
110
  }
@@ -146,7 +146,7 @@ export const handleDevTo: SpecialHandler = async (
146
146
  if (readTime > 0) md += `**Reading time:** ${readTime} min\n`;
147
147
  if (reactions > 0) md += `**Reactions:** ${formatCount(reactions)}\n`;
148
148
  if (comments > 0) md += `**Comments:** ${formatCount(comments)}\n`;
149
- if (tags.length > 0) md += `**Tags:** ${tags.map((t) => `#${t}`).join(", ")}\n`;
149
+ if (tags.length > 0) md += `**Tags:** ${tags.map(t => `#${t}`).join(", ")}\n`;
150
150
  md += `\n---\n\n`;
151
151
 
152
152
  // Prefer body_markdown over body_html
@@ -4,7 +4,6 @@
4
4
  * Uses the Discogs API to extract structured metadata about releases.
5
5
  * API docs: https://www.discogs.com/developers
6
6
  */
7
-
8
7
  import type { RenderResult, SpecialHandler } from "./types";
9
8
  import { finalizeOutput, loadPage } from "./types";
10
9
 
@@ -76,7 +75,7 @@ interface DiscogsMaster {
76
75
  function formatArtists(artists: DiscogsArtist[] | undefined): string {
77
76
  if (!artists?.length) return "Unknown Artist";
78
77
  return artists
79
- .map((a) => {
78
+ .map(a => {
80
79
  const name = a.anv || a.name;
81
80
  const join = a.join || ", ";
82
81
  return name + (a.join ? ` ${join} ` : "");
@@ -126,7 +125,7 @@ function formatFormats(formats: DiscogsFormat[] | undefined): string {
126
125
  if (!formats?.length) return "";
127
126
 
128
127
  return formats
129
- .map((f) => {
128
+ .map(f => {
130
129
  const parts: string[] = [];
131
130
  if (f.qty && parseInt(f.qty, 10) > 1) parts.push(`${f.qty}×`);
132
131
  parts.push(f.name);
@@ -142,7 +141,7 @@ function formatFormats(formats: DiscogsFormat[] | undefined): string {
142
141
  function formatLabels(labels: DiscogsLabel[] | undefined): string {
143
142
  if (!labels?.length) return "";
144
143
  return labels
145
- .map((l) => {
144
+ .map(l => {
146
145
  if (l.catno && l.catno !== "none") return `${l.name} (${l.catno})`;
147
146
  return l.name;
148
147
  })
@@ -157,7 +157,7 @@ export const handleDiscourse: SpecialHandler = async (
157
157
  const fetchedAt = new Date().toISOString();
158
158
 
159
159
  const posts: DiscoursePost[] = [...(topic.post_stream?.posts ?? [])];
160
- if (requestedPost && !posts.some((post) => post.id === requestedPost?.id)) {
160
+ if (requestedPost && !posts.some(post => post.id === requestedPost?.id)) {
161
161
  posts.unshift(requestedPost);
162
162
  }
163
163
 
@@ -134,7 +134,7 @@ export const handleDockerHub: SpecialHandler = async (
134
134
  const size = tag.full_size ? formatSize(tag.full_size) : "-";
135
135
  const archs =
136
136
  tag.images
137
- ?.map((img) => img.architecture)
137
+ ?.map(img => img.architecture)
138
138
  .filter(Boolean)
139
139
  .join(", ") || "-";
140
140
  const updated = tag.last_updated ? new Date(tag.last_updated).toISOString().split("T")[0] : "-";
@@ -30,7 +30,7 @@ function pickLocalizedText(value?: LocalizedText): string | undefined {
30
30
  if (typeof value === "string") return value;
31
31
  const preferred = value["en-US"] ?? value.en_US ?? value.en;
32
32
  if (preferred) return preferred;
33
- const first = Object.values(value).find((entry) => typeof entry === "string");
33
+ const first = Object.values(value).find(entry => typeof entry === "string");
34
34
  return first;
35
35
  }
36
36
 
@@ -61,7 +61,7 @@ function collectAntiFeatures(data: FdroidPackage): string[] {
61
61
  function resolveSuggestedVersion(data: FdroidPackage): string | undefined {
62
62
  if (data.suggestedVersionName) return data.suggestedVersionName;
63
63
  if (data.suggestedVersionCode) {
64
- const match = data.packages?.find((pkg) => pkg.versionCode === data.suggestedVersionCode);
64
+ const match = data.packages?.find(pkg => pkg.versionCode === data.suggestedVersionCode);
65
65
  if (match?.versionName) return match.versionName;
66
66
  }
67
67
  return data.packages?.[0]?.versionName;
@@ -71,7 +71,7 @@ function normalizeCategories(categories?: string[] | Record<string, string[]>):
71
71
  }
72
72
 
73
73
  const seen = new Set<string>();
74
- return values.filter((item) => {
74
+ return values.filter(item => {
75
75
  if (seen.has(item)) return false;
76
76
  seen.add(item);
77
77
  return true;
@@ -135,8 +135,8 @@ export const handleFirefoxAddons: SpecialHandler = async (
135
135
  const description = descriptionRaw ? htmlToBasicMarkdown(descriptionRaw) : undefined;
136
136
 
137
137
  const authors = (data.authors ?? [])
138
- .map((author) => author.name ?? "")
139
- .map((author) => author.trim())
138
+ .map(author => author.name ?? "")
139
+ .map(author => author.trim())
140
140
  .filter(Boolean);
141
141
 
142
142
  const ratingAverage = data.ratings?.average;
@@ -63,7 +63,7 @@ function normalizeStringList(value: unknown): string[] {
63
63
  if (typeof value === "string") {
64
64
  return value
65
65
  .split(/[,;\n]+/)
66
- .map((item) => item.trim())
66
+ .map(item => item.trim())
67
67
  .filter(Boolean);
68
68
  }
69
69
  return [];
@@ -141,7 +141,7 @@ async function renderGitHubIssue(
141
141
  md += `**#${issue.number}** · ${issue.state} · opened by @${issue.user.login}\n`;
142
142
  md += `Created: ${issue.created_at} · Updated: ${issue.updated_at}\n`;
143
143
  if (issue.labels.length > 0) {
144
- md += `Labels: ${issue.labels.map((l) => l.name).join(", ")}\n`;
144
+ md += `Labels: ${issue.labels.map(l => l.name).join(", ")}\n`;
145
145
  }
146
146
  md += `\n---\n\n`;
147
147
  md += issue.body || "*No description provided.*";
@@ -196,7 +196,7 @@ async function renderGitHubIssuesList(
196
196
 
197
197
  for (const issue of issues) {
198
198
  if (issue.pull_request) continue; // Skip PRs in issues list
199
- const labels = issue.labels.length > 0 ? ` [${issue.labels.map((l) => l.name).join(", ")}]` : "";
199
+ const labels = issue.labels.length > 0 ? ` [${issue.labels.map(l => l.name).join(", ")}]` : "";
200
200
  md += `- **#${issue.number}** ${issue.title}${labels}\n`;
201
201
  md += ` by @${issue.user.login} · ${issue.comments} comments · ${issue.created_at}\n\n`;
202
202
  }
@@ -259,7 +259,7 @@ async function renderGitHubTree(
259
259
  md += "```\n\n";
260
260
 
261
261
  // Look for README in this directory
262
- const readmeFile = items.find((item) => item.type === "file" && /^readme\.md$/i.test(item.name));
262
+ const readmeFile = items.find(item => item.type === "file" && /^readme\.md$/i.test(item.name));
263
263
  if (readmeFile) {
264
264
  const readmePath = dirPath ? `${dirPath}/${readmeFile.name}` : readmeFile.name;
265
265
  const rawUrl = `https://raw.githubusercontent.com/${gh.owner}/${gh.repo}/${ref}/${readmePath}`;
@@ -197,8 +197,8 @@ async function renderGitLabTree(
197
197
  md += `**Ref:** ${gl.ref}\n\n`;
198
198
 
199
199
  // Separate directories and files
200
- const dirs = tree.filter((item) => item.type === "tree");
201
- const files = tree.filter((item) => item.type === "blob");
200
+ const dirs = tree.filter(item => item.type === "tree");
201
+ const files = tree.filter(item => item.type === "blob");
202
202
 
203
203
  if (dirs.length > 0) {
204
204
  md += `## Directories (${dirs.length})\n\n`;
@@ -260,7 +260,7 @@ async function renderGitLabIssue(
260
260
  }
261
261
 
262
262
  if (issue.assignees && issue.assignees.length > 0) {
263
- md += `**Assignees:** ${issue.assignees.map((a) => a.name).join(", ")}\n`;
263
+ md += `**Assignees:** ${issue.assignees.map(a => a.name).join(", ")}\n`;
264
264
  }
265
265
 
266
266
  md += `\n---\n\n## Description\n\n`;
@@ -317,7 +317,7 @@ async function renderGitLabMR(
317
317
  }
318
318
 
319
319
  if (mr.assignees && mr.assignees.length > 0) {
320
- md += `**Assignees:** ${mr.assignees.map((a) => a.name).join(", ")}\n`;
320
+ md += `**Assignees:** ${mr.assignees.map(a => a.name).join(", ")}\n`;
321
321
  }
322
322
 
323
323
  md += `\n---\n\n## Description\n\n`;
@@ -29,7 +29,7 @@ async function fetchItem(id: number, timeout: number, signal?: AbortSignal): Pro
29
29
  }
30
30
 
31
31
  async function fetchItems(ids: number[], timeout: number, limit = 20, signal?: AbortSignal): Promise<HNItem[]> {
32
- const promises = ids.slice(0, limit).map((id) => fetchItem(id, timeout, signal));
32
+ const promises = ids.slice(0, limit).map(id => fetchItem(id, timeout, signal));
33
33
  const results = await Promise.all(promises);
34
34
  return results.filter((item): item is HNItem => item !== null && !item.deleted && !item.dead);
35
35
  }
@@ -103,7 +103,7 @@ async function renderStory(item: HNItem, timeout: number, depth = 0, signal?: Ab
103
103
  if (comment.text) {
104
104
  const text = decodeHNText(comment.text);
105
105
  const lines = text.split("\n");
106
- output += `${lines.map((line) => `${indent}${line}`).join("\n")}\n\n`;
106
+ output += `${lines.map(line => `${indent}${line}`).join("\n")}\n\n`;
107
107
  }
108
108
 
109
109
  if (comment.kids && comment.kids.length > 0 && depth < 1) {
@@ -360,7 +360,7 @@ export const handleHuggingFace: SpecialHandler = async (url: string, timeout: nu
360
360
  if (user.numSpaces !== undefined) md += `**Spaces:** ${formatCount(user.numSpaces)}\n`;
361
361
 
362
362
  if (user.orgs?.length) {
363
- md += `**Organizations:** ${user.orgs.map((o) => o.name).join(", ")}\n`;
363
+ md += `**Organizations:** ${user.orgs.map(o => o.name).join(", ")}\n`;
364
364
  }
365
365
 
366
366
  const { content, truncated } = finalizeOutput(md);
@@ -38,10 +38,10 @@ export const handleIacr: SpecialHandler = async (
38
38
  doc.querySelector('meta[name="citation_title"]')?.getAttribute("content");
39
39
  const authors = doc
40
40
  .querySelectorAll('meta[name="citation_author"]')
41
- .map((m) => m.getAttribute("content"))
41
+ .map(m => m.getAttribute("content"))
42
42
  .filter(Boolean);
43
43
  // Abstract is in <p> after <h5>Abstract</h5>
44
- const abstractHeading = doc.querySelectorAll("h5").find((h) => h.text?.includes("Abstract"));
44
+ const abstractHeading = doc.querySelectorAll("h5").find(h => h.text?.includes("Abstract"));
45
45
  const abstract =
46
46
  abstractHeading?.parentNode?.querySelector("p")?.text?.trim() ||
47
47
  doc.querySelector('meta[name="description"]')?.getAttribute("content");
@@ -3,7 +3,6 @@
3
3
  *
4
4
  * Exports all special handlers for site-specific content extraction.
5
5
  */
6
-
7
6
  import { handleArtifactHub } from "./artifacthub";
8
7
  import { handleArxiv } from "./arxiv";
9
8
  import { handleAur } from "./aur";
@@ -114,7 +114,7 @@ export const handleJetBrainsMarketplace: SpecialHandler = async (
114
114
  const vendorName = plugin.vendor?.name ?? plugin.vendor?.publicName;
115
115
  const descriptionSource = plugin.description ?? plugin.preview ?? "";
116
116
  const description = descriptionSource ? htmlToBasicMarkdown(descriptionSource) : "";
117
- const tags = (plugin.tags ?? []).map((tag) => tag.name).filter(Boolean) as string[];
117
+ const tags = (plugin.tags ?? []).map(tag => tag.name).filter(Boolean) as string[];
118
118
  const rating = extractRating(plugin);
119
119
  const buildCompatibility = update ? formatBuildCompatibility(update) : null;
120
120
 
@@ -87,14 +87,14 @@ function formatAuthor(creator: LemmyCreator): string {
87
87
  function indentBlock(text: string, indent: string): string {
88
88
  return text
89
89
  .split("\n")
90
- .map((line) => `${indent}${line}`)
90
+ .map(line => `${indent}${line}`)
91
91
  .join("\n");
92
92
  }
93
93
 
94
94
  function renderComments(comments: LemmyCommentView[]): string {
95
95
  const childrenByParent = new Map<number, LemmyCommentView[]>();
96
96
 
97
- const commentIds = new Set(comments.map((view) => view.comment.id));
97
+ const commentIds = new Set(comments.map(view => view.comment.id));
98
98
 
99
99
  for (const commentView of comments) {
100
100
  const parentId = commentView.comment.parent_id;
@@ -124,10 +124,10 @@ export const handleMaven: SpecialHandler = async (
124
124
 
125
125
  // Add available classifiers/extensions if present
126
126
  if (doc.ec && doc.ec.length > 0) {
127
- const extensions = doc.ec.filter((e) => e && e !== "-");
127
+ const extensions = doc.ec.filter(e => e && e !== "-");
128
128
  if (extensions.length > 0) {
129
129
  md += `\n## Available Extensions\n\n`;
130
- md += `${extensions.map((e) => `- ${e}`).join("\n")}\n`;
130
+ md += `${extensions.map(e => `- ${e}`).join("\n")}\n`;
131
131
  }
132
132
  }
133
133
 
@@ -82,11 +82,9 @@ function convertMDNBody(sections: MDNSection[]): string {
82
82
  case "table":
83
83
  if (value.rows && value.rows.length > 0) {
84
84
  // Simple markdown table
85
- const header = value.rows[0].map((cell) => htmlToBasicMarkdown(cell)).join(" | ");
85
+ const header = value.rows[0].map(cell => htmlToBasicMarkdown(cell)).join(" | ");
86
86
  const separator = value.rows[0].map(() => "---").join(" | ");
87
- const bodyRows = value.rows
88
- .slice(1)
89
- .map((row) => row.map((cell) => htmlToBasicMarkdown(cell)).join(" | "));
87
+ const bodyRows = value.rows.slice(1).map(row => row.map(cell => htmlToBasicMarkdown(cell)).join(" | "));
90
88
 
91
89
  parts.push(`| ${header} |`);
92
90
  parts.push(`| ${separator} |`);
@@ -183,7 +183,7 @@ function formatModuleMarkdown(module: ModuleResponse, release: ReleaseResponse |
183
183
 
184
184
  // Show runtime dependencies
185
185
  const runtimeDeps = release.dependency?.filter(
186
- (d) => d.phase === "runtime" && d.relationship === "requires" && d.module !== "perl",
186
+ d => d.phase === "runtime" && d.relationship === "requires" && d.module !== "perl",
187
187
  );
188
188
  if (runtimeDeps?.length) {
189
189
  md += `\n## Dependencies\n\n`;
@@ -233,7 +233,7 @@ function formatReleaseMarkdown(release: ReleaseResponse): string {
233
233
 
234
234
  // Show runtime dependencies
235
235
  const runtimeDeps = release.dependency?.filter(
236
- (d) => d.phase === "runtime" && d.relationship === "requires" && d.module !== "perl",
236
+ d => d.phase === "runtime" && d.relationship === "requires" && d.module !== "perl",
237
237
  );
238
238
  if (runtimeDeps?.length) {
239
239
  md += `\n## Dependencies\n\n`;
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * MusicBrainz URL handler for artists, releases, and recordings
3
3
  */
4
-
5
4
  import type { RenderResult, SpecialHandler } from "./types";
6
5
  import { finalizeOutput, loadPage } from "./types";
7
6
 
@@ -133,7 +132,7 @@ function formatArtistCredits(credits: MusicBrainzArtistCredit[] | undefined): st
133
132
  if (!credits?.length) return null;
134
133
 
135
134
  const names = credits
136
- .map((credit) => credit.name || credit.artist?.name)
135
+ .map(credit => credit.name || credit.artist?.name)
137
136
  .filter((name): name is string => Boolean(name));
138
137
 
139
138
  if (!names.length) return null;
@@ -84,7 +84,7 @@ export const handleNpm: SpecialHandler = async (
84
84
  const repoUrl = typeof pkg.repository === "string" ? pkg.repository : pkg.repository?.url;
85
85
  if (repoUrl) md += `**Repository:** ${repoUrl.replace(/^git\+/, "").replace(/\.git$/, "")}\n`;
86
86
  if (pkg.keywords?.length) md += `**Keywords:** ${pkg.keywords.join(", ")}\n`;
87
- if (pkg.maintainers?.length) md += `**Maintainers:** ${pkg.maintainers.map((m) => m.name).join(", ")}\n`;
87
+ if (pkg.maintainers?.length) md += `**Maintainers:** ${pkg.maintainers.map(m => m.name).join(", ")}\n`;
88
88
 
89
89
  if (pkg.dependencies && Object.keys(pkg.dependencies).length > 0) {
90
90
  md += `\n## Dependencies\n\n`;
@@ -106,7 +106,7 @@ export const handleNuGet: SpecialHandler = async (
106
106
 
107
107
  if (pageItems) {
108
108
  const found = pageItems.find(
109
- (item) => item.catalogEntry.version.toLowerCase() === requestedVersion.toLowerCase(),
109
+ item => item.catalogEntry.version.toLowerCase() === requestedVersion.toLowerCase(),
110
110
  );
111
111
  if (found) {
112
112
  targetEntry = found.catalogEntry;
@@ -162,7 +162,7 @@ export const handleNuGet: SpecialHandler = async (
162
162
 
163
163
  // Show dependencies by target framework
164
164
  if (targetEntry.dependencyGroups?.length) {
165
- const hasAnyDeps = targetEntry.dependencyGroups.some((g) => g.dependencies?.length);
165
+ const hasAnyDeps = targetEntry.dependencyGroups.some(g => g.dependencies?.length);
166
166
  if (hasAnyDeps) {
167
167
  md += `\n## Dependencies\n\n`;
168
168
  for (const group of targetEntry.dependencyGroups) {
@@ -119,7 +119,7 @@ export const handleNvd: SpecialHandler = async (
119
119
  md += ` · **Modified:** ${formatDate(vuln.lastModified)}\n\n`;
120
120
 
121
121
  // Description
122
- const desc = vuln.descriptions.find((d) => d.lang === "en")?.value;
122
+ const desc = vuln.descriptions.find(d => d.lang === "en")?.value;
123
123
  if (desc) {
124
124
  md += `## Description\n\n${desc}\n\n`;
125
125
  }
@@ -165,8 +165,8 @@ export const handleNvd: SpecialHandler = async (
165
165
 
166
166
  // Weaknesses (CWE)
167
167
  const cwes = vuln.weaknesses
168
- ?.flatMap((w) => w.description)
169
- .filter((d) => d.lang === "en" && d.value !== "NVD-CWE-Other" && d.value !== "NVD-CWE-noinfo");
168
+ ?.flatMap(w => w.description)
169
+ .filter(d => d.lang === "en" && d.value !== "NVD-CWE-Other" && d.value !== "NVD-CWE-noinfo");
170
170
 
171
171
  if (cwes?.length) {
172
172
  md += `## Weaknesses\n\n`;
@@ -108,7 +108,7 @@ function formatSize(bytes: number): string {
108
108
  }
109
109
 
110
110
  function buildModelPath(parts: string[]): string {
111
- return parts.map((part) => encodeURIComponent(part)).join("/");
111
+ return parts.map(part => encodeURIComponent(part)).join("/");
112
112
  }
113
113
 
114
114
  function parseOllamaUrl(url: string): { modelRef: string; baseRef: string; pageUrl: string } | null {
@@ -152,7 +152,7 @@ function sortTags(tags: string[]): string[] {
152
152
 
153
153
  function formatTagList(tags: string[], maxItems: number): string {
154
154
  const limited = tags.slice(0, maxItems);
155
- const formatted = limited.map((tag) => `\`${tag}\``).join(", ");
155
+ const formatted = limited.map(tag => `\`${tag}\``).join(", ");
156
156
  if (tags.length > maxItems) {
157
157
  return `${formatted} (and ${tags.length - maxItems} more)`;
158
158
  }
@@ -205,17 +205,17 @@ export const handleOllama: SpecialHandler = async (
205
205
 
206
206
  const baseLower = baseRef.toLowerCase();
207
207
  const models = tagsData?.models ?? [];
208
- const matchingModels = models.filter((model) => {
208
+ const matchingModels = models.filter(model => {
209
209
  const name = (model.model ?? model.name ?? "").toLowerCase();
210
210
  return name === baseLower || name.startsWith(`${baseLower}:`);
211
211
  });
212
212
 
213
213
  const tagRef = modelRef.includes(":") ? modelRef : null;
214
- const selectedTag = tagRef ? matchingModels.find((model) => (model.model ?? model.name ?? "") === tagRef) : null;
214
+ const selectedTag = tagRef ? matchingModels.find(model => (model.model ?? model.name ?? "") === tagRef) : null;
215
215
 
216
216
  const availableTagsRaw = matchingModels
217
- .map((model) => model.model ?? model.name ?? "")
218
- .filter((tag) => tag.length > 0);
217
+ .map(model => model.model ?? model.name ?? "")
218
+ .filter(tag => tag.length > 0);
219
219
  const availableTags = sortTags(Array.from(new Set(availableTagsRaw)));
220
220
 
221
221
  const fallbackTags = sortTags(Array.from(new Set(htmlTags)));
@@ -223,9 +223,7 @@ export const handleOllama: SpecialHandler = async (
223
223
 
224
224
  const parameterSizes = collectParameterSizes(selectedTag ? [selectedTag] : matchingModels, htmlParameterSizes);
225
225
 
226
- const sizes = matchingModels
227
- .map((model) => model.size)
228
- .filter((size): size is number => typeof size === "number");
226
+ const sizes = matchingModels.map(model => model.size).filter((size): size is number => typeof size === "number");
229
227
  let sizeLine: string | null = null;
230
228
 
231
229
  if (selectedTag?.size) {
@@ -166,8 +166,8 @@ export const handleOpenCorporates: SpecialHandler = async (
166
166
 
167
167
  // Officers/Directors
168
168
  if (company.officers && company.officers.length > 0) {
169
- const activeOfficers = company.officers.filter((o) => !o.officer.inactive && !o.officer.end_date);
170
- const inactiveOfficers = company.officers.filter((o) => o.officer.inactive || o.officer.end_date);
169
+ const activeOfficers = company.officers.filter(o => !o.officer.inactive && !o.officer.end_date);
170
+ const inactiveOfficers = company.officers.filter(o => o.officer.inactive || o.officer.end_date);
171
171
 
172
172
  if (activeOfficers.length > 0) {
173
173
  md += `## Current Officers (${activeOfficers.length})\n\n`;
@@ -129,7 +129,7 @@ async function fetchWork(workId: string, timeout: number, signal?: AbortSignal):
129
129
  // Fetch author names if we have author keys
130
130
  if (work.authors?.length) {
131
131
  const authorNames = await fetchAuthorNames(
132
- work.authors.map((a) => a.author.key),
132
+ work.authors.map(a => a.author.key),
133
133
  timeout,
134
134
  signal,
135
135
  );
@@ -179,7 +179,7 @@ async function fetchEdition(editionId: string, timeout: number, signal?: AbortSi
179
179
  // Fetch author names
180
180
  if (edition.authors?.length) {
181
181
  const authorNames = await fetchAuthorNames(
182
- edition.authors.map((a) => a.key),
182
+ edition.authors.map(a => a.key),
183
183
  timeout,
184
184
  signal,
185
185
  );
@@ -250,11 +250,11 @@ async function fetchByIsbn(isbn: string, timeout: number, signal?: AbortSignal):
250
250
  let md = `# ${book.title}\n\n`;
251
251
 
252
252
  if (book.authors?.length) {
253
- md += `**Authors:** ${book.authors.map((a) => a.name).join(", ")}\n`;
253
+ md += `**Authors:** ${book.authors.map(a => a.name).join(", ")}\n`;
254
254
  }
255
255
 
256
256
  if (book.publishers?.length) {
257
- md += `**Publishers:** ${book.publishers.map((p) => p.name).join(", ")}\n`;
257
+ md += `**Publishers:** ${book.publishers.map(p => p.name).join(", ")}\n`;
258
258
  }
259
259
 
260
260
  if (book.publish_date) {
@@ -280,7 +280,7 @@ async function fetchByIsbn(isbn: string, timeout: number, signal?: AbortSignal):
280
280
  if (book.subjects?.length) {
281
281
  md += `## Subjects\n\n${book.subjects
282
282
  .slice(0, 20)
283
- .map((s) => s.name)
283
+ .map(s => s.name)
284
284
  .join(", ")}\n`;
285
285
  }
286
286
 
@@ -291,7 +291,7 @@ async function fetchAuthorNames(authorKeys: string[], timeout: number, signal?:
291
291
  const names: string[] = [];
292
292
 
293
293
  // Fetch authors in parallel (limit to first 5)
294
- const promises = authorKeys.slice(0, 5).map(async (key) => {
294
+ const promises = authorKeys.slice(0, 5).map(async key => {
295
295
  const authorKey = key.startsWith("/authors/") ? key : `/authors/${key}`;
296
296
  const apiUrl = `https://openlibrary.org${authorKey}.json`;
297
297
  try {
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * ORCID handler for web-fetch
3
3
  */
4
-
5
4
  import type { RenderResult, SpecialHandler } from "./types";
6
5
  import { finalizeOutput, loadPage } from "./types";
7
6