settings

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

/settings — view or change project config

/settings — 查看或更改项目配置

Manages
project.yaml
(team-wide config, committed) and
project.local.yaml
(per-developer overrides, gitignored). Use this skill to inspect current settings or change them without manually editing YAML.
Automation mode: Resolve
modes.automation
(
project.local.yaml
→
project.yaml
→ default
collaborative
). The approval gates in Phase 4 and Phase 5 follow the pattern in
.claude/docs/automation-modes.md
. Note: writing to
project.yaml
is a
schema_changes
decision (a default
automation_always_ask
category), so
/settings
confirms config writes even in
autonomous
mode unless the user has removed
schema_changes
from their always-ask list.
管理
project.yaml
(提交到版本控制库的团队级配置)和
project.local.yaml
(每位开发人员的本地覆盖配置,已被 gitignore)。使用此 skill 可检查当前 设置,或在不手动编辑 YAML 的情况下更改设置。
自动化模式:解析
modes.automation
(
project.local.yaml
→
project.yaml
→ 默认值
collaborative
)。第 4 阶段和第 5 阶段的审批关卡遵循
.claude/docs/automation-modes.md
中的模式。注意: 写入
project.yaml
属于
schema_changes
决策(默认的
automation_always_ask
类别),因此,即使处于
autonomous
模式,
/settings
仍会确认配置写入操作,除非用户已从始终询问列表中删除
schema_changes
。

Phase 0: Parse Arguments

第 0 阶段:解析参数

Determine which of the four forms the user invoked:
FormMode
/settings
(no args)
View-all (Phase 2)
/settings <key>
(single arg, no
=
)
View-one (Phase 3)
/settings <key>=<value>
(single arg with
=
)
Set-yaml (Phase 4)
/settings --local <key>=<value>
(
--local
prefix)
Set-local (Phase 5)
For Set-yaml and Set-local,
<key>
is a dotted YAML path (e.g.
modes.review_mode
,
testing.strict.logic
) and
<value>
is the new value.
Empty-operand validation: After splitting the
key=value
operand on
=
, if either
<key>
or
<value>
is empty, this is malformed — route to Phase 6 (usage) rather than attempting the write. The rule applies in both Set-yaml and Set-local routing: for
--local
, strip the
--local
token first, then check the remaining operand the same way (so
/settings --local =foo
and
/settings --local key=
both go to Phase 6).
Anything else that doesn't match the four forms above → Phase 6 (usage).
确定用户调用的是以下四种形式之一:
形式模式
/settings
(无参数)
查看全部(第 2 阶段)
/settings <key>
(单个参数,不含
=
)
查看单项(第 3 阶段)
/settings <key>=<value>
(单个参数,包含
=
)
设置 YAML(第 4 阶段)
/settings --local <key>=<value>
(带
--local
前缀)
设置本地值(第 5 阶段)
对于“设置 YAML”和“设置本地值”模式,
<key>
是一个点分隔的 YAML 路径 (例如
modes.review_mode
、
testing.strict.logic
),
<value>
是新值。
空操作数验证:按
=
拆分
key=value
操作数后,如果
<key>
或
<value>
任一为空,则格式错误——转到第 6 阶段(用法),不要尝试写入。 此规则同时适用于“设置 YAML”和“设置本地值”路由:对于
--local
,先移除
--local
标记,然后以相同方式检查剩余操作数(因此
/settings --local =foo
和
/settings --local key=
均转到第 6 阶段)。
其他任何不匹配上述四种形式的内容 → 第 6 阶段(用法)。

Phase 1: Load Helper

第 1 阶段:加载辅助脚本

