skill-tree 0.2.0 → 0.2.1

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 (88) hide show
  1. package/README.md +75 -0
  2. package/dist/bowser-CQI7RKRA.mjs +2821 -0
  3. package/dist/chunk-2NL4MXNX.mjs +3156 -0
  4. package/dist/chunk-2STDJU5Y.mjs +1174 -0
  5. package/dist/chunk-3BCRI4CA.mjs +101 -0
  6. package/dist/chunk-3SRB47JW.mjs +8344 -0
  7. package/dist/chunk-43YOKLZP.mjs +6081 -0
  8. package/dist/chunk-4AGZU52D.mjs +7918 -0
  9. package/dist/chunk-4HXHCEFH.mjs +9157 -0
  10. package/dist/chunk-4OC5QFIF.mjs +11267 -0
  11. package/dist/chunk-4QGSDVGH.mjs +580 -0
  12. package/dist/chunk-4TFMKAVC.mjs +1225 -0
  13. package/dist/chunk-55SMGVTP.mjs +7126 -0
  14. package/dist/chunk-5C4MEQMR.mjs +125 -0
  15. package/dist/chunk-6FX4IK4Z.mjs +5368 -0
  16. package/dist/chunk-6UPDN5QM.mjs +163 -0
  17. package/dist/chunk-7EGDKOHV.mjs +9439 -0
  18. package/dist/chunk-7LMOQW5H.mjs +4893 -0
  19. package/dist/chunk-7QIQJVNP.mjs +14206 -0
  20. package/dist/chunk-7VB4ZRZO.mjs +7127 -0
  21. package/dist/chunk-A3SILZYX.mjs +8360 -0
  22. package/dist/chunk-BPVRW25O.mjs +6089 -0
  23. package/dist/chunk-BZ2JKJ54.mjs +1057 -0
  24. package/dist/chunk-CI4476KM.mjs +6607 -0
  25. package/dist/chunk-DCRKELD5.mjs +46 -0
  26. package/dist/chunk-DDXYQ74I.mjs +13969 -0
  27. package/dist/chunk-DQOFJXBX.mjs +6595 -0
  28. package/dist/chunk-E2CVK23F.mjs +8751 -0
  29. package/dist/chunk-F3YEUQAP.mjs +654 -0
  30. package/dist/chunk-FKJJ4RJG.mjs +13874 -0
  31. package/dist/chunk-II7DECZQ.mjs +9111 -0
  32. package/dist/chunk-INKVOZXK.mjs +15898 -0
  33. package/dist/chunk-J2JM7HAK.mjs +8787 -0
  34. package/dist/chunk-K6NRCSAZ.mjs +4355 -0
  35. package/dist/chunk-LACI6YL4.mjs +1379 -0
  36. package/dist/chunk-MBIGW6KU.mjs +644 -0
  37. package/dist/chunk-OYHYXKXO.mjs +7297 -0
  38. package/dist/chunk-P5GJJ4JB.mjs +9237 -0
  39. package/dist/chunk-PDPN7FW7.mjs +1045 -0
  40. package/dist/chunk-QNK3WYNA.mjs +8971 -0
  41. package/dist/chunk-QZ7TP4HQ.mjs +7 -0
  42. package/dist/chunk-RJYJGJO3.mjs +349 -0
  43. package/dist/chunk-T4PVQW5O.mjs +124 -0
  44. package/dist/chunk-TEUB6DZR.mjs +6453 -0
  45. package/dist/chunk-TWPEHDW4.mjs +1067 -0
  46. package/dist/chunk-VHFTX33A.mjs +6724 -0
  47. package/dist/chunk-Y54UK2J3.mjs +13071 -0
  48. package/dist/chunk-YDVZIFIU.mjs +2102 -0
  49. package/dist/chunk-ZQVS7MQK.mjs +6081 -0
  50. package/dist/chunk-ZYKRDDFO.mjs +163 -0
  51. package/dist/cli/index.js +1167 -323
  52. package/dist/cli/index.mjs +202 -9164
  53. package/dist/dist-es-2JG6ZWFR.mjs +69 -0
  54. package/dist/dist-es-2JGXQKUP.mjs +6077 -0
  55. package/dist/dist-es-644EP2LP.mjs +317 -0
  56. package/dist/dist-es-DSNCHWLJ.mjs +170 -0
  57. package/dist/dist-es-FIVW7BUZ.mjs +317 -0
  58. package/dist/dist-es-GXJAFBE5.mjs +22 -0
  59. package/dist/dist-es-HRBPKDMR.mjs +935 -0
  60. package/dist/dist-es-LHPJ63IO.mjs +4437 -0
  61. package/dist/dist-es-LT2AQAG7.mjs +4437 -0
  62. package/dist/dist-es-ORE4PQTL.mjs +87 -0
  63. package/dist/dist-es-TLCYJJ25.mjs +495 -0
  64. package/dist/dist-es-V4LHTSRG.mjs +69 -0
  65. package/dist/dist-es-XHTU3ZU2.mjs +935 -0
  66. package/dist/dist-es-Y2MPJ6IO.mjs +378 -0
  67. package/dist/dist-es-ZYHLY2E6.mjs +487 -0
  68. package/dist/event-streams-KIAAAC7Z.mjs +42 -0
  69. package/dist/index.d.mts +1074 -12
  70. package/dist/index.d.ts +1074 -12
  71. package/dist/index.js +38729 -600
  72. package/dist/index.mjs +129 -9693
  73. package/dist/loadSso-NPRY7QRT.mjs +579 -0
  74. package/dist/loadSso-OYKG6ZRE.mjs +579 -0
  75. package/dist/signin-LMFNL434.mjs +665 -0
  76. package/dist/signin-LUKXFXSI.mjs +743 -0
  77. package/dist/sqlite-MG45OOTV.mjs +6 -0
  78. package/dist/sqlite-OLU72GHB.mjs +6 -0
  79. package/dist/sqlite-RR2SJ3SR.mjs +7 -0
  80. package/dist/sqlite-XJRPMNAJ.mjs +6 -0
  81. package/dist/sso-oidc-NNH6SQIH.mjs +832 -0
  82. package/dist/sso-oidc-STZH2XK2.mjs +832 -0
  83. package/dist/sts-EF755UBF.mjs +6290 -0
  84. package/dist/sts-ZIS4G6FQ.mjs +6290 -0
  85. package/dist/sync-BSWMMDA6.mjs +14 -0
  86. package/dist/sync-WHIIDHML.mjs +14 -0
  87. package/dist/sync-XRWFQYBY.mjs +15 -0
  88. 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,118 @@ 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 scored = docs.map((doc) => {
150
+ let score = 0;
151
+ const tf = /* @__PURE__ */ new Map();
152
+ for (const t of doc.terms) {
153
+ tf.set(t, (tf.get(t) ?? 0) + 1);
154
+ }
155
+ for (const qt of queryTerms) {
156
+ const termDf = df.get(qt) ?? 0;
157
+ if (termDf === 0) continue;
158
+ const idf = Math.log((N - termDf + 0.5) / (termDf + 0.5) + 1);
159
+ const termTf = tf.get(qt) ?? 0;
160
+ const tfNorm = termTf * (k1 + 1) / (termTf + k1 * (1 - b + b * doc.terms.length / avgDl));
161
+ score += idf * tfNorm;
162
+ }
163
+ return { skill: doc.skill, score };
148
164
  });
165
+ return scored.filter((s) => s.score > 0).sort((a, b2) => b2.score - a.score).map((s) => s.skill);
166
+ }
167
+ tokenize(text) {
168
+ return text.toLowerCase().split(/\W+/).filter((t) => t.length > 1 && !_BaseStorageAdapter.STOP_WORDS.has(t)).map((t) => _BaseStorageAdapter.stem(t));
169
+ }
170
+ static stem(word) {
171
+ if (word.length <= 3) return word;
172
+ let w = word;
173
+ w = w.replace(/ies$/, "y");
174
+ w = w.replace(/(ation|tion)$/, "t");
175
+ w = w.replace(/sion$/, "s");
176
+ w = w.replace(/(ing|ment|ness|able|ible|ous|ive|ful|less|ize|ise|ance|ence)$/, "");
177
+ w = w.replace(/([^aeiou])ed$/, "$1");
178
+ w = w.replace(/es$/, "");
179
+ w = w.replace(/([^s])s$/, "$1");
180
+ w = w.replace(/(.)\1$/, "$1");
181
+ if (w.length <= 1) return word;
182
+ return w;
149
183
  }
150
184
  };
