@vibe-forge/workspace-assets 0.9.0 → 0.9.1-alpha.0
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 +355 -360
- package/__tests__/adapter-asset-plan.spec.ts +40 -66
- package/__tests__/bundle.spec.ts +44 -168
- package/__tests__/prompt-selection.spec.ts +14 -358
- package/__tests__/snapshot.ts +127 -75
- package/__tests__/test-helpers.ts +0 -11
- package/__tests__/workspace-assets.snapshot.spec.ts +103 -72
- package/package.json +10 -10
- package/src/adapter-asset-plan.ts +174 -0
- package/src/bundle.ts +178 -0
- package/src/document-assets.ts +191 -0
- package/src/helpers.ts +35 -0
- package/src/index.ts +3 -1368
- package/src/internal-types.ts +39 -1
- package/src/plugin-assets.ts +175 -0
- package/src/prompt-selection.ts +151 -0
- package/LICENSE +0 -21
|
@@ -4,42 +4,43 @@ import { describe, expect, it } from 'vitest'
|
|
|
4
4
|
|
|
5
5
|
import { buildAdapterAssetPlan, resolvePromptAssetSelection, resolveWorkspaceAssetBundle } from '#~/index.js'
|
|
6
6
|
|
|
7
|
-
import { createWorkspace,
|
|
7
|
+
import { createWorkspace, writeDocument } from './test-helpers'
|
|
8
8
|
|
|
9
9
|
describe('buildAdapterAssetPlan', () => {
|
|
10
|
-
it('builds codex diagnostics for prompt, mcp,
|
|
10
|
+
it('builds codex diagnostics for prompt, mcp, native hooks, and unsupported claude native plugins', async () => {
|
|
11
11
|
const workspace = await createWorkspace()
|
|
12
12
|
|
|
13
|
-
await installPluginPackage(workspace, '@vibe-forge/plugin-logger', {
|
|
14
|
-
'package.json': JSON.stringify({
|
|
15
|
-
name: '@vibe-forge/plugin-logger',
|
|
16
|
-
version: '1.0.0'
|
|
17
|
-
}, null, 2),
|
|
18
|
-
'hooks.js': 'module.exports = {}\n'
|
|
19
|
-
})
|
|
20
|
-
await installPluginPackage(workspace, '@vibe-forge/plugin-demo', {
|
|
21
|
-
'package.json': JSON.stringify({
|
|
22
|
-
name: '@vibe-forge/plugin-demo',
|
|
23
|
-
version: '1.0.0'
|
|
24
|
-
}, null, 2),
|
|
25
|
-
'opencode/commands/review.md': '# review\n'
|
|
26
|
-
})
|
|
27
13
|
await writeDocument(
|
|
28
|
-
join(workspace, '.ai
|
|
29
|
-
|
|
14
|
+
join(workspace, '.ai.config.json'),
|
|
15
|
+
JSON.stringify({
|
|
16
|
+
plugins: {
|
|
17
|
+
logger: {}
|
|
18
|
+
},
|
|
19
|
+
enabledPlugins: {
|
|
20
|
+
logger: true
|
|
21
|
+
},
|
|
22
|
+
mcpServers: {
|
|
23
|
+
docs: {
|
|
24
|
+
command: 'npx',
|
|
25
|
+
args: ['docs-server']
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
})
|
|
30
29
|
)
|
|
31
30
|
await writeDocument(
|
|
32
|
-
join(workspace, '.ai/skills/
|
|
33
|
-
'---\ndescription:
|
|
31
|
+
join(workspace, '.ai/skills/research/SKILL.md'),
|
|
32
|
+
'---\ndescription: 检索资料\n---\n阅读 README.md'
|
|
34
33
|
)
|
|
35
34
|
|
|
36
35
|
const bundle = await resolveWorkspaceAssetBundle({
|
|
37
36
|
cwd: workspace,
|
|
38
37
|
configs: [{
|
|
39
|
-
plugins:
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
plugins: {
|
|
39
|
+
logger: {}
|
|
40
|
+
},
|
|
41
|
+
enabledPlugins: {
|
|
42
|
+
logger: true
|
|
43
|
+
},
|
|
43
44
|
mcpServers: {
|
|
44
45
|
docs: {
|
|
45
46
|
command: 'npx',
|
|
@@ -49,17 +50,6 @@ describe('buildAdapterAssetPlan', () => {
|
|
|
49
50
|
}, undefined],
|
|
50
51
|
useDefaultVibeForgeMcpServer: false
|
|
51
52
|
})
|
|
52
|
-
const researchSkillId = bundle.skills.find(asset => asset.name === 'research')?.id
|
|
53
|
-
const reviewSkillId = bundle.skills.find(asset => asset.name === 'review')?.id
|
|
54
|
-
const loggerHookPluginId = bundle.hookPlugins.find(asset => asset.packageId === '@vibe-forge/plugin-logger')?.id
|
|
55
|
-
const demoCommandId = bundle.opencodeOverlayAssets.find(asset => asset.kind === 'command')?.id
|
|
56
|
-
const docsMcpId = bundle.mcpServers.docs?.id
|
|
57
|
-
expect(researchSkillId).toBeDefined()
|
|
58
|
-
expect(reviewSkillId).toBeDefined()
|
|
59
|
-
expect(loggerHookPluginId).toBeDefined()
|
|
60
|
-
expect(demoCommandId).toBeDefined()
|
|
61
|
-
expect(docsMcpId).toBeDefined()
|
|
62
|
-
|
|
63
53
|
const [, resolvedOptions] = await resolvePromptAssetSelection({
|
|
64
54
|
bundle,
|
|
65
55
|
type: undefined,
|
|
@@ -83,33 +73,32 @@ describe('buildAdapterAssetPlan', () => {
|
|
|
83
73
|
})
|
|
84
74
|
|
|
85
75
|
expect(plan.mcpServers).toHaveProperty('docs')
|
|
76
|
+
expect(plan.native.codexHooks?.supportedEvents).toEqual([
|
|
77
|
+
'SessionStart',
|
|
78
|
+
'UserPromptSubmit',
|
|
79
|
+
'PreToolUse',
|
|
80
|
+
'PostToolUse',
|
|
81
|
+
'Stop'
|
|
82
|
+
])
|
|
86
83
|
expect(plan.diagnostics).toEqual(expect.arrayContaining([
|
|
87
84
|
expect.objectContaining({
|
|
88
|
-
assetId: researchSkillId,
|
|
89
85
|
adapter: 'codex',
|
|
90
86
|
status: 'prompt'
|
|
91
87
|
}),
|
|
92
88
|
expect.objectContaining({
|
|
93
89
|
adapter: 'codex',
|
|
94
90
|
status: 'native',
|
|
95
|
-
assetId:
|
|
91
|
+
assetId: 'hookPlugin:project:logger'
|
|
96
92
|
}),
|
|
97
93
|
expect.objectContaining({
|
|
98
94
|
adapter: 'codex',
|
|
99
95
|
status: 'translated',
|
|
100
|
-
assetId:
|
|
96
|
+
assetId: 'mcpServer:project:docs'
|
|
101
97
|
}),
|
|
102
98
|
expect.objectContaining({
|
|
103
99
|
adapter: 'codex',
|
|
104
100
|
status: 'skipped',
|
|
105
|
-
assetId:
|
|
106
|
-
})
|
|
107
|
-
]))
|
|
108
|
-
expect(plan.diagnostics).not.toEqual(expect.arrayContaining([
|
|
109
|
-
expect.objectContaining({
|
|
110
|
-
assetId: reviewSkillId,
|
|
111
|
-
adapter: 'codex',
|
|
112
|
-
status: 'prompt'
|
|
101
|
+
assetId: 'nativePlugin:claude-code:logger'
|
|
113
102
|
})
|
|
114
103
|
]))
|
|
115
104
|
})
|
|
@@ -117,25 +106,18 @@ describe('buildAdapterAssetPlan', () => {
|
|
|
117
106
|
it('builds opencode overlays for skills and native commands', async () => {
|
|
118
107
|
const workspace = await createWorkspace()
|
|
119
108
|
|
|
120
|
-
await installPluginPackage(workspace, '@vibe-forge/plugin-demo', {
|
|
121
|
-
'package.json': JSON.stringify({
|
|
122
|
-
name: '@vibe-forge/plugin-demo',
|
|
123
|
-
version: '1.0.0'
|
|
124
|
-
}, null, 2),
|
|
125
|
-
'opencode/commands/review.md': '# review\n'
|
|
126
|
-
})
|
|
127
109
|
await writeDocument(
|
|
128
110
|
join(workspace, '.ai/skills/research/SKILL.md'),
|
|
129
111
|
'---\ndescription: 检索资料\n---\n阅读 README.md'
|
|
130
112
|
)
|
|
113
|
+
await writeDocument(
|
|
114
|
+
join(workspace, '.ai/plugins/demo/opencode/commands/review.md'),
|
|
115
|
+
'# review'
|
|
116
|
+
)
|
|
131
117
|
|
|
132
118
|
const bundle = await resolveWorkspaceAssetBundle({
|
|
133
119
|
cwd: workspace,
|
|
134
|
-
configs: [
|
|
135
|
-
plugins: [
|
|
136
|
-
{ id: 'demo', scope: 'demo' }
|
|
137
|
-
]
|
|
138
|
-
}, undefined],
|
|
120
|
+
configs: [undefined, undefined],
|
|
139
121
|
useDefaultVibeForgeMcpServer: false
|
|
140
122
|
})
|
|
141
123
|
const plan = buildAdapterAssetPlan({
|
|
@@ -147,7 +129,6 @@ describe('buildAdapterAssetPlan', () => {
|
|
|
147
129
|
}
|
|
148
130
|
}
|
|
149
131
|
})
|
|
150
|
-
const commandAsset = bundle.opencodeOverlayAssets.find(asset => asset.kind === 'command')
|
|
151
132
|
|
|
152
133
|
expect(plan.overlays).toEqual(expect.arrayContaining([
|
|
153
134
|
expect.objectContaining({
|
|
@@ -159,12 +140,5 @@ describe('buildAdapterAssetPlan', () => {
|
|
|
159
140
|
targetPath: 'commands/review.md'
|
|
160
141
|
})
|
|
161
142
|
]))
|
|
162
|
-
expect(plan.diagnostics).toEqual(expect.arrayContaining([
|
|
163
|
-
expect.objectContaining({
|
|
164
|
-
assetId: commandAsset?.id,
|
|
165
|
-
adapter: 'opencode',
|
|
166
|
-
status: 'native'
|
|
167
|
-
})
|
|
168
|
-
]))
|
|
169
143
|
})
|
|
170
144
|
})
|
package/__tests__/bundle.spec.ts
CHANGED
|
@@ -1,62 +1,65 @@
|
|
|
1
|
+
import { join } from 'node:path'
|
|
1
2
|
import process from 'node:process'
|
|
2
3
|
|
|
3
4
|
import { describe, expect, it } from 'vitest'
|
|
4
5
|
|
|
5
6
|
import { resolveWorkspaceAssetBundle } from '#~/index.js'
|
|
6
7
|
|
|
7
|
-
import { createWorkspace,
|
|
8
|
+
import { createWorkspace, writeDocument } from './test-helpers'
|
|
8
9
|
|
|
9
10
|
describe('resolveWorkspaceAssetBundle', () => {
|
|
10
|
-
it('
|
|
11
|
+
it('treats enabledPlugins as a global asset switch', async () => {
|
|
11
12
|
const workspace = await createWorkspace()
|
|
12
13
|
|
|
13
|
-
await
|
|
14
|
-
'
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
14
|
+
await writeDocument(
|
|
15
|
+
join(workspace, '.ai.config.json'),
|
|
16
|
+
JSON.stringify({
|
|
17
|
+
plugins: {
|
|
18
|
+
logger: {}
|
|
19
|
+
},
|
|
20
|
+
enabledPlugins: {
|
|
21
|
+
logger: false,
|
|
22
|
+
demo: false
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
)
|
|
26
|
+
await writeDocument(
|
|
27
|
+
join(workspace, '.ai/plugins/demo/skills/research/SKILL.md'),
|
|
28
|
+
'---\ndescription: 检索资料\n---\n阅读 README.md'
|
|
29
|
+
)
|
|
30
|
+
await writeDocument(
|
|
31
|
+
join(workspace, '.ai/plugins/demo/rules/review.md'),
|
|
32
|
+
'---\ndescription: 评审规则\n---\n必须检查风险'
|
|
33
|
+
)
|
|
34
|
+
await writeDocument(
|
|
35
|
+
join(workspace, '.ai/plugins/demo/mcp/browser.json'),
|
|
36
|
+
JSON.stringify({ command: 'npx', args: ['browser-server'] })
|
|
37
|
+
)
|
|
38
|
+
await writeDocument(
|
|
39
|
+
join(workspace, '.ai/plugins/demo/opencode/commands/review.md'),
|
|
40
|
+
'# review'
|
|
41
|
+
)
|
|
30
42
|
|
|
31
43
|
const bundle = await resolveWorkspaceAssetBundle({
|
|
32
44
|
cwd: workspace,
|
|
33
45
|
configs: [{
|
|
34
|
-
plugins:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
46
|
+
plugins: {
|
|
47
|
+
logger: {}
|
|
48
|
+
},
|
|
49
|
+
enabledPlugins: {
|
|
50
|
+
logger: false,
|
|
51
|
+
demo: false
|
|
52
|
+
}
|
|
38
53
|
}, undefined],
|
|
39
54
|
useDefaultVibeForgeMcpServer: false
|
|
40
55
|
})
|
|
41
56
|
|
|
42
|
-
expect(bundle.skills
|
|
43
|
-
expect(bundle.rules
|
|
44
|
-
expect(Object.keys(bundle.mcpServers)).
|
|
45
|
-
expect(bundle.hookPlugins).
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
})
|
|
49
|
-
]))
|
|
50
|
-
expect(bundle.hookPlugins).toHaveLength(1)
|
|
51
|
-
expect(bundle.opencodeOverlayAssets).toEqual(expect.arrayContaining([
|
|
52
|
-
expect.objectContaining({
|
|
53
|
-
kind: 'command',
|
|
54
|
-
sourcePath: expect.stringContaining('/node_modules/@vibe-forge/plugin-demo/opencode/commands/review.md'),
|
|
55
|
-
payload: expect.objectContaining({
|
|
56
|
-
targetSubpath: 'commands/review.md'
|
|
57
|
-
})
|
|
58
|
-
})
|
|
59
|
-
]))
|
|
57
|
+
expect(bundle.skills).toHaveLength(0)
|
|
58
|
+
expect(bundle.rules).toHaveLength(0)
|
|
59
|
+
expect(Object.keys(bundle.mcpServers)).toHaveLength(0)
|
|
60
|
+
expect(bundle.hookPlugins).toHaveLength(0)
|
|
61
|
+
expect(bundle.assets.some((asset: (typeof bundle.assets)[number]) => asset.pluginId === 'demo' && asset.enabled))
|
|
62
|
+
.toBe(false)
|
|
60
63
|
})
|
|
61
64
|
|
|
62
65
|
it('adds the built-in Vibe Forge MCP server when enabled and omits it when disabled', async () => {
|
|
@@ -82,131 +85,4 @@ describe('resolveWorkspaceAssetBundle', () => {
|
|
|
82
85
|
|
|
83
86
|
expect(disabledBundle.mcpServers).not.toHaveProperty('vibe-forge')
|
|
84
87
|
})
|
|
85
|
-
|
|
86
|
-
it('skips disabled plugin instances and lets disabled child overrides suppress default child activation', async () => {
|
|
87
|
-
const workspace = await createWorkspace()
|
|
88
|
-
|
|
89
|
-
await installPluginPackage(workspace, '@vibe-forge/plugin-demo', {
|
|
90
|
-
'package.json': JSON.stringify({
|
|
91
|
-
name: '@vibe-forge/plugin-demo',
|
|
92
|
-
version: '1.0.0'
|
|
93
|
-
}, null, 2),
|
|
94
|
-
'skills/research/SKILL.md': '---\ndescription: 检索资料\n---\n阅读 README.md'
|
|
95
|
-
})
|
|
96
|
-
await installPluginPackage(workspace, '@vibe-forge/plugin-bundle', {
|
|
97
|
-
'package.json': JSON.stringify({
|
|
98
|
-
name: '@vibe-forge/plugin-bundle',
|
|
99
|
-
version: '1.0.0'
|
|
100
|
-
}, null, 2),
|
|
101
|
-
'index.js': [
|
|
102
|
-
'module.exports = {',
|
|
103
|
-
' __vibeForgePluginManifest: true,',
|
|
104
|
-
' children: {',
|
|
105
|
-
' review: {',
|
|
106
|
-
' source: { type: "package", id: "@vibe-forge/plugin-review" },',
|
|
107
|
-
' activation: "default"',
|
|
108
|
-
' }',
|
|
109
|
-
' }',
|
|
110
|
-
'}',
|
|
111
|
-
''
|
|
112
|
-
].join('\n')
|
|
113
|
-
})
|
|
114
|
-
await installPluginPackage(workspace, '@vibe-forge/plugin-review', {
|
|
115
|
-
'package.json': JSON.stringify({
|
|
116
|
-
name: '@vibe-forge/plugin-review',
|
|
117
|
-
version: '1.0.0'
|
|
118
|
-
}, null, 2),
|
|
119
|
-
'skills/audit/SKILL.md': '---\ndescription: 代码审计\n---\n检查 child plugin 是否启用'
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
const bundle = await resolveWorkspaceAssetBundle({
|
|
123
|
-
cwd: workspace,
|
|
124
|
-
configs: [{
|
|
125
|
-
plugins: [
|
|
126
|
-
{ id: 'demo', scope: 'demo', enabled: false },
|
|
127
|
-
{
|
|
128
|
-
id: 'bundle',
|
|
129
|
-
scope: 'bundle',
|
|
130
|
-
children: [
|
|
131
|
-
{ id: 'review', enabled: false }
|
|
132
|
-
]
|
|
133
|
-
}
|
|
134
|
-
]
|
|
135
|
-
}, undefined],
|
|
136
|
-
useDefaultVibeForgeMcpServer: false
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
expect(bundle.skills).toEqual([])
|
|
140
|
-
expect(bundle.pluginInstances.map(instance => instance.packageId)).toEqual(['@vibe-forge/plugin-bundle'])
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
it('lets later config layers disable matching plugin instances by id and scope', async () => {
|
|
144
|
-
const workspace = await createWorkspace()
|
|
145
|
-
|
|
146
|
-
await installPluginPackage(workspace, '@vibe-forge/plugin-logger', {
|
|
147
|
-
'package.json': JSON.stringify({
|
|
148
|
-
name: '@vibe-forge/plugin-logger',
|
|
149
|
-
version: '1.0.0'
|
|
150
|
-
}, null, 2),
|
|
151
|
-
'hooks.js': 'module.exports = {}\n'
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
const bundle = await resolveWorkspaceAssetBundle({
|
|
155
|
-
cwd: workspace,
|
|
156
|
-
configs: [
|
|
157
|
-
{
|
|
158
|
-
plugins: [
|
|
159
|
-
{ id: 'logger' }
|
|
160
|
-
]
|
|
161
|
-
},
|
|
162
|
-
{
|
|
163
|
-
plugins: [
|
|
164
|
-
{ id: 'logger', enabled: false }
|
|
165
|
-
]
|
|
166
|
-
}
|
|
167
|
-
],
|
|
168
|
-
useDefaultVibeForgeMcpServer: false
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
expect(bundle.pluginConfigs).toEqual([
|
|
172
|
-
{ id: 'logger', enabled: false }
|
|
173
|
-
])
|
|
174
|
-
expect(bundle.pluginInstances).toEqual([])
|
|
175
|
-
expect(bundle.hookPlugins).toEqual([])
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
it('surfaces invalid plugin manifests instead of silently falling back to directory scanning', async () => {
|
|
179
|
-
const workspace = await createWorkspace()
|
|
180
|
-
|
|
181
|
-
await installPluginPackage(workspace, '@vibe-forge/plugin-bad-manifest', {
|
|
182
|
-
'package.json': JSON.stringify({
|
|
183
|
-
name: '@vibe-forge/plugin-bad-manifest',
|
|
184
|
-
version: '1.0.0',
|
|
185
|
-
exports: {
|
|
186
|
-
'.': './index.js'
|
|
187
|
-
}
|
|
188
|
-
}, null, 2),
|
|
189
|
-
'index.js': [
|
|
190
|
-
'module.exports = {',
|
|
191
|
-
' __vibeForgePluginManifest: true,',
|
|
192
|
-
' scope: "bad",',
|
|
193
|
-
' assets: {',
|
|
194
|
-
' skills: "./custom-skills"',
|
|
195
|
-
' }',
|
|
196
|
-
'}',
|
|
197
|
-
''
|
|
198
|
-
].join('\n'),
|
|
199
|
-
'custom-skills/research/SKILL.md': '---\ndescription: 检索资料\n---\n阅读 README.md'
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
await expect(resolveWorkspaceAssetBundle({
|
|
203
|
-
cwd: workspace,
|
|
204
|
-
configs: [{
|
|
205
|
-
plugins: [
|
|
206
|
-
{ id: '@vibe-forge/plugin-bad-manifest' }
|
|
207
|
-
]
|
|
208
|
-
}, undefined],
|
|
209
|
-
useDefaultVibeForgeMcpServer: false
|
|
210
|
-
})).rejects.toThrow('Failed to load plugin manifest for @vibe-forge/plugin-bad-manifest')
|
|
211
|
-
})
|
|
212
88
|
})
|