@agentuity/cli 0.1.14 → 0.1.16

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 (158) hide show
  1. package/dist/auth.d.ts.map +1 -1
  2. package/dist/auth.js +7 -6
  3. package/dist/auth.js.map +1 -1
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +69 -11
  6. package/dist/cli.js.map +1 -1
  7. package/dist/cmd/ai/index.d.ts.map +1 -1
  8. package/dist/cmd/ai/index.js +6 -1
  9. package/dist/cmd/ai/index.js.map +1 -1
  10. package/dist/cmd/ai/opencode/index.d.ts +3 -0
  11. package/dist/cmd/ai/opencode/index.d.ts.map +1 -0
  12. package/dist/cmd/ai/opencode/index.js +27 -0
  13. package/dist/cmd/ai/opencode/index.js.map +1 -0
  14. package/dist/cmd/ai/opencode/install.d.ts +3 -0
  15. package/dist/cmd/ai/opencode/install.d.ts.map +1 -0
  16. package/dist/cmd/ai/opencode/install.js +102 -0
  17. package/dist/cmd/ai/opencode/install.js.map +1 -0
  18. package/dist/cmd/ai/opencode/run.d.ts +3 -0
  19. package/dist/cmd/ai/opencode/run.d.ts.map +1 -0
  20. package/dist/cmd/ai/opencode/run.js +88 -0
  21. package/dist/cmd/ai/opencode/run.js.map +1 -0
  22. package/dist/cmd/ai/opencode/uninstall.d.ts +3 -0
  23. package/dist/cmd/ai/opencode/uninstall.d.ts.map +1 -0
  24. package/dist/cmd/ai/opencode/uninstall.js +82 -0
  25. package/dist/cmd/ai/opencode/uninstall.js.map +1 -0
  26. package/dist/cmd/build/vite/beacon-plugin.d.ts.map +1 -1
  27. package/dist/cmd/build/vite/beacon-plugin.js.map +1 -1
  28. package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
  29. package/dist/cmd/build/vite/vite-builder.js +1 -1
  30. package/dist/cmd/build/vite/vite-builder.js.map +1 -1
  31. package/dist/cmd/cloud/env/delete.d.ts.map +1 -1
  32. package/dist/cmd/cloud/env/delete.js +87 -34
  33. package/dist/cmd/cloud/env/delete.js.map +1 -1
  34. package/dist/cmd/cloud/env/get.d.ts.map +1 -1
  35. package/dist/cmd/cloud/env/get.js +50 -16
  36. package/dist/cmd/cloud/env/get.js.map +1 -1
  37. package/dist/cmd/cloud/env/import.d.ts.map +1 -1
  38. package/dist/cmd/cloud/env/import.js +76 -32
  39. package/dist/cmd/cloud/env/import.js.map +1 -1
  40. package/dist/cmd/cloud/env/index.d.ts.map +1 -1
  41. package/dist/cmd/cloud/env/index.js +6 -2
  42. package/dist/cmd/cloud/env/index.js.map +1 -1
  43. package/dist/cmd/cloud/env/list.d.ts.map +1 -1
  44. package/dist/cmd/cloud/env/list.js +94 -23
  45. package/dist/cmd/cloud/env/list.js.map +1 -1
  46. package/dist/cmd/cloud/env/org-util.d.ts +16 -0
  47. package/dist/cmd/cloud/env/org-util.d.ts.map +1 -0
  48. package/dist/cmd/cloud/env/org-util.js +28 -0
  49. package/dist/cmd/cloud/env/org-util.js.map +1 -0
  50. package/dist/cmd/cloud/env/pull.d.ts.map +1 -1
  51. package/dist/cmd/cloud/env/pull.js +61 -29
  52. package/dist/cmd/cloud/env/pull.js.map +1 -1
  53. package/dist/cmd/cloud/env/push.d.ts.map +1 -1
  54. package/dist/cmd/cloud/env/push.js +69 -23
  55. package/dist/cmd/cloud/env/push.js.map +1 -1
  56. package/dist/cmd/cloud/env/set.d.ts.map +1 -1
  57. package/dist/cmd/cloud/env/set.js +69 -26
  58. package/dist/cmd/cloud/env/set.js.map +1 -1
  59. package/dist/cmd/cloud/keyvalue/create-namespace.js +1 -1
  60. package/dist/cmd/cloud/keyvalue/create-namespace.js.map +1 -1
  61. package/dist/cmd/cloud/keyvalue/delete-namespace.js +2 -2
  62. package/dist/cmd/cloud/keyvalue/delete-namespace.js.map +1 -1
  63. package/dist/cmd/cloud/keyvalue/delete.js +1 -1
  64. package/dist/cmd/cloud/keyvalue/delete.js.map +1 -1
  65. package/dist/cmd/cloud/keyvalue/get.js +1 -1
  66. package/dist/cmd/cloud/keyvalue/get.js.map +1 -1
  67. package/dist/cmd/cloud/keyvalue/index.js +1 -1
  68. package/dist/cmd/cloud/keyvalue/index.js.map +1 -1
  69. package/dist/cmd/cloud/keyvalue/keys.js +1 -1
  70. package/dist/cmd/cloud/keyvalue/keys.js.map +1 -1
  71. package/dist/cmd/cloud/keyvalue/list-namespaces.js +1 -1
  72. package/dist/cmd/cloud/keyvalue/list-namespaces.js.map +1 -1
  73. package/dist/cmd/cloud/keyvalue/repl.d.ts.map +1 -1
  74. package/dist/cmd/cloud/keyvalue/repl.js +8 -5
  75. package/dist/cmd/cloud/keyvalue/repl.js.map +1 -1
  76. package/dist/cmd/cloud/keyvalue/search.js +1 -1
  77. package/dist/cmd/cloud/keyvalue/search.js.map +1 -1
  78. package/dist/cmd/cloud/keyvalue/set.js +1 -1
  79. package/dist/cmd/cloud/keyvalue/set.js.map +1 -1
  80. package/dist/cmd/cloud/keyvalue/stats.js +1 -1
  81. package/dist/cmd/cloud/keyvalue/stats.js.map +1 -1
  82. package/dist/cmd/cloud/keyvalue/util.d.ts +4 -4
  83. package/dist/cmd/cloud/keyvalue/util.d.ts.map +1 -1
  84. package/dist/cmd/cloud/keyvalue/util.js +4 -9
  85. package/dist/cmd/cloud/keyvalue/util.js.map +1 -1
  86. package/dist/cmd/project/create.d.ts.map +1 -1
  87. package/dist/cmd/project/create.js +20 -2
  88. package/dist/cmd/project/create.js.map +1 -1
  89. package/dist/cmd/project/download.d.ts +3 -1
  90. package/dist/cmd/project/download.d.ts.map +1 -1
  91. package/dist/cmd/project/download.js +5 -0
  92. package/dist/cmd/project/download.js.map +1 -1
  93. package/dist/cmd/project/template-flow.d.ts +5 -0
  94. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  95. package/dist/cmd/project/template-flow.js +188 -79
  96. package/dist/cmd/project/template-flow.js.map +1 -1
  97. package/dist/cmd/setup/index.d.ts.map +1 -1
  98. package/dist/cmd/setup/index.js +2 -1
  99. package/dist/cmd/setup/index.js.map +1 -1
  100. package/dist/onboarding/agentPrompt.d.ts +8 -0
  101. package/dist/onboarding/agentPrompt.d.ts.map +1 -0
  102. package/dist/onboarding/agentPrompt.js +263 -0
  103. package/dist/onboarding/agentPrompt.js.map +1 -0
  104. package/dist/schema-generator.d.ts +1 -1
  105. package/dist/schema-generator.d.ts.map +1 -1
  106. package/dist/schema-parser.d.ts +1 -1
  107. package/dist/schema-parser.d.ts.map +1 -1
  108. package/dist/schema-parser.js +36 -1
  109. package/dist/schema-parser.js.map +1 -1
  110. package/dist/tui/prompt.d.ts.map +1 -1
  111. package/dist/tui/prompt.js +7 -1
  112. package/dist/tui/prompt.js.map +1 -1
  113. package/dist/tui.d.ts.map +1 -1
  114. package/dist/tui.js +91 -28
  115. package/dist/tui.js.map +1 -1
  116. package/dist/types.d.ts.map +1 -1
  117. package/dist/types.js.map +1 -1
  118. package/package.json +7 -7
  119. package/src/auth.ts +7 -6
  120. package/src/cli.ts +84 -11
  121. package/src/cmd/ai/index.ts +6 -1
  122. package/src/cmd/ai/opencode/index.ts +28 -0
  123. package/src/cmd/ai/opencode/install.ts +120 -0
  124. package/src/cmd/ai/opencode/run.ts +103 -0
  125. package/src/cmd/ai/opencode/uninstall.ts +90 -0
  126. package/src/cmd/build/vite/beacon-plugin.ts +3 -1
  127. package/src/cmd/build/vite/vite-builder.ts +5 -1
  128. package/src/cmd/cloud/env/delete.ts +100 -41
  129. package/src/cmd/cloud/env/get.ts +53 -16
  130. package/src/cmd/cloud/env/import.ts +86 -37
  131. package/src/cmd/cloud/env/index.ts +6 -2
  132. package/src/cmd/cloud/env/list.ts +102 -27
  133. package/src/cmd/cloud/env/org-util.ts +37 -0
  134. package/src/cmd/cloud/env/pull.ts +67 -31
  135. package/src/cmd/cloud/env/push.ts +81 -28
  136. package/src/cmd/cloud/env/set.ts +82 -33
  137. package/src/cmd/cloud/keyvalue/create-namespace.ts +1 -1
  138. package/src/cmd/cloud/keyvalue/delete-namespace.ts +2 -2
  139. package/src/cmd/cloud/keyvalue/delete.ts +1 -1
  140. package/src/cmd/cloud/keyvalue/get.ts +1 -1
  141. package/src/cmd/cloud/keyvalue/index.ts +1 -1
  142. package/src/cmd/cloud/keyvalue/keys.ts +1 -1
  143. package/src/cmd/cloud/keyvalue/list-namespaces.ts +1 -1
  144. package/src/cmd/cloud/keyvalue/repl.ts +8 -5
  145. package/src/cmd/cloud/keyvalue/search.ts +1 -1
  146. package/src/cmd/cloud/keyvalue/set.ts +1 -1
  147. package/src/cmd/cloud/keyvalue/stats.ts +1 -1
  148. package/src/cmd/cloud/keyvalue/util.ts +8 -17
  149. package/src/cmd/project/create.ts +21 -2
  150. package/src/cmd/project/download.ts +7 -1
  151. package/src/cmd/project/template-flow.ts +215 -80
  152. package/src/cmd/setup/index.ts +2 -1
  153. package/src/onboarding/agentPrompt.ts +263 -0
  154. package/src/schema-generator.ts +1 -1
  155. package/src/schema-parser.ts +42 -3
  156. package/src/tui/prompt.ts +10 -3
  157. package/src/tui.ts +95 -31
  158. package/src/types.ts +1 -0
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Agent Onboarding Prompt
3
+ *
4
+ * This generates the markdown prompt that coding agents use to guide developers
5
+ * through Agentuity setup after the CLI has been installed.
6
+ */
7
+
8
+ export function getAgentPromptMarkdown(): string {
9
+ return `
10
+ **Your goal:** Guide the user from zero to a deployed Agentuity project, then help them understand what they have and what to do next.
11
+
12
+ ---
13
+
14
+ ## What is Agentuity?
15
+
16
+ Agentuity is the full-stack platform for AI agents. Briefly explain to the user:
17
+
18
+ - **Full-stack agents**: Build agents, APIs, and front-end integrations with end-to-end type safety
19
+ - **Bun/TypeScript SDKs**: First-class developer experience with the fastest JavaScript runtime
20
+ - **AI Gateway**: Unified access to LLMs (OpenAI-compatible SDK) with single billing—no separate API keys needed
21
+ - **Observability**: Logs, tracing, and evals built-in so you can debug and improve agents
22
+ - **Production services**: Sandboxes, storage (S3-compatible), Postgres, KV, and enterprise features available as you grow
23
+
24
+ We're going to be setting up the default template quickly.
25
+
26
+ The default template includes AI Gateway integration, thread state persistence, structured logging, Tailwind CSS, and a local Workbench at \`/workbench\` for testing agents.
27
+
28
+ ---
29
+
30
+ ## How to Guide the User
31
+
32
+ **Your job is to make onboarding as smooth as possible with minimal user interaction.**
33
+
34
+ 1. **Start by briefly explaining** what Agentuity is and what you'll help them accomplish
35
+ 2. **Run commands automatically** when you have terminal access—don't ask permission for each step
36
+ 3. **Only pause for human action** when absolutely required
37
+ 4. **At each step**: Briefly explain what it does and why, then execute
38
+ 5. **Celebrate success**: When deployment completes, make sure the user knows they earned free credits and show them what to explore next
39
+
40
+ Assume the user is technical but new to Agentuity. Be warm, efficient, and action-oriented. The goal is a successful deployment with as few interruptions as possible.
41
+
42
+ ---
43
+
44
+ ## Onboarding Steps
45
+
46
+ ### Step 1: You're Already Logged In!
47
+
48
+ **The install command automatically authenticated you.** No manual login needed.
49
+
50
+ Verify with:
51
+ \`\`\`bash
52
+ agentuity auth whoami
53
+ \`\`\`
54
+
55
+ This outputs your user info including your \`org_id\`. You'll need this for the create command.
56
+
57
+ ---
58
+
59
+ ### Step 2: Create a New Project
60
+
61
+ **Why:** This scaffolds a full-stack agent app with AI Gateway, persistence, logging, and a testing UI.
62
+
63
+ **First, get the user's organization ID:**
64
+ \`\`\`bash
65
+ agentuity auth whoami
66
+ \`\`\`
67
+
68
+ This outputs the user's info including their \`org_id\`. You'll need this for the create command.
69
+
70
+ **Then create the project (non-interactive):**
71
+ \`\`\`bash
72
+ agentuity create --name "my-first-agent" --org-id <ORG_ID> --region usc --confirm
73
+ \`\`\`
74
+
75
+ **Required flags for non-interactive mode:**
76
+ - \`--name\`: Project name (use \`my-first-agent\` as default)
77
+ - \`--org-id\`: The organization ID from \`agentuity auth whoami\` (required in non-interactive mode)
78
+ - \`--region\`: Cloud region (use \`usc\` for US Central, or \`use\` for US East)
79
+ - \`--confirm\`: Skips interactive prompts
80
+
81
+ **Example with real org ID:**
82
+ \`\`\`bash
83
+ agentuity create --name "my-first-agent" --org-id ..... --region usc --confirm
84
+ \`\`\`
85
+
86
+ If the user wants a different location, add \`--dir\`:
87
+ \`\`\`bash
88
+ agentuity create --name "my-first-agent" --org-id <ORG_ID> --region usc --dir ~/projects/my-first-agent --confirm
89
+ \`\`\`
90
+
91
+ After creation, \`cd\` into the project directory.
92
+
93
+ **Docs:** https://agentuity.dev/Get-Started/quickstart
94
+
95
+ ---
96
+
97
+ ### Step 3: Run Locally
98
+
99
+ **Why:** Start the development server to test the project locally before deploying.
100
+
101
+ **Command:**
102
+ \`\`\`bash
103
+ agentuity dev
104
+ \`\`\`
105
+
106
+ **⚠️ This is a long-running process!** The dev server runs continuously until killed. Before or after starting it, **immediately tell the user what to do** — don't just wait silently.
107
+
108
+ **What happens:**
109
+ - **Local server** starts at \`http://localhost:3500\` — this is where your APIs and agents are served
110
+ - **Workbench** available at \`http://localhost:3500/workbench\` — a built-in UI for testing and interacting with your agents directly, without needing a frontend
111
+ - **Public tunnel URL** is automatically created — the CLI will display a public URL that tunnels to your local server, useful for testing webhooks or sharing with others
112
+ - **Hot reload** enabled — code changes reflect immediately
113
+
114
+ **Immediately after starting the dev server, tell the user:**
115
+
116
+ > "The dev server is now running! Here's what to try:
117
+ >
118
+ > 1. Open **http://localhost:3500** to see the app
119
+ > 2. Open **http://localhost:3500/workbench** to interact with your agents (this is a key feature!)
120
+ > 3. The public tunnel URL shown in the terminal can be shared with others
121
+ >
122
+ > Take a few minutes to explore. When you're ready to deploy to production, let me know!"
123
+
124
+ **Wait for the user to confirm** they're ready to deploy before proceeding to Step 4.
125
+
126
+ **Keyboard shortcuts** while dev server runs: \`h\` for help, \`r\` to restart, \`q\` to quit.
127
+
128
+ **Docs:** https://agentuity.dev/Reference/CLI/development
129
+
130
+ ---
131
+
132
+ ### Step 4: Deploy to Production
133
+
134
+ **Why:** Make the project live with observability, logs, and analytics.
135
+
136
+ **Important:** Deploying their first project earns the user **free credits** to continue experimenting with the platform. Make sure they know this is a reward for completing onboarding!
137
+
138
+ **First, stop the dev server:**
139
+ Before deploying, you must stop the dev server that's still running from Step 3. Send \`Ctrl+C\` or \`q\` to the terminal running \`agentuity dev\` to gracefully shut it down. Don't leave it running or you'll have an orphaned process on port 3500.
140
+
141
+ **Then deploy:**
142
+ \`\`\`bash
143
+ agentuity deploy
144
+ \`\`\`
145
+
146
+ **What happens:**
147
+ - Project builds and deploys to Agentuity's global edge network
148
+ - The CLI will output the production URL when deployment succeeds
149
+ - Observability (logs, tracing, analytics) is now live
150
+ - **Free credits are awarded** for their first deployment
151
+
152
+ **⚠️ CRITICAL: Do not hallucinate URLs!**
153
+ - **Only show the user URLs that the CLI actually outputs**
154
+ - Do not make up or guess production URLs (e.g., don't invent \`https://project-name.agentuity.app\`)
155
+ - If the deploy command fails or doesn't show a URL, tell the user to check the dashboard instead
156
+ - If there's an error, report the actual error message from the CLI
157
+
158
+ **After successful deployment:**
159
+ 1. Show the user the **exact production URL from the CLI output**
160
+ 2. Direct them to the Agentuity dashboard at https://app.agentuity.com to:
161
+ - View their deployed project
162
+ - Explore logs and traces
163
+ - See their credit balance
164
+ - Monitor analytics
165
+
166
+ **If deployment fails or shows errors:**
167
+ - Report the actual error message
168
+ - Don't claim success if the CLI didn't confirm it
169
+ - Suggest checking the dashboard for deployment status
170
+
171
+ ---
172
+
173
+ ## What the Template Provides
174
+
175
+ After deployment, explain what they now have:
176
+
177
+ | Feature | What It Does |
178
+ |---------|--------------|
179
+ | **AI Gateway** | OpenAI-compatible SDK routed through Agentuity—unified billing, no separate API keys |
180
+ | **Thread State** | Conversations and agent state persist automatically for multi-turn flows |
181
+ | **Structured Logging** | Logs and traces captured for debugging and analytics |
182
+ | **Workbench** | Local UI at \`/workbench\` for testing agents without a frontend |
183
+ | **Tailwind CSS** | Pre-configured styling for the frontend |
184
+ | **Type Safety** | End-to-end types from agent schemas to API routes to frontend |
185
+
186
+ ---
187
+
188
+ ## Next Steps
189
+
190
+ After successful deployment, guide the user through these recommendations:
191
+
192
+ ### 1. Celebrate the Free Credits!
193
+ - Remind the user: their first deployment earned them **free credits** to keep experimenting
194
+ - They can view their credit balance in the Agentuity dashboard
195
+ - These credits let them continue building and testing without upfront cost
196
+
197
+ ### 2. Join the Community
198
+ - **Discord**: https://discord.gg/agentuity
199
+ - Get help, see examples, share what you're building
200
+
201
+ ### 3. Explore the Dashboard
202
+ - **Observability**: View logs, traces, and evals for your deployment
203
+ - **Deployments**: See version history with rollback options
204
+ - **Analytics**: Monitor usage and performance
205
+
206
+ ### 4. Connect GitHub
207
+ - Enable GitHub integration in project settings for:
208
+ - Automatic deployments from branches
209
+ - CI/CD workflows
210
+ - Team collaboration
211
+
212
+ ### 5. Explore Additional Services
213
+ As your project grows, Agentuity provides:
214
+ - **Postgres**: Relational database
215
+ - **KV**: Fast key-value storage
216
+ - **Storage**: S3-compatible file storage
217
+ - **Sandboxes**: Isolated execution environments
218
+
219
+ ### 6. Customize Your Agents
220
+ The SDK makes it easy to:
221
+ - Add new agents in \`src/agent/\` (just add a folder with \`agent.ts\`)
222
+ - Create API routes in \`src/api/\`
223
+ - Build type-safe frontend integrations
224
+
225
+ **Docs:** https://agentuity.dev/Get-Started/project-structure
226
+
227
+ ---
228
+
229
+ ## Handling Common Issues
230
+
231
+ - **Bun not installed**: The CLI will prompt to install it, or run: \`curl -fsSL https://bun.sh/install | bash\`
232
+ - **Login fails/times out**: Rerun \`agentuity login\` and check browser
233
+ - **Port 3500 in use**: Kill the existing process or use \`agentuity dev --port 8080\`
234
+ - **Deploy fails**: Check build errors in CLI output, ensure you're in the project directory
235
+
236
+ ---
237
+
238
+ ## Success Criteria
239
+
240
+ **Onboarding is successful when the user has:**
241
+
242
+ 1. ✅ **CLI installed** and authenticated
243
+ 2. ✅ **Local dev environment running** — they've seen \`localhost:3500\` and tried the Workbench
244
+ 3. ✅ **First deployment complete** — they have a live production URL
245
+ 4. ✅ **Free credits earned** — they know they've been rewarded and can keep building
246
+ 5. ✅ **Clear next steps** — they know about Discord, the dashboard, and how to extend their project
247
+
248
+ **Make them feel accomplished!** They just went from zero to a deployed AI agent platform in minutes.
249
+
250
+ ---
251
+
252
+ ## Style Guidelines
253
+
254
+ - Keep explanations short and practical
255
+ - Explain what & why at each step
256
+ - Give exact commands
257
+ - Handle errors calmly with concrete suggestions
258
+ - Be explicit when human action is required
259
+ - Use non-interactive CLI flags (\`--confirm\`, \`--name\`, \`--org-id\`, \`--region usc\`, \`--dir\`) for smooth automation
260
+ - **Never hallucinate or make up information** — only report URLs, IDs, and status from actual CLI output
261
+ - If something fails or you're unsure, say so honestly and direct the user to the dashboard
262
+ `;
263
+ }
@@ -13,7 +13,7 @@ export interface SchemaArgument {
13
13
 
14
14
  export interface SchemaOption {
15
15
  name: string;
16
- type: 'string' | 'number' | 'boolean' | 'array';
16
+ type: 'string' | 'number' | 'boolean' | 'array' | 'optionalString';
17
17
  required: boolean;
18
18
  default?: unknown;
19
19
  description?: string;
@@ -13,7 +13,7 @@ export interface ParsedArgs {
13
13
  export interface ParsedOption {
14
14
  name: string;
15
15
  description?: string;
16
- type: 'string' | 'number' | 'boolean' | 'array';
16
+ type: 'string' | 'number' | 'boolean' | 'array' | 'optionalString';
17
17
  hasDefault?: boolean;
18
18
  defaultValue?: unknown;
19
19
  enumValues?: string[];
@@ -57,6 +57,42 @@ function unwrapSchema(schema: unknown): unknown {
57
57
  return current;
58
58
  }
59
59
 
60
+ /**
61
+ * Check if a schema is a union of boolean and string (for optional string flags like --org [value])
62
+ * This pattern is used when a flag can be used as a boolean (--org) or with a value (--org=myOrgId)
63
+ */
64
+ function isBooleanStringUnion(schema: unknown): boolean {
65
+ const unwrapped = unwrapSchema(schema) as ZodTypeInternal;
66
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
67
+ const def = unwrapped?._def as any;
68
+ // Zod 3 uses typeName, Zod 4 uses type
69
+ const typeId = def?.typeName || def?.type;
70
+
71
+ if (typeId !== 'ZodUnion' && typeId !== 'union') {
72
+ return false;
73
+ }
74
+
75
+ // Zod 3 uses _def.options, Zod 4 uses .options directly on the schema or _def.options
76
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
77
+ const options = def?.options || (unwrapped as any)?.options;
78
+ if (!Array.isArray(options) || options.length !== 2) {
79
+ return false;
80
+ }
81
+
82
+ const types = new Set<string>();
83
+ for (const opt of options) {
84
+ // Zod 4: type is directly on the object as .type
85
+ // Zod 3: type is _def.typeName
86
+ const optUnknown = opt as unknown as Record<string, unknown>;
87
+ const optDef = optUnknown?._def as Record<string, unknown> | undefined;
88
+ const optType = (optUnknown?.type as string) || (optDef?.typeName as string) || (optDef?.type as string);
89
+ types.add(optType);
90
+ }
91
+
92
+ return (types.has('boolean') || types.has('ZodBoolean')) &&
93
+ (types.has('string') || types.has('ZodString'));
94
+ }
95
+
60
96
  function getShape(schema: ZodType): Record<string, unknown> {
61
97
  const unwrapped = unwrapSchema(schema) as ZodTypeInternal;
62
98
  const typeId = unwrapped?._def?.typeName || unwrapped?._def?.type;
@@ -182,9 +218,12 @@ export function parseOptionsSchema(schema: ZodType): ParsedOption[] {
182
218
  }
183
219
  }
184
220
 
185
- let type: 'string' | 'number' | 'boolean' | 'array' = 'string';
221
+ let type: 'string' | 'number' | 'boolean' | 'array' | 'optionalString' = 'string';
186
222
  let enumValues: string[] | undefined;
187
- if (typeId === 'ZodNumber' || typeId === 'number') {
223
+ if (isBooleanStringUnion(value)) {
224
+ // z.union([z.boolean(), z.string()]) - flag can be used as --flag or --flag=value
225
+ type = 'optionalString';
226
+ } else if (typeId === 'ZodNumber' || typeId === 'number') {
188
227
  type = 'number';
189
228
  } else if (typeId === 'ZodBoolean' || typeId === 'boolean') {
190
229
  type = 'boolean';
package/src/tui/prompt.ts CHANGED
@@ -166,9 +166,16 @@ export class PromptFlow {
166
166
  readline.moveCursor(process.stdout, 0, -linesToClear);
167
167
  readline.clearScreenDown(process.stdout);
168
168
 
169
- process.stdout.write(
170
- `${colors.completed(symbols.completed)} ${message}\n${colors.secondary(symbols.bar)} ${colors.muted(value)}\n${colors.secondary(symbols.bar)}\n`
171
- );
169
+ // If value is empty, only show message and separator (no value line)
170
+ if (value === '') {
171
+ process.stdout.write(
172
+ `${colors.completed(symbols.completed)} ${message}\n${colors.secondary(symbols.bar)}\n`
173
+ );
174
+ } else {
175
+ process.stdout.write(
176
+ `${colors.completed(symbols.completed)} ${message}\n${colors.secondary(symbols.bar)} ${colors.muted(value)}\n${colors.secondary(symbols.bar)}\n`
177
+ );
178
+ }
172
179
 
173
180
  this.states.push({
174
181
  type: 'completed',
package/src/tui.ts CHANGED
@@ -247,7 +247,8 @@ export function getSeverityColor(severity: string): (text: string) => string {
247
247
  export function success(message: string): void {
248
248
  const color = getColor('success');
249
249
  const reset = getColor('reset');
250
- process.stderr.write(`${color}${ICONS.success} ${message}${reset}\n`);
250
+ // Clear line first to ensure no leftover content from previous output
251
+ process.stderr.write(`\r\x1b[2K${color}${ICONS.success} ${message}${reset}\n`);
251
252
  }
252
253
 
253
254
  /**
@@ -829,7 +830,11 @@ export function showLoggedOutMessage(appBaseUrl: string, hasProfile = false): vo
829
830
  // Padding needed: 46 - 17 - signupTitle.length - 1 (space before link) = 28 - signupTitle.length
830
831
  const paddingLength = 28 - signupTitle.length;
831
832
  const padding = ' '.repeat(paddingLength);
832
- const showNewLine = showInline ? '' : `║ ${RESET}${link(signupURL)}${YELLOW} ║`;
833
+ // When not showing inline hyperlink, show URL on separate line with proper padding
834
+ // Box format: "║ " + content + "║" = 48 chars total
835
+ // Content area = 46 chars, with leading space = 45 chars for URL + padding
836
+ const urlPadding = Math.max(0, 45 - signupURL.length);
837
+ const showNewLine = showInline ? '' : `║ ${RESET}${link(signupURL)}${YELLOW}${' '.repeat(urlPadding)}║`;
833
838
 
834
839
  const lines = [
835
840
  '╔══════════════════════════════════════════════╗',
@@ -844,6 +849,8 @@ export function showLoggedOutMessage(appBaseUrl: string, hasProfile = false): vo
844
849
 
845
850
  console.log('');
846
851
  lines.filter(Boolean).map((line) => console.log(YELLOW + line + RESET));
852
+ console.log('');
853
+ console.log('');
847
854
  }
848
855
 
849
856
  /**
@@ -1303,11 +1310,17 @@ export async function spinner<T>(
1303
1310
  // Stop animation
1304
1311
  clearInterval(interval);
1305
1312
 
1306
- // Move cursor to start of output, clear all lines
1313
+ // Move cursor to start of output, clear only our lines (not to end of screen)
1307
1314
  if (linesRendered > 0) {
1308
1315
  process.stderr.write(`\x1b[${linesRendered}A`);
1316
+ for (let i = 0; i < linesRendered; i++) {
1317
+ process.stderr.write('\x1b[2K'); // Clear entire line
1318
+ if (i < linesRendered - 1) {
1319
+ process.stderr.write('\x1b[B'); // Move down one line
1320
+ }
1321
+ }
1322
+ process.stderr.write(`\x1b[${linesRendered}A\r`); // Move back up
1309
1323
  }
1310
- process.stderr.write('\x1b[J'); // Clear from cursor to end of screen
1311
1324
  process.stderr.write('\x1B[?25h'); // Show cursor
1312
1325
 
1313
1326
  process.exit(130); // Standard exit code for SIGINT
@@ -1363,11 +1376,22 @@ export async function spinner<T>(
1363
1376
  // Stop animation first
1364
1377
  clearInterval(interval);
1365
1378
 
1366
- // Move cursor to start of output, clear all lines
1379
+ // Move cursor to start of output, clear only our lines (not to end of screen)
1367
1380
  if (linesRendered > 0) {
1368
1381
  process.stderr.write(`\x1b[${linesRendered}A`);
1382
+ for (let i = 0; i < linesRendered; i++) {
1383
+ process.stderr.write('\r\x1b[2K'); // Clear entire line
1384
+ if (i < linesRendered - 1) {
1385
+ process.stderr.write('\x1b[B'); // Move down one line
1386
+ }
1387
+ }
1388
+ // After loop, cursor is at last cleared line (linesRendered - 1 from start)
1389
+ // Move up (linesRendered - 1) to get back to start position
1390
+ if (linesRendered > 1) {
1391
+ process.stderr.write(`\x1b[${linesRendered - 1}A`);
1392
+ }
1393
+ process.stderr.write('\r');
1369
1394
  }
1370
- process.stderr.write('\x1b[J'); // Clear from cursor to end of screen
1371
1395
  process.stderr.write('\x1B[?25h'); // Show cursor
1372
1396
 
1373
1397
  // If clearOnSuccess is false, show success message
@@ -1384,11 +1408,22 @@ export async function spinner<T>(
1384
1408
  // Stop animation first
1385
1409
  clearInterval(interval);
1386
1410
 
1387
- // Move cursor to start of output, clear all lines
1411
+ // Move cursor to start of output, clear only our lines (not to end of screen)
1388
1412
  if (linesRendered > 0) {
1389
1413
  process.stderr.write(`\x1b[${linesRendered}A`);
1414
+ for (let i = 0; i < linesRendered; i++) {
1415
+ process.stderr.write('\r\x1b[2K'); // Clear entire line
1416
+ if (i < linesRendered - 1) {
1417
+ process.stderr.write('\x1b[B'); // Move down one line
1418
+ }
1419
+ }
1420
+ // After loop, cursor is at last cleared line (linesRendered - 1 from start)
1421
+ // Move up (linesRendered - 1) to get back to start position
1422
+ if (linesRendered > 1) {
1423
+ process.stderr.write(`\x1b[${linesRendered - 1}A`);
1424
+ }
1425
+ process.stderr.write('\r');
1390
1426
  }
1391
- process.stderr.write('\x1b[J'); // Clear from cursor to end of screen
1392
1427
  process.stderr.write('\x1B[?25h'); // Show cursor
1393
1428
 
1394
1429
  // Show error
@@ -1561,7 +1596,8 @@ export async function runCommand(options: CommandRunnerOptions): Promise<number>
1561
1596
 
1562
1597
  for (const line of lines) {
1563
1598
  if (line.trim()) {
1564
- allOutputLines.push(line);
1599
+ // Strip ANSI codes from command output to prevent cursor/display issues
1600
+ allOutputLines.push(stripAnsi(line));
1565
1601
  renderOutput(maxLinesOutput); // Show last N lines while streaming
1566
1602
  }
1567
1603
  }
@@ -1582,25 +1618,44 @@ export async function runCommand(options: CommandRunnerOptions): Promise<number>
1582
1618
  if (linesRendered > 0) {
1583
1619
  // Move up to the command line
1584
1620
  process.stdout.write(`\x1b[${linesRendered}A`);
1585
- // Clear each line (entire line) and move cursor back up
1621
+ // Clear each line (entire line)
1586
1622
  for (let i = 0; i < linesRendered; i++) {
1587
- process.stdout.write('\x1b[2K'); // Clear entire line
1623
+ process.stdout.write('\r\x1b[2K'); // Clear entire line
1588
1624
  if (i < linesRendered - 1) {
1589
1625
  process.stdout.write('\x1b[B'); // Move down one line
1590
1626
  }
1591
1627
  }
1592
- // Move cursor back up to original position
1593
- process.stdout.write(`\x1b[${linesRendered}A\r`);
1628
+ // After loop, cursor is at last cleared line (linesRendered - 1 from start)
1629
+ // Move up (linesRendered - 1) to get back to start position
1630
+ if (linesRendered > 1) {
1631
+ process.stdout.write(`\x1b[${linesRendered - 1}A`);
1632
+ }
1633
+ process.stdout.write('\r');
1594
1634
  }
1595
1635
  return exitCode;
1596
1636
  }
1597
1637
 
1598
- // Clear all rendered lines completely
1638
+ // Determine how many lines to show in final output
1639
+ const finalLinesToShow = exitCode === 0 ? maxLinesOutput : maxLinesOnFailure;
1640
+ const finalOutputLines = allOutputLines.slice(-finalLinesToShow);
1641
+
1642
+ // Clear all rendered lines completely (only our lines, not previous output)
1599
1643
  if (linesRendered > 0) {
1600
1644
  // Move up to the command line (first line of our output)
1601
1645
  process.stdout.write(`\x1b[${linesRendered}A`);
1602
- // Move to beginning of line and clear from cursor to end of screen
1603
- process.stdout.write('\r\x1b[J');
1646
+ // Clear the lines we rendered during streaming
1647
+ for (let i = 0; i < linesRendered; i++) {
1648
+ process.stdout.write('\r\x1b[2K'); // Clear entire line
1649
+ if (i < linesRendered - 1) {
1650
+ process.stdout.write('\x1b[B'); // Move down one line
1651
+ }
1652
+ }
1653
+ // After loop, cursor is at last cleared line (linesRendered - 1 from start)
1654
+ // Move up (linesRendered - 1) to get back to start position
1655
+ if (linesRendered > 1) {
1656
+ process.stdout.write(`\x1b[${linesRendered - 1}A`);
1657
+ }
1658
+ process.stdout.write('\r');
1604
1659
  }
1605
1660
 
1606
1661
  // Determine icon based on exit code
@@ -1612,19 +1667,18 @@ export async function runCommand(options: CommandRunnerOptions): Promise<number>
1612
1667
  `\r\x1b[K${statusColor}${icon}${reset} ${cmdColor}${displayCmd}${reset}\n`
1613
1668
  );
1614
1669
 
1615
- // Determine how many lines to show in final output
1616
- const finalLinesToShow = exitCode === 0 ? maxLinesOutput : maxLinesOnFailure;
1617
-
1618
- // Show final output lines
1619
- const finalOutputLines = allOutputLines.slice(-finalLinesToShow);
1670
+ // Show final output lines (clearing each line first in case we're using more lines than before)
1620
1671
  for (const line of finalOutputLines) {
1621
1672
  const displayLine =
1622
1673
  truncate && getDisplayWidth(line) > maxLineWidth
1623
1674
  ? truncateToWidth(line, maxLineWidth)
1624
1675
  : line;
1625
- process.stdout.write(`\r\x1b[K${mutedColor}${displayLine}${reset}\n`);
1676
+ process.stdout.write(`\x1b[2K${mutedColor}${displayLine}${reset}\n`);
1626
1677
  }
1627
1678
 
1679
+ // If we're showing more lines than we had before, the extra lines may contain old content
1680
+ // We've already written over them, so they're clean now
1681
+
1628
1682
  return exitCode;
1629
1683
  } catch (err) {
1630
1684
  // Move cursor up to clear our UI
@@ -1705,23 +1759,33 @@ export async function selectOrganization(
1705
1759
  return orgs[0].id;
1706
1760
  }
1707
1761
 
1708
- if (!process.stdin.isTTY) {
1709
- if (initial) {
1710
- return initial;
1762
+ // Use saved preference if available (regardless of TTY mode)
1763
+ // This allows consistent behavior without prompting on every command
1764
+ if (initial) {
1765
+ const initialOrg = orgs.find((o) => o.id === initial);
1766
+ if (initialOrg) {
1767
+ return initialOrg.id;
1711
1768
  }
1712
- fatal(
1713
- 'Organization selection required but cannot prompt in non-interactive environment. Set AGENTUITY_CLOUD_ORG_ID or provide a default organization using --org-id'
1714
- );
1715
1769
  }
1716
1770
 
1717
- // Find the index of the initial org to pre-select it in the list
1718
- const initialIndex = initial ? orgs.findIndex((o) => o.id === initial) : -1;
1771
+ // Check for non-interactive environment (check both stdin and stdout)
1772
+ const isNonInteractive = !process.stdin.isTTY || !process.stdout.isTTY;
1773
+ if (isNonInteractive) {
1774
+ // In non-interactive mode with multiple orgs, auto-select first org
1775
+ // This allows scripts and CI/CD to work without explicit org selection
1776
+ warning(
1777
+ `Multiple organizations found. Auto-selecting first org: ${orgs[0].name}. ` +
1778
+ `Set AGENTUITY_CLOUD_ORG_ID or use --org-id to specify a different org.`
1779
+ );
1780
+ return orgs[0].id;
1781
+ }
1719
1782
 
1783
+ // Interactive mode with no saved preference - prompt user
1720
1784
  const response = await enquirer.prompt<{ action: string }>({
1721
1785
  type: 'select',
1722
1786
  name: 'action',
1723
1787
  message: 'Select an organization',
1724
- initial: initialIndex >= 0 ? initialIndex : 0,
1788
+ initial: 0,
1725
1789
  choices: orgs.map((o) => ({ message: o.name, name: o.id })),
1726
1790
  });
1727
1791
 
package/src/types.ts CHANGED
@@ -62,6 +62,7 @@ export const ConfigSchema = zod.object({
62
62
  })
63
63
  .optional()
64
64
  .describe('the gravity client information'),
65
+
65
66
  });
66
67
 
67
68
  export type Config = zod.infer<typeof ConfigSchema>;