Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ Windows 上 Claude 安装器还会额外写入 `~/.claude/commands/{character}-{
- `relationship` → `./skills/relationship/`
- `celebrity` → `./skills/celebrity/`

如果目标 `{character}/{slug}` 已经存在,创建器会停止,不会改写原来的文件。请通过进化模式更新已有 Skill;使用 CLI 时改用 `--action update`,不要删除旧目录后重新创建。例外是命令明确提示本次创建失败、目录可能不完整:先检查该目录,只有确认它就是要丢弃的失败产物时才手动删除,然后重试 `create`。

用于宿主调用的 `SKILL.md` 已经自包含 Persona + Work。安装生成 Skill 时使用统一安装器;它只写入这一文件和 `.distilly-install.json`,不会复制可能含有私有原材料的整个生成目录。对于旧版下划线 frontmatter,安装器只在安装副本中规范为 `{character}-{slug}`,不会修改源 Skill:

```bash
Expand Down
8 changes: 8 additions & 0 deletions INSTALL_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ The installer writes only the self-contained `SKILL.md` and
generated directory, and it normalizes legacy underscore frontmatter only in
the installed copy.

If the target `{character}/{slug}` already exists, creation stops without
changing its files. Use Evolution Mode to update an existing Skill, or pass
`--action update` when using the CLI. Do not delete the old directory and
create it again. The exception is when the command explicitly reports that
this create failed and the directory may be incomplete. Inspect that directory
first. Remove it only after confirming it is the failed create you intend to
discard, then retry `create`.

On Windows, use the dedicated Claude Code installer when a command shim is
needed:

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ Distilly's current creator uses **Persona** as the universal base, with family-s

### 🧬 Evolution

- 🛡️ **Safe create** → if a Skill already uses the same `{character}/{slug}`, creation stops and points you to Evolution instead of rewriting its files
- 📥 **Append files** → auto-analyze delta → merge into relevant sections, never overwrite existing conclusions
- 💬 **Conversation correction** → say "they wouldn't do that, they'd be xxx" → writes to the Correction layer, takes effect immediately
- 🕰️ **Version control** → auto-archive on every update, rollback to any previous version
Expand Down
18 changes: 10 additions & 8 deletions SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,8 @@ Persona 摘要:
- `profile`
- `tags`
- `knowledge_sources`
4. 然后调用:
4. 如果 `{resolved_base_dir}/{slug}` 已经存在,停止创建并转入“进化模式”。writer 会拒绝覆盖已有 Skill;不要删除旧目录后重试。只有当上一条创建命令明确报告目录可能不完整时,才先检查该目录;确认它就是要丢弃的失败产物后,可以手动删除并重试创建。
5. 然后调用:
```bash
python3 "{distilly_skill_root}/tools/skill_writer.py" \
--action create \
Expand All @@ -629,7 +630,7 @@ Persona 摘要:
--persona /tmp/distilly_{slug}_persona.md \
--base-dir {resolved_base_dir}
```
5. 该命令会统一生成:
6. 该命令会统一生成:
- `SKILL.md`
- `work.md`
- `persona.md`
Expand All @@ -648,11 +649,11 @@ Persona 摘要:
- OpenCode:运行 `python3 "{distilly_skill_root}/tools/install_generated_skill.py" --skill-dir "{resolved_base_dir}/{slug}" --host opencode --force`;项目级安装追加 `--skills-dir .opencode/skills`
- 统一安装器只写入自包含的 `SKILL.md` 和安装元数据,会在安装副本中规范旧版 frontmatter;不要手动复制整个生成目录,其中可能包含私有原始材料
- Claude Code on Windows:可再追加 `--install-claude-command-shim`
6. 如果当前是 `celebrity`,创建完成后必须再跑一次质量检查:
7. 如果当前是 `celebrity`,创建完成后必须再跑一次质量检查:
```bash
python3 "{distilly_skill_root}/tools/research/quality_check.py" "{resolved_base_dir}/{slug}/SKILL.md" --profile {research_profile}
```
7. 如果 `celebrity` 的质量检查仍然提示 `source_grounding` 失败:
8. 如果 `celebrity` 的质量检查仍然提示 `source_grounding` 失败:
- 可以补写诚实的来源说明和局限说明
- 但只有在拿到真实、具体、可追溯的外部来源时,才能补充 URL
- **不要**用站点首页、topic 页、搜索页、个人空间首页等泛化链接来“刷过”检查
Expand Down Expand Up @@ -1370,7 +1371,8 @@ After user confirmation, do not hand-build a `skills/colleague/{slug}`-style tre
- `profile`
- `tags`
- `knowledge_sources`
4. Then call:
4. If `{resolved_base_dir}/{slug}` already exists, stop creation and switch to Evolution Mode. The writer refuses to overwrite an existing Skill; do not delete the old directory and retry. Only when the previous create command explicitly reports that the directory may be incomplete should you inspect it, remove it after confirming it is the failed create you intend to discard, and retry creation.
5. Then call:
```bash
python3 "{distilly_skill_root}/tools/skill_writer.py" \
--action create \
Expand All @@ -1383,7 +1385,7 @@ After user confirmation, do not hand-build a `skills/colleague/{slug}`-style tre
--persona /tmp/distilly_{slug}_persona.md \
--base-dir {resolved_base_dir}
```
5. This command will generate:
6. This command will generate:
- `SKILL.md`
- `work.md`
- `persona.md`
Expand All @@ -1402,11 +1404,11 @@ After user confirmation, do not hand-build a `skills/colleague/{slug}`-style tre
- OpenCode: run `python3 "{distilly_skill_root}/tools/install_generated_skill.py" --skill-dir "{resolved_base_dir}/{slug}" --host opencode --force`; append `--skills-dir .opencode/skills` for a project install
- The shared installer writes only the self-contained `SKILL.md` and install metadata and normalizes legacy frontmatter in the installed copy. Do not manually copy the whole generated directory; it may contain private source material
- Claude Code on Windows: optionally add `--install-claude-command-shim`
6. If the current family is `celebrity`, run a quality check after creation:
7. If the current family is `celebrity`, run a quality check after creation:
```bash
python3 "{distilly_skill_root}/tools/research/quality_check.py" "{resolved_base_dir}/{slug}/SKILL.md" --profile {research_profile}
```
7. If `source_grounding` still fails for a `celebrity` skill:
8. If `source_grounding` still fails for a `celebrity` skill:
- you may add honest limitation notes and a grounded source summary
- only add URLs when they are real, specific, and traceable sources
- **never** use site roots, topic pages, search pages, or other generic links as fake grounding
Expand Down
1 change: 1 addition & 0 deletions docs/lang/README_DE.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ Distilly verwendet **Persona** als universelle Basis, mit familienspezifischen M

