@chief-clancy/terminal 0.1.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 (217) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +62 -0
  3. package/dist/hooks/clancy-branch-guard.js +1 -0
  4. package/dist/hooks/clancy-check-update.js +2 -0
  5. package/dist/hooks/clancy-context-monitor.js +9 -0
  6. package/dist/hooks/clancy-credential-guard.js +2 -0
  7. package/dist/hooks/clancy-drift-detector.js +1 -0
  8. package/dist/hooks/clancy-notification.js +1 -0
  9. package/dist/hooks/clancy-post-compact.js +2 -0
  10. package/dist/hooks/clancy-statusline.js +1 -0
  11. package/dist/index.d.ts +24 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +23 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/installer/file-ops/file-ops.d.ts +35 -0
  16. package/dist/installer/file-ops/file-ops.d.ts.map +1 -0
  17. package/dist/installer/file-ops/file-ops.js +95 -0
  18. package/dist/installer/file-ops/file-ops.js.map +1 -0
  19. package/dist/installer/file-ops/index.d.ts +2 -0
  20. package/dist/installer/file-ops/index.d.ts.map +1 -0
  21. package/dist/installer/file-ops/index.js +2 -0
  22. package/dist/installer/file-ops/index.js.map +1 -0
  23. package/dist/installer/hook-installer/hook-installer.d.ts +22 -0
  24. package/dist/installer/hook-installer/hook-installer.d.ts.map +1 -0
  25. package/dist/installer/hook-installer/hook-installer.js +213 -0
  26. package/dist/installer/hook-installer/hook-installer.js.map +1 -0
  27. package/dist/installer/hook-installer/index.d.ts +2 -0
  28. package/dist/installer/hook-installer/index.d.ts.map +1 -0
  29. package/dist/installer/hook-installer/index.js +2 -0
  30. package/dist/installer/hook-installer/index.js.map +1 -0
  31. package/dist/installer/install/index.d.ts +3 -0
  32. package/dist/installer/install/index.d.ts.map +1 -0
  33. package/dist/installer/install/index.js +2 -0
  34. package/dist/installer/install/index.js.map +1 -0
  35. package/dist/installer/install/install.d.ts +124 -0
  36. package/dist/installer/install/install.d.ts.map +1 -0
  37. package/dist/installer/install/install.js +255 -0
  38. package/dist/installer/install/install.js.map +1 -0
  39. package/dist/installer/manifest/index.d.ts +2 -0
  40. package/dist/installer/manifest/index.d.ts.map +1 -0
  41. package/dist/installer/manifest/index.js +2 -0
  42. package/dist/installer/manifest/index.js.map +1 -0
  43. package/dist/installer/manifest/manifest.d.ts +46 -0
  44. package/dist/installer/manifest/manifest.d.ts.map +1 -0
  45. package/dist/installer/manifest/manifest.js +180 -0
  46. package/dist/installer/manifest/manifest.js.map +1 -0
  47. package/dist/installer/prompts/index.d.ts +2 -0
  48. package/dist/installer/prompts/index.d.ts.map +1 -0
  49. package/dist/installer/prompts/index.js +2 -0
  50. package/dist/installer/prompts/index.js.map +1 -0
  51. package/dist/installer/prompts/prompts.d.ts +34 -0
  52. package/dist/installer/prompts/prompts.d.ts.map +1 -0
  53. package/dist/installer/prompts/prompts.js +28 -0
  54. package/dist/installer/prompts/prompts.js.map +1 -0
  55. package/dist/installer/role-filter/index.d.ts +2 -0
  56. package/dist/installer/role-filter/index.d.ts.map +1 -0
  57. package/dist/installer/role-filter/index.js +2 -0
  58. package/dist/installer/role-filter/index.js.map +1 -0
  59. package/dist/installer/role-filter/role-filter.d.ts +33 -0
  60. package/dist/installer/role-filter/role-filter.d.ts.map +1 -0
  61. package/dist/installer/role-filter/role-filter.js +91 -0
  62. package/dist/installer/role-filter/role-filter.js.map +1 -0
  63. package/dist/installer/shared/fs-errors/fs-errors.d.ts +3 -0
  64. package/dist/installer/shared/fs-errors/fs-errors.d.ts.map +1 -0
  65. package/dist/installer/shared/fs-errors/fs-errors.js +7 -0
  66. package/dist/installer/shared/fs-errors/fs-errors.js.map +1 -0
  67. package/dist/installer/shared/fs-errors/index.d.ts +2 -0
  68. package/dist/installer/shared/fs-errors/index.d.ts.map +1 -0
  69. package/dist/installer/shared/fs-errors/index.js +2 -0
  70. package/dist/installer/shared/fs-errors/index.js.map +1 -0
  71. package/dist/installer/shared/fs-guards/fs-guards.d.ts +3 -0
  72. package/dist/installer/shared/fs-guards/fs-guards.d.ts.map +1 -0
  73. package/dist/installer/shared/fs-guards/fs-guards.js +18 -0
  74. package/dist/installer/shared/fs-guards/fs-guards.js.map +1 -0
  75. package/dist/installer/shared/fs-guards/index.d.ts +2 -0
  76. package/dist/installer/shared/fs-guards/index.d.ts.map +1 -0
  77. package/dist/installer/shared/fs-guards/index.js +2 -0
  78. package/dist/installer/shared/fs-guards/index.js.map +1 -0
  79. package/dist/installer/shared/type-guards/index.d.ts +2 -0
  80. package/dist/installer/shared/type-guards/index.d.ts.map +1 -0
  81. package/dist/installer/shared/type-guards/index.js +2 -0
  82. package/dist/installer/shared/type-guards/index.js.map +1 -0
  83. package/dist/installer/shared/type-guards/type-guards.d.ts +8 -0
  84. package/dist/installer/shared/type-guards/type-guards.d.ts.map +1 -0
  85. package/dist/installer/shared/type-guards/type-guards.js +10 -0
  86. package/dist/installer/shared/type-guards/type-guards.js.map +1 -0
  87. package/dist/installer/ui/index.d.ts +2 -0
  88. package/dist/installer/ui/index.d.ts.map +1 -0
  89. package/dist/installer/ui/index.js +2 -0
  90. package/dist/installer/ui/index.js.map +1 -0
  91. package/dist/installer/ui/ui.d.ts +23 -0
  92. package/dist/installer/ui/ui.d.ts.map +1 -0
  93. package/dist/installer/ui/ui.js +121 -0
  94. package/dist/installer/ui/ui.js.map +1 -0
  95. package/dist/runner/autopilot/autopilot.d.ts +71 -0
  96. package/dist/runner/autopilot/autopilot.d.ts.map +1 -0
  97. package/dist/runner/autopilot/autopilot.js +206 -0
  98. package/dist/runner/autopilot/autopilot.js.map +1 -0
  99. package/dist/runner/autopilot/index.d.ts +2 -0
  100. package/dist/runner/autopilot/index.d.ts.map +1 -0
  101. package/dist/runner/autopilot/index.js +2 -0
  102. package/dist/runner/autopilot/index.js.map +1 -0
  103. package/dist/runner/cli-bridge/cli-bridge.d.ts +34 -0
  104. package/dist/runner/cli-bridge/cli-bridge.d.ts.map +1 -0
  105. package/dist/runner/cli-bridge/cli-bridge.js +53 -0
  106. package/dist/runner/cli-bridge/cli-bridge.js.map +1 -0
  107. package/dist/runner/cli-bridge/index.d.ts +2 -0
  108. package/dist/runner/cli-bridge/index.d.ts.map +1 -0
  109. package/dist/runner/cli-bridge/index.js +2 -0
  110. package/dist/runner/cli-bridge/index.js.map +1 -0
  111. package/dist/runner/dep-factory/deliver-phase.d.ts +24 -0
  112. package/dist/runner/dep-factory/deliver-phase.d.ts.map +1 -0
  113. package/dist/runner/dep-factory/deliver-phase.js +57 -0
  114. package/dist/runner/dep-factory/deliver-phase.js.map +1 -0
  115. package/dist/runner/dep-factory/dep-factory.d.ts +38 -0
  116. package/dist/runner/dep-factory/dep-factory.d.ts.map +1 -0
  117. package/dist/runner/dep-factory/dep-factory.js +193 -0
  118. package/dist/runner/dep-factory/dep-factory.js.map +1 -0
  119. package/dist/runner/dep-factory/index.d.ts +2 -0
  120. package/dist/runner/dep-factory/index.d.ts.map +1 -0
  121. package/dist/runner/dep-factory/index.js +2 -0
  122. package/dist/runner/dep-factory/index.js.map +1 -0
  123. package/dist/runner/dep-factory/invoke-phase.d.ts +20 -0
  124. package/dist/runner/dep-factory/invoke-phase.d.ts.map +1 -0
  125. package/dist/runner/dep-factory/invoke-phase.js +45 -0
  126. package/dist/runner/dep-factory/invoke-phase.js.map +1 -0
  127. package/dist/runner/implement/implement.d.ts +38 -0
  128. package/dist/runner/implement/implement.d.ts.map +1 -0
  129. package/dist/runner/implement/implement.js +61 -0
  130. package/dist/runner/implement/implement.js.map +1 -0
  131. package/dist/runner/implement/index.d.ts +2 -0
  132. package/dist/runner/implement/index.d.ts.map +1 -0
  133. package/dist/runner/implement/index.js +2 -0
  134. package/dist/runner/implement/index.js.map +1 -0
  135. package/dist/runner/notify/index.d.ts +2 -0
  136. package/dist/runner/notify/index.d.ts.map +1 -0
  137. package/dist/runner/notify/index.js +2 -0
  138. package/dist/runner/notify/index.js.map +1 -0
  139. package/dist/runner/notify/notify.d.ts +49 -0
  140. package/dist/runner/notify/notify.d.ts.map +1 -0
  141. package/dist/runner/notify/notify.js +90 -0
  142. package/dist/runner/notify/notify.js.map +1 -0
  143. package/dist/runner/prompt-builder/index.d.ts +2 -0
  144. package/dist/runner/prompt-builder/index.d.ts.map +1 -0
  145. package/dist/runner/prompt-builder/index.js +2 -0
  146. package/dist/runner/prompt-builder/index.js.map +1 -0
  147. package/dist/runner/prompt-builder/prompt-builder.d.ts +53 -0
  148. package/dist/runner/prompt-builder/prompt-builder.d.ts.map +1 -0
  149. package/dist/runner/prompt-builder/prompt-builder.js +122 -0
  150. package/dist/runner/prompt-builder/prompt-builder.js.map +1 -0
  151. package/dist/runner/session-report/index.d.ts +2 -0
  152. package/dist/runner/session-report/index.d.ts.map +1 -0
  153. package/dist/runner/session-report/index.js +2 -0
  154. package/dist/runner/session-report/index.js.map +1 -0
  155. package/dist/runner/session-report/session-report.d.ts +81 -0
  156. package/dist/runner/session-report/session-report.d.ts.map +1 -0
  157. package/dist/runner/session-report/session-report.js +227 -0
  158. package/dist/runner/session-report/session-report.js.map +1 -0
  159. package/dist/runner/shared/types.d.ts +30 -0
  160. package/dist/runner/shared/types.d.ts.map +1 -0
  161. package/dist/runner/shared/types.js +2 -0
  162. package/dist/runner/shared/types.js.map +1 -0
  163. package/dist/shared/ansi/ansi.d.ts +59 -0
  164. package/dist/shared/ansi/ansi.d.ts.map +1 -0
  165. package/dist/shared/ansi/ansi.js +59 -0
  166. package/dist/shared/ansi/ansi.js.map +1 -0
  167. package/dist/shared/ansi/index.d.ts +2 -0
  168. package/dist/shared/ansi/index.d.ts.map +1 -0
  169. package/dist/shared/ansi/index.js +2 -0
  170. package/dist/shared/ansi/index.js.map +1 -0
  171. package/package.json +52 -0
  172. package/src/agents/agents.test.ts +57 -0
  173. package/src/agents/arch-agent.md +80 -0
  174. package/src/agents/concerns-agent.md +96 -0
  175. package/src/agents/design-agent.md +146 -0
  176. package/src/agents/devils-advocate.md +54 -0
  177. package/src/agents/quality-agent.md +178 -0
  178. package/src/agents/tech-agent.md +101 -0
  179. package/src/agents/verification-gate.md +128 -0
  180. package/src/roles/implementer/commands/autopilot.md +11 -0
  181. package/src/roles/implementer/commands/dry-run.md +15 -0
  182. package/src/roles/implementer/commands/implement.md +19 -0
  183. package/src/roles/implementer/workflows/autopilot.md +136 -0
  184. package/src/roles/implementer/workflows/implement.md +161 -0
  185. package/src/roles/planner/commands/approve-plan.md +11 -0
  186. package/src/roles/planner/commands/plan.md +22 -0
  187. package/src/roles/planner/workflows/approve-plan.md +970 -0
  188. package/src/roles/planner/workflows/plan.md +868 -0
  189. package/src/roles/reviewer/commands/logs.md +7 -0
  190. package/src/roles/reviewer/commands/review.md +9 -0
  191. package/src/roles/reviewer/commands/status.md +9 -0
  192. package/src/roles/reviewer/workflows/logs.md +109 -0
  193. package/src/roles/reviewer/workflows/review.md +197 -0
  194. package/src/roles/reviewer/workflows/status.md +142 -0
  195. package/src/roles/roles.test.ts +87 -0
  196. package/src/roles/setup/commands/doctor.md +7 -0
  197. package/src/roles/setup/commands/help.md +80 -0
  198. package/src/roles/setup/commands/init.md +7 -0
  199. package/src/roles/setup/commands/map-codebase.md +17 -0
  200. package/src/roles/setup/commands/settings.md +7 -0
  201. package/src/roles/setup/commands/uninstall.md +5 -0
  202. package/src/roles/setup/commands/update-docs.md +9 -0
  203. package/src/roles/setup/commands/update.md +13 -0
  204. package/src/roles/setup/workflows/doctor.md +131 -0
  205. package/src/roles/setup/workflows/init.md +1096 -0
  206. package/src/roles/setup/workflows/map-codebase.md +130 -0
  207. package/src/roles/setup/workflows/scaffold.md +872 -0
  208. package/src/roles/setup/workflows/settings.md +958 -0
  209. package/src/roles/setup/workflows/uninstall.md +170 -0
  210. package/src/roles/setup/workflows/update-docs.md +95 -0
  211. package/src/roles/setup/workflows/update.md +287 -0
  212. package/src/roles/strategist/commands/approve-brief.md +23 -0
  213. package/src/roles/strategist/commands/brief.md +29 -0
  214. package/src/roles/strategist/workflows/approve-brief.md +1540 -0
  215. package/src/roles/strategist/workflows/brief.md +1330 -0
  216. package/src/templates/CLAUDE.md +101 -0
  217. package/src/templates/templates.test.ts +53 -0
