@driftless-sh/cli 0.1.19 → 0.1.20
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 +98 -17
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -214179,6 +214179,8 @@ var require_dist = __commonJS({
|
|
|
214179
214179
|
return enricher_1.enrichComponents;
|
|
214180
214180
|
} });
|
|
214181
214181
|
async function scanRepo2(rootPath) {
|
|
214182
|
+
const startMs = Date.now();
|
|
214183
|
+
const startMem = process.memoryUsage().heapUsed;
|
|
214182
214184
|
const identity = (0, identity_1.identifyRepo)(rootPath);
|
|
214183
214185
|
let components = (0, nestjs_extractor_1.extractNestJS)(rootPath);
|
|
214184
214186
|
components = await (0, enricher_1.enrichComponents)(components);
|
|
@@ -214191,7 +214193,18 @@ var require_dist = __commonJS({
|
|
|
214191
214193
|
total_modules: components.filter((c) => c.type === "module").length,
|
|
214192
214194
|
total_dtos: components.filter((c) => c.type === "dto").length
|
|
214193
214195
|
};
|
|
214194
|
-
|
|
214196
|
+
const durationMs = Date.now() - startMs;
|
|
214197
|
+
const memoryMb = Math.round((process.memoryUsage().heapUsed - startMem) / 1024 / 1024);
|
|
214198
|
+
return {
|
|
214199
|
+
identity,
|
|
214200
|
+
components,
|
|
214201
|
+
stats,
|
|
214202
|
+
telemetry: {
|
|
214203
|
+
duration_ms: durationMs,
|
|
214204
|
+
memory_mb: memoryMb,
|
|
214205
|
+
files_parsed: uniqueFiles.size
|
|
214206
|
+
}
|
|
214207
|
+
};
|
|
214195
214208
|
}
|
|
214196
214209
|
}
|
|
214197
214210
|
});
|
|
@@ -214230,6 +214243,28 @@ function getBaseUrl() {
|
|
|
214230
214243
|
}
|
|
214231
214244
|
return DEFAULT_URL;
|
|
214232
214245
|
}
|
|
214246
|
+
function parseError(e) {
|
|
214247
|
+
const msg = e.message;
|
|
214248
|
+
const jsonMatch = msg.match(/\{[^}]*"message"[^}]*\}/);
|
|
214249
|
+
if (jsonMatch) {
|
|
214250
|
+
try {
|
|
214251
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
214252
|
+
if (parsed.message) return parsed.message;
|
|
214253
|
+
} catch {
|
|
214254
|
+
}
|
|
214255
|
+
}
|
|
214256
|
+
const statusMatch = msg.match(/HTTP (\d+):/);
|
|
214257
|
+
if (statusMatch) {
|
|
214258
|
+
const code = parseInt(statusMatch[1], 10);
|
|
214259
|
+
if (code === 500) return "Internal server error \u2014 the API encountered an unexpected issue";
|
|
214260
|
+
if (code === 401) return "Authentication failed \u2014 check your API key";
|
|
214261
|
+
if (code === 403) return "Access denied \u2014 your API key lacks permission";
|
|
214262
|
+
if (code === 404) return "Not found \u2014 the resource does not exist";
|
|
214263
|
+
if (code === 429) return "Rate limited \u2014 too many requests, try again later";
|
|
214264
|
+
return `Server error (HTTP ${code})`;
|
|
214265
|
+
}
|
|
214266
|
+
return msg;
|
|
214267
|
+
}
|
|
214233
214268
|
function request(method, path, body) {
|
|
214234
214269
|
return new Promise((resolve7, reject) => {
|
|
214235
214270
|
const baseUrl = getBaseUrl();
|
|
@@ -214264,7 +214299,7 @@ function request(method, path, body) {
|
|
|
214264
214299
|
});
|
|
214265
214300
|
}
|
|
214266
214301
|
);
|
|
214267
|
-
req.on("error", reject);
|
|
214302
|
+
req.on("error", (e) => reject(new Error(`Connection failed: ${e.message}`)));
|
|
214268
214303
|
if (body) req.write(JSON.stringify(body));
|
|
214269
214304
|
req.end();
|
|
214270
214305
|
});
|
|
@@ -214282,6 +214317,9 @@ function getApiUrl() {
|
|
|
214282
214317
|
function getApiKey() {
|
|
214283
214318
|
return loadApiKey();
|
|
214284
214319
|
}
|
|
214320
|
+
function formatError(e) {
|
|
214321
|
+
return parseError(e);
|
|
214322
|
+
}
|
|
214285
214323
|
|
|
214286
214324
|
// src/git.ts
|
|
214287
214325
|
var import_node_child_process = require("node:child_process");
|
|
@@ -214516,6 +214554,15 @@ function generateSmartRules(repoId, patterns) {
|
|
|
214516
214554
|
scope: { paths: ["apps/", "libs/"] },
|
|
214517
214555
|
pattern: { type: "FORBIDDEN_IMPORT", params: { imports: ["@modelcontextprotocol", "mcp"] } },
|
|
214518
214556
|
repo_id: repoId
|
|
214557
|
+
},
|
|
214558
|
+
{
|
|
214559
|
+
name: "Controllers must be thin",
|
|
214560
|
+
description: "Controllers should only handle routing and validation. Move business logic, token manipulation, and debug code to services.",
|
|
214561
|
+
type: "STRUCTURAL",
|
|
214562
|
+
severity: "high",
|
|
214563
|
+
scope: { file_patterns: ["*.controller.ts"] },
|
|
214564
|
+
pattern: { type: "CONTROLLER_THICK", params: {} },
|
|
214565
|
+
repo_id: repoId
|
|
214519
214566
|
}
|
|
214520
214567
|
);
|
|
214521
214568
|
return rules;
|
|
@@ -214677,12 +214724,24 @@ async function initCommand(args) {
|
|
|
214677
214724
|
(sum, component) => sum + (component.relations?.length || 0),
|
|
214678
214725
|
0
|
|
214679
214726
|
);
|
|
214680
|
-
|
|
214681
|
-
|
|
214727
|
+
const actualEndpoints = components.filter((c) => c.type === "endpoint").length;
|
|
214728
|
+
const actualServices = components.filter((c) => c.type === "service").length;
|
|
214729
|
+
const actualModules = components.filter((c) => c.type === "module").length;
|
|
214730
|
+
const actualGuards = components.filter((c) => c.type === "guard").length;
|
|
214731
|
+
const actualControllers = components.filter((c) => c.type === "controller").length;
|
|
214732
|
+
const actualDtos = components.filter((c) => c.type === "dto").length;
|
|
214733
|
+
console.log(` Framework: ${summary.framework} | Endpoints: ${actualEndpoints} | Services: ${actualServices}`);
|
|
214734
|
+
console.log(` Modules: ${actualModules} | Guards: ${actualGuards}`);
|
|
214682
214735
|
console.log(` Relations: ${relationCount}`);
|
|
214736
|
+
if (actualControllers > 0) console.log(` Controllers: ${actualControllers}`);
|
|
214737
|
+
if (actualDtos > 0) console.log(` DTOs: ${actualDtos}`);
|
|
214683
214738
|
if (summary.auth_patterns.length > 0) {
|
|
214684
214739
|
console.log(` Auth: ${summary.auth_patterns.join(", ")}`);
|
|
214685
214740
|
}
|
|
214741
|
+
if (scanResult.telemetry) {
|
|
214742
|
+
const t = scanResult.telemetry;
|
|
214743
|
+
console.log(` Scan: ${t.duration_ms}ms, ${t.memory_mb}MB heap, ${t.files_parsed} files`);
|
|
214744
|
+
}
|
|
214686
214745
|
let repo;
|
|
214687
214746
|
try {
|
|
214688
214747
|
const repos = await api.get(`/workspaces/${workspaceSlug}/repos`);
|
|
@@ -214787,7 +214846,7 @@ Creating ${smartRules.length} architectural rules...`);
|
|
|
214787
214846
|
}
|
|
214788
214847
|
console.log(` Docs anchored: ${docsAnchored}`);
|
|
214789
214848
|
console.log("\n---");
|
|
214790
|
-
console.log(`Architecture: ${
|
|
214849
|
+
console.log(`Architecture: ${actualModules} modules, ${actualEndpoints} endpoints, ${actualServices} services`);
|
|
214791
214850
|
console.log(`Relations: ${relationCount} cross-component dependencies mapped`);
|
|
214792
214851
|
console.log(`Rules: ${rulesCreated} architectural rules created from code analysis`);
|
|
214793
214852
|
console.log(`Watchers: ${watchersCreated} context watchers auto-generated`);
|
|
@@ -214832,6 +214891,7 @@ async function scanCommand(args) {
|
|
|
214832
214891
|
const onlyDiff = args.includes("--diff");
|
|
214833
214892
|
let diff = "";
|
|
214834
214893
|
let source = "";
|
|
214894
|
+
let changedFiles = [];
|
|
214835
214895
|
if (onlyDiff) {
|
|
214836
214896
|
diff = getUncommittedDiff();
|
|
214837
214897
|
source = "uncommitted changes";
|
|
@@ -214839,6 +214899,7 @@ async function scanCommand(args) {
|
|
|
214839
214899
|
console.log("No uncommitted changes. Clean.");
|
|
214840
214900
|
process.exit(0);
|
|
214841
214901
|
}
|
|
214902
|
+
changedFiles = diff.split("\n").filter((line) => line.startsWith("+++ b/") || line.startsWith("--- a/")).map((line) => line.replace(/^\+\+\+ b\//, "").replace(/^--- a\//, "")).filter((f, i, arr) => arr.indexOf(f) === i);
|
|
214842
214903
|
} else {
|
|
214843
214904
|
const staged = getStagedDiff();
|
|
214844
214905
|
const unstaged = getUncommittedDiff();
|
|
@@ -214852,6 +214913,9 @@ async function scanCommand(args) {
|
|
|
214852
214913
|
const commitHash = getLastCommitHash();
|
|
214853
214914
|
const author = getAuthorName();
|
|
214854
214915
|
console.log(`Scanning ${source}...`);
|
|
214916
|
+
if (changedFiles.length > 0) {
|
|
214917
|
+
console.log(` Files: ${changedFiles.join(", ")}`);
|
|
214918
|
+
}
|
|
214855
214919
|
const result = await api.post("/scan", {
|
|
214856
214920
|
workspace_id: workspace.workspace_id,
|
|
214857
214921
|
repo_id: repo.id,
|
|
@@ -214859,12 +214923,13 @@ async function scanCommand(args) {
|
|
|
214859
214923
|
commit_hash: commitHash,
|
|
214860
214924
|
author
|
|
214861
214925
|
});
|
|
214926
|
+
const rulesEvaluated = result.rules_evaluated || 0;
|
|
214862
214927
|
if (result.status === "clean") {
|
|
214863
|
-
console.log(
|
|
214928
|
+
console.log(`Clean \u2014 ${rulesEvaluated} rule(s) evaluated, no violations.`);
|
|
214864
214929
|
process.exit(0);
|
|
214865
214930
|
}
|
|
214866
214931
|
if (!result.violations || result.violations.length === 0) {
|
|
214867
|
-
console.log(
|
|
214932
|
+
console.log(`Clean \u2014 ${rulesEvaluated} rule(s) evaluated, no violations.`);
|
|
214868
214933
|
process.exit(0);
|
|
214869
214934
|
}
|
|
214870
214935
|
console.log(`
|
|
@@ -215066,12 +215131,16 @@ async function contextCommand(args) {
|
|
|
215066
215131
|
try {
|
|
215067
215132
|
const summaries = await api.get(`/workspaces/${workspaceSlug}/watchers${qs}`);
|
|
215068
215133
|
if (isHuman) {
|
|
215069
|
-
|
|
215134
|
+
if (summaries.length === 0) {
|
|
215135
|
+
console.log("No context topics yet. Run `driftless init` to auto-generate watchers from your codebase.");
|
|
215136
|
+
} else {
|
|
215137
|
+
renderSummaryHuman(summaries);
|
|
215138
|
+
}
|
|
215070
215139
|
} else {
|
|
215071
215140
|
emitJSON(summaries);
|
|
215072
215141
|
}
|
|
215073
215142
|
} catch (e) {
|
|
215074
|
-
console.error(`List failed: ${e
|
|
215143
|
+
console.error(`List failed: ${formatError(e)}`);
|
|
215075
215144
|
process.exit(1);
|
|
215076
215145
|
}
|
|
215077
215146
|
return;
|
|
@@ -215105,12 +215174,16 @@ async function contextCommand(args) {
|
|
|
215105
215174
|
{ files }
|
|
215106
215175
|
);
|
|
215107
215176
|
if (isHuman) {
|
|
215108
|
-
|
|
215177
|
+
if (results.length === 0) {
|
|
215178
|
+
console.log(`No context topics match these files. No watcher covers the changed paths.`);
|
|
215179
|
+
} else {
|
|
215180
|
+
renderMatchFilesHuman(results, files);
|
|
215181
|
+
}
|
|
215109
215182
|
} else {
|
|
215110
215183
|
emitJSON(results);
|
|
215111
215184
|
}
|
|
215112
215185
|
} catch (e) {
|
|
215113
|
-
console.error(`Match failed: ${e
|
|
215186
|
+
console.error(`Match failed: ${formatError(e)}`);
|
|
215114
215187
|
process.exit(1);
|
|
215115
215188
|
}
|
|
215116
215189
|
return;
|
|
@@ -215145,12 +215218,16 @@ async function contextCommand(args) {
|
|
|
215145
215218
|
`/workspaces/${workspaceSlug}/watchers/search?q=${encodeURIComponent(query)}`
|
|
215146
215219
|
);
|
|
215147
215220
|
if (isHuman) {
|
|
215148
|
-
|
|
215221
|
+
if (results.length === 0) {
|
|
215222
|
+
console.log(`No context topics matching "${query}".`);
|
|
215223
|
+
} else {
|
|
215224
|
+
renderSummaryHuman(results);
|
|
215225
|
+
}
|
|
215149
215226
|
} else {
|
|
215150
215227
|
emitJSON(results);
|
|
215151
215228
|
}
|
|
215152
215229
|
} catch (e) {
|
|
215153
|
-
console.error(`Search failed: ${e
|
|
215230
|
+
console.error(`Search failed: ${formatError(e)}`);
|
|
215154
215231
|
process.exit(1);
|
|
215155
215232
|
}
|
|
215156
215233
|
return;
|
|
@@ -215349,12 +215426,16 @@ async function contextCommand(args) {
|
|
|
215349
215426
|
{ files }
|
|
215350
215427
|
);
|
|
215351
215428
|
if (isHuman) {
|
|
215352
|
-
|
|
215429
|
+
if (results.length === 0) {
|
|
215430
|
+
console.log(`No context topics match these files. No watcher covers the changed paths.`);
|
|
215431
|
+
} else {
|
|
215432
|
+
renderMatchFilesHuman(results, files);
|
|
215433
|
+
}
|
|
215353
215434
|
} else {
|
|
215354
215435
|
emitJSON(results);
|
|
215355
215436
|
}
|
|
215356
215437
|
} catch (e) {
|
|
215357
|
-
console.error(`Push failed: ${e
|
|
215438
|
+
console.error(`Push failed: ${formatError(e)}`);
|
|
215358
215439
|
process.exit(1);
|
|
215359
215440
|
}
|
|
215360
215441
|
return;
|
|
@@ -215590,7 +215671,7 @@ async function doctorCommand() {
|
|
|
215590
215671
|
}
|
|
215591
215672
|
const apiUrl = getApiUrl();
|
|
215592
215673
|
try {
|
|
215593
|
-
await api.get("/
|
|
215674
|
+
await api.get("/health");
|
|
215594
215675
|
checks.push({ name: "API reachable", status: "ok", detail: apiUrl });
|
|
215595
215676
|
} catch {
|
|
215596
215677
|
checks.push({ name: "API reachable", status: "fail", detail: `Cannot reach ${apiUrl}` });
|
|
@@ -215722,7 +215803,7 @@ function pad2(s, n) {
|
|
|
215722
215803
|
}
|
|
215723
215804
|
|
|
215724
215805
|
// src/index.ts
|
|
215725
|
-
var VERSION = "0.1.
|
|
215806
|
+
var VERSION = "0.1.20";
|
|
215726
215807
|
var HELP_TEXT = `Driftless CLI v${VERSION} \u2014 Context integrity for AI engineering teams
|
|
215727
215808
|
|
|
215728
215809
|
Install: npm install -g @driftless-sh/cli
|