@anatolykoptev/krolik-cli 0.10.0 → 0.11.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/README.md CHANGED
@@ -39,12 +39,14 @@ One command replaces 10+ manual searches. AI gets complete project context insta
39
39
  ```bash
40
40
  krolik context --feature auth # Everything about auth feature
41
41
  krolik context --issue 42 # Context from GitHub issue
42
+ krolik context --search "tRPC" # Find code matching pattern
43
+ krolik context --changed-only # Only git-changed files
42
44
  krolik context --minimal # Ultra-compact (~1500 tokens)
43
45
  krolik context --quick # Compact with repo-map (~3500 tokens)
44
46
  krolik context --deep # Full analysis (~5s)
45
47
  ```
46
48
 
47
- **What it collects**: git state, database schema, API routes, project structure, types, past decisions, library docs.
49
+ **What it collects**: git state, database schema, API routes, project structure, types, past decisions, library docs, lib modules with signatures.
48
50
 
49
51
  ---
50
52
 
@@ -69,7 +71,10 @@ Memories are automatically included in context — AI sees what was decided befo
69
71
  Analyzes entire codebase and creates a prioritized report.
70
72
 
71
73
  ```bash
72
- krolik audit
74
+ krolik audit # All issues
75
+ krolik audit --mode release # Security + type-safety
76
+ krolik audit --mode queries # Duplicate Prisma/tRPC queries
77
+ krolik audit --mode pre-commit # Quick check before commit
73
78
  ```
74
79
 
75
80
  **Output**: `.krolik/AI-REPORT.md` with issues ranked by severity, files with most problems, and quick wins that can be auto-fixed.
@@ -133,6 +138,21 @@ krolik docs list # List cached libraries
133
138
 
134
139
  ---
135
140
 
141
+ ### `krolik modules` — Query Lib Modules
142
+
143
+ Discover reusable utilities in your codebase before writing new code.
144
+
145
+ ```bash
146
+ krolik modules # List all lib modules
147
+ krolik modules --search parse # Search exports by name
148
+ krolik modules --get fs # Details of specific module
149
+ krolik modules --paths # Show where modules are found
150
+ ```
151
+
152
+ **What it shows**: functions with signatures, types, constants — everything exported from `lib/@*` directories.
153
+
154
+ ---
155
+
136
156
  ### Other Commands
137
157
 
138
158
  | Command | What it does |
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.10.0";
59
+ KROLIK_VERSION = "0.11.0";
60
60
  TEMPLATE_VERSION = "6.1.0";
61
61
  }
62
62
  });
