@chief-clancy/brief 0.1.1 → 0.2.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.
@@ -0,0 +1,370 @@
1
+ # Board Setup Workflow
2
+
3
+ ## Overview
4
+
5
+ Configure board credentials so `/clancy:brief` can fetch tickets and post briefs as comments. This workflow collects the minimum credentials needed for board access — no pipeline configuration, no role settings, no iteration limits.
6
+
7
+ Credentials are stored in `.clancy/.env` in the current project directory. They are per-project, not global.
8
+
9
+ ---
10
+
11
+ ## Step 1 — Preflight checks
12
+
13
+ ### 1. Check for full pipeline
14
+
15
+ Check if `.clancy/clancy-implement.js` exists in the project root.
16
+
17
+ If present, the full Clancy pipeline is installed. Show:
18
+
19
+ ```
20
+ Full Clancy pipeline detected. Use /clancy:settings to manage board credentials.
21
+ ```
22
+
23
+ Stop. Do not proceed with standalone board setup.
24
+
25
+ ### 2. Check for existing credentials
26
+
27
+ Check if `.clancy/.env` exists and contains board credentials (any of: `JIRA_BASE_URL`, `GITHUB_TOKEN`, `LINEAR_API_KEY`, `SHORTCUT_API_TOKEN`, `NOTION_TOKEN`, `AZDO_ORG`, `AZDO_PAT`, `AZDO_PROJECT`).
28
+
29
+ If board credentials are found, show:
30
+
31
+ ```
32
+ Existing board credentials found in .clancy/.env.
33
+
34
+ [1] Reconfigure (replace current board)
35
+ [2] Cancel
36
+ ```
37
+
38
+ If [2]: stop.
39
+ If [1]: continue to Step 2. The existing `.clancy/.env` will be updated (board-specific vars replaced, other vars preserved).
40
+
41
+ If `.clancy/.env` does not exist, or exists but has no board credentials: continue to Step 2.
42
+
43
+ ---
44
+
45
+ ## Step 2 — Board selection
46
+
47
+ Output:
48
+
49
+ ```
50
+ Which board are you using?
51
+
52
+ [1] Jira
53
+ [2] GitHub Issues
54
+ [3] Linear
55
+ [4] Shortcut
56
+ [5] Notion
57
+ [6] Azure DevOps
58
+ [7] My board isn't listed
59
+ ```
60
+
61
+ If the user selects [7], show:
62
+
63
+ ```
64
+ Clancy currently supports Jira, GitHub Issues, Linear, Shortcut, Notion, and Azure DevOps.
65
+
66
+ Your board isn't supported yet — open an issue:
67
+ github.com/Pushedskydiver/chief-clancy/issues
68
+
69
+ You can still use /clancy:brief with inline text or file input:
70
+ /clancy:brief "Add dark mode support"
71
+ /clancy:brief --from docs/rfc.md
72
+ ```
73
+
74
+ Stop.
75
+
76
+ ---
77
+
78
+ ## Step 3 — Credential collection
79
+
80
+ Ask each question individually and wait for an answer before moving to the next.
81
+
82
+ ### Jira
83
+
84
+ 1. `What's your Jira base URL? (e.g. https://your-org.atlassian.net)`
85
+ 2. `What's your Jira project key? (e.g. PROJ)`
86
+ 3. `What email address do you use to log in to Atlassian?`
87
+ 4. `Paste your Jira API token: (create one at id.atlassian.com/manage-profile/security/api-tokens)`
88
+
89
+ Store as `JIRA_BASE_URL`, `JIRA_PROJECT_KEY`, `JIRA_USER`, `JIRA_API_TOKEN`.
90
+
91
+ ### GitHub Issues
92
+
93
+ 1. `What's your GitHub repo? (owner/name, e.g. acme/my-app)`
94
+ 2. `Paste your GitHub personal access token: (needs repo scope)`
95
+
96
+ Store as `GITHUB_REPO`, `GITHUB_TOKEN`.
97
+
98
+ After collecting credentials, show:
99
+
100
+ ```
101
+ Note: Clancy only picks up GitHub Issues that have the "clancy" label applied.
102
+ Add this label to any issue you want Clancy to brief.
103
+ ```
104
+
105
+ ### Linear
106
+
107
+ 1. `Paste your Linear API key: (create one at linear.app/settings/api)`
108
+ 2. After verifying the API key (Step 4), auto-detect teams by querying `{ teams { nodes { id name } } }`.
109
+ - If exactly 1 team: use it automatically. Show `Using team: {name} ({id})`.
110
+ - If 2+ teams: show a numbered list and let the user pick.
111
+ - If the query fails or returns no teams: fall back to asking manually: `What's your Linear team ID? (find it at linear.app/settings/teams — click your team, copy the ID from the URL)`
112
+
113
+ Store as `LINEAR_API_KEY`, `LINEAR_TEAM_ID`.
114
+
115
+ ### Shortcut
116
+
117
+ 1. `Paste your Shortcut API token: (create one at app.shortcut.com/settings/account/api-tokens)`
118
+
119
+ Store as `SHORTCUT_API_TOKEN`.
120
+
121
+ ### Notion
122
+
123
+ 1. `Paste your Notion integration token: (create one at notion.so/my-integrations)`
124
+ 2. `What's your Notion database ID? (the 32-character hex string in your database URL)`
125
+
126
+ Store as `NOTION_TOKEN`, `NOTION_DATABASE_ID`.
127
+
128
+ ### Azure DevOps
129
+
130
+ 1. `What's your Azure DevOps organisation name? (e.g. your-org)`
131
+ 2. `What's your Azure DevOps project name?`
132
+ 3. `Paste your Azure DevOps personal access token: (needs Work Items Read & Write scope)`
133
+
134
+ Store as `AZDO_ORG`, `AZDO_PROJECT`, `AZDO_PAT`.
135
+
136
+ ---
137
+
138
+ ## Step 4 — Credential verification
139
+
140
+ After collecting all credentials for the chosen board, verify the connection before writing them.
141
+
142
+ ### Jira
143
+
144
+ Call `GET {JIRA_BASE_URL}/rest/api/3/project/{JIRA_PROJECT_KEY}` with basic auth (`{JIRA_USER}:{JIRA_API_TOKEN}` base64-encoded in the `Authorization: Basic` header).
145
+
146
+ On success (HTTP 200):
147
+
148
+ ```
149
+ ✅ Jira connected — project {JIRA_PROJECT_KEY} reachable.
150
+ ```
151
+
152
+ ### GitHub Issues
153
+
154
+ Call `GET https://api.github.com/repos/{GITHUB_REPO}` with `Authorization: Bearer {GITHUB_TOKEN}` and `X-GitHub-Api-Version: 2022-11-28`.
155
+
156
+ On success (HTTP 200):
157
+
158
+ ```
159
+ ✅ GitHub connected — {GITHUB_REPO} reachable.
160
+ ```
161
+
162
+ ### Linear
163
+
164
+ Call `POST https://api.linear.app/graphql` with `Authorization: {LINEAR_API_KEY}` (no Bearer prefix) and body `{"query": "{ viewer { id name } }"}`.
165
+
166
+ On success (HTTP 200 with `data.viewer`):
167
+
168
+ ```
169
+ ✅ Linear connected — {viewer.name}.
170
+ ```
171
+
172
+ ### Shortcut
173
+
174
+ Call `GET https://api.app.shortcut.com/api/v3/member-info` with `Shortcut-Token: {SHORTCUT_API_TOKEN}`.
175
+
176
+ On success (HTTP 200):
177
+
178
+ ```
179
+ ✅ Shortcut connected.
180
+ ```
181
+
182
+ ### Notion
183
+
184
+ Call `GET https://api.notion.com/v1/databases/{NOTION_DATABASE_ID}` with `Authorization: Bearer {NOTION_TOKEN}` and `Notion-Version: 2022-06-28`.
185
+
186
+ On success (HTTP 200):
187
+
188
+ ```
189
+ ✅ Notion connected — database reachable.
190
+ ```
191
+
192
+ ### Azure DevOps
193
+
194
+ Call `GET https://dev.azure.com/{AZDO_ORG}/{AZDO_PROJECT}/_apis/wit/workitemtypes?api-version=7.1` with basic auth (empty user, `AZDO_PAT` as password).
195
+
196
+ On success (HTTP 200):
197
+
198
+ ```
199
+ ✅ Azure DevOps connected — {AZDO_PROJECT} reachable.
200
+ ```
201
+
202
+ ### On failure (any board)
203
+
204
+ ```
205
+ ❌ Couldn't connect to {board} (HTTP {status}).
206
+ Check your credentials.
207
+
208
+ [1] Re-enter credentials
209
+ [2] Skip verification (save anyway)
210
+ [3] Cancel
211
+ ```
212
+
213
+ If [1]: go back to Step 3 for that board.
214
+ If [2]: save the unverified credentials and continue to Step 5.
215
+ If [3]: stop without saving.
216
+
217
+ Never silently continue with unverified credentials — the user must explicitly choose.
218
+
219
+ ---
220
+
221
+ ## Step 5 — Detect base branch
222
+
223
+ Auto-detect the default branch:
224
+
225
+ ```bash
226
+ BASE_BRANCH_REF="$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null)"
227
+ echo "${BASE_BRANCH_REF#refs/remotes/origin/}"
228
+ ```
229
+
230
+ If detection succeeds and the branch is `main` or `master`: use it silently. Store as `CLANCY_BASE_BRANCH`.
231
+
232
+ If detection fails or returns an unexpected branch name, ask:
233
+
234
+ ```
235
+ What's your base branch? [main]
236
+ ```
237
+
238
+ ---
239
+
240
+ ## Step 6 — Write credentials
241
+
242
+ ### Create directory
243
+
244
+ Create `.clancy/` directory if it does not exist.
245
+
246
+ ### Write `.clancy/.env`
247
+
248
+ If `.clancy/.env` already exists (reconfigure path from Step 1, or exists with no board credentials):
249
+
250
+ - Preserve all existing lines (comments, blank lines, non-board vars)
251
+ - Remove any existing board-specific env vars for ALL boards (not just the new one). Board vars to remove:
252
+ - Jira: `JIRA_BASE_URL`, `JIRA_PROJECT_KEY`, `JIRA_USER`, `JIRA_API_TOKEN`, `CLANCY_JQL_STATUS`, `CLANCY_JQL_SPRINT`
253
+ - GitHub: `GITHUB_REPO`, `GITHUB_TOKEN`
254
+ - Linear: `LINEAR_API_KEY`, `LINEAR_TEAM_ID`
255
+ - Shortcut: `SHORTCUT_API_TOKEN`, `SHORTCUT_WORKFLOW`
256
+ - Notion: `NOTION_TOKEN`, `NOTION_DATABASE_ID`, `CLANCY_NOTION_STATUS`, `CLANCY_NOTION_ASSIGNEE`, `CLANCY_NOTION_LABELS`, `CLANCY_NOTION_PARENT`, `CLANCY_NOTION_TODO`
257
+ - Azure DevOps: `AZDO_ORG`, `AZDO_PROJECT`, `AZDO_PAT`, `CLANCY_AZDO_WIT`, `CLANCY_AZDO_STATUS`
258
+ - Preserve all other lines (comments, blank lines, non-board vars like `CLANCY_BASE_BRANCH`)
259
+ - Append the new board's credentials at the end
260
+ - Update `CLANCY_BASE_BRANCH` if it exists, or append it
261
+
262
+ If `.clancy/.env` does not exist, write a new file:
263
+
264
+ ```
265
+ # Clancy — Board credentials
266
+ # Configured by @chief-clancy/brief
267
+ # Do not commit this file to version control.
268
+
269
+ {BOARD_CREDENTIALS}
270
+
271
+ CLANCY_BASE_BRANCH={branch}
272
+ ```
273
+
274
+ Where `{BOARD_CREDENTIALS}` is the board-specific key=value pairs collected in Step 3. Wrap values containing spaces in double quotes.
275
+
276
+ ### Check .gitignore
277
+
278
+ Check if `.gitignore` exists and contains `.clancy/.env` (or a pattern that covers it, like `.clancy/` or `*.env`).
279
+
280
+ If not covered, show:
281
+
282
+ ```
283
+ ⚠️ Add .clancy/.env to your .gitignore to keep credentials out of version control:
284
+
285
+ echo '.clancy/.env' >> .gitignore
286
+ ```
287
+
288
+ ---
289
+
290
+ ## Step 7 — Completion
291
+
292
+ Show the board-specific success message:
293
+
294
+ ### Jira
295
+
296
+ ```
297
+ Board credentials configured for Jira ({JIRA_PROJECT_KEY}).
298
+
299
+ You can now brief from Jira tickets:
300
+ /clancy:brief PROJ-123
301
+
302
+ Credentials are stored in .clancy/.env (this project only).
303
+ To reconfigure: /clancy:board-setup
304
+ For the full pipeline: npx chief-clancy
305
+ ```
306
+
307
+ ### GitHub Issues
308
+
309
+ ```
310
+ Board credentials configured for GitHub Issues ({GITHUB_REPO}).
311
+
312
+ You can now brief from GitHub issues:
313
+ /clancy:brief #42
314
+
315
+ Credentials are stored in .clancy/.env (this project only).
316
+ To reconfigure: /clancy:board-setup
317
+ For the full pipeline: npx chief-clancy
318
+ ```
319
+
320
+ ### Linear
321
+
322
+ ```
323
+ Board credentials configured for Linear.
324
+
325
+ You can now brief from Linear issues:
326
+ /clancy:brief ENG-42
327
+
328
+ Credentials are stored in .clancy/.env (this project only).
329
+ To reconfigure: /clancy:board-setup
330
+ For the full pipeline: npx chief-clancy
331
+ ```
332
+
333
+ ### Shortcut
334
+
335
+ ```
336
+ Board credentials configured for Shortcut.
337
+
338
+ You can now brief from Shortcut stories:
339
+ /clancy:brief SC-123
340
+
341
+ Credentials are stored in .clancy/.env (this project only).
342
+ To reconfigure: /clancy:board-setup
343
+ For the full pipeline: npx chief-clancy
344
+ ```
345
+
346
+ ### Notion
347
+
348
+ ```
349
+ Board credentials configured for Notion.
350
+
351
+ You can now brief from Notion pages:
352
+ /clancy:brief notion-XXXXXXXX
353
+
354
+ Credentials are stored in .clancy/.env (this project only).
355
+ To reconfigure: /clancy:board-setup
356
+ For the full pipeline: npx chief-clancy
357
+ ```
358
+
359
+ ### Azure DevOps
360
+
361
+ ```
362
+ Board credentials configured for Azure DevOps ({AZDO_PROJECT}).
363
+
364
+ You can now brief from Azure DevOps work items:
365
+ /clancy:brief 42
366
+
367
+ Credentials are stored in .clancy/.env (this project only).
368
+ To reconfigure: /clancy:board-setup
369
+ For the full pipeline: npx chief-clancy
370
+ ```
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Overview
4
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 — in **terminal mode**, use `/clancy:approve-brief` for that. In **standalone mode**, install the full pipeline (`npx chief-clancy`) to create tickets from briefs.
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 — in **terminal mode**, use `/clancy:approve-brief` for that. In **standalone mode** or **standalone+board mode**, install the full pipeline (`npx chief-clancy`) to create tickets from briefs.
6
6
 
