@agent-scope/cli 1.15.0 → 1.17.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 +323 -57
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +272 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +274 -17
- package/dist/index.js.map +1 -1
- package/package.json +7 -6
package/dist/index.cjs
CHANGED
|
@@ -11,6 +11,8 @@ var module$1 = require('module');
|
|
|
11
11
|
var readline = require('readline');
|
|
12
12
|
var playwright$1 = require('playwright');
|
|
13
13
|
var playwright = require('@agent-scope/playwright');
|
|
14
|
+
var http = require('http');
|
|
15
|
+
var site = require('@agent-scope/site');
|
|
14
16
|
|
|
15
17
|
function _interopNamespace(e) {
|
|
16
18
|
if (e && e.__esModule) return e;
|
|
@@ -1130,9 +1132,9 @@ function createRL() {
|
|
|
1130
1132
|
});
|
|
1131
1133
|
}
|
|
1132
1134
|
async function ask(rl, question) {
|
|
1133
|
-
return new Promise((
|
|
1135
|
+
return new Promise((resolve17) => {
|
|
1134
1136
|
rl.question(question, (answer) => {
|
|
1135
|
-
|
|
1137
|
+
resolve17(answer.trim());
|
|
1136
1138
|
});
|
|
1137
1139
|
});
|
|
1138
1140
|
}
|
|
@@ -2412,8 +2414,8 @@ Available: ${available}`
|
|
|
2412
2414
|
wastedRenders: opts.wastedRenders
|
|
2413
2415
|
});
|
|
2414
2416
|
await shutdownPool2();
|
|
2415
|
-
const
|
|
2416
|
-
if (
|
|
2417
|
+
const fmt2 = resolveFormat2(opts.format);
|
|
2418
|
+
if (fmt2 === "json") {
|
|
2417
2419
|
process.stdout.write(`${JSON.stringify(instrumentRoot, null, 2)}
|
|
2418
2420
|
`);
|
|
2419
2421
|
} else {
|
|
@@ -3132,12 +3134,12 @@ Available: ${available}`
|
|
|
3132
3134
|
);
|
|
3133
3135
|
return;
|
|
3134
3136
|
}
|
|
3135
|
-
const
|
|
3136
|
-
if (
|
|
3137
|
+
const fmt2 = resolveSingleFormat(opts.format);
|
|
3138
|
+
if (fmt2 === "json") {
|
|
3137
3139
|
const json = formatRenderJson(componentName, props, result);
|
|
3138
3140
|
process.stdout.write(`${JSON.stringify(json, null, 2)}
|
|
3139
3141
|
`);
|
|
3140
|
-
} else if (
|
|
3142
|
+
} else if (fmt2 === "file") {
|
|
3141
3143
|
const dir = path.resolve(process.cwd(), DEFAULT_OUTPUT_DIR);
|
|
3142
3144
|
fs.mkdirSync(dir, { recursive: true });
|
|
3143
3145
|
const outPath = path.resolve(dir, `${componentName}.png`);
|
|
@@ -3258,8 +3260,8 @@ Available: ${available}`
|
|
|
3258
3260
|
process.stderr.write(`Sprite sheet saved to ${spritePath}
|
|
3259
3261
|
`);
|
|
3260
3262
|
}
|
|
3261
|
-
const
|
|
3262
|
-
if (
|
|
3263
|
+
const fmt2 = resolveMatrixFormat(opts.format, opts.sprite !== void 0);
|
|
3264
|
+
if (fmt2 === "file") {
|
|
3263
3265
|
const { SpriteSheetGenerator: SpriteSheetGenerator2 } = await import('@agent-scope/render');
|
|
3264
3266
|
const gen = new SpriteSheetGenerator2();
|
|
3265
3267
|
const sheet = await gen.generate(result);
|
|
@@ -3272,10 +3274,10 @@ Available: ${available}`
|
|
|
3272
3274
|
`\u2713 ${componentName} matrix (${result.stats.totalCells} cells) \u2192 ${relPath} (${result.stats.wallClockTimeMs.toFixed(0)}ms total)
|
|
3273
3275
|
`
|
|
3274
3276
|
);
|
|
3275
|
-
} else if (
|
|
3277
|
+
} else if (fmt2 === "json") {
|
|
3276
3278
|
process.stdout.write(`${JSON.stringify(formatMatrixJson(result), null, 2)}
|
|
3277
3279
|
`);
|
|
3278
|
-
} else if (
|
|
3280
|
+
} else if (fmt2 === "png") {
|
|
3279
3281
|
if (opts.sprite !== void 0) {
|
|
3280
3282
|
} else {
|
|
3281
3283
|
const { SpriteSheetGenerator: SpriteSheetGenerator2 } = await import('@agent-scope/render');
|
|
@@ -3283,9 +3285,9 @@ Available: ${available}`
|
|
|
3283
3285
|
const sheet = await gen.generate(result);
|
|
3284
3286
|
process.stdout.write(sheet.png);
|
|
3285
3287
|
}
|
|
3286
|
-
} else if (
|
|
3288
|
+
} else if (fmt2 === "html") {
|
|
3287
3289
|
process.stdout.write(formatMatrixHtml(componentName, result));
|
|
3288
|
-
} else if (
|
|
3290
|
+
} else if (fmt2 === "csv") {
|
|
3289
3291
|
process.stdout.write(formatMatrixCsv(componentName, result));
|
|
3290
3292
|
}
|
|
3291
3293
|
} catch (err) {
|
|
@@ -3586,12 +3588,12 @@ async function runBaseline(options = {}) {
|
|
|
3586
3588
|
fs.mkdirSync(rendersDir, { recursive: true });
|
|
3587
3589
|
let manifest$1;
|
|
3588
3590
|
if (manifestPath !== void 0) {
|
|
3589
|
-
const { readFileSync:
|
|
3591
|
+
const { readFileSync: readFileSync12 } = await import('fs');
|
|
3590
3592
|
const absPath = path.resolve(rootDir, manifestPath);
|
|
3591
3593
|
if (!fs.existsSync(absPath)) {
|
|
3592
3594
|
throw new Error(`Manifest not found at ${absPath}.`);
|
|
3593
3595
|
}
|
|
3594
|
-
manifest$1 = JSON.parse(
|
|
3596
|
+
manifest$1 = JSON.parse(readFileSync12(absPath, "utf-8"));
|
|
3595
3597
|
process.stderr.write(`Loaded manifest from ${manifestPath}
|
|
3596
3598
|
`);
|
|
3597
3599
|
} else {
|
|
@@ -4225,6 +4227,135 @@ function registerDiffSubCommand(reportCmd) {
|
|
|
4225
4227
|
}
|
|
4226
4228
|
);
|
|
4227
4229
|
}
|
|
4230
|
+
var STATUS_BADGE = {
|
|
4231
|
+
added: "\u2705 added",
|
|
4232
|
+
removed: "\u{1F5D1}\uFE0F removed",
|
|
4233
|
+
unchanged: "\u2014 unchanged",
|
|
4234
|
+
compliance_regressed: "\u274C regressed",
|
|
4235
|
+
compliance_improved: "\u{1F4C8} improved",
|
|
4236
|
+
size_changed: "\u{1F4D0} resized"
|
|
4237
|
+
};
|
|
4238
|
+
function fmt(n) {
|
|
4239
|
+
return `${(n * 100).toFixed(1)}%`;
|
|
4240
|
+
}
|
|
4241
|
+
function fmtDelta(delta) {
|
|
4242
|
+
if (delta === null) return "\u2014";
|
|
4243
|
+
const sign = delta >= 0 ? "+" : "";
|
|
4244
|
+
return `${sign}${(delta * 100).toFixed(1)}%`;
|
|
4245
|
+
}
|
|
4246
|
+
function fmtDimensions(d) {
|
|
4247
|
+
if (d === null) return "\u2014";
|
|
4248
|
+
return `${d.width} \xD7 ${d.height}`;
|
|
4249
|
+
}
|
|
4250
|
+
function complianceDeltaArrow(diff) {
|
|
4251
|
+
const delta = diff.currentAggregateCompliance - diff.baselineAggregateCompliance;
|
|
4252
|
+
if (Math.abs(delta) < 5e-4) return `\u2192 ${fmt(diff.currentAggregateCompliance)} (no change)`;
|
|
4253
|
+
const arrow = delta > 0 ? "\u2191" : "\u2193";
|
|
4254
|
+
return `${arrow} ${fmtDelta(delta)}`;
|
|
4255
|
+
}
|
|
4256
|
+
function formatPrComment(diff) {
|
|
4257
|
+
const { summary, components } = diff;
|
|
4258
|
+
const lines = [];
|
|
4259
|
+
const hasRegressions = diff.hasRegressions;
|
|
4260
|
+
const headerEmoji = hasRegressions ? "\u26A0\uFE0F" : "\u2705";
|
|
4261
|
+
lines.push(`## ${headerEmoji} Scope Report`);
|
|
4262
|
+
lines.push("");
|
|
4263
|
+
lines.push("| Metric | Value |");
|
|
4264
|
+
lines.push("|---|---|");
|
|
4265
|
+
lines.push(`| Baseline compliance | ${fmt(diff.baselineAggregateCompliance)} |`);
|
|
4266
|
+
lines.push(`| Current compliance | ${fmt(diff.currentAggregateCompliance)} |`);
|
|
4267
|
+
lines.push(`| Delta | ${complianceDeltaArrow(diff)} |`);
|
|
4268
|
+
lines.push(
|
|
4269
|
+
`| Components | ${summary.total} total \xB7 ${summary.added} added \xB7 ${summary.removed} removed \xB7 ${summary.complianceRegressed} regressed |`
|
|
4270
|
+
);
|
|
4271
|
+
if (summary.renderFailed > 0) {
|
|
4272
|
+
lines.push(`| Render failures | ${summary.renderFailed} |`);
|
|
4273
|
+
}
|
|
4274
|
+
lines.push("");
|
|
4275
|
+
const changed = components.filter((c) => c.status !== "unchanged");
|
|
4276
|
+
const unchanged = components.filter((c) => c.status === "unchanged");
|
|
4277
|
+
if (changed.length > 0) {
|
|
4278
|
+
lines.push("### Changes");
|
|
4279
|
+
lines.push("");
|
|
4280
|
+
lines.push("| Component | Status | Compliance \u0394 | Dimensions |");
|
|
4281
|
+
lines.push("|---|---|---|---|");
|
|
4282
|
+
for (const c of changed) {
|
|
4283
|
+
const badge = STATUS_BADGE[c.status];
|
|
4284
|
+
const delta = fmtDelta(c.complianceDelta);
|
|
4285
|
+
const dims = fmtDimensions(c.currentDimensions ?? c.baselineDimensions);
|
|
4286
|
+
lines.push(`| \`${c.name}\` | ${badge} | ${delta} | ${dims} |`);
|
|
4287
|
+
}
|
|
4288
|
+
lines.push("");
|
|
4289
|
+
}
|
|
4290
|
+
if (unchanged.length > 0) {
|
|
4291
|
+
lines.push(
|
|
4292
|
+
`<details><summary>${unchanged.length} unchanged component${unchanged.length === 1 ? "" : "s"}</summary>`
|
|
4293
|
+
);
|
|
4294
|
+
lines.push("");
|
|
4295
|
+
lines.push("| Component | Compliance |");
|
|
4296
|
+
lines.push("|---|---|");
|
|
4297
|
+
for (const c of unchanged) {
|
|
4298
|
+
lines.push(
|
|
4299
|
+
`| \`${c.name}\` | ${c.currentCompliance !== null ? fmt(c.currentCompliance) : "\u2014"} |`
|
|
4300
|
+
);
|
|
4301
|
+
}
|
|
4302
|
+
lines.push("");
|
|
4303
|
+
lines.push("</details>");
|
|
4304
|
+
lines.push("");
|
|
4305
|
+
}
|
|
4306
|
+
lines.push(
|
|
4307
|
+
`> Generated by [Scope](https://github.com/FlatFilers/Scope) \xB7 diffed at ${diff.diffedAt}`
|
|
4308
|
+
);
|
|
4309
|
+
return lines.join("\n");
|
|
4310
|
+
}
|
|
4311
|
+
function loadDiffResult(filePath) {
|
|
4312
|
+
const abs = path.resolve(filePath);
|
|
4313
|
+
if (!fs.existsSync(abs)) {
|
|
4314
|
+
throw new Error(`DiffResult file not found: ${abs}`);
|
|
4315
|
+
}
|
|
4316
|
+
let raw;
|
|
4317
|
+
try {
|
|
4318
|
+
raw = fs.readFileSync(abs, "utf-8");
|
|
4319
|
+
} catch (err) {
|
|
4320
|
+
throw new Error(
|
|
4321
|
+
`Failed to read DiffResult file: ${err instanceof Error ? err.message : String(err)}`
|
|
4322
|
+
);
|
|
4323
|
+
}
|
|
4324
|
+
let parsed;
|
|
4325
|
+
try {
|
|
4326
|
+
parsed = JSON.parse(raw);
|
|
4327
|
+
} catch {
|
|
4328
|
+
throw new Error(`DiffResult file is not valid JSON: ${abs}`);
|
|
4329
|
+
}
|
|
4330
|
+
if (typeof parsed !== "object" || parsed === null || !("diffedAt" in parsed) || !("components" in parsed) || !("summary" in parsed)) {
|
|
4331
|
+
throw new Error(
|
|
4332
|
+
`DiffResult file does not match expected shape (missing diffedAt/components/summary): ${abs}`
|
|
4333
|
+
);
|
|
4334
|
+
}
|
|
4335
|
+
return parsed;
|
|
4336
|
+
}
|
|
4337
|
+
function registerPrCommentSubCommand(reportCmd) {
|
|
4338
|
+
reportCmd.command("pr-comment").description(
|
|
4339
|
+
"Format a DiffResult JSON file as a GitHub PR comment (Markdown, written to stdout)"
|
|
4340
|
+
).requiredOption("-i, --input <path>", "Path to DiffResult JSON (from scope report diff --json)").option("-o, --output <path>", "Write comment to file instead of stdout").action(async (opts) => {
|
|
4341
|
+
try {
|
|
4342
|
+
const diff = loadDiffResult(opts.input);
|
|
4343
|
+
const comment = formatPrComment(diff);
|
|
4344
|
+
if (opts.output !== void 0) {
|
|
4345
|
+
fs.writeFileSync(path.resolve(opts.output), comment, "utf-8");
|
|
4346
|
+
process.stderr.write(`PR comment written to ${opts.output}
|
|
4347
|
+
`);
|
|
4348
|
+
} else {
|
|
4349
|
+
process.stdout.write(`${comment}
|
|
4350
|
+
`);
|
|
4351
|
+
}
|
|
4352
|
+
} catch (err) {
|
|
4353
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
4354
|
+
`);
|
|
4355
|
+
process.exit(1);
|
|
4356
|
+
}
|
|
4357
|
+
});
|
|
4358
|
+
}
|
|
4228
4359
|
|
|
4229
4360
|
// src/tree-formatter.ts
|
|
4230
4361
|
var BRANCH2 = "\u251C\u2500\u2500 ";
|
|
@@ -4503,6 +4634,130 @@ function buildStructuredReport(report) {
|
|
|
4503
4634
|
route: report.route?.pattern ?? null
|
|
4504
4635
|
};
|
|
4505
4636
|
}
|
|
4637
|
+
var MIME_TYPES = {
|
|
4638
|
+
".html": "text/html; charset=utf-8",
|
|
4639
|
+
".css": "text/css; charset=utf-8",
|
|
4640
|
+
".js": "application/javascript; charset=utf-8",
|
|
4641
|
+
".json": "application/json; charset=utf-8",
|
|
4642
|
+
".png": "image/png",
|
|
4643
|
+
".jpg": "image/jpeg",
|
|
4644
|
+
".jpeg": "image/jpeg",
|
|
4645
|
+
".svg": "image/svg+xml",
|
|
4646
|
+
".ico": "image/x-icon"
|
|
4647
|
+
};
|
|
4648
|
+
function registerBuild(siteCmd) {
|
|
4649
|
+
siteCmd.command("build").description("Build a static HTML gallery from .reactscope/ output").option("-i, --input <path>", "Path to .reactscope input directory", ".reactscope").option("-o, --output <path>", "Output directory for generated site", ".reactscope/site").option("--base-path <path>", "Base URL path prefix for subdirectory deployment", "/").option("--compliance <path>", "Path to compliance batch report JSON").option("--title <text>", "Site title", "Scope \u2014 Component Gallery").action(
|
|
4650
|
+
async (opts) => {
|
|
4651
|
+
try {
|
|
4652
|
+
const inputDir = path.resolve(process.cwd(), opts.input);
|
|
4653
|
+
const outputDir = path.resolve(process.cwd(), opts.output);
|
|
4654
|
+
if (!fs.existsSync(inputDir)) {
|
|
4655
|
+
throw new Error(
|
|
4656
|
+
`Input directory not found: ${inputDir}
|
|
4657
|
+
Run \`scope manifest generate\` and \`scope render\` first.`
|
|
4658
|
+
);
|
|
4659
|
+
}
|
|
4660
|
+
const manifestPath = path.join(inputDir, "manifest.json");
|
|
4661
|
+
if (!fs.existsSync(manifestPath)) {
|
|
4662
|
+
throw new Error(
|
|
4663
|
+
`Manifest not found at ${manifestPath}
|
|
4664
|
+
Run \`scope manifest generate\` first.`
|
|
4665
|
+
);
|
|
4666
|
+
}
|
|
4667
|
+
process.stderr.write(`Building site from ${inputDir}\u2026
|
|
4668
|
+
`);
|
|
4669
|
+
await site.buildSite({
|
|
4670
|
+
inputDir,
|
|
4671
|
+
outputDir,
|
|
4672
|
+
basePath: opts.basePath,
|
|
4673
|
+
...opts.compliance !== void 0 && {
|
|
4674
|
+
compliancePath: path.resolve(process.cwd(), opts.compliance)
|
|
4675
|
+
},
|
|
4676
|
+
title: opts.title
|
|
4677
|
+
});
|
|
4678
|
+
process.stderr.write(`Site written to ${outputDir}
|
|
4679
|
+
`);
|
|
4680
|
+
process.stdout.write(`${outputDir}
|
|
4681
|
+
`);
|
|
4682
|
+
} catch (err) {
|
|
4683
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
4684
|
+
`);
|
|
4685
|
+
process.exit(1);
|
|
4686
|
+
}
|
|
4687
|
+
}
|
|
4688
|
+
);
|
|
4689
|
+
}
|
|
4690
|
+
function registerServe(siteCmd) {
|
|
4691
|
+
siteCmd.command("serve").description("Serve the built static site locally").option("-p, --port <number>", "Port to listen on", "3000").option("-d, --dir <path>", "Directory to serve", ".reactscope/site").action((opts) => {
|
|
4692
|
+
try {
|
|
4693
|
+
const port = Number.parseInt(opts.port, 10);
|
|
4694
|
+
if (Number.isNaN(port) || port < 1 || port > 65535) {
|
|
4695
|
+
throw new Error(`Invalid port: ${opts.port}`);
|
|
4696
|
+
}
|
|
4697
|
+
const serveDir = path.resolve(process.cwd(), opts.dir);
|
|
4698
|
+
if (!fs.existsSync(serveDir)) {
|
|
4699
|
+
throw new Error(
|
|
4700
|
+
`Serve directory not found: ${serveDir}
|
|
4701
|
+
Run \`scope site build\` first.`
|
|
4702
|
+
);
|
|
4703
|
+
}
|
|
4704
|
+
const server = http.createServer((req, res) => {
|
|
4705
|
+
const rawUrl = req.url ?? "/";
|
|
4706
|
+
const urlPath = decodeURIComponent(rawUrl.split("?")[0] ?? "/");
|
|
4707
|
+
const filePath = path.join(serveDir, urlPath.endsWith("/") ? `${urlPath}index.html` : urlPath);
|
|
4708
|
+
if (!filePath.startsWith(serveDir)) {
|
|
4709
|
+
res.writeHead(403, { "Content-Type": "text/plain" });
|
|
4710
|
+
res.end("Forbidden");
|
|
4711
|
+
return;
|
|
4712
|
+
}
|
|
4713
|
+
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
4714
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
4715
|
+
const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
4716
|
+
res.writeHead(200, { "Content-Type": contentType });
|
|
4717
|
+
fs.createReadStream(filePath).pipe(res);
|
|
4718
|
+
return;
|
|
4719
|
+
}
|
|
4720
|
+
const htmlPath = `${filePath}.html`;
|
|
4721
|
+
if (fs.existsSync(htmlPath) && fs.statSync(htmlPath).isFile()) {
|
|
4722
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
4723
|
+
fs.createReadStream(htmlPath).pipe(res);
|
|
4724
|
+
return;
|
|
4725
|
+
}
|
|
4726
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
4727
|
+
res.end(`Not found: ${urlPath}`);
|
|
4728
|
+
});
|
|
4729
|
+
server.listen(port, () => {
|
|
4730
|
+
process.stderr.write(`Scope site running at http://localhost:${port}
|
|
4731
|
+
`);
|
|
4732
|
+
process.stderr.write(`Serving ${serveDir}
|
|
4733
|
+
`);
|
|
4734
|
+
process.stderr.write("Press Ctrl+C to stop.\n");
|
|
4735
|
+
});
|
|
4736
|
+
server.on("error", (err) => {
|
|
4737
|
+
if (err.code === "EADDRINUSE") {
|
|
4738
|
+
process.stderr.write(`Error: Port ${port} is already in use.
|
|
4739
|
+
`);
|
|
4740
|
+
} else {
|
|
4741
|
+
process.stderr.write(`Server error: ${err.message}
|
|
4742
|
+
`);
|
|
4743
|
+
}
|
|
4744
|
+
process.exit(1);
|
|
4745
|
+
});
|
|
4746
|
+
} catch (err) {
|
|
4747
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
4748
|
+
`);
|
|
4749
|
+
process.exit(1);
|
|
4750
|
+
}
|
|
4751
|
+
});
|
|
4752
|
+
}
|
|
4753
|
+
function createSiteCommand() {
|
|
4754
|
+
const siteCmd = new commander.Command("site").description(
|
|
4755
|
+
"Build and serve the static component gallery site"
|
|
4756
|
+
);
|
|
4757
|
+
registerBuild(siteCmd);
|
|
4758
|
+
registerServe(siteCmd);
|
|
4759
|
+
return siteCmd;
|
|
4760
|
+
}
|
|
4506
4761
|
var DEFAULT_STYLES_PATH = ".reactscope/compliance-styles.json";
|
|
4507
4762
|
function loadStylesFile(stylesPath) {
|
|
4508
4763
|
const absPath = path.resolve(process.cwd(), stylesPath);
|
|
@@ -5520,7 +5775,9 @@ function createProgram(options = {}) {
|
|
|
5520
5775
|
if (existingReportCmd !== void 0) {
|
|
5521
5776
|
registerBaselineSubCommand(existingReportCmd);
|
|
5522
5777
|
registerDiffSubCommand(existingReportCmd);
|
|
5778
|
+
registerPrCommentSubCommand(existingReportCmd);
|
|
5523
5779
|
}
|
|
5780
|
+
program.addCommand(createSiteCommand());
|
|
5524
5781
|
return program;
|
|
5525
5782
|
}
|
|
5526
5783
|
|