@reconcrap/boss-recruit-mcp 1.0.15 → 1.0.16
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/README.md +11 -1
- package/package.json +1 -1
- package/src/cli.js +229 -40
package/README.md
CHANGED
|
@@ -25,10 +25,12 @@ npx @reconcrap/boss-recruit-mcp install
|
|
|
25
25
|
- 安装 Codex skill 到 `$CODEX_HOME/skills/boss-recruit-pipeline`
|
|
26
26
|
- 初始化用户配置到 `$CODEX_HOME/boss-recruit-mcp/screening-config.json`
|
|
27
27
|
- 生成通用 MCP 配置模板到 `$CODEX_HOME/boss-recruit-mcp/agent-mcp-configs`
|
|
28
|
+
- 自动尝试写入已检测到的外部 agent MCP 配置(含 Trae / trae-cn / Cursor / Claude / OpenClaw)
|
|
29
|
+
- 自动尝试把 skill 镜像到已检测到的外部 agent skills 目录
|
|
28
30
|
- 包内自带 `boss-search-cli` 与 `boss-screen-cli` 运行时文件,无需额外目录结构
|
|
29
31
|
- 不包含 `favorite-calibration.json`,首次使用前需要自行校准生成
|
|
30
32
|
|
|
31
|
-
## 跨 Agent 快速接入(Cursor / Trae / Claude Code / OpenClaw)
|
|
33
|
+
## 跨 Agent 快速接入(Cursor / Trae / trae-cn / Claude Code / OpenClaw)
|
|
32
34
|
|
|
33
35
|
生成 MCP 配置模板:
|
|
34
36
|
|
|
@@ -58,6 +60,7 @@ $CODEX_HOME/boss-recruit-mcp/agent-mcp-configs
|
|
|
58
60
|
boss-recruit-mcp mcp-config --client cursor
|
|
59
61
|
boss-recruit-mcp mcp-config --client claudecode
|
|
60
62
|
boss-recruit-mcp mcp-config --client trae
|
|
63
|
+
boss-recruit-mcp mcp-config --client trae-cn
|
|
61
64
|
boss-recruit-mcp mcp-config --client openclaw
|
|
62
65
|
boss-recruit-mcp mcp-config --client generic
|
|
63
66
|
```
|
|
@@ -73,6 +76,13 @@ boss-recruit-mcp mcp-config --client generic
|
|
|
73
76
|
boss-recruit-mcp mcp-config --client generic --command boss-recruit-mcp --args-json "[\"start\"]"
|
|
74
77
|
```
|
|
75
78
|
|
|
79
|
+
可选环境变量(用于跨 agent 自动配置):
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
BOSS_RECRUIT_MCP_CONFIG_TARGETS # JSON 数组或系统 path 分隔路径列表,指定额外 mcp.json 目标文件
|
|
83
|
+
BOSS_RECRUIT_EXTERNAL_SKILL_DIRS # JSON 数组或系统 path 分隔路径列表,指定额外 skills 根目录
|
|
84
|
+
```
|
|
85
|
+
|
|
76
86
|
## 准备配置
|
|
77
87
|
|
|
78
88
|
1. 初始化后编辑用户配置文件:
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -28,14 +28,16 @@ const SUPPORTED_MCP_CLIENTS = ["generic", "cursor", "trae", "claudecode", "openc
|
|
|
28
28
|
const DEFAULT_MCP_SERVER_NAME = "boss-recruit";
|
|
29
29
|
const DEFAULT_MCP_COMMAND = "npx";
|
|
30
30
|
const DEFAULT_MCP_ARGS = ["-y", "@reconcrap/boss-recruit-mcp@latest", "start"];
|
|
31
|
-
const AUTO_SYNC_SKIP_COMMANDS = new Set([
|
|
32
|
-
"install",
|
|
33
|
-
"install-skill",
|
|
34
|
-
"where",
|
|
35
|
-
"help",
|
|
36
|
-
"--help",
|
|
37
|
-
"-h"
|
|
38
|
-
]);
|
|
31
|
+
const AUTO_SYNC_SKIP_COMMANDS = new Set([
|
|
32
|
+
"install",
|
|
33
|
+
"install-skill",
|
|
34
|
+
"where",
|
|
35
|
+
"help",
|
|
36
|
+
"--help",
|
|
37
|
+
"-h"
|
|
38
|
+
]);
|
|
39
|
+
const EXTERNAL_MCP_TARGETS_ENV = "BOSS_RECRUIT_MCP_CONFIG_TARGETS";
|
|
40
|
+
const EXTERNAL_SKILL_DIRS_ENV = "BOSS_RECRUIT_EXTERNAL_SKILL_DIRS";
|
|
39
41
|
|
|
40
42
|
function getPackageVersion() {
|
|
41
43
|
try {
|
|
@@ -58,13 +60,46 @@ function getCodexHome() {
|
|
|
58
60
|
: path.join(os.homedir(), ".codex");
|
|
59
61
|
}
|
|
60
62
|
|
|
61
|
-
function ensureDir(targetPath) {
|
|
62
|
-
fs.mkdirSync(targetPath, { recursive: true });
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function
|
|
66
|
-
|
|
67
|
-
|
|
63
|
+
function ensureDir(targetPath) {
|
|
64
|
+
fs.mkdirSync(targetPath, { recursive: true });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function pathExists(targetPath) {
|
|
68
|
+
try {
|
|
69
|
+
return fs.existsSync(targetPath);
|
|
70
|
+
} catch {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function readJsonObjectFileSafe(filePath) {
|
|
76
|
+
if (!pathExists(filePath)) return {};
|
|
77
|
+
try {
|
|
78
|
+
const parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
79
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
80
|
+
return parsed;
|
|
81
|
+
}
|
|
82
|
+
} catch {
|
|
83
|
+
// Fallback below.
|
|
84
|
+
}
|
|
85
|
+
return {};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function dedupePaths(items) {
|
|
89
|
+
const result = [];
|
|
90
|
+
const seen = new Set();
|
|
91
|
+
for (const item of items || []) {
|
|
92
|
+
const resolved = path.resolve(String(item || ""));
|
|
93
|
+
if (!resolved || seen.has(resolved)) continue;
|
|
94
|
+
seen.add(resolved);
|
|
95
|
+
result.push(resolved);
|
|
96
|
+
}
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function getDesktopDir() {
|
|
101
|
+
return path.join(os.homedir(), "Desktop");
|
|
102
|
+
}
|
|
68
103
|
|
|
69
104
|
function getUserConfigPath() {
|
|
70
105
|
return path.join(getCodexHome(), "boss-recruit-mcp", "screening-config.json");
|
|
@@ -146,12 +181,13 @@ function parseStringArrayOption(value, label) {
|
|
|
146
181
|
return parsed;
|
|
147
182
|
}
|
|
148
183
|
|
|
149
|
-
function normalizeMcpClientName(value) {
|
|
150
|
-
const raw = String(value || "").trim().toLowerCase();
|
|
151
|
-
if (!raw) return "";
|
|
152
|
-
if (raw === "claude-code") return "claudecode";
|
|
153
|
-
return
|
|
154
|
-
|
|
184
|
+
function normalizeMcpClientName(value) {
|
|
185
|
+
const raw = String(value || "").trim().toLowerCase();
|
|
186
|
+
if (!raw) return "";
|
|
187
|
+
if (raw === "claude-code") return "claudecode";
|
|
188
|
+
if (raw === "trae-cn") return "trae";
|
|
189
|
+
return raw;
|
|
190
|
+
}
|
|
155
191
|
|
|
156
192
|
function parseMcpClientTargets(rawValue) {
|
|
157
193
|
if (!rawValue) return SUPPORTED_MCP_CLIENTS.slice();
|
|
@@ -214,7 +250,7 @@ function buildMcpConfigFileContent(options = {}) {
|
|
|
214
250
|
};
|
|
215
251
|
}
|
|
216
252
|
|
|
217
|
-
function writeMcpConfigFiles(options = {}) {
|
|
253
|
+
function writeMcpConfigFiles(options = {}) {
|
|
218
254
|
const clients = parseMcpClientTargets(options.client);
|
|
219
255
|
const outputDir = getAgentConfigOutputDir(options);
|
|
220
256
|
ensureDir(outputDir);
|
|
@@ -227,8 +263,142 @@ function writeMcpConfigFiles(options = {}) {
|
|
|
227
263
|
files.push({ client, file: filePath });
|
|
228
264
|
}
|
|
229
265
|
|
|
230
|
-
return { outputDir, files };
|
|
231
|
-
}
|
|
266
|
+
return { outputDir, files };
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function parsePathListFromEnv(raw) {
|
|
270
|
+
if (!raw) return [];
|
|
271
|
+
const text = String(raw).trim();
|
|
272
|
+
if (!text) return [];
|
|
273
|
+
try {
|
|
274
|
+
const parsed = JSON.parse(text);
|
|
275
|
+
if (Array.isArray(parsed)) {
|
|
276
|
+
return dedupePaths(parsed.filter(Boolean));
|
|
277
|
+
}
|
|
278
|
+
} catch {
|
|
279
|
+
// Fallback to delimiter split.
|
|
280
|
+
}
|
|
281
|
+
return dedupePaths(
|
|
282
|
+
text
|
|
283
|
+
.split(path.delimiter)
|
|
284
|
+
.map((item) => item.trim())
|
|
285
|
+
.filter(Boolean)
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function getKnownExternalMcpConfigPaths() {
|
|
290
|
+
const home = os.homedir();
|
|
291
|
+
const appData = process.env.APPDATA || path.join(home, "AppData", "Roaming");
|
|
292
|
+
return dedupePaths([
|
|
293
|
+
path.join(appData, "Cursor", "User", "mcp.json"),
|
|
294
|
+
path.join(appData, "Trae", "User", "mcp.json"),
|
|
295
|
+
path.join(appData, "Trae CN", "User", "mcp.json"),
|
|
296
|
+
path.join(home, ".trae", "mcp.json"),
|
|
297
|
+
path.join(home, ".trae-cn", "mcp.json"),
|
|
298
|
+
path.join(home, ".claude", "mcp.json"),
|
|
299
|
+
path.join(home, ".openclaw", "mcp.json")
|
|
300
|
+
]);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function resolveExternalMcpConfigTargets() {
|
|
304
|
+
const fromEnv = parsePathListFromEnv(process.env[EXTERNAL_MCP_TARGETS_ENV]);
|
|
305
|
+
const known = getKnownExternalMcpConfigPaths().filter((filePath) => {
|
|
306
|
+
if (pathExists(filePath)) return true;
|
|
307
|
+
return pathExists(path.dirname(filePath));
|
|
308
|
+
});
|
|
309
|
+
return dedupePaths([...fromEnv, ...known]);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function mergeMcpServerConfigFile(filePath, options = {}) {
|
|
313
|
+
const nextConfig = buildMcpConfigFileContent(options);
|
|
314
|
+
const serverName = Object.keys(nextConfig.mcpServers || {})[0] || DEFAULT_MCP_SERVER_NAME;
|
|
315
|
+
const launchConfig = nextConfig.mcpServers?.[serverName] || buildMcpLaunchConfig(options);
|
|
316
|
+
const current = readJsonObjectFileSafe(filePath);
|
|
317
|
+
const existingServers =
|
|
318
|
+
current?.mcpServers && typeof current.mcpServers === "object" && !Array.isArray(current.mcpServers)
|
|
319
|
+
? current.mcpServers
|
|
320
|
+
: {};
|
|
321
|
+
const existingEntry = existingServers[serverName];
|
|
322
|
+
const merged = {
|
|
323
|
+
...current,
|
|
324
|
+
mcpServers: {
|
|
325
|
+
...existingServers,
|
|
326
|
+
[serverName]: launchConfig
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
ensureDir(path.dirname(filePath));
|
|
331
|
+
fs.writeFileSync(filePath, JSON.stringify(merged, null, 2), "utf8");
|
|
332
|
+
const updated = JSON.stringify(existingEntry || null) !== JSON.stringify(launchConfig);
|
|
333
|
+
return {
|
|
334
|
+
file: filePath,
|
|
335
|
+
server: serverName,
|
|
336
|
+
updated
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function installExternalMcpConfigs(options = {}) {
|
|
341
|
+
const targets = resolveExternalMcpConfigTargets();
|
|
342
|
+
const applied = [];
|
|
343
|
+
const skipped = [];
|
|
344
|
+
for (const target of targets) {
|
|
345
|
+
try {
|
|
346
|
+
const existed = pathExists(target);
|
|
347
|
+
const merged = mergeMcpServerConfigFile(target, options);
|
|
348
|
+
applied.push({
|
|
349
|
+
file: target,
|
|
350
|
+
server: merged.server,
|
|
351
|
+
created: !existed,
|
|
352
|
+
updated: merged.updated
|
|
353
|
+
});
|
|
354
|
+
} catch (error) {
|
|
355
|
+
skipped.push({
|
|
356
|
+
file: target,
|
|
357
|
+
reason: error.message
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return { targets, applied, skipped };
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function getKnownExternalSkillBaseDirs() {
|
|
365
|
+
const home = os.homedir();
|
|
366
|
+
const appData = process.env.APPDATA || path.join(home, "AppData", "Roaming");
|
|
367
|
+
return dedupePaths([
|
|
368
|
+
path.join(home, ".cursor", "skills"),
|
|
369
|
+
path.join(home, ".trae", "skills"),
|
|
370
|
+
path.join(home, ".trae-cn", "skills"),
|
|
371
|
+
path.join(home, ".claude", "skills"),
|
|
372
|
+
path.join(home, ".openclaw", "skills"),
|
|
373
|
+
path.join(appData, "Cursor", "User", "skills"),
|
|
374
|
+
path.join(appData, "Trae", "User", "skills"),
|
|
375
|
+
path.join(appData, "Trae CN", "User", "skills"),
|
|
376
|
+
path.join(appData, "OpenClaw", "User", "skills")
|
|
377
|
+
]);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function resolveExternalSkillBaseDirs() {
|
|
381
|
+
const fromEnv = parsePathListFromEnv(process.env[EXTERNAL_SKILL_DIRS_ENV]);
|
|
382
|
+
const known = getKnownExternalSkillBaseDirs().filter((dirPath) => pathExists(dirPath));
|
|
383
|
+
return dedupePaths([...fromEnv, ...known]);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function mirrorSkillToExternalDirs() {
|
|
387
|
+
const baseDirs = resolveExternalSkillBaseDirs();
|
|
388
|
+
const mirrored = [];
|
|
389
|
+
const skipped = [];
|
|
390
|
+
for (const baseDir of baseDirs) {
|
|
391
|
+
try {
|
|
392
|
+
const targetDir = path.join(baseDir, skillName);
|
|
393
|
+
ensureDir(path.dirname(targetDir));
|
|
394
|
+
fs.cpSync(skillSourceDir, targetDir, { recursive: true, force: true });
|
|
395
|
+
mirrored.push({ base_dir: baseDir, target_dir: targetDir });
|
|
396
|
+
} catch (error) {
|
|
397
|
+
skipped.push({ base_dir: baseDir, reason: error.message });
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
return { baseDirs, mirrored, skipped };
|
|
401
|
+
}
|
|
232
402
|
|
|
233
403
|
function readTextFile(filePath, label) {
|
|
234
404
|
const resolved = path.resolve(String(filePath));
|
|
@@ -909,7 +1079,7 @@ async function launchChrome(options) {
|
|
|
909
1079
|
};
|
|
910
1080
|
}
|
|
911
1081
|
|
|
912
|
-
function printHelp() {
|
|
1082
|
+
function printHelp() {
|
|
913
1083
|
console.log("boss-recruit-mcp");
|
|
914
1084
|
console.log("");
|
|
915
1085
|
console.log("Usage:");
|
|
@@ -920,7 +1090,7 @@ function printHelp() {
|
|
|
920
1090
|
console.log(" boss-recruit-mcp install-skill Install only the Codex skill");
|
|
921
1091
|
console.log(" boss-recruit-mcp init-config Create ~/.codex/boss-recruit-mcp/screening-config.json if missing");
|
|
922
1092
|
console.log(" boss-recruit-mcp set-port Persist preferred Chrome debug port to active screening-config");
|
|
923
|
-
console.log(" boss-recruit-mcp mcp-config Generate MCP config JSON for Cursor/Trae/Claude Code/OpenClaw");
|
|
1093
|
+
console.log(" boss-recruit-mcp mcp-config Generate MCP config JSON for Cursor/Trae(含 trae-cn)/Claude Code/OpenClaw");
|
|
924
1094
|
console.log(" boss-recruit-mcp doctor Check config, calibration, and runtime prerequisites");
|
|
925
1095
|
console.log(" boss-recruit-mcp calibrate Auto-open Boss search page, then run favorite-button calibration");
|
|
926
1096
|
console.log(" boss-recruit-mcp launch-chrome Reuse existing Chrome debug instance when possible; otherwise launch one, open Boss search, and check login state");
|
|
@@ -972,22 +1142,41 @@ function printMcpConfig(options = {}) {
|
|
|
972
1142
|
console.log("2. Merge its mcpServers block into that client's MCP config.");
|
|
973
1143
|
}
|
|
974
1144
|
|
|
975
|
-
function installAll() {
|
|
976
|
-
const skillTarget = installSkill();
|
|
977
|
-
const configResult = ensureUserConfig();
|
|
978
|
-
const mcpTemplateResult = writeMcpConfigFiles({ client: "all" });
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
console.log(`Config
|
|
1145
|
+
function installAll() {
|
|
1146
|
+
const skillTarget = installSkill();
|
|
1147
|
+
const configResult = ensureUserConfig();
|
|
1148
|
+
const mcpTemplateResult = writeMcpConfigFiles({ client: "all" });
|
|
1149
|
+
const externalMcpResult = installExternalMcpConfigs({});
|
|
1150
|
+
const externalSkillResult = mirrorSkillToExternalDirs();
|
|
1151
|
+
console.log(`Skill installed to: ${skillTarget}`);
|
|
1152
|
+
if (configResult.created) {
|
|
1153
|
+
console.log(`Config template created at: ${configResult.path}`);
|
|
1154
|
+
} else {
|
|
1155
|
+
console.log(`Config already exists at: ${configResult.path}`);
|
|
984
1156
|
}
|
|
985
1157
|
console.log(`MCP config templates exported to: ${mcpTemplateResult.outputDir}`);
|
|
986
|
-
for (const item of mcpTemplateResult.files) {
|
|
987
|
-
console.log(`- ${item.client}: ${item.file}`);
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
|
|
1158
|
+
for (const item of mcpTemplateResult.files) {
|
|
1159
|
+
console.log(`- ${item.client}: ${item.file}`);
|
|
1160
|
+
}
|
|
1161
|
+
if (externalMcpResult.targets.length > 0) {
|
|
1162
|
+
console.log(`Auto-configured external MCP files: ${externalMcpResult.applied.length}`);
|
|
1163
|
+
for (const item of externalMcpResult.applied) {
|
|
1164
|
+
const action = item.created ? "created" : item.updated ? "updated" : "unchanged";
|
|
1165
|
+
console.log(`- ${item.file} (${action})`);
|
|
1166
|
+
}
|
|
1167
|
+
} else {
|
|
1168
|
+
console.log("No external MCP config target detected. Set BOSS_RECRUIT_MCP_CONFIG_TARGETS to auto-configure custom agents.");
|
|
1169
|
+
}
|
|
1170
|
+
if (externalSkillResult.baseDirs.length > 0) {
|
|
1171
|
+
console.log(`Mirrored skill to external dirs: ${externalSkillResult.mirrored.length}`);
|
|
1172
|
+
for (const item of externalSkillResult.mirrored) {
|
|
1173
|
+
console.log(`- ${item.target_dir}`);
|
|
1174
|
+
}
|
|
1175
|
+
} else {
|
|
1176
|
+
console.log("No external skill dir detected. Set BOSS_RECRUIT_EXTERNAL_SKILL_DIRS to mirror skill for non-Codex agents.");
|
|
1177
|
+
}
|
|
1178
|
+
console.log("");
|
|
1179
|
+
console.log("Next steps:");
|
|
991
1180
|
console.log("1. Fill in baseUrl/apiKey/model in the config file above.");
|
|
992
1181
|
console.log("2. Choose a client template from the exported MCP config files and merge it into your AI client config.");
|
|
993
1182
|
console.log("3. Choose a Chrome remote-debugging port (9222 is recommended, but you can reuse an existing port).");
|