配置管理
CLAUDE.md、settings.json、model pinning、prompt versioning、plugin dependencies
blueprint 在这条 skill 里点了五样东西的名字,却一个行为定义都没给。它们表面上是五个不相干的文件,实际只回答两个问题:同一个设置写在好几处时,谁说了算;以及哪些东西必须钉死版本,钉死之后还剩多少不确定性。本章按这两个问题组织,核心交付是三张真伪速查栏——考场上区分「真键名」与「听起来很合理的键名」,靠的就是这种肌肉记忆。
vb: 出处(登记表见 reference/verified-behaviors.md,含官方原句与查证日期)。三张速查栏里标为「真实」的每一行都有登记支撑;标为「不存在」的每一行都是本书按 ch01 的 D1 工艺自制的干扰项,没有官方出处,捏造只发生在「名字」这一层,不涉及会误导你的机制行为。一、四个 settings 文件与那条优先级栈
Claude Code 从四个 settings 文件读配置,组织还可以从 claude.ai console 下发一份 managed settings。每个来源有自己的 scope——一个键写在哪个文件里,就决定了它作用于谁:只有你、这个项目的所有人,还是整个组织的所有人。
| Scope | 文件 | 影响谁 |
|---|---|---|
| User | ~/.claude/settings.json | 这台机器上你的每一个项目 |
| Shared project | .claude/settings.json | 在该目录里启动 Claude Code 的所有人;提交进仓库即可分发给同事 |
| Project local | .claude/settings.local.json | 只有你,只在这一个项目里 |
| Managed | managed-settings.json 与其他 managed 来源 | 组织下发到的所有人 |
同一个键出现在多处时,取「设置了它的最高层」的值。由高到低是五级:managed settings → command line(claude --settings)→ project local → shared project → user。注意 command line 排在第二而不是第一:--settings 传进去的键压得过本地文件,压不过 managed。
两条最容易在集成里踩的补充规则。第一,list 型的键是合并而不是择一。官方对 permissions.allow 这类键的说明是:同一个 list 键出现在多个文件里时,Claude Code 把这些 list 合起来,于是每个文件都能往里加条目而不会删掉别的文件加的。所以「我在 local 里加了一条 allow,团队那条怎么还在」不是 bug,是设计。
第二,环境变量根本不在这条栈里。官方把它讲得很清楚:环境变量不是这个栈里的一级,某个 shell 变量和某个 settings 键谁生效是逐对决定的,不按层级。具体到最常撞的一对——ANTHROPIC_MODEL 压过任何文件里的 model 键,而 ANTHROPIC_DEFAULT_MODEL 只在没有任何文件设置 model 时才生效。团队里「我明明在项目配置里写了模型,起来还是别的」,十次里有九次是某个人的 shell profile 里躺着一行 export。
速查栏 1 · settings 文件与键名
~/.claude/settings.json ✔ 真实 · user scope
.claude/settings.json ✔ 真实 · shared project,提交进仓库
.claude/settings.local.json ✔ 真实 · project local,只在你这台机器
managed-settings.json ✔ 真实 · managed,组织下发
.claude/settings.priority.json ✘ 不存在 · 自制干扰项
.claude/settings.override.json ✘ 不存在 · 自制干扰项
settings.json · "model" ✔ 真实 · 改会话起始模型
settings.json · "env" ✔ 真实 · 每个会话及其子进程的环境变量
settings.json · "permissions" ✔ 真实 · allow / ask / deny 与起始 permission mode
settings.json · "hooks" ✔ 真实 · 在生命周期点跑自定义命令
settings.json · "enabledPlugins" ✔ 真实 · 按 scope 开关插件
settings.json · "extraKnownMarketplaces" ✔ 真实 · 为仓库或组织登记 marketplace
settings.json · "claudeMdExcludes" ✔ 真实 · 按路径或 glob 跳过指定 CLAUDE.md
settings.json · "$schema" ✔ 真实 · 指向已发布的 JSON schema,给编辑器补全与校验
settings.json · "modelPin" ✘ 不存在 · 自制干扰项
settings.json · "promptVersion" ✘ 不存在 · 自制干扰项
settings.json · "settingsPriority" ✘ 不存在 · 自制干扰项
还有一条工程上极常见、考场上也很好用的细节:settings 文件是严格 JSON。一个 // 注释或一个尾随逗号就是语法错误,Claude Code 会在下次启动时把该文件报成 Settings Error。所以「我加了配置但完全没生效,连报错都没有」,第一件事不是查优先级,是查这个文件还是不是合法 JSON。$schema 那一行的价值也在这里:它指向已发布的 JSON schema,编辑器能给补全与内联校验;但官方同时提醒 schema 可能落后于最新 CLI 版本,所以对一个刚进文档的新键报告警,不代表你的配置无效。
permissions、hooks 与 apiKeyHelper 这类凭据 helper 都在内。出题时这条常被反用:题干说「改了配置、重启过、还是老样子」,那问题就不在「有没有生效」,而在「哪一层压着它」。二、CLAUDE.md:加载顺序、@import,与「它不是闸门」
CLAUDE.md 可以放在好几个位置,各有各的 scope;官方文档的那张表按 load order 从最宽到最窄排列,于是 project 指令在 context 里出现在 user 指令之后。四个位置的路径要能默写:
速查栏 2 · CLAUDE.md 家族
/Library/Application Support/ClaudeCode/CLAUDE.md ✔ 真实 · managed policy(macOS)
/etc/claude-code/CLAUDE.md ✔ 真实 · managed policy(Linux / WSL)
C:\Program Files\ClaudeCode\CLAUDE.md ✔ 真实 · managed policy(Windows)
~/.claude/CLAUDE.md ✔ 真实 · user,你的所有项目
./CLAUDE.md ✔ 真实 · project,随版本控制分发
./.claude/CLAUDE.md ✔ 真实 · project 的另一个合法位置
./CLAUDE.local.md ✔ 真实 · local,进 .gitignore
./.claude/CLAUDE.priority.md ✘ 不存在 · 自制干扰项
./CLAUDE.override.md ✘ 不存在 · 自制干扰项
@docs/git-instructions.md ✔ 真实 · @path 导入语法,最大深度四跳
`@README`(反引号包住) ✔ 真实 · 代码 span 内的 @ 不触发导入
!include docs/git-instructions.md ✘ 不存在 · 自制干扰项
@docs/git-instructions.md --depth=6 ✘ 不存在 · 自制干扰项
这些文件是拼接,不是覆盖。官方原话是:发现的所有文件是concatenated 进 context 而不是互相覆盖;跨目录树的顺序是从文件系统根往下排到你的工作目录,同一个目录内 CLAUDE.local.md 排在 CLAUDE.md 之后。所以「离启动点越近的指令,被读到得越晚」,而不是「越近的把远的顶掉」。这条决定了两个工程后果:其一,两份 CLAUDE.md 说了相反的话,不会有一份「赢」,两份都在 context 里;其二,monorepo 里父目录的规则会一路跟下来——要甩掉它得用 claudeMdExcludes 按路径或 glob 跳过,这个键可以配在任意一层且数组跨层合并。
子目录里的 CLAUDE.md 不在启动时加载。工作目录之上的 CLAUDE.md 与 CLAUDE.local.md 在启动时加载;工作目录之下的那些是按需的——Claude 在那个子目录里读、写或编辑另一个文件时才加载。这是 monorepo 场景最常见的困惑来源:在仓库根启动,然后奇怪为什么 packages/api/CLAUDE.md 的约定没从一开始就生效。答案不是文件写错了,是它还没被触发。
@path 导入有三条要记的性质。相对路径相对于含该导入的那个文件,不是工作目录;可以递归导入,最大深度四跳;被导入的文件在启动时就随引用它的 CLAUDE.md 一起展开进 context。最后这条推出一个反直觉的结论:拆成 import 有助于组织,但不省 context——官方明说 splitting into @path imports 不减少 context,因为被导入的文件在 launch 时就加载了。真要省,得靠把单文件压到 200 行以内这类做法,或者用 path-scoped rules 让指令只在 Claude 处理匹配文件时才进来。另外,导入解析会跳过 Markdown code span 与围栏代码块,想在文中提一个路径而不触发导入,用反引号包起来即可。
为什么 CLAUDE.md 拦不住事——本章最该记住的一句
官方对 CLAUDE.md 与 auto memory 的定位是同一句话:两者都在每次会话开始时加载,但 Claude 把它们当 context 而不是被强制的配置;要在不管 Claude 怎么判断的情况下阻断某个动作,该用 PreToolUse hook。文档另一处把这条分工做成了对照:managed settings 用于 technical enforcement,managed CLAUDE.md 用于 behavioral guidance;settings 规则由 client 强制执行、不看 Claude 怎么决定,而 CLAUDE.md 指令塑造行为、不是一个硬强制层。
| 诉求 | 该配在哪 | 为什么 |
|---|---|---|
| 阻断特定工具、命令或路径 | permissions.deny(managed settings) | 由 client 强制执行 |
| 代码风格与质量约定 | managed CLAUDE.md | 行为指导,塑造倾向 |
| 某个动作在每次编辑后都要跑 | hooks | 固定生命周期点触发 |
| 合规提醒与数据处理注意事项 | managed CLAUDE.md | 行为指导 |
这正是 ch01 P3「拦得住的才叫控制」在配置这一层的落点:把 NEVER delete production data 写进 CLAUDE.md,得到的是一个请求;把它写进 permissions.deny 或一个 PreToolUse hook,得到的才是一道闸门。考场上凡是「往 CLAUDE.md 里加一句更强硬的话」的选项,先按 D3 处理。
/compact 之后,项目根的 CLAUDE.md 会被从磁盘重读并重新注入会话,而子目录里的嵌套 CLAUDE.md 与带 paths: frontmatter 的规则只是按需重新加载,不会随 compaction 立刻回来。这条对理解「压缩之后指令为什么像丢了」很有用,但 .claude/rules/ 与 auto memory 属于 TS 3.1 的 Claude Code 组件面(见 ch15),本章不展开、也不进题。
三、model version pinning:无日期的 ID 也是钉住的
这一节有一个几乎人人搞反的点,值得单独立一段。官方文档专门写了一句 A common misconception is that… 来澄清它。
每个 Claude model ID 都是一个 pinned snapshot。带日期的 ID(例如后缀 20250929)固定到那一次发布;而从 4.6 代起的无日期 ID 同样是 pinned snapshot,不是 evergreen 指针。用某个 model ID 发请求时,其底层模型在该 ID 的整个生命周期内保持不变;Anthropic 不更新既有 model ID 的权重或配置,有更新版本时以新的 model ID 发布。
那 alias 是什么?4.6 代之前的模型在 Claude API 上另有短 alias(例如 claude-sonnet-4-5),它是一个 convenience pointer,解析到该 minor 版本最近的 dated snapshot——这一类才是会浮动的。而 4.6 代的 claude-sonnet-4-6 不是 alias,它本身就是那个 snapshot。两者长得一样都不带日期,语义却相反,这是本章最容易被做成干扰项的地方。
| 写法 | 形态 | 会不会随时间指向别的权重 |
|---|---|---|
claude-sonnet-4-5-20250929 | 4.6 代之前的 dated ID | 不会——固定到那次发布(这个 ID 已于 2026-09-30 deprecated、2026-11-30 退役,这里只当命名示例;pinned 管的是权重不变,不管服役期限) |
claude-sonnet-4-5 | 4.6 代之前的 alias | 会——指向该 minor 版本最近的 dated snapshot |
claude-sonnet-4-6 / claude-opus-5 | 4.6 代及之后的 dateless ID | 不会——它就是 snapshot 本身 |
钉住了就万事大吉吗?不。官方给了一条边界,它比上面那条更值得记:权重对给定 ID 是固定的,但模型周边的 serving infrastructure——请求路由、安全分类器、采样逻辑——会随时间变化;偶尔,基础设施更新会在 model ID 与权重都没变的情况下产生可观察的细微行为差异。文档甚至给了排查提示:在一个此前稳定的 model ID 上注意到意外的行为差异,最可能的原因就是一次基础设施更新。「钉住版本」买到的是「权重不变」,不是「输出逐字不变」。这条正是 ch01 P8 的边界——版本轴与采样轴要分开,而这里还多了第三条轴。
版本钉住之后还要管生命周期。官方用四个状态描述模型生命周期:Active(完全支持、推荐使用)、Legacy(不再更新,未来可能被 deprecate)、Deprecated(仍可用但不再推荐,Anthropic 给出推荐替代与退役日期)、Retired(不再可用,Requests to retired models will fail)。每个 model ID,无论带不带日期,都有各自独立的 deprecation 与 retirement 时间表。通知与迁移的节奏也是写死的:对公开发布的模型,退役前至少提前 60 天通知;一旦 deprecated 就要在 retirement date 之前把用量迁完,过了那个日期的请求会失败;官方还建议提前充分测试替代模型,而不是等到最后一周。
顺带把另一条「版本」讲清楚,它和模型版本不是一回事:每次 API 请求都要带 anthropic-version 请求头(例如 anthropic-version: 2023-06-01),用官方 client SDK 时它被自动处理。在给定 API 版本内,Anthropic 保留既有输入参数与既有输出参数;但可能新增可选输入、新增输出值、改变特定错误类型的触发条件、给 enum 类输出值新增变体(例如 streaming 事件类型)。最后这一条是集成层的直接约束:你的 SSE 事件分派、你的 stop_reason 分支,都要按「将来会多出没见过的取值」来写,否则一次向后兼容的平台更新就能打翻你的 parser。这一点与 ch02 的流式事件序列、ch10 的 defensive parsing 是同一条线。
四、prompt versioning:产品机制在服务端,工程实践在你这边
blueprint 把 prompt versioning 和 model version pinning 并列写进同一条 skill。但这两样东西的「产品支持程度」差得很远,得分开讲。
走 Messages API 时,prompt 没有产品级的版本机制。请求体里没有任何一个字段承载「这是第几版 prompt」——system 字符串就是一串字节,服务端不替你记它的历史。所以在这条路径上,prompt versioning 是工程实践,不是产品机制:把 prompt 从散落在三个源文件里的字符串常量,变成有版本号的构件(独立文件、随代码一起走 code review、发布时带 tag、灰度时新旧并跑对照)。这套做法与 ch05 的版本控制与 SDLC 集成是同一套工具,本书不在此把它包装成 Claude 特性。
vb: 出处。本书的纪律是:查不到官方行为就不写行为,但可以写「这里没有产品机制、于是落到工程实践」——后者是对锚点缺失的如实陈述,不是对行为的猜测。走 Managed Agents 时,prompt versioning 是真的产品机制。官方对 agent 的定义是:一份 reusable, versioned configuration,打包 model、system prompt、tools、MCP servers 与 skills;创建一次,之后每次开 session 用 ID 引用它。也就是说 system prompt 不再住在你的代码里,而是住在服务端的一个带版本的资源里。
| 动作 | 行为 |
|---|---|
| 更新 agent | 配置真的变了才生成新版本;version 从 1 开始,每次「改变了 agent 的更新」递增 |
| 更新但内容没变 | 不创建新版本,直接返回现有版本(no-op detection) |
请求里带 version | 乐观并发:与当前版本不匹配返回 409 |
请求里省略 version | 无条件应用,last write wins |
| List versions | 返回完整版本历史,可用来追踪一段时间内的变化 |
做题时的判据很干脆:题干问「怎么知道上周改了什么、怎么退回去」,答案在版本历史与乐观并发这一侧;题干问「两个人同时改把对方覆盖了」,答案是带上 version——省略它的语义就是 last write wins,两边都不会收到错误。这是 P8「版本钉住,变更当 breaking」在服务端配置上的形态。
五、plugin dependencies:把上游按住在测过的范围里
Claude Code 的 plugin 可以打包 skills、agents、hooks 与 MCP servers。一旦一个 plugin 依赖另一个 plugin,配置管理的问题就从「我这台机器」变成了「谁的发布会打翻谁」。
plugin manifest 是 .claude-plugin/plugin.json,定义插件的元数据与配置。有两件事常被记反:manifest 本身是可选的——省略时 Claude Code 按标准布局加载组件,插件名取自 marketplace 条目(用 --plugin-dir 加载时取目录名);而如果你写了 manifest,name 是唯一必填字段。目录布局也有一条硬规则:.claude-plugin/ 里只放 plugin.json,skills/、commands/、hooks/ 等其余插件文件都放在插件根目录,不能塞进 .claude-plugin/ 里面。
速查栏 3 · plugin 文件与依赖字段
.claude-plugin/plugin.json ✔ 真实 · plugin manifest,可选;写了则 name 必填
.claude-plugin/marketplace.json ✔ 真实 · marketplace manifest
hooks/hooks.json ✔ 真实 · hook 配置的默认位置
.mcp.json ✔ 真实 · MCP server 定义的默认位置
plugin.json · "dependencies" ✔ 真实 · 数组,元素可为裸字符串或对象
plugin.json · dependencies[].name ✔ 真实 · 依赖的插件名
plugin.json · dependencies[].version ✔ 真实 · semver range,如 ~2.1.0 / ^2.0 / >=1.4 / =2.1.0
plugin.json · dependencies[].marketplace ✔ 真实 · 换一个 marketplace 解析 name
marketplace.json · allowCrossMarketplaceDependenciesOn ✔ 真实 · 放行跨 marketplace 依赖
.claude-plugin/dependencies.json ✘ 不存在 · 自制干扰项
plugin.json · "peerPlugins" ✘ 不存在 · 自制干扰项
plugin.json · "pinVersion" ✘ 不存在 · 自制干扰项
plugin.lock ✘ 不存在 · 自制干扰项
依赖写在 plugin.json 的 dependencies 数组里,元素可以是只有插件名的裸字符串,也可以是带 name / version / marketplace 的对象;version 取 semver range,依赖会被取到满足该 range 的最高 tagged 版本。不写约束的代价是明说的:默认情况下依赖跟踪最新可用版本,上游一次发布就可能在无预警的情况下把你脚下的依赖换掉;版本约束的作用,就是把依赖按住在已测过的范围里,直到你决定移动。
解析与冲突的行为有四条要记:
- 自动安装:从 marketplace 安装一个声明了依赖的插件时,Claude Code 会在同一 scope 连带安装并启用这些依赖,成功信息会列出它们;缺失的已声明依赖还会被
/reload-plugins、依赖方 marketplace 的自动更新、重新运行claude plugin install与claude plugin marketplace add补装。 - 按 git tag 解析:版本约束是对着托管该依赖的仓库上的 git tag 解析的,tag 要按
{plugin-name}--v{version}命名,{version}与该 commit 的plugin.json的 version 字段一致;没有满足 range 的 tag 时:有独立仓库的插件安装失败;用相对路径引用的插件改用 marketplace 的当前副本,加载时再检查约束,不满足就让依赖方插件保持禁用。 - 多约束取交集:多个已安装插件约束同一依赖时,Claude Code 把这些 range 求交集,解析到满足全部约束的最高版本。
^2.0与>=2.1能共存;~2.1与~3.0则让后装的那个插件安装失败,报has conflicting version requirements,而原插件与依赖保持原状。 - 跨 marketplace 默认不装:Claude Code 默认不安装位于另一个 marketplace 的依赖——除非用户已在同一 scope 装好并启用了它——理由是防止一个 marketplace 悄悄拉进你没审过的来源。要放行,需由 root marketplace 的维护者在
marketplace.json的allowCrossMarketplaceDependenciesOn里加上目标 marketplace 名;而且只看 root marketplace 的这份允许清单。
启用与禁用也是连带的:启用一个插件会一并启用它已安装但被禁用的依赖(依赖没装则启用失败,并提示先装);反过来,若还有别的已启用插件需要某个依赖,禁用它会被拒绝,并打印一条按正确顺序禁用两者的串联命令。还有一个很实用的打包用法:manifest 只需要 name,所以一个只含 name 与 dependencies 数组的插件就是合法插件——安装它就把整组依赖拉进来,于是「后端标准插件集」可以做成一次安装。这正是 P4「能力放对载体」在插件层的形态:要跨人复用、要独立维护,就做成一个可依赖的单元,而不是让每个人往自己的 settings.json 里手抄一遍。
@path 导入的相对解析与四跳上限、CLAUDE.md 是 context 而非强制配置、model version pinning 的 dateless ID 与 alias 语义、模型生命周期与退役、anthropic-version 与向后兼容边界、prompt versioning 的产品机制与工程实践两条路径、plugin manifest 与 dependencies 的声明、解析、冲突与跨 marketplace 规则)。相邻考点:Claude API 的请求形状与流式事件见 ch02;界面差异、session hygiene 与 plugin management 见 ch03;版本控制与 SDLC 集成见 ch05;跨模型的 breaking behavior changes 与选型见 ch07;hooks 作为确定性闸门见 ch14;Claude Code 的组件五件套与 .claude/rules/ 见 ch15;集成层与模型输出的源头隔离见 ch16。本章题组
CLAUDE.md whose first line reads NEVER run `terraform apply` against the prod workspace. Over three weeks of Claude Code sessions the command still executed twice, both times during long refactors after the context had been compacted. The team's postmortem needs a control that holds regardless of what the model decides. What should they ship?CLAUDE.local.md 在同目录内确实排在 CLAUDE.md 之后,但那是拼接顺序,官方没有把它描述成权重,更不构成强制。.claude/settings.json with "model": "claude-sonnet-5". One engineer's sessions keep starting on a different model. Their ~/.claude/settings.json also sets model, their .claude/settings.local.json does not, no --settings or --model flag is passed, and their organization deploys no managed settings. Their shell profile contains export ANTHROPIC_MODEL=claude-opus-4-8. What explains it, and what is the fix?ANTHROPIC_MODEL 压过任何文件里的 model 键,而 ANTHROPIC_DEFAULT_MODEL 只在没有文件设置 model 时生效。题干把四个文件层逐一排除干净了,剩下的唯一解释就是那行 export。$schema 是给编辑器做补全与内联校验用的,不是键生效的前置条件;官方甚至提醒它可能落后于最新 CLI 版本,对新键报告警也不代表配置无效。这里把一个真实字段安上了它没有的门禁职责。./CLAUDE.md (4-space indentation) and packages/api/CLAUDE.md (2-space, plus the service's error-format rules). Engineers start Claude Code at the repository root. Session transcripts show the API rules appearing only after Claude has opened a file under packages/api/; before that, generated code uses the root conventions. The team wants the API rules in effect from the first turn of a root-launched session. What is happening, and what fixes it?@path 从根文件导入——被导入的文件在 launch 时随引用它的 CLAUDE.md 一起展开。claudeMdExcludes 是真实存在的键,但它只能少加载一个文件,不能让另一个文件提前加载。CLAUDE.local.md 确实排在 CLAUDE.md 之后。后半句是私接的:改名不改变「在工作目录之下」这个事实,按需加载的判据是位置而不是文件名。"model": "claude-sonnet-4-6" in code and has run unchanged for four months. On one Tuesday, latency-sensitive classification outputs shift: same prompts, same parameters, a measurable change in edge-case wording, no code deploy. A teammate argues that the dateless ID is an evergreen pointer that has just rolled onto a newer snapshot. How should the on-call engineer respond?claude-sonnet-4-6 没有「加上日期的同一个 snapshot」这种写法。4.6 代及之后的 ID 就是 dateless 格式,带日期的形态属于 4.6 代之前的模型。那个标识符是本书自制的干扰项,没有官方出处——格式看起来完全合理,只是不存在。system prompt is edited through the API. On Thursday a quality regression appears; nobody can say which of the week's edits caused it, and one engineer's edit silently replaced the other's from twenty minutes earlier. Neither call reported an error. What should the team change?version 从 1 开始递增,List versions 返回完整版本历史用来追踪变化;而「谁也没收到错误就被覆盖了」正是省略 version 的语义——省略即无条件应用、last write wins,带上它则不匹配时返回 409。system 字段。而 agent 本身就是一份 reusable, versioned configuration,system prompt 已经住在一个带版本的服务端资源里;再搭一条自制同步链路,等于把现成的版本轴换成一条没有并发保护的。version 的语义就是 last write wins,两位工程师都不会收到错误,banner 只会跟着被一起覆盖。前半句甚至把病因当成了处方。secrets-vault. Plugin deploy-kit is installed and declares { "name": "secrets-vault", "version": "~2.1.0" }. An engineer now runs claude plugin install release-bot, whose manifest declares { "name": "secrets-vault", "version": "~3.0.0" }. The install fails with a has conflicting version requirements error and the engineer reports that deploy-kit "seems fine, so the dependency must be broken." What is actually going on?~2.1 与 ~3.0 这一组的结果就是后装的那个插件安装失败、报 has conflicting version requirements,而原插件与依赖保持原状。所以「deploy-kit 看着没事」不是矛盾,正是预期行为,处置方向是调整两个 range 之一。has conflicting version requirements,两个 range 不相交,手工装哪个版本都会让另一个插件不满足。真机制、错场景。backend-standard, a manifest that is little more than a dependencies array of four plugins. On a new hire's machine the install pulls in three of them; the fourth stays unresolved and claude plugin list reports a dependency error naming it. That fourth plugin lives in a second internal marketplace that the new hire has never added. What is the correct fix?backend-standard 的那个)的维护者把目标 marketplace 加进 allowCrossMarketplaceDependenciesOn。题干的「三个装上、第四个卡住、它在另一个 marketplace」正是这条默认规则的形状;两条出路都来自同一段官方原文。.mcp.json 是插件里放 MCP server 定义的默认位置,不是声明「我依赖另一个插件」的地方——那是 plugin.json 的 dependencies 数组。把一个插件依赖降级成一份内联的 server 配置,等于放弃版本约束、自动安装与启用连带这一整套。prune 清理的是「已经没有任何已装插件需要它」的孤儿依赖,方向与本题相反——这里的依赖不是多余的,是缺的。而且题干已经指认了源头:那个 marketplace 从来没被添加过。