From 490391dd73498b5be3c54052786ec03dda283efb Mon Sep 17 00:00:00 2001 From: Brandon Corbett Date: Fri, 31 Jul 2026 23:50:12 -0400 Subject: [PATCH] feat: add a non-interactive seamless init `--yes` (-y) answers every question with the option the prompt marks as recommended, so a scaffold runs from CI, a Dockerfile, or a script with no terminal attached. Each question also gets its own flag, honored with or without --yes: --web and --api choose the starters (by id or alias), --email sets the owner who becomes the admin, --auth picks how the auth server runs, and --admin picks where the console is hosted. Values are validated against the registry and the known modes in runCLI, before a directory is created. --yes stops rather than guessing in three places. Managed or local needs --app or --local, because that decides where the project's auth lives for good. Scaffolding into a non-empty directory needs --force, because starter files overwrite anything with the same name. Rotating an application's existing service token needs --force, because it breaks whatever is deployed on the old one. init's argument parsing moves into parseInitArgs so the new switches are not mistaken for template flags or for the project name. Closes #152 --- .changeset/init-non-interactive.md | 23 +++ AGENTS.md | 7 + README.md | 33 ++++ src/commands/helpTopics.ts | 37 +++- src/commands/init.test.ts | 216 ++++++++++++++++++++- src/commands/init.ts | 219 ++++++++++++++++++--- src/index.test.ts | 55 +++++- src/index.ts | 62 ++++-- src/prompts/projectSetup.test.ts | 106 +++++++++++ src/prompts/projectSetup.ts | 295 +++++++++++++++++++---------- 10 files changed, 904 insertions(+), 149 deletions(-) create mode 100644 .changeset/init-non-interactive.md diff --git a/.changeset/init-non-interactive.md b/.changeset/init-non-interactive.md new file mode 100644 index 0000000..38db869 --- /dev/null +++ b/.changeset/init-non-interactive.md @@ -0,0 +1,23 @@ +--- +"seamless-cli": minor +--- + +Add a non-interactive `seamless init`. `--yes` (`-y`) answers every question with the option the +prompt marks as recommended, so a scaffold runs from CI, a Dockerfile, or a script with no terminal +attached: + +```bash +seamless init my-app --local --yes --email=you@example.com +``` + +Each question also gets its own flag, honored with or without `--yes`: `--web=` and +`--api=` choose the starters, `--email=
` sets the owner who becomes the admin, +`--auth=` picks how the auth server runs, and `--admin=` picks +where the admin console is hosted. Unspecified values fall back to the recommended option, except +the owner email, which has no safe default and is taken from `--email` or the portal session. + +`--yes` deliberately stops rather than guessing in three places. Choosing between a managed +application and a local stack needs `--app ` or `--local`. Scaffolding into a directory that is +not empty needs `--force`, since starter files overwrite anything with the same name. Rotating a +managed application's existing service token needs `--force` too, because it breaks whatever is +already deployed on the old one. diff --git a/AGENTS.md b/AGENTS.md index 0a042bc..027f0d3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -77,6 +77,13 @@ The entry point is [src/index.ts](src/index.ts), which dispatches to a command m registry, so no per-flag code. `resolveTemplateAliases` runs in `runCLI` before the project directory is created and before the non-empty-directory confirmation, so an unknown flag can never route through a destructive prompt on its way to an error. + - `--yes` runs the whole thing without prompting: every question has a flag (`--web`, `--api`, + `--email`, `--auth`, `--admin`) and anything unspecified falls back to the option the prompt + marks "(recommended)". `--yes` is never enough for a destructive step: overwriting a non-empty + directory and rotating an existing service token both require `--force`, and choosing between a + managed application and a local stack requires `--app` or `--local`. Flag parsing lives in + `parseInitArgs` ([src/index.ts](src/index.ts)); everything it produces is validated in `runCLI` + before a directory is created. - **templates** ([src/commands/templates.ts](src/commands/templates.ts)) lists the registry (`seamless templates list [--json]`) so those ids and flags are discoverable without a checkout. It reads the same source `init` does and needs no login. diff --git a/README.md b/README.md index b2212c4..842b4b9 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,39 @@ registry `init` does, so `SEAMLESS_TEMPLATES_DIR` and `SEAMLESS_TEMPLATES_REF` a --- +## Scripting init + +`--yes` answers every question with the recommended option instead of prompting, so `init` runs from +CI, a Dockerfile, or a script with no terminal attached: + +```bash +seamless init my-app --local --yes --email=you@example.com +``` + +Each question also has its own flag, honored with or without `--yes`: + +| Flag | Question | Default under `--yes` | +| --- | --- | --- | +| `--web=` | Web example | first selectable web template | +| `--api=` | Backend framework | first selectable api template | +| `--email=
` | Owner email (becomes the admin) | required | +| `--auth=` | How the auth server runs | `docker` | +| `--admin=` | Where the admin console is hosted | `api` | + +Two things `--yes` deliberately will not decide for you: + +- **Managed or local.** With a portal session and neither `--local` nor `--app `, `init` stops + rather than guessing where the project's auth lives. +- **Anything destructive.** Scaffolding into a directory that is not empty (starter files overwrite + anything with the same name) and rotating a managed application's existing service token (which + breaks whatever is deployed on the old one) both take `--force`, not `--yes`. + +`--email` has no safe default, so under `--yes` it is required unless `seamless login` has left a +portal session to take it from. Templates that would prompt for OAuth provider credentials are +scaffolded with none configured; add them afterwards with `seamless config oauth-providers add`. + +--- + ## What gets created Depending on your selections, the CLI generates a project like this: diff --git a/src/commands/helpTopics.ts b/src/commands/helpTopics.ts index 158c83b..617dba9 100644 --- a/src/commands/helpTopics.ts +++ b/src/commands/helpTopics.ts @@ -16,7 +16,10 @@ export interface CommandHelp { export const COMMAND_HELP: CommandHelp[] = [ { name: "init", - usage: ["seamless init [project-name] [--]"], + usage: [ + "seamless init [project-name] [--