@probelabs/visor 0.1.179-ee → 0.1.180-ee

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 (175) hide show
  1. package/README.md +6 -4
  2. package/defaults/assistant.yaml +29 -1
  3. package/defaults/code-talk.yaml +331 -441
  4. package/defaults/engineer.yaml +608 -0
  5. package/defaults/project-setup.yaml +489 -0
  6. package/defaults/skills/engineer.yaml +41 -0
  7. package/dist/{560.index.js → 736.index.js} +359 -3132
  8. package/dist/agent-protocol/task-store.d.ts +3 -0
  9. package/dist/agent-protocol/task-store.d.ts.map +1 -1
  10. package/dist/agent-protocol/track-execution.d.ts.map +1 -1
  11. package/dist/ai-review-service.d.ts +6 -0
  12. package/dist/ai-review-service.d.ts.map +1 -1
  13. package/dist/cli-main.d.ts.map +1 -1
  14. package/dist/cli.d.ts +5 -0
  15. package/dist/cli.d.ts.map +1 -1
  16. package/dist/config.d.ts.map +1 -1
  17. package/dist/defaults/assistant.yaml +29 -1
  18. package/dist/defaults/code-talk.yaml +331 -441
  19. package/dist/defaults/engineer.yaml +608 -0
  20. package/dist/defaults/project-setup.yaml +489 -0
  21. package/dist/defaults/skills/engineer.yaml +41 -0
  22. package/dist/docs/advanced-ai.md +39 -0
  23. package/dist/docs/ai-configuration.md +32 -0
  24. package/dist/docs/ai-custom-tools.md +25 -0
  25. package/dist/docs/architecture.md +3 -0
  26. package/dist/docs/assistant-workflows.md +395 -25
  27. package/dist/docs/configuration.md +2 -1
  28. package/dist/docs/glossary.md +27 -1
  29. package/dist/docs/guides/build-ai-agent.md +4 -0
  30. package/dist/docs/mcp.md +26 -0
  31. package/dist/docs/migration.md +2 -0
  32. package/dist/docs/pluggable.md +28 -1
  33. package/dist/docs/telemetry-reference.md +41 -0
  34. package/dist/docs/timeouts.md +86 -1
  35. package/dist/docs/utcp-provider.md +343 -0
  36. package/dist/docs/workflows.md +30 -0
  37. package/dist/examples/negotiated-timeout.yaml +65 -0
  38. package/dist/examples/screenshot-tool.yaml +100 -0
  39. package/dist/examples/utcp-provider-example.yaml +290 -0
  40. package/dist/examples/workflows/helper-workflow.yaml +15 -0
  41. package/dist/frontends/slack-frontend.d.ts.map +1 -1
  42. package/dist/generated/config-schema.d.ts +75 -8
  43. package/dist/generated/config-schema.d.ts.map +1 -1
  44. package/dist/generated/config-schema.json +86 -8
  45. package/dist/index.js +42497 -33912
  46. package/dist/providers/ai-check-provider.d.ts.map +1 -1
  47. package/dist/providers/check-provider-registry.d.ts.map +1 -1
  48. package/dist/providers/check-provider.interface.d.ts +6 -0
  49. package/dist/providers/check-provider.interface.d.ts.map +1 -1
  50. package/dist/providers/http-client-provider.d.ts.map +1 -1
  51. package/dist/providers/index.d.ts +1 -0
  52. package/dist/providers/index.d.ts.map +1 -1
  53. package/dist/providers/mcp-check-provider.d.ts +0 -8
  54. package/dist/providers/mcp-check-provider.d.ts.map +1 -1
  55. package/dist/providers/mcp-custom-sse-server.d.ts +5 -0
  56. package/dist/providers/mcp-custom-sse-server.d.ts.map +1 -1
  57. package/dist/providers/utcp-check-provider.d.ts +81 -0
  58. package/dist/providers/utcp-check-provider.d.ts.map +1 -0
  59. package/dist/providers/workflow-check-provider.d.ts.map +1 -1
  60. package/dist/sandbox/check-runner.d.ts +1 -1
  61. package/dist/sandbox/check-runner.d.ts.map +1 -1
  62. package/dist/sandbox/compose-generator.d.ts +45 -0
  63. package/dist/sandbox/compose-generator.d.ts.map +1 -0
  64. package/dist/sandbox/index.d.ts +2 -1
  65. package/dist/sandbox/index.d.ts.map +1 -1
  66. package/dist/sandbox/sandbox-manager.d.ts +21 -1
  67. package/dist/sandbox/sandbox-manager.d.ts.map +1 -1
  68. package/dist/sandbox/types.d.ts +40 -0
  69. package/dist/sandbox/types.d.ts.map +1 -1
  70. package/dist/sdk/{a2a-frontend-KJFLIZJT.mjs → a2a-frontend-6FNPD53G.mjs} +20 -2
  71. package/dist/sdk/a2a-frontend-6FNPD53G.mjs.map +1 -0
  72. package/dist/sdk/{check-provider-registry-SYAHJMWJ.mjs → check-provider-registry-T4HIUBMT.mjs} +8 -7
  73. package/dist/sdk/{check-provider-registry-J27YX4IT.mjs → check-provider-registry-Z4MVXFLJ.mjs} +9 -8
  74. package/dist/sdk/{chunk-ZJYQMNPA.mjs → chunk-3ZKBUWDB.mjs} +3 -3
  75. package/dist/sdk/{chunk-OYHDBTKY.mjs → chunk-5DQY4LTK.mjs} +2 -2
  76. package/dist/sdk/chunk-ANCIFGQH.mjs +825 -0
  77. package/dist/sdk/chunk-ANCIFGQH.mjs.map +1 -0
  78. package/dist/sdk/chunk-F25U4YWJ.mjs +825 -0
  79. package/dist/sdk/chunk-F25U4YWJ.mjs.map +1 -0
  80. package/dist/sdk/{chunk-UBTZE3FO.mjs → chunk-J27D43HS.mjs} +51 -8
  81. package/dist/sdk/chunk-J27D43HS.mjs.map +1 -0
  82. package/dist/sdk/{chunk-BMXVAJ2M.mjs → chunk-KAVOGMLR.mjs} +90 -15
  83. package/dist/sdk/chunk-KAVOGMLR.mjs.map +1 -0
  84. package/dist/sdk/{chunk-FTPLYUQ3.mjs → chunk-MWUQFSEK.mjs} +6981 -6461
  85. package/dist/sdk/chunk-MWUQFSEK.mjs.map +1 -0
  86. package/dist/sdk/{chunk-CHARL3TY.mjs → chunk-NBUN22ZG.mjs} +12 -2
  87. package/dist/sdk/chunk-NBUN22ZG.mjs.map +1 -0
  88. package/dist/sdk/{chunk-KWHLB5E3.mjs → chunk-STAAKOPU.mjs} +6516 -5996
  89. package/dist/sdk/chunk-STAAKOPU.mjs.map +1 -0
  90. package/dist/sdk/{config-DFOF7LP4.mjs → config-ZZKC47SV.mjs} +2 -2
  91. package/dist/sdk/dist-X4HSBKSA.mjs +5716 -0
  92. package/dist/sdk/dist-X4HSBKSA.mjs.map +1 -0
  93. package/dist/sdk/dist-XF2FTM6E.mjs +5716 -0
  94. package/dist/sdk/dist-XF2FTM6E.mjs.map +1 -0
  95. package/dist/sdk/{failure-condition-evaluator-V2YGFRKO.mjs → failure-condition-evaluator-WNCCIP6N.mjs} +3 -3
  96. package/dist/sdk/{github-frontend-4LM4NAZK.mjs → github-frontend-QVMVUL3Y.mjs} +3 -3
  97. package/dist/sdk/{host-GBXJKNHL.mjs → host-5TJBWGGH.mjs} +4 -4
  98. package/dist/sdk/{host-XXPPPC76.mjs → host-RVLIZMTE.mjs} +4 -4
  99. package/dist/sdk/{loader-Q7K76ZIY.mjs → loader-ZNKKJEZ3.mjs} +1 -1
  100. package/dist/sdk/{routing-YAYBIVPL.mjs → routing-5DHNS7IW.mjs} +4 -4
  101. package/dist/sdk/{schedule-tool-OIVJDIDK.mjs → schedule-tool-25CFKVOI.mjs} +8 -7
  102. package/dist/sdk/{schedule-tool-WACIV77L.mjs → schedule-tool-UGWYLGJK.mjs} +9 -8
  103. package/dist/sdk/{schedule-tool-handler-ODKY57FO.mjs → schedule-tool-handler-VWBX4JEF.mjs} +8 -7
  104. package/dist/sdk/{schedule-tool-handler-SJF4ZKSB.mjs → schedule-tool-handler-WK22RO3M.mjs} +9 -8
  105. package/dist/sdk/sdk.d.mts +42 -2
  106. package/dist/sdk/sdk.d.ts +42 -2
  107. package/dist/sdk/sdk.js +28250 -21090
  108. package/dist/sdk/sdk.js.map +1 -1
  109. package/dist/sdk/sdk.mjs +8 -7
  110. package/dist/sdk/sdk.mjs.map +1 -1
  111. package/dist/sdk/{slack-frontend-OWD7BSWF.mjs → slack-frontend-BPWXNIHE.mjs} +3 -3
  112. package/dist/sdk/slack-frontend-BPWXNIHE.mjs.map +1 -0
  113. package/dist/sdk/{trace-helpers-QL2B75AK.mjs → trace-helpers-GCLQ3YKO.mjs} +2 -2
  114. package/dist/sdk/{track-execution-2Q66SXBZ.mjs → track-execution-GCGJKMAO.mjs} +6 -4
  115. package/dist/sdk/track-execution-GCGJKMAO.mjs.map +1 -0
  116. package/dist/sdk/utcp-check-provider-6YTBN5WQ.mjs +16 -0
  117. package/dist/sdk/utcp-check-provider-HT2MA5SS.mjs +16 -0
  118. package/dist/sdk/{workflow-check-provider-IXW6BMQA.mjs → workflow-check-provider-I732XE7D.mjs} +8 -7
  119. package/dist/sdk/{workflow-check-provider-UZQZYPOE.mjs → workflow-check-provider-T4PFK2EG.mjs} +9 -8
  120. package/dist/sdk/workflow-check-provider-T4PFK2EG.mjs.map +1 -0
  121. package/dist/sdk/{workflow-registry-LRSRWUM5.mjs → workflow-registry-X2IPY35M.mjs} +2 -2
  122. package/dist/sdk/workflow-registry-X2IPY35M.mjs.map +1 -0
  123. package/dist/slack/adapter.d.ts +8 -0
  124. package/dist/slack/adapter.d.ts.map +1 -1
  125. package/dist/slack/client.d.ts +1 -0
  126. package/dist/slack/client.d.ts.map +1 -1
  127. package/dist/slack/socket-runner.d.ts +1 -0
  128. package/dist/slack/socket-runner.d.ts.map +1 -1
  129. package/dist/state-machine/dispatch/execution-invoker.d.ts.map +1 -1
  130. package/dist/state-machine/dispatch/sandbox-routing.d.ts +25 -1
  131. package/dist/state-machine/dispatch/sandbox-routing.d.ts.map +1 -1
  132. package/dist/state-machine-execution-engine.d.ts.map +1 -1
  133. package/dist/telemetry/opentelemetry.d.ts +6 -0
  134. package/dist/telemetry/opentelemetry.d.ts.map +1 -1
  135. package/dist/test-runner/core/flow-stage.d.ts +1 -2
  136. package/dist/test-runner/core/flow-stage.d.ts.map +1 -1
  137. package/dist/test-runner/index.d.ts +0 -3
  138. package/dist/test-runner/index.d.ts.map +1 -1
  139. package/dist/test-runner/validator.d.ts.map +1 -1
  140. package/dist/types/config.d.ts +36 -2
  141. package/dist/types/config.d.ts.map +1 -1
  142. package/dist/utils/issue-normalizer.d.ts +27 -0
  143. package/dist/utils/issue-normalizer.d.ts.map +1 -0
  144. package/dist/utils/worktree-manager.d.ts +9 -2
  145. package/dist/utils/worktree-manager.d.ts.map +1 -1
  146. package/dist/workflow-registry.d.ts +5 -0
  147. package/dist/workflow-registry.d.ts.map +1 -1
  148. package/package.json +12 -2
  149. package/dist/sdk/a2a-frontend-KJFLIZJT.mjs.map +0 -1
  150. package/dist/sdk/chunk-BMXVAJ2M.mjs.map +0 -1
  151. package/dist/sdk/chunk-CHARL3TY.mjs.map +0 -1
  152. package/dist/sdk/chunk-FTPLYUQ3.mjs.map +0 -1
  153. package/dist/sdk/chunk-KWHLB5E3.mjs.map +0 -1
  154. package/dist/sdk/chunk-UBTZE3FO.mjs.map +0 -1
  155. package/dist/sdk/slack-frontend-OWD7BSWF.mjs.map +0 -1
  156. package/dist/sdk/track-execution-2Q66SXBZ.mjs.map +0 -1
  157. /package/dist/sdk/{check-provider-registry-J27YX4IT.mjs.map → check-provider-registry-T4HIUBMT.mjs.map} +0 -0
  158. /package/dist/sdk/{check-provider-registry-SYAHJMWJ.mjs.map → check-provider-registry-Z4MVXFLJ.mjs.map} +0 -0
  159. /package/dist/sdk/{chunk-ZJYQMNPA.mjs.map → chunk-3ZKBUWDB.mjs.map} +0 -0
  160. /package/dist/sdk/{chunk-OYHDBTKY.mjs.map → chunk-5DQY4LTK.mjs.map} +0 -0
  161. /package/dist/sdk/{config-DFOF7LP4.mjs.map → config-ZZKC47SV.mjs.map} +0 -0
  162. /package/dist/sdk/{failure-condition-evaluator-V2YGFRKO.mjs.map → failure-condition-evaluator-WNCCIP6N.mjs.map} +0 -0
  163. /package/dist/sdk/{github-frontend-4LM4NAZK.mjs.map → github-frontend-QVMVUL3Y.mjs.map} +0 -0
  164. /package/dist/sdk/{host-GBXJKNHL.mjs.map → host-5TJBWGGH.mjs.map} +0 -0
  165. /package/dist/sdk/{host-XXPPPC76.mjs.map → host-RVLIZMTE.mjs.map} +0 -0
  166. /package/dist/sdk/{loader-Q7K76ZIY.mjs.map → loader-ZNKKJEZ3.mjs.map} +0 -0
  167. /package/dist/sdk/{routing-YAYBIVPL.mjs.map → routing-5DHNS7IW.mjs.map} +0 -0
  168. /package/dist/sdk/{schedule-tool-OIVJDIDK.mjs.map → schedule-tool-25CFKVOI.mjs.map} +0 -0
  169. /package/dist/sdk/{schedule-tool-WACIV77L.mjs.map → schedule-tool-UGWYLGJK.mjs.map} +0 -0
  170. /package/dist/sdk/{schedule-tool-handler-ODKY57FO.mjs.map → schedule-tool-handler-VWBX4JEF.mjs.map} +0 -0
  171. /package/dist/sdk/{schedule-tool-handler-SJF4ZKSB.mjs.map → schedule-tool-handler-WK22RO3M.mjs.map} +0 -0
  172. /package/dist/sdk/{trace-helpers-QL2B75AK.mjs.map → trace-helpers-GCLQ3YKO.mjs.map} +0 -0
  173. /package/dist/sdk/{workflow-check-provider-IXW6BMQA.mjs.map → utcp-check-provider-6YTBN5WQ.mjs.map} +0 -0
  174. /package/dist/sdk/{workflow-check-provider-UZQZYPOE.mjs.map → utcp-check-provider-HT2MA5SS.mjs.map} +0 -0
  175. /package/dist/sdk/{workflow-registry-LRSRWUM5.mjs.map → workflow-check-provider-I732XE7D.mjs.map} +0 -0
