11import { readlink , rm , symlink } from 'node:fs/promises' ;
22import { findPackageJSON } from 'node:module' ;
33import { dirname , isAbsolute , relative , resolve } from 'node:path' ;
4- import { platform } from 'node:process' ;
4+ import { cwd , env , platform } from 'node:process' ;
55import { fileURLToPath , pathToFileURL } from 'node:url' ;
66import { NodeHfs } from '@humanfs/node' ;
77import { parse } from 'ultramatter' ;
@@ -15,7 +15,7 @@ const GITIGNORE_START = '# bsh:skills';
1515const GITIGNORE_END = '# /bsh:skills' ;
1616
1717export async function sync ( _ctx : CommandContext ) : Promise < void > {
18- const parentPkg = findParentPackage ( ) ;
18+ const parentPkg = await findParentPackage ( ) ;
1919 if ( ! parentPkg ) {
2020 console . info ( 'Skipping sync — no parent project found (running inside @bomb.sh/tools?)' ) ;
2121 return ;
@@ -174,16 +174,28 @@ function parseFrontmatter(content: string): SkillInfo | undefined {
174174 return { name, description } ;
175175}
176176
177- function findParentPackage ( ) : string | null {
178- const ownPkg = findPackageJSON ( import . meta. url ) ;
179- if ( ! ownPkg ) return null ;
180-
181- let cursor = dirname ( dirname ( ownPkg ) ) ;
182- while ( cursor !== dirname ( cursor ) ) {
183- const candidate = findPackageJSON ( pathToFileURL ( `${ cursor } /` ) ) ;
184- if ( ! candidate ) return null ;
185- if ( candidate !== ownPkg ) return candidate ;
186- cursor = dirname ( dirname ( candidate ) ) ;
177+ /**
178+ * Locate the consuming project's package.json. The project root must come
179+ * from where the command was invoked, never from this package's physical
180+ * location: under pnpm's isolated layout, import.meta.url resolves through
181+ * the node_modules symlink into node_modules/.pnpm/<hash>/, and walking up
182+ * from there lands in the store, not the user's project. INIT_CWD (set by
183+ * pnpm/npm to the directory the script was run from) is preferred because
184+ * package scripts may rewrite cwd. Returns null when no project is found or
185+ * when invoked inside @bomb.sh/tools itself.
186+ */
187+ export async function findParentPackage ( ) : Promise < string | null > {
188+ const startDir = env . INIT_CWD ?? cwd ( ) ;
189+ const candidate = findPackageJSON ( pathToFileURL ( `${ startDir } /` ) ) ;
190+ if ( ! candidate ) return null ;
191+
192+ const text = await hfs . text ( pathToFileURL ( candidate ) ) ;
193+ if ( ! text ) return null ;
194+ try {
195+ const pkg = JSON . parse ( text ) as { name ?: string } ;
196+ if ( pkg . name === '@bomb.sh/tools' ) return null ;
197+ } catch {
198+ return null ;
187199 }
188- return null ;
200+ return candidate ;
189201}
0 commit comments