Create a brand-new skill directory (for CAPTURED). Args: target_dir: New skill directory (must not exist). content: LLM output — complete skill content. patch_type: Output format (AUTO to auto-detect, usually FULL).
(
target_dir: Path,
content: str,
patch_type: PatchType = PatchType.AUTO,
)
| 279 | ) |
| 280 | |
| 281 | def create_skill( |
| 282 | target_dir: Path, |
| 283 | content: str, |
| 284 | patch_type: PatchType = PatchType.AUTO, |
| 285 | ) -> SkillEditResult: |
| 286 | """Create a brand-new skill directory (for CAPTURED). |
| 287 | |
| 288 | Args: |
| 289 | target_dir: New skill directory (must not exist). |
| 290 | content: LLM output — complete skill content. |
| 291 | patch_type: Output format (AUTO to auto-detect, usually FULL). |
| 292 | """ |
| 293 | if target_dir.exists(): |
| 294 | return SkillEditResult(error=f"Target already exists: {target_dir}") |
| 295 | |
| 296 | if patch_type == PatchType.AUTO: |
| 297 | patch_type = detect_patch_type(content) |
| 298 | |
| 299 | try: |
| 300 | target_dir.mkdir(parents=True, exist_ok=True) |
| 301 | |
| 302 | if patch_type == PatchType.PATCH: |
| 303 | # PATCH with only Add File hunks |
| 304 | _apply_multi_file_patch(content, target_dir) |
| 305 | elif patch_type == PatchType.FULL: |
| 306 | _apply_multi_file_full(content, target_dir) |
| 307 | elif patch_type == PatchType.DIFF: |
| 308 | # For CAPTURED, DIFF doesn't make sense — treat as single-file FULL |
| 309 | (target_dir / SKILL_FILENAME).write_text(content, encoding="utf-8") |
| 310 | else: |
| 311 | shutil.rmtree(target_dir, ignore_errors=True) |
| 312 | return SkillEditResult(error=f"Unknown patch type: {patch_type}") |
| 313 | except (PatchError, Exception) as e: |
| 314 | shutil.rmtree(target_dir, ignore_errors=True) |
| 315 | return SkillEditResult(error=str(e)) |
| 316 | |
| 317 | _normalize_skill_frontmatter(target_dir) |
| 318 | |
| 319 | new_files = _collect_files(target_dir) |
| 320 | # Add-all diff (everything is new) |
| 321 | add_all = "\n".join( |
| 322 | compute_unified_diff("", text, filename=name) |
| 323 | for name, text in sorted(new_files.items()) |
| 324 | if compute_unified_diff("", text, filename=name) |
| 325 | ) |
| 326 | |
| 327 | logger.info(f"create_skill: {target_dir.name} ({patch_type.value})") |
| 328 | return SkillEditResult( |
| 329 | skill_dir=target_dir, |
| 330 | content_diff=add_all, |
| 331 | content_snapshot=new_files, |
| 332 | ) |
| 333 | |
| 334 | def detect_patch_type(content: str) -> PatchType: |
| 335 | """Auto-detect the patch format from LLM output. |
no test coverage detected