llmstxt-cli 0.2.0 → 0.4.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.
Files changed (2) hide show
  1. package/dist/index.js +399 -847
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -23,23 +23,23 @@ function error(message) {
23
23
  function spinner2(_text) {
24
24
  const s = p.spinner();
25
25
  return {
26
- start(text) {
27
- s.start(text || _text || "");
26
+ start(text2) {
27
+ s.start(text2 || _text || "");
28
28
  },
29
- stop(text) {
30
- s.stop(text || "");
29
+ stop(text2) {
30
+ s.stop(text2 || "");
31
31
  },
32
- succeed(text) {
33
- s.stop(`${pc.green("\u2713")} ${text}`);
32
+ succeed(text2) {
33
+ s.stop(`${pc.green("\u2713")} ${text2}`);
34
34
  },
35
- fail(text) {
36
- s.stop(`${pc.red("\u2717")} ${text}`);
35
+ fail(text2) {
36
+ s.stop(`${pc.red("\u2717")} ${text2}`);
37
37
  },
38
- info(text) {
39
- s.stop(`${pc.blue("\u2139")} ${text}`);
38
+ info(text2) {
39
+ s.stop(`${pc.blue("\u2139")} ${text2}`);
40
40
  },
41
- warn(text) {
42
- s.stop(`${pc.yellow("\u26A0")} ${text}`);
41
+ warn(text2) {
42
+ s.stop(`${pc.yellow("\u26A0")} ${text2}`);
43
43
  }
44
44
  };
45
45
  }
@@ -6390,6 +6390,9 @@ function searchRegistry(query, categories) {
6390
6390
  function getEntry2(slug) {
6391
6391
  return entries.find((e) => e.slug === slug);
6392
6392
  }
6393
+ function getAllEntries(categories) {
6394
+ return categories ? filterByCategories(entries, categories) : entries;
6395
+ }
6393
6396
  function resolveSlug(nameOrSlug) {
6394
6397
  const exact = entries.find((e) => e.slug === nameOrSlug);
6395
6398
  if (exact) return exact;
@@ -6401,7 +6404,7 @@ function resolveSlug(nameOrSlug) {
6401
6404
 
6402
6405
  // src/lib/storage.ts
6403
6406
  import { createHash } from "crypto";
6404
- import { appendFileSync, existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync3, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
6407
+ import { existsSync as existsSync3, mkdirSync as mkdirSync4, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
6405
6408
  import { join as join4 } from "path";
6406
6409
 
6407
6410
  // src/lib/agents.ts
@@ -6457,7 +6460,7 @@ var agents = [
6457
6460
  displayName: "Antigravity",
6458
6461
  skillsDir: ".agent/skills",
6459
6462
  isUniversal: false,
6460
- detectInstalled: () => existsSync2(join3(process.cwd(), ".agent")) || existsSync2(join3(home, ".gemini/antigravity"))
6463
+ detectInstalled: () => existsSync2(join3(home, ".gemini/antigravity"))
6461
6464
  },
6462
6465
  {
6463
6466
  name: "augment",
@@ -6485,7 +6488,7 @@ var agents = [
6485
6488
  displayName: "CodeBuddy",
6486
6489
  skillsDir: ".codebuddy/skills",
6487
6490
  isUniversal: false,
6488
- detectInstalled: () => existsSync2(join3(process.cwd(), ".codebuddy")) || existsSync2(join3(home, ".codebuddy"))
6491
+ detectInstalled: () => existsSync2(join3(home, ".codebuddy"))
6489
6492
  },
6490
6493
  {
6491
6494
  name: "codex",
@@ -6506,7 +6509,7 @@ var agents = [
6506
6509
  displayName: "Continue",
6507
6510
  skillsDir: ".continue/skills",
6508
6511
  isUniversal: false,
6509
- detectInstalled: () => existsSync2(join3(process.cwd(), ".continue")) || existsSync2(join3(home, ".continue"))
6512
+ detectInstalled: () => existsSync2(join3(home, ".continue"))
6510
6513
  },
6511
6514
  {
6512
6515
  name: "crush",
@@ -6541,7 +6544,7 @@ var agents = [
6541
6544
  displayName: "GitHub Copilot",
6542
6545
  skillsDir: ".agents/skills",
6543
6546
  isUniversal: true,
6544
- detectInstalled: () => existsSync2(join3(process.cwd(), ".github")) || existsSync2(join3(home, ".copilot"))
6547
+ detectInstalled: () => existsSync2(join3(home, ".copilot"))
6545
6548
  },
6546
6549
  {
6547
6550
  name: "goose",
@@ -6632,7 +6635,7 @@ var agents = [
6632
6635
  displayName: "OpenCode",
6633
6636
  skillsDir: ".agents/skills",
6634
6637
  isUniversal: true,
6635
- detectInstalled: () => existsSync2(join3(configHome, "opencode")) || existsSync2(join3(claudeHome, "skills"))
6638
+ detectInstalled: () => existsSync2(join3(configHome, "opencode"))
6636
6639
  },
6637
6640
  {
6638
6641
  name: "openhands",
@@ -6753,15 +6756,6 @@ function removeAgentSkill({ projectDir, slug, agent }) {
6753
6756
  } catch {
6754
6757
  }
6755
6758
  }
6756
- function getGitignoreEntries() {
6757
- const dirs = /* @__PURE__ */ new Set([`${CANONICAL_DIR}/`]);
6758
- for (const agent of agents) {
6759
- if (!agent.isUniversal) {
6760
- dirs.add(`${agent.skillsDir}/`);
6761
- }
6762
- }
6763
- return [...dirs].sort();
6764
- }
6765
6759
 
6766
6760
  // src/lib/storage.ts
6767
6761
  var LLMS_DIR = ".llms";
@@ -6812,7 +6806,8 @@ function installToAgents({
6812
6806
  slug,
6813
6807
  entry,
6814
6808
  content,
6815
- format
6809
+ format,
6810
+ targetAgents
6816
6811
  }) {
6817
6812
  const checksum = createHash("sha256").update(content).digest("hex");
6818
6813
  const size = Buffer.byteLength(content, "utf-8");
@@ -6827,8 +6822,8 @@ function installToAgents({
6827
6822
  writeFileSync3(join4(canonicalDir, "reference.md"), referenceMd, "utf-8");
6828
6823
  }
6829
6824
  installedAgents.push("universal");
6830
- const detectedAgents = detectInstalledAgents();
6831
- for (const agent of detectedAgents) {
6825
+ const agentsToLink = targetAgents ?? detectInstalledAgents();
6826
+ for (const agent of agentsToLink) {
6832
6827
  if (agent.isUniversal) {
6833
6828
  if (!installedAgents.includes(agent.name)) {
6834
6829
  installedAgents.push(agent.name);
@@ -6858,30 +6853,6 @@ function isInstalled({ projectDir, slug }) {
6858
6853
  sanitizeSlug(slug);
6859
6854
  return existsSync3(join4(projectDir, CANONICAL_DIR, slug, "SKILL.md"));
6860
6855
  }
6861
- function addToGitignore(projectDir) {
6862
- try {
6863
- const gitignorePath = join4(projectDir, ".gitignore");
6864
- const gitignoreEntries = [".llms/", ...getGitignoreEntries()];
6865
- if (existsSync3(gitignorePath)) {
6866
- const content = readFileSync3(gitignorePath, "utf-8");
6867
- const missing = gitignoreEntries.filter((e) => !content.includes(e));
6868
- if (missing.length === 0) return false;
6869
- appendFileSync(gitignorePath, `
6870
- # llms.txt documentation
6871
- ${missing.join("\n")}
6872
- `);
6873
- } else {
6874
- writeFileSync3(gitignorePath, `# llms.txt documentation
6875
- ${gitignoreEntries.join("\n")}
6876
- `);
6877
- }
6878
- return true;
6879
- } catch (err) {
6880
- const msg = err instanceof Error ? err.message : String(err);
6881
- console.warn(`Could not update .gitignore: ${msg}`);
6882
- return false;
6883
- }
6884
- }
6885
6856
 
6886
6857
  // src/commands/info.ts
6887
6858
  async function info(name) {
@@ -6934,6 +6905,68 @@ ${pc2.dim("Full:")} ${entry.llmsFullTxtUrl}` : ""}`
6934
6905
  import * as p3 from "@clack/prompts";
6935
6906
  import pc4 from "picocolors";
6936
6907
 
6908
+ // src/lib/agent-selection.ts
6909
+ import { existsSync as existsSync4, mkdirSync as mkdirSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
6910
+ import { join as join5 } from "path";
6911
+ var PREFS_DIR = ".llms";
6912
+ var PREFS_FILE = "agent-prefs.json";
6913
+ var DEFAULT_AGENTS = ["claude-code", "cursor", "codex"];
6914
+ function detectProjectAgents(opts) {
6915
+ const detected = [];
6916
+ for (const agent of opts.allAgents) {
6917
+ if (agent.isUniversal) continue;
6918
+ const configDir = agent.skillsDir.split("/")[0];
6919
+ if (configDir && configDir !== "skills" && existsSync4(join5(opts.projectDir, configDir))) {
6920
+ detected.push(agent.name);
6921
+ }
6922
+ }
6923
+ return detected;
6924
+ }
6925
+ function getInitialAgents(opts) {
6926
+ const validNames = new Set(opts.allAgents.map((a) => a.name));
6927
+ if (opts.savedPrefs && opts.savedPrefs.length > 0) {
6928
+ const filtered = opts.savedPrefs.filter((name) => validNames.has(name));
6929
+ if (filtered.length > 0) return filtered;
6930
+ }
6931
+ if (opts.projectDir) {
6932
+ const detected = detectProjectAgents({ projectDir: opts.projectDir, allAgents: opts.allAgents });
6933
+ if (detected.length > 0) return detected;
6934
+ }
6935
+ return DEFAULT_AGENTS.filter((name) => validNames.has(name));
6936
+ }
6937
+ function ensureUniversalAgents(opts) {
6938
+ const result = [...opts.selected];
6939
+ for (const agent of opts.allAgents) {
6940
+ if (agent.isUniversal && !result.includes(agent.name)) {
6941
+ result.push(agent.name);
6942
+ }
6943
+ }
6944
+ return result;
6945
+ }
6946
+ function loadSavedAgentPrefs(projectDir) {
6947
+ try {
6948
+ const filePath = join5(projectDir, PREFS_DIR, PREFS_FILE);
6949
+ if (!existsSync4(filePath)) return null;
6950
+ const data = JSON.parse(readFileSync4(filePath, "utf-8"));
6951
+ if (Array.isArray(data.agents)) return data.agents;
6952
+ return null;
6953
+ } catch {
6954
+ return null;
6955
+ }
6956
+ }
6957
+ function saveAgentPrefs(projectDir, agentNames) {
6958
+ try {
6959
+ const dir = join5(projectDir, PREFS_DIR);
6960
+ mkdirSync5(dir, { recursive: true });
6961
+ writeFileSync4(
6962
+ join5(dir, PREFS_FILE),
6963
+ JSON.stringify({ agents: agentNames }, null, 2) + "\n",
6964
+ "utf-8"
6965
+ );
6966
+ } catch {
6967
+ }
6968
+ }
6969
+
6937
6970
  // src/lib/banner.ts
6938
6971
  import pc3 from "picocolors";
6939
6972
  var BANNER_LINES = [
@@ -6959,18 +6992,18 @@ ${pc3.dim(` v${version} \xB7 Install llms.txt documentation for AI coding tools
6959
6992
  }
6960
6993
 
6961
6994
  // src/lib/context.ts
6962
- import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
6963
- import { join as join5 } from "path";
6995
+ import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
6996
+ import { join as join6 } from "path";
6964
6997
  var START_MARKER = "<!-- llmstxt:start -->";
6965
6998
  var END_MARKER = "<!-- llmstxt:end -->";
6966
6999
  function syncClaudeMd(projectDir) {
6967
7000
  try {
6968
7001
  const lockfile = readLockfile(projectDir);
6969
7002
  const entries2 = Object.values(lockfile.entries);
6970
- const claudeMdPath = join5(projectDir, "CLAUDE.md");
7003
+ const claudeMdPath = join6(projectDir, "CLAUDE.md");
6971
7004
  let content = "";
6972
- if (existsSync4(claudeMdPath)) {
6973
- content = readFileSync4(claudeMdPath, "utf-8");
7005
+ if (existsSync5(claudeMdPath)) {
7006
+ content = readFileSync5(claudeMdPath, "utf-8");
6974
7007
  }
6975
7008
  const section = buildSection(entries2);
6976
7009
  if (content.includes(START_MARKER)) {
@@ -6993,12 +7026,12 @@ function syncClaudeMd(projectDir) {
6993
7026
  new RegExp(`\\n?${escapeRegex(START_MARKER)}[\\s\\S]*?${escapeRegex(END_MARKER)}\\n?`),
6994
7027
  ""
6995
7028
  ).trim();
6996
- if (content.length === 0 && !existsSync4(claudeMdPath)) {
7029
+ if (content.length === 0 && !existsSync5(claudeMdPath)) {
6997
7030
  return;
6998
7031
  }
6999
7032
  }
7000
- if (content.length > 0 || existsSync4(claudeMdPath)) {
7001
- writeFileSync4(claudeMdPath, content.endsWith("\n") ? content : `${content}
7033
+ if (content.length > 0 || existsSync5(claudeMdPath)) {
7034
+ writeFileSync5(claudeMdPath, content.endsWith("\n") ? content : `${content}
7002
7035
  `, "utf-8");
7003
7036
  }
7004
7037
  } catch (err) {
@@ -7026,701 +7059,76 @@ function escapeRegex(s) {
7026
7059
  }
7027
7060
 
7028
7061
  // src/lib/detector.ts
7029
- import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
7030
- import { join as join6 } from "path";
7031
-
7032
- // data/package-mappings.json
7033
- var package_mappings_default = {
7034
- version: 1,
7035
- npm: {
7036
- agent: "agent",
7037
- "02aidev": "02aidev",
7038
- "abstract-api": "abstract-api",
7039
- aceessayai: "aceessayai",
7040
- acrcloud: "acrcloud",
7041
- activepieces: "activepieces",
7042
- adiacent: "adiacent",
7043
- "advin-servers": "advin-servers",
7044
- adx: "adx",
7045
- "affordable-nvme-ssd-web-hosting-pakistan": "affordable-nvme-ssd-web-hosting-pakistan",
7046
- aftermath: "aftermath",
7047
- agentai: "agentai",
7048
- agentlayer: "agentlayer",
7049
- agentql: "agentql",
7050
- agno: "agno",
7051
- "ai-consulting": "ai-consulting",
7052
- "ai-dream-scope": "ai-dream-scope",
7053
- "ai-predict-stock": "ai-predict-stock",
7054
- "ai-sdk": "ai-sdk",
7055
- "ai-squared": "ai-squared",
7056
- aicryptocore: "aicryptocore",
7057
- akool: "akool",
7058
- alexopdev: "alexopdev",
7059
- "all-in": "all-in",
7060
- "alpha-fi": "alpha-fi",
7061
- alphagate: "alphagate",
7062
- altostrat: "altostrat",
7063
- alva: "alva",
7064
- amema: "amema",
7065
- amplemarket: "amplemarket",
7066
- analog: "analog",
7067
- annoto: "annoto",
7068
- answerai: "answerai",
7069
- "ant-design": "ant-design",
7070
- anthropic: "anthropic",
7071
- anytrack: "anytrack",
7072
- anytype: "anytype",
7073
- "apex-protocol": "apex-protocol",
7074
- apibara: "apibara",
7075
- apify: "apify",
7076
- aporia: "aporia",
7077
- appointo: "appointo",
7078
- appzung: "appzung",
7079
- "aps-sdks-openapi-specification-aps": "aps-sdks-openapi-specification-aps",
7080
- aptible: "aptible",
7081
- arbiscan: "arbiscan",
7082
- arcade: "arcade",
7083
- "argil-ai": "argil-ai",
7084
- "ark-ui": "ark-ui",
7085
- arpeggi: "arpeggi",
7086
- artzero: "artzero",
7087
- asapp: "asapp",
7088
- "asposewords-product-family": "asposewords-product-family",
7089
- "ast-grep": "ast-grep",
7090
- astro: "astro",
7091
- "augment-code": "augment-code",
7092
- autentique: "autentique",
7093
- avaamo: "avaamo",
7094
- avacloud: "avacloud",
7095
- "avada-seo-suite": "avada-seo-suite",
7096
- "avalanche-hvac-services-heating-air": "avalanche-hvac-services-heating-air",
7097
- avo: "avo",
7098
- awesomeapi: "awesomeapi",
7099
- axiom: "axiom",
7100
- axle: "axle",
7101
- azumuta: "azumuta",
7102
- basehub: "basehub",
7103
- beacon: "beacon",
7104
- "best-app-development-company": "best-app-development-company",
7105
- "best-boat-lifts": "best-boat-lifts",
7106
- "beta-three-professional-audio": "beta-three-professional-audio",
7107
- "better-auth": "better-auth",
7108
- beyondwords: "beyondwords",
7109
- bika: "bika",
7110
- birdie: "birdie",
7111
- "bitcoin-cold-storage-tools": "bitcoin-cold-storage-tools",
7112
- bito: "bito",
7113
- "bits-ui": "bits-ui",
7114
- bixgrow: "bixgrow",
7115
- blacknet: "blacknet",
7116
- "blue-media-services": "blue-media-services",
7117
- blueshift: "blueshift",
7118
- booqable: "booqable",
7119
- bottalk: "bottalk",
7120
- braintrust: "braintrust",
7121
- brandfetch: "brandfetch",
7122
- "bright-data": "bright-data",
7123
- browsee: "browsee",
7124
- bucket: "bucket",
7125
- bugsplat: "bugsplat",
7126
- builtwith: "builtwith",
7127
- bun: "bun",
7128
- bunny: "bunny",
7129
- butlr: "butlr",
7130
- caf: "caf",
7131
- calcom: "calcom",
7132
- calendarscripts: "calendarscripts",
7133
- "callback-technologies": "callback-technologies",
7134
- campsite: "campsite",
7135
- captivaterecipes: "captivaterecipes",
7136
- "car-rental-in-airports-ofgeorgia": "car-rental-in-airports-ofgeorgia",
7137
- cardknox: "cardknox",
7138
- carto: "carto",
7139
- carv: "carv",
7140
- catizen: "catizen",
7141
- "cdata-software": "cdata-software",
7142
- "chakra-ui": "chakra-ui",
7143
- chargeblast: "chargeblast",
7144
- chartbeat: "chartbeat",
7145
- chatbase: "chatbase",
7146
- chatknow: "chatknow",
7147
- chatling: "chatling",
7148
- "checkmate-live": "checkmate-live",
7149
- "chic-room": "chic-room",
7150
- "chirp-wireless": "chirp-wireless",
7151
- "cid-contact": "cid-contact",
7152
- cipherstash: "cipherstash",
7153
- circleci: "circleci",
7154
- citizenshipper: "citizenshipper",
7155
- citrusad: "citrusad",
7156
- civic: "civic",
7157
- "claude-mcp-community": "claude-mcp-community",
7158
- clerk: "clerk",
7159
- "clever-cloud": "clever-cloud",
7160
- cloreai: "cloreai",
7161
- cloudfix: "cloudfix",
7162
- cloudflare: "cloudflare",
7163
- clouve: "clouve",
7164
- cloverspace: "cloverspace",
7165
- "cnpjws-api": "cnpjws-api",
7166
- cobo: "cobo",
7167
- cobrowse: "cobrowse",
7168
- codecrafters: "codecrafters",
7169
- codeium: "codeium",
7170
- coefont: "coefont",
7171
- coframe: "coframe",
7172
- cog: "cog",
7173
- cohere: "cohere",
7174
- comforterp: "comforterp",
7175
- comforthrm: "comforthrm",
7176
- comfy: "comfy",
7177
- "commerce-layer": "commerce-layer",
7178
- "common-ninja": "common-ninja",
7179
- "community-helm-charts": "community-helm-charts",
7180
- comparisonator: "comparisonator",
7181
- "concept-designs-marketing": "concept-designs-marketing",
7182
- conductor: "conductor",
7183
- configcat: "configcat",
7184
- consentik: "consentik",
7185
- contentsquare: "contentsquare",
7186
- "contractor-marketing-agency": "contractor-marketing-agency",
7187
- contravault: "contravault",
7188
- convex: "convex",
7189
- coolify: "coolify",
7190
- coollabs: "coollabs",
7191
- "copenhagen-art-and-photography": "copenhagen-art-and-photography",
7192
- "corso-seo-online-certificato": "corso-seo-online-certificato",
7193
- cosmic: "cosmic",
7194
- couchdrop: "couchdrop",
7195
- count: "count",
7196
- courier: "courier",
7197
- craftco: "craftco",
7198
- "cre-data-and-location-intelligence-app": "cre-data-and-location-intelligence-app",
7199
- "creative-handbook-production-resource-directory": "creative-handbook-production-resource-directory",
7200
- creatopy: "creatopy",
7201
- crewai: "crewai",
7202
- "cross-browser-mobile-app-testing": "cross-browser-mobile-app-testing",
7203
- cryptlex: "cryptlex",
7204
- "curling-io": "curling-io",
7205
- cursor: "cursor",
7206
- "customer-service-ai-platform": "customer-service-ai-platform",
7207
- "cyber-security-news": "cyber-security-news",
7208
- daisyui: "daisyui",
7209
- dappier: "dappier",
7210
- dat1co: "dat1co",
7211
- "data-surfer": "data-surfer",
7212
- "databuddy-analytics": "databuddy-analytics",
7213
- datacamp: "datacamp",
7214
- datafold: "datafold",
7215
- dataimpulse: "dataimpulse",
7216
- "datastax-astra-db": "datastax-astra-db",
7217
- daytona: "daytona",
7218
- decocx: "decocx",
7219
- deepconverse: "deepconverse",
7220
- "demo-time": "demo-time",
7221
- "deno-documentation": "deno-documentation",
7222
- deployhq: "deployhq",
7223
- designmodo: "designmodo",
7224
- devhub: "devhub",
7225
- devunus: "devunus",
7226
- "dexpaprika-api-live-crypto-pool-data": "dexpaprika-api-live-crypto-pool-data",
7227
- dns0eu: "dns0eu",
7228
- docetl: "docetl",
7229
- "docker-docs": "docker-docs",
7230
- docspring: "docspring",
7231
- "document-file-translation-powered-by-ai": "document-file-translation-powered-by-ai",
7232
- donobu: "donobu",
7233
- "dopp-finance": "dopp-finance",
7234
- dotenvx: "dotenvx",
7235
- "drizzle-orm": "drizzle-orm",
7236
- dub: "dub",
7237
- duckdb: "duckdb",
7238
- "duende-software": "duende-software",
7239
- dynamic: "dynamic",
7240
- e6data: "e6data",
7241
- effect: "effect",
7242
- elevenlabs: "elevenlabs",
7243
- elysia: "elysia",
7244
- emailgic: "emailgic",
7245
- "embed-notion-pages-into-your-website": "embed-notion-pages-into-your-website",
7246
- embedchain: "embedchain",
7247
- envoyer: "envoyer",
7248
- equipment: "equipment",
7249
- essio: "essio",
7250
- "expo-documentation": "expo-documentation",
7251
- fabric: "fabric",
7252
- facets: "facets",
7253
- "fantasy-sports-app-development-company": "fantasy-sports-app-development-company",
7254
- "farcaster-miniapps": "farcaster-miniapps",
7255
- "feedback-sync": "feedback-sync",
7256
- fern: "fern",
7257
- fibery: "fibery",
7258
- "film-and-screenwriter-school": "film-and-screenwriter-school",
7259
- finch: "finch",
7260
- fireproof: "fireproof",
7261
- "fireworks-ai": "fireworks-ai",
7262
- firmly: "firmly",
7263
- flatfile: "flatfile",
7264
- flipto: "flipto",
7265
- flows: "flows",
7266
- flowx: "flowx",
7267
- "fork-force-australia": "fork-force-australia",
7268
- formo: "formo",
7269
- formwerk: "formwerk",
7270
- fractalpay: "fractalpay",
7271
- "fraction-ai": "fraction-ai",
7272
- fragbin: "fragbin",
7273
- "frank-krav-maga-self-defense-bengaluru": "frank-krav-maga-self-defense-bengaluru",
7274
- "free-ai-image-generator-maker": "free-ai-image-generator-maker",
7275
- frigade: "frigade",
7276
- "front-matter-cms-a-cms-in-your-vs-code-editor": "front-matter-cms-a-cms-in-your-vs-code-editor",
7277
- "funny-stories-of-horrible-stories": "funny-stories-of-horrible-stories",
7278
- galileo: "galileo",
7279
- "game-development-hub": "game-development-hub",
7280
- gaminator: "gaminator",
7281
- "gaslighting-check": "gaslighting-check",
7282
- genaiscript: "genaiscript",
7283
- "generate-ai-ugc-videos-for-ads": "generate-ai-ugc-videos-for-ads",
7284
- getinbox: "getinbox",
7285
- getmarked: "getmarked",
7286
- giselle: "giselle",
7287
- glama: "glama",
7288
- gloodai: "gloodai",
7289
- glucncom: "glucncom",
7290
- "gnosis-chain": "gnosis-chain",
7291
- goody: "goody",
7292
- gradio: "gradio",
7293
- "graphite-note": "graphite-note",
7294
- "greece-vacation-search": "greece-vacation-search",
7295
- "green-apple-travel-tourism": "green-apple-travel-tourism",
7296
- groqcloud: "groqcloud",
7297
- gumlet: "gumlet",
7298
- helicone: "helicone",
7299
- "help-for-erectile-dysfunction": "help-for-erectile-dysfunction",
7300
- "henan-pengfei-pvp-k30-povidone": "henan-pengfei-pvp-k30-povidone",
7301
- herd: "herd",
7302
- "heritage-collection": "heritage-collection",
7303
- heyflow: "heyflow",
7304
- hola: "hola",
7305
- home: "home",
7306
- "home-llms-txt": "home-llms-txt",
7307
- "home-tz": "home-tz",
7308
- "home-angular": "home-angular",
7309
- hono: "hono",
7310
- hub88: "hub88",
7311
- hubspot: "hubspot",
7312
- "hugging-face-accelerate": "hugging-face-accelerate",
7313
- "hugging-face-diffusers": "hugging-face-diffusers",
7314
- "hugging-face-hub": "hugging-face-hub",
7315
- "hugging-face-hub-python-library": "hugging-face-hub-python-library",
7316
- "hugging-face-transformers": "hugging-face-transformers",
7317
- "humanity-protocol": "humanity-protocol",
7318
- hybridai: "hybridai",
7319
- hyperbeam: "hyperbeam",
7320
- hyperline: "hyperline",
7321
- hypermode: "hypermode",
7322
- hypertune: "hypertune",
7323
- i3dnet: "i3dnet",
7324
- "iagent-protocol": "iagent-protocol",
7325
- icepanel: "icepanel",
7326
- id21: "id21",
7327
- ideogram: "ideogram",
7328
- "ig-exporter": "ig-exporter",
7329
- illuvium: "illuvium",
7330
- imaginstudio: "imaginstudio",
7331
- imagineart: "imagineart",
7332
- "img-arena": "img-arena",
7333
- "immortal-rising-2": "immortal-rising-2",
7334
- infera: "infera",
7335
- infisical: "infisical",
7336
- inkeep: "inkeep",
7337
- inngest: "inngest",
7338
- inspector: "inspector",
7339
- "inspira-ui-build-beautiful-websites-using-vue-nuxt": "inspira-ui-build-beautiful-websites-using-vue-nuxt",
7340
- instant: "instant",
7341
- instruqt: "instruqt",
7342
- intelligems: "intelligems",
7343
- intract: "intract",
7344
- intuned: "intuned",
7345
- ionos: "ionos",
7346
- ionq: "ionq",
7347
- ipaper: "ipaper",
7348
- iproyalcom: "iproyalcom",
7349
- irev: "irev",
7350
- "island-router": "island-router",
7351
- jam: "jam",
7352
- jazztools: "jazztools",
7353
- journeyapps: "journeyapps",
7354
- journify: "journify",
7355
- jsfiddle: "jsfiddle",
7356
- jumptask: "jumptask",
7357
- "juniper-creates": "juniper-creates",
7358
- juno: "juno",
7359
- "kaisar-network": "kaisar-network",
7360
- "karrier-one": "karrier-one",
7361
- keeper: "keeper",
7362
- ketch: "ketch",
7363
- kick: "kick",
7364
- koala: "koala",
7365
- kruzty: "kruzty",
7366
- lacremeai: "lacremeai",
7367
- lago: "lago",
7368
- lambdatest: "lambdatest",
7369
- "langchain-javascript-docs": "langchain-javascript-docs",
7370
- "langchain-python-docs": "langchain-python-docs",
7371
- langflow: "langflow",
7372
- langfuse: "langfuse",
7373
- "langgraph-javascript-docs": "langgraph-javascript-docs",
7374
- "langgraph-python-docs": "langgraph-python-docs",
7375
- launchfast: "launchfast",
7376
- lavita: "lavita",
7377
- "law-firm-digital-marketing-agency": "law-firm-digital-marketing-agency",
7378
- layouthub: "layouthub",
7379
- "leather-jackets-coats-for-men-women": "leather-jackets-coats-for-men-women",
7380
- legend: "legend",
7381
- "legend-state": "legend-state",
7382
- "legends-of-learning": "legends-of-learning",
7383
- legitt: "legitt",
7384
- lettermint: "lettermint",
7385
- "liam-erd": "liam-erd",
7386
- liblab: "liblab",
7387
- libmodulor: "libmodulor",
7388
- likec4: "likec4",
7389
- linktree: "linktree",
7390
- litdb: "litdb",
7391
- liveblocks: "liveblocks",
7392
- livecaller: "livecaller",
7393
- livecodes: "livecodes",
7394
- livespotting: "livespotting",
7395
- "llms-txtio": "llms-txtio",
7396
- llmstxt: "llmstxt",
7397
- llmvo: "llmvo",
7398
- "lm-studio": "lm-studio",
7399
- lobehub: "lobehub",
7400
- loglayer: "loglayer",
7401
- loops: "loops",
7402
- "lots-of-csvs": "lots-of-csvs",
7403
- lottielab: "lottielab",
7404
- lunacy: "lunacy",
7405
- luxalgo: "luxalgo",
7406
- luxid: "luxid",
7407
- lynx: "lynx",
7408
- mailmodo: "mailmodo",
7409
- mako: "mako",
7410
- mangopay: "mangopay",
7411
- "manifestly-checklists": "manifestly-checklists",
7412
- "marco-ronco-seo-specialist": "marco-ronco-seo-specialist",
7413
- marimo: "marimo",
7414
- "marketing-doctors": "marketing-doctors",
7415
- "marketing-integration-and-automation": "marketing-integration-and-automation",
7416
- massive: "massive",
7417
- mastory: "mastory",
7418
- mastra: "mastra",
7419
- "material-ui-aka-mui": "material-ui-aka-mui",
7420
- "maxim-ai": "maxim-ai",
7421
- "mdutil-free-markdown-tools": "mdutil-free-markdown-tools",
7422
- mealbymeal: "mealbymeal",
7423
- "meds-canada": "meds-canada",
7424
- medusa: "medusa",
7425
- meilisearch: "meilisearch",
7426
- "mekalite-cnc-machining-services": "mekalite-cnc-machining-services",
7427
- meshconnect: "meshconnect",
7428
- "method-financial": "method-financial",
7429
- micro1: "micro1",
7430
- mintlify: "mintlify",
7431
- mixo: "mixo",
7432
- "model-context-protocol-mcp": "model-context-protocol-mcp",
7433
- "monster-ui": "monster-ui",
7434
- moondream: "moondream",
7435
- "morphic-solutions": "morphic-solutions",
7436
- movehealth: "movehealth",
7437
- "mubashir-hassan": "mubashir-hassan",
7438
- "mui-x-advanced-react-components-for-complex-use-cases": "mui-x-advanced-react-components-for-complex-use-cases",
7439
- multiplier: "multiplier",
7440
- multisynq: "multisynq",
7441
- mux: "mux",
7442
- "mystery-o-matic": "mystery-o-matic",
7443
- nash: "nash",
7444
- nativescript: "nativescript",
7445
- navi: "navi",
7446
- "naviga-global": "naviga-global",
7447
- "needle-cloud": "needle-cloud",
7448
- netlify: "netlify",
7449
- nextevo: "nextevo",
7450
- nextevogroup: "nextevogroup",
7451
- "nile-postgres": "nile-postgres",
7452
- nomadhelperai: "nomadhelperai",
7453
- "nonprofit-management-consulting": "nonprofit-management-consulting",
7454
- "novi-labs": "novi-labs",
7455
- nsoftware: "nsoftware",
7456
- nuxt: "nuxt",
7457
- "omnimind-docs-oss-mcp-client": "omnimind-docs-oss-mcp-client",
7458
- onagentsorg: "onagentsorg",
7459
- "once-ui": "once-ui",
7460
- onetrust: "onetrust",
7461
- openfort: "openfort",
7462
- openphone: "openphone",
7463
- openpipe: "openpipe",
7464
- openrouter: "openrouter",
7465
- "openvpn-zero-trust-vpn": "openvpn-zero-trust-vpn",
7466
- "opik-by-comet": "opik-by-comet",
7467
- "optimajet-form-builder": "optimajet-form-builder",
7468
- oxla: "oxla",
7469
- papergraderpro: "papergraderpro",
7470
- paragon: "paragon",
7471
- "parcelcube-static-dimensioning-systems": "parcelcube-static-dimensioning-systems",
7472
- parseable: "parseable",
7473
- pastaclean: "pastaclean",
7474
- payperfax: "payperfax",
7475
- payrollrabbit: "payrollrabbit",
7476
- "pentesting-red-teaming-company": "pentesting-red-teaming-company",
7477
- perplexity: "perplexity",
7478
- "personal-page": "personal-page",
7479
- "petite-box": "petite-box",
7480
- pgflow: "pgflow",
7481
- phare: "phare",
7482
- pikaicons: "pikaicons",
7483
- pinata: "pinata",
7484
- pinecone: "pinecone",
7485
- plain: "plain",
7486
- "plan-harmony-travel-planning": "plan-harmony-travel-planning",
7487
- platformsh: "platformsh",
7488
- playai: "playai",
7489
- popsmash: "popsmash",
7490
- postfast: "postfast",
7491
- preact: "preact",
7492
- prettier: "prettier",
7493
- primev: "primev",
7494
- prisma: "prisma",
7495
- profound: "profound",
7496
- progressrocks: "progressrocks",
7497
- "proguard-pest-control": "proguard-pest-control",
7498
- projectdiscovery: "projectdiscovery",
7499
- "prompt-kit": "prompt-kit",
7500
- promptfoo: "promptfoo",
7501
- pydantic: "pydantic",
7502
- pydanticai: "pydanticai",
7503
- qrcoau: "qrcoau",
7504
- quill: "quill",
7505
- qwikrank: "qwikrank",
7506
- rainbowkit: "rainbowkit",
7507
- raincamp: "raincamp",
7508
- rankscaleai: "rankscaleai",
7509
- rapidtextai: "rapidtextai",
7510
- raycast: "raycast",
7511
- rcponline: "rcponline",
7512
- redpanda: "redpanda",
7513
- rememberizer: "rememberizer",
7514
- remotion: "remotion",
7515
- remult: "remult",
7516
- render: "render",
7517
- reown: "reown",
7518
- replit: "replit",
7519
- resend: "resend",
7520
- respondio: "respondio",
7521
- retainful: "retainful",
7522
- "ri-xu-online": "ri-xu-online",
7523
- "rims-tires-for-cars-and-trucks": "rims-tires-for-cars-and-trucks",
7524
- roc: "roc",
7525
- rsbuild: "rsbuild",
7526
- rsdoctor: "rsdoctor",
7527
- rslib: "rslib",
7528
- rspack: "rspack",
7529
- rubric: "rubric",
7530
- "ruleta-aleatoria-online-gratis": "ruleta-aleatoria-online-gratis",
7531
- rushdb: "rushdb",
7532
- saev: "saev",
7533
- salesbricks: "salesbricks",
7534
- "salesforce-marketing-cloud-consultants": "salesforce-marketing-cloud-consultants",
7535
- "samsung-food": "samsung-food",
7536
- sandisk: "sandisk",
7537
- sardine: "sardine",
7538
- "scottish-art-prints-by-carol-mcewan": "scottish-art-prints-by-carol-mcewan",
7539
- "scraping-proxies": "scraping-proxies",
7540
- screenshotone: "screenshotone",
7541
- sealos: "sealos",
7542
- "secure-fast-online-money-transfers": "secure-fast-online-money-transfers",
7543
- sekhlopk: "sekhlopk",
7544
- self: "self",
7545
- sellersprite: "sellersprite",
7546
- semgrep: "semgrep",
7547
- seo2llm: "seo2llm",
7548
- servicestack: "servicestack",
7549
- sgnlai: "sgnlai",
7550
- sherlock: "sherlock",
7551
- "shop-positioner-llc": "shop-positioner-llc",
7552
- "side-space": "side-space",
7553
- simplepdf: "simplepdf",
7554
- sinch: "sinch",
7555
- "singing-carrots": "singing-carrots",
7556
- sitespeakai: "sitespeakai",
7557
- skipgo: "skipgo",
7558
- "sky-follower-bridge": "sky-follower-bridge",
7559
- skydeckai: "skydeckai",
7560
- "skyworkthe-ai-workspace-agents": "skyworkthe-ai-workspace-agents",
7561
- smartcar: "smartcar",
7562
- social: "social",
7563
- socialityio: "socialityio",
7564
- solid: "solid",
7565
- sourcegraph: "sourcegraph",
7566
- spacetimedb: "spacetimedb",
7567
- speakeasy: "speakeasy",
7568
- starwind: "starwind",
7569
- statusfield: "statusfield",
7570
- stedi: "stedi",
7571
- "stephanie-kabi": "stephanie-kabi",
7572
- "stock-trading-investment-app": "stock-trading-investment-app",
7573
- stripe: "stripe",
7574
- "study-fetch": "study-fetch",
7575
- "sunra-ai": "sunra-ai",
7576
- supabase: "supabase",
7577
- supadata: "supadata",
7578
- superwall: "superwall",
7579
- svelte: "svelte",
7580
- "svg-viewer": "svg-viewer",
7581
- "swipe-simple-invoicing-and-payments-app": "swipe-simple-invoicing-and-payments-app",
7582
- tamagui: "tamagui",
7583
- "taskade-ai": "taskade-ai",
7584
- tavus: "tavus",
7585
- "tlassistance-snior": "tlassistance-snior",
7586
- televisionai: "televisionai",
7587
- tensorzero: "tensorzero",
7588
- "the-ai-engineers-handbook": "the-ai-engineers-handbook",
7589
- "the-bucket-hat": "the-bucket-hat",
7590
- "the-crawl-tool": "the-crawl-tool",
7591
- "the-data-driven-marketer": "the-data-driven-marketer",
7592
- "the-dinner-detective-murder-mystery-show": "the-dinner-detective-murder-mystery-show",
7593
- theirstack: "theirstack",
7594
- "therapydave-dave-lechnyr-lcsw": "therapydave-dave-lechnyr-lcsw",
7595
- tidb: "tidb",
7596
- tidio: "tidio",
7597
- tinybird: "tinybird",
7598
- tiptap: "tiptap",
7599
- toriut: "toriut",
7600
- trackingplan: "trackingplan",
7601
- trackvia: "trackvia",
7602
- "trail-of-bits": "trail-of-bits",
7603
- transloadit: "transloadit",
7604
- "travel-nepal": "travel-nepal",
7605
- triggerdev: "triggerdev",
7606
- triplit: "triplit",
7607
- trueprofit: "trueprofit",
7608
- truffle: "truffle",
7609
- turbo: "turbo",
7610
- turbodocx: "turbodocx",
7611
- turso: "turso",
7612
- "twicpics-by-frontify": "twicpics-by-frontify",
7613
- twoshoes: "twoshoes",
7614
- "ufc-velvet": "ufc-velvet",
7615
- "uminai-mcp": "uminai-mcp",
7616
- underrunio: "underrunio",
7617
- unifygtm: "unifygtm",
7618
- unistyles: "unistyles",
7619
- uniwebview: "uniwebview",
7620
- unkey: "unkey",
7621
- unstructured: "unstructured",
7622
- upstash: "upstash",
7623
- "upstreet-ai": "upstreet-ai",
7624
- upsun: "upsun",
7625
- useapinet: "useapinet",
7626
- "utrecht-golf-instructor-marina-romanik": "utrecht-golf-instructor-marina-romanik",
7627
- "ux-patterns-for-devs": "ux-patterns-for-devs",
7628
- vald: "vald",
7629
- "vald-health": "vald-health",
7630
- vapi: "vapi",
7631
- vdoninja: "vdoninja",
7632
- velt: "velt",
7633
- vendure: "vendure",
7634
- veniceai: "veniceai",
7635
- ventrata: "ventrata",
7636
- "vercel-ai-sdk": "vercel-ai-sdk",
7637
- verdn: "verdn",
7638
- "video-and-audio-frameworks-for-net": "video-and-audio-frameworks-for-net",
7639
- videosdklive: "videosdklive",
7640
- videowise: "videowise",
7641
- viem: "viem",
7642
- "vignette-id": "vignette-id",
7643
- vital: "vital",
7644
- "voxel-busters": "voxel-busters",
7645
- "voyager-shuffle": "voyager-shuffle",
7646
- vplayed: "vplayed",
7647
- "vue-macros": "vue-macros",
7648
- vuejs: "vuejs",
7649
- wandio: "wandio",
7650
- warp: "warp",
7651
- weathercom: "weathercom",
7652
- "web-design-company-qatar": "web-design-company-qatar",
7653
- "web-development-wordpress-maintenance": "web-development-wordpress-maintenance",
7654
- webrecorder: "webrecorder",
7655
- "wedgwood-insurance": "wedgwood-insurance",
7656
- weka: "weka",
7657
- "welcome-to-bunny": "welcome-to-bunny",
7658
- "welcome-to-htmlhint": "welcome-to-htmlhint",
7659
- "wf-content-creator-platform": "wf-content-creator-platform",
7660
- whereby: "whereby",
7661
- "wild-in-africa": "wild-in-africa",
7662
- wized: "wized",
7663
- wonderchat: "wonderchat",
7664
- woowup: "woowup",
7665
- wordlift: "wordlift",
7666
- workbookly: "workbookly",
7667
- workflow: "workflow",
7668
- "wot-design-uni": "wot-design-uni",
7669
- writer: "writer",
7670
- wxt: "wxt",
7671
- x: "x",
7672
- "x-cmd": "x-cmd",
7673
- xmcp: "xmcp",
7674
- xomatic: "xomatic",
7675
- yamlresume: "yamlresume",
7676
- youform: "youform",
7677
- zag: "zag",
7678
- zapts: "zapts",
7679
- "zaphyr-php-framework": "zaphyr-php-framework",
7680
- zapier: "zapier",
7681
- zeffy: "zeffy",
7682
- zenml: "zenml",
7683
- zep: "zep",
7684
- zigpoll: "zigpoll",
7685
- zipchat: "zipchat",
7686
- zipy: "zipy",
7687
- zodori: "zodori",
7688
- zoko: "zoko",
7689
- "zoomryconveyorshiploader-manufacturer": "zoomryconveyorshiploader-manufacturer"
7062
+ import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
7063
+ import { join as join7 } from "path";
7064
+ function normalize(name) {
7065
+ return name.toLowerCase().replace(/[-_./]/g, "");
7066
+ }
7067
+ function extractTokens(dep) {
7068
+ const tokens = [];
7069
+ const unscoped = dep.startsWith("@") ? dep.slice(1) : dep;
7070
+ tokens.push(unscoped);
7071
+ if (dep.startsWith("@") && unscoped.includes("/")) {
7072
+ const [scope, pkg] = unscoped.split("/");
7073
+ tokens.push(pkg);
7074
+ tokens.push(scope);
7075
+ const cleanScope = scope.replace(/js$/, "").replace(/-ai$/, "");
7076
+ if (cleanScope !== scope) tokens.push(cleanScope);
7690
7077
  }
7691
- };
7692
-
7693
- // src/lib/detector.ts
7078
+ for (const suffix of ["-js", "-sdk", "-client", "-core", "-cli", "-types"]) {
7079
+ if (dep.endsWith(suffix)) {
7080
+ tokens.push(dep.slice(0, -suffix.length));
7081
+ }
7082
+ }
7083
+ return tokens;
7084
+ }
7085
+ function matchesEntry(dep, entry) {
7086
+ const tokens = extractTokens(dep);
7087
+ const entrySlug = entry.slug.toLowerCase();
7088
+ const entryName = normalize(entry.name);
7089
+ for (const token of tokens) {
7090
+ const norm = normalize(token);
7091
+ if (norm === entrySlug) return true;
7092
+ if (norm === entryName) return true;
7093
+ if (entrySlug === norm) return true;
7094
+ if (entryName.startsWith(norm) && norm.length >= 3) return true;
7095
+ }
7096
+ return false;
7097
+ }
7694
7098
  function detectFromPackageJson(projectDir) {
7695
- const pkgPath = join6(projectDir, "package.json");
7696
- if (!existsSync5(pkgPath)) return [];
7099
+ const pkgPath = join7(projectDir, "package.json");
7100
+ if (!existsSync6(pkgPath)) return [];
7697
7101
  let pkg;
7698
7102
  try {
7699
- pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
7103
+ pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
7700
7104
  } catch {
7701
7105
  return [];
7702
7106
  }
7703
- const allDeps = {
7107
+ const depNames = Object.keys({
7704
7108
  ...pkg.dependencies,
7705
7109
  ...pkg.devDependencies
7706
- };
7707
- const mappings = package_mappings_default;
7110
+ });
7111
+ if (depNames.length === 0) return [];
7112
+ const entries2 = getAllEntries();
7708
7113
  const matchesBySlug = /* @__PURE__ */ new Map();
7709
- for (const depName of Object.keys(allDeps)) {
7710
- const slug = mappings.npm[depName];
7711
- if (!slug) continue;
7712
- const existing = matchesBySlug.get(slug) || [];
7713
- existing.push(depName);
7714
- matchesBySlug.set(slug, existing);
7715
- }
7716
- const results = [];
7717
- for (const [slug, matchedPackages] of matchesBySlug) {
7718
- const registryEntry = getEntry2(slug);
7719
- if (registryEntry) {
7720
- results.push({ slug, matchedPackages, registryEntry });
7114
+ for (const dep of depNames) {
7115
+ for (const entry of entries2) {
7116
+ if (matchesEntry(dep, entry)) {
7117
+ const existing = matchesBySlug.get(entry.slug);
7118
+ if (existing) {
7119
+ existing.deps.push(dep);
7120
+ } else {
7121
+ matchesBySlug.set(entry.slug, { deps: [dep], entry });
7122
+ }
7123
+ break;
7124
+ }
7721
7125
  }
7722
7126
  }
7723
- return results;
7127
+ return [...matchesBySlug.values()].map(({ deps, entry }) => ({
7128
+ slug: entry.slug,
7129
+ matchedPackages: deps,
7130
+ registryEntry: entry
7131
+ }));
7724
7132
  }
7725
7133
  function filterMatchesByCategories(matches, categories) {
7726
7134
  if (categories.length === 0) return matches;
@@ -7832,12 +7240,12 @@ async function fetchLlmsTxt({ url, existingEtag }) {
7832
7240
  if (contentLength && Number.parseInt(contentLength, 10) > MAX_SIZE) {
7833
7241
  throw new Error(`Response too large (${contentLength} bytes, max ${MAX_SIZE})`);
7834
7242
  }
7835
- const text = await response.text();
7836
- if (text.length > MAX_SIZE) {
7837
- throw new Error(`Response too large (${text.length} bytes, max ${MAX_SIZE})`);
7243
+ const text2 = await response.text();
7244
+ if (text2.length > MAX_SIZE) {
7245
+ throw new Error(`Response too large (${text2.length} bytes, max ${MAX_SIZE})`);
7838
7246
  }
7839
7247
  return {
7840
- content: text,
7248
+ content: text2,
7841
7249
  etag: response.headers.get("etag"),
7842
7250
  lastModified: response.headers.get("last-modified"),
7843
7251
  notModified: false
@@ -7849,7 +7257,7 @@ async function fetchLlmsTxt({ url, existingEtag }) {
7849
7257
 
7850
7258
  // src/lib/telemetry.ts
7851
7259
  var TELEMETRY_ENDPOINT = "https://llmstxt.directory/api/cli/telemetry";
7852
- var CLI_VERSION = "0.2.0";
7260
+ var CLI_VERSION = "0.4.0";
7853
7261
  var CI_ENV_VARS = ["CI", "GITHUB_ACTIONS", "GITLAB_CI", "CIRCLECI", "TRAVIS"];
7854
7262
  function isDisabled() {
7855
7263
  return process.env.DO_NOT_TRACK === "1" || process.env.LLMSTXT_TELEMETRY_DISABLED === "1";
@@ -7876,27 +7284,13 @@ function track(params) {
7876
7284
  async function init(options) {
7877
7285
  const projectDir = process.cwd();
7878
7286
  const isInteractive = process.stdin.isTTY && !options.yes;
7879
- printBanner("0.2.0");
7287
+ printBanner("0.4.0");
7880
7288
  p3.intro("Install llms.txt documentation for your project");
7881
7289
  const spin = spinner2("Loading registry...");
7882
7290
  spin.start();
7883
7291
  await loadRegistry();
7884
7292
  spin.succeed("Registry loaded");
7885
7293
  const agents2 = detectInstalledAgents();
7886
- if (agents2.length > 0) {
7887
- p3.log.info(`Detected agents: ${agents2.map((a) => pc4.cyan(a.displayName)).join(", ")}`);
7888
- } else {
7889
- p3.log.warn("No AI coding tools detected \u2014 files will be installed to .agents/skills/ only");
7890
- }
7891
- const spin2 = spinner2("Detecting project dependencies...");
7892
- spin2.start();
7893
- let matches = detectFromPackageJson(projectDir);
7894
- if (matches.length === 0) {
7895
- spin2.info("No matching llms.txt entries found for your dependencies");
7896
- p3.log.message(pc4.dim("Try `llmstxt search <query>` to find entries manually"));
7897
- p3.outro("No skills to install.");
7898
- return;
7899
- }
7900
7294
  let activeCategories;
7901
7295
  if (options.allCategories) {
7902
7296
  activeCategories = [];
@@ -7905,95 +7299,223 @@ async function init(options) {
7905
7299
  } else {
7906
7300
  activeCategories = [...PRIMARY_CATEGORIES];
7907
7301
  }
7302
+ let depMatches = detectFromPackageJson(projectDir);
7908
7303
  if (activeCategories.length > 0) {
7909
- matches = filterMatchesByCategories(matches, activeCategories);
7304
+ depMatches = filterMatchesByCategories(depMatches, activeCategories);
7305
+ }
7306
+ const depSlugs = new Set(depMatches.map((m) => m.slug));
7307
+ if (depMatches.length > 0) {
7308
+ p3.log.info(
7309
+ `Found ${depMatches.length} matching your dependencies: ${depMatches.map((m) => pc4.cyan(m.registryEntry.name)).join(", ")}`
7310
+ );
7910
7311
  }
7911
- if (matches.length === 0) {
7912
- spin2.info("No matching entries in selected categories");
7913
- p3.log.message(pc4.dim("Try `llmstxt init --all-categories` to include all categories"));
7914
- p3.outro("No skills to install.");
7312
+ if (!isInteractive) {
7313
+ return installEntries({
7314
+ projectDir,
7315
+ entries: depMatches.map((m) => m.registryEntry),
7316
+ options,
7317
+ agents: agents2,
7318
+ targetAgents: agents2
7319
+ });
7320
+ }
7321
+ const selectedEntries = await browseAndSelect({ projectDir, depMatches, depSlugs });
7322
+ if (selectedEntries.length === 0) {
7323
+ p3.outro("No skills selected.");
7915
7324
  return;
7916
7325
  }
7917
- spin2.succeed(`Found ${matches.length} matching entries`);
7918
- if (options.dryRun) {
7919
- for (const match of matches) {
7920
- const already = isInstalled({ projectDir, slug: match.slug });
7921
- const status = already ? pc4.dim(" (already installed)") : "";
7922
- p3.log.message(` ${pc4.cyan(match.registryEntry.name)}${status}`);
7923
- }
7924
- p3.outro("Dry run \u2014 no files were written");
7326
+ const targetAgents = await pickAgents(projectDir);
7327
+ if (!targetAgents) {
7328
+ p3.cancel("Installation cancelled.");
7925
7329
  return;
7926
7330
  }
7927
- let selectedSlugs;
7928
- if (isInteractive) {
7929
- const byCategory = /* @__PURE__ */ new Map();
7930
- for (const match of matches) {
7931
- const cat = match.registryEntry.category;
7932
- const group = byCategory.get(cat) || [];
7933
- group.push(match);
7934
- byCategory.set(cat, group);
7935
- }
7936
- const options_list = [];
7937
- for (const [category, group] of byCategory) {
7938
- for (const match of group) {
7939
- const already = isInstalled({ projectDir, slug: match.slug });
7940
- options_list.push({
7941
- value: match.slug,
7942
- label: match.registryEntry.name,
7943
- hint: already ? "already installed" : `${category} \xB7 matched: ${match.matchedPackages.join(", ")}`
7944
- });
7945
- }
7946
- }
7947
- const selected = await p3.multiselect({
7948
- message: "Select skills to install:",
7949
- options: options_list,
7950
- initialValues: matches.filter((m) => !isInstalled({ projectDir, slug: m.slug })).map((m) => m.slug),
7951
- required: false
7331
+ const format = await pickFormat(selectedEntries, options);
7332
+ if (!format) {
7333
+ p3.cancel("Installation cancelled.");
7334
+ return;
7335
+ }
7336
+ return installEntries({
7337
+ projectDir,
7338
+ entries: selectedEntries,
7339
+ options: { ...options, full: format === "llms-full.txt" },
7340
+ agents: agents2,
7341
+ targetAgents
7342
+ });
7343
+ }
7344
+ async function browseAndSelect({
7345
+ projectDir,
7346
+ depMatches,
7347
+ depSlugs
7348
+ }) {
7349
+ const selectedEntries = [];
7350
+ while (true) {
7351
+ const action = await p3.select({
7352
+ message: selectedEntries.length === 0 ? "How would you like to find llms.txt documentation?" : `${selectedEntries.length} selected. Add more or install?`,
7353
+ options: [
7354
+ ...depMatches.length > 0 && selectedEntries.length === 0 ? [
7355
+ {
7356
+ value: "suggestions",
7357
+ label: `Suggested from dependencies (${depMatches.length} found)`,
7358
+ hint: depMatches.map((m) => m.registryEntry.name).join(", ")
7359
+ }
7360
+ ] : [],
7361
+ { value: "browse", label: "Browse by category" },
7362
+ { value: "search", label: "Search by name" },
7363
+ ...selectedEntries.length > 0 ? [{ value: "install", label: `Install ${selectedEntries.length} selected` }] : [],
7364
+ { value: "done", label: selectedEntries.length > 0 ? "Cancel" : "Exit" }
7365
+ ]
7952
7366
  });
7953
- if (p3.isCancel(selected)) {
7954
- p3.cancel("Installation cancelled.");
7955
- process.exitCode = 0;
7956
- return;
7367
+ if (p3.isCancel(action) || action === "done") {
7368
+ if (selectedEntries.length > 0) p3.cancel("Installation cancelled.");
7369
+ else p3.outro("No skills selected.");
7370
+ return [];
7957
7371
  }
7958
- selectedSlugs = new Set(selected);
7959
- if (selectedSlugs.size === 0) {
7960
- p3.outro("No skills selected.");
7961
- return;
7372
+ if (action === "install") break;
7373
+ let picked = [];
7374
+ if (action === "suggestions") {
7375
+ picked = await pickFromList(
7376
+ depMatches.map((m) => m.registryEntry),
7377
+ projectDir,
7378
+ depSlugs
7379
+ );
7380
+ } else if (action === "browse") {
7381
+ picked = await browseByCategory(projectDir, depSlugs);
7382
+ } else if (action === "search") {
7383
+ picked = await searchByName(projectDir, depSlugs);
7962
7384
  }
7963
- const shouldContinue = await p3.confirm({
7964
- message: `Install ${selectedSlugs.size} skill(s)?`
7965
- });
7966
- if (p3.isCancel(shouldContinue) || !shouldContinue) {
7967
- p3.cancel("Installation cancelled.");
7968
- process.exitCode = 0;
7969
- return;
7385
+ for (const entry of picked) {
7386
+ if (!selectedEntries.some((e) => e.slug === entry.slug)) {
7387
+ selectedEntries.push(entry);
7388
+ }
7970
7389
  }
7971
- } else {
7972
- selectedSlugs = new Set(
7973
- matches.filter((m) => !isInstalled({ projectDir, slug: m.slug })).map((m) => m.slug)
7974
- );
7975
- if (!process.stdin.isTTY) {
7976
- p3.log.message(pc4.dim("Non-interactive mode. Use -y to skip prompts."));
7390
+ }
7391
+ return selectedEntries;
7392
+ }
7393
+ async function browseByCategory(projectDir, depSlugs) {
7394
+ const allEntries = getAllEntries();
7395
+ const categoryMap = /* @__PURE__ */ new Map();
7396
+ for (const entry of allEntries) {
7397
+ categoryMap.set(entry.category, (categoryMap.get(entry.category) || 0) + 1);
7398
+ }
7399
+ const categoryChoice = await p3.select({
7400
+ message: "Select a category:",
7401
+ options: [...categoryMap.entries()].sort((a, b) => b[1] - a[1]).map(([cat, count]) => ({
7402
+ value: cat,
7403
+ label: cat,
7404
+ hint: `${count} entries`
7405
+ }))
7406
+ });
7407
+ if (p3.isCancel(categoryChoice)) return [];
7408
+ const categoryEntries = allEntries.filter((e) => e.category === categoryChoice);
7409
+ return pickFromList(categoryEntries, projectDir, depSlugs);
7410
+ }
7411
+ async function searchByName(projectDir, depSlugs) {
7412
+ const query = await p3.text({
7413
+ message: "Search for:",
7414
+ placeholder: "e.g. react, stripe, prisma..."
7415
+ });
7416
+ if (p3.isCancel(query) || !query) return [];
7417
+ const results = searchRegistry(query).slice(0, 20);
7418
+ if (results.length === 0) {
7419
+ p3.log.warn(`No results for "${query}"`);
7420
+ return [];
7421
+ }
7422
+ return pickFromList(results, projectDir, depSlugs);
7423
+ }
7424
+ async function pickFromList(entries2, projectDir, depSlugs) {
7425
+ const optionsList = entries2.map((entry) => {
7426
+ const already = isInstalled({ projectDir, slug: entry.slug });
7427
+ const isDep = depSlugs.has(entry.slug);
7428
+ const hints = [];
7429
+ if (already) hints.push("installed");
7430
+ if (isDep) hints.push("in your deps");
7431
+ hints.push(entry.category);
7432
+ return {
7433
+ value: entry.slug,
7434
+ label: entry.name,
7435
+ hint: hints.join(" \xB7 ")
7436
+ };
7437
+ });
7438
+ const selected = await p3.multiselect({
7439
+ message: `Select entries (${entries2.length} available):`,
7440
+ options: optionsList,
7441
+ required: false
7442
+ });
7443
+ if (p3.isCancel(selected)) return [];
7444
+ const slugSet = new Set(selected);
7445
+ return entries2.filter((e) => slugSet.has(e.slug));
7446
+ }
7447
+ async function pickFormat(entries2, options) {
7448
+ if (options.full) return "llms-full.txt";
7449
+ const hasFullAvailable = entries2.some((e) => e.llmsFullTxtUrl);
7450
+ if (!hasFullAvailable) return "llms.txt";
7451
+ const fullCount = entries2.filter((e) => e.llmsFullTxtUrl).length;
7452
+ const choice = await p3.select({
7453
+ message: "Which documentation format?",
7454
+ options: [
7455
+ {
7456
+ value: "llms.txt",
7457
+ label: "llms.txt",
7458
+ hint: "concise \u2014 smaller, faster to load"
7459
+ },
7460
+ {
7461
+ value: "llms-full.txt",
7462
+ label: "llms-full.txt",
7463
+ hint: `comprehensive \u2014 ${fullCount}/${entries2.length} selected have full version`
7464
+ }
7465
+ ]
7466
+ });
7467
+ if (p3.isCancel(choice)) return null;
7468
+ return choice;
7469
+ }
7470
+ async function pickAgents(projectDir) {
7471
+ const savedPrefs = loadSavedAgentPrefs(projectDir);
7472
+ const initialValues = getInitialAgents({ allAgents: agents, savedPrefs, projectDir });
7473
+ const selected = await p3.multiselect({
7474
+ message: "Which agents should receive the skills?",
7475
+ options: agents.map((a) => ({
7476
+ value: a.name,
7477
+ label: a.displayName,
7478
+ hint: a.isUniversal ? "always included" : a.skillsDir
7479
+ })),
7480
+ initialValues,
7481
+ required: true
7482
+ });
7483
+ if (p3.isCancel(selected)) return null;
7484
+ const finalNames = ensureUniversalAgents({ selected, allAgents: agents });
7485
+ saveAgentPrefs(projectDir, selected);
7486
+ const nameSet = new Set(finalNames);
7487
+ return agents.filter((a) => nameSet.has(a.name));
7488
+ }
7489
+ async function installEntries({
7490
+ projectDir,
7491
+ entries: entries2,
7492
+ options,
7493
+ agents: agents2,
7494
+ targetAgents
7495
+ }) {
7496
+ if (options.dryRun) {
7497
+ for (const entry of entries2) {
7498
+ const already = isInstalled({ projectDir, slug: entry.slug });
7499
+ const status = already ? pc4.dim(" (already installed)") : "";
7500
+ p3.log.message(` ${pc4.cyan(entry.name)}${status}`);
7977
7501
  }
7502
+ p3.outro("Dry run \u2014 no files were written");
7503
+ return;
7978
7504
  }
7979
7505
  const format = options.full ? "llms-full.txt" : "llms.txt";
7980
7506
  let installed = 0;
7981
7507
  let skipped = 0;
7982
7508
  let failed = 0;
7983
7509
  const installedSlugs = [];
7984
- for (const match of matches) {
7985
- if (!selectedSlugs.has(match.slug)) {
7986
- continue;
7987
- }
7988
- const entry = match.registryEntry;
7510
+ for (const entry of entries2) {
7989
7511
  const actualFormat = format === "llms-full.txt" && entry.llmsFullTxtUrl ? "llms-full.txt" : "llms.txt";
7990
7512
  const url = actualFormat === "llms-full.txt" ? entry.llmsFullTxtUrl : entry.llmsTxtUrl;
7991
- if (isInstalled({ projectDir, slug: match.slug })) {
7513
+ if (isInstalled({ projectDir, slug: entry.slug })) {
7992
7514
  skipped++;
7993
7515
  continue;
7994
7516
  }
7995
- const spin3 = spinner2(`Fetching ${entry.name}...`);
7996
- spin3.start();
7517
+ const spin = spinner2(`Fetching ${entry.name}...`);
7518
+ spin.start();
7997
7519
  try {
7998
7520
  const result = await fetchLlmsTxt({ url });
7999
7521
  const {
@@ -8002,15 +7524,16 @@ async function init(options) {
8002
7524
  agents: installedTo
8003
7525
  } = installToAgents({
8004
7526
  projectDir,
8005
- slug: match.slug,
7527
+ slug: entry.slug,
8006
7528
  entry,
8007
7529
  content: result.content,
8008
- format: actualFormat
7530
+ format: actualFormat,
7531
+ targetAgents
8009
7532
  });
8010
7533
  addEntry({
8011
7534
  projectDir,
8012
7535
  entry: {
8013
- slug: match.slug,
7536
+ slug: entry.slug,
8014
7537
  format: actualFormat,
8015
7538
  sourceUrl: url,
8016
7539
  etag: result.etag,
@@ -8021,12 +7544,12 @@ async function init(options) {
8021
7544
  name: entry.name
8022
7545
  }
8023
7546
  });
8024
- spin3.succeed(`${entry.name} ${pc4.dim(`\u2192 ${installedTo.join(", ")}`)}`);
7547
+ spin.succeed(`${entry.name} ${pc4.dim(`\u2192 ${installedTo.join(", ")}`)}`);
8025
7548
  installed++;
8026
- installedSlugs.push(match.slug);
7549
+ installedSlugs.push(entry.slug);
8027
7550
  } catch (err) {
8028
7551
  const msg = err instanceof Error ? err.message : String(err);
8029
- spin3.fail(`${entry.name}: ${msg}`);
7552
+ spin.fail(`${entry.name}: ${msg}`);
8030
7553
  failed++;
8031
7554
  }
8032
7555
  }
@@ -8037,9 +7560,6 @@ async function init(options) {
8037
7560
  if (summaryLines.length > 0) {
8038
7561
  p3.note(summaryLines.join("\n"), "Summary");
8039
7562
  }
8040
- if (addToGitignore(projectDir)) {
8041
- p3.log.success("Added .llms/ and .agents/skills/ to .gitignore");
8042
- }
8043
7563
  if (installed > 0) {
8044
7564
  syncClaudeMd(projectDir);
8045
7565
  }
@@ -8068,8 +7588,34 @@ async function install({ names, options }) {
8068
7588
  await loadRegistry();
8069
7589
  spin.succeed("Registry loaded");
8070
7590
  const agents2 = detectInstalledAgents();
8071
- if (agents2.length > 0) {
8072
- p4.log.message(pc5.dim(`Detected: ${agents2.map((a) => a.displayName).join(", ")}`));
7591
+ let targetAgents;
7592
+ if (!process.stdin.isTTY) {
7593
+ targetAgents = agents2;
7594
+ if (agents2.length > 0) {
7595
+ const display = agents2.map((a) => a.displayName).join(", ");
7596
+ p4.log.message(pc5.dim(`Installing to: ${display}`));
7597
+ }
7598
+ } else {
7599
+ const savedPrefs = loadSavedAgentPrefs(projectDir);
7600
+ const initialValues = getInitialAgents({ allAgents: agents, savedPrefs, projectDir });
7601
+ const selected = await p4.multiselect({
7602
+ message: "Which agents should receive the skills?",
7603
+ options: agents.map((a) => ({
7604
+ value: a.name,
7605
+ label: a.displayName,
7606
+ hint: a.isUniversal ? "always included" : a.skillsDir
7607
+ })),
7608
+ initialValues,
7609
+ required: true
7610
+ });
7611
+ if (p4.isCancel(selected)) {
7612
+ p4.cancel("Installation cancelled.");
7613
+ return;
7614
+ }
7615
+ const finalNames = ensureUniversalAgents({ selected, allAgents: agents });
7616
+ saveAgentPrefs(projectDir, selected);
7617
+ const nameSet = new Set(finalNames);
7618
+ targetAgents = agents.filter((a) => nameSet.has(a.name));
8073
7619
  }
8074
7620
  let installed = 0;
8075
7621
  let failed = 0;
@@ -8122,7 +7668,14 @@ async function install({ names, options }) {
8122
7668
  checksum,
8123
7669
  size,
8124
7670
  agents: installedTo
8125
- } = installToAgents({ projectDir, slug: entry.slug, entry, content: result.content, format });
7671
+ } = installToAgents({
7672
+ projectDir,
7673
+ slug: entry.slug,
7674
+ entry,
7675
+ content: result.content,
7676
+ format,
7677
+ targetAgents
7678
+ });
8126
7679
  addEntry({
8127
7680
  projectDir,
8128
7681
  entry: {
@@ -8154,7 +7707,6 @@ async function install({ names, options }) {
8154
7707
  p4.note(lines.join("\n"), "Summary");
8155
7708
  }
8156
7709
  if (installed > 0) {
8157
- addToGitignore(projectDir);
8158
7710
  syncClaudeMd(projectDir);
8159
7711
  }
8160
7712
  if (failed > 0) {
@@ -8378,8 +7930,8 @@ async function update(name, options) {
8378
7930
 
8379
7931
  // src/index.ts
8380
7932
  var program = new Command();
8381
- program.name("llmstxt").description("Install llms.txt files from the llms-txt-hub registry into your AI coding tools").version("0.2.0").action(() => {
8382
- printBanner("0.2.0");
7933
+ program.name("llmstxt").description("Install llms.txt files from the llms-txt-hub registry into your AI coding tools").version("0.4.0").action(() => {
7934
+ printBanner("0.4.0");
8383
7935
  program.outputHelp();
8384
7936
  });
8385
7937
  program.command("init").description("Auto-detect dependencies and install matching llms.txt files").option("--category <categories>", "Filter by categories (comma-separated)").option("--all-categories", "Include all categories").option("--dry-run", "Preview without installing").option("--full", "Prefer llms-full.txt when available").option("-y, --yes", "Skip confirmation prompts").action(init);