superacli 1.1.0 → 1.1.2

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 (261) hide show
  1. package/.beads/.br_history/issues.20260308_235202_180577215.jsonl +57 -0
  2. package/.beads/.br_history/issues.20260308_235202_387414163.jsonl +57 -0
  3. package/.beads/.br_history/issues.20260308_235202_564422794.jsonl +57 -0
  4. package/.beads/.br_history/issues.20260308_235202_742600597.jsonl +57 -0
  5. package/.beads/.br_history/issues.20260308_235208_133360069.jsonl +57 -0
  6. package/.beads/.br_history/issues.20260308_235505_473406307.jsonl +57 -0
  7. package/.beads/.br_history/issues.20260308_235505_662360489.jsonl +57 -0
  8. package/.beads/.br_history/issues.20260308_235505_843935624.jsonl +57 -0
  9. package/.beads/.br_history/issues.20260308_235506_044530221.jsonl +57 -0
  10. package/.beads/.br_history/issues.20260309_002618_115728731.jsonl +57 -0
  11. package/.beads/.br_history/issues.20260309_003748_878174586.jsonl +57 -0
  12. package/.beads/.br_history/issues.20260309_004057_868755623.jsonl +57 -0
  13. package/.beads/.br_history/issues.20260309_004058_512842163.jsonl +57 -0
  14. package/.beads/.br_history/issues.20260309_004058_994445226.jsonl +57 -0
  15. package/.beads/.br_history/issues.20260309_004059_475988596.jsonl +57 -0
  16. package/.beads/.br_history/issues.20260309_161902_566857851.jsonl +57 -0
  17. package/.beads/.br_history/issues.20260309_170512_277017739.jsonl +57 -0
  18. package/.beads/.br_history/issues.20260309_170512_477876921.jsonl +57 -0
  19. package/.beads/.br_history/issues.20260309_170512_664382701.jsonl +57 -0
  20. package/.beads/.br_history/issues.20260309_170512_859400333.jsonl +57 -0
  21. package/.beads/.br_history/issues.20260309_212326_082771164.jsonl +57 -0
  22. package/.beads/.br_history/issues.20260309_212326_245619716.jsonl +58 -0
  23. package/.beads/.br_history/issues.20260309_212326_403198317.jsonl +59 -0
  24. package/.beads/.br_history/issues.20260309_212332_539197678.jsonl +60 -0
  25. package/.beads/.br_history/issues.20260309_212332_731373599.jsonl +60 -0
  26. package/.beads/.br_history/issues.20260309_212332_928710953.jsonl +60 -0
  27. package/.beads/.br_history/issues.20260309_213021_341505240.jsonl +60 -0
  28. package/.beads/.br_history/issues.20260309_213022_023136934.jsonl +60 -0
  29. package/.beads/.br_history/issues.20260309_213022_400050719.jsonl +60 -0
  30. package/.beads/issues.jsonl +20 -17
  31. package/README.md +1 -1
  32. package/__tests__/adapter-schema.test.js +3 -0
  33. package/__tests__/aws-plugin.test.js +84 -0
  34. package/__tests__/az-plugin.test.js +84 -0
  35. package/__tests__/builtin-adapter.test.js +29 -0
  36. package/__tests__/cline-plugin.test.js +109 -0
  37. package/__tests__/cline-skill.test.js +49 -0
  38. package/__tests__/docker-plugin.test.js +3 -1
  39. package/__tests__/eza-plugin.test.js +81 -0
  40. package/__tests__/gcloud-plugin.test.js +86 -0
  41. package/__tests__/gh-plugin.test.js +86 -0
  42. package/__tests__/helm-plugin.test.js +81 -0
  43. package/__tests__/http-adapter.test.js +118 -0
  44. package/__tests__/just-plugin.test.js +82 -0
  45. package/__tests__/kubectl-plugin.test.js +83 -0
  46. package/__tests__/linear-plugin.test.js +81 -0
  47. package/__tests__/mcp-adapter.test.js +187 -0
  48. package/__tests__/nextest-plugin.test.js +82 -0
  49. package/__tests__/npm-plugin.test.js +81 -0
  50. package/__tests__/nullclaw-plugin.test.js +157 -0
  51. package/__tests__/openapi-adapter.test.js +199 -0
  52. package/__tests__/plugin-agency-agents.test.js +6 -6
  53. package/__tests__/plugin-nullclaw.test.js +78 -0
  54. package/__tests__/plugin-visual-explainer.test.js +62 -0
  55. package/__tests__/plugins-manager.test.js +59 -10
  56. package/__tests__/pnpm-plugin.test.js +81 -0
  57. package/__tests__/poetry-plugin.test.js +83 -0
  58. package/__tests__/process-adapter.test.js +124 -90
  59. package/__tests__/pulumi-plugin.test.js +81 -0
  60. package/__tests__/railway-plugin.test.js +84 -0
  61. package/__tests__/server-app.test.js +67 -0
  62. package/__tests__/server-config-service.test.js +79 -0
  63. package/__tests__/server-routes-ask.test.js +89 -0
  64. package/__tests__/server-routes-commands.test.js +55 -0
  65. package/__tests__/server-routes-config.test.js +87 -0
  66. package/__tests__/server-routes-jobs.test.js +53 -0
  67. package/__tests__/server-routes-misc.test.js +112 -0
  68. package/__tests__/server-storage-adapter.test.js +40 -0
  69. package/__tests__/server-storage-file.test.js +73 -0
  70. package/__tests__/server-storage-mongo.test.js +74 -0
  71. package/__tests__/shell-adapter.test.js +81 -22
  72. package/__tests__/stripe-plugin.test.js +3 -1
  73. package/__tests__/supabase-plugin.test.js +86 -0
  74. package/__tests__/terraform-plugin.test.js +83 -0
  75. package/__tests__/uv-plugin.test.js +81 -0
  76. package/__tests__/vercel-plugin.test.js +81 -0
  77. package/__tests__/watchexec-plugin.test.js +80 -0
  78. package/cli/adapter-schema.js +5 -0
  79. package/cli/adapters/process.js +53 -2
  80. package/cli/plugin-install-guidance.js +320 -0
  81. package/cli/plugins-manager.js +272 -212
  82. package/cli/skills.js +16 -5
  83. package/cli/supercli.js +26 -2
  84. package/docs/plugins.md +2 -0
  85. package/docs/skills/cline-non-interactive/SKILL.md +59 -0
  86. package/docs/skills-catalog.md +35 -0
  87. package/docs/visual-overview.md +21 -0
  88. package/jest.config.js +4 -1
  89. package/package.json +4 -3
  90. package/plugins/agency-agents/plugin.json +5 -0
  91. package/{cli/plugin-agency-agents.js → plugins/agency-agents/scripts/post-install.js} +13 -3
  92. package/plugins/aws/README.md +46 -0
  93. package/plugins/aws/plugin.json +42 -0
  94. package/plugins/az/README.md +46 -0
  95. package/plugins/az/plugin.json +42 -0
  96. package/plugins/clickup/plugin.json +38 -0
  97. package/plugins/clickup/scripts/post-install.js +107 -0
  98. package/plugins/clickup/scripts/post-uninstall.js +30 -0
  99. package/plugins/cline/README.md +48 -0
  100. package/plugins/cline/plugin.json +92 -0
  101. package/plugins/eza/README.md +40 -0
  102. package/plugins/eza/plugin.json +42 -0
  103. package/plugins/gcloud/README.md +46 -0
  104. package/plugins/gcloud/plugin.json +42 -0
  105. package/plugins/gh/README.md +46 -0
  106. package/plugins/gh/plugin.json +43 -0
  107. package/plugins/helm/README.md +42 -0
  108. package/plugins/helm/plugin.json +42 -0
  109. package/plugins/just/README.md +42 -0
  110. package/plugins/just/plugin.json +42 -0
  111. package/plugins/kubectl/README.md +46 -0
  112. package/plugins/kubectl/plugin.json +42 -0
  113. package/plugins/linear/README.md +60 -0
  114. package/plugins/linear/plugin.json +42 -0
  115. package/plugins/nextest/README.md +42 -0
  116. package/plugins/nextest/plugin.json +42 -0
  117. package/plugins/npm/README.md +46 -0
  118. package/plugins/npm/plugin.json +42 -0
  119. package/plugins/nullclaw/README.md +45 -0
  120. package/plugins/nullclaw/plugin.json +64 -0
  121. package/plugins/nullclaw/scripts/post-install.js +189 -0
  122. package/plugins/nullclaw/scripts/post-uninstall.js +25 -0
  123. package/plugins/openfang/plugin.json +37 -0
  124. package/plugins/openfang/scripts/post-install.js +163 -0
  125. package/plugins/openfang/scripts/post-uninstall.js +30 -0
  126. package/plugins/plugins.json +234 -0
  127. package/plugins/pnpm/README.md +46 -0
  128. package/plugins/pnpm/plugin.json +42 -0
  129. package/plugins/poetry/README.md +46 -0
  130. package/plugins/poetry/plugin.json +42 -0
  131. package/plugins/pulumi/README.md +46 -0
  132. package/plugins/pulumi/plugin.json +42 -0
  133. package/plugins/railway/README.md +58 -0
  134. package/plugins/railway/plugin.json +43 -0
  135. package/plugins/supabase/README.md +55 -0
  136. package/plugins/supabase/plugin.json +42 -0
  137. package/plugins/superpowers/plugin.json +22 -0
  138. package/plugins/superpowers/scripts/post-install.js +124 -0
  139. package/plugins/superpowers/scripts/post-uninstall.js +30 -0
  140. package/plugins/terraform/README.md +46 -0
  141. package/plugins/terraform/plugin.json +42 -0
  142. package/plugins/uv/README.md +46 -0
  143. package/plugins/uv/plugin.json +42 -0
  144. package/plugins/vercel/README.md +47 -0
  145. package/plugins/vercel/plugin.json +42 -0
  146. package/plugins/visual-explainer/plugin.json +15 -0
  147. package/plugins/visual-explainer/scripts/post-install.js +111 -0
  148. package/plugins/watchexec/README.md +40 -0
  149. package/plugins/watchexec/plugin.json +42 -0
  150. package/tests/test-aws-smoke.sh +56 -0
  151. package/tests/test-az-smoke.sh +56 -0
  152. package/tests/test-cline-smoke.sh +37 -0
  153. package/tests/test-eza-smoke.sh +33 -0
  154. package/tests/test-gcloud-smoke.sh +56 -0
  155. package/tests/test-gh-smoke.sh +56 -0
  156. package/tests/test-helm-smoke.sh +33 -0
  157. package/tests/test-just-smoke.sh +40 -0
  158. package/tests/test-kubectl-smoke.sh +37 -0
  159. package/tests/test-linear-smoke.sh +97 -0
  160. package/tests/test-nextest-smoke.sh +33 -0
  161. package/tests/test-npm-smoke.sh +32 -0
  162. package/tests/test-nullclaw-smoke.sh +51 -0
  163. package/tests/test-plugins-registry.js +110 -0
  164. package/tests/test-pnpm-smoke.sh +33 -0
  165. package/tests/test-poetry-smoke.sh +33 -0
  166. package/tests/test-pulumi-smoke.sh +33 -0
  167. package/tests/test-railway-smoke.sh +95 -0
  168. package/tests/test-supabase-smoke.sh +95 -0
  169. package/tests/test-terraform-smoke.sh +33 -0
  170. package/tests/test-uv-smoke.sh +33 -0
  171. package/tests/test-vercel-smoke.sh +55 -0
  172. package/tests/test-watchexec-smoke.sh +33 -0
  173. package/.beads/.br_history/issues.20260308_180927_477542428.jsonl +0 -12
  174. package/.beads/.br_history/issues.20260308_181032_020230108.jsonl +0 -12
  175. package/.beads/.br_history/issues.20260308_181032_180539413.jsonl +0 -12
  176. package/.beads/.br_history/issues.20260308_181032_372621506.jsonl +0 -12
  177. package/.beads/.br_history/issues.20260308_181032_565142225.jsonl +0 -12
  178. package/.beads/.br_history/issues.20260308_181311_336346464.jsonl +0 -12
  179. package/.beads/.br_history/issues.20260308_181444_039234498.jsonl +0 -13
  180. package/.beads/.br_history/issues.20260308_181503_794764403.jsonl +0 -13
  181. package/.beads/.br_history/issues.20260308_181503_950163105.jsonl +0 -13
  182. package/.beads/.br_history/issues.20260308_192031_852553505.jsonl +0 -13
  183. package/.beads/.br_history/issues.20260308_193552_846920518.jsonl +0 -14
  184. package/.beads/.br_history/issues.20260308_194054_394884833.jsonl +0 -14
  185. package/.beads/.br_history/issues.20260308_194209_440472460.jsonl +0 -15
  186. package/.beads/.br_history/issues.20260308_195319_099391899.jsonl +0 -15
  187. package/.beads/.br_history/issues.20260308_195324_176987204.jsonl +0 -16
  188. package/.beads/.br_history/issues.20260308_195436_929114019.jsonl +0 -16
  189. package/.beads/.br_history/issues.20260308_195437_055808298.jsonl +0 -17
  190. package/.beads/.br_history/issues.20260308_195437_304297399.jsonl +0 -18
  191. package/.beads/.br_history/issues.20260308_195437_556007332.jsonl +0 -19
  192. package/.beads/.br_history/issues.20260308_195444_987209695.jsonl +0 -20
  193. package/.beads/.br_history/issues.20260308_195445_133350193.jsonl +0 -20
  194. package/.beads/.br_history/issues.20260308_195445_400185615.jsonl +0 -20
  195. package/.beads/.br_history/issues.20260308_195445_689886334.jsonl +0 -20
  196. package/.beads/.br_history/issues.20260308_195445_949947727.jsonl +0 -20
  197. package/.beads/.br_history/issues.20260308_195745_580473297.jsonl +0 -20
  198. package/.beads/.br_history/issues.20260308_195745_725920532.jsonl +0 -20
  199. package/.beads/.br_history/issues.20260308_195745_968227911.jsonl +0 -20
  200. package/.beads/.br_history/issues.20260308_195746_224276322.jsonl +0 -20
  201. package/.beads/.br_history/issues.20260308_200018_386890807.jsonl +0 -20
  202. package/ref-btcbot/.env.example +0 -19
  203. package/ref-btcbot/README.md +0 -3
  204. package/ref-btcbot/docs/bot.md +0 -72
  205. package/ref-btcbot/docs/skills/btcbot.backtest/SKILL.md +0 -70
  206. package/ref-btcbot/docs/skills/btcbot.config/SKILL.md +0 -79
  207. package/ref-btcbot/docs/skills/btcbot.orders/SKILL.md +0 -60
  208. package/ref-btcbot/docs/skills/btcbot.positions/SKILL.md +0 -54
  209. package/ref-btcbot/docs/skills/btcbot.risk/SKILL.md +0 -69
  210. package/ref-btcbot/docs/skills/btcbot.run-loop/SKILL.md +0 -63
  211. package/ref-btcbot/docs/skills/btcbot.run-once/SKILL.md +0 -63
  212. package/ref-btcbot/docs/skills/btcbot.status/SKILL.md +0 -59
  213. package/ref-btcbot/examples/sample-candles.json +0 -52
  214. package/ref-btcbot/package.json +0 -18
  215. package/ref-btcbot/plugin/plugin.json +0 -146
  216. package/ref-btcbot/src/cli.js +0 -104
  217. package/ref-btcbot/src/config.js +0 -80
  218. package/ref-btcbot/src/core/bot-runner.js +0 -78
  219. package/ref-btcbot/src/core/exchange-factory.js +0 -19
  220. package/ref-btcbot/src/core/live-exchange.js +0 -12
  221. package/ref-btcbot/src/core/paper-exchange.js +0 -82
  222. package/ref-btcbot/src/core/risk-engine.js +0 -42
  223. package/ref-btcbot/src/core/state-repository.js +0 -47
  224. package/ref-btcbot/src/services/backtest-service.js +0 -44
  225. package/ref-btcbot/src/services/market-data.js +0 -32
  226. package/ref-btcbot/src/storage/json-store.js +0 -45
  227. package/ref-btcbot/src/strategy/hedge-strategy.js +0 -65
  228. package/ref-btcbot/src/strategy/indicators.js +0 -56
  229. package/ref-btcbot/src/web/api.js +0 -50
  230. package/ref-btcbot/src/web/app.js +0 -51
  231. package/ref-btcbot/src/web/index.html +0 -70
  232. package/ref-btcbot/src/web/server.js +0 -33
  233. /package/.beads/.br_history/{issues.20260308_180927_477542428.jsonl.meta.json → issues.20260308_235202_180577215.jsonl.meta.json} +0 -0
  234. /package/.beads/.br_history/{issues.20260308_181032_020230108.jsonl.meta.json → issues.20260308_235202_387414163.jsonl.meta.json} +0 -0
  235. /package/.beads/.br_history/{issues.20260308_181032_180539413.jsonl.meta.json → issues.20260308_235202_564422794.jsonl.meta.json} +0 -0
  236. /package/.beads/.br_history/{issues.20260308_181032_372621506.jsonl.meta.json → issues.20260308_235202_742600597.jsonl.meta.json} +0 -0
  237. /package/.beads/.br_history/{issues.20260308_181032_565142225.jsonl.meta.json → issues.20260308_235208_133360069.jsonl.meta.json} +0 -0
  238. /package/.beads/.br_history/{issues.20260308_181311_336346464.jsonl.meta.json → issues.20260308_235505_473406307.jsonl.meta.json} +0 -0
  239. /package/.beads/.br_history/{issues.20260308_181444_039234498.jsonl.meta.json → issues.20260308_235505_662360489.jsonl.meta.json} +0 -0
  240. /package/.beads/.br_history/{issues.20260308_181503_794764403.jsonl.meta.json → issues.20260308_235505_843935624.jsonl.meta.json} +0 -0
  241. /package/.beads/.br_history/{issues.20260308_181503_950163105.jsonl.meta.json → issues.20260308_235506_044530221.jsonl.meta.json} +0 -0
  242. /package/.beads/.br_history/{issues.20260308_192031_852553505.jsonl.meta.json → issues.20260309_002618_115728731.jsonl.meta.json} +0 -0
  243. /package/.beads/.br_history/{issues.20260308_193552_846920518.jsonl.meta.json → issues.20260309_003748_878174586.jsonl.meta.json} +0 -0
  244. /package/.beads/.br_history/{issues.20260308_194054_394884833.jsonl.meta.json → issues.20260309_004057_868755623.jsonl.meta.json} +0 -0
  245. /package/.beads/.br_history/{issues.20260308_194209_440472460.jsonl.meta.json → issues.20260309_004058_512842163.jsonl.meta.json} +0 -0
  246. /package/.beads/.br_history/{issues.20260308_195319_099391899.jsonl.meta.json → issues.20260309_004058_994445226.jsonl.meta.json} +0 -0
  247. /package/.beads/.br_history/{issues.20260308_195324_176987204.jsonl.meta.json → issues.20260309_004059_475988596.jsonl.meta.json} +0 -0
  248. /package/.beads/.br_history/{issues.20260308_195436_929114019.jsonl.meta.json → issues.20260309_161902_566857851.jsonl.meta.json} +0 -0
  249. /package/.beads/.br_history/{issues.20260308_195437_055808298.jsonl.meta.json → issues.20260309_170512_277017739.jsonl.meta.json} +0 -0
  250. /package/.beads/.br_history/{issues.20260308_195437_304297399.jsonl.meta.json → issues.20260309_170512_477876921.jsonl.meta.json} +0 -0
  251. /package/.beads/.br_history/{issues.20260308_195437_556007332.jsonl.meta.json → issues.20260309_170512_664382701.jsonl.meta.json} +0 -0
  252. /package/.beads/.br_history/{issues.20260308_195444_987209695.jsonl.meta.json → issues.20260309_170512_859400333.jsonl.meta.json} +0 -0
  253. /package/.beads/.br_history/{issues.20260308_195445_133350193.jsonl.meta.json → issues.20260309_212326_082771164.jsonl.meta.json} +0 -0
  254. /package/.beads/.br_history/{issues.20260308_195445_400185615.jsonl.meta.json → issues.20260309_212326_245619716.jsonl.meta.json} +0 -0
  255. /package/.beads/.br_history/{issues.20260308_195445_689886334.jsonl.meta.json → issues.20260309_212326_403198317.jsonl.meta.json} +0 -0
  256. /package/.beads/.br_history/{issues.20260308_195445_949947727.jsonl.meta.json → issues.20260309_212332_539197678.jsonl.meta.json} +0 -0
  257. /package/.beads/.br_history/{issues.20260308_195745_580473297.jsonl.meta.json → issues.20260309_212332_731373599.jsonl.meta.json} +0 -0
  258. /package/.beads/.br_history/{issues.20260308_195745_725920532.jsonl.meta.json → issues.20260309_212332_928710953.jsonl.meta.json} +0 -0
  259. /package/.beads/.br_history/{issues.20260308_195745_968227911.jsonl.meta.json → issues.20260309_213021_341505240.jsonl.meta.json} +0 -0
  260. /package/.beads/.br_history/{issues.20260308_195746_224276322.jsonl.meta.json → issues.20260309_213022_023136934.jsonl.meta.json} +0 -0
  261. /package/.beads/.br_history/{issues.20260308_200018_386890807.jsonl.meta.json → issues.20260309_213022_400050719.jsonl.meta.json} +0 -0
