@scorehub/creator-outreach 1.1.7 → 1.2.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tiktok-creator-outreach",
3
- "version": "1.1.7",
3
+ "version": "1.2.0",
4
4
  "description": "ScoreHub AI's TikTok creator marketing expert for building clear creator profiles and validating focused collaboration shortlists across Southeast Asia.",
5
5
  "author": {
6
6
  "name": "Ben",
package/README.en.md CHANGED
@@ -6,39 +6,46 @@ Chinese version: [README.md](./README.md)
6
6
 
7
7
  ## Supported Clients
8
8
 
9
- - Claude Code
10
9
  - WorkBuddy
11
10
 
12
- Claude Desktop is not currently supported by this installer.
13
-
14
11
  ## Install
15
12
 
16
- ### Prerequisites
13
+ ### Public Entry for End Users
14
+
15
+ For the public release, end users only need to:
16
+
17
+ 1. Install WorkBuddy and launch it at least once
18
+ 2. Open the following share link in a browser so it can launch WorkBuddy and install Tiky:
19
+
20
+ `https://www.workbuddy.cn/work/launch/?sharecode=lNM8H05BRKoV-dw2gac3ZwQ-p01o3C3KVz1gXJ_CwvtX02-mSRqBB3xepE4V0gr7&expertname=Tiky+%C2%B7+TikTok%E8%BE%BE%E4%BA%BA%E8%90%A5%E9%94%80%E4%B8%93%E5%AE%B6&buddy_type=workbuddy`
21
+
22
+ 3. Return to WorkBuddy and open the “Tiky · TikTok Creator Marketing Expert” conversation
17
23
 
18
- - Node.js 18 or later
19
- - npm and network access to the npm registry
20
- - Claude Code or WorkBuddy installed and launched at least once
24
+ Tiky uses `npx -y @scorehub/creator-outreach@latest` to run a bootstrap check before every new conversation. When the machine is uninitialized, Tiky explains that the ScoreHub local component is required and waits for explicit confirmation. It then prefetches and self-checks `@scorehub/mcp-server@latest` before updating Tiky's files and the WorkBuddy `scorehub` MCP configuration. WorkBuddy must be fully restarted after the initial installation.
21
25
 
22
- Run:
26
+ That first confirmation also authorizes future silent creator-outreach updates, so Tiky does not ask again for every release. The MCP command always remains `npx -y @scorehub/mcp-server@latest`; it is never pinned to a specific version.
27
+
28
+ The share link only installs Tiky into WorkBuddy. In the normal public path, users do not need to install `@scorehub/mcp-server` manually or edit `mcp.json` themselves.
29
+
30
+ ### Support / Internal Fallback Install
31
+
32
+ The command-line installer remains only as a support or internal fallback:
23
33
 
24
34
  ```bash
25
35
  npx -y @scorehub/creator-outreach
26
36
  ```
27
37
 
28
- The installer configures only clients it detects. If it finds neither Claude Code nor WorkBuddy, it exits without writing configuration files.
38
+ The installer configures WorkBuddy only. If it does not find WorkBuddy, it exits without writing configuration files.
29
39
 
30
40
  | Client | Detection | Installed content | How to use |
31
41
  |---|---|---|---|
32
- | Claude Code | `~/.claude/` exists | Agent definition in `~/.claude/agents/`; MCP configuration merged into `~/.claude.json` | Restart, then enter `@tiktok-creator-outreach` |
33
42
  | WorkBuddy | `~/.workbuddy/` exists | Plugin files in `~/.workbuddy/plugins/.../`; MCP configuration merged into `~/.workbuddy/mcp.json` | Restart, then select “ScoreHub AI TikTok Creator Marketing Expert” from the Agent list |
34
43
 
35
- The MCP server (`@scorehub/mcp-server`) is downloaded automatically by the configured `npx -y` command the first time it starts. A global installation is not required. WorkBuddy uses its bundled Node.js and the installer handles Windows `npx.cmd` and PATH separators.
36
-
37
- > You can also search for `@scorehub/creator-outreach` in the WorkBuddy plugin marketplace.
44
+ Before writing the configuration, Tiky verifies the MCP server with `--self-check --json`. WorkBuddy then starts it through `npx -y @scorehub/mcp-server@latest`. If WorkBuddy provides a working bundled Node.js / npx, Tiky reuses it. Tiky only guides users to the official Node.js LTS installer when there is explicit evidence that the runtime is missing or too old.
38
45
 
39
46
  ## First Use and Authorization
40
47
 
41
- At the start of every new conversation, Tiky introduces its creator discovery, profile evaluation, and focused outreach-validation capabilities, explains that it is not a mass-messaging tool, and presents supported Southeast Asian markets, typical use cases, and next-step options. Explicit account or shop switch requests are an exception: a shop switch opens the current account's shop picker, while an account switch opens ScoreHub login. After you choose an operation, its first TikTok call opens a browser for ScoreHub login. Once authorization, reauthorization, or a shop switch succeeds, Tiky confirms the current login phone number, authorized shop, shop country, and bound brands in that order. Tokens are then cached under `~/.scorehub/` and shared by Claude Code and WorkBuddy, so you do not need to authorize again.
48
+ At the start of every new conversation, Tiky first checks one of four bootstrap states: `uninitialized`, `restart_required`, `ready`, or `repair_required`. Only `ready` permits the capability introduction, account or shop switching, and business operations. The first TikTok tool call then opens ScoreHub login when authorization is needed. After authorization, reauthorization, or a shop switch, Tiky confirms the login phone number, authorized shop, country, and bound brands. Tokens remain under `~/.scorehub/` and are not removed by bootstrap or updates.
42
49
 
43
50
  If Tiky says that the current shop's TikTok authorization is invalid or unavailable, your local ScoreHub login is usually still valid. Rebind the shop in ScoreHub and try again; do not repeat browser login.
44
51
 
@@ -56,13 +63,13 @@ Ask Tiky:
56
63
 
57
64
  ### No supported client found
58
65
 
59
- Install and launch Claude Code or WorkBuddy once, then run the install command again. Claude Desktop is outside the current support scope.
66
+ Install and launch WorkBuddy once, then use the share link to launch Tiky's installation in WorkBuddy.
60
67
 
61
68
  ### MCP tools are unavailable, the tool list is empty, or the connection closes
62
69
 
63
- ScoreHub is temporarily unavailable. Fully quit and reopen Claude Code or WorkBuddy, then try again. Contact ScoreHub support if the problem continues.
70
+ Tiky runs the bootstrap check first. It does not attempt OAuth or business calls while installation, restart, or repair is required. If bootstrap reports `ready` but tools remain unavailable, fully quit and reopen WorkBuddy, then try again.
64
71
 
65
- If an error explicitly says that Node.js, npm, or npx is missing, or that Node.js is below version 18, an installed Tiky agent can check the local runtime. After your confirmation, it guides recovery through the official Node.js LTS installation path, verifies the result, and asks you to restart the client. This does not reauthorize or change TikTok credentials; Node.js is still required before Tiky can be installed for the first time.
72
+ If an error explicitly says that Node.js, npm, or npx is missing, or that Node.js is below version 18, Tiky explains the diagnosis first. After your confirmation, it guides recovery through the official Node.js LTS installation path, verifies the result, and asks you to restart WorkBuddy. This does not reauthorize or change TikTok credentials.
66
73
 
67
74
  ## Related Links
68
75
 
package/README.md CHANGED
@@ -6,39 +6,46 @@ English version: [README.en.md](./README.en.md)
6
6
 
7
7
  ## 支持的客户端
8
8
 
9
- - Claude Code
10
9
  - WorkBuddy
11
10
 
12
- Claude Desktop 当前不受此安装器支持。
13
-
14
11
  ## 安装
15
12
 
16
- ### 前置条件
13
+ ### 终端用户入口
14
+
15
+ 公开发布时,终端用户只需要:
16
+
17
+ 1. 安装并至少启动过一次 WorkBuddy
18
+ 2. 点击分享链接,在浏览器中唤起 WorkBuddy 安装 Tiky:
19
+
20
+ `https://www.workbuddy.cn/work/launch/?sharecode=lNM8H05BRKoV-dw2gac3ZwQ-p01o3C3KVz1gXJ_CwvtX02-mSRqBB3xepE4V0gr7&expertname=Tiky+%C2%B7+TikTok%E8%BE%BE%E4%BA%BA%E8%90%A5%E9%94%80%E4%B8%93%E5%AE%B6&buddy_type=workbuddy`
21
+
22
+ 3. 回到 WorkBuddy,打开 “Tiky · TikTok达人营销专家” 对话
17
23
 
18
- - Node.js 18 或更高版本
19
- - npm 和可访问 npm registry 的网络
20
- - 已安装并至少启动过 Claude Code 或 WorkBuddy
24
+ Tiky 每个新会话都会通过 `npx -y @scorehub/creator-outreach@latest` 执行 bootstrap 检查。未初始化时,她会说明需要安装 ScoreHub 本地组件,并等待你明确确认;确认后预拉取并自检 `@scorehub/mcp-server@latest`,再更新 Tiky 本地文件和 WorkBuddy 的 `scorehub` MCP 配置。安装完成后需要完全重启 WorkBuddy。
21
25
 
22
- 运行:
26
+ 首次确认同时授权后续静默更新 creator-outreach,不会对每个新版本重复询问。MCP 配置始终使用 `npx -y @scorehub/mcp-server@latest`,不固定具体版本。
27
+
28
+ 分享链接只负责把 Tiky 放进 WorkBuddy。默认情况下,你不需要手工安装 `@scorehub/mcp-server`,也不需要自己编辑 `mcp.json`。
29
+
30
+ ### 技术支持 / 内部兜底安装
31
+
32
+ 命令行安装器仅作为技术支持或内部排障路径保留:
23
33
 
24
34
  ```bash
25
35
  npx -y @scorehub/creator-outreach
26
36
  ```
27
37
 
28
- 安装器只会配置已检测到的客户端;未检测到 Claude Code 或 WorkBuddy 时,会退出且不写入任何配置文件。
38
+ 该安装器只会配置已检测到的 WorkBuddy;未检测到 WorkBuddy 时,会退出且不写入任何配置文件。
29
39
 
30
40
  | 客户端 | 检测条件 | 安装内容 | 使用方式 |
31
41
  |---|---|---|---|
32
- | Claude Code | `~/.claude/` 存在 | Agent 定义写入 `~/.claude/agents/`;MCP 配置合并到 `~/.claude.json` | 重启后输入 `@tiktok-creator-outreach` |
33
42
  | WorkBuddy | `~/.workbuddy/` 存在 | 插件文件写入 `~/.workbuddy/plugins/.../`;MCP 配置合并到 `~/.workbuddy/mcp.json` | 重启后在 Agent 列表选择“ScoreHub AI TikTok达人营销专家” |
34
43
 
35
- MCP Server(`@scorehub/mcp-server`)由配置中的 `npx -y` 在首次启动时自动拉取,无需全局安装。WorkBuddy 会使用其捆绑的 Node.js,并自动适配 Windows 的 `npx.cmd` 与 PATH 分隔符。
36
-
37
- > 也可在 WorkBuddy 插件市场直接搜索 `@scorehub/creator-outreach` 安装。
44
+ MCP Server(`@scorehub/mcp-server`)在写入配置前会先通过 `--self-check --json` 验证可执行性,之后由 `npx -y @scorehub/mcp-server@latest` 在 WorkBuddy 启动时自动拉起。WorkBuddy 若已自带可用 Node.js / npx,会优先复用;只有在存在明确缺失证据时,Tiky 才会引导你安装官方 Node.js LTS。
38
45
 
39
46
  ## 首次使用与授权
40
47
 
41
- 每个新对话的第一轮,Tiky 会先介绍达人搜索、画像评估和小范围建联验证三项能力,说明她不是海量群发工具,并给出当前支持的东南亚市场、典型用法和下一步选项。明确要求切换账号或店铺时除外:切换店铺会直接打开当前账号的店铺确认页,切换账号才会打开 ScoreHub 登录页。选择需要的操作后,它才会在首次调用 TikTok 功能时打开浏览器引导你登录 ScoreHub。授权、重新授权或店铺切换成功后,Tiky 会依次确认当前登录账号、当前授权店铺、店铺所属国家和店铺关联品牌。登录后 Token 缓存在 `~/.scorehub/`,Claude Code 与 WorkBuddy 共享,无需重复授权。
48
+ 每个新对话的第一轮,Tiky 都先检查 `uninitialized` / `restart_required` / `ready` / `repair_required` bootstrap 状态。只有 `ready` 才进入能力介绍、账号/店铺切换或业务操作。首次 TikTok 工具调用时会打开浏览器引导你登录 ScoreHub。授权、重新授权或店铺切换成功后,Tiky 会依次确认当前登录账号、当前授权店铺、店铺所属国家和店铺关联品牌。Token 缓存在 `~/.scorehub/`,不会被 bootstrap 或升级清除。
42
49
 
43
50
  如果提示当前店铺的 TikTok 授权已失效或异常,本地 ScoreHub 登录通常仍然有效。请先到 ScoreHub 重新绑定店铺,再重试;不要反复进行浏览器登录。
44
51
 
@@ -56,13 +63,13 @@ MCP Server(`@scorehub/mcp-server`)由配置中的 `npx -y` 在首次启动
56
63
 
57
64
  ### 找不到受支持的客户端
58
65
 
59
- 先安装并启动 Claude Code 或 WorkBuddy 一次,再重新运行安装命令。Claude Desktop 不在当前支持范围内。
66
+ 先安装并启动 WorkBuddy 一次,再通过分享链接唤起安装 Tiky。
60
67
 
61
68
  ### MCP 工具不可用、工具列表为空或连接已关闭
62
69
 
63
- ScoreHub 服务暂时未连接。请完全退出并重新打开 Claude Code 或 WorkBuddy 后重试;如仍无法使用,请联系 ScoreHub 支持。
70
+ Tiky 会优先运行 bootstrap 检查。未安装、需重启或需修复时,她不会尝试 OAuth 或业务调用;已显示 `ready` 但工具仍不可用时,请完全退出并重新打开 WorkBuddy 后重试。
64
71
 
65
- 如果错误明确提示找不到 Node.js、npm 或 npx,或 Node.js 版本低于 18,已安装的 Tiky 会先检查本地运行环境。经你确认后,它会使用 Node.js 官方 LTS 安装路径协助恢复,复检通过后提示你重启客户端。该流程不会重新授权或修改 TikTok 凭证;首次安装 Tiky 前仍需自行先安装 Node.js。
72
+ 如果错误明确提示找不到 Node.js、npm 或 npx,或 Node.js 版本低于 18,Tiky 会先说明诊断结论。经你确认后,它会使用 Node.js 官方 LTS 安装路径协助恢复,复检通过后提示你重启 WorkBuddy。该流程不会重新授权或修改 TikTok 凭证。
66
73
 
67
74
  ## 相关链接
68
75
 
@@ -22,11 +22,22 @@ skills:
22
22
 
23
23
  我会基于数据给出明确判断:达人是否适合你的品类与受众、是否值得建联,以及应该如何排定合作优先级,而不只是罗列数据。
24
24
 
25
+ 本文件是 Tiky 智能体行为规范的唯一权威来源。`skills/*/SKILL.md` 只保留各领域特有的触发条件、输入输出契约和关键陷阱;`docs/README.md` 与包级 README 仅做产品、安装和协作文档说明。若摘要文档与本文件表述不一致,以本文件为准。
26
+
25
27
  ## 新会话首轮引导(最高优先级)
26
28
 
27
- 每个新对话的第一条回复,除“账号与店铺切换”这一会话准备操作外,**无论用户输入问候、快捷指令还是具体需求**,我都必须先完成能力介绍,不执行搜索、分析、建联或授权。不要展示工具名、技术配置、错误码或内部流程。
29
+ 每个新对话的第一条回复,**无论用户输入问候、快捷指令、账号/店铺切换还是具体业务需求**,都必须先完成 WorkBuddy bootstrap 门禁。bootstrap 未完成时不执行搜索、分析、建联、`authorize` 或 `status`,也不输出标准欢迎语。
30
+
31
+ 首先执行 `npx -y @scorehub/creator-outreach@latest bootstrap --check --json`,并严格按结构化状态处理:
32
+
33
+ - `uninitialized`:只告知用户 Tiky 需要安装 ScoreHub 本地组件才能使用达人能力,且不会清除现有授权数据;**必须等待用户明确确认**。确认后执行 `npx -y @scorehub/creator-outreach@latest bootstrap --install --json`。返回 `restart_required` 后提示完全退出并重启 WorkBuddy,本轮结束。这次确认同时代表用户允许后续静默更新,不再对每个 creator-outreach 新版本重复询问。
34
+ - `restart_required`:若当前 `status` 已可用,且返回 `config_source.managed_by = "@scorehub/creator-outreach"` 与 `config_source.client_host = "workbuddy"`,执行 `npx -y @scorehub/creator-outreach@latest bootstrap --mark-ready --json` 清除重启标记并继续;否则只提示完全退出并重启 WorkBuddy,不进入业务流程。
35
+ - `repair_required`:说明结果中的可恢复原因。若明确是 Node.js / npm / npx 问题,进入“本地运行环境恢复”;其他情况经用户确认后重试 `bootstrap --install --json`。修复前不进入业务流程。
36
+ - `ready`:仅当结果同时返回 `update_due = true` 时,执行 `npx -y @scorehub/creator-outreach@latest bootstrap --update --silent --json`;否则直接继续。更新返回 `ready` 时继续;返回 `restart_required` 时可继续使用当前已加载版本,但需在首轮末尾简短提示重启后使用新版本。网络检查失败但现有 MCP 仍可用时,保留当前版本并继续,不误判为 OAuth 问题。
28
37
 
29
- 在输出欢迎语前,先识别以下明确意图:
38
+ 宿主不支持执行本地命令时,回退为提示用户重新打开分享链接 `https://www.workbuddy.cn/work/launch/?sharecode=lNM8H05BRKoV-dw2gac3ZwQ-p01o3C3KVz1gXJ_CwvtX02-mSRqBB3xepE4V0gr7&expertname=Tiky+%C2%B7+TikTok%E8%BE%BE%E4%BA%BA%E8%90%A5%E9%94%80%E4%B8%93%E5%AE%B6&buddy_type=workbuddy` 进行覆盖安装;不要让终端用户手工编辑 `mcp.json`。
39
+
40
+ bootstrap 返回 `ready` 后,再识别以下明确意图:
30
41
 
31
42
  - 用户要求“切换店铺”“换店”“使用另一个店铺”或同义表达时,立即调用 `authorize`,参数为 `{ "switch_shop": true }`。这会直接打开当前账号的店铺确认页,**不得**传 `force`、不得要求用户重新登录、不得先输出欢迎语。
32
43
  - 用户要求“切换账号”“换号”“登录另一个账号”或同义表达时,立即调用 `authorize`,参数为 `{ "force": true }`。这会重新打开 ScoreHub 登录页,**不得**传 `switch_shop`、不得先输出欢迎语。
@@ -156,16 +167,18 @@ Step 3: 小范围建联验证
156
167
 
157
168
  搜索和评分结果不直接展示 MCP 原始 JSON,统一遵守对应 Skill 的固定输出契约:
158
169
 
159
- - 搜索条件确定后不等待二次确认;类目闭环校验和回退重搜完成后,先展示最终实际生效的市场、类目、GMV、关键词、粉丝画像和目标展示人数,再给结果摘要与达人列表。未设置的维度写“未设置”,未显式指定市场时写“由当前授权店铺决定”。
160
- - 搜索结果按“搜索条件 / 结果摘要 / 排名 / 昵称 / 用户名 / 粉丝数 / 近 30 天 GMV 区间 / 类目匹配 / 达人 ID / 分页状态 / 下一步”顺序展示。默认保持搜索接口返回顺序;只有用户明确要求时才重排。
170
+ - 搜索条件确定后不等待二次确认;类目闭环校验和回退重搜完成后,直接给结果摘要与达人列表。市场、类目、GMV、关键词、粉丝画像和目标展示人数仅用于内部搜索会话、翻页与校验,不得展示在搜索结果或评分结果中。
171
+ - 搜索结果按“结果摘要 / 排名 / 昵称 / 用户名 / 粉丝数 / 近 30 天 GMV 区间 / 类目匹配 / 达人 ID / 分页状态 / 下一步”顺序展示。默认保持搜索接口返回顺序;只有用户明确要求时才重排。
172
+ - 搜索结果的下一步建议包含分析选中达人、继续翻页、调整条件,以及选择 2 位及以上达人进行相对评分排名。评分建议只引导用户明确指定候选人,不自动调用 `creator_performance`。
161
173
  - 用户未指定数量时默认展示 20 位;用户指定超过 60 位时按 60 位执行并提前告知。相同条件下的首次搜索和继续翻页累计最多展示 60 位,条件变更或明确重新搜索才重置额度。达到上限时提示调整条件后重新搜索,不再建议继续翻页;内部 `page_token` 永不展示。
162
174
  - 搜索结果若包含 `username`,则“昵称”列链接到 `https://www.tiktok.com/@{username}`;“用户名”列继续显示纯文本 `@username`。`username` 缺失时昵称保持纯文本,不猜测主页地址。
163
175
  - 每位达人只保留搜索结果返回的 `creator_open_id`。表现分析时将该值放入兼容入参 `creator_user_id`,建联时将同一个值放入 `creator_open_id`。ID 在界面中弱化展示,但不得丢失、改写或推断。
164
176
  - 单达人只展示表现画像、数据缺口和合作建议,不生成候选集相对综合分;至少 2 位候选人才输出综合评分排行榜。
165
177
  - 多人评分固定展示综合分、五个维度分、标签,并补充逐人详情、数据缺口、合作建议和失败名单。默认按综合分、带货分、内容分、粉丝数依次降序处理并列。
178
+ - 评分结果可展示评分对象范围、成功/失败数和相对评分说明,但不复述候选人来源搜索的条件。
166
179
  - 字段缺失显示 `—` 并说明原因;失败达人不以 0 分参与排名;禁止编造缺失指标、内容风格或合作历史。
167
180
 
168
- 结果为 1–5 条时使用 Markdown;达到 6 条时,如果当前宿主提供 HTML/可视化产物能力,默认生成响应式 HTML 报告。用户明确要求 HTML 时不受条数限制;能力不可用或生成失败时回退完整 Markdown。
181
+ 搜索结果为 1–5 条时使用 Markdown。在 WorkBuddy 中达到 6 条时,必须通过当前宿主的 HTML 产物通道创建响应式 HTML 报告;不得仅因宿主未显式声明 HTML/可视化能力而回退 Markdown。仅当产物实际创建失败时,回退为包含全部记录的 Markdown 并如实说明未生成报告。用户明确要求 HTML 时不受条数限制;非 WorkBuddy 宿主使用完整 Markdown。
169
182
 
170
183
  HTML 报告必须自包含,只使用内联 CSS/JavaScript,不依赖 CDN、远程字体或外部图片;工具返回文本必须先做 HTML 转义。搜索结果中的昵称链接若存在,必须使用新标签页打开并带 `rel="noopener noreferrer"`,URL 中的 `username` 需做安全编码。只实现真实可用的搜索、排序、展开和复制交互,不添加无法调用 Agent/MCP 的伪按钮。生成报告后仍在对话中保留简短摘要,且只有实际生成产物时才可声明存在 HTML,不得虚构文件名或 `outputs` 路径。
171
184
 
@@ -174,15 +187,15 @@ HTML 报告必须自包含,只使用内联 CSS/JavaScript,不依赖 CDN、
174
187
  1. **单步确认,多步通报**:单次操作(搜索/分析单个达人)直接执行并汇报结果;多位达人分析或小范围建联验证先确认计划再执行。
175
188
  2. **建联前置确认**:建联验证前必须与你确认品牌名和佣金比例(用于模板变量替换),并让你核对消息内容。
176
189
  3. **画像优先与规模边界**:建联的目的是验证精选候选人的合作假设,而非向成千上万位达人海量群发。当用户提出此类要求时,明确说明不属于 Tiky 的定位,并引导先搜索、分析、排名,再确定精选小名单进行建联验证。
177
- 3. **配额意识**:你每天有 10,000 次 API 配额,我每次操作都会告知消耗量。如果预算紧张,我会建议优先级策略。
178
- 4. **失败不沉默**:API 调用失败时我会用中文明确告知原因和下一步处理建议,不向用户展示错误码或英文原文。
179
- 5. **数据可追溯**:每次搜索结果、分析报告、建联记录都保留完整,方便你后续回溯。
180
- 6. **不在深夜骚扰达人**:建联消息默认按达人所在时区的工作时间(9:00-21:00)发送,避免深夜打扰。
181
- 7. **类目解析规范**:当用户提到品类名称(如"美妆"/"电子产品")时,必须先用 `get_categories` 解析为类目 ID,再搜索达人。绝不要凭空猜测类目 ID 或使用硬编码值。如果 `get_categories` 返回多个匹配结果,用表格展示让用户确认具体要筛选哪些类目。
182
- 8. **结构化错误优先**:远程工具若返回结构化 JSON,必须优先读取其中的 `error_type` 决定下一步动作,不要靠自然语言错误文本猜测。
183
- 9. **两层授权区分**:当 `error_type = "shop_auth_invalid"`,或者 `authorize` / `status` 正常但真实 TikTok 工具提示当前店铺授权失效、过期、撤销或异常时,我必须判断为店铺侧 TikTok 绑定问题,直接提醒用户去 ScoreHub 重新绑定该店铺;不要让用户重复本地 OAuth 登录。
184
- 10. **限流优先等待**:当 `error_type = "rate_limited"` 或 `error_type = "quota_exhausted"`,或者提示请求过于频繁、持续限流或配额受限时,我只会建议你耐心等待、分批执行、缩小请求范围或稍后重试;不要建议“重新授权拿一个干净的 token”,也不要主动调用 `authorize`。只有你自己明确要求重新授权时,我才会这么做。
185
- 11. **切换与重新绑定区分**:用户明确要求切换店铺时,调用 `authorize({ "switch_shop": true })` 打开店铺确认页;用户明确要求切换账号时,调用 `authorize({ "force": true })` 重新登录。当前店铺返回 `shop_auth_invalid` 时仍是 TikTok 授权重新绑定问题,不能把它误作账号或店铺切换。
190
+ 4. **配额意识**:你每天有 10,000 次 API 配额,我每次操作都会告知消耗量。如果预算紧张,我会建议优先级策略。
191
+ 5. **失败不沉默**:API 调用失败时我会用中文明确告知原因和下一步处理建议,不向用户展示错误码或英文原文。
192
+ 6. **数据可追溯**:每次搜索结果、分析报告、建联记录都保留完整,方便你后续回溯。
193
+ 7. **不在深夜骚扰达人**:建联消息默认按达人所在时区的工作时间(9:00-21:00)发送,避免深夜打扰。
194
+ 8. **类目解析规范**:当用户提到品类名称(如"美妆"/"电子产品")时,必须先用 `get_categories` 解析为类目 ID,再搜索达人。绝不要凭空猜测类目 ID 或使用硬编码值。如果 `get_categories` 返回多个匹配结果,用表格展示让用户确认具体要筛选哪些类目。
195
+ 9. **结构化错误优先**:远程工具若返回结构化 JSON,必须优先读取其中的 `error_type` 决定下一步动作,不要靠自然语言错误文本猜测。
196
+ 10. **两层授权区分**:当 `error_type = "shop_auth_invalid"`,或者 `authorize` / `status` 正常但真实 TikTok 工具提示当前店铺授权失效、过期、撤销或异常时,我必须判断为店铺侧 TikTok 绑定问题,直接提醒用户去 ScoreHub 重新绑定该店铺;不要让用户重复本地 OAuth 登录。
197
+ 11. **限流优先等待**:当 `error_type = "rate_limited"` 或 `error_type = "quota_exhausted"`,或者提示请求过于频繁、持续限流或配额受限时,我只会建议你耐心等待、分批执行、缩小请求范围或稍后重试;不要建议“重新授权拿一个干净的 token”,也不要主动调用 `authorize`。只有你自己明确要求重新授权时,我才会这么做。
198
+ 12. **切换与重新绑定区分**:用户明确要求切换店铺时,调用 `authorize({ "switch_shop": true })` 打开店铺确认页;用户明确要求切换账号时,调用 `authorize({ "force": true })` 重新登录。当前店铺返回 `shop_auth_invalid` 时仍是 TikTok 授权重新绑定问题,不能把它误作账号或店铺切换。
186
199
 
187
200
  ## 调用方式
188
201
 
@@ -203,15 +216,17 @@ HTML 报告必须自包含,只使用内联 CSS/JavaScript,不依赖 CDN、
203
216
 
204
217
  只有在宿主错误、终端输出或用户明确反馈表明 `node`、`npm` 或 `npx` 不存在,或者 Node.js 版本低于 18 时,才进入本节。普通的 MCP 工具不可见、工具列表为空、连接关闭或本地 MCP 未启动本身**不是**运行环境缺失的证据,仍按本节后的常规断连提示处理。不要将 `shop_auth_invalid`、`oauth_invalid`、`rate_limited` 或 `quota_exhausted` 误判为运行环境问题,也不要在本流程调用 `authorize`。
205
218
 
219
+ 在 WorkBuddy 公开版中,优先复用 WorkBuddy 已捆绑且可用的 Node.js / npx。只有宿主已明确报告其捆绑运行时不可用,或三条版本命令明确失败/版本过低时,才引导用户安装系统级 Node.js LTS。执行 `npx -y @scorehub/creator-outreach@latest` 前,也必须先尽量复用 WorkBuddy 当前可用的 Node.js / npx。
220
+
206
221
  此例外中可以向用户说明 Node.js、npm 和 npx:
207
222
 
208
223
  1. 宿主提供终端能力时,先读取操作系统,并检查 `node --version`、`npm --version`、`npx --version`。在 WorkBuddy 中,优先检查其已配置的捆绑 Node.js / npx;可用时不要求用户安装另一份。
209
224
  2. 宿主不提供终端能力时,说明无法自动诊断,请用户运行上述三条命令并提供结果;若错误已明确指出 `node`、`npm` 或 `npx` 缺失,可直接进入下一步。
210
225
  3. 缺失、不完整或版本低于 18 时,先明确告知诊断结论、需要安装 Node.js LTS、可能需要管理员授权和将打开官方安装入口;**必须等待用户明确确认**,不得静默下载、安装或修改 PATH。
211
226
  4. 用户确认后:macOS 和 Windows 打开 `https://nodejs.org/en/download` 并引导安装 LTS;Linux 先识别发行版,再使用 NodeSource 的受支持安装路径。没有终端或打开链接能力时,提供同样的官方步骤并等待用户自行完成。不得使用未验证的第三方下载源。
212
- 5. 安装完成后,重新检查三条版本命令。只有 Node.js 为 18+、npm 和 npx 均可用时,才提示用户完全退出并重新打开 Claude Code 或 WorkBuddy;现有 `npx -y @scorehub/mcp-server@latest` 配置会自动拉取并启动本地代理。Node.js 和 npm 存在但 npx 缺失时,视为不完整安装,指导修复或重装 Node.js LTS,**不得**改写 MCP 配置为其他启动命令。
227
+ 5. 安装完成后,重新检查三条版本命令。只有 Node.js 为 18+、npm 和 npx 均可用时,才提示用户完全退出并重新打开 WorkBuddy;现有 `npx -y @scorehub/mcp-server@latest` 配置会自动拉取并启动本地代理。Node.js 和 npm 存在但 npx 缺失时,视为不完整安装,指导修复或重装 Node.js LTS,**不得**改写 MCP 配置为其他启动命令。
213
228
 
214
- 如果没有上述明确证据,直接告诉用户:“ScoreHub 服务暂时未连接,请完全退出并重新打开 Claude Code 或 WorkBuddy 后重试;如仍无法使用,请联系 ScoreHub 支持。”不要向终端用户提及 Node.js、npm、网络、环境变量、TikTok API 凭证或直连 API,也不要调用 `authorize`。
229
+ 如果 bootstrap 已返回 `ready`,但 MCP 工具仍不可见、连接关闭或本地 MCP 未启动,直接告诉用户:“ScoreHub 服务暂时未连接,请完全退出并重新打开 WorkBuddy 后重试;如仍无法使用,请联系 ScoreHub 支持。”不要调用 `authorize`,也不要把该情况自动误判为系统级 Node.js 缺失。
215
230
 
216
231
  如果本地登录状态正常,但真实工具调用提示当前店铺的 TikTok 授权已失效或异常,我会直接提醒你去 ScoreHub 重新绑定该店铺后再试,不会再让你重复浏览器登录。
217
232
 
package/install.js CHANGED
@@ -1,12 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const fs = require('fs');
4
- const path = require('path');
5
- const os = require('os');
6
- const { execSync } = require('child_process');
3
+ const fs = require('node:fs');
4
+ const os = require('node:os');
5
+ const path = require('node:path');
6
+ const { execFileSync } = require('node:child_process');
7
7
 
8
8
  const pkg = require('./package.json');
9
+
9
10
  const MIN_NODE_MAJOR = 18;
11
+ const STATE_SCHEMA_VERSION = 1;
12
+ const UPDATE_INTERVAL_MS = 24 * 60 * 60 * 1000;
13
+ const WORKBUDDY_SHARE_URL = 'https://www.workbuddy.cn/work/launch/?sharecode=lNM8H05BRKoV-dw2gac3ZwQ-p01o3C3KVz1gXJ_CwvtX02-mSRqBB3xepE4V0gr7&expertname=Tiky+%C2%B7+TikTok%E8%BE%BE%E4%BA%BA%E8%90%A5%E9%94%80%E4%B8%93%E5%AE%B6&buddy_type=workbuddy';
14
+ const CREATOR_OUTREACH_SELF_BOOTSTRAP_COMMAND = 'npx -y @scorehub/creator-outreach@latest';
15
+ const MCP_SERVER_SELF_CHECK_COMMAND = 'npx -y @scorehub/mcp-server@latest --self-check --json';
10
16
 
11
17
  const MCP_CONFIG = {
12
18
  type: 'stdio',
@@ -14,6 +20,8 @@ const MCP_CONFIG = {
14
20
  args: ['-y', '@scorehub/mcp-server@latest'],
15
21
  env: {
16
22
  SCOREHUB_REMOTE_MCP_URL: 'https://app.scorehub.cn/mcp',
23
+ SCOREHUB_CLIENT_HOST: 'workbuddy',
24
+ SCOREHUB_MANAGED_BY: '@scorehub/creator-outreach',
17
25
  },
18
26
  };
19
27
 
@@ -26,18 +34,17 @@ function isSupportedNode(nodeVersion = process.versions.node) {
26
34
  }
27
35
 
28
36
  function getPaths(homeDir) {
29
- const claudeDir = path.join(homeDir, '.claude');
30
37
  const workBuddyDir = path.join(homeDir, '.workbuddy');
38
+ const scorehubDir = path.join(workBuddyDir, 'scorehub');
31
39
 
32
40
  return {
33
- claudeDir,
34
- agentsDir: path.join(claudeDir, 'agents'),
35
- claudeJson: path.join(homeDir, '.claude.json'),
36
41
  workBuddyDir,
37
42
  workBuddyPluginDir: path.join(
38
43
  workBuddyDir, 'plugins', 'marketplaces', 'my-experts', 'plugins', 'tiktok-creator-outreach',
39
44
  ),
40
45
  workBuddyMcpJson: path.join(workBuddyDir, 'mcp.json'),
46
+ bootstrapState: path.join(scorehubDir, 'bootstrap-state.json'),
47
+ bootstrapDir: scorehubDir,
41
48
  };
42
49
  }
43
50
 
@@ -45,9 +52,9 @@ function resolveWorkBuddyNode(workBuddyDir) {
45
52
  const versionsDir = path.join(workBuddyDir, 'binaries', 'node', 'versions');
46
53
  const npxExecutable = process.platform === 'win32' ? 'npx.cmd' : 'npx';
47
54
  try {
48
- const versions = fs.readdirSync(versionsDir);
49
- if (versions.length > 0) {
50
- const versionDir = path.join(versionsDir, versions[0]);
55
+ const versions = fs.readdirSync(versionsDir).sort().reverse();
56
+ for (const version of versions) {
57
+ const versionDir = path.join(versionsDir, version);
51
58
  const binDirs = [path.join(versionDir, 'bin'), versionDir];
52
59
  for (const binDir of binDirs) {
53
60
  const npxPath = path.join(binDir, npxExecutable);
@@ -60,120 +67,349 @@ function resolveWorkBuddyNode(workBuddyDir) {
60
67
  return { npx: 'npx', binDir: null };
61
68
  }
62
69
 
63
- function fetchLatestMcpServerVersion() {
70
+ function readJson(filePath) {
71
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
72
+ }
73
+
74
+ function writeJsonAtomic(filePath, value) {
75
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
76
+ const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
77
+ fs.writeFileSync(tempPath, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
64
78
  try {
65
- const out = execSync('npm view @scorehub/mcp-server version', {
66
- encoding: 'utf8',
67
- stdio: ['ignore', 'pipe', 'ignore'],
68
- timeout: 10000,
79
+ fs.renameSync(tempPath, filePath);
80
+ } catch (error) {
81
+ try { fs.rmSync(tempPath, { force: true }); } catch { /* preserve the original error */ }
82
+ throw error;
83
+ }
84
+ }
85
+
86
+ function snapshotFile(filePath) {
87
+ return fs.existsSync(filePath) ? fs.readFileSync(filePath) : null;
88
+ }
89
+
90
+ function restoreFile(filePath, snapshot) {
91
+ if (snapshot === null) {
92
+ fs.rmSync(filePath, { force: true });
93
+ return;
94
+ }
95
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
96
+ const tempPath = `${filePath}.${process.pid}.${Date.now()}.restore`;
97
+ fs.writeFileSync(tempPath, snapshot);
98
+ fs.renameSync(tempPath, filePath);
99
+ }
100
+
101
+ function defaultState() {
102
+ return {
103
+ schema_version: STATE_SCHEMA_VERSION,
104
+ consent_granted: false,
105
+ status: 'uninitialized',
106
+ creator_outreach_version: null,
107
+ observed_mcp_server_version: null,
108
+ last_check_at: null,
109
+ last_error: null,
110
+ restart_required: false,
111
+ };
112
+ }
113
+
114
+ function loadState(statePath) {
115
+ if (!fs.existsSync(statePath)) return defaultState();
116
+ try {
117
+ const state = readJson(statePath);
118
+ if (state.schema_version !== STATE_SCHEMA_VERSION) throw new Error('unsupported state schema');
119
+ return Object.assign(defaultState(), state);
120
+ } catch (error) {
121
+ const reason = error instanceof Error ? error.message : String(error);
122
+ return Object.assign(defaultState(), {
123
+ status: 'repair_required',
124
+ last_error: `bootstrap state is invalid: ${reason}`,
69
125
  });
70
- return out.trim() || null;
126
+ }
127
+ }
128
+
129
+ function readInstalledPluginVersion(pluginDir) {
130
+ const packagePath = path.join(pluginDir, 'package.json');
131
+ try {
132
+ const installed = readJson(packagePath);
133
+ return typeof installed.version === 'string' ? installed.version : null;
71
134
  } catch {
72
135
  return null;
73
136
  }
74
137
  }
75
138
 
76
- function mergeMcpConfig(configPath, serverName, serverConfig, extraFields) {
77
- let config = {};
78
- if (fs.existsSync(configPath)) {
79
- try {
80
- config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
81
- } catch {
82
- console.warn(`Warning: ${configPath} could not be parsed and will be replaced.`);
83
- }
139
+ function buildMcpEnvironment(version, binDir) {
140
+ const existingPath = process.env.PATH || process.env.Path || '';
141
+ return Object.assign({}, MCP_CONFIG.env, {
142
+ SCOREHUB_CREATOR_OUTREACH_VERSION: version,
143
+ ...(binDir ? { PATH: [binDir, existingPath].filter(Boolean).join(path.delimiter) } : {}),
144
+ });
145
+ }
146
+
147
+ function buildMcpConfig(version, npx, binDir) {
148
+ return {
149
+ command: npx,
150
+ args: MCP_CONFIG.args.slice(),
151
+ env: buildMcpEnvironment(version, binDir),
152
+ };
153
+ }
154
+
155
+ function readMcpConfig(configPath) {
156
+ if (!fs.existsSync(configPath)) return { exists: false, config: {} };
157
+ try {
158
+ return { exists: true, config: readJson(configPath) };
159
+ } catch (error) {
160
+ const reason = error instanceof Error ? error.message : String(error);
161
+ return { exists: true, config: null, error: `mcp.json is invalid: ${reason}` };
84
162
  }
163
+ }
164
+
165
+ function isManagedMcpConfig(config) {
166
+ const server = config && config.mcpServers && config.mcpServers.scorehub;
167
+ return !!server
168
+ && Array.isArray(server.args)
169
+ && server.args.includes('@scorehub/mcp-server@latest')
170
+ && server.env
171
+ && server.env.SCOREHUB_MANAGED_BY === '@scorehub/creator-outreach'
172
+ && server.env.SCOREHUB_CLIENT_HOST === 'workbuddy';
173
+ }
85
174
 
175
+ function mergeMcpConfig(configPath, serverConfig) {
176
+ const current = readMcpConfig(configPath);
177
+ if (current.config === null) throw new Error(current.error);
178
+ const config = current.config || {};
86
179
  config.mcpServers = config.mcpServers || {};
87
- config.mcpServers[serverName] = Object.assign({}, serverConfig, extraFields);
88
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
180
+ config.mcpServers.scorehub = serverConfig;
181
+ writeJsonAtomic(configPath, config);
89
182
  }
90
183
 
91
- function install({
92
- homeDir = os.homedir(),
93
- nodeVersion = process.versions.node,
94
- getLatestMcpServerVersion = fetchLatestMcpServerVersion,
95
- } = {}) {
96
- console.log(`${pkg.name} v${pkg.version}\n`);
184
+ function copyPluginToStage(stageDir) {
185
+ const pluginDirs = ['.codebuddy-plugin', 'agents', 'skills', 'avatars'];
186
+ const pluginFiles = ['package.json', 'README.md', 'README.en.md', 'install.js'];
187
+ fs.mkdirSync(stageDir, { recursive: true });
188
+ for (const dir of pluginDirs) {
189
+ const source = path.join(__dirname, dir);
190
+ if (fs.existsSync(source)) fs.cpSync(source, path.join(stageDir, dir), { recursive: true });
191
+ }
192
+ for (const file of pluginFiles) {
193
+ const source = path.join(__dirname, file);
194
+ if (fs.existsSync(source)) fs.copyFileSync(source, path.join(stageDir, file));
195
+ }
196
+ }
97
197
 
98
- if (!isSupportedNode(nodeVersion)) {
99
- console.error(`Node.js ${MIN_NODE_MAJOR}+ is required. Detected ${nodeVersion}. No configuration files were changed.`);
100
- return 1;
198
+ function replacePluginAtomic(pluginDir) {
199
+ const parentDir = path.dirname(pluginDir);
200
+ const stageDir = path.join(parentDir, `.tiktok-creator-outreach.stage-${process.pid}-${Date.now()}`);
201
+ const backupDir = path.join(parentDir, `.tiktok-creator-outreach.backup-${process.pid}-${Date.now()}`);
202
+ copyPluginToStage(stageDir);
203
+ try {
204
+ if (fs.existsSync(pluginDir)) fs.renameSync(pluginDir, backupDir);
205
+ fs.renameSync(stageDir, pluginDir);
206
+ return backupDir;
207
+ } catch (error) {
208
+ if (fs.existsSync(pluginDir) && fs.existsSync(backupDir)) fs.rmSync(pluginDir, { recursive: true, force: true });
209
+ if (fs.existsSync(backupDir) && !fs.existsSync(pluginDir)) fs.renameSync(backupDir, pluginDir);
210
+ if (fs.existsSync(stageDir)) fs.rmSync(stageDir, { recursive: true, force: true });
211
+ throw error;
101
212
  }
213
+ }
102
214
 
103
- const paths = getPaths(homeDir);
104
- const hasClaudeCode = fs.existsSync(paths.claudeDir);
105
- const hasWorkBuddy = fs.existsSync(paths.workBuddyDir);
215
+ function restorePlugin(pluginDir, backupDir) {
216
+ if (fs.existsSync(pluginDir)) fs.rmSync(pluginDir, { recursive: true, force: true });
217
+ if (backupDir && fs.existsSync(backupDir)) fs.renameSync(backupDir, pluginDir);
218
+ }
106
219
 
107
- if (!hasClaudeCode && !hasWorkBuddy) {
108
- console.error('No supported client was found. Launch Claude Code or WorkBuddy once, then run this command again. No configuration files were changed.');
109
- return 1;
220
+ function parseSelfCheckOutput(output) {
221
+ const lines = String(output).trim().split(/\r?\n/).reverse();
222
+ for (const line of lines) {
223
+ try {
224
+ const parsed = JSON.parse(line);
225
+ if (parsed && parsed.schema_version === 1 && typeof parsed.ok === 'boolean') return parsed;
226
+ } catch { /* npx may print non-JSON diagnostics before the payload */ }
110
227
  }
228
+ throw new Error('mcp-server self-check did not return a valid JSON payload');
229
+ }
111
230
 
112
- if (hasClaudeCode) {
113
- const agentSource = path.join(__dirname, 'agents', 'tiktok-creator-outreach.md');
114
- const agentDestination = path.join(paths.agentsDir, 'tiktok-creator-outreach.md');
115
- fs.mkdirSync(paths.agentsDir, { recursive: true });
116
- fs.copyFileSync(agentSource, agentDestination);
117
- mergeMcpConfig(paths.claudeJson, 'scorehub', MCP_CONFIG, {});
118
- console.log(`Claude Code: Agent installed at ${agentDestination}`);
119
- console.log(`Claude Code: MCP server configured at ${paths.claudeJson}`);
231
+ function runMcpSelfCheck({ npx, env, selfCheck = defaultMcpSelfCheck } = {}) {
232
+ return selfCheck({ npx, env });
233
+ }
234
+
235
+ function defaultMcpSelfCheck({ npx, env }) {
236
+ try {
237
+ const output = execFileSync(npx, ['-y', '@scorehub/mcp-server@latest', '--self-check', '--json'], {
238
+ encoding: 'utf8',
239
+ env,
240
+ stdio: ['ignore', 'pipe', 'pipe'],
241
+ timeout: 30000,
242
+ });
243
+ const payload = parseSelfCheckOutput(output);
244
+ if (!payload.ok) throw new Error(`mcp-server self-check failed for Node.js ${payload.node_version}`);
245
+ return payload;
246
+ } catch (error) {
247
+ if (error && typeof error === 'object' && 'stderr' in error && error.stderr) {
248
+ throw new Error(String(error.stderr).trim());
249
+ }
250
+ throw error;
120
251
  }
252
+ }
121
253
 
122
- if (hasWorkBuddy) {
123
- const pluginDirs = ['.codebuddy-plugin', 'agents', 'skills', 'avatars'];
124
- const pluginFiles = ['package.json', 'README.md', 'README.en.md'];
254
+ function detectStatus({ paths, state, nodeVersion }) {
255
+ if (!isSupportedNode(nodeVersion)) {
256
+ return { status: 'repair_required', reason: `Node.js ${MIN_NODE_MAJOR}+ is required. Detected ${nodeVersion}.` };
257
+ }
258
+ if (!fs.existsSync(paths.workBuddyDir)) {
259
+ return { status: 'repair_required', reason: 'WorkBuddy has not been started on this machine.' };
260
+ }
261
+ const mcp = readMcpConfig(paths.workBuddyMcpJson);
262
+ if (mcp.config === null) return { status: 'repair_required', reason: mcp.error };
263
+ if (state.status === 'repair_required') return { status: 'repair_required', reason: state.last_error || 'Bootstrap requires repair.' };
264
+ if (!state.consent_granted || !isManagedMcpConfig(mcp.config)) {
265
+ return { status: 'uninitialized', reason: 'WorkBuddy MCP bootstrap has not been confirmed.' };
266
+ }
267
+ if (state.restart_required) return { status: 'restart_required', reason: 'WorkBuddy must be restarted to load the updated plugin and MCP configuration.' };
268
+ return { status: 'ready', reason: null };
269
+ }
125
270
 
126
- fs.mkdirSync(paths.workBuddyPluginDir, { recursive: true });
127
- for (const dir of pluginDirs) {
128
- const source = path.join(__dirname, dir);
129
- const destination = path.join(paths.workBuddyPluginDir, dir);
130
- if (fs.existsSync(source)) {
131
- fs.cpSync(source, destination, { recursive: true });
132
- }
133
- }
271
+ function check({ homeDir = os.homedir(), nodeVersion = process.versions.node, now = Date.now() } = {}) {
272
+ const paths = getPaths(homeDir);
273
+ const state = loadState(paths.bootstrapState);
274
+ const detected = detectStatus({ paths, state, nodeVersion });
275
+ const lastCheckAt = state.last_check_at ? Date.parse(state.last_check_at) : Number.NaN;
276
+ const updateDue = detected.status === 'ready'
277
+ && (!Number.isFinite(lastCheckAt) || now - lastCheckAt >= UPDATE_INTERVAL_MS);
278
+ return {
279
+ schema_version: STATE_SCHEMA_VERSION,
280
+ status: detected.status,
281
+ reason: detected.reason,
282
+ creator_outreach_version: pkg.version,
283
+ consent_granted: state.consent_granted,
284
+ mcp_configured: detected.status === 'ready' || detected.status === 'restart_required',
285
+ restart_required: state.restart_required,
286
+ update_due: updateDue,
287
+ };
288
+ }
134
289
 
135
- for (const file of pluginFiles) {
136
- const source = path.join(__dirname, file);
137
- const destination = path.join(paths.workBuddyPluginDir, file);
138
- if (fs.existsSync(source)) {
139
- fs.copyFileSync(source, destination);
140
- }
141
- }
290
+ function assertInstallable({ homeDir, nodeVersion }) {
291
+ const paths = getPaths(homeDir);
292
+ if (!isSupportedNode(nodeVersion)) throw new Error(`Node.js ${MIN_NODE_MAJOR}+ is required. Detected ${nodeVersion}.`);
293
+ if (!fs.existsSync(paths.workBuddyDir)) throw new Error('No supported client was found. Launch WorkBuddy once, then retry.');
294
+ return { paths, node: resolveWorkBuddyNode(paths.workBuddyDir) };
295
+ }
296
+
297
+ function install({
298
+ homeDir = os.homedir(),
299
+ nodeVersion = process.versions.node,
300
+ selfCheck = defaultMcpSelfCheck,
301
+ markConsent = true,
302
+ replacePlugin = replacePluginAtomic,
303
+ } = {}) {
304
+ const { paths, node } = assertInstallable({ homeDir, nodeVersion });
305
+ const env = buildMcpEnvironment(pkg.version, node.binDir);
306
+ const mcpCheck = runMcpSelfCheck({ npx: node.npx, env, selfCheck });
307
+ const mcpSnapshot = snapshotFile(paths.workBuddyMcpJson);
308
+ const stateSnapshot = snapshotFile(paths.bootstrapState);
309
+ let pluginBackup = null;
310
+ let pluginReplaced = false;
311
+ try {
312
+ mergeMcpConfig(paths.workBuddyMcpJson, buildMcpConfig(pkg.version, node.npx, node.binDir));
313
+ pluginBackup = replacePlugin(paths.workBuddyPluginDir);
314
+ pluginReplaced = true;
142
315
 
143
- const { npx, binDir } = resolveWorkBuddyNode(paths.workBuddyDir);
144
- const { type: _, ...baseMcpConfig } = MCP_CONFIG;
145
- const existingPath = process.env.PATH || process.env.Path || '';
146
- const env = binDir
147
- ? Object.assign({}, MCP_CONFIG.env, { PATH: [binDir, existingPath].filter(Boolean).join(path.delimiter) })
148
- : MCP_CONFIG.env;
149
- const workBuddyMcpConfig = Object.assign({}, baseMcpConfig, { command: npx, env });
150
- mergeMcpConfig(paths.workBuddyMcpJson, 'scorehub', workBuddyMcpConfig, { disabled: false });
151
- console.log(`WorkBuddy: Plugin installed at ${paths.workBuddyPluginDir}`);
152
- console.log(`WorkBuddy: MCP server configured at ${paths.workBuddyMcpJson}`);
316
+ const state = loadState(paths.bootstrapState);
317
+ state.consent_granted = markConsent || state.consent_granted;
318
+ state.status = 'restart_required';
319
+ state.creator_outreach_version = pkg.version;
320
+ state.observed_mcp_server_version = mcpCheck.server_version || null;
321
+ state.last_check_at = new Date().toISOString();
322
+ state.last_error = null;
323
+ state.restart_required = true;
324
+ writeJsonAtomic(paths.bootstrapState, state);
325
+ } catch (error) {
326
+ if (pluginReplaced) restorePlugin(paths.workBuddyPluginDir, pluginBackup);
327
+ restoreFile(paths.workBuddyMcpJson, mcpSnapshot);
328
+ restoreFile(paths.bootstrapState, stateSnapshot);
329
+ throw error;
153
330
  }
331
+ if (pluginBackup && fs.existsSync(pluginBackup)) fs.rmSync(pluginBackup, { recursive: true, force: true });
332
+ return { status: 'restart_required', mcp_server_version: mcpCheck.server_version || null };
333
+ }
334
+
335
+ function markReady({ homeDir = os.homedir(), nodeVersion = process.versions.node } = {}) {
336
+ const paths = getPaths(homeDir);
337
+ const state = loadState(paths.bootstrapState);
338
+ const detected = detectStatus({ paths, state: Object.assign({}, state, { restart_required: false }), nodeVersion });
339
+ if (detected.status !== 'ready') throw new Error(detected.reason || `Cannot mark bootstrap as ready: ${detected.status}`);
340
+ state.status = 'ready';
341
+ state.restart_required = false;
342
+ state.last_error = null;
343
+ writeJsonAtomic(paths.bootstrapState, state);
344
+ return { status: 'ready' };
345
+ }
154
346
 
155
- const mcpServerVersion = getLatestMcpServerVersion();
156
- if (mcpServerVersion) {
157
- console.log(`\nmcp-server latest version: v${mcpServerVersion} (npx resolves @latest again when it starts).`);
158
- } else {
159
- console.log('\nmcp-server: @latest (the current version could not be queried).');
347
+ function update({
348
+ homeDir = os.homedir(),
349
+ nodeVersion = process.versions.node,
350
+ selfCheck = defaultMcpSelfCheck,
351
+ } = {}) {
352
+ const paths = getPaths(homeDir);
353
+ const state = loadState(paths.bootstrapState);
354
+ if (!state.consent_granted) return { status: 'uninitialized', reason: 'Initial installation confirmation is required.' };
355
+ const detected = detectStatus({ paths, state: Object.assign({}, state, { restart_required: false }), nodeVersion });
356
+ if (detected.status === 'repair_required' || detected.status === 'uninitialized') {
357
+ return { status: detected.status, reason: detected.reason };
358
+ }
359
+ if (state.restart_required) return { status: 'restart_required', reason: 'WorkBuddy must be restarted to load the updated plugin and MCP configuration.' };
360
+ if (readInstalledPluginVersion(paths.workBuddyPluginDir) === pkg.version) {
361
+ state.status = 'ready';
362
+ state.creator_outreach_version = pkg.version;
363
+ state.last_check_at = new Date().toISOString();
364
+ state.last_error = null;
365
+ writeJsonAtomic(paths.bootstrapState, state);
366
+ return { status: 'ready', creator_outreach_version: pkg.version };
160
367
  }
368
+ return install({ homeDir, nodeVersion, selfCheck, markConsent: false });
369
+ }
161
370
 
162
- const platforms = [];
163
- if (hasClaudeCode) platforms.push('Claude Code');
164
- if (hasWorkBuddy) platforms.push('WorkBuddy');
165
- console.log(`\nInstallation complete. Restart ${platforms.join(' / ')} and use Tiky.`);
166
- return 0;
371
+ function printJson(value) {
372
+ process.stdout.write(`${JSON.stringify(value)}\n`);
167
373
  }
168
374
 
169
- if (require.main === module) {
170
- process.exitCode = install();
375
+ function runCli(argv = process.argv.slice(2)) {
376
+ const action = argv[0];
377
+ const options = {
378
+ homeDir: process.env.SCOREHUB_BOOTSTRAP_HOME || os.homedir(),
379
+ nodeVersion: process.versions.node,
380
+ };
381
+ try {
382
+ let result;
383
+ if (action === 'bootstrap' && argv.includes('--check')) result = check(options);
384
+ else if (action === 'bootstrap' && argv.includes('--install')) result = install(options);
385
+ else if (action === 'bootstrap' && argv.includes('--update')) result = update(options);
386
+ else if (action === 'bootstrap' && argv.includes('--mark-ready')) result = markReady(options);
387
+ else result = install(options);
388
+ printJson(result);
389
+ return result.status === 'repair_required' ? 1 : 0;
390
+ } catch (error) {
391
+ const reason = error instanceof Error ? error.message : String(error);
392
+ printJson({ schema_version: STATE_SCHEMA_VERSION, status: 'repair_required', reason });
393
+ return 1;
394
+ }
171
395
  }
172
396
 
397
+ if (require.main === module) process.exitCode = runCli();
398
+
173
399
  module.exports = {
400
+ CREATOR_OUTREACH_SELF_BOOTSTRAP_COMMAND,
401
+ MCP_SERVER_SELF_CHECK_COMMAND,
174
402
  MIN_NODE_MAJOR,
403
+ UPDATE_INTERVAL_MS,
404
+ WORKBUDDY_SHARE_URL,
405
+ check,
406
+ detectStatus,
175
407
  getNodeMajorVersion,
176
408
  getPaths,
177
409
  install,
410
+ isManagedMcpConfig,
178
411
  isSupportedNode,
412
+ markReady,
413
+ runCli,
414
+ update,
179
415
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scorehub/creator-outreach",
3
- "version": "1.1.7",
3
+ "version": "1.2.0",
4
4
  "description": "ScoreHub AI TikTok达人营销专家 Tiky — Agent 定义 + Skills 配置",
5
5
  "engines": {
6
6
  "node": ">=18"
@@ -30,15 +30,7 @@
30
30
  1. **建会话**:调用 **`create_conversation`**,入参 `creator_open_id`(来自搜索结果),返回 `conversation_id`。
31
31
  2. **发消息**:调用 **`send_message`**,入参 `conversation_id` + `msg_type: "TEXT"` + `content`(模板填充后的文本)。
32
32
 
33
- 如果宿主错误、终端输出或用户明确反馈表明 `node`、`npm` 或 `npx` 缺失,或 Node.js 低于 18,优先按 Agent 的“本地运行环境恢复”流程处理:有终端时检查 `node --version`、`npm --version`、`npx --version`,WorkBuddy 优先复用捆绑运行时;先说明诊断与 Node.js LTS 安装影响并等待用户明确确认,再按操作系统打开官方安装入口或执行受支持步骤,最后复检并要求重启客户端。不得调用 `authorize`、静默安装或改写 MCP 配置。
34
-
35
- 其他 ScoreHub MCP 工具不可见、工具列表为空、连接已关闭或本地 MCP 未启动,直接提示用户:“ScoreHub 服务暂时未连接,请完全退出并重新打开 Claude Code 或 WorkBuddy 后重试;如仍无法使用,请联系 ScoreHub 支持。”不要向终端用户提及 Node.js、npm、网络、环境变量、TikTok API 凭证或直连 API,也不要调用 `authorize`。
36
-
37
- 如果工具返回结构化 JSON,优先按 `error_type` 做分流,不要靠自然语言错误文本猜测。
38
-
39
- 如果本地登录状态正常,但工具提示当前店铺的 TikTok 授权失效或异常,直接提醒用户去 ScoreHub 重新绑定该店铺后再试;不要展示错误码,也不要要求用户重复执行 `authorize`。
40
-
41
- 如果工具提示请求过于频繁、持续限流或配额受限,直接提醒用户暂停发送、耐心等待并稍后继续;不要建议“重新授权拿一个干净的 token”,也不要主动走 `authorize`,除非用户自己明确要求重新授权。
33
+ 授权、限流、MCP 不可用和本地运行环境恢复遵循 Agent 的权威规则,本技能不重复展开。
42
34
 
43
35
  ### 速率与话术
44
36
 
@@ -59,14 +51,4 @@
59
51
 
60
52
  ## 错误处理
61
53
 
62
- | 问题类型 | 处理 |
63
- |----------|------|
64
- | 明确的 Node.js / npm / npx 缺失或 Node.js < 18 | 按 Agent 的“本地运行环境恢复”流程:诊断后等待用户确认,使用官方 Node.js LTS 路径恢复、复检并重启客户端;不走 `authorize` |
65
- | 其他 MCP 工具不可见、工具列表为空、连接关闭或本地 MCP 未启动 | 提示“ScoreHub 服务暂时未连接,请完全退出并重新打开客户端后重试;如仍无法使用,请联系 ScoreHub 支持。”不提及技术配置,不走 `authorize` |
66
- | `oauth_invalid` | 仅在真正未授权或本地 OAuth 失效时,才允许提示用户调用 `authorize` |
67
- | 当前店铺的 TikTok 授权失效或异常 | 用中文提醒用户:本地登录状态正常,但当前店铺需要先到 ScoreHub 重新绑定后再试;不要展示错误码,不要要求重复 `authorize` |
68
- | 建会话入参不对或达人标识无效 | 检查传入的是来自搜索结果的 `creator_open_id` |
69
- | 消息配额已用完或当前达人不满足发送条件 | 跳过该达人,继续后续名单 |
70
- | 日配额已用完 | 停止全部 |
71
- | 请求过于频繁或持续限流 | 先提醒用户暂停发送、耐心等待并稍后继续;必要时有限退避重试。不要建议重新授权;只有用户明确要求时才走 `authorize` |
72
- | 参数无效 | 提示用户修正名单、消息内容或建联参数,不走授权路径 |
54
+ 建联专属错误分支仅处理 `creator_open_id` 无效、`create_conversation` 参数不合法、消息内容不合法,以及单达人消息配额或发送条件不满足;其余授权、限流、MCP 可用性与本地环境问题均遵循 Agent 权威规则。
@@ -20,19 +20,11 @@ description: 获取 TikTok 达人近 30 天表现数据,按候选集相对评
20
20
  对候选达人**逐个**调用 MCP 工具 **`creator_performance`**:工具入参名保留为 `creator_user_id`,其值必须直接使用搜索结果中的 `creator_open_id`。拿到表现数据后应用
21
21
  [评分模型](./references/scoring-model.md) 打分排序。批量分析属多步操作,先与用户确认名单再执行。
22
22
 
23
- 如果宿主错误、终端输出或用户明确反馈表明 `node`、`npm` 或 `npx` 缺失,或 Node.js 低于 18,优先按 Agent 的“本地运行环境恢复”流程处理:有终端时检查 `node --version`、`npm --version`、`npx --version`,WorkBuddy 优先复用捆绑运行时;先说明诊断与 Node.js LTS 安装影响并等待用户明确确认,再按操作系统打开官方安装入口或执行受支持步骤,最后复检并要求重启客户端。不得调用 `authorize`、静默安装或改写 MCP 配置。
24
-
25
- 其他 ScoreHub MCP 工具不可见、工具列表为空、连接已关闭或本地 MCP 未启动,直接提示用户:“ScoreHub 服务暂时未连接,请完全退出并重新打开 Claude Code 或 WorkBuddy 后重试;如仍无法使用,请联系 ScoreHub 支持。”不要向终端用户提及 Node.js、npm、网络、环境变量、TikTok API 凭证或直连 API,也不要调用 `authorize`。
26
-
27
- 如果工具返回结构化 JSON,优先按 `error_type` 做分流,不要靠自然语言错误文本猜测。
28
-
29
- 如果本地登录状态正常,但工具提示当前店铺的 TikTok 授权失效或异常,直接提醒用户去 ScoreHub 重新绑定该店铺后再试;不要展示错误码,也不要要求用户重复执行 `authorize`。
30
-
31
- 如果工具提示请求过于频繁、持续限流或配额受限,直接提醒用户耐心等待、减少本次分析人数或稍后重试;不要建议“重新授权拿一个干净的 token”,也不要主动走 `authorize`,除非用户自己明确要求重新授权。
23
+ 授权、限流、MCP 不可用和本地运行环境恢复遵循 Agent 的权威规则,本技能不重复展开。
32
24
 
33
25
  ### 输出(固定格式)
34
26
 
35
- 禁止直接展示 MCP 原始 JSON。评分是候选集内归一化后的**相对分**,按以下两种情况输出:
27
+ 禁止直接展示 MCP 原始 JSON,也不得复述候选人来源搜索的市场、类目、GMV、关键词、粉丝画像或目标人数等条件。评分是候选集内归一化后的**相对分**,按以下两种情况输出:
36
28
 
37
29
  #### 单个达人
38
30
 
@@ -42,7 +34,7 @@ description: 获取 TikTok 达人近 30 天表现数据,按候选集相对评
42
34
 
43
35
  #### 两个及以上达人
44
36
 
45
- 1. **评分摘要**:成功分析数、失败数、评分对象范围和相对评分说明。
37
+ 1. **评分摘要**:成功分析数、失败数、评分对象范围和相对评分说明;不展示原搜索条件。
46
38
  2. **排行榜**:固定展示“排名 / 达人 / 综合分 / 带货能力 / 内容影响力 / 粉丝规模 / 品类匹配 / 粉丝质量 / 标签”。
47
39
  3. **达人详情**:逐人展示关键原始指标、优势/风险、数据缺口、合作建议,以及从搜索上下文保留的标识。
48
40
  4. **失败名单**:部分分析失败时单列达人和用户可理解的原因,不得把失败值作为 0 分参与排序。
@@ -60,7 +52,7 @@ description: 获取 TikTok 达人近 30 天表现数据,按候选集相对评
60
52
 
61
53
  - 成功结果为 1–5 位时使用 Markdown;达到 **6 位**时,若当前宿主提供 HTML/可视化产物能力,默认生成自包含的响应式 HTML 报告。用户明确要求 HTML 时不受人数限制。
62
54
  - 宿主不支持 HTML,或 HTML 生成失败时,必须回退为包含完整排行榜、详情和失败名单的 Markdown。
63
- - HTML 评分报告必须包含:评分摘要、可排序排行榜、五维分数、达人详情、数据缺口、合作建议和弱化的达人 ID 复制区域。
55
+ - HTML 评分报告必须包含:不含原搜索条件的评分摘要、可排序排行榜、五维分数、达人详情、数据缺口、合作建议和弱化的达人 ID 复制区域。
64
56
  - 只使用内联 CSS/JavaScript,不依赖 CDN、远程字体或外部图片;只实现真实可用的搜索、排序、展开和复制交互,不添加无法触发 Agent 或 MCP 操作的伪按钮。
65
57
  - 昵称、用户名、标签及所有工具返回文本写入 HTML 前必须转义;禁止把工具内容作为 HTML 或脚本执行。
66
58
  - 生成 HTML 后仍需在对话中给出成功/失败数量、前三名和数据完整性的简短摘要。只有实际生成产物时才可声明存在 HTML,不得虚构文件名或 `outputs` 路径。
@@ -80,13 +72,4 @@ description: 获取 TikTok 达人近 30 天表现数据,按候选集相对评
80
72
 
81
73
  ## 错误处理
82
74
 
83
- | 问题类型 | 处理 |
84
- |----------|------|
85
- | 明确的 Node.js / npm / npx 缺失或 Node.js < 18 | 按 Agent 的“本地运行环境恢复”流程:诊断后等待用户确认,使用官方 Node.js LTS 路径恢复、复检并重启客户端;不走 `authorize` |
86
- | 其他 MCP 工具不可见、工具列表为空、连接关闭或本地 MCP 未启动 | 提示“ScoreHub 服务暂时未连接,请完全退出并重新打开客户端后重试;如仍无法使用,请联系 ScoreHub 支持。”不提及技术配置,不走 `authorize` |
87
- | `oauth_invalid` | 仅在真正未授权或本地 OAuth 失效时,才允许提示用户调用 `authorize` |
88
- | 当前店铺的 TikTok 授权失效或异常 | 用中文提醒用户:本地登录状态正常,但当前店铺需要先到 ScoreHub 重新绑定后再试;不要展示错误码,不要要求重复 `authorize` |
89
- | 达人当前不可分析(如无电商权限或区域不匹配) | 跳过该达人,并说明当前达人不满足分析条件 |
90
- | 日配额已用完 | 停止 |
91
- | 请求过于频繁或持续限流 | 先提醒用户耐心等待,建议减少本次分析人数或稍后重试;必要时有限退避重试。不要建议重新授权;只有用户明确要求时才走 `authorize` |
92
- | 参数无效 | 提示用户修正待分析名单或输入格式,不走授权路径 |
75
+ 分析专属错误分支仅处理候选名单无效、达人当前不可分析、数据缺口导致维度无法计算,以及 `creator_open_id` 无法从搜索上下文透传;其余授权、限流、MCP 可用性与本地环境问题均遵循 Agent 权威规则。
@@ -38,15 +38,7 @@ description: 在 TikTok Shop Creator Marketplace 中按关键词、类目、GMV
38
38
 
39
39
  调用 MCP 工具 **`search_creators`**,不要写脚本。若已授权可直接调用;未授权时先走 `authorize`。
40
40
 
41
- 如果宿主错误、终端输出或用户明确反馈表明 `node`、`npm` 或 `npx` 缺失,或 Node.js 低于 18,优先按 Agent 的“本地运行环境恢复”流程处理:有终端时检查 `node --version`、`npm --version`、`npx --version`,WorkBuddy 优先复用捆绑运行时;先说明诊断与 Node.js LTS 安装影响并等待用户明确确认,再按操作系统打开官方安装入口或执行受支持步骤,最后复检并要求重启客户端。不得调用 `authorize`、静默安装或改写 MCP 配置。
42
-
43
- 其他 ScoreHub MCP 工具不可见、工具列表为空、连接已关闭或本地 MCP 未启动,直接提示用户:“ScoreHub 服务暂时未连接,请完全退出并重新打开 Claude Code 或 WorkBuddy 后重试;如仍无法使用,请联系 ScoreHub 支持。”不要向终端用户提及 Node.js、npm、网络、环境变量、TikTok API 凭证或直连 API,也不要调用 `authorize`。
44
-
45
- 如果工具返回结构化 JSON,优先按 `error_type` 做分流,不要靠自然语言错误文本猜测。
46
-
47
- 如果本地登录状态正常,但工具提示当前店铺的 TikTok 授权失效或异常,直接提醒用户去 ScoreHub 重新绑定该店铺后再试;不要展示错误码,也不要要求用户重复执行 `authorize`。
48
-
49
- 如果工具提示请求过于频繁、持续限流或配额受限,直接提醒用户耐心等待、缩小搜索范围或稍后重试;不要建议“重新授权拿一个干净的 token”,也不要主动走 `authorize`,除非用户自己明确要求重新授权。
41
+ 授权、限流、MCP 不可用和本地运行环境恢复遵循 Agent 的权威规则,本技能不重复展开。
50
42
 
51
43
  ### 入参
52
44
 
@@ -60,7 +52,7 @@ description: 在 TikTok Shop Creator Marketplace 中按关键词、类目、GMV
60
52
 
61
53
  **GMV 区间取值**:`GMV_RANGE_0_100` / `GMV_RANGE_100_1000` / `GMV_RANGE_1000_10000` / `GMV_RANGE_10000_AND_ABOVE`
62
54
 
63
- ### 搜索条件与人数上限
55
+ ### 搜索会话与人数上限
64
56
 
65
57
  - 用户的已明确条件构成达人画像;不新增等待用户二次确认的回合。类目解析、类目过滤闭环校验和必要的回退重搜完成后,才以最终实际生效的条件作为本轮搜索条件。
66
58
  - 将同一组规范化搜索条件下的首次搜索和“继续翻页”视为同一搜索会话。用户修改任一搜索条件或明确要求重新搜索时,创建新的搜索会话并重置人数预算。
@@ -70,13 +62,12 @@ description: 在 TikTok Shop Creator Marketplace 中按关键词、类目、GMV
70
62
 
71
63
  ### 输出与后续(固定格式)
72
64
 
73
- 禁止直接展示 MCP 原始 JSON。每次成功搜索都按以下顺序输出:
65
+ 禁止直接展示 MCP 原始 JSON,也禁止展示市场、类目、GMV、关键词、粉丝画像或目标展示人数等搜索条件。每次成功搜索都按以下顺序输出:
74
66
 
75
- 1. **本次搜索条件**:先展示最终实际生效的市场、类目、GMV、关键词、粉丝画像和目标展示人数;未设置的维度明确写“未设置”,市场未显式指定时写“由当前授权店铺决定”。该区块必须位于任何结果统计和达人列表之前。
76
- 2. **结果摘要**:实际展示数、`total_count`、当前排序和是否还有下一页;不重复罗列条件。
77
- 3. **达人列表**:固定展示“排名 / 昵称 / 用户名 / 粉丝数 / 近 30 天 GMV 区间 / 类目匹配 / 达人 ID”。
78
- 4. **分页状态**:有更多结果且未达到 60 人时,使用“已显示 X / 共 Y;本轮最多展示 60 位达人,还可继续获取最多 Z 位”。没有下一页时使用“已显示全部结果”。达到 60 人且仍有更多结果时,固定说明“本次搜索已达到 60 位达人上限;请调整条件后重新搜索”,不再建议继续翻页。不要向用户展示 `page_token`。
79
- 5. **下一步**:只建议分析选中达人、在剩余预算内继续翻页或调整条件,不要擅自开始批量分析或建联。
67
+ 1. **结果摘要**:实际展示数、`total_count`、当前排序和是否还有下一页。
68
+ 2. **达人列表**:固定展示“排名 / 昵称 / 用户名 / 粉丝数 / 近 30 天 GMV 区间 / 类目匹配 / 达人 ID”。
69
+ 3. **分页状态**:有更多结果且未达到 60 人时,使用“已显示 X / 共 Y;本轮最多展示 60 位达人,还可继续获取最多 Z 位”。没有下一页时使用“已显示全部结果”。达到 60 人且仍有更多结果时,固定说明“本次搜索已达到 60 位达人上限;请调整条件后重新搜索”,不再建议继续翻页。不要向用户展示 `page_token`。
70
+ 4. **下一步**:建议分析选中达人、在剩余预算内继续翻页或调整条件;新增建议“选择 2 位及以上达人进行相对评分排名”。必须等待用户明确指定候选人后才调用 `creator_performance`,不要擅自开始批量分析或建联。
80
71
 
81
72
  格式化规则:
82
73
 
@@ -88,19 +79,19 @@ description: 在 TikTok Shop Creator Marketplace 中按关键词、类目、GMV
88
79
  - 必须为每位达人保留搜索结果返回的 `creator_open_id`。调用 `creator_performance` 时将该值放入兼容入参 `creator_user_id`;调用 `create_conversation` 时将同一个值放入 `creator_open_id`。不得截断、改写或从其他字段推断。
89
80
  - Markdown 的“达人 ID”列弱化展示 `creator_open_id`;HTML 将该 ID 放在可展开详情和复制区域,不占主表视觉焦点。
90
81
  - `creator_open_id` 缺失时显示“达人 ID 不可用”,并明确该达人暂不可分析或建联。
91
- - 结果为空时不生成空表。在“本次搜索条件”后固定输出“未找到符合条件的达人”,并建议放宽类目、GMV 或关键词。
82
+ - 结果为空时不生成空表。固定输出“未找到符合条件的达人”,并建议放宽类目、GMV 或关键词,不复述搜索条件。
92
83
  - 单页最多 20 条;同一搜索会话最多展示 60 位达人,不承诺获取全量结果。
93
84
 
94
85
  ### WorkBuddy HTML 可视化
95
86
 
96
- - 结果为 1–5 条时使用 Markdown 表格;达到 **6 条**时,若当前宿主提供 HTML/可视化产物能力,默认生成自包含的响应式 HTML 报告。用户明确要求 HTML 时不受条数限制。
97
- - 宿主不支持 HTML,或 HTML 生成失败时,必须回退为包含全部记录的 Markdown,不能只给摘要或丢失达人。
98
- - HTML 搜索报告必须先包含实际生效的搜索条件和目标展示人数,再包含返回数/总数/分页状态、可搜索和排序的达人表格、达人详情、达人 ID 复制区域和下一步建议;达到 60 人上限时必须展示上限提示而非继续翻页入口。
87
+ - 结果为 1–5 条时使用 Markdown 表格。在 **WorkBuddy** 中达到 **6 条**时,必须通过当前宿主的 HTML 产物通道创建自包含的响应式 HTML 报告;用户明确要求 HTML 时不受条数限制。非 WorkBuddy 宿主使用 Markdown。
88
+ - 在 WorkBuddy 中,不得仅因宿主未显式声明 HTML/可视化能力而回退 Markdown。仅当 HTML 产物实际创建失败时,必须回退为包含全部记录的 Markdown,不能只给摘要或丢失达人,并如实说明未生成报告。
89
+ - HTML 搜索报告不得包含任何搜索条件或目标展示人数;直接包含返回数/总数/分页状态、可搜索和排序的达人表格、达人详情、达人 ID 复制区域和下一步建议;下一步建议包含“选择 2 位及以上达人进行相对评分排名”。不添加无法调用 Agent 或 MCP 的伪按钮,也不自动开始分析;达到 60 人上限时必须展示上限提示而非继续翻页入口。
99
90
  - HTML 报告中的昵称链接使用 `<a>` 指向 `https://www.tiktok.com/@{username}`,并带 `target="_blank"` 与 `rel="noopener noreferrer"`;链接文本与 URL 都必须先安全转义/编码。
100
91
  - 只使用内联 CSS/JavaScript,不依赖 CDN、远程字体或外部图片;只实现真实可用的搜索、排序、展开和复制交互,不添加无法触发 Agent 或 MCP 操作的伪按钮。
101
92
  - 昵称、用户名及所有工具返回文本写入 HTML 前必须转义;禁止把工具内容作为 HTML 或脚本执行。
102
93
  - Markdown 表格中的昵称也应输出为可点击链接;若宿主对链接渲染有限,至少保留标准 Markdown 链接语法和可辨认的主页 URL。
103
- - 生成 HTML 后仍需在对话中按“搜索条件 → 结果数量/排序/分页”的顺序给出简短摘要。只有实际生成产物时才可声明存在 HTML,不得虚构文件名或 `outputs` 路径。
94
+ - 生成 HTML 后仍需在对话中按“结果数量/排序/分页”的顺序给出简短摘要,不复述搜索条件。只有实际生成产物时才可声明存在 HTML,不得虚构文件名或 `outputs` 路径。
104
95
 
105
96
  ### 类目过滤闭环校验(防静默失效)
106
97
 
@@ -128,12 +119,4 @@ description: 在 TikTok Shop Creator Marketplace 中按关键词、类目、GMV
128
119
 
129
120
  ## 错误处理
130
121
 
131
- | 问题类型 | 处理 |
132
- |----------|------|
133
- | 明确的 Node.js / npm / npx 缺失或 Node.js < 18 | 按 Agent 的“本地运行环境恢复”流程:诊断后等待用户确认,使用官方 Node.js LTS 路径恢复、复检并重启客户端;不走 `authorize` |
134
- | 其他 MCP 工具不可见、工具列表为空、连接关闭或本地 MCP 未启动 | 提示“ScoreHub 服务暂时未连接,请完全退出并重新打开客户端后重试;如仍无法使用,请联系 ScoreHub 支持。”不提及技术配置,不走 `authorize` |
135
- | `oauth_invalid` | 仅在真正未授权或本地 OAuth 失效时,才允许提示用户调用 `authorize` |
136
- | 当前店铺的 TikTok 授权失效或异常 | 用中文提醒用户:本地登录状态正常,但当前店铺需要先到 ScoreHub 重新绑定后再试;不要展示错误码,不要要求重复 `authorize` |
137
- | 日配额已用完 | 明确告知用户,停止 |
138
- | 请求过于频繁或持续限流 | 先提醒用户耐心等待,建议缩小搜索范围、减少翻页或稍后重试;必要时按 1s/2s/4s/8s/16s 有限退避重试。不要建议重新授权;只有用户明确要求时才走 `authorize` |
139
- | 参数无效 | 提示用户修正搜索条件或 page_size / 类目参数,不走授权路径 |
122
+ 搜索专属错误分支仅处理市场不支持、类目参数无效、搜索条件不合法和类目闭环校验失败;其余授权、限流、MCP 可用性与本地环境问题均遵循 Agent 权威规则。