@cleocode/caamp 1.9.1 → 2026.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@ import {
2
2
  getAllProviders,
3
3
  groupByInstructFile,
4
4
  injectAll
5
- } from "./chunk-O7IVK5JY.js";
5
+ } from "./chunk-5UUABVWS.js";
6
6
  import {
7
7
  __export,
8
8
  getAgentsConfigPath,
@@ -15,7 +15,33 @@ import {
15
15
  resolveProviderConfigPath,
16
16
  resolveProviderProjectPath,
17
17
  resolveProviderSkillsDirs
18
- } from "./chunk-TI6WOJDG.js";
18
+ } from "./chunk-3IEKCREL.js";
19
+
20
+ // src/core/logger.ts
21
+ var verboseMode = false;
22
+ var quietMode = false;
23
+ var humanMode = false;
24
+ function setVerbose(v) {
25
+ verboseMode = v;
26
+ }
27
+ function setQuiet(q) {
28
+ quietMode = q;
29
+ }
30
+ function debug(...args) {
31
+ if (verboseMode) console.error("[debug]", ...args);
32
+ }
33
+ function isVerbose() {
34
+ return verboseMode;
35
+ }
36
+ function isQuiet() {
37
+ return quietMode;
38
+ }
39
+ function setHuman(h) {
40
+ humanMode = h;
41
+ }
42
+ function isHuman() {
43
+ return humanMode;
44
+ }
19
45
 
20
46
  // src/core/formats/utils.ts
