@probelabs/visor 0.1.148 → 0.1.149

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.
Files changed (70) hide show
  1. package/defaults/assistant.yaml +2141 -0
  2. package/defaults/code-talk.yaml +1250 -0
  3. package/defaults/intent-router.yaml +478 -0
  4. package/dist/defaults/assistant.yaml +2141 -0
  5. package/dist/defaults/code-talk.yaml +1250 -0
  6. package/dist/defaults/intent-router.yaml +478 -0
  7. package/dist/index.js +17 -4
  8. package/dist/output/traces/{run-2026-03-02T18-32-11-359Z.ndjson → run-2026-03-03T07-19-07-543Z.ndjson} +84 -84
  9. package/dist/{traces/run-2026-03-02T18-32-55-702Z.ndjson → output/traces/run-2026-03-03T07-19-50-933Z.ndjson} +1866 -1187
  10. package/dist/sdk/{check-provider-registry-35BPTY4W.mjs → check-provider-registry-IYSUDKPB.mjs} +7 -7
  11. package/dist/sdk/{check-provider-registry-DVQDGTOE.mjs → check-provider-registry-LVLC4EPF.mjs} +4 -4
  12. package/dist/sdk/{check-provider-registry-KHPY6LB4.mjs → check-provider-registry-X4OZJWPK.mjs} +4 -4
  13. package/dist/sdk/{chunk-6N6JRWCW.mjs → chunk-6EXCUX7Y.mjs} +10 -10
  14. package/dist/sdk/{chunk-S2YO4ZE3.mjs → chunk-BR7DYA3S.mjs} +2 -2
  15. package/dist/sdk/{chunk-DIND4ZCV.mjs → chunk-DNDS7R3N.mjs} +11 -1
  16. package/dist/sdk/{chunk-DIND4ZCV.mjs.map → chunk-DNDS7R3N.mjs.map} +1 -1
  17. package/dist/sdk/{chunk-IF2UD2KS.mjs → chunk-GFNXX64M.mjs} +18 -18
  18. package/dist/sdk/{chunk-AYQE4JCU.mjs → chunk-Q6EPAJ6Z.mjs} +3 -3
  19. package/dist/sdk/{chunk-H4AYMOAT.mjs → chunk-V6GI4U2M.mjs} +10 -10
  20. package/dist/sdk/{chunk-EGUHXVWS.mjs → chunk-VLUGLWLA.mjs} +2 -2
  21. package/dist/sdk/{chunk-EGUHXVWS.mjs.map → chunk-VLUGLWLA.mjs.map} +1 -1
  22. package/dist/sdk/{chunk-XNTBSV6M.mjs → chunk-YYZAN5NK.mjs} +3 -3
  23. package/dist/sdk/{config-G5UU4WXT.mjs → config-KQH254CA.mjs} +2 -2
  24. package/dist/sdk/{failure-condition-evaluator-I6QWFKV3.mjs → failure-condition-evaluator-LZ2AG5PY.mjs} +3 -3
  25. package/dist/sdk/{github-frontend-2MC77L7F.mjs → github-frontend-S523EEJB.mjs} +3 -3
  26. package/dist/sdk/{host-4F6I3ZXN.mjs → host-7YKRMOUJ.mjs} +2 -2
  27. package/dist/sdk/{routing-UT3BXBXH.mjs → routing-ZMBKWMVI.mjs} +4 -4
  28. package/dist/sdk/{schedule-tool-K3GQXCBN.mjs → schedule-tool-CDVUSZEG.mjs} +7 -7
  29. package/dist/sdk/{schedule-tool-SBXAEBDD.mjs → schedule-tool-EOMZFICZ.mjs} +4 -4
  30. package/dist/sdk/{schedule-tool-CONR4VW3.mjs → schedule-tool-NX75VKGA.mjs} +4 -4
  31. package/dist/sdk/{schedule-tool-handler-GFQCJAVZ.mjs → schedule-tool-handler-3FJHDIPG.mjs} +7 -7
  32. package/dist/sdk/{schedule-tool-handler-R7PG3VMR.mjs → schedule-tool-handler-KKN7XJYT.mjs} +4 -4
  33. package/dist/sdk/{schedule-tool-handler-YUC6CAXX.mjs → schedule-tool-handler-QNZG55DX.mjs} +4 -4
  34. package/dist/sdk/sdk.js +11 -1
  35. package/dist/sdk/sdk.js.map +1 -1
  36. package/dist/sdk/sdk.mjs +6 -6
  37. package/dist/sdk/{trace-helpers-J463EU4B.mjs → trace-helpers-EJUIOP6L.mjs} +2 -2
  38. package/dist/sdk/{workflow-check-provider-GJNGTS3F.mjs → workflow-check-provider-6ERNNCNA.mjs} +7 -7
  39. package/dist/sdk/{workflow-check-provider-DYSO3PML.mjs → workflow-check-provider-AGZ5JY2I.mjs} +4 -4
  40. package/dist/sdk/{workflow-check-provider-FIFFQDQU.mjs → workflow-check-provider-PTNUWM5W.mjs} +4 -4
  41. package/dist/sdk/{workflow-registry-AAD37XKZ.mjs → workflow-registry-MHUSKSD6.mjs} +2 -2
  42. package/dist/traces/{run-2026-03-02T18-32-11-359Z.ndjson → run-2026-03-03T07-19-07-543Z.ndjson} +84 -84
  43. package/dist/{output/traces/run-2026-03-02T18-32-55-702Z.ndjson → traces/run-2026-03-03T07-19-50-933Z.ndjson} +1866 -1187
  44. package/dist/workflow-registry.d.ts.map +1 -1
  45. package/package.json +1 -1
  46. /package/dist/sdk/{check-provider-registry-35BPTY4W.mjs.map → check-provider-registry-IYSUDKPB.mjs.map} +0 -0
  47. /package/dist/sdk/{check-provider-registry-DVQDGTOE.mjs.map → check-provider-registry-LVLC4EPF.mjs.map} +0 -0
  48. /package/dist/sdk/{check-provider-registry-KHPY6LB4.mjs.map → check-provider-registry-X4OZJWPK.mjs.map} +0 -0
  49. /package/dist/sdk/{chunk-6N6JRWCW.mjs.map → chunk-6EXCUX7Y.mjs.map} +0 -0
  50. /package/dist/sdk/{chunk-S2YO4ZE3.mjs.map → chunk-BR7DYA3S.mjs.map} +0 -0
  51. /package/dist/sdk/{chunk-IF2UD2KS.mjs.map → chunk-GFNXX64M.mjs.map} +0 -0
  52. /package/dist/sdk/{chunk-AYQE4JCU.mjs.map → chunk-Q6EPAJ6Z.mjs.map} +0 -0
  53. /package/dist/sdk/{chunk-H4AYMOAT.mjs.map → chunk-V6GI4U2M.mjs.map} +0 -0
  54. /package/dist/sdk/{chunk-XNTBSV6M.mjs.map → chunk-YYZAN5NK.mjs.map} +0 -0
  55. /package/dist/sdk/{config-G5UU4WXT.mjs.map → config-KQH254CA.mjs.map} +0 -0
  56. /package/dist/sdk/{failure-condition-evaluator-I6QWFKV3.mjs.map → failure-condition-evaluator-LZ2AG5PY.mjs.map} +0 -0
  57. /package/dist/sdk/{github-frontend-2MC77L7F.mjs.map → github-frontend-S523EEJB.mjs.map} +0 -0
  58. /package/dist/sdk/{host-4F6I3ZXN.mjs.map → host-7YKRMOUJ.mjs.map} +0 -0
  59. /package/dist/sdk/{routing-UT3BXBXH.mjs.map → routing-ZMBKWMVI.mjs.map} +0 -0
  60. /package/dist/sdk/{schedule-tool-CONR4VW3.mjs.map → schedule-tool-CDVUSZEG.mjs.map} +0 -0
  61. /package/dist/sdk/{schedule-tool-K3GQXCBN.mjs.map → schedule-tool-EOMZFICZ.mjs.map} +0 -0
  62. /package/dist/sdk/{schedule-tool-SBXAEBDD.mjs.map → schedule-tool-NX75VKGA.mjs.map} +0 -0
  63. /package/dist/sdk/{schedule-tool-handler-GFQCJAVZ.mjs.map → schedule-tool-handler-3FJHDIPG.mjs.map} +0 -0
  64. /package/dist/sdk/{schedule-tool-handler-R7PG3VMR.mjs.map → schedule-tool-handler-KKN7XJYT.mjs.map} +0 -0
  65. /package/dist/sdk/{schedule-tool-handler-YUC6CAXX.mjs.map → schedule-tool-handler-QNZG55DX.mjs.map} +0 -0
  66. /package/dist/sdk/{trace-helpers-J463EU4B.mjs.map → trace-helpers-EJUIOP6L.mjs.map} +0 -0
  67. /package/dist/sdk/{workflow-check-provider-DYSO3PML.mjs.map → workflow-check-provider-6ERNNCNA.mjs.map} +0 -0
  68. /package/dist/sdk/{workflow-check-provider-FIFFQDQU.mjs.map → workflow-check-provider-AGZ5JY2I.mjs.map} +0 -0
  69. /package/dist/sdk/{workflow-check-provider-GJNGTS3F.mjs.map → workflow-check-provider-PTNUWM5W.mjs.map} +0 -0
  70. /package/dist/sdk/{workflow-registry-AAD37XKZ.mjs.map → workflow-registry-MHUSKSD6.mjs.map} +0 -0
