@waniwani/kit 0.1.6 → 0.1.8

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 (45) hide show
  1. package/README.md +67 -35
  2. package/dist/cli/codegen.js +1105 -0
  3. package/dist/cli/codegen.js.map +1 -0
  4. package/{cli/env.mjs → dist/cli/env.js} +5 -6
  5. package/dist/cli/env.js.map +1 -0
  6. package/{cli/framework.mjs → dist/cli/framework.js} +112 -115
  7. package/dist/cli/framework.js.map +1 -0
  8. package/dist/cli/index.js +378 -0
  9. package/dist/cli/index.js.map +1 -0
  10. package/dist/cli/init.js +642 -0
  11. package/dist/cli/init.js.map +1 -0
  12. package/dist/cli/log.js +156 -0
  13. package/dist/cli/log.js.map +1 -0
  14. package/dist/cli/manifest.js +57 -0
  15. package/dist/cli/manifest.js.map +1 -0
  16. package/{cli/peers.mjs → dist/cli/peers.js} +77 -88
  17. package/dist/cli/peers.js.map +1 -0
  18. package/dist/cli/scan.js +100 -0
  19. package/dist/cli/scan.js.map +1 -0
  20. package/dist/cli/template.js +173 -0
  21. package/dist/cli/template.js.map +1 -0
  22. package/dist/cli/types.js +14 -0
  23. package/dist/cli/types.js.map +1 -0
  24. package/dist/cli/validate.js +328 -0
  25. package/dist/cli/validate.js.map +1 -0
  26. package/dist/cli/vercel.js +103 -0
  27. package/dist/cli/vercel.js.map +1 -0
  28. package/dist/server.d.ts +1 -1
  29. package/dist/server.d.ts.map +1 -1
  30. package/dist/server.js +0 -1
  31. package/dist/server.js.map +1 -1
  32. package/dist/web.d.ts +8 -7
  33. package/dist/web.d.ts.map +1 -1
  34. package/dist/web.js +7 -6
  35. package/dist/web.js.map +1 -1
  36. package/package.json +13 -9
  37. package/src/server.ts +7 -9
  38. package/src/web.tsx +12 -13
  39. package/cli/codegen.mjs +0 -1267
  40. package/cli/index.mjs +0 -409
  41. package/cli/init.mjs +0 -575
  42. package/cli/log.mjs +0 -178
  43. package/cli/scan.mjs +0 -112
  44. package/cli/template.mjs +0 -190
  45. package/cli/validate.mjs +0 -391
