@wingman-ai/gateway 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. package/README.md +14 -0
  2. package/dist/agent/config/mcpClientManager.cjs +104 -1
  3. package/dist/agent/config/mcpClientManager.d.ts +30 -0
  4. package/dist/agent/config/mcpClientManager.js +104 -1
  5. package/dist/agent/config/modelFactory.cjs +10 -0
  6. package/dist/agent/config/modelFactory.js +10 -0
  7. package/dist/agent/config/xaiImageModel.cjs +242 -0
  8. package/dist/agent/config/xaiImageModel.d.ts +33 -0
  9. package/dist/agent/config/xaiImageModel.js +202 -0
  10. package/dist/agent/tests/mcpClientManager.test.cjs +116 -0
  11. package/dist/agent/tests/mcpClientManager.test.js +117 -1
  12. package/dist/agent/tests/mcpResourceTools.test.cjs +101 -0
  13. package/dist/agent/tests/mcpResourceTools.test.d.ts +1 -0
  14. package/dist/agent/tests/mcpResourceTools.test.js +95 -0
  15. package/dist/agent/tests/modelFactory.test.cjs +16 -2
  16. package/dist/agent/tests/modelFactory.test.js +16 -2
  17. package/dist/agent/tests/xaiImageModel.test.cjs +194 -0
  18. package/dist/agent/tests/xaiImageModel.test.d.ts +1 -0
  19. package/dist/agent/tests/xaiImageModel.test.js +188 -0
  20. package/dist/agent/tools/mcp_resources.cjs +111 -0
  21. package/dist/agent/tools/mcp_resources.d.ts +3 -0
  22. package/dist/agent/tools/mcp_resources.js +77 -0
  23. package/dist/bench/adapters/commandAdapter.cjs +93 -0
  24. package/dist/bench/adapters/commandAdapter.d.ts +6 -0
  25. package/dist/bench/adapters/commandAdapter.js +59 -0
  26. package/dist/bench/adapters/helpers.cjs +170 -0
  27. package/dist/bench/adapters/helpers.d.ts +7 -0
  28. package/dist/bench/adapters/helpers.js +133 -0
  29. package/dist/bench/adapters/index.cjs +41 -0
  30. package/dist/bench/adapters/index.d.ts +2 -0
  31. package/dist/bench/adapters/index.js +7 -0
  32. package/dist/bench/adapters/wingmanCliAdapter.cjs +100 -0
  33. package/dist/bench/adapters/wingmanCliAdapter.d.ts +6 -0
  34. package/dist/bench/adapters/wingmanCliAdapter.js +66 -0
  35. package/dist/bench/cleanup.cjs +122 -0
  36. package/dist/bench/cleanup.d.ts +9 -0
  37. package/dist/bench/cleanup.js +85 -0
  38. package/dist/bench/config.cjs +190 -0
  39. package/dist/bench/config.d.ts +2 -0
  40. package/dist/bench/config.js +156 -0
  41. package/dist/bench/index.cjs +43 -0
  42. package/dist/bench/index.d.ts +3 -0
  43. package/dist/bench/index.js +3 -0
  44. package/dist/bench/official.cjs +616 -0
  45. package/dist/bench/official.d.ts +80 -0
  46. package/dist/bench/official.js +546 -0
  47. package/dist/bench/officialCli.cjs +204 -0
  48. package/dist/bench/officialCli.d.ts +5 -0
  49. package/dist/bench/officialCli.js +170 -0
  50. package/dist/bench/process.cjs +78 -0
  51. package/dist/bench/process.d.ts +14 -0
  52. package/dist/bench/process.js +44 -0
  53. package/dist/bench/runner.cjs +237 -0
  54. package/dist/bench/runner.d.ts +7 -0
  55. package/dist/bench/runner.js +197 -0
  56. package/dist/bench/scoring.cjs +171 -0
  57. package/dist/bench/scoring.d.ts +9 -0
  58. package/dist/bench/scoring.js +137 -0
  59. package/dist/bench/types.cjs +18 -0
  60. package/dist/bench/types.d.ts +200 -0
  61. package/dist/bench/types.js +0 -0
  62. package/dist/bench/validator.cjs +92 -0
  63. package/dist/bench/validator.d.ts +2 -0
  64. package/dist/bench/validator.js +58 -0
  65. package/dist/cli/commands/init.cjs +135 -1
  66. package/dist/cli/commands/init.js +136 -2
  67. package/dist/cli/commands/skill.cjs +7 -3
  68. package/dist/cli/commands/skill.js +7 -3
  69. package/dist/cli/config/loader.cjs +7 -3
  70. package/dist/cli/config/loader.js +7 -3
  71. package/dist/cli/config/schema.cjs +63 -10
  72. package/dist/cli/config/schema.d.ts +64 -4
  73. package/dist/cli/config/schema.js +59 -9
  74. package/dist/cli/config/warnings.cjs +119 -51
  75. package/dist/cli/config/warnings.js +119 -51
  76. package/dist/cli/core/agentInvoker.cjs +58 -13
  77. package/dist/cli/core/agentInvoker.d.ts +1 -0
  78. package/dist/cli/core/agentInvoker.js +58 -13
  79. package/dist/cli/core/imagePersistence.cjs +17 -1
  80. package/dist/cli/core/imagePersistence.d.ts +2 -0
  81. package/dist/cli/core/imagePersistence.js +13 -3
  82. package/dist/cli/core/sessionManager.cjs +2 -0
  83. package/dist/cli/core/sessionManager.js +3 -1
  84. package/dist/cli/services/skillRepository.cjs +155 -69
  85. package/dist/cli/services/skillRepository.d.ts +7 -2
  86. package/dist/cli/services/skillRepository.js +155 -69
  87. package/dist/cli/services/skillService.cjs +93 -26
  88. package/dist/cli/services/skillService.d.ts +7 -0
  89. package/dist/cli/services/skillService.js +96 -29
  90. package/dist/cli/types/skill.d.ts +8 -3
  91. package/dist/cli/types.d.ts +18 -0
  92. package/dist/gateway/adapters/teams.cjs +419 -0
  93. package/dist/gateway/adapters/teams.d.ts +47 -0
  94. package/dist/gateway/adapters/teams.js +361 -0
  95. package/dist/gateway/http/sms.cjs +286 -0
  96. package/dist/gateway/http/sms.d.ts +4 -0
  97. package/dist/gateway/http/sms.js +249 -0
  98. package/dist/gateway/server.cjs +54 -3
  99. package/dist/gateway/server.d.ts +2 -0
  100. package/dist/gateway/server.js +54 -3
  101. package/dist/gateway/sms/commands.cjs +116 -0
  102. package/dist/gateway/sms/commands.d.ts +15 -0
  103. package/dist/gateway/sms/commands.js +79 -0
  104. package/dist/gateway/sms/control.cjs +118 -0
  105. package/dist/gateway/sms/control.d.ts +18 -0
  106. package/dist/gateway/sms/control.js +84 -0
  107. package/dist/gateway/sms/policyStore.cjs +198 -0
  108. package/dist/gateway/sms/policyStore.d.ts +37 -0
  109. package/dist/gateway/sms/policyStore.js +161 -0
  110. package/dist/providers/registry.cjs +1 -0
  111. package/dist/providers/registry.js +1 -0
  112. package/dist/skills/activation.cjs +92 -0
  113. package/dist/skills/activation.d.ts +12 -0
  114. package/dist/skills/activation.js +58 -0
  115. package/dist/skills/bin-requirements.cjs +63 -0
  116. package/dist/skills/bin-requirements.d.ts +3 -0
  117. package/dist/skills/bin-requirements.js +26 -0
  118. package/dist/skills/metadata.cjs +141 -0
  119. package/dist/skills/metadata.d.ts +29 -0
  120. package/dist/skills/metadata.js +104 -0
  121. package/dist/skills/overlay.cjs +75 -0
  122. package/dist/skills/overlay.d.ts +2 -0
  123. package/dist/skills/overlay.js +38 -0
  124. package/dist/tests/cli-config-loader.test.cjs +7 -3
  125. package/dist/tests/cli-config-loader.test.js +7 -3
  126. package/dist/tests/cli-config-warnings.test.cjs +41 -0
  127. package/dist/tests/cli-config-warnings.test.js +41 -0
  128. package/dist/tests/cli-init.test.cjs +86 -26
  129. package/dist/tests/cli-init.test.js +86 -26
  130. package/dist/tests/config-json-schema.test.cjs +12 -0
  131. package/dist/tests/config-json-schema.test.js +12 -0
  132. package/dist/tests/gateway-http-security.test.cjs +21 -0
  133. package/dist/tests/gateway-http-security.test.js +21 -0
  134. package/dist/tests/gateway-origin-policy.test.cjs +22 -0
  135. package/dist/tests/gateway-origin-policy.test.js +22 -0
  136. package/dist/tests/gateway.test.cjs +57 -0
  137. package/dist/tests/gateway.test.js +57 -0
  138. package/dist/tests/imagePersistence.test.cjs +26 -0
  139. package/dist/tests/imagePersistence.test.js +27 -1
  140. package/dist/tests/run-terminal-bench-official-script.test.cjs +61 -0
  141. package/dist/tests/run-terminal-bench-official-script.test.d.ts +1 -0
  142. package/dist/tests/run-terminal-bench-official-script.test.js +55 -0
  143. package/dist/tests/sessions-api.test.cjs +69 -1
  144. package/dist/tests/sessions-api.test.js +70 -2
  145. package/dist/tests/skill-activation.test.cjs +86 -0
  146. package/dist/tests/skill-activation.test.d.ts +1 -0
  147. package/dist/tests/skill-activation.test.js +80 -0
  148. package/dist/tests/skill-metadata.test.cjs +119 -0
  149. package/dist/tests/skill-metadata.test.d.ts +1 -0
  150. package/dist/tests/skill-metadata.test.js +113 -0
  151. package/dist/tests/skill-repository.test.cjs +363 -0
  152. package/dist/tests/skill-repository.test.js +363 -0
  153. package/dist/tests/sms-api.test.cjs +183 -0
  154. package/dist/tests/sms-api.test.d.ts +1 -0
  155. package/dist/tests/sms-api.test.js +177 -0
  156. package/dist/tests/sms-commands.test.cjs +90 -0
  157. package/dist/tests/sms-commands.test.d.ts +1 -0
  158. package/dist/tests/sms-commands.test.js +84 -0
  159. package/dist/tests/sms-policy-store.test.cjs +69 -0
  160. package/dist/tests/sms-policy-store.test.d.ts +1 -0
  161. package/dist/tests/sms-policy-store.test.js +63 -0
  162. package/dist/tests/teams-adapter.test.cjs +58 -0
  163. package/dist/tests/teams-adapter.test.d.ts +1 -0
  164. package/dist/tests/teams-adapter.test.js +52 -0
  165. package/dist/tests/terminal-bench-adapters-helpers.test.cjs +64 -0
  166. package/dist/tests/terminal-bench-adapters-helpers.test.d.ts +1 -0
  167. package/dist/tests/terminal-bench-adapters-helpers.test.js +58 -0
  168. package/dist/tests/terminal-bench-cleanup.test.cjs +93 -0
  169. package/dist/tests/terminal-bench-cleanup.test.d.ts +1 -0
  170. package/dist/tests/terminal-bench-cleanup.test.js +87 -0
  171. package/dist/tests/terminal-bench-config.test.cjs +62 -0
  172. package/dist/tests/terminal-bench-config.test.d.ts +1 -0
  173. package/dist/tests/terminal-bench-config.test.js +56 -0
  174. package/dist/tests/terminal-bench-official.test.cjs +194 -0
  175. package/dist/tests/terminal-bench-official.test.d.ts +1 -0
  176. package/dist/tests/terminal-bench-official.test.js +188 -0
  177. package/dist/tests/terminal-bench-runner.test.cjs +82 -0
  178. package/dist/tests/terminal-bench-runner.test.d.ts +1 -0
  179. package/dist/tests/terminal-bench-runner.test.js +76 -0
  180. package/dist/tests/terminal-bench-scoring.test.cjs +128 -0
  181. package/dist/tests/terminal-bench-scoring.test.d.ts +1 -0
  182. package/dist/tests/terminal-bench-scoring.test.js +122 -0
  183. package/dist/tools/mcp-fal-ai.cjs +1 -1
  184. package/dist/tools/mcp-fal-ai.js +1 -1
  185. package/dist/webui/assets/index-Cyg_Hs57.css +11 -0
  186. package/dist/webui/assets/{index-BMekSELC.js → index-DZXLLjaA.js} +109 -109
  187. package/dist/webui/index.html +2 -2
  188. package/package.json +14 -5
  189. package/skills/gog/SKILL.md +1 -1
  190. package/skills/weather/SKILL.md +1 -1
  191. package/templates/agents/game-dev/agent.md +122 -63
  192. package/templates/agents/game-dev/art-director.md +106 -0
  193. package/templates/agents/game-dev/game-designer.md +87 -0
  194. package/templates/agents/game-dev/scene-engineer.md +474 -0
  195. package/dist/webui/assets/index-Cwkg4DKj.css +0 -11
  196. package/skills/ui-registry/SKILL.md +0 -35
  197. package/templates/agents/game-dev/art-generation.md +0 -38
  198. package/templates/agents/game-dev/asset-refinement.md +0 -17
  199. package/templates/agents/game-dev/planning-idea.md +0 -17
  200. package/templates/agents/game-dev/ui-specialist.md +0 -17
