@stackmemoryai/stackmemory 0.5.4 → 0.5.5

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,471 @@
1
+ import { execSync } from "child_process";
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ import * as os from "os";
5
+ import { logger } from "../core/monitoring/logger.js";
6
+ class APISkill {
7
+ registryPath;
8
+ restishConfigPath;
9
+ registry;
10
+ constructor() {
11
+ this.registryPath = path.join(
12
+ os.homedir(),
13
+ ".stackmemory",
14
+ "api-registry.json"
15
+ );
16
+ this.restishConfigPath = process.platform === "darwin" ? path.join(
17
+ os.homedir(),
18
+ "Library",
19
+ "Application Support",
20
+ "restish",
21
+ "apis.json"
22
+ ) : path.join(os.homedir(), ".config", "restish", "apis.json");
23
+ this.registry = this.loadRegistry();
24
+ }
25
+ loadRegistry() {
26
+ try {
27
+ if (fs.existsSync(this.registryPath)) {
28
+ return JSON.parse(fs.readFileSync(this.registryPath, "utf-8"));
29
+ }
30
+ } catch (error) {
31
+ logger.warn("Failed to load API registry:", error);
32
+ }
33
+ return { apis: {}, version: "1.0.0" };
34
+ }
35
+ saveRegistry() {
36
+ const dir = path.dirname(this.registryPath);
37
+ if (!fs.existsSync(dir)) {
38
+ fs.mkdirSync(dir, { recursive: true });
39
+ }
40
+ fs.writeFileSync(this.registryPath, JSON.stringify(this.registry, null, 2));
41
+ }
42
+ /**
43
+ * Load restish config
44
+ */
45
+ loadRestishConfig() {
46
+ try {
47
+ if (fs.existsSync(this.restishConfigPath)) {
48
+ return JSON.parse(fs.readFileSync(this.restishConfigPath, "utf-8"));
49
+ }
50
+ } catch (error) {
51
+ logger.warn("Failed to load restish config:", error);
52
+ }
53
+ return {};
54
+ }
55
+ /**
56
+ * Save restish config
57
+ */
58
+ saveRestishConfig(config) {
59
+ const dir = path.dirname(this.restishConfigPath);
60
+ if (!fs.existsSync(dir)) {
61
+ fs.mkdirSync(dir, { recursive: true });
62
+ }
63
+ fs.writeFileSync(this.restishConfigPath, JSON.stringify(config, null, 2));
64
+ }
65
+ /**
66
+ * Check if restish is installed
67
+ */
68
+ checkRestish() {
69
+ try {
70
+ execSync("which restish", { stdio: "pipe" });
71
+ return true;
72
+ } catch {
73
+ return false;
74
+ }
75
+ }
76
+ /**
77
+ * Add/register a new API
78
+ */
79
+ async add(name, baseUrl, options) {
80
+ if (!this.checkRestish()) {
81
+ return {
82
+ success: false,
83
+ message: "restish not installed. Run: brew install restish"
84
+ };
85
+ }
86
+ try {
87
+ const restishConfig = this.loadRestishConfig();
88
+ const apiConfig = {
89
+ base: baseUrl
90
+ };
91
+ if (options?.spec) {
92
+ apiConfig.spec_files = [options.spec];
93
+ }
94
+ if (options?.authType === "api-key" && options?.envVar) {
95
+ apiConfig.profiles = {
96
+ default: {
97
+ headers: {
98
+ [options.headerName || "Authorization"]: `$${options.envVar}`
99
+ }
100
+ }
101
+ };
102
+ }
103
+ restishConfig[name] = apiConfig;
104
+ this.saveRestishConfig(restishConfig);
105
+ const config = {
106
+ name,
107
+ baseUrl,
108
+ specUrl: options?.spec,
109
+ authType: options?.authType || "none",
110
+ authConfig: {
111
+ headerName: options?.headerName || "Authorization",
112
+ envVar: options?.envVar
113
+ },
114
+ registeredAt: (/* @__PURE__ */ new Date()).toISOString()
115
+ };
116
+ if (options?.spec) {
117
+ config.specUrl = options.spec;
118
+ }
119
+ this.registry.apis[name] = config;
120
+ this.saveRegistry();
121
+ return {
122
+ success: true,
123
+ message: `API '${name}' registered successfully`,
124
+ data: {
125
+ name,
126
+ baseUrl,
127
+ authType: config.authType,
128
+ operations: config.operations?.length || "auto-discovered"
129
+ }
130
+ };
131
+ } catch (error) {
132
+ logger.error("Failed to add API:", error);
133
+ return {
134
+ success: false,
135
+ message: `Failed to register API: ${error.message}`
136
+ };
137
+ }
138
+ }
139
+ /**
140
+ * Discover available operations for an API
141
+ */
142
+ discoverOperations(apiName) {
143
+ try {
144
+ const output = execSync(`restish ${apiName} --help 2>&1`, {
145
+ encoding: "utf-8",
146
+ stdio: ["pipe", "pipe", "pipe"]
147
+ });
148
+ const operations = [];
149
+ const lines = output.split("\n");
150
+ let inCommands = false;
151
+ for (const line of lines) {
152
+ if (line.includes("Available Commands:")) {
153
+ inCommands = true;
154
+ continue;
155
+ }
156
+ if (inCommands && line.trim()) {
157
+ const match = line.match(/^\s+(\S+)/);
158
+ if (match && !line.includes("help")) {
159
+ operations.push(match[1]);
160
+ }
161
+ }
162
+ if (inCommands && line.includes("Flags:")) {
163
+ break;
164
+ }
165
+ }
166
+ return operations;
167
+ } catch {
168
+ return [];
169
+ }
170
+ }
171
+ /**
172
+ * List registered APIs
173
+ */
174
+ async list() {
175
+ const apis = Object.values(this.registry.apis);
176
+ if (apis.length === 0) {
177
+ return {
178
+ success: true,
179
+ message: "No APIs registered. Use /api add <name> <url> to register one.",
180
+ data: []
181
+ };
182
+ }
183
+ return {
184
+ success: true,
185
+ message: `${apis.length} API(s) registered`,
186
+ data: apis.map((api) => ({
187
+ name: api.name,
188
+ baseUrl: api.baseUrl,
189
+ authType: api.authType,
190
+ operations: api.operations?.length || "unknown",
191
+ registeredAt: api.registeredAt
192
+ }))
193
+ };
194
+ }
195
+ /**
196
+ * Show details for a specific API
197
+ */
198
+ async describe(apiName, operation) {
199
+ const api = this.registry.apis[apiName];
200
+ if (!api) {
201
+ try {
202
+ const output = execSync(`restish api show ${apiName}`, {
203
+ encoding: "utf-8",
204
+ stdio: ["pipe", "pipe", "pipe"]
205
+ });
206
+ return {
207
+ success: true,
208
+ message: `API '${apiName}' (from restish config)`,
209
+ data: { raw: output }
210
+ };
211
+ } catch {
212
+ return {
213
+ success: false,
214
+ message: `API '${apiName}' not found`
215
+ };
216
+ }
217
+ }
218
+ if (operation) {
219
+ try {
220
+ const output = execSync(`restish ${apiName} ${operation} --help`, {
221
+ encoding: "utf-8",
222
+ stdio: ["pipe", "pipe", "pipe"]
223
+ });
224
+ return {
225
+ success: true,
226
+ message: `Operation: ${apiName}.${operation}`,
227
+ data: {
228
+ operation,
229
+ help: output
230
+ }
231
+ };
232
+ } catch {
233
+ return {
234
+ success: false,
235
+ message: `Operation '${operation}' not found for API '${apiName}'`
236
+ };
237
+ }
238
+ }
239
+ const operations = this.discoverOperations(apiName);
240
+ api.operations = operations;
241
+ this.saveRegistry();
242
+ return {
243
+ success: true,
244
+ message: `API: ${apiName}`,
245
+ data: {
246
+ ...api,
247
+ operations
248
+ }
249
+ };
250
+ }
251
+ /**
252
+ * Execute an API operation
253
+ */
254
+ async exec(apiName, operation, params, options) {
255
+ if (!this.checkRestish()) {
256
+ return {
257
+ success: false,
258
+ message: "restish not installed. Run: brew install restish"
259
+ };
260
+ }
261
+ const api = this.registry.apis[apiName];
262
+ if (!api) {
263
+ return {
264
+ success: false,
265
+ message: `API '${apiName}' not registered. Use /api add first.`
266
+ };
267
+ }
268
+ const urlPath = operation.startsWith("/") ? operation : `/${operation}`;
269
+ const fullUrl = `${api.baseUrl}${urlPath}`;
270
+ const args = ["get", fullUrl];
271
+ if (options?.raw) {
272
+ args.push("--rsh-raw");
273
+ }
274
+ if (options?.filter) {
275
+ args.push("--rsh-filter", options.filter);
276
+ }
277
+ if (api?.authConfig?.envVar) {
278
+ const token = process.env[api.authConfig.envVar];
279
+ if (token) {
280
+ const headerName = api.authConfig.headerName || "Authorization";
281
+ args.push("-H", `${headerName}:${token}`);
282
+ }
283
+ }
284
+ if (options?.headers) {
285
+ for (const [key, value] of Object.entries(options.headers)) {
286
+ args.push("-H", `${key}:${value}`);
287
+ }
288
+ }
289
+ if (params) {
290
+ for (const [key, value] of Object.entries(params)) {
291
+ args.push("-q", `${key}=${String(value)}`);
292
+ }
293
+ }
294
+ args.push("-o", "json");
295
+ try {
296
+ logger.info(`Executing: restish ${args.join(" ")}`);
297
+ const output = execSync(`restish ${args.join(" ")}`, {
298
+ encoding: "utf-8",
299
+ stdio: ["pipe", "pipe", "pipe"],
300
+ env: process.env
301
+ });
302
+ let data;
303
+ try {
304
+ data = JSON.parse(output);
305
+ } catch {
306
+ data = output;
307
+ }
308
+ return {
309
+ success: true,
310
+ message: `${apiName} ${operation} executed`,
311
+ data
312
+ };
313
+ } catch (error) {
314
+ const stderr = error.stderr?.toString() || error.message;
315
+ logger.error(`API exec failed:`, stderr);
316
+ return {
317
+ success: false,
318
+ message: `API call failed: ${stderr}`
319
+ };
320
+ }
321
+ }
322
+ /**
323
+ * Configure authentication for an API
324
+ */
325
+ async auth(apiName, options) {
326
+ const api = this.registry.apis[apiName];
327
+ if (!api) {
328
+ return {
329
+ success: false,
330
+ message: `API '${apiName}' not registered. Use /api add first.`
331
+ };
332
+ }
333
+ if (options.token) {
334
+ const envVar = options.envVar || `${apiName.toUpperCase()}_API_KEY`;
335
+ process.env[envVar] = options.token;
336
+ api.authType = "api-key";
337
+ api.authConfig = {
338
+ ...api.authConfig,
339
+ envVar
340
+ };
341
+ this.saveRegistry();
342
+ return {
343
+ success: true,
344
+ message: `Auth configured for '${apiName}'. Token stored in ${envVar}`,
345
+ data: { envVar }
346
+ };
347
+ }
348
+ if (options.oauth) {
349
+ try {
350
+ const scopeArg = options.scopes ? `--scopes=${options.scopes.join(",")}` : "";
351
+ execSync(`restish api configure ${apiName} --auth=oauth2 ${scopeArg}`, {
352
+ stdio: "inherit"
353
+ });
354
+ api.authType = "oauth2";
355
+ this.saveRegistry();
356
+ return {
357
+ success: true,
358
+ message: `OAuth2 configured for '${apiName}'`
359
+ };
360
+ } catch (error) {
361
+ return {
362
+ success: false,
363
+ message: `OAuth setup failed: ${error.message}`
364
+ };
365
+ }
366
+ }
367
+ return {
368
+ success: false,
369
+ message: "Specify --token or --oauth"
370
+ };
371
+ }
372
+ /**
373
+ * Remove an API
374
+ */
375
+ async remove(apiName) {
376
+ if (!this.registry.apis[apiName]) {
377
+ return {
378
+ success: false,
379
+ message: `API '${apiName}' not found`
380
+ };
381
+ }
382
+ delete this.registry.apis[apiName];
383
+ this.saveRegistry();
384
+ return {
385
+ success: true,
386
+ message: `API '${apiName}' removed`
387
+ };
388
+ }
389
+ /**
390
+ * Sync API spec (refresh operations)
391
+ */
392
+ async sync(apiName) {
393
+ if (!this.checkRestish()) {
394
+ return {
395
+ success: false,
396
+ message: "restish not installed. Run: brew install restish"
397
+ };
398
+ }
399
+ try {
400
+ execSync(`restish api sync ${apiName}`, { stdio: "pipe" });
401
+ const operations = this.discoverOperations(apiName);
402
+ if (this.registry.apis[apiName]) {
403
+ this.registry.apis[apiName].operations = operations;
404
+ this.saveRegistry();
405
+ }
406
+ return {
407
+ success: true,
408
+ message: `API '${apiName}' synced`,
409
+ data: { operations }
410
+ };
411
+ } catch (error) {
412
+ return {
413
+ success: false,
414
+ message: `Sync failed: ${error.message}`
415
+ };
416
+ }
417
+ }
418
+ /**
419
+ * Get help for the API skill
420
+ */
421
+ getHelp() {
422
+ return `
423
+ /api - OpenAPI-based API access via Restish
424
+
425
+ Commands:
426
+ /api add <name> <url> [--spec <url>] [--auth-type api-key|oauth2]
427
+ Register a new API
428
+
429
+ /api list
430
+ List all registered APIs
431
+
432
+ /api describe <name> [operation]
433
+ Show API details or specific operation
434
+
435
+ /api exec <name> <operation> [--param value...]
436
+ Execute an API operation
437
+
438
+ /api auth <name> --token <token> [--env-var NAME]
439
+ Configure API authentication
440
+
441
+ /api auth <name> --oauth [--scopes scope1,scope2]
442
+ Configure OAuth2 authentication
443
+
444
+ /api sync <name>
445
+ Refresh API operations from spec
446
+
447
+ /api remove <name>
448
+ Remove a registered API
449
+
450
+ Examples:
451
+ /api add github https://api.github.com --spec https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json
452
+ /api auth github --token "$GITHUB_TOKEN"
453
+ /api exec github repos list-for-user --username octocat
454
+ /api exec github issues list --owner microsoft --repo vscode --state open
455
+
456
+ Built on restish (https://rest.sh) for automatic OpenAPI discovery.
457
+ `;
458
+ }
459
+ }
460
+ let apiSkillInstance = null;
461
+ function getAPISkill() {
462
+ if (!apiSkillInstance) {
463
+ apiSkillInstance = new APISkill();
464
+ }
465
+ return apiSkillInstance;
466
+ }
467
+ export {
468
+ APISkill,
469
+ getAPISkill
470
+ };
471
+ //# sourceMappingURL=api-skill.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/skills/api-skill.ts"],
4
+ "sourcesContent": ["/**\n * API Skill - OpenAPI-based API access via Restish\n *\n * Wraps the restish CLI to provide zero-code API integration\n * based on OpenAPI specifications.\n */\n\nimport { execSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\nimport { logger } from '../core/monitoring/logger.js';\nimport type { SkillResult } from './claude-skills.js';\n\nexport interface APIConfig {\n name: string;\n baseUrl: string;\n specUrl?: string;\n authType: 'none' | 'api-key' | 'oauth2' | 'basic';\n authConfig?: {\n headerName?: string;\n queryParam?: string;\n envVar?: string;\n };\n registeredAt: string;\n operations?: string[];\n}\n\nexport interface APIRegistry {\n apis: Record<string, APIConfig>;\n version: string;\n}\n\nexport class APISkill {\n private registryPath: string;\n private restishConfigPath: string;\n private registry: APIRegistry;\n\n constructor() {\n this.registryPath = path.join(\n os.homedir(),\n '.stackmemory',\n 'api-registry.json'\n );\n // Platform-specific restish config path\n // Mac: ~/Library/Application Support/restish/apis.json\n // Linux: ~/.config/restish/apis.json\n // Windows: %AppData%/restish/apis.json\n this.restishConfigPath =\n process.platform === 'darwin'\n ? path.join(\n os.homedir(),\n 'Library',\n 'Application Support',\n 'restish',\n 'apis.json'\n )\n : path.join(os.homedir(), '.config', 'restish', 'apis.json');\n this.registry = this.loadRegistry();\n }\n\n private loadRegistry(): APIRegistry {\n try {\n if (fs.existsSync(this.registryPath)) {\n return JSON.parse(fs.readFileSync(this.registryPath, 'utf-8'));\n }\n } catch (error) {\n logger.warn('Failed to load API registry:', error);\n }\n return { apis: {}, version: '1.0.0' };\n }\n\n private saveRegistry(): void {\n const dir = path.dirname(this.registryPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n fs.writeFileSync(this.registryPath, JSON.stringify(this.registry, null, 2));\n }\n\n /**\n * Load restish config\n */\n private loadRestishConfig(): Record<string, unknown> {\n try {\n if (fs.existsSync(this.restishConfigPath)) {\n return JSON.parse(fs.readFileSync(this.restishConfigPath, 'utf-8'));\n }\n } catch (error) {\n logger.warn('Failed to load restish config:', error);\n }\n return {};\n }\n\n /**\n * Save restish config\n */\n private saveRestishConfig(config: Record<string, unknown>): void {\n const dir = path.dirname(this.restishConfigPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n fs.writeFileSync(this.restishConfigPath, JSON.stringify(config, null, 2));\n }\n\n /**\n * Check if restish is installed\n */\n private checkRestish(): boolean {\n try {\n execSync('which restish', { stdio: 'pipe' });\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Add/register a new API\n */\n async add(\n name: string,\n baseUrl: string,\n options?: {\n spec?: string;\n authType?: 'none' | 'api-key' | 'oauth2' | 'basic';\n headerName?: string;\n envVar?: string;\n }\n ): Promise<SkillResult> {\n if (!this.checkRestish()) {\n return {\n success: false,\n message: 'restish not installed. Run: brew install restish',\n };\n }\n\n try {\n // Configure restish for this API by writing directly to config\n const restishConfig = this.loadRestishConfig();\n\n // Build restish API config\n const apiConfig: Record<string, unknown> = {\n base: baseUrl,\n };\n\n // Add spec URL if provided for auto-discovery\n if (options?.spec) {\n apiConfig.spec_files = [options.spec];\n }\n\n // Add auth config based on type\n if (options?.authType === 'api-key' && options?.envVar) {\n apiConfig.profiles = {\n default: {\n headers: {\n [options.headerName || 'Authorization']: `$${options.envVar}`,\n },\n },\n };\n }\n\n restishConfig[name] = apiConfig;\n this.saveRestishConfig(restishConfig);\n\n // Store in our registry\n const config: APIConfig = {\n name,\n baseUrl,\n specUrl: options?.spec,\n authType: options?.authType || 'none',\n authConfig: {\n headerName: options?.headerName || 'Authorization',\n envVar: options?.envVar,\n },\n registeredAt: new Date().toISOString(),\n };\n\n // Skip sync during add - it can be slow due to network requests\n // Users can manually sync with: stackmemory api sync <name>\n if (options?.spec) {\n config.specUrl = options.spec;\n }\n\n this.registry.apis[name] = config;\n this.saveRegistry();\n\n return {\n success: true,\n message: `API '${name}' registered successfully`,\n data: {\n name,\n baseUrl,\n authType: config.authType,\n operations: config.operations?.length || 'auto-discovered',\n },\n };\n } catch (error) {\n logger.error('Failed to add API:', error);\n return {\n success: false,\n message: `Failed to register API: ${error.message}`,\n };\n }\n }\n\n /**\n * Discover available operations for an API\n */\n private discoverOperations(apiName: string): string[] {\n try {\n const output = execSync(`restish ${apiName} --help 2>&1`, {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n\n // Parse operations from help output\n const operations: string[] = [];\n const lines = output.split('\\n');\n let inCommands = false;\n\n for (const line of lines) {\n if (line.includes('Available Commands:')) {\n inCommands = true;\n continue;\n }\n if (inCommands && line.trim()) {\n const match = line.match(/^\\s+(\\S+)/);\n if (match && !line.includes('help')) {\n operations.push(match[1]);\n }\n }\n if (inCommands && line.includes('Flags:')) {\n break;\n }\n }\n\n return operations;\n } catch {\n return [];\n }\n }\n\n /**\n * List registered APIs\n */\n async list(): Promise<SkillResult> {\n const apis = Object.values(this.registry.apis);\n\n if (apis.length === 0) {\n return {\n success: true,\n message:\n 'No APIs registered. Use /api add <name> <url> to register one.',\n data: [],\n };\n }\n\n return {\n success: true,\n message: `${apis.length} API(s) registered`,\n data: apis.map((api) => ({\n name: api.name,\n baseUrl: api.baseUrl,\n authType: api.authType,\n operations: api.operations?.length || 'unknown',\n registeredAt: api.registeredAt,\n })),\n };\n }\n\n /**\n * Show details for a specific API\n */\n async describe(apiName: string, operation?: string): Promise<SkillResult> {\n const api = this.registry.apis[apiName];\n\n if (!api) {\n // Try to get info directly from restish\n try {\n const output = execSync(`restish api show ${apiName}`, {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n\n return {\n success: true,\n message: `API '${apiName}' (from restish config)`,\n data: { raw: output },\n };\n } catch {\n return {\n success: false,\n message: `API '${apiName}' not found`,\n };\n }\n }\n\n if (operation) {\n // Get specific operation details\n try {\n const output = execSync(`restish ${apiName} ${operation} --help`, {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n\n return {\n success: true,\n message: `Operation: ${apiName}.${operation}`,\n data: {\n operation,\n help: output,\n },\n };\n } catch {\n return {\n success: false,\n message: `Operation '${operation}' not found for API '${apiName}'`,\n };\n }\n }\n\n // Get all operations\n const operations = this.discoverOperations(apiName);\n api.operations = operations;\n this.saveRegistry();\n\n return {\n success: true,\n message: `API: ${apiName}`,\n data: {\n ...api,\n operations,\n },\n };\n }\n\n /**\n * Execute an API operation\n */\n async exec(\n apiName: string,\n operation: string,\n params?: Record<string, unknown>,\n options?: {\n raw?: boolean;\n filter?: string;\n headers?: Record<string, string>;\n }\n ): Promise<SkillResult> {\n if (!this.checkRestish()) {\n return {\n success: false,\n message: 'restish not installed. Run: brew install restish',\n };\n }\n\n const api = this.registry.apis[apiName];\n if (!api) {\n return {\n success: false,\n message: `API '${apiName}' not registered. Use /api add first.`,\n };\n }\n\n // Build the URL path from operation\n // e.g., \"repos/owner/repo\" or \"/repos/owner/repo\"\n const urlPath = operation.startsWith('/') ? operation : `/${operation}`;\n const fullUrl = `${api.baseUrl}${urlPath}`;\n\n // Build command using direct URL (more reliable than API names)\n const args: string[] = ['get', fullUrl];\n\n // Add options\n if (options?.raw) {\n args.push('--rsh-raw');\n }\n if (options?.filter) {\n args.push('--rsh-filter', options.filter);\n }\n\n // Add headers (including auth)\n if (api?.authConfig?.envVar) {\n const token = process.env[api.authConfig.envVar];\n if (token) {\n const headerName = api.authConfig.headerName || 'Authorization';\n args.push('-H', `${headerName}:${token}`);\n }\n }\n\n if (options?.headers) {\n for (const [key, value] of Object.entries(options.headers)) {\n args.push('-H', `${key}:${value}`);\n }\n }\n\n // Add query parameters\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n args.push('-q', `${key}=${String(value)}`);\n }\n }\n\n // Output as JSON\n args.push('-o', 'json');\n\n try {\n logger.info(`Executing: restish ${args.join(' ')}`);\n\n const output = execSync(`restish ${args.join(' ')}`, {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n env: process.env,\n });\n\n // Try to parse as JSON\n let data: unknown;\n try {\n data = JSON.parse(output);\n } catch {\n data = output;\n }\n\n return {\n success: true,\n message: `${apiName} ${operation} executed`,\n data,\n };\n } catch (error) {\n const stderr = error.stderr?.toString() || error.message;\n logger.error(`API exec failed:`, stderr);\n\n return {\n success: false,\n message: `API call failed: ${stderr}`,\n };\n }\n }\n\n /**\n * Configure authentication for an API\n */\n async auth(\n apiName: string,\n options: {\n token?: string;\n envVar?: string;\n oauth?: boolean;\n scopes?: string[];\n }\n ): Promise<SkillResult> {\n const api = this.registry.apis[apiName];\n\n if (!api) {\n return {\n success: false,\n message: `API '${apiName}' not registered. Use /api add first.`,\n };\n }\n\n if (options.token) {\n // Store token in env var (don't save to disk for security)\n const envVar = options.envVar || `${apiName.toUpperCase()}_API_KEY`;\n process.env[envVar] = options.token;\n\n api.authType = 'api-key';\n api.authConfig = {\n ...api.authConfig,\n envVar,\n };\n this.saveRegistry();\n\n return {\n success: true,\n message: `Auth configured for '${apiName}'. Token stored in ${envVar}`,\n data: { envVar },\n };\n }\n\n if (options.oauth) {\n // Use restish's OAuth flow\n try {\n const scopeArg = options.scopes\n ? `--scopes=${options.scopes.join(',')}`\n : '';\n execSync(`restish api configure ${apiName} --auth=oauth2 ${scopeArg}`, {\n stdio: 'inherit',\n });\n\n api.authType = 'oauth2';\n this.saveRegistry();\n\n return {\n success: true,\n message: `OAuth2 configured for '${apiName}'`,\n };\n } catch (error) {\n return {\n success: false,\n message: `OAuth setup failed: ${error.message}`,\n };\n }\n }\n\n return {\n success: false,\n message: 'Specify --token or --oauth',\n };\n }\n\n /**\n * Remove an API\n */\n async remove(apiName: string): Promise<SkillResult> {\n if (!this.registry.apis[apiName]) {\n return {\n success: false,\n message: `API '${apiName}' not found`,\n };\n }\n\n delete this.registry.apis[apiName];\n this.saveRegistry();\n\n return {\n success: true,\n message: `API '${apiName}' removed`,\n };\n }\n\n /**\n * Sync API spec (refresh operations)\n */\n async sync(apiName: string): Promise<SkillResult> {\n if (!this.checkRestish()) {\n return {\n success: false,\n message: 'restish not installed. Run: brew install restish',\n };\n }\n\n try {\n execSync(`restish api sync ${apiName}`, { stdio: 'pipe' });\n\n const operations = this.discoverOperations(apiName);\n\n if (this.registry.apis[apiName]) {\n this.registry.apis[apiName].operations = operations;\n this.saveRegistry();\n }\n\n return {\n success: true,\n message: `API '${apiName}' synced`,\n data: { operations },\n };\n } catch (error) {\n return {\n success: false,\n message: `Sync failed: ${error.message}`,\n };\n }\n }\n\n /**\n * Get help for the API skill\n */\n getHelp(): string {\n return `\n/api - OpenAPI-based API access via Restish\n\nCommands:\n /api add <name> <url> [--spec <url>] [--auth-type api-key|oauth2]\n Register a new API\n\n /api list\n List all registered APIs\n\n /api describe <name> [operation]\n Show API details or specific operation\n\n /api exec <name> <operation> [--param value...]\n Execute an API operation\n\n /api auth <name> --token <token> [--env-var NAME]\n Configure API authentication\n\n /api auth <name> --oauth [--scopes scope1,scope2]\n Configure OAuth2 authentication\n\n /api sync <name>\n Refresh API operations from spec\n\n /api remove <name>\n Remove a registered API\n\nExamples:\n /api add github https://api.github.com --spec https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json\n /api auth github --token \"$GITHUB_TOKEN\"\n /api exec github repos list-for-user --username octocat\n /api exec github issues list --owner microsoft --repo vscode --state open\n\nBuilt on restish (https://rest.sh) for automatic OpenAPI discovery.\n`;\n }\n}\n\n// Singleton instance\nlet apiSkillInstance: APISkill | null = null;\n\nexport function getAPISkill(): APISkill {\n if (!apiSkillInstance) {\n apiSkillInstance = new APISkill();\n }\n return apiSkillInstance;\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -5,6 +5,7 @@ import {
5
5
  import {
6
6
  RecursiveAgentOrchestrator
7
7
  } from "./recursive-agent-orchestrator.js";
8
+ import { getAPISkill } from "./api-skill.js";
8
9
  import * as fs from "fs";
9
10
  import * as path from "path";
10
11
  import * as os from "os";
@@ -604,6 +605,7 @@ class ClaudeSkillsManager {
604
605
  this.handoffSkill = new HandoffSkill(context);
605
606
  this.checkpointSkill = new CheckpointSkill(context);
606
607
  this.archaeologistSkill = new ArchaeologistSkill(context);
608
+ this.apiSkill = getAPISkill();
607
609
  import("./dashboard-launcher.js").then((module) => {
608
610
  this.dashboardLauncher = new module.DashboardLauncherSkill();
609
611
  logger.info("Dashboard launcher initialized (manual launch required)");
@@ -649,6 +651,7 @@ class ClaudeSkillsManager {
649
651
  dashboardLauncher;
650
652
  repoIngestionSkill = null;
651
653
  rlmOrchestrator = null;
654
+ apiSkill;
652
655
  async executeSkill(skillName, args, options) {
653
656
  switch (skillName) {
654
657
  case "handoff":
@@ -848,6 +851,49 @@ class ClaudeSkillsManager {
848
851
  message: `RLM execution failed: ${error.message}`
849
852
  };
850
853
  }
854
+ case "api":
855
+ const apiCmd = args[0];
856
+ switch (apiCmd) {
857
+ case "add":
858
+ return this.apiSkill.add(args[1], args[2], {
859
+ spec: options?.spec,
860
+ authType: options?.authType,
861
+ headerName: options?.headerName,
862
+ envVar: options?.envVar
863
+ });
864
+ case "list":
865
+ return this.apiSkill.list();
866
+ case "describe":
867
+ return this.apiSkill.describe(args[1], args[2]);
868
+ case "exec":
869
+ const execParams = {};
870
+ for (let i = 3; i < args.length; i += 2) {
871
+ if (args[i] && args[i + 1]) {
872
+ execParams[args[i].replace("--", "")] = args[i + 1];
873
+ }
874
+ }
875
+ return this.apiSkill.exec(args[1], args[2], execParams, {
876
+ raw: options?.raw,
877
+ filter: options?.filter
878
+ });
879
+ case "auth":
880
+ return this.apiSkill.auth(args[1], {
881
+ token: options?.token,
882
+ envVar: options?.envVar,
883
+ oauth: options?.oauth,
884
+ scopes: options?.scopes?.split(",")
885
+ });
886
+ case "sync":
887
+ return this.apiSkill.sync(args[1]);
888
+ case "remove":
889
+ return this.apiSkill.remove(args[1]);
890
+ case "help":
891
+ default:
892
+ return {
893
+ success: true,
894
+ message: this.apiSkill.getHelp()
895
+ };
896
+ }
851
897
  default:
852
898
  return {
853
899
  success: false,
@@ -856,7 +902,7 @@ class ClaudeSkillsManager {
856
902
  }
857
903
  }
858
904
  getAvailableSkills() {
859
- const skills = ["handoff", "checkpoint", "dig", "dashboard"];
905
+ const skills = ["handoff", "checkpoint", "dig", "dashboard", "api"];
860
906
  if (this.repoIngestionSkill) {
861
907
  skills.push("repo");
862
908
  }
@@ -995,6 +1041,8 @@ Examples:
995
1041
  /rlm "Generate comprehensive tests for the API endpoints" --test-mode integration
996
1042
  /rlm "Review and improve code quality" --review-stages 5 --quality-threshold 0.95
997
1043
  `;
1044
+ case "api":
1045
+ return this.apiSkill.getHelp();
998
1046
  default:
999
1047
  return `Unknown skill: ${skillName}`;
1000
1048
  }