expandai 0.0.2 → 0.0.3

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 (271) hide show
  1. package/README.md +42 -5
  2. package/build/dts/ApiClient.d.ts +128 -194
  3. package/build/dts/ApiClient.d.ts.map +1 -1
  4. package/build/dts/Auth/Domain.d.ts +95 -0
  5. package/build/dts/Auth/Domain.d.ts.map +1 -0
  6. package/build/dts/Auth/ManualAuthorization.d.ts +107 -0
  7. package/build/dts/Auth/ManualAuthorization.d.ts.map +1 -0
  8. package/build/dts/Auth/PendingInit.d.ts +35 -0
  9. package/build/dts/Auth/PendingInit.d.ts.map +1 -0
  10. package/build/dts/Auth/Pkce.d.ts +10 -0
  11. package/build/dts/Auth/Pkce.d.ts.map +1 -0
  12. package/build/dts/Auth/SetupGrantAuthorization.d.ts +34 -0
  13. package/build/dts/Auth/SetupGrantAuthorization.d.ts.map +1 -0
  14. package/build/dts/Authentication.d.ts +10 -9
  15. package/build/dts/Authentication.d.ts.map +1 -1
  16. package/build/dts/BrowserOAuth.d.ts +7 -7
  17. package/build/dts/BrowserOAuth.d.ts.map +1 -1
  18. package/build/dts/CommandPath.d.ts +2 -0
  19. package/build/dts/CommandPath.d.ts.map +1 -0
  20. package/build/dts/Commands/Fetch.d.ts.map +1 -1
  21. package/build/dts/Commands/Hook.d.ts +5 -5
  22. package/build/dts/Commands/Init.d.ts +55 -0
  23. package/build/dts/Commands/Init.d.ts.map +1 -0
  24. package/build/dts/Commands/Mcp.d.ts +6 -6
  25. package/build/dts/Commands/Search.d.ts.map +1 -1
  26. package/build/dts/Commands/Skill.d.ts +4 -4
  27. package/build/dts/Commands/Status.d.ts +27 -0
  28. package/build/dts/Commands/Status.d.ts.map +1 -0
  29. package/build/dts/Commands/Telemetry.d.ts +7 -0
  30. package/build/dts/Commands/Telemetry.d.ts.map +1 -0
  31. package/build/dts/Commands/Uninstall.d.ts +4 -4
  32. package/build/dts/Config.d.ts +108 -17
  33. package/build/dts/Config.d.ts.map +1 -1
  34. package/build/dts/Hook/SnapshotNote.d.ts +8 -4
  35. package/build/dts/Hook/SnapshotNote.d.ts.map +1 -1
  36. package/build/dts/Init/ClaudeAdapter.d.ts +61 -0
  37. package/build/dts/Init/ClaudeAdapter.d.ts.map +1 -0
  38. package/build/dts/Init/ClaudeProfile.d.ts +78 -0
  39. package/build/dts/Init/ClaudeProfile.d.ts.map +1 -0
  40. package/build/dts/Init/Domain.d.ts +139 -0
  41. package/build/dts/Init/Domain.d.ts.map +1 -0
  42. package/build/dts/Init/HarnessAdapter.d.ts +33 -0
  43. package/build/dts/Init/HarnessAdapter.d.ts.map +1 -0
  44. package/build/dts/Init/HarnessDetector.d.ts +19 -0
  45. package/build/dts/Init/HarnessDetector.d.ts.map +1 -0
  46. package/build/dts/Init/HarnessRegistry.d.ts +50 -0
  47. package/build/dts/Init/HarnessRegistry.d.ts.map +1 -0
  48. package/build/dts/Init/MutationTransaction.d.ts +96 -0
  49. package/build/dts/Init/MutationTransaction.d.ts.map +1 -0
  50. package/build/dts/Init/Service.d.ts +72 -0
  51. package/build/dts/Init/Service.d.ts.map +1 -0
  52. package/build/dts/Init/StandardAdapters.d.ts +42 -0
  53. package/build/dts/Init/StandardAdapters.d.ts.map +1 -0
  54. package/build/dts/Init/Status.d.ts +149 -0
  55. package/build/dts/Init/Status.d.ts.map +1 -0
  56. package/build/dts/Install/Claude/Hook.d.ts +85 -25
  57. package/build/dts/Install/Claude/Hook.d.ts.map +1 -1
  58. package/build/dts/Install/Claude/Mcp.d.ts +21 -18
  59. package/build/dts/Install/Claude/Mcp.d.ts.map +1 -1
  60. package/build/dts/Install/Claude/Skill.d.ts +14 -15
  61. package/build/dts/Install/Claude/Skill.d.ts.map +1 -1
  62. package/build/dts/Install/Codex/Mcp.d.ts +14 -8
  63. package/build/dts/Install/Codex/Mcp.d.ts.map +1 -1
  64. package/build/dts/Install/Codex/Skill.d.ts +4 -2
  65. package/build/dts/Install/Codex/Skill.d.ts.map +1 -1
  66. package/build/dts/Install/Cursor/Mcp.d.ts +13 -13
  67. package/build/dts/Install/Cursor/Mcp.d.ts.map +1 -1
  68. package/build/dts/Install/Cursor/Skill.d.ts +4 -2
  69. package/build/dts/Install/Cursor/Skill.d.ts.map +1 -1
  70. package/build/dts/Install/Domain.d.ts +35 -5
  71. package/build/dts/Install/Domain.d.ts.map +1 -1
  72. package/build/dts/Install/Hook.d.ts +4 -4
  73. package/build/dts/Install/Hook.d.ts.map +1 -1
  74. package/build/dts/Install/Mcp.d.ts +5 -5
  75. package/build/dts/Install/Mcp.d.ts.map +1 -1
  76. package/build/dts/Install/Opencode/Hook.d.ts +4 -3
  77. package/build/dts/Install/Opencode/Hook.d.ts.map +1 -1
  78. package/build/dts/Install/Opencode/Mcp.d.ts +11 -11
  79. package/build/dts/Install/Opencode/Mcp.d.ts.map +1 -1
  80. package/build/dts/Install/Opencode/Skill.d.ts +4 -2
  81. package/build/dts/Install/Opencode/Skill.d.ts.map +1 -1
  82. package/build/dts/Install/Skill.d.ts +3 -3
  83. package/build/dts/Install/Uninstall.d.ts +6 -6
  84. package/build/dts/Install/Uninstall.d.ts.map +1 -1
  85. package/build/dts/MarkdownAnsi.d.ts +29 -0
  86. package/build/dts/MarkdownAnsi.d.ts.map +1 -0
  87. package/build/dts/Mcp/ExpandDocs.d.ts +17 -5
  88. package/build/dts/Mcp/ExpandDocs.d.ts.map +1 -1
  89. package/build/dts/Mcp/Server.d.ts +1 -1
  90. package/build/dts/Mcp/Server.d.ts.map +1 -1
  91. package/build/dts/Mcp/tools/Citations.d.ts +100 -71
  92. package/build/dts/Mcp/tools/Citations.d.ts.map +1 -1
  93. package/build/dts/Mcp/tools/Fetch.d.ts +16 -15
  94. package/build/dts/Mcp/tools/Fetch.d.ts.map +1 -1
  95. package/build/dts/SecureFile.d.ts +23 -0
  96. package/build/dts/SecureFile.d.ts.map +1 -0
  97. package/build/dts/Snippets.d.ts +11 -12
  98. package/build/dts/Snippets.d.ts.map +1 -1
  99. package/build/dts/Telemetry.d.ts +30 -0
  100. package/build/dts/Telemetry.d.ts.map +1 -0
  101. package/build/dts/Version.d.ts +2 -0
  102. package/build/dts/Version.d.ts.map +1 -0
  103. package/build/dts/Xdg.d.ts +11 -0
  104. package/build/dts/Xdg.d.ts.map +1 -0
  105. package/build/esm/ApiClient.js +133 -10
  106. package/build/esm/ApiClient.js.map +1 -1
  107. package/build/esm/Auth/Domain.js +56 -0
  108. package/build/esm/Auth/Domain.js.map +1 -0
  109. package/build/esm/Auth/ManualAuthorization.js +253 -0
  110. package/build/esm/Auth/ManualAuthorization.js.map +1 -0
  111. package/build/esm/Auth/PendingInit.js +267 -0
  112. package/build/esm/Auth/PendingInit.js.map +1 -0
  113. package/build/esm/Auth/Pkce.js +23 -0
  114. package/build/esm/Auth/Pkce.js.map +1 -0
  115. package/build/esm/Auth/SetupGrantAuthorization.js +91 -0
  116. package/build/esm/Auth/SetupGrantAuthorization.js.map +1 -0
  117. package/build/esm/Authentication.js +38 -7
  118. package/build/esm/Authentication.js.map +1 -1
  119. package/build/esm/BrowserOAuth.js +7 -10
  120. package/build/esm/BrowserOAuth.js.map +1 -1
  121. package/build/esm/CommandPath.js +31 -0
  122. package/build/esm/CommandPath.js.map +1 -0
  123. package/build/esm/Commands/Fetch.js +30 -6
  124. package/build/esm/Commands/Fetch.js.map +1 -1
  125. package/build/esm/Commands/Init.js +371 -0
  126. package/build/esm/Commands/Init.js.map +1 -0
  127. package/build/esm/Commands/Search.js +6 -3
  128. package/build/esm/Commands/Search.js.map +1 -1
  129. package/build/esm/Commands/Status.js +186 -0
  130. package/build/esm/Commands/Status.js.map +1 -0
  131. package/build/esm/Commands/Telemetry.js +23 -0
  132. package/build/esm/Commands/Telemetry.js.map +1 -0
  133. package/build/esm/Config.js +502 -28
  134. package/build/esm/Config.js.map +1 -1
  135. package/build/esm/Hook/Claude.js +1 -1
  136. package/build/esm/Hook/Claude.js.map +1 -1
  137. package/build/esm/Hook/SnapshotNote.js +54 -7
  138. package/build/esm/Hook/SnapshotNote.js.map +1 -1
  139. package/build/esm/Init/ClaudeAdapter.js +137 -0
  140. package/build/esm/Init/ClaudeAdapter.js.map +1 -0
  141. package/build/esm/Init/ClaudeProfile.js +380 -0
  142. package/build/esm/Init/ClaudeProfile.js.map +1 -0
  143. package/build/esm/Init/Domain.js +118 -0
  144. package/build/esm/Init/Domain.js.map +1 -0
  145. package/build/esm/Init/HarnessAdapter.js +21 -0
  146. package/build/esm/Init/HarnessAdapter.js.map +1 -0
  147. package/build/esm/Init/HarnessDetector.js +65 -0
  148. package/build/esm/Init/HarnessDetector.js.map +1 -0
  149. package/build/esm/Init/HarnessRegistry.js +58 -0
  150. package/build/esm/Init/HarnessRegistry.js.map +1 -0
  151. package/build/esm/Init/MutationTransaction.js +494 -0
  152. package/build/esm/Init/MutationTransaction.js.map +1 -0
  153. package/build/esm/Init/Service.js +319 -0
  154. package/build/esm/Init/Service.js.map +1 -0
  155. package/build/esm/Init/StandardAdapters.js +116 -0
  156. package/build/esm/Init/StandardAdapters.js.map +1 -0
  157. package/build/esm/Init/Status.js +215 -0
  158. package/build/esm/Init/Status.js.map +1 -0
  159. package/build/esm/Install/Claude/Hook.js +182 -50
  160. package/build/esm/Install/Claude/Hook.js.map +1 -1
  161. package/build/esm/Install/Claude/Mcp.js +56 -26
  162. package/build/esm/Install/Claude/Mcp.js.map +1 -1
  163. package/build/esm/Install/Claude/Skill.js +49 -27
  164. package/build/esm/Install/Claude/Skill.js.map +1 -1
  165. package/build/esm/Install/Codex/Mcp.js +9 -4
  166. package/build/esm/Install/Codex/Mcp.js.map +1 -1
  167. package/build/esm/Install/Codex/Skill.js +4 -1
  168. package/build/esm/Install/Codex/Skill.js.map +1 -1
  169. package/build/esm/Install/Cursor/Mcp.js +7 -4
  170. package/build/esm/Install/Cursor/Mcp.js.map +1 -1
  171. package/build/esm/Install/Cursor/Skill.js +4 -1
  172. package/build/esm/Install/Cursor/Skill.js.map +1 -1
  173. package/build/esm/Install/Domain.js +60 -3
  174. package/build/esm/Install/Domain.js.map +1 -1
  175. package/build/esm/Install/Hook.js.map +1 -1
  176. package/build/esm/Install/Mcp.js +1 -1
  177. package/build/esm/Install/Mcp.js.map +1 -1
  178. package/build/esm/Install/Opencode/Hook.js +1 -1
  179. package/build/esm/Install/Opencode/Hook.js.map +1 -1
  180. package/build/esm/Install/Opencode/Mcp.js +6 -3
  181. package/build/esm/Install/Opencode/Mcp.js.map +1 -1
  182. package/build/esm/Install/Opencode/Skill.js +4 -1
  183. package/build/esm/Install/Opencode/Skill.js.map +1 -1
  184. package/build/esm/Install/Opencode/expandai-webfetch.js +27 -9
  185. package/build/esm/Install/Skill.js.map +1 -1
  186. package/build/esm/Install/Uninstall.js +2 -2
  187. package/build/esm/Install/Uninstall.js.map +1 -1
  188. package/build/esm/MarkdownAnsi.js +142 -0
  189. package/build/esm/MarkdownAnsi.js.map +1 -0
  190. package/build/esm/Mcp/ExpandDocs.js +44 -52
  191. package/build/esm/Mcp/ExpandDocs.js.map +1 -1
  192. package/build/esm/Mcp/RequestHeaders.js.map +1 -1
  193. package/build/esm/Mcp/Server.js +2 -1
  194. package/build/esm/Mcp/Server.js.map +1 -1
  195. package/build/esm/Mcp/tools/Citations.js +109 -8
  196. package/build/esm/Mcp/tools/Citations.js.map +1 -1
  197. package/build/esm/Mcp/tools/Fetch.js +36 -12
  198. package/build/esm/Mcp/tools/Fetch.js.map +1 -1
  199. package/build/esm/SecureFile.js +36 -0
  200. package/build/esm/SecureFile.js.map +1 -0
  201. package/build/esm/Snippets.js +12 -1
  202. package/build/esm/Snippets.js.map +1 -1
  203. package/build/esm/Telemetry.js +157 -0
  204. package/build/esm/Telemetry.js.map +1 -0
  205. package/build/esm/Version.js +4 -0
  206. package/build/esm/Version.js.map +1 -0
  207. package/build/esm/Xdg.js +15 -0
  208. package/build/esm/Xdg.js.map +1 -0
  209. package/build/esm/main.js +122 -14
  210. package/build/esm/main.js.map +1 -1
  211. package/build/skills/expandai/SKILL.md +26 -3
  212. package/package.json +4 -4
  213. package/src/ApiClient.ts +206 -13
  214. package/src/Auth/Domain.ts +63 -0
  215. package/src/Auth/ManualAuthorization.ts +296 -0
  216. package/src/Auth/PendingInit.ts +338 -0
  217. package/src/Auth/Pkce.ts +26 -0
  218. package/src/Auth/SetupGrantAuthorization.ts +116 -0
  219. package/src/Authentication.ts +49 -11
  220. package/src/BrowserOAuth.ts +10 -12
  221. package/src/CommandPath.ts +30 -0
  222. package/src/Commands/Fetch.ts +42 -11
  223. package/src/Commands/Init.ts +489 -0
  224. package/src/Commands/Search.ts +8 -3
  225. package/src/Commands/Status.ts +228 -0
  226. package/src/Commands/Telemetry.ts +42 -0
  227. package/src/Config.ts +695 -50
  228. package/src/Hook/Claude.ts +1 -1
  229. package/src/Hook/SnapshotNote.ts +55 -7
  230. package/src/Init/ClaudeAdapter.ts +191 -0
  231. package/src/Init/ClaudeProfile.ts +496 -0
  232. package/src/Init/Domain.ts +153 -0
  233. package/src/Init/HarnessAdapter.ts +35 -0
  234. package/src/Init/HarnessDetector.ts +77 -0
  235. package/src/Init/HarnessRegistry.ts +87 -0
  236. package/src/Init/MutationTransaction.ts +747 -0
  237. package/src/Init/Service.ts +385 -0
  238. package/src/Init/StandardAdapters.ts +141 -0
  239. package/src/Init/Status.ts +253 -0
  240. package/src/Install/Claude/Hook.ts +234 -81
  241. package/src/Install/Claude/Mcp.ts +80 -33
  242. package/src/Install/Claude/Skill.ts +70 -37
  243. package/src/Install/Codex/Mcp.ts +14 -9
  244. package/src/Install/Codex/Skill.ts +9 -5
  245. package/src/Install/Cursor/Mcp.ts +12 -9
  246. package/src/Install/Cursor/Skill.ts +9 -5
  247. package/src/Install/Domain.ts +77 -4
  248. package/src/Install/Hook.ts +3 -3
  249. package/src/Install/Mcp.ts +6 -6
  250. package/src/Install/Opencode/Hook.ts +5 -5
  251. package/src/Install/Opencode/Mcp.ts +11 -8
  252. package/src/Install/Opencode/Skill.ts +9 -5
  253. package/src/Install/Opencode/expandai-webfetch.js +27 -9
  254. package/src/Install/Skill.ts +3 -3
  255. package/src/Install/Uninstall.ts +3 -3
  256. package/src/MarkdownAnsi.ts +162 -0
  257. package/src/Mcp/ExpandDocs.ts +46 -55
  258. package/src/Mcp/Server.ts +2 -1
  259. package/src/Mcp/tools/Citations.ts +117 -8
  260. package/src/Mcp/tools/Fetch.ts +40 -14
  261. package/src/SecureFile.ts +70 -0
  262. package/src/Snippets.ts +19 -12
  263. package/src/Telemetry.ts +186 -0
  264. package/src/Version.ts +3 -0
  265. package/src/Xdg.ts +18 -0
  266. package/src/main.ts +171 -12
  267. package/build/dts/DeviceAuth.d.ts +0 -87
  268. package/build/dts/DeviceAuth.d.ts.map +0 -1
  269. package/build/esm/DeviceAuth.js +0 -127
  270. package/build/esm/DeviceAuth.js.map +0 -1
  271. package/src/DeviceAuth.ts +0 -193
