setup-agent 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,7 +11,7 @@ npx setup-agent <url> ask --preset llm-provider --only openai,anthropic --defaul
11
11
  npx setup-agent <url> ask "Where should assistant-ui live?" --optional # keep working; the answer arrives on the stream
12
12
  npx setup-agent <url> ask "Which integrations?" --choices slack,email --icons "slack=message,email=mail" # multi-select; answers ["slack","email"]
13
13
  npx setup-agent <url> ask "Which package manager?" --choices pnpm,npm --icons "pnpm=package,npm=package" --single # the options exclude each other; answers "pnpm"
14
- npx setup-agent <url> wait q1 # block only when nothing else can proceed
14
+ npx setup-agent <url> wait q1 # block for the answer; after 100s it prints {"ok":false,"status":"waiting","next":...} to run again
15
15
  npx setup-agent <url> plan --file plan.md --wait # {"decision":"approve"} or {"decision":"revise","feedback":"..."}
16
16
  npx setup-agent <url> add-step "Install the packages" --detail "pnpm add ..." --active # prints {"stepId":"s1"}
17
17
  npx setup-agent <url> step s1 done --note "installed"
@@ -29,6 +29,10 @@ The URL is the browser's link to its agent, not one setup. A finished or cancell
29
29
 
30
30
  The browser sends the setup `id` and the displayed plan `revision` with each `checkout/plan` decision. The host rejects a mismatched setup or revision with `stale-plan`, so a queued decision cannot approve or revise a newer plan. Older clients may omit these fields and retain their existing behavior. The guard requires a host running this version of `setup-agent`.
31
31
 
32
+ ## Waiting
33
+
34
+ Nothing wakes a coding agent that ends its turn: the stream prints to a background process the agent only reads between commands. So the briefing tells the agent to run the setup to the end in one turn and to block on every answer with `wait <input-id>`, `plan --wait` or `ask --wait`. Those waits return after 100 seconds, under the two minutes Claude Code gives a shell command by default, with `{"ok":false,"status":"waiting","waitingFor":"q1","next":"<command>"}`; the agent runs `next` again. `--timeout <seconds>` sets another limit, and `0` waits as long as it takes. `wait start` has no limit by default, since waiting for the user's next setup is idle time. The agent ends its turn only to ask the user in its own conversation, and after `done`.
35
+
32
36
  ## Inputs
33
37
 
34
38
  Inputs include `text`, `choice`, `model` and `entry-point`, plus the legacy `product` kind. A choice carries `options` (`id`, `label`, `description`, `icon`, `variants`), an optional `default` and a `help` note the browser renders as a "help me choose" disclosure. Option ids are unique, and neither option nor variant ids may contain `:`. The answer is the option id, `<option>:<variant>` when the option has variants (`langgraph:python`), or the user's own text when none of the options fit (including any text with more than one `:`); the stream marks that answer `custom`. An agent's own choice is multi-select unless asked with `--single`: the user checks any number of options (none may have variants), and the answer is a JSON array of those entries, which `wait` and the stream print as an array and `status` holds as text. `--single` is for options that exclude each other (one framework, one model, yes or no) and answers one id. Every one of the agent's own options must carry an `icon` from the pack `OPTION_ICONS` exports (`--icons "slack=message,email=mail"`); the CLI refuses the question otherwise. A preset option carries a brand mark instead. Every answer may carry a `note` from the user. A model input lists providers as its options; the browser collects the provider, its API key, the model and an optional reasoning effort, and the answer is that object as JSON (`{"provider","model","reasoningEffort"?}`); the API key is deposited separately and only ever reaches the env file the agent names through `env`. Only the CLI that asked can take it: `ask` keeps a redeem key in a private file under the user's state directory, or a per-user directory under the temp directory, and refuses a model question when neither can hold it, so `env` runs as the same user on the same machine.
