@runcofactor/cli 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/README.md +125 -0
  2. package/dist/api-catalog-GFGCLVS4.js +401 -0
  3. package/dist/api-catalog-GFGCLVS4.js.map +1 -0
  4. package/dist/attachment-SJC7D7FY.js +276 -0
  5. package/dist/attachment-SJC7D7FY.js.map +1 -0
  6. package/dist/auth-EN6NQCJY.js +31 -0
  7. package/dist/auth-EN6NQCJY.js.map +1 -0
  8. package/dist/bin.js +7 -0
  9. package/dist/bin.js.map +1 -0
  10. package/dist/chunk-3CZU7XCS.js +272 -0
  11. package/dist/chunk-3CZU7XCS.js.map +1 -0
  12. package/dist/chunk-4NRDONSC.js +1048 -0
  13. package/dist/chunk-4NRDONSC.js.map +1 -0
  14. package/dist/chunk-4PNOV7OV.js +51980 -0
  15. package/dist/chunk-4PNOV7OV.js.map +1 -0
  16. package/dist/chunk-52IO7ACJ.js +2941 -0
  17. package/dist/chunk-52IO7ACJ.js.map +1 -0
  18. package/dist/chunk-6LEZCKKV.js +663 -0
  19. package/dist/chunk-6LEZCKKV.js.map +1 -0
  20. package/dist/chunk-A4OFTUVV.js +170 -0
  21. package/dist/chunk-A4OFTUVV.js.map +1 -0
  22. package/dist/chunk-B2VISN2V.js +2459 -0
  23. package/dist/chunk-B2VISN2V.js.map +1 -0
  24. package/dist/chunk-CLAWT7BJ.js +271 -0
  25. package/dist/chunk-CLAWT7BJ.js.map +1 -0
  26. package/dist/chunk-GKY2KXO5.js +281 -0
  27. package/dist/chunk-GKY2KXO5.js.map +1 -0
  28. package/dist/chunk-I4QLI3JO.js +413 -0
  29. package/dist/chunk-I4QLI3JO.js.map +1 -0
  30. package/dist/chunk-IMPZAX6L.js +180 -0
  31. package/dist/chunk-IMPZAX6L.js.map +1 -0
  32. package/dist/chunk-J6SH7GQT.js +241 -0
  33. package/dist/chunk-J6SH7GQT.js.map +1 -0
  34. package/dist/chunk-JDBCZ3Z2.js +298 -0
  35. package/dist/chunk-JDBCZ3Z2.js.map +1 -0
  36. package/dist/chunk-JICSYFRJ.js +46 -0
  37. package/dist/chunk-JICSYFRJ.js.map +1 -0
  38. package/dist/chunk-JWHYXEA2.js +32 -0
  39. package/dist/chunk-JWHYXEA2.js.map +1 -0
  40. package/dist/chunk-N7HFALX3.js +88 -0
  41. package/dist/chunk-N7HFALX3.js.map +1 -0
  42. package/dist/chunk-Q4BO4Z5V.js +178 -0
  43. package/dist/chunk-Q4BO4Z5V.js.map +1 -0
  44. package/dist/chunk-QFDOWABX.js +195 -0
  45. package/dist/chunk-QFDOWABX.js.map +1 -0
  46. package/dist/chunk-SJJW77WN.js +605 -0
  47. package/dist/chunk-SJJW77WN.js.map +1 -0
  48. package/dist/chunk-VKVL7WBN.js +50 -0
  49. package/dist/chunk-VKVL7WBN.js.map +1 -0
  50. package/dist/chunk-WOHL4FTB.js +353 -0
  51. package/dist/chunk-WOHL4FTB.js.map +1 -0
  52. package/dist/chunk-XQXVG7RA.js +426 -0
  53. package/dist/chunk-XQXVG7RA.js.map +1 -0
  54. package/dist/chunk-ZD3KHESI.js +94 -0
  55. package/dist/chunk-ZD3KHESI.js.map +1 -0
  56. package/dist/credential-COLTBV6D.js +450 -0
  57. package/dist/credential-COLTBV6D.js.map +1 -0
  58. package/dist/credential-template-EEPYFG7R.js +291 -0
  59. package/dist/credential-template-EEPYFG7R.js.map +1 -0
  60. package/dist/debug-workflow-KOVH5GOP.js +259 -0
  61. package/dist/debug-workflow-KOVH5GOP.js.map +1 -0
  62. package/dist/dist-44PZR7BQ.js +423 -0
  63. package/dist/dist-44PZR7BQ.js.map +1 -0
  64. package/dist/docs-3SSORHB5.js +366 -0
  65. package/dist/docs-3SSORHB5.js.map +1 -0
  66. package/dist/doctor-OTSIWV6G.js +132 -0
  67. package/dist/doctor-OTSIWV6G.js.map +1 -0
  68. package/dist/feature-flag-T2LRZME3.js +283 -0
  69. package/dist/feature-flag-T2LRZME3.js.map +1 -0
  70. package/dist/file-type-S3UUBVCT.js +4205 -0
  71. package/dist/file-type-S3UUBVCT.js.map +1 -0
  72. package/dist/index.d.ts +3 -0
  73. package/dist/index.js +982 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/init-TGWRECT2.js +208 -0
  76. package/dist/init-TGWRECT2.js.map +1 -0
  77. package/dist/install-DXSNWZKO.js +295 -0
  78. package/dist/install-DXSNWZKO.js.map +1 -0
  79. package/dist/issues-XIZOPX6T.js +564 -0
  80. package/dist/issues-XIZOPX6T.js.map +1 -0
  81. package/dist/knobs-NBINZIUB.js +295 -0
  82. package/dist/knobs-NBINZIUB.js.map +1 -0
  83. package/dist/list-3TDHK637.js +59 -0
  84. package/dist/list-3TDHK637.js.map +1 -0
  85. package/dist/local-HICVD6PU.js +100 -0
  86. package/dist/local-HICVD6PU.js.map +1 -0
  87. package/dist/login-UOIUXVUK.js +315 -0
  88. package/dist/login-UOIUXVUK.js.map +1 -0
  89. package/dist/mcp-54IJEOUA.js +1956 -0
  90. package/dist/mcp-54IJEOUA.js.map +1 -0
  91. package/dist/pdf-extract-ABWV3L5X.js +13 -0
  92. package/dist/pdf-extract-ABWV3L5X.js.map +1 -0
  93. package/dist/pdfjs-DQ2YA5S3.js +41561 -0
  94. package/dist/pdfjs-DQ2YA5S3.js.map +1 -0
  95. package/dist/property-UXLOLPTD.js +701 -0
  96. package/dist/property-UXLOLPTD.js.map +1 -0
  97. package/dist/property-skill-guide.md +189 -0
  98. package/dist/psql-BYUUNLGG.js +115 -0
  99. package/dist/psql-BYUUNLGG.js.map +1 -0
  100. package/dist/record-4TR2UIIS.js +233 -0
  101. package/dist/record-4TR2UIIS.js.map +1 -0
  102. package/dist/redis-35VBUYW7.js +116 -0
  103. package/dist/redis-35VBUYW7.js.map +1 -0
  104. package/dist/release-OOLTUBUT.js +354 -0
  105. package/dist/release-OOLTUBUT.js.map +1 -0
  106. package/dist/sandbox-XGSUU6G7.js +2046 -0
  107. package/dist/sandbox-XGSUU6G7.js.map +1 -0
  108. package/dist/scenarios-GWLJEGHA.js +280 -0
  109. package/dist/scenarios-GWLJEGHA.js.map +1 -0
  110. package/dist/session-MYFNZPRP.js +2414 -0
  111. package/dist/session-MYFNZPRP.js.map +1 -0
  112. package/dist/skill-KM34LDMO.js +1839 -0
  113. package/dist/skill-KM34LDMO.js.map +1 -0
  114. package/dist/skills/debug-run/SKILL.md +262 -0
  115. package/dist/skills/debug-run/history/1.0.0.md +244 -0
  116. package/dist/skills/debug-run/history/1.0.1.md +245 -0
  117. package/dist/skills/debug-run/history/1.0.2.md +254 -0
  118. package/dist/skills/debug-run/history/1.1.0.md +262 -0
  119. package/dist/skills/debug-run/version.json +3 -0
  120. package/dist/spec-RA3CFEKG.js +951 -0
  121. package/dist/spec-RA3CFEKG.js.map +1 -0
  122. package/dist/src-LB4HBTJQ.js +1516 -0
  123. package/dist/src-LB4HBTJQ.js.map +1 -0
  124. package/dist/src-N3TPKGTY.js +244 -0
  125. package/dist/src-N3TPKGTY.js.map +1 -0
  126. package/dist/src-UVDX5UP6.js +17 -0
  127. package/dist/src-UVDX5UP6.js.map +1 -0
  128. package/dist/start-PSEQSQA3.js +151 -0
  129. package/dist/start-PSEQSQA3.js.map +1 -0
  130. package/dist/tag-YY6YAABQ.js +22 -0
  131. package/dist/tag-YY6YAABQ.js.map +1 -0
  132. package/dist/task-IQC3YR5K.js +922 -0
  133. package/dist/task-IQC3YR5K.js.map +1 -0
  134. package/dist/test-suite-DZNJFXDW.js +394 -0
  135. package/dist/test-suite-DZNJFXDW.js.map +1 -0
  136. package/dist/trace-HXIW7U2D.js +14 -0
  137. package/dist/trace-HXIW7U2D.js.map +1 -0
  138. package/dist/triage-ORG45R5W.js +16 -0
  139. package/dist/triage-ORG45R5W.js.map +1 -0
  140. package/dist/upgrade-L2VYMP2V.js +61 -0
  141. package/dist/upgrade-L2VYMP2V.js.map +1 -0
  142. package/dist/workflow-XYFH5LTP.js +3708 -0
  143. package/dist/workflow-XYFH5LTP.js.map +1 -0
  144. package/dist/workflow-cache-NQJQFWMV.js +253 -0
  145. package/dist/workflow-cache-NQJQFWMV.js.map +1 -0
  146. package/dist/workflow-debug-BGD4UIXU.js +927 -0
  147. package/dist/workflow-debug-BGD4UIXU.js.map +1 -0
  148. package/dist/workflow-proposals-MTUDX46O.js +382 -0
  149. package/dist/workflow-proposals-MTUDX46O.js.map +1 -0
  150. package/package.json +61 -0