@@ -0,0 +1,642 @@
1
+ /**
2
+ * `waniwani init`, the first command anyone runs.
3
+ *
4
+ * What it writes is an app folder that already passes `waniwani check` and
5
+ * already renders something: a config, a tool, and the widget that displays what
6
+ * the tool returned. The point of scaffolding a working pair instead of an empty
7
+ * folder is that the tool-to-widget hand-off is the one piece of this framework
8
+ * nobody guesses correctly from the type signatures.
9
+ *
10
+ * waniwani init my-app create my-app/ and scaffold in it
11
+ * waniwani init . scaffold in the current directory
12
+ * waniwani init ask for a name, and put the app where the
13
+ * answer says: a name of its own creates
14
+ * ./<name>/, the offered default (the current
15
+ * folder's name) scaffolds in place
16
+ *
17
+ * --name <name> the MCP server name, default the directory name
18
+ * --host <host> where it deploys: vercel, alpic, container, none
19
+ * --minimal config and one tool, no widget
20
+ * --yes take every default, ask nothing
21
+ * --no-install skip the dependency install
22
+ * --force overwrite app files that are already there
23
+ *
24
+ * Running it inside a repo that already has files is expected and supported.
25
+ * A `package.json` is merged rather than replaced, a `.gitignore` gains the
26
+ * lines it lacks, and a `README.md` or `.env.example` that exists is left
27
+ * alone. Only the app's own source files count as a collision, and those stop
28
+ * the command until `--force` says otherwise.
29
+ */
30
+ import { spawn } from "node:child_process";
31
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
32
+ import { basename, dirname, join, relative } from "node:path";
33
+ import { createInterface } from "node:readline/promises";
34
+ import { bold, dim, green, red, yellow } from "./log.js";
35
+ import { PACKAGE_VERSION } from "./manifest.js";
36
+ import { installable } from "./peers.js";
37
+ /** A directory name as an MCP server name: `My App` becomes `my-app`. */
38
+ function slugify(input) {
39
+ const slug = input
40
+ .trim()
41
+ .toLowerCase()
42
+ .replace(/[^a-z0-9]+/g, "-")
43
+ .replace(/^-+|-+$/g, "");
44
+ return slug || "my-app";
45
+ }
46
+ /** `my-app` becomes `My app`, for the human-facing title. */
47
+ function titleize(slug) {
48
+ const words = slug.replace(/[-_]+/g, " ");
49
+ return words.charAt(0).toUpperCase() + words.slice(1);
50
+ }
51
+ /**
52
+ * A title, made safe to drop into the generated TypeScript and Markdown.
53
+ * Backticks, quotes, backslashes and `$` are the characters that would end a
54
+ * string or a template literal early, and a product name needs none of them.
55
+ */
56
+ function cleanTitle(input) {
57
+ return input
58
+ .replace(/[`"'\\$]/g, "")
59
+ .replace(/\s+/g, " ")
60
+ .trim();
61
+ }
62
+ /**
63
+ * What a new app depends on.
64
+ *
65
+ * Every version is read off this package's own manifest: `@waniwani/kit` at the
66
+ * version of the CLI doing the scaffolding, and the four peers at the floors
67
+ * this package declares, capped by `installable` so a floor does not install
68
+ * the next major on the day it lands. A scaffold that wrote its own numbers
69
+ * here would be the one file in the folder that can be wrong the day it is
70
+ * created.
71
+ *
72
+ * `@waniwani/sdk` is written out even though a required peer is auto-installed
73
+ * without it, because the app imports it directly — `flows/*.ts` calls
74
+ * `createFlow` — and a package you import belongs in your own manifest rather
75
+ * than arriving because something else asked for it.
76
+ */
77
+ function dependencies() {
78
+ return {
79
+ "@waniwani/kit": `^${PACKAGE_VERSION}`,
80
+ "@waniwani/sdk": installable("@waniwani/sdk"),
81
+ react: installable("react"),
82
+ "react-dom": installable("react-dom"),
83
+ zod: installable("zod"),
84
+ };
85
+ }
86
+ // ------------------------------------------------------------ scaffold content
87
+ /**
88
+ * The tool name and the widget name the scaffold uses. They appear in five
89
+ * files, including inside prose the model reads, so they are named once.
90
+ */
91
+ const TOOL = "search-products";
92
+ const WIDGET = "product-list";
93
+ function packageJson(app) {
94
+ return `${JSON.stringify({
95
+ name: app.name,
96
+ private: true,
97
+ type: "module",
98
+ scripts: {
99
+ check: "waniwani check",
100
+ dev: "waniwani dev",
101
+ build: "waniwani build",
102
+ start: "waniwani start",
103
+ },
104
+ dependencies: dependencies(),
105
+ }, null, 2)}\n`;
106
+ }
107
+ function appConfig(app) {
108
+ return `import { defineApp } from "@waniwani/kit";
109
+
110
+ export default defineApp({
111
+ // The MCP server name. Hosts show \`title\` to humans and use this one as the id.
112
+ name: ${JSON.stringify(app.name)},
113
+ title: ${JSON.stringify(app.title)},
114
+ });
115
+ `;
116
+ }
117
+ function tool() {
118
+ return `import { defineTool } from "@waniwani/kit";
119
+ import { z } from "zod";
120
+
121
+ /**
122
+ * The filename is the tool name, so this file is \`${TOOL}\`. Rename the
123
+ * file and the tool renames with it.
124
+ *
125
+ * Swap CATALOGUE for whatever answers the question for real: a fetch, a
126
+ * database, an internal API. \`run\` may be async.
127
+ */
128
+ const CATALOGUE = [
129
+ { id: "aeron", name: "Aeron chair", price: 1290, blurb: "Mesh task chair, twelve-year warranty." },
130
+ { id: "sayl", name: "Sayl chair", price: 545, blurb: "Suspension back, the light one." },
131
+ { id: "nevi", name: "Nevi sit-stand desk", price: 890, blurb: "Electric, 70 to 120 cm." },
132
+ { id: "ollin", name: "Ollin monitor arm", price: 235, blurb: "Single arm, holds up to 9 kg." },
133
+ ];
134
+
135
+ export default defineTool({
136
+ // Shown to humans in connector UIs.
137
+ title: "Search the catalogue",
138
+ // The only thing the model reads before deciding to call this, so it says
139
+ // when to call it and what not to do instead.
140
+ description:
141
+ "Find products matching what the shopper asked for. Call this before naming any product or quoting any price, and never answer either from memory. Pass the shopper's own words as the query.",
142
+ // Zod shapes, written as plain objects instead of z.object({ ... }).
143
+ input: {
144
+ query: z.string().describe("What the shopper asked for, in their words, e.g. 'a chair under 600'."),
145
+ },
146
+ output: {
147
+ products: z.array(
148
+ z.object({
149
+ id: z.string(),
150
+ name: z.string(),
151
+ price: z.number().describe("Price in euros."),
152
+ blurb: z.string(),
153
+ }),
154
+ ),
155
+ },
156
+ // Becomes MCP annotations. This tool reads and does nothing else.
157
+ hints: { readOnly: true },
158
+ run: ({ query }) => {
159
+ const terms = query.toLowerCase().split(/\\s+/).filter(Boolean);
160
+ const matched = CATALOGUE.filter((product) =>
161
+ terms.some((term) => \`\${product.name} \${product.blurb}\`.toLowerCase().includes(term)),
162
+ );
163
+ // The whole catalogue when nothing matched, so an early conversation has
164
+ // something on screen while you are still wiring this up.
165
+ return { products: matched.length > 0 ? matched : CATALOGUE };
166
+ },
167
+ });
168
+ `;
169
+ }
170
+ function widgetContract() {
171
+ return `import { defineWidget } from "@waniwani/kit";
172
+ import { z } from "zod";
173
+
174
+ const product = z.object({
175
+ id: z.string(),
176
+ name: z.string(),
177
+ price: z.number().describe("Price in euros."),
178
+ blurb: z.string().describe("One line about the product."),
179
+ });
180
+
181
+ /**
182
+ * The folder name is the tool name, so this widget is \`${WIDGET}\`.
183
+ *
184
+ * \`data\` is one schema doing three jobs: the tool's input, its structured
185
+ * output, and the props \`useWidget()\` hands ui.tsx. Server and UI cannot drift.
186
+ *
187
+ * This file is imported by the server and by the browser bundle, so it stays
188
+ * free of React and CSS. The component sits next to it in ui.tsx.
189
+ */
190
+ export default defineWidget({
191
+ title: "Product list",
192
+ description:
193
+ "Show the product cards. Call this once ${TOOL} has returned products, passing them through unmodified. Frame it in one short sentence before calling, e.g. \\"Here's what fits.\\" The widget renders every name and price itself, so do NOT list them in text.",
194
+ data: {
195
+ query: z.string().describe("What the shopper asked for. Shown as the heading."),
196
+ products: z.array(product).describe("Products returned by ${TOOL}, unmodified."),
197
+ },
198
+ hints: { readOnly: true },
199
+ // Text handed to the model alongside the rendered widget. Use it to say what
200
+ // the model should not repeat, and what it should wait for.
201
+ llmText: (data) =>
202
+ \`The product list is on screen with \${data.products.length} products. It renders every name and price itself, so do NOT repeat them in text.
203
+
204
+ Wait for the shopper to pick one, then answer about that product.\`,
205
+ });
206
+ `;
207
+ }
208
+ function widgetUi() {
209
+ return `import { useLayout, useSendFollowUpMessage, useWidget } from "@waniwani/kit/web";
210
+ import widget from "./widget.js";
211
+
212
+ const euros = (value: number) =>
213
+ new Intl.NumberFormat("en-IE", { style: "currency", currency: "EUR" }).format(value);
214
+
215
+ export default function ProductList() {
216
+ // Typed off the widget's own \`data\` schema. No generated helpers, no server
217
+ // type import.
218
+ const { data } = useWidget(widget);
219
+ const sendFollowUp = useSendFollowUpMessage();
220
+
221
+ // The host hands the colour scheme to the view instead of to the browser, so
222
+ // \`prefers-color-scheme\` is the wrong signal and Tailwind's \`dark:\` variant is
223
+ // wired to a \`dark\` class (see the template's src/index.css). Every widget puts
224
+ // that class on its own root: a view is its own bundle in its own iframe, so
225
+ // there is no shared ancestor to hang it off.
226
+ const { theme } = useLayout();
227
+ const root = theme === "dark" ? "dark" : "";
228
+
229
+ // \`data\` arrives as soon as the host has the tool input, which on most hosts is
230
+ // before the server has responded. Render optimistically.
231
+ if (!data) {
232
+ return <div className={\`\${root} font-sans text-sm text-slate-500\`}>Loading…</div>;
233
+ }
234
+
235
+ return (
236
+ <div className={\`\${root} font-sans text-slate-900 dark:text-slate-100\`}>
237
+ <h1 className="mb-3 text-lg font-semibold tracking-tight">{data.query}</h1>
238
+
239
+ <div className="grid grid-cols-[repeat(auto-fit,minmax(180px,1fr))] gap-2.5">
240
+ {data.products.map((product) => (
241
+ <button
242
+ type="button"
243
+ key={product.id}
244
+ // A click becomes a message from the shopper, which is what moves
245
+ // the conversation on.
246
+ onClick={() => sendFollowUp(\`Tell me more about the \${product.name}.\`)}
247
+ className="flex cursor-pointer flex-col items-start gap-1 rounded-2xl border-[1.5px] border-slate-200 bg-white p-3.5 text-left transition duration-150 hover:-translate-y-px hover:border-slate-400 hover:shadow-lg hover:shadow-slate-900/10 dark:border-slate-700 dark:bg-slate-900 dark:hover:border-slate-500"
248
+ // What the model reads in place of the pixels.
249
+ data-llm={\`\${product.name}, \${euros(product.price)}: \${product.blurb}\`}
250
+ >
251
+ <span className="text-[22px] font-bold tracking-tight">{euros(product.price)}</span>
252
+ <span className="font-semibold">{product.name}</span>
253
+ <span className="text-[13px] text-slate-500 dark:text-slate-400">{product.blurb}</span>
254
+ </button>
255
+ ))}
256
+ </div>
257
+ </div>
258
+ );
259
+ }
260
+ `;
261
+ }
262
+ function envExample() {
263
+ return `# Optional. Without it the app still runs: flows use MemoryKvStore and
264
+ # withWaniwani degrades to a no-op. With it, flow state is hosted and tracking
265
+ # reaches app.waniwani.ai.
266
+ WANIWANI_API_KEY=
267
+ WANIWANI_PUBLIC_KEY=
268
+ `;
269
+ }
270
+ function gitignore() {
271
+ return `node_modules/
272
+ .waniwani/
273
+ .env
274
+ .env.local
275
+ `;
276
+ }
277
+ function readme(app) {
278
+ return `# ${app.title}
279
+
280
+ An MCP app built with [@waniwani/kit](https://www.npmjs.com/package/@waniwani/kit).
281
+ You own the folders below. The server, the transport, the bundling and the deploy
282
+ files are the kit's.
283
+
284
+ \`\`\`
285
+ waniwani.config.ts the app's name and title, plus optional instructions
286
+ tools/*.ts one file per tool; the filename is the tool name
287
+ widgets/<name>/ widget.ts for the contract, ui.tsx for the component
288
+ flows/*.ts multi-step conversations, from @waniwani/sdk
289
+ \`\`\`
290
+
291
+ ## Commands
292
+
293
+ \`\`\`bash
294
+ npm run dev # dev server, regenerating on every change
295
+ npm run check # validate the folder without building
296
+ npm run build # production build
297
+ npm run start # run the production build
298
+ \`\`\`
299
+
300
+ \`.waniwani/\` is build output, the way \`.next/\` is. Every command regenerates it
301
+ and it stays out of git.
302
+ `;
303
+ }
304
+ /**
305
+ * The deploy targets `init` offers.
306
+ *
307
+ * Only Vercel needs a file in the app repo, and only for one reason: the
308
+ * framework preset is a project setting that Vercel resolves before the build
309
+ * command runs, so nothing the build emits can correct a project whose preset
310
+ * says Next.js or Express. `framework: null` selects `Other`, which is the
311
+ * preset that runs the `build` script and adopts the Build Output tree the kit
312
+ * leaves at `.vercel/output`.
313
+ *
314
+ * Nothing else belongs in that file. A `buildCommand` would restate the `build`
315
+ * script, and a `routes` table would duplicate the routing config the build
316
+ * writes — both would then go stale against a kit that moved on. This one key is
317
+ * a fact about the project rather than about the build, so it never changes.
318
+ *
319
+ * Alpic and a container image both read their config from the generated project,
320
+ * which the build regenerates, so neither leaves anything tracked behind.
321
+ */
322
+ const HOSTS = [
323
+ {
324
+ id: "vercel",
325
+ label: "Vercel",
326
+ note: "git push, or `vercel deploy --prebuilt`",
327
+ deploy: ["git push", "vercel deploy --prebuilt # or upload a local build"],
328
+ },
329
+ {
330
+ id: "alpic",
331
+ label: "Alpic",
332
+ note: "alpic.json comes from the build",
333
+ deploy: ["waniwani build", "cd .waniwani && alpic deploy"],
334
+ },
335
+ {
336
+ id: "container",
337
+ label: "Docker or self-hosted",
338
+ note: "Dockerfile comes from the build",
339
+ deploy: ["waniwani build", "docker build .waniwani"],
340
+ },
341
+ {
342
+ id: "none",
343
+ label: "Not yet",
344
+ note: "nothing written, add it later",
345
+ deploy: [],
346
+ },
347
+ ];
348
+ export function hostById(id) {
349
+ return HOSTS.find((host) => host.id === id) ?? HOSTS[0];
350
+ }
351
+ /**
352
+ * What a git-connected Vercel project needs from the repo, and nothing more.
353
+ * See HOSTS for why this is one key.
354
+ */
355
+ function vercelJson() {
356
+ return `${JSON.stringify({
357
+ $schema: "https://openapi.vercel.sh/vercel.json",
358
+ framework: null,
359
+ }, null, 2)}\n`;
360
+ }
361
+ function scaffold(app, { minimal, host }) {
362
+ const files = [
363
+ {
364
+ path: "package.json",
365
+ contents: packageJson(app),
366
+ whenPresent: "merge",
367
+ merge: mergePackageJson,
368
+ },
369
+ { path: ".gitignore", contents: gitignore(), whenPresent: "merge", merge: mergeGitignore },
370
+ { path: ".env.example", contents: envExample(), whenPresent: "keep" },
371
+ { path: "README.md", contents: readme(app), whenPresent: "keep" },
372
+ { path: "waniwani.config.ts", contents: appConfig(app) },
373
+ { path: `tools/${TOOL}.ts`, contents: tool() },
374
+ ];
375
+ if (!minimal) {
376
+ files.push({ path: `widgets/${WIDGET}/widget.ts`, contents: widgetContract() }, { path: `widgets/${WIDGET}/ui.tsx`, contents: widgetUi() });
377
+ }
378
+ // A repo that already answers Vercel its own way keeps that answer: the file
379
+ // may carry a region, a cron, or a `maxDuration` this has no business
380
+ // replacing. `waniwani check` reads it and names the keys that fight the build.
381
+ if (host === "vercel") {
382
+ files.push({ path: "vercel.json", contents: vercelJson(), whenPresent: "keep" });
383
+ }
384
+ return files;
385
+ }
386
+ // -------------------------------------------------------------------- merging
387
+ /**
388
+ * Add the scripts and dependencies an app needs to a manifest that is already
389
+ * there, and touch nothing else. A key the repo already declares is the repo's
390
+ * decision, including a `dev` script that runs something other than this CLI.
391
+ *
392
+ * @returns the keys added, for the CLI to report
393
+ */
394
+ function mergePackageJson(file, contents) {
395
+ const existing = JSON.parse(readFileSync(file, "utf-8"));
396
+ const generated = JSON.parse(contents);
397
+ const added = [];
398
+ const fold = (section) => {
399
+ const merged = { ...existing[section] };
400
+ for (const [key, value] of Object.entries(generated[section] ?? {})) {
401
+ if (merged[key])
402
+ continue;
403
+ merged[key] = value;
404
+ added.push(`${section}.${key}`);
405
+ }
406
+ return merged;
407
+ };
408
+ const next = {
409
+ ...existing,
410
+ // App modules are ESM. A repo that says commonjs is warned about instead of
411
+ // rewritten, since flipping it changes how the rest of that repo loads.
412
+ type: existing.type ?? "module",
413
+ scripts: fold("scripts"),
414
+ dependencies: fold("dependencies"),
415
+ };
416
+ if (!existing.type)
417
+ added.push("type");
418
+ if (added.length > 0) {
419
+ writeFileSync(file, `${JSON.stringify(next, null, 2)}\n`);
420
+ }
421
+ return added;
422
+ }
423
+ /**
424
+ * Add the lines the app does not ignore yet, leaving every line it wrote alone.
425
+ * A trailing slash is not part of the comparison, so a repo ignoring
426
+ * `node_modules` does not gain `node_modules/` next to it.
427
+ *
428
+ * @returns the lines added, for the CLI to report
429
+ */
430
+ function mergeGitignore(file, contents) {
431
+ const existing = readFileSync(file, "utf-8");
432
+ const bare = (line) => line.trim().replace(/\/$/, "");
433
+ const known = new Set(existing.split("\n").map(bare));
434
+ const additions = contents
435
+ .split("\n")
436
+ .filter((line) => line.trim() && !line.trim().startsWith("#") && !known.has(bare(line)));
437
+ if (additions.length === 0)
438
+ return [];
439
+ const prefix = !existing || existing.endsWith("\n") ? "" : "\n";
440
+ writeFileSync(file, `${existing}${prefix}${additions.join("\n")}\n`);
441
+ return additions;
442
+ }
443
+ // ------------------------------------------------------------------- the shell
444
+ function write(file, contents) {
445
+ mkdirSync(dirname(file), { recursive: true });
446
+ writeFileSync(file, contents);
447
+ }
448
+ /** One question, with the default in parentheses and Enter taking it. */
449
+ async function ask(question, fallback) {
450
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
451
+ try {
452
+ const answer = await rl.question(`${question} ${dim(`(${fallback})`)} `);
453
+ return answer.trim() || fallback;
454
+ }
455
+ finally {
456
+ rl.close();
457
+ }
458
+ }
459
+ /**
460
+ * One question with a numbered list, and Enter taking the first option.
461
+ *
462
+ * Numbered rather than arrow-driven: this CLI writes plain lines everywhere else,
463
+ * and a raw-mode menu is the one piece of terminal state a `waniwani init` piped
464
+ * into something would leave behind.
465
+ */
466
+ async function choose(question, options) {
467
+ const first = options[0];
468
+ console.log(`\n${bold(question)}`);
469
+ for (const [index, option] of options.entries()) {
470
+ console.log(` ${dim(`${index + 1}`)} ${option.label} ${dim(`— ${option.note}`)}`);
471
+ }
472
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
473
+ try {
474
+ const answer = (await rl.question(`\nPick one ${dim(`(1, ${first.label})`)} `)).trim();
475
+ if (!answer)
476
+ return first.id;
477
+ // A number picks by position; anything else is matched against the labels,
478
+ // so typing `vercel` works as well as typing `1`.
479
+ const picked = Number(answer);
480
+ if (Number.isInteger(picked) && picked >= 1 && picked <= options.length) {
481
+ return options[picked - 1].id;
482
+ }
483
+ const lowered = answer.toLowerCase();
484
+ const named = options.find((option) => option.id === lowered || option.label.toLowerCase().startsWith(lowered));
485
+ return named?.id ?? first.id;
486
+ }
487
+ finally {
488
+ rl.close();
489
+ }
490
+ }
491
+ /**
492
+ * The package manager that invoked this command, which npm, pnpm, yarn and bun
493
+ * all announce in `npm_config_user_agent`. Nothing to detect from lockfiles: a
494
+ * new folder has none, and a `waniwani init` inside an existing repo was still
495
+ * typed with one of the four.
496
+ */
497
+ function packageManager() {
498
+ const agent = process.env.npm_config_user_agent ?? "";
499
+ const names = ["bun", "pnpm", "yarn", "npm"];
500
+ return names.find((name) => agent.startsWith(name)) ?? "npm";
501
+ }
502
+ /** How each manager runs a binary out of node_modules, for the closing lines. */
503
+ const RUNNERS = {
504
+ npm: "npx",
505
+ pnpm: "pnpm",
506
+ yarn: "yarn",
507
+ bun: "bunx",
508
+ };
509
+ function install(root, manager) {
510
+ console.log(`\n${dim(`installing with ${manager}…`)}`);
511
+ return new Promise((resolvePromise) => {
512
+ const child = spawn(manager, ["install"], {
513
+ cwd: root,
514
+ stdio: "inherit",
515
+ // npm and yarn are .cmd shims on Windows, which execvp cannot run.
516
+ shell: process.platform === "win32",
517
+ });
518
+ child.on("close", (code) => resolvePromise(code === 0));
519
+ child.on("error", () => resolvePromise(false));
520
+ });
521
+ }
522
+ /**
523
+ * Scaffold an app folder, install its dependencies, and say what to run.
524
+ *
525
+ * @param appRoot the directory to scaffold, created if it does not exist
526
+ * @param flags parsed CLI flags
527
+ * @param options.targeted a directory was named on the command line
528
+ * @returns a process exit code
529
+ */
530
+ export async function init(appRoot, flags, { targeted = true } = {}) {
531
+ const interactive = process.stdin.isTTY && !flags.yes && !flags.name;
532
+ const suggested = slugify(basename(appRoot));
533
+ // One question, and both fields come out of the answer: `Acme Shop` gives the
534
+ // server the name `acme-shop` and keeps `Acme Shop` as the title. An answer
535
+ // that is already a slug gets a title with a capital on the front.
536
+ const answer = flags.name ?? (interactive ? await ask("App name", suggested) : suggested);
537
+ const name = slugify(answer);
538
+ const typed = cleanTitle(answer);
539
+ const app = { name, title: typed && typed !== name ? typed : titleize(name) };
540
+ // A name typed at the prompt with no directory to put it in names the
541
+ // directory as well: `oney` in ~/Projects means ~/Projects/oney, which is how
542
+ // create-next-app's one question reads. Taking the offered default leaves
543
+ // everything where it is, since that default is the current folder's own name,
544
+ // and `waniwani init .` names the current folder outright.
545
+ const root = !targeted && interactive && name !== suggested ? join(appRoot, name) : appRoot;
546
+ // Asked rather than assumed, because the answer decides whether the repo
547
+ // carries a deploy file at all, and because seeing the four options is how
548
+ // someone learns the app is not tied to one host.
549
+ const host = typeof flags.host === "string"
550
+ ? hostById(flags.host).id
551
+ : interactive
552
+ ? await choose("Where will this deploy?", HOSTS)
553
+ : hostById(undefined).id;
554
+ const files = scaffold(app, { minimal: Boolean(flags.minimal), host });
555
+ // Nothing is written until every collision is known, so a refusal leaves the
556
+ // directory exactly as it was.
557
+ const clashes = files.filter((file) => !file.whenPresent && existsSync(join(root, file.path)));
558
+ if (clashes.length > 0 && !flags.force) {
559
+ console.error(`\n${red("✗")} ${bold("already an app folder here")}\n`);
560
+ for (const file of clashes) {
561
+ console.error(` ${file.path}`);
562
+ }
563
+ console.error(`\n${dim("pass --force to overwrite, or init into a new directory")}`);
564
+ return 1;
565
+ }
566
+ mkdirSync(root, { recursive: true });
567
+ const actions = [];
568
+ for (const file of files) {
569
+ const target = join(root, file.path);
570
+ if (!existsSync(target)) {
571
+ write(target, file.contents);
572
+ actions.push([green("+"), file.path, null]);
573
+ continue;
574
+ }
575
+ if (file.whenPresent === "keep") {
576
+ actions.push([dim("·"), file.path, "yours, left alone"]);
577
+ continue;
578
+ }
579
+ if (file.whenPresent === "merge" && file.merge) {
580
+ const changed = file.merge(target, file.contents);
581
+ actions.push([
582
+ changed.length > 0 ? yellow("~") : dim("·"),
583
+ file.path,
584
+ changed.length > 0 ? `+ ${changed.join(", ")}` : "nothing to add",
585
+ ]);
586
+ continue;
587
+ }
588
+ // A collision the caller chose to overwrite.
589
+ write(target, file.contents);
590
+ actions.push([yellow("~"), file.path, "overwritten"]);
591
+ }
592
+ console.log(`\n${green("✓")} ${bold(app.name)} ${dim(`→ ${root}`)}\n`);
593
+ for (const [marker, path, note] of actions) {
594
+ console.log(` ${marker} ${path}${note ? ` ${dim(note)}` : ""}`);
595
+ }
596
+ // The merge leaves a script the repo already had alone, so the way into the dev
597
+ // loop is whatever survived that: `npm run dev` when it is ours, the CLI by
598
+ // name when the repo's own `dev` runs something else.
599
+ const manifest = JSON.parse(readFileSync(join(root, "package.json"), "utf-8"));
600
+ const ours = manifest.scripts?.dev === "waniwani dev";
601
+ if (manifest.type !== "module") {
602
+ console.log(`\n${yellow("!")} ${bold("package.json")} says ${bold(`"type": "${manifest.type}"`)}`);
603
+ console.log(` ${dim('app modules are ESM: set it to "module" or the build cannot load them')}`);
604
+ }
605
+ const manager = packageManager();
606
+ // A scaffold with no node_modules still checks out and still reads, so a
607
+ // failed install is reported and the folder is kept.
608
+ const installed = flags.install === false ? null : await install(root, manager);
609
+ console.log(`\n${bold("From here")}`);
610
+ // `init apps/store` is scaffolded two levels down, so the path is the one to
611
+ // print rather than the directory's own name.
612
+ const from = relative(process.cwd(), root);
613
+ if (from) {
614
+ console.log(` cd ${from}`);
615
+ }
616
+ if (installed === false) {
617
+ console.log(` ${yellow(`${manager} install`)} ${dim("(the first attempt failed)")}`);
618
+ }
619
+ else if (installed === null) {
620
+ console.log(` ${manager} install`);
621
+ }
622
+ console.log(` ${ours ? `${manager} run dev` : `${RUNNERS[manager]} waniwani dev`}`);
623
+ console.log(`\n${bold("Then")}`);
624
+ console.log(` ${dim("·")} edit ${bold(`tools/${TOOL}.ts`)} to answer with your own data`);
625
+ if (!flags.minimal) {
626
+ console.log(` ${dim("·")} edit ${bold(`widgets/${WIDGET}/ui.tsx`)} for how it looks on screen`);
627
+ }
628
+ console.log(` ${dim("·")} add ${bold("flows/<name>.ts")} for a multi-step conversation`);
629
+ const target = hostById(host);
630
+ if (target.deploy.length > 0) {
631
+ console.log(`\n${bold(`Deploying to ${target.label}`)}`);
632
+ for (const line of target.deploy) {
633
+ console.log(` ${line}`);
634
+ }
635
+ }
636
+ else {
637
+ console.log(`\n${bold("Deploying")}`);
638
+ console.log(` ${dim("·")} rerun with ${bold("--host vercel")} for the one file Vercel needs`);
639
+ }
640
+ return 0;
641
+ }
642
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../cli/init.ts"],"names":[],"mappings":"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"}