@vibe-forge/workspace-assets 3.2.1 → 3.2.2-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/adapter-asset-plan.spec.ts +0 -58
- package/__tests__/bundle.spec.ts +10 -303
- package/__tests__/skill-dependencies-cli.spec.ts +72 -234
- package/package.json +5 -5
- package/src/bundle-internal.ts +63 -10
- package/src/bundle.ts +1 -0
- package/src/configured-skills.ts +24 -30
- package/src/plugin-skill-dependencies.ts +1 -0
- package/src/prompt-selection.ts +3 -3
- package/src/selection-internal.ts +45 -5
- package/src/skill-dependencies.ts +10 -108
- package/src/skills-cli-dependency-helpers.ts +0 -94
- package/src/skills-cli-dependency.ts +0 -133
package/src/prompt-selection.ts
CHANGED
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
resolveEntityInheritance,
|
|
30
30
|
resolveExcludedSkillRefs,
|
|
31
31
|
resolveIncludedSkillRefs,
|
|
32
|
-
|
|
32
|
+
resolveNamedSkillAssets,
|
|
33
33
|
resolvePluginOverlay,
|
|
34
34
|
resolveRuleSelection,
|
|
35
35
|
resolveSelectedSkillAssetsWithDependencies,
|
|
@@ -158,10 +158,10 @@ export async function resolvePromptAssetSelection(params: {
|
|
|
158
158
|
const includedAssets = skillSelection == null
|
|
159
159
|
? []
|
|
160
160
|
: includedRefs != null
|
|
161
|
-
? (includedRefs.length > 0 ?
|
|
161
|
+
? (includedRefs.length > 0 ? resolveNamedSkillAssets(effectiveBundle, includedRefs, targetInstancePath) : [])
|
|
162
162
|
: effectiveBundle.skills
|
|
163
163
|
const excludedIds = new Set(
|
|
164
|
-
|
|
164
|
+
resolveNamedSkillAssets(effectiveBundle, excludedRefs, targetInstancePath).map(asset => asset.id)
|
|
165
165
|
)
|
|
166
166
|
|
|
167
167
|
const expandedTargetSkills = await expandSkillAssetDependenciesWithRemoteResolution({
|
|
@@ -15,7 +15,7 @@ import type {
|
|
|
15
15
|
WorkspaceMcpSelection,
|
|
16
16
|
WorkspaceSkillSelection
|
|
17
17
|
} from '@vibe-forge/types'
|
|
18
|
-
import { normalizePath } from '@vibe-forge/utils'
|
|
18
|
+
import { matchesDeclaredSkillSelector, normalizePath } from '@vibe-forge/utils'
|
|
19
19
|
import { mergePluginConfigs, normalizePluginConfig } from '@vibe-forge/utils/plugin-resolver'
|
|
20
20
|
import { glob } from 'fast-glob'
|
|
21
21
|
|
|
@@ -26,6 +26,8 @@ import {
|
|
|
26
26
|
isRemoteRuleReference,
|
|
27
27
|
parseScopedReference
|
|
28
28
|
} from '@vibe-forge/definition-core'
|
|
29
|
+
import { resolveConfiguredProjectSkillInstalls } from './configured-skills'
|
|
30
|
+
import { PLUGIN_SKILL_DEPENDENCY_RESOLVED_BY } from './plugin-skill-dependencies'
|
|
29
31
|
import { expandSkillAssetDependencies, expandSkillAssetDependenciesWithRemoteResolution } from './skill-dependencies'
|
|
30
32
|
|
|
31
33
|
type DocumentAssetKind = Extract<WorkspaceAssetKind, 'rule' | 'spec' | 'entity' | 'skill'>
|
|
@@ -359,6 +361,10 @@ const mergeEntityDefinitions = (
|
|
|
359
361
|
|
|
360
362
|
const uniqueAssetIds = (values: string[]) => toUniqueValues(values, value => value)
|
|
361
363
|
|
|
364
|
+
const isPluginSkillDependencyAsset = (asset: Extract<WorkspaceAsset, { kind: 'skill' }>) => (
|
|
365
|
+
asset.resolvedBy === PLUGIN_SKILL_DEPENDENCY_RESOLVED_BY
|
|
366
|
+
)
|
|
367
|
+
|
|
362
368
|
const formatEntityCycle = (stack: EntityAsset[], asset: EntityAsset) => (
|
|
363
369
|
[...stack.slice(stack.findIndex(item => item.id === asset.id)), asset]
|
|
364
370
|
.map(item => item.displayName)
|
|
@@ -511,15 +517,48 @@ export const resolveExcludedSkillRefs = (selection: string[] | SkillSelection |
|
|
|
511
517
|
return selection.type === 'exclude' ? selection.list : undefined
|
|
512
518
|
}
|
|
513
519
|
|
|
520
|
+
const createMissingConfiguredSkillMessage = (
|
|
521
|
+
bundle: WorkspaceAssetBundle,
|
|
522
|
+
refs: string[] | undefined
|
|
523
|
+
) => {
|
|
524
|
+
const configs = bundle.configs ?? [undefined, undefined]
|
|
525
|
+
const declared = resolveConfiguredProjectSkillInstalls(configs)
|
|
526
|
+
|
|
527
|
+
for (const ref of refs ?? []) {
|
|
528
|
+
const match = declared.find(skill => matchesDeclaredSkillSelector(ref, skill))
|
|
529
|
+
if (match == null) continue
|
|
530
|
+
|
|
531
|
+
return `Skill ${ref} is declared in config but is not installed. ` +
|
|
532
|
+
`Run \`vf skills install ${match.targetName}\` or \`vf skills install\` before selecting it.`
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
return undefined
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
export const resolveNamedSkillAssets = (
|
|
539
|
+
bundle: WorkspaceAssetBundle,
|
|
540
|
+
refs: string[] | undefined,
|
|
541
|
+
currentInstancePath?: string
|
|
542
|
+
) => {
|
|
543
|
+
try {
|
|
544
|
+
return resolveNamedAssets(bundle.skills, refs, currentInstancePath)
|
|
545
|
+
} catch (error) {
|
|
546
|
+
const message = createMissingConfiguredSkillMessage(bundle, refs)
|
|
547
|
+
if (message != null) throw new Error(message)
|
|
548
|
+
throw error
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
514
552
|
export const resolveSelectedSkillAssets = (
|
|
515
553
|
assets: Array<Extract<WorkspaceAsset, { kind: 'skill' }>>,
|
|
516
554
|
selection?: WorkspaceSkillSelection
|
|
517
555
|
): Array<Extract<WorkspaceAsset, { kind: 'skill' }>> => {
|
|
518
556
|
if (selection == null) return assets
|
|
519
557
|
|
|
558
|
+
const rootAssets = assets.filter(asset => !isPluginSkillDependencyAsset(asset))
|
|
520
559
|
const included = selection.include != null && selection.include.length > 0
|
|
521
560
|
? resolveNamedAssets(assets, selection.include)
|
|
522
|
-
:
|
|
561
|
+
: rootAssets
|
|
523
562
|
const excluded = new Set(
|
|
524
563
|
resolveNamedAssets(assets, selection.exclude).map(asset => asset.id)
|
|
525
564
|
)
|
|
@@ -532,11 +571,12 @@ export const resolveSelectedSkillAssetsWithDependencies = async (
|
|
|
532
571
|
bundle: WorkspaceAssetBundle,
|
|
533
572
|
selection?: WorkspaceSkillSelection
|
|
534
573
|
): Promise<Array<Extract<WorkspaceAsset, { kind: 'skill' }>>> => {
|
|
574
|
+
const rootAssets = bundle.skills.filter(asset => !isPluginSkillDependencyAsset(asset))
|
|
535
575
|
const included = selection?.include != null && selection.include.length > 0
|
|
536
|
-
?
|
|
537
|
-
:
|
|
576
|
+
? resolveNamedSkillAssets(bundle, selection.include)
|
|
577
|
+
: rootAssets
|
|
538
578
|
const excluded = new Set(
|
|
539
|
-
|
|
579
|
+
resolveNamedSkillAssets(bundle, selection?.exclude).map(asset => asset.id)
|
|
540
580
|
)
|
|
541
581
|
return await expandSkillAssetDependenciesWithRemoteResolution({
|
|
542
582
|
allAssets: bundle.assets,
|
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
/* eslint-disable max-lines -- dependency normalization and graph expansion share the same local helpers */
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
import { parseScopedReference, resolveSkillIdentifier } from '@vibe-forge/definition-core'
|
|
7
|
-
import type { Config, Definition, Skill, WorkspaceAsset } from '@vibe-forge/types'
|
|
8
|
-
import { formatSkillsSpec, parseSkillsSpec, resolveRelativePath } from '@vibe-forge/utils'
|
|
2
|
+
import { parseScopedReference } from '@vibe-forge/definition-core'
|
|
3
|
+
import type { Config, Skill, WorkspaceAsset } from '@vibe-forge/types'
|
|
4
|
+
import { formatSkillsSpec, parseSkillsSpec } from '@vibe-forge/utils'
|
|
9
5
|
|
|
10
6
|
import { HOME_BRIDGE_RESOLVED_BY } from './home-bridge'
|
|
11
|
-
import { installSkillsCliDependency } from './skills-cli-dependency'
|
|
12
7
|
|
|
13
8
|
type SkillAsset = Extract<WorkspaceAsset, { kind: 'skill' }>
|
|
14
9
|
|
|
@@ -106,39 +101,6 @@ const findSkillAssetByRef = (
|
|
|
106
101
|
return resolveUniqueSkillByName(searchableAssets, ref)
|
|
107
102
|
}
|
|
108
103
|
|
|
109
|
-
const resolveDisplayName = (name: string, scope?: string) => (
|
|
110
|
-
scope != null && scope.trim() !== '' ? `${scope}/${name}` : name
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
const parseFrontmatterSkill = async (path: string): Promise<Definition<Skill>> => {
|
|
114
|
-
const content = await readFile(path, 'utf-8')
|
|
115
|
-
const { body, attributes } = fm<Skill>(content)
|
|
116
|
-
return {
|
|
117
|
-
path,
|
|
118
|
-
body,
|
|
119
|
-
attributes
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const createResolvedSkillAsset = (params: {
|
|
124
|
-
cwd: string
|
|
125
|
-
definition: Definition<Skill>
|
|
126
|
-
}) => {
|
|
127
|
-
const name = resolveSkillIdentifier(params.definition.path, params.definition.attributes.name)
|
|
128
|
-
const displayName = resolveDisplayName(name)
|
|
129
|
-
return {
|
|
130
|
-
id: `skill:workspace:workspace:${displayName}:${resolveRelativePath(params.cwd, params.definition.path)}`,
|
|
131
|
-
kind: 'skill',
|
|
132
|
-
name,
|
|
133
|
-
displayName,
|
|
134
|
-
origin: 'workspace',
|
|
135
|
-
sourcePath: params.definition.path,
|
|
136
|
-
payload: {
|
|
137
|
-
definition: params.definition
|
|
138
|
-
}
|
|
139
|
-
} satisfies SkillAsset
|
|
140
|
-
}
|
|
141
|
-
|
|
142
104
|
export const normalizeSkillDependency = (value: unknown): NormalizedSkillDependency | undefined => {
|
|
143
105
|
const stringValue = asNonEmptyString(value)
|
|
144
106
|
if (stringValue != null) return parseSkillsSpec(stringValue)
|
|
@@ -230,8 +192,6 @@ export const expandSkillAssetDependenciesWithRemoteResolution = async (
|
|
|
230
192
|
) => {
|
|
231
193
|
const selected: SkillAsset[] = []
|
|
232
194
|
const seen = new Set<string>()
|
|
233
|
-
const fetchedDependencyRefs = new Set<string>()
|
|
234
|
-
|
|
235
195
|
const removeSupersededHomeBridgeSkill = (displayName: string) => {
|
|
236
196
|
removeHomeBridgeSkillDuplicates(params.allAssets, displayName)
|
|
237
197
|
removeHomeBridgeSkillDuplicates(params.skillAssets, displayName)
|
|
@@ -239,54 +199,6 @@ export const expandSkillAssetDependenciesWithRemoteResolution = async (
|
|
|
239
199
|
removeHomeBridgeSkillDuplicates(selected, displayName)
|
|
240
200
|
}
|
|
241
201
|
|
|
242
|
-
const installDependencyAsset = async (
|
|
243
|
-
dependency: NormalizedSkillDependency,
|
|
244
|
-
currentInstancePath?: string
|
|
245
|
-
) => {
|
|
246
|
-
const fetchKey = dependency.ref
|
|
247
|
-
if (!fetchedDependencyRefs.has(fetchKey)) {
|
|
248
|
-
fetchedDependencyRefs.add(fetchKey)
|
|
249
|
-
const installed = await installSkillsCliDependency({
|
|
250
|
-
cwd: params.cwd,
|
|
251
|
-
configs: params.configs,
|
|
252
|
-
dependency
|
|
253
|
-
})
|
|
254
|
-
const definition = await parseFrontmatterSkill(installed.skillPath)
|
|
255
|
-
const dependencyAsset = createResolvedSkillAsset({
|
|
256
|
-
cwd: params.cwd,
|
|
257
|
-
definition
|
|
258
|
-
})
|
|
259
|
-
const existingAsset = findSkillDependencyAsset(
|
|
260
|
-
params.skillAssets,
|
|
261
|
-
dependency,
|
|
262
|
-
currentInstancePath,
|
|
263
|
-
{ includeHomeBridge: false }
|
|
264
|
-
) ??
|
|
265
|
-
params.skillAssets.find(existing => (
|
|
266
|
-
existing.resolvedBy !== HOME_BRIDGE_RESOLVED_BY &&
|
|
267
|
-
existing.displayName === dependencyAsset.displayName
|
|
268
|
-
))
|
|
269
|
-
if (existingAsset != null) {
|
|
270
|
-
removeSupersededHomeBridgeSkill(existingAsset.displayName)
|
|
271
|
-
return existingAsset
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
removeSupersededHomeBridgeSkill(dependencyAsset.displayName)
|
|
275
|
-
params.allAssets.push(dependencyAsset)
|
|
276
|
-
params.skillAssets.push(dependencyAsset)
|
|
277
|
-
return dependencyAsset
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// After the first fetch attempt, reuse whichever asset is now visible in the
|
|
281
|
-
// skill set: a newly installed registry skill, or a home-bridge fallback
|
|
282
|
-
// that was accepted by an earlier plain-name dependency resolution.
|
|
283
|
-
const resolvedAsset = findSkillDependencyAsset(params.skillAssets, dependency, currentInstancePath)
|
|
284
|
-
if (resolvedAsset != null && resolvedAsset.resolvedBy !== HOME_BRIDGE_RESOLVED_BY) {
|
|
285
|
-
removeSupersededHomeBridgeSkill(resolvedAsset.displayName)
|
|
286
|
-
}
|
|
287
|
-
return resolvedAsset
|
|
288
|
-
}
|
|
289
|
-
|
|
290
202
|
const addAsset = async (asset: SkillAsset): Promise<void> => {
|
|
291
203
|
if (params.excludedIds?.has(asset.id)) return
|
|
292
204
|
if (seen.has(asset.id)) return
|
|
@@ -311,25 +223,15 @@ export const expandSkillAssetDependenciesWithRemoteResolution = async (
|
|
|
311
223
|
continue
|
|
312
224
|
}
|
|
313
225
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
const dependencyAsset = await installDependencyAsset(dependency, asset.instancePath).catch((error: unknown) => {
|
|
318
|
-
if (
|
|
319
|
-
localOrBridgedDependency != null &&
|
|
320
|
-
dependency.source == null
|
|
321
|
-
) {
|
|
322
|
-
return localOrBridgedDependency
|
|
323
|
-
}
|
|
324
|
-
throw error
|
|
325
|
-
}) ?? (
|
|
326
|
-
dependency.source == null
|
|
327
|
-
? localOrBridgedDependency
|
|
328
|
-
: undefined
|
|
329
|
-
)
|
|
226
|
+
const dependencyAsset = dependency.source == null
|
|
227
|
+
? localOrBridgedDependency
|
|
228
|
+
: undefined
|
|
330
229
|
|
|
331
230
|
if (dependencyAsset == null) {
|
|
332
|
-
throw new Error(
|
|
231
|
+
throw new Error(
|
|
232
|
+
`Skill dependency ${dependency.ref} declared by ${asset.displayName} is missing. ` +
|
|
233
|
+
'Run vf skills install or vf skills update to materialize project skill dependencies.'
|
|
234
|
+
)
|
|
333
235
|
}
|
|
334
236
|
await addAsset(dependencyAsset)
|
|
335
237
|
}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { access, copyFile, lstat, mkdir, readdir } from 'node:fs/promises'
|
|
2
|
-
import { dirname, resolve } from 'node:path'
|
|
3
|
-
import process from 'node:process'
|
|
4
|
-
|
|
5
|
-
import { withDirectoryInstallLock } from '@vibe-forge/utils/install-lock'
|
|
6
|
-
import { resolveProjectSharedCachePath } from '@vibe-forge/utils/project-cache-path'
|
|
7
|
-
import { toSkillSlug } from '@vibe-forge/utils/skills-cli'
|
|
8
|
-
|
|
9
|
-
const toCacheSegment = (value: string) => (
|
|
10
|
-
value
|
|
11
|
-
.trim()
|
|
12
|
-
.toLowerCase()
|
|
13
|
-
.replace(/[^a-z0-9._-]+/g, '-')
|
|
14
|
-
.replace(/^-+|-+$/g, '') || 'default'
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
export const pathExists = async (targetPath: string) => {
|
|
18
|
-
try {
|
|
19
|
-
await access(targetPath)
|
|
20
|
-
return true
|
|
21
|
-
} catch {
|
|
22
|
-
return false
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export const withInstallLock = async <T>(lockDir: string, callback: () => Promise<T>) => {
|
|
27
|
-
try {
|
|
28
|
-
return await withDirectoryInstallLock({ lockDir }, callback)
|
|
29
|
-
} catch (error) {
|
|
30
|
-
const message = error instanceof Error ? error.message : String(error)
|
|
31
|
-
throw new Error(
|
|
32
|
-
message.replace('Timed out waiting for install lock', 'Timed out waiting for skill dependency install lock')
|
|
33
|
-
)
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export const copyRegularFiles = async (sourceDir: string, targetDir: string) => {
|
|
38
|
-
let fileCount = 0
|
|
39
|
-
const entries = await readdir(sourceDir, { withFileTypes: true })
|
|
40
|
-
|
|
41
|
-
await mkdir(targetDir, { recursive: true })
|
|
42
|
-
|
|
43
|
-
for (const entry of entries) {
|
|
44
|
-
const sourcePath = resolve(sourceDir, entry.name)
|
|
45
|
-
const targetPath = resolve(targetDir, entry.name)
|
|
46
|
-
const stat = await lstat(sourcePath)
|
|
47
|
-
|
|
48
|
-
if (stat.isDirectory()) {
|
|
49
|
-
fileCount += await copyRegularFiles(sourcePath, targetPath)
|
|
50
|
-
continue
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (!stat.isFile()) continue
|
|
54
|
-
|
|
55
|
-
await mkdir(dirname(targetPath), { recursive: true })
|
|
56
|
-
await copyFile(sourcePath, targetPath)
|
|
57
|
-
fileCount += 1
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return fileCount
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export const pickSearchResult = <T extends { skill: string }>(
|
|
64
|
-
results: T[],
|
|
65
|
-
name: string
|
|
66
|
-
) => {
|
|
67
|
-
const slug = toSkillSlug(name)
|
|
68
|
-
return results.find(result => (
|
|
69
|
-
result.skill === name ||
|
|
70
|
-
toSkillSlug(result.skill) === slug
|
|
71
|
-
)) ?? results[0]
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export const buildInstallDir = (params: {
|
|
75
|
-
cwd: string
|
|
76
|
-
registry?: string
|
|
77
|
-
skill: string
|
|
78
|
-
source: string
|
|
79
|
-
version?: string
|
|
80
|
-
}) => {
|
|
81
|
-
const registry = params.registry ?? 'default'
|
|
82
|
-
return resolveProjectSharedCachePath(
|
|
83
|
-
params.cwd,
|
|
84
|
-
process.env,
|
|
85
|
-
'skill-dependencies',
|
|
86
|
-
'skills-cli',
|
|
87
|
-
toCacheSegment('skills'),
|
|
88
|
-
toCacheSegment('latest'),
|
|
89
|
-
toCacheSegment(registry),
|
|
90
|
-
...params.source.split('/').map(toCacheSegment),
|
|
91
|
-
toCacheSegment(params.version ?? 'latest'),
|
|
92
|
-
toCacheSegment(params.skill)
|
|
93
|
-
)
|
|
94
|
-
}
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import { mkdir, rename, rm } from 'node:fs/promises'
|
|
2
|
-
import { resolve } from 'node:path'
|
|
3
|
-
import process from 'node:process'
|
|
4
|
-
|
|
5
|
-
import type { Config } from '@vibe-forge/types'
|
|
6
|
-
import { isObjectSkillsConfig, resolveSkillsRegistry } from '@vibe-forge/utils'
|
|
7
|
-
import { findSkillsCli, installSkillsCliRefToTemp, installSkillsCliSkillToTemp } from '@vibe-forge/utils/skills-cli'
|
|
8
|
-
|
|
9
|
-
import type { NormalizedSkillDependency } from './skill-dependencies'
|
|
10
|
-
import {
|
|
11
|
-
buildInstallDir,
|
|
12
|
-
copyRegularFiles,
|
|
13
|
-
pathExists,
|
|
14
|
-
pickSearchResult,
|
|
15
|
-
withInstallLock
|
|
16
|
-
} from './skills-cli-dependency-helpers'
|
|
17
|
-
|
|
18
|
-
const resolveAutoDownloadDependenciesEnabled = (
|
|
19
|
-
projectConfig: Config | undefined,
|
|
20
|
-
userConfig: Config | undefined
|
|
21
|
-
) => (
|
|
22
|
-
(isObjectSkillsConfig(userConfig?.skills) ? userConfig.skills.autoDownloadDependencies : undefined) ??
|
|
23
|
-
(isObjectSkillsConfig(projectConfig?.skills) ? projectConfig.skills.autoDownloadDependencies : undefined) ??
|
|
24
|
-
true
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
export const installSkillsCliDependency = async (params: {
|
|
28
|
-
cwd: string
|
|
29
|
-
configs: [Config?, Config?]
|
|
30
|
-
dependency: NormalizedSkillDependency
|
|
31
|
-
}) => {
|
|
32
|
-
const [projectConfig, userConfig] = params.configs
|
|
33
|
-
const autoDownloadDependenciesEnabled = resolveAutoDownloadDependenciesEnabled(projectConfig, userConfig)
|
|
34
|
-
const defaultRegistry = resolveSkillsRegistry(params.configs[1]?.skills) ??
|
|
35
|
-
resolveSkillsRegistry(params.configs[0]?.skills)
|
|
36
|
-
const registry = params.dependency.registry ?? defaultRegistry
|
|
37
|
-
const resolvedTarget = await (async () => {
|
|
38
|
-
if (params.dependency.source != null) {
|
|
39
|
-
return {
|
|
40
|
-
skill: params.dependency.name,
|
|
41
|
-
source: params.dependency.source
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (!autoDownloadDependenciesEnabled) {
|
|
46
|
-
throw new Error(
|
|
47
|
-
`Skill dependency automatic downloads are disabled; cannot resolve ${params.dependency.ref} without a source`
|
|
48
|
-
)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return await (async () => {
|
|
52
|
-
const searchResults = await findSkillsCli({
|
|
53
|
-
registry,
|
|
54
|
-
query: params.dependency.name
|
|
55
|
-
})
|
|
56
|
-
const selected = pickSearchResult(searchResults, params.dependency.name)
|
|
57
|
-
if (selected == null) {
|
|
58
|
-
throw new Error(`Skill ${params.dependency.name} was not found by the skills CLI search.`)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return {
|
|
62
|
-
installRef: selected.installRef,
|
|
63
|
-
skill: selected.skill,
|
|
64
|
-
source: selected.source
|
|
65
|
-
}
|
|
66
|
-
})()
|
|
67
|
-
})()
|
|
68
|
-
|
|
69
|
-
const installDir = buildInstallDir({
|
|
70
|
-
cwd: params.cwd,
|
|
71
|
-
registry,
|
|
72
|
-
skill: resolvedTarget.skill,
|
|
73
|
-
source: resolvedTarget.source,
|
|
74
|
-
version: params.dependency.version
|
|
75
|
-
})
|
|
76
|
-
const skillPath = resolve(installDir, 'SKILL.md')
|
|
77
|
-
|
|
78
|
-
return await withInstallLock(`${installDir}.lock`, async () => {
|
|
79
|
-
if (await pathExists(skillPath)) {
|
|
80
|
-
return {
|
|
81
|
-
installDir,
|
|
82
|
-
skillPath
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (!autoDownloadDependenciesEnabled) {
|
|
87
|
-
throw new Error(`Skill dependency automatic downloads are disabled; cache not found for ${params.dependency.ref}`)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const tempInstallDir = `${installDir}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
91
|
-
await rm(tempInstallDir, { recursive: true, force: true })
|
|
92
|
-
await mkdir(tempInstallDir, { recursive: true })
|
|
93
|
-
|
|
94
|
-
const installResult = 'installRef' in resolvedTarget
|
|
95
|
-
? params.dependency.version == null
|
|
96
|
-
? await installSkillsCliRefToTemp({
|
|
97
|
-
installRef: resolvedTarget.installRef,
|
|
98
|
-
registry
|
|
99
|
-
})
|
|
100
|
-
: await installSkillsCliSkillToTemp({
|
|
101
|
-
registry,
|
|
102
|
-
skill: resolvedTarget.skill,
|
|
103
|
-
source: resolvedTarget.source,
|
|
104
|
-
version: params.dependency.version
|
|
105
|
-
})
|
|
106
|
-
: await installSkillsCliSkillToTemp({
|
|
107
|
-
registry,
|
|
108
|
-
skill: resolvedTarget.skill,
|
|
109
|
-
source: resolvedTarget.source,
|
|
110
|
-
version: params.dependency.version
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
try {
|
|
114
|
-
await copyRegularFiles(installResult.installedSkill.sourcePath, tempInstallDir)
|
|
115
|
-
if (!await pathExists(resolve(tempInstallDir, 'SKILL.md'))) {
|
|
116
|
-
throw new Error(`Skill dependency ${params.dependency.ref} did not include SKILL.md`)
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
await rm(installDir, { recursive: true, force: true })
|
|
120
|
-
await rename(tempInstallDir, installDir)
|
|
121
|
-
} catch (error) {
|
|
122
|
-
await rm(tempInstallDir, { recursive: true, force: true })
|
|
123
|
-
throw error
|
|
124
|
-
} finally {
|
|
125
|
-
await rm(installResult.tempDir, { recursive: true, force: true })
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return {
|
|
129
|
-
installDir,
|
|
130
|
-
skillPath
|
|
131
|
-
}
|
|
132
|
-
})
|
|
133
|
-
}
|