@alint-js/cli 0.0.35 → 0.0.36
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/bin/index.mjs +1 -1
- package/dist/{cli-CZbrV2gX.mjs → cli-Ct9DDkxy.mjs} +109 -29
- package/dist/index.mjs +1 -1
- package/package.json +3 -3
package/dist/bin/index.mjs
CHANGED
|
@@ -16,7 +16,7 @@ import Gitignore from "gitignore-fs";
|
|
|
16
16
|
import { Minimatch, minimatch } from "minimatch";
|
|
17
17
|
import { errorMessageFrom as errorMessageFrom$1 } from "@moeru/std";
|
|
18
18
|
//#region package.json
|
|
19
|
-
var version = "0.0.
|
|
19
|
+
var version = "0.0.36";
|
|
20
20
|
//#endregion
|
|
21
21
|
//#region src/cli/commands/command.ts
|
|
22
22
|
function defineCommand(node) {
|
|
@@ -873,7 +873,7 @@ function createRuleGroups(state) {
|
|
|
873
873
|
}
|
|
874
874
|
function estimateEta(durationMs, terminal, planned) {
|
|
875
875
|
if (terminal <= 0 || planned <= 0 || durationMs <= 0) return "eta ?";
|
|
876
|
-
return `eta ~${formatDuration(durationMs * (planned - terminal) / terminal)}`;
|
|
876
|
+
return `eta ~${formatDuration$1(durationMs * (planned - terminal) / terminal)}`;
|
|
877
877
|
}
|
|
878
878
|
function fitRow(row, columns) {
|
|
879
879
|
if (columns <= 0) return "";
|
|
@@ -927,7 +927,7 @@ function formatContentRows(groups, state, options, now, contentBudget) {
|
|
|
927
927
|
}
|
|
928
928
|
return rows;
|
|
929
929
|
}
|
|
930
|
-
function formatDuration(ms) {
|
|
930
|
+
function formatDuration$1(ms) {
|
|
931
931
|
return `${(Math.max(ms, 0) / 1e3).toFixed(1)}s`;
|
|
932
932
|
}
|
|
933
933
|
function formatFooters(state, options, now) {
|
|
@@ -937,7 +937,7 @@ function formatFooters(state, options, now) {
|
|
|
937
937
|
const eta = estimateEta(elapsed, terminal, state.execution.planned);
|
|
938
938
|
const projectedTokens = terminal > 0 && state.execution.planned > 0 ? Math.ceil((state.totalTokens + state.cachedTokens) * state.execution.planned / terminal) : void 0;
|
|
939
939
|
return [
|
|
940
|
-
fitRow(`${terminal}/${state.execution.planned}${progressBar} ${formatDuration(elapsed)} -> ${eta === "eta ?" ? "~?" : eta.replace("eta ", "")}`, options.columns),
|
|
940
|
+
fitRow(`${terminal}/${state.execution.planned}${progressBar} ${formatDuration$1(elapsed)} -> ${eta === "eta ?" ? "~?" : eta.replace("eta ", "")}`, options.columns),
|
|
941
941
|
fitRow(`${state.execution.running} concurrency / ${state.execution.queued} queued / ${state.execution.cached} cached / ${state.execution.failed} failed`, options.columns),
|
|
942
942
|
fitRow(`${state.totalTokens.toLocaleString("en-US")} tokens (${state.cachedTokens.toLocaleString("en-US")} cached) -> ${projectedTokens === void 0 ? "~?" : `~${projectedTokens.toLocaleString("en-US")} tokens`}`, options.columns)
|
|
943
943
|
];
|
|
@@ -965,7 +965,7 @@ function formatTargetRow(jobState, options, now, last) {
|
|
|
965
965
|
const inputPath = options.cwd ? relative$1(options.cwd, jobState.job.inputPath) || "." : jobState.job.inputPath;
|
|
966
966
|
const target = jobState.job.target.name ? `${jobState.job.target.kind} ${jobState.job.target.name}` : jobState.job.target.kind;
|
|
967
967
|
const startedAt = jobState.retry?.startedAt ?? jobState.startedAt ?? now;
|
|
968
|
-
return fitRow(`${last ? " └─" : " ├─"} ${inputPath} > ${target} ${jobState.retry ? `${jobState.retry.attempt}/${jobState.retry.maxAttempts} retrying elapsed ${formatDuration(now - startedAt)}` : `running ${formatDuration(now - startedAt)}`}`, options.columns);
|
|
968
|
+
return fitRow(`${last ? " └─" : " ├─"} ${inputPath} > ${target} ${jobState.retry ? `${jobState.retry.attempt}/${jobState.retry.maxAttempts} retrying elapsed ${formatDuration$1(now - startedAt)}` : `running ${formatDuration$1(now - startedAt)}`}`, options.columns);
|
|
969
969
|
}
|
|
970
970
|
function styleRow(row, state, warnCount, errorCount, options) {
|
|
971
971
|
let styledRow = row;
|
|
@@ -1433,6 +1433,7 @@ function accumulate(rows, key, inTok, outTok, totalTok, countRun) {
|
|
|
1433
1433
|
row.totalTok += totalTok;
|
|
1434
1434
|
if (countRun) row.runs += 1;
|
|
1435
1435
|
rows.set(key, row);
|
|
1436
|
+
return row;
|
|
1436
1437
|
}
|
|
1437
1438
|
function isEnoent(error) {
|
|
1438
1439
|
return error instanceof Error && error.code === "ENOENT";
|
|
@@ -1465,6 +1466,7 @@ async function query(dir, filter, now) {
|
|
|
1465
1466
|
let totalIn = 0;
|
|
1466
1467
|
let totalOut = 0;
|
|
1467
1468
|
let totalTok = 0;
|
|
1469
|
+
let totalDuration;
|
|
1468
1470
|
for (const run of runs) {
|
|
1469
1471
|
if (since !== void 0 && run.ts < since) continue;
|
|
1470
1472
|
if (filter.cwd !== void 0 && run.cwd !== filter.cwd) continue;
|
|
@@ -1482,10 +1484,18 @@ async function query(dir, filter, now) {
|
|
|
1482
1484
|
accumulate(rows, key, usageRecord.inTok, usageRecord.outTok, usageRecord.totalTok, !seenKeys.has(key));
|
|
1483
1485
|
seenKeys.add(key);
|
|
1484
1486
|
}
|
|
1487
|
+
if (dimension !== "rule") continue;
|
|
1488
|
+
for (const { durationMs, ruleId } of run.ruleDurations ?? []) {
|
|
1489
|
+
const row = accumulate(rows, ruleId, 0, 0, 0, !seenKeys.has(ruleId));
|
|
1490
|
+
seenKeys.add(ruleId);
|
|
1491
|
+
row.durationMs = (row.durationMs ?? 0) + durationMs;
|
|
1492
|
+
totalDuration = (totalDuration ?? 0) + durationMs;
|
|
1493
|
+
}
|
|
1485
1494
|
}
|
|
1486
1495
|
return {
|
|
1487
1496
|
dimension,
|
|
1488
1497
|
rows: [...rows.values()].sort((left, right) => right.totalTok - left.totalTok),
|
|
1498
|
+
totalDuration,
|
|
1489
1499
|
totalIn,
|
|
1490
1500
|
totalOut,
|
|
1491
1501
|
totalRuns,
|
|
@@ -1546,6 +1556,7 @@ function createRunStat(input) {
|
|
|
1546
1556
|
cwd: input.cwd,
|
|
1547
1557
|
durationMs: input.durationMs,
|
|
1548
1558
|
ruleCounts: input.ruleCounts,
|
|
1559
|
+
ruleDurations: input.ruleDurations,
|
|
1549
1560
|
usage: {
|
|
1550
1561
|
inTok: input.usage.inputTokens,
|
|
1551
1562
|
outTok: input.usage.outputTokens,
|
|
@@ -1583,10 +1594,16 @@ function createStatsCollector() {
|
|
|
1583
1594
|
};
|
|
1584
1595
|
let startedAt;
|
|
1585
1596
|
let endedAt;
|
|
1597
|
+
const ruleMs = /* @__PURE__ */ new Map();
|
|
1586
1598
|
return {
|
|
1587
1599
|
counts,
|
|
1588
1600
|
durationMs: () => startedAt !== void 0 && endedAt !== void 0 ? endedAt - startedAt : void 0,
|
|
1589
1601
|
reporter: {
|
|
1602
|
+
onJobEnd: (payload) => {
|
|
1603
|
+
if (payload.startedAt === void 0 || payload.endedAt === void 0) return;
|
|
1604
|
+
const elapsed = Math.max(payload.endedAt - payload.startedAt, 0);
|
|
1605
|
+
ruleMs.set(payload.job.ruleId, (ruleMs.get(payload.job.ruleId) ?? 0) + elapsed);
|
|
1606
|
+
},
|
|
1590
1607
|
onRunEnd: (payload) => {
|
|
1591
1608
|
counts.cached = payload.execution.cached;
|
|
1592
1609
|
counts.cancelled = payload.execution.cancelled;
|
|
@@ -1598,7 +1615,11 @@ function createStatsCollector() {
|
|
|
1598
1615
|
onRunStart: (payload) => {
|
|
1599
1616
|
startedAt = payload.startedAt;
|
|
1600
1617
|
}
|
|
1601
|
-
}
|
|
1618
|
+
},
|
|
1619
|
+
ruleDurations: () => ruleMs.size === 0 ? void 0 : [...ruleMs].map(([ruleId, durationMs]) => ({
|
|
1620
|
+
durationMs,
|
|
1621
|
+
ruleId
|
|
1622
|
+
}))
|
|
1602
1623
|
};
|
|
1603
1624
|
}
|
|
1604
1625
|
function mergeProgressReporters(base, extra) {
|
|
@@ -1679,6 +1700,7 @@ async function writeRunStats(target, collector, result, cwd) {
|
|
|
1679
1700
|
failed: result.execution.failed,
|
|
1680
1701
|
planned: result.execution.planned
|
|
1681
1702
|
},
|
|
1703
|
+
ruleDurations: collector.ruleDurations(),
|
|
1682
1704
|
usage: result.usage
|
|
1683
1705
|
}));
|
|
1684
1706
|
} catch {}
|
|
@@ -2332,14 +2354,25 @@ const DIMENSION_LABEL = {
|
|
|
2332
2354
|
operation: "operation",
|
|
2333
2355
|
rule: "rule"
|
|
2334
2356
|
};
|
|
2357
|
+
const METRIC_LABEL = {
|
|
2358
|
+
duration: "time",
|
|
2359
|
+
runs: "runs",
|
|
2360
|
+
tokens: "total"
|
|
2361
|
+
};
|
|
2362
|
+
function formatMetric(row, metric) {
|
|
2363
|
+
if (metric === "runs") return String(row.runs);
|
|
2364
|
+
if (metric === "duration") return formatDuration(row.durationMs);
|
|
2365
|
+
return formatTokens(row.totalTok);
|
|
2366
|
+
}
|
|
2335
2367
|
function formatStatsAggregate(aggregate, options = {}) {
|
|
2336
2368
|
const paint = (text, style) => options.color === true ? style(text) : text;
|
|
2337
|
-
const
|
|
2338
|
-
const lines = [
|
|
2369
|
+
const metric = options.metric ?? "tokens";
|
|
2370
|
+
const lines = [summaryLine(aggregate, paint), ""];
|
|
2339
2371
|
if (aggregate.rows.length === 0) {
|
|
2340
2372
|
lines.push("No stats recorded yet.");
|
|
2341
2373
|
return `${lines.join("\n")}\n`;
|
|
2342
2374
|
}
|
|
2375
|
+
const withTime = aggregate.dimension === "rule";
|
|
2343
2376
|
const header = [
|
|
2344
2377
|
DIMENSION_LABEL[aggregate.dimension],
|
|
2345
2378
|
"runs",
|
|
@@ -2347,13 +2380,18 @@ function formatStatsAggregate(aggregate, options = {}) {
|
|
|
2347
2380
|
"output",
|
|
2348
2381
|
"total"
|
|
2349
2382
|
];
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2383
|
+
if (withTime) header.push("time");
|
|
2384
|
+
const rows = sortRows(aggregate.rows, metric).map((row) => {
|
|
2385
|
+
const cells = [
|
|
2386
|
+
row.key,
|
|
2387
|
+
String(row.runs),
|
|
2388
|
+
formatTokens(row.inTok),
|
|
2389
|
+
formatTokens(row.outTok),
|
|
2390
|
+
formatTokens(row.totalTok)
|
|
2391
|
+
];
|
|
2392
|
+
if (withTime) cells.push(formatDuration(row.durationMs));
|
|
2393
|
+
return cells;
|
|
2394
|
+
});
|
|
2357
2395
|
const widths = header.map((cell, index) => Math.max(cell.length, ...rows.map((row) => row[index].length)));
|
|
2358
2396
|
const renderRow = (cells, isHeader) => cells.map((cell, index) => {
|
|
2359
2397
|
const aligned = index === 0 ? cell.padEnd(widths[index]) : cell.padStart(widths[index]);
|
|
@@ -2365,6 +2403,22 @@ function formatStatsAggregate(aggregate, options = {}) {
|
|
|
2365
2403
|
function formatTokens(value) {
|
|
2366
2404
|
return value.toLocaleString("en-US");
|
|
2367
2405
|
}
|
|
2406
|
+
function metricValue(row, metric) {
|
|
2407
|
+
if (metric === "runs") return row.runs;
|
|
2408
|
+
if (metric === "duration") return row.durationMs ?? 0;
|
|
2409
|
+
return row.totalTok;
|
|
2410
|
+
}
|
|
2411
|
+
function sortRows(rows, metric) {
|
|
2412
|
+
return [...rows].sort((left, right) => metricValue(right, metric) - metricValue(left, metric));
|
|
2413
|
+
}
|
|
2414
|
+
function summaryLine(aggregate, paint) {
|
|
2415
|
+
const runLabel = aggregate.totalRuns === 1 ? "run" : "runs";
|
|
2416
|
+
const time = aggregate.totalDuration === void 0 ? "" : ` / ${formatDuration(aggregate.totalDuration)} time`;
|
|
2417
|
+
return `${aggregate.totalRuns} ${runLabel} — ${formatTokens(aggregate.totalIn)} in / ${formatTokens(aggregate.totalOut)} out / ${paint(`${formatTokens(aggregate.totalTok)} total`, colors.cyan)}${time}`;
|
|
2418
|
+
}
|
|
2419
|
+
function formatDuration(ms) {
|
|
2420
|
+
return ms === void 0 ? "—" : `${(Math.max(ms, 0) / 1e3).toFixed(1)}s`;
|
|
2421
|
+
}
|
|
2368
2422
|
//#endregion
|
|
2369
2423
|
//#region src/cli/commands/stats/chart.ts
|
|
2370
2424
|
const BAR_WIDTH = 32;
|
|
@@ -2381,21 +2435,25 @@ const PARTIAL_BLOCKS = [
|
|
|
2381
2435
|
];
|
|
2382
2436
|
function formatStatsChart(aggregate, options = {}) {
|
|
2383
2437
|
const paint = (text, style) => options.color === true ? style(text) : text;
|
|
2384
|
-
const
|
|
2385
|
-
const lines = [
|
|
2438
|
+
const metric = options.metric ?? "tokens";
|
|
2439
|
+
const lines = [summaryLine(aggregate, paint), ""];
|
|
2386
2440
|
if (aggregate.rows.length === 0) {
|
|
2387
2441
|
lines.push("No stats recorded yet.");
|
|
2388
2442
|
return `${lines.join("\n")}\n`;
|
|
2389
2443
|
}
|
|
2390
|
-
const
|
|
2444
|
+
const rows = sortRows(aggregate.rows, metric);
|
|
2445
|
+
const maxValue = Math.max(...rows.map((row) => metricValue(row, metric)));
|
|
2446
|
+
const columnTotal = rows.reduce((sum, row) => sum + metricValue(row, metric), 0);
|
|
2391
2447
|
const label = DIMENSION_LABEL[aggregate.dimension];
|
|
2392
|
-
const
|
|
2393
|
-
const
|
|
2394
|
-
const
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
const
|
|
2448
|
+
const valueLabel = METRIC_LABEL[metric];
|
|
2449
|
+
const keyWidth = Math.max(label.length, ...rows.map((row) => row.key.length));
|
|
2450
|
+
const values = rows.map((row) => formatMetric(row, metric));
|
|
2451
|
+
const valueWidth = Math.max(valueLabel.length, ...values.map((value) => value.length));
|
|
2452
|
+
lines.push(paint(`${label.padEnd(keyWidth)} ${" ".repeat(BAR_WIDTH)} ${valueLabel.padStart(valueWidth)} share`, colors.bold));
|
|
2453
|
+
rows.forEach((row, index) => {
|
|
2454
|
+
const value = metricValue(row, metric);
|
|
2455
|
+
const bar = renderBar(maxValue === 0 ? 0 : value / maxValue);
|
|
2456
|
+
const share = columnTotal === 0 ? 0 : value / columnTotal * 100;
|
|
2399
2457
|
lines.push(`${row.key.padEnd(keyWidth)} ${paint(bar, colors.cyan)} ${values[index].padStart(valueWidth)} ${share.toFixed(1).padStart(5)}%`);
|
|
2400
2458
|
});
|
|
2401
2459
|
return `${lines.join("\n")}\n`;
|
|
@@ -2414,16 +2472,23 @@ const DIMENSIONS = /* @__PURE__ */ new Set([
|
|
|
2414
2472
|
"operation",
|
|
2415
2473
|
"rule"
|
|
2416
2474
|
]);
|
|
2475
|
+
const METRICS = /* @__PURE__ */ new Set([
|
|
2476
|
+
"duration",
|
|
2477
|
+
"runs",
|
|
2478
|
+
"tokens"
|
|
2479
|
+
]);
|
|
2417
2480
|
async function runStatsCommand(options, io) {
|
|
2418
2481
|
try {
|
|
2419
2482
|
const dimension = parseDimension(options.by);
|
|
2483
|
+
const metric = parseMetric(options.metric);
|
|
2484
|
+
if (metric === "duration" && dimension !== "rule") throw new Error("The duration metric is only available with --by rule.");
|
|
2420
2485
|
const [setupConfig, config] = await Promise.all([loadMergedSetupConfig(io), loadAlintConfig(io.cwd)]);
|
|
2421
2486
|
const aggregate = await createJsonlStatsStore({ dir: statsLocation(resolveConfigRunner(config)?.stats) ?? statsLocation(setupConfig.runner?.stats) ?? getStatsDir(io.env) }).query({
|
|
2422
2487
|
by: dimension,
|
|
2423
2488
|
cwd: options.here === true ? io.cwd : options.cwd,
|
|
2424
2489
|
since: options.since
|
|
2425
2490
|
});
|
|
2426
|
-
io.stdout.write(render(aggregate, options, io.stdout.isTTY === true));
|
|
2491
|
+
io.stdout.write(render(aggregate, options, metric, io.stdout.isTTY === true));
|
|
2427
2492
|
return 0;
|
|
2428
2493
|
} catch (error) {
|
|
2429
2494
|
io.stderr.write(`${errorMessageFrom$1(error) ?? "Failed to read stats."}\n`);
|
|
@@ -2435,10 +2500,21 @@ function parseDimension(value) {
|
|
|
2435
2500
|
if (DIMENSIONS.has(value)) return value;
|
|
2436
2501
|
throw new Error(`Invalid --by "${value}": use rule, operation, model, or dir.`);
|
|
2437
2502
|
}
|
|
2438
|
-
function
|
|
2503
|
+
function parseMetric(value) {
|
|
2504
|
+
if (value === void 0) return "tokens";
|
|
2505
|
+
if (METRICS.has(value)) return value;
|
|
2506
|
+
throw new Error(`Invalid --metric "${value}": use tokens, runs, or duration.`);
|
|
2507
|
+
}
|
|
2508
|
+
function render(aggregate, options, metric, color) {
|
|
2439
2509
|
if (options.json === true) return `${JSON.stringify(aggregate, null, 2)}\n`;
|
|
2440
|
-
if (options.chart === true) return formatStatsChart(aggregate, {
|
|
2441
|
-
|
|
2510
|
+
if (options.chart === true) return formatStatsChart(aggregate, {
|
|
2511
|
+
color,
|
|
2512
|
+
metric
|
|
2513
|
+
});
|
|
2514
|
+
return formatStatsAggregate(aggregate, {
|
|
2515
|
+
color,
|
|
2516
|
+
metric
|
|
2517
|
+
});
|
|
2442
2518
|
}
|
|
2443
2519
|
function statsLocation(stats) {
|
|
2444
2520
|
return typeof stats === "object" ? stats.location : void 0;
|
|
@@ -2475,6 +2551,10 @@ const commandTree = [
|
|
|
2475
2551
|
description: "Render grouped rows as a bar chart",
|
|
2476
2552
|
flags: "--chart"
|
|
2477
2553
|
},
|
|
2554
|
+
{
|
|
2555
|
+
description: "Rank by tokens, runs, or duration (duration needs --by rule)",
|
|
2556
|
+
flags: "--metric <metric>"
|
|
2557
|
+
},
|
|
2478
2558
|
{
|
|
2479
2559
|
description: "Output JSON",
|
|
2480
2560
|
flags: "--json"
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as formatJson, n as formatDiagnostics, r as formatStylish, t as executeCli } from "./cli-
|
|
1
|
+
import { i as formatJson, n as formatDiagnostics, r as formatStylish, t as executeCli } from "./cli-Ct9DDkxy.mjs";
|
|
2
2
|
import { ignorePatternsAIAgents, ignorePatternsBuildOutputs, ignorePatternsCaches, ignorePatternsCommon, ignorePatternsEslintDefaults, ignorePatternsGenerated } from "@alint-js/config";
|
|
3
3
|
import { defineConfig } from "@alint-js/core";
|
|
4
4
|
export { defineConfig, executeCli, formatDiagnostics, formatJson, formatStylish, ignorePatternsAIAgents, ignorePatternsBuildOutputs, ignorePatternsCaches, ignorePatternsCommon, ignorePatternsEslintDefaults, ignorePatternsGenerated };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alint-js/cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.36",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
7
7
|
"types": "./dist/index.d.mts",
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"pathe": "^2.0.3",
|
|
28
28
|
"table": "^6.9.0",
|
|
29
29
|
"tinyrainbow": "^3.1.0",
|
|
30
|
-
"@alint-js/core": "0.0.
|
|
31
|
-
"@alint-js/config": "0.0.
|
|
30
|
+
"@alint-js/core": "0.0.36",
|
|
31
|
+
"@alint-js/config": "0.0.36"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@pnpm/find-workspace-dir": "^1000.1.5",
|