@vibe-forge/workspace-assets 1.0.0 → 2.0.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__/__snapshots__/workspace-assets-rich.snapshot.json +55 -0
- package/__tests__/adapter-asset-plan.spec.ts +220 -6
- package/__tests__/bundle.spec.ts +677 -2
- package/__tests__/prompt-selection.spec.ts +307 -0
- package/__tests__/skill-dependencies-cli.spec.ts +175 -0
- package/__tests__/snapshot.ts +1 -0
- package/__tests__/test-helpers.ts +9 -0
- package/__tests__/workspace-assets.snapshot.spec.ts +2 -2
- package/package.json +5 -5
- package/src/adapter-asset-plan.ts +42 -66
- package/src/asset-source.ts +13 -0
- package/src/bundle-internal.ts +242 -22
- package/src/bundle.ts +4 -0
- package/src/configured-skills.ts +99 -0
- package/src/home-bridge.ts +1 -0
- package/src/index.ts +3 -0
- package/src/prompt-selection.ts +44 -19
- package/src/selection-internal.ts +335 -1
- package/src/skill-dependencies.ts +361 -0
- package/src/skills-cli-dependency.ts +208 -0
- package/src/workspace-config.ts +132 -0
- package/src/workspace-prompt.ts +29 -0
- package/src/workspaces.ts +188 -0
|
@@ -435,6 +435,313 @@ describe('resolvePromptAssetSelection', () => {
|
|
|
435
435
|
expect(options.systemPrompt).not.toContain('<skill-content>\n先读 README.md\n</skill-content>')
|
|
436
436
|
})
|
|
437
437
|
|
|
438
|
+
it('appends conventional prompt files when loading a directory entity', async () => {
|
|
439
|
+
const workspace = await createWorkspace()
|
|
440
|
+
|
|
441
|
+
await writeDocument(
|
|
442
|
+
join(workspace, '.ai/entities/reviewer/README.md'),
|
|
443
|
+
[
|
|
444
|
+
'---',
|
|
445
|
+
'description: 代码评审实体',
|
|
446
|
+
'---',
|
|
447
|
+
'基础身份。'
|
|
448
|
+
].join('\n')
|
|
449
|
+
)
|
|
450
|
+
await writeDocument(
|
|
451
|
+
join(workspace, '.ai/entities/reviewer/INTRODUCTION.md'),
|
|
452
|
+
'负责发现行为回归。'
|
|
453
|
+
)
|
|
454
|
+
await writeDocument(
|
|
455
|
+
join(workspace, '.ai/entities/reviewer/PERSONALITY.md'),
|
|
456
|
+
'说话克制,先给出高风险问题。'
|
|
457
|
+
)
|
|
458
|
+
await writeDocument(
|
|
459
|
+
join(workspace, '.ai/entities/reviewer/MEMORY.md'),
|
|
460
|
+
'记住上次评审指出过缺少验证。'
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
const bundle = await resolveWorkspaceAssetBundle({
|
|
464
|
+
cwd: workspace,
|
|
465
|
+
useDefaultVibeForgeMcpServer: false
|
|
466
|
+
})
|
|
467
|
+
const [data, options] = await resolvePromptAssetSelection({
|
|
468
|
+
bundle,
|
|
469
|
+
type: 'entity',
|
|
470
|
+
name: 'reviewer'
|
|
471
|
+
})
|
|
472
|
+
|
|
473
|
+
expect(data.targetBody).toContain('基础身份。')
|
|
474
|
+
expect(data.targetBody).toContain('## Introduction\n\n负责发现行为回归。')
|
|
475
|
+
expect(data.targetBody).toContain('## Personality\n\n说话克制,先给出高风险问题。')
|
|
476
|
+
expect(data.targetBody).toContain('## Memory\n\n记住上次评审指出过缺少验证。')
|
|
477
|
+
expect(options.systemPrompt).toContain('## Introduction\n\n负责发现行为回归。')
|
|
478
|
+
expect(data.targetBody.indexOf('基础身份。')).toBeLessThan(data.targetBody.indexOf('## Introduction'))
|
|
479
|
+
expect(data.targetBody.indexOf('## Introduction')).toBeLessThan(data.targetBody.indexOf('## Personality'))
|
|
480
|
+
expect(data.targetBody.indexOf('## Personality')).toBeLessThan(data.targetBody.indexOf('## Memory'))
|
|
481
|
+
})
|
|
482
|
+
|
|
483
|
+
it('appends alias prompt files for index json entities', async () => {
|
|
484
|
+
const workspace = await createWorkspace()
|
|
485
|
+
|
|
486
|
+
await writeDocument(
|
|
487
|
+
join(workspace, '.ai/entities/reviewer/index.json'),
|
|
488
|
+
JSON.stringify(
|
|
489
|
+
{
|
|
490
|
+
description: '代码评审实体',
|
|
491
|
+
prompt: '基础身份。'
|
|
492
|
+
},
|
|
493
|
+
null,
|
|
494
|
+
2
|
|
495
|
+
)
|
|
496
|
+
)
|
|
497
|
+
await writeDocument(
|
|
498
|
+
join(workspace, '.ai/entities/reviewer/介绍.md'),
|
|
499
|
+
'负责发现行为回归。'
|
|
500
|
+
)
|
|
501
|
+
await writeDocument(
|
|
502
|
+
join(workspace, '.ai/entities/reviewer/personality.md'),
|
|
503
|
+
'说话克制,先给出高风险问题。'
|
|
504
|
+
)
|
|
505
|
+
await writeDocument(
|
|
506
|
+
join(workspace, '.ai/entities/reviewer/记忆.md'),
|
|
507
|
+
'记住上次评审指出过缺少验证。'
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
const bundle = await resolveWorkspaceAssetBundle({
|
|
511
|
+
cwd: workspace,
|
|
512
|
+
useDefaultVibeForgeMcpServer: false
|
|
513
|
+
})
|
|
514
|
+
const [data] = await resolvePromptAssetSelection({
|
|
515
|
+
bundle,
|
|
516
|
+
type: 'entity',
|
|
517
|
+
name: 'reviewer'
|
|
518
|
+
})
|
|
519
|
+
|
|
520
|
+
expect(data.targetBody).toContain('基础身份。')
|
|
521
|
+
expect(data.targetBody).toContain('## Introduction\n\n负责发现行为回归。')
|
|
522
|
+
expect(data.targetBody).toContain('## Personality\n\n说话克制,先给出高风险问题。')
|
|
523
|
+
expect(data.targetBody).toContain('## Memory\n\n记住上次评审指出过缺少验证。')
|
|
524
|
+
})
|
|
525
|
+
|
|
526
|
+
it('inherits prompt, rules, and skills from scoped plugin entities', async () => {
|
|
527
|
+
const workspace = await createWorkspace()
|
|
528
|
+
|
|
529
|
+
await installPluginPackage(workspace, '@vibe-forge/plugin-standard-dev', {
|
|
530
|
+
'package.json': JSON.stringify(
|
|
531
|
+
{
|
|
532
|
+
name: '@vibe-forge/plugin-standard-dev',
|
|
533
|
+
version: '1.0.0'
|
|
534
|
+
},
|
|
535
|
+
null,
|
|
536
|
+
2
|
|
537
|
+
),
|
|
538
|
+
'rules/base-review.md': '---\ndescription: 标准评审规则\n---\n父规则正文',
|
|
539
|
+
'skills/base-review/SKILL.md': '---\ndescription: 标准评审技能\n---\n父技能正文',
|
|
540
|
+
'entities/base-reviewer/README.md': [
|
|
541
|
+
'---',
|
|
542
|
+
'description: 标准评审实体',
|
|
543
|
+
'rules:',
|
|
544
|
+
' - base-review',
|
|
545
|
+
'skills:',
|
|
546
|
+
' - base-review',
|
|
547
|
+
'tools:',
|
|
548
|
+
' include:',
|
|
549
|
+
' - Read',
|
|
550
|
+
'---',
|
|
551
|
+
'父实体提示。'
|
|
552
|
+
].join('\n')
|
|
553
|
+
})
|
|
554
|
+
await writeDocument(
|
|
555
|
+
join(workspace, '.ai/rules/base-review.md'),
|
|
556
|
+
'---\ndescription: 同名本地规则\n---\n同名本地规则正文'
|
|
557
|
+
)
|
|
558
|
+
await writeDocument(
|
|
559
|
+
join(workspace, '.ai/rules/frontend.md'),
|
|
560
|
+
'---\ndescription: 前端规则\n---\n子规则正文'
|
|
561
|
+
)
|
|
562
|
+
await writeDocument(
|
|
563
|
+
join(workspace, '.ai/skills/base-review/SKILL.md'),
|
|
564
|
+
'---\ndescription: 同名本地技能\n---\n同名本地技能正文'
|
|
565
|
+
)
|
|
566
|
+
await writeDocument(
|
|
567
|
+
join(workspace, '.ai/skills/frontend/SKILL.md'),
|
|
568
|
+
'---\ndescription: 前端技能\n---\n子技能正文'
|
|
569
|
+
)
|
|
570
|
+
await writeDocument(
|
|
571
|
+
join(workspace, '.ai/entities/frontend-reviewer/README.md'),
|
|
572
|
+
[
|
|
573
|
+
'---',
|
|
574
|
+
'description: 前端评审实体',
|
|
575
|
+
'extends: std/base-reviewer',
|
|
576
|
+
'rules:',
|
|
577
|
+
' - frontend',
|
|
578
|
+
'skills:',
|
|
579
|
+
' - frontend',
|
|
580
|
+
'tools:',
|
|
581
|
+
' include:',
|
|
582
|
+
' - Grep',
|
|
583
|
+
'---',
|
|
584
|
+
'子实体提示。'
|
|
585
|
+
].join('\n')
|
|
586
|
+
)
|
|
587
|
+
|
|
588
|
+
const bundle = await resolveWorkspaceAssetBundle({
|
|
589
|
+
cwd: workspace,
|
|
590
|
+
configs: [{
|
|
591
|
+
plugins: [
|
|
592
|
+
{ id: 'standard-dev', scope: 'std' }
|
|
593
|
+
]
|
|
594
|
+
}, undefined],
|
|
595
|
+
useDefaultVibeForgeMcpServer: false
|
|
596
|
+
})
|
|
597
|
+
const [data, options] = await resolvePromptAssetSelection({
|
|
598
|
+
bundle,
|
|
599
|
+
type: 'entity',
|
|
600
|
+
name: 'frontend-reviewer'
|
|
601
|
+
})
|
|
602
|
+
const parentEntityId = bundle.entities.find(asset => asset.displayName === 'std/base-reviewer')?.id
|
|
603
|
+
const childEntityId = bundle.entities.find(asset => asset.displayName === 'frontend-reviewer')?.id
|
|
604
|
+
|
|
605
|
+
expect(data.targetBody).toContain('父实体提示。\n\n子实体提示。')
|
|
606
|
+
expect(data.targetSkills.map(skill => skill.resolvedName)).toEqual(['std/base-review', 'frontend'])
|
|
607
|
+
expect(options.systemPrompt).toContain('> 父规则正文')
|
|
608
|
+
expect(options.systemPrompt).toContain('> 子规则正文')
|
|
609
|
+
expect(options.systemPrompt).not.toContain('> 同名本地规则正文')
|
|
610
|
+
expect(options.systemPrompt).not.toContain('<skill-content>\n同名本地技能正文\n</skill-content>')
|
|
611
|
+
expect(options.tools).toEqual({ include: ['Grep'] })
|
|
612
|
+
expect(options.promptAssetIds).toEqual(expect.arrayContaining([parentEntityId, childEntityId]))
|
|
613
|
+
})
|
|
614
|
+
|
|
615
|
+
it('composes multiple parent entities in extends order', async () => {
|
|
616
|
+
const workspace = await createWorkspace()
|
|
617
|
+
|
|
618
|
+
await writeDocument(
|
|
619
|
+
join(workspace, '.ai/entities/base-a.md'),
|
|
620
|
+
[
|
|
621
|
+
'---',
|
|
622
|
+
'description: 父实体 A',
|
|
623
|
+
'tools:',
|
|
624
|
+
' include:',
|
|
625
|
+
' - Read',
|
|
626
|
+
'---',
|
|
627
|
+
'父实体 A 提示。'
|
|
628
|
+
].join('\n')
|
|
629
|
+
)
|
|
630
|
+
await writeDocument(
|
|
631
|
+
join(workspace, '.ai/entities/base-b.md'),
|
|
632
|
+
[
|
|
633
|
+
'---',
|
|
634
|
+
'description: 父实体 B',
|
|
635
|
+
'tools:',
|
|
636
|
+
' include:',
|
|
637
|
+
' - Bash',
|
|
638
|
+
'---',
|
|
639
|
+
'父实体 B 提示。'
|
|
640
|
+
].join('\n')
|
|
641
|
+
)
|
|
642
|
+
await writeDocument(
|
|
643
|
+
join(workspace, '.ai/entities/child.md'),
|
|
644
|
+
[
|
|
645
|
+
'---',
|
|
646
|
+
'description: 子实体',
|
|
647
|
+
'extends:',
|
|
648
|
+
' - base-a',
|
|
649
|
+
' - base-b',
|
|
650
|
+
'---',
|
|
651
|
+
'子实体提示。'
|
|
652
|
+
].join('\n')
|
|
653
|
+
)
|
|
654
|
+
|
|
655
|
+
const bundle = await resolveWorkspaceAssetBundle({
|
|
656
|
+
cwd: workspace,
|
|
657
|
+
useDefaultVibeForgeMcpServer: false
|
|
658
|
+
})
|
|
659
|
+
const [data, options] = await resolvePromptAssetSelection({
|
|
660
|
+
bundle,
|
|
661
|
+
type: 'entity',
|
|
662
|
+
name: 'child'
|
|
663
|
+
})
|
|
664
|
+
|
|
665
|
+
expect(data.targetBody).toContain('父实体 A 提示。\n\n父实体 B 提示。\n\n子实体提示。')
|
|
666
|
+
expect(options.tools).toEqual({ include: ['Bash'] })
|
|
667
|
+
})
|
|
668
|
+
|
|
669
|
+
it('lets the current entity replace inherited rules', async () => {
|
|
670
|
+
const workspace = await createWorkspace()
|
|
671
|
+
|
|
672
|
+
await writeDocument(
|
|
673
|
+
join(workspace, '.ai/rules/base.md'),
|
|
674
|
+
'---\ndescription: 父规则\n---\n父规则正文'
|
|
675
|
+
)
|
|
676
|
+
await writeDocument(
|
|
677
|
+
join(workspace, '.ai/rules/child.md'),
|
|
678
|
+
'---\ndescription: 子规则\n---\n子规则正文'
|
|
679
|
+
)
|
|
680
|
+
await writeDocument(
|
|
681
|
+
join(workspace, '.ai/entities/base.md'),
|
|
682
|
+
[
|
|
683
|
+
'---',
|
|
684
|
+
'description: 父实体',
|
|
685
|
+
'rules:',
|
|
686
|
+
' - base',
|
|
687
|
+
'---',
|
|
688
|
+
'父实体提示。'
|
|
689
|
+
].join('\n')
|
|
690
|
+
)
|
|
691
|
+
await writeDocument(
|
|
692
|
+
join(workspace, '.ai/entities/child.md'),
|
|
693
|
+
[
|
|
694
|
+
'---',
|
|
695
|
+
'description: 子实体',
|
|
696
|
+
'extends: base',
|
|
697
|
+
'inherit:',
|
|
698
|
+
' rules: replace',
|
|
699
|
+
'rules:',
|
|
700
|
+
' - child',
|
|
701
|
+
'---',
|
|
702
|
+
'子实体提示。'
|
|
703
|
+
].join('\n')
|
|
704
|
+
)
|
|
705
|
+
|
|
706
|
+
const bundle = await resolveWorkspaceAssetBundle({
|
|
707
|
+
cwd: workspace,
|
|
708
|
+
useDefaultVibeForgeMcpServer: false
|
|
709
|
+
})
|
|
710
|
+
const [, options] = await resolvePromptAssetSelection({
|
|
711
|
+
bundle,
|
|
712
|
+
type: 'entity',
|
|
713
|
+
name: 'child'
|
|
714
|
+
})
|
|
715
|
+
|
|
716
|
+
expect(options.systemPrompt).toContain('> 子规则正文')
|
|
717
|
+
expect(options.systemPrompt).toContain('> Use when: 父规则')
|
|
718
|
+
expect(options.systemPrompt).not.toContain('> 父规则正文')
|
|
719
|
+
})
|
|
720
|
+
|
|
721
|
+
it('rejects circular entity inheritance', async () => {
|
|
722
|
+
const workspace = await createWorkspace()
|
|
723
|
+
|
|
724
|
+
await writeDocument(
|
|
725
|
+
join(workspace, '.ai/entities/a.md'),
|
|
726
|
+
'---\ndescription: A\nextends: b\n---\nA'
|
|
727
|
+
)
|
|
728
|
+
await writeDocument(
|
|
729
|
+
join(workspace, '.ai/entities/b.md'),
|
|
730
|
+
'---\ndescription: B\nextends: a\n---\nB'
|
|
731
|
+
)
|
|
732
|
+
|
|
733
|
+
const bundle = await resolveWorkspaceAssetBundle({
|
|
734
|
+
cwd: workspace,
|
|
735
|
+
useDefaultVibeForgeMcpServer: false
|
|
736
|
+
})
|
|
737
|
+
|
|
738
|
+
await expect(resolvePromptAssetSelection({
|
|
739
|
+
bundle,
|
|
740
|
+
type: 'entity',
|
|
741
|
+
name: 'a'
|
|
742
|
+
})).rejects.toThrow('Circular entity inheritance detected: a -> b -> a')
|
|
743
|
+
})
|
|
744
|
+
|
|
438
745
|
it('does not preload all skills when the target entity omits skill references', async () => {
|
|
439
746
|
const workspace = await createWorkspace()
|
|
440
747
|
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/* eslint-disable import/first -- hoisted vitest mocks must be declared before importing the bundle entrypoint */
|
|
2
|
+
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'
|
|
3
|
+
import os from 'node:os'
|
|
4
|
+
import path, { join } from 'node:path'
|
|
5
|
+
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
7
|
+
|
|
8
|
+
const mocks = vi.hoisted(() => ({
|
|
9
|
+
findSkillsCli: vi.fn(),
|
|
10
|
+
installSkillsCliRefToTemp: vi.fn(),
|
|
11
|
+
installSkillsCliSkillToTemp: vi.fn()
|
|
12
|
+
}))
|
|
13
|
+
|
|
14
|
+
vi.mock('@vibe-forge/utils/skills-cli', async () => {
|
|
15
|
+
const actual = await vi.importActual<typeof import('@vibe-forge/utils/skills-cli')>('@vibe-forge/utils/skills-cli')
|
|
16
|
+
return {
|
|
17
|
+
...actual,
|
|
18
|
+
findSkillsCli: mocks.findSkillsCli,
|
|
19
|
+
installSkillsCliRefToTemp: mocks.installSkillsCliRefToTemp,
|
|
20
|
+
installSkillsCliSkillToTemp: mocks.installSkillsCliSkillToTemp
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
import { buildAdapterAssetPlan, resolveWorkspaceAssetBundle } from '#~/index.js'
|
|
25
|
+
|
|
26
|
+
import { createWorkspace, writeDocument } from './test-helpers'
|
|
27
|
+
|
|
28
|
+
describe('skills CLI dependency resolution', () => {
|
|
29
|
+
let installWorkspace: string
|
|
30
|
+
|
|
31
|
+
beforeEach(async () => {
|
|
32
|
+
installWorkspace = await mkdtemp(path.join(os.tmpdir(), 'vf-skills-cli-dependency-'))
|
|
33
|
+
vi.clearAllMocks()
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
afterEach(async () => {
|
|
37
|
+
await rm(installWorkspace, { recursive: true, force: true })
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('installs missing bare-name dependencies through skills CLI by default', async () => {
|
|
41
|
+
const workspace = await createWorkspace()
|
|
42
|
+
const installedSkillDir = join(installWorkspace, '.agents', 'skills', 'frontend-design')
|
|
43
|
+
await mkdir(installedSkillDir, { recursive: true })
|
|
44
|
+
await writeFile(
|
|
45
|
+
join(installedSkillDir, 'SKILL.md'),
|
|
46
|
+
'---\nname: frontend-design\ndescription: UI design guidance\n---\nUse strong visual hierarchy.\n'
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
mocks.findSkillsCli.mockResolvedValue([
|
|
50
|
+
{
|
|
51
|
+
installRef: 'anthropics/skills@frontend-design',
|
|
52
|
+
source: 'anthropics/skills',
|
|
53
|
+
skill: 'frontend-design'
|
|
54
|
+
}
|
|
55
|
+
])
|
|
56
|
+
mocks.installSkillsCliRefToTemp.mockResolvedValue({
|
|
57
|
+
tempDir: installWorkspace,
|
|
58
|
+
installedSkill: {
|
|
59
|
+
dirName: 'frontend-design',
|
|
60
|
+
name: 'frontend-design',
|
|
61
|
+
sourcePath: installedSkillDir
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
await writeDocument(
|
|
66
|
+
join(workspace, '.ai/skills/app-builder/SKILL.md'),
|
|
67
|
+
[
|
|
68
|
+
'---',
|
|
69
|
+
'name: app-builder',
|
|
70
|
+
'description: Build apps',
|
|
71
|
+
'dependencies:',
|
|
72
|
+
' - frontend-design',
|
|
73
|
+
'---',
|
|
74
|
+
'Build the app.'
|
|
75
|
+
].join('\n')
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
const bundle = await resolveWorkspaceAssetBundle({
|
|
79
|
+
cwd: workspace,
|
|
80
|
+
configs: [undefined, undefined],
|
|
81
|
+
useDefaultVibeForgeMcpServer: false
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
await buildAdapterAssetPlan({
|
|
85
|
+
adapter: 'opencode',
|
|
86
|
+
bundle,
|
|
87
|
+
options: {
|
|
88
|
+
skills: {
|
|
89
|
+
include: ['app-builder']
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const dependency = bundle.skills.find(asset => asset.name === 'frontend-design')
|
|
95
|
+
expect(bundle.skills.map(asset => asset.name).sort()).toEqual(['app-builder', 'frontend-design'])
|
|
96
|
+
expect(dependency?.sourcePath).toContain(
|
|
97
|
+
'/.ai/caches/skill-dependencies/skills-cli/skills/latest/default/anthropics/skills/frontend-design/'
|
|
98
|
+
)
|
|
99
|
+
expect(mocks.findSkillsCli).toHaveBeenCalledWith({
|
|
100
|
+
config: undefined,
|
|
101
|
+
query: 'frontend-design'
|
|
102
|
+
})
|
|
103
|
+
expect(mocks.installSkillsCliRefToTemp).toHaveBeenCalledWith({
|
|
104
|
+
config: undefined,
|
|
105
|
+
installRef: 'anthropics/skills@frontend-design'
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('merges top-level skillsCli config ahead of legacy skills.cli aliases', async () => {
|
|
110
|
+
const workspace = await createWorkspace()
|
|
111
|
+
const installedSkillDir = join(installWorkspace, '.agents', 'skills', 'frontend-design')
|
|
112
|
+
await mkdir(installedSkillDir, { recursive: true })
|
|
113
|
+
await writeFile(
|
|
114
|
+
join(installedSkillDir, 'SKILL.md'),
|
|
115
|
+
'---\nname: frontend-design\ndescription: UI design guidance\n---\nUse internal design system.\n'
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
mocks.installSkillsCliSkillToTemp.mockResolvedValue({
|
|
119
|
+
tempDir: installWorkspace,
|
|
120
|
+
installedSkill: {
|
|
121
|
+
dirName: 'frontend-design',
|
|
122
|
+
name: 'frontend-design',
|
|
123
|
+
sourcePath: installedSkillDir
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
await writeDocument(
|
|
128
|
+
join(workspace, '.ai/skills/app-builder/SKILL.md'),
|
|
129
|
+
[
|
|
130
|
+
'---',
|
|
131
|
+
'name: app-builder',
|
|
132
|
+
'description: Build apps',
|
|
133
|
+
'dependencies:',
|
|
134
|
+
' - example-source/default/public@frontend-design',
|
|
135
|
+
'---',
|
|
136
|
+
'Build the app.'
|
|
137
|
+
].join('\n')
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
const bundle = await resolveWorkspaceAssetBundle({
|
|
141
|
+
cwd: workspace,
|
|
142
|
+
configs: [{
|
|
143
|
+
skills: {
|
|
144
|
+
cli: {
|
|
145
|
+
package: 'legacy-skills'
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
skillsCli: {
|
|
149
|
+
package: '@byted/skills',
|
|
150
|
+
registry: 'https://registry.example.com'
|
|
151
|
+
}
|
|
152
|
+
}, undefined],
|
|
153
|
+
useDefaultVibeForgeMcpServer: false
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
await buildAdapterAssetPlan({
|
|
157
|
+
adapter: 'opencode',
|
|
158
|
+
bundle,
|
|
159
|
+
options: {
|
|
160
|
+
skills: {
|
|
161
|
+
include: ['app-builder']
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
expect(mocks.installSkillsCliSkillToTemp).toHaveBeenCalledWith({
|
|
167
|
+
config: {
|
|
168
|
+
package: '@byted/skills',
|
|
169
|
+
registry: 'https://registry.example.com'
|
|
170
|
+
},
|
|
171
|
+
skill: 'frontend-design',
|
|
172
|
+
source: 'example-source/default/public'
|
|
173
|
+
})
|
|
174
|
+
})
|
|
175
|
+
})
|
package/__tests__/snapshot.ts
CHANGED
|
@@ -169,6 +169,7 @@ const summarizeDiagnostics = (
|
|
|
169
169
|
assetId: assetIdMap.get(diagnostic.assetId) ?? diagnostic.assetId,
|
|
170
170
|
adapter: diagnostic.adapter,
|
|
171
171
|
status: diagnostic.status,
|
|
172
|
+
source: diagnostic.source,
|
|
172
173
|
reason: diagnostic.reason,
|
|
173
174
|
scope: diagnostic.scope,
|
|
174
175
|
packageId: diagnostic.packageId
|
|
@@ -5,10 +5,14 @@ import { dirname, join } from 'node:path'
|
|
|
5
5
|
import { afterEach } from 'vitest'
|
|
6
6
|
|
|
7
7
|
const tempDirs: string[] = []
|
|
8
|
+
const originalRealHome = process.env.__VF_PROJECT_REAL_HOME__
|
|
8
9
|
|
|
9
10
|
export const createWorkspace = async () => {
|
|
10
11
|
const dir = await mkdtemp(join(tmpdir(), 'workspace-assets-'))
|
|
12
|
+
const realHome = await mkdtemp(join(tmpdir(), 'workspace-assets-home-'))
|
|
11
13
|
tempDirs.push(dir)
|
|
14
|
+
tempDirs.push(realHome)
|
|
15
|
+
process.env.__VF_PROJECT_REAL_HOME__ = realHome
|
|
12
16
|
return dir
|
|
13
17
|
}
|
|
14
18
|
|
|
@@ -32,4 +36,9 @@ export const installPluginPackage = async (
|
|
|
32
36
|
|
|
33
37
|
afterEach(async () => {
|
|
34
38
|
await Promise.all(tempDirs.splice(0).map(dir => rm(dir, { recursive: true, force: true })))
|
|
39
|
+
if (originalRealHome == null) {
|
|
40
|
+
delete process.env.__VF_PROJECT_REAL_HOME__
|
|
41
|
+
} else {
|
|
42
|
+
process.env.__VF_PROJECT_REAL_HOME__ = originalRealHome
|
|
43
|
+
}
|
|
35
44
|
})
|
|
@@ -189,7 +189,7 @@ describe('workspace assets snapshots', () => {
|
|
|
189
189
|
})
|
|
190
190
|
|
|
191
191
|
const adapters = ['claude-code', 'codex', 'gemini', 'opencode'] as const
|
|
192
|
-
const plans = adapters.map(adapter => (
|
|
192
|
+
const plans = await Promise.all(adapters.map(adapter => (
|
|
193
193
|
buildAdapterAssetPlan({
|
|
194
194
|
adapter,
|
|
195
195
|
bundle,
|
|
@@ -201,7 +201,7 @@ describe('workspace assets snapshots', () => {
|
|
|
201
201
|
}
|
|
202
202
|
}
|
|
203
203
|
})
|
|
204
|
-
))
|
|
204
|
+
)))
|
|
205
205
|
|
|
206
206
|
await expect(serializeWorkspaceAssetsSnapshot({
|
|
207
207
|
cwd: workspace,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibe-forge/workspace-assets",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "Workspace asset resolution and adapter asset planning for Vibe Forge",
|
|
5
5
|
"imports": {
|
|
6
6
|
"#~/*.js": {
|
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
"./package.json": "./package.json"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
+
"@vibe-forge/config": "^2.0.2",
|
|
29
30
|
"fast-glob": "^3.3.3",
|
|
30
31
|
"front-matter": "^4.0.2",
|
|
31
32
|
"js-yaml": "^4.1.1",
|
|
32
|
-
"@vibe-forge/
|
|
33
|
-
"@vibe-forge/
|
|
34
|
-
"@vibe-forge/
|
|
35
|
-
"@vibe-forge/utils": "^1.0.0"
|
|
33
|
+
"@vibe-forge/utils": "^2.0.1",
|
|
34
|
+
"@vibe-forge/types": "^2.0.1",
|
|
35
|
+
"@vibe-forge/definition-core": "^2.0.1"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/js-yaml": "^4.0.9"
|