@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.
@@ -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, installPluginPackage, writeDocument } from './test-helpers'
7
+ import { createWorkspace, writeDocument } from './test-helpers'
8
8
 
9
9
  describe('buildAdapterAssetPlan', () => {
10
- it('builds codex diagnostics for prompt, mcp, hook plugins, and unsupported opencode assets', async () => {
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/skills/research/SKILL.md'),
29
- '---\ndescription: 检索资料\n---\n阅读 README.md'
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/review/SKILL.md'),
33
- '---\ndescription: 代码评审\n---\n检查风险'
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
- { id: 'logger' },
41
- { id: 'demo', scope: 'demo' }
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: loggerHookPluginId
91
+ assetId: 'hookPlugin:project:logger'
96
92
  }),
97
93
  expect.objectContaining({
98
94
  adapter: 'codex',
99
95
  status: 'translated',
100
- assetId: docsMcpId
96
+ assetId: 'mcpServer:project:docs'
101
97
  }),
102
98
  expect.objectContaining({
103
99
  adapter: 'codex',
104
100
  status: 'skipped',
105
- assetId: demoCommandId
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
  })
@@ -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, installPluginPackage } from './test-helpers'
8
+ import { createWorkspace, writeDocument } from './test-helpers'
8
9
 
9
10
  describe('resolveWorkspaceAssetBundle', () => {
10
- it('loads npm plugin assets via the package-id fallback and exposes OpenCode overlays', async () => {
11
+ it('treats enabledPlugins as a global asset switch', async () => {
11
12
  const workspace = await createWorkspace()
12
13
 
13
- await installPluginPackage(workspace, '@vibe-forge/plugin-demo', {
14
- 'package.json': JSON.stringify({
15
- name: '@vibe-forge/plugin-demo',
16
- version: '1.0.0'
17
- }, null, 2),
18
- 'skills/research/SKILL.md': '---\ndescription: 检索资料\n---\n阅读 README.md',
19
- 'rules/review.md': '---\ndescription: 评审规则\n---\n必须检查风险',
20
- 'mcp/browser.json': JSON.stringify({ command: 'npx', args: ['browser-server'] }, null, 2),
21
- 'opencode/commands/review.md': '# review\n'
22
- })
23
- await installPluginPackage(workspace, '@vibe-forge/plugin-logger', {
24
- 'package.json': JSON.stringify({
25
- name: '@vibe-forge/plugin-logger',
26
- version: '1.0.0'
27
- }, null, 2),
28
- 'hooks.js': 'module.exports = {}\n'
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
- { id: 'demo', scope: 'demo' },
36
- { id: 'logger' }
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.map(asset => asset.displayName)).toEqual(['demo/research'])
43
- expect(bundle.rules.map(asset => asset.displayName)).toEqual(['demo/review'])
44
- expect(Object.keys(bundle.mcpServers)).toEqual(['demo/browser'])
45
- expect(bundle.hookPlugins).toEqual(expect.arrayContaining([
46
- expect.objectContaining({
47
- packageId: '@vibe-forge/plugin-logger'
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
  })