autohand-cli 0.7.7 → 0.7.9

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 (175) hide show
  1. package/dist/CommunitySkillsCache-N2RWBCY2.cjs +7 -0
  2. package/dist/{CommunitySkillsCache-Q22FUAR5.js → CommunitySkillsCache-YWDFZBKP.js} +2 -2
  3. package/dist/MemoryManager-J7APSLZ7.js +7 -0
  4. package/dist/MemoryManager-RLLYVIDY.cjs +7 -0
  5. package/dist/PermissionManager-ISI5OAJK.cjs +10 -0
  6. package/dist/{PermissionManager-HATZKTRC.js → PermissionManager-WYP5JPCQ.js} +3 -3
  7. package/dist/SessionManager-7MF76Q3Q.cjs +9 -0
  8. package/dist/{SessionManager-S5R6O3NU.js → SessionManager-SFBDIBGA.js} +2 -2
  9. package/dist/{SkillsRegistry-R5WDM6T3.js → SkillsRegistry-E437FFJV.js} +2 -2
  10. package/dist/SkillsRegistry-UTK2YT4M.cjs +8 -0
  11. package/dist/{SyncApiClient-FAOMIZAP.js → SyncApiClient-AYXYSOJM.js} +1 -1
  12. package/dist/SyncApiClient-ID3KXEMA.cjs +10 -0
  13. package/dist/agents-N2ZB4O2A.cjs +9 -0
  14. package/dist/{agents-ICAC3KD3.js → agents-ZMT7HBRT.js} +2 -2
  15. package/dist/agents-new-3EWS2NR3.cjs +11 -0
  16. package/dist/agents-new-TCGUYK5I.js +11 -0
  17. package/dist/cc-2W6M7J45.cjs +8 -0
  18. package/dist/cc-UTTLESTY.js +8 -0
  19. package/dist/{chunk-XFPITUFJ.cjs → chunk-32R47CAQ.cjs} +50 -8
  20. package/dist/{chunk-2JPUEN44.cjs → chunk-3DGR4GNN.cjs} +57 -84
  21. package/dist/{chunk-WIUGUR5T.js → chunk-3DVEGFBZ.js} +8 -8
  22. package/dist/{chunk-YWKZF2SA.js → chunk-3SDEZTZD.js} +46 -46
  23. package/dist/{chunk-KH7BCZJN.js → chunk-4BZ65NIH.js} +1 -1
  24. package/dist/{chunk-52MLYK5P.js → chunk-4YQ2OJAL.js} +1 -1
  25. package/dist/{chunk-VDZJ3W4M.cjs → chunk-5UC3VAJ2.cjs} +2 -2
  26. package/dist/{chunk-NYQVQYIF.cjs → chunk-6DSJ7XST.cjs} +16 -11
  27. package/dist/chunk-6SM6N7DJ.js +2828 -0
  28. package/dist/{chunk-MDWULS57.js → chunk-6UJMCWRY.js} +56 -33
  29. package/dist/{chunk-Q3WCMY3Z.js → chunk-7GJR65DQ.js} +2 -2
  30. package/dist/{chunk-B5N5UAMO.cjs → chunk-7WKEDH3E.cjs} +5 -5
  31. package/dist/{chunk-B4ZPNXZE.cjs → chunk-A5KU3JZW.cjs} +1 -1
  32. package/dist/{chunk-PMMSDR44.cjs → chunk-AQNY2M6Q.cjs} +3 -1
  33. package/dist/{chunk-AVL4DKQO.js → chunk-BTVNRLSE.js} +1 -1
  34. package/dist/{chunk-PU534KPO.cjs → chunk-CHVXUA3O.cjs} +4 -4
  35. package/dist/{chunk-QXAPHGEW.js → chunk-CMNQAF6C.js} +7 -2
  36. package/dist/{chunk-7TOHYAUF.cjs → chunk-CPO5KEQS.cjs} +2 -2
  37. package/dist/{chunk-PR53E47T.cjs → chunk-DRXFJR7F.cjs} +22 -30
  38. package/dist/{chunk-JBKP2CLA.cjs → chunk-FN5S5OE7.cjs} +47 -47
  39. package/dist/{chunk-SFGJQPGC.cjs → chunk-FPHU2ES6.cjs} +56 -33
  40. package/dist/{chunk-3L76MLO5.cjs → chunk-FSTLJIR5.cjs} +21 -28
  41. package/dist/chunk-HF65HHDU.cjs +83 -0
  42. package/dist/{chunk-A552JHUJ.cjs → chunk-HUA44A35.cjs} +21 -18
  43. package/dist/{chunk-B7EUETGY.cjs → chunk-HWDP4HQO.cjs} +4 -4
  44. package/dist/{chunk-OC5YDNFC.js → chunk-IYZBLUEJ.js} +35 -52
  45. package/dist/chunk-J2RUI7GJ.js +83 -0
  46. package/dist/{chunk-WH3D42BQ.js → chunk-J7VV7JAB.js} +55 -82
  47. package/dist/{chunk-VEDIYPWY.cjs → chunk-JRV4SOJU.cjs} +9 -9
  48. package/dist/{chunk-GWIAMKKF.js → chunk-K6CZ2AGI.js} +6 -3
  49. package/dist/{chunk-SYJLMBLP.cjs → chunk-KJAZ4YAR.cjs} +17 -17
  50. package/dist/{chunk-BAFJQUWR.js → chunk-KS45GREN.js} +12 -19
  51. package/dist/chunk-KXNAGJE6.cjs +589 -0
  52. package/dist/chunk-L5QWMGLV.cjs +2828 -0
  53. package/dist/{chunk-U5WIP4HS.js → chunk-LCBOOA7S.js} +47 -5
  54. package/dist/chunk-LJCSSR5V.cjs +63 -0
  55. package/dist/chunk-LKIKNRUP.js +219 -0
  56. package/dist/{chunk-WQSWU2QA.cjs → chunk-LY5VN3B3.cjs} +9 -6
  57. package/dist/{chunk-YAGD43KA.cjs → chunk-LYMZDJX5.cjs} +36 -37
  58. package/dist/{chunk-SLISYSP4.cjs → chunk-MRDZHNCF.cjs} +4 -4
  59. package/dist/chunk-NCC6ETZS.js +362 -0
  60. package/dist/chunk-NCLCL7JK.js +63 -0
  61. package/dist/chunk-NTFNUTY2.js +589 -0
  62. package/dist/{chunk-GDTZQSJ6.cjs → chunk-O3EX3SAD.cjs} +2 -2
  63. package/dist/chunk-OO7EKEJH.cjs +219 -0
  64. package/dist/{chunk-P2Z6GDEN.js → chunk-OXJCLU3X.js} +1 -1
  65. package/dist/{chunk-23JQSCTO.js → chunk-P6EGMTK4.js} +1 -1
  66. package/dist/{chunk-VPAN5H7Q.js → chunk-PFS5ZOCO.js} +30 -31
  67. package/dist/{chunk-EKY5PKQI.js → chunk-PGH3MHPP.js} +3 -1
  68. package/dist/{chunk-GR7VWN63.js → chunk-PVSYZXS7.js} +18 -26
  69. package/dist/{chunk-NI3BQXKU.js → chunk-QAJM6VG5.js} +2 -2
  70. package/dist/{chunk-OBV3UUIL.js → chunk-R5PZPF55.js} +1 -1
  71. package/dist/{chunk-MJFBVQHB.js → chunk-RDGYHJ52.js} +5 -5
  72. package/dist/{chunk-YDH2BMEN.js → chunk-RX4KPL6H.js} +1 -1
  73. package/dist/{chunk-GFJ6AETU.cjs → chunk-T7HQBZZB.cjs} +6 -6
  74. package/dist/chunk-TSV4LVHK.js +35 -0
  75. package/dist/{chunk-7VW3A7DO.cjs → chunk-TUO5SZRD.cjs} +2 -2
  76. package/dist/{chunk-C3IFF3EH.cjs → chunk-UL5T2XIY.cjs} +36 -53
  77. package/dist/chunk-ULQ6MDSJ.cjs +362 -0
  78. package/dist/{chunk-UL7YPRCU.js → chunk-UP6YRP44.js} +1 -1
  79. package/dist/{chunk-G77ZY4QG.js → chunk-VQDWNDP5.js} +1 -1
  80. package/dist/chunk-VXCMGBXY.cjs +35 -0
  81. package/dist/{chunk-JHFH3N4U.cjs → chunk-WCN5WDOI.cjs} +2 -2
  82. package/dist/{chunk-53BR4MUW.cjs → chunk-WGO4ACS7.cjs} +3 -3
  83. package/dist/{chunk-4M2GX7RH.cjs → chunk-WJICM4SK.cjs} +2 -2
  84. package/dist/{chunk-4RWTUT2Z.js → chunk-WT367RVU.js} +19 -16
  85. package/dist/{chunk-A6QBABQ7.js → chunk-XOSFZHSS.js} +2 -2
  86. package/dist/{chunk-3YEDXG6S.js → chunk-YDJA5ETO.js} +1 -1
  87. package/dist/completion-NCSTSKTL.cjs +11 -0
  88. package/dist/{completion-Y42FKDT3.js → completion-NTZERPAZ.js} +2 -1
  89. package/dist/constants-B2X7KDIH.cjs +20 -0
  90. package/dist/{constants-ZLG6M5SI.js → constants-OFIAR4E5.js} +1 -1
  91. package/dist/export-5AJNVX4O.cjs +9 -0
  92. package/dist/{export-WJ5P6E5Z.js → export-ZQHTLV4W.js} +2 -1
  93. package/dist/feedback-F7BMGSV6.cjs +12 -0
  94. package/dist/feedback-NL7CWTRY.js +12 -0
  95. package/dist/hooks-AX5VPCZ3.cjs +10 -0
  96. package/dist/hooks-V22HVLQT.js +10 -0
  97. package/dist/index.cjs +5306 -3582
  98. package/dist/index.js +3780 -2056
  99. package/dist/language-IEKARNQH.js +13 -0
  100. package/dist/language-ULQCKXAD.cjs +13 -0
  101. package/dist/localProjectPermissions-7FI3XF4K.cjs +17 -0
  102. package/dist/{localProjectPermissions-YFFAKLUZ.js → localProjectPermissions-FD5AK5FB.js} +2 -2
  103. package/dist/login-PD3DFJLM.js +15 -0
  104. package/dist/login-VHGT4YKT.cjs +15 -0
  105. package/dist/logout-2QXNFGDT.js +13 -0
  106. package/dist/logout-A4SMKEFO.cjs +13 -0
  107. package/dist/permissions-S7433NQJ.js +10 -0
  108. package/dist/permissions-YZ6WCDBL.cjs +10 -0
  109. package/dist/plan-B3CW5DXJ.cjs +10 -0
  110. package/dist/plan-JFGNRL2S.js +10 -0
  111. package/dist/resume-54ERVSFM.cjs +10 -0
  112. package/dist/resume-YJ5CVKAP.js +10 -0
  113. package/dist/search-4TEQMUPU.js +11 -0
  114. package/dist/search-QGLS4SV7.cjs +11 -0
  115. package/dist/share-2B2T3AUU.cjs +11 -0
  116. package/dist/share-SZMXZIA3.js +11 -0
  117. package/dist/skills-HVQNCTGK.cjs +12 -0
  118. package/dist/{skills-3YEEODHK.js → skills-PB4HXV4R.js} +1 -1
  119. package/dist/{skills-install-FTGOHOZ4.cjs → skills-install-7SFS24HY.cjs} +10 -9
  120. package/dist/{skills-install-KAXAQSN6.js → skills-install-UBBNXWD5.js} +4 -3
  121. package/dist/skills-new-5FSDAJWE.js +12 -0
  122. package/dist/skills-new-IOTZYE6F.cjs +12 -0
  123. package/dist/{status-7LCXYYY4.js → status-AGPSKXSW.js} +2 -2
  124. package/dist/status-WOINF556.cjs +9 -0
  125. package/dist/{sync-3B7SNBYC.js → sync-64MZDWGG.js} +3 -3
  126. package/dist/sync-F5ECJW25.cjs +39 -0
  127. package/dist/{sync-H4UHHLKU.js → sync-LYKDHRFM.js} +3 -3
  128. package/dist/sync-W5UR7BEP.cjs +14 -0
  129. package/dist/theme-CWG3ZLHB.cjs +13 -0
  130. package/dist/theme-HQKV7YAP.js +13 -0
  131. package/package.json +2 -2
  132. package/dist/CommunitySkillsCache-2BITCEAA.cjs +0 -7
  133. package/dist/MemoryManager-2ATHG7BH.js +0 -7
  134. package/dist/MemoryManager-AENCGCEW.cjs +0 -7
  135. package/dist/PermissionManager-6HZGTK2N.cjs +0 -10
  136. package/dist/SessionManager-AG4WT3DP.cjs +0 -9
  137. package/dist/SkillsRegistry-ZXU6YDRP.cjs +0 -8
  138. package/dist/SyncApiClient-UOA4VLLD.cjs +0 -10
  139. package/dist/agents-YONWPKFS.cjs +0 -9
  140. package/dist/agents-new-NV557UVG.cjs +0 -10
  141. package/dist/agents-new-QHM3CO4B.js +0 -10
  142. package/dist/chunk-3HPUOQJN.cjs +0 -23
  143. package/dist/chunk-5DN5KNXU.js +0 -81
  144. package/dist/chunk-7BYSXAKS.js +0 -23
  145. package/dist/chunk-ARVFUZOB.js +0 -736
  146. package/dist/chunk-GWXXFQ3F.cjs +0 -81
  147. package/dist/chunk-XFQS2VGT.cjs +0 -736
  148. package/dist/completion-WVFWX7XQ.cjs +0 -10
  149. package/dist/constants-G2PLP5HH.cjs +0 -20
  150. package/dist/export-BKBJ7PB2.cjs +0 -8
  151. package/dist/feedback-HZBCTSFG.js +0 -10
  152. package/dist/feedback-JBQ3UPGZ.cjs +0 -10
  153. package/dist/hooks-G4VLYMLX.cjs +0 -9
  154. package/dist/hooks-LN4A6NQL.js +0 -9
  155. package/dist/language-KODBDE5R.js +0 -12
  156. package/dist/language-SJT475NW.cjs +0 -12
  157. package/dist/localProjectPermissions-AYQYGTOE.cjs +0 -17
  158. package/dist/login-TC2KROQI.js +0 -14
  159. package/dist/login-TYMR2ZD3.cjs +0 -14
  160. package/dist/logout-2ECV365P.js +0 -12
  161. package/dist/logout-CO3CPYZJ.cjs +0 -12
  162. package/dist/permissions-5MTH22EF.js +0 -9
  163. package/dist/permissions-IP5SITPI.cjs +0 -9
  164. package/dist/resume-EPOEF3WV.cjs +0 -9
  165. package/dist/resume-LOYD5MMP.js +0 -9
  166. package/dist/share-544SIZOY.js +0 -10
  167. package/dist/share-OETK2GUF.cjs +0 -10
  168. package/dist/skills-CRM55MKM.cjs +0 -12
  169. package/dist/skills-new-JF4FKNUT.cjs +0 -11
  170. package/dist/skills-new-JYX2GBKM.js +0 -11
  171. package/dist/status-E7MZEQ26.cjs +0 -9
  172. package/dist/sync-4RARBQIH.cjs +0 -39
  173. package/dist/sync-YZ6YZ42H.cjs +0 -14
  174. package/dist/theme-3XV5BWUB.js +0 -12
  175. package/dist/theme-Z2WS5XWZ.cjs +0 -12
