@vibe-forge/workspace-assets 0.9.1-alpha.0 → 0.9.2-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/AGENTS.md +4 -1
- package/LICENSE +21 -0
- package/__tests__/__snapshots__/workspace-assets-rich.snapshot.json +360 -355
- package/__tests__/adapter-asset-plan.spec.ts +76 -38
- package/__tests__/bundle.spec.ts +195 -43
- package/__tests__/prompt-builders.spec.ts +206 -0
- package/__tests__/prompt-selection.spec.ts +362 -14
- package/__tests__/selection-internal.spec.ts +42 -0
- package/__tests__/snapshot.ts +78 -128
- package/__tests__/test-helpers.ts +13 -0
- package/__tests__/workspace-assets.snapshot.spec.ts +84 -103
- package/package.json +11 -10
- package/src/adapter-asset-plan.ts +92 -111
- package/src/bundle-internal.ts +548 -0
- package/src/bundle.ts +17 -166
- package/src/internal-types.ts +1 -39
- package/src/prompt-builders.ts +184 -0
- package/src/prompt-selection.ts +144 -104
- package/src/selection-internal.ts +275 -0
- package/src/document-assets.ts +0 -191
- package/src/helpers.ts +0 -35
- package/src/plugin-assets.ts +0 -175
package/src/bundle.ts
CHANGED
|
@@ -1,178 +1,29 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { Config, PluginConfig, WorkspaceAssetBundle } from '@vibe-forge/types'
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
DEFAULT_VIBE_FORGE_MCP_SERVER_NAME,
|
|
5
|
-
buildConfigJsonVariables,
|
|
6
|
-
loadConfig,
|
|
7
|
-
resolveDefaultVibeForgeMcpServerConfig
|
|
8
|
-
} from '@vibe-forge/config'
|
|
9
|
-
import { DefinitionLoader } from '@vibe-forge/definition-loader'
|
|
10
|
-
import type { Config, WorkspaceAsset, WorkspaceAssetBundle } from '@vibe-forge/types'
|
|
11
|
-
import { resolveDocumentName, resolveSpecIdentifier } from '@vibe-forge/utils'
|
|
12
|
-
|
|
13
|
-
import {
|
|
14
|
-
createDocumentAsset,
|
|
15
|
-
dedupeDocumentAssets,
|
|
16
|
-
dedupeDocumentAssetsByIdentifier,
|
|
17
|
-
resolveRuleIdentifier,
|
|
18
|
-
resolveSkillIdentifier
|
|
19
|
-
} from './document-assets'
|
|
20
|
-
import { mergeRecord, uniqueValues } from './helpers'
|
|
21
|
-
import {
|
|
22
|
-
createClaudeNativePluginAssets,
|
|
23
|
-
createHookPluginAssets,
|
|
24
|
-
loadOpenCodeOverlayAssets,
|
|
25
|
-
loadPluginMcpAssets
|
|
26
|
-
} from './plugin-assets'
|
|
27
|
-
|
|
28
|
-
const readConfigForWorkspace = async (cwd: string) => {
|
|
29
|
-
return loadConfig({
|
|
30
|
-
cwd,
|
|
31
|
-
jsonVariables: buildConfigJsonVariables(cwd, process.env)
|
|
32
|
-
})
|
|
33
|
-
}
|
|
3
|
+
import { collectWorkspaceAssets } from './bundle-internal'
|
|
34
4
|
|
|
35
5
|
export async function resolveWorkspaceAssetBundle(params: {
|
|
36
6
|
cwd: string
|
|
37
7
|
configs?: [Config?, Config?]
|
|
8
|
+
plugins?: PluginConfig
|
|
9
|
+
overlaySource?: string
|
|
38
10
|
useDefaultVibeForgeMcpServer?: boolean
|
|
39
11
|
}): Promise<WorkspaceAssetBundle> {
|
|
40
|
-
const
|
|
41
|
-
const enabledPlugins = mergeRecord(config?.enabledPlugins, userConfig?.enabledPlugins)
|
|
42
|
-
const extraKnownMarketplaces = mergeRecord(config?.extraKnownMarketplaces, userConfig?.extraKnownMarketplaces)
|
|
43
|
-
const loader = new DefinitionLoader(params.cwd)
|
|
44
|
-
|
|
45
|
-
const [
|
|
46
|
-
rawRules,
|
|
47
|
-
rawSpecs,
|
|
48
|
-
rawEntities,
|
|
49
|
-
rawSkills,
|
|
50
|
-
pluginMcpAssets,
|
|
51
|
-
openCodeOverlayAssets
|
|
52
|
-
] = await Promise.all([
|
|
53
|
-
loader.loadDefaultRules(),
|
|
54
|
-
loader.loadDefaultSpecs(),
|
|
55
|
-
loader.loadDefaultEntities(),
|
|
56
|
-
loader.loadDefaultSkills(),
|
|
57
|
-
loadPluginMcpAssets(params.cwd, enabledPlugins),
|
|
58
|
-
loadOpenCodeOverlayAssets(params.cwd, enabledPlugins)
|
|
59
|
-
])
|
|
60
|
-
|
|
61
|
-
const assets: WorkspaceAsset[] = []
|
|
62
|
-
|
|
63
|
-
const rules = dedupeDocumentAssetsByIdentifier(
|
|
64
|
-
dedupeDocumentAssets(
|
|
65
|
-
rawRules.map((definition) => createDocumentAsset({ cwd: params.cwd, kind: 'rule', definition })),
|
|
66
|
-
enabledPlugins
|
|
67
|
-
),
|
|
68
|
-
asset => resolveRuleIdentifier(asset.payload.definition.path, asset.payload.definition.attributes.name)
|
|
69
|
-
)
|
|
70
|
-
const specs = dedupeDocumentAssetsByIdentifier(
|
|
71
|
-
dedupeDocumentAssets(
|
|
72
|
-
rawSpecs.map((definition) => createDocumentAsset({ cwd: params.cwd, kind: 'spec', definition })),
|
|
73
|
-
enabledPlugins
|
|
74
|
-
),
|
|
75
|
-
asset => resolveSpecIdentifier(asset.payload.definition.path, asset.payload.definition.attributes.name)
|
|
76
|
-
)
|
|
77
|
-
const entities = dedupeDocumentAssetsByIdentifier(
|
|
78
|
-
dedupeDocumentAssets(
|
|
79
|
-
rawEntities.map((definition) => createDocumentAsset({ cwd: params.cwd, kind: 'entity', definition })),
|
|
80
|
-
enabledPlugins
|
|
81
|
-
),
|
|
82
|
-
asset =>
|
|
83
|
-
resolveDocumentName(
|
|
84
|
-
asset.payload.definition.path,
|
|
85
|
-
asset.payload.definition.attributes.name,
|
|
86
|
-
['readme.md', 'index.json']
|
|
87
|
-
)
|
|
88
|
-
)
|
|
89
|
-
const skills = dedupeDocumentAssetsByIdentifier(
|
|
90
|
-
dedupeDocumentAssets(
|
|
91
|
-
rawSkills.map((definition) => createDocumentAsset({ cwd: params.cwd, kind: 'skill', definition })),
|
|
92
|
-
enabledPlugins
|
|
93
|
-
),
|
|
94
|
-
asset => resolveSkillIdentifier(asset.payload.definition.path, asset.payload.definition.attributes.name)
|
|
95
|
-
)
|
|
96
|
-
|
|
97
|
-
assets.push(...rules, ...specs, ...entities, ...skills)
|
|
98
|
-
|
|
99
|
-
const mcpServers = new Map<string, Extract<WorkspaceAsset, { kind: 'mcpServer' }>>()
|
|
100
|
-
if (params.useDefaultVibeForgeMcpServer !== false) {
|
|
101
|
-
const defaultVibeForgeMcpServer = resolveDefaultVibeForgeMcpServerConfig()
|
|
102
|
-
if (defaultVibeForgeMcpServer != null) {
|
|
103
|
-
mcpServers.set(DEFAULT_VIBE_FORGE_MCP_SERVER_NAME, {
|
|
104
|
-
id: `mcpServer:fallback:${DEFAULT_VIBE_FORGE_MCP_SERVER_NAME}`,
|
|
105
|
-
kind: 'mcpServer',
|
|
106
|
-
origin: 'fallback',
|
|
107
|
-
scope: 'adapter',
|
|
108
|
-
enabled: true,
|
|
109
|
-
targets: ['claude-code', 'codex', 'opencode'],
|
|
110
|
-
payload: {
|
|
111
|
-
name: DEFAULT_VIBE_FORGE_MCP_SERVER_NAME,
|
|
112
|
-
config: defaultVibeForgeMcpServer
|
|
113
|
-
}
|
|
114
|
-
})
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
const userMcpServers = userConfig?.mcpServers ?? {}
|
|
118
|
-
for (const [name, serverConfig] of Object.entries(userMcpServers)) {
|
|
119
|
-
mcpServers.set(name, {
|
|
120
|
-
id: `mcpServer:user:${name}`,
|
|
121
|
-
kind: 'mcpServer',
|
|
122
|
-
origin: 'config',
|
|
123
|
-
scope: 'user',
|
|
124
|
-
enabled: true,
|
|
125
|
-
targets: ['claude-code', 'codex', 'opencode'],
|
|
126
|
-
payload: {
|
|
127
|
-
name,
|
|
128
|
-
config: serverConfig
|
|
129
|
-
}
|
|
130
|
-
})
|
|
131
|
-
}
|
|
132
|
-
for (const asset of pluginMcpAssets) {
|
|
133
|
-
mcpServers.set(asset.payload.name, asset)
|
|
134
|
-
}
|
|
135
|
-
for (const [name, serverConfig] of Object.entries(config?.mcpServers ?? {})) {
|
|
136
|
-
mcpServers.set(name, {
|
|
137
|
-
id: `mcpServer:project:${name}`,
|
|
138
|
-
kind: 'mcpServer',
|
|
139
|
-
origin: 'config',
|
|
140
|
-
scope: 'project',
|
|
141
|
-
enabled: true,
|
|
142
|
-
targets: ['claude-code', 'codex', 'opencode'],
|
|
143
|
-
payload: {
|
|
144
|
-
name,
|
|
145
|
-
config: serverConfig
|
|
146
|
-
}
|
|
147
|
-
})
|
|
148
|
-
}
|
|
149
|
-
assets.push(...mcpServers.values())
|
|
150
|
-
|
|
151
|
-
const hookPlugins = [
|
|
152
|
-
...createHookPluginAssets(userConfig?.plugins, enabledPlugins, 'user'),
|
|
153
|
-
...createHookPluginAssets(config?.plugins, enabledPlugins, 'project')
|
|
154
|
-
]
|
|
155
|
-
const claudeNativePlugins = createClaudeNativePluginAssets(enabledPlugins)
|
|
156
|
-
assets.push(...hookPlugins, ...claudeNativePlugins, ...openCodeOverlayAssets)
|
|
12
|
+
const collected = await collectWorkspaceAssets(params)
|
|
157
13
|
|
|
158
14
|
return {
|
|
159
15
|
cwd: params.cwd,
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
]),
|
|
173
|
-
defaultExcludeMcpServers: uniqueValues([
|
|
174
|
-
...(config?.defaultExcludeMcpServers ?? []),
|
|
175
|
-
...(userConfig?.defaultExcludeMcpServers ?? [])
|
|
176
|
-
])
|
|
16
|
+
pluginConfigs: collected.pluginConfigs,
|
|
17
|
+
pluginInstances: collected.pluginInstances,
|
|
18
|
+
assets: collected.assets,
|
|
19
|
+
rules: collected.rules,
|
|
20
|
+
specs: collected.specs,
|
|
21
|
+
entities: collected.entities,
|
|
22
|
+
skills: collected.skills,
|
|
23
|
+
mcpServers: collected.mcpServers,
|
|
24
|
+
hookPlugins: collected.hookPlugins,
|
|
25
|
+
opencodeOverlayAssets: collected.opencodeOverlayAssets,
|
|
26
|
+
defaultIncludeMcpServers: collected.defaultIncludeMcpServers,
|
|
27
|
+
defaultExcludeMcpServers: collected.defaultExcludeMcpServers
|
|
177
28
|
}
|
|
178
29
|
}
|
package/src/internal-types.ts
CHANGED
|
@@ -1,41 +1,3 @@
|
|
|
1
1
|
import type { WorkspaceAsset } from '@vibe-forge/types'
|
|
2
2
|
|
|
3
|
-
export
|
|
4
|
-
definition: TDefinition
|
|
5
|
-
sourcePath: string
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export interface WorkspaceOverlayPayload {
|
|
9
|
-
sourcePath: string
|
|
10
|
-
entryName: string
|
|
11
|
-
targetSubpath: string
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export type WorkspaceOpenCodeOverlayAsset =
|
|
15
|
-
| (
|
|
16
|
-
& Extract<WorkspaceAsset, { kind: 'nativePlugin' }>
|
|
17
|
-
& { payload: WorkspaceOverlayPayload }
|
|
18
|
-
)
|
|
19
|
-
| Extract<WorkspaceAsset, { kind: 'agent' | 'command' | 'mode' }>
|
|
20
|
-
|
|
21
|
-
export type WorkspaceDocumentAsset<TDefinition> =
|
|
22
|
-
& Extract<
|
|
23
|
-
WorkspaceAsset,
|
|
24
|
-
{ kind: 'rule' | 'spec' | 'entity' | 'skill' }
|
|
25
|
-
>
|
|
26
|
-
& {
|
|
27
|
-
payload: WorkspaceDocumentPayload<TDefinition & { path: string }>
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export const isOverlayPayload = (payload: unknown): payload is WorkspaceOverlayPayload => (
|
|
31
|
-
payload != null &&
|
|
32
|
-
typeof payload === 'object' &&
|
|
33
|
-
typeof (payload as WorkspaceOverlayPayload).sourcePath === 'string' &&
|
|
34
|
-
typeof (payload as WorkspaceOverlayPayload).entryName === 'string' &&
|
|
35
|
-
typeof (payload as WorkspaceOverlayPayload).targetSubpath === 'string'
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
export const isOpenCodeOverlayAsset = (asset: WorkspaceAsset): asset is WorkspaceOpenCodeOverlayAsset => (
|
|
39
|
-
(asset.kind === 'nativePlugin' || asset.kind === 'agent' || asset.kind === 'command' || asset.kind === 'mode') &&
|
|
40
|
-
isOverlayPayload(asset.payload)
|
|
41
|
-
)
|
|
3
|
+
export const isOpenCodeOverlayAsset = (asset: WorkspaceAsset) => asset.kind === 'nativePlugin'
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isAlwaysRule,
|
|
3
|
+
resolveDefinitionName,
|
|
4
|
+
resolveDocumentDescription,
|
|
5
|
+
resolveSpecIdentifier
|
|
6
|
+
} from '@vibe-forge/definition-core'
|
|
7
|
+
import type { Definition, Entity, Rule, Skill, Spec } from '@vibe-forge/types'
|
|
8
|
+
import { resolvePromptPath } from '@vibe-forge/utils'
|
|
9
|
+
|
|
10
|
+
const toMarkdownBlockquote = (content: string) => (
|
|
11
|
+
content
|
|
12
|
+
.trim()
|
|
13
|
+
.split('\n')
|
|
14
|
+
.map(line => line === '' ? '>' : `> ${line}`)
|
|
15
|
+
.join('\n')
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
const buildOptionalRuleGuidance = (cwd: string, rule: Definition<Rule>) => {
|
|
19
|
+
const name = resolveDefinitionName(rule)
|
|
20
|
+
const desc = resolveDocumentDescription(rule.body, rule.attributes.description, name)
|
|
21
|
+
return [
|
|
22
|
+
`Use when: ${desc}`,
|
|
23
|
+
`Rule file path: ${resolvePromptPath(cwd, rule.path)}`,
|
|
24
|
+
'Only read this rule file when the task matches the scenario above.'
|
|
25
|
+
].join('\n')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const buildSkillSummary = (
|
|
29
|
+
cwd: string,
|
|
30
|
+
skill: Definition<Skill>,
|
|
31
|
+
guidance: string
|
|
32
|
+
) => {
|
|
33
|
+
const name = resolveDefinitionName(skill, ['skill.md'])
|
|
34
|
+
const desc = resolveDocumentDescription(skill.body, skill.attributes.description, name)
|
|
35
|
+
return toMarkdownBlockquote(
|
|
36
|
+
[
|
|
37
|
+
`Skill description: ${desc}`,
|
|
38
|
+
`Skill file path: ${resolvePromptPath(cwd, skill.path)}`,
|
|
39
|
+
guidance
|
|
40
|
+
].join('\n')
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const renderSkillModule = (
|
|
45
|
+
cwd: string,
|
|
46
|
+
skill: Definition<Skill>,
|
|
47
|
+
options: {
|
|
48
|
+
guidance: string
|
|
49
|
+
includeContent?: boolean
|
|
50
|
+
}
|
|
51
|
+
) => {
|
|
52
|
+
const parts = [
|
|
53
|
+
`# ${resolveDefinitionName(skill, ['skill.md'])}`,
|
|
54
|
+
'',
|
|
55
|
+
buildSkillSummary(cwd, skill, options.guidance)
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
if (options.includeContent) {
|
|
59
|
+
parts.push('', '<skill-content>', skill.body.trim(), '</skill-content>')
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return parts.join('\n')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const generateRulesPrompt = (cwd: string, rules: Definition<Rule>[]) => {
|
|
66
|
+
const rulesPrompt = rules
|
|
67
|
+
.map((rule) => {
|
|
68
|
+
const content = isAlwaysRule(rule.attributes) && rule.body.trim()
|
|
69
|
+
? rule.body.trim()
|
|
70
|
+
: buildOptionalRuleGuidance(cwd, rule)
|
|
71
|
+
return `# ${resolveDefinitionName(rule)}\n\n${toMarkdownBlockquote(content)}`
|
|
72
|
+
})
|
|
73
|
+
.filter(Boolean)
|
|
74
|
+
.join('\n\n')
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
'<system-prompt>\n' +
|
|
78
|
+
'The project system rules are:\n' +
|
|
79
|
+
`${rulesPrompt}\n` +
|
|
80
|
+
'</system-prompt>\n'
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const generateSkillsPrompt = (cwd: string, skills: Definition<Skill>[]) => {
|
|
85
|
+
const modules = skills
|
|
86
|
+
.map(skill =>
|
|
87
|
+
renderSkillModule(cwd, skill, {
|
|
88
|
+
guidance:
|
|
89
|
+
'Resolve relative paths in the resource content relative to the directory containing this skill file.',
|
|
90
|
+
includeContent: true
|
|
91
|
+
})
|
|
92
|
+
)
|
|
93
|
+
.filter(Boolean)
|
|
94
|
+
.join('\n\n')
|
|
95
|
+
|
|
96
|
+
if (modules === '') return ''
|
|
97
|
+
|
|
98
|
+
return `<system-prompt>\nThe following skill modules are loaded for the project:\n${modules}\n</system-prompt>\n`
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export const generateSkillsRoutePrompt = (cwd: string, skills: Definition<Skill>[]) => {
|
|
102
|
+
const modules = skills
|
|
103
|
+
.filter(({ attributes: { always } }) => always !== false)
|
|
104
|
+
.map(skill =>
|
|
105
|
+
renderSkillModule(cwd, skill, {
|
|
106
|
+
guidance:
|
|
107
|
+
'Do not preload the body by default; read the corresponding skill file only when the task clearly requires it.'
|
|
108
|
+
})
|
|
109
|
+
)
|
|
110
|
+
.filter(Boolean)
|
|
111
|
+
.join('\n\n')
|
|
112
|
+
|
|
113
|
+
if (modules === '') return ''
|
|
114
|
+
|
|
115
|
+
return `<skills>\n${modules}\n</skills>\n`
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export const generateSpecRoutePrompt = (
|
|
119
|
+
specsDocuments: Definition<Spec>[],
|
|
120
|
+
options?: { active?: boolean }
|
|
121
|
+
) => {
|
|
122
|
+
const specsRouteStr = specsDocuments
|
|
123
|
+
.filter(({ attributes }) => attributes.always !== false)
|
|
124
|
+
.map((definition) => {
|
|
125
|
+
const name = resolveDefinitionName(definition, ['index.md'])
|
|
126
|
+
const desc = resolveDocumentDescription(definition.body, definition.attributes.description, name)
|
|
127
|
+
const identifier = definition.resolvedName?.trim() ||
|
|
128
|
+
resolveSpecIdentifier(definition.path, definition.attributes.name)
|
|
129
|
+
const params = definition.attributes.params ?? []
|
|
130
|
+
const paramsPrompt = params.length > 0
|
|
131
|
+
? params
|
|
132
|
+
.map(({ name, description }) => ` - ${name}: ${description ?? 'None'}\n`)
|
|
133
|
+
.join('')
|
|
134
|
+
: ' - None\n'
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
`- Workflow name: ${name}\n` +
|
|
138
|
+
` - Description: ${desc}\n` +
|
|
139
|
+
` - Identifier: ${identifier}\n` +
|
|
140
|
+
' - Parameters:\n' +
|
|
141
|
+
`${paramsPrompt}`
|
|
142
|
+
)
|
|
143
|
+
})
|
|
144
|
+
.join('\n')
|
|
145
|
+
|
|
146
|
+
const activeIdentityPrompt = options?.active
|
|
147
|
+
? (
|
|
148
|
+
'You are a professional project execution manager who can skillfully direct other entities to work toward your goal. Expectations:\n' +
|
|
149
|
+
'\n' +
|
|
150
|
+
'- Never complete code development work alone\n' +
|
|
151
|
+
'- You must coordinate other developers to complete tasks\n' +
|
|
152
|
+
'- You must keep them aligned with the goal and verify that their completion reports meet the requirements\n' +
|
|
153
|
+
'\n'
|
|
154
|
+
)
|
|
155
|
+
: ''
|
|
156
|
+
|
|
157
|
+
return (
|
|
158
|
+
`<system-prompt>\n${activeIdentityPrompt}Choose the appropriate workflow based on the user's needs and the actual development goal, and use the workflow identifier to locate and load the corresponding definition.\n` +
|
|
159
|
+
'- Pass the identifier based on the actual need. This is not a path; use the standard workflow loading capability to resolve it.\n' +
|
|
160
|
+
'- Decide how to pass parameters based on their descriptions and actual usage scenarios.\n' +
|
|
161
|
+
'The project includes the following workflows:\n' +
|
|
162
|
+
`${specsRouteStr}\n` +
|
|
163
|
+
'</system-prompt>\n'
|
|
164
|
+
)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export const generateEntitiesRoutePrompt = (entities: Definition<Entity>[]) => {
|
|
168
|
+
return (
|
|
169
|
+
'<system-prompt>\n' +
|
|
170
|
+
'The project includes the following entities:\n' +
|
|
171
|
+
`${
|
|
172
|
+
entities
|
|
173
|
+
.filter(({ attributes }) => attributes.always !== false)
|
|
174
|
+
.map((definition) => {
|
|
175
|
+
const name = resolveDefinitionName(definition, ['readme.md', 'index.json'])
|
|
176
|
+
const desc = resolveDocumentDescription(definition.body, definition.attributes.description, name)
|
|
177
|
+
return ` - ${name}: ${desc}\n`
|
|
178
|
+
})
|
|
179
|
+
.join('')
|
|
180
|
+
}\n` +
|
|
181
|
+
'When solving user problems, you may specify entities through `vibe-forge.StartTasks` as needed and have them coordinate multiple entity types to complete the work; use `GetTaskInfo` and `wait` to track progress.\n' +
|
|
182
|
+
'</system-prompt>\n'
|
|
183
|
+
)
|
|
184
|
+
}
|