@jackwener/opencli 1.7.11 → 1.7.12

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 (730) hide show
  1. package/README.zh-CN.md +2 -1
  2. package/cli-manifest.json +1417 -24
  3. package/clis/1688/assets.js +1 -0
  4. package/clis/1688/download.js +1 -0
  5. package/clis/1688/item.js +1 -0
  6. package/clis/1688/search.js +2 -1
  7. package/clis/1688/store.js +1 -0
  8. package/clis/36kr/article.js +1 -0
  9. package/clis/36kr/hot.js +1 -0
  10. package/clis/36kr/news.js +1 -0
  11. package/clis/36kr/search.js +1 -0
  12. package/clis/51job/company.js +1 -0
  13. package/clis/51job/detail.js +1 -0
  14. package/clis/51job/hot.js +1 -0
  15. package/clis/51job/search.js +1 -0
  16. package/clis/amazon/bestsellers.js +1 -0
  17. package/clis/amazon/discussion.js +1 -0
  18. package/clis/amazon/movers-shakers.js +1 -0
  19. package/clis/amazon/new-releases.js +1 -0
  20. package/clis/amazon/offer.js +1 -0
  21. package/clis/amazon/product.js +1 -0
  22. package/clis/amazon/rankings.js +1 -0
  23. package/clis/amazon/search.js +1 -0
  24. package/clis/antigravity/dump.js +1 -0
  25. package/clis/antigravity/extract-code.js +1 -0
  26. package/clis/antigravity/model.js +1 -0
  27. package/clis/antigravity/new.js +1 -0
  28. package/clis/antigravity/read.js +1 -0
  29. package/clis/antigravity/send.js +1 -0
  30. package/clis/antigravity/status.js +1 -0
  31. package/clis/antigravity/watch.js +1 -0
  32. package/clis/apple-podcasts/episodes.js +1 -0
  33. package/clis/apple-podcasts/search.js +1 -0
  34. package/clis/apple-podcasts/top.js +1 -0
  35. package/clis/arxiv/arxiv.test.js +112 -0
  36. package/clis/arxiv/paper.js +4 -3
  37. package/clis/arxiv/recent.js +33 -0
  38. package/clis/arxiv/search.js +19 -7
  39. package/clis/arxiv/utils.js +68 -5
  40. package/clis/baidu-scholar/search.js +1 -0
  41. package/clis/band/bands.js +1 -0
  42. package/clis/band/mentions.js +1 -0
  43. package/clis/band/post.js +1 -0
  44. package/clis/band/posts.js +1 -0
  45. package/clis/barchart/flow.js +1 -0
  46. package/clis/barchart/greeks.js +1 -0
  47. package/clis/barchart/options.js +1 -0
  48. package/clis/barchart/quote.js +1 -0
  49. package/clis/bbc/news.js +1 -0
  50. package/clis/bilibili/comments.js +1 -0
  51. package/clis/bilibili/download.js +1 -0
  52. package/clis/bilibili/dynamic.js +1 -0
  53. package/clis/bilibili/favorite.js +1 -0
  54. package/clis/bilibili/feed.js +2 -0
  55. package/clis/bilibili/following.js +1 -0
  56. package/clis/bilibili/history.js +1 -0
  57. package/clis/bilibili/hot.js +6 -1
  58. package/clis/bilibili/hot.test.js +17 -0
  59. package/clis/bilibili/me.js +1 -1
  60. package/clis/bilibili/ranking.js +1 -0
  61. package/clis/bilibili/search.js +1 -1
  62. package/clis/bilibili/subtitle.js +1 -0
  63. package/clis/bilibili/user-videos.js +1 -0
  64. package/clis/bilibili/video.js +1 -0
  65. package/clis/binance/asks.js +1 -0
  66. package/clis/binance/depth.js +1 -0
  67. package/clis/binance/gainers.js +1 -0
  68. package/clis/binance/klines.js +1 -0
  69. package/clis/binance/losers.js +1 -0
  70. package/clis/binance/pairs.js +1 -0
  71. package/clis/binance/price.js +1 -0
  72. package/clis/binance/prices.js +1 -0
  73. package/clis/binance/ticker.js +1 -0
  74. package/clis/binance/top.js +1 -0
  75. package/clis/binance/trades.js +1 -0
  76. package/clis/bloomberg/businessweek.js +1 -0
  77. package/clis/bloomberg/economics.js +1 -0
  78. package/clis/bloomberg/feeds.js +1 -0
  79. package/clis/bloomberg/industries.js +1 -0
  80. package/clis/bloomberg/main.js +1 -0
  81. package/clis/bloomberg/markets.js +1 -0
  82. package/clis/bloomberg/news.js +1 -0
  83. package/clis/bloomberg/opinions.js +1 -0
  84. package/clis/bloomberg/politics.js +1 -0
  85. package/clis/bloomberg/tech.js +1 -0
  86. package/clis/bluesky/feeds.js +1 -0
  87. package/clis/bluesky/followers.js +1 -0
  88. package/clis/bluesky/following.js +1 -0
  89. package/clis/bluesky/profile.js +1 -0
  90. package/clis/bluesky/search.js +1 -0
  91. package/clis/bluesky/starter-packs.js +1 -0
  92. package/clis/bluesky/thread.js +1 -0
  93. package/clis/bluesky/trending.js +1 -0
  94. package/clis/bluesky/user.js +3 -1
  95. package/clis/boss/batchgreet.js +1 -0
  96. package/clis/boss/chatlist.js +1 -0
  97. package/clis/boss/chatmsg.js +1 -0
  98. package/clis/boss/detail.js +1 -0
  99. package/clis/boss/exchange.js +1 -0
  100. package/clis/boss/greet.js +1 -0
  101. package/clis/boss/invite.js +1 -0
  102. package/clis/boss/joblist.js +1 -0
  103. package/clis/boss/mark.js +1 -0
  104. package/clis/boss/recommend.js +1 -0
  105. package/clis/boss/resume.js +1 -0
  106. package/clis/boss/search.js +1 -0
  107. package/clis/boss/send.js +1 -0
  108. package/clis/boss/stats.js +1 -0
  109. package/clis/chaoxing/assignments.js +1 -0
  110. package/clis/chaoxing/exams.js +1 -0
  111. package/clis/chatgpt/image.js +1 -0
  112. package/clis/chatgpt-app/ask.js +1 -0
  113. package/clis/chatgpt-app/model.js +1 -0
  114. package/clis/chatgpt-app/new.js +1 -0
  115. package/clis/chatgpt-app/read.js +1 -0
  116. package/clis/chatgpt-app/send.js +1 -0
  117. package/clis/chatgpt-app/status.js +1 -0
  118. package/clis/chatwise/ask.js +1 -0
  119. package/clis/chatwise/export.js +1 -0
  120. package/clis/chatwise/history.js +1 -0
  121. package/clis/chatwise/model.js +1 -0
  122. package/clis/chatwise/read.js +1 -0
  123. package/clis/chatwise/send.js +1 -0
  124. package/clis/claude/ask.js +1 -0
  125. package/clis/claude/detail.js +1 -0
  126. package/clis/claude/history.js +1 -0
  127. package/clis/claude/new.js +1 -0
  128. package/clis/claude/read.js +1 -0
  129. package/clis/claude/send.js +1 -0
  130. package/clis/claude/status.js +1 -0
  131. package/clis/cnki/search.js +1 -0
  132. package/clis/codex/ask.js +1 -0
  133. package/clis/codex/export.js +1 -0
  134. package/clis/codex/extract-diff.js +1 -0
  135. package/clis/codex/history.js +1 -0
  136. package/clis/codex/model.js +1 -0
  137. package/clis/codex/read.js +1 -0
  138. package/clis/codex/send.js +1 -0
  139. package/clis/coupang/add-to-cart.js +1 -0
  140. package/clis/coupang/search.js +1 -0
  141. package/clis/ctrip/search.js +1 -0
  142. package/clis/cursor/ask.js +1 -0
  143. package/clis/cursor/composer.js +1 -0
  144. package/clis/cursor/export.js +1 -0
  145. package/clis/cursor/extract-code.js +1 -0
  146. package/clis/cursor/history.js +1 -0
  147. package/clis/cursor/model.js +1 -0
  148. package/clis/cursor/read.js +1 -0
  149. package/clis/cursor/send.js +1 -0
  150. package/clis/dblp/dblp.test.js +397 -0
  151. package/clis/dblp/paper.js +40 -0
  152. package/clis/dblp/search.js +45 -0
  153. package/clis/dblp/utils.js +290 -0
  154. package/clis/deepseek/ask.js +1 -0
  155. package/clis/deepseek/history.js +1 -0
  156. package/clis/deepseek/new.js +1 -0
  157. package/clis/deepseek/read.js +1 -0
  158. package/clis/deepseek/status.js +1 -0
  159. package/clis/devto/devto.test.js +236 -0
  160. package/clis/devto/read.js +103 -0
  161. package/clis/devto/tag.js +5 -1
  162. package/clis/devto/top.js +5 -1
  163. package/clis/devto/user.js +5 -1
  164. package/clis/dianping/__fixtures__/search.html +168 -0
  165. package/clis/dianping/__fixtures__/shop.html +6 -0
  166. package/clis/dianping/dianping.test.js +424 -0
  167. package/clis/dianping/search.js +154 -0
  168. package/clis/dianping/shop.js +173 -0
  169. package/clis/dianping/utils.js +157 -0
  170. package/clis/dictionary/examples.js +1 -0
  171. package/clis/dictionary/search.js +1 -0
  172. package/clis/dictionary/synonyms.js +1 -0
  173. package/clis/discord-app/channels.js +1 -0
  174. package/clis/discord-app/delete.js +1 -0
  175. package/clis/discord-app/members.js +1 -0
  176. package/clis/discord-app/read.js +1 -0
  177. package/clis/discord-app/search.js +1 -0
  178. package/clis/discord-app/send.js +1 -0
  179. package/clis/discord-app/servers.js +1 -0
  180. package/clis/discord-app/status.js +1 -0
  181. package/clis/douban/book-hot.js +1 -0
  182. package/clis/douban/download.js +1 -0
  183. package/clis/douban/marks.js +1 -0
  184. package/clis/douban/movie-hot.js +2 -1
  185. package/clis/douban/movie-hot.test.js +14 -0
  186. package/clis/douban/photos.js +2 -1
  187. package/clis/douban/reviews.js +1 -0
  188. package/clis/douban/search.js +1 -0
  189. package/clis/douban/subject.js +1 -0
  190. package/clis/douban/top250.js +1 -0
  191. package/clis/douban/utils.js +11 -13
  192. package/clis/douban/utils.test.js +79 -0
  193. package/clis/doubao/ask.js +1 -0
  194. package/clis/doubao/detail.js +1 -0
  195. package/clis/doubao/history.js +1 -0
  196. package/clis/doubao/meeting-summary.js +1 -0
  197. package/clis/doubao/meeting-transcript.js +1 -0
  198. package/clis/doubao/new.js +1 -0
  199. package/clis/doubao/read.js +1 -0
  200. package/clis/doubao/send.js +1 -0
  201. package/clis/doubao/status.js +1 -0
  202. package/clis/doubao-app/ask.js +1 -0
  203. package/clis/doubao-app/dump.js +1 -0
  204. package/clis/doubao-app/new.js +1 -0
  205. package/clis/doubao-app/read.js +1 -0
  206. package/clis/doubao-app/screenshot.js +1 -0
  207. package/clis/doubao-app/send.js +1 -0
  208. package/clis/doubao-app/status.js +1 -0
  209. package/clis/douyin/activities.js +1 -0
  210. package/clis/douyin/collections.js +1 -0
  211. package/clis/douyin/delete.js +1 -0
  212. package/clis/douyin/draft.js +1 -0
  213. package/clis/douyin/drafts.js +1 -0
  214. package/clis/douyin/hashtag.js +1 -0
  215. package/clis/douyin/location.js +1 -0
  216. package/clis/douyin/profile.js +1 -0
  217. package/clis/douyin/publish.js +1 -0
  218. package/clis/douyin/stats.js +1 -0
  219. package/clis/douyin/update.js +1 -0
  220. package/clis/douyin/user-videos.js +1 -0
  221. package/clis/douyin/videos.js +1 -0
  222. package/clis/eastmoney/announcement.js +1 -0
  223. package/clis/eastmoney/convertible.js +1 -0
  224. package/clis/eastmoney/etf.js +1 -0
  225. package/clis/eastmoney/holders.js +1 -0
  226. package/clis/eastmoney/hot-rank.js +1 -0
  227. package/clis/eastmoney/index-board.js +1 -0
  228. package/clis/eastmoney/kline.js +1 -0
  229. package/clis/eastmoney/kuaixun.js +1 -0
  230. package/clis/eastmoney/longhu.js +1 -0
  231. package/clis/eastmoney/money-flow.js +1 -0
  232. package/clis/eastmoney/northbound.js +1 -0
  233. package/clis/eastmoney/quote.js +1 -0
  234. package/clis/eastmoney/rank.js +1 -0
  235. package/clis/eastmoney/sectors.js +1 -0
  236. package/clis/facebook/add-friend.js +1 -0
  237. package/clis/facebook/events.js +1 -0
  238. package/clis/facebook/feed.js +1 -0
  239. package/clis/facebook/friends.js +1 -0
  240. package/clis/facebook/groups.js +1 -0
  241. package/clis/facebook/join-group.js +1 -0
  242. package/clis/facebook/marketplace-inbox.js +1 -0
  243. package/clis/facebook/marketplace-listings.js +1 -0
  244. package/clis/facebook/memories.js +1 -0
  245. package/clis/facebook/notifications.js +1 -0
  246. package/clis/facebook/profile.js +1 -0
  247. package/clis/facebook/search.js +1 -0
  248. package/clis/gemini/ask.js +1 -0
  249. package/clis/gemini/deep-research-result.js +1 -0
  250. package/clis/gemini/deep-research.js +1 -0
  251. package/clis/gemini/image.js +1 -0
  252. package/clis/gemini/new.js +1 -0
  253. package/clis/gitee/search.js +1 -0
  254. package/clis/gitee/trending.js +1 -0
  255. package/clis/gitee/user.js +1 -0
  256. package/clis/google/news.js +1 -0
  257. package/clis/google/search.js +1 -0
  258. package/clis/google/suggest.js +1 -0
  259. package/clis/google/trends.js +1 -0
  260. package/clis/google-scholar/cite.js +1 -0
  261. package/clis/google-scholar/profile.js +1 -0
  262. package/clis/google-scholar/search.js +1 -0
  263. package/clis/gov-law/recent.js +1 -0
  264. package/clis/gov-law/search.js +1 -0
  265. package/clis/gov-policy/recent.js +1 -0
  266. package/clis/gov-policy/search.js +1 -0
  267. package/clis/grok/ask.js +1 -0
  268. package/clis/grok/image.ts +1 -0
  269. package/clis/hackernews/ask.js +3 -1
  270. package/clis/hackernews/best.js +3 -1
  271. package/clis/hackernews/hackernews.test.js +132 -0
  272. package/clis/hackernews/jobs.js +3 -1
  273. package/clis/hackernews/new.js +3 -1
  274. package/clis/hackernews/read.js +188 -0
  275. package/clis/hackernews/search.js +3 -1
  276. package/clis/hackernews/show.js +3 -1
  277. package/clis/hackernews/top.js +3 -1
  278. package/clis/hackernews/user.js +1 -0
  279. package/clis/hf/top.js +1 -0
  280. package/clis/hupu/detail.js +1 -0
  281. package/clis/hupu/hot.js +3 -1
  282. package/clis/hupu/like.js +1 -0
  283. package/clis/hupu/mentions.js +2 -1
  284. package/clis/hupu/reply.js +1 -0
  285. package/clis/hupu/search.js +3 -1
  286. package/clis/hupu/unlike.js +1 -0
  287. package/clis/imdb/person.js +1 -0
  288. package/clis/imdb/reviews.js +1 -0
  289. package/clis/imdb/search.js +1 -0
  290. package/clis/imdb/title.js +1 -0
  291. package/clis/imdb/top.js +1 -0
  292. package/clis/imdb/trending.js +1 -0
  293. package/clis/indeed/indeed.test.js +375 -0
  294. package/clis/indeed/job.js +86 -0
  295. package/clis/indeed/search.js +110 -0
  296. package/clis/indeed/utils.js +152 -0
  297. package/clis/instagram/collection-create.js +1 -0
  298. package/clis/instagram/collection-delete.js +1 -0
  299. package/clis/instagram/comment.js +1 -0
  300. package/clis/instagram/download.js +1 -0
  301. package/clis/instagram/explore.js +1 -0
  302. package/clis/instagram/follow.js +1 -0
  303. package/clis/instagram/followers.js +1 -0
  304. package/clis/instagram/following.js +1 -0
  305. package/clis/instagram/like.js +1 -0
  306. package/clis/instagram/note.js +1 -0
  307. package/clis/instagram/post.js +1 -0
  308. package/clis/instagram/profile.js +1 -0
  309. package/clis/instagram/reel.js +1 -0
  310. package/clis/instagram/save.js +1 -0
  311. package/clis/instagram/saved.js +1 -0
  312. package/clis/instagram/search.js +1 -0
  313. package/clis/instagram/story.js +1 -0
  314. package/clis/instagram/unfollow.js +1 -0
  315. package/clis/instagram/unlike.js +1 -0
  316. package/clis/instagram/unsave.js +1 -0
  317. package/clis/instagram/user.js +1 -0
  318. package/clis/jd/add-cart.js +1 -0
  319. package/clis/jd/cart.js +1 -0
  320. package/clis/jd/detail.js +1 -0
  321. package/clis/jd/item.js +1 -0
  322. package/clis/jd/reviews.js +1 -0
  323. package/clis/jd/search.js +1 -0
  324. package/clis/jianyu/detail.js +1 -0
  325. package/clis/jianyu/search.js +1 -0
  326. package/clis/jike/comment.js +1 -0
  327. package/clis/jike/create.js +1 -0
  328. package/clis/jike/feed.js +3 -1
  329. package/clis/jike/like.js +1 -0
  330. package/clis/jike/notifications.js +1 -0
  331. package/clis/jike/post.js +1 -0
  332. package/clis/jike/repost.js +1 -0
  333. package/clis/jike/search.js +3 -1
  334. package/clis/jike/topic.js +1 -0
  335. package/clis/jike/user.js +3 -1
  336. package/clis/jimeng/generate.js +1 -0
  337. package/clis/jimeng/history.js +1 -0
  338. package/clis/jimeng/new.js +1 -0
  339. package/clis/jimeng/workspaces.js +1 -0
  340. package/clis/ke/chengjiao.js +1 -0
  341. package/clis/ke/ershoufang.js +1 -0
  342. package/clis/ke/xiaoqu.js +1 -0
  343. package/clis/ke/zufang.js +1 -0
  344. package/clis/lesswrong/comments.js +1 -0
  345. package/clis/lesswrong/curated.js +1 -0
  346. package/clis/lesswrong/frontpage.js +1 -0
  347. package/clis/lesswrong/new.js +1 -0
  348. package/clis/lesswrong/read.js +1 -0
  349. package/clis/lesswrong/sequences.js +1 -0
  350. package/clis/lesswrong/shortform.js +1 -0
  351. package/clis/lesswrong/tag.js +1 -0
  352. package/clis/lesswrong/tags.js +1 -0
  353. package/clis/lesswrong/top-month.js +1 -0
  354. package/clis/lesswrong/top-week.js +1 -0
  355. package/clis/lesswrong/top-year.js +1 -0
  356. package/clis/lesswrong/top.js +1 -0
  357. package/clis/lesswrong/user-posts.js +1 -0
  358. package/clis/lesswrong/user.js +1 -0
  359. package/clis/linkedin/search.js +1 -0
  360. package/clis/linkedin/timeline.js +1 -0
  361. package/clis/linux-do/categories.js +1 -0
  362. package/clis/linux-do/category.js +1 -0
  363. package/clis/linux-do/feed.js +1 -0
  364. package/clis/linux-do/hot.js +1 -0
  365. package/clis/linux-do/latest.js +1 -0
  366. package/clis/linux-do/search.js +1 -0
  367. package/clis/linux-do/tags.js +2 -1
  368. package/clis/linux-do/topic-content.js +1 -0
  369. package/clis/linux-do/topic.js +1 -0
  370. package/clis/linux-do/user-posts.js +1 -0
  371. package/clis/linux-do/user-topics.js +1 -0
  372. package/clis/lobsters/active.js +4 -1
  373. package/clis/lobsters/hot.js +4 -1
  374. package/clis/lobsters/lobsters.test.js +169 -0
  375. package/clis/lobsters/newest.js +4 -1
  376. package/clis/lobsters/read.js +196 -0
  377. package/clis/lobsters/tag.js +4 -1
  378. package/clis/maimai/search-talents.js +1 -0
  379. package/clis/medium/feed.js +1 -0
  380. package/clis/medium/search.js +1 -0
  381. package/clis/medium/user.js +1 -0
  382. package/clis/mubu/doc.js +1 -0
  383. package/clis/mubu/docs.js +1 -0
  384. package/clis/mubu/notes.js +1 -0
  385. package/clis/mubu/recent.js +1 -0
  386. package/clis/mubu/search.js +1 -0
  387. package/clis/notebooklm/current.js +1 -0
  388. package/clis/notebooklm/get.js +1 -0
  389. package/clis/notebooklm/history.js +1 -0
  390. package/clis/notebooklm/list.js +1 -0
  391. package/clis/notebooklm/note-list.js +1 -0
  392. package/clis/notebooklm/notes-get.js +1 -0
  393. package/clis/notebooklm/open.js +1 -0
  394. package/clis/notebooklm/source-fulltext.js +1 -0
  395. package/clis/notebooklm/source-get.js +1 -0
  396. package/clis/notebooklm/source-guide.js +1 -0
  397. package/clis/notebooklm/source-list.js +1 -0
  398. package/clis/notebooklm/status.js +1 -0
  399. package/clis/notebooklm/summary.js +1 -0
  400. package/clis/notion/export.js +1 -0
  401. package/clis/notion/favorites.js +1 -0
  402. package/clis/notion/new.js +1 -0
  403. package/clis/notion/read.js +1 -0
  404. package/clis/notion/search.js +1 -0
  405. package/clis/notion/sidebar.js +1 -0
  406. package/clis/notion/status.js +1 -0
  407. package/clis/notion/write.js +1 -0
  408. package/clis/nowcoder/companies.js +1 -0
  409. package/clis/nowcoder/creators.js +1 -0
  410. package/clis/nowcoder/detail.js +1 -0
  411. package/clis/nowcoder/experience.js +1 -0
  412. package/clis/nowcoder/hot.js +1 -0
  413. package/clis/nowcoder/jobs.js +1 -0
  414. package/clis/nowcoder/notifications.js +1 -0
  415. package/clis/nowcoder/papers.js +1 -0
  416. package/clis/nowcoder/practice.js +1 -0
  417. package/clis/nowcoder/recommend.js +1 -0
  418. package/clis/nowcoder/referral.js +1 -0
  419. package/clis/nowcoder/salary.js +1 -0
  420. package/clis/nowcoder/search.js +1 -0
  421. package/clis/nowcoder/suggest.js +1 -0
  422. package/clis/nowcoder/topics.js +1 -0
  423. package/clis/nowcoder/trending.js +1 -0
  424. package/clis/ones/login.js +1 -0
  425. package/clis/ones/logout.js +1 -0
  426. package/clis/ones/me.js +1 -0
  427. package/clis/ones/my-tasks.js +1 -0
  428. package/clis/ones/task.js +1 -0
  429. package/clis/ones/tasks.js +1 -0
  430. package/clis/ones/token-info.js +1 -0
  431. package/clis/ones/worklog.js +1 -0
  432. package/clis/openreview/openreview.test.js +345 -0
  433. package/clis/openreview/paper.js +43 -0
  434. package/clis/openreview/reviews.js +131 -0
  435. package/clis/openreview/search.js +46 -0
  436. package/clis/openreview/utils.js +158 -0
  437. package/clis/openreview/venue.js +63 -0
  438. package/clis/paperreview/feedback.js +1 -0
  439. package/clis/paperreview/review.js +1 -0
  440. package/clis/paperreview/submit.js +1 -0
  441. package/clis/pixiv/detail.js +1 -0
  442. package/clis/pixiv/download.js +1 -0
  443. package/clis/pixiv/illusts.js +2 -1
  444. package/clis/pixiv/ranking.js +2 -1
  445. package/clis/pixiv/search.js +2 -1
  446. package/clis/pixiv/user.js +2 -0
  447. package/clis/powerchina/search.js +1 -0
  448. package/clis/producthunt/browse.js +1 -0
  449. package/clis/producthunt/hot.js +1 -0
  450. package/clis/producthunt/posts.js +1 -0
  451. package/clis/producthunt/today.js +1 -0
  452. package/clis/quark/ls.js +1 -0
  453. package/clis/quark/mkdir.js +1 -0
  454. package/clis/quark/mv.js +1 -0
  455. package/clis/quark/rename.js +1 -0
  456. package/clis/quark/rm.js +1 -0
  457. package/clis/quark/save.js +1 -0
  458. package/clis/quark/share-tree.js +1 -0
  459. package/clis/reddit/comment.js +1 -0
  460. package/clis/reddit/frontpage.js +1 -0
  461. package/clis/reddit/hot.js +6 -1
  462. package/clis/reddit/hot.test.js +18 -0
  463. package/clis/reddit/popular.js +1 -0
  464. package/clis/reddit/read.js +1 -0
  465. package/clis/reddit/save.js +1 -0
  466. package/clis/reddit/saved.js +1 -0
  467. package/clis/reddit/search.js +1 -0
  468. package/clis/reddit/subreddit.js +1 -0
  469. package/clis/reddit/subscribe.js +1 -0
  470. package/clis/reddit/upvote.js +1 -0
  471. package/clis/reddit/upvoted.js +1 -0
  472. package/clis/reddit/user-comments.js +1 -0
  473. package/clis/reddit/user-posts.js +1 -0
  474. package/clis/reddit/user.js +1 -0
  475. package/clis/reuters/search.js +1 -0
  476. package/clis/sinablog/article.js +1 -0
  477. package/clis/sinablog/hot.js +1 -0
  478. package/clis/sinablog/search.js +1 -0
  479. package/clis/sinablog/user.js +1 -0
  480. package/clis/sinafinance/news.js +1 -0
  481. package/clis/sinafinance/rolling-news.js +1 -0
  482. package/clis/sinafinance/stock-rank.js +1 -0
  483. package/clis/sinafinance/stock.js +1 -0
  484. package/clis/smzdm/search.js +1 -0
  485. package/clis/spotify/spotify.js +11 -0
  486. package/clis/stackoverflow/bounties.js +11 -3
  487. package/clis/stackoverflow/hot.js +10 -2
  488. package/clis/stackoverflow/read.js +314 -0
  489. package/clis/stackoverflow/search.js +10 -2
  490. package/clis/stackoverflow/stackoverflow.test.js +346 -0
  491. package/clis/stackoverflow/unanswered.js +9 -2
  492. package/clis/steam/top-sellers.js +1 -0
  493. package/clis/substack/feed.js +1 -0
  494. package/clis/substack/publication.js +1 -0
  495. package/clis/substack/search.js +1 -0
  496. package/clis/taobao/add-cart.js +1 -0
  497. package/clis/taobao/cart.js +1 -0
  498. package/clis/taobao/detail.js +1 -0
  499. package/clis/taobao/reviews.js +1 -0
  500. package/clis/taobao/search.js +1 -0
  501. package/clis/tdx/hot-rank.js +1 -0
  502. package/clis/ths/hot-rank.js +1 -0
  503. package/clis/tieba/hot.js +2 -1
  504. package/clis/tieba/posts.js +1 -0
  505. package/clis/tieba/read.js +1 -0
  506. package/clis/tieba/search.js +2 -1
  507. package/clis/tiktok/comment.js +1 -0
  508. package/clis/tiktok/explore.js +1 -0
  509. package/clis/tiktok/follow.js +1 -0
  510. package/clis/tiktok/following.js +1 -0
  511. package/clis/tiktok/friends.js +1 -0
  512. package/clis/tiktok/like.js +1 -0
  513. package/clis/tiktok/live.js +1 -0
  514. package/clis/tiktok/notifications.js +1 -0
  515. package/clis/tiktok/profile.js +1 -0
  516. package/clis/tiktok/save.js +1 -0
  517. package/clis/tiktok/search.js +1 -0
  518. package/clis/tiktok/unfollow.js +1 -0
  519. package/clis/tiktok/unlike.js +1 -0
  520. package/clis/tiktok/unsave.js +1 -0
  521. package/clis/tiktok/user.js +1 -0
  522. package/clis/toutiao/articles.js +1 -0
  523. package/clis/twitter/accept.js +1 -0
  524. package/clis/twitter/article.js +1 -0
  525. package/clis/twitter/block.js +1 -0
  526. package/clis/twitter/bookmark.js +1 -0
  527. package/clis/twitter/bookmarks.js +2 -1
  528. package/clis/twitter/delete.js +1 -0
  529. package/clis/twitter/download.js +1 -0
  530. package/clis/twitter/follow.js +1 -0
  531. package/clis/twitter/followers.js +1 -0
  532. package/clis/twitter/following.js +1 -0
  533. package/clis/twitter/hide-reply.js +1 -0
  534. package/clis/twitter/like.js +1 -0
  535. package/clis/twitter/likes.js +2 -1
  536. package/clis/twitter/list-add.js +1 -0
  537. package/clis/twitter/list-remove.js +1 -0
  538. package/clis/twitter/list-tweets.js +1 -0
  539. package/clis/twitter/lists.js +1 -0
  540. package/clis/twitter/notifications.js +1 -0
  541. package/clis/twitter/post.js +1 -0
  542. package/clis/twitter/profile.js +1 -0
  543. package/clis/twitter/reply-dm.js +1 -0
  544. package/clis/twitter/reply.js +1 -0
  545. package/clis/twitter/search.js +1 -0
  546. package/clis/twitter/thread.js +1 -0
  547. package/clis/twitter/timeline.js +1 -0
  548. package/clis/twitter/trending.js +11 -12
  549. package/clis/twitter/trending.test.js +15 -0
  550. package/clis/twitter/tweets.js +2 -1
  551. package/clis/twitter/tweets.test.js +2 -2
  552. package/clis/twitter/unblock.js +1 -0
  553. package/clis/twitter/unbookmark.js +1 -0
  554. package/clis/twitter/unfollow.js +1 -0
  555. package/clis/uiverse/code.js +1 -0
  556. package/clis/uiverse/preview.js +1 -0
  557. package/clis/v2ex/daily.js +1 -0
  558. package/clis/v2ex/hot.js +1 -0
  559. package/clis/v2ex/latest.js +1 -0
  560. package/clis/v2ex/me.js +1 -0
  561. package/clis/v2ex/member.js +1 -0
  562. package/clis/v2ex/node.js +1 -0
  563. package/clis/v2ex/nodes.js +1 -0
  564. package/clis/v2ex/notifications.js +1 -0
  565. package/clis/v2ex/replies.js +1 -0
  566. package/clis/v2ex/topic.js +1 -0
  567. package/clis/v2ex/user.js +1 -0
  568. package/clis/wanfang/search.js +1 -0
  569. package/clis/web/read.js +1 -0
  570. package/clis/weibo/comments.js +1 -0
  571. package/clis/weibo/favorites.js +1 -0
  572. package/clis/weibo/feed.js +3 -1
  573. package/clis/weibo/hot.js +1 -0
  574. package/clis/weibo/me.js +1 -0
  575. package/clis/weibo/post.js +1 -0
  576. package/clis/weibo/publish.js +1 -0
  577. package/clis/weibo/search.js +8 -2
  578. package/clis/weibo/user.js +1 -0
  579. package/clis/weixin/create-draft.js +1 -0
  580. package/clis/weixin/download.js +1 -0
  581. package/clis/weixin/drafts.js +1 -0
  582. package/clis/weread/ai-outline.js +1 -0
  583. package/clis/weread/book.js +1 -0
  584. package/clis/weread/highlights.js +1 -0
  585. package/clis/weread/notebooks.js +1 -0
  586. package/clis/weread/notes.js +1 -0
  587. package/clis/weread/ranking.js +1 -0
  588. package/clis/weread/search.js +1 -0
  589. package/clis/weread/shelf.js +1 -0
  590. package/clis/wikipedia/random.js +1 -0
  591. package/clis/wikipedia/search.js +1 -0
  592. package/clis/wikipedia/summary.js +1 -0
  593. package/clis/wikipedia/trending.js +1 -0
  594. package/clis/xianyu/chat.js +1 -0
  595. package/clis/xianyu/item.js +1 -0
  596. package/clis/xianyu/search.js +1 -0
  597. package/clis/xiaoe/catalog.js +2 -1
  598. package/clis/xiaoe/content.js +1 -0
  599. package/clis/xiaoe/courses.js +1 -0
  600. package/clis/xiaoe/detail.js +1 -0
  601. package/clis/xiaoe/play-url.js +1 -0
  602. package/clis/xiaohongshu/comments.js +1 -0
  603. package/clis/xiaohongshu/creator-note-detail.js +1 -0
  604. package/clis/xiaohongshu/creator-notes-summary.js +1 -0
  605. package/clis/xiaohongshu/creator-notes.js +1 -0
  606. package/clis/xiaohongshu/creator-profile.js +1 -0
  607. package/clis/xiaohongshu/creator-stats.js +1 -0
  608. package/clis/xiaohongshu/download.js +1 -0
  609. package/clis/xiaohongshu/feed.js +2 -1
  610. package/clis/xiaohongshu/note.js +1 -0
  611. package/clis/xiaohongshu/notifications.js +1 -0
  612. package/clis/xiaohongshu/publish.js +1 -0
  613. package/clis/xiaohongshu/search.js +1 -0
  614. package/clis/xiaohongshu/user.js +1 -0
  615. package/clis/xiaoyuzhou/download.js +1 -0
  616. package/clis/xiaoyuzhou/episode.js +1 -0
  617. package/clis/xiaoyuzhou/podcast-episodes.js +1 -0
  618. package/clis/xiaoyuzhou/podcast.js +1 -0
  619. package/clis/xiaoyuzhou/transcript.js +1 -0
  620. package/clis/xueqiu/comments.js +1 -0
  621. package/clis/xueqiu/earnings-date.js +1 -0
  622. package/clis/xueqiu/feed.js +1 -0
  623. package/clis/xueqiu/fund-holdings.js +1 -0
  624. package/clis/xueqiu/fund-snapshot.js +1 -0
  625. package/clis/xueqiu/groups.js +1 -0
  626. package/clis/xueqiu/hot-stock.js +1 -0
  627. package/clis/xueqiu/hot.js +1 -0
  628. package/clis/xueqiu/kline.js +1 -0
  629. package/clis/xueqiu/search.js +1 -0
  630. package/clis/xueqiu/stock.js +1 -0
  631. package/clis/xueqiu/watchlist.js +1 -0
  632. package/clis/yahoo-finance/quote.js +1 -0
  633. package/clis/yollomi/background.js +1 -0
  634. package/clis/yollomi/edit.js +1 -0
  635. package/clis/yollomi/face-swap.js +1 -0
  636. package/clis/yollomi/generate.js +1 -0
  637. package/clis/yollomi/models.js +1 -0
  638. package/clis/yollomi/object-remover.js +1 -0
  639. package/clis/yollomi/remove-bg.js +1 -0
  640. package/clis/yollomi/restore.js +1 -0
  641. package/clis/yollomi/try-on.js +1 -0
  642. package/clis/yollomi/upload.js +1 -0
  643. package/clis/yollomi/upscale.js +1 -0
  644. package/clis/yollomi/video.js +1 -0
  645. package/clis/youtube/channel.js +1 -0
  646. package/clis/youtube/comments.js +1 -0
  647. package/clis/youtube/feed.js +8 -7
  648. package/clis/youtube/feed.test.js +131 -0
  649. package/clis/youtube/history.js +1 -0
  650. package/clis/youtube/like.js +1 -0
  651. package/clis/youtube/playlist.js +1 -0
  652. package/clis/youtube/search.js +1 -0
  653. package/clis/youtube/subscribe.js +1 -0
  654. package/clis/youtube/subscriptions.js +1 -0
  655. package/clis/youtube/transcript.js +1 -0
  656. package/clis/youtube/unlike.js +1 -0
  657. package/clis/youtube/unsubscribe.js +1 -0
  658. package/clis/youtube/video.js +1 -0
  659. package/clis/youtube/watch-later.js +1 -0
  660. package/clis/yuanbao/ask.js +1 -0
  661. package/clis/yuanbao/new.js +1 -0
  662. package/clis/zhihu/answer.js +1 -0
  663. package/clis/zhihu/collection.js +1 -0
  664. package/clis/zhihu/collections.js +1 -0
  665. package/clis/zhihu/comment.js +1 -0
  666. package/clis/zhihu/download.js +1 -0
  667. package/clis/zhihu/favorite.js +1 -0
  668. package/clis/zhihu/follow.js +1 -0
  669. package/clis/zhihu/hot.js +1 -0
  670. package/clis/zhihu/like.js +1 -0
  671. package/clis/zhihu/question.js +1 -0
  672. package/clis/zhihu/search.js +1 -0
  673. package/clis/zlibrary/info.js +1 -0
  674. package/clis/zlibrary/search.js +1 -0
  675. package/clis/zsxq/dynamics.js +1 -0
  676. package/clis/zsxq/groups.js +1 -0
  677. package/clis/zsxq/search.js +1 -0
  678. package/clis/zsxq/topic.js +1 -0
  679. package/clis/zsxq/topics.js +1 -0
  680. package/dist/src/adapter-source.test.js +1 -1
  681. package/dist/src/browser/analyze.test.js +2 -0
  682. package/dist/src/browser/base-page.d.ts +9 -0
  683. package/dist/src/browser/base-page.js +72 -3
  684. package/dist/src/browser/base-page.test.js +42 -0
  685. package/dist/src/browser/cdp.js +6 -0
  686. package/dist/src/browser/cdp.test.js +3 -0
  687. package/dist/src/browser/page.d.ts +1 -0
  688. package/dist/src/browser/page.js +6 -0
  689. package/dist/src/browser/page.test.js +17 -0
  690. package/dist/src/browser/target-resolver.d.ts +10 -3
  691. package/dist/src/browser/target-resolver.js +16 -10
  692. package/dist/src/browser/verify-fixture.d.ts +6 -1
  693. package/dist/src/browser/verify-fixture.js +87 -0
  694. package/dist/src/browser/verify-fixture.test.js +44 -1
  695. package/dist/src/build-manifest.js +3 -0
  696. package/dist/src/build-manifest.test.js +18 -12
  697. package/dist/src/capabilityRouting.test.js +1 -1
  698. package/dist/src/cli.js +141 -5
  699. package/dist/src/cli.test.js +179 -0
  700. package/dist/src/commanderAdapter.d.ts +1 -1
  701. package/dist/src/commanderAdapter.js +17 -7
  702. package/dist/src/commanderAdapter.test.js +7 -6
  703. package/dist/src/convention-audit.d.ts +50 -0
  704. package/dist/src/convention-audit.js +546 -0
  705. package/dist/src/convention-audit.test.d.ts +1 -0
  706. package/dist/src/convention-audit.test.js +226 -0
  707. package/dist/src/discovery.js +2 -0
  708. package/dist/src/doctor.js +0 -1
  709. package/dist/src/doctor.test.js +1 -1
  710. package/dist/src/engine.test.js +10 -10
  711. package/dist/src/execution.js +1 -1
  712. package/dist/src/execution.test.js +9 -9
  713. package/dist/src/help.d.ts +15 -0
  714. package/dist/src/help.js +149 -0
  715. package/dist/src/main.js +13 -7
  716. package/dist/src/manifest-types.d.ts +2 -0
  717. package/dist/src/plugin.test.js +26 -26
  718. package/dist/src/registry.d.ts +5 -0
  719. package/dist/src/registry.js +8 -0
  720. package/dist/src/registry.test.js +27 -20
  721. package/dist/src/serialization.d.ts +4 -0
  722. package/dist/src/serialization.js +27 -1
  723. package/dist/src/serialization.test.js +24 -2
  724. package/dist/src/types.d.ts +2 -0
  725. package/package.json +4 -1
  726. package/scripts/check-listing-id-pairing.mjs +193 -0
  727. package/scripts/check-silent-column-drop.mjs +105 -0
  728. package/scripts/check-typed-error-lint.mjs +118 -0
  729. package/scripts/silent-column-drop-baseline.json +962 -0
  730. package/scripts/typed-error-lint-baseline.json +1586 -0
