feat(huanxi): 拆为个人端 / 管理端两个插件,技能全面对齐寰汐 v2
寰汐 v2 上线双 MCP(个人端 /mcp/ 与管理端 /admin-mcp/),两条端点是**两条独立的 信任边界**——Token 前缀不同、工具集不同、视角不同(管理端看全量、个人端只看我参与的)。 一个插件塞两套会让普通员工的客户端里出现他根本调不动的管理工具,因此拆成两个插件, 按角色各装各的。 ## huanxi(个人端,全体员工) 7 个技能:shared / report / leader / task / issue / meeting / org - 新增 `issue` `meeting`——v2 的议题域与会议域(M6v2 会议×议题解耦后的产物) - **删除 `weekly`**——v2 没有 weekly_report 表,周报已并入报告体系 - 各技能重写为**流水线定义**而非使用说明:写明工具调用顺序、ID 在步骤间怎么传、 人工确认节点落在哪。「禁止自动提交」这条 v1 已验证的硬约束保留 - 删掉 `references/` 拆分文件——v2 技能自包含 ## huanxi-admin(管理端,仅后台管理员) 4 个技能:admin-shared / admin-report / admin-module / admin-ops MCP server key 取 `huanxi-admin`(与个人端的 `huanxi` 不同名),否则两插件并存时 会键冲突。 ## 缓存目录按信任边界隔离 `~/.claude/huanxi-cache/` 下分 `personal/` `admin/` `dict/`:前两者视角不同, 混用会越权展示或数据错乱;`dict/` 与身份无关可共享。业务数据(任务/日报/会议/议题) **显式声明不缓存**——v1 没写这条,Agent 会自行决定缓存然后拿到陈旧数据。 ## 源码单一真相不在本仓库 技能源码在寰汐仓库 `skills/`,与 MCP docstring 同仓库同 commit——签名一改, 技能与工具在同一次改动里更新,从结构上消除跨仓库漂移(本仓库记忆 `feedback_plugin_dev.md` 记录的 4 类漂移覆盖全部 6 个 v1 技能,正是这个病)。 本仓库退化为**分发壳**,只接收 `python skills/sync_marketplace.py` 的产物,不手工编辑。 寰汐侧有 CI 守卫:技能里出现的每个工具名必须存在于实际注册表、个人端技能不得 指导调用管理端独有工具、不得硬编码状态字面量。 ## 本分支不合 main 插件配置的域名此刻跑的还是 v1,合进 main 会通过自动更新推给已安装用户, 他们的技能会去调 v1 上不存在的工具。合并前置条件写在 CLAUDE.md「已发布插件」节。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Vyeia9k43dVaUFNLo8Lny
This commit is contained in:
co-authored by
Claude Opus 5
parent
d7aaaf8224
commit
4ce9a92013
@@ -1,142 +1,125 @@
|
||||
---
|
||||
name: huanxi-shared
|
||||
description: "寰汐 MCP 共享基础:本地缓存策略(me/modules/users/workdays)、TTL 规则、缓存读写伪代码、MCP 工具索引。所有 huanxi-* 技能必须先 Read 本文件,再执行各自工作流。"
|
||||
description: "寰汐 MCP 共享基础:工具命名约定、本地缓存策略与过期检查、状态两层模型、全局确认约定。所有 huanxi-* 技能必须先读本文件。"
|
||||
---
|
||||
|
||||
# 寰汐 MCP 共享规则
|
||||
|
||||
本技能是所有 `huanxi-*` 技能的**必读前置**,定义缓存策略、工具索引和全局约定。
|
||||
所有 `huanxi-*` 技能的**必读前置**。
|
||||
|
||||
---
|
||||
|
||||
## 必读声明
|
||||
## 一条最重要的约定:参数以工具自身的说明为准
|
||||
|
||||
**所有 huanxi-* 技能开头都必须先 `Read` 本文件(`../huanxi-shared/SKILL.md`),再执行各自工作流。**
|
||||
**本文件与各技能文档都不重画参数表。** 每个工具的参数名、必填项、取值范围以它在 MCP
|
||||
里注册的 docstring 为唯一真相;技能只描述**调用顺序、ID 如何传递、哪里必须停下来等用户
|
||||
确认**。
|
||||
|
||||
> 这条不是洁癖。上一代技能包重画过参数表,结果字段名、枚举值、必填项四类漂移覆盖了
|
||||
> 全部六个技能——用户侧表现为频繁的「参数缺失」报错。工具签名变了而文档没跟上,
|
||||
> 是必然发生而非可能发生的事。
|
||||
|
||||
---
|
||||
|
||||
## 本地缓存机制
|
||||
## 工具命名
|
||||
|
||||
缓存文件统一存放在 `~/.claude/huanxi-cache/`。
|
||||
Claude Code / Claude Desktop 里工具名带前缀:`mcp__huanxi__task_query`(个人端)、
|
||||
`mcp__huanxi-admin__report_pending`(管理端)。其他平台通常是裸名 `task_query`。
|
||||
本文档统一写**裸名**,实际调用时按你所在平台的约定加前缀。
|
||||
|
||||
### 缓存文件清单
|
||||
两个端点信任边界不同:
|
||||
|
||||
| 文件 | 内容 | TTL | 刷新方式 |
|
||||
|------|------|-----|---------|
|
||||
| `me.json` | 当前用户身份(id, name, feishu_user_id;注意字段名是 `id` 而不是 `user_id`) | 永久 | 手动删除文件 |
|
||||
| `modules.json` | 我参与的模块列表(id, name, my_role) | 24h | 过期自动重拉 或 `/huanxi-org +sync` |
|
||||
| `users.json` | 组织用户搜索结果(name/feishu_user_id 索引) | 24h | 过期自动重拉 或 `/huanxi-org +sync` |
|
||||
| `workdays.json` | 工作日查询结果(date → bool 的 KV 字典) | 永久(按日期 key) | 已有日期不重新查 |
|
||||
| | 个人端 | 管理端 |
|
||||
|---|---|---|
|
||||
| Token | `hxp_` 开头 | `hxa_` 开头 |
|
||||
| 身份 | 你本人,权限与网页端一致 | Token 创建人的管理员身份 |
|
||||
| 视角 | 我参与的 | 全量,不受角色过滤 |
|
||||
|
||||
### 缓存读写伪代码
|
||||
---
|
||||
|
||||
**读缓存(每次使用 MCP 数据前执行此逻辑):**
|
||||
## 状态是可配置的两层模型(v2 起)
|
||||
|
||||
**不要硬编码 `"in_progress"`、`"done"` 这类字面量。** 状态由后台配置,分两层:
|
||||
|
||||
- `category`:四类固定语义 `not_started` / `in_progress` / `completed` / `cancelled`,
|
||||
用于**判断**(这条算不算完成)
|
||||
- `status_option_id`:具体状态项的 UUID,用于**写入**
|
||||
|
||||
改任何实体状态前,先 `dict_get` 取该 `entity_type`(module/task/issue/meeting,**必须选对**)
|
||||
下的选项,再传对应的 `status_option_id`。传旧值或错的 entity_type 会被直接拒绝。
|
||||
|
||||
---
|
||||
|
||||
## 本地缓存
|
||||
|
||||
缓存根目录 `~/.claude/huanxi-cache/`,**按通道隔离**:
|
||||
|
||||
```
|
||||
function read_cache(file, ttl_hours):
|
||||
1. Read ~/.claude/huanxi-cache/{file}.json
|
||||
2. 若文件不存在 → cache_miss
|
||||
3. 读取 cached_at 字段,计算 age = now - cached_at(小时)
|
||||
4. 若 ttl_hours = Infinity 或 age < ttl_hours → 返回 data 字段(cache_hit)
|
||||
5. 否则 → cache_miss
|
||||
~/.claude/huanxi-cache/
|
||||
├── personal/ hxp_ 视角:me.json / my-modules.json / users.json
|
||||
├── admin/ hxa_ 视角:org-tree.json / all-modules.json
|
||||
└── dict/ 与身份无关的配置字典(两个通道共享)
|
||||
```
|
||||
|
||||
function cache_miss_handler(tool_name, params):
|
||||
1. 调用 MCP 工具:mcp__huanxi__{tool_name}(params)
|
||||
2. Write ~/.claude/huanxi-cache/{file}.json:
|
||||
{ "cached_at": "<当前 ISO8601 时间>", "data": <MCP 返回值> }
|
||||
隔离是必须的:管理端看到的是全量模块,个人端只有我参与的——混用会让你把不该展示的
|
||||
东西展示给用户。
|
||||
|
||||
### 分层 TTL
|
||||
|
||||
| 类别 | 文件 | 策略 |
|
||||
|---|---|---|
|
||||
| 身份 | `personal/me.json` | 永久(身份不变) |
|
||||
| **配置字典** | `dict/*.json` | **版本戳比对** + 24h 兜底 |
|
||||
| 组织 | `users.json` / `org-tree.json` / `my-modules.json` | 24h |
|
||||
| 日历 | `dict/workdays.json` | 按日期 key 永久(查过的不再查) |
|
||||
| **业务数据** | 任务 / 日报 / 会议 / 议题 / 公告 | **一律不缓存** |
|
||||
|
||||
最后一行是硬规则。业务数据随时在变,缓存它只会让你把过期状态当成现状汇报给用户。
|
||||
|
||||
### 字典为什么要版本戳而不是只靠 TTL
|
||||
|
||||
其余缓存过期了最多是显示旧数据;字典不一样——状态选项可能被后台停用,你拿 24 小时前
|
||||
的 ID 去改状态会**直接报错**。失败方向从「看到旧数据」变成「操作失败」,值得比对一次。
|
||||
|
||||
```
|
||||
用字典前:
|
||||
1. 调 dict_version() ← 极轻
|
||||
2. 与 dict/versions.json 比对
|
||||
3. 一致 → 用缓存;不一致 → 调 dict_get() 重取该类并更新缓存
|
||||
```
|
||||
|
||||
`dict_get` 的返回里**自带 versions 字段**,直接连内容一起存下来即可——不要分两次调用,
|
||||
那中间字典若被改动,你会把新内容配上旧版本戳缓存起来,之后再也不会刷新。
|
||||
|
||||
### 读缓存伪代码
|
||||
|
||||
```
|
||||
read(file, ttl):
|
||||
1. 读 ~/.claude/huanxi-cache/{file}
|
||||
2. 文件不存在 → miss
|
||||
3. age = now - cached_at
|
||||
4. ttl 为永久 或 age < ttl → 返回 data
|
||||
5. 否则 → miss
|
||||
|
||||
on_miss(tool, params):
|
||||
1. 调用工具
|
||||
2. 写入 { "cached_at": <ISO8601>, "data": <返回值> }
|
||||
3. 返回 data
|
||||
```
|
||||
|
||||
**workdays.json 特殊逻辑(按 key 缓存):**
|
||||
|
||||
```
|
||||
function get_workday(date):
|
||||
1. Read workdays.json → 得到 { "2026-04-14": true, ... }
|
||||
2. 若 date 已在 dict 中 → 直接返回
|
||||
3. 否则 → 调用 mcp__huanxi__system_get_workday(date)
|
||||
4. 将 {date: result} 追加写入 workdays.json
|
||||
```
|
||||
|
||||
### TTL 快速参考
|
||||
|
||||
```
|
||||
me.json → Infinity(永久,身份不变)
|
||||
modules.json → 24h
|
||||
users.json → 24h
|
||||
workdays.json → Infinity(按 date key,已查过的不再查)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## MCP 工具索引
|
||||
|
||||
寰汐 MCP 工具前缀:`mcp__huanxi__`
|
||||
|
||||
### 用户与认证
|
||||
| 工具 | 用途 | 缓存 |
|
||||
|------|------|------|
|
||||
| `user_get_me()` | 获取当前 Token 代表的用户 | → `me.json`(永久)|
|
||||
|
||||
### 模块与组织
|
||||
| 工具 | 用途 | 缓存 |
|
||||
|------|------|------|
|
||||
| `module_list(my_role?)` | 列出我参与的模块 | → `modules.json`(24h)|
|
||||
| `module_get(module_id)` | 获取模块详情(含成员) | 不缓存 |
|
||||
| `user_list(name?, department_id?, is_active?, limit?)` | 搜索组织用户 | → `users.json`(24h)|
|
||||
| `org_get_tree()` | 获取组织架构树 | 不缓存 |
|
||||
| `people_get_board(user_id?, module_id?, date?)` | 人员任务看板(不传则全员;传 user_id 只看该人) | 不缓存 |
|
||||
|
||||
### 任务管理
|
||||
| 工具 | 用途 | 缓存 |
|
||||
|------|------|------|
|
||||
| `task_list_mine(status?, module_id?, priority?, snapshot_active?)` | 我认领的任务 | 不缓存 |
|
||||
| `task_list_by_module(module_id)` | 模块下所有任务 | 不缓存 |
|
||||
| `task_get(task_id)` | 任务详情 | 不缓存 |
|
||||
| `task_create(...)` | 创建任务 | — |
|
||||
| `task_update(task_id, ...)` | 更新任务 | — |
|
||||
| `task_set_assignees(task_id, assignee_ids)` | 设置执行人(幂等) | — |
|
||||
|
||||
### 员工日报
|
||||
| 工具 | 用途 |
|
||||
|------|------|
|
||||
| `report_get_today(date?)` | 获取指定日期日报 |
|
||||
| `report_get_tasks_to_report()` | 获取今日待汇报任务 |
|
||||
| `report_save_draft(items=[...])` | 批量保存草稿(upsert)|
|
||||
| `report_submit_item(item_id, date?)` | 提交单条日报条目 |
|
||||
| `report_withdraw_item(item_id, date?)` | 撤回单条已提交日报条目 |
|
||||
|
||||
### 负责人日报
|
||||
| 工具 | 用途 |
|
||||
|------|------|
|
||||
| `leader_report_get_subordinates(date, module_id)` | 查看下属汇报情况 |
|
||||
| `leader_report_save(report_date, content, module_id?)` | 保存负责人日报草稿 |
|
||||
| `leader_report_submit(report_date, module_id)` | 提交负责人日报 |
|
||||
| `leader_report_withdraw(report_date, module_id)` | 撤回负责人日报 |
|
||||
| `leader_report_get_batch(date, module_ids)` | 批量获取多模块日报 |
|
||||
|
||||
### 周报
|
||||
| 工具 | 用途 |
|
||||
|------|------|
|
||||
| `weekly_report_get(year, week)` | 获取指定 ISO 周的周报 |
|
||||
| `weekly_report_get_batch(year, week, module_ids)` | 批量获取多模块周报 |
|
||||
| `weekly_report_save(module_id, year, week_number, content?, next_week_plan?)` | 保存周报草稿 |
|
||||
| `weekly_report_submit(module_id, year?, week_number?)` | 提交周报(不传 year/week_number 默认提交当周) |
|
||||
| `weekly_report_withdraw(module_id, year?, week_number?)` | 撤回周报 |
|
||||
|
||||
### 系统与 LLM
|
||||
| 工具 | 用途 | 缓存 |
|
||||
|------|------|------|
|
||||
| `system_get_workday(date?)` | 查询是否工作日 | → `workdays.json`(永久)|
|
||||
| `llm_polish_report(content)` | AI 润色日报内容 | — |
|
||||
| `llm_generate_leader_summary(module_id, date)` | AI 生成负责人日报草稿 | — |
|
||||
`workdays.json` 特殊:按日期 key 存 `{ "2026-08-06": true }`,已查过的日期不再查。
|
||||
|
||||
---
|
||||
|
||||
## 全局约定
|
||||
|
||||
1. **提交前必须确认**:`report_submit_item`、`leader_report_submit`、`weekly_report_submit` 执行前必须向用户展示内容并等待明确确认,禁止自动提交。
|
||||
2. **非工作日不强制**:检测到非工作日时,告知用户并询问是否仍要填写,不得直接中断流程。
|
||||
3. **缓存优先**:执行任何需要 module_id / user_id 的操作前,先读缓存;缓存未命中或过期才调 MCP。
|
||||
4. **ISO 周数**:周报的 `year` 字段存 ISO year(`date.isocalendar()[0]`),不是日历年。12 月底 / 1 月初注意跨年。
|
||||
5. **task_set_assignees 幂等**:设置执行人使用此工具(幂等),不要用 task_update 的 assignee 字段追加。
|
||||
6. **工具调用以 MCP 定义为准**:调用任何 `mcp__huanxi__*` 工具前,**必须以该工具在 MCP Server 中实际注册的参数名和类型为准**,本文档及各 skill 中的调用示例仅供工作流引导,不得作为参数的唯一依据。如果示例与工具实际定义有出入,以工具定义优先。
|
||||
1. **提交类操作必须先确认**:`report_submit` / `leader_report_submit` / `issue_close` 等,
|
||||
执行前把最终内容展示给用户、等到明确确认(「确认」「提交」「好的」)再调。
|
||||
不要因为用户说了「帮我写日报」就把提交也一并做了——写和交是两个决定。
|
||||
2. **删除与指派同样需要确认**:`task_set_assignees` 是**替换**语义(传空即清空),
|
||||
不是追加;覆盖别人的名单前先说清楚会变成什么样。
|
||||
3. **非工作日不强行中断**:`workday_check` 显示非工作日时告知用户并询问是否仍要填写,
|
||||
不要直接拒绝——补填、调休上班都是真实场景。
|
||||
4. **先解析 ID 再操作**:需要 module_id / user_id 的操作,先查缓存,未命中再调
|
||||
`module_query` / `user_search`。不要凭名字猜 ID。
|
||||
5. **批量优先**:工具的过滤维度基本都收列表,一次查多个比循环调用快得多,也更省上下文。
|
||||
|
||||
Reference in New Issue
Block a user