All four subcommands need
yaml-helper.sh
. Run each helper call as its own plain command:
bash
bash "${CLAUDE_SKILL_DIR}/../../hooks/yaml-helper.sh" <helper call>
<helper call>
is the function each phase below names, for example
resolve_setting modes.review_mode
or
validate_yaml_enum project.yaml
. The helper moves to the project root itself, so those
project.yaml
and
project.local.yaml
arguments resolve when the session was started in a subfolder, and it keeps the function's exit status.
is_locally_overridable
also prints its
0
or
1
. Run the call exactly as shown: do not
source
the file, wrap it in
cd ... &&
or append
; echo $?
. This skill pre-approves the plain form only, and a compound command asks the user every time.
If
.claude/hooks/yaml-helper.sh
is missing, this is not a CCGS project. Report the error and exit.
所有四个子命令都需要
yaml-helper.sh
。每次辅助函数调用都应单独作为 普通命令运行:
bash
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 $?
。本 skill 仅预先批准普通命令形式; 复合命令每次都会要求用户确认。
如果
.claude/hooks/yaml-helper.sh
不存在,则当前项目不是 CCGS 项目。 报告错误并退出。

Phase 1b: Reserved-setting detection

第 1b 阶段:检测保留设置

Some settings are fully documented and settable, and no skill or hook reads them — setting one changes nothing. This skill must say so. Returning an ordinary success lets a user reasonably believe the behaviour the setting describes is now enforced — an accessibility tier being the case that motivates this. (The key is not named here on purpose; see the note below.) The banner exists only in
.claude/docs/effects-map.md
, which someone configuring their project will never open. A setting that does nothing must never look exactly like one that works —
.claude/rules/skill-authoring.md
obligation 3.
Derive the set; do not hardcode it (obligation 5):
bash
grep -B14 '^> ### RESERVED' .claude/docs/effects-map.md | grep -E '^## '
Match the banner HEADING (
> ### RESERVED
), never the bare word "RESERVED".
platform.cert_tier
's section mentions the banner in prose, to record that it carried one and later became a live setting. A looser match re-reports a working setting as dead — the direction that understates what the framework does, and why the dead-settings gate asserts both directions.
A heading may name two keys joined by " and " — split on that separator so both are captured, or the second one silently escapes the set. Let
RESERVED_SET
be the result.
Deliberately phrased without naming those keys. The dead-settings gate asserts that a setting on the allowlist has no readers, and it counts any skill file that spells the key. Naming them here to illustrate the split would make
/settings
register as a reader of two settings nothing reads, and the gate would fail — correctly. Spelling a dead path out while explaining why not to use it trips the same wire. Reword instead of adding an exemption: the strictness is the point, so do not re-introduce the literal names to make an example clearer.
Never refuse a write to a reserved key. Storing the value a user intends is legitimate; storing it silently is the defect.
有些设置已有完整文档且可以设置,但没有任何 skill 或 hook 读取它们—— 设置这些选项不会产生任何效果。此 skill 必须明确说明这一点。 如果只返回常规成功信息,用户会合理地以为该设置所描述的行为现在已生效—— 无障碍等级设置正是促成这一要求的典型情况。(这里故意不指出具体键名; 请参阅下方说明。)该横幅只存在于
.claude/docs/effects-map.md
, 而配置项目的人永远不会打开它。无效设置绝不能看起来与有效设置完全一样—— 这是
.claude/rules/skill-authoring.md
义务 3 的要求。
应推导出该集合,不要将其硬编码(义务 5):
bash
grep -B14 '^> ### RESERVED' .claude/docs/effects-map.md | grep -E '^## '
必须匹配横幅标题(
> ### RESERVED
),绝不能只匹配裸词
RESERVED
。
platform.cert_tier
的章节在正文中提及过该横幅,用于记录它曾经拥有 横幅,后来变成了有效设置。更宽松的匹配会把有效设置重新报告为失效设置—— 这种错误方向会低估框架的实际能力,也正是失效设置门禁同时断言两个方向的原因。
一个标题可能用
and
连接两个键——必须按该分隔符拆分, 否则第二个键会悄悄漏出集合。令结果为
RESERVED_SET
。
这里刻意不指出这些键名。失效设置门禁会断言,白名单中的设置没有读取方, 并统计任何明确写出键名的 skill 文件。如果在这里为说明拆分方式而指出这些键名,
/settings
就会被登记为两个无人读取设置的读取方,门禁将失败——而且失败得完全正确。 在解释为何不要使用它们时写出失效路径,也会触发同样的陷阱。 请改写措辞,而不是添加例外:严格性正是重点,因此不要为了让示例更清楚 而重新引入字面键名。
绝不能拒绝写入保留键。存储用户意图的值是合理行为;静默存储才是缺陷。

