apteva 0.2.11 → 0.3.6

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.
@@ -0,0 +1,318 @@
1
+ // Skills Integration Provider
2
+ // Fetches from public GitHub repositories
3
+
4
+ export interface MarketplaceSkill {
5
+ id: string;
6
+ name: string;
7
+ description: string;
8
+ content: string; // Full SKILL.md content
9
+ author: string;
10
+ version: string;
11
+ license: string | null;
12
+ compatibility: string | null;
13
+ tags: string[];
14
+ downloads: number;
15
+ rating: number;
16
+ repository: string | null;
17
+ created_at: string;
18
+ updated_at: string;
19
+ }
20
+
21
+ export interface SkillsSearchResult {
22
+ skills: MarketplaceSkill[];
23
+ total: number;
24
+ page: number;
25
+ per_page: number;
26
+ }
27
+
28
+ // GitHub repo sources
29
+ // Add more repositories here as the skills ecosystem grows
30
+ // Each repo should have folders containing SKILL.md files with YAML frontmatter
31
+ const GITHUB_REPOS = [
32
+ {
33
+ owner: "anthropics",
34
+ repo: "skills",
35
+ path: "skills",
36
+ author: "Anthropic",
37
+ },
38
+ // Community repos can be added here, e.g.:
39
+ // { owner: "some-org", repo: "claude-skills", path: "skills", author: "Community" },
40
+ ];
41
+
42
+ // Cache for fetched skills (TTL: 5 minutes)
43
+ let skillsCache: { skills: MarketplaceSkill[]; fetchedAt: number } | null = null;
44
+ const CACHE_TTL = 5 * 60 * 1000;
45
+
46
+ // Fetch all skills from GitHub repos
47
+ async function fetchAllSkills(): Promise<MarketplaceSkill[]> {
48
+ // Check cache
49
+ if (skillsCache && Date.now() - skillsCache.fetchedAt < CACHE_TTL) {
50
+ return skillsCache.skills;
51
+ }
52
+
53
+ const allSkills: MarketplaceSkill[] = [];
54
+
55
+ for (const repo of GITHUB_REPOS) {
56
+ try {
57
+ // Fetch directory listing
58
+ const listRes = await fetch(
59
+ `https://api.github.com/repos/${repo.owner}/${repo.repo}/contents/${repo.path}`,
60
+ {
61
+ headers: {
62
+ Accept: "application/vnd.github.v3+json",
63
+ "User-Agent": "Apteva-Skills-Fetcher",
64
+ },
65
+ }
66
+ );
67
+
68
+ if (!listRes.ok) {
69
+ console.error(`Failed to fetch ${repo.owner}/${repo.repo}: ${listRes.status}`);
70
+ continue;
71
+ }
72
+
73
+ const items = (await listRes.json()) as Array<{
74
+ name: string;
75
+ type: string;
76
+ path: string;
77
+ }>;
78
+
79
+ // Filter directories only
80
+ const skillDirs = items.filter((item) => item.type === "dir");
81
+
82
+ // Fetch each skill's SKILL.md
83
+ const skillPromises = skillDirs.map(async (dir) => {
84
+ try {
85
+ const skillMdRes = await fetch(
86
+ `https://raw.githubusercontent.com/${repo.owner}/${repo.repo}/main/${dir.path}/SKILL.md`,
87
+ {
88
+ headers: { "User-Agent": "Apteva-Skills-Fetcher" },
89
+ }
90
+ );
91
+
92
+ if (!skillMdRes.ok) {
93
+ return null;
94
+ }
95
+
96
+ const content = await skillMdRes.text();
97
+ const parsed = parseSkillMd(content);
98
+
99
+ if (!parsed) {
100
+ return null;
101
+ }
102
+
103
+ return {
104
+ id: `${repo.owner}-${dir.name}`,
105
+ name: parsed.name,
106
+ description: parsed.description,
107
+ content,
108
+ author: repo.author,
109
+ version: parsed.metadata?.version || "1.0.0",
110
+ license: parsed.license || "MIT",
111
+ compatibility: parsed.compatibility || null,
112
+ tags: inferTags(dir.name, parsed.description),
113
+ downloads: 0, // Not available from GitHub
114
+ rating: 4.5, // Default rating
115
+ repository: `https://github.com/${repo.owner}/${repo.repo}/tree/main/${dir.path}`,
116
+ created_at: new Date().toISOString(),
117
+ updated_at: new Date().toISOString(),
118
+ } as MarketplaceSkill;
119
+ } catch (e) {
120
+ console.error(`Failed to fetch skill ${dir.name}:`, e);
121
+ return null;
122
+ }
123
+ });
124
+
125
+ const skills = (await Promise.all(skillPromises)).filter(
126
+ (s): s is MarketplaceSkill => s !== null
127
+ );
128
+ allSkills.push(...skills);
129
+ } catch (e) {
130
+ console.error(`Failed to fetch from ${repo.owner}/${repo.repo}:`, e);
131
+ }
132
+ }
133
+
134
+ // Update cache
135
+ skillsCache = { skills: allSkills, fetchedAt: Date.now() };
136
+
137
+ return allSkills;
138
+ }
139
+
140
+ // Infer tags from skill name and description
141
+ function inferTags(name: string, description: string): string[] {
142
+ const tags: string[] = [];
143
+ const text = `${name} ${description}`.toLowerCase();
144
+
145
+ const tagKeywords: Record<string, string[]> = {
146
+ pdf: ["pdf"],
147
+ document: ["doc", "docx", "document", "word"],
148
+ spreadsheet: ["xlsx", "excel", "spreadsheet"],
149
+ presentation: ["pptx", "powerpoint", "slides", "presentation"],
150
+ design: ["design", "ui", "frontend", "canvas", "art"],
151
+ code: ["code", "programming", "developer", "builder"],
152
+ mcp: ["mcp"],
153
+ testing: ["test", "testing", "qa"],
154
+ communication: ["slack", "comms", "communication"],
155
+ brand: ["brand", "guidelines"],
156
+ };
157
+
158
+ for (const [tag, keywords] of Object.entries(tagKeywords)) {
159
+ if (keywords.some((kw) => text.includes(kw))) {
160
+ tags.push(tag);
161
+ }
162
+ }
163
+
164
+ return tags.length > 0 ? tags : ["general"];
165
+ }
166
+
167
+ // Provider interface
168
+ export interface SkillsProvider {
169
+ id: string;
170
+ name: string;
171
+ search(query: string, page?: number): Promise<SkillsSearchResult>;
172
+ getSkill(skillId: string): Promise<MarketplaceSkill | null>;
173
+ getFeatured(): Promise<MarketplaceSkill[]>;
174
+ getCategories(): Promise<string[]>;
175
+ }
176
+
177
+ export const GithubSkillsProvider: SkillsProvider = {
178
+ id: "github",
179
+ name: "GitHub Public Skills",
180
+
181
+ async search(query: string, page = 1): Promise<SkillsSearchResult> {
182
+ const allSkills = await fetchAllSkills();
183
+
184
+ if (!query.trim()) {
185
+ return {
186
+ skills: allSkills,
187
+ total: allSkills.length,
188
+ page: 1,
189
+ per_page: 50,
190
+ };
191
+ }
192
+
193
+ const lowerQuery = query.toLowerCase();
194
+ const filtered = allSkills.filter(
195
+ (s) =>
196
+ s.name.toLowerCase().includes(lowerQuery) ||
197
+ s.description.toLowerCase().includes(lowerQuery) ||
198
+ s.tags.some((t) => t.toLowerCase().includes(lowerQuery))
199
+ );
200
+
201
+ return {
202
+ skills: filtered,
203
+ total: filtered.length,
204
+ page: 1,
205
+ per_page: 50,
206
+ };
207
+ },
208
+
209
+ async getSkill(skillId: string): Promise<MarketplaceSkill | null> {
210
+ const allSkills = await fetchAllSkills();
211
+ return allSkills.find((s) => s.id === skillId) || null;
212
+ },
213
+
214
+ async getFeatured(): Promise<MarketplaceSkill[]> {
215
+ const allSkills = await fetchAllSkills();
216
+ // Return all skills sorted by name
217
+ return [...allSkills].sort((a, b) => a.name.localeCompare(b.name));
218
+ },
219
+
220
+ async getCategories(): Promise<string[]> {
221
+ const allSkills = await fetchAllSkills();
222
+ const tags = new Set<string>();
223
+ allSkills.forEach((s) => s.tags.forEach((t) => tags.add(t)));
224
+ return Array.from(tags).sort();
225
+ },
226
+ };
227
+
228
+ // Legacy export for compatibility (uses GitHub provider now)
229
+ export const SkillsmpProvider = {
230
+ id: "skillsmp",
231
+ name: "SkillsMP",
232
+
233
+ async search(apiKey: string, query: string, page = 1): Promise<SkillsSearchResult> {
234
+ // Ignore API key, use GitHub provider
235
+ return GithubSkillsProvider.search(query, page);
236
+ },
237
+
238
+ async getSkill(apiKey: string, skillId: string): Promise<MarketplaceSkill | null> {
239
+ return GithubSkillsProvider.getSkill(skillId);
240
+ },
241
+
242
+ async getFeatured(apiKey: string): Promise<MarketplaceSkill[]> {
243
+ return GithubSkillsProvider.getFeatured();
244
+ },
245
+
246
+ async getCategories(apiKey: string): Promise<string[]> {
247
+ return GithubSkillsProvider.getCategories();
248
+ },
249
+ };
250
+
251
+ // Parse SKILL.md content into structured data
252
+ export function parseSkillMd(content: string): {
253
+ name: string;
254
+ description: string;
255
+ body: string;
256
+ license?: string;
257
+ compatibility?: string;
258
+ metadata?: Record<string, string>;
259
+ allowedTools?: string[];
260
+ } | null {
261
+ // Check for YAML frontmatter
262
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
263
+ if (!frontmatterMatch) {
264
+ return null;
265
+ }
266
+
267
+ const [, frontmatter, body] = frontmatterMatch;
268
+
269
+ // Parse YAML (simple parser for common fields)
270
+ const yaml: Record<string, any> = {};
271
+ const lines = frontmatter.split("\n");
272
+ let inMetadata = false;
273
+
274
+ for (const line of lines) {
275
+ if (line.startsWith("metadata:")) {
276
+ inMetadata = true;
277
+ yaml.metadata = {};
278
+ continue;
279
+ }
280
+
281
+ if (inMetadata) {
282
+ if (line.startsWith(" ")) {
283
+ const match = line.trim().match(/^(\w+):\s*["']?(.*)["']?$/);
284
+ if (match) {
285
+ yaml.metadata[match[1]] = match[2].replace(/["']$/, "");
286
+ }
287
+ } else {
288
+ inMetadata = false;
289
+ }
290
+ }
291
+
292
+ if (!inMetadata) {
293
+ const match = line.match(/^(\w[\w-]*):\s*(.*)$/);
294
+ if (match) {
295
+ const [, key, value] = match;
296
+ yaml[key] = value.replace(/^["']|["']$/g, "");
297
+ }
298
+ }
299
+ }
300
+
301
+ if (!yaml.name || !yaml.description) {
302
+ return null;
303
+ }
304
+
305
+ return {
306
+ name: yaml.name,
307
+ description: yaml.description,
308
+ body: body.trim(),
309
+ license: yaml.license,
310
+ compatibility: yaml.compatibility,
311
+ metadata: yaml.metadata,
312
+ allowedTools: yaml["allowed-tools"]?.split(/\s+/).filter(Boolean),
313
+ };
314
+ }
315
+
316
+ // Re-export types for backwards compatibility
317
+ export type SkillsmpSkill = MarketplaceSkill;
318
+ export type SkillsmpSearchResult = SkillsSearchResult;
package/src/providers.ts CHANGED
@@ -128,6 +128,27 @@ export const PROVIDERS = {
128
128
  description: "MCP server registry and hosting",
129
129
  models: [],
130
130
  },
131
+ agentdojo: {
132
+ id: "agentdojo",
133
+ name: "AgentDojo",
134
+ displayName: "AgentDojo",
135
+ type: "integration" as const,
136
+ envVar: "AGENTDOJO_API_KEY",
137
+ docsUrl: "https://agentdojo.com/settings",
138
+ description: "Hosted MCP tools and agent capabilities",
139
+ models: [],
140
+ },
141
+ // Skills Integrations
142
+ skillsmp: {
143
+ id: "skillsmp",
144
+ name: "SkillsMP",
145
+ displayName: "SkillsMP",
146
+ type: "integration" as const,
147
+ envVar: "SKILLSMP_API_KEY",
148
+ docsUrl: "https://skillsmp.com/settings",
149
+ description: "Agent skills marketplace (optional - public registry available without key)",
150
+ models: [],
151
+ },
131
152
  } as const;
132
153
 
133
154
  export type ProviderId = keyof typeof PROVIDERS;