@@ -0,0 +1,226 @@
1
+ import { afterEach, describe, expect, it } from 'vitest';
2
+ import * as fs from 'node:fs';
3
+ import * as os from 'node:os';
4
+ import * as path from 'node:path';
5
+ import { renderConventionAuditText, runConventionAudit } from './convention-audit.js';
6
+ describe('convention audit', () => {
7
+ const tempDirs = [];
8
+ afterEach(() => {
9
+ for (const dir of tempDirs.splice(0)) {
10
+ fs.rmSync(dir, { recursive: true, force: true });
11
+ }
12
+ });
13
+ function makeProject(manifest, files) {
14
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-convention-audit-'));
15
+ tempDirs.push(root);
16
+ fs.mkdirSync(path.join(root, 'clis'), { recursive: true });
17
+ fs.writeFileSync(path.join(root, 'cli-manifest.json'), JSON.stringify(manifest, null, 2));
18
+ for (const [relative, content] of Object.entries(files)) {
19
+ const file = path.join(root, 'clis', relative);
20
+ fs.mkdirSync(path.dirname(file), { recursive: true });
21
+ fs.writeFileSync(file, content);
22
+ }
23
+ return root;
24
+ }
25
+ it('reports column, metadata, typed-error, and write-pair violations', () => {
26
+ const root = makeProject([
27
+ {
28
+ site: 'demo',
29
+ name: 'search',
30
+ access: 'read',
31
+ columns: ['id', 'title', 'authorName'],
32
+ sourceFile: 'demo/search.js',
33
+ },
34
+ {
35
+ site: 'demo',
36
+ name: 'like',
37
+ access: 'write',
38
+ sourceFile: 'demo/like.js',
39
+ },
40
+ {
41
+ site: 'demo',
42
+ name: 'missing',
43
+ columns: [],
44
+ sourceFile: 'demo/missing.js',
45
+ },
46
+ {
47
+ site: 'clean',
48
+ name: 'search',
49
+ access: 'read',
50
+ columns: ['id', 'title'],
51
+ sourceFile: 'clean/search.js',
52
+ },
53
+ ], {
54
+ 'demo/search.js': `
55
+ export async function run(kwargs) {
56
+ const limit = Math.min(kwargs.limit, 100);
57
+ try {
58
+ await fetch('/items');
59
+ } catch {
60
+ return [];
61
+ }
62
+ rows.push({
63
+ id: item.id,
64
+ title: item.title ?? 'unknown',
65
+ url: item.url,
66
+ authorName: item.authorName,
67
+ });
68
+ return limit;
69
+ }
70
+ `,
71
+ 'demo/like.js': 'export async function run() { return { ok: true }; }',
72
+ 'demo/missing.js': 'export async function run() { return { ok: true }; }',
73
+ 'clean/search.js': 'export async function run() { rows.push({ id: item.id, title: item.title }); }',
74
+ });
75
+ const report = runConventionAudit({ projectRoot: root });
76
+ const category = (rule) => report.categories.find((item) => item.rule === rule);
77
+ expect(report.ok).toBe(false);
78
+ expect(category('silent-column-drop').violations[0]).toMatchObject({
79
+ command: 'demo/search',
80
+ details: expect.objectContaining({ missing: ['url'] }),
81
+ });
82
+ expect(category('camelCase-in-columns').violations[0]).toMatchObject({
83
+ command: 'demo/search',
84
+ details: { column: 'authorName' },
85
+ });
86
+ expect(category('missing-access-metadata').violations[0]).toMatchObject({ command: 'demo/missing' });
87
+ expect(category('silent-clamp').violations[0]).toMatchObject({ command: 'demo/search' });
88
+ expect(category('silent-empty-fallback').violations[0]).toMatchObject({ command: 'demo/search' });
89
+ expect(category('silent-sentinel').violations[0]).toMatchObject({ command: 'demo/search' });
90
+ expect(category('write-without-delete-pair').violations[0]).toMatchObject({
91
+ command: 'demo/like',
92
+ details: { expected_any_of: ['unlike'] },
93
+ });
94
+ });
95
+ it('supports site and command target filters', () => {
96
+ const root = makeProject([
97
+ { site: 'demo', name: 'search', access: 'read', columns: ['id'], sourceFile: 'demo/search.js' },
98
+ { site: 'other', name: 'search', access: 'read', columns: ['id'], sourceFile: 'other/search.js' },
99
+ ], {
100
+ 'demo/search.js': 'export async function run() { rows.push({ id: 1, hidden: true }); }',
101
+ 'other/search.js': 'export async function run() { rows.push({ id: 1, hidden: true }); }',
102
+ });
103
+ expect(runConventionAudit({ projectRoot: root, site: 'demo' }).summary.commands).toBe(1);
104
+ expect(runConventionAudit({ projectRoot: root, target: 'other/search' }).summary.commands).toBe(1);
105
+ expect(runConventionAudit({ projectRoot: root, target: 'missing' }).summary.commands).toBe(0);
106
+ });
107
+ it('renders a compact text report', () => {
108
+ const root = makeProject([
109
+ { site: 'demo', name: 'search', access: 'read', columns: ['id'], sourceFile: 'demo/search.js' },
110
+ ], {
111
+ 'demo/search.js': 'export async function run() { rows.push({ id: 1 }); }',
112
+ });
113
+ const text = renderConventionAuditText(runConventionAudit({ projectRoot: root }));
114
+ expect(text).toContain('Convention Audit Report');
115
+ expect(text).toContain('OK - no convention violations found.');
116
+ });
117
+ it('scans pipeline map blocks for silent column drops', () => {
118
+ const root = makeProject([
119
+ { site: 'demo', name: 'feed', access: 'read', columns: ['id', 'title'], sourceFile: 'demo/feed.js' },
120
+ ], {
121
+ 'demo/feed.js': `
122
+ cli({
123
+ site: 'demo',
124
+ name: 'feed',
125
+ access: 'read',
126
+ columns: ['id', 'title'],
127
+ pipeline: [
128
+ { map: {
129
+ id: '\${{ item.id }}',
130
+ title: '\${{ item.title }}',
131
+ url: '\${{ item.url }}',
132
+ } },
133
+ ],
134
+ });
135
+ `,
136
+ });
137
+ const report = runConventionAudit({ projectRoot: root });
138
+ const violations = report.categories.find((item) => item.rule === 'silent-column-drop').violations;
139
+ expect(violations[0]).toMatchObject({
140
+ command: 'demo/feed',
141
+ details: expect.objectContaining({ missing: ['url'] }),
142
+ });
143
+ });
144
+ it('ignores ok:false diagnostic objects when checking emitted rows', () => {
145
+ const root = makeProject([
146
+ { site: 'demo', name: 'search', access: 'read', columns: ['id', 'url'], sourceFile: 'demo/search.js' },
147
+ ], {
148
+ 'demo/search.js': `
149
+ export async function run() {
150
+ if (!document.querySelector('.items')) {
151
+ return { ok: false, bodyLen: document.body.innerText.length, sample: document.body.innerText.slice(0, 800), url: location.href };
152
+ }
153
+ return rows.map((row) => ({ id: row.id, url: row.url }));
154
+ }
155
+ `,
156
+ });
157
+ const report = runConventionAudit({ projectRoot: root });
158
+ const violations = report.categories.find((item) => item.rule === 'silent-column-drop').violations;
159
+ expect(violations).toEqual([]);
160
+ });
161
+ it('ignores raw intermediate keys when a final mapper converts them into columns', () => {
162
+ const root = makeProject([
163
+ {
164
+ site: 'demo',
165
+ name: 'search',
166
+ access: 'read',
167
+ columns: ['id', 'rating', 'reviews', 'price'],
168
+ sourceFile: 'demo/search.js',
169
+ },
170
+ ], {
171
+ 'demo/search.js': `
172
+ export async function run() {
173
+ rows.push({
174
+ id: item.id,
175
+ starClass: item.starClass,
176
+ reviewsRaw: item.reviewsRaw,
177
+ priceRaw: item.priceRaw,
178
+ });
179
+ return rows.map((r) => ({
180
+ id: r.id,
181
+ rating: r.starClass ? Number(r.starClass) / 10 : null,
182
+ reviews: parseReviewCount(r.reviewsRaw),
183
+ price: parsePrice(r.priceRaw),
184
+ }));
185
+ }
186
+ `,
187
+ });
188
+ const report = runConventionAudit({ projectRoot: root });
189
+ const violations = report.categories.find((item) => item.rule === 'silent-column-drop').violations;
190
+ expect(violations).toEqual([]);
191
+ });
192
+ it('still reports raw keys emitted directly without a final mapper', () => {
193
+ const root = makeProject([
194
+ { site: 'demo', name: 'search', access: 'read', columns: ['id', 'title'], sourceFile: 'demo/search.js' },
195
+ ], {
196
+ 'demo/search.js': `
197
+ export async function run() {
198
+ rows.push({
199
+ id: item.id,
200
+ title: titleRaw,
201
+ titleRaw,
202
+ });
203
+ return rows;
204
+ }
205
+ `,
206
+ });
207
+ const report = runConventionAudit({ projectRoot: root });
208
+ const violations = report.categories.find((item) => item.rule === 'silent-column-drop').violations;
209
+ expect(violations[0]).toMatchObject({
210
+ command: 'demo/search',
211
+ details: expect.objectContaining({ missing: ['titleRaw'] }),
212
+ });
213
+ });
214
+ it('only reports empty array fallbacks inside catch blocks', () => {
215
+ const root = makeProject([
216
+ { site: 'demo', name: 'guard', access: 'read', columns: ['id'], sourceFile: 'demo/guard.js' },
217
+ { site: 'demo', name: 'catch', access: 'read', columns: ['id'], sourceFile: 'demo/catch.js' },
218
+ ], {
219
+ 'demo/guard.js': 'export async function run() { if (!store) return []; rows.push({ id: 1 }); }',
220
+ 'demo/catch.js': 'export async function run() { try { await fetch("/"); } catch (err) { return []; } rows.push({ id: 1 }); }',
221
+ });
222
+ const report = runConventionAudit({ projectRoot: root });
223
+ const violations = report.categories.find((item) => item.rule === 'silent-empty-fallback').violations;
224
+ expect(violations.map((violation) => violation.command)).toEqual(['demo/catch']);
225
+ });
226
+ });
@@ -124,6 +124,8 @@ async function loadFromManifest(manifestPath, clisDir) {
124
124
  name: entry.name,
125
125
  aliases: entry.aliases,
126
126
  description: entry.description ?? '',
127
+ access: entry.access,
128
+ example: entry.example,
127
129
  domain: entry.domain,
128
130
  strategy: parseStrategy(entry.strategy),
129
131
  browser: entry.browser,
@@ -285,7 +285,6 @@ export function renderBrowserDoctorReport(report) {
285
285
  }
286
286
  else if (report.daemonRunning && report.extensionConnected) {
287
287
  lines.push('', styleText('green', 'Everything looks good!'));
288
- lines.push(styleText('dim', 'Tip: writing a new adapter? Run `opencli browser analyze <url>` for one-shot site recon.'));
289
288
  }
290
289
  return lines.join('\n');
291
290
  }