Phase 2: View-all Mode

第 2 阶段:查看全部模式

(when no arguments)
  1. Confirm
    project.yaml
    exists. If missing, report:
    "No
    project.yaml
    found — run
    /start
    to create one." And exit.
  2. Check if
    project.local.yaml
    exists.
  3. Run
    validate_yaml_enum project.yaml
    and capture any errors. If
    project.local.yaml
    exists, also run
    validate_yaml_enum project.local.yaml
    . Tag each error with its source file so the user can tell them apart.
  4. Enumerate leaves. Use the
    Read
    tool to read both YAML files as text. Walk through each file's structure and collect every leaf key into a deduplicated list of dotted paths. A leaf is a
    key: value
    line whose value is non-empty (a scalar or inline-array) — NOT a parent block that introduces a nested map. Maintain insertion order per file; merge by appending leaves from
    project.local.yaml
    that aren't already present from
    project.yaml
    . The resulting list is the set of leaves to display.
    Then append the rigor family —
    modes.rigor
    ,
    modes.workflow
    ,
    docs.density
    ,
    qa.level
    ,
    modes.story_granularity
    ,
    modes.review_mode
    ,
    team.size
    — for any of the seven the merged list does not already contain. These are normally leaves of neither file:
    modes.rigor
    has a terminal default and the other six are supplied by the rigor expansion (
    modes.review_mode
    and
    team.size
    may also appear as file leaves when set locally, but must still be appended so their derived value shows). Enumerating only file leaves would hide exactly the settings a user just chose, in the one view meant to show them.
  5. For each leaf, perform three reads and one check:
    • resolve_setting <path>
      →
      <value>
      , TAB,
      <source>
      . This walks the whole chain (
      project.local.yaml
      →
      project.yaml
      → legacy file →
      rigor
      expansion → default), so it is the only read that can report a derived value or name what supplied it.
    • get_yaml_key project.yaml <path>
      → yaml-only value (needed only to show what a local override is shadowing)
    • is_locally_overridable <path>
      → 0 (overridable) or 1 (locked)
    Compute the source annotation from
    <source>
    :
    <source>
    Annotation
    project.local.yaml
    , yaml-only value non-empty
    (LOCAL OVERRIDE — yaml has: <yamlvalue>)
    project.local.yaml
    , yaml-only value empty
    (project.local.yaml)
    project.yaml
    (project.yaml)
    a legacy file path (e.g.
    production/review-mode.txt
    )
    (<path>, legacy)
    rigor:<level>
    (derived from rigor: <level>)
    default
    (default)
    unset
    skip the leaf (don't print unset keys)
    Append
    , locked
    to the annotation when
    is_locally_overridable
    returned 1.
    Append
    , reserved
    when the leaf is in
    RESERVED_SET
    (Phase 1b)
    , and print this line with the legend below the block:
    reserved = stored, but nothing reads it. Setting it changes
    no behaviour today.
    All four phases must carry the reserved marking — view-one, view-all, and both write paths. Marking view-one and the writes while leaving view-all — the most-used form of this skill — unmarked means a user who set a reserved key sees it listed beside working settings with an ordinary
    (project.yaml)
    annotation, in the view most people actually run. Guard all four: view-one and view-all are twins in exactly the way the two write paths are.
  6. Print, grouped by top-level section (
    framework
    ,
    modes
    ,
    engine
    , etc.) in the order leaves first appeared. The grouping is determined by the dotted path, not by which file the leaf came from — e.g.
    modes.automation
    groups under
    modes:
    even if only
    project.local.yaml
    defines it. Within each section, indent leaves by 2 spaces; nest further for sub-blocks (e.g.
    testing.strict.*
    ). Example output:
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.
Print that two-line legend after the config block whenever any rendered line carries
locked
. Without it users read
locked
as "immutable" and conclude a rigor-derived knob cannot be changed — the opposite of what Phase 3's view-one tells them for the same knob ("an explicit value wins over rigor"). The two views must not leave contradictory impressions of the same knob's mutability.
The
docs.density
line above is the precedence rule made visible: an explicit
project.yaml
value keeps winning over the level
rigor
would otherwise derive, so "comprehensive but compact" stays expressible.
If schema errors exist, print them in this form before the effective config block:
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)
(无参数时)
  1. 确认
    project.yaml
    存在。如果不存在,请报告:
    “未找到
    project.yaml
    ——运行
    /start
    创建一个。” 然后退出。
  2. 检查
    project.local.yaml
    是否存在。
  3. 运行
    validate_yaml_enum project.yaml
    并捕获所有错误。 如果
    project.local.yaml
    存在,也运行
    validate_yaml_enum project.local.yaml
    。为每条错误标注其来源文件, 以便用户区分。
  4. 枚举叶节点。 使用
    Read
    工具以文本形式读取两个 YAML 文件。 遍历每个文件的结构,将所有叶节点键收集到去重后的点分隔路径列表中。 叶节点是值非空的
    key: value
    行(标量或行内数组),而不是引入嵌套映射的 父级块。保持每个文件中的插入顺序;对于
    project.yaml
    中尚不存在的键, 将
    project.local.yaml
    中的叶节点追加到末尾。最终得到的列表就是要显示的 叶节点集合。
    然后追加
    rigor
    配置系列
    ——
    modes.rigor
    、
    modes.workflow
    、
    docs.density
    、
    qa.level
    、
    modes.story_granularity
    、
    modes.review_mode
    、
    team.size
    ——将其中尚未出现在合并列表中的键追加进去。这七项通常都不是 两个文件中的叶节点:
    modes.rigor
    具有最终默认值,其他六项由
    rigor
    扩展提供 (当在本地设置时,
    modes.review_mode
    和
    team.size
    也可能作为文件叶节点出现, 但仍须追加它们,以显示其派生值)。仅枚举文件叶节点,会在这个专门用于 展示设置的视图中,恰好隐藏用户刚刚选择的设置。
  5. 针对每个叶节点,执行三次读取和一次检查:
    • resolve_setting <path>
      →
      <value>
      、制表符、
      <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
    中时,追加
    , reserved
    (第 1b 阶段), 并在配置块下方打印以下说明:
    reserved = stored, but nothing reads it. Setting it changes
    no behaviour today.
    所有四种模式都必须带有保留标记——查看单项、查看全部,以及两条写入路径。 只标记查看单项和写入路径,却漏掉查看全部——这个 skill 最常用的形式—— 会导致设置过保留键的用户在最常使用的视图中,看到它与有效设置并列显示, 并附带普通的
    (project.yaml)
    标注。必须保护全部四种模式:查看单项和查看全部 彼此对应,正如两条写入路径彼此对应。
  6. 打印,按顶层区块(
    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)

