@@ -14,6 +14,9 @@ import {
1414 getModelLabel ,
1515 linkSkillToAgents ,
1616 optimizeDocs ,
17+ SECTION_MERGE_ORDER ,
18+ SECTION_OUTPUT_FILES ,
19+ wrapSection ,
1720
1821} from '../agent/index.ts'
1922import {
@@ -25,6 +28,7 @@ import {
2528 isCached ,
2629 linkPkgNamed ,
2730 listReferenceFiles ,
31+ readCachedSection ,
2832 resolvePkgDir ,
2933} from '../cache/index.ts'
3034import { defaultFeatures , readConfig , registerProject } from '../core/config.ts'
@@ -52,7 +56,7 @@ import {
5256 resolveBaseDir ,
5357 resolveLocalDep ,
5458} from './sync-shared.ts'
55- import { ensureAgentInstructions , ensureGitignore , selectLlmConfig , writePromptFiles } from './sync.ts'
59+ import { DEFAULT_SECTIONS , ensureAgentInstructions , ensureGitignore , selectLlmConfig , writePromptFiles } from './sync.ts'
5660
5761type PackageStatus = 'pending' | 'resolving' | 'downloading' | 'embedding' | 'exploring' | 'thinking' | 'generating' | 'done' | 'error'
5862
@@ -221,13 +225,68 @@ export async function syncPackagesParallel(config: ParallelSyncConfig): Promise<
221225 }
222226 }
223227
224- // Phase 2: Ask about LLM enhancement (skip if -y without model, or skipLlm config)
228+ // Apply cached LLM sections for packages that have all sections cached
229+ const cachedPkgs : string [ ] = [ ]
230+ if ( ! config . force ) {
231+ for ( const pkg of successfulPkgs ) {
232+ const data = skillData . get ( pkg ) !
233+ const resolvedName = data . resolved . name
234+ const allCached = DEFAULT_SECTIONS . every ( ( s ) => {
235+ const outputFile = SECTION_OUTPUT_FILES [ s ]
236+ return readCachedSection ( resolvedName , data . version , outputFile ) !== null
237+ } )
238+ if ( allCached ) {
239+ const baseDir = resolveBaseDir ( cwd , config . agent , config . global )
240+ const skillDir = join ( baseDir , data . skillDirName )
241+ const cachedParts : string [ ] = [ ]
242+ for ( const s of SECTION_MERGE_ORDER ) {
243+ if ( ! DEFAULT_SECTIONS . includes ( s ) )
244+ continue
245+ const outputFile = SECTION_OUTPUT_FILES [ s ]
246+ const content = readCachedSection ( resolvedName , data . version , outputFile )
247+ if ( content )
248+ cachedParts . push ( wrapSection ( s , content ) )
249+ }
250+ const cachedBody = cachedParts . join ( '\n\n' )
251+
252+ const skillMd = generateSkillMd ( {
253+ name : resolvedName ,
254+ version : data . version ,
255+ releasedAt : data . resolved . releasedAt ,
256+ dependencies : data . resolved . dependencies ,
257+ distTags : data . resolved . distTags ,
258+ body : cachedBody ,
259+ relatedSkills : data . relatedSkills ,
260+ hasIssues : data . hasIssues ,
261+ hasDiscussions : data . hasDiscussions ,
262+ hasReleases : data . hasReleases ,
263+ hasChangelog : data . hasChangelog ,
264+ docsType : data . docsType ,
265+ hasShippedDocs : data . shippedDocs ,
266+ pkgFiles : data . pkgFiles ,
267+ generatedBy : 'cached' ,
268+ dirName : data . skillDirName ,
269+ packages : data . packages ,
270+ repoUrl : data . resolved . repoUrl ,
271+ features : data . features ,
272+ } )
273+ writeFileSync ( join ( skillDir , 'SKILL.md' ) , skillMd )
274+ cachedPkgs . push ( pkg )
275+ }
276+ }
277+ }
278+
279+ const uncachedPkgs = successfulPkgs . filter ( pkg => ! cachedPkgs . includes ( pkg ) )
280+ if ( cachedPkgs . length > 0 )
281+ p . log . success ( `Applied cached SKILL.md sections for ${ cachedPkgs . join ( ', ' ) } ` )
282+
283+ // Phase 2: Ask about LLM enhancement (skip if -y without model, skipLlm config, or all cached)
225284 const globalConfig = readConfig ( )
226- if ( successfulPkgs . length > 0 && ! globalConfig . skipLlm && ! ( config . yes && ! config . model ) ) {
285+ if ( uncachedPkgs . length > 0 && ! globalConfig . skipLlm && ! ( config . yes && ! config . model ) ) {
227286 const llmConfig = await selectLlmConfig ( config . model )
228287
229288 if ( llmConfig ?. promptOnly ) {
230- for ( const pkg of successfulPkgs ) {
289+ for ( const pkg of uncachedPkgs ) {
231290 const data = skillData . get ( pkg ) !
232291 const baseDir = resolveBaseDir ( cwd , config . agent , config . global )
233292 const skillDir = join ( baseDir , data . skillDirName )
@@ -251,21 +310,21 @@ export async function syncPackagesParallel(config: ParallelSyncConfig): Promise<
251310 else if ( llmConfig ) {
252311 p . log . step ( getModelLabel ( llmConfig . model ) )
253312 // Reset states for LLM phase
254- for ( const pkg of successfulPkgs ) {
313+ for ( const pkg of uncachedPkgs ) {
255314 states . set ( pkg , { name : pkg , status : 'pending' , message : 'Waiting...' } )
256315 }
257316 render ( )
258317
259318 const llmResults = await Promise . allSettled (
260- successfulPkgs . map ( pkg =>
319+ uncachedPkgs . map ( pkg =>
261320 limit ( ( ) => enhanceWithLLM ( pkg , skillData . get ( pkg ) ! , { ...config , model : llmConfig . model } , cwd , update , llmConfig . sections , llmConfig . customPrompt ) ) ,
262321 ) ,
263322 )
264323
265324 logUpdate . done ( )
266325
267326 const llmSucceeded = llmResults . filter ( r => r . status === 'fulfilled' ) . length
268- p . log . success ( `Enhanced ${ llmSucceeded } /${ successfulPkgs . length } skills with LLM` )
327+ p . log . success ( `Enhanced ${ llmSucceeded } /${ uncachedPkgs . length } skills with LLM` )
269328 }
270329 }
271330
0 commit comments