eve 0.12.1 → 0.12.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 (126) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/src/cli/commands/agent-instructions.d.ts +15 -3
  3. package/dist/src/cli/commands/agent-instructions.js +3 -1
  4. package/dist/src/cli/commands/agent-prompt/build-and-verify.md +33 -0
  5. package/dist/src/cli/commands/agent-prompt/collect-intent.md +18 -0
  6. package/dist/src/cli/commands/agent-prompt/intro-handoff.md +4 -0
  7. package/dist/src/cli/commands/agent-prompt/intro-setup.md +5 -0
  8. package/dist/src/cli/commands/agent-prompt/scaffold.md +19 -0
  9. package/dist/src/cli/commands/agent-prompt/vercel-connect.md +3 -0
  10. package/dist/src/cli/commands/init-repl.d.ts +79 -0
  11. package/dist/src/cli/commands/init-repl.js +1 -0
  12. package/dist/src/cli/commands/init.d.ts +6 -0
  13. package/dist/src/cli/commands/init.js +1 -1
  14. package/dist/src/cli/dev/tui/line-editor.d.ts +2 -0
  15. package/dist/src/cli/dev/tui/line-editor.js +2 -2
  16. package/dist/src/cli/dev/tui/prompt-command-handler.js +1 -1
  17. package/dist/src/cli/dev/tui/setup-commands.d.ts +26 -7
  18. package/dist/src/cli/dev/tui/setup-commands.js +2 -2
  19. package/dist/src/cli/dev/tui/setup-flow.d.ts +5 -3
  20. package/dist/src/cli/dev/tui/setup-panel.d.ts +12 -7
  21. package/dist/src/cli/dev/tui/setup-panel.js +3 -3
  22. package/dist/src/cli/dev/tui/terminal-renderer.js +10 -10
  23. package/dist/src/cli/dev/tui/terminal-text.d.ts +11 -0
  24. package/dist/src/cli/dev/tui/terminal-text.js +1 -1
  25. package/dist/src/cli/run.d.ts +1 -0
  26. package/dist/src/cli/run.js +2 -2
  27. package/dist/src/cli/ui/live-row.js +1 -1
  28. package/dist/src/cli/ui/progress-pulse.d.ts +10 -0
  29. package/dist/src/cli/ui/progress-pulse.js +1 -0
  30. package/dist/src/compiled/.vendor-stamp.json +1 -1
  31. package/dist/src/compiled/@vercel/sandbox/_async-retry.d.ts +8 -0
  32. package/dist/src/compiled/@vercel/sandbox/_workflow-serde.d.ts +5 -0
  33. package/dist/src/compiled/@vercel/sandbox/api-client/api-client.d.ts +1442 -0
  34. package/dist/src/compiled/@vercel/sandbox/api-client/api-error.d.ts +29 -0
  35. package/dist/src/compiled/@vercel/sandbox/api-client/base-client.d.ts +39 -0
  36. package/dist/src/compiled/@vercel/sandbox/api-client/file-writer.d.ts +66 -0
  37. package/dist/src/compiled/@vercel/sandbox/api-client/index.d.ts +2 -0
  38. package/dist/src/compiled/@vercel/sandbox/api-client/validators.d.ts +878 -0
  39. package/dist/src/compiled/@vercel/sandbox/api-client/with-retry.d.ts +10 -0
  40. package/dist/src/compiled/@vercel/sandbox/auth/error.d.ts +15 -0
  41. package/dist/src/compiled/@vercel/sandbox/auth/file.d.ts +18 -0
  42. package/dist/src/compiled/@vercel/sandbox/auth/index.d.ts +6 -0
  43. package/dist/src/compiled/@vercel/sandbox/auth/oauth.d.ts +111 -0
  44. package/dist/src/compiled/@vercel/sandbox/auth/poll-for-token.d.ts +28 -0
  45. package/dist/src/compiled/@vercel/sandbox/auth/project.d.ts +40 -0
  46. package/dist/src/compiled/@vercel/sandbox/command.d.ts +290 -0
  47. package/dist/src/compiled/@vercel/sandbox/constants.d.ts +5 -0
  48. package/dist/src/compiled/@vercel/sandbox/filesystem.d.ts +258 -0
  49. package/dist/src/compiled/@vercel/sandbox/index.d.ts +10 -129
  50. package/dist/src/compiled/@vercel/sandbox/proxy.d.ts +55 -0
  51. package/dist/src/compiled/@vercel/sandbox/sandbox.d.ts +1058 -0
  52. package/dist/src/compiled/@vercel/sandbox/session.d.ts +432 -0
  53. package/dist/src/compiled/@vercel/sandbox/snapshot.d.ts +229 -0
  54. package/dist/src/compiled/@vercel/sandbox/utils/get-credentials.d.ts +21 -0
  55. package/dist/src/compiled/@vercel/sandbox/utils/paginator.d.ts +16 -0
  56. package/dist/src/compiled/@vercel/sandbox/utils/resolveSignal.d.ts +15 -0
  57. package/dist/src/compiled/@vercel/sandbox/utils/sandbox-snapshot.d.ts +11 -0
  58. package/dist/src/compiled/@vercel/sandbox/utils/types.d.ts +11 -0
  59. package/dist/src/compiled/chat/_workflow-serde.d.ts +3 -0
  60. package/dist/src/compiled/chat/index.d.ts +1 -1
  61. package/dist/src/discover/discover-agent.js +1 -1
  62. package/dist/src/evals/target.js +1 -1
  63. package/dist/src/execution/node-step.js +1 -1
  64. package/dist/src/execution/sandbox/bindings/just-bash-runtime.js +1 -1
  65. package/dist/src/execution/sandbox/bindings/vercel-base-runtime.d.ts +3 -3
  66. package/dist/src/execution/sandbox/bindings/vercel-create-sdk.d.ts +8 -13
  67. package/dist/src/execution/sandbox/bindings/vercel-credentials.d.ts +3 -3
  68. package/dist/src/execution/sandbox/bindings/vercel-lookup.d.ts +4 -5
  69. package/dist/src/execution/sandbox/bindings/vercel-read-stream.d.ts +5 -0
  70. package/dist/src/execution/sandbox/bindings/vercel-read-stream.js +1 -0
  71. package/dist/src/execution/sandbox/bindings/vercel-sdk-types.d.ts +5 -0
  72. package/dist/src/execution/sandbox/bindings/vercel-sdk-types.js +1 -0
  73. package/dist/src/execution/sandbox/bindings/vercel.d.ts +4 -4
  74. package/dist/src/execution/sandbox/bindings/vercel.js +1 -1
  75. package/dist/src/execution/sandbox/multiplexed-command.d.ts +22 -0
  76. package/dist/src/execution/sandbox/multiplexed-command.js +1 -0
  77. package/dist/src/execution/tool-auth.d.ts +13 -3
  78. package/dist/src/execution/tool-auth.js +1 -1
  79. package/dist/src/execution/workflow-steps.js +1 -1
  80. package/dist/src/harness/tool-loop.js +1 -1
  81. package/dist/src/internal/application/package.js +1 -1
  82. package/dist/src/internal/authored-module-loader.js +2 -2
  83. package/dist/src/internal/nitro/dev-runtime-artifacts.js +1 -1
  84. package/dist/src/public/channels/slack/connections.d.ts +16 -18
  85. package/dist/src/public/channels/slack/defaults.d.ts +1 -1
  86. package/dist/src/public/channels/slack/defaults.js +1 -1
  87. package/dist/src/public/channels/slack/slackChannel.d.ts +6 -7
  88. package/dist/src/public/definitions/tool.d.ts +64 -20
  89. package/dist/src/public/sandbox/vercel-sandbox.d.ts +12 -9
  90. package/dist/src/public/tools/index.d.ts +1 -1
  91. package/dist/src/runtime/connections/scoped-authorization.js +1 -1
  92. package/dist/src/runtime/connections/validate-authorization.js +1 -1
  93. package/dist/src/runtime/framework-tools/connection-search-dynamic.js +1 -1
  94. package/dist/src/setup/ai-gateway-api-key.d.ts +15 -0
  95. package/dist/src/setup/ai-gateway-api-key.js +1 -0
  96. package/dist/src/setup/boxes/apply-ai-gateway-credential.js +1 -1
  97. package/dist/src/setup/boxes/detect-ai-gateway.js +2 -2
  98. package/dist/src/setup/boxes/resolve-provisioning.js +1 -1
  99. package/dist/src/setup/flows/channels.js +1 -1
  100. package/dist/src/setup/flows/deploy.js +1 -1
  101. package/dist/src/setup/flows/install-vercel-cli.js +1 -1
  102. package/dist/src/setup/flows/link.d.ts +2 -1
  103. package/dist/src/setup/flows/link.js +1 -1
  104. package/dist/src/setup/flows/login.js +1 -1
  105. package/dist/src/setup/flows/model.d.ts +3 -2
  106. package/dist/src/setup/flows/model.js +1 -1
  107. package/dist/src/setup/flows/vercel.js +2 -2
  108. package/dist/src/setup/scaffold/create/add-to-project.js +1 -1
  109. package/dist/src/setup/scaffold/create/project.js +1 -1
  110. package/dist/src/setup/vercel-project.d.ts +2 -6
  111. package/dist/src/setup/vercel-project.js +1 -1
  112. package/dist/src/setup/with-spinner.d.ts +3 -0
  113. package/dist/src/setup/with-spinner.js +1 -0
  114. package/dist/src/shared/package-name.d.ts +4 -0
  115. package/dist/src/shared/package-name.js +1 -0
  116. package/dist/src/shared/sandbox-network-policy.d.ts +2 -2
  117. package/docs/channels/slack.mdx +1 -1
  118. package/docs/connections.mdx +9 -6
  119. package/docs/getting-started.mdx +8 -3
  120. package/docs/guides/auth-and-route-protection.md +59 -10
  121. package/docs/guides/dev-tui.md +2 -2
  122. package/docs/reference/cli.md +2 -2
  123. package/docs/reference/typescript-api.md +9 -7
  124. package/package.json +1 -1
  125. package/dist/src/cli/commands/init-agent-handoff.md +0 -12
  126. package/dist/src/cli/commands/init-agent-instructions.md +0 -16
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # eve
2
2
 
3
+ ## 0.12.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 680ff48: Text prompts now use block cursors, while active turns and model or channel setup use shared green progress pulses.
8
+ - 27a9701: Resolve extensionless relative imports whose target basename contains dots when bundling authored modules. Local files such as `./mock-registry.schemas` and dependency requires such as `./Reflect.getPrototypeOf` now probe Eve's configured `.ts` and `.js` extensions before being treated as asset imports.
9
+ - 3a64a8f: `eve init` with no target, when run by a coding agent, now prints a setup guide — what to ask the user, then the scaffold command — instead of scaffolding the current directory. The guide routes both channels (Slack credentials) and connections (per-user OAuth) through Vercel Connect so credentials are provisioned rather than hand-managed. `eve init <name>` and `eve init .` are unchanged.
10
+ - 3a64a8f: `eve init` now offers to open an installed coding-agent REPL when its CLI is on `PATH`, while keeping `eve dev` as the default. It detects Claude Code, Codex, Cursor, Droid, Gemini CLI, opencode, and Pi. The selected REPL starts with a project-specific setup prompt and `eve dev --no-ui` verification guidance. Coding-agent and non-interactive launches, plus systems without any supported CLI, keep the existing development-server handoff.
11
+ - 86a35eb: Add inline tool auth provider overloads so tools can call `ctx.getToken(provider, options?)` and `ctx.requireAuth(provider, options?)` without declaring a single top-level `auth`. Vercel Connect providers can be authored inline with `connect("service/agent")` or `connect({ connector, tokenParams })`; the existing top-level tool `auth` field and no-argument tool auth accessors remain supported for compatibility, but are now deprecated in favor of inline providers.
12
+ - 25ab1e7: Preserve dev-runtime snapshots that are still referenced by local durable workflow data so parked HITL turns can resume after `eve dev` rebuilds.
13
+ - 504f59e: Allow `eve eval` target checks to match a scoped package name such as `@acme/agent` against the runtime agent identity `agent`.
14
+ - 0dca794: Restore Slack authorization status updates by posting a link-free public status while sending the sign-in challenge privately, then updating the public status when authorization completes.
15
+ - 3548363: Strengthen Vercel and just-bash process streaming with deterministic completion, safe output cancellation, and idempotent process operations.
16
+
17
+ ## 0.12.2
18
+
19
+ ### Patch Changes
20
+
21
+ - 8f7d97b: Keep Vercel Sandbox option types synchronized with the installed SDK by vendoring its upstream declaration files instead of maintaining a hand-written copy. Vercel-backed file reads now convert provider Node streams to Eve's public Web stream contract.
22
+
3
23
  ## 0.12.1
4
24
 
5
25
  ### Patch Changes
@@ -1,7 +1,19 @@
1
- export declare function initAgentInstructions(options: {
2
- initCommand: string;
3
- }): string;
1
+ /** Ordered `agent-prompt/` sections composed into the pre-scaffold setup guide. */
2
+ export declare const SETUP_SECTIONS: readonly ["intro-setup.md", "collect-intent.md", "vercel-connect.md", "scaffold.md", "build-and-verify.md"];
3
+ /** Ordered `agent-prompt/` sections composed into the post-scaffold handoff. */
4
+ export declare const HANDOFF_SECTIONS: readonly ["intro-handoff.md", "collect-intent.md", "vercel-connect.md", "build-and-verify.md"];
5
+ /**
6
+ * The pre-scaffold setup guide, shown when a coding agent runs a bare
7
+ * `eve init`. It scaffolds from scratch, so it renders with the universal
8
+ * `npx eve dev` rather than a launcher-specific command.
9
+ */
10
+ export declare function initAgentInstructions(): string;
11
+ /** The post-scaffold handoff printed after a coding agent runs `eve init <name>`. */
4
12
  export declare function initAgentDevHandoff(options: {
5
13
  projectPath: string;
6
14
  devCommand: string;
7
15
  }): string;