Phase 3: View-one Mode

第 3 阶段:查看单项模式

(when single arg, no
=
)
If
<key>
is in
RESERVED_SET
, print this BEFORE the value block
— above, not below. A caveat under a value reads as a footnote on a working setting:
<key>
is RESERVED — nothing reads it.
The value below is stored; it changes no behaviour today.
Let
<key>
be the argument. Perform four reads:
  1. resolve_setting <key>
    →
    <value>
    , TAB,
    <source>
    (effective value + provenance)
  2. get_yaml_key project.yaml <key>
    → yaml-only value
  3. get_yaml_key project.local.yaml <key>
    → local-only value (skip if file absent)
  4. is_locally_overridable <key>
    → 0 (overridable) or 1 (locked)
Determine the source from
<source>
(same table as Phase 2 step 5) and the Locally overridden status:
  • yes
    if the local-only value is non-empty
  • no
    otherwise
Print:
<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">
When both files set the key (the LOCAL OVERRIDE case), add two more lines under Source:
  project.yaml value: <yamlvalue>
  project.local.yaml value: <localvalue>
When
<source>
is
rigor:<level>
, add one line under Source so the user knows the value is settable and how:
  Set explicitly with: /settings <key>=<value>   (an explicit value wins over rigor)
When the effective value is empty (key not set anywhere), print just:
<key>

  Effective value: (not set)
  Locally overridden: no
  Whitelist: <"can be locally overridden" | "locked — project-wide">
