@reconcrap/boss-recommend-mcp 1.1.2 → 1.1.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reconcrap/boss-recommend-mcp",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "Unified MCP pipeline for recommend-page filtering and screening on Boss Zhipin",
5
5
  "keywords": [
6
6
  "boss",
package/src/index.js CHANGED
@@ -203,7 +203,7 @@ function createRunInputSchema() {
203
203
  post_action_confirmed: { type: "boolean" },
204
204
  post_action_value: {
205
205
  type: "string",
206
- enum: ["favorite", "greet"]
206
+ enum: ["favorite", "greet", "none"]
207
207
  },
208
208
  final_confirmed: { type: "boolean" },
209
209
  job_confirmed: { type: "boolean" },
@@ -267,7 +267,7 @@ function createRunInputSchema() {
267
267
  max_greet_count: { type: "integer", minimum: 1 },
268
268
  post_action: {
269
269
  type: "string",
270
- enum: ["favorite", "greet"]
270
+ enum: ["favorite", "greet", "none"]
271
271
  }
272
272
  },
273
273
  additionalProperties: false
package/src/parser.js CHANGED
@@ -28,10 +28,11 @@ const DEGREE_ORDER = [
28
28
  ];
29
29
  const GENDER_OPTIONS = ["不限", "男", "女"];
30
30
  const RECENT_NOT_VIEW_OPTIONS = ["不限", "近14天没有"];
31
- const POST_ACTION_OPTIONS = ["favorite", "greet"];
31
+ const POST_ACTION_OPTIONS = ["favorite", "greet", "none"];
32
32
  const POST_ACTION_LABELS = {
33
33
  favorite: "收藏",
34
- greet: "直接沟通"
34
+ greet: "直接沟通",
35
+ none: "什么也不做"
35
36
  };
36
37
  const LEADING_NOISE_PATTERNS = [
37
38
  /^使用boss-recommend-pipeline skills/i,
@@ -90,7 +91,7 @@ const FILTER_CLAUSE_PATTERNS = [
90
91
  /近?14天(?:内)?没有|近?14天(?:内)?没看过|近?14天(?:内)?未查看|过滤[^。;;\n]{0,12}14天|排除[^。;;\n]{0,12}14天/i,
91
92
  /目标(?:处理|筛选)?(?:人数|数量)?|至少(?:处理|筛选)|(?:处理|筛选)\s*\d+\s*(?:位|人)/i,
92
93
  /最多(?:打招呼|沟通|联系)|(?:打招呼|沟通|联系)(?:上限|最多|不超过|至多)/i,
93
- /收藏|打招呼|直接沟通/i
94
+ /收藏|打招呼|直接沟通|什么也不做|不做任何操作|不操作|仅筛选|只筛选/i
94
95
  ];
95
96
  const META_CLAUSE_PATTERNS = [
96
97
  /推荐页|推荐页面|boss推荐/i,
@@ -259,6 +260,9 @@ function normalizePostAction(value) {
259
260
  if (!normalized) return null;
260
261
  if (["favorite", "fav", "收藏"].includes(normalized)) return "favorite";
261
262
  if (["greet", "chat", "打招呼", "直接沟通", "沟通"].includes(normalized)) return "greet";
263
+ if (["none", "noop", "no-op", "什么也不做", "不做任何操作", "不操作", "仅筛选", "只筛选"].includes(normalized)) {
264
+ return "none";
265
+ }
262
266
  return null;
263
267
  }
264
268
 
@@ -377,6 +381,8 @@ function resolvePostAction({ instruction, confirmation, overrides }) {
377
381
  ? "favorite"
378
382
  : /打招呼|直接沟通|沟通/.test(instruction)
379
383
  ? "greet"
384
+ : /什么也不做|不做任何操作|不操作|仅筛选|只筛选/.test(instruction)
385
+ ? "none"
380
386
  : null;
381
387
  const proposed = overrideValue || confirmationValue || instructionValue || null;
382
388
 
@@ -534,41 +540,25 @@ export function parseRecommendInstruction({ instruction, confirmation, overrides
534
540
  invalidOverrideSchoolTags: schoolTagAudit.invalid,
535
541
  maxGreetCountResolution
536
542
  });
537
- const hasSchoolTagSignal = Boolean(
538
- (Array.isArray(overrideSchoolTag) && overrideSchoolTag.length > 0)
539
- || (Array.isArray(confirmationSchoolTag) && confirmationSchoolTag.length > 0)
540
- || (Array.isArray(detectedSchoolTags) && detectedSchoolTags.length > 0)
541
- );
542
- const hasDegreeSignal = Boolean(
543
- (Array.isArray(overrideDegrees) && overrideDegrees.length > 0)
544
- || (Array.isArray(confirmationDegrees) && confirmationDegrees.length > 0)
545
- || (Array.isArray(detectedDegrees) && detectedDegrees.length > 0)
546
- );
547
- const hasGenderSignal = Boolean(
548
- overrideGender
549
- || confirmationGender
550
- || extractGender(text)
551
- );
552
- const hasRecentNotViewSignal = Boolean(
553
- overrideRecentNotView
554
- || confirmationRecentNotView
555
- || extractRecentNotView(text)
556
- );
543
+ const hasConfirmedSchoolTagValue = Array.isArray(confirmationSchoolTag) && confirmationSchoolTag.length > 0;
544
+ const hasConfirmedDegreeValue = Array.isArray(confirmationDegrees) && confirmationDegrees.length > 0;
545
+ const hasConfirmedGenderValue = Boolean(confirmationGender);
546
+ const hasConfirmedRecentNotViewValue = Boolean(confirmationRecentNotView);
557
547
  const needs_school_tag_confirmation = (
558
548
  confirmation?.school_tag_confirmed !== true
559
- || !hasSchoolTagSignal
549
+ || !hasConfirmedSchoolTagValue
560
550
  );
561
551
  const needs_degree_confirmation = (
562
552
  confirmation?.degree_confirmed !== true
563
- || !hasDegreeSignal
553
+ || !hasConfirmedDegreeValue
564
554
  );
565
555
  const needs_gender_confirmation = (
566
556
  confirmation?.gender_confirmed !== true
567
- || !hasGenderSignal
557
+ || !hasConfirmedGenderValue
568
558
  );
569
559
  const needs_recent_not_view_confirmation = (
570
560
  confirmation?.recent_not_view_confirmed !== true
571
- || !hasRecentNotViewSignal
561
+ || !hasConfirmedRecentNotViewValue
572
562
  );
573
563
  const needs_filters_confirmation = (
574
564
  confirmation?.filters_confirmed !== true
@@ -659,7 +649,8 @@ export function parseRecommendInstruction({ instruction, confirmation, overrides
659
649
  value: postActionResolution.proposed_post_action,
660
650
  options: [
661
651
  { label: POST_ACTION_LABELS.favorite, value: "favorite" },
662
- { label: POST_ACTION_LABELS.greet, value: "greet" }
652
+ { label: POST_ACTION_LABELS.greet, value: "greet" },
653
+ { label: POST_ACTION_LABELS.none, value: "none" }
663
654
  ]
664
655
  });
665
656
  }
package/src/pipeline.js CHANGED
@@ -959,7 +959,7 @@ export async function runRecommendPipeline(
959
959
  });
960
960
 
961
961
  return {
962
- status: "COMPLETED",
962
+ status: "COMPLETED",
963
963
  search_params: parsed.searchParams,
964
964
  screen_params: parsed.screenParams,
965
965
  result: {
@@ -976,8 +976,10 @@ export async function runRecommendPipeline(
976
976
  post_action: parsed.screenParams.post_action,
977
977
  max_greet_count: parsed.screenParams.max_greet_count,
978
978
  greet_count: screenSummary.greet_count ?? 0,
979
- greet_limit_fallback_count: screenSummary.greet_limit_fallback_count ?? 0
980
- },
981
- message: "Recommend 流水线已完成。post_action 在运行开始时已一次性确认;若选择打招呼并设置上限,超出上限后会自动改为收藏。"
982
- };
983
- }
979
+ greet_limit_fallback_count: screenSummary.greet_limit_fallback_count ?? 0
980
+ },
981
+ message: parsed.screenParams.post_action === "none"
982
+ ? "Recommend 流水线已完成。本次 post_action=none:符合条件的人选仅记录到 CSV,不执行收藏或打招呼。"
983
+ : "Recommend 流水线已完成。post_action 在运行开始时已一次性确认;若选择打招呼并设置上限,超出上限后会自动改为收藏。"
984
+ };
985
+ }
@@ -29,9 +29,13 @@ function testConfirmedPostActionAndOverrides() {
29
29
  confirmation: {
30
30
  filters_confirmed: true,
31
31
  school_tag_confirmed: true,
32
+ school_tag_value: ["211"],
32
33
  degree_confirmed: true,
34
+ degree_value: ["本科"],
33
35
  gender_confirmed: true,
36
+ gender_value: "女",
34
37
  recent_not_view_confirmed: true,
38
+ recent_not_view_value: "近14天没有",
35
39
  criteria_confirmed: true,
36
40
  target_count_confirmed: true,
37
41
  target_count_value: 12,
@@ -67,6 +71,30 @@ function testConfirmedPostActionAndOverrides() {
67
71
  assert.equal(result.needs_max_greet_count_confirmation, false);
68
72
  }
69
73
 
74
+ function testMissingRecentNotViewValueShouldRequireReconfirmation() {
75
+ const result = parseRecommendInstruction({
76
+ instruction: "推荐页筛选985男生,近14天没有,有销售经验,符合标准收藏",
77
+ confirmation: {
78
+ filters_confirmed: true,
79
+ school_tag_confirmed: true,
80
+ school_tag_value: ["985"],
81
+ degree_confirmed: true,
82
+ degree_value: ["本科"],
83
+ gender_confirmed: true,
84
+ gender_value: "男",
85
+ recent_not_view_confirmed: true,
86
+ criteria_confirmed: true,
87
+ target_count_confirmed: true,
88
+ post_action_confirmed: true,
89
+ post_action_value: "favorite"
90
+ },
91
+ overrides: null
92
+ });
93
+
94
+ assert.equal(result.needs_recent_not_view_confirmation, true);
95
+ assert.equal(result.pending_questions.some((q) => q.field === "recent_not_view"), true);
96
+ }
97
+
70
98
  function testFilterConfirmedWithoutExplicitValuesShouldRequireReconfirmation() {
71
99
  const result = parseRecommendInstruction({
72
100
  instruction: "通过boss推荐skill帮我找人",
@@ -401,6 +429,31 @@ function testTargetCountCanBeSkippedAfterConfirmation() {
401
429
  assert.equal(result.screenParams.target_count, null);
402
430
  }
403
431
 
432
+ function testPostActionNoneCanBeConfirmed() {
433
+ const result = parseRecommendInstruction({
434
+ instruction: "推荐页筛选211女生,近14天没有,有AI经验,符合标准什么也不做",
435
+ confirmation: {
436
+ filters_confirmed: true,
437
+ school_tag_confirmed: true,
438
+ school_tag_value: ["211"],
439
+ degree_confirmed: true,
440
+ degree_value: ["本科"],
441
+ gender_confirmed: true,
442
+ gender_value: "女",
443
+ recent_not_view_confirmed: true,
444
+ recent_not_view_value: "近14天没有",
445
+ criteria_confirmed: true,
446
+ target_count_confirmed: true,
447
+ post_action_confirmed: true,
448
+ post_action_value: "none"
449
+ },
450
+ overrides: null
451
+ });
452
+
453
+ assert.equal(result.screenParams.post_action, "none");
454
+ assert.equal(result.needs_post_action_confirmation, false);
455
+ }
456
+
404
457
  function testJobSelectionHintCanComeFromOverrides() {
405
458
  const result = parseRecommendInstruction({
406
459
  instruction: "推荐页筛选211女生,有算法经验,符合标准收藏",
@@ -426,6 +479,7 @@ function testMcpMentionShouldStayInCriteria() {
426
479
  function main() {
427
480
  testNeedConfirmationIncludesPostAction();
428
481
  testConfirmedPostActionAndOverrides();
482
+ testMissingRecentNotViewValueShouldRequireReconfirmation();
429
483
  testFilterConfirmedWithoutExplicitValuesShouldRequireReconfirmation();
430
484
  testFilterConfirmedWithExplicitConfirmationValuesShouldNotFallbackToUnlimited();
431
485
  testMultipleSchoolTagsMarkedSuspicious();
@@ -445,6 +499,7 @@ function main() {
445
499
  testGreetAutoFilledMaxGreetCountShouldRequireReconfirmation();
446
500
  testTargetCountNeedsConfirmationEvenWhenOptional();
447
501
  testTargetCountCanBeSkippedAfterConfirmation();
502
+ testPostActionNoneCanBeConfirmed();
448
503
  testJobSelectionHintCanComeFromOverrides();
449
504
  console.log("parser tests passed");
450
505
  }
@@ -31,6 +31,9 @@ function normalizePostAction(value) {
31
31
  if (!normalized) return null;
32
32
  if (["favorite", "fav", "收藏"].includes(normalized)) return "favorite";
33
33
  if (["greet", "chat", "打招呼", "直接沟通", "沟通"].includes(normalized)) return "greet";
34
+ if (["none", "noop", "no-op", "什么也不做", "不做任何操作", "不操作", "仅筛选", "只筛选"].includes(normalized)) {
35
+ return "none";
36
+ }
34
37
  return null;
35
38
  }
36
39
 
@@ -210,10 +213,11 @@ async function promptMissingInputs(args) {
210
213
  if (!(args.postActionConfirmed === true && args.postAction)) {
211
214
  args.postAction = await askWithValidation(
212
215
  ask,
213
- "本次通过人选统一执行什么动作?请输入 1(收藏) 2(直接沟通): ",
216
+ "本次通过人选统一执行什么动作?请输入 1(收藏) / 2(直接沟通) / 3(什么也不做): ",
214
217
  (value) => {
215
218
  if (value === "1") return "favorite";
216
219
  if (value === "2") return "greet";
220
+ if (value === "3") return "none";
217
221
  return null;
218
222
  }
219
223
  );
@@ -289,9 +293,10 @@ async function promptPostAction() {
289
293
  });
290
294
  const ask = (question) => new Promise((resolve) => rl.question(question, resolve));
291
295
  try {
292
- const answer = normalizeText(await ask("本次通过人选统一执行什么动作?请输入 1(收藏) 2(直接沟通): "));
296
+ const answer = normalizeText(await ask("本次通过人选统一执行什么动作?请输入 1(收藏) / 2(直接沟通) / 3(什么也不做): "));
293
297
  if (answer === "1") return "favorite";
294
298
  if (answer === "2") return "greet";
299
+ if (answer === "3") return "none";
295
300
  throw new Error("INVALID_POST_ACTION_CONFIRMATION");
296
301
  } finally {
297
302
  rl.close();
@@ -1955,7 +1960,9 @@ class RecommendScreenCli {
1955
1960
  }
1956
1961
  const actionResult = effectiveAction === "favorite"
1957
1962
  ? await this.favoriteCandidate()
1958
- : await this.greetCandidate();
1963
+ : effectiveAction === "greet"
1964
+ ? await this.greetCandidate()
1965
+ : { actionTaken: "none" };
1959
1966
  if (actionResult.actionTaken === "greet") {
1960
1967
  this.greetCount += 1;
1961
1968
  }
@@ -2050,7 +2057,7 @@ async function main() {
2050
2057
  console.log(JSON.stringify({
2051
2058
  status: "COMPLETED",
2052
2059
  result: {
2053
- usage: "node boss-recommend-screen-cli.cjs --criteria \"有 MCP 开发经验\" --post-action greet --max-greet-count 10 --post-action-confirmed true --baseurl <url> --apikey <key> --model <model> --port 9222 --output <csv-path> --checkpoint-path <checkpoint.json> --pause-control-path <pause-control.json> [--resume]"
2060
+ usage: "node boss-recommend-screen-cli.cjs --criteria \"有 MCP 开发经验\" --post-action <favorite|greet|none> --max-greet-count 10 --post-action-confirmed true --baseurl <url> --apikey <key> --model <model> --port 9222 --output <csv-path> --checkpoint-path <checkpoint.json> --pause-control-path <pause-control.json> [--resume]"
2054
2061
  }
2055
2062
  }));
2056
2063
  return;