### 🧬 Evolution

- 🛡️ **Sicheres Erstellen** → wenn bereits ein Skill mit demselben `{character}/{slug}` existiert, stoppt die Erstellung und verweist auf Evolution, statt dessen Dateien zu überschreiben
- 📥 **Dateien anfügen** → automatische Delta-Analyse → Merge in die relevanten Abschnitte, überschreibt nie bestehende Schlussfolgerungen
- 💬 **Gesprächskorrektur** → sage „so würden sie das nicht tun, sie wären xxx" → wird in die Korrekturschicht geschrieben, wirkt sofort
- 🕰️ **Versionskontrolle** → automatische Archivierung bei jedem Update, Rollback zu jeder früheren Version
Expand Down
1 change: 1 addition & 0 deletions docs/lang/README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ Distilly's current creator uses **Persona** as the universal base, with family-s

### 🧬 Evolution

- 🛡️ **Safe create** → if a Skill already uses the same `{character}/{slug}`, creation stops and points you to Evolution instead of rewriting its files
- 📥 **Append files** → auto-analyze delta → merge into relevant sections, never overwrite existing conclusions
- 💬 **Conversation correction** → say "they wouldn't do that, they'd be xxx" → writes to the Correction layer, takes effect immediately
- 🕰️ **Version control** → auto-archive on every update, rollback to any previous version
Expand Down
1 change: 1 addition & 0 deletions docs/lang/README_ES.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ Distilly usa **Persona** como base universal, con módulos específicos de cada

### 🧬 Evolución

- 🛡️ **Creación segura** → si ya existe un Skill con el mismo `{character}/{slug}`, la creación se detiene y te dirige a Evolución en lugar de sobrescribir sus archivos
- 📥 **Agregar archivos** → auto-analizar el delta → fusionar en secciones relevantes, nunca sobrescribe conclusiones existentes
- 💬 **Corrección por conversación** → di "él no haría eso, sería xxx" → se escribe en la capa de Corrección, efecto inmediato
- 🕰️ **Control de versiones** → auto-archivo en cada actualización, revertir a cualquier versión anterior
Expand Down
1 change: 1 addition & 0 deletions docs/lang/README_JA.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ Distilly は **Persona** を共通の土台とし、その上にファミリー

### 🧬 進化メカニズム

- 🛡️ **安全な作成** → 同じ `{character}/{slug}` の Skill がすでに存在する場合、ファイルを上書きせずに作成を停止し、進化モードの利用を案内します
- 📥 **ファイル追加** → 自動で差分分析 → 関連セクションにマージ、既存の結論は上書きしない
- 💬 **会話による修正** → 「彼はそんなことしない、xxx のはず」と伝える → Correction レイヤーに書き込まれ、即座に反映
- 🕰️ **バージョン管理** → 更新のたびに自動アーカイブ、任意の過去バージョンへロールバック可能
Expand Down
1 change: 1 addition & 0 deletions docs/lang/README_KO.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ Distilly는 **Persona**를 범용 베이스로 삼고, 그 위에 패밀리별

### 🧬 진화 방식

