@agentuity/cli 1.0.0 → 1.0.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 (331) hide show
  1. package/AGENTS.md +40 -24
  2. package/bin/cli.ts +47 -22
  3. package/dist/agent-detection.d.ts +23 -38
  4. package/dist/agent-detection.d.ts.map +1 -1
  5. package/dist/agent-detection.js +412 -153
  6. package/dist/agent-detection.js.map +1 -1
  7. package/dist/ai-help.d.ts +23 -0
  8. package/dist/ai-help.d.ts.map +1 -0
  9. package/dist/ai-help.js +328 -0
  10. package/dist/ai-help.js.map +1 -0
  11. package/dist/api.js +1 -1
  12. package/dist/api.js.map +1 -1
  13. package/dist/auth.d.ts +10 -1
  14. package/dist/auth.d.ts.map +1 -1
  15. package/dist/auth.js +176 -16
  16. package/dist/auth.js.map +1 -1
  17. package/dist/banner.d.ts.map +1 -1
  18. package/dist/banner.js +5 -0
  19. package/dist/banner.js.map +1 -1
  20. package/dist/cache/agent-intro.d.ts +13 -0
  21. package/dist/cache/agent-intro.d.ts.map +1 -0
  22. package/dist/cache/agent-intro.js +54 -0
  23. package/dist/cache/agent-intro.js.map +1 -0
  24. package/dist/cache/index.d.ts +1 -0
  25. package/dist/cache/index.d.ts.map +1 -1
  26. package/dist/cache/index.js +1 -0
  27. package/dist/cache/index.js.map +1 -1
  28. package/dist/cache/resource-region.d.ts +3 -2
  29. package/dist/cache/resource-region.d.ts.map +1 -1
  30. package/dist/cache/resource-region.js +13 -4
  31. package/dist/cache/resource-region.js.map +1 -1
  32. package/dist/catalyst.d.ts +7 -0
  33. package/dist/catalyst.d.ts.map +1 -0
  34. package/dist/catalyst.js +15 -0
  35. package/dist/catalyst.js.map +1 -0
  36. package/dist/cli.d.ts +12 -1
  37. package/dist/cli.d.ts.map +1 -1
  38. package/dist/cli.js +290 -67
  39. package/dist/cli.js.map +1 -1
  40. package/dist/cmd/ai/detect.d.ts +3 -0
  41. package/dist/cmd/ai/detect.d.ts.map +1 -0
  42. package/dist/cmd/ai/detect.js +49 -0
  43. package/dist/cmd/ai/detect.js.map +1 -0
  44. package/dist/cmd/ai/index.d.ts.map +1 -1
  45. package/dist/cmd/ai/index.js +11 -1
  46. package/dist/cmd/ai/index.js.map +1 -1
  47. package/dist/cmd/ai/intro.d.ts +7 -0
  48. package/dist/cmd/ai/intro.d.ts.map +1 -0
  49. package/dist/cmd/ai/intro.js +141 -0
  50. package/dist/cmd/ai/intro.js.map +1 -0
  51. package/dist/cmd/ai/opencode/run.d.ts.map +1 -1
  52. package/dist/cmd/ai/opencode/run.js +5 -0
  53. package/dist/cmd/ai/opencode/run.js.map +1 -1
  54. package/dist/cmd/build/ast.d.ts.map +1 -1
  55. package/dist/cmd/build/ast.js +79 -0
  56. package/dist/cmd/build/ast.js.map +1 -1
  57. package/dist/cmd/build/vite/bun-dev-server.d.ts.map +1 -1
  58. package/dist/cmd/build/vite/bun-dev-server.js +2 -0
  59. package/dist/cmd/build/vite/bun-dev-server.js.map +1 -1
  60. package/dist/cmd/build/vite/docs-generator.d.ts.map +1 -1
  61. package/dist/cmd/build/vite/docs-generator.js +15 -1
  62. package/dist/cmd/build/vite/docs-generator.js.map +1 -1
  63. package/dist/cmd/build/vite/env-types-generator.d.ts +26 -0
  64. package/dist/cmd/build/vite/env-types-generator.d.ts.map +1 -0
  65. package/dist/cmd/build/vite/env-types-generator.js +110 -0
  66. package/dist/cmd/build/vite/env-types-generator.js.map +1 -0
  67. package/dist/cmd/build/vite/index.d.ts +2 -0
  68. package/dist/cmd/build/vite/index.d.ts.map +1 -1
  69. package/dist/cmd/build/vite/index.js +12 -1
  70. package/dist/cmd/build/vite/index.js.map +1 -1
  71. package/dist/cmd/build/vite/public-asset-path-plugin.d.ts.map +1 -1
  72. package/dist/cmd/build/vite/public-asset-path-plugin.js.map +1 -1
  73. package/dist/cmd/build/vite/vite-builder.d.ts +2 -0
  74. package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
  75. package/dist/cmd/build/vite/vite-builder.js +10 -1
  76. package/dist/cmd/build/vite/vite-builder.js.map +1 -1
  77. package/dist/cmd/cloud/db/create.js.map +1 -1
  78. package/dist/cmd/cloud/db/delete.js.map +1 -1
  79. package/dist/cmd/cloud/db/get.d.ts.map +1 -1
  80. package/dist/cmd/cloud/db/get.js +27 -12
  81. package/dist/cmd/cloud/db/get.js.map +1 -1
  82. package/dist/cmd/cloud/deploy-fork.d.ts.map +1 -1
  83. package/dist/cmd/cloud/deploy-fork.js +2 -0
  84. package/dist/cmd/cloud/deploy-fork.js.map +1 -1
  85. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  86. package/dist/cmd/cloud/deploy.js +17 -0
  87. package/dist/cmd/cloud/deploy.js.map +1 -1
  88. package/dist/cmd/cloud/env/import.js.map +1 -1
  89. package/dist/cmd/cloud/env/list.js.map +1 -1
  90. package/dist/cmd/cloud/env/push.js.map +1 -1
  91. package/dist/cmd/cloud/keyvalue/util.d.ts.map +1 -1
  92. package/dist/cmd/cloud/keyvalue/util.js +3 -3
  93. package/dist/cmd/cloud/keyvalue/util.js.map +1 -1
  94. package/dist/cmd/cloud/machine/list.js +3 -3
  95. package/dist/cmd/cloud/machine/list.js.map +1 -1
  96. package/dist/cmd/cloud/region/index.js.map +1 -1
  97. package/dist/cmd/cloud/region-lookup.d.ts +7 -4
  98. package/dist/cmd/cloud/region-lookup.d.ts.map +1 -1
  99. package/dist/cmd/cloud/region-lookup.js +59 -14
  100. package/dist/cmd/cloud/region-lookup.js.map +1 -1
  101. package/dist/cmd/cloud/sandbox/cp.d.ts.map +1 -1
  102. package/dist/cmd/cloud/sandbox/cp.js +7 -5
  103. package/dist/cmd/cloud/sandbox/cp.js.map +1 -1
  104. package/dist/cmd/cloud/sandbox/create.js +1 -1
  105. package/dist/cmd/cloud/sandbox/create.js.map +1 -1
  106. package/dist/cmd/cloud/sandbox/delete.d.ts.map +1 -1
  107. package/dist/cmd/cloud/sandbox/delete.js +8 -7
  108. package/dist/cmd/cloud/sandbox/delete.js.map +1 -1
  109. package/dist/cmd/cloud/sandbox/download.d.ts.map +1 -1
  110. package/dist/cmd/cloud/sandbox/download.js +7 -5
  111. package/dist/cmd/cloud/sandbox/download.js.map +1 -1
  112. package/dist/cmd/cloud/sandbox/env.d.ts.map +1 -1
  113. package/dist/cmd/cloud/sandbox/env.js +7 -5
  114. package/dist/cmd/cloud/sandbox/env.js.map +1 -1
  115. package/dist/cmd/cloud/sandbox/exec.d.ts.map +1 -1
  116. package/dist/cmd/cloud/sandbox/exec.js +7 -5
  117. package/dist/cmd/cloud/sandbox/exec.js.map +1 -1
  118. package/dist/cmd/cloud/sandbox/get.d.ts.map +1 -1
  119. package/dist/cmd/cloud/sandbox/get.js +12 -7
  120. package/dist/cmd/cloud/sandbox/get.js.map +1 -1
  121. package/dist/cmd/cloud/sandbox/list.d.ts.map +1 -1
  122. package/dist/cmd/cloud/sandbox/list.js +40 -63
  123. package/dist/cmd/cloud/sandbox/list.js.map +1 -1
  124. package/dist/cmd/cloud/sandbox/ls.d.ts.map +1 -1
  125. package/dist/cmd/cloud/sandbox/ls.js +7 -5
  126. package/dist/cmd/cloud/sandbox/ls.js.map +1 -1
  127. package/dist/cmd/cloud/sandbox/mkdir.d.ts.map +1 -1
  128. package/dist/cmd/cloud/sandbox/mkdir.js +7 -5
  129. package/dist/cmd/cloud/sandbox/mkdir.js.map +1 -1
  130. package/dist/cmd/cloud/sandbox/rm.d.ts.map +1 -1
  131. package/dist/cmd/cloud/sandbox/rm.js +7 -5
  132. package/dist/cmd/cloud/sandbox/rm.js.map +1 -1
  133. package/dist/cmd/cloud/sandbox/rmdir.d.ts.map +1 -1
  134. package/dist/cmd/cloud/sandbox/rmdir.js +7 -5
  135. package/dist/cmd/cloud/sandbox/rmdir.js.map +1 -1
  136. package/dist/cmd/cloud/sandbox/run.js +1 -1
  137. package/dist/cmd/cloud/sandbox/run.js.map +1 -1
  138. package/dist/cmd/cloud/sandbox/snapshot/build.js.map +1 -1
  139. package/dist/cmd/cloud/sandbox/snapshot/get.js.map +1 -1
  140. package/dist/cmd/cloud/sandbox/upload.d.ts.map +1 -1
  141. package/dist/cmd/cloud/sandbox/upload.js +7 -5
  142. package/dist/cmd/cloud/sandbox/upload.js.map +1 -1
  143. package/dist/cmd/cloud/sandbox/util.d.ts +2 -2
  144. package/dist/cmd/cloud/sandbox/util.d.ts.map +1 -1
  145. package/dist/cmd/cloud/sandbox/util.js +14 -13
  146. package/dist/cmd/cloud/sandbox/util.js.map +1 -1
  147. package/dist/cmd/cloud/ssh.d.ts.map +1 -1
  148. package/dist/cmd/cloud/ssh.js +3 -3
  149. package/dist/cmd/cloud/ssh.js.map +1 -1
  150. package/dist/cmd/cloud/storage/create.js.map +1 -1
  151. package/dist/cmd/cloud/storage/delete.js.map +1 -1
  152. package/dist/cmd/cloud/storage/get.d.ts.map +1 -1
  153. package/dist/cmd/cloud/storage/get.js +5 -11
  154. package/dist/cmd/cloud/storage/get.js.map +1 -1
  155. package/dist/cmd/cloud/storage/list.d.ts.map +1 -1
  156. package/dist/cmd/cloud/storage/list.js +6 -6
  157. package/dist/cmd/cloud/storage/list.js.map +1 -1
  158. package/dist/cmd/cloud/stream/create.d.ts.map +1 -1
  159. package/dist/cmd/cloud/stream/create.js +7 -4
  160. package/dist/cmd/cloud/stream/create.js.map +1 -1
  161. package/dist/cmd/cloud/stream/delete.d.ts.map +1 -1
  162. package/dist/cmd/cloud/stream/delete.js +25 -4
  163. package/dist/cmd/cloud/stream/delete.js.map +1 -1
  164. package/dist/cmd/cloud/stream/get.d.ts.map +1 -1
  165. package/dist/cmd/cloud/stream/get.js +91 -62
  166. package/dist/cmd/cloud/stream/get.js.map +1 -1
  167. package/dist/cmd/cloud/stream/list.d.ts.map +1 -1
  168. package/dist/cmd/cloud/stream/list.js +66 -38
  169. package/dist/cmd/cloud/stream/list.js.map +1 -1
  170. package/dist/cmd/cloud/stream/util.d.ts +20 -0
  171. package/dist/cmd/cloud/stream/util.d.ts.map +1 -1
  172. package/dist/cmd/cloud/stream/util.js +27 -3
  173. package/dist/cmd/cloud/stream/util.js.map +1 -1
  174. package/dist/cmd/cloud/vector/util.d.ts.map +1 -1
  175. package/dist/cmd/cloud/vector/util.js +3 -3
  176. package/dist/cmd/cloud/vector/util.js.map +1 -1
  177. package/dist/cmd/dev/index.js.map +1 -1
  178. package/dist/cmd/git/account/add.js.map +1 -1
  179. package/dist/cmd/git/list.js.map +1 -1
  180. package/dist/cmd/project/add/database.d.ts +2 -0
  181. package/dist/cmd/project/add/database.d.ts.map +1 -0
  182. package/dist/cmd/project/add/database.js +123 -0
  183. package/dist/cmd/project/add/database.js.map +1 -0
  184. package/dist/cmd/project/add/domain.d.ts +2 -0
  185. package/dist/cmd/project/add/domain.d.ts.map +1 -0
  186. package/dist/cmd/project/add/domain.js +152 -0
  187. package/dist/cmd/project/add/domain.js.map +1 -0
  188. package/dist/cmd/project/add/index.d.ts +2 -0
  189. package/dist/cmd/project/add/index.d.ts.map +1 -0
  190. package/dist/cmd/project/add/index.js +35 -0
  191. package/dist/cmd/project/add/index.js.map +1 -0
  192. package/dist/cmd/project/add/storage.d.ts +2 -0
  193. package/dist/cmd/project/add/storage.d.ts.map +1 -0
  194. package/dist/cmd/project/add/storage.js +123 -0
  195. package/dist/cmd/project/add/storage.js.map +1 -0
  196. package/dist/cmd/project/auth/init.js.map +1 -1
  197. package/dist/cmd/project/index.d.ts.map +1 -1
  198. package/dist/cmd/project/index.js +7 -0
  199. package/dist/cmd/project/index.js.map +1 -1
  200. package/dist/cmd/project/reconcile.d.ts.map +1 -1
  201. package/dist/cmd/project/reconcile.js +32 -0
  202. package/dist/cmd/project/reconcile.js.map +1 -1
  203. package/dist/cmd/support/report.js.map +1 -1
  204. package/dist/cmd/support/system.js +2 -2
  205. package/dist/cmd/support/system.js.map +1 -1
  206. package/dist/config.d.ts +6 -3
  207. package/dist/config.d.ts.map +1 -1
  208. package/dist/config.js +31 -7
  209. package/dist/config.js.map +1 -1
  210. package/dist/errors.d.ts +2 -1
  211. package/dist/errors.d.ts.map +1 -1
  212. package/dist/errors.js +5 -0
  213. package/dist/errors.js.map +1 -1
  214. package/dist/index.d.ts +4 -1
  215. package/dist/index.d.ts.map +1 -1
  216. package/dist/index.js +3 -0
  217. package/dist/index.js.map +1 -1
  218. package/dist/repl.js +2 -1
  219. package/dist/repl.js.map +1 -1
  220. package/dist/tui/box.d.ts +3 -1
  221. package/dist/tui/box.d.ts.map +1 -1
  222. package/dist/tui/box.js +22 -7
  223. package/dist/tui/box.js.map +1 -1
  224. package/dist/tui/colors.d.ts +0 -3
  225. package/dist/tui/colors.d.ts.map +1 -1
  226. package/dist/tui/colors.js +76 -23
  227. package/dist/tui/colors.js.map +1 -1
  228. package/dist/tui/prompt.d.ts +2 -0
  229. package/dist/tui/prompt.d.ts.map +1 -1
  230. package/dist/tui/prompt.js +44 -3
  231. package/dist/tui/prompt.js.map +1 -1
  232. package/dist/tui/symbols.d.ts +0 -4
  233. package/dist/tui/symbols.d.ts.map +1 -1
  234. package/dist/tui/symbols.js +5 -0
  235. package/dist/tui/symbols.js.map +1 -1
  236. package/dist/tui.d.ts +8 -0
  237. package/dist/tui.d.ts.map +1 -1
  238. package/dist/tui.js +54 -9
  239. package/dist/tui.js.map +1 -1
  240. package/dist/types.d.ts +37 -2
  241. package/dist/types.d.ts.map +1 -1
  242. package/dist/types.js +1 -0
  243. package/dist/types.js.map +1 -1
  244. package/dist/version-check.d.ts.map +1 -1
  245. package/dist/version-check.js +5 -0
  246. package/dist/version-check.js.map +1 -1
  247. package/package.json +6 -6
  248. package/src/agent-detection.ts +457 -160
  249. package/src/ai-help.ts +391 -0
  250. package/src/api.ts +1 -1
  251. package/src/auth.ts +226 -17
  252. package/src/banner.ts +5 -0
  253. package/src/cache/agent-intro.ts +61 -0
  254. package/src/cache/index.ts +2 -0
  255. package/src/cache/resource-region.ts +28 -7
  256. package/src/catalyst.ts +16 -0
  257. package/src/cli.ts +375 -93
  258. package/src/cmd/ai/detect.ts +52 -0
  259. package/src/cmd/ai/index.ts +11 -1
  260. package/src/cmd/ai/intro.ts +154 -0
  261. package/src/cmd/ai/opencode/run.ts +5 -0
  262. package/src/cmd/build/ast.ts +97 -0
  263. package/src/cmd/build/vite/bun-dev-server.ts +2 -0
  264. package/src/cmd/build/vite/docs-generator.ts +15 -1
  265. package/src/cmd/build/vite/env-types-generator.ts +145 -0
  266. package/src/cmd/build/vite/index.ts +15 -0
  267. package/src/cmd/build/vite/public-asset-path-plugin.ts +8 -2
  268. package/src/cmd/build/vite/vite-builder.ts +31 -11
  269. package/src/cmd/cloud/db/create.ts +16 -16
  270. package/src/cmd/cloud/db/delete.ts +19 -19
  271. package/src/cmd/cloud/db/get.ts +32 -17
  272. package/src/cmd/cloud/deploy-fork.ts +2 -0
  273. package/src/cmd/cloud/deploy.ts +17 -0
  274. package/src/cmd/cloud/env/import.ts +6 -6
  275. package/src/cmd/cloud/env/list.ts +11 -11
  276. package/src/cmd/cloud/env/push.ts +6 -6
  277. package/src/cmd/cloud/keyvalue/util.ts +3 -3
  278. package/src/cmd/cloud/machine/list.ts +3 -3
  279. package/src/cmd/cloud/region/index.ts +3 -3
  280. package/src/cmd/cloud/region-lookup.ts +82 -22
  281. package/src/cmd/cloud/sandbox/cp.ts +9 -4
  282. package/src/cmd/cloud/sandbox/create.ts +1 -1
  283. package/src/cmd/cloud/sandbox/delete.ts +10 -7
  284. package/src/cmd/cloud/sandbox/download.ts +8 -5
  285. package/src/cmd/cloud/sandbox/env.ts +8 -5
  286. package/src/cmd/cloud/sandbox/exec.ts +10 -5
  287. package/src/cmd/cloud/sandbox/get.ts +13 -7
  288. package/src/cmd/cloud/sandbox/list.ts +47 -73
  289. package/src/cmd/cloud/sandbox/ls.ts +9 -5
  290. package/src/cmd/cloud/sandbox/mkdir.ts +9 -5
  291. package/src/cmd/cloud/sandbox/rm.ts +9 -5
  292. package/src/cmd/cloud/sandbox/rmdir.ts +9 -5
  293. package/src/cmd/cloud/sandbox/run.ts +1 -1
  294. package/src/cmd/cloud/sandbox/snapshot/build.ts +31 -31
  295. package/src/cmd/cloud/sandbox/snapshot/get.ts +17 -17
  296. package/src/cmd/cloud/sandbox/upload.ts +8 -5
  297. package/src/cmd/cloud/sandbox/util.ts +15 -14
  298. package/src/cmd/cloud/ssh.ts +2 -4
  299. package/src/cmd/cloud/storage/create.ts +16 -16
  300. package/src/cmd/cloud/storage/delete.ts +19 -19
  301. package/src/cmd/cloud/storage/get.ts +5 -16
  302. package/src/cmd/cloud/storage/list.ts +12 -6
  303. package/src/cmd/cloud/stream/create.ts +8 -4
  304. package/src/cmd/cloud/stream/delete.ts +28 -4
  305. package/src/cmd/cloud/stream/get.ts +102 -64
  306. package/src/cmd/cloud/stream/list.ts +76 -44
  307. package/src/cmd/cloud/stream/util.ts +39 -3
  308. package/src/cmd/cloud/vector/util.ts +3 -3
  309. package/src/cmd/dev/index.ts +4 -4
  310. package/src/cmd/git/account/add.ts +5 -5
  311. package/src/cmd/git/list.ts +7 -7
  312. package/src/cmd/project/add/database.ts +145 -0
  313. package/src/cmd/project/add/domain.ts +181 -0
  314. package/src/cmd/project/add/index.ts +35 -0
  315. package/src/cmd/project/add/storage.ts +147 -0
  316. package/src/cmd/project/auth/init.ts +6 -6
  317. package/src/cmd/project/index.ts +7 -0
  318. package/src/cmd/project/reconcile.ts +40 -0
  319. package/src/cmd/support/report.ts +5 -5
  320. package/src/cmd/support/system.ts +2 -2
  321. package/src/config.ts +40 -12
  322. package/src/errors.ts +7 -0
  323. package/src/index.ts +11 -0
  324. package/src/repl.ts +4 -1
  325. package/src/tui/box.ts +24 -9
  326. package/src/tui/colors.ts +83 -26
  327. package/src/tui/prompt.ts +55 -3
  328. package/src/tui/symbols.ts +6 -0
  329. package/src/tui.ts +55 -9
  330. package/src/types.ts +46 -2
  331. package/src/version-check.ts +6 -0
