abtars 0.1.0-alpha.1

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 (312) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +84 -0
  3. package/bundle/_registry.generated-M4WY2MMI.js +35 -0
  4. package/bundle/_registry.generated-M4WY2MMI.js.map +7 -0
  5. package/bundle/abtars-browser.js +162 -0
  6. package/bundle/abtars-browser.js.map +7 -0
  7. package/bundle/abtars-cli.js +1438 -0
  8. package/bundle/abtars-cli.js.map +7 -0
  9. package/bundle/abtars-restart.js +12 -0
  10. package/bundle/abtars-restart.js.map +7 -0
  11. package/bundle/abtars-rss.js +165 -0
  12. package/bundle/abtars-rss.js.map +7 -0
  13. package/bundle/abtars-task.js +258 -0
  14. package/bundle/abtars-task.js.map +7 -0
  15. package/bundle/abtars.js +4072 -0
  16. package/bundle/abtars.js.map +7 -0
  17. package/bundle/agent-api-rate-limit-OQNFMXTZ.js +38 -0
  18. package/bundle/agent-api-rate-limit-OQNFMXTZ.js.map +7 -0
  19. package/bundle/agent-registry-LT4JNQH6.js +18 -0
  20. package/bundle/agent-registry-LT4JNQH6.js.map +7 -0
  21. package/bundle/agents/default.md +29 -0
  22. package/bundle/anthropic-adapter-2APTH3LA.js +40 -0
  23. package/bundle/anthropic-adapter-2APTH3LA.js.map +7 -0
  24. package/bundle/bridge-lock-transport-4AC2G5G6.js +39 -0
  25. package/bundle/bridge-lock-transport-4AC2G5G6.js.map +7 -0
  26. package/bundle/browse-delivery-JXBY36GK.js +17 -0
  27. package/bundle/browse-delivery-JXBY36GK.js.map +7 -0
  28. package/bundle/browser-ELNDVPLC.js +18 -0
  29. package/bundle/browser-ELNDVPLC.js.map +7 -0
  30. package/bundle/capability-CIL3G4FI.js +17 -0
  31. package/bundle/capability-CIL3G4FI.js.map +7 -0
  32. package/bundle/chunk-265TPOPC.js +289 -0
  33. package/bundle/chunk-265TPOPC.js.map +7 -0
  34. package/bundle/chunk-2UENBO6M.js +223 -0
  35. package/bundle/chunk-2UENBO6M.js.map +7 -0
  36. package/bundle/chunk-2UPU3OW6.js +67 -0
  37. package/bundle/chunk-2UPU3OW6.js.map +7 -0
  38. package/bundle/chunk-2XU2X4OI.js +125 -0
  39. package/bundle/chunk-2XU2X4OI.js.map +7 -0
  40. package/bundle/chunk-3B7BBE4F.js +758 -0
  41. package/bundle/chunk-3B7BBE4F.js.map +7 -0
  42. package/bundle/chunk-3E545J66.js +69 -0
  43. package/bundle/chunk-3E545J66.js.map +7 -0
  44. package/bundle/chunk-5R2ANXQ7.js +510 -0
  45. package/bundle/chunk-5R2ANXQ7.js.map +7 -0
  46. package/bundle/chunk-6CPN4IGS.js +507 -0
  47. package/bundle/chunk-6CPN4IGS.js.map +7 -0
  48. package/bundle/chunk-6NR3OHEW.js +88 -0
  49. package/bundle/chunk-6NR3OHEW.js.map +7 -0
  50. package/bundle/chunk-6SETMHNN.js +206 -0
  51. package/bundle/chunk-6SETMHNN.js.map +7 -0
  52. package/bundle/chunk-6UCRKRWR.js +644 -0
  53. package/bundle/chunk-6UCRKRWR.js.map +7 -0
  54. package/bundle/chunk-AR6GO6YC.js +83 -0
  55. package/bundle/chunk-AR6GO6YC.js.map +7 -0
  56. package/bundle/chunk-AZJIODTQ.js +54 -0
  57. package/bundle/chunk-AZJIODTQ.js.map +7 -0
  58. package/bundle/chunk-BHMZ4RCC.js +3706 -0
  59. package/bundle/chunk-BHMZ4RCC.js.map +7 -0
  60. package/bundle/chunk-BQ2L4GMG.js +9175 -0
  61. package/bundle/chunk-BQ2L4GMG.js.map +7 -0
  62. package/bundle/chunk-BSSBCSCL.js +159 -0
  63. package/bundle/chunk-BSSBCSCL.js.map +7 -0
  64. package/bundle/chunk-BUUVFUPO.js +157 -0
  65. package/bundle/chunk-BUUVFUPO.js.map +7 -0
  66. package/bundle/chunk-CEVRHKJY.js +131 -0
  67. package/bundle/chunk-CEVRHKJY.js.map +7 -0
  68. package/bundle/chunk-CWOHNFUV.js +39 -0
  69. package/bundle/chunk-CWOHNFUV.js.map +7 -0
  70. package/bundle/chunk-D2DCBO6M.js +228 -0
  71. package/bundle/chunk-D2DCBO6M.js.map +7 -0
  72. package/bundle/chunk-FMWKEPM7.js +31 -0
  73. package/bundle/chunk-FMWKEPM7.js.map +7 -0
  74. package/bundle/chunk-GRNENTPA.js +145 -0
  75. package/bundle/chunk-GRNENTPA.js.map +7 -0
  76. package/bundle/chunk-GST5T3WZ.js +93 -0
  77. package/bundle/chunk-GST5T3WZ.js.map +7 -0
  78. package/bundle/chunk-GUQVJC3U.js +299 -0
  79. package/bundle/chunk-GUQVJC3U.js.map +7 -0
  80. package/bundle/chunk-HX7Y7EYP.js +3659 -0
  81. package/bundle/chunk-HX7Y7EYP.js.map +7 -0
  82. package/bundle/chunk-JCJS4ZIB.js +296 -0
  83. package/bundle/chunk-JCJS4ZIB.js.map +7 -0
  84. package/bundle/chunk-JW6RU47G.js +184 -0
  85. package/bundle/chunk-JW6RU47G.js.map +7 -0
  86. package/bundle/chunk-LSPKJQCI.js +24 -0
  87. package/bundle/chunk-LSPKJQCI.js.map +7 -0
  88. package/bundle/chunk-M6VBAPNT.js +16 -0
  89. package/bundle/chunk-M6VBAPNT.js.map +7 -0
  90. package/bundle/chunk-MPX525QO.js +129 -0
  91. package/bundle/chunk-MPX525QO.js.map +7 -0
  92. package/bundle/chunk-MW6WDLU7.js +130 -0
  93. package/bundle/chunk-MW6WDLU7.js.map +7 -0
  94. package/bundle/chunk-NT3OBORC.js +215 -0
  95. package/bundle/chunk-NT3OBORC.js.map +7 -0
  96. package/bundle/chunk-NWDBD4PA.js +50 -0
  97. package/bundle/chunk-NWDBD4PA.js.map +7 -0
  98. package/bundle/chunk-OP7BTAWY.js +29 -0
  99. package/bundle/chunk-OP7BTAWY.js.map +7 -0
  100. package/bundle/chunk-PLCY3GFH.js +77 -0
  101. package/bundle/chunk-PLCY3GFH.js.map +7 -0
  102. package/bundle/chunk-PNEDC45Y.js +97 -0
  103. package/bundle/chunk-PNEDC45Y.js.map +7 -0
  104. package/bundle/chunk-QBGBT5QS.js +81 -0
  105. package/bundle/chunk-QBGBT5QS.js.map +7 -0
  106. package/bundle/chunk-RVE2N7FA.js +70 -0
  107. package/bundle/chunk-RVE2N7FA.js.map +7 -0
  108. package/bundle/chunk-TZHIDLDS.js +71910 -0
  109. package/bundle/chunk-TZHIDLDS.js.map +7 -0
  110. package/bundle/chunk-UCQ2WC3B.js +126 -0
  111. package/bundle/chunk-UCQ2WC3B.js.map +7 -0
  112. package/bundle/chunk-UHRP745J.js +214 -0
  113. package/bundle/chunk-UHRP745J.js.map +7 -0
  114. package/bundle/chunk-V76TVMCM.js +58 -0
  115. package/bundle/chunk-V76TVMCM.js.map +7 -0
  116. package/bundle/chunk-VVEDVGCR.js +981 -0
  117. package/bundle/chunk-VVEDVGCR.js.map +7 -0
  118. package/bundle/chunk-W6FAL35D.js +102 -0
  119. package/bundle/chunk-W6FAL35D.js.map +7 -0
  120. package/bundle/chunk-X6TERNVJ.js +15902 -0
  121. package/bundle/chunk-X6TERNVJ.js.map +7 -0
  122. package/bundle/chunk-X76UX47U.js +47 -0
  123. package/bundle/chunk-X76UX47U.js.map +7 -0
  124. package/bundle/chunk-XREWVCUO.js +518 -0
  125. package/bundle/chunk-XREWVCUO.js.map +7 -0
  126. package/bundle/chunk-Y6XAEX2Q.js +408 -0
  127. package/bundle/chunk-Y6XAEX2Q.js.map +7 -0
  128. package/bundle/chunk-YOCTDKKL.js +28 -0
  129. package/bundle/chunk-YOCTDKKL.js.map +7 -0
  130. package/bundle/chunk-ZXPXCDA6.js +160 -0
  131. package/bundle/chunk-ZXPXCDA6.js.map +7 -0
  132. package/bundle/commands-BHVUOU3V.js +31 -0
  133. package/bundle/commands-BHVUOU3V.js.map +7 -0
  134. package/bundle/completion-buffer-P253ONKF.js +13 -0
  135. package/bundle/completion-buffer-P253ONKF.js.map +7 -0
  136. package/bundle/config-RGSDAPZN.js +19 -0
  137. package/bundle/config-RGSDAPZN.js.map +7 -0
  138. package/bundle/config-show-ERTATR6E.js +40 -0
  139. package/bundle/config-show-ERTATR6E.js.map +7 -0
  140. package/bundle/context-HCEGZNDC.js +72 -0
  141. package/bundle/context-HCEGZNDC.js.map +7 -0
  142. package/bundle/delegation-tools-GYTS2D6A.js +27 -0
  143. package/bundle/delegation-tools-GYTS2D6A.js.map +7 -0
  144. package/bundle/deploy-lib-import-32ZFKHWP.js +49 -0
  145. package/bundle/deploy-lib-import-32ZFKHWP.js.map +7 -0
  146. package/bundle/digital-signature-OFCGSHWO.js +13 -0
  147. package/bundle/digital-signature-OFCGSHWO.js.map +7 -0
  148. package/bundle/direct-api-transport-YR7SXXNN.js +860 -0
  149. package/bundle/direct-api-transport-YR7SXXNN.js.map +7 -0
  150. package/bundle/discord-adapter-YYWVMPPU.js +584 -0
  151. package/bundle/discord-adapter-YYWVMPPU.js.map +7 -0
  152. package/bundle/dist-MTMKARCP.js +1969 -0
  153. package/bundle/dist-MTMKARCP.js.map +7 -0
  154. package/bundle/dns-wakeup-27M7D2MR.js +107 -0
  155. package/bundle/dns-wakeup-27M7D2MR.js.map +7 -0
  156. package/bundle/doctor-QNUSDY73.js +248 -0
  157. package/bundle/doctor-QNUSDY73.js.map +7 -0
  158. package/bundle/ensure-invariants-NMXNS476.js +49 -0
  159. package/bundle/ensure-invariants-NMXNS476.js.map +7 -0
  160. package/bundle/env-schema-2KBHBDGN.js +19 -0
  161. package/bundle/env-schema-2KBHBDGN.js.map +7 -0
  162. package/bundle/esm-DDP6NCZG.js +100663 -0
  163. package/bundle/esm-DDP6NCZG.js.map +7 -0
  164. package/bundle/fallback-policy-L4QV2PEJ.js +46 -0
  165. package/bundle/fallback-policy-L4QV2PEJ.js.map +7 -0
  166. package/bundle/health-check-SPA7NT6N.js +56 -0
  167. package/bundle/health-check-SPA7NT6N.js.map +7 -0
  168. package/bundle/hook-system-6Q5YTR53.js +17 -0
  169. package/bundle/hook-system-6Q5YTR53.js.map +7 -0
  170. package/bundle/hotskills-K7BM4YLB.js +12 -0
  171. package/bundle/hotskills-K7BM4YLB.js.map +7 -0
  172. package/bundle/install-6HRZVKUM.js +15 -0
  173. package/bundle/install-6HRZVKUM.js.map +7 -0
  174. package/bundle/install-log-IAPHYKD4.js +28 -0
  175. package/bundle/install-log-IAPHYKD4.js.map +7 -0
  176. package/bundle/install-manifest-SPQRUNXL.js +102 -0
  177. package/bundle/install-manifest-SPQRUNXL.js.map +7 -0
  178. package/bundle/install-validate-PVLZXYLQ.js +53 -0
  179. package/bundle/install-validate-PVLZXYLQ.js.map +7 -0
  180. package/bundle/irc-adapter-OI5UZSQF.js +293 -0
  181. package/bundle/irc-adapter-OI5UZSQF.js.map +7 -0
  182. package/bundle/irc-config-55YO6EGB.js +88 -0
  183. package/bundle/irc-config-55YO6EGB.js.map +7 -0
  184. package/bundle/logs-ZNYXX5PA.js +19 -0
  185. package/bundle/logs-ZNYXX5PA.js.map +7 -0
  186. package/bundle/media-utils-XNNDTYFI.js +4662 -0
  187. package/bundle/media-utils-XNNDTYFI.js.map +7 -0
  188. package/bundle/message-pipeline-LLH5SYMO.js +33 -0
  189. package/bundle/message-pipeline-LLH5SYMO.js.map +7 -0
  190. package/bundle/meta.json +41304 -0
  191. package/bundle/model-health-registry-35LQNVQR.js +11 -0
  192. package/bundle/model-health-registry-35LQNVQR.js.map +7 -0
  193. package/bundle/notification-Y5S5MMLV.js +13 -0
  194. package/bundle/notification-Y5S5MMLV.js.map +7 -0
  195. package/bundle/openrouter-credits-EDY7ETAU.js +32 -0
  196. package/bundle/openrouter-credits-EDY7ETAU.js.map +7 -0
  197. package/bundle/passwd-RRFV4CC5.js +133 -0
  198. package/bundle/passwd-RRFV4CC5.js.map +7 -0
  199. package/bundle/paths-G33RZWZ7.js +17 -0
  200. package/bundle/paths-G33RZWZ7.js.map +7 -0
  201. package/bundle/peer-client-52XYMNI7.js +156 -0
  202. package/bundle/peer-client-52XYMNI7.js.map +7 -0
  203. package/bundle/peer-config-VK6EDLN5.js +16 -0
  204. package/bundle/peer-config-VK6EDLN5.js.map +7 -0
  205. package/bundle/peer-sessions-EAXTNQ36.js +49 -0
  206. package/bundle/peer-sessions-EAXTNQ36.js.map +7 -0
  207. package/bundle/pending-callback-RIMQZ7FJ.js +40 -0
  208. package/bundle/pending-callback-RIMQZ7FJ.js.map +7 -0
  209. package/bundle/phase-transport-KYERDL2O.js +22 -0
  210. package/bundle/phase-transport-KYERDL2O.js.map +7 -0
  211. package/bundle/public/css/dashboard.css +542 -0
  212. package/bundle/public/index.html +180 -0
  213. package/bundle/public/js/app.js +437 -0
  214. package/bundle/public/memory-universe.js +384 -0
  215. package/bundle/responses-adapter-AAQTY3K4.js +30 -0
  216. package/bundle/responses-adapter-AAQTY3K4.js.map +7 -0
  217. package/bundle/restore-ZE3SEPSS.js +46 -0
  218. package/bundle/restore-ZE3SEPSS.js.map +7 -0
  219. package/bundle/self-healer-utils-DMUUXC47.js +43 -0
  220. package/bundle/self-healer-utils-DMUUXC47.js.map +7 -0
  221. package/bundle/skill-stats-LLEXEXLR.js +22 -0
  222. package/bundle/skill-stats-LLEXEXLR.js.map +7 -0
  223. package/bundle/sleep-OYIUOVQD.js +19 -0
  224. package/bundle/sleep-OYIUOVQD.js.map +7 -0
  225. package/bundle/soul-loader-54WCVNLJ.js +16 -0
  226. package/bundle/soul-loader-54WCVNLJ.js.map +7 -0
  227. package/bundle/src-JL4PVO23.js +8 -0
  228. package/bundle/src-JL4PVO23.js.map +7 -0
  229. package/bundle/sse-parser-anthropic-P7CE2MH2.js +72 -0
  230. package/bundle/sse-parser-anthropic-P7CE2MH2.js.map +7 -0
  231. package/bundle/sse-parser-responses-EQQA5FWN.js +63 -0
  232. package/bundle/sse-parser-responses-EQQA5FWN.js.map +7 -0
  233. package/bundle/ssrf-guard-FZCBYIVW.js +64 -0
  234. package/bundle/ssrf-guard-FZCBYIVW.js.map +7 -0
  235. package/bundle/start-FH3GRMJ4.js +35 -0
  236. package/bundle/start-FH3GRMJ4.js.map +7 -0
  237. package/bundle/stream-single-WSG4D53C.js +33 -0
  238. package/bundle/stream-single-WSG4D53C.js.map +7 -0
  239. package/bundle/stt-2UH3RITX.js +14 -0
  240. package/bundle/stt-2UH3RITX.js.map +7 -0
  241. package/bundle/subagent-runtime-LE2ZXH3G.js +12 -0
  242. package/bundle/subagent-runtime-LE2ZXH3G.js.map +7 -0
  243. package/bundle/system-message-T5R3EYYN.js +30 -0
  244. package/bundle/system-message-T5R3EYYN.js.map +7 -0
  245. package/bundle/system-status-KQ6KHFJ6.js +189 -0
  246. package/bundle/system-status-KQ6KHFJ6.js.map +7 -0
  247. package/bundle/task-store-K7CQDEPI.js +22 -0
  248. package/bundle/task-store-K7CQDEPI.js.map +7 -0
  249. package/bundle/telegram-adapter-2V3XUMT5.js +1060 -0
  250. package/bundle/telegram-adapter-2V3XUMT5.js.map +7 -0
  251. package/bundle/tool-registry-MU3OX4UI.js +38 -0
  252. package/bundle/tool-registry-MU3OX4UI.js.map +7 -0
  253. package/bundle/tool-sandbox-VYOK4ZOA.js +20 -0
  254. package/bundle/tool-sandbox-VYOK4ZOA.js.map +7 -0
  255. package/bundle/transport-config-YLXU33RO.js +57 -0
  256. package/bundle/transport-config-YLXU33RO.js.map +7 -0
  257. package/bundle/update-QCW5LXRN.js +13 -0
  258. package/bundle/update-QCW5LXRN.js.map +7 -0
  259. package/bundle/update-check-27KZSAP6.js +12 -0
  260. package/bundle/update-check-27KZSAP6.js.map +7 -0
  261. package/bundle/usage-tracker-OVVEVMOY.js +17 -0
  262. package/bundle/usage-tracker-OVVEVMOY.js.map +7 -0
  263. package/bundle/user-registry-D4SD73UV.js +16 -0
  264. package/bundle/user-registry-D4SD73UV.js.map +7 -0
  265. package/core/professor.json +14 -0
  266. package/core/prompts/browsing_prompt.md +39 -0
  267. package/core/prompts/compaction.md +32 -0
  268. package/core/skills/memory/classification/SKILL.md +37 -0
  269. package/core/skills/memory/memory-anomalies/SKILL.md +39 -0
  270. package/core/skills/memory/memory-search/SKILL.md +48 -0
  271. package/core/skills/memory/topic-save/SKILL.md +44 -0
  272. package/core/skills/ops/cron/SKILL.md +51 -0
  273. package/core/skills/ops/gdrive-backup/SKILL.md +15 -0
  274. package/core/skills/ops/session-start/SKILL.md +11 -0
  275. package/core/skills/ops/skill-authoring/SKILL.md +54 -0
  276. package/core/skills/ops/system-health/SKILL.md +104 -0
  277. package/core/skills/ops/troubleshooting/SKILL.md +48 -0
  278. package/core/skills/ops/trust-gating/SKILL.md +30 -0
  279. package/core/skills/tools/a2a-communication/SKILL.md +68 -0
  280. package/core/skills/tools/browse-delegate/SKILL.md +27 -0
  281. package/core/skills/tools/browser/SKILL.md +36 -0
  282. package/core/skills/tools/clawhub/SKILL.md +44 -0
  283. package/core/skills/tools/delegation/SKILL.md +48 -0
  284. package/core/skills/tools/fxtwitter/SKILL.md +52 -0
  285. package/core/skills/tools/gmail/SKILL.md +44 -0
  286. package/core/skills/tools/irc-chat/SKILL.md +84 -0
  287. package/core/skills/tools/linear/SKILL.md +90 -0
  288. package/core/skills/tools/mcporter/SKILL.md +46 -0
  289. package/core/skills/tools/model-scout/SKILL.md +132 -0
  290. package/core/skills/tools/model-scout/scout-add-model.py +67 -0
  291. package/core/skills/tools/model-scout/scout-ollama.py +116 -0
  292. package/core/skills/tools/model-scout/scout-openrouter.py +85 -0
  293. package/core/skills/tools/nlm/SKILL.md +40 -0
  294. package/core/skills/tools/todo/SKILL.md +30 -0
  295. package/core/skills/tools/twitterX/SKILL.md +52 -0
  296. package/core/skills/tools/twitterX/scripts/abtars-tweet.js +532 -0
  297. package/core/skills/tools/twitterX/scripts/package.json +1 -0
  298. package/core/skills/tools/web-fetch/SKILL.md +29 -0
  299. package/package.json +59 -0
  300. package/scripts/abtars-daemon.service +23 -0
  301. package/scripts/abtars-fetch.sh +42 -0
  302. package/scripts/abtars-watchdog.service +13 -0
  303. package/scripts/abtars.sh +14 -0
  304. package/scripts/abtars@.service +21 -0
  305. package/scripts/browser-patchright.sh +79 -0
  306. package/scripts/com.abtars.daemon.plist +24 -0
  307. package/scripts/com.abtars.watchdog.plist +27 -0
  308. package/scripts/daily-backup.sh +62 -0
  309. package/scripts/doctor.sh +553 -0
  310. package/scripts/hooks/audit-logger.sh +22 -0
  311. package/scripts/upgrade-deps.sh +64 -0
  312. package/scripts/watchdog.sh +309 -0
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: twitter
3
+ description: Fetch tweets and monitor AI influencers via FXTwitter API + web search. No API keys needed for single tweets.
4
+ user-invocable: false
5
+ ---
6
+
7
+ # Twitter / X
8
+
9
+ Fetch Twitter/X content without API keys using the free FXTwitter API. No timeline/search endpoint — use web search for discovery, FXTwitter for structured data.
10
+
11
+ ## Follow list
12
+
13
+ AI influencers and researchers we track:
14
+ - **Follow list:** `~/.abtars/workspace/twitterX/follows.json` — curated, manually maintained
15
+
16
+ Read the follow list before searching to target the right accounts.
17
+
18
+ ## Pipeline
19
+
20
+ 1. Read follow list: `cat ~/.abtars/workspace/twitterX/base.follows.json`
21
+ 2. Search for recent tweets: `web_search("site:x.com from:handle 2026")`
22
+ 3. Extract tweet ID from URL: `https://x.com/{user}/status/{id}`
23
+ 4. Fetch structured data: `curl -s "https://api.fxtwitter.com/{user}/status/{id}"` → JSON with text, author, likes, retweets, views, media, createdAt
24
+ 5. Compile results
25
+
26
+ ## Endpoints
27
+
28
+ - **Tweet:** `GET https://api.fxtwitter.com/{screen_name}/status/{tweet_id}`
29
+ - **Tweet + translation:** `GET https://api.fxtwitter.com/{screen_name}/status/{tweet_id}/{lang_code}`
30
+ - **User profile:** `GET https://api.fxtwitter.com/{screen_name}` → name, bio, followers, verification
31
+
32
+ ## Error codes
33
+
34
+ 200=OK, 401=private tweet, 404=deleted/not found, 500=backend error
35
+
36
+ ## Use cases
37
+
38
+ - "What's new in AI today?" → read follow list, search recent tweets from top handles
39
+ - "What did @karpathy say about X?" → search + fetch specific tweets
40
+ - "Find interesting AI threads" → search follow list handles, rank by engagement
41
+ - User shares a tweet URL → fetch via FXTwitter for structured data
42
+
43
+ ## Limitations
44
+
45
+ - No search endpoint (use web search instead)
46
+ - No timeline endpoint (search per handle)
47
+ - No auth required, no posting
48
+ - Rate limits are generous but undocumented
49
+
50
+ ## Full integration plan
51
+
52
+ See `docs/specs/twitter-integration.plan.md` for the `abtars-tweet` CLI roadmap (rettiwt-api, daily newsletter, discovery).
@@ -0,0 +1,44 @@
1
+ ---
2
+ name: gmail
3
+ description: Read and manage Gmail via gws-cli
4
+ user-invocable: false
5
+ ---
6
+
7
+ # Gmail Access
8
+
9
+ Read, search, and manage emails using `gws-cli gmail`. Auth is pre-configured.
10
+
11
+ ## Commands
12
+
13
+ ```bash
14
+ # List unread emails
15
+ gws-cli gmail list -q "is:unread" -n 20
16
+
17
+ # Read a message by ID
18
+ gws-cli gmail read MSG_ID
19
+
20
+ # Search
21
+ gws-cli gmail search -q "from:someone subject:invoice"
22
+
23
+ # Mark as read
24
+ gws-cli gmail mark-read MSG_ID
25
+
26
+ # Mark as unread
27
+ gws-cli gmail mark-unread MSG_ID
28
+ ```
29
+
30
+ ## Search query syntax
31
+
32
+ Standard Gmail operators: `from:`, `to:`, `subject:`, `is:unread`, `newer_than:1d`, `has:attachment`, `label:`, `after:2026/03/01`.
33
+
34
+ ## When checking emails
35
+
36
+ 1. `gws-cli gmail list -q "is:unread" -n 20`
37
+ 2. For each unread email: `gws-cli gmail read <id>`, then send a SHORT summary to the user as a separate message (one message per email)
38
+ 3. Summary format: **From:** / **Subject:** / 1-2 sentence gist
39
+ 4. Do NOT create files, reports, or .md documents — deliver summaries directly in chat
40
+ 5. After summarizing, mark as read: `gws-cli gmail mark-read <id>`
41
+
42
+ ## Rules
43
+ - Never delete or trash emails without explicit user request
44
+ - If no unread emails, do not send any message — stay silent
@@ -0,0 +1,84 @@
1
+ ---
2
+ name: irc-chat
3
+ description: Participate in IRC channels when the bridge has irc.json configured. Post on user request, respond to @mentions and the user, coordinate with other bots on shared channels.
4
+ requires: abtars
5
+ ---
6
+
7
+ # IRC Chat Participation
8
+
9
+ You can participate in IRC channels. The bridge's IRC connections are defined in `~/.abtars/config/irc.json`. Each server entry lists channels you're configured for, with nick and mode per channel.
10
+
11
+ ## How to find your IRC context
12
+
13
+ ```bash
14
+ cat ~/.abtars/config/irc.json
15
+ ```
16
+
17
+ The file has `servers[]`, each with `id`, `host`, `nick`, and `channels{}`. For each channel:
18
+ - `mode`: `"plain"` or `"secure"` (secure = Ed25519-signed, plain = unauthenticated)
19
+ - `requireMention`: `true` = only respond when your nick is mentioned
20
+ - `allowFrom`: nicks whose messages you will react to (others are ignored even if addressed to you)
21
+ - `trustedKeys`: (secure mode only) map of nick → Ed25519 public key
22
+
23
+ If no `irc.json` or no servers, IRC is inactive for this bridge.
24
+
25
+ ## When to post
26
+
27
+ **Post only when the user explicitly asks:**
28
+ - "post to IRC that the deploy is done"
29
+ - "tell the #bridges channel I'm restarting"
30
+ - "announce on IRC: ..."
31
+
32
+ The platform adapter handles tool invocation — you don't call raw IRC commands. Use the same outbound message channel you'd use for any reply; the adapter routes to IRC when the user's prompt came from IRC.
33
+
34
+ **Don't post proactively** unless the user configured you to (e.g. a cron'd status broadcast). Spontaneous IRC posts from an LLM are noise.
35
+
36
+ ## When to respond
37
+
38
+ - An inbound IRC message mentioned your nick (adapter filtered it already — if it reached you, it's addressed to you)
39
+ - The sender is in `allowFrom` for that channel (adapter filtered)
40
+ - Keep replies short — one or two lines. IRC is a ticker, not a chat window
41
+
42
+ If you're uncertain whether to respond, don't. A missed reply is recoverable; a noisy loop between bots is not.
43
+
44
+ ## Channel awareness
45
+
46
+ You may be on multiple channels. The inbound message carries its channel — respond on the same channel unless the user says otherwise. Don't cross-post.
47
+
48
+ If the user says "join channel #X", you can't actually join from the LLM side — joining is a config change (`irc.json`). Tell the user: "To join #X, add it under the appropriate server in `~/.abtars/config/irc.json` and restart. I can describe the config shape if you want."
49
+
50
+ ## Message format
51
+
52
+ - Plain text. No markdown (IRC doesn't render it).
53
+ - One line per message. The adapter splits long outputs automatically, but keep replies short on purpose — IRC lines above ~340 chars on secure channels get truncated.
54
+ - No emojis unless you know the channel supports UTF-8 (most modern servers do, but older clients break).
55
+
56
+ ## Signatures (secure channels)
57
+
58
+ Secure channels require Ed25519 signatures. **You do not sign anything manually** — the adapter layer signs every outbound message on secure channels automatically using the bot's private key from `.env`.
59
+
60
+ - Secure channels are bot-to-bot (humans can't sign in their IRC client).
61
+ - If you receive a message on a secure channel that fails verification, the adapter drops it before you see it — you'll never be tricked into replying to a spoofed sender.
62
+ - Don't try to embed signature-like tags in your replies (`[sig:...]`). The adapter does that.
63
+
64
+ ## What NOT to do
65
+
66
+ - Don't relay private conversations (user-to-you) to IRC without explicit instruction
67
+ - Don't quote secrets, tokens, file paths, or PII — IRC messages may be logged or world-readable depending on server
68
+ - Don't chat with another bot unprompted. A brief acknowledgement to coordinate is fine; small talk is not
69
+ - Don't respond with long code blocks — commit to git and reference the SHA
70
+ - Don't ignore `allowFrom` or `requireMention` — the adapter respects these, and if a message reached you, trust that it should be answered
71
+
72
+ ## Interaction with peer_ask (a2a-communication)
73
+
74
+ These are separate tools for different purposes:
75
+
76
+ | | peer_ask (a2a-communication) | IRC |
77
+ |---|---|---|
78
+ | Model | Sync RPC | Async broadcast |
79
+ | Audience | One peer | Whole channel |
80
+ | Visibility | Log-only | Human-watchable (via IRC client) |
81
+ | Use when | You need an answer from a specific peer | User asks you to announce / converse on a channel |
82
+
83
+ If the user says "ask <peer> what time it is" → use `peer_ask`.
84
+ If the user says "tell IRC you're restarting" → reply through the adapter, it goes to IRC.
@@ -0,0 +1,90 @@
1
+ ---
2
+ name: linear
3
+ description: Manage Linear projects, issues, and tasks via the bundled Node CLI and the official Linear API. Use when you need to read, create, update, or organize Linear issues, projects, teams, milestones, comments, cycles, labels, and documents.
4
+ metadata: {"gracebot":{"always":false,"emoji":"📐","homepage":"https://github.com/MaTriXy/linear-skill","requires":{"bins":["node","npm"],"env":["LINEAR_API_KEY"]},"primaryEnv":"LINEAR_API_KEY","install":[{"id":"node-brew","kind":"brew","formula":"node","bins":["node","npm"],"label":"Install Node.js (brew)"}]},"clawdbot":{"always":false,"emoji":"📐","homepage":"https://github.com/MaTriXy/linear-skill","requires":{"bins":["node","npm"],"env":["LINEAR_API_KEY"]},"primaryEnv":"LINEAR_API_KEY","install":[{"id":"node-brew","kind":"brew","formula":"node","bins":["node","npm"],"label":"Install Node.js (brew)"}]}}
5
+ ---
6
+
7
+ # Linear Workflow Management
8
+
9
+ Manage Linear issues and projects through the bundled CLI at `{baseDir}/scripts/linear-cli.js`.
10
+
11
+ ## Scope and Runtime Model
12
+
13
+ - This skill runs `node {baseDir}/scripts/linear-cli.js ...`.
14
+ - The CLI uses the official `@linear/sdk`.
15
+ - Authentication is `LINEAR_API_KEY` from the local environment.
16
+ - Expected API destination is Linear GraphQL (`https://api.linear.app/graphql`) through the official SDK.
17
+
18
+ ## Prerequisites
19
+
20
+ 1. Node.js and npm are installed.
21
+ 2. Install script dependencies once:
22
+ - `cd {baseDir}/scripts && npm install`
23
+ 3. Set your API key:
24
+ - `export LINEAR_API_KEY="lin_api_..."`
25
+
26
+ If dependencies or `LINEAR_API_KEY` are missing, stop and complete setup before issue/project operations.
27
+
28
+ ## Authentication and Credentials
29
+
30
+ - Required credential: `LINEAR_API_KEY`.
31
+ - Get it from `https://linear.app/settings/api`.
32
+ - Use least-privilege access and a dedicated token for automation.
33
+
34
+ ## Required Workflow
35
+
36
+ 1. Clarify intent and scope:
37
+ - Team/project, labels, cycle, assignee, due date, priority.
38
+ 2. Read current state first:
39
+ - List/get issues, projects, statuses, labels, users, cycles.
40
+ 3. Apply mutations second:
41
+ - Create/update issues, comments, projects, milestones, labels.
42
+ 4. Summarize exactly what changed:
43
+ - Mention IDs, states, assignees, blockers, and follow-up actions.
44
+
45
+ ## Command Coverage
46
+
47
+ - Teams and projects:
48
+ `teams`, `projects`, `createProject`
49
+ - Issues:
50
+ `issues`, `issue`, `createIssue`, `updateIssue`
51
+ - Comments:
52
+ `createComment`
53
+ - States and labels:
54
+ `states`, `labels`
55
+ - User:
56
+ `user`
57
+
58
+ ## Quick Examples
59
+
60
+ ```bash
61
+ node {baseDir}/scripts/linear-cli.js teams
62
+ node {baseDir}/scripts/linear-cli.js projects
63
+ node {baseDir}/scripts/linear-cli.js issues
64
+ node {baseDir}/scripts/linear-cli.js issue ENG-123
65
+ node {baseDir}/scripts/linear-cli.js createIssue "Title" "Description" "team-id" '{"priority":2}'
66
+ node {baseDir}/scripts/linear-cli.js updateIssue "issue-id" '{"stateId":"state-id"}'
67
+ ```
68
+
69
+ ## Practical Workflows
70
+
71
+ - Triage urgent bugs:
72
+ list high-priority open issues, assign owners, move state to `In Progress`, add triage comments.
73
+ - Sprint planning:
74
+ review cycle scope, create missing issues, set priorities and estimates, align assignees.
75
+ - Release prep:
76
+ verify blockers, update project status, create milestone tasks, add rollout comments.
77
+ - Documentation cleanup:
78
+ find stale docs/issues, open follow-up tasks, link related records.
79
+
80
+ ## Safety and Operational Rules
81
+
82
+ - Never invent IDs; fetch and confirm before updates.
83
+ - Prefer narrow updates over broad bulk edits.
84
+ - For bulk edits, explain grouping logic before applying changes.
85
+ - Do not include secrets in issue comments or descriptions.
86
+ - Do not send data to endpoints outside Linear API scope for this skill.
87
+
88
+ ## References
89
+
90
+ - `references/API.md` for priority values and workflow patterns.
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: mcporter
3
+ description: Call external MCP servers via the mcporter CLI. Use for accessing tools from services like Context7, Linear, GitHub, Notion, etc.
4
+ ---
5
+
6
+ # mcporter — MCP Tool Access
7
+
8
+ Use `mcporter` to discover and call tools on external MCP servers.
9
+
10
+ ## List available servers
11
+
12
+ ```bash
13
+ mcporter list
14
+ ```
15
+
16
+ ## List tools on a specific server
17
+
18
+ ```bash
19
+ mcporter list <server> --schema
20
+ ```
21
+
22
+ ## Call a tool
23
+
24
+ ```bash
25
+ mcporter call <server>.<tool> key=value key2=value2
26
+ ```
27
+
28
+ ## Examples
29
+
30
+ ```bash
31
+ # Resolve a library ID on Context7
32
+ mcporter call context7.resolve-library-id libraryName=react
33
+
34
+ # Get library docs
35
+ mcporter call context7.get-library-docs context7CompatibleLibraryID=/websites/react_dev topic=hooks
36
+
37
+ # Search Linear
38
+ mcporter call linear.search_documentation query="automations"
39
+ ```
40
+
41
+ ## Notes
42
+
43
+ - Config: `~/workspace/mcporter/config/mcporter.json`
44
+ - Auto-imports configs from Cursor, Claude, Codex, VS Code
45
+ - For OAuth servers, run `mcporter auth <server>` first
46
+ - Use `--output json` for machine-readable results
@@ -0,0 +1,132 @@
1
+ # Model Scout
2
+
3
+ Find the best free cloud models and propose the top 3 candidates.
4
+
5
+ ## When to use
6
+ User asks about model recommendations, best deals, or you need to evaluate if a better model is available.
7
+
8
+ ## Config files
9
+
10
+ - **models.json** — `~/.abtars/config/models.json` (hot-reloaded)
11
+ - **transport.json** — `~/.abtars/config/transport.json` (lists providers, defaults, fallbackChain)
12
+
13
+ ## transport.json provider fields (relevant to scouting)
14
+
15
+ Each provider in `transport.json` may have:
16
+ - **`defaults`** — `Record<agent, { model, fallbacks? }>` — preset models loaded on `/model change → provider`. Professor is required; missing subagents inherit professor's model.
17
+ - **`fallbackChain`** — `string[]` — ordered list of always-available models tried when the configured model fails. Used by subagent runtime (not professor).
18
+
19
+ When proposing new models, consider whether they should be added to a provider's `defaults` or `fallbackChain`.
20
+
21
+ ## models.json schema
22
+
23
+ ```json
24
+ {
25
+ "model-id": {
26
+ "contextWindow": 262144,
27
+ "maxOutput": 16384,
28
+ "rank": 2,
29
+ "cost": { "input": 0.0, "output": 0.0 },
30
+ "transports": ["ollama", "openrouter"],
31
+ "description": "High IQ free model, top Intelligence Index score",
32
+ "validatedAt": "2026-04-13"
33
+ }
34
+ }
35
+ ```
36
+
37
+ - **rank**: 1 = frontier, 2 = strong, 3 = good, 4 = basic, 5 = minimal
38
+ - **cost**: per million tokens in USD. `0.0` = free
39
+ - **transports**: provider names from transport.json that can serve this model
40
+ - **description**: why this model was added (scout writes this)
41
+ - **validatedAt**: date when last verified alive (auto-set by script)
42
+
43
+ ## Scouting workflow
44
+
45
+ ### 1. Scan available free models
46
+
47
+ ```bash
48
+ # OpenRouter free tier
49
+ python3 {baseDir}/scout-openrouter.py
50
+
51
+ # Ollama cloud models (no local, cloud only)
52
+ python3 {baseDir}/scout-ollama.py
53
+ ```
54
+
55
+ ### 2. Research quality
56
+
57
+ Browse leaderboards to get Intelligence Index scores:
58
+ ```bash
59
+ abtars-browser --action navigate --url "https://artificialanalysis.ai/leaderboards/models"
60
+ abtars-browser --action extract_text --max-chars 5000
61
+ ```
62
+
63
+ Search for new cloud models on Ollama:
64
+ ```bash
65
+ abtars-browser --action navigate --url "https://ollama.com/search?q=cloud"
66
+ abtars-browser --action extract_text --max-chars 5000
67
+ ```
68
+
69
+ ### 3. Propose top 3
70
+
71
+ After scanning and researching, propose exactly 3 candidates ranked by:
72
+
73
+ 1. **Intelligence Index** (higher = better) → determines rank
74
+ 2. **Context window** (bigger = fewer compactions)
75
+ 3. **Tool calling** (required — model must support function calling)
76
+ 4. **Throughput** (tokens/sec, matters for interactive use)
77
+
78
+ Format:
79
+ ```
80
+ 🏆 Top 3 free cloud models:
81
+
82
+ 1. model-name (provider) — Intelligence: XX, Context: XXK
83
+ Why: [one sentence reason]
84
+
85
+ 2. model-name (provider) — Intelligence: XX, Context: XXK
86
+ Why: [one sentence reason]
87
+
88
+ 3. model-name (provider) — Intelligence: XX, Context: XXK
89
+ Why: [one sentence reason]
90
+ ```
91
+
92
+ ### 4. Add approved models
93
+
94
+ After user approves, add with description explaining why:
95
+
96
+ ```bash
97
+ python3 {baseDir}/scout-add-model.py \
98
+ "model-id" contextWindow maxOutput rank input_cost output_cost \
99
+ "Why this model: Intelligence XX, free, large context" \
100
+ ollama openrouter
101
+ ```
102
+
103
+ The script automatically:
104
+ - Backs up models.json to `.old`
105
+ - Sets `validatedAt` to today's date
106
+ - Validates all entries after write
107
+ - Restores from backup if validation fails
108
+
109
+ ### 5. Test
110
+
111
+ ```
112
+ /model change → pick provider → verify defaults loaded
113
+ /model (check fallback chain displayed)
114
+ ```
115
+
116
+ ## Liveness test
117
+
118
+ Before proposing, verify the model actually responds:
119
+
120
+ ```bash
121
+ # Ollama cloud
122
+ curl -s http://localhost:11434/v1/chat/completions -d '{
123
+ "model": "model-name",
124
+ "messages": [{"role":"user","content":"Say hi"}],
125
+ "max_tokens": 5
126
+ }'
127
+
128
+ # OpenRouter
129
+ curl -s https://openrouter.ai/api/v1/chat/completions \
130
+ -H "Authorization: Bearer $OPENROUTER_API_KEY" \
131
+ -d '{"model":"model-id","messages":[{"role":"user","content":"Say hi"}],"max_tokens":5}'
132
+ ```
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env python3
2
+ """scout-add-model.py — Add or update a model in models.json.
3
+
4
+ Usage:
5
+ scout-add-model.py MODEL_ID CTX_WINDOW MAX_OUTPUT RANK INPUT_COST OUTPUT_COST "DESCRIPTION" TRANSPORT [TRANSPORT...]
6
+
7
+ Example:
8
+ scout-add-model.py "nemotron-3-super:cloud" 262144 16384 2 0.0 0.0 "Nvidia 120B MoE, free" ollama openrouter
9
+ """
10
+ import json, sys, shutil
11
+ from pathlib import Path
12
+ from datetime import date
13
+
14
+ MODELS_PATH = Path.home() / ".abtars" / "config" / "models.json"
15
+ REQUIRED = {"contextWindow", "maxOutput", "rank", "cost", "transports"}
16
+
17
+ def main():
18
+ if len(sys.argv) < 8:
19
+ print(__doc__.strip(), file=sys.stderr)
20
+ sys.exit(2)
21
+
22
+ model_id = sys.argv[1]
23
+ ctx = int(sys.argv[2])
24
+ max_out = int(sys.argv[3])
25
+ rank = int(sys.argv[4])
26
+ in_cost = float(sys.argv[5])
27
+ out_cost = float(sys.argv[6])
28
+ desc = sys.argv[7]
29
+ transports = sys.argv[8:]
30
+
31
+ if not transports:
32
+ print("Error: at least one transport required", file=sys.stderr)
33
+ sys.exit(2)
34
+
35
+ backup = MODELS_PATH.with_suffix(".json.old")
36
+ shutil.copy2(MODELS_PATH, backup)
37
+
38
+ with open(MODELS_PATH) as f:
39
+ models = json.load(f)
40
+
41
+ models[model_id] = {
42
+ "contextWindow": ctx,
43
+ "maxOutput": max_out,
44
+ "rank": rank,
45
+ "cost": {"input": in_cost, "output": out_cost},
46
+ "transports": transports,
47
+ "description": desc,
48
+ "validatedAt": str(date.today()),
49
+ }
50
+
51
+ # Validate all entries
52
+ for mid, m in models.items():
53
+ missing = REQUIRED - set(m.keys())
54
+ if missing:
55
+ print(f"Validation failed: {mid} missing {missing}. Restoring backup.", file=sys.stderr)
56
+ shutil.copy2(backup, MODELS_PATH)
57
+ sys.exit(1)
58
+
59
+ with open(MODELS_PATH, "w") as f:
60
+ json.dump(models, f, indent=2)
61
+ f.write("\n")
62
+
63
+ free = "FREE" if in_cost == 0 and out_cost == 0 else f"${in_cost}/{out_cost}"
64
+ print(f"✓ {model_id} (rank {rank}, ctx {ctx//1024}K, {free}, via {transports})")
65
+
66
+ if __name__ == "__main__":
67
+ main()
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env python3
2
+ """List Ollama cloud models (free, no local GPU needed) + local installed.
3
+ Fetches from ollama.com library API for cloud models.
4
+ Usage:
5
+ scout-ollama.py # list only
6
+ scout-ollama.py --test # list + liveness test cloud models + write status
7
+ """
8
+ import json, os, sys, urllib.request
9
+
10
+ config_dir = os.path.expanduser("~/.abtars/config")
11
+ models_path = os.path.join(config_dir, "models.json")
12
+ do_test = "--test" in sys.argv
13
+
14
+ # Load current catalog
15
+ try:
16
+ catalog = json.load(open(models_path))
17
+ except Exception:
18
+ catalog = {}
19
+ cataloged = {k for k in catalog if "ollama" in catalog[k].get("transports", [])}
20
+
21
+ # --- Cloud models from ollama.com ---
22
+ print("=== Ollama Cloud Models (free) ===")
23
+ print(f"{'Model':<45} {'Status'}")
24
+ print("-" * 60)
25
+
26
+ try:
27
+ req = urllib.request.Request(
28
+ "https://ollama.com/search?c=cloud",
29
+ headers={"User-Agent": "abtars-scout/1.0"}
30
+ )
31
+ import re
32
+ html = urllib.request.urlopen(req, timeout=10).read().decode()
33
+ cloud_names = list(dict.fromkeys(re.findall(r'href="/library/([^"]+)"', html)))
34
+ cloud_models = [{"name": f"{n}:cloud"} for n in cloud_names]
35
+ except Exception as e:
36
+ # Fallback: check local tags for :cloud suffix
37
+ cloud_models = []
38
+ print(f" (ollama.com scrape failed: {e}, falling back to local tags)")
39
+
40
+ # If API didn't work, check local tags for :cloud suffix
41
+ if not cloud_models:
42
+ try:
43
+ local_data = json.loads(urllib.request.urlopen("http://localhost:11434/api/tags", timeout=5).read())
44
+ cloud_models = [{"name": m["name"]} for m in local_data.get("models", []) if ":cloud" in m["name"]]
45
+ except Exception:
46
+ pass
47
+
48
+ from datetime import date
49
+ today = date.today().isoformat()
50
+
51
+ for m in cloud_models:
52
+ name = m["name"] if isinstance(m, dict) else m
53
+ if not name.endswith(":cloud") and ":cloud" not in name:
54
+ name = f"{name}:cloud"
55
+ status = "✓ cataloged" if name in cataloged else "NEW"
56
+
57
+ # Liveness test
58
+ if do_test and status == "NEW":
59
+ try:
60
+ test_data = json.dumps({"model": name, "messages": [{"role": "user", "content": "hi"}], "max_tokens": 5}).encode()
61
+ test_req = urllib.request.Request(
62
+ "http://localhost:11434/v1/chat/completions",
63
+ headers={"Content-Type": "application/json"},
64
+ data=test_data
65
+ )
66
+ resp = json.loads(urllib.request.urlopen(test_req, timeout=15).read())
67
+ content = resp.get("choices", [{}])[0].get("message", {}).get("content", "")
68
+ alive = bool(content.strip())
69
+ except Exception:
70
+ alive = False
71
+ status = f"{'✓ alive' if alive else '✗ dead'}"
72
+
73
+ if name not in catalog:
74
+ catalog[name] = {
75
+ "contextWindow": 262144, "maxOutput": 16384, "rank": 3,
76
+ "cost": {"input": 0.0, "output": 0.0},
77
+ "transports": ["ollama"],
78
+ "description": f"Ollama cloud model, scouted {today}",
79
+ "validatedAt": today, "status": "alive" if alive else "dead"
80
+ }
81
+ else:
82
+ catalog[name]["validatedAt"] = today
83
+ catalog[name]["status"] = "alive" if alive else "dead"
84
+
85
+ print(f" {name:<43} {status}")
86
+
87
+ # --- Local installed models ---
88
+ print(f"\n=== Local Installed ===")
89
+ print(f"{'Model':<45} {'Size':>8} {'Status'}")
90
+ print("-" * 60)
91
+
92
+ try:
93
+ local_data = json.loads(urllib.request.urlopen("http://localhost:11434/api/tags", timeout=5).read())
94
+ installed = local_data.get("models", [])
95
+ except Exception:
96
+ installed = []
97
+ print(" (Ollama not reachable at localhost:11434)")
98
+
99
+ try:
100
+ ps = json.loads(urllib.request.urlopen("http://localhost:11434/api/ps", timeout=5).read())
101
+ running = {m["name"] for m in ps.get("models", [])}
102
+ except Exception:
103
+ running = set()
104
+
105
+ for m in installed:
106
+ name = m["name"]
107
+ size_gb = m.get("size", 0) / 1e9
108
+ run_mark = " ▶" if name in running else ""
109
+ status = "✓ cataloged" if name in cataloged else "NEW"
110
+ print(f" {name:<43} {size_gb:>6.1f}G{run_mark} {status}")
111
+
112
+ if do_test:
113
+ with open(models_path, "w") as f:
114
+ json.dump(catalog, f, indent=2)
115
+ f.write("\n")
116
+ print(f"\n✓ models.json updated ({len(catalog)} entries)")