@reconcrap/boss-recommend-mcp 1.2.10 → 1.3.0

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 (34) hide show
  1. package/README.md +82 -1
  2. package/package.json +2 -1
  3. package/skills/boss-chat/README.md +5 -0
  4. package/skills/boss-chat/SKILL.md +69 -0
  5. package/skills/boss-recommend-pipeline/SKILL.md +40 -4
  6. package/src/adapters.js +19 -5
  7. package/src/boss-chat.js +436 -0
  8. package/src/cli.js +294 -129
  9. package/src/index.js +459 -108
  10. package/src/pipeline.js +605 -8
  11. package/src/run-state.js +5 -0
  12. package/src/test-adapters-runtime.js +69 -0
  13. package/src/test-boss-chat.js +399 -0
  14. package/src/test-index-async.js +238 -4
  15. package/src/test-pipeline.js +408 -1
  16. package/vendor/boss-chat-cli/README.md +134 -0
  17. package/vendor/boss-chat-cli/package.json +53 -0
  18. package/vendor/boss-chat-cli/src/app.js +769 -0
  19. package/vendor/boss-chat-cli/src/browser/chat-page.js +2681 -0
  20. package/vendor/boss-chat-cli/src/cli.js +1350 -0
  21. package/vendor/boss-chat-cli/src/mcp/server.js +149 -0
  22. package/vendor/boss-chat-cli/src/mcp/tool-runtime.js +193 -0
  23. package/vendor/boss-chat-cli/src/runtime/async-run-state.js +260 -0
  24. package/vendor/boss-chat-cli/src/runtime/interaction.js +102 -0
  25. package/vendor/boss-chat-cli/src/runtime/run-control.js +102 -0
  26. package/vendor/boss-chat-cli/src/services/chrome-client.js +97 -0
  27. package/vendor/boss-chat-cli/src/services/llm.js +352 -0
  28. package/vendor/boss-chat-cli/src/services/profile-store.js +157 -0
  29. package/vendor/boss-chat-cli/src/services/report-store.js +19 -0
  30. package/vendor/boss-chat-cli/src/services/resume-capture.js +554 -0
  31. package/vendor/boss-chat-cli/src/services/state-store.js +217 -0
  32. package/vendor/boss-chat-cli/src/utils/customer-key.js +82 -0
  33. package/vendor/boss-recommend-screen-cli/boss-recommend-screen-cli.cjs +902 -56
  34. package/vendor/boss-recommend-screen-cli/test-recoverable-resume-failures.cjs +387 -1
package/src/cli.js CHANGED
@@ -16,6 +16,14 @@ import {
16
16
  switchRecommendTab,
17
17
  waitRecommendFeaturedDetailReady
18
18
  } from "./adapters.js";
19
+ import {
20
+ cancelBossChatRun,
21
+ getBossChatHealthCheck,
22
+ getBossChatRun,
23
+ pauseBossChatRun,
24
+ resumeBossChatRun,
25
+ startBossChatRun
26
+ } from "./boss-chat.js";
19
27
  import { runRecommendPipeline } from "./pipeline.js";
20
28
 
21
29
  const require = createRequire(import.meta.url);
@@ -23,7 +31,7 @@ const currentFilePath = fileURLToPath(import.meta.url);
23
31
  const packageRoot = path.resolve(path.dirname(currentFilePath), "..");
24
32
  const packageJsonPath = path.join(packageRoot, "package.json");
25
33
  const skillName = "boss-recommend-pipeline";
26
- const skillSourceDir = path.join(packageRoot, "skills", skillName);
34
+ const bundledSkillNames = [skillName, "boss-chat"];
27
35
  const exampleConfigPath = path.join(packageRoot, "config", "screening-config.example.json");
28
36
  const bossUrl = "https://www.zhipin.com/web/chat/recommend";
29
37
  const chromeOnboardingUrlPattern = /^chrome:\/\/(welcome|intro|newtab|signin|history-sync|settings\/syncSetup)/i;
