@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.
- package/__tests__/__snapshots__/workspace-assets-rich.snapshot.json +56 -1
- package/__tests__/adapter-asset-plan.spec.ts +218 -6
- package/__tests__/bundle.spec.ts +602 -2
- package/__tests__/prompt-builders.spec.ts +39 -0
- package/__tests__/prompt-selection.spec.ts +307 -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 +4 -4
- package/src/adapter-asset-plan.ts +42 -66
- package/src/asset-source.ts +13 -0
- package/src/bundle-internal.ts +226 -21
- package/src/bundle.ts +2 -0
- package/src/home-bridge.ts +1 -0
- package/src/index.ts +3 -0
- package/src/prompt-builders.ts +4 -0
- package/src/prompt-selection.ts +44 -19
- package/src/selection-internal.ts +335 -1
- package/src/skill-dependencies.ts +361 -0
- package/src/skill-registry.ts +329 -0
- package/src/task-tool-guidance.ts +15 -0
- package/src/workspace-config.ts +132 -0
- package/src/workspace-prompt.ts +33 -0
- package/src/workspaces.ts +188 -0
- package/vibe-forge-workspace-assets-2.0.2.tgz +0 -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
|
|
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": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
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/utils": "^2.0.0",
|
|
33
33
|
"@vibe-forge/definition-core": "^2.0.0",
|
|
34
|
-
"@vibe-forge/types": "^2.0.
|
|
35
|
-
"@vibe-forge/
|
|
34
|
+
"@vibe-forge/types": "^2.0.2",
|
|
35
|
+
"@vibe-forge/utils": "^2.0.3"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/js-yaml": "^4.0.9"
|
|
@@ -11,9 +11,10 @@ import type {
|
|
|
11
11
|
} from '@vibe-forge/types'
|
|
12
12
|
|
|
13
13
|
import { resolveNativeSkillDiagnosticReason, supportsNativeProjectSkills } from './adapter-capabilities'
|
|
14
|
-
import {
|
|
14
|
+
import { resolveWorkspaceAssetSource } from './asset-source'
|
|
15
|
+
import { resolveSelectedMcpNames, resolveSelectedSkillAssetsWithDependencies } from './selection-internal'
|
|
15
16
|
|
|
16
|
-
export function buildAdapterAssetPlan(params: {
|
|
17
|
+
export async function buildAdapterAssetPlan(params: {
|
|
17
18
|
adapter: WorkspaceAssetAdapter
|
|
18
19
|
bundle: WorkspaceAssetBundle
|
|
19
20
|
options: {
|
|
@@ -21,17 +22,24 @@ export function buildAdapterAssetPlan(params: {
|
|
|
21
22
|
skills?: WorkspaceSkillSelection
|
|
22
23
|
promptAssetIds?: string[]
|
|
23
24
|
}
|
|
24
|
-
}): AdapterAssetPlan {
|
|
25
|
+
}): Promise<AdapterAssetPlan> {
|
|
25
26
|
const diagnostics: AssetDiagnostic[] = []
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
const pushDiagnostic = (
|
|
28
|
+
asset: Parameters<typeof resolveWorkspaceAssetSource>[0] & {
|
|
29
|
+
id: string
|
|
30
|
+
packageId?: string
|
|
31
|
+
scope?: string
|
|
32
|
+
instancePath?: string
|
|
33
|
+
taskOverlaySource?: string
|
|
34
|
+
},
|
|
35
|
+
diagnostic: Pick<AssetDiagnostic, 'adapter' | 'status' | 'reason'>
|
|
36
|
+
) => {
|
|
30
37
|
diagnostics.push({
|
|
31
|
-
assetId,
|
|
32
|
-
adapter:
|
|
33
|
-
status:
|
|
34
|
-
reason:
|
|
38
|
+
assetId: asset.id,
|
|
39
|
+
adapter: diagnostic.adapter,
|
|
40
|
+
status: diagnostic.status,
|
|
41
|
+
reason: diagnostic.reason,
|
|
42
|
+
source: resolveWorkspaceAssetSource(asset),
|
|
35
43
|
packageId: asset.packageId,
|
|
36
44
|
scope: asset.scope,
|
|
37
45
|
instancePath: asset.instancePath,
|
|
@@ -41,6 +49,16 @@ export function buildAdapterAssetPlan(params: {
|
|
|
41
49
|
})
|
|
42
50
|
}
|
|
43
51
|
|
|
52
|
+
for (const assetId of params.options.promptAssetIds ?? []) {
|
|
53
|
+
const asset = params.bundle.assets.find(item => item.id === assetId)
|
|
54
|
+
if (asset == null || asset.kind === 'mcpServer') continue
|
|
55
|
+
pushDiagnostic(asset, {
|
|
56
|
+
adapter: params.adapter,
|
|
57
|
+
status: 'prompt',
|
|
58
|
+
reason: 'Mapped into the generated system prompt.'
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
44
62
|
const selectedMcpNames = resolveSelectedMcpNames(params.bundle, params.options.mcpServers)
|
|
45
63
|
const mcpServers = Object.fromEntries(
|
|
46
64
|
selectedMcpNames.map(name => [name, params.bundle.mcpServers[name].payload.config])
|
|
@@ -48,25 +66,17 @@ export function buildAdapterAssetPlan(params: {
|
|
|
48
66
|
|
|
49
67
|
selectedMcpNames.forEach((name) => {
|
|
50
68
|
const asset = params.bundle.mcpServers[name]
|
|
51
|
-
|
|
52
|
-
assetId: asset.id,
|
|
69
|
+
pushDiagnostic(asset, {
|
|
53
70
|
adapter: params.adapter,
|
|
54
71
|
status: params.adapter === 'claude-code' ? 'native' : 'translated',
|
|
55
72
|
reason: params.adapter === 'claude-code'
|
|
56
73
|
? 'Mapped into adapter MCP settings.'
|
|
57
|
-
: 'Translated into adapter-specific MCP configuration.'
|
|
58
|
-
packageId: asset.packageId,
|
|
59
|
-
scope: asset.scope,
|
|
60
|
-
instancePath: asset.instancePath,
|
|
61
|
-
origin: asset.origin,
|
|
62
|
-
resolvedBy: asset.resolvedBy,
|
|
63
|
-
taskOverlaySource: asset.taskOverlaySource
|
|
74
|
+
: 'Translated into adapter-specific MCP configuration.'
|
|
64
75
|
})
|
|
65
76
|
})
|
|
66
77
|
|
|
67
78
|
params.bundle.hookPlugins.forEach((asset) => {
|
|
68
|
-
|
|
69
|
-
assetId: asset.id,
|
|
79
|
+
pushDiagnostic(asset, {
|
|
70
80
|
adapter: params.adapter,
|
|
71
81
|
status: params.adapter === 'copilot' ? 'translated' : 'native',
|
|
72
82
|
reason: params.adapter === 'claude-code'
|
|
@@ -79,80 +89,46 @@ export function buildAdapterAssetPlan(params: {
|
|
|
79
89
|
? 'Handled by the Vibe Forge task hook bridge.'
|
|
80
90
|
: params.adapter === 'kimi'
|
|
81
91
|
? 'Mapped into the Kimi native hooks bridge.'
|
|
82
|
-
: 'Mapped into the OpenCode native hooks bridge.'
|
|
83
|
-
packageId: asset.packageId,
|
|
84
|
-
scope: asset.scope,
|
|
85
|
-
instancePath: asset.instancePath,
|
|
86
|
-
origin: asset.origin,
|
|
87
|
-
resolvedBy: asset.resolvedBy,
|
|
88
|
-
taskOverlaySource: asset.taskOverlaySource
|
|
92
|
+
: 'Mapped into the OpenCode native hooks bridge.'
|
|
89
93
|
})
|
|
90
94
|
})
|
|
91
95
|
|
|
92
|
-
const selectedSkillAssets =
|
|
96
|
+
const selectedSkillAssets = await resolveSelectedSkillAssetsWithDependencies(params.bundle, params.options.skills)
|
|
93
97
|
if (supportsNativeProjectSkills(params.adapter)) {
|
|
94
98
|
selectedSkillAssets.forEach((asset) => {
|
|
95
|
-
|
|
96
|
-
assetId: asset.id,
|
|
99
|
+
pushDiagnostic(asset, {
|
|
97
100
|
adapter: params.adapter,
|
|
98
101
|
status: 'native',
|
|
99
|
-
reason: resolveNativeSkillDiagnosticReason(params.adapter)
|
|
100
|
-
packageId: asset.packageId,
|
|
101
|
-
scope: asset.scope,
|
|
102
|
-
instancePath: asset.instancePath,
|
|
103
|
-
origin: asset.origin,
|
|
104
|
-
resolvedBy: asset.resolvedBy,
|
|
105
|
-
taskOverlaySource: asset.taskOverlaySource
|
|
102
|
+
reason: resolveNativeSkillDiagnosticReason(params.adapter)
|
|
106
103
|
})
|
|
107
104
|
})
|
|
108
105
|
}
|
|
109
106
|
if (params.adapter === 'opencode') {
|
|
110
107
|
params.bundle.opencodeOverlayAssets.forEach((asset) => {
|
|
111
|
-
|
|
112
|
-
assetId: asset.id,
|
|
108
|
+
pushDiagnostic(asset, {
|
|
113
109
|
adapter: params.adapter,
|
|
114
110
|
status: 'native',
|
|
115
|
-
reason: 'Mirrored into OPENCODE_CONFIG_DIR as a native OpenCode asset.'
|
|
116
|
-
packageId: asset.packageId,
|
|
117
|
-
scope: asset.scope,
|
|
118
|
-
instancePath: asset.instancePath,
|
|
119
|
-
origin: asset.origin,
|
|
120
|
-
resolvedBy: asset.resolvedBy,
|
|
121
|
-
taskOverlaySource: asset.taskOverlaySource
|
|
111
|
+
reason: 'Mirrored into OPENCODE_CONFIG_DIR as a native OpenCode asset.'
|
|
122
112
|
})
|
|
123
113
|
})
|
|
124
114
|
} else if (params.adapter === 'codex' || params.adapter === 'copilot' || params.adapter === 'kimi') {
|
|
125
115
|
params.bundle.opencodeOverlayAssets.forEach((asset) => {
|
|
126
|
-
|
|
127
|
-
assetId: asset.id,
|
|
116
|
+
pushDiagnostic(asset, {
|
|
128
117
|
adapter: params.adapter,
|
|
129
118
|
status: 'skipped',
|
|
130
119
|
reason: params.adapter === 'codex'
|
|
131
120
|
? 'No stable native Codex mapping exists for this asset kind in V1.'
|
|
132
121
|
: params.adapter === 'copilot'
|
|
133
122
|
? 'No stable native Copilot mapping exists for this asset kind in V1.'
|
|
134
|
-
: 'No stable native Kimi mapping exists for this asset kind in V1.'
|
|
135
|
-
packageId: asset.packageId,
|
|
136
|
-
scope: asset.scope,
|
|
137
|
-
instancePath: asset.instancePath,
|
|
138
|
-
origin: asset.origin,
|
|
139
|
-
resolvedBy: asset.resolvedBy,
|
|
140
|
-
taskOverlaySource: asset.taskOverlaySource
|
|
123
|
+
: 'No stable native Kimi mapping exists for this asset kind in V1.'
|
|
141
124
|
})
|
|
142
125
|
})
|
|
143
126
|
} else if (params.adapter === 'gemini') {
|
|
144
127
|
params.bundle.opencodeOverlayAssets.forEach((asset) => {
|
|
145
|
-
|
|
146
|
-
assetId: asset.id,
|
|
128
|
+
pushDiagnostic(asset, {
|
|
147
129
|
adapter: params.adapter,
|
|
148
130
|
status: 'skipped',
|
|
149
|
-
reason: 'No stable native Gemini mapping exists for this asset kind in V1.'
|
|
150
|
-
packageId: asset.packageId,
|
|
151
|
-
scope: asset.scope,
|
|
152
|
-
instancePath: asset.instancePath,
|
|
153
|
-
origin: asset.origin,
|
|
154
|
-
resolvedBy: asset.resolvedBy,
|
|
155
|
-
taskOverlaySource: asset.taskOverlaySource
|
|
131
|
+
reason: 'No stable native Gemini mapping exists for this asset kind in V1.'
|
|
156
132
|
})
|
|
157
133
|
})
|
|
158
134
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { DefinitionSource, WorkspaceAsset } from '@vibe-forge/types'
|
|
2
|
+
|
|
3
|
+
import { HOME_BRIDGE_RESOLVED_BY } from './home-bridge'
|
|
4
|
+
|
|
5
|
+
export const resolveWorkspaceAssetSource = (
|
|
6
|
+
asset: Pick<WorkspaceAsset, 'origin' | 'resolvedBy'>
|
|
7
|
+
): DefinitionSource => (
|
|
8
|
+
asset.resolvedBy === HOME_BRIDGE_RESOLVED_BY
|
|
9
|
+
? 'home'
|
|
10
|
+
: asset.origin === 'plugin'
|
|
11
|
+
? 'plugin'
|
|
12
|
+
: 'project'
|
|
13
|
+
)
|