@cursor/july 0.1.2 → 0.1.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 (174) hide show
  1. package/AGENTS.md +16 -5
  2. package/README.md +8 -9
  3. package/dist/bin/agent-serve.d.ts +3 -1
  4. package/dist/bin/agent-serve.d.ts.map +1 -1
  5. package/dist/bin/agent-serve.js +389 -152
  6. package/dist/docs/404.html +2 -2
  7. package/dist/docs/ab.html +3 -3
  8. package/dist/docs/assets/{app.Oje4vhlk.js → app.BR0RbIdx.js} +1 -1
  9. package/dist/docs/assets/chunks/@localSearchIndexroot.DtIOQzGj.js +1 -0
  10. package/dist/docs/assets/chunks/{VPLocalSearchBox.H5XZ2zCB.js → VPLocalSearchBox.qjsVTneI.js} +1 -1
  11. package/dist/docs/assets/chunks/{theme.CTR_TuaE.js → theme.2Kg8jIp_.js} +2 -2
  12. package/dist/docs/assets/{quickstart.md.CfU8_uTC.js → quickstart.md.BU6Iwi_9.js} +18 -6
  13. package/dist/docs/assets/{reference_cli.md.DA730zCu.js → reference_cli.md.Bv6pOxcF.js} +11 -6
  14. package/dist/docs/assets/{reference_cli.md.DA730zCu.lean.js → reference_cli.md.Bv6pOxcF.lean.js} +1 -1
  15. package/dist/docs/building-with-agents.html +3 -3
  16. package/dist/docs/concepts.html +3 -3
  17. package/dist/docs/deployment.html +3 -3
  18. package/dist/docs/evals.html +3 -3
  19. package/dist/docs/guides/agent-to-agent.html +3 -3
  20. package/dist/docs/guides/cloud-runtime.html +3 -3
  21. package/dist/docs/guides/github.html +3 -3
  22. package/dist/docs/guides/human-in-the-loop.html +3 -3
  23. package/dist/docs/guides/slack.html +3 -3
  24. package/dist/docs/guides/webhooks.html +3 -3
  25. package/dist/docs/hashmap.json +1 -1
  26. package/dist/docs/hillclimbing.html +3 -3
  27. package/dist/docs/index.html +3 -3
  28. package/dist/docs/quickstart.html +22 -10
  29. package/dist/docs/reference/agent-config.html +3 -3
  30. package/dist/docs/reference/channels.html +3 -3
  31. package/dist/docs/reference/cli.html +14 -9
  32. package/dist/docs/reference/connections.html +3 -3
  33. package/dist/docs/reference/hooks.html +3 -3
  34. package/dist/docs/reference/http-api.html +3 -3
  35. package/dist/docs/reference/instructions.html +3 -3
  36. package/dist/docs/reference/playground.html +3 -3
  37. package/dist/docs/reference/project-layout.html +3 -3
  38. package/dist/docs/reference/schedules.html +3 -3
  39. package/dist/docs/reference/sessions.html +3 -3
  40. package/dist/docs/reference/skills.html +3 -3
  41. package/dist/docs/reference/subagents.html +3 -3
  42. package/dist/docs/reference/tools.html +3 -3
  43. package/dist/docs/scaffolding-agents.html +3 -3
  44. package/dist/docs/storage.html +3 -3
  45. package/dist/docs/troubleshooting.html +3 -3
  46. package/dist/internal/chat-client.d.ts +29 -3
  47. package/dist/internal/chat-client.d.ts.map +1 -1
  48. package/dist/internal/chat-client.js +180 -27
  49. package/dist/internal/cli-ax.d.ts +75 -2
  50. package/dist/internal/cli-ax.d.ts.map +1 -1
  51. package/dist/internal/cli-ax.js +600 -52
  52. package/dist/internal/cli-cursor.d.ts.map +1 -1
  53. package/dist/internal/cli-cursor.js +8 -36
  54. package/dist/internal/cli-deploy.d.ts +59 -3
  55. package/dist/internal/cli-deploy.d.ts.map +1 -1
  56. package/dist/internal/cli-deploy.js +316 -38
  57. package/dist/internal/cursor/credentials.d.ts +9 -0
  58. package/dist/internal/cursor/credentials.d.ts.map +1 -1
  59. package/dist/internal/cursor/credentials.js +12 -0
  60. package/dist/internal/deploy-client.d.ts +9 -1
  61. package/dist/internal/deploy-client.d.ts.map +1 -1
  62. package/dist/internal/deploy-client.js +23 -3
  63. package/dist/internal/deploy-source.d.ts +35 -0
  64. package/dist/internal/deploy-source.d.ts.map +1 -0
  65. package/dist/internal/deploy-source.js +118 -0
  66. package/dist/internal/discovery.d.ts +11 -0
  67. package/dist/internal/discovery.d.ts.map +1 -1
  68. package/dist/internal/discovery.js +43 -14
  69. package/dist/internal/eval-run-store.d.ts.map +1 -1
  70. package/dist/internal/eval-run-store.js +2 -1
  71. package/dist/internal/eval-runner.d.ts +2 -0
  72. package/dist/internal/eval-runner.d.ts.map +1 -1
  73. package/dist/internal/eval-runner.js +2 -0
  74. package/dist/internal/event-mapper.d.ts +54 -1
  75. package/dist/internal/event-mapper.d.ts.map +1 -1
  76. package/dist/internal/event-mapper.js +151 -41
  77. package/dist/internal/init-project.d.ts +88 -1
  78. package/dist/internal/init-project.d.ts.map +1 -1
  79. package/dist/internal/init-project.js +221 -31
  80. package/dist/internal/install-cursor-skills.d.ts +64 -0
  81. package/dist/internal/install-cursor-skills.d.ts.map +1 -0
  82. package/dist/internal/install-cursor-skills.js +274 -0
  83. package/dist/internal/logs-client.d.ts +60 -0
  84. package/dist/internal/logs-client.d.ts.map +1 -0
  85. package/dist/internal/logs-client.js +311 -0
  86. package/dist/internal/open-browser.d.ts +5 -0
  87. package/dist/internal/open-browser.d.ts.map +1 -0
  88. package/dist/internal/open-browser.js +34 -0
  89. package/dist/internal/playground/toolchain.d.ts.map +1 -1
  90. package/dist/internal/playground/toolchain.js +16 -11
  91. package/dist/internal/playground-proxy.d.ts +69 -0
  92. package/dist/internal/playground-proxy.d.ts.map +1 -0
  93. package/dist/internal/playground-proxy.js +468 -0
  94. package/dist/internal/prompt-context.d.ts +35 -0
  95. package/dist/internal/prompt-context.d.ts.map +1 -0
  96. package/dist/internal/prompt-context.js +71 -0
  97. package/dist/internal/request-headers.d.ts +11 -0
  98. package/dist/internal/request-headers.d.ts.map +1 -0
  99. package/dist/internal/request-headers.js +14 -0
  100. package/dist/internal/resolve-prod-target.d.ts +42 -0
  101. package/dist/internal/resolve-prod-target.d.ts.map +1 -0
  102. package/dist/internal/resolve-prod-target.js +111 -0
  103. package/dist/internal/run-client.d.ts +2 -2
  104. package/dist/internal/run-client.d.ts.map +1 -1
  105. package/dist/internal/run-client.js +38 -23
  106. package/dist/internal/server.d.ts.map +1 -1
  107. package/dist/internal/server.js +10 -4
  108. package/dist/internal/session-engine.d.ts +1 -1
  109. package/dist/internal/session-engine.d.ts.map +1 -1
  110. package/dist/internal/session-engine.js +21 -6
  111. package/dist/internal/sessions-client.d.ts +21 -0
  112. package/dist/internal/sessions-client.d.ts.map +1 -0
  113. package/dist/internal/sessions-client.js +168 -0
  114. package/dist/internal/stream-progress.d.ts.map +1 -1
  115. package/dist/internal/stream-progress.js +31 -3
  116. package/dist/internal/terminal-style.d.ts +22 -0
  117. package/dist/internal/terminal-style.d.ts.map +1 -0
  118. package/dist/internal/terminal-style.js +49 -0
  119. package/dist/internal/trajectory.d.ts +10 -5
  120. package/dist/internal/trajectory.d.ts.map +1 -1
  121. package/dist/internal/trajectory.js +82 -10
  122. package/dist/internal/workspace.d.ts +11 -0
  123. package/dist/internal/workspace.d.ts.map +1 -1
  124. package/dist/internal/workspace.js +38 -4
  125. package/dist/playground/assets/index-D-vo2lV_.css +1 -0
  126. package/dist/playground/assets/index-x60b9q2j.js +312 -0
  127. package/dist/playground/index.html +2 -2
  128. package/dist/types.d.ts +26 -1
  129. package/dist/types.d.ts.map +1 -1
  130. package/docs/quickstart.md +22 -7
  131. package/docs/reference/cli.md +51 -3
  132. package/package.json +3 -1
  133. package/skills/ab/SKILL.md +8 -8
  134. package/skills/create-agent/SKILL.md +28 -22
  135. package/skills/debug/SKILL.md +11 -11
  136. package/skills/evals/SKILL.md +16 -16
  137. package/skills/framework-map/SKILL.md +6 -6
  138. package/skills/github/SKILL.md +13 -13
  139. package/skills/hillclimb/SKILL.md +10 -10
  140. package/skills/setup-slack/SKILL.md +13 -13
  141. package/src/bin/agent-serve.ts +422 -188
  142. package/src/internal/chat-client.ts +252 -37
  143. package/src/internal/cli-ax.ts +722 -72
  144. package/src/internal/cli-cursor.ts +14 -42
  145. package/src/internal/cli-deploy.ts +428 -49
  146. package/src/internal/cursor/credentials.ts +14 -0
  147. package/src/internal/deploy-client.ts +45 -4
  148. package/src/internal/deploy-source.ts +133 -0
  149. package/src/internal/discovery.ts +53 -16
  150. package/src/internal/eval-run-store.ts +2 -1
  151. package/src/internal/eval-runner.ts +5 -0
  152. package/src/internal/event-mapper.ts +222 -42
  153. package/src/internal/init-project.ts +333 -51
  154. package/src/internal/install-cursor-skills.ts +327 -0
  155. package/src/internal/logs-client.ts +442 -0
  156. package/src/internal/open-browser.ts +41 -0
  157. package/src/internal/playground/toolchain.ts +15 -13
  158. package/src/internal/playground-proxy.ts +576 -0
  159. package/src/internal/prompt-context.ts +95 -0
  160. package/src/internal/request-headers.ts +23 -0
  161. package/src/internal/resolve-prod-target.ts +150 -0
  162. package/src/internal/run-client.ts +39 -36
  163. package/src/internal/server.ts +12 -3
  164. package/src/internal/session-engine.ts +22 -3
  165. package/src/internal/sessions-client.ts +182 -0
  166. package/src/internal/stream-progress.ts +36 -2
  167. package/src/internal/terminal-style.ts +74 -0
  168. package/src/internal/trajectory.ts +91 -13
  169. package/src/internal/workspace.ts +42 -4
  170. package/src/types.ts +42 -6
  171. package/dist/docs/assets/chunks/@localSearchIndexroot.zwQ9RCQ7.js +0 -1
  172. package/dist/playground/assets/index-B1Qc9h2u.css +0 -1
  173. package/dist/playground/assets/index-CpDYCj8W.js +0 -79
  174. /package/dist/docs/assets/{quickstart.md.CfU8_uTC.lean.js → quickstart.md.BU6Iwi_9.lean.js} +0 -0