@@ -0,0 +1,1839 @@
1
+ import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
2
+ import "./chunk-VKVL7WBN.js";
3
+
4
+ // src/skill.ts
5
+ import { existsSync, mkdirSync, writeFileSync } from "fs";
6
+ import { homedir } from "os";
7
+ import { join as join2 } from "path";
8
+
9
+ // src/skill-templates/issue-log-xref.ts
10
+ var issueLogXrefTemplate = {
11
+ name: "issue-log-xref",
12
+ description: "Cross-reference Canary issues with server logs to find root causes",
13
+ version: "1.0.0",
14
+ render: () => `---
15
+ name: issue-log-xref
16
+ version: 1.0.0
17
+ description: Cross-reference Canary QA issues with server logs to identify root causes. Use when investigating why a test failed, correlating browser errors with backend logs, or debugging issues found by Canary.
18
+ allowed-tools: Bash(canary issues:*), Bash(canary login:*), Grep, Read
19
+ ---
20
+
21
+ # Issue Log Cross-Reference
22
+
23
+ Cross-reference Canary-detected issues with server logs to identify root causes, correlate errors, and provide actionable debugging context.
24
+
25
+ ## Prerequisites
26
+
27
+ - \`canary\` CLI installed and authenticated (\`canary login\`)
28
+ - Access to server logs (see Log Source Configuration below)
29
+
30
+ ## Workflow
31
+
32
+ ### 1. Fetch issues from Canary
33
+
34
+ List recent issues to find ones worth investigating:
35
+
36
+ \`\`\`bash
37
+ canary issues list --format markdown
38
+ \`\`\`
39
+
40
+ Use filters to narrow down:
41
+
42
+ \`\`\`bash
43
+ canary issues list --severity high --status open --format markdown
44
+ \`\`\`
45
+
46
+ ### 2. Get issue details
47
+
48
+ Once you identify an issue, fetch its full diagnostics:
49
+
50
+ \`\`\`bash
51
+ canary issues get <issue-id> --format markdown
52
+ \`\`\`
53
+
54
+ Note the following from the issue details:
55
+ - **Timestamps** \u2014 when the issue was detected
56
+ - **URLs / endpoints** \u2014 which pages or API routes were involved
57
+ - **Error messages** \u2014 any visible error text captured in screenshots or DOM
58
+ - **HTTP status codes** \u2014 failed network requests
59
+
60
+ ### 3. Access server logs
61
+
62
+ <!-- ============================================================
63
+ LOG SOURCE CONFIGURATION
64
+
65
+ Replace {{LOG_SOURCE_INSTRUCTIONS}} below with instructions
66
+ specific to your infrastructure. The AI agent reading this
67
+ skill will use these instructions to fetch logs.
68
+
69
+ Examples:
70
+
71
+ **AWS CloudWatch:**
72
+ Query CloudWatch Logs Insights for the relevant log group:
73
+ \`\`\`bash
74
+ aws logs filter-log-events \\
75
+ --log-group-name /ecs/my-api \\
76
+ --start-time <epoch-ms> \\
77
+ --end-time <epoch-ms> \\
78
+ --filter-pattern "<error-text>"
79
+ \`\`\`
80
+
81
+ **Local log files:**
82
+ Search local log files for matching entries:
83
+ \`\`\`bash
84
+ grep -i "<error-text>" /var/log/my-app/*.log
85
+ \`\`\`
86
+
87
+ **Datadog:**
88
+ Use the Datadog CLI or API to query logs:
89
+ \`\`\`bash
90
+ dog logs list --query "service:my-api status:error" \\
91
+ --from <ISO-timestamp> --to <ISO-timestamp>
92
+ \`\`\`
93
+
94
+ **kubectl (Kubernetes):**
95
+ Fetch pod logs for the relevant service:
96
+ \`\`\`bash
97
+ kubectl logs -l app=my-api --since=1h | grep "<error-text>"
98
+ \`\`\`
99
+ ============================================================ -->
100
+
101
+ {{LOG_SOURCE_INSTRUCTIONS}}
102
+
103
+ ### 4. Cross-reference and correlate
104
+
105
+ Match the Canary issue with log entries using these correlation points:
106
+
107
+ - **Timestamps** \u2014 find log entries within a few seconds of the issue detection time
108
+ - **Error messages** \u2014 search logs for the same error text shown in the issue
109
+ - **HTTP status codes** \u2014 look for 4xx/5xx responses matching the failed requests
110
+ - **URLs / routes** \u2014 filter logs by the endpoint or page path from the issue
111
+ - **Stack traces** \u2014 identify the code path that produced the error
112
+ - **Request IDs** \u2014 if your app uses correlation/request IDs, trace the full request lifecycle
113
+
114
+ ## Analysis Guidelines
115
+
116
+ When presenting findings:
117
+
118
+ 1. **State the match confidence** \u2014 clear match, likely match, or circumstantial
119
+ 2. **Show the timeline** \u2014 lay out events in chronological order across both sources
120
+ 3. **Identify the root cause** \u2014 distinguish between the symptom (what Canary caught) and the cause (what the logs reveal)
121
+ 4. **Suggest next steps** \u2014 point to the specific code, service, or configuration that needs attention
122
+ 5. **Note gaps** \u2014 if logs are missing or incomplete, say so explicitly rather than speculating
123
+ `
124
+ };
125
+
126
+ // src/skill-templates/browser-testing.ts
127
+ var browserTestingTemplate = {
128
+ name: "browser-testing",
129
+ description: "Use the Canary CLI to control a browser for testing web applications",
130
+ version: "1.0.0",
131
+ render: () => `---
132
+ name: browser-testing
133
+ version: 1.0.0
134
+ description: Control a local browser via the Canary CLI to test web applications interactively. Use when the user asks to test, browse, interact with, or verify a web application using canary browser sessions.
135
+ allowed-tools: Bash(canary session:*), Bash(canary login:*), Bash(canary workflow:*), Bash(canary credential:*), Read
136
+ ---
137
+
138
+ # Browser Testing with Canary
139
+
140
+ Use the \`canary session\` CLI to launch and control a local browser for testing web applications. You interact with pages through **snapshots** (accessibility tree + screenshot), then issue commands to click, type, navigate, and assert.
141
+
142
+ ## Prerequisites
143
+
144
+ - \`canary\` CLI installed and authenticated (\`canary login\`)
145
+ - Run \`canary session --help\` for the full command reference
146
+
147
+ ## Environments & Authentication
148
+
149
+ Canary credentials are stored remotely and belong to a **property** (an app with a base URL). When the user asks you to test, determine the environment:
150
+
151
+ ### Remote / Deployed environments (default)
152
+
153
+ Credentials are pre-configured with storage state. Just use \`--credential\` to load one:
154
+
155
+ \`\`\`bash
156
+ # Interactive picker \u2014 shows credential name, property name, and base URL
157
+ canary session start --credential
158
+
159
+ # By name
160
+ canary session start --credential "Admin User" --url https://staging.example.com
161
+ \`\`\`
162
+
163
+ ### Local development
164
+
165
+ Credentials stored in Canary won't work against localhost because session cookies are bound to the remote domain. When testing a local dev server:
166
+
167
+ 1. **Ask the user to log in manually.** Start a session without credentials and tell them to authenticate in the browser:
168
+
169
+ \`\`\`bash
170
+ canary session start --url http://localhost:5173
171
+ \`\`\`
172
+
173
+ Then tell the user: "I've opened a browser to your local app. Please log in, and let me know when you're ready for me to continue."
174
+
175
+ 2. **Wait for confirmation** before proceeding with testing.
176
+
177
+ 3. Alternatively, if the user has a **local storage state file**, use it:
178
+
179
+ \`\`\`bash
180
+ canary session start --storage-state ./local-auth.json --url http://localhost:5173
181
+ \`\`\`
182
+
183
+ **Rule:** If the URL is localhost or 127.0.0.1, do NOT use \`--credential\` \u2014 it will download remote session state that won't work locally. Instead, prompt the user to log in.
184
+
185
+ ## Core Workflow
186
+
187
+ The fundamental loop is: **snapshot \u2192 read \u2192 act \u2192 snapshot \u2192 verify**.
188
+
189
+ ### 1. Start a session
190
+
191
+ \`\`\`bash
192
+ # Start a headed browser (you can watch it)
193
+ canary session start --url https://your-app.com
194
+
195
+ # Start with a saved credential (interactive picker shows property + base URL)
196
+ canary session start --credential
197
+
198
+ # Start with a specific credential by name
199
+ canary session start --credential "Admin User" --url https://your-app.com
200
+
201
+ # Load a credential into an already-running session
202
+ canary session load-credential
203
+ \`\`\`
204
+
205
+ ### 2. Take a snapshot to see the page
206
+
207
+ Snapshots are your primary way to understand what's on the page. They return an **accessibility tree** with element references (refs) and a **screenshot**.
208
+
209
+ \`\`\`bash
210
+ # Full snapshot: accessibility tree + screenshot
211
+ canary session snapshot
212
+
213
+ # Search for specific elements (fastest way to find what you need)
214
+ canary session snapshot --search "Submit"
215
+ canary session snapshot --search "Email|Password"
216
+
217
+ # Expand a collapsed section (use the ref shown in the tree, e.g. [e50])
218
+ canary session snapshot --expand e50
219
+
220
+ # Screenshot only (useful for visual verification)
221
+ canary session snapshot --mode screenshot
222
+
223
+ # Tree only (useful when you need ref details, no image)
224
+ canary session snapshot --mode tree
225
+ \`\`\`
226
+
227
+ **Reading snapshot output:**
228
+
229
+ The accessibility tree shows elements with refs like \`[ref=e15]\`. Use these refs in subsequent commands. The tree is structured to show the page hierarchy \u2014 forms, buttons, links, headings, etc.
230
+
231
+ When you use \`--search\`, matching elements are highlighted in the output and on the screenshot. Search is OR-matched and case-insensitive. Use search to **locate** elements, not to validate visual states \u2014 search overlays can obscure subtle UI changes.
232
+
233
+ Large sections of the tree may be collapsed (shown as e.g. \`(127 elements - use expand="e50" to see content)\`). Use \`--expand <ref>\` to reveal the full contents of a collapsed section.
234
+
235
+ ### 3. Interact with the page
236
+
237
+ Always reference elements by their **ref** from the snapshot.
238
+
239
+ \`\`\`bash
240
+ # Click an element
241
+ canary session click --ref e15 --element "Submit button"
242
+
243
+ # Type into an input field
244
+ canary session type --ref e7 --text "user@example.com" --element "Email input"
245
+
246
+ # Type and submit (presses Enter after typing)
247
+ canary session type --ref e7 --text "search query" --element "Search box" --submit
248
+
249
+ # Fill multiple form fields at once
250
+ canary session fill --fields '[{"ref":"e3","value":"John"},{"ref":"e4","value":"john@example.com"}]'
251
+
252
+ # Select a dropdown option
253
+ canary session select --ref e12 --value "admin" --element "Role dropdown"
254
+
255
+ # Press a keyboard key
256
+ canary session press --key Enter
257
+ canary session press --key Escape
258
+ canary session press --key ArrowDown
259
+
260
+ # Hover to reveal hidden content (tooltips, dropdowns)
261
+ canary session hover --ref e8 --element "User menu"
262
+ \`\`\`
263
+
264
+ ### 4. Navigate
265
+
266
+ \`\`\`bash
267
+ canary session navigate --url https://your-app.com/settings
268
+ canary session back
269
+ \`\`\`
270
+
271
+ ### 5. Scroll
272
+
273
+ \`\`\`bash
274
+ # Scroll down the page
275
+ canary session scroll --direction down
276
+
277
+ # Scroll with more intensity (1-10, default 3)
278
+ canary session scroll --direction down --amount 8
279
+
280
+ # Scroll up
281
+ canary session scroll --direction up
282
+ \`\`\`
283
+
284
+ ### 6. Verify results
285
+
286
+ After every action, take a snapshot to verify the result:
287
+
288
+ \`\`\`bash
289
+ # Check what happened after clicking submit
290
+ canary session snapshot
291
+
292
+ # Search for a success message
293
+ canary session snapshot --search "Success|saved"
294
+
295
+ # Check for error messages
296
+ canary session snapshot --search "Error|failed|invalid"
297
+
298
+ # Check console for JavaScript errors
299
+ canary session console --only-errors
300
+
301
+ # Check network requests for failed API calls
302
+ canary session network
303
+ \`\`\`
304
+
305
+ ## Session Management
306
+
307
+ \`\`\`bash
308
+ # List all active sessions
309
+ canary session list
310
+
311
+ # Check session details
312
+ canary session status
313
+
314
+ # Stop a session
315
+ canary session stop
316
+
317
+ # Stop all sessions
318
+ canary session stop --all
319
+
320
+ # When multiple sessions are active, specify which one
321
+ canary session snapshot --session s1
322
+ canary session click --ref e5 --element "Button" --session s2
323
+ \`\`\`
324
+
325
+ ## Advanced Techniques
326
+
327
+ ### Waiting for page changes
328
+
329
+ \`\`\`bash
330
+ # Wait for text to appear (after navigation or async action)
331
+ canary session wait --text "Dashboard loaded"
332
+
333
+ # Wait for text to disappear (loading spinner)
334
+ canary session wait --text "Loading..."
335
+
336
+ # Wait a fixed time (use sparingly)
337
+ canary session wait --time 2
338
+ \`\`\`
339
+
340
+ ### Coordinate-based clicking
341
+
342
+ When ref-based clicking doesn't work (overlays, canvas elements), use coordinates. First take a snapshot, look at the screenshot, estimate coordinates, then click:
343
+
344
+ \`\`\`bash
345
+ # Click at specific coordinates
346
+ canary session click --x 450 --y 300 --element "Canvas element"
347
+ \`\`\`
348
+
349
+ ### Tab management
350
+
351
+ \`\`\`bash
352
+ canary session tabs --action list
353
+ canary session tabs --action new
354
+ canary session tabs --action select --index 0
355
+ canary session tabs --action close --index 1
356
+ \`\`\`
357
+
358
+ ### Evaluating JavaScript
359
+
360
+ \`\`\`bash
361
+ canary session evaluate --js "() => document.title"
362
+ canary session evaluate --js "() => window.location.href"
363
+ canary session evaluate --js "() => document.querySelectorAll('.error').length"
364
+ \`\`\`
365
+
366
+ ### Taking labeled screenshots
367
+
368
+ \`\`\`bash
369
+ # Take a labeled screenshot for later reference
370
+ canary session screenshot --label "before-submit"
371
+
372
+ # Full page screenshot
373
+ canary session screenshot --full-page --label "full-page-state"
374
+ \`\`\`
375
+
376
+ ### File downloads
377
+
378
+ \`\`\`bash
379
+ # List downloaded files
380
+ canary session evaluate --js "() => 'trigger download'"
381
+ # ... after download completes:
382
+ canary session list-downloads
383
+ canary session read-download
384
+ \`\`\`
385
+
386
+ ## Testing Guidelines
387
+
388
+ 1. **Always snapshot after actions** \u2014 never assume an action succeeded. Take a snapshot to verify.
389
+ 2. **Use search to locate elements** \u2014 \`--search "Submit"\` is faster than scanning the full tree.
390
+ 3. **Use refs, not coordinates** \u2014 ref-based interaction is more reliable. Only fall back to coordinates when refs fail.
391
+ 4. **Check for errors** \u2014 after form submissions or navigation, check both the page content and console/network for errors.
392
+ 5. **Wait for async operations** \u2014 if an action triggers an API call, use \`wait --text\` for the expected result rather than fixed delays.
393
+ 6. **Load credentials for authenticated testing** \u2014 use \`canary session start --credential\` or \`canary session load-credential\` to test as specific users.
394
+ 7. **Report what you find** \u2014 describe what you see in snapshots, what worked, what failed, and include relevant refs or error messages.
395
+
396
+ ## Improving Testability (Local Development)
397
+
398
+ When testing locally, you have access to the source code \u2014 use this advantage. If the accessibility tree is confusing or elements are hard to target, **diagnose why** and suggest concrete improvements to the user.
399
+
400
+ ### When to investigate
401
+
402
+ If you encounter any of these signals, dig deeper before moving on:
403
+ - Multiple elements with identical or vague labels (e.g., several "Button" or "div" entries)
404
+ - Interactive elements missing from the accessibility tree entirely
405
+ - Custom components that render as opaque containers with no semantic children
406
+ - Difficulty distinguishing between similar repeated elements (e.g., rows in a table)
407
+ - Elements you can see in the screenshot but can't find in the tree
408
+
409
+ ### How to investigate
410
+
411
+ 1. **Use JavaScript evaluation** to inspect the actual DOM structure:
412
+ \`\`\`bash
413
+ canary session evaluate --js "() => document.querySelector('[data-testid]')?.outerHTML"
414
+ canary session evaluate --js "() => document.querySelectorAll('button').length"
415
+ canary session evaluate --js "() => [...document.querySelectorAll('button')].map(b => ({ text: b.textContent?.trim(), role: b.getAttribute('role'), ariaLabel: b.getAttribute('aria-label') }))"
416
+ \`\`\`
417
+
418
+ 2. **Read the component source code** \u2014 find and read the relevant component or template file to understand why the accessibility tree looks the way it does.
419
+
420
+ 3. **Compare screenshot vs tree** \u2014 take a screenshot-only snapshot and a tree-only snapshot. If you can see something visually but it's missing or ambiguous in the tree, that's a testability gap.
421
+
422
+ ### What to suggest
423
+
424
+ When you find testability gaps, suggest specific fixes to the user. Frame suggestions as improvements that help both automated testing and human accessibility:
425
+
426
+ - **Missing labels**: "The 'Save' buttons in each card are indistinguishable in the accessibility tree. Adding \`aria-label="Save {itemName}"\` or \`data-testid="save-{itemId}"\` would make them individually targetable."
427
+ - **Non-semantic elements**: "This clickable \`<div>\` doesn't appear as a button in the tree. Changing it to \`<button>\` or adding \`role="button"\` would fix this."
428
+ - **Missing headings/landmarks**: "This page section has no heading \u2014 screen readers and test agents can't tell what it contains. Adding an \`<h2>\` or \`aria-label\` to the \`<section>\` would help."
429
+ - **Unlabeled form fields**: "These inputs have placeholder text but no associated \`<label>\`. Adding \`<label htmlFor="...">\` or \`aria-label\` would make them reliably targetable."
430
+ - **Repeated generic elements**: "The table rows are hard to distinguish. Adding \`data-testid="row-{id}"\` or meaningful \`aria-label\` values to each row would make them individually addressable."
431
+
432
+ **Tone**: These are suggestions, not demands. Present them as quick wins that improve both test reliability and accessibility. Group them at the end of your testing report rather than interrupting the testing flow.
433
+
434
+ ## Creating Tests from Browser Exploration
435
+
436
+ After exploring an app via \`canary session\`, you can persist what you discovered as a reusable test workflow.
437
+
438
+ ### Explore \u2192 Author \u2192 Create flow
439
+
440
+ 1. **Explore**: Use \`canary session\` to navigate, snapshot, and interact with the app
441
+ 2. **Author**: Construct workflow JSON with nodes and playbook steps based on what was observed
442
+ 3. **Create**: Pipe JSON to \`canary workflow create\`
443
+ 4. **Verify**: Check with \`canary workflow get <id>\`
444
+
445
+ ### Node authoring guidance
446
+
447
+ - Group related actions into single action nodes (one node per logical step, not per click)
448
+ - Node types: \`login\` (authentication), \`navigate\` (go to URL), \`action\` (interact with elements), \`assertion\` (verify standalone page state), \`end\` (terminal node)
449
+ - Every action node **must** have a \`customActionTitle\` \u2014 a short (3-8 word) human-readable title describing the step
450
+ - **Prefer inline assertions** on action nodes over separate assertion nodes. Add \`inlineAssertions\` to verify outcomes right after the action completes. Use separate \`assertion\` nodes only for standalone page-level verifications (e.g., initial page state before any action)
451
+ - Inline assertion format: \`{ "condition": "...", "severity": "fail" }\`. **Do not author an \`id\` field** \u2014 the server stamps a UUID on persist. Hand-rolled UUIDs (especially placeholders like \`a1b2c3d4-...\`) are rejected and replaced.
452
+
453
+ ### Playbook step authoring guidance
454
+
455
+ - Use **semantic targets** from the accessibility tree (button text, field labels, placeholder text) \u2014 NOT refs or CSS selectors
456
+ - Target format: describe as a human would ("Submit button", "Email input field", "Status dropdown")
457
+ - For \`fill\` actions: include \`params: { "value": "..." }\`
458
+ - **Always prefer macros over hardcoded test data.** Macros generate fresh values each run, preventing flaky tests from stale or colliding data. Never hardcode emails, names, phone numbers, or dates when a macro can generate them.
459
+ - Prefix test data with "QA Test" for identifiability (e.g., \`"QA Test $FIRST_NAME()"\`)
460
+ - Valid actions: \`click\`, \`fill\`, \`fill_form\`, \`select\`, \`navigate\`, \`wait\`, \`scroll\`, \`hover\`, \`upload\`, \`verify\`, \`press_key\`, \`drag\`, \`resize\`, \`capture\`, \`extract_table\`, \`python_execute\`, \`verify_download\`, \`verify_download_content\`, \`screenshot\`, \`agent_loop\`
461
+
462
+ ### Using macros and variables
463
+
464
+ Macros are resolved at run time \u2014 each execution gets fresh values, making tests repeatable and collision-free.
465
+
466
+ **Available macros:**
467
+
468
+ | Macro | Args | Output | Example |
469
+ |-------|------|--------|---------|
470
+ | \`$EMAIL()\` | \u2014 | Unique test email | \`test-xk9mq2@example-test.com\` |
471
+ | \`$FIRST_NAME()\` | \u2014 | Random first name | \`Jordan\` |
472
+ | \`$LAST_NAME()\` | \u2014 | Random last name | \`Chen\` |
473
+ | \`$PHONE()\` | \u2014 | US phone (555 prefix) | \`+1-555-382-7491\` |
474
+ | \`$UUID()\` | \u2014 | Random UUID v4 | \`a1b2c3d4-...\` |
475
+ | \`$RANDOM_INT(min, max)\` | 2 ints | Random integer (inclusive) | \`$RANDOM_INT(100,999)\` \u2192 \`427\` |
476
+ | \`$RANDOM_STRING(length)\` | 1 int (1\u20131000) | Alphanumeric string | \`$RANDOM_STRING(8)\` \u2192 \`xK9mQ3bR\` |
477
+ | \`$TODAY()\` | \u2014 | Current date (YYYY-MM-DD) | \`2026-04-06\` |
478
+ | \`$DATE(days)\` | 1 int | Date offset from today | \`$DATE(-7)\` \u2192 last week |
479
+ | \`$NOW()\` | \u2014 | ISO 8601 timestamp | \`2026-04-06T14:30:00.000Z\` |
480
+ | \`$TIMESTAMP()\` | \u2014 | Unix milliseconds | \`1771520400000\` |
481
+ | \`$SEQUENCE("name")\` | counter name | Auto-incrementing per run | \`1\`, \`2\`, \`3\`... |
482
+
483
+ **When to use macros:**
484
+ - Email fields \u2192 \`$EMAIL()\`
485
+ - Name fields \u2192 \`$FIRST_NAME()\` / \`$LAST_NAME()\`
486
+ - Amounts, quantities, identifiers \u2192 \`$RANDOM_INT()\` or \`$RANDOM_STRING()\`
487
+ - Date fields \u2192 \`$TODAY()\` / \`$DATE()\`
488
+ - Sequential numbering \u2192 \`$SEQUENCE("name")\`
489
+ - Macros combine inline: \`"QA Test $FIRST_NAME() $LAST_NAME()"\`, \`"order-$RANDOM_STRING(6)"\`
490
+
491
+ **Variables (\`$var.name\`):**
492
+ - Steps can reference data captured from earlier steps using \`$var.name\`
493
+ - Variable values are extracted during execution and available to subsequent steps
494
+ - Use step-qualified refs when multiple steps set the same variable: \`$var.Step2_Create_Client.name\`
495
+
496
+ ### Credential selection
497
+
498
+ **Always use \`credentialId\` (UUID) in the login node \u2014 never just \`credentialName\`.**
499
+
500
+ 1. **List available credentials** before authoring:
501
+ \`\`\`bash
502
+ canary credential list --json
503
+ \`\`\`
504
+ This returns each credential's \`id\`, \`name\`, \`isDefault\`, \`propertyName\`, and environment info.
505
+
506
+ 2. **Pick the right credential**:
507
+ - Prefer the **default credential** (\`isDefault: true\`)
508
+ - **Never** use a credential with "local" in the name \u2014 it won't work in deployed environments
509
+ - If multiple non-local credentials exist and you're unsure which to use, **ask the user**
510
+
511
+ 3. **Set both \`credentialId\` and \`credentialName\`** in the login node's \`configJson\`:
512
+ \`\`\`json
513
+ { "credentialId": "4f6db838-...", "credentialName": "QA User" }
514
+ \`\`\`
515
+
516
+ 4. **If no matching credential exists**, warn the user that one needs to be created first in the web app.
517
+
518
+ The CLI will auto-resolve credentials if \`credentialId\` is missing, but it may fail if the choice is ambiguous. Always specify the ID explicitly for reliable uploads.
519
+
520
+ ### Environment-aware publishing
521
+
522
+ - **CLI-created workflows default to \`published\`** \u2014 no flag needed. Reliability is tracked automatically by the workflow's \`health\` (starts \`unverified\`); a flow that keeps failing becomes \`degraded\` and is skipped in suites until edited or re-enabled.
523
+ - **Deployed/known environment**: Use \`--run-and-publish\` \u2014 test runs immediately, publishes if it passes.
524
+
525
+ ### Usage
526
+
527
+ \`\`\`bash
528
+ # Create (published by default)
529
+ echo '<workflow JSON>' | canary workflow create --from-stdin
530
+
531
+ # Run-then-publish for deployed environments
532
+ echo '<workflow JSON>' | canary workflow create --from-stdin --run-and-publish --property "My App"
533
+
534
+ # From file
535
+ canary workflow create --from-file workflow.json
536
+ \`\`\`
537
+
538
+ ### Example workflow JSON
539
+
540
+ \`\`\`json
541
+ {
542
+ "name": "Verify user can create an invoice",
543
+ "description": "Tests the invoice creation flow end-to-end",
544
+ "nodes": [
545
+ {
546
+ "nodeType": "login",
547
+ "configJson": {
548
+ "credentialId": "4f6db838-16be-49e8-8887-8d0be1c83a87",
549
+ "credentialName": "QA User"
550
+ }
551
+ },
552
+ {
553
+ "nodeType": "navigate",
554
+ "configJson": { "pageUrl": "/invoices", "pageTitle": "Invoices" }
555
+ },
556
+ {
557
+ "nodeType": "action",
558
+ "configJson": {
559
+ "customActionTitle": "Create a new invoice",
560
+ "actionDescription": "Fill out the new invoice form and save",
561
+ "playbookSteps": [
562
+ { "order": 1, "action": "click", "target": "New Invoice button" },
563
+ { "order": 2, "action": "fill", "target": "Client name field", "params": { "value": "QA Test $FIRST_NAME()" } },
564
+ { "order": 3, "action": "fill", "target": "Amount field", "params": { "value": "$RANDOM_INT(100,999)" } },
565
+ { "order": 4, "action": "click", "target": "Save button" }
566
+ ],
567
+ "inlineAssertions": [
568
+ {
569
+ "condition": "Success message 'Invoice created' is visible and the new invoice appears in the list",
570
+ "severity": "fail"
571
+ }
572
+ ]
573
+ }
574
+ }
575
+ ]
576
+ }
577
+ \`\`\`
578
+
579
+ ### Updating existing workflows
580
+
581
+ Use \`canary workflow sync\` to replace a workflow's nodes. It accepts the same
582
+ simple format as create \u2014 no edges, positions, or IDs needed:
583
+
584
+ \`\`\`bash
585
+ # From stdin
586
+ echo '{"nodes": [...]}' | canary workflow sync <workflowId> --from-stdin
587
+
588
+ # From file
589
+ canary workflow sync <workflowId> --from-file updated.json
590
+
591
+ # Create a proposal for review instead of applying directly
592
+ canary workflow sync <workflowId> --from-file updated.json --propose
593
+ \`\`\`
594
+
595
+ When using \`--propose\` on an existing workflow, fetch the current state first
596
+ and reuse each node's \`id\` for steps you keep \u2014 only omit \`id\` for genuinely
597
+ new steps. This is the best way to ensure the diff stays accurate so reviewers
598
+ see only the fields that actually changed:
599
+
600
+ \`\`\`bash
601
+ # Round-trip pattern for proposals
602
+ canary workflow get <workflowId> --format json > current.json
603
+ # edit current.json: change configJsons, keep ids you're not replacing
604
+ canary workflow sync <workflowId> --from-file current.json --propose
605
+ \`\`\`
606
+
607
+ ### Generating API sequences
608
+
609
+ Convert a browser-based workflow to a fast API sequence using its latest successful run:
610
+
611
+ \`\`\`bash
612
+ # Generate from the workflow's latest run
613
+ canary workflow generate-api-sequence <workflowId>
614
+
615
+ # With additional guidance
616
+ canary workflow generate-api-sequence <workflowId> --guidance "Focus on the create endpoint"
617
+
618
+ # JSON output
619
+ canary workflow generate-api-sequence <workflowId> --json
620
+ \`\`\`
621
+
622
+ ### Reference
623
+
624
+ Run \`canary workflow guide\` for the full authoring reference:
625
+
626
+ \`\`\`bash
627
+ canary workflow guide # All sections
628
+ canary workflow guide node-types # Available node types
629
+ canary workflow guide macros # Dynamic test data ($EMAIL, $RANDOM_STRING, etc.)
630
+ canary workflow guide playbook # Playbook steps and caching
631
+ canary workflow guide seed-workflows # Seed workflow patterns
632
+ canary workflow guide assertions # Inline and standalone assertions
633
+ \`\`\`
634
+ `
635
+ };
636
+
637
+ // src/skill-templates/agent-tasks.ts
638
+ var agentTasksTemplate = {
639
+ name: "agent-tasks",
640
+ description: "Manage trigger-based agent tasks for deferred work items",
641
+ version: "1.0.0",
642
+ render: () => `---
643
+ name: agent-tasks
644
+ version: 1.0.0
645
+ description: Manage trigger-based agent tasks via the Canary CLI. Use when the user asks to create a task, schedule post-deploy work, set up reminders for flag cleanup, track knob deprecation, or manage deferred work items triggered by system events.
646
+ allowed-tools: Bash(canary task:*), Read, Grep, Glob
647
+ ---
648
+
649
+ # Agent Tasks with Canary
650
+
651
+ Use \`canary task\` to create and manage **trigger-based work items** \u2014 deferred tasks that become actionable when a system event fires (deploy, flag change, schedule, etc.).
652
+
653
+ ## Prerequisites
654
+
655
+ - \`canary\` CLI installed and authenticated (\`canary login\`)
656
+ - The **AGENT_TASKS** feature flag must be enabled for the org
657
+ - Run \`canary task --help\` for the full command reference
658
+
659
+ ## Quick Reference
660
+
661
+ \`\`\`bash
662
+ # Create a task
663
+ canary task create --title "Remove feature flag" \\
664
+ --description "Delete COOL_FEATURE flag and all gating code" \\
665
+ --trigger flag-deleted --flag COOL_FEATURE
666
+
667
+ # List tasks
668
+ canary task list # all tasks
669
+ canary task list --status waiting # waiting for trigger
670
+ canary task list --status actionable # ready to work on
671
+ canary task list --trigger production-deploy # by trigger type
672
+
673
+ # Manage tasks
674
+ canary task get <id> # view details + event history
675
+ canary task update <id> --title "New title" # update task fields
676
+ canary task claim <id> # mark in progress
677
+ canary task complete <id> # mark done
678
+ canary task complete <id> --commit abc123 # done, link to resolution commit
679
+ canary task cancel <id> # cancel
680
+ canary task trigger <id> # manually fire trigger
681
+ \`\`\`
682
+
683
+ ## Trigger Types
684
+
685
+ | Type | Fires when... | Auto-fired? | Required flag |
686
+ |------|---------------|-------------|---------------|
687
+ | \`production-deploy\` | Code deployed to production | **Yes** | \u2014 |
688
+ | \`flag-deprecated\` | Feature flag set to deprecated | Yes | \`--flag <name>\` |
689
+ | \`flag-cleanup\` | Feature flag set to ready_for_cleanup | Yes | \`--flag <name>\` |
690
+ | \`flag-deleted\` | Feature flag deleted | Yes | \`--flag <name>\` |
691
+ | \`knob-deprecated\` | Knob set to deprecated | Yes | \`--knob <key>\` |
692
+ | \`knob-cleanup\` | Knob set to ready_for_cleanup | Yes | \`--knob <key>\` |
693
+ | \`knob-deleted\` | Knob deleted | Yes | \`--knob <key>\` |
694
+ | \`scheduled\` | A date/time is reached | Yes | \`--scheduled-date <ISO>\` |
695
+ | \`pr-merged\` | A specific PR is merged | Yes | \`--pr <number>\` |
696
+ | \`immediate\` | Immediately (no waiting) | N/A | \u2014 |
697
+ | \`manual\` | Manually triggered via CLI | N/A | \u2014 |
698
+
699
+ **Note:** \`pr-merged\` tasks are scoped to the linked repository selected by \`--repo\`, \`--role\`, or \`--repo-id\`.
700
+
701
+ ## Task Lifecycle
702
+
703
+ \`\`\`
704
+ \u250C\u2500\u2500\u2500 immediate \u2500\u2500\u2500\u25BA actionable
705
+ \u2502 \u2502
706
+ create \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 claim
707
+ \u2502 \u2502
708
+ \u2514\u2500\u2500\u2500 other types \u2500\u2500\u25BA waiting \u2500\u2500trigger\u2500\u2500\u25BA actionable \u2500\u2500\u25BA in_progress \u2500\u2500\u25BA completed
709
+ \u251C\u2500\u2500\u25BA canceled
710
+ \u2514\u2500\u2500\u25BA expired
711
+ \`\`\`
712
+
713
+ - **waiting**: Created, waiting for its trigger event
714
+ - **actionable**: Trigger fired (or immediate type), ready to be worked on
715
+ - **in_progress**: Claimed by someone, actively being worked
716
+ - **completed**: Done (optionally linked to a resolution commit)
717
+ - **canceled** / **expired**: Terminated without completion
718
+
719
+ ## Common Workflows
720
+
721
+ ### Post-deploy cleanup (remove temporary code after next production deploy)
722
+
723
+ \`\`\`bash
724
+ canary task create \\
725
+ --title "Remove backwards-compat shim for widget API" \\
726
+ --description "Old endpoint can be removed now that new endpoint is deployed." \\
727
+ --trigger production-deploy \\
728
+ --priority 1 \\
729
+ --tag cleanup --tag api
730
+ \`\`\`
731
+
732
+ This task automatically becomes actionable after the next successful production deployment.
733
+
734
+ ### Flag removal after GA
735
+
736
+ When a feature graduates from behind a flag:
737
+
738
+ \`\`\`bash
739
+ canary task create \\
740
+ --title "Clean up COOL_FEATURE flag gating code" \\
741
+ --description "Feature is GA. Remove all gating checks and delete the constant." \\
742
+ --trigger flag-deprecated --flag COOL_FEATURE \\
743
+ --tag cleanup --tag feature-flags
744
+ \`\`\`
745
+
746
+ Auto-fires when someone runs \`canary feature-flag lifecycle COOL_FEATURE --stage deprecated\`.
747
+
748
+ ### Knob deprecation follow-up
749
+
750
+ \`\`\`bash
751
+ canary task create \\
752
+ --title "Remove agent.truncation.enabled knob reads" \\
753
+ --description "Replace all knob reads with the hardcoded final value." \\
754
+ --trigger knob-deprecated --knob agent.truncation.enabled \\
755
+ --tag cleanup
756
+ \`\`\`
757
+
758
+ ### Scheduled future work
759
+
760
+ \`\`\`bash
761
+ canary task create \\
762
+ --title "Rotate API signing keys" \\
763
+ --description "Quarterly key rotation per security policy." \\
764
+ --trigger scheduled --scheduled-date 2026-06-30T00:00:00Z \\
765
+ --priority 2 --tag security
766
+ \`\`\`
767
+
768
+ ### Immediate task (starts actionable, no trigger wait)
769
+
770
+ \`\`\`bash
771
+ canary task create \\
772
+ --title "Fix type error in auth middleware" \\
773
+ --description "Strict mode caught a missing null check. Needs immediate fix." \\
774
+ --trigger immediate \\
775
+ --priority 2 --assign dev@example.com
776
+ \`\`\`
777
+
778
+ ## Integration Points
779
+
780
+ ### Feature Flags \u2192 Tasks
781
+
782
+ Flag lifecycle changes automatically fire matching task triggers:
783
+ - \`canary feature-flag lifecycle <name> --stage deprecated\` \u2192 fires \`flag-deprecated\` tasks
784
+ - \`canary feature-flag lifecycle <name> --stage ready_for_cleanup\` \u2192 fires \`flag-cleanup\` tasks
785
+ - \`canary feature-flag delete <name>\` \u2192 fires \`flag-deleted\` tasks
786
+
787
+ ### Knobs \u2192 Tasks
788
+
789
+ Same pattern as flags:
790
+ - \`canary knobs lifecycle <key> --stage deprecated\` \u2192 fires \`knob-deprecated\` tasks
791
+ - \`canary knobs lifecycle <key> --stage ready_for_cleanup\` \u2192 fires \`knob-cleanup\` tasks
792
+ - \`canary knobs delete <key>\` \u2192 fires \`knob-deleted\` tasks
793
+
794
+ ### Production Deploys \u2192 Tasks
795
+
796
+ The deployment notifier Lambda fires \`production-deploy\` task triggers automatically on successful production deployments. Any waiting tasks with this trigger type transition to \`actionable\`.
797
+
798
+ ## Tips
799
+
800
+ - \`--commit\` defaults to \`git HEAD\` \u2014 auto-detects the current commit
801
+ - \`--repo\` is auto-selected if the org has a single linked repository
802
+ - \`--tag\` is repeatable: \`--tag cleanup --tag api --tag urgent\`
803
+ - \`--description-file <path>\` reads description from a file (useful for long markdown descriptions)
804
+ - \`--json\` on any command returns structured JSON output for scripting
805
+ - \`--priority\`: 0 = normal (default), 1 = high, 2 = critical
806
+ - \`--expires <ISO date>\` sets an expiry after which the task auto-expires if not completed
807
+ - Use \`canary task trigger <id>\` to manually fire any waiting task's trigger
808
+ `
809
+ };
810
+
811
+ // src/skill-templates/canary-basic.ts
812
+ var canaryBasicTemplate = {
813
+ name: "canary-basic",
814
+ description: "Learn the Canary CLI \u2014 commands, help, and common patterns",
815
+ version: "1.0.0",
816
+ render: () => `---
817
+ name: canary-basic
818
+ version: 1.0.0
819
+ description: Reference guide for the Canary CLI. Invoke when anyone mentions canary, canary CLI, or asks about CLI commands for testing, browser sessions, workflows, issues, or releases.
820
+ allowed-tools: Read, Grep, Glob
821
+ ---
822
+
823
+ # Canary CLI Guide
824
+
825
+ The \`canary\` CLI is a command-line tool for running tests, controlling browser sessions, managing workflows, and inspecting issues.
826
+
827
+ ## Install & Login
828
+
829
+ \`\`\`bash
830
+ # Install globally
831
+ npm install -g @canaryai/cli
832
+ # or
833
+ bun add -g @canaryai/cli
834
+
835
+ # Authenticate (opens browser)
836
+ canary login
837
+
838
+ # Login to a specific org (for multi-org users)
839
+ canary login --org "My Org"
840
+ \`\`\`
841
+
842
+ ### Environment switching
843
+
844
+ \`\`\`bash
845
+ # Production (default)
846
+ canary login
847
+
848
+ # Dev
849
+ canary login --app-url https://app.dev.trycanary.ai --api-url https://api.dev.trycanary.ai
850
+
851
+ # Local
852
+ canary login --app-url http://localhost:5173 --api-url http://localhost:3000
853
+
854
+ # Or set the env var
855
+ export CANARY_API_URL=http://localhost:3000
856
+ \`\`\`
857
+
858
+ ---
859
+
860
+ ## Command Reference
861
+
862
+ ### Test Suites
863
+
864
+ \`\`\`bash
865
+ # Trigger a test suite and stream results
866
+ canary test-suite run
867
+ canary test-suite run --tag smoke
868
+ canary test-suite run --property "My App" --environment staging --verbose
869
+
870
+ # Query test suite results
871
+ canary test-suite list
872
+ canary test-suite get <id> --failures-only
873
+ canary test-suite diagnostics <workflowId> <runId>
874
+ canary test-suite steps <workflowId> <runId>
875
+ \`\`\`
876
+
877
+ Run options: \`--token\`, \`--property\`, \`--environment\`, \`--tag\`, \`--name-pattern\`, \`--verbose\`
878
+
879
+ ---
880
+
881
+ ### Browser Sessions
882
+
883
+ Start and control browser sessions for interactive testing.
884
+
885
+ #### Session lifecycle
886
+
887
+ \`\`\`bash
888
+ canary session start # Start a new session
889
+ canary session start --credential "admin" # Start with a credential
890
+ canary session start --url https://example.com --headless
891
+ canary session list # List active sessions
892
+ canary session status # Show session details
893
+ canary session stop # Stop the current session
894
+ canary session stop --all # Stop all sessions
895
+ canary session load-credential # Load a credential into a running session
896
+ canary session allow-screenshots # Grant Claude auto-read for screenshots
897
+ \`\`\`
898
+
899
+ #### Browser actions
900
+
901
+ \`\`\`bash
902
+ # Navigation
903
+ canary session navigate --url https://example.com
904
+ canary session back
905
+
906
+ # Observe the page
907
+ canary session snapshot # Get accessibility tree
908
+ canary session snapshot --mode screenshot # Get a screenshot
909
+ canary session snapshot --mode both # Tree + screenshot
910
+ canary session snapshot --search "Login" # Search for text
911
+ canary session snapshot --expand e5 # Expand a collapsed ref
912
+ canary session screenshot --full-page # Full-page screenshot
913
+
914
+ # Interact with elements (use ref from snapshot)
915
+ canary session click --ref e12
916
+ canary session type --ref e8 --text "hello" --submit
917
+ canary session fill --fields '[{"ref":"e1","value":"user@example.com"},{"ref":"e2","value":"pass123"}]'
918
+ canary session select --ref e15 --value "option-1"
919
+ canary session hover --ref e3
920
+ canary session press --key Enter
921
+ canary session scroll --direction down --amount 3
922
+
923
+ # Tabs
924
+ canary session tabs # List tabs
925
+ canary session tabs --action new # Open new tab
926
+ canary session tabs --action select --index 0 # Switch to tab
927
+ canary session tabs --action close # Close current tab
928
+
929
+ # Debugging
930
+ canary session wait --text "Success" --timeout 5000
931
+ canary session console --only-errors # Show console messages
932
+ canary session network # Show network requests
933
+ canary session evaluate --js "document.title" # Run JavaScript
934
+ \`\`\`
935
+
936
+ Common options: \`--session <id|name>\` (target a specific session), \`--json\`, \`--env\`
937
+
938
+ #### Control (for cloud-connected sessions)
939
+
940
+ \`\`\`bash
941
+ canary session take-control # Force CLI control (pauses cloud)
942
+ canary session resume-cloud # Resume cloud execution
943
+ \`\`\`
944
+
945
+ ---
946
+
947
+ ### Recording
948
+
949
+ Record browser interactions for creating test workflows.
950
+
951
+ \`\`\`bash
952
+ canary record
953
+ canary record --credential admin --url https://example.com
954
+ canary record --output ./recordings --no-upload # Save locally only
955
+ \`\`\`
956
+
957
+ Options: \`--credential\`, \`--url\`, \`--output\`, \`--env\`, \`--no-upload\`
958
+
959
+ ---
960
+
961
+ ### Workflows
962
+
963
+ Inspect and manage workflow definitions.
964
+
965
+ \`\`\`bash
966
+ # List and search
967
+ canary workflow list
968
+ canary workflow list --search "checkout" --status published --json
969
+
970
+ # Get workflow definition
971
+ canary workflow get <workflowId> --format markdown
972
+ canary workflow get <workflowId> --json
973
+
974
+ # Create from file or stdin
975
+ canary workflow create --from-file workflow.json
976
+ canary workflow create --run-and-publish --property "My App"
977
+
978
+ # Update workflow
979
+ canary workflow patch <workflowId> --name "New Name" --status published
980
+
981
+ # Run a workflow
982
+ canary workflow run <workflowId> --stream
983
+
984
+ # Replace all nodes/edges
985
+ canary workflow sync <workflowId> --from-file updated.json
986
+
987
+ # Authoring guide
988
+ canary workflow guide
989
+ canary workflow guide selectors
990
+ \`\`\`
991
+
992
+ List filters: \`--search\`, \`--flow-type\` (standard|setup|seed|teardown), \`--status\` (draft|published), \`--page\`, \`--page-size\`
993
+
994
+ Workflow statuses: \`draft\` = editable, not run by suites. \`published\` = active, included in regression runs. Reliability is tracked automatically by the \`health\` dimension \u2014 a flow that keeps failing in suites becomes \`degraded\` and is skipped (shown "degraded \u2014 not run") until it's edited or manually re-enabled.
995
+
996
+ ---
997
+
998
+ ### Issues
999
+
1000
+ Search and inspect QA issues detected by Canary.
1001
+
1002
+ \`\`\`bash
1003
+ # List issues
1004
+ canary issues list
1005
+ canary issues list --severity high --status open --format markdown
1006
+ canary issues list --search "timeout" --property-id <uuid>
1007
+
1008
+ # Get issue details
1009
+ canary issues get <issue-id>
1010
+ canary issues get <issue-id> --format markdown
1011
+ \`\`\`
1012
+
1013
+ List options: \`--search\`, \`--severity\` (low|medium|high|unknown), \`--status\` (open|closed|not_a_bug), \`--property-id\`, \`--page\`, \`--page-size\`, \`--json\`, \`--format markdown\`
1014
+
1015
+ ---
1016
+
1017
+ ### Release QA (CI/CD)
1018
+
1019
+ Trigger and monitor Release QA runs from CI pipelines.
1020
+
1021
+ \`\`\`bash
1022
+ canary release trigger --property-id <uuid>
1023
+ canary release status <run-id> --json
1024
+ canary release run --property-id <uuid> --timeout 600 # Trigger and poll
1025
+ \`\`\`
1026
+
1027
+ ---
1028
+
1029
+ ### Properties & Credentials
1030
+
1031
+ \`\`\`bash
1032
+ canary property list # List properties and linked repos
1033
+ canary credential list # List credentials with environment info
1034
+ \`\`\`
1035
+
1036
+ ---
1037
+
1038
+ ### Superadmin Commands
1039
+
1040
+ These commands require superadmin access.
1041
+
1042
+ \`\`\`bash
1043
+ # Knobs (global runtime config)
1044
+ canary knobs list
1045
+ canary knobs get <key>
1046
+ canary knobs set <key> <value> --type boolean|number|string
1047
+ canary knobs toggle <key>
1048
+ canary knobs delete <key>
1049
+ canary knobs lifecycle <key> --stage <stage>
1050
+
1051
+ # Feature flags (per-org gating)
1052
+ canary feature-flag list
1053
+ canary feature-flag create <name> --description "..."
1054
+ canary feature-flag enable <name> --org <orgId>
1055
+ canary feature-flag disable <name> --org <orgId>
1056
+ canary feature-flag delete <name>
1057
+ canary feature-flag lifecycle <name> --stage <stage>
1058
+
1059
+ # Database (read-only)
1060
+ canary psql "SELECT * FROM knobs LIMIT 5" --json
1061
+
1062
+ # Redis (read-only)
1063
+ canary redis "GET some:key" --json
1064
+
1065
+ # Documentation pages
1066
+ canary docs <sub-command>
1067
+
1068
+ # Debug sessions
1069
+ canary debug-session --env dev --json
1070
+ \`\`\`
1071
+
1072
+ ---
1073
+
1074
+ ## Common Patterns
1075
+
1076
+ | Pattern | Description |
1077
+ |---------|-------------|
1078
+ | \`--json\` | Raw JSON output (most commands) |
1079
+ | \`--format markdown\` | Markdown-formatted output (issues, workflows) |
1080
+ | \`--env <env>\` | Target environment: \`local\`, \`dev\`, \`prod\` |
1081
+ | \`--verbose, -v\` | Show all events / detailed output |
1082
+ | \`-h, --help\` | Show help for any command |
1083
+
1084
+ ### Environment variables
1085
+
1086
+ | Variable | Default | Description |
1087
+ |----------|---------|-------------|
1088
+ | \`CANARY_API_URL\` | \`https://api.trycanary.ai\` | API endpoint |
1089
+ | \`CANARY_APP_URL\` | \`https://app.trycanary.ai\` | App URL for login |
1090
+ | \`CANARY_API_TOKEN\` | \u2014 | API key (alternative to \`canary login\`) |
1091
+
1092
+ ## Getting Help
1093
+
1094
+ \`\`\`bash
1095
+ canary help # Show all commands
1096
+ canary <command> --help # Show help for a specific command
1097
+ canary version # Show CLI version
1098
+ canary upgrade # Upgrade to latest version
1099
+ \`\`\`
1100
+ `
1101
+ };
1102
+
1103
+ // src/skill-templates/test-cases.ts
1104
+ var testCasesTemplate = {
1105
+ name: "test-cases",
1106
+ description: "Author high-quality test cases as `.canary.md` specs that compile straight into Canary workflows. Invoke when the user asks to write test cases, draft a test plan, plan QA scenarios, decide what to test, or convert a PRD/ticket/design into tests.",
1107
+ version: "2.2.0",
1108
+ render: () => `---
1109
+ name: test-cases
1110
+ version: 2.2.0
1111
+ description: Author high-quality test cases as \`.canary.md\` specs that compile straight into Canary workflows. Invoke when the user asks to write test cases, draft a test plan, plan QA scenarios, decide what to test, or convert a PRD/ticket/design into tests.
1112
+ allowed-tools: Read, Grep, Glob, Bash, Write
1113
+ ---
1114
+
1115
+ # Test Case Authoring (Canary)
1116
+
1117
+ Use this skill when the user asks for test cases, a test plan, QA scenarios, "what should we test for X", or wants to turn a PRD / ticket / design into tests. The output is one or more **\`.canary.md\` spec files** \u2014 each file is one workflow, one journey, one test case. \`canary spec compile <file>\` turns them into runnable Canary workflows; \`canary spec validate <file>\` enforces the structural rules below.
1118
+
1119
+ The skill has three layers:
1120
+ 1. **Industry rubric** \u2014 what makes a test case good (ISTQB-aligned).
1121
+ 2. **Spec mapping** \u2014 how each rubric piece becomes a section of \`.canary.md\`.
1122
+ 3. **Domain Context** \u2014 customer-specific sources of truth, filled in once.
1123
+
1124
+ Read the Domain Context block at the bottom **first**. If it is unfilled, follow the discovery instructions there before authoring any cases.
1125
+
1126
+ ---
1127
+
1128
+ ## 1. The 7-rule rubric
1129
+
1130
+ Each case must satisfy all seven. If a rule cannot be satisfied, say so explicitly and explain why \u2014 don't silently skip.
1131
+
1132
+ ### 1. One coherent journey per case (atomic, but not micro)
1133
+ A case covers a single coherent journey \u2014 one user objective, possibly with several verifications along the way. "Atomic" means *coherent*, not *minimal*: if multiple checks share the same setup and naturally flow from one another, they belong in the same case.
1134
+
1135
+ - Good (coherent journey, multiple checks): "Onboard an org: create org \u2192 invite a user \u2192 claim invite \u2192 land on dashboard." Each step verifies its own outcome inline.
1136
+ - Good (single short journey): "Submit a ticket and confirm it appears in the queue."
1137
+ - Bad (unrelated jobs): "Submit a ticket, edit a user, generate a report." \u2014 three journeys, three separate \`.canary.md\` files.
1138
+
1139
+ **Rule of thumb:** if isolating a check (e.g. "tagging works") would force you to duplicate most of another case's setup, fold it in as an inline assertion on the larger journey instead. Splitting only pays off when the setup is genuinely different or the failure modes are independent.
1140
+
1141
+ ### 2. Specific, observable expected results
1142
+ Every assertion must be something a stranger could verify without judgement.
1143
+ - Good: "URL ends with \`/dashboard\`." / "A row with name \`X\` appears in the table."
1144
+ - Bad: "The page looks correct." / "It works."
1145
+
1146
+ Lint enforces this as **R1**: a spec with zero \`fail\`-severity assertions warns.
1147
+
1148
+ ### 3. Independent and self-sufficient
1149
+ A case must not depend on another case running first or on long-lived shared state. Provision data inline (\`## Inputs\` + \`## Scenarios\`, or a \`**Seed**\` node) and clean up in \`## Cleanup\`. Use macros (\\$EMAIL, \\$UUID) so reruns don't collide.
1150
+
1151
+ ### 4. Traceable
1152
+ Every case carries \`requirementRef:\` in frontmatter pointing at the ticket / PRD / design. If you can't find one, flag the gap to the user \u2014 untraceable specs rot. Lint **R2** warns when missing.
1153
+
1154
+ ### 5. Risk-prioritized
1155
+ Tag each case with a priority via the \`tags:\` array. Use one of: \`p0\`, \`p1\`, \`p2\`.
1156
+ - **p0** \u2014 happy path of the core promise; if this breaks, the feature is broken.
1157
+ - **p1** \u2014 primary edge cases, validation, role variants, common error states.
1158
+ - **p2** \u2014 rare conditions, visual/agentic checks, low-frequency edges.
1159
+
1160
+ ### 6. Includes preconditions, data, and cleanup
1161
+ - **Auth/role preconditions** \u2192 \`actor:\` in frontmatter.
1162
+ - **Other preconditions** (feature flags, seeded data, sister flows) \u2192 bulleted prose in \`## Preflight\`.
1163
+ - **Test data** \u2192 \`## Inputs\` (with macros) and per-variant \`## Scenarios\`.
1164
+ - **Teardown** \u2192 bulleted prose in \`## Cleanup\`.
1165
+
1166
+ ### 7. Written as human intent, not UI mechanics
1167
+ Action titles describe *what the user is trying to do*; the optional \`> blockquote\` underneath gives just enough context for the recorder/agent. Don't write click-by-click sequences.
1168
+ - Good: \`**Action \u2014 Assign the ticket to Support**\`
1169
+ - Bad: \`**Action \u2014 Click 'Assign' dropdown, scroll, click 'Support', click Save**\`
1170
+
1171
+ Lint **R9** warns when an action reads as click-by-click.
1172
+
1173
+ ---
1174
+
1175
+ ## 2. Coverage checklist
1176
+
1177
+ Before handing back a set of specs, sanity-check coverage. For each, either include a case or note why it doesn't apply:
1178
+ - Happy path (p0)
1179
+ - Primary edges / boundary inputs
1180
+ - Negative paths / validation errors
1181
+ - Permissions and role variants (admin / member / unauthenticated)
1182
+ - Empty, loading, and error states
1183
+ - Cross-entity side effects (does this change something visible elsewhere?)
1184
+ - Cleanup / undo where applicable
1185
+
1186
+ Aim for the smallest set that proves the feature works. Five sharp cases beat twenty fuzzy ones.
1187
+
1188
+ ---
1189
+
1190
+ ## 3. Authoring \`.canary.md\`
1191
+
1192
+ \`.canary.md\` is one workflow per file. Frontmatter sets identity; H2 sections carry prose and structured config; \`## Journey\` is the linear test itself. The compiler validates structure; lint enforces the rubric semantics above.
1193
+
1194
+ Run these for the definitive grammar \u2014 do **not** restate them here:
1195
+
1196
+ \`\`\`bash
1197
+ canary spec --help # validate / compile / diff commands
1198
+ canary spec validate <file> # parse + lint offline; enforce the rules in \xA71
1199
+ canary spec compile <file> # create or update the workflow (prints the live URL)
1200
+ canary workflow guide # node-shape and macro reference
1201
+ \`\`\`
1202
+
1203
+ After \`canary spec compile\`, echo the printed \`URL:\` line back to the user verbatim \u2014 it points at the workflow you just created (\`https://app.trycanary.ai/flows/<id>\` for prod, \`https://app.dev.trycanary.ai/flows/<id>\` for dev). Don't construct it yourself.
1204
+
1205
+ Mapping at a glance:
1206
+
1207
+ | Rubric piece | Where it lives in \`.canary.md\` |
1208
+ |-----------------------------------------|---------------------------------------------------------------------------|
1209
+ | Case title | \`frontmatter.name\` |
1210
+ | Requirement / ticket ref | \`frontmatter.requirementRef\` |
1211
+ | Priority (rule 5) | \`tags: [p0]\` (or p1/p2) in frontmatter |
1212
+ | Auth role precondition | \`actor: <X>\` + \`authenticated: true\` in frontmatter. \`<X>\` is a credential **template** name by default (e.g. \`actor: Admin\` or the explicit \`actor: template Admin\`), or a specific raw credential via \`actor: credential <name>\` when the case genuinely needs one named account. Templates resolve at run/suite level; raw credentials bind a single fixed account. List templates with \`canary credential-template list --property "<property>"\`; list credentials with \`canary credential list\`. |
1213
+ | Feature flag / seeded data preconditions| Bulleted prose in \`## Preflight\` ("Feature flag X enabled.", "Seed workflow Y published.") |
1214
+ | One-paragraph user-visible promise | \`## Spirit\` (blockquote) |
1215
+ | Workflow-level declared inputs | \`## Inputs\` (e.g. \`- foo: \\$EMAIL\`) \u2014 parses to \`workflows.inputs_json\`, surfaces in Workflow Settings \u2192 Inputs |
1216
+ | Per-step bound values | \`- field X: Y\` under an Action \u2014 populates *that* step's Inputs panel with \`Y\` as the default (compiler mirrors fields \u2192 \`dataRequirements\` + \`inputValues\` on the node). Prefer \`## Inputs\` for values shared across actions. |
1217
+ | Variants (free / enterprise, etc.) | \`## Scenarios\` with per-scenario \`values:\` / \`credentials:\` overrides |
1218
+ | Steps (human intent) | \`## Journey\` \u2014 \`**Navigate to** \u2026\`, \`**Action \u2014 \u2026**\`, \`**Seed** \u2026\` |
1219
+ | Inline expected results | \`- assert (fail \\| warn): <condition>\` under the action that produced it |
1220
+ | Standalone assertion | \`**Assert** <condition>\` node (prefer inline when possible) |
1221
+ | Role change mid-journey | \`**Switch role to** <X>\` \u2014 mirrors \`actor:\`. \`<X>\` is a credential template name by default (\`**Switch role to** Admin\`), or \`credential <name>\` to bind a specific raw credential (\`**Switch role to** credential admin-bob\`). Compiles to a login node with \`performLogin: true\`. Repeat as many times as needed in one journey. |
1222
+ | Cross-action data flow | \`- capture: <name>\` on the upstream action, \`\\$var.<name>\` downstream |
1223
+ | Cleanup | Bulleted prose in \`## Cleanup\` |
1224
+
1225
+ When a journey shares setup and naturally flows end-to-end, emit **one** \`.canary.md\` with multiple action nodes and inline assertions. When journeys are unrelated, emit **one file per journey**; if one journey depends on data created by another, link them with \`**Seed** <spec-name>\` or \`**Setup** <spec-name>\` at the top of the dependent file.
1226
+
1227
+ ### Inputs defaults \u2014 the decision rule
1228
+
1229
+ When you need a value (workflow-level \`## Inputs\` or step-level \`- field\`), pick where it comes from in this order:
1230
+
1231
+ 1. **Names existing data in the target environment** \u2014 a property name, a credential, a seeded record, an existing org/user/team. **Stop and ask the user for the value before writing the spec.** Don't hand-pick. The right sources of truth:
1232
+ - \`canary property list\` \u2014 properties (target apps)
1233
+ - \`canary credential-template list --property "<property>"\` \u2014 credential templates (these are what \`actor:\` and \`- credential \u2026\` reference)
1234
+ - \`canary workflow list --search <term>\` \u2014 existing workflows to seed/link to
1235
+ - \`canary feature-flag list --env <env>\` \u2014 flag names for Preflight prose
1236
+ 2. **Synthesizable** \u2014 a new email, a UUID, a throwaway string, an ISO date. Pick a macro (\`\\$EMAIL\`, \`\\$UUID\`, \`\\$ISO_DATE\`) and move on without asking. These render as pills in the Inputs panel and resolve at run time.
1237
+ 3. **Prefer credential templates** for any auth/role-scoped input. \`actor: Admin\` (frontmatter) and \`- credential admin: Admin\` (inputs) are cleaner than passing \`username:\` + \`password:\` and work across environments. Reach for the raw-credential form (\`actor: credential <name>\`, \`**Switch role to** credential <name>\`, \`- credential admin: <name>\`) only when the case genuinely requires a specific fixed account.
1238
+
1239
+ ---
1240
+
1241
+ ## 4. Output format
1242
+
1243
+ For each case, write a \`.canary.md\` file alongside a one-row summary line. Default location: \`canary/specs/<slug>.canary.md\` (confirm with the user if their repo uses a different layout \u2014 check Domain Context below).
1244
+
1245
+ ### Summary table
1246
+
1247
+ | file | name | priority | requirementRef |
1248
+ |-----------------------------------|--------------------------------------------|----------|------------------|
1249
+ | canary/specs/submit-ticket.canary.md | Submit a ticket and confirm it appears in the queue | p0 | <ticket/PRD ref> |
1250
+
1251
+ ### Per-case file
1252
+
1253
+ \`\`\`markdown
1254
+ ---
1255
+ name: <one user goal, action-oriented>
1256
+ actor: <credential template name from Domain Context \u2014 verbatim, e.g. "Admin"; or \`credential <name>\` for a specific raw credential>
1257
+ authenticated: true
1258
+ tags: [p0] # or p1 / p2; add other tags from Domain Context's tagging conventions
1259
+ requirementRef: <ticket id, PRD path, design link>
1260
+ ---
1261
+
1262
+ # <same as name>
1263
+
1264
+ ## Spirit
1265
+ > <one short paragraph: the user-visible promise this case proves>
1266
+
1267
+ ## Preflight
1268
+ - <feature flag / seed / sister flow / role precondition, one per bullet>
1269
+
1270
+ ## Cleanup
1271
+ - <records to delete, flags to reset>
1272
+
1273
+ ## Inputs
1274
+ - <name>: <value or macro>
1275
+
1276
+ ## Journey
1277
+
1278
+ **Navigate to** \\\`/some/path\\\`.
1279
+
1280
+ **Action \u2014 <human intent>**
1281
+ > <one-sentence context for the recorder/agent>
1282
+ - field <name> [(type)]: <value or \\$input>
1283
+ - capture: <name> # optional, only if a later step needs it via \\$var.<name>
1284
+ - assert (fail): <observable condition>
1285
+ - assert (warn): <softer signal>
1286
+
1287
+ **End**.
1288
+ \`\`\`
1289
+
1290
+ Keep journeys short (3\u20138 nodes). If a case grows past that, it's probably two cases.
1291
+
1292
+ ---
1293
+
1294
+ ## 5. Worked example (generic)
1295
+
1296
+ Request: "Test the support ticket flow."
1297
+
1298
+ | file | name | priority | requirementRef |
1299
+ |-------------------------------------------|-----------------------------------------------------|----------|----------------|
1300
+ | canary/specs/submit-ticket.canary.md | Submit a ticket and confirm it appears in the queue | p0 | <fill in> |
1301
+ | canary/specs/ticket-required-fields.canary.md | Required-field validation blocks submit | p1 | <fill in> |
1302
+ | canary/specs/ticket-non-admin-reassign.canary.md | Non-admin cannot reassign a ticket | p1 | <fill in> |
1303
+
1304
+ \`canary/specs/submit-ticket.canary.md\`:
1305
+
1306
+ \`\`\`markdown
1307
+ ---
1308
+ name: Submit a ticket and confirm it appears in the queue
1309
+ actor: Admin
1310
+ authenticated: true
1311
+ tags: [p0, smoke, support]
1312
+ requirementRef: <fill in>
1313
+ ---
1314
+
1315
+ # Submit a ticket and confirm it appears in the queue
1316
+
1317
+ ## Spirit
1318
+ > A member can file a support ticket from the in-app form and immediately
1319
+ > see it land in their team's queue without a refresh.
1320
+
1321
+ ## Preflight
1322
+ - At least one Support team exists in the test org (seed workflow \\\`support-team-seed\\\`).
1323
+ - Feature flag \\\`support_inbox_v3\\\` is enabled.
1324
+
1325
+ ## Cleanup
1326
+ - Delete the created ticket.
1327
+
1328
+ ## Inputs
1329
+ - subject: "QA \\$UUID"
1330
+ - priority: high
1331
+
1332
+ ## Journey
1333
+
1334
+ **Navigate to** \\\`/support/new\\\`.
1335
+
1336
+ **Action \u2014 Submit the ticket**
1337
+ > Fill subject and priority, then submit.
1338
+ - field subject: \\$subject
1339
+ - field priority (select): \\$priority
1340
+ - assert (warn): Toast contains "Ticket created"
1341
+ - assert (fail): URL ends with \\\`/support\\\`
1342
+
1343
+ **Navigate to** \\\`/support/queue\\\`.
1344
+
1345
+ **Action \u2014 Confirm it appears in the queue**
1346
+ > Locate the new ticket in the team queue.
1347
+ - assert (fail): A row matching \\\`\\$subject\\\` is visible
1348
+
1349
+ **End**.
1350
+ \`\`\`
1351
+
1352
+ After writing, run \`canary spec validate canary/specs/submit-ticket.canary.md\` and resolve any errors / warnings before handing back.
1353
+
1354
+ ---
1355
+
1356
+ ## 6. Anti-patterns
1357
+
1358
+ - Mega-files that try to cover five unrelated journeys.
1359
+ - The opposite: splitting one coherent journey into N tiny files that each re-create the same setup. Bundle them into one \`## Journey\` instead.
1360
+ - Click-by-click action titles ("Click X, scroll, click Y").
1361
+ - "Looks correct" / "everything works" assertions.
1362
+ - Hardcoded emails, IDs, or names that collide on rerun. Use macros (\\$EMAIL, \\$UUID).
1363
+ - Specs that depend on another spec having run first without an explicit \`**Seed**\` / \`**Setup**\` node.
1364
+ - Skipping \`requirementRef\` because "it's obvious".
1365
+ - Asserting state via toasts only \u2014 confirm the underlying record changed.
1366
+ - Dropping the priority tag \u2014 risk-rank every case (\`p0\` / \`p1\` / \`p2\`).
1367
+
1368
+ ---
1369
+
1370
+ ## 7. Domain Context
1371
+
1372
+ <!-- AGENT/CUSTOMER: fill in this block on first use of the skill, then commit it. -->
1373
+ <!-- If any field below is "<unfilled>", do a discovery pass before authoring cases: -->
1374
+ <!-- - Read README.md, CLAUDE.md, AGENTS.md, docs/, specs/ for requirement and design pointers. -->
1375
+ <!-- - Scan recent git log for ticket-tracker references (JIRA-123, LIN-456, #issue). -->
1376
+ <!-- - Run \`canary property list\` and \`canary credential-template list --property "<property>"\` to learn properties + the credential templates that map to \`actor:\` values. -->
1377
+ <!-- - (\`canary credential list\` shows live credentials \u2014 useful, but the \`actor:\` field references credential *templates*, not credentials.) -->
1378
+ <!-- - Run \`canary workflow list --search <feature>\` to avoid duplicating existing workflows. -->
1379
+ <!-- - Run \`canary feature-flag list\` to confirm flag names referenced in Preflight prose. -->
1380
+ <!-- - Propose values to the user, get confirmation, then edit this block in place. -->
1381
+
1382
+ - **Requirements / PRDs**: <unfilled \u2014 paths or URLs, e.g. \`docs/specs/\`, Notion space, Linear project>
1383
+ - **Designs**: <unfilled \u2014 Figma links, design system doc>
1384
+ - **Ticket tracker**: <unfilled \u2014 Jira/Linear project key + how to format \`requirementRef\` (e.g. \`PROJ-123\` or a URL)>
1385
+ - **Default property**: <unfilled \u2014 the property name to target; find via \`canary property list\`>
1386
+ - **Default environment**: <unfilled \u2014 \`prod\` | \`staging\` | \`dev\`>
1387
+ - **Credentials / role matrix** (\`actor:\` is a credential template name by default \u2014 list via \`canary credential-template list --property "<property>"\`. Prefix with \`credential \` to bind a specific raw credential \u2014 list via \`canary credential list\`. Use the template form unless the case genuinely needs a fixed named account.): <unfilled \u2014 list the credential template names available on the default property, with a one-line note on which to use when. Common shape: \`actor: Admin\` (standard user), \`actor: Superadmin\` (full access).>
1388
+ - **Tagging conventions**: <unfilled \u2014 e.g. \`smoke\`, \`regression\`, plus the priority tags \`p0\` / \`p1\` / \`p2\`>
1389
+ - **Spec directory**: <unfilled \u2014 where \`.canary.md\` files live in this repo, e.g. \`canary/specs/\`>
1390
+ - **Existing workflows to extend or avoid duplicating**: <unfilled \u2014 search via \`canary workflow list --search <term>\`>
1391
+ - **Glossary / domain terms**: <unfilled \u2014 terms that show up in tickets and should appear verbatim in spec names>
1392
+ - **Other sources of truth**: <unfilled \u2014 runbooks, support tickets, analytics dashboards>
1393
+
1394
+ When this block is fully populated, every case you author should reference at least the **Requirements**, **Default property**, **Spec directory**, and **Credentials** entries.
1395
+ `
1396
+ };
1397
+
1398
+ // src/skill-templates/debug-run.ts
1399
+ import { readFileSync, readdirSync } from "fs";
1400
+ import { dirname, join } from "path";
1401
+ import { fileURLToPath } from "url";
1402
+ var HERE = dirname(fileURLToPath(import.meta.url));
1403
+ function resolveSkillDir() {
1404
+ const candidates = [
1405
+ join(HERE, "..", "..", "skills", "debug-run"),
1406
+ // source tree
1407
+ join(HERE, "skills", "debug-run"),
1408
+ // dist
1409
+ join(HERE, "..", "skills", "debug-run")
1410
+ // dist split-chunk variant
1411
+ ];
1412
+ for (const c of candidates) {
1413
+ try {
1414
+ readFileSync(join(c, "version.json"), "utf-8");
1415
+ return c;
1416
+ } catch {
1417
+ }
1418
+ }
1419
+ throw new Error(
1420
+ `Could not locate debug-run skill assets relative to ${HERE}. Tried: ${candidates.join(", ")}`
1421
+ );
1422
+ }
1423
+ var SKILL_DIR = resolveSkillDir();
1424
+ var HISTORY_DIR = join(SKILL_DIR, "history");
1425
+ var versionMeta = JSON.parse(
1426
+ readFileSync(join(SKILL_DIR, "version.json"), "utf-8")
1427
+ );
1428
+ function readVersion(version) {
1429
+ return readFileSync(join(HISTORY_DIR, `${version}.md`), "utf-8");
1430
+ }
1431
+ function listVersionsOnDisk() {
1432
+ return readdirSync(HISTORY_DIR).filter((f) => f.endsWith(".md")).map((f) => f.slice(0, -3));
1433
+ }
1434
+ var previousVersions = {};
1435
+ for (const v of listVersionsOnDisk()) {
1436
+ if (v !== versionMeta.current) {
1437
+ previousVersions[v] = () => readVersion(v);
1438
+ }
1439
+ }
1440
+ var debugRunTemplate = {
1441
+ name: "debug-run",
1442
+ description: "Diagnose why a workflow test failed \u2014 root-cause an end-to-end run from its materialised artifacts",
1443
+ version: versionMeta.current,
1444
+ render: () => readVersion(versionMeta.current),
1445
+ previousVersions
1446
+ };
1447
+
1448
+ // src/skill-templates/index.ts
1449
+ var SKILL_TEMPLATES = {
1450
+ "canary-basic": canaryBasicTemplate,
1451
+ "agent-tasks": agentTasksTemplate,
1452
+ "issue-log-xref": issueLogXrefTemplate,
1453
+ "browser-testing": browserTestingTemplate,
1454
+ "test-cases": testCasesTemplate,
1455
+ "debug-run": debugRunTemplate
1456
+ };
1457
+
1458
+ // src/skill.ts
1459
+ var SUPPORTED_AGENTS = ["claude", "codex", "copilot"];
1460
+ function isAgentType(value) {
1461
+ return value === "claude" || value === "codex" || value === "copilot";
1462
+ }
1463
+ function detectAgent(options = {}) {
1464
+ const env = options.env ?? process.env;
1465
+ const cwd = options.cwd ?? process.cwd();
1466
+ const pathExists = options.exists ?? existsSync;
1467
+ if (env.CLAUDE) return "claude";
1468
+ if (env.CODEX || env.CODEX_CI || env.CODEX_THREAD_ID || env.CODEX_MANAGED_PACKAGE_ROOT || env.AGENT_MODE === "codex")
1469
+ return "codex";
1470
+ if (env.GITHUB_COPILOT || env.COPILOT_AGENT || env.AGENT_MODE === "copilot")
1471
+ return "copilot";
1472
+ const foundAgents = SUPPORTED_AGENTS.filter(
1473
+ (agent) => pathExists(join2(cwd, agent === "copilot" ? ".github" : `.${agent}`))
1474
+ );
1475
+ if (foundAgents.length === 1) return foundAgents[0] ?? null;
1476
+ return null;
1477
+ }
1478
+ function resolveInstallDir(skillName, agent, personal, options = {}) {
1479
+ const base = personal ? options.home ?? homedir() : options.cwd ?? process.cwd();
1480
+ switch (agent) {
1481
+ case "claude":
1482
+ return join2(base, ".claude", "skills", skillName);
1483
+ case "codex":
1484
+ return join2(base, ".codex", "skills", skillName);
1485
+ case "copilot":
1486
+ return personal ? join2(base, ".copilot", "skills", skillName) : join2(base, ".github", "skills", skillName);
1487
+ }
1488
+ }
1489
+ function printSkillHelp() {
1490
+ const lines = [
1491
+ "Usage:",
1492
+ " canary skill <name> Output skill template to stdout",
1493
+ " canary skill install <name> Install skill (auto-detects agent)",
1494
+ " canary skill install <name> --agent codex Install for a specific agent",
1495
+ " canary skill install <name> --personal Install to ~/.<agent>/skills/",
1496
+ " canary skill list List available skill templates",
1497
+ " canary skill sync Print manifest + instructions for the",
1498
+ " agent to version-compare and update",
1499
+ " installed skills",
1500
+ " canary skill diff <name> [--from V] [--to V] Unified diff between two versions of",
1501
+ " a skill (defaults: previous \u2192 current)",
1502
+ " canary skill versions <name> List all known versions of a skill",
1503
+ "",
1504
+ "Agent auto-detection:",
1505
+ " The install command detects which AI agent is running and writes the",
1506
+ " SKILL.md to the correct location:",
1507
+ "",
1508
+ " Agent Project path Personal path",
1509
+ " \u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
1510
+ " claude .claude/skills/<name>/ ~/.claude/skills/<name>/",
1511
+ " codex .codex/skills/<name>/ ~/.codex/skills/<name>/",
1512
+ " copilot .github/skills/<name>/ ~/.copilot/skills/<name>/",
1513
+ "",
1514
+ " Detection checks (in order):",
1515
+ " 1. CLAUDE env var \u2192 claude",
1516
+ " 2. CODEX_* env vars \u2192 codex",
1517
+ " 3. GITHUB_COPILOT env var \u2192 copilot",
1518
+ " 4. .claude/.codex/.github dirs \u2192 whichever single one exists",
1519
+ " 5. If ambiguous, use --agent to specify explicitly",
1520
+ "",
1521
+ "Available templates:"
1522
+ ];
1523
+ for (const [key, template] of Object.entries(SKILL_TEMPLATES)) {
1524
+ lines.push(
1525
+ ` ${key.padEnd(24)} v${template.version.padEnd(7)} ${template.description}`
1526
+ );
1527
+ }
1528
+ console.log(lines.join("\n"));
1529
+ }
1530
+ function compareSemver(a, b) {
1531
+ const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
1532
+ const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
1533
+ for (let i = 0; i < 3; i++) {
1534
+ const d = (pa[i] ?? 0) - (pb[i] ?? 0);
1535
+ if (d !== 0) return d < 0 ? -1 : 1;
1536
+ }
1537
+ return 0;
1538
+ }
1539
+ function knownVersions(name) {
1540
+ const t = SKILL_TEMPLATES[name];
1541
+ if (!t) return [];
1542
+ const all = /* @__PURE__ */ new Set([t.version, ...Object.keys(t.previousVersions ?? {})]);
1543
+ return [...all].sort(compareSemver);
1544
+ }
1545
+ function renderAtVersion(name, version) {
1546
+ const t = SKILL_TEMPLATES[name];
1547
+ if (!t) return null;
1548
+ if (version === t.version) return t.render();
1549
+ const prev = t.previousVersions?.[version];
1550
+ return prev ? prev() : null;
1551
+ }
1552
+ function parseFlagArgs(argv) {
1553
+ const positional = [];
1554
+ const flags = {};
1555
+ for (let i = 0; i < argv.length; i++) {
1556
+ const a = argv[i];
1557
+ if (a.startsWith("--")) {
1558
+ const key = a.slice(2);
1559
+ const next = argv[i + 1];
1560
+ if (next !== void 0 && !next.startsWith("--")) {
1561
+ flags[key] = next;
1562
+ i++;
1563
+ } else {
1564
+ flags[key] = true;
1565
+ }
1566
+ } else {
1567
+ positional.push(a);
1568
+ }
1569
+ }
1570
+ return { positional, flags };
1571
+ }
1572
+ function runVersions(argv) {
1573
+ const [name] = argv;
1574
+ if (!name || !SKILL_TEMPLATES[name]) {
1575
+ console.error(`Usage: canary skill versions <name>`);
1576
+ process.exit(1);
1577
+ }
1578
+ const versions = knownVersions(name);
1579
+ const current = SKILL_TEMPLATES[name].version;
1580
+ for (const v of versions) {
1581
+ console.log(v === current ? `${v} (current)` : v);
1582
+ }
1583
+ }
1584
+ function jsUnifiedDiff(a, b, labelA, labelB) {
1585
+ const aLines = a.split("\n");
1586
+ const bLines = b.split("\n");
1587
+ const n = aLines.length;
1588
+ const m = bLines.length;
1589
+ const dp = Array.from(
1590
+ { length: n + 1 },
1591
+ () => Array.from({ length: m + 1 }).fill(0)
1592
+ );
1593
+ for (let i2 = n - 1; i2 >= 0; i2--) {
1594
+ for (let j2 = m - 1; j2 >= 0; j2--) {
1595
+ dp[i2][j2] = aLines[i2] === bLines[j2] ? dp[i2 + 1][j2 + 1] + 1 : Math.max(dp[i2 + 1][j2], dp[i2][j2 + 1]);
1596
+ }
1597
+ }
1598
+ const out = [`--- ${labelA}`, `+++ ${labelB}`];
1599
+ let i = 0;
1600
+ let j = 0;
1601
+ while (i < n && j < m) {
1602
+ if (aLines[i] === bLines[j]) {
1603
+ out.push(` ${aLines[i]}`);
1604
+ i++;
1605
+ j++;
1606
+ } else if (dp[i + 1][j] >= dp[i][j + 1]) {
1607
+ out.push(`-${aLines[i++]}`);
1608
+ } else {
1609
+ out.push(`+${bLines[j++]}`);
1610
+ }
1611
+ }
1612
+ while (i < n) out.push(`-${aLines[i++]}`);
1613
+ while (j < m) out.push(`+${bLines[j++]}`);
1614
+ return out.join("\n") + "\n";
1615
+ }
1616
+ function runDiff(argv) {
1617
+ const { positional, flags } = parseFlagArgs(argv);
1618
+ const [name] = positional;
1619
+ if (!name) {
1620
+ console.error(
1621
+ "Usage: canary skill diff <name> [--from <version>] [--to <version>]"
1622
+ );
1623
+ process.exit(1);
1624
+ }
1625
+ const template = SKILL_TEMPLATES[name];
1626
+ if (!template) {
1627
+ console.error(`Unknown skill "${name}".`);
1628
+ process.exit(1);
1629
+ }
1630
+ const versions = knownVersions(name);
1631
+ const to = typeof flags.to === "string" ? flags.to : template.version;
1632
+ const from = typeof flags.from === "string" ? flags.from : versions.filter((v) => v !== to).slice(-1)[0];
1633
+ if (!from) {
1634
+ console.error(
1635
+ `No prior version of "${name}" to diff against. Only known version: ${template.version}.`
1636
+ );
1637
+ process.exit(1);
1638
+ }
1639
+ const fromContent = renderAtVersion(name, from);
1640
+ const toContent = renderAtVersion(name, to);
1641
+ if (fromContent === null) {
1642
+ console.error(
1643
+ `Version "${from}" of skill "${name}" is not in the CLI. Known: ${versions.join(", ")}.`
1644
+ );
1645
+ process.exit(1);
1646
+ }
1647
+ if (toContent === null) {
1648
+ console.error(
1649
+ `Version "${to}" of skill "${name}" is not in the CLI. Known: ${versions.join(", ")}.`
1650
+ );
1651
+ process.exit(1);
1652
+ }
1653
+ if (from === to) {
1654
+ console.log(`(no diff \u2014 both sides are v${from})`);
1655
+ return;
1656
+ }
1657
+ process.stdout.write(
1658
+ jsUnifiedDiff(fromContent, toContent, `${name}@${from}`, `${name}@${to}`)
1659
+ );
1660
+ }
1661
+ function runSync() {
1662
+ const manifest = Object.entries(SKILL_TEMPLATES).map(([key, t]) => ({
1663
+ name: key,
1664
+ version: t.version,
1665
+ description: t.description,
1666
+ knownVersions: knownVersions(key),
1667
+ installedPaths: {
1668
+ claudeProject: `.claude/skills/${key}/SKILL.md`,
1669
+ claudePersonal: join2(homedir(), ".claude", "skills", key, "SKILL.md"),
1670
+ codexProject: `.codex/skills/${key}/SKILL.md`,
1671
+ codexPersonal: join2(homedir(), ".codex", "skills", key, "SKILL.md"),
1672
+ copilotProject: `.github/skills/${key}/SKILL.md`,
1673
+ copilotPersonal: join2(homedir(), ".copilot", "skills", key, "SKILL.md")
1674
+ }
1675
+ }));
1676
+ const instructions = `# Canary Skill Sync \u2014 Instructions for the Agent
1677
+
1678
+ You are an AI coding agent (Claude, Codex, Copilot, etc.). The user ran \`canary skill sync\`
1679
+ to bring their installed Canary skill templates up to date. Follow these steps:
1680
+
1681
+ ## Step 1 \u2014 For each skill in the manifest below
1682
+
1683
+ Check whether it is installed in any of the \`installedPaths\` (project or personal,
1684
+ for your agent type). A skill is considered installed if the \`SKILL.md\` file exists.
1685
+
1686
+ ## Step 2 \u2014 Read the version of each installed skill
1687
+
1688
+ The \`SKILL.md\` frontmatter contains a \`version: X.Y.Z\` line. Parse that value.
1689
+ If the frontmatter has no \`version:\` field, treat the installed version as \`0.0.0\`.
1690
+
1691
+ ## Step 3 \u2014 Compare semver
1692
+
1693
+ For each installed skill, compare \`installedVersion\` to the \`version\` in the manifest:
1694
+ - If \`installedVersion < manifestVersion\` \u2192 the skill is **outdated**, update it.
1695
+ - If equal or greater \u2192 leave it alone.
1696
+
1697
+ ## Step 4 \u2014 For each outdated skill, inspect what changed
1698
+
1699
+ Before overwriting, read the diff so you know what's new:
1700
+
1701
+ canary skill diff <name> --from <installedVersion>
1702
+
1703
+ The CLI retains past renders of each skill in \`previousVersions\`, so this prints a
1704
+ unified diff from the user's installed version \u2192 the current version. Only versions
1705
+ listed in \`knownVersions\` in the manifest can be diffed; if \`installedVersion\` is
1706
+ older than the oldest known version, diff against the oldest known version instead
1707
+ and warn the user that the older content is no longer reproducible.
1708
+
1709
+ Use the diff to:
1710
+ - Summarize what changed for the user (new sections, removed guidance, renamed commands).
1711
+ - Decide whether any user-local edits to the installed SKILL.md would be clobbered \u2014
1712
+ if the user appears to have edited their local copy, surface that and ask before
1713
+ overwriting.
1714
+
1715
+ ## Step 5 \u2014 Update outdated skills
1716
+
1717
+ Re-install by running:
1718
+
1719
+ canary skill install <name> # for project installs
1720
+ canary skill install <name> --personal # for personal installs
1721
+
1722
+ Preserve whichever location it was already installed in. Do NOT install skills that
1723
+ weren't already installed \u2014 sync only updates existing installs.
1724
+
1725
+ ## Step 6 \u2014 Report
1726
+
1727
+ Print a concise summary: which skills were updated (old \u2192 new version), which were
1728
+ already current, and which were not installed (skipped).
1729
+
1730
+ ## Manifest
1731
+
1732
+ \`\`\`json
1733
+ ${JSON.stringify({ skills: manifest }, null, 2)}
1734
+ \`\`\`
1735
+ `;
1736
+ process.stdout.write(instructions);
1737
+ }
1738
+ function parseInstallArgs(argv) {
1739
+ let name;
1740
+ let agent;
1741
+ let personal = false;
1742
+ for (let i = 0; i < argv.length; i++) {
1743
+ const arg = argv[i];
1744
+ if (arg === "--agent" && i + 1 < argv.length) {
1745
+ const val = argv[++i];
1746
+ if (!isAgentType(val)) {
1747
+ console.error(
1748
+ `Unknown agent "${val}". Supported agents: ${SUPPORTED_AGENTS.join(", ")}`
1749
+ );
1750
+ process.exit(1);
1751
+ }
1752
+ agent = val;
1753
+ } else if (arg === "--personal") {
1754
+ personal = true;
1755
+ } else if (!arg.startsWith("-") && !name) {
1756
+ name = arg;
1757
+ }
1758
+ }
1759
+ return { name, agent, personal };
1760
+ }
1761
+ async function runInstall(argv) {
1762
+ const { name, agent: explicitAgent, personal } = parseInstallArgs(argv);
1763
+ if (!name) {
1764
+ console.error("Missing skill name.\n");
1765
+ console.error(
1766
+ `Usage: canary skill install <name> [--agent ${SUPPORTED_AGENTS.join("|")}] [--personal]`
1767
+ );
1768
+ process.exit(1);
1769
+ }
1770
+ const template = SKILL_TEMPLATES[name];
1771
+ if (!template) {
1772
+ console.error(`Unknown skill template "${name}".
1773
+ `);
1774
+ console.error("Available templates:");
1775
+ for (const [key, t] of Object.entries(SKILL_TEMPLATES)) {
1776
+ console.error(` ${key.padEnd(24)} ${t.description}`);
1777
+ }
1778
+ process.exit(1);
1779
+ }
1780
+ const agent = explicitAgent ?? detectAgent();
1781
+ if (!agent) {
1782
+ console.error(
1783
+ "Could not auto-detect the AI agent. Use --agent to specify:\n"
1784
+ );
1785
+ console.error(
1786
+ " canary skill install <name> --agent claude # Claude Code"
1787
+ );
1788
+ console.error(
1789
+ " canary skill install <name> --agent codex # OpenAI Codex"
1790
+ );
1791
+ console.error(
1792
+ " canary skill install <name> --agent copilot # GitHub Copilot"
1793
+ );
1794
+ process.exit(1);
1795
+ }
1796
+ const dir = resolveInstallDir(name, agent, personal);
1797
+ const filePath = join2(dir, "SKILL.md");
1798
+ mkdirSync(dir, { recursive: true });
1799
+ writeFileSync(filePath, template.render(), "utf-8");
1800
+ const relPath = personal ? filePath : filePath.replace(process.cwd() + "/", "");
1801
+ console.log(`Installed ${name} skill for ${agent} \u2192 ${relPath}`);
1802
+ }
1803
+ async function runSkill(argv) {
1804
+ const [sub] = argv;
1805
+ if (!sub || sub === "help" || sub === "--help" || sub === "-h" || sub === "list") {
1806
+ printSkillHelp();
1807
+ return;
1808
+ }
1809
+ if (sub === "install") {
1810
+ await runInstall(argv.slice(1));
1811
+ return;
1812
+ }
1813
+ if (sub === "sync") {
1814
+ runSync();
1815
+ return;
1816
+ }
1817
+ if (sub === "diff") {
1818
+ runDiff(argv.slice(1));
1819
+ return;
1820
+ }
1821
+ if (sub === "versions") {
1822
+ runVersions(argv.slice(1));
1823
+ return;
1824
+ }
1825
+ const template = SKILL_TEMPLATES[sub];
1826
+ if (!template) {
1827
+ console.error(`Unknown skill template "${sub}".
1828
+ `);
1829
+ printSkillHelp();
1830
+ process.exit(1);
1831
+ }
1832
+ process.stdout.write(template.render());
1833
+ }
1834
+ export {
1835
+ detectAgent,
1836
+ resolveInstallDir,
1837
+ runSkill
1838
+ };
1839
+ //# sourceMappingURL=skill-KM34LDMO.js.map