- 🛡️ **안전한 생성** → 같은 `{character}/{slug}`에 Skill이 이미 있으면 파일을 덮어쓰지 않고 생성을 중단한 뒤 진화 모드를 안내합니다
- 📥 **파일 추가** → 변경 내용을 자동 분석해 관련 섹션에 병합, 기존 결론은 덮어쓰지 않음
- 💬 **대화 기반 수정** → "그 사람은 이렇게 안 해, xxx여야 해"라고 말하면 Correction 레이어에 기록되어 즉시 반영
- 🕰️ **버전 관리** → 업데이트할 때마다 자동 아카이브, 이전 어느 버전으로든 롤백 가능
Expand Down
1 change: 1 addition & 0 deletions docs/lang/README_PT.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ A Distilly usa **Persona** como a base universal, com módulos específicos de c

### 🧬 Evolução

- 🛡️ **Criação segura** → se já existir um Skill com o mesmo `{character}/{slug}`, a criação para e orienta o uso da Evolução em vez de sobrescrever os arquivos
- 📥 **Adicionar arquivos** → auto-análise de delta → merge nas seções relevantes, nunca sobrescreve conclusões existentes
- 💬 **Correção por conversa** → diga "ele não faria isso, ele seria xxx" → escreve na camada de Correção, efeito imediato
- 🕰️ **Controle de versão** → auto-arquivamento a cada atualização, rollback para qualquer versão anterior
Expand Down
1 change: 1 addition & 0 deletions docs/lang/README_RU.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ Distilly использует **Persona** как универсальную ба

### 🧬 Эволюция

- 🛡️ **Безопасное создание** → если Skill с тем же `{character}/{slug}` уже существует, создание останавливается и предлагает использовать эволюцию вместо перезаписи файлов
- 📥 **Добавить файлы** → автоанализ дельты → мерж в соответствующие секции, никогда не перезаписывает существующие выводы
- 💬 **Коррекция через диалог** → скажи «он бы так не сделал, он должен быть xxx» → записывается в слой коррекции, мгновенный эффект
- 🕰️ **Версионирование** → автоархивация при каждом обновлении, откат к любой предыдущей версии
Expand Down
1 change: 1 addition & 0 deletions docs/lang/README_ZH.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ Distilly 当前的创建器以 **Persona** 为通用底座,不同家族按场

### 🧬 进化机制

- 🛡️ **安全创建** → 如果同一个 `{character}/{slug}` 已经有 Skill,创建会停止并提示使用进化模式,不会改写原文件
- 📥 **追加文件** → 自动分析增量 → merge 进对应部分,不覆盖已有结论
- 💬 **对话纠正** → 说「他不会这样,他应该是 xxx」→ 写入 Correction 层,立即生效
- 🕰️ **版本管理** → 每次更新自动存档,支持回滚到任意历史版本
Expand Down
62 changes: 62 additions & 0 deletions tests/test_cli_lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,68 @@ def test_create_name_only_normalizes_slug_and_rejects_unsafe_explicit_slug(self)
)
self.assertFalse((root / "skills" / "escape").exists())

def test_create_refuses_to_overwrite_an_existing_skill(self) -> None:
with tempfile.TemporaryDirectory() as tmp_dir:
root = Path(tmp_dir)
writer = str(PROJECT_ROOT / "tools" / "skill_writer.py")
base_dir = root / "skills" / "colleague"
original_work = root / "original-work.md"
replacement_work = root / "replacement-work.md"
original_work.write_text("Original work\n", encoding="utf-8")
replacement_work.write_text("Replacement work\n", encoding="utf-8")

self.run_cmd(
PYTHON,
writer,
"--action",
"create",
"--slug",
"existing",
"--name",
"Original Name",
"--work",
str(original_work),
"--base-dir",
str(base_dir),
cwd=root,
)

skill_dir = base_dir / "existing"
original_meta = (skill_dir / "meta.json").read_text(encoding="utf-8")
original_generated_work = (skill_dir / "work.md").read_text(encoding="utf-8")

with self.assertRaises(subprocess.CalledProcessError) as context:
self.run_cmd(
PYTHON,
writer,
"--action",
"create",
"--slug",
"existing",
"--name",
"Replacement Name",
"--work",
str(replacement_work),
"--base-dir",
str(base_dir),
cwd=root,
)

self.assertTrue(
context.exception.stderr.startswith("error: create target already exists")
)
self.assertIn("--action update", context.exception.stderr)
self.assertNotIn("Traceback", context.exception.stderr)
self.assertEqual(
(skill_dir / "meta.json").read_text(encoding="utf-8"),
original_meta,
)
self.assertEqual(
(skill_dir / "work.md").read_text(encoding="utf-8"),
original_generated_work,
)
self.assertEqual(list((skill_dir / "versions").iterdir()), [])

def test_update_accepts_safe_legacy_slug_with_spaces(self) -> None:
with tempfile.TemporaryDirectory() as tmp_dir:
root = Path(tmp_dir)
Expand Down
Loading
Loading