@@ -24,6 +24,8 @@ var __webpack_require__ = {};
24
24
  var __webpack_exports__ = {};
25
25
  __webpack_require__.r(__webpack_exports__);
26
26
  __webpack_require__.d(__webpack_exports__, {
27
+ removeSessionMediaDirectory: ()=>removeSessionMediaDirectory,
28
+ getSessionMediaDirectory: ()=>getSessionMediaDirectory,
27
29
  persistAssistantImagesToDisk: ()=>persistAssistantImagesToDisk,
28
30
  parseBase64DataUrl: ()=>parseBase64DataUrl,
29
31
  resolveImageExtension: ()=>resolveImageExtension
@@ -34,7 +36,7 @@ const external_node_path_namespaceObject = require("node:path");
34
36
  const DATA_URL_BASE64_PATTERN = /^data:([^;,]+);base64,(.+)$/i;
35
37
  function persistAssistantImagesToDisk(input) {
36
38
  if (!input.messages.length) return;
37
- const mediaRoot = (0, external_node_path_namespaceObject.join)((0, external_node_path_namespaceObject.dirname)(input.dbPath), "media", sanitizePathSegment(input.sessionId));
39
+ const mediaRoot = getSessionMediaDirectory(input.dbPath, input.sessionId);
38
40
  for (const message of input.messages)if ("assistant" === message.role) {
39
41
  if (Array.isArray(message.attachments) && 0 !== message.attachments.length) for (const attachment of message.attachments){
40
42
  if (!attachment || "image" !== attachment.kind) continue;
@@ -66,6 +68,16 @@ function persistAssistantImagesToDisk(input) {
66
68
  }
67
69
  }
68
70
  }
71
+ function getSessionMediaDirectory(dbPath, sessionId) {
72
+ return (0, external_node_path_namespaceObject.join)((0, external_node_path_namespaceObject.dirname)(dbPath), "media", sanitizePathSegment(sessionId));
73
+ }
74
+ function removeSessionMediaDirectory(dbPath, sessionId) {
75
+ const mediaDir = getSessionMediaDirectory(dbPath, sessionId);
76
+ (0, external_node_fs_namespaceObject.rmSync)(mediaDir, {
77
+ recursive: true,
78
+ force: true
79
+ });
80
+ }
69
81
  function parseBase64DataUrl(dataUrl) {
70
82
  if ("string" != typeof dataUrl) return null;
71
83
  const match = dataUrl.match(DATA_URL_BASE64_PATTERN);
@@ -112,12 +124,16 @@ function sanitizePathSegment(value) {
112
124
  const sanitized = normalized.replace(/[^a-zA-Z0-9._-]/g, "_");
113
125
  return sanitized.slice(0, 120) || "default-session";
114
126
  }
127
+ exports.getSessionMediaDirectory = __webpack_exports__.getSessionMediaDirectory;
115
128
  exports.parseBase64DataUrl = __webpack_exports__.parseBase64DataUrl;
116
129
  exports.persistAssistantImagesToDisk = __webpack_exports__.persistAssistantImagesToDisk;
130
+ exports.removeSessionMediaDirectory = __webpack_exports__.removeSessionMediaDirectory;
117
131
  exports.resolveImageExtension = __webpack_exports__.resolveImageExtension;
118
132
  for(var __rspack_i in __webpack_exports__)if (-1 === [
133
+ "getSessionMediaDirectory",
119
134
  "parseBase64DataUrl",
120
135
  "persistAssistantImagesToDisk",
136
+ "removeSessionMediaDirectory",
121
137
  "resolveImageExtension"
122
138
  ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
123
139
  Object.defineProperty(exports, '__esModule', {
@@ -19,6 +19,8 @@ export declare function persistAssistantImagesToDisk(input: {
19
19
  sessionId: string;
20
20
  messages: PersistableMessage[];
21
21
  }): void;
22
+ export declare function getSessionMediaDirectory(dbPath: string, sessionId: string): string;
23
+ export declare function removeSessionMediaDirectory(dbPath: string, sessionId: string): void;
22
24
  export declare function parseBase64DataUrl(dataUrl: string): ParsedDataUrl | null;
23
25
  export declare function resolveImageExtension(mimeType: string): string;
24
26
  export {};
@@ -1,10 +1,10 @@
1
1
  import { createHash } from "node:crypto";
2
- import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
+ import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
3
3
  import { dirname, join } from "node:path";
4
4
  const DATA_URL_BASE64_PATTERN = /^data:([^;,]+);base64,(.+)$/i;
5
5
  function persistAssistantImagesToDisk(input) {
6
6
  if (!input.messages.length) return;
7
- const mediaRoot = join(dirname(input.dbPath), "media", sanitizePathSegment(input.sessionId));
7
+ const mediaRoot = getSessionMediaDirectory(input.dbPath, input.sessionId);
8
8
  for (const message of input.messages)if ("assistant" === message.role) {
9
9
  if (Array.isArray(message.attachments) && 0 !== message.attachments.length) for (const attachment of message.attachments){
10
10
  if (!attachment || "image" !== attachment.kind) continue;
@@ -36,6 +36,16 @@ function persistAssistantImagesToDisk(input) {
36
36
  }
37
37
  }
38
38
  }
39
+ function getSessionMediaDirectory(dbPath, sessionId) {
40
+ return join(dirname(dbPath), "media", sanitizePathSegment(sessionId));
41
+ }
42
+ function removeSessionMediaDirectory(dbPath, sessionId) {
43
+ const mediaDir = getSessionMediaDirectory(dbPath, sessionId);
44
+ rmSync(mediaDir, {
45
+ recursive: true,
46
+ force: true
47
+ });
48
+ }
39
49
  function parseBase64DataUrl(dataUrl) {
40
50
  if ("string" != typeof dataUrl) return null;
41
51
  const match = dataUrl.match(DATA_URL_BASE64_PATTERN);
@@ -82,4 +92,4 @@ function sanitizePathSegment(value) {
82
92
  const sanitized = normalized.replace(/[^a-zA-Z0-9._-]/g, "_");
83
93
  return sanitized.slice(0, 120) || "default-session";
84
94
  }
85
- export { parseBase64DataUrl, persistAssistantImagesToDisk, resolveImageExtension };
95
+ export { getSessionMediaDirectory, parseBase64DataUrl, persistAssistantImagesToDisk, removeSessionMediaDirectory, resolveImageExtension };
@@ -227,6 +227,7 @@ class SessionManager {
227
227
  writesStmt.run(sessionId);
228
228
  const pendingStmt = this.db.prepare("DELETE FROM session_pending_messages WHERE session_id = ?");
229
229
  pendingStmt.run(sessionId);
230
+ (0, external_imagePersistence_cjs_namespaceObject.removeSessionMediaDirectory)(this.dbPath, sessionId);
230
231
  }
231
232
  clearSessionMessages(sessionId) {
232
233
  if (!this.db || !this.checkpointer) throw new Error("SessionManager not initialized");
@@ -242,6 +243,7 @@ class SessionManager {
242
243
  WHERE id = ?
243
244
  `);
244
245
  sessionStmt.run(Date.now(), sessionId);
246
+ (0, external_imagePersistence_cjs_namespaceObject.removeSessionMediaDirectory)(this.dbPath, sessionId);
245
247
  }
246
248
  persistPendingMessage(input) {
247
249
  if (!this.db) throw new Error("SessionManager not initialized");
@@ -1,7 +1,7 @@
1
1
  import { SqliteSaver } from "@langchain/langgraph-checkpoint-sqlite";
2
2
  import { createDeepAgent } from "deepagents";
3
3
  import { v4 } from "uuid";
4
- import { persistAssistantImagesToDisk } from "./imagePersistence.js";
4
+ import { persistAssistantImagesToDisk, removeSessionMediaDirectory } from "./imagePersistence.js";
5
5
  function _define_property(obj, key, value) {
6
6
  if (key in obj) Object.defineProperty(obj, key, {
7
7
  value: value,
@@ -194,6 +194,7 @@ class SessionManager {
194
194
  writesStmt.run(sessionId);
195
195
  const pendingStmt = this.db.prepare("DELETE FROM session_pending_messages WHERE session_id = ?");
196
196
  pendingStmt.run(sessionId);
197
+ removeSessionMediaDirectory(this.dbPath, sessionId);
197
198
  }
198
199
  clearSessionMessages(sessionId) {
199
200
  if (!this.db || !this.checkpointer) throw new Error("SessionManager not initialized");
@@ -209,6 +210,7 @@ class SessionManager {
209
210
  WHERE id = ?
210
211
  `);
211
212
  sessionStmt.run(Date.now(), sessionId);
213
+ removeSessionMediaDirectory(this.dbPath, sessionId);
212
214
  }
213
215
  persistPendingMessage(input) {
214
216
  if (!this.db) throw new Error("SessionManager not initialized");
@@ -27,6 +27,7 @@ __webpack_require__.d(__webpack_exports__, {
27
27
  SkillRepository: ()=>SkillRepository
28
28
  });
29
29
  const external_logger_cjs_namespaceObject = require("../../logger.cjs");
30
+ const metadata_cjs_namespaceObject = require("../../skills/metadata.cjs");
30
31
  function _define_property(obj, key, value) {
31
32
  if (key in obj) Object.defineProperty(obj, key, {
32
33
  value: value,
@@ -63,7 +64,8 @@ class SkillRepository {
63
64
  async listAvailableSkills() {
64
65
  try {
65
66
  if ("clawhub" === this.provider) return await this.listSkillsFromClawhub();
66
- return await this.listSkillsFromGitHub();
67
+ if ("github" === this.provider) return await this.listSkillsFromGitHub();
68
+ return await this.listSkillsFromHybrid();
67
69
  } catch (error) {
68
70
  if (error instanceof Error) throw new Error(`Failed to list skills: ${error.message}`);
69
71
  throw error;
@@ -85,19 +87,39 @@ class SkillRepository {
85
87
  return Buffer.from(arrayBuffer);
86
88
  }
87
89
  async getSkillMetadata(skillName) {
88
- if ("clawhub" === this.provider) return await this.getClawhubSkillMetadata(skillName);
89
90
  try {
90
- const skillMdPath = `/repos/${this.owner}/${this.repo}/contents/skills/${skillName}/SKILL.md`;
91
- const skillMd = await this.fetchGitHub(skillMdPath);
92
- if ("file" !== skillMd.type || !skillMd.content) throw new Error("SKILL.md not found or invalid");
93
- const content = Buffer.from(skillMd.content, "base64").toString("utf-8");
94
- const metadata = this.parseSkillMetadata(content);
95
- return metadata;
91
+ if ("clawhub" === this.provider) return await this.getClawhubSkillMetadata(skillName);
92
+ if ("github" === this.provider) {
93
+ const repository = await this.resolveGitHubRepositoryForSkill(skillName);
94
+ return await this.getGitHubSkillMetadata(skillName, repository);
95
+ }
96
+ return await this.getHybridSkillMetadata(skillName);
96
97
  } catch (error) {
97
98
  if (error instanceof Error) throw new Error(`Failed to fetch skill metadata for ${skillName}: ${error.message}`);
98
99
  throw error;
99
100
  }
100
101
  }
102
+ async getGitHubSkillMetadata(skillName, repository) {
103
+ const skillMdPath = `/repos/${repository.owner}/${repository.name}/contents/skills/${skillName}/SKILL.md`;
104
+ const skillMd = await this.fetchGitHub(skillMdPath);
105
+ if ("file" !== skillMd.type || !skillMd.content) throw new Error(`SKILL.md not found or invalid in ${repository.owner}/${repository.name}`);
106
+ const content = Buffer.from(skillMd.content, "base64").toString("utf-8");
107
+ return this.parseSkillMetadata(content);
108
+ }
109
+ async resolveGitHubRepositoryForSkill(skillName) {
110
+ const repositories = this.getGitHubRepositories();
111
+ for(let index = repositories.length - 1; index >= 0; index -= 1){
112
+ const repository = repositories[index];
113
+ try {
114
+ await this.fetchGitHub(`/repos/${repository.owner}/${repository.name}/contents/skills/${skillName}/SKILL.md`);
115
+ return repository;
116
+ } catch (error) {
117
+ if (error instanceof Error && error.message.includes("Resource not found")) continue;
118
+ throw error;
119
+ }
120
+ }
121
+ throw new Error(`Skill '${skillName}' not found in configured GitHub repositories: ${repositories.map((repository)=>`${repository.owner}/${repository.name}`).join(", ")}`);
122
+ }
101
123
  async getClawhubSkillMetadata(skillName) {
102
124
  try {
103
125
  const detail = await this.fetchJson(`${this.clawhubBaseUrl}/api/v1/skills/${encodeURIComponent(skillName)}`, {
@@ -127,51 +149,50 @@ class SkillRepository {
127
149
  throw error;
128
150
  }
129
151
  }
152
+ async getHybridSkillMetadata(skillName) {
153
+ let githubError;
154
+ try {
155
+ const repository = await this.resolveGitHubRepositoryForSkill(skillName);
156
+ return await this.getGitHubSkillMetadata(skillName, repository);
157
+ } catch (error) {
158
+ githubError = error;
159
+ logger.debug(`Falling back to ClawHub metadata lookup for '${skillName}' after GitHub error: ${error instanceof Error ? error.message : String(error)}`);
160
+ }
161
+ try {
162
+ return await this.getClawhubSkillMetadata(skillName);
163
+ } catch (clawhubError) {
164
+ throw new Error(`GitHub error: ${githubError instanceof Error ? githubError.message : String(githubError)}; ClawHub error: ${clawhubError instanceof Error ? clawhubError.message : String(clawhubError)}`);
165
+ }
166
+ }
130
167
  parseSkillMetadata(content) {
131
- const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---/;
132
- const match = content.match(frontmatterRegex);
133
- if (!match) throw new Error("Invalid SKILL.md format: missing YAML frontmatter");
134
- const frontmatter = match[1];
135
- const metadata = {
136
- name: "",
137
- description: ""
168
+ const parsed = (0, metadata_cjs_namespaceObject.parseSkillFrontmatter)(content);
169
+ return {
170
+ name: parsed.name,
171
+ description: parsed.description,
172
+ ...parsed.license ? {
173
+ license: parsed.license
174
+ } : {},
175
+ ...parsed.compatibility ? {
176
+ compatibility: parsed.compatibility
177
+ } : {},
178
+ ...parsed.allowedTools.length > 0 ? {
179
+ allowedTools: parsed.allowedTools
180
+ } : {},
181
+ ...parsed.metadata ? {
182
+ metadata: parsed.metadata
183
+ } : {}
138
184
  };
139
- const lines = frontmatter.split("\n");
140
- for (const line of lines){
141
- const colonIndex = line.indexOf(":");
142
- if (-1 === colonIndex) continue;
143
- const key = line.substring(0, colonIndex).trim();
144
- const value = line.substring(colonIndex + 1).trim();
145
- switch(key){
146
- case "name":
147
- metadata.name = value;
148
- break;
149
- case "description":
150
- metadata.description = value;
151
- break;
152
- case "license":
153
- metadata.license = value;
154
- break;
155
- case "compatibility":
156
- metadata.compatibility = value;
157
- break;
158
- case "allowed-tools":
159
- metadata.allowedTools = value;
160
- break;
161
- }
162
- }
163
- if (!metadata.name) throw new Error("Invalid SKILL.md: missing required field 'name'");
164
- if (!metadata.description) throw new Error("Invalid SKILL.md: missing required field 'description'");
165
- const nameRegex = /^[a-z0-9]+(-[a-z0-9]+)*$/;
166
- if (!nameRegex.test(metadata.name)) throw new Error(`Invalid skill name '${metadata.name}': must be lowercase alphanumeric with hyphens only`);
167
- return metadata;
168
185
  }
169
186
  async downloadSkill(skillName) {
170
- if ("clawhub" === this.provider) return await this.downloadSkillFromClawhub(skillName);
171
187
  try {
172
- const files = new Map();
173
- await this.downloadDirectory(`skills/${skillName}`, files, skillName);
174
- return files;
188
+ if ("clawhub" === this.provider) return await this.downloadSkillFromClawhub(skillName);
189
+ if ("github" === this.provider) {
190
+ const repository = await this.resolveGitHubRepositoryForSkill(skillName);
191
+ const files = new Map();
192
+ await this.downloadDirectory(`skills/${skillName}`, files, skillName, repository);
193
+ return files;
194
+ }
195
+ return await this.downloadHybridSkill(skillName);
175
196
  } catch (error) {
176
197
  if (error instanceof Error) throw new Error(`Failed to download skill ${skillName}: ${error.message}`);
177
198
  throw error;
@@ -214,8 +235,25 @@ class SkillRepository {
214
235
  throw error;
215
236
  }
216
237
  }
217
- async downloadDirectory(path, files, skillName) {
218
- const contents = await this.fetchGitHub(`/repos/${this.owner}/${this.repo}/contents/${path}`);
238
+ async downloadHybridSkill(skillName) {
239
+ let githubError;
240
+ try {
241
+ const repository = await this.resolveGitHubRepositoryForSkill(skillName);
242
+ const files = new Map();
243
+ await this.downloadDirectory(`skills/${skillName}`, files, skillName, repository);
244
+ return files;
245
+ } catch (error) {
246
+ githubError = error;
247
+ logger.debug(`Falling back to ClawHub download for '${skillName}' after GitHub error: ${error instanceof Error ? error.message : String(error)}`);
248
+ }
249
+ try {
250
+ return await this.downloadSkillFromClawhub(skillName);
251
+ } catch (clawhubError) {
252
+ throw new Error(`GitHub error: ${githubError instanceof Error ? githubError.message : String(githubError)}; ClawHub error: ${clawhubError instanceof Error ? clawhubError.message : String(clawhubError)}`);
253
+ }
254
+ }
255
+ async downloadDirectory(path, files, skillName, repository) {
256
+ const contents = await this.fetchGitHub(`/repos/${repository.owner}/${repository.name}/contents/${path}`);
219
257
  for (const item of contents)if ("file" === item.type) if (item.content) {
220
258
  const content = Buffer.from(item.content, "base64");
221
259
  const relativePath = item.path.replace(`skills/${skillName}/`, "");
@@ -228,7 +266,7 @@ class SkillRepository {
228
266
  files.set(relativePath, content);
229
267
  }
230
268
  }
231
- else if ("dir" === item.type) await this.downloadDirectory(item.path, files, skillName);
269
+ else if ("dir" === item.type) await this.downloadDirectory(item.path, files, skillName, repository);
232
270
  }
233
271
  async listSkillsFromClawhub() {
234
272
  const allSkills = [];
@@ -257,32 +295,80 @@ class SkillRepository {
257
295
  }while (cursor);
258
296
  return allSkills;
259
297
  }
260
- async listSkillsFromGitHub() {
261
- const contents = await this.fetchGitHub(`/repos/${this.owner}/${this.repo}/contents/skills`);
262
- const skills = [];
263
- for (const item of contents)if ("dir" === item.type) try {
264
- const metadata = await this.getSkillMetadata(item.name);
265
- skills.push({
266
- name: item.name,
267
- description: metadata.description || "No description",
268
- path: item.path,
269
- metadata
270
- });
298
+ async listSkillsFromHybrid() {
299
+ let clawhubSkills = [];
300
+ let githubSkills = [];
301
+ let clawhubError;
302
+ let githubError;
303
+ try {
304
+ clawhubSkills = await this.listSkillsFromClawhub();
271
305
  } catch (error) {
272
- logger.warn(`Could not read skill ${item.name}`, error instanceof Error ? error.message : String(error));
306
+ clawhubError = error;
307
+ logger.warn(`Failed to list ClawHub skills in hybrid mode: ${error instanceof Error ? error.message : String(error)}`);
273
308
  }
274
- return skills;
309
+ try {
310
+ githubSkills = await this.listSkillsFromGitHub();
311
+ } catch (error) {
312
+ githubError = error;
313
+ logger.warn(`Failed to list GitHub skills in hybrid mode: ${error instanceof Error ? error.message : String(error)}`);
314
+ }
315
+ if (0 === clawhubSkills.length && 0 === githubSkills.length) throw new Error(`No skill sources available. ClawHub error: ${clawhubError instanceof Error ? clawhubError.message : "none"}; GitHub error: ${githubError instanceof Error ? githubError.message : "none"}`);
316
+ const mergedSkills = new Map();
317
+ for (const skill of clawhubSkills)mergedSkills.set(skill.name, skill);
318
+ for (const skill of githubSkills){
319
+ if (mergedSkills.has(skill.name)) mergedSkills.delete(skill.name);
320
+ mergedSkills.set(skill.name, skill);
321
+ }
322
+ return [
323
+ ...mergedSkills.values()
324
+ ];
325
+ }
326
+ async listSkillsFromGitHub() {
327
+ const mergedSkills = new Map();
328
+ for (const repository of this.getGitHubRepositories()){
329
+ const contents = await this.fetchGitHub(`/repos/${repository.owner}/${repository.name}/contents/skills`);
330
+ for (const item of contents)if ("dir" === item.type) try {
331
+ const metadata = await this.getGitHubSkillMetadata(item.name, repository);
332
+ if (mergedSkills.has(item.name)) mergedSkills.delete(item.name);
333
+ mergedSkills.set(item.name, {
334
+ name: item.name,
335
+ description: metadata.description || "No description",
336
+ path: item.path,
337
+ metadata
338
+ });
339
+ } catch (error) {
340
+ logger.warn(`Could not read skill ${item.name} from ${repository.owner}/${repository.name}`, error instanceof Error ? error.message : String(error));
341
+ }
342
+ }
343
+ return [
344
+ ...mergedSkills.values()
345
+ ];
346
+ }
347
+ getGitHubRepositories() {
348
+ if (this.repositories.length > 0) return this.repositories;
349
+ throw new Error("No GitHub skill repositories configured. Set skills.repositories or the legacy skills.repositoryOwner + skills.repositoryName fields.");
275
350
  }
276
351
  constructor(options = {}){
277
352
  _define_property(this, "githubBaseUrl", "https://api.github.com");
278
- _define_property(this, "owner", void 0);
279
- _define_property(this, "repo", void 0);
353
+ _define_property(this, "repositories", void 0);
280
354
  _define_property(this, "token", void 0);
281
355
  _define_property(this, "provider", void 0);
282
356
  _define_property(this, "clawhubBaseUrl", void 0);
283
- this.provider = options.provider || "github";
284
- this.owner = options.repositoryOwner || "anthropics";
285
- this.repo = options.repositoryName || "skills";
357
+ this.provider = options.provider || "hybrid";
358
+ const normalizedRepositories = (options.repositories || []).map((repository)=>({
359
+ owner: repository.owner.trim(),
360
+ name: repository.name.trim()
361
+ })).filter((repository)=>repository.owner && repository.name);
362
+ const legacyOwner = options.repositoryOwner?.trim();
363
+ const legacyName = options.repositoryName?.trim();
364
+ if (normalizedRepositories.length > 0) this.repositories = normalizedRepositories;
365
+ else if (legacyOwner && legacyName) this.repositories = [
366
+ {
367
+ owner: legacyOwner,
368
+ name: legacyName
369
+ }
370
+ ];
371
+ else this.repositories = [];
286
372
  this.token = options.githubToken || process.env.GITHUB_TOKEN || void 0;
287
373
  this.clawhubBaseUrl = (options.clawhubBaseUrl || "https://clawhub.ai").replace(/\/+$/, "");
288
374
  }
@@ -4,8 +4,7 @@ import type { SkillInfo, SkillMetadata, SkillRepositoryOptions } from "../types/
4
4
  */
5
5
  export declare class SkillRepository {
6
6
  private readonly githubBaseUrl;
7
- private readonly owner;
8
- private readonly repo;
7
+ private readonly repositories;
9
8
  private readonly token?;
10
9
  private readonly provider;
11
10
  private readonly clawhubBaseUrl;
@@ -24,7 +23,10 @@ export declare class SkillRepository {
24
23
  * Get skill metadata by fetching and parsing SKILL.md
25
24
  */
26
25
  getSkillMetadata(skillName: string): Promise<SkillMetadata>;
26
+ private getGitHubSkillMetadata;
27
+ private resolveGitHubRepositoryForSkill;
27
28
  private getClawhubSkillMetadata;
29
+ private getHybridSkillMetadata;
28
30
  /**
29
31
  * Parse SKILL.md content to extract YAML frontmatter
30
32
  */
@@ -34,10 +36,13 @@ export declare class SkillRepository {
34
36
  */
35
37
  downloadSkill(skillName: string): Promise<Map<string, string | Buffer>>;
36
38
  private downloadSkillFromClawhub;
39
+ private downloadHybridSkill;
37
40
  /**
38
41
  * Recursively download all files in a directory
39
42
  */
40
43
  private downloadDirectory;
41
44
  private listSkillsFromClawhub;
45
+ private listSkillsFromHybrid;
42
46
  private listSkillsFromGitHub;
47
+ private getGitHubRepositories;
43
48
  }