1- import { readlink , rm , symlink } from 'node:fs/promises' ;
1+ import { readlink , realpath , rm , symlink } from 'node:fs/promises' ;
22import { findPackageJSON } from 'node:module' ;
33import { cwd , env , platform } from 'node:process' ;
44import { fileURLToPath , pathToFileURL } from 'node:url' ;
@@ -22,7 +22,7 @@ export async function sync(_ctx: CommandContext): Promise<void> {
2222 }
2323
2424 const root = new URL ( './' , pathToFileURL ( parentPkg ) ) ;
25- const source = new URL ( '../../skills/' , import . meta. url ) ;
25+ const source = await resolveSkillsSource ( root , new URL ( '../../skills/' , import . meta. url ) ) ;
2626
2727 if ( ! ( await hfs . isDirectory ( source ) ) ) {
2828 console . error ( 'Could not locate bundled skills directory.' ) ;
@@ -36,8 +36,15 @@ export async function sync(_ctx: CommandContext): Promise<void> {
3636 console . info ( `Synced ${ skills . length } skills to skills/` ) ;
3737}
3838
39- export async function resolveSkillsSource ( _root : URL , fallback : URL ) : Promise < URL > {
40- return fallback ;
39+ /**
40+ * Prefer linking through the project's `node_modules/@bomb.sh/tools` over
41+ * `import.meta.url`, which resolves to the real path. Under pnpm that is a
42+ * versioned `node_modules/.pnpm/<hash>/` directory, so links into it dangle
43+ * once a reinstall changes the hash.
44+ */
45+ export async function resolveSkillsSource ( root : URL , fallback : URL ) : Promise < URL > {
46+ const linked = new URL ( 'node_modules/@bomb.sh/tools/skills/' , root ) ;
47+ return ( await hfs . isDirectory ( linked ) ) ? linked : fallback ;
4148}
4249
4350interface SkillInfo {
@@ -93,14 +100,17 @@ async function pruneStaleLinks(options: {
93100 const { dest, source, keep } = options ;
94101 if ( ! ( await hfs . isDirectory ( dest ) ) ) return ;
95102
103+ // Links from older syncs may target the real path rather than `source`.
104+ const sources = [ source . href , `${ pathToFileURL ( await realpath ( source ) ) . href } /` ] ;
105+
96106 for await ( const entry of hfs . list ( dest ) ) {
97107 if ( ! entry . isSymlink ) continue ;
98108 if ( keep . has ( entry . name ) ) continue ;
99109
100110 const linkPath = fileURLToPath ( new URL ( entry . name , dest ) ) ;
101111 try {
102- const target = await readlink ( linkPath ) ;
103- if ( resolveLinkTarget ( dest , target ) . href . startsWith ( source . href ) ) {
112+ const { href } = resolveLinkTarget ( dest , await readlink ( linkPath ) ) ;
113+ if ( sources . some ( ( s ) => href . startsWith ( s ) ) ) {
104114 await hfs . deleteAll ( linkPath ) ;
105115 }
106116 } catch {
0 commit comments