expandai 0.0.4 → 0.0.6

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 (321) hide show
  1. package/README.md +2 -2
  2. package/build/{esm/Install/Opencode → assets/opencode}/expandai-webfetch.js +22 -4
  3. package/build/dts/Mcp/RequestHeaders.d.ts +0 -1
  4. package/build/dts/Mcp/Server.d.ts +1 -2
  5. package/build/dts/index.d.ts +0 -1
  6. package/build/esm/index.js +193 -2
  7. package/build/esm/main.js +691 -200
  8. package/build/skills/expandai/SKILL.md +100 -8
  9. package/package.json +20 -12
  10. package/build/dts/ApiClient.d.ts +0 -165
  11. package/build/dts/ApiClient.d.ts.map +0 -1
  12. package/build/dts/Auth/Domain.d.ts +0 -95
  13. package/build/dts/Auth/Domain.d.ts.map +0 -1
  14. package/build/dts/Auth/ManualAuthorization.d.ts +0 -107
  15. package/build/dts/Auth/ManualAuthorization.d.ts.map +0 -1
  16. package/build/dts/Auth/PendingInit.d.ts +0 -35
  17. package/build/dts/Auth/PendingInit.d.ts.map +0 -1
  18. package/build/dts/Auth/Pkce.d.ts +0 -10
  19. package/build/dts/Auth/Pkce.d.ts.map +0 -1
  20. package/build/dts/Auth/SetupGrantAuthorization.d.ts +0 -34
  21. package/build/dts/Auth/SetupGrantAuthorization.d.ts.map +0 -1
  22. package/build/dts/Authentication.d.ts +0 -24
  23. package/build/dts/Authentication.d.ts.map +0 -1
  24. package/build/dts/BrowserOAuth.d.ts +0 -99
  25. package/build/dts/BrowserOAuth.d.ts.map +0 -1
  26. package/build/dts/CommandPath.d.ts +0 -2
  27. package/build/dts/CommandPath.d.ts.map +0 -1
  28. package/build/dts/Commands/Fetch.d.ts +0 -30
  29. package/build/dts/Commands/Fetch.d.ts.map +0 -1
  30. package/build/dts/Commands/Hook.d.ts +0 -17
  31. package/build/dts/Commands/Hook.d.ts.map +0 -1
  32. package/build/dts/Commands/Init.d.ts +0 -55
  33. package/build/dts/Commands/Init.d.ts.map +0 -1
  34. package/build/dts/Commands/Mcp.d.ts +0 -11
  35. package/build/dts/Commands/Mcp.d.ts.map +0 -1
  36. package/build/dts/Commands/McpRun.d.ts +0 -5
  37. package/build/dts/Commands/McpRun.d.ts.map +0 -1
  38. package/build/dts/Commands/Search.d.ts +0 -21
  39. package/build/dts/Commands/Search.d.ts.map +0 -1
  40. package/build/dts/Commands/Skill.d.ts +0 -12
  41. package/build/dts/Commands/Skill.d.ts.map +0 -1
  42. package/build/dts/Commands/Status.d.ts +0 -27
  43. package/build/dts/Commands/Status.d.ts.map +0 -1
  44. package/build/dts/Commands/Telemetry.d.ts +0 -7
  45. package/build/dts/Commands/Telemetry.d.ts.map +0 -1
  46. package/build/dts/Commands/Uninstall.d.ts +0 -9
  47. package/build/dts/Commands/Uninstall.d.ts.map +0 -1
  48. package/build/dts/Config.d.ts +0 -196
  49. package/build/dts/Config.d.ts.map +0 -1
  50. package/build/dts/Hook/Claude.d.ts +0 -13
  51. package/build/dts/Hook/Claude.d.ts.map +0 -1
  52. package/build/dts/Hook/Errors.d.ts +0 -20
  53. package/build/dts/Hook/Errors.d.ts.map +0 -1
  54. package/build/dts/Hook/Opencode.d.ts +0 -13
  55. package/build/dts/Hook/Opencode.d.ts.map +0 -1
  56. package/build/dts/Hook/SnapshotNote.d.ts +0 -12
  57. package/build/dts/Hook/SnapshotNote.d.ts.map +0 -1
  58. package/build/dts/Init/ClaudeAdapter.d.ts +0 -61
  59. package/build/dts/Init/ClaudeAdapter.d.ts.map +0 -1
  60. package/build/dts/Init/ClaudeProfile.d.ts +0 -77
  61. package/build/dts/Init/ClaudeProfile.d.ts.map +0 -1
  62. package/build/dts/Init/Domain.d.ts +0 -139
  63. package/build/dts/Init/Domain.d.ts.map +0 -1
  64. package/build/dts/Init/HarnessAdapter.d.ts +0 -33
  65. package/build/dts/Init/HarnessAdapter.d.ts.map +0 -1
  66. package/build/dts/Init/HarnessDetector.d.ts +0 -19
  67. package/build/dts/Init/HarnessDetector.d.ts.map +0 -1
  68. package/build/dts/Init/HarnessRegistry.d.ts +0 -50
  69. package/build/dts/Init/HarnessRegistry.d.ts.map +0 -1
  70. package/build/dts/Init/MutationTransaction.d.ts +0 -96
  71. package/build/dts/Init/MutationTransaction.d.ts.map +0 -1
  72. package/build/dts/Init/Service.d.ts +0 -72
  73. package/build/dts/Init/Service.d.ts.map +0 -1
  74. package/build/dts/Init/StandardAdapters.d.ts +0 -42
  75. package/build/dts/Init/StandardAdapters.d.ts.map +0 -1
  76. package/build/dts/Init/Status.d.ts +0 -149
  77. package/build/dts/Init/Status.d.ts.map +0 -1
  78. package/build/dts/Install/Claude/Hook.d.ts +0 -88
  79. package/build/dts/Install/Claude/Hook.d.ts.map +0 -1
  80. package/build/dts/Install/Claude/Mcp.d.ts +0 -48
  81. package/build/dts/Install/Claude/Mcp.d.ts.map +0 -1
  82. package/build/dts/Install/Claude/Skill.d.ts +0 -23
  83. package/build/dts/Install/Claude/Skill.d.ts.map +0 -1
  84. package/build/dts/Install/Codex/Mcp.d.ts +0 -45
  85. package/build/dts/Install/Codex/Mcp.d.ts.map +0 -1
  86. package/build/dts/Install/Codex/Skill.d.ts +0 -22
  87. package/build/dts/Install/Codex/Skill.d.ts.map +0 -1
  88. package/build/dts/Install/Cursor/Mcp.d.ts +0 -45
  89. package/build/dts/Install/Cursor/Mcp.d.ts.map +0 -1
  90. package/build/dts/Install/Cursor/Skill.d.ts +0 -29
  91. package/build/dts/Install/Cursor/Skill.d.ts.map +0 -1
  92. package/build/dts/Install/Domain.d.ts +0 -103
  93. package/build/dts/Install/Domain.d.ts.map +0 -1
  94. package/build/dts/Install/Hook.d.ts +0 -30
  95. package/build/dts/Install/Hook.d.ts.map +0 -1
  96. package/build/dts/Install/Mcp.d.ts +0 -28
  97. package/build/dts/Install/Mcp.d.ts.map +0 -1
  98. package/build/dts/Install/Opencode/Hook.d.ts +0 -24
  99. package/build/dts/Install/Opencode/Hook.d.ts.map +0 -1
  100. package/build/dts/Install/Opencode/Mcp.d.ts +0 -47
  101. package/build/dts/Install/Opencode/Mcp.d.ts.map +0 -1
  102. package/build/dts/Install/Opencode/Skill.d.ts +0 -26
  103. package/build/dts/Install/Opencode/Skill.d.ts.map +0 -1
  104. package/build/dts/Install/Skill.d.ts +0 -32
  105. package/build/dts/Install/Skill.d.ts.map +0 -1
  106. package/build/dts/Install/Uninstall.d.ts +0 -32
  107. package/build/dts/Install/Uninstall.d.ts.map +0 -1
  108. package/build/dts/MarkdownAnsi.d.ts +0 -29
  109. package/build/dts/MarkdownAnsi.d.ts.map +0 -1
  110. package/build/dts/Mcp/ExpandDocs.d.ts +0 -19
  111. package/build/dts/Mcp/ExpandDocs.d.ts.map +0 -1
  112. package/build/dts/Mcp/RequestHeaders.d.ts.map +0 -1
  113. package/build/dts/Mcp/Server.d.ts.map +0 -1
  114. package/build/dts/Mcp/Toolkit.d.ts +0 -3
  115. package/build/dts/Mcp/Toolkit.d.ts.map +0 -1
  116. package/build/dts/Mcp/tools/Citations.d.ts +0 -266
  117. package/build/dts/Mcp/tools/Citations.d.ts.map +0 -1
  118. package/build/dts/Mcp/tools/Fetch.d.ts +0 -168
  119. package/build/dts/Mcp/tools/Fetch.d.ts.map +0 -1
  120. package/build/dts/SecureFile.d.ts +0 -23
  121. package/build/dts/SecureFile.d.ts.map +0 -1
  122. package/build/dts/Snippets.d.ts +0 -18
  123. package/build/dts/Snippets.d.ts.map +0 -1
  124. package/build/dts/Telemetry.d.ts +0 -30
  125. package/build/dts/Telemetry.d.ts.map +0 -1
  126. package/build/dts/Version.d.ts +0 -2
  127. package/build/dts/Version.d.ts.map +0 -1
  128. package/build/dts/Xdg.d.ts +0 -11
  129. package/build/dts/Xdg.d.ts.map +0 -1
  130. package/build/dts/index.d.ts.map +0 -1
  131. package/build/dts/main.d.ts +0 -3
  132. package/build/dts/main.d.ts.map +0 -1
  133. package/build/esm/ApiClient.js +0 -305
  134. package/build/esm/ApiClient.js.map +0 -1
  135. package/build/esm/Auth/Domain.js +0 -56
  136. package/build/esm/Auth/Domain.js.map +0 -1
  137. package/build/esm/Auth/ManualAuthorization.js +0 -253
  138. package/build/esm/Auth/ManualAuthorization.js.map +0 -1
  139. package/build/esm/Auth/PendingInit.js +0 -267
  140. package/build/esm/Auth/PendingInit.js.map +0 -1
  141. package/build/esm/Auth/Pkce.js +0 -23
  142. package/build/esm/Auth/Pkce.js.map +0 -1
  143. package/build/esm/Auth/SetupGrantAuthorization.js +0 -91
  144. package/build/esm/Auth/SetupGrantAuthorization.js.map +0 -1
  145. package/build/esm/Authentication.js +0 -143
  146. package/build/esm/Authentication.js.map +0 -1
  147. package/build/esm/BrowserOAuth.js +0 -251
  148. package/build/esm/BrowserOAuth.js.map +0 -1
  149. package/build/esm/CommandPath.js +0 -31
  150. package/build/esm/CommandPath.js.map +0 -1
  151. package/build/esm/Commands/Fetch.js +0 -119
  152. package/build/esm/Commands/Fetch.js.map +0 -1
  153. package/build/esm/Commands/Hook.js +0 -20
  154. package/build/esm/Commands/Hook.js.map +0 -1
  155. package/build/esm/Commands/Init.js +0 -371
  156. package/build/esm/Commands/Init.js.map +0 -1
  157. package/build/esm/Commands/Mcp.js +0 -19
  158. package/build/esm/Commands/Mcp.js.map +0 -1
  159. package/build/esm/Commands/McpRun.js +0 -15
  160. package/build/esm/Commands/McpRun.js.map +0 -1
  161. package/build/esm/Commands/Search.js +0 -46
  162. package/build/esm/Commands/Search.js.map +0 -1
  163. package/build/esm/Commands/Skill.js +0 -17
  164. package/build/esm/Commands/Skill.js.map +0 -1
  165. package/build/esm/Commands/Status.js +0 -186
  166. package/build/esm/Commands/Status.js.map +0 -1
  167. package/build/esm/Commands/Telemetry.js +0 -23
  168. package/build/esm/Commands/Telemetry.js.map +0 -1
  169. package/build/esm/Commands/Uninstall.js +0 -28
  170. package/build/esm/Commands/Uninstall.js.map +0 -1
  171. package/build/esm/Config.js +0 -619
  172. package/build/esm/Config.js.map +0 -1
  173. package/build/esm/Hook/Claude.js +0 -74
  174. package/build/esm/Hook/Claude.js.map +0 -1
  175. package/build/esm/Hook/Errors.js +0 -18
  176. package/build/esm/Hook/Errors.js.map +0 -1
  177. package/build/esm/Hook/Opencode.js +0 -50
  178. package/build/esm/Hook/Opencode.js.map +0 -1
  179. package/build/esm/Hook/SnapshotNote.js +0 -65
  180. package/build/esm/Hook/SnapshotNote.js.map +0 -1
  181. package/build/esm/Init/ClaudeAdapter.js +0 -137
  182. package/build/esm/Init/ClaudeAdapter.js.map +0 -1
  183. package/build/esm/Init/ClaudeProfile.js +0 -344
  184. package/build/esm/Init/ClaudeProfile.js.map +0 -1
  185. package/build/esm/Init/Domain.js +0 -118
  186. package/build/esm/Init/Domain.js.map +0 -1
  187. package/build/esm/Init/HarnessAdapter.js +0 -21
  188. package/build/esm/Init/HarnessAdapter.js.map +0 -1
  189. package/build/esm/Init/HarnessDetector.js +0 -65
  190. package/build/esm/Init/HarnessDetector.js.map +0 -1
  191. package/build/esm/Init/HarnessRegistry.js +0 -58
  192. package/build/esm/Init/HarnessRegistry.js.map +0 -1
  193. package/build/esm/Init/MutationTransaction.js +0 -494
  194. package/build/esm/Init/MutationTransaction.js.map +0 -1
  195. package/build/esm/Init/Service.js +0 -319
  196. package/build/esm/Init/Service.js.map +0 -1
  197. package/build/esm/Init/StandardAdapters.js +0 -116
  198. package/build/esm/Init/StandardAdapters.js.map +0 -1
  199. package/build/esm/Init/Status.js +0 -215
  200. package/build/esm/Init/Status.js.map +0 -1
  201. package/build/esm/Install/Claude/Hook.js +0 -77
  202. package/build/esm/Install/Claude/Hook.js.map +0 -1
  203. package/build/esm/Install/Claude/Mcp.js +0 -101
  204. package/build/esm/Install/Claude/Mcp.js.map +0 -1
  205. package/build/esm/Install/Claude/Skill.js +0 -56
  206. package/build/esm/Install/Claude/Skill.js.map +0 -1
  207. package/build/esm/Install/Codex/Mcp.js +0 -97
  208. package/build/esm/Install/Codex/Mcp.js.map +0 -1
  209. package/build/esm/Install/Codex/Skill.js +0 -33
  210. package/build/esm/Install/Codex/Skill.js.map +0 -1
  211. package/build/esm/Install/Cursor/Mcp.js +0 -74
  212. package/build/esm/Install/Cursor/Mcp.js.map +0 -1
  213. package/build/esm/Install/Cursor/Skill.js +0 -40
  214. package/build/esm/Install/Cursor/Skill.js.map +0 -1
  215. package/build/esm/Install/Domain.js +0 -162
  216. package/build/esm/Install/Domain.js.map +0 -1
  217. package/build/esm/Install/Hook.js +0 -38
  218. package/build/esm/Install/Hook.js.map +0 -1
  219. package/build/esm/Install/Mcp.js +0 -40
  220. package/build/esm/Install/Mcp.js.map +0 -1
  221. package/build/esm/Install/Opencode/Hook.js +0 -44
  222. package/build/esm/Install/Opencode/Hook.js.map +0 -1
  223. package/build/esm/Install/Opencode/Mcp.js +0 -76
  224. package/build/esm/Install/Opencode/Mcp.js.map +0 -1
  225. package/build/esm/Install/Opencode/Skill.js +0 -37
  226. package/build/esm/Install/Opencode/Skill.js.map +0 -1
  227. package/build/esm/Install/Skill.js +0 -41
  228. package/build/esm/Install/Skill.js.map +0 -1
  229. package/build/esm/Install/Uninstall.js +0 -49
  230. package/build/esm/Install/Uninstall.js.map +0 -1
  231. package/build/esm/MarkdownAnsi.js +0 -142
  232. package/build/esm/MarkdownAnsi.js.map +0 -1
  233. package/build/esm/Mcp/ExpandDocs.js +0 -56
  234. package/build/esm/Mcp/ExpandDocs.js.map +0 -1
  235. package/build/esm/Mcp/RequestHeaders.js +0 -13
  236. package/build/esm/Mcp/RequestHeaders.js.map +0 -1
  237. package/build/esm/Mcp/Server.js +0 -12
  238. package/build/esm/Mcp/Server.js.map +0 -1
  239. package/build/esm/Mcp/Toolkit.js +0 -16
  240. package/build/esm/Mcp/Toolkit.js.map +0 -1
  241. package/build/esm/Mcp/tools/Citations.js +0 -193
  242. package/build/esm/Mcp/tools/Citations.js.map +0 -1
  243. package/build/esm/Mcp/tools/Fetch.js +0 -165
  244. package/build/esm/Mcp/tools/Fetch.js.map +0 -1
  245. package/build/esm/SecureFile.js +0 -36
  246. package/build/esm/SecureFile.js.map +0 -1
  247. package/build/esm/Snippets.js +0 -29
  248. package/build/esm/Snippets.js.map +0 -1
  249. package/build/esm/Telemetry.js +0 -157
  250. package/build/esm/Telemetry.js.map +0 -1
  251. package/build/esm/Version.js +0 -4
  252. package/build/esm/Version.js.map +0 -1
  253. package/build/esm/Xdg.js +0 -15
  254. package/build/esm/Xdg.js.map +0 -1
  255. package/build/esm/index.js.map +0 -1
  256. package/build/esm/main.js.map +0 -1
  257. package/src/ApiClient.ts +0 -450
  258. package/src/Auth/Domain.ts +0 -63
  259. package/src/Auth/ManualAuthorization.ts +0 -296
  260. package/src/Auth/PendingInit.ts +0 -338
  261. package/src/Auth/Pkce.ts +0 -26
  262. package/src/Auth/SetupGrantAuthorization.ts +0 -116
  263. package/src/Authentication.ts +0 -202
  264. package/src/BrowserOAuth.ts +0 -359
  265. package/src/CommandPath.ts +0 -30
  266. package/src/Commands/Fetch.ts +0 -188
  267. package/src/Commands/Hook.ts +0 -48
  268. package/src/Commands/Init.ts +0 -489
  269. package/src/Commands/Mcp.ts +0 -30
  270. package/src/Commands/McpRun.ts +0 -25
  271. package/src/Commands/Search.ts +0 -97
  272. package/src/Commands/Skill.ts +0 -30
  273. package/src/Commands/Status.ts +0 -228
  274. package/src/Commands/Telemetry.ts +0 -42
  275. package/src/Commands/Uninstall.ts +0 -38
  276. package/src/Config.ts +0 -840
  277. package/src/Hook/Claude.ts +0 -81
  278. package/src/Hook/Errors.ts +0 -19
  279. package/src/Hook/Opencode.ts +0 -58
  280. package/src/Hook/SnapshotNote.ts +0 -66
  281. package/src/Init/ClaudeAdapter.ts +0 -191
  282. package/src/Init/ClaudeProfile.ts +0 -453
  283. package/src/Init/Domain.ts +0 -153
  284. package/src/Init/HarnessAdapter.ts +0 -35
  285. package/src/Init/HarnessDetector.ts +0 -77
  286. package/src/Init/HarnessRegistry.ts +0 -87
  287. package/src/Init/MutationTransaction.ts +0 -747
  288. package/src/Init/Service.ts +0 -385
  289. package/src/Init/StandardAdapters.ts +0 -141
  290. package/src/Init/Status.ts +0 -253
  291. package/src/Install/Claude/Hook.ts +0 -123
  292. package/src/Install/Claude/Mcp.ts +0 -129
  293. package/src/Install/Claude/Skill.ts +0 -77
  294. package/src/Install/Codex/Mcp.ts +0 -114
  295. package/src/Install/Codex/Skill.ts +0 -44
  296. package/src/Install/Cursor/Mcp.ts +0 -85
  297. package/src/Install/Cursor/Skill.ts +0 -51
  298. package/src/Install/Domain.ts +0 -221
  299. package/src/Install/Hook.ts +0 -50
  300. package/src/Install/Mcp.ts +0 -71
  301. package/src/Install/Opencode/Hook.ts +0 -55
  302. package/src/Install/Opencode/Mcp.ts +0 -87
  303. package/src/Install/Opencode/Skill.ts +0 -48
  304. package/src/Install/Opencode/expandai-webfetch.d.ts +0 -23
  305. package/src/Install/Opencode/expandai-webfetch.js +0 -56
  306. package/src/Install/Skill.ts +0 -57
  307. package/src/Install/Uninstall.ts +0 -82
  308. package/src/MarkdownAnsi.ts +0 -162
  309. package/src/Mcp/ExpandDocs.ts +0 -62
  310. package/src/Mcp/RequestHeaders.ts +0 -16
  311. package/src/Mcp/Server.ts +0 -15
  312. package/src/Mcp/Toolkit.ts +0 -21
  313. package/src/Mcp/tools/Citations.ts +0 -219
  314. package/src/Mcp/tools/Fetch.ts +0 -203
  315. package/src/SecureFile.ts +0 -70
  316. package/src/Snippets.ts +0 -39
  317. package/src/Telemetry.ts +0 -186
  318. package/src/Version.ts +0 -3
  319. package/src/Xdg.ts +0 -18
  320. package/src/index.ts +0 -1
  321. package/src/main.ts +0 -305