@@ -0,0 +1,2141 @@
1
+ # =============================================================================
2
+ # assistant: High-level AI Assistant Workflow
3
+ # =============================================================================
4
+ #
5
+ # A declarative, batteries-included workflow for building AI assistants.
6
+ # Combines intent routing, dynamic tool selection, knowledge embedding,
7
+ # and code exploration into a single configurable workflow.
8
+ #
9
+ # Just declare your:
10
+ # - Intents (for high-level routing)
11
+ # - Skills (unified: knowledge + tools with dependencies)
12
+ #
13
+ # The workflow handles all the wiring automatically.
14
+ #
15
+ # Usage with unified skills (recommended):
16
+ # imports:
17
+ # - visor://assistant.yaml
18
+ #
19
+ # checks:
20
+ # chat:
21
+ # type: workflow
22
+ # workflow: assistant
23
+ # args:
24
+ # question: "{{ outputs['ask'].text }}"
25
+ # intents:
26
+ # - id: chat
27
+ # description: general Q&A
28
+ # skills:
29
+ # - id: jira
30
+ # description: needs Jira access
31
+ # knowledge: "Use jira_get_issue to fetch tickets."
32
+ # tools: # supports multiple tools per skill
33
+ # jira: # tool name is the key
34
+ # command: uvx
35
+ # args: [mcp-atlassian]
36
+ # - id: ticketing
37
+ # description: cross-platform ticketing
38
+ # tools:
39
+ # jira:
40
+ # command: uvx
41
+ # args: [mcp-atlassian]
42
+ # zendesk:
43
+ # command: uvx
44
+ # args: [mcp-zendesk]
45
+ #
46
+ # =============================================================================
47
+
48
+ id: assistant
49
+ name: AI Assistant
50
+ description: |
51
+ High-level AI assistant workflow with declarative configuration.
52
+ Handles intent routing, dynamic tools, and knowledge injection automatically.
53
+ version: "1.0.0"
54
+
55
+ # Note: These imports are relative to this file's location
56
+ # When importing assistant.yaml from a URL, these become relative URLs
57
+ imports:
58
+ - intent-router.yaml
59
+ - code-talk.yaml
60
+
61
+ inputs:
62
+ # =========================================================================
63
+ # Core Input
64
+ # =========================================================================
65
+ - name: question
66
+ required: true
67
+ description: The user's question/message
68
+ schema:
69
+ type: string
70
+
71
+ - name: history
72
+ required: false
73
+ description: Conversation history (array of {role, text})
74
+ default: []
75
+ schema:
76
+ type: array
77
+
78
+ # =========================================================================
79
+ # Intent & Tag Configuration
80
+ # =========================================================================
81
+ - name: intents
82
+ required: true
83
+ description: |
84
+ Array of intent definitions for routing:
85
+ - id: unique identifier
86
+ - description: when to use this intent
87
+ - default_skills: (optional) skills to always activate for this intent
88
+ schema:
89
+ type: array
90
+ items:
91
+ type: object
92
+ properties:
93
+ id: { type: string }
94
+ description: { type: string }
95
+ default_skills:
96
+ type: array
97
+ items: { type: string }
98
+ required: [id, description]
99
+
100
+ - name: tags
101
+ required: false
102
+ description: |
103
+ Array of tag definitions for capability classification:
104
+ - id: unique identifier
105
+ - description: when to apply this tag
106
+ default: []
107
+ schema:
108
+ type: array
109
+ items:
110
+ type: object
111
+ properties:
112
+ id: { type: string }
113
+ description: { type: string }
114
+ required: [id, description]
115
+
116
+ - name: routing_instructions
117
+ required: false
118
+ description: Additional routing rules for the intent classifier
119
+ default: ""
120
+ schema:
121
+ type: string
122
+
123
+ # =========================================================================
124
+ # Knowledge Configuration
125
+ # =========================================================================
126
+ - name: knowledge
127
+ required: false
128
+ description: |
129
+ Array of knowledge documents to inject based on tags:
130
+ - tags: array of tag IDs that trigger this knowledge
131
+ - content: the knowledge content to inject
132
+ - intent: (optional) specific intent that triggers this
133
+ default: []
134
+ schema:
135
+ type: array
136
+ items:
137
+ type: object
138
+ properties:
139
+ tags:
140
+ type: array
141
+ items: { type: string }
142
+ intent:
143
+ type: string
144
+ content:
145
+ type: string
146
+ required: [content]
147
+
148
+ # =========================================================================
149
+ # MCP Server Configuration
150
+ # =========================================================================
151
+ - name: mcp_servers
152
+ required: false
153
+ description: |
154
+ Array of MCP servers to enable based on tags:
155
+ - tags: array of tag IDs that trigger this server
156
+ - intent: (optional) specific intent that triggers this
157
+ - name: server name (used as key in ai_mcp_servers)
158
+ - server: MCP server configuration object
159
+ For external servers: { command, args, env, allowedMethods }
160
+ For workflow tools: { workflow, inputs }
161
+ For built-in tools: { tool: 'tool-name' }
162
+ default: []
163
+ schema:
164
+ type: array
165
+ items:
166
+ type: object
167
+ properties:
168
+ tags:
169
+ type: array
170
+ items: { type: string }
171
+ intent:
172
+ type: string
173
+ name:
174
+ type: string
175
+ server:
176
+ type: object
177
+ required: [name, server]
178
+
179
+ # =========================================================================
180
+ # Skills Configuration (unified knowledge + tools)
181
+ # =========================================================================
182
+ - name: skills
183
+ required: false
184
+ description: |
185
+ Array of skill definitions that bundle knowledge and tools together.
186
+ Each skill combines related functionality in one place:
187
+ - id: unique skill identifier
188
+ - tags: array of tag IDs that trigger this skill
189
+ - intent: (optional) specific intent that triggers this skill
190
+ - knowledge: knowledge content to inject when skill is active
191
+ - tools: (optional) object mapping tool names to configurations
192
+ Each key is the tool name, value is the config:
193
+ For external MCP: { command, args, env, allowedMethods }
194
+ For workflows: { workflow, inputs }
195
+ For built-in: { tool: 'tool-name' }
196
+
197
+ Skills are merged with standalone knowledge[] and mcp_servers[] configs.
198
+ default: []
199
+ schema:
200
+ type: array
201
+ items:
202
+ type: object
203
+ properties:
204
+ id:
205
+ type: string
206
+ description: Unique skill identifier (used for classification and requires)
207
+ description:
208
+ type: string
209
+ description: When this skill should activate (used by classifier)
210
+ requires:
211
+ type: array
212
+ items: { type: string }
213
+ description: Other skill IDs to auto-activate when this skill is active
214
+ tags:
215
+ type: array
216
+ items: { type: string }
217
+ description: (Legacy) Tag IDs that trigger this skill
218
+ intent:
219
+ type: string
220
+ description: (Legacy) Specific intent that triggers this skill
221
+ knowledge:
222
+ type: string
223
+ description: Knowledge content to inject when skill is active
224
+ tools:
225
+ type: object
226
+ description: Tools to enable when skill is active (key = tool name)
227
+ additionalProperties:
228
+ type: object
229
+ description: Tool configuration
230
+ properties:
231
+ command:
232
+ type: string
233
+ description: Command to run (for external MCP servers)
234
+ args:
235
+ type: array
236
+ items: { type: string }
237
+ description: Command arguments
238
+ env:
239
+ type: object
240
+ additionalProperties: { type: string }
241
+ description: Environment variables (supports ${VAR} syntax)
242
+ allowedMethods:
243
+ type: array
244
+ items: { type: string }
245
+ description: Restrict to specific MCP methods
246
+ workflow:
247
+ type: string
248
+ description: Workflow name (for workflow-based tools)
249
+ inputs:
250
+ type: object
251
+ description: Workflow inputs (for workflow-based tools)
252
+ tool:
253
+ type: string
254
+ description: Built-in tool name (for built-in tools)
255
+ allowed_commands:
256
+ type: array
257
+ items: { type: string }
258
+ description: Bash command patterns this skill is allowed to run (e.g., 'git:log:*')
259
+ disallowed_commands:
260
+ type: array
261
+ items: { type: string }
262
+ description: Bash command patterns this skill should not run (e.g., 'git:push:*')
263
+ always:
264
+ type: boolean
265
+ description: Always activate this skill regardless of classification
266
+ required: [id]
267
+
268
+ # =========================================================================
269
+ # AI Configuration
270
+ # =========================================================================
271
+ - name: system_prompt
272
+ required: false
273
+ description: System prompt for the AI (identity, capabilities, and guidelines)
274
+ default: "You are a helpful AI assistant with access to specialized tools and knowledge."
275
+ schema:
276
+ type: string
277
+
278
+ - name: max_iterations
279
+ required: false
280
+ description: Maximum AI iterations
281
+ default: 30
282
+ schema:
283
+ type: number
284
+
285
+ - name: enableDelegate
286
+ required: false
287
+ description: Enable the delegate tool for task distribution to subagents
288
+ default: true
289
+ schema:
290
+ type: boolean
291
+
292
+ - name: enableTasks
293
+ required: false
294
+ description: Enable task management for tracking multi-goal requests
295
+ default: false
296
+ schema:
297
+ type: boolean
298
+
299
+ - name: enableExecutePlan
300
+ required: false
301
+ description: Enable the execute_plan DSL orchestration tool
302
+ default: false
303
+ schema:
304
+ type: boolean
305
+
306
+ # =========================================================================
307
+ # Outputs
308
+ # =========================================================================
309
+ outputs:
310
+ - name: text
311
+ description: The AI response text
312
+ value_js: |
313
+ return outputs?.['generate-response']?.text ?? null;
314
+
315
+ - name: intent
316
+ description: The classified intent
317
+ value_js: |
318
+ return outputs?.['route-intent']?.intent ?? null;
319
+
320
+ - name: tags
321
+ description: The classified tags
322
+ value_js: |
323
+ return outputs?.['route-intent']?.tags ?? [];
324
+
325
+ - name: topic
326
+ description: The rewritten topic/question
327
+ value_js: |
328
+ return outputs?.['route-intent']?.topic ?? null;
329
+
330
+ # =========================================================================
331
+ # Steps
332
+ # =========================================================================
333
+ steps:
334
+ # -------------------------------------------------------------------------
335
+ # Step 1: Route intent and classify skills (or tags for backwards compat)
336
+ # -------------------------------------------------------------------------
337
+ route-intent:
338
+ type: workflow
339
+ workflow: intent-router
340
+ criticality: internal
341
+ assume:
342
+ - "true"
343
+ guarantee: "output?.intent != null"
344
+ args:
345
+ question: "{{ inputs.question }}"
346
+ # Use expression syntax to pass arrays directly (not as JSON strings)
347
+ intents:
348
+ expression: "inputs.intents"
349
+ # Pass skills if provided (unified mode), otherwise fall back to tags
350
+ skills:
351
+ expression: |
352
+ // If skills have descriptions, pass them for classification
353
+ const skills = inputs.skills || [];
354
+ // Extract id and description for classification
355
+ return skills.filter(s => s.description).map(s => ({
356
+ id: s.id,
357
+ description: s.description,
358
+ requires: s.requires || []
359
+ }));
360
+ tags:
361
+ expression: "inputs.tags || []"
362
+ routing_instructions: "{{ inputs.routing_instructions }}"
363
+
364
+ # -------------------------------------------------------------------------
365
+ # Step 2: Build dynamic configuration for the AI step
366
+ # -------------------------------------------------------------------------
367
+ build-config:
368
+ type: script
369
+ criticality: internal
370
+ depends_on: [route-intent]
371
+ assume:
372
+ - "outputs['route-intent'] != null"
373
+ schema:
374
+ type: object
375
+ properties:
376
+ mcp_servers:
377
+ type: object
378
+ knowledge_content:
379
+ type: string
380
+ bash_config:
381
+ type: object
382
+ properties:
383
+ allow:
384
+ type: array
385
+ items: { type: string }
386
+ deny:
387
+ type: array
388
+ items: { type: string }
389
+ content: |
390
+ const intent = outputs['route-intent']?.intent ?? '';
391
+ const tags = outputs['route-intent']?.tags ?? [];
392
+ const question = inputs.question ?? '';
393
+ const intentConfigs = Array.isArray(inputs.intents) ? inputs.intents : [];
394
+
395
+ // Get activated skill IDs from router and expand dependencies
396
+ const rawSelectedSkills = outputs['route-intent']?.skills ?? [];
397
+ const skillConfigs = Array.isArray(inputs.skills) ? inputs.skills : [];
398
+
399
+ // Build requires map for dependency expansion
400
+ const requiresMap = {};
401
+ const allSkillIds = {};
402
+ for (let i = 0; i < skillConfigs.length; i++) {
403
+ const skill = skillConfigs[i];
404
+ if (skill.id) {
405
+ allSkillIds[skill.id] = true;
406
+ if (Array.isArray(skill.requires)) {
407
+ requiresMap[skill.id] = skill.requires;
408
+ }
409
+ }
410
+ }
411
+
412
+ // Validate that all requires reference existing skill IDs
413
+ const unknownDeps = [];
414
+ for (let i = 0; i < skillConfigs.length; i++) {
415
+ const skill = skillConfigs[i];
416
+ if (Array.isArray(skill.requires)) {
417
+ for (let j = 0; j < skill.requires.length; j++) {
418
+ const dep = skill.requires[j];
419
+ if (!allSkillIds[dep]) {
420
+ unknownDeps.push({ skill: skill.id, unknown: dep });
421
+ }
422
+ }
423
+ }
424
+ }
425
+ if (unknownDeps.length > 0) {
426
+ log("⚠️ Unknown skill dependencies detected:", JSON.stringify(unknownDeps));
427
+ }
428
+
429
+ // Add default skills configured on the selected intent
430
+ let defaultSkillsForIntent = [];
431
+ for (let i = 0; i < intentConfigs.length; i++) {
432
+ const intentConfig = intentConfigs[i];
433
+ if (intentConfig?.id === intent && Array.isArray(intentConfig.default_skills)) {
434
+ defaultSkillsForIntent = intentConfig.default_skills;
435
+ break;
436
+ }
437
+ }
438
+
439
+ const unknownDefaultSkills = [];
440
+ for (let i = 0; i < defaultSkillsForIntent.length; i++) {
441
+ const skillId = defaultSkillsForIntent[i];
442
+ if (!allSkillIds[skillId]) {
443
+ unknownDefaultSkills.push({ intent, unknown: skillId });
444
+ }
445
+ }
446
+ if (unknownDefaultSkills.length > 0) {
447
+ log("⚠️ Unknown intent default_skills detected:", JSON.stringify(unknownDefaultSkills));
448
+ }
449
+
450
+ // Expand selected skills to include their dependencies using array
451
+ const activatedSkillsArr = Array.isArray(rawSelectedSkills) ? rawSelectedSkills.slice() : [];
452
+ for (let i = 0; i < defaultSkillsForIntent.length; i++) {
453
+ const skillId = defaultSkillsForIntent[i];
454
+ if (activatedSkillsArr.indexOf(skillId) === -1) {
455
+ activatedSkillsArr.push(skillId);
456
+ }
457
+ }
458
+ // Add always-on skills
459
+ for (let i = 0; i < skillConfigs.length; i++) {
460
+ const skill = skillConfigs[i];
461
+ if (skill.always === true && skill.id && activatedSkillsArr.indexOf(skill.id) === -1) {
462
+ activatedSkillsArr.push(skill.id);
463
+ }
464
+ }
465
+ let changed = true;
466
+ while (changed) {
467
+ changed = false;
468
+ const currentSkills = activatedSkillsArr.slice();
469
+ for (let i = 0; i < currentSkills.length; i++) {
470
+ const skillId = currentSkills[i];
471
+ const deps = requiresMap[skillId] || [];
472
+ for (let j = 0; j < deps.length; j++) {
473
+ const dep = deps[j];
474
+ if (activatedSkillsArr.indexOf(dep) === -1) {
475
+ activatedSkillsArr.push(dep);
476
+ changed = true;
477
+ }
478
+ }
479
+ }
480
+ }
481
+
482
+ const activatedSkills = activatedSkillsArr;
483
+ const activatedSkillSet = {};
484
+ for (let i = 0; i < activatedSkills.length; i++) {
485
+ activatedSkillSet[activatedSkills[i]] = true;
486
+ }
487
+
488
+ // Helper function to check if tags/intent match (for backwards compatibility)
489
+ function shouldActivateByTagsOrIntent(itemTags, itemIntent) {
490
+ // Check if any tag matches
491
+ if (itemTags && itemTags.length > 0) {
492
+ for (const tag of itemTags) {
493
+ if (tags.includes(tag)) {
494
+ return true;
495
+ }
496
+ }
497
+ }
498
+ // Check if intent matches
499
+ if (itemIntent && intent === itemIntent) {
500
+ return true;
501
+ }
502
+ return false;
503
+ }
504
+
505
+ // Build MCP servers, knowledge, and bash config from skills + standalone configs
506
+ const servers = {};
507
+ const knowledgeParts = [];
508
+ const bashAllow = [];
509
+ const bashDeny = [];
510
+ let bashEnabled = false;
511
+ let executePlanEnabled = false;
512
+
513
+ // =========================================================================
514
+ // Process skills - check by ID if skills mode, or by tags/intent otherwise
515
+ // =========================================================================
516
+ const usingSkillsMode = activatedSkills.length > 0;
517
+
518
+ for (const skill of skillConfigs) {
519
+ let isActive = false;
520
+
521
+ if (usingSkillsMode) {
522
+ // Skills mode: check if this skill ID was selected by the router
523
+ isActive = activatedSkillSet[skill.id] === true;
524
+ } else {
525
+ // Legacy mode: check by tags/intent
526
+ const skillTags = Array.isArray(skill.tags) ? skill.tags : [];
527
+ const skillIntent = skill.intent;
528
+ isActive = shouldActivateByTagsOrIntent(skillTags, skillIntent);
529
+ }
530
+
531
+ if (isActive) {
532
+ log('[build-config] Activating skill:', skill.id, 'hasTools:', !!skill.tools, 'hasKnowledge:', !!skill.knowledge);
533
+
534
+ // Build structured XML block for each active skill
535
+ const parts = [];
536
+ parts.push('<skill>');
537
+ parts.push(' <id>' + (skill.id || '') + '</id>');
538
+ parts.push(' <description>' + (skill.description || '') + '</description>');
539
+ if (skill.knowledge) {
540
+ parts.push(' <knowledge>' + skill.knowledge + '</knowledge>');
541
+ }
542
+ if (skill.tools && typeof skill.tools === 'object') {
543
+ const toolIds = Object.keys(skill.tools);
544
+ if (toolIds.length > 0) {
545
+ parts.push(' <tools>' + toolIds.join(', ') + '</tools>');
546
+ }
547
+ }
548
+ parts.push('</skill>');
549
+ knowledgeParts.push(parts.join('\n'));
550
+
551
+ // Add tools if present
552
+ if (skill.tools && typeof skill.tools === 'object') {
553
+ const skillTools = skill.tools;
554
+ const toolNames = Object.keys(skillTools);
555
+ log('[build-config] Skill', skill.id, 'has tools:', toolNames.join(', '));
556
+ for (let i = 0; i < toolNames.length; i++) {
557
+ const toolName = toolNames[i];
558
+ const toolConfig = skillTools[toolName];
559
+
560
+ // Handle built-in Probe tools (not MCP servers)
561
+ if (toolName === 'bash') {
562
+ bashEnabled = true;
563
+ log('[build-config] Bash tool enabled by skill:', skill.id);
564
+ continue;
565
+ }
566
+ if (toolName === 'execute_plan') {
567
+ executePlanEnabled = true;
568
+ log('[build-config] execute_plan tool enabled by skill:', skill.id);
569
+ continue;
570
+ }
571
+
572
+ if (toolConfig) {
573
+ // If tool already exists, merge allowedMethods/blockedMethods
574
+ if (servers[toolName]) {
575
+ const existing = servers[toolName];
576
+ // Merge allowedMethods arrays
577
+ if (Array.isArray(toolConfig.allowedMethods)) {
578
+ if (!Array.isArray(existing.allowedMethods)) {
579
+ existing.allowedMethods = [];
580
+ }
581
+ for (let j = 0; j < toolConfig.allowedMethods.length; j++) {
582
+ const method = toolConfig.allowedMethods[j];
583
+ if (existing.allowedMethods.indexOf(method) === -1) {
584
+ existing.allowedMethods.push(method);
585
+ }
586
+ }
587
+ }
588
+ // Merge blockedMethods arrays
589
+ if (Array.isArray(toolConfig.blockedMethods)) {
590
+ if (!Array.isArray(existing.blockedMethods)) {
591
+ existing.blockedMethods = [];
592
+ }
593
+ for (let j = 0; j < toolConfig.blockedMethods.length; j++) {
594
+ const method = toolConfig.blockedMethods[j];
595
+ if (existing.blockedMethods.indexOf(method) === -1) {
596
+ existing.blockedMethods.push(method);
597
+ }
598
+ }
599
+ }
600
+ } else {
601
+ servers[toolName] = toolConfig;
602
+ }
603
+ }
604
+ }
605
+ }
606
+
607
+ // Collect bash command patterns from active skills
608
+ if (Array.isArray(skill.allowed_commands)) {
609
+ for (let j = 0; j < skill.allowed_commands.length; j++) {
610
+ if (bashAllow.indexOf(skill.allowed_commands[j]) === -1) {
611
+ bashAllow.push(skill.allowed_commands[j]);
612
+ }
613
+ }
614
+ }
615
+ if (Array.isArray(skill.disallowed_commands)) {
616
+ for (let j = 0; j < skill.disallowed_commands.length; j++) {
617
+ if (bashDeny.indexOf(skill.disallowed_commands[j]) === -1) {
618
+ bashDeny.push(skill.disallowed_commands[j]);
619
+ }
620
+ }
621
+ }
622
+ }
623
+ }
624
+
625
+ // =========================================================================
626
+ // Process standalone MCP servers (for backwards compatibility)
627
+ // =========================================================================
628
+ const mcpConfigs = Array.isArray(inputs.mcp_servers) ? inputs.mcp_servers : [];
629
+ for (const config of mcpConfigs) {
630
+ const serverTags = Array.isArray(config.tags) ? config.tags : [];
631
+ const serverIntent = config.intent;
632
+ const serverName = config.name;
633
+ const serverConfig = config.server;
634
+
635
+ if (!serverName || !serverConfig) continue;
636
+
637
+ // Check if this server should be enabled
638
+ let shouldEnable = shouldActivateByTagsOrIntent(serverTags, serverIntent);
639
+
640
+ // If no tags or intent specified, always enable
641
+ if (serverTags.length === 0 && !serverIntent) {
642
+ shouldEnable = true;
643
+ }
644
+
645
+ if (shouldEnable) {
646
+ servers[serverName] = serverConfig;
647
+ }
648
+ }
649
+
650
+ // =========================================================================
651
+ // Process standalone knowledge (for backwards compatibility)
652
+ // =========================================================================
653
+ const knowledgeConfigs = Array.isArray(inputs.knowledge) ? inputs.knowledge : [];
654
+
655
+ for (const config of knowledgeConfigs) {
656
+ const knowledgeTags = Array.isArray(config.tags) ? config.tags : [];
657
+ const knowledgeIntent = config.intent;
658
+ const content = config.content;
659
+
660
+ if (!content) continue;
661
+
662
+ let shouldInclude = shouldActivateByTagsOrIntent(knowledgeTags, knowledgeIntent);
663
+
664
+ if (shouldInclude) {
665
+ knowledgeParts.push(content);
666
+ }
667
+ }
668
+
669
+ // Debug: log what servers are being returned
670
+ const serverNames = Object.keys(servers);
671
+ log('[build-config] MCP servers to return:', serverNames.length, 'servers:', serverNames.join(', '));
672
+ if (serverNames.length > 0) {
673
+ log('[build-config] First server config:', JSON.stringify(servers[serverNames[0]]).substring(0, 200));
674
+ }
675
+
676
+ return {
677
+ mcp_servers: servers,
678
+ knowledge_content: knowledgeParts.join('\n\n'),
679
+ bash_enabled: bashEnabled,
680
+ execute_plan_enabled: executePlanEnabled,
681
+ bash_config: {
682
+ allow: bashAllow,
683
+ deny: bashDeny
684
+ }
685
+ };
686
+
687
+ # -------------------------------------------------------------------------
688
+ # Step 3: Generate response with dynamic tools and knowledge
689
+ # -------------------------------------------------------------------------
690
+ generate-response:
691
+ type: ai
692
+ criticality: internal
693
+ depends_on: [route-intent, build-config]
694
+ assume:
695
+ - "outputs['route-intent'] != null"
696
+ - "outputs['build-config'] != null"
697
+ guarantee: "(output?.text ?? '').length > 0"
698
+ ai:
699
+ max_iterations: "{{ inputs.max_iterations }}"
700
+ enableDelegate: "{{ inputs.enableDelegate }}"
701
+ enableTasks: "{{ inputs.enableTasks }}"
702
+ enableExecutePlan: "{{ inputs.enableExecutePlan }}"
703
+ skip_code_context: true
704
+ skip_slack_context: false
705
+ # Post-completion validation to prevent hallucination of completed actions
706
+ completion_prompt: |
707
+ CRITICAL VALIDATION - Before finalizing your response, verify:
708
+
709
+ Action verification:
710
+ 1. If you claimed to CREATE something (Jira ticket, PR, file, etc.), did you actually call the tool?
711
+ - Creating a Jira ticket requires calling mcp__jira__jira_create_issue
712
+ - Creating a PR requires calling the engineer tool
713
+ - If you didn't call the tool, DO NOT claim the action was completed
714
+ 2. If you claimed to UPDATE something, did you actually call the update tool?
715
+ 3. If you claimed to SEARCH or FETCH something, did you get actual results?
716
+
717
+ Tool output verification:
718
+ 4. Check your tool call history - does it support your claims?
719
+ 5. If a tool returned an error or empty result, acknowledge it honestly
720
+ 6. Never claim success if you only PLANNED to do something but didn't execute it
721
+
722
+ If you find discrepancies:
723
+ - Correct your response to reflect what actually happened
724
+ - If you couldn't complete an action, explain why and what the user can do
725
+ - Be honest about failures rather than claiming false success
726
+ # Wildcard-with-exclusions pattern: allow everything except tools that
727
+ # the AI tends to misuse instead of skill-specific MCP tools.
728
+ # ProbeAgent resolves "*" and "!" patterns natively.
729
+ ai_allowed_tools_js: |
730
+ // Start with wildcard — allow all tools by default
731
+ const excluded = ['search', 'query', 'extract', 'listFiles', 'searchFiles', 'delegate'];
732
+ const bashEnabled = outputs['build-config']?.bash_enabled === true;
733
+ if (!bashEnabled) {
734
+ excluded.push('bash');
735
+ }
736
+ if (outputs['build-config']?.execute_plan_enabled !== true) {
737
+ excluded.push('execute_plan');
738
+ }
739
+ return ['*', ...excluded.map(t => '!' + t)];
740
+ ai_mcp_servers_js: |
741
+ // Return the pre-built MCP servers from build-config
742
+ return outputs['build-config']?.mcp_servers ?? {};
743
+ ai_bash_config_js: |
744
+ return outputs['build-config']?.bash_config ?? {};
745
+ schema:
746
+ type: object
747
+ properties:
748
+ text:
749
+ type: string
750
+ description: The response to the user
751
+ required: [text]
752
+ prompt: |
753
+ {{ inputs.system_prompt }}
754
+
755
+ Your capabilities are dynamically enabled based on the user's request.
756
+ When tools are available, use them to provide accurate, up-to-date information.
757
+ When knowledge is injected below, incorporate it into your responses.
758
+
759
+ {% if inputs.history and inputs.history.size > 0 %}
760
+ <history>
761
+ {% for m in inputs.history %}
762
+ {{ m.role | capitalize }}: {{ m.text }}
763
+ {% endfor %}
764
+ </history>
765
+ {% endif %}
766
+
767
+ ## Current Request
768
+ User: {{ inputs.question }}
769
+
770
+ ## Classification
771
+ Intent: {{ outputs['route-intent'].intent }}
772
+ {% if outputs['route-intent'].skills and outputs['route-intent'].skills.size > 0 %}
773
+ Skills: {{ outputs['route-intent'].skills | join: ', ' }}
774
+ {% endif %}
775
+ {% if outputs['route-intent'].tags and outputs['route-intent'].tags.size > 0 %}
776
+ Tags: {{ outputs['route-intent'].tags | join: ', ' }}
777
+ {% endif %}
778
+
779
+ {% if outputs['build-config'].knowledge_content and outputs['build-config'].knowledge_content != '' %}
780
+ {{ outputs['build-config'].knowledge_content }}
781
+ {% endif %}
782
+
783
+ ## Guidelines
784
+ - This is an ONGOING DIALOGUE - treat it as a continuous conversation, not isolated messages
785
+ - If continuing previous work (user says "ok", "do it", "continue", etc.), pick up where you left off
786
+ - Reference previous context when relevant - don't make the user repeat themselves
787
+ - Be concise but thorough - avoid unnecessary filler
788
+ - Provide specific references (file paths, line numbers, URLs) when available
789
+ - If you're unsure, say so rather than guessing
790
+ - Use available tools proactively - don't make up information that could be retrieved
791
+ - When a tool returns an error, explain what happened and suggest alternatives
792
+ - **CRITICAL: Always use `attempt_completion` tool to submit your final response** - this enables validation of your actions before the response is finalized
793
+
794
+ ## CRITICAL: Preserve Tool Output Details
795
+ When tools return detailed data (lists, analytics, search results):
796
+ - **DO NOT summarize or compress** the tool output
797
+ - **RELAY THE FULL DATA** including all names, specifics, and details
798
+
799
+ ## CRITICAL: Never Modify URLs from Tool Outputs
800
+ When a tool returns URLs, **COPY THE URL EXACTLY** as returned - do not modify
801
+
802
+ ## CRITICAL: Code Exploration Has Limited Access
803
+ Code exploration tools (code-talk, code-explorer) are sandboxed to code repositories only.
804
+ They do NOT have access to external APIs like Jira, Zendesk, GitHub Issues, or Confluence.
805
+ If a code question references external data (tickets, issues, docs), YOU must fetch that
806
+ data first using the appropriate tools before delegating to code exploration.
807
+
808
+ # =============================================================================
809
+ # Tests
810
+ # =============================================================================
811
+ tests:
812
+ defaults:
813
+ strict: true
814
+ ai_provider: mock
815
+
816
+ cases:
817
+ # =========================================================================
818
+ # Basic Routing Tests
819
+ # =========================================================================
820
+ - name: basic-chat-routing
821
+ description: Routes simple chat to correct intent
822
+ event: manual
823
+ fixture: local.minimal
824
+ workflow_input:
825
+ question: "Hello, how are you?"
826
+ intents:
827
+ - id: chat
828
+ description: general Q&A
829
+ - id: code_help
830
+ description: code questions
831
+ tags: []
832
+ mocks:
833
+ route-intent:
834
+ intent: chat
835
+ topic: "Hello, how are you?"
836
+ tags: []
837
+ generate-response:
838
+ text: "Hello! I'm doing well, how can I help you?"
839
+ expect:
840
+ calls:
841
+ - step: route-intent
842
+ exactly: 1
843
+ - step: build-config
844
+ exactly: 1
845
+ - step: generate-response
846
+ exactly: 1
847
+ workflow_output:
848
+ - path: intent
849
+ equals: "chat"
850
+ - path: text
851
+ contains: "Hello"
852
+
853
+ - name: code-help-routing
854
+ description: Routes code questions with codebase tag
855
+ event: manual
856
+ fixture: local.minimal
857
+ workflow_input:
858
+ question: "How does authentication work?"
859
+ intents:
860
+ - id: chat
861
+ description: general Q&A
862
+ - id: code_help
863
+ description: code questions
864
+ tags:
865
+ - id: codebase
866
+ description: needs code exploration
867
+ mocks:
868
+ route-intent:
869
+ intent: code_help
870
+ topic: "How does authentication work?"
871
+ tags: ["codebase"]
872
+ generate-response:
873
+ text: "Authentication is implemented using JWT tokens."
874
+ expect:
875
+ calls:
876
+ - step: route-intent
877
+ exactly: 1
878
+ - step: build-config
879
+ exactly: 1
880
+ - step: generate-response
881
+ exactly: 1
882
+ workflow_output:
883
+ - path: intent
884
+ equals: "code_help"
885
+ - path: tags
886
+ contains: "codebase"
887
+
888
+ # =========================================================================
889
+ # Knowledge Injection Tests
890
+ # =========================================================================
891
+ - name: knowledge-injection-by-tag
892
+ description: Injects knowledge when tag matches
893
+ event: manual
894
+ fixture: local.minimal
895
+ workflow_input:
896
+ question: "What can you do?"
897
+ intents:
898
+ - id: chat
899
+ description: general Q&A
900
+ tags:
901
+ - id: capabilities
902
+ description: user asks about capabilities
903
+ knowledge:
904
+ - tags: ["capabilities"]
905
+ content: |
906
+ ## Capabilities
907
+ I can help with code exploration and task execution.
908
+ mocks:
909
+ route-intent:
910
+ intent: chat
911
+ topic: "What can you do?"
912
+ tags: ["capabilities"]
913
+ generate-response:
914
+ text: "I can help with code exploration and task execution."
915
+ expect:
916
+ calls:
917
+ - step: route-intent
918
+ exactly: 1
919
+ - step: build-config
920
+ exactly: 1
921
+ - step: generate-response
922
+ exactly: 1
923
+ workflow_output:
924
+ - path: tags
925
+ contains: "capabilities"
926
+
927
+ - name: knowledge-injection-by-intent
928
+ description: Injects knowledge when intent matches
929
+ event: manual
930
+ fixture: local.minimal
931
+ workflow_input:
932
+ question: "How does the API work?"
933
+ intents:
934
+ - id: chat
935
+ description: general Q&A
936
+ - id: code_help
937
+ description: code questions
938
+ knowledge:
939
+ - intent: code_help
940
+ content: |
941
+ ## Code Exploration
942
+ Use the code-explorer tool to search the codebase.
943
+ mocks:
944
+ route-intent:
945
+ intent: code_help
946
+ topic: "How does the API work?"
947
+ tags: []
948
+ generate-response:
949
+ text: "The API is implemented in the handlers package."
950
+ expect:
951
+ calls:
952
+ - step: route-intent
953
+ exactly: 1
954
+ - step: build-config
955
+ exactly: 1
956
+ - step: generate-response
957
+ exactly: 1
958
+ workflow_output:
959
+ - path: intent
960
+ equals: "code_help"
961
+
962
+ # =========================================================================
963
+ # MCP Server Tests
964
+ # =========================================================================
965
+ - name: mcp-server-enabled-by-tag
966
+ description: MCP server is enabled when tag matches
967
+ event: manual
968
+ fixture: local.minimal
969
+ workflow_input:
970
+ question: "Get ticket TT-123"
971
+ intents:
972
+ - id: chat
973
+ description: general Q&A
974
+ tags:
975
+ - id: jira
976
+ description: needs Jira access
977
+ mcp_servers:
978
+ - tags: ["jira"]
979
+ name: atlassian
980
+ server:
981
+ command: uvx
982
+ args: ["mcp-atlassian"]
983
+ mocks:
984
+ route-intent:
985
+ intent: chat
986
+ topic: "Get ticket TT-123"
987
+ tags: ["jira"]
988
+ generate-response:
989
+ text: "Here is ticket TT-123."
990
+ expect:
991
+ calls:
992
+ - step: route-intent
993
+ exactly: 1
994
+ - step: build-config
995
+ exactly: 1
996
+ - step: generate-response
997
+ exactly: 1
998
+ workflow_output:
999
+ - path: tags
1000
+ contains: "jira"
1001
+
1002
+ - name: mcp-server-not-enabled-without-tag
1003
+ description: MCP server is not enabled when tag doesn't match
1004
+ event: manual
1005
+ fixture: local.minimal
1006
+ workflow_input:
1007
+ question: "Hello"
1008
+ intents:
1009
+ - id: chat
1010
+ description: general Q&A
1011
+ tags:
1012
+ - id: jira
1013
+ description: needs Jira access
1014
+ mcp_servers:
1015
+ - tags: ["jira"]
1016
+ name: atlassian
1017
+ server:
1018
+ command: uvx
1019
+ args: ["mcp-atlassian"]
1020
+ mocks:
1021
+ route-intent:
1022
+ intent: chat
1023
+ topic: "Hello"
1024
+ tags: [] # No jira tag
1025
+ generate-response:
1026
+ text: "Hello!"
1027
+ expect:
1028
+ calls:
1029
+ - step: route-intent
1030
+ exactly: 1
1031
+ - step: build-config
1032
+ exactly: 1
1033
+ - step: generate-response
1034
+ exactly: 1
1035
+ workflow_output:
1036
+ - path: tags
1037
+ equals: []
1038
+
1039
+ # =========================================================================
1040
+ # Unified Skills Tests (with description - skills as classification targets)
1041
+ # =========================================================================
1042
+ - name: unified-skills-classification
1043
+ description: Skills with descriptions are used for direct classification
1044
+ event: manual
1045
+ fixture: local.minimal
1046
+ workflow_input:
1047
+ question: "Get ticket TT-123"
1048
+ intents:
1049
+ - id: chat
1050
+ description: general Q&A
1051
+ skills:
1052
+ - id: jira
1053
+ description: request references Jira tickets
1054
+ knowledge: |
1055
+ ## Jira Tools
1056
+ Use jira_get_issue to fetch tickets.
1057
+ tools:
1058
+ atlassian:
1059
+ command: uvx
1060
+ args: ["mcp-atlassian"]
1061
+ - id: zendesk
1062
+ description: request references Zendesk tickets
1063
+ knowledge: |
1064
+ ## Zendesk Tools
1065
+ Use zendesk tools for support.
1066
+ tools:
1067
+ zendesk:
1068
+ command: uvx
1069
+ args: ["zendesk-mcp"]
1070
+ mocks:
1071
+ route-intent:
1072
+ intent: chat
1073
+ topic: "Get ticket TT-123?"
1074
+ skills: ["jira"]
1075
+ generate-response:
1076
+ text: "Here is ticket TT-123."
1077
+ expect:
1078
+ calls:
1079
+ - step: route-intent
1080
+ exactly: 1
1081
+ - step: build-config
1082
+ exactly: 1
1083
+ - step: generate-response
1084
+ exactly: 1
1085
+ workflow_output:
1086
+ - path: intent
1087
+ equals: "chat"
1088
+
1089
+ - name: unified-skills-with-requires
1090
+ description: Skill dependencies are automatically activated
1091
+ event: manual
1092
+ fixture: local.minimal
1093
+ workflow_input:
1094
+ question: "Create a PR to fix the bug"
1095
+ intents:
1096
+ - id: chat
1097
+ description: general Q&A
1098
+ - id: code_help
1099
+ description: code questions
1100
+ skills:
1101
+ - id: code-explorer
1102
+ description: needs codebase exploration
1103
+ knowledge: |
1104
+ ## Code Explorer
1105
+ Use to search codebase.
1106
+ tools:
1107
+ code-talk:
1108
+ workflow: code-talk
1109
+ inputs: {}
1110
+ - id: engineer
1111
+ description: user wants code changes or PR created
1112
+ requires: [code-explorer]
1113
+ knowledge: |
1114
+ ## Engineer Tool
1115
+ Use to make changes and create PRs.
1116
+ tools:
1117
+ engineer:
1118
+ workflow: engineer
1119
+ inputs: {}
1120
+ mocks:
1121
+ route-intent:
1122
+ intent: code_help
1123
+ topic: "Create a PR to fix the bug?"
1124
+ skills: ["engineer", "code-explorer"]
1125
+ generate-response:
1126
+ text: "Created PR #123."
1127
+ expect:
1128
+ calls:
1129
+ - step: route-intent
1130
+ exactly: 1
1131
+ - step: build-config
1132
+ exactly: 1
1133
+ - step: generate-response
1134
+ exactly: 1
1135
+ workflow_output:
1136
+ - path: intent
1137
+ equals: "code_help"
1138
+
1139
+ - name: intent-default-skills-activated
1140
+ description: default_skills on intent are activated even when router returns no skills
1141
+ event: manual
1142
+ fixture: local.minimal
1143
+ workflow_input:
1144
+ question: "Find ticket TT-123"
1145
+ intents:
1146
+ - id: chat
1147
+ description: general Q&A
1148
+ default_skills: [jira]
1149
+ skills:
1150
+ - id: jira
1151
+ description: request references Jira tickets
1152
+ knowledge: |
1153
+ ## Jira Tools
1154
+ Use jira_get_issue to fetch ticket details.
1155
+ tools:
1156
+ atlassian:
1157
+ command: uvx
1158
+ args: ["mcp-atlassian"]
1159
+ mocks:
1160
+ route-intent:
1161
+ intent: chat
1162
+ topic: "Find ticket TT-123?"
1163
+ skills: []
1164
+ generate-response:
1165
+ text: "Found ticket."
1166
+ expect:
1167
+ calls:
1168
+ - step: route-intent
1169
+ exactly: 1
1170
+ - step: build-config
1171
+ exactly: 1
1172
+ - step: generate-response
1173
+ exactly: 1
1174
+ outputs:
1175
+ - step: build-config
1176
+ path: mcp_servers.atlassian.command
1177
+ equals: "uvx"
1178
+ - step: build-config
1179
+ path: knowledge_content
1180
+ matches: "<id>jira</id>"
1181
+
1182
+ - name: intent-default-skills-expand-requires
1183
+ description: default_skills participate in requires dependency expansion
1184
+ event: manual
1185
+ fixture: local.minimal
1186
+ workflow_input:
1187
+ question: "Create a PR for auth fix"
1188
+ intents:
1189
+ - id: code_help
1190
+ description: code questions
1191
+ default_skills: [engineer]
1192
+ skills:
1193
+ - id: code-explorer
1194
+ description: needs codebase exploration
1195
+ tools:
1196
+ code-explorer:
1197
+ workflow: code-talk
1198
+ inputs: {}
1199
+ - id: engineer
1200
+ description: user wants code changes or PR created
1201
+ requires: [code-explorer]
1202
+ tools:
1203
+ engineer:
1204
+ workflow: engineer
1205
+ inputs: {}
1206
+ mocks:
1207
+ route-intent:
1208
+ intent: code_help
1209
+ topic: "Create a PR for auth fix?"
1210
+ skills: []
1211
+ generate-response:
1212
+ text: "Created PR."
1213
+ expect:
1214
+ calls:
1215
+ - step: route-intent
1216
+ exactly: 1
1217
+ - step: build-config
1218
+ exactly: 1
1219
+ - step: generate-response
1220
+ exactly: 1
1221
+ outputs:
1222
+ - step: build-config
1223
+ path: mcp_servers.engineer.workflow
1224
+ equals: "engineer"
1225
+ - step: build-config
1226
+ path: mcp_servers.code-explorer.workflow
1227
+ equals: "code-talk"
1228
+
1229
+ - name: always-on-skill-activated
1230
+ description: Skills with always=true are activated even when not selected by router
1231
+ event: manual
1232
+ fixture: local.minimal
1233
+ workflow_input:
1234
+ question: "Hello, how are you?"
1235
+ intents:
1236
+ - id: chat
1237
+ description: general Q&A
1238
+ skills:
1239
+ - id: feedback
1240
+ always: true
1241
+ description: auto-feedback on mistakes
1242
+ requires: [slack]
1243
+ knowledge: |
1244
+ ## Feedback Skill
1245
+ Send feedback to admin when mistakes are detected.
1246
+ - id: slack
1247
+ description: send DMs and search Slack
1248
+ knowledge: |
1249
+ ## Slack Tools
1250
+ Use slack-send-dm to send messages.
1251
+ tools:
1252
+ slack-send-dm:
1253
+ workflow: slack-send-dm
1254
+ inputs: {}
1255
+ mocks:
1256
+ route-intent:
1257
+ intent: chat
1258
+ topic: "Greeting"
1259
+ skills: []
1260
+ generate-response:
1261
+ text: "Hello!"
1262
+ expect:
1263
+ calls:
1264
+ - step: route-intent
1265
+ exactly: 1
1266
+ - step: build-config
1267
+ exactly: 1
1268
+ - step: generate-response
1269
+ exactly: 1
1270
+ outputs:
1271
+ - step: build-config
1272
+ path: knowledge_content
1273
+ matches: "<id>feedback</id>"
1274
+ - step: build-config
1275
+ path: knowledge_content
1276
+ matches: "<id>slack</id>"
1277
+ - step: build-config
1278
+ path: mcp_servers.slack-send-dm.workflow
1279
+ equals: "slack-send-dm"
1280
+
1281
+ - name: circular-dependency-handled
1282
+ description: Circular skill dependencies don't cause infinite loops
1283
+ event: manual
1284
+ fixture: local.minimal
1285
+ workflow_input:
1286
+ question: "Help me with A"
1287
+ intents:
1288
+ - id: chat
1289
+ description: general Q&A
1290
+ skills:
1291
+ - id: skill-a
1292
+ description: skill A functionality
1293
+ requires: [skill-b]
1294
+ knowledge: |
1295
+ ## Skill A
1296
+ This is skill A.
1297
+ - id: skill-b
1298
+ description: skill B functionality
1299
+ requires: [skill-a]
1300
+ knowledge: |
1301
+ ## Skill B
1302
+ This is skill B.
1303
+ mocks:
1304
+ route-intent:
1305
+ intent: chat
1306
+ topic: "Help me with A?"
1307
+ skills: ["skill-a"]
1308
+ generate-response:
1309
+ text: "Done with A and B."
1310
+ expect:
1311
+ calls:
1312
+ - step: route-intent
1313
+ exactly: 1
1314
+ - step: build-config
1315
+ exactly: 1
1316
+ - step: generate-response
1317
+ exactly: 1
1318
+
1319
+ - name: deep-dependency-chain
1320
+ description: Deep skill dependency chains are fully expanded (A → B → C → D)
1321
+ event: manual
1322
+ fixture: local.minimal
1323
+ workflow_input:
1324
+ question: "Help me with task A"
1325
+ intents:
1326
+ - id: chat
1327
+ description: general Q&A
1328
+ skills:
1329
+ - id: skill-a
1330
+ description: top level skill
1331
+ requires: [skill-b]
1332
+ knowledge: "Skill A knowledge"
1333
+ - id: skill-b
1334
+ description: mid level skill
1335
+ requires: [skill-c]
1336
+ knowledge: "Skill B knowledge"
1337
+ - id: skill-c
1338
+ description: lower level skill
1339
+ requires: [skill-d]
1340
+ knowledge: "Skill C knowledge"
1341
+ - id: skill-d
1342
+ description: base level skill
1343
+ knowledge: "Skill D knowledge"
1344
+ mocks:
1345
+ route-intent:
1346
+ intent: chat
1347
+ topic: "Help me with task A?"
1348
+ skills: ["skill-a"]
1349
+ generate-response:
1350
+ text: "All skills activated."
1351
+ expect:
1352
+ calls:
1353
+ - step: route-intent
1354
+ exactly: 1
1355
+ - step: build-config
1356
+ exactly: 1
1357
+ - step: generate-response
1358
+ exactly: 1
1359
+
1360
+ - name: diamond-dependency-pattern
1361
+ description: Diamond dependencies (A → B+C, B → D, C → D) don't duplicate D
1362
+ event: manual
1363
+ fixture: local.minimal
1364
+ workflow_input:
1365
+ question: "Run skill A"
1366
+ intents:
1367
+ - id: chat
1368
+ description: general Q&A
1369
+ skills:
1370
+ - id: skill-a
1371
+ description: top skill requiring B and C
1372
+ requires: [skill-b, skill-c]
1373
+ knowledge: "Skill A"
1374
+ - id: skill-b
1375
+ description: left branch requiring D
1376
+ requires: [skill-d]
1377
+ knowledge: "Skill B"
1378
+ - id: skill-c
1379
+ description: right branch requiring D
1380
+ requires: [skill-d]
1381
+ knowledge: "Skill C"
1382
+ - id: skill-d
1383
+ description: common base skill
1384
+ knowledge: "Skill D"
1385
+ mocks:
1386
+ route-intent:
1387
+ intent: chat
1388
+ topic: "Run skill A?"
1389
+ skills: ["skill-a"]
1390
+ generate-response:
1391
+ text: "Diamond pattern handled."
1392
+ expect:
1393
+ calls:
1394
+ - step: route-intent
1395
+ exactly: 1
1396
+ - step: build-config
1397
+ exactly: 1
1398
+ - step: generate-response
1399
+ exactly: 1
1400
+
1401
+ - name: self-referencing-skill
1402
+ description: Skill that requires itself doesn't cause infinite loop
1403
+ event: manual
1404
+ fixture: local.minimal
1405
+ workflow_input:
1406
+ question: "Use recursive skill"
1407
+ intents:
1408
+ - id: chat
1409
+ description: general Q&A
1410
+ skills:
1411
+ - id: self-skill
1412
+ description: skill that references itself
1413
+ requires: [self-skill]
1414
+ knowledge: "Self-referencing skill"
1415
+ mocks:
1416
+ route-intent:
1417
+ intent: chat
1418
+ topic: "Use recursive skill?"
1419
+ skills: ["self-skill"]
1420
+ generate-response:
1421
+ text: "Self-reference handled."
1422
+ expect:
1423
+ calls:
1424
+ - step: route-intent
1425
+ exactly: 1
1426
+ - step: build-config
1427
+ exactly: 1
1428
+ - step: generate-response
1429
+ exactly: 1
1430
+
1431
+ - name: unknown-dependency-warns
1432
+ description: Unknown skill in requires logs a warning but doesn't fail
1433
+ event: manual
1434
+ fixture: local.minimal
1435
+ workflow_input:
1436
+ question: "Help me"
1437
+ intents:
1438
+ - id: chat
1439
+ description: general Q&A
1440
+ skills:
1441
+ - id: my-skill
1442
+ description: skill with typo in requires
1443
+ requires: [unknown-skill, another-typo]
1444
+ knowledge: "My skill knowledge"
1445
+ mocks:
1446
+ route-intent:
1447
+ intent: chat
1448
+ topic: "Help me?"
1449
+ skills: ["my-skill"]
1450
+ generate-response:
1451
+ text: "Done."
1452
+ expect:
1453
+ calls:
1454
+ - step: route-intent
1455
+ exactly: 1
1456
+ - step: build-config
1457
+ exactly: 1
1458
+ - step: generate-response
1459
+ exactly: 1
1460
+
1461
+ # =========================================================================
1462
+ # Skills Tests (legacy - using tags for activation)
1463
+ # =========================================================================
1464
+ - name: skill-activates-knowledge-and-server
1465
+ description: Skill activates both knowledge and server when tag matches
1466
+ event: manual
1467
+ fixture: local.minimal
1468
+ workflow_input:
1469
+ question: "Get ticket TT-123"
1470
+ intents:
1471
+ - id: chat
1472
+ description: general Q&A
1473
+ tags:
1474
+ - id: jira
1475
+ description: needs Jira access
1476
+ skills:
1477
+ - id: jira
1478
+ tags: ["jira"]
1479
+ knowledge: |
1480
+ ## Jira Tools
1481
+ Use jira_get_issue to fetch ticket details.
1482
+ tools:
1483
+ atlassian:
1484
+ command: uvx
1485
+ args: ["mcp-atlassian"]
1486
+ mocks:
1487
+ route-intent:
1488
+ intent: chat
1489
+ topic: "Get ticket TT-123"
1490
+ tags: ["jira"]
1491
+ generate-response:
1492
+ text: "Here is ticket TT-123."
1493
+ expect:
1494
+ calls:
1495
+ - step: route-intent
1496
+ exactly: 1
1497
+ - step: build-config
1498
+ exactly: 1
1499
+ - step: generate-response
1500
+ exactly: 1
1501
+ workflow_output:
1502
+ - path: tags
1503
+ contains: "jira"
1504
+
1505
+ - name: skill-inactive-when-no-tag-match
1506
+ description: Skill stays inactive when no tags match
1507
+ event: manual
1508
+ fixture: local.minimal
1509
+ workflow_input:
1510
+ question: "Hello"
1511
+ intents:
1512
+ - id: chat
1513
+ description: general Q&A
1514
+ tags:
1515
+ - id: jira
1516
+ description: needs Jira access
1517
+ skills:
1518
+ - id: jira
1519
+ tags: ["jira"]
1520
+ knowledge: |
1521
+ ## Jira Tools
1522
+ Use jira_get_issue to fetch ticket details.
1523
+ tools:
1524
+ atlassian:
1525
+ command: uvx
1526
+ args: ["mcp-atlassian"]
1527
+ mocks:
1528
+ route-intent:
1529
+ intent: chat
1530
+ topic: "Hello"
1531
+ tags: []
1532
+ generate-response:
1533
+ text: "Hello!"
1534
+ expect:
1535
+ calls:
1536
+ - step: route-intent
1537
+ exactly: 1
1538
+ - step: build-config
1539
+ exactly: 1
1540
+ - step: generate-response
1541
+ exactly: 1
1542
+ workflow_output:
1543
+ - path: tags
1544
+ equals: []
1545
+
1546
+ - name: skill-with-knowledge-only
1547
+ description: Skill can have knowledge without a server
1548
+ event: manual
1549
+ fixture: local.minimal
1550
+ workflow_input:
1551
+ question: "What can you do?"
1552
+ intents:
1553
+ - id: chat
1554
+ description: general Q&A
1555
+ tags:
1556
+ - id: capabilities
1557
+ description: user asks about capabilities
1558
+ skills:
1559
+ - id: capabilities
1560
+ tags: ["capabilities"]
1561
+ knowledge: |
1562
+ ## Capabilities
1563
+ I can help with code exploration and Jira.
1564
+ mocks:
1565
+ route-intent:
1566
+ intent: chat
1567
+ topic: "What can you do?"
1568
+ tags: ["capabilities"]
1569
+ generate-response:
1570
+ text: "I can help with code exploration and Jira."
1571
+ expect:
1572
+ calls:
1573
+ - step: route-intent
1574
+ exactly: 1
1575
+ - step: build-config
1576
+ exactly: 1
1577
+ - step: generate-response
1578
+ exactly: 1
1579
+ workflow_output:
1580
+ - path: tags
1581
+ contains: "capabilities"
1582
+
1583
+ # =========================================================================
1584
+ # Combined Tests
1585
+ # =========================================================================
1586
+ - name: full-config-with-all-features
1587
+ description: All features working together using skills
1588
+ event: manual
1589
+ fixture: local.minimal
1590
+ workflow_input:
1591
+ question: "Create a PR to fix the auth bug in TT-456"
1592
+ system_prompt: |
1593
+ You are an engineering assistant.
1594
+ Be concise and provide code references.
1595
+ intents:
1596
+ - id: chat
1597
+ description: general Q&A
1598
+ - id: code_help
1599
+ description: code questions
1600
+ skills:
1601
+ - id: code-explorer
1602
+ description: needs code exploration
1603
+ tools:
1604
+ code-explorer:
1605
+ workflow: code-talk
1606
+ inputs:
1607
+ architecture: "# Architecture"
1608
+ docs_repo: "my-org/docs"
1609
+ projects:
1610
+ - id: backend
1611
+ repo: my-org/backend
1612
+ description: Backend services
1613
+ - id: engineer
1614
+ description: needs code changes
1615
+ requires: [code-explorer]
1616
+ knowledge: |
1617
+ ## Engineering Guidelines
1618
+ Always explore code first, then make changes.
1619
+ tools:
1620
+ engineer:
1621
+ workflow: engineer
1622
+ inputs: {}
1623
+ - id: jira
1624
+ description: needs Jira access
1625
+ knowledge: |
1626
+ ## Jira Access
1627
+ You can read and update Jira tickets.
1628
+ tools:
1629
+ atlassian:
1630
+ command: uvx
1631
+ args: ["mcp-atlassian"]
1632
+ mocks:
1633
+ route-intent:
1634
+ intent: code_help
1635
+ topic: "Fix auth bug in TT-456"
1636
+ skills: ["code-explorer", "engineer", "jira"]
1637
+ generate-response:
1638
+ text: "Created PR #123 to fix the auth bug."
1639
+ expect:
1640
+ calls:
1641
+ - step: route-intent
1642
+ exactly: 1
1643
+ - step: build-config
1644
+ exactly: 1
1645
+ - step: generate-response
1646
+ exactly: 1
1647
+ workflow_output:
1648
+ - path: intent
1649
+ equals: "code_help"
1650
+ - path: text
1651
+ contains: "PR"
1652
+
1653
+ # =========================================================================
1654
+ # Prompt Content Assertions
1655
+ # =========================================================================
1656
+ - name: prompt-contains-skill-knowledge
1657
+ description: Skill knowledge is injected into prompt when skill activates
1658
+ event: manual
1659
+ fixture: local.minimal
1660
+ workflow_input:
1661
+ question: "Get ticket TT-123"
1662
+ intents:
1663
+ - id: chat
1664
+ description: general Q&A
1665
+ skills:
1666
+ - id: jira
1667
+ description: needs Jira access
1668
+ knowledge: |
1669
+ ## Jira Tools
1670
+ Use jira_get_issue to fetch ticket details.
1671
+ tools:
1672
+ atlassian:
1673
+ command: uvx
1674
+ args: ["mcp-atlassian"]
1675
+ mocks:
1676
+ route-intent:
1677
+ intent: chat
1678
+ topic: "Get ticket TT-123"
1679
+ skills: ["jira"]
1680
+ generate-response:
1681
+ text: "Here is ticket TT-123."
1682
+ expect:
1683
+ calls:
1684
+ - step: route-intent
1685
+ exactly: 1
1686
+ - step: build-config
1687
+ exactly: 1
1688
+ - step: generate-response
1689
+ exactly: 1
1690
+ prompts:
1691
+ - step: generate-response
1692
+ contains:
1693
+ - "<skill>"
1694
+ - "<id>jira</id>"
1695
+ - "<description>needs Jira access</description>"
1696
+ - "<knowledge>"
1697
+ - "## Jira Tools"
1698
+ - "jira_get_issue"
1699
+ - "</knowledge>"
1700
+ - "<tools>atlassian</tools>"
1701
+ - "</skill>"
1702
+ outputs:
1703
+ - step: build-config
1704
+ path: knowledge_content
1705
+ matches: "<skill>"
1706
+ - step: build-config
1707
+ path: knowledge_content
1708
+ matches: "<id>jira</id>"
1709
+ - step: build-config
1710
+ path: knowledge_content
1711
+ matches: "Jira Tools"
1712
+
1713
+ - name: prompt-excludes-inactive-skill-knowledge
1714
+ description: Knowledge from inactive skills is NOT in prompt
1715
+ event: manual
1716
+ fixture: local.minimal
1717
+ workflow_input:
1718
+ question: "Hello there"
1719
+ intents:
1720
+ - id: chat
1721
+ description: general Q&A
1722
+ skills:
1723
+ - id: jira
1724
+ description: needs Jira access
1725
+ knowledge: |
1726
+ ## Jira Tools
1727
+ Use jira_get_issue to fetch ticket details.
1728
+ tools:
1729
+ atlassian:
1730
+ command: uvx
1731
+ args: ["mcp-atlassian"]
1732
+ - id: capabilities
1733
+ description: what can you do
1734
+ knowledge: |
1735
+ ## My Capabilities
1736
+ I can help with code and tickets.
1737
+ mocks:
1738
+ route-intent:
1739
+ intent: chat
1740
+ topic: "Hello there"
1741
+ skills: []
1742
+ generate-response:
1743
+ text: "Hello!"
1744
+ expect:
1745
+ calls:
1746
+ - step: route-intent
1747
+ exactly: 1
1748
+ - step: build-config
1749
+ exactly: 1
1750
+ - step: generate-response
1751
+ exactly: 1
1752
+ prompts:
1753
+ - step: generate-response
1754
+ not_contains:
1755
+ - "## Jira Tools"
1756
+ - "## My Capabilities"
1757
+ outputs:
1758
+ - step: build-config
1759
+ path: knowledge_content
1760
+ equals: ""
1761
+
1762
+ - name: prompt-contains-multiple-skill-knowledge
1763
+ description: Multiple activated skills inject their knowledge into prompt
1764
+ event: manual
1765
+ fixture: local.minimal
1766
+ workflow_input:
1767
+ question: "Get ticket TT-123 and explore the code"
1768
+ intents:
1769
+ - id: chat
1770
+ description: general Q&A
1771
+ skills:
1772
+ - id: jira
1773
+ description: needs Jira access
1774
+ knowledge: |
1775
+ ## Jira Tools
1776
+ Use jira_get_issue to fetch ticket details.
1777
+ tools:
1778
+ atlassian:
1779
+ command: uvx
1780
+ args: ["mcp-atlassian"]
1781
+ - id: code-explorer
1782
+ description: needs code exploration
1783
+ knowledge: |
1784
+ ## Code Exploration
1785
+ Use code-explorer tool to search code.
1786
+ mocks:
1787
+ route-intent:
1788
+ intent: chat
1789
+ topic: "Get ticket TT-123 and explore the code"
1790
+ skills: ["jira", "code-explorer"]
1791
+ generate-response:
1792
+ text: "Found ticket and code."
1793
+ expect:
1794
+ calls:
1795
+ - step: route-intent
1796
+ exactly: 1
1797
+ - step: build-config
1798
+ exactly: 1
1799
+ - step: generate-response
1800
+ exactly: 1
1801
+ prompts:
1802
+ - step: generate-response
1803
+ contains:
1804
+ - "<id>jira</id>"
1805
+ - "## Jira Tools"
1806
+ - "<id>code-explorer</id>"
1807
+ - "## Code Exploration"
1808
+ outputs:
1809
+ - step: build-config
1810
+ path: knowledge_content
1811
+ matches: "<id>jira</id>"
1812
+ - step: build-config
1813
+ path: knowledge_content
1814
+ matches: "Jira Tools"
1815
+ - step: build-config
1816
+ path: knowledge_content
1817
+ matches: "<id>code-explorer</id>"
1818
+ - step: build-config
1819
+ path: knowledge_content
1820
+ matches: "Code Exploration"
1821
+
1822
+ # =========================================================================
1823
+ # MCP Server Configuration Assertions
1824
+ # =========================================================================
1825
+ - name: mcp-server-added-when-skill-activates
1826
+ description: MCP server is configured when skill with server activates
1827
+ event: manual
1828
+ fixture: local.minimal
1829
+ workflow_input:
1830
+ question: "Get ticket TT-123"
1831
+ intents:
1832
+ - id: chat
1833
+ description: general Q&A
1834
+ skills:
1835
+ - id: jira
1836
+ description: needs Jira access
1837
+ knowledge: "Jira knowledge"
1838
+ tools:
1839
+ atlassian:
1840
+ command: uvx
1841
+ args: ["mcp-atlassian"]
1842
+ env:
1843
+ JIRA_URL: "https://jira.example.com"
1844
+ mocks:
1845
+ route-intent:
1846
+ intent: chat
1847
+ topic: "Get ticket TT-123"
1848
+ skills: ["jira"]
1849
+ generate-response:
1850
+ text: "Here is ticket TT-123."
1851
+ expect:
1852
+ calls:
1853
+ - step: route-intent
1854
+ exactly: 1
1855
+ - step: build-config
1856
+ exactly: 1
1857
+ outputs:
1858
+ - step: build-config
1859
+ path: mcp_servers.atlassian.command
1860
+ equals: "uvx"
1861
+ - step: build-config
1862
+ path: mcp_servers.atlassian.args
1863
+ equalsDeep: ["mcp-atlassian"]
1864
+ - step: build-config
1865
+ path: mcp_servers.atlassian.env.JIRA_URL
1866
+ equals: "https://jira.example.com"
1867
+
1868
+ - name: mcp-server-not-added-when-skill-inactive
1869
+ description: MCP server is NOT configured when skill is inactive
1870
+ event: manual
1871
+ fixture: local.minimal
1872
+ workflow_input:
1873
+ question: "Hello"
1874
+ intents:
1875
+ - id: chat
1876
+ description: general Q&A
1877
+ skills:
1878
+ - id: jira
1879
+ description: needs Jira access
1880
+ knowledge: "Jira knowledge"
1881
+ tools:
1882
+ atlassian:
1883
+ command: uvx
1884
+ args: ["mcp-atlassian"]
1885
+ mocks:
1886
+ route-intent:
1887
+ intent: chat
1888
+ topic: "Hello"
1889
+ skills: []
1890
+ generate-response:
1891
+ text: "Hello!"
1892
+ expect:
1893
+ calls:
1894
+ - step: route-intent
1895
+ exactly: 1
1896
+ - step: build-config
1897
+ exactly: 1
1898
+ outputs:
1899
+ - step: build-config
1900
+ path: mcp_servers
1901
+ equalsDeep: {}
1902
+
1903
+ - name: multiple-mcp-servers-from-multiple-skills
1904
+ description: Multiple MCP servers configured when multiple skills activate
1905
+ event: manual
1906
+ fixture: local.minimal
1907
+ workflow_input:
1908
+ question: "Get ticket TT-123 and check Zendesk"
1909
+ intents:
1910
+ - id: chat
1911
+ description: general Q&A
1912
+ skills:
1913
+ - id: jira
1914
+ description: needs Jira access
1915
+ tools:
1916
+ atlassian:
1917
+ command: uvx
1918
+ args: ["mcp-atlassian"]
1919
+ - id: zendesk
1920
+ description: needs Zendesk access
1921
+ tools:
1922
+ zendesk-server:
1923
+ command: uvx
1924
+ args: ["mcp-zendesk"]
1925
+ mocks:
1926
+ route-intent:
1927
+ intent: chat
1928
+ topic: "Get ticket TT-123 and check Zendesk"
1929
+ skills: ["jira", "zendesk"]
1930
+ generate-response:
1931
+ text: "Found tickets."
1932
+ expect:
1933
+ calls:
1934
+ - step: route-intent
1935
+ exactly: 1
1936
+ - step: build-config
1937
+ exactly: 1
1938
+ outputs:
1939
+ - step: build-config
1940
+ path: mcp_servers.atlassian.command
1941
+ equals: "uvx"
1942
+ - step: build-config
1943
+ path: mcp_servers.zendesk-server.command
1944
+ equals: "uvx"
1945
+
1946
+ - name: same-server-name-merges-allowed-methods
1947
+ description: Skills with same server name merge their allowedMethods
1948
+ event: manual
1949
+ fixture: local.minimal
1950
+ workflow_input:
1951
+ question: "Analyze sprint velocity for TT-123"
1952
+ intents:
1953
+ - id: chat
1954
+ description: general Q&A
1955
+ skills:
1956
+ - id: jira
1957
+ description: basic jira access
1958
+ tools:
1959
+ jira:
1960
+ command: uvx
1961
+ args: ["mcp-atlassian"]
1962
+ allowedMethods:
1963
+ - jira_get_issue
1964
+ - jira_search
1965
+ - id: jira-ext
1966
+ description: sprint analysis
1967
+ tools:
1968
+ jira:
1969
+ command: uvx
1970
+ args: ["mcp-atlassian"]
1971
+ allowedMethods:
1972
+ - jira_get_sprint_issues
1973
+ - jira_get_board_sprints
1974
+ mocks:
1975
+ route-intent:
1976
+ intent: chat
1977
+ topic: "Analyze sprint velocity"
1978
+ skills: ["jira", "jira-ext"]
1979
+ generate-response:
1980
+ text: "Sprint analysis complete."
1981
+ expect:
1982
+ calls:
1983
+ - step: route-intent
1984
+ exactly: 1
1985
+ - step: build-config
1986
+ exactly: 1
1987
+ outputs:
1988
+ # Only one server named 'jira', not two
1989
+ - step: build-config
1990
+ path: mcp_servers.jira.command
1991
+ equals: "uvx"
1992
+ # Merged allowedMethods from both skills
1993
+ - step: build-config
1994
+ path: mcp_servers.jira.allowedMethods
1995
+ equalsDeep: ["jira_get_issue", "jira_search", "jira_get_sprint_issues", "jira_get_board_sprints"]
1996
+
1997
+ - name: skill-dependency-adds-server-from-required-skill
1998
+ description: Required skill's MCP server is added via dependency expansion
1999
+ event: manual
2000
+ fixture: local.minimal
2001
+ workflow_input:
2002
+ question: "Create a ticket with jira"
2003
+ intents:
2004
+ - id: chat
2005
+ description: general Q&A
2006
+ skills:
2007
+ - id: ticket-creator
2008
+ description: create tickets
2009
+ requires: [jira]
2010
+ knowledge: "Ticket creation guide"
2011
+ - id: jira
2012
+ description: jira access
2013
+ knowledge: "Jira knowledge"
2014
+ tools:
2015
+ atlassian:
2016
+ command: uvx
2017
+ args: ["mcp-atlassian"]
2018
+ mocks:
2019
+ route-intent:
2020
+ intent: chat
2021
+ topic: "Create a ticket"
2022
+ skills: ["ticket-creator"]
2023
+ generate-response:
2024
+ text: "Created ticket."
2025
+ expect:
2026
+ calls:
2027
+ - step: route-intent
2028
+ exactly: 1
2029
+ - step: build-config
2030
+ exactly: 1
2031
+ - step: generate-response
2032
+ exactly: 1
2033
+ prompts:
2034
+ - step: generate-response
2035
+ contains:
2036
+ - "<id>ticket-creator</id>"
2037
+ - "Ticket creation guide"
2038
+ - "<id>jira</id>"
2039
+ - "Jira knowledge"
2040
+ - "<tools>atlassian</tools>"
2041
+ outputs:
2042
+ - step: build-config
2043
+ path: mcp_servers.atlassian.command
2044
+ equals: "uvx"
2045
+ - step: build-config
2046
+ path: knowledge_content
2047
+ matches: "<id>ticket-creator</id>"
2048
+ - step: build-config
2049
+ path: knowledge_content
2050
+ matches: "Ticket creation guide"
2051
+ - step: build-config
2052
+ path: knowledge_content
2053
+ matches: "<id>jira</id>"
2054
+ - step: build-config
2055
+ path: knowledge_content
2056
+ matches: "Jira knowledge"
2057
+
2058
+ # =========================================================================
2059
+ # Code Explorer Workflow Tests
2060
+ # =========================================================================
2061
+ - name: code-explorer-skill-adds-code-talk-server
2062
+ description: Skill with code-talk workflow server is added when skill activates
2063
+ event: manual
2064
+ fixture: local.minimal
2065
+ workflow_input:
2066
+ question: "How does authentication work?"
2067
+ intents:
2068
+ - id: code_help
2069
+ description: code questions
2070
+ skills:
2071
+ - id: code-explorer
2072
+ description: code exploration
2073
+ knowledge: "Use code-explorer for questions"
2074
+ tools:
2075
+ code-explorer:
2076
+ workflow: code-talk
2077
+ inputs:
2078
+ architecture: "# My Architecture"
2079
+ docs_repo: "my-org/docs"
2080
+ projects:
2081
+ - id: backend
2082
+ repo: my-org/backend
2083
+ description: Backend services
2084
+ mocks:
2085
+ route-intent:
2086
+ intent: code_help
2087
+ topic: "How does authentication work?"
2088
+ skills: ["code-explorer"]
2089
+ generate-response:
2090
+ text: "Auth is implemented in backend."
2091
+ expect:
2092
+ calls:
2093
+ - step: route-intent
2094
+ exactly: 1
2095
+ - step: build-config
2096
+ exactly: 1
2097
+ outputs:
2098
+ - step: build-config
2099
+ path: mcp_servers.code-explorer.workflow
2100
+ equals: "code-talk"
2101
+ - step: build-config
2102
+ path: mcp_servers.code-explorer.inputs.architecture
2103
+ equals: "# My Architecture"
2104
+ - step: build-config
2105
+ path: mcp_servers.code-explorer.inputs.docs_repo
2106
+ equals: "my-org/docs"
2107
+
2108
+ - name: code-explorer-server-not-added-when-skill-inactive
2109
+ description: Code explorer server not added when skill is not activated
2110
+ event: manual
2111
+ fixture: local.minimal
2112
+ workflow_input:
2113
+ question: "Hello"
2114
+ intents:
2115
+ - id: chat
2116
+ description: general Q&A
2117
+ skills:
2118
+ - id: code-explorer
2119
+ description: code exploration
2120
+ tools:
2121
+ code-explorer:
2122
+ workflow: code-talk
2123
+ inputs:
2124
+ architecture: "# My Architecture"
2125
+ mocks:
2126
+ route-intent:
2127
+ intent: chat
2128
+ topic: "Hello"
2129
+ skills: []
2130
+ generate-response:
2131
+ text: "Hello!"
2132
+ expect:
2133
+ calls:
2134
+ - step: route-intent
2135
+ exactly: 1
2136
+ - step: build-config
2137
+ exactly: 1
2138
+ outputs:
2139
+ - step: build-config
2140
+ path: mcp_servers
2141
+ equalsDeep: {}