@anatolykoptev/krolik-cli 0.10.1 → 0.12.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/bin/cli.js +691 -60
- package/dist/bin/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/bin/cli.js
CHANGED
|
@@ -56,7 +56,7 @@ var KROLIK_VERSION, TEMPLATE_VERSION;
|
|
|
56
56
|
var init_version = __esm({
|
|
57
57
|
"src/version.ts"() {
|
|
58
58
|
init_esm_shims();
|
|
59
|
-
KROLIK_VERSION = "0.
|
|
59
|
+
KROLIK_VERSION = "0.12.0";
|
|
60
60
|
TEMPLATE_VERSION = "6.1.0";
|
|
61
61
|
}
|
|
62
62
|
});
|
|
@@ -13289,18 +13289,33 @@ var init_routes2 = __esm({
|
|
|
13289
13289
|
init_esm_shims();
|
|
13290
13290
|
init_core();
|
|
13291
13291
|
routesFlagSchema = {
|
|
13292
|
-
json: COMMON_FLAGS.json
|
|
13292
|
+
json: COMMON_FLAGS.json,
|
|
13293
|
+
compact: { flag: "--compact" },
|
|
13294
|
+
full: { flag: "--full" }
|
|
13293
13295
|
};
|
|
13294
13296
|
routesTool = {
|
|
13295
13297
|
name: "krolik_routes",
|
|
13296
|
-
description:
|
|
13298
|
+
description: `Analyze tRPC API routes. Returns all procedures with types and protection status.
|
|
13299
|
+
|
|
13300
|
+
**Output modes:**
|
|
13301
|
+
- (default) Smart format - groups procedures by type (queries/mutations), shows only unprotected as exceptions
|
|
13302
|
+
- compact: true - overview only, routers with Q/M counts (smallest output)
|
|
13303
|
+
- full: true - verbose legacy format with all procedure attributes`,
|
|
13297
13304
|
inputSchema: {
|
|
13298
13305
|
type: "object",
|
|
13299
13306
|
properties: {
|
|
13300
13307
|
...PROJECT_PROPERTY,
|
|
13301
13308
|
json: {
|
|
13302
13309
|
type: "boolean",
|
|
13303
|
-
description: "Return JSON format instead of
|
|
13310
|
+
description: "Return JSON format instead of XML"
|
|
13311
|
+
},
|
|
13312
|
+
compact: {
|
|
13313
|
+
type: "boolean",
|
|
13314
|
+
description: "Compact output - routers with procedure counts only. Smallest output."
|
|
13315
|
+
},
|
|
13316
|
+
full: {
|
|
13317
|
+
type: "boolean",
|
|
13318
|
+
description: "Full verbose output - all procedures with all attributes. Use only when you need complete details."
|
|
13304
13319
|
}
|
|
13305
13320
|
}
|
|
13306
13321
|
},
|
|
@@ -13325,18 +13340,47 @@ var init_schema2 = __esm({
|
|
|
13325
13340
|
init_esm_shims();
|
|
13326
13341
|
init_core();
|
|
13327
13342
|
schemaFlagSchema = {
|
|
13328
|
-
json: COMMON_FLAGS.json
|
|
13343
|
+
json: COMMON_FLAGS.json,
|
|
13344
|
+
model: { flag: "--model" },
|
|
13345
|
+
domain: { flag: "--domain" },
|
|
13346
|
+
compact: { flag: "--compact" },
|
|
13347
|
+
full: { flag: "--full" }
|
|
13329
13348
|
};
|
|
13330
13349
|
schemaTool = {
|
|
13331
13350
|
name: "krolik_schema",
|
|
13332
|
-
description:
|
|
13351
|
+
description: `Analyze Prisma database schema. Returns models, fields, relations, and enums.
|
|
13352
|
+
|
|
13353
|
+
**Output modes:**
|
|
13354
|
+
- (default) Smart format - AI-optimized, hides standard fields (id, createdAt, updatedAt), obvious defaults
|
|
13355
|
+
- compact: true - overview only, models with relations (smallest output)
|
|
13356
|
+
- full: true - verbose legacy format with all fields and attributes
|
|
13357
|
+
|
|
13358
|
+
**Filters:**
|
|
13359
|
+
- model: "User" - filter by model name (partial match)
|
|
13360
|
+
- domain: "Auth" - filter by domain (derived from schema filename)`,
|
|
13333
13361
|
inputSchema: {
|
|
13334
13362
|
type: "object",
|
|
13335
13363
|
properties: {
|
|
13336
13364
|
...PROJECT_PROPERTY,
|
|
13337
13365
|
json: {
|
|
13338
13366
|
type: "boolean",
|
|
13339
|
-
description: "Return JSON format instead of
|
|
13367
|
+
description: "Return JSON format instead of XML"
|
|
13368
|
+
},
|
|
13369
|
+
model: {
|
|
13370
|
+
type: "string",
|
|
13371
|
+
description: 'Filter by model name (partial match, case-insensitive). Example: "User", "Booking"'
|
|
13372
|
+
},
|
|
13373
|
+
domain: {
|
|
13374
|
+
type: "string",
|
|
13375
|
+
description: 'Filter by domain (derived from schema filename). Example: "Auth", "Bookings", "CRM"'
|
|
13376
|
+
},
|
|
13377
|
+
compact: {
|
|
13378
|
+
type: "boolean",
|
|
13379
|
+
description: "Compact output - models with relations only, no field details. Smallest output."
|
|
13380
|
+
},
|
|
13381
|
+
full: {
|
|
13382
|
+
type: "boolean",
|
|
13383
|
+
description: "Full verbose output - all fields with all attributes. Use only when you need complete details."
|
|
13340
13384
|
}
|
|
13341
13385
|
}
|
|
13342
13386
|
},
|
|
@@ -39165,8 +39209,8 @@ function formatAI2(data) {
|
|
|
39165
39209
|
` <stats routers="${data.routers.length}" procedures="${data.totalProcedures}" queries="${data.queries}" mutations="${data.mutations}" protected="${data.protectedCount}" />`
|
|
39166
39210
|
);
|
|
39167
39211
|
lines.push("");
|
|
39168
|
-
const grouped =
|
|
39169
|
-
for (const [domain, routers] of
|
|
39212
|
+
const grouped = groupByDomainDynamic(data.routers);
|
|
39213
|
+
for (const [domain, routers] of grouped) {
|
|
39170
39214
|
if (routers.length === 0) continue;
|
|
39171
39215
|
lines.push(` <domain name="${domain}">`);
|
|
39172
39216
|
for (const router of routers) {
|
|
@@ -39186,6 +39230,80 @@ function formatAI2(data) {
|
|
|
39186
39230
|
lines.push("</trpc-routes>");
|
|
39187
39231
|
return lines.join("\n");
|
|
39188
39232
|
}
|
|
39233
|
+
function formatSmart(data) {
|
|
39234
|
+
const lines = [];
|
|
39235
|
+
const protectedRatio = data.protectedCount / data.totalProcedures;
|
|
39236
|
+
const mostProtected = protectedRatio > 0.6;
|
|
39237
|
+
lines.push("<trpc-routes>");
|
|
39238
|
+
lines.push(
|
|
39239
|
+
` <stats routers="${data.routers.length}" procedures="${data.totalProcedures}" queries="${data.queries}" mutations="${data.mutations}" />`
|
|
39240
|
+
);
|
|
39241
|
+
if (mostProtected) {
|
|
39242
|
+
lines.push(
|
|
39243
|
+
` <note>Most procedures are protected (${Math.round(protectedRatio * 100)}%). Unprotected shown explicitly.</note>`
|
|
39244
|
+
);
|
|
39245
|
+
}
|
|
39246
|
+
const grouped = groupByDomainDynamic(data.routers);
|
|
39247
|
+
const domains = Array.from(grouped.keys());
|
|
39248
|
+
lines.push(` <domains>${domains.join(", ")}</domains>`);
|
|
39249
|
+
lines.push("");
|
|
39250
|
+
for (const [domain, routers] of grouped) {
|
|
39251
|
+
if (routers.length === 0) continue;
|
|
39252
|
+
lines.push(` <domain name="${domain}">`);
|
|
39253
|
+
for (const router of routers) {
|
|
39254
|
+
const queries = router.procedures.filter((p) => p.type === "query").map((p) => p.name);
|
|
39255
|
+
const mutations = router.procedures.filter((p) => p.type === "mutation").map((p) => p.name);
|
|
39256
|
+
const unprotected = router.procedures.filter((p) => !p.isProtected).map((p) => p.name);
|
|
39257
|
+
lines.push(` <router file="${router.file}">`);
|
|
39258
|
+
if (queries.length > 0) {
|
|
39259
|
+
lines.push(` <queries>${queries.join(", ")}</queries>`);
|
|
39260
|
+
}
|
|
39261
|
+
if (mutations.length > 0) {
|
|
39262
|
+
lines.push(` <mutations>${mutations.join(", ")}</mutations>`);
|
|
39263
|
+
}
|
|
39264
|
+
if (mostProtected && unprotected.length > 0) {
|
|
39265
|
+
lines.push(` <public>${unprotected.join(", ")}</public>`);
|
|
39266
|
+
}
|
|
39267
|
+
if (!mostProtected) {
|
|
39268
|
+
const protectedProcs = router.procedures.filter((p) => p.isProtected).map((p) => p.name);
|
|
39269
|
+
if (protectedProcs.length > 0) {
|
|
39270
|
+
lines.push(` <protected>${protectedProcs.join(", ")}</protected>`);
|
|
39271
|
+
}
|
|
39272
|
+
}
|
|
39273
|
+
lines.push(" </router>");
|
|
39274
|
+
}
|
|
39275
|
+
lines.push(" </domain>");
|
|
39276
|
+
lines.push("");
|
|
39277
|
+
}
|
|
39278
|
+
lines.push("</trpc-routes>");
|
|
39279
|
+
return lines.join("\n");
|
|
39280
|
+
}
|
|
39281
|
+
function formatCompact(data) {
|
|
39282
|
+
const lines = [];
|
|
39283
|
+
lines.push('<trpc-routes mode="compact">');
|
|
39284
|
+
lines.push(
|
|
39285
|
+
` <stats routers="${data.routers.length}" procedures="${data.totalProcedures}" queries="${data.queries}" mutations="${data.mutations}" />`
|
|
39286
|
+
);
|
|
39287
|
+
const grouped = groupByDomainDynamic(data.routers);
|
|
39288
|
+
const domains = Array.from(grouped.keys());
|
|
39289
|
+
lines.push(` <domains>${domains.join(", ")}</domains>`);
|
|
39290
|
+
lines.push("");
|
|
39291
|
+
for (const [domain, routers] of grouped) {
|
|
39292
|
+
if (routers.length === 0) continue;
|
|
39293
|
+
const routerSummaries = routers.map((r) => {
|
|
39294
|
+
const q = r.procedures.filter((p) => p.type === "query").length;
|
|
39295
|
+
const m = r.procedures.filter((p) => p.type === "mutation").length;
|
|
39296
|
+
return `${r.file}(${q}Q/${m}M)`;
|
|
39297
|
+
});
|
|
39298
|
+
lines.push(` <domain name="${domain}">`);
|
|
39299
|
+
lines.push(` ${routerSummaries.join(", ")}`);
|
|
39300
|
+
lines.push(" </domain>");
|
|
39301
|
+
}
|
|
39302
|
+
lines.push("");
|
|
39303
|
+
lines.push(" <hint>Use --full for procedure details</hint>");
|
|
39304
|
+
lines.push("</trpc-routes>");
|
|
39305
|
+
return lines.join("\n");
|
|
39306
|
+
}
|
|
39189
39307
|
function formatMarkdown2(data) {
|
|
39190
39308
|
const lines = [
|
|
39191
39309
|
"# API Routes (tRPC)",
|
|
@@ -39224,28 +39342,34 @@ function formatMarkdown2(data) {
|
|
|
39224
39342
|
lines.push("*Generated by krolik-cli*");
|
|
39225
39343
|
return lines.join("\n");
|
|
39226
39344
|
}
|
|
39227
|
-
function
|
|
39228
|
-
const
|
|
39229
|
-
Business: [],
|
|
39230
|
-
User: [],
|
|
39231
|
-
Content: [],
|
|
39232
|
-
Social: [],
|
|
39233
|
-
System: []
|
|
39234
|
-
};
|
|
39345
|
+
function groupByDomainDynamic(routers) {
|
|
39346
|
+
const result = /* @__PURE__ */ new Map();
|
|
39235
39347
|
for (const router of routers) {
|
|
39236
|
-
|
|
39237
|
-
|
|
39238
|
-
|
|
39239
|
-
domains.User?.push(router);
|
|
39240
|
-
} else if (["places", "events", "reviews", "search"].some((k) => router.file.includes(k))) {
|
|
39241
|
-
domains.Content?.push(router);
|
|
39242
|
-
} else if (["social", "activity", "referral", "interactions"].some((k) => router.file.includes(k))) {
|
|
39243
|
-
domains.Social?.push(router);
|
|
39244
|
-
} else {
|
|
39245
|
-
domains.System?.push(router);
|
|
39348
|
+
const domain = inferDomainFromPath(router.file);
|
|
39349
|
+
if (!result.has(domain)) {
|
|
39350
|
+
result.set(domain, []);
|
|
39246
39351
|
}
|
|
39352
|
+
result.get(domain).push(router);
|
|
39247
39353
|
}
|
|
39248
|
-
return
|
|
39354
|
+
return new Map([...result.entries()].sort((a, b) => a[0].localeCompare(b[0])));
|
|
39355
|
+
}
|
|
39356
|
+
function inferDomainFromPath(filePath) {
|
|
39357
|
+
const firstSegment = filePath.split("/")[0] ?? filePath;
|
|
39358
|
+
if (firstSegment.startsWith("business")) {
|
|
39359
|
+
return "Business";
|
|
39360
|
+
}
|
|
39361
|
+
if (firstSegment.startsWith("admin")) {
|
|
39362
|
+
return "Admin";
|
|
39363
|
+
}
|
|
39364
|
+
return firstSegment.charAt(0).toUpperCase() + firstSegment.slice(1);
|
|
39365
|
+
}
|
|
39366
|
+
function groupByDomain2(routers) {
|
|
39367
|
+
const grouped = groupByDomainDynamic(routers);
|
|
39368
|
+
const result = {};
|
|
39369
|
+
for (const [domain, routerList] of grouped) {
|
|
39370
|
+
result[domain] = routerList;
|
|
39371
|
+
}
|
|
39372
|
+
return result;
|
|
39249
39373
|
}
|
|
39250
39374
|
var MAX_LENGTH2, SLICE_ARG1_VALUE2;
|
|
39251
39375
|
var init_output6 = __esm({
|
|
@@ -39583,7 +39707,15 @@ async function runRoutes(ctx) {
|
|
|
39583
39707
|
printRoutes(result, logger2);
|
|
39584
39708
|
return;
|
|
39585
39709
|
}
|
|
39586
|
-
|
|
39710
|
+
if (options.compact) {
|
|
39711
|
+
console.log(formatCompact(result));
|
|
39712
|
+
return;
|
|
39713
|
+
}
|
|
39714
|
+
if (options.full) {
|
|
39715
|
+
console.log(xmlOutput);
|
|
39716
|
+
return;
|
|
39717
|
+
}
|
|
39718
|
+
console.log(formatSmart(result));
|
|
39587
39719
|
}
|
|
39588
39720
|
var init_routes3 = __esm({
|
|
39589
39721
|
"src/commands/routes/index.ts"() {
|
|
@@ -39594,42 +39726,246 @@ var init_routes3 = __esm({
|
|
|
39594
39726
|
}
|
|
39595
39727
|
});
|
|
39596
39728
|
|
|
39729
|
+
// src/commands/schema/relation-graph.ts
|
|
39730
|
+
function buildRelationGraph(models) {
|
|
39731
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
39732
|
+
for (const model of models) {
|
|
39733
|
+
nodes.set(model.name, {
|
|
39734
|
+
model: model.name,
|
|
39735
|
+
file: model.file,
|
|
39736
|
+
relations: model.relations,
|
|
39737
|
+
referencedBy: [],
|
|
39738
|
+
inDegree: 0,
|
|
39739
|
+
outDegree: model.relations.length
|
|
39740
|
+
});
|
|
39741
|
+
}
|
|
39742
|
+
for (const model of models) {
|
|
39743
|
+
for (const relation of model.relations) {
|
|
39744
|
+
const targetNode = nodes.get(relation);
|
|
39745
|
+
if (targetNode) {
|
|
39746
|
+
targetNode.referencedBy.push(model.name);
|
|
39747
|
+
targetNode.inDegree++;
|
|
39748
|
+
}
|
|
39749
|
+
}
|
|
39750
|
+
}
|
|
39751
|
+
return nodes;
|
|
39752
|
+
}
|
|
39753
|
+
function findSCC(nodes) {
|
|
39754
|
+
const state = {
|
|
39755
|
+
index: /* @__PURE__ */ new Map(),
|
|
39756
|
+
lowlink: /* @__PURE__ */ new Map(),
|
|
39757
|
+
onStack: /* @__PURE__ */ new Set(),
|
|
39758
|
+
stack: [],
|
|
39759
|
+
sccs: [],
|
|
39760
|
+
currentIndex: 0
|
|
39761
|
+
};
|
|
39762
|
+
function strongConnect(nodeId) {
|
|
39763
|
+
state.index.set(nodeId, state.currentIndex);
|
|
39764
|
+
state.lowlink.set(nodeId, state.currentIndex);
|
|
39765
|
+
state.currentIndex++;
|
|
39766
|
+
state.stack.push(nodeId);
|
|
39767
|
+
state.onStack.add(nodeId);
|
|
39768
|
+
const node = nodes.get(nodeId);
|
|
39769
|
+
const deps = node?.relations ?? [];
|
|
39770
|
+
for (const depId of deps) {
|
|
39771
|
+
if (!nodes.has(depId)) continue;
|
|
39772
|
+
if (!state.index.has(depId)) {
|
|
39773
|
+
strongConnect(depId);
|
|
39774
|
+
state.lowlink.set(nodeId, Math.min(state.lowlink.get(nodeId), state.lowlink.get(depId)));
|
|
39775
|
+
} else if (state.onStack.has(depId)) {
|
|
39776
|
+
state.lowlink.set(nodeId, Math.min(state.lowlink.get(nodeId), state.index.get(depId)));
|
|
39777
|
+
}
|
|
39778
|
+
}
|
|
39779
|
+
if (state.lowlink.get(nodeId) === state.index.get(nodeId)) {
|
|
39780
|
+
const scc = [];
|
|
39781
|
+
let w;
|
|
39782
|
+
do {
|
|
39783
|
+
w = state.stack.pop();
|
|
39784
|
+
state.onStack.delete(w);
|
|
39785
|
+
scc.push(w);
|
|
39786
|
+
} while (w !== nodeId);
|
|
39787
|
+
state.sccs.push(scc);
|
|
39788
|
+
}
|
|
39789
|
+
}
|
|
39790
|
+
for (const nodeId of nodes.keys()) {
|
|
39791
|
+
if (!state.index.has(nodeId)) {
|
|
39792
|
+
strongConnect(nodeId);
|
|
39793
|
+
}
|
|
39794
|
+
}
|
|
39795
|
+
return state.sccs;
|
|
39796
|
+
}
|
|
39797
|
+
function findCommonPrefix(models) {
|
|
39798
|
+
if (models.length < 2) return null;
|
|
39799
|
+
const prefixes = /* @__PURE__ */ new Map();
|
|
39800
|
+
for (const model of models) {
|
|
39801
|
+
const parts = model.split(/(?=[A-Z])/);
|
|
39802
|
+
if (parts.length >= 2) {
|
|
39803
|
+
const prefix = parts[0];
|
|
39804
|
+
if (prefix.length >= 3) {
|
|
39805
|
+
prefixes.set(prefix, (prefixes.get(prefix) ?? 0) + 1);
|
|
39806
|
+
}
|
|
39807
|
+
}
|
|
39808
|
+
}
|
|
39809
|
+
for (const [prefix, count] of prefixes) {
|
|
39810
|
+
const ratio = count / models.length;
|
|
39811
|
+
if (ratio >= 0.6) {
|
|
39812
|
+
return prefix;
|
|
39813
|
+
}
|
|
39814
|
+
}
|
|
39815
|
+
return null;
|
|
39816
|
+
}
|
|
39817
|
+
function findCoreEntity(models, nodes) {
|
|
39818
|
+
let maxInDegree = 0;
|
|
39819
|
+
let core = null;
|
|
39820
|
+
for (const model of models) {
|
|
39821
|
+
const node = nodes.get(model);
|
|
39822
|
+
if (node && node.inDegree > maxInDegree) {
|
|
39823
|
+
maxInDegree = node.inDegree;
|
|
39824
|
+
core = model;
|
|
39825
|
+
}
|
|
39826
|
+
}
|
|
39827
|
+
return maxInDegree >= 2 ? core : null;
|
|
39828
|
+
}
|
|
39829
|
+
function inferClusterName(models, nodes) {
|
|
39830
|
+
const prefix = findCommonPrefix(models);
|
|
39831
|
+
if (prefix) {
|
|
39832
|
+
return {
|
|
39833
|
+
name: prefix.endsWith("s") ? prefix : `${prefix}s`,
|
|
39834
|
+
confidence: 85,
|
|
39835
|
+
source: "prefix"
|
|
39836
|
+
};
|
|
39837
|
+
}
|
|
39838
|
+
const core = findCoreEntity(models, nodes);
|
|
39839
|
+
if (core && models.length >= 2) {
|
|
39840
|
+
return {
|
|
39841
|
+
name: core.endsWith("s") ? core : `${core}s`,
|
|
39842
|
+
confidence: 75,
|
|
39843
|
+
source: "core-entity"
|
|
39844
|
+
};
|
|
39845
|
+
}
|
|
39846
|
+
const files = new Set(models.map((m) => nodes.get(m)?.file ?? ""));
|
|
39847
|
+
if (files.size === 1) {
|
|
39848
|
+
const file = [...files][0];
|
|
39849
|
+
const domain = file.replace(/\.prisma$/, "").replace(/^.*\//, "");
|
|
39850
|
+
if (domain && domain !== "schema") {
|
|
39851
|
+
return {
|
|
39852
|
+
name: domain.charAt(0).toUpperCase() + domain.slice(1),
|
|
39853
|
+
confidence: 50,
|
|
39854
|
+
source: "filename"
|
|
39855
|
+
};
|
|
39856
|
+
}
|
|
39857
|
+
}
|
|
39858
|
+
return { name: "", confidence: 0, source: "filename" };
|
|
39859
|
+
}
|
|
39860
|
+
function analyzeRelationGraph(models) {
|
|
39861
|
+
const nodes = buildRelationGraph(models);
|
|
39862
|
+
const sccs = findSCC(nodes);
|
|
39863
|
+
const clusters = [];
|
|
39864
|
+
const unclustered = [];
|
|
39865
|
+
for (const scc of sccs) {
|
|
39866
|
+
if (scc.length < MIN_CLUSTER_SIZE) {
|
|
39867
|
+
const node = nodes.get(scc[0]);
|
|
39868
|
+
if (!node || node.inDegree < 3 && node.outDegree < 3) {
|
|
39869
|
+
unclustered.push(...scc);
|
|
39870
|
+
continue;
|
|
39871
|
+
}
|
|
39872
|
+
}
|
|
39873
|
+
const { name, confidence, source } = inferClusterName(scc, nodes);
|
|
39874
|
+
if (confidence < MIN_CONFIDENCE2) {
|
|
39875
|
+
unclustered.push(...scc);
|
|
39876
|
+
continue;
|
|
39877
|
+
}
|
|
39878
|
+
const core = findCoreEntity(scc, nodes);
|
|
39879
|
+
clusters.push({
|
|
39880
|
+
name,
|
|
39881
|
+
core,
|
|
39882
|
+
models: scc.sort(),
|
|
39883
|
+
confidence,
|
|
39884
|
+
source
|
|
39885
|
+
});
|
|
39886
|
+
}
|
|
39887
|
+
clusters.sort((a, b) => b.models.length - a.models.length);
|
|
39888
|
+
return {
|
|
39889
|
+
nodes: Array.from(nodes.values()),
|
|
39890
|
+
clusters,
|
|
39891
|
+
unclustered: unclustered.sort()
|
|
39892
|
+
};
|
|
39893
|
+
}
|
|
39894
|
+
var MIN_CONFIDENCE2, MIN_CLUSTER_SIZE;
|
|
39895
|
+
var init_relation_graph = __esm({
|
|
39896
|
+
"src/commands/schema/relation-graph.ts"() {
|
|
39897
|
+
init_esm_shims();
|
|
39898
|
+
MIN_CONFIDENCE2 = 60;
|
|
39899
|
+
MIN_CLUSTER_SIZE = 2;
|
|
39900
|
+
}
|
|
39901
|
+
});
|
|
39902
|
+
|
|
39597
39903
|
// src/commands/schema/grouping.ts
|
|
39598
39904
|
function groupByFile2(models) {
|
|
39599
39905
|
return groupByProperty(models, "file");
|
|
39600
39906
|
}
|
|
39601
39907
|
function groupByDomain3(models) {
|
|
39602
|
-
|
|
39603
|
-
|
|
39604
|
-
|
|
39605
|
-
const
|
|
39606
|
-
|
|
39607
|
-
|
|
39608
|
-
|
|
39908
|
+
if (models.length === 0) {
|
|
39909
|
+
return /* @__PURE__ */ new Map();
|
|
39910
|
+
}
|
|
39911
|
+
const files = new Set(models.map((m) => m.file));
|
|
39912
|
+
const isSingleFile = files.size === 1;
|
|
39913
|
+
if (!isSingleFile) {
|
|
39914
|
+
return groupByFilename(models);
|
|
39915
|
+
}
|
|
39916
|
+
const { clusters, unclustered } = analyzeRelationGraph(models);
|
|
39917
|
+
if (clusters.length === 0) {
|
|
39918
|
+
return groupByFilename(models);
|
|
39919
|
+
}
|
|
39920
|
+
const result = /* @__PURE__ */ new Map();
|
|
39921
|
+
const modelMap = new Map(models.map((m) => [m.name, m]));
|
|
39922
|
+
for (const cluster of clusters) {
|
|
39923
|
+
const clusterModels = cluster.models.map((name) => modelMap.get(name)).filter((m) => m !== void 0);
|
|
39924
|
+
if (clusterModels.length > 0) {
|
|
39925
|
+
result.set(cluster.name, clusterModels);
|
|
39926
|
+
}
|
|
39927
|
+
}
|
|
39928
|
+
if (unclustered.length > 0) {
|
|
39929
|
+
const unclusteredModels = unclustered.map((name) => modelMap.get(name)).filter((m) => m !== void 0);
|
|
39930
|
+
for (const model of unclusteredModels) {
|
|
39931
|
+
const domain = inferDomainFromFilename(model.file);
|
|
39932
|
+
const existing = result.get(domain) ?? [];
|
|
39933
|
+
existing.push(model);
|
|
39934
|
+
result.set(domain, existing);
|
|
39609
39935
|
}
|
|
39610
39936
|
}
|
|
39611
|
-
return
|
|
39937
|
+
return new Map([...result.entries()].sort((a, b) => a[0].localeCompare(b[0])));
|
|
39938
|
+
}
|
|
39939
|
+
function groupByFilename(models) {
|
|
39940
|
+
const result = /* @__PURE__ */ new Map();
|
|
39941
|
+
for (const model of models) {
|
|
39942
|
+
const domain = inferDomainFromFilename(model.file);
|
|
39943
|
+
const existing = result.get(domain) ?? [];
|
|
39944
|
+
existing.push(model);
|
|
39945
|
+
result.set(domain, existing);
|
|
39946
|
+
}
|
|
39947
|
+
return new Map([...result.entries()].sort((a, b) => a[0].localeCompare(b[0])));
|
|
39948
|
+
}
|
|
39949
|
+
function inferDomainFromFilename(filename) {
|
|
39950
|
+
const base = filename.replace(/\.prisma$/, "").replace(/^.*\//, "");
|
|
39951
|
+
if (!base || base === "schema") return "Other";
|
|
39952
|
+
const parts = base.split(/[-_]/);
|
|
39953
|
+
const formatted = parts.map((part) => {
|
|
39954
|
+
const lower = part.toLowerCase();
|
|
39955
|
+
if (UPPERCASE_ABBREVS.includes(lower)) {
|
|
39956
|
+
return lower.toUpperCase();
|
|
39957
|
+
}
|
|
39958
|
+
return part.charAt(0).toUpperCase() + part.slice(1).toLowerCase();
|
|
39959
|
+
});
|
|
39960
|
+
return formatted.join(" ");
|
|
39612
39961
|
}
|
|
39613
|
-
var
|
|
39962
|
+
var UPPERCASE_ABBREVS;
|
|
39614
39963
|
var init_grouping4 = __esm({
|
|
39615
39964
|
"src/commands/schema/grouping.ts"() {
|
|
39616
39965
|
init_esm_shims();
|
|
39617
39966
|
init_core2();
|
|
39618
|
-
|
|
39619
|
-
|
|
39620
|
-
user: "Users",
|
|
39621
|
-
content: "Content",
|
|
39622
|
-
booking: "Bookings",
|
|
39623
|
-
event: "Events",
|
|
39624
|
-
ticket: "Ticketing",
|
|
39625
|
-
business: "Business",
|
|
39626
|
-
social: "Social",
|
|
39627
|
-
gamification: "Gamification",
|
|
39628
|
-
payment: "Payments",
|
|
39629
|
-
notification: "Notifications",
|
|
39630
|
-
integration: "Integrations",
|
|
39631
|
-
system: "System"
|
|
39632
|
-
};
|
|
39967
|
+
init_relation_graph();
|
|
39968
|
+
UPPERCASE_ABBREVS = ["crm", "api", "ugc", "sso", "oauth", "jwt", "sql"];
|
|
39633
39969
|
}
|
|
39634
39970
|
});
|
|
39635
39971
|
|
|
@@ -39756,11 +40092,208 @@ function formatMarkdown3(data) {
|
|
|
39756
40092
|
lines.push("*Generated by krolik-cli*");
|
|
39757
40093
|
return lines.join("\n");
|
|
39758
40094
|
}
|
|
40095
|
+
function isObviousDefault(value) {
|
|
40096
|
+
if (!value) return false;
|
|
40097
|
+
return OBVIOUS_DEFAULTS.has(value) || value.startsWith('"') || value.startsWith("'");
|
|
40098
|
+
}
|
|
40099
|
+
function isRelationArray(field) {
|
|
40100
|
+
return field.isArray && field.type.charAt(0) === field.type.charAt(0).toUpperCase() && ![
|
|
40101
|
+
"String",
|
|
40102
|
+
"Int",
|
|
40103
|
+
"Float",
|
|
40104
|
+
"Boolean",
|
|
40105
|
+
"DateTime",
|
|
40106
|
+
"Json",
|
|
40107
|
+
"Bytes",
|
|
40108
|
+
"BigInt",
|
|
40109
|
+
"Decimal"
|
|
40110
|
+
].includes(field.type);
|
|
40111
|
+
}
|
|
40112
|
+
function classifyField(field) {
|
|
40113
|
+
if (STANDARD_FIELDS.has(field.name)) return "skip";
|
|
40114
|
+
if (isRelationArray(field)) return "skip";
|
|
40115
|
+
if (field.name.endsWith("Id") || field.isUnique || field.isId) return "key";
|
|
40116
|
+
if (field.name.includes("At") || field.name.includes("Date") || field.name.startsWith("created") || field.name.startsWith("updated")) {
|
|
40117
|
+
return "meta";
|
|
40118
|
+
}
|
|
40119
|
+
return "business";
|
|
40120
|
+
}
|
|
40121
|
+
function formatSmart2(data, fullData) {
|
|
40122
|
+
const lines = [];
|
|
40123
|
+
const total = fullData ?? data;
|
|
40124
|
+
const isFiltered = fullData && data.modelCount !== fullData.modelCount;
|
|
40125
|
+
lines.push("<prisma-schema>");
|
|
40126
|
+
if (isFiltered) {
|
|
40127
|
+
lines.push(
|
|
40128
|
+
` <stats models="${data.modelCount}" enums="${data.enumCount}" filtered="true" total="${total.modelCount}" />`
|
|
40129
|
+
);
|
|
40130
|
+
} else {
|
|
40131
|
+
lines.push(` <stats models="${data.modelCount}" enums="${data.enumCount}" />`);
|
|
40132
|
+
}
|
|
40133
|
+
const byDomain = groupByDomain3(data.models);
|
|
40134
|
+
const domains = Array.from(byDomain.keys()).sort();
|
|
40135
|
+
lines.push(` <domains>${domains.join(", ")}</domains>`);
|
|
40136
|
+
lines.push("");
|
|
40137
|
+
for (const [domain, models] of byDomain) {
|
|
40138
|
+
lines.push(` <domain name="${domain}">`);
|
|
40139
|
+
for (const model of models) {
|
|
40140
|
+
const keyFields = [];
|
|
40141
|
+
const businessFields = [];
|
|
40142
|
+
const metaFields = [];
|
|
40143
|
+
const relationArrays = [];
|
|
40144
|
+
for (const field of model.fields) {
|
|
40145
|
+
if (isRelationArray(field)) {
|
|
40146
|
+
relationArrays.push(`${field.name}\u2192${field.type}[]`);
|
|
40147
|
+
continue;
|
|
40148
|
+
}
|
|
40149
|
+
const category = classifyField(field);
|
|
40150
|
+
if (category === "skip") continue;
|
|
40151
|
+
const formatted = formatFieldSmart(field);
|
|
40152
|
+
switch (category) {
|
|
40153
|
+
case "key":
|
|
40154
|
+
keyFields.push(formatted);
|
|
40155
|
+
break;
|
|
40156
|
+
case "business":
|
|
40157
|
+
businessFields.push(formatted);
|
|
40158
|
+
break;
|
|
40159
|
+
case "meta":
|
|
40160
|
+
metaFields.push(formatted);
|
|
40161
|
+
break;
|
|
40162
|
+
}
|
|
40163
|
+
}
|
|
40164
|
+
lines.push(` <model name="${model.name}">`);
|
|
40165
|
+
const allRelations = [
|
|
40166
|
+
...model.relations,
|
|
40167
|
+
...relationArrays.length > 0 ? relationArrays : []
|
|
40168
|
+
];
|
|
40169
|
+
if (allRelations.length > 0) {
|
|
40170
|
+
lines.push(` <relations>${allRelations.join(", ")}</relations>`);
|
|
40171
|
+
}
|
|
40172
|
+
if (keyFields.length > 0) {
|
|
40173
|
+
lines.push(` <keys>${keyFields.join(", ")}</keys>`);
|
|
40174
|
+
}
|
|
40175
|
+
if (businessFields.length > 0) {
|
|
40176
|
+
lines.push(` <data>${businessFields.join(", ")}</data>`);
|
|
40177
|
+
}
|
|
40178
|
+
if (metaFields.length > 0) {
|
|
40179
|
+
lines.push(` <meta>${metaFields.join(", ")}</meta>`);
|
|
40180
|
+
}
|
|
40181
|
+
lines.push(" </model>");
|
|
40182
|
+
}
|
|
40183
|
+
lines.push(" </domain>");
|
|
40184
|
+
lines.push("");
|
|
40185
|
+
}
|
|
40186
|
+
if (data.enums.length > 0) {
|
|
40187
|
+
lines.push(" <enums>");
|
|
40188
|
+
const enumsByPrefix = groupEnumsByPrefix(data.enums);
|
|
40189
|
+
for (const [prefix, names] of enumsByPrefix) {
|
|
40190
|
+
if (names.length === 1) {
|
|
40191
|
+
lines.push(` ${names[0]}`);
|
|
40192
|
+
} else {
|
|
40193
|
+
lines.push(` ${prefix}*: ${names.join(", ")}`);
|
|
40194
|
+
}
|
|
40195
|
+
}
|
|
40196
|
+
lines.push(" </enums>");
|
|
40197
|
+
}
|
|
40198
|
+
lines.push("</prisma-schema>");
|
|
40199
|
+
return lines.join("\n");
|
|
40200
|
+
}
|
|
40201
|
+
function formatFieldSmart(field) {
|
|
40202
|
+
let result = field.name;
|
|
40203
|
+
const simpleTypes = ["String", "Int", "Float", "Boolean", "DateTime", "Json"];
|
|
40204
|
+
if (!simpleTypes.includes(field.type)) {
|
|
40205
|
+
result += `:${field.type}`;
|
|
40206
|
+
}
|
|
40207
|
+
if (!field.isRequired) result += "?";
|
|
40208
|
+
if (field.isUnique && !field.name.endsWith("Id")) result += "!";
|
|
40209
|
+
if (field.isArray) result += "[]";
|
|
40210
|
+
if (field.default && !isObviousDefault(field.default)) {
|
|
40211
|
+
result += `=${field.default}`;
|
|
40212
|
+
}
|
|
40213
|
+
return result;
|
|
40214
|
+
}
|
|
40215
|
+
function groupEnumsByPrefix(enums) {
|
|
40216
|
+
const result = /* @__PURE__ */ new Map();
|
|
40217
|
+
for (const e of enums) {
|
|
40218
|
+
const match = e.name.match(/^([A-Z][a-z]+)/);
|
|
40219
|
+
const prefix = match?.[1] ?? "Other";
|
|
40220
|
+
if (!result.has(prefix)) result.set(prefix, []);
|
|
40221
|
+
result.get(prefix).push(e.name);
|
|
40222
|
+
}
|
|
40223
|
+
return new Map([...result.entries()].sort((a, b) => a[0].localeCompare(b[0])));
|
|
40224
|
+
}
|
|
40225
|
+
function formatCompact2(data, fullData) {
|
|
40226
|
+
const lines = [];
|
|
40227
|
+
const total = fullData ?? data;
|
|
40228
|
+
const isFiltered = fullData && data.modelCount !== fullData.modelCount;
|
|
40229
|
+
lines.push('<prisma-schema mode="compact">');
|
|
40230
|
+
if (isFiltered) {
|
|
40231
|
+
lines.push(
|
|
40232
|
+
` <stats models="${data.modelCount}" enums="${data.enumCount}" filtered="true" total-models="${total.modelCount}" total-enums="${total.enumCount}" />`
|
|
40233
|
+
);
|
|
40234
|
+
} else {
|
|
40235
|
+
lines.push(` <stats models="${data.modelCount}" enums="${data.enumCount}" />`);
|
|
40236
|
+
}
|
|
40237
|
+
const byDomain = groupByDomain3(data.models);
|
|
40238
|
+
const domains = Array.from(byDomain.keys()).sort();
|
|
40239
|
+
lines.push(` <domains>${domains.join(", ")}</domains>`);
|
|
40240
|
+
lines.push("");
|
|
40241
|
+
for (const [domain, models] of byDomain) {
|
|
40242
|
+
const modelCount = models.length;
|
|
40243
|
+
lines.push(` <domain name="${domain}" models="${modelCount}">`);
|
|
40244
|
+
for (const model of models) {
|
|
40245
|
+
const relStr = model.relations.length > 0 ? ` relations="${model.relations.join(", ")}"` : "";
|
|
40246
|
+
const keyFields = model.fields.filter((f) => f.isId || f.isUnique || f.name.endsWith("Id"));
|
|
40247
|
+
const keyFieldNames = keyFields.map((f) => f.name).slice(0, 5);
|
|
40248
|
+
const keyStr = keyFieldNames.length > 0 ? ` keys="${keyFieldNames.join(", ")}"` : "";
|
|
40249
|
+
lines.push(
|
|
40250
|
+
` <model name="${model.name}" fields="${model.fields.length}"${relStr}${keyStr} />`
|
|
40251
|
+
);
|
|
40252
|
+
}
|
|
40253
|
+
lines.push(" </domain>");
|
|
40254
|
+
}
|
|
40255
|
+
if (data.enums.length > 0) {
|
|
40256
|
+
lines.push("");
|
|
40257
|
+
lines.push(` <enums count="${data.enums.length}">`);
|
|
40258
|
+
const enumNames = data.enums.map((e) => e.name).sort();
|
|
40259
|
+
if (enumNames.length > 20) {
|
|
40260
|
+
const byPrefix = /* @__PURE__ */ new Map();
|
|
40261
|
+
for (const name of enumNames) {
|
|
40262
|
+
const prefix = name.replace(/[A-Z][a-z]+$/, "").replace(/Status$|Type$|Role$|State$/, "") || "General";
|
|
40263
|
+
if (!byPrefix.has(prefix)) byPrefix.set(prefix, []);
|
|
40264
|
+
byPrefix.get(prefix).push(name);
|
|
40265
|
+
}
|
|
40266
|
+
for (const [prefix, names] of byPrefix) {
|
|
40267
|
+
lines.push(` <group prefix="${prefix}">${names.join(", ")}</group>`);
|
|
40268
|
+
}
|
|
40269
|
+
} else {
|
|
40270
|
+
lines.push(` ${enumNames.join(", ")}`);
|
|
40271
|
+
}
|
|
40272
|
+
lines.push(" </enums>");
|
|
40273
|
+
}
|
|
40274
|
+
lines.push("");
|
|
40275
|
+
lines.push(' <hint>Use --model "Name" or --domain "Domain" for details</hint>');
|
|
40276
|
+
lines.push("</prisma-schema>");
|
|
40277
|
+
return lines.join("\n");
|
|
40278
|
+
}
|
|
40279
|
+
var STANDARD_FIELDS, OBVIOUS_DEFAULTS;
|
|
39759
40280
|
var init_output7 = __esm({
|
|
39760
40281
|
"src/commands/schema/output.ts"() {
|
|
39761
40282
|
init_esm_shims();
|
|
39762
40283
|
init_format();
|
|
39763
40284
|
init_grouping4();
|
|
40285
|
+
STANDARD_FIELDS = /* @__PURE__ */ new Set(["id", "createdAt", "updatedAt"]);
|
|
40286
|
+
OBVIOUS_DEFAULTS = /* @__PURE__ */ new Set([
|
|
40287
|
+
"cuid(",
|
|
40288
|
+
"uuid(",
|
|
40289
|
+
"now(",
|
|
40290
|
+
"autoincrement(",
|
|
40291
|
+
"false",
|
|
40292
|
+
"true",
|
|
40293
|
+
"0",
|
|
40294
|
+
"1",
|
|
40295
|
+
'""'
|
|
40296
|
+
]);
|
|
39764
40297
|
}
|
|
39765
40298
|
});
|
|
39766
40299
|
|
|
@@ -39800,6 +40333,29 @@ function findSchemaDir2(projectRoot, configSchemaDir) {
|
|
|
39800
40333
|
}
|
|
39801
40334
|
return null;
|
|
39802
40335
|
}
|
|
40336
|
+
function filterSchema(result, options) {
|
|
40337
|
+
let { models, enums } = result;
|
|
40338
|
+
if (options.domain) {
|
|
40339
|
+
const domainLower = options.domain.toLowerCase();
|
|
40340
|
+
const byDomain = groupByDomain3(models);
|
|
40341
|
+
const matchingDomain = Array.from(byDomain.keys()).find((d) => d.toLowerCase() === domainLower);
|
|
40342
|
+
if (matchingDomain) {
|
|
40343
|
+
models = byDomain.get(matchingDomain) ?? [];
|
|
40344
|
+
} else {
|
|
40345
|
+
models = [];
|
|
40346
|
+
}
|
|
40347
|
+
}
|
|
40348
|
+
if (options.model) {
|
|
40349
|
+
const modelLower = options.model.toLowerCase();
|
|
40350
|
+
models = models.filter((m) => m.name.toLowerCase().includes(modelLower));
|
|
40351
|
+
}
|
|
40352
|
+
return {
|
|
40353
|
+
models,
|
|
40354
|
+
enums,
|
|
40355
|
+
modelCount: models.length,
|
|
40356
|
+
enumCount: enums.length
|
|
40357
|
+
};
|
|
40358
|
+
}
|
|
39803
40359
|
async function runSchema(ctx) {
|
|
39804
40360
|
const { config, logger: logger2, options } = ctx;
|
|
39805
40361
|
const schemaDir = findSchemaDir2(config.projectRoot, config.prisma?.schemaDir);
|
|
@@ -39808,9 +40364,11 @@ async function runSchema(ctx) {
|
|
|
39808
40364
|
logger2.info("Checked: prisma, packages/db/prisma, src/prisma, db/prisma");
|
|
39809
40365
|
return;
|
|
39810
40366
|
}
|
|
39811
|
-
const
|
|
40367
|
+
const fullResult = analyzeSchema(schemaDir);
|
|
40368
|
+
const hasFilters = options.model || options.domain;
|
|
40369
|
+
const result = hasFilters ? filterSchema(fullResult, options) : fullResult;
|
|
39812
40370
|
const format2 = options.format ?? "ai";
|
|
39813
|
-
const xmlOutput = formatAI3(
|
|
40371
|
+
const xmlOutput = formatAI3(fullResult);
|
|
39814
40372
|
saveKrolikFile(config.projectRoot, "SCHEMA.xml", xmlOutput);
|
|
39815
40373
|
if (options.save) {
|
|
39816
40374
|
const md = formatMarkdown3(result);
|
|
@@ -39831,12 +40389,21 @@ async function runSchema(ctx) {
|
|
|
39831
40389
|
printSchema(result, logger2, options.groupBy ?? "file");
|
|
39832
40390
|
return;
|
|
39833
40391
|
}
|
|
39834
|
-
|
|
40392
|
+
if (options.compact) {
|
|
40393
|
+
console.log(formatCompact2(result, fullResult));
|
|
40394
|
+
return;
|
|
40395
|
+
}
|
|
40396
|
+
if (options.full) {
|
|
40397
|
+
console.log(hasFilters ? formatAI3(result) : xmlOutput);
|
|
40398
|
+
return;
|
|
40399
|
+
}
|
|
40400
|
+
console.log(formatSmart2(result, hasFilters ? fullResult : void 0));
|
|
39835
40401
|
}
|
|
39836
40402
|
var init_schema3 = __esm({
|
|
39837
40403
|
"src/commands/schema/index.ts"() {
|
|
39838
40404
|
init_esm_shims();
|
|
39839
40405
|
init_fs2();
|
|
40406
|
+
init_grouping4();
|
|
39840
40407
|
init_output7();
|
|
39841
40408
|
init_parser2();
|
|
39842
40409
|
}
|
|
@@ -59071,21 +59638,85 @@ function registerReviewCommand(program) {
|
|
|
59071
59638
|
|
|
59072
59639
|
// src/cli/commands/routes.ts
|
|
59073
59640
|
init_esm_shims();
|
|
59641
|
+
init_projects();
|
|
59074
59642
|
function registerRoutesCommand(program) {
|
|
59075
|
-
program.command("routes").description("Analyze tRPC routes").option("--save", "Save to ROUTES.md").
|
|
59643
|
+
program.command("routes").description("Analyze tRPC routes").option("--save", "Save to ROUTES.md").option("-c, --compact", "Compact output (routers with procedure counts only)").option("-f, --full", "Full verbose output (all procedures with all attributes)").option("-p, --project <name>", "Project folder name (for multi-project workspaces)").addHelpText(
|
|
59644
|
+
"after",
|
|
59645
|
+
`
|
|
59646
|
+
Output modes:
|
|
59647
|
+
(default) Smart format - groups procedures by type, shows only exceptions
|
|
59648
|
+
--compact Overview only - routers with Q/M counts
|
|
59649
|
+
--full Verbose - all procedures, all attributes (legacy format)
|
|
59650
|
+
|
|
59651
|
+
Examples:
|
|
59652
|
+
krolik routes # Smart format (recommended)
|
|
59653
|
+
krolik routes --compact # Compact overview
|
|
59654
|
+
krolik routes --full # Full verbose output
|
|
59655
|
+
krolik routes --project piternow-wt-fix # Specific project
|
|
59656
|
+
`
|
|
59657
|
+
).action(async (options) => {
|
|
59076
59658
|
const { runRoutes: runRoutes2 } = await Promise.resolve().then(() => (init_routes3(), routes_exports));
|
|
59659
|
+
if (options.project) {
|
|
59660
|
+
const resolved = resolveProjectPath(process.cwd(), options.project);
|
|
59661
|
+
if ("error" in resolved) {
|
|
59662
|
+
console.error(resolved.error);
|
|
59663
|
+
process.exit(1);
|
|
59664
|
+
}
|
|
59665
|
+
process.env.KROLIK_PROJECT_ROOT = resolved.path;
|
|
59666
|
+
}
|
|
59077
59667
|
const ctx = await createContext3(program, options);
|
|
59078
|
-
await runRoutes2(
|
|
59668
|
+
await runRoutes2({
|
|
59669
|
+
...ctx,
|
|
59670
|
+
options: {
|
|
59671
|
+
...ctx.options,
|
|
59672
|
+
compact: options.compact,
|
|
59673
|
+
full: options.full
|
|
59674
|
+
}
|
|
59675
|
+
});
|
|
59079
59676
|
});
|
|
59080
59677
|
}
|
|
59081
59678
|
|
|
59082
59679
|
// src/cli/commands/schema.ts
|
|
59083
59680
|
init_esm_shims();
|
|
59681
|
+
init_projects();
|
|
59084
59682
|
function registerSchemaCommand(program) {
|
|
59085
|
-
program.command("schema").description("Analyze Prisma schema").option("--save", "Save to SCHEMA.md").
|
|
59683
|
+
program.command("schema").description("Analyze Prisma schema").option("--save", "Save to SCHEMA.md").option("-m, --model <name>", "Filter by model name (partial match)").option("-d, --domain <name>", "Filter by domain name").option("-c, --compact", "Compact output (models with relations only, no field details)").option("-f, --full", "Full verbose output (all fields with all attributes)").option("-p, --project <name>", "Project folder name (for multi-project workspaces)").addHelpText(
|
|
59684
|
+
"after",
|
|
59685
|
+
`
|
|
59686
|
+
Output modes:
|
|
59687
|
+
(default) Smart format - optimized for AI, hides standard fields
|
|
59688
|
+
--compact Overview only - models with relations, no field details
|
|
59689
|
+
--full Verbose - all fields, all attributes (legacy format)
|
|
59690
|
+
|
|
59691
|
+
Examples:
|
|
59692
|
+
krolik schema # Smart format (recommended)
|
|
59693
|
+
krolik schema --compact # Compact overview
|
|
59694
|
+
krolik schema --full # Full verbose output
|
|
59695
|
+
krolik schema --domain Bookings # Only Bookings domain
|
|
59696
|
+
krolik schema --model User # Only models containing "User"
|
|
59697
|
+
krolik schema --project piternow-wt-fix # Specific project
|
|
59698
|
+
`
|
|
59699
|
+
).action(async (options) => {
|
|
59086
59700
|
const { runSchema: runSchema2 } = await Promise.resolve().then(() => (init_schema3(), schema_exports));
|
|
59701
|
+
if (options.project) {
|
|
59702
|
+
const resolved = resolveProjectPath(process.cwd(), options.project);
|
|
59703
|
+
if ("error" in resolved) {
|
|
59704
|
+
console.error(resolved.error);
|
|
59705
|
+
process.exit(1);
|
|
59706
|
+
}
|
|
59707
|
+
process.env.KROLIK_PROJECT_ROOT = resolved.path;
|
|
59708
|
+
}
|
|
59087
59709
|
const ctx = await createContext3(program, options);
|
|
59088
|
-
await runSchema2(
|
|
59710
|
+
await runSchema2({
|
|
59711
|
+
...ctx,
|
|
59712
|
+
options: {
|
|
59713
|
+
...ctx.options,
|
|
59714
|
+
model: options.model,
|
|
59715
|
+
domain: options.domain,
|
|
59716
|
+
compact: options.compact,
|
|
59717
|
+
full: options.full
|
|
59718
|
+
}
|
|
59719
|
+
});
|
|
59089
59720
|
});
|
|
59090
59721
|
}
|
|
59091
59722
|
|