@@ -6,7 +6,7 @@ import { TransformResult } from './types.js';
66import { execCommand , commandExists } from '../utils.js' ;
77
88const PLUGIN_NAME = 'syncable-cli-skills' ;
9- const PLUGIN_VERSION = '0.1.8 ' ;
9+ const PLUGIN_VERSION = '0.1.11 ' ;
1010const MARKETPLACE_NAME = 'syncable' ;
1111const MARKETPLACE_REPO = 'syncable-dev/syncable-cli' ;
1212
@@ -173,32 +173,23 @@ function enablePluginInSettings(): void {
173173 * 2. Fall back to manual: write cache files + update settings.json
174174 */
175175export async function installClaudePlugin ( skills : Skill [ ] ) : Promise < { cacheDir : string ; skillCount : number } > {
176- // Try the official CLI first — this handles enabledPlugins registration.
177- // We don't return early on success because the CLI may have cached an old
178- // version of the plugin that is missing the skills directory (e.g. from a
179- // previous install before skills were added, or from a stale npx cache).
180- // We always write skills directly to the cache so they're guaranteed to exist.
176+ // Try the official CLI first — this registers the marketplace and plugin
177+ // in Claude Code's settings. We still do a manual write afterwards because
178+ // the CLI-cached version may be stale or missing skills.
181179 await tryClaudeCliInstall ( ) ;
182180
183181 const cacheDir = getClaudePluginCacheDir ( ) ;
182+ const pluginRootDir = path . dirname ( cacheDir ) ; // .../syncable-cli-skills/
184183
185- // Remove stale older-version cache entries so Claude Code doesn't load an
186- // empty/outdated version instead of the current one.
187- const pluginRootDir = path . dirname ( cacheDir ) ;
184+ // Nuke the ENTIRE plugin cache (all versions) and recreate fresh.
185+ // This prevents version mismatches, stale caches, and — critically —
186+ // removes any .orphaned_at marker that Claude Code writes when a cached
187+ // version doesn't match the marketplace catalog.
188188 if ( fs . existsSync ( pluginRootDir ) ) {
189- for ( const entry of fs . readdirSync ( pluginRootDir ) ) {
190- if ( entry !== PLUGIN_VERSION ) {
191- fs . rmSync ( path . join ( pluginRootDir , entry ) , { recursive : true , force : true } ) ;
192- }
193- }
194- }
195-
196- // Clear old skills and rewrite them so the cache is always up to date.
197- const skillsDir = path . join ( cacheDir , 'skills' ) ;
198- if ( fs . existsSync ( skillsDir ) ) {
199- fs . rmSync ( skillsDir , { recursive : true } ) ;
189+ fs . rmSync ( pluginRootDir , { recursive : true , force : true } ) ;
200190 }
201191
192+ // Write every skill into a clean cache directory.
202193 for ( const skill of skills ) {
203194 const results = transformForClaude ( skill ) ;
204195 for ( const { relativePath, content } of results ) {
@@ -211,9 +202,32 @@ export async function installClaudePlugin(skills: Skill[]): Promise<{ cacheDir:
211202 writePluginManifest ( cacheDir ) ;
212203 enablePluginInSettings ( ) ;
213204
205+ // Also write skills to ~/.claude/skills/ for SDK-based integrations
206+ // (e.g. Zed's ACP adapter) that don't read from the plugin cache.
207+ // The SDK loads user-level skills from this directory when configured
208+ // with settingSources: ["user"].
209+ writeUserLevelSkills ( skills ) ;
210+
214211 return { cacheDir, skillCount : skills . length } ;
215212}
216213
214+ /**
215+ * Write skills to ~/.claude/skills/ so they're available to SDK-based
216+ * integrations (Zed, etc.) that don't read the plugin cache.
217+ */
218+ function writeUserLevelSkills ( skills : Skill [ ] ) : void {
219+ const userSkillsDir = path . join ( os . homedir ( ) , '.claude' , 'skills' ) ;
220+
221+ for ( const skill of skills ) {
222+ const results = transformForClaude ( skill ) ;
223+ for ( const { relativePath, content } of results ) {
224+ const fullPath = path . join ( userSkillsDir , relativePath ) ;
225+ fs . mkdirSync ( path . dirname ( fullPath ) , { recursive : true } ) ;
226+ fs . writeFileSync ( fullPath , content ) ;
227+ }
228+ }
229+ }
230+
217231/**
218232 * Remove the Claude Code plugin.
219233 */
@@ -263,19 +277,18 @@ export async function uninstallClaudePlugin(): Promise<void> {
263277 }
264278 }
265279
266- // Clean up old flat-file skills
267- const oldDirs = [
268- path . join ( os . homedir ( ) , '.claude' , 'skills' , 'syncable' ) ,
269- ] ;
270- for ( const dir of oldDirs ) {
271- if ( fs . existsSync ( dir ) ) fs . rmSync ( dir , { recursive : true } ) ;
272- }
273-
274- const flatSkillsDir = path . join ( os . homedir ( ) , '.claude' , 'skills' ) ;
275- if ( fs . existsSync ( flatSkillsDir ) ) {
276- for ( const file of fs . readdirSync ( flatSkillsDir ) ) {
277- if ( file . startsWith ( 'syncable-' ) && file . endsWith ( '.md' ) ) {
278- fs . unlinkSync ( path . join ( flatSkillsDir , file ) ) ;
280+ // Clean up user-level skills (both old flat files and new directory format)
281+ const userSkillsDir = path . join ( os . homedir ( ) , '.claude' , 'skills' ) ;
282+ if ( fs . existsSync ( userSkillsDir ) ) {
283+ for ( const entry of fs . readdirSync ( userSkillsDir ) ) {
284+ if ( entry . startsWith ( 'syncable-' ) ) {
285+ const entryPath = path . join ( userSkillsDir , entry ) ;
286+ const stat = fs . statSync ( entryPath ) ;
287+ if ( stat . isDirectory ( ) ) {
288+ fs . rmSync ( entryPath , { recursive : true } ) ;
289+ } else if ( entry . endsWith ( '.md' ) ) {
290+ fs . unlinkSync ( entryPath ) ;
291+ }
279292 }
280293 }
281294 }
0 commit comments