Skip to content

Commit 54ab54e

Browse files
committed
refactor: centralize generated skill writes
1 parent a58f40d commit 54ab54e

11 files changed

Lines changed: 77 additions & 41 deletions

File tree

src/agent/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export { detectInstalledAgents, detectProjectAgents, detectTargetAgent, getAgent
2424
export { computeSkillDirName, installSkillForAgents, linkSkillToAgents, sanitizeName, unlinkSkillFromAgents } from './install.ts'
2525

2626
// Skill generation
27-
export { extractMarkedSections, generateSkillMd, getSectionValidator, portabilizePrompt, wrapSection } from './prompts/index.ts'
27+
export { extractMarkedSections, generateSkillMd, getSectionValidator, portabilizePrompt, wrapSection, writeGeneratedSkillMd, writeSkillMd } from './prompts/index.ts'
2828

2929
export type { SkillOptions } from './prompts/index.ts'
3030
// Registry

src/agent/prompts/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export type { CustomPrompt, SectionValidationWarning } from './optional/index.ts'
22
export { buildAllSectionPrompts, buildSectionPrompt, extractMarkedSections, getSectionValidator, portabilizePrompt, SECTION_MERGE_ORDER, SECTION_OUTPUT_FILES, wrapSection } from './prompt.ts'
33
export type { BuildSkillPromptOptions, SkillSection } from './prompt.ts'
4-
export { generateSkillMd } from './skill.ts'
4+
export { generateSkillMd, writeGeneratedSkillMd, writeSkillMd } from './skill.ts'
55
export type { SkillOptions } from './skill.ts'

src/agent/prompts/skill.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
*/
44

55
import type { FeaturesConfig } from '../../core/config.ts'
6+
import { writeFileSync } from 'node:fs'
7+
import { join } from 'pathe'
68
import { repairMarkdown, sanitizeMarkdown } from '../../core/sanitize.ts'
79
import { resolveSkilldCommand } from '../../core/shared.ts'
810
import { yamlEscape } from '../../core/yaml.ts'
@@ -42,6 +44,16 @@ export interface SkillOptions {
4244
eject?: boolean
4345
}
4446

47+
export function writeSkillMd(skillDir: string, content: string): void {
48+
writeFileSync(join(skillDir, 'SKILL.md'), content)
49+
}
50+
51+
export function writeGeneratedSkillMd(skillDir: string, opts: SkillOptions): string {
52+
const content = generateSkillMd(opts)
53+
writeSkillMd(skillDir, content)
54+
return content
55+
}
56+
4557
export function generateSkillMd(opts: SkillOptions): string {
4658
const header = generatePackageHeader(opts)
4759
const search = !opts.eject && opts.features?.search !== false ? generateSearchBlock(opts.name) : ''

src/commands/author.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import type { OptimizeModel } from '../agent/index.ts'
22
import type { FeaturesConfig } from '../core/config.ts'
33
import type { LlmConfig } from './sync-shared.ts'
4-
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
4+
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync } from 'node:fs'
55
import * as p from '@clack/prompts'
66
import { defineCommand } from 'citty'
77
import { join, relative, resolve } from 'pathe'
88
import {
99
computeSkillDirName,
10-
generateSkillMd,
1110
getModelLabel,
11+
writeGeneratedSkillMd,
1212
} from '../agent/index.ts'
1313
import {
1414
ensureCacheDir,
@@ -421,7 +421,7 @@ async function authorSinglePackage(opts: {
421421
const hasReleases = existsSync(join(cacheDir, 'releases'))
422422

423423
// Generate base SKILL.md
424-
const baseSkillMd = generateSkillMd({
424+
writeGeneratedSkillMd(outDir, {
425425
name: packageName,
426426
version,
427427
description: opts.description,
@@ -438,7 +438,6 @@ async function authorSinglePackage(opts: {
438438
features,
439439
eject: true,
440440
})
441-
writeFileSync(join(outDir, 'SKILL.md'), baseSkillMd)
442441
p.log.success(`Created base skill: ${relative(packageDir, outDir)}`)
443442

444443
// LLM enhancement (config resolved by caller)

src/commands/install.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import * as p from '@clack/prompts'
1818
import { defineCommand } from 'citty'
1919
import { dirname, join } from 'pathe'
2020
import { agents, createToolProgress, getModelLabel, linkSkillToAgents, optimizeDocs } from '../agent/index.ts'
21-
import { generateSkillMd } from '../agent/prompts/skill.ts'
21+
import { writeGeneratedSkillMd, writeSkillMd } from '../agent/prompts/skill.ts'
2222
import {
2323
hasShippedDocs as checkShippedDocs,
2424
ensureCacheDir,
@@ -170,7 +170,7 @@ export async function installCommand(opts: InstallOptions): Promise<void> {
170170
if (match) {
171171
const skillDir = join(skillsDir, name)
172172
mkdirSync(skillDir, { recursive: true })
173-
writeFileSync(join(skillDir, 'SKILL.md'), sanitizeMarkdown(match.content))
173+
writeSkillMd(skillDir, sanitizeMarkdown(match.content))
174174
for (const f of match.files) {
175175
const filePath = join(skillDir, f.path)
176176
mkdirSync(dirname(filePath), { recursive: true })
@@ -597,7 +597,7 @@ async function enhanceRegenerated(
597597
const dirName = skillDir.split('/').pop()
598598

599599
const allPackages = parsePackages(packages).map(p => ({ name: p.name }))
600-
const skillMd = generateSkillMd({
600+
writeGeneratedSkillMd(skillDir, {
601601
name: pkgName,
602602
version,
603603
description,
@@ -613,7 +613,6 @@ async function enhanceRegenerated(
613613
packages: allPackages.length > 1 ? allPackages : undefined,
614614
features,
615615
})
616-
writeFileSync(join(skillDir, 'SKILL.md'), skillMd)
617616
}
618617
else {
619618
llmLog.message('Enhancement skipped')
@@ -689,7 +688,8 @@ function regenerateBaseSkillMd(
689688
// Build multi-package list from lockfile packages field
690689
const allPackages = parsePackages(packages).map(p => ({ name: p.name }))
691690

692-
const content = generateSkillMd({
691+
mkdirSync(skillDir, { recursive: true })
692+
writeGeneratedSkillMd(skillDir, {
693693
name: pkgName,
694694
version,
695695
description,
@@ -705,8 +705,6 @@ function regenerateBaseSkillMd(
705705
features: readConfig().features ?? defaultFeatures,
706706
})
707707

708-
mkdirSync(skillDir, { recursive: true })
709-
writeFileSync(skillMdPath, content)
710708
return true
711709
}
712710

src/commands/sync-git.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ import * as p from '@clack/prompts'
1010
import { dirname, join, relative } from 'pathe'
1111
import {
1212
agents,
13-
generateSkillMd,
1413
getModelLabel,
1514
linkSkillToAgents,
1615
sanitizeName,
16+
writeGeneratedSkillMd,
17+
writeSkillMd,
1718
} from '../agent/index.ts'
1819
import {
1920
CACHE_DIR,
@@ -134,7 +135,7 @@ export async function syncGitSkills(opts: GitSyncOptions): Promise<void> {
134135
mkdirSync(skillDir, { recursive: true })
135136

136137
// Sanitize and write SKILL.md
137-
writeFileSync(join(skillDir, 'SKILL.md'), sanitizeMarkdown(skill.content))
138+
writeSkillMd(skillDir, sanitizeMarkdown(skill.content))
138139

139140
// Write supporting files directly in skill dir (not under .skilld/)
140141
// so SKILL.md relative paths like ./references/docs/guide.md resolve correctly
@@ -275,7 +276,7 @@ async function syncGitHubRepo(opts: GitSyncOptions): Promise<void> {
275276
})
276277

277278
// Write base SKILL.md
278-
const baseSkillMd = generateSkillMd({
279+
writeGeneratedSkillMd(skillDir, {
279280
name: packageName,
280281
version,
281282
releasedAt: resolved.releasedAt,
@@ -292,7 +293,6 @@ async function syncGitHubRepo(opts: GitSyncOptions): Promise<void> {
292293
repoUrl,
293294
features,
294295
})
295-
writeFileSync(join(skillDir, 'SKILL.md'), baseSkillMd)
296296

297297
p.log.success(`Created base skill: ${relative(cwd, skillDir)}`)
298298

src/commands/sync-parallel.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { AgentType, CustomPrompt, OptimizeModel, SkillSection } from '../agent/index.ts'
22
import type { FeaturesConfig } from '../core/config.ts'
33
import type { ResolvedPackage } from '../sources/index.ts'
4-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
4+
import { existsSync, mkdirSync, readFileSync } from 'node:fs'
55
import * as p from '@clack/prompts'
66
import logUpdate from 'log-update'
77
import pLimit from 'p-limit'
@@ -10,13 +10,13 @@ import {
1010
agents,
1111
computeSkillDirName,
1212

13-
generateSkillMd,
1413
getModelLabel,
1514
linkSkillToAgents,
1615
optimizeDocs,
1716
SECTION_MERGE_ORDER,
1817
SECTION_OUTPUT_FILES,
1918
wrapSection,
19+
writeGeneratedSkillMd,
2020

2121
} from '../agent/index.ts'
2222
import {
@@ -262,7 +262,7 @@ export async function syncPackagesParallel(config: ParallelSyncConfig): Promise<
262262
}
263263
const cachedBody = cachedParts.join('\n\n')
264264

265-
const skillMd = generateSkillMd({
265+
writeGeneratedSkillMd(skillDir, {
266266
name: resolvedName,
267267
version: data.version,
268268
releasedAt: data.resolved.releasedAt,
@@ -283,7 +283,6 @@ export async function syncPackagesParallel(config: ParallelSyncConfig): Promise<
283283
repoUrl: data.resolved.repoUrl,
284284
features: data.features,
285285
})
286-
writeFileSync(join(skillDir, 'SKILL.md'), skillMd)
287286
cachedPkgs.push(pkg)
288287
}
289288
}
@@ -574,7 +573,7 @@ async function syncBaseSkill(
574573
const updatedLock = readLock(baseDir)?.skills[skillDirName]
575574
const allPackages = parsePackages(updatedLock?.packages).map(p => ({ name: p.name }))
576575

577-
const skillMd = generateSkillMd({
576+
const skillMd = writeGeneratedSkillMd(skillDir, {
578577
name: packageName,
579578
version,
580579
releasedAt: resolved.releasedAt,
@@ -594,7 +593,6 @@ async function syncBaseSkill(
594593
repoUrl: resolved.repoUrl,
595594
features,
596595
})
597-
writeFileSync(join(skillDir, 'SKILL.md'), skillMd)
598596
const overheadLines = skillMd.split('\n').length
599597

600598
// Link shared dir to per-agent dirs
@@ -682,7 +680,7 @@ async function enhanceWithLLM(
682680
}
683681

684682
if (wasOptimized) {
685-
const skillMd = generateSkillMd({
683+
writeGeneratedSkillMd(skillDir, {
686684
name: packageName,
687685
version: data.version,
688686
releasedAt: data.resolved.releasedAt,
@@ -701,7 +699,6 @@ async function enhanceWithLLM(
701699
repoUrl: data.resolved.repoUrl,
702700
features: data.features,
703701
})
704-
writeFileSync(join(skillDir, 'SKILL.md'), skillMd)
705702
}
706703

707704
update(packageName, 'done', 'Skill optimized', versionKey)

src/commands/sync-registry.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@
99

1010
import type { AgentType } from '../agent/index.ts'
1111
import type { RegistrySkill } from '../registry/client.ts'
12-
import { mkdirSync, writeFileSync } from 'node:fs'
12+
import { mkdirSync } from 'node:fs'
1313
import { join } from 'pathe'
1414
import { linkSkillToAgents } from '../agent/install.ts'
15+
import { writeSkillMd } from '../agent/prompts/skill.ts'
1516
import { writeLock } from '../core/lockfile.ts'
1617
import { SHARED_SKILLS_DIR } from '../core/shared.ts'
1718
import { fetchRegistrySkill } from '../registry/client.ts'
@@ -38,7 +39,7 @@ export async function syncRegistrySkill(opts: SyncRegistryOptions): Promise<Regi
3839
const sharedDir = join(cwd, SHARED_SKILLS_DIR)
3940
const skillDir = join(sharedDir, skill.name)
4041
mkdirSync(skillDir, { recursive: true })
41-
writeFileSync(join(skillDir, 'SKILL.md'), skill.content)
42+
writeSkillMd(skillDir, skill.content)
4243

4344
// Update lockfile
4445
const baseDir = join(cwd, '.claude', 'skills')

src/commands/sync-shared.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ import {
88
agents,
99
buildAllSectionPrompts,
1010
createToolProgress,
11-
generateSkillMd,
1211
getAvailableModels,
1312
getModelLabel,
1413
getModelName,
1514
optimizeDocs,
1615
SECTION_OUTPUT_FILES,
16+
writeGeneratedSkillMd,
1717
} from '../agent/index.ts'
1818
import { maxItems, maxLines } from '../agent/prompts/optional/budget.ts'
1919
import {
@@ -1430,7 +1430,7 @@ export async function enhanceSkillWithLLM(opts: EnhanceOptions): Promise<void> {
14301430
for (const w of warnings)
14311431
p.log.warn(`\x1B[33m${w}\x1B[0m`)
14321432
}
1433-
const skillMd = generateSkillMd({
1433+
writeGeneratedSkillMd(skillDir, {
14341434
name: packageName,
14351435
version,
14361436
releasedAt: resolved.releasedAt,
@@ -1452,7 +1452,6 @@ export async function enhanceSkillWithLLM(opts: EnhanceOptions): Promise<void> {
14521452
features,
14531453
eject,
14541454
})
1455-
writeFileSync(join(skillDir, 'SKILL.md'), skillMd)
14561455
}
14571456
else {
14581457
if (error && /\b429\b|rate.?limit|exhausted.*capacity|quota.*reset/i.test(error))

src/commands/sync.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {
1111
buildAllSectionPrompts,
1212
computeSkillDirName,
1313
detectImportedPackages,
14-
generateSkillMd,
1514
getAvailableModels,
1615
getModelLabel,
1716
linkSkillToAgents,
@@ -20,6 +19,7 @@ import {
2019
SECTION_MERGE_ORDER,
2120
SECTION_OUTPUT_FILES,
2221
wrapSection,
22+
writeGeneratedSkillMd,
2323
} from '../agent/index.ts'
2424
import {
2525
ensureCacheDir,
@@ -479,7 +479,7 @@ async function syncSinglePackage(packageSpec: string, config: SyncConfig): Promi
479479
const shippedDocs = hasShippedDocs(existingStorageName, cwd, existingLock.version)
480480

481481
const mergeFeatures = readConfig().features ?? defaultFeatures
482-
const skillMd = generateSkillMd({
482+
writeGeneratedSkillMd(skillDir, {
483483
name: existingLock.packageName!,
484484
version: existingLock.version,
485485
relatedSkills,
@@ -493,7 +493,6 @@ async function syncSinglePackage(packageSpec: string, config: SyncConfig): Promi
493493
packages: allPackages,
494494
features: mergeFeatures,
495495
})
496-
writeFileSync(join(skillDir, 'SKILL.md'), skillMd)
497496

498497
const mergeShared = !config.global && getSharedSkillsDir(cwd)
499498
if (mergeShared)
@@ -604,7 +603,7 @@ async function syncSinglePackage(packageSpec: string, config: SyncConfig): Promi
604603
const allPackages = parsePackages(updatedLock?.packages).map(p => ({ name: p.name }))
605604

606605
const isEject = !!config.eject
607-
const baseSkillMd = generateSkillMd({
606+
const baseSkillMd = writeGeneratedSkillMd(skillDir, {
608607
name: identityPackageName,
609608
version,
610609
releasedAt: resolved.releasedAt,
@@ -625,7 +624,6 @@ async function syncSinglePackage(packageSpec: string, config: SyncConfig): Promi
625624
features,
626625
eject: isEject,
627626
})
628-
writeFileSync(join(skillDir, 'SKILL.md'), baseSkillMd)
629627
const overheadLines = baseSkillMd.split('\n').length
630628

631629
p.log.success(config.mode === 'update' ? `Updated skill: ${relative(cwd, skillDir)}` : `Created base skill: ${relative(cwd, skillDir)}`)
@@ -649,7 +647,7 @@ async function syncSinglePackage(packageSpec: string, config: SyncConfig): Promi
649647
}
650648
const cachedBody = cachedParts.join('\n\n')
651649

652-
const skillMd = generateSkillMd({
650+
writeGeneratedSkillMd(skillDir, {
653651
name: identityPackageName,
654652
version,
655653
releasedAt: resolved.releasedAt,
@@ -672,7 +670,6 @@ async function syncSinglePackage(packageSpec: string, config: SyncConfig): Promi
672670
features,
673671
eject: isEject,
674672
})
675-
writeFileSync(join(skillDir, 'SKILL.md'), skillMd)
676673
p.log.success('Applied cached SKILL.md sections')
677674
}
678675

@@ -1230,7 +1227,7 @@ export async function exportPortablePrompts(packageSpec: string, opts: {
12301227

12311228
// Generate SKILL.md (ejected — uses ./references/ paths)
12321229
const relatedSkills = await findRelatedSkills(packageName, join(skillDir, '..'))
1233-
const skillMd = generateSkillMd({
1230+
writeGeneratedSkillMd(skillDir, {
12341231
name: packageName,
12351232
version,
12361233
releasedAt: resolved.releasedAt,
@@ -1249,7 +1246,6 @@ export async function exportPortablePrompts(packageSpec: string, opts: {
12491246
features,
12501247
eject: true,
12511248
})
1252-
writeFileSync(join(skillDir, 'SKILL.md'), skillMd)
12531249

12541250
// Write lockfile so skilld list/update/assemble can discover this skill
12551251
const repoSlug = resolved.repoUrl?.match(/github\.com\/([^/]+\/[^/]+?)(?:\.git)?(?:[/#]|$)/)?.[1]

0 commit comments

Comments
 (0)