@@ -0,0 +1,589 @@
1
+ import {
2
+ saveConfig
3
+ } from "./chunk-K6CZ2AGI.js";
4
+ import {
5
+ showModal,
6
+ showPassword
7
+ } from "./chunk-6SM6N7DJ.js";
8
+
9
+ // src/commands/search.ts
10
+ import chalk from "chalk";
11
+
12
+ // src/actions/web.ts
13
+ import * as https from "https";
14
+ import * as http from "http";
15
+ var globalSearchConfig = {
16
+ provider: "duckduckgo"
17
+ };
18
+ function configureSearch(config) {
19
+ globalSearchConfig = { ...globalSearchConfig, ...config };
20
+ }
21
+ function getSearchConfig() {
22
+ return { ...globalSearchConfig };
23
+ }
24
+ async function simpleFetch(url, options = {}) {
25
+ const timeout = options.timeout ?? 1e4;
26
+ const maxLength = options.maxLength ?? 5e4;
27
+ return new Promise((resolve, reject) => {
28
+ const parsedUrl = new URL(url);
29
+ const protocol = parsedUrl.protocol === "https:" ? https : http;
30
+ const req = protocol.get(url, {
31
+ timeout,
32
+ headers: {
33
+ "User-Agent": "Autohand-CLI/1.0 (https://autohand.ai)",
34
+ "Accept": "text/html,application/json,text/plain,*/*",
35
+ "Accept-Language": "en-US,en;q=0.9"
36
+ }
37
+ }, (res) => {
38
+ if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
39
+ simpleFetch(res.headers.location, options).then(resolve).catch(reject);
40
+ return;
41
+ }
42
+ if (res.statusCode && res.statusCode >= 400) {
43
+ reject(new Error(`HTTP ${res.statusCode}: ${res.statusMessage}`));
44
+ return;
45
+ }
46
+ let data = "";
47
+ res.on("data", (chunk) => {
48
+ data += chunk;
49
+ if (data.length > maxLength) {
50
+ res.destroy();
51
+ resolve(data.slice(0, maxLength) + "\n... (truncated)");
52
+ }
53
+ });
54
+ res.on("end", () => resolve(data));
55
+ res.on("error", reject);
56
+ });
57
+ req.on("error", reject);
58
+ req.on("timeout", () => {
59
+ req.destroy();
60
+ reject(new Error("Request timed out"));
61
+ });
62
+ });
63
+ }
64
+ function htmlToText(html) {
65
+ return html.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "").replace(/<noscript[^>]*>[\s\S]*?<\/noscript>/gi, "").replace(/<!--[\s\S]*?-->/g, "").replace(/<(\/?(p|div|br|h[1-6]|li|tr|td|th|blockquote|pre|hr))[^>]*>/gi, "\n").replace(/<[^>]+>/g, " ").replace(/&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&[a-z]+;/gi, "").replace(/[ \t]+/g, " ").replace(/\n[ \t]+/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
66
+ }
67
+ async function webSearch(query, options = {}) {
68
+ const maxResults = options.maxResults ?? 5;
69
+ const searchType = options.searchType ?? "general";
70
+ let enhancedQuery = query;
71
+ switch (searchType) {
72
+ case "packages":
73
+ enhancedQuery = `${query} npm package OR pypi package site:npmjs.com OR site:pypi.org`;
74
+ break;
75
+ case "docs":
76
+ enhancedQuery = `${query} documentation OR docs OR guide`;
77
+ break;
78
+ case "changelog":
79
+ enhancedQuery = `${query} changelog OR release notes OR what's new`;
80
+ break;
81
+ }
82
+ const provider = options.provider ?? globalSearchConfig.provider;
83
+ const braveApiKey = globalSearchConfig.braveApiKey ?? process.env.BRAVE_SEARCH_API_KEY;
84
+ const parallelApiKey = globalSearchConfig.parallelApiKey ?? process.env.PARALLEL_API_KEY;
85
+ switch (provider) {
86
+ case "brave":
87
+ if (!braveApiKey) {
88
+ throw new Error(
89
+ "Brave Search requires an API key. Configure it with /search or set BRAVE_SEARCH_API_KEY environment variable. Get a free API key at: https://brave.com/search/api/"
90
+ );
91
+ }
92
+ return braveSearch(enhancedQuery, braveApiKey, maxResults);
93
+ case "parallel":
94
+ if (!parallelApiKey) {
95
+ throw new Error(
96
+ "Parallel.ai Search requires an API key. Configure it with /search or set PARALLEL_API_KEY environment variable. Get an API key at: https://platform.parallel.ai"
97
+ );
98
+ }
99
+ return parallelSearch(enhancedQuery, parallelApiKey, maxResults);
100
+ case "duckduckgo":
101
+ default:
102
+ return duckduckgoSearch(enhancedQuery, maxResults);
103
+ }
104
+ }
105
+ async function duckduckgoSearch(query, maxResults) {
106
+ try {
107
+ const searchUrl = `https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`;
108
+ const html = await simpleFetch(searchUrl, { timeout: 15e3, maxLength: 1e5 });
109
+ if (html.includes("anomaly-modal") || html.includes("bots use DuckDuckGo") || html.includes("cc=botnet")) {
110
+ throw new Error(
111
+ "DuckDuckGo blocked this search with a CAPTCHA challenge. Configure a different search provider with /search command. Options: brave (https://brave.com/search/api/) or parallel (https://platform.parallel.ai)"
112
+ );
113
+ }
114
+ const results = [];
115
+ const resultRegex = /<a[^>]*class="result__a"[^>]*href="([^"]+)"[^>]*>([^<]*)<\/a>[\s\S]*?<a[^>]*class="result__snippet"[^>]*>([^<]*(?:<[^>]+>[^<]*)*)<\/a>/gi;
116
+ let match;
117
+ while ((match = resultRegex.exec(html)) !== null && results.length < maxResults) {
118
+ const url = match[1];
119
+ const title = match[2].trim();
120
+ const snippet = htmlToText(match[3]).slice(0, 300);
121
+ if (url && title && !url.includes("duckduckgo.com")) {
122
+ results.push({ title, url, snippet });
123
+ }
124
+ }
125
+ if (results.length === 0) {
126
+ const altRegex = /<a[^>]*href="(https?:\/\/[^"]+)"[^>]*>([^<]+)<\/a>/gi;
127
+ while ((match = altRegex.exec(html)) !== null && results.length < maxResults) {
128
+ const url = match[1];
129
+ const title = match[2].trim();
130
+ if (url && title && !url.includes("duckduckgo.com") && title.length > 5) {
131
+ results.push({ title, url, snippet: "" });
132
+ }
133
+ }
134
+ }
135
+ if (results.length === 0) {
136
+ throw new Error(
137
+ "No search results found. DuckDuckGo may be rate-limiting requests. Configure a different search provider with /search command."
138
+ );
139
+ }
140
+ return results;
141
+ } catch (error) {
142
+ throw new Error(`DuckDuckGo search failed: ${error instanceof Error ? error.message : String(error)}`);
143
+ }
144
+ }
145
+ async function parallelSearch(query, apiKey, maxResults) {
146
+ return new Promise((resolve, reject) => {
147
+ const postData = JSON.stringify({
148
+ objective: query,
149
+ search_queries: [query],
150
+ max_results: maxResults,
151
+ excerpts: {
152
+ max_chars_per_result: 500
153
+ }
154
+ });
155
+ const options = {
156
+ hostname: "api.parallel.ai",
157
+ port: 443,
158
+ path: "/v1beta/search",
159
+ method: "POST",
160
+ headers: {
161
+ "Content-Type": "application/json",
162
+ "Content-Length": Buffer.byteLength(postData),
163
+ "x-api-key": apiKey,
164
+ "parallel-beta": "search-extract-2025-10-10"
165
+ }
166
+ };
167
+ const req = https.request(options, (res) => {
168
+ let data = "";
169
+ res.on("data", (chunk) => {
170
+ data += chunk;
171
+ });
172
+ res.on("end", () => {
173
+ if (res.statusCode && res.statusCode >= 400) {
174
+ reject(new Error(`Parallel.ai API error: HTTP ${res.statusCode} - ${data}`));
175
+ return;
176
+ }
177
+ try {
178
+ const json = JSON.parse(data);
179
+ const results = [];
180
+ if (json.results && Array.isArray(json.results)) {
181
+ for (const result of json.results.slice(0, maxResults)) {
182
+ results.push({
183
+ title: result.title || result.url || "Untitled",
184
+ url: result.url || "",
185
+ snippet: result.excerpt || result.content?.slice(0, 300) || ""
186
+ });
187
+ }
188
+ } else if (json.search_results && Array.isArray(json.search_results)) {
189
+ for (const result of json.search_results.slice(0, maxResults)) {
190
+ results.push({
191
+ title: result.title || result.url || "Untitled",
192
+ url: result.url || "",
193
+ snippet: result.snippet || result.description || ""
194
+ });
195
+ }
196
+ }
197
+ resolve(results);
198
+ } catch (parseError) {
199
+ reject(new Error(`Failed to parse Parallel.ai response: ${parseError instanceof Error ? parseError.message : String(parseError)}`));
200
+ }
201
+ });
202
+ res.on("error", reject);
203
+ });
204
+ req.on("error", reject);
205
+ req.on("timeout", () => {
206
+ req.destroy();
207
+ reject(new Error("Parallel.ai request timed out"));
208
+ });
209
+ req.write(postData);
210
+ req.end();
211
+ });
212
+ }
213
+ async function braveSearch(query, apiKey, maxResults) {
214
+ const url = `https://api.search.brave.com/res/v1/web/search?q=${encodeURIComponent(query)}&count=${maxResults}`;
215
+ return new Promise((resolve, reject) => {
216
+ const req = https.get(url, {
217
+ headers: {
218
+ "Accept": "application/json",
219
+ "Accept-Encoding": "gzip",
220
+ "X-Subscription-Token": apiKey
221
+ }
222
+ }, (res) => {
223
+ if (res.statusCode && res.statusCode >= 400) {
224
+ reject(new Error(`Brave Search API error: HTTP ${res.statusCode}`));
225
+ return;
226
+ }
227
+ let data = "";
228
+ res.on("data", (chunk) => {
229
+ data += chunk;
230
+ });
231
+ res.on("end", () => {
232
+ try {
233
+ const json = JSON.parse(data);
234
+ if (json.web?.results) {
235
+ const results = json.web.results.slice(0, maxResults).map((r) => ({
236
+ title: r.title || "",
237
+ url: r.url || "",
238
+ snippet: r.description || ""
239
+ }));
240
+ resolve(results);
241
+ } else {
242
+ resolve([]);
243
+ }
244
+ } catch (parseError) {
245
+ reject(new Error(`Failed to parse Brave Search response: ${parseError instanceof Error ? parseError.message : String(parseError)}`));
246
+ }
247
+ });
248
+ res.on("error", reject);
249
+ });
250
+ req.on("error", reject);
251
+ req.on("timeout", () => {
252
+ req.destroy();
253
+ reject(new Error("Brave Search request timed out"));
254
+ });
255
+ });
256
+ }
257
+ async function fetchUrl(url, options = {}) {
258
+ const maxLength = options.maxLength ?? 3e4;
259
+ try {
260
+ const content = await simpleFetch(url, { timeout: 15e3, maxLength: maxLength * 2 });
261
+ if (content.trim().startsWith("{") || content.trim().startsWith("[")) {
262
+ try {
263
+ const json = JSON.parse(content);
264
+ return JSON.stringify(json, null, 2).slice(0, maxLength);
265
+ } catch {
266
+ }
267
+ }
268
+ const text = htmlToText(content);
269
+ return text.slice(0, maxLength);
270
+ } catch (error) {
271
+ throw new Error(`Failed to fetch URL: ${error instanceof Error ? error.message : String(error)}`);
272
+ }
273
+ }
274
+ async function getNpmInfo(packageName, version) {
275
+ try {
276
+ const url = version ? `https://registry.npmjs.org/${encodeURIComponent(packageName)}/${encodeURIComponent(version)}` : `https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`;
277
+ const content = await simpleFetch(url, { timeout: 1e4 });
278
+ const data = JSON.parse(content);
279
+ return {
280
+ registry: "npm",
281
+ name: data.name,
282
+ version: data.version,
283
+ description: data.description || "",
284
+ homepage: data.homepage,
285
+ repository: typeof data.repository === "string" ? data.repository : data.repository?.url,
286
+ license: data.license,
287
+ dependencies: data.dependencies,
288
+ keywords: data.keywords,
289
+ authors: data.maintainers?.map((m) => m.name || m.email)
290
+ };
291
+ } catch (error) {
292
+ throw new Error(`Failed to get npm info for ${packageName}: ${error instanceof Error ? error.message : String(error)}`);
293
+ }
294
+ }
295
+ async function getPyPIInfo(packageName, version) {
296
+ try {
297
+ const url = version ? `https://pypi.org/pypi/${encodeURIComponent(packageName)}/${encodeURIComponent(version)}/json` : `https://pypi.org/pypi/${encodeURIComponent(packageName)}/json`;
298
+ const content = await simpleFetch(url, { timeout: 1e4 });
299
+ const data = JSON.parse(content);
300
+ const info = data.info;
301
+ return {
302
+ registry: "pypi",
303
+ name: info.name,
304
+ version: info.version,
305
+ description: info.summary || "",
306
+ homepage: info.home_page || info.project_url,
307
+ repository: info.project_urls?.Repository || info.project_urls?.Source,
308
+ license: info.license,
309
+ dependencies: info.requires_dist?.reduce((acc, dep) => {
310
+ const [name] = dep.split(/[<>=!;\s]/);
311
+ acc[name] = dep;
312
+ return acc;
313
+ }, {}),
314
+ keywords: info.keywords?.split(",").map((k) => k.trim()).filter(Boolean),
315
+ authors: info.author ? [info.author] : []
316
+ };
317
+ } catch (error) {
318
+ throw new Error(`Failed to get PyPI info for ${packageName}: ${error instanceof Error ? error.message : String(error)}`);
319
+ }
320
+ }
321
+ async function getCargoInfo(packageName, version) {
322
+ try {
323
+ const url = `https://crates.io/api/v1/crates/${encodeURIComponent(packageName)}`;
324
+ const content = await simpleFetch(url, { timeout: 1e4 });
325
+ const data = JSON.parse(content);
326
+ const crate = data.crate;
327
+ const ver = version ? data.versions?.find((v) => v.num === version) : data.versions?.[0];
328
+ return {
329
+ registry: "crates",
330
+ name: crate.name,
331
+ version: ver?.num || crate.newest_version,
332
+ description: crate.description || "",
333
+ homepage: crate.homepage,
334
+ repository: crate.repository,
335
+ license: ver?.license,
336
+ keywords: crate.keywords,
337
+ authors: ver?.published_by?.name ? [ver.published_by.name] : []
338
+ };
339
+ } catch (error) {
340
+ throw new Error(`Failed to get Cargo info for ${packageName}: ${error instanceof Error ? error.message : String(error)}`);
341
+ }
342
+ }
343
+ async function getRubyGemsInfo(packageName, version) {
344
+ try {
345
+ const url = version ? `https://rubygems.org/api/v1/versions/${encodeURIComponent(packageName)}.json` : `https://rubygems.org/api/v1/gems/${encodeURIComponent(packageName)}.json`;
346
+ const content = await simpleFetch(url, { timeout: 1e4 });
347
+ const data = JSON.parse(content);
348
+ const gem = Array.isArray(data) ? data.find((v) => v.number === version) || data[0] : data;
349
+ return {
350
+ registry: "rubygems",
351
+ name: gem.name || packageName,
352
+ version: gem.version || gem.number,
353
+ description: gem.info || gem.summary || "",
354
+ homepage: gem.homepage_uri,
355
+ repository: gem.source_code_uri,
356
+ license: gem.licenses?.[0],
357
+ keywords: [],
358
+ authors: gem.authors ? [gem.authors] : []
359
+ };
360
+ } catch (error) {
361
+ throw new Error(`Failed to get RubyGems info for ${packageName}: ${error instanceof Error ? error.message : String(error)}`);
362
+ }
363
+ }
364
+ async function getGoModuleInfo(modulePath, _version) {
365
+ try {
366
+ const url = `https://proxy.golang.org/${encodeURIComponent(modulePath)}/@latest`;
367
+ const content = await simpleFetch(url, { timeout: 1e4 });
368
+ const data = JSON.parse(content);
369
+ return {
370
+ registry: "go",
371
+ name: modulePath,
372
+ version: data.Version || "latest",
373
+ description: `Go module: ${modulePath}`,
374
+ homepage: `https://pkg.go.dev/${modulePath}`,
375
+ repository: modulePath.startsWith("github.com") ? `https://${modulePath}` : void 0,
376
+ license: void 0,
377
+ // Go proxy doesn't provide license info
378
+ keywords: [],
379
+ authors: []
380
+ };
381
+ } catch (error) {
382
+ throw new Error(`Failed to get Go module info for ${modulePath}: ${error instanceof Error ? error.message : String(error)}`);
383
+ }
384
+ }
385
+ async function getPackageInfo(packageName, options = {}) {
386
+ const registry = options.registry || detectRegistry(packageName);
387
+ switch (registry) {
388
+ case "npm":
389
+ return getNpmInfo(packageName, options.version);
390
+ case "pypi":
391
+ return getPyPIInfo(packageName, options.version);
392
+ case "crates":
393
+ return getCargoInfo(packageName, options.version);
394
+ case "rubygems":
395
+ return getRubyGemsInfo(packageName, options.version);
396
+ case "go":
397
+ return getGoModuleInfo(packageName, options.version);
398
+ default:
399
+ return getNpmInfo(packageName, options.version);
400
+ }
401
+ }
402
+ function detectRegistry(packageName) {
403
+ if (packageName.includes("/") && (packageName.startsWith("github.com/") || packageName.startsWith("golang.org/") || packageName.startsWith("google.golang.org/"))) {
404
+ return "go";
405
+ }
406
+ if (packageName.includes("_") && !packageName.startsWith("@")) {
407
+ return "crates";
408
+ }
409
+ return "npm";
410
+ }
411
+ function formatSearchResults(results) {
412
+ if (results.length === 0) {
413
+ return "No results found.";
414
+ }
415
+ return results.map((r, i) => {
416
+ const lines = [`${i + 1}. **${r.title}**`, ` ${r.url}`];
417
+ if (r.snippet) {
418
+ lines.push(` ${r.snippet}`);
419
+ }
420
+ return lines.join("\n");
421
+ }).join("\n\n");
422
+ }
423
+ function formatPackageInfo(info) {
424
+ const registryLabels = {
425
+ npm: "npm",
426
+ pypi: "PyPI",
427
+ crates: "crates.io",
428
+ maven: "Maven",
429
+ go: "Go",
430
+ rubygems: "RubyGems"
431
+ };
432
+ const lines = [
433
+ `**${info.name}** v${info.version} (${registryLabels[info.registry]})`,
434
+ "",
435
+ info.description || "No description",
436
+ ""
437
+ ];
438
+ if (info.homepage) {
439
+ lines.push(`Homepage: ${info.homepage}`);
440
+ }
441
+ if (info.repository) {
442
+ lines.push(`Repository: ${info.repository}`);
443
+ }
444
+ if (info.license) {
445
+ lines.push(`License: ${info.license}`);
446
+ }
447
+ if (info.keywords?.length) {
448
+ lines.push(`Keywords: ${info.keywords.join(", ")}`);
449
+ }
450
+ if (info.authors?.length) {
451
+ lines.push(`Authors: ${info.authors.join(", ")}`);
452
+ }
453
+ if (info.dependencies && Object.keys(info.dependencies).length > 0) {
454
+ lines.push("", "Dependencies:");
455
+ for (const [name, version] of Object.entries(info.dependencies).slice(0, 10)) {
456
+ lines.push(` - ${name}: ${version}`);
457
+ }
458
+ if (Object.keys(info.dependencies).length > 10) {
459
+ lines.push(` ... and ${Object.keys(info.dependencies).length - 10} more`);
460
+ }
461
+ }
462
+ return lines.join("\n");
463
+ }
464
+
465
+ // src/commands/search.ts
466
+ async function search(ctx) {
467
+ const currentConfig = getSearchConfig();
468
+ const config = ctx.config;
469
+ console.log(chalk.bold("\nWeb Search Configuration\n"));
470
+ console.log(chalk.gray(`Current provider: ${chalk.cyan(currentConfig.provider)}`));
471
+ const braveKeySet = !!(currentConfig.braveApiKey || process.env.BRAVE_SEARCH_API_KEY);
472
+ const parallelKeySet = !!(currentConfig.parallelApiKey || process.env.PARALLEL_API_KEY);
473
+ console.log(chalk.gray(`Brave API key: ${braveKeySet ? chalk.green("configured") : chalk.yellow("not set")}`));
474
+ console.log(chalk.gray(`Parallel API key: ${parallelKeySet ? chalk.green("configured") : chalk.yellow("not set")}`));
475
+ console.log();
476
+ const providerOptions = [
477
+ {
478
+ label: `DuckDuckGo ${chalk.gray("(no API key required, may be blocked by CAPTCHA)")}`,
479
+ value: "duckduckgo"
480
+ },
481
+ {
482
+ label: `Brave Search ${chalk.gray("(requires API key)")} ${braveKeySet ? chalk.green("\u2713") : ""}`,
483
+ value: "brave"
484
+ },
485
+ {
486
+ label: `Parallel.ai ${chalk.gray("(requires API key)")} ${parallelKeySet ? chalk.green("\u2713") : ""}`,
487
+ value: "parallel"
488
+ }
489
+ ];
490
+ try {
491
+ const result = await showModal({
492
+ title: "Select search provider:",
493
+ options: providerOptions,
494
+ initialIndex: providerOptions.findIndex((c) => c.value === currentConfig.provider)
495
+ });
496
+ if (!result) {
497
+ console.log(chalk.gray("Search configuration cancelled."));
498
+ return null;
499
+ }
500
+ const provider = result.value;
501
+ let braveApiKey = currentConfig.braveApiKey;
502
+ let parallelApiKey = currentConfig.parallelApiKey;
503
+ if (provider === "brave" && !braveKeySet) {
504
+ console.log(chalk.gray("\nGet your free Brave Search API key at: https://brave.com/search/api/\n"));
505
+ const apiKey = await showPassword({
506
+ title: "Enter Brave Search API key:"
507
+ });
508
+ if (apiKey?.trim()) {
509
+ braveApiKey = apiKey.trim();
510
+ } else {
511
+ console.log(chalk.yellow("No API key entered. Brave Search will not work without an API key."));
512
+ return null;
513
+ }
514
+ }
515
+ if (provider === "parallel" && !parallelKeySet) {
516
+ console.log(chalk.gray("\nGet your Parallel.ai API key at: https://platform.parallel.ai\n"));
517
+ const apiKey = await showPassword({
518
+ title: "Enter Parallel.ai API key:"
519
+ });
520
+ if (apiKey?.trim()) {
521
+ parallelApiKey = apiKey.trim();
522
+ } else {
523
+ console.log(chalk.yellow("No API key entered. Parallel.ai Search will not work without an API key."));
524
+ return null;
525
+ }
526
+ }
527
+ configureSearch({
528
+ provider,
529
+ braveApiKey,
530
+ parallelApiKey
531
+ });
532
+ if (config) {
533
+ config.search = {
534
+ provider,
535
+ braveApiKey,
536
+ parallelApiKey
537
+ };
538
+ await saveConfig(config);
539
+ console.log(chalk.green(`
540
+ \u2713 Search provider set to ${provider} and saved to config`));
541
+ } else {
542
+ console.log(chalk.green(`
543
+ \u2713 Search provider set to ${provider} (session only)`));
544
+ }
545
+ switch (provider) {
546
+ case "duckduckgo":
547
+ console.log(chalk.gray("Note: DuckDuckGo may block automated requests with a CAPTCHA."));
548
+ break;
549
+ case "brave":
550
+ console.log(chalk.gray("Brave Search is now active. Free tier allows 2,000 queries/month."));
551
+ break;
552
+ case "parallel":
553
+ console.log(chalk.gray("Parallel.ai Search is now active with deep research capabilities."));
554
+ break;
555
+ }
556
+ return null;
557
+ } catch (error) {
558
+ throw error;
559
+ }
560
+ }
561
+ var metadata = {
562
+ command: "/search",
563
+ description: "configure web search provider (brave, duckduckgo, parallel)",
564
+ implemented: true
565
+ };
566
+
567
+ export {
568
+ configureSearch,
569
+ webSearch,
570
+ fetchUrl,
571
+ getPackageInfo,
572
+ formatSearchResults,
573
+ formatPackageInfo,
574
+ search,
575
+ metadata
576
+ };
577
+ /**
578
+ * @license
579
+ * Copyright 2025 Autohand AI LLC
580
+ * SPDX-License-Identifier: Apache-2.0
581
+ *
582
+ * Web search and URL fetching capabilities for up-to-date information
583
+ * about packages, dependencies, documentation, and changelogs.
584
+ */
585
+ /**
586
+ * @license
587
+ * Copyright 2025 Autohand AI LLC
588
+ * SPDX-License-Identifier: Apache-2.0
589
+ */
@@ -1,6 +1,6 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
- var _chunkPMMSDR44cjs = require('./chunk-PMMSDR44.cjs');
3
+ var _chunkAQNY2M6Qcjs = require('./chunk-AQNY2M6Q.cjs');
4
4
 
5
5
  // src/skills/CommunitySkillsCache.ts
6
6
  var _fsextra = require('fs-extra'); var _fsextra2 = _interopRequireDefault(_fsextra);
@@ -9,7 +9,7 @@ var DEFAULT_TTL_MS = 24 * 60 * 60 * 1e3;
9
9
  var DEFAULT_MAX_SKILLS_CACHE = 50;
10
10
  var CommunitySkillsCache = class {
11
11
  constructor(config = {}) {
12
- this.cacheDir = config.cacheDir || _path2.default.join(_chunkPMMSDR44cjs.AUTOHAND_HOME, "community-skills", "cache");
12
+ this.cacheDir = config.cacheDir || _path2.default.join(_chunkAQNY2M6Qcjs.AUTOHAND_HOME, "community-skills", "cache");
13
13
  this.ttlMs = _nullishCoalesce(config.ttlMs, () => ( DEFAULT_TTL_MS));
14
14
  this.maxSkillsCache = _nullishCoalesce(config.maxSkillsCache, () => ( DEFAULT_MAX_SKILLS_CACHE));
15
15
  }