@@ -0,0 +1,73 @@
1
+ const fs = require("fs").promises
2
+ const path = require("path")
3
+ const FileAdapter = require("../server/storage/file")
4
+
5
+ jest.mock("fs", () => ({
6
+ promises: {
7
+ readFile: jest.fn(),
8
+ writeFile: jest.fn(),
9
+ unlink: jest.fn(),
10
+ readdir: jest.fn(),
11
+ mkdir: jest.fn()
12
+ },
13
+ existsSync: jest.fn()
14
+ }))
15
+ const fsMock = require("fs")
16
+
17
+ describe("FileAdapter", () => {
18
+ const baseDir = "/tmp/storage"
19
+ let adapter
20
+
21
+ beforeEach(() => {
22
+ jest.clearAllMocks()
23
+ adapter = new FileAdapter(baseDir)
24
+ })
25
+
26
+ test("get returns parsed JSON", async () => {
27
+ fs.readFile.mockResolvedValue('{"a": 1}')
28
+ const result = await adapter.get("mykey")
29
+ expect(result).toEqual({ a: 1 })
30
+ expect(fs.readFile).toHaveBeenCalledWith(expect.stringContaining("mykey.json"), "utf-8")
31
+ })
32
+
33
+ test("get returns null on ENOENT", async () => {
34
+ fs.readFile.mockRejectedValue({ code: "ENOENT" })
35
+ expect(await adapter.get("missing")).toBeNull()
36
+ })
37
+
38
+ test("get throws other errors", async () => {
39
+ fs.readFile.mockRejectedValue(new Error("crash"))
40
+ await expect(adapter.get("err")).rejects.toThrow("crash")
41
+ })
42
+
43
+ test("set creates dir and writes file", async () => {
44
+ await adapter.set("key", { x: 1 })
45
+ expect(fs.mkdir).toHaveBeenCalledWith(baseDir, { recursive: true })
46
+ expect(fs.writeFile).toHaveBeenCalledWith(
47
+ expect.stringContaining("key.json"),
48
+ expect.stringContaining('"x": 1'),
49
+ "utf-8"
50
+ )
51
+ })
52
+
53
+ test("delete unlinks file", async () => {
54
+ await adapter.delete("key")
55
+ expect(fs.unlink).toHaveBeenCalled()
56
+ })
57
+
58
+ test("delete ignores ENOENT", async () => {
59
+ fs.unlink.mockRejectedValue({ code: "ENOENT" })
60
+ await expect(adapter.delete("key")).resolves.not.toThrow()
61
+ })
62
+
63
+ test("listKeys filters and maps files", async () => {
64
+ fs.readdir.mockResolvedValue(["abc_1.json", "abc_2.json", "other.json", "abc_3.txt"])
65
+ const keys = await adapter.listKeys("abc")
66
+ expect(keys).toEqual(["abc_1", "abc_2"])
67
+ })
68
+
69
+ test("listKeys returns empty on ENOENT", async () => {
70
+ fs.readdir.mockRejectedValue({ code: "ENOENT" })
71
+ expect(await adapter.listKeys()).toEqual([])
72
+ })
73
+ })
@@ -0,0 +1,74 @@
1
+ const MongoAdapter = require("../server/storage/mongo")
2
+ const { MongoClient } = require("mongodb")
3
+
4
+ jest.mock("mongodb")
5
+
6
+ describe("MongoAdapter", () => {
7
+ let adapter
8
+ let mockCollection
9
+ let mockDb
10
+ let mockClient
11
+
12
+ beforeEach(() => {
13
+ jest.clearAllMocks()
14
+ mockCollection = {
15
+ findOne: jest.fn(),
16
+ updateOne: jest.fn(),
17
+ deleteOne: jest.fn(),
18
+ find: jest.fn().mockReturnThis(),
19
+ toArray: jest.fn()
20
+ }
21
+ mockDb = {
22
+ collection: jest.fn().mockReturnValue(mockCollection)
23
+ }
24
+ mockClient = {
25
+ connect: jest.fn().mockResolvedValue({}),
26
+ db: jest.fn().mockReturnValue(mockDb)
27
+ }
28
+ MongoClient.mockImplementation(() => mockClient)
29
+
30
+ adapter = new MongoAdapter("mongodb://uri")
31
+ })
32
+
33
+ test("get returns value field", async () => {
34
+ mockCollection.findOne.mockResolvedValue({ _id: "k", value: { x: 1 } })
35
+ const result = await adapter.get("k")
36
+ expect(result).toEqual({ x: 1 })
37
+ expect(mockCollection.findOne).toHaveBeenCalledWith({ _id: "k" })
38
+ })
39
+
40
+ test("get returns null if doc missing", async () => {
41
+ mockCollection.findOne.mockResolvedValue(null)
42
+ expect(await adapter.get("k")).toBeNull()
43
+ })
44
+
45
+ test("set updates with upsert", async () => {
46
+ await adapter.set("k", { y: 2 })
47
+ expect(mockCollection.updateOne).toHaveBeenCalledWith(
48
+ { _id: "k" },
49
+ { $set: { value: { y: 2 } } },
50
+ { upsert: true }
51
+ )
52
+ })
53
+
54
+ test("delete removes by _id", async () => {
55
+ await adapter.delete("k")
56
+ expect(mockCollection.deleteOne).toHaveBeenCalledWith({ _id: "k" })
57
+ })
58
+
59
+ test("listKeys uses regex and maps ids", async () => {
60
+ mockCollection.toArray.mockResolvedValue([{ _id: "pre:1" }, { _id: "pre:2" }])
61
+ const keys = await adapter.listKeys("pre:")
62
+ expect(keys).toEqual(["pre:1", "pre:2"])
63
+ expect(mockCollection.find).toHaveBeenCalledWith(
64
+ { _id: { $regex: "^pre:" } },
65
+ { projection: { _id: 1 } }
66
+ )
67
+ })
68
+
69
+ test("listKeys without prefix", async () => {
70
+ mockCollection.toArray.mockResolvedValue([])
71
+ await adapter.listKeys()
72
+ expect(mockCollection.find).toHaveBeenCalledWith({}, expect.anything())
73
+ })
74
+ })
@@ -1,31 +1,90 @@
1
1
  const { execute } = require("../cli/adapters/shell")
