synthesisui 0.16.60 → 0.16.61

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.
@@ -637,11 +637,11 @@ async function sayIfWorkspace(root) {
637
637
  console.log("");
638
638
  if (shared.length > 0) {
639
639
  console.log(body("The shape that works, if your vocabulary is shared:"));
640
- console.log(body(` ${paint.strong(`synthesisui import --dir ${shared[0]}`)} ${paint.faint("the system comes from here")}`));
640
+ console.log(body(` ${paint.strong(`synthesisui import --scope ${shared[0]}`)} ${paint.faint("the system comes from here")}`));
641
641
  }
642
642
  else {
643
643
  console.log(body("The shape that works:"));
644
- console.log(body(` ${paint.strong(`synthesisui import --dir ${apps[0]}`)} ${paint.faint("one app, one vocabulary")}`));
644
+ console.log(body(` ${paint.strong(`synthesisui import --scope ${apps[0]}`)} ${paint.faint("one app, one vocabulary")}`));
645
645
  }
646
646
  for (const app of apps.slice(0, 3)) {
647
647
  console.log(body(` ${paint.dim(`synthesisui doctor ${app}`)} ${paint.faint("how far this one is from it")}`));
@@ -863,6 +863,16 @@ function sayReach(c) {
863
863
  }
864
864
  export async function runImport(opts) {
865
865
  const root = opts.root ?? process.cwd();
866
+ /**
867
+ * WHAT TO READ, as opposed to WHERE TO WRITE. In a monorepo the design
868
+ * decisions live in one package and the governance belongs at the root, and
869
+ * conflating the two meant every re-measure scattered another census.
870
+ */
871
+ const scope = opts.scope
872
+ ?.trim()
873
+ .replace(/^\.\/+/, "")
874
+ .replace(/\/+$/, "");
875
+ const readFrom = scope ? join(root, scope) : root;
866
876
  // A census handed to us (an agent annotated it) is sent as-is; the numbers
867
877
  // inside were still ours to begin with.
868
878
  let census;
@@ -889,15 +899,20 @@ export async function runImport(opts) {
889
899
  }
890
900
  else {
891
901
  console.log(section("Reading your project"));
892
- census = await takeCensus(root);
902
+ census = await takeCensus(readFrom);
903
+ if (scope)
904
+ census.scope = scope;
893
905
  }
894
906
  summarize(census);
895
- if (!opts.census)
907
+ // Only when nobody has scoped yet. Telling someone to scope to the folder
908
+ // they just scoped to reads as the tool not having noticed.
909
+ if (!opts.census && !scope)
896
910
  await sayIfWorkspace(root);
897
- // A census handed to us is written BACK TO ITSELF. Deriving the path from the
898
- // root instead planted a second census at the repo root when the real one
899
- // lived in `packages/ui/_synthesisui/` - two files, silently disagreeing
900
- // about which import happened (dono, 31/07).
911
+ // A census handed to us is written BACK TO ITSELF; one we took lands at the
912
+ // ROOT, whatever it measured. Everything else in `_synthesisui/` is anchored
913
+ // there - config, `ds/<slug>/`, the hook's marker - and the census was the
914
+ // only artefact that travelled with `--dir`, which is how two of them ended
915
+ // up disagreeing about which import happened (dono, 31/07).
901
916
  const out = opts.census ?? join(root, "_synthesisui", "census.json");
902
917
  await mkdir(dirname(out), { recursive: true });
903
918
  await writeFile(out, `${JSON.stringify(census, null, 2)}\n`, "utf8");
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] [--name <n>] read the app you already have and make it a system
44
+ synthesisui import [--dry] [--scope <p>] 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
@@ -66,6 +66,7 @@ Options:
66
66
  --version <n> install a specific version (default: latest)
67
67
  --ds <slug> init: bring this DS in right away · generate: target DS (default: installed)
68
68
  --name <name> preferred component name for generate
69
+ --scope <path> import: measure only this folder; the census still lands at the root
69
70
  --scheme <s> dark|light - which end of your ladder is the default
70
71
  --target <t> template/init target: next | general (default: next)
71
72
  --pages-dir <dir> init: folder for generated pages (default: app)
@@ -162,6 +163,8 @@ async function main() {
162
163
  name: typeof flags.name === "string" ? flags.name : undefined,
163
164
  // Only these two words. Anything else is a typo that would silently
164
165
  // invert a system, so it falls through to being measured and asked.
166
+ // WHAT to read. `--dir` stays WHERE the project is, in every command.
167
+ scope: typeof flags.scope === "string" ? flags.scope : undefined,
165
168
  scheme: flags.scheme === "dark" || flags.scheme === "light"
166
169
  ? flags.scheme
167
170
  : undefined,
@@ -69,17 +69,42 @@ mismatch instead of letting the server reject it, but the point is not to reach
69
69
 
70
70
  ### 1. Measure
71
71
 
72
+ **Always run from the project root, and always twice in a monorepo.** The root is where the
73
+ governance lives - \`_synthesisui/config.json\`, \`ds/<slug>/\`, and the census. One root, one
74
+ census; re-measuring overwrites it rather than leaving copies to send the wrong one.
75
+
76
+ First, unscoped, to find out what kind of repo this is:
77
+
78
+ \`\`\`
79
+ npx synthesisui import --dry
80
+ \`\`\`
81
+
82
+ If it is a single app, that census is the one. If it holds several projects, the CLI says so
83
+ and **names the candidates itself**, ranked by how many tokens each declares:
84
+
85
+ \`\`\`
86
+ This root holds several projects
87
+ 3 apps (apps/web-admin, apps/web-dashboard, apps/web-review) - and this census
88
+ is the average of all of them. That is a fine DIAGNOSIS and a poor system: a
89
+ light app and a dark one average into a palette that is neither.
90
+
91
+ synthesisui import --scope packages/ui the system comes from here
92
+ \`\`\`
93
+
94
+ Then measure again, narrowed:
95
+
72
96
  \`\`\`
73
- npx synthesisui import --dry --dir <the folder that holds the design decisions>
97
+ npx synthesisui import --dry --scope packages/ui
74
98
  \`\`\`
75
99
 
76
- In a monorepo that folder is almost never the repo root. Look for the package every app
77
- imports from - \`packages/ui\`, \`packages/design-system\`, \`libs/shared-ui\`. The CLI names the
78
- candidates and ranks them by how many tokens they declare; if it printed a workspace summary,
79
- read it before choosing. Scoping to the wrong folder measures the wrong thing, and the score
80
- it reports will be meaninglessly low.
100
+ **Do not import the average.** Three apps measured together produce a palette that belongs to
101
+ none of them, and it will look plausible - a real monorepo reported 36% coverage across 2797
102
+ files at the root and 47% across 114 in its shared package. The second number is the system;
103
+ the first is a diagnosis.
81
104
 
82
- This writes \`_synthesisui/census.json\` and **sends nothing**. Read that file.
105
+ \`--scope\` narrows what is READ. The census still lands at \`<root>/_synthesisui/census.json\`
106
+ and records what it measured, so you always send the same path and there is never a second
107
+ file to pick between. Nothing is sent by \`--dry\`. Read the file.
83
108
 
84
109
  ### 2. Read what arithmetic cannot
85
110
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.60",
3
+ "version": "0.16.61",
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": {