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,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 writeFakeHelmBinary(dir) {
29
+ const bin = path.join(dir, "helm")
30
+ fs.writeFileSync(bin, [
31
+ "#!/usr/bin/env node",
32
+ "const args = process.argv.slice(2);",
33
+ "if (args[0] === 'version' && args.includes('--short')) { console.log('v4.1.1+gtest'); 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("helm plugin", () => {
41
+ const fakeDir = fs.mkdtempSync(path.join(os.tmpdir(), "dcli-helm-"))
42
+ const tempHome = fs.mkdtempSync(path.join(os.tmpdir(), "dcli-home-helm-"))
43
+ writeFakeHelmBinary(fakeDir)
44
+ const env = { ...process.env, PATH: `${fakeDir}:${process.env.PATH || ""}`, SUPERCLI_HOME: tempHome }
45
+
46
+ beforeAll(() => {
47
+ runNoServer("plugins install ./plugins/helm --on-conflict replace --json", { env })
48
+ })
49
+
50
+ afterAll(() => {
51
+ runNoServer("plugins remove helm --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("helm cli version --json", { env })
58
+ expect(r.ok).toBe(true)
59
+ const data = JSON.parse(r.output)
60
+ expect(data.command).toBe("helm.cli.version")
61
+ expect(data.data.raw).toBe("v4.1.1+gtest")
62
+ })
63
+
64
+ test("supports namespace passthrough", () => {
65
+ const r = runNoServer("helm list --output json", { env })
66
+ expect(r.ok).toBe(true)
67
+ const data = JSON.parse(r.output)
68
+ expect(data.command).toBe("helm.passthrough")
69
+ expect(data.data.args[0]).toBe("list")
70
+ expect(data.data.args).toContain("--output")
71
+ expect(data.data.args).toContain("json")
72
+ })
73
+
74
+ test("doctor reports helm dependency as healthy", () => {
75
+ const r = runNoServer("plugins doctor helm --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 === "helm" && c.ok === true)).toBe(true)
80
+ })
81
+ })
@@ -0,0 +1,118 @@
1
+ const { execute } = require("../cli/adapters/http")
2
+
3
+ describe("http adapter", () => {
4
+ beforeEach(() => {
5
+ global.fetch = jest.fn()
6
+ })
7
+
8
+ test("throws if url missing", async () => {
9
+ await expect(execute({ adapterConfig: {} }, {}, {})).rejects.toThrow(/requires 'url'/)
10
+ })
11
+
12
+ test("performs GET request with placeholders and query params", async () => {
13
+ global.fetch.mockResolvedValue({
14
+ ok: true,
15
+ headers: { get: () => "application/json" },
16
+ json: () => Promise.resolve({ data: "ok" })
17
+ })
18
+
19
+ const result = await execute({
20
+ adapterConfig: { url: "https://api.test/users/{id}" }
21
+ }, { id: "123", q: "search", human: true }, {})
22
+
23
+ expect(global.fetch).toHaveBeenCalledWith(
24
+ "https://api.test/users/123?q=search",
25
+ expect.objectContaining({ method: "GET" })
26
+ )
27
+ expect(result).toEqual({ data: "ok" })
28
+ })
29
+
30
+ test("performs POST request with body from flags", async () => {
31
+ global.fetch.mockResolvedValue({
32
+ ok: true,
33
+ headers: { get: () => "application/json" },
34
+ json: () => Promise.resolve({ ok: true })
35
+ })
36
+
37
+ const result = await execute({
38
+ adapterConfig: { url: "https://api.test/users", method: "POST" }
39
+ }, { name: "alice", role: "admin" }, {})
40
+
41
+ expect(global.fetch).toHaveBeenCalledWith(
42
+ "https://api.test/users",
43
+ expect.objectContaining({
44
+ method: "POST",
45
+ body: JSON.stringify({ name: "alice", role: "admin" })
46
+ })
47
+ )
48
+ })
49
+
50
+ test("performs POST request with predefined body", async () => {
51
+ global.fetch.mockResolvedValue({
52
+ ok: true,
53
+ headers: { get: () => "text/plain" },
54
+ text: () => Promise.resolve("raw-response")
55
+ })
56
+
57
+ const result = await execute({
58
+ adapterConfig: {
59
+ url: "https://api.test/users",
60
+ method: "PATCH",
61
+ body: { fixed: true }
62
+ }
63
+ }, { ignored: "val" }, {})
64
+
65
+ expect(global.fetch).toHaveBeenCalledWith(
66
+ "https://api.test/users",
67
+ expect.objectContaining({
68
+ method: "PATCH",
69
+ body: JSON.stringify({ fixed: true })
70
+ })
71
+ )
72
+ expect(result).toEqual({ raw: "raw-response" })
73
+ })
74
+
75
+ test("handles HTTP error codes (500)", async () => {
76
+ global.fetch.mockResolvedValue({
77
+ ok: false,
78
+ status: 500,
79
+ statusText: "Server Error",
80
+ text: () => Promise.resolve("error details")
81
+ })
82
+
83
+ await expect(execute({ adapterConfig: { url: "u" } }, {}, {}))
84
+ .rejects.toMatchObject({
85
+ code: 105,
86
+ type: "integration_error",
87
+ recoverable: true
88
+ })
89
+ })
90
+
91
+ test("handles HTTP error codes (404)", async () => {
92
+ global.fetch.mockResolvedValue({
93
+ ok: false,
94
+ status: 404,
95
+ statusText: "Not Found",
96
+ text: () => Promise.resolve("")
97
+ })
98
+
99
+ await expect(execute({ adapterConfig: { url: "u" } }, {}, {}))
100
+ .rejects.toMatchObject({
101
+ code: 92,
102
+ type: "resource_not_found",
103
+ recoverable: false
104
+ })
105
+ })
106
+
107
+ test("handles fetch text failure in error path", async () => {
108
+ global.fetch.mockResolvedValue({
109
+ ok: false,
110
+ status: 400,
111
+ statusText: "Bad Request",
112
+ text: () => Promise.reject("cant-read-body")
113
+ })
114
+
115
+ await expect(execute({ adapterConfig: { url: "u" } }, {}, {}))
116
+ .rejects.toThrow(/HTTP request failed: 400 Bad Request/)
117
+ })
118
+ })
@@ -0,0 +1,82 @@
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 writeFakeJustBinary(dir) {
29
+ const bin = path.join(dir, "just")
30
+ fs.writeFileSync(bin, [
31
+ "#!/usr/bin/env node",
32
+ "const args = process.argv.slice(2);",
33
+ "if (args[0] === '--version') { console.log('just 1.39.0-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("just plugin", () => {
41
+ const fakeDir = fs.mkdtempSync(path.join(os.tmpdir(), "dcli-just-"))
42
+ const tempHome = fs.mkdtempSync(path.join(os.tmpdir(), "dcli-home-just-"))
43
+ writeFakeJustBinary(fakeDir)
44
+ const env = { ...process.env, PATH: `${fakeDir}:${process.env.PATH || ""}`, SUPERCLI_HOME: tempHome }
45
+
46
+ beforeAll(() => {
47
+ runNoServer("plugins install ./plugins/just --on-conflict replace --json", { env })
48
+ })
49
+
50
+ afterAll(() => {
51
+ runNoServer("plugins remove just --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("just cli version --json", { env })
58
+ expect(r.ok).toBe(true)
59
+ const data = JSON.parse(r.output)
60
+ expect(data.command).toBe("just.cli.version")
61
+ expect(data.data.raw).toBe("just 1.39.0-test")
62
+ })
63
+
64
+ test("supports namespace passthrough", () => {
65
+ const r = runNoServer("just --list --justfile ./justfile --json", { env })
66
+ expect(r.ok).toBe(true)
67
+ const data = JSON.parse(r.output)
68
+ expect(data.command).toBe("just.passthrough")
69
+ expect(data.data.args).toContain("--list")
70
+ expect(data.data.args).toContain("--justfile")
71
+ expect(data.data.args).toContain("./justfile")
72
+ expect(data.data.args).toContain("--json")
73
+ })
74
+
75
+ test("doctor reports just dependency as healthy", () => {
76
+ const r = runNoServer("plugins doctor just --json", { env })
77
+ expect(r.ok).toBe(true)
78
+ const data = JSON.parse(r.output)
79
+ expect(data.ok).toBe(true)
80
+ expect(data.checks.some(c => c.type === "binary" && c.binary === "just" && c.ok === true)).toBe(true)
81
+ })
82
+ })
@@ -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 writeFakeKubectlBinary(dir) {
29
+ const bin = path.join(dir, "kubectl")
30
+ fs.writeFileSync(bin, [
31
+ "#!/usr/bin/env node",
32
+ "const args = process.argv.slice(2);",
33
+ "if (args[0] === 'version' && args.includes('--client')) { console.log('Client Version: v1.35.0-test'); process.exit(0); }",
34
+ "if (args[0] === 'config' && args[1] === 'current-context') { console.log('mock-context'); 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("kubectl plugin", () => {
42
+ const fakeDir = fs.mkdtempSync(path.join(os.tmpdir(), "dcli-kubectl-"))
43
+ const tempHome = fs.mkdtempSync(path.join(os.tmpdir(), "dcli-home-kubectl-"))
44
+ writeFakeKubectlBinary(fakeDir)
45
+ const env = { ...process.env, PATH: `${fakeDir}:${process.env.PATH || ""}`, SUPERCLI_HOME: tempHome }
46
+
47
+ beforeAll(() => {
48
+ runNoServer("plugins install ./plugins/kubectl --on-conflict replace --json", { env })
49
+ })
50
+
51
+ afterAll(() => {
52
+ runNoServer("plugins remove kubectl --json", { env })
53
+ fs.rmSync(fakeDir, { recursive: true, force: true })
54
+ fs.rmSync(tempHome, { recursive: true, force: true })
55
+ })
56
+
57
+ test("routes current-context wrapped command", () => {
58
+ const r = runNoServer("kubectl config current-context --json", { env })
59
+ expect(r.ok).toBe(true)
60
+ const data = JSON.parse(r.output)
61
+ expect(data.command).toBe("kubectl.config.current-context")
62
+ expect(data.data.raw).toBe("mock-context")
63
+ })
64
+
65
+ test("supports namespace passthrough", () => {
66
+ const r = runNoServer("kubectl get pods -o json", { env })
67
+ expect(r.ok).toBe(true)
68
+ const data = JSON.parse(r.output)
69
+ expect(data.command).toBe("kubectl.passthrough")
70
+ expect(data.data.args[0]).toBe("get")
71
+ expect(data.data.args[1]).toBe("pods")
72
+ expect(data.data.args).toContain("-o")
73
+ expect(data.data.args).toContain("json")
74
+ })
75
+
76
+ test("doctor reports kubectl dependency as healthy", () => {
77
+ const r = runNoServer("plugins doctor kubectl --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 === "kubectl" && 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 writeFakeLinearBinary(dir) {
29
+ const bin = path.join(dir, "linear")
30
+ fs.writeFileSync(bin, [
31
+ "#!/usr/bin/env node",
32
+ "const args = process.argv.slice(2);",
33
+ "if (args.includes('--version')) { console.log('linear 0.1.0-test'); process.exit(0); }",
34
+ "if (args[0] === 'auth' && args[1] === 'whoami') { console.log('mock-linear-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("linear plugin", () => {
42
+ const fakeDir = fs.mkdtempSync(path.join(os.tmpdir(), "dcli-linear-"))
43
+ const tempHome = fs.mkdtempSync(path.join(os.tmpdir(), "dcli-home-linear-"))
44
+ writeFakeLinearBinary(fakeDir)
45
+ const env = { ...process.env, PATH: `${fakeDir}:${process.env.PATH || ""}`, SUPERCLI_HOME: tempHome }
46
+
47
+ beforeAll(() => {
48
+ runNoServer("plugins install ./plugins/linear --on-conflict replace --json", { env })
49
+ })
50
+
51
+ afterAll(() => {
52
+ runNoServer("plugins remove linear --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("linear account whoami", { env })
59
+ expect(r.ok).toBe(true)
60
+ const data = JSON.parse(r.output)
61
+ expect(data.command).toBe("linear.account.whoami")
62
+ expect(data.data.raw).toBe("mock-linear-user")
63
+ })
64
+
65
+ test("supports namespace passthrough", () => {
66
+ const r = runNoServer("linear issue list", { env })
67
+ expect(r.ok).toBe(true)
68
+ const data = JSON.parse(r.output)
69
+ expect(data.command).toBe("linear.passthrough")
70
+ expect(data.data.args[0]).toBe("issue")
71
+ expect(data.data.args[1]).toBe("list")
72
+ })
73
+
74
+ test("doctor reports linear dependency as healthy", () => {
75
+ const r = runNoServer("plugins doctor linear --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 === "linear" && c.ok === true)).toBe(true)
80
+ })
81
+ })
@@ -0,0 +1,187 @@
1
+ const { execute } = require("../cli/adapters/mcp")
2
+ const { spawn } = require("child_process")
3
+ const EventEmitter = require("events")
4
+
5
+ jest.mock("child_process")
6
+
7
+ describe("mcp adapter", () => {
8
+ beforeEach(() => {
9
+ jest.clearAllMocks()
10
+ global.fetch = jest.fn()
11
+ })
12
+
13
+ test("throws if tool or source missing", async () => {
14
+ await expect(execute({ adapterConfig: {} }, {}, {})).rejects.toThrow(/requires 'tool'/)
15
+ await expect(execute({ adapterConfig: { tool: "t" } }, {}, {})).rejects.toThrow(/one of: 'server', 'url', or 'command'/)
16
+ })
17
+
18
+ describe("stdio source", () => {
19
+ let mockChild
20
+
21
+ beforeEach(() => {
22
+ mockChild = new EventEmitter()
23
+ mockChild.stdout = new EventEmitter()
24
+ mockChild.stdout.setEncoding = jest.fn()
25
+ mockChild.stderr = new EventEmitter()
26
+ mockChild.stderr.setEncoding = jest.fn()
27
+ mockChild.stdin = { write: jest.fn(), end: jest.fn() }
28
+ mockChild.kill = jest.fn()
29
+ spawn.mockReturnValue(mockChild)
30
+ })
31
+
32
+ test("successfully calls stdio tool", async () => {
33
+ const promise = execute({
34
+ adapterConfig: { tool: "calc", command: "node", commandArgs: ["tool.js"] }
35
+ }, { x: 1 }, {})
36
+
37
+ mockChild.stdout.emit("data", '{"result": 42}')
38
+ mockChild.emit("close", 0)
39
+
40
+ const result = await promise
41
+ expect(result).toEqual({ result: 42 })
42
+ expect(mockChild.stdin.write).toHaveBeenCalledWith(expect.stringContaining('"tool":"calc"'))
43
+ })
44
+
45
+ test("handles stdio tool error exit", async () => {
46
+ const promise = execute({
47
+ adapterConfig: { tool: "calc", command: "node" }
48
+ }, {}, {})
49
+
50
+ mockChild.stderr.emit("data", "fatal error")
51
+ mockChild.emit("close", 1)
52
+
53
+ await expect(promise).rejects.toMatchObject({
54
+ message: expect.stringContaining("exited with code 1: fatal error")
55
+ })
56
+ })
57
+
58
+ test("handles stdio tool crash", async () => {
59
+ const promise = execute({
60
+ adapterConfig: { tool: "calc", command: "node" }
61
+ }, {}, {})
62
+
63
+ mockChild.emit("error", new Error("spawn error"))
64
+
65
+ await expect(promise).rejects.toMatchObject({
66
+ message: expect.stringContaining("Failed to start MCP stdio command: spawn error")
67
+ })
68
+ })
69
+
70
+ test("handles invalid JSON response", async () => {
71
+ const promise = execute({
72
+ adapterConfig: { tool: "calc", command: "node" }
73
+ }, {}, {})
74
+
75
+ mockChild.stdout.emit("data", "not-json")
76
+ mockChild.emit("close", 0)
77
+
78
+ await expect(promise).rejects.toMatchObject({
79
+ message: expect.stringContaining("response is not valid JSON")
80
+ })
81
+ })
82
+
83
+ test("handles timeout", async () => {
84
+ jest.useFakeTimers()
85
+ const promise = execute({
86
+ adapterConfig: { tool: "calc", command: "node", timeout_ms: 100 }
87
+ }, {}, {})
88
+
89
+ jest.advanceTimersByTime(101)
90
+
91
+ await expect(promise).rejects.toMatchObject({
92
+ message: expect.stringContaining("timed out after 100ms")
93
+ })
94
+ jest.useRealTimers()
95
+ })
96
+ })
97
+
98
+ describe("http source", () => {
99
+ test("successfully calls http mcp tool (direct url)", async () => {
100
+ global.fetch.mockResolvedValue({
101
+ ok: true,
102
+ json: () => Promise.resolve({ data: "ok" })
103
+ })
104
+
105
+ const result = await execute({
106
+ adapterConfig: { tool: "t1", url: "http://mcp.local" }
107
+ }, { arg1: "v1" }, {})
108
+
109
+ expect(global.fetch).toHaveBeenCalledWith(
110
+ "http://mcp.local/tool",
111
+ expect.objectContaining({
112
+ method: "POST",
113
+ body: JSON.stringify({ tool: "t1", input: { arg1: "v1" } })
114
+ })
115
+ )
116
+ expect(result).toEqual({ data: "ok" })
117
+ })
118
+
119
+ test("resolves server url from local context", async () => {
120
+ global.fetch.mockResolvedValue({
121
+ ok: true,
122
+ json: () => Promise.resolve({ ok: true })
123
+ })
124
+
125
+ const context = {
126
+ config: {
127
+ mcp_servers: [{ name: "s1", url: "http://server1" }]
128
+ }
129
+ }
130
+
131
+ await execute({
132
+ adapterConfig: { tool: "t1", server: "s1" }
133
+ }, {}, context)
134
+
135
+ expect(global.fetch).toHaveBeenCalledWith("http://server1/tool", expect.anything())
136
+ })
137
+
138
+ test("resolves server url from remote server", async () => {
139
+ global.fetch
140
+ .mockResolvedValueOnce({ // resolveHttpServerUrl fetch
141
+ ok: true,
142
+ json: () => Promise.resolve([{ name: "s2", url: "http://remote-s2" }])
143
+ })
144
+ .mockResolvedValueOnce({ // tool call fetch
145
+ ok: true,
146
+ json: () => Promise.resolve({ ok: true })
147
+ })
148
+
149
+ const context = { server: "http://api.test" }
150
+
151
+ await execute({
152
+ adapterConfig: { tool: "t1", server: "s2" }
153
+ }, {}, context)
154
+
155
+ expect(global.fetch).toHaveBeenCalledWith("http://api.test/api/mcp?format=json")
156
+ expect(global.fetch).toHaveBeenCalledWith("http://remote-s2/tool", expect.anything())
157
+ })
158
+
159
+ test("throws if server not found", async () => {
160
+ const context = { config: { mcp_servers: [] } }
161
+ await expect(execute({
162
+ adapterConfig: { tool: "t1", server: "missing" }
163
+ }, {}, context)).rejects.toThrow(/not found in local config/)
164
+ })
165
+
166
+ test("handles fetch remote mcp list failure", async () => {
167
+ global.fetch.mockResolvedValue({ ok: false, status: 500 })
168
+ const context = { server: "http://api.test" }
169
+
170
+ await expect(execute({
171
+ adapterConfig: { tool: "t1", server: "s1" }
172
+ }, {}, context)).rejects.toMatchObject({ code: 105 })
173
+ })
174
+
175
+ test("handles tool call failure", async () => {
176
+ global.fetch.mockResolvedValue({
177
+ ok: false,
178
+ status: 400,
179
+ text: () => Promise.resolve("bad tool")
180
+ })
181
+
182
+ await expect(execute({
183
+ adapterConfig: { tool: "t1", url: "http://u" }
184
+ }, {}, {})).rejects.toThrow(/MCP tool call failed: 400 bad tool/)
185
+ })
186
+ })
187
+ })