create-avocado-site 0.16.0 → 0.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +24 -0
  2. package/assets/generated-images/gen_1789981693445_284abc56.jpg +0 -0
  3. package/assets/generated-images/gen_1789981703140_99ea2d8b.jpg +0 -0
  4. package/assets/generated-images/gen_1789981712535_d49ec7bb.jpg +0 -0
  5. package/assets/generated-images/gen_1789981721898_119231c3.jpg +0 -0
  6. package/assets/generated-images/gen_1789981730383_637ea1b8.jpg +0 -0
  7. package/assets/generated-images/gen_1789981739789_5cc514da.jpg +0 -0
  8. package/assets/generated-images/gen_1789981749474_0be65523.jpg +0 -0
  9. package/assets/generated-images/gen_1789982485512_1932c58c.jpg +0 -0
  10. package/assets/generated-images/gen_1789982495151_4a0113e0.jpg +0 -0
  11. package/assets/generated-images/gen_1789982505374_5c12f604.jpg +0 -0
  12. package/assets/generated-images/gen_1789982517293_ed7fc0f2.jpg +0 -0
  13. package/assets/generated-images/gen_1789982525626_5468907f.jpg +0 -0
  14. package/assets/generated-images/gen_1789982534675_6d1b122e.jpg +0 -0
  15. package/assets/generated-images/gen_1789982542619_faba5835.jpg +0 -0
  16. package/assets/generated-images/gen_1789982552275_17891fd1.jpg +0 -0
  17. package/assets/generated-images/gen_1789982561072_c3631ea6.jpg +0 -0
  18. package/assets/generated-images/gen_1789982570680_82f806c3.jpg +0 -0
  19. package/assets/generated-images/gen_1789982580474_c3304b74.jpg +0 -0
  20. package/assets/generated-images/gen_1789982589383_87f4e381.jpg +0 -0
  21. package/assets/generated-images/gen_1789982599838_c8f89267.jpg +0 -0
  22. package/assets/generated-images/gen_1789982608948_b08d16ad.jpg +0 -0
  23. package/assets/generated-images/gen_1789982618063_c2d3fb42.jpg +0 -0
  24. package/assets/generated-images/gen_1789982626863_3c386cb6.jpg +0 -0
  25. package/assets/generated-images/gen_1789982636865_c9fdfa16.jpg +0 -0
  26. package/assets/generated-images/gen_1789982646478_0e7c60b3.jpg +0 -0
  27. package/assets/generated-images/gen_1789982655687_3d71a0cc.jpg +0 -0
  28. package/assets/generated-images/gen_1789982665153_e2ecd91f.jpg +0 -0
  29. package/assets/generated-images/gen_1789982674589_9bcabe11.jpg +0 -0
  30. package/assets/generated-images/gen_1789982683093_e92fc3ac.jpg +0 -0
  31. package/assets/generated-images/gen_1789982693035_b6d184c7.jpg +0 -0
  32. package/assets/generated-images/gen_1789982702257_3b108f7b.jpg +0 -0
  33. package/assets/generated-images/gen_1789982711758_9f480e3f.jpg +0 -0
  34. package/assets/generated-images/gen_1789982721489_4bf84186.jpg +0 -0
  35. package/assets/generated-images/gen_1789982730122_db36d0e4.jpg +0 -0
  36. package/assets/generated-images/gen_1789982740142_bea82ed7.jpg +0 -0
  37. package/assets/generated-images/gen_1789982748906_a97ffb7a.jpg +0 -0
  38. package/assets/generated-images/gen_1789982818435_30194053.jpg +0 -0
  39. package/assets/logos/avocado-hub.svg +7 -4
  40. package/dist/args.d.ts +42 -0
  41. package/dist/args.js +97 -0
  42. package/dist/index.js +77 -25
  43. package/dist/prompts.js +26 -6
  44. package/dist/templates/demo-content.d.ts +1 -1
  45. package/dist/templates/demo-content.js +1 -1
  46. package/dist/versions.d.ts +1 -1
  47. package/dist/versions.js +1 -1
  48. package/package.json +4 -4
  49. package/assets/generated-images/gen_1774991902955_0p1i2o.webp +0 -0
  50. package/assets/generated-images/gen_1788955681679_454dc316.webp +0 -0
  51. package/assets/generated-images/gen_1789906172816_d7d537c1.jpg +0 -0
  52. package/assets/generated-images/var_1773434968055_8g99nk.webp +0 -0
  53. package/assets/generated-images/var_1773435042084_wvet78.webp +0 -0
  54. package/assets/generated-images/var_1773435061562_6hf8s6.webp +0 -0
  55. package/assets/generated-images/var_1773435080764_6ocahu.webp +0 -0
  56. package/assets/generated-images/var_1773435101045_1y544z.webp +0 -0
  57. package/assets/generated-images/var_1776858715876_iuu72w.webp +0 -0
