youiyoux 0.1.7 → 0.1.9

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 (66) hide show
  1. package/dist/chunks/{chunk-O66KB7BU.js → chunk-3HON5Q5K.js} +3 -3
  2. package/dist/chunks/{chunk-3SQQNQDN.js → chunk-6GITMASZ.js} +1 -1
  3. package/dist/chunks/{chunk-BITS5O6H.js → chunk-DCBF7QQY.js} +4 -4
  4. package/dist/chunks/{chunk-YWBQO7PO.js → chunk-DWYDHVLZ.js} +74 -6
  5. package/dist/chunks/{chunk-N6RVMQCV.js → chunk-E5OHZJBS.js} +391 -155
  6. package/dist/chunks/{chunk-SIXL67TZ.js → chunk-F33PKGES.js} +2 -2
  7. package/dist/chunks/{chunk-TV5UX3FE.js → chunk-FLBRX6Y3.js} +1 -1
  8. package/dist/chunks/{chunk-7P4UXAIY.js → chunk-HDJ2TOWZ.js} +1 -1
  9. package/dist/chunks/{chunk-4DVBWN7H.js → chunk-Y4IBE6WS.js} +3 -3
  10. package/dist/chunks/{src-SDJCFXRI.js → src-BYNWPNRT.js} +11 -1
  11. package/dist/chunks/{src-DYZIE4XZ.js → src-DPFJ4S2N.js} +2 -2
  12. package/dist/chunks/{src-4JGIPP5A.js → src-EUVBYTKY.js} +4 -4
  13. package/dist/chunks/{src-BKPSGYS2.js → src-M3OJMCUE.js} +3 -3
  14. package/dist/chunks/{src-WP66UHF7.js → src-PURAHRCR.js} +2 -2
  15. package/dist/chunks/{src-NWPGCDGH.js → src-YKPJ3PII.js} +6 -6
  16. package/dist/chunks/{src-J6GPLCVO.js → src-ZG7JU6YJ.js} +2 -2
  17. package/dist/hub/assets/{CandidatePanel-D6Tenu7w.js → CandidatePanel-ZzbsyZo3.js} +1 -1
  18. package/dist/hub/assets/{CatalogScreen-C1tvk0sB.js → CatalogScreen-DDYlvOHy.js} +1 -1
  19. package/dist/hub/assets/{ChangesScreen-D_6yzFGP.js → ChangesScreen-C8_EyHgJ.js} +1 -1
  20. package/dist/hub/assets/{CheatSheet-Ci7mG0sm.js → CheatSheet-DfcPiXdS.js} +1 -1
  21. package/dist/hub/assets/{CommandPalette-C4twwce1.js → CommandPalette-DfFkDTVg.js} +1 -1
  22. package/dist/hub/assets/DiscoverScreen-D6NTgEgk.js +2 -0
  23. package/dist/hub/assets/{DraftEditor-DGK0Fbp1.js → DraftEditor-CUNGYbg0.js} +1 -1
  24. package/dist/hub/assets/{GapsScreen-pYuuMGEF.js → GapsScreen-CqedCj4z.js} +1 -1
  25. package/dist/hub/assets/{HomeScreen-5rm4q3ZO.js → HomeScreen-CIj1QTkw.js} +1 -1
  26. package/dist/hub/assets/{InventoryScreen-CMKKrpTo.js → InventoryScreen-CyvFXa2_.js} +1 -1
  27. package/dist/hub/assets/{LibraryScreen-CV8lwjT6.js → LibraryScreen-C4WU0f42.js} +1 -1
  28. package/dist/hub/assets/{Opened-BvT3JnuG.js → Opened-CeRWkA7c.js} +1 -1
  29. package/dist/hub/assets/{Preview-DNOsVJ9-.js → Preview-CgW-FMB7.js} +1 -1
  30. package/dist/hub/assets/{RuleDetail-BNQbhncD.js → RuleDetail-BDhZ1gCd.js} +1 -1
  31. package/dist/hub/assets/{RulesScreen-ByYZB2Rs.js → RulesScreen-D3rSo_z2.js} +1 -1
  32. package/dist/hub/assets/{ScreenImage-Dypjm8_v.js → ScreenImage-DLeA3nRh.js} +1 -1
  33. package/dist/hub/assets/{ScreensScreen-bgwSlccW.js → ScreensScreen-bGuFIyWj.js} +1 -1
  34. package/dist/hub/assets/{SearchField-DyTJpGOP.js → SearchField-DtEgq9-Q.js} +1 -1
  35. package/dist/hub/assets/{ServiceDown-f9NYtkr_.js → ServiceDown-Bn6AcA3J.js} +1 -1
  36. package/dist/hub/assets/{SpecimenCapture-DFoGMhOM.js → SpecimenCapture-p6YErF5y.js} +1 -1
  37. package/dist/hub/assets/{Specimens-BGwJj05V.js → Specimens-DuaE0WeO.js} +2 -2
  38. package/dist/hub/assets/{StackScreen-q132_QG3.js → StackScreen-DHDulDng.js} +1 -1
  39. package/dist/hub/assets/{StubScreens-DWhYvJku.js → StubScreens-CmhVNX_Z.js} +1 -1
  40. package/dist/hub/assets/{StyleScreen-CcgUv16e.js → StyleScreen-LwTqfGID.js} +1 -1
  41. package/dist/hub/assets/{Tabs-Dy1Z02s7.js → Tabs-Cbh9BCKW.js} +1 -1
  42. package/dist/hub/assets/{TokenDetail-DsYgNrPw.js → TokenDetail-BICobLK1.js} +1 -1
  43. package/dist/hub/assets/{TokensScreen-CTT0GbAH.js → TokensScreen-CyTHn55q.js} +1 -1
  44. package/dist/hub/assets/WorkspaceScreen-uPWqfklh.js +1 -0
  45. package/dist/hub/assets/{client-CIo9EOzr.js → client-BXHiuV_w.js} +1 -1
  46. package/dist/hub/assets/{discover-DGlqvyIe.js → discover-CFhyKler.js} +1 -1
  47. package/dist/hub/assets/{dist-2euSoh6x.js → dist-C6iTWL0b.js} +1 -1
  48. package/dist/hub/assets/{dist-CpzxT6kj.js → dist-DYNYuTjI.js} +1 -1
  49. package/dist/hub/assets/{dist-D8LFTd5W.js → dist-VmEpVQjP.js} +1 -1
  50. package/dist/hub/assets/{family-tiles-Cc7tTwyr.js → family-tiles-B8_onXUi.js} +1 -1
  51. package/dist/hub/assets/folder-tree-YghfTmlZ.js +1 -0
  52. package/dist/hub/assets/main-CfbpuT4Y.js +2 -0
  53. package/dist/hub/assets/{try-live-C-dwP1zF.js → try-live-CX04d2L2.js} +1 -1
  54. package/dist/hub/assets/workflow-DbBdiOmA.js +1 -0
  55. package/dist/hub/index.html +2 -1
  56. package/dist/hub-previews.js +5 -5
  57. package/dist/hub-server.js +3 -3
  58. package/dist/hub-snapshot.js +3 -2
  59. package/dist/schema/youi.status.schema.json +306 -0
  60. package/dist/schema/youi.workspace.schema.json +31 -0
  61. package/dist/youi.js +6 -6
  62. package/dist/youiyoux.js +6 -6
  63. package/package.json +4 -4
  64. package/dist/hub/assets/DiscoverScreen-SOJMVxIq.js +0 -2
  65. package/dist/hub/assets/main-Bjq_U0cU.js +0 -2
  66. package/dist/hub/assets/workflow-CnSY-W41.js +0 -1
