@vibe-forge/workspace-assets 0.10.0 → 0.10.1-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.
|
@@ -241,6 +241,90 @@ describe('resolvePromptAssetSelection', () => {
|
|
|
241
241
|
expect(options.systemPrompt).not.toContain('先读 README.md')
|
|
242
242
|
})
|
|
243
243
|
|
|
244
|
+
it('omits route-only project skills when the adapter provides native skill loading', async () => {
|
|
245
|
+
const workspace = await createWorkspace()
|
|
246
|
+
|
|
247
|
+
await writeDocument(
|
|
248
|
+
join(workspace, '.ai/skills/research/SKILL.md'),
|
|
249
|
+
[
|
|
250
|
+
'---',
|
|
251
|
+
'description: 检索项目信息',
|
|
252
|
+
'---',
|
|
253
|
+
'先读 README.md'
|
|
254
|
+
].join('\n')
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
const bundle = await resolveWorkspaceAssetBundle({
|
|
258
|
+
cwd: workspace,
|
|
259
|
+
useDefaultVibeForgeMcpServer: false
|
|
260
|
+
})
|
|
261
|
+
const [data, options] = await resolvePromptAssetSelection({
|
|
262
|
+
bundle,
|
|
263
|
+
adapter: 'claude-code',
|
|
264
|
+
type: undefined
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
expect(data.targetSkills).toEqual([])
|
|
268
|
+
expect(options.systemPrompt).not.toContain('<skills>')
|
|
269
|
+
expect(options.systemPrompt).not.toContain('# research')
|
|
270
|
+
expect(options.systemPrompt).not.toContain('Skill file path: .ai/skills/research/SKILL.md')
|
|
271
|
+
expect(options.promptAssetIds).not.toContain(bundle.skills[0]?.id)
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
it('keeps explicitly referenced skills embedded for adapters with native project skills', async () => {
|
|
275
|
+
const workspace = await createWorkspace()
|
|
276
|
+
|
|
277
|
+
await writeDocument(
|
|
278
|
+
join(workspace, '.ai/skills/research/SKILL.md'),
|
|
279
|
+
[
|
|
280
|
+
'---',
|
|
281
|
+
'description: 检索项目信息',
|
|
282
|
+
'---',
|
|
283
|
+
'先读 README.md'
|
|
284
|
+
].join('\n')
|
|
285
|
+
)
|
|
286
|
+
await writeDocument(
|
|
287
|
+
join(workspace, '.ai/skills/review/SKILL.md'),
|
|
288
|
+
[
|
|
289
|
+
'---',
|
|
290
|
+
'description: 评审代码改动',
|
|
291
|
+
'---',
|
|
292
|
+
'检查回归风险'
|
|
293
|
+
].join('\n')
|
|
294
|
+
)
|
|
295
|
+
await writeDocument(
|
|
296
|
+
join(workspace, '.ai/specs/release/index.md'),
|
|
297
|
+
[
|
|
298
|
+
'---',
|
|
299
|
+
'description: 发布流程',
|
|
300
|
+
'skills:',
|
|
301
|
+
' - research',
|
|
302
|
+
'---',
|
|
303
|
+
'执行发布'
|
|
304
|
+
].join('\n')
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
const bundle = await resolveWorkspaceAssetBundle({
|
|
308
|
+
cwd: workspace,
|
|
309
|
+
useDefaultVibeForgeMcpServer: false
|
|
310
|
+
})
|
|
311
|
+
const [data, options] = await resolvePromptAssetSelection({
|
|
312
|
+
bundle,
|
|
313
|
+
adapter: 'claude-code',
|
|
314
|
+
type: 'spec',
|
|
315
|
+
name: 'release'
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
expect(data.targetSkills.map(skill => skill.resolvedName ?? skill.attributes.name)).toEqual(['research'])
|
|
319
|
+
expect(options.systemPrompt).toContain('The following skill modules are loaded for the project')
|
|
320
|
+
expect(options.systemPrompt).toContain('# research')
|
|
321
|
+
expect(options.systemPrompt).toContain('<skill-content>')
|
|
322
|
+
expect(options.systemPrompt).toContain('先读 README.md')
|
|
323
|
+
expect(options.systemPrompt).not.toContain('<skills>')
|
|
324
|
+
expect(options.systemPrompt).not.toContain('# review')
|
|
325
|
+
expect(options.systemPrompt).not.toContain('Skill file path: .ai/skills/review/SKILL.md')
|
|
326
|
+
})
|
|
327
|
+
|
|
244
328
|
it('keeps spec route guidance without default identity in normal mode', async () => {
|
|
245
329
|
const workspace = await createWorkspace()
|
|
246
330
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibe-forge/workspace-assets",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.1-alpha.1",
|
|
4
4
|
"description": "Workspace asset resolution and adapter asset planning for Vibe Forge",
|
|
5
5
|
"imports": {
|
|
6
6
|
"#~/*.js": {
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
"fast-glob": "^3.3.3",
|
|
30
30
|
"front-matter": "^4.0.2",
|
|
31
31
|
"js-yaml": "^4.1.1",
|
|
32
|
-
"@vibe-forge/definition-core": "^0.10.0",
|
|
33
32
|
"@vibe-forge/config": "^0.10.0",
|
|
34
|
-
"@vibe-forge/types": "^0.10.
|
|
35
|
-
"@vibe-forge/utils": "^0.10.
|
|
33
|
+
"@vibe-forge/types": "^0.10.1",
|
|
34
|
+
"@vibe-forge/utils": "^0.10.1",
|
|
35
|
+
"@vibe-forge/definition-core": "^0.10.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/js-yaml": "^4.0.9"
|
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
WorkspaceSkillSelection
|
|
11
11
|
} from '@vibe-forge/types'
|
|
12
12
|
|
|
13
|
+
import { resolveNativeSkillDiagnosticReason, supportsNativeProjectSkills } from './adapter-capabilities'
|
|
13
14
|
import { resolveSelectedMcpNames, resolveSelectedSkillAssets } from './selection-internal'
|
|
14
15
|
|
|
15
16
|
export function buildAdapterAssetPlan(params: {
|
|
@@ -83,13 +84,13 @@ export function buildAdapterAssetPlan(params: {
|
|
|
83
84
|
})
|
|
84
85
|
|
|
85
86
|
const selectedSkillAssets = resolveSelectedSkillAssets(params.bundle.skills, params.options.skills)
|
|
86
|
-
if (params.adapter
|
|
87
|
+
if (supportsNativeProjectSkills(params.adapter)) {
|
|
87
88
|
selectedSkillAssets.forEach((asset) => {
|
|
88
89
|
diagnostics.push({
|
|
89
90
|
assetId: asset.id,
|
|
90
91
|
adapter: params.adapter,
|
|
91
92
|
status: 'native',
|
|
92
|
-
reason:
|
|
93
|
+
reason: resolveNativeSkillDiagnosticReason(params.adapter),
|
|
93
94
|
packageId: asset.packageId,
|
|
94
95
|
scope: asset.scope,
|
|
95
96
|
instancePath: asset.instancePath,
|
|
@@ -98,6 +99,8 @@ export function buildAdapterAssetPlan(params: {
|
|
|
98
99
|
taskOverlaySource: asset.taskOverlaySource
|
|
99
100
|
})
|
|
100
101
|
})
|
|
102
|
+
}
|
|
103
|
+
if (params.adapter === 'opencode') {
|
|
101
104
|
params.bundle.opencodeOverlayAssets.forEach((asset) => {
|
|
102
105
|
diagnostics.push({
|
|
103
106
|
assetId: asset.id,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { WorkspaceAssetAdapter } from '@vibe-forge/types'
|
|
2
|
+
|
|
3
|
+
const NATIVE_SKILL_ADAPTERS = new Set<WorkspaceAssetAdapter>(['claude-code', 'opencode'])
|
|
4
|
+
|
|
5
|
+
export const supportsNativeProjectSkills = (adapter?: string): adapter is WorkspaceAssetAdapter =>
|
|
6
|
+
adapter != null && NATIVE_SKILL_ADAPTERS.has(adapter as WorkspaceAssetAdapter)
|
|
7
|
+
|
|
8
|
+
export const resolveNativeSkillDiagnosticReason = (adapter: WorkspaceAssetAdapter) => (
|
|
9
|
+
adapter === 'claude-code'
|
|
10
|
+
? 'Synced into the Claude mock home as a native skill.'
|
|
11
|
+
: 'Mirrored into OPENCODE_CONFIG_DIR as a native skill.'
|
|
12
|
+
)
|
package/src/prompt-selection.ts
CHANGED
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
WorkspaceSkillSelection
|
|
12
12
|
} from '@vibe-forge/types'
|
|
13
13
|
|
|
14
|
+
import { supportsNativeProjectSkills } from './adapter-capabilities'
|
|
14
15
|
import { resolveWorkspaceAssetBundle } from './bundle'
|
|
15
16
|
import {
|
|
16
17
|
generateEntitiesRoutePrompt,
|
|
@@ -35,6 +36,7 @@ export async function resolvePromptAssetSelection(params: {
|
|
|
35
36
|
bundle: WorkspaceAssetBundle
|
|
36
37
|
type: 'spec' | 'entity' | undefined
|
|
37
38
|
name?: string
|
|
39
|
+
adapter?: string
|
|
38
40
|
input?: {
|
|
39
41
|
skills?: WorkspaceSkillSelection
|
|
40
42
|
}
|
|
@@ -83,10 +85,11 @@ export async function resolvePromptAssetSelection(params: {
|
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
const selectedSkillAssets = resolveSelectedSkillAssets(effectiveBundle.skills, params.input?.skills)
|
|
88
|
+
const useNativeProjectSkills = supportsNativeProjectSkills(params.adapter)
|
|
86
89
|
const promptAssetIds = new Set<string>([
|
|
87
90
|
...effectiveBundle.rules.map(asset => asset.id),
|
|
88
91
|
...effectiveBundle.specs.map(asset => asset.id),
|
|
89
|
-
...selectedSkillAssets.map(asset => asset.id),
|
|
92
|
+
...(useNativeProjectSkills ? [] : selectedSkillAssets.map(asset => asset.id)),
|
|
90
93
|
...(params.type !== 'entity' ? effectiveBundle.entities.map(asset => asset.id) : [])
|
|
91
94
|
])
|
|
92
95
|
const ruleDefinitions = new Map<string, Definition<Rule>>(
|
|
@@ -164,10 +167,12 @@ export async function resolvePromptAssetSelection(params: {
|
|
|
164
167
|
generateRulesPrompt(effectiveBundle.cwd, rules),
|
|
165
168
|
generateSkillsPrompt(effectiveBundle.cwd, targetSkills),
|
|
166
169
|
generateEntitiesRoutePrompt(entities),
|
|
167
|
-
generateSkillsRoutePrompt(effectiveBundle.cwd, routedSkills),
|
|
170
|
+
useNativeProjectSkills ? '' : generateSkillsRoutePrompt(effectiveBundle.cwd, routedSkills),
|
|
168
171
|
generateSpecRoutePrompt(specs, { active: params.type === 'spec' }),
|
|
169
172
|
targetBody
|
|
170
|
-
]
|
|
173
|
+
]
|
|
174
|
+
.filter(section => section !== '')
|
|
175
|
+
.join('\n\n')
|
|
171
176
|
|
|
172
177
|
if (targetToolsFilter != null) {
|
|
173
178
|
options.tools = targetToolsFilter
|