16
+ /** The initial prompt for a coding-agent REPL opened inside the scaffolded project. */
17
+ export declare function initAgentReplPrompt(options: {
18
+ devCommand: string;
19
+ }): string;
@@ -1 +1,3 @@
1
- import{readFileSync}from"node:fs";function readTemplate(t){return readFileSync(new URL(t,import.meta.url),`utf8`).trim()}function initAgentInstructions(e){return readTemplate(`./init-agent-instructions.md`).replace(`{{initCommand}}`,()=>e.initCommand)}function initAgentDevHandoff(e){return readTemplate(`./init-agent-handoff.md`).replaceAll(`{{projectPath}}`,()=>e.projectPath).replace(`{{devCommand}}`,()=>e.devCommand)}export{initAgentDevHandoff,initAgentInstructions};
1
+ import{readFileSync}from"node:fs";const SETUP_SECTIONS=[`intro-setup.md`,`collect-intent.md`,`vercel-connect.md`,`scaffold.md`,`build-and-verify.md`],HANDOFF_SECTIONS=[`intro-handoff.md`,`collect-intent.md`,`vercel-connect.md`,`build-and-verify.md`];function compose(t,n){let r=t.map(t=>readFileSync(new URL(`./agent-prompt/${t}`,import.meta.url),`utf8`).trim()).join(`
2
+
3
+ `).replaceAll(`{{devCommand}}`,()=>n.devCommand),{workingDirectory:i}=n;return i===void 0?r:r.replaceAll(`{{workingDirectory}}`,()=>i)}function initAgentInstructions(){return compose(SETUP_SECTIONS,{devCommand:`npx eve dev`})}function initAgentDevHandoff(e){return compose(HANDOFF_SECTIONS,{devCommand:e.devCommand,workingDirectory:e.projectPath})}function initAgentReplPrompt(e){return compose(HANDOFF_SECTIONS,{devCommand:e.devCommand,workingDirectory:`.`})}export{HANDOFF_SECTIONS,SETUP_SECTIONS,initAgentDevHandoff,initAgentInstructions,initAgentReplPrompt};
@@ -0,0 +1,33 @@
1
+ ## Build it out, then verify
2
+
3
+ Work from the project directory. Once eve is installed, the full docs are bundled
4
+ at `node_modules/eve/docs/` and match the installed version exactly. Read
5
+ `README.md` there first, then the guide for what you're adding, such as
6
+ `connections`, `channels/slack`, or `guides/auth-and-route-protection` for the
7
+ Vercel Connect flow.
8
+
9
+ - Put the purpose in `agent/instructions.md` (the always-on system prompt),
10
+ replacing the scaffold's placeholder with what the user said the agent should
11
+ do.
12
+ - Add a first typed tool under `agent/tools/` with `defineTool` from `eve/tools`
13
+ and a Zod `inputSchema`.
14
+
15
+ `{{devCommand}}` starts eve's HMR development server and opens the agent's
16
+ terminal REPL. It does not start or control this coding-agent session, so don't
17
+ use the bare command as a background verification process. Start eve without the
18
+ terminal UI in a controllable background process instead:
19
+
20
+ {{devCommand}} --no-ui
21
+
22
+ Wait for the server URL, then exercise the HTTP API: create a session with
23
+ `POST /eve/v1/session`, attach to `GET /eve/v1/session/:id/stream`, and send a
24
+ follow-up with the returned `continuationToken`. Stop the dev process after
25
+ verification.
26
+
27
+ When the user is ready to use their agent's REPL, give them the interactive
28
+ command to run from the project directory:
29
+
30
+ {{devCommand}}
31
+
32
+ Verify the project's typecheck passes, adapt the model and provider to the user's
33
+ data and use case, and don't commit unless the user asks.
@@ -0,0 +1,18 @@
1
+ ## Collect intent
2
+
3
+ If the user's intent is not already clear, ask the questions one at a time, use
4
+ the coding harness's prompt tools when available, and do not guess.
5
+
6
+ 1. What should the agent do? This becomes its always-on purpose.
7
+ 2. Where should it be reachable? Every agent ships the built-in HTTP channel. On
8
+ top of that:
9
+ - **Web Chat** (a Next.js app): add it at init with `--channel-web-nextjs`.
10
+ - **Slack** and other platforms: add after deploy with `eve channels add slack`.
11
+ Credentials run through **Vercel Connect**, which provisions the bot token
12
+ and verifies inbound webhooks, so there is no `SLACK_BOT_TOKEN` or signing
13
+ secret to manage.
14
+ 3. Which external systems does it need programmatic read/write access to, such as
15
+ Slack, Salesforce, Linear, GitHub, or your own API? Each becomes a connection
16
+ under `agent/connections/`. When a system needs every end-user to sign in, wire
17
+ its auth through **Vercel Connect** (`connect()` from `@vercel/connect/eve`),
18
+ which handles consent, encrypted token storage, and refresh.
@@ -0,0 +1,4 @@
1
+ # Build this eve agent with the user
2
+
3
+ The project at `{{workingDirectory}}` is already scaffolded. Work with the user to
4
+ complete it, running the commands below from that directory.
@@ -0,0 +1,5 @@
1
+ # Set up an eve agent
2
+
3
+ eve is a filesystem-first framework for durable backend AI agents. The agent is a
4
+ directory of files that eve compiles and runs: its instructions, tools,
5
+ connections, and channels. Work through this with the user.
@@ -0,0 +1,19 @@
1
+ ## Scaffold
2
+
3
+ First settle the target: a new project, or the agent added to an existing
4
+ directory? For a new project, propose a name and ask the user to confirm it; for
5
+ an existing one, ask for the directory.
6
+
7
+ For a new project, run (append `--channel-web-nextjs` only if the user wants Web
8
+ Chat):
9
+
10
+ npx eve@latest init <name>
11
+
12
+ This creates the project, installs its dependencies, and initializes Git. Since a
13
+ coding agent launched init, it prints a development handoff instead of starting
14
+ the interactive terminal UI.
15
+
16
+ For an existing app, run `npx eve@latest init .` from its directory. This adds the
17
+ agent and missing dependencies while leaving the existing Git repository and app
18
+ scripts alone. If init cannot be used, install by hand with
19
+ `npm install eve@latest ai zod`; manual installation does not add package scripts.
@@ -0,0 +1,3 @@
1
+ Reach for Vercel Connect for both channels and connections rather than
2
+ hand-managing tokens. It is the path to a working setup out of the box. See
3
+ https://vercel.com/docs/connect.
@@ -0,0 +1,79 @@
1
+ import { type Prompter } from "#setup/prompter.js";
2
+ declare const CODING_AGENT_REPLS: readonly [{
3
+ readonly command: "claude";
4
+ readonly label: "Claude Code";
5
+ readonly promptArgs: (prompt: string) => string[];
6
+ }, {
7
+ readonly command: "codex";
8
+ readonly label: "Codex";
9
+ readonly promptArgs: (prompt: string) => string[];
10
+ }, {
11
+ readonly command: "cursor-agent";
12
+ readonly label: "Cursor";
13
+ readonly promptArgs: (prompt: string) => string[];
14
+ }, {
15
+ readonly command: "droid";
16
+ readonly label: "Droid";
17
+ readonly promptArgs: (prompt: string) => string[];
18
+ }, {
19
+ readonly command: "gemini";
20
+ readonly label: "Gemini CLI";
21
+ readonly promptArgs: (prompt: string) => string[];
22
+ }, {
23
+ readonly command: "opencode";
24
+ readonly label: "opencode";
25
+ readonly promptArgs: (prompt: string) => string[];
26
+ }, {
27
+ readonly command: "pi";
28
+ readonly label: "Pi";
29
+ readonly promptArgs: (prompt: string) => string[];
30
+ }];
31
+ type CodingAgentReplDefinition = (typeof CODING_AGENT_REPLS)[number];
32
+ /** A coding-agent REPL that can take over the terminal after `eve init`. */
33
+ export type CodingAgentRepl = CodingAgentReplDefinition["command"];
34
+ /** The one post-init continuation point for a human terminal session. */
35
+ export type InitHandoff = "eve-dev" | CodingAgentRepl;
36
+ export interface InitReplDependencies {
37
+ createPrompter(): Prompter;
38
+ hasInteractiveTerminal(): boolean;
39
+ isCodingAgentReplAvailable(command: CodingAgentRepl): Promise<boolean>;
40
+ }
41
+ /** The full path of the named REPL's executable on `PATH`, or null if absent. */
42
+ export declare function resolveCodingAgentRepl(command: CodingAgentRepl): Promise<string | null>;
43
+ /** True when the named REPL resolves to an executable on the current `PATH`. */
44
+ export declare function isCodingAgentReplAvailable(command: CodingAgentRepl): Promise<boolean>;
45
+ /**
46
+ * Offers any locally installed coding-agent REPLs immediately before the
47
+ * existing `eve dev` handoff. Non-interactive sessions, and systems with none
48
+ * of them on `PATH`, keep the prior direct-to-dev behavior.
49
+ */
50
+ export declare function selectInitHandoff(input: {
51
+ agentName: string;
52
+ deps?: Partial<InitReplDependencies>;
53
+ }): Promise<InitHandoff>;
54
+ interface ReplSpawnPlan {
55
+ file: string;
56
+ args: readonly string[];
57
+ shell: boolean;
58
+ /** False means the prompt is not in argv and the caller must surface it. */
59
+ seeded: boolean;
60
+ }
61
+ /**
62
+ * Decides how to launch a REPL from its resolved executable path. A directly
63
+ * spawnable executable runs without a shell, so the multi-line prompt passes
64
+ * through argv verbatim. A `.cmd`/`.bat` shim (or an unresolved command) can
65
+ * only run via cmd.exe, which cannot carry the prompt's newlines or
66
+ * metacharacters in an argument, so it launches the bare REPL and reports the
67
+ * prompt as unseeded.
68
+ */
69
+ export declare function replSpawnPlan(command: CodingAgentRepl, resolvedPath: string | null, prompt: string, platform?: NodeJS.Platform): ReplSpawnPlan;
70
+ /** Starts the selected coding-agent REPL in the newly initialized project. */
71
+ export declare function spawnCodingAgentRepl(input: {
72
+ command: CodingAgentRepl;
73
+ cwd: string;
74
+ prompt: string;
75
+ /** Surfaces the prompt when it could not be seeded into the REPL's argv. */
76
+ onPromptUnseeded?: (prompt: string) => void;
77
+ resolvePath?: (command: CodingAgentRepl) => Promise<string | null>;
78
+ }): Promise<boolean>;
79
+ export {};
@@ -0,0 +1 @@
1
+ import{hasInteractiveTerminal}from"./preconditions.js";import{constants}from"node:fs";import{delimiter,extname,join}from"node:path";import{createPrompter}from"#setup/prompter.js";import{spawn}from"node:child_process";import{access,stat}from"node:fs/promises";const positional=e=>[e],CODING_AGENT_REPLS=[{command:`claude`,label:`Claude Code`,promptArgs:positional},{command:`codex`,label:`Codex`,promptArgs:positional},{command:`cursor-agent`,label:`Cursor`,promptArgs:positional},{command:`droid`,label:`Droid`,promptArgs:positional},{command:`gemini`,label:`Gemini CLI`,promptArgs:e=>[`-i`,e]},{command:`opencode`,label:`opencode`,promptArgs:e=>[`--prompt`,e]},{command:`pi`,label:`Pi`,promptArgs:positional}],WINDOWS_EXECUTABLE_EXTENSIONS=[`.COM`,`.EXE`,`.BAT`,`.CMD`],defaultDependencies={createPrompter,hasInteractiveTerminal,isCodingAgentReplAvailable};function executableNames(e){if(process.platform!==`win32`)return[e];let t=process.env.PATHEXT?.split(`;`).filter(Boolean);return(t&&t.length>0?t:WINDOWS_EXECUTABLE_EXTENSIONS).map(t=>`${e}${t}`)}async function isExecutable(e){try{return(await stat(e)).isFile()?(await access(e,constants.X_OK),!0):!1}catch{return!1}}async function resolveCodingAgentRepl(e){let t=process.env.PATH;if(t===void 0||t.length===0)return null;for(let r of t.split(delimiter))if(r.length!==0)for(let t of executableNames(e)){let e=join(r,t);if(await isExecutable(e))return e}return null}async function isCodingAgentReplAvailable(e){return await resolveCodingAgentRepl(e)!==null}function handoffOptions(e,t){return[{value:`eve-dev`,label:`Start eve dev`,focusHint:`talk to '${t}' in your terminal`},...e.map(e=>({value:e.command,label:`Open ${e.label}`,focusHint:`build '${t}' using ${e.label}`}))]}async function selectInitHandoff(e){let t={...defaultDependencies,...e.deps};if(!t.hasInteractiveTerminal())return`eve-dev`;let n=await Promise.all(CODING_AGENT_REPLS.map(({command:e})=>t.isCodingAgentReplAvailable(e))),r=CODING_AGENT_REPLS.filter((e,t)=>n[t]);return r.length===0?`eve-dev`:t.createPrompter().select({message:`How would you like to continue?`,options:handoffOptions(r,e.agentName),initialValue:`eve-dev`})}function codingAgentRepl(e){let t=CODING_AGENT_REPLS.find(t=>t.command===e);if(t===void 0)throw Error(`Unsupported coding-agent REPL "${e}".`);return t}function isDirectlySpawnable(e,t){if(t!==`win32`)return!0;let n=extname(e).toLowerCase();return n===`.exe`||n===`.com`}function replSpawnPlan(e,t,n,r=process.platform){return t!==null&&isDirectlySpawnable(t,r)?{file:t,args:codingAgentRepl(e).promptArgs(n),shell:!1,seeded:!0}:{file:e,args:[],shell:r===`win32`,seeded:!1}}async function spawnCodingAgentRepl(e){let t=e.resolvePath??resolveCodingAgentRepl,n=replSpawnPlan(e.command,await t(e.command),e.prompt);return n.seeded||e.onPromptUnseeded?.(e.prompt),new Promise(t=>{let r=spawn(n.file,n.args,{cwd:e.cwd,shell:n.shell,stdio:`inherit`}),i=!1,settle=e=>{i||(i=!0,t(e))};r.once(`error`,()=>settle(!1)),r.once(`close`,e=>settle(e===0))})}export{isCodingAgentReplAvailable,replSpawnPlan,resolveCodingAgentRepl,selectInitHandoff,spawnCodingAgentRepl};
@@ -4,6 +4,7 @@ import { runPackageManagerInstall, spawnPackageManager } from "#setup/primitives
4
4
  import { addAgentToProject } from "#setup/scaffold/create/add-to-project.js";
