@alint-js/cli 0.0.34 → 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-D8-tnU0B.mjs → cli-Ct9DDkxy.mjs} +146 -16
- 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,67 @@ 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
|
+
}
|
|
2422
|
+
//#endregion
|
|
2423
|
+
//#region src/cli/commands/stats/chart.ts
|
|
2424
|
+
const BAR_WIDTH = 32;
|
|
2425
|
+
const FULL_BLOCK = "█";
|
|
2426
|
+
const PARTIAL_BLOCKS = [
|
|
2427
|
+
"",
|
|
2428
|
+
"▏",
|
|
2429
|
+
"▎",
|
|
2430
|
+
"▍",
|
|
2431
|
+
"▌",
|
|
2432
|
+
"▋",
|
|
2433
|
+
"▊",
|
|
2434
|
+
"▉"
|
|
2435
|
+
];
|
|
2436
|
+
function formatStatsChart(aggregate, options = {}) {
|
|
2437
|
+
const paint = (text, style) => options.color === true ? style(text) : text;
|
|
2438
|
+
const metric = options.metric ?? "tokens";
|
|
2439
|
+
const lines = [summaryLine(aggregate, paint), ""];
|
|
2440
|
+
if (aggregate.rows.length === 0) {
|
|
2441
|
+
lines.push("No stats recorded yet.");
|
|
2442
|
+
return `${lines.join("\n")}\n`;
|
|
2443
|
+
}
|
|
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);
|
|
2447
|
+
const label = DIMENSION_LABEL[aggregate.dimension];
|
|
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;
|
|
2457
|
+
lines.push(`${row.key.padEnd(keyWidth)} ${paint(bar, colors.cyan)} ${values[index].padStart(valueWidth)} ${share.toFixed(1).padStart(5)}%`);
|
|
2458
|
+
});
|
|
2459
|
+
return `${lines.join("\n")}\n`;
|
|
2460
|
+
}
|
|
2461
|
+
function renderBar(fraction) {
|
|
2462
|
+
const eighths = fraction <= 0 ? 0 : Math.max(1, Math.round(fraction * BAR_WIDTH * 8));
|
|
2463
|
+
const full = Math.floor(eighths / 8);
|
|
2464
|
+
const remainder = eighths % 8;
|
|
2465
|
+
return `${FULL_BLOCK.repeat(full)}${PARTIAL_BLOCKS[remainder]}`.padEnd(BAR_WIDTH);
|
|
2466
|
+
}
|
|
2368
2467
|
//#endregion
|
|
2369
2468
|
//#region src/cli/commands/stats/stats.ts
|
|
2370
2469
|
const DIMENSIONS = /* @__PURE__ */ new Set([
|
|
@@ -2373,16 +2472,23 @@ const DIMENSIONS = /* @__PURE__ */ new Set([
|
|
|
2373
2472
|
"operation",
|
|
2374
2473
|
"rule"
|
|
2375
2474
|
]);
|
|
2475
|
+
const METRICS = /* @__PURE__ */ new Set([
|
|
2476
|
+
"duration",
|
|
2477
|
+
"runs",
|
|
2478
|
+
"tokens"
|
|
2479
|
+
]);
|
|
2376
2480
|
async function runStatsCommand(options, io) {
|
|
2377
2481
|
try {
|
|
2378
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.");
|
|
2379
2485
|
const [setupConfig, config] = await Promise.all([loadMergedSetupConfig(io), loadAlintConfig(io.cwd)]);
|
|
2380
2486
|
const aggregate = await createJsonlStatsStore({ dir: statsLocation(resolveConfigRunner(config)?.stats) ?? statsLocation(setupConfig.runner?.stats) ?? getStatsDir(io.env) }).query({
|
|
2381
2487
|
by: dimension,
|
|
2382
2488
|
cwd: options.here === true ? io.cwd : options.cwd,
|
|
2383
2489
|
since: options.since
|
|
2384
2490
|
});
|
|
2385
|
-
io.stdout.write(
|
|
2491
|
+
io.stdout.write(render(aggregate, options, metric, io.stdout.isTTY === true));
|
|
2386
2492
|
return 0;
|
|
2387
2493
|
} catch (error) {
|
|
2388
2494
|
io.stderr.write(`${errorMessageFrom$1(error) ?? "Failed to read stats."}\n`);
|
|
@@ -2394,6 +2500,22 @@ function parseDimension(value) {
|
|
|
2394
2500
|
if (DIMENSIONS.has(value)) return value;
|
|
2395
2501
|
throw new Error(`Invalid --by "${value}": use rule, operation, model, or dir.`);
|
|
2396
2502
|
}
|
|
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) {
|
|
2509
|
+
if (options.json === true) return `${JSON.stringify(aggregate, null, 2)}\n`;
|
|
2510
|
+
if (options.chart === true) return formatStatsChart(aggregate, {
|
|
2511
|
+
color,
|
|
2512
|
+
metric
|
|
2513
|
+
});
|
|
2514
|
+
return formatStatsAggregate(aggregate, {
|
|
2515
|
+
color,
|
|
2516
|
+
metric
|
|
2517
|
+
});
|
|
2518
|
+
}
|
|
2397
2519
|
function statsLocation(stats) {
|
|
2398
2520
|
return typeof stats === "object" ? stats.location : void 0;
|
|
2399
2521
|
}
|
|
@@ -2425,6 +2547,14 @@ const commandTree = [
|
|
|
2425
2547
|
description: "Only include runs from the current directory",
|
|
2426
2548
|
flags: "--here"
|
|
2427
2549
|
},
|
|
2550
|
+
{
|
|
2551
|
+
description: "Render grouped rows as a bar chart",
|
|
2552
|
+
flags: "--chart"
|
|
2553
|
+
},
|
|
2554
|
+
{
|
|
2555
|
+
description: "Rank by tokens, runs, or duration (duration needs --by rule)",
|
|
2556
|
+
flags: "--metric <metric>"
|
|
2557
|
+
},
|
|
2428
2558
|
{
|
|
2429
2559
|
description: "Output JSON",
|
|
2430
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/
|
|
31
|
-
"@alint-js/
|
|
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",
|