compact-bensz-skills
当用户明确要求“压缩/瘦身/精简某个 Agent Skill 的 Markdown 文档”“在不改变功能前提下降低 skill 上下文开销”时使用。先理解目标 skill 的真实能力与安全边界,再在忽略 `tests/`、`plans/` 以及目标 skill 的 `README.md`、`CHANGELOG.md` 的前提下,压缩 `SKILL.md`、`references/*.md` 等工作型 Markdown,并把中间产物隔离到 `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skill
#agent-skills
Install
npx skills add https://github.com/huangwb8/skills/tree/main/skills/alpha/compact-bensz-skills
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install huangwb8-skills@llmmart
git clone https://github.com/huangwb8/skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole huangwb8/skills collection as a plugin from our marketplace. Git is the plain clone.
README
compact-bensz-skills
compact-bensz-skills 用来压缩某个 Agent Skill 里的工作型 Markdown 文档,目标是在不改变原有功能的前提下,显著降低上下文体积。
最推荐用法
请使用 compact-bensz-skills skill 压缩这个 Agent Skill 的工作型 Markdown 文档。
输入:/path/to/target-skill
输出:更新后的 skill 源文件;所有中间文件保存在目标目录下的 .bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/
适用场景
- 目标 skill 的
SKILL.md、references/*.md明显冗长 - 你希望节省上下文,但不想动脚本逻辑
- 你需要先理解 skill,再做保守压缩
默认行为
- 工作区根:
<skill_root>/.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/ - 每次运行目录:
<skill_root>/.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/ - 最近一次运行指针:
<skill_root>/.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/latest-run.txt - 测试区:
<skill_root>/tests/compact-bensz-skills/ - 自动忽略:
tests/、plans/、目标 skill 根目录下的README.md、CHANGELOG.md - 优先保留:frontmatter、输入输出契约、安全边界、命令与路径
- 如果你显式指定外部
workspace_dir,脚本会接受,但验证报告会提示“中间文件已离开 skill 根目录”
常用示例
示例 1:压缩单个 skill
请使用 compact-bensz-skills skill 压缩 `git-pr-review` 这个 skill 的工作型 Markdown 文档。
输入:/workspace/skills/git-pr-review
输出:原 skill 目录内更新后的 Markdown;中间文件放到 `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/`
示例 2:指定额外约束
请使用 compact-bensz-skills skill 压缩这个 skill 的工作型 Markdown。
输入:/workspace/skills/my-skill
输出:更新后的 skill 源文件
另外,还有下列参数约束:
- 只压缩 Markdown,不改 Python/Bash 脚本
- 默认不要动 README.md / CHANGELOG.md
- 不要改变 SKILL.md frontmatter 的 name
- 压缩完成后要输出压缩前后统计
运行时会生成什么
.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/latest-run.txt.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/analysis/file-inventory.json.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/analysis/compaction-plan.md.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/reports/size-before.json.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/reports/size-after.json.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/reports/size-delta.md.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/reports/validation.json
备选用法(脚本)
python3 compact-bensz-skills/scripts/init_workspace.py --skill-root /path/to/target-skill
python3 compact-bensz-skills/scripts/measure_markdown.py --skill-root /path/to/target-skill --phase after
python3 compact-bensz-skills/scripts/validate_compaction.py --skill-root /path/to/target-skill
如果你想显式锁定某一轮:
python3 compact-bensz-skills/scripts/init_workspace.py --skill-root /path/to/target-skill
# 记下输出里的 run_id
python3 compact-bensz-skills/scripts/measure_markdown.py --skill-root /path/to/target-skill --run-id 2026-03-28-15-52 --phase after
python3 compact-bensz-skills/scripts/validate_compaction.py --skill-root /path/to/target-skill --run-id 2026-03-28-15-52
WHICHMODEL
最后核对:2026-03-28。
- OpenAI 路线:
- 复杂 skill 压缩、跨多份
references/去重、需要保守保留约束时,优先gpt-5.4;OpenAI 官方把它列为复杂推理、coding 和 agentic workflows 的起点。 - 如果你主要在 Codex 里工作,且任务是“读文档 + 改文档 + 跑脚本验证”的 agentic coding 流程,可优先
gpt-5-codex;官方说明它是面向 Codex 一类环境优化的 agentic coding 模型。 - 只做轻量统计、跑 helper scripts、整理测试记录时,可降到
gpt-5.4-mini或同级快模型。
- 复杂 skill 压缩、跨多份
- Anthropic 路线:
- 最复杂的压缩任务优先
Opus;Anthropic 官方建议复杂任务先从 Opus 开始。 - 日常 skill 压缩、文档改写和一般验证优先
Sonnet;官方将其定位为速度与智能的最佳平衡,并在 Claude Code 中作为日常 coding 默认推荐档位。 - 只做简单检查或批量轻任务时可用
Haiku。
- 最复杂的压缩任务优先
- 长上下文优先级:
- 如果目标 skill 很大,优先选支持更长上下文的模型。OpenAI 当前前沿模型页给
gpt-5.4标注了 1M context;Anthropic 也为 Sonnet/Opus 提供了 1M context 选项或长会话模式。
- 如果目标 skill 很大,优先选支持更长上下文的模型。OpenAI 当前前沿模型页给
参考:
- OpenAI Models: https://developers.openai.com/api/docs/models
- OpenAI GPT-5-Codex: https://developers.openai.com/api/docs/models/gpt-5-codex
- Anthropic Models Overview: https://platform.claude.com/docs/en/about-claude/models/overview
FAQ
它会改 tests/ 和 plans/ 吗?
不会。这个 skill 默认忽略目标 skill 里的 tests/ 和 plans/。
它会处理 README.md 或 CHANGELOG.md 吗?
默认不会。这个 skill 只把工作型 Markdown 视为主要目标;目标 skill 根目录下的 README.md、CHANGELOG.md 一般属于面向人类的说明或发布记录,不纳入默认压缩范围。
它会自动改脚本代码吗?
默认不会。它的核心目标是压缩 Markdown 文档;只有当文档与脚本明显不一致时,才应先指出风险,再做最小修正。
它怎么保证不破坏功能?
它要求先理解 SKILL.md、config.yaml、scripts/,然后再压缩文档,并在最后运行统计和校验脚本。
为什么现在要用 `
因为这个 skill 可能会反复执行。按分钟级 run 目录隔离后,每一轮的快照、统计和验证结果都能独立追溯,不会被下一轮覆盖;同一分钟重复运行时脚本会自动追加后缀。
它会检查相对链接是否越界吗?
会。validate_compaction.py 现在不仅检查链接是否存在,也会拒绝链接跳出目标 skill 根目录。
Skill manifest
compact-bensz-skills
目标
当用户明确要求“压缩/瘦身/精简某个 Agent Skill 的 Markdown 文档”“在不改变功能前提下降低 skill 上下文开销”时使用。先理解目标 skill 的真实能力与安全边界,再在忽略 tests/、plans/ 以及目标 skill 的 README.md、CHANGELOG.md 的前提下,压缩 SKILL.md、references/*.md 等工作型 Markdown,并把中间产物隔离到 .bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/。⚠️ 不适用:用户主要想新增功能、修复脚本逻辑、批量改代码、或只想压缩非 skill 文档。
流程
输入
输入
skill_root(必需)- 目标 Agent Skill 根目录
workspace_dir(可选)- 默认把本轮工作区建在
<skill_root>/.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/ - 如果用户显式指定其它目录,则把它视为“run 容器根目录”,本轮仍落到其中的
{yyyy-mm-dd-hh-mm}/
- 默认把本轮工作区建在
run_id(可选)- 用于在
init -> measure -> validate间显式复用同一轮工作区
- 用于在
test_dir(可选)- 默认
<skill_root>/tests/compact-bensz-skills/
- 默认
执行步骤
核心原则
- 先理解,再压缩:先读
SKILL.md、config.yaml、scripts/和必要的references/,再判断哪些 Markdown 可以缩短。 - 不改行为,只改表达:压缩的是文档体积,不是功能边界;不得擅自新增、删除或扭曲目标 skill 的能力。
- 保护触发语义:
SKILL.mdfrontmatter 的name必须保持不变;description只能等价压缩,不能丢失关键触发条件。 - 保护硬约束:输入、输出、默认路径、安全限制、必跑脚本、失败条件、与其它 skill 的协作约定都必须保留。
- 只动源工作文件:忽略目标 skill 的
tests/、plans/及其内容,不把它们视为待压缩源文件。 - 默认不动说明文档:目标 skill 根目录下的
README.md、CHANGELOG.md一般属于面向人类的说明或发布记录,不视为默认压缩目标。 - 中间文件隔离:分析、快照、统计、验证结果都写到隐藏目录
.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/;除非用户另有指定,不向外泄露中间文件。 - 按轮次隔离:每次运行都应在
.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/内工作,避免多次压缩会话互相覆盖。 - 链接不能越界:压缩后保留的本地 Markdown 链接必须仍位于目标 skill 根目录内,不能借相对路径跳到 skill 外部。
标准工作流
1. 初始化隐藏工作区
优先使用确定性脚本创建工作区、快照和 Markdown 清单:
python3 compact-bensz-skills/scripts/init_workspace.py --skill-root /path/to/target-skill
脚本会:
- 创建
<skill_root>/.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/ - 在隐藏根目录写入
latest-run.txt - 扫描待压缩 Markdown(忽略
tests/、plans/、README.md、CHANGELOG.md) - 生成
analysis/file-inventory.json - 备份原文到
snapshots/before/ - 生成
analysis/compaction-plan.md - 记录压缩前统计到
reports/size-before.json
2. 理解目标 skill 的真实功能
最低阅读范围:
SKILL.mdconfig.yaml(如存在)scripts/(如存在)references/中与核心流程直接相关的文档- 仅当
README.md、CHANGELOG.md与核心行为边界强相关时才辅助阅读;默认不把它们纳入压缩目标
理解时重点确认:
- 技能触发条件与不适用范围
- 输入输出契约
- 默认工作区 / 测试区 / 中间文件路径
- 安全边界与只读/只写限制
- 任何“必须执行”“不得省略”的步骤
3. 执行 Markdown 压缩
优先顺序:
- 删重复:移除跨文件、跨章节重复解释
- 缩长句:把啰嗦描述改成短句、表格或清单
- 主从分离:
SKILL.md只保留触发逻辑、主流程、硬约束;细节下沉到references/ - 压示例:保留最小可用命令和最关键示例,删除低价值变体
默认优先处理:
SKILL.mdreferences/中真正承载执行细则的 Markdown
默认不处理:
- 目标 skill 根目录下的
README.md - 目标 skill 根目录下的
CHANGELOG.md
压缩时必须保留:
SKILL.mdfrontmatter 与关键词可发现性- 关键命令、路径、文件名、配置键
- 输入/输出、默认目录、安全限制
- 会改变行为的条件分支
- 与
bensz-collect-bugs等跨 skill 约定
压缩时禁止:
- 把“必需”改成“可选”
- 删除失败条件、边界条件、路径约束
- 删除唯一的命令示例或唯一的输出说明
- 只为了省字而制造歧义
4. 复测压缩收益
完成文档修改后重新统计:
python3 compact-bensz-skills/scripts/measure_markdown.py --skill-root /path/to/target-skill --phase after
如需显式复用某一轮:
python3 compact-bensz-skills/scripts/measure_markdown.py \
--skill-root /path/to/target-skill \
--run-id 2026-03-28-15-52 \
--phase after
该脚本会输出:
reports/size-after.jsonreports/size-delta.md
5. 校验压缩后仍可用
python3 compact-bensz-skills/scripts/validate_compaction.py --skill-root /path/to/target-skill
默认会复用 latest-run.txt 指向的最近一轮;如果你在多个 run 之间切换,显式传 --run-id 更稳妥。
至少检查:
SKILL.mdfrontmatter 是否完整metadata.author是否保留metadata.keywords是否仍包含 skill 名- 本地 Markdown 相对链接是否仍有效且没有越出目标 skill 根目录
- 压缩后的总字数是否低于压缩前
- 若
description有改动,确认只是等价压缩而非改坏触发语义
何时读取参考文档
- 需要决定“哪些内容必须保留”时,读
references/preservation-checklist.md - 需要具体压缩手法时,读
references/compaction-playbook.md - 做收尾校验时,读
references/validation-checklist.md
输出
输出
对用户的主要交付:
- 更新后的目标 skill 工作型 Markdown 源文件
隐藏工作区产物:
.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/latest-run.txt.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/analysis/file-inventory.json.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/analysis/compaction-plan.md.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/reports/size-before.json.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/reports/size-after.json.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/reports/size-delta.md.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/reports/validation.json
输出管理
BenszAPI 任务工作区
校验
至少检查 frontmatter、metadata.author、技能关键词、相对链接、关键命令/路径/失败条件和触发语义均保留;压缩后的 Markdown 总量应低于压缩前,且 validate_compaction.py 与引用检查通过。不能证明语义等价时不得交付压缩版本。
失败与恢复
快照、统计、链接或语义校验失败时保留 snapshots/before/、报告和错误日志,停止写回正式 Skill;可在同一 run 目录修正后重试,若收益或保真无法满足阈值则恢复基线并报告原因。
约束
公共硬约束
本块由 docs/templates/skill-common-constraints.md 统一维护;每个 SKILL.md 的 ## 约束 必须逐字同步本块,不得在副本中改写公共规则。
- 任务需要落盘时,使用唯一的
./.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/根目录;共享材料放入shared/,Skill 专属材料放入该 Skill 的input/、output/、log/。 - 正式交付物、源代码和正式计划按项目约定保存,不写入任务工作区;未经授权不覆盖、删除、迁移或远程写入。
- 项目维护变更检查 BAC 可用性并记录需求、AI 产出、工具结果、文件改动和验证摘要;BAC 只做过程审计,不替代署名、责任或合规判断。
- 不记录 API Key、访问令牌、密码、Cookie、环境/凭据文件、私有 Prompt、身份信息、本地用户名、主机名或不必要的大体积原始数据。
- 文件路径必须规范化并限制在授权项目范围内;外部 URL、子进程和网络访问遵循最小权限,防止路径遍历、SSRF 和命令注入。
- Skill 版本唯一记录在自身
config.yaml:skill_info.version;公开 API、协议、目录或配置变更同步文档与CHANGELOG.md。 bensz-collect-bugs是一个 Agent Skill;仅将 Bensz Agent Skill 或 Bensz 基础设施本身的设计缺陷交给它。先脱敏写入~/.bensz-skills/bugs/,当前任务不中断,只有用户明确要求才公开上报,禁止直接修改用户已安装的 Skill 源码。
Files (skills)
-
references
-
compaction-playbook.md 1.7 KB
# 压缩操作手册 ## 目标 把一个 Agent Skill 的工作型 Markdown 文档压短,但不破坏: - 触发语义 - 输入输出契约 - 安全边界 - 默认路径与关键命令 ## 推荐顺序 1. 先压 `SKILL.md` 2. 再压 `references/` 中真正会被频繁读取的文档 3. 默认不要把目标 skill 根目录下的 `README.md`、`CHANGELOG.md` 当成压缩目标 ## 常用压缩手法 ### 1. 删除重复解释 - 同一条规则只保留在最合适的一处 - `SKILL.md` 里保留摘要,细节放 `references/` - 面向人类的说明文档默认不纳入压缩范围;若只为省字而改它们,往往是在压错对象 ### 2. 缩短句子 - 一段只表达一个决策 - 把“原因 + 例外 + 重复铺垫”改成短句 - 长段落优先改为 3-5 条扁平 bullet ### 3. 合并低价值示例 - 保留最小可用示例 - 删除只是在换措辞、没有新增约束的信息 - 多个相近命令只保留主路径,其他写成“按需替换参数” ### 4. 主从分离 - `SKILL.md`:触发条件、主流程、硬约束、关键命令 - `references/`:详细检查清单、延伸案例、设计哲学 ### 5. 把软建议和硬约束分开 - 硬约束必须留在主文档 - 经验性建议可以下沉到 `references/` ## 常见误伤 - 删除唯一的默认路径说明 - 把“不适用”删掉,导致 skill 误触发 - 把“必须执行”缩成“建议执行” - 把输出文件名删掉,导致无法验收 ## 简单判断标准 如果删掉一句话后,会让以下任一问题变得更难回答,就不要删: - 什么时候触发这个 skill? - 输入是什么? - 输出是什么? - 中间文件去哪? - 哪些事不能做? -
preservation-checklist.md 891 B
# 保留清单 压缩前后,至少逐项确认以下信息没有丢失。 ## SKILL.md frontmatter - `name` - `description` - `metadata.author` - `metadata.keywords` - `metadata.keywords` 仍包含 skill 名 ## 触发与边界 - 什么时候应该用这个 skill - 什么时候不该用 - 用户最小输入是什么 - 最终输出是什么 ## 行为约束 - 默认工作区 / 隐藏目录 - 测试区或测试约束 - 只读/只写边界 - 必须执行的步骤 - 失败时的处理方式 ## 关键实现信息 - 唯一或主要命令示例 - 关键脚本路径 - 配置文件路径或关键配置键 - 会影响行为的文件命名规则 - 默认待压缩范围是否仍限定在工作型 Markdown,而非 `README.md` / `CHANGELOG.md` ## 跨 skill 约定 - 与 `bensz-collect-bugs` 的协作约定 - 与 `parallel-vibe`、`auto-test-skill` 等强依赖 skill 的衔接方式 -
validation-checklist.md 902 B
# 校验清单 ## 结构校验 - `SKILL.md` 仍然存在 - frontmatter 合法 - 关键 Markdown 链接未失效,且没有跳出目标 skill 根目录 - `checked_files` 没有把目标 skill 根目录下的 `README.md`、`CHANGELOG.md` 当成默认压缩对象 - 中间文件都在 `.compact-bensz-skills/run-{timestamp}/` - `latest-run.txt` 指向的是本次验证对应的 run ## 语义校验 - 触发条件没有变窄或变偏 - 不适用范围仍然存在 - 安全限制仍然清楚 - 输入/输出/默认目录仍然可回答 ## 收益校验 - 总字数下降 - 最大文件的字数明显下降,或有合理解释 - `SKILL.md` 比修改前更短、更聚焦 ## 风险提示 如果出现以下情况,应停止并回看快照: - frontmatter 被改坏 - 关键命令消失 - 输出文件名或路径说明消失 - 只读/只写限制被删 - 关键链接改成了越界相对路径
-
-
scripts
-
common.py 9.9 KB
from __future__ import annotations import fnmatch import json import re import shutil from datetime import datetime from pathlib import Path from typing import Any try: import yaml except ModuleNotFoundError as exc: # pragma: no cover - import guard raise SystemExit( "缺少 PyYAML 依赖,无法读取 compact-bensz-skills/config.yaml。请先安装 pyyaml。" ) from exc FRONTMATTER_RE = re.compile(r"\A---\n(.*?)\n---\n?", re.DOTALL) WORD_RE = re.compile(r"[A-Za-z0-9_]+|[\u4e00-\u9fff]") HEADING_RE = re.compile(r"^#{1,6}\s", re.MULTILINE) FENCE_RE = re.compile(r"^```", re.MULTILINE) LOCAL_LINK_RE = re.compile(r"\[[^\]]+\]\(([^)]+)\)") def skill_dir() -> Path: return Path(__file__).resolve().parent.parent def load_config() -> dict[str, Any]: config_path = skill_dir() / "config.yaml" return yaml.safe_load(config_path.read_text(encoding="utf-8")) def resolve_skill_root(skill_root: str) -> Path: path = Path(skill_root).expanduser().resolve() if not path.is_dir(): raise SystemExit(f"skill_root 不存在或不是目录: {path}") if not (path / "SKILL.md").exists(): raise SystemExit(f"目标目录缺少 SKILL.md: {path}") return path def resolve_workspace_root( target_skill_root: Path, config: dict[str, Any], workspace_dir: str | None = None, run_id: str | None = None, create: bool = False, ) -> Path: workspace_base, explicit_run_id = resolve_workspace_base(target_skill_root, config, workspace_dir) effective_run_id = run_id or explicit_run_id if effective_run_id is None: if create: effective_run_id = allocate_unique_run_id(workspace_base, config, generate_run_id(config)) else: effective_run_id = read_latest_run_id(workspace_base, config) if effective_run_id is None: raise SystemExit( "未找到可复用的 run 目录。请先运行 init_workspace.py,或显式传入 --run-id。" ) return workspace_base / effective_run_id def resolve_workspace_base( target_skill_root: Path, config: dict[str, Any], workspace_dir: str | None = None, ) -> tuple[Path, str | None]: if workspace_dir: candidate = Path(workspace_dir).expanduser().resolve() if is_run_dir_name(candidate.name, config): return candidate.parent, candidate.name return candidate, None return target_skill_root / config["workspace"]["hidden_dir"], None def generate_run_id(config: dict[str, Any]) -> str: prefix = config["workspace"]["run_prefix"] timestamp_format = config["workspace"]["timestamp_format"] return f"{prefix}{datetime.now().strftime(timestamp_format)}" def is_run_dir_name(name: str, config: dict[str, Any]) -> bool: prefix = config["workspace"]["run_prefix"] timestamp_format = config["workspace"]["timestamp_format"] sample = datetime(2000, 1, 2, 3, 4, 5).strftime(timestamp_format) digit_pattern = "".join(r"\d" if ch.isdigit() else re.escape(ch) for ch in sample) pattern = rf"^{re.escape(prefix)}{digit_pattern}(?:-\d{{2}})?$" return re.match(pattern, name) is not None def allocate_unique_run_id(workspace_base: Path, config: dict[str, Any], base_run_id: str) -> str: if not (workspace_base / base_run_id).exists(): return base_run_id for idx in range(2, 100): candidate = f"{base_run_id}-{idx:02d}" if not (workspace_base / candidate).exists(): return candidate raise SystemExit(f"无法在 {workspace_base} 下分配唯一 run 目录: {base_run_id}") def latest_run_pointer_path(workspace_base: Path, config: dict[str, Any]) -> Path: return workspace_base / config["workspace"]["latest_run_pointer"] def write_latest_run_id(workspace_base: Path, config: dict[str, Any], run_id: str) -> None: pointer = latest_run_pointer_path(workspace_base, config) pointer.parent.mkdir(parents=True, exist_ok=True) pointer.write_text(run_id + "\n", encoding="utf-8") def read_latest_run_id(workspace_base: Path, config: dict[str, Any]) -> str | None: pointer = latest_run_pointer_path(workspace_base, config) if not pointer.exists(): return None run_id = pointer.read_text(encoding="utf-8").strip() return run_id or None def path_within(base_dir: Path, target: Path) -> bool: try: target.resolve().relative_to(base_dir.resolve()) return True except ValueError: return False def workspace_subpaths(config: dict[str, Any], workspace_root: Path) -> dict[str, Path]: subpaths = {name: workspace_root / name for name in config["workspace"]["subdirs"]} subpaths["workspace_root"] = workspace_root subpaths["workspace_base"] = workspace_root.parent subpaths["before_snapshot_dir"] = workspace_root / config["workspace"]["before_snapshot_dir"] for key, relative_path in config["reports"].items(): subpaths[key] = workspace_root / relative_path return subpaths def ensure_workspace(config: dict[str, Any], workspace_root: Path) -> dict[str, Path]: paths = workspace_subpaths(config, workspace_root) for path in paths.values(): if path == paths["workspace_base"]: path.mkdir(parents=True, exist_ok=True) continue if path.suffix: path.parent.mkdir(parents=True, exist_ok=True) else: path.mkdir(parents=True, exist_ok=True) return paths def should_ignore_path(relative_path: Path, config: dict[str, Any]) -> bool: ignore_dirs = set(config["source_files"]["ignore_dirs"]) if any(part in ignore_dirs for part in relative_path.parts[:-1]): return True ignore_file_names = set(config["source_files"].get("ignore_file_names", [])) if relative_path.name in ignore_file_names: return True ignore_globs = config["source_files"]["ignore_globs"] return any(fnmatch.fnmatch(relative_path.name, pattern) for pattern in ignore_globs) def iter_markdown_files( target_skill_root: Path, config: dict[str, Any], workspace_root: Path, ) -> list[Path]: markdown_exts = set(config["source_files"]["markdown_extensions"]) files: list[Path] = [] for path in sorted(target_skill_root.rglob("*")): if not path.is_file(): continue if path.suffix.lower() not in markdown_exts: continue try: relative_path = path.relative_to(target_skill_root) except ValueError: continue if workspace_root in path.parents or path == workspace_root: continue if should_ignore_path(relative_path, config): continue files.append(path) return files def read_text(path: Path) -> str: return path.read_text(encoding="utf-8") def compute_text_stats(text: str) -> dict[str, int]: return { "chars": len(text), "words": len(WORD_RE.findall(text)), "lines": text.count("\n") + (0 if not text else 1), "headings": len(HEADING_RE.findall(text)), "fence_markers": len(FENCE_RE.findall(text)), } def compute_file_record(target_skill_root: Path, path: Path) -> dict[str, Any]: text = read_text(path) record = {"path": str(path.relative_to(target_skill_root))} record.update(compute_text_stats(text)) return record def build_totals(records: list[dict[str, Any]], phase: str) -> dict[str, Any]: return { "phase": phase, "file_count": len(records), "total_chars": sum(int(record["chars"]) for record in records), "total_words": sum(int(record["words"]) for record in records), "total_lines": sum(int(record["lines"]) for record in records), "total_headings": sum(int(record["headings"]) for record in records), "total_fence_markers": sum(int(record["fence_markers"]) for record in records), "files": records, } def write_json(path: Path, payload: dict[str, Any] | list[Any]) -> None: path.parent.mkdir(parents=True, exist_ok=True) path.write_text( json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=False) + "\n", encoding="utf-8", ) def write_text(path: Path, text: str) -> None: path.parent.mkdir(parents=True, exist_ok=True) path.write_text(text, encoding="utf-8") def snapshot_files(target_skill_root: Path, files: list[Path], snapshot_root: Path) -> None: if snapshot_root.exists(): shutil.rmtree(snapshot_root) for file_path in files: relative_path = file_path.relative_to(target_skill_root) destination = snapshot_root / relative_path destination.parent.mkdir(parents=True, exist_ok=True) shutil.copy2(file_path, destination) def parse_frontmatter(text: str) -> tuple[dict[str, Any], str]: match = FRONTMATTER_RE.match(text) if not match: return {}, text data = yaml.safe_load(match.group(1)) or {} body = text[match.end() :] return data, body def nested_get(data: dict[str, Any], dotted_key: str) -> Any: value: Any = data for part in dotted_key.split("."): if not isinstance(value, dict) or part not in value: return None value = value[part] return value def normalize_link_target(target: str) -> str | None: cleaned = target.strip() if not cleaned or cleaned.startswith("#"): return None if "://" in cleaned or cleaned.startswith("mailto:"): return None return cleaned.split("#", 1)[0] def find_local_link_issues( base_file: Path, text: str, target_skill_root: Path, ) -> list[str]: issues: list[str] = [] for raw_target in LOCAL_LINK_RE.findall(text): normalized = normalize_link_target(raw_target) if not normalized: continue resolved = (base_file.parent / normalized).resolve() if not path_within(target_skill_root, resolved): issues.append(f"本地链接越出 skill 根目录 -> {raw_target}") continue if not resolved.exists(): issues.append(f"本地链接不存在 -> {raw_target}") return issues -
init_workspace.py 3.8 KB
#!/usr/bin/env python3 from __future__ import annotations import argparse from common import ( build_totals, compute_file_record, ensure_workspace, iter_markdown_files, load_config, path_within, resolve_skill_root, resolve_workspace_root, snapshot_files, write_latest_run_id, write_json, write_text, ) def build_plan(records: list[dict[str, object]]) -> str: top_records = sorted(records, key=lambda item: item["words"], reverse=True)[:5] lines = [ "# 压缩计划", "", "## 当前判断", "", "- 先阅读 `SKILL.md`、`config.yaml` 与关键脚本,再决定哪些文档可以缩短。", "- `tests/`、`plans/`、`README.md`、`CHANGELOG.md` 已被排除,不作为默认待压缩源文件。", "- 优先压缩字数最高、重复解释最多的 Markdown 文件。", "", "## 优先处理文件", "", ] if not top_records: lines.append("- 暂无待处理 Markdown 文件") else: for record in top_records: lines.append(f"- `{record['path']}`:约 {record['words']} 词,{record['chars']} 字符") lines.extend( [ "", "## 必保留信息", "", "- frontmatter 中的 `name`、`description`、`metadata.author`", "- 关键命令、关键路径、默认输出", "- 安全限制与不适用范围", "", "## 完成后必做", "", "- 重新运行 `measure_markdown.py --phase after`", "- 运行 `validate_compaction.py`", ] ) return "\n".join(lines) + "\n" def main() -> None: parser = argparse.ArgumentParser(description="初始化 compact-bensz-skills 隐藏工作区") parser.add_argument("--skill-root", required=True, help="目标 skill 根目录") parser.add_argument("--workspace-dir", help="自定义隐藏工作区") parser.add_argument("--run-id", help="显式指定 run 目录名,例如 run-20260328194500") args = parser.parse_args() config = load_config() target_skill_root = resolve_skill_root(args.skill_root) workspace_root = resolve_workspace_root( target_skill_root, config, args.workspace_dir, run_id=args.run_id, create=True, ) paths = ensure_workspace(config, workspace_root) write_latest_run_id(paths["workspace_base"], config, workspace_root.name) markdown_files = iter_markdown_files(target_skill_root, config, workspace_root) snapshot_files(target_skill_root, markdown_files, paths["before_snapshot_dir"]) records = [compute_file_record(target_skill_root, path) for path in markdown_files] inventory = { "skill_root": str(target_skill_root), "workspace_base": str(paths["workspace_base"]), "workspace_root": str(workspace_root), "run_id": workspace_root.name, "workspace_inside_skill_root": path_within(target_skill_root, workspace_root), "file_count": len(records), "files": records, } totals = build_totals(records, phase="before") write_json(paths["inventory_json"], inventory) write_json(paths["size_before_json"], totals) write_text(paths["plan_markdown"], build_plan(records)) print(f"workspace_base={paths['workspace_base']}") print(f"workspace_root={workspace_root}") print(f"run_id={workspace_root.name}") print(f"inventory={paths['inventory_json']}") print(f"snapshot_dir={paths['before_snapshot_dir']}") print(f"size_before={paths['size_before_json']}") if not inventory["workspace_inside_skill_root"]: print("warning=workspace_dir 位于目标 skill 根目录之外;这只应在用户明确指定时使用") if __name__ == "__main__": main() -
measure_markdown.py 2.9 KB
#!/usr/bin/env python3 from __future__ import annotations import argparse from common import ( build_totals, compute_file_record, ensure_workspace, iter_markdown_files, load_config, resolve_skill_root, resolve_workspace_root, write_json, write_text, ) def format_delta(before: dict[str, int], after: dict[str, int]) -> str: delta_words = after["total_words"] - before["total_words"] delta_chars = after["total_chars"] - before["total_chars"] lines = [ "# 压缩统计对比", "", f"- 压缩前总词数:{before['total_words']}", f"- 压缩后总词数:{after['total_words']}", f"- 词数变化:{delta_words}", f"- 压缩前总字符数:{before['total_chars']}", f"- 压缩后总字符数:{after['total_chars']}", f"- 字符变化:{delta_chars}", "", "## 当前最大文件", "", ] for record in sorted(after["files"], key=lambda item: item["words"], reverse=True)[:5]: lines.append(f"- `{record['path']}`:{record['words']} 词,{record['chars']} 字符") return "\n".join(lines) + "\n" def main() -> None: parser = argparse.ArgumentParser(description="统计目标 skill Markdown 体积") parser.add_argument("--skill-root", required=True, help="目标 skill 根目录") parser.add_argument( "--phase", default="after", choices=["before", "after"], help="写入前/后统计文件", ) parser.add_argument("--workspace-dir", help="自定义隐藏工作区") parser.add_argument("--run-id", help="复用 init_workspace.py 创建的 run 目录名") args = parser.parse_args() config = load_config() target_skill_root = resolve_skill_root(args.skill_root) workspace_root = resolve_workspace_root( target_skill_root, config, args.workspace_dir, run_id=args.run_id, ) paths = ensure_workspace(config, workspace_root) markdown_files = iter_markdown_files(target_skill_root, config, workspace_root) records = [compute_file_record(target_skill_root, path) for path in markdown_files] totals = build_totals(records, phase=args.phase) output_key = "size_before_json" if args.phase == "before" else "size_after_json" write_json(paths[output_key], totals) before_path = paths["size_before_json"] after_path = paths["size_after_json"] if before_path.exists() and after_path.exists(): import json before = json.loads(before_path.read_text(encoding="utf-8")) after = json.loads(after_path.read_text(encoding="utf-8")) write_text(paths["size_delta_markdown"], format_delta(before, after)) print(f"workspace_root={workspace_root}") print(f"run_id={workspace_root.name}") print(f"{args.phase}_stats={paths[output_key]}") if paths["size_delta_markdown"].exists(): print(f"delta_report={paths['size_delta_markdown']}") if __name__ == "__main__": main() -
validate_compaction.py 4.7 KB
#!/usr/bin/env python3 from __future__ import annotations import argparse import json from pathlib import Path from common import ( build_totals, compute_file_record, ensure_workspace, find_local_link_issues, iter_markdown_files, load_config, nested_get, path_within, parse_frontmatter, read_text, resolve_skill_root, resolve_workspace_root, write_json, ) def main() -> None: parser = argparse.ArgumentParser(description="校验压缩后的 skill Markdown") parser.add_argument("--skill-root", required=True, help="目标 skill 根目录") parser.add_argument("--workspace-dir", help="自定义隐藏工作区") parser.add_argument("--run-id", help="复用 init_workspace.py 创建的 run 目录名") args = parser.parse_args() config = load_config() target_skill_root = resolve_skill_root(args.skill_root) workspace_root = resolve_workspace_root( target_skill_root, config, args.workspace_dir, run_id=args.run_id, ) paths = ensure_workspace(config, workspace_root) markdown_files = iter_markdown_files(target_skill_root, config, workspace_root) errors: list[str] = [] warnings: list[str] = [] skill_md = target_skill_root / "SKILL.md" frontmatter, skill_body = parse_frontmatter(read_text(skill_md)) for dotted_key in config["preservation"]["required_skill_frontmatter_fields"]: if nested_get(frontmatter, dotted_key) in (None, "", []): errors.append(f"SKILL.md frontmatter 缺少必需字段: {dotted_key}") if nested_get(frontmatter, "name") != target_skill_root.name: warnings.append( "SKILL.md frontmatter.name 与目录名不一致;若这是历史兼容设计可忽略,否则建议修正" ) keywords = nested_get(frontmatter, "metadata.keywords") or [] if config["preservation"]["required_skill_keywords_include_name"] and nested_get( frontmatter, "name" ) not in keywords: errors.append("metadata.keywords 未包含 skill 名") if not path_within(target_skill_root, workspace_root): warnings.append("workspace_dir 位于目标 skill 根目录之外;只有用户明确指定时才应这样做") for path in markdown_files: issues = find_local_link_issues(path, read_text(path), target_skill_root) errors.extend( [f"{path.relative_to(target_skill_root)}: {issue}" for issue in issues] ) if not skill_body.strip(): errors.append("SKILL.md 正文为空") before_stats_path = paths["size_before_json"] after_stats_path = paths["size_after_json"] if before_stats_path.exists(): before = json.loads(before_stats_path.read_text(encoding="utf-8")) if not after_stats_path.exists(): current_records = [compute_file_record(target_skill_root, path) for path in markdown_files] write_json(after_stats_path, build_totals(current_records, phase="after")) after = json.loads(after_stats_path.read_text(encoding="utf-8")) if after["total_words"] >= before["total_words"]: warnings.append( "压缩后总词数未下降;如果这是有意保留,请在报告中说明原因" ) snapshot_skill_md = paths["before_snapshot_dir"] / "SKILL.md" if snapshot_skill_md.exists(): snapshot_frontmatter, _ = parse_frontmatter(read_text(snapshot_skill_md)) if nested_get(snapshot_frontmatter, "name") != nested_get(frontmatter, "name"): errors.append("SKILL.md frontmatter.name 与压缩前快照不一致") if nested_get(snapshot_frontmatter, "description") != nested_get(frontmatter, "description"): warnings.append("SKILL.md frontmatter.description 与压缩前快照不同;请确认只是等价压缩") result = { "skill_root": str(target_skill_root), "workspace_base": str(paths["workspace_base"]), "workspace_root": str(workspace_root), "run_id": workspace_root.name, "workspace_inside_skill_root": path_within(target_skill_root, workspace_root), "checked_files": [str(path.relative_to(target_skill_root)) for path in markdown_files], "error_count": len(errors), "warning_count": len(warnings), "errors": errors, "warnings": warnings, } write_json(paths["validation_json"], result) print(f"workspace_root={workspace_root}") print(f"run_id={workspace_root.name}") print(f"validation={paths['validation_json']}") if warnings: print(f"warnings={len(warnings)}") if errors: for error in errors: print(f"ERROR: {error}") raise SystemExit(1) if __name__ == "__main__": main()
-
-
CHANGELOG.md 2.5 KB
# compact-bensz-skills - 变更日志 遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/) 与语义化版本规范。 ## [Unreleased] ### Added(新增) - 新增 `workspace_inside_skill_root` 诊断字段到初始化清单与校验结果,便于识别用户显式指定的外部工作区 - 新增 `latest-run.txt` 与 `--run-id` 机制,支持 `measure_markdown.py` / `validate_compaction.py` 复用指定 run ### Changed(变更) - 默认待压缩范围收紧为工作型 Markdown:目标 skill 根目录下的 `README.md`、`CHANGELOG.md` 不再纳入 `file-inventory.json`、快照和体积统计;`SKILL.md`、README、参考清单与测试夹具已同步改为这一口径 - `validate_compaction.py` 现在会在缺少 `size-after.json` 时自动补算当前统计,降低验证顺序对结果的影响 - `README.md`、`SKILL.md`、`references/validation-checklist.md` 同步补充“外部工作区会被警告”和“本地链接不得越出 skill 根目录”的口径 - 默认工作区从单一 `.compact-bensz-skills/` 切换为按运行隔离的 `.compact-bensz-skills/run-{timestamp}/` - `init_workspace.py` 会输出 `workspace_base` / `run_id` 并刷新 `latest-run.txt`,后续脚本默认复用最近一次运行 - `config.yaml` 版本号 `0.2.0 -> 0.3.0` ### Fixed(修复) - 修复本地 Markdown 链接校验只检查“是否存在”而不检查“是否越出目标 skill 根目录”的缺口 - 修复压缩后 `SKILL.md` frontmatter.name 可能与压缩前快照悄然漂移而未被校验的问题 - 修复多次连续运行时中间文件可能被同一隐藏目录相互覆盖、导致不同会话互相污染的问题 ## [0.1.0] - 2026-03-28 ### Added(新增) - 初始化 `compact-bensz-skills` skill,用于压缩 Agent Skill 的 Markdown 文档,同时保留触发语义、输入输出契约与安全边界 - 新增 `scripts/init_workspace.py`:创建目标 skill 的 `.compact-bensz-skills/` 隐藏工作区,生成 Markdown 清单、快照与压缩计划骨架 - 新增 `scripts/measure_markdown.py`:统计压缩前后字数/字符数/标题数/代码块数,并输出对比报告 - 新增 `scripts/validate_compaction.py`:校验 `SKILL.md` frontmatter、相对 Markdown 链接、忽略目录约束与压缩收益 - 新增 `references/compaction-playbook.md`、`references/preservation-checklist.md`、`references/validation-checklist.md` 三份参考文档 - 新增 `tests/compact-bensz-skills/fixture-skill/` 轻量夹具,用于验证文件发现、工作区隔离与压缩校验流程 -
config.yaml 1.6 KB
skill_info: name: "compact-bensz-skills" version: "0.3.0" description: "压缩 Agent Skill 中的工作型 Markdown 文档,在不改变功能与安全边界的前提下降低上下文体积。" author: "Bensz Conan" category: "skill-maintenance" workspace: hidden_dir: ".bensz-api/skills/compact-bensz-skills" run_prefix: "" timestamp_format: "%Y-%m-%d-%H-%M" latest_run_pointer: "latest-run.txt" keep_intermediates_inside_hidden_dir: true subdirs: - "input" - "output" - "log" - "snapshots" - "analysis" - "reports" before_snapshot_dir: "snapshots/before" directories: default_test_dir: "tests/compact-bensz-skills" source_files: markdown_extensions: - ".md" ignore_dirs: - "tests" - "plans" - ".compact-bensz-skills" - ".bensz-api" - ".git" - "__pycache__" ignore_globs: - ".DS_Store" - "*.pyc" ignore_file_names: - "README.md" - "CHANGELOG.md" preservation: required_skill_frontmatter_fields: - "name" - "description" - "metadata.author" required_skill_keywords_include_name: true keep_code_blocks: true keep_commands_and_paths: true keep_safety_constraints: true keep_user_visible_outputs: true reports: inventory_json: "analysis/file-inventory.json" plan_markdown: "analysis/compaction-plan.md" size_before_json: "reports/size-before.json" size_after_json: "reports/size-after.json" size_delta_markdown: "reports/size-delta.md" validation_json: "reports/validation.json" scripts: init_workspace: "scripts/init_workspace.py" measure_markdown: "scripts/measure_markdown.py" validate_compaction: "scripts/validate_compaction.py" -
README.md 6.3 KB
# compact-bensz-skills `compact-bensz-skills` 用来压缩某个 Agent Skill 里的工作型 Markdown 文档,目标是在**不改变原有功能**的前提下,显著降低上下文体积。 ## 最推荐用法 ```text 请使用 compact-bensz-skills skill 压缩这个 Agent Skill 的工作型 Markdown 文档。 输入:/path/to/target-skill 输出:更新后的 skill 源文件;所有中间文件保存在目标目录下的 .bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/ ``` ## 适用场景 - 目标 skill 的 `SKILL.md`、`references/*.md` 明显冗长 - 你希望节省上下文,但不想动脚本逻辑 - 你需要先理解 skill,再做保守压缩 ## 默认行为 - 工作区根:`<skill_root>/.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/` - 每次运行目录:`<skill_root>/.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/` - 最近一次运行指针:`<skill_root>/.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/latest-run.txt` - 测试区:`<skill_root>/tests/compact-bensz-skills/` - 自动忽略:`tests/`、`plans/`、目标 skill 根目录下的 `README.md`、`CHANGELOG.md` - 优先保留:frontmatter、输入输出契约、安全边界、命令与路径 - 如果你显式指定外部 `workspace_dir`,脚本会接受,但验证报告会提示“中间文件已离开 skill 根目录” ## 常用示例 ### 示例 1:压缩单个 skill ```text 请使用 compact-bensz-skills skill 压缩 `git-pr-review` 这个 skill 的工作型 Markdown 文档。 输入:/workspace/skills/git-pr-review 输出:原 skill 目录内更新后的 Markdown;中间文件放到 `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/` ``` ### 示例 2:指定额外约束 ```text 请使用 compact-bensz-skills skill 压缩这个 skill 的工作型 Markdown。 输入:/workspace/skills/my-skill 输出:更新后的 skill 源文件 另外,还有下列参数约束: - 只压缩 Markdown,不改 Python/Bash 脚本 - 默认不要动 README.md / CHANGELOG.md - 不要改变 SKILL.md frontmatter 的 name - 压缩完成后要输出压缩前后统计 ``` ## 运行时会生成什么 - `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/latest-run.txt` - `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/analysis/file-inventory.json` - `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/analysis/compaction-plan.md` - `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/reports/size-before.json` - `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/reports/size-after.json` - `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/reports/size-delta.md` - `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/reports/validation.json` ## 备选用法(脚本) ```bash python3 compact-bensz-skills/scripts/init_workspace.py --skill-root /path/to/target-skill python3 compact-bensz-skills/scripts/measure_markdown.py --skill-root /path/to/target-skill --phase after python3 compact-bensz-skills/scripts/validate_compaction.py --skill-root /path/to/target-skill ``` 如果你想显式锁定某一轮: ```bash python3 compact-bensz-skills/scripts/init_workspace.py --skill-root /path/to/target-skill # 记下输出里的 run_id python3 compact-bensz-skills/scripts/measure_markdown.py --skill-root /path/to/target-skill --run-id 2026-03-28-15-52 --phase after python3 compact-bensz-skills/scripts/validate_compaction.py --skill-root /path/to/target-skill --run-id 2026-03-28-15-52 ``` ## WHICHMODEL 最后核对:2026-03-28。 - OpenAI 路线: - 复杂 skill 压缩、跨多份 `references/` 去重、需要保守保留约束时,优先 `gpt-5.4`;OpenAI 官方把它列为复杂推理、coding 和 agentic workflows 的起点。 - 如果你主要在 Codex 里工作,且任务是“读文档 + 改文档 + 跑脚本验证”的 agentic coding 流程,可优先 `gpt-5-codex`;官方说明它是面向 Codex 一类环境优化的 agentic coding 模型。 - 只做轻量统计、跑 helper scripts、整理测试记录时,可降到 `gpt-5.4-mini` 或同级快模型。 - Anthropic 路线: - 最复杂的压缩任务优先 `Opus`;Anthropic 官方建议复杂任务先从 Opus 开始。 - 日常 skill 压缩、文档改写和一般验证优先 `Sonnet`;官方将其定位为速度与智能的最佳平衡,并在 Claude Code 中作为日常 coding 默认推荐档位。 - 只做简单检查或批量轻任务时可用 `Haiku`。 - 长上下文优先级: - 如果目标 skill 很大,优先选支持更长上下文的模型。OpenAI 当前前沿模型页给 `gpt-5.4` 标注了 1M context;Anthropic 也为 Sonnet/Opus 提供了 1M context 选项或长会话模式。 参考: - OpenAI Models: https://developers.openai.com/api/docs/models - OpenAI GPT-5-Codex: https://developers.openai.com/api/docs/models/gpt-5-codex - Anthropic Models Overview: https://platform.claude.com/docs/en/about-claude/models/overview ## FAQ ### 它会改 `tests/` 和 `plans/` 吗? 不会。这个 skill 默认忽略目标 skill 里的 `tests/` 和 `plans/`。 ### 它会处理 `README.md` 或 `CHANGELOG.md` 吗? 默认不会。这个 skill 只把工作型 Markdown 视为主要目标;目标 skill 根目录下的 `README.md`、`CHANGELOG.md` 一般属于面向人类的说明或发布记录,不纳入默认压缩范围。 ### 它会自动改脚本代码吗? 默认不会。它的核心目标是压缩 Markdown 文档;只有当文档与脚本明显不一致时,才应先指出风险,再做最小修正。 ### 它怎么保证不破坏功能? 它要求先理解 `SKILL.md`、`config.yaml`、`scripts/`,然后再压缩文档,并在最后运行统计和校验脚本。 ### 为什么现在要用 `{yyyy-mm-dd-hh-mm}`? 因为这个 skill 可能会反复执行。按分钟级 run 目录隔离后,每一轮的快照、统计和验证结果都能独立追溯,不会被下一轮覆盖;同一分钟重复运行时脚本会自动追加后缀。 ### 它会检查相对链接是否越界吗? 会。`validate_compaction.py` 现在不仅检查链接是否存在,也会拒绝链接跳出目标 skill 根目录。 -
SKILL.md 9.9 KB
--- name: compact-bensz-skills description: 当用户明确要求压缩、瘦身或精简 Agent Skill 文档、降低上下文开销且不改变功能时使用。⚠️ 不适用:新增功能、修复脚本逻辑、批量改代码或压缩普通文档。 metadata: author: Bensz Conan short-description: 在不改变功能的前提下压缩 Agent Skill 的 Markdown 上下文 keywords: - compact-bensz-skills - skill compaction - markdown compression - context reduction - Agent Skills --- # compact-bensz-skills ## 目标 当用户明确要求“压缩/瘦身/精简某个 Agent Skill 的 Markdown 文档”“在不改变功能前提下降低 skill 上下文开销”时使用。先理解目标 skill 的真实能力与安全边界,再在忽略 `tests/`、`plans/` 以及目标 skill 的 `README.md`、`CHANGELOG.md` 的前提下,压缩 `SKILL.md`、`references/*.md` 等工作型 Markdown,并把中间产物隔离到 `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/`。⚠️ 不适用:用户主要想新增功能、修复脚本逻辑、批量改代码、或只想压缩非 skill 文档。 ## 流程 ### 输入 #### 输入 1. `skill_root`(必需) - 目标 Agent Skill 根目录 2. `workspace_dir`(可选) - 默认把本轮工作区建在 `<skill_root>/.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/` - 如果用户显式指定其它目录,则把它视为“run 容器根目录”,本轮仍落到其中的 `{yyyy-mm-dd-hh-mm}/` 3. `run_id`(可选) - 用于在 `init -> measure -> validate` 间显式复用同一轮工作区 4. `test_dir`(可选) - 默认 `<skill_root>/tests/compact-bensz-skills/` ### 执行步骤 #### 核心原则 - **先理解,再压缩**:先读 `SKILL.md`、`config.yaml`、`scripts/` 和必要的 `references/`,再判断哪些 Markdown 可以缩短。 - **不改行为,只改表达**:压缩的是文档体积,不是功能边界;不得擅自新增、删除或扭曲目标 skill 的能力。 - **保护触发语义**:`SKILL.md` frontmatter 的 `name` 必须保持不变;`description` 只能等价压缩,不能丢失关键触发条件。 - **保护硬约束**:输入、输出、默认路径、安全限制、必跑脚本、失败条件、与其它 skill 的协作约定都必须保留。 - **只动源工作文件**:忽略目标 skill 的 `tests/`、`plans/` 及其内容,不把它们视为待压缩源文件。 - **默认不动说明文档**:目标 skill 根目录下的 `README.md`、`CHANGELOG.md` 一般属于面向人类的说明或发布记录,不视为默认压缩目标。 - **中间文件隔离**:分析、快照、统计、验证结果都写到隐藏目录 `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/`;除非用户另有指定,不向外泄露中间文件。 - **按轮次隔离**:每次运行都应在 `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/` 内工作,避免多次压缩会话互相覆盖。 - **链接不能越界**:压缩后保留的本地 Markdown 链接必须仍位于目标 skill 根目录内,不能借相对路径跳到 skill 外部。 #### 标准工作流 ##### 1. 初始化隐藏工作区 优先使用确定性脚本创建工作区、快照和 Markdown 清单: ```bash python3 compact-bensz-skills/scripts/init_workspace.py --skill-root /path/to/target-skill ``` 脚本会: - 创建 `<skill_root>/.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/` - 在隐藏根目录写入 `latest-run.txt` - 扫描待压缩 Markdown(忽略 `tests/`、`plans/`、`README.md`、`CHANGELOG.md`) - 生成 `analysis/file-inventory.json` - 备份原文到 `snapshots/before/` - 生成 `analysis/compaction-plan.md` - 记录压缩前统计到 `reports/size-before.json` ##### 2. 理解目标 skill 的真实功能 最低阅读范围: - `SKILL.md` - `config.yaml`(如存在) - `scripts/`(如存在) - `references/` 中与核心流程直接相关的文档 - 仅当 `README.md`、`CHANGELOG.md` 与核心行为边界强相关时才辅助阅读;默认不把它们纳入压缩目标 理解时重点确认: - 技能触发条件与不适用范围 - 输入输出契约 - 默认工作区 / 测试区 / 中间文件路径 - 安全边界与只读/只写限制 - 任何“必须执行”“不得省略”的步骤 ##### 3. 执行 Markdown 压缩 优先顺序: 1. 删重复:移除跨文件、跨章节重复解释 2. 缩长句:把啰嗦描述改成短句、表格或清单 3. 主从分离:`SKILL.md` 只保留触发逻辑、主流程、硬约束;细节下沉到 `references/` 4. 压示例:保留最小可用命令和最关键示例,删除低价值变体 默认优先处理: - `SKILL.md` - `references/` 中真正承载执行细则的 Markdown 默认不处理: - 目标 skill 根目录下的 `README.md` - 目标 skill 根目录下的 `CHANGELOG.md` 压缩时必须保留: - `SKILL.md` frontmatter 与关键词可发现性 - 关键命令、路径、文件名、配置键 - 输入/输出、默认目录、安全限制 - 会改变行为的条件分支 - 与 `bensz-collect-bugs` 等跨 skill 约定 压缩时禁止: - 把“必需”改成“可选” - 删除失败条件、边界条件、路径约束 - 删除唯一的命令示例或唯一的输出说明 - 只为了省字而制造歧义 ##### 4. 复测压缩收益 完成文档修改后重新统计: ```bash python3 compact-bensz-skills/scripts/measure_markdown.py --skill-root /path/to/target-skill --phase after ``` 如需显式复用某一轮: ```bash python3 compact-bensz-skills/scripts/measure_markdown.py \ --skill-root /path/to/target-skill \ --run-id 2026-03-28-15-52 \ --phase after ``` 该脚本会输出: - `reports/size-after.json` - `reports/size-delta.md` ##### 5. 校验压缩后仍可用 ```bash python3 compact-bensz-skills/scripts/validate_compaction.py --skill-root /path/to/target-skill ``` 默认会复用 `latest-run.txt` 指向的最近一轮;如果你在多个 run 之间切换,显式传 `--run-id` 更稳妥。 至少检查: - `SKILL.md` frontmatter 是否完整 - `metadata.author` 是否保留 - `metadata.keywords` 是否仍包含 skill 名 - 本地 Markdown 相对链接是否仍有效且没有越出目标 skill 根目录 - 压缩后的总字数是否低于压缩前 - 若 `description` 有改动,确认只是等价压缩而非改坏触发语义 #### 何时读取参考文档 - 需要决定“哪些内容必须保留”时,读 `references/preservation-checklist.md` - 需要具体压缩手法时,读 `references/compaction-playbook.md` - 做收尾校验时,读 `references/validation-checklist.md` ### 输出 #### 输出 对用户的主要交付: - 更新后的目标 skill 工作型 Markdown 源文件 隐藏工作区产物: - `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/latest-run.txt` - `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/analysis/file-inventory.json` - `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/analysis/compaction-plan.md` - `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/reports/size-before.json` - `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/reports/size-after.json` - `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/reports/size-delta.md` - `.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/compact-bensz-skills/{yyyy-mm-dd-hh-mm}/reports/validation.json` ### 输出管理 #### BenszAPI 任务工作区 ### 校验 至少检查 frontmatter、`metadata.author`、技能关键词、相对链接、关键命令/路径/失败条件和触发语义均保留;压缩后的 Markdown 总量应低于压缩前,且 `validate_compaction.py` 与引用检查通过。不能证明语义等价时不得交付压缩版本。 ### 失败与恢复 快照、统计、链接或语义校验失败时保留 `snapshots/before/`、报告和错误日志,停止写回正式 Skill;可在同一 run 目录修正后重试,若收益或保真无法满足阈值则恢复基线并报告原因。 ## 约束 <!-- BEGIN COMMON CONSTRAINTS --> <!-- Source-Hash: sha256:15120201e9e0c7569517261d57ecefb63ac279c26ed13876f8e95b6dc35854d3 --> <!-- Template-ID: skill-common-constraints; Template-Version: 1; Sync-Policy: exact-block --> ### 公共硬约束 本块由 `docs/templates/skill-common-constraints.md` 统一维护;每个 `SKILL.md` 的 `## 约束` 必须逐字同步本块,不得在副本中改写公共规则。 - 任务需要落盘时,使用唯一的 `./.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/` 根目录;共享材料放入 `shared/`,Skill 专属材料放入该 Skill 的 `input/`、`output/`、`log/`。 - 正式交付物、源代码和正式计划按项目约定保存,不写入任务工作区;未经授权不覆盖、删除、迁移或远程写入。 - 项目维护变更检查 BAC 可用性并记录需求、AI 产出、工具结果、文件改动和验证摘要;BAC 只做过程审计,不替代署名、责任或合规判断。 - 不记录 API Key、访问令牌、密码、Cookie、环境/凭据文件、私有 Prompt、身份信息、本地用户名、主机名或不必要的大体积原始数据。 - 文件路径必须规范化并限制在授权项目范围内;外部 URL、子进程和网络访问遵循最小权限,防止路径遍历、SSRF 和命令注入。 - Skill 版本唯一记录在自身 `config.yaml:skill_info.version`;公开 API、协议、目录或配置变更同步文档与 `CHANGELOG.md`。 - `bensz-collect-bugs` 是一个 Agent Skill;仅将 Bensz Agent Skill 或 Bensz 基础设施本身的设计缺陷交给它。先脱敏写入 `~/.bensz-skills/bugs/`,当前任务不中断,只有用户明确要求才公开上报,禁止直接修改用户已安装的 Skill 源码。 <!-- End of canonical common constraints. --> <!-- END COMMON CONSTRAINTS -->
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.