@@ -43,7 +43,7 @@ describe('doctor report rendering', () => {
43
43
  expect(text).toContain('(v1.7.9)');
44
44
  expect(text).toContain('[OK] Extension: connected (v1.6.8)');
45
45
  expect(text).toContain('Everything looks good!');
46
- expect(text).toContain('opencli browser analyze <url>');
46
+ expect(text).not.toContain('opencli browser analyze <url>');
47
47
  });
48
48
  it('renders a warning when daemon version is stale', () => {
49
49
  const text = strip(renderBrowserDoctorReport({
@@ -27,7 +27,7 @@ export const helper = true;
27
27
  import { cli, Strategy } from '${pathToFileURL(path.join(process.cwd(), 'src', 'registry.ts')).href}';
28
28
  cli({
29
29
  site: 'temp-site',
30
- name: 'hello',
30
+ name: 'hello', access: 'read',
31
31
  description: 'hello command',
32
32
  strategy: Strategy.PUBLIC,
33
33
  browser: false,
@@ -57,7 +57,7 @@ cli({
57
57
  import { cli, Strategy } from '${pathToFileURL(path.join(process.cwd(), 'src', 'registry.ts')).href}';
58
58
  cli({
59
59
  site: 'fallback-site',
60
- name: 'hello',
60
+ name: 'hello', access: 'read',
61
61
  description: 'hello command',
62
62
  strategy: Strategy.PUBLIC,
63
63
  browser: false,
@@ -86,7 +86,7 @@ import { htmlToMarkdown } from '@jackwener/opencli/utils';
86
86
 
87
87
  cli({
88
88
  site: 'legacy-site',
89
- name: 'hello',
89
+ name: 'hello', access: 'read',
90
90
  description: 'hello command',
91
91
  strategy: Strategy.PUBLIC,
92
92
  browser: false,
@@ -204,7 +204,7 @@ describe('executeCommand', () => {
204
204
  it('accepts kebab-case option names after Commander camelCases them', async () => {
205
205
  const cmd = cli({
206
206
  site: 'test-engine',
207
- name: 'kebab-arg-test',
207
+ name: 'kebab-arg-test', access: 'read',
208
208
  description: 'test command with kebab-case arg',
209
209
  browser: false,
210
210
  strategy: Strategy.PUBLIC,
@@ -219,7 +219,7 @@ describe('executeCommand', () => {
219
219
  it('executes a command with func', async () => {
220
220
  const cmd = cli({
221
221
  site: 'test-engine',
222
- name: 'func-test',
222
+ name: 'func-test', access: 'read',
223
223
  description: 'test command with func',
224
224
  browser: false,
225
225
  strategy: Strategy.PUBLIC,
@@ -233,7 +233,7 @@ describe('executeCommand', () => {
233
233
  it('executes a command with pipeline', async () => {
234
234
  const cmd = cli({
235
235
  site: 'test-engine',
236
- name: 'pipe-test',
236
+ name: 'pipe-test', access: 'read',
237
237
  description: 'test command with pipeline',
238
238
  browser: false,
239
239
  strategy: Strategy.PUBLIC,
@@ -248,7 +248,7 @@ describe('executeCommand', () => {
248
248
  it('throws for command with no func or pipeline', async () => {
249
249
  const cmd = cli({
250
250
  site: 'test-engine',
251
- name: 'empty-test',
251
+ name: 'empty-test', access: 'read',
252
252
  description: 'empty command',
253
253
  browser: false,
254
254
  });
@@ -258,7 +258,7 @@ describe('executeCommand', () => {
258
258
  let receivedDebug = false;
259
259
  const cmd = cli({
260
260
  site: 'test-engine',
261
- name: 'debug-test',
261
+ name: 'debug-test', access: 'read',
262
262
  description: 'debug test',
263
263
  browser: false,
264
264
  func: async (_kwargs, debug) => {
@@ -276,7 +276,7 @@ describe('executeCommand', () => {
276
276
  });
277
277
  const cmd = cli({
278
278
  site: 'test-engine',
279
- name: 'failing-test',
279
+ name: 'failing-test', access: 'read',
280
280
  description: 'failing command',
281
281
  browser: false,
282
282
  strategy: Strategy.PUBLIC,
@@ -297,7 +297,7 @@ describe('executeCommand', () => {
297
297
  .mockResolvedValue('http://127.0.0.1:9228');
298
298
  const cmd = cli({
299
299
  site: 'chatwise',
300
- name: 'status',
300
+ name: 'status', access: 'read',
301
301
  description: 'chatwise status',
302
302
  browser: true,
303
303
  strategy: Strategy.PUBLIC,
@@ -301,7 +301,7 @@ export async function executeCommand(cmd, rawKwargs, debug = false, opts = {}) {
301
301
  await page.closeWindow?.().catch(() => { });
302
302
  throw err;
303
303
  }
304
- }, { workspace: `site:${cmd.site}`, cdpEndpoint, contextId });
304
+ }, { workspace: `site:${cmd.site}:${crypto.randomUUID()}`, cdpEndpoint, contextId });
305
305
  }
306
306
  else {
307
307
  // Non-browser commands: apply timeout only when explicitly configured.
@@ -12,7 +12,7 @@ describe('executeCommand — non-browser timeout', () => {
12
12
  it('applies timeoutSeconds to non-browser commands', async () => {
13
13
  const cmd = cli({
14
14
  site: 'test-execution',
15
- name: 'non-browser-timeout',
15
+ name: 'non-browser-timeout', access: 'read',
16
16
  description: 'test non-browser timeout',
17
17
  browser: false,
18
18
  strategy: Strategy.PUBLIC,
@@ -32,7 +32,7 @@ describe('executeCommand — non-browser timeout', () => {
32
32
  it('skips timeout when timeoutSeconds is 0', async () => {
33
33
  const cmd = cli({
34
34
  site: 'test-execution',
35
- name: 'non-browser-zero-timeout',
35
+ name: 'non-browser-zero-timeout', access: 'read',
36
36
  description: 'test zero timeout bypasses wrapping',
37
37
  browser: false,
38
38
  strategy: Strategy.PUBLIC,
@@ -53,7 +53,7 @@ describe('executeCommand — non-browser timeout', () => {
53
53
  });
54
54
  const cmd = cli({
55
55
  site: 'test-execution',
56
- name: 'browser-close-on-error',
56
+ name: 'browser-close-on-error', access: 'read',
57
57
  description: 'test closeWindow on failure',
58
58
  browser: true,
59
59
  strategy: Strategy.PUBLIC,
@@ -73,7 +73,7 @@ describe('executeCommand — non-browser timeout', () => {
73
73
  try {
74
74
  const cmd = cli({
75
75
  site: 'test-execution',
76
- name: 'browser-live-success',
76
+ name: 'browser-live-success', access: 'read',
77
77
  description: 'test closeWindow skipped with --live on success',
78
78
  browser: true,
79
79
  strategy: Strategy.PUBLIC,
@@ -100,7 +100,7 @@ describe('executeCommand — non-browser timeout', () => {
100
100
  try {
101
101
  const cmd = cli({
102
102
  site: 'test-execution',
103
- name: 'browser-live-failure',
103
+ name: 'browser-live-failure', access: 'read',
104
104
  description: 'test closeWindow skipped with --live on failure',
105
105
  browser: true,
106
106
  strategy: Strategy.PUBLIC,
@@ -121,7 +121,7 @@ describe('executeCommand — non-browser timeout', () => {
121
121
  const validateArgs = vi.fn();
122
122
  const cmd = {
123
123
  site: 'test-execution',
124
- name: 'prepared-validation',
124
+ name: 'prepared-validation', access: 'read',
125
125
  description: 'test prepared validation path',
126
126
  browser: false,
127
127
  strategy: Strategy.PUBLIC,
@@ -164,7 +164,7 @@ describe('executeCommand — non-browser timeout', () => {
164
164
  try {
165
165
  const cmd = cli({
166
166
  site: 'test-execution',
167
- name: 'browser-trace-failure',
167
+ name: 'browser-trace-failure', access: 'read',
168
168
  description: 'test trace export',
169
169
  browser: true,
170
170
  strategy: Strategy.PUBLIC,
@@ -224,7 +224,7 @@ describe('executeCommand — non-browser timeout', () => {
224
224
  try {
225
225
  const cmd = cli({
226
226
  site: 'test-execution',
227
- name: 'browser-trace-success',
227
+ name: 'browser-trace-success', access: 'read',
228
228
  description: 'test trace export on success',
229
229
  browser: true,
230
230
  strategy: Strategy.PUBLIC,
@@ -281,7 +281,7 @@ describe('executeCommand — non-browser timeout', () => {
281
281
  try {
282
282
  const cmd = cli({
283
283
  site: 'test-execution',
284
- name: 'browser-trace-export-fails',
284
+ name: 'browser-trace-export-fails', access: 'read',
285
285
  description: 'test trace export failure handling',
286
286
  browser: true,
287
287
  strategy: Strategy.PUBLIC,
@@ -0,0 +1,15 @@
1
+ import { Command } from 'commander';
2
+ import type { CliCommand } from './registry.js';
3
+ export type StructuredHelpFormat = 'yaml' | 'json';
4
+ export declare function getRequestedHelpFormat(argv?: readonly string[]): StructuredHelpFormat | undefined;
5
+ export declare function renderStructuredHelp(data: unknown, format: StructuredHelpFormat): string;
6
+ export declare function wrapCommaList(items: readonly string[], opts?: {
7
+ width?: number;
8
+ indent?: string;
9
+ }): string;
10
+ export declare function formatRootAdapterHelpText(siteNames: readonly string[]): string;
11
+ export declare function rootHelpData(program: Command, siteNames: readonly string[]): Record<string, unknown>;
12
+ export declare function siteHelpData(site: string, commands: readonly CliCommand[]): Record<string, unknown>;
13
+ export declare function commandHelpData(cmd: CliCommand): Record<string, unknown>;
14
+ export declare function installStructuredHelp(command: Command, data: () => unknown, textSuffix?: string | (() => string)): void;
15
+ export declare function formatSiteCommandDescription(cmd: CliCommand): string;
@@ -0,0 +1,149 @@
1
+ import yaml from 'js-yaml';
2
+ import { fullName } from './registry.js';
3
+ import { formatCommandExample } from './serialization.js';
4
+ function normalizeStructuredHelpFormat(value) {
5
+ const normalized = value?.toLowerCase();
6
+ if (normalized === 'yaml' || normalized === 'yml')
7
+ return 'yaml';
8
+ if (normalized === 'json')
9
+ return 'json';
10
+ return undefined;
11
+ }
12
+ export function getRequestedHelpFormat(argv = process.argv) {
13
+ for (let i = 0; i < argv.length; i++) {
14
+ const token = argv[i];
15
+ if (token === '-f' || token === '--format') {
16
+ return normalizeStructuredHelpFormat(argv[i + 1]);
17
+ }
18
+ if (token.startsWith('--format=')) {
19
+ return normalizeStructuredHelpFormat(token.slice('--format='.length));
20
+ }
21
+ if (token.startsWith('-f') && token.length > 2) {
22
+ return normalizeStructuredHelpFormat(token.slice(2));
23
+ }
24
+ }
25
+ return undefined;
26
+ }
27
+ export function renderStructuredHelp(data, format) {
28
+ if (format === 'json')
29
+ return `${JSON.stringify(data, null, 2)}\n`;
30
+ return yaml.dump(data, { sortKeys: false, lineWidth: 120, noRefs: true });
31
+ }
32
+ export function wrapCommaList(items, opts = {}) {
33
+ const width = Math.max(opts.width ?? process.stdout.columns ?? 100, 40);
34
+ const indent = opts.indent ?? ' ';
35
+ const sorted = [...items].sort((a, b) => a.localeCompare(b));
36
+ const lines = [];
37
+ let line = indent;
38
+ sorted.forEach((item, index) => {
39
+ const token = `${item}${index < sorted.length - 1 ? ',' : ''}`;
40
+ const prefix = line === indent ? '' : ' ';
41
+ if (line.length + prefix.length + token.length > width && line.trim()) {
42
+ lines.push(line);
43
+ line = `${indent}${token}`;
44
+ }
45
+ else {
46
+ line += `${prefix}${token}`;
47
+ }
48
+ });
49
+ if (line.trim())
50
+ lines.push(line);
51
+ return lines.join('\n');
52
+ }
53
+ export function formatRootAdapterHelpText(siteNames) {
54
+ if (siteNames.length === 0)
55
+ return '';
56
+ return [
57
+ '',
58
+ `Site adapters (${siteNames.length}):`,
59
+ wrapCommaList(siteNames),
60
+ '',
61
+ "Run 'opencli list' for full command details, or 'opencli <site> --help' to inspect one site.",
62
+ "Agent tip: use 'opencli <site> --help -f yaml' for structured commands, args, access, and examples.",
63
+ '',
64
+ ].join('\n');
65
+ }
66
+ function compactArg(arg) {
67
+ return {
68
+ name: arg.name,
69
+ ...(arg.type && arg.type !== 'string' ? { type: arg.type } : {}),
70
+ ...(arg.positional ? { positional: true } : {}),
71
+ ...(arg.required ? { required: true } : {}),
72
+ ...(arg.valueRequired ? { valueRequired: true } : {}),
73
+ ...(arg.default !== undefined ? { default: arg.default } : {}),
74
+ ...(arg.choices?.length ? { choices: arg.choices } : {}),
75
+ ...(arg.help ? { help: arg.help } : {}),
76
+ };
77
+ }
78
+ function compactCommand(cmd, opts = {}) {
79
+ return {
80
+ name: cmd.name,
81
+ command: `opencli ${cmd.site} ${cmd.name}`,
82
+ access: cmd.access,
83
+ description: cmd.description,
84
+ ...(cmd.aliases?.length ? { aliases: cmd.aliases } : {}),
85
+ args: cmd.args.map(compactArg),
86
+ example: formatCommandExample(cmd),
87
+ ...(opts.includeColumns && cmd.columns?.length ? { columns: cmd.columns } : {}),
88
+ ...(cmd.deprecated ? { deprecated: cmd.deprecated } : {}),
89
+ ...(cmd.replacedBy ? { replacedBy: cmd.replacedBy } : {}),
90
+ };
91
+ }
92
+ export function rootHelpData(program, siteNames) {
93
+ const siteSet = new Set(siteNames);
94
+ const commands = program.commands
95
+ .filter(command => !siteSet.has(command.name()))
96
+ .map(command => ({
97
+ name: command.name(),
98
+ description: command.description(),
99
+ }));
100
+ return {
101
+ name: program.name(),
102
+ description: program.description(),
103
+ commands,
104
+ site_adapters: {
105
+ count: siteNames.length,
106
+ sites: [...siteNames].sort((a, b) => a.localeCompare(b)),
107
+ },
108
+ next: [
109
+ 'opencli <site> --help -f yaml',
110
+ 'opencli list -f yaml',
111
+ 'opencli <site> <command> -f yaml',
112
+ ],
113
+ };
114
+ }
115
+ export function siteHelpData(site, commands) {
116
+ const unique = [...new Map(commands.map(cmd => [fullName(cmd), cmd])).values()]
117
+ .sort((a, b) => a.name.localeCompare(b.name));
118
+ return {
119
+ site,
120
+ command_count: unique.length,
121
+ commands: unique.map(cmd => compactCommand(cmd)),
122
+ next: [
123
+ `opencli ${site} <command> --help -f yaml`,
124
+ `opencli ${site} <command> -f yaml`,
125
+ ],
126
+ };
127
+ }
128
+ export function commandHelpData(cmd) {
129
+ return {
130
+ site: cmd.site,
131
+ ...compactCommand(cmd, { includeColumns: true }),
132
+ output_formats: ['table', 'plain', 'yaml', 'json', 'md', 'csv'],
133
+ };
134
+ }
135
+ export function installStructuredHelp(command, data, textSuffix) {
136
+ const original = command.helpInformation.bind(command);
137
+ command.helpInformation = ((contextOptions) => {
138
+ const format = getRequestedHelpFormat();
139
+ if (format)
140
+ return renderStructuredHelp(data(), format);
141
+ const suffix = typeof textSuffix === 'function' ? textSuffix() : textSuffix ?? '';
142
+ return original(contextOptions) + suffix;
143
+ });
144
+ }
145
+ export function formatSiteCommandDescription(cmd) {
146
+ const access = cmd.access === 'write' ? '[write]' : '[read]';
147
+ const deprecatedSuffix = cmd.deprecated ? ' [deprecated]' : '';
148
+ return `${access} ${cmd.description}${deprecatedSuffix}`;
149
+ }
package/dist/src/main.js CHANGED
@@ -118,13 +118,19 @@ installNodeNetwork();
118
118
  // user-CLI discovery MUST run after built-in discovery to preserve the
119
119
  // intended override order (user adapters override built-in ones).
120
120
  // - discoverPlugins runs last: plugins may override both built-in and user CLIs.
121
- const [, ,] = await Promise.all([
122
- ensureUserCliCompatShims(),
123
- ensureUserAdapters(),
124
- discoverClis(BUILTIN_CLIS),
125
- ]);
126
- await discoverClis(USER_CLIS);
127
- await discoverPlugins();
121
+ const skipUserDiscovery = argv[0] === 'convention-audit';
122
+ if (skipUserDiscovery) {
123
+ await discoverClis(BUILTIN_CLIS);
124
+ }
125
+ else {
126
+ const [, ,] = await Promise.all([
127
+ ensureUserCliCompatShims(),
128
+ ensureUserAdapters(),
129
+ discoverClis(BUILTIN_CLIS),
130
+ ]);
131
+ await discoverClis(USER_CLIS);
132
+ await discoverPlugins();
133
+ }
128
134
  // Register exit hook: notice appears after command output (same as npm/gh/yarn)
129
135
  registerUpdateNoticeOnExit();
130
136
  // Kick off background fetch for next run (non-blocking)
@@ -11,6 +11,8 @@ export interface ManifestEntry {
11
11
  name: string;
12
12
  aliases?: string[];
13
13
  description: string;
14
+ access: 'read' | 'write';
15
+ example?: string;
14
16
  domain?: string;
15
17
  strategy: string;
16
18
  browser: boolean;