@reconcrap/boss-recommend-mcp 1.3.5 → 1.3.6
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 +1 -0
- package/package.json +1 -1
- package/skills/boss-chat/README.md +5 -0
- package/skills/boss-chat/SKILL.md +7 -0
- package/src/boss-chat.js +12 -2
- package/src/cli.js +9 -1
- package/src/index.js +16 -6
- package/src/test-boss-chat.js +36 -0
package/README.md
CHANGED
|
@@ -215,6 +215,7 @@ chat-only 交互建议:
|
|
|
215
215
|
|
|
216
216
|
- 先调用一次 `start_boss_chat_run`(可不带参数),服务会先导航到 `https://www.zhipin.com/web/chat/index` 并返回 `NEED_INPUT`,其中包含岗位 `job_options` 与待补字段。
|
|
217
217
|
- 然后基于 `job_options` 让用户选择 `job`,并补齐 `start_from`、`target_count`、`criteria` 后再次调用 `start_boss_chat_run` 启动任务。
|
|
218
|
+
- `target_count` 支持正整数;若用户给出 `全部候选人` / `所有候选人`,会自动按不限(扫到底)处理。
|
|
218
219
|
|
|
219
220
|
Trae-CN / 长对话防循环建议:
|
|
220
221
|
|
package/package.json
CHANGED
|
@@ -23,4 +23,9 @@ Anti-loop rules:
|
|
|
23
23
|
- On validation errors, list all missing/invalid fields once.
|
|
24
24
|
- Do not call start_boss_chat_run repeatedly in one turn.
|
|
25
25
|
- Do not call get_boss_chat_run unless user explicitly asks for progress.
|
|
26
|
+
|
|
27
|
+
target_count mapping:
|
|
28
|
+
- Positive integer means explicit cap (for example 20).
|
|
29
|
+
- `all` / `unlimited` / `全部` / `不限` / `扫到底` / `全量` means unlimited.
|
|
30
|
+
- `全部候选人` / `所有候选人` must also be treated as unlimited.
|
|
26
31
|
```
|
|
@@ -37,6 +37,12 @@ description: "Use when users want Boss chat-page screening/outreach via the bund
|
|
|
37
37
|
- `safe_pacing`
|
|
38
38
|
- `batch_rest_enabled`
|
|
39
39
|
|
|
40
|
+
`target_count` 填写规则(关键):
|
|
41
|
+
|
|
42
|
+
- 正整数:如 `20`
|
|
43
|
+
- 扫到底:`all` / `unlimited` / `全部` / `不限` / `扫到底` / `全量`
|
|
44
|
+
- 同义短语也可直接用:`全部候选人` / `所有候选人`(等价于扫到底)
|
|
45
|
+
|
|
40
46
|
## Hard Rules
|
|
41
47
|
|
|
42
48
|
- LLM 配置必须复用 `boss-recommend-mcp` 的 `screening-config.json`;不要再向用户单独要 `baseUrl/apiKey/model`。
|
|
@@ -46,6 +52,7 @@ description: "Use when users want Boss chat-page screening/outreach via the bund
|
|
|
46
52
|
- 不得在 recommend 任务尚未完成时并行启动独立 chat run。
|
|
47
53
|
- `job` / `start_from` / `criteria` 缺一不可;缺参时只补缺口。
|
|
48
54
|
- `target_count` 在 chat-only 启动前也是必填项,不能默认省略。
|
|
55
|
+
- 当用户说“全部候选人/所有候选人”时,必须按“扫到底(unlimited)”处理,不要再追问正整数。
|
|
49
56
|
- 禁止 agent 自行补全 `job/start_from/criteria` 并直接执行,必须由用户明确给出或确认。
|
|
50
57
|
- chat-only 启动流程必须先进入聊天页并拉取岗位列表,再让用户从列表中选择 `job`。
|
|
51
58
|
- 允许先用空参调用 `start_boss_chat_run` 触发 `NEED_INPUT`;若返回了 `job_options`,必须完整展示所有岗位选项给用户确认。
|
package/src/boss-chat.js
CHANGED
|
@@ -33,19 +33,29 @@ function parsePositiveInteger(value, fallback = null) {
|
|
|
33
33
|
function isUnlimitedTargetCountToken(value) {
|
|
34
34
|
const token = normalizeText(value).toLowerCase();
|
|
35
35
|
if (!token) return false;
|
|
36
|
-
|
|
36
|
+
const compact = token.replace(/\s+/g, "");
|
|
37
|
+
const knownTokens = new Set([
|
|
37
38
|
"all",
|
|
38
39
|
"unlimited",
|
|
39
40
|
"infinity",
|
|
40
41
|
"inf",
|
|
41
42
|
"max",
|
|
42
43
|
"full",
|
|
44
|
+
"allcandidates",
|
|
43
45
|
"全部",
|
|
44
46
|
"全量",
|
|
45
47
|
"不限",
|
|
46
48
|
"扫到底",
|
|
49
|
+
"全部候选人",
|
|
50
|
+
"所有候选人",
|
|
51
|
+
"全部人选",
|
|
52
|
+
"所有人选",
|
|
47
53
|
"直到完成所有人选"
|
|
48
|
-
]
|
|
54
|
+
]);
|
|
55
|
+
if (knownTokens.has(token) || knownTokens.has(compact)) return true;
|
|
56
|
+
if (/^(?:all|unlimited|infinity|inf|max|full)(?:candidate|candidates)?$/i.test(compact)) return true;
|
|
57
|
+
if (/^(?:全部|所有|全量|不限)(?:候选人|人选|牛人|人才|人员)?$/u.test(compact)) return true;
|
|
58
|
+
return false;
|
|
49
59
|
}
|
|
50
60
|
|
|
51
61
|
function parseBossChatTargetCount(value) {
|
package/src/cli.js
CHANGED
|
@@ -195,6 +195,14 @@ function parsePositivePort(raw) {
|
|
|
195
195
|
return Number.isFinite(port) && port > 0 ? port : null;
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
+
function parseBossChatTargetCountOption(raw) {
|
|
199
|
+
if (raw === undefined || raw === null) return undefined;
|
|
200
|
+
const text = String(raw).trim();
|
|
201
|
+
if (!text) return undefined;
|
|
202
|
+
const parsed = parsePositivePort(text);
|
|
203
|
+
return parsed ?? text;
|
|
204
|
+
}
|
|
205
|
+
|
|
198
206
|
function parseBooleanOption(raw, fallback = undefined) {
|
|
199
207
|
if (raw === undefined || raw === null || raw === "") return fallback;
|
|
200
208
|
if (raw === true) return true;
|
|
@@ -1327,7 +1335,7 @@ function buildBossChatCliInput(options = {}) {
|
|
|
1327
1335
|
job: typeof options.job === "string" ? options.job.trim() : undefined,
|
|
1328
1336
|
start_from: String(options["start-from"] || options.start_from || "").trim().toLowerCase() || undefined,
|
|
1329
1337
|
criteria: typeof options.criteria === "string" ? options.criteria.trim() : undefined,
|
|
1330
|
-
target_count:
|
|
1338
|
+
target_count: parseBossChatTargetCountOption(options.targetCount || options["target-count"] || options.target_count),
|
|
1331
1339
|
port: parsePositivePort(options.port),
|
|
1332
1340
|
dry_run: options["dry-run"] === true || options.dryRun === true,
|
|
1333
1341
|
no_state: options["no-state"] === true || options.noState === true,
|
package/src/index.js
CHANGED
|
@@ -77,19 +77,29 @@ function normalizeText(value) {
|
|
|
77
77
|
function isUnlimitedTargetCountToken(value) {
|
|
78
78
|
const token = normalizeText(value).toLowerCase();
|
|
79
79
|
if (!token) return false;
|
|
80
|
-
|
|
80
|
+
const compact = token.replace(/\s+/g, "");
|
|
81
|
+
const knownTokens = new Set([
|
|
81
82
|
"all",
|
|
82
83
|
"unlimited",
|
|
83
84
|
"infinity",
|
|
84
85
|
"inf",
|
|
85
86
|
"max",
|
|
86
87
|
"full",
|
|
88
|
+
"allcandidates",
|
|
87
89
|
"全部",
|
|
88
90
|
"全量",
|
|
89
91
|
"不限",
|
|
90
92
|
"扫到底",
|
|
93
|
+
"全部候选人",
|
|
94
|
+
"所有候选人",
|
|
95
|
+
"全部人选",
|
|
96
|
+
"所有人选",
|
|
91
97
|
"直到完成所有人选"
|
|
92
|
-
]
|
|
98
|
+
]);
|
|
99
|
+
if (knownTokens.has(token) || knownTokens.has(compact)) return true;
|
|
100
|
+
if (/^(?:all|unlimited|infinity|inf|max|full)(?:candidate|candidates)?$/i.test(compact)) return true;
|
|
101
|
+
if (/^(?:全部|所有|全量|不限)(?:候选人|人选|牛人|人才|人员)?$/u.test(compact)) return true;
|
|
102
|
+
return false;
|
|
93
103
|
}
|
|
94
104
|
|
|
95
105
|
function parsePositiveInteger(raw, fallback) {
|
|
@@ -383,7 +393,7 @@ function createRunInputSchema() {
|
|
|
383
393
|
},
|
|
384
394
|
{
|
|
385
395
|
type: "string",
|
|
386
|
-
enum: ["all", "unlimited", "全部", "不限", "扫到底", "全量"]
|
|
396
|
+
enum: ["all", "unlimited", "全部", "不限", "扫到底", "全量", "全部候选人", "所有候选人"]
|
|
387
397
|
}
|
|
388
398
|
]
|
|
389
399
|
},
|
|
@@ -432,10 +442,10 @@ function createBossChatStartInputSchema() {
|
|
|
432
442
|
},
|
|
433
443
|
{
|
|
434
444
|
type: "string",
|
|
435
|
-
enum: ["all", "unlimited", "全部", "不限", "扫到底", "全量"]
|
|
445
|
+
enum: ["all", "unlimited", "全部", "不限", "扫到底", "全量", "全部候选人", "所有候选人"]
|
|
436
446
|
}
|
|
437
447
|
],
|
|
438
|
-
description: "本次处理人数上限;支持正整数或 all
|
|
448
|
+
description: "本次处理人数上限;支持正整数或 all/不限/全部候选人(扫到底)"
|
|
439
449
|
},
|
|
440
450
|
port: {
|
|
441
451
|
type: "integer",
|
|
@@ -713,7 +723,7 @@ function validateBossChatStartArgs(args) {
|
|
|
713
723
|
typeof rawTargetCount === "string" && isUnlimitedTargetCountToken(rawTargetCount);
|
|
714
724
|
const numericUnlimited = Number.isFinite(targetCount) && targetCount === -1;
|
|
715
725
|
if ((!Number.isFinite(targetCount) || targetCount <= 0) && !tokenAllowed && !numericUnlimited) {
|
|
716
|
-
return "target_count must be a positive integer or one of: all, unlimited, 全部, 不限, 扫到底,
|
|
726
|
+
return "target_count must be a positive integer or one of: all, unlimited, 全部, 不限, 扫到底, 全量, 全部候选人, 所有候选人";
|
|
717
727
|
}
|
|
718
728
|
}
|
|
719
729
|
if (Object.prototype.hasOwnProperty.call(args, "port")) {
|
package/src/test-boss-chat.js
CHANGED
|
@@ -242,6 +242,20 @@ async function testBossChatAdapterShouldResolveSharedConfigAndInvokeLocalCli() {
|
|
|
242
242
|
assert.equal(stateAfterStart.last_start_args.model, "gpt-4.1-mini");
|
|
243
243
|
assert.equal(stateAfterStart.last_start_args.port, "9666");
|
|
244
244
|
|
|
245
|
+
const startedAll = await startBossChatRun({
|
|
246
|
+
workspaceRoot,
|
|
247
|
+
input: {
|
|
248
|
+
profile: "default",
|
|
249
|
+
job: "算法工程师",
|
|
250
|
+
start_from: "all",
|
|
251
|
+
criteria: "全部候选人都过一遍",
|
|
252
|
+
target_count: "全部候选人"
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
assert.equal(startedAll.status, "ACCEPTED");
|
|
256
|
+
const stateAfterStartAll = readStubState(workspaceRoot);
|
|
257
|
+
assert.equal(stateAfterStartAll.last_start_args.targetCount, "-1");
|
|
258
|
+
|
|
245
259
|
const running = await getBossChatRun({
|
|
246
260
|
workspaceRoot,
|
|
247
261
|
input: {
|
|
@@ -313,6 +327,16 @@ async function testBossChatMcpToolsShouldValidateAndRoute() {
|
|
|
313
327
|
}, 14);
|
|
314
328
|
assert.equal(started.status, "ACCEPTED");
|
|
315
329
|
|
|
330
|
+
const startedAll = await callTool(workspaceRoot, TOOL_BOSS_CHAT_START_RUN, {
|
|
331
|
+
job: "算法工程师",
|
|
332
|
+
start_from: "all",
|
|
333
|
+
criteria: "全部候选人都过一遍",
|
|
334
|
+
target_count: "全部候选人"
|
|
335
|
+
}, 140);
|
|
336
|
+
assert.equal(startedAll.status, "ACCEPTED");
|
|
337
|
+
const stateAfterStartAll = readStubState(workspaceRoot);
|
|
338
|
+
assert.equal(stateAfterStartAll.last_start_args.targetCount, "-1");
|
|
339
|
+
|
|
316
340
|
const running = await callTool(workspaceRoot, TOOL_BOSS_CHAT_GET_RUN, {
|
|
317
341
|
run_id: started.run_id,
|
|
318
342
|
profile: "default"
|
|
@@ -386,6 +410,18 @@ async function testBossChatCliShouldSupportRunAndFollowUpParsing() {
|
|
|
386
410
|
assert.equal(typeof payload.run_id, "string");
|
|
387
411
|
const state = readStubState(workspaceRoot);
|
|
388
412
|
assert.equal(state.get_calls[payload.run_id] || 0, 0);
|
|
413
|
+
|
|
414
|
+
await captureConsoleLogs(async () => {
|
|
415
|
+
await cliTestables.runBossChatCliCommand("run", {
|
|
416
|
+
"workspace-root": workspaceRoot,
|
|
417
|
+
job: "算法工程师",
|
|
418
|
+
"start-from": "all",
|
|
419
|
+
criteria: "全部候选人都过一遍",
|
|
420
|
+
targetCount: "全部候选人"
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
const allState = readStubState(workspaceRoot);
|
|
424
|
+
assert.equal(allState.last_start_args.targetCount, "-1");
|
|
389
425
|
});
|
|
390
426
|
}
|
|
391
427
|
|