@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.
- package/dist/{api-catalog-GFGCLVS4.js → api-catalog-7HLEN7C2.js} +5 -4
- package/dist/{api-catalog-GFGCLVS4.js.map → api-catalog-7HLEN7C2.js.map} +1 -1
- package/dist/{attachment-SJC7D7FY.js → attachment-OVNQD6Z2.js} +5 -4
- package/dist/{attachment-SJC7D7FY.js.map → attachment-OVNQD6Z2.js.map} +1 -1
- package/dist/{auth-EN6NQCJY.js → auth-VUHL74FH.js} +3 -2
- package/dist/{chunk-IMPZAX6L.js → chunk-5GD47G2D.js} +37 -1
- package/dist/{chunk-IMPZAX6L.js.map → chunk-5GD47G2D.js.map} +1 -1
- package/dist/{chunk-WOHL4FTB.js → chunk-CQE46HSM.js} +5 -5
- package/dist/chunk-CQE46HSM.js.map +1 -0
- package/dist/{chunk-52IO7ACJ.js → chunk-CRHPDPRS.js} +39 -3
- package/dist/{chunk-52IO7ACJ.js.map → chunk-CRHPDPRS.js.map} +1 -1
- package/dist/{chunk-XQXVG7RA.js → chunk-EEN2TZUQ.js} +3 -3
- package/dist/{chunk-I4QLI3JO.js → chunk-EJ4U242G.js} +2 -2
- package/dist/{chunk-CLAWT7BJ.js → chunk-ENDHMYEJ.js} +3 -3
- package/dist/chunk-G4UYGRZR.js +2 -0
- package/dist/chunk-JZMJUPZP.js +92 -0
- package/dist/chunk-JZMJUPZP.js.map +1 -0
- package/dist/{chunk-GKY2KXO5.js → chunk-NL7CVG56.js} +105 -35
- package/dist/chunk-NL7CVG56.js.map +1 -0
- package/dist/{chunk-A4OFTUVV.js → chunk-PZ4LCN6K.js} +7 -4
- package/dist/chunk-PZ4LCN6K.js.map +1 -0
- package/dist/{chunk-SJJW77WN.js → chunk-QGI4UCJX.js} +69 -5
- package/dist/chunk-QGI4UCJX.js.map +1 -0
- package/dist/{chunk-4PNOV7OV.js → chunk-QQVWGWPI.js} +1023 -150
- package/dist/chunk-QQVWGWPI.js.map +1 -0
- package/dist/{chunk-N7HFALX3.js → chunk-VGTKUOYS.js} +2 -2
- package/dist/{chunk-B2VISN2V.js → chunk-YN763NWQ.js} +230 -66
- package/dist/chunk-YN763NWQ.js.map +1 -0
- package/dist/{chunk-QFDOWABX.js → chunk-Z5LUTUDN.js} +6 -3
- package/dist/chunk-Z5LUTUDN.js.map +1 -0
- package/dist/{chunk-Q4BO4Z5V.js → chunk-ZTOE3ND3.js} +4 -4
- package/dist/{credential-COLTBV6D.js → credential-LO2SEEHU.js} +24 -20
- package/dist/credential-LO2SEEHU.js.map +1 -0
- package/dist/{credential-template-EEPYFG7R.js → credential-template-ZN7W7734.js} +30 -28
- package/dist/credential-template-ZN7W7734.js.map +1 -0
- package/dist/{debug-workflow-KOVH5GOP.js → debug-workflow-R3RYYAXK.js} +5 -4
- package/dist/{debug-workflow-KOVH5GOP.js.map → debug-workflow-R3RYYAXK.js.map} +1 -1
- package/dist/{docs-3SSORHB5.js → docs-DIAFBDXQ.js} +5 -4
- package/dist/{docs-3SSORHB5.js.map → docs-DIAFBDXQ.js.map} +1 -1
- package/dist/{doctor-OTSIWV6G.js → doctor-NZPUSCRE.js} +9 -5
- package/dist/doctor-NZPUSCRE.js.map +1 -0
- package/dist/environment-RQVU4P5M.js +221 -0
- package/dist/environment-RQVU4P5M.js.map +1 -0
- package/dist/{feature-flag-T2LRZME3.js → feature-flag-XG26RURR.js} +72 -10
- package/dist/feature-flag-XG26RURR.js.map +1 -0
- package/dist/index.js +57 -48
- package/dist/index.js.map +1 -1
- package/dist/{init-TGWRECT2.js → init-EDNLKGPT.js} +5 -4
- package/dist/{init-TGWRECT2.js.map → init-EDNLKGPT.js.map} +1 -1
- package/dist/{install-DXSNWZKO.js → install-FYZ3HNZK.js} +7 -7
- package/dist/{issues-XIZOPX6T.js → issues-NNRVTJ4L.js} +5 -4
- package/dist/{issues-XIZOPX6T.js.map → issues-NNRVTJ4L.js.map} +1 -1
- package/dist/{knobs-NBINZIUB.js → knobs-A2BOBH22.js} +5 -4
- package/dist/{knobs-NBINZIUB.js.map → knobs-A2BOBH22.js.map} +1 -1
- package/dist/{list-3TDHK637.js → list-F33VZ4BJ.js} +5 -4
- package/dist/{list-3TDHK637.js.map → list-F33VZ4BJ.js.map} +1 -1
- package/dist/{local-HICVD6PU.js → local-DQDTM3XA.js} +7 -6
- package/dist/{local-HICVD6PU.js.map → local-DQDTM3XA.js.map} +1 -1
- package/dist/{login-UOIUXVUK.js → login-BUMVIB36.js} +5 -4
- package/dist/{login-UOIUXVUK.js.map → login-BUMVIB36.js.map} +1 -1
- package/dist/{mcp-54IJEOUA.js → mcp-B5Y5CDMQ.js} +23 -24
- package/dist/mcp-B5Y5CDMQ.js.map +1 -0
- package/dist/{property-UXLOLPTD.js → property-NJ4SRXQG.js} +76 -4
- package/dist/property-NJ4SRXQG.js.map +1 -0
- package/dist/{psql-BYUUNLGG.js → psql-VBZFCMNR.js} +3 -2
- package/dist/{psql-BYUUNLGG.js.map → psql-VBZFCMNR.js.map} +1 -1
- package/dist/{record-4TR2UIIS.js → record-WAO36O7G.js} +16 -17
- package/dist/record-WAO36O7G.js.map +1 -0
- package/dist/{redis-35VBUYW7.js → redis-DHZXHL4S.js} +3 -2
- package/dist/{redis-35VBUYW7.js.map → redis-DHZXHL4S.js.map} +1 -1
- package/dist/{release-OOLTUBUT.js → release-STJKUKTE.js} +5 -4
- package/dist/{release-OOLTUBUT.js.map → release-STJKUKTE.js.map} +1 -1
- package/dist/{sandbox-XGSUU6G7.js → sandbox-DWSGLY7B.js} +9 -8
- package/dist/{sandbox-XGSUU6G7.js.map → sandbox-DWSGLY7B.js.map} +1 -1
- package/dist/{scenarios-GWLJEGHA.js → scenarios-HCIDY725.js} +5 -4
- package/dist/{scenarios-GWLJEGHA.js.map → scenarios-HCIDY725.js.map} +1 -1
- package/dist/{session-MYFNZPRP.js → session-Y5RHU54D.js} +76 -8
- package/dist/session-Y5RHU54D.js.map +1 -0
- package/dist/{skill-KM34LDMO.js → skill-23WAHUOB.js} +217 -67
- package/dist/skill-23WAHUOB.js.map +1 -0
- package/dist/skills/debug-run/SKILL.md +33 -22
- package/dist/skills/debug-run/history/1.2.0.md +262 -0
- package/dist/skills/debug-run/history/1.3.0.md +273 -0
- package/dist/skills/debug-run/version.json +1 -1
- package/dist/skills/optimize-cache/SKILL.md +206 -0
- package/dist/{spec-RA3CFEKG.js → spec-LM5XHOQ5.js} +54 -25
- package/dist/spec-LM5XHOQ5.js.map +1 -0
- package/dist/{src-LB4HBTJQ.js → src-22RI5VP2.js} +19 -11
- package/dist/src-22RI5VP2.js.map +1 -0
- package/dist/src-BGDRMOKC.js +5748 -0
- package/dist/src-BGDRMOKC.js.map +1 -0
- package/dist/{src-N3TPKGTY.js → src-NCJPUAE6.js} +3 -2
- package/dist/{src-UVDX5UP6.js → src-Y3K7UJEF.js} +4 -2
- package/dist/{start-PSEQSQA3.js → start-SWY55CZX.js} +5 -4
- package/dist/{start-PSEQSQA3.js.map → start-SWY55CZX.js.map} +1 -1
- package/dist/{tag-YY6YAABQ.js → tag-YSU2XXPI.js} +6 -5
- package/dist/{task-IQC3YR5K.js → task-NLBKLD7D.js} +5 -4
- package/dist/{task-IQC3YR5K.js.map → task-NLBKLD7D.js.map} +1 -1
- package/dist/{test-suite-DZNJFXDW.js → test-suite-QCPWZ2LS.js} +9 -6
- package/dist/{test-suite-DZNJFXDW.js.map → test-suite-QCPWZ2LS.js.map} +1 -1
- package/dist/{trace-HXIW7U2D.js → trace-A3ZY6K26.js} +6 -4
- package/dist/{triage-ORG45R5W.js → triage-TT4BMDBU.js} +6 -5
- package/dist/triage-TT4BMDBU.js.map +1 -0
- package/dist/{upgrade-L2VYMP2V.js → upgrade-6ZUFETKX.js} +3 -3
- package/dist/{workflow-XYFH5LTP.js → workflow-2CX3ZNOQ.js} +241 -237
- package/dist/workflow-2CX3ZNOQ.js.map +1 -0
- package/dist/{workflow-cache-NQJQFWMV.js → workflow-cache-GXNE6SU2.js} +140 -7
- package/dist/workflow-cache-GXNE6SU2.js.map +1 -0
- package/dist/{workflow-debug-BGD4UIXU.js → workflow-debug-PRX3DWGD.js} +8 -7
- package/dist/{workflow-debug-BGD4UIXU.js.map → workflow-debug-PRX3DWGD.js.map} +1 -1
- package/dist/{workflow-proposals-MTUDX46O.js → workflow-proposals-BW327WGL.js} +5 -4
- package/dist/{workflow-proposals-MTUDX46O.js.map → workflow-proposals-BW327WGL.js.map} +1 -1
- package/package.json +2 -1
- package/dist/chunk-4PNOV7OV.js.map +0 -1
- package/dist/chunk-A4OFTUVV.js.map +0 -1
- package/dist/chunk-B2VISN2V.js.map +0 -1
- package/dist/chunk-GKY2KXO5.js.map +0 -1
- package/dist/chunk-JICSYFRJ.js +0 -46
- package/dist/chunk-JICSYFRJ.js.map +0 -1
- package/dist/chunk-QFDOWABX.js.map +0 -1
- package/dist/chunk-SJJW77WN.js.map +0 -1
- package/dist/chunk-WOHL4FTB.js.map +0 -1
- package/dist/credential-COLTBV6D.js.map +0 -1
- package/dist/credential-template-EEPYFG7R.js.map +0 -1
- package/dist/doctor-OTSIWV6G.js.map +0 -1
- package/dist/feature-flag-T2LRZME3.js.map +0 -1
- package/dist/mcp-54IJEOUA.js.map +0 -1
- package/dist/property-UXLOLPTD.js.map +0 -1
- package/dist/record-4TR2UIIS.js.map +0 -1
- package/dist/session-MYFNZPRP.js.map +0 -1
- package/dist/skill-KM34LDMO.js.map +0 -1
- package/dist/spec-RA3CFEKG.js.map +0 -1
- package/dist/src-LB4HBTJQ.js.map +0 -1
- package/dist/workflow-XYFH5LTP.js.map +0 -1
- package/dist/workflow-cache-NQJQFWMV.js.map +0 -1
- /package/dist/{auth-EN6NQCJY.js.map → auth-VUHL74FH.js.map} +0 -0
- /package/dist/{chunk-XQXVG7RA.js.map → chunk-EEN2TZUQ.js.map} +0 -0
- /package/dist/{chunk-I4QLI3JO.js.map → chunk-EJ4U242G.js.map} +0 -0
- /package/dist/{chunk-CLAWT7BJ.js.map → chunk-ENDHMYEJ.js.map} +0 -0
- /package/dist/{src-N3TPKGTY.js.map → chunk-G4UYGRZR.js.map} +0 -0
- /package/dist/{chunk-N7HFALX3.js.map → chunk-VGTKUOYS.js.map} +0 -0
- /package/dist/{chunk-Q4BO4Z5V.js.map → chunk-ZTOE3ND3.js.map} +0 -0
- /package/dist/{install-DXSNWZKO.js.map → install-FYZ3HNZK.js.map} +0 -0
- /package/dist/{src-UVDX5UP6.js.map → src-NCJPUAE6.js.map} +0 -0
- /package/dist/{tag-YY6YAABQ.js.map → src-Y3K7UJEF.js.map} +0 -0
- /package/dist/{trace-HXIW7U2D.js.map → tag-YSU2XXPI.js.map} +0 -0
- /package/dist/{triage-ORG45R5W.js.map → trace-A3ZY6K26.js.map} +0 -0
- /package/dist/{upgrade-L2VYMP2V.js.map → upgrade-6ZUFETKX.js.map} +0 -0
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
brandSkillContentFor,
|
|
4
|
+
command
|
|
5
|
+
} from "./chunk-JZMJUPZP.js";
|
|
2
6
|
import "./chunk-VKVL7WBN.js";
|
|
3
7
|
|
|
4
8
|
// src/skill.ts
|
|
5
|
-
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
|
9
|
+
import { existsSync, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
6
10
|
import { homedir } from "os";
|
|
7
11
|
import { join as join2 } from "path";
|
|
8
12
|
|
|
@@ -448,7 +452,7 @@ After exploring an app via \`canary session\`, you can persist what you discover
|
|
|
448
452
|
- Node types: \`login\` (authentication), \`navigate\` (go to URL), \`action\` (interact with elements), \`assertion\` (verify standalone page state), \`end\` (terminal node)
|
|
449
453
|
- Every action node **must** have a \`customActionTitle\` \u2014 a short (3-8 word) human-readable title describing the step
|
|
450
454
|
- **Prefer inline assertions** on action nodes over separate assertion nodes. Add \`inlineAssertions\` to verify outcomes right after the action completes. Use separate \`assertion\` nodes only for standalone page-level verifications (e.g., initial page state before any action)
|
|
451
|
-
- Inline assertion format: \`{ "condition": "...", "severity": "fail" }
|
|
455
|
+
- Inline assertion format: \`{ "condition": "...", "severity": "fatal" | "fail" | "warn" }\` (fatal = halt the test now, fail = mark run failed but continue, warn = record only). **Do not author an \`id\` field** \u2014 the server stamps a UUID on persist. Hand-rolled UUIDs (especially placeholders like \`a1b2c3d4-...\`) are rejected and replaced.
|
|
452
456
|
|
|
453
457
|
### Playbook step authoring guidance
|
|
454
458
|
|
|
@@ -1103,22 +1107,22 @@ canary upgrade # Upgrade to latest version
|
|
|
1103
1107
|
// src/skill-templates/test-cases.ts
|
|
1104
1108
|
var testCasesTemplate = {
|
|
1105
1109
|
name: "test-cases",
|
|
1106
|
-
description: "Author high-quality test cases as `.
|
|
1107
|
-
version: "2.
|
|
1110
|
+
description: "Author high-quality test cases as `.cofactor.md` specs that compile straight into Cofactor 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.",
|
|
1111
|
+
version: "2.3.0",
|
|
1108
1112
|
render: () => `---
|
|
1109
1113
|
name: test-cases
|
|
1110
|
-
version: 2.
|
|
1111
|
-
description: Author high-quality test cases as \`.
|
|
1114
|
+
version: 2.3.0
|
|
1115
|
+
description: Author high-quality test cases as \`.cofactor.md\` specs that compile straight into Cofactor workflows. Invoke when the user asks to write test cases, draft a test plan, plan QA scenarios, decide what to test, or convert a PRD/ticket/design into tests.
|
|
1112
1116
|
allowed-tools: Read, Grep, Glob, Bash, Write
|
|
1113
1117
|
---
|
|
1114
1118
|
|
|
1115
|
-
# Test Case Authoring (
|
|
1119
|
+
# Test Case Authoring (Cofactor)
|
|
1116
1120
|
|
|
1117
|
-
Use this skill when the user asks for test cases, a test plan, QA scenarios, "what should we test for X", or wants to turn a PRD / ticket / design into tests. The output is one or more **\`.
|
|
1121
|
+
Use this skill when the user asks for test cases, a test plan, QA scenarios, "what should we test for X", or wants to turn a PRD / ticket / design into tests. The output is one or more **\`.cofactor.md\` spec files** \u2014 each file is one workflow, one journey, one test case. \`cofactor spec compile <file>\` turns them into runnable Cofactor workflows; \`cofactor spec validate <file>\` enforces the structural rules below. (Legacy \`.canary.md\` files still validate and compile; new specs default to \`.cofactor.md\`.)
|
|
1118
1122
|
|
|
1119
1123
|
The skill has three layers:
|
|
1120
1124
|
1. **Industry rubric** \u2014 what makes a test case good (ISTQB-aligned).
|
|
1121
|
-
2. **Spec mapping** \u2014 how each rubric piece becomes a section of \`.
|
|
1125
|
+
2. **Spec mapping** \u2014 how each rubric piece becomes a section of \`.cofactor.md\`.
|
|
1122
1126
|
3. **Domain Context** \u2014 customer-specific sources of truth, filled in once.
|
|
1123
1127
|
|
|
1124
1128
|
Read the Domain Context block at the bottom **first**. If it is unfilled, follow the discovery instructions there before authoring any cases.
|
|
@@ -1134,7 +1138,7 @@ A case covers a single coherent journey \u2014 one user objective, possibly with
|
|
|
1134
1138
|
|
|
1135
1139
|
- Good (coherent journey, multiple checks): "Onboard an org: create org \u2192 invite a user \u2192 claim invite \u2192 land on dashboard." Each step verifies its own outcome inline.
|
|
1136
1140
|
- Good (single short journey): "Submit a ticket and confirm it appears in the queue."
|
|
1137
|
-
- Bad (unrelated jobs): "Submit a ticket, edit a user, generate a report." \u2014 three journeys, three separate \`.
|
|
1141
|
+
- Bad (unrelated jobs): "Submit a ticket, edit a user, generate a report." \u2014 three journeys, three separate \`.cofactor.md\` files.
|
|
1138
1142
|
|
|
1139
1143
|
**Rule of thumb:** if isolating a check (e.g. "tagging works") would force you to duplicate most of another case's setup, fold it in as an inline assertion on the larger journey instead. Splitting only pays off when the setup is genuinely different or the failure modes are independent.
|
|
1140
1144
|
|
|
@@ -1143,7 +1147,7 @@ Every assertion must be something a stranger could verify without judgement.
|
|
|
1143
1147
|
- Good: "URL ends with \`/dashboard\`." / "A row with name \`X\` appears in the table."
|
|
1144
1148
|
- Bad: "The page looks correct." / "It works."
|
|
1145
1149
|
|
|
1146
|
-
Lint enforces this as **R1**: a spec with zero \`fail
|
|
1150
|
+
Lint enforces this as **R1**: a spec with zero run-failing (\`fail\`/\`fatal\`) assertions warns.
|
|
1147
1151
|
|
|
1148
1152
|
### 3. Independent and self-sufficient
|
|
1149
1153
|
A case must not depend on another case running first or on long-lived shared state. Provision data inline (\`## Inputs\` + \`## Scenarios\`, or a \`**Seed**\` node) and clean up in \`## Cleanup\`. Use macros (\\$EMAIL, \\$UUID) so reruns don't collide.
|
|
@@ -1187,52 +1191,52 @@ Aim for the smallest set that proves the feature works. Five sharp cases beat tw
|
|
|
1187
1191
|
|
|
1188
1192
|
---
|
|
1189
1193
|
|
|
1190
|
-
## 3. Authoring \`.
|
|
1194
|
+
## 3. Authoring \`.cofactor.md\`
|
|
1191
1195
|
|
|
1192
|
-
\`.
|
|
1196
|
+
\`.cofactor.md\` is one workflow per file. Frontmatter sets identity; H2 sections carry prose and structured config; \`## Journey\` is the linear test itself. The compiler validates structure; lint enforces the rubric semantics above.
|
|
1193
1197
|
|
|
1194
1198
|
Run these for the definitive grammar \u2014 do **not** restate them here:
|
|
1195
1199
|
|
|
1196
1200
|
\`\`\`bash
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
+
cofactor spec --help # validate / compile / diff commands
|
|
1202
|
+
cofactor spec validate <file> # parse + lint offline; enforce the rules in \xA71
|
|
1203
|
+
cofactor spec compile <file> # create or update the workflow (prints the live URL)
|
|
1204
|
+
cofactor workflow guide # node-shape and macro reference
|
|
1201
1205
|
\`\`\`
|
|
1202
1206
|
|
|
1203
|
-
After \`
|
|
1207
|
+
After \`cofactor spec compile\`, echo the printed \`URL:\` line back to the user verbatim \u2014 it points at the workflow you just created (\`https://app.trycanary.ai/flows/<id>\` for prod, \`https://app.dev.trycanary.ai/flows/<id>\` for dev). Don't construct it yourself.
|
|
1204
1208
|
|
|
1205
1209
|
Mapping at a glance:
|
|
1206
1210
|
|
|
1207
|
-
| Rubric piece | Where it lives in \`.
|
|
1211
|
+
| Rubric piece | Where it lives in \`.cofactor.md\` |
|
|
1208
1212
|
|-----------------------------------------|---------------------------------------------------------------------------|
|
|
1209
1213
|
| Case title | \`frontmatter.name\` |
|
|
1210
1214
|
| Requirement / ticket ref | \`frontmatter.requirementRef\` |
|
|
1211
1215
|
| Priority (rule 5) | \`tags: [p0]\` (or p1/p2) in frontmatter |
|
|
1212
|
-
| Auth role precondition | \`actor: <X>\` + \`authenticated: true\` in frontmatter. \`<X>\` is a credential **template** name by default (e.g. \`actor: Admin\` or the explicit \`actor: template Admin\`), or a specific raw credential via \`actor: credential <name>\` when the case genuinely needs one named account. Templates resolve at run/suite level; raw credentials bind a single fixed account. List templates with \`
|
|
1216
|
+
| 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 \`cofactor credential-template list --property "<property>"\`; list credentials with \`cofactor credential list\`. |
|
|
1213
1217
|
| Feature flag / seeded data preconditions| Bulleted prose in \`## Preflight\` ("Feature flag X enabled.", "Seed workflow Y published.") |
|
|
1214
1218
|
| One-paragraph user-visible promise | \`## Spirit\` (blockquote) |
|
|
1215
1219
|
| Workflow-level declared inputs | \`## Inputs\` (e.g. \`- foo: \\$EMAIL\`) \u2014 parses to \`workflows.inputs_json\`, surfaces in Workflow Settings \u2192 Inputs |
|
|
1216
1220
|
| Per-step bound values | \`- field X: Y\` under an Action \u2014 populates *that* step's Inputs panel with \`Y\` as the default (compiler mirrors fields \u2192 \`dataRequirements\` + \`inputValues\` on the node). Prefer \`## Inputs\` for values shared across actions. |
|
|
1217
1221
|
| Variants (free / enterprise, etc.) | \`## Scenarios\` with per-scenario \`values:\` / \`credentials:\` overrides |
|
|
1218
1222
|
| Steps (human intent) | \`## Journey\` \u2014 \`**Navigate to** \u2026\`, \`**Action \u2014 \u2026**\`, \`**Seed** \u2026\` |
|
|
1219
|
-
| Inline expected results | \`- assert (fail \\| warn): <condition>\` under the action that produced it |
|
|
1223
|
+
| Inline expected results | \`- assert (fatal \\| fail \\| warn): <condition>\` under the action that produced it. \`fatal\` ends the test now (gating preconditions), \`fail\` marks the run failed but keeps going (feature claims), \`warn\` records without failing (cosmetics) |
|
|
1220
1224
|
| Standalone assertion | \`**Assert** <condition>\` node (prefer inline when possible) |
|
|
1221
1225
|
| Role change mid-journey | \`**Switch role to** <X>\` \u2014 mirrors \`actor:\`. \`<X>\` is a credential template name by default (\`**Switch role to** Admin\`), or \`credential <name>\` to bind a specific raw credential (\`**Switch role to** credential admin-bob\`). Compiles to a login node with \`performLogin: true\`. Repeat as many times as needed in one journey. |
|
|
1222
1226
|
| Cross-action data flow | \`- capture: <name>\` on the upstream action, \`\\$var.<name>\` downstream |
|
|
1223
1227
|
| Cleanup | Bulleted prose in \`## Cleanup\` |
|
|
1224
1228
|
|
|
1225
|
-
When a journey shares setup and naturally flows end-to-end, emit **one** \`.
|
|
1229
|
+
When a journey shares setup and naturally flows end-to-end, emit **one** \`.cofactor.md\` with multiple action nodes and inline assertions. When journeys are unrelated, emit **one file per journey**; if one journey depends on data created by another, link them with \`**Seed** <spec-name>\` or \`**Setup** <spec-name>\` at the top of the dependent file.
|
|
1226
1230
|
|
|
1227
1231
|
### Inputs defaults \u2014 the decision rule
|
|
1228
1232
|
|
|
1229
1233
|
When you need a value (workflow-level \`## Inputs\` or step-level \`- field\`), pick where it comes from in this order:
|
|
1230
1234
|
|
|
1231
1235
|
1. **Names existing data in the target environment** \u2014 a property name, a credential, a seeded record, an existing org/user/team. **Stop and ask the user for the value before writing the spec.** Don't hand-pick. The right sources of truth:
|
|
1232
|
-
- \`
|
|
1233
|
-
- \`
|
|
1234
|
-
- \`
|
|
1235
|
-
- \`
|
|
1236
|
+
- \`cofactor property list\` \u2014 properties (target apps)
|
|
1237
|
+
- \`cofactor credential-template list --property "<property>"\` \u2014 credential templates (these are what \`actor:\` and \`- credential \u2026\` reference)
|
|
1238
|
+
- \`cofactor workflow list --search <term>\` \u2014 existing workflows to seed/link to
|
|
1239
|
+
- \`cofactor feature-flag list --env <env>\` \u2014 flag names for Preflight prose
|
|
1236
1240
|
2. **Synthesizable** \u2014 a new email, a UUID, a throwaway string, an ISO date. Pick a macro (\`\\$EMAIL\`, \`\\$UUID\`, \`\\$ISO_DATE\`) and move on without asking. These render as pills in the Inputs panel and resolve at run time.
|
|
1237
1241
|
3. **Prefer credential templates** for any auth/role-scoped input. \`actor: Admin\` (frontmatter) and \`- credential admin: Admin\` (inputs) are cleaner than passing \`username:\` + \`password:\` and work across environments. Reach for the raw-credential form (\`actor: credential <name>\`, \`**Switch role to** credential <name>\`, \`- credential admin: <name>\`) only when the case genuinely requires a specific fixed account.
|
|
1238
1242
|
|
|
@@ -1240,13 +1244,13 @@ When you need a value (workflow-level \`## Inputs\` or step-level \`- field\`),
|
|
|
1240
1244
|
|
|
1241
1245
|
## 4. Output format
|
|
1242
1246
|
|
|
1243
|
-
For each case, write a \`.
|
|
1247
|
+
For each case, write a \`.cofactor.md\` file alongside a one-row summary line. Default location: \`cofactor/specs/<slug>.cofactor.md\` (confirm with the user if their repo uses a different layout \u2014 check Domain Context below).
|
|
1244
1248
|
|
|
1245
1249
|
### Summary table
|
|
1246
1250
|
|
|
1247
1251
|
| file | name | priority | requirementRef |
|
|
1248
1252
|
|-----------------------------------|--------------------------------------------|----------|------------------|
|
|
1249
|
-
|
|
|
1253
|
+
| cofactor/specs/submit-ticket.cofactor.md | Submit a ticket and confirm it appears in the queue | p0 | <ticket/PRD ref> |
|
|
1250
1254
|
|
|
1251
1255
|
### Per-case file
|
|
1252
1256
|
|
|
@@ -1281,8 +1285,9 @@ requirementRef: <ticket id, PRD path, design link>
|
|
|
1281
1285
|
> <one-sentence context for the recorder/agent>
|
|
1282
1286
|
- field <name> [(type)]: <value or \\$input>
|
|
1283
1287
|
- capture: <name> # optional, only if a later step needs it via \\$var.<name>
|
|
1284
|
-
- assert (
|
|
1285
|
-
- assert (
|
|
1288
|
+
- assert (fatal): <gating precondition \u2014 test must stop if wrong>
|
|
1289
|
+
- assert (fail): <observable condition \u2014 feature is broken if wrong>
|
|
1290
|
+
- assert (warn): <softer signal \u2014 cosmetic, run still passes>
|
|
1286
1291
|
|
|
1287
1292
|
**End**.
|
|
1288
1293
|
\`\`\`
|
|
@@ -1297,11 +1302,11 @@ Request: "Test the support ticket flow."
|
|
|
1297
1302
|
|
|
1298
1303
|
| file | name | priority | requirementRef |
|
|
1299
1304
|
|-------------------------------------------|-----------------------------------------------------|----------|----------------|
|
|
1300
|
-
|
|
|
1301
|
-
|
|
|
1302
|
-
|
|
|
1305
|
+
| cofactor/specs/submit-ticket.cofactor.md | Submit a ticket and confirm it appears in the queue | p0 | <fill in> |
|
|
1306
|
+
| cofactor/specs/ticket-required-fields.cofactor.md | Required-field validation blocks submit | p1 | <fill in> |
|
|
1307
|
+
| cofactor/specs/ticket-non-admin-reassign.cofactor.md | Non-admin cannot reassign a ticket | p1 | <fill in> |
|
|
1303
1308
|
|
|
1304
|
-
\`
|
|
1309
|
+
\`cofactor/specs/submit-ticket.cofactor.md\`:
|
|
1305
1310
|
|
|
1306
1311
|
\`\`\`markdown
|
|
1307
1312
|
---
|
|
@@ -1349,7 +1354,7 @@ requirementRef: <fill in>
|
|
|
1349
1354
|
**End**.
|
|
1350
1355
|
\`\`\`
|
|
1351
1356
|
|
|
1352
|
-
After writing, run \`
|
|
1357
|
+
After writing, run \`cofactor spec validate cofactor/specs/submit-ticket.cofactor.md\` and resolve any errors / warnings before handing back.
|
|
1353
1358
|
|
|
1354
1359
|
---
|
|
1355
1360
|
|
|
@@ -1373,21 +1378,21 @@ After writing, run \`canary spec validate canary/specs/submit-ticket.canary.md\`
|
|
|
1373
1378
|
<!-- If any field below is "<unfilled>", do a discovery pass before authoring cases: -->
|
|
1374
1379
|
<!-- - Read README.md, CLAUDE.md, AGENTS.md, docs/, specs/ for requirement and design pointers. -->
|
|
1375
1380
|
<!-- - Scan recent git log for ticket-tracker references (JIRA-123, LIN-456, #issue). -->
|
|
1376
|
-
<!-- - Run \`
|
|
1377
|
-
<!-- - (\`
|
|
1378
|
-
<!-- - Run \`
|
|
1379
|
-
<!-- - Run \`
|
|
1381
|
+
<!-- - Run \`cofactor property list\` and \`cofactor credential-template list --property "<property>"\` to learn properties + the credential templates that map to \`actor:\` values. -->
|
|
1382
|
+
<!-- - (\`cofactor credential list\` shows live credentials \u2014 useful, but the \`actor:\` field references credential *templates*, not credentials.) -->
|
|
1383
|
+
<!-- - Run \`cofactor workflow list --search <feature>\` to avoid duplicating existing workflows. -->
|
|
1384
|
+
<!-- - Run \`cofactor feature-flag list\` to confirm flag names referenced in Preflight prose. -->
|
|
1380
1385
|
<!-- - Propose values to the user, get confirmation, then edit this block in place. -->
|
|
1381
1386
|
|
|
1382
1387
|
- **Requirements / PRDs**: <unfilled \u2014 paths or URLs, e.g. \`docs/specs/\`, Notion space, Linear project>
|
|
1383
1388
|
- **Designs**: <unfilled \u2014 Figma links, design system doc>
|
|
1384
1389
|
- **Ticket tracker**: <unfilled \u2014 Jira/Linear project key + how to format \`requirementRef\` (e.g. \`PROJ-123\` or a URL)>
|
|
1385
|
-
- **Default property**: <unfilled \u2014 the property name to target; find via \`
|
|
1390
|
+
- **Default property**: <unfilled \u2014 the property name to target; find via \`cofactor property list\`>
|
|
1386
1391
|
- **Default environment**: <unfilled \u2014 \`prod\` | \`staging\` | \`dev\`>
|
|
1387
|
-
- **Credentials / role matrix** (\`actor:\` is a credential template name by default \u2014 list via \`
|
|
1392
|
+
- **Credentials / role matrix** (\`actor:\` is a credential template name by default \u2014 list via \`cofactor credential-template list --property "<property>"\`. Prefix with \`credential \` to bind a specific raw credential \u2014 list via \`cofactor credential list\`. Use the template form unless the case genuinely needs a fixed named account.): <unfilled \u2014 list the credential template names available on the default property, with a one-line note on which to use when. Common shape: \`actor: Admin\` (standard user), \`actor: Superadmin\` (full access).>
|
|
1388
1393
|
- **Tagging conventions**: <unfilled \u2014 e.g. \`smoke\`, \`regression\`, plus the priority tags \`p0\` / \`p1\` / \`p2\`>
|
|
1389
|
-
- **Spec directory**: <unfilled \u2014 where \`.
|
|
1390
|
-
- **Existing workflows to extend or avoid duplicating**: <unfilled \u2014 search via \`
|
|
1394
|
+
- **Spec directory**: <unfilled \u2014 where \`.cofactor.md\` files live in this repo, e.g. \`cofactor/specs/\`>
|
|
1395
|
+
- **Existing workflows to extend or avoid duplicating**: <unfilled \u2014 search via \`cofactor workflow list --search <term>\`>
|
|
1391
1396
|
- **Glossary / domain terms**: <unfilled \u2014 terms that show up in tickets and should appear verbatim in spec names>
|
|
1392
1397
|
- **Other sources of truth**: <unfilled \u2014 runbooks, support tickets, analytics dashboards>
|
|
1393
1398
|
|
|
@@ -1456,6 +1461,70 @@ var SKILL_TEMPLATES = {
|
|
|
1456
1461
|
};
|
|
1457
1462
|
|
|
1458
1463
|
// src/skill.ts
|
|
1464
|
+
var SKILL_SOURCE_KEY = "skill-source";
|
|
1465
|
+
function renderForActiveBrand(template) {
|
|
1466
|
+
return brandSkillContentFor(template.render(), command);
|
|
1467
|
+
}
|
|
1468
|
+
function splitFrontmatter(content) {
|
|
1469
|
+
const lines = content.split("\n");
|
|
1470
|
+
if (lines[0] !== "---") return null;
|
|
1471
|
+
for (let i = 1; i < lines.length; i++) {
|
|
1472
|
+
if (lines[i] === "---") {
|
|
1473
|
+
return {
|
|
1474
|
+
frontmatter: lines.slice(1, i).join("\n"),
|
|
1475
|
+
// Keep the closing `---` and everything after it in the body so a
|
|
1476
|
+
// round-trip with `applyInstallIdentity` reproduces the file exactly.
|
|
1477
|
+
body: lines.slice(i).join("\n")
|
|
1478
|
+
};
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
return null;
|
|
1482
|
+
}
|
|
1483
|
+
function readFrontmatterField(frontmatter, key) {
|
|
1484
|
+
for (const line of frontmatter.split("\n")) {
|
|
1485
|
+
const match = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
|
|
1486
|
+
if (match && match[1] === key) return match[2].trim();
|
|
1487
|
+
}
|
|
1488
|
+
return null;
|
|
1489
|
+
}
|
|
1490
|
+
function installedSourceKey(content) {
|
|
1491
|
+
const parts = splitFrontmatter(content);
|
|
1492
|
+
if (!parts) return null;
|
|
1493
|
+
return readFrontmatterField(parts.frontmatter, SKILL_SOURCE_KEY) ?? readFrontmatterField(parts.frontmatter, "name");
|
|
1494
|
+
}
|
|
1495
|
+
function applyInstallIdentity(content, installName, templateKey) {
|
|
1496
|
+
if (installName === templateKey) return content;
|
|
1497
|
+
const parts = splitFrontmatter(content);
|
|
1498
|
+
if (!parts) return content;
|
|
1499
|
+
const out = [];
|
|
1500
|
+
let wroteName = false;
|
|
1501
|
+
let wroteSource = false;
|
|
1502
|
+
for (const line of parts.frontmatter.split("\n")) {
|
|
1503
|
+
const match = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
|
|
1504
|
+
const key = match?.[1];
|
|
1505
|
+
if (key === "name") {
|
|
1506
|
+
out.push(`name: ${installName}`);
|
|
1507
|
+
out.push(`${SKILL_SOURCE_KEY}: ${templateKey}`);
|
|
1508
|
+
wroteName = true;
|
|
1509
|
+
wroteSource = true;
|
|
1510
|
+
} else if (key === SKILL_SOURCE_KEY) {
|
|
1511
|
+
if (!wroteSource) {
|
|
1512
|
+
out.push(`${SKILL_SOURCE_KEY}: ${templateKey}`);
|
|
1513
|
+
wroteSource = true;
|
|
1514
|
+
}
|
|
1515
|
+
} else {
|
|
1516
|
+
out.push(line);
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
if (!wroteName) out.unshift(`name: ${installName}`);
|
|
1520
|
+
if (!wroteSource) {
|
|
1521
|
+
const nameIdx = out.findIndex((l) => l.startsWith("name:"));
|
|
1522
|
+
out.splice(nameIdx >= 0 ? nameIdx + 1 : 0, 0, `${SKILL_SOURCE_KEY}: ${templateKey}`);
|
|
1523
|
+
}
|
|
1524
|
+
return `---
|
|
1525
|
+
${out.join("\n")}
|
|
1526
|
+
${parts.body}`;
|
|
1527
|
+
}
|
|
1459
1528
|
var SUPPORTED_AGENTS = ["claude", "codex", "copilot"];
|
|
1460
1529
|
function isAgentType(value) {
|
|
1461
1530
|
return value === "claude" || value === "codex" || value === "copilot";
|
|
@@ -1493,6 +1562,8 @@ function printSkillHelp() {
|
|
|
1493
1562
|
" canary skill install <name> Install skill (auto-detects agent)",
|
|
1494
1563
|
" canary skill install <name> --agent codex Install for a specific agent",
|
|
1495
1564
|
" canary skill install <name> --personal Install to ~/.<agent>/skills/",
|
|
1565
|
+
" canary skill install <name> --name <as> Install under a custom skill name",
|
|
1566
|
+
" canary skill install <name> --force Overwrite a name held by another skill",
|
|
1496
1567
|
" canary skill list List available skill templates",
|
|
1497
1568
|
" canary skill sync Print manifest + instructions for the",
|
|
1498
1569
|
" agent to version-compare and update",
|
|
@@ -1518,6 +1589,21 @@ function printSkillHelp() {
|
|
|
1518
1589
|
" 4. .claude/.codex/.github dirs \u2192 whichever single one exists",
|
|
1519
1590
|
" 5. If ambiguous, use --agent to specify explicitly",
|
|
1520
1591
|
"",
|
|
1592
|
+
"Custom skill names (--name):",
|
|
1593
|
+
" By default a skill installs under its template name. Pass --name to install",
|
|
1594
|
+
" it under a name of your choosing \u2014 useful when you want every Canary-provided",
|
|
1595
|
+
" skill namespaced (e.g. canary-browser-testing) to avoid colliding with your",
|
|
1596
|
+
" own skills. --name sets both the install directory and the SKILL.md",
|
|
1597
|
+
" frontmatter name: (agents require those to match), and records the source",
|
|
1598
|
+
" template in a skill-source: field so `sync` and `diff` keep tracking updates",
|
|
1599
|
+
" even under the renamed install.",
|
|
1600
|
+
"",
|
|
1601
|
+
" canary skill install browser-testing --name canary-browser-testing",
|
|
1602
|
+
"",
|
|
1603
|
+
" Installing a different skill onto an existing custom name is refused unless",
|
|
1604
|
+
" you pass --force. When an agent installs skills on your behalf, it should",
|
|
1605
|
+
" follow your naming convention.",
|
|
1606
|
+
"",
|
|
1521
1607
|
"Available templates:"
|
|
1522
1608
|
];
|
|
1523
1609
|
for (const [key, template] of Object.entries(SKILL_TEMPLATES)) {
|
|
@@ -1678,10 +1764,25 @@ function runSync() {
|
|
|
1678
1764
|
You are an AI coding agent (Claude, Codex, Copilot, etc.). The user ran \`canary skill sync\`
|
|
1679
1765
|
to bring their installed Canary skill templates up to date. Follow these steps:
|
|
1680
1766
|
|
|
1681
|
-
## Step 1 \u2014
|
|
1767
|
+
## Step 1 \u2014 Discover installed skills (including renamed ones)
|
|
1768
|
+
|
|
1769
|
+
Skills can be installed under a **custom name** (\`canary skill install <name> --name <as>\`),
|
|
1770
|
+
so do not rely only on the canonical \`installedPaths\` below. List every \`SKILL.md\`
|
|
1771
|
+
under your agent's skills directory \u2014 both project and personal:
|
|
1772
|
+
|
|
1773
|
+
.claude/skills/*/SKILL.md and ~/.claude/skills/*/SKILL.md (claude)
|
|
1774
|
+
.codex/skills/*/SKILL.md and ~/.codex/skills/*/SKILL.md (codex)
|
|
1775
|
+
.github/skills/*/SKILL.md and ~/.copilot/skills/*/SKILL.md (copilot)
|
|
1682
1776
|
|
|
1683
|
-
|
|
1684
|
-
|
|
1777
|
+
For each file, read its frontmatter and resolve which manifest skill it came from:
|
|
1778
|
+
- If a \`skill-source: <key>\` line is present, that \`<key>\` is the template name
|
|
1779
|
+
(the skill was installed under a custom \`--name\`). The install directory is the
|
|
1780
|
+
custom name; remember it for Step 5.
|
|
1781
|
+
- Otherwise the \`name:\` value is the template name (a default install, where the
|
|
1782
|
+
install name equals the template key).
|
|
1783
|
+
|
|
1784
|
+
Match that resolved key against the manifest \`name\` field. Ignore any \`SKILL.md\`
|
|
1785
|
+
whose resolved key is not in the manifest (it isn't a Canary skill).
|
|
1685
1786
|
|
|
1686
1787
|
## Step 2 \u2014 Read the version of each installed skill
|
|
1687
1788
|
|
|
@@ -1696,9 +1797,11 @@ For each installed skill, compare \`installedVersion\` to the \`version\` in the
|
|
|
1696
1797
|
|
|
1697
1798
|
## Step 4 \u2014 For each outdated skill, inspect what changed
|
|
1698
1799
|
|
|
1699
|
-
Before overwriting, read the diff so you know what's new
|
|
1800
|
+
Before overwriting, read the diff so you know what's new. Diff by the **template
|
|
1801
|
+
key** you resolved in Step 1 (not the custom install name) \u2014 the diff compares
|
|
1802
|
+
source renders and is independent of how the skill was named on disk:
|
|
1700
1803
|
|
|
1701
|
-
canary skill diff <
|
|
1804
|
+
canary skill diff <templateKey> --from <installedVersion>
|
|
1702
1805
|
|
|
1703
1806
|
The CLI retains past renders of each skill in \`previousVersions\`, so this prints a
|
|
1704
1807
|
unified diff from the user's installed version \u2192 the current version. Only versions
|
|
@@ -1714,13 +1817,19 @@ Use the diff to:
|
|
|
1714
1817
|
|
|
1715
1818
|
## Step 5 \u2014 Update outdated skills
|
|
1716
1819
|
|
|
1717
|
-
Re-install
|
|
1820
|
+
Re-install with the template key, preserving both the location AND the custom name
|
|
1821
|
+
it was installed under (the install directory name from Step 1):
|
|
1718
1822
|
|
|
1719
|
-
canary skill install <
|
|
1720
|
-
canary skill install <
|
|
1823
|
+
canary skill install <templateKey> # default-named project install
|
|
1824
|
+
canary skill install <templateKey> --personal # default-named personal install
|
|
1825
|
+
canary skill install <templateKey> --name <installDir> # custom-named project install
|
|
1826
|
+
canary skill install <templateKey> --name <installDir> --personal
|
|
1721
1827
|
|
|
1722
|
-
Preserve whichever location it was already installed in
|
|
1723
|
-
|
|
1828
|
+
Preserve whichever location and name it was already installed in \u2014 if it lived in
|
|
1829
|
+
\`canary-browser-testing/\`, re-install with \`--name canary-browser-testing\` so the
|
|
1830
|
+
update lands in place rather than creating a second default-named copy. Do NOT install
|
|
1831
|
+
skills that weren't already installed \u2014 sync only updates existing installs. When the
|
|
1832
|
+
user has a naming convention, keep installing under their chosen names on their behalf.
|
|
1724
1833
|
|
|
1725
1834
|
## Step 6 \u2014 Report
|
|
1726
1835
|
|
|
@@ -1735,10 +1844,15 @@ ${JSON.stringify({ skills: manifest }, null, 2)}
|
|
|
1735
1844
|
`;
|
|
1736
1845
|
process.stdout.write(instructions);
|
|
1737
1846
|
}
|
|
1847
|
+
function isValidInstallName(value) {
|
|
1848
|
+
return /^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(value);
|
|
1849
|
+
}
|
|
1738
1850
|
function parseInstallArgs(argv) {
|
|
1739
|
-
let
|
|
1851
|
+
let templateName;
|
|
1852
|
+
let installName;
|
|
1740
1853
|
let agent;
|
|
1741
1854
|
let personal = false;
|
|
1855
|
+
let force = false;
|
|
1742
1856
|
for (let i = 0; i < argv.length; i++) {
|
|
1743
1857
|
const arg = argv[i];
|
|
1744
1858
|
if (arg === "--agent" && i + 1 < argv.length) {
|
|
@@ -1750,26 +1864,43 @@ function parseInstallArgs(argv) {
|
|
|
1750
1864
|
process.exit(1);
|
|
1751
1865
|
}
|
|
1752
1866
|
agent = val;
|
|
1867
|
+
} else if (arg === "--name" && i + 1 < argv.length) {
|
|
1868
|
+
const val = argv[++i];
|
|
1869
|
+
if (!isValidInstallName(val)) {
|
|
1870
|
+
console.error(
|
|
1871
|
+
`Invalid skill name "${val}". Use letters, digits, hyphens, or underscores (e.g. canary-browser-testing).`
|
|
1872
|
+
);
|
|
1873
|
+
process.exit(1);
|
|
1874
|
+
}
|
|
1875
|
+
installName = val;
|
|
1753
1876
|
} else if (arg === "--personal") {
|
|
1754
1877
|
personal = true;
|
|
1755
|
-
} else if (
|
|
1756
|
-
|
|
1878
|
+
} else if (arg === "--force" || arg === "-f") {
|
|
1879
|
+
force = true;
|
|
1880
|
+
} else if (!arg.startsWith("-") && !templateName) {
|
|
1881
|
+
templateName = arg;
|
|
1757
1882
|
}
|
|
1758
1883
|
}
|
|
1759
|
-
return {
|
|
1884
|
+
return { templateName, installName: installName ?? templateName, agent, personal, force };
|
|
1760
1885
|
}
|
|
1761
1886
|
async function runInstall(argv) {
|
|
1762
|
-
const {
|
|
1763
|
-
|
|
1887
|
+
const {
|
|
1888
|
+
templateName,
|
|
1889
|
+
installName,
|
|
1890
|
+
agent: explicitAgent,
|
|
1891
|
+
personal,
|
|
1892
|
+
force
|
|
1893
|
+
} = parseInstallArgs(argv);
|
|
1894
|
+
if (!templateName || !installName) {
|
|
1764
1895
|
console.error("Missing skill name.\n");
|
|
1765
1896
|
console.error(
|
|
1766
|
-
`Usage: canary skill install <name> [--agent ${SUPPORTED_AGENTS.join("|")}] [--personal]`
|
|
1897
|
+
`Usage: canary skill install <name> [--name <install-name>] [--agent ${SUPPORTED_AGENTS.join("|")}] [--personal] [--force]`
|
|
1767
1898
|
);
|
|
1768
1899
|
process.exit(1);
|
|
1769
1900
|
}
|
|
1770
|
-
const template = SKILL_TEMPLATES[
|
|
1901
|
+
const template = SKILL_TEMPLATES[templateName];
|
|
1771
1902
|
if (!template) {
|
|
1772
|
-
console.error(`Unknown skill template "${
|
|
1903
|
+
console.error(`Unknown skill template "${templateName}".
|
|
1773
1904
|
`);
|
|
1774
1905
|
console.error("Available templates:");
|
|
1775
1906
|
for (const [key, t] of Object.entries(SKILL_TEMPLATES)) {
|
|
@@ -1793,12 +1924,27 @@ async function runInstall(argv) {
|
|
|
1793
1924
|
);
|
|
1794
1925
|
process.exit(1);
|
|
1795
1926
|
}
|
|
1796
|
-
const dir = resolveInstallDir(
|
|
1927
|
+
const dir = resolveInstallDir(installName, agent, personal);
|
|
1797
1928
|
const filePath = join2(dir, "SKILL.md");
|
|
1929
|
+
if (existsSync(filePath) && !force) {
|
|
1930
|
+
const existingSource = installedSourceKey(readFileSync2(filePath, "utf-8"));
|
|
1931
|
+
if (existingSource && existingSource !== templateName) {
|
|
1932
|
+
console.error(
|
|
1933
|
+
`\u2717 ${installName} already holds '${existingSource}'. Pass --force to overwrite.`
|
|
1934
|
+
);
|
|
1935
|
+
process.exit(1);
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
const content = applyInstallIdentity(
|
|
1939
|
+
renderForActiveBrand(template),
|
|
1940
|
+
installName,
|
|
1941
|
+
templateName
|
|
1942
|
+
);
|
|
1798
1943
|
mkdirSync(dir, { recursive: true });
|
|
1799
|
-
writeFileSync(filePath,
|
|
1944
|
+
writeFileSync(filePath, content, "utf-8");
|
|
1800
1945
|
const relPath = personal ? filePath : filePath.replace(process.cwd() + "/", "");
|
|
1801
|
-
|
|
1946
|
+
const named = installName === templateName ? "" : ` as ${installName}`;
|
|
1947
|
+
console.log(`Installed ${templateName} skill for ${agent}${named} \u2192 ${relPath}`);
|
|
1802
1948
|
}
|
|
1803
1949
|
async function runSkill(argv) {
|
|
1804
1950
|
const [sub] = argv;
|
|
@@ -1829,11 +1975,15 @@ async function runSkill(argv) {
|
|
|
1829
1975
|
printSkillHelp();
|
|
1830
1976
|
process.exit(1);
|
|
1831
1977
|
}
|
|
1832
|
-
process.stdout.write(template
|
|
1978
|
+
process.stdout.write(renderForActiveBrand(template));
|
|
1833
1979
|
}
|
|
1834
1980
|
export {
|
|
1981
|
+
SKILL_SOURCE_KEY,
|
|
1982
|
+
applyInstallIdentity,
|
|
1835
1983
|
detectAgent,
|
|
1984
|
+
installedSourceKey,
|
|
1985
|
+
isValidInstallName,
|
|
1836
1986
|
resolveInstallDir,
|
|
1837
1987
|
runSkill
|
|
1838
1988
|
};
|
|
1839
|
-
//# sourceMappingURL=skill-
|
|
1989
|
+
//# sourceMappingURL=skill-23WAHUOB.js.map
|