(单个参数,不含
=
时)
如果
<key>
位于
RESERVED_SET
中,请在值块之前打印以下内容
—— 放在上方,而不是下方。位于值下方的警告看起来会像有效设置的脚注:
<key>
已保留(RESERVED)——没有任何组件读取它。
下方值已存储; 目前不会改变任何行为。
令
<key>
为传入的参数。执行四次读取:
  1. resolve_setting <key>
    →
    <value>
    、制表符、
    <source>
    (有效值和来源)
  2. get_yaml_key project.yaml <key>
    → 仅项目 YAML 中的值
  3. get_yaml_key project.local.yaml <key>
    → 仅本地 YAML 中的值(文件不存在时跳过)
  4. 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">
当两个文件都设置了该键时,在 Source 下增加两行:
  project.yaml value: <yamlvalue>
  project.local.yaml value: <localvalue>
当
<source>
为
rigor:<level>
时,在 Source 下增加一行,让用户知道该值 可以设置以及设置方式:
  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">

Phase 4: Set-yaml Mode

第 4 阶段:设置 YAML 模式

(when
<key>=<value>
, no
--local
)
  1. Schema validation: call
    validate_enum_value <key> <value>
    . If it returns 1, the stderr line is shown to the user verbatim (
    Invalid value '<value>' for '<key>'. Allowed: ...
    ) and the skill exits without writing. If it returns 0, the value is either valid or the key has no enum constraint — proceed.
1b. Known-key check: run, from the project root as in Phase 1,
bash .claude/scripts/config-set.sh <key> <value> --dry-run
and read its
known
row. If it says
yes
, proceed. If it says
no
, nothing in CCGS reads that key, so writing it changes nothing; it is most often a typo. Do not write yet: say so, and ask via
AskUserQuestion
with the options
Use <nearest> instead
(only when the row ends
nearest: <key>
),
Write <key> anyway
,
Cancel
. Continue only on an explicit choice; with
Use <nearest> instead
, start this phase again with that key. A skipped or empty answer cancels.
  1. Reserved warning: if
    <key>
    is in
    RESERVED_SET
    (Phase 1b), print BEFORE the approval prompt:
    "Note:
    <key>
    is RESERVED — no skill or hook reads it, so setting it will not change any behaviour. Your value is stored either way."
    Then continue to the approval gate as normal. Phase 5 carries the identical warning. Warning on one write path and not the other leaves a silent route to the same outcome, which is the whole failure this guards against.
  2. Shadow warning: read
    get_yaml_key project.local.yaml <key>
    . If it returns a non-empty value, print BEFORE the approval prompt:
    "Note: this setting is currently locally overridden in
    project.local.yaml
    (value:
    <localvalue>
    ). Writing to
    project.yaml
    will not change your effective value."
  3. Approval gate: use
    AskUserQuestion
    :
    • Prompt:
      Set `<key>=<value>` in `project.yaml`?
    • Options:
      [A] Yes, write
      /
      [B] No, cancel
  4. Write using the
    Edit
    tool. The edit strategy depends on what already exists in
    project.yaml
    :
    Case A — key exists: Read
    project.yaml
    . Locate the line
    <lastsegment>: <oldvalue>
    and the line immediately above it that introduces its parent block (e.g.
    modes:
    for
    modes.review_mode
    ). Construct the
    Edit
    with
    old_string
    that includes the parent block line PLUS the leaf line, so the match is unique even if
    <lastsegment>: <oldvalue>
    appears elsewhere. Example for
    modes.review_mode=full
    :
    old_string:
      modes:
        review_mode: lean
    new_string:
      modes:
        review_mode: full
    Case B — parent block exists, leaf doesn't: Find the last line of the parent block. Use
    Edit
    to append the new leaf line at the correct indentation, using the last existing leaf as the
    old_string
    anchor.
    Case C — parent block doesn't exist: Append a new top-level block at end-of-file (deterministic — always end-of-file, never between blocks). Use
    Edit
    with the last non-empty line of the file as
    old_string
    and append the new block after it.
    Case D — file structure is unusual (commented-out keys with the same name, ambiguous nesting, malformed YAML): STOP and ask via
    AskUserQuestion
    :
    "Couldn't find a clean insertion point for
    <key>
    in
    project.yaml
    . Show me what to do."
  5. Confirm:
    Set `<key>=<value>` in `project.yaml`.
    When
    <key>
    is
    modes.rigor
    , the write also moves six other knobs, so print what they resolve to now — re-read each with
    resolve_setting
    rather than reciting the expansion table, since any of them may carry an explicit value that still wins:
    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)
