@runcofactor/cli 0.10.0 → 0.13.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 (148) hide show
  1. package/dist/{api-catalog-GFGCLVS4.js → api-catalog-7HLEN7C2.js} +5 -4
  2. package/dist/{api-catalog-GFGCLVS4.js.map → api-catalog-7HLEN7C2.js.map} +1 -1
  3. package/dist/{attachment-SJC7D7FY.js → attachment-OVNQD6Z2.js} +5 -4
  4. package/dist/{attachment-SJC7D7FY.js.map → attachment-OVNQD6Z2.js.map} +1 -1
  5. package/dist/{auth-EN6NQCJY.js → auth-VUHL74FH.js} +3 -2
  6. package/dist/{chunk-IMPZAX6L.js → chunk-5GD47G2D.js} +37 -1
  7. package/dist/{chunk-IMPZAX6L.js.map → chunk-5GD47G2D.js.map} +1 -1
  8. package/dist/{chunk-WOHL4FTB.js → chunk-CQE46HSM.js} +5 -5
  9. package/dist/chunk-CQE46HSM.js.map +1 -0
  10. package/dist/{chunk-52IO7ACJ.js → chunk-CRHPDPRS.js} +39 -3
  11. package/dist/{chunk-52IO7ACJ.js.map → chunk-CRHPDPRS.js.map} +1 -1
  12. package/dist/{chunk-XQXVG7RA.js → chunk-EEN2TZUQ.js} +3 -3
  13. package/dist/{chunk-I4QLI3JO.js → chunk-EJ4U242G.js} +2 -2
  14. package/dist/{chunk-CLAWT7BJ.js → chunk-ENDHMYEJ.js} +3 -3
  15. package/dist/chunk-G4UYGRZR.js +2 -0
  16. package/dist/chunk-JZMJUPZP.js +92 -0
  17. package/dist/chunk-JZMJUPZP.js.map +1 -0
  18. package/dist/{chunk-GKY2KXO5.js → chunk-NL7CVG56.js} +105 -35
  19. package/dist/chunk-NL7CVG56.js.map +1 -0
  20. package/dist/{chunk-A4OFTUVV.js → chunk-PZ4LCN6K.js} +7 -4
  21. package/dist/chunk-PZ4LCN6K.js.map +1 -0
  22. package/dist/{chunk-SJJW77WN.js → chunk-QGI4UCJX.js} +69 -5
  23. package/dist/chunk-QGI4UCJX.js.map +1 -0
  24. package/dist/{chunk-4PNOV7OV.js → chunk-QQVWGWPI.js} +1023 -150
  25. package/dist/chunk-QQVWGWPI.js.map +1 -0
  26. package/dist/{chunk-N7HFALX3.js → chunk-VGTKUOYS.js} +2 -2
  27. package/dist/{chunk-B2VISN2V.js → chunk-YN763NWQ.js} +230 -66
  28. package/dist/chunk-YN763NWQ.js.map +1 -0
  29. package/dist/{chunk-QFDOWABX.js → chunk-Z5LUTUDN.js} +6 -3
  30. package/dist/chunk-Z5LUTUDN.js.map +1 -0
  31. package/dist/{chunk-Q4BO4Z5V.js → chunk-ZTOE3ND3.js} +4 -4
  32. package/dist/{credential-COLTBV6D.js → credential-LO2SEEHU.js} +24 -20
  33. package/dist/credential-LO2SEEHU.js.map +1 -0
  34. package/dist/{credential-template-EEPYFG7R.js → credential-template-ZN7W7734.js} +30 -28
  35. package/dist/credential-template-ZN7W7734.js.map +1 -0
  36. package/dist/{debug-workflow-KOVH5GOP.js → debug-workflow-R3RYYAXK.js} +5 -4
  37. package/dist/{debug-workflow-KOVH5GOP.js.map → debug-workflow-R3RYYAXK.js.map} +1 -1
  38. package/dist/{docs-3SSORHB5.js → docs-DIAFBDXQ.js} +5 -4
  39. package/dist/{docs-3SSORHB5.js.map → docs-DIAFBDXQ.js.map} +1 -1
  40. package/dist/{doctor-OTSIWV6G.js → doctor-NZPUSCRE.js} +9 -5
  41. package/dist/doctor-NZPUSCRE.js.map +1 -0
  42. package/dist/environment-RQVU4P5M.js +221 -0
  43. package/dist/environment-RQVU4P5M.js.map +1 -0
  44. package/dist/{feature-flag-T2LRZME3.js → feature-flag-XG26RURR.js} +72 -10
  45. package/dist/feature-flag-XG26RURR.js.map +1 -0
  46. package/dist/index.js +57 -48
  47. package/dist/index.js.map +1 -1
  48. package/dist/{init-TGWRECT2.js → init-EDNLKGPT.js} +5 -4
  49. package/dist/{init-TGWRECT2.js.map → init-EDNLKGPT.js.map} +1 -1
  50. package/dist/{install-DXSNWZKO.js → install-FYZ3HNZK.js} +7 -7
  51. package/dist/{issues-XIZOPX6T.js → issues-NNRVTJ4L.js} +5 -4
  52. package/dist/{issues-XIZOPX6T.js.map → issues-NNRVTJ4L.js.map} +1 -1
  53. package/dist/{knobs-NBINZIUB.js → knobs-A2BOBH22.js} +5 -4
  54. package/dist/{knobs-NBINZIUB.js.map → knobs-A2BOBH22.js.map} +1 -1
  55. package/dist/{list-3TDHK637.js → list-F33VZ4BJ.js} +5 -4
  56. package/dist/{list-3TDHK637.js.map → list-F33VZ4BJ.js.map} +1 -1
  57. package/dist/{local-HICVD6PU.js → local-DQDTM3XA.js} +7 -6
  58. package/dist/{local-HICVD6PU.js.map → local-DQDTM3XA.js.map} +1 -1
  59. package/dist/{login-UOIUXVUK.js → login-BUMVIB36.js} +5 -4
  60. package/dist/{login-UOIUXVUK.js.map → login-BUMVIB36.js.map} +1 -1
  61. package/dist/{mcp-54IJEOUA.js → mcp-B5Y5CDMQ.js} +23 -24
  62. package/dist/mcp-B5Y5CDMQ.js.map +1 -0
  63. package/dist/{property-UXLOLPTD.js → property-NJ4SRXQG.js} +76 -4
  64. package/dist/property-NJ4SRXQG.js.map +1 -0
  65. package/dist/{psql-BYUUNLGG.js → psql-VBZFCMNR.js} +3 -2
  66. package/dist/{psql-BYUUNLGG.js.map → psql-VBZFCMNR.js.map} +1 -1
  67. package/dist/{record-4TR2UIIS.js → record-WAO36O7G.js} +16 -17
  68. package/dist/record-WAO36O7G.js.map +1 -0
  69. package/dist/{redis-35VBUYW7.js → redis-DHZXHL4S.js} +3 -2
  70. package/dist/{redis-35VBUYW7.js.map → redis-DHZXHL4S.js.map} +1 -1
  71. package/dist/{release-OOLTUBUT.js → release-STJKUKTE.js} +5 -4
  72. package/dist/{release-OOLTUBUT.js.map → release-STJKUKTE.js.map} +1 -1
  73. package/dist/{sandbox-XGSUU6G7.js → sandbox-DWSGLY7B.js} +9 -8
  74. package/dist/{sandbox-XGSUU6G7.js.map → sandbox-DWSGLY7B.js.map} +1 -1
  75. package/dist/{scenarios-GWLJEGHA.js → scenarios-HCIDY725.js} +5 -4
  76. package/dist/{scenarios-GWLJEGHA.js.map → scenarios-HCIDY725.js.map} +1 -1
  77. package/dist/{session-MYFNZPRP.js → session-Y5RHU54D.js} +76 -8
  78. package/dist/session-Y5RHU54D.js.map +1 -0
  79. package/dist/{skill-KM34LDMO.js → skill-23WAHUOB.js} +217 -67
  80. package/dist/skill-23WAHUOB.js.map +1 -0
  81. package/dist/skills/debug-run/SKILL.md +33 -22
  82. package/dist/skills/debug-run/history/1.2.0.md +262 -0
  83. package/dist/skills/debug-run/history/1.3.0.md +273 -0
  84. package/dist/skills/debug-run/version.json +1 -1
  85. package/dist/skills/optimize-cache/SKILL.md +206 -0
  86. package/dist/{spec-RA3CFEKG.js → spec-LM5XHOQ5.js} +54 -25
  87. package/dist/spec-LM5XHOQ5.js.map +1 -0
  88. package/dist/{src-LB4HBTJQ.js → src-22RI5VP2.js} +19 -11
  89. package/dist/src-22RI5VP2.js.map +1 -0
  90. package/dist/src-BGDRMOKC.js +5748 -0
  91. package/dist/src-BGDRMOKC.js.map +1 -0
  92. package/dist/{src-N3TPKGTY.js → src-NCJPUAE6.js} +3 -2
  93. package/dist/{src-UVDX5UP6.js → src-Y3K7UJEF.js} +4 -2
  94. package/dist/{start-PSEQSQA3.js → start-SWY55CZX.js} +5 -4
  95. package/dist/{start-PSEQSQA3.js.map → start-SWY55CZX.js.map} +1 -1
  96. package/dist/{tag-YY6YAABQ.js → tag-YSU2XXPI.js} +6 -5
  97. package/dist/{task-IQC3YR5K.js → task-NLBKLD7D.js} +5 -4
  98. package/dist/{task-IQC3YR5K.js.map → task-NLBKLD7D.js.map} +1 -1
  99. package/dist/{test-suite-DZNJFXDW.js → test-suite-QCPWZ2LS.js} +9 -6
  100. package/dist/{test-suite-DZNJFXDW.js.map → test-suite-QCPWZ2LS.js.map} +1 -1
  101. package/dist/{trace-HXIW7U2D.js → trace-A3ZY6K26.js} +6 -4
  102. package/dist/{triage-ORG45R5W.js → triage-TT4BMDBU.js} +6 -5
  103. package/dist/triage-TT4BMDBU.js.map +1 -0
  104. package/dist/{upgrade-L2VYMP2V.js → upgrade-6ZUFETKX.js} +3 -3
  105. package/dist/{workflow-XYFH5LTP.js → workflow-2CX3ZNOQ.js} +241 -237
  106. package/dist/workflow-2CX3ZNOQ.js.map +1 -0
  107. package/dist/{workflow-cache-NQJQFWMV.js → workflow-cache-GXNE6SU2.js} +140 -7
  108. package/dist/workflow-cache-GXNE6SU2.js.map +1 -0
  109. package/dist/{workflow-debug-BGD4UIXU.js → workflow-debug-PRX3DWGD.js} +8 -7
  110. package/dist/{workflow-debug-BGD4UIXU.js.map → workflow-debug-PRX3DWGD.js.map} +1 -1
  111. package/dist/{workflow-proposals-MTUDX46O.js → workflow-proposals-BW327WGL.js} +5 -4
  112. package/dist/{workflow-proposals-MTUDX46O.js.map → workflow-proposals-BW327WGL.js.map} +1 -1
  113. package/package.json +2 -1
  114. package/dist/chunk-4PNOV7OV.js.map +0 -1
  115. package/dist/chunk-A4OFTUVV.js.map +0 -1
  116. package/dist/chunk-B2VISN2V.js.map +0 -1
  117. package/dist/chunk-GKY2KXO5.js.map +0 -1
  118. package/dist/chunk-JICSYFRJ.js +0 -46
  119. package/dist/chunk-JICSYFRJ.js.map +0 -1
  120. package/dist/chunk-QFDOWABX.js.map +0 -1
  121. package/dist/chunk-SJJW77WN.js.map +0 -1
  122. package/dist/chunk-WOHL4FTB.js.map +0 -1
  123. package/dist/credential-COLTBV6D.js.map +0 -1
  124. package/dist/credential-template-EEPYFG7R.js.map +0 -1
  125. package/dist/doctor-OTSIWV6G.js.map +0 -1
  126. package/dist/feature-flag-T2LRZME3.js.map +0 -1
  127. package/dist/mcp-54IJEOUA.js.map +0 -1
  128. package/dist/property-UXLOLPTD.js.map +0 -1
  129. package/dist/record-4TR2UIIS.js.map +0 -1
  130. package/dist/session-MYFNZPRP.js.map +0 -1
  131. package/dist/skill-KM34LDMO.js.map +0 -1
  132. package/dist/spec-RA3CFEKG.js.map +0 -1
  133. package/dist/src-LB4HBTJQ.js.map +0 -1
  134. package/dist/workflow-XYFH5LTP.js.map +0 -1
  135. package/dist/workflow-cache-NQJQFWMV.js.map +0 -1
  136. /package/dist/{auth-EN6NQCJY.js.map → auth-VUHL74FH.js.map} +0 -0
  137. /package/dist/{chunk-XQXVG7RA.js.map → chunk-EEN2TZUQ.js.map} +0 -0
  138. /package/dist/{chunk-I4QLI3JO.js.map → chunk-EJ4U242G.js.map} +0 -0
  139. /package/dist/{chunk-CLAWT7BJ.js.map → chunk-ENDHMYEJ.js.map} +0 -0
  140. /package/dist/{src-N3TPKGTY.js.map → chunk-G4UYGRZR.js.map} +0 -0
  141. /package/dist/{chunk-N7HFALX3.js.map → chunk-VGTKUOYS.js.map} +0 -0
  142. /package/dist/{chunk-Q4BO4Z5V.js.map → chunk-ZTOE3ND3.js.map} +0 -0
  143. /package/dist/{install-DXSNWZKO.js.map → install-FYZ3HNZK.js.map} +0 -0
  144. /package/dist/{src-UVDX5UP6.js.map → src-NCJPUAE6.js.map} +0 -0
  145. /package/dist/{tag-YY6YAABQ.js.map → src-Y3K7UJEF.js.map} +0 -0
  146. /package/dist/{trace-HXIW7U2D.js.map → tag-YSU2XXPI.js.map} +0 -0
  147. /package/dist/{triage-ORG45R5W.js.map → trace-A3ZY6K26.js.map} +0 -0
  148. /package/dist/{upgrade-L2VYMP2V.js.map → upgrade-6ZUFETKX.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/skill.ts","../src/skill-templates/issue-log-xref.ts","../src/skill-templates/browser-testing.ts","../src/skill-templates/agent-tasks.ts","../src/skill-templates/canary-basic.ts","../src/skill-templates/test-cases.ts","../src/skill-templates/debug-run.ts","../src/skill-templates/index.ts"],"sourcesContent":["import { existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { SKILL_TEMPLATES } from \"./skill-templates/index.js\";\n\nexport type AgentType = \"claude\" | \"codex\" | \"copilot\";\n\nconst SUPPORTED_AGENTS: AgentType[] = [\"claude\", \"codex\", \"copilot\"];\n\ntype DetectAgentOptions = {\n cwd?: string;\n env?: NodeJS.ProcessEnv;\n exists?: (path: string) => boolean;\n};\n\ntype InstallPathOptions = {\n cwd?: string;\n home?: string;\n};\n\nfunction isAgentType(value: string): value is AgentType {\n return value === \"claude\" || value === \"codex\" || value === \"copilot\";\n}\n\n/**\n * Detect which AI agent is currently running based on environment variables\n * and project directory structure.\n */\nexport function detectAgent(options: DetectAgentOptions = {}): AgentType | null {\n const env = options.env ?? process.env;\n const cwd = options.cwd ?? process.cwd();\n const pathExists = options.exists ?? existsSync;\n\n // Claude Code sets this\n if (env.CLAUDE) return \"claude\";\n\n // OpenAI Codex sets CODEX_* variables in managed sessions\n if (\n env.CODEX ||\n env.CODEX_CI ||\n env.CODEX_THREAD_ID ||\n env.CODEX_MANAGED_PACKAGE_ROOT ||\n env.AGENT_MODE === \"codex\"\n )\n return \"codex\";\n\n // GitHub Copilot agent mode\n if (\n env.GITHUB_COPILOT ||\n env.COPILOT_AGENT ||\n env.AGENT_MODE === \"copilot\"\n )\n return \"copilot\";\n\n // Fall back to directory heuristics in the current working directory\n const foundAgents = SUPPORTED_AGENTS.filter((agent) =>\n pathExists(join(cwd, agent === \"copilot\" ? \".github\" : `.${agent}`))\n );\n\n // If only one exists, use that\n if (foundAgents.length === 1) return foundAgents[0] ?? null;\n\n // Multiple or none — can't determine\n return null;\n}\n\n/**\n * Resolve the install directory for a skill based on agent type.\n *\n * Project-level paths:\n * claude → .claude/skills/<name>/\n * codex → .codex/skills/<name>/\n * copilot → .github/skills/<name>/\n *\n * Personal paths (--personal):\n * claude → ~/.claude/skills/<name>/\n * codex → ~/.codex/skills/<name>/\n * copilot → ~/.copilot/skills/<name>/\n */\nexport function resolveInstallDir(\n skillName: string,\n agent: AgentType,\n personal: boolean,\n options: InstallPathOptions = {}\n): string {\n const base = personal\n ? (options.home ?? homedir())\n : (options.cwd ?? process.cwd());\n\n switch (agent) {\n case \"claude\":\n return join(base, \".claude\", \"skills\", skillName);\n case \"codex\":\n return join(base, \".codex\", \"skills\", skillName);\n case \"copilot\":\n return personal\n ? join(base, \".copilot\", \"skills\", skillName)\n : join(base, \".github\", \"skills\", skillName);\n }\n}\n\nfunction printSkillHelp() {\n const lines = [\n \"Usage:\",\n \" canary skill <name> Output skill template to stdout\",\n \" canary skill install <name> Install skill (auto-detects agent)\",\n \" canary skill install <name> --agent codex Install for a specific agent\",\n \" canary skill install <name> --personal Install to ~/.<agent>/skills/\",\n \" canary skill list List available skill templates\",\n \" canary skill sync Print manifest + instructions for the\",\n \" agent to version-compare and update\",\n \" installed skills\",\n \" canary skill diff <name> [--from V] [--to V] Unified diff between two versions of\",\n \" a skill (defaults: previous → current)\",\n \" canary skill versions <name> List all known versions of a skill\",\n \"\",\n \"Agent auto-detection:\",\n \" The install command detects which AI agent is running and writes the\",\n \" SKILL.md to the correct location:\",\n \"\",\n \" Agent Project path Personal path\",\n \" ───── ──────────── ─────────────\",\n \" claude .claude/skills/<name>/ ~/.claude/skills/<name>/\",\n \" codex .codex/skills/<name>/ ~/.codex/skills/<name>/\",\n \" copilot .github/skills/<name>/ ~/.copilot/skills/<name>/\",\n \"\",\n \" Detection checks (in order):\",\n \" 1. CLAUDE env var → claude\",\n \" 2. CODEX_* env vars → codex\",\n \" 3. GITHUB_COPILOT env var → copilot\",\n \" 4. .claude/.codex/.github dirs → whichever single one exists\",\n \" 5. If ambiguous, use --agent to specify explicitly\",\n \"\",\n \"Available templates:\",\n ];\n\n for (const [key, template] of Object.entries(SKILL_TEMPLATES)) {\n lines.push(\n ` ${key.padEnd(24)} v${template.version.padEnd(7)} ${template.description}`\n );\n }\n\n console.log(lines.join(\"\\n\"));\n}\n\nfunction compareSemver(a: string, b: string): number {\n const pa = a.split(\".\").map((n) => parseInt(n, 10) || 0);\n const pb = b.split(\".\").map((n) => parseInt(n, 10) || 0);\n for (let i = 0; i < 3; i++) {\n const d = (pa[i] ?? 0) - (pb[i] ?? 0);\n if (d !== 0) return d < 0 ? -1 : 1;\n }\n return 0;\n}\n\nfunction knownVersions(name: string): string[] {\n const t = SKILL_TEMPLATES[name];\n if (!t) return [];\n const all = new Set<string>([t.version, ...Object.keys(t.previousVersions ?? {})]);\n return [...all].sort(compareSemver);\n}\n\nfunction renderAtVersion(name: string, version: string): string | null {\n const t = SKILL_TEMPLATES[name];\n if (!t) return null;\n if (version === t.version) return t.render();\n const prev = t.previousVersions?.[version];\n return prev ? prev() : null;\n}\n\nfunction parseFlagArgs(argv: string[]): {\n positional: string[];\n flags: Record<string, string | true>;\n} {\n const positional: string[] = [];\n const flags: Record<string, string | true> = {};\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i];\n if (a.startsWith(\"--\")) {\n const key = a.slice(2);\n const next = argv[i + 1];\n if (next !== undefined && !next.startsWith(\"--\")) {\n flags[key] = next;\n i++;\n } else {\n flags[key] = true;\n }\n } else {\n positional.push(a);\n }\n }\n return { positional, flags };\n}\n\nfunction runVersions(argv: string[]) {\n const [name] = argv;\n if (!name || !SKILL_TEMPLATES[name]) {\n console.error(`Usage: canary skill versions <name>`);\n process.exit(1);\n }\n const versions = knownVersions(name);\n const current = SKILL_TEMPLATES[name].version;\n for (const v of versions) {\n console.log(v === current ? `${v} (current)` : v);\n }\n}\n\nfunction jsUnifiedDiff(\n a: string,\n b: string,\n labelA: string,\n labelB: string\n): string {\n const aLines = a.split(\"\\n\");\n const bLines = b.split(\"\\n\");\n const n = aLines.length;\n const m = bLines.length;\n const dp: number[][] = Array.from({ length: n + 1 }, () =>\n Array.from<number>({ length: m + 1 }).fill(0)\n );\n for (let i = n - 1; i >= 0; i--) {\n for (let j = m - 1; j >= 0; j--) {\n dp[i][j] =\n aLines[i] === bLines[j]\n ? dp[i + 1][j + 1] + 1\n : Math.max(dp[i + 1][j], dp[i][j + 1]);\n }\n }\n const out: string[] = [`--- ${labelA}`, `+++ ${labelB}`];\n let i = 0;\n let j = 0;\n while (i < n && j < m) {\n if (aLines[i] === bLines[j]) {\n out.push(` ${aLines[i]}`);\n i++;\n j++;\n } else if (dp[i + 1][j] >= dp[i][j + 1]) {\n out.push(`-${aLines[i++]}`);\n } else {\n out.push(`+${bLines[j++]}`);\n }\n }\n while (i < n) out.push(`-${aLines[i++]}`);\n while (j < m) out.push(`+${bLines[j++]}`);\n return out.join(\"\\n\") + \"\\n\";\n}\n\nfunction runDiff(argv: string[]) {\n const { positional, flags } = parseFlagArgs(argv);\n const [name] = positional;\n\n if (!name) {\n console.error(\n \"Usage: canary skill diff <name> [--from <version>] [--to <version>]\"\n );\n process.exit(1);\n }\n\n const template = SKILL_TEMPLATES[name];\n if (!template) {\n console.error(`Unknown skill \"${name}\".`);\n process.exit(1);\n }\n\n const versions = knownVersions(name);\n const to =\n typeof flags.to === \"string\" ? flags.to : template.version;\n const from =\n typeof flags.from === \"string\"\n ? flags.from\n : versions.filter((v) => v !== to).slice(-1)[0];\n\n if (!from) {\n console.error(\n `No prior version of \"${name}\" to diff against. Only known version: ${template.version}.`\n );\n process.exit(1);\n }\n\n const fromContent = renderAtVersion(name, from);\n const toContent = renderAtVersion(name, to);\n\n if (fromContent === null) {\n console.error(\n `Version \"${from}\" of skill \"${name}\" is not in the CLI. Known: ${versions.join(\", \")}.`\n );\n process.exit(1);\n }\n if (toContent === null) {\n console.error(\n `Version \"${to}\" of skill \"${name}\" is not in the CLI. Known: ${versions.join(\", \")}.`\n );\n process.exit(1);\n }\n\n if (from === to) {\n console.log(`(no diff — both sides are v${from})`);\n return;\n }\n\n process.stdout.write(\n jsUnifiedDiff(fromContent, toContent, `${name}@${from}`, `${name}@${to}`)\n );\n}\n\nfunction runSync() {\n const manifest = Object.entries(SKILL_TEMPLATES).map(([key, t]) => ({\n name: key,\n version: t.version,\n description: t.description,\n knownVersions: knownVersions(key),\n installedPaths: {\n claudeProject: `.claude/skills/${key}/SKILL.md`,\n claudePersonal: join(homedir(), \".claude\", \"skills\", key, \"SKILL.md\"),\n codexProject: `.codex/skills/${key}/SKILL.md`,\n codexPersonal: join(homedir(), \".codex\", \"skills\", key, \"SKILL.md\"),\n copilotProject: `.github/skills/${key}/SKILL.md`,\n copilotPersonal: join(homedir(), \".copilot\", \"skills\", key, \"SKILL.md\"),\n },\n }));\n\n const instructions = `# Canary Skill Sync — Instructions for the Agent\n\nYou are an AI coding agent (Claude, Codex, Copilot, etc.). The user ran \\`canary skill sync\\`\nto bring their installed Canary skill templates up to date. Follow these steps:\n\n## Step 1 — For each skill in the manifest below\n\nCheck whether it is installed in any of the \\`installedPaths\\` (project or personal,\nfor your agent type). A skill is considered installed if the \\`SKILL.md\\` file exists.\n\n## Step 2 — Read the version of each installed skill\n\nThe \\`SKILL.md\\` frontmatter contains a \\`version: X.Y.Z\\` line. Parse that value.\nIf the frontmatter has no \\`version:\\` field, treat the installed version as \\`0.0.0\\`.\n\n## Step 3 — Compare semver\n\nFor each installed skill, compare \\`installedVersion\\` to the \\`version\\` in the manifest:\n- If \\`installedVersion < manifestVersion\\` → the skill is **outdated**, update it.\n- If equal or greater → leave it alone.\n\n## Step 4 — For each outdated skill, inspect what changed\n\nBefore overwriting, read the diff so you know what's new:\n\n canary skill diff <name> --from <installedVersion>\n\nThe CLI retains past renders of each skill in \\`previousVersions\\`, so this prints a\nunified diff from the user's installed version → the current version. Only versions\nlisted in \\`knownVersions\\` in the manifest can be diffed; if \\`installedVersion\\` is\nolder than the oldest known version, diff against the oldest known version instead\nand warn the user that the older content is no longer reproducible.\n\nUse the diff to:\n- Summarize what changed for the user (new sections, removed guidance, renamed commands).\n- Decide whether any user-local edits to the installed SKILL.md would be clobbered —\n if the user appears to have edited their local copy, surface that and ask before\n overwriting.\n\n## Step 5 — Update outdated skills\n\nRe-install by running:\n\n canary skill install <name> # for project installs\n canary skill install <name> --personal # for personal installs\n\nPreserve whichever location it was already installed in. Do NOT install skills that\nweren't already installed — sync only updates existing installs.\n\n## Step 6 — Report\n\nPrint a concise summary: which skills were updated (old → new version), which were\nalready current, and which were not installed (skipped).\n\n## Manifest\n\n\\`\\`\\`json\n${JSON.stringify({ skills: manifest }, null, 2)}\n\\`\\`\\`\n`;\n\n process.stdout.write(instructions);\n}\n\nfunction parseInstallArgs(argv: string[]): {\n name: string | undefined;\n agent: AgentType | undefined;\n personal: boolean;\n} {\n let name: string | undefined;\n let agent: AgentType | undefined;\n let personal = false;\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (arg === \"--agent\" && i + 1 < argv.length) {\n const val = argv[++i];\n if (!isAgentType(val)) {\n console.error(\n `Unknown agent \"${val}\". Supported agents: ${SUPPORTED_AGENTS.join(\", \")}`\n );\n process.exit(1);\n }\n agent = val;\n } else if (arg === \"--personal\") {\n personal = true;\n } else if (!arg.startsWith(\"-\") && !name) {\n name = arg;\n }\n }\n\n return { name, agent, personal };\n}\n\nasync function runInstall(argv: string[]) {\n const { name, agent: explicitAgent, personal } = parseInstallArgs(argv);\n\n if (!name) {\n console.error(\"Missing skill name.\\n\");\n console.error(\n `Usage: canary skill install <name> [--agent ${SUPPORTED_AGENTS.join(\"|\")}] [--personal]`\n );\n process.exit(1);\n }\n\n const template = SKILL_TEMPLATES[name];\n if (!template) {\n console.error(`Unknown skill template \"${name}\".\\n`);\n console.error(\"Available templates:\");\n for (const [key, t] of Object.entries(SKILL_TEMPLATES)) {\n console.error(` ${key.padEnd(24)} ${t.description}`);\n }\n process.exit(1);\n }\n\n // Resolve agent type\n const agent = explicitAgent ?? detectAgent();\n if (!agent) {\n console.error(\n \"Could not auto-detect the AI agent. Use --agent to specify:\\n\"\n );\n console.error(\n \" canary skill install <name> --agent claude # Claude Code\"\n );\n console.error(\n \" canary skill install <name> --agent codex # OpenAI Codex\"\n );\n console.error(\n \" canary skill install <name> --agent copilot # GitHub Copilot\"\n );\n process.exit(1);\n }\n\n const dir = resolveInstallDir(name, agent, personal);\n const filePath = join(dir, \"SKILL.md\");\n\n // Write the file\n mkdirSync(dir, { recursive: true });\n writeFileSync(filePath, template.render(), \"utf-8\");\n\n const relPath = personal ? filePath : filePath.replace(process.cwd() + \"/\", \"\");\n console.log(`Installed ${name} skill for ${agent} → ${relPath}`);\n}\n\nexport async function runSkill(argv: string[]) {\n const [sub] = argv;\n\n if (!sub || sub === \"help\" || sub === \"--help\" || sub === \"-h\" || sub === \"list\") {\n printSkillHelp();\n return;\n }\n\n // canary skill install <name> [--agent ...]\n if (sub === \"install\") {\n await runInstall(argv.slice(1));\n return;\n }\n\n // canary skill sync — print instructions for the agent\n if (sub === \"sync\") {\n runSync();\n return;\n }\n\n if (sub === \"diff\") {\n runDiff(argv.slice(1));\n return;\n }\n\n if (sub === \"versions\") {\n runVersions(argv.slice(1));\n return;\n }\n\n // canary skill <name> — output to stdout (backward compatible)\n const template = SKILL_TEMPLATES[sub];\n if (!template) {\n console.error(`Unknown skill template \"${sub}\".\\n`);\n printSkillHelp();\n process.exit(1);\n }\n\n process.stdout.write(template.render());\n}\n","export type SkillTemplate = {\n name: string;\n description: string;\n version: string;\n render: () => string;\n /**\n * Renderers for past versions of this skill, keyed by semver.\n * When bumping `version` + `render`, move the previous render fn in here\n * under its old version string so `canary skill diff` can reproduce it.\n */\n previousVersions?: Record<string, () => string>;\n};\n\nexport const issueLogXrefTemplate: SkillTemplate = {\n name: \"issue-log-xref\",\n description:\n \"Cross-reference Canary issues with server logs to find root causes\",\n version: \"1.0.0\",\n render: () => `---\nname: issue-log-xref\nversion: 1.0.0\ndescription: 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.\nallowed-tools: Bash(canary issues:*), Bash(canary login:*), Grep, Read\n---\n\n# Issue Log Cross-Reference\n\nCross-reference Canary-detected issues with server logs to identify root causes, correlate errors, and provide actionable debugging context.\n\n## Prerequisites\n\n- \\`canary\\` CLI installed and authenticated (\\`canary login\\`)\n- Access to server logs (see Log Source Configuration below)\n\n## Workflow\n\n### 1. Fetch issues from Canary\n\nList recent issues to find ones worth investigating:\n\n\\`\\`\\`bash\ncanary issues list --format markdown\n\\`\\`\\`\n\nUse filters to narrow down:\n\n\\`\\`\\`bash\ncanary issues list --severity high --status open --format markdown\n\\`\\`\\`\n\n### 2. Get issue details\n\nOnce you identify an issue, fetch its full diagnostics:\n\n\\`\\`\\`bash\ncanary issues get <issue-id> --format markdown\n\\`\\`\\`\n\nNote the following from the issue details:\n- **Timestamps** — when the issue was detected\n- **URLs / endpoints** — which pages or API routes were involved\n- **Error messages** — any visible error text captured in screenshots or DOM\n- **HTTP status codes** — failed network requests\n\n### 3. Access server logs\n\n<!-- ============================================================\n LOG SOURCE CONFIGURATION\n\n Replace {{LOG_SOURCE_INSTRUCTIONS}} below with instructions\n specific to your infrastructure. The AI agent reading this\n skill will use these instructions to fetch logs.\n\n Examples:\n\n **AWS CloudWatch:**\n Query CloudWatch Logs Insights for the relevant log group:\n \\`\\`\\`bash\n aws logs filter-log-events \\\\\n --log-group-name /ecs/my-api \\\\\n --start-time <epoch-ms> \\\\\n --end-time <epoch-ms> \\\\\n --filter-pattern \"<error-text>\"\n \\`\\`\\`\n\n **Local log files:**\n Search local log files for matching entries:\n \\`\\`\\`bash\n grep -i \"<error-text>\" /var/log/my-app/*.log\n \\`\\`\\`\n\n **Datadog:**\n Use the Datadog CLI or API to query logs:\n \\`\\`\\`bash\n dog logs list --query \"service:my-api status:error\" \\\\\n --from <ISO-timestamp> --to <ISO-timestamp>\n \\`\\`\\`\n\n **kubectl (Kubernetes):**\n Fetch pod logs for the relevant service:\n \\`\\`\\`bash\n kubectl logs -l app=my-api --since=1h | grep \"<error-text>\"\n \\`\\`\\`\n============================================================ -->\n\n{{LOG_SOURCE_INSTRUCTIONS}}\n\n### 4. Cross-reference and correlate\n\nMatch the Canary issue with log entries using these correlation points:\n\n- **Timestamps** — find log entries within a few seconds of the issue detection time\n- **Error messages** — search logs for the same error text shown in the issue\n- **HTTP status codes** — look for 4xx/5xx responses matching the failed requests\n- **URLs / routes** — filter logs by the endpoint or page path from the issue\n- **Stack traces** — identify the code path that produced the error\n- **Request IDs** — if your app uses correlation/request IDs, trace the full request lifecycle\n\n## Analysis Guidelines\n\nWhen presenting findings:\n\n1. **State the match confidence** — clear match, likely match, or circumstantial\n2. **Show the timeline** — lay out events in chronological order across both sources\n3. **Identify the root cause** — distinguish between the symptom (what Canary caught) and the cause (what the logs reveal)\n4. **Suggest next steps** — point to the specific code, service, or configuration that needs attention\n5. **Note gaps** — if logs are missing or incomplete, say so explicitly rather than speculating\n`,\n};\n","import type { SkillTemplate } from './issue-log-xref.js';\n\nexport const browserTestingTemplate: SkillTemplate = {\n name: 'browser-testing',\n description: 'Use the Canary CLI to control a browser for testing web applications',\n version: '1.0.0',\n render: () => `---\nname: browser-testing\nversion: 1.0.0\ndescription: 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.\nallowed-tools: Bash(canary session:*), Bash(canary login:*), Bash(canary workflow:*), Bash(canary credential:*), Read\n---\n\n# Browser Testing with Canary\n\nUse 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.\n\n## Prerequisites\n\n- \\`canary\\` CLI installed and authenticated (\\`canary login\\`)\n- Run \\`canary session --help\\` for the full command reference\n\n## Environments & Authentication\n\nCanary 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:\n\n### Remote / Deployed environments (default)\n\nCredentials are pre-configured with storage state. Just use \\`--credential\\` to load one:\n\n\\`\\`\\`bash\n# Interactive picker — shows credential name, property name, and base URL\ncanary session start --credential\n\n# By name\ncanary session start --credential \"Admin User\" --url https://staging.example.com\n\\`\\`\\`\n\n### Local development\n\nCredentials stored in Canary won't work against localhost because session cookies are bound to the remote domain. When testing a local dev server:\n\n1. **Ask the user to log in manually.** Start a session without credentials and tell them to authenticate in the browser:\n\n\\`\\`\\`bash\ncanary session start --url http://localhost:5173\n\\`\\`\\`\n\nThen 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.\"\n\n2. **Wait for confirmation** before proceeding with testing.\n\n3. Alternatively, if the user has a **local storage state file**, use it:\n\n\\`\\`\\`bash\ncanary session start --storage-state ./local-auth.json --url http://localhost:5173\n\\`\\`\\`\n\n**Rule:** If the URL is localhost or 127.0.0.1, do NOT use \\`--credential\\` — it will download remote session state that won't work locally. Instead, prompt the user to log in.\n\n## Core Workflow\n\nThe fundamental loop is: **snapshot → read → act → snapshot → verify**.\n\n### 1. Start a session\n\n\\`\\`\\`bash\n# Start a headed browser (you can watch it)\ncanary session start --url https://your-app.com\n\n# Start with a saved credential (interactive picker shows property + base URL)\ncanary session start --credential\n\n# Start with a specific credential by name\ncanary session start --credential \"Admin User\" --url https://your-app.com\n\n# Load a credential into an already-running session\ncanary session load-credential\n\\`\\`\\`\n\n### 2. Take a snapshot to see the page\n\nSnapshots are your primary way to understand what's on the page. They return an **accessibility tree** with element references (refs) and a **screenshot**.\n\n\\`\\`\\`bash\n# Full snapshot: accessibility tree + screenshot\ncanary session snapshot\n\n# Search for specific elements (fastest way to find what you need)\ncanary session snapshot --search \"Submit\"\ncanary session snapshot --search \"Email|Password\"\n\n# Expand a collapsed section (use the ref shown in the tree, e.g. [e50])\ncanary session snapshot --expand e50\n\n# Screenshot only (useful for visual verification)\ncanary session snapshot --mode screenshot\n\n# Tree only (useful when you need ref details, no image)\ncanary session snapshot --mode tree\n\\`\\`\\`\n\n**Reading snapshot output:**\n\nThe accessibility tree shows elements with refs like \\`[ref=e15]\\`. Use these refs in subsequent commands. The tree is structured to show the page hierarchy — forms, buttons, links, headings, etc.\n\nWhen 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 — search overlays can obscure subtle UI changes.\n\nLarge 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.\n\n### 3. Interact with the page\n\nAlways reference elements by their **ref** from the snapshot.\n\n\\`\\`\\`bash\n# Click an element\ncanary session click --ref e15 --element \"Submit button\"\n\n# Type into an input field\ncanary session type --ref e7 --text \"user@example.com\" --element \"Email input\"\n\n# Type and submit (presses Enter after typing)\ncanary session type --ref e7 --text \"search query\" --element \"Search box\" --submit\n\n# Fill multiple form fields at once\ncanary session fill --fields '[{\"ref\":\"e3\",\"value\":\"John\"},{\"ref\":\"e4\",\"value\":\"john@example.com\"}]'\n\n# Select a dropdown option\ncanary session select --ref e12 --value \"admin\" --element \"Role dropdown\"\n\n# Press a keyboard key\ncanary session press --key Enter\ncanary session press --key Escape\ncanary session press --key ArrowDown\n\n# Hover to reveal hidden content (tooltips, dropdowns)\ncanary session hover --ref e8 --element \"User menu\"\n\\`\\`\\`\n\n### 4. Navigate\n\n\\`\\`\\`bash\ncanary session navigate --url https://your-app.com/settings\ncanary session back\n\\`\\`\\`\n\n### 5. Scroll\n\n\\`\\`\\`bash\n# Scroll down the page\ncanary session scroll --direction down\n\n# Scroll with more intensity (1-10, default 3)\ncanary session scroll --direction down --amount 8\n\n# Scroll up\ncanary session scroll --direction up\n\\`\\`\\`\n\n### 6. Verify results\n\nAfter every action, take a snapshot to verify the result:\n\n\\`\\`\\`bash\n# Check what happened after clicking submit\ncanary session snapshot\n\n# Search for a success message\ncanary session snapshot --search \"Success|saved\"\n\n# Check for error messages\ncanary session snapshot --search \"Error|failed|invalid\"\n\n# Check console for JavaScript errors\ncanary session console --only-errors\n\n# Check network requests for failed API calls\ncanary session network\n\\`\\`\\`\n\n## Session Management\n\n\\`\\`\\`bash\n# List all active sessions\ncanary session list\n\n# Check session details\ncanary session status\n\n# Stop a session\ncanary session stop\n\n# Stop all sessions\ncanary session stop --all\n\n# When multiple sessions are active, specify which one\ncanary session snapshot --session s1\ncanary session click --ref e5 --element \"Button\" --session s2\n\\`\\`\\`\n\n## Advanced Techniques\n\n### Waiting for page changes\n\n\\`\\`\\`bash\n# Wait for text to appear (after navigation or async action)\ncanary session wait --text \"Dashboard loaded\"\n\n# Wait for text to disappear (loading spinner)\ncanary session wait --text \"Loading...\"\n\n# Wait a fixed time (use sparingly)\ncanary session wait --time 2\n\\`\\`\\`\n\n### Coordinate-based clicking\n\nWhen ref-based clicking doesn't work (overlays, canvas elements), use coordinates. First take a snapshot, look at the screenshot, estimate coordinates, then click:\n\n\\`\\`\\`bash\n# Click at specific coordinates\ncanary session click --x 450 --y 300 --element \"Canvas element\"\n\\`\\`\\`\n\n### Tab management\n\n\\`\\`\\`bash\ncanary session tabs --action list\ncanary session tabs --action new\ncanary session tabs --action select --index 0\ncanary session tabs --action close --index 1\n\\`\\`\\`\n\n### Evaluating JavaScript\n\n\\`\\`\\`bash\ncanary session evaluate --js \"() => document.title\"\ncanary session evaluate --js \"() => window.location.href\"\ncanary session evaluate --js \"() => document.querySelectorAll('.error').length\"\n\\`\\`\\`\n\n### Taking labeled screenshots\n\n\\`\\`\\`bash\n# Take a labeled screenshot for later reference\ncanary session screenshot --label \"before-submit\"\n\n# Full page screenshot\ncanary session screenshot --full-page --label \"full-page-state\"\n\\`\\`\\`\n\n### File downloads\n\n\\`\\`\\`bash\n# List downloaded files\ncanary session evaluate --js \"() => 'trigger download'\"\n# ... after download completes:\ncanary session list-downloads\ncanary session read-download\n\\`\\`\\`\n\n## Testing Guidelines\n\n1. **Always snapshot after actions** — never assume an action succeeded. Take a snapshot to verify.\n2. **Use search to locate elements** — \\`--search \"Submit\"\\` is faster than scanning the full tree.\n3. **Use refs, not coordinates** — ref-based interaction is more reliable. Only fall back to coordinates when refs fail.\n4. **Check for errors** — after form submissions or navigation, check both the page content and console/network for errors.\n5. **Wait for async operations** — if an action triggers an API call, use \\`wait --text\\` for the expected result rather than fixed delays.\n6. **Load credentials for authenticated testing** — use \\`canary session start --credential\\` or \\`canary session load-credential\\` to test as specific users.\n7. **Report what you find** — describe what you see in snapshots, what worked, what failed, and include relevant refs or error messages.\n\n## Improving Testability (Local Development)\n\nWhen testing locally, you have access to the source code — use this advantage. If the accessibility tree is confusing or elements are hard to target, **diagnose why** and suggest concrete improvements to the user.\n\n### When to investigate\n\nIf you encounter any of these signals, dig deeper before moving on:\n- Multiple elements with identical or vague labels (e.g., several \"Button\" or \"div\" entries)\n- Interactive elements missing from the accessibility tree entirely\n- Custom components that render as opaque containers with no semantic children\n- Difficulty distinguishing between similar repeated elements (e.g., rows in a table)\n- Elements you can see in the screenshot but can't find in the tree\n\n### How to investigate\n\n1. **Use JavaScript evaluation** to inspect the actual DOM structure:\n \\`\\`\\`bash\n canary session evaluate --js \"() => document.querySelector('[data-testid]')?.outerHTML\"\n canary session evaluate --js \"() => document.querySelectorAll('button').length\"\n canary session evaluate --js \"() => [...document.querySelectorAll('button')].map(b => ({ text: b.textContent?.trim(), role: b.getAttribute('role'), ariaLabel: b.getAttribute('aria-label') }))\"\n \\`\\`\\`\n\n2. **Read the component source code** — find and read the relevant component or template file to understand why the accessibility tree looks the way it does.\n\n3. **Compare screenshot vs tree** — 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.\n\n### What to suggest\n\nWhen you find testability gaps, suggest specific fixes to the user. Frame suggestions as improvements that help both automated testing and human accessibility:\n\n- **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.\"\n- **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.\"\n- **Missing headings/landmarks**: \"This page section has no heading — screen readers and test agents can't tell what it contains. Adding an \\`<h2>\\` or \\`aria-label\\` to the \\`<section>\\` would help.\"\n- **Unlabeled form fields**: \"These inputs have placeholder text but no associated \\`<label>\\`. Adding \\`<label htmlFor=\"...\">\\` or \\`aria-label\\` would make them reliably targetable.\"\n- **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.\"\n\n**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.\n\n## Creating Tests from Browser Exploration\n\nAfter exploring an app via \\`canary session\\`, you can persist what you discovered as a reusable test workflow.\n\n### Explore → Author → Create flow\n\n1. **Explore**: Use \\`canary session\\` to navigate, snapshot, and interact with the app\n2. **Author**: Construct workflow JSON with nodes and playbook steps based on what was observed\n3. **Create**: Pipe JSON to \\`canary workflow create\\`\n4. **Verify**: Check with \\`canary workflow get <id>\\`\n\n### Node authoring guidance\n\n- Group related actions into single action nodes (one node per logical step, not per click)\n- Node types: \\`login\\` (authentication), \\`navigate\\` (go to URL), \\`action\\` (interact with elements), \\`assertion\\` (verify standalone page state), \\`end\\` (terminal node)\n- Every action node **must** have a \\`customActionTitle\\` — a short (3-8 word) human-readable title describing the step\n- **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)\n- Inline assertion format: \\`{ \"condition\": \"...\", \"severity\": \"fail\" }\\`. **Do not author an \\`id\\` field** — the server stamps a UUID on persist. Hand-rolled UUIDs (especially placeholders like \\`a1b2c3d4-...\\`) are rejected and replaced.\n\n### Playbook step authoring guidance\n\n- Use **semantic targets** from the accessibility tree (button text, field labels, placeholder text) — NOT refs or CSS selectors\n- Target format: describe as a human would (\"Submit button\", \"Email input field\", \"Status dropdown\")\n- For \\`fill\\` actions: include \\`params: { \"value\": \"...\" }\\`\n- **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.\n- Prefix test data with \"QA Test\" for identifiability (e.g., \\`\"QA Test $FIRST_NAME()\"\\`)\n- 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\\`\n\n### Using macros and variables\n\nMacros are resolved at run time — each execution gets fresh values, making tests repeatable and collision-free.\n\n**Available macros:**\n\n| Macro | Args | Output | Example |\n|-------|------|--------|---------|\n| \\`$EMAIL()\\` | — | Unique test email | \\`test-xk9mq2@example-test.com\\` |\n| \\`$FIRST_NAME()\\` | — | Random first name | \\`Jordan\\` |\n| \\`$LAST_NAME()\\` | — | Random last name | \\`Chen\\` |\n| \\`$PHONE()\\` | — | US phone (555 prefix) | \\`+1-555-382-7491\\` |\n| \\`$UUID()\\` | — | Random UUID v4 | \\`a1b2c3d4-...\\` |\n| \\`$RANDOM_INT(min, max)\\` | 2 ints | Random integer (inclusive) | \\`$RANDOM_INT(100,999)\\` → \\`427\\` |\n| \\`$RANDOM_STRING(length)\\` | 1 int (1–1000) | Alphanumeric string | \\`$RANDOM_STRING(8)\\` → \\`xK9mQ3bR\\` |\n| \\`$TODAY()\\` | — | Current date (YYYY-MM-DD) | \\`2026-04-06\\` |\n| \\`$DATE(days)\\` | 1 int | Date offset from today | \\`$DATE(-7)\\` → last week |\n| \\`$NOW()\\` | — | ISO 8601 timestamp | \\`2026-04-06T14:30:00.000Z\\` |\n| \\`$TIMESTAMP()\\` | — | Unix milliseconds | \\`1771520400000\\` |\n| \\`$SEQUENCE(\"name\")\\` | counter name | Auto-incrementing per run | \\`1\\`, \\`2\\`, \\`3\\`... |\n\n**When to use macros:**\n- Email fields → \\`$EMAIL()\\`\n- Name fields → \\`$FIRST_NAME()\\` / \\`$LAST_NAME()\\`\n- Amounts, quantities, identifiers → \\`$RANDOM_INT()\\` or \\`$RANDOM_STRING()\\`\n- Date fields → \\`$TODAY()\\` / \\`$DATE()\\`\n- Sequential numbering → \\`$SEQUENCE(\"name\")\\`\n- Macros combine inline: \\`\"QA Test $FIRST_NAME() $LAST_NAME()\"\\`, \\`\"order-$RANDOM_STRING(6)\"\\`\n\n**Variables (\\`$var.name\\`):**\n- Steps can reference data captured from earlier steps using \\`$var.name\\`\n- Variable values are extracted during execution and available to subsequent steps\n- Use step-qualified refs when multiple steps set the same variable: \\`$var.Step2_Create_Client.name\\`\n\n### Credential selection\n\n**Always use \\`credentialId\\` (UUID) in the login node — never just \\`credentialName\\`.**\n\n1. **List available credentials** before authoring:\n \\`\\`\\`bash\n canary credential list --json\n \\`\\`\\`\n This returns each credential's \\`id\\`, \\`name\\`, \\`isDefault\\`, \\`propertyName\\`, and environment info.\n\n2. **Pick the right credential**:\n - Prefer the **default credential** (\\`isDefault: true\\`)\n - **Never** use a credential with \"local\" in the name — it won't work in deployed environments\n - If multiple non-local credentials exist and you're unsure which to use, **ask the user**\n\n3. **Set both \\`credentialId\\` and \\`credentialName\\`** in the login node's \\`configJson\\`:\n \\`\\`\\`json\n { \"credentialId\": \"4f6db838-...\", \"credentialName\": \"QA User\" }\n \\`\\`\\`\n\n4. **If no matching credential exists**, warn the user that one needs to be created first in the web app.\n\nThe 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.\n\n### Environment-aware publishing\n\n- **CLI-created workflows default to \\`published\\`** — 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.\n- **Deployed/known environment**: Use \\`--run-and-publish\\` — test runs immediately, publishes if it passes.\n\n### Usage\n\n\\`\\`\\`bash\n# Create (published by default)\necho '<workflow JSON>' | canary workflow create --from-stdin\n\n# Run-then-publish for deployed environments\necho '<workflow JSON>' | canary workflow create --from-stdin --run-and-publish --property \"My App\"\n\n# From file\ncanary workflow create --from-file workflow.json\n\\`\\`\\`\n\n### Example workflow JSON\n\n\\`\\`\\`json\n{\n \"name\": \"Verify user can create an invoice\",\n \"description\": \"Tests the invoice creation flow end-to-end\",\n \"nodes\": [\n {\n \"nodeType\": \"login\",\n \"configJson\": {\n \"credentialId\": \"4f6db838-16be-49e8-8887-8d0be1c83a87\",\n \"credentialName\": \"QA User\"\n }\n },\n {\n \"nodeType\": \"navigate\",\n \"configJson\": { \"pageUrl\": \"/invoices\", \"pageTitle\": \"Invoices\" }\n },\n {\n \"nodeType\": \"action\",\n \"configJson\": {\n \"customActionTitle\": \"Create a new invoice\",\n \"actionDescription\": \"Fill out the new invoice form and save\",\n \"playbookSteps\": [\n { \"order\": 1, \"action\": \"click\", \"target\": \"New Invoice button\" },\n { \"order\": 2, \"action\": \"fill\", \"target\": \"Client name field\", \"params\": { \"value\": \"QA Test $FIRST_NAME()\" } },\n { \"order\": 3, \"action\": \"fill\", \"target\": \"Amount field\", \"params\": { \"value\": \"$RANDOM_INT(100,999)\" } },\n { \"order\": 4, \"action\": \"click\", \"target\": \"Save button\" }\n ],\n \"inlineAssertions\": [\n {\n \"condition\": \"Success message 'Invoice created' is visible and the new invoice appears in the list\",\n \"severity\": \"fail\"\n }\n ]\n }\n }\n ]\n}\n\\`\\`\\`\n\n### Updating existing workflows\n\nUse \\`canary workflow sync\\` to replace a workflow's nodes. It accepts the same\nsimple format as create — no edges, positions, or IDs needed:\n\n\\`\\`\\`bash\n# From stdin\necho '{\"nodes\": [...]}' | canary workflow sync <workflowId> --from-stdin\n\n# From file\ncanary workflow sync <workflowId> --from-file updated.json\n\n# Create a proposal for review instead of applying directly\ncanary workflow sync <workflowId> --from-file updated.json --propose\n\\`\\`\\`\n\nWhen using \\`--propose\\` on an existing workflow, fetch the current state first\nand reuse each node's \\`id\\` for steps you keep — only omit \\`id\\` for genuinely\nnew steps. This is the best way to ensure the diff stays accurate so reviewers\nsee only the fields that actually changed:\n\n\\`\\`\\`bash\n# Round-trip pattern for proposals\ncanary workflow get <workflowId> --format json > current.json\n# edit current.json: change configJsons, keep ids you're not replacing\ncanary workflow sync <workflowId> --from-file current.json --propose\n\\`\\`\\`\n\n### Generating API sequences\n\nConvert a browser-based workflow to a fast API sequence using its latest successful run:\n\n\\`\\`\\`bash\n# Generate from the workflow's latest run\ncanary workflow generate-api-sequence <workflowId>\n\n# With additional guidance\ncanary workflow generate-api-sequence <workflowId> --guidance \"Focus on the create endpoint\"\n\n# JSON output\ncanary workflow generate-api-sequence <workflowId> --json\n\\`\\`\\`\n\n### Reference\n\nRun \\`canary workflow guide\\` for the full authoring reference:\n\n\\`\\`\\`bash\ncanary workflow guide # All sections\ncanary workflow guide node-types # Available node types\ncanary workflow guide macros # Dynamic test data ($EMAIL, $RANDOM_STRING, etc.)\ncanary workflow guide playbook # Playbook steps and caching\ncanary workflow guide seed-workflows # Seed workflow patterns\ncanary workflow guide assertions # Inline and standalone assertions\n\\`\\`\\`\n`,\n};\n","import type { SkillTemplate } from './issue-log-xref.js';\n\nexport const agentTasksTemplate: SkillTemplate = {\n name: 'agent-tasks',\n description: 'Manage trigger-based agent tasks for deferred work items',\n version: '1.0.0',\n render: () => `---\nname: agent-tasks\nversion: 1.0.0\ndescription: 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.\nallowed-tools: Bash(canary task:*), Read, Grep, Glob\n---\n\n# Agent Tasks with Canary\n\nUse \\`canary task\\` to create and manage **trigger-based work items** — deferred tasks that become actionable when a system event fires (deploy, flag change, schedule, etc.).\n\n## Prerequisites\n\n- \\`canary\\` CLI installed and authenticated (\\`canary login\\`)\n- The **AGENT_TASKS** feature flag must be enabled for the org\n- Run \\`canary task --help\\` for the full command reference\n\n## Quick Reference\n\n\\`\\`\\`bash\n# Create a task\ncanary task create --title \"Remove feature flag\" \\\\\n --description \"Delete COOL_FEATURE flag and all gating code\" \\\\\n --trigger flag-deleted --flag COOL_FEATURE\n\n# List tasks\ncanary task list # all tasks\ncanary task list --status waiting # waiting for trigger\ncanary task list --status actionable # ready to work on\ncanary task list --trigger production-deploy # by trigger type\n\n# Manage tasks\ncanary task get <id> # view details + event history\ncanary task update <id> --title \"New title\" # update task fields\ncanary task claim <id> # mark in progress\ncanary task complete <id> # mark done\ncanary task complete <id> --commit abc123 # done, link to resolution commit\ncanary task cancel <id> # cancel\ncanary task trigger <id> # manually fire trigger\n\\`\\`\\`\n\n## Trigger Types\n\n| Type | Fires when... | Auto-fired? | Required flag |\n|------|---------------|-------------|---------------|\n| \\`production-deploy\\` | Code deployed to production | **Yes** | — |\n| \\`flag-deprecated\\` | Feature flag set to deprecated | Yes | \\`--flag <name>\\` |\n| \\`flag-cleanup\\` | Feature flag set to ready_for_cleanup | Yes | \\`--flag <name>\\` |\n| \\`flag-deleted\\` | Feature flag deleted | Yes | \\`--flag <name>\\` |\n| \\`knob-deprecated\\` | Knob set to deprecated | Yes | \\`--knob <key>\\` |\n| \\`knob-cleanup\\` | Knob set to ready_for_cleanup | Yes | \\`--knob <key>\\` |\n| \\`knob-deleted\\` | Knob deleted | Yes | \\`--knob <key>\\` |\n| \\`scheduled\\` | A date/time is reached | Yes | \\`--scheduled-date <ISO>\\` |\n| \\`pr-merged\\` | A specific PR is merged | Yes | \\`--pr <number>\\` |\n| \\`immediate\\` | Immediately (no waiting) | N/A | — |\n| \\`manual\\` | Manually triggered via CLI | N/A | — |\n\n**Note:** \\`pr-merged\\` tasks are scoped to the linked repository selected by \\`--repo\\`, \\`--role\\`, or \\`--repo-id\\`.\n\n## Task Lifecycle\n\n\\`\\`\\`\n ┌─── immediate ───► actionable\n │ │\n create ──────────┤ claim\n │ │\n └─── other types ──► waiting ──trigger──► actionable ──► in_progress ──► completed\n ├──► canceled\n └──► expired\n\\`\\`\\`\n\n- **waiting**: Created, waiting for its trigger event\n- **actionable**: Trigger fired (or immediate type), ready to be worked on\n- **in_progress**: Claimed by someone, actively being worked\n- **completed**: Done (optionally linked to a resolution commit)\n- **canceled** / **expired**: Terminated without completion\n\n## Common Workflows\n\n### Post-deploy cleanup (remove temporary code after next production deploy)\n\n\\`\\`\\`bash\ncanary task create \\\\\n --title \"Remove backwards-compat shim for widget API\" \\\\\n --description \"Old endpoint can be removed now that new endpoint is deployed.\" \\\\\n --trigger production-deploy \\\\\n --priority 1 \\\\\n --tag cleanup --tag api\n\\`\\`\\`\n\nThis task automatically becomes actionable after the next successful production deployment.\n\n### Flag removal after GA\n\nWhen a feature graduates from behind a flag:\n\n\\`\\`\\`bash\ncanary task create \\\\\n --title \"Clean up COOL_FEATURE flag gating code\" \\\\\n --description \"Feature is GA. Remove all gating checks and delete the constant.\" \\\\\n --trigger flag-deprecated --flag COOL_FEATURE \\\\\n --tag cleanup --tag feature-flags\n\\`\\`\\`\n\nAuto-fires when someone runs \\`canary feature-flag lifecycle COOL_FEATURE --stage deprecated\\`.\n\n### Knob deprecation follow-up\n\n\\`\\`\\`bash\ncanary task create \\\\\n --title \"Remove agent.truncation.enabled knob reads\" \\\\\n --description \"Replace all knob reads with the hardcoded final value.\" \\\\\n --trigger knob-deprecated --knob agent.truncation.enabled \\\\\n --tag cleanup\n\\`\\`\\`\n\n### Scheduled future work\n\n\\`\\`\\`bash\ncanary task create \\\\\n --title \"Rotate API signing keys\" \\\\\n --description \"Quarterly key rotation per security policy.\" \\\\\n --trigger scheduled --scheduled-date 2026-06-30T00:00:00Z \\\\\n --priority 2 --tag security\n\\`\\`\\`\n\n### Immediate task (starts actionable, no trigger wait)\n\n\\`\\`\\`bash\ncanary task create \\\\\n --title \"Fix type error in auth middleware\" \\\\\n --description \"Strict mode caught a missing null check. Needs immediate fix.\" \\\\\n --trigger immediate \\\\\n --priority 2 --assign dev@example.com\n\\`\\`\\`\n\n## Integration Points\n\n### Feature Flags → Tasks\n\nFlag lifecycle changes automatically fire matching task triggers:\n- \\`canary feature-flag lifecycle <name> --stage deprecated\\` → fires \\`flag-deprecated\\` tasks\n- \\`canary feature-flag lifecycle <name> --stage ready_for_cleanup\\` → fires \\`flag-cleanup\\` tasks\n- \\`canary feature-flag delete <name>\\` → fires \\`flag-deleted\\` tasks\n\n### Knobs → Tasks\n\nSame pattern as flags:\n- \\`canary knobs lifecycle <key> --stage deprecated\\` → fires \\`knob-deprecated\\` tasks\n- \\`canary knobs lifecycle <key> --stage ready_for_cleanup\\` → fires \\`knob-cleanup\\` tasks\n- \\`canary knobs delete <key>\\` → fires \\`knob-deleted\\` tasks\n\n### Production Deploys → Tasks\n\nThe deployment notifier Lambda fires \\`production-deploy\\` task triggers automatically on successful production deployments. Any waiting tasks with this trigger type transition to \\`actionable\\`.\n\n## Tips\n\n- \\`--commit\\` defaults to \\`git HEAD\\` — auto-detects the current commit\n- \\`--repo\\` is auto-selected if the org has a single linked repository\n- \\`--tag\\` is repeatable: \\`--tag cleanup --tag api --tag urgent\\`\n- \\`--description-file <path>\\` reads description from a file (useful for long markdown descriptions)\n- \\`--json\\` on any command returns structured JSON output for scripting\n- \\`--priority\\`: 0 = normal (default), 1 = high, 2 = critical\n- \\`--expires <ISO date>\\` sets an expiry after which the task auto-expires if not completed\n- Use \\`canary task trigger <id>\\` to manually fire any waiting task's trigger\n`,\n};\n","import type { SkillTemplate } from './issue-log-xref.js';\n\nexport const canaryBasicTemplate: SkillTemplate = {\n name: 'canary-basic',\n description: 'Learn the Canary CLI — commands, help, and common patterns',\n version: '1.0.0',\n render: () => `---\nname: canary-basic\nversion: 1.0.0\ndescription: 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.\nallowed-tools: Read, Grep, Glob\n---\n\n# Canary CLI Guide\n\nThe \\`canary\\` CLI is a command-line tool for running tests, controlling browser sessions, managing workflows, and inspecting issues.\n\n## Install & Login\n\n\\`\\`\\`bash\n# Install globally\nnpm install -g @canaryai/cli\n# or\nbun add -g @canaryai/cli\n\n# Authenticate (opens browser)\ncanary login\n\n# Login to a specific org (for multi-org users)\ncanary login --org \"My Org\"\n\\`\\`\\`\n\n### Environment switching\n\n\\`\\`\\`bash\n# Production (default)\ncanary login\n\n# Dev\ncanary login --app-url https://app.dev.trycanary.ai --api-url https://api.dev.trycanary.ai\n\n# Local\ncanary login --app-url http://localhost:5173 --api-url http://localhost:3000\n\n# Or set the env var\nexport CANARY_API_URL=http://localhost:3000\n\\`\\`\\`\n\n---\n\n## Command Reference\n\n### Test Suites\n\n\\`\\`\\`bash\n# Trigger a test suite and stream results\ncanary test-suite run\ncanary test-suite run --tag smoke\ncanary test-suite run --property \"My App\" --environment staging --verbose\n\n# Query test suite results\ncanary test-suite list\ncanary test-suite get <id> --failures-only\ncanary test-suite diagnostics <workflowId> <runId>\ncanary test-suite steps <workflowId> <runId>\n\\`\\`\\`\n\nRun options: \\`--token\\`, \\`--property\\`, \\`--environment\\`, \\`--tag\\`, \\`--name-pattern\\`, \\`--verbose\\`\n\n---\n\n### Browser Sessions\n\nStart and control browser sessions for interactive testing.\n\n#### Session lifecycle\n\n\\`\\`\\`bash\ncanary session start # Start a new session\ncanary session start --credential \"admin\" # Start with a credential\ncanary session start --url https://example.com --headless\ncanary session list # List active sessions\ncanary session status # Show session details\ncanary session stop # Stop the current session\ncanary session stop --all # Stop all sessions\ncanary session load-credential # Load a credential into a running session\ncanary session allow-screenshots # Grant Claude auto-read for screenshots\n\\`\\`\\`\n\n#### Browser actions\n\n\\`\\`\\`bash\n# Navigation\ncanary session navigate --url https://example.com\ncanary session back\n\n# Observe the page\ncanary session snapshot # Get accessibility tree\ncanary session snapshot --mode screenshot # Get a screenshot\ncanary session snapshot --mode both # Tree + screenshot\ncanary session snapshot --search \"Login\" # Search for text\ncanary session snapshot --expand e5 # Expand a collapsed ref\ncanary session screenshot --full-page # Full-page screenshot\n\n# Interact with elements (use ref from snapshot)\ncanary session click --ref e12\ncanary session type --ref e8 --text \"hello\" --submit\ncanary session fill --fields '[{\"ref\":\"e1\",\"value\":\"user@example.com\"},{\"ref\":\"e2\",\"value\":\"pass123\"}]'\ncanary session select --ref e15 --value \"option-1\"\ncanary session hover --ref e3\ncanary session press --key Enter\ncanary session scroll --direction down --amount 3\n\n# Tabs\ncanary session tabs # List tabs\ncanary session tabs --action new # Open new tab\ncanary session tabs --action select --index 0 # Switch to tab\ncanary session tabs --action close # Close current tab\n\n# Debugging\ncanary session wait --text \"Success\" --timeout 5000\ncanary session console --only-errors # Show console messages\ncanary session network # Show network requests\ncanary session evaluate --js \"document.title\" # Run JavaScript\n\\`\\`\\`\n\nCommon options: \\`--session <id|name>\\` (target a specific session), \\`--json\\`, \\`--env\\`\n\n#### Control (for cloud-connected sessions)\n\n\\`\\`\\`bash\ncanary session take-control # Force CLI control (pauses cloud)\ncanary session resume-cloud # Resume cloud execution\n\\`\\`\\`\n\n---\n\n### Recording\n\nRecord browser interactions for creating test workflows.\n\n\\`\\`\\`bash\ncanary record\ncanary record --credential admin --url https://example.com\ncanary record --output ./recordings --no-upload # Save locally only\n\\`\\`\\`\n\nOptions: \\`--credential\\`, \\`--url\\`, \\`--output\\`, \\`--env\\`, \\`--no-upload\\`\n\n---\n\n### Workflows\n\nInspect and manage workflow definitions.\n\n\\`\\`\\`bash\n# List and search\ncanary workflow list\ncanary workflow list --search \"checkout\" --status published --json\n\n# Get workflow definition\ncanary workflow get <workflowId> --format markdown\ncanary workflow get <workflowId> --json\n\n# Create from file or stdin\ncanary workflow create --from-file workflow.json\ncanary workflow create --run-and-publish --property \"My App\"\n\n# Update workflow\ncanary workflow patch <workflowId> --name \"New Name\" --status published\n\n# Run a workflow\ncanary workflow run <workflowId> --stream\n\n# Replace all nodes/edges\ncanary workflow sync <workflowId> --from-file updated.json\n\n# Authoring guide\ncanary workflow guide\ncanary workflow guide selectors\n\\`\\`\\`\n\nList filters: \\`--search\\`, \\`--flow-type\\` (standard|setup|seed|teardown), \\`--status\\` (draft|published), \\`--page\\`, \\`--page-size\\`\n\nWorkflow statuses: \\`draft\\` = editable, not run by suites. \\`published\\` = active, included in regression runs. Reliability is tracked automatically by the \\`health\\` dimension — a flow that keeps failing in suites becomes \\`degraded\\` and is skipped (shown \"degraded — not run\") until it's edited or manually re-enabled.\n\n---\n\n### Issues\n\nSearch and inspect QA issues detected by Canary.\n\n\\`\\`\\`bash\n# List issues\ncanary issues list\ncanary issues list --severity high --status open --format markdown\ncanary issues list --search \"timeout\" --property-id <uuid>\n\n# Get issue details\ncanary issues get <issue-id>\ncanary issues get <issue-id> --format markdown\n\\`\\`\\`\n\nList options: \\`--search\\`, \\`--severity\\` (low|medium|high|unknown), \\`--status\\` (open|closed|not_a_bug), \\`--property-id\\`, \\`--page\\`, \\`--page-size\\`, \\`--json\\`, \\`--format markdown\\`\n\n---\n\n### Release QA (CI/CD)\n\nTrigger and monitor Release QA runs from CI pipelines.\n\n\\`\\`\\`bash\ncanary release trigger --property-id <uuid>\ncanary release status <run-id> --json\ncanary release run --property-id <uuid> --timeout 600 # Trigger and poll\n\\`\\`\\`\n\n---\n\n### Properties & Credentials\n\n\\`\\`\\`bash\ncanary property list # List properties and linked repos\ncanary credential list # List credentials with environment info\n\\`\\`\\`\n\n---\n\n### Superadmin Commands\n\nThese commands require superadmin access.\n\n\\`\\`\\`bash\n# Knobs (global runtime config)\ncanary knobs list\ncanary knobs get <key>\ncanary knobs set <key> <value> --type boolean|number|string\ncanary knobs toggle <key>\ncanary knobs delete <key>\ncanary knobs lifecycle <key> --stage <stage>\n\n# Feature flags (per-org gating)\ncanary feature-flag list\ncanary feature-flag create <name> --description \"...\"\ncanary feature-flag enable <name> --org <orgId>\ncanary feature-flag disable <name> --org <orgId>\ncanary feature-flag delete <name>\ncanary feature-flag lifecycle <name> --stage <stage>\n\n# Database (read-only)\ncanary psql \"SELECT * FROM knobs LIMIT 5\" --json\n\n# Redis (read-only)\ncanary redis \"GET some:key\" --json\n\n# Documentation pages\ncanary docs <sub-command>\n\n# Debug sessions\ncanary debug-session --env dev --json\n\\`\\`\\`\n\n---\n\n## Common Patterns\n\n| Pattern | Description |\n|---------|-------------|\n| \\`--json\\` | Raw JSON output (most commands) |\n| \\`--format markdown\\` | Markdown-formatted output (issues, workflows) |\n| \\`--env <env>\\` | Target environment: \\`local\\`, \\`dev\\`, \\`prod\\` |\n| \\`--verbose, -v\\` | Show all events / detailed output |\n| \\`-h, --help\\` | Show help for any command |\n\n### Environment variables\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| \\`CANARY_API_URL\\` | \\`https://api.trycanary.ai\\` | API endpoint |\n| \\`CANARY_APP_URL\\` | \\`https://app.trycanary.ai\\` | App URL for login |\n| \\`CANARY_API_TOKEN\\` | — | API key (alternative to \\`canary login\\`) |\n\n## Getting Help\n\n\\`\\`\\`bash\ncanary help # Show all commands\ncanary <command> --help # Show help for a specific command\ncanary version # Show CLI version\ncanary upgrade # Upgrade to latest version\n\\`\\`\\`\n`,\n};\n","import type { SkillTemplate } from './issue-log-xref.js';\n\nexport const testCasesTemplate: SkillTemplate = {\n name: 'test-cases',\n description:\n '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.',\n version: '2.2.0',\n render: () => `---\nname: test-cases\nversion: 2.2.0\ndescription: 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.\nallowed-tools: Read, Grep, Glob, Bash, Write\n---\n\n# Test Case Authoring (Canary)\n\nUse 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** — 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.\n\nThe skill has three layers:\n1. **Industry rubric** — what makes a test case good (ISTQB-aligned).\n2. **Spec mapping** — how each rubric piece becomes a section of \\`.canary.md\\`.\n3. **Domain Context** — customer-specific sources of truth, filled in once.\n\nRead the Domain Context block at the bottom **first**. If it is unfilled, follow the discovery instructions there before authoring any cases.\n\n---\n\n## 1. The 7-rule rubric\n\nEach case must satisfy all seven. If a rule cannot be satisfied, say so explicitly and explain why — don't silently skip.\n\n### 1. One coherent journey per case (atomic, but not micro)\nA case covers a single coherent journey — 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.\n\n- Good (coherent journey, multiple checks): \"Onboard an org: create org → invite a user → claim invite → land on dashboard.\" Each step verifies its own outcome inline.\n- Good (single short journey): \"Submit a ticket and confirm it appears in the queue.\"\n- Bad (unrelated jobs): \"Submit a ticket, edit a user, generate a report.\" — three journeys, three separate \\`.canary.md\\` files.\n\n**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.\n\n### 2. Specific, observable expected results\nEvery assertion must be something a stranger could verify without judgement.\n- Good: \"URL ends with \\`/dashboard\\`.\" / \"A row with name \\`X\\` appears in the table.\"\n- Bad: \"The page looks correct.\" / \"It works.\"\n\nLint enforces this as **R1**: a spec with zero \\`fail\\`-severity assertions warns.\n\n### 3. Independent and self-sufficient\nA 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.\n\n### 4. Traceable\nEvery case carries \\`requirementRef:\\` in frontmatter pointing at the ticket / PRD / design. If you can't find one, flag the gap to the user — untraceable specs rot. Lint **R2** warns when missing.\n\n### 5. Risk-prioritized\nTag each case with a priority via the \\`tags:\\` array. Use one of: \\`p0\\`, \\`p1\\`, \\`p2\\`.\n- **p0** — happy path of the core promise; if this breaks, the feature is broken.\n- **p1** — primary edge cases, validation, role variants, common error states.\n- **p2** — rare conditions, visual/agentic checks, low-frequency edges.\n\n### 6. Includes preconditions, data, and cleanup\n- **Auth/role preconditions** → \\`actor:\\` in frontmatter.\n- **Other preconditions** (feature flags, seeded data, sister flows) → bulleted prose in \\`## Preflight\\`.\n- **Test data** → \\`## Inputs\\` (with macros) and per-variant \\`## Scenarios\\`.\n- **Teardown** → bulleted prose in \\`## Cleanup\\`.\n\n### 7. Written as human intent, not UI mechanics\nAction 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.\n- Good: \\`**Action — Assign the ticket to Support**\\`\n- Bad: \\`**Action — Click 'Assign' dropdown, scroll, click 'Support', click Save**\\`\n\nLint **R9** warns when an action reads as click-by-click.\n\n---\n\n## 2. Coverage checklist\n\nBefore handing back a set of specs, sanity-check coverage. For each, either include a case or note why it doesn't apply:\n- Happy path (p0)\n- Primary edges / boundary inputs\n- Negative paths / validation errors\n- Permissions and role variants (admin / member / unauthenticated)\n- Empty, loading, and error states\n- Cross-entity side effects (does this change something visible elsewhere?)\n- Cleanup / undo where applicable\n\nAim for the smallest set that proves the feature works. Five sharp cases beat twenty fuzzy ones.\n\n---\n\n## 3. Authoring \\`.canary.md\\`\n\n\\`.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.\n\nRun these for the definitive grammar — do **not** restate them here:\n\n\\`\\`\\`bash\ncanary spec --help # validate / compile / diff commands\ncanary spec validate <file> # parse + lint offline; enforce the rules in §1\ncanary spec compile <file> # create or update the workflow (prints the live URL)\ncanary workflow guide # node-shape and macro reference\n\\`\\`\\`\n\nAfter \\`canary spec compile\\`, echo the printed \\`URL:\\` line back to the user verbatim — 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.\n\nMapping at a glance:\n\n| Rubric piece | Where it lives in \\`.canary.md\\` |\n|-----------------------------------------|---------------------------------------------------------------------------|\n| Case title | \\`frontmatter.name\\` |\n| Requirement / ticket ref | \\`frontmatter.requirementRef\\` |\n| Priority (rule 5) | \\`tags: [p0]\\` (or p1/p2) in frontmatter |\n| 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\\`. |\n| Feature flag / seeded data preconditions| Bulleted prose in \\`## Preflight\\` (\"Feature flag X enabled.\", \"Seed workflow Y published.\") |\n| One-paragraph user-visible promise | \\`## Spirit\\` (blockquote) |\n| Workflow-level declared inputs | \\`## Inputs\\` (e.g. \\`- foo: \\\\$EMAIL\\`) — parses to \\`workflows.inputs_json\\`, surfaces in Workflow Settings → Inputs |\n| Per-step bound values | \\`- field X: Y\\` under an Action — populates *that* step's Inputs panel with \\`Y\\` as the default (compiler mirrors fields → \\`dataRequirements\\` + \\`inputValues\\` on the node). Prefer \\`## Inputs\\` for values shared across actions. |\n| Variants (free / enterprise, etc.) | \\`## Scenarios\\` with per-scenario \\`values:\\` / \\`credentials:\\` overrides |\n| Steps (human intent) | \\`## Journey\\` — \\`**Navigate to** …\\`, \\`**Action — …**\\`, \\`**Seed** …\\` |\n| Inline expected results | \\`- assert (fail \\\\| warn): <condition>\\` under the action that produced it |\n| Standalone assertion | \\`**Assert** <condition>\\` node (prefer inline when possible) |\n| Role change mid-journey | \\`**Switch role to** <X>\\` — 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. |\n| Cross-action data flow | \\`- capture: <name>\\` on the upstream action, \\`\\\\$var.<name>\\` downstream |\n| Cleanup | Bulleted prose in \\`## Cleanup\\` |\n\nWhen 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.\n\n### Inputs defaults — the decision rule\n\nWhen you need a value (workflow-level \\`## Inputs\\` or step-level \\`- field\\`), pick where it comes from in this order:\n\n1. **Names existing data in the target environment** — 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:\n - \\`canary property list\\` — properties (target apps)\n - \\`canary credential-template list --property \"<property>\"\\` — credential templates (these are what \\`actor:\\` and \\`- credential …\\` reference)\n - \\`canary workflow list --search <term>\\` — existing workflows to seed/link to\n - \\`canary feature-flag list --env <env>\\` — flag names for Preflight prose\n2. **Synthesizable** — 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.\n3. **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.\n\n---\n\n## 4. Output format\n\nFor 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 — check Domain Context below).\n\n### Summary table\n\n| file | name | priority | requirementRef |\n|-----------------------------------|--------------------------------------------|----------|------------------|\n| canary/specs/submit-ticket.canary.md | Submit a ticket and confirm it appears in the queue | p0 | <ticket/PRD ref> |\n\n### Per-case file\n\n\\`\\`\\`markdown\n---\nname: <one user goal, action-oriented>\nactor: <credential template name from Domain Context — verbatim, e.g. \"Admin\"; or \\`credential <name>\\` for a specific raw credential>\nauthenticated: true\ntags: [p0] # or p1 / p2; add other tags from Domain Context's tagging conventions\nrequirementRef: <ticket id, PRD path, design link>\n---\n\n# <same as name>\n\n## Spirit\n> <one short paragraph: the user-visible promise this case proves>\n\n## Preflight\n- <feature flag / seed / sister flow / role precondition, one per bullet>\n\n## Cleanup\n- <records to delete, flags to reset>\n\n## Inputs\n- <name>: <value or macro>\n\n## Journey\n\n**Navigate to** \\\\\\`/some/path\\\\\\`.\n\n**Action — <human intent>**\n> <one-sentence context for the recorder/agent>\n- field <name> [(type)]: <value or \\\\$input>\n- capture: <name> # optional, only if a later step needs it via \\\\$var.<name>\n- assert (fail): <observable condition>\n- assert (warn): <softer signal>\n\n**End**.\n\\`\\`\\`\n\nKeep journeys short (3–8 nodes). If a case grows past that, it's probably two cases.\n\n---\n\n## 5. Worked example (generic)\n\nRequest: \"Test the support ticket flow.\"\n\n| file | name | priority | requirementRef |\n|-------------------------------------------|-----------------------------------------------------|----------|----------------|\n| canary/specs/submit-ticket.canary.md | Submit a ticket and confirm it appears in the queue | p0 | <fill in> |\n| canary/specs/ticket-required-fields.canary.md | Required-field validation blocks submit | p1 | <fill in> |\n| canary/specs/ticket-non-admin-reassign.canary.md | Non-admin cannot reassign a ticket | p1 | <fill in> |\n\n\\`canary/specs/submit-ticket.canary.md\\`:\n\n\\`\\`\\`markdown\n---\nname: Submit a ticket and confirm it appears in the queue\nactor: Admin\nauthenticated: true\ntags: [p0, smoke, support]\nrequirementRef: <fill in>\n---\n\n# Submit a ticket and confirm it appears in the queue\n\n## Spirit\n> A member can file a support ticket from the in-app form and immediately\n> see it land in their team's queue without a refresh.\n\n## Preflight\n- At least one Support team exists in the test org (seed workflow \\\\\\`support-team-seed\\\\\\`).\n- Feature flag \\\\\\`support_inbox_v3\\\\\\` is enabled.\n\n## Cleanup\n- Delete the created ticket.\n\n## Inputs\n- subject: \"QA \\\\$UUID\"\n- priority: high\n\n## Journey\n\n**Navigate to** \\\\\\`/support/new\\\\\\`.\n\n**Action — Submit the ticket**\n> Fill subject and priority, then submit.\n- field subject: \\\\$subject\n- field priority (select): \\\\$priority\n- assert (warn): Toast contains \"Ticket created\"\n- assert (fail): URL ends with \\\\\\`/support\\\\\\`\n\n**Navigate to** \\\\\\`/support/queue\\\\\\`.\n\n**Action — Confirm it appears in the queue**\n> Locate the new ticket in the team queue.\n- assert (fail): A row matching \\\\\\`\\\\$subject\\\\\\` is visible\n\n**End**.\n\\`\\`\\`\n\nAfter writing, run \\`canary spec validate canary/specs/submit-ticket.canary.md\\` and resolve any errors / warnings before handing back.\n\n---\n\n## 6. Anti-patterns\n\n- Mega-files that try to cover five unrelated journeys.\n- The opposite: splitting one coherent journey into N tiny files that each re-create the same setup. Bundle them into one \\`## Journey\\` instead.\n- Click-by-click action titles (\"Click X, scroll, click Y\").\n- \"Looks correct\" / \"everything works\" assertions.\n- Hardcoded emails, IDs, or names that collide on rerun. Use macros (\\\\$EMAIL, \\\\$UUID).\n- Specs that depend on another spec having run first without an explicit \\`**Seed**\\` / \\`**Setup**\\` node.\n- Skipping \\`requirementRef\\` because \"it's obvious\".\n- Asserting state via toasts only — confirm the underlying record changed.\n- Dropping the priority tag — risk-rank every case (\\`p0\\` / \\`p1\\` / \\`p2\\`).\n\n---\n\n## 7. Domain Context\n\n<!-- AGENT/CUSTOMER: fill in this block on first use of the skill, then commit it. -->\n<!-- If any field below is \"<unfilled>\", do a discovery pass before authoring cases: -->\n<!-- - Read README.md, CLAUDE.md, AGENTS.md, docs/, specs/ for requirement and design pointers. -->\n<!-- - Scan recent git log for ticket-tracker references (JIRA-123, LIN-456, #issue). -->\n<!-- - Run \\`canary property list\\` and \\`canary credential-template list --property \"<property>\"\\` to learn properties + the credential templates that map to \\`actor:\\` values. -->\n<!-- - (\\`canary credential list\\` shows live credentials — useful, but the \\`actor:\\` field references credential *templates*, not credentials.) -->\n<!-- - Run \\`canary workflow list --search <feature>\\` to avoid duplicating existing workflows. -->\n<!-- - Run \\`canary feature-flag list\\` to confirm flag names referenced in Preflight prose. -->\n<!-- - Propose values to the user, get confirmation, then edit this block in place. -->\n\n- **Requirements / PRDs**: <unfilled — paths or URLs, e.g. \\`docs/specs/\\`, Notion space, Linear project>\n- **Designs**: <unfilled — Figma links, design system doc>\n- **Ticket tracker**: <unfilled — Jira/Linear project key + how to format \\`requirementRef\\` (e.g. \\`PROJ-123\\` or a URL)>\n- **Default property**: <unfilled — the property name to target; find via \\`canary property list\\`>\n- **Default environment**: <unfilled — \\`prod\\` | \\`staging\\` | \\`dev\\`>\n- **Credentials / role matrix** (\\`actor:\\` is a credential template name by default — list via \\`canary credential-template list --property \"<property>\"\\`. Prefix with \\`credential \\` to bind a specific raw credential — list via \\`canary credential list\\`. Use the template form unless the case genuinely needs a fixed named account.): <unfilled — 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).>\n- **Tagging conventions**: <unfilled — e.g. \\`smoke\\`, \\`regression\\`, plus the priority tags \\`p0\\` / \\`p1\\` / \\`p2\\`>\n- **Spec directory**: <unfilled — where \\`.canary.md\\` files live in this repo, e.g. \\`canary/specs/\\`>\n- **Existing workflows to extend or avoid duplicating**: <unfilled — search via \\`canary workflow list --search <term>\\`>\n- **Glossary / domain terms**: <unfilled — terms that show up in tickets and should appear verbatim in spec names>\n- **Other sources of truth**: <unfilled — runbooks, support tickets, analytics dashboards>\n\nWhen this block is fully populated, every case you author should reference at least the **Requirements**, **Default property**, **Spec directory**, and **Credentials** entries.\n`,\n};\n","/**\n * `debug-run` skill template.\n *\n * Unlike the other entries in SKILL_TEMPLATES (which inline their content as\n * JS strings), the debug-run skill is rendered from the Handlebars partials in\n * `@chatsdet/diagnostic-playbook`. Source of truth = the playbook partials.\n *\n * Versioning: each released version is a frozen snapshot file under\n * `packages/canary-cli/skills/debug-run/history/<version>.md`. The current\n * version is stamped in `version.json` next to the history dir. To mint a new\n * version after editing partials, run:\n *\n * bun run --filter=@canaryai/cli bump-skill debug-run [patch|minor|major]\n *\n * The drift test in `apps/api/tests/unit/diagnostics/skill-rendered.test.ts`\n * fails if the live `renderSkill()` output diverges from\n * `history/<currentVersion>.md`, forcing a bump rather than silent edits to a\n * previously-released version. `previousVersions` is auto-populated from the\n * history dir so `canary skill diff debug-run` can replay any released version.\n */\n\nimport { readFileSync, readdirSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { SkillTemplate } from './issue-log-xref.js';\n\nconst HERE = dirname(fileURLToPath(import.meta.url));\n\n/**\n * Resolve the on-disk location of the debug-run skill assets.\n *\n * At runtime the assets live in one of two places depending on whether we\n * are running from the source tree (tests, dev), the bundled dist (npm\n * install), or from a published @canaryai/cli inside node_modules:\n *\n * - source: packages/canary-cli/src/skill-templates/debug-run.ts\n * → ../../skills/debug-run/\n * - dist: packages/canary-cli/dist/<chunk>.js\n * → ./skills/debug-run/ (copied by `cp -r skills dist/`)\n *\n * The first candidate that has a readable `version.json` wins.\n */\nfunction resolveSkillDir(): string {\n const candidates = [\n join(HERE, '..', '..', 'skills', 'debug-run'), // source tree\n join(HERE, 'skills', 'debug-run'), // dist\n join(HERE, '..', 'skills', 'debug-run'), // dist split-chunk variant\n ];\n for (const c of candidates) {\n try {\n readFileSync(join(c, 'version.json'), 'utf-8');\n return c;\n } catch {\n // try next\n }\n }\n throw new Error(\n `Could not locate debug-run skill assets relative to ${HERE}. Tried: ${candidates.join(', ')}`,\n );\n}\n\nconst SKILL_DIR = resolveSkillDir();\nconst HISTORY_DIR = join(SKILL_DIR, 'history');\n\nconst versionMeta = JSON.parse(\n readFileSync(join(SKILL_DIR, 'version.json'), 'utf-8'),\n) as { current: string };\n\nfunction readVersion(version: string): string {\n return readFileSync(join(HISTORY_DIR, `${version}.md`), 'utf-8');\n}\n\nfunction listVersionsOnDisk(): string[] {\n return readdirSync(HISTORY_DIR)\n .filter((f) => f.endsWith('.md'))\n .map((f) => f.slice(0, -3));\n}\n\nconst previousVersions: Record<string, () => string> = {};\nfor (const v of listVersionsOnDisk()) {\n if (v !== versionMeta.current) {\n previousVersions[v] = () => readVersion(v);\n }\n}\n\nexport const debugRunTemplate: SkillTemplate = {\n name: 'debug-run',\n description:\n 'Diagnose why a workflow test failed — root-cause an end-to-end run from its materialised artifacts',\n version: versionMeta.current,\n render: () => readVersion(versionMeta.current),\n previousVersions,\n};\n","import type { SkillTemplate } from './issue-log-xref.js';\nimport { issueLogXrefTemplate } from './issue-log-xref.js';\nimport { browserTestingTemplate } from './browser-testing.js';\nimport { agentTasksTemplate } from './agent-tasks.js';\nimport { canaryBasicTemplate } from './canary-basic.js';\nimport { testCasesTemplate } from './test-cases.js';\nimport { debugRunTemplate } from './debug-run.js';\n\n// SkillTemplate kept file-local (defined in issue-log-xref.ts)\n\nexport const SKILL_TEMPLATES: Record<string, SkillTemplate> = {\n 'canary-basic': canaryBasicTemplate,\n 'agent-tasks': agentTasksTemplate,\n 'issue-log-xref': issueLogXrefTemplate,\n 'browser-testing': browserTestingTemplate,\n 'test-cases': testCasesTemplate,\n 'debug-run': debugRunTemplate,\n};\n"],"mappings":";;;;AAAA,SAAS,YAAY,WAAW,qBAAqB;AACrD,SAAS,eAAe;AACxB,SAAS,QAAAA,aAAY;;;ACWd,IAAM,uBAAsC;AAAA,EACjD,MAAM;AAAA,EACN,aACE;AAAA,EACF,SAAS;AAAA,EACT,QAAQ,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8GhB;;;AC9HO,IAAM,yBAAwC;AAAA,EACnD,MAAM;AAAA,EACN,aAAa;AAAA,EACb,SAAS;AAAA,EACT,QAAQ,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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","names":["join","join","i","j"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/spec/index.ts","../src/spec/sync.ts"],"sourcesContent":["/**\n * `canary spec` subcommand entry points.\n *\n * Subcommands:\n * canary spec validate <file> — parse + lint, exit non-zero on errors\n * canary spec compile <file> — validate, then POST to /workflows/create-with-nodes\n * canary spec pull <id> — reverse: workflow → spec (TODO)\n * canary spec diff <file> <id> — what would compile change? (TODO)\n *\n * @module spec\n */\n\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport process from 'node:process';\nimport { getArgValue, hasFlag, resolveAppUrl } from '../auth.js';\nimport { fetchOrExit } from '../api-fetch.js';\nimport {\n ApiSequenceFileSchema,\n diffWorkflow,\n emitSpec,\n extractPreservedFrontmatter,\n hasAnyChange,\n lintSpec,\n parseSpec,\n summarize,\n workflowToSpec,\n writeNodeIds,\n writeWorkflowId,\n type DiffResult,\n type LiveWorkflow,\n type PullWorkflow,\n type PullOptions,\n type SpecAst,\n type SpecIssue,\n} from '@chatsdet/canary-spec';\nimport { applyDiff } from './sync.js';\n\nexport async function handleSpec(argv: string[], apiUrl: string, token: string): Promise<void> {\n const sub = argv[0];\n switch (sub) {\n case 'validate':\n await handleValidate(argv.slice(1));\n return;\n case 'compile':\n await handleCompile(argv.slice(1), apiUrl, token);\n return;\n case 'pull':\n await handlePull(argv.slice(1), apiUrl, token);\n return;\n case 'diff':\n await handleDiff(argv.slice(1), apiUrl, token);\n return;\n default:\n printSpecHelp();\n process.exit(sub ? 1 : 0);\n }\n}\n\nfunction printSpecHelp(): void {\n console.log(\n [\n 'Usage: canary spec <subcommand> <file>',\n '',\n 'Subcommands:',\n ' validate <file> Parse + lint the spec offline',\n ' compile <file> Validate, then create the workflow',\n ' diff <file> Preview what `compile` would change (no writes)',\n ' pull <workflow-id> Reverse: workflow → round-trippable .canary.md',\n ' pull --all Sync a directory of specs down from all workflows',\n '',\n 'Compile flags:',\n ' --publish Create as published instead of draft',\n ' --run-and-publish After create, trigger a test run; publish if green',\n ' --allow-duplicate Fresh compile: create even when a same-named workflow exists',\n ' --create-with-duplicate-title',\n ' Alias for --allow-duplicate',\n ' --json Machine-readable output',\n '',\n 'Pull flags:',\n ' --output <path|dir> File (single) or directory (--all); default canary/specs/',\n ' --all Sync every workflow into the directory (add new, update matching by workflowId)',\n ' --status <s> With --all, filter workflows by status (draft|published|…)',\n ' --force Overwrite an existing file (single pull)',\n ].join('\\n')\n );\n}\n\nasync function handleValidate(argv: string[]): Promise<void> {\n const file = argv[0];\n if (!file) {\n console.error('Error: canary spec validate <file>');\n process.exit(1);\n }\n const json = hasFlag(argv, '--json');\n\n const result = await loadAndValidate(file);\n if (json) {\n console.log(\n JSON.stringify(\n {\n ok: result.ok,\n issues: result.issues,\n ...(result.ok ? { workflowName: result.ast.frontmatter.name } : {}),\n },\n null,\n 2\n )\n );\n process.exit(result.ok ? 0 : 1);\n }\n printIssues(result.issues, file);\n if (!result.ok) {\n process.exit(1);\n }\n console.log(`✓ ${file} validates`);\n}\n\nasync function handleCompile(argv: string[], apiUrl: string, token: string): Promise<void> {\n const file = argv[0];\n if (!file) {\n console.error('Error: canary spec compile <file>');\n process.exit(1);\n }\n const result = await loadAndValidate(file);\n printIssues(result.issues, file);\n if (!result.ok) {\n process.exit(1);\n }\n\n const ast = result.ast;\n // Append a default end node only on a fresh create; on recompile (spec\n // already bound to a workflow) suppress it so an end-less workflow doesn't\n // gain a phantom node on every round-trip.\n const emitted = emitSpec(ast, { appendDefaultEnd: !ast.frontmatter.workflowId });\n\n // Load any api_sequence sidecar referenced by a node. Multiple nodes pointing\n // at the same path share one sidecar (DB enforces one api_sequence row per\n // workflow); divergent paths produce a clear error before any state changes.\n try {\n emitted.apiSequenceConfig = await loadApiSequenceSidecar(emitted.nodes, file);\n } catch (err) {\n console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n }\n\n // Branch: recompile (frontmatter has `workflowId`) vs fresh create.\n if (emitted.workflowId) {\n await runRecompile({ argv, apiUrl, token, file, emitted });\n return;\n }\n\n await runFreshCompile({ argv, apiUrl, token, file, emitted });\n}\n\n/**\n * Resolve and load the api_sequence sidecar(s) referenced by spec nodes.\n *\n * The DB enforces one api_sequence row per workflow, so a single workflow may\n * carry many `nodeType: 'api_sequence'` nodes but they all point at the same\n * sidecar. We accept multiple identical paths but reject divergent ones with\n * a clear error before any state mutation.\n */\nasync function loadApiSequenceSidecar(\n nodes: ReadonlyArray<{ nodeType: string; configJson: Record<string, unknown> }>,\n specFile: string\n): Promise<unknown> {\n const refs = nodes\n .filter((n) => n.nodeType === 'api_sequence')\n .map((n) => n.configJson['__apiSequenceSidecar'])\n .filter((p): p is string => typeof p === 'string' && p.length > 0);\n if (refs.length === 0) return undefined;\n const unique = Array.from(new Set(refs));\n if (unique.length > 1) {\n throw new Error(\n `Spec has divergent api_sequence sidecar paths: ${unique\n .map((p) => `\"${p}\"`)\n .join(', ')}. The DB stores one config per workflow — pick a single path.`\n );\n }\n const sidecarPath = path.resolve(path.dirname(specFile), unique[0]);\n let raw: string;\n try {\n raw = await fs.readFile(sidecarPath, 'utf-8');\n } catch (err) {\n throw new Error(\n `Could not read api_sequence sidecar at ${sidecarPath}: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n const yaml = await import('yaml');\n let parsed: unknown;\n try {\n parsed = yaml.parse(raw);\n } catch (err) {\n throw new Error(\n `Sidecar YAML at ${sidecarPath} is invalid: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n const validated = ApiSequenceFileSchema.safeParse(parsed);\n if (!validated.success) {\n throw new Error(\n `Sidecar at ${sidecarPath} doesn't match the api_sequence shape: ${validated.error.message}`\n );\n }\n return validated.data;\n}\n\nasync function handleDiff(argv: string[], apiUrl: string, token: string): Promise<void> {\n const file = argv[0];\n if (!file) {\n console.error('Error: canary spec diff <file>');\n process.exit(1);\n }\n const result = await loadAndValidate(file);\n printIssues(result.issues, file);\n if (!result.ok) process.exit(1);\n\n const emitted = emitSpec(result.ast, { appendDefaultEnd: false });\n if (!emitted.workflowId) {\n console.log(\n `ℹ ${file} has no workflowId in frontmatter — \\`canary spec compile\\` would create a new workflow.`\n );\n console.log(` Nodes that would be created: ${emitted.nodes.length}`);\n return;\n }\n\n const liveRes = await fetchOrExit(`${apiUrl}/workflows/${emitted.workflowId}`, {\n method: 'GET',\n headers: { Authorization: `Bearer ${token}` },\n token,\n });\n if (!liveRes.ok) {\n console.error(`Error: failed to fetch workflow ${emitted.workflowId} (${liveRes.status})`);\n process.exit(1);\n }\n const liveBody = (await liveRes.json().catch(() => ({}))) as { workflow?: LiveWorkflow };\n const live = liveBody.workflow;\n if (!live) {\n console.error(`Error: workflow ${emitted.workflowId} not found.`);\n process.exit(1);\n }\n\n let diff: DiffResult;\n try {\n diff = diffWorkflow(emitted, live);\n } catch (err) {\n console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n }\n\n if (hasFlag(argv, '--json')) {\n console.log(JSON.stringify({ ok: true, summary: summarize(diff), diff }, null, 2));\n return;\n }\n\n if (!hasAnyChange(diff)) {\n console.log(`✓ ${file} — in sync with workflow ${emitted.workflowId}`);\n return;\n }\n const s = summarize(diff);\n console.log(`\\nPreview for \"${live.name}\" (${emitted.workflowId}):`);\n console.log(\n ` +${s.created} create · ~${s.updated} update · -${s.deleted} delete · ↻ ${s.moved} move`\n );\n if (diff.nameDrift) console.log(` ✎ rename: \"${diff.nameDrift.from}\" → \"${diff.nameDrift.to}\"`);\n const fieldKeys = Object.keys(diff.workflowFieldChanges).filter((k) => k !== 'name');\n if (fieldKeys.length > 0) console.log(` ✎ workflow fields: ${fieldKeys.join(', ')}`);\n console.log(`\\nRun \\`canary spec compile ${file}\\` to apply.`);\n}\n\nasync function handlePull(argv: string[], apiUrl: string, token: string): Promise<void> {\n if (hasFlag(argv, '--all')) {\n await handlePullAll(argv, apiUrl, token);\n return;\n }\n\n const workflowId = argv.find((a) => !a.startsWith('-'));\n if (!workflowId) {\n console.error('Error: canary spec pull <workflow-id> [--output <path>] [--force]');\n console.error(' canary spec pull --all [--output <dir>] [--status <s>] [--force]');\n process.exit(1);\n }\n const json = hasFlag(argv, '--json');\n const force = hasFlag(argv, '--force');\n\n const live = await fetchWorkflow(apiUrl, token, workflowId);\n if (!live) {\n console.error(`Error: workflow ${workflowId} not found (or no permission).`);\n process.exit(1);\n }\n\n const outPath =\n getArgValue(argv, '--output') ??\n path.join('canary', 'specs', `${slugify(live.name)}.canary.md`);\n\n // Preserve spec-source-only frontmatter from the prior file (requirementRef,\n // scenario skipReason). These have no live equivalent, so a naive overwrite\n // would silently drop the author's notes on re-pull.\n const preserved = await readPreservedFrontmatter(outPath);\n // When the workflow has an api_sequence config, fetch it + write a sidecar\n // file alongside the spec. Pull annotates each api_sequence node in the live\n // workflow with `__apiSequenceSidecar` so `workflowToSpec` can emit the\n // `- apiSequence:` bullet. The sidecar path lives next to the spec under a\n // sibling `sequences/` directory by convention.\n const sidecarPath = await fetchAndWriteApiSequenceSidecar(\n apiUrl,\n token,\n workflowId,\n live,\n outPath\n );\n if (sidecarPath) {\n for (const node of live.nodes) {\n if (node.nodeType === 'api_sequence') {\n node.configJson = {\n ...node.configJson,\n __apiSequenceSidecar: path.relative(path.dirname(outPath), sidecarPath),\n };\n }\n }\n }\n const source = workflowToSpec(live, preserved);\n\n // Validate the round-trip so a malformed pull surfaces immediately.\n const parsed = parseSpec(source);\n const lintIssues = parsed.ok ? lintSpec(parsed.ast) : [];\n const parseIssues = parsed.ok ? [] : parsed.issues;\n const issues = [...parseIssues, ...lintIssues];\n\n if (!force) {\n try {\n await fs.access(outPath);\n console.error(\n `Error: ${outPath} already exists. Use --output <path> or --force to overwrite.`\n );\n process.exit(1);\n } catch {\n // file does not exist — good.\n }\n }\n\n await fs.mkdir(path.dirname(outPath), { recursive: true });\n await fs.writeFile(outPath, source, 'utf-8');\n\n if (json) {\n console.log(JSON.stringify({ ok: true, file: outPath, workflowId, issues }, null, 2));\n return;\n }\n\n console.log(`\\n✓ Pulled \"${live.name}\" → ${outPath}`);\n console.log(` Nodes: ${live.nodes.length}`);\n console.log(` Edit the file, then \\`canary spec compile ${outPath}\\` to push changes back.`);\n if (issues.length > 0) {\n console.log('\\n Notes:');\n printIssues(issues, outPath);\n }\n}\n\n/**\n * `canary spec pull --all` — sync a directory of `.canary.md` files down from\n * the live workflows. Existing files are matched to a workflow by their\n * `workflowId:` frontmatter and rewritten in place; workflows with no matching\n * file get a fresh `<slug>.canary.md`. Files are only written when their\n * content actually changes, so an already-synced repo produces no churn.\n */\nasync function handlePullAll(argv: string[], apiUrl: string, token: string): Promise<void> {\n const json = hasFlag(argv, '--json');\n const dir =\n getArgValue(argv, '--output') ??\n argv.find((a) => !a.startsWith('-')) ??\n path.join('canary', 'specs');\n const status = getArgValue(argv, '--status');\n\n const workflows = await fetchWorkflowList(apiUrl, token, status);\n // Map existing files by their declared workflowId so re-pulls land in place.\n const byWorkflowId = await indexSpecFiles(dir);\n const usedNames = new Set<string>([...byWorkflowId.values()].map((p) => path.basename(p)));\n\n const results: Array<{\n id: string;\n name: string;\n file: string;\n action: 'added' | 'synced' | 'unchanged' | 'failed';\n note?: string;\n }> = [];\n\n for (const wf of workflows) {\n const live = await fetchWorkflow(apiUrl, token, wf.id);\n if (!live) {\n results.push({ id: wf.id, name: wf.name, file: '', action: 'failed', note: 'fetch failed' });\n continue;\n }\n const existing = byWorkflowId.get(wf.id);\n let target: string;\n let isNew = false;\n if (existing) {\n target = existing;\n } else {\n target = path.join(dir, uniqueName(slugify(live.name), wf.id, usedNames));\n usedNames.add(path.basename(target));\n isNew = true;\n }\n\n let prior: string | null = null;\n try {\n prior = await fs.readFile(target, 'utf-8');\n } catch {\n // no prior file\n }\n // Spec-source-only fields (requirementRef, scenario skipReason) have no\n // server-side representation — lift them from the prior file before\n // emitting, so re-pull doesn't drop them.\n const preserved = prior ? extractPreservedFrontmatter(prior) : {};\n const source = workflowToSpec(live, preserved);\n if (prior === source) {\n results.push({ id: wf.id, name: live.name, file: target, action: 'unchanged' });\n continue;\n }\n await fs.mkdir(path.dirname(target), { recursive: true });\n await fs.writeFile(target, source, 'utf-8');\n results.push({ id: wf.id, name: live.name, file: target, action: isNew ? 'added' : 'synced' });\n }\n\n if (json) {\n console.log(JSON.stringify({ ok: true, dir, results }, null, 2));\n return;\n }\n const count = (a: string) => results.filter((r) => r.action === a).length;\n console.log(`\\n✓ Synced ${results.length} workflow(s) → ${dir}`);\n console.log(\n ` +${count('added')} added · ~${count('synced')} synced · =${count('unchanged')} unchanged${count('failed') ? ` · ✗${count('failed')} failed` : ''}`\n );\n for (const r of results) {\n if (r.action === 'unchanged') continue;\n const mark = r.action === 'added' ? '+' : r.action === 'synced' ? '~' : '✗';\n console.log(\n ` ${mark} ${path.relative(process.cwd(), r.file) || r.file}${r.note ? ` (${r.note})` : ''}`\n );\n }\n}\n\n/** Fetch the full live workflow (with nodes + edges) for the diff/pull path. */\n/**\n * Fetch the workflow's api_sequence config (if any) and write it to a sidecar\n * YAML alongside the spec. Returns the absolute path to the sidecar, or null\n * when the workflow doesn't have an api_sequence row.\n *\n * The sidecar filename is derived from the workflow slug and lands in a\n * sibling `sequences/` directory: `canary/specs/sequences/<slug>.yaml`.\n * Re-pull overwrites the file in place.\n */\nasync function fetchAndWriteApiSequenceSidecar(\n apiUrl: string,\n token: string,\n workflowId: string,\n live: PullWorkflow,\n outPath: string\n): Promise<string | null> {\n // Skip when the workflow has no api_sequence nodes.\n if (!live.nodes.some((n) => n.nodeType === 'api_sequence')) return null;\n const res = await fetchOrExit(`${apiUrl}/api-sequences/by-workflow/${workflowId}`, {\n method: 'GET',\n headers: { Authorization: `Bearer ${token}` },\n token,\n });\n if (!res.ok) {\n // Workflow may have api_sequence nodes but no row yet (e.g. legacy state);\n // leave the sidecar off so the round-trip stays graceful.\n return null;\n }\n const body = (await res.json().catch(() => ({}))) as { data?: { config?: unknown } };\n const config = body.data?.config;\n if (!config) return null;\n\n const sequencesDir = path.join(path.dirname(outPath), 'sequences');\n const sidecarSlug = slugify(live.name);\n const sidecarPath = path.join(sequencesDir, `${sidecarSlug}.yaml`);\n await fs.mkdir(sequencesDir, { recursive: true });\n const yaml = await import('yaml');\n await fs.writeFile(sidecarPath, yaml.stringify(config), 'utf-8');\n return sidecarPath;\n}\n\n/**\n * Read spec-source-only frontmatter from the prior file at `target` (if any).\n * Returns an empty `PullOptions` when no prior file exists.\n */\nasync function readPreservedFrontmatter(target: string): Promise<PullOptions> {\n try {\n const prior = await fs.readFile(target, 'utf-8');\n return extractPreservedFrontmatter(prior);\n } catch {\n return {};\n }\n}\n\nasync function fetchWorkflow(\n apiUrl: string,\n token: string,\n workflowId: string\n): Promise<PullWorkflow | null> {\n const res = await fetchOrExit(`${apiUrl}/workflows/${workflowId}`, {\n method: 'GET',\n headers: { Authorization: `Bearer ${token}` },\n token,\n });\n if (!res.ok) return null;\n const body = (await res.json().catch(() => ({}))) as { workflow?: PullWorkflow };\n return body.workflow ?? null;\n}\n\n/** List workflows (paginated), optionally filtered by status. */\nasync function fetchWorkflowList(\n apiUrl: string,\n token: string,\n status: string | undefined\n): Promise<Array<{ id: string; name: string }>> {\n const out: Array<{ id: string; name: string }> = [];\n let page = 1;\n for (;;) {\n const params = new URLSearchParams({ page: String(page), pageSize: '100' });\n if (status) params.set('status', status);\n const res = await fetchOrExit(`${apiUrl}/workflows?${params.toString()}`, {\n method: 'GET',\n headers: { Authorization: `Bearer ${token}` },\n token,\n });\n if (!res.ok) {\n console.error(`Error: failed to list workflows (${res.status})`);\n process.exit(1);\n }\n const body = (await res.json().catch(() => ({}))) as {\n data?: Array<{ id: string; name: string }>;\n pagination?: { page: number; totalPages: number };\n };\n for (const w of body.data ?? []) out.push({ id: w.id, name: w.name });\n const pg = body.pagination;\n if (!pg || pg.page >= pg.totalPages || (body.data ?? []).length === 0) break;\n page += 1;\n }\n return out;\n}\n\n/** Build a map of `workflowId` → spec file path across a directory tree. */\nasync function indexSpecFiles(dir: string): Promise<Map<string, string>> {\n const map = new Map<string, string>();\n let entries: string[];\n try {\n entries = await fs.readdir(dir, { recursive: true });\n } catch {\n return map; // directory doesn't exist yet\n }\n const ID_RE = /^workflowId:\\s*([0-9a-f-]{36})\\s*$/im;\n for (const rel of entries) {\n if (!rel.endsWith('.canary.md')) continue;\n const full = path.join(dir, rel);\n try {\n const src = await fs.readFile(full, 'utf-8');\n const m = ID_RE.exec(src);\n if (m && !map.has(m[1])) map.set(m[1], full);\n } catch {\n // unreadable — skip\n }\n }\n return map;\n}\n\n/** Pick a collision-free filename, disambiguating with a short id when needed. */\nfunction uniqueName(slug: string, workflowId: string, used: Set<string>): string {\n let name = `${slug}.canary.md`;\n if (used.has(name)) name = `${slug}-${workflowId.slice(0, 8)}.canary.md`;\n return name;\n}\n\nasync function runFreshCompile(opts: {\n argv: string[];\n apiUrl: string;\n token: string;\n file: string;\n emitted: ReturnType<typeof emitSpec>;\n}): Promise<void> {\n const { argv, apiUrl, token, file, emitted } = opts;\n\n const duplicateMatches = await fetchFreshCompileDuplicateWorkflows({\n argv,\n apiUrl,\n token,\n workflowName: emitted.name,\n });\n if (duplicateMatches.length > 0) {\n const message = formatDuplicateWorkflowTitleWarning({\n file,\n workflowName: emitted.name,\n matches: duplicateMatches,\n });\n if (hasFlag(argv, '--json')) {\n console.log(\n JSON.stringify(\n {\n ok: false,\n error: 'duplicate_workflow_title',\n message,\n workflows: duplicateMatches,\n },\n null,\n 2\n )\n );\n } else {\n console.error(message);\n }\n process.exit(1);\n }\n\n // `--publish` opts a spec-created flow straight to published (default: draft). Reliability\n // is tracked separately by the workflow's `health` once it starts running in suites.\n const status = hasFlag(argv, '--publish') ? 'published' : (emitted.status ?? 'draft');\n\n const payload: Record<string, unknown> = {\n name: emitted.name,\n description: emitted.description,\n preflight: emitted.preflight,\n cleanup: emitted.cleanup,\n status,\n flowType: emitted.flowType,\n createdVia: 'cli-spec',\n // The route only knows about (nodeType, configJson) — strip our meta.\n nodes: emitted.nodes.map(({ nodeType, configJson }) => ({ nodeType, configJson })),\n ...(emitted.inputsJson.length > 0 ? { inputsJson: emitted.inputsJson } : {}),\n ...(emitted.scenariosJson.length > 0 ? { scenariosJson: emitted.scenariosJson } : {}),\n };\n\n const res = await fetchOrExit(`${apiUrl}/workflows/create-with-nodes`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n token,\n body: JSON.stringify(payload),\n });\n\n const body = (await res.json().catch(() => ({}))) as {\n ok: boolean;\n error?: string;\n warnings?: string[];\n workflow?: { id: string; name: string; status: string };\n nodes?: Array<{ id: string; nodeType: string; orderIndex: number }>;\n };\n\n if (!res.ok || !body.ok || !body.workflow) {\n console.error(`Error: ${body.error ?? `${res.status} ${res.statusText}`}`);\n process.exit(1);\n }\n\n // Write workflowId back to frontmatter + @id comments to each lead-in line.\n // Pair each emitted node with its sourceLine and the corresponding new\n // server-side node UUID (the route returns nodes in orderIndex order).\n const writebackPairs: Array<{ sourceLine: number; nodeId: string }> = [];\n if (body.nodes && body.nodes.length === emitted.nodes.length) {\n for (let i = 0; i < emitted.nodes.length; i++) {\n const sourceLine = emitted.nodes[i].sourceLine;\n if (sourceLine === undefined) continue;\n writebackPairs.push({ sourceLine, nodeId: body.nodes[i].id });\n }\n }\n\n try {\n const source = await fs.readFile(file, 'utf-8');\n const withWorkflowId = writeWorkflowId(source, body.workflow.id);\n // writeWorkflowId may insert a new frontmatter line, shifting all subsequent\n // line numbers by +1. Shift sourceLine accordingly so node @id markers land\n // on the lead-in (not on the blank line before it).\n const lineShift = withWorkflowId.split('\\n').length - source.split('\\n').length;\n const shiftedPairs = writebackPairs.map((p) => ({\n ...p,\n sourceLine: p.sourceLine + lineShift,\n }));\n const next = writeNodeIds(withWorkflowId, shiftedPairs);\n if (next !== source) {\n await fs.writeFile(file, next, 'utf-8');\n }\n } catch (err) {\n console.warn(\n `⚠ Workflow created, but failed to write IDs back to ${file}: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n\n if (hasFlag(argv, '--json')) {\n console.log(JSON.stringify({ ok: true, workflow: body.workflow, writebackPairs }, null, 2));\n return;\n }\n\n const appUrl = resolveAppUrl(apiUrl);\n console.log(`\\n✓ Spec compiled: ${body.workflow.name}`);\n console.log(` ID: ${body.workflow.id}`);\n console.log(` Status: ${body.workflow.status}`);\n console.log(` Nodes: ${body.nodes?.length ?? 0}`);\n console.log(` URL: ${appUrl}/flows/${body.workflow.id}`);\n console.log(` → wrote workflowId + @id markers back to ${file}`);\n if (body.warnings && body.warnings.length > 0) {\n console.log('\\n Warnings:');\n for (const w of body.warnings) console.log(` ⚠ ${w}`);\n }\n\n if (hasFlag(argv, '--run-and-publish')) {\n console.log('\\nℹ --run-and-publish for spec compile is not wired in this MVP. Run:');\n console.log(` canary workflow run ${body.workflow.id} --publish-on-pass`);\n }\n}\n\nexport const DUPLICATE_WORKFLOW_TITLE_ALLOW_FLAGS = [\n '--allow-duplicate',\n '--create-with-duplicate-title',\n] as const;\n\nexport type WorkflowTitleMatch = {\n id: string;\n name: string;\n status?: string;\n flowType?: string;\n};\n\nexport function allowsDuplicateWorkflowTitle(argv: string[]): boolean {\n return DUPLICATE_WORKFLOW_TITLE_ALLOW_FLAGS.some((flag) => hasFlag(argv, flag));\n}\n\nexport function exactWorkflowTitleMatches(\n workflows: ReadonlyArray<WorkflowTitleMatch>,\n workflowName: string\n): WorkflowTitleMatch[] {\n return workflows.filter((workflow) => workflow.name === workflowName);\n}\n\nexport function formatDuplicateWorkflowTitleWarning(opts: {\n file: string;\n workflowName: string;\n matches: ReadonlyArray<WorkflowTitleMatch>;\n}): string {\n const first = opts.matches[0];\n const stampedFile = stampedSpecPath(opts.file);\n const workflowLines = opts.matches\n .map((workflow) => {\n const details = [workflow.status, workflow.flowType].filter(Boolean).join(', ');\n return ` - ${workflow.id}${details ? ` (${details})` : ''}`;\n })\n .join('\\n');\n return [\n `Error: fresh spec compile would create a duplicate workflow named \"${opts.workflowName}\".`,\n '',\n 'Existing workflow(s):',\n workflowLines,\n '',\n 'To update an existing workflow instead, pull a stamped spec, re-apply your edits, then compile it:',\n ` canary spec pull ${first.id} --output ${stampedFile}`,\n ` canary spec compile ${stampedFile}`,\n '',\n 'To intentionally create another workflow with the same title, re-run with:',\n ` canary spec compile ${opts.file} --allow-duplicate`,\n ].join('\\n');\n}\n\nasync function fetchFreshCompileDuplicateWorkflows(opts: {\n argv: string[];\n apiUrl: string;\n token: string;\n workflowName: string;\n}): Promise<WorkflowTitleMatch[]> {\n if (allowsDuplicateWorkflowTitle(opts.argv)) return [];\n const params = new URLSearchParams({\n search: opts.workflowName,\n pageSize: '50',\n });\n const res = await fetchOrExit(`${opts.apiUrl}/workflows?${params.toString()}`, {\n method: 'GET',\n headers: { Authorization: `Bearer ${opts.token}` },\n token: opts.token,\n });\n if (!res.ok) {\n console.error(`Error: failed to check for existing workflow titles (${res.status})`);\n process.exit(1);\n }\n const body = (await res.json().catch(() => ({}))) as {\n data?: WorkflowTitleMatch[];\n };\n return exactWorkflowTitleMatches(body.data ?? [], opts.workflowName);\n}\n\nfunction stampedSpecPath(file: string): string {\n if (file.endsWith('.canary.md')) {\n return `${file.slice(0, -'.canary.md'.length)}.stamped.canary.md`;\n }\n return `${file}.stamped.canary.md`;\n}\n\nasync function runRecompile(opts: {\n argv: string[];\n apiUrl: string;\n token: string;\n file: string;\n emitted: ReturnType<typeof emitSpec>;\n}): Promise<void> {\n const { argv, apiUrl, token, file, emitted } = opts;\n const workflowId = emitted.workflowId!;\n\n // 1. Fetch live workflow. GET /workflows/:id returns the workflow with\n // nodes + edges via getWorkflowWithDetails.\n const liveRes = await fetchOrExit(`${apiUrl}/workflows/${workflowId}`, {\n method: 'GET',\n headers: { Authorization: `Bearer ${token}` },\n token,\n });\n if (!liveRes.ok) {\n console.error(`Error: failed to fetch workflow ${workflowId} (${liveRes.status})`);\n process.exit(1);\n }\n const liveBody = (await liveRes.json().catch(() => ({}))) as {\n workflow?: LiveWorkflow & { snapshotVersion?: number };\n };\n const live = liveBody.workflow;\n if (!live) {\n console.error(`Error: workflow ${workflowId} not found (or no permission).`);\n process.exit(1);\n }\n\n // 2. Compute the diff.\n let diff: DiffResult;\n try {\n diff = diffWorkflow(emitted, live);\n } catch (err) {\n console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n }\n\n if (!hasAnyChange(diff)) {\n console.log(`✓ ${file} — no changes (workflow ${workflowId} already in sync)`);\n return;\n }\n\n // 3. Apply.\n const ctx = { apiUrl, token, workflowId };\n const sync = await applyDiff(ctx, diff, emitted, live);\n\n // 4. Write back any new @id comments.\n if (sync.created.length > 0) {\n try {\n const source = await fs.readFile(file, 'utf-8');\n const next = writeNodeIds(source, sync.created);\n if (next !== source) await fs.writeFile(file, next, 'utf-8');\n } catch (err) {\n console.warn(\n `⚠ Recompile applied, but failed to write new @id markers to ${file}: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n }\n\n // 5. Summary.\n const s = summarize(diff);\n const appUrl = resolveAppUrl(apiUrl);\n console.log(`\\n✓ Recompiled \"${live.name}\"`);\n console.log(\n ` +${s.created} created · ~${s.updated} updated · -${s.deleted} deleted · ↻ ${s.moved} moved`\n );\n console.log(` URL: ${appUrl}/flows/${workflowId}`);\n if (diff.nameDrift) {\n console.log(` ✎ renamed: \"${diff.nameDrift.from}\" → \"${diff.nameDrift.to}\"`);\n }\n if (live.status === 'published') {\n console.log(\n ` ⚠ workflow is published — changes land on draft state; next publish captures them.`\n );\n }\n if (diff.duplicateSpecIds.length > 0) {\n console.log(\n ` ⚠ duplicate @id in spec (treated as fresh creates): ${diff.duplicateSpecIds.join(', ')}`\n );\n }\n if (diff.orphanedSpecIds.length > 0) {\n console.log(\n ` ⚠ stale @id in spec (no matching node — rebound to new): ${diff.orphanedSpecIds.join(', ')}`\n );\n }\n\n if (hasFlag(argv, '--json')) {\n console.log(JSON.stringify({ ok: true, workflowId, summary: s, diff }, null, 2));\n }\n}\n\ntype LoadResult =\n | { ok: true; ast: SpecAst; issues: SpecIssue[] }\n | { ok: false; issues: SpecIssue[] };\n\nasync function loadAndValidate(file: string): Promise<LoadResult> {\n let source: string;\n try {\n source = await fs.readFile(file, 'utf-8');\n } catch (err) {\n return {\n ok: false,\n issues: [\n {\n severity: 'error',\n message: `Could not read ${file}: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n };\n }\n const parsed = parseSpec(source);\n if (!parsed.ok) return { ok: false, issues: parsed.issues };\n const lintIssues = lintSpec(parsed.ast);\n const allIssues = [...lintIssues];\n const hasError = allIssues.some((i) => i.severity === 'error');\n if (hasError) return { ok: false, issues: allIssues };\n return { ok: true, ast: parsed.ast, issues: allIssues };\n}\n\n/** Kebab-case slug for the default pull filename. */\nfunction slugify(name: string): string {\n const slug = name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 60);\n return slug || 'workflow';\n}\n\nfunction printIssues(issues: SpecIssue[], file: string): void {\n if (issues.length === 0) return;\n for (const i of issues) {\n const prefix = i.severity === 'error' ? '✗ error' : '⚠ warn';\n const loc = i.line ? `${file}:${i.line}` : file;\n console.error(`${prefix} ${loc}: ${i.message}`);\n if (i.hint) console.error(` hint: ${i.hint}`);\n }\n}\n\nexport { handleValidate, handleCompile };\n\n// Used by tests.\nexport { emitSpec, lintSpec, parseSpec };\n","/**\n * Apply a DiffResult to a live workflow via the existing REST endpoints\n * (`POST /workflows/:id/nodes`, `PUT /workflows/:id/nodes/:nodeId`,\n * `POST /workflows/:id/nodes/bulk-delete`, edge routes, `PUT /workflows/:id`).\n *\n * Returns the set of `(sourceLine → newNodeId)` pairs the writeback step\n * needs to stamp the spec file with fresh `@id` comments.\n *\n * @module spec/sync\n */\n\nimport { fetchOrExit } from '../api-fetch.js';\nimport {\n reconcileLinearEdges,\n type DiffResult,\n type EmittedWorkflow,\n type LiveEdge,\n type LiveWorkflow,\n type SpecOp,\n type WorkflowFieldChanges,\n} from '@chatsdet/canary-spec';\n\nexport type SyncContext = {\n apiUrl: string;\n token: string;\n workflowId: string;\n};\n\nexport type SyncResult = {\n /** New nodes that were created — caller uses these to write `@id` back to the spec. */\n created: Array<{ sourceLine: number; nodeId: string }>;\n};\n\nexport async function applyDiff(\n ctx: SyncContext,\n diff: DiffResult,\n emitted: EmittedWorkflow,\n live: LiveWorkflow,\n): Promise<SyncResult> {\n // 1. Bulk-delete removed nodes (reconnect: true so adjacent edges heal).\n const deletes = diff.ops.filter((o): o is Extract<SpecOp, { kind: 'delete' }> => o.kind === 'delete');\n if (deletes.length > 0) {\n await postJson(ctx, `/workflows/${ctx.workflowId}/nodes/bulk-delete`, {\n nodeIds: deletes.map((o) => o.nodeId),\n reconnect: true,\n });\n }\n\n // 2. Create new nodes. The route assigns final orderIndex, but we send the\n // desired orderIndex so it lands at the right position. We need final IDs\n // back for both writeback and downstream edge reconciliation.\n const creates = diff.ops.filter((o): o is Extract<SpecOp, { kind: 'create' }> => o.kind === 'create');\n const createdIds = new Map<number, string>(); // specIndex → new node UUID\n const writebackPairs: SyncResult['created'] = [];\n for (const op of creates) {\n const res = await postJson<{ ok?: boolean; node?: { id: string }; error?: string }>(\n ctx,\n `/workflows/${ctx.workflowId}/nodes`,\n {\n nodeType: op.nodeType,\n configJson: op.configJson,\n orderIndex: op.orderIndex,\n },\n );\n const created = res.node;\n if (!created) {\n throw new Error(\n `Failed to create node at orderIndex ${op.orderIndex}: ${res.error ?? JSON.stringify(res)}`,\n );\n }\n createdIds.set(op.specIndex, created.id);\n if (op.sourceLine !== undefined) {\n writebackPairs.push({ sourceLine: op.sourceLine, nodeId: created.id });\n }\n }\n\n // 3. Update existing nodes whose config or orderIndex changed.\n const updates = diff.ops.filter((o): o is Extract<SpecOp, { kind: 'update' }> => o.kind === 'update');\n for (const op of updates) {\n if (!op.changes.configChanged && !op.changes.orderChanged) continue;\n await putJson(ctx, `/workflows/${ctx.workflowId}/nodes/${op.nodeId}`, {\n ...(op.changes.configChanged ? { configJson: op.configJson } : {}),\n ...(op.changes.orderChanged ? { orderIndex: op.orderIndex } : {}),\n });\n }\n\n // 4. Edge reconciliation. Spec authors a single linear default chain; we\n // re-derive the expected edges from final node order and DELETE+POST to\n // converge. Non-default edges (condition then/else) are left alone.\n if (diff.ops.length > 0) {\n const finalNodeIds = computeFinalNodeOrder(diff, live, createdIds);\n const fresh = await getJson<{ workflow?: { edges?: LiveEdge[] } }>(\n ctx,\n `/workflows/${ctx.workflowId}`,\n );\n const liveEdges = fresh.workflow?.edges ?? live.edges ?? [];\n const { ops: edgeOps } = reconcileLinearEdges(finalNodeIds, liveEdges);\n for (const op of edgeOps) {\n if (op.kind === 'delete') {\n await deleteJson(ctx, `/workflows/${ctx.workflowId}/edges/${op.edgeId}`);\n } else {\n await postJson(ctx, `/workflows/${ctx.workflowId}/edges`, {\n sourceNodeId: op.sourceNodeId,\n targetNodeId: op.targetNodeId,\n edgeType: 'default',\n });\n }\n }\n }\n\n // 5. Workflow-level fields. Some fields require name → id resolution against\n // org/property scope (tags, environments, sandboxTemplate); those land in\n // a follow-up PR. Surface a clear error rather than silently dropping the\n // edit, so the author isn't surprised by missing changes.\n rejectUnsupportedFieldEdits(diff.workflowFieldChanges);\n if (Object.keys(diff.workflowFieldChanges).length > 0) {\n const payload = await resolveFieldUpdatePayload(ctx, diff.workflowFieldChanges);\n await putJson(ctx, `/workflows/${ctx.workflowId}`, payload);\n }\n\n // 6. Optional api_sequence DSL upsert. The spec carries the full\n // ApiSequenceConfig in a YAML sidecar referenced from any api_sequence\n // node; here we POST it to `/api-sequences/save`, which upserts the\n // workflow-scoped row keyed by workflowId. Skipping when the sidecar is\n // absent leaves any existing live config untouched (no implicit clear).\n if (emitted.apiSequenceConfig) {\n await postJson(ctx, `/api-sequences/save`, {\n workflowId: ctx.workflowId,\n config: emitted.apiSequenceConfig,\n });\n }\n\n return { created: writebackPairs };\n}\n\n/**\n * Look up a teardown workflow by name in the org. Matches on (name, flowType\n * === 'teardown'); throws when missing so the author gets a clear \"compile the\n * teardown spec first\" message instead of a silent FK clear.\n */\nasync function resolveTeardownWorkflowId(\n ctx: SyncContext,\n specName: string,\n): Promise<string> {\n const res = await getJson<{\n ok?: boolean;\n data?: Array<{ id: string; name: string; flowType: string }>;\n }>(\n ctx,\n `/workflows?search=${encodeURIComponent(specName)}&flowType=teardown&pageSize=50`,\n );\n const match = (res.data ?? []).find(\n (w) => w.name === specName && w.flowType === 'teardown',\n );\n if (!match) {\n throw new Error(\n `Teardown workflow \"${specName}\" not found (flowType=teardown). ` +\n `Compile the teardown spec first so the seed can link to it by name.`,\n );\n }\n return match.id;\n}\n\n/**\n * Map name-form workflow fields to their wire shape. Currently only teardown\n * and seedOutputs need lookup; tags/environments/sandboxTemplate are gated by\n * `rejectUnsupportedFieldEdits` above and never reach here.\n */\nasync function resolveFieldUpdatePayload(\n ctx: SyncContext,\n changes: WorkflowFieldChanges,\n): Promise<Record<string, unknown>> {\n const payload = fieldUpdatePayload(changes);\n // teardownSpec carries the name; the API expects teardownWorkflowId.\n if ('teardownSpec' in payload) {\n const specName = payload.teardownSpec;\n delete payload.teardownSpec;\n if (specName === null) {\n payload.teardownWorkflowId = null;\n } else if (typeof specName === 'string') {\n payload.teardownWorkflowId = await resolveTeardownWorkflowId(ctx, specName);\n }\n }\n // teardownSnapshotVersion: 'latest' → null on the wire (DB convention).\n if ('teardownSnapshotVersion' in payload) {\n if (payload.teardownSnapshotVersion === 'latest') payload.teardownSnapshotVersion = null;\n }\n // seedOutputs: rename to `seedOutputsJson` and resolve propertyName → id.\n // The route's normalizeSeedOutputs accepts entries with propertyId set;\n // we ferry across credentialOutput/attributes verbatim per the diff layer.\n if ('seedOutputs' in payload) {\n const inputRows = payload.seedOutputs as\n | Array<{\n dataKey: string;\n label?: string;\n description?: string;\n propertyName?: string;\n credentialOutput?: unknown;\n attributes?: unknown;\n }>\n | undefined;\n delete payload.seedOutputs;\n if (inputRows) {\n const propIds = await resolvePropertyNameMap(\n ctx,\n Array.from(new Set(inputRows.map((r) => r.propertyName).filter((p): p is string => !!p))),\n );\n payload.seedOutputsJson = inputRows.map((r) => {\n const propertyId = r.propertyName ? propIds.get(r.propertyName) : undefined;\n if (r.propertyName && !propertyId) {\n throw new Error(\n `Declared output \"${r.dataKey}\": property \"${r.propertyName}\" not found in this org.`,\n );\n }\n return {\n dataKey: r.dataKey,\n name: r.dataKey,\n displayLabel: r.label ?? r.dataKey,\n ...(r.description ? { description: r.description } : {}),\n ...(propertyId ? { propertyId } : {}),\n ...(r.credentialOutput !== undefined ? { credentialOutput: r.credentialOutput } : {}),\n ...(r.attributes !== undefined ? { attributes: r.attributes } : {}),\n };\n });\n }\n }\n return payload;\n}\n\n/**\n * Look up property ids by name in a single round trip. Used for seed-output\n * declarations where the spec author writes `property: <name>` and sync needs\n * the propertyId for the wire payload.\n */\nasync function resolvePropertyNameMap(\n ctx: SyncContext,\n names: string[],\n): Promise<Map<string, string>> {\n const out = new Map<string, string>();\n if (names.length === 0) return out;\n const res = await getJson<{\n ok?: boolean;\n data?: Array<{ id: string; name: string }>;\n properties?: Array<{ id: string; name: string }>;\n }>(ctx, `/org/properties`);\n const all = res.data ?? res.properties ?? [];\n for (const want of names) {\n const match = all.find((p) => p.name === want);\n if (match) out.set(want, match.id);\n }\n return out;\n}\n\nexport class UnsupportedFieldEditError extends Error {\n constructor(fields: string[]) {\n super(\n `Editing ${fields.join(', ')} via .canary.md is not supported yet. ` +\n `These fields require name → id resolution that lands in a follow-up. ` +\n `Edit them in the Canary UI for now; the rest of your changes will still apply.`,\n );\n this.name = 'UnsupportedFieldEditError';\n }\n}\n\nexport function rejectUnsupportedFieldEdits(changes: WorkflowFieldChanges): void {\n const unsupported = (['tags', 'environmentConstraints', 'sandboxTemplate'] as const).filter(\n (k) => changes[k] !== undefined,\n );\n if (unsupported.length > 0) throw new UnsupportedFieldEditError(unsupported);\n}\n\n/**\n * Build the final ordered list of node UUIDs after applying the diff —\n * positions correspond to the spec's order, IDs resolved from either the live\n * workflow (for UPDATE) or the new-node map (for CREATE).\n */\nfunction computeFinalNodeOrder(\n diff: DiffResult,\n live: LiveWorkflow,\n createdIds: Map<number, string>,\n): string[] {\n const liveById = new Map(live.nodes.map((n) => [n.id, n] as const));\n const total = diff.ops.filter((o) => o.kind !== 'delete').length;\n const result: string[] = Array.from({ length: total });\n for (const op of diff.ops) {\n if (op.kind === 'create') {\n const id = createdIds.get(op.specIndex);\n if (!id) throw new Error(`Missing created node id for specIndex ${op.specIndex}`);\n result[op.specIndex] = id;\n } else if (op.kind === 'update') {\n if (!liveById.has(op.nodeId)) {\n throw new Error(`UPDATE references nodeId ${op.nodeId} not in live workflow`);\n }\n result[op.specIndex] = op.nodeId;\n }\n }\n return result;\n}\n\nfunction fieldUpdatePayload(changes: WorkflowFieldChanges): Record<string, unknown> {\n // PUT /workflows/:id accepts each field independently; pass through only\n // what changed. Scenario `skipReason` is spec-source-only — the API's\n // `normalizeScenarios` strips it, but stripping here keeps the wire payload\n // honest and the round-trip test deterministic.\n const payload: Record<string, unknown> = {};\n for (const k of Object.keys(changes) as (keyof WorkflowFieldChanges)[]) {\n if (k === 'scenariosJson' && Array.isArray(changes.scenariosJson)) {\n payload.scenariosJson = (changes.scenariosJson as Array<Record<string, unknown>>).map((s) => {\n const { skipReason: _skip, ...rest } = s;\n return rest;\n });\n } else {\n payload[k] = changes[k];\n }\n }\n return payload;\n}\n\nasync function postJson<T = unknown>(\n ctx: SyncContext,\n path: string,\n body: unknown,\n): Promise<T> {\n const res = await fetchOrExit(`${ctx.apiUrl}${path}`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${ctx.token}`,\n 'Content-Type': 'application/json',\n },\n token: ctx.token,\n body: JSON.stringify(body),\n });\n return (await res.json()) as T;\n}\n\nasync function putJson<T = unknown>(\n ctx: SyncContext,\n path: string,\n body: unknown,\n): Promise<T> {\n const res = await fetchOrExit(`${ctx.apiUrl}${path}`, {\n method: 'PUT',\n headers: {\n Authorization: `Bearer ${ctx.token}`,\n 'Content-Type': 'application/json',\n },\n token: ctx.token,\n body: JSON.stringify(body),\n });\n return (await res.json()) as T;\n}\n\nasync function getJson<T = unknown>(ctx: SyncContext, path: string): Promise<T> {\n const res = await fetchOrExit(`${ctx.apiUrl}${path}`, {\n method: 'GET',\n headers: { Authorization: `Bearer ${ctx.token}` },\n token: ctx.token,\n });\n return (await res.json()) as T;\n}\n\nasync function deleteJson<T = unknown>(ctx: SyncContext, path: string): Promise<T> {\n const res = await fetchOrExit(`${ctx.apiUrl}${path}`, {\n method: 'DELETE',\n headers: { Authorization: `Bearer ${ctx.token}` },\n token: ctx.token,\n });\n return (await res.json()) as T;\n}\n"],"mappings":"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","names":["path"]}