@@ -1,7 +1,8 @@
1
- import { spawn } from 'node:child_process';
1
+ import { dlopen, FFIType, ptr } from 'bun:ffi';
2
+ import { readFileSync } from 'node:fs';
2
3
  /**
3
4
  * Map of process names to internal agent short names.
4
- * The key is the process name (or substring) that appears in the parent process command line.
5
+ * The key is the process name (or substring) that appears in the parent process path or command line.
5
6
  * The value is the internal short name used to identify the agent.
6
7
  *
7
8
  * Process names verified via `agentuity cloud sandbox run --runtime <agent>:latest`:
@@ -29,212 +30,470 @@ export const KNOWN_AGENTS = [
29
30
  ['windsurf', 'windsurf'],
30
31
  ['zed', 'zed'],
31
32
  ['amp', 'amp'],
33
+ ['warp', 'warp'],
32
34
  // TODO: VSCode Agent Mode detection - need to find a reliable way to detect
33
35
  // when VSCode's built-in agent (Copilot Chat) is running commands vs just
34
36
  // running in VSCode's integrated terminal. May need env var detection.
35
37
  ];
36
38
  /**
37
- * Promise for the detection result (set when detection starts)
39
+ * Display names for known agents (human-friendly names)
38
40
  */
39
- let detectionPromise = null;
41
+ export const AGENT_DISPLAY_NAMES = {
42
+ opencode: 'Open Code',
43
+ codex: 'OpenAI Codex',
44
+ cursor: 'Cursor',
45
+ 'claude-code': 'Claude Code',
46
+ copilot: 'GitHub Copilot',
47
+ gemini: 'Gemini',
48
+ cline: 'Cline',
49
+ roo: 'Roo Code',
50
+ windsurf: 'Windsurf',
51
+ zed: 'Zed',
52
+ amp: 'Amp',
53
+ warp: 'Warp',
54
+ };
40
55
  /**
41
- * Cached result after detection completes (null = not yet resolved)
56
+ * Get the display name for an agent ID
42
57
  */
