{"record":{"id":"a064201365f3418f","repo":"farion1231/cc-switch","slug":"pi-skill-directory","errorCode":null,"errorMessage":"Pi 中已存在同名但内容不同的 Skill，拒绝覆盖或删除: {directory}","messagePattern":"Pi 中已存在同名但内容不同的 Skill，拒绝覆盖或删除: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/src/services/skill.rs","lineNumber":2163,"sourceCode":"            ) {\n                return Ok(Some(PiSkillDeployment::Symlink {\n                    expected_target: resolved,\n                }));\n            }\n        } else if destination.is_dir() {\n            if let (Ok(destination_hash), Ok(source_hash)) = (\n                Self::compute_pi_deployment_hash(destination),\n                Self::compute_pi_deployment_hash(source),\n            ) {\n                if destination_hash == source_hash {\n                    return Ok(Some(PiSkillDeployment::Copy {\n                        expected_hash: destination_hash,\n                    }));\n                }\n            }\n        }\n\n        Err(anyhow!(\n            \"Pi 中已存在同名但内容不同的 Skill，拒绝覆盖或删除: {directory}\"\n        ))\n    }\n\n    fn remove_verified_pi_destination(\n        source: &Path,\n        destination: &Path,\n        directory: &str,\n    ) -> Result<()> {\n        match Self::inspect_pi_skill_destination(source, destination, directory)? {\n            Some(_) => Self::remove_path(destination),\n            None => Ok(()),\n        }\n    }\n\n    fn refresh_pi_skill_destination(\n        source: &Path,\n        destination: &Path,","sourceCodeStart":2145,"sourceCodeEnd":2181,"githubUrl":"https://github.com/farion1231/cc-switch/blob/0b5da510168914b251481654a568c3ffacd62cf4/src-tauri/src/services/skill.rs#L2145-L2181","documentation":"inspect_pi_skill_destination found the destination path inside Pi's skills directory already occupied, and the occupant is provably not ours: it is not a symlink resolving to the SSOT source, and it is not a byte-identical copy (deployment hash differs). The service refuses to overwrite or delete content it cannot verify it owns — a data-loss guard for the Pi app directory.","triggerScenarios":"User manually installed a same-named skill into Pi's skills folder; Pi (or another tool) modified the deployed copy so its hash no longer matches the SSOT source; switching sync methods (copy vs symlink) with diverged content; leftover Pi deployment from a removed SSOT skill.","commonSituations":"Power users managing Pi's skills directory by hand; Pi upgrading/rewriting skill files in place; machines migrated with only the Pi directory copied.","solutions":["Open Pi's skills directory, back up the conflicting same-named folder, then remove or rename it, and retry the install/sync","If the Pi-side content is the one you want to keep, copy it back into the SSOT directory first so hashes align","Avoid hand-editing skills inside Pi's directory; edit the SSOT copy and re-sync"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await invoke('install_skill_unified', payload);\n} catch (e) {\n  const msg = String(e);\n  if (msg.includes('Pi 中已存在同名但内容不同的 Skill')) {\n    notify('A different skill with this name exists in Pi — back it up and remove it from Pi\\'s skills dir, then retry');\n    return;\n  }\n  throw e;\n}","preventionTips":["Never manually install same-named skills into Pi's skills directory","Edit the SSOT copy and re-sync instead of editing Pi-side files","When a deliberate divergence is wanted, rename one of the two directories first"],"tags":["pi","conflict","symlink","hash","safety-guard","skill-manager"],"backgroundTag":"destination-path-conflict","analyzedSha":"0b5da510168914b251481654a568c3ffacd62cf4","analyzedAt":"2026-08-20T14:29:00.113Z","contentChangedAt":"2026-08-20T14:29:00.113Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}