@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.
- package/LICENSE +21 -0
- package/README.md +62 -0
- package/dist/hooks/clancy-branch-guard.js +1 -0
- package/dist/hooks/clancy-check-update.js +2 -0
- package/dist/hooks/clancy-context-monitor.js +9 -0
- package/dist/hooks/clancy-credential-guard.js +2 -0
- package/dist/hooks/clancy-drift-detector.js +1 -0
- package/dist/hooks/clancy-notification.js +1 -0
- package/dist/hooks/clancy-post-compact.js +2 -0
- package/dist/hooks/clancy-statusline.js +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/installer/file-ops/file-ops.d.ts +35 -0
- package/dist/installer/file-ops/file-ops.d.ts.map +1 -0
- package/dist/installer/file-ops/file-ops.js +95 -0
- package/dist/installer/file-ops/file-ops.js.map +1 -0
- package/dist/installer/file-ops/index.d.ts +2 -0
- package/dist/installer/file-ops/index.d.ts.map +1 -0
- package/dist/installer/file-ops/index.js +2 -0
- package/dist/installer/file-ops/index.js.map +1 -0
- package/dist/installer/hook-installer/hook-installer.d.ts +22 -0
- package/dist/installer/hook-installer/hook-installer.d.ts.map +1 -0
- package/dist/installer/hook-installer/hook-installer.js +213 -0
- package/dist/installer/hook-installer/hook-installer.js.map +1 -0
- package/dist/installer/hook-installer/index.d.ts +2 -0
- package/dist/installer/hook-installer/index.d.ts.map +1 -0
- package/dist/installer/hook-installer/index.js +2 -0
- package/dist/installer/hook-installer/index.js.map +1 -0
- package/dist/installer/install/index.d.ts +3 -0
- package/dist/installer/install/index.d.ts.map +1 -0
- package/dist/installer/install/index.js +2 -0
- package/dist/installer/install/index.js.map +1 -0
- package/dist/installer/install/install.d.ts +124 -0
- package/dist/installer/install/install.d.ts.map +1 -0
- package/dist/installer/install/install.js +255 -0
- package/dist/installer/install/install.js.map +1 -0
- package/dist/installer/manifest/index.d.ts +2 -0
- package/dist/installer/manifest/index.d.ts.map +1 -0
- package/dist/installer/manifest/index.js +2 -0
- package/dist/installer/manifest/index.js.map +1 -0
- package/dist/installer/manifest/manifest.d.ts +46 -0
- package/dist/installer/manifest/manifest.d.ts.map +1 -0
- package/dist/installer/manifest/manifest.js +180 -0
- package/dist/installer/manifest/manifest.js.map +1 -0
- package/dist/installer/prompts/index.d.ts +2 -0
- package/dist/installer/prompts/index.d.ts.map +1 -0
- package/dist/installer/prompts/index.js +2 -0
- package/dist/installer/prompts/index.js.map +1 -0
- package/dist/installer/prompts/prompts.d.ts +34 -0
- package/dist/installer/prompts/prompts.d.ts.map +1 -0
- package/dist/installer/prompts/prompts.js +28 -0
- package/dist/installer/prompts/prompts.js.map +1 -0
- package/dist/installer/role-filter/index.d.ts +2 -0
- package/dist/installer/role-filter/index.d.ts.map +1 -0
- package/dist/installer/role-filter/index.js +2 -0
- package/dist/installer/role-filter/index.js.map +1 -0
- package/dist/installer/role-filter/role-filter.d.ts +33 -0
- package/dist/installer/role-filter/role-filter.d.ts.map +1 -0
- package/dist/installer/role-filter/role-filter.js +91 -0
- package/dist/installer/role-filter/role-filter.js.map +1 -0
- package/dist/installer/shared/fs-errors/fs-errors.d.ts +3 -0
- package/dist/installer/shared/fs-errors/fs-errors.d.ts.map +1 -0
- package/dist/installer/shared/fs-errors/fs-errors.js +7 -0
- package/dist/installer/shared/fs-errors/fs-errors.js.map +1 -0
- package/dist/installer/shared/fs-errors/index.d.ts +2 -0
- package/dist/installer/shared/fs-errors/index.d.ts.map +1 -0
- package/dist/installer/shared/fs-errors/index.js +2 -0
- package/dist/installer/shared/fs-errors/index.js.map +1 -0
- package/dist/installer/shared/fs-guards/fs-guards.d.ts +3 -0
- package/dist/installer/shared/fs-guards/fs-guards.d.ts.map +1 -0
- package/dist/installer/shared/fs-guards/fs-guards.js +18 -0
- package/dist/installer/shared/fs-guards/fs-guards.js.map +1 -0
- package/dist/installer/shared/fs-guards/index.d.ts +2 -0
- package/dist/installer/shared/fs-guards/index.d.ts.map +1 -0
- package/dist/installer/shared/fs-guards/index.js +2 -0
- package/dist/installer/shared/fs-guards/index.js.map +1 -0
- package/dist/installer/shared/type-guards/index.d.ts +2 -0
- package/dist/installer/shared/type-guards/index.d.ts.map +1 -0
- package/dist/installer/shared/type-guards/index.js +2 -0
- package/dist/installer/shared/type-guards/index.js.map +1 -0
- package/dist/installer/shared/type-guards/type-guards.d.ts +8 -0
- package/dist/installer/shared/type-guards/type-guards.d.ts.map +1 -0
- package/dist/installer/shared/type-guards/type-guards.js +10 -0
- package/dist/installer/shared/type-guards/type-guards.js.map +1 -0
- package/dist/installer/ui/index.d.ts +2 -0
- package/dist/installer/ui/index.d.ts.map +1 -0
- package/dist/installer/ui/index.js +2 -0
- package/dist/installer/ui/index.js.map +1 -0
- package/dist/installer/ui/ui.d.ts +23 -0
- package/dist/installer/ui/ui.d.ts.map +1 -0
- package/dist/installer/ui/ui.js +121 -0
- package/dist/installer/ui/ui.js.map +1 -0
- package/dist/runner/autopilot/autopilot.d.ts +71 -0
- package/dist/runner/autopilot/autopilot.d.ts.map +1 -0
- package/dist/runner/autopilot/autopilot.js +206 -0
- package/dist/runner/autopilot/autopilot.js.map +1 -0
- package/dist/runner/autopilot/index.d.ts +2 -0
- package/dist/runner/autopilot/index.d.ts.map +1 -0
- package/dist/runner/autopilot/index.js +2 -0
- package/dist/runner/autopilot/index.js.map +1 -0
- package/dist/runner/cli-bridge/cli-bridge.d.ts +34 -0
- package/dist/runner/cli-bridge/cli-bridge.d.ts.map +1 -0
- package/dist/runner/cli-bridge/cli-bridge.js +53 -0
- package/dist/runner/cli-bridge/cli-bridge.js.map +1 -0
- package/dist/runner/cli-bridge/index.d.ts +2 -0
- package/dist/runner/cli-bridge/index.d.ts.map +1 -0
- package/dist/runner/cli-bridge/index.js +2 -0
- package/dist/runner/cli-bridge/index.js.map +1 -0
- package/dist/runner/dep-factory/deliver-phase.d.ts +24 -0
- package/dist/runner/dep-factory/deliver-phase.d.ts.map +1 -0
- package/dist/runner/dep-factory/deliver-phase.js +57 -0
- package/dist/runner/dep-factory/deliver-phase.js.map +1 -0
- package/dist/runner/dep-factory/dep-factory.d.ts +38 -0
- package/dist/runner/dep-factory/dep-factory.d.ts.map +1 -0
- package/dist/runner/dep-factory/dep-factory.js +193 -0
- package/dist/runner/dep-factory/dep-factory.js.map +1 -0
- package/dist/runner/dep-factory/index.d.ts +2 -0
- package/dist/runner/dep-factory/index.d.ts.map +1 -0
- package/dist/runner/dep-factory/index.js +2 -0
- package/dist/runner/dep-factory/index.js.map +1 -0
- package/dist/runner/dep-factory/invoke-phase.d.ts +20 -0
- package/dist/runner/dep-factory/invoke-phase.d.ts.map +1 -0
- package/dist/runner/dep-factory/invoke-phase.js +45 -0
- package/dist/runner/dep-factory/invoke-phase.js.map +1 -0
- package/dist/runner/implement/implement.d.ts +38 -0
- package/dist/runner/implement/implement.d.ts.map +1 -0
- package/dist/runner/implement/implement.js +61 -0
- package/dist/runner/implement/implement.js.map +1 -0
- package/dist/runner/implement/index.d.ts +2 -0
- package/dist/runner/implement/index.d.ts.map +1 -0
- package/dist/runner/implement/index.js +2 -0
- package/dist/runner/implement/index.js.map +1 -0
- package/dist/runner/notify/index.d.ts +2 -0
- package/dist/runner/notify/index.d.ts.map +1 -0
- package/dist/runner/notify/index.js +2 -0
- package/dist/runner/notify/index.js.map +1 -0
- package/dist/runner/notify/notify.d.ts +49 -0
- package/dist/runner/notify/notify.d.ts.map +1 -0
- package/dist/runner/notify/notify.js +90 -0
- package/dist/runner/notify/notify.js.map +1 -0
- package/dist/runner/prompt-builder/index.d.ts +2 -0
- package/dist/runner/prompt-builder/index.d.ts.map +1 -0
- package/dist/runner/prompt-builder/index.js +2 -0
- package/dist/runner/prompt-builder/index.js.map +1 -0
- package/dist/runner/prompt-builder/prompt-builder.d.ts +53 -0
- package/dist/runner/prompt-builder/prompt-builder.d.ts.map +1 -0
- package/dist/runner/prompt-builder/prompt-builder.js +122 -0
- package/dist/runner/prompt-builder/prompt-builder.js.map +1 -0
- package/dist/runner/session-report/index.d.ts +2 -0
- package/dist/runner/session-report/index.d.ts.map +1 -0
- package/dist/runner/session-report/index.js +2 -0
- package/dist/runner/session-report/index.js.map +1 -0
- package/dist/runner/session-report/session-report.d.ts +81 -0
- package/dist/runner/session-report/session-report.d.ts.map +1 -0
- package/dist/runner/session-report/session-report.js +227 -0
- package/dist/runner/session-report/session-report.js.map +1 -0
- package/dist/runner/shared/types.d.ts +30 -0
- package/dist/runner/shared/types.d.ts.map +1 -0
- package/dist/runner/shared/types.js +2 -0
- package/dist/runner/shared/types.js.map +1 -0
- package/dist/shared/ansi/ansi.d.ts +59 -0
- package/dist/shared/ansi/ansi.d.ts.map +1 -0
- package/dist/shared/ansi/ansi.js +59 -0
- package/dist/shared/ansi/ansi.js.map +1 -0
- package/dist/shared/ansi/index.d.ts +2 -0
- package/dist/shared/ansi/index.d.ts.map +1 -0
- package/dist/shared/ansi/index.js +2 -0
- package/dist/shared/ansi/index.js.map +1 -0
- package/package.json +52 -0
- package/src/agents/agents.test.ts +57 -0
- package/src/agents/arch-agent.md +80 -0
- package/src/agents/concerns-agent.md +96 -0
- package/src/agents/design-agent.md +146 -0
- package/src/agents/devils-advocate.md +54 -0
- package/src/agents/quality-agent.md +178 -0
- package/src/agents/tech-agent.md +101 -0
- package/src/agents/verification-gate.md +128 -0
- package/src/roles/implementer/commands/autopilot.md +11 -0
- package/src/roles/implementer/commands/dry-run.md +15 -0
- package/src/roles/implementer/commands/implement.md +19 -0
- package/src/roles/implementer/workflows/autopilot.md +136 -0
- package/src/roles/implementer/workflows/implement.md +161 -0
- package/src/roles/planner/commands/approve-plan.md +11 -0
- package/src/roles/planner/commands/plan.md +22 -0
- package/src/roles/planner/workflows/approve-plan.md +970 -0
- package/src/roles/planner/workflows/plan.md +868 -0
- package/src/roles/reviewer/commands/logs.md +7 -0
- package/src/roles/reviewer/commands/review.md +9 -0
- package/src/roles/reviewer/commands/status.md +9 -0
- package/src/roles/reviewer/workflows/logs.md +109 -0
- package/src/roles/reviewer/workflows/review.md +197 -0
- package/src/roles/reviewer/workflows/status.md +142 -0
- package/src/roles/roles.test.ts +87 -0
- package/src/roles/setup/commands/doctor.md +7 -0
- package/src/roles/setup/commands/help.md +80 -0
- package/src/roles/setup/commands/init.md +7 -0
- package/src/roles/setup/commands/map-codebase.md +17 -0
- package/src/roles/setup/commands/settings.md +7 -0
- package/src/roles/setup/commands/uninstall.md +5 -0
- package/src/roles/setup/commands/update-docs.md +9 -0
- package/src/roles/setup/commands/update.md +13 -0
- package/src/roles/setup/workflows/doctor.md +131 -0
- package/src/roles/setup/workflows/init.md +1096 -0
- package/src/roles/setup/workflows/map-codebase.md +130 -0
- package/src/roles/setup/workflows/scaffold.md +872 -0
- package/src/roles/setup/workflows/settings.md +958 -0
- package/src/roles/setup/workflows/uninstall.md +170 -0
- package/src/roles/setup/workflows/update-docs.md +95 -0
- package/src/roles/setup/workflows/update.md +287 -0
- package/src/roles/strategist/commands/approve-brief.md +23 -0
- package/src/roles/strategist/commands/brief.md +29 -0
- package/src/roles/strategist/workflows/approve-brief.md +1540 -0
- package/src/roles/strategist/workflows/brief.md +1330 -0
- package/src/templates/CLAUDE.md +101 -0
- package/src/templates/templates.test.ts +53 -0
|
@@ -0,0 +1,1330 @@
|
|
|
1
|
+
# Clancy Brief Workflow
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Research an idea, interrogate it thoroughly, and generate a structured strategic brief with vertical-slice ticket decomposition. Briefs are saved locally and optionally posted as comments on the source ticket. Does not create tickets — that is `/clancy:approve-brief`.
|
|
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 `strategist` (or env var is unset, which indicates a global install where all roles are available). If `CLANCY_ROLES` is set but does not include `strategist`:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
The Strategist role is not enabled. Add "strategist" to CLANCY_ROLES in .clancy/.env or run /clancy:settings.
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Stop.
|
|
28
|
+
|
|
29
|
+
4. 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:
|
|
30
|
+
|
|
31
|
+
**AFK mode** (`--afk` flag or `CLANCY_MODE=afk`): auto-pull without prompting. Run `git pull origin $CLANCY_BASE_BRANCH` and continue.
|
|
32
|
+
|
|
33
|
+
**Interactive mode:**
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
⚠️ Your local branch is behind origin/{CLANCY_BASE_BRANCH} by {N} commit(s).
|
|
37
|
+
|
|
38
|
+
[1] Pull latest
|
|
39
|
+
[2] Continue anyway
|
|
40
|
+
[3] Abort
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
- [1] runs `git pull origin $CLANCY_BASE_BRANCH` and continues
|
|
44
|
+
- [2] continues without pulling
|
|
45
|
+
- [3] stops
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Step 2 — Parse arguments
|
|
50
|
+
|
|
51
|
+
Parse the arguments passed to the command. Arguments can appear in any order.
|
|
52
|
+
|
|
53
|
+
### Flags
|
|
54
|
+
|
|
55
|
+
- **`--list`** — show brief inventory and stop (no brief generated)
|
|
56
|
+
- **`--fresh`** — discard any existing brief and start over from scratch
|
|
57
|
+
- **`--research`** — force web research agent (adds 1 web agent to the research phase)
|
|
58
|
+
- **`--afk`** — use AI-grill instead of human grill (no interactive questions)
|
|
59
|
+
- **`--epic {KEY}`** — hint for `/clancy:approve-brief` later. Stored in the brief's metadata. Ignored if the input is a board ticket (the source ticket is the parent).
|
|
60
|
+
|
|
61
|
+
### Input modes
|
|
62
|
+
|
|
63
|
+
- **No input (no flags that consume arguments):** Interactive mode — but first check for `--afk`:
|
|
64
|
+
- If running in AFK mode (`--afk` flag OR `CLANCY_MODE=afk`): there is no human to answer. Display: `✗ Cannot run /clancy:brief in AFK mode without a ticket or idea. Use: /clancy:brief --afk #42 (GitHub) or PROJ-123 (Jira) or ENG-42 (Linear) or SC-123 (Shortcut) or 42 (Azure DevOps), or notion-XXXXXXXX (Notion), or /clancy:brief --afk "Add dark mode", or /clancy:brief 3 (batch mode — implies --afk).` Stop.
|
|
65
|
+
- Otherwise: prompt `What's the idea?` and parse the response. If the response looks like a ticket reference (`#42`, `PROJ-123`, `ENG-42`, `SC-123`, bare number on AzDo/Shortcut, or UUID/`notion-XXXXXXXX`), switch to board ticket mode. Otherwise treat as inline text.
|
|
66
|
+
- **Ticket key** (`PROJ-123`, `#42`, `ENG-42`, `42`): Board ticket mode — fetch the ticket from the board API. Validate format per platform:
|
|
67
|
+
- `#N` — 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.
|
|
68
|
+
- `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.
|
|
69
|
+
- **Quoted string or unquoted non-matching text** (e.g. `"Add dark mode"`): Inline text mode — use the text directly as the idea.
|
|
70
|
+
- **`--from {path}`** — From file mode. Cannot be combined with a ticket reference (error if both present: `Cannot use both a ticket reference and --from. Use one or the other.`). Validate:
|
|
71
|
+
- File does not exist: `File not found: {path}` Stop.
|
|
72
|
+
- File is empty: `File is empty: {path}` Stop.
|
|
73
|
+
- File > 50KB: Warn `Large file ({size}KB). Clancy will use the first ~50KB for context.` Truncate internally, continue.
|
|
74
|
+
- **Bare positive integer** (e.g. `/clancy:brief 3`): Batch mode or ambiguous.
|
|
75
|
+
- Board is GitHub and value could be an issue: Ambiguous — ask: `Did you mean issue #3 or batch 3 tickets? [1] Brief issue #3 [2] Brief 3 tickets from queue`
|
|
76
|
+
- Board is Azure DevOps and value > 10: treat as a work item ID (no ambiguity — AzDo always uses numeric IDs)
|
|
77
|
+
- Board is Jira, Linear, Shortcut, or Notion: Batch mode (N tickets from queue). Implies `--afk` (AI-grill for all).
|
|
78
|
+
|
|
79
|
+
If N > 10: `Maximum batch size is 10. Briefing 10 tickets.`
|
|
80
|
+
|
|
81
|
+
### --list flag handling
|
|
82
|
+
|
|
83
|
+
If `--list` is present (with or without other arguments), jump to Step 11 (Brief Inventory) and stop.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Step 3 — Gather idea (mode-specific)
|
|
88
|
+
|
|
89
|
+
### Board ticket mode
|
|
90
|
+
|
|
91
|
+
Fetch the source ticket from the board API.
|
|
92
|
+
|
|
93
|
+
#### GitHub — Fetch specific issue
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
RESPONSE=$(curl -s \
|
|
97
|
+
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
|
98
|
+
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
99
|
+
"https://api.github.com/repos/$GITHUB_REPO/issues/$ISSUE_NUMBER")
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Validate the response:
|
|
103
|
+
|
|
104
|
+
- If `pull_request` field is present and non-null: `#N is a pull request, not an issue.` Stop.
|
|
105
|
+
- If `state` is `closed`: warn `#N is closed. Brief it anyway? [y/N]`
|
|
106
|
+
- If `body` is null/empty: warn `No issue description — briefing from title only.`
|
|
107
|
+
- Extract: `title`, `body`, `labels`, `milestone`.
|
|
108
|
+
|
|
109
|
+
Fetch comments for existing brief detection:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
COMMENTS=$(curl -s \
|
|
113
|
+
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
|
114
|
+
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
115
|
+
"https://api.github.com/repos/$GITHUB_REPO/issues/$ISSUE_NUMBER/comments?per_page=100")
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
#### Jira — Fetch specific ticket
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
RESPONSE=$(curl -s \
|
|
122
|
+
-u "$JIRA_USER:$JIRA_API_TOKEN" \
|
|
123
|
+
-H "Accept: application/json" \
|
|
124
|
+
"$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY?fields=summary,description,status,issuetype,parent,customfield_10014,components,priority,comment,project")
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Validate the response:
|
|
128
|
+
|
|
129
|
+
- If `fields.status.statusCategory.key` is `done`: warn `{KEY} is Done. Brief it anyway? [y/N]`
|
|
130
|
+
- If `fields.status.statusCategory.key` is `indeterminate`: warn `{KEY} is In Progress — briefing anyway.`
|
|
131
|
+
- If `fields.issuetype.name` is `Epic`: note `{KEY} is an Epic — child tickets will be created under it.`
|
|
132
|
+
- Extract: `summary`, `description` (ADF → plain text via `extractAdfText()`), status, existing comments from `comment.comments[]`.
|
|
133
|
+
|
|
134
|
+
#### Linear — Fetch specific issue
|
|
135
|
+
|
|
136
|
+
```graphql
|
|
137
|
+
query {
|
|
138
|
+
issues(filter: { identifier: { eq: "$IDENTIFIER" } }) {
|
|
139
|
+
nodes {
|
|
140
|
+
id
|
|
141
|
+
identifier
|
|
142
|
+
title
|
|
143
|
+
description
|
|
144
|
+
state {
|
|
145
|
+
id
|
|
146
|
+
name
|
|
147
|
+
type
|
|
148
|
+
}
|
|
149
|
+
parent {
|
|
150
|
+
id
|
|
151
|
+
identifier
|
|
152
|
+
title
|
|
153
|
+
}
|
|
154
|
+
children {
|
|
155
|
+
nodes {
|
|
156
|
+
id
|
|
157
|
+
identifier
|
|
158
|
+
title
|
|
159
|
+
state {
|
|
160
|
+
type
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
team {
|
|
165
|
+
id
|
|
166
|
+
key
|
|
167
|
+
name
|
|
168
|
+
}
|
|
169
|
+
labels {
|
|
170
|
+
nodes {
|
|
171
|
+
id
|
|
172
|
+
name
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
priority
|
|
176
|
+
estimate
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Validate the response:
|
|
183
|
+
|
|
184
|
+
- If `nodes` is empty: `Issue {KEY} not found on Linear.` Stop.
|
|
185
|
+
- If `state.type` is `completed` or `canceled`: warn `{KEY} is {state.name}. Brief it anyway? [y/N]`
|
|
186
|
+
- If `state.type` is `started`: warn `{KEY} is In Progress — briefing anyway.`
|
|
187
|
+
- If `parent` is present: warn `{KEY} is a sub-issue of {parent.identifier}. Creating children will produce a 3-level hierarchy. Continue? [Y/n]`
|
|
188
|
+
- If `team.id` differs from `LINEAR_TEAM_ID`: warn `{KEY} belongs to team "{team.name}", but LINEAR_TEAM_ID is different. Continue? [Y/n]`
|
|
189
|
+
|
|
190
|
+
#### Shortcut — Fetch specific story
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
RESPONSE=$(curl -s \
|
|
194
|
+
-H "Shortcut-Token: $SHORTCUT_API_TOKEN" \
|
|
195
|
+
-H "Content-Type: application/json" \
|
|
196
|
+
"https://api.app.shortcut.com/api/v3/stories/$STORY_ID")
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Extract the story ID from the key (e.g. `SC-123` → `123`, or use the numeric portion). Validate the response:
|
|
200
|
+
|
|
201
|
+
- If response status is 404: `Story ${KEY} not found on Shortcut.` Stop.
|
|
202
|
+
- If `completed` is `true` or `archived` is `true`: warn `Story is completed/archived. Brief it anyway? [y/N]`
|
|
203
|
+
- If `started` is `true`: warn `Story is in progress — briefing anyway.`
|
|
204
|
+
- If `epic_id` is present: note `Story belongs to epic — child tickets will be created under it.`
|
|
205
|
+
|
|
206
|
+
Fetch comments for existing brief detection:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
COMMENTS=$(curl -s \
|
|
210
|
+
-H "Shortcut-Token: $SHORTCUT_API_TOKEN" \
|
|
211
|
+
"https://api.app.shortcut.com/api/v3/stories/$STORY_ID/comments")
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Map fields: title = `name`, description = `description` (markdown), parent = `epic_id` (fetch epic name via `GET /api/v3/epics/$EPIC_ID` if set), labels = `labels[].name`.
|
|
215
|
+
|
|
216
|
+
#### Azure DevOps — Fetch specific work item
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
RESPONSE=$(curl -s \
|
|
220
|
+
-u ":$AZDO_PAT" \
|
|
221
|
+
-H "Accept: application/json" \
|
|
222
|
+
"https://dev.azure.com/$AZDO_ORG/$AZDO_PROJECT/_apis/wit/workitems/$WORK_ITEM_ID?\$expand=relations&api-version=7.1")
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Validate the response:
|
|
226
|
+
|
|
227
|
+
- If response contains `"message"` with `"does not exist"`: `Work item ${ID} not found.` Stop.
|
|
228
|
+
- If `fields["System.State"]` is a done/resolved state (e.g. `Done`, `Closed`, `Resolved`): warn `Work item is done. Brief it anyway? [y/N]`
|
|
229
|
+
- If `fields["System.State"]` is `Active`: warn `Work item is Active — briefing anyway.`
|
|
230
|
+
- Extract: `fields["System.Title"]`, `fields["System.Description"]` (HTML — strip tags), `fields["System.State"]`, `fields["System.Tags"]`.
|
|
231
|
+
|
|
232
|
+
Fetch comments (separate endpoint):
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
COMMENTS=$(curl -s \
|
|
236
|
+
-u ":$AZDO_PAT" \
|
|
237
|
+
-H "Accept: application/json" \
|
|
238
|
+
"https://dev.azure.com/$AZDO_ORG/$AZDO_PROJECT/_apis/wit/workitems/$WORK_ITEM_ID/comments?api-version=7.1-preview.4")
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Check `relations` array for parent (type `System.LinkTypes.Hierarchy-Reverse`) and existing children (type `System.LinkTypes.Hierarchy-Forward`).
|
|
242
|
+
|
|
243
|
+
#### Notion — Fetch specific page
|
|
244
|
+
|
|
245
|
+
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.
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
RESPONSE=$(curl -s \
|
|
249
|
+
-H "Authorization: Bearer $NOTION_TOKEN" \
|
|
250
|
+
-H "Notion-Version: 2022-06-28" \
|
|
251
|
+
"https://api.notion.com/v1/pages/$PAGE_ID")
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
If the key is a short `notion-XXXXXXXX` format, query the database instead:
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
RESPONSE=$(curl -s \
|
|
258
|
+
-H "Authorization: Bearer $NOTION_TOKEN" \
|
|
259
|
+
-H "Notion-Version: 2022-06-28" \
|
|
260
|
+
-X POST \
|
|
261
|
+
"https://api.notion.com/v1/databases/$NOTION_DATABASE_ID/query" \
|
|
262
|
+
-d '{"page_size": 100}')
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Then match pages by ID prefix.
|
|
266
|
+
|
|
267
|
+
Validate the response:
|
|
268
|
+
|
|
269
|
+
- If `archived` is `true`: warn `Page is archived. Brief it anyway? [y/N]`
|
|
270
|
+
|
|
271
|
+
Fetch comments for existing brief detection:
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
COMMENTS=$(curl -s \
|
|
275
|
+
-H "Authorization: Bearer $NOTION_TOKEN" \
|
|
276
|
+
-H "Notion-Version: 2022-06-28" \
|
|
277
|
+
"https://api.notion.com/v1/comments?block_id=$PAGE_ID")
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
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`.
|
|
281
|
+
|
|
282
|
+
**Notion limitation:** Page content is stored as blocks, not a single text field. Read all child blocks and concatenate their text content for brief context.
|
|
283
|
+
|
|
284
|
+
**Notion limitation:** Comments use `rich_text` format, not markdown. When scanning for `Clancy Strategic Brief`, search for the text content within `rich_text` arrays.
|
|
285
|
+
|
|
286
|
+
#### All platforms — error handling
|
|
287
|
+
|
|
288
|
+
If the API call fails:
|
|
289
|
+
|
|
290
|
+
- 404: `{KEY} not found — check the ticket key.` Stop.
|
|
291
|
+
- 401: `Auth failed — check credentials in .clancy/.env` Stop.
|
|
292
|
+
- 403: `Permission denied — check token scopes.` Stop.
|
|
293
|
+
- 5xx / timeout: `Server error. Try again in a few minutes.` Stop.
|
|
294
|
+
- Network error: `Could not reach {platform} — check network connection.` Stop.
|
|
295
|
+
|
|
296
|
+
### Inline text mode
|
|
297
|
+
|
|
298
|
+
Use the provided text directly. No API call.
|
|
299
|
+
|
|
300
|
+
### From file mode
|
|
301
|
+
|
|
302
|
+
Read the file content. Slug derived from filename (strip extension, strip date prefix if present).
|
|
303
|
+
|
|
304
|
+
### Batch mode
|
|
305
|
+
|
|
306
|
+
Fetch N issues from the planning queue (same labels/statuses as `/clancy:plan`):
|
|
307
|
+
|
|
308
|
+
#### GitHub batch fetch
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
GITHUB_USERNAME=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" https://api.github.com/user | jq -r '.login')
|
|
312
|
+
|
|
313
|
+
RESPONSE=$(curl -s \
|
|
314
|
+
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
|
315
|
+
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
316
|
+
"https://api.github.com/repos/$GITHUB_REPO/issues?state=open&assignee=$GITHUB_USERNAME&labels=$CLANCY_LABEL_PLAN&per_page=$N")
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
Filter out PRs (entries with `pull_request` key).
|
|
320
|
+
|
|
321
|
+
#### Jira batch fetch
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
RESPONSE=$(curl -s \
|
|
325
|
+
-u "$JIRA_USER:$JIRA_API_TOKEN" \
|
|
326
|
+
-X POST \
|
|
327
|
+
-H "Content-Type: application/json" \
|
|
328
|
+
-H "Accept: application/json" \
|
|
329
|
+
"$JIRA_BASE_URL/rest/api/3/search/jql" \
|
|
330
|
+
-d '{"jql": "project=$JIRA_PROJECT_KEY AND assignee=currentUser() AND status=\"$CLANCY_PLAN_STATUS\" ORDER BY priority ASC", "maxResults": <N>, "fields": ["summary", "description", "status", "issuetype", "parent", "comment"]}')
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
`CLANCY_PLAN_STATUS` defaults to `Backlog`.
|
|
334
|
+
|
|
335
|
+
#### Linear batch fetch
|
|
336
|
+
|
|
337
|
+
```graphql
|
|
338
|
+
query {
|
|
339
|
+
viewer {
|
|
340
|
+
assignedIssues(
|
|
341
|
+
filter: {
|
|
342
|
+
state: { type: { eq: "unstarted" } }
|
|
343
|
+
team: { id: { eq: "$LINEAR_TEAM_ID" } }
|
|
344
|
+
}
|
|
345
|
+
first: $N
|
|
346
|
+
orderBy: priority
|
|
347
|
+
) {
|
|
348
|
+
nodes {
|
|
349
|
+
id
|
|
350
|
+
identifier
|
|
351
|
+
title
|
|
352
|
+
description
|
|
353
|
+
state {
|
|
354
|
+
id
|
|
355
|
+
name
|
|
356
|
+
type
|
|
357
|
+
}
|
|
358
|
+
parent {
|
|
359
|
+
id
|
|
360
|
+
identifier
|
|
361
|
+
title
|
|
362
|
+
}
|
|
363
|
+
children {
|
|
364
|
+
nodes {
|
|
365
|
+
id
|
|
366
|
+
identifier
|
|
367
|
+
title
|
|
368
|
+
state {
|
|
369
|
+
type
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
team {
|
|
374
|
+
id
|
|
375
|
+
key
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
#### Azure DevOps batch fetch
|
|
384
|
+
|
|
385
|
+
```bash
|
|
386
|
+
# Step 1: WIQL query for planning queue
|
|
387
|
+
WIQL_RESPONSE=$(curl -s \
|
|
388
|
+
-u ":$AZDO_PAT" \
|
|
389
|
+
-X POST \
|
|
390
|
+
-H "Content-Type: application/json" \
|
|
391
|
+
"https://dev.azure.com/$AZDO_ORG/$AZDO_PROJECT/_apis/wit/wiql?api-version=7.1" \
|
|
392
|
+
-d '{"query": "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"}')
|
|
393
|
+
|
|
394
|
+
# Step 2: Batch fetch work items (first N IDs)
|
|
395
|
+
IDS=$(echo "$WIQL_RESPONSE" | jq -r '.workItems[0:'$N'] | map(.id) | join(",")')
|
|
396
|
+
|
|
397
|
+
RESPONSE=$(curl -s \
|
|
398
|
+
-u ":$AZDO_PAT" \
|
|
399
|
+
-H "Accept: application/json" \
|
|
400
|
+
"https://dev.azure.com/$AZDO_ORG/$AZDO_PROJECT/_apis/wit/workitems?ids=$IDS&\$expand=relations&api-version=7.1")
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
`CLANCY_PLAN_STATUS` defaults to `New`. For each work item, fetch comments separately via `GET /_apis/wit/workitems/{id}/comments?api-version=7.1-preview.4`.
|
|
404
|
+
|
|
405
|
+
#### Shortcut batch fetch
|
|
406
|
+
|
|
407
|
+
Search for stories in the planning workflow state. Shortcut uses workflow states (not labels) for queue filtering, but labels can further narrow results.
|
|
408
|
+
|
|
409
|
+
- `SHORTCUT_WORKFLOW` — optional workflow ID. If not set, use the default workflow.
|
|
410
|
+
- `CLANCY_LABEL_PLAN` — optional label name to filter stories (falls back to `CLANCY_PLAN_LABEL`).
|
|
411
|
+
|
|
412
|
+
```bash
|
|
413
|
+
RESPONSE=$(curl -s \
|
|
414
|
+
-H "Shortcut-Token: $SHORTCUT_API_TOKEN" \
|
|
415
|
+
-H "Content-Type: application/json" \
|
|
416
|
+
-X POST \
|
|
417
|
+
"https://api.app.shortcut.com/api/v3/stories/search" \
|
|
418
|
+
-d '{"owner_ids": ["<current member UUID>"], "workflow_state_types": ["backlog"], "page_size": <N>}')
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
To resolve the current member UUID:
|
|
422
|
+
|
|
423
|
+
```bash
|
|
424
|
+
MEMBER=$(curl -s \
|
|
425
|
+
-H "Shortcut-Token: $SHORTCUT_API_TOKEN" \
|
|
426
|
+
"https://api.app.shortcut.com/api/v3/member-info")
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
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`).
|
|
430
|
+
|
|
431
|
+
For each story, fetch comments separately:
|
|
432
|
+
|
|
433
|
+
```bash
|
|
434
|
+
COMMENTS=$(curl -s \
|
|
435
|
+
-H "Shortcut-Token: $SHORTCUT_API_TOKEN" \
|
|
436
|
+
"https://api.app.shortcut.com/api/v3/stories/$STORY_ID/comments")
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
Map fields: title = `name`, description = `description` (markdown), parent = `epic_id` (resolve via `GET /api/v3/epics/$EPIC_ID`), labels = `labels[].name`.
|
|
440
|
+
|
|
441
|
+
#### Notion batch fetch
|
|
442
|
+
|
|
443
|
+
Query the database with status and assignee filters:
|
|
444
|
+
|
|
445
|
+
- `CLANCY_NOTION_STATUS` — the status property name (configurable, defaults auto-detected from database schema)
|
|
446
|
+
- `CLANCY_PLAN_STATUS` — the status value to filter on (defaults to `Backlog` if not set)
|
|
447
|
+
- `CLANCY_NOTION_ASSIGNEE` — the assignee property name (configurable)
|
|
448
|
+
- `CLANCY_LABEL_PLAN` — optional label/tag to filter on (via multi-select property `CLANCY_NOTION_LABELS`)
|
|
449
|
+
|
|
450
|
+
```bash
|
|
451
|
+
RESPONSE=$(curl -s \
|
|
452
|
+
-H "Authorization: Bearer $NOTION_TOKEN" \
|
|
453
|
+
-H "Notion-Version: 2022-06-28" \
|
|
454
|
+
-X POST \
|
|
455
|
+
"https://api.notion.com/v1/databases/$NOTION_DATABASE_ID/query" \
|
|
456
|
+
-d '{"filter": {"and": [{"property": "$CLANCY_NOTION_STATUS", "status": {"equals": "$CLANCY_PLAN_STATUS"}}, ...]}, "page_size": <N>}')
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
Add assignee and label filters to the `and` array as needed. Notion filters use property-specific types (`status`, `people`, `multi_select`).
|
|
460
|
+
|
|
461
|
+
For each page, fetch comments separately:
|
|
462
|
+
|
|
463
|
+
```bash
|
|
464
|
+
COMMENTS=$(curl -s \
|
|
465
|
+
-H "Authorization: Bearer $NOTION_TOKEN" \
|
|
466
|
+
-H "Notion-Version: 2022-06-28" \
|
|
467
|
+
"https://api.notion.com/v1/comments?block_id=$PAGE_ID")
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
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.
|
|
471
|
+
|
|
472
|
+
**Notion limitation:** Comments use `rich_text` format, not markdown. When scanning for `Clancy Strategic Brief`, search for the text content within `rich_text` arrays.
|
|
473
|
+
|
|
474
|
+
If no tickets found:
|
|
475
|
+
|
|
476
|
+
```
|
|
477
|
+
No tickets in the planning queue. Check your queue label/status configuration.
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
Stop.
|
|
481
|
+
|
|
482
|
+
For batch mode, process each ticket sequentially through Steps 4-10. Skip tickets that already have a brief (check `.clancy/briefs/`). Batch mode always uses AI-grill (no human interaction per ticket).
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
|
|
486
|
+
## Step 4 — Grill phase
|
|
487
|
+
|
|
488
|
+
The grill phase is the most critical part of the brief workflow. Its purpose is to walk every branch of the design tree, resolving ambiguity upfront rather than encoding it into vague tickets.
|
|
489
|
+
|
|
490
|
+
### Mode detection
|
|
491
|
+
|
|
492
|
+
```
|
|
493
|
+
--afk flag passed? -> AI-GRILL
|
|
494
|
+
CLANCY_MODE=afk in env? -> AI-GRILL
|
|
495
|
+
Batch mode (N tickets)? -> AI-GRILL
|
|
496
|
+
Otherwise -> HUMAN GRILL
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
The `--afk` flag takes precedence over `CLANCY_MODE`.
|
|
500
|
+
|
|
501
|
+
### Human grill
|
|
502
|
+
|
|
503
|
+
Interview the user RELENTLESSLY about every aspect of the idea until you reach a shared understanding.
|
|
504
|
+
|
|
505
|
+
**Core principle** (from Matt Pocock's "grill me" skill):
|
|
506
|
+
|
|
507
|
+
> "Interview me relentlessly about every aspect of this plan until we reach a shared understanding. Walk down each branch of the design tree, resolving dependencies between decisions one by one. If a question can be answered by exploring the codebase, explore the codebase instead."
|
|
508
|
+
|
|
509
|
+
**Rules:**
|
|
510
|
+
|
|
511
|
+
1. Be RELENTLESS. Do not accept vague answers. If the user says "it should be fast", ask "what's the latency budget? 100ms? 500ms? Per-request or p99?" If they say "just pick something", explain the trade-offs and make them choose.
|
|
512
|
+
|
|
513
|
+
2. For each question, **provide your recommended answer** based on codebase context, board context, or best practices. The user can agree, disagree, or ask for more detail. This speeds up the grill — the user confirms or overrides rather than researching from scratch.
|
|
514
|
+
|
|
515
|
+
3. Walk each branch of the design tree to its CONCLUSION before moving to the next. Don't jump between topics — follow each thread until it's fully resolved.
|
|
516
|
+
|
|
517
|
+
4. Explore the codebase instead of asking when the answer is in the code. Don't ask "do you have an auth module?" — check. Then ask informed follow-ups: "I see `src/auth/sso-provider.ts` uses SAML. Should the new feature use the same provider?"
|
|
518
|
+
|
|
519
|
+
5. This is a TWO-WAY conversation. The user can ask questions back at any time:
|
|
520
|
+
- "What does the codebase currently use?" → explore and answer
|
|
521
|
+
- "What do other projects typically do?" → web research
|
|
522
|
+
- "Are there related tickets?" → board query
|
|
523
|
+
- "What would you recommend?" → give an informed opinion with trade-offs, then let the user decide
|
|
524
|
+
|
|
525
|
+
6. Answers spawn follow-up questions (multi-round): "We want SSO" → "SAML or OIDC?" → "OIDC" → "Which provider? No OIDC client in codebase yet."
|
|
526
|
+
|
|
527
|
+
7. Do NOT generate the brief until the grill is complete. The goal is ZERO AMBIGUITY before a single ticket is written. Push back if the user tries to rush: "We still have open questions about X and Y. Let's resolve those first."
|
|
528
|
+
|
|
529
|
+
8. Stop when you reach a SHARED UNDERSTANDING — both sides agree they understand the full scope, constraints, and decisions. Not just "no more questions" but genuine mutual comprehension.
|
|
530
|
+
|
|
531
|
+
9. The resolved answers feed into the `## Discovery` section of the brief.
|
|
532
|
+
|
|
533
|
+
**Question categories:**
|
|
534
|
+
|
|
535
|
+
- **Scope:** What's in and what's out?
|
|
536
|
+
- **Users:** Who uses this? What are the personas?
|
|
537
|
+
- **Constraints:** Performance budget? Browser support? Auth?
|
|
538
|
+
- **Edge cases:** What happens when X is empty / fails / times out?
|
|
539
|
+
- **Dependencies:** Does this depend on other in-flight work?
|
|
540
|
+
- **Existing code:** How does this interact with `{module}`?
|
|
541
|
+
- **Data:** What's the data model? Volume? Retention?
|
|
542
|
+
- **Security:** Who can access this? What's the auth boundary?
|
|
543
|
+
- **Observability:** How will you know if this breaks?
|
|
544
|
+
|
|
545
|
+
Typical: 5-20 clarifying questions over 2-5 rounds.
|
|
546
|
+
|
|
547
|
+
### AI-grill
|
|
548
|
+
|
|
549
|
+
Same relentless energy as the human grill, but directed at the strategist itself via a devil's advocate agent.
|
|
550
|
+
|
|
551
|
+
1. Generate 10-15 clarifying questions using the same categories as the human grill (scope, users, constraints, edge cases, dependencies, existing code, data, security, observability).
|
|
552
|
+
|
|
553
|
+
2. Spawn the devil's advocate agent via the Agent tool, passing:
|
|
554
|
+
- The idea text (ticket title + description, or inline text, or file content)
|
|
555
|
+
- The 10-15 generated questions
|
|
556
|
+
- The path to the agent prompt: `src/agents/devils-advocate.md`
|
|
557
|
+
|
|
558
|
+
3. The devil's advocate agent answers each question by INTERROGATING ITS SOURCES:
|
|
559
|
+
- **Codebase:** explore affected areas, read `.clancy/docs/`, check existing patterns. Don't assume — look.
|
|
560
|
+
- **Board:** parent ticket, related tickets, existing children. Check for conflicting requirements.
|
|
561
|
+
- **Web:** when the question involves external technology, patterns, or third-party integrations. Same trigger as Step 6: `--research` flag forces it, otherwise judgement-based.
|
|
562
|
+
|
|
563
|
+
4. The agent CHALLENGES ITS OWN ANSWERS. If the codebase says one thing but the ticket description says another, flag the conflict. If a question can be partially answered, answer the part it can and flag the rest. Do NOT accept vague self-answers — if the codebase doesn't clearly support a decision, don't guess.
|
|
564
|
+
|
|
565
|
+
5. Answers may spawn SELF-FOLLOW-UPS within the same pass: "Should this support SSO?" → checks codebase → finds `src/auth/sso-provider.ts` → "SSO exists, but it's SAML. Should the new feature use SAML or add OIDC?" → checks ticket description → no mention → checks web → "OIDC is the modern standard" → resolves as OIDC with caveat. All resolved in one pass.
|
|
566
|
+
|
|
567
|
+
6. Single pass — no multi-round loop with the human. But the agent must be thorough enough in one pass that a second would add nothing.
|
|
568
|
+
|
|
569
|
+
7. The agent NEVER asks the human questions (that defeats `--afk` mode). Unresolvable questions go to `## Open Questions` for the PO to address during brief review.
|
|
570
|
+
|
|
571
|
+
8. Classify each question:
|
|
572
|
+
- **Answerable** (>80% confidence, or technical decision with clear codebase precedent) → `## Discovery` with source tag
|
|
573
|
+
- **Conflicting evidence** (codebase says X, ticket says Y) → `## Open Questions` with conflict noted
|
|
574
|
+
- **Not answerable** (business decision, ambiguous requirements, no codebase precedent, involves money/legal/compliance/security policy) → `## Open Questions` for PO
|
|
575
|
+
|
|
576
|
+
Typical: 10-15 questions, 8-12 resolved, 2-4 open.
|
|
577
|
+
|
|
578
|
+
### Output from both modes
|
|
579
|
+
|
|
580
|
+
Both grill modes produce a `## Discovery` section and an `## Open Questions` section. Each Q&A in Discovery includes a source tag:
|
|
581
|
+
|
|
582
|
+
```
|
|
583
|
+
## Discovery
|
|
584
|
+
|
|
585
|
+
Q: Should we support system preference detection?
|
|
586
|
+
A: Yes — the codebase already uses `prefers-color-scheme` in
|
|
587
|
+
`src/styles/media.ts`. (Source: codebase)
|
|
588
|
+
|
|
589
|
+
Q: Should dark mode persist across sessions?
|
|
590
|
+
A: Yes, store in localStorage. User confirmed. (Source: human)
|
|
591
|
+
|
|
592
|
+
Q: What's the industry standard for dark mode colour contrast?
|
|
593
|
+
A: WCAG AA requires 4.5:1 ratio for normal text. (Source: web)
|
|
594
|
+
|
|
595
|
+
## Open Questions
|
|
596
|
+
- [ ] Should dark mode apply to emails/PDFs or just the web UI?
|
|
597
|
+
- [ ] Should portal users see all org data or only their team's?
|
|
598
|
+
(No RBAC policy found in codebase or ticket — needs PO input)
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
Source tags: `(Source: human)`, `(Source: codebase)`, `(Source: board)`, `(Source: web)`
|
|
602
|
+
|
|
603
|
+
---
|
|
604
|
+
|
|
605
|
+
## Step 5 — Auto-detect existing brief
|
|
606
|
+
|
|
607
|
+
Scan `.clancy/briefs/` for an existing brief matching this idea:
|
|
608
|
+
|
|
609
|
+
- **Board ticket:** match by ticket key in the `**Source:**` line
|
|
610
|
+
- **Inline text / file:** match by slug in the filename
|
|
611
|
+
|
|
612
|
+
| Condition | Behaviour |
|
|
613
|
+
| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
|
|
614
|
+
| No existing brief | Continue to Step 6 (fresh brief) |
|
|
615
|
+
| Existing brief + `--fresh` flag | Delete old file, continue to Step 6 |
|
|
616
|
+
| Existing brief + feedback found | Revise: read existing brief + all feedback, generate revised brief with `### Changes From Previous Brief` section |
|
|
617
|
+
| Existing brief + no feedback + no `--fresh` | Stop: `Already briefed. Add feedback to revise, or use --fresh to start over.` |
|
|
618
|
+
|
|
619
|
+
### Feedback detection (3 sources, checked in order)
|
|
620
|
+
|
|
621
|
+
1. **Local brief file** — check for `## Feedback` section appended to `.clancy/briefs/{date}-{slug}.md`
|
|
622
|
+
2. **Companion file** — check for `.clancy/briefs/{date}-{slug}.feedback.md`
|
|
623
|
+
3. **Board comments** (board-sourced only) — fetch ALL comments on the source ticket. Scan each comment body for the text `Clancy Strategic Brief` (case-insensitive, match anywhere in the body — it may appear as `# Clancy Strategic Brief`, `## Clancy Strategic Brief`, or just the text). The most recent matching comment is the brief. Collect all comments posted AFTER it as feedback.
|
|
624
|
+
|
|
625
|
+
Board comment feedback filtering (all platforms): collect all comments posted AFTER the brief comment. Exclude any comment that itself contains `Clancy Strategic Brief` (case-insensitive) — these are Clancy-generated brief postings, not human feedback. All other post-brief comments are treated as feedback regardless of author, since Clancy posts using the user's own credentials.
|
|
626
|
+
|
|
627
|
+
Merge order: local `## Feedback` section first, then `.feedback.md` file, then board comments (chronological). All passed to generation step as additional context.
|
|
628
|
+
|
|
629
|
+
### Edge cases
|
|
630
|
+
|
|
631
|
+
- **Board comment exists but local file is missing:** Re-download the brief from the board comment into `.clancy/briefs/`. Then check for feedback normally.
|
|
632
|
+
- **Local file exists but board comment was deleted:** Use local feedback only. No board feedback to read.
|
|
633
|
+
- **Multiple brief comments on same ticket:** Use the MOST RECENT (latest timestamp) as the reference point for feedback detection.
|
|
634
|
+
|
|
635
|
+
---
|
|
636
|
+
|
|
637
|
+
## Step 6 — Relevance check
|
|
638
|
+
|
|
639
|
+
Read `.clancy/docs/STACK.md` and `ARCHITECTURE.md` (if they exist). Compare the idea's domain against the codebase technology stack.
|
|
640
|
+
|
|
641
|
+
If the idea is clearly irrelevant (targets a platform or technology completely outside the codebase):
|
|
642
|
+
|
|
643
|
+
```
|
|
644
|
+
Skipping — this idea targets {platform}, but this codebase is {stack}.
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
Log: `YYYY-MM-DD HH:MM | BRIEF | {slug} | SKIPPED — not relevant ({reason})`
|
|
648
|
+
Stop.
|
|
649
|
+
|
|
650
|
+
If the idea mentions a technology not listed in STACK.md, flag it as a concern but do NOT skip — include a note in the brief's Technical Considerations section.
|
|
651
|
+
|
|
652
|
+
---
|
|
653
|
+
|
|
654
|
+
## Step 7 — Research (adaptive agents)
|
|
655
|
+
|
|
656
|
+
Assess complexity from the idea title + description:
|
|
657
|
+
|
|
658
|
+
| Complexity | Agents | Trigger |
|
|
659
|
+
| ------------------------------------------ | ----------------- | ------------ |
|
|
660
|
+
| Narrow (single feature, few files) | 1 codebase agent | Simple scope |
|
|
661
|
+
| Moderate (multi-component, clear boundary) | 2 codebase agents | Medium scope |
|
|
662
|
+
| Broad (cross-cutting, multiple subsystems) | 3 codebase agents | Large scope |
|
|
663
|
+
|
|
664
|
+
Web research agent (adds 1 to the count above, max 4 total):
|
|
665
|
+
|
|
666
|
+
- `--research` flag → always add web agent
|
|
667
|
+
- Idea involves new/external technology → add web agent (judgement-based)
|
|
668
|
+
- Internal refactor → no web agent
|
|
669
|
+
|
|
670
|
+
### What agents explore
|
|
671
|
+
|
|
672
|
+
- `.clancy/docs/` — STACK.md, ARCHITECTURE.md, CONVENTIONS.md, TESTING.md, DESIGN-SYSTEM.md
|
|
673
|
+
- Affected code areas via Glob + Read
|
|
674
|
+
- Board for duplicates/related tickets (text-match against title):
|
|
675
|
+
- **GitHub:** `GET /repos/$GITHUB_REPO/issues?state=open&per_page=30` and text-match
|
|
676
|
+
- **Jira:** `POST /rest/api/3/search/jql` with `summary ~ "keywords"`
|
|
677
|
+
- **Linear:** `issues(filter: ...)` by text search
|
|
678
|
+
- **Azure DevOps:** `POST /_apis/wit/wiql` with `[System.Title] CONTAINS 'keywords'`
|
|
679
|
+
- **Shortcut:** `POST /api/v3/stories/search` with `{"query": "keywords"}`
|
|
680
|
+
- **Notion:** `POST /v1/databases/$NOTION_DATABASE_ID/query` with title text filter
|
|
681
|
+
- Existing children of the source ticket (board-sourced only):
|
|
682
|
+
- **GitHub:** scan open issues for `Epic: #{parent}` in body
|
|
683
|
+
- **Jira:** `POST /rest/api/3/search/jql` with `parent = {KEY}`
|
|
684
|
+
- **Linear:** already included in the fetch response (`children.nodes`)
|
|
685
|
+
- **Azure DevOps:** check `relations` array for `System.LinkTypes.Hierarchy-Forward` entries
|
|
686
|
+
- **Shortcut:** check `story_links` array for related stories; if `epic_id` is set, fetch epic stories via `GET /api/v3/epics/$EPIC_ID/stories`
|
|
687
|
+
- **Notion:** check `relation` properties on the page for linked child pages
|
|
688
|
+
- Web research (if triggered)
|
|
689
|
+
|
|
690
|
+
Display per-agent progress:
|
|
691
|
+
|
|
692
|
+
```
|
|
693
|
+
Researching...
|
|
694
|
+
Agent 1: Codebase structure ✅
|
|
695
|
+
Agent 2: Testing patterns ✅
|
|
696
|
+
Agent 3: Web research ✅ (3 sources)
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
---
|
|
700
|
+
|
|
701
|
+
## Step 8 — Generate brief
|
|
702
|
+
|
|
703
|
+
Using all gathered context (idea, grill output, research findings), generate the brief in this exact template:
|
|
704
|
+
|
|
705
|
+
```markdown
|
|
706
|
+
# Clancy Strategic Brief
|
|
707
|
+
|
|
708
|
+
**Source:** {source — see below}
|
|
709
|
+
**Date:** {YYYY-MM-DD}
|
|
710
|
+
**Status:** Draft
|
|
711
|
+
|
|
712
|
+
---
|
|
713
|
+
|
|
714
|
+
## Problem Statement
|
|
715
|
+
|
|
716
|
+
{2-4 sentences: what problem does this solve and why does it matter?}
|
|
717
|
+
|
|
718
|
+
## Goals
|
|
719
|
+
|
|
720
|
+
- {Specific, measurable goal}
|
|
721
|
+
- {Specific, measurable goal}
|
|
722
|
+
|
|
723
|
+
## Non-Goals
|
|
724
|
+
|
|
725
|
+
- {What is explicitly out of scope}
|
|
726
|
+
- {What is explicitly out of scope}
|
|
727
|
+
|
|
728
|
+
## Discovery
|
|
729
|
+
|
|
730
|
+
{Q&A pairs from the grill phase, each with source tag — see Step 4 output format}
|
|
731
|
+
|
|
732
|
+
## Background Research
|
|
733
|
+
|
|
734
|
+
{Findings from codebase exploration and web research. Include file paths, patterns found, and external references.}
|
|
735
|
+
|
|
736
|
+
## Related Existing Work
|
|
737
|
+
|
|
738
|
+
{Existing tickets, PRs, or code that overlaps with this idea. If the source ticket has children, list them here. "None found" if clean.}
|
|
739
|
+
|
|
740
|
+
## User Stories
|
|
741
|
+
|
|
742
|
+
- As a {persona}, I want to {action} so that {outcome}.
|
|
743
|
+
- As a {persona}, I want to {action} so that {outcome}.
|
|
744
|
+
- As a {persona}, I want to {action} so that {outcome}.
|
|
745
|
+
|
|
746
|
+
## Technical Considerations
|
|
747
|
+
|
|
748
|
+
- {Architectural decisions, patterns to follow, constraints}
|
|
749
|
+
- {Integration points, migration needs, backwards compatibility}
|
|
750
|
+
- {Performance, security, accessibility considerations}
|
|
751
|
+
|
|
752
|
+
## Ticket Decomposition
|
|
753
|
+
|
|
754
|
+
| # | Title | Description | Size | Deps | Mode |
|
|
755
|
+
| --- | ---------------------- | --------------- | ---- | ---- | ---- |
|
|
756
|
+
| 1 | {Vertical slice title} | {1-2 sentences} | S | — | AFK |
|
|
757
|
+
| 2 | {Vertical slice title} | {1-2 sentences} | M | #1 | AFK |
|
|
758
|
+
| 3 | {Vertical slice title} | {1-2 sentences} | M | #1 | HITL |
|
|
759
|
+
|
|
760
|
+
## Open Questions
|
|
761
|
+
|
|
762
|
+
- [ ] {Unresolved question from grill phase — with reason}
|
|
763
|
+
- [ ] {Unresolved question — needs PO input}
|
|
764
|
+
|
|
765
|
+
## Success Criteria
|
|
766
|
+
|
|
767
|
+
- [ ] {Specific, testable criterion for the entire initiative}
|
|
768
|
+
- [ ] {Specific, testable criterion}
|
|
769
|
+
|
|
770
|
+
## Risks
|
|
771
|
+
|
|
772
|
+
- {Specific risk and mitigation strategy}
|
|
773
|
+
- {Specific risk and mitigation strategy}
|
|
774
|
+
|
|
775
|
+
---
|
|
776
|
+
|
|
777
|
+
_Generated by [Clancy](https://github.com/Pushedskydiver/chief-clancy). To answer open questions or request changes: comment on the source ticket or add a ## Feedback section to the brief file, then re-run `/clancy:brief` to revise. To approve: `/clancy:approve-brief`. To start over: `/clancy:brief --fresh`._
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
### Source field format
|
|
781
|
+
|
|
782
|
+
- **Board ticket:** `[{KEY}] {Title}` (e.g. `[#50] Redesign settings page`, `[PROJ-200] Add customer portal`, `[ENG-42] Add real-time notifications`)
|
|
783
|
+
- **Inline text:** `"{text}"` (e.g. `"Add dark mode support"`)
|
|
784
|
+
- **From file:** `{path}` (e.g. `docs/rfcs/auth-rework.md`)
|
|
785
|
+
|
|
786
|
+
### Ticket Decomposition rules
|
|
787
|
+
|
|
788
|
+
1. **Max 10 tickets.** If the idea needs more, note "Consider splitting this initiative into multiple briefs."
|
|
789
|
+
2. **Vertical slices only.** Each ticket must cut through all layers needed to deliver one thin, working piece of functionality end-to-end. If a ticket title mentions only one layer (e.g. "Set up database schema", "Create React components"), restructure it into a slice that delivers observable behaviour.
|
|
790
|
+
3. **Dependencies** reference other tickets in the table by `#N` (e.g. `#1`, `#1, #3`). Use `—` for no dependencies.
|
|
791
|
+
4. **Size:** S (< 1 hour, few files), M (1-4 hours, moderate), L (4+ hours, significant).
|
|
792
|
+
5. **Mode:**
|
|
793
|
+
- `AFK` — ticket can be implemented autonomously by `/clancy:implement` or `/clancy:autopilot`
|
|
794
|
+
- `HITL` — ticket requires human judgement, approval, or input (design decisions, credentials, external service setup, UX review, ambiguous requirements)
|
|
795
|
+
6. Every ticket must trace to at least one user story.
|
|
796
|
+
|
|
797
|
+
### User Story rules
|
|
798
|
+
|
|
799
|
+
- Write 3-8 user stories per brief (more = scope too large)
|
|
800
|
+
- Each story must be testable (implies acceptance criteria)
|
|
801
|
+
- Use the format: `As a {persona}, I want to {action} so that {outcome}.`
|
|
802
|
+
|
|
803
|
+
### Re-brief revision
|
|
804
|
+
|
|
805
|
+
If revising from feedback (Step 5):
|
|
806
|
+
|
|
807
|
+
1. **Cross-reference feedback against Open Questions.** For each Open Question in the existing brief, check if the feedback contains an answer (exact or paraphrased — match by intent, not syntax). Resolved questions move to `## Discovery` with `(Source: human)` tag. Unresolved questions stay in `## Open Questions`.
|
|
808
|
+
|
|
809
|
+
2. **Apply all other feedback** — changes to scope, goals, decomposition, user stories, etc.
|
|
810
|
+
|
|
811
|
+
3. **Prepend a section** before Problem Statement:
|
|
812
|
+
|
|
813
|
+
```markdown
|
|
814
|
+
### Changes From Previous Brief
|
|
815
|
+
|
|
816
|
+
{What feedback was addressed and how the brief changed.
|
|
817
|
+
List resolved open questions explicitly.}
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
---
|
|
821
|
+
|
|
822
|
+
## Step 9 — Save locally
|
|
823
|
+
|
|
824
|
+
Write to `.clancy/briefs/{YYYY-MM-DD}-{slug}.md`.
|
|
825
|
+
|
|
826
|
+
**Slug generation:**
|
|
827
|
+
|
|
828
|
+
- **Board ticket:** derive from title — lowercase, replace non-alphanumeric with hyphens, trim, truncate to 50 chars. E.g. `add-customer-portal`.
|
|
829
|
+
- **Inline text:** derive from the text — same rules.
|
|
830
|
+
- **From file:** derive from filename — strip extension, strip date prefix if present.
|
|
831
|
+
|
|
832
|
+
**Slug collision:** If file already exists, append `-2`, `-3`, etc.
|
|
833
|
+
|
|
834
|
+
**Create `.clancy/briefs/` directory** if it does not exist.
|
|
835
|
+
|
|
836
|
+
---
|
|
837
|
+
|
|
838
|
+
## Step 10 — Post to board
|
|
839
|
+
|
|
840
|
+
Only for board-sourced briefs (ticket key was provided). Inline text and file briefs are local only.
|
|
841
|
+
|
|
842
|
+
### GitHub — POST comment
|
|
843
|
+
|
|
844
|
+
```bash
|
|
845
|
+
curl -s \
|
|
846
|
+
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
|
847
|
+
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
848
|
+
-X POST \
|
|
849
|
+
"https://api.github.com/repos/$GITHUB_REPO/issues/$ISSUE_NUMBER/comments" \
|
|
850
|
+
-d '{"body": "<full brief markdown>"}'
|
|
851
|
+
```
|
|
852
|
+
|
|
853
|
+
GitHub accepts Markdown directly.
|
|
854
|
+
|
|
855
|
+
### Jira — POST comment
|
|
856
|
+
|
|
857
|
+
```bash
|
|
858
|
+
curl -s \
|
|
859
|
+
-u "$JIRA_USER:$JIRA_API_TOKEN" \
|
|
860
|
+
-X POST \
|
|
861
|
+
-H "Content-Type: application/json" \
|
|
862
|
+
-H "Accept: application/json" \
|
|
863
|
+
"$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY/comment" \
|
|
864
|
+
-d '<ADF JSON body>'
|
|
865
|
+
```
|
|
866
|
+
|
|
867
|
+
Construct ADF (Atlassian Document Format) JSON for the comment body:
|
|
868
|
+
|
|
869
|
+
- `## Heading` → `heading` node (level 2)
|
|
870
|
+
- `### Heading` → `heading` node (level 3)
|
|
871
|
+
- `- bullet` → `bulletList > listItem > paragraph`
|
|
872
|
+
- `| table |` → `table > tableRow > tableCell`
|
|
873
|
+
- `**bold**` → marks: `[{ "type": "strong" }]`
|
|
874
|
+
- `` `code` `` → marks: `[{ "type": "code" }]`
|
|
875
|
+
|
|
876
|
+
If ADF construction is too complex for a particular element, fall back to wrapping that section in a `codeBlock` node.
|
|
877
|
+
|
|
878
|
+
Comment marker heading: `## Clancy Strategic Brief` (H2 ADF heading node).
|
|
879
|
+
|
|
880
|
+
### Linear — commentCreate mutation
|
|
881
|
+
|
|
882
|
+
```bash
|
|
883
|
+
curl -s \
|
|
884
|
+
-X POST \
|
|
885
|
+
-H "Content-Type: application/json" \
|
|
886
|
+
-H "Authorization: $LINEAR_API_KEY" \
|
|
887
|
+
"https://api.linear.app/graphql" \
|
|
888
|
+
-d '{"query": "mutation { commentCreate(input: { issueId: \"$ISSUE_ID\", body: \"<full brief markdown>\" }) { success } }"}'
|
|
889
|
+
```
|
|
890
|
+
|
|
891
|
+
Linear accepts Markdown directly. Comment marker heading: `# Clancy Strategic Brief`.
|
|
892
|
+
|
|
893
|
+
Note: Linear personal API keys do NOT use `Bearer` prefix.
|
|
894
|
+
|
|
895
|
+
### Azure DevOps — POST comment
|
|
896
|
+
|
|
897
|
+
```bash
|
|
898
|
+
curl -s \
|
|
899
|
+
-u ":$AZDO_PAT" \
|
|
900
|
+
-X POST \
|
|
901
|
+
-H "Content-Type: application/json" \
|
|
902
|
+
"https://dev.azure.com/$AZDO_ORG/$AZDO_PROJECT/_apis/wit/workitems/$WORK_ITEM_ID/comments?api-version=7.1-preview.4" \
|
|
903
|
+
-d '{"text": "<brief as HTML>"}'
|
|
904
|
+
```
|
|
905
|
+
|
|
906
|
+
Azure DevOps comments use **HTML**, not markdown. Convert the brief markdown to HTML (same rules as planner: headings → `<h2>/<h3>`, bullets → `<ul><li>`, bold → `<strong>`, code → `<code>`, tables → `<table>`). If HTML construction is too complex, wrap in `<pre>` tags as fallback.
|
|
907
|
+
|
|
908
|
+
Comment marker heading: `<h2>Clancy Strategic Brief</h2>`.
|
|
909
|
+
|
|
910
|
+
### Shortcut — POST comment
|
|
911
|
+
|
|
912
|
+
```bash
|
|
913
|
+
curl -s \
|
|
914
|
+
-H "Shortcut-Token: $SHORTCUT_API_TOKEN" \
|
|
915
|
+
-H "Content-Type: application/json" \
|
|
916
|
+
-X POST \
|
|
917
|
+
"https://api.app.shortcut.com/api/v3/stories/$STORY_ID/comments" \
|
|
918
|
+
-d '{"text": "<full brief markdown>"}'
|
|
919
|
+
```
|
|
920
|
+
|
|
921
|
+
Shortcut accepts Markdown directly in comment text.
|
|
922
|
+
|
|
923
|
+
Comment marker heading: `# Clancy Strategic Brief`.
|
|
924
|
+
|
|
925
|
+
### Notion — POST comment
|
|
926
|
+
|
|
927
|
+
```bash
|
|
928
|
+
curl -s \
|
|
929
|
+
-H "Authorization: Bearer $NOTION_TOKEN" \
|
|
930
|
+
-H "Notion-Version: 2022-06-28" \
|
|
931
|
+
-X POST \
|
|
932
|
+
"https://api.notion.com/v1/comments" \
|
|
933
|
+
-d '{"parent": {"page_id": "$PAGE_ID"}, "rich_text": [{"type": "text", "text": {"content": "<brief text>"}}]}'
|
|
934
|
+
```
|
|
935
|
+
|
|
936
|
+
**Notion limitation:** Comments use `rich_text` blocks, not markdown. For the brief content, use a single `text` block with the full brief as plain text. Notion will render it without markdown formatting. For better readability, consider splitting the brief into multiple `rich_text` blocks (one per section) with `annotations` for bold headings.
|
|
937
|
+
|
|
938
|
+
**Notion limitation:** The `rich_text` array has a **2000-character limit per text block**. If the brief 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.
|
|
939
|
+
|
|
940
|
+
Comment marker: include `Clancy Strategic Brief` as the first text content in the `rich_text` array.
|
|
941
|
+
|
|
942
|
+
### On failure (any platform)
|
|
943
|
+
|
|
944
|
+
```
|
|
945
|
+
⚠️ Failed to post brief comment on {KEY}. Brief saved locally at .clancy/briefs/{file}. Paste it manually.
|
|
946
|
+
```
|
|
947
|
+
|
|
948
|
+
Continue — do not stop. The local file is the source of truth.
|
|
949
|
+
|
|
950
|
+
---
|
|
951
|
+
|
|
952
|
+
## Step 10a — Apply pipeline label (board-sourced only)
|
|
953
|
+
|
|
954
|
+
Only for board-sourced briefs (ticket key was provided). Inline text and file briefs skip this step.
|
|
955
|
+
|
|
956
|
+
**This step is mandatory for board-sourced briefs — always apply the label.** Use `CLANCY_LABEL_BRIEF` from `.clancy/.env` if set. If not set, use `clancy:brief` as the default. Ensure the label exists on the board (create it if missing), then add it to the ticket. Also read `CLANCY_LABEL_PLAN` (default: `clancy:plan`) and `CLANCY_LABEL_BUILD` (default: `clancy:build`) for cleanup during re-briefs.
|
|
957
|
+
|
|
958
|
+
### Re-brief cleanup (`--fresh` flag)
|
|
959
|
+
|
|
960
|
+
If this is a re-brief (`--fresh`), the ticket may already have `clancy:plan` or `clancy:build` from a prior approval. Remove them first (best-effort — ignore failures):
|
|
961
|
+
|
|
962
|
+
#### GitHub
|
|
963
|
+
|
|
964
|
+
```bash
|
|
965
|
+
# Remove plan label (ignore 404)
|
|
966
|
+
curl -s \
|
|
967
|
+
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
|
968
|
+
-H "Accept: application/vnd.github+json" \
|
|
969
|
+
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
970
|
+
-X DELETE \
|
|
971
|
+
"https://api.github.com/repos/$GITHUB_REPO/issues/$ISSUE_NUMBER/labels/$(echo $CLANCY_LABEL_PLAN | jq -Rr @uri)"
|
|
972
|
+
|
|
973
|
+
# Remove build label (ignore 404)
|
|
974
|
+
curl -s \
|
|
975
|
+
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
|
976
|
+
-H "Accept: application/vnd.github+json" \
|
|
977
|
+
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
978
|
+
-X DELETE \
|
|
979
|
+
"https://api.github.com/repos/$GITHUB_REPO/issues/$ISSUE_NUMBER/labels/$(echo $CLANCY_LABEL_BUILD | jq -Rr @uri)"
|
|
980
|
+
```
|
|
981
|
+
|
|
982
|
+
#### Jira
|
|
983
|
+
|
|
984
|
+
```bash
|
|
985
|
+
# Fetch current labels, remove plan + build labels, PUT updated list
|
|
986
|
+
CURRENT_LABELS=$(curl -s \
|
|
987
|
+
-u "$JIRA_USER:$JIRA_API_TOKEN" \
|
|
988
|
+
-H "Accept: application/json" \
|
|
989
|
+
"$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY?fields=labels" | jq -r '.fields.labels')
|
|
990
|
+
|
|
991
|
+
UPDATED_LABELS=$(echo "$CURRENT_LABELS" | jq --arg plan "$CLANCY_LABEL_PLAN" --arg build "$CLANCY_LABEL_BUILD" '[.[] | select(. != $plan and . != $build)]')
|
|
992
|
+
|
|
993
|
+
curl -s \
|
|
994
|
+
-u "$JIRA_USER:$JIRA_API_TOKEN" \
|
|
995
|
+
-X PUT \
|
|
996
|
+
-H "Content-Type: application/json" \
|
|
997
|
+
"$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY" \
|
|
998
|
+
-d "{\"fields\": {\"labels\": $UPDATED_LABELS}}"
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
#### Linear
|
|
1002
|
+
|
|
1003
|
+
```bash
|
|
1004
|
+
# Fetch current label IDs, remove plan + build label IDs, update issue
|
|
1005
|
+
# Query current labels on the issue
|
|
1006
|
+
ISSUE_DATA=$(curl -s \
|
|
1007
|
+
-X POST \
|
|
1008
|
+
-H "Content-Type: application/json" \
|
|
1009
|
+
-H "Authorization: $LINEAR_API_KEY" \
|
|
1010
|
+
"https://api.linear.app/graphql" \
|
|
1011
|
+
-d '{"query": "query { issues(filter: { identifier: { eq: \"$IDENTIFIER\" } }) { nodes { id labels { nodes { id name } } } } }"}')
|
|
1012
|
+
|
|
1013
|
+
# Filter out plan + build label IDs, then issueUpdate with remaining labelIds
|
|
1014
|
+
```
|
|
1015
|
+
|
|
1016
|
+
#### Azure DevOps
|
|
1017
|
+
|
|
1018
|
+
```bash
|
|
1019
|
+
# Fetch current tags, remove plan + build tags from semicolon-delimited string
|
|
1020
|
+
CURRENT=$(curl -s \
|
|
1021
|
+
-u ":$AZDO_PAT" \
|
|
1022
|
+
-H "Accept: application/json" \
|
|
1023
|
+
"https://dev.azure.com/$AZDO_ORG/$AZDO_PROJECT/_apis/wit/workitems/$WORK_ITEM_ID?fields=System.Tags&api-version=7.1")
|
|
1024
|
+
|
|
1025
|
+
# Parse tags, remove plan + build tags, rejoin with "; " separator
|
|
1026
|
+
# E.g., "clancy:plan; clancy:build; feature" → "feature"
|
|
1027
|
+
curl -s \
|
|
1028
|
+
-u ":$AZDO_PAT" \
|
|
1029
|
+
-X PATCH \
|
|
1030
|
+
-H "Content-Type: application/json-patch+json" \
|
|
1031
|
+
"https://dev.azure.com/$AZDO_ORG/$AZDO_PROJECT/_apis/wit/workitems/$WORK_ITEM_ID?api-version=7.1" \
|
|
1032
|
+
-d '[{"op": "replace", "path": "/fields/System.Tags", "value": "<tags without plan/build>"}]'
|
|
1033
|
+
```
|
|
1034
|
+
|
|
1035
|
+
#### Shortcut
|
|
1036
|
+
|
|
1037
|
+
```bash
|
|
1038
|
+
# Fetch current story labels, filter out plan + build labels, update
|
|
1039
|
+
STORY=$(curl -s \
|
|
1040
|
+
-H "Shortcut-Token: $SHORTCUT_API_TOKEN" \
|
|
1041
|
+
"https://api.app.shortcut.com/api/v3/stories/$STORY_ID")
|
|
1042
|
+
|
|
1043
|
+
# Remove plan + build labels from the labels array, then update story:
|
|
1044
|
+
curl -s \
|
|
1045
|
+
-H "Shortcut-Token: $SHORTCUT_API_TOKEN" \
|
|
1046
|
+
-H "Content-Type: application/json" \
|
|
1047
|
+
-X PUT \
|
|
1048
|
+
"https://api.app.shortcut.com/api/v3/stories/$STORY_ID" \
|
|
1049
|
+
-d '{"labels": [labels_without_plan_and_build]}'
|
|
1050
|
+
```
|
|
1051
|
+
|
|
1052
|
+
#### Notion
|
|
1053
|
+
|
|
1054
|
+
```bash
|
|
1055
|
+
# Fetch current page properties, remove plan + build labels from multi-select
|
|
1056
|
+
PAGE=$(curl -s \
|
|
1057
|
+
-H "Authorization: Bearer $NOTION_TOKEN" \
|
|
1058
|
+
-H "Notion-Version: 2022-06-28" \
|
|
1059
|
+
"https://api.notion.com/v1/pages/$PAGE_ID")
|
|
1060
|
+
|
|
1061
|
+
# Update multi-select property without plan + build labels
|
|
1062
|
+
curl -s \
|
|
1063
|
+
-H "Authorization: Bearer $NOTION_TOKEN" \
|
|
1064
|
+
-H "Notion-Version: 2022-06-28" \
|
|
1065
|
+
-X PATCH \
|
|
1066
|
+
"https://api.notion.com/v1/pages/$PAGE_ID" \
|
|
1067
|
+
-d '{"properties": {"$CLANCY_NOTION_LABELS": {"multi_select": [options_without_plan_and_build]}}}'
|
|
1068
|
+
```
|
|
1069
|
+
|
|
1070
|
+
### Add brief label
|
|
1071
|
+
|
|
1072
|
+
Ensure the label exists and add it to the ticket. Best-effort — warn on failure, never stop.
|
|
1073
|
+
|
|
1074
|
+
#### GitHub
|
|
1075
|
+
|
|
1076
|
+
```bash
|
|
1077
|
+
# Ensure label exists (ignore 422 = already exists)
|
|
1078
|
+
curl -s \
|
|
1079
|
+
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
|
1080
|
+
-H "Accept: application/vnd.github+json" \
|
|
1081
|
+
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
1082
|
+
-H "Content-Type: application/json" \
|
|
1083
|
+
-X POST \
|
|
1084
|
+
"https://api.github.com/repos/$GITHUB_REPO/labels" \
|
|
1085
|
+
-d '{"name": "$CLANCY_LABEL_BRIEF", "color": "0075ca"}'
|
|
1086
|
+
|
|
1087
|
+
# Add label to issue
|
|
1088
|
+
curl -s \
|
|
1089
|
+
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
|
1090
|
+
-H "Accept: application/vnd.github+json" \
|
|
1091
|
+
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
1092
|
+
-H "Content-Type: application/json" \
|
|
1093
|
+
-X POST \
|
|
1094
|
+
"https://api.github.com/repos/$GITHUB_REPO/issues/$ISSUE_NUMBER/labels" \
|
|
1095
|
+
-d '{"labels": ["$CLANCY_LABEL_BRIEF"]}'
|
|
1096
|
+
```
|
|
1097
|
+
|
|
1098
|
+
#### Jira
|
|
1099
|
+
|
|
1100
|
+
```bash
|
|
1101
|
+
# Jira auto-creates labels — just add to the issue's label array
|
|
1102
|
+
CURRENT_LABELS=$(curl -s \
|
|
1103
|
+
-u "$JIRA_USER:$JIRA_API_TOKEN" \
|
|
1104
|
+
-H "Accept: application/json" \
|
|
1105
|
+
"$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY?fields=labels" | jq -r '.fields.labels')
|
|
1106
|
+
|
|
1107
|
+
UPDATED_LABELS=$(echo "$CURRENT_LABELS" | jq --arg brief "$CLANCY_LABEL_BRIEF" '. + [$brief] | unique')
|
|
1108
|
+
|
|
1109
|
+
curl -s \
|
|
1110
|
+
-u "$JIRA_USER:$JIRA_API_TOKEN" \
|
|
1111
|
+
-X PUT \
|
|
1112
|
+
-H "Content-Type: application/json" \
|
|
1113
|
+
"$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY" \
|
|
1114
|
+
-d "{\"fields\": {\"labels\": $UPDATED_LABELS}}"
|
|
1115
|
+
```
|
|
1116
|
+
|
|
1117
|
+
#### Linear
|
|
1118
|
+
|
|
1119
|
+
```bash
|
|
1120
|
+
# Ensure label exists (check team labels, workspace labels, create if missing)
|
|
1121
|
+
# Then add to issue via issueUpdate with updated labelIds array
|
|
1122
|
+
curl -s \
|
|
1123
|
+
-X POST \
|
|
1124
|
+
-H "Content-Type: application/json" \
|
|
1125
|
+
-H "Authorization: $LINEAR_API_KEY" \
|
|
1126
|
+
"https://api.linear.app/graphql" \
|
|
1127
|
+
-d '{"query": "mutation { issueLabelCreate(input: { teamId: \"$LINEAR_TEAM_ID\", name: \"$CLANCY_LABEL_BRIEF\", color: \"#0075ca\" }) { success issueLabel { id } } }"}'
|
|
1128
|
+
|
|
1129
|
+
# Add label to issue (fetch current labelIds, append new, issueUpdate)
|
|
1130
|
+
```
|
|
1131
|
+
|
|
1132
|
+
#### Azure DevOps
|
|
1133
|
+
|
|
1134
|
+
```bash
|
|
1135
|
+
# Fetch current tags, append brief tag
|
|
1136
|
+
CURRENT=$(curl -s \
|
|
1137
|
+
-u ":$AZDO_PAT" \
|
|
1138
|
+
-H "Accept: application/json" \
|
|
1139
|
+
"https://dev.azure.com/$AZDO_ORG/$AZDO_PROJECT/_apis/wit/workitems/$WORK_ITEM_ID?fields=System.Tags&api-version=7.1")
|
|
1140
|
+
|
|
1141
|
+
# Append brief tag to semicolon-delimited string
|
|
1142
|
+
curl -s \
|
|
1143
|
+
-u ":$AZDO_PAT" \
|
|
1144
|
+
-X PATCH \
|
|
1145
|
+
-H "Content-Type: application/json-patch+json" \
|
|
1146
|
+
"https://dev.azure.com/$AZDO_ORG/$AZDO_PROJECT/_apis/wit/workitems/$WORK_ITEM_ID?api-version=7.1" \
|
|
1147
|
+
-d '[{"op": "replace", "path": "/fields/System.Tags", "value": "<existing tags>; $CLANCY_LABEL_BRIEF"}]'
|
|
1148
|
+
```
|
|
1149
|
+
|
|
1150
|
+
#### Shortcut
|
|
1151
|
+
|
|
1152
|
+
```bash
|
|
1153
|
+
# Resolve brief label ID (create if missing)
|
|
1154
|
+
LABELS=$(curl -s \
|
|
1155
|
+
-H "Shortcut-Token: $SHORTCUT_API_TOKEN" \
|
|
1156
|
+
"https://api.app.shortcut.com/api/v3/labels")
|
|
1157
|
+
|
|
1158
|
+
# Find or create the brief label, get its ID
|
|
1159
|
+
# Then add to story (merge with existing labels):
|
|
1160
|
+
curl -s \
|
|
1161
|
+
-H "Shortcut-Token: $SHORTCUT_API_TOKEN" \
|
|
1162
|
+
-H "Content-Type: application/json" \
|
|
1163
|
+
-X PUT \
|
|
1164
|
+
"https://api.app.shortcut.com/api/v3/stories/$STORY_ID" \
|
|
1165
|
+
-d '{"labels": [{"name": "$CLANCY_LABEL_BRIEF"}, ...existing_labels]}'
|
|
1166
|
+
```
|
|
1167
|
+
|
|
1168
|
+
#### Notion
|
|
1169
|
+
|
|
1170
|
+
```bash
|
|
1171
|
+
# Fetch current page properties, add brief label to multi-select
|
|
1172
|
+
PAGE=$(curl -s \
|
|
1173
|
+
-H "Authorization: Bearer $NOTION_TOKEN" \
|
|
1174
|
+
-H "Notion-Version: 2022-06-28" \
|
|
1175
|
+
"https://api.notion.com/v1/pages/$PAGE_ID")
|
|
1176
|
+
|
|
1177
|
+
# Update multi-select property to include brief label
|
|
1178
|
+
curl -s \
|
|
1179
|
+
-H "Authorization: Bearer $NOTION_TOKEN" \
|
|
1180
|
+
-H "Notion-Version: 2022-06-28" \
|
|
1181
|
+
-X PATCH \
|
|
1182
|
+
"https://api.notion.com/v1/pages/$PAGE_ID" \
|
|
1183
|
+
-d '{"properties": {"$CLANCY_NOTION_LABELS": {"multi_select": [existing_options, {"name": "$CLANCY_LABEL_BRIEF"}]}}}'
|
|
1184
|
+
```
|
|
1185
|
+
|
|
1186
|
+
#### On failure (any platform)
|
|
1187
|
+
|
|
1188
|
+
```
|
|
1189
|
+
⚠️ Could not add pipeline label to {KEY}. The brief was saved and posted successfully — label it manually if needed.
|
|
1190
|
+
```
|
|
1191
|
+
|
|
1192
|
+
Continue — do not stop.
|
|
1193
|
+
|
|
1194
|
+
---
|
|
1195
|
+
|
|
1196
|
+
## Step 11 — Brief inventory (`--list`)
|
|
1197
|
+
|
|
1198
|
+
If `--list` flag is present, display an inventory of all briefs and stop.
|
|
1199
|
+
|
|
1200
|
+
Scan `.clancy/briefs/` for all `.md` files. For each file:
|
|
1201
|
+
|
|
1202
|
+
- Parse date from filename prefix (`YYYY-MM-DD-slug.md`)
|
|
1203
|
+
- Parse `**Source:**` line
|
|
1204
|
+
- Parse Status (check for `.approved` marker file)
|
|
1205
|
+
- Parse ticket count from decomposition table (`?` if unparseable)
|
|
1206
|
+
- Calculate age (today - date)
|
|
1207
|
+
- Check stale: unapproved + age > 7 days
|
|
1208
|
+
|
|
1209
|
+
Sort by date (newest first). Display:
|
|
1210
|
+
|
|
1211
|
+
```
|
|
1212
|
+
Clancy — Briefs
|
|
1213
|
+
================================================================
|
|
1214
|
+
|
|
1215
|
+
[1] dark-mode-support 2026-03-14 Draft 3 tickets Source: #50
|
|
1216
|
+
[2] customer-portal 2026-03-13 Approved 8 tickets Source: PROJ-200 ✅
|
|
1217
|
+
[3] real-time-notifications 2026-03-12 Draft 4 tickets Source: ENG-42
|
|
1218
|
+
[4] auth-rework 2026-03-05 Draft 6 tickets Source: file STALE (9 days)
|
|
1219
|
+
|
|
1220
|
+
3 unapproved drafts. 1 stale (>7 days).
|
|
1221
|
+
|
|
1222
|
+
To approve: /clancy:approve-brief <slug or index>
|
|
1223
|
+
To review stale briefs: open the file and add ## Feedback, or delete it.
|
|
1224
|
+
```
|
|
1225
|
+
|
|
1226
|
+
If `.clancy/briefs/` does not exist or is empty:
|
|
1227
|
+
|
|
1228
|
+
```
|
|
1229
|
+
No briefs found. Run /clancy:brief to create one.
|
|
1230
|
+
```
|
|
1231
|
+
|
|
1232
|
+
Stop.
|
|
1233
|
+
|
|
1234
|
+
---
|
|
1235
|
+
|
|
1236
|
+
## Step 12 — Display
|
|
1237
|
+
|
|
1238
|
+
Print the full brief to stdout, followed by the sign-off:
|
|
1239
|
+
|
|
1240
|
+
```
|
|
1241
|
+
"I'm going to need to ask you some questions, and I want them answered immediately."
|
|
1242
|
+
```
|
|
1243
|
+
|
|
1244
|
+
### Next steps (board-sourced)
|
|
1245
|
+
|
|
1246
|
+
```
|
|
1247
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
1248
|
+
Next Steps
|
|
1249
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
1250
|
+
|
|
1251
|
+
Answer open questions or request changes:
|
|
1252
|
+
• Comment on {KEY} on your board
|
|
1253
|
+
• Or add a ## Feedback section to the brief file
|
|
1254
|
+
Then re-run: /clancy:brief {KEY}
|
|
1255
|
+
|
|
1256
|
+
Approve: /clancy:approve-brief {KEY}
|
|
1257
|
+
Start over: /clancy:brief --fresh {KEY}
|
|
1258
|
+
```
|
|
1259
|
+
|
|
1260
|
+
### Next steps (inline text / from file)
|
|
1261
|
+
|
|
1262
|
+
```
|
|
1263
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
1264
|
+
Next Steps
|
|
1265
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
1266
|
+
|
|
1267
|
+
Answer open questions or request changes:
|
|
1268
|
+
• Add a ## Feedback section to:
|
|
1269
|
+
.clancy/briefs/{date}-{slug}.md
|
|
1270
|
+
• Or create a companion file:
|
|
1271
|
+
.clancy/briefs/{date}-{slug}.feedback.md
|
|
1272
|
+
Then re-run: /clancy:brief
|
|
1273
|
+
|
|
1274
|
+
Approve: /clancy:approve-brief {slug}
|
|
1275
|
+
With parent: /clancy:approve-brief {slug} --epic {KEY}
|
|
1276
|
+
Start over: /clancy:brief --fresh
|
|
1277
|
+
```
|
|
1278
|
+
|
|
1279
|
+
---
|
|
1280
|
+
|
|
1281
|
+
## Step 13 — Log
|
|
1282
|
+
|
|
1283
|
+
Append to `.clancy/progress.txt`:
|
|
1284
|
+
|
|
1285
|
+
| Outcome | Log entry |
|
|
1286
|
+
| ----------------------------- | -------------------------------------------------------------------------- |
|
|
1287
|
+
| Brief generated | `YYYY-MM-DD HH:MM \| BRIEF \| {slug} \| {N} proposed tickets` |
|
|
1288
|
+
| Brief revised (from feedback) | `YYYY-MM-DD HH:MM \| BRIEF \| {slug} \| REVISED - {N} proposed tickets` |
|
|
1289
|
+
| Brief skipped (not relevant) | `YYYY-MM-DD HH:MM \| BRIEF \| {slug} \| SKIPPED - not relevant ({reason})` |
|
|
1290
|
+
| Brief skipped (ticket Done) | `YYYY-MM-DD HH:MM \| BRIEF \| {slug} \| SKIPPED - ticket is Done` |
|
|
1291
|
+
| Brief skipped (not found) | `YYYY-MM-DD HH:MM \| BRIEF \| {slug} \| SKIPPED - ticket not found` |
|
|
1292
|
+
| Already briefed (no feedback) | (nothing logged) |
|
|
1293
|
+
| `--list` display | (nothing logged) |
|
|
1294
|
+
| Auth/network failure | (nothing logged) |
|
|
1295
|
+
|
|
1296
|
+
---
|
|
1297
|
+
|
|
1298
|
+
## Step 14 — Batch summary
|
|
1299
|
+
|
|
1300
|
+
After all tickets in a batch are processed, display:
|
|
1301
|
+
|
|
1302
|
+
```
|
|
1303
|
+
Briefed {M} of {N} tickets. {K} skipped.
|
|
1304
|
+
|
|
1305
|
+
✅ [{KEY1}] {Title} — 4 tickets proposed
|
|
1306
|
+
✅ [{KEY2}] {Title} — 6 tickets proposed
|
|
1307
|
+
⏭️ [{KEY3}] {Title} — already briefed
|
|
1308
|
+
⏭️ [{KEY4}] {Title} — not relevant
|
|
1309
|
+
|
|
1310
|
+
Briefs saved to .clancy/briefs/. Run /clancy:approve-brief to create tickets.
|
|
1311
|
+
```
|
|
1312
|
+
|
|
1313
|
+
---
|
|
1314
|
+
|
|
1315
|
+
## Notes
|
|
1316
|
+
|
|
1317
|
+
- This command does NOT create tickets — it generates briefs only. Ticket creation is `/clancy:approve-brief`.
|
|
1318
|
+
- Briefs are saved locally in `.clancy/briefs/` and optionally posted as comments on the source ticket.
|
|
1319
|
+
- The grill phase is the most important part — do not skip or rush it. Zero ambiguity is the goal.
|
|
1320
|
+
- Re-running without `--fresh` auto-detects feedback: if feedback exists, revises; if no feedback, stops with guidance.
|
|
1321
|
+
- The `--fresh` flag discards the existing brief entirely and generates a new one from scratch.
|
|
1322
|
+
- The `--list` flag is an inventory display only — no brief generated, no API calls beyond the local filesystem.
|
|
1323
|
+
- Batch mode (`/clancy:brief 3`) implies AI-grill — each ticket is briefed autonomously.
|
|
1324
|
+
- All board API calls are best-effort — if a comment fails to post, print the brief and warn. The local file is the source of truth.
|
|
1325
|
+
- The `Clancy Strategic Brief` text in comments is the marker used by both `/clancy:brief` (to detect existing briefs and feedback) and `/clancy:approve-brief` (to find the brief). Search case-insensitively and match regardless of heading level (`#`, `##`, or plain text).
|
|
1326
|
+
- Jira uses ADF for comments (with `codeBlock` fallback). GitHub, Linear, and Shortcut accept Markdown directly.
|
|
1327
|
+
- Linear personal API keys do NOT use `Bearer` prefix.
|
|
1328
|
+
- Shortcut uses `Shortcut-Token` header (not `Authorization: Bearer`). API base: `https://api.app.shortcut.com/api/v3`. Stories use `name` (not `title`), `description` is markdown, dependencies use `story_links` with `blocks` verb, and workflow state transitions use `workflow_state_id`.
|
|
1329
|
+
- Notion uses `Authorization: Bearer $NOTION_TOKEN` and `Notion-Version: 2022-06-28` headers. API base: `https://api.notion.com/v1`. Comments use `rich_text` blocks (not markdown) with a 2000-character limit per text block. Page content is stored as blocks (use `GET /v1/blocks/$PAGE_ID/children`), not a single description field. Labels use multi-select properties. The Notion API does not support editing comments — post new comments instead.
|
|
1330
|
+
- Jira uses the new `POST /rest/api/3/search/jql` endpoint (old GET `/search` removed Aug 2025).
|