@driftless-sh/cli 0.1.48 → 0.1.50
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/index.js +300 -16
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -213387,15 +213387,23 @@ var require_nestjs_extractor = __commonJS({
|
|
|
213387
213387
|
};
|
|
213388
213388
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
213389
213389
|
exports2.extractNestJS = extractNestJS;
|
|
213390
|
+
exports2.extractNestJSWithReport = extractNestJSWithReport;
|
|
213390
213391
|
var node_path_1 = __importDefault(require("node:path"));
|
|
213391
213392
|
var node_fs_1 = __importDefault(require("node:fs"));
|
|
213392
213393
|
var typescript_1 = __importDefault(require_typescript());
|
|
213393
|
-
|
|
213394
|
+
var DEFAULT_MAX_FILE_BYTES = 15e5;
|
|
213395
|
+
function maxFileBytes() {
|
|
213396
|
+
const raw = Number(process.env.DRIFTLESS_MAX_FILE_BYTES);
|
|
213397
|
+
return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : DEFAULT_MAX_FILE_BYTES;
|
|
213398
|
+
}
|
|
213399
|
+
function discoverScanFiles(rootPath) {
|
|
213394
213400
|
const files = [];
|
|
213401
|
+
const skipped = [];
|
|
213395
213402
|
const srcDir = node_fs_1.default.existsSync(node_path_1.default.join(rootPath, "src")) ? node_path_1.default.join(rootPath, "src") : rootPath;
|
|
213396
213403
|
if (!node_fs_1.default.existsSync(srcDir))
|
|
213397
|
-
return files;
|
|
213404
|
+
return { files, skipped };
|
|
213398
213405
|
const SKIP = /* @__PURE__ */ new Set(["node_modules", "dist", ".next", "build", ".turbo", "coverage"]);
|
|
213406
|
+
const cap = maxFileBytes();
|
|
213399
213407
|
function walk(dir) {
|
|
213400
213408
|
const entries = node_fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
213401
213409
|
for (const entry of entries) {
|
|
@@ -213404,13 +213412,23 @@ var require_nestjs_extractor = __commonJS({
|
|
|
213404
213412
|
if (SKIP.has(entry.name))
|
|
213405
213413
|
continue;
|
|
213406
213414
|
walk(full);
|
|
213407
|
-
} else if (entry.isFile() && entry.name.endsWith(".ts") && !entry.name.endsWith(".d.ts") && !entry.name.endsWith(".spec.ts") && !entry.name.endsWith(".test.ts")) {
|
|
213408
|
-
|
|
213415
|
+
} else if (entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".tsx")) && !entry.name.endsWith(".d.ts") && !entry.name.endsWith(".spec.ts") && !entry.name.endsWith(".test.ts")) {
|
|
213416
|
+
let size = 0;
|
|
213417
|
+
try {
|
|
213418
|
+
size = node_fs_1.default.statSync(full).size;
|
|
213419
|
+
} catch {
|
|
213420
|
+
continue;
|
|
213421
|
+
}
|
|
213422
|
+
if (size > cap) {
|
|
213423
|
+
skipped.push({ path: node_path_1.default.relative(rootPath, full), bytes: size });
|
|
213424
|
+
} else {
|
|
213425
|
+
files.push(full);
|
|
213426
|
+
}
|
|
213409
213427
|
}
|
|
213410
213428
|
}
|
|
213411
213429
|
}
|
|
213412
213430
|
walk(srcDir);
|
|
213413
|
-
return files;
|
|
213431
|
+
return { files, skipped };
|
|
213414
213432
|
}
|
|
213415
213433
|
function parseFile(filePath, rootPath) {
|
|
213416
213434
|
const sourceText = node_fs_1.default.readFileSync(filePath, "utf8");
|
|
@@ -213880,10 +213898,15 @@ var require_nestjs_extractor = __commonJS({
|
|
|
213880
213898
|
return c;
|
|
213881
213899
|
}
|
|
213882
213900
|
function extractNestJS(rootPath) {
|
|
213901
|
+
return extractNestJSWithReport(rootPath).components;
|
|
213902
|
+
}
|
|
213903
|
+
function extractNestJSWithReport(rootPath) {
|
|
213883
213904
|
const components = [];
|
|
213884
|
-
const
|
|
213905
|
+
const discovered = discoverScanFiles(rootPath);
|
|
213906
|
+
const files = discovered.files.filter((f) => isInSourceDir(node_path_1.default.relative(rootPath, f))).sort();
|
|
213907
|
+
const skipped = discovered.skipped.filter((s) => isInSourceDir(s.path));
|
|
213885
213908
|
if (files.length === 0)
|
|
213886
|
-
return components;
|
|
213909
|
+
return { components, skipped };
|
|
213887
213910
|
const symbolIndex = /* @__PURE__ */ new Map();
|
|
213888
213911
|
const moduleByDir = /* @__PURE__ */ new Map();
|
|
213889
213912
|
for (const file of files) {
|
|
@@ -214051,7 +214074,7 @@ var require_nestjs_extractor = __commonJS({
|
|
|
214051
214074
|
}
|
|
214052
214075
|
}
|
|
214053
214076
|
}
|
|
214054
|
-
return components;
|
|
214077
|
+
return { components, skipped };
|
|
214055
214078
|
}
|
|
214056
214079
|
function extractModuleRelations(cls, symbolIndex) {
|
|
214057
214080
|
const meta = cls.moduleMeta;
|
|
@@ -214321,12 +214344,222 @@ var require_enricher = __commonJS({
|
|
|
214321
214344
|
}
|
|
214322
214345
|
});
|
|
214323
214346
|
|
|
214347
|
+
// ../../libs/scanner/dist/coverage.js
|
|
214348
|
+
var require_coverage = __commonJS({
|
|
214349
|
+
"../../libs/scanner/dist/coverage.js"(exports2) {
|
|
214350
|
+
"use strict";
|
|
214351
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
214352
|
+
exports2.computeCoverage = computeCoverage;
|
|
214353
|
+
exports2.summarizeCoverage = summarizeCoverage;
|
|
214354
|
+
function normalizePath(p) {
|
|
214355
|
+
return p.trim().replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "");
|
|
214356
|
+
}
|
|
214357
|
+
function globToRegExp(glob) {
|
|
214358
|
+
const g = normalizePath(glob);
|
|
214359
|
+
let out = "";
|
|
214360
|
+
for (let i = 0; i < g.length; i++) {
|
|
214361
|
+
const c = g[i];
|
|
214362
|
+
if (c === "*") {
|
|
214363
|
+
if (g[i + 1] === "*") {
|
|
214364
|
+
i++;
|
|
214365
|
+
if (g[i + 1] === "/")
|
|
214366
|
+
i++;
|
|
214367
|
+
out += ".*";
|
|
214368
|
+
} else {
|
|
214369
|
+
out += "[^/]*";
|
|
214370
|
+
}
|
|
214371
|
+
} else if (c === "?") {
|
|
214372
|
+
out += "[^/]";
|
|
214373
|
+
} else if ("\\^$+.()|{}[]".includes(c)) {
|
|
214374
|
+
out += "\\" + c;
|
|
214375
|
+
} else {
|
|
214376
|
+
out += c;
|
|
214377
|
+
}
|
|
214378
|
+
}
|
|
214379
|
+
return new RegExp("^" + out + "$");
|
|
214380
|
+
}
|
|
214381
|
+
function matchesPattern(filePath, pattern) {
|
|
214382
|
+
if (!pattern)
|
|
214383
|
+
return false;
|
|
214384
|
+
try {
|
|
214385
|
+
return globToRegExp(pattern).test(normalizePath(filePath));
|
|
214386
|
+
} catch {
|
|
214387
|
+
return false;
|
|
214388
|
+
}
|
|
214389
|
+
}
|
|
214390
|
+
var SENSITIVE = /(auth|admin|billing|payment|credit|invoice|token|secret|webhook|api[-_]?key|password|session)/i;
|
|
214391
|
+
function isHighRisk(c) {
|
|
214392
|
+
if (c.type === "guard")
|
|
214393
|
+
return true;
|
|
214394
|
+
if (c.type === "endpoint") {
|
|
214395
|
+
const guards = c.metadata?.guard_names;
|
|
214396
|
+
if (!guards || guards.length === 0)
|
|
214397
|
+
return true;
|
|
214398
|
+
}
|
|
214399
|
+
const hay = `${c.file_path} ${c.name} ${c.metadata?.path ?? ""}`;
|
|
214400
|
+
return SENSITIVE.test(hay);
|
|
214401
|
+
}
|
|
214402
|
+
var RANK_TO_SPECIFICITY = {
|
|
214403
|
+
4: "file",
|
|
214404
|
+
3: "pattern",
|
|
214405
|
+
2: "repo",
|
|
214406
|
+
1: "weak"
|
|
214407
|
+
};
|
|
214408
|
+
function matchRank(filePath, topic, repoId) {
|
|
214409
|
+
const np = normalizePath(filePath);
|
|
214410
|
+
const files = topic.where_files ?? [];
|
|
214411
|
+
if (files.some((f) => normalizePath(f) === np))
|
|
214412
|
+
return 4;
|
|
214413
|
+
if (matchesPattern(np, topic.pattern))
|
|
214414
|
+
return 3;
|
|
214415
|
+
const repoScoped = !!repoId && !!topic.where_repos && topic.where_repos.includes(repoId);
|
|
214416
|
+
if (repoScoped) {
|
|
214417
|
+
const hasAnchors = files.length > 0 || !!topic.pattern;
|
|
214418
|
+
return hasAnchors ? 2 : 1;
|
|
214419
|
+
}
|
|
214420
|
+
return 0;
|
|
214421
|
+
}
|
|
214422
|
+
function reviewLevel(topic) {
|
|
214423
|
+
if (topic.suggested)
|
|
214424
|
+
return "suggested";
|
|
214425
|
+
if (topic.status === "reviewed")
|
|
214426
|
+
return "reviewed";
|
|
214427
|
+
if (topic.status === "draft")
|
|
214428
|
+
return "draft";
|
|
214429
|
+
return "none";
|
|
214430
|
+
}
|
|
214431
|
+
var STATUS_RANK = {
|
|
214432
|
+
none: 0,
|
|
214433
|
+
suggested: 1,
|
|
214434
|
+
draft: 2,
|
|
214435
|
+
reviewed: 3
|
|
214436
|
+
};
|
|
214437
|
+
function strongerStatus(a, b) {
|
|
214438
|
+
return STATUS_RANK[a] >= STATUS_RANK[b] ? a : b;
|
|
214439
|
+
}
|
|
214440
|
+
function decideAction(best, highRisk, hasStale, hasOrphaned) {
|
|
214441
|
+
if (highRisk && best === "none")
|
|
214442
|
+
return "document-now";
|
|
214443
|
+
if (hasOrphaned)
|
|
214444
|
+
return "reattach-or-remove";
|
|
214445
|
+
if (hasStale)
|
|
214446
|
+
return "refresh-stale";
|
|
214447
|
+
if (best === "none")
|
|
214448
|
+
return "add-topic";
|
|
214449
|
+
if (best === "suggested")
|
|
214450
|
+
return "review-suggested";
|
|
214451
|
+
if (best === "draft")
|
|
214452
|
+
return "promote-draft";
|
|
214453
|
+
return "none";
|
|
214454
|
+
}
|
|
214455
|
+
function computeCoverage(components, topics, opts = {}) {
|
|
214456
|
+
return components.map((c) => {
|
|
214457
|
+
const direct = [];
|
|
214458
|
+
const indirect = [];
|
|
214459
|
+
let bestRank = 0;
|
|
214460
|
+
let best = "none";
|
|
214461
|
+
let hasStale = false;
|
|
214462
|
+
let hasOrphaned = false;
|
|
214463
|
+
for (const t of topics) {
|
|
214464
|
+
const rank = matchRank(c.file_path, t, opts.repoId);
|
|
214465
|
+
if (rank === 0)
|
|
214466
|
+
continue;
|
|
214467
|
+
if (t.stale)
|
|
214468
|
+
hasStale = true;
|
|
214469
|
+
if (t.status === "orphaned")
|
|
214470
|
+
hasOrphaned = true;
|
|
214471
|
+
if (rank >= 4)
|
|
214472
|
+
direct.push(t.slug);
|
|
214473
|
+
else
|
|
214474
|
+
indirect.push(t.slug);
|
|
214475
|
+
if (rank > bestRank)
|
|
214476
|
+
bestRank = rank;
|
|
214477
|
+
best = strongerStatus(best, reviewLevel(t));
|
|
214478
|
+
}
|
|
214479
|
+
const highRisk = isHighRisk(c);
|
|
214480
|
+
const specificity = bestRank === 0 ? "weak" : RANK_TO_SPECIFICITY[bestRank];
|
|
214481
|
+
return {
|
|
214482
|
+
component: `${c.type}:${normalizePath(c.file_path)}:${c.name}:${c.line_number}`,
|
|
214483
|
+
file_path: c.file_path,
|
|
214484
|
+
best_status: best,
|
|
214485
|
+
direct_topics: [...new Set(direct)].sort(),
|
|
214486
|
+
indirect_topics: [...new Set(indirect)].sort(),
|
|
214487
|
+
match_specificity: specificity,
|
|
214488
|
+
has_stale: hasStale,
|
|
214489
|
+
has_orphaned: hasOrphaned,
|
|
214490
|
+
is_high_risk: highRisk,
|
|
214491
|
+
recommended_action: decideAction(best, highRisk, hasStale, hasOrphaned)
|
|
214492
|
+
};
|
|
214493
|
+
});
|
|
214494
|
+
}
|
|
214495
|
+
function summarizeCoverage(rows) {
|
|
214496
|
+
const by_status = { reviewed: 0, draft: 0, suggested: 0, none: 0 };
|
|
214497
|
+
const by_specificity = {
|
|
214498
|
+
component: 0,
|
|
214499
|
+
file: 0,
|
|
214500
|
+
pattern: 0,
|
|
214501
|
+
repo: 0,
|
|
214502
|
+
weak: 0
|
|
214503
|
+
};
|
|
214504
|
+
let covered = 0;
|
|
214505
|
+
let strong = 0;
|
|
214506
|
+
let high_risk_uncovered = 0;
|
|
214507
|
+
let stale_count = 0;
|
|
214508
|
+
let orphaned_count = 0;
|
|
214509
|
+
for (const r of rows) {
|
|
214510
|
+
by_status[r.best_status]++;
|
|
214511
|
+
by_specificity[r.match_specificity]++;
|
|
214512
|
+
if (r.best_status !== "none")
|
|
214513
|
+
covered++;
|
|
214514
|
+
if (r.best_status === "reviewed" && (r.match_specificity === "file" || r.match_specificity === "component")) {
|
|
214515
|
+
strong++;
|
|
214516
|
+
}
|
|
214517
|
+
if (r.is_high_risk && r.best_status === "none")
|
|
214518
|
+
high_risk_uncovered++;
|
|
214519
|
+
if (r.has_stale)
|
|
214520
|
+
stale_count++;
|
|
214521
|
+
if (r.has_orphaned)
|
|
214522
|
+
orphaned_count++;
|
|
214523
|
+
}
|
|
214524
|
+
const total = rows.length;
|
|
214525
|
+
const uncovered = total - covered;
|
|
214526
|
+
const partial = covered - strong;
|
|
214527
|
+
let status;
|
|
214528
|
+
if (total === 0)
|
|
214529
|
+
status = "empty";
|
|
214530
|
+
else if (high_risk_uncovered > 0)
|
|
214531
|
+
status = "critical-gap";
|
|
214532
|
+
else if (uncovered > 0)
|
|
214533
|
+
status = "gap";
|
|
214534
|
+
else if (partial > 0)
|
|
214535
|
+
status = "partial";
|
|
214536
|
+
else
|
|
214537
|
+
status = "covered";
|
|
214538
|
+
return {
|
|
214539
|
+
total,
|
|
214540
|
+
covered,
|
|
214541
|
+
uncovered,
|
|
214542
|
+
by_status,
|
|
214543
|
+
by_specificity,
|
|
214544
|
+
strong,
|
|
214545
|
+
partial,
|
|
214546
|
+
high_risk_uncovered,
|
|
214547
|
+
stale_count,
|
|
214548
|
+
orphaned_count,
|
|
214549
|
+
has_stale: stale_count > 0,
|
|
214550
|
+
has_orphaned: orphaned_count > 0,
|
|
214551
|
+
status
|
|
214552
|
+
};
|
|
214553
|
+
}
|
|
214554
|
+
}
|
|
214555
|
+
});
|
|
214556
|
+
|
|
214324
214557
|
// ../../libs/scanner/dist/index.js
|
|
214325
214558
|
var require_dist = __commonJS({
|
|
214326
214559
|
"../../libs/scanner/dist/index.js"(exports2) {
|
|
214327
214560
|
"use strict";
|
|
214328
214561
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
214329
|
-
exports2.enrichComponents = exports2.extractNestJS = exports2.identifyRepo = void 0;
|
|
214562
|
+
exports2.summarizeCoverage = exports2.computeCoverage = exports2.enrichComponents = exports2.extractNestJSWithReport = exports2.extractNestJS = exports2.identifyRepo = void 0;
|
|
214330
214563
|
exports2.scanRepo = scanRepo2;
|
|
214331
214564
|
var identity_1 = require_identity();
|
|
214332
214565
|
Object.defineProperty(exports2, "identifyRepo", { enumerable: true, get: function() {
|
|
@@ -214336,16 +214569,27 @@ var require_dist = __commonJS({
|
|
|
214336
214569
|
Object.defineProperty(exports2, "extractNestJS", { enumerable: true, get: function() {
|
|
214337
214570
|
return nestjs_extractor_1.extractNestJS;
|
|
214338
214571
|
} });
|
|
214572
|
+
Object.defineProperty(exports2, "extractNestJSWithReport", { enumerable: true, get: function() {
|
|
214573
|
+
return nestjs_extractor_1.extractNestJSWithReport;
|
|
214574
|
+
} });
|
|
214339
214575
|
var enricher_1 = require_enricher();
|
|
214340
214576
|
Object.defineProperty(exports2, "enrichComponents", { enumerable: true, get: function() {
|
|
214341
214577
|
return enricher_1.enrichComponents;
|
|
214342
214578
|
} });
|
|
214579
|
+
var coverage_1 = require_coverage();
|
|
214580
|
+
Object.defineProperty(exports2, "computeCoverage", { enumerable: true, get: function() {
|
|
214581
|
+
return coverage_1.computeCoverage;
|
|
214582
|
+
} });
|
|
214583
|
+
Object.defineProperty(exports2, "summarizeCoverage", { enumerable: true, get: function() {
|
|
214584
|
+
return coverage_1.summarizeCoverage;
|
|
214585
|
+
} });
|
|
214343
214586
|
async function scanRepo2(rootPath) {
|
|
214344
214587
|
const startMs = Date.now();
|
|
214345
214588
|
const startMem = process.memoryUsage().heapUsed;
|
|
214346
214589
|
const identity = (0, identity_1.identifyRepo)(rootPath);
|
|
214347
|
-
|
|
214348
|
-
|
|
214590
|
+
const extracted = (0, nestjs_extractor_1.extractNestJSWithReport)(rootPath);
|
|
214591
|
+
const skipped = extracted.skipped;
|
|
214592
|
+
let components = await (0, enricher_1.enrichComponents)(extracted.components);
|
|
214349
214593
|
const uniqueFiles = new Set(components.map((c) => c.file_path));
|
|
214350
214594
|
const stats = {
|
|
214351
214595
|
total_files: uniqueFiles.size,
|
|
@@ -214364,7 +214608,8 @@ var require_dist = __commonJS({
|
|
|
214364
214608
|
telemetry: {
|
|
214365
214609
|
duration_ms: durationMs,
|
|
214366
214610
|
memory_mb: memoryMb,
|
|
214367
|
-
files_parsed: uniqueFiles.size
|
|
214611
|
+
files_parsed: uniqueFiles.size,
|
|
214612
|
+
skipped_files: skipped
|
|
214368
214613
|
}
|
|
214369
214614
|
};
|
|
214370
214615
|
}
|
|
@@ -214597,7 +214842,7 @@ async function installSkillCommand() {
|
|
|
214597
214842
|
// src/commands/init.ts
|
|
214598
214843
|
function getVersion() {
|
|
214599
214844
|
try {
|
|
214600
|
-
return "0.1.
|
|
214845
|
+
return "0.1.50";
|
|
214601
214846
|
} catch {
|
|
214602
214847
|
return "0.0.0";
|
|
214603
214848
|
}
|
|
@@ -214992,6 +215237,17 @@ async function initCommand(args) {
|
|
|
214992
215237
|
} else {
|
|
214993
215238
|
console.log(` Scan: ${scanMs}ms`);
|
|
214994
215239
|
}
|
|
215240
|
+
const skippedFiles = telemetry?.skipped_files ?? [];
|
|
215241
|
+
if (skippedFiles.length > 0) {
|
|
215242
|
+
const mb = (n) => `${(n / 1e6).toFixed(1)} MB`;
|
|
215243
|
+
console.warn(` \u26A0 ${skippedFiles.length} file(s) skipped \u2014 over the per-file scan cap (not parsed):`);
|
|
215244
|
+
for (const f of skippedFiles.slice(0, 10)) {
|
|
215245
|
+
console.warn(` ${f.path} (${mb(f.bytes)})`);
|
|
215246
|
+
}
|
|
215247
|
+
if (skippedFiles.length > 10) console.warn(` \u2026 and ${skippedFiles.length - 10} more`);
|
|
215248
|
+
console.warn(" Generated/minified/vendored bundles are skipped to avoid OOM.");
|
|
215249
|
+
console.warn(" Override with DRIFTLESS_MAX_FILE_BYTES=<bytes> if a real source file was skipped.");
|
|
215250
|
+
}
|
|
214995
215251
|
console.log("");
|
|
214996
215252
|
let repo;
|
|
214997
215253
|
try {
|
|
@@ -216618,8 +216874,8 @@ async function graphCommand(args) {
|
|
|
216618
216874
|
const isJSON = args.includes("--json");
|
|
216619
216875
|
const depthIdx = args.indexOf("--depth");
|
|
216620
216876
|
const depth = depthIdx !== -1 && args[depthIdx + 1] ? `&depth=${encodeURIComponent(args[depthIdx + 1])}` : "";
|
|
216621
|
-
if (sub !== "file" && sub !== "impact") {
|
|
216622
|
-
console.error('Usage:\n driftless graph file <path> [--depth N] [--json]\n driftless graph impact --files "a,b" [--depth N] [--json]');
|
|
216877
|
+
if (sub !== "file" && sub !== "impact" && sub !== "coverage") {
|
|
216878
|
+
console.error('Usage:\n driftless graph file <path> [--depth N] [--json]\n driftless graph impact --files "a,b" [--depth N] [--json]\n driftless graph coverage [--json]');
|
|
216623
216879
|
process.exit(1);
|
|
216624
216880
|
}
|
|
216625
216881
|
const resolution = await resolveRepo();
|
|
@@ -216634,6 +216890,34 @@ async function graphCommand(args) {
|
|
|
216634
216890
|
const { workspaceSlug, repoId } = resolution;
|
|
216635
216891
|
const base = `/workspaces/${workspaceSlug}/repos/${repoId}/graph`;
|
|
216636
216892
|
try {
|
|
216893
|
+
if (sub === "coverage") {
|
|
216894
|
+
const r = await api.get(`/workspaces/${workspaceSlug}/repos/${repoId}/architecture-summary`);
|
|
216895
|
+
const cov = r?.coverage;
|
|
216896
|
+
if (!cov || !cov.summary) {
|
|
216897
|
+
console.error("No coverage in architecture-summary. The API may be on an older build, or run `driftless init` first.");
|
|
216898
|
+
process.exit(1);
|
|
216899
|
+
}
|
|
216900
|
+
if (isJSON) {
|
|
216901
|
+
emitJSON4(cov);
|
|
216902
|
+
process.exit(0);
|
|
216903
|
+
}
|
|
216904
|
+
const s = cov.summary;
|
|
216905
|
+
console.log("\u258C context coverage\n");
|
|
216906
|
+
console.log(` status ${s.status}`);
|
|
216907
|
+
console.log(` components ${s.covered}/${s.total} covered (strong ${s.strong} \xB7 partial ${s.partial} \xB7 none ${s.uncovered})`);
|
|
216908
|
+
if (s.high_risk_uncovered > 0) console.log(` \u26A0 high-risk uncovered: ${s.high_risk_uncovered}`);
|
|
216909
|
+
if (s.stale_count > 0 || s.orphaned_count > 0) {
|
|
216910
|
+
console.log(` flags stale ${s.stale_count} \xB7 orphaned ${s.orphaned_count}`);
|
|
216911
|
+
}
|
|
216912
|
+
const mods = Object.entries(cov.by_module).sort((a, b) => a[0].localeCompare(b[0]));
|
|
216913
|
+
if (mods.length) {
|
|
216914
|
+
console.log("\n modules:");
|
|
216915
|
+
for (const [name, m] of mods) {
|
|
216916
|
+
console.log(` ${name} \u2014 ${m.status} (${m.covered}/${m.total})`);
|
|
216917
|
+
}
|
|
216918
|
+
}
|
|
216919
|
+
process.exit(0);
|
|
216920
|
+
}
|
|
216637
216921
|
if (sub === "file") {
|
|
216638
216922
|
const path = args[1];
|
|
216639
216923
|
if (!path || path.startsWith("--")) {
|
|
@@ -216713,7 +216997,7 @@ async function graphCommand(args) {
|
|
|
216713
216997
|
}
|
|
216714
216998
|
|
|
216715
216999
|
// src/index.ts
|
|
216716
|
-
var VERSION = "0.1.
|
|
217000
|
+
var VERSION = "0.1.50";
|
|
216717
217001
|
var HELP_TEXT = `Driftless CLI v${VERSION} \u2014 Living repo context for humans and coding agents
|
|
216718
217002
|
|
|
216719
217003
|
Install: npm install -g @driftless-sh/cli
|