2
+ const { spawn } = require("child_process")
3
+ const EventEmitter = require("events")
4
+
5
+ jest.mock("child_process")
2
6
 
3
7
  describe("shell adapter", () => {
4
- test("executes script and parses json", async () => {
5
- const result = await execute({
6
- adapterConfig: {
7
- shell: "bash",
8
- unsafe: true,
9
- script: "printf '{\"ok\":true,\"msg\":\"{{text}}\"}'",
10
- parseJson: true,
11
- timeout_ms: 1000
12
- }
13
- }, { text: "hello" })
14
-
15
- expect(result).toEqual({ ok: true, msg: "hello" })
8
+ let mockChild
9
+
10
+ beforeEach(() => {
11
+ jest.clearAllMocks()
12
+ mockChild = new EventEmitter()
13
+ mockChild.stdout = new EventEmitter()
14
+ mockChild.stdout.setEncoding = jest.fn()
15
+ mockChild.stderr = new EventEmitter()
16
+ mockChild.stderr.setEncoding = jest.fn()
17
+ mockChild.kill = jest.fn()
18
+ spawn.mockReturnValue(mockChild)
19
+ })
20
+
21
+ test("interpolates script and executes", async () => {
22
+ const promise = execute({
23
+ adapterConfig: { script: "echo {{name}}", parseJson: false }
24
+ }, { name: 'world"quoted' })
25
+
26
+ mockChild.stdout.emit("data", "hello world\\\"quoted")
27
+ mockChild.emit("close", 0)
28
+
29
+ const result = await promise
30
+ expect(result).toEqual({ raw: "hello world\\\"quoted" })
31
+ expect(spawn).toHaveBeenCalledWith("bash", ["-lc", "echo world\\\"quoted"], expect.anything())
32
+ })
33
+
34
+ test("handles JSON parsing", async () => {
35
+ const promise = execute({
36
+ adapterConfig: { script: "echo '{\"ok\":true}'" }
37
+ }, {})
38
+
39
+ mockChild.stdout.emit("data", '{"ok":true}')
40
+ mockChild.emit("close", 0)
41
+
42
+ const result = await promise
43
+ expect(result).toEqual({ ok: true })
16
44
  })
17
45
 
18
- test("returns raw text when parseJson is false", async () => {
19
- const result = await execute({
20
- adapterConfig: {
21
- shell: "bash",
22
- unsafe: true,
23
- script: "printf 'plain-output'",
24
- parseJson: false,
25
- timeout_ms: 1000
26
- }
46
+ test("handles non-JSON when parseJson is true", async () => {
47
+ const promise = execute({
48
+ adapterConfig: { script: "echo not-json" }
27
49
  }, {})
28
50
 
29
- expect(result).toEqual({ raw: "plain-output" })
51
+ mockChild.stdout.emit("data", "not-json")
52
+ mockChild.emit("close", 0)
53
+
54
+ const result = await promise
55
+ expect(result).toEqual({ raw: "not-json" })
56
+ })
57
+
58
+ test("handles timeout", async () => {
59
+ jest.useFakeTimers()
60
+ const promise = execute({
61
+ adapterConfig: { script: "sleep 10", timeout_ms: 100 }
62
+ }, {})
63
+
64
+ jest.advanceTimersByTime(200)
65
+
66
+ await expect(promise).rejects.toThrow(/timed out after 100ms/)
67
+ jest.useRealTimers()
68
+ })
69
+
70
+ test("handles spawn error", async () => {
71
+ const promise = execute({
72
+ adapterConfig: { script: "fail" }
73
+ }, {})
74
+
75
+ mockChild.emit("error", new Error("spawn fail"))
76
+
77
+ await expect(promise).rejects.toThrow("Failed to start shell adapter")
78
+ })
79
+
80
+ test("handles non-zero exit", async () => {
81
+ const promise = execute({
82
+ adapterConfig: { script: "exit 1" }
83
+ }, {})
84
+
85
+ mockChild.stderr.emit("data", "error details")
86
+ mockChild.emit("close", 1)
87
+
88
+ await expect(promise).rejects.toThrow(/Shell adapter failed.*error details/)
30
89
  })
31
90
  })
@@ -39,8 +39,9 @@ function writeFakeStripeBinary(dir) {
39
39
 
40
40
  describe("stripe plugin", () => {
41
41
  const fakeDir = fs.mkdtempSync(path.join(os.tmpdir(), "dcli-stripe-"))
42
+ const tempHome = fs.mkdtempSync(path.join(os.tmpdir(), "dcli-home-stripe-"))
42
43
  writeFakeStripeBinary(fakeDir)
43
- const env = { ...process.env, PATH: `${fakeDir}:${process.env.PATH || ""}` }
44
+ const env = { ...process.env, PATH: `${fakeDir}:${process.env.PATH || ""}`, SUPERCLI_HOME: tempHome }
44
45
 
45
46
  beforeAll(() => {
46
47
  runNoServer("plugins install ./plugins/stripe --on-conflict replace --json", { env })
@@ -49,6 +50,7 @@ describe("stripe plugin", () => {
49
50
  afterAll(() => {
50
51
  runNoServer("plugins remove stripe --json", { env })
51
52
  fs.rmSync(fakeDir, { recursive: true, force: true })
53
+ fs.rmSync(tempHome, { recursive: true, force: true })
52
54
  })
53
55
 
54
56
  test("routes customers list command", () => {
@@ -0,0 +1,86 @@
1
+ const fs = require("fs")
2
+ const os = require("os")
3
+ const path = require("path")
4
+ const { execSync } = require("child_process")
5
+
6
+ const CLI = path.join(__dirname, "..", "cli", "supercli.js")
7
+
8
+ function runNoServer(args, options = {}) {
9
+ try {
10
+ const env = { ...process.env }
11
+ delete env.SUPERCLI_SERVER
12
+ const out = execSync(`node ${CLI} ${args}`, {
13
+ encoding: "utf-8",
14
+ timeout: 15000,
15
+ env: { ...env, ...(options.env || {}) }
16
+ })
17
+ return { ok: true, output: out.trim(), code: 0 }
18
+ } catch (err) {
19
+ return {
20
+ ok: false,
21
+ output: (err.stdout || "").trim(),
22
+ stderr: (err.stderr || "").trim(),
23
+ code: err.status
24
+ }
25
+ }
26
+ }
27
+
28
+ function writeFakeSupabaseBinary(dir) {
29
+ const bin = path.join(dir, "supabase")
30
+ fs.writeFileSync(bin, [
31
+ "#!/usr/bin/env node",
32
+ "const args = process.argv.slice(2);",
33
+ "if (args.includes('--version')) { console.log('supabase 2.0.0-test'); process.exit(0); }",
34
+ "if (args[0] === 'projects' && args[1] === 'list' && args.includes('--output') && args.includes('json')) {",
35
+ " console.log(JSON.stringify([{ id: 'proj_1', name: 'mock-project' }]));",
36
+ " process.exit(0);",
37
+ "}",
38
+ "console.log(JSON.stringify({ ok: true, args }));"
39
+ ].join("\n"), "utf-8")
40
+ fs.chmodSync(bin, 0o755)
41
+ return bin
42
+ }
43
+
44
+ describe("supabase plugin", () => {
45
+ const fakeDir = fs.mkdtempSync(path.join(os.tmpdir(), "dcli-supabase-"))
46
+ const tempHome = fs.mkdtempSync(path.join(os.tmpdir(), "dcli-home-supabase-"))
47
+ writeFakeSupabaseBinary(fakeDir)
48
+ const env = { ...process.env, PATH: `${fakeDir}:${process.env.PATH || ""}`, SUPERCLI_HOME: tempHome }
49
+
50
+ beforeAll(() => {
51
+ runNoServer("plugins install ./plugins/supabase --on-conflict replace --json", { env })
52
+ })
53
+
54
+ afterAll(() => {
55
+ runNoServer("plugins remove supabase --json", { env })
56
+ fs.rmSync(fakeDir, { recursive: true, force: true })
57
+ fs.rmSync(tempHome, { recursive: true, force: true })
58
+ })
59
+
60
+ test("routes projects list wrapped command", () => {
61
+ const r = runNoServer("supabase projects list --json", { env })
62
+ expect(r.ok).toBe(true)
63
+ const data = JSON.parse(r.output)
64
+ expect(data.command).toBe("supabase.projects.list")
65
+ expect(Array.isArray(data.data)).toBe(true)
66
+ expect(data.data[0].name).toBe("mock-project")
67
+ })
68
+
69
+ test("supports namespace passthrough", () => {
70
+ const r = runNoServer("supabase status --output json", { env })
71
+ expect(r.ok).toBe(true)
72
+ const data = JSON.parse(r.output)
73
+ expect(data.command).toBe("supabase.passthrough")
74
+ expect(data.data.args[0]).toBe("status")
75
+ expect(data.data.args).toContain("--output")
76
+ expect(data.data.args).toContain("json")
77
+ })
78
+
79
+ test("doctor reports supabase dependency as healthy", () => {
80
+ const r = runNoServer("plugins doctor supabase --json", { env })
81
+ expect(r.ok).toBe(true)
82
+ const data = JSON.parse(r.output)
83
+ expect(data.ok).toBe(true)
84
+ expect(data.checks.some(c => c.type === "binary" && c.binary === "supabase" && c.ok === true)).toBe(true)
85
+ })
86
+ })
@@ -0,0 +1,83 @@
1
+ const fs = require("fs")
2
+ const os = require("os")
3
+ const path = require("path")
4
+ const { execSync } = require("child_process")
5
+
6
+ const CLI = path.join(__dirname, "..", "cli", "supercli.js")
7
+
8
+ function runNoServer(args, options = {}) {
9
+ try {
10
+ const env = { ...process.env }
11
+ delete env.SUPERCLI_SERVER
12
+ const out = execSync(`node ${CLI} ${args}`, {
13
+ encoding: "utf-8",
14
+ timeout: 15000,
15
+ env: { ...env, ...(options.env || {}) }
16
+ })
17
+ return { ok: true, output: out.trim(), code: 0 }
18
+ } catch (err) {
19
+ return {
20
+ ok: false,
21
+ output: (err.stdout || "").trim(),
22
+ stderr: (err.stderr || "").trim(),
23
+ code: err.status
24
+ }
25
+ }
26
+ }
27
+
28
+ function writeFakeTerraformBinary(dir) {
29
+ const bin = path.join(dir, "terraform")
30
+ fs.writeFileSync(bin, [
31
+ "#!/usr/bin/env node",
32
+ "const args = process.argv.slice(2);",
33
+ "if (args[0] === 'version' && args.includes('-json')) {",
34
+ " console.log(JSON.stringify({ terraform_version: '1.9.0-test', platform: 'linux_amd64' }));",
35
+ " process.exit(0);",
36
+ "}",
37
+ "console.log(JSON.stringify({ ok: true, args }));"
38
+ ].join("\n"), "utf-8")
39
+ fs.chmodSync(bin, 0o755)
40
+ return bin
41
+ }
42
+
43
+ describe("terraform plugin", () => {
44
+ const fakeDir = fs.mkdtempSync(path.join(os.tmpdir(), "dcli-terraform-"))
45
+ const tempHome = fs.mkdtempSync(path.join(os.tmpdir(), "dcli-home-terraform-"))
46
+ writeFakeTerraformBinary(fakeDir)
47
+ const env = { ...process.env, PATH: `${fakeDir}:${process.env.PATH || ""}`, SUPERCLI_HOME: tempHome }
48
+
49
+ beforeAll(() => {
50
+ runNoServer("plugins install ./plugins/terraform --on-conflict replace --json", { env })
51
+ })
52
+
53
+ afterAll(() => {
54
+ runNoServer("plugins remove terraform --json", { env })
55
+ fs.rmSync(fakeDir, { recursive: true, force: true })
56
+ fs.rmSync(tempHome, { recursive: true, force: true })
57
+ })
58
+
59
+ test("routes cli version wrapped command", () => {
60
+ const r = runNoServer("terraform cli version --json", { env })
61
+ expect(r.ok).toBe(true)
62
+ const data = JSON.parse(r.output)
63
+ expect(data.command).toBe("terraform.cli.version")
64
+ expect(data.data.terraform_version).toBe("1.9.0-test")
65
+ })
66
+
67
+ test("supports namespace passthrough", () => {
68
+ const r = runNoServer("terraform output -json", { env })
69
+ expect(r.ok).toBe(true)
70
+ const data = JSON.parse(r.output)
71
+ expect(data.command).toBe("terraform.passthrough")
72
+ expect(data.data.args[0]).toBe("output")
73
+ expect(data.data.args).toContain("-json")
74
+ })
75
+
76
+ test("doctor reports terraform dependency as healthy", () => {
77
+ const r = runNoServer("plugins doctor terraform --json", { env })
78
+ expect(r.ok).toBe(true)
79
+ const data = JSON.parse(r.output)
80
+ expect(data.ok).toBe(true)
81
+ expect(data.checks.some(c => c.type === "binary" && c.binary === "terraform" && c.ok === true)).toBe(true)
82
+ })
83
+ })
@@ -0,0 +1,81 @@
1
+ const fs = require("fs")
2
+ const os = require("os")
3
+ const path = require("path")
4
+ const { execSync } = require("child_process")
5
+
6
+ const CLI = path.join(__dirname, "..", "cli", "supercli.js")
7
+
8
+ function runNoServer(args, options = {}) {
9
+ try {
10
+ const env = { ...process.env }
11
+ delete env.SUPERCLI_SERVER
12
+ const out = execSync(`node ${CLI} ${args}`, {
13
+ encoding: "utf-8",
14
+ timeout: 15000,
15
+ env: { ...env, ...(options.env || {}) }
16
+ })
17
+ return { ok: true, output: out.trim(), code: 0 }
18
+ } catch (err) {
19
+ return {
20
+ ok: false,
21
+ output: (err.stdout || "").trim(),
22
+ stderr: (err.stderr || "").trim(),
23
+ code: err.status
24
+ }
25
+ }
26
+ }
27
+
28
+ function writeFakeUvBinary(dir) {
29
+ const bin = path.join(dir, "uv")
30
+ fs.writeFileSync(bin, [
31
+ "#!/usr/bin/env node",
32
+ "const args = process.argv.slice(2);",
33
+ "if (args[0] === '--version') { console.log('uv 0.6.5-test'); process.exit(0); }",
34
+ "console.log(JSON.stringify({ ok: true, args }));"
35
+ ].join("\n"), "utf-8")
36
+ fs.chmodSync(bin, 0o755)
37
+ return bin
38
+ }
39
+
40
+ describe("uv plugin", () => {
41
+ const fakeDir = fs.mkdtempSync(path.join(os.tmpdir(), "dcli-uv-"))
42
+ const tempHome = fs.mkdtempSync(path.join(os.tmpdir(), "dcli-home-uv-"))
43
+ writeFakeUvBinary(fakeDir)
44
+ const env = { ...process.env, PATH: `${fakeDir}:${process.env.PATH || ""}`, SUPERCLI_HOME: tempHome }
45
+
46
+ beforeAll(() => {
47
+ runNoServer("plugins install ./plugins/uv --on-conflict replace --json", { env })
48
+ })
49
+
50
+ afterAll(() => {
51
+ runNoServer("plugins remove uv --json", { env })
52
+ fs.rmSync(fakeDir, { recursive: true, force: true })
53
+ fs.rmSync(tempHome, { recursive: true, force: true })
54
+ })
55
+
56
+ test("routes cli version wrapped command", () => {
57
+ const r = runNoServer("uv cli version --json", { env })
58
+ expect(r.ok).toBe(true)
59
+ const data = JSON.parse(r.output)
60
+ expect(data.command).toBe("uv.cli.version")
61
+ expect(data.data.raw).toBe("uv 0.6.5-test")
62
+ })
63
+
64
+ test("supports namespace passthrough", () => {
65
+ const r = runNoServer("uv python list --json", { env })
66
+ expect(r.ok).toBe(true)
67
+ const data = JSON.parse(r.output)
68
+ expect(data.command).toBe("uv.passthrough")
69
+ expect(data.data.args[0]).toBe("python")
70
+ expect(data.data.args[1]).toBe("list")
71
+ expect(data.data.args).toContain("--json")
72
+ })
73
+
74
+ test("doctor reports uv dependency as healthy", () => {
75
+ const r = runNoServer("plugins doctor uv --json", { env })
76
+ expect(r.ok).toBe(true)
77
+ const data = JSON.parse(r.output)
78
+ expect(data.ok).toBe(true)
79
+ expect(data.checks.some(c => c.type === "binary" && c.binary === "uv" && c.ok === true)).toBe(true)
80
+ })
81
+ })
@@ -0,0 +1,81 @@
1
+ const fs = require("fs")
2
+ const os = require("os")
3
+ const path = require("path")
4
+ const { execSync } = require("child_process")
5
+
6
+ const CLI = path.join(__dirname, "..", "cli", "supercli.js")
7
+
8
+ function runNoServer(args, options = {}) {
9
+ try {
10
+ const env = { ...process.env }
11
+ delete env.SUPERCLI_SERVER
12
+ const out = execSync(`node ${CLI} ${args}`, {
13
+ encoding: "utf-8",
14
+ timeout: 15000,
15
+ env: { ...env, ...(options.env || {}) }
16
+ })
17
+ return { ok: true, output: out.trim(), code: 0 }
18
+ } catch (err) {
19
+ return {
20
+ ok: false,
21
+ output: (err.stdout || "").trim(),
22
+ stderr: (err.stderr || "").trim(),
23
+ code: err.status
24
+ }
25
+ }
26
+ }
27
+
28
+ function writeFakeVercelBinary(dir) {
29
+ const bin = path.join(dir, "vercel")
30
+ fs.writeFileSync(bin, [
31
+ "#!/usr/bin/env node",
32
+ "const args = process.argv.slice(2);",
33
+ "if (args.includes('--version')) { console.log('vercel 33.0.0'); process.exit(0); }",
34
+ "if (args[0] === 'whoami') { console.log('mock-vercel-user'); process.exit(0); }",
35
+ "console.log(JSON.stringify({ ok: true, args }));"
36
+ ].join("\n"), "utf-8")
37
+ fs.chmodSync(bin, 0o755)
38
+ return bin
39
+ }
40
+
41
+ describe("vercel plugin", () => {
42
+ const fakeDir = fs.mkdtempSync(path.join(os.tmpdir(), "dcli-vercel-"))
43
+ const tempHome = fs.mkdtempSync(path.join(os.tmpdir(), "dcli-home-vercel-"))
44
+ writeFakeVercelBinary(fakeDir)
45
+ const env = { ...process.env, PATH: `${fakeDir}:${process.env.PATH || ""}`, SUPERCLI_HOME: tempHome }
46
+
47
+ beforeAll(() => {
48
+ runNoServer("plugins install ./plugins/vercel --on-conflict replace --json", { env })
49
+ })
50
+
51
+ afterAll(() => {
52
+ runNoServer("plugins remove vercel --json", { env })
53
+ fs.rmSync(fakeDir, { recursive: true, force: true })
54
+ fs.rmSync(tempHome, { recursive: true, force: true })
55
+ })
56
+
57
+ test("routes account whoami wrapped command", () => {
58
+ const r = runNoServer("vercel account whoami", { env })
59
+ expect(r.ok).toBe(true)
60
+ const data = JSON.parse(r.output)
61
+ expect(data.command).toBe("vercel.account.whoami")
62
+ expect(data.data.raw).toBe("mock-vercel-user")
63
+ })
64
+
65
+ test("supports namespace passthrough", () => {
66
+ const r = runNoServer("vercel project ls", { env })
67
+ expect(r.ok).toBe(true)
68
+ const data = JSON.parse(r.output)
69
+ expect(data.command).toBe("vercel.passthrough")
70
+ expect(data.data.args[0]).toBe("project")
71
+ expect(data.data.args[1]).toBe("ls")
72
+ })
73
+
74
+ test("doctor reports vercel dependency as healthy", () => {
75
+ const r = runNoServer("plugins doctor vercel --json", { env })
76
+ expect(r.ok).toBe(true)
77
+ const data = JSON.parse(r.output)
78
+ expect(data.ok).toBe(true)
79
+ expect(data.checks.some(c => c.type === "binary" && c.binary === "vercel" && c.ok === true)).toBe(true)
80
+ })
81
+ })