7
7
  ---
8
8
 
@@ -12,12 +12,17 @@ Research an idea, interrogate it thoroughly, and generate a structured strategic
12
12
 
13
13
  Check for `.clancy/.env`:
14
14
 
15
- - **Present** → **terminal mode**. Full Clancy installation detected.
16
- - **Absent** → **standalone mode**. Running from `@chief-clancy/brief` only.
15
+ - **Absent** → **standalone mode**. No board credentials. Board ticket and batch modes are blocked.
16
+ - **Present** → continue to `.clancy/clancy-implement.js` check below.
17
17
 
18
- ### 2. Terminal-mode preflight (skip in standalone mode)
18
+ If `.clancy/.env` is present, check for `.clancy/clancy-implement.js`:
19
19
 
20
- If `.clancy/.env` is present:
20
+ - **Present** **terminal mode**. Full Clancy pipeline installed.
21
+ - **Absent** → **standalone+board mode**. Board credentials available via `/clancy:board-setup`. Board ticket mode works. Steps 10/10a work. But `/clancy:approve-brief` is not available (requires full pipeline).
22
+
23
+ ### 2. Terminal-mode preflight (skip in standalone mode and standalone+board mode)
24
+
25
+ If in **terminal mode** (`.clancy/.env` present AND `.clancy/clancy-implement.js` present):
21
26
 