@@ -13325,18 +13325,38 @@ var init_schema2 = __esm({
13325
13325
  init_esm_shims();
13326
13326
  init_core();
13327
13327
  schemaFlagSchema = {
13328
- json: COMMON_FLAGS.json
13328
+ json: COMMON_FLAGS.json,
13329
+ model: { flag: "--model" },
13330
+ domain: { flag: "--domain" },
13331
+ compact: { flag: "--compact" }
13329
13332
  };
13330
13333
  schemaTool = {
13331
13334
  name: "krolik_schema",
13332
- description: "Analyze Prisma database schema. Returns all models, fields, relations, and enums.",
13335
+ description: `Analyze Prisma database schema. Returns models, fields, relations, and enums.
13336
+
13337
+ **For large schemas**, use compact mode or filters to reduce output:
13338
+ - compact: true - shows only model names with relations (no field details)
13339
+ - model: "User" - filter by model name (partial match)
13340
+ - domain: "Auth" - filter by domain (derived from schema filename)`,
13333
13341
  inputSchema: {
13334
13342
  type: "object",
13335
13343
  properties: {
13336
13344
  ...PROJECT_PROPERTY,
13337
13345
  json: {
13338
13346
  type: "boolean",
13339
- description: "Return JSON format instead of markdown"
13347
+ description: "Return JSON format instead of XML"
13348
+ },
13349
+ model: {
13350
+ type: "string",
13351
+ description: 'Filter by model name (partial match, case-insensitive). Example: "User", "Booking"'
13352
+ },
13353
+ domain: {
13354
+ type: "string",
13355
+ description: 'Filter by domain (derived from schema filename). Example: "Auth", "Bookings", "CRM"'
13356
+ },
13357
+ compact: {
13358
+ type: "boolean",
13359
+ description: "Compact output - models with relations only, no field details. Recommended for large schemas."
13340
13360
  }
13341
13361
  }
13342
13362
  },
@@ -39602,34 +39622,24 @@ function groupByDomain3(models) {
39602
39622
  return groupBy(models, (model) => inferDomain(model.file));
39603
39623
  }
39604
39624
  function inferDomain(filename) {
39605
- const base = filename.replace(".prisma", "").toLowerCase();
39606
- for (const [key, label] of Object.entries(DEFAULT_DOMAINS)) {
39607
- if (base.includes(key)) {
39608
- return label;
39609
- }
39610
- }
39611
- return "Other";
39625
+ const base = filename.replace(/\.prisma$/, "").replace(/^.*\//, "");
39626
+ if (!base) return "Other";
39627
+ const parts = base.split(/[-_]/);
39628
+ const formatted = parts.map((part) => {
39629
+ const lower = part.toLowerCase();
39630
+ if (UPPERCASE_ABBREVS.includes(lower)) {
39631
+ return lower.toUpperCase();
39632
+ }
39633
+ return part.charAt(0).toUpperCase() + part.slice(1).toLowerCase();
39634
+ });
39635
+ return formatted.join(" ");
39612
39636
  }
39613
- var DEFAULT_DOMAINS;
39637
+ var UPPERCASE_ABBREVS;
39614
39638
  var init_grouping4 = __esm({
39615
39639
  "src/commands/schema/grouping.ts"() {
39616
39640
  init_esm_shims();
39617
39641
  init_core2();
39618
- DEFAULT_DOMAINS = {
39619
- auth: "Authentication",
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
- };
39642
+ UPPERCASE_ABBREVS = ["crm", "api", "ugc", "sso", "oauth", "jwt", "sql"];
39633
39643
  }
39634
39644
  });
39635
39645
 
@@ -39756,6 +39766,60 @@ function formatMarkdown3(data) {
39756
39766
  lines.push("*Generated by krolik-cli*");
39757
39767
  return lines.join("\n");
39758
39768
  }
39769
+ function formatCompact(data, fullData) {
39770
+ const lines = [];
39771
+ const total = fullData ?? data;
39772
+ const isFiltered = fullData && data.modelCount !== fullData.modelCount;
39773
+ lines.push('<prisma-schema mode="compact">');
39774
+ if (isFiltered) {
39775
+ lines.push(
39776
+ ` <stats models="${data.modelCount}" enums="${data.enumCount}" filtered="true" total-models="${total.modelCount}" total-enums="${total.enumCount}" />`
39777
+ );
39778
+ } else {
39779
+ lines.push(` <stats models="${data.modelCount}" enums="${data.enumCount}" />`);
39780
+ }
39781
+ const byDomain = groupByDomain3(data.models);
39782
+ const domains = Array.from(byDomain.keys()).sort();
39783
+ lines.push(` <domains>${domains.join(", ")}</domains>`);
39784
+ lines.push("");
39785
+ for (const [domain, models] of byDomain) {
39786
+ const modelCount = models.length;
39787
+ lines.push(` <domain name="${domain}" models="${modelCount}">`);
39788
+ for (const model of models) {
39789
+ const relStr = model.relations.length > 0 ? ` relations="${model.relations.join(", ")}"` : "";
39790
+ const keyFields = model.fields.filter((f) => f.isId || f.isUnique || f.name.endsWith("Id"));
39791
+ const keyFieldNames = keyFields.map((f) => f.name).slice(0, 5);
39792
+ const keyStr = keyFieldNames.length > 0 ? ` keys="${keyFieldNames.join(", ")}"` : "";
39793
+ lines.push(
39794
+ ` <model name="${model.name}" fields="${model.fields.length}"${relStr}${keyStr} />`
39795
+ );
39796
+ }
39797
+ lines.push(" </domain>");
39798
+ }
39799
+ if (data.enums.length > 0) {
39800
+ lines.push("");
39801
+ lines.push(` <enums count="${data.enums.length}">`);
39802
+ const enumNames = data.enums.map((e) => e.name).sort();
39803
+ if (enumNames.length > 20) {
39804
+ const byPrefix = /* @__PURE__ */ new Map();
39805
+ for (const name of enumNames) {
39806
+ const prefix = name.replace(/[A-Z][a-z]+$/, "").replace(/Status$|Type$|Role$|State$/, "") || "General";
39807
+ if (!byPrefix.has(prefix)) byPrefix.set(prefix, []);
39808
+ byPrefix.get(prefix).push(name);
39809
+ }
39810
+ for (const [prefix, names] of byPrefix) {
39811
+ lines.push(` <group prefix="${prefix}">${names.join(", ")}</group>`);
39812
+ }
39813
+ } else {
39814
+ lines.push(` ${enumNames.join(", ")}`);
39815
+ }
39816
+ lines.push(" </enums>");
39817
+ }
39818
+ lines.push("");
39819
+ lines.push(' <hint>Use --model "Name" or --domain "Domain" for details</hint>');
39820
+ lines.push("</prisma-schema>");
39821
+ return lines.join("\n");
39822
+ }
39759
39823
  var init_output7 = __esm({
39760
39824
  "src/commands/schema/output.ts"() {
39761
39825
  init_esm_shims();
@@ -39800,6 +39864,29 @@ function findSchemaDir2(projectRoot, configSchemaDir) {
39800
39864
  }
39801
39865
  return null;
39802
39866
  }
39867
+ function filterSchema(result, options) {
39868
+ let { models, enums } = result;
39869
+ if (options.domain) {
39870
+ const domainLower = options.domain.toLowerCase();
39871
+ const byDomain = groupByDomain3(models);
39872
+ const matchingDomain = Array.from(byDomain.keys()).find((d) => d.toLowerCase() === domainLower);
39873
+ if (matchingDomain) {
39874
+ models = byDomain.get(matchingDomain) ?? [];
39875
+ } else {
39876
+ models = [];
39877
+ }
39878
+ }
39879
+ if (options.model) {
39880
+ const modelLower = options.model.toLowerCase();
39881
+ models = models.filter((m) => m.name.toLowerCase().includes(modelLower));
39882
+ }
39883
+ return {
39884
+ models,
39885
+ enums,
39886
+ modelCount: models.length,
39887
+ enumCount: enums.length
39888
+ };
39889
+ }
39803
39890
  async function runSchema(ctx) {
39804
39891
  const { config, logger: logger2, options } = ctx;
39805
39892
  const schemaDir = findSchemaDir2(config.projectRoot, config.prisma?.schemaDir);
@@ -39808,9 +39895,11 @@ async function runSchema(ctx) {
39808
39895
  logger2.info("Checked: prisma, packages/db/prisma, src/prisma, db/prisma");
39809
39896
  return;
39810
39897
  }
39811
- const result = analyzeSchema(schemaDir);
39898
+ const fullResult = analyzeSchema(schemaDir);
39899
+ const hasFilters = options.model || options.domain;
39900
+ const result = hasFilters ? filterSchema(fullResult, options) : fullResult;
39812
39901
  const format2 = options.format ?? "ai";
39813
- const xmlOutput = formatAI3(result);
39902
+ const xmlOutput = formatAI3(fullResult);
39814
39903
  saveKrolikFile(config.projectRoot, "SCHEMA.xml", xmlOutput);
39815
39904
  if (options.save) {
39816
39905
  const md = formatMarkdown3(result);
@@ -39831,12 +39920,17 @@ async function runSchema(ctx) {
39831
39920
  printSchema(result, logger2, options.groupBy ?? "file");
39832
39921
  return;
39833
39922
  }
39834
- console.log(xmlOutput);
39923
+ if (options.compact) {
39924
+ console.log(formatCompact(result, fullResult));
39925
+ return;
39926
+ }
39927
+ console.log(hasFilters ? formatAI3(result) : xmlOutput);
39835
39928
  }
39836
39929
  var init_schema3 = __esm({
39837
39930
  "src/commands/schema/index.ts"() {
39838
39931
  init_esm_shims();
39839
39932
  init_fs2();
39933
+ init_grouping4();
39840
39934
  init_output7();
39841
39935
  init_parser2();
39842
39936
  }
@@ -59082,10 +59176,27 @@ function registerRoutesCommand(program) {
59082
59176
  // src/cli/commands/schema.ts
59083
59177
  init_esm_shims();
59084
59178
  function registerSchemaCommand(program) {
59085
- program.command("schema").description("Analyze Prisma schema").option("--save", "Save to SCHEMA.md").action(async (options) => {
59179
+ 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)").addHelpText(
59180
+ "after",
59181
+ `
59182
+ Examples:
59183
+ krolik schema # Full schema (may be large)
59184
+ krolik schema --compact # Compact view - just models and relations
59185
+ krolik schema --domain Bookings # Only Bookings domain
59186
+ krolik schema --model User # Only models containing "User"
59187
+ `
59188
+ ).action(async (options) => {
59086
59189
  const { runSchema: runSchema2 } = await Promise.resolve().then(() => (init_schema3(), schema_exports));
59087
59190
  const ctx = await createContext3(program, options);
59088
- await runSchema2(ctx);
59191
+ await runSchema2({
59192
+ ...ctx,
59193
+ options: {
59194
+ ...ctx.options,
59195
+ model: options.model,
59196
+ domain: options.domain,
59197
+ compact: options.compact
59198
+ }
59199
+ });
59089
59200
  });
59090
59201
  }
59091
59202