@recapt/mcp 0.0.5-beta → 0.0.7-beta
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/dist/api/client.js +24 -75
- package/dist/cli/commands/setup.d.ts +7 -0
- package/dist/cli/commands/setup.js +178 -0
- package/dist/cli/commands/skill.d.ts +23 -0
- package/dist/cli/commands/skill.js +251 -0
- package/dist/cli/index.d.ts +11 -0
- package/dist/cli/index.js +24 -0
- package/dist/cli/skill.d.ts +14 -0
- package/dist/cli/skill.js +249 -0
- package/dist/cli/utils/ide-config.d.ts +31 -0
- package/dist/cli/utils/ide-config.js +294 -0
- package/dist/cli/utils/prompts.d.ts +22 -0
- package/dist/cli/utils/prompts.js +133 -0
- package/dist/index.js +7 -33
- package/dist/tools/catalog/searchTools.js +55 -10
- package/dist/tools/catalog/toolCatalog.json +16246 -0
- package/package.json +8 -5
- package/skills/deep-dive.md +92 -0
- package/skills/regression-hunt.md +120 -0
- package/skills/self-healing.md +95 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive Prompts
|
|
3
|
+
*
|
|
4
|
+
* Readline-based prompts for CLI interactions.
|
|
5
|
+
*/
|
|
6
|
+
import readline from "readline";
|
|
7
|
+
function createInterface() {
|
|
8
|
+
return readline.createInterface({
|
|
9
|
+
input: process.stdin,
|
|
10
|
+
output: process.stdout,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export async function confirm(message, defaultYes = true) {
|
|
14
|
+
const hint = defaultYes ? "(Y/n)" : "(y/N)";
|
|
15
|
+
const askOnce = () => {
|
|
16
|
+
const rl = createInterface();
|
|
17
|
+
return new Promise((resolve) => {
|
|
18
|
+
rl.question(`${message} ${hint} `, (answer) => {
|
|
19
|
+
rl.close();
|
|
20
|
+
const normalized = answer.trim().toLowerCase();
|
|
21
|
+
if (normalized === "") {
|
|
22
|
+
resolve(defaultYes);
|
|
23
|
+
}
|
|
24
|
+
else if (normalized === "y" || normalized === "yes") {
|
|
25
|
+
resolve(true);
|
|
26
|
+
}
|
|
27
|
+
else if (normalized === "n" || normalized === "no") {
|
|
28
|
+
resolve(false);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
resolve(null);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
let result = await askOnce();
|
|
37
|
+
while (result === null) {
|
|
38
|
+
console.log('Please enter "y" or "n"');
|
|
39
|
+
result = await askOnce();
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
export async function input(message, defaultValue) {
|
|
44
|
+
const rl = createInterface();
|
|
45
|
+
const hint = defaultValue ? ` (${defaultValue})` : "";
|
|
46
|
+
return new Promise((resolve) => {
|
|
47
|
+
rl.question(`${message}${hint}: `, (answer) => {
|
|
48
|
+
rl.close();
|
|
49
|
+
const value = answer.trim();
|
|
50
|
+
resolve(value || defaultValue || "");
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
export async function secret(message) {
|
|
55
|
+
const rl = createInterface();
|
|
56
|
+
return new Promise((resolve) => {
|
|
57
|
+
rl.question(`${message}: `, (answer) => {
|
|
58
|
+
rl.close();
|
|
59
|
+
resolve(answer.trim());
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
export async function multiSelect(message, options) {
|
|
64
|
+
const rl = createInterface();
|
|
65
|
+
console.log(`\n${message}`);
|
|
66
|
+
console.log("(Enter comma-separated numbers, or 'all' for all options)\n");
|
|
67
|
+
options.forEach((opt, i) => {
|
|
68
|
+
const marker = opt.selected ? "[x]" : "[ ]";
|
|
69
|
+
console.log(` ${i + 1}. ${marker} ${opt.label}`);
|
|
70
|
+
});
|
|
71
|
+
return new Promise((resolve) => {
|
|
72
|
+
rl.question("\nYour selection: ", (answer) => {
|
|
73
|
+
rl.close();
|
|
74
|
+
const normalized = answer.trim().toLowerCase();
|
|
75
|
+
if (normalized === "all" || normalized === "a") {
|
|
76
|
+
resolve(options.map((o) => o.value));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (normalized === "" || normalized === "none" || normalized === "n") {
|
|
80
|
+
resolve(options.filter((o) => o.selected).map((o) => o.value));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const indices = normalized
|
|
84
|
+
.split(/[,\s]+/)
|
|
85
|
+
.map((s) => parseInt(s, 10) - 1)
|
|
86
|
+
.filter((i) => i >= 0 && i < options.length);
|
|
87
|
+
resolve(indices.map((i) => options[i].value));
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
export async function select(message, options) {
|
|
92
|
+
const rl = createInterface();
|
|
93
|
+
console.log(`\n${message}\n`);
|
|
94
|
+
options.forEach((opt, i) => {
|
|
95
|
+
console.log(` ${i + 1}. ${opt.label}`);
|
|
96
|
+
});
|
|
97
|
+
return new Promise((resolve) => {
|
|
98
|
+
rl.question("\nYour selection: ", (answer) => {
|
|
99
|
+
rl.close();
|
|
100
|
+
const index = parseInt(answer.trim(), 10) - 1;
|
|
101
|
+
if (index >= 0 && index < options.length) {
|
|
102
|
+
resolve(options[index].value);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
resolve(null);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
export function print(message) {
|
|
111
|
+
console.log(message);
|
|
112
|
+
}
|
|
113
|
+
export function success(message) {
|
|
114
|
+
console.log(`✓ ${message}`);
|
|
115
|
+
}
|
|
116
|
+
export function error(message) {
|
|
117
|
+
console.error(`✗ ${message}`);
|
|
118
|
+
}
|
|
119
|
+
export function info(message) {
|
|
120
|
+
console.log(`ℹ ${message}`);
|
|
121
|
+
}
|
|
122
|
+
export function warn(message) {
|
|
123
|
+
console.log(`⚠ ${message}`);
|
|
124
|
+
}
|
|
125
|
+
export function newline() {
|
|
126
|
+
console.log();
|
|
127
|
+
}
|
|
128
|
+
export function header(title) {
|
|
129
|
+
console.log();
|
|
130
|
+
console.log(title);
|
|
131
|
+
console.log("=".repeat(title.length));
|
|
132
|
+
console.log();
|
|
133
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -223,29 +223,6 @@ All behavioral scores are 0-1 where higher = more of that signal:
|
|
|
223
223
|
- **drop_off_rate** > 0.3: Major leak point in a flow
|
|
224
224
|
- **spike_ratio** > 2: Critical frustration spike, likely recent regression
|
|
225
225
|
|
|
226
|
-
## Common Workflows
|
|
227
|
-
|
|
228
|
-
### Starting point - understand the landscape
|
|
229
|
-
1. \`get_domains\` to see tracked domains
|
|
230
|
-
2. \`run_full_diagnostic\` for comprehensive overview
|
|
231
|
-
3. Or search: "site overview" → scan_site, list_pages, get_ux_health_report
|
|
232
|
-
|
|
233
|
-
### Diagnosing a problematic page
|
|
234
|
-
Search: "page analysis" or "element friction" → get_page_metrics, get_element_friction, get_dead_clicks, get_console_errors, get_form_friction
|
|
235
|
-
|
|
236
|
-
### Understanding user flows
|
|
237
|
-
Search: "user navigation" or "funnel" → analyze_flow, analyze_funnel, get_journey_patterns, get_flow_friction
|
|
238
|
-
|
|
239
|
-
### Finding issues to fix
|
|
240
|
-
1. \`run_full_diagnostic\` (always available)
|
|
241
|
-
2. Or search: "issues" → get_issues, get_actionable_issues, get_anomalies, detect_regressions
|
|
242
|
-
|
|
243
|
-
### Comparing segments
|
|
244
|
-
Search: "compare users" or "cohorts" → compare_cohorts, compare_periods, discover_personas
|
|
245
|
-
|
|
246
|
-
### Deep-diving a session
|
|
247
|
-
Search: "session details" → search_sessions, list_sessions, get_session_details, predict_outcomes
|
|
248
|
-
|
|
249
226
|
## Reasoning Tips
|
|
250
227
|
|
|
251
228
|
- High rage clicks on body/root elements often indicate JS errors - search "console errors"
|
|
@@ -254,17 +231,14 @@ Search: "session details" → search_sessions, list_sessions, get_session_detail
|
|
|
254
231
|
- High frustration + low confusion = users know what to do but can't (broken UI)
|
|
255
232
|
- Use \`memory_save\` to store intermediate results when doing multi-step analysis
|
|
256
233
|
|
|
257
|
-
##
|
|
234
|
+
## Installable Skills
|
|
258
235
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
4. **Fix**: search "propose fix" → propose_fix, get_similar_fixes, get_fix_history
|
|
266
|
-
5. **Track**: search "deployment" → confirm_deployment, evaluate_fix, list_pending_fixes
|
|
267
|
-
6. **Learn**: search "site knowledge" → get_site_knowledge, add_site_knowledge`;
|
|
236
|
+
For guided workflows (self-healing, deep-dive analysis, regression hunting), install recapt skills:
|
|
237
|
+
\`\`\`bash
|
|
238
|
+
npx @recapt/mcp skill list
|
|
239
|
+
npx @recapt/mcp skill install self-healing
|
|
240
|
+
\`\`\`
|
|
241
|
+
Skills provide step-by-step guidance for complex multi-tool workflows.`;
|
|
268
242
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
269
243
|
// Main
|
|
270
244
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -38,20 +38,65 @@ function cosineSimilarity(a, b) {
|
|
|
38
38
|
}
|
|
39
39
|
function searchByKeyword(query, limit) {
|
|
40
40
|
const catalog = loadCatalog();
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
.
|
|
41
|
+
const normalizedQuery = query.toLowerCase();
|
|
42
|
+
// Extract words, keeping short ones that might be meaningful (e.g., "ux", "js")
|
|
43
|
+
const queryWords = normalizedQuery
|
|
44
|
+
.split(/[\s_-]+/)
|
|
45
|
+
.filter((w) => w.length >= 2);
|
|
46
|
+
// Also check for the full query as a phrase
|
|
47
|
+
const queryPhrases = [normalizedQuery];
|
|
48
|
+
// Common synonyms and related terms
|
|
49
|
+
const synonyms = {
|
|
50
|
+
error: ["console", "js", "javascript", "bug", "crash", "exception"],
|
|
51
|
+
page: ["pages", "route", "url", "path"],
|
|
52
|
+
user: ["users", "session", "visitor"],
|
|
53
|
+
click: ["clicks", "tap", "press", "rage"],
|
|
54
|
+
form: ["forms", "input", "field", "submit"],
|
|
55
|
+
flow: ["flows", "journey", "funnel", "navigation", "path"],
|
|
56
|
+
issue: ["issues", "problem", "bug", "friction"],
|
|
57
|
+
fix: ["fixes", "remediation", "repair", "resolve"],
|
|
58
|
+
compare: ["comparison", "diff", "versus", "cohort"],
|
|
59
|
+
health: ["score", "metrics", "ux"],
|
|
60
|
+
dead: ["unresponsive", "broken", "stuck"],
|
|
61
|
+
rage: ["angry", "frustrated", "frustration"],
|
|
62
|
+
};
|
|
63
|
+
// Expand query words with synonyms
|
|
64
|
+
const expandedWords = new Set(queryWords);
|
|
65
|
+
for (const word of queryWords) {
|
|
66
|
+
if (synonyms[word]) {
|
|
67
|
+
synonyms[word].forEach((syn) => expandedWords.add(syn));
|
|
68
|
+
}
|
|
69
|
+
// Also check if query word is a synonym value
|
|
70
|
+
for (const [key, values] of Object.entries(synonyms)) {
|
|
71
|
+
if (values.includes(word)) {
|
|
72
|
+
expandedWords.add(key);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
45
76
|
return catalog
|
|
46
77
|
.map((tool) => {
|
|
47
|
-
const
|
|
78
|
+
const toolName = tool.name.toLowerCase().replace(/_/g, " ");
|
|
79
|
+
const toolNameParts = tool.name.toLowerCase().split("_");
|
|
80
|
+
const text = `${toolName} ${tool.description} ${tool.category}`.toLowerCase();
|
|
48
81
|
let score = 0;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
82
|
+
// Phrase match in description (highest value)
|
|
83
|
+
for (const phrase of queryPhrases) {
|
|
84
|
+
if (phrase.length > 3 && text.includes(phrase))
|
|
85
|
+
score += 5;
|
|
86
|
+
}
|
|
87
|
+
// Word matches
|
|
88
|
+
for (const word of expandedWords) {
|
|
89
|
+
// Exact word in tool name parts (e.g., "page" matches "get_page_metrics")
|
|
90
|
+
if (toolNameParts.includes(word))
|
|
91
|
+
score += 4;
|
|
92
|
+
// Word appears in tool name
|
|
93
|
+
if (toolName.includes(word))
|
|
94
|
+
score += 3;
|
|
95
|
+
// Category match
|
|
54
96
|
if (tool.category.toLowerCase() === word)
|
|
97
|
+
score += 2;
|
|
98
|
+
// Word in description
|
|
99
|
+
if (text.includes(word))
|
|
55
100
|
score += 1;
|
|
56
101
|
}
|
|
57
102
|
return { tool, score };
|