@@ -1 +1 @@
1
- {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE;QACP,iEAAiE;QACjE,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;QAC5B,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;QACnC,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH,CAAC;AAEF,6EAA6E;AAC7E,eAAO,MAAM,aAAa,EAAE,SAAS,WAAW,EAqG/C,CAAC;AA2CF,kGAAkG;AAClG,eAAO,MAAM,eAAe,GAC1B,MAAK,MAAM,CAAC,UAAwB,KACnC,MAAM,GAAG,SAaX,CAAC;AAEF,2GAA2G;AAC3G,eAAO,MAAM,kBAAkB,GAC7B,MAAK,MAAM,CAAC,UAAwB,EACpC,QAAO;IAAE,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAAkB,EACtD,SAAQ;IAAE,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAAmB,YAInC,CAAC;AAExB,6DAA6D;AAC7D,eAAO,MAAM,WAAW,GACtB,UAAU,SAAS,MAAM,EAAE,EAC3B,MAAK,MAAM,CAAC,UAAwB,KACnC,MAAM,GAAG,SAoBX,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,cAAc,GACzB,OAAO,WAAW,EAClB,QAAQ,MAAM,KACb,OAAO,CAAC,MAAM,GAAG,SAAS,CAgB5B,CAAC"}
1
+ {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE;QACP,iEAAiE;QACjE,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;QAC5B,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;QACnC,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH,CAAC;AAEF,6EAA6E;AAC7E,eAAO,MAAM,aAAa,EAAE,SAAS,WAAW,EAqG/C,CAAC;AA2CF,kGAAkG;AAClG,eAAO,MAAM,eAAe,SACrB,MAAM,CAAC,UAAU,KACrB,MAAM,GAAG,SAaX,CAAC;AAEF,2GAA2G;AAC3G,eAAO,MAAM,kBAAkB,SACxB,MAAM,CAAC,UAAU,UACf;IAAE,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAAE,WAC9B;IAAE,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAAE,YAIlB,CAAC;AAExB,6DAA6D;AAC7D,eAAO,MAAM,WAAW,aACZ,SAAS,MAAM,EAAE,QACtB,MAAM,CAAC,UAAU,KACrB,MAAM,GAAG,SAoBX,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,cAAc,UAClB,WAAW,UACV,MAAM,KACb,OAAO,CAAC,MAAM,GAAG,SAAS,CAgB5B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,mBAAgB;AAEzC,OAAO,KAAK,EAAE,QAAQ,EAAE,sBAAmB;AAE3C,oEAAoE;AACpE,eAAO,MAAM,uBAAuB,QAAS,CAAC;AAC9C,sGAAsG;AACtG,eAAO,MAAM,sBAAsB,QAA+B,CAAC;AAEnE;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG,iBAAiB,GAAG,gBAAgB,GAAG,YAAY,CAAC;AAE5E,MAAM,MAAM,iBAAiB,GACzB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,SAAS,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9E;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,GAAI,wBAI/B;IACD,0EAA0E;IAC1E,GAAG,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,KAAK,MAAM,GAAG,SAAS,CAAC;IACnD,+DAA+D;IAC/D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;oBAwCmB,QAAQ,CAAC,KAAK,KAAG,iBAAiB,EAAE;CAkEvD,CAAC"}
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAS,CAAC;AAEzC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAY,CAAC;AAE3C,oEAAoE;AACpE,eAAO,MAAM,uBAAuB,QAAS,CAAC;AAC9C,sGAAsG;AACtG,eAAO,MAAM,sBAAsB,QAA+B,CAAC;AAEnE;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG,iBAAiB,GAAG,gBAAgB,GAAG,YAAY,CAAC;AAE5E,MAAM,MAAM,iBAAiB,GACzB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,SAAS,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9E;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,2BAI3B;IACD,0EAA0E;IAC1E,GAAG,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,KAAK,MAAM,GAAG,SAAS,CAAC;IACnD,+DAA+D;IAC/D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;IAwCG,MAAM,UAAU,QAAQ,CAAC,KAAK,KAAG,iBAAiB,EAAE;CAkEvD,CAAC"}
package/dist/cli.d.ts CHANGED
@@ -28,7 +28,7 @@ export type AgentInstructionsOptions = {
28
28
  };
29
29
  };
30
30
  export declare const agentInstructions: (setupUrl: string, options?: AgentInstructionsOptions) => string;
31
- export declare const INSTRUCTIONS: (setupUrl: string, options?: AgentInstructionsOptions) => string;
31
+ export declare const INSTRUCTIONS: typeof agentInstructions;
32
32
  type Parsed = {
33
33
  positional: string[];
34
34
  flags: Map<string, string | true>;
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,oBAAiB;AA2B/C,OAAO,KAAK,EAAE,QAAQ,EAAE,sBAAmB;AAI3C,OAAO,EAAE,UAAU,EAAE,oBAAiB;AAItC,eAAO,MAAM,IAAI,QAoChB,CAAC;AAMF;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GAAI,OAAO,MAAM,KAAG,MA6B7C,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,8FAA8F;IAC9F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,cAAc,CAAC,EAAE;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,8FAA8F;QAC9F,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB;;;WAGG;QACH,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB,CAAC;CACH,CAAC;AAuCF,eAAO,MAAM,iBAAiB,GAC5B,UAAU,MAAM,EAChB,UAAS,wBAA6B,WA6CvC,CAAC;AAEF,eAAO,MAAM,YAAY,aAhDb,MAAM,YACP,wBAAwB,WA+CU,CAAC;AAE9C,KAAK,MAAM,GAAG;IAAE,UAAU,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAA;CAAE,CAAC;AAuB1E,OAAO,EACL,aAAa,EACb,eAAe,EACf,WAAW,EACX,kBAAkB,EAClB,cAAc,GACf,oBAAiB;AAClB,YAAY,EAAE,WAAW,EAAE,oBAAiB;AAuG5C,2LAA2L;AAC3L,eAAO,MAAM,YAAY,GACvB,QAAQ,IAAI,CAAC,cAAc,EAAE,OAAO,GAAG,WAAW,CAAC,6BAiBjD,CAAC;AAwDL,eAAO,MAAM,OAAO,GAClB,MAAM,SAAS,MAAM,EAAE,EACvB,OAAO,MAAM,CAAC,OAAO,CAAC,KACrB,QAAQ,CAAC,SAAS,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CA+HxC,CAAC;AAQF,+EAA+E;AAC/E,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,EAAE,KAAK,MAAM,EAAE,OAAO,MAAM,WAqBxE,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GACzB,MAAM,MAAM,EACZ,KAAK,MAAM,EACX,MAAM,MAAM,OAAO,CAAC,MAAM,CAAC,EAC3B,oBAA2B,kBAsD5B,CAAC;AAgHF,MAAM,MAAM,sBAAsB,GAAG;IACnC,0FAA0F;IAC1F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qGAAqG;IACrG,OAAO,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CACtD,CAAC;AAkCF,eAAO,MAAM,IAAI,GAAI,MAAM,SAAS,MAAM,EAAE,kBAA0B,CAAC;AAEvE;;;GAGG;AACH,eAAO,MAAM,eAAe,GAC1B,MAAM,SAAS,MAAM,EAAE,EACvB,UAAS,sBAA2B,kBAwRrC,CAAC"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAU,CAAC;AA2B/C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAY,CAAC;AAI3C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAU,CAAC;AAatC,eAAO,MAAM,IAAI,QAuChB,CAAC;AAMF;;;;;GAKG;AACH,eAAO,MAAM,aAAa,UAAW,MAAM,KAAG,MA6B7C,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,8FAA8F;IAC9F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,cAAc,CAAC,EAAE;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,8FAA8F;QAC9F,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB;;;WAGG;QACH,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB,CAAC;CACH,CAAC;AA6CF,eAAO,MAAM,iBAAiB,aAClB,MAAM,YACP,wBAAwB,WA+ClC,CAAC;AAEF,eAAO,MAAM,YAAY,0BAAoB,CAAC;AAE9C,KAAK,MAAM,GAAG;IAAE,UAAU,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAA;CAAE,CAAC;AAuB1E,OAAO,EACL,aAAa,EACb,eAAe,EACf,WAAW,EACX,kBAAkB,EAClB,cAAc,GACf,MAAM,aAAU,CAAC;AAClB,YAAY,EAAE,WAAW,EAAE,MAAM,aAAU,CAAC;AAuG5C,2LAA2L;AAC3L,eAAO,MAAM,YAAY,WACf,IAAI,CAAC,cAAc,EAAE,OAAO,GAAG,WAAW,CAAC,6BAiBjD,CAAC;AAiGL,eAAO,MAAM,OAAO,SACZ,SAAS,MAAM,EAAE,SAChB,MAAM,CAAC,OAAO,CAAC,KACrB,QAAQ,CAAC,SAAS,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CA+HxC,CAAC;AAQF,+EAA+E;AAC/E,eAAO,MAAM,aAAa,YAAa,MAAM,OAAO,MAAM,SAAS,MAAM,WAqBxE,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,cAAc,SACnB,MAAM,OACP,MAAM,QACL,MAAM,OAAO,CAAC,MAAM,CAAC,wCAuD5B,CAAC;AAgHF,MAAM,MAAM,sBAAsB,GAAG;IACnC,0FAA0F;IAC1F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qGAAqG;IACrG,OAAO,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CACtD,CAAC;AAkCF,eAAO,MAAM,IAAI,SAAU,SAAS,MAAM,EAAE,kBAA0B,CAAC;AAEvE;;;GAGG;AACH,eAAO,MAAM,eAAe,SACpB,SAAS,MAAM,EAAE,YACd,sBAAsB,kBA4ThC,CAAC"}
package/dist/cli.js CHANGED
@@ -12,6 +12,14 @@ import { homedir, tmpdir, userInfo } from "node:os";
12
12
  import { createInterface } from "node:readline/promises";
13
13
  // src/cli.ts
14
14
  const DEFAULT_COMMAND = "npx setup-agent";
15
+ /**
16
+ * How long "wait <input-id>", "wait plan" and "--wait" block before handing
17
+ * control back with a "waiting" result, in seconds. Agent shells kill a
18
+ * command after a limit of their own (two minutes by default in Claude
19
+ * Code); a wait that returns first lets the agent run it again instead of
20
+ * reading the kill as the end of the setup.
21
+ */
22
+ const DEFAULT_WAIT_SECONDS = 100;
15
23
  const HELP = `setup-agent <url> [command]
16
24
 
17
25
  Commands (the url always comes first):
@@ -36,10 +44,13 @@ Commands (the url always comes first):
36
44
  ask <prompt> --entry-points '<JSON array>' [--default <id>] [--optional] [--step <step-id>] [--wait]
37
45
  Ask where users should access the assistant. Each of 1–3 options has id, label, description and entryPoint {formFactor: modal|sidebar|full-page, placement, trigger, recommended?}. Answers one option id.
38
46
  wait start Block until the user clicks Begin plan in their browser; after a finished or cancelled setup, until the next one begins.
39
- wait <input-id> Block until the user answers or dismisses an input. A multi-select answer prints as an array.
40
- plan (<markdown> | --file <path>) [--wait]
47
+ wait <input-id> [--timeout <seconds>]
48
+ Block until the user answers or dismisses an input. A multi-select answer prints as an array.
49
+ plan (<markdown> | --file <path>) [--wait] [--timeout <seconds>]
41
50
  Propose the plan for the user to approve. --wait blocks until they decide.
42
- wait plan Block until the user approves the plan or asks for changes.
51
+ wait plan [--timeout <seconds>] Block until the user approves the plan or asks for changes.
52
+ wait <input-id>, wait plan and every --wait give up after ${DEFAULT_WAIT_SECONDS}s (--timeout, 0 for no limit) and print
53
+ {"ok":false,"status":"waiting","next":"<command>"}: run "next" again to keep waiting.
43
54
  env <input-id> <ENV_VAR> [--file <path>]
44
55
  Write the API key behind an answered llm-provider input to an env file (default .env.local) as ENV_VAR=<key>, without printing it.
45
56
  add-step <title> [--detail <text>] [--product <slug>] [--active]
@@ -76,14 +87,18 @@ const beginPlanHandoff = (cmd, url) => `Before planning, check the current setup
76
87
  ${cmd} ${url} wait start
77
88
  Keep the stream running while you wait. Do not inspect the project, fetch guides, ask setup questions, or propose a plan until this command returns {"ok":true}. If it returns {"ok":false,"status":"cancelled"}, the user ended this setup before planning: do not plan, tell the user you are connected and waiting for their next setup, and run "wait start" again. Only the user in the browser should start planning; never invoke the browser's start command yourself. "planning.started" also arrives on the stream. If the status is already "planning" or "installing", resume that phase without asking the user to begin again. If it is "done" or "cancelled", that setup is over: tell the user you are connected and waiting for their next setup, and run "wait start", which returns once the next one begins.`;
78
89
  const agentStartedHandoff = ({ browserUrl, opened = true, streaming = false }) => streaming ? `You started this setup, and it is already planning: there is no Begin plan click to wait for. Check "status" before planning: if it is "planning" or "installing", go on with that phase. If the status is "done" or "cancelled", that setup is over: tell the user you are connected and waiting for their next setup, and run "wait start", which returns once the next one begins.` : `You started this setup, and it is already planning: there is no Begin plan click to wait for. The browser asks the user to connect to you first; it shows your working directory once your stream is running. Check "status" before planning: if it is "planning" or "installing", go on with that phase. Tell the user in your own conversation, once: ${opened ? `"I opened the assistant-ui setup in your browser. I'll ask my questions there, and you can watch my progress and steer me from that page. If it didn't open, go to ${browserUrl} ."` : `"Open ${browserUrl} to follow the assistant-ui setup. I'll ask my questions there, and you can watch my progress and steer me from that page."`} If the status is "done" or "cancelled", that setup is over: tell the user you are connected and waiting for their next setup, and run "wait start", which returns once the next one begins.`;
79
- const streamSection = (cmd, url, streaming, noOpen) => streaming ? `The command that printed this is the stream. Keep it running in the background until the setup ends: the browser shows you as connected only while it runs, and it is how answers reach you. Each new line of its output is a JSON array of events: ${EVENT_LIST}, plus:
80
- - "browser.opened" ({id, message}): setup opened the setup page in the user's browser for a question. Tell the user once, in your own conversation, that it waits there.
81
- - "input.needed" ({id, message, question, options?, answer}): the user may not see the browser right now. Do what its message says: wait for "input.answered", or ask the user in your own conversation with that question and options, then run its "answer" command with their reply. Answered either way, the setup goes on.
90
+ const streamSection = (cmd, url, streaming, noOpen) => streaming ? `The command that printed this is the stream. Keep it running in the background until the setup ends: the browser shows you as connected only while it runs. New output does not wake you: read its new lines between your other commands. Each new line is a JSON array of events: ${EVENT_LIST}, plus:
91
+ - "browser.opened" ({id, message}): setup opened the setup page in the user's browser for a question. Tell the user once, in your own conversation, that it waits there, and keep working in the same turn.
92
+ - "input.needed" ({id, message, question, options?, answer}): the user may not see the browser right now. Do what its message says: run "wait <id>" for their answer in the browser, or ask the user in your own conversation with that question and options, then run its "answer" command with their reply. Answered either way, the setup goes on.
82
93
  If this command stops, start it again in the background with
83
94
  ${cmd} ${url} stream${noOpen ? " --no-open" : ""}
84
95
  The URL is the user's link to you from their browser, not one setup: it outlives this one, and their next setup arrives on the same stream.` : `First, start a persistent Monitor on
85
96
  ${cmd} ${url} stream
86
97
  and keep it running: the browser shows you as connected only while it runs, and it is how answers reach you. The URL is the user's link to you from their browser, not one setup: it outlives this one, and their next setup arrives on the same stream. Each line is a JSON array of events: ${EVENT_LIST}.`;
98
+ /** Why the agent must not end its turn mid-setup, and how it waits instead. */
99
+ const keepGoing = (cmd, url) => `Run the setup to the end in one go. Do not count on the stream or the browser to wake you: if you end your turn before the install is done, the setup can stay paused until the user types to you, even after they answer in the browser. So never end your turn to wait for an answer, and do not stop after telling the user something. To get an answer, block on it:
100
+ ${cmd} ${url} wait <input-id>
101
+ ("plan --wait" and "ask --wait" block the same way). A wait that prints {"ok":false,"status":"waiting"} has not failed and the setup is not stuck: run its "next" command, right away. "next" only waits; never repeat an "ask" or "plan" to keep waiting, since that asks or submits again. End your turn only to ask the user in your own conversation (an "input.needed" you relay, or a value only they can give), and go on as soon as they reply; or once the install is done and proposed with "done".`;
87
102
  const agentInstructions = (setupUrl, options = {}) => {
88
103
  const url = shellWord(setupUrl);
89
104
  const cmd = options.command ?? DEFAULT_COMMAND;
@@ -93,6 +108,8 @@ const agentInstructions = (setupUrl, options = {}) => {
93
108
 
94
109
  ${streamSection(cmd, url, streaming, started?.opened === false)}
95
110
 
111
+ ${keepGoing(cmd, url)}
112
+
96
113
  The user can message you at any time, including while a question is waiting. Read "message.created" events, acknowledge each with "${cmd} ${url} ack <message-id>", and reply using "log" with what you will do. Pending messages are replayed when the stream starts and listed in "status". A message is not an answer or plan approval; keep those decisions explicit.
97
114
 
98
115
  "log" is a message to the user, and the user reads every one: send it only to reply to their message, to tell them something no question or step conveys (a blocker, a deviation from the plan, a decision you made for them), or once as a wrap-up when the install is finished. Findings go in the plan, progress in step notes, questions in "ask"; do not narrate.
@@ -110,10 +127,10 @@ The setup's products are starting names and guide references. If the setup lists
110
127
  When the setup puts an assistant in the app, ask how users should access it after discovering the actual app and before proposing the plan. Skip this question when no product's guide installs an assistant (a standalone library or service setup, for example). Use an entry-point question with only 1–3 sensible alternatives. Describe the actual app's pages, workflow and component placement, not generic placeholder copy. Recommend one clear fit when there is one; two options may use the same form factor in different places. Each option needs a stable id, label, contextual description and entryPoint {"formFactor":"modal"|"sidebar"|"full-page","placement":"where it appears","trigger":"how users open it or navigate to it","recommended":true?}. Write the JSON array for this app and send it with:
111
128
  ${cmd} ${url} ask "Where should users access the assistant?" --entry-points '<JSON array>' --wait
112
129
  Mark a clear fit with "recommended":true rather than --default, so the user makes the pick. When you ask it, do not submit the plan before the user picks an entry point. Respect the selected placement and trigger in the plan and implementation. Do not ask for a new-versus-existing app setting; discover the app from its files and ask only for an install path when needed.
113
- Skip a preset the code already answers (a chat route on the AI SDK, a provider key in .env.local) and log why. Ask everything at once, keep investigating, and block with "wait <input-id>" only when nothing else can proceed. A framework answer is "<framework>:<language>", for example "ai-sdk:typescript". A model answer is JSON {"provider","model","reasoningEffort"?}. An answer may carry a "note" from the user; follow it. A choice answer that matches none of the options is the user's own text.
130
+ Skip a preset the code already answers (a chat route on the AI SDK, a provider key in .env.local) and log why. Ask everything at once, keep investigating, and then run "wait <input-id>" for each open question before you write the plan; do not end your turn while they are open. A framework answer is "<framework>:<language>", for example "ai-sdk:typescript". A model answer is JSON {"provider","model","reasoningEffort"?}. An answer may carry a "note" from the user; follow it. A choice answer that matches none of the options is the user's own text.
114
131
  Then write the plan as markdown and submit it:
115
132
  ${cmd} ${url} plan --file <path> --wait
116
- Use these headings, each with bullets: "## What I found" (facts as \`- **Label:** value\`: app framework, package manager, agent framework, model provider and model, where components live), "## What I will install" (packages and files, one per bullet), "## Steps" (a numbered list, one line each), and "## Open questions" only if any remain. Keep it under 30 lines; no preamble. "plan --wait" prints {"decision":"approve"} or {"decision":"revise","feedback":"..."}. On revise, address the feedback (ask again if you need to) and submit a new plan; repeat until approved. Do not change the project before approval: the setup refuses steps until then.
133
+ Use these headings, each with bullets: "## What I found" (facts as \`- **Label:** value\`: app framework, package manager, agent framework, model provider and model, where components live), "## What I will install" (packages and files, one per bullet), "## Steps" (a numbered list, one line each), and "## Open questions" only if any remain. Keep it under 30 lines; no preamble. "plan --wait" prints {"decision":"approve"} or {"decision":"revise","feedback":"..."}; when it prints {"ok":false,"status":"waiting"}, the plan is submitted and still with the user, so keep waiting with "wait plan" and do not submit it again. On revise, address the feedback (ask again if you need to) and submit a new plan; repeat until approved. Do not change the project before approval: the setup refuses steps until then.
117
134
 
118
135
  2. Install. Declare the steps from your plan up front, in order:
119
136
  ${cmd} ${url} add-step "<title>" --detail "<one line>"
@@ -263,6 +280,35 @@ const waitForPlan = (client, revision) => new Promise((resolve) => {
263
280
  const unsubscribe = client.subscribe(check);
264
281
  check();
265
282
  });
283
+ /** The seconds `--timeout` asks a wait to block for; 0 is no limit. */
284
+ const waitSeconds = (flags, fallback) => {
285
+ const value = flagText(flags, "timeout");
286
+ if (value === void 0) return fallback;
287
+ const seconds = Number(value);
288
+ if (!/^\d+(?:\.\d+)?$/.test(value) || !Number.isFinite(seconds)) return fail("--timeout takes a number of seconds, 0 for no limit");
289
+ return seconds;
290
+ };
291
+ const TIMED_OUT = Symbol("timed out");
292
+ /** `promise`, or `TIMED_OUT` once `seconds` pass first; 0 waits as long as it takes. */
293
+ const within = async (promise, seconds) => {
294
+ if (seconds === 0) return promise;
295
+ let timer;
296
+ try {
297
+ return await Promise.race([promise, new Promise((resolve) => {
298
+ timer = setTimeout(() => resolve(TIMED_OUT), seconds * 1e3);
299
+ })]);
300
+ } finally {
301
+ clearTimeout(timer);
302
+ }
303
+ };
304
+ /** What a wait prints when the user has not decided yet: the command to run again, now. */
305
+ const stillWaiting = (target, next) => ({
306
+ ok: false,
307
+ status: "waiting",
308
+ waitingFor: target,
309
+ next,
310
+ message: `The user has not answered yet. Run "next" now to keep waiting (it only waits; do not repeat the command you ran, which would ask or submit again), or go on with other work and run it when you need the answer. Do not end your turn: nothing resumes you until you run it.`
311
+ });
266
312
  const decided = (plan, state) => {
267
313
  if (state.status === "cancelled") return {
268
314
  ok: false,
@@ -547,6 +593,7 @@ const runSetupCommand = async (argv, options = {}) => {
547
593
  if (command === "intro" && isDirectInvocation()) return launchAgent(url);
548
594
  const client = await connectCheckout(url).catch((error) => fail(error instanceof Error ? error.message : String(error)));
549
595
  const commands = client.commands;
596
+ const waitCommand = (target) => `${options.command ?? DEFAULT_COMMAND} ${shellWord(url)} wait ${target}`;
550
597
  try {
551
598
  switch (command) {
552
599
  case "intro": {
@@ -619,8 +666,8 @@ const runSetupCommand = async (argv, options = {}) => {
619
666
  });
620
667
  break;
621
668
  }
622
- const plan = await waitForPlan(client, revision);
623
- print(decided(plan, requireState(client)));
669
+ const plan = await within(waitForPlan(client, revision), waitSeconds(flags, DEFAULT_WAIT_SECONDS));
670
+ print(plan === TIMED_OUT ? stillWaiting("plan", waitCommand("plan")) : decided(plan, requireState(client)));
624
671
  break;
625
672
  }
626
673
  case "ask": {
@@ -645,14 +692,19 @@ const runSetupCommand = async (argv, options = {}) => {
645
692
  });
646
693
  break;
647
694
  }
648
- print(await waitForInput(client, inputId).then(answered));
695
+ const input = await within(waitForInput(client, inputId), waitSeconds(flags, DEFAULT_WAIT_SECONDS));
696
+ print(input === TIMED_OUT ? stillWaiting(inputId, waitCommand(inputId)) : answered(input));
649
697
  break;
650
698
  }
651
699
  case "wait": {
652
700
  const target = rest[0] ?? fail("usage: wait start | wait <input-id> | wait plan");
653
701
  const state = requireState(client);
654
702
  if (target === "start") {
655
- const status = await waitForStart(client);
703
+ const status = await within(waitForStart(client), waitSeconds(flags, 0));
704
+ if (status === TIMED_OUT) {
705
+ print(stillWaiting("start", waitCommand("start")));
706
+ break;
707
+ }
656
708
  print({
657
709
  ok: status === "planning" || status === "installing",
658
710
  status
@@ -661,11 +713,13 @@ const runSetupCommand = async (argv, options = {}) => {
661
713
  }
662
714
  if (target === "plan") {
663
715
  const plan = currentPlan(state) ?? fail("no plan has been proposed");
664
- print(decided(await waitForPlan(client, plan.revision), requireState(client)));
716
+ const decision = await within(waitForPlan(client, plan.revision), waitSeconds(flags, DEFAULT_WAIT_SECONDS));
717
+ print(decision === TIMED_OUT ? stillWaiting("plan", waitCommand("plan")) : decided(decision, requireState(client)));
665
718
  break;
666
719
  }
667
720
  if (!state.inputs.some((input) => input.id === target)) fail(`no input "${target}"`);
668
- print(await waitForInput(client, target).then(answered));
721
+ const input = await within(waitForInput(client, target), waitSeconds(flags, DEFAULT_WAIT_SECONDS));
722
+ print(input === TIMED_OUT ? stillWaiting(target, waitCommand(target)) : answered(input));
669
723
  break;
670
724
  }
671
725
  case "env": {
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["import {\n CODING_AGENTS,\n detectAgentKind,\n isDirectInvocation,\n runCodingAgent,\n} from \"./agents\";\nimport type { CodingAgent } from \"./agents\";\nimport { connectCheckout } from \"./client\";\nimport type { CheckoutClient } from \"./client\";\nimport { INPUT_PRESETS, isPresetId, presetInput } from \"./presets\";\nimport { createHash, randomBytes } from \"node:crypto\";\nimport { constants } from \"node:fs\";\nimport {\n chmod,\n lstat,\n mkdir,\n open,\n readFile,\n realpath,\n unlink,\n} from \"node:fs/promises\";\nimport { homedir, tmpdir, userInfo } from \"node:os\";\nimport { createInterface } from \"node:readline/promises\";\nimport { dirname, isAbsolute, join, relative, resolve, sep } from \"node:path\";\nimport {\n OPTION_ICONS,\n currentPlan,\n isAgentPresent,\n isClosed,\n isLoopbackHostname,\n isOptionIcon,\n isValidModelAnswer,\n parseEntryPointOptions,\n stepProgress,\n} from \"./protocol\";\nimport type { Checkout } from \"./protocol\";\nimport { answerNeed } from \"./needs\";\nimport { answerFields, followSetup, shellWord } from \"./stream\";\n\nexport { diffEvents } from \"./stream\";\n\nconst DEFAULT_COMMAND = \"npx setup-agent\";\n\nexport const HELP = `setup-agent <url> [command]\n\nCommands (the url always comes first):\n status Print the setup as JSON.\n stream [--setup-app-url <url>] [--no-open]\n Follow the setup: one JSON array of events per line. With --setup-app-url, open the setup page when the setup waits on the user.\n answer <input-id> <answer> [--note <text>] [--dismiss]\n answer plan approve | answer plan revise --feedback <text> | answer finish close\n Answer for the user what they told you in your own conversation, as an \"input.needed\" event's \"answer\" shows.\n log <text> Send a message to the user: a reply, something no question or step conveys, or one wrap-up when finished.\n ack <message-id> Acknowledge a user message.\n ask <prompt> [--placeholder <text>] [--optional] [--step <step-id>] [--wait]\n Ask the user for a line of text.\n ask [prompt] --preset <framework|llm-provider> [--only <id,id>] [--choices <id=description,...>] [--default <id>] [--optional] [--step <step-id>] [--wait]\n Ask a standard question. --only restricts the options and leaves out --choices (one id locks it in); --default preselects one.\n \"framework\" answers \"<framework>:<language>\"; \"llm-provider\" answers JSON with provider, model and reasoningEffort; the key itself arrives through \"env\".\n ask <prompt> --choices <id,id,...> --icons <id=icon,...> [--single] [--default <id>] [--optional] [--step <step-id>] [--wait]\n Ask the user to pick from your own options; they may also type their own answer.\n The user checks any number of options and the answer is a JSON array of option ids (their own text is one entry).\n --single is for options that exclude each other (one framework, one model, yes or no); the answer is then one id.\n --icons is required and names an icon for every option, from: ${OPTION_ICONS.join(\", \")}.\n ask <prompt> --entry-points '<JSON array>' [--default <id>] [--optional] [--step <step-id>] [--wait]\n Ask where users should access the assistant. Each of 1–3 options has id, label, description and entryPoint {formFactor: modal|sidebar|full-page, placement, trigger, recommended?}. Answers one option id.\n wait start Block until the user clicks Begin plan in their browser; after a finished or cancelled setup, until the next one begins.\n wait <input-id> Block until the user answers or dismisses an input. A multi-select answer prints as an array.\n plan (<markdown> | --file <path>) [--wait]\n Propose the plan for the user to approve. --wait blocks until they decide.\n wait plan Block until the user approves the plan or asks for changes.\n env <input-id> <ENV_VAR> [--file <path>]\n Write the API key behind an answered llm-provider input to an env file (default .env.local) as ENV_VAR=<key>, without printing it.\n add-step <title> [--detail <text>] [--product <slug>] [--active]\n Declare a step of the install (after the plan is approved). Prints its id.\n step <step-id> <status> [--note <text>]\n Set a step to pending|active|done|skipped|blocked.\n done [--summary <text>] [--preview <url>]\n Mark every remaining step done and propose closing; the user closes it.\n`;\n\n/** Characters a setup URL may hold: an origin and a path, nothing a shell or a reader treats as more than an address. */\nconst SETUP_URL_PATTERN =\n /^https?:\\/\\/[A-Za-z0-9.-]+(?::[0-9]+)?(?:\\/[A-Za-z0-9._~%/-]*)?$|^http:\\/\\/\\[::1\\](?::[0-9]+)?(?:\\/[A-Za-z0-9._~%/-]*)?$/;\n\n/**\n * The normalized setup URL, or an error saying why it is refused. A setup URL\n * is printed into shell commands and the coding agent's prompt, and the API\n * key travels over it, so it must be https (http only on this machine), carry\n * no credentials, query or fragment, and hold only address characters.\n */\nexport const parseSetupUrl = (value: string): string => {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n throw new Error(`\"${value}\" is not a URL`);\n }\n if (\n url.protocol !== \"https:\" &&\n !(url.protocol === \"http:\" && isLoopbackHostname(url.hostname))\n ) {\n throw new Error(\"the setup URL must use https (http only on localhost)\");\n }\n if (url.username !== \"\" || url.password !== \"\") {\n throw new Error(\"the setup URL must not carry credentials\");\n }\n if (\n url.search !== \"\" ||\n url.hash !== \"\" ||\n value.includes(\"?\") ||\n value.includes(\"#\")\n ) {\n throw new Error(\"the setup URL must not have a query or fragment\");\n }\n const href = url.href.replace(/\\/$/, \"\");\n if (!SETUP_URL_PATTERN.test(href)) {\n throw new Error(\"the setup URL has characters a setup URL does not use\");\n }\n return href;\n};\n\nexport type AgentInstructionsOptions = {\n /** How the agent invokes the setup commands, before the URL; `npx setup-agent` by default. */\n command?: string;\n /**\n * The agent started this setup with `assistant-ui setup`, which already\n * began planning; there is no Begin plan click to wait for.\n */\n startedByAgent?: {\n browserUrl: string;\n /** False when the browser was not opened for the user; the agent then hands them the link. */\n opened?: boolean;\n /**\n * The command that printed this briefing is the stream: it keeps running\n * and opens the browser only when the setup waits on the user.\n */\n streaming?: boolean;\n };\n};\n\nconst EVENT_LIST = `\"planning.started\", \"input.answered\" ({inputId, answer, note?}; answer is an array for a choice question not asked with --single), \"input.dismissed\", \"plan.approved\", \"plan.changes-requested\" ({feedback}), \"message.created\" ({messageId, text}), \"finished\", \"cancelled\", \"setup.created\" ({products})`;\n\nconst beginPlanHandoff = (cmd: string, url: string) =>\n `Before planning, check the current setup status. If it is \"waiting\", tell the user in your own conversation: \"I'm connected. Go back to your browser and click Begin plan. You can watch my progress, answer questions, and steer me there.\" Then run:\n ${cmd} ${url} wait start\nKeep the stream running while you wait. Do not inspect the project, fetch guides, ask setup questions, or propose a plan until this command returns {\"ok\":true}. If it returns {\"ok\":false,\"status\":\"cancelled\"}, the user ended this setup before planning: do not plan, tell the user you are connected and waiting for their next setup, and run \"wait start\" again. Only the user in the browser should start planning; never invoke the browser's start command yourself. \"planning.started\" also arrives on the stream. If the status is already \"planning\" or \"installing\", resume that phase without asking the user to begin again. If it is \"done\" or \"cancelled\", that setup is over: tell the user you are connected and waiting for their next setup, and run \"wait start\", which returns once the next one begins.`;\n\nconst agentStartedHandoff = ({\n browserUrl,\n opened = true,\n streaming = false,\n}: NonNullable<AgentInstructionsOptions[\"startedByAgent\"]>) =>\n streaming\n ? `You started this setup, and it is already planning: there is no Begin plan click to wait for. Check \"status\" before planning: if it is \"planning\" or \"installing\", go on with that phase. If the status is \"done\" or \"cancelled\", that setup is over: tell the user you are connected and waiting for their next setup, and run \"wait start\", which returns once the next one begins.`\n : `You started this setup, and it is already planning: there is no Begin plan click to wait for. The browser asks the user to connect to you first; it shows your working directory once your stream is running. Check \"status\" before planning: if it is \"planning\" or \"installing\", go on with that phase. Tell the user in your own conversation, once: ${\n opened\n ? `\"I opened the assistant-ui setup in your browser. I'll ask my questions there, and you can watch my progress and steer me from that page. If it didn't open, go to ${browserUrl} .\"`\n : `\"Open ${browserUrl} to follow the assistant-ui setup. I'll ask my questions there, and you can watch my progress and steer me from that page.\"`\n } If the status is \"done\" or \"cancelled\", that setup is over: tell the user you are connected and waiting for their next setup, and run \"wait start\", which returns once the next one begins.`;\n\nconst streamSection = (\n cmd: string,\n url: string,\n streaming: boolean,\n noOpen: boolean,\n) =>\n streaming\n ? `The command that printed this is the stream. Keep it running in the background until the setup ends: the browser shows you as connected only while it runs, and it is how answers reach you. Each new line of its output is a JSON array of events: ${EVENT_LIST}, plus:\n- \"browser.opened\" ({id, message}): setup opened the setup page in the user's browser for a question. Tell the user once, in your own conversation, that it waits there.\n- \"input.needed\" ({id, message, question, options?, answer}): the user may not see the browser right now. Do what its message says: wait for \"input.answered\", or ask the user in your own conversation with that question and options, then run its \"answer\" command with their reply. Answered either way, the setup goes on.\nIf this command stops, start it again in the background with\n ${cmd} ${url} stream${noOpen ? \" --no-open\" : \"\"}\nThe URL is the user's link to you from their browser, not one setup: it outlives this one, and their next setup arrives on the same stream.`\n : `First, start a persistent Monitor on\n ${cmd} ${url} stream\nand keep it running: the browser shows you as connected only while it runs, and it is how answers reach you. The URL is the user's link to you from their browser, not one setup: it outlives this one, and their next setup arrives on the same stream. Each line is a JSON array of events: ${EVENT_LIST}.`;\n\nexport const agentInstructions = (\n setupUrl: string,\n options: AgentInstructionsOptions = {},\n) => {\n const url = shellWord(setupUrl);\n const cmd = options.command ?? DEFAULT_COMMAND;\n const started = options.startedByAgent;\n const streaming = started?.streaming === true;\n return `You are the coding agent for a setup ${started ? (streaming ? (started.opened === false ? \"you started; setup opens no browser (--no-open), so the user follows it on the setup page if they open it, or answers you in your own conversation\" : \"you started; the user follows it in their browser, which setup opens when you ask the user something\") : \"you opened in the user's browser\") : \"the user opened in their browser\"}. The browser shows this setup live; every command below updates it. The user watches the browser, not this terminal: once setup begins, send questions through \"ask\" so the user can answer in their browser.\n\n${streamSection(cmd, url, streaming, started?.opened === false)}\n\nThe user can message you at any time, including while a question is waiting. Read \"message.created\" events, acknowledge each with \"${cmd} ${url} ack <message-id>\", and reply using \"log\" with what you will do. Pending messages are replayed when the stream starts and listed in \"status\". A message is not an answer or plan approval; keep those decisions explicit.\n\n\"log\" is a message to the user, and the user reads every one: send it only to reply to their message, to tell them something no question or step conveys (a blocker, a deviation from the plan, a decision you made for them), or once as a wrap-up when the install is finished. Findings go in the plan, progress in step notes, questions in \"ask\"; do not narrate.\n\n${started ? agentStartedHandoff(started) : beginPlanHandoff(cmd, url)}\n\nThe remaining phases are plan, then install.\n\n1. Plan. Investigate only; change nothing yet. If the current directory is not an app to install into (a monorepo root, an empty folder, a library), ask where to install first:\n ${cmd} ${url} ask \"Which project should I install into?\" --placeholder \"An absolute path to the app\" --wait\nRead the project: its app framework and package manager, the agent framework and model provider it already uses, where components live, any existing chat route. Fetch each product's \"guide\" URL from the setup below; it is the install reference. Ask for what the project does not decide, using the standard questions so the browser can render them well:\n ${cmd} ${url} ask --preset framework\n ${cmd} ${url} ask --preset llm-provider\nThe setup's products are starting names and guide references. If the setup lists no products, first ask the user what to set up with a choice question whose options are slugs from https://www.assistant-ui.com/catalog.md, then fetch https://www.assistant-ui.com/install.md?items=<slugs> as the guide. Explore each product's setup instructions, then discover and plan any other relevant products or dependencies the app needs. Read /catalog.md on the guide URL’s origin to find related products and fetch their setup guides. The starting list does not restrict the implementation or require adding another product to the list. Include relevant additions in the plan for approval.\nWhen the setup puts an assistant in the app, ask how users should access it after discovering the actual app and before proposing the plan. Skip this question when no product's guide installs an assistant (a standalone library or service setup, for example). Use an entry-point question with only 1–3 sensible alternatives. Describe the actual app's pages, workflow and component placement, not generic placeholder copy. Recommend one clear fit when there is one; two options may use the same form factor in different places. Each option needs a stable id, label, contextual description and entryPoint {\"formFactor\":\"modal\"|\"sidebar\"|\"full-page\",\"placement\":\"where it appears\",\"trigger\":\"how users open it or navigate to it\",\"recommended\":true?}. Write the JSON array for this app and send it with:\n ${cmd} ${url} ask \"Where should users access the assistant?\" --entry-points '<JSON array>' --wait\nMark a clear fit with \"recommended\":true rather than --default, so the user makes the pick. When you ask it, do not submit the plan before the user picks an entry point. Respect the selected placement and trigger in the plan and implementation. Do not ask for a new-versus-existing app setting; discover the app from its files and ask only for an install path when needed.\nSkip a preset the code already answers (a chat route on the AI SDK, a provider key in .env.local) and log why. Ask everything at once, keep investigating, and block with \"wait <input-id>\" only when nothing else can proceed. A framework answer is \"<framework>:<language>\", for example \"ai-sdk:typescript\". A model answer is JSON {\"provider\",\"model\",\"reasoningEffort\"?}. An answer may carry a \"note\" from the user; follow it. A choice answer that matches none of the options is the user's own text.\nThen write the plan as markdown and submit it:\n ${cmd} ${url} plan --file <path> --wait\nUse these headings, each with bullets: \"## What I found\" (facts as \\`- **Label:** value\\`: app framework, package manager, agent framework, model provider and model, where components live), \"## What I will install\" (packages and files, one per bullet), \"## Steps\" (a numbered list, one line each), and \"## Open questions\" only if any remain. Keep it under 30 lines; no preamble. \"plan --wait\" prints {\"decision\":\"approve\"} or {\"decision\":\"revise\",\"feedback\":\"...\"}. On revise, address the feedback (ask again if you need to) and submit a new plan; repeat until approved. Do not change the project before approval: the setup refuses steps until then.\n\n2. Install. Declare the steps from your plan up front, in order:\n ${cmd} ${url} add-step \"<title>\" --detail \"<one line>\"\nwhich prints the step id. Then work through them: \"step <id> active\" before, \"step <id> done --note '<one line on what you did>'\" after; \"skipped\" when the step is already satisfied, \"blocked\" when you cannot continue, saying why in --note. Add steps as the work reveals them. Ask when you need another decision or value (--choices for a pick, plain for text; --optional when you can proceed without an answer; --wait only when the answer is the very next thing you need). A choice question is multi-select: the user checks any number of your options, and the answer is an array of the ids they checked, plus their own text as an entry when they typed one. Pass --single only when the options exclude each other (one framework, one model, yes or no); the answer is then one id. Every option must carry an icon, given as --icons \"<id>=<icon>,...\" with the closest name from this pack: ${OPTION_ICONS.join(\", \")}. A question without an icon for each option is refused. Open inputs the user has not answered are listed under \"inputs\" in \"status\"; check them before asking again.\nThe API key behind a model answer is not in the answer and you never see it: pick the variable name the provider's SDK reads (OPENAI_API_KEY, ANTHROPIC_API_KEY, ...) and run\n ${cmd} ${url} env <input-id> <ENV_VAR> --file .env.local\nwhich writes ENV_VAR=<key> into that file. Use the answer's model in the chat route and map reasoningEffort onto the provider's setting when it is set. If \"env\" says the key was never deposited (the model question was answered from your conversation), ask the user to add the key to that file themselves. Never invent a key or a model name.\n\nWhen every product is installed and verified, run\n ${cmd} ${url} done --summary \"<one or two lines on what you installed and how to try it>\" --preview http://localhost:<port>\nBefore that, start the project's dev server in the background when the project has one, check that the page you changed loads, and pass the address as --preview (the page itself when it is not the root); leave the server running so the user can try the result. Omit --preview when there is nothing to open. This proposes closing the setup; only the user closes it. Do not stop the stream or exit: stay idle on it, because the user may follow up with a \"message.created\" event. Handle it, then run \"done\" again. The stream prints \"finished\" once the user closes the setup, and \"cancelled\" if they end it early. Keep the stream running after either: the user's next setup starts on this same URL and arrives as \"setup.created\" with its products. Run\n ${cmd} ${url} wait start\nwhich returns once that setup begins, then run \"status\" for its products, guides and instructions and go through plan and install again. A \"setup.created\" event while you are still working means the user replaced this setup: drop what you were doing and start over the same way.\n`;\n};\n\nexport const INSTRUCTIONS = agentInstructions;\n\ntype Parsed = { positional: string[]; flags: Map<string, string | true> };\n\nconst parseArgs = (argv: readonly string[]): Parsed => {\n const positional: string[] = [];\n const flags = new Map<string, string | true>();\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]!;\n if (!arg.startsWith(\"--\")) {\n positional.push(arg);\n continue;\n }\n const name = arg.slice(2);\n const next = argv[i + 1];\n if (next !== undefined && !next.startsWith(\"--\")) {\n flags.set(name, next);\n i++;\n } else {\n flags.set(name, true);\n }\n }\n return { positional, flags };\n};\n\nexport {\n CODING_AGENTS,\n detectAgentKind,\n findCommand,\n isDirectInvocation,\n runCodingAgent,\n} from \"./agents\";\nexport type { CodingAgent } from \"./agents\";\n\nconst flagText = (flags: Parsed[\"flags\"], name: string) => {\n const value = flags.get(name);\n return typeof value === \"string\" ? value : undefined;\n};\n\nconst fail = (message: string): never => {\n process.stderr.write(`setup-agent: ${message}\\n`);\n return process.exit(1);\n};\n\nconst print = (value: unknown) => {\n process.stdout.write(`${JSON.stringify(value)}\\n`);\n};\n\n/** The agents `setup-agent <url>` can start itself, for a person who ran it in a terminal. */\nconst LAUNCHERS = CODING_AGENTS.filter(\n (\n agent,\n ): agent is CodingAgent & { launch: NonNullable<CodingAgent[\"launch\"]> } =>\n agent.launch !== undefined,\n);\n\nconst chooseLauncher = async () => {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n process.stdout.write(\n \"This command is meant for a coding agent, and none is running here.\\nStart one to do the install:\\n\",\n );\n LAUNCHERS.forEach((launcher, index) => {\n process.stdout.write(` ${index + 1}. ${launcher.name}\\n`);\n });\n while (true) {\n const answer = (\n await rl.question(\"Choose [1]: \").catch(() => process.exit(1))\n ).trim();\n if (answer === \"\") return LAUNCHERS[0]!;\n const byNumber = LAUNCHERS[Number(answer) - 1];\n if (byNumber) return byNumber;\n const byName = LAUNCHERS.find(\n (launcher) =>\n launcher.kind === answer.toLowerCase() ||\n launcher.name.toLowerCase() === answer.toLowerCase(),\n );\n if (byName) return byName;\n process.stdout.write(\"Enter a number from the list.\\n\");\n }\n } finally {\n rl.close();\n }\n};\n\nconst launchAgent = async (url: string) => {\n const launcher = await chooseLauncher();\n const prompt = `Run \\`npx setup-agent ${shellWord(url)}\\` and follow the instructions it prints.`;\n process.stdout.write(`\\nStarting ${launcher.name}...\\n\\n`);\n const code = await runCodingAgent(launcher, prompt);\n if (code === undefined) {\n process.stderr.write(\n `${launcher.name} is not installed. Install it with:\\n ${launcher.launch.install}\\nthen run this command again.\\n`,\n );\n }\n // Exit once stdout and stderr have drained.\n process.exitCode = code ?? 1;\n};\n\nconst requireState = (client: CheckoutClient): Checkout.State =>\n client.state ?? fail(\"the setup has no state\");\n\nconst summary = (state: Checkout.State) => {\n const plan = currentPlan(state);\n return {\n status: state.status,\n agentConnected: isAgentPresent(state),\n products: state.products,\n instructions: state.instructions,\n plan:\n plan === undefined\n ? null\n : {\n revision: plan.revision,\n status: plan.status,\n ...(plan.feedback !== undefined && { feedback: plan.feedback }),\n },\n progress: stepProgress(state),\n steps: state.steps,\n inputs: state.inputs,\n log: state.log.slice(-20),\n pendingMessages: state.log.filter(\n (entry) => entry.role === \"user\" && entry.acknowledgedAt === undefined,\n ),\n };\n};\n\nconst STEP_STATUSES: readonly Checkout.StepStatus[] = [\n \"pending\",\n \"active\",\n \"done\",\n \"skipped\",\n \"blocked\",\n];\n\n/** Resolves once a setup is planning or installing, or once the one being waited on is cancelled; a setup already closed when the wait begins is over, so the wait is for the next one. */\nexport const waitForStart = (\n client: Pick<CheckoutClient, \"state\" | \"subscribe\">,\n) =>\n new Promise<Checkout.Status>((resolve) => {\n let previous: Checkout.Status | undefined;\n const check = () => {\n const state = client.state;\n if (state === undefined) return;\n const started =\n state.status === \"planning\" || state.status === \"installing\";\n const ended = isClosed(state) && previous === \"waiting\";\n previous = state.status;\n if (!started && !ended) return;\n unsubscribe();\n resolve(state.status);\n };\n const unsubscribe = client.subscribe(check);\n check();\n });\n\nconst waitForInput = (client: CheckoutClient, inputId: string) =>\n new Promise<Checkout.Input>((resolve) => {\n const check = () => {\n const state = client.state;\n const input = state?.inputs.find((candidate) => candidate.id === inputId);\n if (!input) return;\n if (input.status !== \"open\" || state?.status === \"cancelled\") {\n unsubscribe();\n resolve(input);\n }\n };\n const unsubscribe = client.subscribe(check);\n check();\n });\n\nconst waitForPlan = (client: CheckoutClient, revision: number) =>\n new Promise<Checkout.Plan | undefined>((resolve) => {\n const check = () => {\n const state = client.state;\n const plan = state?.plans.find(\n (candidate) => candidate.revision === revision,\n );\n if (\n plan === undefined ||\n plan.status !== \"proposed\" ||\n state?.status === \"cancelled\"\n ) {\n unsubscribe();\n resolve(plan);\n }\n };\n const unsubscribe = client.subscribe(check);\n check();\n });\n\nconst decided = (plan: Checkout.Plan | undefined, state: Checkout.State) => {\n if (state.status === \"cancelled\") return { ok: false, status: \"cancelled\" };\n if (plan === undefined) return fail(\"the plan disappeared\");\n return plan.status === \"approved\"\n ? { ok: true, decision: \"approve\", revision: plan.revision }\n : {\n ok: true,\n decision: \"revise\",\n revision: plan.revision,\n feedback: plan.feedback ?? \"\",\n };\n};\n\nconst splitList = (value: string | undefined) =>\n value\n ?.split(\",\")\n .map((entry) => entry.trim())\n .filter(Boolean);\n\nexport const askSeed = (\n rest: readonly string[],\n flags: Parsed[\"flags\"],\n): Checkout.InputSeed & { stepId?: string } => {\n const stepId = flagText(flags, \"step\");\n const optional = flags.has(\"optional\");\n const fallback = flagText(flags, \"default\");\n if (flags.has(\"entry-points\")) {\n if (\n [\n \"preset\",\n \"product\",\n \"choices\",\n \"icons\",\n \"multiple\",\n \"single\",\n \"only\",\n \"placeholder\",\n ].some((flag) => flags.has(flag))\n )\n return fail(\n \"--entry-points cannot be combined with other input kinds, --multiple, --single or --only\",\n );\n const json = flagText(flags, \"entry-points\");\n let value: unknown;\n try {\n value = JSON.parse(json ?? \"\");\n } catch {\n return fail(\"--entry-points needs a JSON array of options\");\n }\n const options = parseEntryPointOptions(value);\n if (!options)\n return fail(\n \"--entry-points needs 1–3 unique options with id, label, description and entryPoint {formFactor, placement, trigger, recommended?}; at most one is recommended\",\n );\n if (\n fallback !== undefined &&\n !options.some((option) => option.id === fallback)\n )\n return fail(`no option \"${fallback}\" to default to`);\n const prompt =\n rest[0] ?? fail(\"usage: ask <prompt> --entry-points '<JSON array>'\");\n if (prompt.trim() === \"\")\n return fail(\"an entry-point prompt cannot be empty\");\n return {\n kind: \"entry-point\",\n prompt,\n options,\n ...(fallback !== undefined && { default: fallback }),\n optional,\n ...(stepId !== undefined && { stepId }),\n };\n }\n const preset = flagText(flags, \"preset\");\n if (preset !== undefined) {\n if (!isPresetId(preset)) {\n return fail(\n `unknown preset \"${preset}\"; use ${Object.keys(INPUT_PRESETS).join(\" or \")}`,\n );\n }\n const only = splitList(flagText(flags, \"only\"));\n const found = splitList(flagText(flags, \"choices\"))?.map((entry) => {\n const [id = \"\", ...description] = entry.split(\"=\");\n return description.length === 0\n ? { id }\n : { id, description: description.join(\"=\") };\n });\n try {\n const seed = presetInput(preset, {\n ...(only !== undefined && { only }),\n ...(found !== undefined && { found }),\n ...(fallback !== undefined && { default: fallback }),\n optional,\n ...(stepId !== undefined && { stepId }),\n });\n return rest[0] === undefined ? seed : { ...seed, prompt: rest[0] };\n } catch (error) {\n return fail(error instanceof Error ? error.message : String(error));\n }\n }\n const prompt = rest[0] ?? fail(\"usage: ask <prompt>\");\n const product = flagText(flags, \"product\");\n if (product !== undefined) {\n return {\n kind: \"product\",\n prompt,\n product,\n optional,\n ...(stepId !== undefined && { stepId }),\n };\n }\n const choices = splitList(flagText(flags, \"choices\"));\n if (choices !== undefined) {\n if (flags.has(\"single\") && flags.has(\"multiple\"))\n return fail(\"--single and --multiple exclude each other\");\n const icons = new Map<string, string>();\n for (const entry of splitList(flagText(flags, \"icons\")) ?? []) {\n const [id = \"\", icon = \"\"] = entry.split(\"=\", 2);\n if (!choices.includes(id))\n return fail(`no choice \"${id}\" to put an icon on`);\n if (!isOptionIcon(icon)) {\n return fail(\n `unknown icon \"${icon}\"; pick one of ${OPTION_ICONS.join(\", \")}`,\n );\n }\n icons.set(id, icon);\n }\n const missing = choices.filter((id) => !icons.has(id));\n if (missing.length > 0) {\n return fail(\n `every option needs an icon; ${missing.map((id) => `\"${id}\"`).join(\", \")} ${missing.length === 1 ? \"has\" : \"have\"} none. Pass --icons \"${choices.map((id) => `${id}=<icon>`).join(\",\")}\" with icons from: ${OPTION_ICONS.join(\", \")}`,\n );\n }\n return {\n kind: \"choice\",\n prompt,\n options: choices.map((id) => ({ id, label: id, icon: icons.get(id)! })),\n ...(!flags.has(\"single\") && { multiple: true }),\n ...(fallback !== undefined && { default: fallback }),\n optional,\n ...(stepId !== undefined && { stepId }),\n };\n }\n const placeholder = flagText(flags, \"placeholder\");\n return {\n prompt,\n ...(placeholder !== undefined && { placeholder }),\n optional,\n ...(stepId !== undefined && { stepId }),\n };\n};\n\nconst assertEnvKey = (key: string) => {\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {\n throw new Error(`\"${key}\" is not an environment variable name`);\n }\n};\n\n/** Sets `key=value` in dotenv text, replacing an existing line for the key. */\nexport const upsertEnvLine = (content: string, key: string, value: string) => {\n assertEnvKey(key);\n // A line break or other control character would end the line and let the\n // value write lines of its own.\n const isLineBreaking = (code: number) =>\n code < 0x20 ||\n (code >= 0x7f && code <= 0x9f) ||\n code === 0x2028 ||\n code === 0x2029;\n if ([...value].some((char) => isLineBreaking(char.charCodeAt(0)))) {\n throw new Error(`the value for ${key} must be a single line`);\n }\n const line = `${key}=${value}`;\n const lines = content === \"\" ? [] : content.replace(/\\n$/, \"\").split(\"\\n\");\n const index = lines.findIndex(\n (entry) =>\n /^(?:export\\s+)?([A-Za-z_][A-Za-z0-9_]*)\\s*=/.exec(entry)?.[1] === key,\n );\n if (index === -1) lines.push(line);\n else lines[index] = line;\n return `${lines.join(\"\\n\")}\\n`;\n};\n\n/**\n * Write the secret `take` returns to `file` under `key`, leaving the file\n * owner-only. The file is opened before `take` runs, because taking the\n * secret consumes it and an unwritable destination must fail while it is\n * still deposited; it is read after, so lines written meanwhile survive.\n */\nexport const writeEnvSecret = async (\n file: string,\n key: string,\n take: () => Promise<string>,\n projectRoot = process.cwd(),\n) => {\n assertEnvKey(key);\n try {\n if ((await lstat(file)).isSymbolicLink()) {\n throw new Error(\n `${file} is a symbolic link; pass --file with its target to write there`,\n );\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n }\n const projectRelative = relative(projectRoot, file);\n if (\n projectRelative !== \"..\" &&\n !projectRelative.startsWith(`..${sep}`) &&\n !isAbsolute(projectRelative)\n ) {\n const root = await realpath(projectRoot);\n const parent = await realpath(dirname(file));\n const parentRelative = relative(root, parent);\n if (\n parentRelative === \"..\" ||\n parentRelative.startsWith(`..${sep}`) ||\n isAbsolute(parentRelative)\n ) {\n throw new Error(`${file} has a parent directory outside the project`);\n }\n }\n const handle = await open(\n file,\n constants.O_RDWR |\n constants.O_APPEND |\n constants.O_CREAT |\n constants.O_NOFOLLOW,\n 0o600,\n ).catch((error: NodeJS.ErrnoException) => {\n if (error.code === \"ELOOP\")\n throw new Error(\n `${file} is a symbolic link; pass --file with its target to write there`,\n );\n throw error;\n });\n try {\n await handle.chmod(0o600);\n const value = await take();\n const current = await handle.readFile(\"utf8\");\n // Built before truncating, so a refused value leaves the file intact.\n const next = upsertEnvLine(current, key, value);\n await handle.truncate(0);\n await handle.writeFile(next);\n } finally {\n await handle.close();\n }\n};\n\nconst redeemKeyDirectories = () => {\n const stateHome =\n process.platform === \"win32\"\n ? process.env.LOCALAPPDATA || join(homedir(), \"AppData\", \"Local\")\n : process.env.XDG_STATE_HOME || join(homedir(), \".local\", \"state\");\n return [\n join(stateHome, \"setup-agent\"),\n join(\n tmpdir(),\n `setup-agent-${process.platform === \"win32\" ? userInfo().username : process.getuid!()}`,\n ),\n ];\n};\n\nconst checkRedeemKeyDirectory = async (directory: string) => {\n if (!isAbsolute(directory)) {\n throw new Error(\"state home must be an absolute path\");\n }\n const info = await lstat(directory);\n if (!info.isDirectory() || info.isSymbolicLink()) {\n throw new Error(\n `redeem key directory must not be a symbolic link: ${directory}`,\n );\n }\n if (process.platform !== \"win32\") {\n if (info.uid !== process.getuid!()) {\n throw new Error(\n `redeem key directory must be owned by the user: ${directory}`,\n );\n }\n if ((info.mode & 0o077) !== 0) await chmod(directory, 0o700);\n }\n};\n\nconst prepareRedeemKeyDirectory = async (directory: string) => {\n if (isAbsolute(directory)) {\n await mkdir(directory, { recursive: true, mode: 0o700 });\n }\n await checkRedeemKeyDirectory(directory);\n};\n\nconst saveRedeemKey = async (hash: string, key: string) => {\n let lastError: unknown;\n for (const directory of redeemKeyDirectories()) {\n const file = join(directory, `${hash}.key`);\n let created = false;\n try {\n await prepareRedeemKeyDirectory(directory);\n const handle = await open(\n file,\n constants.O_WRONLY |\n constants.O_CREAT |\n constants.O_EXCL |\n constants.O_NOFOLLOW,\n 0o600,\n );\n created = true;\n try {\n await handle.writeFile(key);\n } finally {\n await handle.close();\n }\n return file;\n } catch (error) {\n if (created) await unlink(file).catch(() => {});\n lastError = error;\n }\n }\n throw lastError;\n};\n\nconst readRedeemKey = async (hash: string) => {\n if (!/^[0-9a-f]{64}$/.test(hash)) return undefined;\n for (const directory of redeemKeyDirectories()) {\n try {\n await checkRedeemKeyDirectory(directory);\n const file = join(directory, `${hash}.key`);\n const handle = await open(\n file,\n constants.O_RDONLY | constants.O_NOFOLLOW,\n );\n try {\n const info = await handle.stat();\n if (\n !info.isFile() ||\n (process.platform !== \"win32\" && (info.mode & 0o077) !== 0)\n ) {\n throw new Error(\"redeem key file must be private and regular\");\n }\n const key = await handle.readFile(\"utf8\");\n if (createHash(\"sha256\").update(key).digest(\"hex\") === hash) {\n return { key, file };\n }\n } finally {\n await handle.close();\n }\n } catch {\n continue;\n }\n }\n return undefined;\n};\n\nconst answered = (input: Checkout.Input) => ({\n ok: true,\n inputId: input.id,\n status: input.status,\n ...(input.answer !== undefined && answerFields(input, input.answer)),\n});\n\nexport type RunSetupCommandOptions = {\n /** How the agent invokes these commands, before the URL; `npx setup-agent` by default. */\n command?: string;\n /** Lets \"stream\" open the setup page when the setup waits on the user, as `--setup-app-url` does. */\n browser?: { setupAppUrl: string; canOpen?: boolean };\n};\n\nconst runStream = async (\n client: CheckoutClient,\n url: string,\n flags: Parsed[\"flags\"],\n options: RunSetupCommandOptions,\n) => {\n const setupAppUrl =\n options.browser?.setupAppUrl ?? flagText(flags, \"setup-app-url\");\n const linkId = new URL(url).pathname.split(\"/\").filter(Boolean).at(-1);\n const stream = await followSetup(client, {\n ...(setupAppUrl !== undefined &&\n linkId !== undefined && {\n browser: {\n setupAppUrl: parseSetupUrl(setupAppUrl),\n linkId,\n answerCommand: `${options.command ?? DEFAULT_COMMAND} ${shellWord(url)} answer`,\n canOpen: (options.browser?.canOpen ?? true) && !flags.has(\"no-open\"),\n },\n }),\n });\n const stop = () => {\n void stream.stop().finally(() => {\n client.dispose();\n process.exit(0);\n });\n };\n process.on(\"SIGHUP\", stop);\n process.on(\"SIGINT\", stop);\n process.on(\"SIGTERM\", stop);\n await new Promise(() => {});\n};\n\nexport const main = (argv: readonly string[]) => runSetupCommand(argv);\n\n/**\n * Runs one setup command (`<url> <command> ...`) the way `npx setup-agent`\n * does, for a CLI that embeds setup as its own command.\n */\nexport const runSetupCommand = async (\n argv: readonly string[],\n options: RunSetupCommandOptions = {},\n) => {\n const { positional, flags } = parseArgs(argv);\n const [first, command = \"intro\", ...rest] = positional;\n if (flags.has(\"help\") || flags.has(\"h\") || first === undefined) {\n process.stdout.write(HELP);\n return process.exit(first === undefined ? 1 : 0);\n }\n let url: string;\n try {\n url = parseSetupUrl(first);\n } catch (error) {\n return fail(error instanceof Error ? error.message : String(error));\n }\n if (command === \"intro\" && isDirectInvocation()) return launchAgent(url);\n\n const client = await connectCheckout(url).catch((error: unknown) =>\n fail(error instanceof Error ? error.message : String(error)),\n );\n const commands = client.commands;\n\n try {\n switch (command) {\n case \"intro\": {\n const kind = detectAgentKind();\n await commands[\"agent/intro\"]({\n ...(kind !== undefined && { kind }),\n }).catch(() => {});\n process.stdout.write(\n agentInstructions(url, {\n ...(options.command !== undefined && { command: options.command }),\n }),\n );\n process.stdout.write(\"\\nCurrent setup:\\n\");\n print(summary(requireState(client)));\n break;\n }\n case \"status\": {\n print(summary(requireState(client)));\n break;\n }\n case \"stream\": {\n await runStream(client, url, flags, options);\n break;\n }\n case \"answer\": {\n const id =\n rest[0] ??\n fail(\"usage: answer <input-id|plan|finish> <answer> [--note <text>]\");\n const note = flagText(flags, \"note\");\n const feedback = flagText(flags, \"feedback\");\n print(\n await answerNeed(client, id, rest[1], {\n ...(note !== undefined && { note }),\n ...(feedback !== undefined && { feedback }),\n ...(flags.has(\"dismiss\") && { dismiss: true }),\n }),\n );\n break;\n }\n case \"step\": {\n const usage =\n \"usage: step <step-id> <pending|active|done|skipped|blocked>\";\n const stepId = rest[0] ?? fail(usage);\n const status = rest[1];\n if (!STEP_STATUSES.includes(status as Checkout.StepStatus)) fail(usage);\n const note = flagText(flags, \"note\");\n await commands[\"agent/step\"]({\n stepId,\n status: status as Checkout.StepStatus,\n ...(note !== undefined && { note }),\n });\n print({ ok: true, stepId, status });\n break;\n }\n case \"add-step\": {\n const title = rest[0] ?? fail(\"usage: add-step <title>\");\n const detail = flagText(flags, \"detail\");\n const product = flagText(flags, \"product\");\n const result = await commands[\"agent/add-step\"]({\n title,\n ...(detail !== undefined && { detail }),\n ...(product !== undefined && { product }),\n ...(flags.has(\"active\") && { active: true }),\n });\n print({ ok: true, ...result });\n break;\n }\n case \"plan\": {\n const file = flagText(flags, \"file\");\n const markdown =\n file !== undefined\n ? await readFile(resolve(file), \"utf8\").catch(() =>\n fail(`could not read ${file}`),\n )\n : (rest[0] ?? fail(\"usage: plan <markdown> | plan --file <path>\"));\n const { revision } = await commands[\"agent/plan\"]({ markdown });\n if (!flags.has(\"wait\")) {\n print({ ok: true, revision });\n break;\n }\n const plan = await waitForPlan(client, revision);\n print(decided(plan, requireState(client)));\n break;\n }\n case \"ask\": {\n const seed = askSeed(rest, flags);\n const redeemKey =\n seed.kind === \"model\" ? randomBytes(32).toString(\"hex\") : undefined;\n const redeemKeyHash =\n redeemKey === undefined\n ? undefined\n : createHash(\"sha256\").update(redeemKey).digest(\"hex\");\n const keyFile =\n redeemKey === undefined || redeemKeyHash === undefined\n ? undefined\n : await saveRedeemKey(redeemKeyHash, redeemKey).catch(\n (error: unknown) =>\n fail(\n `could not store the model redeem key: ${error instanceof Error ? error.message : String(error)}`,\n ),\n );\n let inputId: string;\n try {\n ({ inputId } = await commands[\"agent/ask\"]({\n ...seed,\n ...(redeemKeyHash !== undefined && { redeemKeyHash }),\n }));\n } catch (error) {\n if (keyFile !== undefined) await unlink(keyFile).catch(() => {});\n throw error;\n }\n if (!flags.has(\"wait\")) {\n print({ ok: true, inputId });\n break;\n }\n print(await waitForInput(client, inputId).then(answered));\n break;\n }\n case \"wait\": {\n const target =\n rest[0] ?? fail(\"usage: wait start | wait <input-id> | wait plan\");\n const state = requireState(client);\n if (target === \"start\") {\n const status = await waitForStart(client);\n print({\n ok: status === \"planning\" || status === \"installing\",\n status,\n });\n break;\n }\n if (target === \"plan\") {\n const plan = currentPlan(state) ?? fail(\"no plan has been proposed\");\n print(\n decided(\n await waitForPlan(client, plan.revision),\n requireState(client),\n ),\n );\n break;\n }\n if (!state.inputs.some((input) => input.id === target)) {\n fail(`no input \"${target}\"`);\n }\n print(await waitForInput(client, target).then(answered));\n break;\n }\n case \"env\": {\n const usage = \"usage: env <input-id> <ENV_VAR> [--file <path>]\";\n const inputId = rest[0] ?? fail(usage);\n const envKey = rest[1] ?? fail(usage);\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(envKey)) {\n fail(`\"${envKey}\" is not an environment variable name`);\n }\n const state = requireState(client);\n const input =\n state.inputs.find((candidate) => candidate.id === inputId) ??\n fail(`no input \"${inputId}\"`);\n const setupId = state.id ?? fail(\"the setup has not been created yet\");\n if (isClosed(state)) {\n fail(\"the setup is not open\");\n }\n if (input.status !== \"answered\") {\n fail(`input \"${inputId}\" is ${input.status}`);\n }\n if (\n input.kind !== \"model\" ||\n !isValidModelAnswer(input, input.answer ?? \"\")\n ) {\n fail(`input \"${inputId}\" is not an llm-provider answer`);\n }\n const storedKey =\n input.redeemKeyHash === undefined\n ? undefined\n : await readRedeemKey(input.redeemKeyHash);\n if (input.redeemKeyHash !== undefined && storedKey === undefined) {\n fail(`no local redeem key for \"${inputId}\"`);\n }\n const file = resolve(flagText(flags, \"file\") ?? \".env.local\");\n await writeEnvSecret(file, envKey, async () => {\n const secretUrl = new URL(url);\n if (\n secretUrl.protocol !== \"https:\" &&\n !(\n secretUrl.protocol === \"http:\" &&\n isLoopbackHostname(secretUrl.hostname)\n )\n ) {\n fail(\"the key is only fetched over https\");\n }\n const response = await fetch(\n `${url.replace(/\\/$/, \"\")}/secret/${encodeURIComponent(inputId)}?setup=${encodeURIComponent(setupId)}`,\n storedKey === undefined\n ? undefined\n : { headers: { \"Setup-Agent-Redeem-Key\": storedKey.key } },\n );\n if (response.status === 403) {\n fail(`redeem key rejected for \"${inputId}\"`);\n }\n if (response.status === 404) {\n fail(\n `the key for \"${inputId}\" was already taken or never deposited`,\n );\n }\n if (!response.ok) fail(`could not fetch the key: ${response.status}`);\n const key = await response.text();\n // An API key is one word of letters, digits and a few separators;\n // anything else could break out of the env line, be read differently\n // by dotenv, or run as shell syntax when the file is sourced.\n if (!/^[A-Za-z0-9._~+/=:@%,-]+$/.test(key)) {\n fail(\"the deposited key is not a valid API key\");\n }\n return key;\n });\n if (storedKey !== undefined) {\n await unlink(storedKey.file).catch((error: NodeJS.ErrnoException) => {\n if (error.code !== \"ENOENT\") {\n process.stderr.write(\n `warning: could not remove a redeem key: ${error.message}\\n`,\n );\n }\n });\n }\n print({ ok: true, envKey, file });\n break;\n }\n case \"ack\": {\n const messageId = rest[0] ?? fail(\"usage: ack <message-id>\");\n await commands[\"agent/ack\"]({ messageId });\n print({ ok: true });\n break;\n }\n case \"log\": {\n const text = rest[0] ?? fail(\"usage: log <text>\");\n await commands[\"agent/log\"]({ text });\n print({ ok: true });\n break;\n }\n case \"done\": {\n const summary = flagText(flags, \"summary\");\n const preview = flagText(flags, \"preview\");\n await commands[\"agent/done\"]({\n ...(summary !== undefined && { summary }),\n ...(preview !== undefined && { preview }),\n });\n print({\n ok: true,\n status: \"proposed\",\n next: \"Keep the stream running and stay idle. The user closes the setup or sends a follow-up message.\",\n });\n break;\n }\n default:\n fail(`unknown command \"${command}\"\\n\\n${HELP}`);\n }\n } catch (error) {\n fail(error instanceof Error ? error.message : String(error));\n } finally {\n client.dispose();\n }\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["import {\n CODING_AGENTS,\n detectAgentKind,\n isDirectInvocation,\n runCodingAgent,\n} from \"./agents\";\nimport type { CodingAgent } from \"./agents\";\nimport { connectCheckout } from \"./client\";\nimport type { CheckoutClient } from \"./client\";\nimport { INPUT_PRESETS, isPresetId, presetInput } from \"./presets\";\nimport { createHash, randomBytes } from \"node:crypto\";\nimport { constants } from \"node:fs\";\nimport {\n chmod,\n lstat,\n mkdir,\n open,\n readFile,\n realpath,\n unlink,\n} from \"node:fs/promises\";\nimport { homedir, tmpdir, userInfo } from \"node:os\";\nimport { createInterface } from \"node:readline/promises\";\nimport { dirname, isAbsolute, join, relative, resolve, sep } from \"node:path\";\nimport {\n OPTION_ICONS,\n currentPlan,\n isAgentPresent,\n isClosed,\n isLoopbackHostname,\n isOptionIcon,\n isValidModelAnswer,\n parseEntryPointOptions,\n stepProgress,\n} from \"./protocol\";\nimport type { Checkout } from \"./protocol\";\nimport { answerNeed } from \"./needs\";\nimport { answerFields, followSetup, shellWord } from \"./stream\";\n\nexport { diffEvents } from \"./stream\";\n\nconst DEFAULT_COMMAND = \"npx setup-agent\";\n\n/**\n * How long \"wait <input-id>\", \"wait plan\" and \"--wait\" block before handing\n * control back with a \"waiting\" result, in seconds. Agent shells kill a\n * command after a limit of their own (two minutes by default in Claude\n * Code); a wait that returns first lets the agent run it again instead of\n * reading the kill as the end of the setup.\n */\nconst DEFAULT_WAIT_SECONDS = 100;\n\nexport const HELP = `setup-agent <url> [command]\n\nCommands (the url always comes first):\n status Print the setup as JSON.\n stream [--setup-app-url <url>] [--no-open]\n Follow the setup: one JSON array of events per line. With --setup-app-url, open the setup page when the setup waits on the user.\n answer <input-id> <answer> [--note <text>] [--dismiss]\n answer plan approve | answer plan revise --feedback <text> | answer finish close\n Answer for the user what they told you in your own conversation, as an \"input.needed\" event's \"answer\" shows.\n log <text> Send a message to the user: a reply, something no question or step conveys, or one wrap-up when finished.\n ack <message-id> Acknowledge a user message.\n ask <prompt> [--placeholder <text>] [--optional] [--step <step-id>] [--wait]\n Ask the user for a line of text.\n ask [prompt] --preset <framework|llm-provider> [--only <id,id>] [--choices <id=description,...>] [--default <id>] [--optional] [--step <step-id>] [--wait]\n Ask a standard question. --only restricts the options and leaves out --choices (one id locks it in); --default preselects one.\n \"framework\" answers \"<framework>:<language>\"; \"llm-provider\" answers JSON with provider, model and reasoningEffort; the key itself arrives through \"env\".\n ask <prompt> --choices <id,id,...> --icons <id=icon,...> [--single] [--default <id>] [--optional] [--step <step-id>] [--wait]\n Ask the user to pick from your own options; they may also type their own answer.\n The user checks any number of options and the answer is a JSON array of option ids (their own text is one entry).\n --single is for options that exclude each other (one framework, one model, yes or no); the answer is then one id.\n --icons is required and names an icon for every option, from: ${OPTION_ICONS.join(\", \")}.\n ask <prompt> --entry-points '<JSON array>' [--default <id>] [--optional] [--step <step-id>] [--wait]\n Ask where users should access the assistant. Each of 1–3 options has id, label, description and entryPoint {formFactor: modal|sidebar|full-page, placement, trigger, recommended?}. Answers one option id.\n wait start Block until the user clicks Begin plan in their browser; after a finished or cancelled setup, until the next one begins.\n wait <input-id> [--timeout <seconds>]\n Block until the user answers or dismisses an input. A multi-select answer prints as an array.\n plan (<markdown> | --file <path>) [--wait] [--timeout <seconds>]\n Propose the plan for the user to approve. --wait blocks until they decide.\n wait plan [--timeout <seconds>] Block until the user approves the plan or asks for changes.\n wait <input-id>, wait plan and every --wait give up after ${DEFAULT_WAIT_SECONDS}s (--timeout, 0 for no limit) and print\n {\"ok\":false,\"status\":\"waiting\",\"next\":\"<command>\"}: run \"next\" again to keep waiting.\n env <input-id> <ENV_VAR> [--file <path>]\n Write the API key behind an answered llm-provider input to an env file (default .env.local) as ENV_VAR=<key>, without printing it.\n add-step <title> [--detail <text>] [--product <slug>] [--active]\n Declare a step of the install (after the plan is approved). Prints its id.\n step <step-id> <status> [--note <text>]\n Set a step to pending|active|done|skipped|blocked.\n done [--summary <text>] [--preview <url>]\n Mark every remaining step done and propose closing; the user closes it.\n`;\n\n/** Characters a setup URL may hold: an origin and a path, nothing a shell or a reader treats as more than an address. */\nconst SETUP_URL_PATTERN =\n /^https?:\\/\\/[A-Za-z0-9.-]+(?::[0-9]+)?(?:\\/[A-Za-z0-9._~%/-]*)?$|^http:\\/\\/\\[::1\\](?::[0-9]+)?(?:\\/[A-Za-z0-9._~%/-]*)?$/;\n\n/**\n * The normalized setup URL, or an error saying why it is refused. A setup URL\n * is printed into shell commands and the coding agent's prompt, and the API\n * key travels over it, so it must be https (http only on this machine), carry\n * no credentials, query or fragment, and hold only address characters.\n */\nexport const parseSetupUrl = (value: string): string => {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n throw new Error(`\"${value}\" is not a URL`);\n }\n if (\n url.protocol !== \"https:\" &&\n !(url.protocol === \"http:\" && isLoopbackHostname(url.hostname))\n ) {\n throw new Error(\"the setup URL must use https (http only on localhost)\");\n }\n if (url.username !== \"\" || url.password !== \"\") {\n throw new Error(\"the setup URL must not carry credentials\");\n }\n if (\n url.search !== \"\" ||\n url.hash !== \"\" ||\n value.includes(\"?\") ||\n value.includes(\"#\")\n ) {\n throw new Error(\"the setup URL must not have a query or fragment\");\n }\n const href = url.href.replace(/\\/$/, \"\");\n if (!SETUP_URL_PATTERN.test(href)) {\n throw new Error(\"the setup URL has characters a setup URL does not use\");\n }\n return href;\n};\n\nexport type AgentInstructionsOptions = {\n /** How the agent invokes the setup commands, before the URL; `npx setup-agent` by default. */\n command?: string;\n /**\n * The agent started this setup with `assistant-ui setup`, which already\n * began planning; there is no Begin plan click to wait for.\n */\n startedByAgent?: {\n browserUrl: string;\n /** False when the browser was not opened for the user; the agent then hands them the link. */\n opened?: boolean;\n /**\n * The command that printed this briefing is the stream: it keeps running\n * and opens the browser only when the setup waits on the user.\n */\n streaming?: boolean;\n };\n};\n\nconst EVENT_LIST = `\"planning.started\", \"input.answered\" ({inputId, answer, note?}; answer is an array for a choice question not asked with --single), \"input.dismissed\", \"plan.approved\", \"plan.changes-requested\" ({feedback}), \"message.created\" ({messageId, text}), \"finished\", \"cancelled\", \"setup.created\" ({products})`;\n\nconst beginPlanHandoff = (cmd: string, url: string) =>\n `Before planning, check the current setup status. If it is \"waiting\", tell the user in your own conversation: \"I'm connected. Go back to your browser and click Begin plan. You can watch my progress, answer questions, and steer me there.\" Then run:\n ${cmd} ${url} wait start\nKeep the stream running while you wait. Do not inspect the project, fetch guides, ask setup questions, or propose a plan until this command returns {\"ok\":true}. If it returns {\"ok\":false,\"status\":\"cancelled\"}, the user ended this setup before planning: do not plan, tell the user you are connected and waiting for their next setup, and run \"wait start\" again. Only the user in the browser should start planning; never invoke the browser's start command yourself. \"planning.started\" also arrives on the stream. If the status is already \"planning\" or \"installing\", resume that phase without asking the user to begin again. If it is \"done\" or \"cancelled\", that setup is over: tell the user you are connected and waiting for their next setup, and run \"wait start\", which returns once the next one begins.`;\n\nconst agentStartedHandoff = ({\n browserUrl,\n opened = true,\n streaming = false,\n}: NonNullable<AgentInstructionsOptions[\"startedByAgent\"]>) =>\n streaming\n ? `You started this setup, and it is already planning: there is no Begin plan click to wait for. Check \"status\" before planning: if it is \"planning\" or \"installing\", go on with that phase. If the status is \"done\" or \"cancelled\", that setup is over: tell the user you are connected and waiting for their next setup, and run \"wait start\", which returns once the next one begins.`\n : `You started this setup, and it is already planning: there is no Begin plan click to wait for. The browser asks the user to connect to you first; it shows your working directory once your stream is running. Check \"status\" before planning: if it is \"planning\" or \"installing\", go on with that phase. Tell the user in your own conversation, once: ${\n opened\n ? `\"I opened the assistant-ui setup in your browser. I'll ask my questions there, and you can watch my progress and steer me from that page. If it didn't open, go to ${browserUrl} .\"`\n : `\"Open ${browserUrl} to follow the assistant-ui setup. I'll ask my questions there, and you can watch my progress and steer me from that page.\"`\n } If the status is \"done\" or \"cancelled\", that setup is over: tell the user you are connected and waiting for their next setup, and run \"wait start\", which returns once the next one begins.`;\n\nconst streamSection = (\n cmd: string,\n url: string,\n streaming: boolean,\n noOpen: boolean,\n) =>\n streaming\n ? `The command that printed this is the stream. Keep it running in the background until the setup ends: the browser shows you as connected only while it runs. New output does not wake you: read its new lines between your other commands. Each new line is a JSON array of events: ${EVENT_LIST}, plus:\n- \"browser.opened\" ({id, message}): setup opened the setup page in the user's browser for a question. Tell the user once, in your own conversation, that it waits there, and keep working in the same turn.\n- \"input.needed\" ({id, message, question, options?, answer}): the user may not see the browser right now. Do what its message says: run \"wait <id>\" for their answer in the browser, or ask the user in your own conversation with that question and options, then run its \"answer\" command with their reply. Answered either way, the setup goes on.\nIf this command stops, start it again in the background with\n ${cmd} ${url} stream${noOpen ? \" --no-open\" : \"\"}\nThe URL is the user's link to you from their browser, not one setup: it outlives this one, and their next setup arrives on the same stream.`\n : `First, start a persistent Monitor on\n ${cmd} ${url} stream\nand keep it running: the browser shows you as connected only while it runs, and it is how answers reach you. The URL is the user's link to you from their browser, not one setup: it outlives this one, and their next setup arrives on the same stream. Each line is a JSON array of events: ${EVENT_LIST}.`;\n\n/** Why the agent must not end its turn mid-setup, and how it waits instead. */\nconst keepGoing = (cmd: string, url: string) =>\n `Run the setup to the end in one go. Do not count on the stream or the browser to wake you: if you end your turn before the install is done, the setup can stay paused until the user types to you, even after they answer in the browser. So never end your turn to wait for an answer, and do not stop after telling the user something. To get an answer, block on it:\n ${cmd} ${url} wait <input-id>\n(\"plan --wait\" and \"ask --wait\" block the same way). A wait that prints {\"ok\":false,\"status\":\"waiting\"} has not failed and the setup is not stuck: run its \"next\" command, right away. \"next\" only waits; never repeat an \"ask\" or \"plan\" to keep waiting, since that asks or submits again. End your turn only to ask the user in your own conversation (an \"input.needed\" you relay, or a value only they can give), and go on as soon as they reply; or once the install is done and proposed with \"done\".`;\n\nexport const agentInstructions = (\n setupUrl: string,\n options: AgentInstructionsOptions = {},\n) => {\n const url = shellWord(setupUrl);\n const cmd = options.command ?? DEFAULT_COMMAND;\n const started = options.startedByAgent;\n const streaming = started?.streaming === true;\n return `You are the coding agent for a setup ${started ? (streaming ? (started.opened === false ? \"you started; setup opens no browser (--no-open), so the user follows it on the setup page if they open it, or answers you in your own conversation\" : \"you started; the user follows it in their browser, which setup opens when you ask the user something\") : \"you opened in the user's browser\") : \"the user opened in their browser\"}. The browser shows this setup live; every command below updates it. The user watches the browser, not this terminal: once setup begins, send questions through \"ask\" so the user can answer in their browser.\n\n${streamSection(cmd, url, streaming, started?.opened === false)}\n\n${keepGoing(cmd, url)}\n\nThe user can message you at any time, including while a question is waiting. Read \"message.created\" events, acknowledge each with \"${cmd} ${url} ack <message-id>\", and reply using \"log\" with what you will do. Pending messages are replayed when the stream starts and listed in \"status\". A message is not an answer or plan approval; keep those decisions explicit.\n\n\"log\" is a message to the user, and the user reads every one: send it only to reply to their message, to tell them something no question or step conveys (a blocker, a deviation from the plan, a decision you made for them), or once as a wrap-up when the install is finished. Findings go in the plan, progress in step notes, questions in \"ask\"; do not narrate.\n\n${started ? agentStartedHandoff(started) : beginPlanHandoff(cmd, url)}\n\nThe remaining phases are plan, then install.\n\n1. Plan. Investigate only; change nothing yet. If the current directory is not an app to install into (a monorepo root, an empty folder, a library), ask where to install first:\n ${cmd} ${url} ask \"Which project should I install into?\" --placeholder \"An absolute path to the app\" --wait\nRead the project: its app framework and package manager, the agent framework and model provider it already uses, where components live, any existing chat route. Fetch each product's \"guide\" URL from the setup below; it is the install reference. Ask for what the project does not decide, using the standard questions so the browser can render them well:\n ${cmd} ${url} ask --preset framework\n ${cmd} ${url} ask --preset llm-provider\nThe setup's products are starting names and guide references. If the setup lists no products, first ask the user what to set up with a choice question whose options are slugs from https://www.assistant-ui.com/catalog.md, then fetch https://www.assistant-ui.com/install.md?items=<slugs> as the guide. Explore each product's setup instructions, then discover and plan any other relevant products or dependencies the app needs. Read /catalog.md on the guide URL’s origin to find related products and fetch their setup guides. The starting list does not restrict the implementation or require adding another product to the list. Include relevant additions in the plan for approval.\nWhen the setup puts an assistant in the app, ask how users should access it after discovering the actual app and before proposing the plan. Skip this question when no product's guide installs an assistant (a standalone library or service setup, for example). Use an entry-point question with only 1–3 sensible alternatives. Describe the actual app's pages, workflow and component placement, not generic placeholder copy. Recommend one clear fit when there is one; two options may use the same form factor in different places. Each option needs a stable id, label, contextual description and entryPoint {\"formFactor\":\"modal\"|\"sidebar\"|\"full-page\",\"placement\":\"where it appears\",\"trigger\":\"how users open it or navigate to it\",\"recommended\":true?}. Write the JSON array for this app and send it with:\n ${cmd} ${url} ask \"Where should users access the assistant?\" --entry-points '<JSON array>' --wait\nMark a clear fit with \"recommended\":true rather than --default, so the user makes the pick. When you ask it, do not submit the plan before the user picks an entry point. Respect the selected placement and trigger in the plan and implementation. Do not ask for a new-versus-existing app setting; discover the app from its files and ask only for an install path when needed.\nSkip a preset the code already answers (a chat route on the AI SDK, a provider key in .env.local) and log why. Ask everything at once, keep investigating, and then run \"wait <input-id>\" for each open question before you write the plan; do not end your turn while they are open. A framework answer is \"<framework>:<language>\", for example \"ai-sdk:typescript\". A model answer is JSON {\"provider\",\"model\",\"reasoningEffort\"?}. An answer may carry a \"note\" from the user; follow it. A choice answer that matches none of the options is the user's own text.\nThen write the plan as markdown and submit it:\n ${cmd} ${url} plan --file <path> --wait\nUse these headings, each with bullets: \"## What I found\" (facts as \\`- **Label:** value\\`: app framework, package manager, agent framework, model provider and model, where components live), \"## What I will install\" (packages and files, one per bullet), \"## Steps\" (a numbered list, one line each), and \"## Open questions\" only if any remain. Keep it under 30 lines; no preamble. \"plan --wait\" prints {\"decision\":\"approve\"} or {\"decision\":\"revise\",\"feedback\":\"...\"}; when it prints {\"ok\":false,\"status\":\"waiting\"}, the plan is submitted and still with the user, so keep waiting with \"wait plan\" and do not submit it again. On revise, address the feedback (ask again if you need to) and submit a new plan; repeat until approved. Do not change the project before approval: the setup refuses steps until then.\n\n2. Install. Declare the steps from your plan up front, in order:\n ${cmd} ${url} add-step \"<title>\" --detail \"<one line>\"\nwhich prints the step id. Then work through them: \"step <id> active\" before, \"step <id> done --note '<one line on what you did>'\" after; \"skipped\" when the step is already satisfied, \"blocked\" when you cannot continue, saying why in --note. Add steps as the work reveals them. Ask when you need another decision or value (--choices for a pick, plain for text; --optional when you can proceed without an answer; --wait only when the answer is the very next thing you need). A choice question is multi-select: the user checks any number of your options, and the answer is an array of the ids they checked, plus their own text as an entry when they typed one. Pass --single only when the options exclude each other (one framework, one model, yes or no); the answer is then one id. Every option must carry an icon, given as --icons \"<id>=<icon>,...\" with the closest name from this pack: ${OPTION_ICONS.join(\", \")}. A question without an icon for each option is refused. Open inputs the user has not answered are listed under \"inputs\" in \"status\"; check them before asking again.\nThe API key behind a model answer is not in the answer and you never see it: pick the variable name the provider's SDK reads (OPENAI_API_KEY, ANTHROPIC_API_KEY, ...) and run\n ${cmd} ${url} env <input-id> <ENV_VAR> --file .env.local\nwhich writes ENV_VAR=<key> into that file. Use the answer's model in the chat route and map reasoningEffort onto the provider's setting when it is set. If \"env\" says the key was never deposited (the model question was answered from your conversation), ask the user to add the key to that file themselves. Never invent a key or a model name.\n\nWhen every product is installed and verified, run\n ${cmd} ${url} done --summary \"<one or two lines on what you installed and how to try it>\" --preview http://localhost:<port>\nBefore that, start the project's dev server in the background when the project has one, check that the page you changed loads, and pass the address as --preview (the page itself when it is not the root); leave the server running so the user can try the result. Omit --preview when there is nothing to open. This proposes closing the setup; only the user closes it. Do not stop the stream or exit: stay idle on it, because the user may follow up with a \"message.created\" event. Handle it, then run \"done\" again. The stream prints \"finished\" once the user closes the setup, and \"cancelled\" if they end it early. Keep the stream running after either: the user's next setup starts on this same URL and arrives as \"setup.created\" with its products. Run\n ${cmd} ${url} wait start\nwhich returns once that setup begins, then run \"status\" for its products, guides and instructions and go through plan and install again. A \"setup.created\" event while you are still working means the user replaced this setup: drop what you were doing and start over the same way.\n`;\n};\n\nexport const INSTRUCTIONS = agentInstructions;\n\ntype Parsed = { positional: string[]; flags: Map<string, string | true> };\n\nconst parseArgs = (argv: readonly string[]): Parsed => {\n const positional: string[] = [];\n const flags = new Map<string, string | true>();\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]!;\n if (!arg.startsWith(\"--\")) {\n positional.push(arg);\n continue;\n }\n const name = arg.slice(2);\n const next = argv[i + 1];\n if (next !== undefined && !next.startsWith(\"--\")) {\n flags.set(name, next);\n i++;\n } else {\n flags.set(name, true);\n }\n }\n return { positional, flags };\n};\n\nexport {\n CODING_AGENTS,\n detectAgentKind,\n findCommand,\n isDirectInvocation,\n runCodingAgent,\n} from \"./agents\";\nexport type { CodingAgent } from \"./agents\";\n\nconst flagText = (flags: Parsed[\"flags\"], name: string) => {\n const value = flags.get(name);\n return typeof value === \"string\" ? value : undefined;\n};\n\nconst fail = (message: string): never => {\n process.stderr.write(`setup-agent: ${message}\\n`);\n return process.exit(1);\n};\n\nconst print = (value: unknown) => {\n process.stdout.write(`${JSON.stringify(value)}\\n`);\n};\n\n/** The agents `setup-agent <url>` can start itself, for a person who ran it in a terminal. */\nconst LAUNCHERS = CODING_AGENTS.filter(\n (\n agent,\n ): agent is CodingAgent & { launch: NonNullable<CodingAgent[\"launch\"]> } =>\n agent.launch !== undefined,\n);\n\nconst chooseLauncher = async () => {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n process.stdout.write(\n \"This command is meant for a coding agent, and none is running here.\\nStart one to do the install:\\n\",\n );\n LAUNCHERS.forEach((launcher, index) => {\n process.stdout.write(` ${index + 1}. ${launcher.name}\\n`);\n });\n while (true) {\n const answer = (\n await rl.question(\"Choose [1]: \").catch(() => process.exit(1))\n ).trim();\n if (answer === \"\") return LAUNCHERS[0]!;\n const byNumber = LAUNCHERS[Number(answer) - 1];\n if (byNumber) return byNumber;\n const byName = LAUNCHERS.find(\n (launcher) =>\n launcher.kind === answer.toLowerCase() ||\n launcher.name.toLowerCase() === answer.toLowerCase(),\n );\n if (byName) return byName;\n process.stdout.write(\"Enter a number from the list.\\n\");\n }\n } finally {\n rl.close();\n }\n};\n\nconst launchAgent = async (url: string) => {\n const launcher = await chooseLauncher();\n const prompt = `Run \\`npx setup-agent ${shellWord(url)}\\` and follow the instructions it prints.`;\n process.stdout.write(`\\nStarting ${launcher.name}...\\n\\n`);\n const code = await runCodingAgent(launcher, prompt);\n if (code === undefined) {\n process.stderr.write(\n `${launcher.name} is not installed. Install it with:\\n ${launcher.launch.install}\\nthen run this command again.\\n`,\n );\n }\n // Exit once stdout and stderr have drained.\n process.exitCode = code ?? 1;\n};\n\nconst requireState = (client: CheckoutClient): Checkout.State =>\n client.state ?? fail(\"the setup has no state\");\n\nconst summary = (state: Checkout.State) => {\n const plan = currentPlan(state);\n return {\n status: state.status,\n agentConnected: isAgentPresent(state),\n products: state.products,\n instructions: state.instructions,\n plan:\n plan === undefined\n ? null\n : {\n revision: plan.revision,\n status: plan.status,\n ...(plan.feedback !== undefined && { feedback: plan.feedback }),\n },\n progress: stepProgress(state),\n steps: state.steps,\n inputs: state.inputs,\n log: state.log.slice(-20),\n pendingMessages: state.log.filter(\n (entry) => entry.role === \"user\" && entry.acknowledgedAt === undefined,\n ),\n };\n};\n\nconst STEP_STATUSES: readonly Checkout.StepStatus[] = [\n \"pending\",\n \"active\",\n \"done\",\n \"skipped\",\n \"blocked\",\n];\n\n/** Resolves once a setup is planning or installing, or once the one being waited on is cancelled; a setup already closed when the wait begins is over, so the wait is for the next one. */\nexport const waitForStart = (\n client: Pick<CheckoutClient, \"state\" | \"subscribe\">,\n) =>\n new Promise<Checkout.Status>((resolve) => {\n let previous: Checkout.Status | undefined;\n const check = () => {\n const state = client.state;\n if (state === undefined) return;\n const started =\n state.status === \"planning\" || state.status === \"installing\";\n const ended = isClosed(state) && previous === \"waiting\";\n previous = state.status;\n if (!started && !ended) return;\n unsubscribe();\n resolve(state.status);\n };\n const unsubscribe = client.subscribe(check);\n check();\n });\n\nconst waitForInput = (client: CheckoutClient, inputId: string) =>\n new Promise<Checkout.Input>((resolve) => {\n const check = () => {\n const state = client.state;\n const input = state?.inputs.find((candidate) => candidate.id === inputId);\n if (!input) return;\n if (input.status !== \"open\" || state?.status === \"cancelled\") {\n unsubscribe();\n resolve(input);\n }\n };\n const unsubscribe = client.subscribe(check);\n check();\n });\n\nconst waitForPlan = (client: CheckoutClient, revision: number) =>\n new Promise<Checkout.Plan | undefined>((resolve) => {\n const check = () => {\n const state = client.state;\n const plan = state?.plans.find(\n (candidate) => candidate.revision === revision,\n );\n if (\n plan === undefined ||\n plan.status !== \"proposed\" ||\n state?.status === \"cancelled\"\n ) {\n unsubscribe();\n resolve(plan);\n }\n };\n const unsubscribe = client.subscribe(check);\n check();\n });\n\n/** The seconds `--timeout` asks a wait to block for; 0 is no limit. */\nconst waitSeconds = (flags: Parsed[\"flags\"], fallback: number) => {\n const value = flagText(flags, \"timeout\");\n if (value === undefined) return fallback;\n const seconds = Number(value);\n if (!/^\\d+(?:\\.\\d+)?$/.test(value) || !Number.isFinite(seconds)) {\n return fail(\"--timeout takes a number of seconds, 0 for no limit\");\n }\n return seconds;\n};\n\nconst TIMED_OUT = Symbol(\"timed out\");\n\n/** `promise`, or `TIMED_OUT` once `seconds` pass first; 0 waits as long as it takes. */\nconst within = async <T>(\n promise: Promise<T>,\n seconds: number,\n): Promise<T | typeof TIMED_OUT> => {\n if (seconds === 0) return promise;\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<typeof TIMED_OUT>((resolve) => {\n timer = setTimeout(() => resolve(TIMED_OUT), seconds * 1000);\n }),\n ]);\n } finally {\n clearTimeout(timer);\n }\n};\n\n/** What a wait prints when the user has not decided yet: the command to run again, now. */\nconst stillWaiting = (target: string, next: string) => ({\n ok: false,\n status: \"waiting\",\n waitingFor: target,\n next,\n message: `The user has not answered yet. Run \"next\" now to keep waiting (it only waits; do not repeat the command you ran, which would ask or submit again), or go on with other work and run it when you need the answer. Do not end your turn: nothing resumes you until you run it.`,\n});\n\nconst decided = (plan: Checkout.Plan | undefined, state: Checkout.State) => {\n if (state.status === \"cancelled\") return { ok: false, status: \"cancelled\" };\n if (plan === undefined) return fail(\"the plan disappeared\");\n return plan.status === \"approved\"\n ? { ok: true, decision: \"approve\", revision: plan.revision }\n : {\n ok: true,\n decision: \"revise\",\n revision: plan.revision,\n feedback: plan.feedback ?? \"\",\n };\n};\n\nconst splitList = (value: string | undefined) =>\n value\n ?.split(\",\")\n .map((entry) => entry.trim())\n .filter(Boolean);\n\nexport const askSeed = (\n rest: readonly string[],\n flags: Parsed[\"flags\"],\n): Checkout.InputSeed & { stepId?: string } => {\n const stepId = flagText(flags, \"step\");\n const optional = flags.has(\"optional\");\n const fallback = flagText(flags, \"default\");\n if (flags.has(\"entry-points\")) {\n if (\n [\n \"preset\",\n \"product\",\n \"choices\",\n \"icons\",\n \"multiple\",\n \"single\",\n \"only\",\n \"placeholder\",\n ].some((flag) => flags.has(flag))\n )\n return fail(\n \"--entry-points cannot be combined with other input kinds, --multiple, --single or --only\",\n );\n const json = flagText(flags, \"entry-points\");\n let value: unknown;\n try {\n value = JSON.parse(json ?? \"\");\n } catch {\n return fail(\"--entry-points needs a JSON array of options\");\n }\n const options = parseEntryPointOptions(value);\n if (!options)\n return fail(\n \"--entry-points needs 1–3 unique options with id, label, description and entryPoint {formFactor, placement, trigger, recommended?}; at most one is recommended\",\n );\n if (\n fallback !== undefined &&\n !options.some((option) => option.id === fallback)\n )\n return fail(`no option \"${fallback}\" to default to`);\n const prompt =\n rest[0] ?? fail(\"usage: ask <prompt> --entry-points '<JSON array>'\");\n if (prompt.trim() === \"\")\n return fail(\"an entry-point prompt cannot be empty\");\n return {\n kind: \"entry-point\",\n prompt,\n options,\n ...(fallback !== undefined && { default: fallback }),\n optional,\n ...(stepId !== undefined && { stepId }),\n };\n }\n const preset = flagText(flags, \"preset\");\n if (preset !== undefined) {\n if (!isPresetId(preset)) {\n return fail(\n `unknown preset \"${preset}\"; use ${Object.keys(INPUT_PRESETS).join(\" or \")}`,\n );\n }\n const only = splitList(flagText(flags, \"only\"));\n const found = splitList(flagText(flags, \"choices\"))?.map((entry) => {\n const [id = \"\", ...description] = entry.split(\"=\");\n return description.length === 0\n ? { id }\n : { id, description: description.join(\"=\") };\n });\n try {\n const seed = presetInput(preset, {\n ...(only !== undefined && { only }),\n ...(found !== undefined && { found }),\n ...(fallback !== undefined && { default: fallback }),\n optional,\n ...(stepId !== undefined && { stepId }),\n });\n return rest[0] === undefined ? seed : { ...seed, prompt: rest[0] };\n } catch (error) {\n return fail(error instanceof Error ? error.message : String(error));\n }\n }\n const prompt = rest[0] ?? fail(\"usage: ask <prompt>\");\n const product = flagText(flags, \"product\");\n if (product !== undefined) {\n return {\n kind: \"product\",\n prompt,\n product,\n optional,\n ...(stepId !== undefined && { stepId }),\n };\n }\n const choices = splitList(flagText(flags, \"choices\"));\n if (choices !== undefined) {\n if (flags.has(\"single\") && flags.has(\"multiple\"))\n return fail(\"--single and --multiple exclude each other\");\n const icons = new Map<string, string>();\n for (const entry of splitList(flagText(flags, \"icons\")) ?? []) {\n const [id = \"\", icon = \"\"] = entry.split(\"=\", 2);\n if (!choices.includes(id))\n return fail(`no choice \"${id}\" to put an icon on`);\n if (!isOptionIcon(icon)) {\n return fail(\n `unknown icon \"${icon}\"; pick one of ${OPTION_ICONS.join(\", \")}`,\n );\n }\n icons.set(id, icon);\n }\n const missing = choices.filter((id) => !icons.has(id));\n if (missing.length > 0) {\n return fail(\n `every option needs an icon; ${missing.map((id) => `\"${id}\"`).join(\", \")} ${missing.length === 1 ? \"has\" : \"have\"} none. Pass --icons \"${choices.map((id) => `${id}=<icon>`).join(\",\")}\" with icons from: ${OPTION_ICONS.join(\", \")}`,\n );\n }\n return {\n kind: \"choice\",\n prompt,\n options: choices.map((id) => ({ id, label: id, icon: icons.get(id)! })),\n ...(!flags.has(\"single\") && { multiple: true }),\n ...(fallback !== undefined && { default: fallback }),\n optional,\n ...(stepId !== undefined && { stepId }),\n };\n }\n const placeholder = flagText(flags, \"placeholder\");\n return {\n prompt,\n ...(placeholder !== undefined && { placeholder }),\n optional,\n ...(stepId !== undefined && { stepId }),\n };\n};\n\nconst assertEnvKey = (key: string) => {\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {\n throw new Error(`\"${key}\" is not an environment variable name`);\n }\n};\n\n/** Sets `key=value` in dotenv text, replacing an existing line for the key. */\nexport const upsertEnvLine = (content: string, key: string, value: string) => {\n assertEnvKey(key);\n // A line break or other control character would end the line and let the\n // value write lines of its own.\n const isLineBreaking = (code: number) =>\n code < 0x20 ||\n (code >= 0x7f && code <= 0x9f) ||\n code === 0x2028 ||\n code === 0x2029;\n if ([...value].some((char) => isLineBreaking(char.charCodeAt(0)))) {\n throw new Error(`the value for ${key} must be a single line`);\n }\n const line = `${key}=${value}`;\n const lines = content === \"\" ? [] : content.replace(/\\n$/, \"\").split(\"\\n\");\n const index = lines.findIndex(\n (entry) =>\n /^(?:export\\s+)?([A-Za-z_][A-Za-z0-9_]*)\\s*=/.exec(entry)?.[1] === key,\n );\n if (index === -1) lines.push(line);\n else lines[index] = line;\n return `${lines.join(\"\\n\")}\\n`;\n};\n\n/**\n * Write the secret `take` returns to `file` under `key`, leaving the file\n * owner-only. The file is opened before `take` runs, because taking the\n * secret consumes it and an unwritable destination must fail while it is\n * still deposited; it is read after, so lines written meanwhile survive.\n */\nexport const writeEnvSecret = async (\n file: string,\n key: string,\n take: () => Promise<string>,\n projectRoot = process.cwd(),\n) => {\n assertEnvKey(key);\n try {\n if ((await lstat(file)).isSymbolicLink()) {\n throw new Error(\n `${file} is a symbolic link; pass --file with its target to write there`,\n );\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n }\n const projectRelative = relative(projectRoot, file);\n if (\n projectRelative !== \"..\" &&\n !projectRelative.startsWith(`..${sep}`) &&\n !isAbsolute(projectRelative)\n ) {\n const root = await realpath(projectRoot);\n const parent = await realpath(dirname(file));\n const parentRelative = relative(root, parent);\n if (\n parentRelative === \"..\" ||\n parentRelative.startsWith(`..${sep}`) ||\n isAbsolute(parentRelative)\n ) {\n throw new Error(`${file} has a parent directory outside the project`);\n }\n }\n const handle = await open(\n file,\n constants.O_RDWR |\n constants.O_APPEND |\n constants.O_CREAT |\n constants.O_NOFOLLOW,\n 0o600,\n ).catch((error: NodeJS.ErrnoException) => {\n if (error.code === \"ELOOP\")\n throw new Error(\n `${file} is a symbolic link; pass --file with its target to write there`,\n );\n throw error;\n });\n try {\n await handle.chmod(0o600);\n const value = await take();\n const current = await handle.readFile(\"utf8\");\n // Built before truncating, so a refused value leaves the file intact.\n const next = upsertEnvLine(current, key, value);\n await handle.truncate(0);\n await handle.writeFile(next);\n } finally {\n await handle.close();\n }\n};\n\nconst redeemKeyDirectories = () => {\n const stateHome =\n process.platform === \"win32\"\n ? process.env.LOCALAPPDATA || join(homedir(), \"AppData\", \"Local\")\n : process.env.XDG_STATE_HOME || join(homedir(), \".local\", \"state\");\n return [\n join(stateHome, \"setup-agent\"),\n join(\n tmpdir(),\n `setup-agent-${process.platform === \"win32\" ? userInfo().username : process.getuid!()}`,\n ),\n ];\n};\n\nconst checkRedeemKeyDirectory = async (directory: string) => {\n if (!isAbsolute(directory)) {\n throw new Error(\"state home must be an absolute path\");\n }\n const info = await lstat(directory);\n if (!info.isDirectory() || info.isSymbolicLink()) {\n throw new Error(\n `redeem key directory must not be a symbolic link: ${directory}`,\n );\n }\n if (process.platform !== \"win32\") {\n if (info.uid !== process.getuid!()) {\n throw new Error(\n `redeem key directory must be owned by the user: ${directory}`,\n );\n }\n if ((info.mode & 0o077) !== 0) await chmod(directory, 0o700);\n }\n};\n\nconst prepareRedeemKeyDirectory = async (directory: string) => {\n if (isAbsolute(directory)) {\n await mkdir(directory, { recursive: true, mode: 0o700 });\n }\n await checkRedeemKeyDirectory(directory);\n};\n\nconst saveRedeemKey = async (hash: string, key: string) => {\n let lastError: unknown;\n for (const directory of redeemKeyDirectories()) {\n const file = join(directory, `${hash}.key`);\n let created = false;\n try {\n await prepareRedeemKeyDirectory(directory);\n const handle = await open(\n file,\n constants.O_WRONLY |\n constants.O_CREAT |\n constants.O_EXCL |\n constants.O_NOFOLLOW,\n 0o600,\n );\n created = true;\n try {\n await handle.writeFile(key);\n } finally {\n await handle.close();\n }\n return file;\n } catch (error) {\n if (created) await unlink(file).catch(() => {});\n lastError = error;\n }\n }\n throw lastError;\n};\n\nconst readRedeemKey = async (hash: string) => {\n if (!/^[0-9a-f]{64}$/.test(hash)) return undefined;\n for (const directory of redeemKeyDirectories()) {\n try {\n await checkRedeemKeyDirectory(directory);\n const file = join(directory, `${hash}.key`);\n const handle = await open(\n file,\n constants.O_RDONLY | constants.O_NOFOLLOW,\n );\n try {\n const info = await handle.stat();\n if (\n !info.isFile() ||\n (process.platform !== \"win32\" && (info.mode & 0o077) !== 0)\n ) {\n throw new Error(\"redeem key file must be private and regular\");\n }\n const key = await handle.readFile(\"utf8\");\n if (createHash(\"sha256\").update(key).digest(\"hex\") === hash) {\n return { key, file };\n }\n } finally {\n await handle.close();\n }\n } catch {\n continue;\n }\n }\n return undefined;\n};\n\nconst answered = (input: Checkout.Input) => ({\n ok: true,\n inputId: input.id,\n status: input.status,\n ...(input.answer !== undefined && answerFields(input, input.answer)),\n});\n\nexport type RunSetupCommandOptions = {\n /** How the agent invokes these commands, before the URL; `npx setup-agent` by default. */\n command?: string;\n /** Lets \"stream\" open the setup page when the setup waits on the user, as `--setup-app-url` does. */\n browser?: { setupAppUrl: string; canOpen?: boolean };\n};\n\nconst runStream = async (\n client: CheckoutClient,\n url: string,\n flags: Parsed[\"flags\"],\n options: RunSetupCommandOptions,\n) => {\n const setupAppUrl =\n options.browser?.setupAppUrl ?? flagText(flags, \"setup-app-url\");\n const linkId = new URL(url).pathname.split(\"/\").filter(Boolean).at(-1);\n const stream = await followSetup(client, {\n ...(setupAppUrl !== undefined &&\n linkId !== undefined && {\n browser: {\n setupAppUrl: parseSetupUrl(setupAppUrl),\n linkId,\n answerCommand: `${options.command ?? DEFAULT_COMMAND} ${shellWord(url)} answer`,\n canOpen: (options.browser?.canOpen ?? true) && !flags.has(\"no-open\"),\n },\n }),\n });\n const stop = () => {\n void stream.stop().finally(() => {\n client.dispose();\n process.exit(0);\n });\n };\n process.on(\"SIGHUP\", stop);\n process.on(\"SIGINT\", stop);\n process.on(\"SIGTERM\", stop);\n await new Promise(() => {});\n};\n\nexport const main = (argv: readonly string[]) => runSetupCommand(argv);\n\n/**\n * Runs one setup command (`<url> <command> ...`) the way `npx setup-agent`\n * does, for a CLI that embeds setup as its own command.\n */\nexport const runSetupCommand = async (\n argv: readonly string[],\n options: RunSetupCommandOptions = {},\n) => {\n const { positional, flags } = parseArgs(argv);\n const [first, command = \"intro\", ...rest] = positional;\n if (flags.has(\"help\") || flags.has(\"h\") || first === undefined) {\n process.stdout.write(HELP);\n return process.exit(first === undefined ? 1 : 0);\n }\n let url: string;\n try {\n url = parseSetupUrl(first);\n } catch (error) {\n return fail(error instanceof Error ? error.message : String(error));\n }\n if (command === \"intro\" && isDirectInvocation()) return launchAgent(url);\n\n const client = await connectCheckout(url).catch((error: unknown) =>\n fail(error instanceof Error ? error.message : String(error)),\n );\n const commands = client.commands;\n const waitCommand = (target: string) =>\n `${options.command ?? DEFAULT_COMMAND} ${shellWord(url)} wait ${target}`;\n\n try {\n switch (command) {\n case \"intro\": {\n const kind = detectAgentKind();\n await commands[\"agent/intro\"]({\n ...(kind !== undefined && { kind }),\n }).catch(() => {});\n process.stdout.write(\n agentInstructions(url, {\n ...(options.command !== undefined && { command: options.command }),\n }),\n );\n process.stdout.write(\"\\nCurrent setup:\\n\");\n print(summary(requireState(client)));\n break;\n }\n case \"status\": {\n print(summary(requireState(client)));\n break;\n }\n case \"stream\": {\n await runStream(client, url, flags, options);\n break;\n }\n case \"answer\": {\n const id =\n rest[0] ??\n fail(\"usage: answer <input-id|plan|finish> <answer> [--note <text>]\");\n const note = flagText(flags, \"note\");\n const feedback = flagText(flags, \"feedback\");\n print(\n await answerNeed(client, id, rest[1], {\n ...(note !== undefined && { note }),\n ...(feedback !== undefined && { feedback }),\n ...(flags.has(\"dismiss\") && { dismiss: true }),\n }),\n );\n break;\n }\n case \"step\": {\n const usage =\n \"usage: step <step-id> <pending|active|done|skipped|blocked>\";\n const stepId = rest[0] ?? fail(usage);\n const status = rest[1];\n if (!STEP_STATUSES.includes(status as Checkout.StepStatus)) fail(usage);\n const note = flagText(flags, \"note\");\n await commands[\"agent/step\"]({\n stepId,\n status: status as Checkout.StepStatus,\n ...(note !== undefined && { note }),\n });\n print({ ok: true, stepId, status });\n break;\n }\n case \"add-step\": {\n const title = rest[0] ?? fail(\"usage: add-step <title>\");\n const detail = flagText(flags, \"detail\");\n const product = flagText(flags, \"product\");\n const result = await commands[\"agent/add-step\"]({\n title,\n ...(detail !== undefined && { detail }),\n ...(product !== undefined && { product }),\n ...(flags.has(\"active\") && { active: true }),\n });\n print({ ok: true, ...result });\n break;\n }\n case \"plan\": {\n const file = flagText(flags, \"file\");\n const markdown =\n file !== undefined\n ? await readFile(resolve(file), \"utf8\").catch(() =>\n fail(`could not read ${file}`),\n )\n : (rest[0] ?? fail(\"usage: plan <markdown> | plan --file <path>\"));\n const { revision } = await commands[\"agent/plan\"]({ markdown });\n if (!flags.has(\"wait\")) {\n print({ ok: true, revision });\n break;\n }\n const plan = await within(\n waitForPlan(client, revision),\n waitSeconds(flags, DEFAULT_WAIT_SECONDS),\n );\n print(\n plan === TIMED_OUT\n ? stillWaiting(\"plan\", waitCommand(\"plan\"))\n : decided(plan, requireState(client)),\n );\n break;\n }\n case \"ask\": {\n const seed = askSeed(rest, flags);\n const redeemKey =\n seed.kind === \"model\" ? randomBytes(32).toString(\"hex\") : undefined;\n const redeemKeyHash =\n redeemKey === undefined\n ? undefined\n : createHash(\"sha256\").update(redeemKey).digest(\"hex\");\n const keyFile =\n redeemKey === undefined || redeemKeyHash === undefined\n ? undefined\n : await saveRedeemKey(redeemKeyHash, redeemKey).catch(\n (error: unknown) =>\n fail(\n `could not store the model redeem key: ${error instanceof Error ? error.message : String(error)}`,\n ),\n );\n let inputId: string;\n try {\n ({ inputId } = await commands[\"agent/ask\"]({\n ...seed,\n ...(redeemKeyHash !== undefined && { redeemKeyHash }),\n }));\n } catch (error) {\n if (keyFile !== undefined) await unlink(keyFile).catch(() => {});\n throw error;\n }\n if (!flags.has(\"wait\")) {\n print({ ok: true, inputId });\n break;\n }\n const input = await within(\n waitForInput(client, inputId),\n waitSeconds(flags, DEFAULT_WAIT_SECONDS),\n );\n print(\n input === TIMED_OUT\n ? stillWaiting(inputId, waitCommand(inputId))\n : answered(input),\n );\n break;\n }\n case \"wait\": {\n const target =\n rest[0] ?? fail(\"usage: wait start | wait <input-id> | wait plan\");\n const state = requireState(client);\n if (target === \"start\") {\n // Waiting for the user's next setup is idle time, so it has no limit by default.\n const status = await within(\n waitForStart(client),\n waitSeconds(flags, 0),\n );\n if (status === TIMED_OUT) {\n print(stillWaiting(\"start\", waitCommand(\"start\")));\n break;\n }\n print({\n ok: status === \"planning\" || status === \"installing\",\n status,\n });\n break;\n }\n if (target === \"plan\") {\n const plan = currentPlan(state) ?? fail(\"no plan has been proposed\");\n const decision = await within(\n waitForPlan(client, plan.revision),\n waitSeconds(flags, DEFAULT_WAIT_SECONDS),\n );\n print(\n decision === TIMED_OUT\n ? stillWaiting(\"plan\", waitCommand(\"plan\"))\n : decided(decision, requireState(client)),\n );\n break;\n }\n if (!state.inputs.some((input) => input.id === target)) {\n fail(`no input \"${target}\"`);\n }\n const input = await within(\n waitForInput(client, target),\n waitSeconds(flags, DEFAULT_WAIT_SECONDS),\n );\n print(\n input === TIMED_OUT\n ? stillWaiting(target, waitCommand(target))\n : answered(input),\n );\n break;\n }\n case \"env\": {\n const usage = \"usage: env <input-id> <ENV_VAR> [--file <path>]\";\n const inputId = rest[0] ?? fail(usage);\n const envKey = rest[1] ?? fail(usage);\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(envKey)) {\n fail(`\"${envKey}\" is not an environment variable name`);\n }\n const state = requireState(client);\n const input =\n state.inputs.find((candidate) => candidate.id === inputId) ??\n fail(`no input \"${inputId}\"`);\n const setupId = state.id ?? fail(\"the setup has not been created yet\");\n if (isClosed(state)) {\n fail(\"the setup is not open\");\n }\n if (input.status !== \"answered\") {\n fail(`input \"${inputId}\" is ${input.status}`);\n }\n if (\n input.kind !== \"model\" ||\n !isValidModelAnswer(input, input.answer ?? \"\")\n ) {\n fail(`input \"${inputId}\" is not an llm-provider answer`);\n }\n const storedKey =\n input.redeemKeyHash === undefined\n ? undefined\n : await readRedeemKey(input.redeemKeyHash);\n if (input.redeemKeyHash !== undefined && storedKey === undefined) {\n fail(`no local redeem key for \"${inputId}\"`);\n }\n const file = resolve(flagText(flags, \"file\") ?? \".env.local\");\n await writeEnvSecret(file, envKey, async () => {\n const secretUrl = new URL(url);\n if (\n secretUrl.protocol !== \"https:\" &&\n !(\n secretUrl.protocol === \"http:\" &&\n isLoopbackHostname(secretUrl.hostname)\n )\n ) {\n fail(\"the key is only fetched over https\");\n }\n const response = await fetch(\n `${url.replace(/\\/$/, \"\")}/secret/${encodeURIComponent(inputId)}?setup=${encodeURIComponent(setupId)}`,\n storedKey === undefined\n ? undefined\n : { headers: { \"Setup-Agent-Redeem-Key\": storedKey.key } },\n );\n if (response.status === 403) {\n fail(`redeem key rejected for \"${inputId}\"`);\n }\n if (response.status === 404) {\n fail(\n `the key for \"${inputId}\" was already taken or never deposited`,\n );\n }\n if (!response.ok) fail(`could not fetch the key: ${response.status}`);\n const key = await response.text();\n // An API key is one word of letters, digits and a few separators;\n // anything else could break out of the env line, be read differently\n // by dotenv, or run as shell syntax when the file is sourced.\n if (!/^[A-Za-z0-9._~+/=:@%,-]+$/.test(key)) {\n fail(\"the deposited key is not a valid API key\");\n }\n return key;\n });\n if (storedKey !== undefined) {\n await unlink(storedKey.file).catch((error: NodeJS.ErrnoException) => {\n if (error.code !== \"ENOENT\") {\n process.stderr.write(\n `warning: could not remove a redeem key: ${error.message}\\n`,\n );\n }\n });\n }\n print({ ok: true, envKey, file });\n break;\n }\n case \"ack\": {\n const messageId = rest[0] ?? fail(\"usage: ack <message-id>\");\n await commands[\"agent/ack\"]({ messageId });\n print({ ok: true });\n break;\n }\n case \"log\": {\n const text = rest[0] ?? fail(\"usage: log <text>\");\n await commands[\"agent/log\"]({ text });\n print({ ok: true });\n break;\n }\n case \"done\": {\n const summary = flagText(flags, \"summary\");\n const preview = flagText(flags, \"preview\");\n await commands[\"agent/done\"]({\n ...(summary !== undefined && { summary }),\n ...(preview !== undefined && { preview }),\n });\n print({\n ok: true,\n status: \"proposed\",\n next: \"Keep the stream running and stay idle. The user closes the setup or sends a follow-up message.\",\n });\n break;\n }\n default:\n fail(`unknown command \"${command}\"\\n\\n${HELP}`);\n }\n } catch (error) {\n fail(error instanceof Error ? error.message : String(error));\n } finally {\n client.dispose();\n }\n};\n"],"mappings":"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"}