43
- let cachedResult = null;
58
+ export function getAgentDisplayName(agentId) {
59
+ return AGENT_DISPLAY_NAMES[agentId] || agentId;
60
+ }
61
+ // ============================================================================
62
+ // FFI-based Fast Process Path Resolution
63
+ // ============================================================================
64
+ const PATH_MAX = 4096; // Linux PATH_MAX, also sufficient for macOS
65
+ const ARG_MAX = 65536; // Maximum command line length
44
66
  /**
45
- * Callbacks to invoke when agent detection completes
67
+ * Stub FFI functions for unsupported platforms or when FFI fails to load.
46
68
  */
47
- const detectionCallbacks = [];
69
+ const unsupportedFFI = {
70
+ getProcessPath: () => null,
71
+ getParentPid: () => null,
72
+ getProcessCmdline: () => null,
73
+ };
48
74
  /**
49
- * Get the command line and parent PID for a given PID in a single ps call
75
+ * Initialize FFI functions for the current platform.
76
+ * Returns null functions for unsupported platforms or if FFI initialization fails.
50
77
  */
51
- function getProcessInfo(pid) {
52
- return new Promise((resolve) => {
53
- const ps = spawn('ps', ['-p', String(pid), '-o', 'ppid=,command='], {
54
- stdio: ['ignore', 'pipe', 'ignore'],
55
- });
56
- let output = '';
57
- ps.stdout.on('data', (data) => {
58
- output += data.toString();
59
- });
60
- ps.on('close', () => {
61
- const trimmed = output.trim();
62
- if (!trimmed) {
63
- resolve(undefined);
64
- return;
65
- }
66
- // Output format: " PPID COMMAND" (ppid is right-aligned, then space, then command)
67
- const match = trimmed.match(/^\s*(\d+)\s+(.+)$/);
68
- if (!match) {
69
- resolve(undefined);
70
- return;
71
- }
72
- const ppidStr = match[1];
73
- const commandStr = match[2];
74
- if (!ppidStr || !commandStr) {
75
- resolve(undefined);
76
- return;
77
- }
78
- const ppid = parseInt(ppidStr, 10);
79
- const command = commandStr.toLowerCase();
80
- if (isNaN(ppid) || ppid <= 1 || !command) {
81
- resolve(undefined);
82
- return;
83
- }
84
- resolve({ command, ppid });
85
- });
86
- ps.on('error', () => {
87
- resolve(undefined);
88
- });
89
- });
90
- }
91
- /**
92
- * Check if a command matches any known agent
93
- */
94
- function matchAgent(command) {
95
- for (const [processName, agentName] of KNOWN_AGENTS) {
96
- if (command.includes(processName)) {
97
- return agentName;
78
+ function initFFI() {
79
+ try {
80
+ if (process.platform === 'darwin') {
81
+ return initDarwinFFI();
82
+ }
83
+ else if (process.platform === 'linux') {
84
+ return initLinuxFFI();
98
85
  }
99
86
  }
100
- return undefined;
87
+ catch (err) {
88
+ // FFI initialization failed (e.g., missing libc on musl/Alpine, dlopen error)
89
+ // Fall back to unsupported stub - agent detection will be skipped
90
+ if (process.env.AGENTUITY_DEBUG_AGENT_DETECTION === '1') {
91
+ console.error('[agent-detection] FFI initialization failed:', err instanceof Error ? err.message : err);
92
+ }
93
+ return unsupportedFFI;
94
+ }
95
+ // Unsupported platform (Windows, etc.)
96
+ return unsupportedFFI;
101
97
  }
102
98
  /**
103
- * Detect the parent process command and check if it matches a known agent.
104
- * Walks up the process tree to find the first matching agent.
99
+ * Initialize macOS FFI functions using libSystem.dylib
100
+ *
101
+ * NOTE: Shared mutable buffers (pathBuf, argBuf, kinfoBuffer) are safe only in
102
+ * single-threaded use. Detection is synchronous and single-threaded, but any
103
+ * future concurrent usage would corrupt these buffers.
105
104
  */
106
- function detectParentAgent() {
107
- return new Promise((resolve) => {
108
- // TODO: Implement Windows support using wmic or PowerShell
109
- if (process.platform === 'win32') {
110
- resolve(undefined);
111
- return;
112
- }
113
- const maxDepth = 10; // Limit how far up we walk the tree
114
- async function walkTree(pid, depth) {
115
- if (depth >= maxDepth || pid <= 1) {
116
- return undefined;
105
+ function initDarwinFFI() {
106
+ // Shared buffers - reused across calls (single-threaded assumption)
107
+ const pathBuf = new Uint8Array(PATH_MAX);
108
+ const argBuf = new Uint8Array(ARG_MAX);
109
+ // Size of kinfo_proc struct on macOS (arm64 and x86_64)
110
+ const KINFO_PROC_SIZE = 648;
111
+ const kinfoBuffer = new Uint8Array(KINFO_PROC_SIZE);
112
+ // Load libSystem for proc_pidpath and sysctl
113
+ const lib = dlopen('libSystem.dylib', {
114
+ proc_pidpath: {
115
+ args: [FFIType.i32, FFIType.ptr, FFIType.u32],
116
+ returns: FFIType.i32,
117
+ },
118
+ sysctl: {
119
+ args: [FFIType.ptr, FFIType.u32, FFIType.ptr, FFIType.ptr, FFIType.ptr, FFIType.u64],
120
+ returns: FFIType.i32,
121
+ },
122
+ });
123
+ const getProcessPath = (pid) => {
124
+ if (pid <= 0)
125
+ return null;
126
+ try {
127
+ const len = lib.symbols.proc_pidpath(pid, ptr(pathBuf), PATH_MAX);
128
+ if (len > 0) {
129
+ return new TextDecoder().decode(pathBuf.subarray(0, len)).replace(/\0.*/, '');
117
130
  }
118
- // Get both command and parent PID in a single ps call
119
- const info = await getProcessInfo(pid);
120
- if (!info) {
121
- return undefined;
131
+ }
132
+ catch {
133
+ // Ignore errors (process may have died, permission denied, etc.)
134
+ }
135
+ return null;
136
+ };
137
+ // Get parent PID using sysctl KERN_PROC_PID
138
+ // CTL_KERN = 1, KERN_PROC = 14, KERN_PROC_PID = 1
139
+ const getParentPid = (pid) => {
140
+ if (pid <= 1)
141
+ return null;
142
+ try {
143
+ const mib = new Int32Array([1, 14, 1, pid]);
144
+ const sizePtr = new BigUint64Array([BigInt(KINFO_PROC_SIZE)]);
145
+ const result = lib.symbols.sysctl(ptr(mib), 4, ptr(kinfoBuffer), ptr(sizePtr), null, 0);
146
+ if (result === 0) {
147
+ // e_ppid offset in kinfo_proc on macOS arm64/x86_64:
148
+ // kp_eproc starts at 296, e_ppid is at offset 264 within kp_eproc
149
+ // Total offset: 560
150
+ const view = new DataView(kinfoBuffer.buffer);
151
+ const ppid = view.getInt32(560, true); // little-endian
152
+ return ppid > 1 ? ppid : null;
122
153
  }
123
- // Check if this process matches a known agent
124
- const agent = matchAgent(info.command);
125
- if (agent) {
126
- return agent;
154
+ }
155
+ catch {
156
+ // Ignore errors
157
+ }
158
+ return null;
159
+ };
160
+ // Get command line using sysctl KERN_PROCARGS2
161
+ // CTL_KERN = 1, KERN_PROCARGS2 = 49
162
+ const getProcessCmdline = (pid) => {
163
+ if (pid <= 0)
164
+ return null;
165
+ try {
166
+ // MIB for KERN_PROCARGS2: [CTL_KERN, KERN_PROCARGS2, pid]
167
+ const mib = new Int32Array([1, 49, pid]);
168
+ const sizePtr = new BigUint64Array([BigInt(ARG_MAX)]);
169
+ const result = lib.symbols.sysctl(ptr(mib), 3, ptr(argBuf), ptr(sizePtr), null, 0);
170
+ if (result === 0) {
171
+ const size = Number(sizePtr[0]);
172
+ if (size > 0) {
173
+ // KERN_PROCARGS2 format:
174
+ // - 4 bytes: argc (number of arguments)
175
+ // - exec_path\0
176
+ // - padding (zeros until aligned)
177
+ // - arg0\0arg1\0arg2\0...
178
+ // We want to extract exactly argc args (not env vars that follow)
179
+ const data = argBuf.subarray(0, size);
180
+ // Read argc (first 4 bytes, little-endian)
181
+ const argc = new DataView(data.buffer, data.byteOffset).getInt32(0, true);
182
+ let offset = 4;
183
+ // Skip exec_path (find first null)
184
+ while (offset < size && data[offset] !== 0)
185
+ offset++;
186
+ offset++; // Skip the null
187
+ // Skip padding (multiple nulls)
188
+ while (offset < size && data[offset] === 0)
189
+ offset++;
190
+ // Now we're at the arguments - collect exactly argc args
191
+ const args = [];
192
+ let start = offset;
193
+ for (let i = offset; i < size; i++) {
194
+ if (data[i] === 0) {
195
+ if (i > start) {
196
+ args.push(new TextDecoder().decode(data.subarray(start, i)));
197
+ }
198
+ start = i + 1;
199
+ // Stop after collecting argc args
200
+ if (args.length >= argc)
201
+ break;
202
+ }
203
+ }
204
+ return args.join(' ');
205
+ }
127
206
  }
128
- // Walk up to parent
129
- return walkTree(info.ppid, depth + 1);
130
207
  }
131
- const ppid = process.ppid;
132
- if (!ppid) {
133
- resolve(undefined);
134
- return;
208
+ catch {
209
+ // Ignore errors
135
210
  }
136
- walkTree(ppid, 0)
137
- .then(resolve)
138
- .catch(() => resolve(undefined));
139
- });
211
+ return null;
212
+ };
213
+ return { getProcessPath, getParentPid, getProcessCmdline };
140
214
  }
141
215
  /**
142
- * Start agent detection immediately (non-blocking).
143
- * Call this early in CLI startup to begin detection in the background.
216
+ * Initialize Linux FFI functions using libc.so.6
144
217
  */
145
- export function startAgentDetection() {
146
- if (detectionPromise !== null) {
147
- // Already started
148
- return;
149
- }
150
- detectionPromise = detectParentAgent().then((result) => {
151
- cachedResult = result;
152
- // Invoke all registered callbacks
153
- for (const callback of detectionCallbacks) {
154
- try {
155
- callback(result);
218
+ function initLinuxFFI() {
219
+ const pathBuf = new Uint8Array(PATH_MAX);
220
+ const lib = dlopen('libc.so.6', {
221
+ readlink: {
222
+ args: [FFIType.cstring, FFIType.ptr, FFIType.u64],
223
+ returns: FFIType.i64,
224
+ },
225
+ });
226
+ const getProcessPath = (pid) => {
227
+ if (pid <= 0)
228
+ return null;
229
+ try {
230
+ const procPath = Buffer.from(`/proc/${pid}/exe\0`);
231
+ const len = Number(lib.symbols.readlink(ptr(procPath), ptr(pathBuf), PATH_MAX));
232
+ if (len > 0) {
233
+ return new TextDecoder().decode(pathBuf.subarray(0, len));
156
234
  }
157
- catch {
158
- // Ignore callback errors
235
+ }
236
+ catch {
237
+ // Ignore errors (process may have died, permission denied, etc.)
238
+ }
239
+ return null;
240
+ };
241
+ const getParentPid = (pid) => {
242
+ if (pid <= 1)
243
+ return null;
244
+ try {
245
+ // Read /proc/{pid}/stat to get parent PID (4th field)
246
+ const statPath = `/proc/${pid}/stat`;
247
+ const content = readFileSync(statPath, 'utf-8');
248
+ // Format: pid (comm) state ppid ...
249
+ // Need to handle comm with spaces/parens: find last ')' then parse
250
+ const lastParen = content.lastIndexOf(')');
251
+ if (lastParen === -1)
252
+ return null;
253
+ const rest = content.slice(lastParen + 2); // Skip ') '
254
+ const fields = rest.split(' ');
255
+ const ppidField = fields[1]; // ppid is 2nd field after state
256
+ if (!ppidField)
257
+ return null;
258
+ const ppid = parseInt(ppidField, 10);
259
+ return isNaN(ppid) || ppid <= 1 ? null : ppid;
260
+ }
261
+ catch {
262
+ // Ignore errors
263
+ }
264
+ return null;
265
+ };
266
+ // Read command line from /proc/{pid}/cmdline (null-separated args)
267
+ const getProcessCmdline = (pid) => {
268
+ if (pid <= 0)
269
+ return null;
270
+ try {
271
+ const cmdlinePath = `/proc/${pid}/cmdline`;
272
+ const content = readFileSync(cmdlinePath);
273
+ if (content.length > 0) {
274
+ // Replace null bytes with spaces to get full command line
275
+ return new TextDecoder().decode(content).replace(/\0/g, ' ').trim();
159
276
  }
160
277
  }
161
- return result;
162
- });
278
+ catch {
279
+ // Ignore errors
280
+ }
281
+ return null;
282
+ };
283
+ return { getProcessPath, getParentPid, getProcessCmdline };
284
+ }
285
+ // Initialize FFI functions lazily
286
+ let ffi = null;
287
+ function getFFI() {
288
+ if (!ffi) {
289
+ ffi = initFFI();
290
+ }
291
+ return ffi;
163
292
  }
293
+ // ============================================================================
294
+ // Agent Detection Logic
295
+ // ============================================================================
164
296
  /**
165
- * Register a callback to be invoked when agent detection completes.
166
- * If detection has already completed, the callback is invoked immediately.
167
- * This is non-blocking and does not return a promise.
168
- *
169
- * @example
170
- * ```typescript
171
- * onAgentDetected((agent) => {
172
- * if (agent) {
173
- * console.log(`Detected agent: ${agent}`);
174
- * }
175
- * });
176
- * ```
297
+ * Cached detection result (null = not yet run, undefined = no agent detected)
177
298
  */
178
- export function onAgentDetected(callback) {
179
- // If detection already completed, invoke immediately
180
- if (cachedResult !== null) {
181
- try {
182
- callback(cachedResult);
299
+ let cachedResult = null;
300
+ /**
301
+ * Check if a path's basename matches any known agent
302
+ */
303
+ function matchAgentPath(path) {
304
+ // Extract basename from path
305
+ const basename = path.split('/').pop()?.toLowerCase() ?? '';
306
+ for (const [processName, agentName] of KNOWN_AGENTS) {
307
+ if (basename.includes(processName)) {
308
+ return agentName;
183
309
  }
184
- catch {
185
- // Ignore callback errors
310
+ }
311
+ return undefined;
312
+ }
313
+ /**
314
+ * Check if a cmdline matches any known agent.
315
+ * Only checks argv[0] and arguments that look like executable paths,
316
+ * NOT environment variables or arbitrary path strings.
317
+ */
318
+ function matchAgentCmdline(cmdline) {
319
+ // Split cmdline into arguments (null-separated on macOS/Linux, but we get space-separated here)
320
+ // The cmdline format from our FFI is: "cmd arg1 arg2 ENV1=val1 ENV2=val2..."
321
+ // We only want to check the command and args that look like executables
322
+ const parts = cmdline.split(/\s+/);
323
+ for (const part of parts) {
324
+ // Skip environment variables (contain =)
325
+ if (part.includes('=')) {
326
+ continue;
327
+ }
328
+ // Check if this looks like an executable path or command
329
+ // - Starts with / (absolute path)
330
+ // - Is a simple command name (no path separators, no special chars)
331
+ const isPath = part.startsWith('/');
332
+ const isSimpleCommand = !part.includes('/') && !part.includes('=') && part.length < 50;
333
+ if (isPath || isSimpleCommand) {
334
+ const basename = part.split('/').pop()?.toLowerCase() ?? '';
335
+ for (const [processName, agentName] of KNOWN_AGENTS) {
336
+ if (basename.includes(processName)) {
337
+ return agentName;
338
+ }
339
+ }
186
340
  }
187
- return;
188
341
  }
189
- // Otherwise, register for later invocation
190
- detectionCallbacks.push(callback);
342
+ return undefined;
191
343
  }
192
344
  /**
193
- * Get the cached detection result synchronously.
194
- * Returns undefined if detection hasn't completed yet or no agent was detected.
195
- * Returns null if detection hasn't started or completed yet.
196
- *
197
- * Use this for synchronous access when you don't want to wait for detection.
345
+ * Check if stdin is connected to a TTY (interactive terminal).
346
+ * When humans type commands, stdin is usually a TTY.
347
+ * When agents run commands programmatically, stdin is usually piped/closed.
198
348
  */
199
- export function getDetectedAgent() {
200
- return cachedResult;
349
+ function isInteractiveSession() {
350
+ return process.stdin.isTTY === true;
351
+ }
352
+ // Enable debug output with AGENTUITY_DEBUG_AGENT_DETECTION=1
353
+ const DEBUG = process.env.AGENTUITY_DEBUG_AGENT_DETECTION === '1';
354
+ function debugLog(...args) {
355
+ if (DEBUG) {
356
+ console.error('[agent-detection]', ...args);
357
+ }
201
358
  }
202
359
  /**
203
- * Wait for agent detection to complete and ensure all callbacks have been invoked.
204
- * Call this before CLI exit to ensure the detected agent is written to session logs.
360
+ * Synchronously detect the parent agent by walking up the process tree.
361
+ * Uses FFI for fast process path and command line resolution.
205
362
  *
206
- * This is a no-op if detection hasn't started or has already completed.
363
+ * Detection strategy:
364
+ * - If stdin is a TTY (interactive session), don't report as agent
365
+ * (human is typing commands, even if inside an agent's terminal)
366
+ * - If stdin is NOT a TTY, check the process tree for known agents
207
367
  */
208
- export async function flushAgentDetection() {
209
- if (detectionPromise !== null) {
210
- await detectionPromise;
368
+ function detectParentAgent() {
369
+ debugLog('Starting detection, PID:', process.pid, 'PPID:', process.ppid);
370
+ debugLog('stdin.isTTY:', process.stdin.isTTY);
371
+ // Dump relevant env vars for debugging agent detection
372
+ if (DEBUG) {
373
+ const relevantEnvVars = Object.entries(process.env)
374
+ .filter(([key]) => key.startsWith('WARP') ||
375
+ key.startsWith('TERM') ||
376
+ key.startsWith('AGENTUITY') ||
377
+ key === 'SHELL' ||
378
+ key === 'LC_TERMINAL' ||
379
+ key === 'ITERM_SESSION_ID' ||
380
+ key === 'VSCODE_INJECTION' ||
381
+ key === 'CURSOR_TRACE_ID')
382
+ .map(([key, value]) => `${key}=${value}`)
383
+ .join(', ');
384
+ debugLog('Relevant env vars:', relevantEnvVars || '(none)');
385
+ debugLog('WARP_AGENT_MODE:', process.env.WARP_AGENT_MODE ?? '(not set)');
211
386
  }
387
+ // Short-circuit: if AGENTUITY_AGENT_MODE is set, use it directly
388
+ // Use "false", "0", or "none" to explicitly disable detection
389
+ const agentMode = process.env.AGENTUITY_AGENT_MODE;
390
+ if (agentMode) {
391
+ if (agentMode === 'false' || agentMode === '0' || agentMode === 'none') {
392
+ debugLog('AGENTUITY_AGENT_MODE explicitly disabled:', agentMode);
393
+ return undefined;
394
+ }
395
+ debugLog('Using AGENTUITY_AGENT_MODE:', agentMode);
396
+ return agentMode;
397
+ }
398
+ // Warp terminal: detect via WARP_AGENT_MODE env var (set by Warp AI)
399
+ // Note: Warp doesn't currently set this, but will in the future
400
+ if (process.env.WARP_AGENT_MODE === 'true') {
401
+ debugLog('Detected Warp AI via WARP_AGENT_MODE=true');
402
+ return 'warp';
403
+ }
404
+ // If this is an interactive session (TTY), assume human is running the command
405
+ // Note: Warp AI also runs with TTY=true, so Warp users should set
406
+ // AGENTUITY_AGENT_MODE=warp until Warp sets WARP_AGENT_MODE=true
407
+ if (isInteractiveSession()) {
408
+ debugLog('Interactive session (TTY), skipping detection');
409
+ return undefined;
410
+ }
411
+ // Unsupported on Windows (for now)
412
+ if (process.platform === 'win32') {
413
+ debugLog('Windows not supported');
414
+ return undefined;
415
+ }
416
+ const { getProcessPath, getParentPid, getProcessCmdline } = getFFI();
417
+ // Guard for no parent process (e.g., PID 1 in containers)
418
+ const ppid = process.ppid;
419
+ if (!ppid || ppid <= 1) {
420
+ debugLog('No parent process (ppid:', ppid, ')');
421
+ return undefined;
422
+ }
423
+ // Walk up the process tree looking for known agents
424
+ const maxDepth = 10;
425
+ let currentPid = ppid;
426
+ for (let depth = 0; depth < maxDepth && currentPid > 1; depth++) {
427
+ // Check the executable path for agent match
428
+ const path = getProcessPath(currentPid);
429
+ debugLog(`[${depth}] PID ${currentPid} path:`, path);
430
+ if (path) {
431
+ const agent = matchAgentPath(path);
432
+ if (agent) {
433
+ debugLog(`[${depth}] Matched agent from path:`, agent);
434
+ return agent;
435
+ }
436
+ }
437
+ // Check the command line (for agents running as node/bun scripts)
438
+ const cmdline = getProcessCmdline(currentPid);
439
+ debugLog(`[${depth}] PID ${currentPid} cmdline:`, cmdline?.substring(0, 200) + (cmdline && cmdline.length > 200 ? '...' : ''));
440
+ if (cmdline) {
441
+ const agent = matchAgentCmdline(cmdline);
442
+ if (agent) {
443
+ debugLog(`[${depth}] Matched agent from cmdline:`, agent);
444
+ return agent;
445
+ }
446
+ }
447
+ // Move up
448
+ const parentPid = getParentPid(currentPid);
449
+ debugLog(`[${depth}] Parent of ${currentPid}:`, parentPid);
450
+ if (!parentPid)
451
+ break;
452
+ currentPid = parentPid;
453
+ }
454
+ debugLog('No agent found');
455
+ return undefined;
212
456
  }
213
457
  /**
214
- * Check if the CLI is being executed from a known coding agent.
215
- * Returns the agent name if detected, undefined otherwise.
458
+ * Get the executing agent if the CLI is being run from a known coding agent.
459
+ * Returns the agent ID if detected, undefined otherwise.
216
460
  *
217
- * This function returns immediately if detection has already completed,
218
- * otherwise it awaits the detection promise started by startAgentDetection().
461
+ * This function runs synchronously using FFI for fast process path resolution.
462
+ * Results are cached after the first call.
219
463
  *
220
464
  * @example
221
465
  * ```typescript
222
- * const agent = await isExecutingFromAgent();
466
+ * const agent = getExecutingAgent();
223
467
  * if (agent) {
224
468
  * logger.debug(`Running from agent: ${agent}`);
225
469
  * }
226
470
  * ```
227
471
  */
228
- export async function isExecutingFromAgent() {
229
- // Return cached result if detection has completed
472
+ export function getExecutingAgent() {
473
+ // Return cached result if already detected
230
474
  if (cachedResult !== null) {
231
- return cachedResult;
475
+ return cachedResult ?? undefined;
232
476
  }
233
- // If detection hasn't started yet, start it now
234
- if (detectionPromise === null) {
235
- startAgentDetection();
477
+ // Run detection and cache
478
+ cachedResult = detectParentAgent();
479
+ return cachedResult;
480
+ }
481
+ /**
482
+ * Get environment variables to pass to subprocesses for agent detection.
483
+ * This allows child processes to skip re-detection by using the cached result.
484
+ *
485
+ * @example
486
+ * ```typescript
487
+ * const proc = Bun.spawn(['bun', 'run', 'dev'], {
488
+ * env: { ...process.env, ...getAgentEnv() },
489
+ * });
490
+ * ```
491
+ */
492
+ export function getAgentEnv() {
493
+ const agent = getExecutingAgent();
494
+ if (agent) {
495
+ return { AGENTUITY_AGENT_MODE: agent };
236
496
  }
237
- // Wait for detection to complete
238
- return detectionPromise;
497
+ return {};
239
498
  }
240
499
  //# sourceMappingURL=agent-detection.js.map