create-avocado-site 0.11.7 → 0.11.9

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/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import { spawn } from "node:child_process";
4
4
  import { mkdir, readdir } from "node:fs/promises";
5
5
  import { existsSync } from "node:fs";
6
6
  import { resolve } from "node:path";
7
- import { runPrompts, demoConfig, promptForApiKey } from "./prompts.js";
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
10
  async function main() {
@@ -26,9 +26,17 @@ async function main() {
26
26
  * prompt is the cheapest way to stop most people arriving there.
27
27
  */
28
28
  const target = process.argv.slice(2).find((arg) => !arg.startsWith("-"));
29
- const config = target
30
- ? await demoConfig(target, await promptForApiKey())
31
- : await runPrompts(cwd);
29
+ let config;
30
+ if (target) {
31
+ // `runPrompts` opens with this; the fast path had no intro at all, so a
32
+ // stranger's first interaction with the product was an unexplained request
33
+ // for a credential on an otherwise blank terminal.
34
+ p.intro(AVOCADO_INTRO);
35
+ config = await demoConfig(target, await promptForApiKey());
36
+ }
37
+ else {
38
+ config = await runPrompts(cwd);
39
+ }
32
40
  if (!config)
33
41
  return;
34
42
  if (config.mode === "demo") {
@@ -90,10 +98,9 @@ async function bootstrapDemo(cwd, dirName, config) {
90
98
  p.log.warn(`Run \`npm install\` in ${dirName} yourself — everything else is in place.`);
91
99
  }
92
100
  p.log.message([
93
- ` cd ${dirName}`,
94
- ` npm run dev`,
101
+ ` cd ${dirName} && npm run dev`,
95
102
  ``,
96
- ` Opens the editor at`,
103
+ ` opens the editor at`,
97
104
  ``,
98
105
  ` http://localhost:${config.editorPort}/?siteId=${config.siteId}&session=dev`,
99
106
  ``,
@@ -102,12 +109,81 @@ async function bootstrapDemo(cwd, dirName, config) {
102
109
  ` default site when the URL does not name one. \`npm run dev\` opens this`,
103
110
  ` URL for you.`,
104
111
  ``,
105
- config.apiKey
106
- ? ` Chat is enabled \u2014 ${config.apiKey.variable} is in .env.local`
107
- : ` No API key needed to look around; add ANTHROPIC_API_KEY to .env.local and restart`,
108
- ` when you want to edit by chat.`,
112
+ /*
113
+ * Two whole sentences, not one with a dangling clause.
114
+ *
115
+ * This was a single line ending "…add ANTHROPIC_API_KEY to .env.local"
116
+ * followed by a hard-coded "when you want to edit by chat." — fine as one
117
+ * sentence, nonsense as soon as the first half could say something else.
118
+ * The keyed variant read "Chat is enabled — OPENAI_API_KEY is in
119
+ * .env.local when you want to edit by chat."
120
+ */
121
+ /*
122
+ * Said at the start and again here, because the banner is what stays on
123
+ * screen while the servers boot and is the last thing read before the
124
+ * browser opens. A preview that only announces itself once, eight lines
125
+ * earlier, has not really announced itself.
126
+ */
127
+ ` Avocado Studio is a research preview \u2014 expect rough edges,`,
128
+ ` and please tell us about them.`,
129
+ ``,
130
+ /*
131
+ * The demo's job is to make somebody want this on their own site, and
132
+ * until now nothing in the terminal said where that starts. `/sites` is
133
+ * the decision page — three paths with the recommended one first — rather
134
+ * than the Next.js wiring guide, which is the right page only once the
135
+ * decision is made.
136
+ */
137
+ ` Want this on your own site?`,
138
+ ` https://docs.avocadostudio.dev/sites`,
139
+ ``,
140
+ ...(config.apiKey
141
+ ? [` Chat is enabled \u2014 ${config.apiKey.variable} is in .env.local.`]
142
+ : [
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.`,
146
+ ]),
109
147
  ].join("\n"));
110
- p.outro("Done.");
148
+ /*
149
+ * Offer to run it, rather than ending on homework.
150
+ *
151
+ * The scaffold installed 29 files and every dependency, chose two free ports,
152
+ * generated three secrets and wrote a key — and then handed back two commands
153
+ * to type. Everything it needed to know it already knew; the remaining steps
154
+ * were manual only because nobody had automated them.
155
+ *
156
+ * Asked, not assumed. Starting two long-running servers is not something to
157
+ * do to somebody's terminal without permission, and anyone scaffolding into a
158
+ * script or a container wants the files and nothing else. The default is yes
159
+ * because the overwhelmingly common case is a person who wants to see it.
160
+ */
161
+ const start = await p.confirm({
162
+ message: `Start it now? (runs \`npm run dev\` in ${dirName})`,
163
+ initialValue: true,
164
+ });
165
+ if (p.isCancel(start) || !start) {
166
+ p.outro(`Done. Run \`cd ${dirName} && npm run dev\` when you are ready.`);
167
+ return;
168
+ }
169
+ p.outro("Starting \u2014 press Ctrl+C to stop.");
170
+ /*
171
+ * `stdio: "inherit"` and no `await` on a resolved promise: this is the user's
172
+ * session now. The dev script prints its own banner, opens the editor, and
173
+ * owns Ctrl+C from here.
174
+ */
175
+ const dev = spawn("npm", ["run", "dev"], {
176
+ cwd: dir,
177
+ stdio: "inherit",
178
+ shell: process.platform === "win32",
179
+ });
180
+ await new Promise((resolve) => {
181
+ dev.on("exit", () => resolve());
182
+ dev.on("error", () => {
183
+ p.log.warn(`Could not start it. Run \`cd ${dirName} && npm run dev\` yourself.`);
184
+ resolve();
185
+ });
186
+ });
111
187
  }
112
188
  /** Resolves false rather than throwing — the caller decides what a failure means. */
113
189
  function run(command, args, cwd) {
package/dist/prompts.d.ts CHANGED
@@ -1,4 +1,11 @@
1
1
  import type { ScaffoldConfig } from "./types.js";
2
+ /**
3
+ * The product, named, with its own mark.
4
+ *
5
+ * Shared so both entry paths use it. The directory-argument path had no intro
6
+ * at all — it went straight from an `npx` line to a demand for a secret.
7
+ */
8
+ export declare const AVOCADO_INTRO = "\uD83E\uDD51 Avocado Studio \u2014 research preview";
2
9
  /** A directory name turned into something the orchestrator will accept as a site id. */
3
10
  export declare function toSiteId(name: string): string;
4
11
  /**
package/dist/prompts.js CHANGED
@@ -3,6 +3,13 @@ import { randomBytes } from "node:crypto";
3
3
  import { basename } from "node:path";
4
4
  import { detectNextMajor } from "./next-version.js";
5
5
  import { findFreePort } from "./ports.js";
6
+ /**
7
+ * The product, named, with its own mark.
8
+ *
9
+ * Shared so both entry paths use it. The directory-argument path had no intro
10
+ * at all — it went straight from an `npx` line to a demand for a secret.
11
+ */
12
+ export const AVOCADO_INTRO = "\u{1F951} Avocado Studio \u2014 research preview";
6
13
  /** A directory name turned into something the orchestrator will accept as a site id. */
7
14
  export function toSiteId(name) {
8
15
  const cleaned = name.replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
@@ -44,8 +51,27 @@ export function variableForKey(raw) {
44
51
  * anywhere, and this is the cheapest way to stop most people reaching it.
45
52
  */
46
53
  export async function promptForApiKey() {
54
+ /*
55
+ * Say what this is before asking for a secret.
56
+ *
57
+ * On the `npm create avocado-site my-site` path this was the **first thing a
58
+ * stranger ever saw** — no product name, no explanation, just "Paste an API
59
+ * key" on an otherwise empty terminal. Asking for a credential is the one
60
+ * moment where an unexplained prompt is not merely unfriendly; it is the
61
+ * shape of something a careful person refuses.
62
+ *
63
+ * It also never said *which* key. Three providers are accepted and the
64
+ * variable is chosen from the prefix, so a reader had no way to know whether
65
+ * their key was one of the right ones.
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" +
69
+ " ANTHROPIC_API_KEY \u2190 recommended, the best-tested planner\n" +
70
+ " OPENAI_API_KEY also does image generation\n" +
71
+ " 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");
47
73
  const key = await p.password({
48
- message: "Paste an API key to enable chat, or press Enter to skip",
74
+ message: "Paste a key, or press Enter to skip",
49
75
  mask: "\u2022",
50
76
  validate: () => undefined,
51
77
  });
@@ -55,13 +81,35 @@ export async function promptForApiKey() {
55
81
  if (!value) {
56
82
  p.note("Chat will use the built-in demo planner, which handles simple, literal edits.\n" +
57
83
  "Everything else works: the preview, click-to-select, the property panel and publishing.\n" +
58
- "You can add a key to .env.local later and restart.", "No key \u2014 that is fine");
84
+ "You can add a key to .env.local later and restart the dev server.", "No key \u2014 that is fine");
59
85
  return undefined;
60
86
  }
61
87
  const variable = variableForKey(value);
62
88
  if (!variable)
63
89
  return undefined;
64
- p.note(`Writing ${variable} to .env.local. No restart needed.`, "Key saved");
90
+ /*
91
+ * Say *why* this variable, and how to change it.
92
+ *
93
+ * The confirmation used to state a conclusion and nothing else. A tester who
94
+ * meant to paste an Anthropic key and grabbed an OpenAI one from the line
95
+ * above it in the same file read "Writing OPENAI_API_KEY" and had no idea
96
+ * whether the product had misread the key or they had copied the wrong line —
97
+ * and no route back either way. Naming the evidence (the prefix) settles
98
+ * which of the two happened in one glance.
99
+ */
100
+ const hint = value.startsWith("sk-ant-")
101
+ ? "sk-ant-"
102
+ : value.startsWith("sk-")
103
+ ? "sk-"
104
+ : value.startsWith("AIza")
105
+ ? "AIza"
106
+ : null;
107
+ p.note(`${variable} \u2192 .env.local\n` +
108
+ (hint
109
+ ? `Chosen because the key starts with "${hint}".\n`
110
+ : "The key matched no known prefix, so this is a best guess.\n") +
111
+ "Wrong one? Edit .env.local and restart \u2014 nothing else needs to change.\n\n" +
112
+ "Live on first start. No restart needed now.", "Key saved");
65
113
  return { variable, value };
66
114
  }
67
115
  /** Everything a demo scaffold needs, with no questions asked. */
@@ -88,7 +136,7 @@ export async function demoConfig(dirName, apiKey) {
88
136
  * seen what the product does.
89
137
  */
90
138
  export async function runPrompts(cwd) {
91
- p.intro("Avocado Studio");
139
+ p.intro(AVOCADO_INTRO);
92
140
  const mode = await p.select({
93
141
  message: "What would you like to do?",
94
142
  options: [
@@ -17,7 +17,7 @@
17
17
  * `@avocadostudio-ai/orchestrator-core`, already pinned there; naming it again
18
18
  * here is how a project ends up with two.
19
19
  */
20
- export declare const AVOCADO = "0.11.7";
20
+ export declare const AVOCADO = "0.11.9";
21
21
  /**
22
22
  * Next 15.5.15 rather than 16, because that is the version every example app
23
23
  * and the demo site in this repository build and test against. The SDK
package/dist/versions.js CHANGED
@@ -17,7 +17,7 @@
17
17
  * `@avocadostudio-ai/orchestrator-core`, already pinned there; naming it again
18
18
  * here is how a project ends up with two.
19
19
  */
20
- export const AVOCADO = "0.11.7";
20
+ export const AVOCADO = "0.11.9";
21
21
  /**
22
22
  * Next 15.5.15 rather than 16, because that is the version every example app
23
23
  * and the demo site in this repository build and test against. The SDK
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-avocado-site",
3
- "version": "0.11.7",
3
+ "version": "0.11.9",
4
4
  "description": "Bootstrap a runnable Avocado Studio demo site, or wire Avocado into an existing Next.js project",
5
5
  "type": "module",
6
6
  "bin": {
@@ -34,8 +34,8 @@
34
34
  "@types/node": "^22.13.10",
35
35
  "tsx": "^4.19.0",
36
36
  "typescript": "^5.7.3",
37
- "@avocadostudio-ai/site-sdk": "0.11.7",
38
- "@avocadostudio-ai/shared": "0.11.7"
37
+ "@avocadostudio-ai/shared": "0.11.9",
38
+ "@avocadostudio-ai/site-sdk": "0.11.9"
39
39
  },
40
40
  "license": "Apache-2.0",
41
41
  "homepage": "https://docs.avocadostudio.dev",