(当参数为
<key>=<value>
,且不含
--local
时)
  1. Schema 验证:调用
    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
时,使用该键重新开始本阶段。跳过或提供空回答均视为取消。
  1. 保留设置警告:如果
    <key>
    位于
    RESERVED_SET
    中(第 1b 阶段), 请在审批提示之前打印:
    “注意:
    <key>
    已保留(RESERVED)——没有 skill 或 hook 读取它, 因此设置它不会改变任何行为。无论如何都会存储你的值。”
    然后照常继续执行审批关卡。第 5 阶段包含完全相同的警告。 如果只警告一条写入路径而不警告另一条,就会留下通向同一结果的静默路径, 而这正是此机制要防范的整个问题。
  2. 遮蔽警告:读取
    get_yaml_key project.local.yaml <key>
    。如果 返回非空值,请在审批提示之前打印:
    “注意:此设置目前在
    project.local.yaml
    中被本地覆盖(值:
    <localvalue>
    )。 写入
    project.yaml
    不会改变你的有效值。”
  3. 审批关卡:使用
    AskUserQuestion
    :
    • 提示:
      Set `<key>=<value>` in `project.yaml`?
    • 选项:
      [A] Yes, write
      /
      [B] No, cancel
  4. 使用
    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>
    找到清晰的插入点。请告诉我该怎么做。”
  5. 确认:
    Set `<key>=<value>` in `project.yaml`.
    当
    <key>
    为
    modes.rigor
    时
    ,此次写入还会影响另外六个选项, 因此请打印它们当前解析出的值——应重新读取每个选项,而不是照抄扩展表, 因为其中任何选项都可能具有仍然优先的显式值:
    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)

Phase 5: Set-local Mode

第 5 阶段:设置本地值模式

(when
--local <key>=<value>
)
  1. Whitelist check: call
    is_locally_overridable <key>
    .
    • If it returns 1 (locked), print one line:
      <key> cannot be locally overridden — it's a project-wide setting. Use `/settings <key>=<value>` (no `--local`) to change it for the whole team.
    • Exit without writing.
  2. Schema validation: call
    validate_enum_value <key> <value>
    . If it returns 1, show the stderr message verbatim and exit without writing.
2a. Known-key check: as in Phase 4 step 1b, with
--local
before
<key>
: if the
known
row says
no
, ask before writing, offering the
nearest:
key when the row names one.
2b. Reserved warning: if
<key>
is in
RESERVED_SET
(Phase 1b), print BEFORE the approval prompt:
"Note:
<key>
is RESERVED — no skill or hook reads it, so setting it will not change any behaviour. Your value is stored either way."
Identical to Phase 4 step 2, and deliberately so. Both write paths reach the same file-of-record for a user's intent; warning on one only would leave
--local
as the silent route.
  1. Base check: if
    project.yaml
    doesn't exist:
    "Cannot create
    project.local.yaml
    —
    project.yaml
    is missing. Run
    /start
    to create one first." Exit. (Local overrides require a base, per spec.)
  2. Approval gate: use
    AskUserQuestion
    :
    • Prompt:
      Set `<key>=<value>` in `project.local.yaml` (your personal override, gitignored)?
    • Options:
      [A] Yes, write
      /
      [B] No, cancel
  3. Write:
    • If
      project.local.yaml
      doesn't exist, use
      Write
      to create it with the new setting wrapped in its parent block(s). Minimal example for
      --local modes.automation=autonomous
      :
      yaml
      # project.local.yaml — per-developer overrides (gitignored)
      modes:
        automation: autonomous
    • If it exists, use
      Edit
      with the same Case A/B/C strategy from Phase 4 step 4.
  4. Confirm:
    Set `<key>=<value>` in `project.local.yaml` (your override).
