@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.
- package/bin/claude-sm +6 -0
- package/bin/claude-smd +6 -0
- package/dist/cli/claude-sm-danger.js +20 -0
- package/dist/cli/claude-sm-danger.js.map +7 -0
- package/dist/cli/commands/api.js +228 -0
- package/dist/cli/commands/api.js.map +7 -0
- package/dist/cli/commands/cleanup-processes.js +64 -0
- package/dist/cli/commands/cleanup-processes.js.map +7 -0
- package/dist/cli/commands/hooks.js +294 -0
- package/dist/cli/commands/hooks.js.map +7 -0
- package/dist/cli/commands/shell.js +248 -0
- package/dist/cli/commands/shell.js.map +7 -0
- package/dist/cli/index.js +9 -1
- package/dist/cli/index.js.map +2 -2
- package/dist/hooks/config.js +146 -0
- package/dist/hooks/config.js.map +7 -0
- package/dist/hooks/daemon.js +360 -0
- package/dist/hooks/daemon.js.map +7 -0
- package/dist/hooks/events.js +51 -0
- package/dist/hooks/events.js.map +7 -0
- package/dist/hooks/index.js +4 -0
- package/dist/hooks/index.js.map +7 -0
- package/dist/skills/api-discovery.js +349 -0
- package/dist/skills/api-discovery.js.map +7 -0
- package/dist/skills/api-skill.js +471 -0
- package/dist/skills/api-skill.js.map +7 -0
- package/dist/skills/claude-skills.js +49 -1
- package/dist/skills/claude-skills.js.map +2 -2
- package/dist/utils/process-cleanup.js +132 -0
- package/dist/utils/process-cleanup.js.map +7 -0
- package/package.json +4 -2
- package/templates/shell/sweep-complete.zsh +116 -0
- package/templates/shell/sweep-suggest.js +161 -0
|
@@ -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
|
}
|