@@ -3,10 +3,12 @@
3
3
  * agent-serve CLI.
4
4
  *
5
5
  * agent-serve serve [--dir .] [--port 3000] [--host 127.0.0.1] [--dev]
6
+ * agent-serve dev [path] [--port 3000] [--host 127.0.0.1]
6
7
  * agent-serve chat [--url http://127.0.0.1:3000] [--message "..."] [--json]
8
+ * agent-serve resume <sessionId> [--url | --prod] [--message "..."]
7
9
  * agent-serve run [--dir .] [--message "..."] [--json|--text]
8
10
  * agent-serve trajectory --events <file.ndjson>
9
- * agent-serve init [dir]
11
+ * agent-serve init [dir] [--json]
10
12
  * agent-serve eval [--dir .] [evalId...]
11
13
  * agent-serve validate [--dir .]
12
14
  * agent-serve slack <subcommand>
@@ -22,13 +24,18 @@ import {
22
24
  serve,
23
25
  } from "../index.js";
24
26
  import { registerAuthoredModuleLoaders } from "../internal/authored-loaders.js";
25
- import { runChat } from "../internal/chat-client.js";
26
27
  import {
27
28
  type AxCliOptions,
28
29
  cmdCall,
30
+ cmdChat,
29
31
  cmdEval,
30
32
  cmdInit,
33
+ cmdLogs,
34
+ cmdPlayground,
35
+ cmdResume,
31
36
  cmdRun,
37
+ cmdSession,
38
+ cmdSessions,
32
39
  cmdTrajectory,
33
40
  } from "../internal/cli-ax.js";
34
41
  import {
@@ -44,8 +51,7 @@ import {
44
51
  type DeployCliOptions,
45
52
  } from "../internal/cli-deploy.js";
46
53
  import { CLI_COMMAND_NAME as CLI } from "../internal/distribution.js";
47
- import { runSession } from "../internal/run-client.js";
48
- import { formatTrajectoryText } from "../internal/trajectory.js";
54
+ import { clearTerminal, stdoutPalette } from "../internal/terminal-style.js";
49
55
  import type { AgentProject, AgentServeHandle } from "../types.js";
50
56
 
51
57
  const HELP = `${CLI} — serve a filesystem-defined Cursor agent over channels
@@ -55,24 +61,42 @@ Usage:
55
61
  [--mode single|multi] [--api-key <key>] [--no-schedules]
56
62
  [--public-url <url>]
57
63
  [--cursor-events --repo <owner/name>]...
58
- ${CLI} chat [--url <baseUrl>] [--message <text>] [--json] [--text]
59
- ${CLI} run [--dir <path>] [--message <text>]... [--url <baseUrl>]
64
+ ${CLI} dev [path] [--port <n>] [--host <host>]
65
+ Same flags as serve; implies --dev. Path defaults to
66
+ the current directory (or pass --dir).
67
+ ${CLI} chat [--url <baseUrl> | --prod] [--message <text>] [--json] [--text]
68
+ [--session <id>] [--continuation-token <t>]
69
+ [--slug <slug>] [--team <id>]
70
+ ${CLI} resume <sessionId> [--url <baseUrl> | --prod] [--message <text>]
71
+ [--json] [--text] [--continuation-token <t>]
72
+ [--slug <slug>] [--team <id>]
73
+ ${CLI} logs [--url <baseUrl> | --prod] [--once] [--json]
74
+ [--slug <slug>] [--team <id>] [--dir <path>]
75
+ ${CLI} sessions [--url <baseUrl> | --prod] [--json]
76
+ [--slug <slug>] [--team <id>] [--dir <path>]
77
+ ${CLI} session <sessionId> [--url <baseUrl> | --prod]
78
+ [--json | --text | --events]
79
+ [--slug <slug>] [--team <id>] [--dir <path>]
80
+ ${CLI} playground [--url <baseUrl> | --prod] [--session <id>] [--print]
81
+ [--slug <slug>] [--team <id>] [--dir <path>]
82
+ ${CLI} run [--dir <path>] [--message <text>]... [--url <baseUrl> | --prod]
60
83
  [--session <id>] [--continuation-token <t>]
61
84
  [--events <file.ndjson>] [--no-events] [--text]
62
- [--timeout-ms <n>] [--no-stream]
85
+ [--timeout-ms <n>] [--no-stream] [--slug <slug>] [--team <id>]
63
86
  ${CLI} trajectory --events <file.ndjson> [--text]
64
- ${CLI} call <tool> [--input <json>] [--dir <path> | --url <baseUrl>]
65
- [--session <id>] [--slug <slug>]
66
- ${CLI} init [dir]
87
+ ${CLI} call <tool> [--input <json>] [--dir <path> | --url <baseUrl> | --prod]
88
+ [--session <id>] [--slug <slug>] [--team <id>]
89
+ ${CLI} init [dir] [--json]
67
90
  ${CLI} eval [--dir <path>] [evalId...] [--list] [--json]
68
91
  [--tag <tag>]... [--verbose] [--timeout-ms <n>]
69
- [--no-stream]
92
+ [--no-stream] [--url <baseUrl> | --prod] [--slug <slug>]
93
+ [--team <id>]
70
94
  ${CLI} info [--dir <path>] [--json]
71
95
  ${CLI} validate [--dir <path>]
72
96
  ${CLI} login [--api-key <key>] [--key-name <name>]
73
97
  ${CLI} logout
74
98
  ${CLI} whoami [--json]
75
- ${CLI} deploy [--dir <path>] [--slug <slug>] [--team <id>] [--json]
99
+ ${CLI} deploy [--dir <path>] [--slug <slug> | --all] [--team <id>] [--json]
76
100
  [--no-wait] [--repo <url>] [--ref <ref>]
77
101
  [--path <agentPath>] [--cursor-events-repo <owner/name>]...
78
102
  [--allow-domain <domain>]...
@@ -89,15 +113,41 @@ Usage:
89
113
  Commands:
90
114
  serve Serve agents under /<slug>/... (index at /). On a TTY, press
91
115
  Enter to restart (reloads agent code + Vite HMR child).
92
- chat Talk to a running server (streams the reply). --json prints a trajectory.
116
+ dev Local development: same as \`${CLI} serve --dev\` (manual
117
+ schedule/reminder dispatch, unsigned GitHub loopback, Vite HMR).
118
+ chat Interactive REPL against a running server (follow-ups keep the
119
+ session + continuation token). Optional --message seeds the first
120
+ turn then stays open on a TTY. --session reattaches (looks up the
121
+ continuation token via /v1/sessions when omitted) and replays the
122
+ transcript. --json is one-shot. Pass --prod to hit the team's
123
+ hosted deployment (slug from --slug or the directory name).
124
+ resume Reattach chat to an existing session id (same as
125
+ \`${CLI} chat --session <id>\`). Looks up the continuation token
126
+ via /v1/sessions when --continuation-token is omitted, then
127
+ replays the transcript. Pass --prod for a hosted deployment.
128
+ logs Tail agent logs. Default follows (Ctrl-C to stop); --once dumps
129
+ the current buffer. --prod shows deploy progress until the
130
+ hosted deployment is running, then tails runtime logs. Without
131
+ --prod, tails the local server (or --url).
132
+ sessions List sessions on a running agent (or --prod hosted deployment).
133
+ session Show one session. Default: text trajectory. --json for trajectory
134
+ JSON; --events for the raw event list. To continue interactively,
135
+ use \`${CLI} resume <id>\`.
136
+ playground Open the web playground. --prod starts a loopback proxy that
137
+ injects hosted engine credentials (browsers cannot set them).
138
+ --print skips opening a browser. Ctrl-C stops the proxy.
93
139
  run One-shot create→test loop: ephemeral server + JSON trajectory.
94
140
  Writes a single events.ndjson trace (visualize in the playground).
95
141
  Streams tool calls and reply text to stderr when stderr is a TTY.
142
+ Pass --prod to hit the hosted deployment instead of a local server.
96
143
  trajectory Summarize an events.ndjson file into a trajectory.
97
144
  call Call an authored server tool deterministically (no model turn)
98
145
  via POST /v1/tools/:toolName. --session runs it inside an
99
146
  existing session and records it on that session's event stream.
100
- init Scaffold a project, or print the coding-agent setup guide (no args).
147
+ init Scaffold a project (skipping existing files), npm install,
148
+ run login when unsigned, then print next steps. --json is
149
+ machine-readable and never blocks on login. No directory:
150
+ print the setup guide.
101
151
  eval Run filesystem evals under evals/**/*.eval.ts (streams on a TTY).
102
152
  Pass evalId args to select: one datapoint (weather/nyc), a whole
103
153
  file (weather → all cases in that file), several of either, or
@@ -110,8 +160,11 @@ Commands:
110
160
  logout Remove the stored Cursor credentials.
111
161
  whoami Show which Cursor account powers this host (and why).
112
162
  deploy Deploy onto Cursor's managed agent-serve hosting, then wait for
113
- the engine to come up. First deploy of a slug prints the alias
114
- token exactly once save it.
163
+ the engine to come up. --repo/--ref/--path stay available;
164
+ inside an agent git checkout they are inferred when omitted
165
+ (flags always win). When --dir is a multi-agent parent,
166
+ pick one interactively, or pass --slug / --all. First deploy
167
+ of a slug prints the alias token exactly once — save it.
115
168
  deployments List the team's deployments (slug, status, generation, kind).
116
169
  deployment Show one deployment in full (status, lastError, engine info).
117
170
  stop Stop a deployment and wait until it reports stopped.
@@ -132,6 +185,7 @@ Agent loop:
132
185
  ${CLI} info --dir . --json
133
186
  ${CLI} run --dir . --message "..."
134
187
  ${CLI} eval --dir .
188
+ ${CLI} dev .
135
189
 
136
190
  Options:
137
191
  --dir Project root (defaults to the current directory).
@@ -152,28 +206,49 @@ Options:
152
206
  send it as the Authorization bearer.
153
207
  --allow-anonymous serve: admit all HTTP callers (allowAll). For demos behind a
154
208
  trusted network only; mutually exclusive with --bearer-token.
209
+ --allow-anonymous-cursor-github
210
+ serve: allow --allow-anonymous together with
211
+ githubChannel({ cursorAccount }). Admitted callers can drive
212
+ sessions holding the account's repo-scoped GitHub credential,
213
+ so use only behind a trusted boundary (e.g. an SSO proxy).
155
214
  --public-url serve: base URL where this host is reachable from outside
156
215
  (e.g. https://agent-serve.example.com). Required for peer
157
216
  connections (defineConnection({ agent })) on cloud-runtime
158
217
  turns; local turns always use loopback.
159
218
  --no-schedules Disable the cron runner outside dev mode.
160
- --team deploy/deployments/deployment/stop/rotate-token: Cursor
161
- team id. Defaults to the account's team when it has one.
219
+ --team deploy/deployments/deployment/stop/rotate-token and
220
+ chat/resume/run/call/eval/logs/sessions/session/playground
221
+ --prod: Cursor team id. Defaults to the account's team when
222
+ it has one.
223
+ --prod chat/resume/run/call/eval/logs/sessions/session/playground:
224
+ use the team's hosted deployment. Slug from --slug or the
225
+ --dir basename. Mutually exclusive with --url. Requires
226
+ \`${CLI} login\`.
227
+ --once logs: print current progress / log buffer once and exit
228
+ (default is to follow).
229
+ --print playground: print the URL only; do not open a browser.
162
230
  --no-wait deploy/stop: return right after the request is accepted
163
231
  instead of polling until a terminal status.
164
232
  --no-playground Disable the bundled web playground at /playground (also skips
165
233
  auto-build / HMR).
166
234
  --no-docs Disable the documentation site at /docs (also skips its
167
235
  auto-build).
168
- --url chat/run/eval: base URL of a running agent (skip local host).
169
- --message chat/run: user message (repeatable for run).
236
+ --url chat/resume/run/call/eval/logs/sessions/session/playground:
237
+ base URL of a running agent (skip local host). Mutually
238
+ exclusive with --prod.
239
+ --message chat/resume/run: user message (repeatable for run).
170
240
  --input call: JSON object of tool arguments (default {}).
171
241
  --messages-file JSON array of strings (run).
172
- --session Existing session id for a follow-up turn.
173
- --continuation-token Token from a prior run/chat response.
242
+ --session Existing session id (run/call follow-up, or chat reattach).
243
+ Prefer \`${CLI} resume <sessionId>\` for interactive
244
+ reattach.
245
+ --continuation-token Token from a prior run/chat/resume response (optional
246
+ for resume / chat --session; looked up via /v1/sessions
247
+ when omitted).
174
248
  --events run: write NDJSON trace to this path (default
175
249
  <dir>/.agent-serve/traces/<sessionId>.ndjson).
176
- trajectory: read this file.
250
+ trajectory: read this file. session: print raw
251
+ { sessionId, events } JSON (boolean; no path).
177
252
  --no-events run: do not write a trace file.
178
253
  --slug Agent slug when --dir mounts multiple agents.
179
254
  --timeout-ms Abort a hung turn after N milliseconds.
@@ -181,7 +256,9 @@ Options:
181
256
  --list eval: print discovered cases without running.
182
257
  --verbose eval: stream t.log lines and reply snippets.
183
258
  --no-stream run/eval: silence live progress on stderr (JSON still prints).
184
- --json JSON output (info/chat/eval/github; run defaults to JSON).
259
+ --json JSON output (init/info/chat/eval/logs/sessions/session/
260
+ github; run defaults to JSON). logs: NDJSON progress/log
261
+ events. session: trajectory JSON (not raw events).
185
262
  --no-color chat: force plain output.
186
263
  --cursor-events serve: pull SCM webhook events from /v0/scm-events. The
187
264
  stream is read as your Cursor user, so serve requires
@@ -191,10 +268,15 @@ Options:
191
268
  --repo serve --cursor-events: repo to subscribe to (repeatable).
192
269
  github forward: owner/repo to forward from (inferred from
193
270
  the git remote when omitted; last wins).
194
- deploy: https git repo URL the control plane builds from.
195
- --ref deploy: git ref to build (defaults to the repo's default
196
- branch server-side).
197
- --path deploy: agent directory relative to the repo root.
271
+ deploy: https git repo URL (optional; inferred from
272
+ git remote origin when omitted).
273
+ --ref deploy: git ref to build (optional; inferred from the
274
+ current branch when omitted).
275
+ --path deploy: agent dir relative to repo root (optional;
276
+ inferred from --dir vs git toplevel when omitted).
277
+ --all deploy: every child agent under a multi-agent --dir
278
+ (skips the interactive picker; mutually exclusive with
279
+ --slug).
198
280
  --cursor-events-repo deploy: repo (owner/name) whose SCM events the
199
281
  deployed engine pulls (repeatable).
200
282
  --allow-domain deploy: egress domain the engine pod may reach
@@ -215,7 +297,7 @@ Options:
215
297
  `;
216
298
 
217
299
  async function main(argv: string[]): Promise<number> {
218
- const [command, ...rest] = argv;
300
+ const [command, ...restIn] = argv;
219
301
  if (
220
302
  command === undefined ||
221
303
  command === "help" ||
@@ -226,6 +308,13 @@ async function main(argv: string[]): Promise<number> {
226
308
  return command === undefined ? 1 : 0;
227
309
  }
228
310
 
311
+ // `session --events` means "dump raw events" (boolean). Rewrite so it does
312
+ // not collide with run/trajectory's `--events <file.ndjson>` string flag.
313
+ const rest =
314
+ command === "session"
315
+ ? restIn.map((arg) => (arg === "--events" ? "--raw-events" : arg))
316
+ : restIn;
317
+
229
318
  const { values, positionals } = parseArgs({
230
319
  args: rest,
231
320
  allowPositionals: true,
@@ -240,10 +329,12 @@ async function main(argv: string[]): Promise<number> {
240
329
  "state-root": { type: "string" },
241
330
  "bearer-token": { type: "string" },
242
331
  "allow-anonymous": { type: "boolean", default: false },
332
+ "allow-anonymous-cursor-github": { type: "boolean", default: false },
243
333
  "no-schedules": { type: "boolean", default: false },
244
334
  "no-playground": { type: "boolean", default: false },
245
335
  "no-docs": { type: "boolean", default: false },
246
336
  url: { type: "string" },
337
+ prod: { type: "boolean", default: false },
247
338
  message: { type: "string", multiple: true },
248
339
  input: { type: "string" },
249
340
  "messages-file": { type: "string" },
@@ -260,6 +351,8 @@ async function main(argv: string[]): Promise<number> {
260
351
  text: { type: "boolean", default: false },
261
352
  "no-color": { type: "boolean", default: false },
262
353
  "no-stream": { type: "boolean", default: false },
354
+ once: { type: "boolean", default: false },
355
+ "raw-events": { type: "boolean", default: false },
263
356
  json: { type: "boolean", default: false },
264
357
  name: { type: "string" },
265
358
  env: { type: "string" },
@@ -287,6 +380,7 @@ async function main(argv: string[]): Promise<number> {
287
380
  "no-wait": { type: "boolean", default: false },
288
381
  ref: { type: "string" },
289
382
  path: { type: "string" },
383
+ all: { type: "boolean", default: false },
290
384
  "cursor-events-repo": { type: "string", multiple: true },
291
385
  "allow-domain": { type: "string", multiple: true },
292
386
  },
@@ -352,6 +446,7 @@ async function main(argv: string[]): Promise<number> {
352
446
  return cmdInit({
353
447
  dir,
354
448
  initTarget: positionals[0],
449
+ json: values.json,
355
450
  });
356
451
  }
357
452
 
@@ -381,6 +476,7 @@ async function main(argv: string[]): Promise<number> {
381
476
  ...deployOptionsFromValues(values),
382
477
  dir,
383
478
  ...(values.slug === undefined ? {} : { slug: values.slug }),
479
+ all: values.all === true,
384
480
  ...(repo === undefined ? {} : { repo }),
385
481
  ...(values.ref === undefined ? {} : { ref: values.ref }),
386
482
  ...(values.path === undefined ? {} : { path: values.path }),
@@ -454,6 +550,9 @@ async function main(argv: string[]): Promise<number> {
454
550
  : values.events,
455
551
  text: values.text,
456
552
  url: values.url,
553
+ prod: values.prod,
554
+ team: values.team,
555
+ apiKey: values["api-key"],
457
556
  slug: values.slug,
458
557
  bearerToken: values["bearer-token"],
459
558
  stateRoot: values["state-root"],
@@ -471,6 +570,9 @@ async function main(argv: string[]): Promise<number> {
471
570
  toolInput: values.input,
472
571
  sessionId: values["session-id"] ?? values.session,
473
572
  url: values.url,
573
+ prod: values.prod,
574
+ team: values.team,
575
+ apiKey: values["api-key"],
474
576
  slug: values.slug,
475
577
  bearerToken: values["bearer-token"],
476
578
  stateRoot: values["state-root"],
@@ -502,6 +604,9 @@ async function main(argv: string[]): Promise<number> {
502
604
  json: values.json,
503
605
  tags: values.tag,
504
606
  url: values.url,
607
+ prod: values.prod,
608
+ team: values.team,
609
+ apiKey: values["api-key"],
505
610
  slug: values.slug,
506
611
  bearerToken: values["bearer-token"],
507
612
  stateRoot: values["state-root"],
@@ -512,25 +617,110 @@ async function main(argv: string[]): Promise<number> {
512
617
  }
513
618
 
514
619
  case "chat": {
515
- if (values.json) {
516
- return chatJson({
517
- baseUrl: values.url ?? "http://127.0.0.1:3000",
518
- message: (values.message as string[] | undefined)?.[0],
620
+ return cmdChat(
621
+ axFromOptions({
622
+ dir,
623
+ messages: values.message,
624
+ url: values.url,
625
+ prod: values.prod,
626
+ team: values.team,
627
+ apiKey: values["api-key"],
628
+ slug: values.slug,
629
+ sessionId: values["session-id"] ?? values.session,
630
+ continuationToken: values["continuation-token"],
519
631
  bearerToken: values["bearer-token"],
632
+ json: values.json,
520
633
  text: values.text,
521
- });
522
- }
523
- await runChat({
524
- baseUrl: values.url ?? "http://127.0.0.1:3000",
525
- ...((values.message as string[] | undefined)?.[0] === undefined
526
- ? {}
527
- : { message: (values.message as string[])[0]! }),
528
- ...(values["bearer-token"] === undefined
529
- ? {}
530
- : { bearerToken: values["bearer-token"] }),
531
- ...(values["no-color"] ? { noColor: true } : {}),
532
- });
533
- return 0;
634
+ noColor: values["no-color"],
635
+ })
636
+ );
637
+ }
638
+
639
+ case "resume": {
640
+ return cmdResume(
641
+ positionals[0],
642
+ axFromOptions({
643
+ dir,
644
+ messages: values.message,
645
+ url: values.url,
646
+ prod: values.prod,
647
+ team: values.team,
648
+ apiKey: values["api-key"],
649
+ slug: values.slug,
650
+ // Positional wins; --session is accepted as a redundant alias.
651
+ sessionId: positionals[0] ?? values["session-id"] ?? values.session,
652
+ continuationToken: values["continuation-token"],
653
+ bearerToken: values["bearer-token"],
654
+ json: values.json,
655
+ text: values.text,
656
+ noColor: values["no-color"],
657
+ })
658
+ );
659
+ }
660
+
661
+ case "logs": {
662
+ return cmdLogs(
663
+ axFromOptions({
664
+ dir,
665
+ url: values.url,
666
+ prod: values.prod,
667
+ team: values.team,
668
+ apiKey: values["api-key"],
669
+ slug: values.slug,
670
+ bearerToken: values["bearer-token"],
671
+ json: values.json,
672
+ once: values.once,
673
+ })
674
+ );
675
+ }
676
+
677
+ case "sessions": {
678
+ return cmdSessions(
679
+ axFromOptions({
680
+ dir,
681
+ url: values.url,
682
+ prod: values.prod,
683
+ team: values.team,
684
+ apiKey: values["api-key"],
685
+ slug: values.slug,
686
+ bearerToken: values["bearer-token"],
687
+ json: values.json,
688
+ })
689
+ );
690
+ }
691
+
692
+ case "session": {
693
+ return cmdSession(
694
+ positionals[0],
695
+ axFromOptions({
696
+ dir,
697
+ url: values.url,
698
+ prod: values.prod,
699
+ team: values.team,
700
+ apiKey: values["api-key"],
701
+ slug: values.slug,
702
+ bearerToken: values["bearer-token"],
703
+ json: values.json,
704
+ text: values.text,
705
+ rawEvents: values["raw-events"],
706
+ })
707
+ );
708
+ }
709
+
710
+ case "playground": {
711
+ return cmdPlayground(
712
+ axFromOptions({
713
+ dir,
714
+ url: values.url,
715
+ prod: values.prod,
716
+ team: values.team,
717
+ apiKey: values["api-key"],
718
+ slug: values.slug,
719
+ bearerToken: values["bearer-token"],
720
+ sessionId: values["session-id"] ?? values.session,
721
+ print: values.print,
722
+ })
723
+ );
534
724
  }
535
725
 
536
726
  case "validate": {
@@ -645,7 +835,19 @@ async function main(argv: string[]): Promise<number> {
645
835
  return info.diagnostics.some((d) => d.severity === "error") ? 1 : 0;
646
836
  }
647
837
 
838
+ case "dev":
648
839
  case "serve": {
840
+ const serveDirResult = resolveServeDir({
841
+ command,
842
+ dir,
843
+ positionals,
844
+ dirFlagPassed: rest.includes("--dir"),
845
+ });
846
+ if (typeof serveDirResult !== "string") {
847
+ process.stderr.write(serveDirResult.error);
848
+ return 1;
849
+ }
850
+ const serveDir = serveDirResult;
649
851
  const port =
650
852
  values.port === undefined ? 3000 : Number.parseInt(values.port, 10);
651
853
  if (Number.isNaN(port) || port < 0 || port > 65535) {
@@ -668,44 +870,45 @@ async function main(argv: string[]): Promise<number> {
668
870
  );
669
871
  return 1;
670
872
  }
873
+ const dev = command === "dev" || values.dev === true;
874
+ const style = stdoutPalette();
671
875
  const serveOptions = {
672
876
  port,
673
- ...(values.host === undefined ? {} : { host: values.host }),
674
- ...(mode === undefined ? {} : { mode: mode as "single" | "multi" }),
675
- dev: values.dev,
676
- ...(values["api-key"] === undefined
677
- ? {}
678
- : { apiKey: values["api-key"] }),
679
- ...(values["state-root"] === undefined
680
- ? {}
681
- : { stateRoot: values["state-root"] }),
682
- ...(values["bearer-token"] === undefined
683
- ? {}
684
- : { authToken: values["bearer-token"] }),
685
- ...(values["allow-anonymous"] ? { allowAnonymous: true } : {}),
686
- ...(values["no-schedules"] ? { schedules: false } : {}),
687
- ...(values["no-playground"] ? { playground: false } : {}),
688
- ...(values["no-docs"] ? { docs: false } : {}),
689
- ...(values["public-url"] === undefined
690
- ? {}
691
- : { publicUrl: values["public-url"] }),
692
- ...(values["cursor-events"]
693
- ? { cursorEvents: { repos: values.repo ?? [] } }
694
- : {}),
877
+ host: values.host,
878
+ mode: mode as "single" | "multi" | undefined,
879
+ dev,
880
+ apiKey: values["api-key"],
881
+ stateRoot: values["state-root"],
882
+ authToken: values["bearer-token"],
883
+ allowAnonymous: values["allow-anonymous"] === true ? true : undefined,
884
+ allowAnonymousCursorGithub:
885
+ values["allow-anonymous-cursor-github"] === true ? true : undefined,
886
+ schedules: values["no-schedules"] === true ? false : undefined,
887
+ playground: values["no-playground"] === true ? false : undefined,
888
+ docs: values["no-docs"] === true ? false : undefined,
889
+ publicUrl: values["public-url"],
890
+ cursorEvents:
891
+ values["cursor-events"] === true
892
+ ? { repos: values.repo ?? [] }
893
+ : undefined,
894
+ // Soft boot logs (GitHub auth, etc.) stay secondary to the banner.
895
+ logger: (line: string) => {
896
+ process.stdout.write(`${style.dim(line)}\n`);
897
+ },
695
898
  };
696
899
  const enterToRestart = process.stdin.isTTY === true;
697
900
  const bannerOpts = {
698
901
  noPlayground: values["no-playground"] === true,
699
- dev: values.dev === true,
902
+ dev,
700
903
  bearerToken: values["bearer-token"] !== undefined,
701
904
  noSchedules: values["no-schedules"] === true,
702
905
  };
703
906
 
704
907
  const start = async (): Promise<AgentServeHandle> => {
705
- const next = await serve(dir, serveOptions);
908
+ const next = await serve(serveDir, serveOptions);
706
909
  printServeBanner(next, bannerOpts);
707
910
  if (enterToRestart) {
708
- process.stdout.write(" press Enter to restart\n");
911
+ process.stdout.write(style.dim(" press Enter to restart\n"));
709
912
  }
710
913
  return next;
711
914
  };
@@ -738,10 +941,16 @@ async function main(argv: string[]): Promise<number> {
738
941
  return;
739
942
  }
740
943
  restarting = true;
741
- process.stdout.write("\nrestarting…\n");
742
- void handle
743
- .close()
744
- .then(start)
944
+ // Close + re-serve reloads authored agent modules and respawns Vite.
945
+ // Hold the "restarting…" frame briefly so a fast close doesn't look
946
+ // like Enter did nothing.
947
+ clearTerminal();
948
+ process.stdout.write(style.dim("restarting…\n"));
949
+ void Promise.all([handle.close(), sleep(450)])
950
+ .then(() => {
951
+ clearTerminal();
952
+ return start();
953
+ })
745
954
  .then((next) => {
746
955
  handle = next;
747
956
  })
@@ -790,40 +999,34 @@ function axFromOptions(
790
999
  ): AxCliOptions {
791
1000
  return {
792
1001
  dir: partial.dir,
793
- ...(partial.cwd === undefined ? {} : { cwd: partial.cwd }),
794
- ...(partial.slug === undefined ? {} : { slug: partial.slug }),
795
- ...(partial.bearerToken === undefined
796
- ? {}
797
- : { bearerToken: partial.bearerToken }),
798
- ...(partial.stateRoot === undefined
799
- ? {}
800
- : { stateRoot: partial.stateRoot }),
801
- ...(partial.messages === undefined ? {} : { messages: partial.messages }),
802
- ...(partial.sessionId === undefined
803
- ? {}
804
- : { sessionId: partial.sessionId }),
805
- ...(partial.continuationToken === undefined
806
- ? {}
807
- : { continuationToken: partial.continuationToken }),
808
- ...(partial.timeoutMs === undefined
809
- ? {}
810
- : { timeoutMs: partial.timeoutMs }),
811
- ...(partial.events === undefined ? {} : { events: partial.events }),
812
- ...(partial.text === undefined ? {} : { text: partial.text }),
813
- ...(partial.json === undefined ? {} : { json: partial.json }),
814
- ...(partial.list === undefined ? {} : { list: partial.list }),
815
- ...(partial.verbose === undefined ? {} : { verbose: partial.verbose }),
816
- ...(partial.tags === undefined ? {} : { tags: partial.tags }),
817
- ...(partial.evalIds === undefined ? {} : { evalIds: partial.evalIds }),
818
- ...(partial.initTarget === undefined
819
- ? {}
820
- : { initTarget: partial.initTarget }),
821
- ...(partial.toolName === undefined ? {} : { toolName: partial.toolName }),
822
- ...(partial.toolInput === undefined
823
- ? {}
824
- : { toolInput: partial.toolInput }),
825
- ...(partial.url === undefined ? {} : { url: partial.url }),
826
- ...(partial.stream === undefined ? {} : { stream: partial.stream }),
1002
+ cwd: partial.cwd,
1003
+ slug: partial.slug,
1004
+ bearerToken: partial.bearerToken,
1005
+ stateRoot: partial.stateRoot,
1006
+ messages: partial.messages,
1007
+ sessionId: partial.sessionId,
1008
+ continuationToken: partial.continuationToken,
1009
+ timeoutMs: partial.timeoutMs,
1010
+ events: partial.events,
1011
+ text: partial.text,
1012
+ json: partial.json,
1013
+ list: partial.list,
1014
+ verbose: partial.verbose,
1015
+ tags: partial.tags,
1016
+ evalIds: partial.evalIds,
1017
+ initTarget: partial.initTarget,
1018
+ toolName: partial.toolName,
1019
+ toolInput: partial.toolInput,
1020
+ url: partial.url,
1021
+ prod: partial.prod,
1022
+ team: partial.team,
1023
+ apiKey: partial.apiKey,
1024
+ fetchImpl: partial.fetchImpl,
1025
+ stream: partial.stream,
1026
+ noColor: partial.noColor,
1027
+ once: partial.once,
1028
+ rawEvents: partial.rawEvents,
1029
+ print: partial.print,
827
1030
  };
828
1031
  }
829
1032
 
@@ -862,42 +1065,6 @@ function parseTimeoutMs(value: string | undefined): number | undefined {
862
1065
  return n;
863
1066
  }
864
1067
 
865
- async function chatJson(args: {
866
- baseUrl: string;
867
- message?: string;
868
- bearerToken?: string;
869
- text?: boolean;
870
- }): Promise<number> {
871
- if (args.message === undefined) {
872
- process.stderr.write("chat --json requires --message\n");
873
- return 2;
874
- }
875
- const result = await runSession({
876
- baseUrl: args.baseUrl,
877
- messages: [args.message],
878
- ...(args.bearerToken === undefined
879
- ? {}
880
- : { bearerToken: args.bearerToken }),
881
- });
882
- if (args.text) {
883
- process.stdout.write(formatTrajectoryText(result.trajectory));
884
- } else {
885
- console.log(
886
- JSON.stringify(
887
- {
888
- ok: result.ok,
889
- sessionId: result.sessionId,
890
- continuationToken: result.continuationToken ?? null,
891
- trajectory: result.trajectory,
892
- },
893
- null,
894
- 2
895
- )
896
- );
897
- }
898
- return result.ok ? 0 : 1;
899
- }
900
-
901
1068
  /**
902
1069
  * Load mounts the same way `serve` does (multi-agent layout by default).
903
1070
  */
@@ -918,6 +1085,42 @@ function isEnterOnly(text: string): boolean {
918
1085
  return text.length > 0 && [...text].every((ch) => ch === "\n" || ch === "\r");
919
1086
  }
920
1087
 
1088
+ function sleep(ms: number): Promise<void> {
1089
+ return new Promise((resolve) => {
1090
+ setTimeout(resolve, ms);
1091
+ });
1092
+ }
1093
+
1094
+ /**
1095
+ * `dev [path]` takes a positional project root; `serve` keeps `--dir` only.
1096
+ * Reject mixing a positional with an explicit `--dir`, and extra args on `dev`.
1097
+ */
1098
+ function resolveServeDir(args: {
1099
+ command: string;
1100
+ dir: string;
1101
+ positionals: string[];
1102
+ dirFlagPassed: boolean;
1103
+ }): string | { error: string } {
1104
+ if (args.command !== "dev") {
1105
+ return args.dir;
1106
+ }
1107
+ if (args.positionals.length > 1) {
1108
+ return {
1109
+ error: `${CLI} dev: unexpected arguments: ${args.positionals.slice(1).join(" ")}\n`,
1110
+ };
1111
+ }
1112
+ const positionalDir = args.positionals[0];
1113
+ if (positionalDir === undefined) {
1114
+ return args.dir;
1115
+ }
1116
+ if (args.dirFlagPassed && args.dir !== positionalDir) {
1117
+ return {
1118
+ error: `${CLI} dev: pass a path as a positional or --dir, not both\n`,
1119
+ };
1120
+ }
1121
+ return positionalDir;
1122
+ }
1123
+
921
1124
  function printServeBanner(
922
1125
  handle: AgentServeHandle,
923
1126
  opts: {
@@ -927,57 +1130,66 @@ function printServeBanner(
927
1130
  noSchedules: boolean;
928
1131
  }
929
1132
  ): void {
930
- const multi = handle.agents.length > 1 || handle.agents[0]?.slug !== "";
931
- if (multi) {
932
- process.stdout.write(
933
- `${handle.agents.length} agents listening on ${handle.url}\n`
934
- );
935
- if (!opts.noPlayground) {
936
- process.stdout.write(` index: ${handle.url}/\n`);
937
- }
938
- for (const agent of handle.agents) {
939
- const info = projectInfo(agent.project);
940
- const suffix = opts.noPlayground
941
- ? `${agent.url}/v1/session`
942
- : `${agent.url}/playground`;
943
- process.stdout.write(` /${agent.slug} ${info.name} — ${suffix}\n`);
944
- }
1133
+ const c = stdoutPalette();
1134
+ const agents = handle.agents;
1135
+ if (agents.length > 1) {
1136
+ process.stdout.write(`${agents.length} agents listening\n`);
945
1137
  } else {
946
- const info = projectInfo(handle.project);
947
- process.stdout.write(`${info.name} listening on ${handle.url}\n`);
948
- if (!opts.noPlayground) {
949
- process.stdout.write(` playground: ${handle.url}/playground\n`);
950
- }
951
- process.stdout.write(` POST ${handle.url}/v1/session\n`);
952
- process.stdout.write(` GET ${handle.url}/v1/session/:sessionId/stream\n`);
953
- for (const channel of info.channels) {
954
- if (channel.id === "http") {
955
- continue;
1138
+ const info = projectInfo(agents[0]?.project ?? handle.project);
1139
+ process.stdout.write(`${info.name} listening\n`);
1140
+ }
1141
+
1142
+ const playgroundUrl = resolvePlaygroundBannerUrl(handle, opts.noPlayground);
1143
+ if (playgroundUrl !== undefined) {
1144
+ process.stdout.write(` playground ${c.cyan(playgroundUrl)}\n`);
1145
+ }
1146
+
1147
+ // Dev is playground-first (Vite proxies the API). Keep API/route detail for
1148
+ // plain `serve`, where operators often curl the HTTP surface directly.
1149
+ if (!opts.dev) {
1150
+ process.stdout.write(c.dim(` api ${handle.url}\n`));
1151
+ if (agents.length > 1 || agents[0]?.slug !== "") {
1152
+ for (const agent of agents) {
1153
+ const info = projectInfo(agent.project);
1154
+ process.stdout.write(c.dim(` /${agent.slug} ${info.name}\n`));
956
1155
  }
957
- for (const route of channel.routes) {
958
- process.stdout.write(` ${route.method} ${handle.url}${route.url}\n`);
1156
+ } else {
1157
+ const info = projectInfo(handle.project);
1158
+ process.stdout.write(c.dim(` POST ${handle.url}/v1/session\n`));
1159
+ process.stdout.write(
1160
+ c.dim(` GET ${handle.url}/v1/session/:sessionId/stream\n`)
1161
+ );
1162
+ for (const channel of info.channels) {
1163
+ if (channel.id === "http") {
1164
+ continue;
1165
+ }
1166
+ for (const route of channel.routes) {
1167
+ process.stdout.write(
1168
+ c.dim(` ${route.method} ${handle.url}${route.url}\n`)
1169
+ );
1170
+ }
959
1171
  }
960
- }
961
- if (info.schedules.length > 0) {
962
- const scheduleMode = opts.dev
963
- ? "dev mode: dispatch with POST /v1/dev/schedules/:scheduleId"
964
- : opts.noSchedules
1172
+ if (info.schedules.length > 0) {
1173
+ const scheduleMode = opts.noSchedules
965
1174
  ? "disabled"
966
1175
  : "running on cron cadence (UTC)";
967
- process.stdout.write(
968
- ` schedules: ${info.schedules.map((s) => s.name).join(", ")} — ${scheduleMode}\n`
969
- );
1176
+ process.stdout.write(
1177
+ c.dim(
1178
+ ` schedules: ${info.schedules.map((s) => s.name).join(", ")} — ${scheduleMode}\n`
1179
+ )
1180
+ );
1181
+ }
1182
+ }
1183
+ if (handle.docsUrl !== undefined) {
1184
+ process.stdout.write(c.dim(` docs ${handle.docsUrl}\n`));
970
1185
  }
971
1186
  }
972
- if (handle.playgroundDevUrl !== undefined) {
973
- process.stdout.write(` playground (HMR): ${handle.playgroundDevUrl}\n`);
974
- }
975
- if (handle.docsUrl !== undefined) {
976
- process.stdout.write(` docs: ${handle.docsUrl}\n`);
977
- }
1187
+
978
1188
  if (opts.bearerToken) {
979
1189
  process.stdout.write(
980
- " auth: bearer token required on API routes (playground has a token field)\n"
1190
+ c.dim(
1191
+ " auth: bearer token required on API routes (playground has a token field)\n"
1192
+ )
981
1193
  );
982
1194
  }
983
1195
  if (
@@ -989,11 +1201,33 @@ function printServeBanner(
989
1201
  )
990
1202
  ) {
991
1203
  process.stdout.write(
992
- ` github: dev accepts unsigned loopback deliveries (${CLI} github forward works without a secret; set GITHUB_WEBHOOK_SECRET to verify signatures)\n`
1204
+ c.dim(
1205
+ ` github: dev accepts unsigned loopback deliveries (${CLI} github forward works without a secret; set GITHUB_WEBHOOK_SECRET to verify signatures)\n`
1206
+ )
993
1207
  );
994
1208
  }
995
1209
  }
996
1210
 
1211
+ /** Prefer the Vite playground URL in --dev; otherwise the static SPA path. */
1212
+ function resolvePlaygroundBannerUrl(
1213
+ handle: AgentServeHandle,
1214
+ noPlayground: boolean
1215
+ ): string | undefined {
1216
+ if (noPlayground) {
1217
+ return undefined;
1218
+ }
1219
+ if (handle.playgroundDevUrl !== undefined) {
1220
+ return handle.playgroundDevUrl.replace(/\/$/, "");
1221
+ }
1222
+ if (handle.agents.length === 1) {
1223
+ return `${handle.agents[0]!.url}/playground`;
1224
+ }
1225
+ if (handle.agents.length > 1) {
1226
+ return handle.url;
1227
+ }
1228
+ return undefined;
1229
+ }
1230
+
997
1231
  function formatModel(model: unknown): string {
998
1232
  if (model === undefined) {
999
1233
  return "(unset)";