Files
yixiong-claude-marketplace/.claude/memory/decisions.md
T

261 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: 架构决策
description: Marketplace 设计中的关键技术决策及其原因:无版本号/userConfig/Bearer Token/SKILL.md规范/路径锁定/memcore三项优化/memcore-shared共享层/lint_report保活/Base commit兜底/obsidian社区对标审查/memory-lint速度分档过期检测/Codex插件骨架与memcore-codex独立目录/Codex侧bearer_token_env_var/不做Antigravity兼容/zentao-mcp网桥双认证格式兼容/Hermes插件骨架与packs选装/Hermes安全扫描description限制/Hermes MCP session-id已知bug
type: project
last_updated: 2026-08-25
commit: 9e1dcf6
---
# 关键架构决策
## plugin.json 不设 version 字段
**结论**:所有 plugin.json 均不含 `version` 字段。
**Why**Claude Code 官方文档说明,若 plugin.json 设置了固定 `version`,推送新 commit 不改 version 字符串时,已安装用户看不到更新。省略 version 后,Claude Code 自动用 git commit SHA 做版本判断,每次推送 main 分支即为新版本。
**How to apply**:新增插件时不要加 `version` 字段。memcore 和 huanxi 均已按此规范执行。
---
## huanxi plugin 使用 userConfig 而非环境变量传 Token
**结论**`plugin.json``userConfig` + `sensitive: true` 声明 Bearer Token 输入,`mcpServers.headers` 中用 `${user_config.token}` 引用。
**Why**`sensitive: true` 将 token 存入系统钥匙链(或 `~/.claude/.credentials.json`),不会出现在 settings.json 中,避免随仓库提交泄露。Claude Code 安装插件时自动弹窗提示用户输入,体验好于环境变量。
**How to apply**:其他需要用户配置 API Key/Token 的插件,均应使用此模式,不要用 `${ENV_VAR}` 方式。此模式仅限 Claude Code 侧——Codex 没有等价钥匙链机制,见 [[decisions.md#Codex 侧 MCP Token 用 bearer_token_env_var,不用 ${VAR} 模板(2026-08-22]]。
```json
"userConfig": {
"token": {
"type": "string",
"title": "寰汐 Personal Token",
"description": "在寰汐系统后台生成,hxp_ 前缀",
"sensitive": true
}
}
```
**See Also**[[project_overview.md#已发布插件]]、[[decisions.md#zentao-mcp 网桥已打补丁,双认证格式兼容(2026-08-25]]
---
## huanxi MCP Server 认证:Bearer Token 直连,不改后端
**结论**plugin.json 配置 `Authorization: Bearer ${user_config.token}` headerMCP server 已有的 `_PersonalTokenVerifier` 直接验证 `hxp_` token,无需改后端。
**Why**:后端已有 ASGI 中间件模式(AdminMcpAuthMiddleware)和 PersonalTokenVerifierBearer Token 天然支持。OAuth2 Discovery Flow 是 FastMCP 框架层的特性,当客户端直接在 header 中传 Bearer Token 时可绕过 OAuth 流程。改后端风险高且无必要。
**How to apply**:未来新增 MCP server 插件时,只需在 plugin.json 配置 header,不需要修改后端认证逻辑。
---
## memcore memory-update/lint 路径锁定:禁止写入系统自动记忆路径
**结论**`memory-update``memory-lint` 的唯一操作路径锁定为 `$PROJECT_DIR/.claude/memory/`;严禁写入 `~/.claude/projects/*/memory/`;两个技能执行期间不触发 auto memory 系统写入。
**Why**Claude Code 的 auto memory 是 system-level 指令,在技能执行期间始终有效。若技能只在 description 中说"不推送远程"而无显式路径约束,AI 会在执行 memory-update/lint 时被 auto memory 指令并发触发,将内容写入系统级路径(`~/.claude/projects/xxx/memory/`),背离"项目本地 .claude/memory/ 是唯一权威"的初衷。
**How to apply**:两个技能均在正文最前加"⚠ 路径锁定"块(含禁止路径清单 + 执行前断言代码),memory-sync 的 Phase 3 加方向锁定注释(Phase 10 是唯一远程写入窗口)。未来新增操作本地记忆的技能也应遵循同等约束。
**See Also**[[project_overview.md#已发布插件]]
---
## memcoresynonyms.md 作为独立等价词表文件
**结论**:等价表述清单以独立的 `synonyms.md``type: reference`)存放于 `.claude/memory/`,而非内嵌到 feedback.md。
**Why**feedback.md 存放协作规范(含 Why + How to apply),语义上属于"决策"synonyms.md 是纯配置数据,在 lint Phase 4 矛盾检测前作为输入加载。两者职责不同,混放会让 lint 的加载逻辑复杂化。`type: reference` 符合"外部参考资料"语义,且该类型已在 frontmatter 枚举中。
**How to apply**:新建项目记忆体系时,若项目有领域专有术语缩写(如 PG/PostgreSQL、KT/Kotlin),在 `.claude/memory/synonyms.md` 中维护等价组(每行逗号分隔)。幽灵检测自动排除该文件,不纳入 MEMORY.md 索引。
**See Also**[[decisions.md#memcore memory-update/lint 路径锁定:禁止写入系统自动记忆路径]]
---
## memcoresynthesis 三触发分工——快扫 vs 精扫
**结论**:synthesis 升级触发拆为三路:A 会话内主动、B Phase 3C 即时快扫(每次 memory-update 必跑)、C lint Top 反向触发(月度运行)。
**Why**:原双触发中,B 路径依赖 lint 生成 `lint_report.md` 的「条目级高频引用 Top」段,短会话或任务型对话中 lint 常被跳过,导致高频引用条目长期未升级为 synthesis。Phase 3C 快扫在每次 memory-update 结束时强制执行,覆盖短会话盲区;lint 精扫按源文件去重计数,用于月度深度检查。两者以 `**Synthesized:**` 标记作为唯一判重依据,不重复创建文件。
**How to apply**:实现新的 memory-update 类技能时,引用计数类检查应分"即时快扫"和"月度精扫"两档,分别对应"覆盖率"和"准确率"的不同优先级。
---
## memcore memory-sync:冲突检测必须先于 git commit
**结论**memory-sync Phase 0 重构为两步:Step 1 冲突优先检测(`diff --diff-filter=U`)→ Step 2 普通变更提交。冲突语义合并完成后才允许 commit,再进入 Phase 1。
**Why**:原设计中 Phase 0 先执行 `git add + commit`,Phase 0.5 才检测冲突。若文件存在 git merge conflict markers`git commit` 会静默失败(git 拒绝提交含冲突标记的文件),整个 sync 流程进入不确定状态且无明显报错。改为"先检测再提交"消除了这条静默失败路径。
**How to apply**:设计任何含"检测 + 操作"两步的流程时,检测必须先于操作,且检测结果应作为操作的前置条件,而非事后处理。
---
## memcore-shared:路径锁定 + 全局常量的单一来源
**结论**:把路径锁定、全局常量(SYNTHESIS_THRESHOLD / LINT_STALE_*_DAYS / MULTI_HOST_WARN_DAYS)、PROJECT_DIR 跨平台解析提取到独立 skill `memcore-shared`,三个主技能(memory-sync / memory-update / memory-lint)开头 `Read ../memcore-shared/SKILL.md` 引用其约束。description 中显式说明"内部 include,不由用户直接调用"。
**Why**:原设计中 memory-update 和 memory-lint 各自维护一份 20 行的"路径锁定"块,完全重复;阈值常量 `≥3``≥30/90 天``≥7 天` 分散硬编码在多个文件多个位置,调整需多处改动。共享层独立成 skill 后:① 单点维护、② 阈值修改只动一处、③ 与 huanxi-shared 同模式,可演进性强。
**How to apply**:未来 memcore 类多 skill 插件如出现「共享约束 + 多处硬编码常量」时,提取为独立 `<plugin>-shared` skill;常量声明在共享 skill 顶部表格,子技能引用常量名而非裸数字。
**See Also**[[decisions.md#memcore memory-update/lint 路径锁定:禁止写入系统自动记忆路径]] [[feedback_plugin_dev.md#skill 不要重写 MCP 参数表,引用 docstring 为单一真相]]
---
## memcore lint_report 增量保活:稳定 ID + resolved 跳过
**结论**lint Phase 8 生成 NEED-HUMAN 条目时,末尾附 `<!-- id: 8位sha1 -->`(基于 phase + 文件 + 章节 + 关键事实计算)。Phase 8-pre 提取旧 `lint_report.md` 中带 `<!-- resolved -->` 标记的 ID 集合,新报告中同 ID 条目跳过。
**Why**:原 lint_report.md 每次覆盖写入,用户即使在 NEED-HUMAN 条目处理完或决定"不处理"后,下次 lint 仍会重新列出。导致信号疲劳,长期看反而忽视所有 lint 提示。引入稳定 ID + resolved 标记后,用户对每个条目的判断(处理/接受现状)能跨多次 lint 持续生效,lint_report 变成只列"真正待处理"的事项。
**How to apply**:任何"周期性扫描 + 报告生成"的 lint/check 系统,凡有用户主观判断维度(不只是机器判定)时,输出条目都应有稳定 ID + 用户标记跳过机制。ID 计算用「问题本体」字段(位置+事实),不要包含执行时间/扫描序号。
---
## memory-update Phase 1 锚点丢失兜底
**结论**memory-update Phase 1 读 `Base commit` 锚点时,若 MEMORY.md 头部该行缺失或为 N/A,从各 memory 文件 frontmatter 的 `commit:` 字段取最旧值兜底,避免退化为全量。
**Why**MEMORY.md 头部 `Base commit: HASH` 是单一锚点来源,一旦用户手动编辑误删此行,整个 git diff 范围退化为全量审查,触发 memory-update 对所有文件做"按变更维度重写"。即使大部分文件没真实变化,也会被刷一次 last_updated 和 commit 字段,造成虚假改动。兜底机制从各文件 frontmatter 取最旧 commit,确保覆盖所有真实差量而非无差别全量。
**How to apply**:任何"单点配置 → 关键路径"的设计,必须考虑配置丢失时的退路。优先级:单点 → 多点冗余 → 兜底推导。memcore 当前是「单点 + 兜底推导」,无需冗余存储。
**See Also**[[decisions.md#memcore memory-sync:冲突检测必须先于 git commit]]
---
## SKILL.md frontmatter 只保留 name 和 description
**结论**SKILL.md frontmatter 只写 `name``description` 两个字段,去掉 `version`
**Why**Claude Code 插件规范中 SKILL.md frontmatter 只定义了 `name``description``version` 字段不在规范内,silently ignored,且与 plugin.json 层面的版本管理重复。已从所有 huanxi skill 文件中移除。
**How to apply**:新建 SKILL.md 时只写这两个字段。`description` 字段写用户实际口语触发词(第三人称描述),不要用"当需要…时触发"的内部视角表达。
---
## obsidian skill 集对标社区基准的审查 + 优化(2026-06-12
**结论**:基于公开社区调研结果(kepano/obsidian-skills 31.8k★、AgriciDaniel/claude-obsidian 15 技能、qhuang20/obsidian-skills、pablo-mano/Obsidian-CLI-skill),对本地 9 技能完成全量审查并执行方案 B(中等扩展):
1. **P1 描述去冗余**8 个子技能 description 末尾"检测到 .obsidian/ 时与核心技能同步激活"全部删除——Claude Code 路由器按关键词独立打分,没有"伴随激活"机制,此声明纯占预算。
2. **P5 obsidian-plugins 缩范围**:从"控制面板"宽泛定位收敛到"环境层(插件/主题/CSS/Templates/快捷键/命令)",加"首次配置 vault 批量装常用插件"高频场景。
3. **P6 obsidian-workflow-pkm 加引导**description 显式声明"多步骤复合需求优先匹配本技能,单一原子操作走子技能",让"清理 inbox"等短指令更易命中编排层。
4. **P3 核心 obsidian 补 OFM 语法速查**:在第 4 章末尾增"Obsidian Flavored Markdown 语法速查"小节,覆盖 wikilinks/embeds/callouts/block refs/highlight/math 全表 + 写入时高频陷阱。
5. **P4 workflow-pkm 补 Workflow 8 Web Clip → Permanent**:单篇网页剪藏轻量流,引用 defuddleObsidian 团队官方 web→md 清洗工具)+ WebFetch 兜底,明确与 Workflow 7(批量文献)的边界。
6. **P2 新增 obsidian-canvas skill**JSON Canvas 1.0 schema 速查 + 16 hex ID 生成 + 直接 Read/Write JSON 路线(社区共识:obsidian-cli 不原生支持 .canvas 写入)+ 安全 SOP 4 选项对照表(A git stash / B .bak / C 原子写 / D File Recovery)。
**Why**
- 社区呈现两条路线:kepano「按文件格式分技能」(5 技能/精)、AgriciDaniel「按方法论分技能」(15 技能/全)。我们的「按工作域分技能」(9→10 技能)取中间路线,保留跨插件感知和职责互斥声明的独特优势。
- 8 处"伴随激活"提示是隐蔽设计错误——它假设了 Claude Code 路由器看不懂的联动语义,是单次审查中最大的描述质量收益点(每技能省 ~28 字符预算给真触发词)。
- canvas 是 Obsidian 开放格式且独立于 Markdown 体系,社区标杆都作为独立 skill 维护;不加 canvas 等于把"视觉知识图"这类高频场景拱手让人。
**How to apply**
- 新增 skill 时,description 末尾**不要写"检测到 X 时与 Y 同步激活"**——Claude Code 路由按关键词独立打分。
- 触发词列表用 `触发词:A、B、C` 显式列;`不用于:X(技能名)` 显式互斥;这种结构提升路由准确率。
- 涉及 vault 内格式(.canvas/.base/.md)的能力,**默认独立成 skill**——不要塞进核心 obsidian。
- 学习模式契机:obsidian-canvas 的"AI 大批量写入回滚策略"4 选项对照表留给团队/用户决策,AI 不强制做掉,默认采用 C+D 作为决策前兜底。
**See Also**[[feedback_plugin_dev.md#MCP 后端工具签名变更后必须全量扫描所有 skill]]、kepano/obsidian-skills、AgriciDaniel/claude-obsidian
---
## memory-lint 过期检测改用提交速度分档,弃用固定 30/90 天阈值(2026-07-10
**结论**`/memory-lint` Phase 5 过期检测从固定 `LINT_STALE_WARN_DAYS=30` / `LINT_STALE_ERROR_DAYS=90` 改为「自 last_updated 以来的全仓库提交速度」分档:`LINT_STALE_MIN_DAYS=7`(不足 7 天跳过检测)+ 速度 ≥`LINT_HIGH_VELOCITY`(1.0 次/天) → ERROR,≥`LINT_LOW_VELOCITY`(0.3 次/天) → WARN,低于此速度不判定过期,但 `LINT_STALE_ABSOLUTE_DAYS`(180 天) 绝对兜底。
**Why**:AI 辅助开发下代码迭代速度远超传统人工节奏,高频项目 7 天内可能已发生大量架构变更,30 天固定阈值严重滞后不报警;反过来低活跃期项目(如进入维护期)超过 30 天没提交,旧记忆大概率仍准确,固定天数会误报过期。纯日历天数无法区分"高频漂移"和"低频稳定"两种情况,需要用提交速度代理"内容漂移风险"。
**How to apply**:新增/调整任何"距离上次更新多久算过期"的判定逻辑时,优先考虑用活跃度信号(提交频次、变更行数等)分档,而非固定日历阈值。常量集中在 `memcore-shared` 全局常量表单点维护,调整数值只改一处。
**See Also**[[decisions.md#memcore-shared:路径锁定 + 全局常量的单一来源]]
---
## Codex/ChatGPT 桌面应用插件骨架——三个插件共享 skills/memcore 独立目录(2026-08-22
**结论**huanxi、huanxi-admin、obsidian 的 Codex 版本通过在同一插件目录下新增 `.codex-plugin/plugin.json`(与 `.claude-plugin/plugin.json` 并列)实现,共用同一份 `skills/``memcore` 因为 Codex 插件校验器要求 `skills` 字段必须精确指向 `./skills`(不能自定义子路径),且 Codex 版记忆体系架构(`AGENTS.md` 会话入口 / `.codex/memory` 目录约定 / 无远程同步)与 Claude 版本质不同,无法共用同一份 `skills/` 内容,故新建独立插件目录 `plugins/memcore-codex/`marketplace.json 里对外插件名仍叫 `memcore`(目录名与插件名不要求一致)。`memcore-codex` 以本机已装的 Codex 原生 memcore 技能为底稿,抽出 `memcore-shared` 共享 include,并吸纳了 Claude 版四项内容:过期检测速度分档、NEED-HUMAN 稳定 ID 保活、memory-update 锚点丢失兜底、更完整的 lint_report 模板。
**Why**:用本机已安装的 Codex `plugin-creator` 技能自带的 `validate_plugin.py` 实测确认——`skills` 字段规整化后必须精确等于 `"skills"``mcpServers` 字符串路径必须精确等于 `"./.mcp.json"`,且都必须在插件根目录(不能嵌套进 `.codex-plugin/`);顶层 `interface` 块(displayName/shortDescription/longDescription/developerName/category/capabilities/defaultPrompt)是必填项,`hooks` 字段不被接受,`disable-model-invocation` 只能是 `false` 或不写。旧的 `feat/codex-marketplace` 分支骨架因为缺 `interface` 块、`.mcp.json` 放错位置,实际过不了这个校验(已删除该分支)。Codex 侧技能级"内部 include 不给用户直接调用"靠 `agents/openai.yaml``policy.allow_implicit_invocation: false` + description 措辞实现,不能像 Claude 侧那样用 frontmatter 禁用模型调用。
**How to apply**:新增/修改 Codex 插件时,先跑本机 `~/.codex/skills/.system/plugin-creator/scripts/validate_plugin.py <plugin-path>` 校验再算完成;技能内容若和 Claude 版能共用就共用同一 `skills/`,若架构本质不同(如需要独立会话入口/目录约定)就整个插件目录独立,不要硬塞进同一 `skills/`
**See Also**[[project_overview.md#已发布插件]]、[[decisions.md#memcore-shared:路径锁定 + 全局常量的单一来源]]、[[decisions.md#memcore lint_report 增量保活:稳定 ID + resolved 跳过]]、[[decisions.md#memory-update Phase 1 锚点丢失兜底]]
---
## Codex 侧 MCP Token 用 bearer_token_env_var,不用 ${VAR} 模板(2026-08-22
**结论**CodexCLI + ChatGPT 桌面应用)没有等价于 Claude `userConfig` 钥匙链的插件级敏感配置机制。`.mcp.json` 里 HTTP 类型 MCP server 的 Bearer Token 必须用专用字段 `bearer_token_env_var: "ENV_VAR_NAME"`(只放变量名,不放值,由 Codex 进程启动时读取该环境变量),而不是在 `headers` 里写 `"Authorization": "Bearer ${VAR}"` 模板插值——后者不被 Codex 支持,会把 `${VAR}` 字面量原样发出去导致鉴权失败。
**Why**:查证 Codex 官方 MCP 配置文档(`config.toml` / `codex mcp add` 场景)确认专用字段是 `bearer_token_env_var` / `env_http_headers`;且有未解决的官方 issue[openai/codex#24401](https://github.com/openai/codex/issues/24401))明确指出插件打包的 MCP server 目前没有官方定义的用户密钥配置路径,环境变量注入(父进程启动前已设置)是当前唯一现实可用方式。实测踩坑:改完 `.mcp.json` 后本机 `codex plugin add` 缓存的旧版本(`~/.codex/plugins/cache/<marketplace>/<plugin>/<version>/`)不会自动更新,必须 `codex plugin marketplace upgrade <marketplace>` 刷新快照后重新 `codex plugin add` 才会生效。
**How to apply**:新增/修改任何 Codex 插件的 HTTP MCP server 配置,一律用 `bearer_token_env_var` 字段;环境变量的设置方式区分场景——CLI 用 shell `export`(写进启动脚本),ChatGPT 桌面应用(图形界面启动,不继承 shell)用 macOS `launchctl setenv` 或 Windows 系统环境变量。改完插件内容 push 后,本机测试前要先 `codex plugin marketplace upgrade <marketplace>` + 重新 `codex plugin add <plugin>@<marketplace>`,否则读到的还是装的时候那份缓存。
**See Also**[[decisions.md#huanxi plugin 使用 userConfig 而非环境变量传 Token]](对照:Claude 侧用 userConfig 钥匙链,Codex 侧被迫用环境变量,是两个平台能力差异,不是我们设计不一致)、[[decisions.md#zentao-mcp 网桥已打补丁,双认证格式兼容(2026-08-25]]
---
## 不做 AntigravityGoogle agy / Antigravity 2.0)兼容(2026-08-22
**结论**:调研后决定暂不为 Google Antigravity CLIagy)和 Antigravity 2.0 桌面应用建插件市场骨架。
**Why**:官方文档(antigravity.google/docs/cli/features/)确认 agy 支持插件(skills/agents/rules/MCP/hooks 打包),但**没有** marketplace 概念——无 `marketplace.json`、无"注册市场源"命令,只有 `agy plugin install <本地路径或 git URL>` 直接安装;网上搜到的"agy 支持 marketplace.json"等说法查证后均来自第三方社区工具(如 `agy-plugins-cli`),非 Google 官方能力。Antigravity 2.0 桌面应用官方文档完全没提插件/市场机制。该产品线是 Google I/O 2026 才发布,文档还在变动(schema 页面实测 404)。
**How to apply**Claude Code 和 Codex 是当前团队实际使用的主流工具,继续投入维护;未来遇到新 AI 编程工具想接入本 marketplace 时,先确认该工具官方是否有稳定的 marketplace/plugin 协议(有市场索引格式 + 远程仓库注册命令),协议不成熟就先不投入,避免跟着一个还在剧烈变动的规范返工。
**See Also**[[project_overview.md#已发布插件]]
---
## zentao-mcp 网桥已打补丁,双认证格式兼容(2026-08-25)
**结论**zentao-mcp 网桥(部署在 `pm.ops.yixiong-tech.com/mcp`,基于 merzzzl/openapi-mcp-server 二次开发)已确认同时兼容两种认证格式:Claude 侧 `.claude-plugin/plugin.json` 用的自定义 `headers: {"token": "..."}`,以及 Codex 侧 `.mcp.json``bearer_token_env_var` 机制底层发出的标准 `Authorization: Bearer <token>`
**Why**:新增 zentao 插件时曾担心两侧 header 格式不一致会导致 Codex 链路认证失败——Claude 侧网桥原生认的是自定义 `token` header,而 Codex 的 `bearer_token_env_var` 只会发标准 Bearer 格式,两者字面不同。用户确认网桥已就此打过补丁,双格式都认,不存在兼容问题。
**How to apply**zentao 插件的 Claude/Codex 双端 Token 配置模式确认与 huanxi 一致([[decisions.md#huanxi plugin 使用 userConfig 而非环境变量传 Token]] + [[decisions.md#Codex 侧 MCP Token 用 bearer_token_env_var,不用 ${VAR} 模板(2026-08-22]]),无需为 zentao 网桥单独定制认证适配。未来若网桥做重大改版,需重新确认这条双格式兼容性是否还成立。
**See Also**[[project_overview.md#已发布插件]]
---
## Hermes 插件骨架:Agent Plugins v1 + packs 选装(2026-08-25
**结论**huanxi/huanxi-admin/obsidian/zentao 在插件根目录新增裸 `plugin.json`[Agent Plugins v1.0.0](https://agent-plugins.org/) 标准,字段仅 `$schema`+`name`+可选元数据),复用同一份 `skills/``memcore` 因架构差异走独立目录 `plugins/memcore-hermes/`(记忆目录优先级 `.claude/memory``.codex/memory` → 新建 `.agents/memory`,且显式提醒 Hermes 自己按 profile 隔离的全局 `~/.hermes/memories/` 不是项目记忆后端)。分发不走 `hermes plugins install owner/repo`(该命令按文档examples只支持整仓库=一个插件包),改用 `packs/<name>.yaml` + `hermes plugins pack install`pack manifest 的 `subdir` 字段官方支持 monorepo 定位)。
**Why**:调研过程中依次排除了三条路径——① `hermes plugins install owner/repo` 无 subdir 支持(用户质疑"仓库多插件不合理"后深挖才找到 pack 机制,此前调研不够);② 自建 `plugins.index_url` 覆盖官方社区索引会让用户暂时搜不到 NousResearch 官方索引里的插件,属单值配置非叠加,用户认为代价太大;③ 最终定为每插件一个 `packs/<name>.yaml`,互不影响、无副作用。凭证类插件不打包 `mcp.json` 是因为 Agent Plugins v1 规范明文禁止内嵌密钥(headers/env 都不行),Hermes 原生 `~/.hermes/config.yaml` 支持 `${VAR}` 插值,改为 README 手动配置指引,用户体验类比 Codex 桌面应用的 `launchctl setenv` 变通方案。
**How to apply**Hermes 的 `ref` 字段要求精确 40 位 commit SHA、不接受分支名,`packs/*.yaml` 需要在每次相关内容发布后手动 bump(不像 Claude Code 的 git SHA 自动追新)。两处此前未经验证的点已由用户用真实 Hermes 实测确认:① `pack install` 支持直接传 http(s) raw 链接,不用先 clone;② `subdir` 定位的目录只有 `plugin.json`(没有原生 `plugin.yaml`)能被正确安装(前提是通过安全扫描,见 [[decisions.md#Hermes 插件安装安全扫描:plugin.json description 不能含配置路径字面量(2026-08-25)]])。
**See Also**[[project_overview.md#已发布插件]]、[[decisions.md#Hermes 插件安装安全扫描:plugin.json description 不能含配置路径字面量(2026-08-25]]、[[decisions.md#Hermes MCP Streamable HTTP session-id 已知 bug——凭证类插件连接受阻(2026-08-25]]
---
## Hermes 插件安装安全扫描:plugin.json description 不能含配置路径字面量(2026-08-25)
**结论**huanxi/huanxi-admin/zentao 的 `plugin.json` `description` 字段精简为一句话简介,删除了原先写的 `~/.hermes/config.yaml` 等操作指引文字;具体配置步骤只保留在 README 里。
**Why**:用户实测 `hermes plugins pack install` 被安全扫描 BLOCKED——Hermes 官方文档确认 community source(非 Nous 官方审核)插件的扫描是零容忍策略,任意 1 个 finding 就拒绝安装且 `--force` 无法覆盖;报错精确指向 `plugin.json:5`description 字段),命中的是 `CRITICAL persistence` 类别。description 里字面写的 `~/.hermes/config.yaml`(点前缀配置文件路径字符串)大概率撞上了扫描器针对"持久化/自我修改配置"模式的启发式规则——这类字符串常见于恶意插件描述自己如何篡改用户配置实现驻留,扫描器无法区分"教用户怎么手动配置"和"指导 AI 怎么植入后门"两种语义。
**How to apply**:任何 Hermes 插件(尤其面向 community source 分发的)的 `plugin.json` description 只写功能简介,不要出现具体文件路径(尤其 `~/.` 开头的配置/凭证类路径)、shell 命令片段或操作步骤——这类内容一律放 README,不进 plugin.json。遇到 BLOCKED 报错时先看 `Verdict`/`findings` 指向的具体文件和行号,大概率能定位到触发字符串。
**See Also**[[decisions.md#Hermes 插件骨架:Agent Plugins v1 + packs 选装(2026-08-25]]
---
## Hermes MCP Streamable HTTP session-id 已知 bug——凭证类插件连接受阻(2026-08-25)
**结论**huanxi/huanxi-admin/zentao 在 Hermes 上配置 HTTP 类型 MCP Server 后,`initialize` 握手能成功,但后续请求会报 400 并最终 park 连接。用 curl 直连 zentao-mcp 网桥验证过网桥本身没问题(认证正常、`initialize` 返回 200 且带 `mcp-session-id`),判断是 Hermes 客户端未正确捕获/回传 Streamable HTTP 协议要求的 `mcp-session-id`,与 [NousResearch/hermes-agent#20349](https://github.com/NousResearch/hermes-agent/issues/20349) 描述的现象一致。用户决定暂不深究 workaround,等 Hermes 上游修复。
**Why**MCP Streamable HTTP 传输协议是有状态会话——服务器在 `initialize` 响应头里下发 `mcp-session-id`,客户端后续请求必须原样带回 `Mcp-Session-Id` 请求头,服务器才认下一步请求;curl 手工构造带完整 header 的请求能跑通全流程,排除了网桥端的问题。`protocol: legacy`/`skip_preflight: true` 都试过无效,因为问题出在握手**之后**的会话保持,不是握手协商本身。
**How to apply**:这不是我们插件配置能修的问题,不要在 plugin.json/pack/README 里继续折腾 MCP 相关参数试图绕过。定期检查 [#20349](https://github.com/NousResearch/hermes-agent/issues/20349) 状态,Hermes 发布修复版本后回来验证并更新 README 里的已知问题说明;`obsidian`/`memcore-hermes` 不含 MCP,不受影响,可以正常使用。
**See Also**[[decisions.md#Hermes 插件骨架:Agent Plugins v1 + packs 选装(2026-08-25]]