21
47
  function deepMerge(target, source) {
@@ -49,35 +75,9 @@ async function ensureDir(filePath) {
49
75
  await mkdir4(dirname3(filePath), { recursive: true });
50
76
  }
51
77
 
52
- // src/core/logger.ts
53
- var verboseMode = false;
54
- var quietMode = false;
55
- var humanMode = false;
56
- function setVerbose(v) {
57
- verboseMode = v;
58
- }
59
- function setQuiet(q) {
60
- quietMode = q;
61
- }
62
- function debug(...args) {
63
- if (verboseMode) console.error("[debug]", ...args);
64
- }
65
- function isVerbose() {
66
- return verboseMode;
67
- }
68
- function isQuiet() {
69
- return quietMode;
70
- }
71
- function setHuman(h) {
72
- humanMode = h;
73
- }
74
- function isHuman() {
75
- return humanMode;
76
- }
77
-
78
78
  // src/core/formats/json.ts
79
- import { readFile, writeFile } from "fs/promises";
80
79
  import { existsSync } from "fs";
80
+ import { readFile, writeFile } from "fs/promises";
81
81
  import * as jsonc from "jsonc-parser";
82
82
  async function readJsonConfig(filePath) {
83
83
  if (!existsSync(filePath)) return {};
@@ -154,37 +154,32 @@ async function removeJsonConfig(filePath, configKey, serverName) {
154
154
  return true;
155
155
  }
156
156
 
157
- // src/core/formats/yaml.ts
157
+ // src/core/formats/toml.ts
158
158
  import { existsSync as existsSync2 } from "fs";
159
159
  import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
160
- import yaml from "js-yaml";
161
- async function readYamlConfig(filePath) {
160
+ import TOML from "@iarna/toml";
161
+ async function readTomlConfig(filePath) {
162
162
  if (!existsSync2(filePath)) return {};
163
163
  const content = await readFile2(filePath, "utf-8");
164
164
  if (!content.trim()) return {};
165
- const result = yaml.load(content);
166
- return result ?? {};
165
+ const result = TOML.parse(content);
166
+ return result;
167
167
  }
168
- async function writeYamlConfig(filePath, configKey, serverName, serverConfig) {
168
+ async function writeTomlConfig(filePath, configKey, serverName, serverConfig) {
169
169
  await ensureDir(filePath);
170
- const existing = await readYamlConfig(filePath);
170
+ const existing = await readTomlConfig(filePath);
171
171
  const keyParts = configKey.split(".");
172
172
  let newEntry = { [serverName]: serverConfig };
173
173
  for (const part of [...keyParts].reverse()) {
174
174
  newEntry = { [part]: newEntry };
175
175
  }
176
176
  const merged = deepMerge(existing, newEntry);
177
- const content = yaml.dump(merged, {
178
- indent: 2,
179
- lineWidth: -1,
180
- noRefs: true,
181
- sortKeys: false
182
- });
177
+ const content = TOML.stringify(merged);
183
178
  await writeFile2(filePath, content, "utf-8");
184
179
  }
185
- async function removeYamlConfig(filePath, configKey, serverName) {
180
+ async function removeTomlConfig(filePath, configKey, serverName) {
186
181
  if (!existsSync2(filePath)) return false;
187
- const existing = await readYamlConfig(filePath);
182
+ const existing = await readTomlConfig(filePath);
188
183
  const keyParts = configKey.split(".");
189
184
  let current = existing;
190
185
  for (const part of keyParts) {
@@ -194,42 +189,42 @@ async function removeYamlConfig(filePath, configKey, serverName) {
194
189
  }
195
190
  if (!(serverName in current)) return false;
196
191
  delete current[serverName];
197
- const content = yaml.dump(existing, {
198
- indent: 2,
199
- lineWidth: -1,
200
- noRefs: true,
201
- sortKeys: false
202
- });
192
+ const content = TOML.stringify(existing);
203
193
  await writeFile2(filePath, content, "utf-8");
204
194
  return true;
205
195
  }
206
196
 
207
- // src/core/formats/toml.ts
197
+ // src/core/formats/yaml.ts
208
198
  import { existsSync as existsSync3 } from "fs";
209
199
  import { readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
210
- import TOML from "@iarna/toml";
211
- async function readTomlConfig(filePath) {
200
+ import yaml from "js-yaml";
201
+ async function readYamlConfig(filePath) {
212
202
  if (!existsSync3(filePath)) return {};
213
203
  const content = await readFile3(filePath, "utf-8");
214
204
  if (!content.trim()) return {};
215
- const result = TOML.parse(content);
216
- return result;
205
+ const result = yaml.load(content);
206
+ return result ?? {};
217
207
  }
218
- async function writeTomlConfig(filePath, configKey, serverName, serverConfig) {
208
+ async function writeYamlConfig(filePath, configKey, serverName, serverConfig) {
219
209
  await ensureDir(filePath);
220
- const existing = await readTomlConfig(filePath);
210
+ const existing = await readYamlConfig(filePath);
221
211
  const keyParts = configKey.split(".");
222
212
  let newEntry = { [serverName]: serverConfig };
223
213
  for (const part of [...keyParts].reverse()) {
224
214
  newEntry = { [part]: newEntry };
225
215
  }
226
216
  const merged = deepMerge(existing, newEntry);
227
- const content = TOML.stringify(merged);
217
+ const content = yaml.dump(merged, {
218
+ indent: 2,
219
+ lineWidth: -1,
220
+ noRefs: true,
221
+ sortKeys: false
222
+ });
228
223
  await writeFile3(filePath, content, "utf-8");
229
224
  }
230
- async function removeTomlConfig(filePath, configKey, serverName) {
225
+ async function removeYamlConfig(filePath, configKey, serverName) {
231
226
  if (!existsSync3(filePath)) return false;
232
- const existing = await readTomlConfig(filePath);
227
+ const existing = await readYamlConfig(filePath);
233
228
  const keyParts = configKey.split(".");
234
229
  let current = existing;
235
230
  for (const part of keyParts) {
@@ -239,7 +234,12 @@ async function removeTomlConfig(filePath, configKey, serverName) {
239
234
  }
240
235
  if (!(serverName in current)) return false;
241
236
  delete current[serverName];
242
- const content = TOML.stringify(existing);
237
+ const content = yaml.dump(existing, {
238
+ indent: 2,
239
+ lineWidth: -1,
240
+ noRefs: true,
241
+ sortKeys: false
242
+ });
243
243
  await writeFile3(filePath, content, "utf-8");
244
244
  return true;
245
245
  }
@@ -802,15 +802,7 @@ async function listCanonicalSkills() {
802
802
 
803
803
  // src/core/advanced/orchestration.ts
804
804
  import { existsSync as existsSync7, lstatSync as lstatSync2 } from "fs";
805
- import {
806
- cp as cp2,
807
- mkdir as mkdir2,
808
- readFile as readFile4,
809
- readlink,
810
- rm as rm2,
811
- symlink as symlink2,
812
- writeFile as writeFile4
813
- } from "fs/promises";
805
+ import { cp as cp2, mkdir as mkdir2, readFile as readFile4, readlink, rm as rm2, symlink as symlink2, writeFile as writeFile4 } from "fs/promises";
814
806
  import { tmpdir } from "os";
815
807
  import { basename, dirname, join as join3 } from "path";
816
808
 
@@ -983,7 +975,9 @@ async function installBatchWithRollback(options) {
983
975
  isGlobal,
984
976
  projectDir
985
977
  );
986
- const linkedProviders = providers.filter((provider) => result.linkedAgents.includes(provider.id));
978
+ const linkedProviders = providers.filter(
979
+ (provider) => result.linkedAgents.includes(provider.id)
980
+ );
987
981
  appliedSkills.push({
988
982
  skillName: operation.skillName,
989
983
  isGlobal,
@@ -1093,7 +1087,10 @@ async function applyMcpInstallWithPolicy(providers, operations, policy = "fail",
1093
1087
  const conflictKey = (providerId, serverName, scope) => `${providerId}::${serverName}::${scope}`;
1094
1088
  const conflictMap = /* @__PURE__ */ new Map();
1095
1089
  for (const conflict of conflicts) {
1096
- conflictMap.set(conflictKey(conflict.providerId, conflict.serverName, conflict.scope), conflict);
1090
+ conflictMap.set(
1091
+ conflictKey(conflict.providerId, conflict.serverName, conflict.scope),
1092
+ conflict
1093
+ );
1097
1094
  }
1098
1095
  if (policy === "fail" && conflicts.length > 0) {
1099
1096
  return { conflicts, applied: [], skipped: [] };
@@ -1156,35 +1153,59 @@ async function configureProviderGlobalAndProject(provider, options) {
1156
1153
  const projectOps = options.projectMcp ?? [];
1157
1154
  const globalResults = [];
1158
1155
  for (const operation of globalOps) {
1159
- globalResults.push(await installMcpServer(
1160
- provider,
1161
- operation.serverName,
1162
- operation.config,
1163
- "global",
1164
- projectDir
1165
- ));
1156
+ globalResults.push(
1157
+ await installMcpServer(
1158
+ provider,
1159
+ operation.serverName,
1160
+ operation.config,
1161
+ "global",
1162
+ projectDir
1163
+ )
1164
+ );
1166
1165
  }
1167
1166
  const projectResults = [];
1168
1167
  for (const operation of projectOps) {
1169
- projectResults.push(await installMcpServer(
1170
- provider,
1171
- operation.serverName,
1172
- operation.config,
1173
- "project",
1174
- projectDir
1175
- ));
1168
+ projectResults.push(
1169
+ await installMcpServer(
1170
+ provider,
1171
+ operation.serverName,
1172
+ operation.config,
1173
+ "project",
1174
+ projectDir
1175
+ )
1176
+ );
1176
1177
  }
1177
1178
  const instructionResults = {};
1178
1179
  const instructionContent = options.instructionContent;
1179
1180
  if (typeof instructionContent === "string") {
1180
- instructionResults.global = await injectAll([provider], projectDir, "global", instructionContent);
1181
- instructionResults.project = await injectAll([provider], projectDir, "project", instructionContent);
1181
+ instructionResults.global = await injectAll(
1182
+ [provider],
1183
+ projectDir,
1184
+ "global",
1185
+ instructionContent
1186
+ );
1187
+ instructionResults.project = await injectAll(
1188
+ [provider],
1189
+ projectDir,
1190
+ "project",
1191
+ instructionContent
1192
+ );
1182
1193
  } else if (instructionContent) {
1183
1194
  if (instructionContent.global) {
1184
- instructionResults.global = await injectAll([provider], projectDir, "global", instructionContent.global);
1195
+ instructionResults.global = await injectAll(
1196
+ [provider],
1197
+ projectDir,
1198
+ "global",
1199
+ instructionContent.global
1200
+ );
1185
1201
  }
1186
1202
  if (instructionContent.project) {
1187
- instructionResults.project = await injectAll([provider], projectDir, "project", instructionContent.project);
1203
+ instructionResults.project = await injectAll(
1204
+ [provider],
1205
+ projectDir,
1206
+ "project",
1207
+ instructionContent.project
1208
+ );
1188
1209
  }
1189
1210
  }
1190
1211
  return {
@@ -1205,7 +1226,7 @@ async function configureProviderGlobalAndProject(provider, options) {
1205
1226
  import { execFileSync as execFileSync2 } from "child_process";
1206
1227
  import { existsSync as existsSync8 } from "fs";
1207
1228
  import { homedir } from "os";
1208
- import { isAbsolute, resolve as resolve2 } from "path";
1229
+ import { isAbsolute, resolve } from "path";
1209
1230
  var CLEO_SERVER_NAMES = {
1210
1231
  stable: "cleo",
1211
1232
  beta: "cleo-beta",
@@ -1288,7 +1309,7 @@ function buildCleoProfile(options) {
1288
1309
  function expandHome(pathValue) {
1289
1310
  if (pathValue === "~") return homedir();
1290
1311
  if (pathValue.startsWith("~/")) {
1291
- return resolve2(homedir(), pathValue.slice(2));
1312
+ return resolve(homedir(), pathValue.slice(2));
1292
1313
  }
1293
1314
  return pathValue;
1294
1315
  }
@@ -1296,7 +1317,7 @@ function checkCommandReachability(command) {
1296
1317
  const hasPathSeparator = command.includes("/") || command.includes("\\");
1297
1318
  if (hasPathSeparator || command.startsWith("~")) {
1298
1319
  const expanded = expandHome(command);
1299
- const candidate = isAbsolute(expanded) ? expanded : resolve2(process.cwd(), expanded);
1320
+ const candidate = isAbsolute(expanded) ? expanded : resolve(process.cwd(), expanded);
1300
1321
  if (existsSync8(candidate)) {
1301
1322
  return { reachable: true, method: "path", detail: candidate };
1302
1323
  }
@@ -1337,12 +1358,12 @@ function isCleoSource(source) {
1337
1358
  }
1338
1359
 
1339
1360
  // src/core/lock-utils.ts
1340
- import { open, readFile as readFile5, writeFile as writeFile5, mkdir as mkdir3, rm as rm3, rename, stat } from "fs/promises";
1341
1361
  import { existsSync as existsSync9 } from "fs";
1362
+ import { mkdir as mkdir3, open, readFile as readFile5, rename, rm as rm3, stat, writeFile as writeFile5 } from "fs/promises";
1342
1363
  var LOCK_GUARD_PATH = `${LOCK_FILE_PATH}.lock`;
1343
1364
  var STALE_LOCK_MS = 5e3;
1344
1365
  function sleep(ms) {
1345
- return new Promise((resolve3) => setTimeout(resolve3, ms));
1366
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
1346
1367
  }
1347
1368
  async function removeStaleLock() {
1348
1369
  try {
@@ -1452,9 +1473,7 @@ async function getLastSelectedAgents() {
1452
1473
  function inferCleoLockData(config, channel) {
1453
1474
  const command = typeof config.command === "string" ? config.command : "";
1454
1475
  const args = Array.isArray(config.args) ? config.args.filter((a) => typeof a === "string") : [];
1455
- const packageArg = args.find(
1456
- (a) => a.includes(CLEO_MCP_NPM_PACKAGE)
1457
- );
1476
+ const packageArg = args.find((a) => a.includes(CLEO_MCP_NPM_PACKAGE));
1458
1477
  if (packageArg) {
1459
1478
  const version = extractVersionTag(packageArg);
1460
1479
  return {
@@ -1639,7 +1658,9 @@ function inferName(source, type) {
1639
1658
  }
1640
1659
  if (type === "command") {
1641
1660
  const parts = source.split(/\s+/);
1642
- const command = parts.find((p) => !p.startsWith("-") && p !== "npx" && p !== "node" && p !== "python" && p !== "python3");
1661
+ const command = parts.find(
1662
+ (p) => !p.startsWith("-") && p !== "npx" && p !== "node" && p !== "python" && p !== "python3"
1663
+ );
1643
1664
  return command ?? parts[0] ?? source;
1644
1665
  }
1645
1666
  return source;
@@ -1751,1169 +1772,13 @@ function isMarketplaceScoped(input) {
1751
1772
  return /^@[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/.test(input);
1752
1773
  }
1753
1774
 
1754
- // src/core/network/fetch.ts
1755
- var DEFAULT_FETCH_TIMEOUT_MS = 1e4;
1756
- var NetworkError = class extends Error {
1757
- /** Classification of the failure. */
1758
- kind;
1759
- /** URL that was being fetched. */
1760
- url;
1761
- /** HTTP status code (only present for `"http"` kind). */
1762
- status;
1763
- constructor(message, kind, url, status) {
1764
- super(message);
1765
- this.name = "NetworkError";
1766
- this.kind = kind;
1767
- this.url = url;
1768
- this.status = status;
1769
- }
1770
- };
1771
- function isAbortError(error) {
1772
- return error instanceof Error && error.name === "AbortError";
1773
- }
1774
- async function fetchWithTimeout(url, init, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
1775
- try {
1776
- return await fetch(url, {
1777
- ...init,
1778
- signal: AbortSignal.timeout(timeoutMs)
1779
- });
1780
- } catch (error) {
1781
- if (isAbortError(error)) {
1782
- throw new NetworkError(`Request timed out after ${timeoutMs}ms`, "timeout", url);
1783
- }
1784
- throw new NetworkError("Network request failed", "network", url);
1785
- }
1786
- }
1787
- function ensureOkResponse(response, url) {
1788
- if (!response.ok) {
1789
- throw new NetworkError(`Request failed with status ${response.status}`, "http", url, response.status);
1790
- }
1791
- return response;
1792
- }
1793
- function formatNetworkError(error) {
1794
- if (error instanceof NetworkError) {
1795
- if (error.kind === "timeout") {
1796
- return "Network request timed out. Please check your connection and try again.";
1797
- }
1798
- if (error.kind === "http") {
1799
- return `Marketplace request failed with HTTP ${error.status ?? "unknown"}. Please try again shortly.`;
1800
- }
1801
- return "Network request failed. Please check your connection and try again.";
1802
- }
1803
- if (error instanceof Error) return error.message;
1804
- return String(error);
1805
- }
1775
+ // src/core/skills/audit/scanner.ts
1776
+ import { existsSync as existsSync10 } from "fs";
1777
+ import { readFile as readFile6 } from "fs/promises";
1806
1778
 
1807
- // src/core/marketplace/skillsmp.ts
1808
- var API_BASE = "https://www.agentskills.in/api/skills";
1809
- function parseScopedName(value) {
1810
- const match = value.match(/^@([^/]+)\/([^/]+)$/);
1811
- if (!match) return null;
1812
- return {
1813
- author: match[1],
1814
- name: match[2]
1815
- };
1816
- }
1817
- function toResult(skill) {
1818
- return {
1819
- name: skill.name,
1820
- scopedName: skill.scopedName,
1821
- description: skill.description,
1822
- author: skill.author,
1823
- stars: skill.stars,
1824
- githubUrl: skill.githubUrl,
1825
- repoFullName: skill.repoFullName,
1826
- path: skill.path,
1827
- source: "agentskills.in"
1828
- };
1829
- }
1830
- var SkillsMPAdapter = class {
1831
- /** The marketplace identifier used in search results. */
1832
- name = "agentskills.in";
1833
- /**
1834
- * Search for skills by query string.
1835
- *
1836
- * @param query - Search query to match against skill names and descriptions.
1837
- * @param limit - Maximum number of results to return.
1838
- * @returns Array of marketplace results sorted by stars.
1839
- */
1840
- async search(query, limit = 20) {
1841
- const params = new URLSearchParams({
1842
- search: query,
1843
- limit: String(limit),
1844
- sortBy: "stars"
1845
- });
1846
- const url = `${API_BASE}?${params}`;
1847
- const response = ensureOkResponse(await fetchWithTimeout(url), url);
1848
- const data = await response.json();
1849
- return data.skills.map(toResult);
1850
- }
1851
- /**
1852
- * Look up a specific skill by its scoped name.
1853
- *
1854
- * @param scopedName - The scoped skill name (e.g. `"@author/skill-name"`).
1855
- * @returns The matching marketplace result, or `null` if not found.
1856
- */
1857
- async getSkill(scopedName) {
1858
- const parts = parseScopedName(scopedName);
1859
- const searchTerms = parts ? [parts.name, `${parts.author} ${parts.name}`, scopedName] : [scopedName];
1860
- const seen = /* @__PURE__ */ new Set();
1861
- for (const term of searchTerms) {
1862
- if (seen.has(term)) continue;
1863
- seen.add(term);
1864
- const params = new URLSearchParams({
1865
- search: term,
1866
- limit: "50",
1867
- sortBy: "stars"
1868
- });
1869
- const url = `${API_BASE}?${params}`;
1870
- const response = ensureOkResponse(await fetchWithTimeout(url), url);
1871
- const data = await response.json();
1872
- const match = data.skills.find(
1873
- (s) => s.scopedName === scopedName || `@${s.author}/${s.name}` === scopedName
1874
- );
1875
- if (match) {
1876
- return toResult(match);
1877
- }
1878
- }
1879
- return null;
1880
- }
1881
- };
1882
-
1883
- // src/core/marketplace/skillssh.ts
1884
- var API_BASE2 = "https://skills.sh/api";
1885
- function toResult2(skill) {
1886
- return {
1887
- name: skill.name,
1888
- scopedName: `@${skill.author}/${skill.name}`,
1889
- description: skill.description,
1890
- author: skill.author,
1891
- stars: skill.stars ?? 0,
1892
- githubUrl: skill.url,
1893
- repoFullName: skill.repo,
1894
- path: "",
1895
- source: "skills.sh"
1896
- };
1897
- }
1898
- var SkillsShAdapter = class {
1899
- /** The marketplace identifier used in search results. */
1900
- name = "skills.sh";
1901
- /**
1902
- * Search for skills by query string.
1903
- *
1904
- * @param query - Search query to match against skill names.
1905
- * @param limit - Maximum number of results to return.
1906
- * @returns Array of marketplace results.
1907
- */
1908
- async search(query, limit = 20) {
1909
- const params = new URLSearchParams({
1910
- q: query,
1911
- limit: String(limit)
1912
- });
1913
- const url = `${API_BASE2}/search?${params}`;
1914
- const response = ensureOkResponse(await fetchWithTimeout(url), url);
1915
- const data = await response.json();
1916
- return data.results.map(toResult2);
1917
- }
1918
- /**
1919
- * Look up a specific skill by its scoped name.
1920
- *
1921
- * @param scopedName - The scoped skill name (e.g. `"@author/skill-name"`).
1922
- * @returns The matching marketplace result, or `null` if not found.
1923
- */
1924
- async getSkill(scopedName) {
1925
- const results = await this.search(scopedName, 5);
1926
- return results.find((r) => r.scopedName === scopedName) ?? null;
1927
- }
1928
- };
1929
-
1930
- // src/core/marketplace/client.ts
1931
- var MarketplaceUnavailableError = class extends Error {
1932
- /** Per-adapter failure messages. */
1933
- details;
1934
- constructor(message, details) {
1935
- super(message);
1936
- this.name = "MarketplaceUnavailableError";
1937
- this.details = details;
1938
- }
1939
- };
1940
- var MarketplaceClient = class {
1941
- /** Configured marketplace adapters. */
1942
- adapters;
1943
- /**
1944
- * Create a new marketplace client.
1945
- *
1946
- * @param adapters - Custom marketplace adapters (defaults to agentskills.in and skills.sh)
1947
- *
1948
- * @example
1949
- * ```typescript
1950
- * // Use default adapters
1951
- * const client = new MarketplaceClient();
1952
- *
1953
- * // Use custom adapters
1954
- * const client = new MarketplaceClient([myAdapter]);
1955
- * ```
1956
- */
1957
- constructor(adapters) {
1958
- this.adapters = adapters ?? [
1959
- new SkillsMPAdapter(),
1960
- new SkillsShAdapter()
1961
- ];
1962
- }
1963
- /**
1964
- * Search all marketplaces and return deduplicated, sorted results.
1965
- *
1966
- * Queries all adapters in parallel and deduplicates by `scopedName`,
1967
- * keeping the entry with the highest star count. Results are sorted by
1968
- * stars descending.
1969
- *
1970
- * @param query - Search query string
1971
- * @param limit - Maximum number of results to return (default: 20)
1972
- * @returns Deduplicated and sorted marketplace results
1973
- *
1974
- * @example
1975
- * ```typescript
1976
- * const results = await client.search("code review", 10);
1977
- * ```
1978
- */
1979
- async search(query, limit = 20) {
1980
- const settled = await Promise.allSettled(this.adapters.map((adapter) => adapter.search(query, limit)));
1981
- const flat = [];
1982
- const failures = [];
1983
- for (const [index, result] of settled.entries()) {
1984
- const adapterName = this.adapters[index]?.name ?? "unknown";
1985
- if (result.status === "fulfilled") {
1986
- flat.push(...result.value);
1987
- } else {
1988
- const reason = result.reason instanceof Error ? result.reason.message : String(result.reason);
1989
- failures.push(`${adapterName}: ${reason}`);
1990
- }
1991
- }
1992
- if (flat.length === 0 && failures.length > 0) {
1993
- throw new MarketplaceUnavailableError("All marketplace sources failed.", failures);
1994
- }
1995
- const seen = /* @__PURE__ */ new Map();
1996
- for (const result of flat) {
1997
- const existing = seen.get(result.scopedName);
1998
- if (!existing || result.stars > existing.stars) {
1999
- seen.set(result.scopedName, result);
2000
- }
2001
- }
2002
- const deduplicated = Array.from(seen.values());
2003
- deduplicated.sort((a, b) => b.stars - a.stars);
2004
- return deduplicated.slice(0, limit);
2005
- }
2006
- /**
2007
- * Get a specific skill by its scoped name from any marketplace.
2008
- *
2009
- * Tries each adapter in order and returns the first match.
2010
- *
2011
- * @param scopedName - Scoped skill name (e.g. `"@author/my-skill"`)
2012
- * @returns The marketplace result, or `null` if not found in any marketplace
2013
- *
2014
- * @example
2015
- * ```typescript
2016
- * const skill = await client.getSkill("@anthropic/memory");
2017
- * ```
2018
- */
2019
- async getSkill(scopedName) {
2020
- const failures = [];
2021
- for (const adapter of this.adapters) {
2022
- try {
2023
- const result = await adapter.getSkill(scopedName);
2024
- if (result) return result;
2025
- } catch (error) {
2026
- const reason = error instanceof Error ? error.message : String(error);
2027
- failures.push(`${adapter.name}: ${reason}`);
2028
- }
2029
- }
2030
- if (failures.length === this.adapters.length && this.adapters.length > 0) {
2031
- throw new MarketplaceUnavailableError("All marketplace sources failed.", failures);
2032
- }
2033
- return null;
2034
- }
2035
- };
2036
-
2037
- // src/core/skills/library-loader.ts
2038
- import { createRequire } from "module";
2039
- import { existsSync as existsSync10, readdirSync, readFileSync } from "fs";
2040
- import { basename as basename2, dirname as dirname2, join as join4 } from "path";
2041
- var require2 = createRequire(import.meta.url);
2042
- function loadLibraryFromModule(root) {
2043
- let mod;
2044
- try {
2045
- mod = require2(root);
2046
- } catch {
2047
- throw new Error(`Failed to load skill library module from ${root}`);
2048
- }
2049
- const requiredMethods = [
2050
- "listSkills",
2051
- "getSkill",
2052
- "getSkillPath",
2053
- "getSkillDir",
2054
- "readSkillContent",
2055
- "getCoreSkills",
2056
- "getSkillsByCategory",
2057
- "getSkillDependencies",
2058
- "resolveDependencyTree",
2059
- "listProfiles",
2060
- "getProfile",
2061
- "resolveProfile",
2062
- "listSharedResources",
2063
- "getSharedResourcePath",
2064
- "readSharedResource",
2065
- "listProtocols",
2066
- "getProtocolPath",
2067
- "readProtocol",
2068
- "validateSkillFrontmatter",
2069
- "validateAll",
2070
- "getDispatchMatrix"
2071
- ];
2072
- for (const method of requiredMethods) {
2073
- if (typeof mod[method] !== "function") {
2074
- throw new Error(
2075
- `Skill library at ${root} does not implement required method: ${method}`
2076
- );
2077
- }
2078
- }
2079
- if (!mod.version || typeof mod.version !== "string") {
2080
- throw new Error(`Skill library at ${root} is missing 'version' property`);
2081
- }
2082
- if (!mod.libraryRoot || typeof mod.libraryRoot !== "string") {
2083
- throw new Error(`Skill library at ${root} is missing 'libraryRoot' property`);
2084
- }
2085
- return mod;
2086
- }
2087
- function buildLibraryFromFiles(root) {
2088
- const catalogPath = join4(root, "skills.json");
2089
- if (!existsSync10(catalogPath)) {
2090
- throw new Error(`No skills.json found at ${root}`);
2091
- }
2092
- const catalogData = JSON.parse(readFileSync(catalogPath, "utf-8"));
2093
- const entries = catalogData.skills ?? [];
2094
- const version = catalogData.version ?? "0.0.0";
2095
- const manifestPath = join4(root, "skills", "manifest.json");
2096
- let manifest;
2097
- if (existsSync10(manifestPath)) {
2098
- manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
2099
- } else {
2100
- manifest = {
2101
- $schema: "",
2102
- _meta: {},
2103
- dispatch_matrix: { by_task_type: {}, by_keyword: {}, by_protocol: {} },
2104
- skills: []
2105
- };
2106
- }
2107
- const profilesDir = join4(root, "profiles");
2108
- const profiles = /* @__PURE__ */ new Map();
2109
- if (existsSync10(profilesDir)) {
2110
- for (const file of readdirSync(profilesDir)) {
2111
- if (!file.endsWith(".json")) continue;
2112
- try {
2113
- const profile = JSON.parse(
2114
- readFileSync(join4(profilesDir, file), "utf-8")
2115
- );
2116
- profiles.set(profile.name, profile);
2117
- } catch {
2118
- }
2119
- }
2120
- }
2121
- const skillMap = /* @__PURE__ */ new Map();
2122
- for (const entry of entries) {
2123
- skillMap.set(entry.name, entry);
2124
- }
2125
- function getSkillDir2(name) {
2126
- const entry = skillMap.get(name);
2127
- if (entry) {
2128
- return dirname2(join4(root, entry.path));
2129
- }
2130
- return join4(root, "skills", name);
2131
- }
2132
- function resolveDeps(names, visited = /* @__PURE__ */ new Set()) {
2133
- const result = [];
2134
- for (const name of names) {
2135
- if (visited.has(name)) continue;
2136
- visited.add(name);
2137
- const entry = skillMap.get(name);
2138
- if (entry && entry.dependencies.length > 0) {
2139
- result.push(...resolveDeps(entry.dependencies, visited));
2140
- }
2141
- result.push(name);
2142
- }
2143
- return result;
2144
- }
2145
- function resolveProfileByName(name, visited = /* @__PURE__ */ new Set()) {
2146
- if (visited.has(name)) return [];
2147
- visited.add(name);
2148
- const profile = profiles.get(name);
2149
- if (!profile) return [];
2150
- let skills = [];
2151
- if (profile.extends) {
2152
- skills = resolveProfileByName(profile.extends, visited);
2153
- }
2154
- skills.push(...profile.skills);
2155
- return resolveDeps([...new Set(skills)]);
2156
- }
2157
- function discoverFiles(dir, ext) {
2158
- if (!existsSync10(dir)) return [];
2159
- return readdirSync(dir).filter((f) => f.endsWith(ext)).map((f) => basename2(f, ext));
2160
- }
2161
- const library = {
2162
- version,
2163
- libraryRoot: root,
2164
- skills: entries,
2165
- manifest,
2166
- listSkills() {
2167
- return entries.map((e) => e.name);
2168
- },
2169
- getSkill(name) {
2170
- return skillMap.get(name);
2171
- },
2172
- getSkillPath(name) {
2173
- const entry = skillMap.get(name);
2174
- if (entry) {
2175
- return join4(root, entry.path);
2176
- }
2177
- return join4(root, "skills", name, "SKILL.md");
2178
- },
2179
- getSkillDir: getSkillDir2,
2180
- readSkillContent(name) {
2181
- const skillPath = library.getSkillPath(name);
2182
- if (!existsSync10(skillPath)) {
2183
- throw new Error(`Skill content not found: ${skillPath}`);
2184
- }
2185
- return readFileSync(skillPath, "utf-8");
2186
- },
2187
- getCoreSkills() {
2188
- return entries.filter((e) => e.core);
2189
- },
2190
- getSkillsByCategory(category) {
2191
- return entries.filter((e) => e.category === category);
2192
- },
2193
- getSkillDependencies(name) {
2194
- return skillMap.get(name)?.dependencies ?? [];
2195
- },
2196
- resolveDependencyTree(names) {
2197
- return resolveDeps(names);
2198
- },
2199
- listProfiles() {
2200
- return [...profiles.keys()];
2201
- },
2202
- getProfile(name) {
2203
- return profiles.get(name);
2204
- },
2205
- resolveProfile(name) {
2206
- return resolveProfileByName(name);
2207
- },
2208
- listSharedResources() {
2209
- return discoverFiles(join4(root, "skills", "_shared"), ".md");
2210
- },
2211
- getSharedResourcePath(name) {
2212
- const resourcePath = join4(root, "skills", "_shared", `${name}.md`);
2213
- return existsSync10(resourcePath) ? resourcePath : void 0;
2214
- },
2215
- readSharedResource(name) {
2216
- const resourcePath = library.getSharedResourcePath(name);
2217
- if (!resourcePath) return void 0;
2218
- return readFileSync(resourcePath, "utf-8");
2219
- },
2220
- listProtocols() {
2221
- const rootProtocols = discoverFiles(join4(root, "protocols"), ".md");
2222
- if (rootProtocols.length > 0) return rootProtocols;
2223
- return discoverFiles(join4(root, "skills", "protocols"), ".md");
2224
- },
2225
- getProtocolPath(name) {
2226
- const rootPath = join4(root, "protocols", `${name}.md`);
2227
- if (existsSync10(rootPath)) return rootPath;
2228
- const skillsPath = join4(root, "skills", "protocols", `${name}.md`);
2229
- return existsSync10(skillsPath) ? skillsPath : void 0;
2230
- },
2231
- readProtocol(name) {
2232
- const protocolPath = library.getProtocolPath(name);
2233
- if (!protocolPath) return void 0;
2234
- return readFileSync(protocolPath, "utf-8");
2235
- },
2236
- validateSkillFrontmatter(name) {
2237
- const entry = skillMap.get(name);
2238
- if (!entry) {
2239
- return {
2240
- valid: false,
2241
- issues: [{ level: "error", field: "name", message: `Skill not found: ${name}` }]
2242
- };
2243
- }
2244
- const issues = [];
2245
- if (!entry.name) {
2246
- issues.push({ level: "error", field: "name", message: "Missing name" });
2247
- }
2248
- if (!entry.description) {
2249
- issues.push({ level: "error", field: "description", message: "Missing description" });
2250
- }
2251
- if (!entry.version) {
2252
- issues.push({ level: "warn", field: "version", message: "Missing version" });
2253
- }
2254
- const skillPath = join4(root, entry.path);
2255
- if (!existsSync10(skillPath)) {
2256
- issues.push({ level: "error", field: "path", message: `SKILL.md not found at ${entry.path}` });
2257
- }
2258
- return {
2259
- valid: !issues.some((i) => i.level === "error"),
2260
- issues
2261
- };
2262
- },
2263
- validateAll() {
2264
- const results = /* @__PURE__ */ new Map();
2265
- for (const entry of entries) {
2266
- results.set(entry.name, library.validateSkillFrontmatter(entry.name));
2267
- }
2268
- return results;
2269
- },
2270
- getDispatchMatrix() {
2271
- return manifest.dispatch_matrix;
2272
- }
2273
- };
2274
- return library;
2275
- }
2276
-
2277
- // src/core/skills/catalog.ts
2278
- var catalog_exports = {};
2279
- __export(catalog_exports, {
2280
- clearRegisteredLibrary: () => clearRegisteredLibrary,
2281
- getCoreSkills: () => getCoreSkills,
2282
- getDispatchMatrix: () => getDispatchMatrix,
2283
- getLibraryRoot: () => getLibraryRoot,
2284
- getManifest: () => getManifest,
2285
- getProfile: () => getProfile,
2286
- getProtocolPath: () => getProtocolPath,
2287
- getSharedResourcePath: () => getSharedResourcePath,
2288
- getSkill: () => getSkill,
2289
- getSkillDependencies: () => getSkillDependencies,
2290
- getSkillDir: () => getSkillDir,
2291
- getSkillPath: () => getSkillPath,
2292
- getSkills: () => getSkills,
2293
- getSkillsByCategory: () => getSkillsByCategory,
2294
- getVersion: () => getVersion,
2295
- isCatalogAvailable: () => isCatalogAvailable,
2296
- listProfiles: () => listProfiles,
2297
- listProtocols: () => listProtocols,
2298
- listSharedResources: () => listSharedResources,
2299
- listSkills: () => listSkills,
2300
- readProtocol: () => readProtocol,
2301
- readSharedResource: () => readSharedResource,
2302
- readSkillContent: () => readSkillContent,
2303
- registerSkillLibrary: () => registerSkillLibrary,
2304
- registerSkillLibraryFromPath: () => registerSkillLibraryFromPath,
2305
- resolveDependencyTree: () => resolveDependencyTree,
2306
- resolveProfile: () => resolveProfile,
2307
- validateAll: () => validateAll,
2308
- validateSkillFrontmatter: () => validateSkillFrontmatter
2309
- });
2310
- import { existsSync as existsSync11 } from "fs";
2311
- import { join as join5 } from "path";
2312
- var _library = null;
2313
- function registerSkillLibrary(library) {
2314
- _library = library;
2315
- }
2316
- function registerSkillLibraryFromPath(root) {
2317
- const indexPath = join5(root, "index.js");
2318
- if (existsSync11(indexPath)) {
2319
- _library = loadLibraryFromModule(root);
2320
- return;
2321
- }
2322
- _library = buildLibraryFromFiles(root);
2323
- }
2324
- function clearRegisteredLibrary() {
2325
- _library = null;
2326
- }
2327
- function discoverLibrary() {
2328
- const envPath = process.env["CAAMP_SKILL_LIBRARY"];
2329
- if (envPath && existsSync11(envPath)) {
2330
- try {
2331
- const indexPath = join5(envPath, "index.js");
2332
- if (existsSync11(indexPath)) {
2333
- return loadLibraryFromModule(envPath);
2334
- }
2335
- if (existsSync11(join5(envPath, "skills.json"))) {
2336
- return buildLibraryFromFiles(envPath);
2337
- }
2338
- } catch {
2339
- }
2340
- }
2341
- return null;
2342
- }
2343
- function getLibrary() {
2344
- if (!_library) {
2345
- const discovered = discoverLibrary();
2346
- if (discovered) {
2347
- _library = discovered;
2348
- }
2349
- }
2350
- if (!_library) {
2351
- throw new Error(
2352
- "No skill library registered. Register one with registerSkillLibraryFromPath() or set the CAAMP_SKILL_LIBRARY environment variable."
2353
- );
2354
- }
2355
- return _library;
2356
- }
2357
- function isCatalogAvailable() {
2358
- try {
2359
- getLibrary();
2360
- return true;
2361
- } catch {
2362
- return false;
2363
- }
2364
- }
2365
- function getSkills() {
2366
- return getLibrary().skills;
2367
- }
2368
- function getManifest() {
2369
- return getLibrary().manifest;
2370
- }
2371
- function listSkills() {
2372
- return getLibrary().listSkills();
2373
- }
2374
- function getSkill(name) {
2375
- return getLibrary().getSkill(name);
2376
- }
2377
- function getSkillPath(name) {
2378
- return getLibrary().getSkillPath(name);
2379
- }
2380
- function getSkillDir(name) {
2381
- return getLibrary().getSkillDir(name);
2382
- }
2383
- function readSkillContent(name) {
2384
- return getLibrary().readSkillContent(name);
2385
- }
2386
- function getCoreSkills() {
2387
- return getLibrary().getCoreSkills();
2388
- }
2389
- function getSkillsByCategory(category) {
2390
- return getLibrary().getSkillsByCategory(category);
2391
- }
2392
- function getSkillDependencies(name) {
2393
- return getLibrary().getSkillDependencies(name);
2394
- }
2395
- function resolveDependencyTree(names) {
2396
- return getLibrary().resolveDependencyTree(names);
2397
- }
2398
- function listProfiles() {
2399
- return getLibrary().listProfiles();
2400
- }
2401
- function getProfile(name) {
2402
- return getLibrary().getProfile(name);
2403
- }
2404
- function resolveProfile(name) {
2405
- return getLibrary().resolveProfile(name);
2406
- }
2407
- function listSharedResources() {
2408
- return getLibrary().listSharedResources();
2409
- }
2410
- function getSharedResourcePath(name) {
2411
- return getLibrary().getSharedResourcePath(name);
2412
- }
2413
- function readSharedResource(name) {
2414
- return getLibrary().readSharedResource(name);
2415
- }
2416
- function listProtocols() {
2417
- return getLibrary().listProtocols();
2418
- }
2419
- function getProtocolPath(name) {
2420
- return getLibrary().getProtocolPath(name);
2421
- }
2422
- function readProtocol(name) {
2423
- return getLibrary().readProtocol(name);
2424
- }
2425
- function validateSkillFrontmatter(name) {
2426
- return getLibrary().validateSkillFrontmatter(name);
2427
- }
2428
- function validateAll() {
2429
- return getLibrary().validateAll();
2430
- }
2431
- function getDispatchMatrix() {
2432
- return getLibrary().getDispatchMatrix();
2433
- }
2434
- function getVersion() {
2435
- return getLibrary().version;
2436
- }
2437
- function getLibraryRoot() {
2438
- return getLibrary().libraryRoot;
2439
- }
2440
-
2441
- // src/core/skills/discovery.ts
2442
- import { readFile as readFile6, readdir } from "fs/promises";
2443
- import { existsSync as existsSync12 } from "fs";
2444
- import { join as join6 } from "path";
2445
- import matter from "gray-matter";
2446
- async function parseSkillFile(filePath) {
2447
- try {
2448
- const content = await readFile6(filePath, "utf-8");
2449
- const { data } = matter(content);
2450
- if (!data.name || !data.description) {
2451
- return null;
2452
- }
2453
- const allowedTools = data["allowed-tools"] ?? data.allowedTools;
2454
- return {
2455
- name: String(data.name),
2456
- description: String(data.description),
2457
- license: data.license ? String(data.license) : void 0,
2458
- compatibility: data.compatibility ? String(data.compatibility) : void 0,
2459
- metadata: data.metadata,
2460
- allowedTools: typeof allowedTools === "string" ? allowedTools.split(/\s+/) : Array.isArray(allowedTools) ? allowedTools.map(String) : void 0,
2461
- version: data.version ? String(data.version) : void 0
2462
- };
2463
- } catch {
2464
- return null;
2465
- }
2466
- }
2467
- async function discoverSkill(skillDir) {
2468
- const skillFile = join6(skillDir, "SKILL.md");
2469
- if (!existsSync12(skillFile)) return null;
2470
- const metadata = await parseSkillFile(skillFile);
2471
- if (!metadata) return null;
2472
- return {
2473
- name: metadata.name,
2474
- scopedName: metadata.name,
2475
- path: skillDir,
2476
- metadata
2477
- };
2478
- }
2479
- async function discoverSkills(rootDir) {
2480
- if (!existsSync12(rootDir)) return [];
2481
- const entries = await readdir(rootDir, { withFileTypes: true });
2482
- const skills = [];
2483
- for (const entry of entries) {
2484
- if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
2485
- const skillDir = join6(rootDir, entry.name);
2486
- const skill = await discoverSkill(skillDir);
2487
- if (skill) {
2488
- skills.push(skill);
2489
- }
2490
- }
2491
- return skills;
2492
- }
2493
- async function discoverSkillsMulti(dirs) {
2494
- const all = [];
2495
- const seen = /* @__PURE__ */ new Set();
2496
- for (const dir of dirs) {
2497
- const skills = await discoverSkills(dir);
2498
- for (const skill of skills) {
2499
- if (!seen.has(skill.name)) {
2500
- seen.add(skill.name);
2501
- all.push(skill);
2502
- }
2503
- }
2504
- }
2505
- return all;
2506
- }
2507
-
2508
- // src/core/skills/lock.ts
2509
- import { simpleGit } from "simple-git";
2510
- import { execFile } from "child_process";
2511
- import { promisify } from "util";
2512
- var execFileAsync = promisify(execFile);
2513
- async function recordSkillInstall(skillName, scopedName, source, sourceType, agents, canonicalPath, isGlobal, projectDir, version) {
2514
- await updateLockFile((lock) => {
2515
- const now = (/* @__PURE__ */ new Date()).toISOString();
2516
- const existing = lock.skills[skillName];
2517
- lock.skills[skillName] = {
2518
- name: skillName,
2519
- scopedName: existing?.scopedName ?? scopedName,
2520
- source: existing?.source ?? source,
2521
- sourceType: existing?.sourceType ?? sourceType,
2522
- version: version ?? existing?.version,
2523
- installedAt: existing?.installedAt ?? now,
2524
- updatedAt: now,
2525
- agents: [.../* @__PURE__ */ new Set([...existing?.agents ?? [], ...agents])],
2526
- canonicalPath,
2527
- isGlobal: existing?.isGlobal ?? isGlobal,
2528
- projectDir: existing?.projectDir ?? projectDir
2529
- };
2530
- });
2531
- }
2532
- async function removeSkillFromLock(skillName) {
2533
- let removed = false;
2534
- await updateLockFile((lock) => {
2535
- if (!(skillName in lock.skills)) return;
2536
- delete lock.skills[skillName];
2537
- removed = true;
2538
- });
2539
- return removed;
2540
- }
2541
- async function getTrackedSkills() {
2542
- const lock = await readLockFile();
2543
- return lock.skills;
2544
- }
2545
- async function fetchLatestSha(repoUrl, ref) {
2546
- try {
2547
- const git = simpleGit();
2548
- const target = ref ?? "HEAD";
2549
- const args = target === "HEAD" ? [repoUrl, "HEAD"] : ["--refs", repoUrl, target];
2550
- const result = await git.listRemote(args);
2551
- const firstLine = result.trim().split("\n")[0];
2552
- if (!firstLine) return null;
2553
- const sha = firstLine.split(" ")[0];
2554
- return sha ?? null;
2555
- } catch {
2556
- return null;
2557
- }
2558
- }
2559
- async function fetchLatestPackageVersion(packageName) {
2560
- try {
2561
- const { stdout } = await execFileAsync("npm", ["view", packageName, "version"]);
2562
- return stdout.trim() || null;
2563
- } catch {
2564
- return null;
2565
- }
2566
- }
2567
- async function checkSkillUpdate(skillName) {
2568
- const lock = await readLockFile();
2569
- const entry = lock.skills[skillName];
2570
- if (!entry) {
2571
- return { hasUpdate: false, status: "unknown" };
2572
- }
2573
- if (entry.sourceType !== "github" && entry.sourceType !== "gitlab" && entry.sourceType !== "library") {
2574
- return {
2575
- hasUpdate: false,
2576
- currentVersion: entry.version,
2577
- status: "unknown"
2578
- };
2579
- }
2580
- const parsed = parseSource(entry.source);
2581
- if (!parsed.owner) {
2582
- return {
2583
- hasUpdate: false,
2584
- currentVersion: entry.version,
2585
- status: "unknown"
2586
- };
2587
- }
2588
- if (entry.sourceType === "library") {
2589
- const packageName = parsed.owner;
2590
- const latestVersion = await fetchLatestPackageVersion(packageName);
2591
- if (!latestVersion) {
2592
- return {
2593
- hasUpdate: false,
2594
- currentVersion: entry.version,
2595
- status: "unknown"
2596
- };
2597
- }
2598
- const currentVersion2 = entry.version;
2599
- const hasUpdate2 = !currentVersion2 || currentVersion2 !== latestVersion;
2600
- return {
2601
- hasUpdate: hasUpdate2,
2602
- currentVersion: currentVersion2 ?? "unknown",
2603
- latestVersion,
2604
- status: hasUpdate2 ? "update-available" : "up-to-date"
2605
- };
2606
- }
2607
- if (!parsed.repo) {
2608
- return {
2609
- hasUpdate: false,
2610
- currentVersion: entry.version,
2611
- status: "unknown"
2612
- };
2613
- }
2614
- const host = parsed.type === "gitlab" ? "gitlab.com" : "github.com";
2615
- const repoUrl = `https://${host}/${parsed.owner}/${parsed.repo}.git`;
2616
- const latestSha = await fetchLatestSha(repoUrl, parsed.ref);
2617
- if (!latestSha) {
2618
- return {
2619
- hasUpdate: false,
2620
- currentVersion: entry.version,
2621
- status: "unknown"
2622
- };
2623
- }
2624
- const currentVersion = entry.version;
2625
- const hasUpdate = !currentVersion || !latestSha.startsWith(currentVersion.slice(0, 7));
2626
- return {
2627
- hasUpdate,
2628
- currentVersion: currentVersion ?? "unknown",
2629
- latestVersion: latestSha.slice(0, 12),
2630
- status: hasUpdate ? "update-available" : "up-to-date"
2631
- };
2632
- }
2633
- async function checkAllSkillUpdates() {
2634
- const lock = await readLockFile();
2635
- const skillNames = Object.keys(lock.skills);
2636
- const results = {};
2637
- await Promise.all(skillNames.map(async (name) => {
2638
- results[name] = await checkSkillUpdate(name);
2639
- }));
2640
- return results;
2641
- }
2642
-
2643
- // src/core/skills/recommendation.ts
2644
- var RECOMMENDATION_ERROR_CODES = {
2645
- QUERY_INVALID: "E_SKILLS_QUERY_INVALID",
2646
- NO_MATCHES: "E_SKILLS_NO_MATCHES",
2647
- SOURCE_UNAVAILABLE: "E_SKILLS_SOURCE_UNAVAILABLE",
2648
- CRITERIA_CONFLICT: "E_SKILLS_CRITERIA_CONFLICT"
2649
- };
2650
- var DEFAULT_WEIGHTS = {
2651
- mustHaveMatch: 10,
2652
- preferMatch: 4,
2653
- queryTokenMatch: 3,
2654
- starsFactor: 2,
2655
- metadataBoost: 2,
2656
- modernMarkerBoost: 3,
2657
- legacyMarkerPenalty: 3,
2658
- excludePenalty: 25,
2659
- missingMustHavePenalty: 20
2660
- };
2661
- var DEFAULT_MODERN_MARKERS = ["svelte 5", "runes", "lafs", "slsa", "drizzle", "better-auth"];
2662
- var DEFAULT_LEGACY_MARKERS = ["svelte 3", "jquery", "bower", "legacy", "book.json", "gitbook-cli"];
2663
- function tokenizeCriteriaValue(value) {
2664
- return value.split(",").map((part) => part.trim().toLowerCase()).filter(Boolean);
2665
- }
2666
- function normalizeList(value) {
2667
- if (value === void 0) return [];
2668
- if (!(typeof value === "string" || Array.isArray(value))) return [];
2669
- const source = Array.isArray(value) ? value : [value];
2670
- const flattened = source.flatMap((item) => typeof item === "string" ? tokenizeCriteriaValue(item) : []);
2671
- return Array.from(new Set(flattened)).sort((a, b) => a.localeCompare(b));
2672
- }
2673
- function hasAnyCriteriaInput(input) {
2674
- const query = typeof input.query === "string" ? input.query.trim() : "";
2675
- if (query.length > 0) return true;
2676
- const lists = [input.mustHave, input.prefer, input.exclude];
2677
- return lists.some((list) => normalizeList(list).length > 0);
2678
- }
2679
- function validateRecommendationCriteria(input) {
2680
- const issues = [];
2681
- if (input.query !== void 0 && typeof input.query !== "string") {
2682
- issues.push({
2683
- code: RECOMMENDATION_ERROR_CODES.QUERY_INVALID,
2684
- field: "query",
2685
- message: "query must be a string"
2686
- });
2687
- }
2688
- if (input.mustHave !== void 0 && !(typeof input.mustHave === "string" || Array.isArray(input.mustHave))) {
2689
- issues.push({
2690
- code: RECOMMENDATION_ERROR_CODES.QUERY_INVALID,
2691
- field: "mustHave",
2692
- message: "mustHave must be a string or string[]"
2693
- });
2694
- }
2695
- if (input.prefer !== void 0 && !(typeof input.prefer === "string" || Array.isArray(input.prefer))) {
2696
- issues.push({
2697
- code: RECOMMENDATION_ERROR_CODES.QUERY_INVALID,
2698
- field: "prefer",
2699
- message: "prefer must be a string or string[]"
2700
- });
2701
- }
2702
- if (input.exclude !== void 0 && !(typeof input.exclude === "string" || Array.isArray(input.exclude))) {
2703
- issues.push({
2704
- code: RECOMMENDATION_ERROR_CODES.QUERY_INVALID,
2705
- field: "exclude",
2706
- message: "exclude must be a string or string[]"
2707
- });
2708
- }
2709
- const mustHave = normalizeList(input.mustHave);
2710
- const prefer = normalizeList(input.prefer);
2711
- const exclude = normalizeList(input.exclude);
2712
- const conflict = mustHave.some((term) => exclude.includes(term)) || prefer.some((term) => exclude.includes(term));
2713
- if (conflict) {
2714
- issues.push({
2715
- code: RECOMMENDATION_ERROR_CODES.CRITERIA_CONFLICT,
2716
- field: "exclude",
2717
- message: "criteria terms cannot appear in both prefer/must-have and exclude"
2718
- });
2719
- }
2720
- if (issues.length === 0 && !hasAnyCriteriaInput(input)) {
2721
- issues.push({
2722
- code: RECOMMENDATION_ERROR_CODES.QUERY_INVALID,
2723
- field: "query",
2724
- message: "at least one criteria value is required"
2725
- });
2726
- }
2727
- return {
2728
- valid: issues.length === 0,
2729
- issues
2730
- };
2731
- }
2732
- function normalizeRecommendationCriteria(input) {
2733
- const query = (input.query ?? "").trim().toLowerCase();
2734
- return {
2735
- query,
2736
- queryTokens: query ? Array.from(new Set(tokenizeCriteriaValue(query.replace(/\s+/g, ",")))).sort((a, b) => a.localeCompare(b)) : [],
2737
- mustHave: normalizeList(input.mustHave),
2738
- prefer: normalizeList(input.prefer),
2739
- exclude: normalizeList(input.exclude)
2740
- };
2741
- }
2742
- function countMatches(haystack, needles) {
2743
- let count = 0;
2744
- for (const needle of needles) {
2745
- if (haystack.includes(needle)) {
2746
- count += 1;
2747
- }
2748
- }
2749
- return count;
2750
- }
2751
- function clampScore(value) {
2752
- return Number(value.toFixed(6));
2753
- }
2754
- function buildSearchText(skill) {
2755
- return `${skill.name} ${skill.scopedName} ${skill.description} ${skill.author}`.toLowerCase();
2756
- }
2757
- function scoreSkillRecommendation(skill, criteria, options = {}) {
2758
- const weights = { ...DEFAULT_WEIGHTS, ...options.weights };
2759
- const modernMarkers = (options.modernMarkers ?? DEFAULT_MODERN_MARKERS).map((marker) => marker.toLowerCase());
2760
- const legacyMarkers = (options.legacyMarkers ?? DEFAULT_LEGACY_MARKERS).map((marker) => marker.toLowerCase());
2761
- const text = buildSearchText(skill);
2762
- const reasons = [];
2763
- const tradeoffs = [];
2764
- const mustHaveMatches = countMatches(text, criteria.mustHave);
2765
- const missingMustHave = Math.max(criteria.mustHave.length - mustHaveMatches, 0);
2766
- const preferMatches = countMatches(text, criteria.prefer);
2767
- const queryMatches = countMatches(text, criteria.queryTokens);
2768
- const excludeMatches = countMatches(text, criteria.exclude);
2769
- const modernMatches = countMatches(text, modernMarkers);
2770
- const legacyMatches = countMatches(text, legacyMarkers);
2771
- const metadataSignal = skill.description.trim().length >= 80 ? 1 : 0;
2772
- const starsSignal = Math.log10(skill.stars + 1);
2773
- const sourceConfidence = skill.source === "agentskills.in" ? 1 : skill.source === "skills.sh" ? 0.8 : 0.6;
2774
- const mustHaveScore = mustHaveMatches * weights.mustHaveMatch - missingMustHave * weights.missingMustHavePenalty;
2775
- const preferScore = preferMatches * weights.preferMatch;
2776
- const queryScore = queryMatches * weights.queryTokenMatch;
2777
- const starsScore = starsSignal * weights.starsFactor;
2778
- const metadataScore = (metadataSignal + sourceConfidence) * weights.metadataBoost;
2779
- const modernityScore = modernMatches * weights.modernMarkerBoost - legacyMatches * weights.legacyMarkerPenalty;
2780
- const exclusionPenalty = excludeMatches * weights.excludePenalty;
2781
- const hasGitbookTopic = text.includes("gitbook");
2782
- const hasGitSync = text.includes("git sync") || text.includes("git") && text.includes("sync");
2783
- const hasApiWorkflow = text.includes("api") && (text.includes("workflow") || text.includes("sync"));
2784
- const hasLegacyCli = text.includes("gitbook-cli") || text.includes("book.json");
2785
- const topicScore = (hasGitbookTopic ? 3 : 0) + (hasGitSync ? 2 : 0) + (hasApiWorkflow ? 2 : 0) - (hasLegacyCli ? 4 : 0);
2786
- const total = clampScore(
2787
- mustHaveScore + preferScore + queryScore + starsScore + metadataScore + modernityScore + topicScore - exclusionPenalty
2788
- );
2789
- if (hasGitbookTopic) reasons.push({ code: "MATCH_TOPIC_GITBOOK" });
2790
- if (hasGitSync) reasons.push({ code: "HAS_GIT_SYNC" });
2791
- if (hasApiWorkflow) reasons.push({ code: "HAS_API_WORKFLOW" });
2792
- if (hasLegacyCli) reasons.push({ code: "PENALTY_LEGACY_CLI" });
2793
- if (mustHaveMatches > 0) reasons.push({ code: "MUST_HAVE_MATCH", detail: String(mustHaveMatches) });
2794
- if (missingMustHave > 0) reasons.push({ code: "MISSING_MUST_HAVE", detail: String(missingMustHave) });
2795
- if (preferMatches > 0) reasons.push({ code: "PREFER_MATCH", detail: String(preferMatches) });
2796
- if (queryMatches > 0) reasons.push({ code: "QUERY_MATCH", detail: String(queryMatches) });
2797
- if (starsSignal > 0) reasons.push({ code: "STAR_SIGNAL" });
2798
- if (metadataSignal > 0) reasons.push({ code: "METADATA_SIGNAL" });
2799
- if (modernMatches > 0) reasons.push({ code: "MODERN_MARKER", detail: String(modernMatches) });
2800
- if (legacyMatches > 0) reasons.push({ code: "LEGACY_MARKER", detail: String(legacyMatches) });
2801
- if (excludeMatches > 0) reasons.push({ code: "EXCLUDE_MATCH", detail: String(excludeMatches) });
2802
- if (missingMustHave > 0) tradeoffs.push("Missing one or more required criteria terms.");
2803
- if (excludeMatches > 0) tradeoffs.push("Matches one or more excluded terms.");
2804
- if (skill.stars < 10) tradeoffs.push("Low quality signal from repository stars.");
2805
- if (hasLegacyCli) tradeoffs.push("Contains legacy GitBook CLI markers.");
2806
- const result = {
2807
- skill,
2808
- score: total,
2809
- reasons,
2810
- tradeoffs,
2811
- excluded: excludeMatches > 0
2812
- };
2813
- if (options.includeDetails) {
2814
- result.breakdown = {
2815
- mustHave: clampScore(mustHaveScore),
2816
- prefer: clampScore(preferScore),
2817
- query: clampScore(queryScore),
2818
- stars: clampScore(starsScore),
2819
- metadata: clampScore(metadataScore),
2820
- modernity: clampScore(modernityScore),
2821
- exclusionPenalty: clampScore(exclusionPenalty),
2822
- total
2823
- };
2824
- }
2825
- return result;
2826
- }
2827
- function recommendSkills(skills, criteriaInput, options = {}) {
2828
- const validation = validateRecommendationCriteria(criteriaInput);
2829
- if (!validation.valid) {
2830
- const first = validation.issues[0];
2831
- const error = new Error(first?.message ?? "Invalid recommendation criteria");
2832
- error.code = first?.code;
2833
- error.issues = validation.issues;
2834
- throw error;
2835
- }
2836
- const criteria = normalizeRecommendationCriteria(criteriaInput);
2837
- const ranking = skills.map((skill) => scoreSkillRecommendation(skill, criteria, options)).sort((a, b) => {
2838
- if (b.score !== a.score) return b.score - a.score;
2839
- if (b.skill.stars !== a.skill.stars) return b.skill.stars - a.skill.stars;
2840
- return a.skill.scopedName.localeCompare(b.skill.scopedName);
2841
- });
2842
- return {
2843
- criteria,
2844
- ranking: typeof options.top === "number" ? ranking.slice(0, Math.max(0, options.top)) : ranking
2845
- };
2846
- }
2847
- var rankSkills = recommendSkills;
2848
-
2849
- // src/core/skills/recommendation-api.ts
2850
- function formatSkillRecommendations(result, opts) {
2851
- const top = result.ranking;
2852
- if (opts.mode === "human") {
2853
- if (top.length === 0) return "No recommendations found.";
2854
- const lines = ["Recommended skills:", ""];
2855
- for (const [index, entry] of top.entries()) {
2856
- const marker = index === 0 ? " (Recommended)" : "";
2857
- lines.push(`${index + 1}) ${entry.skill.scopedName}${marker}`);
2858
- lines.push(` why: ${entry.reasons.map((reason) => reason.code).join(", ") || "score-based match"}`);
2859
- lines.push(` tradeoff: ${entry.tradeoffs[0] ?? "none"}`);
2860
- }
2861
- lines.push("");
2862
- lines.push(`CHOOSE: ${top.map((_, index) => index + 1).join(",")}`);
2863
- return lines.join("\n");
2864
- }
2865
- const options = top.map((entry, index) => ({
2866
- rank: index + 1,
2867
- scopedName: entry.skill.scopedName,
2868
- score: entry.score,
2869
- reasons: entry.reasons,
2870
- tradeoffs: entry.tradeoffs,
2871
- ...opts.details ? {
2872
- description: entry.skill.description,
2873
- source: entry.skill.source,
2874
- evidence: entry.breakdown ?? null
2875
- } : {}
2876
- }));
2877
- return {
2878
- query: result.criteria.query,
2879
- recommended: options[0] ?? null,
2880
- options
2881
- };
2882
- }
2883
- async function searchSkills(query, options = {}) {
2884
- const trimmed = query.trim();
2885
- if (!trimmed) {
2886
- const error = new Error("query must be non-empty");
2887
- error.code = RECOMMENDATION_ERROR_CODES.QUERY_INVALID;
2888
- throw error;
2889
- }
2890
- const client = new MarketplaceClient();
2891
- try {
2892
- return await client.search(trimmed, options.limit ?? 20);
2893
- } catch (error) {
2894
- const wrapped = new Error(error instanceof Error ? error.message : String(error));
2895
- wrapped.code = RECOMMENDATION_ERROR_CODES.SOURCE_UNAVAILABLE;
2896
- throw wrapped;
2897
- }
2898
- }
2899
- async function recommendSkills2(query, criteria, options = {}) {
2900
- const hits = await searchSkills(query, { limit: options.limit ?? Math.max((options.top ?? 3) * 5, 20) });
2901
- const ranked = recommendSkills(hits, { ...criteria, query }, options);
2902
- if (ranked.ranking.length === 0) {
2903
- const error = new Error("no matches found");
2904
- error.code = RECOMMENDATION_ERROR_CODES.NO_MATCHES;
2905
- throw error;
2906
- }
2907
- return ranked;
2908
- }
2909
-
2910
- // src/core/skills/audit/scanner.ts
2911
- import { existsSync as existsSync13 } from "fs";
2912
- import { readFile as readFile7 } from "fs/promises";
2913
-
2914
- // src/core/skills/audit/rules.ts
2915
- function rule(id, name, description, severity, category, pattern) {
2916
- return { id, name, description, severity, category, pattern };
1779
+ // src/core/skills/audit/rules.ts
1780
+ function rule(id, name, description, severity, category, pattern) {
1781
+ return { id, name, description, severity, category, pattern };
2917
1782
  }
2918
1783
  var AUDIT_RULES = [
2919
1784
  // ── Prompt Injection ────────────────────────────────────────
@@ -2998,14 +1863,7 @@ var AUDIT_RULES = [
2998
1863
  "command-injection",
2999
1864
  /(?:curl|wget|fetch)\s+.*\|\s*(?:sh|bash|zsh|python|node|eval)/i
3000
1865
  ),
3001
- rule(
3002
- "CI003",
3003
- "Eval usage",
3004
- "Dynamic code execution",
3005
- "high",
3006
- "command-injection",
3007
- /\beval\s*\(/
3008
- ),
1866
+ rule("CI003", "Eval usage", "Dynamic code execution", "high", "command-injection", /\beval\s*\(/),
3009
1867
  rule(
3010
1868
  "CI004",
3011
1869
  "Shell spawn",
@@ -3279,108 +2137,1295 @@ var AUDIT_RULES = [
3279
2137
  )
3280
2138
  ];
3281
2139
 
3282
- // src/core/skills/audit/scanner.ts
3283
- var SEVERITY_WEIGHTS = {
3284
- critical: 25,
3285
- high: 15,
3286
- medium: 8,
3287
- low: 3,
3288
- info: 0
3289
- };
3290
- async function scanFile(filePath, rules) {
3291
- if (!existsSync13(filePath)) {
3292
- return { file: filePath, findings: [], score: 100, passed: true };
2140
+ // src/core/skills/audit/scanner.ts
2141
+ var SEVERITY_WEIGHTS = {
2142
+ critical: 25,
2143
+ high: 15,
2144
+ medium: 8,
2145
+ low: 3,
2146
+ info: 0
2147
+ };
2148
+ async function scanFile(filePath, rules) {
2149
+ if (!existsSync10(filePath)) {
2150
+ return { file: filePath, findings: [], score: 100, passed: true };
2151
+ }
2152
+ const content = await readFile6(filePath, "utf-8");
2153
+ const lines = content.split("\n");
2154
+ const activeRules = rules ?? AUDIT_RULES;
2155
+ const findings = [];
2156
+ for (const rule2 of activeRules) {
2157
+ for (let i = 0; i < lines.length; i++) {
2158
+ const line = lines[i] ?? "";
2159
+ const match = line.match(rule2.pattern);
2160
+ if (match) {
2161
+ findings.push({
2162
+ rule: rule2,
2163
+ line: i + 1,
2164
+ column: (match.index ?? 0) + 1,
2165
+ match: match[0],
2166
+ context: line.trim()
2167
+ });
2168
+ }
2169
+ }
2170
+ }
2171
+ const totalPenalty = findings.reduce(
2172
+ (sum, f) => sum + (SEVERITY_WEIGHTS[f.rule.severity] ?? 0),
2173
+ 0
2174
+ );
2175
+ const score = Math.max(0, 100 - totalPenalty);
2176
+ const passed = !findings.some(
2177
+ (f) => f.rule.severity === "critical" || f.rule.severity === "high"
2178
+ );
2179
+ return { file: filePath, findings, score, passed };
2180
+ }
2181
+ async function scanDirectory(dirPath) {
2182
+ const { readdir: readdir2 } = await import("fs/promises");
2183
+ const { join: join7 } = await import("path");
2184
+ if (!existsSync10(dirPath)) return [];
2185
+ const entries = await readdir2(dirPath, { withFileTypes: true });
2186
+ const results = [];
2187
+ for (const entry of entries) {
2188
+ if (entry.isDirectory() || entry.isSymbolicLink()) {
2189
+ const skillFile = join7(dirPath, entry.name, "SKILL.md");
2190
+ if (existsSync10(skillFile)) {
2191
+ results.push(await scanFile(skillFile));
2192
+ }
2193
+ }
2194
+ }
2195
+ return results;
2196
+ }
2197
+ function toSarif(results) {
2198
+ return {
2199
+ $schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json",
2200
+ version: "2.1.0",
2201
+ runs: [
2202
+ {
2203
+ tool: {
2204
+ driver: {
2205
+ name: "caamp-audit",
2206
+ version: "0.1.0",
2207
+ rules: AUDIT_RULES.map((r) => ({
2208
+ id: r.id,
2209
+ name: r.name,
2210
+ shortDescription: { text: r.description },
2211
+ defaultConfiguration: {
2212
+ level: r.severity === "critical" || r.severity === "high" ? "error" : "warning"
2213
+ },
2214
+ properties: { category: r.category }
2215
+ }))
2216
+ }
2217
+ },
2218
+ results: results.flatMap(
2219
+ (result) => result.findings.map((f) => ({
2220
+ ruleId: f.rule.id,
2221
+ level: f.rule.severity === "critical" || f.rule.severity === "high" ? "error" : "warning",
2222
+ message: { text: `${f.rule.description}: ${f.match}` },
2223
+ locations: [
2224
+ {
2225
+ physicalLocation: {
2226
+ artifactLocation: { uri: result.file },
2227
+ region: {
2228
+ startLine: f.line,
2229
+ startColumn: f.column
2230
+ }
2231
+ }
2232
+ }
2233
+ ]
2234
+ }))
2235
+ )
2236
+ }
2237
+ ]
2238
+ };
2239
+ }
2240
+
2241
+ // src/core/skills/lock.ts
2242
+ import { execFile } from "child_process";
2243
+ import { promisify } from "util";
2244
+ import { simpleGit } from "simple-git";
2245
+ var execFileAsync = promisify(execFile);
2246
+ async function recordSkillInstall(skillName, scopedName, source, sourceType, agents, canonicalPath, isGlobal, projectDir, version) {
2247
+ await updateLockFile((lock) => {
2248
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2249
+ const existing = lock.skills[skillName];
2250
+ lock.skills[skillName] = {
2251
+ name: skillName,
2252
+ scopedName: existing?.scopedName ?? scopedName,
2253
+ source: existing?.source ?? source,
2254
+ sourceType: existing?.sourceType ?? sourceType,
2255
+ version: version ?? existing?.version,
2256
+ installedAt: existing?.installedAt ?? now,
2257
+ updatedAt: now,
2258
+ agents: [.../* @__PURE__ */ new Set([...existing?.agents ?? [], ...agents])],
2259
+ canonicalPath,
2260
+ isGlobal: existing?.isGlobal ?? isGlobal,
2261
+ projectDir: existing?.projectDir ?? projectDir
2262
+ };
2263
+ });
2264
+ }
2265
+ async function removeSkillFromLock(skillName) {
2266
+ let removed = false;
2267
+ await updateLockFile((lock) => {
2268
+ if (!(skillName in lock.skills)) return;
2269
+ delete lock.skills[skillName];
2270
+ removed = true;
2271
+ });
2272
+ return removed;
2273
+ }
2274
+ async function getTrackedSkills() {
2275
+ const lock = await readLockFile();
2276
+ return lock.skills;
2277
+ }
2278
+ async function fetchLatestSha(repoUrl, ref) {
2279
+ try {
2280
+ const git = simpleGit();
2281
+ const target = ref ?? "HEAD";
2282
+ const args = target === "HEAD" ? [repoUrl, "HEAD"] : ["--refs", repoUrl, target];
2283
+ const result = await git.listRemote(args);
2284
+ const firstLine = result.trim().split("\n")[0];
2285
+ if (!firstLine) return null;
2286
+ const sha = firstLine.split(" ")[0];
2287
+ return sha ?? null;
2288
+ } catch {
2289
+ return null;
2290
+ }
2291
+ }
2292
+ async function fetchLatestPackageVersion(packageName) {
2293
+ try {
2294
+ const { stdout } = await execFileAsync("npm", ["view", packageName, "version"]);
2295
+ return stdout.trim() || null;
2296
+ } catch {
2297
+ return null;
2298
+ }
2299
+ }
2300
+ async function checkSkillUpdate(skillName) {
2301
+ const lock = await readLockFile();
2302
+ const entry = lock.skills[skillName];
2303
+ if (!entry) {
2304
+ return { hasUpdate: false, status: "unknown" };
2305
+ }
2306
+ if (entry.sourceType !== "github" && entry.sourceType !== "gitlab" && entry.sourceType !== "library") {
2307
+ return {
2308
+ hasUpdate: false,
2309
+ currentVersion: entry.version,
2310
+ status: "unknown"
2311
+ };
2312
+ }
2313
+ const parsed = parseSource(entry.source);
2314
+ if (!parsed.owner) {
2315
+ return {
2316
+ hasUpdate: false,
2317
+ currentVersion: entry.version,
2318
+ status: "unknown"
2319
+ };
2320
+ }
2321
+ if (entry.sourceType === "library") {
2322
+ const packageName = parsed.owner;
2323
+ const latestVersion = await fetchLatestPackageVersion(packageName);
2324
+ if (!latestVersion) {
2325
+ return {
2326
+ hasUpdate: false,
2327
+ currentVersion: entry.version,
2328
+ status: "unknown"
2329
+ };
2330
+ }
2331
+ const currentVersion2 = entry.version;
2332
+ const hasUpdate2 = !currentVersion2 || currentVersion2 !== latestVersion;
2333
+ return {
2334
+ hasUpdate: hasUpdate2,
2335
+ currentVersion: currentVersion2 ?? "unknown",
2336
+ latestVersion,
2337
+ status: hasUpdate2 ? "update-available" : "up-to-date"
2338
+ };
2339
+ }
2340
+ if (!parsed.repo) {
2341
+ return {
2342
+ hasUpdate: false,
2343
+ currentVersion: entry.version,
2344
+ status: "unknown"
2345
+ };
2346
+ }
2347
+ const host = parsed.type === "gitlab" ? "gitlab.com" : "github.com";
2348
+ const repoUrl = `https://${host}/${parsed.owner}/${parsed.repo}.git`;
2349
+ const latestSha = await fetchLatestSha(repoUrl, parsed.ref);
2350
+ if (!latestSha) {
2351
+ return {
2352
+ hasUpdate: false,
2353
+ currentVersion: entry.version,
2354
+ status: "unknown"
2355
+ };
2356
+ }
2357
+ const currentVersion = entry.version;
2358
+ const hasUpdate = !currentVersion || !latestSha.startsWith(currentVersion.slice(0, 7));
2359
+ return {
2360
+ hasUpdate,
2361
+ currentVersion: currentVersion ?? "unknown",
2362
+ latestVersion: latestSha.slice(0, 12),
2363
+ status: hasUpdate ? "update-available" : "up-to-date"
2364
+ };
2365
+ }
2366
+ async function checkAllSkillUpdates() {
2367
+ const lock = await readLockFile();
2368
+ const skillNames = Object.keys(lock.skills);
2369
+ const results = {};
2370
+ await Promise.all(
2371
+ skillNames.map(async (name) => {
2372
+ results[name] = await checkSkillUpdate(name);
2373
+ })
2374
+ );
2375
+ return results;
2376
+ }
2377
+
2378
+ // src/core/network/fetch.ts
2379
+ var DEFAULT_FETCH_TIMEOUT_MS = 1e4;
2380
+ var NetworkError = class extends Error {
2381
+ /** Classification of the failure. */
2382
+ kind;
2383
+ /** URL that was being fetched. */
2384
+ url;
2385
+ /** HTTP status code (only present for `"http"` kind). */
2386
+ status;
2387
+ constructor(message, kind, url, status) {
2388
+ super(message);
2389
+ this.name = "NetworkError";
2390
+ this.kind = kind;
2391
+ this.url = url;
2392
+ this.status = status;
2393
+ }
2394
+ };
2395
+ function isAbortError(error) {
2396
+ return error instanceof Error && error.name === "AbortError";
2397
+ }
2398
+ async function fetchWithTimeout(url, init, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
2399
+ try {
2400
+ return await fetch(url, {
2401
+ ...init,
2402
+ signal: AbortSignal.timeout(timeoutMs)
2403
+ });
2404
+ } catch (error) {
2405
+ if (isAbortError(error)) {
2406
+ throw new NetworkError(`Request timed out after ${timeoutMs}ms`, "timeout", url);
2407
+ }
2408
+ throw new NetworkError("Network request failed", "network", url);
2409
+ }
2410
+ }
2411
+ function ensureOkResponse(response, url) {
2412
+ if (!response.ok) {
2413
+ throw new NetworkError(
2414
+ `Request failed with status ${response.status}`,
2415
+ "http",
2416
+ url,
2417
+ response.status
2418
+ );
2419
+ }
2420
+ return response;
2421
+ }
2422
+ function formatNetworkError(error) {
2423
+ if (error instanceof NetworkError) {
2424
+ if (error.kind === "timeout") {
2425
+ return "Network request timed out. Please check your connection and try again.";
2426
+ }
2427
+ if (error.kind === "http") {
2428
+ return `Marketplace request failed with HTTP ${error.status ?? "unknown"}. Please try again shortly.`;
2429
+ }
2430
+ return "Network request failed. Please check your connection and try again.";
2431
+ }
2432
+ if (error instanceof Error) return error.message;
2433
+ return String(error);
2434
+ }
2435
+
2436
+ // src/core/marketplace/skillsmp.ts
2437
+ var API_BASE = "https://www.agentskills.in/api/skills";
2438
+ function parseScopedName(value) {
2439
+ const match = value.match(/^@([^/]+)\/([^/]+)$/);
2440
+ if (!match) return null;
2441
+ return {
2442
+ author: match[1],
2443
+ name: match[2]
2444
+ };
2445
+ }
2446
+ function toResult(skill) {
2447
+ return {
2448
+ name: skill.name,
2449
+ scopedName: skill.scopedName,
2450
+ description: skill.description,
2451
+ author: skill.author,
2452
+ stars: skill.stars,
2453
+ githubUrl: skill.githubUrl,
2454
+ repoFullName: skill.repoFullName,
2455
+ path: skill.path,
2456
+ source: "agentskills.in"
2457
+ };
2458
+ }
2459
+ var SkillsMPAdapter = class {
2460
+ /** The marketplace identifier used in search results. */
2461
+ name = "agentskills.in";
2462
+ /**
2463
+ * Search for skills by query string.
2464
+ *
2465
+ * @param query - Search query to match against skill names and descriptions.
2466
+ * @param limit - Maximum number of results to return.
2467
+ * @returns Array of marketplace results sorted by stars.
2468
+ */
2469
+ async search(query, limit = 20) {
2470
+ const params = new URLSearchParams({
2471
+ search: query,
2472
+ limit: String(limit),
2473
+ sortBy: "stars"
2474
+ });
2475
+ const url = `${API_BASE}?${params}`;
2476
+ const response = ensureOkResponse(await fetchWithTimeout(url), url);
2477
+ const data = await response.json();
2478
+ return data.skills.map(toResult);
2479
+ }
2480
+ /**
2481
+ * Look up a specific skill by its scoped name.
2482
+ *
2483
+ * @param scopedName - The scoped skill name (e.g. `"@author/skill-name"`).
2484
+ * @returns The matching marketplace result, or `null` if not found.
2485
+ */
2486
+ async getSkill(scopedName) {
2487
+ const parts = parseScopedName(scopedName);
2488
+ const searchTerms = parts ? [parts.name, `${parts.author} ${parts.name}`, scopedName] : [scopedName];
2489
+ const seen = /* @__PURE__ */ new Set();
2490
+ for (const term of searchTerms) {
2491
+ if (seen.has(term)) continue;
2492
+ seen.add(term);
2493
+ const params = new URLSearchParams({
2494
+ search: term,
2495
+ limit: "50",
2496
+ sortBy: "stars"
2497
+ });
2498
+ const url = `${API_BASE}?${params}`;
2499
+ const response = ensureOkResponse(await fetchWithTimeout(url), url);
2500
+ const data = await response.json();
2501
+ const match = data.skills.find(
2502
+ (s) => s.scopedName === scopedName || `@${s.author}/${s.name}` === scopedName
2503
+ );
2504
+ if (match) {
2505
+ return toResult(match);
2506
+ }
2507
+ }
2508
+ return null;
2509
+ }
2510
+ };
2511
+
2512
+ // src/core/marketplace/skillssh.ts
2513
+ var API_BASE2 = "https://skills.sh/api";
2514
+ function toResult2(skill) {
2515
+ return {
2516
+ name: skill.name,
2517
+ scopedName: `@${skill.author}/${skill.name}`,
2518
+ description: skill.description,
2519
+ author: skill.author,
2520
+ stars: skill.stars ?? 0,
2521
+ githubUrl: skill.url,
2522
+ repoFullName: skill.repo,
2523
+ path: "",
2524
+ source: "skills.sh"
2525
+ };
2526
+ }
2527
+ var SkillsShAdapter = class {
2528
+ /** The marketplace identifier used in search results. */
2529
+ name = "skills.sh";
2530
+ /**
2531
+ * Search for skills by query string.
2532
+ *
2533
+ * @param query - Search query to match against skill names.
2534
+ * @param limit - Maximum number of results to return.
2535
+ * @returns Array of marketplace results.
2536
+ */
2537
+ async search(query, limit = 20) {
2538
+ const params = new URLSearchParams({
2539
+ q: query,
2540
+ limit: String(limit)
2541
+ });
2542
+ const url = `${API_BASE2}/search?${params}`;
2543
+ const response = ensureOkResponse(await fetchWithTimeout(url), url);
2544
+ const data = await response.json();
2545
+ return data.results.map(toResult2);
2546
+ }
2547
+ /**
2548
+ * Look up a specific skill by its scoped name.
2549
+ *
2550
+ * @param scopedName - The scoped skill name (e.g. `"@author/skill-name"`).
2551
+ * @returns The matching marketplace result, or `null` if not found.
2552
+ */
2553
+ async getSkill(scopedName) {
2554
+ const results = await this.search(scopedName, 5);
2555
+ return results.find((r) => r.scopedName === scopedName) ?? null;
2556
+ }
2557
+ };
2558
+
2559
+ // src/core/marketplace/client.ts
2560
+ var MarketplaceUnavailableError = class extends Error {
2561
+ /** Per-adapter failure messages. */
2562
+ details;
2563
+ constructor(message, details) {
2564
+ super(message);
2565
+ this.name = "MarketplaceUnavailableError";
2566
+ this.details = details;
2567
+ }
2568
+ };
2569
+ var MarketplaceClient = class {
2570
+ /** Configured marketplace adapters. */
2571
+ adapters;
2572
+ /**
2573
+ * Create a new marketplace client.
2574
+ *
2575
+ * @param adapters - Custom marketplace adapters (defaults to agentskills.in and skills.sh)
2576
+ *
2577
+ * @example
2578
+ * ```typescript
2579
+ * // Use default adapters
2580
+ * const client = new MarketplaceClient();
2581
+ *
2582
+ * // Use custom adapters
2583
+ * const client = new MarketplaceClient([myAdapter]);
2584
+ * ```
2585
+ */
2586
+ constructor(adapters) {
2587
+ this.adapters = adapters ?? [new SkillsMPAdapter(), new SkillsShAdapter()];
2588
+ }
2589
+ /**
2590
+ * Search all marketplaces and return deduplicated, sorted results.
2591
+ *
2592
+ * Queries all adapters in parallel and deduplicates by `scopedName`,
2593
+ * keeping the entry with the highest star count. Results are sorted by
2594
+ * stars descending.
2595
+ *
2596
+ * @param query - Search query string
2597
+ * @param limit - Maximum number of results to return (default: 20)
2598
+ * @returns Deduplicated and sorted marketplace results
2599
+ *
2600
+ * @example
2601
+ * ```typescript
2602
+ * const results = await client.search("code review", 10);
2603
+ * ```
2604
+ */
2605
+ async search(query, limit = 20) {
2606
+ const settled = await Promise.allSettled(
2607
+ this.adapters.map((adapter) => adapter.search(query, limit))
2608
+ );
2609
+ const flat = [];
2610
+ const failures = [];
2611
+ for (const [index, result] of settled.entries()) {
2612
+ const adapterName = this.adapters[index]?.name ?? "unknown";
2613
+ if (result.status === "fulfilled") {
2614
+ flat.push(...result.value);
2615
+ } else {
2616
+ const reason = result.reason instanceof Error ? result.reason.message : String(result.reason);
2617
+ failures.push(`${adapterName}: ${reason}`);
2618
+ }
2619
+ }
2620
+ if (flat.length === 0 && failures.length > 0) {
2621
+ throw new MarketplaceUnavailableError("All marketplace sources failed.", failures);
2622
+ }
2623
+ const seen = /* @__PURE__ */ new Map();
2624
+ for (const result of flat) {
2625
+ const existing = seen.get(result.scopedName);
2626
+ if (!existing || result.stars > existing.stars) {
2627
+ seen.set(result.scopedName, result);
2628
+ }
2629
+ }
2630
+ const deduplicated = Array.from(seen.values());
2631
+ deduplicated.sort((a, b) => b.stars - a.stars);
2632
+ return deduplicated.slice(0, limit);
2633
+ }
2634
+ /**
2635
+ * Get a specific skill by its scoped name from any marketplace.
2636
+ *
2637
+ * Tries each adapter in order and returns the first match.
2638
+ *
2639
+ * @param scopedName - Scoped skill name (e.g. `"@author/my-skill"`)
2640
+ * @returns The marketplace result, or `null` if not found in any marketplace
2641
+ *
2642
+ * @example
2643
+ * ```typescript
2644
+ * const skill = await client.getSkill("@anthropic/memory");
2645
+ * ```
2646
+ */
2647
+ async getSkill(scopedName) {
2648
+ const failures = [];
2649
+ for (const adapter of this.adapters) {
2650
+ try {
2651
+ const result = await adapter.getSkill(scopedName);
2652
+ if (result) return result;
2653
+ } catch (error) {
2654
+ const reason = error instanceof Error ? error.message : String(error);
2655
+ failures.push(`${adapter.name}: ${reason}`);
2656
+ }
2657
+ }
2658
+ if (failures.length === this.adapters.length && this.adapters.length > 0) {
2659
+ throw new MarketplaceUnavailableError("All marketplace sources failed.", failures);
2660
+ }
2661
+ return null;
2662
+ }
2663
+ };
2664
+
2665
+ // src/core/skills/recommendation.ts
2666
+ var RECOMMENDATION_ERROR_CODES = {
2667
+ QUERY_INVALID: "E_SKILLS_QUERY_INVALID",
2668
+ NO_MATCHES: "E_SKILLS_NO_MATCHES",
2669
+ SOURCE_UNAVAILABLE: "E_SKILLS_SOURCE_UNAVAILABLE",
2670
+ CRITERIA_CONFLICT: "E_SKILLS_CRITERIA_CONFLICT"
2671
+ };
2672
+ var DEFAULT_WEIGHTS = {
2673
+ mustHaveMatch: 10,
2674
+ preferMatch: 4,
2675
+ queryTokenMatch: 3,
2676
+ starsFactor: 2,
2677
+ metadataBoost: 2,
2678
+ modernMarkerBoost: 3,
2679
+ legacyMarkerPenalty: 3,
2680
+ excludePenalty: 25,
2681
+ missingMustHavePenalty: 20
2682
+ };
2683
+ var DEFAULT_MODERN_MARKERS = ["svelte 5", "runes", "lafs", "slsa", "drizzle", "better-auth"];
2684
+ var DEFAULT_LEGACY_MARKERS = [
2685
+ "svelte 3",
2686
+ "jquery",
2687
+ "bower",
2688
+ "legacy",
2689
+ "book.json",
2690
+ "gitbook-cli"
2691
+ ];
2692
+ function tokenizeCriteriaValue(value) {
2693
+ return value.split(",").map((part) => part.trim().toLowerCase()).filter(Boolean);
2694
+ }
2695
+ function normalizeList(value) {
2696
+ if (value === void 0) return [];
2697
+ if (!(typeof value === "string" || Array.isArray(value))) return [];
2698
+ const source = Array.isArray(value) ? value : [value];
2699
+ const flattened = source.flatMap(
2700
+ (item) => typeof item === "string" ? tokenizeCriteriaValue(item) : []
2701
+ );
2702
+ return Array.from(new Set(flattened)).sort((a, b) => a.localeCompare(b));
2703
+ }
2704
+ function hasAnyCriteriaInput(input) {
2705
+ const query = typeof input.query === "string" ? input.query.trim() : "";
2706
+ if (query.length > 0) return true;
2707
+ const lists = [input.mustHave, input.prefer, input.exclude];
2708
+ return lists.some((list) => normalizeList(list).length > 0);
2709
+ }
2710
+ function validateRecommendationCriteria(input) {
2711
+ const issues = [];
2712
+ if (input.query !== void 0 && typeof input.query !== "string") {
2713
+ issues.push({
2714
+ code: RECOMMENDATION_ERROR_CODES.QUERY_INVALID,
2715
+ field: "query",
2716
+ message: "query must be a string"
2717
+ });
2718
+ }
2719
+ if (input.mustHave !== void 0 && !(typeof input.mustHave === "string" || Array.isArray(input.mustHave))) {
2720
+ issues.push({
2721
+ code: RECOMMENDATION_ERROR_CODES.QUERY_INVALID,
2722
+ field: "mustHave",
2723
+ message: "mustHave must be a string or string[]"
2724
+ });
2725
+ }
2726
+ if (input.prefer !== void 0 && !(typeof input.prefer === "string" || Array.isArray(input.prefer))) {
2727
+ issues.push({
2728
+ code: RECOMMENDATION_ERROR_CODES.QUERY_INVALID,
2729
+ field: "prefer",
2730
+ message: "prefer must be a string or string[]"
2731
+ });
2732
+ }
2733
+ if (input.exclude !== void 0 && !(typeof input.exclude === "string" || Array.isArray(input.exclude))) {
2734
+ issues.push({
2735
+ code: RECOMMENDATION_ERROR_CODES.QUERY_INVALID,
2736
+ field: "exclude",
2737
+ message: "exclude must be a string or string[]"
2738
+ });
2739
+ }
2740
+ const mustHave = normalizeList(input.mustHave);
2741
+ const prefer = normalizeList(input.prefer);
2742
+ const exclude = normalizeList(input.exclude);
2743
+ const conflict = mustHave.some((term) => exclude.includes(term)) || prefer.some((term) => exclude.includes(term));
2744
+ if (conflict) {
2745
+ issues.push({
2746
+ code: RECOMMENDATION_ERROR_CODES.CRITERIA_CONFLICT,
2747
+ field: "exclude",
2748
+ message: "criteria terms cannot appear in both prefer/must-have and exclude"
2749
+ });
2750
+ }
2751
+ if (issues.length === 0 && !hasAnyCriteriaInput(input)) {
2752
+ issues.push({
2753
+ code: RECOMMENDATION_ERROR_CODES.QUERY_INVALID,
2754
+ field: "query",
2755
+ message: "at least one criteria value is required"
2756
+ });
2757
+ }
2758
+ return {
2759
+ valid: issues.length === 0,
2760
+ issues
2761
+ };
2762
+ }
2763
+ function normalizeRecommendationCriteria(input) {
2764
+ const query = (input.query ?? "").trim().toLowerCase();
2765
+ return {
2766
+ query,
2767
+ queryTokens: query ? Array.from(new Set(tokenizeCriteriaValue(query.replace(/\s+/g, ",")))).sort(
2768
+ (a, b) => a.localeCompare(b)
2769
+ ) : [],
2770
+ mustHave: normalizeList(input.mustHave),
2771
+ prefer: normalizeList(input.prefer),
2772
+ exclude: normalizeList(input.exclude)
2773
+ };
2774
+ }
2775
+ function countMatches(haystack, needles) {
2776
+ let count = 0;
2777
+ for (const needle of needles) {
2778
+ if (haystack.includes(needle)) {
2779
+ count += 1;
2780
+ }
2781
+ }
2782
+ return count;
2783
+ }
2784
+ function clampScore(value) {
2785
+ return Number(value.toFixed(6));
2786
+ }
2787
+ function buildSearchText(skill) {
2788
+ return `${skill.name} ${skill.scopedName} ${skill.description} ${skill.author}`.toLowerCase();
2789
+ }
2790
+ function scoreSkillRecommendation(skill, criteria, options = {}) {
2791
+ const weights = { ...DEFAULT_WEIGHTS, ...options.weights };
2792
+ const modernMarkers = (options.modernMarkers ?? DEFAULT_MODERN_MARKERS).map(
2793
+ (marker) => marker.toLowerCase()
2794
+ );
2795
+ const legacyMarkers = (options.legacyMarkers ?? DEFAULT_LEGACY_MARKERS).map(
2796
+ (marker) => marker.toLowerCase()
2797
+ );
2798
+ const text = buildSearchText(skill);
2799
+ const reasons = [];
2800
+ const tradeoffs = [];
2801
+ const mustHaveMatches = countMatches(text, criteria.mustHave);
2802
+ const missingMustHave = Math.max(criteria.mustHave.length - mustHaveMatches, 0);
2803
+ const preferMatches = countMatches(text, criteria.prefer);
2804
+ const queryMatches = countMatches(text, criteria.queryTokens);
2805
+ const excludeMatches = countMatches(text, criteria.exclude);
2806
+ const modernMatches = countMatches(text, modernMarkers);
2807
+ const legacyMatches = countMatches(text, legacyMarkers);
2808
+ const metadataSignal = skill.description.trim().length >= 80 ? 1 : 0;
2809
+ const starsSignal = Math.log10(skill.stars + 1);
2810
+ const sourceConfidence = skill.source === "agentskills.in" ? 1 : skill.source === "skills.sh" ? 0.8 : 0.6;
2811
+ const mustHaveScore = mustHaveMatches * weights.mustHaveMatch - missingMustHave * weights.missingMustHavePenalty;
2812
+ const preferScore = preferMatches * weights.preferMatch;
2813
+ const queryScore = queryMatches * weights.queryTokenMatch;
2814
+ const starsScore = starsSignal * weights.starsFactor;
2815
+ const metadataScore = (metadataSignal + sourceConfidence) * weights.metadataBoost;
2816
+ const modernityScore = modernMatches * weights.modernMarkerBoost - legacyMatches * weights.legacyMarkerPenalty;
2817
+ const exclusionPenalty = excludeMatches * weights.excludePenalty;
2818
+ const hasGitbookTopic = text.includes("gitbook");
2819
+ const hasGitSync = text.includes("git sync") || text.includes("git") && text.includes("sync");
2820
+ const hasApiWorkflow = text.includes("api") && (text.includes("workflow") || text.includes("sync"));
2821
+ const hasLegacyCli = text.includes("gitbook-cli") || text.includes("book.json");
2822
+ const topicScore = (hasGitbookTopic ? 3 : 0) + (hasGitSync ? 2 : 0) + (hasApiWorkflow ? 2 : 0) - (hasLegacyCli ? 4 : 0);
2823
+ const total = clampScore(
2824
+ mustHaveScore + preferScore + queryScore + starsScore + metadataScore + modernityScore + topicScore - exclusionPenalty
2825
+ );
2826
+ if (hasGitbookTopic) reasons.push({ code: "MATCH_TOPIC_GITBOOK" });
2827
+ if (hasGitSync) reasons.push({ code: "HAS_GIT_SYNC" });
2828
+ if (hasApiWorkflow) reasons.push({ code: "HAS_API_WORKFLOW" });
2829
+ if (hasLegacyCli) reasons.push({ code: "PENALTY_LEGACY_CLI" });
2830
+ if (mustHaveMatches > 0)
2831
+ reasons.push({ code: "MUST_HAVE_MATCH", detail: String(mustHaveMatches) });
2832
+ if (missingMustHave > 0)
2833
+ reasons.push({ code: "MISSING_MUST_HAVE", detail: String(missingMustHave) });
2834
+ if (preferMatches > 0) reasons.push({ code: "PREFER_MATCH", detail: String(preferMatches) });
2835
+ if (queryMatches > 0) reasons.push({ code: "QUERY_MATCH", detail: String(queryMatches) });
2836
+ if (starsSignal > 0) reasons.push({ code: "STAR_SIGNAL" });
2837
+ if (metadataSignal > 0) reasons.push({ code: "METADATA_SIGNAL" });
2838
+ if (modernMatches > 0) reasons.push({ code: "MODERN_MARKER", detail: String(modernMatches) });
2839
+ if (legacyMatches > 0) reasons.push({ code: "LEGACY_MARKER", detail: String(legacyMatches) });
2840
+ if (excludeMatches > 0) reasons.push({ code: "EXCLUDE_MATCH", detail: String(excludeMatches) });
2841
+ if (missingMustHave > 0) tradeoffs.push("Missing one or more required criteria terms.");
2842
+ if (excludeMatches > 0) tradeoffs.push("Matches one or more excluded terms.");
2843
+ if (skill.stars < 10) tradeoffs.push("Low quality signal from repository stars.");
2844
+ if (hasLegacyCli) tradeoffs.push("Contains legacy GitBook CLI markers.");
2845
+ const result = {
2846
+ skill,
2847
+ score: total,
2848
+ reasons,
2849
+ tradeoffs,
2850
+ excluded: excludeMatches > 0
2851
+ };
2852
+ if (options.includeDetails) {
2853
+ result.breakdown = {
2854
+ mustHave: clampScore(mustHaveScore),
2855
+ prefer: clampScore(preferScore),
2856
+ query: clampScore(queryScore),
2857
+ stars: clampScore(starsScore),
2858
+ metadata: clampScore(metadataScore),
2859
+ modernity: clampScore(modernityScore),
2860
+ exclusionPenalty: clampScore(exclusionPenalty),
2861
+ total
2862
+ };
2863
+ }
2864
+ return result;
2865
+ }
2866
+ function recommendSkills(skills, criteriaInput, options = {}) {
2867
+ const validation = validateRecommendationCriteria(criteriaInput);
2868
+ if (!validation.valid) {
2869
+ const first = validation.issues[0];
2870
+ const error = new Error(first?.message ?? "Invalid recommendation criteria");
2871
+ error.code = first?.code;
2872
+ error.issues = validation.issues;
2873
+ throw error;
2874
+ }
2875
+ const criteria = normalizeRecommendationCriteria(criteriaInput);
2876
+ const ranking = skills.map((skill) => scoreSkillRecommendation(skill, criteria, options)).sort((a, b) => {
2877
+ if (b.score !== a.score) return b.score - a.score;
2878
+ if (b.skill.stars !== a.skill.stars) return b.skill.stars - a.skill.stars;
2879
+ return a.skill.scopedName.localeCompare(b.skill.scopedName);
2880
+ });
2881
+ return {
2882
+ criteria,
2883
+ ranking: typeof options.top === "number" ? ranking.slice(0, Math.max(0, options.top)) : ranking
2884
+ };
2885
+ }
2886
+ var rankSkills = recommendSkills;
2887
+
2888
+ // src/core/skills/recommendation-api.ts
2889
+ function formatSkillRecommendations(result, opts) {
2890
+ const top = result.ranking;
2891
+ if (opts.mode === "human") {
2892
+ if (top.length === 0) return "No recommendations found.";
2893
+ const lines = ["Recommended skills:", ""];
2894
+ for (const [index, entry] of top.entries()) {
2895
+ const marker = index === 0 ? " (Recommended)" : "";
2896
+ lines.push(`${index + 1}) ${entry.skill.scopedName}${marker}`);
2897
+ lines.push(
2898
+ ` why: ${entry.reasons.map((reason) => reason.code).join(", ") || "score-based match"}`
2899
+ );
2900
+ lines.push(` tradeoff: ${entry.tradeoffs[0] ?? "none"}`);
2901
+ }
2902
+ lines.push("");
2903
+ lines.push(`CHOOSE: ${top.map((_, index) => index + 1).join(",")}`);
2904
+ return lines.join("\n");
2905
+ }
2906
+ const options = top.map((entry, index) => ({
2907
+ rank: index + 1,
2908
+ scopedName: entry.skill.scopedName,
2909
+ score: entry.score,
2910
+ reasons: entry.reasons,
2911
+ tradeoffs: entry.tradeoffs,
2912
+ ...opts.details ? {
2913
+ description: entry.skill.description,
2914
+ source: entry.skill.source,
2915
+ evidence: entry.breakdown ?? null
2916
+ } : {}
2917
+ }));
2918
+ return {
2919
+ query: result.criteria.query,
2920
+ recommended: options[0] ?? null,
2921
+ options
2922
+ };
2923
+ }
2924
+ async function searchSkills(query, options = {}) {
2925
+ const trimmed = query.trim();
2926
+ if (!trimmed) {
2927
+ const error = new Error("query must be non-empty");
2928
+ error.code = RECOMMENDATION_ERROR_CODES.QUERY_INVALID;
2929
+ throw error;
2930
+ }
2931
+ const client = new MarketplaceClient();
2932
+ try {
2933
+ return await client.search(trimmed, options.limit ?? 20);
2934
+ } catch (error) {
2935
+ const wrapped = new Error(error instanceof Error ? error.message : String(error));
2936
+ wrapped.code = RECOMMENDATION_ERROR_CODES.SOURCE_UNAVAILABLE;
2937
+ throw wrapped;
2938
+ }
2939
+ }
2940
+ async function recommendSkills2(query, criteria, options = {}) {
2941
+ const hits = await searchSkills(query, {
2942
+ limit: options.limit ?? Math.max((options.top ?? 3) * 5, 20)
2943
+ });
2944
+ const ranked = recommendSkills(hits, { ...criteria, query }, options);
2945
+ if (ranked.ranking.length === 0) {
2946
+ const error = new Error("no matches found");
2947
+ error.code = RECOMMENDATION_ERROR_CODES.NO_MATCHES;
2948
+ throw error;
2949
+ }
2950
+ return ranked;
2951
+ }
2952
+
2953
+ // src/core/skills/library-loader.ts
2954
+ import { existsSync as existsSync11, readdirSync, readFileSync } from "fs";
2955
+ import { createRequire } from "module";
2956
+ import { basename as basename2, dirname as dirname2, join as join4 } from "path";
2957
+ var require2 = createRequire(import.meta.url);
2958
+ function loadLibraryFromModule(root) {
2959
+ let mod;
2960
+ try {
2961
+ mod = require2(root);
2962
+ } catch {
2963
+ throw new Error(`Failed to load skill library module from ${root}`);
2964
+ }
2965
+ const requiredMethods = [
2966
+ "listSkills",
2967
+ "getSkill",
2968
+ "getSkillPath",
2969
+ "getSkillDir",
2970
+ "readSkillContent",
2971
+ "getCoreSkills",
2972
+ "getSkillsByCategory",
2973
+ "getSkillDependencies",
2974
+ "resolveDependencyTree",
2975
+ "listProfiles",
2976
+ "getProfile",
2977
+ "resolveProfile",
2978
+ "listSharedResources",
2979
+ "getSharedResourcePath",
2980
+ "readSharedResource",
2981
+ "listProtocols",
2982
+ "getProtocolPath",
2983
+ "readProtocol",
2984
+ "validateSkillFrontmatter",
2985
+ "validateAll",
2986
+ "getDispatchMatrix"
2987
+ ];
2988
+ for (const method of requiredMethods) {
2989
+ if (typeof mod[method] !== "function") {
2990
+ throw new Error(`Skill library at ${root} does not implement required method: ${method}`);
2991
+ }
2992
+ }
2993
+ if (!mod.version || typeof mod.version !== "string") {
2994
+ throw new Error(`Skill library at ${root} is missing 'version' property`);
2995
+ }
2996
+ if (!mod.libraryRoot || typeof mod.libraryRoot !== "string") {
2997
+ throw new Error(`Skill library at ${root} is missing 'libraryRoot' property`);
2998
+ }
2999
+ return mod;
3000
+ }
3001
+ function buildLibraryFromFiles(root) {
3002
+ const catalogPath = join4(root, "skills.json");
3003
+ if (!existsSync11(catalogPath)) {
3004
+ throw new Error(`No skills.json found at ${root}`);
3005
+ }
3006
+ const catalogData = JSON.parse(readFileSync(catalogPath, "utf-8"));
3007
+ const entries = catalogData.skills ?? [];
3008
+ const version = catalogData.version ?? "0.0.0";
3009
+ const manifestPath = join4(root, "skills", "manifest.json");
3010
+ let manifest;
3011
+ if (existsSync11(manifestPath)) {
3012
+ manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
3013
+ } else {
3014
+ manifest = {
3015
+ $schema: "",
3016
+ _meta: {},
3017
+ dispatch_matrix: { by_task_type: {}, by_keyword: {}, by_protocol: {} },
3018
+ skills: []
3019
+ };
3020
+ }
3021
+ const profilesDir = join4(root, "profiles");
3022
+ const profiles = /* @__PURE__ */ new Map();
3023
+ if (existsSync11(profilesDir)) {
3024
+ for (const file of readdirSync(profilesDir)) {
3025
+ if (!file.endsWith(".json")) continue;
3026
+ try {
3027
+ const profile = JSON.parse(
3028
+ readFileSync(join4(profilesDir, file), "utf-8")
3029
+ );
3030
+ profiles.set(profile.name, profile);
3031
+ } catch {
3032
+ }
3033
+ }
3034
+ }
3035
+ const skillMap = /* @__PURE__ */ new Map();
3036
+ for (const entry of entries) {
3037
+ skillMap.set(entry.name, entry);
3038
+ }
3039
+ function getSkillDir2(name) {
3040
+ const entry = skillMap.get(name);
3041
+ if (entry) {
3042
+ return dirname2(join4(root, entry.path));
3043
+ }
3044
+ return join4(root, "skills", name);
3045
+ }
3046
+ function resolveDeps(names, visited = /* @__PURE__ */ new Set()) {
3047
+ const result = [];
3048
+ for (const name of names) {
3049
+ if (visited.has(name)) continue;
3050
+ visited.add(name);
3051
+ const entry = skillMap.get(name);
3052
+ if (entry && entry.dependencies.length > 0) {
3053
+ result.push(...resolveDeps(entry.dependencies, visited));
3054
+ }
3055
+ result.push(name);
3056
+ }
3057
+ return result;
3058
+ }
3059
+ function resolveProfileByName(name, visited = /* @__PURE__ */ new Set()) {
3060
+ if (visited.has(name)) return [];
3061
+ visited.add(name);
3062
+ const profile = profiles.get(name);
3063
+ if (!profile) return [];
3064
+ let skills = [];
3065
+ if (profile.extends) {
3066
+ skills = resolveProfileByName(profile.extends, visited);
3067
+ }
3068
+ skills.push(...profile.skills);
3069
+ return resolveDeps([...new Set(skills)]);
3070
+ }
3071
+ function discoverFiles(dir, ext) {
3072
+ if (!existsSync11(dir)) return [];
3073
+ return readdirSync(dir).filter((f) => f.endsWith(ext)).map((f) => basename2(f, ext));
3074
+ }
3075
+ const library = {
3076
+ version,
3077
+ libraryRoot: root,
3078
+ skills: entries,
3079
+ manifest,
3080
+ listSkills() {
3081
+ return entries.map((e) => e.name);
3082
+ },
3083
+ getSkill(name) {
3084
+ return skillMap.get(name);
3085
+ },
3086
+ getSkillPath(name) {
3087
+ const entry = skillMap.get(name);
3088
+ if (entry) {
3089
+ return join4(root, entry.path);
3090
+ }
3091
+ return join4(root, "skills", name, "SKILL.md");
3092
+ },
3093
+ getSkillDir: getSkillDir2,
3094
+ readSkillContent(name) {
3095
+ const skillPath = library.getSkillPath(name);
3096
+ if (!existsSync11(skillPath)) {
3097
+ throw new Error(`Skill content not found: ${skillPath}`);
3098
+ }
3099
+ return readFileSync(skillPath, "utf-8");
3100
+ },
3101
+ getCoreSkills() {
3102
+ return entries.filter((e) => e.core);
3103
+ },
3104
+ getSkillsByCategory(category) {
3105
+ return entries.filter((e) => e.category === category);
3106
+ },
3107
+ getSkillDependencies(name) {
3108
+ return skillMap.get(name)?.dependencies ?? [];
3109
+ },
3110
+ resolveDependencyTree(names) {
3111
+ return resolveDeps(names);
3112
+ },
3113
+ listProfiles() {
3114
+ return [...profiles.keys()];
3115
+ },
3116
+ getProfile(name) {
3117
+ return profiles.get(name);
3118
+ },
3119
+ resolveProfile(name) {
3120
+ return resolveProfileByName(name);
3121
+ },
3122
+ listSharedResources() {
3123
+ return discoverFiles(join4(root, "skills", "_shared"), ".md");
3124
+ },
3125
+ getSharedResourcePath(name) {
3126
+ const resourcePath = join4(root, "skills", "_shared", `${name}.md`);
3127
+ return existsSync11(resourcePath) ? resourcePath : void 0;
3128
+ },
3129
+ readSharedResource(name) {
3130
+ const resourcePath = library.getSharedResourcePath(name);
3131
+ if (!resourcePath) return void 0;
3132
+ return readFileSync(resourcePath, "utf-8");
3133
+ },
3134
+ listProtocols() {
3135
+ const rootProtocols = discoverFiles(join4(root, "protocols"), ".md");
3136
+ if (rootProtocols.length > 0) return rootProtocols;
3137
+ return discoverFiles(join4(root, "skills", "protocols"), ".md");
3138
+ },
3139
+ getProtocolPath(name) {
3140
+ const rootPath = join4(root, "protocols", `${name}.md`);
3141
+ if (existsSync11(rootPath)) return rootPath;
3142
+ const skillsPath = join4(root, "skills", "protocols", `${name}.md`);
3143
+ return existsSync11(skillsPath) ? skillsPath : void 0;
3144
+ },
3145
+ readProtocol(name) {
3146
+ const protocolPath = library.getProtocolPath(name);
3147
+ if (!protocolPath) return void 0;
3148
+ return readFileSync(protocolPath, "utf-8");
3149
+ },
3150
+ validateSkillFrontmatter(name) {
3151
+ const entry = skillMap.get(name);
3152
+ if (!entry) {
3153
+ return {
3154
+ valid: false,
3155
+ issues: [{ level: "error", field: "name", message: `Skill not found: ${name}` }]
3156
+ };
3157
+ }
3158
+ const issues = [];
3159
+ if (!entry.name) {
3160
+ issues.push({ level: "error", field: "name", message: "Missing name" });
3161
+ }
3162
+ if (!entry.description) {
3163
+ issues.push({ level: "error", field: "description", message: "Missing description" });
3164
+ }
3165
+ if (!entry.version) {
3166
+ issues.push({ level: "warn", field: "version", message: "Missing version" });
3167
+ }
3168
+ const skillPath = join4(root, entry.path);
3169
+ if (!existsSync11(skillPath)) {
3170
+ issues.push({
3171
+ level: "error",
3172
+ field: "path",
3173
+ message: `SKILL.md not found at ${entry.path}`
3174
+ });
3175
+ }
3176
+ return {
3177
+ valid: !issues.some((i) => i.level === "error"),
3178
+ issues
3179
+ };
3180
+ },
3181
+ validateAll() {
3182
+ const results = /* @__PURE__ */ new Map();
3183
+ for (const entry of entries) {
3184
+ results.set(entry.name, library.validateSkillFrontmatter(entry.name));
3185
+ }
3186
+ return results;
3187
+ },
3188
+ getDispatchMatrix() {
3189
+ return manifest.dispatch_matrix;
3190
+ }
3191
+ };
3192
+ return library;
3193
+ }
3194
+
3195
+ // src/core/skills/catalog.ts
3196
+ var catalog_exports = {};
3197
+ __export(catalog_exports, {
3198
+ clearRegisteredLibrary: () => clearRegisteredLibrary,
3199
+ getCoreSkills: () => getCoreSkills,
3200
+ getDispatchMatrix: () => getDispatchMatrix,
3201
+ getLibraryRoot: () => getLibraryRoot,
3202
+ getManifest: () => getManifest,
3203
+ getProfile: () => getProfile,
3204
+ getProtocolPath: () => getProtocolPath,
3205
+ getSharedResourcePath: () => getSharedResourcePath,
3206
+ getSkill: () => getSkill,
3207
+ getSkillDependencies: () => getSkillDependencies,
3208
+ getSkillDir: () => getSkillDir,
3209
+ getSkillPath: () => getSkillPath,
3210
+ getSkills: () => getSkills,
3211
+ getSkillsByCategory: () => getSkillsByCategory,
3212
+ getVersion: () => getVersion,
3213
+ isCatalogAvailable: () => isCatalogAvailable,
3214
+ listProfiles: () => listProfiles,
3215
+ listProtocols: () => listProtocols,
3216
+ listSharedResources: () => listSharedResources,
3217
+ listSkills: () => listSkills,
3218
+ readProtocol: () => readProtocol,
3219
+ readSharedResource: () => readSharedResource,
3220
+ readSkillContent: () => readSkillContent,
3221
+ registerSkillLibrary: () => registerSkillLibrary,
3222
+ registerSkillLibraryFromPath: () => registerSkillLibraryFromPath,
3223
+ resolveDependencyTree: () => resolveDependencyTree,
3224
+ resolveProfile: () => resolveProfile,
3225
+ validateAll: () => validateAll,
3226
+ validateSkillFrontmatter: () => validateSkillFrontmatter
3227
+ });
3228
+ import { existsSync as existsSync12 } from "fs";
3229
+ import { join as join5 } from "path";
3230
+ var _library = null;
3231
+ function registerSkillLibrary(library) {
3232
+ _library = library;
3233
+ }
3234
+ function registerSkillLibraryFromPath(root) {
3235
+ const indexPath = join5(root, "index.js");
3236
+ if (existsSync12(indexPath)) {
3237
+ _library = loadLibraryFromModule(root);
3238
+ return;
3239
+ }
3240
+ _library = buildLibraryFromFiles(root);
3241
+ }
3242
+ function clearRegisteredLibrary() {
3243
+ _library = null;
3244
+ }
3245
+ function discoverLibrary() {
3246
+ const envPath = process.env["CAAMP_SKILL_LIBRARY"];
3247
+ if (envPath && existsSync12(envPath)) {
3248
+ try {
3249
+ const indexPath = join5(envPath, "index.js");
3250
+ if (existsSync12(indexPath)) {
3251
+ return loadLibraryFromModule(envPath);
3252
+ }
3253
+ if (existsSync12(join5(envPath, "skills.json"))) {
3254
+ return buildLibraryFromFiles(envPath);
3255
+ }
3256
+ } catch {
3257
+ }
3258
+ }
3259
+ return null;
3260
+ }
3261
+ function getLibrary() {
3262
+ if (!_library) {
3263
+ const discovered = discoverLibrary();
3264
+ if (discovered) {
3265
+ _library = discovered;
3266
+ }
3267
+ }
3268
+ if (!_library) {
3269
+ throw new Error(
3270
+ "No skill library registered. Register one with registerSkillLibraryFromPath() or set the CAAMP_SKILL_LIBRARY environment variable."
3271
+ );
3272
+ }
3273
+ return _library;
3274
+ }
3275
+ function isCatalogAvailable() {
3276
+ try {
3277
+ getLibrary();
3278
+ return true;
3279
+ } catch {
3280
+ return false;
3293
3281
  }
3294
- const content = await readFile7(filePath, "utf-8");
3295
- const lines = content.split("\n");
3296
- const activeRules = rules ?? AUDIT_RULES;
3297
- const findings = [];
3298
- for (const rule2 of activeRules) {
3299
- for (let i = 0; i < lines.length; i++) {
3300
- const line = lines[i] ?? "";
3301
- const match = line.match(rule2.pattern);
3302
- if (match) {
3303
- findings.push({
3304
- rule: rule2,
3305
- line: i + 1,
3306
- column: (match.index ?? 0) + 1,
3307
- match: match[0],
3308
- context: line.trim()
3309
- });
3310
- }
3282
+ }
3283
+ function getSkills() {
3284
+ return getLibrary().skills;
3285
+ }
3286
+ function getManifest() {
3287
+ return getLibrary().manifest;
3288
+ }
3289
+ function listSkills() {
3290
+ return getLibrary().listSkills();
3291
+ }
3292
+ function getSkill(name) {
3293
+ return getLibrary().getSkill(name);
3294
+ }
3295
+ function getSkillPath(name) {
3296
+ return getLibrary().getSkillPath(name);
3297
+ }
3298
+ function getSkillDir(name) {
3299
+ return getLibrary().getSkillDir(name);
3300
+ }
3301
+ function readSkillContent(name) {
3302
+ return getLibrary().readSkillContent(name);
3303
+ }
3304
+ function getCoreSkills() {
3305
+ return getLibrary().getCoreSkills();
3306
+ }
3307
+ function getSkillsByCategory(category) {
3308
+ return getLibrary().getSkillsByCategory(category);
3309
+ }
3310
+ function getSkillDependencies(name) {
3311
+ return getLibrary().getSkillDependencies(name);
3312
+ }
3313
+ function resolveDependencyTree(names) {
3314
+ return getLibrary().resolveDependencyTree(names);
3315
+ }
3316
+ function listProfiles() {
3317
+ return getLibrary().listProfiles();
3318
+ }
3319
+ function getProfile(name) {
3320
+ return getLibrary().getProfile(name);
3321
+ }
3322
+ function resolveProfile(name) {
3323
+ return getLibrary().resolveProfile(name);
3324
+ }
3325
+ function listSharedResources() {
3326
+ return getLibrary().listSharedResources();
3327
+ }
3328
+ function getSharedResourcePath(name) {
3329
+ return getLibrary().getSharedResourcePath(name);
3330
+ }
3331
+ function readSharedResource(name) {
3332
+ return getLibrary().readSharedResource(name);
3333
+ }
3334
+ function listProtocols() {
3335
+ return getLibrary().listProtocols();
3336
+ }
3337
+ function getProtocolPath(name) {
3338
+ return getLibrary().getProtocolPath(name);
3339
+ }
3340
+ function readProtocol(name) {
3341
+ return getLibrary().readProtocol(name);
3342
+ }
3343
+ function validateSkillFrontmatter(name) {
3344
+ return getLibrary().validateSkillFrontmatter(name);
3345
+ }
3346
+ function validateAll() {
3347
+ return getLibrary().validateAll();
3348
+ }
3349
+ function getDispatchMatrix() {
3350
+ return getLibrary().getDispatchMatrix();
3351
+ }
3352
+ function getVersion() {
3353
+ return getLibrary().version;
3354
+ }
3355
+ function getLibraryRoot() {
3356
+ return getLibrary().libraryRoot;
3357
+ }
3358
+
3359
+ // src/core/skills/discovery.ts
3360
+ import { existsSync as existsSync13 } from "fs";
3361
+ import { readdir, readFile as readFile7 } from "fs/promises";
3362
+ import { join as join6 } from "path";
3363
+ import matter from "gray-matter";
3364
+ async function parseSkillFile(filePath) {
3365
+ try {
3366
+ const content = await readFile7(filePath, "utf-8");
3367
+ const { data } = matter(content);
3368
+ if (!data.name || !data.description) {
3369
+ return null;
3311
3370
  }
3371
+ const allowedTools = data["allowed-tools"] ?? data.allowedTools;
3372
+ return {
3373
+ name: String(data.name),
3374
+ description: String(data.description),
3375
+ license: data.license ? String(data.license) : void 0,
3376
+ compatibility: data.compatibility ? String(data.compatibility) : void 0,
3377
+ metadata: data.metadata,
3378
+ allowedTools: typeof allowedTools === "string" ? allowedTools.split(/\s+/) : Array.isArray(allowedTools) ? allowedTools.map(String) : void 0,
3379
+ version: data.version ? String(data.version) : void 0
3380
+ };
3381
+ } catch {
3382
+ return null;
3312
3383
  }
3313
- const totalPenalty = findings.reduce(
3314
- (sum, f) => sum + (SEVERITY_WEIGHTS[f.rule.severity] ?? 0),
3315
- 0
3316
- );
3317
- const score = Math.max(0, 100 - totalPenalty);
3318
- const passed = !findings.some((f) => f.rule.severity === "critical" || f.rule.severity === "high");
3319
- return { file: filePath, findings, score, passed };
3320
3384
  }
3321
- async function scanDirectory(dirPath) {
3322
- const { readdir: readdir2 } = await import("fs/promises");
3323
- const { join: join7 } = await import("path");
3324
- if (!existsSync13(dirPath)) return [];
3325
- const entries = await readdir2(dirPath, { withFileTypes: true });
3326
- const results = [];
3385
+ async function discoverSkill(skillDir) {
3386
+ const skillFile = join6(skillDir, "SKILL.md");
3387
+ if (!existsSync13(skillFile)) return null;
3388
+ const metadata = await parseSkillFile(skillFile);
3389
+ if (!metadata) return null;
3390
+ return {
3391
+ name: metadata.name,
3392
+ scopedName: metadata.name,
3393
+ path: skillDir,
3394
+ metadata
3395
+ };
3396
+ }
3397
+ async function discoverSkills(rootDir) {
3398
+ if (!existsSync13(rootDir)) return [];
3399
+ const entries = await readdir(rootDir, { withFileTypes: true });
3400
+ const skills = [];
3327
3401
  for (const entry of entries) {
3328
- if (entry.isDirectory() || entry.isSymbolicLink()) {
3329
- const skillFile = join7(dirPath, entry.name, "SKILL.md");
3330
- if (existsSync13(skillFile)) {
3331
- results.push(await scanFile(skillFile));
3332
- }
3402
+ if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
3403
+ const skillDir = join6(rootDir, entry.name);
3404
+ const skill = await discoverSkill(skillDir);
3405
+ if (skill) {
3406
+ skills.push(skill);
3333
3407
  }
3334
3408
  }
3335
- return results;
3409
+ return skills;
3336
3410
  }
3337
- function toSarif(results) {
3338
- return {
3339
- $schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json",
3340
- version: "2.1.0",
3341
- runs: [
3342
- {
3343
- tool: {
3344
- driver: {
3345
- name: "caamp-audit",
3346
- version: "0.1.0",
3347
- rules: AUDIT_RULES.map((r) => ({
3348
- id: r.id,
3349
- name: r.name,
3350
- shortDescription: { text: r.description },
3351
- defaultConfiguration: {
3352
- level: r.severity === "critical" || r.severity === "high" ? "error" : "warning"
3353
- },
3354
- properties: { category: r.category }
3355
- }))
3356
- }
3357
- },
3358
- results: results.flatMap(
3359
- (result) => result.findings.map((f) => ({
3360
- ruleId: f.rule.id,
3361
- level: f.rule.severity === "critical" || f.rule.severity === "high" ? "error" : "warning",
3362
- message: { text: `${f.rule.description}: ${f.match}` },
3363
- locations: [
3364
- {
3365
- physicalLocation: {
3366
- artifactLocation: { uri: result.file },
3367
- region: {
3368
- startLine: f.line,
3369
- startColumn: f.column
3370
- }
3371
- }
3372
- }
3373
- ]
3374
- }))
3375
- )
3411
+ async function discoverSkillsMulti(dirs) {
3412
+ const all = [];
3413
+ const seen = /* @__PURE__ */ new Set();
3414
+ for (const dir of dirs) {
3415
+ const skills = await discoverSkills(dir);
3416
+ for (const skill of skills) {
3417
+ if (!seen.has(skill.name)) {
3418
+ seen.add(skill.name);
3419
+ all.push(skill);
3376
3420
  }
3377
- ]
3378
- };
3421
+ }
3422
+ }
3423
+ return all;
3379
3424
  }
3380
3425
 
3381
3426
  // src/core/skills/validator.ts
3382
- import { readFile as readFile8 } from "fs/promises";
3383
3427
  import { existsSync as existsSync14 } from "fs";
3428
+ import { readFile as readFile8 } from "fs/promises";
3384
3429
  import matter2 from "gray-matter";
3385
3430
  var RESERVED_NAMES = [
3386
3431
  "anthropic",
@@ -3465,7 +3510,11 @@ async function validateSkill(filePath) {
3465
3510
  }
3466
3511
  }
3467
3512
  if (!data.description) {
3468
- issues.push({ level: "error", field: "description", message: "Missing required field: description" });
3513
+ issues.push({
3514
+ level: "error",
3515
+ field: "description",
3516
+ message: "Missing required field: description"
3517
+ });
3469
3518
  } else {
3470
3519
  const desc = String(data.description);
3471
3520
  if (desc.length > MAX_DESCRIPTION_LENGTH) {
@@ -3514,15 +3563,15 @@ async function validateSkill(filePath) {
3514
3563
  }
3515
3564
 
3516
3565
  export {
3517
- deepMerge,
3518
- getNestedValue,
3519
- ensureDir,
3520
3566
  setVerbose,
3521
3567
  setQuiet,
3522
3568
  isVerbose,
3523
3569
  isQuiet,
3524
3570
  setHuman,
3525
3571
  isHuman,
3572
+ deepMerge,
3573
+ getNestedValue,
3574
+ ensureDir,
3526
3575
  readConfig,
3527
3576
  writeConfig,
3528
3577
  removeConfig,
@@ -3568,8 +3617,25 @@ export {
3568
3617
  reconcileCleoLock,
3569
3618
  parseSource,
3570
3619
  isMarketplaceScoped,
3620
+ scanFile,
3621
+ scanDirectory,
3622
+ toSarif,
3623
+ recordSkillInstall,
3624
+ removeSkillFromLock,
3625
+ getTrackedSkills,
3626
+ checkSkillUpdate,
3627
+ checkAllSkillUpdates,
3571
3628
  formatNetworkError,
3572
3629
  MarketplaceClient,
3630
+ RECOMMENDATION_ERROR_CODES,
3631
+ tokenizeCriteriaValue,
3632
+ validateRecommendationCriteria,
3633
+ normalizeRecommendationCriteria,
3634
+ scoreSkillRecommendation,
3635
+ rankSkills,
3636
+ formatSkillRecommendations,
3637
+ searchSkills,
3638
+ recommendSkills2 as recommendSkills,
3573
3639
  loadLibraryFromModule,
3574
3640
  buildLibraryFromFiles,
3575
3641
  registerSkillLibrary,
@@ -3586,23 +3652,6 @@ export {
3586
3652
  discoverSkill,
3587
3653
  discoverSkills,
3588
3654
  discoverSkillsMulti,
3589
- recordSkillInstall,
3590
- removeSkillFromLock,
3591
- getTrackedSkills,
3592
- checkSkillUpdate,
3593
- checkAllSkillUpdates,
3594
- RECOMMENDATION_ERROR_CODES,
3595
- tokenizeCriteriaValue,
3596
- validateRecommendationCriteria,
3597
- normalizeRecommendationCriteria,
3598
- scoreSkillRecommendation,
3599
- rankSkills,
3600
- formatSkillRecommendations,
3601
- searchSkills,
3602
- recommendSkills2 as recommendSkills,
3603
- scanFile,
3604
- scanDirectory,
3605
- toSarif,
3606
3655
  validateSkill
3607
3656
  };
3608
- //# sourceMappingURL=chunk-ZF4W3K5H.js.map
3657
+ //# sourceMappingURL=chunk-3WKBFXLE.js.map