@vibe-forge/workspace-assets 3.0.0 → 3.2.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.
@@ -467,7 +467,7 @@
467
467
  ]
468
468
  },
469
469
  "options": {
470
- "systemPrompt": "<system-prompt>\nThe project system rules are:\n# review\n\n> 必须检查发布改动的回归风险。\n\n# demo/security\n\n> Use when: 插件安全规则\n> Rule file path: node_modules/@vibe-forge/plugin-demo/rules/security.md\n> Only read this rule file when the task matches the scenario above.\n</system-prompt>\n\n\n<system-prompt>\nThe following skill modules are loaded for the project:\n# research\n\n> Skill description: 检索资料\n> Skill file path: .ai/skills/research/SKILL.md\n> Resolve relative paths in the resource content relative to the directory containing this skill file.\n\n<skill-content>\n先阅读 README.md,再补充结论。\n</skill-content>\n</system-prompt>\n\n\n<system-prompt>\nThe project includes the following entities:\n - architect: 负责拆解方案的实体\n\nWhen solving user problems, you may specify entities through `VibeForge.StartTasks` as needed and have them coordinate multiple entity types to complete the work; use `VibeForge.GetTaskInfo` and `wait` to track progress.\nTask tool guide:\n- Use `VibeForge.StartTasks` to start a new child task when the work should run in a separate entity or workspace, or when it needs to continue independently from the current turn.\n- After starting a task, use `VibeForge.GetTaskInfo` with `{ taskId }` to inspect one task. It is also the right tool when a task seems stalled, failed, or might be waiting for input.\n- By default, `GetTaskInfo` returns the 10 most recent log entries in descending order, so newer entries appear earlier in the `logs` array. Pass `logLimit` to inspect a different number of recent logs, and set `logOrder` to `\"asc\"` when you want the selected log window in oldest-to-newest order.\n- Use `VibeForge.ListTasks` with the same `logLimit` and `logOrder` fields when you need to find a taskId or inspect multiple tasks at once.\n- Use `VibeForge.SendTaskMessage` with `{ taskId, message }` when you need to give a task another instruction. Running tasks continue immediately; completed or failed tasks resume the same conversation instead of forcing a replacement task.\n- Use `VibeForge.SubmitTaskInput` only when `VibeForge.GetTaskInfo` or `VibeForge.ListTasks` shows `pendingInput` or `pendingInteraction`, or the task status is `waiting_input`. Do not use it for ordinary follow-up instructions.\n- If a task is `completed` or `failed` but you want to keep working in that same thread of execution, prefer `VibeForge.SendTaskMessage` to resume it. Start a new task only when you actually want a replacement task.\n- When a task is still making progress, use `wait` between checks instead of repeatedly restarting it.\n</system-prompt>\n\n\n<system-prompt>\nYou are a professional project execution manager who can skillfully direct other entities to work toward your goal. Expectations:\n\n- Never complete code development work alone\n- You must coordinate other developers to complete tasks\n- You must keep them aligned with the goal and verify that their completion reports meet the requirements\n\nChoose 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- Pass the identifier based on the actual need. This is not a path; use the standard workflow loading capability to resolve it.\n- Decide how to pass parameters based on their descriptions and actual usage scenarios.\nThe project includes the following workflows:\n- Workflow name: release\n - Description: 正式发布流程\n - Identifier: release\n - Parameters:\n - None\n\n- Workflow name: demo/release\n - Description: 插件发布流程\n - Identifier: demo/release\n - Parameters:\n - None\n\n</system-prompt>\n\n\n执行正式发布,并整理变更摘要。",
470
+ "systemPrompt": "<system-prompt>\nThe project system rules are:\n# review\n\n> 必须检查发布改动的回归风险。\n\n# demo/security\n\n> Use when: 插件安全规则\n> Rule file path: node_modules/@vibe-forge/plugin-demo/rules/security.md\n> Only read this rule file when the task matches the scenario above.\n</system-prompt>\n\n\n<system-prompt>\nThe following skill modules are loaded for the project:\n# research\n\n> Skill description: 检索资料\n> Skill file path: .ai/skills/research/SKILL.md\n> Resolve relative paths in the resource content relative to the directory containing this skill file.\n\n<skill-content>\n先阅读 README.md,再补充结论。\n</skill-content>\n</system-prompt>\n\n\n<system-prompt>\nThe project includes the following entities:\n - architect: 负责拆解方案的实体\n\nWhen solving user problems, you may start child runtime sessions with `vf run --input-format stream-json --output-format stream-json` and a `session.start` protocol envelope as needed; use `session.status`, `session.events`, and `wait` to track progress.\nAgent runtime guide:\n- Use unified CLI protocol mode, `vf run --input-format stream-json --output-format stream-json`, to start a child runtime session when the work should run in a separate entity or continue independently from the current turn.\n- Send typed runtime protocol envelopes such as `session.start`, `session.message`, `session.status`, `session.events`, `session.submit`, and `session.stop`; do not treat dedicated `vf agent ...` subcommands as the standard integration surface.\n- Ordinary new sessions stay session-scoped. A room is created or discovered only when a unified CLI runtime protocol start command launches a child runtime session from a server-managed host session and the server projects runtime store metadata/events.\n- Do not use MCP task tools, `vf agent ...`, legacy StartTasks, hand-written DB edits, or ad-hoc TS scripts as the task consumer surface. Use CLI protocol mode and the runtime protocol/store for start, status, events, follow-up messages, input submission, and cancellation.\n- Server-managed host sessions inject the current adapter, model, effort, and permission mode as runtime protocol defaults. Omit these fields to inherit the host selection, or set them explicitly only when a child task must use a different runtime profile.\n- Copyable JSONL example; write one `session.start` line per child task, and use multiple lines for multiple subtasks:\n```bash\ncat <<'JSONL' | vf run --input-format stream-json --output-format stream-json\n{\"commandId\":\"start-planner\",\"type\":\"session.start\",\"payload\":{\"title\":\"Plan Agent Room UI fix\",\"message\":\"Plan the frontend changes and tests for the Agent Room UI fix.\",\"entity\":\"dev-planner\",\"background\":true},\"title\":\"Plan Agent Room UI fix\",\"message\":\"Plan the frontend changes and tests for the Agent Room UI fix.\",\"entity\":\"dev-planner\",\"background\":true}\n{\"commandId\":\"start-reviewer\",\"type\":\"session.start\",\"payload\":{\"title\":\"Review Agent Room UI fix\",\"message\":\"Review the implemented Agent Room UI fix for regressions and missing tests.\",\"entity\":\"dev-reviewer\",\"background\":true},\"title\":\"Review Agent Room UI fix\",\"message\":\"Review the implemented Agent Room UI fix for regressions and missing tests.\",\"entity\":\"dev-reviewer\",\"background\":true}\nJSONL\n```\n- Keep `payload.title`, `payload.message`, `payload.entity`, and `payload.background: true` explicit in each start envelope. The mirrored top-level fields make the JSONL executable by the current `vf run` protocol reader.\n- Include a short `title` when the task prompt is long; it becomes the child session title and room run label. Put any room or workspace context in the title and initial message.\n- Read the returned `sessionId` and use it for follow-up protocol commands. Read the latest runtime snapshot from the runtime store or a `session.status` protocol command, and read progress from runtime events or a `session.events` protocol command.\n- Use a follow/read-events workflow when you need to watch progress instead of repeatedly restarting work.\n- Use a `session.message` protocol command to give an existing session another instruction. Running sessions continue immediately; completed or failed sessions resume the same conversation when the runtime allows resume.\n- When the chat UI sends a `[ROOM_TASK_MESSAGE] ... [/ROOM_TASK_MESSAGE]` block, treat it as a runtime relay envelope instead of ordinary prose. Parse the `sessionId` or legacy `taskId`, `message`, and optional `mode` / `request` fields. If the envelope indicates `mode: interaction`, or runtime status shows `waiting_input` / pending input, use a `session.submit` protocol command. Otherwise use `session.message`. Do not reply inline instead of routing the relay.\n- Use `session.submit` only when a runtime session is waiting for an explicit input or approval request. Do not use it for ordinary follow-up instructions.\n- Use a `session.stop` protocol command for graceful cancellation and set `mode` to `force` only when stop cannot recover the session.\n- Compatibility aliases such as `vf agent start/status/events/send/submit/stop` may exist for debugging or legacy scripts, but they are not the primary guidance for new agent workflows.\n- When a session is still making progress, use `wait` between checks and inspect status/events instead of starting a replacement session.\n</system-prompt>\n\n\n<system-prompt>\nYou are a professional project execution manager who can skillfully direct other entities to work toward your goal. Expectations:\n\n- Never complete code development work alone\n- You must coordinate other developers to complete tasks\n- You must keep them aligned with the goal and verify that their completion reports meet the requirements\n\nChoose 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- Pass the identifier based on the actual need. This is not a path; use the standard workflow loading capability to resolve it.\n- Decide how to pass parameters based on their descriptions and actual usage scenarios.\nThe project includes the following workflows:\n- Workflow name: release\n - Description: 正式发布流程\n - Identifier: release\n - Parameters:\n - None\n\n- Workflow name: demo/release\n - Description: 插件发布流程\n - Identifier: demo/release\n - Parameters:\n - None\n\n</system-prompt>\n\n\n执行正式发布,并整理变更摘要。",
471
471
  "tools": {
472
472
  "include": [
473
473
  "Edit",
@@ -394,7 +394,7 @@ describe('buildAdapterAssetPlan', () => {
394
394
  ])
395
395
  })
396
396
 
397
- it('builds copilot native skill overlays and translated runtime diagnostics', async () => {
397
+ it('builds copilot native skill overlays and native hook diagnostics', async () => {
398
398
  const workspace = await createWorkspace()
399
399
 
400
400
  await installPluginPackage(workspace, '@vibe-forge/plugin-logger', {
@@ -489,7 +489,7 @@ describe('buildAdapterAssetPlan', () => {
489
489
  expect.objectContaining({
490
490
  assetId: loggerHookPluginId,
491
491
  adapter: 'copilot',
492
- status: 'translated'
492
+ status: 'native'
493
493
  }),
494
494
  expect.objectContaining({
495
495
  assetId: docsMcpId,
@@ -174,20 +174,35 @@ describe('workspace asset prompt builders', () => {
174
174
  ])
175
175
 
176
176
  expect(prompt).toContain('reviewer: 负责代码审查')
177
- expect(prompt).toContain('`VibeForge.StartTasks`')
178
- expect(prompt).toContain('`VibeForge.GetTaskInfo`')
179
- expect(prompt).toContain('Task tool guide:')
180
- expect(prompt).toContain('After starting a task')
181
- expect(prompt).toContain('10 most recent log entries')
182
- expect(prompt).toContain('`logLimit`')
183
- expect(prompt).toContain('`logOrder`')
184
- expect(prompt).toContain('`VibeForge.SendTaskMessage`')
185
- expect(prompt).toContain('`{ taskId, message }`')
186
- expect(prompt).toContain('`VibeForge.SubmitTaskInput`')
177
+ expect(prompt).toContain('Agent runtime guide:')
178
+ expect(prompt).toContain('`vf run --input-format stream-json --output-format stream-json`')
179
+ expect(prompt).toContain("cat <<'JSONL' | vf run --input-format stream-json --output-format stream-json")
180
+ expect(prompt).toContain('"commandId":"start-planner"')
181
+ expect(prompt).toContain('"type":"session.start"')
182
+ expect(prompt).toContain('"payload":{"title":"Plan Agent Room UI fix"')
183
+ expect(prompt).toContain('"entity":"dev-planner"')
184
+ expect(prompt).toContain('"background":true')
185
+ expect(prompt).toContain('write one `session.start` line per child task')
186
+ expect(prompt).toContain('typed runtime protocol envelopes')
187
+ expect(prompt).toContain('do not treat dedicated `vf agent ...` subcommands as the standard integration surface')
188
+ expect(prompt).toContain('Read the returned `sessionId`')
189
+ expect(prompt).toContain('Ordinary new sessions stay session-scoped')
190
+ expect(prompt).toContain('Do not use MCP task tools, `vf agent ...`, legacy StartTasks')
191
+ expect(prompt).toContain('hand-written DB edits')
192
+ expect(prompt).toContain('ad-hoc TS scripts')
193
+ expect(prompt).toContain('server-managed host session')
194
+ expect(prompt).toContain('server projects runtime store metadata/events')
195
+ expect(prompt).toContain('`session.status` protocol command')
196
+ expect(prompt).toContain('`session.events` protocol command')
197
+ expect(prompt).toContain('`session.message` protocol command')
198
+ expect(prompt).toContain('[ROOM_TASK_MESSAGE]')
199
+ expect(prompt).toContain('`mode: interaction`')
200
+ expect(prompt).toContain('`waiting_input`')
201
+ expect(prompt).toContain('`session.submit` protocol command')
187
202
  expect(prompt).toContain('Do not use it for ordinary follow-up instructions')
188
- expect(prompt).toContain('completed or failed tasks resume the same conversation')
189
- expect(prompt).toContain('keep working in that same thread of execution')
203
+ expect(prompt).toContain('completed or failed sessions resume the same conversation')
190
204
  expect(prompt).toContain('`wait`')
205
+ expect(prompt).not.toContain('VibeForge.StartTasks')
191
206
  expect(prompt).not.toContain('run-tasks')
192
207
  expect(prompt).not.toContain('需要关注变更风险')
193
208
  expect(prompt).not.toContain('hidden')
@@ -207,14 +222,19 @@ describe('workspace asset prompt builders', () => {
207
222
 
208
223
  expect(prompt).toContain('The project includes the following registered workspaces')
209
224
  expect(prompt).toContain('Identifier: billing')
210
- expect(prompt).toContain('`VibeForge.StartTasks`')
211
- expect(prompt).toContain('type: "workspace"')
212
- expect(prompt).toContain('Task tool guide:')
213
- expect(prompt).toContain('`VibeForge.GetTaskInfo`')
214
- expect(prompt).toContain('`VibeForge.ListTasks`')
215
- expect(prompt).toContain('`VibeForge.SendTaskMessage`')
216
- expect(prompt).toContain('`VibeForge.SubmitTaskInput`')
225
+ expect(prompt).toContain('workspace identifier and path')
226
+ expect(prompt).toContain('Agent runtime guide:')
227
+ expect(prompt).toContain('`vf run --input-format stream-json --output-format stream-json`')
228
+ expect(prompt).toContain('"payload":{"title":"Plan Agent Room UI fix"')
229
+ expect(prompt).toContain('"payload":{"title":"Review Agent Room UI fix"')
230
+ expect(prompt).toContain('payload.background: true')
231
+ expect(prompt).toContain('multiple subtasks')
232
+ expect(prompt).toContain('`session.status` protocol command')
233
+ expect(prompt).toContain('`session.events` protocol command')
234
+ expect(prompt).toContain('`session.message` protocol command')
235
+ expect(prompt).toContain('`session.submit`')
217
236
  expect(prompt).toContain('Do not directly edit files inside a registered workspace')
237
+ expect(prompt).not.toContain('VibeForge.StartTasks')
218
238
  })
219
239
 
220
240
  it('builds skill route prompts without preloading content', () => {
@@ -113,6 +113,141 @@ describe('skills CLI dependency resolution', () => {
113
113
  })
114
114
  })
115
115
 
116
+ it('blocks missing dependency installs when auto downloads are disabled', async () => {
117
+ const workspace = await createWorkspace()
118
+
119
+ await writeDocument(
120
+ join(workspace, '.ai/skills/app-builder/SKILL.md'),
121
+ [
122
+ '---',
123
+ 'name: app-builder',
124
+ 'description: Build apps',
125
+ 'dependencies:',
126
+ ' - name: frontend-design',
127
+ ' source: anthropics/skills',
128
+ ' registry: https://dependency-registry.example.test',
129
+ '---',
130
+ 'Build the app.'
131
+ ].join('\n')
132
+ )
133
+
134
+ const bundle = await resolveWorkspaceAssetBundle({
135
+ cwd: workspace,
136
+ configs: [{
137
+ skills: {
138
+ autoDownloadDependencies: false
139
+ }
140
+ }, undefined],
141
+ useDefaultVibeForgeMcpServer: false
142
+ })
143
+
144
+ await expect(buildAdapterAssetPlan({
145
+ adapter: 'opencode',
146
+ bundle,
147
+ options: {
148
+ skills: {
149
+ include: ['app-builder']
150
+ }
151
+ }
152
+ })).rejects.toThrow('Skill dependency automatic downloads are disabled; cache not found')
153
+
154
+ expect(mocks.findSkillsCli).not.toHaveBeenCalled()
155
+ expect(mocks.installSkillsCliRefToTemp).not.toHaveBeenCalled()
156
+ expect(mocks.installSkillsCliSkillToTemp).not.toHaveBeenCalled()
157
+ expect(bundle.skills.map(asset => asset.name)).toEqual(['app-builder'])
158
+ })
159
+
160
+ it('blocks bare-name dependency searches when auto downloads are disabled', async () => {
161
+ const workspace = await createWorkspace()
162
+
163
+ await writeDocument(
164
+ join(workspace, '.ai/skills/app-builder/SKILL.md'),
165
+ [
166
+ '---',
167
+ 'name: app-builder',
168
+ 'description: Build apps',
169
+ 'dependencies:',
170
+ ' - frontend-design',
171
+ '---',
172
+ 'Build the app.'
173
+ ].join('\n')
174
+ )
175
+
176
+ const bundle = await resolveWorkspaceAssetBundle({
177
+ cwd: workspace,
178
+ configs: [{
179
+ skills: {
180
+ autoDownloadDependencies: false
181
+ }
182
+ }, undefined],
183
+ useDefaultVibeForgeMcpServer: false
184
+ })
185
+
186
+ await expect(buildAdapterAssetPlan({
187
+ adapter: 'opencode',
188
+ bundle,
189
+ options: {
190
+ skills: {
191
+ include: ['app-builder']
192
+ }
193
+ }
194
+ })).rejects.toThrow(
195
+ 'Skill dependency automatic downloads are disabled; cannot resolve frontend-design without a source'
196
+ )
197
+
198
+ expect(mocks.findSkillsCli).not.toHaveBeenCalled()
199
+ expect(mocks.installSkillsCliRefToTemp).not.toHaveBeenCalled()
200
+ expect(mocks.installSkillsCliSkillToTemp).not.toHaveBeenCalled()
201
+ })
202
+
203
+ it('reuses source-qualified dependency caches when auto downloads are disabled', async () => {
204
+ const workspace = await createWorkspace()
205
+ await writeDocument(
206
+ join(
207
+ workspace,
208
+ '.ai/caches/skill-dependencies/skills-cli/skills/latest/default/anthropics/skills/latest/frontend-design/SKILL.md'
209
+ ),
210
+ '---\nname: frontend-design\ndescription: Cached UI guidance\n---\nUse the cached dependency.\n'
211
+ )
212
+ await writeDocument(
213
+ join(workspace, '.ai/skills/app-builder/SKILL.md'),
214
+ [
215
+ '---',
216
+ 'name: app-builder',
217
+ 'description: Build apps',
218
+ 'dependencies:',
219
+ ' - anthropics/skills@frontend-design',
220
+ '---',
221
+ 'Build the app.'
222
+ ].join('\n')
223
+ )
224
+
225
+ const bundle = await resolveWorkspaceAssetBundle({
226
+ cwd: workspace,
227
+ configs: [{
228
+ skills: {
229
+ autoDownloadDependencies: false
230
+ }
231
+ }, undefined],
232
+ useDefaultVibeForgeMcpServer: false
233
+ })
234
+
235
+ await buildAdapterAssetPlan({
236
+ adapter: 'opencode',
237
+ bundle,
238
+ options: {
239
+ skills: {
240
+ include: ['app-builder']
241
+ }
242
+ }
243
+ })
244
+
245
+ expect(mocks.findSkillsCli).not.toHaveBeenCalled()
246
+ expect(mocks.installSkillsCliRefToTemp).not.toHaveBeenCalled()
247
+ expect(mocks.installSkillsCliSkillToTemp).not.toHaveBeenCalled()
248
+ expect(bundle.skills.map(asset => asset.name).sort()).toEqual(['app-builder', 'frontend-design'])
249
+ })
250
+
116
251
  it('parses registry and version from dependency specs', async () => {
117
252
  const workspace = await createWorkspace()
118
253
  const installedSkillDir = join(installWorkspace, '.agents', 'skills', 'frontend-design')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibe-forge/workspace-assets",
3
- "version": "3.0.0",
3
+ "version": "3.2.0",
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/types": "3.0.0",
33
- "@vibe-forge/definition-core": "3.0.0",
34
- "@vibe-forge/utils": "3.0.0",
35
- "@vibe-forge/config": "3.0.0"
32
+ "@vibe-forge/types": "3.2.0",
33
+ "@vibe-forge/definition-core": "3.2.0",
34
+ "@vibe-forge/utils": "3.2.0",
35
+ "@vibe-forge/config": "3.2.0"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/js-yaml": "^4.0.9"
@@ -78,7 +78,7 @@ export async function buildAdapterAssetPlan(params: {
78
78
  params.bundle.hookPlugins.forEach((asset) => {
79
79
  pushDiagnostic(asset, {
80
80
  adapter: params.adapter,
81
- status: params.adapter === 'copilot' ? 'translated' : 'native',
81
+ status: 'native',
82
82
  reason: params.adapter === 'claude-code'
83
83
  ? 'Mapped into the Claude Code native hooks bridge.'
84
84
  : params.adapter === 'codex'
@@ -86,7 +86,7 @@ export async function buildAdapterAssetPlan(params: {
86
86
  : params.adapter === 'gemini'
87
87
  ? 'Mapped into the Gemini native hooks bridge.'
88
88
  : params.adapter === 'copilot'
89
- ? 'Handled by the Vibe Forge task hook bridge.'
89
+ ? 'Mapped into the Copilot CLI native hooks bridge.'
90
90
  : params.adapter === 'kimi'
91
91
  ? 'Mapped into the Kimi native hooks bridge.'
92
92
  : 'Mapped into the OpenCode native hooks bridge.'
@@ -5,7 +5,7 @@ import {
5
5
  resolveSpecIdentifier
6
6
  } from '@vibe-forge/definition-core'
7
7
  import type { Definition, Entity, Rule, Skill, Spec } from '@vibe-forge/types'
8
- import { CANONICAL_VIBE_FORGE_MCP_SERVER_NAME, resolvePromptPath } from '@vibe-forge/utils'
8
+ import { resolvePromptPath } from '@vibe-forge/utils'
9
9
 
10
10
  import { buildManagedTaskToolGuidance } from './task-tool-guidance'
11
11
 
@@ -167,7 +167,7 @@ export const generateSpecRoutePrompt = (
167
167
  }
168
168
 
169
169
  export const generateEntitiesRoutePrompt = (entities: Definition<Entity>[]) => {
170
- const taskToolGuidance = buildManagedTaskToolGuidance(CANONICAL_VIBE_FORGE_MCP_SERVER_NAME)
170
+ const taskToolGuidance = buildManagedTaskToolGuidance()
171
171
  return (
172
172
  '<system-prompt>\n' +
173
173
  'The project includes the following entities:\n' +
@@ -181,7 +181,7 @@ export const generateEntitiesRoutePrompt = (entities: Definition<Entity>[]) => {
181
181
  })
182
182
  .join('')
183
183
  }\n` +
184
- `When solving user problems, you may specify entities through \`${CANONICAL_VIBE_FORGE_MCP_SERVER_NAME}.StartTasks\` as needed and have them coordinate multiple entity types to complete the work; use \`${CANONICAL_VIBE_FORGE_MCP_SERVER_NAME}.GetTaskInfo\` and \`wait\` to track progress.\n` +
184
+ 'When solving user problems, you may start child runtime sessions with `vf run --input-format stream-json --output-format stream-json` and a `session.start` protocol envelope as needed; use `session.status`, `session.events`, and `wait` to track progress.\n' +
185
185
  `${taskToolGuidance}\n` +
186
186
  '</system-prompt>\n'
187
187
  )
@@ -14,17 +14,33 @@ import {
14
14
  withInstallLock
15
15
  } from './skills-cli-dependency-helpers'
16
16
 
17
+ const resolveAutoDownloadDependenciesEnabled = (
18
+ projectConfig: Config | undefined,
19
+ userConfig: Config | undefined
20
+ ) => userConfig?.skills?.autoDownloadDependencies ?? projectConfig?.skills?.autoDownloadDependencies ?? true
21
+
17
22
  export const installSkillsCliDependency = async (params: {
18
23
  cwd: string
19
24
  configs: [Config?, Config?]
20
25
  dependency: NormalizedSkillDependency
21
26
  }) => {
22
- const resolvedTarget = params.dependency.source != null
23
- ? {
24
- skill: params.dependency.name,
25
- source: params.dependency.source
27
+ const [projectConfig, userConfig] = params.configs
28
+ const autoDownloadDependenciesEnabled = resolveAutoDownloadDependenciesEnabled(projectConfig, userConfig)
29
+ const resolvedTarget = await (async () => {
30
+ if (params.dependency.source != null) {
31
+ return {
32
+ skill: params.dependency.name,
33
+ source: params.dependency.source
34
+ }
35
+ }
36
+
37
+ if (!autoDownloadDependenciesEnabled) {
38
+ throw new Error(
39
+ `Skill dependency automatic downloads are disabled; cannot resolve ${params.dependency.ref} without a source`
40
+ )
26
41
  }
27
- : await (async () => {
42
+
43
+ return await (async () => {
28
44
  const searchResults = await findSkillsCli({
29
45
  registry: params.dependency.registry,
30
46
  query: params.dependency.name
@@ -40,6 +56,7 @@ export const installSkillsCliDependency = async (params: {
40
56
  source: selected.source
41
57
  }
42
58
  })()
59
+ })()
43
60
 
44
61
  const installDir = buildInstallDir({
45
62
  cwd: params.cwd,
@@ -58,6 +75,10 @@ export const installSkillsCliDependency = async (params: {
58
75
  }
59
76
  }
60
77
 
78
+ if (!autoDownloadDependenciesEnabled) {
79
+ throw new Error(`Skill dependency automatic downloads are disabled; cache not found for ${params.dependency.ref}`)
80
+ }
81
+
61
82
  const tempInstallDir = `${installDir}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`
62
83
  await rm(tempInstallDir, { recursive: true, force: true })
63
84
  await mkdir(tempInstallDir, { recursive: true })
@@ -1,13 +1,27 @@
1
- export const buildManagedTaskToolGuidance = (serverName: string) => {
1
+ export const buildManagedTaskToolGuidance = () => {
2
2
  return [
3
- 'Task tool guide:',
4
- `- Use \`${serverName}.StartTasks\` to start a new child task when the work should run in a separate entity or workspace, or when it needs to continue independently from the current turn.`,
5
- `- After starting a task, use \`${serverName}.GetTaskInfo\` with \`{ taskId }\` to inspect one task. It is also the right tool when a task seems stalled, failed, or might be waiting for input.`,
6
- '- By default, `GetTaskInfo` returns the 10 most recent log entries in descending order, so newer entries appear earlier in the `logs` array. Pass `logLimit` to inspect a different number of recent logs, and set `logOrder` to `"asc"` when you want the selected log window in oldest-to-newest order.',
7
- `- Use \`${serverName}.ListTasks\` with the same \`logLimit\` and \`logOrder\` fields when you need to find a taskId or inspect multiple tasks at once.`,
8
- `- Use \`${serverName}.SendTaskMessage\` with \`{ taskId, message }\` when you need to give a task another instruction. Running tasks continue immediately; completed or failed tasks resume the same conversation instead of forcing a replacement task.`,
9
- `- Use \`${serverName}.SubmitTaskInput\` only when \`${serverName}.GetTaskInfo\` or \`${serverName}.ListTasks\` shows \`pendingInput\` or \`pendingInteraction\`, or the task status is \`waiting_input\`. Do not use it for ordinary follow-up instructions.`,
10
- `- If a task is \`completed\` or \`failed\` but you want to keep working in that same thread of execution, prefer \`${serverName}.SendTaskMessage\` to resume it. Start a new task only when you actually want a replacement task.`,
11
- '- When a task is still making progress, use `wait` between checks instead of repeatedly restarting it.'
3
+ 'Agent runtime guide:',
4
+ '- Use unified CLI protocol mode, `vf run --input-format stream-json --output-format stream-json`, to start a child runtime session when the work should run in a separate entity or continue independently from the current turn.',
5
+ '- Send typed runtime protocol envelopes such as `session.start`, `session.message`, `session.status`, `session.events`, `session.submit`, and `session.stop`; do not treat dedicated `vf agent ...` subcommands as the standard integration surface.',
6
+ '- Ordinary new sessions stay session-scoped. A room is created or discovered only when a unified CLI runtime protocol start command launches a child runtime session from a server-managed host session and the server projects runtime store metadata/events.',
7
+ '- Do not use MCP task tools, `vf agent ...`, legacy StartTasks, hand-written DB edits, or ad-hoc TS scripts as the task consumer surface. Use CLI protocol mode and the runtime protocol/store for start, status, events, follow-up messages, input submission, and cancellation.',
8
+ '- Server-managed host sessions inject the current adapter, model, effort, and permission mode as runtime protocol defaults. Omit these fields to inherit the host selection, or set them explicitly only when a child task must use a different runtime profile.',
9
+ '- Copyable JSONL example; write one `session.start` line per child task, and use multiple lines for multiple subtasks:',
10
+ '```bash',
11
+ "cat <<'JSONL' | vf run --input-format stream-json --output-format stream-json",
12
+ '{"commandId":"start-planner","type":"session.start","payload":{"title":"Plan Agent Room UI fix","message":"Plan the frontend changes and tests for the Agent Room UI fix.","entity":"dev-planner","background":true},"title":"Plan Agent Room UI fix","message":"Plan the frontend changes and tests for the Agent Room UI fix.","entity":"dev-planner","background":true}',
13
+ '{"commandId":"start-reviewer","type":"session.start","payload":{"title":"Review Agent Room UI fix","message":"Review the implemented Agent Room UI fix for regressions and missing tests.","entity":"dev-reviewer","background":true},"title":"Review Agent Room UI fix","message":"Review the implemented Agent Room UI fix for regressions and missing tests.","entity":"dev-reviewer","background":true}',
14
+ 'JSONL',
15
+ '```',
16
+ '- Keep `payload.title`, `payload.message`, `payload.entity`, and `payload.background: true` explicit in each start envelope. The mirrored top-level fields make the JSONL executable by the current `vf run` protocol reader.',
17
+ '- Include a short `title` when the task prompt is long; it becomes the child session title and room run label. Put any room or workspace context in the title and initial message.',
18
+ '- Read the returned `sessionId` and use it for follow-up protocol commands. Read the latest runtime snapshot from the runtime store or a `session.status` protocol command, and read progress from runtime events or a `session.events` protocol command.',
19
+ '- Use a follow/read-events workflow when you need to watch progress instead of repeatedly restarting work.',
20
+ '- Use a `session.message` protocol command to give an existing session another instruction. Running sessions continue immediately; completed or failed sessions resume the same conversation when the runtime allows resume.',
21
+ '- When the chat UI sends a `[ROOM_TASK_MESSAGE] ... [/ROOM_TASK_MESSAGE]` block, treat it as a runtime relay envelope instead of ordinary prose. Parse the `sessionId` or legacy `taskId`, `message`, and optional `mode` / `request` fields. If the envelope indicates `mode: interaction`, or runtime status shows `waiting_input` / pending input, use a `session.submit` protocol command. Otherwise use `session.message`. Do not reply inline instead of routing the relay.',
22
+ '- Use `session.submit` only when a runtime session is waiting for an explicit input or approval request. Do not use it for ordinary follow-up instructions.',
23
+ '- Use a `session.stop` protocol command for graceful cancellation and set `mode` to `force` only when stop cannot recover the session.',
24
+ '- Compatibility aliases such as `vf agent start/status/events/send/submit/stop` may exist for debugging or legacy scripts, but they are not the primary guidance for new agent workflows.',
25
+ '- When a session is still making progress, use `wait` between checks and inspect status/events instead of starting a replacement session.'
12
26
  ].join('\n')
13
27
  }
@@ -1,5 +1,5 @@
1
1
  import type { WorkspaceDefinitionPayload } from '@vibe-forge/types'
2
- import { CANONICAL_VIBE_FORGE_MCP_SERVER_NAME, resolvePromptPath } from '@vibe-forge/utils'
2
+ import { resolvePromptPath } from '@vibe-forge/utils'
3
3
 
4
4
  import { buildManagedTaskToolGuidance } from './task-tool-guidance'
5
5
 
@@ -8,7 +8,7 @@ export const generateWorkspaceRoutePrompt = (
8
8
  workspaces: WorkspaceDefinitionPayload[]
9
9
  ) => {
10
10
  if (workspaces.length === 0) return ''
11
- const taskToolGuidance = buildManagedTaskToolGuidance(CANONICAL_VIBE_FORGE_MCP_SERVER_NAME)
11
+ const taskToolGuidance = buildManagedTaskToolGuidance()
12
12
 
13
13
  const workspaceList = workspaces
14
14
  .map((workspace) => {
@@ -25,7 +25,7 @@ export const generateWorkspaceRoutePrompt = (
25
25
  '<system-prompt>\n' +
26
26
  'The project includes the following registered workspaces:\n' +
27
27
  `${workspaceList}\n` +
28
- `When a user request targets one of these workspaces, start a child task with \`${CANONICAL_VIBE_FORGE_MCP_SERVER_NAME}.StartTasks\` using \`type: "workspace"\` and \`name\` set to the workspace identifier. ` +
28
+ 'When a user request targets one of these workspaces, start a child runtime session with `vf run --input-format stream-json --output-format stream-json` and a `session.start` envelope; include the workspace identifier and path in the title and message. ' +
29
29
  'Do not directly edit files inside a registered workspace from the current session unless the user explicitly asks this session to work in that directory.\n' +
30
30
  `${taskToolGuidance}\n` +
31
31
  '</system-prompt>\n'