skill-tree 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 (130) hide show
  1. package/README.md +75 -0
  2. package/dist/bowser-CQI7RKRA.mjs +2821 -0
  3. package/dist/bowser-XBWM4HVL.mjs +2821 -0
  4. package/dist/chunk-2NL4MXNX.mjs +3156 -0
  5. package/dist/chunk-2STDJU5Y.mjs +1174 -0
  6. package/dist/chunk-3BCRI4CA.mjs +101 -0
  7. package/dist/chunk-3MV4GQ3N.mjs +19 -0
  8. package/dist/chunk-3SRB47JW.mjs +8344 -0
  9. package/dist/chunk-43YOKLZP.mjs +6081 -0
  10. package/dist/chunk-4AGZU52D.mjs +7918 -0
  11. package/dist/chunk-4HXHCEFH.mjs +9157 -0
  12. package/dist/chunk-4OC5QFIF.mjs +11267 -0
  13. package/dist/chunk-4QGSDVGH.mjs +580 -0
  14. package/dist/chunk-4TFMKAVC.mjs +1225 -0
  15. package/dist/chunk-55SMGVTP.mjs +7126 -0
  16. package/dist/chunk-5C4MEQMR.mjs +125 -0
  17. package/dist/chunk-6AZMD3Q3.mjs +1243 -0
  18. package/dist/chunk-6FX4IK4Z.mjs +5368 -0
  19. package/dist/chunk-6UPDN5QM.mjs +163 -0
  20. package/dist/chunk-7EGDKOHV.mjs +9439 -0
  21. package/dist/chunk-7IHYDFWW.mjs +163 -0
  22. package/dist/chunk-7LMOQW5H.mjs +4893 -0
  23. package/dist/chunk-7QIQJVNP.mjs +14206 -0
  24. package/dist/chunk-7VB4ZRZO.mjs +7127 -0
  25. package/dist/chunk-A3SILZYX.mjs +8360 -0
  26. package/dist/chunk-BPVRW25O.mjs +6089 -0
  27. package/dist/chunk-BZ2JKJ54.mjs +1057 -0
  28. package/dist/chunk-CI4476KM.mjs +6607 -0
  29. package/dist/chunk-DCRKELD5.mjs +46 -0
  30. package/dist/chunk-DDXYQ74I.mjs +13969 -0
  31. package/dist/chunk-DQOFJXBX.mjs +6595 -0
  32. package/dist/chunk-E2CVK23F.mjs +8751 -0
  33. package/dist/chunk-F3YEUQAP.mjs +654 -0
  34. package/dist/chunk-FKJJ4RJG.mjs +13874 -0
  35. package/dist/chunk-GBIK7WMX.mjs +9293 -0
  36. package/dist/chunk-GFK5SZRJ.mjs +580 -0
  37. package/dist/chunk-GPN6UQVR.mjs +9238 -0
  38. package/dist/chunk-II7DECZQ.mjs +9111 -0
  39. package/dist/chunk-INKVOZXK.mjs +15898 -0
  40. package/dist/chunk-J2JM7HAK.mjs +8787 -0
  41. package/dist/chunk-K6NRCSAZ.mjs +4355 -0
  42. package/dist/chunk-LACI6YL4.mjs +1379 -0
  43. package/dist/chunk-M4RPUUZT.mjs +3156 -0
  44. package/dist/chunk-MBF2MJS5.mjs +1230 -0
  45. package/dist/chunk-MBIGW6KU.mjs +644 -0
  46. package/dist/chunk-MR4TVINH.mjs +1234 -0
  47. package/dist/chunk-OOECXYLU.mjs +1379 -0
  48. package/dist/chunk-OYHYXKXO.mjs +7297 -0
  49. package/dist/chunk-P5GJJ4JB.mjs +9237 -0
  50. package/dist/chunk-PDPN7FW7.mjs +1045 -0
  51. package/dist/chunk-PJJJQXJL.mjs +1174 -0
  52. package/dist/chunk-PK3BAIFW.mjs +9294 -0
  53. package/dist/chunk-QNK3WYNA.mjs +8971 -0
  54. package/dist/chunk-QZ7TP4HQ.mjs +7 -0
  55. package/dist/chunk-RJYJGJO3.mjs +349 -0
  56. package/dist/chunk-T4PVQW5O.mjs +124 -0
  57. package/dist/chunk-TENXZJB3.mjs +349 -0
  58. package/dist/chunk-TEUB6DZR.mjs +6453 -0
  59. package/dist/chunk-TWPEHDW4.mjs +1067 -0
  60. package/dist/chunk-VHFTX33A.mjs +6724 -0
  61. package/dist/chunk-VNZSS2WY.mjs +1057 -0
  62. package/dist/chunk-WJP5XYS7.mjs +2102 -0
  63. package/dist/chunk-WX6N7KNO.mjs +1239 -0
  64. package/dist/chunk-Y54UK2J3.mjs +13071 -0
  65. package/dist/chunk-YDNGMDXC.mjs +9294 -0
  66. package/dist/chunk-YDVZIFIU.mjs +2102 -0
  67. package/dist/chunk-YJ6NZQLT.mjs +9237 -0
  68. package/dist/chunk-YWRKGXK4.mjs +9300 -0
  69. package/dist/chunk-ZI4AIAWQ.mjs +46 -0
  70. package/dist/chunk-ZQVS7MQK.mjs +6081 -0
  71. package/dist/chunk-ZYKRDDFO.mjs +163 -0
  72. package/dist/cli/index.js +1173 -324
  73. package/dist/cli/index.mjs +202 -9164
  74. package/dist/dist-es-27NPMXP7.mjs +22 -0
  75. package/dist/dist-es-2JG6ZWFR.mjs +69 -0
  76. package/dist/dist-es-2JGXQKUP.mjs +6077 -0
  77. package/dist/dist-es-5QD5QJS2.mjs +495 -0
  78. package/dist/dist-es-5ZD454R2.mjs +317 -0
  79. package/dist/dist-es-644EP2LP.mjs +317 -0
  80. package/dist/dist-es-DSNCHWLJ.mjs +170 -0
  81. package/dist/dist-es-DYHMPEKZ.mjs +170 -0
  82. package/dist/dist-es-FIVW7BUZ.mjs +317 -0
  83. package/dist/dist-es-GXJAFBE5.mjs +22 -0
  84. package/dist/dist-es-HRBPKDMR.mjs +935 -0
  85. package/dist/dist-es-L5AMJHSY.mjs +935 -0
  86. package/dist/dist-es-LHPJ63IO.mjs +4437 -0
  87. package/dist/dist-es-LT2AQAG7.mjs +4437 -0
  88. package/dist/dist-es-OK2J7EV3.mjs +378 -0
  89. package/dist/dist-es-ORE4PQTL.mjs +87 -0
  90. package/dist/dist-es-TLCYJJ25.mjs +495 -0
  91. package/dist/dist-es-V4LHTSRG.mjs +69 -0
  92. package/dist/dist-es-XFAHNA2L.mjs +69 -0
  93. package/dist/dist-es-XHTU3ZU2.mjs +935 -0
  94. package/dist/dist-es-XPNJAJI7.mjs +4437 -0
  95. package/dist/dist-es-Y2MPJ6IO.mjs +378 -0
  96. package/dist/dist-es-Y4JPNLF3.mjs +6077 -0
  97. package/dist/dist-es-ZGPJUGVW.mjs +87 -0
  98. package/dist/dist-es-ZYHLY2E6.mjs +487 -0
  99. package/dist/event-streams-6MFHPNRF.mjs +42 -0
  100. package/dist/event-streams-KIAAAC7Z.mjs +42 -0
  101. package/dist/index.d.mts +1189 -13
  102. package/dist/index.d.ts +1189 -13
  103. package/dist/index.js +38737 -601
  104. package/dist/index.mjs +131 -9693
  105. package/dist/lib-B245IUXF.mjs +778 -0
  106. package/dist/loadSso-CAWKILED.mjs +579 -0
  107. package/dist/loadSso-NPRY7QRT.mjs +579 -0
  108. package/dist/loadSso-OYKG6ZRE.mjs +579 -0
  109. package/dist/signin-KUENA7ZD.mjs +743 -0
  110. package/dist/signin-LMFNL434.mjs +665 -0
  111. package/dist/signin-LUKXFXSI.mjs +743 -0
  112. package/dist/sqlite-5LHEQTBD.mjs +7 -0
  113. package/dist/sqlite-BZK5GF76.mjs +7 -0
  114. package/dist/sqlite-MG45OOTV.mjs +6 -0
  115. package/dist/sqlite-OLU72GHB.mjs +6 -0
  116. package/dist/sqlite-RR2SJ3SR.mjs +7 -0
  117. package/dist/sqlite-V6GFGHTD.mjs +7 -0
  118. package/dist/sqlite-XJRPMNAJ.mjs +6 -0
  119. package/dist/sqlite-ZKQKQKPT.mjs +7 -0
  120. package/dist/sso-oidc-3VGFPMFD.mjs +832 -0
  121. package/dist/sso-oidc-NNH6SQIH.mjs +832 -0
  122. package/dist/sso-oidc-STZH2XK2.mjs +832 -0
  123. package/dist/sts-EF755UBF.mjs +6290 -0
  124. package/dist/sts-QGXULWRT.mjs +6290 -0
  125. package/dist/sts-ZIS4G6FQ.mjs +6290 -0
  126. package/dist/sync-4DCV43GA.mjs +15 -0
  127. package/dist/sync-BSWMMDA6.mjs +14 -0
  128. package/dist/sync-WHIIDHML.mjs +14 -0
  129. package/dist/sync-XRWFQYBY.mjs +15 -0
  130. package/package.json +9 -2
package/dist/cli/index.js CHANGED
@@ -30,20 +30,12 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
30
30
  mod
31
31
  ));
32
32
 
33
- // node_modules/tsup/assets/cjs_shims.js
34
- var init_cjs_shims = __esm({
35
- "node_modules/tsup/assets/cjs_shims.js"() {
36
- "use strict";
37
- }
38
- });
39
-
40
33
  // src/storage/base.ts
