@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.
- package/defaults/assistant.yaml +2141 -0
- package/defaults/code-talk.yaml +1250 -0
- package/defaults/intent-router.yaml +478 -0
- package/dist/defaults/assistant.yaml +2141 -0
- package/dist/defaults/code-talk.yaml +1250 -0
- package/dist/defaults/intent-router.yaml +478 -0
- package/dist/index.js +17 -4
- package/dist/output/traces/{run-2026-03-02T18-32-11-359Z.ndjson → run-2026-03-03T07-19-07-543Z.ndjson} +84 -84
- package/dist/{traces/run-2026-03-02T18-32-55-702Z.ndjson → output/traces/run-2026-03-03T07-19-50-933Z.ndjson} +1866 -1187
- package/dist/sdk/{check-provider-registry-35BPTY4W.mjs → check-provider-registry-IYSUDKPB.mjs} +7 -7
- package/dist/sdk/{check-provider-registry-DVQDGTOE.mjs → check-provider-registry-LVLC4EPF.mjs} +4 -4
- package/dist/sdk/{check-provider-registry-KHPY6LB4.mjs → check-provider-registry-X4OZJWPK.mjs} +4 -4
- package/dist/sdk/{chunk-6N6JRWCW.mjs → chunk-6EXCUX7Y.mjs} +10 -10
- package/dist/sdk/{chunk-S2YO4ZE3.mjs → chunk-BR7DYA3S.mjs} +2 -2
- package/dist/sdk/{chunk-DIND4ZCV.mjs → chunk-DNDS7R3N.mjs} +11 -1
- package/dist/sdk/{chunk-DIND4ZCV.mjs.map → chunk-DNDS7R3N.mjs.map} +1 -1
- package/dist/sdk/{chunk-IF2UD2KS.mjs → chunk-GFNXX64M.mjs} +18 -18
- package/dist/sdk/{chunk-AYQE4JCU.mjs → chunk-Q6EPAJ6Z.mjs} +3 -3
- package/dist/sdk/{chunk-H4AYMOAT.mjs → chunk-V6GI4U2M.mjs} +10 -10
- package/dist/sdk/{chunk-EGUHXVWS.mjs → chunk-VLUGLWLA.mjs} +2 -2
- package/dist/sdk/{chunk-EGUHXVWS.mjs.map → chunk-VLUGLWLA.mjs.map} +1 -1
- package/dist/sdk/{chunk-XNTBSV6M.mjs → chunk-YYZAN5NK.mjs} +3 -3
- package/dist/sdk/{config-G5UU4WXT.mjs → config-KQH254CA.mjs} +2 -2
- package/dist/sdk/{failure-condition-evaluator-I6QWFKV3.mjs → failure-condition-evaluator-LZ2AG5PY.mjs} +3 -3
- package/dist/sdk/{github-frontend-2MC77L7F.mjs → github-frontend-S523EEJB.mjs} +3 -3
- package/dist/sdk/{host-4F6I3ZXN.mjs → host-7YKRMOUJ.mjs} +2 -2
- package/dist/sdk/{routing-UT3BXBXH.mjs → routing-ZMBKWMVI.mjs} +4 -4
- package/dist/sdk/{schedule-tool-K3GQXCBN.mjs → schedule-tool-CDVUSZEG.mjs} +7 -7
- package/dist/sdk/{schedule-tool-SBXAEBDD.mjs → schedule-tool-EOMZFICZ.mjs} +4 -4
- package/dist/sdk/{schedule-tool-CONR4VW3.mjs → schedule-tool-NX75VKGA.mjs} +4 -4
- package/dist/sdk/{schedule-tool-handler-GFQCJAVZ.mjs → schedule-tool-handler-3FJHDIPG.mjs} +7 -7
- package/dist/sdk/{schedule-tool-handler-R7PG3VMR.mjs → schedule-tool-handler-KKN7XJYT.mjs} +4 -4
- package/dist/sdk/{schedule-tool-handler-YUC6CAXX.mjs → schedule-tool-handler-QNZG55DX.mjs} +4 -4
- package/dist/sdk/sdk.js +11 -1
- package/dist/sdk/sdk.js.map +1 -1
- package/dist/sdk/sdk.mjs +6 -6
- package/dist/sdk/{trace-helpers-J463EU4B.mjs → trace-helpers-EJUIOP6L.mjs} +2 -2
- package/dist/sdk/{workflow-check-provider-GJNGTS3F.mjs → workflow-check-provider-6ERNNCNA.mjs} +7 -7
- package/dist/sdk/{workflow-check-provider-DYSO3PML.mjs → workflow-check-provider-AGZ5JY2I.mjs} +4 -4
- package/dist/sdk/{workflow-check-provider-FIFFQDQU.mjs → workflow-check-provider-PTNUWM5W.mjs} +4 -4
- package/dist/sdk/{workflow-registry-AAD37XKZ.mjs → workflow-registry-MHUSKSD6.mjs} +2 -2
- package/dist/traces/{run-2026-03-02T18-32-11-359Z.ndjson → run-2026-03-03T07-19-07-543Z.ndjson} +84 -84
- package/dist/{output/traces/run-2026-03-02T18-32-55-702Z.ndjson → traces/run-2026-03-03T07-19-50-933Z.ndjson} +1866 -1187
- package/dist/workflow-registry.d.ts.map +1 -1
- package/package.json +1 -1
- /package/dist/sdk/{check-provider-registry-35BPTY4W.mjs.map → check-provider-registry-IYSUDKPB.mjs.map} +0 -0
- /package/dist/sdk/{check-provider-registry-DVQDGTOE.mjs.map → check-provider-registry-LVLC4EPF.mjs.map} +0 -0
- /package/dist/sdk/{check-provider-registry-KHPY6LB4.mjs.map → check-provider-registry-X4OZJWPK.mjs.map} +0 -0
- /package/dist/sdk/{chunk-6N6JRWCW.mjs.map → chunk-6EXCUX7Y.mjs.map} +0 -0
- /package/dist/sdk/{chunk-S2YO4ZE3.mjs.map → chunk-BR7DYA3S.mjs.map} +0 -0
- /package/dist/sdk/{chunk-IF2UD2KS.mjs.map → chunk-GFNXX64M.mjs.map} +0 -0
- /package/dist/sdk/{chunk-AYQE4JCU.mjs.map → chunk-Q6EPAJ6Z.mjs.map} +0 -0
- /package/dist/sdk/{chunk-H4AYMOAT.mjs.map → chunk-V6GI4U2M.mjs.map} +0 -0
- /package/dist/sdk/{chunk-XNTBSV6M.mjs.map → chunk-YYZAN5NK.mjs.map} +0 -0
- /package/dist/sdk/{config-G5UU4WXT.mjs.map → config-KQH254CA.mjs.map} +0 -0
- /package/dist/sdk/{failure-condition-evaluator-I6QWFKV3.mjs.map → failure-condition-evaluator-LZ2AG5PY.mjs.map} +0 -0
- /package/dist/sdk/{github-frontend-2MC77L7F.mjs.map → github-frontend-S523EEJB.mjs.map} +0 -0
- /package/dist/sdk/{host-4F6I3ZXN.mjs.map → host-7YKRMOUJ.mjs.map} +0 -0
- /package/dist/sdk/{routing-UT3BXBXH.mjs.map → routing-ZMBKWMVI.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-CONR4VW3.mjs.map → schedule-tool-CDVUSZEG.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-K3GQXCBN.mjs.map → schedule-tool-EOMZFICZ.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-SBXAEBDD.mjs.map → schedule-tool-NX75VKGA.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-handler-GFQCJAVZ.mjs.map → schedule-tool-handler-3FJHDIPG.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-handler-R7PG3VMR.mjs.map → schedule-tool-handler-KKN7XJYT.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-handler-YUC6CAXX.mjs.map → schedule-tool-handler-QNZG55DX.mjs.map} +0 -0
- /package/dist/sdk/{trace-helpers-J463EU4B.mjs.map → trace-helpers-EJUIOP6L.mjs.map} +0 -0
- /package/dist/sdk/{workflow-check-provider-DYSO3PML.mjs.map → workflow-check-provider-6ERNNCNA.mjs.map} +0 -0
- /package/dist/sdk/{workflow-check-provider-FIFFQDQU.mjs.map → workflow-check-provider-AGZ5JY2I.mjs.map} +0 -0
- /package/dist/sdk/{workflow-check-provider-GJNGTS3F.mjs.map → workflow-check-provider-PTNUWM5W.mjs.map} +0 -0
- /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: {}
|