synthesisui 0.16.42 → 0.16.43

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,5 +1,5 @@
1
1
  import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
2
- import { join, relative } from "node:path";
2
+ import { basename, join, relative } from "node:path";
3
3
  import { readToken, resolveRegistry } from "../config.js";
4
4
  import { isNearDuplicate } from "../doctor/color-distance.js";
5
5
  import { diagnose, scanSource } from "../doctor/scan.js";
@@ -396,6 +396,70 @@ async function sayIfWorkspace(root) {
396
396
  console.log("");
397
397
  console.log(body(paint.dim("One system, many consumers - each app gets its own number against the same contract.")));
398
398
  }
399
+ /**
400
+ * A name derived from the package, and honest about when it is a guess.
401
+ *
402
+ * `@frontend-hub/ui` stripped to "ui" (dono, 30/07), which names nothing: half
403
+ * the workspaces on earth have a package called ui, app or core. When the last
404
+ * segment is one of those generic words the scope carries the meaning, so it
405
+ * comes along - `frontend-hub-ui` - and failing that the folder the project
406
+ * lives in does.
407
+ */
408
+ const GENERIC = new Set([
409
+ "ui",
410
+ "app",
411
+ "web",
412
+ "core",
413
+ "lib",
414
+ "libs",
415
+ "src",
416
+ "client",
417
+ "frontend",
418
+ "design",
419
+ "design-system",
420
+ "components",
421
+ "shared",
422
+ "common",
423
+ ]);
424
+ export function deriveName(pkgName, dirName) {
425
+ const raw = (pkgName ?? "").trim();
426
+ const scoped = /^@([^/]+)\/(.+)$/.exec(raw);
427
+ const tail = (scoped ? scoped[2] : raw).toLowerCase();
428
+ if (tail && !GENERIC.has(tail))
429
+ return scoped ? scoped[2] : raw;
430
+ const owner = scoped ? scoped[1] : dirName;
431
+ if (owner && tail)
432
+ return `${owner}-${tail}`;
433
+ return tail || owner || "";
434
+ }
435
+ /**
436
+ * Ask for the name, once, with the derived one ready to accept.
437
+ *
438
+ * Only on a real run and only at a terminal: a piped or scripted invocation
439
+ * must never block waiting for a person who is not there. `--name` skips it
440
+ * entirely, which is what CI should pass.
441
+ */
442
+ async function askName(suggested) {
443
+ if (!process.stdin.isTTY || !process.stdout.isTTY)
444
+ return suggested;
445
+ const { createInterface } = await import("node:readline/promises");
446
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
447
+ try {
448
+ console.log("");
449
+ console.log(section("What should this system be called"));
450
+ console.log(body(paint.faint("Press enter to accept, or type another. You can rename it later; the install slug never changes.")));
451
+ const answer = await rl.question(` name [${suggested}]: `);
452
+ const clean = answer.trim().slice(0, 60);
453
+ return clean || suggested;
454
+ }
455
+ catch {
456
+ // A closed stdin is not a failure worth stopping an import over.
457
+ return suggested;
458
+ }
459
+ finally {
460
+ rl.close();
461
+ }
462
+ }
399
463
  export async function runImport(opts) {
400
464
  const root = opts.root ?? process.cwd();
401
465
  // A census handed to us (an agent annotated it) is sent as-is; the numbers
@@ -440,6 +504,8 @@ export async function runImport(opts) {
440
504
  console.log("");
441
505
  return;
442
506
  }
507
+ const suggested = deriveName(census.project.name, basename(root) || "system");
508
+ const chosen = opts.name?.trim() || (await askName(suggested));
443
509
  const token = await readToken();
444
510
  if (!token) {
445
511
  console.log("");
@@ -456,7 +522,7 @@ export async function runImport(opts) {
456
522
  "content-type": "application/json",
457
523
  Authorization: `Bearer ${token}`,
458
524
  },
459
- body: JSON.stringify({ census, name: opts.name }),
525
+ body: JSON.stringify({ census, name: chosen }),
460
526
  }).catch(() => null);
461
527
  if (!res || !res.ok) {
462
528
  const detail = res
package/dist/index.js CHANGED
@@ -41,7 +41,7 @@ Usage - deterministic, FREE:
41
41
 
42
42
  Usage - governance (deterministic, FREE):
43
43
  synthesisui adopt [--write] turn the design system you ALREADY have into a contract
44
- synthesisui import [--dry] read the app you already have and make it a system
44
+ synthesisui import [--dry] [--name <n>] read the app you already have and make it a system
45
45
  your agent follows - without touching your CSS
46
46
  synthesisui connect wire your agent: the check as an editor hook, the system
47
47
  as MCP tools, and a contract that stops repeating itself
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.42",
3
+ "version": "0.16.43",
4
4
  "description": "Bring SynthesisUI design systems into any project - tokens, typed components, whole pages and an agent-ready CLAUDE.md manifest.",
5
5
  "type": "module",
6
6
  "bin": {