@@ -0,0 +1,489 @@
1
+ # =============================================================================
2
+ # project-setup: Shared project routing and checkout workflow
3
+ # =============================================================================
4
+ #
5
+ # Reusable workflow that handles:
6
+ # 1. Docs checkout
7
+ # 2. AI-based project routing (or pre-selected projects)
8
+ # 3. Project checkout with metadata (services, setup, knowledge)
9
+ #
10
+ # Used by code-talk and engineer workflows.
11
+ #
12
+ # Usage:
13
+ # imports:
14
+ # - ./project-setup.yaml
15
+ #
16
+ # checks:
17
+ # setup:
18
+ # type: workflow
19
+ # workflow: project-setup
20
+ # args:
21
+ # question: "How does auth work?"
22
+ # architecture: ./ARCHITECTURE.md
23
+ # docs_repo: my-org/docs
24
+ # projects:
25
+ # - id: backend
26
+ # repo: my-org/backend
27
+ # description: Backend API services
28
+ #
29
+ # =============================================================================
30
+
31
+ id: project-setup
32
+ name: Project Setup
33
+ description: Shared project routing and checkout with metadata propagation
34
+ version: "1.0.0"
35
+
36
+ inputs:
37
+ - name: question
38
+ required: true
39
+ description: The question or task driving project selection
40
+ schema:
41
+ type: string
42
+
43
+ - name: architecture
44
+ required: true
45
+ description: |
46
+ Architecture model - file path or inline markdown describing
47
+ project topology and routing rules
48
+ schema:
49
+ type: string
50
+
51
+ - name: docs_repo
52
+ required: true
53
+ description: Documentation repository (owner/name format)
54
+ schema:
55
+ type: string
56
+
57
+ - name: projects
58
+ required: true
59
+ description: |
60
+ Array of available code projects for AI routing:
61
+ - id: unique identifier for routing
62
+ - repo: GitHub repository (owner/name)
63
+ - description: used by AI for routing decisions
64
+ schema:
65
+ type: array
66
+ items:
67
+ type: object
68
+ properties:
69
+ id: { type: string }
70
+ repo: { type: string }
71
+ description: { type: string }
72
+ sandbox: { type: string }
73
+ services:
74
+ type: object
75
+ additionalProperties:
76
+ type: object
77
+ properties:
78
+ image: { type: string }
79
+ ports:
80
+ type: array
81
+ items: { type: number }
82
+ environment:
83
+ type: object
84
+ additionalProperties: { type: string }
85
+ volumes:
86
+ type: array
87
+ items: { type: string }
88
+ healthcheck:
89
+ type: object
90
+ properties:
91
+ test:
92
+ type: array
93
+ items: { type: string }
94
+ interval: { type: string }
95
+ timeout: { type: string }
96
+ retries: { type: number }
97
+ required: [image]
98
+ setup:
99
+ type: array
100
+ items: { type: string }
101
+ knowledge: { type: string }
102
+ required: [id, repo, description]
103
+
104
+ - name: max_projects
105
+ default: 3
106
+ description: Maximum code projects to checkout (excludes docs)
107
+ schema:
108
+ type: number
109
+
110
+ - name: docs_ref
111
+ default: main
112
+ description: Git ref for docs repository
113
+ schema:
114
+ type: string
115
+
116
+ - name: routing_prompt
117
+ required: false
118
+ description: Additional routing instructions (appended to built-in)
119
+ schema:
120
+ type: string
121
+
122
+ - name: selected_projects
123
+ required: false
124
+ description: |
125
+ Pre-selected project IDs to skip AI routing. When provided,
126
+ route-projects is skipped and these projects are used directly.
127
+ default: []
128
+ schema:
129
+ type: array
130
+ items:
131
+ type: string
132
+
133
+ outputs:
134
+ - name: docs_path
135
+ description: Path where docs repo was checked out
136
+ value_js: |
137
+ return outputs?.['checkout-docs']?.path ?? null;
138
+
139
+ - name: project_items
140
+ description: Routed project descriptors with metadata
141
+ value_js: |
142
+ const historyItems = outputs?.history?.['project-items'];
143
+ const lastItems = Array.isArray(historyItems) ? historyItems[historyItems.length - 1] : historyItems;
144
+ return Array.isArray(lastItems) ? lastItems : (Array.isArray(outputs?.['project-items']) ? outputs['project-items'] : []);
145
+
146
+ - name: routing_decision
147
+ description: Raw routing output from AI (or null if pre-selected)
148
+ value_js: |
149
+ return outputs?.['route-projects'] ?? null;
150
+
151
+ - name: checkout_projects
152
+ description: Checked-out project paths aligned with routed projects
153
+ value_js: |
154
+ const historyItems = outputs?.history?.['project-items'];
155
+ const lastHistory = Array.isArray(historyItems) ? historyItems[historyItems.length - 1] : historyItems;
156
+ const items = Array.isArray(outputs?.['project-items'])
157
+ ? outputs['project-items']
158
+ : Array.isArray(lastHistory)
159
+ ? lastHistory
160
+ : [];
161
+
162
+ const checkoutOutput = outputs?.['checkout-projects'];
163
+ const checkouts = Array.isArray(checkoutOutput?.forEachItems)
164
+ ? checkoutOutput.forEachItems
165
+ : Array.isArray(checkoutOutput)
166
+ ? checkoutOutput
167
+ : [];
168
+
169
+ const result = [];
170
+ if (!Array.isArray(items) || !Array.isArray(checkouts)) return result;
171
+
172
+ for (let i = 0; i < items.length; i++) {
173
+ const item = items[i];
174
+ if (!item?.project_id) continue;
175
+ const checkout = checkouts[i] ?? {};
176
+ result.push({
177
+ project_id: item.project_id,
178
+ repository: item.repository ?? item.repo ?? '',
179
+ description: item.description ?? '',
180
+ path: checkout.path ?? checkout.workspace_path ?? '',
181
+ sandbox: item.sandbox ?? null,
182
+ services: item.services ?? null,
183
+ setup: item.setup ?? null,
184
+ knowledge: item.knowledge ?? null
185
+ });
186
+ }
187
+ return result;
188
+
189
+ steps:
190
+ # ===========================================================================
191
+ # Step 1: Checkout documentation (always first)
192
+ # ===========================================================================
193
+ checkout-docs:
194
+ type: git-checkout
195
+ criticality: internal
196
+ assume:
197
+ - "true"
198
+ repository: "{{ inputs.docs_repo }}"
199
+ ref: "{{ inputs.docs_ref }}"
200
+ fetch_depth: 1
201
+ description: "Documentation"
202
+
203
+ # ===========================================================================
204
+ # Step 2: Route to relevant projects (AI-based)
205
+ # Skipped when selected_projects are provided.
206
+ # ===========================================================================
207
+ route-projects:
208
+ type: ai
209
+ criticality: internal
210
+ depends_on: [checkout-docs]
211
+ continue_on_failure: true
212
+ if: "!inputs.selected_projects || inputs.selected_projects.length === 0"
213
+ assume:
214
+ - "true"
215
+ guarantee: "(output?.projects?.length ?? 0) > 0 || (output?.notes?.length ?? 0) > 0"
216
+ fail_if: "(output?.projects?.length ?? 0) === 0 && !(output?.notes?.length > 0)"
217
+ ai:
218
+ skip_code_context: true
219
+ prompt_type: general
220
+ system_prompt: |
221
+ <role>
222
+ You are a project routing planner. Given a question, you decide which
223
+ code repositories need to be queried. You do NOT answer questions
224
+ directly - you only plan which projects to consult.
225
+ </role>
226
+
227
+ <context_handling>
228
+ The full conversation context (e.g., Slack thread) may be available in
229
+ <slack_context>. Use it to understand the real question. If the latest
230
+ message is a correction, reconstruct the original question from earlier
231
+ messages. Questions may include appended ticket/page context.
232
+ </context_handling>
233
+
234
+ <architecture>
235
+ {{ inputs.architecture }}
236
+ </architecture>
237
+
238
+ <docs_context>
239
+ The documentation has been checked out and is available for reference.
240
+ Use it to understand feature behavior, configuration options, and
241
+ cross-component interactions before deciding which code projects to query.
242
+ Path: {{ outputs['checkout-docs'].path }}
243
+ </docs_context>
244
+
245
+ <available_projects>
246
+ {% for p in inputs.projects %}
247
+ - id: {{ p.id }}
248
+ repo: {{ p.repo }}
249
+ description: {{ p.description }}
250
+ {% endfor %}
251
+ </available_projects>
252
+ schema: |
253
+ {
254
+ "type": "object",
255
+ "additionalProperties": false,
256
+ "properties": {
257
+ "projects": {
258
+ "type": "array",
259
+ "description": "Projects to query for this question",
260
+ "items": {
261
+ "type": "object",
262
+ "additionalProperties": false,
263
+ "properties": {
264
+ "project_id": {
265
+ "type": "string",
266
+ "description": "One of the available project IDs",
267
+ "enum": [{% for p in inputs.projects %}"{{ p.id }}"{% unless forloop.last %}, {% endunless %}{% endfor %}]
268
+ },
269
+ "reason": {
270
+ "type": "string",
271
+ "description": "Brief explanation of why this project is relevant"
272
+ }
273
+ },
274
+ "required": ["project_id"]
275
+ }
276
+ },
277
+ "notes": {
278
+ "type": "string",
279
+ "description": "Optional planner notes"
280
+ }
281
+ },
282
+ "required": ["projects"]
283
+ }
284
+ prompt: |
285
+ <question>{{ inputs.question }}</question>
286
+
287
+ <task>
288
+ Determine which projects are needed to answer this question.
289
+ Consider all parts of the stack that the functionality touches, then
290
+ select the smallest set that covers the behavior.
291
+
292
+ Return a "projects" array with:
293
+ - "project_id": from the available project IDs listed above
294
+ - "reason" (optional): brief explanation of relevance
295
+ - If you cannot determine the relevant projects from the context,
296
+ return an empty "projects" array and include a "notes" string
297
+ asking for the missing details you need.
298
+
299
+ Routing guidelines:
300
+ - When functionality spans multiple components, include ALL affected projects
301
+ - It's better to include a small superset than to miss a relevant project
302
+ - Typical plans contain 1-3 projects, but include more when necessary
303
+ - Consider how data/config flows between services
304
+ - Maximum projects: {{ inputs.max_projects }}
305
+ </task>
306
+
307
+ {% if inputs.routing_prompt %}
308
+ <additional_routing_rules>
309
+ {{ inputs.routing_prompt }}
310
+ </additional_routing_rules>
311
+ {% endif %}
312
+
313
+ # ===========================================================================
314
+ # Step 3: Map routed projects to checkout descriptors
315
+ # Uses AI routing output when available, falls back to selected_projects.
316
+ # Depends on checkout-docs (not route-projects) to avoid skip cascade.
317
+ # ===========================================================================
318
+ project-items:
319
+ type: script
320
+ criticality: internal
321
+ depends_on: [checkout-docs, route-projects]
322
+ assume:
323
+ - "true"
324
+ guarantee: "Array.isArray(output)"
325
+ schema:
326
+ type: array
327
+ content: |
328
+ // Build a map of project ID -> project info from inputs
329
+ const projectMap = {};
330
+ const inputProjects = Array.isArray(inputs?.projects) ? inputs.projects : [];
331
+ for (let i = 0; i < inputProjects.length; i++) {
332
+ const p = inputProjects[i];
333
+ if (p?.id) {
334
+ projectMap[p.id] = {
335
+ repo: p.repo,
336
+ description: p.description ?? p.id,
337
+ sandbox: p.sandbox ?? null,
338
+ services: p.services ?? null,
339
+ setup: p.setup ?? null,
340
+ knowledge: p.knowledge ?? null
341
+ };
342
+ }
343
+ }
344
+
345
+ // Determine routed projects: AI output or pre-selected
346
+ let routedProjects = [];
347
+ const routeOutput = outputs?.['route-projects'];
348
+ if (routeOutput && Array.isArray(routeOutput.projects)) {
349
+ routedProjects = routeOutput.projects;
350
+ } else {
351
+ // Fall back to selected_projects input
352
+ const selected = Array.isArray(inputs?.selected_projects) ? inputs.selected_projects : [];
353
+ for (let s = 0; s < selected.length; s++) {
354
+ routedProjects.push({ project_id: selected[s], reason: 'pre-selected' });
355
+ }
356
+ }
357
+
358
+ // Map to checkout descriptors
359
+ const result = [];
360
+ const maxProjects = inputs?.max_projects ?? 3;
361
+
362
+ for (let j = 0; j < routedProjects.length && result.length < maxProjects; j++) {
363
+ const routed = routedProjects[j];
364
+ if (!routed?.project_id) continue;
365
+
366
+ const info = projectMap[routed.project_id];
367
+ // Skip unknown project IDs (not in inputs.projects)
368
+ if (!info?.repo) continue;
369
+
370
+ result.push({
371
+ project_id: routed.project_id,
372
+ reason: routed.reason ?? '',
373
+ repository: info.repo,
374
+ description: info.description,
375
+ sandbox: info.sandbox,
376
+ services: info.services,
377
+ setup: info.setup,
378
+ knowledge: info.knowledge
379
+ });
380
+ }
381
+
382
+ return result;
383
+ forEach: true
384
+
385
+ # ===========================================================================
386
+ # Step 4: Checkout each selected project
387
+ # ===========================================================================
388
+ checkout-projects:
389
+ type: git-checkout
390
+ criticality: internal
391
+ depends_on: [project-items]
392
+ assume:
393
+ - "output != null"
394
+ repository: "{{ outputs['project-items'].repository }}"
395
+ ref: main
396
+ fetch_depth: 1
397
+ description: "{{ outputs['project-items'].description }}"
398
+
399
+ # =============================================================================
400
+ # Tests
401
+ # =============================================================================
402
+ tests:
403
+ defaults:
404
+ strict: true
405
+ ai_provider: mock
406
+
407
+ cases:
408
+ - name: basic-routing
409
+ event: manual
410
+ fixture: local.minimal
411
+ workflow_input:
412
+ question: "How does authentication work?"
413
+ architecture: |
414
+ # Architecture
415
+ ## Projects
416
+ - gateway: API Gateway
417
+ - backend: Backend services
418
+ docs_repo: org/docs
419
+ projects:
420
+ - id: gateway
421
+ repo: org/gateway
422
+ description: API Gateway
423
+ - id: backend
424
+ repo: org/backend
425
+ description: Backend services
426
+ mocks:
427
+ checkout-docs:
428
+ path: "/tmp/visor/docs"
429
+ repository: "org/docs"
430
+ ref: "main"
431
+ checkout-projects:
432
+ path: "/tmp/visor/project"
433
+ repository: "org/gateway"
434
+ ref: "main"
435
+ route-projects:
436
+ projects:
437
+ - project_id: "gateway"
438
+ reason: "Gateway handles authentication"
439
+ expect:
440
+ calls:
441
+ - step: checkout-docs
442
+ exactly: 1
443
+ - step: route-projects
444
+ exactly: 1
445
+ - step: project-items
446
+ exactly: 1
447
+ - step: checkout-projects
448
+ exactly: 1
449
+ prompts:
450
+ - step: route-projects
451
+ contains:
452
+ - "How does authentication work?"
453
+ - "Maximum projects:"
454
+
455
+ - name: pre-selected-projects
456
+ event: manual
457
+ fixture: local.minimal
458
+ workflow_input:
459
+ question: "Fix the auth bug"
460
+ architecture: "# Arch"
461
+ docs_repo: org/docs
462
+ projects:
463
+ - id: gateway
464
+ repo: org/gateway
465
+ description: API Gateway
466
+ - id: backend
467
+ repo: org/backend
468
+ description: Backend services
469
+ selected_projects:
470
+ - gateway
471
+ mocks:
472
+ checkout-docs:
473
+ path: "/tmp/visor/docs"
474
+ repository: "org/docs"
475
+ ref: "main"
476
+ checkout-projects:
477
+ path: "/tmp/visor/project"
478
+ repository: "org/gateway"
479
+ ref: "main"
480
+ expect:
481
+ calls:
482
+ - step: checkout-docs
483
+ exactly: 1
484
+ - step: route-projects
485
+ exactly: 0
486
+ - step: project-items
487
+ exactly: 1
488
+ - step: checkout-projects
489
+ exactly: 1
@@ -0,0 +1,41 @@
1
+ # =============================================================================
2
+ # Default engineer skill shipped with visor
3
+ # =============================================================================
4
+ #
5
+ # Provides a ready-to-use code modification capability. Users only need to
6
+ # provide project-specific config (repos, architecture) via extends:
7
+ #
8
+ # - extends: "visor://skills/engineer.yaml"
9
+ # tools:
10
+ # engineer:
11
+ # inputs:
12
+ # expression: "loadConfig('config/my-projects.yaml')"
13
+ #
14
+ # =============================================================================
15
+
16
+ id: engineer
17
+ description: >
18
+ user wants to modify files, edit code, fix bugs, implement features,
19
+ refactor code, or create a pull request. READ-WRITE — can modify files
20
+ and create PRs.
21
+ requires: [code-explorer]
22
+ knowledge: |
23
+ ## Engineer — Code Changes and PR Creation
24
+ The `engineer` tool modifies code, implements features, fixes bugs, and creates PRs.
25
+
26
+ **IMPORTANT**: Call `code-explorer` FIRST to understand the codebase before making changes.
27
+ The engineer tool needs pre-resolved project paths from code-explorer's output.
28
+
29
+ Workflow:
30
+ 1. Use `code-explorer` to investigate and understand relevant code
31
+ 2. Pass the explored project information to `engineer` for implementation
32
+ 3. The engineer will create branches, make changes, run tests, and optionally create PRs
33
+
34
+ The tool returns:
35
+ - `text`: Summary of changes made
36
+ - `changes`: Array of modified files with actions (created/modified/deleted)
37
+ - `pr_url`: URL of created pull request (if applicable)
38
+ tools:
39
+ engineer:
40
+ workflow: engineer
41
+ inputs: {}