@vibe-forge/workspace-assets 2.0.0 → 2.0.2

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.
@@ -1,6 +1,7 @@
1
+ /* eslint-disable max-lines -- adapter asset plan scenarios share setup helpers and assertions */
1
2
  import { join } from 'node:path'
2
3
 
3
- import { describe, expect, it } from 'vitest'
4
+ import { describe, expect, it, vi } from 'vitest'
4
5
 
5
6
  import { buildAdapterAssetPlan, resolvePromptAssetSelection, resolveWorkspaceAssetBundle } from '#~/index.js'
6
7
 
@@ -78,7 +79,7 @@ describe('buildAdapterAssetPlan', () => {
78
79
  }
79
80
  }
80
81
  })
81
- const plan = buildAdapterAssetPlan({
82
+ const plan = await buildAdapterAssetPlan({
82
83
  adapter: 'codex',
83
84
  bundle,
84
85
  options: {
@@ -150,7 +151,7 @@ describe('buildAdapterAssetPlan', () => {
150
151
  }, undefined],
151
152
  useDefaultVibeForgeMcpServer: false
152
153
  })
153
- const plan = buildAdapterAssetPlan({
154
+ const plan = await buildAdapterAssetPlan({
154
155
  adapter: 'opencode',
155
156
  bundle,
156
157
  options: {
@@ -180,6 +181,217 @@ describe('buildAdapterAssetPlan', () => {
180
181
  ]))
181
182
  })
182
183
 
184
+ it('labels home-bridged skill diagnostics with source=home', async () => {
185
+ const workspace = await createWorkspace()
186
+ const realHome = process.env.__VF_PROJECT_REAL_HOME__
187
+
188
+ await writeDocument(
189
+ join(realHome!, '.agents/skills/research/SKILL.md'),
190
+ '---\ndescription: 检索资料\n---\n阅读 README.md'
191
+ )
192
+
193
+ const bundle = await resolveWorkspaceAssetBundle({
194
+ cwd: workspace,
195
+ configs: [undefined, undefined],
196
+ useDefaultVibeForgeMcpServer: false
197
+ })
198
+ const researchSkillId = bundle.skills.find(asset => asset.name === 'research')?.id
199
+
200
+ const plan = await buildAdapterAssetPlan({
201
+ adapter: 'opencode',
202
+ bundle,
203
+ options: {
204
+ skills: {
205
+ include: ['research']
206
+ }
207
+ }
208
+ })
209
+
210
+ expect(plan.diagnostics).toEqual(expect.arrayContaining([
211
+ expect.objectContaining({
212
+ assetId: researchSkillId,
213
+ adapter: 'opencode',
214
+ status: 'native',
215
+ source: 'home'
216
+ })
217
+ ]))
218
+ })
219
+
220
+ it('includes transitive skill dependencies in selected native overlays', async () => {
221
+ const workspace = await createWorkspace()
222
+
223
+ await writeDocument(
224
+ join(workspace, '.ai/skills/app-builder/SKILL.md'),
225
+ [
226
+ '---',
227
+ 'name: app-builder',
228
+ 'description: Build apps',
229
+ 'dependencies:',
230
+ ' - frontend-design',
231
+ '---',
232
+ 'Build the app.'
233
+ ].join('\n')
234
+ )
235
+ await writeDocument(
236
+ join(workspace, '.ai/skills/frontend-design/SKILL.md'),
237
+ '---\nname: frontend-design\ndescription: UI design guidance\n---\nDesign the UI.'
238
+ )
239
+
240
+ const bundle = await resolveWorkspaceAssetBundle({
241
+ cwd: workspace,
242
+ configs: [undefined, undefined],
243
+ useDefaultVibeForgeMcpServer: false
244
+ })
245
+ const plan = await buildAdapterAssetPlan({
246
+ adapter: 'opencode',
247
+ bundle,
248
+ options: {
249
+ skills: {
250
+ include: ['app-builder']
251
+ }
252
+ }
253
+ })
254
+
255
+ expect(plan.overlays).toEqual(expect.arrayContaining([
256
+ expect.objectContaining({
257
+ kind: 'skill',
258
+ targetPath: 'skills/app-builder'
259
+ }),
260
+ expect.objectContaining({
261
+ kind: 'skill',
262
+ targetPath: 'skills/frontend-design'
263
+ })
264
+ ]))
265
+ })
266
+
267
+ it('keeps explicit registry dependencies ahead of preselected home-bridged skills in overlays', async () => {
268
+ const workspace = await createWorkspace()
269
+ const realHome = process.env.__VF_PROJECT_REAL_HOME__
270
+ const fetchMock = vi.fn(async (url: string) => {
271
+ if (url === 'https://registry.example.test/api/search?q=foo&limit=10') {
272
+ return new Response(JSON.stringify({
273
+ skills: [{
274
+ id: 'anthropics/skills/foo',
275
+ skillId: 'foo',
276
+ name: 'foo',
277
+ source: 'anthropics/skills'
278
+ }]
279
+ }))
280
+ }
281
+ if (url === 'https://registry.example.test/api/download/anthropics/skills/foo') {
282
+ return new Response(JSON.stringify({
283
+ files: [{
284
+ path: 'SKILL.md',
285
+ contents: '---\nname: foo\ndescription: Registry foo\n---\nUse the registry definition.\n'
286
+ }]
287
+ }))
288
+ }
289
+ return new Response('not found', { status: 404 })
290
+ })
291
+ vi.stubGlobal('fetch', fetchMock)
292
+
293
+ try {
294
+ await writeDocument(
295
+ join(realHome!, '.agents/skills/foo/SKILL.md'),
296
+ '---\ndescription: Home foo\n---\nUse the home definition.\n'
297
+ )
298
+ await writeDocument(
299
+ join(workspace, '.ai/skills/app-builder/SKILL.md'),
300
+ [
301
+ '---',
302
+ 'name: app-builder',
303
+ 'description: Build apps',
304
+ 'dependencies:',
305
+ ' - anthropics/skills@foo',
306
+ '---',
307
+ 'Build the app.'
308
+ ].join('\n')
309
+ )
310
+
311
+ const bundle = await resolveWorkspaceAssetBundle({
312
+ cwd: workspace,
313
+ configs: [{
314
+ skills: {
315
+ registry: 'https://registry.example.test'
316
+ }
317
+ }, undefined],
318
+ useDefaultVibeForgeMcpServer: false
319
+ })
320
+ const plan = await buildAdapterAssetPlan({
321
+ adapter: 'opencode',
322
+ bundle,
323
+ options: {
324
+ skills: {
325
+ include: ['foo', 'app-builder']
326
+ }
327
+ }
328
+ })
329
+
330
+ const fooOverlays = plan.overlays.filter(entry => entry.kind === 'skill' && entry.targetPath === 'skills/foo')
331
+ expect(fooOverlays).toHaveLength(1)
332
+ expect(fooOverlays[0]?.sourcePath).toContain('/.ai/caches/skill-dependencies/registry.example.test/')
333
+ expect(fooOverlays[0]?.sourcePath).not.toBe(join(realHome!, '.agents/skills/foo'))
334
+ } finally {
335
+ vi.unstubAllGlobals()
336
+ }
337
+ })
338
+
339
+ it('prunes excluded skill dependency subtrees from selected native overlays', async () => {
340
+ const workspace = await createWorkspace()
341
+
342
+ await writeDocument(
343
+ join(workspace, '.ai/skills/app-builder/SKILL.md'),
344
+ [
345
+ '---',
346
+ 'name: app-builder',
347
+ 'description: Build apps',
348
+ 'dependencies:',
349
+ ' - frontend-design',
350
+ '---',
351
+ 'Build the app.'
352
+ ].join('\n')
353
+ )
354
+ await writeDocument(
355
+ join(workspace, '.ai/skills/frontend-design/SKILL.md'),
356
+ [
357
+ '---',
358
+ 'name: frontend-design',
359
+ 'description: UI design guidance',
360
+ 'dependencies:',
361
+ ' - color-system',
362
+ '---',
363
+ 'Design the UI.'
364
+ ].join('\n')
365
+ )
366
+ await writeDocument(
367
+ join(workspace, '.ai/skills/color-system/SKILL.md'),
368
+ '---\nname: color-system\ndescription: Color guidance\n---\nPick accessible colors.'
369
+ )
370
+
371
+ const bundle = await resolveWorkspaceAssetBundle({
372
+ cwd: workspace,
373
+ configs: [undefined, undefined],
374
+ useDefaultVibeForgeMcpServer: false
375
+ })
376
+ const plan = await buildAdapterAssetPlan({
377
+ adapter: 'opencode',
378
+ bundle,
379
+ options: {
380
+ skills: {
381
+ include: ['app-builder'],
382
+ exclude: ['frontend-design']
383
+ }
384
+ }
385
+ })
386
+
387
+ expect(plan.overlays).toEqual([
388
+ expect.objectContaining({
389
+ kind: 'skill',
390
+ targetPath: 'skills/app-builder'
391
+ })
392
+ ])
393
+ })
394
+
183
395
  it('builds copilot native skill overlays and translated runtime diagnostics', async () => {
184
396
  const workspace = await createWorkspace()
185
397
 
@@ -246,7 +458,7 @@ describe('buildAdapterAssetPlan', () => {
246
458
  }
247
459
  }
248
460
  })
249
- const plan = buildAdapterAssetPlan({
461
+ const plan = await buildAdapterAssetPlan({
250
462
  adapter: 'copilot',
251
463
  bundle,
252
464
  options: {
@@ -346,7 +558,7 @@ describe('buildAdapterAssetPlan', () => {
346
558
  const loggerHookPluginId = bundle.hookPlugins.find(asset => asset.packageId === '@vibe-forge/plugin-logger')?.id
347
559
  const demoCommandId = bundle.opencodeOverlayAssets.find(asset => asset.kind === 'command')?.id
348
560
 
349
- const plan = buildAdapterAssetPlan({
561
+ const plan = await buildAdapterAssetPlan({
350
562
  adapter: 'kimi',
351
563
  bundle,
352
564
  options: {
@@ -400,7 +612,7 @@ describe('buildAdapterAssetPlan', () => {
400
612
  })
401
613
  const loggerHookPluginId = bundle.hookPlugins.find(asset => asset.packageId === '@vibe-forge/plugin-logger')?.id
402
614
 
403
- const plan = buildAdapterAssetPlan({
615
+ const plan = await buildAdapterAssetPlan({
404
616
  adapter: 'gemini',
405
617
  bundle,
406
618
  options: {}