@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.
@@ -280,64 +280,6 @@ describe('buildAdapterAssetPlan', () => {
280
280
  ]))
281
281
  })
282
282
 
283
- it('keeps explicit skills CLI dependencies ahead of preselected home-bridged skills in overlays', async () => {
284
- const workspace = await createWorkspace()
285
- const realHome = process.env.__VF_PROJECT_REAL_HOME__
286
- const tempInstallDir = join(workspace, '.tmp-install-skills-cli-foo')
287
- const installedSkillDir = join(tempInstallDir, '.agents', 'skills', 'foo')
288
- await writeDocument(
289
- join(installedSkillDir, 'SKILL.md'),
290
- '---\nname: foo\ndescription: Registry foo\n---\nUse the registry definition.\n'
291
- )
292
- skillsCliMocks.installSkillsCliSkillToTemp.mockResolvedValue({
293
- tempDir: tempInstallDir,
294
- installedSkill: {
295
- dirName: 'foo',
296
- name: 'foo',
297
- sourcePath: installedSkillDir
298
- }
299
- })
300
-
301
- await writeDocument(
302
- join(realHome!, '.agents/skills/foo/SKILL.md'),
303
- '---\ndescription: Home foo\n---\nUse the home definition.\n'
304
- )
305
- await writeDocument(
306
- join(workspace, '.ai/skills/app-builder/SKILL.md'),
307
- [
308
- '---',
309
- 'name: app-builder',
310
- 'description: Build apps',
311
- 'dependencies:',
312
- ' - anthropics/skills@foo',
313
- '---',
314
- 'Build the app.'
315
- ].join('\n')
316
- )
317
-
318
- const bundle = await resolveWorkspaceAssetBundle({
319
- cwd: workspace,
320
- configs: [undefined, undefined],
321
- useDefaultVibeForgeMcpServer: false
322
- })
323
- const plan = await buildAdapterAssetPlan({
324
- adapter: 'opencode',
325
- bundle,
326
- options: {
327
- skills: {
328
- include: ['foo', 'app-builder']
329
- }
330
- }
331
- })
332
-
333
- const fooOverlays = plan.overlays.filter(entry => entry.kind === 'skill' && entry.targetPath === 'skills/foo')
334
- expect(fooOverlays).toHaveLength(1)
335
- expect(fooOverlays[0]?.sourcePath).toContain(
336
- '/.ai/caches/skill-dependencies/skills-cli/skills/latest/default/anthropics/skills/latest/foo'
337
- )
338
- expect(fooOverlays[0]?.sourcePath).not.toBe(join(realHome!, '.agents/skills/foo'))
339
- })
340
-
341
283
  it('prunes excluded skill dependency subtrees from selected native overlays', async () => {
342
284
  const workspace = await createWorkspace()
343
285
 
@@ -2,7 +2,6 @@
2
2
  import { join } from 'node:path'
3
3
  import process from 'node:process'
4
4
 
5
- import { readFile } from 'node:fs/promises'
6
5
  import { afterEach, describe, expect, it, vi } from 'vitest'
7
6
 
8
7
  const skillsCliMocks = vi.hoisted(() => ({
@@ -27,6 +26,7 @@ import { createWorkspace, installPluginPackage, writeDocument } from './test-hel
27
26
 
28
27
  afterEach(() => {
29
28
  vi.clearAllMocks()
29
+ vi.restoreAllMocks()
30
30
  vi.unstubAllGlobals()
31
31
  })
32
32
 
@@ -291,7 +291,7 @@ describe('resolveWorkspaceAssetBundle', () => {
291
291
 
292
292
  expect(bundle.skills).toEqual([])
293
293
  expect(warnSpy).toHaveBeenCalledWith(
294
- expect.stringContaining('Ignoring invalid skills.homeBridge root "./team-skills"')
294
+ expect.stringContaining('Ignoring invalid skillsMeta.homeBridge root "./team-skills"')
295
295
  )
296
296
  } finally {
297
297
  warnSpy.mockRestore()
@@ -386,95 +386,9 @@ describe('resolveWorkspaceAssetBundle', () => {
386
386
  }))
387
387
  })
388
388
 
389
- it('installs selected missing skill dependencies from the skills CLI cache', async () => {
389
+ it('does not install configured project skills during bundle resolution and warns when requested', async () => {
390
390
  const workspace = await createWorkspace()
391
- const realHome = process.env.__VF_PROJECT_REAL_HOME__
392
- const tempInstallDir = join(workspace, '.tmp-install-skills-cli')
393
- const installedSkillDir = join(tempInstallDir, '.agents', 'skills', 'frontend-design')
394
- await writeDocument(
395
- join(installedSkillDir, 'SKILL.md'),
396
- '---\nname: frontend-design\ndescription: UI design guidance\n---\nUse strong visual hierarchy.\n'
397
- )
398
- skillsCliMocks.installSkillsCliSkillToTemp.mockResolvedValue({
399
- tempDir: tempInstallDir,
400
- installedSkill: {
401
- dirName: 'frontend-design',
402
- name: 'frontend-design',
403
- sourcePath: installedSkillDir
404
- }
405
- })
406
-
407
- await writeDocument(
408
- join(realHome!, '.agents/skills/frontend-design/SKILL.md'),
409
- '---\ndescription: home frontend design\n---\nUse the home definition.'
410
- )
411
- await writeDocument(
412
- join(workspace, '.ai/skills/app-builder/SKILL.md'),
413
- [
414
- '---',
415
- 'name: app-builder',
416
- 'description: Build apps',
417
- 'dependencies:',
418
- ' - anthropics/skills@frontend-design',
419
- '---',
420
- 'Build the app.'
421
- ].join('\n')
422
- )
423
-
424
- const bundle = await resolveWorkspaceAssetBundle({
425
- cwd: workspace,
426
- configs: [undefined, undefined],
427
- useDefaultVibeForgeMcpServer: false
428
- })
429
-
430
- expect(bundle.skills.map(asset => asset.name).sort()).toEqual(['app-builder', 'frontend-design'])
431
- expect(bundle.skills.find(asset => asset.name === 'frontend-design')).toEqual(expect.objectContaining({
432
- resolvedBy: 'home-bridge',
433
- sourcePath: join(realHome!, '.agents/skills/frontend-design/SKILL.md')
434
- }))
435
-
436
- await buildAdapterAssetPlan({
437
- adapter: 'opencode',
438
- bundle,
439
- options: {
440
- skills: {
441
- include: ['app-builder']
442
- }
443
- }
444
- })
445
-
446
- const dependency = bundle.skills.find(asset => asset.name === 'frontend-design')
447
- expect(bundle.skills.map(asset => asset.name).sort()).toEqual(['app-builder', 'frontend-design'])
448
- expect(dependency?.sourcePath).toContain(
449
- '/.ai/caches/skill-dependencies/skills-cli/skills/latest/default/anthropics/skills/latest/frontend-design/'
450
- )
451
- expect(bundle.skills.find(asset => (
452
- asset.name === 'frontend-design' && asset.resolvedBy === 'home-bridge'
453
- ))).toBeUndefined()
454
- expect(skillsCliMocks.findSkillsCli).not.toHaveBeenCalled()
455
- expect(skillsCliMocks.installSkillsCliSkillToTemp).toHaveBeenCalledWith({
456
- config: undefined,
457
- skill: 'frontend-design',
458
- source: 'anthropics/skills'
459
- })
460
- })
461
-
462
- it('installs configured project skills before bundle resolution and rewrites renamed skill names', async () => {
463
- const workspace = await createWorkspace()
464
- const tempInstallDir = join(workspace, '.tmp-configured-install')
465
- const installedSkillDir = join(tempInstallDir, '.agents', 'skills', 'design-review')
466
- await writeDocument(
467
- join(installedSkillDir, 'SKILL.md'),
468
- '---\nname: design-review\ndescription: Review design work\n---\nReview the UI implementation.\n'
469
- )
470
- skillsCliMocks.installSkillsCliSkillToTemp.mockResolvedValue({
471
- tempDir: tempInstallDir,
472
- installedSkill: {
473
- dirName: 'design-review',
474
- name: 'design-review',
475
- sourcePath: installedSkillDir
476
- }
477
- })
391
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined)
478
392
 
479
393
  const bundle = await resolveWorkspaceAssetBundle({
480
394
  cwd: workspace,
@@ -487,22 +401,16 @@ describe('resolveWorkspaceAssetBundle', () => {
487
401
  }
488
402
  ]
489
403
  }, undefined],
490
- syncConfiguredSkills: true,
404
+ warnMissingConfiguredSkills: true,
491
405
  useDefaultVibeForgeMcpServer: false
492
406
  })
493
407
 
494
- expect(bundle.skills.map(asset => asset.name)).toContain('internal-review')
495
- expect(skillsCliMocks.installSkillsCliSkillToTemp).toHaveBeenCalledWith({
496
- config: undefined,
497
- skill: 'design-review',
498
- source: 'example-source/default/public'
499
- })
500
- await expect(readFile(join(workspace, '.ai/skills/internal-review/SKILL.md'), 'utf8')).resolves.toContain(
501
- 'name: internal-review'
502
- )
408
+ expect(bundle.skills.map(asset => asset.name)).not.toContain('internal-review')
409
+ expect(skillsCliMocks.installSkillsCliSkillToTemp).not.toHaveBeenCalled()
410
+ expect(warn).toHaveBeenCalledWith(expect.stringContaining('Declared skills are not installed: internal-review'))
503
411
  })
504
412
 
505
- it('skips configured skill reinstalls unless updateConfiguredSkills is enabled', async () => {
413
+ it('loads installed configured skills without attempting runtime updates', async () => {
506
414
  const workspace = await createWorkspace()
507
415
  await writeDocument(
508
416
  join(workspace, '.ai/skills/internal-review/SKILL.md'),
@@ -520,213 +428,12 @@ describe('resolveWorkspaceAssetBundle', () => {
520
428
  }
521
429
  ]
522
430
  }, undefined],
523
- syncConfiguredSkills: true,
431
+ warnMissingConfiguredSkills: true,
524
432
  useDefaultVibeForgeMcpServer: false
525
433
  })
526
434
 
527
435
  expect(skippedBundle.skills.map(asset => asset.name)).toContain('internal-review')
528
436
  expect(skillsCliMocks.installSkillsCliSkillToTemp).not.toHaveBeenCalled()
529
-
530
- const tempInstallDir = join(workspace, '.tmp-configured-update')
531
- const installedSkillDir = join(tempInstallDir, '.agents', 'skills', 'design-review')
532
- await writeDocument(
533
- join(installedSkillDir, 'SKILL.md'),
534
- '---\nname: design-review\ndescription: Updated skill\n---\nUpdated content.\n'
535
- )
536
- skillsCliMocks.installSkillsCliSkillToTemp.mockResolvedValueOnce({
537
- tempDir: tempInstallDir,
538
- installedSkill: {
539
- dirName: 'design-review',
540
- name: 'design-review',
541
- sourcePath: installedSkillDir
542
- }
543
- })
544
-
545
- await resolveWorkspaceAssetBundle({
546
- cwd: workspace,
547
- configs: [{
548
- skills: [
549
- {
550
- name: 'design-review',
551
- source: 'example-source/default/public',
552
- rename: 'internal-review'
553
- }
554
- ]
555
- }, undefined],
556
- syncConfiguredSkills: true,
557
- updateConfiguredSkills: true,
558
- useDefaultVibeForgeMcpServer: false
559
- })
560
-
561
- expect(skillsCliMocks.installSkillsCliSkillToTemp).toHaveBeenCalledTimes(1)
562
- await expect(readFile(join(workspace, '.ai/skills/internal-review/SKILL.md'), 'utf8')).resolves.toContain(
563
- 'Updated content.'
564
- )
565
- })
566
-
567
- it('installs skill dependencies into the primary workspace shared cache', async () => {
568
- const primary = await createWorkspace()
569
- const worktree = await createWorkspace()
570
- const previousPrimaryWorkspace = process.env.__VF_PROJECT_PRIMARY_WORKSPACE_FOLDER__
571
- const tempInstallDir = join(worktree, '.tmp-install-skills-cli')
572
- const installedSkillDir = join(tempInstallDir, '.agents', 'skills', 'frontend-design')
573
- await writeDocument(
574
- join(installedSkillDir, 'SKILL.md'),
575
- '---\nname: frontend-design\ndescription: UI design guidance\n---\nUse primary cache.\n'
576
- )
577
- skillsCliMocks.installSkillsCliSkillToTemp.mockResolvedValue({
578
- tempDir: tempInstallDir,
579
- installedSkill: {
580
- dirName: 'frontend-design',
581
- name: 'frontend-design',
582
- sourcePath: installedSkillDir
583
- }
584
- })
585
-
586
- try {
587
- process.env.__VF_PROJECT_PRIMARY_WORKSPACE_FOLDER__ = primary
588
- await writeDocument(
589
- join(worktree, '.ai/skills/app-builder/SKILL.md'),
590
- [
591
- '---',
592
- 'name: app-builder',
593
- 'description: Build apps',
594
- 'dependencies:',
595
- ' - anthropics/skills@frontend-design',
596
- '---',
597
- 'Build the app.'
598
- ].join('\n')
599
- )
600
-
601
- const bundle = await resolveWorkspaceAssetBundle({
602
- cwd: worktree,
603
- configs: [undefined, undefined],
604
- useDefaultVibeForgeMcpServer: false
605
- })
606
-
607
- await buildAdapterAssetPlan({
608
- adapter: 'opencode',
609
- bundle,
610
- options: {
611
- skills: {
612
- include: ['app-builder']
613
- }
614
- }
615
- })
616
-
617
- const dependency = bundle.skills.find(asset => asset.name === 'frontend-design')
618
- expect(dependency?.sourcePath).toContain(join(
619
- primary,
620
- '.ai/caches/skill-dependencies/skills-cli/skills/latest/default/anthropics/skills/latest/frontend-design/'
621
- ))
622
- expect(dependency?.sourcePath).not.toContain(join(worktree, '.ai/caches'))
623
- } finally {
624
- if (previousPrimaryWorkspace == null) {
625
- delete process.env.__VF_PROJECT_PRIMARY_WORKSPACE_FOLDER__
626
- } else {
627
- process.env.__VF_PROJECT_PRIMARY_WORKSPACE_FOLDER__ = previousPrimaryWorkspace
628
- }
629
- }
630
- })
631
-
632
- it('reuses complete skill dependency caches without deleting or downloading them again', async () => {
633
- const workspace = await createWorkspace()
634
-
635
- const cachedSkillPath = join(
636
- workspace,
637
- '.ai/caches/skill-dependencies/skills-cli/skills/latest/default/anthropics/skills/latest/frontend-design/SKILL.md'
638
- )
639
- await writeDocument(
640
- cachedSkillPath,
641
- '---\nname: frontend-design\ndescription: Cached UI guidance\n---\nUse the cached copy.\n'
642
- )
643
- await writeDocument(
644
- join(workspace, '.ai/skills/app-builder/SKILL.md'),
645
- [
646
- '---',
647
- 'name: app-builder',
648
- 'description: Build apps',
649
- 'dependencies:',
650
- ' - anthropics/skills@frontend-design',
651
- '---',
652
- 'Build the app.'
653
- ].join('\n')
654
- )
655
-
656
- const bundle = await resolveWorkspaceAssetBundle({
657
- cwd: workspace,
658
- configs: [undefined, undefined],
659
- useDefaultVibeForgeMcpServer: false
660
- })
661
-
662
- await buildAdapterAssetPlan({
663
- adapter: 'opencode',
664
- bundle,
665
- options: {
666
- skills: {
667
- include: ['app-builder']
668
- }
669
- }
670
- })
671
-
672
- expect(skillsCliMocks.findSkillsCli).not.toHaveBeenCalled()
673
- expect(skillsCliMocks.installSkillsCliSkillToTemp).not.toHaveBeenCalled()
674
- expect(await readFile(cachedSkillPath, 'utf8')).toContain('Use the cached copy.')
675
- expect(bundle.skills.map(asset => asset.name).sort()).toEqual(['app-builder', 'frontend-design'])
676
- })
677
-
678
- it('parses registry/source/version dependency specs and forwards them to the skills CLI installer', async () => {
679
- const workspace = await createWorkspace()
680
- const tempInstallDir = join(workspace, '.tmp-install-skills-cli')
681
- const installedSkillDir = join(tempInstallDir, '.agents', 'skills', 'frontend-design')
682
- await writeDocument(
683
- join(installedSkillDir, 'SKILL.md'),
684
- '---\nname: frontend-design\ndescription: UI design guidance\n---\nUse internal design tokens.\n'
685
- )
686
- skillsCliMocks.installSkillsCliSkillToTemp.mockResolvedValue({
687
- tempDir: tempInstallDir,
688
- installedSkill: {
689
- dirName: 'frontend-design',
690
- name: 'frontend-design',
691
- sourcePath: installedSkillDir
692
- }
693
- })
694
-
695
- await writeDocument(
696
- join(workspace, '.ai/skills/app-builder/SKILL.md'),
697
- [
698
- '---',
699
- 'name: app-builder',
700
- 'description: Build apps',
701
- 'dependencies:',
702
- ' - https://registry.example.com@example-source/default/public@frontend-design@1.0.3',
703
- '---',
704
- 'Build the app.'
705
- ].join('\n')
706
- )
707
-
708
- const bundle = await resolveWorkspaceAssetBundle({
709
- cwd: workspace,
710
- configs: [undefined, undefined],
711
- useDefaultVibeForgeMcpServer: false
712
- })
713
-
714
- await buildAdapterAssetPlan({
715
- adapter: 'opencode',
716
- bundle,
717
- options: {
718
- skills: {
719
- include: ['app-builder']
720
- }
721
- }
722
- })
723
-
724
- expect(skillsCliMocks.installSkillsCliSkillToTemp).toHaveBeenCalledWith({
725
- registry: 'https://registry.example.com',
726
- skill: 'frontend-design',
727
- source: 'example-source/default/public',
728
- version: '1.0.3'
729
- })
730
437
  })
731
438
 
732
439
  it('loads workspace entities from the env-configured entities dir', async () => {