@@ -0,0 +1,868 @@
1
+ # Clancy Plan Workflow
2
+
3
+ ## Overview
4
+
5
+ Fetch backlog tickets from the board, explore the codebase, and generate structured implementation plans. Plans are posted as comments on the ticket for human review. Does not implement anything — planning only.
6
+
7
+ ---
8
+
9
+ ## Step 1 — Preflight checks
10
+
11
+ 1. Check `.clancy/` exists and `.clancy/.env` is present. If not:
12
+
13
+ ```
14
+ .clancy/ not found. Run /clancy:init to set up Clancy first.
15
+ ```
16
+
17
+ Stop.
18
+
19
+ 2. Source `.clancy/.env` and check board credentials are present.
20
+
21
+ 3. Check `CLANCY_ROLES` includes `planner` (or env var is unset, which indicates a global install where all roles are available). If `CLANCY_ROLES` is set but does not include `planner`:
22
+
23
+ ```
24
+ The Planner role is not enabled. Add "planner" to CLANCY_ROLES in .clancy/.env or run /clancy:settings.
25
+ ```
26
+
27
+ Stop.
28
+
29
+ 4. Check `.clancy/docs/` — if the directory is empty or missing:
30
+
31
+ **AFK mode** (`--afk` flag or `CLANCY_MODE=afk`): continue without prompting (log a warning).
32
+
33
+ **Interactive mode:**
34
+
35
+ ```
36
+ ⚠️ No codebase documentation found in .clancy/docs/
37
+ Plans will be less accurate without codebase context.
38
+ Run /clancy:map-codebase first for better results.
39
+
40
+ Continue anyway? [y/N]
41
+ ```
42
+
43
+ If the user declines, stop. If they confirm, continue without docs context.
44
+
45
+ 5. Branch freshness check — run `git fetch origin` and compare the current HEAD with `origin/$CLANCY_BASE_BRANCH` (defaults to `main`). If the local branch is behind:
46
+
47
+ **AFK mode** (`--afk` flag or `CLANCY_MODE=afk`): auto-pull without prompting. Run `git pull origin $CLANCY_BASE_BRANCH` and continue.
48
+
49
+ **Interactive mode:**
50
+
51
+ ```
52
+ ⚠️ Your local branch is behind origin/{CLANCY_BASE_BRANCH} by {N} commit(s).
53
+
54
+ [1] Pull latest
55
+ [2] Continue anyway
56
+ [3] Abort
57
+ ```
58
+
59
+ - [1] runs `git pull origin $CLANCY_BASE_BRANCH` and continues
60
+ - [2] continues without pulling
61
+ - [3] stops
62
+
63
+ ---
64
+
65
+ ## Step 2 — Parse arguments
66
+
67
+ Parse the arguments passed to the command:
68
+
69
+ - **No argument:** plan 1 ticket from the queue
70
+ - **Numeric argument** (e.g. `/clancy:plan 3`): plan up to N tickets from the queue, cap at 10
71
+ - **Specific ticket key:** plan a single ticket by key, with per-platform validation:
72
+ - `#42` — valid for GitHub only. If board is Jira, Linear, or Shortcut: `The #N format is for GitHub Issues. Use a ticket key like PROJ-123.` Stop. If board is Azure DevOps: `The #N format is for GitHub Issues. Use a numeric work item ID (e.g. 42).` Stop. If board is Notion: `The #N format is for GitHub Issues. Use a Notion page UUID or notion-XXXXXXXX key.` Stop.
73
+ - `PROJ-123` / `ENG-42` (letters-dash-number) — valid for Jira, Linear, and Shortcut. If board is GitHub: `Use #N format for GitHub Issues (e.g. #42).` Stop. If board is Azure DevOps: `Use a numeric work item ID for Azure DevOps (e.g. 42).` Stop. If board is Notion: `Use a Notion page UUID or notion-XXXXXXXX key.` Stop.
74
+ - Bare integer — valid for Azure DevOps (work item ID) and GitHub (issue number). On Azure DevOps with value > 10: treat as a work item ID (no ambiguity — AzDo always uses numeric IDs). On GitHub with value > 10: ambiguous — ask:
75
+ - Bare integer on GitHub (e.g. `/clancy:plan 42` where 42 > 10): ambiguous — ask:
76
+ ```
77
+ Did you mean issue #42 or batch mode (42 tickets)?
78
+ [1] Plan issue #42
79
+ [2] Plan 10 tickets (max batch)
80
+ ```
81
+ - **`--fresh`:** discard any existing plan and start over from scratch. This is NOT re-plan with feedback — it ignores existing plans entirely.
82
+ - Arguments can appear in any order (e.g. `/clancy:plan --fresh PROJ-123` or `/clancy:plan PROJ-123 --fresh`)
83
+
84
+ If N > 10: `Maximum batch size is 10. Planning 10 tickets.`
85
+
86
+ If N >= 5: display a confirmation (skip in AFK mode — `--afk` flag or `CLANCY_MODE=afk`):
87
+
88
+ ```
89
+ Planning {N} tickets — each requires codebase exploration. Continue? [Y/n]
90
+ ```
91
+
92
+ ---
93
+
94
+ ## Step 3 — Fetch backlog tickets
95
+
96
+ Detect board from `.clancy/.env` and fetch tickets from the **planning queue** (different from the implementation queue used by `/clancy:implement`).
97
+
98
+ ### Specific ticket key (if provided)
99
+
100
+ If a specific ticket key was parsed in Step 2, fetch that single ticket instead of the queue:
101
+
102
+ #### GitHub — Fetch specific issue
103
+
104
+ ```bash
105
+ RESPONSE=$(curl -s \
106
+ -H "Authorization: Bearer $GITHUB_TOKEN" \
107
+ -H "X-GitHub-Api-Version: 2022-11-28" \
108
+ "https://api.github.com/repos/$GITHUB_REPO/issues/$ISSUE_NUMBER")
109
+ ```
110
+
111
+ Validate the response:
112
+
113
+ - If `pull_request` field is present (not null): `#{N} is a PR, not an issue.` Stop.
114
+ - If `state` is `closed`: warn `Issue #${N} is closed. Plan anyway? [y/N]` (in AFK mode: skip this ticket — do not plan closed issues unattended)
115
+
116
+ #### Jira — Fetch specific ticket
117
+
118
+ ```bash
119
+ RESPONSE=$(curl -s \
120
+ -u "$JIRA_USER:$JIRA_API_TOKEN" \
121
+ -H "Accept: application/json" \
122
+ "$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY?fields=summary,description,issuelinks,parent,customfield_10014,comment,status,issuetype")
123
+ ```
124
+
125
+ Validate the response:
126
+
127
+ - If `fields.status.statusCategory.key` is `done`: warn `Ticket is done. Plan anyway? [y/N]` (in AFK mode: skip this ticket)
128
+ - If `fields.issuetype.name` is `Epic`: note `This is an epic.` (continue normally)
129
+
130
+ #### Linear — Fetch specific issue
131
+
132
+ ```graphql
133
+ query {
134
+ issues(filter: { identifier: { eq: "$IDENTIFIER" } }) {
135
+ nodes {
136
+ id
137
+ identifier
138
+ title
139
+ description
140
+ state {
141
+ type
142
+ name
143
+ }
144
+ parent {
145
+ identifier
146
+ title
147
+ }
148
+ comments {
149
+ nodes {
150
+ id
151
+ body
152
+ createdAt
153
+ user {
154
+ id
155
+ }
156
+ }
157
+ }
158
+ }
159
+ }
160
+ }
161
+ ```
162
+
163
+ Validate the response:
164
+
165
+ - If `nodes` is empty: `Issue {KEY} not found on Linear.` Stop.
166
+ - If `state.type` is `completed`: warn `Issue is completed. Plan anyway? [y/N]` (in AFK mode: skip this ticket)
167
+ - If `state.type` is `canceled`: warn `Issue is canceled. Plan anyway? [y/N]` (in AFK mode: skip this ticket)
168
+
169
+ Then skip to Step 3b with this single ticket.
170
+
171
+ #### Azure DevOps — Fetch specific work item
172
+
173
+ ```bash
174
+ RESPONSE=$(curl -s \
175
+ -u ":$AZDO_PAT" \
176
+ -H "Accept: application/json" \
177
+ "https://dev.azure.com/$AZDO_ORG/$AZDO_PROJECT/_apis/wit/workitems/$WORK_ITEM_ID?\$expand=relations&api-version=7.1")
178
+ ```
179
+
180
+ Validate the response:
181
+
182
+ - If response contains `"message"` with `"does not exist"`: `Work item ${ID} not found.` Stop.
183
+ - If `fields["System.State"]` is a done/resolved state (e.g. `Done`, `Closed`, `Resolved`): warn `Work item is done. Plan anyway? [y/N]` (in AFK mode: skip this ticket)
184
+
185
+ To fetch comments (separate endpoint):
186
+
187
+ ```bash
188
+ COMMENTS=$(curl -s \
189
+ -u ":$AZDO_PAT" \
190
+ -H "Accept: application/json" \
191
+ "https://dev.azure.com/$AZDO_ORG/$AZDO_PROJECT/_apis/wit/workitems/$WORK_ITEM_ID/comments?api-version=7.1-preview.4")
192
+ ```
193
+
194
+ Map fields: title = `fields["System.Title"]`, description = `fields["System.Description"]` (HTML — strip tags for plan context), parent = check `relations` array for `System.LinkTypes.Hierarchy-Reverse` type.
195
+
196
+ Then skip to Step 3b with this single ticket.
197
+
198
+ #### Shortcut — Fetch specific story
199
+
200
+ ```bash
201
+ RESPONSE=$(curl -s \
202
+ -H "Shortcut-Token: $SHORTCUT_API_TOKEN" \
203
+ -H "Content-Type: application/json" \
204
+ "https://api.app.shortcut.com/api/v3/stories/$STORY_ID")
205
+ ```
206
+
207
+ Extract the story ID from the key (e.g. `SC-123` → `123`, or use the numeric portion). Validate the response:
208
+
209
+ - If response status is 404: `Story ${KEY} not found on Shortcut.` Stop.
210
+ - If `completed` is `true` or `archived` is `true`: warn `Story is completed/archived. Plan anyway? [y/N]` (in AFK mode: skip this ticket)
211
+
212
+ To fetch comments:
213
+
214
+ ```bash
215
+ COMMENTS=$(curl -s \
216
+ -H "Shortcut-Token: $SHORTCUT_API_TOKEN" \
217
+ "https://api.app.shortcut.com/api/v3/stories/$STORY_ID/comments")
218
+ ```
219
+
220
+ Map fields: title = `name`, description = `description` (markdown), parent = `epic_id` (fetch epic name via `GET /api/v3/epics/$EPIC_ID` if set).
221
+
222
+ Then skip to Step 3b with this single ticket.
223
+
224
+ #### Notion — Fetch specific page
225
+
226
+ Notion page IDs are UUIDs (32 hex chars, optionally with dashes). The key format in `.clancy/progress.txt` is `notion-{first 8 chars}` for brevity. To fetch, use the full page ID if available, or search the database.
227
+
228
+ ```bash
229
+ RESPONSE=$(curl -s \
230
+ -H "Authorization: Bearer $NOTION_TOKEN" \
231
+ -H "Notion-Version: 2022-06-28" \
232
+ "https://api.notion.com/v1/pages/$PAGE_ID")
233
+ ```
234
+
235
+ If the key is a short `notion-XXXXXXXX` format, query the database instead:
236
+
237
+ ```bash
238
+ RESPONSE=$(curl -s \
239
+ -H "Authorization: Bearer $NOTION_TOKEN" \
240
+ -H "Notion-Version: 2022-06-28" \
241
+ -X POST \
242
+ "https://api.notion.com/v1/databases/$NOTION_DATABASE_ID/query" \
243
+ -d '{"page_size": 100}')
244
+ ```
245
+
246
+ Then match pages by ID prefix.
247
+
248
+ Validate the response:
249
+
250
+ - If `archived` is `true`: warn `Page is archived. Plan anyway? [y/N]` (in AFK mode: skip)
251
+
252
+ To fetch comments (separate endpoint):
253
+
254
+ ```bash
255
+ COMMENTS=$(curl -s \
256
+ -H "Authorization: Bearer $NOTION_TOKEN" \
257
+ -H "Notion-Version: 2022-06-28" \
258
+ "https://api.notion.com/v1/comments?block_id=$PAGE_ID")
259
+ ```
260
+
261
+ Map fields: title = extract from `properties` (find the `title` type property), description = fetch page content via `GET /v1/blocks/$PAGE_ID/children` (Notion stores content as blocks, not a single description field), parent = `parent.page_id` or `parent.database_id`.
262
+
263
+ **Notion limitation:** Page content is stored as blocks, not a single text field. Read all child blocks and concatenate their text content for plan context.
264
+
265
+ Then skip to Step 3b with this single ticket.
266
+
267
+ ### Queue fetch (no specific key)
268
+
269
+ #### Jira
270
+
271
+ Build the JQL using planning-specific env vars:
272
+
273
+ - `CLANCY_PLAN_STATUS` defaults to `Backlog` if not set
274
+ - Sprint clause: include `AND sprint in openSprints()` if `CLANCY_JQL_SPRINT` is set
275
+ - Label clause: include `AND labels = "$CLANCY_LABEL_PLAN"` if `CLANCY_LABEL_PLAN` is set (falls back to `CLANCY_PLAN_LABEL` if `CLANCY_LABEL_PLAN` is not set). If neither is set, include `AND labels = "$CLANCY_LABEL"` if `CLANCY_LABEL` is set.
276
+
277
+ Full JQL: `project=$JIRA_PROJECT_KEY [AND sprint in openSprints()] [AND labels = "$CLANCY_LABEL_PLAN"] AND assignee=currentUser() AND status="$CLANCY_PLAN_STATUS" ORDER BY priority ASC`
278
+
279
+ ```bash
280
+ RESPONSE=$(curl -s \
281
+ -u "$JIRA_USER:$JIRA_API_TOKEN" \
282
+ -X POST \
283
+ -H "Content-Type: application/json" \
284
+ -H "Accept: application/json" \
285
+ "$JIRA_BASE_URL/rest/api/3/search/jql" \
286
+ -d '{"jql": "<jql as above>", "maxResults": <N>, "fields": ["summary", "description", "issuelinks", "parent", "customfield_10014", "comment"]}')
287
+ ```
288
+
289
+ Note: include the `comment` field so we can check for existing plans and read feedback.
290
+
291
+ #### GitHub Issues
292
+
293
+ First resolve the authenticated username (don't use `@me` — it breaks with fine-grained PATs):
294
+
295
+ ```bash
296
+ GITHUB_USERNAME=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" https://api.github.com/user | jq -r '.login')
297
+ ```
298
+
299
+ Then fetch issues:
300
+
301
+ ```bash
302
+ RESPONSE=$(curl -s \
303
+ -H "Authorization: Bearer $GITHUB_TOKEN" \
304
+ -H "X-GitHub-Api-Version: 2022-11-28" \
305
+ "https://api.github.com/repos/$GITHUB_REPO/issues?state=open&assignee=$GITHUB_USERNAME&labels=$CLANCY_LABEL_PLAN&per_page=<N>")
306
+ ```
307
+
308
+ - `CLANCY_LABEL_PLAN` is the pipeline label for the planning queue (default: `clancy:plan`). Falls back to `CLANCY_PLAN_LABEL` if `CLANCY_LABEL_PLAN` is not set. If neither is set, defaults to `needs-refinement`.
309
+ - Filter out PRs (entries with `pull_request` key)
310
+ - For each issue, fetch comments: `GET /repos/$GITHUB_REPO/issues/{number}/comments`
311
+
312
+ #### Linear
313
+
314
+ Build the filter using `CLANCY_PLAN_STATE_TYPE` (defaults to `backlog` if not set). If `CLANCY_LABEL_PLAN` is set (falls back to `CLANCY_PLAN_LABEL`), add a label filter to the query:
315
+
316
+ ```graphql
317
+ query {
318
+ viewer {
319
+ assignedIssues(
320
+ filter: {
321
+ state: { type: { eq: "$CLANCY_PLAN_STATE_TYPE" } }
322
+ team: { id: { eq: "$LINEAR_TEAM_ID" } }
323
+ labels: { name: { eq: "$CLANCY_LABEL_PLAN" } } # Only if CLANCY_LABEL_PLAN is set
324
+ }
325
+ first: $N
326
+ orderBy: priority
327
+ ) {
328
+ nodes {
329
+ id
330
+ identifier
331
+ title
332
+ description
333
+ parent {
334
+ identifier
335
+ title
336
+ }
337
+ comments {
338
+ nodes {
339
+ id
340
+ body
341
+ createdAt
342
+ user {
343
+ id
344
+ }
345
+ }
346
+ }
347
+ }
348
+ }
349
+ }
350
+ }
351
+ ```
352
+
353
+ #### Azure DevOps
354
+
355
+ Build a WIQL query using planning-specific env vars:
356
+
357
+ - `CLANCY_PLAN_STATUS` defaults to `New` if not set (Azure DevOps uses `New`, `Active`, `Resolved`, `Closed`, `Removed`)
358
+ - Tag clause: include `AND [System.Tags] CONTAINS '$CLANCY_LABEL_PLAN'` if `CLANCY_LABEL_PLAN` is set (falls back to `CLANCY_PLAN_LABEL`). If neither is set, defaults to `clancy:plan`.
359
+ - Assigned to: `AND [System.AssignedTo] = @Me`
360
+
361
+ Full WIQL: `SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = '$AZDO_PROJECT' AND [System.State] = '$CLANCY_PLAN_STATUS' [AND [System.Tags] CONTAINS '$CLANCY_LABEL_PLAN'] AND [System.AssignedTo] = @Me ORDER BY [Microsoft.VSTS.Common.Priority] ASC`
362
+
363
+ ```bash
364
+ # Step 1: Run WIQL query to get work item IDs
365
+ WIQL_RESPONSE=$(curl -s \
366
+ -u ":$AZDO_PAT" \
367
+ -X POST \
368
+ -H "Content-Type: application/json" \
369
+ "https://dev.azure.com/$AZDO_ORG/$AZDO_PROJECT/_apis/wit/wiql?api-version=7.1" \
370
+ -d '{"query": "<WIQL as above>"}')
371
+
372
+ # Step 2: Batch fetch work items (take first N IDs)
373
+ IDS=$(echo "$WIQL_RESPONSE" | jq -r '.workItems[0:N] | map(.id) | join(",")')
374
+
375
+ RESPONSE=$(curl -s \
376
+ -u ":$AZDO_PAT" \
377
+ -H "Accept: application/json" \
378
+ "https://dev.azure.com/$AZDO_ORG/$AZDO_PROJECT/_apis/wit/workitems?ids=$IDS&\$expand=relations&api-version=7.1")
379
+ ```
380
+
381
+ Note: Azure DevOps comments are NOT included in work item responses. For each work item, fetch comments separately:
382
+
383
+ ```bash
384
+ COMMENTS=$(curl -s \
385
+ -u ":$AZDO_PAT" \
386
+ -H "Accept: application/json" \
387
+ "https://dev.azure.com/$AZDO_ORG/$AZDO_PROJECT/_apis/wit/workitems/$ID/comments?api-version=7.1-preview.4")
388
+ ```
389
+
390
+ Map fields: title = `fields["System.Title"]`, description = `fields["System.Description"]` (HTML), parent = relation with `System.LinkTypes.Hierarchy-Reverse` rel type, tags = `fields["System.Tags"]` (semicolon-delimited string).
391
+
392
+ #### Shortcut
393
+
394
+ Search for stories in the planning workflow state. Shortcut uses workflow states (not labels) for queue filtering, but labels can further narrow results.
395
+
396
+ - `SHORTCUT_WORKFLOW` — optional workflow ID. If not set, use the default workflow.
397
+ - `CLANCY_LABEL_PLAN` — optional label name to filter stories (falls back to `CLANCY_PLAN_LABEL`).
398
+
399
+ ```bash
400
+ RESPONSE=$(curl -s \
401
+ -H "Shortcut-Token: $SHORTCUT_API_TOKEN" \
402
+ -H "Content-Type: application/json" \
403
+ -X POST \
404
+ "https://api.app.shortcut.com/api/v3/stories/search" \
405
+ -d '{"owner_ids": ["<current member UUID>"], "workflow_state_types": ["backlog"], "page_size": <N>}')
406
+ ```
407
+
408
+ To resolve the current member UUID:
409
+
410
+ ```bash
411
+ MEMBER=$(curl -s \
412
+ -H "Shortcut-Token: $SHORTCUT_API_TOKEN" \
413
+ "https://api.app.shortcut.com/api/v3/member-info")
414
+ ```
415
+
416
+ Use `MEMBER.id` as the owner filter. If `CLANCY_LABEL_PLAN` is set, add `"label_ids": [<label_id>]` to the search body (resolve label ID via `GET /api/v3/labels`).
417
+
418
+ For each story, fetch comments separately:
419
+
420
+ ```bash
421
+ COMMENTS=$(curl -s \
422
+ -H "Shortcut-Token: $SHORTCUT_API_TOKEN" \
423
+ "https://api.app.shortcut.com/api/v3/stories/$STORY_ID/comments")
424
+ ```
425
+
426
+ Map fields: title = `name`, description = `description` (markdown), parent = `epic_id` (resolve via `GET /api/v3/epics/$EPIC_ID`), labels = `labels[].name`.
427
+
428
+ #### Notion
429
+
430
+ Query the database with status and assignee filters:
431
+
432
+ - `CLANCY_NOTION_STATUS` — the status property name (configurable, defaults auto-detected from database schema)
433
+ - `CLANCY_PLAN_STATUS` — the status value to filter on (defaults to `Backlog` if not set)
434
+ - `CLANCY_NOTION_ASSIGNEE` — the assignee property name (configurable)
435
+ - `CLANCY_LABEL_PLAN` — optional label/tag to filter on (via multi-select property `CLANCY_NOTION_LABELS`)
436
+
437
+ ```bash
438
+ RESPONSE=$(curl -s \
439
+ -H "Authorization: Bearer $NOTION_TOKEN" \
440
+ -H "Notion-Version: 2022-06-28" \
441
+ -X POST \
442
+ "https://api.notion.com/v1/databases/$NOTION_DATABASE_ID/query" \
443
+ -d '{"filter": {"and": [{"property": "$CLANCY_NOTION_STATUS", "status": {"equals": "$CLANCY_PLAN_STATUS"}}, ...]}, "page_size": <N>}')
444
+ ```
445
+
446
+ Add assignee and label filters to the `and` array as needed. Notion filters use property-specific types (`status`, `people`, `multi_select`).
447
+
448
+ For each page, fetch comments separately:
449
+
450
+ ```bash
451
+ COMMENTS=$(curl -s \
452
+ -H "Authorization: Bearer $NOTION_TOKEN" \
453
+ -H "Notion-Version: 2022-06-28" \
454
+ "https://api.notion.com/v1/comments?block_id=$PAGE_ID")
455
+ ```
456
+
457
+ Map fields: title = title property value, description = fetch child blocks via `GET /v1/blocks/$PAGE_ID/children` (concatenate text), parent = `parent.page_id`, labels = multi-select property values.
458
+
459
+ **Notion limitation:** Comments use `rich_text` format, not markdown. When scanning for `## Clancy Implementation Plan`, search for the text content within `rich_text` arrays.
460
+
461
+ If the API call fails (non-200 response or network error):
462
+
463
+ ```
464
+ ❌ Board API error: {HTTP status or error message}
465
+
466
+ Check your credentials in .clancy/.env or run /clancy:doctor to diagnose.
467
+ ```
468
+
469
+ Stop.
470
+
471
+ If no tickets found:
472
+
473
+ ```
474
+ 🚨 Clancy — Plan
475
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
476
+
477
+ "Nothing to see here." — No backlog tickets to plan.
478
+ ```
479
+
480
+ Then display board-specific guidance:
481
+
482
+ - **GitHub:** `For GitHub: planning uses the "$CLANCY_LABEL_PLAN" label (default: clancy:plan, fallback: $CLANCY_PLAN_LABEL or needs-refinement). Apply that label to issues you want planned.`
483
+ - **Jira:** `Check that CLANCY_PLAN_STATUS (currently: "$CLANCY_PLAN_STATUS") matches a status in your Jira project, and that tickets in that status are assigned to you.`
484
+ - **Linear:** `Check that CLANCY_PLAN_STATE_TYPE (currently: "$CLANCY_PLAN_STATE_TYPE") is a valid Linear state type (backlog, unstarted, started, completed, canceled, triage), and that tickets in that state are assigned to you in team $LINEAR_TEAM_ID.`
485
+ - **Azure DevOps:** `Check that CLANCY_PLAN_STATUS (currently: "${CLANCY_PLAN_STATUS || 'New'}") matches a state in your Azure DevOps project, and that work items in that state are assigned to you. Tag "${CLANCY_LABEL_PLAN || 'clancy:plan'}" must be applied.`
486
+ - **Shortcut:** `Check that your stories are in a "backlog" workflow state and assigned to you. If using CLANCY_LABEL_PLAN, ensure the label exists and is applied to stories you want planned.`
487
+ - **Notion:** `Check that pages in database $NOTION_DATABASE_ID have status "${CLANCY_PLAN_STATUS || 'Backlog'}" and are assigned to you. If using CLANCY_LABEL_PLAN, ensure the multi-select label is applied.`
488
+
489
+ Stop.
490
+
491
+ ---
492
+
493
+ ## Step 3b — Check for existing plans
494
+
495
+ For each ticket, scan its comments for the marker `## Clancy Implementation Plan`. Then apply the following logic:
496
+
497
+ | Condition | Behaviour |
498
+ | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
499
+ | Has plan + feedback comments found after the plan | Revise: proceed to Step 3c to read feedback, then generate a revised plan |
500
+ | Has plan + `--fresh` flag | Discard existing plan, proceed to Step 4 (fresh plan from scratch) |
501
+ | Has plan + no feedback + no `--fresh` | Stop for this ticket: `Already planned. Comment on the ticket to provide feedback, then re-run /clancy:plan {KEY} to revise. Or use --fresh to start over.` |
502
+ | No plan found | Proceed to Step 4 |
503
+
504
+ Feedback detection: scan all comments posted AFTER the most recent `## Clancy Implementation Plan` comment. Exclude comments that are themselves Clancy-generated (contain `## Clancy Implementation Plan` or start with `Clancy skipped this ticket:`). All remaining comments are treated as feedback — regardless of author, since Clancy posts using the user's own credentials.
505
+
506
+ This is content-based filtering, not author-based. The user's own feedback comments must not be excluded.
507
+
508
+ ---
509
+
510
+ ## Step 3c — Read feedback comments
511
+
512
+ When revising a plan (auto-detected from feedback comments after the existing plan), read all comments posted AFTER the most recent `## Clancy Implementation Plan` comment.
513
+
514
+ Filter out Clancy-generated comments by content — exclude any comment that contains `## Clancy Implementation Plan` or starts with `Clancy skipped this ticket:`. These are Clancy's own outputs, not human feedback.
515
+
516
+ All other post-plan comments are treated as feedback regardless of author. This is critical because Clancy posts using the user's own credentials — author-based filtering would incorrectly exclude the user's own feedback.
517
+
518
+ No special syntax needed — users just comment normally on the ticket.
519
+
520
+ Pass this feedback to the plan generation step as additional context.
521
+
522
+ ---
523
+
524
+ ## Step 4 — For each ticket: Generate plan
525
+
526
+ Display the header:
527
+
528
+ ```
529
+ 🚨 Clancy — Plan
530
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
531
+
532
+ "Let me consult my crime files..." — Planning {N} ticket(s).
533
+ ```
534
+
535
+ For each ticket, display a progress line when starting:
536
+
537
+ ```
538
+ [{KEY}] {Title}
539
+ Exploring codebase...
540
+ ```
541
+
542
+ And when the plan is posted:
543
+
544
+ ```
545
+ ✅ Plan posted as comment.
546
+ ```
547
+
548
+ For multi-ticket runs, this provides visibility into progress. `Ctrl+C` to stop early — completed plans are already posted.
549
+
550
+ ### 4a. Quick feasibility scan
551
+
552
+ Before spending time exploring files, scan the ticket title and description for obvious non-codebase signals. Skip immediately if the ticket clearly requires work outside the codebase.
553
+
554
+ **Fail signals (skip immediately):**
555
+
556
+ - External platform references: "in Google Tag Manager", "in Salesforce", "in the AWS console", "in HubSpot", "in Jira admin"
557
+ - Human process steps: "get sign-off", "coordinate with", "schedule a meeting", "send an email to customers"
558
+ - Non-code deliverables: "write a runbook", "create a presentation", "update the wiki"
559
+ - Infrastructure ops: "rotate API keys in prod", "scale the fleet", "restart the service"
560
+
561
+ **STACK.md cross-reference:** If `.clancy/docs/STACK.md` exists, read it. If the ticket mentions a technology not listed in STACK.md, flag it as a concern (but do not skip — include a note in the plan's Risks section instead).
562
+
563
+ If infeasible:
564
+
565
+ ```
566
+ ⏭️ [{KEY}] {Title} — not a codebase change. Skipping.
567
+ → {reason, e.g. "Ticket describes work in Google Tag Manager, not in the codebase."}
568
+ ```
569
+
570
+ **Post skip comment to board:** Check `CLANCY_SKIP_COMMENTS` env var (default: `true`). If not `false`, post a brief comment on the ticket:
571
+
572
+ > Clancy skipped this ticket: {reason}
573
+ >
574
+ > This ticket appears to require work outside the codebase (e.g. {specific signal}). If this is incorrect, add more context to the ticket description and re-run `/clancy:plan`.
575
+
576
+ Use the same comment API patterns as Step 5 (plan posting). Best-effort — warn on failure, do not stop.
577
+
578
+ **Log SKIPPED entry:** Append to `.clancy/progress.txt`:
579
+
580
+ ```
581
+ YYYY-MM-DD HH:MM | {KEY} | SKIPPED | {reason}
582
+ ```
583
+
584
+ Continue to the next ticket. **Pass signals:** Anything mentioning code, components, features, bugs, UI, API, tests, refactoring, or lacking enough context to determine (benefit of the doubt).
585
+
586
+ ### 4b. Check for previous implementation (QA return detection)
587
+
588
+ Check `.clancy/progress.txt` for any previous entry matching this ticket key that ends with `| DONE` (search for `| {KEY} |` on a line ending with `| DONE`). If found, the ticket was previously implemented by Clancy and has returned (likely from QA).
589
+
590
+ If detected:
591
+
592
+ - Flag as "Previously implemented — returned from QA"
593
+ - Read QA/review comments from the board (same mechanism as feedback loop in Step 3c)
594
+ - Focus the plan on what likely went wrong and what needs fixing
595
+
596
+ If no progress entry exists: treat as fresh.
597
+
598
+ ### 4c. Read codebase context
599
+
600
+ If `.clancy/docs/` exists, read the following docs:
601
+
602
+ - `STACK.md`, `ARCHITECTURE.md`, `CONVENTIONS.md`, `TESTING.md`, `DESIGN-SYSTEM.md`, `ACCESSIBILITY.md`, `DEFINITION-OF-DONE.md`
603
+
604
+ These inform the plan's technical approach, affected files, and test plan.
605
+
606
+ ### 4d. Figma design context (if applicable)
607
+
608
+ If the ticket description contains a Figma URL and `FIGMA_API_KEY` is configured in `.clancy/.env`, fetch design context using Clancy's existing Figma MCP integration (3 MCP calls: metadata, design context, screenshot). This informs the acceptance criteria and affected components in the plan.
609
+
610
+ If Figma URL is present but `FIGMA_API_KEY` is not configured: note in the plan — "Figma URL present but API key not configured. Run /clancy:settings to add it."
611
+
612
+ ### 4e. Explore source files
613
+
614
+ Based on the ticket title AND description, explore the codebase to identify affected files.
615
+
616
+ **For S-sized tickets (simple/obvious scope):** Single-pass exploration — Glob and Read directly.
617
+
618
+ **For M/L-sized tickets (broad scope, multiple subsystems):** Spin up 2-3 parallel Explore subagents:
619
+
620
+ - **Agent 1:** Search for files matching ticket keywords, find existing implementations of similar features
621
+ - **Agent 2:** Identify related test files, check test patterns in affected areas
622
+ - **Agent 3:** (if UI ticket) Check component structure, design system usage, accessibility patterns
623
+
624
+ The size is estimated from the ticket title/description before exploration begins (rough heuristic). Subagents return their findings, which are merged into the plan.
625
+
626
+ ### 4f. Generate plan
627
+
628
+ Write the plan in this exact template:
629
+
630
+ ```markdown
631
+ ## Clancy Implementation Plan
632
+
633
+ **Ticket:** [{KEY}] {Title}
634
+ **Planned:** {YYYY-MM-DD}
635
+
636
+ ### Summary
637
+
638
+ {1-3 sentences: what this ticket asks for, why it matters, gaps filled}
639
+
640
+ ### Affected Files
641
+
642
+ | File | Change Type | Description |
643
+ | ----------------------- | ----------- | ------------------------- |
644
+ | `src/path/file.ts` | Modify | {What changes and why} |
645
+ | `src/path/new-file.ts` | Create | {What this new file does} |
646
+ | `src/path/file.test.ts` | Modify | {What changes and why} |
647
+
648
+ ### Implementation Approach
649
+
650
+ {2-4 sentences: implementation strategy, patterns, key decisions}
651
+
652
+ ### Test Strategy
653
+
654
+ - [ ] {Specific test to write or verify}
655
+ - [ ] {Specific test to write or verify}
656
+
657
+ ### Acceptance Criteria
658
+
659
+ - [ ] {Specific, testable criterion}
660
+ - [ ] {Specific, testable criterion}
661
+ - [ ] {Specific, testable criterion}
662
+
663
+ ### Dependencies
664
+
665
+ {Blockers, prerequisites, external deps. "None" if clean.}
666
+
667
+ ### Figma Link
668
+
669
+ {If a Figma URL was found in the ticket, include it here. Otherwise omit this section entirely.}
670
+
671
+ ### Risks / Considerations
672
+
673
+ - {Specific risk or consideration and handling}
674
+ - {Specific risk or consideration and handling}
675
+
676
+ ### Size Estimate
677
+
678
+ **{S / M / L}** — {Brief justification}
679
+
680
+ ---
681
+
682
+ _Generated by [Clancy](https://github.com/Pushedskydiver/chief-clancy). To request changes: comment on this ticket, then re-run `/clancy:plan` to revise. To start over: `/clancy:plan --fresh`. To approve: `/clancy:approve-plan {KEY}`._
683
+ ```
684
+
685
+ **If re-planning with feedback**, prepend a section before Summary:
686
+
687
+ ```markdown
688
+ ### Changes From Previous Plan
689
+
690
+ {What feedback was addressed and how the plan changed}
691
+ ```
692
+
693
+ **Quality rules:**
694
+
695
+ - Acceptance criteria must be testable ("user can X", "system does Y"), never vague
696
+ - Affected files must be real files found during exploration, not guesses
697
+ - Risks / Considerations must be specific to this ticket, not generic
698
+ - Size: S (< 1 hour, few files), M (1-4 hours, moderate), L (4+ hours, significant)
699
+ - If affected files > 15: add a note "Consider splitting this ticket"
700
+ - If UI ticket without Figma URL: note in plan
701
+ - If ticket mentions tech not in STACK.md: note in Risks / Considerations
702
+
703
+ **Dependency detection:**
704
+
705
+ | Type | Detection | Action |
706
+ | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
707
+ | Blocked by another ticket | Jira: issuelinks (type "Blocks"). GitHub: referenced issues. Linear: relations. Azure DevOps: relations (Dependency type). Shortcut: story_links (blocked type). Notion: relation properties. | List blocking tickets. Note "Complete {KEY} first." |
708
+ | Depends on external API | Mentioned in description or inferred from affected code | If API exists with docs: include integration approach. If API doesn't exist: mark as blocked. |
709
+ | Depends on unfinished design | UI ticket with no Figma URL or design reference | Note "Design dependency — no spec provided. Visual accuracy may vary." |
710
+ | Depends on library upgrade | Ticket mentions upgrading a dependency | Include upgrade as prerequisite step. Note potential breaking changes. |
711
+ | Depends on infra in the repo | DB migrations, docker-compose, CI config | Include in affected files and plan normally. |
712
+
713
+ ---
714
+
715
+ ## Step 5 — Post plan as comment
716
+
717
+ ### Jira — POST comment
718
+
719
+ ```bash
720
+ curl -s \
721
+ -u "$JIRA_USER:$JIRA_API_TOKEN" \
722
+ -X POST \
723
+ -H "Content-Type: application/json" \
724
+ -H "Accept: application/json" \
725
+ "$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY/comment" \
726
+ -d '<ADF JSON body>'
727
+ ```
728
+
729
+ Construct ADF (Atlassian Document Format) JSON for the comment body. Key mappings:
730
+
731
+ - `## Heading` → `heading` node (level 2)
732
+ - `### Heading` → `heading` node (level 3)
733
+ - `- bullet` → `bulletList > listItem > paragraph`
734
+ - `- [ ] checkbox` → `taskList > taskItem` (state: "TODO")
735
+ - `| table |` → `table > tableRow > tableCell`
736
+ - `**bold**` → marks: `[{ "type": "strong" }]`
737
+ - `` `code` `` → marks: `[{ "type": "code" }]`
738
+
739
+ If ADF construction is too complex for a particular element, fall back to wrapping that section in a code block (`codeBlock` node).
740
+
741
+ ### GitHub — POST comment
742
+
743
+ ```bash
744
+ curl -s \
745
+ -H "Authorization: Bearer $GITHUB_TOKEN" \
746
+ -H "X-GitHub-Api-Version: 2022-11-28" \
747
+ -X POST \
748
+ "https://api.github.com/repos/$GITHUB_REPO/issues/$ISSUE_NUMBER/comments" \
749
+ -d '{"body": "<markdown plan>"}'
750
+ ```
751
+
752
+ GitHub accepts Markdown directly — post the plan as-is.
753
+
754
+ ### Linear — commentCreate mutation
755
+
756
+ ```bash
757
+ curl -s \
758
+ -X POST \
759
+ -H "Content-Type: application/json" \
760
+ -H "Authorization: $LINEAR_API_KEY" \
761
+ "https://api.linear.app/graphql" \
762
+ -d '{"query": "mutation { commentCreate(input: { issueId: \"$ISSUE_ID\", body: \"<markdown plan>\" }) { success } }"}'
763
+ ```
764
+
765
+ Linear accepts Markdown directly.
766
+
767
+ ### Azure DevOps — POST comment
768
+
769
+ ```bash
770
+ curl -s \
771
+ -u ":$AZDO_PAT" \
772
+ -X POST \
773
+ -H "Content-Type: application/json" \
774
+ "https://dev.azure.com/$AZDO_ORG/$AZDO_PROJECT/_apis/wit/workitems/$WORK_ITEM_ID/comments?api-version=7.1-preview.4" \
775
+ -d '{"text": "<html plan>"}'
776
+ ```
777
+
778
+ Azure DevOps work item comments use **HTML**, not markdown. Convert the plan markdown to HTML:
779
+
780
+ - `## Heading` → `<h2>Heading</h2>`
781
+ - `### Heading` → `<h3>Heading</h3>`
782
+ - `- bullet` → `<ul><li>bullet</li></ul>`
783
+ - `- [ ] checkbox` → `<ul><li>☐ checkbox</li></ul>`
784
+ - `| table |` → `<table><tr><td>...</td></tr></table>`
785
+ - `**bold**` → `<strong>bold</strong>`
786
+ - `` `code` `` → `<code>code</code>`
787
+ - Newlines → `<br>` or `<p>` tags
788
+
789
+ If HTML construction is too complex for a particular element, wrap that section in `<pre>` tags as fallback.
790
+
791
+ ### Shortcut — POST comment
792
+
793
+ ```bash
794
+ curl -s \
795
+ -H "Shortcut-Token: $SHORTCUT_API_TOKEN" \
796
+ -H "Content-Type: application/json" \
797
+ -X POST \
798
+ "https://api.app.shortcut.com/api/v3/stories/$STORY_ID/comments" \
799
+ -d '{"text": "<markdown plan>"}'
800
+ ```
801
+
802
+ Shortcut accepts Markdown directly in comment text.
803
+
804
+ ### Notion — POST comment
805
+
806
+ ```bash
807
+ curl -s \
808
+ -H "Authorization: Bearer $NOTION_TOKEN" \
809
+ -H "Notion-Version: 2022-06-28" \
810
+ -X POST \
811
+ "https://api.notion.com/v1/comments" \
812
+ -d '{"parent": {"page_id": "$PAGE_ID"}, "rich_text": [{"type": "text", "text": {"content": "<plan text>"}}]}'
813
+ ```
814
+
815
+ **Notion limitation:** Comments use `rich_text` blocks, not markdown. For the plan content, use a single `text` block with the full plan as plain text. Notion will render it without markdown formatting. For better readability, consider splitting the plan into multiple `rich_text` blocks (one per section) with `annotations` for bold headings.
816
+
817
+ **Notion limitation:** The `rich_text` array has a **2000-character limit per text block**. If the plan exceeds 2000 characters, split it across multiple `rich_text` blocks within the same comment (each block up to 2000 chars). The total comment can contain many blocks.
818
+
819
+ **On failure:** Print the plan to stdout and warn — do not lose the plan. The user can manually paste it.
820
+
821
+ ```
822
+ ⚠️ Failed to post comment for [{KEY}]. Plan printed above — paste it manually.
823
+ ```
824
+
825
+ ---
826
+
827
+ ## Step 6 — Log
828
+
829
+ For each planned ticket, append to `.clancy/progress.txt` using the appropriate variant:
830
+
831
+ | Outcome | Log entry |
832
+ | ------------------------------- | ------------------------------------------------------ |
833
+ | Normal | `YYYY-MM-DD HH:MM \| {KEY} \| PLAN \| {S/M/L}` |
834
+ | Revised (re-plan with feedback) | `YYYY-MM-DD HH:MM \| {KEY} \| REVISED \| {S/M/L}` |
835
+ | Comment post failed | `YYYY-MM-DD HH:MM \| {KEY} \| POST_FAILED \| {reason}` |
836
+ | Skipped (infeasible) | `YYYY-MM-DD HH:MM \| {KEY} \| SKIPPED \| {reason}` |
837
+
838
+ ---
839
+
840
+ ## Step 7 — Summary
841
+
842
+ After all tickets are processed, display:
843
+
844
+ ```
845
+ Planned {N} ticket(s):
846
+
847
+ ✅ [{KEY1}] {Title} — M | 6 files | Comment posted
848
+ ✅ [{KEY2}] {Title} — S | 2 files | Comment posted
849
+ ⏭️ [{KEY3}] {Title} — already planned
850
+ ⏭️ [{KEY4}] {Title} — not a codebase change
851
+
852
+ Plans written to your board. After review, run /clancy:approve-plan {KEY} to promote.
853
+
854
+ "Let me dust this for prints..."
855
+ ```
856
+
857
+ ---
858
+
859
+ ## Notes
860
+
861
+ - This command does NOT implement anything — it generates plans only
862
+ - Plans are posted as comments, never overwriting the ticket description (that's `/clancy:approve-plan`)
863
+ - Re-running without `--fresh` auto-detects feedback: if feedback exists, revises; if no feedback, stops with guidance
864
+ - The `--fresh` flag discards the existing plan entirely and generates a new one from scratch
865
+ - The planning queue is separate from the implementation queue — they never compete for the same tickets
866
+ - All board API calls are best-effort — if a comment fails to post, print the plan to stdout as fallback
867
+ - When exploring the codebase, use Glob and Read for small tickets, parallel Explore subagents for larger ones
868
+ - The `## Clancy Implementation Plan` marker in comments is used by both `/clancy:plan` (to detect existing plans) and `/clancy:approve-plan` (to find the plan to promote)