22
27
  a. Source `.clancy/.env` and check board credentials are present.
23
28
 
@@ -90,8 +95,9 @@ If `--list` is present (with or without other arguments), jump to Step 11 (Brief
90
95
  If running in **standalone mode** (Step 1 detected no `.clancy/.env`) and the resolved input mode is **board ticket** or **batch mode**:
91
96
 
92
97
  ```
93
- Board credentials not found. To brief from a board ticket, install the
94
- full Clancy pipeline: npx chief-clancy
98
+ Board credentials not found. To brief from a board ticket:
99
+ /clancy:board-setup — configure board credentials (standalone)
100
+ npx chief-clancy — install the full pipeline
95
101
 
96
102
  For now, use:
97
103
  /clancy:brief "Add dark mode" — inline text
@@ -100,6 +106,8 @@ For now, use:
100
106
 
101
107
  Stop.
102
108
 
109
+ In **standalone+board mode**, board ticket and batch modes proceed normally — credentials are available.
110
+
103
111
  ---
104
112
 
105
113
  ## Step 3 — Gather idea (mode-specific)
@@ -855,7 +863,7 @@ Write to `.clancy/briefs/{YYYY-MM-DD}-{slug}.md`.
855
863
 
856
864
  ## Step 10 — Post to board
857
865
 
858
- Only for board-sourced briefs in **terminal mode** (ticket key was provided AND `.clancy/.env` is present). In **standalone mode**, skip this step and Step 10a entirely — the local file in `.clancy/briefs/` is the source of truth.
866
+ Only for board-sourced briefs when board credentials are available (terminal mode or standalone+board mode). In **standalone mode** (no `.clancy/.env`), skip this step and Step 10a entirely — the local file in `.clancy/briefs/` is the source of truth.
859
867
 
860
868
  Inline text and file briefs are also local only — skip this step regardless of mode.
861
869
 
@@ -971,7 +979,7 @@ Continue — do not stop. The local file is the source of truth.
971
979
 
972
980
  ## Step 10a — Apply pipeline label (board-sourced only)
973
981
 
974
- Only for board-sourced briefs in **terminal mode** (ticket key was provided AND `.clancy/.env` is present). In **standalone mode**, skip this step entirely (see Step 10 guard). Inline text and file briefs also skip this step.
982
+ Only for board-sourced briefs when board credentials are available (terminal mode or standalone+board mode). In **standalone mode** (no `.clancy/.env`), skip this step entirely (see Step 10 guard). Inline text and file briefs also skip this step. Note: in standalone+board mode, this step creates a `clancy:brief` label on the board — this is expected behaviour when credentials are configured.
975
983
 
976
984
  **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.
977
985
 
@@ -1240,7 +1248,7 @@ Clancy — Briefs
1240
1248
  3 unapproved drafts. 1 stale (>7 days).
1241
1249
 
1242
1250
  To approve (terminal mode): /clancy:approve-brief <slug or index>
1243
- To approve (standalone): npx chief-clancy, then /clancy:approve-brief
1251
+ To approve (standalone/standalone+board): npx chief-clancy, then /clancy:approve-brief
1244
1252
  To review stale briefs: open the file and add ## Feedback, or delete it.
1245
1253
  ```
1246
1254
 
@@ -1275,7 +1283,7 @@ Next Steps
1275
1283
  Then re-run: /clancy:brief {KEY}
1276
1284
 
1277
1285
  Approve: /clancy:approve-brief {KEY} (terminal mode)
1278
- Full pipeline: npx chief-clancy (if standalone)
1286
+ Full pipeline: npx chief-clancy (if not terminal mode)
1279
1287
  Start over: /clancy:brief --fresh {KEY}
1280
1288
  ```
1281
1289
 
@@ -1295,7 +1303,7 @@ Next Steps
1295
1303
 
1296
1304
  Approve: /clancy:approve-brief {slug} (terminal mode)
1297
1305
  With parent: /clancy:approve-brief {slug} --epic {KEY}
1298
- Full pipeline: npx chief-clancy (if standalone)
1306
+ Full pipeline: npx chief-clancy (if not terminal mode)
1299
1307
  Start over: /clancy:brief --fresh
1300
1308
  ```
1301
1309
 
@@ -1330,7 +1338,7 @@ Briefed {M} of {N} tickets. {K} skipped.
1330
1338
  ⏭️ [{KEY3}] {Title} — already briefed
1331
1339
  ⏭️ [{KEY4}] {Title} — not relevant
1332
1340
 
1333
- Briefs saved to .clancy/briefs/. To create tickets: /clancy:approve-brief (terminal mode) or install the full pipeline first (npx chief-clancy).
1341
+ Briefs saved to .clancy/briefs/. To create tickets: /clancy:approve-brief (requires full pipeline npx chief-clancy).
1334
1342
  ```
1335
1343
 
1336
1344
  ---
@@ -11,7 +11,7 @@ import { describe, expect, it } from 'vitest';
11
11
 
12
12
  const WORKFLOWS_DIR = fileURLToPath(new URL('.', import.meta.url));
13
13
 
14
- const EXPECTED_WORKFLOWS = ['brief.md'];
14
+ const EXPECTED_WORKFLOWS = ['approve-brief.md', 'board-setup.md', 'brief.md'];
15
15
 
16
16
  describe('workflows directory structure', () => {
17
17
  it('contains exactly the expected workflow files', () => {
@@ -43,15 +43,104 @@ describe('workflows directory structure', () => {
43
43
  expect(content).toContain('## Step 1');
44
44
  expect(content).toContain('.clancy/briefs/');
45
45
  });
46
+
47
+ it('approve-brief workflow has structural step markers', () => {
48
+ const content = readFileSync(
49
+ new URL('approve-brief.md', import.meta.url),
50
+ 'utf8',
51
+ );
52
+
53
+ expect(content).toContain('## Step 1');
54
+ expect(content).toContain('.clancy/briefs/');
55
+ });
46
56
  });
47
57
 
48
- describe('standalone mode adaptations', () => {
58
+ // ---------------------------------------------------------------------------
59
+ // board-setup.md content assertions
60
+ // ---------------------------------------------------------------------------
61
+
62
+ describe('board-setup workflow', () => {
63
+ const content = readFileSync(
64
+ new URL('board-setup.md', import.meta.url),
65
+ 'utf8',
66
+ );
67
+
68
+ it('checks for full pipeline before proceeding', () => {
69
+ expect(content).toContain('clancy-implement.js');
70
+ expect(content).toContain('/clancy:settings');
71
+ });
72
+
73
+ it('checks for existing board credentials', () => {
74
+ expect(content).toContain('Existing board credentials found');
75
+ expect(content).toContain('Reconfigure');
76
+ });
77
+
78
+ it('offers all 6 supported boards', () => {
79
+ expect(content).toContain('Jira');
80
+ expect(content).toContain('GitHub Issues');
81
+ expect(content).toContain('Linear');
82
+ expect(content).toContain('Shortcut');
83
+ expect(content).toContain('Notion');
84
+ expect(content).toContain('Azure DevOps');
85
+ });
86
+
87
+ it('includes credential prompts for each board', () => {
88
+ expect(content).toContain('JIRA_BASE_URL');
89
+ expect(content).toContain('GITHUB_TOKEN');
90
+ expect(content).toContain('LINEAR_API_KEY');
91
+ expect(content).toContain('SHORTCUT_API_TOKEN');
92
+ expect(content).toContain('NOTION_TOKEN');
93
+ expect(content).toContain('AZDO_PAT');
94
+ });
95
+
96
+ it('includes credential verification for each board', () => {
97
+ expect(content).toContain('Jira connected');
98
+ expect(content).toContain('GitHub connected');
99
+ expect(content).toContain('Linear connected');
100
+ expect(content).toContain('Shortcut connected');
101
+ expect(content).toContain('Notion connected');
102
+ expect(content).toContain('Azure DevOps connected');
103
+ });
104
+
105
+ it('offers re-enter, skip, and cancel on verification failure', () => {
106
+ expect(content).toContain('Re-enter credentials');
107
+ expect(content).toContain('Skip verification');
108
+ expect(content).toContain('Cancel');
109
+ });
110
+
111
+ it('warns about .gitignore', () => {
112
+ expect(content).toContain('.gitignore');
113
+ expect(content).toContain('.clancy/.env');
114
+ });
115
+
116
+ it('notes credentials are per-project', () => {
117
+ expect(content).toContain('this project only');
118
+ });
119
+
120
+ it('includes brief header comment in env file', () => {
121
+ expect(content).toContain('Configured by @chief-clancy/brief');
122
+ });
123
+ });
124
+
125
+ // ---------------------------------------------------------------------------
126
+ // brief.md content assertions
127
+ // ---------------------------------------------------------------------------
128
+
129
+ describe('three-state mode detection', () => {
49
130
  const content = readFileSync(new URL('brief.md', import.meta.url), 'utf8');
50
131
 
51
- it('Step 1 uses standalone/terminal mode detection', () => {
132
+ it('Step 1 detects three installation states', () => {
52
133
  expect(content).toContain('standalone mode');
134
+ expect(content).toContain('standalone+board mode');
53
135
  expect(content).toContain('terminal mode');
54
- expect(content).toContain('Detect installation context');
136
+ });
137
+
138
+ it('Step 1 checks .clancy/.env for credentials', () => {
139
+ expect(content).toContain('.clancy/.env');
140
+ });
141
+
142
+ it('Step 1 checks clancy-implement.js for terminal detection', () => {
143
+ expect(content).toContain('clancy-implement.js');
55
144
  });
56
145
 
57
146
  it('Step 1 does not hard-stop on missing .clancy/.env', () => {
@@ -60,19 +149,26 @@ describe('standalone mode adaptations', () => {
60
149
  );
61
150
  });
62
151
 
63
- it('includes standalone board-ticket guard', () => {
152
+ it('CLANCY_ROLES check only runs in terminal mode', () => {
153
+ expect(content).toContain('Terminal-mode preflight');
154
+ expect(content).toContain(
155
+ 'skip in standalone mode and standalone+board mode',
156
+ );
157
+ });
158
+
159
+ it('standalone guard mentions /clancy:board-setup', () => {
64
160
  expect(content).toContain('Standalone board-ticket guard');
65
161
  expect(content).toContain('Board credentials not found');
66
- expect(content).toContain('npx chief-clancy');
162
+ expect(content).toContain('/clancy:board-setup');
67
163
  });
68
164
 
69
- it('Step 10 includes standalone skip guard', () => {
165
+ it('Step 10 runs when board credentials are available', () => {
70
166
  expect(content).toContain(
71
- 'In **standalone mode**, skip this step and Step 10a entirely',
167
+ 'when board credentials are available (terminal mode or standalone+board mode)',
72
168
  );
73
169
  });
74
170
 
75
- it('Step 10a includes standalone skip back-reference', () => {
171
+ it('Step 10a runs when board credentials are available', () => {
76
172
  expect(content).toContain('see Step 10 guard');
77
173
  });
78
174
 
@@ -81,7 +177,7 @@ describe('standalone mode adaptations', () => {
81
177
  expect(content).not.toContain('src/agents/devils-advocate.md');
82
178
  });
83
179
 
84
- it('approve-brief references include standalone context', () => {
180
+ it('approve-brief references include standalone guidance', () => {
85
181
  expect(content).not.toMatch(/that is `\/clancy:approve-brief`\./);
86
182
  expect(content).toContain('npx chief-clancy');
87
183
  });