@@ -113,7 +113,7 @@ async function recordSourceProvenance(opts: {
113113 } ) ;
114114}
115115
116- function resolveFetchedSourcePath ( source : string , cachePath : string ) : string {
116+ export function resolveFetchedSourcePath ( source : string , cachePath : string ) : string {
117117 if ( ! isGitHubUrl ( source ) ) return cachePath ;
118118 const parsed = parseGitHubUrl ( source ) ;
119119 return parsed ?. subpath ? join ( cachePath , parsed . subpath ) : cachePath ;
@@ -458,21 +458,32 @@ type InstallSkillResult =
458458 *
459459 * In both cases, set the plugin to allowlist mode with only the requested skill.
460460 */
461- async function installSkillFromSource ( opts : {
461+ type InstallSkillFromSourceDeps = {
462+ fetchPlugin ?: typeof fetchPlugin ;
463+ parseMarketplaceManifest ?: typeof parseMarketplaceManifest ;
464+ installSkillViaMarketplace ?: typeof installSkillViaMarketplace ;
465+ installSkillDirect ?: typeof installSkillDirect ;
466+ } ;
467+
468+ export async function installSkillFromSource ( opts : {
462469 skill : string ;
463470 from : string ;
464471 isUser : boolean ;
465472 workspacePath : string ;
466- } ) : Promise < InstallSkillResult > {
473+ } , deps : InstallSkillFromSourceDeps = { } ) : Promise < InstallSkillResult > {
467474 const { skill, from, isUser, workspacePath } = opts ;
475+ const fetchPluginFn = deps . fetchPlugin ?? fetchPlugin ;
476+ const parseMarketplaceManifestFn = deps . parseMarketplaceManifest ?? parseMarketplaceManifest ;
477+ const installSkillViaMarketplaceFn = deps . installSkillViaMarketplace ?? installSkillViaMarketplace ;
478+ const installSkillDirectFn = deps . installSkillDirect ?? installSkillDirect ;
468479
469480 if ( ! isJsonMode ( ) ) {
470481 console . log ( `Installing skill '${ skill } ' from ${ from } ...` ) ;
471482 }
472483
473484 // Fetch the source to a local cache so we can inspect it
474485 const parsed = isGitHubUrl ( from ) ? parseGitHubUrl ( from ) : null ;
475- const fetchResult = await fetchPlugin ( from , {
486+ const fetchResult = await fetchPluginFn ( from , {
476487 ...( parsed ?. branch && { branch : parsed . branch } ) ,
477488 } ) ;
478489 if ( ! fetchResult . success ) {
@@ -482,14 +493,14 @@ async function installSkillFromSource(opts: {
482493 const sourcePath = resolveFetchedSourcePath ( from , fetchResult . cachePath ) ;
483494
484495 // Check if the source is a marketplace
485- const manifestResult = await parseMarketplaceManifest ( sourcePath ) ;
496+ const manifestResult = await parseMarketplaceManifestFn ( sourcePath ) ;
486497
487498 if ( manifestResult . success ) {
488- return installSkillViaMarketplace ( { skill, from, isUser, workspacePath } ) ;
499+ return installSkillViaMarketplaceFn ( { skill, from, isUser, workspacePath } ) ;
489500 }
490501
491502 // Not a marketplace — install as a direct plugin
492- return installSkillDirect ( { skill, from, isUser, workspacePath, cachePath : sourcePath } ) ;
503+ return installSkillDirectFn ( { skill, from, isUser, workspacePath, sourcePath } ) ;
493504}
494505
495506/**
@@ -593,10 +604,9 @@ async function installSkillDirect(opts: {
593604 from : string ;
594605 isUser : boolean ;
595606 workspacePath : string ;
596- cachePath : string ;
607+ sourcePath : string ;
597608} ) : Promise < InstallSkillResult > {
598- const { skill, from, isUser, workspacePath, cachePath } = opts ;
599- const sourcePath = resolveFetchedSourcePath ( from , cachePath ) ;
609+ const { skill, from, isUser, workspacePath, sourcePath } = opts ;
600610
601611 // Verify the skill exists in the cached plugin before installing
602612 const availableSkills = await discoverSkillNames ( sourcePath ) ;
0 commit comments