@oh-my-pi/pi-coding-agent 3.30.0 → 3.31.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 +71 -0
- package/package.json +5 -5
- package/src/cli/args.ts +4 -0
- package/src/core/agent-session.ts +29 -2
- package/src/core/bash-executor.ts +2 -1
- package/src/core/custom-commands/bundled/review/index.ts +369 -14
- package/src/core/custom-commands/bundled/wt/index.ts +1 -1
- package/src/core/session-manager.ts +158 -246
- package/src/core/session-storage.ts +379 -0
- package/src/core/settings-manager.ts +155 -4
- package/src/core/system-prompt.ts +62 -64
- package/src/core/tools/ask.ts +5 -4
- package/src/core/tools/bash-interceptor.ts +26 -61
- package/src/core/tools/bash.ts +13 -8
- package/src/core/tools/edit-diff.ts +11 -4
- package/src/core/tools/edit.ts +7 -13
- package/src/core/tools/find.ts +111 -50
- package/src/core/tools/gemini-image.ts +128 -147
- package/src/core/tools/grep.ts +397 -415
- package/src/core/tools/index.test.ts +5 -1
- package/src/core/tools/index.ts +6 -8
- package/src/core/tools/ls.ts +12 -10
- package/src/core/tools/lsp/client.ts +58 -9
- package/src/core/tools/lsp/config.ts +205 -656
- package/src/core/tools/lsp/defaults.json +465 -0
- package/src/core/tools/lsp/index.ts +55 -32
- package/src/core/tools/lsp/rust-analyzer.ts +49 -10
- package/src/core/tools/lsp/types.ts +1 -0
- package/src/core/tools/lsp/utils.ts +1 -1
- package/src/core/tools/read.ts +150 -74
- package/src/core/tools/render-utils.ts +70 -10
- package/src/core/tools/review.ts +38 -126
- package/src/core/tools/task/artifacts.ts +5 -4
- package/src/core/tools/task/executor.ts +94 -83
- package/src/core/tools/task/index.ts +129 -92
- package/src/core/tools/task/parallel.ts +30 -3
- package/src/core/tools/task/render.ts +85 -39
- package/src/core/tools/task/types.ts +15 -6
- package/src/core/tools/task/worker.ts +124 -89
- package/src/core/tools/web-fetch.ts +112 -377
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/artifacthub.ts +6 -1
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/arxiv.ts +8 -4
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/aur.ts +6 -2
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/biorxiv.ts +6 -1
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/bluesky.ts +10 -3
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/brew.ts +6 -2
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/cheatsh.ts +6 -1
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/chocolatey.ts +6 -1
- package/src/core/tools/web-scrapers/choosealicense.ts +110 -0
- package/src/core/tools/web-scrapers/cisa-kev.ts +100 -0
- package/src/core/tools/web-scrapers/clojars.ts +180 -0
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/coingecko.ts +6 -1
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/crates-io.ts +7 -2
- package/src/core/tools/web-scrapers/crossref.ts +149 -0
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/devto.ts +8 -4
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/discogs.ts +6 -1
- package/src/core/tools/web-scrapers/discourse.ts +221 -0
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/dockerhub.ts +7 -3
- package/src/core/tools/web-scrapers/fdroid.ts +158 -0
- package/src/core/tools/web-scrapers/firefox-addons.ts +214 -0
- package/src/core/tools/web-scrapers/flathub.ts +239 -0
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/github-gist.ts +6 -2
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/github.ts +63 -32
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/gitlab.ts +31 -19
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/go-pkg.ts +8 -4
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/hackage.ts +6 -1
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/hackernews.ts +18 -18
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/hex.ts +3 -3
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/huggingface.ts +10 -10
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/iacr.ts +8 -4
- package/src/core/tools/web-scrapers/index.ts +250 -0
- package/src/core/tools/web-scrapers/jetbrains-marketplace.ts +169 -0
- package/src/core/tools/web-scrapers/lemmy.ts +220 -0
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/lobsters.ts +3 -3
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/mastodon.ts +11 -3
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/maven.ts +6 -1
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/mdn.ts +2 -2
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/metacpan.ts +13 -7
- package/src/core/tools/web-scrapers/musicbrainz.ts +273 -0
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/npm.ts +12 -5
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/nuget.ts +9 -5
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/nvd.ts +6 -1
- package/src/core/tools/web-scrapers/ollama.ts +267 -0
- package/src/core/tools/web-scrapers/open-vsx.ts +119 -0
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/opencorporates.ts +2 -0
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/openlibrary.ts +18 -12
- package/src/core/tools/web-scrapers/orcid.ts +299 -0
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/osv.ts +6 -1
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/packagist.ts +6 -2
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/pub-dev.ts +3 -3
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/pubmed.ts +8 -4
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/pypi.ts +7 -3
- package/src/core/tools/web-scrapers/rawg.ts +124 -0
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/readthedocs.ts +7 -3
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/reddit.ts +6 -2
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/repology.ts +6 -1
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/rfc.ts +7 -3
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/rubygems.ts +6 -1
- package/src/core/tools/web-scrapers/searchcode.ts +217 -0
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/sec-edgar.ts +6 -1
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/semantic-scholar.ts +2 -2
- package/src/core/tools/web-scrapers/snapcraft.ts +200 -0
- package/src/core/tools/web-scrapers/sourcegraph.ts +373 -0
- package/src/core/tools/web-scrapers/spdx.ts +121 -0
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/spotify.ts +3 -3
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/stackoverflow.ts +3 -2
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/terraform.ts +11 -3
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/tldr.ts +6 -2
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/twitter.ts +15 -3
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/types.ts +98 -27
- package/src/core/tools/web-scrapers/utils.ts +162 -0
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/vimeo.ts +3 -3
- package/src/core/tools/web-scrapers/vscode-marketplace.ts +195 -0
- package/src/core/tools/web-scrapers/w3c.ts +163 -0
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/wikidata.ts +13 -5
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/wikipedia.ts +7 -3
- package/src/core/tools/{web-fetch-handlers → web-scrapers}/youtube.ts +72 -20
- package/src/core/tools/write.ts +21 -18
- package/src/core/voice.ts +3 -2
- package/src/lib/worktree/collapse.ts +2 -1
- package/src/lib/worktree/git.ts +2 -18
- package/src/main.ts +59 -3
- package/src/modes/interactive/components/extensions/extension-dashboard.ts +33 -19
- package/src/modes/interactive/components/extensions/extension-list.ts +15 -8
- package/src/modes/interactive/components/hook-editor.ts +2 -1
- package/src/modes/interactive/components/model-selector.ts +19 -4
- package/src/modes/interactive/interactive-mode.ts +41 -38
- package/src/modes/interactive/theme/theme.ts +58 -58
- package/src/modes/rpc/rpc-mode.ts +10 -9
- package/src/prompts/review-request.md +27 -0
- package/src/prompts/reviewer.md +64 -68
- package/src/prompts/tools/output.md +22 -3
- package/src/prompts/tools/task.md +32 -33
- package/src/utils/clipboard.ts +2 -1
- package/examples/extensions/subagent/agents/reviewer.md +0 -35
- package/src/core/tools/web-fetch-handlers/index.ts +0 -69
- package/src/core/tools/web-fetch-handlers/utils.ts +0 -91
- /package/src/core/tools/{web-fetch-handlers → web-scrapers}/academic.test.ts +0 -0
- /package/src/core/tools/{web-fetch-handlers → web-scrapers}/business.test.ts +0 -0
- /package/src/core/tools/{web-fetch-handlers → web-scrapers}/dev-platforms.test.ts +0 -0
- /package/src/core/tools/{web-fetch-handlers → web-scrapers}/documentation.test.ts +0 -0
- /package/src/core/tools/{web-fetch-handlers → web-scrapers}/finance-media.test.ts +0 -0
- /package/src/core/tools/{web-fetch-handlers → web-scrapers}/git-hosting.test.ts +0 -0
- /package/src/core/tools/{web-fetch-handlers → web-scrapers}/media.test.ts +0 -0
- /package/src/core/tools/{web-fetch-handlers → web-scrapers}/package-managers-2.test.ts +0 -0
- /package/src/core/tools/{web-fetch-handlers → web-scrapers}/package-managers.test.ts +0 -0
- /package/src/core/tools/{web-fetch-handlers → web-scrapers}/package-registries.test.ts +0 -0
- /package/src/core/tools/{web-fetch-handlers → web-scrapers}/research.test.ts +0 -0
- /package/src/core/tools/{web-fetch-handlers → web-scrapers}/security.test.ts +0 -0
- /package/src/core/tools/{web-fetch-handlers → web-scrapers}/social-extended.test.ts +0 -0
- /package/src/core/tools/{web-fetch-handlers → web-scrapers}/social.test.ts +0 -0
- /package/src/core/tools/{web-fetch-handlers → web-scrapers}/stackexchange.test.ts +0 -0
- /package/src/core/tools/{web-fetch-handlers → web-scrapers}/standards.test.ts +0 -0
- /package/src/core/tools/{web-fetch-handlers → web-scrapers}/wikipedia.test.ts +0 -0
- /package/src/core/tools/{web-fetch-handlers → web-scrapers}/youtube.test.ts +0 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web Fetch Special Handlers Index
|
|
3
|
+
*
|
|
4
|
+
* Exports all special handlers for site-specific content extraction.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { handleArtifactHub } from "./artifacthub";
|
|
8
|
+
import { handleArxiv } from "./arxiv";
|
|
9
|
+
import { handleAur } from "./aur";
|
|
10
|
+
import { handleBiorxiv } from "./biorxiv";
|
|
11
|
+
import { handleBluesky } from "./bluesky";
|
|
12
|
+
import { handleBrew } from "./brew";
|
|
13
|
+
import { handleCheatSh } from "./cheatsh";
|
|
14
|
+
import { handleChocolatey } from "./chocolatey";
|
|
15
|
+
import { handleChooseALicense } from "./choosealicense";
|
|
16
|
+
import { handleCisaKev } from "./cisa-kev";
|
|
17
|
+
import { handleClojars } from "./clojars";
|
|
18
|
+
import { handleCoinGecko } from "./coingecko";
|
|
19
|
+
import { handleCratesIo } from "./crates-io";
|
|
20
|
+
import { handleCrossref } from "./crossref";
|
|
21
|
+
import { handleDevTo } from "./devto";
|
|
22
|
+
import { handleDiscogs } from "./discogs";
|
|
23
|
+
import { handleDiscourse } from "./discourse";
|
|
24
|
+
import { handleDockerHub } from "./dockerhub";
|
|
25
|
+
import { handleFdroid } from "./fdroid";
|
|
26
|
+
import { handleFirefoxAddons } from "./firefox-addons";
|
|
27
|
+
import { handleFlathub } from "./flathub";
|
|
28
|
+
import { fetchGitHubApi, handleGitHub } from "./github";
|
|
29
|
+
import { handleGitHubGist } from "./github-gist";
|
|
30
|
+
import { handleGitLab } from "./gitlab";
|
|
31
|
+
import { handleGoPkg } from "./go-pkg";
|
|
32
|
+
import { handleHackage } from "./hackage";
|
|
33
|
+
import { handleHackerNews } from "./hackernews";
|
|
34
|
+
import { handleHex } from "./hex";
|
|
35
|
+
import { handleHuggingFace } from "./huggingface";
|
|
36
|
+
import { handleIacr } from "./iacr";
|
|
37
|
+
import { handleJetBrainsMarketplace } from "./jetbrains-marketplace";
|
|
38
|
+
import { handleLemmy } from "./lemmy";
|
|
39
|
+
import { handleLobsters } from "./lobsters";
|
|
40
|
+
import { handleMastodon } from "./mastodon";
|
|
41
|
+
import { handleMaven } from "./maven";
|
|
42
|
+
import { handleMDN } from "./mdn";
|
|
43
|
+
import { handleMetaCPAN } from "./metacpan";
|
|
44
|
+
import { handleMusicBrainz } from "./musicbrainz";
|
|
45
|
+
import { handleNpm } from "./npm";
|
|
46
|
+
import { handleNuGet } from "./nuget";
|
|
47
|
+
import { handleNvd } from "./nvd";
|
|
48
|
+
import { handleOllama } from "./ollama";
|
|
49
|
+
import { handleOpenVsx } from "./open-vsx";
|
|
50
|
+
import { handleOpenCorporates } from "./opencorporates";
|
|
51
|
+
import { handleOpenLibrary } from "./openlibrary";
|
|
52
|
+
import { handleOrcid } from "./orcid";
|
|
53
|
+
import { handleOsv } from "./osv";
|
|
54
|
+
import { handlePackagist } from "./packagist";
|
|
55
|
+
import { handlePubDev } from "./pub-dev";
|
|
56
|
+
import { handlePubMed } from "./pubmed";
|
|
57
|
+
import { handlePyPI } from "./pypi";
|
|
58
|
+
import { handleRawg } from "./rawg";
|
|
59
|
+
import { handleReadTheDocs } from "./readthedocs";
|
|
60
|
+
import { handleReddit } from "./reddit";
|
|
61
|
+
import { handleRepology } from "./repology";
|
|
62
|
+
import { handleRfc } from "./rfc";
|
|
63
|
+
import { handleRubyGems } from "./rubygems";
|
|
64
|
+
import { handleSearchcode } from "./searchcode";
|
|
65
|
+
import { handleSecEdgar } from "./sec-edgar";
|
|
66
|
+
import { handleSemanticScholar } from "./semantic-scholar";
|
|
67
|
+
import { handleSnapcraft } from "./snapcraft";
|
|
68
|
+
import { handleSourcegraph } from "./sourcegraph";
|
|
69
|
+
import { handleSpdx } from "./spdx";
|
|
70
|
+
import { handleSpotify } from "./spotify";
|
|
71
|
+
import { handleStackOverflow } from "./stackoverflow";
|
|
72
|
+
import { handleTerraform } from "./terraform";
|
|
73
|
+
import { handleTldr } from "./tldr";
|
|
74
|
+
import { handleTwitter } from "./twitter";
|
|
75
|
+
import type { SpecialHandler } from "./types";
|
|
76
|
+
import { handleVimeo } from "./vimeo";
|
|
77
|
+
import { handleVscodeMarketplace } from "./vscode-marketplace";
|
|
78
|
+
import { handleW3c } from "./w3c";
|
|
79
|
+
import { handleWikidata } from "./wikidata";
|
|
80
|
+
import { handleWikipedia } from "./wikipedia";
|
|
81
|
+
import { handleYouTube } from "./youtube";
|
|
82
|
+
|
|
83
|
+
export type { RenderResult, SpecialHandler } from "./types";
|
|
84
|
+
|
|
85
|
+
export {
|
|
86
|
+
fetchGitHubApi,
|
|
87
|
+
handleArtifactHub,
|
|
88
|
+
handleArxiv,
|
|
89
|
+
handleAur,
|
|
90
|
+
handleBiorxiv,
|
|
91
|
+
handleBluesky,
|
|
92
|
+
handleBrew,
|
|
93
|
+
handleCheatSh,
|
|
94
|
+
handleCisaKev,
|
|
95
|
+
handleChocolatey,
|
|
96
|
+
handleClojars,
|
|
97
|
+
handleChooseALicense,
|
|
98
|
+
handleCoinGecko,
|
|
99
|
+
handleCratesIo,
|
|
100
|
+
handleCrossref,
|
|
101
|
+
handleDevTo,
|
|
102
|
+
handleDiscogs,
|
|
103
|
+
handleDiscourse,
|
|
104
|
+
handleDockerHub,
|
|
105
|
+
handleFdroid,
|
|
106
|
+
handleFlathub,
|
|
107
|
+
handleFirefoxAddons,
|
|
108
|
+
handleGitHub,
|
|
109
|
+
handleGitHubGist,
|
|
110
|
+
handleGitLab,
|
|
111
|
+
handleGoPkg,
|
|
112
|
+
handleHackage,
|
|
113
|
+
handleHackerNews,
|
|
114
|
+
handleHex,
|
|
115
|
+
handleHuggingFace,
|
|
116
|
+
handleIacr,
|
|
117
|
+
handleJetBrainsMarketplace,
|
|
118
|
+
handleLemmy,
|
|
119
|
+
handleLobsters,
|
|
120
|
+
handleMastodon,
|
|
121
|
+
handleMaven,
|
|
122
|
+
handleMDN,
|
|
123
|
+
handleMetaCPAN,
|
|
124
|
+
handleMusicBrainz,
|
|
125
|
+
handleNpm,
|
|
126
|
+
handleNuGet,
|
|
127
|
+
handleNvd,
|
|
128
|
+
handleOllama,
|
|
129
|
+
handleOpenCorporates,
|
|
130
|
+
handleOpenLibrary,
|
|
131
|
+
handleOrcid,
|
|
132
|
+
handleOpenVsx,
|
|
133
|
+
handleOsv,
|
|
134
|
+
handlePackagist,
|
|
135
|
+
handlePubDev,
|
|
136
|
+
handlePubMed,
|
|
137
|
+
handlePyPI,
|
|
138
|
+
handleRawg,
|
|
139
|
+
handleReadTheDocs,
|
|
140
|
+
handleReddit,
|
|
141
|
+
handleRepology,
|
|
142
|
+
handleRfc,
|
|
143
|
+
handleRubyGems,
|
|
144
|
+
handleSecEdgar,
|
|
145
|
+
handleSearchcode,
|
|
146
|
+
handleSemanticScholar,
|
|
147
|
+
handleSnapcraft,
|
|
148
|
+
handleSourcegraph,
|
|
149
|
+
handleSpotify,
|
|
150
|
+
handleSpdx,
|
|
151
|
+
handleStackOverflow,
|
|
152
|
+
handleTerraform,
|
|
153
|
+
handleTldr,
|
|
154
|
+
handleTwitter,
|
|
155
|
+
handleVimeo,
|
|
156
|
+
handleVscodeMarketplace,
|
|
157
|
+
handleW3c,
|
|
158
|
+
handleWikidata,
|
|
159
|
+
handleWikipedia,
|
|
160
|
+
handleYouTube,
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export const specialHandlers: SpecialHandler[] = [
|
|
164
|
+
// Git hosting
|
|
165
|
+
handleGitHubGist,
|
|
166
|
+
handleGitHub,
|
|
167
|
+
handleGitLab,
|
|
168
|
+
// Video/Media
|
|
169
|
+
handleYouTube,
|
|
170
|
+
handleVimeo,
|
|
171
|
+
handleSpotify,
|
|
172
|
+
handleDiscogs,
|
|
173
|
+
handleMusicBrainz,
|
|
174
|
+
// Games
|
|
175
|
+
handleRawg,
|
|
176
|
+
// Social/News
|
|
177
|
+
handleTwitter,
|
|
178
|
+
handleBluesky,
|
|
179
|
+
handleMastodon,
|
|
180
|
+
handleLemmy,
|
|
181
|
+
handleHackerNews,
|
|
182
|
+
handleLobsters,
|
|
183
|
+
handleReddit,
|
|
184
|
+
handleDiscourse,
|
|
185
|
+
// Developer content
|
|
186
|
+
handleStackOverflow,
|
|
187
|
+
handleDevTo,
|
|
188
|
+
handleMDN,
|
|
189
|
+
handleReadTheDocs,
|
|
190
|
+
handleSearchcode,
|
|
191
|
+
handleSourcegraph,
|
|
192
|
+
handleTldr,
|
|
193
|
+
handleCheatSh,
|
|
194
|
+
// Package registries
|
|
195
|
+
handleNpm,
|
|
196
|
+
handleFirefoxAddons,
|
|
197
|
+
handleVscodeMarketplace,
|
|
198
|
+
handleNuGet,
|
|
199
|
+
handleChocolatey,
|
|
200
|
+
handleClojars,
|
|
201
|
+
handleBrew,
|
|
202
|
+
handlePyPI,
|
|
203
|
+
handleCratesIo,
|
|
204
|
+
handleDockerHub,
|
|
205
|
+
handleFdroid,
|
|
206
|
+
handleFlathub,
|
|
207
|
+
handleGoPkg,
|
|
208
|
+
handleHex,
|
|
209
|
+
handlePackagist,
|
|
210
|
+
handlePubDev,
|
|
211
|
+
handleMaven,
|
|
212
|
+
handleJetBrainsMarketplace,
|
|
213
|
+
handleOpenVsx,
|
|
214
|
+
handleArtifactHub,
|
|
215
|
+
handleRubyGems,
|
|
216
|
+
handleTerraform,
|
|
217
|
+
handleAur,
|
|
218
|
+
handleHackage,
|
|
219
|
+
handleMetaCPAN,
|
|
220
|
+
handleRepology,
|
|
221
|
+
handleSnapcraft,
|
|
222
|
+
// ML/AI
|
|
223
|
+
handleHuggingFace,
|
|
224
|
+
handleOllama,
|
|
225
|
+
// Academic
|
|
226
|
+
handleArxiv,
|
|
227
|
+
handleBiorxiv,
|
|
228
|
+
handleCrossref,
|
|
229
|
+
handleIacr,
|
|
230
|
+
handleOrcid,
|
|
231
|
+
handleSemanticScholar,
|
|
232
|
+
handlePubMed,
|
|
233
|
+
handleRfc,
|
|
234
|
+
// Security
|
|
235
|
+
handleCisaKev,
|
|
236
|
+
handleNvd,
|
|
237
|
+
handleOsv,
|
|
238
|
+
// Crypto
|
|
239
|
+
handleCoinGecko,
|
|
240
|
+
// Business
|
|
241
|
+
handleOpenCorporates,
|
|
242
|
+
handleSecEdgar,
|
|
243
|
+
// Reference
|
|
244
|
+
handleOpenLibrary,
|
|
245
|
+
handleChooseALicense,
|
|
246
|
+
handleW3c,
|
|
247
|
+
handleSpdx,
|
|
248
|
+
handleWikidata,
|
|
249
|
+
handleWikipedia,
|
|
250
|
+
];
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import type { RenderResult, SpecialHandler } from "./types";
|
|
2
|
+
import { finalizeOutput, formatCount, htmlToBasicMarkdown, loadPage } from "./types";
|
|
3
|
+
|
|
4
|
+
interface PluginVendor {
|
|
5
|
+
name?: string;
|
|
6
|
+
publicName?: string;
|
|
7
|
+
url?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface PluginTag {
|
|
11
|
+
name?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type PluginRating =
|
|
15
|
+
| number
|
|
16
|
+
| {
|
|
17
|
+
rating?: number;
|
|
18
|
+
value?: number;
|
|
19
|
+
score?: number;
|
|
20
|
+
votes?: number;
|
|
21
|
+
totalVotes?: number;
|
|
22
|
+
count?: number;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
interface PluginData {
|
|
26
|
+
id?: number;
|
|
27
|
+
name?: string;
|
|
28
|
+
description?: string;
|
|
29
|
+
preview?: string;
|
|
30
|
+
vendor?: PluginVendor;
|
|
31
|
+
rating?: PluginRating;
|
|
32
|
+
ratingCount?: number;
|
|
33
|
+
downloads?: number;
|
|
34
|
+
tags?: PluginTag[];
|
|
35
|
+
urls?: {
|
|
36
|
+
url?: string;
|
|
37
|
+
docUrl?: string;
|
|
38
|
+
sourceCodeUrl?: string;
|
|
39
|
+
bugtrackerUrl?: string;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface UpdateData {
|
|
44
|
+
version?: string;
|
|
45
|
+
since?: string;
|
|
46
|
+
until?: string;
|
|
47
|
+
sinceUntil?: string;
|
|
48
|
+
channel?: string;
|
|
49
|
+
downloads?: number;
|
|
50
|
+
compatibleVersions?: Record<string, string>;
|
|
51
|
+
cdate?: string | number;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const MARKETPLACE_HOSTS = new Set(["plugins.jetbrains.com"]);
|
|
55
|
+
|
|
56
|
+
function extractRating(plugin: PluginData): { value: number | null; votes: number | null } {
|
|
57
|
+
const rating = plugin.rating;
|
|
58
|
+
if (typeof rating === "number" && Number.isFinite(rating)) {
|
|
59
|
+
return { value: rating, votes: plugin.ratingCount ?? null };
|
|
60
|
+
}
|
|
61
|
+
if (rating && typeof rating === "object") {
|
|
62
|
+
const value = rating.rating ?? rating.value ?? rating.score ?? null;
|
|
63
|
+
const votes = rating.votes ?? rating.totalVotes ?? rating.count ?? plugin.ratingCount ?? null;
|
|
64
|
+
return { value: typeof value === "number" ? value : null, votes: typeof votes === "number" ? votes : null };
|
|
65
|
+
}
|
|
66
|
+
return { value: null, votes: plugin.ratingCount ?? null };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function formatBuildCompatibility(update: UpdateData): string | null {
|
|
70
|
+
if (update.sinceUntil) return update.sinceUntil;
|
|
71
|
+
if (update.since && update.until) return `${update.since} - ${update.until}`;
|
|
72
|
+
if (update.since) return `${update.since}+`;
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const handleJetBrainsMarketplace: SpecialHandler = async (
|
|
77
|
+
url: string,
|
|
78
|
+
timeout: number,
|
|
79
|
+
signal?: AbortSignal,
|
|
80
|
+
): Promise<RenderResult | null> => {
|
|
81
|
+
try {
|
|
82
|
+
const parsed = new URL(url);
|
|
83
|
+
if (!MARKETPLACE_HOSTS.has(parsed.hostname)) return null;
|
|
84
|
+
|
|
85
|
+
const match = parsed.pathname.match(/^\/plugin\/(\d+)(?:-[^/]+)?(?:\/|$)/);
|
|
86
|
+
if (!match) return null;
|
|
87
|
+
|
|
88
|
+
const pluginId = match[1];
|
|
89
|
+
const fetchedAt = new Date().toISOString();
|
|
90
|
+
|
|
91
|
+
const pluginUrl = `https://plugins.jetbrains.com/api/plugins/${pluginId}`;
|
|
92
|
+
const updatesUrl = `https://plugins.jetbrains.com/api/plugins/${pluginId}/updates?size=1`;
|
|
93
|
+
|
|
94
|
+
const [pluginResult, updatesResult] = await Promise.all([
|
|
95
|
+
loadPage(pluginUrl, { timeout, signal }),
|
|
96
|
+
loadPage(updatesUrl, { timeout, signal }),
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
if (!pluginResult.ok || !updatesResult.ok) return null;
|
|
100
|
+
|
|
101
|
+
let plugin: PluginData;
|
|
102
|
+
let updates: UpdateData[];
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
plugin = JSON.parse(pluginResult.content) as PluginData;
|
|
106
|
+
updates = JSON.parse(updatesResult.content) as UpdateData[];
|
|
107
|
+
} catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const update = updates[0];
|
|
112
|
+
if (!plugin?.name) return null;
|
|
113
|
+
|
|
114
|
+
const vendorName = plugin.vendor?.name ?? plugin.vendor?.publicName;
|
|
115
|
+
const descriptionSource = plugin.description ?? plugin.preview ?? "";
|
|
116
|
+
const description = descriptionSource ? htmlToBasicMarkdown(descriptionSource) : "";
|
|
117
|
+
const tags = (plugin.tags ?? []).map((tag) => tag.name).filter(Boolean) as string[];
|
|
118
|
+
const rating = extractRating(plugin);
|
|
119
|
+
const buildCompatibility = update ? formatBuildCompatibility(update) : null;
|
|
120
|
+
|
|
121
|
+
let md = `# ${plugin.name}\n\n`;
|
|
122
|
+
if (description) md += `${description}\n\n`;
|
|
123
|
+
|
|
124
|
+
md += `**Plugin ID:** ${pluginId}\n`;
|
|
125
|
+
if (vendorName) md += `**Vendor:** ${vendorName}\n`;
|
|
126
|
+
if (plugin.downloads !== undefined) {
|
|
127
|
+
md += `**Downloads:** ${formatCount(plugin.downloads)}\n`;
|
|
128
|
+
}
|
|
129
|
+
if (rating.value !== null) {
|
|
130
|
+
md += `**Rating:** ${rating.value.toFixed(2)}`;
|
|
131
|
+
if (rating.votes !== null) md += ` (${formatCount(rating.votes)} votes)`;
|
|
132
|
+
md += "\n";
|
|
133
|
+
}
|
|
134
|
+
if (tags.length) md += `**Tags:** ${tags.join(", ")}\n`;
|
|
135
|
+
|
|
136
|
+
if (update) {
|
|
137
|
+
md += "\n## Latest Release\n\n";
|
|
138
|
+
if (update.version) md += `**Version:** ${update.version}\n`;
|
|
139
|
+
if (update.channel) md += `**Channel:** ${update.channel}\n`;
|
|
140
|
+
if (buildCompatibility) md += `**Build Compatibility:** ${buildCompatibility}\n`;
|
|
141
|
+
if (update.downloads !== undefined) {
|
|
142
|
+
md += `**Release Downloads:** ${formatCount(update.downloads)}\n`;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const compatibility = update?.compatibleVersions ?? {};
|
|
147
|
+
const compatibilityEntries = Object.entries(compatibility).sort(([a], [b]) => a.localeCompare(b));
|
|
148
|
+
if (compatibilityEntries.length) {
|
|
149
|
+
md += "\n## IDE Compatibility\n\n";
|
|
150
|
+
for (const [product, version] of compatibilityEntries) {
|
|
151
|
+
md += `- ${product}: ${version}\n`;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const output = finalizeOutput(md);
|
|
156
|
+
return {
|
|
157
|
+
url,
|
|
158
|
+
finalUrl: url,
|
|
159
|
+
contentType: "text/markdown",
|
|
160
|
+
method: "jetbrains-marketplace",
|
|
161
|
+
content: output.content,
|
|
162
|
+
fetchedAt,
|
|
163
|
+
truncated: output.truncated,
|
|
164
|
+
notes: ["Fetched via JetBrains Marketplace API"],
|
|
165
|
+
};
|
|
166
|
+
} catch {}
|
|
167
|
+
|
|
168
|
+
return null;
|
|
169
|
+
};
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import type { RenderResult, SpecialHandler } from "./types";
|
|
2
|
+
import { finalizeOutput, loadPage } from "./types";
|
|
3
|
+
|
|
4
|
+
interface LemmyCreator {
|
|
5
|
+
name: string;
|
|
6
|
+
actor_id?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface LemmyCommunity {
|
|
10
|
+
name: string;
|
|
11
|
+
actor_id?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface LemmyCounts {
|
|
15
|
+
score: number;
|
|
16
|
+
comments?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface LemmyPost {
|
|
20
|
+
id: number;
|
|
21
|
+
name: string;
|
|
22
|
+
body?: string;
|
|
23
|
+
url?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface LemmyPostView {
|
|
27
|
+
post: LemmyPost;
|
|
28
|
+
creator: LemmyCreator;
|
|
29
|
+
community: LemmyCommunity;
|
|
30
|
+
counts: LemmyCounts;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface LemmyPostResponse {
|
|
34
|
+
post_view?: LemmyPostView;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface LemmyComment {
|
|
38
|
+
id: number;
|
|
39
|
+
content?: string;
|
|
40
|
+
path?: string;
|
|
41
|
+
parent_id?: number | null;
|
|
42
|
+
post_id?: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface LemmyCommentView {
|
|
46
|
+
comment: LemmyComment;
|
|
47
|
+
creator: LemmyCreator;
|
|
48
|
+
counts: LemmyCounts;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface LemmyCommentListResponse {
|
|
52
|
+
comments?: LemmyCommentView[];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface LemmyCommentResponse {
|
|
56
|
+
comment_view?: LemmyCommentView;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function parseJson<T>(content: string): T | null {
|
|
60
|
+
try {
|
|
61
|
+
return JSON.parse(content) as T;
|
|
62
|
+
} catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function formatCommunity(community: LemmyCommunity): string {
|
|
68
|
+
if (community.actor_id) {
|
|
69
|
+
try {
|
|
70
|
+
const host = new URL(community.actor_id).hostname;
|
|
71
|
+
return `!${community.name}@${host}`;
|
|
72
|
+
} catch {}
|
|
73
|
+
}
|
|
74
|
+
return `!${community.name}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function formatAuthor(creator: LemmyCreator): string {
|
|
78
|
+
if (creator.actor_id) {
|
|
79
|
+
try {
|
|
80
|
+
const host = new URL(creator.actor_id).hostname;
|
|
81
|
+
return `@${creator.name}@${host}`;
|
|
82
|
+
} catch {}
|
|
83
|
+
}
|
|
84
|
+
return creator.name;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function indentBlock(text: string, indent: string): string {
|
|
88
|
+
return text
|
|
89
|
+
.split("\n")
|
|
90
|
+
.map((line) => `${indent}${line}`)
|
|
91
|
+
.join("\n");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function renderComments(comments: LemmyCommentView[]): string {
|
|
95
|
+
const childrenByParent = new Map<number, LemmyCommentView[]>();
|
|
96
|
+
|
|
97
|
+
const commentIds = new Set(comments.map((view) => view.comment.id));
|
|
98
|
+
|
|
99
|
+
for (const commentView of comments) {
|
|
100
|
+
const parentId = commentView.comment.parent_id;
|
|
101
|
+
const resolvedParent = parentId && commentIds.has(parentId) ? parentId : 0;
|
|
102
|
+
const list = childrenByParent.get(resolvedParent);
|
|
103
|
+
if (list) {
|
|
104
|
+
list.push(commentView);
|
|
105
|
+
} else {
|
|
106
|
+
childrenByParent.set(resolvedParent, [commentView]);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const renderThread = (parentId: number, depth: number): string => {
|
|
111
|
+
const items = childrenByParent.get(parentId) ?? [];
|
|
112
|
+
let output = "";
|
|
113
|
+
|
|
114
|
+
for (const view of items) {
|
|
115
|
+
const author = view.creator?.name ? formatAuthor(view.creator) : "unknown";
|
|
116
|
+
const score = view.counts?.score ?? 0;
|
|
117
|
+
const content = (view.comment.content ?? "").trim();
|
|
118
|
+
const indent = " ".repeat(depth);
|
|
119
|
+
|
|
120
|
+
output += `${indent}- **${author}** · ${score} points\n`;
|
|
121
|
+
if (content) {
|
|
122
|
+
output += `${indentBlock(content, `${indent} `)}\n`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
output += renderThread(view.comment.id, depth + 1);
|
|
126
|
+
output += "\n";
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return output;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
return renderThread(0, 0).trim();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export const handleLemmy: SpecialHandler = async (
|
|
136
|
+
url: string,
|
|
137
|
+
timeout: number,
|
|
138
|
+
signal?: AbortSignal,
|
|
139
|
+
): Promise<RenderResult | null> => {
|
|
140
|
+
try {
|
|
141
|
+
const parsed = new URL(url);
|
|
142
|
+
const match = parsed.pathname.match(/^\/(post|comment)\/(\d+)/);
|
|
143
|
+
if (!match) return null;
|
|
144
|
+
|
|
145
|
+
const kind = match[1];
|
|
146
|
+
const id = Number.parseInt(match[2], 10);
|
|
147
|
+
if (!Number.isFinite(id)) return null;
|
|
148
|
+
|
|
149
|
+
const baseUrl = parsed.origin;
|
|
150
|
+
const fetchedAt = new Date().toISOString();
|
|
151
|
+
|
|
152
|
+
let postId = id;
|
|
153
|
+
if (kind === "comment") {
|
|
154
|
+
const commentUrl = `${baseUrl}/api/v3/comment?id=${id}`;
|
|
155
|
+
const commentResult = await loadPage(commentUrl, { timeout, signal });
|
|
156
|
+
if (!commentResult.ok) return null;
|
|
157
|
+
|
|
158
|
+
const commentData = parseJson<LemmyCommentResponse>(commentResult.content);
|
|
159
|
+
const commentView = commentData?.comment_view;
|
|
160
|
+
const commentPostId = commentView?.comment?.post_id;
|
|
161
|
+
if (!commentPostId) return null;
|
|
162
|
+
postId = commentPostId;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const postUrl = `${baseUrl}/api/v3/post?id=${postId}`;
|
|
166
|
+
const commentsUrl = `${baseUrl}/api/v3/comment/list?post_id=${postId}`;
|
|
167
|
+
|
|
168
|
+
const [postResult, commentsResult] = await Promise.all([
|
|
169
|
+
loadPage(postUrl, { timeout, signal }),
|
|
170
|
+
loadPage(commentsUrl, { timeout, signal }),
|
|
171
|
+
]);
|
|
172
|
+
|
|
173
|
+
if (!postResult.ok || !commentsResult.ok) return null;
|
|
174
|
+
|
|
175
|
+
const postData = parseJson<LemmyPostResponse>(postResult.content);
|
|
176
|
+
const postView = postData?.post_view;
|
|
177
|
+
if (!postView) return null;
|
|
178
|
+
|
|
179
|
+
const commentsData = parseJson<LemmyCommentListResponse>(commentsResult.content);
|
|
180
|
+
const comments = commentsData?.comments ?? [];
|
|
181
|
+
|
|
182
|
+
let md = `# ${postView.post.name}\n\n`;
|
|
183
|
+
|
|
184
|
+
const communityLabel = formatCommunity(postView.community);
|
|
185
|
+
const authorLabel = formatAuthor(postView.creator);
|
|
186
|
+
const score = postView.counts?.score ?? 0;
|
|
187
|
+
const commentCount = postView.counts?.comments ?? comments.length;
|
|
188
|
+
|
|
189
|
+
md += `**Community:** ${communityLabel} · **Author:** ${authorLabel} · **Score:** ${score} · **Comments:** ${commentCount}\n`;
|
|
190
|
+
if (postView.post.url) {
|
|
191
|
+
md += `**Link:** ${postView.post.url}\n`;
|
|
192
|
+
}
|
|
193
|
+
md += "\n";
|
|
194
|
+
|
|
195
|
+
if (postView.post.body) {
|
|
196
|
+
md += `---\n\n${postView.post.body}\n\n`;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (comments.length > 0) {
|
|
200
|
+
const threadedComments = renderComments(comments);
|
|
201
|
+
if (threadedComments) {
|
|
202
|
+
md += `---\n\n## Comments\n\n${threadedComments}\n`;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const output = finalizeOutput(md);
|
|
207
|
+
return {
|
|
208
|
+
url,
|
|
209
|
+
finalUrl: url,
|
|
210
|
+
contentType: "text/markdown",
|
|
211
|
+
method: "lemmy-api",
|
|
212
|
+
content: output.content,
|
|
213
|
+
fetchedAt,
|
|
214
|
+
truncated: output.truncated,
|
|
215
|
+
notes: ["Fetched via Lemmy API"],
|
|
216
|
+
};
|
|
217
|
+
} catch {}
|
|
218
|
+
|
|
219
|
+
return null;
|
|
220
|
+
};
|
|
@@ -74,7 +74,7 @@ function renderComments(comments: LobstersComment[], maxDepth = 5): string {
|
|
|
74
74
|
/**
|
|
75
75
|
* Handle Lobste.rs URLs via JSON API
|
|
76
76
|
*/
|
|
77
|
-
export const handleLobsters: SpecialHandler = async (url: string, timeout: number) => {
|
|
77
|
+
export const handleLobsters: SpecialHandler = async (url: string, timeout: number, signal?: AbortSignal) => {
|
|
78
78
|
try {
|
|
79
79
|
const parsed = new URL(url);
|
|
80
80
|
if (!parsed.hostname.includes("lobste.rs")) return null;
|
|
@@ -87,7 +87,7 @@ export const handleLobsters: SpecialHandler = async (url: string, timeout: numbe
|
|
|
87
87
|
const storyMatch = parsed.pathname.match(/^\/s\/([^/]+)/);
|
|
88
88
|
if (storyMatch) {
|
|
89
89
|
jsonUrl = `https://lobste.rs/s/${storyMatch[1]}.json`;
|
|
90
|
-
const result = await loadPage(jsonUrl, { timeout });
|
|
90
|
+
const result = await loadPage(jsonUrl, { timeout, signal });
|
|
91
91
|
if (!result.ok) return null;
|
|
92
92
|
|
|
93
93
|
const story = JSON.parse(result.content) as LobstersStoryResponse;
|
|
@@ -140,7 +140,7 @@ export const handleLobsters: SpecialHandler = async (url: string, timeout: numbe
|
|
|
140
140
|
|
|
141
141
|
if (!jsonUrl) return null;
|
|
142
142
|
|
|
143
|
-
const result = await loadPage(jsonUrl, { timeout });
|
|
143
|
+
const result = await loadPage(jsonUrl, { timeout, signal });
|
|
144
144
|
if (!result.ok) return null;
|
|
145
145
|
|
|
146
146
|
const stories = JSON.parse(result.content) as LobstersStory[];
|