chief-clancy 0.1.7 → 0.2.0-beta.2
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 +18 -7
- package/bin/install.js +61 -109
- package/hooks/clancy-check-update.js +71 -0
- package/hooks/clancy-context-monitor.js +101 -0
- package/hooks/clancy-statusline.js +82 -0
- package/package.json +2 -1
- package/src/commands/dry-run.md +14 -0
- package/src/commands/help.md +2 -1
- package/src/commands/once.md +4 -0
- package/src/templates/.env.example.jira +6 -0
- package/src/templates/.env.example.linear +6 -0
- package/src/templates/CLAUDE.md +12 -5
- package/src/templates/scripts/clancy-once-github.sh +27 -3
- package/src/templates/scripts/clancy-once-linear.sh +71 -3
- package/src/templates/scripts/clancy-once.sh +66 -3
- package/src/workflows/init.md +1 -1
- package/src/workflows/once.md +16 -0
- package/src/workflows/scaffold.md +176 -9
- package/src/workflows/settings.md +73 -42
- package/src/workflows/uninstall.md +26 -2
- package/src/workflows/update.md +0 -28
|
@@ -3,6 +3,15 @@
|
|
|
3
3
|
# This means any command that fails will stop the script immediately rather than silently continuing.
|
|
4
4
|
set -euo pipefail
|
|
5
5
|
|
|
6
|
+
# Parse flags — must happen before preflight so --dry-run works without side effects.
|
|
7
|
+
DRY_RUN=false
|
|
8
|
+
for arg in "$@"; do
|
|
9
|
+
case "$arg" in
|
|
10
|
+
--dry-run) DRY_RUN=true ;;
|
|
11
|
+
esac
|
|
12
|
+
done
|
|
13
|
+
readonly DRY_RUN
|
|
14
|
+
|
|
6
15
|
# ─── WHAT THIS SCRIPT DOES ─────────────────────────────────────────────────────
|
|
7
16
|
#
|
|
8
17
|
# Board: GitHub Issues
|
|
@@ -136,17 +145,31 @@ TICKET_BRANCH="feature/issue-${ISSUE_NUMBER}"
|
|
|
136
145
|
if [ "$MILESTONE" != "none" ]; then
|
|
137
146
|
MILESTONE_SLUG=$(echo "$MILESTONE" | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | tr -cd '[:alnum:]-')
|
|
138
147
|
TARGET_BRANCH="milestone/${MILESTONE_SLUG}"
|
|
139
|
-
git show-ref --verify --quiet "refs/heads/$TARGET_BRANCH" \
|
|
140
|
-
|| git checkout -b "$TARGET_BRANCH" "$BASE_BRANCH"
|
|
141
148
|
else
|
|
142
149
|
TARGET_BRANCH="$BASE_BRANCH"
|
|
143
150
|
fi
|
|
144
151
|
|
|
152
|
+
# ─── DRY RUN ───────────────────────────────────────────────────────────────────
|
|
153
|
+
|
|
154
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
155
|
+
echo ""
|
|
156
|
+
echo "── Dry run ──────────────────────────────────────"
|
|
157
|
+
echo " Issue: [#${ISSUE_NUMBER}] $TITLE"
|
|
158
|
+
echo " Milestone: $MILESTONE"
|
|
159
|
+
echo " Target branch: $TARGET_BRANCH"
|
|
160
|
+
echo " Feature branch: $TICKET_BRANCH"
|
|
161
|
+
echo "─────────────────────────────────────────────────"
|
|
162
|
+
echo " No changes made. Remove --dry-run to run for real."
|
|
163
|
+
exit 0
|
|
164
|
+
fi
|
|
165
|
+
|
|
145
166
|
# ─── IMPLEMENT ─────────────────────────────────────────────────────────────────
|
|
146
167
|
|
|
147
168
|
echo "Picking up: [#${ISSUE_NUMBER}] $TITLE"
|
|
148
169
|
echo "Milestone: $MILESTONE | Target branch: $TARGET_BRANCH"
|
|
149
170
|
|
|
171
|
+
git show-ref --verify --quiet "refs/heads/$TARGET_BRANCH" \
|
|
172
|
+
|| git checkout -b "$TARGET_BRANCH" "$BASE_BRANCH"
|
|
150
173
|
git checkout "$TARGET_BRANCH"
|
|
151
174
|
# -B creates the branch if it doesn't exist, or resets it to HEAD if it does.
|
|
152
175
|
# This handles retries cleanly without failing on an already-existing branch.
|
|
@@ -172,7 +195,8 @@ If you must SKIP this issue:
|
|
|
172
195
|
4. Stop — no branches, no file changes, no git operations.
|
|
173
196
|
|
|
174
197
|
If the issue IS implementable, continue:
|
|
175
|
-
1. Read
|
|
198
|
+
1. Read core docs in .clancy/docs/: STACK.md, ARCHITECTURE.md, CONVENTIONS.md, GIT.md, DEFINITION-OF-DONE.md, CONCERNS.md
|
|
199
|
+
Also read if relevant to this ticket: INTEGRATIONS.md (external APIs/services/auth), TESTING.md (tests/specs/coverage), DESIGN-SYSTEM.md (UI/components/styles), ACCESSIBILITY.md (accessibility/ARIA/WCAG)
|
|
176
200
|
2. Follow the conventions in GIT.md exactly
|
|
177
201
|
3. Implement the issue fully
|
|
178
202
|
4. Commit your work following the conventions in GIT.md
|
|
@@ -3,6 +3,15 @@
|
|
|
3
3
|
# This means any command that fails will stop the script immediately rather than silently continuing.
|
|
4
4
|
set -euo pipefail
|
|
5
5
|
|
|
6
|
+
# Parse flags — must happen before preflight so --dry-run works without side effects.
|
|
7
|
+
DRY_RUN=false
|
|
8
|
+
for arg in "$@"; do
|
|
9
|
+
case "$arg" in
|
|
10
|
+
--dry-run) DRY_RUN=true ;;
|
|
11
|
+
esac
|
|
12
|
+
done
|
|
13
|
+
readonly DRY_RUN
|
|
14
|
+
|
|
6
15
|
# ─── WHAT THIS SCRIPT DOES ─────────────────────────────────────────────────────
|
|
7
16
|
#
|
|
8
17
|
# Board: Linear
|
|
@@ -153,6 +162,7 @@ if [ "$NODE_COUNT" -eq 0 ]; then
|
|
|
153
162
|
exit 0
|
|
154
163
|
fi
|
|
155
164
|
|
|
165
|
+
ISSUE_ID=$(echo "$RESPONSE" | jq -r '.data.viewer.assignedIssues.nodes[0].id')
|
|
156
166
|
IDENTIFIER=$(echo "$RESPONSE" | jq -r '.data.viewer.assignedIssues.nodes[0].identifier')
|
|
157
167
|
TITLE=$(echo "$RESPONSE" | jq -r '.data.viewer.assignedIssues.nodes[0].title')
|
|
158
168
|
DESCRIPTION=$(echo "$RESPONSE" | jq -r '.data.viewer.assignedIssues.nodes[0].description // "No description"')
|
|
@@ -172,22 +182,58 @@ TICKET_BRANCH="feature/$(echo "$IDENTIFIER" | tr '[:upper:]' '[:lower:]')"
|
|
|
172
182
|
# BASE_BRANCH if it doesn't exist yet). Otherwise branch from BASE_BRANCH directly.
|
|
173
183
|
if [ "$PARENT_ID" != "none" ]; then
|
|
174
184
|
TARGET_BRANCH="epic/$(echo "$PARENT_ID" | tr '[:upper:]' '[:lower:]')"
|
|
175
|
-
git show-ref --verify --quiet "refs/heads/$TARGET_BRANCH" \
|
|
176
|
-
|| git checkout -b "$TARGET_BRANCH" "$BASE_BRANCH"
|
|
177
185
|
else
|
|
178
186
|
TARGET_BRANCH="$BASE_BRANCH"
|
|
179
187
|
fi
|
|
180
188
|
|
|
189
|
+
# ─── DRY RUN ───────────────────────────────────────────────────────────────────
|
|
190
|
+
|
|
191
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
192
|
+
echo ""
|
|
193
|
+
echo "── Dry run ──────────────────────────────────────"
|
|
194
|
+
echo " Issue: [$IDENTIFIER] $TITLE"
|
|
195
|
+
echo " Epic: $EPIC_INFO"
|
|
196
|
+
echo " Target branch: $TARGET_BRANCH"
|
|
197
|
+
echo " Feature branch: $TICKET_BRANCH"
|
|
198
|
+
echo "─────────────────────────────────────────────────"
|
|
199
|
+
echo " No changes made. Remove --dry-run to run for real."
|
|
200
|
+
exit 0
|
|
201
|
+
fi
|
|
202
|
+
|
|
181
203
|
# ─── IMPLEMENT ─────────────────────────────────────────────────────────────────
|
|
182
204
|
|
|
183
205
|
echo "Picking up: [$IDENTIFIER] $TITLE"
|
|
184
206
|
echo "Epic: $EPIC_INFO | Target branch: $TARGET_BRANCH"
|
|
185
207
|
|
|
208
|
+
git show-ref --verify --quiet "refs/heads/$TARGET_BRANCH" \
|
|
209
|
+
|| git checkout -b "$TARGET_BRANCH" "$BASE_BRANCH"
|
|
186
210
|
git checkout "$TARGET_BRANCH"
|
|
187
211
|
# -B creates the branch if it doesn't exist, or resets it to HEAD if it does.
|
|
188
212
|
# This handles retries cleanly without failing on an already-existing branch.
|
|
189
213
|
git checkout -B "$TICKET_BRANCH"
|
|
190
214
|
|
|
215
|
+
# Transition issue to In Progress (best-effort — never fails the run).
|
|
216
|
+
# Queries team workflow states by type "started", picks the first match.
|
|
217
|
+
if [ -n "${CLANCY_STATUS_IN_PROGRESS:-}" ]; then
|
|
218
|
+
STATE_RESP=$(curl -s -X POST https://api.linear.app/graphql \
|
|
219
|
+
-H "Content-Type: application/json" \
|
|
220
|
+
-H "Authorization: $LINEAR_API_KEY" \
|
|
221
|
+
-d "$(jq -n --arg teamId "$LINEAR_TEAM_ID" --arg name "$CLANCY_STATUS_IN_PROGRESS" \
|
|
222
|
+
'{"query": "query($teamId: String!, $name: String!) { workflowStates(filter: { team: { id: { eq: $teamId } } name: { eq: $name } }) { nodes { id } } }", "variables": {"teamId": $teamId, "name": $name}}')")
|
|
223
|
+
IN_PROGRESS_STATE_ID=$(echo "$STATE_RESP" | jq -r '.data.workflowStates.nodes[0].id // empty')
|
|
224
|
+
if [ -n "$IN_PROGRESS_STATE_ID" ]; then
|
|
225
|
+
curl -s -X POST https://api.linear.app/graphql \
|
|
226
|
+
-H "Content-Type: application/json" \
|
|
227
|
+
-H "Authorization: $LINEAR_API_KEY" \
|
|
228
|
+
-d "$(jq -n --arg issueId "$ISSUE_ID" --arg stateId "$IN_PROGRESS_STATE_ID" \
|
|
229
|
+
'{"query": "mutation($issueId: String!, $stateId: String!) { issueUpdate(id: $issueId, input: { stateId: $stateId }) { success } }", "variables": {"issueId": $issueId, "stateId": $stateId}}')" \
|
|
230
|
+
>/dev/null 2>&1 || true
|
|
231
|
+
echo " → Transitioned to $CLANCY_STATUS_IN_PROGRESS"
|
|
232
|
+
else
|
|
233
|
+
echo " ⚠ Workflow state '$CLANCY_STATUS_IN_PROGRESS' not found — check CLANCY_STATUS_IN_PROGRESS in .clancy/.env."
|
|
234
|
+
fi
|
|
235
|
+
fi
|
|
236
|
+
|
|
191
237
|
PROMPT="You are implementing Linear issue $IDENTIFIER.
|
|
192
238
|
|
|
193
239
|
Title: $TITLE
|
|
@@ -208,7 +254,8 @@ If you must SKIP this issue:
|
|
|
208
254
|
4. Stop — no branches, no file changes, no git operations.
|
|
209
255
|
|
|
210
256
|
If the issue IS implementable, continue:
|
|
211
|
-
1. Read
|
|
257
|
+
1. Read core docs in .clancy/docs/: STACK.md, ARCHITECTURE.md, CONVENTIONS.md, GIT.md, DEFINITION-OF-DONE.md, CONCERNS.md
|
|
258
|
+
Also read if relevant to this ticket: INTEGRATIONS.md (external APIs/services/auth), TESTING.md (tests/specs/coverage), DESIGN-SYSTEM.md (UI/components/styles), ACCESSIBILITY.md (accessibility/ARIA/WCAG)
|
|
212
259
|
2. Follow the conventions in GIT.md exactly
|
|
213
260
|
3. Implement the issue fully
|
|
214
261
|
4. Commit your work following the conventions in GIT.md
|
|
@@ -232,6 +279,27 @@ fi
|
|
|
232
279
|
# Delete ticket branch locally
|
|
233
280
|
git branch -d "$TICKET_BRANCH"
|
|
234
281
|
|
|
282
|
+
# Transition issue to Done (best-effort — never fails the run).
|
|
283
|
+
if [ -n "${CLANCY_STATUS_DONE:-}" ]; then
|
|
284
|
+
STATE_RESP=$(curl -s -X POST https://api.linear.app/graphql \
|
|
285
|
+
-H "Content-Type: application/json" \
|
|
286
|
+
-H "Authorization: $LINEAR_API_KEY" \
|
|
287
|
+
-d "$(jq -n --arg teamId "$LINEAR_TEAM_ID" --arg name "$CLANCY_STATUS_DONE" \
|
|
288
|
+
'{"query": "query($teamId: String!, $name: String!) { workflowStates(filter: { team: { id: { eq: $teamId } } name: { eq: $name } }) { nodes { id } } }", "variables": {"teamId": $teamId, "name": $name}}')")
|
|
289
|
+
DONE_STATE_ID=$(echo "$STATE_RESP" | jq -r '.data.workflowStates.nodes[0].id // empty')
|
|
290
|
+
if [ -n "$DONE_STATE_ID" ]; then
|
|
291
|
+
curl -s -X POST https://api.linear.app/graphql \
|
|
292
|
+
-H "Content-Type: application/json" \
|
|
293
|
+
-H "Authorization: $LINEAR_API_KEY" \
|
|
294
|
+
-d "$(jq -n --arg issueId "$ISSUE_ID" --arg stateId "$DONE_STATE_ID" \
|
|
295
|
+
'{"query": "mutation($issueId: String!, $stateId: String!) { issueUpdate(id: $issueId, input: { stateId: $stateId }) { success } }", "variables": {"issueId": $issueId, "stateId": $stateId}}')" \
|
|
296
|
+
>/dev/null 2>&1 || true
|
|
297
|
+
echo " → Transitioned to $CLANCY_STATUS_DONE"
|
|
298
|
+
else
|
|
299
|
+
echo " ⚠ Workflow state '$CLANCY_STATUS_DONE' not found — check CLANCY_STATUS_DONE in .clancy/.env."
|
|
300
|
+
fi
|
|
301
|
+
fi
|
|
302
|
+
|
|
235
303
|
# Log progress
|
|
236
304
|
echo "$(date '+%Y-%m-%d %H:%M') | $IDENTIFIER | $TITLE | DONE" >> .clancy/progress.txt
|
|
237
305
|
|
|
@@ -3,6 +3,15 @@
|
|
|
3
3
|
# This means any command that fails will stop the script immediately rather than silently continuing.
|
|
4
4
|
set -euo pipefail
|
|
5
5
|
|
|
6
|
+
# Parse flags — must happen before preflight so --dry-run works without side effects.
|
|
7
|
+
DRY_RUN=false
|
|
8
|
+
for arg in "$@"; do
|
|
9
|
+
case "$arg" in
|
|
10
|
+
--dry-run) DRY_RUN=true ;;
|
|
11
|
+
esac
|
|
12
|
+
done
|
|
13
|
+
readonly DRY_RUN
|
|
14
|
+
|
|
6
15
|
# ─── WHAT THIS SCRIPT DOES ─────────────────────────────────────────────────────
|
|
7
16
|
#
|
|
8
17
|
# Board: Jira
|
|
@@ -178,22 +187,56 @@ TICKET_BRANCH="feature/$(echo "$TICKET_KEY" | tr '[:upper:]' '[:lower:]')"
|
|
|
178
187
|
# BASE_BRANCH if it doesn't exist yet). Otherwise branch from BASE_BRANCH directly.
|
|
179
188
|
if [ "$EPIC_INFO" != "none" ]; then
|
|
180
189
|
TARGET_BRANCH="epic/$(echo "$EPIC_INFO" | tr '[:upper:]' '[:lower:]')"
|
|
181
|
-
git show-ref --verify --quiet "refs/heads/$TARGET_BRANCH" \
|
|
182
|
-
|| git checkout -b "$TARGET_BRANCH" "$BASE_BRANCH"
|
|
183
190
|
else
|
|
184
191
|
TARGET_BRANCH="$BASE_BRANCH"
|
|
185
192
|
fi
|
|
186
193
|
|
|
194
|
+
# ─── DRY RUN ───────────────────────────────────────────────────────────────────
|
|
195
|
+
|
|
196
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
197
|
+
echo ""
|
|
198
|
+
echo "── Dry run ──────────────────────────────────────"
|
|
199
|
+
echo " Ticket: [$TICKET_KEY] $SUMMARY"
|
|
200
|
+
echo " Epic: $EPIC_INFO"
|
|
201
|
+
echo " Blockers: $BLOCKERS"
|
|
202
|
+
echo " Target branch: $TARGET_BRANCH"
|
|
203
|
+
echo " Feature branch: $TICKET_BRANCH"
|
|
204
|
+
echo "─────────────────────────────────────────────────"
|
|
205
|
+
echo " No changes made. Remove --dry-run to run for real."
|
|
206
|
+
exit 0
|
|
207
|
+
fi
|
|
208
|
+
|
|
187
209
|
# ─── IMPLEMENT ─────────────────────────────────────────────────────────────────
|
|
188
210
|
|
|
189
211
|
echo "Picking up: [$TICKET_KEY] $SUMMARY"
|
|
190
212
|
echo "Epic: $EPIC_INFO | Target branch: $TARGET_BRANCH | Blockers: $BLOCKERS"
|
|
191
213
|
|
|
214
|
+
git show-ref --verify --quiet "refs/heads/$TARGET_BRANCH" \
|
|
215
|
+
|| git checkout -b "$TARGET_BRANCH" "$BASE_BRANCH"
|
|
192
216
|
git checkout "$TARGET_BRANCH"
|
|
193
217
|
# -B creates the branch if it doesn't exist, or resets it to HEAD if it does.
|
|
194
218
|
# This handles retries cleanly without failing on an already-existing branch.
|
|
195
219
|
git checkout -B "$TICKET_BRANCH"
|
|
196
220
|
|
|
221
|
+
# Transition ticket to In Progress (best-effort — never fails the run)
|
|
222
|
+
if [ -n "${CLANCY_STATUS_IN_PROGRESS:-}" ]; then
|
|
223
|
+
TRANSITIONS=$(curl -s \
|
|
224
|
+
-u "$JIRA_USER:$JIRA_API_TOKEN" \
|
|
225
|
+
-H "Accept: application/json" \
|
|
226
|
+
"$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY/transitions")
|
|
227
|
+
IN_PROGRESS_ID=$(echo "$TRANSITIONS" | jq -r \
|
|
228
|
+
--arg name "$CLANCY_STATUS_IN_PROGRESS" \
|
|
229
|
+
'.transitions[] | select(.name == $name) | .id' | head -1)
|
|
230
|
+
if [ -n "$IN_PROGRESS_ID" ]; then
|
|
231
|
+
curl -s -X POST \
|
|
232
|
+
-u "$JIRA_USER:$JIRA_API_TOKEN" \
|
|
233
|
+
-H "Content-Type: application/json" \
|
|
234
|
+
"$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY/transitions" \
|
|
235
|
+
-d "$(jq -n --arg id "$IN_PROGRESS_ID" '{"transition":{"id":$id}}')" >/dev/null 2>&1 || true
|
|
236
|
+
echo " → Transitioned to $CLANCY_STATUS_IN_PROGRESS"
|
|
237
|
+
fi
|
|
238
|
+
fi
|
|
239
|
+
|
|
197
240
|
PROMPT="You are implementing Jira ticket $TICKET_KEY.
|
|
198
241
|
|
|
199
242
|
Summary: $SUMMARY
|
|
@@ -215,7 +258,8 @@ If you must SKIP this ticket:
|
|
|
215
258
|
4. Stop — no branches, no file changes, no git operations.
|
|
216
259
|
|
|
217
260
|
If the ticket IS implementable, continue:
|
|
218
|
-
1. Read
|
|
261
|
+
1. Read core docs in .clancy/docs/: STACK.md, ARCHITECTURE.md, CONVENTIONS.md, GIT.md, DEFINITION-OF-DONE.md, CONCERNS.md
|
|
262
|
+
Also read if relevant to this ticket: INTEGRATIONS.md (external APIs/services/auth), TESTING.md (tests/specs/coverage), DESIGN-SYSTEM.md (UI/components/styles), ACCESSIBILITY.md (accessibility/ARIA/WCAG)
|
|
219
263
|
2. Follow the conventions in GIT.md exactly
|
|
220
264
|
3. Implement the ticket fully
|
|
221
265
|
4. Commit your work following the conventions in GIT.md
|
|
@@ -239,6 +283,25 @@ fi
|
|
|
239
283
|
# Delete ticket branch locally (never push deletes)
|
|
240
284
|
git branch -d "$TICKET_BRANCH"
|
|
241
285
|
|
|
286
|
+
# Transition ticket to Done (best-effort — never fails the run)
|
|
287
|
+
if [ -n "${CLANCY_STATUS_DONE:-}" ]; then
|
|
288
|
+
TRANSITIONS=$(curl -s \
|
|
289
|
+
-u "$JIRA_USER:$JIRA_API_TOKEN" \
|
|
290
|
+
-H "Accept: application/json" \
|
|
291
|
+
"$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY/transitions")
|
|
292
|
+
DONE_ID=$(echo "$TRANSITIONS" | jq -r \
|
|
293
|
+
--arg name "$CLANCY_STATUS_DONE" \
|
|
294
|
+
'.transitions[] | select(.name == $name) | .id' | head -1)
|
|
295
|
+
if [ -n "$DONE_ID" ]; then
|
|
296
|
+
curl -s -X POST \
|
|
297
|
+
-u "$JIRA_USER:$JIRA_API_TOKEN" \
|
|
298
|
+
-H "Content-Type: application/json" \
|
|
299
|
+
"$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY/transitions" \
|
|
300
|
+
-d "$(jq -n --arg id "$DONE_ID" '{"transition":{"id":$id}}')" >/dev/null 2>&1 || true
|
|
301
|
+
echo " → Transitioned to $CLANCY_STATUS_DONE"
|
|
302
|
+
fi
|
|
303
|
+
fi
|
|
304
|
+
|
|
242
305
|
# Log progress
|
|
243
306
|
echo "$(date '+%Y-%m-%d %H:%M') | $TICKET_KEY | $SUMMARY | DONE" >> .clancy/progress.txt
|
|
244
307
|
|
package/src/workflows/init.md
CHANGED
|
@@ -420,4 +420,4 @@ Clancy is ready.
|
|
|
420
420
|
- Config: `.clancy/.env`
|
|
421
421
|
- CLAUDE.md: updated
|
|
422
422
|
|
|
423
|
-
Run `/clancy:
|
|
423
|
+
Run `/clancy:dry-run` to preview the first ticket without making changes, `/clancy:once` to pick it up, or `/clancy:run` to process the full queue.
|
package/src/workflows/once.md
CHANGED
|
@@ -45,6 +45,10 @@ Pick up exactly one ticket from the Kanban board, implement it, commit, squash-m
|
|
|
45
45
|
|
|
46
46
|
## Step 2 — Run
|
|
47
47
|
|
|
48
|
+
Check if the user passed `--dry-run` as an argument to the slash command.
|
|
49
|
+
|
|
50
|
+
**Without `--dry-run`:**
|
|
51
|
+
|
|
48
52
|
Display:
|
|
49
53
|
```
|
|
50
54
|
Running Clancy for one ticket.
|
|
@@ -55,6 +59,18 @@ Execute:
|
|
|
55
59
|
bash .clancy/clancy-once.sh
|
|
56
60
|
```
|
|
57
61
|
|
|
62
|
+
**With `--dry-run`:**
|
|
63
|
+
|
|
64
|
+
Display:
|
|
65
|
+
```
|
|
66
|
+
Running Clancy in dry-run mode — no changes will be made.
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Execute:
|
|
70
|
+
```bash
|
|
71
|
+
bash .clancy/clancy-once.sh --dry-run
|
|
72
|
+
```
|
|
73
|
+
|
|
58
74
|
Stream output directly — do not buffer or summarise.
|
|
59
75
|
|
|
60
76
|
---
|
|
@@ -304,6 +304,15 @@ Write this file when the chosen board is **Jira**:
|
|
|
304
304
|
# This means any command that fails will stop the script immediately rather than silently continuing.
|
|
305
305
|
set -euo pipefail
|
|
306
306
|
|
|
307
|
+
# Parse flags — must happen before preflight so --dry-run works without side effects.
|
|
308
|
+
DRY_RUN=false
|
|
309
|
+
for arg in "$@"; do
|
|
310
|
+
case "$arg" in
|
|
311
|
+
--dry-run) DRY_RUN=true ;;
|
|
312
|
+
esac
|
|
313
|
+
done
|
|
314
|
+
readonly DRY_RUN
|
|
315
|
+
|
|
307
316
|
# ─── WHAT THIS SCRIPT DOES ─────────────────────────────────────────────────────
|
|
308
317
|
#
|
|
309
318
|
# Board: Jira
|
|
@@ -479,22 +488,56 @@ TICKET_BRANCH="feature/$(echo "$TICKET_KEY" | tr '[:upper:]' '[:lower:]')"
|
|
|
479
488
|
# BASE_BRANCH if it doesn't exist yet). Otherwise branch from BASE_BRANCH directly.
|
|
480
489
|
if [ "$EPIC_INFO" != "none" ]; then
|
|
481
490
|
TARGET_BRANCH="epic/$(echo "$EPIC_INFO" | tr '[:upper:]' '[:lower:]')"
|
|
482
|
-
git show-ref --verify --quiet "refs/heads/$TARGET_BRANCH" \
|
|
483
|
-
|| git checkout -b "$TARGET_BRANCH" "$BASE_BRANCH"
|
|
484
491
|
else
|
|
485
492
|
TARGET_BRANCH="$BASE_BRANCH"
|
|
486
493
|
fi
|
|
487
494
|
|
|
495
|
+
# ─── DRY RUN ───────────────────────────────────────────────────────────────────
|
|
496
|
+
|
|
497
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
498
|
+
echo ""
|
|
499
|
+
echo "── Dry run ──────────────────────────────────────"
|
|
500
|
+
echo " Ticket: [$TICKET_KEY] $SUMMARY"
|
|
501
|
+
echo " Epic: $EPIC_INFO"
|
|
502
|
+
echo " Blockers: $BLOCKERS"
|
|
503
|
+
echo " Target branch: $TARGET_BRANCH"
|
|
504
|
+
echo " Feature branch: $TICKET_BRANCH"
|
|
505
|
+
echo "─────────────────────────────────────────────────"
|
|
506
|
+
echo " No changes made. Remove --dry-run to run for real."
|
|
507
|
+
exit 0
|
|
508
|
+
fi
|
|
509
|
+
|
|
488
510
|
# ─── IMPLEMENT ─────────────────────────────────────────────────────────────────
|
|
489
511
|
|
|
490
512
|
echo "Picking up: [$TICKET_KEY] $SUMMARY"
|
|
491
513
|
echo "Epic: $EPIC_INFO | Target branch: $TARGET_BRANCH | Blockers: $BLOCKERS"
|
|
492
514
|
|
|
515
|
+
git show-ref --verify --quiet "refs/heads/$TARGET_BRANCH" \
|
|
516
|
+
|| git checkout -b "$TARGET_BRANCH" "$BASE_BRANCH"
|
|
493
517
|
git checkout "$TARGET_BRANCH"
|
|
494
518
|
# -B creates the branch if it doesn't exist, or resets it to HEAD if it does.
|
|
495
519
|
# This handles retries cleanly without failing on an already-existing branch.
|
|
496
520
|
git checkout -B "$TICKET_BRANCH"
|
|
497
521
|
|
|
522
|
+
# Transition ticket to In Progress (best-effort — never fails the run)
|
|
523
|
+
if [ -n "${CLANCY_STATUS_IN_PROGRESS:-}" ]; then
|
|
524
|
+
TRANSITIONS=$(curl -s \
|
|
525
|
+
-u "$JIRA_USER:$JIRA_API_TOKEN" \
|
|
526
|
+
-H "Accept: application/json" \
|
|
527
|
+
"$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY/transitions")
|
|
528
|
+
IN_PROGRESS_ID=$(echo "$TRANSITIONS" | jq -r \
|
|
529
|
+
--arg name "$CLANCY_STATUS_IN_PROGRESS" \
|
|
530
|
+
'.transitions[] | select(.name == $name) | .id' | head -1)
|
|
531
|
+
if [ -n "$IN_PROGRESS_ID" ]; then
|
|
532
|
+
curl -s -X POST \
|
|
533
|
+
-u "$JIRA_USER:$JIRA_API_TOKEN" \
|
|
534
|
+
-H "Content-Type: application/json" \
|
|
535
|
+
"$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY/transitions" \
|
|
536
|
+
-d "$(jq -n --arg id "$IN_PROGRESS_ID" '{"transition":{"id":$id}}')" >/dev/null 2>&1 || true
|
|
537
|
+
echo " → Transitioned to $CLANCY_STATUS_IN_PROGRESS"
|
|
538
|
+
fi
|
|
539
|
+
fi
|
|
540
|
+
|
|
498
541
|
PROMPT="You are implementing Jira ticket $TICKET_KEY.
|
|
499
542
|
|
|
500
543
|
Summary: $SUMMARY
|
|
@@ -516,7 +559,8 @@ If you must SKIP this ticket:
|
|
|
516
559
|
4. Stop — no branches, no file changes, no git operations.
|
|
517
560
|
|
|
518
561
|
If the ticket IS implementable, continue:
|
|
519
|
-
1. Read
|
|
562
|
+
1. Read core docs in .clancy/docs/: STACK.md, ARCHITECTURE.md, CONVENTIONS.md, GIT.md, DEFINITION-OF-DONE.md, CONCERNS.md
|
|
563
|
+
Also read if relevant to this ticket: INTEGRATIONS.md (external APIs/services/auth), TESTING.md (tests/specs/coverage), DESIGN-SYSTEM.md (UI/components/styles), ACCESSIBILITY.md (accessibility/ARIA/WCAG)
|
|
520
564
|
2. Follow the conventions in GIT.md exactly
|
|
521
565
|
3. Implement the ticket fully
|
|
522
566
|
4. Commit your work following the conventions in GIT.md
|
|
@@ -540,6 +584,25 @@ fi
|
|
|
540
584
|
# Delete ticket branch locally (never push deletes)
|
|
541
585
|
git branch -d "$TICKET_BRANCH"
|
|
542
586
|
|
|
587
|
+
# Transition ticket to Done (best-effort — never fails the run)
|
|
588
|
+
if [ -n "${CLANCY_STATUS_DONE:-}" ]; then
|
|
589
|
+
TRANSITIONS=$(curl -s \
|
|
590
|
+
-u "$JIRA_USER:$JIRA_API_TOKEN" \
|
|
591
|
+
-H "Accept: application/json" \
|
|
592
|
+
"$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY/transitions")
|
|
593
|
+
DONE_ID=$(echo "$TRANSITIONS" | jq -r \
|
|
594
|
+
--arg name "$CLANCY_STATUS_DONE" \
|
|
595
|
+
'.transitions[] | select(.name == $name) | .id' | head -1)
|
|
596
|
+
if [ -n "$DONE_ID" ]; then
|
|
597
|
+
curl -s -X POST \
|
|
598
|
+
-u "$JIRA_USER:$JIRA_API_TOKEN" \
|
|
599
|
+
-H "Content-Type: application/json" \
|
|
600
|
+
"$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY/transitions" \
|
|
601
|
+
-d "$(jq -n --arg id "$DONE_ID" '{"transition":{"id":$id}}')" >/dev/null 2>&1 || true
|
|
602
|
+
echo " → Transitioned to $CLANCY_STATUS_DONE"
|
|
603
|
+
fi
|
|
604
|
+
fi
|
|
605
|
+
|
|
543
606
|
# Log progress
|
|
544
607
|
echo "$(date '+%Y-%m-%d %H:%M') | $TICKET_KEY | $SUMMARY | DONE" >> .clancy/progress.txt
|
|
545
608
|
|
|
@@ -572,6 +635,15 @@ Write this file when the chosen board is **GitHub Issues**:
|
|
|
572
635
|
# This means any command that fails will stop the script immediately rather than silently continuing.
|
|
573
636
|
set -euo pipefail
|
|
574
637
|
|
|
638
|
+
# Parse flags — must happen before preflight so --dry-run works without side effects.
|
|
639
|
+
DRY_RUN=false
|
|
640
|
+
for arg in "$@"; do
|
|
641
|
+
case "$arg" in
|
|
642
|
+
--dry-run) DRY_RUN=true ;;
|
|
643
|
+
esac
|
|
644
|
+
done
|
|
645
|
+
readonly DRY_RUN
|
|
646
|
+
|
|
575
647
|
# ─── WHAT THIS SCRIPT DOES ─────────────────────────────────────────────────────
|
|
576
648
|
#
|
|
577
649
|
# Board: GitHub Issues
|
|
@@ -705,17 +777,31 @@ TICKET_BRANCH="feature/issue-${ISSUE_NUMBER}"
|
|
|
705
777
|
if [ "$MILESTONE" != "none" ]; then
|
|
706
778
|
MILESTONE_SLUG=$(echo "$MILESTONE" | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | tr -cd '[:alnum:]-')
|
|
707
779
|
TARGET_BRANCH="milestone/${MILESTONE_SLUG}"
|
|
708
|
-
git show-ref --verify --quiet "refs/heads/$TARGET_BRANCH" \
|
|
709
|
-
|| git checkout -b "$TARGET_BRANCH" "$BASE_BRANCH"
|
|
710
780
|
else
|
|
711
781
|
TARGET_BRANCH="$BASE_BRANCH"
|
|
712
782
|
fi
|
|
713
783
|
|
|
784
|
+
# ─── DRY RUN ───────────────────────────────────────────────────────────────────
|
|
785
|
+
|
|
786
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
787
|
+
echo ""
|
|
788
|
+
echo "── Dry run ──────────────────────────────────────"
|
|
789
|
+
echo " Issue: [#${ISSUE_NUMBER}] $TITLE"
|
|
790
|
+
echo " Milestone: $MILESTONE"
|
|
791
|
+
echo " Target branch: $TARGET_BRANCH"
|
|
792
|
+
echo " Feature branch: $TICKET_BRANCH"
|
|
793
|
+
echo "─────────────────────────────────────────────────"
|
|
794
|
+
echo " No changes made. Remove --dry-run to run for real."
|
|
795
|
+
exit 0
|
|
796
|
+
fi
|
|
797
|
+
|
|
714
798
|
# ─── IMPLEMENT ─────────────────────────────────────────────────────────────────
|
|
715
799
|
|
|
716
800
|
echo "Picking up: [#${ISSUE_NUMBER}] $TITLE"
|
|
717
801
|
echo "Milestone: $MILESTONE | Target branch: $TARGET_BRANCH"
|
|
718
802
|
|
|
803
|
+
git show-ref --verify --quiet "refs/heads/$TARGET_BRANCH" \
|
|
804
|
+
|| git checkout -b "$TARGET_BRANCH" "$BASE_BRANCH"
|
|
719
805
|
git checkout "$TARGET_BRANCH"
|
|
720
806
|
# -B creates the branch if it doesn't exist, or resets it to HEAD if it does.
|
|
721
807
|
# This handles retries cleanly without failing on an already-existing branch.
|
|
@@ -741,7 +827,8 @@ If you must SKIP this issue:
|
|
|
741
827
|
4. Stop — no branches, no file changes, no git operations.
|
|
742
828
|
|
|
743
829
|
If the issue IS implementable, continue:
|
|
744
|
-
1. Read
|
|
830
|
+
1. Read core docs in .clancy/docs/: STACK.md, ARCHITECTURE.md, CONVENTIONS.md, GIT.md, DEFINITION-OF-DONE.md, CONCERNS.md
|
|
831
|
+
Also read if relevant to this ticket: INTEGRATIONS.md (external APIs/services/auth), TESTING.md (tests/specs/coverage), DESIGN-SYSTEM.md (UI/components/styles), ACCESSIBILITY.md (accessibility/ARIA/WCAG)
|
|
745
832
|
2. Follow the conventions in GIT.md exactly
|
|
746
833
|
3. Implement the issue fully
|
|
747
834
|
4. Commit your work following the conventions in GIT.md
|
|
@@ -806,6 +893,15 @@ Write this file when the chosen board is **Linear**:
|
|
|
806
893
|
# This means any command that fails will stop the script immediately rather than silently continuing.
|
|
807
894
|
set -euo pipefail
|
|
808
895
|
|
|
896
|
+
# Parse flags — must happen before preflight so --dry-run works without side effects.
|
|
897
|
+
DRY_RUN=false
|
|
898
|
+
for arg in "$@"; do
|
|
899
|
+
case "$arg" in
|
|
900
|
+
--dry-run) DRY_RUN=true ;;
|
|
901
|
+
esac
|
|
902
|
+
done
|
|
903
|
+
readonly DRY_RUN
|
|
904
|
+
|
|
809
905
|
# ─── WHAT THIS SCRIPT DOES ─────────────────────────────────────────────────────
|
|
810
906
|
#
|
|
811
907
|
# Board: Linear
|
|
@@ -956,6 +1052,7 @@ if [ "$NODE_COUNT" -eq 0 ]; then
|
|
|
956
1052
|
exit 0
|
|
957
1053
|
fi
|
|
958
1054
|
|
|
1055
|
+
ISSUE_ID=$(echo "$RESPONSE" | jq -r '.data.viewer.assignedIssues.nodes[0].id')
|
|
959
1056
|
IDENTIFIER=$(echo "$RESPONSE" | jq -r '.data.viewer.assignedIssues.nodes[0].identifier')
|
|
960
1057
|
TITLE=$(echo "$RESPONSE" | jq -r '.data.viewer.assignedIssues.nodes[0].title')
|
|
961
1058
|
DESCRIPTION=$(echo "$RESPONSE" | jq -r '.data.viewer.assignedIssues.nodes[0].description // "No description"')
|
|
@@ -975,22 +1072,58 @@ TICKET_BRANCH="feature/$(echo "$IDENTIFIER" | tr '[:upper:]' '[:lower:]')"
|
|
|
975
1072
|
# BASE_BRANCH if it doesn't exist yet). Otherwise branch from BASE_BRANCH directly.
|
|
976
1073
|
if [ "$PARENT_ID" != "none" ]; then
|
|
977
1074
|
TARGET_BRANCH="epic/$(echo "$PARENT_ID" | tr '[:upper:]' '[:lower:]')"
|
|
978
|
-
git show-ref --verify --quiet "refs/heads/$TARGET_BRANCH" \
|
|
979
|
-
|| git checkout -b "$TARGET_BRANCH" "$BASE_BRANCH"
|
|
980
1075
|
else
|
|
981
1076
|
TARGET_BRANCH="$BASE_BRANCH"
|
|
982
1077
|
fi
|
|
983
1078
|
|
|
1079
|
+
# ─── DRY RUN ───────────────────────────────────────────────────────────────────
|
|
1080
|
+
|
|
1081
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
1082
|
+
echo ""
|
|
1083
|
+
echo "── Dry run ──────────────────────────────────────"
|
|
1084
|
+
echo " Issue: [$IDENTIFIER] $TITLE"
|
|
1085
|
+
echo " Epic: $EPIC_INFO"
|
|
1086
|
+
echo " Target branch: $TARGET_BRANCH"
|
|
1087
|
+
echo " Feature branch: $TICKET_BRANCH"
|
|
1088
|
+
echo "─────────────────────────────────────────────────"
|
|
1089
|
+
echo " No changes made. Remove --dry-run to run for real."
|
|
1090
|
+
exit 0
|
|
1091
|
+
fi
|
|
1092
|
+
|
|
984
1093
|
# ─── IMPLEMENT ─────────────────────────────────────────────────────────────────
|
|
985
1094
|
|
|
986
1095
|
echo "Picking up: [$IDENTIFIER] $TITLE"
|
|
987
1096
|
echo "Epic: $EPIC_INFO | Target branch: $TARGET_BRANCH"
|
|
988
1097
|
|
|
1098
|
+
git show-ref --verify --quiet "refs/heads/$TARGET_BRANCH" \
|
|
1099
|
+
|| git checkout -b "$TARGET_BRANCH" "$BASE_BRANCH"
|
|
989
1100
|
git checkout "$TARGET_BRANCH"
|
|
990
1101
|
# -B creates the branch if it doesn't exist, or resets it to HEAD if it does.
|
|
991
1102
|
# This handles retries cleanly without failing on an already-existing branch.
|
|
992
1103
|
git checkout -B "$TICKET_BRANCH"
|
|
993
1104
|
|
|
1105
|
+
# Transition issue to In Progress (best-effort — never fails the run).
|
|
1106
|
+
# Queries team workflow states by type "started", picks the first match.
|
|
1107
|
+
if [ -n "${CLANCY_STATUS_IN_PROGRESS:-}" ]; then
|
|
1108
|
+
STATE_RESP=$(curl -s -X POST https://api.linear.app/graphql \
|
|
1109
|
+
-H "Content-Type: application/json" \
|
|
1110
|
+
-H "Authorization: $LINEAR_API_KEY" \
|
|
1111
|
+
-d "$(jq -n --arg teamId "$LINEAR_TEAM_ID" --arg name "$CLANCY_STATUS_IN_PROGRESS" \
|
|
1112
|
+
'{"query": "query($teamId: String!, $name: String!) { workflowStates(filter: { team: { id: { eq: $teamId } } name: { eq: $name } }) { nodes { id } } }", "variables": {"teamId": $teamId, "name": $name}}')")
|
|
1113
|
+
IN_PROGRESS_STATE_ID=$(echo "$STATE_RESP" | jq -r '.data.workflowStates.nodes[0].id // empty')
|
|
1114
|
+
if [ -n "$IN_PROGRESS_STATE_ID" ]; then
|
|
1115
|
+
curl -s -X POST https://api.linear.app/graphql \
|
|
1116
|
+
-H "Content-Type: application/json" \
|
|
1117
|
+
-H "Authorization: $LINEAR_API_KEY" \
|
|
1118
|
+
-d "$(jq -n --arg issueId "$ISSUE_ID" --arg stateId "$IN_PROGRESS_STATE_ID" \
|
|
1119
|
+
'{"query": "mutation($issueId: String!, $stateId: String!) { issueUpdate(id: $issueId, input: { stateId: $stateId }) { success } }", "variables": {"issueId": $issueId, "stateId": $stateId}}')" \
|
|
1120
|
+
>/dev/null 2>&1 || true
|
|
1121
|
+
echo " → Transitioned to $CLANCY_STATUS_IN_PROGRESS"
|
|
1122
|
+
else
|
|
1123
|
+
echo " ⚠ Workflow state '$CLANCY_STATUS_IN_PROGRESS' not found — check CLANCY_STATUS_IN_PROGRESS in .clancy/.env."
|
|
1124
|
+
fi
|
|
1125
|
+
fi
|
|
1126
|
+
|
|
994
1127
|
PROMPT="You are implementing Linear issue $IDENTIFIER.
|
|
995
1128
|
|
|
996
1129
|
Title: $TITLE
|
|
@@ -1011,7 +1144,8 @@ If you must SKIP this issue:
|
|
|
1011
1144
|
4. Stop — no branches, no file changes, no git operations.
|
|
1012
1145
|
|
|
1013
1146
|
If the issue IS implementable, continue:
|
|
1014
|
-
1. Read
|
|
1147
|
+
1. Read core docs in .clancy/docs/: STACK.md, ARCHITECTURE.md, CONVENTIONS.md, GIT.md, DEFINITION-OF-DONE.md, CONCERNS.md
|
|
1148
|
+
Also read if relevant to this ticket: INTEGRATIONS.md (external APIs/services/auth), TESTING.md (tests/specs/coverage), DESIGN-SYSTEM.md (UI/components/styles), ACCESSIBILITY.md (accessibility/ARIA/WCAG)
|
|
1015
1149
|
2. Follow the conventions in GIT.md exactly
|
|
1016
1150
|
3. Implement the issue fully
|
|
1017
1151
|
4. Commit your work following the conventions in GIT.md
|
|
@@ -1035,6 +1169,27 @@ fi
|
|
|
1035
1169
|
# Delete ticket branch locally
|
|
1036
1170
|
git branch -d "$TICKET_BRANCH"
|
|
1037
1171
|
|
|
1172
|
+
# Transition issue to Done (best-effort — never fails the run).
|
|
1173
|
+
if [ -n "${CLANCY_STATUS_DONE:-}" ]; then
|
|
1174
|
+
STATE_RESP=$(curl -s -X POST https://api.linear.app/graphql \
|
|
1175
|
+
-H "Content-Type: application/json" \
|
|
1176
|
+
-H "Authorization: $LINEAR_API_KEY" \
|
|
1177
|
+
-d "$(jq -n --arg teamId "$LINEAR_TEAM_ID" --arg name "$CLANCY_STATUS_DONE" \
|
|
1178
|
+
'{"query": "query($teamId: String!, $name: String!) { workflowStates(filter: { team: { id: { eq: $teamId } } name: { eq: $name } }) { nodes { id } } }", "variables": {"teamId": $teamId, "name": $name}}')")
|
|
1179
|
+
DONE_STATE_ID=$(echo "$STATE_RESP" | jq -r '.data.workflowStates.nodes[0].id // empty')
|
|
1180
|
+
if [ -n "$DONE_STATE_ID" ]; then
|
|
1181
|
+
curl -s -X POST https://api.linear.app/graphql \
|
|
1182
|
+
-H "Content-Type: application/json" \
|
|
1183
|
+
-H "Authorization: $LINEAR_API_KEY" \
|
|
1184
|
+
-d "$(jq -n --arg issueId "$ISSUE_ID" --arg stateId "$DONE_STATE_ID" \
|
|
1185
|
+
'{"query": "mutation($issueId: String!, $stateId: String!) { issueUpdate(id: $issueId, input: { stateId: $stateId }) { success } }", "variables": {"issueId": $issueId, "stateId": $stateId}}')" \
|
|
1186
|
+
>/dev/null 2>&1 || true
|
|
1187
|
+
echo " → Transitioned to $CLANCY_STATUS_DONE"
|
|
1188
|
+
else
|
|
1189
|
+
echo " ⚠ Workflow state '$CLANCY_STATUS_DONE' not found — check CLANCY_STATUS_DONE in .clancy/.env."
|
|
1190
|
+
fi
|
|
1191
|
+
fi
|
|
1192
|
+
|
|
1038
1193
|
# Log progress
|
|
1039
1194
|
echo "$(date '+%Y-%m-%d %H:%M') | $IDENTIFIER | $TITLE | DONE" >> .clancy/progress.txt
|
|
1040
1195
|
|
|
@@ -1234,6 +1389,12 @@ MAX_ITERATIONS=5
|
|
|
1234
1389
|
# PLAYWRIGHT_STORYBOOK_PORT=6006
|
|
1235
1390
|
# PLAYWRIGHT_STARTUP_WAIT=15
|
|
1236
1391
|
|
|
1392
|
+
# ─── Optional: Status transitions ────────────────────────────────────────────
|
|
1393
|
+
# Move tickets automatically when Clancy picks up or completes them.
|
|
1394
|
+
# Set to the exact status name shown in your Jira board column header.
|
|
1395
|
+
# CLANCY_STATUS_IN_PROGRESS="In Progress"
|
|
1396
|
+
# CLANCY_STATUS_DONE="Done"
|
|
1397
|
+
|
|
1237
1398
|
# ─── Optional: Notifications ──────────────────────────────────────────────────
|
|
1238
1399
|
# Webhook URL for Slack or Teams notifications on ticket completion
|
|
1239
1400
|
# CLANCY_NOTIFY_WEBHOOK=https://hooks.slack.com/services/your/webhook/url
|
|
@@ -1330,6 +1491,12 @@ MAX_ITERATIONS=20
|
|
|
1330
1491
|
# PLAYWRIGHT_STORYBOOK_PORT=6006
|
|
1331
1492
|
# PLAYWRIGHT_STARTUP_WAIT=15
|
|
1332
1493
|
|
|
1494
|
+
# ─── Optional: Status transitions ────────────────────────────────────────────
|
|
1495
|
+
# Move issues automatically when Clancy picks up or completes them.
|
|
1496
|
+
# Set to the exact workflow state name shown in your Linear board column header.
|
|
1497
|
+
# CLANCY_STATUS_IN_PROGRESS="In Progress"
|
|
1498
|
+
# CLANCY_STATUS_DONE="Done"
|
|
1499
|
+
|
|
1333
1500
|
# ─── Optional: Notifications ──────────────────────────────────────────────────
|
|
1334
1501
|
# Webhook URL for Slack or Teams notifications on ticket completion
|
|
1335
1502
|
# CLANCY_NOTIFY_WEBHOOK=https://hooks.slack.com/services/your/webhook/url
|