(当参数为
--local <key>=<value>
时)
  1. 白名单检查:调用
    is_locally_overridable <key>
    。
    • 如果返回
      1
      (已锁定),打印一行:
      “
      <key>
      无法在本地覆盖——这是项目级设置。使用
      /settings <key>=<value>
      (不带
      --local
      )为整个团队更改。” 不写入任何内容并退出。
  2. Schema 验证:调用
    validate_enum_value <key> <value>
    。如果 返回
    1
    ,则将 stderr 消息原样显示,并且不写入任何内容就退出。
2a. 已知键检查:与第 4 阶段第 1b 步相同,但
--local
位于
<key>
之前: 如果
known
行的结果为
no
,则先询问用户是否仍要写入,并在该行指定
nearest:
键时提供相应选项。
2b. 保留设置警告:如果
<key>
位于
RESERVED_SET
中(第 1b 阶段), 请在审批提示之前打印:
“注意:
<key>
已保留(RESERVED)——没有 skill 或 hook 读取它, 因此设置它不会改变任何行为。无论如何都会存储你的值。”
这与第 4 阶段第 2 步完全相同,而且是有意如此。两条写入路径最终都会将 用户意图写入同一个正式配置记录;如果只警告其中一条,就会让
--local
成为静默路径。
  1. 基础文件检查:如果
    project.yaml
    不存在:
    “无法创建
    project.local.yaml
    ——缺少
    project.yaml
    。 请先运行
    /start
    创建一个。” 然后退出。(按照规范,本地覆盖必须基于项目级配置。)
  2. 审批关卡:使用
    AskUserQuestion
    :
    • 提示:
      Set `<key>=<value>` in `project.local.yaml` (your personal override, gitignored)?
    • 选项:
      [A] Yes, write
      /
      [B] No, cancel
  3. 写入:
    • 如果
      project.local.yaml
      不存在,使用
      Write
      创建它,并将新设置 包裹在相应的父级块中。为
      --local modes.automation=autonomous
      时的 最小示例:
      yaml
      # project.local.yaml — per-developer overrides (gitignored)
      modes:
        automation: autonomous
    • 如果该文件已存在,使用
      Edit
      ,并采用与第 4 阶段第 5 步相同的情况 A/B/C 策略。
  4. 确认:
    Set `<key>=<value>` in `project.local.yaml` (your override).

Phase 6: Usage

第 6 阶段:用法

When the argument doesn't match any of the four valid forms (or has empty operands), print:
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
当参数不匹配四种有效形式之一(或操作数为空)时,打印:
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

Notes

说明

  • All YAML writes go through
    Edit
    (existing keys/blocks) or
    Write
    (new files). The skill does not depend on
    yq
    or any external YAML library.
  • The whitelist is enforced in
    yaml-helper.sh
    (
    is_locally_overridable
    function). To extend it, edit the
    _yaml_helper_locally_overridable
    constant — do not duplicate the list here.
  • Derived values are not stored anywhere.
    modes.workflow
    ,
    docs.density
    ,
    qa.level
    ,
    modes.story_granularity
    ,
    modes.review_mode
    and
    team.size
    have no terminal default; when no file sets them their value comes from the
    modes.rigor
    expansion in
    yaml-helper.sh
    (
    _yaml_helper_rigor_expansion
    ). Report them via
    resolve_setting
    — a
    get_yaml_key
    read returns empty and would render them "(not set)" while every skill is in fact acting on a real value.
  • The schema-validation enum constants are in
    yaml-helper.sh
    (
    _yaml_helper_enums
    ).
    validate_enum_value
    is the single-key validator for pending writes;
    validate_yaml_enum
    validates whole files (used by
    session-start.sh
    ).
  • project.local.yaml
    is gitignored. These overrides never propagate to teammates.
  • For which value to choose for a given game type or situation — and when to suggest changing one — see
    .claude/docs/settings-guidance.md
    (advisory presets
    • change triggers).
      effects-map.md
      says what each setting does; that doc says which to pick.
  • 所有 YAML 写入都通过
    Edit
    (现有键或区块)或
    Write
    (新文件)完成。 此 skill 不依赖
    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
    读取会返回空值, 从而将其显示为“(not set)”,但实际上每个 skill 都在使用真实值。
  • Schema 验证的枚举常量位于
    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
    说明每个设置的作用;该文档则说明应选择哪个值。