llmstxt-cli 0.2.0 → 0.3.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 -810
  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;
@@ -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",
@@ -6812,7 +6815,8 @@ function installToAgents({
6812
6815
  slug,
6813
6816
  entry,
6814
6817
  content,
6815
- format
6818
+ format,
6819
+ targetAgents
6816
6820
  }) {
6817
6821
  const checksum = createHash("sha256").update(content).digest("hex");
6818
6822
  const size = Buffer.byteLength(content, "utf-8");
@@ -6827,8 +6831,8 @@ function installToAgents({
6827
6831
  writeFileSync3(join4(canonicalDir, "reference.md"), referenceMd, "utf-8");
6828
6832
  }
6829
6833
  installedAgents.push("universal");
6830
- const detectedAgents = detectInstalledAgents();
6831
- for (const agent of detectedAgents) {
6834
+ const agentsToLink = targetAgents ?? detectInstalledAgents();
6835
+ for (const agent of agentsToLink) {
6832
6836
  if (agent.isUniversal) {
6833
6837
  if (!installedAgents.includes(agent.name)) {
6834
6838
  installedAgents.push(agent.name);
@@ -6934,6 +6938,68 @@ ${pc2.dim("Full:")} ${entry.llmsFullTxtUrl}` : ""}`
6934
6938
  import * as p3 from "@clack/prompts";
6935
6939
  import pc4 from "picocolors";
6936
6940
 
6941
+ // src/lib/agent-selection.ts
6942
+ import { existsSync as existsSync4, mkdirSync as mkdirSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
6943
+ import { join as join5 } from "path";
6944
+ var PREFS_DIR = ".llms";
6945
+ var PREFS_FILE = "agent-prefs.json";
6946
+ var DEFAULT_AGENTS = ["claude-code", "cursor", "codex"];
6947
+ function detectProjectAgents(opts) {
6948
+ const detected = [];
6949
+ for (const agent of opts.allAgents) {
6950
+ if (agent.isUniversal) continue;
6951
+ const configDir = agent.skillsDir.split("/")[0];
6952
+ if (configDir && configDir !== "skills" && existsSync4(join5(opts.projectDir, configDir))) {
6953
+ detected.push(agent.name);
6954
+ }
6955
+ }
6956
+ return detected;
6957
+ }
6958
+ function getInitialAgents(opts) {
6959
+ const validNames = new Set(opts.allAgents.map((a) => a.name));
6960
+ if (opts.savedPrefs && opts.savedPrefs.length > 0) {
6961
+ const filtered = opts.savedPrefs.filter((name) => validNames.has(name));
6962
+ if (filtered.length > 0) return filtered;
6963
+ }
6964
+ if (opts.projectDir) {
6965
+ const detected = detectProjectAgents({ projectDir: opts.projectDir, allAgents: opts.allAgents });
6966
+ if (detected.length > 0) return detected;
6967
+ }
6968
+ return DEFAULT_AGENTS.filter((name) => validNames.has(name));
6969
+ }
6970
+ function ensureUniversalAgents(opts) {
6971
+ const result = [...opts.selected];
6972
+ for (const agent of opts.allAgents) {
6973
+ if (agent.isUniversal && !result.includes(agent.name)) {
6974
+ result.push(agent.name);
6975
+ }
6976
+ }
6977
+ return result;
6978
+ }
6979
+ function loadSavedAgentPrefs(projectDir) {
6980
+ try {
6981
+ const filePath = join5(projectDir, PREFS_DIR, PREFS_FILE);
6982
+ if (!existsSync4(filePath)) return null;
6983
+ const data = JSON.parse(readFileSync4(filePath, "utf-8"));
6984
+ if (Array.isArray(data.agents)) return data.agents;
6985
+ return null;
6986
+ } catch {
6987
+ return null;
6988
+ }
6989
+ }
6990
+ function saveAgentPrefs(projectDir, agentNames) {
6991
+ try {
6992
+ const dir = join5(projectDir, PREFS_DIR);
6993
+ mkdirSync5(dir, { recursive: true });
6994
+ writeFileSync4(
6995
+ join5(dir, PREFS_FILE),
6996
+ JSON.stringify({ agents: agentNames }, null, 2) + "\n",
6997
+ "utf-8"
6998
+ );
6999
+ } catch {
7000
+ }
7001
+ }
7002
+
6937
7003
  // src/lib/banner.ts
6938
7004
  import pc3 from "picocolors";
6939
7005
  var BANNER_LINES = [
@@ -6959,18 +7025,18 @@ ${pc3.dim(` v${version} \xB7 Install llms.txt documentation for AI coding tools
6959
7025
  }
6960
7026
 
6961
7027
  // src/lib/context.ts
6962
- import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
6963
- import { join as join5 } from "path";
7028
+ import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
7029
+ import { join as join6 } from "path";
6964
7030
  var START_MARKER = "<!-- llmstxt:start -->";
6965
7031
  var END_MARKER = "<!-- llmstxt:end -->";
6966
7032
  function syncClaudeMd(projectDir) {
6967
7033
  try {
6968
7034
  const lockfile = readLockfile(projectDir);
6969
7035
  const entries2 = Object.values(lockfile.entries);
6970
- const claudeMdPath = join5(projectDir, "CLAUDE.md");
7036
+ const claudeMdPath = join6(projectDir, "CLAUDE.md");
6971
7037
  let content = "";
6972
- if (existsSync4(claudeMdPath)) {
6973
- content = readFileSync4(claudeMdPath, "utf-8");
7038
+ if (existsSync5(claudeMdPath)) {
7039
+ content = readFileSync5(claudeMdPath, "utf-8");
6974
7040
  }
6975
7041
  const section = buildSection(entries2);
6976
7042
  if (content.includes(START_MARKER)) {
@@ -6993,12 +7059,12 @@ function syncClaudeMd(projectDir) {
6993
7059
  new RegExp(`\\n?${escapeRegex(START_MARKER)}[\\s\\S]*?${escapeRegex(END_MARKER)}\\n?`),
6994
7060
  ""
6995
7061
  ).trim();
6996
- if (content.length === 0 && !existsSync4(claudeMdPath)) {
7062
+ if (content.length === 0 && !existsSync5(claudeMdPath)) {
6997
7063
  return;
6998
7064
  }
6999
7065
  }
7000
- if (content.length > 0 || existsSync4(claudeMdPath)) {
7001
- writeFileSync4(claudeMdPath, content.endsWith("\n") ? content : `${content}
7066
+ if (content.length > 0 || existsSync5(claudeMdPath)) {
7067
+ writeFileSync5(claudeMdPath, content.endsWith("\n") ? content : `${content}
7002
7068
  `, "utf-8");
7003
7069
  }
7004
7070
  } catch (err) {
@@ -7026,701 +7092,76 @@ function escapeRegex(s) {
7026
7092
  }
7027
7093
 
7028
7094
  // 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"
7095
+ import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
7096
+ import { join as join7 } from "path";
7097
+ function normalize(name) {
7098
+ return name.toLowerCase().replace(/[-_./]/g, "");
7099
+ }
7100
+ function extractTokens(dep) {
7101
+ const tokens = [];
7102
+ const unscoped = dep.startsWith("@") ? dep.slice(1) : dep;
7103
+ tokens.push(unscoped);
7104
+ if (dep.startsWith("@") && unscoped.includes("/")) {
7105
+ const [scope, pkg] = unscoped.split("/");
7106
+ tokens.push(pkg);
7107
+ tokens.push(scope);
7108
+ const cleanScope = scope.replace(/js$/, "").replace(/-ai$/, "");
7109
+ if (cleanScope !== scope) tokens.push(cleanScope);
7690
7110
  }
7691
- };
7692
-
7693
- // src/lib/detector.ts
7111
+ for (const suffix of ["-js", "-sdk", "-client", "-core", "-cli", "-types"]) {
7112
+ if (dep.endsWith(suffix)) {
7113
+ tokens.push(dep.slice(0, -suffix.length));
7114
+ }
7115
+ }
7116
+ return tokens;
7117
+ }
7118
+ function matchesEntry(dep, entry) {
7119
+ const tokens = extractTokens(dep);
7120
+ const entrySlug = entry.slug.toLowerCase();
7121
+ const entryName = normalize(entry.name);
7122
+ for (const token of tokens) {
7123
+ const norm = normalize(token);
7124
+ if (norm === entrySlug) return true;
7125
+ if (norm === entryName) return true;
7126
+ if (entrySlug === norm) return true;
7127
+ if (entryName.startsWith(norm) && norm.length >= 3) return true;
7128
+ }
7129
+ return false;
7130
+ }
7694
7131
  function detectFromPackageJson(projectDir) {
7695
- const pkgPath = join6(projectDir, "package.json");
7696
- if (!existsSync5(pkgPath)) return [];
7132
+ const pkgPath = join7(projectDir, "package.json");
7133
+ if (!existsSync6(pkgPath)) return [];
7697
7134
  let pkg;
7698
7135
  try {
7699
- pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
7136
+ pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
7700
7137
  } catch {
7701
7138
  return [];
7702
7139
  }
7703
- const allDeps = {
7140
+ const depNames = Object.keys({
7704
7141
  ...pkg.dependencies,
7705
7142
  ...pkg.devDependencies
7706
- };
7707
- const mappings = package_mappings_default;
7143
+ });
7144
+ if (depNames.length === 0) return [];
7145
+ const entries2 = getAllEntries();
7708
7146
  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 });
7147
+ for (const dep of depNames) {
7148
+ for (const entry of entries2) {
7149
+ if (matchesEntry(dep, entry)) {
7150
+ const existing = matchesBySlug.get(entry.slug);
7151
+ if (existing) {
7152
+ existing.deps.push(dep);
7153
+ } else {
7154
+ matchesBySlug.set(entry.slug, { deps: [dep], entry });
7155
+ }
7156
+ break;
7157
+ }
7721
7158
  }
7722
7159
  }
7723
- return results;
7160
+ return [...matchesBySlug.values()].map(({ deps, entry }) => ({
7161
+ slug: entry.slug,
7162
+ matchedPackages: deps,
7163
+ registryEntry: entry
7164
+ }));
7724
7165
  }
7725
7166
  function filterMatchesByCategories(matches, categories) {
7726
7167
  if (categories.length === 0) return matches;
@@ -7832,12 +7273,12 @@ async function fetchLlmsTxt({ url, existingEtag }) {
7832
7273
  if (contentLength && Number.parseInt(contentLength, 10) > MAX_SIZE) {
7833
7274
  throw new Error(`Response too large (${contentLength} bytes, max ${MAX_SIZE})`);
7834
7275
  }
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})`);
7276
+ const text2 = await response.text();
7277
+ if (text2.length > MAX_SIZE) {
7278
+ throw new Error(`Response too large (${text2.length} bytes, max ${MAX_SIZE})`);
7838
7279
  }
7839
7280
  return {
7840
- content: text,
7281
+ content: text2,
7841
7282
  etag: response.headers.get("etag"),
7842
7283
  lastModified: response.headers.get("last-modified"),
7843
7284
  notModified: false
@@ -7849,7 +7290,7 @@ async function fetchLlmsTxt({ url, existingEtag }) {
7849
7290
 
7850
7291
  // src/lib/telemetry.ts
7851
7292
  var TELEMETRY_ENDPOINT = "https://llmstxt.directory/api/cli/telemetry";
7852
- var CLI_VERSION = "0.2.0";
7293
+ var CLI_VERSION = "0.3.0";
7853
7294
  var CI_ENV_VARS = ["CI", "GITHUB_ACTIONS", "GITLAB_CI", "CIRCLECI", "TRAVIS"];
7854
7295
  function isDisabled() {
7855
7296
  return process.env.DO_NOT_TRACK === "1" || process.env.LLMSTXT_TELEMETRY_DISABLED === "1";
@@ -7876,27 +7317,13 @@ function track(params) {
7876
7317
  async function init(options) {
7877
7318
  const projectDir = process.cwd();
7878
7319
  const isInteractive = process.stdin.isTTY && !options.yes;
7879
- printBanner("0.2.0");
7320
+ printBanner("0.3.0");
7880
7321
  p3.intro("Install llms.txt documentation for your project");
7881
7322
  const spin = spinner2("Loading registry...");
7882
7323
  spin.start();
7883
7324
  await loadRegistry();
7884
7325
  spin.succeed("Registry loaded");
7885
7326
  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
7327
  let activeCategories;
7901
7328
  if (options.allCategories) {
7902
7329
  activeCategories = [];
@@ -7905,95 +7332,223 @@ async function init(options) {
7905
7332
  } else {
7906
7333
  activeCategories = [...PRIMARY_CATEGORIES];
7907
7334
  }
7335
+ let depMatches = detectFromPackageJson(projectDir);
7908
7336
  if (activeCategories.length > 0) {
7909
- matches = filterMatchesByCategories(matches, activeCategories);
7337
+ depMatches = filterMatchesByCategories(depMatches, activeCategories);
7338
+ }
7339
+ const depSlugs = new Set(depMatches.map((m) => m.slug));
7340
+ if (depMatches.length > 0) {
7341
+ p3.log.info(
7342
+ `Found ${depMatches.length} matching your dependencies: ${depMatches.map((m) => pc4.cyan(m.registryEntry.name)).join(", ")}`
7343
+ );
7910
7344
  }
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.");
7345
+ if (!isInteractive) {
7346
+ return installEntries({
7347
+ projectDir,
7348
+ entries: depMatches.map((m) => m.registryEntry),
7349
+ options,
7350
+ agents: agents2,
7351
+ targetAgents: agents2
7352
+ });
7353
+ }
7354
+ const selectedEntries = await browseAndSelect({ projectDir, depMatches, depSlugs });
7355
+ if (selectedEntries.length === 0) {
7356
+ p3.outro("No skills selected.");
7915
7357
  return;
7916
7358
  }
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");
7359
+ const targetAgents = await pickAgents(projectDir);
7360
+ if (!targetAgents) {
7361
+ p3.cancel("Installation cancelled.");
7925
7362
  return;
7926
7363
  }
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
7364
+ const format = await pickFormat(selectedEntries, options);
7365
+ if (!format) {
7366
+ p3.cancel("Installation cancelled.");
7367
+ return;
7368
+ }
7369
+ return installEntries({
7370
+ projectDir,
7371
+ entries: selectedEntries,
7372
+ options: { ...options, full: format === "llms-full.txt" },
7373
+ agents: agents2,
7374
+ targetAgents
7375
+ });
7376
+ }
7377
+ async function browseAndSelect({
7378
+ projectDir,
7379
+ depMatches,
7380
+ depSlugs
7381
+ }) {
7382
+ const selectedEntries = [];
7383
+ while (true) {
7384
+ const action = await p3.select({
7385
+ message: selectedEntries.length === 0 ? "How would you like to find llms.txt documentation?" : `${selectedEntries.length} selected. Add more or install?`,
7386
+ options: [
7387
+ ...depMatches.length > 0 && selectedEntries.length === 0 ? [
7388
+ {
7389
+ value: "suggestions",
7390
+ label: `Suggested from dependencies (${depMatches.length} found)`,
7391
+ hint: depMatches.map((m) => m.registryEntry.name).join(", ")
7392
+ }
7393
+ ] : [],
7394
+ { value: "browse", label: "Browse by category" },
7395
+ { value: "search", label: "Search by name" },
7396
+ ...selectedEntries.length > 0 ? [{ value: "install", label: `Install ${selectedEntries.length} selected` }] : [],
7397
+ { value: "done", label: selectedEntries.length > 0 ? "Cancel" : "Exit" }
7398
+ ]
7952
7399
  });
7953
- if (p3.isCancel(selected)) {
7954
- p3.cancel("Installation cancelled.");
7955
- process.exitCode = 0;
7956
- return;
7400
+ if (p3.isCancel(action) || action === "done") {
7401
+ if (selectedEntries.length > 0) p3.cancel("Installation cancelled.");
7402
+ else p3.outro("No skills selected.");
7403
+ return [];
7957
7404
  }
7958
- selectedSlugs = new Set(selected);
7959
- if (selectedSlugs.size === 0) {
7960
- p3.outro("No skills selected.");
7961
- return;
7405
+ if (action === "install") break;
7406
+ let picked = [];
7407
+ if (action === "suggestions") {
7408
+ picked = await pickFromList(
7409
+ depMatches.map((m) => m.registryEntry),
7410
+ projectDir,
7411
+ depSlugs
7412
+ );
7413
+ } else if (action === "browse") {
7414
+ picked = await browseByCategory(projectDir, depSlugs);
7415
+ } else if (action === "search") {
7416
+ picked = await searchByName(projectDir, depSlugs);
7962
7417
  }
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;
7418
+ for (const entry of picked) {
7419
+ if (!selectedEntries.some((e) => e.slug === entry.slug)) {
7420
+ selectedEntries.push(entry);
7421
+ }
7970
7422
  }
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."));
7423
+ }
7424
+ return selectedEntries;
7425
+ }
7426
+ async function browseByCategory(projectDir, depSlugs) {
7427
+ const allEntries = getAllEntries();
7428
+ const categoryMap = /* @__PURE__ */ new Map();
7429
+ for (const entry of allEntries) {
7430
+ categoryMap.set(entry.category, (categoryMap.get(entry.category) || 0) + 1);
7431
+ }
7432
+ const categoryChoice = await p3.select({
7433
+ message: "Select a category:",
7434
+ options: [...categoryMap.entries()].sort((a, b) => b[1] - a[1]).map(([cat, count]) => ({
7435
+ value: cat,
7436
+ label: cat,
7437
+ hint: `${count} entries`
7438
+ }))
7439
+ });
7440
+ if (p3.isCancel(categoryChoice)) return [];
7441
+ const categoryEntries = allEntries.filter((e) => e.category === categoryChoice);
7442
+ return pickFromList(categoryEntries, projectDir, depSlugs);
7443
+ }
7444
+ async function searchByName(projectDir, depSlugs) {
7445
+ const query = await p3.text({
7446
+ message: "Search for:",
7447
+ placeholder: "e.g. react, stripe, prisma..."
7448
+ });
7449
+ if (p3.isCancel(query) || !query) return [];
7450
+ const results = searchRegistry(query).slice(0, 20);
7451
+ if (results.length === 0) {
7452
+ p3.log.warn(`No results for "${query}"`);
7453
+ return [];
7454
+ }
7455
+ return pickFromList(results, projectDir, depSlugs);
7456
+ }
7457
+ async function pickFromList(entries2, projectDir, depSlugs) {
7458
+ const optionsList = entries2.map((entry) => {
7459
+ const already = isInstalled({ projectDir, slug: entry.slug });
7460
+ const isDep = depSlugs.has(entry.slug);
7461
+ const hints = [];
7462
+ if (already) hints.push("installed");
7463
+ if (isDep) hints.push("in your deps");
7464
+ hints.push(entry.category);
7465
+ return {
7466
+ value: entry.slug,
7467
+ label: entry.name,
7468
+ hint: hints.join(" \xB7 ")
7469
+ };
7470
+ });
7471
+ const selected = await p3.multiselect({
7472
+ message: `Select entries (${entries2.length} available):`,
7473
+ options: optionsList,
7474
+ required: false
7475
+ });
7476
+ if (p3.isCancel(selected)) return [];
7477
+ const slugSet = new Set(selected);
7478
+ return entries2.filter((e) => slugSet.has(e.slug));
7479
+ }
7480
+ async function pickFormat(entries2, options) {
7481
+ if (options.full) return "llms-full.txt";
7482
+ const hasFullAvailable = entries2.some((e) => e.llmsFullTxtUrl);
7483
+ if (!hasFullAvailable) return "llms.txt";
7484
+ const fullCount = entries2.filter((e) => e.llmsFullTxtUrl).length;
7485
+ const choice = await p3.select({
7486
+ message: "Which documentation format?",
7487
+ options: [
7488
+ {
7489
+ value: "llms.txt",
7490
+ label: "llms.txt",
7491
+ hint: "concise \u2014 smaller, faster to load"
7492
+ },
7493
+ {
7494
+ value: "llms-full.txt",
7495
+ label: "llms-full.txt",
7496
+ hint: `comprehensive \u2014 ${fullCount}/${entries2.length} selected have full version`
7497
+ }
7498
+ ]
7499
+ });
7500
+ if (p3.isCancel(choice)) return null;
7501
+ return choice;
7502
+ }
7503
+ async function pickAgents(projectDir) {
7504
+ const savedPrefs = loadSavedAgentPrefs(projectDir);
7505
+ const initialValues = getInitialAgents({ allAgents: agents, savedPrefs, projectDir });
7506
+ const selected = await p3.multiselect({
7507
+ message: "Which agents should receive the skills?",
7508
+ options: agents.map((a) => ({
7509
+ value: a.name,
7510
+ label: a.displayName,
7511
+ hint: a.isUniversal ? "always included" : a.skillsDir
7512
+ })),
7513
+ initialValues,
7514
+ required: true
7515
+ });
7516
+ if (p3.isCancel(selected)) return null;
7517
+ const finalNames = ensureUniversalAgents({ selected, allAgents: agents });
7518
+ saveAgentPrefs(projectDir, selected);
7519
+ const nameSet = new Set(finalNames);
7520
+ return agents.filter((a) => nameSet.has(a.name));
7521
+ }
7522
+ async function installEntries({
7523
+ projectDir,
7524
+ entries: entries2,
7525
+ options,
7526
+ agents: agents2,
7527
+ targetAgents
7528
+ }) {
7529
+ if (options.dryRun) {
7530
+ for (const entry of entries2) {
7531
+ const already = isInstalled({ projectDir, slug: entry.slug });
7532
+ const status = already ? pc4.dim(" (already installed)") : "";
7533
+ p3.log.message(` ${pc4.cyan(entry.name)}${status}`);
7977
7534
  }
7535
+ p3.outro("Dry run \u2014 no files were written");
7536
+ return;
7978
7537
  }
7979
7538
  const format = options.full ? "llms-full.txt" : "llms.txt";
7980
7539
  let installed = 0;
7981
7540
  let skipped = 0;
7982
7541
  let failed = 0;
7983
7542
  const installedSlugs = [];
7984
- for (const match of matches) {
7985
- if (!selectedSlugs.has(match.slug)) {
7986
- continue;
7987
- }
7988
- const entry = match.registryEntry;
7543
+ for (const entry of entries2) {
7989
7544
  const actualFormat = format === "llms-full.txt" && entry.llmsFullTxtUrl ? "llms-full.txt" : "llms.txt";
7990
7545
  const url = actualFormat === "llms-full.txt" ? entry.llmsFullTxtUrl : entry.llmsTxtUrl;
7991
- if (isInstalled({ projectDir, slug: match.slug })) {
7546
+ if (isInstalled({ projectDir, slug: entry.slug })) {
7992
7547
  skipped++;
7993
7548
  continue;
7994
7549
  }
7995
- const spin3 = spinner2(`Fetching ${entry.name}...`);
7996
- spin3.start();
7550
+ const spin = spinner2(`Fetching ${entry.name}...`);
7551
+ spin.start();
7997
7552
  try {
7998
7553
  const result = await fetchLlmsTxt({ url });
7999
7554
  const {
@@ -8002,15 +7557,16 @@ async function init(options) {
8002
7557
  agents: installedTo
8003
7558
  } = installToAgents({
8004
7559
  projectDir,
8005
- slug: match.slug,
7560
+ slug: entry.slug,
8006
7561
  entry,
8007
7562
  content: result.content,
8008
- format: actualFormat
7563
+ format: actualFormat,
7564
+ targetAgents
8009
7565
  });
8010
7566
  addEntry({
8011
7567
  projectDir,
8012
7568
  entry: {
8013
- slug: match.slug,
7569
+ slug: entry.slug,
8014
7570
  format: actualFormat,
8015
7571
  sourceUrl: url,
8016
7572
  etag: result.etag,
@@ -8021,12 +7577,12 @@ async function init(options) {
8021
7577
  name: entry.name
8022
7578
  }
8023
7579
  });
8024
- spin3.succeed(`${entry.name} ${pc4.dim(`\u2192 ${installedTo.join(", ")}`)}`);
7580
+ spin.succeed(`${entry.name} ${pc4.dim(`\u2192 ${installedTo.join(", ")}`)}`);
8025
7581
  installed++;
8026
- installedSlugs.push(match.slug);
7582
+ installedSlugs.push(entry.slug);
8027
7583
  } catch (err) {
8028
7584
  const msg = err instanceof Error ? err.message : String(err);
8029
- spin3.fail(`${entry.name}: ${msg}`);
7585
+ spin.fail(`${entry.name}: ${msg}`);
8030
7586
  failed++;
8031
7587
  }
8032
7588
  }
@@ -8038,7 +7594,7 @@ async function init(options) {
8038
7594
  p3.note(summaryLines.join("\n"), "Summary");
8039
7595
  }
8040
7596
  if (addToGitignore(projectDir)) {
8041
- p3.log.success("Added .llms/ and .agents/skills/ to .gitignore");
7597
+ p3.log.success("Added skill directories to .gitignore");
8042
7598
  }
8043
7599
  if (installed > 0) {
8044
7600
  syncClaudeMd(projectDir);
@@ -8068,8 +7624,34 @@ async function install({ names, options }) {
8068
7624
  await loadRegistry();
8069
7625
  spin.succeed("Registry loaded");
8070
7626
  const agents2 = detectInstalledAgents();
8071
- if (agents2.length > 0) {
8072
- p4.log.message(pc5.dim(`Detected: ${agents2.map((a) => a.displayName).join(", ")}`));
7627
+ let targetAgents;
7628
+ if (!process.stdin.isTTY) {
7629
+ targetAgents = agents2;
7630
+ if (agents2.length > 0) {
7631
+ const display = agents2.map((a) => a.displayName).join(", ");
7632
+ p4.log.message(pc5.dim(`Installing to: ${display}`));
7633
+ }
7634
+ } else {
7635
+ const savedPrefs = loadSavedAgentPrefs(projectDir);
7636
+ const initialValues = getInitialAgents({ allAgents: agents, savedPrefs, projectDir });
7637
+ const selected = await p4.multiselect({
7638
+ message: "Which agents should receive the skills?",
7639
+ options: agents.map((a) => ({
7640
+ value: a.name,
7641
+ label: a.displayName,
7642
+ hint: a.isUniversal ? "always included" : a.skillsDir
7643
+ })),
7644
+ initialValues,
7645
+ required: true
7646
+ });
7647
+ if (p4.isCancel(selected)) {
7648
+ p4.cancel("Installation cancelled.");
7649
+ return;
7650
+ }
7651
+ const finalNames = ensureUniversalAgents({ selected, allAgents: agents });
7652
+ saveAgentPrefs(projectDir, selected);
7653
+ const nameSet = new Set(finalNames);
7654
+ targetAgents = agents.filter((a) => nameSet.has(a.name));
8073
7655
  }
8074
7656
  let installed = 0;
8075
7657
  let failed = 0;
@@ -8122,7 +7704,14 @@ async function install({ names, options }) {
8122
7704
  checksum,
8123
7705
  size,
8124
7706
  agents: installedTo
8125
- } = installToAgents({ projectDir, slug: entry.slug, entry, content: result.content, format });
7707
+ } = installToAgents({
7708
+ projectDir,
7709
+ slug: entry.slug,
7710
+ entry,
7711
+ content: result.content,
7712
+ format,
7713
+ targetAgents
7714
+ });
8126
7715
  addEntry({
8127
7716
  projectDir,
8128
7717
  entry: {
@@ -8378,8 +7967,8 @@ async function update(name, options) {
8378
7967
 
8379
7968
  // src/index.ts
8380
7969
  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");
7970
+ program.name("llmstxt").description("Install llms.txt files from the llms-txt-hub registry into your AI coding tools").version("0.3.0").action(() => {
7971
+ printBanner("0.3.0");
8383
7972
  program.outputHelp();
8384
7973
  });
8385
7974
  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);