33Kimi uses the ``.kimi-code/skills/speckit-<name>/SKILL.md`` layout with
44``/skill:speckit-<name>`` invocation syntax.
55
6- Includes legacy migration logic for projects initialised before Kimi
7- Code CLI adopted the ``.kimi-code/`` directory, as well as for the
8- older dotted skill directory naming (``speckit.xxx`` → ``speckit-xxx``).
6+ Legacy migration covers projects created before Kimi Code CLI moved to
7+ this layout and handles two distinct changes: the directory move from
8+ ``.kimi/`` to ``.kimi-code/`` (including the ``KIMI.md`` → ``AGENTS.md``
9+ context file), and the dotted-to-hyphenated skill naming
10+ (``speckit.xxx`` → ``speckit-xxx``).
911"""
1012
1113from __future__ import annotations
@@ -204,7 +206,10 @@ def _migrate_legacy_kimi_skills_dir(
204206 for legacy_dir in legacy_dirs :
205207 if legacy_dir .is_symlink () or not legacy_dir .is_dir ():
206208 continue
207- if not (legacy_dir / "SKILL.md" ).exists ():
209+ legacy_skill = legacy_dir / "SKILL.md"
210+ # Treat a symlinked SKILL.md as invalid: later read_bytes() would
211+ # otherwise follow it and read content from outside the project.
212+ if legacy_skill .is_symlink () or not legacy_skill .is_file ():
208213 continue
209214
210215 target_name = _legacy_to_target_name (legacy_dir .name )
@@ -223,9 +228,13 @@ def _migrate_legacy_kimi_skills_dir(
223228 migrated_count += 1
224229 continue
225230
226- # Target exists — only remove legacy if SKILL.md is identical
231+ # Target exists — only remove legacy if SKILL.md is identical.
232+ # Skip when the target SKILL.md is a symlink so the byte comparison
233+ # never follows it outside the project. (legacy_skill is already
234+ # guaranteed to be a real file by the guard above.)
227235 target_skill = target_dir / "SKILL.md"
228- legacy_skill = legacy_dir / "SKILL.md"
236+ if target_skill .is_symlink ():
237+ continue
229238 if target_skill .is_file ():
230239 try :
231240 if target_skill .read_bytes () == legacy_skill .read_bytes ():
@@ -265,7 +274,9 @@ def _is_speckit_generated_skill(skill_dir: Path) -> bool:
265274 ``SkillsIntegration.setup()`` to avoid deleting user-authored skills.
266275 """
267276 skill_file = skill_dir / "SKILL.md"
268- if not skill_file .is_file ():
277+ # A symlinked SKILL.md is never treated as Speckit-generated, so teardown
278+ # cleanup never follows it to read frontmatter from outside the project.
279+ if skill_file .is_symlink () or not skill_file .is_file ():
269280 return False
270281
271282 try :
0 commit comments