关于 settings
settings 是一项用于查看或修改 CCGS 项目配置的技能,可处理团队共享的 project.yaml 和个人覆盖文件 project.local.yaml。它按配置模式读取、校验并展示设置,也会在写入前检查键和值及审批要求。
主要功能
- 01查看全部或单项配置
- 02设置共享或本地 YAML 值
- 03解析配置来源与有效值
- 04校验枚举并检查已知键
- 05按自动化模式执行写入审批
适用场景
- 01查看项目当前生效配置
- 02修改个人本地覆盖项
- 03调整团队共享设置并确认写入
- 04识别无效或未被读取的配置键
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ donchitos/ claude-code-game-studios --skill settings安装后在 Claude Code 中输入/settings确认已加载。
详细介绍
/settings — 查看或更改项目配置
管理 project.yaml(团队范围的配置,会提交到版本控制)和 project.local.yaml
(每位开发者的覆盖配置,会被 git 忽略)。使用此技能检查当前设置或更改设置,无需手动编辑 YAML。
自动化模式:解析 modes.automation(project.local.yaml →
project.yaml → 默认值 collaborative)。Phase 4 和
Phase 5 中的审批关卡遵循 .claude/docs/automation-modes.md 中的模式。注意:
写入 project.yaml 属于 schema_changes 决策(默认
automation_always_ask 类别),因此即使处于 autonomous 模式,/settings 也会确认配置写入,
除非用户已将 schema_changes 从其始终询问列表中移除。
Phase 0:解析参数
确定用户调用的是以下四种形式中的哪一种:
| 形式 | 模式 |
|---|---|
/settings(无参数) | 查看全部(Phase 2) |
/settings <key>(单个参数,不含 =) | 查看一项(Phase 3) |
/settings <key>=<value>(含 = 的单个参数) | 设置 YAML(Phase 4) |
/settings --local <key>=<value>(--local 前缀) | 设置本地配置(Phase 5) |
对于“设置 YAML”和“设置本地配置”,<key> 是点分隔的 YAML 路径(例如
modes.review_mode、testing.strict.logic),而 <value> 是新值。
**空操作数验证:**按 = 拆分 key=value 操作数后,如果 <key> 或 <value> 为空,则格式错误——转到
Phase 6(用法说明),而不是尝试写入。此规则适用于“设置 YAML”和“设置本地配置”两种路由:对于 --local,先去掉 --local 标记,
再以相同方式检查剩余操作数(因此,/settings --local =foo
和 /settings --local key= 都会转到 Phase 6)。
任何其他不符合上述四种形式的输入 → Phase 6(用法说明)。
Phase 1:加载辅助脚本
所有四个子命令都需要 yaml-helper.sh。将每次辅助函数调用作为独立的普通命令运行:
bash "${CLAUDE_SKILL_DIR}/../../hooks/yaml-helper.sh" <helper call>
<helper call> 是下方各阶段指定的函数,例如
resolve_setting modes.review_mode 或 validate_yaml_enum project.yaml。辅助脚本会自行切换到项目根目录,因此即使会话是在子文件夹中启动的,project.yaml 和
project.local.yaml 参数也能正确解析,并且会保留函数的退出状态。is_locally_overridable
也会输出 0 或 1。请严格按所示方式运行调用:不要 source 文件,不要用 cd ... && 包装,也不要追加 ; echo $?。此技能仅预先批准普通形式,复合命令每次都会询问用户。
如果缺少 .claude/hooks/yaml-helper.sh,说明这不是 CCGS 项目。
报告错误并退出。
阶段 1b:保留设置检测
有些设置有完整文档且可以设置,但没有任何技能或钩子会读取它们——设置它们不会产生任何变化。**本技能必须明确说明这一点。**如果返回普通的成功信息,用户完全有理由相信设置描述的行为已经生效——无障碍等级正是促使我们处理此问题的情况。(此处有意不点明该键;请参阅下方说明。)提示仅存在于 .claude/docs/effects-map.md 中,而配置项目的人永远不会打开这个文件。毫无作用的设置绝不能看起来和有效设置一模一样——这是 .claude/rules/skill-authoring.md 中义务 3 的要求。
推导这个集合;不要硬编码(义务 5):
grep -B14 '^> ### RESERVED' .claude/docs/effects-map.md | grep -E '^## '
匹配提示标题(> ### RESERVED),绝不要只匹配单词 “RESERVED”。
platform.cert_tier 的章节在说明文字中提到了该提示,用来记录它曾经带有此提示,后来则变成了有效设置。更宽泛的匹配会再次将一个有效设置报告为无效——这会低估框架的功能,也是无效设置检查需要验证两个方向的原因。
一个标题可能会用 “ and ” 连接两个键——按此分隔符拆分,确保两个键都被捕获,否则第二个键会悄悄漏出集合。将结果记为 RESERVED_SET。
此处刻意不点明这些键。无效设置检查会断言允许列表中的设置没有读取方,并统计任何写出该键的技能文件。在这里点名说明如何拆分,会让
/settings看起来像是读取了两个实际上无人读取的设置,从而导致检查失败——而且这是正确的。在解释为何不该使用某条路径时写出无效路径,也会触发同样的检查。请改写,而不是添加豁免:严格执行正是目的,因此不要为了让示例更清楚而再次写出这些字面名称。
绝不要拒绝对保留键的写入。存储用户想要的值是合理的;缺陷在于悄无声息地存储它。
阶段 2:查看全部模式
(未提供参数时)
-
确认
project.yaml存在。如果缺失,请报告:“未找到
project.yaml——请运行/start创建文件。” 然后退出。 -
检查
project.local.yaml是否存在。 -
运行
validate_yaml_enum project.yaml并捕获所有错误。 如果project.local.yaml存在,也运行validate_yaml_enum project.local.yaml。 为每个错误标注其来源文件,以便用户区分。 -
**枚举叶节点。**使用
Read工具将两个 YAML 文件作为文本读取。遍历每个文件的结构,收集所有叶键,组成去重后的点分路径列表。叶节点是值非空的key: value行(标量或内联数组),不是引入嵌套映射的父级块。保持每个文件中的插入顺序;合并时,将project.local.yaml中尚未出现在project.yaml里的叶节点追加到列表。所得列表即为要显示的叶节点集合。然后追加严谨度系列设置——
modes.rigor、modes.workflow、docs.density、qa.level、modes.story_granularity、modes.review_mode、team.size——若这七项中的任一项尚未包含在合并列表中,就将其追加。这些设置通常不是任一文件中的叶节点:modes.rigor有终端默认值,另外六项由严谨度扩展提供(设置为本地值时,modes.review_mode和team.size也可能作为文件叶节点出现,但仍必须追加,以显示其派生值)。只枚举文件叶节点会隐藏用户刚刚选择的设置,而这恰恰是唯一一个旨在展示这些设置的视图。 -
对每个叶子项,执行三次读取和一次检查:
resolve_setting <path>→<value>、TAB、<source>。此操作会遍历 整条链路(project.local.yaml→project.yaml→ 旧版文件 →rigor展开 → 默认值),因此只有它能报告派生值或指出值的来源。get_yaml_key project.yaml <path>→ 仅 YAML 值(仅当需要显示本地覆盖项所遮蔽的值时才需要)is_locally_overridable <path>→ 0(可覆盖)或 1(锁定)
根据
<source>计算来源标注:<source>标注 project.local.yaml,仅 YAML 值非空(LOCAL OVERRIDE — yaml has: <yamlvalue>)project.local.yaml,仅 YAML 值为空(project.local.yaml)project.yaml(project.yaml)旧版文件路径(例如 production/review-mode.txt)(<path>, legacy)rigor:<level>(derived from rigor: <level>)default(default)unset跳过此叶子项(不要打印未设置的键) 当
is_locally_overridable返回 1 时,在标注后追加, locked。如果叶子项属于
RESERVED_SET(阶段 1b),则追加, reserved,并在代码块下方打印这一行图例:reserved = stored, but nothing reads it. Setting it changes no behaviour today.四个阶段都必须标记 reserved — 单项视图、全部视图,以及两种写入路径。 如果标记了单项视图和写入路径,却漏掉全部视图 — 这是此技能最常用的形式 — 那么设置了保留键的用户就会看到它与正常设置并列,且只带有普通的
(project.yaml)标注,出现在大多数人实际使用的视图中。四处都要防护:单项视图和全部视图完全相同,就像两种写入路径一样。 -
按顶层分组打印(
framework、modes、engine等),顺序按叶子项首次出现的顺序排列。分组由点分路径决定,而不是由叶子项来自哪个文件决定 — 例如,即使只有project.local.yaml定义了modes.automation,它也应归入modes:。每个分组内的叶子项缩进 2 个空格;子块进一步缩进(例如testing.strict.*)。 输出示例:
project.yaml: present
project.local.yaml: present
Schema validation: ok
Effective config:
framework:
version: 1.2.0 (project.yaml, locked)
modes:
review_mode: lean (project.yaml)
automation: autonomous (LOCAL OVERRIDE — yaml has: collaborative)
rigor: full (project.yaml, locked)
workflow: full (derived from rigor: full, locked)
story_granularity: fine (derived from rigor: full, locked)
docs:
density: terse (project.yaml, locked)
qa:
level: full (derived from rigor: full, locked)
engine:
name: Godot (project.yaml, locked)
version: 4.7 (project.yaml, locked)
testing:
strict:
logic: false (LOCAL OVERRIDE — yaml has: true)
integration: true (project.yaml)
locked = cannot be overridden in project.local.yaml (project-wide setting).
It does NOT mean fixed: a derived value can still be set explicitly with
/settings <key>=<value>, and an explicit value wins over rigor.
只要任何已渲染的行带有 locked,就应在配置块之后打印这两行图例。否则,用户会把 locked 理解为“不可变”,并得出由 rigor 派生的选项无法更改的结论 — 这与阶段 3 的单项视图对同一选项的说明(“显式值优先于 rigor”)正好相反。两个视图不能让用户对同一选项是否可变产生矛盾的印象。
上方的 docs.density 行体现了优先级规则:显式设置的 project.yaml 值会继续优先于 rigor 原本会派生出的值,因此仍可表达“全面但简洁”。
如果存在 schema 错误,请按以下格式在有效配置块之前打印:
Schema validation: ERRORS
[project.yaml] modes.review_mode: 'chaotic' is not a valid value (expected: full|lean|solo)
[project.local.yaml] engine.name: 'Pygame' is not a valid value (expected: Godot|Unity|Unreal)
阶段 3:查看单个模式
(仅有一个参数时,不含 =)
如果 <key> 在 RESERVED_SET 中,请在值块之前打印此内容——放在上方,
不要放在下方。在一个有效设置的值下方添加注意事项,会被视为脚注:
<key>已保留——没有任何内容会读取它。 下方的值已存储,但 当前不会改变任何行为。
将参数记为 <key>。执行四次读取:
resolve_setting <key>→<value>、TAB、<source>(有效值 + 来源信息)get_yaml_key project.yaml <key>→ 仅 YAML 值get_yaml_key project.local.yaml <key>→ 仅本地值(文件不存在时跳过)is_locally_overridable <key>→ 0(可覆盖)或 1(锁定)
根据 <source> 确定来源(与阶段 2 第 5 步使用相同的表),
并确定本地覆盖状态:
- 如果仅本地值非空,则为
yes - 否则为
no
打印:
<key>
Effective value: <effective-value or "(not set)" if <source> is unset>
Source: <project.yaml | project.local.yaml | project.local.yaml (overrides project.yaml) | derived from rigor: <level> | default>
Locally overridden: <yes | no>
Whitelist: <"can be locally overridden" if is_locally_overridable returned 0, else "locked — project-wide">
如果两个文件都设置了此键(即本地覆盖情况),请在“来源”下方再添加两行:
project.yaml value: <yamlvalue>
project.local.yaml value: <localvalue>
当 <source> 为 rigor:<level> 时,请在“来源”下方添加一行,让用户
知道该值可以设置,以及如何设置:
Set explicitly with: /settings <key>=<value> (an explicit value wins over rigor)
当有效值为空(该键在任何地方都未设置)时,仅打印:
<key>
Effective value: (not set)
Locally overridden: no
Whitelist: <"can be locally overridden" | "locked — project-wide">
阶段 4:Set-yaml 模式
(当使用 <key>=<value> 且不带 --local 时)
- 架构验证:调用
validate_enum_value <key> <value>。如果 返回 1,则将 stderr 行原样展示给用户 (Invalid value '<value>' for '<key>'. Allowed: ...),技能 随即退出,不进行写入。如果返回 0,则该值有效,或该键没有枚举约束——继续。
1b. 已知键检查:按照阶段 1 中的方式,从项目根目录运行
bash .claude/scripts/config-set.sh <key> <value> --dry-run,并读取其
known 行。如果显示 yes,则继续。如果显示 no,说明 CCGS 中没有任何内容会读取
该键,因此写入不会产生任何变化;这通常是拼写错误。暂时不要
写入:说明这一点,并通过 AskUserQuestion 询问,选项为
Use <nearest> instead(仅当该行以 nearest: <key> 结尾时显示)、
Write <key> anyway、Cancel。只有用户明确选择后才继续;如果跳过或
答案为空,则取消。选择 Use <nearest> instead 时,使用该键重新开始本阶段。
-
保留键警告:如果
<key>在RESERVED_SET(阶段 1b)中,请在审批提示 之前打印:"注意:
<key>是 RESERVED——没有技能或钩子会读取它,因此设置它不会 改变任何行为。无论如何,你的值都会被存储。"然后像往常一样继续审批关卡。阶段 5 会显示完全相同的 警告。一个写入路径显示警告,而另一个不显示,会留下通往相同结果的静默路径, 这正是此检查要防止的问题。
-
覆盖警告:读取
get_yaml_key project.local.yaml <key>。如果 返回非空值,请在审批提示之前打印:"注意:此设置当前在
project.local.yaml中被本地覆盖(值:<localvalue>)。写入project.yaml不会改变你的有效值。" -
审批关卡:使用
AskUserQuestion:- 提示:
Set `<key>=<value>` in `project.yaml`? - 选项:
[A] Yes, write/[B] No, cancel
- 提示:
-
使用
Edit工具写入。编辑策略取决于project.yaml中 已有的内容:情况 A — 键已存在:读取
project.yaml。定位<lastsegment>: <oldvalue>这一行,以及它正上方引入其父级块的那一行(例如,modes.review_mode的modes:)。 构造Edit,使old_string同时包含父级 块行和叶节点行,这样即使<lastsegment>: <oldvalue>出现在其他位置, 匹配也具有唯一性。modes.review_mode=full的示例:old_string: modes: review_mode: lean new_string: modes: review_mode: full情况 B — 父级块存在,叶节点不存在:找到父级块的最后一行。 使用
Edit,在正确的缩进级别追加新的叶节点行,并使用 最后一个现有叶节点作为old_string锚点。情况 C — 父级块不存在:在文件末尾追加新的顶级 块(确定性操作——始终追加到文件末尾,绝不插入块与块之间)。使用
Edit, 将文件中最后一个非空行作为old_string,并在其后追加新块。情况 D — 文件结构异常(存在同名注释键、嵌套关系不明确、YAML 格式错误):停止并通过
AskUserQuestion询问:"无法在
project.yaml中为<key>找到合适的插入位置。请告诉我该怎么做。" -
确认:
Set `<key>=<value>` in `project.yaml`.当
<key>为modes.rigor时,此次写入还会调整其他六项配置,因此请打印它们现在解析出的值——对每项重新调用resolve_setting,而不要照抄展开表,因为其中任何一项都可能带有优先级更高的显式值:Set modes.rigor=full in project.yaml. It now supplies: modes.workflow: full (derived from rigor: full) docs.density: terse (project.yaml — explicit, unchanged) qa.level: full (derived from rigor: full) modes.story_granularity: fine (derived from rigor: full) modes.review_mode: full (derived from rigor: full) team.size: studio (derived from rigor: full)
第 5 阶段:本地模式
(当使用 --local <key>=<value> 时)
-
白名单检查:调用
is_locally_overridable <key>。- 如果返回 1(已锁定),打印一行:
<key> 不能在本地覆盖——它是项目级设置。使用 `/settings <key>=<value>`(不带 `--local`)来为整个团队更改此设置。 - 不写入并退出。
- 如果返回 1(已锁定),打印一行:
-
架构验证:调用
validate_enum_value <key> <value>。如果 返回 1,原样显示 stderr 消息,并且不写入就退出。
2a. 已知键检查:与第 4 阶段步骤 1b 相同,在 <key> 前加上
--local:如果 known 行显示 no,写入前先询问;如果该行提供了 nearest: 键,则提供该键。
2b. 保留键警告:如果 <key> 在 RESERVED_SET(第 1b 阶段)中,请在
确认提示前打印:
"注意:
<key>是 RESERVED——没有技能或 hook 会读取它,因此设置它 不会改变任何行为。无论如何,你的值都会被保存。"
与第 4 阶段步骤 2 相同,这是有意为之。两种写入路径都会将用户意图写入
同一个记录文件;如果只在一种路径中警告,--local 就会成为
不提示用户的途径。
-
基础文件检查:如果
project.yaml不存在:"无法创建
project.local.yaml——缺少project.yaml。请先运行/start创建该文件。" 退出。(根据规范,本地覆盖需要基础文件。) -
确认关卡:使用
AskUserQuestion:- 提示:
在 `project.local.yaml` 中设置 `<key>=<value>`(你的个人覆盖,已加入 gitignore)? - 选项:
[A] 是,写入/[B] 否,取消
- 提示:
-
写入:
- 如果
project.local.yaml不存在,使用Write创建该文件, 并将新设置置于其父级块中。--local modes.automation=autonomous的最简 示例:# project.local.yaml — 每位开发者的覆盖设置(已加入 gitignore) modes: automation: autonomous - 如果文件存在,使用
Edit,并采用 第 4 阶段步骤 4 中相同的 Case A/B/C 策略。
- 如果
-
确认:
已在 `project.local.yaml` 中设置 `<key>=<value>`(你的覆盖设置)。
第 6 阶段:用法
当参数与四种有效形式都不匹配(或操作数为空)时,打印:
Usage:
/settings — view all effective config
/settings <key> — view a single setting
/settings <key>=<value> — change a setting (project.yaml)
/settings --local <key>=<value> — change a setting locally
(project.local.yaml, gitignored)
Examples:
/settings modes.review_mode
/settings modes.review_mode=full
/settings --local modes.automation=autonomous
说明
- 所有 YAML 写入都通过
Edit(已有键/块)或Write(新文件)完成。此技能不依赖yq或任何外部 YAML 库。 - 白名单由
yaml-helper.sh(is_locally_overridable函数)强制执行。要扩展白名单,请编辑_yaml_helper_locally_overridable常量——不要在此重复列出。 - 派生值不会存储在任何地方。
modes.workflow、docs.density、qa.level、modes.story_granularity、modes.review_mode和team.size都没有 终端默认值;如果没有文件设置它们,其值将来自yaml-helper.sh中的modes.rigor展开逻辑(_yaml_helper_rigor_expansion)。使用resolve_setting报告它们——get_yaml_key读取结果为空,会将其显示为“(未设置)”,但实际上每项技能都在依据真实值运行。 - 架构验证所用的枚举常量位于
yaml-helper.sh(_yaml_helper_enums)。validate_enum_value是用于待写入值的单键 验证器;validate_yaml_enum用于验证整个文件(由session-start.sh使用)。 project.local.yaml已加入 gitignore。这些 覆盖设置不会传播给队友。- 对于特定游戏类型或情境应选择哪个值,以及何时
建议更改设置,请参阅
.claude/docs/settings-guidance.md(建议预设值- 更改触发条件)。
effects-map.md说明各项设置的作用;该文档说明 应选择哪项设置。
- 更改触发条件)。