unoverse 0.1.4 → 0.1.6

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/bin/unoverse.mjs CHANGED
@@ -17,8 +17,9 @@ const HELP = `
17
17
  ⬡ unoverse
18
18
 
19
19
  unoverse create [name] What are you building? Studio project · universe · client app
20
+ No name scaffolds into the folder you are in; a name makes one
20
21
  unoverse studio Launch Unoverse Studio (authoring: components, nodes, skills)
21
- unoverse urls Your deployed universe's addresses — probed live, not recited
22
+ unoverse where Where your universe isaddresses probed live, not recited
22
23
  unoverse update Update this CLI to the latest version
23
24
  unoverse help This
24
25
 
@@ -48,6 +49,8 @@ switch (cmd) {
48
49
  process.exit(r.status ?? 0);
49
50
  }
50
51
 
52
+ // `urls` stays as an alias: it shipped in 0.1.4/0.1.5 and is in people's fingers.
53
+ case "where":
51
54
  case "urls": {
52
55
  const { urls } = await import("../lib/urls.mjs");
53
56
  await urls();
package/lib/create.mjs CHANGED
@@ -8,7 +8,7 @@
8
8
  * a real auth round-trip against the registry, not a format check.
9
9
  */
10
10
  import { createInterface } from "node:readline/promises";
11
- import { existsSync, mkdirSync } from "node:fs";
11
+ import { existsSync, mkdirSync, readdirSync } from "node:fs";
12
12
  import { spawnSync } from "node:child_process";
13
13
 
14
14
  const REGISTRY_HOST = "registry.digitalocean.com";
@@ -56,15 +56,43 @@ async function validateRegistryToken(token) {
56
56
  }
57
57
  }
58
58
 
59
- function download(dir, name) {
59
+ /**
60
+ * Where to scaffold. NO NAME MEANS THE FOLDER YOU ARE STANDING IN.
61
+ *
62
+ * That is what a Studio project has always done (option 1 hands the cwd to Studio), so
63
+ * the other two paths agreeing removes a difference nobody could have guessed. Passing a
64
+ * name still makes a subfolder: `unoverse create acme` → ./acme.
65
+ *
66
+ * The old guard refused any target that EXISTED, which made "." impossible — the folder
67
+ * you are in always exists, so it failed with "./. already exists". Existence was never
68
+ * the question; whether there is anything to overwrite is. `.git` and `.DS_Store` do not
69
+ * count, so scaffolding into a folder you just `git init`ed works.
70
+ */
71
+ function resolveTarget(nameArg) {
72
+ const dir = nameArg || ".";
73
+ const here = dir === ".";
74
+ const existing = existsSync(dir)
75
+ ? readdirSync(dir).filter((f) => f !== ".git" && f !== ".DS_Store")
76
+ : [];
77
+ if (existing.length) {
78
+ fail(here ? "this folder is not empty" : `./${dir} is not empty`);
79
+ console.log(` ${dim("Scaffolding would overwrite what is here. Use an empty folder, or pass a name.")}`);
80
+ process.exit(1);
81
+ }
82
+ return dir;
83
+ }
84
+
85
+ /** Human name for the target, for messages that should not say "./.". */
86
+ const label = (dir) => (dir === "." ? "this folder" : `./${dir}`);
87
+
88
+ function download(dir, stripComponents = 1, filter = "") {
60
89
  mkdirSync(dir, { recursive: true });
61
90
  const r = spawnSync(
62
91
  "sh",
63
- ["-c", `curl -fsSL '${STARTER_TARBALL}' | tar -xz --strip-components=1 -C '${dir}'`],
92
+ ["-c", `curl -fsSL '${STARTER_TARBALL}' | tar -xz --strip-components=${stripComponents} -C '${dir}' ${filter}`],
64
93
  { stdio: ["ignore", "inherit", "inherit"] },
65
94
  );
66
95
  if (r.status !== 0) throw new Error("download failed");
67
- ok(`universe scaffolded at ./${name}`);
68
96
  }
69
97
 
70
98
  export async function create(nameArg) {
@@ -102,15 +130,12 @@ export async function create(nameArg) {
102
130
  }
103
131
  ok("token accepted");
104
132
 
105
- const name = nameArg || (await ask(rl, "Universe folder name [my-universe]: ")) || "my-universe";
106
- if (existsSync(name)) {
107
- fail(`./${name} already exists`);
108
- process.exit(1);
109
- }
110
- download(name, name);
133
+ const name = resolveTarget(nameArg);
134
+ download(name);
135
+ ok(`universe scaffolded in ${label(name)}`);
111
136
  console.log(`
112
- Next:
113
- cd ${name}
137
+ Next:${name === "." ? "" : `
138
+ cd ${name}`}
114
139
  ./unoverse init ${dim("(setup wizard — have this token ready, it asks for it)")}
115
140
  ./unoverse start
116
141
 
@@ -122,21 +147,11 @@ export async function create(nameArg) {
122
147
  }
123
148
 
124
149
  if (choice === "3") {
125
- const name = nameArg || (await ask(rl, "App folder name [my-client]: ")) || "my-client";
126
- if (existsSync(name)) {
127
- fail(`./${name} already exists`);
128
- process.exit(1);
129
- }
150
+ const name = resolveTarget(nameArg);
130
151
  // The reference client ships inside the starter — take just that folder.
131
- mkdirSync(name, { recursive: true });
132
- const r = spawnSync(
133
- "sh",
134
- ["-c", `curl -fsSL '${STARTER_TARBALL}' | tar -xz --strip-components=3 -C '${name}' '*/packages/client'`],
135
- { stdio: ["ignore", "inherit", "inherit"] },
136
- );
137
- if (r.status !== 0 ) throw new Error("download failed");
138
- ok(`client app scaffolded at ./${name}`);
139
- console.log(`\n Next:\n cd ${name}\n npm install && npm run dev\n\n ${cyan("Build with Claude:")} open the folder in Claude Code and ask for what you want.\n`);
152
+ download(name, 3, "'*/packages/client'");
153
+ ok(`client app scaffolded in ${label(name)}`);
154
+ console.log(`\n Next:${name === "." ? "" : `\n cd ${name}`}\n npm install && npm run dev\n\n ${cyan("Build with Claude:")} open the folder in Claude Code and ask for what you want.\n`);
140
155
  return;
141
156
  }
142
157
 
package/lib/urls.mjs CHANGED
@@ -1,5 +1,7 @@
1
1
  /**
2
- * `unoverse urls` — the deployed stack's addresses, PROBED, not recited.
2
+ * `unoverse where` — the deployed stack's addresses, PROBED, not recited.
3
+ *
4
+ * Named `urls` until 0.1.5; that spelling still works as an alias.
3
5
  *
4
6
  * Run it in a universe folder (or anywhere above one). It reads the deployment's
5
7
  * own facts — .env.production first, the ground's terraform outputs as enrichment —
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unoverse",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "The Unoverse front door — create a Studio project, a universe, or a client app, and launch Studio.",
5
5
  "license": "SEE LICENSE IN README.md",
6
6
  "type": "module",