@@ -0,0 +1,228 @@
1
+ import { Command, Options, Prompt } from '@effect/cli'
2
+ import { Terminal } from '@effect/platform'
3
+ import { Console, Effect, Either, Logger, LogLevel, Option, Schema } from 'effect'
4
+ import { InitErrorEnvelope, InitHarness, InitScope } from '../Init/Domain.js'
5
+ import { StatusRequest, StatusResult, StatusService, StatusServiceError } from '../Init/Status.js'
6
+ import { type OnboardingExecutionModeInput, resolveOnboardingExecutionMode } from './Init.js'
7
+
8
+ const harness = Options.text('harness').pipe(Options.optional, Options.withDescription('Agent harness to inspect'))
9
+ const scope = Options.text('scope').pipe(Options.optional, Options.withDescription('Configuration scope'))
10
+ const json = Options.boolean('json').pipe(Options.withDescription('Emit one machine-readable JSON result'))
11
+ const config = { harness, scope, json }
12
+
13
+ const ErrorJson = Schema.parseJson(InitErrorEnvelope)
14
+ const StatusResultJson = Schema.parseJson(StatusResult)
15
+ const INVALID_ARGUMENTS_MESSAGE =
16
+ 'Agent status requires --harness claude-code|cursor|opencode|codex, --scope project|global, and --json.'
17
+
18
+ function errorJson(code: string, message: string, recoverable: boolean): string {
19
+ return Schema.encodeSync(ErrorJson)(
20
+ new InitErrorEnvelope({ schemaVersion: 1, status: 'error', code, message, recoverable }),
21
+ )
22
+ }
23
+
24
+ function invalidArgumentsJson(): string {
25
+ return errorJson('invalid_arguments', INVALID_ARGUMENTS_MESSAGE, true)
26
+ }
27
+
28
+ function emitError(code: string, message: string, recoverable: boolean, exitCode: number) {
29
+ return Console.error(errorJson(code, message, recoverable)).pipe(
30
+ Effect.zipRight(Effect.sync(() => void (process.exitCode = exitCode))),
31
+ )
32
+ }
33
+
34
+ export interface StatusCommandOptions {
35
+ readonly harness: Option.Option<string>
36
+ readonly json: boolean
37
+ readonly scope: Option.Option<string>
38
+ }
39
+
40
+ /** Selects the status surface using the same machine-safety precedence as init. */
41
+ export function resolveStatusExecutionMode(input: OnboardingExecutionModeInput): 'human' | 'machine' | undefined {
42
+ return resolveOnboardingExecutionMode(input, 'status')
43
+ }
44
+
45
+ function parseMachineArguments(args: readonly string[]): StatusCommandOptions | undefined {
46
+ function parse(index: number, parsed: StatusCommandOptions): StatusCommandOptions | undefined {
47
+ if (index >= args.length) return parsed
48
+ const token = args[index]
49
+ if (token === '--json' && !parsed.json) return parse(index + 1, { ...parsed, json: true })
50
+ if (token !== '--harness' && token !== '--scope') return undefined
51
+ const value = args[index + 1]
52
+ if (value === undefined || value.startsWith('-')) return undefined
53
+ if (token === '--harness' && Option.isNone(parsed.harness)) {
54
+ return parse(index + 2, { ...parsed, harness: Option.some(value) })
55
+ }
56
+ if (token === '--scope' && Option.isNone(parsed.scope)) {
57
+ return parse(index + 2, { ...parsed, scope: Option.some(value) })
58
+ }
59
+ return undefined
60
+ }
61
+
62
+ return parse(0, { harness: Option.none(), json: false, scope: Option.none() })
63
+ }
64
+
65
+ /** Returns safe JSON before Effect CLI can render parser help or acquire services. */
66
+ export function preflightAgentStatusArgv(argv: readonly string[]): string | undefined {
67
+ if (argv[2] !== 'status') return undefined
68
+ const args = argv.slice(3)
69
+ if (args.length === 1 && (args[0] === '--help' || args[0] === '-h')) return undefined
70
+ const parsed = parseMachineArguments(args)
71
+ if (parsed === undefined || !parsed.json) return invalidArgumentsJson()
72
+ if (Option.isNone(Option.flatMap(parsed.harness, (value) => Schema.decodeUnknownOption(InitHarness)(value)))) {
73
+ return invalidArgumentsJson()
74
+ }
75
+ if (!Option.contains(parsed.scope, 'project') && !Option.contains(parsed.scope, 'global')) {
76
+ return invalidArgumentsJson()
77
+ }
78
+ return undefined
79
+ }
80
+
81
+ const HARNESS_LABELS: Record<typeof InitHarness.Type, string> = {
82
+ 'claude-code': 'Claude Code',
83
+ cursor: 'Cursor',
84
+ codex: 'Codex',
85
+ opencode: 'OpenCode',
86
+ }
87
+
88
+ /** Only these install a web-fetch hook; the line is meaningless for the rest. */
89
+ const HOOK_HARNESSES: ReadonlyArray<typeof InitHarness.Type> = ['claude-code', 'opencode']
90
+
91
+ function renderHumanStatus(result: StatusResult): string {
92
+ const pending = result.pendingAuthorization.count === 1 ? 'approval pending' : 'none'
93
+ const installation = result.installation.configured ? 'configured' : 'partial or not configured'
94
+ const configured = (present: boolean) => (present ? 'configured' : 'not configured')
95
+ // `result.harness` was never read: every harness printed as "Claude Code", and
96
+ // a correctly configured Cursor machine also reported a missing WebFetch hook
97
+ // it never installs. The guide sends users here to confirm setup, so this was
98
+ // the last thing they saw.
99
+ return [
100
+ 'Expand status',
101
+ `Auth: ${result.auth.state}`,
102
+ `Pending authorization: ${pending}`,
103
+ `${HARNESS_LABELS[result.harness]}: ${installation}`,
104
+ ...(HOOK_HARNESSES.includes(result.harness)
105
+ ? [`WebFetch hook: ${configured(result.installation.components.webfetchHook)}`]
106
+ : []),
107
+ `Runtime skill: ${configured(result.installation.components.runtimeSkill)}`,
108
+ `MCP: ${configured(result.installation.components.mcp)}`,
109
+ `Runtime: ${result.installation.runtimeState}`,
110
+ ].join('\n')
111
+ }
112
+
113
+ const runStatusService = Effect.fnUntraced(function* (request: StatusRequest, jsonOutput: boolean) {
114
+ const service = yield* StatusService
115
+ // Log suppression belongs here, not in the service, and only for machine
116
+ // output: `--json` writes its result *and* its structured error envelope to
117
+ // stderr, so a warning interleaved there breaks the one-object contract an
118
+ // agent parses. Human status keeps its diagnostics — it is the command
119
+ // someone runs when something is already wrong.
120
+ const result = yield* Effect.either(
121
+ jsonOutput ? service.run(request).pipe(Logger.withMinimumLogLevel(LogLevel.None)) : service.run(request),
122
+ )
123
+ if (Either.isLeft(result)) {
124
+ const error = result.left
125
+ if (error instanceof StatusServiceError) {
126
+ yield* emitError(error.code, error.message, error.recoverable, 1)
127
+ return
128
+ }
129
+ yield* emitError('status_failed', 'Expand status could not be inspected safely.', true, 1)
130
+ return
131
+ }
132
+ if (!jsonOutput) {
133
+ yield* Console.log(renderHumanStatus(result.right))
134
+ return
135
+ }
136
+ const encoded = yield* Schema.encode(StatusResultJson)(result.right).pipe(
137
+ Effect.catchAll(() =>
138
+ emitError('invalid_status_result', 'Expand status returned an invalid result.', false, 1).pipe(
139
+ Effect.as(undefined),
140
+ ),
141
+ ),
142
+ )
143
+ if (typeof encoded === 'string') yield* Console.log(encoded)
144
+ })
145
+
146
+ const runMachineStatus = Effect.fnUntraced(function* ({ harness, json, scope }: StatusCommandOptions) {
147
+ if (
148
+ !json ||
149
+ Option.isNone(Option.flatMap(harness, (value) => Schema.decodeUnknownOption(InitHarness)(value))) ||
150
+ (!Option.contains(scope, 'project') && !Option.contains(scope, 'global'))
151
+ ) {
152
+ yield* emitError('invalid_arguments', INVALID_ARGUMENTS_MESSAGE, true, 2)
153
+ return
154
+ }
155
+ const request = yield* Schema.decodeUnknown(StatusRequest)({
156
+ harness: Option.getOrUndefined(harness),
157
+ scope: Option.getOrUndefined(scope),
158
+ }).pipe(
159
+ Effect.catchAll(() =>
160
+ emitError('invalid_arguments', INVALID_ARGUMENTS_MESSAGE, true, 2).pipe(Effect.as(undefined)),
161
+ ),
162
+ )
163
+ if (request !== undefined) yield* runStatusService(request, true)
164
+ })
165
+
166
+ export class StatusTerminalInteraction extends Effect.Service<StatusTerminalInteraction>()(
167
+ '@expandai/cli/Commands/StatusTerminalInteraction',
168
+ {
169
+ effect: Effect.gen(function* () {
170
+ const terminal = yield* Terminal.Terminal
171
+ const chooseHarness = Effect.fnUntraced(function* () {
172
+ return yield* Prompt.select({
173
+ message: 'Which agent harness should Expand inspect?',
174
+ // init configures all four, so inspection must cover all four.
175
+ choices: [
176
+ { title: 'Claude Code', value: 'claude-code' as const },
177
+ { title: 'Cursor', value: 'cursor' as const },
178
+ { title: 'opencode', value: 'opencode' as const },
179
+ { title: 'Codex', value: 'codex' as const },
180
+ ],
181
+ }).pipe(Effect.provideService(Terminal.Terminal, terminal))
182
+ })
183
+ const chooseScope = Effect.fnUntraced(function* () {
184
+ return yield* Prompt.select({
185
+ message: 'Which configuration scope should Expand inspect?',
186
+ choices: [
187
+ { title: 'This project', value: 'project' as const },
188
+ { title: 'All projects for this user', value: 'global' as const },
189
+ ],
190
+ }).pipe(Effect.provideService(Terminal.Terminal, terminal))
191
+ })
192
+ return { chooseHarness, chooseScope } as const
193
+ }),
194
+ },
195
+ ) {}
196
+
197
+ export const runHumanStatus = Effect.fnUntraced(function* (options: StatusCommandOptions) {
198
+ if (options.json) {
199
+ yield* runMachineStatus(options)
200
+ return
201
+ }
202
+ const interaction = yield* StatusTerminalInteraction
203
+ const harnessInput = Option.isSome(options.harness) ? options.harness.value : yield* interaction.chooseHarness()
204
+ const scopeInput = Option.isSome(options.scope) ? options.scope.value : yield* interaction.chooseScope()
205
+ const decodedHarness = Schema.decodeUnknownOption(InitHarness)(harnessInput)
206
+ const decodedScope = Schema.decodeUnknownOption(InitScope)(scopeInput)
207
+ if (Option.isNone(decodedHarness) || Option.isNone(decodedScope)) {
208
+ yield* emitError('invalid_arguments', INVALID_ARGUMENTS_MESSAGE, true, 2)
209
+ return
210
+ }
211
+ const request = new StatusRequest({
212
+ harness: decodedHarness.value,
213
+ scope: decodedScope.value,
214
+ })
215
+ yield* runStatusService(request, false)
216
+ })
217
+
218
+ const description = 'Inspect redacted local Expand onboarding status'
219
+
220
+ export const HumanStatusCommand = Command.make('status', config).pipe(
221
+ Command.withDescription(description),
222
+ Command.withHandler(runHumanStatus),
223
+ )
224
+
225
+ export const AgentStatusCommand = Command.make('status', config).pipe(
226
+ Command.withDescription(description),
227
+ Command.withHandler(runMachineStatus),
228
+ )
@@ -0,0 +1,42 @@
1
+ import { Command } from '@effect/cli'
2
+ import { Console, Effect } from 'effect'
3
+ import { ConfigManager } from '../Config.js'
4
+ import { Telemetry } from '../Telemetry.js'
5
+
6
+ const setEnabled = Effect.fnUntraced(function* (enabled: boolean) {
7
+ const configManager = yield* ConfigManager
8
+ const { config } = yield* configManager.readConfigFile()
9
+ // updateConfig fails with ConfigParseError on an undecodable file — surfaced
10
+ // to the user rather than overwriting whatever the file held.
11
+ yield* configManager.updateConfig({ telemetry: { ...config.telemetry, enabled } })
12
+ yield* Console.log(`Telemetry ${enabled ? 'enabled' : 'disabled'}.`)
13
+ })
14
+
15
+ const showStatus = Effect.fnUntraced(function* () {
16
+ const telemetry = yield* Telemetry
17
+ yield* Console.log(
18
+ telemetry.enabled
19
+ ? 'Telemetry is enabled. Disable with `expandai telemetry disable` or EXPANDAI_NO_TELEMETRY=1.'
20
+ : 'Telemetry is disabled.',
21
+ )
22
+ })
23
+
24
+ const StatusCommand = Command.make('status').pipe(
25
+ Command.withDescription('Show whether anonymous usage telemetry is enabled'),
26
+ Command.withHandler(() => showStatus()),
27
+ )
28
+
29
+ const EnableCommand = Command.make('enable').pipe(
30
+ Command.withDescription('Enable anonymous usage telemetry'),
31
+ Command.withHandler(() => setEnabled(true)),
32
+ )
33
+
34
+ const DisableCommand = Command.make('disable').pipe(
35
+ Command.withDescription('Disable anonymous usage telemetry'),
36
+ Command.withHandler(() => setEnabled(false)),
37
+ )
38
+
39
+ export const TelemetryCommand = Command.make('telemetry').pipe(
40
+ Command.withDescription('Manage anonymous usage telemetry'),
41
+ Command.withSubcommands([StatusCommand, EnableCommand, DisableCommand]),
42
+ )