@recapt/mcp 0.0.19-beta → 0.0.20

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 (93) hide show
  1. package/dist/cli/index.d.ts +0 -11
  2. package/dist/cli/index.js +1059 -21
  3. package/dist/index.d.ts +0 -19
  4. package/dist/index.js +3210 -89
  5. package/package.json +5 -3
  6. package/dist/api/client.d.ts +0 -6
  7. package/dist/api/client.js +0 -6
  8. package/dist/cli/commands/setup-self-improvement-gh.d.ts +0 -7
  9. package/dist/cli/commands/setup-self-improvement-gh.js +0 -310
  10. package/dist/cli/commands/setup.d.ts +0 -7
  11. package/dist/cli/commands/setup.js +0 -178
  12. package/dist/cli/commands/skill.d.ts +0 -24
  13. package/dist/cli/commands/skill.js +0 -264
  14. package/dist/cli/utils/ide-config.d.ts +0 -31
  15. package/dist/cli/utils/ide-config.js +0 -294
  16. package/dist/cli/utils/prompts.d.ts +0 -22
  17. package/dist/cli/utils/prompts.js +0 -71
  18. package/dist/tools/analyzeFlow.d.ts +0 -7
  19. package/dist/tools/analyzeFlow.js +0 -68
  20. package/dist/tools/analyzeFunnel.d.ts +0 -7
  21. package/dist/tools/analyzeFunnel.js +0 -63
  22. package/dist/tools/catalog/callTool.d.ts +0 -22
  23. package/dist/tools/catalog/callTool.js +0 -92
  24. package/dist/tools/catalog/index.d.ts +0 -11
  25. package/dist/tools/catalog/index.js +0 -11
  26. package/dist/tools/catalog/searchTools.d.ts +0 -22
  27. package/dist/tools/catalog/searchTools.js +0 -194
  28. package/dist/tools/compareCohorts.d.ts +0 -6
  29. package/dist/tools/compareCohorts.js +0 -84
  30. package/dist/tools/comparePeriods.d.ts +0 -6
  31. package/dist/tools/comparePeriods.js +0 -54
  32. package/dist/tools/detectDrift.d.ts +0 -6
  33. package/dist/tools/detectDrift.js +0 -55
  34. package/dist/tools/detectRegressions.d.ts +0 -6
  35. package/dist/tools/detectRegressions.js +0 -63
  36. package/dist/tools/diagnostic.d.ts +0 -6
  37. package/dist/tools/diagnostic.js +0 -109
  38. package/dist/tools/discoverPersonas.d.ts +0 -6
  39. package/dist/tools/discoverPersonas.js +0 -50
  40. package/dist/tools/getActionableIssues.d.ts +0 -7
  41. package/dist/tools/getActionableIssues.js +0 -55
  42. package/dist/tools/getAnomalies.d.ts +0 -6
  43. package/dist/tools/getAnomalies.js +0 -53
  44. package/dist/tools/getConsoleErrors.d.ts +0 -6
  45. package/dist/tools/getConsoleErrors.js +0 -61
  46. package/dist/tools/getDeadClicks.d.ts +0 -6
  47. package/dist/tools/getDeadClicks.js +0 -42
  48. package/dist/tools/getDomains.d.ts +0 -6
  49. package/dist/tools/getDomains.js +0 -34
  50. package/dist/tools/getElementFriction.d.ts +0 -6
  51. package/dist/tools/getElementFriction.js +0 -45
  52. package/dist/tools/getFlowFriction.d.ts +0 -7
  53. package/dist/tools/getFlowFriction.js +0 -57
  54. package/dist/tools/getFormFriction.d.ts +0 -6
  55. package/dist/tools/getFormFriction.js +0 -42
  56. package/dist/tools/getIssues.d.ts +0 -6
  57. package/dist/tools/getIssues.js +0 -82
  58. package/dist/tools/getJourneyPatterns.d.ts +0 -7
  59. package/dist/tools/getJourneyPatterns.js +0 -50
  60. package/dist/tools/getPageMetrics.d.ts +0 -6
  61. package/dist/tools/getPageMetrics.js +0 -47
  62. package/dist/tools/getPageTrends.d.ts +0 -6
  63. package/dist/tools/getPageTrends.js +0 -46
  64. package/dist/tools/getSessionDetails.d.ts +0 -6
  65. package/dist/tools/getSessionDetails.js +0 -70
  66. package/dist/tools/getSessionPages.d.ts +0 -7
  67. package/dist/tools/getSessionPages.js +0 -74
  68. package/dist/tools/getUxHealthReport.d.ts +0 -7
  69. package/dist/tools/getUxHealthReport.js +0 -50
  70. package/dist/tools/improvementRun.d.ts +0 -6
  71. package/dist/tools/improvementRun.js +0 -386
  72. package/dist/tools/knowledge.d.ts +0 -6
  73. package/dist/tools/knowledge.js +0 -186
  74. package/dist/tools/listPages.d.ts +0 -6
  75. package/dist/tools/listPages.js +0 -50
  76. package/dist/tools/listSessions.d.ts +0 -7
  77. package/dist/tools/listSessions.js +0 -67
  78. package/dist/tools/memory.d.ts +0 -7
  79. package/dist/tools/memory.js +0 -119
  80. package/dist/tools/predictOutcomes.d.ts +0 -6
  81. package/dist/tools/predictOutcomes.js +0 -66
  82. package/dist/tools/remediation.d.ts +0 -6
  83. package/dist/tools/remediation.js +0 -335
  84. package/dist/tools/scanSite.d.ts +0 -6
  85. package/dist/tools/scanSite.js +0 -51
  86. package/dist/tools/searchSessions.d.ts +0 -6
  87. package/dist/tools/searchSessions.js +0 -51
  88. package/dist/tools/triage.d.ts +0 -6
  89. package/dist/tools/triage.js +0 -114
  90. package/dist/tools/triageSessions.d.ts +0 -8
  91. package/dist/tools/triageSessions.js +0 -197
  92. package/dist/tools/upgradeOptions.d.ts +0 -7
  93. package/dist/tools/upgradeOptions.js +0 -67
package/dist/cli/index.js CHANGED
@@ -1,26 +1,1064 @@
1
1
  #!/usr/bin/env node
