@agent-scope/cli 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/program.ts
4
- import { readFileSync as readFileSync4 } from "fs";
4
+ import { readFileSync as readFileSync6 } from "fs";
5
5
  import { generateTest, loadTrace } from "@agent-scope/playwright";
6
- import { Command as Command5 } from "commander";
6
+ import { Command as Command6 } from "commander";
7
7
 
8
8
  // src/browser.ts
9
9
  import { writeFileSync } from "fs";
@@ -50,10 +50,365 @@ function writeReportToFile(report, outputPath, pretty) {
50
50
  writeFileSync(outputPath, json, "utf-8");
51
51
  }
52
52
 
53
+ // src/init/index.ts
54
+ import { appendFileSync, existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
55
+ import { join as join2 } from "path";
56
+ import * as readline from "readline";
57
+
58
+ // src/init/detect.ts
59
+ import { existsSync, readdirSync, readFileSync } from "fs";
60
+ import { join } from "path";
61
+ function hasConfigFile(dir, stem) {
62
+ if (!existsSync(dir)) return false;
63
+ try {
64
+ const entries = readdirSync(dir);
65
+ return entries.some((f) => f === stem || f.startsWith(`${stem}.`));
66
+ } catch {
67
+ return false;
68
+ }
69
+ }
70
+ function readSafe(path) {
71
+ try {
72
+ return readFileSync(path, "utf-8");
73
+ } catch {
74
+ return null;
75
+ }
76
+ }
77
+ function detectFramework(rootDir, packageDeps) {
78
+ if (hasConfigFile(rootDir, "next.config")) return "next";
79
+ if (hasConfigFile(rootDir, "vite.config")) return "vite";
80
+ if (hasConfigFile(rootDir, "remix.config")) return "remix";
81
+ if ("react-scripts" in packageDeps) return "cra";
82
+ return "unknown";
83
+ }
84
+ function detectPackageManager(rootDir) {
85
+ if (existsSync(join(rootDir, "bun.lock"))) return "bun";
86
+ if (existsSync(join(rootDir, "yarn.lock"))) return "yarn";
87
+ if (existsSync(join(rootDir, "pnpm-lock.yaml"))) return "pnpm";
88
+ if (existsSync(join(rootDir, "package-lock.json"))) return "npm";
89
+ return "npm";
90
+ }
91
+ function detectTypeScript(rootDir) {
92
+ const candidate = join(rootDir, "tsconfig.json");
93
+ if (existsSync(candidate)) {
94
+ return { typescript: true, tsconfigPath: candidate };
95
+ }
96
+ return { typescript: false, tsconfigPath: null };
97
+ }
98
+ var COMPONENT_DIRS = ["src/components", "src/app", "src/pages", "src/ui", "src/features", "src"];
99
+ var COMPONENT_EXTS = [".tsx", ".jsx"];
100
+ function detectComponentPatterns(rootDir, typescript) {
101
+ const patterns = [];
102
+ const ext = typescript ? "tsx" : "jsx";
103
+ const altExt = typescript ? "jsx" : "jsx";
104
+ for (const dir of COMPONENT_DIRS) {
105
+ const absDir = join(rootDir, dir);
106
+ if (!existsSync(absDir)) continue;
107
+ let hasComponents = false;
108
+ try {
109
+ const entries = readdirSync(absDir, { withFileTypes: true });
110
+ hasComponents = entries.some(
111
+ (e) => e.isFile() && COMPONENT_EXTS.some((x) => e.name.endsWith(x))
112
+ );
113
+ if (!hasComponents) {
114
+ hasComponents = entries.some(
115
+ (e) => e.isDirectory() && (() => {
116
+ try {
117
+ return readdirSync(join(absDir, e.name)).some(
118
+ (f) => COMPONENT_EXTS.some((x) => f.endsWith(x))
119
+ );
120
+ } catch {
121
+ return false;
122
+ }
123
+ })()
124
+ );
125
+ }
126
+ } catch {
127
+ continue;
128
+ }
129
+ if (hasComponents) {
130
+ patterns.push(`${dir}/**/*.${ext}`);
131
+ if (altExt !== ext) {
132
+ patterns.push(`${dir}/**/*.${altExt}`);
133
+ }
134
+ }
135
+ }
136
+ const unique = [...new Set(patterns)];
137
+ if (unique.length === 0) {
138
+ return [`**/*.${ext}`];
139
+ }
140
+ return unique;
141
+ }
142
+ var TAILWIND_STEMS = ["tailwind.config"];
143
+ var CSS_EXTS = [".css", ".scss", ".sass", ".less"];
144
+ var THEME_SUFFIXES = [".theme.ts", ".theme.js", ".theme.tsx"];
145
+ var CSS_CUSTOM_PROPS_RE = /:root\s*\{[^}]*--[a-zA-Z]/;
146
+ function detectTokenSources(rootDir) {
147
+ const sources = [];
148
+ for (const stem of TAILWIND_STEMS) {
149
+ if (hasConfigFile(rootDir, stem)) {
150
+ try {
151
+ const entries = readdirSync(rootDir);
152
+ const match = entries.find((f) => f === stem || f.startsWith(`${stem}.`));
153
+ if (match) {
154
+ sources.push({ kind: "tailwind-config", path: join(rootDir, match) });
155
+ }
156
+ } catch {
157
+ }
158
+ }
159
+ }
160
+ const srcDir = join(rootDir, "src");
161
+ const dirsToScan = existsSync(srcDir) ? [srcDir] : [];
162
+ for (const scanDir of dirsToScan) {
163
+ try {
164
+ const entries = readdirSync(scanDir, { withFileTypes: true });
165
+ for (const entry of entries) {
166
+ if (entry.isFile() && CSS_EXTS.some((x) => entry.name.endsWith(x))) {
167
+ const filePath = join(scanDir, entry.name);
168
+ const content = readSafe(filePath);
169
+ if (content !== null && CSS_CUSTOM_PROPS_RE.test(content)) {
170
+ sources.push({ kind: "css-custom-properties", path: filePath });
171
+ }
172
+ }
173
+ }
174
+ } catch {
175
+ }
176
+ }
177
+ if (existsSync(srcDir)) {
178
+ try {
179
+ const entries = readdirSync(srcDir);
180
+ for (const entry of entries) {
181
+ if (THEME_SUFFIXES.some((s) => entry.endsWith(s))) {
182
+ sources.push({ kind: "theme-file", path: join(srcDir, entry) });
183
+ }
184
+ }
185
+ } catch {
186
+ }
187
+ }
188
+ return sources;
189
+ }
190
+ function detectProject(rootDir) {
191
+ const pkgPath = join(rootDir, "package.json");
192
+ let packageDeps = {};
193
+ const pkgContent = readSafe(pkgPath);
194
+ if (pkgContent !== null) {
195
+ try {
196
+ const pkg = JSON.parse(pkgContent);
197
+ packageDeps = {
198
+ ...pkg.dependencies,
199
+ ...pkg.devDependencies
200
+ };
201
+ } catch {
202
+ }
203
+ }
204
+ const framework = detectFramework(rootDir, packageDeps);
205
+ const { typescript, tsconfigPath } = detectTypeScript(rootDir);
206
+ const packageManager = detectPackageManager(rootDir);
207
+ const componentPatterns = detectComponentPatterns(rootDir, typescript);
208
+ const tokenSources = detectTokenSources(rootDir);
209
+ return {
210
+ framework,
211
+ typescript,
212
+ tsconfigPath,
213
+ componentPatterns,
214
+ tokenSources,
215
+ packageManager
216
+ };
217
+ }
218
+
219
+ // src/init/index.ts
220
+ import { Command } from "commander";
221
+ function buildDefaultConfig(detected, tokenFile, outputDir) {
222
+ const include = detected.componentPatterns.length > 0 ? detected.componentPatterns : ["src/**/*.tsx"];
223
+ return {
224
+ components: {
225
+ include,
226
+ exclude: ["**/*.test.tsx", "**/*.stories.tsx"],
227
+ wrappers: { providers: [], globalCSS: [] }
228
+ },
229
+ render: {
230
+ viewport: { default: { width: 1280, height: 800 } },
231
+ theme: "light",
232
+ warmBrowser: true
233
+ },
234
+ tokens: {
235
+ file: tokenFile,
236
+ compliance: { threshold: 90 }
237
+ },
238
+ output: {
239
+ dir: outputDir,
240
+ sprites: { format: "png", cellPadding: 8, labelAxes: true },
241
+ json: { pretty: true }
242
+ },
243
+ ci: {
244
+ complianceThreshold: 90,
245
+ failOnA11yViolations: true,
246
+ failOnConsoleErrors: false,
247
+ baselinePath: `${outputDir}baseline/`
248
+ }
249
+ };
250
+ }
251
+ function createRL() {
252
+ return readline.createInterface({
253
+ input: process.stdin,
254
+ output: process.stdout
255
+ });
256
+ }
257
+ async function ask(rl, question) {
258
+ return new Promise((resolve6) => {
259
+ rl.question(question, (answer) => {
260
+ resolve6(answer.trim());
261
+ });
262
+ });
263
+ }
264
+ async function askWithDefault(rl, label, defaultValue) {
265
+ const answer = await ask(rl, ` ${label} [${defaultValue}]: `);
266
+ return answer.length > 0 ? answer : defaultValue;
267
+ }
268
+ function ensureGitignoreEntry(rootDir, entry) {
269
+ const gitignorePath = join2(rootDir, ".gitignore");
270
+ if (existsSync2(gitignorePath)) {
271
+ const content = readFileSync2(gitignorePath, "utf-8");
272
+ const normalised = entry.replace(/\/$/, "");
273
+ const lines = content.split("\n").map((l) => l.trim());
274
+ if (lines.includes(entry) || lines.includes(normalised)) {
275
+ return;
276
+ }
277
+ const suffix = content.endsWith("\n") ? "" : "\n";
278
+ appendFileSync(gitignorePath, `${suffix}${entry}
279
+ `);
280
+ } else {
281
+ writeFileSync2(gitignorePath, `${entry}
282
+ `);
283
+ }
284
+ }
285
+ function scaffoldConfig(rootDir, config) {
286
+ const path = join2(rootDir, "reactscope.config.json");
287
+ writeFileSync2(path, `${JSON.stringify(config, null, 2)}
288
+ `);
289
+ return path;
290
+ }
291
+ function scaffoldTokenFile(rootDir, tokenFile) {
292
+ const path = join2(rootDir, tokenFile);
293
+ if (!existsSync2(path)) {
294
+ const stub = {
295
+ $schema: "https://raw.githubusercontent.com/FlatFilers/Scope/main/packages/tokens/schema.json",
296
+ tokens: {}
297
+ };
298
+ writeFileSync2(path, `${JSON.stringify(stub, null, 2)}
299
+ `);
300
+ }
301
+ return path;
302
+ }
303
+ function scaffoldOutputDir(rootDir, outputDir) {
304
+ const dirPath = join2(rootDir, outputDir);
305
+ mkdirSync(dirPath, { recursive: true });
306
+ const keepPath = join2(dirPath, ".gitkeep");
307
+ if (!existsSync2(keepPath)) {
308
+ writeFileSync2(keepPath, "");
309
+ }
310
+ return dirPath;
311
+ }
312
+ async function runInit(options) {
313
+ const rootDir = options.cwd ?? process.cwd();
314
+ const configPath = join2(rootDir, "reactscope.config.json");
315
+ const created = [];
316
+ if (existsSync2(configPath) && !options.force) {
317
+ const msg = "reactscope.config.json already exists. Run with --force to overwrite.";
318
+ process.stderr.write(`\u26A0\uFE0F ${msg}
319
+ `);
320
+ return { success: false, message: msg, created: [], skipped: true };
321
+ }
322
+ const detected = detectProject(rootDir);
323
+ const defaultTokenFile = "reactscope.tokens.json";
324
+ const defaultOutputDir = ".reactscope/";
325
+ let config = buildDefaultConfig(detected, defaultTokenFile, defaultOutputDir);
326
+ if (options.yes) {
327
+ process.stdout.write("\n\u{1F50D} Detected project settings:\n");
328
+ process.stdout.write(` Framework : ${detected.framework}
329
+ `);
330
+ process.stdout.write(` TypeScript : ${detected.typescript}
331
+ `);
332
+ process.stdout.write(` Include globs : ${config.components.include.join(", ")}
333
+ `);
334
+ process.stdout.write(` Token file : ${config.tokens.file}
335
+ `);
336
+ process.stdout.write(` Output dir : ${config.output.dir}
337
+
338
+ `);
339
+ } else {
340
+ const rl = createRL();
341
+ process.stdout.write("\n\u{1F680} scope init \u2014 project configuration\n");
342
+ process.stdout.write(" Press Enter to accept the detected value shown in brackets.\n\n");
343
+ try {
344
+ process.stdout.write(` Detected framework: ${detected.framework}
345
+ `);
346
+ const includeRaw = await askWithDefault(
347
+ rl,
348
+ "Component include patterns (comma-separated)",
349
+ config.components.include.join(", ")
350
+ );
351
+ config.components.include = includeRaw.split(",").map((s) => s.trim()).filter(Boolean);
352
+ const excludeRaw = await askWithDefault(
353
+ rl,
354
+ "Component exclude patterns (comma-separated)",
355
+ config.components.exclude.join(", ")
356
+ );
357
+ config.components.exclude = excludeRaw.split(",").map((s) => s.trim()).filter(Boolean);
358
+ const tokenFile = await askWithDefault(rl, "Token file location", config.tokens.file);
359
+ config.tokens.file = tokenFile;
360
+ config.ci.baselinePath = `${config.output.dir}baseline/`;
361
+ const outputDir = await askWithDefault(rl, "Output directory", config.output.dir);
362
+ config.output.dir = outputDir.endsWith("/") ? outputDir : `${outputDir}/`;
363
+ config.ci.baselinePath = `${config.output.dir}baseline/`;
364
+ config = buildDefaultConfig(detected, config.tokens.file, config.output.dir);
365
+ config.components.include = includeRaw.split(",").map((s) => s.trim()).filter(Boolean);
366
+ config.components.exclude = excludeRaw.split(",").map((s) => s.trim()).filter(Boolean);
367
+ } finally {
368
+ rl.close();
369
+ }
370
+ process.stdout.write("\n");
371
+ }
372
+ const cfgPath = scaffoldConfig(rootDir, config);
373
+ created.push(cfgPath);
374
+ const tokPath = scaffoldTokenFile(rootDir, config.tokens.file);
375
+ created.push(tokPath);
376
+ const outDirPath = scaffoldOutputDir(rootDir, config.output.dir);
377
+ created.push(outDirPath);
378
+ ensureGitignoreEntry(rootDir, config.output.dir);
379
+ process.stdout.write("\u2705 Scope project initialised!\n\n");
380
+ process.stdout.write(" Created files:\n");
381
+ for (const p of created) {
382
+ process.stdout.write(` ${p}
383
+ `);
384
+ }
385
+ process.stdout.write("\n Next steps: run `scope manifest` to scan your components.\n\n");
386
+ return {
387
+ success: true,
388
+ message: "Project initialised successfully.",
389
+ created,
390
+ skipped: false
391
+ };
392
+ }
393
+ function createInitCommand() {
394
+ return new Command("init").description("Initialise a Scope project \u2014 scaffold reactscope.config.json and friends").option("-y, --yes", "Accept all detected defaults without prompting", false).option("--force", "Overwrite existing reactscope.config.json if present", false).action(async (opts) => {
395
+ try {
396
+ const result = await runInit({ yes: opts.yes, force: opts.force });
397
+ if (!result.success && !result.skipped) {
398
+ process.exit(1);
399
+ }
400
+ } catch (err) {
401
+ process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
402
+ `);
403
+ process.exit(1);
404
+ }
405
+ });
406
+ }
407
+
53
408
  // src/instrument/renders.ts
54
409
  import { resolve as resolve2 } from "path";
55
410
  import { BrowserPool } from "@agent-scope/render";
56
- import { Command as Command2 } from "commander";
411
+ import { Command as Command3 } from "commander";
57
412
 
58
413
  // src/component-bundler.ts
59
414
  import { dirname } from "path";
@@ -170,10 +525,10 @@ ${projectCss.replace(/<\/style>/gi, "<\\/style>")}
170
525
  }
171
526
 
172
527
  // src/manifest-commands.ts
173
- import { existsSync, mkdirSync, readFileSync, writeFileSync as writeFileSync2 } from "fs";
528
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
174
529
  import { resolve } from "path";
175
530
  import { generateManifest } from "@agent-scope/manifest";
176
- import { Command } from "commander";
531
+ import { Command as Command2 } from "commander";
177
532
 
178
533
  // src/manifest-formatter.ts
179
534
  function isTTY() {
@@ -274,11 +629,11 @@ function matchGlob(pattern, value) {
274
629
  var MANIFEST_PATH = ".reactscope/manifest.json";
275
630
  function loadManifest(manifestPath = MANIFEST_PATH) {
276
631
  const absPath = resolve(process.cwd(), manifestPath);
277
- if (!existsSync(absPath)) {
632
+ if (!existsSync3(absPath)) {
278
633
  throw new Error(`Manifest not found at ${absPath}.
279
634
  Run \`scope manifest generate\` first.`);
280
635
  }
281
- const raw = readFileSync(absPath, "utf-8");
636
+ const raw = readFileSync3(absPath, "utf-8");
282
637
  return JSON.parse(raw);
283
638
  }
284
639
  function resolveFormat(formatFlag) {
@@ -416,10 +771,10 @@ function registerGenerate(manifestCmd) {
416
771
  process.stderr.write(`Found ${componentCount} components.
417
772
  `);
418
773
  const outputDir = outputPath.replace(/\/[^/]+$/, "");
419
- if (!existsSync(outputDir)) {
420
- mkdirSync(outputDir, { recursive: true });
774
+ if (!existsSync3(outputDir)) {
775
+ mkdirSync2(outputDir, { recursive: true });
421
776
  }
422
- writeFileSync2(outputPath, JSON.stringify(manifest, null, 2), "utf-8");
777
+ writeFileSync3(outputPath, JSON.stringify(manifest, null, 2), "utf-8");
423
778
  process.stderr.write(`Manifest written to ${outputPath}
424
779
  `);
425
780
  process.stdout.write(`${outputPath}
@@ -432,7 +787,7 @@ function registerGenerate(manifestCmd) {
432
787
  });
433
788
  }
434
789
  function createManifestCommand() {
435
- const manifestCmd = new Command("manifest").description(
790
+ const manifestCmd = new Command2("manifest").description(
436
791
  "Query and explore the component manifest"
437
792
  );
438
793
  registerList(manifestCmd);
@@ -1017,7 +1372,7 @@ function formatRendersTable(result) {
1017
1372
  return lines.join("\n");
1018
1373
  }
1019
1374
  function createInstrumentRendersCommand() {
1020
- return new Command2("renders").description("Trace re-render causality chains for a component during an interaction sequence").argument("<component>", "Component name to instrument (must be in manifest)").option(
1375
+ return new Command3("renders").description("Trace re-render causality chains for a component during an interaction sequence").argument("<component>", "Component name to instrument (must be in manifest)").option(
1021
1376
  "--interaction <json>",
1022
1377
  `Interaction sequence JSON, e.g. '[{"action":"click","target":"button"}]'`,
1023
1378
  "[]"
@@ -1062,7 +1417,7 @@ function createInstrumentRendersCommand() {
1062
1417
  );
1063
1418
  }
1064
1419
  function createInstrumentCommand() {
1065
- const instrumentCmd = new Command2("instrument").description(
1420
+ const instrumentCmd = new Command3("instrument").description(
1066
1421
  "Structured instrumentation commands for React component analysis"
1067
1422
  );
1068
1423
  instrumentCmd.addCommand(createInstrumentRendersCommand());
@@ -1070,7 +1425,7 @@ function createInstrumentCommand() {
1070
1425
  }
1071
1426
 
1072
1427
  // src/render-commands.ts
1073
- import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
1428
+ import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
1074
1429
  import { resolve as resolve4 } from "path";
1075
1430
  import {
1076
1431
  ALL_CONTEXT_IDS,
@@ -1082,10 +1437,10 @@ import {
1082
1437
  safeRender,
1083
1438
  stressAxis
1084
1439
  } from "@agent-scope/render";
1085
- import { Command as Command3 } from "commander";
1440
+ import { Command as Command4 } from "commander";
1086
1441
 
1087
1442
  // src/tailwind-css.ts
1088
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
1443
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
1089
1444
  import { createRequire } from "module";
1090
1445
  import { resolve as resolve3 } from "path";
1091
1446
  var CONFIG_FILENAMES = [
@@ -1112,39 +1467,39 @@ function getCachedBuild(cwd) {
1112
1467
  function findStylesEntry(cwd) {
1113
1468
  for (const name of CONFIG_FILENAMES) {
1114
1469
  const p = resolve3(cwd, name);
1115
- if (!existsSync2(p)) continue;
1470
+ if (!existsSync4(p)) continue;
1116
1471
  try {
1117
1472
  if (name.endsWith(".json")) {
1118
- const raw = readFileSync2(p, "utf-8");
1473
+ const raw = readFileSync4(p, "utf-8");
1119
1474
  const data = JSON.parse(raw);
1120
1475
  const scope = data.scope;
1121
1476
  const entry = scope?.stylesEntry ?? data.stylesEntry;
1122
1477
  if (typeof entry === "string") {
1123
1478
  const full = resolve3(cwd, entry);
1124
- if (existsSync2(full)) return full;
1479
+ if (existsSync4(full)) return full;
1125
1480
  }
1126
1481
  }
1127
1482
  } catch {
1128
1483
  }
1129
1484
  }
1130
1485
  const pkgPath = resolve3(cwd, "package.json");
1131
- if (existsSync2(pkgPath)) {
1486
+ if (existsSync4(pkgPath)) {
1132
1487
  try {
1133
- const raw = readFileSync2(pkgPath, "utf-8");
1488
+ const raw = readFileSync4(pkgPath, "utf-8");
1134
1489
  const pkg = JSON.parse(raw);
1135
1490
  const entry = pkg.scope?.stylesEntry;
1136
1491
  if (typeof entry === "string") {
1137
1492
  const full = resolve3(cwd, entry);
1138
- if (existsSync2(full)) return full;
1493
+ if (existsSync4(full)) return full;
1139
1494
  }
1140
1495
  } catch {
1141
1496
  }
1142
1497
  }
1143
1498
  for (const candidate of STYLE_ENTRY_CANDIDATES) {
1144
1499
  const full = resolve3(cwd, candidate);
1145
- if (existsSync2(full)) {
1500
+ if (existsSync4(full)) {
1146
1501
  try {
1147
- const content = readFileSync2(full, "utf-8");
1502
+ const content = readFileSync4(full, "utf-8");
1148
1503
  if (TAILWIND_IMPORT.test(content)) return full;
1149
1504
  } catch {
1150
1505
  }
@@ -1167,22 +1522,22 @@ async function getTailwindCompiler(cwd) {
1167
1522
  } catch {
1168
1523
  return null;
1169
1524
  }
1170
- const entryContent = readFileSync2(entryPath, "utf-8");
1525
+ const entryContent = readFileSync4(entryPath, "utf-8");
1171
1526
  const loadStylesheet = async (id, base) => {
1172
1527
  if (id === "tailwindcss") {
1173
1528
  const nodeModules = resolve3(cwd, "node_modules");
1174
1529
  const tailwindCssPath = resolve3(nodeModules, "tailwindcss", "index.css");
1175
- if (!existsSync2(tailwindCssPath)) {
1530
+ if (!existsSync4(tailwindCssPath)) {
1176
1531
  throw new Error(
1177
1532
  `Tailwind v4: tailwindcss package not found at ${tailwindCssPath}. Install with: npm install tailwindcss`
1178
1533
  );
1179
1534
  }
1180
- const content = readFileSync2(tailwindCssPath, "utf-8");
1535
+ const content = readFileSync4(tailwindCssPath, "utf-8");
1181
1536
  return { path: "virtual:tailwindcss/index.css", base, content };
1182
1537
  }
1183
1538
  const full = resolve3(base, id);
1184
- if (existsSync2(full)) {
1185
- const content = readFileSync2(full, "utf-8");
1539
+ if (existsSync4(full)) {
1540
+ const content = readFileSync4(full, "utf-8");
1186
1541
  return { path: full, base: resolve3(full, ".."), content };
1187
1542
  }
1188
1543
  throw new Error(`Tailwind v4: could not load stylesheet: ${id} (base: ${base})`);
@@ -1383,7 +1738,7 @@ Available: ${available}`
1383
1738
  const result = outcome.result;
1384
1739
  if (opts.output !== void 0) {
1385
1740
  const outPath = resolve4(process.cwd(), opts.output);
1386
- writeFileSync3(outPath, result.screenshot);
1741
+ writeFileSync4(outPath, result.screenshot);
1387
1742
  process.stdout.write(
1388
1743
  `\u2713 ${componentName} \u2192 ${opts.output} (${result.width}\xD7${result.height}, ${result.renderTimeMs.toFixed(0)}ms)
1389
1744
  `
@@ -1397,9 +1752,9 @@ Available: ${available}`
1397
1752
  `);
1398
1753
  } else if (fmt === "file") {
1399
1754
  const dir = resolve4(process.cwd(), DEFAULT_OUTPUT_DIR);
1400
- mkdirSync2(dir, { recursive: true });
1755
+ mkdirSync3(dir, { recursive: true });
1401
1756
  const outPath = resolve4(dir, `${componentName}.png`);
1402
- writeFileSync3(outPath, result.screenshot);
1757
+ writeFileSync4(outPath, result.screenshot);
1403
1758
  const relPath = `${DEFAULT_OUTPUT_DIR}/${componentName}.png`;
1404
1759
  process.stdout.write(
1405
1760
  `\u2713 ${componentName} \u2192 ${relPath} (${result.width}\xD7${result.height}, ${result.renderTimeMs.toFixed(0)}ms)
@@ -1407,9 +1762,9 @@ Available: ${available}`
1407
1762
  );
1408
1763
  } else {
1409
1764
  const dir = resolve4(process.cwd(), DEFAULT_OUTPUT_DIR);
1410
- mkdirSync2(dir, { recursive: true });
1765
+ mkdirSync3(dir, { recursive: true });
1411
1766
  const outPath = resolve4(dir, `${componentName}.png`);
1412
- writeFileSync3(outPath, result.screenshot);
1767
+ writeFileSync4(outPath, result.screenshot);
1413
1768
  const relPath = `${DEFAULT_OUTPUT_DIR}/${componentName}.png`;
1414
1769
  process.stdout.write(
1415
1770
  `\u2713 ${componentName} \u2192 ${relPath} (${result.width}\xD7${result.height}, ${result.renderTimeMs.toFixed(0)}ms)
@@ -1512,7 +1867,7 @@ Available: ${available}`
1512
1867
  const gen = new SpriteSheetGenerator();
1513
1868
  const sheet = await gen.generate(result);
1514
1869
  const spritePath = resolve4(process.cwd(), opts.sprite);
1515
- writeFileSync3(spritePath, sheet.png);
1870
+ writeFileSync4(spritePath, sheet.png);
1516
1871
  process.stderr.write(`Sprite sheet saved to ${spritePath}
1517
1872
  `);
1518
1873
  }
@@ -1522,9 +1877,9 @@ Available: ${available}`
1522
1877
  const gen = new SpriteSheetGenerator();
1523
1878
  const sheet = await gen.generate(result);
1524
1879
  const dir = resolve4(process.cwd(), DEFAULT_OUTPUT_DIR);
1525
- mkdirSync2(dir, { recursive: true });
1880
+ mkdirSync3(dir, { recursive: true });
1526
1881
  const outPath = resolve4(dir, `${componentName}-matrix.png`);
1527
- writeFileSync3(outPath, sheet.png);
1882
+ writeFileSync4(outPath, sheet.png);
1528
1883
  const relPath = `${DEFAULT_OUTPUT_DIR}/${componentName}-matrix.png`;
1529
1884
  process.stdout.write(
1530
1885
  `\u2713 ${componentName} matrix (${result.stats.totalCells} cells) \u2192 ${relPath} (${result.stats.wallClockTimeMs.toFixed(0)}ms total)
@@ -1568,7 +1923,7 @@ function registerRenderAll(renderCmd) {
1568
1923
  }
1569
1924
  const concurrency = Math.max(1, parseInt(opts.concurrency, 10) || 4);
1570
1925
  const outputDir = resolve4(process.cwd(), opts.outputDir);
1571
- mkdirSync2(outputDir, { recursive: true });
1926
+ mkdirSync3(outputDir, { recursive: true });
1572
1927
  const rootDir = process.cwd();
1573
1928
  process.stderr.write(`Rendering ${total} components (concurrency: ${concurrency})\u2026
1574
1929
  `);
@@ -1601,7 +1956,7 @@ function registerRenderAll(renderCmd) {
1601
1956
  errorMessage: outcome.error.message
1602
1957
  });
1603
1958
  const errPath = resolve4(outputDir, `${name}.error.json`);
1604
- writeFileSync3(
1959
+ writeFileSync4(
1605
1960
  errPath,
1606
1961
  JSON.stringify(
1607
1962
  {
@@ -1619,9 +1974,9 @@ function registerRenderAll(renderCmd) {
1619
1974
  const result = outcome.result;
1620
1975
  results.push({ name, renderTimeMs: result.renderTimeMs, success: true });
1621
1976
  const pngPath = resolve4(outputDir, `${name}.png`);
1622
- writeFileSync3(pngPath, result.screenshot);
1977
+ writeFileSync4(pngPath, result.screenshot);
1623
1978
  const jsonPath = resolve4(outputDir, `${name}.json`);
1624
- writeFileSync3(jsonPath, JSON.stringify(formatRenderJson(name, {}, result), null, 2));
1979
+ writeFileSync4(jsonPath, JSON.stringify(formatRenderJson(name, {}, result), null, 2));
1625
1980
  if (isTTY()) {
1626
1981
  process.stdout.write(
1627
1982
  `\u2713 ${name} \u2192 ${opts.outputDir}/${name}.png (${result.width}\xD7${result.height}, ${result.renderTimeMs.toFixed(0)}ms)
@@ -1683,7 +2038,7 @@ function resolveMatrixFormat(formatFlag, spriteAlreadyWritten) {
1683
2038
  return "json";
1684
2039
  }
1685
2040
  function createRenderCommand() {
1686
- const renderCmd = new Command3("render").description(
2041
+ const renderCmd = new Command4("render").description(
1687
2042
  "Render components to PNG or JSON via esbuild + BrowserPool"
1688
2043
  );
1689
2044
  registerRenderSingle(renderCmd);
@@ -1972,7 +2327,7 @@ function buildStructuredReport(report) {
1972
2327
  }
1973
2328
 
1974
2329
  // src/tokens/commands.ts
1975
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
2330
+ import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
1976
2331
  import { resolve as resolve5 } from "path";
1977
2332
  import {
1978
2333
  parseTokenFileSync,
@@ -1981,7 +2336,7 @@ import {
1981
2336
  TokenValidationError,
1982
2337
  validateTokenFile
1983
2338
  } from "@agent-scope/tokens";
1984
- import { Command as Command4 } from "commander";
2339
+ import { Command as Command5 } from "commander";
1985
2340
  var DEFAULT_TOKEN_FILE = "reactscope.tokens.json";
1986
2341
  var CONFIG_FILE = "reactscope.config.json";
1987
2342
  function isTTY2() {
@@ -2006,9 +2361,9 @@ function resolveTokenFilePath(fileFlag) {
2006
2361
  return resolve5(process.cwd(), fileFlag);
2007
2362
  }
2008
2363
  const configPath = resolve5(process.cwd(), CONFIG_FILE);
2009
- if (existsSync3(configPath)) {
2364
+ if (existsSync5(configPath)) {
2010
2365
  try {
2011
- const raw = readFileSync3(configPath, "utf-8");
2366
+ const raw = readFileSync5(configPath, "utf-8");
2012
2367
  const config = JSON.parse(raw);
2013
2368
  if (typeof config === "object" && config !== null && "tokens" in config && typeof config.tokens === "object" && config.tokens !== null && typeof config.tokens?.file === "string") {
2014
2369
  const file = config.tokens.file;
@@ -2020,13 +2375,13 @@ function resolveTokenFilePath(fileFlag) {
2020
2375
  return resolve5(process.cwd(), DEFAULT_TOKEN_FILE);
2021
2376
  }
2022
2377
  function loadTokens(absPath) {
2023
- if (!existsSync3(absPath)) {
2378
+ if (!existsSync5(absPath)) {
2024
2379
  throw new Error(
2025
2380
  `Token file not found at ${absPath}.
2026
2381
  Create a reactscope.tokens.json file or use --file to specify a path.`
2027
2382
  );
2028
2383
  }
2029
- const raw = readFileSync3(absPath, "utf-8");
2384
+ const raw = readFileSync5(absPath, "utf-8");
2030
2385
  return parseTokenFileSync(raw);
2031
2386
  }
2032
2387
  function getRawValue(node, segments) {
@@ -2240,13 +2595,13 @@ function registerValidate(tokensCmd) {
2240
2595
  ).option("--file <path>", "Path to token file (overrides config)").option("--format <fmt>", "Output format: json or text (default: auto-detect)").action((opts) => {
2241
2596
  try {
2242
2597
  const filePath = resolveTokenFilePath(opts.file);
2243
- if (!existsSync3(filePath)) {
2598
+ if (!existsSync5(filePath)) {
2244
2599
  throw new Error(
2245
2600
  `Token file not found at ${filePath}.
2246
2601
  Create a reactscope.tokens.json file or use --file to specify a path.`
2247
2602
  );
2248
2603
  }
2249
- const raw = readFileSync3(filePath, "utf-8");
2604
+ const raw = readFileSync5(filePath, "utf-8");
2250
2605
  const useJson = opts.format === "json" || opts.format !== "text" && !isTTY2();
2251
2606
  const errors = [];
2252
2607
  let parsed;
@@ -2314,7 +2669,7 @@ function outputValidationResult(filePath, errors, useJson) {
2314
2669
  }
2315
2670
  }
2316
2671
  function createTokensCommand() {
2317
- const tokensCmd = new Command4("tokens").description(
2672
+ const tokensCmd = new Command5("tokens").description(
2318
2673
  "Query and validate design tokens from a reactscope.tokens.json file"
2319
2674
  );
2320
2675
  registerGet2(tokensCmd);
@@ -2327,7 +2682,7 @@ function createTokensCommand() {
2327
2682
 
2328
2683
  // src/program.ts
2329
2684
  function createProgram(options = {}) {
2330
- const program2 = new Command5("scope").version(options.version ?? "0.1.0").description("Scope \u2014 React instrumentation toolkit");
2685
+ const program2 = new Command6("scope").version(options.version ?? "0.1.0").description("Scope \u2014 React instrumentation toolkit");
2331
2686
  program2.command("capture <url>").description("Capture a React component tree from a live URL and output as JSON").option("-o, --output <path>", "Write JSON to file instead of stdout").option("--pretty", "Pretty-print JSON output (default: minified)", false).option("--timeout <ms>", "Max wait time for React to mount (ms)", "10000").option("--wait <ms>", "Additional wait after page load before capture (ms)", "0").action(
2332
2687
  async (url, opts) => {
2333
2688
  try {
@@ -2400,7 +2755,7 @@ function createProgram(options = {}) {
2400
2755
  }
2401
2756
  );
2402
2757
  program2.command("generate").description("Generate a Playwright test from a Scope trace file").argument("<trace>", "Path to a serialized Scope trace (.json)").option("-o, --output <path>", "Output file path", "scope.spec.ts").option("-d, --description <text>", "Test description").action((tracePath, opts) => {
2403
- const raw = readFileSync4(tracePath, "utf-8");
2758
+ const raw = readFileSync6(tracePath, "utf-8");
2404
2759
  const trace = loadTrace(raw);
2405
2760
  const source = generateTest(trace, {
2406
2761
  description: opts.description,
@@ -2413,6 +2768,7 @@ function createProgram(options = {}) {
2413
2768
  program2.addCommand(createRenderCommand());
2414
2769
  program2.addCommand(createTokensCommand());
2415
2770
  program2.addCommand(createInstrumentCommand());
2771
+ program2.addCommand(createInitCommand());
2416
2772
  return program2;
2417
2773
  }
2418
2774