@@ -37,6 +45,10 @@ const autoSyncSkipCommands = new Set(["install", "install-skill", "where", "help
37
45
  const externalMcpTargetsEnv = "BOSS_RECOMMEND_MCP_CONFIG_TARGETS";
38
46
  const externalSkillDirsEnv = "BOSS_RECOMMEND_EXTERNAL_SKILL_DIRS";
39
47
 
48
+ function getSkillSourceDir(name = skillName) {
49
+ return path.join(packageRoot, "skills", name);
50
+ }
51
+
40
52
  function getPackageVersion() {
41
53
  try {
42
54
  const parsed = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
@@ -137,16 +149,16 @@ function getLegacyUserConfigPath() {
137
149
  return path.join(getCodexHome(), "boss-recommend-mcp", "screening-config.json");
138
150
  }
139
151
 
140
- function getSkillTargetDir() {
141
- return path.join(getCodexHome(), "skills", skillName);
152
+ function getSkillTargetDir(name = skillName) {
153
+ return path.join(getCodexHome(), "skills", name);
142
154
  }
143
155
 
144
- function getSkillVersionMarkerPath() {
145
- return path.join(getSkillTargetDir(), ".installed-version");
156
+ function getSkillVersionMarkerPath(name = skillName) {
157
+ return path.join(getSkillTargetDir(name), ".installed-version");
146
158
  }
147
159
 
148
- function readInstalledSkillVersion() {
149
- const markerPath = getSkillVersionMarkerPath();
160
+ function readInstalledSkillVersion(name = skillName) {
161
+ const markerPath = getSkillVersionMarkerPath(name);
150
162
  if (!fs.existsSync(markerPath)) return null;
151
163
  try {
152
164
  return fs.readFileSync(markerPath, "utf8").trim() || null;
@@ -155,8 +167,8 @@ function readInstalledSkillVersion() {
155
167
  }
156
168
  }
157
169
 
158
- function writeInstalledSkillVersion(version) {
159
- const markerPath = getSkillVersionMarkerPath();
170
+ function writeInstalledSkillVersion(name, version) {
171
+ const markerPath = getSkillVersionMarkerPath(name);
160
172
  ensureDir(path.dirname(markerPath));
161
173
  fs.writeFileSync(markerPath, `${version}\n`, "utf8");
162
174
  }
@@ -183,6 +195,15 @@ function parsePositivePort(raw) {
183
195
  return Number.isFinite(port) && port > 0 ? port : null;
184
196
  }
185
197
 
198
+ function parseBooleanOption(raw, fallback = undefined) {
199
+ if (raw === undefined || raw === null || raw === "") return fallback;
200
+ if (raw === true) return true;
201
+ const normalized = String(raw).trim().toLowerCase();
202
+ if (["true", "1", "yes", "y", "on"].includes(normalized)) return true;
203
+ if (["false", "0", "no", "n", "off"].includes(normalized)) return false;
204
+ return fallback;
205
+ }
206
+
186
207
  function normalizePageScope(value) {
187
208
  const normalized = String(value || "").trim().toLowerCase();
188
209
  if (!normalized) return null;
@@ -259,6 +280,13 @@ function getRunOverrides(options) {
259
280
  return parseJsonOption(options["overrides-json"], "overrides");
260
281
  }
261
282
 
283
+ function getRunFollowUp(options) {
284
+ if (typeof options["follow-up-file"] === "string" && options["follow-up-file"].trim()) {
285
+ return parseJsonOption(readTextFile(options["follow-up-file"], "follow_up"), "follow_up");
286
+ }
287
+ return parseJsonOption(options["follow-up-json"], "follow_up");
288
+ }
289
+
262
290
  function normalizeMcpClientName(value) {
263
291
  const raw = String(value || "").trim().toLowerCase();
264
292
  if (!raw) return "";
@@ -542,13 +570,15 @@ function mirrorSkillToExternalDirs(options = {}) {
542
570
  const mirrored = [];
543
571
  const skipped = [];
544
572
  for (const baseDir of baseDirs) {
545
- try {
546
- const targetDir = path.join(baseDir, skillName);
547
- ensureDir(path.dirname(targetDir));
548
- fs.cpSync(skillSourceDir, targetDir, { recursive: true, force: true });
549
- mirrored.push({ base_dir: baseDir, target_dir: targetDir });
550
- } catch (error) {
551
- skipped.push({ base_dir: baseDir, reason: error.message });
573
+ for (const bundledSkillName of bundledSkillNames) {
574
+ try {
575
+ const targetDir = path.join(baseDir, bundledSkillName);
576
+ ensureDir(path.dirname(targetDir));
577
+ fs.cpSync(getSkillSourceDir(bundledSkillName), targetDir, { recursive: true, force: true });
578
+ mirrored.push({ base_dir: baseDir, target_dir: targetDir, skill: bundledSkillName });
579
+ } catch (error) {
580
+ skipped.push({ base_dir: baseDir, skill: bundledSkillName, reason: error.message });
581
+ }
552
582
  }
553
583
  }
554
584
  return { baseDirs, mirrored, skipped };
@@ -556,17 +586,29 @@ function mirrorSkillToExternalDirs(options = {}) {
556
586
 
557
587
  function syncSkillAssets(options = {}) {
558
588
  const force = options.force === true;
559
- const targetDir = getSkillTargetDir();
560
- const skillEntry = path.join(targetDir, "SKILL.md");
561
- const installedVersion = readInstalledSkillVersion();
562
- const needsSync = force || !fs.existsSync(skillEntry) || installedVersion !== packageVersion;
563
- if (!needsSync) {
564
- return { targetDir, updated: false, installedVersion, packageVersion };
589
+ const results = [];
590
+ for (const bundledSkillName of bundledSkillNames) {
591
+ const targetDir = getSkillTargetDir(bundledSkillName);
592
+ const skillEntry = path.join(targetDir, "SKILL.md");
593
+ const installedVersion = readInstalledSkillVersion(bundledSkillName);
594
+ const needsSync = force || !fs.existsSync(skillEntry) || installedVersion !== packageVersion;
595
+ if (needsSync) {
596
+ ensureDir(path.dirname(targetDir));
597
+ fs.cpSync(getSkillSourceDir(bundledSkillName), targetDir, { recursive: true, force: true });
598
+ writeInstalledSkillVersion(bundledSkillName, packageVersion);
599
+ }
600
+ results.push({
601
+ skill: bundledSkillName,
602
+ targetDir,
603
+ updated: needsSync,
604
+ installedVersion,
605
+ packageVersion
606
+ });
565
607
  }
566
- ensureDir(path.dirname(targetDir));
567
- fs.cpSync(skillSourceDir, targetDir, { recursive: true, force: true });
568
- writeInstalledSkillVersion(packageVersion);
569
- return { targetDir, updated: true, installedVersion, packageVersion };
608
+ return {
609
+ primaryTargetDir: results[0]?.targetDir || null,
610
+ results
611
+ };
570
612
  }
571
613
 
572
614
  function ensureAssetsUpToDate(command) {
@@ -579,7 +621,7 @@ function ensureAssetsUpToDate(command) {
579
621
  }
580
622
 
581
623
  function installSkill() {
582
- return syncSkillAssets({ force: true }).targetDir;
624
+ return syncSkillAssets({ force: true }).results;
583
625
  }
584
626
 
585
627
  function pathStartsWith(filePath, rootPath) {
@@ -1163,10 +1205,10 @@ function printPaths() {
1163
1205
  const stateHome = getStateHome();
1164
1206
  const calibrationResolution = getFeaturedCalibrationResolution(process.cwd());
1165
1207
  console.log(`package_root=${packageRoot}`);
1166
- console.log(`skill_source=${skillSourceDir}`);
1208
+ console.log(`skill_sources=${bundledSkillNames.map((name) => getSkillSourceDir(name)).join(" | ")}`);
1167
1209
  console.log(`codex_home=${codexHome}`);
1168
1210
  console.log(`state_home=${stateHome}`);
1169
- console.log(`skill_target=${path.join(codexHome, "skills", skillName)}`);
1211
+ console.log(`skill_targets=${bundledSkillNames.map((name) => path.join(codexHome, "skills", name)).join(" | ")}`);
1170
1212
  console.log(`config_target=${getUserConfigPath()}`);
1171
1213
  console.log(`legacy_config_target=${getLegacyUserConfigPath()}`);
1172
1214
  console.log(`calibration_target=${calibrationResolution.calibration_path}`);
@@ -1181,8 +1223,9 @@ function printHelp() {
1181
1223
  console.log(" boss-recommend-mcp Start the MCP server");
1182
1224
  console.log(" boss-recommend-mcp start Start the MCP server");
1183
1225
  console.log(" boss-recommend-mcp run Run the recommend pipeline once via CLI and print JSON");
1226
+ console.log(" boss-recommend-mcp chat <subcommand> Run bundled boss-chat commands via the recommend package");
1184
1227
  console.log(" boss-recommend-mcp install Install skill/MCP templates and auto-init screening-config.json (supports --agent trae-cn/cursor/...)");
1185
- console.log(" boss-recommend-mcp install-skill Install only the Codex skill");
1228
+ console.log(" boss-recommend-mcp install-skill Install bundled Codex skills");
1186
1229
  console.log(" boss-recommend-mcp init-config Create screening-config.json if missing (prefer workspace config/, fallback ~/.boss-recommend-mcp)");
1187
1230
  console.log(" boss-recommend-mcp config set Write baseUrl/apiKey/model (prefer workspace config/, fallback ~/.boss-recommend-mcp)");
1188
1231
  console.log(" boss-recommend-mcp set-port Persist preferred Chrome debug port to screening-config.json");
@@ -1193,7 +1236,8 @@ function printHelp() {
1193
1236
  console.log(" boss-recommend-mcp where Print installed package, skill, and config paths");
1194
1237
  console.log("");
1195
1238
  console.log("Run command:");
1196
- console.log(" boss-recommend-mcp run --instruction \"推荐页上筛选211男生,近14天没有,有大模型平台经验\" [--confirmation-json '{...}'] [--overrides-json '{...}']");
1239
+ console.log(" boss-recommend-mcp run --instruction \"推荐页上筛选211男生,近14天没有,有大模型平台经验\" [--confirmation-json '{...}'] [--overrides-json '{...}'] [--follow-up-json '{...}']");
1240
+ console.log(" boss-recommend-mcp chat run --job \"算法工程师\" --start-from unread --criteria \"有 AI Agent 经验\" --targetCount 20 # 后台启动,不自动轮询");
1197
1241
  console.log(" boss-recommend-mcp config set --base-url <url> --api-key <key> --model <model> [--openai-organization <id>] [--openai-project <id>]");
1198
1242
  console.log(" boss-recommend-mcp install --agent trae-cn");
1199
1243
  console.log(" boss-recommend-mcp doctor --agent trae-cn --page-scope featured");
@@ -1214,12 +1258,15 @@ function printMcpConfig(options = {}) {
1214
1258
  }
1215
1259
 
1216
1260
  function installAll(options = {}) {
1217
- const skillTarget = installSkill();
1261
+ const skillResults = installSkill();
1218
1262
  const configResult = ensureUserConfig(options);
1219
1263
  const mcpTemplateResult = writeMcpConfigFiles({ client: "all" });
1220
1264
  const externalMcpResult = installExternalMcpConfigs(options);
1221
1265
  const externalSkillResult = mirrorSkillToExternalDirs(options);
1222
- console.log(`Skill installed to: ${skillTarget}`);
1266
+ console.log(`Bundled skills installed: ${skillResults.length}`);
1267
+ for (const item of skillResults) {
1268
+ console.log(`- ${item.skill}: ${item.targetDir}`);
1269
+ }
1223
1270
  console.log(
1224
1271
  configResult.created
1225
1272
  ? `screening-config.json created: ${configResult.path}`
@@ -1256,6 +1303,7 @@ async function runPipelineOnce(options) {
1256
1303
  const instruction = getRunInstruction(options);
1257
1304
  const confirmation = getRunConfirmation(options);
1258
1305
  const overrides = getRunOverrides(options);
1306
+ const followUp = getRunFollowUp(options);
1259
1307
  const workspaceRoot = getWorkspaceRoot(options);
1260
1308
  const explicitPort = parsePositivePort(options.port);
1261
1309
  if (explicitPort) {
@@ -1267,78 +1315,168 @@ async function runPipelineOnce(options) {
1267
1315
  workspaceRoot,
1268
1316
  instruction,
1269
1317
  confirmation,
1270
- overrides
1318
+ overrides,
1319
+ followUp
1271
1320
  });
1272
1321
  printJson(result);
1273
1322
  }
1274
1323
 
1275
- const command = process.argv[2] || "start";
1276
- const options = parseOptions(process.argv.slice(3));
1277
- ensureAssetsUpToDate(command);
1324
+ function buildBossChatCliInput(options = {}) {
1325
+ return {
1326
+ profile: typeof options.profile === "string" ? options.profile.trim() : undefined,
1327
+ job: typeof options.job === "string" ? options.job.trim() : undefined,
1328
+ start_from: String(options["start-from"] || options.start_from || "").trim().toLowerCase() || undefined,
1329
+ criteria: typeof options.criteria === "string" ? options.criteria.trim() : undefined,
1330
+ target_count: parsePositivePort(options.targetCount || options["target-count"] || options.target_count),
1331
+ port: parsePositivePort(options.port),
1332
+ dry_run: options["dry-run"] === true || options.dryRun === true,
1333
+ no_state: options["no-state"] === true || options.noState === true,
1334
+ safe_pacing: parseBooleanOption(options["safe-pacing"] ?? options.safe_pacing),
1335
+ batch_rest_enabled: parseBooleanOption(options["batch-rest"] ?? options.batch_rest_enabled)
1336
+ };
1337
+ }
1278
1338
 
1279
- switch (command) {
1280
- case "start":
1281
- startServer();
1282
- break;
1283
- case "run":
1284
- try {
1285
- await runPipelineOnce(options);
1286
- } catch (error) {
1287
- printJson({
1288
- status: "FAILED",
1289
- error: {
1290
- code: "INVALID_CLI_INPUT",
1291
- message: error.message || "Invalid CLI input",
1292
- retryable: false
1293
- }
1294
- });
1295
- process.exitCode = 1;
1296
- }
1297
- break;
1298
- case "install":
1299
- try {
1300
- installAll(options);
1301
- } catch (error) {
1302
- console.error(error.message || "Install failed.");
1303
- process.exitCode = 1;
1304
- }
1305
- break;
1306
- case "install-skill":
1307
- console.log(`Skill installed to: ${installSkill()}`);
1308
- break;
1309
- case "init-config": {
1310
- const result = ensureUserConfig(options);
1311
- console.log(result.created ? `Config template created at: ${result.path}` : `Config already exists at: ${result.path}`);
1312
- break;
1313
- }
1314
- case "set-port": {
1315
- try {
1316
- const result = setDebugPort(options);
1317
- console.log(`Preferred debug port saved: ${result.port}`);
1318
- console.log(`Updated config: ${result.configPath}`);
1319
- console.log("Port priority for runtime commands: --port > BOSS_RECOMMEND_CHROME_PORT > screening-config.json.debugPort > 9222");
1320
- } catch (error) {
1321
- console.error(error.message || "Failed to persist debug port.");
1322
- process.exitCode = 1;
1323
- }
1324
- break;
1339
+ function getBossChatCliRunTarget(options = {}) {
1340
+ return {
1341
+ profile: typeof options.profile === "string" ? options.profile.trim() : undefined,
1342
+ run_id: String(options["run-id"] || options.runId || options.run_id || "").trim()
1343
+ };
1344
+ }
1345
+
1346
+ async function runBossChatCliCommand(subcommand, options = {}) {
1347
+ const workspaceRoot = getWorkspaceRoot(options);
1348
+ if (subcommand === "health-check") {
1349
+ printJson(getBossChatHealthCheck(workspaceRoot, {
1350
+ port: parsePositivePort(options.port)
1351
+ }));
1352
+ return;
1325
1353
  }
1326
- case "set-config": {
1327
- try {
1328
- const result = setScreeningConfig(options);
1329
- console.log(`screening-config.json updated: ${result.path}`);
1330
- } catch (error) {
1331
- console.error(error.message || "Failed to write screening-config.json.");
1332
- process.exitCode = 1;
1333
- }
1334
- break;
1354
+
1355
+ if (subcommand === "run") {
1356
+ printJson(await startBossChatRun({
1357
+ workspaceRoot,
1358
+ input: buildBossChatCliInput(options)
1359
+ }));
1360
+ return;
1361
+ }
1362
+
1363
+ if (subcommand === "start-run") {
1364
+ printJson(await startBossChatRun({
1365
+ workspaceRoot,
1366
+ input: buildBossChatCliInput(options)
1367
+ }));
1368
+ return;
1369
+ }
1370
+
1371
+ if (subcommand === "get-run") {
1372
+ printJson(await getBossChatRun({
1373
+ workspaceRoot,
1374
+ input: getBossChatCliRunTarget(options)
1375
+ }));
1376
+ return;
1377
+ }
1378
+
1379
+ if (subcommand === "pause-run") {
1380
+ printJson(await pauseBossChatRun({
1381
+ workspaceRoot,
1382
+ input: getBossChatCliRunTarget(options)
1383
+ }));
1384
+ return;
1385
+ }
1386
+
1387
+ if (subcommand === "resume-run") {
1388
+ printJson(await resumeBossChatRun({
1389
+ workspaceRoot,
1390
+ input: getBossChatCliRunTarget(options)
1391
+ }));
1392
+ return;
1393
+ }
1394
+
1395
+ if (subcommand === "cancel-run") {
1396
+ printJson(await cancelBossChatRun({
1397
+ workspaceRoot,
1398
+ input: getBossChatCliRunTarget(options)
1399
+ }));
1400
+ return;
1335
1401
  }
1336
- case "config": {
1337
- const sub = String(process.argv[3] || "").trim().toLowerCase();
1338
- if (!sub || sub.startsWith("--") || sub === "set") {
1339
- const configOptions = sub === "set" ? parseOptions(process.argv.slice(4)) : options;
1402
+
1403
+ throw new Error(`Unknown chat subcommand: ${subcommand || ""}`);
1404
+ }
1405
+
1406
+ export async function runCli(argv = process.argv) {
1407
+ const command = argv[2] || "start";
1408
+ const options = parseOptions(argv.slice(3));
1409
+ ensureAssetsUpToDate(command);
1410
+
1411
+ switch (command) {
1412
+ case "start":
1413
+ startServer();
1414
+ break;
1415
+ case "run":
1416
+ try {
1417
+ await runPipelineOnce(options);
1418
+ } catch (error) {
1419
+ printJson({
1420
+ status: "FAILED",
1421
+ error: {
1422
+ code: "INVALID_CLI_INPUT",
1423
+ message: error.message || "Invalid CLI input",
1424
+ retryable: false
1425
+ }
1426
+ });
1427
+ process.exitCode = 1;
1428
+ }
1429
+ break;
1430
+ case "chat":
1340
1431
  try {
1341
- const result = setScreeningConfig(configOptions);
1432
+ const chatSubcommand = String(argv[3] || "").trim().toLowerCase();
1433
+ const chatOptions = parseOptions(argv.slice(4));
1434
+ await runBossChatCliCommand(chatSubcommand, chatOptions);
1435
+ } catch (error) {
1436
+ printJson({
1437
+ status: "FAILED",
1438
+ error: {
1439
+ code: "INVALID_CHAT_CLI_INPUT",
1440
+ message: error.message || "Invalid chat CLI input",
1441
+ retryable: false
1442
+ }
1443
+ });
1444
+ process.exitCode = 1;
1445
+ }
1446
+ break;
1447
+ case "install":
1448
+ try {
1449
+ installAll(options);
1450
+ } catch (error) {
1451
+ console.error(error.message || "Install failed.");
1452
+ process.exitCode = 1;
1453
+ }
1454
+ break;
1455
+ case "install-skill":
1456
+ for (const item of installSkill()) {
1457
+ console.log(`Skill installed: ${item.skill} -> ${item.targetDir}`);
1458
+ }
1459
+ break;
1460
+ case "init-config": {
1461
+ const result = ensureUserConfig(options);
1462
+ console.log(result.created ? `Config template created at: ${result.path}` : `Config already exists at: ${result.path}`);
1463
+ break;
1464
+ }
1465
+ case "set-port": {
1466
+ try {
1467
+ const result = setDebugPort(options);
1468
+ console.log(`Preferred debug port saved: ${result.port}`);
1469
+ console.log(`Updated config: ${result.configPath}`);
1470
+ console.log("Port priority for runtime commands: --port > BOSS_RECOMMEND_CHROME_PORT > screening-config.json.debugPort > 9222");
1471
+ } catch (error) {
1472
+ console.error(error.message || "Failed to persist debug port.");
1473
+ process.exitCode = 1;
1474
+ }
1475
+ break;
1476
+ }
1477
+ case "set-config": {
1478
+ try {
1479
+ const result = setScreeningConfig(options);
1342
1480
  console.log(`screening-config.json updated: ${result.path}`);
1343
1481
  } catch (error) {
1344
1482
  console.error(error.message || "Failed to write screening-config.json.");
@@ -1346,37 +1484,64 @@ switch (command) {
1346
1484
  }
1347
1485
  break;
1348
1486
  }
1349
- console.error(`Unknown config subcommand: ${sub}`);
1350
- process.exitCode = 1;
1351
- break;
1352
- }
1353
- case "mcp-config":
1354
- try {
1355
- printMcpConfig(options);
1356
- } catch (error) {
1357
- console.error(error.message || "Failed to generate MCP config template.");
1487
+ case "config": {
1488
+ const sub = String(argv[3] || "").trim().toLowerCase();
1489
+ if (!sub || sub.startsWith("--") || sub === "set") {
1490
+ const configOptions = sub === "set" ? parseOptions(argv.slice(4)) : options;
1491
+ try {
1492
+ const result = setScreeningConfig(configOptions);
1493
+ console.log(`screening-config.json updated: ${result.path}`);
1494
+ } catch (error) {
1495
+ console.error(error.message || "Failed to write screening-config.json.");
1496
+ process.exitCode = 1;
1497
+ }
1498
+ break;
1499
+ }
1500
+ console.error(`Unknown config subcommand: ${sub}`);
1358
1501
  process.exitCode = 1;
1502
+ break;
1359
1503
  }
1360
- break;
1361
- case "doctor":
1362
- await printDoctor(options);
1363
- break;
1364
- case "calibrate":
1365
- await calibrate(options);
1366
- break;
1367
- case "launch-chrome":
1368
- await launchChrome(options);
1369
- break;
1370
- case "where":
1371
- printPaths();
1372
- break;
1373
- case "help":
1374
- case "--help":
1375
- case "-h":
1376
- printHelp();
1377
- break;
1378
- default:
1379
- console.error(`Unknown command: ${command}`);
1380
- console.error("Run `boss-recommend-mcp --help` for usage.");
1381
- process.exitCode = 1;
1382
- }
1504
+ case "mcp-config":
1505
+ try {
1506
+ printMcpConfig(options);
1507
+ } catch (error) {
1508
+ console.error(error.message || "Failed to generate MCP config template.");
1509
+ process.exitCode = 1;
1510
+ }
1511
+ break;
1512
+ case "doctor":
1513
+ await printDoctor(options);
1514
+ break;
1515
+ case "calibrate":
1516
+ await calibrate(options);
1517
+ break;
1518
+ case "launch-chrome":
1519
+ await launchChrome(options);
1520
+ break;
1521
+ case "where":
1522
+ printPaths();
1523
+ break;
1524
+ case "help":
1525
+ case "--help":
1526
+ case "-h":
1527
+ printHelp();
1528
+ break;
1529
+ default:
1530
+ console.error(`Unknown command: ${command}`);
1531
+ console.error("Run `boss-recommend-mcp --help` for usage.");
1532
+ process.exitCode = 1;
1533
+ }
1534
+ }
1535
+
1536
+ export const __testables = {
1537
+ buildBossChatCliInput,
1538
+ getBossChatCliRunTarget,
1539
+ getRunFollowUp,
1540
+ installSkill,
1541
+ runBossChatCliCommand,
1542
+ runPipelineOnce
1543
+ };
1544
+
1545
+ if (process.argv[1] && path.resolve(process.argv[1]) === currentFilePath) {
1546
+ await runCli(process.argv);
1547
+ }