@@ -1,55 +0,0 @@
1
- import { homedir } from 'node:os'
2
- import { FileSystem, Path } from '@effect/platform'
3
- import { NodeFileSystem, NodePath } from '@effect/platform-node'
4
- import { Config, Console, Effect, Match } from 'effect'
5
- import type { InstallScope } from '../Mcp.js'
6
-
7
- /**
8
- * OpenCode hook = a JS plugin file at `.opencode/plugins/` (project) or
9
- * `~/.config/opencode/plugins/` (global). The plugin shells out to
10
- * `expandai hook opencode` so all fetch logic stays in the CLI.
11
- *
12
- * The plugin uses `tool.execute.after` (the only hook whose contract lets us
13
- * replace the rendered result via `output.output`); `tool.execute.before` is
14
- * limited to mutating tool args. See @opencode-ai/plugin's dist/index.d.ts.
15
- */
16
-
17
- export const PLUGIN_FILE = 'expandai-webfetch.js'
18
-
19
- export class OpencodeHook extends Effect.Service<OpencodeHook>()('@expandai/cli/Install/Opencode/Hook', {
20
- dependencies: [NodeFileSystem.layer, NodePath.layer],
21
- effect: Effect.gen(function* () {
22
- const fs = yield* FileSystem.FileSystem
23
- const path = yield* Path.Path
24
- const source = path.join(path.dirname(yield* path.fromFileUrl(new URL(import.meta.url))), PLUGIN_FILE)
25
-
26
- const home = yield* Config.string('HOME').pipe(Config.withDefault(homedir()))
27
- const cwd = yield* Config.string('PWD').pipe(Config.withDefault(process.cwd()))
28
-
29
- const pathFor = Effect.fn(function* (scope: InstallScope) {
30
- return yield* Match.value(scope).pipe(
31
- Match.when('global', () => Effect.succeed(`${home}/.config/opencode/plugins/${PLUGIN_FILE}`)),
32
- Match.when('project', () => Effect.succeed(`${cwd}/.opencode/plugins/${PLUGIN_FILE}`)),
33
- Match.exhaustive,
34
- )
35
- })
36
-
37
- const install = Effect.fn('OpencodeHook.install')(function* (scope: InstallScope) {
38
- const target = yield* pathFor(scope)
39
- yield* fs.makeDirectory(path.dirname(target), { recursive: true })
40
- yield* fs.copyFile(source, target)
41
- yield* Console.log(`Installed expandai webfetch plugin at ${target}`)
42
- })
43
-
44
- const uninstall = Effect.fn('OpencodeHook.uninstall')(function* (scope: InstallScope) {
45
- const target = yield* pathFor(scope)
46
- const exists = yield* fs.exists(target)
47
- if (exists) {
48
- yield* fs.remove(target)
49
- yield* Console.log(`Removed expandai webfetch plugin from ${target}`)
50
- }
51
- })
52
-
53
- return { install, uninstall, pathFor } as const
54
- }),
55
- }) {}
@@ -1,87 +0,0 @@
1
- import { homedir } from 'node:os'
2
- import { Config, Console, Effect, Match, Record, Schema } from 'effect'
3
- import { CliInvocation, ConfigIo, McpParseError, McpWriteError, OpencodeConfigSchema, SERVER_KEY } from '../Domain.js'
4
- import type { InstallScope } from '../Mcp.js'
5
-
6
- /**
7
- * OpenCode MCP descriptor — its own shape (per opencode.ai/docs/mcp-servers):
8
- * top-level key is `mcp` (not `mcpServers`), `command` is an array (no
9
- * separate args), env is called `environment`, and `type` uses
10
- * `local` / `remote` rather than `stdio` / `http`.
11
- *
12
- * - global → `~/.config/opencode/opencode.json`
13
- * - project → `<cwd>/opencode.json`
14
- */
15
- export class Entry extends Schema.Class<Entry>('OpencodeMcpEntry')({
16
- type: Schema.Literal('local'),
17
- command: Schema.Array(Schema.String),
18
- environment: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.String })),
19
- enabled: Schema.optional(Schema.Boolean),
20
- }) {}
21
-
22
- export class OpencodeMcp extends Effect.Service<OpencodeMcp>()('@expandai/cli/Install/Opencode/Mcp', {
23
- dependencies: [ConfigIo.Default, CliInvocation.Default],
24
- effect: Effect.gen(function* () {
25
- const io = yield* ConfigIo
26
- const cli = yield* CliInvocation
27
- const home = yield* Config.string('HOME').pipe(Config.withDefault(homedir()))
28
- const cwd = yield* Config.string('PWD').pipe(Config.withDefault(process.cwd()))
29
-
30
- const pathFor = Effect.fn(function* (scope: InstallScope) {
31
- return yield* Match.value(scope).pipe(
32
- Match.when('global', () => Effect.succeed(`${home}/.config/opencode/opencode.json`)),
33
- Match.when('project', () => Effect.succeed(`${cwd}/opencode.json`)),
34
- Match.exhaustive,
35
- )
36
- })
37
-
38
- const readConfig = Effect.fn(function* (file: string) {
39
- return yield* io
40
- .read(file, OpencodeConfigSchema)
41
- .pipe(Effect.mapError((error) => new McpParseError({ path: file, error })))
42
- })
43
-
44
- const writeConfig = Effect.fn(function* (file: string, value: unknown) {
45
- yield* io
46
- .write(file, value, OpencodeConfigSchema)
47
- .pipe(Effect.mapError((error) => new McpWriteError({ path: file, error })))
48
- })
49
-
50
- const install = Effect.fn(function* (scope: InstallScope) {
51
- const file = yield* pathFor(scope)
52
- const config = yield* readConfig(file)
53
- const invocation = yield* cli.resolve()
54
- yield* writeConfig(file, {
55
- ...config,
56
- mcp: {
57
- ...config.mcp,
58
- [SERVER_KEY]: new Entry({
59
- type: 'local',
60
- command: [invocation.command, ...invocation.args, 'mcp', 'run'],
61
- environment: { EXPANDAI_HARNESS: 'opencode' },
62
- enabled: true,
63
- }),
64
- },
65
- })
66
- yield* Console.log(`Installed expandai MCP server into ${file}`)
67
- })
68
-
69
- const uninstall = Effect.fn(function* (scope: InstallScope) {
70
- const file = yield* pathFor(scope)
71
- const config = yield* readConfig(file)
72
- if (!(SERVER_KEY in config.mcp)) {
73
- return
74
- }
75
- yield* writeConfig(file, { ...config, mcp: Record.remove(config.mcp, SERVER_KEY) })
76
- yield* Console.log(`Removed expandai MCP server from ${file}`)
77
- })
78
-
79
- const isInstalled = Effect.fn(function* (scope: InstallScope) {
80
- const file = yield* pathFor(scope)
81
- const config = yield* readConfig(file)
82
- return SERVER_KEY in config.mcp
83
- })
84
-
85
- return { install, uninstall, isInstalled, pathFor } as const
86
- }),
87
- }) {}
@@ -1,48 +0,0 @@
1
- import { homedir } from 'node:os'
2
- import { Config, Effect, Match } from 'effect'
3
- import { SkillIo } from '../Domain.js'
4
- import type { InstallScope } from '../Mcp.js'
5
-
6
- /**
7
- * OpenCode skills are Claude-Code-compatible Markdown files. Per
8
- * opencode.ai/docs/skills, OpenCode auto-discovers them under either
9
- * `.opencode/skills/<name>/`, `.claude/skills/<name>/`, or
10
- * `.agents/skills/<name>/` (and the `~/.config/opencode/...` equivalents
11
- * for global). We ship to OpenCode-native paths.
12
- *
13
- * - global → `~/.config/opencode/skills/expandai/SKILL.md`
14
- * - project → `<cwd>/.opencode/skills/expandai/SKILL.md`
15
- */
16
-
17
- export const SKILL_NAME = 'expandai'
18
-
19
- export class OpencodeSkill extends Effect.Service<OpencodeSkill>()('@expandai/cli/Install/Opencode/Skill', {
20
- dependencies: [SkillIo.Default],
21
- effect: Effect.gen(function* () {
22
- const io = yield* SkillIo
23
- const home = yield* Config.string('HOME').pipe(Config.withDefault(homedir()))
24
- const cwd = yield* Config.string('PWD').pipe(Config.withDefault(process.cwd()))
25
-
26
- const pathFor = Effect.fn(function* (scope: InstallScope) {
27
- return yield* Match.value(scope).pipe(
28
- Match.when('global', () => Effect.succeed(`${home}/.config/opencode/skills/${SKILL_NAME}`)),
29
- Match.when('project', () => Effect.succeed(`${cwd}/.opencode/skills/${SKILL_NAME}`)),
30
- Match.exhaustive,
31
- )
32
- })
33
-
34
- const install = Effect.fn(function* (scope: InstallScope) {
35
- return yield* io.install(yield* pathFor(scope))
36
- })
37
-
38
- const uninstall = Effect.fn(function* (scope: InstallScope) {
39
- return yield* io.uninstall(yield* pathFor(scope))
40
- })
41
-
42
- const isCurrent = Effect.fn(function* (scope: InstallScope) {
43
- return yield* io.isCurrent(yield* pathFor(scope))
44
- })
45
-
46
- return { install, uninstall, isCurrent, pathFor } as const
47
- }),
48
- }) {}
@@ -1,23 +0,0 @@
1
- export interface OpencodeWebfetchPluginInput {
2
- readonly tool: string
3
- readonly sessionID?: string
4
- readonly callID?: string
5
- readonly args?: {
6
- readonly url?: string
7
- }
8
- }
9
-
10
- export interface OpencodeWebfetchPluginOutput {
11
- title?: string
12
- output?: string
13
- metadata?: unknown
14
- }
15
-
16
- export interface OpencodeWebfetchPluginHooks {
17
- readonly 'tool.execute.after': (
18
- input: OpencodeWebfetchPluginInput,
19
- output: OpencodeWebfetchPluginOutput,
20
- ) => Promise<void>
21
- }
22
-
23
- export declare const ExpandaiWebfetch: () => Promise<OpencodeWebfetchPluginHooks>
@@ -1,56 +0,0 @@
1
- // expandai OpenCode plugin - routes webfetch through the expandai CLI.
2
- // Generated by `expandai hook install opencode <scope>`.
3
- //
4
- // We hook `tool.execute.after` rather than `.before` because OpenCode's plugin
5
- // contract only lets `before` mutate args (`output.args`); replacing the
6
- // rendered result is only possible from `after`, where `output` is the tool
7
- // output object (`{ title, output, metadata }`). See @opencode-ai/plugin's
8
- // dist/index.d.ts.
9
- import { spawn } from 'node:child_process'
10
-
11
- const runHook = (command, args, payload) =>
12
- new Promise((resolve, reject) => {
13
- const child = spawn(command, args, {
14
- stdio: ['pipe', 'pipe', 'inherit'],
15
- env: { ...process.env, EXPANDAI_CLI_CONTEXT: 'agent', EXPANDAI_HARNESS: 'opencode' },
16
- })
17
- let stdout = ''
18
- child.stdout.setEncoding('utf8')
19
- child.stdout.on('data', (chunk) => {
20
- stdout += chunk
21
- })
22
- child.on('error', reject)
23
- child.on('close', (exitCode) => resolve({ exitCode, stdout }))
24
- // Writing to stdin of a binary that failed to spawn raises EPIPE; the
25
- // spawn failure itself is reported through the 'error' handler above.
26
- child.stdin.on('error', () => {})
27
- child.stdin.end(payload)
28
- })
29
-
30
- // Prefer the installed binary; fall back to `npx -y expandai` so the plugin
31
- // works for people who have not installed the expandai CLI globally.
32
- const runExpandai = (args, payload) =>
33
- runHook('expandai', args, payload).catch((error) =>
34
- error?.code === 'ENOENT' ? runHook('npx', ['-y', 'expandai', ...args], payload) : Promise.reject(error),
35
- )
36
-
37
- export const ExpandaiWebfetch = async () => ({
38
- 'tool.execute.after': async (input, output) => {
39
- if (input.tool !== 'webfetch') return
40
- const url = input.args?.url
41
- if (typeof url !== 'string' || url.length === 0) return
42
-
43
- try {
44
- const { exitCode, stdout } = await runExpandai(['hook', 'opencode'], JSON.stringify({ url }))
45
- if (exitCode !== 0 || stdout.trim().length === 0) return
46
-
47
- const { markdown } = JSON.parse(stdout)
48
- if (typeof markdown !== 'string' || markdown.length === 0) return
49
-
50
- output.output = markdown
51
- output.title = url
52
- } catch (error) {
53
- console.error(`expandai-webfetch plugin failed: ${error?.message ?? error}`)
54
- }
55
- },
56
- })
@@ -1,57 +0,0 @@
1
- import { Console, Effect, Match } from 'effect'
2
- import { ClaudeSkill } from './Claude/Skill.js'
3
- import { CodexSkill } from './Codex/Skill.js'
4
- import { CursorSkill } from './Cursor/Skill.js'
5
- import type { Host, InstallScope } from './Mcp.js'
6
- import { OpencodeSkill } from './Opencode/Skill.js'
7
-
8
- /**
9
- * Skill install dispatcher. Copies the bundled `SKILL.md` from the CLI's
10
- * own `skills/expandai/` source into the host-specific destination.
11
- *
12
- * - claude-code: `~/.claude/skills/expandai/` (global) or
13
- * `<cwd>/.claude/skills/expandai/` (project)
14
- * - cursor: `~/.cursor/skills/expandai/` (global) or
15
- * `<cwd>/.cursor/skills/expandai/` (project)
16
- * - opencode: `~/.config/opencode/skills/expandai/` (global) or
17
- * `<cwd>/.opencode/skills/expandai/` (project)
18
- * - codex: `~/.agents/skills/expandai/` (global) or
19
- * `<cwd>/.agents/skills/expandai/` (project)
20
- *
21
- * All hosts use the same `SKILL.md` + YAML frontmatter open standard,
22
- * so the bundled skill directory copies in unchanged.
23
- */
24
-
25
- export class Skill extends Effect.Service<Skill>()('@expandai/cli/Install/Skill', {
26
- dependencies: [ClaudeSkill.Default, CursorSkill.Default, OpencodeSkill.Default, CodexSkill.Default],
27
- effect: Effect.gen(function* () {
28
- const claude = yield* ClaudeSkill
29
- const cursor = yield* CursorSkill
30
- const opencode = yield* OpencodeSkill
31
- const codex = yield* CodexSkill
32
-
33
- const install = Effect.fn('Skill.install')(function* (host: Host, scope: InstallScope) {
34
- const target = yield* Match.value(host).pipe(
35
- Match.when('claude-code', () => claude.install(scope)),
36
- Match.when('cursor', () => cursor.install(scope)),
37
- Match.when('opencode', () => opencode.install(scope)),
38
- Match.when('codex', () => codex.install(scope)),
39
- Match.exhaustive,
40
- )
41
- yield* Console.log(`Installed expandai skill into ${target}`)
42
- })
43
-
44
- const uninstall = Effect.fn('Skill.uninstall')(function* (host: Host, scope: InstallScope) {
45
- const target = yield* Match.value(host).pipe(
46
- Match.when('claude-code', () => claude.uninstall(scope)),
47
- Match.when('cursor', () => cursor.uninstall(scope)),
48
- Match.when('opencode', () => opencode.uninstall(scope)),
49
- Match.when('codex', () => codex.uninstall(scope)),
50
- Match.exhaustive,
51
- )
52
- yield* Console.log(`Removed expandai skill from ${target}`)
53
- })
54
-
55
- return { install, uninstall } as const
56
- }),
57
- }) {}
@@ -1,82 +0,0 @@
1
- import { Array, Effect, Match, Schema } from 'effect'
2
- import { type HookHost, HookInstaller } from './Hook.js'
3
- import { Host, InstallScope, Mcp } from './Mcp.js'
4
- import { Skill } from './Skill.js'
5
-
6
- export const UninstallTarget = Schema.Literal('all', 'hook', 'skill', 'mcp')
7
-
8
- export class UninstallOptions extends Schema.Class<UninstallOptions>('UninstallOptions')({
9
- target: Schema.optional(UninstallTarget),
10
- host: Schema.optional(Host),
11
- scope: Schema.optional(InstallScope),
12
- }) {}
13
-
14
- const SCOPES: (typeof InstallScope.Type)[] = ['global', 'project']
15
- const HOSTS: (typeof Host.Type)[] = ['claude-code', 'cursor', 'opencode', 'codex']
16
-
17
- const isHookHost = (host: typeof Host.Type): host is HookHost => host === 'claude-code' || host === 'opencode'
18
-
19
- export class Uninstall extends Effect.Service<Uninstall>()('@expandai/cli/Install/Uninstall', {
20
- dependencies: [HookInstaller.Default, Mcp.Default, Skill.Default],
21
- effect: Effect.gen(function* () {
22
- const hook = yield* HookInstaller
23
- const mcp = yield* Mcp
24
- const skill = yield* Skill
25
-
26
- const uninstall = Effect.fn(function* (options: UninstallOptions = new UninstallOptions({})) {
27
- const target = options.target ?? 'all'
28
- const hosts = options.host ? [options.host] : HOSTS
29
- const scopes = options.scope ? [options.scope] : SCOPES
30
-
31
- // Sequential by default: a future change that merges any two `(host,
32
- // scope)` config paths into the same file shouldn't silently race.
33
- // Logout/uninstall happens once per session; throughput isn't the
34
- // bottleneck.
35
- const uninstallHooks = Effect.fnUntraced(function* () {
36
- const hookHosts = Array.filter(hosts, isHookHost)
37
- yield* Effect.forEach(
38
- hookHosts,
39
- (host) => Effect.forEach(scopes, (scope) => hook.uninstall(host, scope), { discard: true }),
40
- { discard: true },
41
- )
42
- })
43
-
44
- const uninstallSkills = Effect.fnUntraced(function* () {
45
- yield* Effect.forEach(
46
- hosts,
47
- (host) => Effect.forEach(scopes, (scope) => skill.uninstall(host, scope), { discard: true }),
48
- { discard: true },
49
- )
50
- })
51
-
52
- const uninstallMcp = Effect.fnUntraced(function* () {
53
- yield* Effect.forEach(
54
- hosts,
55
- (host) => Effect.forEach(scopes, (scope) => mcp.uninstall(host, scope), { discard: true }),
56
- { discard: true },
57
- )
58
- })
59
-
60
- const uninstallMcpWithoutHooks = Effect.fnUntraced(function* () {
61
- const mcpOnlyHosts = Array.filter(hosts, (host) => !isHookHost(host))
62
- yield* Effect.forEach(
63
- mcpOnlyHosts,
64
- (host) => Effect.forEach(scopes, (scope) => mcp.uninstall(host, scope), { discard: true }),
65
- { discard: true },
66
- )
67
- })
68
-
69
- yield* Match.value(target).pipe(
70
- Match.when('all', () =>
71
- uninstallHooks().pipe(Effect.zipRight(uninstallMcpWithoutHooks()), Effect.zipRight(uninstallSkills())),
72
- ),
73
- Match.when('hook', () => uninstallHooks()),
74
- Match.when('skill', () => uninstallSkills()),
75
- Match.when('mcp', () => uninstallMcp()),
76
- Match.exhaustive,
77
- )
78
- })
79
-
80
- return { uninstall } as const
81
- }),
82
- }) {}
@@ -1,162 +0,0 @@
1
- /**
2
- * Zero-dependency ANSI syntax highlighting for markdown printed to an
3
- * interactive terminal.
4
- *
5
- * This is a highlighter, not a renderer: the input text is preserved
6
- * byte-for-byte and only wrapped in ANSI escape sequences, so copying
7
- * from the terminal or stripping the escapes yields the exact document
8
- * the API returned. Piped output (non-TTY) must stay unstyled — gate
9
- * calls with `shouldColorizeMarkdown`.
10
- */
11
-
12
- // Styling is intentionally limited to 4-bit SGR (bold/dim/underline + the 8
13
- // base colors): it renders on every color-capable terminal and inherits the
14
- // user's palette. Do NOT introduce 256-color (38;5;n) or truecolor (38;2;r;g;b)
15
- // sequences without adding capability detection first — truecolor needs
16
- // COLORTERM=truecolor|24bit (TERM alone is not enough: e.g. ghostty sets
17
- // TERM=xterm-ghostty), and 256-color needs TERM=*-256color; emitting either
18
- // tier on a lesser terminal produces garbage.
19
- const RESET = '\x1b[0m'
20
- const BOLD = '\x1b[1m'
21
- const DIM = '\x1b[2m'
22
- const CYAN = '\x1b[36m'
23
- const YELLOW = '\x1b[33m'
24
- const GREEN = '\x1b[32m'
25
- const BLUE_UNDERLINE = '\x1b[34;4m'
26
- const HEADING = '\x1b[1;36m'
27
-
28
- export interface ColorizeEnvironment {
29
- readonly isTty: boolean
30
- readonly env: Readonly<Record<string, string | undefined>>
31
- }
32
-
33
- /**
34
- * Standard CLI color negotiation: color only on an interactive terminal,
35
- * `NO_COLOR` (https://no-color.org) and `TERM=dumb` opt out, a non-zero
36
- * `FORCE_COLOR` opts in regardless of the terminal.
37
- */
38
- export function shouldColorizeMarkdown(
39
- environment: ColorizeEnvironment = { isTty: process.stdout.isTTY === true, env: process.env },
40
- ): boolean {
41
- const { env, isTty } = environment
42
- const forceColor = env.FORCE_COLOR?.trim()
43
- if (forceColor !== undefined && forceColor !== '') return forceColor !== '0'
44
- if (env.NO_COLOR !== undefined && env.NO_COLOR !== '') return false
45
- if (env.TERM === 'dumb') return false
46
- return isTty
47
- }
48
-
49
- function styleLine(style: string, line: string): string {
50
- return line === '' ? line : `${style}${line}${RESET}`
51
- }
52
-
53
- // Inline code spans are styled first and must be shielded from the link,
54
- // bold, and table-pipe passes; ANSI escapes never contain \x00, so a
55
- // NUL-delimited stash is collision-free.
56
- function styleInline(line: string, options?: { readonly dimPipes: boolean }): string {
57
- const stash: string[] = []
58
- let out = line.replace(/`[^`\n]+`/g, (code) => {
59
- stash.push(`${CYAN}${code}${RESET}`)
60
- return `\x00${stash.length - 1}\x00`
61
- })
62
- if (options?.dimPipes) {
63
- // While code spans are stashed, so pipes inside `...` keep their
64
- // code styling instead of being re-wrapped in DIM.
65
- out = out.replaceAll('|', `${DIM}|${RESET}`)
66
- }
67
- out = out.replace(
68
- /\[([^\]\n]*)\]\(([^)\n]*)\)/g,
69
- (_match, text: string, target: string) =>
70
- `${DIM}[${RESET}${BLUE_UNDERLINE}${text}${RESET}${DIM}](${target})${RESET}`,
71
- )
72
- out = out.replace(/\*\*([^*\n]+)\*\*/g, (_match, text: string) => `${BOLD}**${text}**${RESET}`)
73
- return out.replace(/\x00(\d+)\x00/g, (_match, index: string) => stash[Number(index)] ?? '')
74
- }
75
-
76
- const FENCE_PATTERN = /^(\s*)(`{3,}|~{3,})(.*)$/
77
- const HEADING_PATTERN = /^#{1,6}\s/
78
- const HR_PATTERN = /^ {0,3}(-{3,}|\*{3,}|_{3,})\s*$/
79
- const LIST_MARKER_PATTERN = /^(\s*)([-*+]|\d{1,9}[.)])(\s+)(.*)$/
80
- const FRONTMATTER_KEY_PATTERN = /^(\s*)([\w.-]+):(.*)$/
81
-
82
- /**
83
- * Wrap a markdown document in ANSI styles for terminal display. Line-based:
84
- * headings, fences, front matter, blockquotes, list markers, and table pipes
85
- * get block styling; plain text lines get inline code / link / bold styling.
86
- */
87
- export function highlightMarkdownAnsi(markdown: string): string {
88
- const lines = markdown.split('\n')
89
- const out: string[] = []
90
- let inFrontmatter = false
91
- let fenceClose: RegExp | undefined
92
-
93
- lines.forEach((line, index) => {
94
- if (fenceClose) {
95
- if (FENCE_PATTERN.test(line) && fenceClose.test(line)) {
96
- fenceClose = undefined
97
- out.push(styleLine(DIM, line))
98
- } else {
99
- out.push(styleLine(YELLOW, line))
100
- }
101
- return
102
- }
103
-
104
- if (index === 0 && line === '---') {
105
- inFrontmatter = true
106
- out.push(styleLine(DIM, line))
107
- return
108
- }
109
- if (inFrontmatter) {
110
- if (line === '---') {
111
- inFrontmatter = false
112
- out.push(styleLine(DIM, line))
113
- return
114
- }
115
- const keyMatch = line.match(FRONTMATTER_KEY_PATTERN)
116
- out.push(
117
- keyMatch ? `${keyMatch[1]}${CYAN}${keyMatch[2]}${RESET}${DIM}:${keyMatch[3]}${RESET}` : styleLine(DIM, line),
118
- )
119
- return
120
- }
121
-
122
- const fenceMatch = line.match(FENCE_PATTERN)
123
- if (fenceMatch) {
124
- const marker = fenceMatch[2] ?? '```'
125
- fenceClose = new RegExp(`^\\s*${marker[0] === '`' ? '`' : '~'}{${marker.length},}\\s*$`)
126
- out.push(styleLine(DIM, line))
127
- return
128
- }
129
-
130
- if (HEADING_PATTERN.test(line)) {
131
- out.push(styleLine(HEADING, line))
132
- return
133
- }
134
- if (HR_PATTERN.test(line)) {
135
- out.push(styleLine(DIM, line))
136
- return
137
- }
138
- if (line.trimStart().startsWith('>')) {
139
- out.push(styleLine(GREEN, line))
140
- return
141
- }
142
- if (line.trimStart().startsWith('|')) {
143
- out.push(styleInline(line, { dimPipes: true }))
144
- return
145
- }
146
- const listMatch = line.match(LIST_MARKER_PATTERN)
147
- if (listMatch) {
148
- out.push(`${listMatch[1]}${YELLOW}${listMatch[2]}${RESET}${listMatch[3]}${styleInline(listMatch[4] ?? '')}`)
149
- return
150
- }
151
- out.push(styleInline(line))
152
- })
153
-
154
- return out.join('\n')
155
- }
156
-
157
- /** Strip the escape sequences this module emits; used to verify lossless styling. */
158
- export function stripAnsi(text: string): string {
159
- // Only SGR sequences are emitted by this module.
160
- // biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escapes are control characters by definition.
161
- return text.replace(/\x1b\[[0-9;]*m/g, '')
162
- }
@@ -1,62 +0,0 @@
1
- import { McpServer } from '@effect/ai'
2
- import { Effect, Layer, String } from 'effect'
3
- import { ConfigManager } from '../Config.js'
4
-
5
- /**
6
- * These resources are read by an agent that will follow their links, so every
7
- * URL must resolve to the stack this CLI is configured against — production by
8
- * default, or whatever `EXPAND_APP_URL` names for local and self-hosted setups.
9
- */
10
- export function makeAgentSetupPrompt(appUrl: string): string {
11
- return `Read ${new URL('/start.md', appUrl).toString()} and set up Expand. Fetch it as raw text with curl — a summarizing web-fetch tool drops the exact commands.`
12
- }
13
-
14
- /**
15
- * Points at the hosted guide instead of restating it. A local copy of the setup
16
- * procedure drifts silently the moment the commands change, and this resource
17
- * is read by an agent that can fetch the current one itself.
18
- */
19
- export function makeStartGuideMarkdown(appUrl: string): string {
20
- return String.stripMargin(`
21
- |# Expand Start Guide
22
- |
23
- |The setup procedure is served as a single document. Fetch it and follow it:
24
- |
25
- |${new URL('/start.md', appUrl).toString()}
26
- |
27
- |## Agent Setup Prompt
28
- |
29
- |${makeAgentSetupPrompt(appUrl)}
30
- |`)
31
- }
32
-
33
- export function makeAboutMarkdown(appUrl: string): string {
34
- return String.stripMargin(`
35
- |# expand.ai
36
- |
37
- |- Website: ${appUrl}
38
- |- Quickstart: ${new URL('/docs', appUrl).toString()}
39
- |- Dashboard: ${new URL('/dashboard', appUrl).toString()}
40
- |`)
41
- }
42
-
43
- const appUrl = Effect.gen(function* () {
44
- const configManager = yield* ConfigManager
45
- return yield* configManager.getAppUrl()
46
- })
47
-
48
- export const ExpandDocs = McpServer.resource({
49
- uri: 'expand://about',
50
- name: 'About expand.ai',
51
- description: 'Key links for the expand.ai platform',
52
- content: Effect.map(appUrl, makeAboutMarkdown),
53
- })
54
-
55
- export const ExpandStartDocs = McpServer.resource({
56
- uri: 'expand://start',
57
- name: 'Expand Start Guide',
58
- description: 'Where to find the Expand agent setup guide, and the prompt that runs it',
59
- content: Effect.map(appUrl, makeStartGuideMarkdown),
60
- })
61
-
62
- export const ExpandDocsLayer = Layer.mergeAll(ExpandDocs, ExpandStartDocs).pipe(Layer.provide(ConfigManager.Default))
@@ -1,16 +0,0 @@
1
- import { Context, Layer } from 'effect'
2
-
3
- /**
4
- * Per-request HTTP headers (lowercase keys per WHATWG Headers).
5
- *
6
- * Consumers should read this via `Effect.serviceOption(CurrentRequestHeaders)`
7
- * so the requirement does not leak into their `R`. The API's MCP route
8
- * overlays a fresh `Context.make` per request; the stdio CLI path leaves
9
- * the option as `none`.
10
- */
11
- export class CurrentRequestHeaders extends Context.Tag('@expandai/cli/Mcp/RequestHeaders/CurrentRequestHeaders')<
12
- CurrentRequestHeaders,
13
- Record<string, string>
14
- >() {
15
- static readonly Default = Layer.succeed(this, {})
16
- }
package/src/Mcp/Server.ts DELETED
@@ -1,15 +0,0 @@
1
- import { Layer } from 'effect'
2
- import { SERVER_KEY } from '../Install/Domain.js'
3
- import { CLI_VERSION } from '../Version.js'
4
- import { ExpandDocsLayer } from './ExpandDocs.js'
5
- import { ExpandToolKit } from './Toolkit.js'
6
-
7
- export { CurrentRequestHeaders } from './RequestHeaders.js'
8
-
9
- export const ServerInfo = {
10
- name: SERVER_KEY,
11
- version: CLI_VERSION,
12
- } as const
13
-
14
- export const makeServerLayer = (options: { readonly includeDocs?: boolean } = {}) =>
15
- options.includeDocs ? Layer.mergeAll(ExpandToolKit, ExpandDocsLayer) : ExpandToolKit