@@ -53,7 +53,7 @@ import {
53
53
  vendoredPath,
54
54
  whereUsed,
55
55
  withFile
56
- } from "./chunk-O66KB7BU.js";
56
+ } from "./chunk-3HON5Q5K.js";
57
57
  import {
58
58
  escapingLinks,
59
59
  within
@@ -67,7 +67,7 @@ import {
67
67
  libraryOfPackage,
68
68
  packageOf,
69
69
  playwrightCli
70
- } from "./chunk-7P4UXAIY.js";
70
+ } from "./chunk-HDJ2TOWZ.js";
71
71
  import {
72
72
  MissingIndexError,
73
73
  buildGaps,
@@ -91,7 +91,7 @@ import {
91
91
  showFromCatalog,
92
92
  storeLocation,
93
93
  syncIndex
94
- } from "./chunk-3SQQNQDN.js";
94
+ } from "./chunk-6GITMASZ.js";
95
95
  import {
96
96
  DL_EXT,
97
97
  buildTokens,
@@ -101,7 +101,7 @@ import {
101
101
  parseCssColor,
102
102
  parseDimension,
103
103
  readRules
104
- } from "./chunk-TV5UX3FE.js";
104
+ } from "./chunk-FLBRX6Y3.js";
105
105
  import {
106
106
  Baseline,
107
107
  DESIGN_CONFIG_FILE,
@@ -115,6 +115,8 @@ import {
115
115
  PACKAGE_NAME,
116
116
  ProjectConfig,
117
117
  ScanReport,
118
+ WORKSPACE_FILE,
119
+ WorkspaceConfig,
118
120
  copyleft,
119
121
  executionLabel,
120
122
  findFiles,
@@ -136,7 +138,7 @@ import {
136
138
  shortHash,
137
139
  toPosix,
138
140
  writeFileAtomic
139
- } from "./chunk-YWBQO7PO.js";
141
+ } from "./chunk-DWYDHVLZ.js";
140
142
  import {
141
143
  __toESM
142
144
  } from "./chunk-WNTS7ZO4.js";
@@ -146,7 +148,7 @@ var import_yaml4 = __toESM(require_dist(), 1);
146
148
  import { execFile as execFile5 } from "node:child_process";
147
149
  import { existsSync as existsSync5 } from "node:fs";
148
150
  import { appendFile as appendFile2, readFile as readFile10, rm as rm6, rmdir as rmdir3 } from "node:fs/promises";
149
- import { basename as basename5, dirname as dirname12, join as join24, relative as relative10, resolve as resolve14 } from "node:path";
151
+ import { basename as basename5, dirname as dirname13, join as join25, relative as relative11, resolve as resolve15 } from "node:path";
150
152
  import { parseArgs, promisify as promisify5 } from "node:util";
151
153
 
152
154
  // ../cli/src/check.ts
@@ -164,7 +166,7 @@ import { join as join2, relative, resolve } from "node:path";
164
166
 
165
167
  // ../cli/src/command.ts
166
168
  var COMMAND = "youiyoux";
167
- var SUBCOMMANDS = ["show", "where", "bom", "check", "baseline", "rules", "plan", "migrate", "init", "adopt", "system", "sync", "link", "unlink", "scan", "watch", "hooks", "agents", "mcp", "ui", "library", "discover", "index", "report", "render", "screens", "requests", "detect", "build", "parity", "schema", "inventory", "gaps", "help"];
169
+ var SUBCOMMANDS = ["show", "where", "bom", "check", "baseline", "rules", "plan", "migrate", "init", "adopt", "status", "system", "sync", "link", "unlink", "scan", "watch", "hooks", "agents", "mcp", "ui", "library", "discover", "index", "report", "render", "screens", "requests", "detect", "build", "parity", "schema", "inventory", "gaps", "help"];
168
170
 
169
171
  // ../cli/src/agents.ts
170
172
  var import_yaml = __toESM(require_dist(), 1);
@@ -1058,7 +1060,7 @@ async function runCheck(project2, options = {}) {
1058
1060
  const { report } = scan;
1059
1061
  const checked = new Set(scan.files);
1060
1062
  const unmatched = scope ? [...scope].filter((f) => !checked.has(f)) : [];
1061
- const failed = report.coverage.failed.filter((f) => !scope || scope.has(f.file) || !/\.(tsx?|css)$/.test(f.file));
1063
+ const failed2 = report.coverage.failed.filter((f) => !scope || scope.has(f.file) || !/\.(tsx?|css)$/.test(f.file));
1062
1064
  const wantedRule = (id) => !options.rule || id.endsWith(`/rule/${options.rule}`);
1063
1065
  const violations = report.violations.filter((v) => wantedRule(v.rule) && (!options.quiet || v.severity === "error"));
1064
1066
  const notRun = failedChecks(report.checks).filter((c) => wantedRule(c.rule));
@@ -1069,7 +1071,7 @@ async function runCheck(project2, options = {}) {
1069
1071
  const failing = baseline ? [...baseline.comparison.new, ...baseline.comparison.regressed] : violations;
1070
1072
  const errors = failing.filter((v) => v.severity === "error").length + (lock ?? []).filter((i) => i.severity === "error").length;
1071
1073
  const drift = diff ? await analyseChange(project2, scan, diff, { violations }) : null;
1072
- return { identity: scan.identity, scan, scope, unmatched, failed, notRun, violations, lock, baseline, errors, incomplete: unmatched.length > 0 || failed.length > 0 || notRun.length > 0 || (drift?.incomplete.length ?? 0) > 0, diff: drift };
1074
+ return { identity: scan.identity, scan, scope, unmatched, failed: failed2, notRun, violations, lock, baseline, errors, incomplete: unmatched.length > 0 || failed2.length > 0 || notRun.length > 0 || (drift?.incomplete.length ?? 0) > 0, diff: drift };
1073
1075
  }
1074
1076
  async function lockIssues(project2) {
1075
1077
  const lock = await verifyLock(project2);
@@ -1779,18 +1781,18 @@ async function runClaudeHook(input, target = {}) {
1779
1781
  try {
1780
1782
  const project2 = await load(target, dirname5(file));
1781
1783
  const real = (path) => realpath3(path).catch(() => path);
1782
- const relative11 = relPath(await real(project2.root), await real(file));
1783
- if (!isChecked(project2, relative11)) return SILENT;
1784
- const found = await changeFor(() => workingTreeChange(project2, void 0, [relative11]));
1784
+ const relative12 = relPath(await real(project2.root), await real(file));
1785
+ if (!isChecked(project2, relative12)) return SILENT;
1786
+ const found = await changeFor(() => workingTreeChange(project2, void 0, [relative12]));
1785
1787
  if (found.problem !== void 0) return claudeContext(found.problem, project2.root);
1786
1788
  const { change } = found;
1787
1789
  const result = configured(project2) ? await runCheck(project2, { files: [file], lock: false, baseline: await baselineFor(project2, target), diff: change }) : await runRepoCheck(project2, change);
1788
1790
  const lines = findingLines(result, project2.root);
1789
- const see = COMPONENT_FILE.test(relative11) ? `See it: youiyoux render ${quoted(relative11)} (or the MCP tool ui_render), then open the images: light and dark, desktop and phone, accessibility issues and the diff against the base.` : null;
1790
- if (lines.length === 0) return see ? claudeContext(`youiyoux: ${relative11} passes the design-system check. ${see}`, project2.root) : SILENT;
1791
+ const see = COMPONENT_FILE.test(relative12) ? `See it: youiyoux render ${quoted(relative12)} (or the MCP tool ui_render), then open the images: light and dark, desktop and phone, accessibility issues and the diff against the base.` : null;
1792
+ if (lines.length === 0) return see ? claudeContext(`youiyoux: ${relative12} passes the design-system check. ${see}`, project2.root) : SILENT;
1791
1793
  const older = "violations" in result ? result.violations.filter((v) => !isAdded(change.added, v.file, v.line)).length : 0;
1792
1794
  const known = older > 0 ? ` (${older} older findings on unchanged lines omitted)` : "";
1793
- return claudeContext(`youiyoux design-system check (advisory) on ${relative11}${known}:
1795
+ return claudeContext(`youiyoux design-system check (advisory) on ${relative12}${known}:
1794
1796
  ${lines.join("\n")}${!configured(project2) ? `
1795
1797
  ${result.diff?.notes.join("\n")}` : ""}
1796
1798
  Fix these before finishing (each "Fix:" says what to write); \`youiyoux show <component>\` and the youiyoux MCP tools give the legal values. CI runs the authoritative check.${see ? `
@@ -2085,7 +2087,7 @@ import { homedir, userInfo } from "node:os";
2085
2087
  import { join as join12, resolve as resolve6 } from "node:path";
2086
2088
  async function swapImpact(candidate, family) {
2087
2089
  try {
2088
- const { discoverIndexPath: discoverIndexPath2, sharedStore: sharedStore2 } = await import("./src-DYZIE4XZ.js");
2090
+ const { discoverIndexPath: discoverIndexPath2, sharedStore: sharedStore2 } = await import("./src-DPFJ4S2N.js");
2089
2091
  const path = discoverIndexPath2();
2090
2092
  if (!process.env.DATABASE_URL && !existsSync(path)) return null;
2091
2093
  const impact = await (await sharedStore2(path, process.env, { readOnly: true })).swapImpact(candidate, family);
@@ -2096,7 +2098,7 @@ async function swapImpact(candidate, family) {
2096
2098
  }
2097
2099
  async function libraryLicence(id) {
2098
2100
  try {
2099
- const { discoverCacheDir: discoverCacheDir2, findListing } = await import("./src-DYZIE4XZ.js");
2101
+ const { discoverCacheDir: discoverCacheDir2, findListing } = await import("./src-DPFJ4S2N.js");
2100
2102
  const { entry } = await findListing(discoverCacheDir2(void 0, process.env), id);
2101
2103
  return entry.id === id ? entry.license : null;
2102
2104
  } catch {
@@ -2104,7 +2106,7 @@ async function libraryLicence(id) {
2104
2106
  }
2105
2107
  }
2106
2108
  async function startRuleEditor(options) {
2107
- const { FileStore, RuleWorkflow, serveWorkflow } = await import("./src-4JGIPP5A.js");
2109
+ const { FileStore, RuleWorkflow, serveWorkflow } = await import("./src-EUVBYTKY.js");
2108
2110
  const projectRoot = resolve6(options.project);
2109
2111
  const load2 = { ...options.config ? { configPath: resolve6(options.config) } : {}, ...options.design ? { designRoot: resolve6(options.design) } : {}, ...options.root ? { productRoot: resolve6(options.root) } : {} };
2110
2112
  const project2 = await loadProject(projectRoot, load2);
@@ -2145,7 +2147,7 @@ function builtHubPlan(toolRoot, options, out) {
2145
2147
  if (options.watch && !namesSystem(options)) throw new Error("--watch keeps a system's snapshot live: name it with --design or --project (the demo system is not watched)");
2146
2148
  const hub = join13(toolRoot, BUILT_HUB);
2147
2149
  const snapshot = flags.length > 0 ? [join13(toolRoot, "hub-snapshot.js"), "--out", out, ...flags] : null;
2148
- const server = [join13(toolRoot, "hub-server.js"), "--dist", hub, "--kit", join13(hub, "kit"), "--snapshot", snapshot ? out : join13(toolRoot, BUILT_DEMO), ...snapshot ? ["--build-frame"] : [], ...options.port ? ["--port", options.port] : [], ...options.api ? ["--api", options.api] : [], ...options.watch ? ["--live"] : []];
2150
+ const server = [join13(toolRoot, "hub-server.js"), "--dist", hub, "--kit", join13(hub, "kit"), "--snapshot", snapshot ? out : join13(toolRoot, BUILT_DEMO), ...snapshot ? ["--build-frame"] : [], ...options.port ? ["--port", options.port] : [], ...options.api ? ["--api", options.api] : [], ...options.watch || options.workspace ? ["--live"] : []];
2149
2151
  return { snapshot, server, watch: options.watch && snapshot ? [...snapshot, "--watch"] : null };
2150
2152
  }
2151
2153
  function run5(args, cwd, env, started) {
@@ -2170,6 +2172,7 @@ async function runBuiltHub(toolRoot, options) {
2170
2172
  const code = await run5(plan.snapshot, process.cwd(), {}, (child) => children.add(child));
2171
2173
  if (code !== 0) return code;
2172
2174
  }
2175
+ options.workspace?.((project2) => run5(builtHubPlan(toolRoot, { ...options, project: project2 }, out).snapshot, process.cwd(), {}, (child) => children.add(child)));
2173
2176
  if (plan.watch) children.add(spawn(process.execPath, plan.watch, { cwd: process.cwd(), stdio: "inherit" }));
2174
2177
  return await run5(plan.server, process.cwd(), {}, (child) => children.add(child));
2175
2178
  } finally {
@@ -2189,6 +2192,7 @@ async function runHub(toolRoot, options) {
2189
2192
  const code = await run5(plan.snapshot, process.cwd(), {});
2190
2193
  if (code !== 0) return code;
2191
2194
  }
2195
+ options.workspace?.((project2) => run5(hubPlan(toolRoot, { ...options, project: project2 }).snapshot, process.cwd(), {}));
2192
2196
  const watcher = plan.watch ? spawn(process.execPath, plan.watch, { cwd: process.cwd(), stdio: "inherit" }) : null;
2193
2197
  try {
2194
2198
  return await run5(plan.vite, plan.web, plan.env);
@@ -2353,14 +2357,26 @@ function moveDeclarations(css, blocks, moved, importPath) {
2353
2357
  const rules = readRules(css).filter((r) => blocks.some((b) => r.preludes.at(-1) === b.selector && (b.outer ? r.preludes.length === 2 && r.preludes[0] === b.outer : r.preludes.length === 1)));
2354
2358
  if (rules.length === 0) return null;
2355
2359
  const lines = css.split("\n");
2360
+ const byLine = /* @__PURE__ */ new Map();
2361
+ for (const rule2 of rules) for (const d of rule2.declarations) if (moved.has(d.property.slice(2))) byLine.set(d.line - 1, (byLine.get(d.line - 1) ?? /* @__PURE__ */ new Set()).add(d.property));
2362
+ let count = 0;
2363
+ const kept = [];
2356
2364
  const drop = /* @__PURE__ */ new Set();
2357
- for (const rule2 of rules) {
2358
- for (const d of rule2.declarations) {
2359
- const line = lines[d.line - 1] ?? "";
2360
- if (moved.has(d.property.slice(2)) && new RegExp(`^\\s*${d.property.replace(/[-]/g, "\\-")}\\s*:[^;]*;?\\s*$`).test(line)) drop.add(d.line - 1);
2361
- }
2365
+ for (const [index, properties] of byLine) {
2366
+ const segments = lines[index].split(";");
2367
+ const left = segments.filter((segment) => {
2368
+ const property = /^\s*(--[\w-]+)\s*:/.exec(segment)?.[1];
2369
+ if (property === void 0 || !properties.has(property)) return true;
2370
+ properties.delete(property);
2371
+ count++;
2372
+ return false;
2373
+ });
2374
+ kept.push(...[...properties].map((p) => p.slice(2)));
2375
+ const text = left.join(";");
2376
+ if (text.trim() === "") drop.add(index);
2377
+ else lines[index] = /^\s/.test(text) || index === 0 ? text : `${lines[index].match(/^\s*/)[0]}${text.trimStart()}`;
2362
2378
  }
2363
- if (drop.size === 0) return null;
2379
+ if (count === 0) return null;
2364
2380
  let content = lines.filter((_, i) => !drop.has(i)).join("\n");
2365
2381
  for (const block of blocks) content = content.replace(new RegExp(`(^|\\n)[ \\t]*${block.selector.replace(/[.[\]()"=:]/g, "\\$&")}\\s*\\{\\s*\\}[ \\t]*(?=\\n|$)`, "g"), "");
2366
2382
  for (const outer of new Set(blocks.flatMap((b) => b.outer ? [b.outer] : []))) content = content.replace(new RegExp(`(^|\\n)[ \\t]*${outer.replace(/[.[\]()"=:]/g, "\\$&")}\\s*\\{\\s*\\}[ \\t]*(?=\\n|$)`, "g"), "");
@@ -2370,7 +2386,7 @@ function moveDeclarations(css, blocks, moved, importPath) {
2370
2386
  content = at > 0 ? `${content.slice(0, at)}
2371
2387
  ${statement}${content.slice(at)}` : `${statement}
2372
2388
  ${content}`;
2373
- return { content: content.replace(/\n{3,}/g, "\n\n"), moved: drop.size };
2389
+ return { content: content.replace(/\n{3,}/g, "\n\n"), moved: count, kept: [...new Set(kept)].sort() };
2374
2390
  }
2375
2391
  async function planAdoption(options) {
2376
2392
  const root = options.root;
@@ -2466,6 +2482,10 @@ async function planAdoption(options) {
2466
2482
  const rewritten = moving && found.forked ? { ...moving, content: moving.content.split("\n").filter((line) => !line.includes(GENERATED_MARK) && !/^\/\* Built with Terrazzo/.test(line)).join("\n") } : moving;
2467
2483
  if (found.forked) notes.push(`forked from ${found.forked.system ?? "the System"}${found.forked.version ? ` v${found.forked.version}` : ""}: this product's System starts from the ${found.forked.count} variables it generated into ${found.forked.file}, and the product no longer follows ${found.forked.system ?? "it"}`);
2468
2484
  if (css && moved.size > 0 && !rewritten) notes.push(`${stylesheet}'s variables were copied, not moved (their blocks are nested or share lines): import ${OUT_DIR}/tokens.css from it and remove them by hand`);
2485
+ const read = [":root", ...darkBlocks.map((b) => [b.outer, b.selector].filter(Boolean).join(" > "))];
2486
+ const others = css ? [...new Set(readRules(css).filter((r) => !read.includes(r.preludes.join(" > ")) && !r.preludes.at(-1).startsWith("@") && r.declarations.some((d) => d.property.startsWith("--"))).map((r) => r.preludes.join(" > ").replace(/\s+/g, " ")))] : [];
2487
+ if (others.length > 0) notes.push(`${plural(others.length, "other block")} in ${stylesheet} set${others.length === 1 ? "s" : ""} variables too and stay${others.length === 1 ? "s" : ""} there, as ${others.length === 1 ? "it was" : "they were"} (${others.slice(0, 4).join(" \xB7 ")}${others.length > 4 ? " \xB7 \u2026" : ""}): the System holds the :root${darkBlocks.length > 0 ? " and dark" : ""} values only`);
2488
+ if (rewritten && rewritten.kept.length > 0) notes.push(`${plural(rewritten.kept.length, "variable")} ${rewritten.kept.length === 1 ? "stays" : "stay"} in ${stylesheet} too, written where adopt cannot take them out alone (beside a selector): ${rewritten.kept.slice(0, 4).map((n) => `--${n}`).join(", ")}${rewritten.kept.length > 4 ? ", \u2026" : ""}; they hold the same values, so remove them by hand`);
2469
2489
  for (const [rule2, content] of starterRules(id, componentsDir)) out.push({ path: `design/rules/${rule2}.rule.yaml`, content });
2470
2490
  out.push({
2471
2491
  path: "design/design.md",
@@ -2630,15 +2650,193 @@ async function extractSystem(project2, dir, options = {}) {
2630
2650
  }
2631
2651
  }
2632
2652
 
2653
+ // ../cli/src/status.ts
2654
+ import { realpath as realpath5 } from "node:fs/promises";
2655
+ import { dirname as dirname9, join as join16, relative as relative8, resolve as resolve10 } from "node:path";
2656
+ var placeOf = (cwd, root) => {
2657
+ const rel = relative8(cwd, root) || ".";
2658
+ return rel.split(/[\\/]/).filter((part) => part === "..").length > 1 ? resolve10(root) : rel;
2659
+ };
2660
+ var WRITES = /* @__PURE__ */ new Set(["created", "updated", "merged", "adopted", "conflict", "removed"]);
2661
+ function sourceOf(project2) {
2662
+ if (project2.linked) return `your checkout ${project2.linked}`;
2663
+ const source = project2.config.source;
2664
+ if ("package" in source) return `package ${source.package}`;
2665
+ if ("git" in source) return `git ${source.git} at ${source.ref}`;
2666
+ return resolve10(project2.root) === resolve10(project2.repo.root) ? "this repo" : `path ${source.path}`;
2667
+ }
2668
+ function nextStep(s) {
2669
+ if (s.sync.state === "never") return "youiyoux sync: this product has never been synced with its System";
2670
+ if (s.sync.state === "conflict") return "youiyoux sync: resolve the conflicts it reports, then youiyoux sync --adopt records them";
2671
+ if (s.sync.state === "behind") return `youiyoux sync: ${plural(s.sync.changes.length, "file")} would change`;
2672
+ if (s.check.incomplete) return "youiyoux check: some files or rules could not be checked, and it says which";
2673
+ if (s.check.errors > 0) return `youiyoux check: fix ${plural(s.check.errors, "error")}`;
2674
+ if (s.requests.open > 0) return `youiyoux requests: answer ${plural(s.requests.open, "open request")}`;
2675
+ if (s.check.warnings > 0 && !s.check.baseline) return `youiyoux check: ${plural(s.check.warnings, "warning")} to fix, or youiyoux baseline records them as accepted debt`;
2676
+ return "nothing to do";
2677
+ }
2678
+ async function projectStatus(project2, cwd = process.cwd()) {
2679
+ const lock = await readLock(project2.root);
2680
+ const { actions } = await syncProject(project2, { dryRun: true });
2681
+ const changes = actions.filter((a) => WRITES.has(a.action)).map((a) => ({ file: a.file, action: a.action }));
2682
+ const baselineFile2 = baselinePath(project2);
2683
+ const check = await runCheck(project2, { baseline: await readTextOrNull(baselineFile2) === null ? null : baselineFile2 });
2684
+ const comparison = check.baseline?.comparison;
2685
+ const counted = comparison ? [...comparison.new, ...comparison.regressed] : check.violations;
2686
+ const usages = check.scan.report.usages;
2687
+ const { RequestStore } = await import("./src-EUVBYTKY.js");
2688
+ const requests = await new RequestStore(project2.root, project2.config.id).list();
2689
+ const status = {
2690
+ id: project2.config.id,
2691
+ root: placeOf(cwd, project2.root),
2692
+ system: { id: project2.system.config.id, name: project2.system.config.name, version: project2.system.config.version, source: sourceOf(project2), linked: Boolean(project2.linked), commit: project2.designCommit ?? null },
2693
+ lock: lock ? { systemVersion: lock.systemVersion, syncedAt: lock.syncedAt } : null,
2694
+ sync: { state: lock === null ? "never" : actions.some((a) => a.action === "conflict") ? "conflict" : changes.length > 0 ? "behind" : "current", changes },
2695
+ drift: (await verifyLock(project2) ?? []).map((i) => ({ file: i.file, severity: i.severity, message: i.message })),
2696
+ check: {
2697
+ errors: check.errors,
2698
+ warnings: counted.filter((v) => v.severity === "warn").length,
2699
+ baseline: comparison ? { known: comparison.known.length, new: comparison.new.length + comparison.regressed.length, fixed: comparison.fixed.reduce((n, f) => n + f.gone, 0) } : null,
2700
+ incomplete: check.incomplete
2701
+ },
2702
+ components: { system: project2.system.components.length, used: new Set(usages.map((u) => u.element)).size, sites: usages.length },
2703
+ requests: { open: requests.requests.filter((r) => r.status === "open").length, addressed: requests.requests.filter((r) => r.status === "addressed").length, unreadable: requests.unreadable.length }
2704
+ };
2705
+ return { ...status, next: nextStep(status) };
2706
+ }
2707
+ async function statusTargets(cwd, named2) {
2708
+ if (named2) return { roots: [resolve10(named2)], workspace: null };
2709
+ const product2 = findUp(cwd, PROJECT_FILE);
2710
+ if (product2) return { roots: [product2], workspace: null };
2711
+ const at = findUp(cwd, WORKSPACE_FILE);
2712
+ if (at) {
2713
+ const file = join16(at, WORKSPACE_FILE);
2714
+ const workspace = await readDefinition(file, WorkspaceConfig);
2715
+ return { roots: workspace.projects.map((p) => resolve10(at, p)), workspace: file };
2716
+ }
2717
+ const top = findUp(cwd, ".git") ?? null;
2718
+ if (top === null) return { roots: [], workspace: null };
2719
+ const configs = await findFiles(cwd, [`**/${PROJECT_FILE}`]);
2720
+ const roots = [];
2721
+ for (const config of configs) {
2722
+ const dir = resolve10(cwd, dirname9(config));
2723
+ if (await realpath5(findUp(dir, ".git") ?? dir) === await realpath5(top)) roots.push(dir);
2724
+ }
2725
+ return { roots, workspace: null };
2726
+ }
2727
+ async function workspaceStatus(cwd, named2) {
2728
+ const { roots, workspace } = await statusTargets(cwd, named2);
2729
+ const projects = [];
2730
+ for (const root of roots) {
2731
+ try {
2732
+ projects.push(await projectStatus(await loadProject(root), cwd));
2733
+ } catch (error) {
2734
+ const label = placeOf(cwd, root);
2735
+ projects.push({ root: label, error: error instanceof Error ? error.message : String(error), next: await readTextOrNull(join16(root, PROJECT_FILE)) === null ? `youiyoux adopt or youiyoux init --design <dir> in ${label}` : `fix ${join16(label, PROJECT_FILE)}` });
2736
+ }
2737
+ }
2738
+ return { schema: "youi.status/v1", generatedAt: (/* @__PURE__ */ new Date()).toISOString(), workspace: workspace ? relative8(cwd, workspace) || WORKSPACE_FILE : null, projects };
2739
+ }
2740
+ var failed = (p) => "error" in p;
2741
+ function projectLines(s) {
2742
+ const row = (label, text) => ` ${padEnd(label, 11)} ${text}`;
2743
+ const synced = s.lock ? `synced ${s.lock.syncedAt.slice(0, 10)}${s.lock.systemVersion !== s.system.version ? ` at v${s.lock.systemVersion}` : ""}` : "never synced";
2744
+ const sync = { current: style.ok("up to date"), behind: style.warn(`behind: ${plural(s.sync.changes.length, "file")} would change`), conflict: style.error(`conflict: ${plural(s.sync.changes.filter((c) => c.action === "conflict").length, "file")}`), never: style.warn("never synced") }[s.sync.state];
2745
+ const edited = s.drift.filter((d) => d.severity === "warn");
2746
+ const broken = s.drift.filter((d) => d.severity === "error");
2747
+ const drift = s.drift.length === 0 ? style.ok("none") : [broken.length > 0 ? style.error(`${plural(broken.length, "file")} wrong (${broken.slice(0, 3).map((d) => d.file.split("/").at(-1)).join(", ")})`) : "", edited.length > 0 ? style.warn(`${plural(edited.length, "owned file")} edited locally, merged on sync (${edited.slice(0, 3).map((d) => d.file.split("/").at(-1)).join(", ")})`) : ""].filter(Boolean).join("; ");
2748
+ const { check } = s;
2749
+ const counts = `${check.errors > 0 ? style.error(plural(check.errors, "error")) : style.ok("0 errors")} \xB7 ${plural(check.warnings, "warning")}`;
2750
+ const baseline = check.baseline ? style.dim(` beyond the baseline (${check.baseline.known} accepted${check.baseline.fixed > 0 ? `, ${check.baseline.fixed} fixed` : ""})`) : "";
2751
+ return [
2752
+ `${style.bold(s.id)}${s.root === s.id ? "" : ` ${style.dim(`\xB7 ${s.root}`)}`} \xB7 ${s.system.name} v${s.system.version} ${style.dim(`(${s.system.source}${s.system.linked ? ", linked" : ""}) \xB7 ${synced}`)}`,
2753
+ row("sync", sync),
2754
+ row("drift", drift),
2755
+ row("check", `${counts}${baseline}${check.incomplete ? style.warn(" \xB7 incomplete: not a pass") : ""}`),
2756
+ row("components", `${s.components.used} of ${s.components.system} System components used, at ${plural(s.components.sites, "site")}`),
2757
+ row("requests", s.requests.open + s.requests.addressed + s.requests.unreadable === 0 ? style.dim("none") : [s.requests.open > 0 ? style.warn(`${s.requests.open} open`) : "0 open", `${s.requests.addressed} addressed`, ...s.requests.unreadable > 0 ? [style.error(`${s.requests.unreadable} unreadable`)] : []].join(" \xB7 ")),
2758
+ `${style.bold("Next:")} ${s.next}`
2759
+ ];
2760
+ }
2761
+ function renderStatus(report) {
2762
+ if (report.projects.length === 0) return `${style.warn("!")} no product here: no ${PROJECT_FILE} upward, no ${WORKSPACE_FILE}, and none in this repository below here
2763
+ Next: youiyoux adopt makes this product's own UI its design system, or youiyoux init --design <dir> follows one`;
2764
+ const blocks = report.projects.map((p) => failed(p) ? [`${style.bold(p.root)} ${style.error("\u2716")} ${p.error}`, `${style.bold("Next:")} ${p.next}`] : projectLines(p));
2765
+ const head = report.projects.length > 1 ? [`${style.bold("workspace")} ${style.dim(`\xB7 ${plural(report.projects.length, "product")}${report.workspace ? ` from ${report.workspace}` : " in this repository"}`)}`, ""] : [];
2766
+ return [...head, ...blocks.flatMap((b, i) => i === 0 ? b : ["", ...b])].join("\n");
2767
+ }
2768
+ var statusExitCode = (report) => report.projects.some((p) => failed(p) || p.check.incomplete || p.check.errors > 0 || p.sync.state === "conflict") ? 1 : 0;
2769
+
2770
+ // ../cli/src/workspace-hub.ts
2771
+ import { randomBytes as randomBytes2 } from "node:crypto";
2772
+ import { createServer } from "node:http";
2773
+ var KEY_HEADER = "x-youi-session";
2774
+ function send(res, status, body) {
2775
+ res.writeHead(status, { "Content-Type": "application/json", "Cache-Control": "no-store" });
2776
+ res.end(JSON.stringify(body));
2777
+ }
2778
+ async function readBody(req) {
2779
+ let text = "";
2780
+ for await (const chunk2 of req) {
2781
+ text += String(chunk2);
2782
+ if (text.length > 4096) throw new Error("request too large");
2783
+ }
2784
+ return text ? JSON.parse(text) : {};
2785
+ }
2786
+ async function startWorkspaceHub(options) {
2787
+ const key2 = randomBytes2(18).toString("base64url");
2788
+ let report = await options.status();
2789
+ let current = options.current;
2790
+ let busy = false;
2791
+ const server = createServer((req, res) => {
2792
+ void (async () => {
2793
+ const path = (req.url ?? "").split("?")[0];
2794
+ if (req.method === "GET" && path === "/api/workspace") return send(res, 200, { report, current });
2795
+ if (req.method !== "POST" || path !== "/api/workspace/open" && path !== "/api/workspace/refresh") {
2796
+ return send(res, 404, { error: "Not in a workspace hub: Rules and Changes are read-only here; youiyoux ui --project <dir> opens the rule editor on one product" });
2797
+ }
2798
+ if (req.headers[KEY_HEADER] !== key2) return send(res, 401, { error: "this tab does not carry this run's key" });
2799
+ if (busy) return send(res, 409, { error: "another product is opening; try again in a moment" });
2800
+ busy = true;
2801
+ try {
2802
+ if (path === "/api/workspace/refresh") {
2803
+ report = await options.status();
2804
+ return send(res, 200, { report, current });
2805
+ }
2806
+ const { index } = await readBody(req);
2807
+ const entry = typeof index === "number" && Number.isInteger(index) ? report.projects[index] : void 0;
2808
+ if (entry === void 0 || options.roots[index] === void 0) return send(res, 400, { error: `no product ${JSON.stringify(index)} in this workspace` });
2809
+ if ("error" in entry) return send(res, 400, { error: `${entry.root} cannot be opened: ${entry.error}`, problems: [entry.next] });
2810
+ const code = await options.open(options.roots[index]);
2811
+ if (code !== 0) return send(res, 500, { error: `the snapshot of ${entry.root} failed (exit ${code}); the hub still shows the product it showed` });
2812
+ current = index;
2813
+ return send(res, 200, { report, current });
2814
+ } catch (error) {
2815
+ return send(res, 400, { error: error instanceof Error ? error.message : String(error) });
2816
+ } finally {
2817
+ busy = false;
2818
+ }
2819
+ })();
2820
+ });
2821
+ await new Promise((done) => server.listen(0, "127.0.0.1", done));
2822
+ const address = server.address();
2823
+ const port = typeof address === "object" && address ? address.port : 0;
2824
+ return {
2825
+ url: `http://127.0.0.1:${port}`,
2826
+ key: key2,
2827
+ close: () => new Promise((done) => server.close(() => done()))
2828
+ };
2829
+ }
2830
+
2633
2831
  // ../cli/src/index-store.ts
2634
2832
  import { appendFile, mkdir as mkdir4, readFile as readFile6, stat as stat2, rename as rename2, mkdtemp as mkdtemp4, rm as rm5 } from "node:fs/promises";
2635
- import { dirname as dirname9, join as join16, resolve as resolve10 } from "node:path";
2833
+ import { dirname as dirname10, join as join17, resolve as resolve11 } from "node:path";
2636
2834
  var SOURCES_FILE = "index-sources.json";
2637
2835
  var SCANS_LOG = "scans.jsonl";
2638
2836
  var EMPTY = { discover: null, inventories: [], projects: [] };
2639
2837
  async function readIndexSources(home) {
2640
2838
  try {
2641
- const raw = JSON.parse(await readFile6(join16(home, SOURCES_FILE), "utf8"));
2839
+ const raw = JSON.parse(await readFile6(join17(home, SOURCES_FILE), "utf8"));
2642
2840
  return { discover: raw.discover ?? null, inventories: raw.inventories ?? [], projects: raw.projects ?? [] };
2643
2841
  } catch {
2644
2842
  return EMPTY;
@@ -2651,14 +2849,14 @@ async function noteSource(home, change) {
2651
2849
  inventories: change.inventory && !sources.inventories.includes(change.inventory) ? [...sources.inventories, change.inventory] : sources.inventories,
2652
2850
  projects: change.project ? [...sources.projects.filter((p) => p.root !== change.project.root), change.project] : sources.projects
2653
2851
  };
2654
- await writeFileAtomic(join16(home, SOURCES_FILE), `${JSON.stringify(next2, null, 2)}
2852
+ await writeFileAtomic(join17(home, SOURCES_FILE), `${JSON.stringify(next2, null, 2)}
2655
2853
  `);
2656
2854
  }
2657
2855
  async function recordInventory(store, home, report, file) {
2658
- const run7 = inventoryRun(report, resolve10(file));
2856
+ const run7 = inventoryRun(report, resolve11(file));
2659
2857
  const logged = { run: run7.run, source: run7.source, scannedAt: run7.scannedAt, projects: run7.projects, families: run7.families };
2660
2858
  await mkdir4(home, { recursive: true });
2661
- await appendFile(join16(home, SCANS_LOG), `${JSON.stringify({ schema: "youi.scan-log/v1", ...logged })}
2859
+ await appendFile(join17(home, SCANS_LOG), `${JSON.stringify({ schema: "youi.scan-log/v1", ...logged })}
2662
2860
  `);
2663
2861
  await store.recordInventory(run7);
2664
2862
  await noteSource(home, { inventory: run7.source });
@@ -2669,7 +2867,7 @@ async function recordProject(store, project2, usage) {
2669
2867
  await store.replaceGraph(`project:${project2.root}`, projectGraph({ project: project2.config.id, rules: project2.system.rules, components: project2.system.components.map((c) => ({ id: c.contract.id, name: c.contract.name })), usage }));
2670
2868
  }
2671
2869
  async function readLog(home) {
2672
- const text = await readFile6(join16(home, SCANS_LOG), "utf8").catch(() => "");
2870
+ const text = await readFile6(join17(home, SCANS_LOG), "utf8").catch(() => "");
2673
2871
  return text.split("\n").flatMap((line) => {
2674
2872
  if (!line.trim()) return [];
2675
2873
  try {
@@ -2684,9 +2882,9 @@ async function rebuildIndex(path, options = {}) {
2684
2882
  const started = performance.now();
2685
2883
  const env = options.env ?? process.env;
2686
2884
  const home = indexHome(path, env);
2687
- await mkdir4(dirname9(path), { recursive: true });
2885
+ await mkdir4(dirname10(path), { recursive: true });
2688
2886
  const scratch = !env.DATABASE_URL ? await mkdtemp4(`${path}.rebuild-`) : null;
2689
- const target = scratch ? join16(scratch, "index.db") : path;
2887
+ const target = scratch ? join17(scratch, "index.db") : path;
2690
2888
  const store = scratch ? await openSqlite(target) : await sharedStore(path, env);
2691
2889
  try {
2692
2890
  if (env.DATABASE_URL) await store.reset();
@@ -2694,7 +2892,7 @@ async function rebuildIndex(path, options = {}) {
2694
2892
  const sources = await readIndexSources(home);
2695
2893
  const cache = options.discoverCache ?? sources.discover;
2696
2894
  let discover = { cache, chosen: 0, all: 0 };
2697
- if (cache && await stat2(join16(cache, "index.json")).then(() => true, () => false)) {
2895
+ if (cache && await stat2(join17(cache, "index.json")).then(() => true, () => false)) {
2698
2896
  const loaded = await ingestCatalog(store, cache);
2699
2897
  discover = { cache, chosen: loaded.chosen, all: loaded.all };
2700
2898
  }
@@ -2711,25 +2909,25 @@ async function rebuildIndex(path, options = {}) {
2711
2909
  await store.recordInventory(inventoryRun(parsed.data, file));
2712
2910
  replayed++;
2713
2911
  }
2714
- const failed = [];
2912
+ const failed2 = [];
2715
2913
  let projects = 0;
2716
2914
  const gone = [];
2717
2915
  for (const source of sources.projects) {
2718
2916
  if (!await stat2(source.root).then((s) => s.isDirectory(), () => false)) {
2719
2917
  gone.push(source.root);
2720
- failed.push({ root: source.root, reason: "folder not found (moved or deleted), skipped" });
2918
+ failed2.push({ root: source.root, reason: "folder not found (moved or deleted), skipped" });
2721
2919
  continue;
2722
2920
  }
2723
2921
  try {
2724
2922
  const project2 = await loadProject(source.root, source.options);
2725
- const usage = ScanReport.safeParse(await readJson(source.usage ?? join16(project2.root, ".youi/usage.json")));
2923
+ const usage = ScanReport.safeParse(await readJson(source.usage ?? join17(project2.root, ".youi/usage.json")));
2726
2924
  await recordProject(store, project2, usage.success ? usage.data : null);
2727
2925
  projects++;
2728
2926
  } catch (error) {
2729
- failed.push({ root: source.root, reason: error instanceof Error ? error.message.split("\n")[0] : String(error) });
2927
+ failed2.push({ root: source.root, reason: error instanceof Error ? error.message.split("\n")[0] : String(error) });
2730
2928
  }
2731
2929
  }
2732
- const broken = failed.filter((f) => !gone.includes(f.root));
2930
+ const broken = failed2.filter((f) => !gone.includes(f.root));
2733
2931
  if (broken.length) throw new Error(`Index rebuild failed: ${broken.map((f) => `${f.root}: ${f.reason}`).join("; ")}`);
2734
2932
  await linkCandidates(store);
2735
2933
  const tables = (await store.stats()).tables;
@@ -2753,7 +2951,7 @@ async function rebuildIndex(path, options = {}) {
2753
2951
  throw error;
2754
2952
  }
2755
2953
  }
2756
- return { location: scratch ? path : store.location, discover, scans: log.length, inventories: { replayed, missing }, projects: { replayed: projects, failed }, tables, durationMs: Math.round(performance.now() - started) };
2954
+ return { location: scratch ? path : store.location, discover, scans: log.length, inventories: { replayed, missing }, projects: { replayed: projects, failed: failed2 }, tables, durationMs: Math.round(performance.now() - started) };
2757
2955
  } finally {
2758
2956
  if (scratch) {
2759
2957
  await store.close();
@@ -2939,9 +3137,9 @@ function renderAdoptionRequest(request) {
2939
3137
  }
2940
3138
 
2941
3139
  // ../cli/src/report/run.ts
2942
- import { randomBytes as randomBytes2 } from "node:crypto";
3140
+ import { randomBytes as randomBytes3 } from "node:crypto";
2943
3141
  import { mkdir as mkdir5, rename as rename3, stat as stat3, writeFile as writeFile2 } from "node:fs/promises";
2944
- import { join as join23, relative as relative9, resolve as resolve13 } from "node:path";
3142
+ import { join as join24, relative as relative10, resolve as resolve14 } from "node:path";
2945
3143
 
2946
3144
  // ../cli/src/report/card.ts
2947
3145
  import { deflateSync } from "node:zlib";
@@ -3177,7 +3375,7 @@ function drawCard(input) {
3177
3375
 
3178
3376
  // ../cli/src/report/model.ts
3179
3377
  import { readFile as readFile8 } from "node:fs/promises";
3180
- import { join as join20 } from "node:path";
3378
+ import { join as join21 } from "node:path";
3181
3379
 
3182
3380
  // ../cli/src/report/filters.ts
3183
3381
  var SHOWCASE_GLOBS = ["**/{__stories__,.storybook,storybook,storybook-static,example,examples,demo,demos,playground,showcase,showcases}/**", "**/*.stories.*", "**/*.story.*"];
@@ -3304,11 +3502,11 @@ function publicLook(caption) {
3304
3502
 
3305
3503
  // ../cli/src/report/groups.ts
3306
3504
  import { existsSync as existsSync3 } from "node:fs";
3307
- import { join as join18 } from "node:path";
3505
+ import { join as join19 } from "node:path";
3308
3506
 
3309
3507
  // ../cli/src/report/imports.ts
3310
3508
  import { realpathSync } from "node:fs";
3311
- import { dirname as dirname10, join as join17, relative as relative8, resolve as resolve11 } from "node:path";
3509
+ import { dirname as dirname11, join as join18, relative as relative9, resolve as resolve12 } from "node:path";
3312
3510
  import ts from "typescript";
3313
3511
  var resolvers = /* @__PURE__ */ new Map();
3314
3512
  function resolverOf(root) {
@@ -3316,7 +3514,7 @@ function resolverOf(root) {
3316
3514
  if (!found) {
3317
3515
  const config = ts.findConfigFile(root, ts.sys.fileExists, "tsconfig.json") ?? ts.findConfigFile(root, ts.sys.fileExists, "jsconfig.json");
3318
3516
  const read = config ? ts.readConfigFile(config, ts.sys.readFile) : null;
3319
- const options = config && read && !read.error ? ts.parseJsonConfigFileContent(read.config, ts.sys, dirname10(config)).options : {};
3517
+ const options = config && read && !read.error ? ts.parseJsonConfigFileContent(read.config, ts.sys, dirname11(config)).options : {};
3320
3518
  found = { options, cache: ts.createModuleResolutionCache(root, (name) => name, options) };
3321
3519
  resolvers.set(root, found);
3322
3520
  }
@@ -3327,13 +3525,13 @@ function clearImportCache() {
3327
3525
  }
3328
3526
  async function verifiedImport(root, keeper, from, suggested) {
3329
3527
  const { options, cache } = resolverOf(root);
3330
- let local = relative8(dirname10(join17(root, from)), join17(root, keeper)).replaceAll("\\", "/").replace(/\.(tsx?|jsx?|mjs)$/, "");
3528
+ let local = relative9(dirname11(join18(root, from)), join18(root, keeper)).replaceAll("\\", "/").replace(/\.(tsx?|jsx?|mjs)$/, "");
3331
3529
  if (!local.startsWith(".")) local = `./${local}`;
3332
3530
  for (const specifier of /* @__PURE__ */ new Set([suggested, local])) {
3333
- const found = ts.resolveModuleName(specifier, resolve11(root, from), options, ts.sys, cache).resolvedModule?.resolvedFileName;
3531
+ const found = ts.resolveModuleName(specifier, resolve12(root, from), options, ts.sys, cache).resolvedModule?.resolvedFileName;
3334
3532
  if (found) {
3335
3533
  try {
3336
- if (realpathSync(found) === realpathSync(join17(root, keeper))) return specifier;
3534
+ if (realpathSync(found) === realpathSync(join18(root, keeper))) return specifier;
3337
3535
  } catch {
3338
3536
  }
3339
3537
  }
@@ -3741,7 +3939,7 @@ function factsOf(project2, id, modules, eligible, workspaceNames, dependencies,
3741
3939
  if (dirs.has(file.slice(0, at))) break;
3742
3940
  dirs.add(file.slice(0, at));
3743
3941
  }
3744
- const roots = [...dirs].filter((dir) => existsSync3(join18(project2.root, dir, "package.json"))).sort((a, b) => b.length - a.length);
3942
+ const roots = [...dirs].filter((dir) => existsSync3(join19(project2.root, dir, "package.json"))).sort((a, b) => b.length - a.length);
3745
3943
  const nested = (file) => roots.find((dir) => file.startsWith(`${dir}/`)) ?? "";
3746
3944
  return { project: project2, id, dependencies, packageImports, jsxCounts, eligible, twins, dirs, importer, packageOf: nested };
3747
3945
  }
@@ -3749,11 +3947,11 @@ function factsOf(project2, id, modules, eligible, workspaceNames, dependencies,
3749
3947
  // ../cli/src/report/projects.ts
3750
3948
  var import_yaml3 = __toESM(require_dist(), 1);
3751
3949
  import { readFile as readFile7 } from "node:fs/promises";
3752
- import { basename as basename4, join as join19, resolve as resolve12 } from "node:path";
3950
+ import { basename as basename4, join as join20, resolve as resolve13 } from "node:path";
3753
3951
  var UI_FILES = ["**/*.{tsx,jsx,astro,vue,svelte}"];
3754
3952
  var strings = (value) => Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
3755
3953
  async function workspaceGlobs(root, pkg) {
3756
- const pnpm = await readFile7(join19(root, "pnpm-workspace.yaml"), "utf8").catch(() => null);
3954
+ const pnpm = await readFile7(join20(root, "pnpm-workspace.yaml"), "utf8").catch(() => null);
3757
3955
  if (pnpm !== null) {
3758
3956
  try {
3759
3957
  const globs2 = strings((0, import_yaml3.parse)(pnpm)?.packages);
@@ -3767,8 +3965,8 @@ async function workspaceGlobs(root, pkg) {
3767
3965
  }
3768
3966
  var hasUi = async (dir) => (await findFiles(dir, UI_FILES, DEFAULT_EXCLUDE)).length > 0;
3769
3967
  async function findProjects(dir) {
3770
- const root = resolve12(dir);
3771
- const pkg = await readJson(join19(root, "package.json"));
3968
+ const root = resolve13(dir);
3969
+ const pkg = await readJson(join20(root, "package.json"));
3772
3970
  const name = typeof pkg?.name === "string" && pkg.name.trim() ? pkg.name.trim() : basename4(root);
3773
3971
  const single = { dir: ".", root, name: typeof pkg?.name === "string" ? pkg.name : null };
3774
3972
  const workspace = await workspaceGlobs(root, pkg);
@@ -3779,12 +3977,12 @@ async function findProjects(dir) {
3779
3977
  const projects = [];
3780
3978
  const skipped = [];
3781
3979
  for (const member of members) {
3782
- const memberRoot = join19(root, member);
3980
+ const memberRoot = join20(root, member);
3783
3981
  if (!await hasUi(memberRoot)) {
3784
3982
  skipped.push(member);
3785
3983
  continue;
3786
3984
  }
3787
- const memberPkg = await readJson(join19(memberRoot, "package.json"));
3985
+ const memberPkg = await readJson(join20(memberRoot, "package.json"));
3788
3986
  projects.push({ dir: member, root: memberRoot, name: typeof memberPkg?.name === "string" ? memberPkg.name : null });
3789
3987
  }
3790
3988
  if (projects.length === 0) return { root, name, layout: `one project (${workspace.source} names no package with UI files)`, projects: [single], skipped };
@@ -3811,7 +4009,7 @@ var Lines = class {
3811
4009
  if (!this.enabled) return { at };
3812
4010
  let lines = this.files.get(file);
3813
4011
  if (!lines) {
3814
- lines = (await readFile8(join20(this.root, file), "utf8").catch(() => "")).split("\n");
4012
+ lines = (await readFile8(join21(this.root, file), "utf8").catch(() => "")).split("\n");
3815
4013
  this.files.set(file, lines);
3816
4014
  }
3817
4015
  const code = lines[line - 1]?.trim();
@@ -3826,15 +4024,15 @@ function hardcodedValue(finding3) {
3826
4024
  const value = bracket ?? (declaration ? `${declaration[2]} (${declaration[1]})` : finding3.text);
3827
4025
  return { word: word === "type" ? "text size" : word, value, tokens };
3828
4026
  }
3829
- async function modulesOf(project2, target, failed) {
4027
+ async function modulesOf(project2, target, failed2) {
3830
4028
  const modules = [];
3831
4029
  for (const file of target.files) {
3832
4030
  if (isShowcase(file)) continue;
3833
4031
  try {
3834
- const module = await parseModule(file, await readFile8(join20(project2.root, file), "utf8"));
4032
+ const module = await parseModule(file, await readFile8(join21(project2.root, file), "utf8"));
3835
4033
  if (module) modules.push(module);
3836
4034
  } catch (error) {
3837
- failed.push(`${repoPath(project2.dir, file)}: ${errorText(error)}`);
4035
+ failed2.push(`${repoPath(project2.dir, file)}: ${errorText(error)}`);
3838
4036
  }
3839
4037
  }
3840
4038
  return modules;
@@ -3855,7 +4053,7 @@ async function grouped(project2, found, lines, describe) {
3855
4053
  return findings2.sort((a, b) => b.count - a.count || a.title.localeCompare(b.title));
3856
4054
  }
3857
4055
  async function analyseProject(project2, index, inventory, inventoryError, workspaceNames, lines) {
3858
- const failed = [];
4056
+ const failed2 = [];
3859
4057
  const incomplete = [];
3860
4058
  const notes = [];
3861
4059
  const projectId = inventory?.projects[index]?.id;
@@ -3863,7 +4061,7 @@ async function analyseProject(project2, index, inventory, inventoryError, worksp
3863
4061
  let target = null;
3864
4062
  try {
3865
4063
  target = await loadInventoryTarget(project2.root);
3866
- modules = await modulesOf(project2, target, failed);
4064
+ modules = await modulesOf(project2, target, failed2);
3867
4065
  } catch (error) {
3868
4066
  incomplete.push(`The code could not be read (${errorText(error)})`);
3869
4067
  }
@@ -3906,7 +4104,7 @@ async function analyseProject(project2, index, inventory, inventoryError, worksp
3906
4104
  const components = [...byComponent.values()].map((c) => ({ name: c.name, file: c.file, uses: c.roles.length, role: [...new Set(c.roles)].sort((a, b) => c.roles.filter((r) => r === b).length - c.roles.filter((r) => r === a).length)[0] })).sort((a, b) => b.uses - a.uses || a.name.localeCompare(b.name)).slice(0, TOP_COMPONENTS);
3907
4105
  const inventoryProject = inventory?.projects[index];
3908
4106
  if (inventoryError) incomplete.push(`The component inventory could not be built (${inventoryError}); run ${NPX} inventory --project ${project2.dir} to see why`);
3909
- for (const f of inventoryProject?.failed ?? []) failed.push(`${repoPath(project2.dir, f.file)}: ${f.reason}`);
4107
+ for (const f of inventoryProject?.failed ?? []) failed2.push(`${repoPath(project2.dir, f.file)}: ${f.reason}`);
3910
4108
  let hardcoded = [];
3911
4109
  let offLibrary = [];
3912
4110
  const everyLine = () => true;
@@ -3935,7 +4133,7 @@ async function analyseProject(project2, index, inventory, inventoryError, worksp
3935
4133
  incomplete.push(`Library choices were not compared (${errorText(error)}); run ${NPX} detect ${project2.dir} to see why`);
3936
4134
  }
3937
4135
  }
3938
- const pkg = await readFile8(join20(project2.root, "package.json"), "utf8").then((text) => JSON.parse(text), () => null);
4136
+ const pkg = await readFile8(join21(project2.root, "package.json"), "utf8").then((text) => JSON.parse(text), () => null);
3939
4137
  const dependencies = new Set(["dependencies", "devDependencies", "peerDependencies"].flatMap((key2) => Object.keys(pkg?.[key2] ?? {})));
3940
4138
  const importer = changeProject ? await importOf(changeProject) : () => "";
3941
4139
  const facts = factsOf(project2, projectId ?? null, modules, eligible, workspaceNames, dependencies, target?.files ?? [], importer);
@@ -3943,7 +4141,7 @@ async function analyseProject(project2, index, inventory, inventoryError, worksp
3943
4141
  dir: project2.dir,
3944
4142
  name: project2.name,
3945
4143
  files: inventoryProject?.files ?? modules.length,
3946
- failed,
4144
+ failed: failed2,
3947
4145
  libraries,
3948
4146
  unused,
3949
4147
  definitions: inventory ? inventory.definitions.filter((d) => d.project === projectId && !isShowcase(d.file)).length : 0,
@@ -4379,36 +4577,36 @@ ${model.projects.map((p) => projectSection(p, many, looks)).join("\n")}
4379
4577
  import { spawn as spawn2 } from "node:child_process";
4380
4578
  import { existsSync as existsSync4, readFileSync as readFileSync2 } from "node:fs";
4381
4579
  import { homedir as homedir2 } from "node:os";
4382
- import { dirname as dirname11, join as join21 } from "node:path";
4580
+ import { dirname as dirname12, join as join22 } from "node:path";
4383
4581
  var LOOKS_PER_PROJECT = 4;
4384
4582
  var DOWNLOAD_SIZE = "~100 MB";
4385
4583
  var DEPENDENCIES = "Install the repo's dependencies to see rendered looks";
4386
4584
  var UNDRAWABLE = /not installed|loads no stylesheet|drawn from (?:its|their) (?:tag and )?classes|did not render|bundle failed|unstyled|stub fallback/i;
4387
- var playwrightDir = () => dirname11(playwrightCli());
4585
+ var playwrightDir = () => dirname12(playwrightCli());
4388
4586
  function browsersPath(env) {
4389
4587
  const set = env.PLAYWRIGHT_BROWSERS_PATH;
4390
- if (set === "0") return join21(playwrightDir(), ".local-browsers");
4588
+ if (set === "0") return join22(playwrightDir(), ".local-browsers");
4391
4589
  if (set) return set;
4392
- if (process.platform === "darwin") return join21(homedir2(), "Library/Caches/ms-playwright");
4393
- if (process.platform === "win32") return join21(env.LOCALAPPDATA ?? join21(homedir2(), "AppData/Local"), "ms-playwright");
4394
- return join21(env.XDG_CACHE_HOME ?? join21(homedir2(), ".cache"), "ms-playwright");
4590
+ if (process.platform === "darwin") return join22(homedir2(), "Library/Caches/ms-playwright");
4591
+ if (process.platform === "win32") return join22(env.LOCALAPPDATA ?? join22(homedir2(), "AppData/Local"), "ms-playwright");
4592
+ return join22(env.XDG_CACHE_HOME ?? join22(homedir2(), ".cache"), "ms-playwright");
4395
4593
  }
4396
4594
  function chromiumStatus(env = process.env) {
4397
4595
  if (env.YOUI_CHROMIUM) return { available: existsSync4(env.YOUI_CHROMIUM), where: env.YOUI_CHROMIUM };
4398
4596
  try {
4399
- const browsers = JSON.parse(readFileSync2(join21(playwrightDir(), "browsers.json"), "utf8"));
4597
+ const browsers = JSON.parse(readFileSync2(join22(playwrightDir(), "browsers.json"), "utf8"));
4400
4598
  const revision2 = browsers.browsers.find((b) => b.name === "chromium-headless-shell")?.revision;
4401
- const dir = join21(browsersPath(env), `chromium_headless_shell-${revision2}`);
4402
- return { available: revision2 !== void 0 && existsSync4(join21(dir, "INSTALLATION_COMPLETE")), where: dir };
4599
+ const dir = join22(browsersPath(env), `chromium_headless_shell-${revision2}`);
4600
+ return { available: revision2 !== void 0 && existsSync4(join22(dir, "INSTALLATION_COMPLETE")), where: dir };
4403
4601
  } catch {
4404
4602
  return { available: false, where: "playwright-core is not installed" };
4405
4603
  }
4406
4604
  }
4407
4605
  function downloadChromium() {
4408
- return new Promise((resolve15) => {
4409
- const child = spawn2(process.execPath, [join21(playwrightDir(), "cli.js"), "install", "chromium-headless-shell"], { stdio: ["ignore", 2, 2] });
4410
- child.on("error", () => resolve15(false));
4411
- child.on("close", (code) => resolve15(code === 0));
4606
+ return new Promise((resolve16) => {
4607
+ const child = spawn2(process.execPath, [join22(playwrightDir(), "cli.js"), "install", "chromium-headless-shell"], { stdio: ["ignore", 2, 2] });
4608
+ child.on("error", () => resolve16(false));
4609
+ child.on("close", (code) => resolve16(code === 0));
4412
4610
  });
4413
4611
  }
4414
4612
  var dataUri = (file) => file && existsSync4(file) ? `data:image/png;base64,${readFileSync2(file).toString("base64")}` : null;
@@ -4422,24 +4620,24 @@ async function addLooks(model, repoRoot, options = {}) {
4422
4620
  const where2 = section.dir === "." ? "this repo" : section.dir;
4423
4621
  try {
4424
4622
  const installed = (name) => {
4425
- let dir = join21(repoRoot, section.dir);
4623
+ let dir = join22(repoRoot, section.dir);
4426
4624
  while (true) {
4427
- if (existsSync4(join21(dir, "node_modules", name, "package.json"))) return true;
4428
- if (dir === repoRoot || dirname11(dir) === dir) return false;
4429
- dir = dirname11(dir);
4625
+ if (existsSync4(join22(dir, "node_modules", name, "package.json"))) return true;
4626
+ if (dir === repoRoot || dirname12(dir) === dir) return false;
4627
+ dir = dirname12(dir);
4430
4628
  }
4431
4629
  };
4432
4630
  if (!installed("react") || !installed("react-dom")) {
4433
4631
  missing.push(where2);
4434
4632
  continue;
4435
4633
  }
4436
- const projectRoot = join21(repoRoot, section.dir);
4634
+ const projectRoot = join22(repoRoot, section.dir);
4437
4635
  const target = await loadInventoryTarget(projectRoot);
4438
4636
  if (findStylesheets(projectRoot, target.files, { tsconfig: target.tsconfig, override: target.specimenCss, templates: target.templates }).found.length === 0) {
4439
4637
  missing.push(where2);
4440
4638
  continue;
4441
4639
  }
4442
- const report = await renderShots(join21(repoRoot, section.dir), names, { download: false, base: "HEAD", ...options.executablePath ? { executablePath: options.executablePath } : {}, ...options.cache ? { cache: options.cache } : {}, ...options.log ? { log: options.log } : {} });
4640
+ const report = await renderShots(join22(repoRoot, section.dir), names, { download: false, base: "HEAD", ...options.executablePath ? { executablePath: options.executablePath } : {}, ...options.cache ? { cache: options.cache } : {}, ...options.log ? { log: options.log } : {} });
4443
4641
  const unavailable = report.notes.some((note) => UNDRAWABLE.test(note));
4444
4642
  if (unavailable) {
4445
4643
  missing.push(where2);
@@ -4511,7 +4709,7 @@ function renderMarkdown(model, reportFile) {
4511
4709
  import { realpathSync as realpathSync2 } from "node:fs";
4512
4710
  import { readFile as readFile9 } from "node:fs/promises";
4513
4711
  import { homedir as homedir3, userInfo as userInfo2 } from "node:os";
4514
- import { join as join22 } from "node:path";
4712
+ import { join as join23 } from "node:path";
4515
4713
  var REDACTED = "[redacted]";
4516
4714
  var OUTSIDE = "[path outside the repo]";
4517
4715
  var MIN_VALUE = 6;
@@ -4523,7 +4721,7 @@ async function dotenvValues(root) {
4523
4721
  const files = await findFiles(root, [".env", ".env.*", "**/.env", "**/.env.*"]).catch(() => []);
4524
4722
  const values = [];
4525
4723
  for (const file of files) {
4526
- const text = await readFile9(join22(root, file), "utf8").catch(() => "");
4724
+ const text = await readFile9(join23(root, file), "utf8").catch(() => "");
4527
4725
  for (const line of text.split(/\r?\n/)) {
4528
4726
  const match = /^\s*(?:export\s+)?[A-Za-z_][A-Za-z0-9_.-]*\s*=\s*(.*)$/.exec(line);
4529
4727
  if (!match) continue;
@@ -4650,12 +4848,12 @@ async function askTerminal(question) {
4650
4848
  }
4651
4849
  }
4652
4850
  async function writeBinaryAtomic(file, bytes) {
4653
- const temp = `${file}.${process.pid}.${randomBytes2(6).toString("hex")}.tmp`;
4851
+ const temp = `${file}.${process.pid}.${randomBytes3(6).toString("hex")}.tmp`;
4654
4852
  await writeFile2(temp, bytes);
4655
4853
  await rename3(temp, file);
4656
4854
  }
4657
4855
  var shown = (file) => {
4658
- const rel = relative9(process.cwd(), file);
4856
+ const rel = relative10(process.cwd(), file);
4659
4857
  return rel && !rel.startsWith("..") ? rel : file;
4660
4858
  };
4661
4859
  async function runReport(options = {}, io = {}) {
@@ -4664,7 +4862,7 @@ async function runReport(options = {}, io = {}) {
4664
4862
  const error = io.error ?? ((text) => void process.stderr.write(`${text}
4665
4863
  `));
4666
4864
  const env = io.env ?? process.env;
4667
- const root = resolve13(options.path ?? process.cwd());
4865
+ const root = resolve14(options.path ?? process.cwd());
4668
4866
  if (!(await stat3(root).catch(() => null))?.isDirectory()) {
4669
4867
  error(`\u2716 ${options.path ?? root} is not a directory: run ${NPX} in a repo, or pass its path (${NPX} path/to/repo)`);
4670
4868
  return { code: 2, files: null, model: null };
@@ -4694,9 +4892,9 @@ async function runReport(options = {}, io = {}) {
4694
4892
  model.durationMs += Math.round(performance.now() - started);
4695
4893
  const redactor = await Redactor.forRepo(root, { includeCode: options.includeCode === true, env });
4696
4894
  const clean = redactor.model(model);
4697
- const outDir = resolve13(options.out ?? root);
4895
+ const outDir = resolve14(options.out ?? root);
4698
4896
  await mkdir5(outDir, { recursive: true });
4699
- const files = { report: join23(outDir, REPORT_FILE), card: join23(outDir, CARD_FILE), summary: join23(outDir, SUMMARY_FILE) };
4897
+ const files = { report: join24(outDir, REPORT_FILE), card: join24(outDir, CARD_FILE), summary: join24(outDir, SUMMARY_FILE) };
4700
4898
  const t = clean.totals;
4701
4899
  const card = {
4702
4900
  repo: redactor.scrub(clean.repo),
@@ -4780,6 +4978,11 @@ ${style.bold("Anywhere")} ${style.dim("no design system needed")}
4780
4978
  an unchanged component comes from the cache (YOUI_SPECIMEN_CACHE)
4781
4979
 
4782
4980
  ${style.bold("In a product repo")}
4981
+ status [--json] where the product stands with its design system, in a few lines: the System
4982
+ and its source, what a sync would change (a dry run, offline), local drift,
4983
+ errors and warnings (beyond the baseline), components used, open requests,
4984
+ and the one thing to do next. In a workspace (youi.workspace.json, or a repo
4985
+ holding several products), one block per product
4783
4986
  adopt [--components dir] [--css file] [--system id] [--fork] [--dry-run]
4784
4987
  make this product's own UI its design system, kept in this repo: a draft
4785
4988
  contract per component in the components folder (the files stay where they
@@ -4883,7 +5086,8 @@ ${style.bold("Detect")} ${style.dim("which UI libraries a repo or website uses
4883
5086
 
4884
5087
  ${style.bold("The hub")}
4885
5088
  ui [--design dir] [--project dir] [--config file] [--root dir] [--port n] [--watch] [--state dir] [--inventory file] [--detect file]
4886
- open the web hub on a design system (default: the demo system);
5089
+ open the web hub on a design system (default: the demo system; beside a
5090
+ youi.workspace.json, its products, with Work > Workspace to switch);
4887
5091
  --project adds that product's usage map (read-only scan) and the
4888
5092
  rule editor (System > Rules, Work > Changes): drafts, impact preview,
4889
5093
  approval, a review branch in the design repo (drafts kept in --state);
@@ -4992,10 +5196,10 @@ async function readJsonOrFile(value) {
4992
5196
  async function cmdScan(values) {
4993
5197
  const p = await project(values);
4994
5198
  const { report } = await scanProject(p);
4995
- const out = values.out ?? join24(p.root, ".youi/usage.json");
4996
- await writeFileAtomic(resolve14(out), `${JSON.stringify(report)}
5199
+ const out = values.out ?? join25(p.root, ".youi/usage.json");
5200
+ await writeFileAtomic(resolve15(out), `${JSON.stringify(report)}
4997
5201
  `);
4998
- const recorded = await recordUsage(values, p, report, resolve14(out));
5202
+ const recorded = await recordUsage(values, p, report, resolve15(out));
4999
5203
  if (values.json) json3(report);
5000
5204
  else print([renderScan(report, out), ...recorded ? [recorded] : []].join("\n"));
5001
5205
  return 0;
@@ -5004,7 +5208,7 @@ async function recordUsage(values, p, report, usage) {
5004
5208
  const { path, home } = indexOf(values);
5005
5209
  try {
5006
5210
  await recordProject(await sharedStore(path), p, report);
5007
- await noteSource(home, { project: { root: resolve14(values.project ?? process.cwd()), options: Object.fromEntries(Object.entries(loadOptions(values)).map(([key2, value]) => [key2, resolve14(value)])), usage } });
5211
+ await noteSource(home, { project: { root: resolve15(values.project ?? process.cwd()), options: Object.fromEntries(Object.entries(loadOptions(values)).map(([key2, value]) => [key2, resolve15(value)])), usage } });
5008
5212
  return null;
5009
5213
  } catch (error) {
5010
5214
  return notRecorded(error);
@@ -5019,17 +5223,17 @@ function indexOf(values) {
5019
5223
  }
5020
5224
  async function cmdGaps(values) {
5021
5225
  const dirs = inventoryDirs(values);
5022
- const inventory = values.inventory ? InventoryReport.parse(JSON.parse(await readFile10(resolve14(values.inventory), "utf8"))) : await scanInventory(values, dirs);
5023
- const cwd = values.inventory ? dirname12(resolve14(values.inventory)) : process.cwd();
5024
- const project2 = await loadProject(resolve14(dirs[0]), loadOptions(values)).catch(() => null);
5226
+ const inventory = values.inventory ? InventoryReport.parse(JSON.parse(await readFile10(resolve15(values.inventory), "utf8"))) : await scanInventory(values, dirs);
5227
+ const cwd = values.inventory ? dirname13(resolve15(values.inventory)) : process.cwd();
5228
+ const project2 = await loadProject(resolve15(dirs[0]), loadOptions(values)).catch(() => null);
5025
5229
  const system = project2 ? { name: project2.system.config.name, components: project2.system.components.map((c) => c.contract.name) } : null;
5026
- const store = await readCatalog(resolve14(discoverCacheDir(values.cache)), indexOf(values).path).catch((error) => {
5230
+ const store = await readCatalog(resolve15(discoverCacheDir(values.cache)), indexOf(values).path).catch((error) => {
5027
5231
  if (error instanceof MissingIndexError) return null;
5028
5232
  throw error;
5029
5233
  });
5030
- const roots = new Map(inventory.projects.map((p) => [p.id, resolve14(cwd, p.root)]));
5234
+ const roots = new Map(inventory.projects.map((p) => [p.id, resolve15(cwd, p.root)]));
5031
5235
  const report = await buildGaps({ inventory, system, dependencies: declaredPackages(roots) }, store);
5032
- if (values.out) await writeFileAtomic(resolve14(values.out), `${JSON.stringify(report, null, 2)}
5236
+ if (values.out) await writeFileAtomic(resolve15(values.out), `${JSON.stringify(report, null, 2)}
5033
5237
  `);
5034
5238
  if (values.json) json3(report);
5035
5239
  else print(renderGaps(report));
@@ -5039,12 +5243,12 @@ async function cmdInventory(values) {
5039
5243
  if (values.history) return cmdHistory(values, []);
5040
5244
  const dirs = inventoryDirs(values);
5041
5245
  const report = await scanInventory(values, dirs);
5042
- const out = resolve14(values.out ?? join24(dirs[0], ".youi/inventory.json"));
5246
+ const out = resolve15(values.out ?? join25(dirs[0], ".youi/inventory.json"));
5043
5247
  await writeFileAtomic(out, `${JSON.stringify(report, null, 2)}
5044
5248
  `);
5045
5249
  const recorded = await recordScan(values, report, out);
5046
5250
  if (values.json) json3(report);
5047
- else print([renderInventory(report, relative10(process.cwd(), out).startsWith("..") ? out : relative10(process.cwd(), out)), recorded].join("\n"));
5251
+ else print([renderInventory(report, relative11(process.cwd(), out).startsWith("..") ? out : relative11(process.cwd(), out)), recorded].join("\n"));
5048
5252
  return 0;
5049
5253
  }
5050
5254
  async function recordScan(values, report, out) {
@@ -5069,7 +5273,7 @@ async function cmdIndex(values, positionals) {
5069
5273
  const [action, ...rest] = positionals;
5070
5274
  const { path, home } = indexOf(values);
5071
5275
  if (action === "path") {
5072
- if (values.json) json3({ store: storeLocation({ path }), sources: join24(home, SOURCES_FILE), scans: join24(home, SCANS_LOG) });
5276
+ if (values.json) json3({ store: storeLocation({ path }), sources: join25(home, SOURCES_FILE), scans: join25(home, SCANS_LOG) });
5073
5277
  else print(storeLocation({ path }));
5074
5278
  return 0;
5075
5279
  }
@@ -5080,7 +5284,7 @@ async function cmdIndex(values, positionals) {
5080
5284
  return 0;
5081
5285
  }
5082
5286
  if (action === "rebuild") {
5083
- const summary = await rebuildIndex(path, values.cache ? { discoverCache: resolve14(values.cache) } : {});
5287
+ const summary = await rebuildIndex(path, values.cache ? { discoverCache: resolve15(values.cache) } : {});
5084
5288
  if (values.json) json3(summary);
5085
5289
  else print(renderRebuild(summary));
5086
5290
  return 0;
@@ -5120,7 +5324,7 @@ async function cmdBom(values, positionals) {
5120
5324
  return 0;
5121
5325
  }
5122
5326
  function baselineFile(values, p) {
5123
- if (values["baseline-file"]) return resolve14(values["baseline-file"]);
5327
+ if (values["baseline-file"]) return resolve15(values["baseline-file"]);
5124
5328
  return values.baseline ? baselinePath(p, values.config) : null;
5125
5329
  }
5126
5330
  var checkScope = (values, positionals) => ({ ...positionals.length > 0 ? { files: positionals } : {}, ...values.rule ? { rule: values.rule } : {}, quiet: values.quiet });
@@ -5177,7 +5381,7 @@ async function cmdBaseline(values) {
5177
5381
  const { totals } = written.baseline;
5178
5382
  if (values.json) json3({ file: written.file, action: written.action, totals, notAdded: written.notAdded, identity: written.baseline.identity });
5179
5383
  else {
5180
- print(`${style.ok(written.action)} ${relative10(process.cwd(), file) || file}: ${plural(totals.errors, "error")} \xB7 ${plural(totals.warnings, "warning")} accepted`);
5384
+ print(`${style.ok(written.action)} ${relative11(process.cwd(), file) || file}: ${plural(totals.errors, "error")} \xB7 ${plural(totals.warnings, "warning")} accepted`);
5181
5385
  print(style.dim(` rules ${describeIdentity(written.baseline.identity)}`));
5182
5386
  if (written.notAdded > 0) print(style.warn(` ${plural(written.notAdded, "current violation")} not added: fix them, or accept them deliberately with --force`));
5183
5387
  }
@@ -5248,7 +5452,7 @@ async function cmdInit(values) {
5248
5452
  if (!values.design) throw new UsageError("youiyoux init --design <dir|package> (a design repo checkout, the directory holding youi.config.json, or the npm package that holds it)");
5249
5453
  const root = values.project ?? process.cwd();
5250
5454
  const design = await designArg(values.design, root);
5251
- const result = await initProject({ root, ...design, schemaDir: join24(TOOL_ROOT, "schema"), ...values.system ? { system: values.system } : {}, ...values.components ? { componentsDir: values.components } : {}, force: values.force });
5455
+ const result = await initProject({ root, ...design, schemaDir: join25(TOOL_ROOT, "schema"), ...values.system ? { system: values.system } : {}, ...values.components ? { componentsDir: values.components } : {}, force: values.force });
5252
5456
  if (!values.json) {
5253
5457
  print(`${style.ok("created")} ${result.file}`);
5254
5458
  for (const note of result.notes) print(style.dim(` \xB7 ${note}`));
@@ -5258,28 +5462,28 @@ async function cmdInit(values) {
5258
5462
  return cmdSync({ ...design.package ? rest : values, project: root });
5259
5463
  }
5260
5464
  async function cmdAdopt(values) {
5261
- const root = resolve14(values.project ?? process.cwd());
5262
- if (await exists(join24(root, PROJECT_FILE))) throw new Error(`${root} already has ${PROJECT_FILE}: it follows a design system already (youiyoux sync updates it)`);
5465
+ const root = resolve15(values.project ?? process.cwd());
5466
+ if (await exists(join25(root, PROJECT_FILE))) throw new Error(`${root} already has ${PROJECT_FILE}: it follows a design system already (youiyoux sync updates it)`);
5263
5467
  const plan = await planAdoption({ root, ...values.components ? { componentsDir: values.components } : {}, ...values.css ? { css: values.css } : {}, ...values.system ? { system: values.system } : {}, fork: values.fork });
5264
- const taken = (await Promise.all(plan.files.map(async (f) => await exists(join24(root, f.path)) ? f.path : null))).filter((f) => f !== null);
5468
+ const taken = (await Promise.all(plan.files.map(async (f) => await exists(join25(root, f.path)) ? f.path : null))).filter((f) => f !== null);
5265
5469
  if (taken.length > 0) throw new Error(`nothing was written: ${taken.slice(0, 5).join(", ")}${taken.length > 5 ? ` and ${taken.length - 5} more` : ""} already exist${taken.length === 1 ? "s" : ""}`);
5266
5470
  if (values["dry-run"]) {
5267
5471
  if (values.json) json3(plan);
5268
5472
  else print([...adoptLines(plan, true), "", style.dim(`dry run: nothing written; would write ${plan.files.map((f) => f.path).join(", ")}${plan.stylesheet ? ` and change ${plan.stylesheet.path}` : ""}`)].join("\n"));
5269
5473
  return 0;
5270
5474
  }
5271
- const stylesheet = plan.stylesheet ? await readFile10(join24(root, plan.stylesheet.path), "utf8") : null;
5475
+ const stylesheet = plan.stylesheet ? await readFile10(join25(root, plan.stylesheet.path), "utf8") : null;
5272
5476
  try {
5273
- for (const file of plan.files) await writeFileAtomic(join24(root, file.path), file.content);
5274
- if (plan.stylesheet) await writeFileAtomic(join24(root, plan.stylesheet.path), plan.stylesheet.content);
5275
- await initProject({ root, design: root, schemaDir: join24(TOOL_ROOT, "schema"), system: plan.system.id, componentsDir: plan.componentsDir, outputs: { ...plan.outputs, targets: [...plan.outputs.targets], dark: [...plan.outputs.dark] }, tokenSources: plan.tokenSources });
5477
+ for (const file of plan.files) await writeFileAtomic(join25(root, file.path), file.content);
5478
+ if (plan.stylesheet) await writeFileAtomic(join25(root, plan.stylesheet.path), plan.stylesheet.content);
5479
+ await initProject({ root, design: root, schemaDir: join25(TOOL_ROOT, "schema"), system: plan.system.id, componentsDir: plan.componentsDir, outputs: { ...plan.outputs, targets: [...plan.outputs.targets], dark: [...plan.outputs.dark] }, tokenSources: plan.tokenSources });
5276
5480
  await syncProject(await loadProject(root), { dryRun: true });
5277
5481
  } catch (error) {
5278
- for (const file of [...plan.files.map((f) => f.path), PROJECT_FILE]) await rm6(join24(root, file), { force: true });
5279
- for (const folder of [...new Set(plan.files.map((f) => dirname12(f.path)))].sort((a, b) => b.length - a.length)) await rmdir3(join24(root, folder)).catch(() => void 0);
5280
- await rmdir3(join24(root, "design/components")).catch(() => void 0);
5281
- await rmdir3(join24(root, "design")).catch(() => void 0);
5282
- if (plan.stylesheet && stylesheet !== null) await writeFileAtomic(join24(root, plan.stylesheet.path), stylesheet);
5482
+ for (const file of [...plan.files.map((f) => f.path), PROJECT_FILE]) await rm6(join25(root, file), { force: true });
5483
+ for (const folder of [...new Set(plan.files.map((f) => dirname13(f.path)))].sort((a, b) => b.length - a.length)) await rmdir3(join25(root, folder)).catch(() => void 0);
5484
+ await rmdir3(join25(root, "design/components")).catch(() => void 0);
5485
+ await rmdir3(join25(root, "design")).catch(() => void 0);
5486
+ if (plan.stylesheet && stylesheet !== null) await writeFileAtomic(join25(root, plan.stylesheet.path), stylesheet);
5283
5487
  throw new Error(`adopt was undone, nothing kept: ${error instanceof Error ? error.message : String(error)}`);
5284
5488
  }
5285
5489
  if (!values.json) print([...adoptLines(plan), ""].join("\n"));
@@ -5305,6 +5509,12 @@ function adoptLines(plan, dryRun = false) {
5305
5509
  })
5306
5510
  ];
5307
5511
  }
5512
+ async function cmdStatus(values) {
5513
+ const report = await workspaceStatus(process.cwd(), values.project);
5514
+ if (values.json) json3(report);
5515
+ else print(renderStatus(report));
5516
+ return report.projects.length === 0 ? 1 : statusExitCode(report);
5517
+ }
5308
5518
  async function cmdSystem(values, positionals) {
5309
5519
  const [action, dir, ...extra] = positionals;
5310
5520
  if (action !== "extract" || dir === void 0 || extra.length > 0) throw new UsageError("youiyoux system extract <dir> [--package <name>] [--dry-run] (move the System youiyoux adopt keeps in this product into a design repo of its own)");
@@ -5324,7 +5534,7 @@ async function cmdSystem(values, positionals) {
5324
5534
  print([...lines, ""].join("\n"));
5325
5535
  const synced = await cmdSync({ ...values, project: p.root, package: void 0 });
5326
5536
  const git3 = `push it and set source to { "git": "<its url>", "ref": "v${result.system.version}" }`;
5327
- print(`Next: make ${relative10(process.cwd(), result.target) || "."} a repository of its own and commit it. For each app to follow it, ${values.package ? `publish ${values.package}, add it as a dependency and set source to { "package": "${values.package}" }, or ${git3}` : `${git3}, or publish it as an npm package (--package names it)`}. Change components there from now on: youiyoux sync merges them into each product`);
5537
+ print(`Next: make ${relative11(process.cwd(), result.target) || "."} a repository of its own and commit it. For each app to follow it, ${values.package ? `publish ${values.package}, add it as a dependency and set source to { "package": "${values.package}" }, or ${git3}` : `${git3}, or publish it as an npm package (--package names it)`}. Change components there from now on: youiyoux sync merges them into each product`);
5328
5538
  return synced;
5329
5539
  }
5330
5540
  async function cmdLink(values, positionals) {
@@ -5342,31 +5552,31 @@ async function cmdUnlink(values) {
5342
5552
  const root = values.project ?? process.cwd();
5343
5553
  const linked = await linkedDesign(root);
5344
5554
  if (linked !== null) await setLink(root, null);
5345
- print(linked ? `${style.ok("unlinked")} ${resolve14(root)} uses its config's source again` : `${resolve14(root)} was not linked`);
5555
+ print(linked ? `${style.ok("unlinked")} ${resolve15(root)} uses its config's source again` : `${resolve15(root)} was not linked`);
5346
5556
  return 0;
5347
5557
  }
5348
5558
  async function designArg(value, root) {
5349
- if (existsSync5(resolve14(value)) || !PACKAGE_NAME.test(value)) return { design: value };
5350
- return { design: await designRootOf({ package: value }, join24(resolve14(root), PROJECT_FILE), root), package: value };
5559
+ if (existsSync5(resolve15(value)) || !PACKAGE_NAME.test(value)) return { design: value };
5560
+ return { design: await designRootOf({ package: value }, join25(resolve15(root), PROJECT_FILE), root), package: value };
5351
5561
  }
5352
5562
  async function cmdBuild(values) {
5353
- const root = resolve14(values.design ?? process.cwd());
5563
+ const root = resolve15(values.design ?? process.cwd());
5354
5564
  const system = getSystem(await loadDesignRepo(root), values.system);
5355
5565
  if (safeId(system.config.id) === null) throw new Error(`Cannot build system ${JSON.stringify(system.config.id)}: the id must be a plain name without ".." or path separators.`);
5356
5566
  const output = OutputConfig.parse({ cssVarPrefix: values.prefix ?? "youi", targets: ["css-vars", "tailwind-v4", "ts", "react-native"] });
5357
5567
  const started = performance.now();
5358
5568
  const built = await buildTokens(root, system.config, output, { contracts: system.components.map((c) => c.contract) });
5359
- const out = resolve14(values.out ?? join24(root, "build", system.config.id));
5360
- const boundary = values.out ? out : join24(root, "build");
5361
- if (!within(boundary, out) || built.files.some((file) => !within(out, join24(out, file.path)))) throw new Error(`Cannot build system ${JSON.stringify(system.config.id)}: an output path leaves the output directory.`);
5362
- for (const file of built.files) await writeFileAtomic(join24(out, file.path), file.content);
5569
+ const out = resolve15(values.out ?? join25(root, "build", system.config.id));
5570
+ const boundary = values.out ? out : join25(root, "build");
5571
+ if (!within(boundary, out) || built.files.some((file) => !within(out, join25(out, file.path)))) throw new Error(`Cannot build system ${JSON.stringify(system.config.id)}: an output path leaves the output directory.`);
5572
+ for (const file of built.files) await writeFileAtomic(join25(out, file.path), file.content);
5363
5573
  if (values.json) json3({ out, files: built.files.map((f) => f.path), tokens: built.tokens.length });
5364
5574
  else print(`${style.ok("built")} ${plural(built.tokens.length, "token")} \u2192 ${built.files.map((f) => f.path).join(", ")} ${style.dim(`in ${out} (${ms(performance.now() - started)})`)}`);
5365
5575
  return 0;
5366
5576
  }
5367
5577
  async function cmdParity(values) {
5368
5578
  if (!values.css) throw new UsageError("youiyoux parity --css <file> [--prefix p]");
5369
- const root = resolve14(values.design ?? process.cwd());
5579
+ const root = resolve15(values.design ?? process.cwd());
5370
5580
  const system = getSystem(await loadDesignRepo(root), values.system);
5371
5581
  const prefix = values.prefix ?? "youi";
5372
5582
  const dark = { selector: values.dark ?? ':root[data-theme="dark"]' };
@@ -5384,16 +5594,16 @@ async function cmdParity(values) {
5384
5594
  async function cmdSchema(values) {
5385
5595
  const schemas = renderJsonSchemas();
5386
5596
  if (values.check) {
5387
- const dir = join24(values.design ? resolve14(values.design) : TOOL_ROOT, "schema");
5597
+ const dir = join25(values.design ? resolve15(values.design) : TOOL_ROOT, "schema");
5388
5598
  const stale = [];
5389
- for (const { file, content } of schemas) if (await readFile10(join24(dir, file), "utf8").catch(() => null) !== content) stale.push(file);
5599
+ for (const { file, content } of schemas) if (await readFile10(join25(dir, file), "utf8").catch(() => null) !== content) stale.push(file);
5390
5600
  if (values.json) json3({ dir, stale });
5391
5601
  else print(stale.length === 0 ? `${style.ok("\u2713")} ${dir} matches this youi's schemas` : `${style.error("\u2716")} out of date in ${dir}: ${stale.join(", ")}; run youiyoux schema --write${values.design ? " --design <dir>" : ""}`);
5392
5602
  return stale.length === 0 ? 0 : 1;
5393
5603
  }
5394
5604
  if (values.write) {
5395
- const root = values.design ? resolve14(values.design) : TOOL_ROOT;
5396
- for (const { file, content } of schemas) await writeFileAtomic(join24(root, "schema", file), content);
5605
+ const root = values.design ? resolve15(values.design) : TOOL_ROOT;
5606
+ for (const { file, content } of schemas) await writeFileAtomic(join25(root, "schema", file), content);
5397
5607
  print(`${style.ok("wrote")} ${schemas.map((s) => s.file).join(", ")}`);
5398
5608
  } else json3(Object.fromEntries(schemas.map((s) => [s.file, JSON.parse(s.content)])));
5399
5609
  return 0;
@@ -5416,7 +5626,7 @@ async function cmdHooks(values, positionals) {
5416
5626
  if (action !== "install") throw new UsageError("youiyoux hooks install [--claude] [--git] [--ci [--diff]] [--strict] or youiyoux hooks run claude|pre-commit");
5417
5627
  const p = await loadCheckProject(values.project ?? process.cwd(), loadOptions(values), !values.project);
5418
5628
  if (values.ci && !configured(p)) throw new UsageError("Run youiyoux init --design <dir> before installing the pinned CI workflow; repo-mode CI can run youiyoux check --diff <base> --strict directly");
5419
- const target = { ...hookTarget(values), project: p.root, ...configured(p) && p.configFile && p.configFile !== join24(p.root, PROJECT_FILE) ? { config: p.configFile } : {}, ...values.root !== void 0 ? { root: p.root } : {} };
5629
+ const target = { ...hookTarget(values), project: p.root, ...configured(p) && p.configFile && p.configFile !== join25(p.root, PROJECT_FILE) ? { config: p.configFile } : {}, ...values.root !== void 0 ? { root: p.root } : {} };
5420
5630
  const all = !values.claude && !values.git && !values.ci;
5421
5631
  const results = [];
5422
5632
  if (values.claude || all) results.push({ kind: "Claude Code", ...await installClaudeHook(await repoRootOf(p.root), hookCommand(TOOL_ROOT, "claude", target)) });
@@ -5431,11 +5641,11 @@ async function cmdHooks(values, positionals) {
5431
5641
  async function cmdAgents(values, positionals) {
5432
5642
  const [action] = positionals;
5433
5643
  if (action !== "install" && action !== "remove") throw new UsageError("youiyoux agents install|remove [--claude] [--codex] [--json]");
5434
- const projectRoot = resolve14(values.project ?? process.cwd());
5644
+ const projectRoot = resolve15(values.project ?? process.cwd());
5435
5645
  const repoRoot = await repoRootOf(projectRoot);
5436
- const target = { project: projectRoot, ...values.config ? { config: resolve14(values.config) } : {}, ...values.design ? { design: resolve14(values.design) } : {}, ...values.root !== void 0 ? { root: resolve14(values.root || ".") } : {} };
5646
+ const target = { project: projectRoot, ...values.config ? { config: resolve15(values.config) } : {}, ...values.design ? { design: resolve15(values.design) } : {}, ...values.root !== void 0 ? { root: resolve15(values.root || ".") } : {} };
5437
5647
  const server = mcpCommand(TOOL_ROOT, target);
5438
- const system = existsSync5(resolve14(values.config ?? join24(projectRoot, PROJECT_FILE))) ? await project(values) : null;
5648
+ const system = existsSync5(resolve15(values.config ?? join25(projectRoot, PROJECT_FILE))) ? await project(values) : null;
5439
5649
  const claudeServer = system && system.root === repoRoot ? { command: "node", args: mcpArgs(system) } : relativeTo(repoRoot, server);
5440
5650
  const both = !values.claude && !values.codex;
5441
5651
  const results = [];
@@ -5506,7 +5716,32 @@ async function startEditor(values, port) {
5506
5716
  return null;
5507
5717
  }
5508
5718
  }
5719
+ async function workspaceUi(values) {
5720
+ if (values.design || values.project || values.config || values.root) return null;
5721
+ const { roots, workspace } = await statusTargets(process.cwd());
5722
+ if (workspace === null || roots.length === 0) return null;
5723
+ print(style.dim(`reading the status of ${plural(roots.length, "product")}\u2026`));
5724
+ const report = await workspaceStatus(process.cwd());
5725
+ const first = report.projects.findIndex((p) => !("error" in p));
5726
+ if (first < 0) {
5727
+ print(renderStatus(report));
5728
+ throw new Error("no product in this workspace could be read: see each one's Next above");
5729
+ }
5730
+ const port = values.port ?? DEFAULT_HUB_PORT;
5731
+ let open = null;
5732
+ const hub = await startWorkspaceHub({ roots, current: first, status: () => workspaceStatus(process.cwd()), open: (root) => open ? open(root) : Promise.resolve(1) });
5733
+ print(`${style.ok("workspace hub")} http://localhost:${port}/workspace#key=${hub.key}`);
5734
+ print(style.dim(` ${plural(roots.length, "product")}; showing ${report.projects[first].root} first. Open the link: it carries this run's key, which switching needs`));
5735
+ print(style.dim(" Rules and Changes are read-only here; youiyoux ui --project <dir> opens the rule editor on one product"));
5736
+ try {
5737
+ return await runHub(TOOL_ROOT, { project: roots[first], port, api: hub.url, workspace: (snapshot) => open = snapshot, ...values.inventory ? { inventory: values.inventory } : {}, ...values.detect ? { detect: values.detect } : {} });
5738
+ } finally {
5739
+ await hub.close();
5740
+ }
5741
+ }
5509
5742
  async function cmdUi(values) {
5743
+ const workspace = await workspaceUi(values);
5744
+ if (workspace !== null) return workspace;
5510
5745
  const port = values.port ?? DEFAULT_HUB_PORT;
5511
5746
  const editor = await startEditor(values, port);
5512
5747
  try {
@@ -5518,20 +5753,20 @@ async function cmdUi(values) {
5518
5753
  async function cmdDetect(values, positionals) {
5519
5754
  const target = detectTarget(positionals);
5520
5755
  if (!target) throw new UsageError("youiyoux detect <repo-path> or youiyoux detect <url\u2026> [--expose] (one repo, or one or more URLs; not both)");
5521
- const { repoReport, siteReport, writeReport } = await import("./src-J6GPLCVO.js");
5522
- const out = resolve14(values.out ?? ".youi/detect");
5756
+ const { repoReport, siteReport, writeReport } = await import("./src-ZG7JU6YJ.js");
5757
+ const out = resolve15(values.out ?? ".youi/detect");
5523
5758
  const report = target.kind === "repo" ? await repoReport(target.path) : await siteReport(target.urls, { out, expose: values.expose, ...process.env.YOUI_CHROMIUM ? { executablePath: process.env.YOUI_CHROMIUM } : {} });
5524
5759
  const file = await writeReport(report, out);
5525
5760
  if (values.json) json3(report);
5526
5761
  else {
5527
- const shown2 = relative10(process.cwd(), file);
5762
+ const shown2 = relative11(process.cwd(), file);
5528
5763
  print(renderDetect(report, shown2 && !shown2.startsWith("..") ? shown2 : file));
5529
5764
  }
5530
5765
  return 0;
5531
5766
  }
5532
5767
  async function cmdRender(values, positionals, io) {
5533
- const root = resolve14(values.root ?? values.project ?? process.cwd());
5534
- const targets = positionals.map((t) => existsSync5(resolve14(t)) ? resolve14(t) : t);
5768
+ const root = resolve15(values.root ?? values.project ?? process.cwd());
5769
+ const targets = positionals.map((t) => existsSync5(resolve15(t)) ? resolve15(t) : t);
5535
5770
  try {
5536
5771
  const report = await renderShots(root, targets, { ...values.base ? { base: values.base } : {}, ...process.env.YOUI_CHROMIUM ? { executablePath: process.env.YOUI_CHROMIUM } : {}, ...io.render });
5537
5772
  if (values.json) json3({ ...report, next: shotsNext(report, (t) => `run youiyoux render${t ? ` ${t}` : ""} again`) });
@@ -5545,14 +5780,14 @@ async function cmdRender(values, positionals, io) {
5545
5780
  }
5546
5781
  }
5547
5782
  async function product(values) {
5548
- const project2 = await loadProject(resolve14(values.project ?? process.cwd()), loadOptions(values)).catch(() => null);
5549
- return { project: project2, root: project2?.root ?? resolve14(values.root ?? values.project ?? process.cwd()) };
5783
+ const project2 = await loadProject(resolve15(values.project ?? process.cwd()), loadOptions(values)).catch(() => null);
5784
+ return { project: project2, root: project2?.root ?? resolve15(values.root ?? values.project ?? process.cwd()) };
5550
5785
  }
5551
5786
  async function cmdRequests(values, positionals) {
5552
5787
  const usage = "youiyoux requests [--status open|addressed|closed|all] [--route r] or youiyoux requests show <id> | reply <id> --message <text> | addressed <id> --message <what you changed>";
5553
5788
  const [action, id, ...rest] = positionals;
5554
5789
  if (rest.length > 0) throw new UsageError(usage);
5555
- const { RequestStore, requestBrief } = await import("./src-4JGIPP5A.js");
5790
+ const { RequestStore, requestBrief } = await import("./src-EUVBYTKY.js");
5556
5791
  const { project: project2, root } = await product(values);
5557
5792
  const store = new RequestStore(root, project2?.config.id ?? basename5(root));
5558
5793
  const agent = { kind: "agent", name: "youiyoux CLI" };
@@ -5584,17 +5819,17 @@ async function cmdScreens(values, positionals, io) {
5584
5819
  const usage = "youiyoux screens capture --base <url of the app in development> [--route <one route>] [--project dir] [--out <screens dir>] [--storage-state <signed-in session>] [--json] or youiyoux screens login --base <url> [--project dir] [--storage-state <file>]";
5585
5820
  if (!["capture", "login"].includes(positionals[0] ?? "") || positionals.length > 1 || !values.base) throw new UsageError(usage);
5586
5821
  const { root } = await product(values);
5587
- if (positionals[0] === "login") return screensLogin(values.base, values["storage-state"] ? resolve14(values["storage-state"]) : sessionFile(root), io);
5588
- const dir = resolve14(values.out ?? join24(root, ".youi/screens"));
5589
- if (!existsSync5(join24(dir, "screens.json"))) {
5822
+ if (positionals[0] === "login") return screensLogin(values.base, values["storage-state"] ? resolve15(values["storage-state"]) : sessionFile(root), io);
5823
+ const dir = resolve15(values.out ?? join25(root, ".youi/screens"));
5824
+ if (!existsSync5(join25(dir, "screens.json"))) {
5590
5825
  process.stderr.write(`${style.error("\u2716")} no screens.json in ${dir}: list the routes to capture there, as {"schema":"youiyoux.screens/v1","project":"<name>","source":"<what the app is>","capturedAt":"","viewport":{"width":1440,"height":900},"screens":[{"route":"/","path":"/"}]}
5591
5826
  `);
5592
5827
  return 1;
5593
5828
  }
5594
- const session = chosenSession(root, values["storage-state"] ? resolve14(values["storage-state"]) : void 0);
5829
+ const session = chosenSession(root, values["storage-state"] ? resolve15(values["storage-state"]) : void 0);
5595
5830
  if (session?.saved) process.stderr.write(`${style.dim(`signed in with the session youiyoux screens login saved (${session.file}); delete it to capture signed out`)}
5596
5831
  `);
5597
- const { launchBrowser } = await import("./src-J6GPLCVO.js");
5832
+ const { launchBrowser } = await import("./src-ZG7JU6YJ.js");
5598
5833
  const browser = await launchBrowser(process.env.YOUI_CHROMIUM, { purpose: { what: "youiyoux screens capture", command: "youiyoux screens capture" } });
5599
5834
  try {
5600
5835
  const report = await captureScreens(dir, { base: values.base, root, browser, ...values.route ? { routes: [values.route] } : {}, ...session ? { storageState: session.file } : {}, log: (line) => process.stderr.write(`${style.dim(line)}
@@ -5651,7 +5886,7 @@ async function cmdReport(values, positionals, io) {
5651
5886
  return result.code;
5652
5887
  }
5653
5888
  async function cmdMcp(values) {
5654
- const { startStdioServer } = await import("./src-NWPGCDGH.js");
5889
+ const { startStdioServer } = await import("./src-YKPJ3PII.js");
5655
5890
  await startStdioServer({ projectRoot: values.project ?? process.cwd(), ...loadOptions(values), ...values.cache ? { discoverCache: values.cache } : {} });
5656
5891
  return -1;
5657
5892
  }
@@ -5662,7 +5897,7 @@ function positiveInt(value, flag) {
5662
5897
  return n;
5663
5898
  }
5664
5899
  async function cmdDiscover(values, positionals, io) {
5665
- const cacheDir = resolve14(discoverCacheDir(values.cache));
5900
+ const cacheDir = resolve15(discoverCacheDir(values.cache));
5666
5901
  const { path, home } = indexOf(values);
5667
5902
  const catalog = () => readCatalog(cacheDir, path);
5668
5903
  const [action, ...rest] = positionals;
@@ -5714,6 +5949,7 @@ ${style.dim(`store \u2192 ${storeLocation({ path })} (${plural(loaded.chosen, "l
5714
5949
  }
5715
5950
  var COMMANDS = {
5716
5951
  adopt: cmdAdopt,
5952
+ status: cmdStatus,
5717
5953
  system: cmdSystem,
5718
5954
  link: cmdLink,
5719
5955
  unlink: cmdUnlink,