5
5
  import { ensureChannel, scaffoldBaseProject } from "#setup/scaffold/index.js";
6
6
  import { tryInitializeGit } from "./init-git.js";
7
+ import { selectInitHandoff, spawnCodingAgentRepl } from "./init-repl.js";
7
8
  export interface InitCliLogger {
8
9
  error(message: string): void;
9
10
  log(message: string): void;
@@ -21,6 +22,8 @@ export interface InitCommandDependencies {
21
22
  now: () => number;
22
23
  runPackageManagerInstall: typeof runPackageManagerInstall;
23
24
  scaffoldBaseProject: typeof scaffoldBaseProject;
25
+ selectInitHandoff: typeof selectInitHandoff;
26
+ spawnCodingAgentRepl: typeof spawnCodingAgentRepl;
24
27
  spawnPackageManager: typeof spawnPackageManager;
25
28
  tryInitializeGit: typeof tryInitializeGit;
26
29
  }
@@ -32,5 +35,8 @@ export declare const EVE_INIT_PACKAGE_SPEC_ENV = "EVE_INIT_PACKAGE_SPEC";
32
35
  *
33
36
  * Runs launched by a coding agent get the dev command printed instead of
34
37
  * spawned after scaffolding, since the dev TUI would wedge the launching agent.
38
+ * A coding agent that omits the target entirely gets the setup guide printed and
39
+ * nothing scaffolded, since a bare `eve init` means it has not yet chosen what to
40
+ * build.
35
41
  */
36
42
  export declare function runInitCommand(logger: InitCliLogger, parentDirectory: string, target: string | undefined, options: InitCommandOptions, dependencies?: InitCommandDependencies): Promise<void>;
@@ -1 +1 @@
1
- import{__toESM}from"../../_virtual/_rolldown/runtime.js";import{require_picocolors}from"../../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js";import{initAgentDevHandoff}from"./agent-instructions.js";import{tryInitializeGit}from"./init-git.js";import{createLogger,isLogLevelEnabled}from"#internal/logging.js";import{basename,join,resolve}from"node:path";import{ensureChannel,scaffoldBaseProject}from"#setup/scaffold/index.js";import{mkdtemp,readdir,rename,rm,stat}from"node:fs/promises";import{performance}from"node:perf_hooks";import{isCodingAgentLaunch}from"#cli/agent-detection.js";import{EVE_WORDMARK}from"#cli/banner.js";import{formatElapsed}from"#cli/format-elapsed.js";import{startCliLiveRow}from"#cli/ui/live-row.js";import{DEFAULT_AGENT_MODEL_ID}from"#shared/default-agent-model.js";import{formatNodeEngineOverrideWarning}from"#setup/node-engine.js";import{detectInvokingPackageManager,detectPackageManager}from"#setup/package-manager.js";import{pathExists}from"#setup/path-exists.js";import{parseProjectName}from"#setup/project-name.js";import{eveDevArguments,runPackageManagerInstall,spawnPackageManager}from"#setup/primitives/index.js";import{addAgentToProject}from"#setup/scaffold/create/add-to-project.js";import{DEFAULT_EVE_PACKAGE_CONTRACT}from"#setup/scaffold/create/project.js";var import_picocolors=__toESM(require_picocolors(),1);const defaultDependencies={addAgentToProject,detectInvokingPackageManager,detectPackageManager,ensureChannel,isCodingAgentLaunch,now:()=>performance.now(),runPackageManagerInstall,scaffoldBaseProject,spawnPackageManager,tryInitializeGit},ALLOWED_CREATE_IN_PLACE_ENTRIES=new Set([`.DS_Store`,`.git`,`.gitkeep`,`.hg`]),EVE_INIT_PACKAGE_SPEC_ENV=`EVE_INIT_PACKAGE_SPEC`,initLog=createLogger(`init`);async function resolveTargetDirectory(e,t){let n=resolve(e,t);return(await stat(n).catch(()=>void 0))?.isDirectory()?n:void 0}function isCurrentDirectoryTarget(e){return/^\.(?:[/\\]+\.?)*$/u.test(e.trim())}async function assertCanScaffoldInPlace(e){let t=(await readdir(e)).filter(e=>!ALLOWED_CREATE_IN_PLACE_ENTRIES.has(e));if(t.length===0)return;let n=t.slice(0,5).join(`, `),r=t.length>5?`, and ${t.length-5} more`:``;throw Error(`Cannot create project in current directory because it is not empty. Found: ${n}${r}. Use an empty directory.`)}async function moveDirectoryContents(e,t){for(let n of await readdir(e))await rename(join(e,n),join(t,n))}async function addToExistingProject(e,t,n,r){if(t.channelWebNextjs===!0)throw Error("`--channel-web-nextjs` is not supported when adding an agent to an existing project. Run `eve channels add web` from the project afterwards instead.");let i=await n.detectPackageManager(e),a=await n.addAgentToProject({projectRoot:e,model:DEFAULT_AGENT_MODEL_ID,packageManager:i.kind,evePackage:r});return{packageManager:i.kind,nodeEngineOverride:a.nodeEngineOverride}}async function resolveScaffoldPackageManager(e,t){let n=await t.detectPackageManager(e);return n.source==="default"?t.detectInvokingPackageManager()??`pnpm`:n.kind}async function scaffoldProject(e,t,n,r,i,a){let c=resolve(e),l=t===`.`,d=l?c:join(c,t);if(l)await assertCanScaffoldInPlace(d);else if(await pathExists(d))throw Error(`Cannot create project because "${d}" already exists.`);let m=await mkdtemp(join(c,`.eve-init-`));try{let e={projectName:l?basename(d):t,model:DEFAULT_AGENT_MODEL_ID,evePackage:a,targetDirectory:m,packageManager:n},s=await i.scaffoldBaseProject(e);return r.channelWebNextjs===!0&&await i.ensureChannel({projectRoot:s,kind:`web`,packageManager:n,configureVercelServices:!1}),l?await moveDirectoryContents(s,d):await rename(s,d),d}finally{await rm(m,{recursive:!0,force:!0})}}function installProgressDetail(e,t){let n=t.text.trim();if(n===``||e!==`npm`)return n||void 0;let r=/^npm silly fetch manifest (.+)$/u.exec(n);if(r!==null)return`Resolving ${r[1]}`;let i=/^npm http fetch \S+ \S+ attempt (\d+) failed with (\S+)$/u.exec(n);if(i!==null)return`npm registry · attempt ${i[1]} failed: ${i[2]}`;if(t.stream===`stdout`||/^npm (?:error|warn)\b/u.test(n))return n}const NPM_NOISE_LINE=/^\s*npm (?:silly|verbose|http|timing)\b/u;async function runInitSteps(e){let{dependencies:t,logger:n,options:r,parentDirectory:i,target:o}=e,c=isLogLevelEnabled(`debug`),l=startCliLiveRow(n);l.update(`Preparing project`);try{let e=await t.isCodingAgentLaunch(),a=o??`.`,u=isCurrentDirectoryTarget(a),d=u?await pathExists(join(resolve(i),`package.json`))?resolve(i):void 0:await resolveTargetDirectory(i,a),f=resolveInitEvePackageOverride(),p=d===void 0?`creating agent`:`adding agent`;l.update(d===void 0?`Creating agent`:`Adding agent`),initLog.debug(p);let m=t.now(),h;if(d===void 0){let e=u?`.`:parseProjectName(a),n=resolve(i),o=await resolveScaffoldPackageManager(e===`.`?n:join(n,e),t);h={kind:`created`,packageManager:o,projectPath:await scaffoldProject(i,e,o,r,t,f)}}else{let e=await addToExistingProject(d,r,t,f);h=e.nodeEngineOverride===void 0?{kind:`added`,packageManager:e.packageManager,projectPath:d}:{kind:`added`,nodeEngineOverride:e.nodeEngineOverride,packageManager:e.packageManager,projectPath:d}}let g=t.now()-m;initLog.debug(`${p} done`,{ms:g}),l.update(`Installing dependencies`,`${h.packageManager} install`),initLog.debug(`installing dependencies with ${h.packageManager}`);let _=t.now(),v=[],y=[],b=await t.runPackageManagerInstall(h.packageManager,h.projectPath,{bypassMinimumReleaseAge:!0,progressDetails:process.stdout.isTTY===!0&&!c,onOutput:e=>{e.text.trim()!==``&&(y.push(e.text),y.length>20&&y.shift(),NPM_NOISE_LINE.test(e.text)||v.push(e.text)),c&&initLog.debug(e.text);let t=installProgressDetail(h.packageManager,e);t!==void 0&&l.update(`Installing dependencies`,t)}}),x=t.now()-_;if(!b){initLog.debug(`dependency installation failed`,{ms:x}),l.stop();let e=v.length>0?v:y;for(let t of e)n.error(t);throw Error(`Failed to install dependencies in "${h.projectPath}".`)}return initLog.debug(`dependencies installed`,{ms:x}),h.kind===`created`?(l.update(`Initializing Git repository`),initLog.debug(`initializing git repository`),{...h,agentElapsedMs:g,agentLaunched:e,gitResult:await t.tryInitializeGit(h.projectPath),installElapsedMs:x}):{...h,agentElapsedMs:g,agentLaunched:e,installElapsedMs:x}}finally{l.stop()}}async function runInitCommand(e,t,r,i,a=defaultDependencies){let o=await runInitSteps({dependencies:a,logger:e,options:i,parentDirectory:t,target:r});if(o.kind===`created`?e.log(`${import_picocolors.default.green(`✓`)} Created an ${EVE_WORDMARK} agent in ${import_picocolors.default.bold(o.projectPath)} ${import_picocolors.default.dim(`in ${formatElapsed(o.agentElapsedMs)}`)}`):(e.log(`${import_picocolors.default.green(`✓`)} Added an ${EVE_WORDMARK} agent to ${import_picocolors.default.bold(o.projectPath)} ${import_picocolors.default.dim(`in ${formatElapsed(o.agentElapsedMs)}`)}`),o.nodeEngineOverride!==void 0&&e.log(import_picocolors.default.yellow(`⚠ ${formatNodeEngineOverrideWarning(o.nodeEngineOverride)}`))),e.log(`${import_picocolors.default.green(`✓`)} Installed dependencies ${import_picocolors.default.dim(`in ${formatElapsed(o.installElapsedMs)}`)}`),o.kind===`created`&&o.gitResult.kind===`failed`&&e.error(import_picocolors.default.yellow(`Git initialization failed: ${o.gitResult.reason}`)),o.agentLaunched){e.log(initAgentDevHandoff({projectPath:o.projectPath,devCommand:[o.packageManager,...eveDevArguments(o.packageManager)].join(` `)}));return}let s=eveDevArguments(o.packageManager);if(e.log(import_picocolors.default.dim(`$ eve dev`)),!await a.spawnPackageManager(o.packageManager,o.projectPath,s))throw Error(`Development server exited unsuccessfully in "${o.projectPath}".`)}function resolveInitEvePackageOverride(){let e=process.env[EVE_INIT_PACKAGE_SPEC_ENV]?.trim();if(!(e===void 0||e.length===0))return{nodeEngine:DEFAULT_EVE_PACKAGE_CONTRACT.nodeEngine,version:e}}export{EVE_INIT_PACKAGE_SPEC_ENV,runInitCommand};
1
+ import{__toESM}from"../../_virtual/_rolldown/runtime.js";import{require_picocolors}from"../../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js";import{initAgentDevHandoff,initAgentInstructions,initAgentReplPrompt}from"./agent-instructions.js";import{tryInitializeGit}from"./init-git.js";import{selectInitHandoff,spawnCodingAgentRepl}from"./init-repl.js";import{createLogger,isLogLevelEnabled}from"#internal/logging.js";import{basename,join,resolve}from"node:path";import{ensureChannel,scaffoldBaseProject}from"#setup/scaffold/index.js";import{mkdtemp,readdir,rename,rm,stat}from"node:fs/promises";import{performance}from"node:perf_hooks";import{isCodingAgentLaunch}from"#cli/agent-detection.js";import{EVE_WORDMARK}from"#cli/banner.js";import{formatElapsed}from"#cli/format-elapsed.js";import{startCliLiveRow}from"#cli/ui/live-row.js";import{DEFAULT_AGENT_MODEL_ID}from"#shared/default-agent-model.js";import{formatNodeEngineOverrideWarning}from"#setup/node-engine.js";import{detectInvokingPackageManager,detectPackageManager}from"#setup/package-manager.js";import{pathExists}from"#setup/path-exists.js";import{parseProjectName}from"#setup/project-name.js";import{eveDevArguments,runPackageManagerInstall,spawnPackageManager}from"#setup/primitives/index.js";import{addAgentToProject}from"#setup/scaffold/create/add-to-project.js";import{WizardCancelledError}from"#setup/step.js";import{DEFAULT_EVE_PACKAGE_CONTRACT}from"#setup/scaffold/create/project.js";var import_picocolors=__toESM(require_picocolors(),1);const defaultDependencies={addAgentToProject,detectInvokingPackageManager,detectPackageManager,ensureChannel,isCodingAgentLaunch,now:()=>performance.now(),runPackageManagerInstall,scaffoldBaseProject,selectInitHandoff,spawnCodingAgentRepl,spawnPackageManager,tryInitializeGit},ALLOWED_CREATE_IN_PLACE_ENTRIES=new Set([`.DS_Store`,`.git`,`.gitkeep`,`.hg`]),EVE_INIT_PACKAGE_SPEC_ENV=`EVE_INIT_PACKAGE_SPEC`,initLog=createLogger(`init`);async function resolveTargetDirectory(e,t){let n=resolve(e,t);return(await stat(n).catch(()=>void 0))?.isDirectory()?n:void 0}function isCurrentDirectoryTarget(e){return/^\.(?:[/\\]+\.?)*$/u.test(e.trim())}async function assertCanScaffoldInPlace(e){let t=(await readdir(e)).filter(e=>!ALLOWED_CREATE_IN_PLACE_ENTRIES.has(e));if(t.length===0)return;let n=t.slice(0,5).join(`, `),r=t.length>5?`, and ${t.length-5} more`:``;throw Error(`Cannot create project in current directory because it is not empty. Found: ${n}${r}. Use an empty directory.`)}async function moveDirectoryContents(e,t){for(let n of await readdir(e))await rename(join(e,n),join(t,n))}async function addToExistingProject(e,t,n,r){if(t.channelWebNextjs===!0)throw Error("`--channel-web-nextjs` is not supported when adding an agent to an existing project. Run `eve channels add web` from the project afterwards instead.");let i=await n.detectPackageManager(e),a=await n.addAgentToProject({projectRoot:e,model:DEFAULT_AGENT_MODEL_ID,packageManager:i.kind,evePackage:r});return{packageManager:i.kind,nodeEngineOverride:a.nodeEngineOverride}}async function resolveScaffoldPackageManager(e,t){let n=await t.detectPackageManager(e);return n.source==="default"?t.detectInvokingPackageManager()??`pnpm`:n.kind}async function scaffoldProject(e,t,n,r,i,a){let o=resolve(e),s=t===`.`,c=s?o:join(o,t);if(s)await assertCanScaffoldInPlace(c);else if(await pathExists(c))throw Error(`Cannot create project because "${c}" already exists.`);let l=await mkdtemp(join(o,`.eve-init-`));try{let e={projectName:s?basename(c):t,model:DEFAULT_AGENT_MODEL_ID,evePackage:a,targetDirectory:l,packageManager:n},o=await i.scaffoldBaseProject(e);return r.channelWebNextjs===!0&&await i.ensureChannel({projectRoot:o,kind:`web`,packageManager:n,configureVercelServices:!1}),s?await moveDirectoryContents(o,c):await rename(o,c),c}finally{await rm(l,{recursive:!0,force:!0})}}function installProgressDetail(e,t){let n=t.text.trim();if(n===``||e!==`npm`)return n||void 0;let r=/^npm silly fetch manifest (.+)$/u.exec(n);if(r!==null)return`Resolving ${r[1]}`;let i=/^npm http fetch \S+ \S+ attempt (\d+) failed with (\S+)$/u.exec(n);if(i!==null)return`npm registry · attempt ${i[1]} failed: ${i[2]}`;if(t.stream===`stdout`||/^npm (?:error|warn)\b/u.test(n))return n}const NPM_NOISE_LINE=/^\s*npm (?:silly|verbose|http|timing)\b/u;async function runInitSteps(e){let{dependencies:t,logger:n,options:r,parentDirectory:i,target:a}=e,o=isLogLevelEnabled(`debug`),s=startCliLiveRow(n);s.update(`Preparing project`);try{let e=await t.isCodingAgentLaunch(),c=a??`.`,l=isCurrentDirectoryTarget(c),u=l?await pathExists(join(resolve(i),`package.json`))?resolve(i):void 0:await resolveTargetDirectory(i,c),p=resolveInitEvePackageOverride(),m=u===void 0?`creating agent`:`adding agent`;s.update(u===void 0?`Creating agent`:`Adding agent`),initLog.debug(m);let h=t.now(),g;if(u===void 0){let e=l?`.`:parseProjectName(c),n=resolve(i),a=await resolveScaffoldPackageManager(e===`.`?n:join(n,e),t);g={kind:`created`,packageManager:a,projectPath:await scaffoldProject(i,e,a,r,t,p)}}else{let e=await addToExistingProject(u,r,t,p);g=e.nodeEngineOverride===void 0?{kind:`added`,packageManager:e.packageManager,projectPath:u}:{kind:`added`,nodeEngineOverride:e.nodeEngineOverride,packageManager:e.packageManager,projectPath:u}}let _=t.now()-h;initLog.debug(`${m} done`,{ms:_}),s.update(`Installing dependencies`,`${g.packageManager} install`),initLog.debug(`installing dependencies with ${g.packageManager}`);let v=t.now(),y=[],b=[],x=await t.runPackageManagerInstall(g.packageManager,g.projectPath,{bypassMinimumReleaseAge:!0,progressDetails:process.stdout.isTTY===!0&&!o,onOutput:e=>{e.text.trim()!==``&&(b.push(e.text),b.length>20&&b.shift(),NPM_NOISE_LINE.test(e.text)||y.push(e.text)),o&&initLog.debug(e.text);let t=installProgressDetail(g.packageManager,e);t!==void 0&&s.update(`Installing dependencies`,t)}}),S=t.now()-v;if(!x){initLog.debug(`dependency installation failed`,{ms:S}),s.stop();let e=y.length>0?y:b;for(let t of e)n.error(t);throw Error(`Failed to install dependencies in "${g.projectPath}".`)}return initLog.debug(`dependencies installed`,{ms:S}),g.kind===`created`?(s.update(`Initializing Git repository`),initLog.debug(`initializing git repository`),{...g,agentElapsedMs:_,agentLaunched:e,gitResult:await t.tryInitializeGit(g.projectPath),installElapsedMs:S}):{...g,agentElapsedMs:_,agentLaunched:e,installElapsedMs:S}}finally{s.stop()}}async function runInitCommand(e,t,a,o,s=defaultDependencies){if(a===void 0&&await s.isCodingAgentLaunch()){e.log(initAgentInstructions());return}let c=await runInitSteps({dependencies:s,logger:e,options:o,parentDirectory:t,target:a});c.kind===`created`?e.log(`${import_picocolors.default.green(`✓`)} Created an ${EVE_WORDMARK} agent in ${import_picocolors.default.bold(c.projectPath)} ${import_picocolors.default.dim(`in ${formatElapsed(c.agentElapsedMs)}`)}`):(e.log(`${import_picocolors.default.green(`✓`)} Added an ${EVE_WORDMARK} agent to ${import_picocolors.default.bold(c.projectPath)} ${import_picocolors.default.dim(`in ${formatElapsed(c.agentElapsedMs)}`)}`),c.nodeEngineOverride!==void 0&&e.log(import_picocolors.default.yellow(`⚠ ${formatNodeEngineOverrideWarning(c.nodeEngineOverride)}`))),e.log(`${import_picocolors.default.green(`✓`)} Installed dependencies ${import_picocolors.default.dim(`in ${formatElapsed(c.installElapsedMs)}`)}`),c.kind===`created`&&c.gitResult.kind===`failed`&&e.error(import_picocolors.default.yellow(`Git initialization failed: ${c.gitResult.reason}`));let l=[c.packageManager,...eveDevArguments(c.packageManager)].join(` `),d=initAgentDevHandoff({projectPath:c.projectPath,devCommand:l});if(c.agentLaunched){e.log(d);return}let f;try{f=await s.selectInitHandoff({agentName:basename(c.projectPath)})}catch(e){if(e instanceof WizardCancelledError)return;throw e}if(f!==`eve-dev`){if(e.log(import_picocolors.default.dim(`$ ${f}`)),!await s.spawnCodingAgentRepl({command:f,cwd:c.projectPath,prompt:initAgentReplPrompt({devCommand:l}),onPromptUnseeded:t=>{e.log(import_picocolors.default.yellow(`Could not seed ${f} automatically. Paste this prompt into it:`)),e.log(t)}}))throw Error(`Coding-agent REPL exited unsuccessfully in "${c.projectPath}".`);return}let p=eveDevArguments(c.packageManager);if(e.log(import_picocolors.default.dim(`$ eve dev`)),!await s.spawnPackageManager(c.packageManager,c.projectPath,p))throw Error(`Development server exited unsuccessfully in "${c.projectPath}".`)}function resolveInitEvePackageOverride(){let e=process.env[EVE_INIT_PACKAGE_SPEC_ENV]?.trim();if(!(e===void 0||e.length===0))return{nodeEngine:DEFAULT_EVE_PACKAGE_CONTRACT.nodeEngine,version:e}}export{EVE_INIT_PACKAGE_SPEC_ENV,runInitCommand};
@@ -22,6 +22,8 @@ export interface LineState {
22
22
  export declare const EMPTY_LINE: LineState;
23
23
  /** Builds a line from `text` with the caret placed at the end. */
24
24
  export declare function lineOf(text: string): LineState;
25
+ /** Masks each grapheme with one bullet while preserving the caret's grapheme position. */
26
+ export declare function maskLine(state: LineState): LineState;
25
27
  /** Inserts `value` at the caret and advances the caret past it. */
26
28
  export declare function insert(state: LineState, value: string): LineState;
27
29
  /** Deletes the grapheme before the caret (Backspace). */
@@ -1,6 +1,6 @@
1
- import{inputTextWidth,offsetAtVisibleColumn}from"./terminal-text.js";import{graphemeBoundaryAtOrAfter,graphemes,nextGraphemeBoundary,previousGraphemeBoundary}from"#shared/text-boundaries.js";const EMPTY_LINE={text:``,cursor:0};function lineOf(e){return{text:e,cursor:e.length}}function insert(e,t){if(t.length===0)return e;let r=e.text.slice(0,e.cursor)+t+e.text.slice(e.cursor);return{text:r,cursor:graphemeBoundaryAtOrAfter(r,e.cursor+t.length)}}function backspace(e){if(e.cursor===0)return e;let t=previousGraphemeBoundary(e.text,e.cursor),r=e.text.slice(0,t)+e.text.slice(e.cursor);return{text:r,cursor:graphemeBoundaryAtOrAfter(r,t)}}function deleteForward(e){if(e.cursor>=e.text.length)return e;let t=nextGraphemeBoundary(e.text,e.cursor),r=e.text.slice(0,e.cursor)+e.text.slice(t);return{text:r,cursor:graphemeBoundaryAtOrAfter(r,e.cursor)}}function moveLeft(e){return e.cursor===0?e:{text:e.text,cursor:previousGraphemeBoundary(e.text,e.cursor)}}function moveRight(e){return e.cursor>=e.text.length?e:{text:e.text,cursor:nextGraphemeBoundary(e.text,e.cursor)}}function moveHome(e){let t=logicalLineStart(e.text,e.cursor);return e.cursor===t?e:{text:e.text,cursor:t}}function moveEnd(e){let t=logicalLineEnd(e.text,e.cursor);return e.cursor===t?e:{text:e.text,cursor:t}}function killToEnd(e){let t=logicalLineEnd(e.text,e.cursor);return e.cursor>=t?e:{text:e.text.slice(0,e.cursor)+e.text.slice(t),cursor:e.cursor}}function killToStart(e){let t=logicalLineStart(e.text,e.cursor);return e.cursor<=t?e:{text:e.text.slice(0,t)+e.text.slice(e.cursor),cursor:t}}function deleteWord(e){if(e.cursor===0)return e;let t=logicalLineStart(e.text,e.cursor),r=e.cursor;for(;r>t;){let t=previousGraphemeBoundary(e.text,r);if(!isWhitespace(e.text.slice(t,r)))break;r=t}for(;r>t;){let t=previousGraphemeBoundary(e.text,r);if(isWhitespace(e.text.slice(t,r)))break;r=t}let i=e.text.slice(0,r)+e.text.slice(e.cursor);return{text:i,cursor:graphemeBoundaryAtOrAfter(i,r)}}function logicalLineStart(e,t){return t===0?0:e.lastIndexOf(`
1
+ import{inputTextWidth,offsetAtVisibleColumn}from"./terminal-text.js";import{graphemeBoundaryAtOrAfter,graphemes,nextGraphemeBoundary,previousGraphemeBoundary}from"#shared/text-boundaries.js";const EMPTY_LINE={text:``,cursor:0};function lineOf(e){return{text:e,cursor:e.length}}function maskLine(e){return{text:`•`.repeat(graphemes(e.text).length),cursor:graphemes(e.text.slice(0,e.cursor)).length}}function insert(e,t){if(t.length===0)return e;let r=e.text.slice(0,e.cursor)+t+e.text.slice(e.cursor);return{text:r,cursor:graphemeBoundaryAtOrAfter(r,e.cursor+t.length)}}function backspace(e){if(e.cursor===0)return e;let t=previousGraphemeBoundary(e.text,e.cursor),r=e.text.slice(0,t)+e.text.slice(e.cursor);return{text:r,cursor:graphemeBoundaryAtOrAfter(r,t)}}function deleteForward(e){if(e.cursor>=e.text.length)return e;let t=nextGraphemeBoundary(e.text,e.cursor),r=e.text.slice(0,e.cursor)+e.text.slice(t);return{text:r,cursor:graphemeBoundaryAtOrAfter(r,e.cursor)}}function moveLeft(e){return e.cursor===0?e:{text:e.text,cursor:previousGraphemeBoundary(e.text,e.cursor)}}function moveRight(e){return e.cursor>=e.text.length?e:{text:e.text,cursor:nextGraphemeBoundary(e.text,e.cursor)}}function moveHome(e){let t=logicalLineStart(e.text,e.cursor);return e.cursor===t?e:{text:e.text,cursor:t}}function moveEnd(e){let t=logicalLineEnd(e.text,e.cursor);return e.cursor===t?e:{text:e.text,cursor:t}}function killToEnd(e){let t=logicalLineEnd(e.text,e.cursor);return e.cursor>=t?e:{text:e.text.slice(0,e.cursor)+e.text.slice(t),cursor:e.cursor}}function killToStart(e){let t=logicalLineStart(e.text,e.cursor);return e.cursor<=t?e:{text:e.text.slice(0,t)+e.text.slice(e.cursor),cursor:t}}function deleteWord(e){if(e.cursor===0)return e;let t=logicalLineStart(e.text,e.cursor),r=e.cursor;for(;r>t;){let t=previousGraphemeBoundary(e.text,r);if(!isWhitespace(e.text.slice(t,r)))break;r=t}for(;r>t;){let t=previousGraphemeBoundary(e.text,r);if(isWhitespace(e.text.slice(t,r)))break;r=t}let i=e.text.slice(0,r)+e.text.slice(e.cursor);return{text:i,cursor:graphemeBoundaryAtOrAfter(i,r)}}function logicalLineStart(e,t){return t===0?0:e.lastIndexOf(`
2
2
  `,t-1)+1}function logicalLineEnd(e,t){let n=e.indexOf(`
3
3
  `,t);return n===-1?e.length:n}function applyLineEditorKey(e,t,n){let r=n?.multiline??!1;switch(t.type){case`text`:return insert(e,r?t.value:t.value.replaceAll(`
4
4
  `,` `));case`newline`:return r?insert(e,`
5
5
  `):void 0;case`backspace`:return backspace(e);case`delete`:return deleteForward(e);case`left`:return moveLeft(e);case`right`:return moveRight(e);case`home`:case`ctrl-a`:return moveHome(e);case`end`:case`ctrl-e`:return moveEnd(e);case`ctrl-k`:return killToEnd(e);case`ctrl-u`:return killToStart(e);case`ctrl-w`:return deleteWord(e);default:return}}function isWhitespace(e){return/^\s+$/u.test(e)}function visibleLine(t,n,i=`…`){let a=Math.max(1,n),{text:o,cursor:s}=t,c=graphemes(o),l=c.findIndex(e=>e.start===s),u=l===-1?c.length:l,d=c[u]?.text??``;if(inputTextWidth(o)<=a)return{before:o.slice(0,s),under:d,after:o.slice(s+d.length)};let f=u,p=u+ +(d.length>0),windowWidth=(t,n)=>{let r=c[t]?.start??o.length,a=c[n]?.start??o.length;return inputTextWidth(o.slice(r,a))+(t>0?inputTextWidth(i):0)+(n<c.length?inputTextWidth(i):0)},m=!0;for(;;){let e=f>0&&windowWidth(f-1,p)<=a,t=p<c.length&&windowWidth(f,p+1)<=a;if(!e&&!t)break;m&&e||!t?--f:p+=1,m=!m}let h=c[f]?.start??o.length,g=c[p]?.start??o.length;return{before:`${f>0?i:``}${o.slice(h,s)}`,under:d,after:`${o.slice(s+d.length,g)}${p<c.length?i:``}`}}function layoutPromptInput(e){let t=[],n=0,r=0,i=0;for(let a of e.text.split(`
6
- `))e.cursor>=i&&e.cursor<=i+a.length&&(n=t.length,r=e.cursor-i),t.push({text:a,start:i}),i+=a.length+1;return{rows:t,caretRow:n,caretOffset:r}}function movePromptLine(n,r){let i=layoutPromptInput(n),a=r===`up`?i.caretRow-1:i.caretRow+1;if(a<0||a>=i.rows.length)return;let o=i.rows[i.caretRow],s=i.rows[a],c=inputTextWidth(o.text.slice(0,i.caretOffset));return{text:n.text,cursor:s.start+offsetAtVisibleColumn(s.text,c)}}var PromptHistory=class{#e=[];#t=0;#n=``;add(e){if(e.trim().length!==0){if(this.#e.at(-1)===e){this.#r();return}this.#e.push(e),this.#r()}}begin(e){this.#t=this.#e.length,this.#n=e}previous(e){if(this.#e.length!==0&&(this.#t===this.#e.length&&(this.#n=e),this.#t!==0))return--this.#t,this.#e[this.#t]}next(){if(!(this.#t>=this.#e.length))return this.#t+=1,this.#t===this.#e.length?this.#n:this.#e[this.#t]}#r(){this.#t=this.#e.length,this.#n=``}};export{EMPTY_LINE,PromptHistory,applyLineEditorKey,backspace,deleteForward,deleteWord,insert,killToEnd,killToStart,layoutPromptInput,lineOf,moveEnd,moveHome,moveLeft,movePromptLine,moveRight,visibleLine};
6
+ `))e.cursor>=i&&e.cursor<=i+a.length&&(n=t.length,r=e.cursor-i),t.push({text:a,start:i}),i+=a.length+1;return{rows:t,caretRow:n,caretOffset:r}}function movePromptLine(n,r){let i=layoutPromptInput(n),a=r===`up`?i.caretRow-1:i.caretRow+1;if(a<0||a>=i.rows.length)return;let o=i.rows[i.caretRow],s=i.rows[a],c=inputTextWidth(o.text.slice(0,i.caretOffset));return{text:n.text,cursor:s.start+offsetAtVisibleColumn(s.text,c)}}var PromptHistory=class{#e=[];#t=0;#n=``;add(e){if(e.trim().length!==0){if(this.#e.at(-1)===e){this.#r();return}this.#e.push(e),this.#r()}}begin(e){this.#t=this.#e.length,this.#n=e}previous(e){if(this.#e.length!==0&&(this.#t===this.#e.length&&(this.#n=e),this.#t!==0))return--this.#t,this.#e[this.#t]}next(){if(!(this.#t>=this.#e.length))return this.#t+=1,this.#t===this.#e.length?this.#n:this.#e[this.#t]}#r(){this.#t=this.#e.length,this.#n=``}};export{EMPTY_LINE,PromptHistory,applyLineEditorKey,backspace,deleteForward,deleteWord,insert,killToEnd,killToStart,layoutPromptInput,lineOf,maskLine,moveEnd,moveHome,moveLeft,movePromptLine,moveRight,visibleLine};
@@ -1 +1 @@
1
- import{toErrorMessage}from"#shared/errors.js";function createPromptCommandHandler(t){return{async handle(n,r){let i=t.appRoot;if(i===void 0)return{message:`/${n.name} needs eve dev running the local server (it is not available with --url).`};if(n.name===`model`&&n.argument.length>0)try{let{changeAgentModel:e,formatApplyModelOutcome:r,modelChangeRefusalForUneditableModel:a}=await import(`#setup/flows/model.js`),o=await(t.modelChangeRefusal??a)(i);return o===null?{message:r(await(t.applyModel??e)({appRoot:i,slug:n.argument}))}:{message:o}}catch(t){return{message:`Couldn't change the model: ${toErrorMessage(t)}`}}let a=r.renderer.setupFlow;if(a===void 0)return{message:`/${n.name} is not supported by this renderer.`};let o;try{o=await import(`./setup-commands.js`)}catch(t){return{message:`/${n.name} failed: ${toErrorMessage(t)}`}}let{runTuiSetupCommand:s,SETUP_FLOW_TITLES:c}=o;a.begin(c[n.name]);let l=!0;try{let e={command:n.name,appRoot:i,renderer:a};r.initialModelStep!==void 0&&(e.initialModelStep=r.initialModelStep),t.flows!==void 0&&(e.flows=t.flows);let o=await s(e);l=o.preserveFlowDiagnostics;let c={message:o.message};return o.effect!==void 0&&(c.effect=o.effect),c}finally{a.end({preserveDiagnostics:l})}}}}export{createPromptCommandHandler};
1
+ import{toErrorMessage}from"#shared/errors.js";function createPromptCommandHandler(t){return{async handle(n,r){let i=t.appRoot;if(i===void 0)return{message:`/${n.name} needs eve dev running the local server (it is not available with --url).`};if(n.name===`model`&&n.argument.length>0)try{let{changeAgentModel:e,formatApplyModelOutcome:r,modelChangeRefusalForUneditableModel:a}=await import(`#setup/flows/model.js`),o=await(t.modelChangeRefusal??a)(i);return o===null?{message:r(await(t.applyModel??e)({appRoot:i,slug:n.argument}))}:{message:o}}catch(t){return{message:`Couldn't change the model: ${toErrorMessage(t)}`}}let a=r.renderer.setupFlow;if(a===void 0)return{message:`/${n.name} is not supported by this renderer.`};let o;try{o=await import(`./setup-commands.js`)}catch(t){return{message:`/${n.name} failed: ${toErrorMessage(t)}`}}let{runTuiSetupCommand:s,SETUP_FLOW_CONFIG:c}=o,l=c[n.name];a.begin(l.title,l.indicator);let u=!0;try{let e={command:n.name,appRoot:i,renderer:a};r.initialModelStep!==void 0&&(e.initialModelStep=r.initialModelStep),t.flows!==void 0&&(e.flows=t.flows);let o=await s(e);u=o.preserveFlowDiagnostics;let c={message:o.message};return o.effect!==void 0&&(c.effect=o.effect),c}finally{a.end({preserveDiagnostics:u})}}}}export{createPromptCommandHandler};
@@ -10,13 +10,32 @@ import type { SetupFlowRenderer } from "./setup-flow.js";
10
10
  import type { VercelStatusEffect } from "./vercel-status.js";
11
11
  export type TuiSetupCommand = PromptCommandExtensionName;
12
12
  /**
13
- * Human panel titles per command. The bordered panel never repeats the echoed
14
- * command verbatim (the transcript already shows it), but it always carries a
15
- * title: flows move past their opening question (project pickers, name
16
- * prompts), and without a constant header those later questions float
17
- * unanchored in the panel.
13
+ * Panel title and loading indicator per command. The bordered panel never
14
+ * repeats the echoed command verbatim, but it keeps a constant title as flows
15
+ * move past their opening question.
18
16
  */
19
- export declare const SETUP_FLOW_TITLES: Record<TuiSetupCommand, string>;
17
+ export declare const SETUP_FLOW_CONFIG: {
18
+ vc: {
19
+ title: string;
20
+ indicator: "spinner";
21
+ };
22
+ login: {
23
+ title: string;
24
+ indicator: "spinner";
25
+ };
26
+ model: {
27
+ title: string;
28
+ indicator: "pulse";
29
+ };
30
+ channels: {
31
+ title: string;
32
+ indicator: "pulse";
33
+ };
34
+ deploy: {
35
+ title: string;
36
+ indicator: "spinner";
37
+ };
38
+ };
20
39
  /** The prompter surface plus the working-state interrupt trap a command races against. */
21
40
  export type TuiSetupCommandRenderer = TuiPrompterRenderer & Pick<SetupFlowRenderer, "waitForInterrupt">;
22
41
  export interface TuiSetupCommandInput {
@@ -53,7 +72,7 @@ export interface TuiSetupCommandResult {
53
72
  * Runs one TUI setup command (/model, /channels, /deploy) over the
54
73
  * shared setup flows, asking through the TUI's own bordered panel. Never throws:
55
74
  * every outcome — done, cancelled, failed — folds into the returned command
56
- * result. Ctrl-C or Esc on the working spinner (no question open) aborts the
75
+ * result. Ctrl-C or Esc on the working indicator (no question open) aborts the
57
76
  * active flow, then keeps command ownership until its subprocesses and setup
58
77
  * stack have unwound.
59
78
  */
@@ -1,2 +1,2 @@
1
- import{createTuiPrompter}from"./tui-prompter.js";import{runDeployFlow}from"#setup/flows/deploy.js";import{HumanActionRequiredError}from"#setup/human-action.js";import{runChannelsFlow}from"#setup/flows/channels.js";import{runInstallVercelCliFlow}from"#setup/flows/install-vercel-cli.js";import{runLoginFlow}from"#setup/flows/login.js";import{runModelFlow}from"#setup/flows/model.js";import{openUrl}from"#setup/primitives/open-url.js";import{slackMessageDeepLink}from"#setup/slack-connect.js";import{WizardCancelledError}from"#setup/step.js";const SETUP_FLOW_TITLES={vc:`Install the Vercel CLI`,login:`Log in to Vercel`,model:`Configure the agent model`,channels:`Agent channels`,deploy:`Deploy to Vercel`};function muteableRenderer(e,t){return{readSelect:n=>t()?Promise.resolve(void 0):e.readSelect(n),readEditableSelect:n=>t()?Promise.resolve(void 0):e.readEditableSelect(n),readText:n=>t()?Promise.resolve(void 0):e.readText(n),readAcknowledge:n=>t()?Promise.resolve():e.readAcknowledge(n),readChoice:n=>t()?{choice:Promise.resolve(void 0),close:()=>{}}:e.readChoice(n),setStatus:n=>{t()||e.setStatus(n)},renderLine:(n,r)=>{(!t()||r===`warning`||r===`error`)&&e.renderLine(n,r)},renderOutput:n=>{t()||e.renderOutput(n)}}}async function runTuiSetupCommand(t){let{command:n}=t,r=!1,i=new AbortController,a=(t.createPrompter??createTuiPrompter)(muteableRenderer(t.renderer,()=>r)),o=t.renderer.waitForInterrupt(),s=Symbol(`interrupted`),c=executeSetupCommand(t,a,i.signal);try{let e=await Promise.race([c,o.promise.then(()=>s)]);return e===s?(r=!0,i.abort(new WizardCancelledError),{...await c,message:`/${n} interrupted.`,preserveFlowDiagnostics:!0}):e}finally{o.dispose(),t.renderer.setStatus(void 0)}}async function executeSetupCommand(e,n,s){let{command:c,appRoot:l}=e,u={runInstallVercelCliFlow,runLoginFlow,runModelFlow,runChannelsFlow,runDeployFlow,...e.flows};try{switch(c){case`vc`:return installVercelCliResultMessage(await u.runInstallVercelCliFlow({appRoot:l,prompter:n,signal:s}));case`login`:return loginResultMessage(await u.runLoginFlow({appRoot:l,prompter:n,signal:s}));case`model`:{let t={appRoot:l,prompter:n,signal:s};e.initialModelStep!==void 0&&(t.initialStep=e.initialModelStep);let r=await u.runModelFlow(t);if(r.kind===`cancelled`)return{message:`/model cancelled.`,preserveFlowDiagnostics:!1};let i=[];r.modelMessage!==void 0&&i.push(r.modelMessage),r.providerOutcome!==void 0&&i.push(providerOutcomeMessage(r.providerOutcome));let a={message:i.join(`
2
- `),preserveFlowDiagnostics:!1};return r.providerOutcome!==void 0&&(a.effect={kind:`model-access-changed`}),a}case`channels`:{let e=await u.runChannelsFlow({appRoot:l,prompter:n,signal:s});switch(e.kind){case`failed`:return pendingChannelsResult(`Channel files changed, but /channels failed: ${e.message}`);case`cancelled`:return{message:`/channels cancelled.`,preserveFlowDiagnostics:!0};case`deploy-and-chat`:return await runDeployAndChat(u,{appRoot:l,prompter:n,signal:s},e.chat);case`done`:return e.addedChannels.length===0?{message:`No channels added.`,preserveFlowDiagnostics:!0}:{message:`Channels added: ${e.addedChannels.join(`, `)} — run /deploy to ship them.`,preserveFlowDiagnostics:!0,effect:{kind:`channels-added`}}}}case`deploy`:{let e=await u.runDeployFlow({appRoot:l,prompter:n,interactive:!0,signal:s});return e.kind===`cancelled`?{message:`/deploy cancelled.`,preserveFlowDiagnostics:!0}:e.kind===`needs-link`?{message:`Not linked to a Vercel project — run /model to connect one first.`,preserveFlowDiagnostics:!0}:{message:e.productionUrl===void 0?`Deployed.`:`Deployed: ${e.productionUrl}`,preserveFlowDiagnostics:!0,effect:{kind:`deployed`}}}}}catch(e){if(e instanceof WizardCancelledError)return{message:`/${c} cancelled.`,preserveFlowDiagnostics:c!==`model`};let t=vercelActionOutcome(e,c);return t===void 0?{message:`/${c} failed: ${e instanceof Error?e.message:String(e)}`,preserveFlowDiagnostics:!0}:t}}function vercelActionOutcome(e,t){if(!(e instanceof HumanActionRequiredError))return;let r=vercelActionMessage(e.action.kind,t);return r===void 0?void 0:{message:r,preserveFlowDiagnostics:!0}}function vercelActionMessage(e,t){switch(e){case`vercel-login`:return`You're not logged in to Vercel — run /login, then retry /${t}.`;case`vercel-forbidden`:return`Vercel denied access to that team — run /login to re-authenticate (for example to complete SSO), or pick a team you can access, then retry /${t}.`;case`vercel-cli-missing`:return`The Vercel CLI isn't installed — run /vc to install it, then retry /${t}.`;default:return}}async function runDeployAndChat(e,t,n){let r;try{r=await e.runDeployFlow({...t,interactive:!0})}catch(e){if(e instanceof WizardCancelledError)return pendingChannelsResult(`Channels added, but /deploy was cancelled. Run /deploy to ship them.`);let t=vercelActionOutcome(e,`deploy`);return pendingChannelsResult(t===void 0?`Channels added, but /deploy failed: ${e instanceof Error?e.message:String(e)}`:`Channels added. ${t.message}`)}if(r.kind===`cancelled`)return pendingChannelsResult(`Channels added, but /deploy was cancelled. Run /deploy to ship them.`);if(r.kind===`needs-link`)return pendingChannelsResult(`Channels added, but this directory is not linked to Vercel. Run /model, then /deploy.`);let i=r.productionUrl===void 0?`Deployed.`:`Deployed: ${r.productionUrl}`,a;if(n.chatUrl===void 0)a=`Message your agent in Slack to see it live.`;else{let e=slackMessageDeepLink(n.chatUrl);openUrl(e),a=`Chat with your agent in Slack: ${e}`}return{message:`${i}\n${a}`,preserveFlowDiagnostics:!0,effect:{kind:`deployed`}}}function installVercelCliResultMessage(e){switch(e.kind){case`cancelled`:return{message:`/vc cancelled.`,preserveFlowDiagnostics:!1};case`already`:return{message:`The Vercel CLI is already installed.`,preserveFlowDiagnostics:!1};case`failed`:return{message:"Couldn't install the Vercel CLI — install it manually with `npm i -g vercel@latest`.",preserveFlowDiagnostics:!0};case`installed`:return{message:`Installed the Vercel CLI. Run /login next.`,preserveFlowDiagnostics:!1,effect:{kind:`refresh-identity`}}}}function loginResultMessage(e){switch(e.kind){case`cancelled`:return{message:`/login cancelled.`,preserveFlowDiagnostics:!1};case`already`:return{message:`You're already logged in to Vercel.`,preserveFlowDiagnostics:!1};case`cli-missing`:return{message:`The Vercel CLI isn't installed — run /vc to install it, then retry /login.`,preserveFlowDiagnostics:!0};case`failed`:return{message:`Vercel login didn't complete — run /login to try again.`,preserveFlowDiagnostics:!0};case`logged-in`:return{message:`Logged in to Vercel.`,preserveFlowDiagnostics:!1,effect:{kind:`refresh-identity`}};case`unavailable`:return{message:`Couldn't reach Vercel — check your connection, then retry /login.`,preserveFlowDiagnostics:!0}}}function pendingChannelsResult(e){return{message:e,preserveFlowDiagnostics:!0,effect:{kind:`channels-added`}}}function providerOutcomeMessage(e){let{credential:t,status:n}=e;return n.kind===`gateway-project`?t===void 0?`Project linked. No model credential found; set AI_GATEWAY_API_KEY in .env.local.`:`Project linked. Connected to AI Gateway via ${t}.`:n.kind===`gateway-key`?`Connected to AI Gateway via ${n.envKey} in ${n.envFile}.`:`Provider updated — no gateway credential detected; set AI_GATEWAY_API_KEY in .env.local.`}export{SETUP_FLOW_TITLES,runTuiSetupCommand};
1
+ import{createTuiPrompter}from"./tui-prompter.js";import{runDeployFlow}from"#setup/flows/deploy.js";import{WizardCancelledError}from"#setup/step.js";import{HumanActionRequiredError}from"#setup/human-action.js";import{runChannelsFlow}from"#setup/flows/channels.js";import{runInstallVercelCliFlow}from"#setup/flows/install-vercel-cli.js";import{runLoginFlow}from"#setup/flows/login.js";import{runModelFlow}from"#setup/flows/model.js";import{openUrl}from"#setup/primitives/open-url.js";import{slackMessageDeepLink}from"#setup/slack-connect.js";const SETUP_FLOW_CONFIG={vc:{title:`Install the Vercel CLI`,indicator:`spinner`},login:{title:`Log in to Vercel`,indicator:`spinner`},model:{title:`Configure the agent model`,indicator:`pulse`},channels:{title:`Agent channels`,indicator:`pulse`},deploy:{title:`Deploy to Vercel`,indicator:`spinner`}};function muteableRenderer(e,t){return{readSelect:n=>t()?Promise.resolve(void 0):e.readSelect(n),readEditableSelect:n=>t()?Promise.resolve(void 0):e.readEditableSelect(n),readText:n=>t()?Promise.resolve(void 0):e.readText(n),readAcknowledge:n=>t()?Promise.resolve():e.readAcknowledge(n),readChoice:n=>t()?{choice:Promise.resolve(void 0),close:()=>{}}:e.readChoice(n),setStatus:n=>{t()||e.setStatus(n)},renderLine:(n,r)=>{(!t()||r===`warning`||r===`error`)&&e.renderLine(n,r)},renderOutput:n=>{t()||e.renderOutput(n)}}}async function runTuiSetupCommand(t){let{command:r}=t,i=!1,a=new AbortController,o=(t.createPrompter??createTuiPrompter)(muteableRenderer(t.renderer,()=>i)),s=t.renderer.waitForInterrupt(),c=Symbol(`interrupted`),l=executeSetupCommand(t,o,a.signal);try{let e=await Promise.race([l,s.promise.then(()=>c)]);return e===c?(i=!0,a.abort(new WizardCancelledError),{...await l,message:`/${r} interrupted.`,preserveFlowDiagnostics:!0}):e}finally{s.dispose(),t.renderer.setStatus(void 0)}}async function executeSetupCommand(e,r,c){let{command:l,appRoot:u}=e,d={runInstallVercelCliFlow,runLoginFlow,runModelFlow,runChannelsFlow,runDeployFlow,...e.flows};try{switch(l){case`vc`:return installVercelCliResultMessage(await d.runInstallVercelCliFlow({appRoot:u,prompter:r,signal:c}));case`login`:return loginResultMessage(await d.runLoginFlow({appRoot:u,prompter:r,signal:c}));case`model`:{let t={appRoot:u,prompter:r,signal:c};e.initialModelStep!==void 0&&(t.initialStep=e.initialModelStep);let n=await d.runModelFlow(t);if(n.kind===`cancelled`)return{message:`/model cancelled.`,preserveFlowDiagnostics:!1};let i=[];n.modelMessage!==void 0&&i.push(n.modelMessage),n.providerOutcome!==void 0&&i.push(providerOutcomeMessage(n.providerOutcome));let a={message:i.join(`
2
+ `),preserveFlowDiagnostics:!1};return n.providerOutcome!==void 0&&(a.effect={kind:`model-access-changed`}),a}case`channels`:{let e=await d.runChannelsFlow({appRoot:u,prompter:r,signal:c});switch(e.kind){case`failed`:return pendingChannelsResult(`Channel files changed, but /channels failed: ${e.message}`);case`cancelled`:return{message:`/channels cancelled.`,preserveFlowDiagnostics:!0};case`deploy-and-chat`:return await runDeployAndChat(d,{appRoot:u,prompter:r,signal:c},e.chat);case`done`:return e.addedChannels.length===0?{message:`No channels added.`,preserveFlowDiagnostics:!0}:{message:`Channels added: ${e.addedChannels.join(`, `)} — run /deploy to ship them.`,preserveFlowDiagnostics:!0,effect:{kind:`channels-added`}}}}case`deploy`:{let e=await d.runDeployFlow({appRoot:u,prompter:r,interactive:!0,signal:c});return e.kind===`cancelled`?{message:`/deploy cancelled.`,preserveFlowDiagnostics:!0}:e.kind===`needs-link`?{message:`Not linked to a Vercel project — run /model to connect one first.`,preserveFlowDiagnostics:!0}:{message:e.productionUrl===void 0?`Deployed.`:`Deployed: ${e.productionUrl}`,preserveFlowDiagnostics:!0,effect:{kind:`deployed`}}}}}catch(e){if(e instanceof WizardCancelledError)return{message:`/${l} cancelled.`,preserveFlowDiagnostics:l!==`model`};let t=vercelActionOutcome(e,l);return t===void 0?{message:`/${l} failed: ${e instanceof Error?e.message:String(e)}`,preserveFlowDiagnostics:!0}:t}}function vercelActionOutcome(e,t){if(!(e instanceof HumanActionRequiredError))return;let n=vercelActionMessage(e.action.kind,t);return n===void 0?void 0:{message:n,preserveFlowDiagnostics:!0}}function vercelActionMessage(e,t){switch(e){case`vercel-login`:return`You're not logged in to Vercel — run /login, then retry /${t}.`;case`vercel-forbidden`:return`Vercel denied access to that team — run /login to re-authenticate (for example to complete SSO), or pick a team you can access, then retry /${t}.`;case`vercel-cli-missing`:return`The Vercel CLI isn't installed — run /vc to install it, then retry /${t}.`;default:return}}async function runDeployAndChat(e,t,r){let i;try{i=await e.runDeployFlow({...t,interactive:!0})}catch(e){if(e instanceof WizardCancelledError)return pendingChannelsResult(`Channels added, but /deploy was cancelled. Run /deploy to ship them.`);let t=vercelActionOutcome(e,`deploy`);return pendingChannelsResult(t===void 0?`Channels added, but /deploy failed: ${e instanceof Error?e.message:String(e)}`:`Channels added. ${t.message}`)}if(i.kind===`cancelled`)return pendingChannelsResult(`Channels added, but /deploy was cancelled. Run /deploy to ship them.`);if(i.kind===`needs-link`)return pendingChannelsResult(`Channels added, but this directory is not linked to Vercel. Run /model, then /deploy.`);let a=i.productionUrl===void 0?`Deployed.`:`Deployed: ${i.productionUrl}`,o;if(r.chatUrl===void 0)o=`Message your agent in Slack to see it live.`;else{let e=slackMessageDeepLink(r.chatUrl);openUrl(e),o=`Chat with your agent in Slack: ${e}`}return{message:`${a}\n${o}`,preserveFlowDiagnostics:!0,effect:{kind:`deployed`}}}function installVercelCliResultMessage(e){switch(e.kind){case`cancelled`:return{message:`/vc cancelled.`,preserveFlowDiagnostics:!1};case`already`:return{message:`The Vercel CLI is already installed.`,preserveFlowDiagnostics:!1};case`failed`:return{message:"Couldn't install the Vercel CLI — install it manually with `npm i -g vercel@latest`.",preserveFlowDiagnostics:!0};case`installed`:return{message:`Installed the Vercel CLI. Run /login next.`,preserveFlowDiagnostics:!1,effect:{kind:`refresh-identity`}}}}function loginResultMessage(e){switch(e.kind){case`cancelled`:return{message:`/login cancelled.`,preserveFlowDiagnostics:!1};case`already`:return{message:`You're already logged in to Vercel.`,preserveFlowDiagnostics:!1};case`cli-missing`:return{message:`The Vercel CLI isn't installed — run /vc to install it, then retry /login.`,preserveFlowDiagnostics:!0};case`failed`:return{message:`Vercel login didn't complete — run /login to try again.`,preserveFlowDiagnostics:!0};case`logged-in`:return{message:`Logged in to Vercel.`,preserveFlowDiagnostics:!1,effect:{kind:`refresh-identity`}};case`unavailable`:return{message:`Couldn't reach Vercel — check your connection, then retry /login.`,preserveFlowDiagnostics:!0}}}function pendingChannelsResult(e){return{message:e,preserveFlowDiagnostics:!0,effect:{kind:`channels-added`}}}function providerOutcomeMessage(e){let{credential:t,status:n}=e;return n.kind===`gateway-project`?t===void 0?`Project linked. No model credential found; set AI_GATEWAY_API_KEY in .env.local.`:`Project linked. Connected to AI Gateway via ${t}.`:n.kind===`gateway-key`?`Connected to AI Gateway via ${n.envKey} in ${n.envFile}.`:`Provider updated — no gateway credential detected; set AI_GATEWAY_API_KEY in .env.local.`}export{SETUP_FLOW_CONFIG,runTuiSetupCommand};
@@ -9,6 +9,8 @@ export type SetupEditableSelectResult = {
9
9
  value: string;
10
10
  text: string;
11
11
  };
12
+ /** Animation shown while a setup flow is between questions. */
13
+ export type SetupFlowIndicator = "spinner" | "pulse";
12
14
  interface SetupSelectRequestBase {
13
15
  message: string;
14
16
  options: readonly SetupPanelOption[];
@@ -41,7 +43,7 @@ interface SetupSearchableMultiSelectRequest extends SetupSelectRequestBase {
41
43
  */
42
44
  export type SetupSelectRequest = SetupSingleSelectRequest | SetupSearchSelectRequest | SetupMultiSelectRequest | SetupSearchableMultiSelectRequest;
43
45
  export interface SetupFlowRenderer {
44
- begin(title: string): void;
46
+ begin(title: string, indicator?: SetupFlowIndicator): void;
45
47
  end(options?: {
46
48
  preserveDiagnostics?: boolean;
47
49
  }): void;
@@ -71,7 +73,7 @@ export interface SetupFlowRenderer {
71
73
  }): Promise<void>;
72
74
  /**
73
75
  * Presents an inert context row and a separate action menu beside the live
74
- * flow spinner. Returns the choice plus a `close()` that dismisses the menu
76
+ * flow indicator. Returns the choice plus a `close()` that dismisses the menu
75
77
  * when a concurrent wait resolves first. Used by the Slack install wait for
76
78
  * "Try again" / "Cancel": the poll keeps running while the prompt is up, and
77
79
  * whichever settles first wins.
@@ -81,7 +83,7 @@ export interface SetupFlowRenderer {
81
83
  renderLine(text: string, tone: "info" | "success" | "warning" | "error"): void;
82
84
  renderOutput(text: string): void;
83
85
  /**
84
- * Arms a key trap for the flow's working state — the status spinner between
86
+ * Arms a key trap for the flow's working state — the status indicator between
85
87
  * questions, where no prompt is consuming keys. Ctrl-C or Esc resolves the
86
88
  * promise so the command can abandon an in-flight flow (e.g. a parked
87
89
  * `vercel connect create` browser OAuth). Open questions own their keys; the
@@ -98,29 +98,34 @@ export interface FlowPanelLine {
98
98
  */
99
99
  evidence?: boolean;
100
100
  }
101
+ /** One already-resolved animation frame and its active color. */
102
+ export interface FlowPanelIndicator {
103
+ glyph: string;
104
+ color: "green" | "yellow";
105
+ }
101
106
  export type FlowPanelContent = {
102
107
  kind: "question";
103
108
  rows: readonly string[];
104
- /** The install wait keeps its spinner above the concurrent actions. */
109
+ /** The install wait keeps its indicator above the concurrent actions. */
105
110
  status?: {
106
111
  text: string;
107
- frame: string;
112
+ indicator: FlowPanelIndicator;
108
113
  };
109
114
  } | {
110
115
  kind: "status";
111
116
  status: {
112
117
  text: string;
113
- frame: string;
118
+ indicator: FlowPanelIndicator;
114
119
  };
115
120
  /** Latest child-process output shown transiently beneath the status. */
116
121
  preview?: string;
117
122
  } | {
118
123
  kind: "preview";
119
124
  text: string;
120
- frame: string;
125
+ indicator: FlowPanelIndicator;
121
126
  } | {
122
127
  kind: "idle";
123
- frame: string;
128
+ indicator: FlowPanelIndicator;
124
129
  };
125
130
  /** The whole bordered section: title, recent progress, and one explicit mode. */
126
131
  export interface FlowPanelState {
@@ -131,7 +136,7 @@ export interface FlowPanelState {
131
136
  }
132
137
  /**
133
138
  * Paints the bordered flow panel. Everything a running command produces lives
134
- * here — progress, questions, the status spinner — and the panel vanishes
139
+ * here — progress, questions, the status indicator — and the panel vanishes
135
140
  * wholesale when the command resolves; only the command echo and the elbow
136
141
  * outcome persist in the transcript.
137
142
  */
@@ -143,7 +148,7 @@ export declare function renderFlowPanel(state: FlowPanelState, theme: Theme, wid
143
148
  * the cursor and advertises the rest with a count footer.
144
149
  */
145
150
  export declare function renderSelectQuestion(state: SetupSelectPanelState, theme: Theme, width: number): string[];
146
- /** Paints a text question section: message, a caret-bearing input line, hints. */
151
+ /** Paints a text question section: message, a block-cursor input line, hints. */
147
152
  export declare function renderTextQuestion(state: SetupTextPanelState, theme: Theme, width: number, caretVisible: boolean): string[];
148
153
  /**
149
154
  * Paints a static acknowledgement section (for the flow panel): a heading and
@@ -1,3 +1,3 @@
1
- import{clipVisible,renderInputText,visibleLength,wrapVisibleLine}from"./terminal-text.js";import{visibleLine}from"./line-editor.js";import{graphemes}from"#shared/text-boundaries.js";import{renderCursorRow,renderOptionRow,renderOptionRowContinuation,resolveOptionRowState}from"#setup/cli/option-row.js";import{filterOptions,submitRowIndex}from"#setup/cli/select-state.js";function clip(t,n){return clipVisible(t,n)}function questionFooter(e,t){let n=t.colors;return[``,` ${n.dim(n.italic(e.join(` ${t.glyph.dot} `)))}`]}const BOLD_OR_DIM_CLOSE=`\x1B[22m`;function dimWithEmphasis(e,t){return t.colors.dim(e.replaceAll(BOLD_OR_DIM_CLOSE,`${BOLD_OR_DIM_CLOSE}`))}function toneGlyph(e,t){let n=t.colors;switch(e){case`success`:return n.green(t.glyph.success);case`warning`:return n.yellow(t.glyph.warning);case`error`:return n.red(t.glyph.error);case`info`:return n.dim(t.glyph.dot)}}function renderFlowPanel(e,t,n){let r=t.colors,i=[r.dim(t.glyph.hrule.repeat(Math.max(1,n)))];e.title.length>0&&i.push(` ${r.bold(e.title)}`),i.push(``);let a=e.lines.slice(-6);for(let e of a){let n=e.tone===`info`?r.dim(e.text):e.text;i.push(` ${toneGlyph(e.tone,t)} ${n}`)}switch(a.length>0&&i.push(``),e.content.kind){case`question`:e.content.status!==void 0&&i.push(` ${r.yellow(e.content.status.frame)} ${r.dim(e.content.status.text)}`,``),i.push(...e.content.rows);break;case`status`:i.push(` ${r.yellow(e.content.status.frame)} ${r.dim(e.content.status.text)}`),e.content.preview!==void 0&&i.push(` ${r.dim(e.content.preview)}`);break;case`preview`:i.push(` ${r.yellow(e.content.frame)} ${r.dim(e.content.text)}`);break;case`idle`:i.push(` ${r.yellow(e.content.frame)} ${r.dim(`Working…`)}`);break}return i.map((e,t)=>t===0||e.length===0?clip(e,n):clip(` ${e}`,n))}function optionRow(e){let{option:t,theme:n}=e;return renderOptionRow({colors:n.colors,glyphs:{pointer:n.glyph.pointer,selectedPointer:n.glyph.selectedPointer,success:n.glyph.success,placeholder:n.glyph.option,dot:n.glyph.dot},label:t.label,hint:t.hint,focusHint:t.focusHint,accent:t.accent,isCursor:e.isCursor,state:resolveOptionRowState(t,e.isChecked),placeholder:e.placeholder,hintPadding:e.hintPadding})}function selectPresentation(e){switch(e.kind){case`single`:return{selection:`single`,filter:void 0,layout:`plain`,edit:void 0};case`search`:return{selection:`single`,filter:{placeholder:e.placeholder},layout:`plain`,edit:void 0};case`multi`:return{selection:`multiple`,filter:void 0,layout:`plain`,edit:void 0};case`searchable-multi`:return{selection:`multiple`,filter:{placeholder:e.placeholder},layout:`plain`,edit:void 0};case`stacked`:return{selection:`single`,filter:void 0,layout:`stacked`,edit:void 0};case`task-list`:return{selection:`single`,filter:void 0,layout:`task-list`,edit:void 0};case`editable`:return{selection:`single`,filter:void 0,layout:`task-list`,edit:e.edit}}}function selectMessageRows(e,t,n){if(e===``)return[];let r=e.split(`
2
- `).map((e,r)=>` ${t===`stacked`||r>0?n.colors.bold(e):e}`);return r.push(``),r}function searchFilter(e,t,n){return e.length>0?e+n.colors.dim(n.glyph.caret):t===void 0?n.colors.dim(n.glyph.caret):n.colors.dim(`> ${t}`)}function selectViewSize(e){return e.search?e.filter===``&&e.featuredLead>0?Math.min(e.featuredLead,8):8:e.optionCount}function noticeBody(e,t,n){return e.tone===`info`?n.colors.dim(e.text):e.tone===`success`&&t===`task-list`?n.colors.bold(e.text):e.text}function editableOption(e,t,n,r){if(!t||n?.optionValue!==e.value)return e;let i=n.editor.text||n.defaultValue,a=n.editor.text.length===0?i.length:n.editor.cursor,o=n.caretVisible?r.colors.cyan(r.glyph.caret):``,s=`${i.slice(0,a)}${o}${i.slice(a)}`;return{...e,hint:n.formatHint(s)}}function optionWithoutStackedHint(e,t){if(t!==`stacked`||e.hint===void 0)return{option:e,stackedHint:void 0};let{hint:n,...r}=e;return{option:r,stackedHint:n}}function optionUsesPlaceholder(e,t,n){let r=e.filter!==void 0,i=e.layout===`task-list`&&t===n-1,a=e.selection===`multiple`,o=e.layout!==`plain`;return!r&&!i&&(a||o)}function appendSelectOptionRows(e){let{rows:t,state:n,presentation:r,visible:i,start:a,end:o,cursor:s,visibleLabelWidth:l,theme:u}=e,d=u.colors;for(let e=a;e<o;e+=1){let f=i[e],p=e===s;r.layout===`task-list`&&e===o-1&&e>a&&t.push(``);let{option:m,stackedHint:h}=optionWithoutStackedHint(editableOption(f,p,r.edit,u),r.layout);t.push(` ${optionRow({option:m,isCursor:p,isChecked:r.selection===`multiple`&&n.select.selected.has(f.value),placeholder:optionUsesPlaceholder(r,e,i.length),hintPadding:l-f.label.length,theme:u})}`),h!==void 0&&t.push(` ${renderOptionRowContinuation(dimWithEmphasis(h,u))}`),f.description!==void 0&&(f.disabled===!0||p)&&t.push(` ${d.dim(f.description)}`),r.layout===`stacked`&&e<o-1&&t.push(``)}}function appendSubmitRow(e,t,n,r){if(n<0)return;let i=t===n,a=i?`${r.glyph.selectedPointer} ${r.colors.bold(`Submit`)}`:` Submit`;e.push(``,` ${renderCursorRow(a,i,r.colors)}`)}function appendSelectNotices(e,t,i,a,o){if(!(t===void 0||t.length===0)){e.push(``);for(let s of t){let t=toneGlyph(s.tone,a),c=` `.repeat(visibleLength(t)+1),l=Math.max(1,o-2-visibleLength(t)-1),u=wrapVisibleLine(s.text,l);for(let[n,r]of u.entries()){let o=noticeBody({...s,text:r},i,a);e.push(n===0?` ${t} ${o}`:` ${c}${o}`)}}}}function selectFooterHints(e,t,n){let r=[];return e.edit!==void 0&&t[n]?.value===e.edit.optionValue&&r.push(`type to rename`),e.filter!==void 0&&r.push(`type to filter`),r.push(`↑/↓ move`),r.push(e.selection===`multiple`?`space to toggle`:`enter to select`),e.selection===`multiple`&&r.push(`enter on Submit to confirm`),r.push(`esc to cancel`),r}function renderActionQuestion(e,t,n){let r=[` ${t.colors.dim(`${t.glyph.dot} ${e.context}`)}`,``];for(let[n,i]of e.actions.entries())r.push(` ${optionRow({option:i,isCursor:n===e.cursor,isChecked:!1,placeholder:!0,hintPadding:0,theme:t})}`);return r.push(...questionFooter([`↑/↓ move`,`enter to select`,`esc to cancel`],t)),r.map(e=>clip(e,n))}function renderSelectQuestion(e,t,n){if(e.kind===`actions`)return renderActionQuestion(e,t,n);let r=t.colors,i=selectPresentation(e),a=i.filter?filterOptions(e.options,e.select.filter):e.options,o=i.selection===`multiple`?submitRowIndex(a):-1,s=e.select.cursor,c=selectMessageRows(e.message,i.layout,t);i.filter!==void 0&&c.push(` ${searchFilter(e.select.filter,i.filter.placeholder,t)}`);let l=0;for(;a[l]?.featured;)l+=1;let f=selectViewSize({search:i.filter!==void 0,filter:e.select.filter,featuredLead:l,optionCount:a.length}),p=Math.max(0,Math.min(s-Math.floor(f/2),Math.max(0,a.length-f))),m=Math.min(p+f,a.length),h=a.slice(p,m).filter(e=>e.hint!==void 0||e.focusHint!==void 0).reduce((e,t)=>Math.max(e,t.label.length),0);return a.length===0&&c.push(` ${r.dim(`(no matches)`)}`),appendSelectOptionRows({rows:c,state:e,presentation:i,visible:a,start:p,end:m,cursor:s,visibleLabelWidth:h,theme:t}),appendSubmitRow(c,s,o,t),a.length>m-p&&c.push(` ${r.dim(`↑↓ ${a.length} options, showing ${p+1}–${m}`)}`),appendSelectNotices(c,e.notices,i.layout,t,n),e.error!==void 0&&c.push(``,` ${r.red(e.error)}`),c.push(...questionFooter(selectFooterHints(i,a,s),t)),c.map(e=>clip(e,n))}function renderTextQuestion(e,n,r,o){let s=n.colors,c=[];for(let t of e.notices??[]){let e=t.tone===`info`?s.dim(t.text):t.text;c.push(`${toneGlyph(t.tone,n)} ${e}`)}c.push(...e.message.split(`
3
- `).map(e=>` ${s.bold(e)}`));let l=Math.max(4,r-4),{before:u,under:d,after:f}=visibleLine(e.mask?{text:`•`.repeat(graphemes(e.editor.text).length),cursor:graphemes(e.editor.text.slice(0,e.editor.cursor)).length}:{text:e.editor.text,cursor:e.editor.cursor},l,n.glyph.ellipsis),p=o?s.cyan(n.glyph.caret):` `,m=e.editor.text.length===0&&e.placeholder!==void 0?`${p}${s.dim(e.placeholder)}`:`${renderInputText(u)}${p}${renderInputText(d)}${renderInputText(f)}`;return c.push(` ${m}`),e.error!==void 0&&c.push(``,` ${s.red(e.error)}`),c.push(...questionFooter([`enter to submit`,`esc to cancel`],n)),c.map(e=>clip(e,r))}function renderAcknowledgeQuestion(e,t,n){let r=t.colors,i=[` ${r.bold(e.message)}`];if(e.lines.length>0){i.push(``);for(let t of e.lines)i.push(` ${r.dim(t)}`)}return i.push(...questionFooter([`enter to continue`],t)),i.map(e=>clip(e,n))}export{renderAcknowledgeQuestion,renderFlowPanel,renderSelectQuestion,renderTextQuestion};
1
+ import{clipVisible,renderInputText,renderInputWithBlockCursor,visibleLength,wrapVisibleLine}from"./terminal-text.js";import{maskLine,visibleLine}from"./line-editor.js";import{renderCursorRow,renderOptionRow,renderOptionRowContinuation,resolveOptionRowState}from"#setup/cli/option-row.js";import{filterOptions,submitRowIndex}from"#setup/cli/select-state.js";function clip(t,n){return clipVisible(t,n)}function questionFooter(e,t){let n=t.colors;return[``,` ${n.dim(n.italic(e.join(` ${t.glyph.dot} `)))}`]}const BOLD_OR_DIM_CLOSE=`\x1B[22m`;function dimWithEmphasis(e,t){return t.colors.dim(e.replaceAll(BOLD_OR_DIM_CLOSE,`${BOLD_OR_DIM_CLOSE}`))}function toneGlyph(e,t){let n=t.colors;switch(e){case`success`:return n.green(t.glyph.success);case`warning`:return n.yellow(t.glyph.warning);case`error`:return n.red(t.glyph.error);case`info`:return n.dim(t.glyph.dot)}}function renderIndicator(e,t){return e.color===`green`?t.colors.green(e.glyph):t.colors.yellow(e.glyph)}function renderFlowPanel(e,t,n){let r=t.colors,i=[r.dim(t.glyph.hrule.repeat(Math.max(1,n)))];e.title.length>0&&i.push(` ${r.bold(e.title)}`),i.push(``);let a=e.lines.slice(-6);for(let e of a){let n=e.tone===`info`?r.dim(e.text):e.text;i.push(` ${toneGlyph(e.tone,t)} ${n}`)}switch(a.length>0&&i.push(``),e.content.kind){case`question`:e.content.status!==void 0&&i.push(` ${renderIndicator(e.content.status.indicator,t)} ${r.dim(e.content.status.text)}`,``),i.push(...e.content.rows);break;case`status`:i.push(` ${renderIndicator(e.content.status.indicator,t)} ${r.dim(e.content.status.text)}`),e.content.preview!==void 0&&i.push(` ${r.dim(e.content.preview)}`);break;case`preview`:i.push(` ${renderIndicator(e.content.indicator,t)} ${r.dim(e.content.text)}`);break;case`idle`:i.push(` ${renderIndicator(e.content.indicator,t)} ${r.dim(`Working…`)}`);break}return i.map((e,t)=>t===0||e.length===0?clip(e,n):clip(` ${e}`,n))}function optionRow(e){let{option:t,theme:n}=e;return renderOptionRow({colors:n.colors,glyphs:{pointer:n.glyph.pointer,selectedPointer:n.glyph.selectedPointer,success:n.glyph.success,placeholder:n.glyph.option,dot:n.glyph.dot},label:t.label,hint:t.hint,focusHint:t.focusHint,accent:t.accent,isCursor:e.isCursor,state:resolveOptionRowState(t,e.isChecked),placeholder:e.placeholder,hintPadding:e.hintPadding})}function selectPresentation(e){switch(e.kind){case`single`:return{selection:`single`,filter:void 0,layout:`plain`,edit:void 0};case`search`:return{selection:`single`,filter:{placeholder:e.placeholder},layout:`plain`,edit:void 0};case`multi`:return{selection:`multiple`,filter:void 0,layout:`plain`,edit:void 0};case`searchable-multi`:return{selection:`multiple`,filter:{placeholder:e.placeholder},layout:`plain`,edit:void 0};case`stacked`:return{selection:`single`,filter:void 0,layout:`stacked`,edit:void 0};case`task-list`:return{selection:`single`,filter:void 0,layout:`task-list`,edit:void 0};case`editable`:return{selection:`single`,filter:void 0,layout:`task-list`,edit:e.edit}}}function selectMessageRows(e,t,n){if(e===``)return[];let r=e.split(`
2
+ `).map((e,r)=>` ${t===`stacked`||r>0?n.colors.bold(e):e}`);return r.push(``),r}function searchFilter(e,t,n){return e.length>0?e+n.colors.dim(n.glyph.caret):t===void 0?n.colors.dim(n.glyph.caret):n.colors.dim(`> ${t}`)}function selectViewSize(e){return e.search?e.filter===``&&e.featuredLead>0?Math.min(e.featuredLead,8):8:e.optionCount}function noticeBody(e,t,n){return e.tone===`info`?n.colors.dim(e.text):e.tone===`success`&&t===`task-list`?n.colors.bold(e.text):e.text}function editableOption(e,t,n,r){if(!t||n?.optionValue!==e.value)return e;let i=n.editor.text||n.defaultValue,a=n.editor.text.length===0?i.length:n.editor.cursor,o=n.caretVisible?r.colors.cyan(r.glyph.caret):``,s=`${i.slice(0,a)}${o}${i.slice(a)}`;return{...e,hint:n.formatHint(s)}}function optionWithoutStackedHint(e,t){if(t!==`stacked`||e.hint===void 0)return{option:e,stackedHint:void 0};let{hint:n,...r}=e;return{option:r,stackedHint:n}}function optionUsesPlaceholder(e,t,n){let r=e.filter!==void 0,i=e.layout===`task-list`&&t===n-1,a=e.selection===`multiple`,o=e.layout!==`plain`;return!r&&!i&&(a||o)}function appendSelectOptionRows(e){let{rows:t,state:n,presentation:r,visible:i,start:a,end:o,cursor:s,visibleLabelWidth:c,theme:u}=e,d=u.colors;for(let e=a;e<o;e+=1){let f=i[e],p=e===s;r.layout===`task-list`&&e===o-1&&e>a&&t.push(``);let{option:m,stackedHint:h}=optionWithoutStackedHint(editableOption(f,p,r.edit,u),r.layout);t.push(` ${optionRow({option:m,isCursor:p,isChecked:r.selection===`multiple`&&n.select.selected.has(f.value),placeholder:optionUsesPlaceholder(r,e,i.length),hintPadding:c-f.label.length,theme:u})}`),h!==void 0&&t.push(` ${renderOptionRowContinuation(dimWithEmphasis(h,u))}`),f.description!==void 0&&(f.disabled===!0||p)&&t.push(` ${d.dim(f.description)}`),r.layout===`stacked`&&e<o-1&&t.push(``)}}function appendSubmitRow(e,t,n,r){if(n<0)return;let i=t===n,a=i?`${r.glyph.selectedPointer} ${r.colors.bold(`Submit`)}`:` Submit`;e.push(``,` ${renderCursorRow(a,i,r.colors)}`)}function appendSelectNotices(e,t,n,a,o){if(!(t===void 0||t.length===0)){e.push(``);for(let s of t){let t=toneGlyph(s.tone,a),c=` `.repeat(visibleLength(t)+1),l=Math.max(1,o-2-visibleLength(t)-1),u=wrapVisibleLine(s.text,l);for(let[r,i]of u.entries()){let o=noticeBody({...s,text:i},n,a);e.push(r===0?` ${t} ${o}`:` ${c}${o}`)}}}}function selectFooterHints(e,t,n){let r=[];return e.edit!==void 0&&t[n]?.value===e.edit.optionValue&&r.push(`type to rename`),e.filter!==void 0&&r.push(`type to filter`),r.push(`↑/↓ move`),r.push(e.selection===`multiple`?`space to toggle`:`enter to select`),e.selection===`multiple`&&r.push(`enter on Submit to confirm`),r.push(`esc to cancel`),r}function renderActionQuestion(e,t,n){let r=[` ${t.colors.dim(`${t.glyph.dot} ${e.context}`)}`,``];for(let[n,i]of e.actions.entries())r.push(` ${optionRow({option:i,isCursor:n===e.cursor,isChecked:!1,placeholder:!0,hintPadding:0,theme:t})}`);return r.push(...questionFooter([`↑/↓ move`,`enter to select`,`esc to cancel`],t)),r.map(e=>clip(e,n))}function renderSelectQuestion(e,t,n){if(e.kind===`actions`)return renderActionQuestion(e,t,n);let r=t.colors,i=selectPresentation(e),a=i.filter?filterOptions(e.options,e.select.filter):e.options,o=i.selection===`multiple`?submitRowIndex(a):-1,s=e.select.cursor,c=selectMessageRows(e.message,i.layout,t);i.filter!==void 0&&c.push(` ${searchFilter(e.select.filter,i.filter.placeholder,t)}`);let l=0;for(;a[l]?.featured;)l+=1;let u=selectViewSize({search:i.filter!==void 0,filter:e.select.filter,featuredLead:l,optionCount:a.length}),p=Math.max(0,Math.min(s-Math.floor(u/2),Math.max(0,a.length-u))),m=Math.min(p+u,a.length),h=a.slice(p,m).filter(e=>e.hint!==void 0||e.focusHint!==void 0).reduce((e,t)=>Math.max(e,t.label.length),0);return a.length===0&&c.push(` ${r.dim(`(no matches)`)}`),appendSelectOptionRows({rows:c,state:e,presentation:i,visible:a,start:p,end:m,cursor:s,visibleLabelWidth:h,theme:t}),appendSubmitRow(c,s,o,t),a.length>m-p&&c.push(` ${r.dim(`↑↓ ${a.length} options, showing ${p+1}–${m}`)}`),appendSelectNotices(c,e.notices,i.layout,t,n),e.error!==void 0&&c.push(``,` ${r.red(e.error)}`),c.push(...questionFooter(selectFooterHints(i,a,s),t)),c.map(e=>clip(e,n))}function renderTextQuestion(e,r,i,s){let c=r.colors,l=[];for(let t of e.notices??[]){let e=t.tone===`info`?c.dim(t.text):t.text;l.push(`${toneGlyph(t.tone,r)} ${e}`)}l.push(...e.message.split(`
3
+ `).map(e=>` ${c.bold(e)}`));let u=Math.max(4,i-4),d=e.mask?maskLine(e.editor):e.editor,f=e.editor.text.length===0?e.placeholder:void 0,p=renderInputWithBlockCursor({...visibleLine(f===void 0?d:{text:f,cursor:0},u,r.glyph.ellipsis),visible:s,inverse:c.inverse,render:f===void 0?renderInputText:e=>c.dim(renderInputText(e))});return l.push(` ${p}`),e.error!==void 0&&l.push(``,` ${c.red(e.error)}`),l.push(...questionFooter([`enter to submit`,`esc to cancel`],r)),l.map(e=>clip(e,i))}function renderAcknowledgeQuestion(e,t,n){let r=t.colors,i=[` ${r.bold(e.message)}`];if(e.lines.length>0){i.push(``);for(let t of e.lines)i.push(` ${r.dim(t)}`)}return i.push(...questionFooter([`enter to continue`],t)),i.map(e=>clip(e,n))}export{renderAcknowledgeQuestion,renderFlowPanel,renderSelectQuestion,renderTextQuestion};