create-macostack 0.2.0 → 0.2.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.
@@ -1,10 +1,8 @@
1
1
  #!/usr/bin/env bun
2
2
  import {
3
3
  cancel,
4
- confirm,
5
4
  intro,
6
5
  isCancel,
7
- log,
8
6
  multiselect,
9
7
  note,
10
8
  outro,
@@ -12,11 +10,9 @@ import {
12
10
  text,
13
11
  } from "@clack/prompts";
14
12
  import {
15
- closeSync,
16
13
  existsSync,
17
14
  mkdirSync,
18
15
  mkdtempSync,
19
- openSync,
20
16
  readdirSync,
21
17
  rmSync,
22
18
  } from "node:fs";
@@ -127,29 +123,6 @@ const answered = <T>(value: T | symbol): T => {
127
123
  return value as T;
128
124
  };
129
125
 
130
- // The setup wizards downstream are interactive. Under `bun create` / `bunx`,
131
- // stdin often arrives as a pipe (not a TTY) even though a human is right
132
- // there — so we hand children the real terminal via /dev/tty.
133
- const openTty = (): number | null => {
134
- try {
135
- return openSync("/dev/tty", "r");
136
- } catch {
137
- return null;
138
- }
139
- };
140
-
141
- const run = (command: string[], cwd: string) => {
142
- const ttyFd = openTty();
143
- const result = Bun.spawnSync(command, {
144
- cwd,
145
- stdin: ttyFd ?? "inherit",
146
- stdout: "inherit",
147
- stderr: "inherit",
148
- });
149
- if (ttyFd !== null) closeSync(ttyFd);
150
- return result.exitCode;
151
- };
152
-
153
126
  const githubToken = (): string | null => {
154
127
  const fromEnv = Bun.env.GITHUB_TOKEN ?? Bun.env.GH_TOKEN;
155
128
  if (fromEnv) return fromEnv;
@@ -299,19 +272,59 @@ for (const p of selected) {
299
272
  );
300
273
  }
301
274
 
302
- // Hand over to the template's own wizardit knows its modules, we don't.
275
+ // Configure the template. It OWNS its questions we fetch them as data
276
+ // (`setup --questions`), ask them in THIS wizard (nested interactive
277
+ // prompts don't get keyboard control under `bun create`), and apply the
278
+ // answers non-interactively (`setup --yes`).
303
279
  const pkg = existsSync(resolve(dest, "package.json"))
304
280
  ? await Bun.file(resolve(dest, "package.json")).json()
305
281
  : null;
306
282
  if (!flags["skip-setup"] && pkg?.scripts?.setup) {
307
- log.step(`Now shaping the ${p.label.toLowerCase()} its own wizard takes it from here:`);
308
- const args = flags.yes
309
- ? ["run", "setup", "--yes", "--name", appName]
310
- : ["run", "setup", "--name", appName];
311
- const code = run(["bun", ...args], dest);
312
- if (code !== 0) {
283
+ let questions: { modules?: { options: string[]; initial: string[]; exclusive?: string[][] } } | null = null;
284
+ if (!flags.yes) {
285
+ const q = Bun.spawnSync(["bun", "run", "setup", "--questions"], {
286
+ cwd: dest,
287
+ stdout: "pipe",
288
+ stderr: "pipe",
289
+ });
290
+ if (q.exitCode === 0) {
291
+ try {
292
+ questions = JSON.parse(q.stdout.toString().trim().split("\n").at(-1) ?? "");
293
+ } catch {}
294
+ }
295
+ }
296
+
297
+ const setupArgs = ["run", "setup", "--yes", "--name", appName];
298
+
299
+ if (questions?.modules) {
300
+ let chosen: string[];
301
+ for (;;) {
302
+ chosen = answered(
303
+ await multiselect({
304
+ message: `${p.label} — which modules should start ON? (everything stays in the code; off = dormant, ready when you need it)`,
305
+ options: questions.modules.options.map((m) => ({ value: m, label: m })),
306
+ initialValues: questions.modules.initial,
307
+ required: false,
308
+ }),
309
+ );
310
+ const clash = (questions.modules.exclusive ?? []).find((pair) =>
311
+ pair.every((m) => chosen.includes(m)),
312
+ );
313
+ if (!clash) break;
314
+ note(`${clash.join(" and ")} can't both be on — keep one of the two.`, "One thing");
315
+ }
316
+ setupArgs.push("--modules", chosen.join(","));
317
+ }
318
+
319
+ const s3 = spinner();
320
+ s3.start(`${p.label} — applying your setup (modules, fresh secrets, git)`);
321
+ const setup = Bun.spawnSync(["bun", ...setupArgs], { cwd: dest, stdout: "pipe", stderr: "pipe" });
322
+ if (setup.exitCode === 0) {
323
+ s3.stop(`${p.label} — configured`);
324
+ } else {
325
+ s3.stop(`${p.label} — setup didn't finish`);
313
326
  note(
314
- `The ${p.folder} wizard didn't finish. Everything downloaded fine — resume with:\n cd ${appName}/${p.folder} && bun run setup`,
327
+ `Everything downloaded fine — just finish it yourself:\n cd ${appName}/${p.folder} && bun run setup`,
315
328
  "Heads up",
316
329
  );
317
330
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-macostack",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Private macostack scaffold orchestrator for detached server/client repos.",
5
5
  "type": "module",
6
6
  "bin": {