package/README.md CHANGED
@@ -55,6 +55,30 @@ For an existing site of any size this is the smaller half of the job. The
55
55
  larger half — your components, your content model, your CMS — is at
56
56
  [docs.avocadostudio.dev/sites](https://docs.avocadostudio.dev/sites).
57
57
 
58
+ ## Unattended runs
59
+
60
+ The demo path takes a directory name and needs no terminal, so it can be
61
+ scripted, run in CI, or baked into an image:
62
+
63
+ ```bash
64
+ npm create avocado-site@latest my-demo -- --yes --no-install
65
+ ```
66
+
67
+ | Option | |
68
+ |---|---|
69
+ | `-y`, `--yes` | Never ask. Implied whenever stdin is not a terminal. |
70
+ | `--no-install` | Write the files and stop, for callers that install their own way. |
71
+ | `--api-key KEY` | Write `KEY` to `.env.local`; the variable is chosen from its prefix. Also read from `AVOCADO_SETUP_API_KEY`. |
72
+ | `-h`, `--help` | Print the usage. |
73
+
74
+ A key is written only when you name it. A provider variable that happens to be
75
+ exported in your shell is left alone — it is usually your own credential, and a
76
+ scaffold is about to become a git repository.
77
+
78
+ Unattended runs never start the dev server; they print the command instead. A
79
+ run with no terminal and no directory name exits 1 and says why, because the
80
+ guided path has questions that nothing can answer for you.
81
+
58
82
  ## Requirements
59
83
 
60
84
  - Node.js 22+
@@ -1,7 +1,10 @@
1
1
  <svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="title desc">
2
2
  <title id="title">The Avocado Hub logo</title>
3
- <desc id="desc">Stylized avocado mark — dark green shell, light green flesh, brown pit.</desc>
4
- <path d="M64 8C88 8 112 40 112 72C112 104 88 120 64 120C40 120 16 104 16 72C16 40 40 8 64 8Z" fill="#1F7A3A"/>
5
- <path d="M64 18C82 18 100 44 100 72C100 100 82 110 64 110C46 110 28 100 28 72C28 44 46 18 64 18Z" fill="#7ED957"/>
6
- <circle cx="64" cy="72" r="20" fill="#8B5E3C"/>
3
+ <desc id="desc">Stylized avocado mark — brand green shell, warm cream flesh, muted brown stone.</desc>
4
+ <!-- One green, and it is the brand's. The mark previously used #1F7A3A over
5
+ #7ED957: two greens, neither of them the #0f766e on every button, and the
6
+ lime saturated well past anything else on the page. -->
7
+ <path d="M64 8C88 8 112 40 112 72C112 104 88 120 64 120C40 120 16 104 16 72C16 40 40 8 64 8Z" fill="#0f766e"/>
8
+ <path d="M64 18C82 18 100 44 100 72C100 100 82 110 64 110C46 110 28 100 28 72C28 44 46 18 64 18Z" fill="#e9e4d4"/>
9
+ <circle cx="64" cy="72" r="20" fill="#8a6a4f"/>
7
10
  </svg>
package/dist/args.d.ts ADDED
@@ -0,0 +1,42 @@
1
+ /**
2
+ * What the command was asked to do, decided before anything can block.
3
+ *
4
+ * This file exists because the scaffolder could not be run by anything that is
5
+ * not a person at a keyboard. The demo path asks for an API key and then, at
6
+ * the end, whether to start the dev server — both unconditional, both
7
+ * `@clack/prompts`. With stdin closed the first one cancels and the process
8
+ * exits 1 having created nothing; under a pty with a newline piped in it
9
+ * ignores the keystroke and waits forever. So no CI job, no Dockerfile, no
10
+ * agent and no gate could run `npm create avocado-site`, which is the one
11
+ * command every new user runs first. `scaffold-serve-check.mjs` generates from
12
+ * the scaffolder's *templates* rather than by running it, so nothing in the
13
+ * repo noticed.
14
+ *
15
+ * Parsing is separated from `main()` for the reason the prompts had to be: a
16
+ * pure function over `argv` and `env` can be tested without a terminal.
17
+ */
18
+ export type ParsedArgs = {
19
+ /** The directory to create. Its absence is what selects the guided path. */
20
+ target?: string;
21
+ /** No question may block: `--yes`, or stdin is not a terminal. */
22
+ nonInteractive: boolean;
23
+ /** `--no-install` writes the files and stops, for gates and images. */
24
+ install: boolean;
25
+ /** Only ever from an explicit flag or `AVOCADO_SETUP_API_KEY` — see below. */
26
+ apiKey?: {
27
+ variable: string;
28
+ value: string;
29
+ };
30
+ help: boolean;
31
+ unknown: string[];
32
+ };
33
+ export declare const USAGE = "Usage\n npm create avocado-site [directory] [options]\n\n With a directory name, bootstraps the demo into it. With none, asks what to\n build \u2014 which needs a terminal.\n\nOptions\n -y, --yes Never ask. Implied when stdin is not a terminal.\n --no-install Write the files, skip `npm install`.\n --api-key KEY Write KEY to .env.local. The variable is chosen from the\n key's prefix. Also read from AVOCADO_SETUP_API_KEY.\n -h, --help Show this.\n\nNon-interactive runs never start the dev server; the command to start it is\nprinted instead.";
34
+ export declare function parseArgs(argv: string[], env?: Record<string, string | undefined>, isTty?: boolean): ParsedArgs;
35
+ /**
36
+ * Why a run with no terminal and no directory name cannot continue.
37
+ *
38
+ * The guided path is four questions; there is no answer to guess at. What this
39
+ * must not do is what it used to: exit 1 with an empty directory listing and
40
+ * nothing on stderr, which reads as a crash rather than as a choice.
41
+ */
42
+ export declare const NEEDS_A_NAME = "create-avocado-site: no terminal, and no directory name.\n\nThe guided setup asks what to build, which needs a terminal. To run unattended,\nname the directory to create:\n\n npm create avocado-site my-site -- --yes\n\nUsage\n npm create avocado-site [directory] [options]\n\n With a directory name, bootstraps the demo into it. With none, asks what to\n build \u2014 which needs a terminal.\n\nOptions\n -y, --yes Never ask. Implied when stdin is not a terminal.\n --no-install Write the files, skip `npm install`.\n --api-key KEY Write KEY to .env.local. The variable is chosen from the\n key's prefix. Also read from AVOCADO_SETUP_API_KEY.\n -h, --help Show this.\n\nNon-interactive runs never start the dev server; the command to start it is\nprinted instead.";
package/dist/args.js ADDED
@@ -0,0 +1,97 @@
1
+ import { variableForKey } from "./prompts.js";
2
+ export const USAGE = `Usage
3
+ npm create avocado-site [directory] [options]
4
+
5
+ With a directory name, bootstraps the demo into it. With none, asks what to
6
+ build — which needs a terminal.
7
+
8
+ Options
9
+ -y, --yes Never ask. Implied when stdin is not a terminal.
10
+ --no-install Write the files, skip \`npm install\`.
11
+ --api-key KEY Write KEY to .env.local. The variable is chosen from the
12
+ key's prefix. Also read from AVOCADO_SETUP_API_KEY.
13
+ -h, --help Show this.
14
+
15
+ Non-interactive runs never start the dev server; the command to start it is
16
+ printed instead.`;
17
+ /**
18
+ * The key is taken only when it is named, never inherited from the shell.
19
+ *
20
+ * `--api-key` and `AVOCADO_SETUP_API_KEY` both say "put this in the project I
21
+ * am creating". A bare `ANTHROPIC_API_KEY` in the environment says nothing of
22
+ * the sort — it is usually the developer's own key, exported in a shell
23
+ * profile, and harvesting it would write a live credential into a new
24
+ * `.env.local` in a directory that is about to become a git repository. The
25
+ * interactive path has always required the key to be typed at it; the
26
+ * unattended path requires it to be named. Neither reads what happens to be
27
+ * lying around.
28
+ */
29
+ const API_KEY_ENV_VAR = "AVOCADO_SETUP_API_KEY";
30
+ export function parseArgs(argv, env = process.env, isTty = Boolean(process.stdin.isTTY)) {
31
+ let target;
32
+ let yes = false;
33
+ let install = true;
34
+ let help = false;
35
+ let rawKey;
36
+ const unknown = [];
37
+ for (let i = 0; i < argv.length; i++) {
38
+ const arg = argv[i];
39
+ /*
40
+ * `--api-key sk-ant-…` takes the next token, which is why the old
41
+ * one-liner could not survive a flag that takes a value: it picked the
42
+ * target with `find(arg => !arg.startsWith("-"))`, so the key itself would
43
+ * have become the directory name — and the scaffold would have been
44
+ * created in a directory named after a secret.
45
+ */
46
+ if (arg === "--api-key") {
47
+ rawKey = argv[++i];
48
+ continue;
49
+ }
50
+ if (arg.startsWith("--api-key=")) {
51
+ rawKey = arg.slice("--api-key=".length);
52
+ continue;
53
+ }
54
+ if (arg === "-y" || arg === "--yes") {
55
+ yes = true;
56
+ continue;
57
+ }
58
+ if (arg === "--no-install") {
59
+ install = false;
60
+ continue;
61
+ }
62
+ if (arg === "-h" || arg === "--help") {
63
+ help = true;
64
+ continue;
65
+ }
66
+ if (arg.startsWith("-")) {
67
+ unknown.push(arg);
68
+ continue;
69
+ }
70
+ target ??= arg;
71
+ }
72
+ const value = (rawKey ?? env[API_KEY_ENV_VAR] ?? "").trim();
73
+ const variable = value ? variableForKey(value) : null;
74
+ return {
75
+ target,
76
+ nonInteractive: yes || !isTty,
77
+ install,
78
+ apiKey: variable && value ? { variable, value } : undefined,
79
+ help,
80
+ unknown,
81
+ };
82
+ }
83
+ /**
84
+ * Why a run with no terminal and no directory name cannot continue.
85
+ *
86
+ * The guided path is four questions; there is no answer to guess at. What this
87
+ * must not do is what it used to: exit 1 with an empty directory listing and
88
+ * nothing on stderr, which reads as a crash rather than as a choice.
89
+ */
90
+ export const NEEDS_A_NAME = `create-avocado-site: no terminal, and no directory name.
91
+
92
+ The guided setup asks what to build, which needs a terminal. To run unattended,
93
+ name the directory to create:
94
+
95
+ npm create avocado-site my-site -- --yes
96
+
97
+ ${USAGE}`;
package/dist/index.js CHANGED
@@ -7,8 +7,33 @@ import { resolve } from "node:path";
7
7
  import { runPrompts, demoConfig, promptForApiKey, AVOCADO_INTRO } from "./prompts.js";
8
8
  import { collectFiles, collectDemoFiles, generateFiles } from "./generator.js";
9
9
  import { printInstructions } from "./instructions.js";
10
+ import { parseArgs, USAGE, NEEDS_A_NAME } from "./args.js";
10
11
  async function main() {
11
12
  const cwd = process.cwd();
13
+ const args = parseArgs(process.argv.slice(2));
14
+ if (args.help) {
15
+ console.log(USAGE);
16
+ return;
17
+ }
18
+ if (args.unknown.length > 0) {
19
+ console.error(`create-avocado-site: unknown option ${args.unknown.join(", ")}\n\n${USAGE}`);
20
+ process.exitCode = 1;
21
+ return;
22
+ }
23
+ /*
24
+ * Say so, rather than cancelling into an empty directory listing.
25
+ *
26
+ * Without a terminal the guided path's first prompt returns a cancel
27
+ * symbol, `main` returns, and the process exits 1 with nothing written and
28
+ * nothing printed. Every unattended caller that ever tried this — a CI job,
29
+ * an image build, an agent — saw a bare exit code and no way to tell a crash
30
+ * from a refusal.
31
+ */
32
+ if (args.nonInteractive && !args.target) {
33
+ console.error(NEEDS_A_NAME);
34
+ process.exitCode = 1;
35
+ return;
36
+ }
12
37
  /*
13
38
  * `npm create avocado-site my-demo` — a directory argument means the demo,
14
39
  * with no *configuration* questions. Somebody who has typed a name has
@@ -24,15 +49,21 @@ async function main() {
24
49
  * and nothing in the product confirms either happened. "I added the key and
25
50
  * nothing changed" has no error message anywhere in the system, and this
26
51
  * prompt is the cheapest way to stop most people arriving there.
52
+ *
53
+ * Unattended, that reasoning inverts: there is nobody to ask, and a question
54
+ * nobody can answer is not a prompt, it is a hang. `--api-key` says the same
55
+ * thing in advance, and skipping stays free — a scaffold with no key is a
56
+ * complete, working project.
27
57
  */
28
- const target = process.argv.slice(2).find((arg) => !arg.startsWith("-"));
58
+ const target = args.target;
29
59
  let config;
30
60
  if (target) {
31
61
  // `runPrompts` opens with this; the fast path had no intro at all, so a
32
62
  // stranger's first interaction with the product was an unexplained request
33
63
  // for a credential on an otherwise blank terminal.
34
64
  p.intro(AVOCADO_INTRO);
35
- config = await demoConfig(target, await promptForApiKey());
65
+ const apiKey = args.apiKey ?? (args.nonInteractive ? undefined : await promptForApiKey());
66
+ config = await demoConfig(target, apiKey);
36
67
  }
37
68
  else {
38
69
  config = await runPrompts(cwd);
@@ -40,7 +71,7 @@ async function main() {
40
71
  if (!config)
41
72
  return;
42
73
  if (config.mode === "demo") {
43
- await bootstrapDemo(cwd, target ?? config.siteId, config);
74
+ await bootstrapDemo(cwd, target ?? config.siteId, config, args);
44
75
  return;
45
76
  }
46
77
  await scaffoldInto(cwd, collectFiles(config), config);
@@ -69,7 +100,7 @@ async function scaffoldInto(dir, files, config) {
69
100
  * ended, and every printed step is a place for somebody evaluating the product
70
101
  * to put it down.
71
102
  */
72
- async function bootstrapDemo(cwd, dirName, config) {
103
+ async function bootstrapDemo(cwd, dirName, config, options) {
73
104
  const dir = resolve(cwd, dirName);
74
105
  if (existsSync(dir) && (await readdir(dir)).length > 0) {
75
106
  p.log.error(`${dirName} already exists and is not empty.`);
@@ -81,21 +112,32 @@ async function bootstrapDemo(cwd, dirName, config) {
81
112
  const files = collectDemoFiles(config);
82
113
  await generateFiles(dir, files);
83
114
  p.log.success(`Created ${dirName} — ${files.length} files`);
84
- const spinner = p.spinner();
85
- spinner.start("Installing dependencies");
86
- const installed = await run("npm", ["install", "--no-audit", "--no-fund"], dir);
87
- if (installed) {
88
- spinner.stop("Installed dependencies");
115
+ /*
116
+ * `--no-install` is for the callers that install differently or not at all —
117
+ * a gate that overlays tarballs packed from a working tree, an image build
118
+ * with its own cache, a test that only cares that the files are correct.
119
+ * Without it, proving the command runs costs a full `npm install`.
120
+ */
121
+ if (options.install) {
122
+ const spinner = p.spinner();
123
+ spinner.start("Installing dependencies");
124
+ const installed = await run("npm", ["install", "--no-audit", "--no-fund"], dir);
125
+ if (installed) {
126
+ spinner.stop("Installed dependencies");
127
+ }
128
+ else {
129
+ /*
130
+ * Not fatal, and worth being explicit about rather than exiting. A failed
131
+ * install is usually a registry or proxy problem on the user's side, and
132
+ * the project on disk is complete and correct — `npm install` in it will
133
+ * work once that is fixed. Deleting it would throw away the good half.
134
+ */
135
+ spinner.stop("Could not install dependencies");
136
+ p.log.warn(`Run \`npm install\` in ${dirName} yourself — everything else is in place.`);
137
+ }
89
138
  }
90
139
  else {
91
- /*
92
- * Not fatal, and worth being explicit about rather than exiting. A failed
93
- * install is usually a registry or proxy problem on the user's side, and
94
- * the project on disk is complete and correct — `npm install` in it will
95
- * work once that is fixed. Deleting it would throw away the good half.
96
- */
97
- spinner.stop("Could not install dependencies");
98
- p.log.warn(`Run \`npm install\` in ${dirName} yourself — everything else is in place.`);
140
+ p.log.warn(`Skipped \`npm install\` — run it in ${dirName} before starting.`);
99
141
  }
100
142
  p.log.message([
101
143
  ` cd ${dirName} && npm run dev`,
@@ -138,11 +180,12 @@ async function bootstrapDemo(cwd, dirName, config) {
138
180
  ` https://docs.avocadostudio.dev/sites`,
139
181
  ``,
140
182
  ...(config.apiKey
141
- ? [` Chat is enabled \u2014 ${config.apiKey.variable} is in .env.local.`]
183
+ ? [` AI editing is on \u2014 ${config.apiKey.variable} is in .env.local.`]
142
184
  : [
143
- ` No API key needed to look around. Add ANTHROPIC_API_KEY,`,
144
- ` OPENAI_API_KEY or GOOGLE_GENAI_API_KEY to .env.local and restart`,
145
- ` when you want to edit by chat.`,
185
+ ` Visual editing works now: click a block, edit its fields, publish.`,
186
+ ` For AI editing \u2014 describing a change and having it planned for you \u2014`,
187
+ ` add ANTHROPIC_API_KEY, OPENAI_API_KEY or GOOGLE_GENAI_API_KEY to`,
188
+ ` .env.local and restart.`,
146
189
  ]),
147
190
  ].join("\n"));
148
191
  /*
@@ -158,10 +201,19 @@ async function bootstrapDemo(cwd, dirName, config) {
158
201
  * script or a container wants the files and nothing else. The default is yes
159
202
  * because the overwhelmingly common case is a person who wants to see it.
160
203
  */
161
- const start = await p.confirm({
162
- message: `Start it now? (runs \`npm run dev\` in ${dirName})`,
163
- initialValue: true,
164
- });
204
+ /*
205
+ * The second thing that could not be answered without a keyboard, and the
206
+ * more dangerous of the two: a `confirm` whose default is *yes* would, if it
207
+ * ever stopped blocking, spawn two long-running dev servers inside a CI job
208
+ * or an image build. Unattended, the answer is always no — the caller asked
209
+ * for a project, not for a process it has no way to stop.
210
+ */
211
+ const start = options.nonInteractive
212
+ ? false
213
+ : await p.confirm({
214
+ message: `Start it now? (runs \`npm run dev\` in ${dirName})`,
215
+ initialValue: true,
216
+ });
165
217
  if (p.isCancel(start) || !start) {
166
218
  p.outro(`Done. Run \`cd ${dirName} && npm run dev\` when you are ready.`);
167
219
  return;
package/dist/prompts.js CHANGED
@@ -64,12 +64,30 @@ export async function promptForApiKey() {
64
64
  * variable is chosen from the prefix, so a reader had no way to know whether
65
65
  * their key was one of the right ones.
66
66
  */
67
- p.note("Chat needs a model. Everything else works without one \u2014 the preview,\n" +
68
- "click-to-select, the property panel and publishing.\n\n" +
67
+ /*
68
+ * Name the product's own headline feature as the headline feature.
69
+ *
70
+ * This heading read "Optional \u2014 enable chat now", which is two mistakes in
71
+ * five words. "Chat" names the input box rather than what it does, and
72
+ * "optional" is the first word a newcomer reads about the one capability
73
+ * the whole product exists for \u2014 describing a change in a sentence and
74
+ * having it planned, applied and previewed for you. Skipping has to stay
75
+ * available and free of friction; it does not have to be the frame.
76
+ *
77
+ * So the heading says what this is, the first paragraph says what it does
78
+ * with an example, and *then* it says you can skip \u2014 in that order,
79
+ * because a reader who skips should know what they are skipping.
80
+ */
81
+ p.note("Describe a change and Avocado plans it, applies it and shows you the\n" +
82
+ "result in the live preview \u2014 \"make the hero headline shorter\",\n" +
83
+ "\"add a testimonials section\", \"translate this page to German\".\n" +
84
+ "That is the product, and it needs a model.\n\n" +
85
+ "You can skip and add one later. Visual editing works without a key:\n" +
86
+ "the preview, click-to-select, the property panel and publishing.\n\n" +
69
87
  " ANTHROPIC_API_KEY \u2190 recommended, the best-tested planner\n" +
70
88
  " OPENAI_API_KEY also does image generation\n" +
71
89
  " GOOGLE_GENAI_API_KEY also does image generation\n\n" +
72
- "The right variable is picked from the key itself.", "Optional \u2014 enable chat now");
90
+ "The right variable is picked from the key itself.", "AI editing \u2014 the main event");
73
91
  const key = await p.password({
74
92
  message: "Paste a key, or press Enter to skip",
75
93
  mask: "\u2022",
@@ -79,9 +97,11 @@ export async function promptForApiKey() {
79
97
  return undefined;
80
98
  const value = typeof key === "string" ? key.trim() : "";
81
99
  if (!value) {
82
- p.note("Chat will use the built-in demo planner, which handles simple, literal edits.\n" +
83
- "Everything else works: the preview, click-to-select, the property panel and publishing.\n" +
84
- "You can add a key to .env.local later and restart the dev server.", "No key \u2014 that is fine");
100
+ p.note("Visual editing works in full: the preview, click-to-select, the property\n" +
101
+ "panel and publishing. AI editing falls back to a built-in demo planner,\n" +
102
+ "which handles simple, literal edits and cannot write copy or reason\n" +
103
+ "about a page.\n\n" +
104
+ "Add a key to .env.local and restart the dev server to turn on the real thing.", "No key \u2014 that is fine");
85
105
  return undefined;
86
106
  }
87
107
  const variable = variableForKey(value);