41
- var BaseStorageAdapter, MemoryStorageAdapter;
34
+ var _BaseStorageAdapter, BaseStorageAdapter, MemoryStorageAdapter;
42
35
  var init_base = __esm({
43
36
  "src/storage/base.ts"() {
44
37
  "use strict";
45
- init_cjs_shims();
46
- BaseStorageAdapter = class {
38
+ _BaseStorageAdapter = class _BaseStorageAdapter {
47
39
  constructor() {
48
40
  this.initialized = false;
49
41
  }
@@ -135,19 +127,123 @@ var init_base = __esm({
135
127
  * Simple text search across skill fields
136
128
  */
137
129
  textSearch(skills, query) {
138
- const lowerQuery = query.toLowerCase();
139
- const terms = lowerQuery.split(/\s+/).filter((t) => t.length > 0);
140
- return skills.filter((skill) => {
141
- const searchText = [
142
- skill.name,
143
- skill.description,
144
- skill.instructions,
145
- ...skill.tags
146
- ].join(" ").toLowerCase();
147
- return terms.every((term) => searchText.includes(term));
130
+ const queryTerms = this.tokenize(query);
131
+ if (queryTerms.length === 0) return [];
132
+ const N = skills.length;
133
+ const k1 = 1.5;
134
+ const b = 0.75;
135
+ const docs = skills.map((skill) => ({
136
+ skill,
137
+ terms: this.tokenize(
138
+ [skill.name, skill.description, ...skill.tags].join(" ")
139
+ )
140
+ }));
141
+ const avgDl = docs.reduce((sum, d) => sum + d.terms.length, 0) / (N || 1);
142
+ const df = /* @__PURE__ */ new Map();
143
+ for (const doc of docs) {
144
+ const unique = new Set(doc.terms);
145
+ for (const t of unique) {
146
+ df.set(t, (df.get(t) ?? 0) + 1);
147
+ }
148
+ }
149
+ const requiredTerms = [...new Set(queryTerms)].filter((qt) => (df.get(qt) ?? 0) > 0);
150
+ const scored = docs.map((doc) => {
151
+ let score = 0;
152
+ const tf = /* @__PURE__ */ new Map();
153
+ for (const t of doc.terms) {
154
+ tf.set(t, (tf.get(t) ?? 0) + 1);
155
+ }
156
+ const matchesAll = requiredTerms.every((qt) => (tf.get(qt) ?? 0) > 0);
157
+ if (!matchesAll) {
158
+ return { skill: doc.skill, score: 0 };
159
+ }
160
+ for (const qt of queryTerms) {
161
+ const termDf = df.get(qt) ?? 0;
162
+ if (termDf === 0) continue;
163
+ const idf = Math.log((N - termDf + 0.5) / (termDf + 0.5) + 1);
164
+ const termTf = tf.get(qt) ?? 0;
165
+ const tfNorm = termTf * (k1 + 1) / (termTf + k1 * (1 - b + b * doc.terms.length / avgDl));
166
+ score += idf * tfNorm;
167
+ }
168
+ return { skill: doc.skill, score };
148
169
  });
170
+ return scored.filter((s) => s.score > 0).sort((a, b2) => b2.score - a.score).map((s) => s.skill);
171
+ }
172
+ tokenize(text) {
173
+ return text.toLowerCase().split(/\W+/).filter((t) => t.length > 1 && !_BaseStorageAdapter.STOP_WORDS.has(t)).map((t) => _BaseStorageAdapter.stem(t));
174
+ }
175
+ static stem(word) {
176
+ if (word.length <= 3) return word;
177
+ let w = word;
178
+ w = w.replace(/ies$/, "y");
179
+ w = w.replace(/(ation|tion)$/, "t");
180
+ w = w.replace(/sion$/, "s");
181
+ w = w.replace(/(ing|ment|ness|able|ible|ous|ive|ful|less|ize|ise|ance|ence)$/, "");
182
+ w = w.replace(/([^aeiou])ed$/, "$1");
183
+ w = w.replace(/es$/, "");
184
+ w = w.replace(/([^s])s$/, "$1");
185
+ w = w.replace(/(.)\1$/, "$1");
186
+ if (w.length <= 1) return word;
187
+ return w;
149
188
  }
150
189
  };
190
+ _BaseStorageAdapter.STOP_WORDS = /* @__PURE__ */ new Set([
191
+ "the",
192
+ "a",
193
+ "an",
194
+ "is",
195
+ "are",
196
+ "was",
197
+ "were",
198
+ "be",
199
+ "been",
200
+ "being",
201
+ "have",
202
+ "has",
203
+ "had",
204
+ "do",
205
+ "does",
206
+ "did",
207
+ "will",
208
+ "would",
209
+ "could",
210
+ "should",
211
+ "may",
212
+ "might",
213
+ "shall",
214
+ "can",
215
+ "to",
216
+ "of",
217
+ "in",
218
+ "for",
219
+ "on",
220
+ "with",
221
+ "at",
222
+ "by",
223
+ "from",
224
+ "as",
225
+ "into",
226
+ "through",
227
+ "and",
228
+ "but",
229
+ "or",
230
+ "not",
231
+ "so",
232
+ "yet",
233
+ "if",
234
+ "when",
235
+ "that",
236
+ "this",
237
+ "it",
238
+ "its",
239
+ "also",
240
+ "which",
241
+ "what",
242
+ "how",
243
+ "why",
244
+ "where"
245
+ ]);
246
+ BaseStorageAdapter = _BaseStorageAdapter;
151
247
  MemoryStorageAdapter = class extends BaseStorageAdapter {
152
248
  constructor() {
153
249
  super(...arguments);
@@ -312,7 +408,6 @@ var import_better_sqlite3, path7, fs7, SCHEMA_VERSION, SQLiteStorageAdapter;
312
408
  var init_sqlite = __esm({
313
409
  "src/storage/sqlite.ts"() {
314
410
  "use strict";
315
- init_cjs_shims();
316
411
  import_better_sqlite3 = __toESM(require("better-sqlite3"));
317
412
  path7 = __toESM(require("path"));
318
413
  fs7 = __toESM(require("fs"));
@@ -930,15 +1025,15 @@ var init_sqlite = __esm({
930
1025
  /**
931
1026
  * Get or create a taxonomy node
932
1027
  */
933
- async ensureTaxonomyNode(path18) {
1028
+ async ensureTaxonomyNode(path19) {
934
1029
  this.ensureInitialized();
935
1030
  const db = this.getDb();
936
- const pathStr = path18.join("/");
1031
+ const pathStr = path19.join("/");
937
1032
  const existing = db.prepare("SELECT id FROM taxonomy_nodes WHERE path = ?").get(pathStr);
938
1033
  if (existing) return existing.id;
939
1034
  const id = `node-${pathStr.replace(/\//g, "-").toLowerCase()}`;
940
- const name = path18[path18.length - 1] || "Root";
941
- const parentPath = path18.slice(0, -1);
1035
+ const name = path19[path19.length - 1] || "Root";
1036
+ const parentPath = path19.slice(0, -1);
942
1037
  let parentId = null;
943
1038
  if (parentPath.length > 0) {
944
1039
  parentId = await this.ensureTaxonomyNode(parentPath);
@@ -1180,7 +1275,6 @@ var DEFAULT_AGENTS_CONFIG;
1180
1275
  var init_types = __esm({
1181
1276
  "src/agents/types.ts"() {
1182
1277
  "use strict";
1183
- init_cjs_shims();
1184
1278
  DEFAULT_AGENTS_CONFIG = {
1185
1279
  format: "xml",
1186
1280
  includeIds: true,
@@ -1195,7 +1289,6 @@ var AgentsGenerator;
1195
1289
  var init_generator = __esm({
1196
1290
  "src/agents/generator.ts"() {
1197
1291
  "use strict";
1198
- init_cjs_shims();
1199
1292
  init_types();
1200
1293
  AgentsGenerator = class {
1201
1294
  constructor(config2) {
@@ -1407,7 +1500,6 @@ var AgentsParser;
1407
1500
  var init_parser = __esm({
1408
1501
  "src/agents/parser.ts"() {
1409
1502
  "use strict";
1410
- init_cjs_shims();
1411
1503
  AgentsParser = class {
1412
1504
  /**
1413
1505
  * Parse AGENTS.md content
@@ -1669,7 +1761,6 @@ var fs9, path9, AgentsSync;
1669
1761
  var init_sync = __esm({
1670
1762
  "src/agents/sync.ts"() {
1671
1763
  "use strict";
1672
- init_cjs_shims();
1673
1764
  fs9 = __toESM(require("fs"));
1674
1765
  path9 = __toESM(require("path"));
1675
1766
  init_generator();
@@ -1839,17 +1930,9 @@ var init_sync = __esm({
1839
1930
  });
1840
1931
 
1841
1932
  // src/cli/index.ts
1842
- init_cjs_shims();
1843
- var import_commander38 = require("commander");
1844
-
1845
- // src/index.ts
1846
- init_cjs_shims();
1847
-
1848
- // src/skill-bank.ts
1849
- init_cjs_shims();
1933
+ var import_commander39 = require("commander");
1850
1934
 
1851
1935
  // src/types.ts
1852
- init_cjs_shims();
1853
1936
  function hasTaxonomySupport(storage) {
1854
1937
  return typeof storage.placeInTaxonomy === "function";
1855
1938
  }
@@ -1857,16 +1940,11 @@ function hasForkSupport(storage) {
1857
1940
  return typeof storage.recordFork === "function";
1858
1941
  }
1859
1942
 
1860
- // src/sync/sync-manager.ts
1861
- init_cjs_shims();
1862
-
1863
1943
  // src/sync/git-sync-adapter.ts
1864
- init_cjs_shims();
1865
1944
  var fs2 = __toESM(require("fs"));
1866
1945
  var path2 = __toESM(require("path"));
1867
1946
 
1868
1947
  // src/sync/conflict-store.ts
1869
- init_cjs_shims();
1870
1948
  var fs = __toESM(require("fs"));
1871
1949
  var path = __toESM(require("path"));
1872
1950
  var ConflictStore = class {
@@ -2914,14 +2992,7 @@ var SyncManager = class {
2914
2992
  }
2915
2993
  };
2916
2994
 
2917
- // src/serving/graph-server.ts
2918
- init_cjs_shims();
2919
-
2920
- // src/serving/catalog-renderer.ts
2921
- init_cjs_shims();
2922
-
2923
2995
  // src/serving/xml-utils.ts
2924
- init_cjs_shims();
2925
2996
  function escapeXml(text) {
2926
2997
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
2927
2998
  }
@@ -2953,15 +3024,12 @@ var DEFAULT_CONFIG = {
2953
3024
  maxSummaryLength: 80,
2954
3025
  format: "xml"
2955
3026
  };
2956
- var CatalogRenderer = class _CatalogRenderer {
3027
+ var _CatalogRenderer = class _CatalogRenderer {
2957
3028
  constructor(storage, config2) {
2958
3029
  this.storage = storage;
2959
3030
  this.overviewCache = null;
2960
3031
  this.config = { ...DEFAULT_CONFIG, ...config2 };
2961
3032
  }
2962
- static {
2963
- this.CACHE_TTL_MS = 6e4;
2964
- }
2965
3033
  /**
2966
3034
  * Render level-0 catalog overview for system prompt injection.
2967
3035
  * Shows top-level categories with counts. ~200 tokens.
@@ -2989,11 +3057,11 @@ var CatalogRenderer = class _CatalogRenderer {
2989
3057
  * Render a specific category path for browse drill-down.
2990
3058
  * Shows subcategories at intermediate nodes, or skill summaries at leaf nodes.
2991
3059
  */
2992
- async renderCategory(path18) {
3060
+ async renderCategory(path19) {
2993
3061
  if (hasCatalogSupport(this.storage)) {
2994
- return this.renderCategoryFromTaxonomy(this.storage, path18);
3062
+ return this.renderCategoryFromTaxonomy(this.storage, path19);
2995
3063
  }
2996
- return this.renderCategoryFromTags(path18);
3064
+ return this.renderCategoryFromTags(path19);
2997
3065
  }
2998
3066
  /**
2999
3067
  * Invalidate the overview cache (e.g., after skill changes).
@@ -3031,9 +3099,9 @@ var CatalogRenderer = class _CatalogRenderer {
3031
3099
  const categories = counted.sort((a, b) => b.count - a.count).slice(0, this.config.maxCategoriesPerLevel).map((c) => ({ name: c.node.name, count: c.count }));
3032
3100
  return this.renderOverviewXml(totalSkills, categories);
3033
3101
  }
3034
- async renderCategoryFromTaxonomy(storage, path18) {
3035
- const tree = await storage.getTaxonomyTree(path18);
3036
- const pathStr = path18.join("/");
3102
+ async renderCategoryFromTaxonomy(storage, path19) {
3103
+ const tree = await storage.getTaxonomyTree(path19);
3104
+ const pathStr = path19.join("/");
3037
3105
  if (tree.length > 0 && tree.some((n) => n.children.length > 0)) {
3038
3106
  const root = tree[0];
3039
3107
  const rootCount = this.countNodeSkills(root);
@@ -3043,7 +3111,7 @@ var CatalogRenderer = class _CatalogRenderer {
3043
3111
  lines.push(`<catalog_browse path="${escapeXml(pathStr)}" count="${rootCount}">`);
3044
3112
  lines.push(" <subcategories>");
3045
3113
  for (const { node: child, count } of children) {
3046
- const childPath = [...path18, child.name].join("/");
3114
+ const childPath = [...path19, child.name].join("/");
3047
3115
  lines.push(` <category path="${escapeXml(childPath)}" count="${count}" />`);
3048
3116
  }
3049
3117
  lines.push(" </subcategories>");
@@ -3067,13 +3135,13 @@ var CatalogRenderer = class _CatalogRenderer {
3067
3135
  const categories = Array.from(tagCounts.entries()).sort((a, b) => b[1] - a[1]).slice(0, this.config.maxCategoriesPerLevel).map(([name, count]) => ({ name, count }));
3068
3136
  return this.renderOverviewXml(skills.length, categories);
3069
3137
  }
3070
- async renderCategoryFromTags(path18) {
3071
- if (path18.length === 0) {
3138
+ async renderCategoryFromTags(path19) {
3139
+ if (path19.length === 0) {
3072
3140
  return this.renderOverviewFromTags();
3073
3141
  }
3074
- const tag = path18[0];
3142
+ const tag = path19[0];
3075
3143
  const matching = await this.storage.listSkills({ status: ["active"], tags: [tag] });
3076
- const pathStr = path18.join("/");
3144
+ const pathStr = path19.join("/");
3077
3145
  return this.renderLeafSkills(matching, pathStr, matching.length);
3078
3146
  }
3079
3147
  // ===========================================================================
@@ -3126,13 +3194,276 @@ var CatalogRenderer = class _CatalogRenderer {
3126
3194
  return count;
3127
3195
  }
3128
3196
  };
3197
+ _CatalogRenderer.CACHE_TTL_MS = 6e4;
3198
+ var CatalogRenderer = _CatalogRenderer;
3199
+
3200
+ // src/serving/term-similarity.ts
3201
+ var STOP_WORDS = /* @__PURE__ */ new Set([
3202
+ "the",
3203
+ "a",
3204
+ "an",
3205
+ "is",
3206
+ "are",
3207
+ "was",
3208
+ "were",
3209
+ "be",
3210
+ "been",
3211
+ "being",
3212
+ "have",
3213
+ "has",
3214
+ "had",
3215
+ "do",
3216
+ "does",
3217
+ "did",
3218
+ "will",
3219
+ "would",
3220
+ "could",
3221
+ "should",
3222
+ "may",
3223
+ "might",
3224
+ "shall",
3225
+ "can",
3226
+ "to",
3227
+ "of",
3228
+ "in",
3229
+ "for",
3230
+ "on",
3231
+ "with",
3232
+ "at",
3233
+ "by",
3234
+ "from",
3235
+ "as",
3236
+ "into",
3237
+ "through",
3238
+ "during",
3239
+ "before",
3240
+ "after",
3241
+ "and",
3242
+ "but",
3243
+ "or",
3244
+ "nor",
3245
+ "not",
3246
+ "so",
3247
+ "yet",
3248
+ "both",
3249
+ "either",
3250
+ "neither",
3251
+ "each",
3252
+ "every",
3253
+ "all",
3254
+ "any",
3255
+ "few",
3256
+ "more",
3257
+ "most",
3258
+ "other",
3259
+ "some",
3260
+ "such",
3261
+ "no",
3262
+ "only",
3263
+ "own",
3264
+ "same",
3265
+ "than",
3266
+ "too",
3267
+ "very",
3268
+ "just",
3269
+ "because",
3270
+ "if",
3271
+ "when",
3272
+ "that",
3273
+ "this",
3274
+ "it",
3275
+ "its",
3276
+ "also",
3277
+ "which",
3278
+ "what",
3279
+ "how",
3280
+ "why",
3281
+ "where"
3282
+ ]);
3283
+ function tokenizeList(text) {
3284
+ return text.toLowerCase().split(/\W+/).filter((t) => t.length > 2 && !STOP_WORDS.has(t));
3285
+ }
3286
+ function tokenize(text) {
3287
+ return new Set(tokenizeList(text));
3288
+ }
3289
+
3290
+ // src/serving/hybrid-retrieval.ts
3291
+ var DEFAULT_FIELD_WEIGHTS = {
3292
+ name: 10,
3293
+ description: 5,
3294
+ body: 5,
3295
+ tags: 3
3296
+ };
3297
+ var DEFAULTS = {
3298
+ rrfK: 60,
3299
+ bm25K1: 1.2,
3300
+ bm25B: 0.75,
3301
+ bm25Saturation: 8,
3302
+ signalWeights: { lexical: 0.5, dense: 0.5 }
3303
+ };
3304
+ function buildFieldDoc(skill, fw) {
3305
+ const fields = [
3306
+ [fw.name, tokenizeList(skill.name)],
3307
+ [fw.description, tokenizeList(skill.description)],
3308
+ [fw.body, tokenizeList(skill.instructions ?? "")],
3309
+ [fw.tags, skill.tags.flatMap((t) => tokenizeList(t))]
3310
+ ];
3311
+ const wtf = /* @__PURE__ */ new Map();
3312
+ let dl = 0;
3313
+ for (const [weight, toks] of fields) {
3314
+ if (weight <= 0) continue;
3315
+ dl += weight * toks.length;
3316
+ for (const t of toks) wtf.set(t, (wtf.get(t) ?? 0) + weight);
3317
+ }
3318
+ return { id: skill.id, wtf, dl };
3319
+ }
3320
+ function bm25Scores(query, skills, fieldWeights = DEFAULT_FIELD_WEIGHTS, k1 = DEFAULTS.bm25K1, b = DEFAULTS.bm25B) {
3321
+ const scores = /* @__PURE__ */ new Map();
3322
+ if (skills.length === 0) return scores;
3323
+ const queryTerms = [...tokenize(query)];
3324
+ const docs = skills.map((s) => buildFieldDoc(s, fieldWeights));
3325
+ const N = docs.length;
3326
+ const avgdl = docs.reduce((sum, d) => sum + d.dl, 0) / N || 1;
3327
+ const df = /* @__PURE__ */ new Map();
3328
+ for (const t of queryTerms) {
3329
+ let count = 0;
3330
+ for (const d of docs) if (d.wtf.has(t)) count++;
3331
+ df.set(t, count);
3332
+ }
3333
+ for (const d of docs) {
3334
+ let score = 0;
3335
+ for (const t of queryTerms) {
3336
+ const f = d.wtf.get(t);
3337
+ if (!f) continue;
3338
+ const n = df.get(t);
3339
+ const idf = Math.log(1 + (N - n + 0.5) / (n + 0.5));
3340
+ const denom = f + k1 * (1 - b + b * (d.dl / avgdl));
3341
+ score += idf * (f * (k1 + 1) / (denom || 1));
3342
+ }
3343
+ scores.set(d.id, score);
3344
+ }
3345
+ return scores;
3346
+ }
3347
+ function cosineSimilarity(a, b) {
3348
+ const len = Math.min(a.length, b.length);
3349
+ let dot = 0;
3350
+ let na = 0;
3351
+ let nb = 0;
3352
+ for (let i = 0; i < len; i++) {
3353
+ dot += a[i] * b[i];
3354
+ na += a[i] * a[i];
3355
+ nb += b[i] * b[i];
3356
+ }
3357
+ if (na === 0 || nb === 0) return 0;
3358
+ return dot / (Math.sqrt(na) * Math.sqrt(nb));
3359
+ }
3360
+ function reciprocalRankFusion(rankings, k = DEFAULTS.rrfK) {
3361
+ const fused = /* @__PURE__ */ new Map();
3362
+ for (const ranking of rankings) {
3363
+ ranking.forEach((id, idx) => {
3364
+ fused.set(id, (fused.get(id) ?? 0) + 1 / (k + idx + 1));
3365
+ });
3366
+ }
3367
+ return fused;
3368
+ }
3369
+ function skillEmbedText(skill) {
3370
+ return [
3371
+ skill.name,
3372
+ skill.description,
3373
+ skill.tags.join(" "),
3374
+ skill.instructions ?? ""
3375
+ ].join("\n");
3376
+ }
3377
+ function rankByScore(scores) {
3378
+ return [...scores.entries()].sort((a, b) => b[1] - a[1]).map(([id]) => id);
3379
+ }
3380
+ async function scoreSkillsHybrid(query, skills, options = {}) {
3381
+ if (skills.length === 0) return [];
3382
+ const fieldWeights = options.fieldWeights ?? DEFAULT_FIELD_WEIGHTS;
3383
+ const sat = options.bm25Saturation ?? DEFAULTS.bm25Saturation;
3384
+ const raw = bm25Scores(
3385
+ query,
3386
+ skills,
3387
+ fieldWeights,
3388
+ options.bm25K1 ?? DEFAULTS.bm25K1,
3389
+ options.bm25B ?? DEFAULTS.bm25B
3390
+ );
3391
+ const lexAbs = /* @__PURE__ */ new Map();
3392
+ for (const s of skills) {
3393
+ const r = raw.get(s.id) ?? 0;
3394
+ lexAbs.set(s.id, r / (r + sat));
3395
+ }
3396
+ let denseAbs = null;
3397
+ if (options.embedder) {
3398
+ try {
3399
+ const vectors = await options.embedder.embed([
3400
+ query,
3401
+ ...skills.map(skillEmbedText)
3402
+ ]);
3403
+ const queryVec = vectors[0];
3404
+ if (queryVec && vectors.length === skills.length + 1) {
3405
+ denseAbs = /* @__PURE__ */ new Map();
3406
+ skills.forEach((s, i) => {
3407
+ denseAbs.set(s.id, Math.max(0, cosineSimilarity(queryVec, vectors[i + 1])));
3408
+ });
3409
+ }
3410
+ } catch {
3411
+ denseAbs = null;
3412
+ }
3413
+ }
3414
+ const sw = options.signalWeights ?? DEFAULTS.signalWeights;
3415
+ const wLex = sw.lexical;
3416
+ const wDense = denseAbs ? sw.dense : 0;
3417
+ const wSum = wLex + wDense || 1;
3418
+ const useUtility = !!options.utilityScorer?.trained;
3419
+ const scored = skills.map((s) => {
3420
+ const l = lexAbs.get(s.id) ?? 0;
3421
+ const d = denseAbs?.get(s.id) ?? 0;
3422
+ const relevanceScore = useUtility ? options.utilityScorer.score({ skillId: s.id, lexAbs: l, denseAbs: d }) : (wLex * l + wDense * d) / wSum;
3423
+ return { skill: s, relevanceScore };
3424
+ });
3425
+ if (!useUtility && (options.fusion ?? "weighted") === "rrf" && denseAbs) {
3426
+ const rrf = reciprocalRankFusion(
3427
+ [rankByScore(lexAbs), rankByScore(denseAbs)],
3428
+ options.rrfK ?? DEFAULTS.rrfK
3429
+ );
3430
+ scored.sort(
3431
+ (a, b) => (rrf.get(b.skill.id) ?? 0) - (rrf.get(a.skill.id) ?? 0) || b.relevanceScore - a.relevanceScore
3432
+ );
3433
+ } else {
3434
+ scored.sort((a, b) => b.relevanceScore - a.relevanceScore);
3435
+ }
3436
+ if (options.reranker) {
3437
+ const topN = options.rerankTopN ?? 20;
3438
+ const head = scored.slice(0, topN);
3439
+ if (head.length > 1) {
3440
+ const ranked = await options.reranker.rerank(
3441
+ query,
3442
+ head.map((h) => ({
3443
+ id: h.skill.id,
3444
+ text: `${h.skill.name}
3445
+ ${h.skill.description}
3446
+ ${h.skill.instructions ?? ""}`
3447
+ }))
3448
+ );
3449
+ const byId = new Map(head.map((h) => [h.skill.id, h.skill]));
3450
+ const fusedDesc = head.map((h) => h.relevanceScore);
3451
+ const reordered = ranked.filter((r) => byId.has(r.id)).map((r, i) => ({ skill: byId.get(r.id), relevanceScore: fusedDesc[i] ?? 0 }));
3452
+ const headIds = new Set(reordered.map((s) => s.skill.id));
3453
+ const tail = scored.filter((s) => !headIds.has(s.skill.id));
3454
+ return [...reordered, ...tail];
3455
+ }
3456
+ }
3457
+ return scored;
3458
+ }
3129
3459
 
3130
3460
  // src/serving/loadout-compiler.ts
3131
- init_cjs_shims();
3132
3461
  var DEFAULT_CONFIG2 = {
3133
3462
  defaultMaxSkills: 15,
3134
3463
  defaultStatus: ["active"],
3135
- semanticThreshold: 0.6
3464
+ semanticThreshold: 0.6,
3465
+ retrieval: {},
3466
+ scoringPoolSize: 200
3136
3467
  };
3137
3468
  var LoadoutCompiler = class {
3138
3469
  constructor(storage, config2) {
@@ -3184,6 +3515,66 @@ var LoadoutCompiler = class {
3184
3515
  maxSkills: this.config.defaultMaxSkills
3185
3516
  });
3186
3517
  }
3518
+ /**
3519
+ * Compile with hybrid-retrieval scoring against a task description
3520
+ * (Tier 1). Returns skills annotated with absolute relevance scores in
3521
+ * [0,1], sorted by descending relevance. Used by the hybrid loadout
3522
+ * strategy to determine which skills should be auto-expanded vs shown
3523
+ * as summaries vs excluded.
3524
+ *
3525
+ * Scoring uses field-weighted BM25 over the skill name/description/body/
3526
+ * tags (the body matters most), optionally fused with dense embeddings
3527
+ * (when an `embedder` is
3528
+ * configured via `retrieval`). The candidate pool is the (filtered) set
3529
+ * up to `scoringPoolSize` — larger than the final loadout — so the ranker
3530
+ * re-ranks a real corpus rather than only a lexically pre-truncated top-N.
3531
+ */
3532
+ async compileWithScoring(taskDescription, criteria) {
3533
+ const baseCriteria = {
3534
+ ...criteria,
3535
+ taskDescription,
3536
+ // Score a broad pool, not the final loadout size. Callers narrow the
3537
+ // result via partitionByConfidence + maxExpanded downstream.
3538
+ maxSkills: criteria?.maxSkills ?? this.config.scoringPoolSize
3539
+ };
3540
+ const skills = await this.compile(baseCriteria);
3541
+ return scoreSkillsHybrid(taskDescription, skills, this.config.retrieval);
3542
+ }
3543
+ /**
3544
+ * Partition scored skills into confidence tiers.
3545
+ * - High confidence (>= expandAbove): should be auto-expanded
3546
+ * - Medium confidence (>= includeAbove): included as summaries
3547
+ * - Below includeAbove: excluded
3548
+ *
3549
+ * Abstain floor (Tier 1, T1.3): if `thresholds.minConfidence` is set and
3550
+ * even the single best-scoring skill is below it, the whole loadout
3551
+ * abstains — every skill is excluded and **nothing** is injected. This
3552
+ * makes "no sufficiently relevant skill" a first-class outcome (B=0),
3553
+ * which prevents irrelevant skills from dragging task success below the
3554
+ * no-skill baseline. `scored` is expected to be sorted descending, but we
3555
+ * defensively take the max rather than assume order.
3556
+ */
3557
+ partitionByConfidence(scored, thresholds) {
3558
+ const expand = [];
3559
+ const summarize = [];
3560
+ const excluded = [];
3561
+ if (thresholds.minConfidence !== void 0) {
3562
+ const topScore = scored.reduce((m, s) => Math.max(m, s.relevanceScore), 0);
3563
+ if (scored.length === 0 || topScore < thresholds.minConfidence) {
3564
+ return { expand, summarize, excluded: [...scored] };
3565
+ }
3566
+ }
3567
+ for (const item of scored) {
3568
+ if (item.relevanceScore >= thresholds.expandAbove) {
3569
+ expand.push(item);
3570
+ } else if (item.relevanceScore >= thresholds.includeAbove) {
3571
+ summarize.push(item);
3572
+ } else {
3573
+ excluded.push(item);
3574
+ }
3575
+ }
3576
+ return { expand, summarize, excluded };
3577
+ }
3187
3578
  /**
3188
3579
  * Compile from a named profile
3189
3580
  */
@@ -3255,13 +3646,31 @@ var LoadoutCompiler = class {
3255
3646
  return result;
3256
3647
  }
3257
3648
  /**
3258
- * Apply semantic filters (task description, problem context, etc.)
3649
+ * Apply semantic filters (task description matching).
3259
3650
  *
3260
- * Currently returns skills unchanged. Semantic matching was removed;
3261
- * use SQLite FTS via storage.searchSkills() for keyword-based search.
3651
+ * When `taskDescription` is provided, uses storage.searchSkills()
3652
+ * to find matching skills and boosts them to the front. Skills not
3653
+ * matching the search are retained at lower priority so that tag
3654
+ * filters and explicit includes still work.
3262
3655
  */
3263
- async applySemanticFilters(skills, _criteria) {
3264
- return skills;
3656
+ async applySemanticFilters(skills, criteria) {
3657
+ if (!criteria.taskDescription) return skills;
3658
+ const searchResults = await this.storage.searchSkills(criteria.taskDescription);
3659
+ const searchIds = new Set(searchResults.map((s) => s.id));
3660
+ const candidateIds = new Set(skills.map((s) => s.id));
3661
+ const boosted = [];
3662
+ const rest = [];
3663
+ for (const s of searchResults) {
3664
+ if (candidateIds.has(s.id)) {
3665
+ boosted.push(s);
3666
+ }
3667
+ }
3668
+ for (const s of skills) {
3669
+ if (!searchIds.has(s.id)) {
3670
+ rest.push(s);
3671
+ }
3672
+ }
3673
+ return [...boosted, ...rest];
3265
3674
  }
3266
3675
  /**
3267
3676
  * Apply relationship-based filters (root skills, dependencies)
@@ -3347,8 +3756,6 @@ var LoadoutCompiler = class {
3347
3756
  */
3348
3757
  applyLimits(skills, criteria) {
3349
3758
  let result = skills;
3350
- if (criteria.priorityOrder === "relevance") {
3351
- }
3352
3759
  const maxSkills = criteria.maxSkills ?? this.config.defaultMaxSkills;
3353
3760
  if (result.length > maxSkills) {
3354
3761
  result = result.slice(0, maxSkills);
@@ -3384,79 +3791,13 @@ var LoadoutCompiler = class {
3384
3791
  };
3385
3792
 
3386
3793
  // src/serving/project-detector.ts
3387
- init_cjs_shims();
3388
3794
  var import_fs = require("fs");
3389
3795
  var import_path = require("path");
3390
- var ProjectDetector = class _ProjectDetector {
3796
+ var _ProjectDetector = class _ProjectDetector {
3391
3797
  constructor() {
3392
3798
  /** Cache for project context */
3393
3799
  this.cache = /* @__PURE__ */ new Map();
3394
3800
  }
3395
- static {
3396
- /** Project type patterns */
3397
- this.PROJECT_TYPES = [
3398
- { manifestFile: "package.json", type: "nodejs", tags: ["nodejs", "javascript"], packageManager: "npm" },
3399
- { manifestFile: "pyproject.toml", type: "python", tags: ["python"], packageManager: "pip" },
3400
- { manifestFile: "requirements.txt", type: "python", tags: ["python"], packageManager: "pip" },
3401
- { manifestFile: "Cargo.toml", type: "rust", tags: ["rust"], packageManager: "cargo" },
3402
- { manifestFile: "go.mod", type: "go", tags: ["go", "golang"] },
3403
- { manifestFile: "pom.xml", type: "java", tags: ["java", "maven"], packageManager: "maven" },
3404
- { manifestFile: "build.gradle", type: "java", tags: ["java", "gradle"], packageManager: "gradle" },
3405
- { manifestFile: "build.gradle.kts", type: "kotlin", tags: ["kotlin", "gradle"], packageManager: "gradle" }
3406
- ];
3407
- }
3408
- static {
3409
- /** TypeScript detection */
3410
- this.TYPESCRIPT_FILES = ["tsconfig.json", "tsconfig.base.json"];
3411
- }
3412
- static {
3413
- /** Node.js framework patterns */
3414
- this.NODE_FRAMEWORKS = [
3415
- { name: "react", packageName: "react", tags: ["react", "frontend"] },
3416
- { name: "next", packageName: "next", tags: ["nextjs", "react", "fullstack"] },
3417
- { name: "vue", packageName: "vue", tags: ["vue", "frontend"] },
3418
- { name: "nuxt", packageName: "nuxt", tags: ["nuxt", "vue", "fullstack"] },
3419
- { name: "angular", packageName: "@angular/core", tags: ["angular", "frontend"] },
3420
- { name: "svelte", packageName: "svelte", tags: ["svelte", "frontend"] },
3421
- { name: "express", packageName: "express", tags: ["express", "backend", "api"] },
3422
- { name: "fastify", packageName: "fastify", tags: ["fastify", "backend", "api"] },
3423
- { name: "nestjs", packageName: "@nestjs/core", tags: ["nestjs", "backend", "api"] },
3424
- { name: "hono", packageName: "hono", tags: ["hono", "backend", "api"] },
3425
- { name: "prisma", packageName: "@prisma/client", tags: ["prisma", "database", "orm"] },
3426
- { name: "drizzle", packageName: "drizzle-orm", tags: ["drizzle", "database", "orm"] },
3427
- { name: "typeorm", packageName: "typeorm", tags: ["typeorm", "database", "orm"] },
3428
- { name: "jest", packageName: "jest", tags: ["testing", "jest"] },
3429
- { name: "vitest", packageName: "vitest", tags: ["testing", "vitest"] },
3430
- { name: "playwright", packageName: "@playwright/test", tags: ["testing", "e2e", "playwright"] },
3431
- { name: "cypress", packageName: "cypress", tags: ["testing", "e2e", "cypress"] }
3432
- ];
3433
- }
3434
- static {
3435
- /** Python framework patterns (from pyproject.toml or requirements.txt) */
3436
- this.PYTHON_FRAMEWORKS = [
3437
- { name: "fastapi", packageName: "fastapi", tags: ["fastapi", "backend", "api"] },
3438
- { name: "django", packageName: "django", tags: ["django", "backend", "fullstack"] },
3439
- { name: "flask", packageName: "flask", tags: ["flask", "backend", "api"] },
3440
- { name: "sqlalchemy", packageName: "sqlalchemy", tags: ["sqlalchemy", "database", "orm"] },
3441
- { name: "pytest", packageName: "pytest", tags: ["testing", "pytest"] },
3442
- { name: "pydantic", packageName: "pydantic", tags: ["pydantic", "validation"] }
3443
- ];
3444
- }
3445
- static {
3446
- /** Directory patterns */
3447
- this.DIRECTORY_PATTERNS = [
3448
- { pattern: ".github/workflows", feature: "github-actions", tags: ["ci", "github-actions"] },
3449
- { pattern: ".gitlab-ci.yml", feature: "gitlab-ci", tags: ["ci", "gitlab"] },
3450
- { pattern: "Dockerfile", feature: "docker", tags: ["docker", "containers"] },
3451
- { pattern: "docker-compose.yml", feature: "docker-compose", tags: ["docker", "containers"] },
3452
- { pattern: "docker-compose.yaml", feature: "docker-compose", tags: ["docker", "containers"] },
3453
- { pattern: "terraform", feature: "terraform", tags: ["terraform", "infrastructure"] },
3454
- { pattern: "kubernetes", feature: "kubernetes", tags: ["kubernetes", "infrastructure"] },
3455
- { pattern: "k8s", feature: "kubernetes", tags: ["kubernetes", "infrastructure"] },
3456
- { pattern: ".env.example", feature: "env-config", tags: ["configuration"] },
3457
- { pattern: "prisma/schema.prisma", feature: "prisma", tags: ["prisma", "database"] }
3458
- ];
3459
- }
3460
3801
  /**
3461
3802
  * Detect project context from a directory
3462
3803
  */
@@ -3618,9 +3959,64 @@ var ProjectDetector = class _ProjectDetector {
3618
3959
  }
3619
3960
  }
3620
3961
  };
3962
+ /** Project type patterns */
3963
+ _ProjectDetector.PROJECT_TYPES = [
3964
+ { manifestFile: "package.json", type: "nodejs", tags: ["nodejs", "javascript"], packageManager: "npm" },
3965
+ { manifestFile: "pyproject.toml", type: "python", tags: ["python"], packageManager: "pip" },
3966
+ { manifestFile: "requirements.txt", type: "python", tags: ["python"], packageManager: "pip" },
3967
+ { manifestFile: "Cargo.toml", type: "rust", tags: ["rust"], packageManager: "cargo" },
3968
+ { manifestFile: "go.mod", type: "go", tags: ["go", "golang"] },
3969
+ { manifestFile: "pom.xml", type: "java", tags: ["java", "maven"], packageManager: "maven" },
3970
+ { manifestFile: "build.gradle", type: "java", tags: ["java", "gradle"], packageManager: "gradle" },
3971
+ { manifestFile: "build.gradle.kts", type: "kotlin", tags: ["kotlin", "gradle"], packageManager: "gradle" }
3972
+ ];
3973
+ /** TypeScript detection */
3974
+ _ProjectDetector.TYPESCRIPT_FILES = ["tsconfig.json", "tsconfig.base.json"];
3975
+ /** Node.js framework patterns */
3976
+ _ProjectDetector.NODE_FRAMEWORKS = [
3977
+ { name: "react", packageName: "react", tags: ["react", "frontend"] },
3978
+ { name: "next", packageName: "next", tags: ["nextjs", "react", "fullstack"] },
3979
+ { name: "vue", packageName: "vue", tags: ["vue", "frontend"] },
3980
+ { name: "nuxt", packageName: "nuxt", tags: ["nuxt", "vue", "fullstack"] },
3981
+ { name: "angular", packageName: "@angular/core", tags: ["angular", "frontend"] },
3982
+ { name: "svelte", packageName: "svelte", tags: ["svelte", "frontend"] },
3983
+ { name: "express", packageName: "express", tags: ["express", "backend", "api"] },
3984
+ { name: "fastify", packageName: "fastify", tags: ["fastify", "backend", "api"] },
3985
+ { name: "nestjs", packageName: "@nestjs/core", tags: ["nestjs", "backend", "api"] },
3986
+ { name: "hono", packageName: "hono", tags: ["hono", "backend", "api"] },
3987
+ { name: "prisma", packageName: "@prisma/client", tags: ["prisma", "database", "orm"] },
3988
+ { name: "drizzle", packageName: "drizzle-orm", tags: ["drizzle", "database", "orm"] },
3989
+ { name: "typeorm", packageName: "typeorm", tags: ["typeorm", "database", "orm"] },
3990
+ { name: "jest", packageName: "jest", tags: ["testing", "jest"] },
3991
+ { name: "vitest", packageName: "vitest", tags: ["testing", "vitest"] },
3992
+ { name: "playwright", packageName: "@playwright/test", tags: ["testing", "e2e", "playwright"] },
3993
+ { name: "cypress", packageName: "cypress", tags: ["testing", "e2e", "cypress"] }
3994
+ ];
3995
+ /** Python framework patterns (from pyproject.toml or requirements.txt) */
3996
+ _ProjectDetector.PYTHON_FRAMEWORKS = [
3997
+ { name: "fastapi", packageName: "fastapi", tags: ["fastapi", "backend", "api"] },
3998
+ { name: "django", packageName: "django", tags: ["django", "backend", "fullstack"] },
3999
+ { name: "flask", packageName: "flask", tags: ["flask", "backend", "api"] },
4000
+ { name: "sqlalchemy", packageName: "sqlalchemy", tags: ["sqlalchemy", "database", "orm"] },
4001
+ { name: "pytest", packageName: "pytest", tags: ["testing", "pytest"] },
4002
+ { name: "pydantic", packageName: "pydantic", tags: ["pydantic", "validation"] }
4003
+ ];
4004
+ /** Directory patterns */
4005
+ _ProjectDetector.DIRECTORY_PATTERNS = [
4006
+ { pattern: ".github/workflows", feature: "github-actions", tags: ["ci", "github-actions"] },
4007
+ { pattern: ".gitlab-ci.yml", feature: "gitlab-ci", tags: ["ci", "gitlab"] },
4008
+ { pattern: "Dockerfile", feature: "docker", tags: ["docker", "containers"] },
4009
+ { pattern: "docker-compose.yml", feature: "docker-compose", tags: ["docker", "containers"] },
4010
+ { pattern: "docker-compose.yaml", feature: "docker-compose", tags: ["docker", "containers"] },
4011
+ { pattern: "terraform", feature: "terraform", tags: ["terraform", "infrastructure"] },
4012
+ { pattern: "kubernetes", feature: "kubernetes", tags: ["kubernetes", "infrastructure"] },
4013
+ { pattern: "k8s", feature: "kubernetes", tags: ["kubernetes", "infrastructure"] },
4014
+ { pattern: ".env.example", feature: "env-config", tags: ["configuration"] },
4015
+ { pattern: "prisma/schema.prisma", feature: "prisma", tags: ["prisma", "database"] }
4016
+ ];
4017
+ var ProjectDetector = _ProjectDetector;
3621
4018
 
3622
4019
  // src/serving/view-renderer.ts
3623
- init_cjs_shims();
3624
4020
  var DEFAULT_CONFIG3 = {
3625
4021
  includeTokenEstimates: false,
3626
4022
  maxSummaryLength: 150
@@ -3672,6 +4068,9 @@ var ViewRenderer = class {
3672
4068
  lines.push(`<skill id="${this.escapeXml(id)}" state="available">`);
3673
4069
  lines.push(` <name>${this.escapeXml(skill.name)}</name>`);
3674
4070
  lines.push(` <description>${this.escapeXml(summary)}</description>`);
4071
+ if (skill.serving?.instructionPreview) {
4072
+ lines.push(` <key_insight>${this.escapeXml(skill.serving.instructionPreview)}</key_insight>`);
4073
+ }
3675
4074
  if (skill.tags.length > 0) {
3676
4075
  lines.push(` <tags>${skill.tags.map((t) => this.escapeXml(t)).join(", ")}</tags>`);
3677
4076
  }
@@ -3810,13 +4209,15 @@ var ViewRenderer = class {
3810
4209
  */
3811
4210
  estimateSummaryTokens(skill) {
3812
4211
  const summary = this.getSummary(skill);
3813
- const content = [skill.name, summary, skill.tags.join(" ")].join(" ");
3814
- return Math.ceil(content.length / 4);
4212
+ const parts = [skill.name, summary, skill.tags.join(" ")];
4213
+ if (skill.serving?.instructionPreview) {
4214
+ parts.push(skill.serving.instructionPreview);
4215
+ }
4216
+ return Math.ceil(parts.join(" ").length / 4);
3815
4217
  }
3816
4218
  };
3817
4219
 
3818
4220
  // src/serving/profiles/index.ts
3819
- init_cjs_shims();
3820
4221
  var codeReviewProfile = {
3821
4222
  tags: ["review", "quality", "security", "best-practices"],
3822
4223
  taskDescription: "review code for quality, security, and best practices",
@@ -3879,6 +4280,10 @@ var builtInProfiles = {
3879
4280
  };
3880
4281
 
3881
4282
  // src/serving/graph-server.ts
4283
+ var DEFAULT_CONFIDENCE_THRESHOLDS = {
4284
+ expandAbove: 0.3,
4285
+ includeAbove: 0.15
4286
+ };
3882
4287
  var DEFAULT_CONFIG4 = {
3883
4288
  agentCanModify: true,
3884
4289
  agentCanSetLoadout: false,
@@ -3886,8 +4291,16 @@ var DEFAULT_CONFIG4 = {
3886
4291
  requireApproval: false,
3887
4292
  autoExpandOnUse: true,
3888
4293
  autoExpandRelated: true,
3889
- maxExpanded: 5,
4294
+ // Default cap on simultaneously-expanded (full-body) skills. Set to 3:
4295
+ // on the SkillsBench selection benchmark the true-positive skill lands in
4296
+ // the top 3 every time, so 3 (vs 5) lifts loadout precision ~23%→38% with
4297
+ // zero recall loss — fewer irrelevant bodies injected into context.
4298
+ maxExpanded: 3,
3890
4299
  evictionStrategy: "lru",
4300
+ confidenceThresholds: DEFAULT_CONFIDENCE_THRESHOLDS,
4301
+ retrieval: {},
4302
+ scoringPoolSize: 200,
4303
+ deferExpansion: false,
3891
4304
  persistState: false,
3892
4305
  outputFormat: "xml",
3893
4306
  includeTokenEstimates: false,
@@ -3895,18 +4308,22 @@ var DEFAULT_CONFIG4 = {
3895
4308
  profiles: {}
3896
4309
  };
3897
4310
  var SkillGraphServer = class {
3898
- // Track LRU for eviction
3899
4311
  constructor(storage, config2) {
3900
4312
  this.storage = storage;
3901
4313
  this.catalogRenderer = null;
3902
4314
  this.handlers = /* @__PURE__ */ new Set();
3903
4315
  this.lruOrder = [];
4316
+ this.relevanceScores = /* @__PURE__ */ new Map();
3904
4317
  this.config = {
3905
4318
  ...DEFAULT_CONFIG4,
3906
4319
  ...config2,
3907
- profiles: { ...builtInProfiles, ...DEFAULT_CONFIG4.profiles, ...config2?.profiles }
4320
+ profiles: { ...builtInProfiles, ...DEFAULT_CONFIG4.profiles, ...config2?.profiles },
4321
+ confidenceThresholds: { ...DEFAULT_CONFIDENCE_THRESHOLDS, ...config2?.confidenceThresholds }
3908
4322
  };
3909
- this.compiler = new LoadoutCompiler(storage);
4323
+ this.compiler = new LoadoutCompiler(storage, {
4324
+ retrieval: this.config.retrieval,
4325
+ scoringPoolSize: this.config.scoringPoolSize
4326
+ });
3910
4327
  this.projectDetector = new ProjectDetector();
3911
4328
  this.viewRenderer = new ViewRenderer({
3912
4329
  includeTokenEstimates: this.config.includeTokenEstimates
@@ -3947,11 +4364,32 @@ var SkillGraphServer = class {
3947
4364
  return this.applyLoadout(skills, { type: "criteria", criteria });
3948
4365
  }
3949
4366
  /**
3950
- * Set loadout based on task description (semantic matching)
4367
+ * Set loadout based on task description using hybrid confidence-tiered
4368
+ * compilation. Skills above the high threshold are auto-expanded,
4369
+ * skills between high and low thresholds are included as summaries,
4370
+ * and skills below the low threshold are excluded.
4371
+ *
4372
+ * Stores relevance scores for use by the 'relevance' eviction strategy.
3951
4373
  */
3952
4374
  async setLoadoutForTask(taskDescription) {
3953
- const skills = await this.compiler.compileForTask(taskDescription);
3954
- return this.applyLoadout(skills, { type: "task", taskDescription });
4375
+ const scored = await this.compiler.compileWithScoring(taskDescription);
4376
+ const { expand, summarize, excluded } = this.compiler.partitionByConfidence(
4377
+ scored,
4378
+ this.config.confidenceThresholds
4379
+ );
4380
+ const allIncluded = [...expand, ...summarize];
4381
+ const skills = allIncluded.map((s) => s.skill);
4382
+ const state = this.applyLoadout(skills, { type: "task", taskDescription });
4383
+ for (const item of allIncluded) {
4384
+ this.relevanceScores.set(item.skill.id, item.relevanceScore);
4385
+ }
4386
+ for (const item of expand) {
4387
+ if (this.state.expanded.size >= this.config.maxExpanded) break;
4388
+ this.state.expanded.add(item.skill.id);
4389
+ this.touchLru(item.skill.id);
4390
+ this.emit({ type: "skill:expanded", skillId: item.skill.id });
4391
+ }
4392
+ return state;
3955
4393
  }
3956
4394
  /**
3957
4395
  * Set loadout based on detected project context
@@ -4151,12 +4589,8 @@ var SkillGraphServer = class {
4151
4589
  * Returns skill summaries for display
4152
4590
  */
4153
4591
  async agentSearchSkills(query, limit = 5) {
4154
- const allSkills = await this.storage.listSkills({ status: ["active"] });
4155
- const queryLower = query.toLowerCase();
4156
- const matches = allSkills.filter(
4157
- (skill) => skill.name.toLowerCase().includes(queryLower) || skill.description.toLowerCase().includes(queryLower) || skill.instructions.toLowerCase().includes(queryLower) || skill.tags.some((tag) => tag.toLowerCase().includes(queryLower))
4158
- ).slice(0, limit);
4159
- return matches.map((skill) => ({
4592
+ const matches = await this.storage.searchSkills(query);
4593
+ return matches.slice(0, limit).map((skill) => ({
4160
4594
  id: skill.id,
4161
4595
  name: skill.name,
4162
4596
  description: this.getSummary(skill),
@@ -4197,19 +4631,19 @@ var SkillGraphServer = class {
4197
4631
  * Returns rendered category view (subcategories or skill summaries at leaf).
4198
4632
  * Pass no path for the top-level overview.
4199
4633
  */
4200
- async agentBrowseCatalog(path18) {
4634
+ async agentBrowseCatalog(path19) {
4201
4635
  if (!this.catalogRenderer) {
4202
4636
  return "<error>Catalog browsing is not enabled</error>";
4203
4637
  }
4204
- if (!path18 || path18.length === 0) {
4638
+ if (!path19 || path19.length === 0) {
4205
4639
  const result2 = await this.catalogRenderer.renderOverview();
4206
4640
  if (result2) {
4207
4641
  this.emit({ type: "catalog:browsed", path: [] });
4208
4642
  }
4209
4643
  return result2;
4210
4644
  }
4211
- const result = await this.catalogRenderer.renderCategory(path18);
4212
- this.emit({ type: "catalog:browsed", path: path18 });
4645
+ const result = await this.catalogRenderer.renderCategory(path19);
4646
+ this.emit({ type: "catalog:browsed", path: path19 });
4213
4647
  return result;
4214
4648
  }
4215
4649
  /**
@@ -4241,13 +4675,25 @@ var SkillGraphServer = class {
4241
4675
  /**
4242
4676
  * Render current state as system prompt content.
4243
4677
  * Includes catalog overview when catalog is enabled.
4678
+ *
4679
+ * When `deferExpansion` is enabled, all skills are rendered as
4680
+ * summaries regardless of expansion state — the agent must
4681
+ * explicitly request expansion. This avoids the reactive-signals
4682
+ * problem where upfront skill injection derails model planning.
4244
4683
  */
4245
4684
  async renderSystemPrompt() {
4685
+ let renderState = this.state;
4686
+ if (this.config.deferExpansion) {
4687
+ renderState = {
4688
+ ...this.state,
4689
+ expanded: /* @__PURE__ */ new Set()
4690
+ };
4691
+ }
4246
4692
  let prompt;
4247
4693
  if (this.config.outputFormat === "markdown") {
4248
- prompt = this.viewRenderer.renderMarkdown(this.state);
4694
+ prompt = this.viewRenderer.renderMarkdown(renderState);
4249
4695
  } else {
4250
- prompt = this.viewRenderer.renderXml(this.state);
4696
+ prompt = this.viewRenderer.renderXml(renderState);
4251
4697
  }
4252
4698
  if (this.catalogRenderer) {
4253
4699
  const overview = await this.catalogRenderer.renderOverview();
@@ -4285,21 +4731,70 @@ var SkillGraphServer = class {
4285
4731
  // PRIVATE HELPERS
4286
4732
  // ===========================================================================
4287
4733
  /**
4288
- * Apply a new set of skills as the loadout
4734
+ * Get the relevance score for a skill (0 if not scored).
4735
+ */
4736
+ getRelevanceScore(skillId) {
4737
+ return this.relevanceScores.get(skillId) ?? 0;
4738
+ }
4739
+ /**
4740
+ * Apply a new set of skills as the loadout.
4741
+ * After populating the available set, evaluates autoExpand triggers
4742
+ * on each skill to determine if any should be pre-expanded.
4289
4743
  */
4290
4744
  applyLoadout(skills, source) {
4291
4745
  this.state.available.clear();
4292
4746
  this.state.expanded.clear();
4293
4747
  this.state.pending.clear();
4294
4748
  this.lruOrder = [];
4749
+ this.relevanceScores.clear();
4295
4750
  for (const skill of skills) {
4296
4751
  this.state.available.set(skill.id, skill);
4297
4752
  }
4753
+ this.evaluateAutoExpand(source);
4298
4754
  this.state.source = source;
4299
4755
  this.state.updatedAt = /* @__PURE__ */ new Date();
4300
4756
  this.emit({ type: "loadout:changed", state: this.state });
4301
4757
  return this.state;
4302
4758
  }
4759
+ /**
4760
+ * Evaluate autoExpand trigger conditions for all skills in the loadout.
4761
+ * Checks keyword matches against the task description, file pattern
4762
+ * matches against the project path, and framework matches.
4763
+ */
4764
+ evaluateAutoExpand(source) {
4765
+ const taskText = source.taskDescription ?? "";
4766
+ for (const [id, skill] of this.state.available) {
4767
+ if (this.state.expanded.size >= this.config.maxExpanded) break;
4768
+ if (this.state.expanded.has(id)) continue;
4769
+ const triggers = skill.serving?.autoExpand;
4770
+ if (!triggers || triggers.length === 0) continue;
4771
+ for (const trigger of triggers) {
4772
+ if (this.matchesTrigger(trigger, taskText, source)) {
4773
+ this.state.expanded.add(id);
4774
+ this.touchLru(id);
4775
+ this.emit({ type: "skill:expanded", skillId: id });
4776
+ break;
4777
+ }
4778
+ }
4779
+ }
4780
+ }
4781
+ /**
4782
+ * Check if a single autoExpand trigger matches the current context.
4783
+ */
4784
+ matchesTrigger(trigger, taskText, source) {
4785
+ const conditions = trigger.conditions;
4786
+ if (!conditions) return false;
4787
+ const taskLower = taskText.toLowerCase();
4788
+ if (trigger.on === "mention" && conditions.keywords?.length) {
4789
+ return conditions.keywords.some((kw) => taskLower.includes(kw.toLowerCase()));
4790
+ }
4791
+ if (trigger.on === "file-match" && conditions.filePatterns?.length && source.projectPath) {
4792
+ return conditions.filePatterns.some(
4793
+ (pattern) => source.projectPath.includes(pattern)
4794
+ );
4795
+ }
4796
+ return false;
4797
+ }
4303
4798
  /**
4304
4799
  * Evict a skill from expanded based on strategy
4305
4800
  */
@@ -4310,7 +4805,7 @@ var SkillGraphServer = class {
4310
4805
  case "lru":
4311
4806
  toEvict = this.lruOrder.shift();
4312
4807
  break;
4313
- case "priority":
4808
+ case "priority": {
4314
4809
  let lowestPriority = Infinity;
4315
4810
  for (const id of this.state.expanded) {
4316
4811
  const skill = this.state.available.get(id);
@@ -4321,6 +4816,18 @@ var SkillGraphServer = class {
4321
4816
  }
4322
4817
  }
4323
4818
  break;
4819
+ }
4820
+ case "relevance": {
4821
+ let lowestScore = Infinity;
4822
+ for (const id of this.state.expanded) {
4823
+ const score = this.relevanceScores.get(id) ?? 0;
4824
+ if (score < lowestScore) {
4825
+ lowestScore = score;
4826
+ toEvict = id;
4827
+ }
4828
+ }
4829
+ break;
4830
+ }
4324
4831
  case "manual":
4325
4832
  return;
4326
4833
  }
@@ -4376,7 +4883,6 @@ var SkillGraphServer = class {
4376
4883
  };
4377
4884
 
4378
4885
  // src/serving/interfaces.ts
4379
- init_cjs_shims();
4380
4886
  function createStorageView(storage) {
4381
4887
  return {
4382
4888
  getSkill: storage.getSkill.bind(storage),
@@ -4416,11 +4922,7 @@ function createServingEventBridge() {
4416
4922
  };
4417
4923
  }
4418
4924
 
4419
- // src/federation/index.ts
4420
- init_cjs_shims();
4421
-
4422
4925
  // src/federation/remote-store.ts
4423
- init_cjs_shims();
4424
4926
  var fs3 = __toESM(require("fs"));
4425
4927
  var path3 = __toESM(require("path"));
4426
4928
  var REMOTES_FILE = "remotes.json";
@@ -4616,14 +5118,12 @@ var RemoteStore = class {
4616
5118
  };
4617
5119
 
4618
5120
  // src/federation/remote-manager.ts
4619
- init_cjs_shims();
4620
5121
  var fs5 = __toESM(require("fs"));
4621
5122
  var path5 = __toESM(require("path"));
4622
5123
  var import_child_process = require("child_process");
4623
5124
  var import_util = require("util");
4624
5125
 
4625
5126
  // src/federation/skilltree-config.ts
4626
- init_cjs_shims();
4627
5127
  var fs4 = __toESM(require("fs"));
4628
5128
  var path4 = __toESM(require("path"));
4629
5129
  function resolveSkilltreeDir(repoRoot) {
@@ -5292,11 +5792,7 @@ ${body.join("\n")}
5292
5792
  }
5293
5793
  };
5294
5794
 
5295
- // src/federation/federation-manager.ts
5296
- init_cjs_shims();
5297
-
5298
5795
  // src/versioning/semver.ts
5299
- init_cjs_shims();
5300
5796
  function parseVersion(version) {
5301
5797
  const match = version.match(
5302
5798
  /^(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9.-]+))?(?:\+([a-zA-Z0-9.-]+))?$/
@@ -5846,13 +6342,11 @@ ${remote.instructions}` : remote.instructions,
5846
6342
  init_base();
5847
6343
 
5848
6344
  // src/storage/cached.ts
5849
- init_cjs_shims();
5850
6345
  var fs8 = __toESM(require("fs"));
5851
6346
  var path8 = __toESM(require("path"));
5852
6347
  init_base();
5853
6348
 
5854
6349
  // src/storage/filesystem.ts
5855
- init_cjs_shims();
5856
6350
  var fs6 = __toESM(require("fs/promises"));
5857
6351
  var path6 = __toESM(require("path"));
5858
6352
  init_base();
@@ -6507,11 +7001,7 @@ var CachedStorageAdapter = class extends BaseStorageAdapter {
6507
7001
  }
6508
7002
  };
6509
7003
 
6510
- // src/versioning/index.ts
6511
- init_cjs_shims();
6512
-
6513
7004
  // src/versioning/lineage.ts
6514
- init_cjs_shims();
6515
7005
  var LineageTracker = class {
6516
7006
  constructor(storage) {
6517
7007
  this.storage = storage;
@@ -6754,11 +7244,7 @@ ${source}`;
6754
7244
  }
6755
7245
  };
6756
7246
 
6757
- // src/versioning/merge.ts
6758
- init_cjs_shims();
6759
-
6760
7247
  // src/hooks/registry.ts
6761
- init_cjs_shims();
6762
7248
  var import_crypto = require("crypto");
6763
7249
  var PRIORITY_ORDER = {
6764
7250
  high: 0,
@@ -6943,7 +7429,6 @@ var HookRegistry = class {
6943
7429
  var hookRegistry = new HookRegistry();
6944
7430
 
6945
7431
  // src/materialization/materializer.ts
6946
- init_cjs_shims();
6947
7432
  var fs10 = __toESM(require("fs"));
6948
7433
  var path10 = __toESM(require("path"));
6949
7434
  var SKILLTREE_MARKER_START = "<!-- SKILLTREE_START -->";
@@ -7691,8 +8176,7 @@ var SkillBank = class {
7691
8176
  * The `loadout:changed` event is currently the only one we react to.
7692
8177
  * Earlier versions also handled `skill:used` / `skill:feedback` to mutate
7693
8178
  * `Skill.metrics`, but skill-tree no longer tracks per-skill usage —
7694
- * cognitive-core owns that signal via `playbook.evolution.*`. See
7695
- * docs/SKILL_TREE_METRICS_DEPRECATION.md.
8179
+ * cognitive-core owns that signal via `playbook.evolution.*`.
7696
8180
  */
7697
8181
  async handleServingEvent(event) {
7698
8182
  switch (event.type) {
@@ -7797,38 +8281,15 @@ var SkillBank = class {
7797
8281
  };
7798
8282
 
7799
8283
  // src/storage/index.ts
7800
- init_cjs_shims();
7801
8284
  init_base();
7802
8285
  init_sqlite();
7803
8286
 
7804
- // src/storage/migration.ts
7805
- init_cjs_shims();
7806
-
7807
8287
  // src/agents/index.ts
7808
- init_cjs_shims();
7809
8288
  init_types();
7810
8289
  init_generator();
7811
8290
  init_parser();
7812
8291
  init_sync();
7813
8292
 
7814
- // src/hooks/index.ts
7815
- init_cjs_shims();
7816
-
7817
- // src/hooks/builtin.ts
7818
- init_cjs_shims();
7819
-
7820
- // src/serving/index.ts
7821
- init_cjs_shims();
7822
-
7823
- // src/materialization/index.ts
7824
- init_cjs_shims();
7825
-
7826
- // src/sync/index.ts
7827
- init_cjs_shims();
7828
-
7829
- // src/sync/hierarchical-sync-adapter.ts
7830
- init_cjs_shims();
7831
-
7832
8293
  // src/sync/index.ts
7833
8294
  function createDefaultSyncConfig(remoteUrl, agentId, options) {
7834
8295
  return {
@@ -7858,15 +8319,10 @@ function createDefaultSyncConfig(remoteUrl, agentId, options) {
7858
8319
  }
7859
8320
 
7860
8321
  // src/services/indexer.ts
7861
- init_cjs_shims();
7862
8322
  var path12 = __toESM(require("path"));
7863
8323
  var fs12 = __toESM(require("fs"));
7864
8324
 
7865
- // src/config/index.ts
7866
- init_cjs_shims();
7867
-
7868
8325
  // src/config/types.ts
7869
- init_cjs_shims();
7870
8326
  var DEFAULT_CONFIG6 = {
7871
8327
  storage: {
7872
8328
  path: "~/.skill-tree"
@@ -7901,7 +8357,6 @@ var DEFAULT_CONFIG6 = {
7901
8357
  };
7902
8358
 
7903
8359
  // src/config/loader.ts
7904
- init_cjs_shims();
7905
8360
  var fs11 = __toESM(require("fs"));
7906
8361
  var path11 = __toESM(require("path"));
7907
8362
  var os = __toESM(require("os"));
@@ -7953,8 +8408,8 @@ function substituteEnvVarsInObject(obj) {
7953
8408
  }
7954
8409
  return obj;
7955
8410
  }
7956
- function setNestedProperty(obj, path18, value) {
7957
- const parts = path18.split(".");
8411
+ function setNestedProperty(obj, path19, value) {
8412
+ const parts = path19.split(".");
7958
8413
  let current = obj;
7959
8414
  for (let i = 0; i < parts.length - 1; i++) {
7960
8415
  const part = parts[i];
@@ -8113,8 +8568,8 @@ var ConfigLoader = class {
8113
8568
  /**
8114
8569
  * Get a specific config value by path
8115
8570
  */
8116
- get(path18) {
8117
- const parts = path18.split(".");
8571
+ get(path19) {
8572
+ const parts = path19.split(".");
8118
8573
  let current = this.getConfig();
8119
8574
  for (const part of parts) {
8120
8575
  if (current === null || typeof current !== "object") {
@@ -8197,7 +8652,6 @@ function loadConfig(configPath) {
8197
8652
  }
8198
8653
 
8199
8654
  // src/import/converter.ts
8200
- init_cjs_shims();
8201
8655
  function convertIndexerSkill(indexerSkill) {
8202
8656
  const warnings = [];
8203
8657
  const instructions = indexerSkill.content || "";
@@ -9060,20 +9514,349 @@ function createIntegratedIndexer(skillBank, config2 = {}) {
9060
9514
  return new IndexerService(config2, skillBank);
9061
9515
  }
9062
9516
 
9517
+ // src/import/skillmd.ts
9518
+ function splitFrontmatter(content) {
9519
+ const normalized = content.replace(/\r\n/g, "\n");
9520
+ const match = normalized.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
9521
+ if (match) {
9522
+ return { frontmatter: match[1], body: match[2], hasFrontmatter: true };
9523
+ }
9524
+ return { frontmatter: "", body: normalized, hasFrontmatter: false };
9525
+ }
9526
+ function extractField(yaml, field) {
9527
+ const match = yaml.match(new RegExp(`^${field}:\\s*(.+)$`, "m"));
9528
+ if (!match) return void 0;
9529
+ return match[1].trim().replace(/^["']|["']$/g, "");
9530
+ }
9531
+ function extractMultiline(yaml, field) {
9532
+ const match = yaml.match(
9533
+ new RegExp(`^${field}:\\s*\\|\\n((?:\\s{2}.+\\n?)+)`, "m")
9534
+ );
9535
+ if (match) {
9536
+ return match[1].split("\n").map((line) => line.replace(/^\s{2}/, "")).join("\n").trim();
9537
+ }
9538
+ return extractField(yaml, field);
9539
+ }
9540
+ function extractList(yaml, field) {
9541
+ const match = yaml.match(new RegExp(`^${field}:\\n((?:\\s+-\\s+.+\\n?)+)`, "m"));
9542
+ if (match) {
9543
+ return match[1].split("\n").map((line) => line.replace(/^\s+-\s+/, "").trim().replace(/^["']|["']$/g, "")).filter((line) => line.length > 0);
9544
+ }
9545
+ return [];
9546
+ }
9547
+ function parseSkillMd(content) {
9548
+ const { frontmatter, body, hasFrontmatter } = splitFrontmatter(content);
9549
+ return {
9550
+ name: extractField(frontmatter, "name"),
9551
+ description: extractMultiline(frontmatter, "description"),
9552
+ version: extractField(frontmatter, "version"),
9553
+ author: extractField(frontmatter, "author"),
9554
+ status: extractField(frontmatter, "status"),
9555
+ date: extractField(frontmatter, "date"),
9556
+ tags: extractList(frontmatter, "tags"),
9557
+ body: body.trim(),
9558
+ hasFrontmatter
9559
+ };
9560
+ }
9561
+
9562
+ // src/import/skill-from-md.ts
9563
+ var VALID_STATUSES2 = ["draft", "active", "deprecated", "experimental"];
9564
+ function slugify(input) {
9565
+ return input.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80) || "skill";
9566
+ }
9567
+ function skillFromSkillMd(content, options = {}) {
9568
+ const warnings = [];
9569
+ const parsed = parseSkillMd(content);
9570
+ if (!parsed.hasFrontmatter) {
9571
+ warnings.push("SKILL.md has no YAML frontmatter; using fallback metadata");
9572
+ }
9573
+ if (!parsed.body) {
9574
+ warnings.push("SKILL.md body is empty; instructions will be empty");
9575
+ }
9576
+ const id = slugify(options.id || parsed.name || options.defaultName || "skill");
9577
+ const tags = new Set(parsed.tags);
9578
+ for (const tag of options.extraTags ?? []) {
9579
+ if (tag) tags.add(tag);
9580
+ }
9581
+ const status = parsed.status && VALID_STATUSES2.includes(parsed.status) ? parsed.status : options.defaultStatus ?? "active";
9582
+ const now = options.now ?? /* @__PURE__ */ new Date();
9583
+ const skill = {
9584
+ id,
9585
+ name: parsed.name || options.defaultName || id,
9586
+ version: parsed.version || options.defaultVersion || "1.0.0",
9587
+ description: parsed.description || options.defaultDescription || "",
9588
+ instructions: parsed.body,
9589
+ author: parsed.author || options.defaultAuthor || "unknown",
9590
+ tags: Array.from(tags),
9591
+ createdAt: now,
9592
+ updatedAt: now,
9593
+ status,
9594
+ source: options.source,
9595
+ taxonomy: options.taxonomyPath && options.taxonomyPath.length > 0 ? { primaryPath: options.taxonomyPath } : void 0,
9596
+ externalSource: options.externalSource
9597
+ };
9598
+ return { skill, warnings, parsed };
9599
+ }
9600
+
9601
+ // src/services/skillnet.ts
9602
+ var DEFAULT_SKILLNET_API = "http://api-skillnet.openkg.cn/v1";
9603
+ function parseGitHubUrl(url) {
9604
+ const m = url.match(
9605
+ /github\.com\/([^/]+)\/([^/]+?)(?:\.git)?(?:\/(?:tree|blob)\/([^/]+)\/(.*))?$/
9606
+ );
9607
+ if (!m) return null;
9608
+ const [, owner, repo, ref, path19] = m;
9609
+ return {
9610
+ owner,
9611
+ repo,
9612
+ ref: ref || "main",
9613
+ path: (path19 || "").replace(/\/$/, "")
9614
+ };
9615
+ }
9616
+ var SkillNetClient = class {
9617
+ constructor(config2 = {}) {
9618
+ this.apiBaseUrl = (config2.apiBaseUrl || DEFAULT_SKILLNET_API).replace(/\/$/, "");
9619
+ this.githubToken = config2.githubToken;
9620
+ this.githubMirror = config2.githubMirror;
9621
+ const resolved = config2.fetchImpl || globalThis.fetch;
9622
+ if (!resolved) {
9623
+ throw new Error(
9624
+ "No fetch implementation available. Provide config.fetchImpl or run on Node 18+."
9625
+ );
9626
+ }
9627
+ this.fetchImpl = resolved;
9628
+ }
9629
+ /**
9630
+ * Search the SkillNet index. Free and requires no API key.
9631
+ */
9632
+ async search(query, options = {}) {
9633
+ const params = new URLSearchParams({ q: query });
9634
+ if (options.mode) params.set("mode", options.mode);
9635
+ if (options.category) params.set("category", options.category);
9636
+ if (options.limit != null) params.set("limit", String(options.limit));
9637
+ if (options.page != null) params.set("page", String(options.page));
9638
+ if (options.minStars != null) params.set("min_stars", String(options.minStars));
9639
+ if (options.sortBy) params.set("sort_by", options.sortBy);
9640
+ if (options.threshold != null) params.set("threshold", String(options.threshold));
9641
+ const url = `${this.apiBaseUrl}/search?${params.toString()}`;
9642
+ const res = await this.fetchImpl(url);
9643
+ if (!res.ok) {
9644
+ throw new Error(`SkillNet search failed: ${res.status} ${res.statusText}`);
9645
+ }
9646
+ const body = await res.json();
9647
+ const rows = Array.isArray(body.data) ? body.data : [];
9648
+ return rows.map((row) => ({
9649
+ skillName: String(row.skill_name ?? ""),
9650
+ skillDescription: row.skill_description != null ? String(row.skill_description) : void 0,
9651
+ author: row.author != null ? String(row.author) : void 0,
9652
+ stars: typeof row.stars === "number" ? row.stars : Number(row.stars) || 0,
9653
+ skillUrl: String(row.skill_url ?? ""),
9654
+ category: row.category != null ? String(row.category) : void 0,
9655
+ evaluation: row.evaluation && typeof row.evaluation === "object" ? row.evaluation : void 0
9656
+ }));
9657
+ }
9658
+ /**
9659
+ * Convert a GitHub skill URL into the raw URL for its SKILL.md.
9660
+ * Applies the configured mirror prefix when set.
9661
+ */
9662
+ toRawSkillMdUrl(skillUrl) {
9663
+ const parsed = parseGitHubUrl(skillUrl);
9664
+ if (!parsed) {
9665
+ throw new Error(`Not a recognizable GitHub skill URL: ${skillUrl}`);
9666
+ }
9667
+ const { owner, repo, ref, path: path19 } = parsed;
9668
+ const filePath = /SKILL\.md$/i.test(path19) ? path19 : path19 ? `${path19}/SKILL.md` : "SKILL.md";
9669
+ const raw = `https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${filePath}`;
9670
+ return this.githubMirror ? `${this.githubMirror.replace(/\/$/, "")}/${raw}` : raw;
9671
+ }
9672
+ /**
9673
+ * Fetch the raw SKILL.md content for a skill URL.
9674
+ */
9675
+ async fetchSkillMd(skillUrl) {
9676
+ const rawUrl = this.toRawSkillMdUrl(skillUrl);
9677
+ const headers = {};
9678
+ if (this.githubToken) {
9679
+ headers.Authorization = `Bearer ${this.githubToken}`;
9680
+ }
9681
+ const res = await this.fetchImpl(rawUrl, { headers });
9682
+ if (!res.ok) {
9683
+ throw new Error(
9684
+ `Failed to fetch SKILL.md (${res.status} ${res.statusText}): ${rawUrl}`
9685
+ );
9686
+ }
9687
+ return { content: await res.text(), rawUrl };
9688
+ }
9689
+ /**
9690
+ * Convert a SkillNet search result + its SKILL.md content into a skill-tree Skill.
9691
+ */
9692
+ convertSkillNetSkill(result, content, rawUrl) {
9693
+ const gh = parseGitHubUrl(result.skillUrl);
9694
+ const folderName = gh?.path ? gh.path.split("/").filter(Boolean).pop() : void 0;
9695
+ const now = /* @__PURE__ */ new Date();
9696
+ const { skill, warnings } = skillFromSkillMd(content, {
9697
+ id: folderName || result.skillName,
9698
+ defaultName: result.skillName,
9699
+ defaultDescription: result.skillDescription,
9700
+ defaultAuthor: result.author || "skillnet",
9701
+ // SkillNet skills are curated/published → default active.
9702
+ defaultStatus: "active",
9703
+ extraTags: [
9704
+ ...result.category ? [result.category.toLowerCase()] : [],
9705
+ ...gh?.repo ? [gh.repo] : []
9706
+ ],
9707
+ taxonomyPath: result.category ? [result.category] : void 0,
9708
+ source: {
9709
+ type: "imported",
9710
+ location: result.skillUrl,
9711
+ importedAt: now
9712
+ },
9713
+ externalSource: {
9714
+ url: result.skillUrl,
9715
+ repo: gh ? `${gh.owner}/${gh.repo}` : "",
9716
+ scrapedAt: now
9717
+ },
9718
+ now
9719
+ });
9720
+ return { skill, warnings, rawUrl };
9721
+ }
9722
+ /**
9723
+ * Import a single skill by its SkillNet/GitHub URL into a SkillBank.
9724
+ */
9725
+ async importSkill(skillUrl, bank, meta = {}) {
9726
+ const { content, rawUrl } = await this.fetchSkillMd(skillUrl);
9727
+ const result = {
9728
+ skillName: meta.skillName || "",
9729
+ skillDescription: meta.skillDescription,
9730
+ author: meta.author,
9731
+ stars: meta.stars ?? 0,
9732
+ skillUrl,
9733
+ category: meta.category,
9734
+ evaluation: meta.evaluation
9735
+ };
9736
+ const converted = this.convertSkillNetSkill(result, content, rawUrl);
9737
+ await bank.saveSkill(converted.skill);
9738
+ return converted;
9739
+ }
9740
+ /**
9741
+ * Search SkillNet and import the matching skills into a SkillBank.
9742
+ */
9743
+ async importFromSearch(query, bank, options = {}) {
9744
+ const result = {
9745
+ imported: 0,
9746
+ failed: 0,
9747
+ skills: [],
9748
+ errors: []
9749
+ };
9750
+ const results = await this.search(query, options);
9751
+ for (const row of results) {
9752
+ if (!row.skillUrl) {
9753
+ result.failed++;
9754
+ result.errors.push(`Skipped "${row.skillName}": no skill_url`);
9755
+ continue;
9756
+ }
9757
+ try {
9758
+ const converted = await this.importSkill(row.skillUrl, bank, row);
9759
+ result.imported++;
9760
+ result.skills.push(converted.skill);
9761
+ } catch (err) {
9762
+ result.failed++;
9763
+ result.errors.push(
9764
+ `Failed to import ${row.skillUrl}: ${err.message}`
9765
+ );
9766
+ }
9767
+ }
9768
+ return result;
9769
+ }
9770
+ };
9771
+ function createSkillNetClient(config2 = {}) {
9772
+ return new SkillNetClient(config2);
9773
+ }
9774
+
9775
+ // src/import/local.ts
9776
+ var fs13 = __toESM(require("fs/promises"));
9777
+ var path13 = __toESM(require("path"));
9778
+ var SKILL_FILE_RE = /^skill\.md$/i;
9779
+ async function findSkillMdFiles(root, maxDepth = 6) {
9780
+ const found = [];
9781
+ async function walk(dir, depth) {
9782
+ if (depth > maxDepth) return;
9783
+ let entries;
9784
+ try {
9785
+ entries = await fs13.readdir(dir, { withFileTypes: true });
9786
+ } catch (err) {
9787
+ if (err.code === "ENOENT") return;
9788
+ throw err;
9789
+ }
9790
+ for (const entry of entries) {
9791
+ if (entry.isFile() && SKILL_FILE_RE.test(entry.name)) {
9792
+ found.push({
9793
+ filePath: path13.join(dir, entry.name),
9794
+ directory: dir,
9795
+ id: path13.basename(dir)
9796
+ });
9797
+ }
9798
+ }
9799
+ for (const entry of entries) {
9800
+ if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
9801
+ await walk(path13.join(dir, entry.name), depth + 1);
9802
+ }
9803
+ }
9804
+ }
9805
+ await walk(root, 0);
9806
+ return found;
9807
+ }
9808
+ async function importSkillMdFile(filePath, bank, options = {}) {
9809
+ const content = await fs13.readFile(filePath, "utf-8");
9810
+ const dirName = path13.basename(path13.dirname(filePath));
9811
+ const now = /* @__PURE__ */ new Date();
9812
+ const { skill, warnings } = skillFromSkillMd(content, {
9813
+ source: { type: "imported", location: filePath, importedAt: now },
9814
+ now,
9815
+ ...options,
9816
+ id: options.id ?? dirName
9817
+ });
9818
+ await bank.saveSkill(skill);
9819
+ return { skill, warnings };
9820
+ }
9821
+ async function importLocalSkillDir(dirPath, bank, options = {}) {
9822
+ const result = {
9823
+ imported: 0,
9824
+ failed: 0,
9825
+ skills: [],
9826
+ errors: []
9827
+ };
9828
+ const files = await findSkillMdFiles(dirPath);
9829
+ const seen = /* @__PURE__ */ new Set();
9830
+ for (const file of files) {
9831
+ if (seen.has(file.id)) {
9832
+ result.errors.push(`Skipped duplicate id "${file.id}" at ${file.filePath}`);
9833
+ continue;
9834
+ }
9835
+ try {
9836
+ const { skill } = await importSkillMdFile(file.filePath, bank, {
9837
+ ...options,
9838
+ id: file.id
9839
+ });
9840
+ seen.add(skill.id);
9841
+ result.imported++;
9842
+ result.skills.push(skill);
9843
+ } catch (err) {
9844
+ result.failed++;
9845
+ result.errors.push(`Failed to import ${file.filePath}: ${err.message}`);
9846
+ }
9847
+ }
9848
+ return result;
9849
+ }
9850
+
9063
9851
  // src/index.ts
9064
- var VERSION = "0.2.0";
9852
+ var VERSION = "0.3.0";
9065
9853
 
9066
9854
  // src/cli/commands/list.ts
9067
- init_cjs_shims();
9068
9855
  var import_commander = require("commander");
9069
9856
 
9070
- // src/cli/utils/skillbank.ts
9071
- init_cjs_shims();
9072
-
9073
9857
  // src/cli/utils/paths.ts
9074
- init_cjs_shims();
9075
- var fs13 = __toESM(require("fs"));
9076
- var path13 = __toESM(require("path"));
9858
+ var fs14 = __toESM(require("fs"));
9859
+ var path14 = __toESM(require("path"));
9077
9860
  var os2 = __toESM(require("os"));
9078
9861
  var DEFAULT_PATHS = [
9079
9862
  ".claude/skills",
@@ -9087,13 +9870,13 @@ var DEFAULT_PATHS = [
9087
9870
  ];
9088
9871
  function expandHome(p) {
9089
9872
  if (p.startsWith("~/")) {
9090
- return path13.join(os2.homedir(), p.slice(2));
9873
+ return path14.join(os2.homedir(), p.slice(2));
9091
9874
  }
9092
9875
  return p;
9093
9876
  }
9094
9877
  function dirExists(p) {
9095
9878
  try {
9096
- return fs13.statSync(p).isDirectory();
9879
+ return fs14.statSync(p).isDirectory();
9097
9880
  } catch {
9098
9881
  return false;
9099
9882
  }
@@ -9101,23 +9884,23 @@ function dirExists(p) {
9101
9884
  function resolveSkillPath(explicitPath) {
9102
9885
  if (explicitPath) {
9103
9886
  const resolved = expandHome(explicitPath);
9104
- return path13.resolve(resolved);
9887
+ return path14.resolve(resolved);
9105
9888
  }
9106
9889
  const envPath = process.env.SKILL_TREE_PATH;
9107
9890
  if (envPath) {
9108
- return path13.resolve(expandHome(envPath));
9891
+ return path14.resolve(expandHome(envPath));
9109
9892
  }
9110
9893
  for (const defaultPath of DEFAULT_PATHS) {
9111
- const resolved = path13.resolve(expandHome(defaultPath));
9894
+ const resolved = path14.resolve(expandHome(defaultPath));
9112
9895
  if (dirExists(resolved)) {
9113
9896
  return resolved;
9114
9897
  }
9115
9898
  }
9116
- return path13.resolve(".claude/skills");
9899
+ return path14.resolve(".claude/skills");
9117
9900
  }
9118
9901
  function ensureDir(dir) {
9119
9902
  if (!dirExists(dir)) {
9120
- fs13.mkdirSync(dir, { recursive: true });
9903
+ fs14.mkdirSync(dir, { recursive: true });
9121
9904
  }
9122
9905
  }
9123
9906
 
@@ -9154,7 +9937,6 @@ function getSkillPath(options) {
9154
9937
  var getSkillBank = createSkillBankFromOptions;
9155
9938
 
9156
9939
  // src/cli/utils/output.ts
9157
- init_cjs_shims();
9158
9940
  var import_chalk = __toESM(require("chalk"));
9159
9941
  function formatSkillLine(skill) {
9160
9942
  const status = formatStatus(skill.status);
@@ -9336,7 +10118,6 @@ var listCommand = new import_commander.Command("list").description("List all ski
9336
10118
  });
9337
10119
 
9338
10120
  // src/cli/commands/show.ts
9339
- init_cjs_shims();
9340
10121
  var import_commander2 = require("commander");
9341
10122
  var showCommand = new import_commander2.Command("show").description("Show skill details").argument("<id>", "Skill ID").option("-v, --version <version>", "Show specific version").action(async (id, options, command) => {
9342
10123
  const globalOpts = command.optsWithGlobals();
@@ -9359,7 +10140,6 @@ var showCommand = new import_commander2.Command("show").description("Show skill
9359
10140
  });
9360
10141
 
9361
10142
  // src/cli/commands/search.ts
9362
- init_cjs_shims();
9363
10143
  var import_commander3 = require("commander");
9364
10144
  var searchCommand = new import_commander3.Command("search").description("Search skills by text").argument("<query>", "Search query").action(async (query, options, command) => {
9365
10145
  const globalOpts = command.optsWithGlobals();
@@ -9392,7 +10172,6 @@ var searchCommand = new import_commander3.Command("search").description("Search
9392
10172
  });
9393
10173
 
9394
10174
  // src/cli/commands/stats.ts
9395
- init_cjs_shims();
9396
10175
  var import_commander4 = require("commander");
9397
10176
  var statsCommand = new import_commander4.Command("stats").description("Show skill bank statistics").action(async (options, command) => {
9398
10177
  const globalOpts = command.optsWithGlobals();
@@ -9411,7 +10190,6 @@ var statsCommand = new import_commander4.Command("stats").description("Show skil
9411
10190
  });
9412
10191
 
9413
10192
  // src/cli/commands/versions.ts
9414
- init_cjs_shims();
9415
10193
  var import_commander5 = require("commander");
9416
10194
  var versionsCommand = new import_commander5.Command("versions").description("Show version history for a skill").argument("<id>", "Skill ID").action(async (id, options, command) => {
9417
10195
  const globalOpts = command.optsWithGlobals();
@@ -9434,7 +10212,6 @@ var versionsCommand = new import_commander5.Command("versions").description("Sho
9434
10212
  });
9435
10213
 
9436
10214
  // src/cli/commands/diff.ts
9437
- init_cjs_shims();
9438
10215
  var import_commander6 = require("commander");
9439
10216
  var diffCommand = new import_commander6.Command("diff").description("Compare two versions of a skill").argument("<id>", "Skill ID").argument("<version-a>", "First version").argument("<version-b>", "Second version").action(async (id, versionA, versionB, options, command) => {
9440
10217
  const globalOpts = command.optsWithGlobals();
@@ -9453,7 +10230,6 @@ var diffCommand = new import_commander6.Command("diff").description("Compare two
9453
10230
  });
9454
10231
 
9455
10232
  // src/cli/commands/rollback.ts
9456
- init_cjs_shims();
9457
10233
  var import_commander7 = require("commander");
9458
10234
  var rollbackCommand = new import_commander7.Command("rollback").description("Rollback a skill to a previous version").argument("<id>", "Skill ID").requiredOption("--to <version>", "Version to rollback to").action(async (id, options, command) => {
9459
10235
  const globalOpts = command.optsWithGlobals();
@@ -9472,7 +10248,6 @@ var rollbackCommand = new import_commander7.Command("rollback").description("Rol
9472
10248
  });
9473
10249
 
9474
10250
  // src/cli/commands/fork.ts
9475
- init_cjs_shims();
9476
10251
  var import_commander8 = require("commander");
9477
10252
  var forkCommand = new import_commander8.Command("fork").description("Fork a skill to create a variant").argument("<id>", "Skill ID to fork from").requiredOption("--new-id <id>", "ID for the new forked skill").requiredOption("--reason <reason>", "Reason for forking").option("--name <name>", "Name for the forked skill").option("--from-version <version>", "Version to fork from (defaults to latest)").action(async (id, options, command) => {
9478
10253
  const globalOpts = command.optsWithGlobals();
@@ -9496,7 +10271,6 @@ var forkCommand = new import_commander8.Command("fork").description("Fork a skil
9496
10271
  });
9497
10272
 
9498
10273
  // src/cli/commands/deprecate.ts
9499
- init_cjs_shims();
9500
10274
  var import_commander9 = require("commander");
9501
10275
  var deprecateCommand = new import_commander9.Command("deprecate").description("Mark a skill as deprecated").argument("<id>", "Skill ID").action(async (id, options, command) => {
9502
10276
  const globalOpts = command.optsWithGlobals();
@@ -9515,7 +10289,6 @@ var deprecateCommand = new import_commander9.Command("deprecate").description("M
9515
10289
  });
9516
10290
 
9517
10291
  // src/cli/commands/activate.ts
9518
- init_cjs_shims();
9519
10292
  var import_commander10 = require("commander");
9520
10293
  var activateCommand = new import_commander10.Command("activate").description("Mark a skill as active").argument("<id>", "Skill ID").action(async (id, options, command) => {
9521
10294
  const globalOpts = command.optsWithGlobals();
@@ -9541,7 +10314,6 @@ var activateCommand = new import_commander10.Command("activate").description("Ma
9541
10314
  });
9542
10315
 
9543
10316
  // src/cli/commands/delete.ts
9544
- init_cjs_shims();
9545
10317
  var import_commander11 = require("commander");
9546
10318
  var deleteCommand = new import_commander11.Command("delete").description("Delete a skill").argument("<id>", "Skill ID").option("-f, --force", "Skip confirmation").option("-v, --version <version>", "Delete specific version only").action(async (id, options, command) => {
9547
10319
  const globalOpts = command.optsWithGlobals();
@@ -9574,9 +10346,8 @@ var deleteCommand = new import_commander11.Command("delete").description("Delete
9574
10346
  });
9575
10347
 
9576
10348
  // src/cli/commands/export.ts
9577
- init_cjs_shims();
9578
10349
  var import_commander12 = require("commander");
9579
- var fs14 = __toESM(require("fs"));
10350
+ var fs15 = __toESM(require("fs"));
9580
10351
  var exportCommand = new import_commander12.Command("export").description("Export all skills to JSON").option("-o, --output <file>", "Output file path (defaults to stdout)").action(async (options, command) => {
9581
10352
  const globalOpts = command.optsWithGlobals();
9582
10353
  try {
@@ -9584,7 +10355,7 @@ var exportCommand = new import_commander12.Command("export").description("Export
9584
10355
  const skills = await skillBank.exportAll();
9585
10356
  const json = JSON.stringify(skills, null, 2);
9586
10357
  if (options.output) {
9587
- fs14.writeFileSync(options.output, json, "utf-8");
10358
+ fs15.writeFileSync(options.output, json, "utf-8");
9588
10359
  if (!globalOpts.quiet) {
9589
10360
  printSuccess(`Exported ${skills.length} skill(s) to ${options.output}`);
9590
10361
  }
@@ -9598,15 +10369,10 @@ var exportCommand = new import_commander12.Command("export").description("Export
9598
10369
  });
9599
10370
 
9600
10371
  // src/cli/commands/import.ts
9601
- init_cjs_shims();
9602
10372
  var import_commander13 = require("commander");
9603
- var fs15 = __toESM(require("fs"));
9604
-
9605
- // src/import/index.ts
9606
- init_cjs_shims();
10373
+ var fs16 = __toESM(require("fs"));
9607
10374
 
9608
10375
  // src/import/detect.ts
9609
- init_cjs_shims();
9610
10376
  function isSkillTreeSkill(obj) {
9611
10377
  if (typeof obj !== "object" || obj === null) return false;
9612
10378
  const skill = obj;
@@ -9663,14 +10429,38 @@ function isLikelyIndexerFormat(content) {
9663
10429
  }
9664
10430
 
9665
10431
  // src/cli/commands/import.ts
9666
- var importCommand = new import_commander13.Command("import").description("Import skills from JSON file").argument("<file>", "JSON file to import").option("--from-indexer", "Import from skill-indexer export format").option("--auto-detect", "Auto-detect format (default: true)", true).action(async (file, options, command) => {
10432
+ var importCommand = new import_commander13.Command("import").description("Import skills from a JSON file, a SKILL.md file, or a directory of skills").argument("<path>", "JSON file, SKILL.md file, or directory to import").option("--from-indexer", "Import from skill-indexer export format").option("--skill-md", "Treat input as SKILL.md (auto-detected for directories and .md files)").option("--auto-detect", "Auto-detect format (default: true)", true).action(async (file, options, command) => {
9667
10433
  const globalOpts = command.optsWithGlobals();
9668
10434
  try {
9669
- if (!fs15.existsSync(file)) {
9670
- printError(`File not found: ${file}`);
10435
+ if (!fs16.existsSync(file)) {
10436
+ printError(`Path not found: ${file}`);
9671
10437
  process.exit(1);
9672
10438
  }
9673
- const content = fs15.readFileSync(file, "utf-8");
10439
+ const stat = fs16.statSync(file);
10440
+ const isMarkdown = options.skillMd || stat.isDirectory() || /\.md$/i.test(file);
10441
+ if (isMarkdown) {
10442
+ const bank = await createSkillBankFromOptions(globalOpts);
10443
+ if (stat.isDirectory()) {
10444
+ const result2 = await importLocalSkillDir(file, bank);
10445
+ if (globalOpts.json) {
10446
+ console.log(JSON.stringify(result2, null, 2));
10447
+ return;
10448
+ }
10449
+ printSuccess(`Imported ${result2.imported} skill(s) from ${file}`);
10450
+ if (result2.failed > 0) printWarning(`Failed to import ${result2.failed} skill(s)`);
10451
+ for (const e of result2.errors) printInfo(` - ${e}`);
10452
+ } else {
10453
+ const { skill, warnings } = await importSkillMdFile(file, bank);
10454
+ if (globalOpts.json) {
10455
+ console.log(JSON.stringify({ imported: 1, skill: skill.id, warnings }, null, 2));
10456
+ return;
10457
+ }
10458
+ printSuccess(`Imported "${skill.id}"`);
10459
+ for (const w of warnings) printWarning(w);
10460
+ }
10461
+ return;
10462
+ }
10463
+ const content = fs16.readFileSync(file, "utf-8");
9674
10464
  let skills;
9675
10465
  const isIndexerFormat = options.fromIndexer || options.autoDetect !== false && isLikelyIndexerFormat(content);
9676
10466
  if (isIndexerFormat) {
@@ -9729,11 +10519,9 @@ var importCommand = new import_commander13.Command("import").description("Import
9729
10519
  });
9730
10520
 
9731
10521
  // src/cli/commands/indexer/index.ts
9732
- init_cjs_shims();
9733
10522
  var import_commander20 = require("commander");
9734
10523
 
9735
10524
  // src/cli/commands/indexer/scrape.ts
9736
- init_cjs_shims();
9737
10525
  var import_commander14 = require("commander");
9738
10526
  var import_child_process2 = require("child_process");
9739
10527
  var scrapeCommand = new import_commander14.Command("scrape").description("Scrape skills from GitHub sources").argument("[url]", "Repository or awesome-list URL").option("-d, --discover", "Discover from default sources").option("-f, --force", "Force scrape even if no changes detected").option("--standalone", "Use standalone skillindexer CLI (fallback)").option("--import", "Auto-import scraped skills into skill-tree", true).action(async (url, options, command) => {
@@ -9858,7 +10646,6 @@ async function runSkillIndexer(args, quiet) {
9858
10646
  }
9859
10647
 
9860
10648
  // src/cli/commands/indexer/classify.ts
9861
- init_cjs_shims();
9862
10649
  var import_commander15 = require("commander");
9863
10650
  var import_child_process3 = require("child_process");
9864
10651
  var classifyCommand = new import_commander15.Command("classify").description("Classify unindexed skills using AI").option("-s, --skill <id>", "Classify specific skill by ID").option("--all", "Re-classify all skills (including already indexed)").option("--standalone", "Use standalone skillindexer CLI (fallback)").action(async (options, command) => {
@@ -9953,7 +10740,6 @@ async function runSkillIndexer2(args, quiet) {
9953
10740
  }
9954
10741
 
9955
10742
  // src/cli/commands/indexer/taxonomy.ts
9956
- init_cjs_shims();
9957
10743
  var import_commander16 = require("commander");
9958
10744
  var import_child_process4 = require("child_process");
9959
10745
  var taxonomyCommand = new import_commander16.Command("taxonomy").description("Browse the taxonomy tree").argument("[path]", 'Subtree path (e.g., "Development/Python")').option("-i, --interactive", "Interactive browsing mode").option("--standalone", "Use standalone skillindexer CLI (fallback)").action(async (pathArg, options, command) => {
@@ -10034,7 +10820,6 @@ async function runSkillIndexer3(args, quiet) {
10034
10820
  }
10035
10821
 
10036
10822
  // src/cli/commands/indexer/relationships.ts
10037
- init_cjs_shims();
10038
10823
  var import_commander17 = require("commander");
10039
10824
  var import_child_process5 = require("child_process");
10040
10825
  var relationshipsCommand = new import_commander17.Command("relationships").description("Detect relationships between indexed skills").option("-s, --skill <id>", "Detect relationships for specific skill").option("--use-ai", "Use AI for relationship reasoning (slower, more accurate)").option("--clear", "Clear existing relationships before detection").option("--standalone", "Use standalone skillindexer CLI (fallback)").action(async (options, command) => {
@@ -10121,7 +10906,6 @@ async function runSkillIndexer4(args, quiet) {
10121
10906
  }
10122
10907
 
10123
10908
  // src/cli/commands/indexer/stats.ts
10124
- init_cjs_shims();
10125
10909
  var import_commander18 = require("commander");
10126
10910
  var import_child_process6 = require("child_process");
10127
10911
  var indexerStatsCommand = new import_commander18.Command("stats").description("Show indexer database statistics").option("--standalone", "Use standalone skillindexer CLI (fallback)").action(async (options, command) => {
@@ -10201,15 +10985,13 @@ async function runSkillIndexer5(args, quiet) {
10201
10985
  }
10202
10986
 
10203
10987
  // src/cli/commands/indexer/sync.ts
10204
- init_cjs_shims();
10205
10988
  var import_commander19 = require("commander");
10206
- var fs16 = __toESM(require("fs"));
10207
- var path14 = __toESM(require("path"));
10989
+ var fs17 = __toESM(require("fs"));
10990
+ var path15 = __toESM(require("path"));
10208
10991
  var os3 = __toESM(require("os"));
10209
10992
  var import_child_process7 = require("child_process");
10210
10993
 
10211
10994
  // src/services/sync.ts
10212
- init_cjs_shims();
10213
10995
  var SyncService = class {
10214
10996
  constructor(skillBank, config2 = {}) {
10215
10997
  this.syncStates = /* @__PURE__ */ new Map();
@@ -10666,11 +11448,11 @@ async function runLegacyImport(options, globalOpts) {
10666
11448
  if (!globalOpts.quiet) {
10667
11449
  printInfo("Exporting skills from indexer...");
10668
11450
  }
10669
- exportPath = path14.join(os3.tmpdir(), `skill-tree-sync-${Date.now()}.json`);
11451
+ exportPath = path15.join(os3.tmpdir(), `skill-tree-sync-${Date.now()}.json`);
10670
11452
  const args = ["export-skilltree", "-o", exportPath, "-f", "json"];
10671
11453
  if (options.indexedOnly) args.push("--indexed-only");
10672
11454
  await runSkillIndexer6(args, true);
10673
- if (!fs16.existsSync(exportPath)) {
11455
+ if (!fs17.existsSync(exportPath)) {
10674
11456
  printError("Failed to export skills from indexer");
10675
11457
  process.exit(1);
10676
11458
  }
@@ -10678,7 +11460,7 @@ async function runLegacyImport(options, globalOpts) {
10678
11460
  if (!globalOpts.quiet) {
10679
11461
  printInfo(`Reading export from ${exportPath}...`);
10680
11462
  }
10681
- const content = fs16.readFileSync(exportPath, "utf-8");
11463
+ const content = fs17.readFileSync(exportPath, "utf-8");
10682
11464
  const { skills, stats } = parseIndexerExport(content);
10683
11465
  if (!globalOpts.quiet) {
10684
11466
  printInfo(`Found ${stats.total} skills (${stats.withStructuredContent} with structured content)`);
@@ -10698,7 +11480,7 @@ async function runLegacyImport(options, globalOpts) {
10698
11480
  const result = await skillBank.importSkills(skills);
10699
11481
  if (!options.exportPath && exportPath) {
10700
11482
  try {
10701
- fs16.unlinkSync(exportPath);
11483
+ fs17.unlinkSync(exportPath);
10702
11484
  } catch {
10703
11485
  }
10704
11486
  }
@@ -10734,9 +11516,8 @@ async function runSkillIndexer6(args, quiet) {
10734
11516
  var indexerCommand = new import_commander20.Command("index").description("Skill indexer - discover and classify skills from GitHub").addCommand(scrapeCommand).addCommand(classifyCommand).addCommand(taxonomyCommand).addCommand(relationshipsCommand).addCommand(indexerStatsCommand).addCommand(syncCommand);
10735
11517
 
10736
11518
  // src/cli/commands/config.ts
10737
- init_cjs_shims();
10738
11519
  var import_commander21 = require("commander");
10739
- var fs17 = __toESM(require("fs"));
11520
+ var fs18 = __toESM(require("fs"));
10740
11521
  var configCommand = new import_commander21.Command("config").description("View and manage configuration");
10741
11522
  configCommand.command("show").description("Show current configuration").option("--path <path>", "Path to specific config value (e.g., storage.path)").action((options) => {
10742
11523
  const globalOpts = configCommand.parent?.opts() || {};
@@ -10769,7 +11550,7 @@ configCommand.command("show").description("Show current configuration").option("
10769
11550
  configCommand.command("init").description("Create default configuration file").option("-f, --force", "Overwrite existing config file").action((options) => {
10770
11551
  const globalOpts = configCommand.parent?.opts() || {};
10771
11552
  const configPath = expandPath(globalOpts.config || getConfigPath());
10772
- if (fs17.existsSync(configPath) && !options.force) {
11553
+ if (fs18.existsSync(configPath) && !options.force) {
10773
11554
  console.error(`Config file already exists: ${configPath}`);
10774
11555
  console.error("Use --force to overwrite");
10775
11556
  process.exit(1);
@@ -10788,7 +11569,7 @@ configCommand.command("path").description("Show configuration file path").action
10788
11569
  configCommand.command("edit").description("Open configuration file in editor").action(() => {
10789
11570
  const globalOpts = configCommand.parent?.opts() || {};
10790
11571
  const configPath = expandPath(globalOpts.config || getConfigPath());
10791
- if (!fs17.existsSync(configPath)) {
11572
+ if (!fs18.existsSync(configPath)) {
10792
11573
  const loader = new ConfigLoader(configPath);
10793
11574
  loader.createDefaultConfigFile();
10794
11575
  }
@@ -10819,7 +11600,7 @@ configCommand.command("defaults").description("Show default configuration values
10819
11600
  configCommand.command("validate").description("Validate configuration file").action(() => {
10820
11601
  const globalOpts = configCommand.parent?.opts() || {};
10821
11602
  const configPath = expandPath(globalOpts.config || getConfigPath());
10822
- if (!fs17.existsSync(configPath)) {
11603
+ if (!fs18.existsSync(configPath)) {
10823
11604
  console.error(`Config file not found: ${configPath}`);
10824
11605
  process.exit(1);
10825
11606
  }
@@ -10885,17 +11666,16 @@ function printConfig(obj, indent) {
10885
11666
  }
10886
11667
 
10887
11668
  // src/cli/commands/sync.ts
10888
- init_cjs_shims();
10889
11669
  var import_commander22 = require("commander");
10890
- var path15 = __toESM(require("path"));
10891
- var fs18 = __toESM(require("fs"));
11670
+ var path16 = __toESM(require("path"));
11671
+ var fs19 = __toESM(require("fs"));
10892
11672
  function getSyncConfigPath(basePath) {
10893
- return path15.join(basePath, ".skillbank", "sync-config.json");
11673
+ return path16.join(basePath, ".skillbank", "sync-config.json");
10894
11674
  }
10895
11675
  async function loadSyncConfig(basePath) {
10896
11676
  const configPath = getSyncConfigPath(basePath);
10897
11677
  try {
10898
- const content = await fs18.promises.readFile(configPath, "utf-8");
11678
+ const content = await fs19.promises.readFile(configPath, "utf-8");
10899
11679
  return JSON.parse(content);
10900
11680
  } catch {
10901
11681
  return null;
@@ -10903,8 +11683,8 @@ async function loadSyncConfig(basePath) {
10903
11683
  }
10904
11684
  async function saveSyncConfig(basePath, config2) {
10905
11685
  const configPath = getSyncConfigPath(basePath);
10906
- await fs18.promises.mkdir(path15.dirname(configPath), { recursive: true });
10907
- await fs18.promises.writeFile(configPath, JSON.stringify(config2, null, 2), "utf-8");
11686
+ await fs19.promises.mkdir(path16.dirname(configPath), { recursive: true });
11687
+ await fs19.promises.writeFile(configPath, JSON.stringify(config2, null, 2), "utf-8");
10908
11688
  }
10909
11689
  async function createSyncAdapter(basePath, globalOpts) {
10910
11690
  const config2 = await loadSyncConfig(basePath);
@@ -10934,8 +11714,8 @@ function initCommand() {
10934
11714
  agentName: options.name,
10935
11715
  environment: options.env
10936
11716
  });
10937
- const gitDir = path15.join(basePath, ".git");
10938
- if (!fs18.existsSync(gitDir)) {
11717
+ const gitDir = path16.join(basePath, ".git");
11718
+ if (!fs19.existsSync(gitDir)) {
10939
11719
  printError(`Not a git repository: ${basePath}`);
10940
11720
  printInfo("Initialize a git repository first with: git init");
10941
11721
  process.exit(1);
@@ -11210,9 +11990,8 @@ function resolveCommand() {
11210
11990
  }
11211
11991
 
11212
11992
  // src/cli/commands/read.ts
11213
- init_cjs_shims();
11214
11993
  var import_commander23 = require("commander");
11215
- var path16 = __toESM(require("path"));
11994
+ var path17 = __toESM(require("path"));
11216
11995
  function serializeSkillMd(skill) {
11217
11996
  const lines = [];
11218
11997
  lines.push("---");
@@ -11258,7 +12037,7 @@ var readCommand = new import_commander23.Command("read").description("Read skill
11258
12037
  } else {
11259
12038
  console.log(`Reading: ${skill.name}`);
11260
12039
  }
11261
- const skillDir = path16.join(basePath, ".skilltree", "skills", skill.id);
12040
+ const skillDir = path17.join(basePath, ".skilltree", "skills", skill.id);
11262
12041
  console.log(`Base directory: ${skillDir}`);
11263
12042
  console.log("");
11264
12043
  console.log(serializeSkillMd(skill));
@@ -11275,7 +12054,6 @@ var readCommand = new import_commander23.Command("read").description("Read skill
11275
12054
  });
11276
12055
 
11277
12056
  // src/cli/commands/materialize.ts
11278
- init_cjs_shims();
11279
12057
  var import_commander24 = require("commander");
11280
12058
  var materializeCommand = new import_commander24.Command("materialize").description("Materialize skills to agent-discoverable paths").option("--paths <dirs>", "Comma-separated target directories (e.g. .claude/skills,.agent/skills)").option("--agents-md <path>", "Path to write AGENTS.md").option("--format <format>", "AGENTS.md format: xml, markdown, json", "xml").option("--mode <mode>", "Materialization mode: symlink or copy", "symlink").option("-w, --watch", "Watch for changes and re-materialize").action(async (options, command) => {
11281
12059
  const globalOpts = command.optsWithGlobals();
@@ -11336,25 +12114,19 @@ var materializeCommand = new import_commander24.Command("materialize").descripti
11336
12114
  });
11337
12115
 
11338
12116
  // src/cli/commands/loadout/index.ts
11339
- init_cjs_shims();
11340
12117
  var import_commander37 = require("commander");
11341
12118
 
11342
12119
  // src/cli/commands/loadout/list.ts
11343
- init_cjs_shims();
11344
12120
  var import_commander25 = require("commander");
11345
12121
 
11346
- // src/cli/utils/loadout-server.ts
11347
- init_cjs_shims();
11348
-
11349
12122
  // src/serving/state-persistence.ts
11350
- init_cjs_shims();
11351
- var fs19 = __toESM(require("fs"));
11352
- var path17 = __toESM(require("path"));
12123
+ var fs20 = __toESM(require("fs"));
12124
+ var path18 = __toESM(require("path"));
11353
12125
  var STATE_FILENAME = ".loadout-state.json";
11354
12126
  function loadState(skillPath) {
11355
- const filePath = path17.join(skillPath, STATE_FILENAME);
12127
+ const filePath = path18.join(skillPath, STATE_FILENAME);
11356
12128
  try {
11357
- const raw = fs19.readFileSync(filePath, "utf-8");
12129
+ const raw = fs20.readFileSync(filePath, "utf-8");
11358
12130
  const data = JSON.parse(raw);
11359
12131
  return {
11360
12132
  available: new Map(data.available),
@@ -11368,7 +12140,7 @@ function loadState(skillPath) {
11368
12140
  }
11369
12141
  }
11370
12142
  function saveState(skillPath, state) {
11371
- const filePath = path17.join(skillPath, STATE_FILENAME);
12143
+ const filePath = path18.join(skillPath, STATE_FILENAME);
11372
12144
  const data = {
11373
12145
  available: Array.from(state.available.entries()),
11374
12146
  expanded: Array.from(state.expanded),
@@ -11376,12 +12148,12 @@ function saveState(skillPath, state) {
11376
12148
  source: state.source,
11377
12149
  updatedAt: state.updatedAt.toISOString()
11378
12150
  };
11379
- fs19.writeFileSync(filePath, JSON.stringify(data, null, 2));
12151
+ fs20.writeFileSync(filePath, JSON.stringify(data, null, 2));
11380
12152
  }
11381
12153
  function clearState(skillPath) {
11382
- const filePath = path17.join(skillPath, STATE_FILENAME);
12154
+ const filePath = path18.join(skillPath, STATE_FILENAME);
11383
12155
  try {
11384
- fs19.unlinkSync(filePath);
12156
+ fs20.unlinkSync(filePath);
11385
12157
  return true;
11386
12158
  } catch {
11387
12159
  return false;
@@ -11455,7 +12227,6 @@ var listSubcommand = new import_commander25.Command("list").description("List sk
11455
12227
  });
11456
12228
 
11457
12229
  // src/cli/commands/loadout/search.ts
11458
- init_cjs_shims();
11459
12230
  var import_commander26 = require("commander");
11460
12231
  var searchSubcommand = new import_commander26.Command("search").description("Search for skills to add to the loadout").argument("<query>", "Search query").option("-l, --limit <n>", "Maximum results", "10").action(async (query, options, command) => {
11461
12232
  const globalOpts = command.optsWithGlobals();
@@ -11487,7 +12258,6 @@ var searchSubcommand = new import_commander26.Command("search").description("Sea
11487
12258
  });
11488
12259
 
11489
12260
  // src/cli/commands/loadout/add.ts
11490
- init_cjs_shims();
11491
12261
  var import_commander27 = require("commander");
11492
12262
  var addSubcommand = new import_commander27.Command("add").description("Add skills to the loadout").argument("<ids...>", "Skill IDs to add").action(async (ids, _options, command) => {
11493
12263
  const globalOpts = command.optsWithGlobals();
@@ -11507,7 +12277,6 @@ var addSubcommand = new import_commander27.Command("add").description("Add skill
11507
12277
  });
11508
12278
 
11509
12279
  // src/cli/commands/loadout/remove.ts
11510
- init_cjs_shims();
11511
12280
  var import_commander28 = require("commander");
11512
12281
  var removeSubcommand = new import_commander28.Command("remove").description("Remove skills from the loadout").argument("<ids...>", "Skill IDs to remove").action(async (ids, _options, command) => {
11513
12282
  const globalOpts = command.optsWithGlobals();
@@ -11527,7 +12296,6 @@ var removeSubcommand = new import_commander28.Command("remove").description("Rem
11527
12296
  });
11528
12297
 
11529
12298
  // src/cli/commands/loadout/profile.ts
11530
- init_cjs_shims();
11531
12299
  var import_commander29 = require("commander");
11532
12300
  var profileSubcommand = new import_commander29.Command("profile").description("Switch to a named skill profile").argument("[name]", "Profile name (e.g. debugging, security, code-review)").option("--list", "List available profiles").action(async (name, options, command) => {
11533
12301
  const globalOpts = command.optsWithGlobals();
@@ -11560,7 +12328,6 @@ var profileSubcommand = new import_commander29.Command("profile").description("S
11560
12328
  });
11561
12329
 
11562
12330
  // src/cli/commands/loadout/set.ts
11563
- init_cjs_shims();
11564
12331
  var import_commander30 = require("commander");
11565
12332
  var setSubcommand = new import_commander30.Command("set").description("Set loadout from criteria (tags, task description, etc.)").option("--tags <tags>", "Comma-separated tags to match").option("--task <description>", "Task description for semantic matching").option("--max-skills <n>", "Maximum number of skills").action(async (options, command) => {
11566
12333
  const globalOpts = command.optsWithGlobals();
@@ -11591,7 +12358,6 @@ var setSubcommand = new import_commander30.Command("set").description("Set loado
11591
12358
  });
11592
12359
 
11593
12360
  // src/cli/commands/loadout/expand.ts
11594
- init_cjs_shims();
11595
12361
  var import_commander31 = require("commander");
11596
12362
  var expandSubcommand = new import_commander31.Command("expand").description("Expand a skill to see its full content").argument("<id>", "Skill ID to expand").action(async (id, _options, command) => {
11597
12363
  const globalOpts = command.optsWithGlobals();
@@ -11618,7 +12384,6 @@ var expandSubcommand = new import_commander31.Command("expand").description("Exp
11618
12384
  });
11619
12385
 
11620
12386
  // src/cli/commands/loadout/collapse.ts
11621
- init_cjs_shims();
11622
12387
  var import_commander32 = require("commander");
11623
12388
  var collapseSubcommand = new import_commander32.Command("collapse").description("Collapse an expanded skill back to summary").argument("<id>", "Skill ID to collapse").action(async (id, _options, command) => {
11624
12389
  const globalOpts = command.optsWithGlobals();
@@ -11642,7 +12407,6 @@ var collapseSubcommand = new import_commander32.Command("collapse").description(
11642
12407
  });
11643
12408
 
11644
12409
  // src/cli/commands/loadout/get.ts
11645
- init_cjs_shims();
11646
12410
  var import_commander33 = require("commander");
11647
12411
  var getSubcommand = new import_commander33.Command("get").description("Get details about a skill in the loadout").argument("<id>", "Skill ID").action(async (id, _options, command) => {
11648
12412
  const globalOpts = command.optsWithGlobals();
@@ -11666,7 +12430,6 @@ var getSubcommand = new import_commander33.Command("get").description("Get detai
11666
12430
  });
11667
12431
 
11668
12432
  // src/cli/commands/loadout/render.ts
11669
- init_cjs_shims();
11670
12433
  var import_commander34 = require("commander");
11671
12434
  var renderSubcommand = new import_commander34.Command("render").description("Render the current loadout as a system prompt (XML or Markdown)").option("--format <format>", "Output format: xml or markdown", "xml").action(async (options, command) => {
11672
12435
  const globalOpts = command.optsWithGlobals();
@@ -11686,7 +12449,6 @@ var renderSubcommand = new import_commander34.Command("render").description("Ren
11686
12449
  });
11687
12450
 
11688
12451
  // src/cli/commands/loadout/clear.ts
11689
- init_cjs_shims();
11690
12452
  var import_commander35 = require("commander");
11691
12453
  var clearSubcommand = new import_commander35.Command("clear").description("Clear the current loadout state").action(async (_options, command) => {
11692
12454
  const globalOpts = command.optsWithGlobals();
@@ -11709,16 +12471,15 @@ var clearSubcommand = new import_commander35.Command("clear").description("Clear
11709
12471
  });
11710
12472
 
11711
12473
  // src/cli/commands/loadout/browse.ts
11712
- init_cjs_shims();
11713
12474
  var import_commander36 = require("commander");
11714
12475
  var browseSubcommand = new import_commander36.Command("browse").description("Browse the skill catalog by category").argument("[path]", "Category path to browse (e.g., Development/Python)").action(async (pathArg, _options, command) => {
11715
12476
  const globalOpts = command.optsWithGlobals();
11716
12477
  try {
11717
12478
  const { server } = await createLoadoutServer(globalOpts);
11718
- const path18 = pathArg ? pathArg.split("/").filter(Boolean) : void 0;
11719
- const output = await server.agentBrowseCatalog(path18);
12479
+ const path19 = pathArg ? pathArg.split("/").filter(Boolean) : void 0;
12480
+ const output = await server.agentBrowseCatalog(path19);
11720
12481
  if (globalOpts.json) {
11721
- console.log(JSON.stringify({ path: path18 ?? [], output }, null, 2));
12482
+ console.log(JSON.stringify({ path: path19 ?? [], output }, null, 2));
11722
12483
  return;
11723
12484
  }
11724
12485
  if (!output) {
@@ -11735,8 +12496,95 @@ var browseSubcommand = new import_commander36.Command("browse").description("Bro
11735
12496
  // src/cli/commands/loadout/index.ts
11736
12497
  var loadoutCommand = new import_commander37.Command("loadout").description("Manage skill loadouts for agent sessions").addCommand(listSubcommand).addCommand(searchSubcommand).addCommand(addSubcommand).addCommand(removeSubcommand).addCommand(profileSubcommand).addCommand(setSubcommand).addCommand(expandSubcommand).addCommand(collapseSubcommand).addCommand(getSubcommand).addCommand(renderSubcommand).addCommand(clearSubcommand).addCommand(browseSubcommand);
11737
12498
 
12499
+ // src/cli/commands/skillnet.ts
12500
+ var import_commander38 = require("commander");
12501
+ function buildClient() {
12502
+ return createSkillNetClient({
12503
+ apiBaseUrl: process.env.SKILLNET_API_URL,
12504
+ githubToken: process.env.GITHUB_TOKEN,
12505
+ githubMirror: process.env.GITHUB_MIRROR
12506
+ });
12507
+ }
12508
+ var searchSubcommand2 = new import_commander38.Command("search").description("Search the SkillNet index (free, no API key)").argument("<query>", "Search query (keywords or natural language)").option("-m, --mode <mode>", "Search mode: keyword or vector", "keyword").option("-c, --category <category>", "Category filter (Development, Research, ...)").option("-l, --limit <n>", "Results per page (max 50)", (v) => parseInt(v, 10)).option("--min-stars <n>", "Minimum star count (keyword mode)", (v) => parseInt(v, 10)).option("--sort-by <field>", "Sort by stars or recent (keyword mode)").option("--threshold <n>", "Similarity threshold 0-1 (vector mode)", (v) => parseFloat(v)).action(async (query, options, command) => {
12509
+ const globalOpts = command.optsWithGlobals();
12510
+ try {
12511
+ const client = buildClient();
12512
+ const searchOpts = {
12513
+ mode: options.mode,
12514
+ category: options.category,
12515
+ limit: options.limit,
12516
+ minStars: options.minStars,
12517
+ sortBy: options.sortBy,
12518
+ threshold: options.threshold
12519
+ };
12520
+ const results = await client.search(query, searchOpts);
12521
+ if (globalOpts.json) {
12522
+ console.log(JSON.stringify(results, null, 2));
12523
+ return;
12524
+ }
12525
+ if (results.length === 0) {
12526
+ printInfo("No skills found.");
12527
+ return;
12528
+ }
12529
+ printInfo(`Found ${results.length} skill(s):
12530
+ `);
12531
+ for (const r of results) {
12532
+ console.log(` ${r.skillName} \u2B50${r.stars}${r.category ? ` [${r.category}]` : ""}`);
12533
+ if (r.skillDescription) console.log(` ${r.skillDescription}`);
12534
+ console.log(` ${r.skillUrl}`);
12535
+ }
12536
+ } catch (err) {
12537
+ printError(err.message);
12538
+ process.exit(1);
12539
+ }
12540
+ });
12541
+ var importSubcommand = new import_commander38.Command("import").description("Import skills from SkillNet into the local skill bank").argument("[query]", "Search query to import matching skills").option("-u, --url <url>", "Import a single skill by its GitHub/SkillNet URL").option("-m, --mode <mode>", "Search mode: keyword or vector", "keyword").option("-c, --category <category>", "Category filter").option("-l, --limit <n>", "Max skills to import", (v) => parseInt(v, 10), 5).option("--min-stars <n>", "Minimum star count", (v) => parseInt(v, 10)).option("--threshold <n>", "Similarity threshold 0-1 (vector mode)", (v) => parseFloat(v)).action(async (query, options, command) => {
12542
+ const globalOpts = command.optsWithGlobals();
12543
+ if (!query && !options.url) {
12544
+ printError("Provide a search query or --url to import a single skill");
12545
+ process.exit(1);
12546
+ }
12547
+ try {
12548
+ const client = buildClient();
12549
+ const bank = await createSkillBankFromOptions(globalOpts);
12550
+ if (options.url) {
12551
+ const converted = await client.importSkill(options.url, bank);
12552
+ if (globalOpts.json) {
12553
+ console.log(JSON.stringify({ imported: 1, skill: converted.skill.id }, null, 2));
12554
+ } else {
12555
+ printSuccess(`Imported "${converted.skill.id}"`);
12556
+ for (const w of converted.warnings) printWarning(w);
12557
+ }
12558
+ return;
12559
+ }
12560
+ if (!globalOpts.quiet) {
12561
+ printInfo(`Searching SkillNet for "${query}"...`);
12562
+ }
12563
+ const result = await client.importFromSearch(query, bank, {
12564
+ mode: options.mode,
12565
+ category: options.category,
12566
+ limit: options.limit,
12567
+ minStars: options.minStars,
12568
+ threshold: options.threshold
12569
+ });
12570
+ if (globalOpts.json) {
12571
+ console.log(JSON.stringify(result, null, 2));
12572
+ return;
12573
+ }
12574
+ printSuccess(`Imported ${result.imported} skill(s)`);
12575
+ if (result.failed > 0) {
12576
+ printWarning(`Failed to import ${result.failed} skill(s)`);
12577
+ for (const e of result.errors) printInfo(` - ${e}`);
12578
+ }
12579
+ } catch (err) {
12580
+ printError(err.message);
12581
+ process.exit(1);
12582
+ }
12583
+ });
12584
+ var skillnetCommand = new import_commander38.Command("skillnet").description("Search and import skills from the SkillNet ecosystem").addCommand(searchSubcommand2).addCommand(importSubcommand);
12585
+
11738
12586
  // src/cli/index.ts
11739
- var program = new import_commander38.Command();
12587
+ var program = new import_commander39.Command();
11740
12588
  var config = loadConfig();
11741
12589
  program.name("skill-tree").description("Management CLI for agent skills").version(VERSION).option("-p, --path <dir>", "Skills directory path", config.storage.path).option("-c, --config <file>", "Config file path", getConfigPath()).option("--json", "Output as JSON", config.cli.output_format === "json").option("-q, --quiet", "Suppress non-essential output", config.cli.quiet).option("--no-color", "Disable colored output", !config.cli.color);
11742
12590
  program.addCommand(listCommand);
@@ -11758,4 +12606,5 @@ program.addCommand(syncCommand2);
11758
12606
  program.addCommand(readCommand);
11759
12607
  program.addCommand(materializeCommand);
11760
12608
  program.addCommand(loadoutCommand);
12609
+ program.addCommand(skillnetCommand);
11761
12610
  program.parse();