chief-clancy 0.5.4 → 0.5.6
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/README.md +3 -3
- package/dist/bundle/clancy-once.js +54 -29
- package/dist/installer/install.js +1 -1
- package/dist/installer/install.js.map +1 -1
- package/dist/schemas/bitbucket-pr.d.ts +122 -0
- package/dist/schemas/bitbucket-pr.d.ts.map +1 -0
- package/dist/schemas/bitbucket-pr.js +69 -0
- package/dist/schemas/bitbucket-pr.js.map +1 -0
- package/dist/schemas/env.d.ts +12 -0
- package/dist/schemas/env.d.ts.map +1 -1
- package/dist/schemas/env.js +4 -0
- package/dist/schemas/env.js.map +1 -1
- package/dist/schemas/github.d.ts +60 -0
- package/dist/schemas/github.d.ts.map +1 -1
- package/dist/schemas/github.js +32 -0
- package/dist/schemas/github.js.map +1 -1
- package/dist/schemas/gitlab-mr.d.ts +62 -0
- package/dist/schemas/gitlab-mr.d.ts.map +1 -0
- package/dist/schemas/gitlab-mr.js +31 -0
- package/dist/schemas/gitlab-mr.js.map +1 -0
- package/dist/scripts/once/once.d.ts.map +1 -1
- package/dist/scripts/once/once.js +181 -23
- package/dist/scripts/once/once.js.map +1 -1
- package/dist/scripts/shared/git-ops/git-ops.d.ts +9 -0
- package/dist/scripts/shared/git-ops/git-ops.d.ts.map +1 -1
- package/dist/scripts/shared/git-ops/git-ops.js +20 -0
- package/dist/scripts/shared/git-ops/git-ops.js.map +1 -1
- package/dist/scripts/shared/progress/progress.d.ts +41 -0
- package/dist/scripts/shared/progress/progress.d.ts.map +1 -1
- package/dist/scripts/shared/progress/progress.js +96 -1
- package/dist/scripts/shared/progress/progress.js.map +1 -1
- package/dist/scripts/shared/prompt/prompt.d.ts +27 -0
- package/dist/scripts/shared/prompt/prompt.d.ts.map +1 -1
- package/dist/scripts/shared/prompt/prompt.js +32 -7
- package/dist/scripts/shared/prompt/prompt.js.map +1 -1
- package/dist/scripts/shared/pull-request/bitbucket/bitbucket.d.ts +41 -9
- package/dist/scripts/shared/pull-request/bitbucket/bitbucket.d.ts.map +1 -1
- package/dist/scripts/shared/pull-request/bitbucket/bitbucket.js +182 -0
- package/dist/scripts/shared/pull-request/bitbucket/bitbucket.js.map +1 -1
- package/dist/scripts/shared/pull-request/github/github.d.ts +34 -1
- package/dist/scripts/shared/pull-request/github/github.d.ts.map +1 -1
- package/dist/scripts/shared/pull-request/github/github.js +106 -2
- package/dist/scripts/shared/pull-request/github/github.js.map +1 -1
- package/dist/scripts/shared/pull-request/gitlab/gitlab.d.ts +32 -9
- package/dist/scripts/shared/pull-request/gitlab/gitlab.d.ts.map +1 -1
- package/dist/scripts/shared/pull-request/gitlab/gitlab.js +127 -0
- package/dist/scripts/shared/pull-request/gitlab/gitlab.js.map +1 -1
- package/dist/scripts/shared/pull-request/pr-body/pr-body.d.ts.map +1 -1
- package/dist/scripts/shared/pull-request/pr-body/pr-body.js +7 -0
- package/dist/scripts/shared/pull-request/pr-body/pr-body.js.map +1 -1
- package/dist/scripts/shared/pull-request/rework-comment/rework-comment.d.ts +23 -0
- package/dist/scripts/shared/pull-request/rework-comment/rework-comment.d.ts.map +1 -0
- package/dist/scripts/shared/pull-request/rework-comment/rework-comment.js +30 -0
- package/dist/scripts/shared/pull-request/rework-comment/rework-comment.js.map +1 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/remote.d.ts +9 -0
- package/dist/types/remote.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/roles/planner/commands/approve-plan.md +7 -0
- package/src/roles/planner/commands/plan.md +11 -1
- package/src/roles/planner/workflows/approve-plan.md +478 -0
- package/src/roles/planner/workflows/plan.md +171 -49
- package/src/roles/reviewer/workflows/logs.md +7 -3
- package/src/roles/setup/commands/help.md +3 -2
- package/src/roles/setup/workflows/init.md +68 -2
- package/src/roles/setup/workflows/scaffold.md +41 -0
- package/src/roles/setup/workflows/settings.md +41 -6
- package/src/templates/CLAUDE.md +2 -0
- package/src/roles/planner/commands/approve.md +0 -7
- package/src/roles/planner/workflows/approve.md +0 -237
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
# Clancy Approve Plan Workflow
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Promote an approved Clancy plan from a ticket comment to the ticket description. The plan is appended below the existing description, never replacing it. After promotion, the ticket is transitioned to the implementation queue.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Step 1 — Preflight checks
|
|
10
|
+
|
|
11
|
+
1. Check `.clancy/` exists and `.clancy/.env` is present. If not:
|
|
12
|
+
```
|
|
13
|
+
.clancy/ not found. Run /clancy:init to set up Clancy first.
|
|
14
|
+
```
|
|
15
|
+
Stop.
|
|
16
|
+
|
|
17
|
+
2. Source `.clancy/.env` and check board credentials are present.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Step 2 — Parse argument / Resolve ticket
|
|
22
|
+
|
|
23
|
+
### If no argument provided:
|
|
24
|
+
|
|
25
|
+
1. Scan `.clancy/progress.txt` for entries matching `| PLAN |` or `| REVISED |` that have no subsequent `| APPROVE |` for the same key.
|
|
26
|
+
2. Sort by timestamp ascending (oldest first).
|
|
27
|
+
3. If 0 found:
|
|
28
|
+
```
|
|
29
|
+
No planned tickets awaiting approval. Run /clancy:plan first.
|
|
30
|
+
```
|
|
31
|
+
Stop.
|
|
32
|
+
4. If 1+ found, auto-select the oldest. Show:
|
|
33
|
+
```
|
|
34
|
+
Auto-selected [{KEY}] {Title} (planned {date}). Promote this plan? [Y/n]
|
|
35
|
+
```
|
|
36
|
+
To resolve the title, fetch the ticket from the board:
|
|
37
|
+
- **GitHub:** `GET /repos/$GITHUB_REPO/issues/$ISSUE_NUMBER` → use `.title`
|
|
38
|
+
- **Jira:** `GET $JIRA_BASE_URL/rest/api/3/issue/$KEY?fields=summary` → use `.fields.summary`
|
|
39
|
+
- **Linear:** `issues(filter: { identifier: { eq: "$KEY" } }) { nodes { title } }` → use `nodes[0].title`
|
|
40
|
+
If fetching fails, show the key without a title: `Auto-selected [{KEY}] (planned {date}). Promote? [Y/n]`
|
|
41
|
+
5. If user declines:
|
|
42
|
+
```
|
|
43
|
+
Cancelled.
|
|
44
|
+
```
|
|
45
|
+
Stop.
|
|
46
|
+
6. Note that the user has already confirmed — set a flag to skip the Step 4 confirmation.
|
|
47
|
+
|
|
48
|
+
### If argument provided:
|
|
49
|
+
|
|
50
|
+
Validate the key format per board (case-insensitive):
|
|
51
|
+
- **GitHub:** `#\d+` or bare number
|
|
52
|
+
- **Jira:** `[A-Za-z][A-Za-z0-9]+-\d+` (e.g. `PROJ-123` or `proj-123`)
|
|
53
|
+
- **Linear:** `[A-Za-z]{1,10}-\d+` (e.g. `ENG-42` or `eng-42`)
|
|
54
|
+
|
|
55
|
+
If invalid format:
|
|
56
|
+
```
|
|
57
|
+
Invalid ticket key: {input}. Expected format: {board-specific example}.
|
|
58
|
+
```
|
|
59
|
+
Stop.
|
|
60
|
+
|
|
61
|
+
Proceed with that key.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Step 3 — Fetch the plan comment
|
|
66
|
+
|
|
67
|
+
Detect board from `.clancy/.env` and fetch comments for the specified ticket.
|
|
68
|
+
|
|
69
|
+
### Jira
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
RESPONSE=$(curl -s \
|
|
73
|
+
-u "$JIRA_USER:$JIRA_API_TOKEN" \
|
|
74
|
+
-H "Accept: application/json" \
|
|
75
|
+
"$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY/comment")
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Search for the most recent comment containing an ADF heading node with text `Clancy Implementation Plan`. **Capture the comment `id`** for later editing in Step 5b.
|
|
79
|
+
|
|
80
|
+
### GitHub
|
|
81
|
+
|
|
82
|
+
First, determine the issue number from the ticket key (strip the `#` prefix if present):
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
RESPONSE=$(curl -s \
|
|
86
|
+
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
|
87
|
+
-H "Accept: application/vnd.github+json" \
|
|
88
|
+
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
89
|
+
"https://api.github.com/repos/$GITHUB_REPO/issues/$ISSUE_NUMBER/comments?per_page=100")
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Search for the most recent comment body containing `## Clancy Implementation Plan`. **Capture the comment `id`** for later editing in Step 5b.
|
|
93
|
+
|
|
94
|
+
### Linear
|
|
95
|
+
|
|
96
|
+
Use the filter-based query (preferred over `issueSearch`):
|
|
97
|
+
|
|
98
|
+
```graphql
|
|
99
|
+
query {
|
|
100
|
+
issues(filter: { identifier: { eq: "$KEY" } }) {
|
|
101
|
+
nodes {
|
|
102
|
+
id identifier title description
|
|
103
|
+
comments {
|
|
104
|
+
nodes { id body createdAt user { id } }
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
If the filter-based query returns no results, fall back to `issueSearch`:
|
|
112
|
+
|
|
113
|
+
```graphql
|
|
114
|
+
query {
|
|
115
|
+
issueSearch(query: "$IDENTIFIER", first: 5) {
|
|
116
|
+
nodes {
|
|
117
|
+
id identifier title description
|
|
118
|
+
comments { nodes { id body createdAt user { id } } }
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Important:** `issueSearch` is a fuzzy text search. After fetching results, verify the returned issue's `identifier` field exactly matches the provided key (case-insensitive). If no exact match is found in the results, report: `Issue {KEY} not found. Check the identifier and try again.`
|
|
125
|
+
|
|
126
|
+
Search the comments for the most recent one containing `## Clancy Implementation Plan`. **Capture the comment `id`** and the existing comment `body` for later editing in Step 5b. Also capture the issue's internal `id` (UUID) for transitions in Step 6.
|
|
127
|
+
|
|
128
|
+
If no plan comment is found:
|
|
129
|
+
```
|
|
130
|
+
No Clancy plan found for {KEY}. Run /clancy:plan first.
|
|
131
|
+
```
|
|
132
|
+
Stop.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Step 3b — Check for existing plan in description
|
|
137
|
+
|
|
138
|
+
Before confirming, check if the ticket description already contains `## Clancy Implementation Plan`.
|
|
139
|
+
|
|
140
|
+
If it does:
|
|
141
|
+
```
|
|
142
|
+
This ticket's description already contains a Clancy plan.
|
|
143
|
+
Continuing will add a duplicate.
|
|
144
|
+
|
|
145
|
+
[1] Continue anyway
|
|
146
|
+
[2] Cancel
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
If the user picks [2], stop: `Cancelled. No changes made.`
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Step 4 — Confirm
|
|
154
|
+
|
|
155
|
+
**If the user already confirmed via auto-select in Step 2, SKIP this step entirely** (avoid double-confirmation).
|
|
156
|
+
|
|
157
|
+
Display a summary and ask for confirmation:
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
Clancy — Approve Plan
|
|
161
|
+
|
|
162
|
+
[{KEY}] {Title}
|
|
163
|
+
Size: {S/M/L} | {N} affected files
|
|
164
|
+
Planned: {date from plan}
|
|
165
|
+
|
|
166
|
+
Promote this plan to the ticket description? [Y/n]
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
If the user declines, stop:
|
|
170
|
+
```
|
|
171
|
+
Cancelled. No changes made.
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Step 5 — Update ticket description
|
|
177
|
+
|
|
178
|
+
Append the plan below the existing description with a separator. Never overwrite the original description.
|
|
179
|
+
|
|
180
|
+
The updated description follows this format:
|
|
181
|
+
```
|
|
182
|
+
{existing description}
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
{full plan content}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Jira — PUT issue
|
|
190
|
+
|
|
191
|
+
Fetch the current description first:
|
|
192
|
+
```bash
|
|
193
|
+
CURRENT=$(curl -s \
|
|
194
|
+
-u "$JIRA_USER:$JIRA_API_TOKEN" \
|
|
195
|
+
-H "Accept: application/json" \
|
|
196
|
+
"$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY?fields=description")
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Merge the existing ADF description with a `rule` node (horizontal rule) and the plan content as new ADF nodes. Then update:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
curl -s \
|
|
203
|
+
-u "$JIRA_USER:$JIRA_API_TOKEN" \
|
|
204
|
+
-X PUT \
|
|
205
|
+
-H "Content-Type: application/json" \
|
|
206
|
+
-H "Accept: application/json" \
|
|
207
|
+
"$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY" \
|
|
208
|
+
-d '{"fields": {"description": <merged ADF>}}'
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
If ADF construction fails for the plan content, wrap the plan in a `codeBlock` node as fallback.
|
|
212
|
+
|
|
213
|
+
### GitHub — PATCH issue
|
|
214
|
+
|
|
215
|
+
Fetch the current body:
|
|
216
|
+
```bash
|
|
217
|
+
CURRENT=$(curl -s \
|
|
218
|
+
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
|
219
|
+
-H "Accept: application/vnd.github+json" \
|
|
220
|
+
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
221
|
+
"https://api.github.com/repos/$GITHUB_REPO/issues/$ISSUE_NUMBER")
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Append the plan:
|
|
225
|
+
```bash
|
|
226
|
+
curl -s \
|
|
227
|
+
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
|
228
|
+
-H "Accept: application/vnd.github+json" \
|
|
229
|
+
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
230
|
+
-X PATCH \
|
|
231
|
+
"https://api.github.com/repos/$GITHUB_REPO/issues/$ISSUE_NUMBER" \
|
|
232
|
+
-d '{"body": "<existing body>\n\n---\n\n<plan>"}'
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Linear — issueUpdate mutation
|
|
236
|
+
|
|
237
|
+
Fetch the current description:
|
|
238
|
+
```graphql
|
|
239
|
+
query { issue(id: "$ISSUE_ID") { description } }
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Update with appended plan:
|
|
243
|
+
```graphql
|
|
244
|
+
mutation {
|
|
245
|
+
issueUpdate(
|
|
246
|
+
id: "$ISSUE_ID"
|
|
247
|
+
input: { description: "<existing>\n\n---\n\n<plan>" }
|
|
248
|
+
) { success }
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Step 5b — Edit plan comment (approval note)
|
|
255
|
+
|
|
256
|
+
After updating the description, edit the original plan comment to prepend an approval note. This is **best-effort** — warn on failure, continue.
|
|
257
|
+
|
|
258
|
+
### GitHub
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
curl -s \
|
|
262
|
+
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
|
263
|
+
-H "Accept: application/vnd.github+json" \
|
|
264
|
+
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
265
|
+
-H "Content-Type: application/json" \
|
|
266
|
+
-X PATCH \
|
|
267
|
+
"https://api.github.com/repos/$GITHUB_REPO/issues/comments/$COMMENT_ID" \
|
|
268
|
+
-d '{"body": "> **Plan approved and promoted to description** -- {YYYY-MM-DD}\n\n{existing_comment_body}"}'
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Jira
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
curl -s \
|
|
275
|
+
-u "$JIRA_USER:$JIRA_API_TOKEN" \
|
|
276
|
+
-X PUT \
|
|
277
|
+
-H "Content-Type: application/json" \
|
|
278
|
+
-H "Accept: application/json" \
|
|
279
|
+
"$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY/comment/$COMMENT_ID" \
|
|
280
|
+
-d '{
|
|
281
|
+
"body": {
|
|
282
|
+
"version": 1,
|
|
283
|
+
"type": "doc",
|
|
284
|
+
"content": [
|
|
285
|
+
{"type": "paragraph", "content": [
|
|
286
|
+
{"type": "text", "text": "Plan approved and promoted to description -- {YYYY-MM-DD}.",
|
|
287
|
+
"marks": [{"type": "strong"}]}
|
|
288
|
+
]},
|
|
289
|
+
<...existing ADF content nodes...>
|
|
290
|
+
]
|
|
291
|
+
}
|
|
292
|
+
}'
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Linear
|
|
296
|
+
|
|
297
|
+
```graphql
|
|
298
|
+
mutation {
|
|
299
|
+
commentUpdate(
|
|
300
|
+
id: "$COMMENT_ID"
|
|
301
|
+
input: {
|
|
302
|
+
body: "> **Plan approved and promoted to description** -- {YYYY-MM-DD}\n\n{existing_comment_body}"
|
|
303
|
+
}
|
|
304
|
+
) { success }
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
On failure for any platform:
|
|
309
|
+
```
|
|
310
|
+
Could not update plan comment. The plan is still promoted to the description.
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## Step 6 — Post-approval transition
|
|
316
|
+
|
|
317
|
+
Transition the ticket from the planning queue to the implementation queue. This is **best-effort** — warn on failure, continue.
|
|
318
|
+
|
|
319
|
+
### GitHub (always, not configurable)
|
|
320
|
+
|
|
321
|
+
1. **Remove planning label:**
|
|
322
|
+
```bash
|
|
323
|
+
curl -s \
|
|
324
|
+
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
|
325
|
+
-H "Accept: application/vnd.github+json" \
|
|
326
|
+
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
327
|
+
-X DELETE \
|
|
328
|
+
"https://api.github.com/repos/$GITHUB_REPO/issues/$ISSUE_NUMBER/labels/$CLANCY_PLAN_LABEL"
|
|
329
|
+
```
|
|
330
|
+
`CLANCY_PLAN_LABEL` defaults to `needs-refinement`. URL-encode the label name. Ignore 404 (label not on issue). If `CLANCY_PLAN_LABEL` is not set, use the default `needs-refinement`.
|
|
331
|
+
|
|
332
|
+
2. **Add implementation label** (only if `CLANCY_LABEL` is set — skip if unset):
|
|
333
|
+
```bash
|
|
334
|
+
curl -s \
|
|
335
|
+
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
|
336
|
+
-H "Accept: application/vnd.github+json" \
|
|
337
|
+
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
338
|
+
-H "Content-Type: application/json" \
|
|
339
|
+
-X POST \
|
|
340
|
+
"https://api.github.com/repos/$GITHUB_REPO/issues/$ISSUE_NUMBER/labels" \
|
|
341
|
+
-d '{"labels": ["$CLANCY_LABEL"]}'
|
|
342
|
+
```
|
|
343
|
+
If `CLANCY_LABEL` is not set, skip this step entirely (only the plan label removal is done). `CLANCY_LABEL` has no default — it is fully optional. When set (e.g. `clancy`), use its value. If the label does not exist on the repo, attempt to create it:
|
|
344
|
+
```bash
|
|
345
|
+
curl -s \
|
|
346
|
+
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
|
347
|
+
-H "Accept: application/vnd.github+json" \
|
|
348
|
+
-H "Content-Type: application/json" \
|
|
349
|
+
-X POST \
|
|
350
|
+
"https://api.github.com/repos/$GITHUB_REPO/labels" \
|
|
351
|
+
-d '{"name": "$CLANCY_LABEL", "color": "0075ca"}'
|
|
352
|
+
```
|
|
353
|
+
If 403 (no admin access) or 422 (invalid name): warn, continue without label.
|
|
354
|
+
|
|
355
|
+
### Jira (only if `CLANCY_STATUS_PLANNED` is set)
|
|
356
|
+
|
|
357
|
+
If `CLANCY_STATUS_PLANNED` is not set: skip transition, no warning needed.
|
|
358
|
+
|
|
359
|
+
If `CLANCY_STATUS_PLANNED` is set:
|
|
360
|
+
|
|
361
|
+
1. **Fetch transitions:**
|
|
362
|
+
```bash
|
|
363
|
+
curl -s \
|
|
364
|
+
-u "$JIRA_USER:$JIRA_API_TOKEN" \
|
|
365
|
+
-H "Accept: application/json" \
|
|
366
|
+
"$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY/transitions"
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
2. **Find matching transition:** Look for a transition where `.name` matches `CLANCY_STATUS_PLANNED` (case-insensitive). This matches the pattern used in the runtime Jira module.
|
|
370
|
+
|
|
371
|
+
3. **Execute transition:**
|
|
372
|
+
```bash
|
|
373
|
+
curl -s \
|
|
374
|
+
-u "$JIRA_USER:$JIRA_API_TOKEN" \
|
|
375
|
+
-X POST \
|
|
376
|
+
-H "Content-Type: application/json" \
|
|
377
|
+
"$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY/transitions" \
|
|
378
|
+
-d '{"transition": {"id": "$TRANSITION_ID"}}'
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
On failure:
|
|
382
|
+
```
|
|
383
|
+
Could not transition ticket. Move it manually to your implementation queue.
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### Linear (always)
|
|
387
|
+
|
|
388
|
+
1. **Resolve "unstarted" state:**
|
|
389
|
+
```graphql
|
|
390
|
+
query {
|
|
391
|
+
workflowStates(filter: {
|
|
392
|
+
team: { id: { eq: "$LINEAR_TEAM_ID" } }
|
|
393
|
+
type: { eq: "unstarted" }
|
|
394
|
+
}) {
|
|
395
|
+
nodes { id name }
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
Use `nodes[0].id` as the target state. If nodes is empty: warn `No 'unstarted' state found for team.` Skip transition.
|
|
400
|
+
|
|
401
|
+
2. **Transition:**
|
|
402
|
+
```graphql
|
|
403
|
+
mutation {
|
|
404
|
+
issueUpdate(
|
|
405
|
+
id: "$ISSUE_UUID"
|
|
406
|
+
input: { stateId: "$UNSTARTED_STATE_ID" }
|
|
407
|
+
) { success }
|
|
408
|
+
}
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
On failure:
|
|
412
|
+
```
|
|
413
|
+
Could not transition ticket. Move it manually to your implementation queue.
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
## Step 7 — Confirm and log
|
|
419
|
+
|
|
420
|
+
On success, display a board-specific message:
|
|
421
|
+
|
|
422
|
+
**GitHub (with CLANCY_LABEL set):**
|
|
423
|
+
```
|
|
424
|
+
Plan promoted. Label swapped: {plan_label} → {impl_label}. Ready for /clancy:once.
|
|
425
|
+
|
|
426
|
+
"Book 'em, Lou." — The ticket is ready for /clancy:once.
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
**GitHub (without CLANCY_LABEL):**
|
|
430
|
+
```
|
|
431
|
+
Plan promoted. Label removed: {plan_label}. Add your implementation label manually, or run /clancy:once.
|
|
432
|
+
|
|
433
|
+
"Book 'em, Lou."
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
**Jira (with transition):**
|
|
437
|
+
```
|
|
438
|
+
Plan promoted. Ticket transitioned to {CLANCY_STATUS_PLANNED}.
|
|
439
|
+
|
|
440
|
+
"Book 'em, Lou." -- The ticket is ready for /clancy:once.
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
**Jira (no transition configured):**
|
|
444
|
+
```
|
|
445
|
+
Plan promoted. Move [{KEY}] to your implementation queue for /clancy:once.
|
|
446
|
+
|
|
447
|
+
"Book 'em, Lou." -- The ticket is ready for /clancy:once.
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
**Linear:**
|
|
451
|
+
```
|
|
452
|
+
Plan promoted. Moved to unstarted. Ready for /clancy:once.
|
|
453
|
+
|
|
454
|
+
"Book 'em, Lou." -- The ticket is ready for /clancy:once.
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
Append to `.clancy/progress.txt`:
|
|
458
|
+
```
|
|
459
|
+
YYYY-MM-DD HH:MM | {KEY} | APPROVE | —
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
On failure:
|
|
463
|
+
```
|
|
464
|
+
Failed to update description for [{KEY}]. Check your board permissions.
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
---
|
|
468
|
+
|
|
469
|
+
## Notes
|
|
470
|
+
|
|
471
|
+
- This command only appends -- it never overwrites the existing ticket description
|
|
472
|
+
- If the ticket has multiple plan comments, the most recent one is used
|
|
473
|
+
- The plan content is taken verbatim from the comment -- no regeneration
|
|
474
|
+
- Step 3b checks for existing plans in the description to prevent accidental duplication
|
|
475
|
+
- The ticket key is case-insensitive -- accept `PROJ-123`, `proj-123`, or `#123` (GitHub)
|
|
476
|
+
- Step 5b edits the plan comment with an approval note -- this is best-effort and does not block the workflow
|
|
477
|
+
- Step 6 transitions the ticket to the implementation queue -- this is best-effort and board-specific
|
|
478
|
+
- 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)
|