Skip to content

Commit 65bac2e

Browse files
CodeCasterXcodexclaude
authored
fix(templates): 修复模板同步回退与 Claude 命令元数据丢失 (#599)
* chore(infra): capture post-update baseline - Record the runtime skill regression produced by template sync - Preserve generated command metadata changes for follow-up analysis Co-Authored-By: Codex <noreply@openai.com> * fix(sync): preserve custom command metadata - forward explicit Claude invocation metadata during custom skill sync - restore manual validation templates and structural regression coverage - keep generated commands and inline sync copies idempotent Co-Authored-By: Codex <noreply@openai.com> Co-Authored-By: Claude <noreply@anthropic.com> * fix(sync): preserve multiline skill metadata - generalize model-invocation metadata for TUI adapters - retain literal description line breaks across YAML and TOML commands - document and test generated metadata semantics Co-Authored-By: Codex <noreply@openai.com> Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Codex <noreply@openai.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 8fb592b commit 65bac2e

16 files changed

Lines changed: 220 additions & 27 deletions

File tree

.agents/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,14 @@
165165
name: enforce-style
166166
description: "在代码审查前应用团队风格规范。当需要在评审前统一团队代码风格时使用"
167167
args: "<task-id>" # 可选
168+
disable-model-invocation: true # 可选;由支持该能力的 TUI 适配器消费
168169
---
169170
```
170171

171172
`description` 采用「一句话职责 + 场景触发子句」写法:在简短职责描述后补充「当……时使用」(英文 `SKILL.en.md` 用「Use when …」),作为跨 TUI 的触发语义,供支持 Agent Skills 的工具在自然对话中自发现该 skill。**不要**为此新增 `triggers` 等额外 frontmatter 字段。
172173

174+
当生成的 TUI 命令需要禁用模型调用时,将通用可选字段 `disable-model-invocation` 设为 `true`;各 TUI 适配器按自身能力决定是否消费。多行 `description` 如需保留换行,使用 YAML literal block(`|`);同步器会为各 TUI 输出对应的多行元数据格式。
175+
173176
新增或修改自定义 skill 后,再执行一次 `update-agent-infra`。同步过程会自动检测非内置 skill,并为 Claude Code、Gemini CLI、OpenCode 生成对应命令。
174177

175178
### 共享 skill 源

.agents/skills/entropy-check/SKILL.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
---
22
name: entropy-check
3-
description: >
3+
description: |
44
周期性审查 agent-infra 仓库软熵增并输出结构化熵减审查报告。
55
当本仓库需要在发版前或维护窗口识别规则重叠、文档膨胀、死约定和版本散落风险时使用。
6+
disable-model-invocation: true
67
---
78

89
# 熵减审查

.agents/skills/update-agent-infra/scripts/sync-templates.js

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ function parseSkillFrontmatter(filePath) {
235235
if (!pair) continue;
236236

237237
const [, key, rawValue] = pair;
238-
if (rawValue === '>') {
238+
if (rawValue === '>' || rawValue === '|') {
239239
const block = [];
240240
for (let offset = index + 1; offset < lines.length; offset += 1) {
241241
const nextLine = lines[offset];
@@ -244,7 +244,7 @@ function parseSkillFrontmatter(filePath) {
244244
block.push(nextLine.trim());
245245
index = offset;
246246
}
247-
result[key] = block.join(' ').trim();
247+
result[key] = block.join(rawValue === '|' ? '\n' : ' ').trim();
248248
continue;
249249
}
250250

@@ -286,7 +286,8 @@ function detectCustomSkills(projectRoot, templateSkillNames) {
286286
dirName: entry.name,
287287
name: meta.name || entry.name,
288288
description: meta.description || '',
289-
args: meta.args || null
289+
args: meta.args || null,
290+
disableModelInvocation: meta['disable-model-invocation'] === 'true'
290291
};
291292
})
292293
.filter(Boolean)
@@ -501,14 +502,35 @@ function cleanStaleSyncedFiles(projectRoot, syncedSkills, report) {
501502
}
502503
}
503504

505+
function formatYamlMetadata(key, value) {
506+
if (!value.includes('\n')) {
507+
return [`${key}: ${JSON.stringify(value)}`];
508+
}
509+
510+
return [`${key}: |-`, ...value.split('\n').map((line) => ` ${line}`)];
511+
}
512+
513+
function formatTomlMetadata(key, value) {
514+
if (!value.includes('\n')) {
515+
return `${key} = ${JSON.stringify(value)}`;
516+
}
517+
518+
const lines = value.split('\n').map((line) => JSON.stringify(line).slice(1, -1));
519+
return `${key} = """${lines.join('\n')}"""`;
520+
}
521+
504522
function generateClaudeCommand(skill, lang) {
505523
const isZhCN = lang === 'zh-CN';
506-
const lines = ['---', `description: ${JSON.stringify(skill.description)}`];
524+
const lines = ['---', ...formatYamlMetadata('description', skill.description)];
507525

508526
if (skill.args) {
509527
lines.push(`usage: ${JSON.stringify(`/${skill.dirName} ${skill.args}`)}`);
510528
}
511529

530+
if (skill.disableModelInvocation) {
531+
lines.push('disable-model-invocation: true');
532+
}
533+
512534
lines.push('---', '');
513535
lines.push(
514536
isZhCN
@@ -539,7 +561,7 @@ function generateGeminiCommand(skill, lang) {
539561
promptLines.push(isZhCN ? '严格按照技能中定义的所有步骤执行。' : 'Follow all steps defined in the skill exactly.');
540562

541563
return [
542-
`description = ${JSON.stringify(skill.description)}`,
564+
formatTomlMetadata('description', skill.description),
543565
'prompt = """',
544566
...promptLines,
545567
'"""'
@@ -550,7 +572,7 @@ function generateOpenCodeCommand(skill, lang) {
550572
const isZhCN = lang === 'zh-CN';
551573
const lines = [
552574
'---',
553-
`description: ${JSON.stringify(skill.description)}`,
575+
...formatYamlMetadata('description', skill.description),
554576
'agent: general',
555577
'subtask: false',
556578
'---',

.claude/commands/entropy-check.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
---
2-
description: "审查项目软熵增并输出熵减审查报告"
2+
description: |-
3+
周期性审查 agent-infra 仓库软熵增并输出结构化熵减审查报告。
4+
当本仓库需要在发版前或维护窗口识别规则重叠、文档膨胀、死约定和版本散落风险时使用。
35
disable-model-invocation: true
46
---
57

.gemini/commands/agent-infra/entropy-check.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
description = "审查项目软熵增并输出熵减审查报告"
1+
description = """周期性审查 agent-infra 仓库软熵增并输出结构化熵减审查报告。
2+
当本仓库需要在发版前或维护窗口识别规则重叠、文档膨胀、死约定和版本散落风险时使用。"""
23
prompt = """
34
读取并执行 `.agents/skills/entropy-check/SKILL.md` 中的 entropy-check 技能。
45

.opencode/commands/entropy-check.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
---
2-
description: "审查项目软熵增并输出熵减审查报告"
2+
description: |-
3+
周期性审查 agent-infra 仓库软熵增并输出结构化熵减审查报告。
4+
当本仓库需要在发版前或维护窗口识别规则重叠、文档膨胀、死约定和版本散落风险时使用。
35
agent: general
46
subtask: false
57
---

docs/en/custom-skills.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,16 @@ Minimum frontmatter:
2323
name: enforce-style
2424
description: "Apply team style checks before submitting code"
2525
args: "<task-id>" # optional
26+
disable-model-invocation: true # optional; consumed by supporting TUI adapters
2627
---
2728
```
2829

2930
- `name`: user-facing skill name
3031
- `description`: used when generating editor command metadata
3132
- `args`: optional argument hint; agent-infra uses it when generating slash commands for supported AI TUIs
33+
- `disable-model-invocation`: optional generic metadata; each TUI adapter consumes it according to its capabilities
34+
35+
Use a YAML literal block (`|`) when a multiline `description` should retain its line breaks. Sync emits the corresponding multiline metadata format for each TUI.
3236

3337
After adding the skill, run `update-agent-infra` again:
3438

docs/zh-CN/custom-skills.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,16 @@
2323
name: enforce-style
2424
description: "在提交代码前执行团队风格检查"
2525
args: "<task-id>" # 可选
26+
disable-model-invocation: true # 可选;由支持该能力的 TUI 适配器消费
2627
---
2728
```
2829

2930
- `name`:对用户可见的 skill 名称
3031
- `description`:用于生成编辑器命令元数据
3132
- `args`:可选参数提示;agent-infra 会在生成支持的 AI TUI 命令时使用它
33+
- `disable-model-invocation`:可选通用元数据;各 TUI 适配器按自身能力决定是否消费
34+
35+
多行 `description` 如需保留换行,使用 YAML literal block(`|`);同步器会为各 TUI 输出对应的多行元数据格式。
3236

3337
添加 skill 后,再执行一次 `update-agent-infra`
3438

src/sync-templates.js

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ function parseSkillFrontmatter(filePath) {
171171
if (!pair) continue;
172172

173173
const [, key, rawValue] = pair;
174-
if (rawValue === '>') {
174+
if (rawValue === '>' || rawValue === '|') {
175175
const block = [];
176176
for (let offset = index + 1; offset < lines.length; offset += 1) {
177177
const nextLine = lines[offset];
@@ -180,7 +180,7 @@ function parseSkillFrontmatter(filePath) {
180180
block.push(nextLine.trim());
181181
index = offset;
182182
}
183-
result[key] = block.join(' ').trim();
183+
result[key] = block.join(rawValue === '|' ? '\n' : ' ').trim();
184184
continue;
185185
}
186186

@@ -222,7 +222,8 @@ function detectCustomSkills(projectRoot, templateSkillNames) {
222222
dirName: entry.name,
223223
name: meta.name || entry.name,
224224
description: meta.description || '',
225-
args: meta.args || null
225+
args: meta.args || null,
226+
disableModelInvocation: meta['disable-model-invocation'] === 'true'
226227
};
227228
})
228229
.filter(Boolean)
@@ -437,14 +438,35 @@ function cleanStaleSyncedFiles(projectRoot, syncedSkills, report) {
437438
}
438439
}
439440

441+
function formatYamlMetadata(key, value) {
442+
if (!value.includes('\n')) {
443+
return [`${key}: ${JSON.stringify(value)}`];
444+
}
445+
446+
return [`${key}: |-`, ...value.split('\n').map((line) => ` ${line}`)];
447+
}
448+
449+
function formatTomlMetadata(key, value) {
450+
if (!value.includes('\n')) {
451+
return `${key} = ${JSON.stringify(value)}`;
452+
}
453+
454+
const lines = value.split('\n').map((line) => JSON.stringify(line).slice(1, -1));
455+
return `${key} = """${lines.join('\n')}"""`;
456+
}
457+
440458
function generateClaudeCommand(skill, lang) {
441459
const isZhCN = lang === 'zh-CN';
442-
const lines = ['---', `description: ${JSON.stringify(skill.description)}`];
460+
const lines = ['---', ...formatYamlMetadata('description', skill.description)];
443461

444462
if (skill.args) {
445463
lines.push(`usage: ${JSON.stringify(`/${skill.dirName} ${skill.args}`)}`);
446464
}
447465

466+
if (skill.disableModelInvocation) {
467+
lines.push('disable-model-invocation: true');
468+
}
469+
448470
lines.push('---', '');
449471
lines.push(
450472
isZhCN
@@ -475,7 +497,7 @@ function generateGeminiCommand(skill, lang) {
475497
promptLines.push(isZhCN ? '严格按照技能中定义的所有步骤执行。' : 'Follow all steps defined in the skill exactly.');
476498

477499
return [
478-
`description = ${JSON.stringify(skill.description)}`,
500+
formatTomlMetadata('description', skill.description),
479501
'prompt = """',
480502
...promptLines,
481503
'"""'
@@ -486,7 +508,7 @@ function generateOpenCodeCommand(skill, lang) {
486508
const isZhCN = lang === 'zh-CN';
487509
const lines = [
488510
'---',
489-
`description: ${JSON.stringify(skill.description)}`,
511+
...formatYamlMetadata('description', skill.description),
490512
'agent: general',
491513
'subtask: false',
492514
'---',

templates/.agents/README.en.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,14 @@ Recommended frontmatter:
165165
name: enforce-style
166166
description: "Apply the team style guide before code review. Use when you need to align the team's code style before review"
167167
args: "<task-id>" # optional
168+
disable-model-invocation: true # optional; consumed by supporting TUI adapters
168169
---
169170
```
170171

171172
Write `description` as "one-line responsibility + scenario clause": after the short responsibility, append "Use when …" (the Chinese `SKILL.zh-CN.md` variant uses "当……时使用") as the cross-TUI trigger semantics, so Agent Skills-capable tools can self-discover the skill in natural conversation. Do **not** add a separate `triggers` (or similar) frontmatter field for this.
172173

174+
Set the generic optional `disable-model-invocation` field to `true` when generated TUI commands should disable model invocation; each TUI adapter consumes it according to its capabilities. To preserve line breaks in a multiline `description`, use a YAML literal block (`|`); sync emits the corresponding multiline metadata format for each TUI.
175+
173176
After adding or updating a custom skill, run `update-agent-infra` again. The sync step detects non-built-in skills and generates matching commands for Claude Code, Gemini CLI, and OpenCode automatically.
174177

175178
### Shared skill sources

0 commit comments

Comments
 (0)