185
+ _BaseStorageAdapter.STOP_WORDS = /* @__PURE__ */ new Set([
186
+ "the",
187
+ "a",
188
+ "an",
189
+ "is",
190
+ "are",
191
+ "was",
192
+ "were",
193
+ "be",
194
+ "been",
195
+ "being",
196
+ "have",
197
+ "has",
198
+ "had",
199
+ "do",
200
+ "does",
201
+ "did",
202
+ "will",
203
+ "would",
204
+ "could",
205
+ "should",
206
+ "may",
207
+ "might",
208
+ "shall",
209
+ "can",
210
+ "to",
211
+ "of",
212
+ "in",
213
+ "for",
214
+ "on",
215
+ "with",
216
+ "at",
217
+ "by",
218
+ "from",
219
+ "as",
220
+ "into",
221
+ "through",
222
+ "and",
223
+ "but",
224
+ "or",
225
+ "not",
226
+ "so",
227
+ "yet",
228
+ "if",
229
+ "when",
230
+ "that",
231
+ "this",
232
+ "it",
233
+ "its",
234
+ "also",
235
+ "which",
236
+ "what",
237
+ "how",
238
+ "why",
239
+ "where"
240
+ ]);
241
+ BaseStorageAdapter = _BaseStorageAdapter;
151
242
  MemoryStorageAdapter = class extends BaseStorageAdapter {
152
243
  constructor() {
153
244
  super(...arguments);
@@ -312,7 +403,6 @@ var import_better_sqlite3, path7, fs7, SCHEMA_VERSION, SQLiteStorageAdapter;
312
403
  var init_sqlite = __esm({
313
404
  "src/storage/sqlite.ts"() {
314
405
  "use strict";
315
- init_cjs_shims();
316
406
  import_better_sqlite3 = __toESM(require("better-sqlite3"));
317
407
  path7 = __toESM(require("path"));
318
408
  fs7 = __toESM(require("fs"));
@@ -930,15 +1020,15 @@ var init_sqlite = __esm({
930
1020
  /**
931
1021
  * Get or create a taxonomy node
932
1022
  */
933
- async ensureTaxonomyNode(path18) {
1023
+ async ensureTaxonomyNode(path19) {
934
1024
  this.ensureInitialized();
935
1025
  const db = this.getDb();
936
- const pathStr = path18.join("/");
1026
+ const pathStr = path19.join("/");
937
1027
  const existing = db.prepare("SELECT id FROM taxonomy_nodes WHERE path = ?").get(pathStr);
938
1028
  if (existing) return existing.id;
939
1029
  const id = `node-${pathStr.replace(/\//g, "-").toLowerCase()}`;
940
- const name = path18[path18.length - 1] || "Root";
941
- const parentPath = path18.slice(0, -1);
1030
+ const name = path19[path19.length - 1] || "Root";
1031
+ const parentPath = path19.slice(0, -1);
942
1032
  let parentId = null;
943
1033
  if (parentPath.length > 0) {
944
1034
  parentId = await this.ensureTaxonomyNode(parentPath);
@@ -1180,7 +1270,6 @@ var DEFAULT_AGENTS_CONFIG;
1180
1270
  var init_types = __esm({
1181
1271
  "src/agents/types.ts"() {
1182
1272
  "use strict";
1183
- init_cjs_shims();
1184
1273
  DEFAULT_AGENTS_CONFIG = {
1185
1274
  format: "xml",
1186
1275
  includeIds: true,
@@ -1195,7 +1284,6 @@ var AgentsGenerator;
1195
1284
  var init_generator = __esm({
1196
1285
  "src/agents/generator.ts"() {
1197
1286
  "use strict";
1198
- init_cjs_shims();
1199
1287
  init_types();
1200
1288
  AgentsGenerator = class {
1201
1289
  constructor(config2) {
@@ -1407,7 +1495,6 @@ var AgentsParser;
1407
1495
  var init_parser = __esm({
1408
1496
  "src/agents/parser.ts"() {
1409
1497
  "use strict";
1410
- init_cjs_shims();
1411
1498
  AgentsParser = class {
1412
1499
  /**
1413
1500
  * Parse AGENTS.md content
@@ -1669,7 +1756,6 @@ var fs9, path9, AgentsSync;
1669
1756
  var init_sync = __esm({
1670
1757
  "src/agents/sync.ts"() {
1671
1758
  "use strict";
1672
- init_cjs_shims();
1673
1759
  fs9 = __toESM(require("fs"));
1674
1760
  path9 = __toESM(require("path"));
1675
1761
  init_generator();
@@ -1839,17 +1925,9 @@ var init_sync = __esm({
1839
1925
  });
1840
1926
 
1841
1927
  // 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();
1928
+ var import_commander39 = require("commander");
1850
1929
 
1851
1930
  // src/types.ts
1852
- init_cjs_shims();
1853
1931
  function hasTaxonomySupport(storage) {
1854
1932
  return typeof storage.placeInTaxonomy === "function";
1855
1933
  }
@@ -1857,16 +1935,11 @@ function hasForkSupport(storage) {
1857
1935
  return typeof storage.recordFork === "function";
1858
1936
  }
1859
1937
 
1860
- // src/sync/sync-manager.ts
1861
- init_cjs_shims();
1862
-
1863
1938
  // src/sync/git-sync-adapter.ts
1864
- init_cjs_shims();
1865
1939
  var fs2 = __toESM(require("fs"));
1866
1940
  var path2 = __toESM(require("path"));
1867
1941
 
1868
1942
  // src/sync/conflict-store.ts
1869
- init_cjs_shims();
1870
1943
  var fs = __toESM(require("fs"));
1871
1944
  var path = __toESM(require("path"));
1872
1945
  var ConflictStore = class {
@@ -2914,14 +2987,7 @@ var SyncManager = class {
2914
2987
  }
2915
2988
  };
2916
2989
 
2917
- // src/serving/graph-server.ts
2918
- init_cjs_shims();
2919
-
2920
- // src/serving/catalog-renderer.ts
2921
- init_cjs_shims();
2922
-
2923
2990
  // src/serving/xml-utils.ts
2924
- init_cjs_shims();
2925
2991
  function escapeXml(text) {
2926
2992
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
2927
2993
  }
@@ -2953,15 +3019,12 @@ var DEFAULT_CONFIG = {
2953
3019
  maxSummaryLength: 80,
2954
3020
  format: "xml"
2955
3021
  };
2956
- var CatalogRenderer = class _CatalogRenderer {
3022
+ var _CatalogRenderer = class _CatalogRenderer {
2957
3023
  constructor(storage, config2) {
2958
3024
  this.storage = storage;
2959
3025
  this.overviewCache = null;
2960
3026
  this.config = { ...DEFAULT_CONFIG, ...config2 };
2961
3027
  }
2962
- static {
2963
- this.CACHE_TTL_MS = 6e4;
2964
- }
2965
3028
  /**
2966
3029
  * Render level-0 catalog overview for system prompt injection.
2967
3030
  * Shows top-level categories with counts. ~200 tokens.
@@ -2989,11 +3052,11 @@ var CatalogRenderer = class _CatalogRenderer {
2989
3052
  * Render a specific category path for browse drill-down.
2990
3053
  * Shows subcategories at intermediate nodes, or skill summaries at leaf nodes.
2991
3054
  */
2992
- async renderCategory(path18) {
3055
+ async renderCategory(path19) {
2993
3056
  if (hasCatalogSupport(this.storage)) {
2994
- return this.renderCategoryFromTaxonomy(this.storage, path18);
3057
+ return this.renderCategoryFromTaxonomy(this.storage, path19);
2995
3058
  }
2996
- return this.renderCategoryFromTags(path18);
3059
+ return this.renderCategoryFromTags(path19);
2997
3060
  }
2998
3061
  /**
2999
3062
  * Invalidate the overview cache (e.g., after skill changes).
@@ -3031,9 +3094,9 @@ var CatalogRenderer = class _CatalogRenderer {
3031
3094
  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
3095
  return this.renderOverviewXml(totalSkills, categories);
3033
3096
  }
3034
- async renderCategoryFromTaxonomy(storage, path18) {
3035
- const tree = await storage.getTaxonomyTree(path18);
3036
- const pathStr = path18.join("/");
3097
+ async renderCategoryFromTaxonomy(storage, path19) {
3098
+ const tree = await storage.getTaxonomyTree(path19);
3099
+ const pathStr = path19.join("/");
3037
3100
  if (tree.length > 0 && tree.some((n) => n.children.length > 0)) {
3038
3101
  const root = tree[0];
3039
3102
  const rootCount = this.countNodeSkills(root);
@@ -3043,7 +3106,7 @@ var CatalogRenderer = class _CatalogRenderer {
3043
3106
  lines.push(`<catalog_browse path="${escapeXml(pathStr)}" count="${rootCount}">`);
3044
3107
  lines.push(" <subcategories>");
3045
3108
  for (const { node: child, count } of children) {
3046
- const childPath = [...path18, child.name].join("/");
3109
+ const childPath = [...path19, child.name].join("/");
3047
3110
  lines.push(` <category path="${escapeXml(childPath)}" count="${count}" />`);
3048
3111
  }
3049
3112
  lines.push(" </subcategories>");
@@ -3067,13 +3130,13 @@ var CatalogRenderer = class _CatalogRenderer {
3067
3130
  const categories = Array.from(tagCounts.entries()).sort((a, b) => b[1] - a[1]).slice(0, this.config.maxCategoriesPerLevel).map(([name, count]) => ({ name, count }));
3068
3131
  return this.renderOverviewXml(skills.length, categories);
3069
3132
  }
3070
- async renderCategoryFromTags(path18) {
3071
- if (path18.length === 0) {
3133
+ async renderCategoryFromTags(path19) {
3134
+ if (path19.length === 0) {
3072
3135
  return this.renderOverviewFromTags();
3073
3136
  }
3074
- const tag = path18[0];
3137
+ const tag = path19[0];
3075
3138
  const matching = await this.storage.listSkills({ status: ["active"], tags: [tag] });
3076
- const pathStr = path18.join("/");
3139
+ const pathStr = path19.join("/");
3077
3140
  return this.renderLeafSkills(matching, pathStr, matching.length);
3078
3141
  }
3079
3142
  // ===========================================================================
@@ -3126,13 +3189,276 @@ var CatalogRenderer = class _CatalogRenderer {
3126
3189
  return count;
3127
3190
  }
3128
3191
  };
3192
+ _CatalogRenderer.CACHE_TTL_MS = 6e4;
3193
+ var CatalogRenderer = _CatalogRenderer;
3194
+
3195
+ // src/serving/term-similarity.ts
3196
+ var STOP_WORDS = /* @__PURE__ */ new Set([
3197
+ "the",
3198
+ "a",
3199
+ "an",
3200
+ "is",
3201
+ "are",
3202
+ "was",
3203
+ "were",
3204
+ "be",
3205
+ "been",
3206
+ "being",
3207
+ "have",
3208
+ "has",
3209
+ "had",
3210
+ "do",
3211
+ "does",
3212
+ "did",
3213
+ "will",
3214
+ "would",
3215
+ "could",
3216
+ "should",
3217
+ "may",
3218
+ "might",
3219
+ "shall",
3220
+ "can",
3221
+ "to",
3222
+ "of",
3223
+ "in",
3224
+ "for",
3225
+ "on",
3226
+ "with",
3227
+ "at",
3228
+ "by",
3229
+ "from",
3230
+ "as",
3231
+ "into",
3232
+ "through",
3233
+ "during",
3234
+ "before",
3235
+ "after",
3236
+ "and",
3237
+ "but",
3238
+ "or",
3239
+ "nor",
3240
+ "not",
3241
+ "so",
3242
+ "yet",
3243
+ "both",
3244
+ "either",
3245
+ "neither",
3246
+ "each",
3247
+ "every",
3248
+ "all",
3249
+ "any",
3250
+ "few",
3251
+ "more",
3252
+ "most",
3253
+ "other",
3254
+ "some",
3255
+ "such",
3256
+ "no",
3257
+ "only",
3258
+ "own",
3259
+ "same",
3260
+ "than",
3261
+ "too",
3262
+ "very",
3263
+ "just",
3264
+ "because",
3265
+ "if",
3266
+ "when",
3267
+ "that",
3268
+ "this",
3269
+ "it",
3270
+ "its",
3271
+ "also",
3272
+ "which",
3273
+ "what",
3274
+ "how",
3275
+ "why",
3276
+ "where"
3277
+ ]);
3278
+ function tokenizeList(text) {
3279
+ return text.toLowerCase().split(/\W+/).filter((t) => t.length > 2 && !STOP_WORDS.has(t));
3280
+ }
3281
+ function tokenize(text) {
3282
+ return new Set(tokenizeList(text));
3283
+ }
3284
+
3285
+ // src/serving/hybrid-retrieval.ts
3286
+ var DEFAULT_FIELD_WEIGHTS = {
3287
+ name: 10,
3288
+ description: 5,
3289
+ body: 5,
3290
+ tags: 3
3291
+ };
3292
+ var DEFAULTS = {
3293
+ rrfK: 60,
3294
+ bm25K1: 1.2,
3295
+ bm25B: 0.75,
3296
+ bm25Saturation: 8,
3297
+ signalWeights: { lexical: 0.5, dense: 0.5 }
3298
+ };
3299
+ function buildFieldDoc(skill, fw) {
3300
+ const fields = [
3301
+ [fw.name, tokenizeList(skill.name)],
3302
+ [fw.description, tokenizeList(skill.description)],
3303
+ [fw.body, tokenizeList(skill.instructions ?? "")],
3304
+ [fw.tags, skill.tags.flatMap((t) => tokenizeList(t))]
3305
+ ];
3306
+ const wtf = /* @__PURE__ */ new Map();
3307
+ let dl = 0;
3308
+ for (const [weight, toks] of fields) {
3309
+ if (weight <= 0) continue;
3310
+ dl += weight * toks.length;
3311
+ for (const t of toks) wtf.set(t, (wtf.get(t) ?? 0) + weight);
3312
+ }
3313
+ return { id: skill.id, wtf, dl };
3314
+ }
3315
+ function bm25Scores(query, skills, fieldWeights = DEFAULT_FIELD_WEIGHTS, k1 = DEFAULTS.bm25K1, b = DEFAULTS.bm25B) {
3316
+ const scores = /* @__PURE__ */ new Map();
3317
+ if (skills.length === 0) return scores;
3318
+ const queryTerms = [...tokenize(query)];
3319
+ const docs = skills.map((s) => buildFieldDoc(s, fieldWeights));
3320
+ const N = docs.length;
3321
+ const avgdl = docs.reduce((sum, d) => sum + d.dl, 0) / N || 1;
3322
+ const df = /* @__PURE__ */ new Map();
3323
+ for (const t of queryTerms) {
3324
+ let count = 0;
3325
+ for (const d of docs) if (d.wtf.has(t)) count++;
3326
+ df.set(t, count);
3327
+ }
3328
+ for (const d of docs) {
3329
+ let score = 0;
3330
+ for (const t of queryTerms) {
3331
+ const f = d.wtf.get(t);
3332
+ if (!f) continue;
3333
+ const n = df.get(t);
3334
+ const idf = Math.log(1 + (N - n + 0.5) / (n + 0.5));
3335
+ const denom = f + k1 * (1 - b + b * (d.dl / avgdl));
3336
+ score += idf * (f * (k1 + 1) / (denom || 1));
3337
+ }
3338
+ scores.set(d.id, score);
3339
+ }
3340
+ return scores;
3341
+ }
3342
+ function cosineSimilarity(a, b) {
3343
+ const len = Math.min(a.length, b.length);
3344
+ let dot = 0;
3345
+ let na = 0;
3346
+ let nb = 0;
3347
+ for (let i = 0; i < len; i++) {
3348
+ dot += a[i] * b[i];
3349
+ na += a[i] * a[i];
3350
+ nb += b[i] * b[i];
3351
+ }
3352
+ if (na === 0 || nb === 0) return 0;
3353
+ return dot / (Math.sqrt(na) * Math.sqrt(nb));
3354
+ }
3355
+ function reciprocalRankFusion(rankings, k = DEFAULTS.rrfK) {
3356
+ const fused = /* @__PURE__ */ new Map();
3357
+ for (const ranking of rankings) {
3358
+ ranking.forEach((id, idx) => {
3359
+ fused.set(id, (fused.get(id) ?? 0) + 1 / (k + idx + 1));
3360
+ });
3361
+ }
3362
+ return fused;
3363
+ }
3364
+ function skillEmbedText(skill) {
3365
+ return [
3366
+ skill.name,
3367
+ skill.description,
3368
+ skill.tags.join(" "),
3369
+ skill.instructions ?? ""
3370
+ ].join("\n");
3371
+ }
3372
+ function rankByScore(scores) {
3373
+ return [...scores.entries()].sort((a, b) => b[1] - a[1]).map(([id]) => id);
3374
+ }
3375
+ async function scoreSkillsHybrid(query, skills, options = {}) {
3376
+ if (skills.length === 0) return [];
3377
+ const fieldWeights = options.fieldWeights ?? DEFAULT_FIELD_WEIGHTS;
3378
+ const sat = options.bm25Saturation ?? DEFAULTS.bm25Saturation;
3379
+ const raw = bm25Scores(
3380
+ query,
3381
+ skills,
3382
+ fieldWeights,
3383
+ options.bm25K1 ?? DEFAULTS.bm25K1,
3384
+ options.bm25B ?? DEFAULTS.bm25B
3385
+ );
3386
+ const lexAbs = /* @__PURE__ */ new Map();
3387
+ for (const s of skills) {
3388
+ const r = raw.get(s.id) ?? 0;
3389
+ lexAbs.set(s.id, r / (r + sat));
3390
+ }
3391
+ let denseAbs = null;
3392
+ if (options.embedder) {
3393
+ try {
3394
+ const vectors = await options.embedder.embed([
3395
+ query,
3396
+ ...skills.map(skillEmbedText)
3397
+ ]);
3398
+ const queryVec = vectors[0];
3399
+ if (queryVec && vectors.length === skills.length + 1) {
3400
+ denseAbs = /* @__PURE__ */ new Map();
3401
+ skills.forEach((s, i) => {
3402
+ denseAbs.set(s.id, Math.max(0, cosineSimilarity(queryVec, vectors[i + 1])));
3403
+ });
3404
+ }
3405
+ } catch {
3406
+ denseAbs = null;
3407
+ }
3408
+ }
3409
+ const sw = options.signalWeights ?? DEFAULTS.signalWeights;
3410
+ const wLex = sw.lexical;
3411
+ const wDense = denseAbs ? sw.dense : 0;
3412
+ const wSum = wLex + wDense || 1;
3413
+ const useUtility = !!options.utilityScorer?.trained;
3414
+ const scored = skills.map((s) => {
3415
+ const l = lexAbs.get(s.id) ?? 0;
3416
+ const d = denseAbs?.get(s.id) ?? 0;
3417
+ const relevanceScore = useUtility ? options.utilityScorer.score({ skillId: s.id, lexAbs: l, denseAbs: d }) : (wLex * l + wDense * d) / wSum;
3418
+ return { skill: s, relevanceScore };
3419
+ });
3420
+ if (!useUtility && (options.fusion ?? "weighted") === "rrf" && denseAbs) {
3421
+ const rrf = reciprocalRankFusion(
3422
+ [rankByScore(lexAbs), rankByScore(denseAbs)],
3423
+ options.rrfK ?? DEFAULTS.rrfK
3424
+ );
3425
+ scored.sort(
3426
+ (a, b) => (rrf.get(b.skill.id) ?? 0) - (rrf.get(a.skill.id) ?? 0) || b.relevanceScore - a.relevanceScore
3427
+ );
3428
+ } else {
3429
+ scored.sort((a, b) => b.relevanceScore - a.relevanceScore);
3430
+ }
3431
+ if (options.reranker) {
3432
+ const topN = options.rerankTopN ?? 20;
3433
+ const head = scored.slice(0, topN);
3434
+ if (head.length > 1) {
3435
+ const ranked = await options.reranker.rerank(
3436
+ query,
3437
+ head.map((h) => ({
3438
+ id: h.skill.id,
3439
+ text: `${h.skill.name}
3440
+ ${h.skill.description}
3441
+ ${h.skill.instructions ?? ""}`
3442
+ }))
3443
+ );
3444
+ const byId = new Map(head.map((h) => [h.skill.id, h.skill]));
3445
+ const fusedDesc = head.map((h) => h.relevanceScore);
3446
+ const reordered = ranked.filter((r) => byId.has(r.id)).map((r, i) => ({ skill: byId.get(r.id), relevanceScore: fusedDesc[i] ?? 0 }));
3447
+ const headIds = new Set(reordered.map((s) => s.skill.id));
3448
+ const tail = scored.filter((s) => !headIds.has(s.skill.id));
3449
+ return [...reordered, ...tail];
3450
+ }
3451
+ }
3452
+ return scored;
3453
+ }
3129
3454
 
3130
3455
  // src/serving/loadout-compiler.ts
3131
- init_cjs_shims();
3132
3456
  var DEFAULT_CONFIG2 = {
3133
3457
  defaultMaxSkills: 15,
3134
3458
  defaultStatus: ["active"],
3135
- semanticThreshold: 0.6
3459
+ semanticThreshold: 0.6,
3460
+ retrieval: {},
3461
+ scoringPoolSize: 200
3136
3462
  };
3137
3463
  var LoadoutCompiler = class {
3138
3464
  constructor(storage, config2) {
@@ -3184,6 +3510,66 @@ var LoadoutCompiler = class {
3184
3510
  maxSkills: this.config.defaultMaxSkills
3185
3511
  });
3186
3512
  }
3513
+ /**
3514
+ * Compile with hybrid-retrieval scoring against a task description
3515
+ * (Tier 1). Returns skills annotated with absolute relevance scores in
3516
+ * [0,1], sorted by descending relevance. Used by the hybrid loadout
3517
+ * strategy to determine which skills should be auto-expanded vs shown
3518
+ * as summaries vs excluded.
3519
+ *
3520
+ * Scoring uses field-weighted BM25 over the skill name/description/body/
3521
+ * tags (the body matters most), optionally fused with dense embeddings
3522
+ * (when an `embedder` is
3523
+ * configured via `retrieval`). The candidate pool is the (filtered) set
3524
+ * up to `scoringPoolSize` — larger than the final loadout — so the ranker
3525
+ * re-ranks a real corpus rather than only a lexically pre-truncated top-N.
3526
+ */
3527
+ async compileWithScoring(taskDescription, criteria) {
3528
+ const baseCriteria = {
3529
+ ...criteria,
3530
+ taskDescription,
3531
+ // Score a broad pool, not the final loadout size. Callers narrow the
3532
+ // result via partitionByConfidence + maxExpanded downstream.
3533
+ maxSkills: criteria?.maxSkills ?? this.config.scoringPoolSize
3534
+ };
3535
+ const skills = await this.compile(baseCriteria);
3536
+ return scoreSkillsHybrid(taskDescription, skills, this.config.retrieval);
3537
+ }
3538
+ /**
3539
+ * Partition scored skills into confidence tiers.
3540
+ * - High confidence (>= expandAbove): should be auto-expanded
3541
+ * - Medium confidence (>= includeAbove): included as summaries
3542
+ * - Below includeAbove: excluded
3543
+ *
3544
+ * Abstain floor (Tier 1, T1.3): if `thresholds.minConfidence` is set and
3545
+ * even the single best-scoring skill is below it, the whole loadout
3546
+ * abstains — every skill is excluded and **nothing** is injected. This
3547
+ * makes "no sufficiently relevant skill" a first-class outcome (B=0),
3548
+ * which prevents irrelevant skills from dragging task success below the
3549
+ * no-skill baseline. `scored` is expected to be sorted descending, but we
3550
+ * defensively take the max rather than assume order.
3551
+ */
3552
+ partitionByConfidence(scored, thresholds) {
3553
+ const expand = [];
3554
+ const summarize = [];
3555
+ const excluded = [];
3556
+ if (thresholds.minConfidence !== void 0) {
3557
+ const topScore = scored.reduce((m, s) => Math.max(m, s.relevanceScore), 0);
3558
+ if (scored.length === 0 || topScore < thresholds.minConfidence) {
3559
+ return { expand, summarize, excluded: [...scored] };
3560
+ }
3561
+ }
3562
+ for (const item of scored) {
3563
+ if (item.relevanceScore >= thresholds.expandAbove) {
3564
+ expand.push(item);
3565
+ } else if (item.relevanceScore >= thresholds.includeAbove) {
3566
+ summarize.push(item);
3567
+ } else {
3568
+ excluded.push(item);
3569
+ }
3570
+ }
3571
+ return { expand, summarize, excluded };
3572
+ }
3187
3573
  /**
3188
3574
  * Compile from a named profile
3189
3575
  */
@@ -3255,13 +3641,31 @@ var LoadoutCompiler = class {
3255
3641
  return result;
3256
3642
  }
3257
3643
  /**
3258
- * Apply semantic filters (task description, problem context, etc.)
3644
+ * Apply semantic filters (task description matching).
3259
3645
  *
3260
- * Currently returns skills unchanged. Semantic matching was removed;
3261
- * use SQLite FTS via storage.searchSkills() for keyword-based search.
3646
+ * When `taskDescription` is provided, uses storage.searchSkills()
3647
+ * to find matching skills and boosts them to the front. Skills not
3648
+ * matching the search are retained at lower priority so that tag
3649
+ * filters and explicit includes still work.
3262
3650
  */
3263
- async applySemanticFilters(skills, _criteria) {
3264
- return skills;
3651
+ async applySemanticFilters(skills, criteria) {
3652
+ if (!criteria.taskDescription) return skills;
3653
+ const searchResults = await this.storage.searchSkills(criteria.taskDescription);
3654
+ const searchIds = new Set(searchResults.map((s) => s.id));
3655
+ const candidateIds = new Set(skills.map((s) => s.id));
3656
+ const boosted = [];
3657
+ const rest = [];
3658
+ for (const s of searchResults) {
3659
+ if (candidateIds.has(s.id)) {
3660
+ boosted.push(s);
3661
+ }
3662
+ }
3663
+ for (const s of skills) {
3664
+ if (!searchIds.has(s.id)) {
3665
+ rest.push(s);
3666
+ }
3667
+ }
3668
+ return [...boosted, ...rest];
3265
3669
  }
3266
3670
  /**
3267
3671
  * Apply relationship-based filters (root skills, dependencies)
@@ -3347,8 +3751,6 @@ var LoadoutCompiler = class {
3347
3751
  */
3348
3752
  applyLimits(skills, criteria) {
3349
3753
  let result = skills;
3350
- if (criteria.priorityOrder === "relevance") {
3351
- }
3352
3754
  const maxSkills = criteria.maxSkills ?? this.config.defaultMaxSkills;
3353
3755
  if (result.length > maxSkills) {
3354
3756
  result = result.slice(0, maxSkills);
@@ -3384,79 +3786,13 @@ var LoadoutCompiler = class {
3384
3786
  };
3385
3787
 
3386
3788
  // src/serving/project-detector.ts
3387
- init_cjs_shims();
3388
3789
  var import_fs = require("fs");
3389
3790
  var import_path = require("path");
3390
- var ProjectDetector = class _ProjectDetector {
3791
+ var _ProjectDetector = class _ProjectDetector {
3391
3792
  constructor() {
3392
3793
  /** Cache for project context */
3393
3794
  this.cache = /* @__PURE__ */ new Map();
3394
3795
  }
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
3796
  /**
3461
3797
  * Detect project context from a directory
3462
3798
  */
@@ -3618,9 +3954,64 @@ var ProjectDetector = class _ProjectDetector {
3618
3954
  }
3619
3955
  }
3620
3956
  };
3957
+ /** Project type patterns */
3958
+ _ProjectDetector.PROJECT_TYPES = [
3959
+ { manifestFile: "package.json", type: "nodejs", tags: ["nodejs", "javascript"], packageManager: "npm" },
3960
+ { manifestFile: "pyproject.toml", type: "python", tags: ["python"], packageManager: "pip" },
3961
+ { manifestFile: "requirements.txt", type: "python", tags: ["python"], packageManager: "pip" },
3962
+ { manifestFile: "Cargo.toml", type: "rust", tags: ["rust"], packageManager: "cargo" },
3963
+ { manifestFile: "go.mod", type: "go", tags: ["go", "golang"] },
3964
+ { manifestFile: "pom.xml", type: "java", tags: ["java", "maven"], packageManager: "maven" },
3965
+ { manifestFile: "build.gradle", type: "java", tags: ["java", "gradle"], packageManager: "gradle" },
3966
+ { manifestFile: "build.gradle.kts", type: "kotlin", tags: ["kotlin", "gradle"], packageManager: "gradle" }
3967
+ ];
3968
+ /** TypeScript detection */
3969
+ _ProjectDetector.TYPESCRIPT_FILES = ["tsconfig.json", "tsconfig.base.json"];
3970
+ /** Node.js framework patterns */
3971
+ _ProjectDetector.NODE_FRAMEWORKS = [
3972
+ { name: "react", packageName: "react", tags: ["react", "frontend"] },
3973
+ { name: "next", packageName: "next", tags: ["nextjs", "react", "fullstack"] },
3974
+ { name: "vue", packageName: "vue", tags: ["vue", "frontend"] },
3975
+ { name: "nuxt", packageName: "nuxt", tags: ["nuxt", "vue", "fullstack"] },
3976
+ { name: "angular", packageName: "@angular/core", tags: ["angular", "frontend"] },
3977
+ { name: "svelte", packageName: "svelte", tags: ["svelte", "frontend"] },
3978
+ { name: "express", packageName: "express", tags: ["express", "backend", "api"] },
3979
+ { name: "fastify", packageName: "fastify", tags: ["fastify", "backend", "api"] },
3980
+ { name: "nestjs", packageName: "@nestjs/core", tags: ["nestjs", "backend", "api"] },
3981
+ { name: "hono", packageName: "hono", tags: ["hono", "backend", "api"] },
3982
+ { name: "prisma", packageName: "@prisma/client", tags: ["prisma", "database", "orm"] },
3983
+ { name: "drizzle", packageName: "drizzle-orm", tags: ["drizzle", "database", "orm"] },
3984
+ { name: "typeorm", packageName: "typeorm", tags: ["typeorm", "database", "orm"] },
3985
+ { name: "jest", packageName: "jest", tags: ["testing", "jest"] },
3986
+ { name: "vitest", packageName: "vitest", tags: ["testing", "vitest"] },
3987
+ { name: "playwright", packageName: "@playwright/test", tags: ["testing", "e2e", "playwright"] },
3988
+ { name: "cypress", packageName: "cypress", tags: ["testing", "e2e", "cypress"] }
3989
+ ];
3990
+ /** Python framework patterns (from pyproject.toml or requirements.txt) */
3991
+ _ProjectDetector.PYTHON_FRAMEWORKS = [
3992
+ { name: "fastapi", packageName: "fastapi", tags: ["fastapi", "backend", "api"] },
3993
+ { name: "django", packageName: "django", tags: ["django", "backend", "fullstack"] },
3994
+ { name: "flask", packageName: "flask", tags: ["flask", "backend", "api"] },
3995
+ { name: "sqlalchemy", packageName: "sqlalchemy", tags: ["sqlalchemy", "database", "orm"] },
3996
+ { name: "pytest", packageName: "pytest", tags: ["testing", "pytest"] },
3997
+ { name: "pydantic", packageName: "pydantic", tags: ["pydantic", "validation"] }
3998
+ ];
3999
+ /** Directory patterns */
4000
+ _ProjectDetector.DIRECTORY_PATTERNS = [
4001
+ { pattern: ".github/workflows", feature: "github-actions", tags: ["ci", "github-actions"] },
4002
+ { pattern: ".gitlab-ci.yml", feature: "gitlab-ci", tags: ["ci", "gitlab"] },
4003
+ { pattern: "Dockerfile", feature: "docker", tags: ["docker", "containers"] },
4004
+ { pattern: "docker-compose.yml", feature: "docker-compose", tags: ["docker", "containers"] },
4005
+ { pattern: "docker-compose.yaml", feature: "docker-compose", tags: ["docker", "containers"] },
4006
+ { pattern: "terraform", feature: "terraform", tags: ["terraform", "infrastructure"] },
4007
+ { pattern: "kubernetes", feature: "kubernetes", tags: ["kubernetes", "infrastructure"] },
4008
+ { pattern: "k8s", feature: "kubernetes", tags: ["kubernetes", "infrastructure"] },
4009
+ { pattern: ".env.example", feature: "env-config", tags: ["configuration"] },
4010
+ { pattern: "prisma/schema.prisma", feature: "prisma", tags: ["prisma", "database"] }
4011
+ ];
4012
+ var ProjectDetector = _ProjectDetector;
3621
4013
 
3622
4014
  // src/serving/view-renderer.ts
3623
- init_cjs_shims();
3624
4015
  var DEFAULT_CONFIG3 = {
3625
4016
  includeTokenEstimates: false,
3626
4017
  maxSummaryLength: 150
@@ -3672,6 +4063,9 @@ var ViewRenderer = class {
3672
4063
  lines.push(`<skill id="${this.escapeXml(id)}" state="available">`);
3673
4064
  lines.push(` <name>${this.escapeXml(skill.name)}</name>`);
3674
4065
  lines.push(` <description>${this.escapeXml(summary)}</description>`);
4066
+ if (skill.serving?.instructionPreview) {
4067
+ lines.push(` <key_insight>${this.escapeXml(skill.serving.instructionPreview)}</key_insight>`);
4068
+ }
3675
4069
  if (skill.tags.length > 0) {
3676
4070
  lines.push(` <tags>${skill.tags.map((t) => this.escapeXml(t)).join(", ")}</tags>`);
3677
4071
  }
@@ -3810,13 +4204,15 @@ var ViewRenderer = class {
3810
4204
  */
3811
4205
  estimateSummaryTokens(skill) {
3812
4206
  const summary = this.getSummary(skill);
3813
- const content = [skill.name, summary, skill.tags.join(" ")].join(" ");
3814
- return Math.ceil(content.length / 4);
4207
+ const parts = [skill.name, summary, skill.tags.join(" ")];
4208
+ if (skill.serving?.instructionPreview) {
4209
+ parts.push(skill.serving.instructionPreview);
4210
+ }
4211
+ return Math.ceil(parts.join(" ").length / 4);
3815
4212
  }
3816
4213
  };
3817
4214
 
3818
4215
  // src/serving/profiles/index.ts
3819
- init_cjs_shims();
3820
4216
  var codeReviewProfile = {
3821
4217
  tags: ["review", "quality", "security", "best-practices"],
3822
4218
  taskDescription: "review code for quality, security, and best practices",
@@ -3879,6 +4275,10 @@ var builtInProfiles = {
3879
4275
  };
3880
4276
 
3881
4277
  // src/serving/graph-server.ts
4278
+ var DEFAULT_CONFIDENCE_THRESHOLDS = {
4279
+ expandAbove: 0.3,
4280
+ includeAbove: 0.15
4281
+ };
3882
4282
  var DEFAULT_CONFIG4 = {
3883
4283
  agentCanModify: true,
3884
4284
  agentCanSetLoadout: false,
@@ -3886,8 +4286,16 @@ var DEFAULT_CONFIG4 = {
3886
4286
  requireApproval: false,
3887
4287
  autoExpandOnUse: true,
3888
4288
  autoExpandRelated: true,
3889
- maxExpanded: 5,
4289
+ // Default cap on simultaneously-expanded (full-body) skills. Set to 3:
4290
+ // on the SkillsBench selection benchmark the true-positive skill lands in
4291
+ // the top 3 every time, so 3 (vs 5) lifts loadout precision ~23%→38% with
4292
+ // zero recall loss — fewer irrelevant bodies injected into context.
4293
+ maxExpanded: 3,
3890
4294
  evictionStrategy: "lru",
4295
+ confidenceThresholds: DEFAULT_CONFIDENCE_THRESHOLDS,
4296
+ retrieval: {},
4297
+ scoringPoolSize: 200,
4298
+ deferExpansion: false,
3891
4299
  persistState: false,
3892
4300
  outputFormat: "xml",
3893
4301
  includeTokenEstimates: false,
@@ -3895,18 +4303,22 @@ var DEFAULT_CONFIG4 = {
3895
4303
  profiles: {}
3896
4304
  };
3897
4305
  var SkillGraphServer = class {
3898
- // Track LRU for eviction
3899
4306
  constructor(storage, config2) {
3900
4307
  this.storage = storage;
3901
4308
  this.catalogRenderer = null;
3902
4309
  this.handlers = /* @__PURE__ */ new Set();
3903
4310
  this.lruOrder = [];
4311
+ this.relevanceScores = /* @__PURE__ */ new Map();
3904
4312
  this.config = {
3905
4313
  ...DEFAULT_CONFIG4,
3906
4314
  ...config2,
3907
- profiles: { ...builtInProfiles, ...DEFAULT_CONFIG4.profiles, ...config2?.profiles }
4315
+ profiles: { ...builtInProfiles, ...DEFAULT_CONFIG4.profiles, ...config2?.profiles },
4316
+ confidenceThresholds: { ...DEFAULT_CONFIDENCE_THRESHOLDS, ...config2?.confidenceThresholds }
3908
4317
  };
3909
- this.compiler = new LoadoutCompiler(storage);
4318
+ this.compiler = new LoadoutCompiler(storage, {
4319
+ retrieval: this.config.retrieval,
4320
+ scoringPoolSize: this.config.scoringPoolSize
4321
+ });
3910
4322
  this.projectDetector = new ProjectDetector();
3911
4323
  this.viewRenderer = new ViewRenderer({
3912
4324
  includeTokenEstimates: this.config.includeTokenEstimates
@@ -3947,11 +4359,32 @@ var SkillGraphServer = class {
3947
4359
  return this.applyLoadout(skills, { type: "criteria", criteria });
3948
4360
  }
3949
4361
  /**
3950
- * Set loadout based on task description (semantic matching)
4362
+ * Set loadout based on task description using hybrid confidence-tiered
4363
+ * compilation. Skills above the high threshold are auto-expanded,
4364
+ * skills between high and low thresholds are included as summaries,
4365
+ * and skills below the low threshold are excluded.
4366
+ *
4367
+ * Stores relevance scores for use by the 'relevance' eviction strategy.
3951
4368
  */
3952
4369
  async setLoadoutForTask(taskDescription) {
3953
- const skills = await this.compiler.compileForTask(taskDescription);
3954
- return this.applyLoadout(skills, { type: "task", taskDescription });
4370
+ const scored = await this.compiler.compileWithScoring(taskDescription);
4371
+ const { expand, summarize, excluded } = this.compiler.partitionByConfidence(
4372
+ scored,
4373
+ this.config.confidenceThresholds
4374
+ );
4375
+ const allIncluded = [...expand, ...summarize];
4376
+ const skills = allIncluded.map((s) => s.skill);
4377
+ const state = this.applyLoadout(skills, { type: "task", taskDescription });
4378
+ for (const item of allIncluded) {
4379
+ this.relevanceScores.set(item.skill.id, item.relevanceScore);
4380
+ }
4381
+ for (const item of expand) {
4382
+ if (this.state.expanded.size >= this.config.maxExpanded) break;
4383
+ this.state.expanded.add(item.skill.id);
4384
+ this.touchLru(item.skill.id);
4385
+ this.emit({ type: "skill:expanded", skillId: item.skill.id });
4386
+ }
4387
+ return state;
3955
4388
  }
3956
4389
  /**
3957
4390
  * Set loadout based on detected project context
@@ -4151,12 +4584,8 @@ var SkillGraphServer = class {
4151
4584
  * Returns skill summaries for display
4152
4585
  */
4153
4586
  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) => ({
4587
+ const matches = await this.storage.searchSkills(query);
4588
+ return matches.slice(0, limit).map((skill) => ({
4160
4589
  id: skill.id,
4161
4590
  name: skill.name,
4162
4591
  description: this.getSummary(skill),
@@ -4197,19 +4626,19 @@ var SkillGraphServer = class {
4197
4626
  * Returns rendered category view (subcategories or skill summaries at leaf).
4198
4627
  * Pass no path for the top-level overview.
4199
4628
  */
4200
- async agentBrowseCatalog(path18) {
4629
+ async agentBrowseCatalog(path19) {
4201
4630
  if (!this.catalogRenderer) {
4202
4631
  return "<error>Catalog browsing is not enabled</error>";
4203
4632
  }
4204
- if (!path18 || path18.length === 0) {
4633
+ if (!path19 || path19.length === 0) {
4205
4634
  const result2 = await this.catalogRenderer.renderOverview();
4206
4635
  if (result2) {
4207
4636
  this.emit({ type: "catalog:browsed", path: [] });
4208
4637
  }
4209
4638
  return result2;
4210
4639
  }
4211
- const result = await this.catalogRenderer.renderCategory(path18);
4212
- this.emit({ type: "catalog:browsed", path: path18 });
4640
+ const result = await this.catalogRenderer.renderCategory(path19);
4641
+ this.emit({ type: "catalog:browsed", path: path19 });
4213
4642
  return result;
4214
4643
  }
4215
4644
  /**
@@ -4241,13 +4670,25 @@ var SkillGraphServer = class {
4241
4670
  /**
4242
4671
  * Render current state as system prompt content.
4243
4672
  * Includes catalog overview when catalog is enabled.
4673
+ *
4674
+ * When `deferExpansion` is enabled, all skills are rendered as
4675
+ * summaries regardless of expansion state — the agent must
4676
+ * explicitly request expansion. This avoids the reactive-signals
4677
+ * problem where upfront skill injection derails model planning.
4244
4678
  */
4245
4679
  async renderSystemPrompt() {
4680
+ let renderState = this.state;
4681
+ if (this.config.deferExpansion) {
4682
+ renderState = {
4683
+ ...this.state,
4684
+ expanded: /* @__PURE__ */ new Set()
4685
+ };
4686
+ }
4246
4687
  let prompt;
4247
4688
  if (this.config.outputFormat === "markdown") {
4248
- prompt = this.viewRenderer.renderMarkdown(this.state);
4689
+ prompt = this.viewRenderer.renderMarkdown(renderState);
4249
4690
  } else {
4250
- prompt = this.viewRenderer.renderXml(this.state);
4691
+ prompt = this.viewRenderer.renderXml(renderState);
4251
4692
  }
4252
4693
  if (this.catalogRenderer) {
4253
4694
  const overview = await this.catalogRenderer.renderOverview();
@@ -4285,21 +4726,70 @@ var SkillGraphServer = class {
4285
4726
  // PRIVATE HELPERS
4286
4727
  // ===========================================================================
4287
4728
  /**
4288
- * Apply a new set of skills as the loadout
4729
+ * Get the relevance score for a skill (0 if not scored).
4730
+ */
4731
+ getRelevanceScore(skillId) {
4732
+ return this.relevanceScores.get(skillId) ?? 0;
4733
+ }
4734
+ /**
4735
+ * Apply a new set of skills as the loadout.
4736
+ * After populating the available set, evaluates autoExpand triggers
4737
+ * on each skill to determine if any should be pre-expanded.
4289
4738
  */
4290
4739
  applyLoadout(skills, source) {
4291
4740
  this.state.available.clear();
4292
4741
  this.state.expanded.clear();
4293
4742
  this.state.pending.clear();
4294
4743
  this.lruOrder = [];
4744
+ this.relevanceScores.clear();
4295
4745
  for (const skill of skills) {
4296
4746
  this.state.available.set(skill.id, skill);
4297
4747
  }
4748
+ this.evaluateAutoExpand(source);
4298
4749
  this.state.source = source;
4299
4750
  this.state.updatedAt = /* @__PURE__ */ new Date();
4300
4751
  this.emit({ type: "loadout:changed", state: this.state });
4301
4752
  return this.state;
4302
4753
  }
4754
+ /**
4755
+ * Evaluate autoExpand trigger conditions for all skills in the loadout.
4756
+ * Checks keyword matches against the task description, file pattern
4757
+ * matches against the project path, and framework matches.
4758
+ */
4759
+ evaluateAutoExpand(source) {
4760
+ const taskText = source.taskDescription ?? "";
4761
+ for (const [id, skill] of this.state.available) {
4762
+ if (this.state.expanded.size >= this.config.maxExpanded) break;
4763
+ if (this.state.expanded.has(id)) continue;
4764
+ const triggers = skill.serving?.autoExpand;
4765
+ if (!triggers || triggers.length === 0) continue;
4766
+ for (const trigger of triggers) {
4767
+ if (this.matchesTrigger(trigger, taskText, source)) {
4768
+ this.state.expanded.add(id);
4769
+ this.touchLru(id);
4770
+ this.emit({ type: "skill:expanded", skillId: id });
4771
+ break;
4772
+ }
4773
+ }
4774
+ }
4775
+ }
4776
+ /**
4777
+ * Check if a single autoExpand trigger matches the current context.
4778
+ */
4779
+ matchesTrigger(trigger, taskText, source) {
4780
+ const conditions = trigger.conditions;
4781
+ if (!conditions) return false;
4782
+ const taskLower = taskText.toLowerCase();
4783
+ if (trigger.on === "mention" && conditions.keywords?.length) {
4784
+ return conditions.keywords.some((kw) => taskLower.includes(kw.toLowerCase()));
4785
+ }
4786
+ if (trigger.on === "file-match" && conditions.filePatterns?.length && source.projectPath) {
4787
+ return conditions.filePatterns.some(
4788
+ (pattern) => source.projectPath.includes(pattern)
4789
+ );
4790
+ }
4791
+ return false;
4792
+ }
4303
4793
  /**
4304
4794
  * Evict a skill from expanded based on strategy
4305
4795
  */
@@ -4310,7 +4800,7 @@ var SkillGraphServer = class {
4310
4800
  case "lru":
4311
4801
  toEvict = this.lruOrder.shift();
4312
4802
  break;
4313
- case "priority":
4803
+ case "priority": {
4314
4804
  let lowestPriority = Infinity;
4315
4805
  for (const id of this.state.expanded) {
4316
4806
  const skill = this.state.available.get(id);
@@ -4321,6 +4811,18 @@ var SkillGraphServer = class {
4321
4811
  }
4322
4812
  }
4323
4813
  break;
4814
+ }
4815
+ case "relevance": {
4816
+ let lowestScore = Infinity;
4817
+ for (const id of this.state.expanded) {
4818
+ const score = this.relevanceScores.get(id) ?? 0;
4819
+ if (score < lowestScore) {
4820
+ lowestScore = score;
4821
+ toEvict = id;
4822
+ }
4823
+ }
4824
+ break;
4825
+ }
4324
4826
  case "manual":
4325
4827
  return;
4326
4828
  }
@@ -4376,7 +4878,6 @@ var SkillGraphServer = class {
4376
4878
  };
4377
4879
 
4378
4880
  // src/serving/interfaces.ts
4379
- init_cjs_shims();
4380
4881
  function createStorageView(storage) {
4381
4882
  return {
4382
4883
  getSkill: storage.getSkill.bind(storage),
@@ -4416,11 +4917,7 @@ function createServingEventBridge() {
4416
4917
  };
4417
4918
  }
4418
4919
 
4419
- // src/federation/index.ts
4420
- init_cjs_shims();
4421
-
4422
4920
  // src/federation/remote-store.ts
4423
- init_cjs_shims();
4424
4921
  var fs3 = __toESM(require("fs"));
4425
4922
  var path3 = __toESM(require("path"));
4426
4923
  var REMOTES_FILE = "remotes.json";
@@ -4616,14 +5113,12 @@ var RemoteStore = class {
4616
5113
  };
4617
5114
 
4618
5115
  // src/federation/remote-manager.ts
4619
- init_cjs_shims();
4620
5116
  var fs5 = __toESM(require("fs"));
4621
5117
  var path5 = __toESM(require("path"));
4622
5118
  var import_child_process = require("child_process");
4623
5119
  var import_util = require("util");
4624
5120
 
4625
5121
  // src/federation/skilltree-config.ts
4626
- init_cjs_shims();
4627
5122
  var fs4 = __toESM(require("fs"));
4628
5123
  var path4 = __toESM(require("path"));
4629
5124
  function resolveSkilltreeDir(repoRoot) {
@@ -5292,11 +5787,7 @@ ${body.join("\n")}
5292
5787
  }
5293
5788
  };
5294
5789
 
5295
- // src/federation/federation-manager.ts
5296
- init_cjs_shims();
5297
-
5298
5790
  // src/versioning/semver.ts
5299
- init_cjs_shims();
5300
5791
  function parseVersion(version) {
5301
5792
  const match = version.match(
5302
5793
  /^(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9.-]+))?(?:\+([a-zA-Z0-9.-]+))?$/
@@ -5846,13 +6337,11 @@ ${remote.instructions}` : remote.instructions,
5846
6337
  init_base();
5847
6338
 
5848
6339
  // src/storage/cached.ts
5849
- init_cjs_shims();
5850
6340
  var fs8 = __toESM(require("fs"));
5851
6341
  var path8 = __toESM(require("path"));
5852
6342
  init_base();
5853
6343
 
5854
6344
  // src/storage/filesystem.ts
5855
- init_cjs_shims();
5856
6345
  var fs6 = __toESM(require("fs/promises"));
5857
6346
  var path6 = __toESM(require("path"));
5858
6347
  init_base();
@@ -6507,11 +6996,7 @@ var CachedStorageAdapter = class extends BaseStorageAdapter {
6507
6996
  }
6508
6997
  };
6509
6998
 
6510
- // src/versioning/index.ts
6511
- init_cjs_shims();
6512
-
6513
6999
  // src/versioning/lineage.ts
6514
- init_cjs_shims();
6515
7000
  var LineageTracker = class {
6516
7001
  constructor(storage) {
6517
7002
  this.storage = storage;
@@ -6754,11 +7239,7 @@ ${source}`;
6754
7239
  }
6755
7240
  };
6756
7241
 
6757
- // src/versioning/merge.ts
6758
- init_cjs_shims();
6759
-
6760
7242
  // src/hooks/registry.ts
6761
- init_cjs_shims();
6762
7243
  var import_crypto = require("crypto");
6763
7244
  var PRIORITY_ORDER = {
6764
7245
  high: 0,
@@ -6943,7 +7424,6 @@ var HookRegistry = class {
6943
7424
  var hookRegistry = new HookRegistry();
6944
7425
 
6945
7426
  // src/materialization/materializer.ts
6946
- init_cjs_shims();
6947
7427
  var fs10 = __toESM(require("fs"));
6948
7428
  var path10 = __toESM(require("path"));
6949
7429
  var SKILLTREE_MARKER_START = "<!-- SKILLTREE_START -->";
@@ -7691,8 +8171,7 @@ var SkillBank = class {
7691
8171
  * The `loadout:changed` event is currently the only one we react to.
7692
8172
  * Earlier versions also handled `skill:used` / `skill:feedback` to mutate
7693
8173
  * `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.
8174
+ * cognitive-core owns that signal via `playbook.evolution.*`.
7696
8175
  */
7697
8176
  async handleServingEvent(event) {
7698
8177
  switch (event.type) {
@@ -7797,38 +8276,15 @@ var SkillBank = class {
7797
8276
  };
7798
8277
 
7799
8278
  // src/storage/index.ts
7800
- init_cjs_shims();
7801
8279
  init_base();
7802
8280
  init_sqlite();
7803
8281
 
7804
- // src/storage/migration.ts
7805
- init_cjs_shims();
7806
-
7807
8282
  // src/agents/index.ts
7808
- init_cjs_shims();
7809
8283
  init_types();
7810
8284
  init_generator();
7811
8285
  init_parser();
7812
8286
  init_sync();
7813
8287
 
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
8288
  // src/sync/index.ts
7833
8289
  function createDefaultSyncConfig(remoteUrl, agentId, options) {
7834
8290
  return {
@@ -7858,15 +8314,10 @@ function createDefaultSyncConfig(remoteUrl, agentId, options) {
7858
8314
  }
7859
8315
 
7860
8316
  // src/services/indexer.ts
7861
- init_cjs_shims();
7862
8317
  var path12 = __toESM(require("path"));
7863
8318
  var fs12 = __toESM(require("fs"));
7864
8319
 
7865
- // src/config/index.ts
7866
- init_cjs_shims();
7867
-
7868
8320
  // src/config/types.ts
7869
- init_cjs_shims();
7870
8321
  var DEFAULT_CONFIG6 = {
7871
8322
  storage: {
7872
8323
  path: "~/.skill-tree"
@@ -7901,7 +8352,6 @@ var DEFAULT_CONFIG6 = {
7901
8352
  };
7902
8353
 
7903
8354
  // src/config/loader.ts
7904
- init_cjs_shims();
7905
8355
  var fs11 = __toESM(require("fs"));
7906
8356
  var path11 = __toESM(require("path"));
7907
8357
  var os = __toESM(require("os"));
@@ -7953,8 +8403,8 @@ function substituteEnvVarsInObject(obj) {
7953
8403
  }
7954
8404
  return obj;
7955
8405
  }
7956
- function setNestedProperty(obj, path18, value) {
7957
- const parts = path18.split(".");
8406
+ function setNestedProperty(obj, path19, value) {
8407
+ const parts = path19.split(".");
7958
8408
  let current = obj;
7959
8409
  for (let i = 0; i < parts.length - 1; i++) {
7960
8410
  const part = parts[i];
@@ -8113,8 +8563,8 @@ var ConfigLoader = class {
8113
8563
  /**
8114
8564
  * Get a specific config value by path
8115
8565
  */
8116
- get(path18) {
8117
- const parts = path18.split(".");
8566
+ get(path19) {
8567
+ const parts = path19.split(".");
8118
8568
  let current = this.getConfig();
8119
8569
  for (const part of parts) {
8120
8570
  if (current === null || typeof current !== "object") {
@@ -8197,7 +8647,6 @@ function loadConfig(configPath) {
8197
8647
  }
8198
8648
 
8199
8649
  // src/import/converter.ts
8200
- init_cjs_shims();
8201
8650
  function convertIndexerSkill(indexerSkill) {
8202
8651
  const warnings = [];
8203
8652
  const instructions = indexerSkill.content || "";
@@ -9060,20 +9509,349 @@ function createIntegratedIndexer(skillBank, config2 = {}) {
9060
9509
  return new IndexerService(config2, skillBank);
9061
9510
  }
9062
9511
 
9512
+ // src/import/skillmd.ts
9513
+ function splitFrontmatter(content) {
9514
+ const normalized = content.replace(/\r\n/g, "\n");
9515
+ const match = normalized.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
9516
+ if (match) {
9517
+ return { frontmatter: match[1], body: match[2], hasFrontmatter: true };
9518
+ }
9519
+ return { frontmatter: "", body: normalized, hasFrontmatter: false };
9520
+ }
9521
+ function extractField(yaml, field) {
9522
+ const match = yaml.match(new RegExp(`^${field}:\\s*(.+)$`, "m"));
9523
+ if (!match) return void 0;
9524
+ return match[1].trim().replace(/^["']|["']$/g, "");
9525
+ }
9526
+ function extractMultiline(yaml, field) {
9527
+ const match = yaml.match(
9528
+ new RegExp(`^${field}:\\s*\\|\\n((?:\\s{2}.+\\n?)+)`, "m")
9529
+ );
9530
+ if (match) {
9531
+ return match[1].split("\n").map((line) => line.replace(/^\s{2}/, "")).join("\n").trim();
9532
+ }
9533
+ return extractField(yaml, field);
9534
+ }
9535
+ function extractList(yaml, field) {
9536
+ const match = yaml.match(new RegExp(`^${field}:\\n((?:\\s+-\\s+.+\\n?)+)`, "m"));
9537
+ if (match) {
9538
+ return match[1].split("\n").map((line) => line.replace(/^\s+-\s+/, "").trim().replace(/^["']|["']$/g, "")).filter((line) => line.length > 0);
9539
+ }
9540
+ return [];
9541
+ }
9542
+ function parseSkillMd(content) {
9543
+ const { frontmatter, body, hasFrontmatter } = splitFrontmatter(content);
9544
+ return {
9545
+ name: extractField(frontmatter, "name"),
9546
+ description: extractMultiline(frontmatter, "description"),
9547
+ version: extractField(frontmatter, "version"),
9548
+ author: extractField(frontmatter, "author"),
9549
+ status: extractField(frontmatter, "status"),
9550
+ date: extractField(frontmatter, "date"),
9551
+ tags: extractList(frontmatter, "tags"),
9552
+ body: body.trim(),
9553
+ hasFrontmatter
9554
+ };
9555
+ }
9556
+
9557
+ // src/import/skill-from-md.ts
9558
+ var VALID_STATUSES2 = ["draft", "active", "deprecated", "experimental"];
9559
+ function slugify(input) {
9560
+ return input.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80) || "skill";
9561
+ }
9562
+ function skillFromSkillMd(content, options = {}) {
9563
+ const warnings = [];
9564
+ const parsed = parseSkillMd(content);
9565
+ if (!parsed.hasFrontmatter) {
9566
+ warnings.push("SKILL.md has no YAML frontmatter; using fallback metadata");
9567
+ }
9568
+ if (!parsed.body) {
9569
+ warnings.push("SKILL.md body is empty; instructions will be empty");
9570
+ }
9571
+ const id = slugify(options.id || parsed.name || options.defaultName || "skill");
9572
+ const tags = new Set(parsed.tags);
9573
+ for (const tag of options.extraTags ?? []) {
9574
+ if (tag) tags.add(tag);
9575
+ }
9576
+ const status = parsed.status && VALID_STATUSES2.includes(parsed.status) ? parsed.status : options.defaultStatus ?? "active";
9577
+ const now = options.now ?? /* @__PURE__ */ new Date();
9578
+ const skill = {
9579
+ id,
9580
+ name: parsed.name || options.defaultName || id,
9581
+ version: parsed.version || options.defaultVersion || "1.0.0",
9582
+ description: parsed.description || options.defaultDescription || "",
9583
+ instructions: parsed.body,
9584
+ author: parsed.author || options.defaultAuthor || "unknown",
9585
+ tags: Array.from(tags),
9586
+ createdAt: now,
9587
+ updatedAt: now,
9588
+ status,
9589
+ source: options.source,
9590
+ taxonomy: options.taxonomyPath && options.taxonomyPath.length > 0 ? { primaryPath: options.taxonomyPath } : void 0,
9591
+ externalSource: options.externalSource
9592
+ };
9593
+ return { skill, warnings, parsed };
9594
+ }
9595
+
9596
+ // src/services/skillnet.ts
9597
+ var DEFAULT_SKILLNET_API = "http://api-skillnet.openkg.cn/v1";
9598
+ function parseGitHubUrl(url) {
9599
+ const m = url.match(
9600
+ /github\.com\/([^/]+)\/([^/]+?)(?:\.git)?(?:\/(?:tree|blob)\/([^/]+)\/(.*))?$/
9601
+ );
9602
+ if (!m) return null;
9603
+ const [, owner, repo, ref, path19] = m;
9604
+ return {
9605
+ owner,
9606
+ repo,
9607
+ ref: ref || "main",
9608
+ path: (path19 || "").replace(/\/$/, "")
9609
+ };
9610
+ }
9611
+ var SkillNetClient = class {
9612
+ constructor(config2 = {}) {
9613
+ this.apiBaseUrl = (config2.apiBaseUrl || DEFAULT_SKILLNET_API).replace(/\/$/, "");
9614
+ this.githubToken = config2.githubToken;
9615
+ this.githubMirror = config2.githubMirror;
9616
+ const resolved = config2.fetchImpl || globalThis.fetch;
9617
+ if (!resolved) {
9618
+ throw new Error(
9619
+ "No fetch implementation available. Provide config.fetchImpl or run on Node 18+."
9620
+ );
9621
+ }
9622
+ this.fetchImpl = resolved;
9623
+ }
9624
+ /**
9625
+ * Search the SkillNet index. Free and requires no API key.
9626
+ */
9627
+ async search(query, options = {}) {
9628
+ const params = new URLSearchParams({ q: query });
9629
+ if (options.mode) params.set("mode", options.mode);
9630
+ if (options.category) params.set("category", options.category);
9631
+ if (options.limit != null) params.set("limit", String(options.limit));
9632
+ if (options.page != null) params.set("page", String(options.page));
9633
+ if (options.minStars != null) params.set("min_stars", String(options.minStars));
9634
+ if (options.sortBy) params.set("sort_by", options.sortBy);
9635
+ if (options.threshold != null) params.set("threshold", String(options.threshold));
9636
+ const url = `${this.apiBaseUrl}/search?${params.toString()}`;
9637
+ const res = await this.fetchImpl(url);
9638
+ if (!res.ok) {
9639
+ throw new Error(`SkillNet search failed: ${res.status} ${res.statusText}`);
9640
+ }
9641
+ const body = await res.json();
9642
+ const rows = Array.isArray(body.data) ? body.data : [];
9643
+ return rows.map((row) => ({
9644
+ skillName: String(row.skill_name ?? ""),
9645
+ skillDescription: row.skill_description != null ? String(row.skill_description) : void 0,
9646
+ author: row.author != null ? String(row.author) : void 0,
9647
+ stars: typeof row.stars === "number" ? row.stars : Number(row.stars) || 0,
9648
+ skillUrl: String(row.skill_url ?? ""),
9649
+ category: row.category != null ? String(row.category) : void 0,
9650
+ evaluation: row.evaluation && typeof row.evaluation === "object" ? row.evaluation : void 0
9651
+ }));
9652
+ }
9653
+ /**
9654
+ * Convert a GitHub skill URL into the raw URL for its SKILL.md.
9655
+ * Applies the configured mirror prefix when set.
9656
+ */
9657
+ toRawSkillMdUrl(skillUrl) {
9658
+ const parsed = parseGitHubUrl(skillUrl);
9659
+ if (!parsed) {
9660
+ throw new Error(`Not a recognizable GitHub skill URL: ${skillUrl}`);
9661
+ }
9662
+ const { owner, repo, ref, path: path19 } = parsed;
9663
+ const filePath = /SKILL\.md$/i.test(path19) ? path19 : path19 ? `${path19}/SKILL.md` : "SKILL.md";
9664
+ const raw = `https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${filePath}`;
9665
+ return this.githubMirror ? `${this.githubMirror.replace(/\/$/, "")}/${raw}` : raw;
9666
+ }
9667
+ /**
9668
+ * Fetch the raw SKILL.md content for a skill URL.
9669
+ */
9670
+ async fetchSkillMd(skillUrl) {
9671
+ const rawUrl = this.toRawSkillMdUrl(skillUrl);
9672
+ const headers = {};
9673
+ if (this.githubToken) {
9674
+ headers.Authorization = `Bearer ${this.githubToken}`;
9675
+ }
9676
+ const res = await this.fetchImpl(rawUrl, { headers });
9677
+ if (!res.ok) {
9678
+ throw new Error(
9679
+ `Failed to fetch SKILL.md (${res.status} ${res.statusText}): ${rawUrl}`
9680
+ );
9681
+ }
9682
+ return { content: await res.text(), rawUrl };
9683
+ }
9684
+ /**
9685
+ * Convert a SkillNet search result + its SKILL.md content into a skill-tree Skill.
9686
+ */
9687
+ convertSkillNetSkill(result, content, rawUrl) {
9688
+ const gh = parseGitHubUrl(result.skillUrl);
9689
+ const folderName = gh?.path ? gh.path.split("/").filter(Boolean).pop() : void 0;
9690
+ const now = /* @__PURE__ */ new Date();
9691
+ const { skill, warnings } = skillFromSkillMd(content, {
9692
+ id: folderName || result.skillName,
9693
+ defaultName: result.skillName,
9694
+ defaultDescription: result.skillDescription,
9695
+ defaultAuthor: result.author || "skillnet",
9696
+ // SkillNet skills are curated/published → default active.
9697
+ defaultStatus: "active",
9698
+ extraTags: [
9699
+ ...result.category ? [result.category.toLowerCase()] : [],
9700
+ ...gh?.repo ? [gh.repo] : []
9701
+ ],
9702
+ taxonomyPath: result.category ? [result.category] : void 0,
9703
+ source: {
9704
+ type: "imported",
9705
+ location: result.skillUrl,
9706
+ importedAt: now
9707
+ },
9708
+ externalSource: {
9709
+ url: result.skillUrl,
9710
+ repo: gh ? `${gh.owner}/${gh.repo}` : "",
9711
+ scrapedAt: now
9712
+ },
9713
+ now
9714
+ });
9715
+ return { skill, warnings, rawUrl };
9716
+ }
9717
+ /**
9718
+ * Import a single skill by its SkillNet/GitHub URL into a SkillBank.
9719
+ */
9720
+ async importSkill(skillUrl, bank, meta = {}) {
9721
+ const { content, rawUrl } = await this.fetchSkillMd(skillUrl);
9722
+ const result = {
9723
+ skillName: meta.skillName || "",
9724
+ skillDescription: meta.skillDescription,
9725
+ author: meta.author,
9726
+ stars: meta.stars ?? 0,
9727
+ skillUrl,
9728
+ category: meta.category,
9729
+ evaluation: meta.evaluation
9730
+ };
9731
+ const converted = this.convertSkillNetSkill(result, content, rawUrl);
9732
+ await bank.saveSkill(converted.skill);
9733
+ return converted;
9734
+ }
9735
+ /**
9736
+ * Search SkillNet and import the matching skills into a SkillBank.
9737
+ */
9738
+ async importFromSearch(query, bank, options = {}) {
9739
+ const result = {
9740
+ imported: 0,
9741
+ failed: 0,
9742
+ skills: [],
9743
+ errors: []
9744
+ };
9745
+ const results = await this.search(query, options);
9746
+ for (const row of results) {
9747
+ if (!row.skillUrl) {
9748
+ result.failed++;
9749
+ result.errors.push(`Skipped "${row.skillName}": no skill_url`);
9750
+ continue;
9751
+ }
9752
+ try {
9753
+ const converted = await this.importSkill(row.skillUrl, bank, row);
9754
+ result.imported++;
9755
+ result.skills.push(converted.skill);
9756
+ } catch (err) {
9757
+ result.failed++;
9758
+ result.errors.push(
9759
+ `Failed to import ${row.skillUrl}: ${err.message}`
9760
+ );
9761
+ }
9762
+ }
9763
+ return result;
9764
+ }
9765
+ };
9766
+ function createSkillNetClient(config2 = {}) {
9767
+ return new SkillNetClient(config2);
9768
+ }
9769
+
9770
+ // src/import/local.ts
9771
+ var fs13 = __toESM(require("fs/promises"));
9772
+ var path13 = __toESM(require("path"));
9773
+ var SKILL_FILE_RE = /^skill\.md$/i;
9774
+ async function findSkillMdFiles(root, maxDepth = 6) {
9775
+ const found = [];
9776
+ async function walk(dir, depth) {
9777
+ if (depth > maxDepth) return;
9778
+ let entries;
9779
+ try {
9780
+ entries = await fs13.readdir(dir, { withFileTypes: true });
9781
+ } catch (err) {
9782
+ if (err.code === "ENOENT") return;
9783
+ throw err;
9784
+ }
9785
+ for (const entry of entries) {
9786
+ if (entry.isFile() && SKILL_FILE_RE.test(entry.name)) {
9787
+ found.push({
9788
+ filePath: path13.join(dir, entry.name),
9789
+ directory: dir,
9790
+ id: path13.basename(dir)
9791
+ });
9792
+ }
9793
+ }
9794
+ for (const entry of entries) {
9795
+ if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
9796
+ await walk(path13.join(dir, entry.name), depth + 1);
9797
+ }
9798
+ }
9799
+ }
9800
+ await walk(root, 0);
9801
+ return found;
9802
+ }
9803
+ async function importSkillMdFile(filePath, bank, options = {}) {
9804
+ const content = await fs13.readFile(filePath, "utf-8");
9805
+ const dirName = path13.basename(path13.dirname(filePath));
9806
+ const now = /* @__PURE__ */ new Date();
9807
+ const { skill, warnings } = skillFromSkillMd(content, {
9808
+ source: { type: "imported", location: filePath, importedAt: now },
9809
+ now,
9810
+ ...options,
9811
+ id: options.id ?? dirName
9812
+ });
9813
+ await bank.saveSkill(skill);
9814
+ return { skill, warnings };
9815
+ }
9816
+ async function importLocalSkillDir(dirPath, bank, options = {}) {
9817
+ const result = {
9818
+ imported: 0,
9819
+ failed: 0,
9820
+ skills: [],
9821
+ errors: []
9822
+ };
9823
+ const files = await findSkillMdFiles(dirPath);
9824
+ const seen = /* @__PURE__ */ new Set();
9825
+ for (const file of files) {
9826
+ if (seen.has(file.id)) {
9827
+ result.errors.push(`Skipped duplicate id "${file.id}" at ${file.filePath}`);
9828
+ continue;
9829
+ }
9830
+ try {
9831
+ const { skill } = await importSkillMdFile(file.filePath, bank, {
9832
+ ...options,
9833
+ id: file.id
9834
+ });
9835
+ seen.add(skill.id);
9836
+ result.imported++;
9837
+ result.skills.push(skill);
9838
+ } catch (err) {
9839
+ result.failed++;
9840
+ result.errors.push(`Failed to import ${file.filePath}: ${err.message}`);
9841
+ }
9842
+ }
9843
+ return result;
9844
+ }
9845
+
9063
9846
  // src/index.ts
9064
9847
  var VERSION = "0.2.0";
9065
9848
 
9066
9849
  // src/cli/commands/list.ts
9067
- init_cjs_shims();
9068
9850
  var import_commander = require("commander");
9069
9851
 
9070
- // src/cli/utils/skillbank.ts
9071
- init_cjs_shims();
9072
-
9073
9852
  // src/cli/utils/paths.ts
9074
- init_cjs_shims();
9075
- var fs13 = __toESM(require("fs"));
9076
- var path13 = __toESM(require("path"));
9853
+ var fs14 = __toESM(require("fs"));
9854
+ var path14 = __toESM(require("path"));
9077
9855
  var os2 = __toESM(require("os"));
9078
9856
  var DEFAULT_PATHS = [
9079
9857
  ".claude/skills",
@@ -9087,13 +9865,13 @@ var DEFAULT_PATHS = [
9087
9865
  ];
9088
9866
  function expandHome(p) {
9089
9867
  if (p.startsWith("~/")) {
9090
- return path13.join(os2.homedir(), p.slice(2));
9868
+ return path14.join(os2.homedir(), p.slice(2));
9091
9869
  }
9092
9870
  return p;
9093
9871
  }
9094
9872
  function dirExists(p) {
9095
9873
  try {
9096
- return fs13.statSync(p).isDirectory();
9874
+ return fs14.statSync(p).isDirectory();
9097
9875
  } catch {
9098
9876
  return false;
9099
9877
  }
@@ -9101,23 +9879,23 @@ function dirExists(p) {
9101
9879
  function resolveSkillPath(explicitPath) {
9102
9880
  if (explicitPath) {
9103
9881
  const resolved = expandHome(explicitPath);
9104
- return path13.resolve(resolved);
9882
+ return path14.resolve(resolved);
9105
9883
  }
9106
9884
  const envPath = process.env.SKILL_TREE_PATH;
9107
9885
  if (envPath) {
9108
- return path13.resolve(expandHome(envPath));
9886
+ return path14.resolve(expandHome(envPath));
9109
9887
  }
9110
9888
  for (const defaultPath of DEFAULT_PATHS) {
9111
- const resolved = path13.resolve(expandHome(defaultPath));
9889
+ const resolved = path14.resolve(expandHome(defaultPath));
9112
9890
  if (dirExists(resolved)) {
9113
9891
  return resolved;
9114
9892
  }
9115
9893
  }
9116
- return path13.resolve(".claude/skills");
9894
+ return path14.resolve(".claude/skills");
9117
9895
  }
9118
9896
  function ensureDir(dir) {
9119
9897
  if (!dirExists(dir)) {
9120
- fs13.mkdirSync(dir, { recursive: true });
9898
+ fs14.mkdirSync(dir, { recursive: true });
9121
9899
  }
9122
9900
  }
9123
9901
 
@@ -9154,7 +9932,6 @@ function getSkillPath(options) {
9154
9932
  var getSkillBank = createSkillBankFromOptions;
9155
9933
 
9156
9934
  // src/cli/utils/output.ts
9157
- init_cjs_shims();
9158
9935
  var import_chalk = __toESM(require("chalk"));
9159
9936
  function formatSkillLine(skill) {
9160
9937
  const status = formatStatus(skill.status);
@@ -9336,7 +10113,6 @@ var listCommand = new import_commander.Command("list").description("List all ski
9336
10113
  });
9337
10114
 
9338
10115
  // src/cli/commands/show.ts
9339
- init_cjs_shims();
9340
10116
  var import_commander2 = require("commander");
9341
10117
  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
10118
  const globalOpts = command.optsWithGlobals();
@@ -9359,7 +10135,6 @@ var showCommand = new import_commander2.Command("show").description("Show skill
9359
10135
  });
9360
10136
 
9361
10137
  // src/cli/commands/search.ts
9362
- init_cjs_shims();
9363
10138
  var import_commander3 = require("commander");
9364
10139
  var searchCommand = new import_commander3.Command("search").description("Search skills by text").argument("<query>", "Search query").action(async (query, options, command) => {
9365
10140
  const globalOpts = command.optsWithGlobals();
@@ -9392,7 +10167,6 @@ var searchCommand = new import_commander3.Command("search").description("Search
9392
10167
  });
9393
10168
 
9394
10169
  // src/cli/commands/stats.ts
9395
- init_cjs_shims();
9396
10170
  var import_commander4 = require("commander");
9397
10171
  var statsCommand = new import_commander4.Command("stats").description("Show skill bank statistics").action(async (options, command) => {
9398
10172
  const globalOpts = command.optsWithGlobals();
@@ -9411,7 +10185,6 @@ var statsCommand = new import_commander4.Command("stats").description("Show skil
9411
10185
  });
9412
10186
 
9413
10187
  // src/cli/commands/versions.ts
9414
- init_cjs_shims();
9415
10188
  var import_commander5 = require("commander");
9416
10189
  var versionsCommand = new import_commander5.Command("versions").description("Show version history for a skill").argument("<id>", "Skill ID").action(async (id, options, command) => {
9417
10190
  const globalOpts = command.optsWithGlobals();
@@ -9434,7 +10207,6 @@ var versionsCommand = new import_commander5.Command("versions").description("Sho
9434
10207
  });
9435
10208
 
9436
10209
  // src/cli/commands/diff.ts
9437
- init_cjs_shims();
9438
10210
  var import_commander6 = require("commander");
9439
10211
  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
10212
  const globalOpts = command.optsWithGlobals();
@@ -9453,7 +10225,6 @@ var diffCommand = new import_commander6.Command("diff").description("Compare two
9453
10225
  });
9454
10226
 
9455
10227
  // src/cli/commands/rollback.ts
9456
- init_cjs_shims();
9457
10228
  var import_commander7 = require("commander");
9458
10229
  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
10230
  const globalOpts = command.optsWithGlobals();
@@ -9472,7 +10243,6 @@ var rollbackCommand = new import_commander7.Command("rollback").description("Rol
9472
10243
  });
9473
10244
 
9474
10245
  // src/cli/commands/fork.ts
9475
- init_cjs_shims();
9476
10246
  var import_commander8 = require("commander");
9477
10247
  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
10248
  const globalOpts = command.optsWithGlobals();
@@ -9496,7 +10266,6 @@ var forkCommand = new import_commander8.Command("fork").description("Fork a skil
9496
10266
  });
9497
10267
 
9498
10268
  // src/cli/commands/deprecate.ts
9499
- init_cjs_shims();
9500
10269
  var import_commander9 = require("commander");
9501
10270
  var deprecateCommand = new import_commander9.Command("deprecate").description("Mark a skill as deprecated").argument("<id>", "Skill ID").action(async (id, options, command) => {
9502
10271
  const globalOpts = command.optsWithGlobals();
@@ -9515,7 +10284,6 @@ var deprecateCommand = new import_commander9.Command("deprecate").description("M
9515
10284
  });
9516
10285
 
9517
10286
  // src/cli/commands/activate.ts
9518
- init_cjs_shims();
9519
10287
  var import_commander10 = require("commander");
9520
10288
  var activateCommand = new import_commander10.Command("activate").description("Mark a skill as active").argument("<id>", "Skill ID").action(async (id, options, command) => {
9521
10289
  const globalOpts = command.optsWithGlobals();
@@ -9541,7 +10309,6 @@ var activateCommand = new import_commander10.Command("activate").description("Ma
9541
10309
  });
9542
10310
 
9543
10311
  // src/cli/commands/delete.ts
9544
- init_cjs_shims();
9545
10312
  var import_commander11 = require("commander");
9546
10313
  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
10314
  const globalOpts = command.optsWithGlobals();
@@ -9574,9 +10341,8 @@ var deleteCommand = new import_commander11.Command("delete").description("Delete
9574
10341
  });
9575
10342
 
9576
10343
  // src/cli/commands/export.ts
9577
- init_cjs_shims();
9578
10344
  var import_commander12 = require("commander");
9579
- var fs14 = __toESM(require("fs"));
10345
+ var fs15 = __toESM(require("fs"));
9580
10346
  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
10347
  const globalOpts = command.optsWithGlobals();
9582
10348
  try {
@@ -9584,7 +10350,7 @@ var exportCommand = new import_commander12.Command("export").description("Export
9584
10350
  const skills = await skillBank.exportAll();
9585
10351
  const json = JSON.stringify(skills, null, 2);
9586
10352
  if (options.output) {
9587
- fs14.writeFileSync(options.output, json, "utf-8");
10353
+ fs15.writeFileSync(options.output, json, "utf-8");
9588
10354
  if (!globalOpts.quiet) {
9589
10355
  printSuccess(`Exported ${skills.length} skill(s) to ${options.output}`);
9590
10356
  }
@@ -9598,15 +10364,10 @@ var exportCommand = new import_commander12.Command("export").description("Export
9598
10364
  });
9599
10365
 
9600
10366
  // src/cli/commands/import.ts
9601
- init_cjs_shims();
9602
10367
  var import_commander13 = require("commander");
9603
- var fs15 = __toESM(require("fs"));
9604
-
9605
- // src/import/index.ts
9606
- init_cjs_shims();
10368
+ var fs16 = __toESM(require("fs"));
9607
10369
 
9608
10370
  // src/import/detect.ts
9609
- init_cjs_shims();
9610
10371
  function isSkillTreeSkill(obj) {
9611
10372
  if (typeof obj !== "object" || obj === null) return false;
9612
10373
  const skill = obj;
@@ -9663,14 +10424,38 @@ function isLikelyIndexerFormat(content) {
9663
10424
  }
9664
10425
 
9665
10426
  // 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) => {
10427
+ 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
10428
  const globalOpts = command.optsWithGlobals();
9668
10429
  try {
9669
- if (!fs15.existsSync(file)) {
9670
- printError(`File not found: ${file}`);
10430
+ if (!fs16.existsSync(file)) {
10431
+ printError(`Path not found: ${file}`);
9671
10432
  process.exit(1);
9672
10433
  }
9673
- const content = fs15.readFileSync(file, "utf-8");
10434
+ const stat = fs16.statSync(file);
10435
+ const isMarkdown = options.skillMd || stat.isDirectory() || /\.md$/i.test(file);
10436
+ if (isMarkdown) {
10437
+ const bank = await createSkillBankFromOptions(globalOpts);
10438
+ if (stat.isDirectory()) {
10439
+ const result2 = await importLocalSkillDir(file, bank);
10440
+ if (globalOpts.json) {
10441
+ console.log(JSON.stringify(result2, null, 2));
10442
+ return;
10443
+ }
10444
+ printSuccess(`Imported ${result2.imported} skill(s) from ${file}`);
10445
+ if (result2.failed > 0) printWarning(`Failed to import ${result2.failed} skill(s)`);
10446
+ for (const e of result2.errors) printInfo(` - ${e}`);
10447
+ } else {
10448
+ const { skill, warnings } = await importSkillMdFile(file, bank);
10449
+ if (globalOpts.json) {
10450
+ console.log(JSON.stringify({ imported: 1, skill: skill.id, warnings }, null, 2));
10451
+ return;
10452
+ }
10453
+ printSuccess(`Imported "${skill.id}"`);
10454
+ for (const w of warnings) printWarning(w);
10455
+ }
10456
+ return;
10457
+ }
10458
+ const content = fs16.readFileSync(file, "utf-8");
9674
10459
  let skills;
9675
10460
  const isIndexerFormat = options.fromIndexer || options.autoDetect !== false && isLikelyIndexerFormat(content);
9676
10461
  if (isIndexerFormat) {
@@ -9729,11 +10514,9 @@ var importCommand = new import_commander13.Command("import").description("Import
9729
10514
  });
9730
10515
 
9731
10516
  // src/cli/commands/indexer/index.ts
9732
- init_cjs_shims();
9733
10517
  var import_commander20 = require("commander");
9734
10518
 
9735
10519
  // src/cli/commands/indexer/scrape.ts
9736
- init_cjs_shims();
9737
10520
  var import_commander14 = require("commander");
9738
10521
  var import_child_process2 = require("child_process");
9739
10522
  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 +10641,6 @@ async function runSkillIndexer(args, quiet) {
9858
10641
  }
9859
10642
 
9860
10643
  // src/cli/commands/indexer/classify.ts
9861
- init_cjs_shims();
9862
10644
  var import_commander15 = require("commander");
9863
10645
  var import_child_process3 = require("child_process");
9864
10646
  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 +10735,6 @@ async function runSkillIndexer2(args, quiet) {
9953
10735
  }
9954
10736
 
9955
10737
  // src/cli/commands/indexer/taxonomy.ts
9956
- init_cjs_shims();
9957
10738
  var import_commander16 = require("commander");
9958
10739
  var import_child_process4 = require("child_process");
9959
10740
  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 +10815,6 @@ async function runSkillIndexer3(args, quiet) {
10034
10815
  }
10035
10816
 
10036
10817
  // src/cli/commands/indexer/relationships.ts
10037
- init_cjs_shims();
10038
10818
  var import_commander17 = require("commander");
10039
10819
  var import_child_process5 = require("child_process");
10040
10820
  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 +10901,6 @@ async function runSkillIndexer4(args, quiet) {
10121
10901
  }
10122
10902
 
10123
10903
  // src/cli/commands/indexer/stats.ts
10124
- init_cjs_shims();
10125
10904
  var import_commander18 = require("commander");
10126
10905
  var import_child_process6 = require("child_process");
10127
10906
  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 +10980,13 @@ async function runSkillIndexer5(args, quiet) {
10201
10980
  }
10202
10981
 
10203
10982
  // src/cli/commands/indexer/sync.ts
10204
- init_cjs_shims();
10205
10983
  var import_commander19 = require("commander");
10206
- var fs16 = __toESM(require("fs"));
10207
- var path14 = __toESM(require("path"));
10984
+ var fs17 = __toESM(require("fs"));
10985
+ var path15 = __toESM(require("path"));
10208
10986
  var os3 = __toESM(require("os"));
10209
10987
  var import_child_process7 = require("child_process");
10210
10988
 
10211
10989
  // src/services/sync.ts
10212
- init_cjs_shims();
10213
10990
  var SyncService = class {
10214
10991
  constructor(skillBank, config2 = {}) {
10215
10992
  this.syncStates = /* @__PURE__ */ new Map();
@@ -10666,11 +11443,11 @@ async function runLegacyImport(options, globalOpts) {
10666
11443
  if (!globalOpts.quiet) {
10667
11444
  printInfo("Exporting skills from indexer...");
10668
11445
  }
10669
- exportPath = path14.join(os3.tmpdir(), `skill-tree-sync-${Date.now()}.json`);
11446
+ exportPath = path15.join(os3.tmpdir(), `skill-tree-sync-${Date.now()}.json`);
10670
11447
  const args = ["export-skilltree", "-o", exportPath, "-f", "json"];
10671
11448
  if (options.indexedOnly) args.push("--indexed-only");
10672
11449
  await runSkillIndexer6(args, true);
10673
- if (!fs16.existsSync(exportPath)) {
11450
+ if (!fs17.existsSync(exportPath)) {
10674
11451
  printError("Failed to export skills from indexer");
10675
11452
  process.exit(1);
10676
11453
  }
@@ -10678,7 +11455,7 @@ async function runLegacyImport(options, globalOpts) {
10678
11455
  if (!globalOpts.quiet) {
10679
11456
  printInfo(`Reading export from ${exportPath}...`);
10680
11457
  }
10681
- const content = fs16.readFileSync(exportPath, "utf-8");
11458
+ const content = fs17.readFileSync(exportPath, "utf-8");
10682
11459
  const { skills, stats } = parseIndexerExport(content);
10683
11460
  if (!globalOpts.quiet) {
10684
11461
  printInfo(`Found ${stats.total} skills (${stats.withStructuredContent} with structured content)`);
@@ -10698,7 +11475,7 @@ async function runLegacyImport(options, globalOpts) {
10698
11475
  const result = await skillBank.importSkills(skills);
10699
11476
  if (!options.exportPath && exportPath) {
10700
11477
  try {
10701
- fs16.unlinkSync(exportPath);
11478
+ fs17.unlinkSync(exportPath);
10702
11479
  } catch {
10703
11480
  }
10704
11481
  }
@@ -10734,9 +11511,8 @@ async function runSkillIndexer6(args, quiet) {
10734
11511
  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
11512
 
10736
11513
  // src/cli/commands/config.ts
10737
- init_cjs_shims();
10738
11514
  var import_commander21 = require("commander");
10739
- var fs17 = __toESM(require("fs"));
11515
+ var fs18 = __toESM(require("fs"));
10740
11516
  var configCommand = new import_commander21.Command("config").description("View and manage configuration");
10741
11517
  configCommand.command("show").description("Show current configuration").option("--path <path>", "Path to specific config value (e.g., storage.path)").action((options) => {
10742
11518
  const globalOpts = configCommand.parent?.opts() || {};
@@ -10769,7 +11545,7 @@ configCommand.command("show").description("Show current configuration").option("
10769
11545
  configCommand.command("init").description("Create default configuration file").option("-f, --force", "Overwrite existing config file").action((options) => {
10770
11546
  const globalOpts = configCommand.parent?.opts() || {};
10771
11547
  const configPath = expandPath(globalOpts.config || getConfigPath());
10772
- if (fs17.existsSync(configPath) && !options.force) {
11548
+ if (fs18.existsSync(configPath) && !options.force) {
10773
11549
  console.error(`Config file already exists: ${configPath}`);
10774
11550
  console.error("Use --force to overwrite");
10775
11551
  process.exit(1);
@@ -10788,7 +11564,7 @@ configCommand.command("path").description("Show configuration file path").action
10788
11564
  configCommand.command("edit").description("Open configuration file in editor").action(() => {
10789
11565
  const globalOpts = configCommand.parent?.opts() || {};
10790
11566
  const configPath = expandPath(globalOpts.config || getConfigPath());
10791
- if (!fs17.existsSync(configPath)) {
11567
+ if (!fs18.existsSync(configPath)) {
10792
11568
  const loader = new ConfigLoader(configPath);
10793
11569
  loader.createDefaultConfigFile();
10794
11570
  }
@@ -10819,7 +11595,7 @@ configCommand.command("defaults").description("Show default configuration values
10819
11595
  configCommand.command("validate").description("Validate configuration file").action(() => {
10820
11596
  const globalOpts = configCommand.parent?.opts() || {};
10821
11597
  const configPath = expandPath(globalOpts.config || getConfigPath());
10822
- if (!fs17.existsSync(configPath)) {
11598
+ if (!fs18.existsSync(configPath)) {
10823
11599
  console.error(`Config file not found: ${configPath}`);
10824
11600
  process.exit(1);
10825
11601
  }
@@ -10885,17 +11661,16 @@ function printConfig(obj, indent) {
10885
11661
  }
10886
11662
 
10887
11663
  // src/cli/commands/sync.ts
10888
- init_cjs_shims();
10889
11664
  var import_commander22 = require("commander");
10890
- var path15 = __toESM(require("path"));
10891
- var fs18 = __toESM(require("fs"));
11665
+ var path16 = __toESM(require("path"));
11666
+ var fs19 = __toESM(require("fs"));
10892
11667
  function getSyncConfigPath(basePath) {
10893
- return path15.join(basePath, ".skillbank", "sync-config.json");
11668
+ return path16.join(basePath, ".skillbank", "sync-config.json");
10894
11669
  }
10895
11670
  async function loadSyncConfig(basePath) {
10896
11671
  const configPath = getSyncConfigPath(basePath);
10897
11672
  try {
10898
- const content = await fs18.promises.readFile(configPath, "utf-8");
11673
+ const content = await fs19.promises.readFile(configPath, "utf-8");
10899
11674
  return JSON.parse(content);
10900
11675
  } catch {
10901
11676
  return null;
@@ -10903,8 +11678,8 @@ async function loadSyncConfig(basePath) {
10903
11678
  }
10904
11679
  async function saveSyncConfig(basePath, config2) {
10905
11680
  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");
11681
+ await fs19.promises.mkdir(path16.dirname(configPath), { recursive: true });
11682
+ await fs19.promises.writeFile(configPath, JSON.stringify(config2, null, 2), "utf-8");
10908
11683
  }
10909
11684
  async function createSyncAdapter(basePath, globalOpts) {
10910
11685
  const config2 = await loadSyncConfig(basePath);
@@ -10934,8 +11709,8 @@ function initCommand() {
10934
11709
  agentName: options.name,
10935
11710
  environment: options.env
10936
11711
  });
10937
- const gitDir = path15.join(basePath, ".git");
10938
- if (!fs18.existsSync(gitDir)) {
11712
+ const gitDir = path16.join(basePath, ".git");
11713
+ if (!fs19.existsSync(gitDir)) {
10939
11714
  printError(`Not a git repository: ${basePath}`);
10940
11715
  printInfo("Initialize a git repository first with: git init");
10941
11716
  process.exit(1);
@@ -11210,9 +11985,8 @@ function resolveCommand() {
11210
11985
  }
11211
11986
 
11212
11987
  // src/cli/commands/read.ts
11213
- init_cjs_shims();
11214
11988
  var import_commander23 = require("commander");
11215
- var path16 = __toESM(require("path"));
11989
+ var path17 = __toESM(require("path"));
11216
11990
  function serializeSkillMd(skill) {
11217
11991
  const lines = [];
11218
11992
  lines.push("---");
@@ -11258,7 +12032,7 @@ var readCommand = new import_commander23.Command("read").description("Read skill
11258
12032
  } else {
11259
12033
  console.log(`Reading: ${skill.name}`);
11260
12034
  }
11261
- const skillDir = path16.join(basePath, ".skilltree", "skills", skill.id);
12035
+ const skillDir = path17.join(basePath, ".skilltree", "skills", skill.id);
11262
12036
  console.log(`Base directory: ${skillDir}`);
11263
12037
  console.log("");
11264
12038
  console.log(serializeSkillMd(skill));
@@ -11275,7 +12049,6 @@ var readCommand = new import_commander23.Command("read").description("Read skill
11275
12049
  });
11276
12050
 
11277
12051
  // src/cli/commands/materialize.ts
11278
- init_cjs_shims();
11279
12052
  var import_commander24 = require("commander");
11280
12053
  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
12054
  const globalOpts = command.optsWithGlobals();
@@ -11336,25 +12109,19 @@ var materializeCommand = new import_commander24.Command("materialize").descripti
11336
12109
  });
11337
12110
 
11338
12111
  // src/cli/commands/loadout/index.ts
11339
- init_cjs_shims();
11340
12112
  var import_commander37 = require("commander");
11341
12113
 
11342
12114
  // src/cli/commands/loadout/list.ts
11343
- init_cjs_shims();
11344
12115
  var import_commander25 = require("commander");
11345
12116
 
11346
- // src/cli/utils/loadout-server.ts
11347
- init_cjs_shims();
11348
-
11349
12117
  // src/serving/state-persistence.ts
11350
- init_cjs_shims();
11351
- var fs19 = __toESM(require("fs"));
11352
- var path17 = __toESM(require("path"));
12118
+ var fs20 = __toESM(require("fs"));
12119
+ var path18 = __toESM(require("path"));
11353
12120
  var STATE_FILENAME = ".loadout-state.json";
11354
12121
  function loadState(skillPath) {
11355
- const filePath = path17.join(skillPath, STATE_FILENAME);
12122
+ const filePath = path18.join(skillPath, STATE_FILENAME);
11356
12123
  try {
11357
- const raw = fs19.readFileSync(filePath, "utf-8");
12124
+ const raw = fs20.readFileSync(filePath, "utf-8");
11358
12125
  const data = JSON.parse(raw);
11359
12126
  return {
11360
12127
  available: new Map(data.available),
@@ -11368,7 +12135,7 @@ function loadState(skillPath) {
11368
12135
  }
11369
12136
  }
11370
12137
  function saveState(skillPath, state) {
11371
- const filePath = path17.join(skillPath, STATE_FILENAME);
12138
+ const filePath = path18.join(skillPath, STATE_FILENAME);
11372
12139
  const data = {
11373
12140
  available: Array.from(state.available.entries()),
11374
12141
  expanded: Array.from(state.expanded),
@@ -11376,12 +12143,12 @@ function saveState(skillPath, state) {
11376
12143
  source: state.source,
11377
12144
  updatedAt: state.updatedAt.toISOString()
11378
12145
  };
11379
- fs19.writeFileSync(filePath, JSON.stringify(data, null, 2));
12146
+ fs20.writeFileSync(filePath, JSON.stringify(data, null, 2));
11380
12147
  }
11381
12148
  function clearState(skillPath) {
11382
- const filePath = path17.join(skillPath, STATE_FILENAME);
12149
+ const filePath = path18.join(skillPath, STATE_FILENAME);
11383
12150
  try {
11384
- fs19.unlinkSync(filePath);
12151
+ fs20.unlinkSync(filePath);
11385
12152
  return true;
11386
12153
  } catch {
11387
12154
  return false;
@@ -11455,7 +12222,6 @@ var listSubcommand = new import_commander25.Command("list").description("List sk
11455
12222
  });
11456
12223
 
11457
12224
  // src/cli/commands/loadout/search.ts
11458
- init_cjs_shims();
11459
12225
  var import_commander26 = require("commander");
11460
12226
  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
12227
  const globalOpts = command.optsWithGlobals();
@@ -11487,7 +12253,6 @@ var searchSubcommand = new import_commander26.Command("search").description("Sea
11487
12253
  });
11488
12254
 
11489
12255
  // src/cli/commands/loadout/add.ts
11490
- init_cjs_shims();
11491
12256
  var import_commander27 = require("commander");
11492
12257
  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
12258
  const globalOpts = command.optsWithGlobals();
@@ -11507,7 +12272,6 @@ var addSubcommand = new import_commander27.Command("add").description("Add skill
11507
12272
  });
11508
12273
 
11509
12274
  // src/cli/commands/loadout/remove.ts
11510
- init_cjs_shims();
11511
12275
  var import_commander28 = require("commander");
11512
12276
  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
12277
  const globalOpts = command.optsWithGlobals();
@@ -11527,7 +12291,6 @@ var removeSubcommand = new import_commander28.Command("remove").description("Rem
11527
12291
  });
11528
12292
 
11529
12293
  // src/cli/commands/loadout/profile.ts
11530
- init_cjs_shims();
11531
12294
  var import_commander29 = require("commander");
11532
12295
  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
12296
  const globalOpts = command.optsWithGlobals();
@@ -11560,7 +12323,6 @@ var profileSubcommand = new import_commander29.Command("profile").description("S
11560
12323
  });
11561
12324
 
11562
12325
  // src/cli/commands/loadout/set.ts
11563
- init_cjs_shims();
11564
12326
  var import_commander30 = require("commander");
11565
12327
  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
12328
  const globalOpts = command.optsWithGlobals();
@@ -11591,7 +12353,6 @@ var setSubcommand = new import_commander30.Command("set").description("Set loado
11591
12353
  });
11592
12354
 
11593
12355
  // src/cli/commands/loadout/expand.ts
11594
- init_cjs_shims();
11595
12356
  var import_commander31 = require("commander");
11596
12357
  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
12358
  const globalOpts = command.optsWithGlobals();
@@ -11618,7 +12379,6 @@ var expandSubcommand = new import_commander31.Command("expand").description("Exp
11618
12379
  });
11619
12380
 
11620
12381
  // src/cli/commands/loadout/collapse.ts
11621
- init_cjs_shims();
11622
12382
  var import_commander32 = require("commander");
11623
12383
  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
12384
  const globalOpts = command.optsWithGlobals();
@@ -11642,7 +12402,6 @@ var collapseSubcommand = new import_commander32.Command("collapse").description(
11642
12402
  });
11643
12403
 
11644
12404
  // src/cli/commands/loadout/get.ts
11645
- init_cjs_shims();
11646
12405
  var import_commander33 = require("commander");
11647
12406
  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
12407
  const globalOpts = command.optsWithGlobals();
@@ -11666,7 +12425,6 @@ var getSubcommand = new import_commander33.Command("get").description("Get detai
11666
12425
  });
11667
12426
 
11668
12427
  // src/cli/commands/loadout/render.ts
11669
- init_cjs_shims();
11670
12428
  var import_commander34 = require("commander");
11671
12429
  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
12430
  const globalOpts = command.optsWithGlobals();
@@ -11686,7 +12444,6 @@ var renderSubcommand = new import_commander34.Command("render").description("Ren
11686
12444
  });
11687
12445
 
11688
12446
  // src/cli/commands/loadout/clear.ts
11689
- init_cjs_shims();
11690
12447
  var import_commander35 = require("commander");
11691
12448
  var clearSubcommand = new import_commander35.Command("clear").description("Clear the current loadout state").action(async (_options, command) => {
11692
12449
  const globalOpts = command.optsWithGlobals();
@@ -11709,16 +12466,15 @@ var clearSubcommand = new import_commander35.Command("clear").description("Clear
11709
12466
  });
11710
12467
 
11711
12468
  // src/cli/commands/loadout/browse.ts
11712
- init_cjs_shims();
11713
12469
  var import_commander36 = require("commander");
11714
12470
  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
12471
  const globalOpts = command.optsWithGlobals();
11716
12472
  try {
11717
12473
  const { server } = await createLoadoutServer(globalOpts);
11718
- const path18 = pathArg ? pathArg.split("/").filter(Boolean) : void 0;
11719
- const output = await server.agentBrowseCatalog(path18);
12474
+ const path19 = pathArg ? pathArg.split("/").filter(Boolean) : void 0;
12475
+ const output = await server.agentBrowseCatalog(path19);
11720
12476
  if (globalOpts.json) {
11721
- console.log(JSON.stringify({ path: path18 ?? [], output }, null, 2));
12477
+ console.log(JSON.stringify({ path: path19 ?? [], output }, null, 2));
11722
12478
  return;
11723
12479
  }
11724
12480
  if (!output) {
@@ -11735,8 +12491,95 @@ var browseSubcommand = new import_commander36.Command("browse").description("Bro
11735
12491
  // src/cli/commands/loadout/index.ts
11736
12492
  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
12493
 
12494
+ // src/cli/commands/skillnet.ts
12495
+ var import_commander38 = require("commander");
12496
+ function buildClient() {
12497
+ return createSkillNetClient({
12498
+ apiBaseUrl: process.env.SKILLNET_API_URL,
12499
+ githubToken: process.env.GITHUB_TOKEN,
12500
+ githubMirror: process.env.GITHUB_MIRROR
12501
+ });
12502
+ }
12503
+ 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) => {
12504
+ const globalOpts = command.optsWithGlobals();
12505
+ try {
12506
+ const client = buildClient();
12507
+ const searchOpts = {
12508
+ mode: options.mode,
12509
+ category: options.category,
12510
+ limit: options.limit,
12511
+ minStars: options.minStars,
12512
+ sortBy: options.sortBy,
12513
+ threshold: options.threshold
12514
+ };
12515
+ const results = await client.search(query, searchOpts);
12516
+ if (globalOpts.json) {
12517
+ console.log(JSON.stringify(results, null, 2));
12518
+ return;
12519
+ }
12520
+ if (results.length === 0) {
12521
+ printInfo("No skills found.");
12522
+ return;
12523
+ }
12524
+ printInfo(`Found ${results.length} skill(s):
12525
+ `);
12526
+ for (const r of results) {
12527
+ console.log(` ${r.skillName} \u2B50${r.stars}${r.category ? ` [${r.category}]` : ""}`);
12528
+ if (r.skillDescription) console.log(` ${r.skillDescription}`);
12529
+ console.log(` ${r.skillUrl}`);
12530
+ }
12531
+ } catch (err) {
12532
+ printError(err.message);
12533
+ process.exit(1);
12534
+ }
12535
+ });
12536
+ 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) => {
12537
+ const globalOpts = command.optsWithGlobals();
12538
+ if (!query && !options.url) {
12539
+ printError("Provide a search query or --url to import a single skill");
12540
+ process.exit(1);
12541
+ }
12542
+ try {
12543
+ const client = buildClient();
12544
+ const bank = await createSkillBankFromOptions(globalOpts);
12545
+ if (options.url) {
12546
+ const converted = await client.importSkill(options.url, bank);
12547
+ if (globalOpts.json) {
12548
+ console.log(JSON.stringify({ imported: 1, skill: converted.skill.id }, null, 2));
12549
+ } else {
12550
+ printSuccess(`Imported "${converted.skill.id}"`);
12551
+ for (const w of converted.warnings) printWarning(w);
12552
+ }
12553
+ return;
12554
+ }
12555
+ if (!globalOpts.quiet) {
12556
+ printInfo(`Searching SkillNet for "${query}"...`);
12557
+ }
12558
+ const result = await client.importFromSearch(query, bank, {
12559
+ mode: options.mode,
12560
+ category: options.category,
12561
+ limit: options.limit,
12562
+ minStars: options.minStars,
12563
+ threshold: options.threshold
12564
+ });
12565
+ if (globalOpts.json) {
12566
+ console.log(JSON.stringify(result, null, 2));
12567
+ return;
12568
+ }
12569
+ printSuccess(`Imported ${result.imported} skill(s)`);
12570
+ if (result.failed > 0) {
12571
+ printWarning(`Failed to import ${result.failed} skill(s)`);
12572
+ for (const e of result.errors) printInfo(` - ${e}`);
12573
+ }
12574
+ } catch (err) {
12575
+ printError(err.message);
12576
+ process.exit(1);
12577
+ }
12578
+ });
12579
+ var skillnetCommand = new import_commander38.Command("skillnet").description("Search and import skills from the SkillNet ecosystem").addCommand(searchSubcommand2).addCommand(importSubcommand);
12580
+
11738
12581
  // src/cli/index.ts
11739
- var program = new import_commander38.Command();
12582
+ var program = new import_commander39.Command();
11740
12583
  var config = loadConfig();
11741
12584
  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
12585
  program.addCommand(listCommand);
@@ -11758,4 +12601,5 @@ program.addCommand(syncCommand2);
11758
12601
  program.addCommand(readCommand);
11759
12602
  program.addCommand(materializeCommand);
11760
12603
  program.addCommand(loadoutCommand);
12604
+ program.addCommand(skillnetCommand);
11761
12605
  program.parse();