2
- /**
3
- * Recapt MCP CLI
4
- *
5
- * Unified command-line interface for recapt MCP server management.
6
- *
7
- * Commands:
8
- * setup - Configure recapt MCP server for your AI IDE(s)
9
- * setup-self-improvement-gh - Set up automated self-improvement workflow for GitHub
10
- * skill - Manage recapt workflow skills
11
- */
12
- import { Command } from "commander";
2
+
3
+ // src/cli/index.ts
4
+ import { Command as Command4 } from "commander";
13
5
  import { createRequire } from "module";
14
- import { skillCommand } from "./commands/skill.js";
15
- import { setupCommand } from "./commands/setup.js";
16
- import { setupSelfImprovementGhCommand } from "./commands/setup-self-improvement-gh.js";
17
- const require = createRequire(import.meta.url);
18
- const pkg = require("../../package.json");
19
- const program = new Command();
20
- program
21
- .name("recapt")
22
- .description("Recapt MCP CLI - Configure and manage recapt for AI IDEs")
23
- .version(pkg.version);
6
+
7
+ // src/cli/commands/skill.ts
8
+ import { Command } from "commander";
9
+ import fs from "fs";
10
+ import path from "path";
11
+ import { fileURLToPath } from "url";
12
+ var __filename = fileURLToPath(import.meta.url);
13
+ var __dirname = path.dirname(__filename);
14
+ var SKILLS_DIR = path.resolve(__dirname, "../../../skills");
15
+ var AGENTS_DIR = ".agents/recapt";
16
+ var AGENTS_MD = "AGENTS.md";
17
+ var MARKER_START = "<!-- recapt:skills -->";
18
+ var MARKER_END = "<!-- /recapt:skills -->";
19
+ function getAvailableSkills() {
20
+ if (!fs.existsSync(SKILLS_DIR)) {
21
+ throw new Error(`Skills directory not found: ${SKILLS_DIR}`);
22
+ }
23
+ const files = fs.readdirSync(SKILLS_DIR).filter((f) => f.endsWith(".md"));
24
+ if (files.length === 0) {
25
+ throw new Error(`No skill files found in: ${SKILLS_DIR}`);
26
+ }
27
+ return files.map((file) => {
28
+ const content = fs.readFileSync(path.join(SKILLS_DIR, file), "utf-8");
29
+ const firstLine = content.split("\n")[0];
30
+ const description = firstLine.startsWith("# ") ? firstLine.slice(2).trim() : file.replace(".md", "");
31
+ return {
32
+ name: file.replace(".md", ""),
33
+ file,
34
+ description
35
+ };
36
+ });
37
+ }
38
+ function getInstalledSkills(cwd) {
39
+ const agentsDir = path.join(cwd, AGENTS_DIR);
40
+ if (!fs.existsSync(agentsDir)) {
41
+ return [];
42
+ }
43
+ return fs.readdirSync(agentsDir).filter((f) => f.endsWith(".md")).map((f) => f.replace(".md", ""));
44
+ }
45
+ function ensureAgentsDir(cwd) {
46
+ const agentsDir = path.join(cwd, AGENTS_DIR);
47
+ if (!fs.existsSync(agentsDir)) {
48
+ fs.mkdirSync(agentsDir, { recursive: true });
49
+ console.log(`Created ${AGENTS_DIR}/`);
50
+ }
51
+ }
52
+ function readAgentsMd(cwd) {
53
+ const agentsMdPath = path.join(cwd, AGENTS_MD);
54
+ if (!fs.existsSync(agentsMdPath)) {
55
+ return "";
56
+ }
57
+ return fs.readFileSync(agentsMdPath, "utf-8");
58
+ }
59
+ function writeAgentsMd(cwd, content) {
60
+ const agentsMdPath = path.join(cwd, AGENTS_MD);
61
+ fs.writeFileSync(agentsMdPath, content);
62
+ }
63
+ function getSkillDisplayName(skillName) {
64
+ try {
65
+ const skills = getAvailableSkills();
66
+ const skill = skills.find((s) => s.name === skillName);
67
+ return skill?.description ?? skillName;
68
+ } catch {
69
+ return skillName;
70
+ }
71
+ }
72
+ function updateAgentsMdReferences(cwd) {
73
+ const installed = getInstalledSkills(cwd);
74
+ let content = readAgentsMd(cwd);
75
+ if (installed.length === 0) {
76
+ if (content.includes(MARKER_START)) {
77
+ const regex = new RegExp(
78
+ `\\n?${MARKER_START}[\\s\\S]*?${MARKER_END}\\n?`,
79
+ "g"
80
+ );
81
+ content = content.replace(regex, "").trim();
82
+ if (content) {
83
+ writeAgentsMd(cwd, content + "\n");
84
+ } else {
85
+ const agentsMdPath = path.join(cwd, AGENTS_MD);
86
+ if (fs.existsSync(agentsMdPath)) {
87
+ fs.unlinkSync(agentsMdPath);
88
+ console.log(`Removed empty ${AGENTS_MD}`);
89
+ }
90
+ }
91
+ }
92
+ return;
93
+ }
94
+ const skillLinks = installed.map((name) => `- [${getSkillDisplayName(name)}](${AGENTS_DIR}/${name}.md)`).join("\n");
95
+ const newBlock = `${MARKER_START}
96
+ For recapt behavioral intelligence workflows, see:
97
+ ${skillLinks}
98
+ ${MARKER_END}`;
99
+ if (content.includes(MARKER_START)) {
100
+ const regex = new RegExp(`${MARKER_START}[\\s\\S]*?${MARKER_END}`, "g");
101
+ content = content.replace(regex, newBlock);
102
+ } else {
103
+ if (content.trim()) {
104
+ content = content.trim() + "\n\n" + newBlock;
105
+ } else {
106
+ content = `# Project Agent Instructions
107
+
108
+ ${newBlock}`;
109
+ }
110
+ }
111
+ writeAgentsMd(cwd, content + "\n");
112
+ }
113
+ function installSkill(cwd, skillName, force = false) {
114
+ let skills;
115
+ try {
116
+ skills = getAvailableSkills();
117
+ } catch (err) {
118
+ console.error(
119
+ `Failed to load skills: ${err instanceof Error ? err.message : err}`
120
+ );
121
+ return false;
122
+ }
123
+ const skill = skills.find((s) => s.name === skillName);
124
+ if (!skill) {
125
+ console.error(`Unknown skill: ${skillName}`);
126
+ console.error(`Available skills: ${skills.map((s) => s.name).join(", ")}`);
127
+ return false;
128
+ }
129
+ ensureAgentsDir(cwd);
130
+ const sourcePath = path.join(SKILLS_DIR, skill.file);
131
+ const destPath = path.join(cwd, AGENTS_DIR, skill.file);
132
+ if (fs.existsSync(destPath)) {
133
+ const sourceContent = fs.readFileSync(sourcePath, "utf-8");
134
+ const destContent = fs.readFileSync(destPath, "utf-8");
135
+ if (sourceContent === destContent) {
136
+ console.log(`Skill already up to date: ${skillName}`);
137
+ return true;
138
+ }
139
+ if (!force) {
140
+ console.log(`Skill outdated: ${skillName} (use --force to update)`);
141
+ return true;
142
+ }
143
+ fs.copyFileSync(sourcePath, destPath);
144
+ console.log(`Updated: ${skillName} \u2192 ${AGENTS_DIR}/${skill.file}`);
145
+ } else {
146
+ fs.copyFileSync(sourcePath, destPath);
147
+ console.log(`Installed: ${skillName} \u2192 ${AGENTS_DIR}/${skill.file}`);
148
+ }
149
+ updateAgentsMdReferences(cwd);
150
+ return true;
151
+ }
152
+ function uninstallSkill(cwd, skillName) {
153
+ const skillPath = path.join(cwd, AGENTS_DIR, `${skillName}.md`);
154
+ if (!fs.existsSync(skillPath)) {
155
+ console.error(`Skill not installed: ${skillName}`);
156
+ return false;
157
+ }
158
+ fs.unlinkSync(skillPath);
159
+ console.log(`Uninstalled: ${skillName}`);
160
+ updateAgentsMdReferences(cwd);
161
+ const agentsDir = path.join(cwd, AGENTS_DIR);
162
+ const remaining = fs.readdirSync(agentsDir);
163
+ if (remaining.length === 0) {
164
+ fs.rmdirSync(agentsDir);
165
+ const parentDir = path.dirname(agentsDir);
166
+ if (fs.existsSync(parentDir) && fs.readdirSync(parentDir).length === 0) {
167
+ fs.rmdirSync(parentDir);
168
+ }
169
+ console.log(`Removed empty ${AGENTS_DIR}/`);
170
+ }
171
+ return true;
172
+ }
173
+ function listSkills(cwd) {
174
+ let available;
175
+ try {
176
+ available = getAvailableSkills();
177
+ } catch (err) {
178
+ console.error(
179
+ `Failed to load skills: ${err instanceof Error ? err.message : err}`
180
+ );
181
+ process.exit(1);
182
+ }
183
+ const installed = new Set(getInstalledSkills(cwd));
184
+ console.log("\nAvailable recapt skills:\n");
185
+ for (const skill of available) {
186
+ const status = installed.has(skill.name) ? "[installed]" : "";
187
+ console.log(` ${skill.name.padEnd(20)} ${skill.description} ${status}`);
188
+ }
189
+ console.log();
190
+ }
191
+ function installAllSkills(cwd, force = false) {
192
+ let skills;
193
+ try {
194
+ skills = getAvailableSkills();
195
+ } catch (err) {
196
+ const message = err instanceof Error ? err.message : String(err);
197
+ console.error(`Failed to load skills: ${message}`);
198
+ return { success: false, installed: [], failed: [], error: message };
199
+ }
200
+ return installSelectedSkills(
201
+ cwd,
202
+ skills.map((s) => s.name),
203
+ force
204
+ );
205
+ }
206
+ function installSelectedSkills(cwd, skillNames, force = false) {
207
+ const installed = [];
208
+ const failed = [];
209
+ for (const skillName of skillNames) {
210
+ try {
211
+ if (installSkill(cwd, skillName, force)) {
212
+ installed.push(skillName);
213
+ } else {
214
+ failed.push(skillName);
215
+ }
216
+ } catch (err) {
217
+ console.error(`Failed to install ${skillName}: ${err}`);
218
+ failed.push(skillName);
219
+ }
220
+ }
221
+ const success2 = failed.length === 0 && installed.length > 0;
222
+ if (success2 && installed.length > 1) {
223
+ console.log(`
224
+ All skills installed. See ${AGENTS_MD} for references.`);
225
+ }
226
+ return { success: success2, installed, failed };
227
+ }
228
+ var listCommand = new Command("list").description("Show available skills").action(() => {
229
+ listSkills(process.cwd());
230
+ });
231
+ var installCommand = new Command("install").description("Install a skill").argument("[name]", "Skill name to install").option("-a, --all", "Install all skills").option("-f, --force", "Force update if skill already exists").action(
232
+ (name, options) => {
233
+ const cwd = process.cwd();
234
+ if (options.all) {
235
+ installAllSkills(cwd, options.force);
236
+ return;
237
+ }
238
+ if (!name) {
239
+ console.error(
240
+ "Usage: recapt skill install <name> or recapt skill install --all"
241
+ );
242
+ process.exit(1);
243
+ }
244
+ if (installSkill(cwd, name, options.force)) {
245
+ console.log(`
246
+ Skill reference added to ${AGENTS_MD}`);
247
+ }
248
+ }
249
+ );
250
+ var uninstallCommand = new Command("uninstall").description("Remove a skill").argument("<name>", "Skill name to uninstall").action((name) => {
251
+ uninstallSkill(process.cwd(), name);
252
+ });
253
+ var skillCommand = new Command("skill").description("Manage recapt workflow skills").addCommand(listCommand).addCommand(installCommand).addCommand(uninstallCommand);
254
+
255
+ // src/cli/commands/setup.ts
256
+ import { Command as Command2 } from "commander";
257
+ import { exec } from "child_process";
258
+
259
+ // src/cli/utils/ide-config.ts
260
+ import fs2 from "fs";
261
+ import path2 from "path";
262
+ import os from "os";
263
+ var IDE_CONFIGS = [
264
+ {
265
+ name: "Cursor",
266
+ globalPath: "~/.cursor/mcp.json",
267
+ projectPath: ".cursor/mcp.json",
268
+ rootKey: "mcpServers",
269
+ format: "json"
270
+ },
271
+ {
272
+ name: "Claude Code",
273
+ globalPath: "~/.claude.json",
274
+ projectPath: ".mcp.json",
275
+ rootKey: "mcpServers",
276
+ format: "json"
277
+ },
278
+ {
279
+ name: "Windsurf",
280
+ globalPath: "~/.windsurf/mcp.json",
281
+ projectPath: ".windsurf/mcp.json",
282
+ rootKey: "mcpServers",
283
+ format: "json"
284
+ },
285
+ {
286
+ name: "VS Code",
287
+ globalPath: null,
288
+ projectPath: ".vscode/mcp.json",
289
+ rootKey: "servers",
290
+ format: "json"
291
+ },
292
+ {
293
+ name: "Codex CLI",
294
+ globalPath: "~/.codex/config.toml",
295
+ projectPath: ".codex/config.toml",
296
+ rootKey: "mcp_servers",
297
+ format: "toml"
298
+ },
299
+ {
300
+ name: "Zed",
301
+ globalPath: "~/.config/zed/settings.json",
302
+ projectPath: ".zed/settings.json",
303
+ rootKey: "context_servers",
304
+ format: "json"
305
+ },
306
+ {
307
+ name: "GitHub Copilot",
308
+ globalPath: null,
309
+ projectPath: ".github/mcp.json",
310
+ rootKey: "servers",
311
+ format: "json"
312
+ }
313
+ ];
314
+ function expandPath(p) {
315
+ if (p.startsWith("~/")) {
316
+ return path2.join(os.homedir(), p.slice(2));
317
+ }
318
+ return p;
319
+ }
320
+ function ideExists(config) {
321
+ if (!config.globalPath) {
322
+ return false;
323
+ }
324
+ const resolved = expandPath(config.globalPath);
325
+ const dir = path2.dirname(resolved);
326
+ if (config.name === "Claude Code") {
327
+ return fs2.existsSync(resolved) || fs2.existsSync(path2.join(os.homedir(), ".claude"));
328
+ }
329
+ if (config.name === "Zed") {
330
+ return fs2.existsSync(path2.join(os.homedir(), ".config", "zed"));
331
+ }
332
+ if (config.name === "Codex CLI") {
333
+ return fs2.existsSync(path2.join(os.homedir(), ".codex"));
334
+ }
335
+ return fs2.existsSync(dir);
336
+ }
337
+ function detectInstalledIdes() {
338
+ return IDE_CONFIGS.map((config) => ({
339
+ config,
340
+ detected: ideExists(config),
341
+ globalPathResolved: config.globalPath ? expandPath(config.globalPath) : null
342
+ }));
343
+ }
344
+ function getRecaptServerConfig(secretKey) {
345
+ return {
346
+ type: "stdio",
347
+ command: "npx",
348
+ args: ["-y", "@recapt/mcp@latest"],
349
+ env: {
350
+ RECAPT_SECRET_KEY: secretKey
351
+ }
352
+ };
353
+ }
354
+ function readIdeConfig(configPath, format = "json") {
355
+ const resolved = expandPath(configPath);
356
+ if (!fs2.existsSync(resolved)) {
357
+ return {};
358
+ }
359
+ try {
360
+ const content = fs2.readFileSync(resolved, "utf-8");
361
+ if (format === "toml") {
362
+ return parseToml(content);
363
+ }
364
+ return JSON.parse(content);
365
+ } catch {
366
+ return {};
367
+ }
368
+ }
369
+ function parseToml(content) {
370
+ const result = {};
371
+ let currentSection = "";
372
+ let currentSubSection = "";
373
+ for (const line of content.split("\n")) {
374
+ const trimmed = line.trim();
375
+ if (!trimmed || trimmed.startsWith("#")) {
376
+ continue;
377
+ }
378
+ const sectionMatch = trimmed.match(/^\[([^\]]+)\]$/);
379
+ if (sectionMatch) {
380
+ const section = sectionMatch[1];
381
+ const parts = section.split(".");
382
+ if (parts.length === 1) {
383
+ currentSection = parts[0];
384
+ currentSubSection = "";
385
+ if (!result[currentSection]) {
386
+ result[currentSection] = {};
387
+ }
388
+ } else if (parts.length === 2) {
389
+ currentSection = parts[0];
390
+ currentSubSection = parts[1];
391
+ if (!result[currentSection]) {
392
+ result[currentSection] = {};
393
+ }
394
+ const sectionObj = result[currentSection];
395
+ if (!sectionObj[currentSubSection]) {
396
+ sectionObj[currentSubSection] = {};
397
+ }
398
+ }
399
+ continue;
400
+ }
401
+ const kvMatch = trimmed.match(/^(\w+)\s*=\s*(.+)$/);
402
+ if (kvMatch) {
403
+ const [, key, rawValue] = kvMatch;
404
+ const value = parseTomlValue(rawValue);
405
+ if (currentSubSection) {
406
+ const sectionObj = result[currentSection];
407
+ const subSectionObj = sectionObj[currentSubSection];
408
+ subSectionObj[key] = value;
409
+ } else if (currentSection) {
410
+ const sectionObj = result[currentSection];
411
+ sectionObj[key] = value;
412
+ } else {
413
+ result[key] = value;
414
+ }
415
+ }
416
+ }
417
+ return result;
418
+ }
419
+ function parseTomlValue(raw) {
420
+ const trimmed = raw.trim();
421
+ if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
422
+ return trimmed.slice(1, -1);
423
+ }
424
+ if (trimmed.startsWith("'") && trimmed.endsWith("'")) {
425
+ return trimmed.slice(1, -1);
426
+ }
427
+ if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
428
+ const inner = trimmed.slice(1, -1);
429
+ if (!inner.trim()) return [];
430
+ return inner.split(",").map((item) => {
431
+ const t = item.trim();
432
+ if (t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'")) {
433
+ return t.slice(1, -1);
434
+ }
435
+ return t;
436
+ });
437
+ }
438
+ if (trimmed === "true") return true;
439
+ if (trimmed === "false") return false;
440
+ const num = Number(trimmed);
441
+ if (!isNaN(num)) return num;
442
+ return trimmed;
443
+ }
444
+ function stringifyToml(obj, prefix = "") {
445
+ const lines = [];
446
+ for (const [key, value] of Object.entries(obj)) {
447
+ if (value === null || value === void 0) continue;
448
+ if (typeof value === "object" && !Array.isArray(value)) {
449
+ const sectionKey = prefix ? `${prefix}.${key}` : key;
450
+ const nested = value;
451
+ const hasNestedObjects = Object.values(nested).some(
452
+ (v) => typeof v === "object" && !Array.isArray(v)
453
+ );
454
+ if (hasNestedObjects) {
455
+ for (const [subKey, subValue] of Object.entries(nested)) {
456
+ if (typeof subValue === "object" && !Array.isArray(subValue)) {
457
+ lines.push(`[${sectionKey}.${subKey}]`);
458
+ for (const [k, v] of Object.entries(
459
+ subValue
460
+ )) {
461
+ lines.push(`${k} = ${tomlValue(v)}`);
462
+ }
463
+ lines.push("");
464
+ }
465
+ }
466
+ } else {
467
+ lines.push(`[${sectionKey}]`);
468
+ for (const [k, v] of Object.entries(nested)) {
469
+ lines.push(`${k} = ${tomlValue(v)}`);
470
+ }
471
+ lines.push("");
472
+ }
473
+ }
474
+ }
475
+ return lines.join("\n");
476
+ }
477
+ function tomlValue(value) {
478
+ if (typeof value === "string") {
479
+ return `"${value}"`;
480
+ }
481
+ if (Array.isArray(value)) {
482
+ return `[${value.map((v) => typeof v === "string" ? `"${v}"` : String(v)).join(", ")}]`;
483
+ }
484
+ return String(value);
485
+ }
486
+ function writeIdeConfig(configPath, config, format = "json") {
487
+ const resolved = expandPath(configPath);
488
+ const dir = path2.dirname(resolved);
489
+ if (!fs2.existsSync(dir)) {
490
+ fs2.mkdirSync(dir, { recursive: true });
491
+ }
492
+ if (format === "toml") {
493
+ fs2.writeFileSync(resolved, stringifyToml(config));
494
+ } else {
495
+ fs2.writeFileSync(resolved, JSON.stringify(config, null, 2) + "\n");
496
+ }
497
+ }
498
+ function addRecaptToIdeConfig(ide, secretKey, useGlobal = true) {
499
+ const configPath = useGlobal && ide.globalPath ? ide.globalPath : ide.projectPath;
500
+ const resolved = expandPath(configPath);
501
+ const existing = readIdeConfig(configPath, ide.format);
502
+ const rootKey = ide.rootKey;
503
+ const servers = existing[rootKey] || {};
504
+ servers["recapt"] = getRecaptServerConfig(secretKey);
505
+ existing[rootKey] = servers;
506
+ writeIdeConfig(configPath, existing, ide.format);
507
+ return resolved;
508
+ }
509
+
510
+ // src/cli/utils/prompts.ts
511
+ import {
512
+ confirm as inquirerConfirm,
513
+ input as inquirerInput,
514
+ password as inquirerPassword,
515
+ select as inquirerSelect,
516
+ checkbox as inquirerCheckbox
517
+ } from "@inquirer/prompts";
518
+ async function confirm(message, defaultYes = true) {
519
+ return inquirerConfirm({
520
+ message,
521
+ default: defaultYes
522
+ });
523
+ }
524
+ async function input(message, defaultValue) {
525
+ return inquirerInput({
526
+ message,
527
+ default: defaultValue
528
+ });
529
+ }
530
+ async function secret(message) {
531
+ const value = await inquirerPassword({
532
+ message,
533
+ mask: "*"
534
+ });
535
+ return value.trim();
536
+ }
537
+ async function multiSelect(message, options) {
538
+ return inquirerCheckbox({
539
+ message,
540
+ choices: options.map((opt) => ({
541
+ name: opt.label,
542
+ value: opt.value,
543
+ checked: opt.selected ?? false
544
+ }))
545
+ });
546
+ }
547
+ async function select(message, options) {
548
+ if (options.length === 0) {
549
+ return null;
550
+ }
551
+ return inquirerSelect({
552
+ message,
553
+ choices: options.map((opt) => ({
554
+ name: opt.label,
555
+ value: opt.value
556
+ }))
557
+ });
558
+ }
559
+ function print(message) {
560
+ console.log(message);
561
+ }
562
+ function success(message) {
563
+ console.log(`\u2713 ${message}`);
564
+ }
565
+ function error(message) {
566
+ console.error(`\u2717 ${message}`);
567
+ }
568
+ function info(message) {
569
+ console.log(`\u2139 ${message}`);
570
+ }
571
+ function warn(message) {
572
+ console.log(`\u26A0 ${message}`);
573
+ }
574
+ function newline() {
575
+ console.log();
576
+ }
577
+ function header(title) {
578
+ console.log();
579
+ console.log(title);
580
+ console.log("=".repeat(title.length));
581
+ console.log();
582
+ }
583
+
584
+ // src/cli/commands/setup.ts
585
+ var SETTINGS_URL = "https://dash.recapt.app/settings";
586
+ var MCP_KEY_PREFIX = "mcp_sk_";
587
+ function openBrowser(url) {
588
+ const platform = process.platform;
589
+ let command;
590
+ switch (platform) {
591
+ case "darwin":
592
+ command = `open "${url}"`;
593
+ break;
594
+ case "win32":
595
+ command = `start "" "${url}"`;
596
+ break;
597
+ default:
598
+ command = `xdg-open "${url}"`;
599
+ }
600
+ exec(command, (err) => {
601
+ if (err) {
602
+ info(`Could not open browser automatically. Please visit: ${url}`);
603
+ }
604
+ });
605
+ }
606
+ function validateMcpKey(key) {
607
+ return key.startsWith(MCP_KEY_PREFIX);
608
+ }
609
+ async function promptForKey() {
610
+ const hasKey = await confirm("Do you have a Recapt MCP key?", false);
611
+ if (!hasKey) {
612
+ newline();
613
+ info(`Opening ${SETTINGS_URL} (MCP Keys tab)...`);
614
+ info("Create a new MCP key and copy it.");
615
+ newline();
616
+ openBrowser(SETTINGS_URL);
617
+ await new Promise((resolve) => setTimeout(resolve, 2e3));
618
+ }
619
+ const key = await secret("Paste your MCP key");
620
+ if (!key) {
621
+ error("No key provided.");
622
+ return null;
623
+ }
624
+ if (!validateMcpKey(key)) {
625
+ error(
626
+ `Invalid MCP key format. Keys should start with "${MCP_KEY_PREFIX}".`
627
+ );
628
+ return null;
629
+ }
630
+ return key;
631
+ }
632
+ async function runSetup() {
633
+ header("Recapt MCP Setup");
634
+ const detectedIdes = detectInstalledIdes();
635
+ const installedIdes = detectedIdes.filter((ide) => ide.detected);
636
+ print("Detected AI IDEs:");
637
+ newline();
638
+ for (const ide of detectedIdes) {
639
+ const marker = ide.detected ? "[x]" : "[ ]";
640
+ const status = ide.detected ? `(${ide.globalPathResolved})` : "(not found)";
641
+ print(` ${marker} ${ide.config.name} ${status}`);
642
+ }
643
+ newline();
644
+ if (installedIdes.length === 0) {
645
+ error("No supported AI IDEs detected.");
646
+ info("Supported IDEs: Cursor, Claude Code, Windsurf, VS Code");
647
+ info("Make sure at least one is installed before running setup.");
648
+ process.exit(1);
649
+ }
650
+ const options = installedIdes.map((ide) => ({
651
+ label: ide.config.name,
652
+ value: ide.config,
653
+ selected: true
654
+ }));
655
+ const selectedIdes = await multiSelect(
656
+ "Which IDE(s) do you want to configure?",
657
+ options
658
+ );
659
+ if (selectedIdes.length === 0) {
660
+ info("No IDEs selected. Exiting.");
661
+ return;
662
+ }
663
+ newline();
664
+ const mcpKey = await promptForKey();
665
+ if (!mcpKey) {
666
+ process.exit(1);
667
+ }
668
+ newline();
669
+ print("Writing configuration...");
670
+ newline();
671
+ const configuredPaths = [];
672
+ for (const ide of selectedIdes) {
673
+ try {
674
+ const configPath = addRecaptToIdeConfig(ide, mcpKey, true);
675
+ success(`${ide.name}: ${configPath}`);
676
+ configuredPaths.push(configPath);
677
+ } catch (err) {
678
+ error(`${ide.name}: Failed to write config - ${err}`);
679
+ }
680
+ }
681
+ if (configuredPaths.length === 0) {
682
+ error("Failed to configure any IDEs.");
683
+ process.exit(1);
684
+ }
685
+ newline();
686
+ const shouldInstallSkills = await confirm("Install recapt skills?", true);
687
+ if (shouldInstallSkills) {
688
+ newline();
689
+ let availableSkills;
690
+ try {
691
+ availableSkills = getAvailableSkills();
692
+ } catch (err) {
693
+ error(
694
+ `Failed to load skills: ${err instanceof Error ? err.message : err}`
695
+ );
696
+ availableSkills = [];
697
+ }
698
+ if (availableSkills.length > 0) {
699
+ const skillOptions = [
700
+ { label: "Install all skills", value: "__all__", selected: true },
701
+ ...availableSkills.map((skill) => ({
702
+ label: `${skill.name} - ${skill.description}`,
703
+ value: skill.name,
704
+ selected: false
705
+ }))
706
+ ];
707
+ const selectedSkills = await multiSelect(
708
+ "Which skills do you want to install?",
709
+ skillOptions
710
+ );
711
+ if (selectedSkills.length > 0) {
712
+ newline();
713
+ print("Installing skills...");
714
+ newline();
715
+ const cwd = process.cwd();
716
+ let result;
717
+ if (selectedSkills.includes("__all__")) {
718
+ result = installAllSkills(cwd);
719
+ } else {
720
+ result = installSelectedSkills(cwd, selectedSkills);
721
+ }
722
+ if (result.error) {
723
+ error(`Failed to load skills: ${result.error}`);
724
+ } else if (result.installed.length > 0) {
725
+ success(
726
+ `Installed ${result.installed.length} skill(s): ${result.installed.join(", ")}`
727
+ );
728
+ if (result.failed.length > 0) {
729
+ warn(`Failed to install: ${result.failed.join(", ")}`);
730
+ }
731
+ } else if (result.failed.length > 0) {
732
+ error(`Failed to install skills: ${result.failed.join(", ")}`);
733
+ } else {
734
+ info("No skills were installed.");
735
+ }
736
+ } else {
737
+ info("No skills selected.");
738
+ }
739
+ }
740
+ }
741
+ newline();
742
+ success("Setup complete!");
743
+ newline();
744
+ info("Restart your IDE(s) to activate the MCP server.");
745
+ newline();
746
+ }
747
+ var setupCommand = new Command2("setup").description("Configure recapt MCP server for your AI IDE(s)").action(async () => {
748
+ try {
749
+ await runSetup();
750
+ } catch (err) {
751
+ error(`Setup failed: ${err}`);
752
+ process.exit(1);
753
+ }
754
+ });
755
+
756
+ // src/cli/commands/setup-self-improvement-gh.ts
757
+ import { Command as Command3 } from "commander";
758
+ import { exec as exec2, execSync } from "child_process";
759
+ import fs3 from "fs";
760
+ import path3 from "path";
761
+ import { fileURLToPath as fileURLToPath2 } from "url";
762
+ var __filename3 = fileURLToPath2(import.meta.url);
763
+ var __dirname3 = path3.dirname(__filename3);
764
+ var TEMPLATES_DIR = path3.resolve(__dirname3, "../../../templates");
765
+ var WORKFLOW_TEMPLATE = "self-improvement.md";
766
+ var WORKFLOW_DEST = ".github/workflows/self-improvement.md";
767
+ var ENGINES = [
768
+ { id: "claude", name: "Claude (Anthropic)", secretName: "ANTHROPIC_API_KEY" },
769
+ { id: "copilot", name: "GitHub Copilot", secretName: "COPILOT_GITHUB_TOKEN" },
770
+ { id: "codex", name: "Codex (OpenAI)", secretName: "OPENAI_API_KEY" }
771
+ ];
772
+ var API_URL = process.env.RECAPT_API_URL || process.env.EXTERNAL_API_URL || "https://api.recapt.app";
773
+ async function fetchDomains(secretKey) {
774
+ try {
775
+ const res = await fetch(`${API_URL}/v1beta/query/domains`, {
776
+ headers: {
777
+ "x-private-key": secretKey,
778
+ "Content-Type": "application/json"
779
+ }
780
+ });
781
+ if (!res.ok) {
782
+ return null;
783
+ }
784
+ const data = await res.json();
785
+ return data;
786
+ } catch {
787
+ return null;
788
+ }
789
+ }
790
+ function commandExists(cmd) {
791
+ try {
792
+ execSync(`which ${cmd}`, { stdio: "ignore" });
793
+ return true;
794
+ } catch {
795
+ return false;
796
+ }
797
+ }
798
+ function ghExtensionInstalled(extension) {
799
+ try {
800
+ const output = execSync("gh extension list", { encoding: "utf-8" });
801
+ return output.includes(extension);
802
+ } catch {
803
+ return false;
804
+ }
805
+ }
806
+ function isGitRepo() {
807
+ try {
808
+ execSync("git rev-parse --is-inside-work-tree", { stdio: "ignore" });
809
+ return true;
810
+ } catch {
811
+ return false;
812
+ }
813
+ }
814
+ function getRepoUrl() {
815
+ try {
816
+ const url = execSync("gh repo view --json url -q .url", {
817
+ encoding: "utf-8"
818
+ }).trim();
819
+ return url || null;
820
+ } catch {
821
+ return null;
822
+ }
823
+ }
824
+ function openBrowser2(url) {
825
+ const platform = process.platform;
826
+ let command;
827
+ switch (platform) {
828
+ case "darwin":
829
+ command = `open "${url}"`;
830
+ break;
831
+ case "win32":
832
+ command = `start "" "${url}"`;
833
+ break;
834
+ default:
835
+ command = `xdg-open "${url}"`;
836
+ }
837
+ exec2(command, (err) => {
838
+ if (err) {
839
+ info(`Could not open browser automatically. Please visit: ${url}`);
840
+ }
841
+ });
842
+ }
843
+ async function runSetupSelfImprovementGh() {
844
+ header("Recapt Self-Improvement GitHub Setup");
845
+ const cwd = process.cwd();
846
+ print("Checking prerequisites...");
847
+ newline();
848
+ const hasGh = commandExists("gh");
849
+ print(` ${hasGh ? "[x]" : "[ ]"} gh CLI installed`);
850
+ if (!hasGh) {
851
+ newline();
852
+ error("GitHub CLI (gh) is required but not installed.");
853
+ info("Install it from: https://cli.github.com/");
854
+ process.exit(1);
855
+ }
856
+ const hasGhAw = ghExtensionInstalled("github/gh-aw");
857
+ print(` ${hasGhAw ? "[x]" : "[ ]"} gh-aw extension installed`);
858
+ if (!hasGhAw) {
859
+ newline();
860
+ const shouldInstall = await confirm(
861
+ "gh-aw extension is required. Install it now?",
862
+ true
863
+ );
864
+ if (shouldInstall) {
865
+ print("Installing gh-aw extension...");
866
+ try {
867
+ execSync("gh extension install github/gh-aw", { stdio: "inherit" });
868
+ success("gh-aw extension installed");
869
+ } catch {
870
+ error("Failed to install gh-aw extension");
871
+ info("Try manually: gh extension install github/gh-aw");
872
+ process.exit(1);
873
+ }
874
+ } else {
875
+ error("gh-aw extension is required to continue.");
876
+ process.exit(1);
877
+ }
878
+ }
879
+ const inGitRepo = isGitRepo();
880
+ print(` ${inGitRepo ? "[x]" : "[ ]"} Git repository detected`);
881
+ if (!inGitRepo) {
882
+ newline();
883
+ error("This command must be run from within a git repository.");
884
+ process.exit(1);
885
+ }
886
+ newline();
887
+ const engineOptions = ENGINES.map((engine) => ({
888
+ label: `${engine.name} (requires ${engine.secretName} secret)`,
889
+ value: engine
890
+ }));
891
+ const selectedEngine = await select(
892
+ "Which AI engine do you want to use?",
893
+ engineOptions
894
+ );
895
+ if (!selectedEngine) {
896
+ error("No engine selected.");
897
+ process.exit(1);
898
+ }
899
+ newline();
900
+ print("Configuring domain filter...");
901
+ info("You can optionally limit the workflow to specific domains.");
902
+ newline();
903
+ let selectedDomains = [];
904
+ const recaptKey = await input(
905
+ "Enter your RECAPT_SECRET_KEY to fetch domains (or press Enter to skip)"
906
+ );
907
+ if (recaptKey) {
908
+ const domains = await fetchDomains(recaptKey);
909
+ if (domains && domains.length > 0) {
910
+ const domainOptions = [
911
+ { label: "All domains", value: "__all__", selected: true },
912
+ ...domains.map((d) => ({
913
+ label: d.domainName,
914
+ value: d.domainName,
915
+ selected: false
916
+ }))
917
+ ];
918
+ const selected = await multiSelect(
919
+ "Which domains should this workflow analyze?",
920
+ domainOptions
921
+ );
922
+ if (!selected.includes("__all__") && selected.length > 0) {
923
+ selectedDomains = selected;
924
+ success(`Selected domains: ${selectedDomains.join(", ")}`);
925
+ } else {
926
+ info("Will analyze all domains");
927
+ }
928
+ } else {
929
+ warn("Could not fetch domains. Will analyze all domains.");
930
+ }
931
+ } else {
932
+ const manualDomain = await input(
933
+ "Enter a specific domain to filter (or press Enter for all domains)"
934
+ );
935
+ if (manualDomain) {
936
+ selectedDomains = [manualDomain];
937
+ success(`Selected domain: ${manualDomain}`);
938
+ } else {
939
+ info("Will analyze all domains");
940
+ }
941
+ }
942
+ newline();
943
+ print("Installing self-improvement skill...");
944
+ const skillInstalled = installSkill(cwd, "self-improvement", true);
945
+ if (!skillInstalled) {
946
+ error("Failed to install self-improvement skill");
947
+ process.exit(1);
948
+ }
949
+ newline();
950
+ print("Creating workflow...");
951
+ const templatePath = path3.join(TEMPLATES_DIR, WORKFLOW_TEMPLATE);
952
+ const destPath = path3.join(cwd, WORKFLOW_DEST);
953
+ const destDir = path3.dirname(destPath);
954
+ if (!fs3.existsSync(templatePath)) {
955
+ error(`Workflow template not found: ${templatePath}`);
956
+ process.exit(1);
957
+ }
958
+ if (!fs3.existsSync(destDir)) {
959
+ fs3.mkdirSync(destDir, { recursive: true });
960
+ }
961
+ const templateContent = fs3.readFileSync(templatePath, "utf-8");
962
+ let domainConfig = "";
963
+ if (selectedDomains.length > 0) {
964
+ const domainList = selectedDomains.join(", ");
965
+ domainConfig = `### Domain Restriction
966
+
967
+ **Target domains:** ${domainList}
968
+
969
+ Before doing any work:
970
+ 1. Call \`get_domains\` to verify the target domain(s) exist
971
+ 2. If any target domain is NOT in the list, stop immediately and report: "Domain not found: [domain]. Available domains: [list]"
972
+ 3. If domains are valid, pass the \`domain\` parameter to all tools that support filtering (e.g., \`run_full_diagnostic\`, \`get_page_metrics\`, etc.)
973
+
974
+ Do NOT analyze or fix issues for domains not in the target list.
975
+
976
+ `;
977
+ } else {
978
+ domainConfig = `### Domain Scope
979
+
980
+ **Target:** All domains
981
+
982
+ Analyze and fix issues across all domains configured in recapt. Call \`get_domains\` first to see available domains.
983
+
984
+ `;
985
+ }
986
+ const workflowContent = templateContent.replace(/\{\s*\{\s*ENGINE\s*\}\s*\}/g, selectedEngine.id).replace(/\{\s*\{\s*DOMAIN_CONFIG\s*\}\s*\}/g, domainConfig);
987
+ if (fs3.existsSync(destPath)) {
988
+ const overwrite = await confirm(
989
+ "Workflow file already exists. Overwrite?",
990
+ false
991
+ );
992
+ if (!overwrite) {
993
+ info("Skipping workflow creation");
994
+ } else {
995
+ fs3.writeFileSync(destPath, workflowContent);
996
+ success(`Created: ${WORKFLOW_DEST}`);
997
+ }
998
+ } else {
999
+ fs3.writeFileSync(destPath, workflowContent);
1000
+ success(`Created: ${WORKFLOW_DEST}`);
1001
+ }
1002
+ newline();
1003
+ print("Compiling workflow...");
1004
+ try {
1005
+ execSync("gh aw compile", { cwd, stdio: "inherit" });
1006
+ success("Created: .github/workflows/self-improvement.lock.yml");
1007
+ } catch {
1008
+ warn("Failed to compile workflow. You can run 'gh aw compile' manually.");
1009
+ }
1010
+ newline();
1011
+ print("GitHub Secrets Required:");
1012
+ info(
1013
+ `1. ${selectedEngine.secretName} - For ${selectedEngine.name} to run the agent`
1014
+ );
1015
+ info("2. RECAPT_SECRET_KEY - For recapt behavioral intelligence");
1016
+ newline();
1017
+ const repoUrl = getRepoUrl();
1018
+ if (repoUrl) {
1019
+ const secretsUrl = `${repoUrl}/settings/secrets/actions`;
1020
+ const openSecrets = await confirm("Open GitHub secrets page?", true);
1021
+ if (openSecrets) {
1022
+ openBrowser2(secretsUrl);
1023
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
1024
+ }
1025
+ } else {
1026
+ info("Go to your repository Settings \u2192 Secrets and variables \u2192 Actions");
1027
+ info(`Add ${selectedEngine.secretName} and RECAPT_SECRET_KEY as secrets.`);
1028
+ }
1029
+ newline();
1030
+ success("Setup complete!");
1031
+ newline();
1032
+ print("Next steps:");
1033
+ print(
1034
+ ' 1. git add . && git commit -m "Add recapt self-improvement workflow"'
1035
+ );
1036
+ print(" 2. git push");
1037
+ print(" 3. Run manually: gh aw run self-improvement");
1038
+ newline();
1039
+ warn("Important: Enable PR creation permissions");
1040
+ info("The workflow needs permission to create pull requests.");
1041
+ info("Go to Settings \u2192 Actions \u2192 General \u2192 Workflow permissions");
1042
+ info('Enable "Allow GitHub Actions to create and approve pull requests"');
1043
+ info("(This may need to be set at the organization level)");
1044
+ newline();
1045
+ }
1046
+ var setupSelfImprovementGhCommand = new Command3(
1047
+ "setup-self-improvement-gh"
1048
+ ).description("Set up automated self-improvement workflow for GitHub Actions").action(async () => {
1049
+ try {
1050
+ await runSetupSelfImprovementGh();
1051
+ } catch (err) {
1052
+ error(`Setup failed: ${err}`);
1053
+ process.exit(1);
1054
+ }
1055
+ });
1056
+
1057
+ // src/cli/index.ts
1058
+ var require2 = createRequire(import.meta.url);
1059
+ var pkg = require2("../../package.json");
1060
+ var program = new Command4();
1061
+ program.name("recapt").description("Recapt MCP CLI - Configure and manage recapt for AI IDEs").version(pkg.version);
24
1062
  program.addCommand(setupCommand);
25
1063
  program.addCommand(setupSelfImprovementGhCommand);
26
1064
  program.addCommand(skillCommand);