@gallopsystems/agent-skills 1.0.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/README.md +137 -0
- package/package.json +26 -0
- package/plugins/doctl/.claude-plugin/plugin.json +8 -0
- package/plugins/doctl/skills/doctl/SKILL.md +93 -0
- package/plugins/kysely-postgres/.claude-plugin/plugin.json +8 -0
- package/plugins/kysely-postgres/skills/kysely-postgres/SKILL.md +1101 -0
- package/plugins/kysely-postgres/skills/kysely-postgres/references/aggregations.ts +167 -0
- package/plugins/kysely-postgres/skills/kysely-postgres/references/ctes.ts +165 -0
- package/plugins/kysely-postgres/skills/kysely-postgres/references/expressions.ts +272 -0
- package/plugins/kysely-postgres/skills/kysely-postgres/references/joins.ts +206 -0
- package/plugins/kysely-postgres/skills/kysely-postgres/references/json-arrays.ts +398 -0
- package/plugins/kysely-postgres/skills/kysely-postgres/references/mutations.ts +199 -0
- package/plugins/kysely-postgres/skills/kysely-postgres/references/orderby-pagination.ts +117 -0
- package/plugins/kysely-postgres/skills/kysely-postgres/references/relations.ts +176 -0
- package/plugins/kysely-postgres/skills/kysely-postgres/references/select-where.ts +146 -0
- package/plugins/linear/.claude-plugin/plugin.json +8 -0
- package/plugins/linear/skills/linear/SKILL.md +1040 -0
- package/plugins/linear/skills/linear/bin/linear.mjs +1228 -0
- package/plugins/linear/skills/linear/tech-stack.md +273 -0
- package/plugins/nitro-testing/.claude-plugin/plugin.json +8 -0
- package/plugins/nitro-testing/skills/nitro-testing/SKILL.md +497 -0
- package/plugins/nitro-testing/skills/nitro-testing/async-testing.md +270 -0
- package/plugins/nitro-testing/skills/nitro-testing/ci-setup.md +226 -0
- package/plugins/nitro-testing/skills/nitro-testing/examples/global-setup.ts +90 -0
- package/plugins/nitro-testing/skills/nitro-testing/examples/handler.test.ts +167 -0
- package/plugins/nitro-testing/skills/nitro-testing/examples/setup.ts +29 -0
- package/plugins/nitro-testing/skills/nitro-testing/examples/test-utils-index.ts +297 -0
- package/plugins/nitro-testing/skills/nitro-testing/examples/vitest.config.ts +42 -0
- package/plugins/nitro-testing/skills/nitro-testing/factories.md +278 -0
- package/plugins/nitro-testing/skills/nitro-testing/frontend-testing.md +512 -0
- package/plugins/nitro-testing/skills/nitro-testing/test-utils.md +262 -0
- package/plugins/nitro-testing/skills/nitro-testing/transaction-rollback.md +183 -0
- package/plugins/nitro-testing/skills/nitro-testing/vitest-config.md +236 -0
- package/plugins/nuxt-nitro-api/.claude-plugin/plugin.json +8 -0
- package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/SKILL.md +260 -0
- package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/auth-patterns.md +228 -0
- package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/composables-utils.md +174 -0
- package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/deep-linking.md +190 -0
- package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/auth-middleware.ts +32 -0
- package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/auth-utils.ts +51 -0
- package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/deep-link-page.vue +61 -0
- package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/service-util.ts +63 -0
- package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/sse-endpoint.ts +59 -0
- package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/validation-endpoint.ts +38 -0
- package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/fetch-patterns.md +178 -0
- package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/nitro-tasks.md +243 -0
- package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/page-structure.md +162 -0
- package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/server-services.md +238 -0
- package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/sse.md +221 -0
- package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/ssr-client.md +166 -0
- package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/validation.md +131 -0
- package/scripts/link-skills.mjs +252 -0
|
@@ -0,0 +1,1040 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: linear
|
|
3
|
+
description: Create, triage, and manage Linear issues at Gallop Systems following the team's workflow conventions — cycle placement, issue templates, project/milestone hierarchy, and project refresh / cycle rebalance procedures. Use whenever the user asks for Linear work (creating issues, planning cycles, refreshing projects) on a Gallop client.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Linear Project Management — Team Workflow & CLI Guide
|
|
7
|
+
|
|
8
|
+
## The CLI
|
|
9
|
+
|
|
10
|
+
The fallback tooling is a single zero-dependency Node script, `bin/linear.mjs`. It runs on bare `node` (v18.3+ — no `npm install`, no `tsx`, no build step) and uses symbolic names instead of raw UUIDs (`--state todo`, `--assignee frontend`, `--labels bug,frontend`, `--cycle current`).
|
|
11
|
+
|
|
12
|
+
Invoke it as `node <skill>/bin/linear.mjs <command> [args] [--flags]`. Examples below write `node linear.mjs` for brevity — use the full path to the file, or `cd` into the skill's `bin/` directory first. Run `node linear.mjs help` for the full command list.
|
|
13
|
+
|
|
14
|
+
## First-time Setup (run once per user)
|
|
15
|
+
|
|
16
|
+
### Check 1 — Workspace bootstrap config exists
|
|
17
|
+
|
|
18
|
+
Before running any `linear.mjs` command, verify that the per-user workspace config exists at `~/.config/linctl/workspace.json` (override path with `$LINCTL_WORKSPACE_FILE`). This file holds the team UUID, the Linear member UUIDs that play the Frontend/PM and Backend roles, and the workflow-state and label UUIDs the CLI resolves symbolic names against. Without it, every command that needs the team, members, states, or labels will refuse to run.
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
[ -f "${LINCTL_WORKSPACE_FILE:-$HOME/.config/linctl/workspace.json}" ] && echo "ok" || echo "missing"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**If missing,** instruct the user to run:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
node linear.mjs init
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`init` calls Linear's GraphQL API, lists the workspace's members, and prompts the user to designate (1) the Frontend/PM lead and (2) the Backend lead by number. It then writes `~/.config/linctl/workspace.json`. The config is read fresh on every invocation — no re-sourcing needed.
|
|
31
|
+
|
|
32
|
+
### Check 2 — Linear MCP server installed and authorized
|
|
33
|
+
|
|
34
|
+
This skill routes most operations through `mcp__linear-server__*` tools. Before doing any Linear work, verify the MCP server is available:
|
|
35
|
+
|
|
36
|
+
- **Not installed:** if no `mcp__linear-server__*` tools appear in your toolset, stop and tell the user: *"This skill needs Linear's MCP server. Install it with `claude mcp add --transport sse linear https://mcp.linear.app/sse`, restart Claude Code, then tell me to continue."* Don't try to fall back to `linear.mjs` for everything — the CLI only covers a small subset of operations.
|
|
37
|
+
- **Installed but not authorized:** if a `mcp__linear-server__*` call returns an auth/OAuth error, tell the user: *"The Linear MCP server is installed but not authorized. The next call will open a browser to sign in — please complete OAuth, then tell me to continue."*
|
|
38
|
+
|
|
39
|
+
Don't silently skip these checks. A user who hits an MCP error mid-task without context will be confused.
|
|
40
|
+
|
|
41
|
+
### Check 3 — `LINEAR_API_KEY` for the CLI
|
|
42
|
+
|
|
43
|
+
`linear.mjs` reads `LINEAR_API_KEY` from the environment. Before using any command, check whether it's set:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
[ -n "$LINEAR_API_KEY" ] && echo "set" || echo "missing"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**If missing, onboard the user:**
|
|
50
|
+
|
|
51
|
+
1. Tell them: *"I need a Linear personal API key to run the CLI. Create one at https://linear.app/settings/account/security (click 'New API key', name it 'Claude Code', copy the `lin_api_...` token), then paste it here in chat."*
|
|
52
|
+
2. When they paste the key, install it into `~/.zshenv` so every future shell — including the ones Claude Code spawns — picks it up automatically:
|
|
53
|
+
```bash
|
|
54
|
+
echo 'export LINEAR_API_KEY=lin_api_THEIR_KEY_HERE' >> ~/.zshenv
|
|
55
|
+
```
|
|
56
|
+
(Use `~/.bashrc` instead if the user is on bash.)
|
|
57
|
+
3. Export it in the current shell too so the next tool call works without restart:
|
|
58
|
+
```bash
|
|
59
|
+
export LINEAR_API_KEY=lin_api_THEIR_KEY_HERE
|
|
60
|
+
```
|
|
61
|
+
4. Verify with a harmless call: `node linear.mjs list-members`.
|
|
62
|
+
|
|
63
|
+
**Never commit the key, never write it into `.env` or any project file** — `~/.zshenv` is the single source of truth.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Team Overview
|
|
68
|
+
|
|
69
|
+
- **Workspace Team Key:** `GAL`
|
|
70
|
+
- **Team Size:** 2 members
|
|
71
|
+
- **Sprint Cycle:** 2 weeks
|
|
72
|
+
- **Stack:** Nuxt 4 (Vue frontend + Nitro backend)
|
|
73
|
+
- **Work Type:** Client/agency projects
|
|
74
|
+
|
|
75
|
+
### Team Roles
|
|
76
|
+
|
|
77
|
+
| Role | Responsibilities |
|
|
78
|
+
|------|-----------------|
|
|
79
|
+
| **Frontend/PM Lead** | Frontend development, requirement gathering, project design, client communication, light backend (e.g., adding endpoints), issue triage, client IT coordination (DNS, infrastructure requests) |
|
|
80
|
+
| **Backend Lead** | Data modeling, database design, backend architecture, API logic |
|
|
81
|
+
|
|
82
|
+
The Frontend/PM lead triages incoming client requests and translates them into Linear issues.
|
|
83
|
+
|
|
84
|
+
On first run, `node linear.mjs init` binds these roles to specific Linear members; Claude reads `~/.config/linctl/workspace.json` to know who they are. Pass `--assignee frontend` or `--assignee backend` and the CLI resolves it to the corresponding Linear user UUID.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Workflow Statuses
|
|
89
|
+
|
|
90
|
+
| Status | Meaning |
|
|
91
|
+
|--------|---------|
|
|
92
|
+
| **Backlog** | Captured but not yet planned for a cycle |
|
|
93
|
+
| **Todo** | Committed to the current or next cycle |
|
|
94
|
+
| **In Progress** | Actively being worked on |
|
|
95
|
+
| **In Review** | Code complete, awaiting review or client feedback |
|
|
96
|
+
| **Done** | Shipped and verified |
|
|
97
|
+
| **Canceled** | Dropped or no longer relevant |
|
|
98
|
+
|
|
99
|
+
> **Note:** "In Review" is a recommended addition to the default Linear statuses. It provides a clear handoff point for code review between the two team members and for client sign-off.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Priority Levels
|
|
104
|
+
|
|
105
|
+
| Priority | When to Use |
|
|
106
|
+
|----------|-------------|
|
|
107
|
+
| **Urgent** | Production issues, client-blocking bugs, deadline-critical items |
|
|
108
|
+
| **High** | Current sprint commitments, important client deliverables |
|
|
109
|
+
| **Medium** | Planned work, non-blocking improvements |
|
|
110
|
+
| **Low** | Nice-to-haves, tech debt, internal tooling |
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Labels (Recommended)
|
|
115
|
+
|
|
116
|
+
### By Type
|
|
117
|
+
- `bug` — Something is broken
|
|
118
|
+
- `feature` — New functionality
|
|
119
|
+
- `improvement` — Enhancement to existing functionality
|
|
120
|
+
- `chore` — Maintenance, config, devops, dependencies
|
|
121
|
+
- `spike` — Research or investigation task
|
|
122
|
+
|
|
123
|
+
### By Domain
|
|
124
|
+
- `frontend` — UI/UX, Vue components, pages, styling
|
|
125
|
+
- `backend` — API, database, data modeling, server logic
|
|
126
|
+
- `fullstack` — Touches both frontend and backend
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Estimation (T-Shirt Sizes)
|
|
131
|
+
|
|
132
|
+
| Size | Meaning | Rough Effort |
|
|
133
|
+
|------|---------|-------------|
|
|
134
|
+
| **S** | Small, well-understood task | A few hours |
|
|
135
|
+
| **M** | Medium complexity, clear scope | Half a day to a full day |
|
|
136
|
+
| **L** | Large, may span multiple days | 2–3 days |
|
|
137
|
+
| **XL** | Very large — consider breaking down | 3+ days, likely needs subtasks |
|
|
138
|
+
|
|
139
|
+
If an issue is XL, break it into smaller sub-issues before starting work.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Sprint Cycle Process
|
|
144
|
+
|
|
145
|
+
### Cycle Start (Every 2 Weeks)
|
|
146
|
+
1. Review **Backlog** — pull items into **Todo** for the cycle
|
|
147
|
+
2. Assign issues to the appropriate team member based on domain (backend vs. frontend)
|
|
148
|
+
3. Ensure each issue has: priority, estimate, label(s), and assignee
|
|
149
|
+
4. Keep cycle scope realistic — a 2-person team should commit to what's achievable
|
|
150
|
+
|
|
151
|
+
### During the Cycle
|
|
152
|
+
- Move issues to **In Progress** when you start working on them
|
|
153
|
+
- Move to **In Review** when code is ready for review or client feedback
|
|
154
|
+
- Move to **Done** when merged/deployed and verified
|
|
155
|
+
- If scope changes, add new issues to **Backlog** unless they're urgent
|
|
156
|
+
|
|
157
|
+
### Cycle End
|
|
158
|
+
- Review what got done vs. what was planned
|
|
159
|
+
- Move incomplete **Todo** / **In Progress** items to the next cycle or back to **Backlog**
|
|
160
|
+
- Archive the completed cycle
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Linear Tooling — MCP First, `linear.mjs` as Fallback
|
|
165
|
+
|
|
166
|
+
**Default to the Linear MCP server (`mcp__linear__*` tools)** for all standard operations: creating/updating issues, listing projects/milestones/cycles/initiatives/labels/users, comments, etc. The MCP tools take strings directly — pass real markdown with real newlines, no JSON-escaping.
|
|
167
|
+
|
|
168
|
+
**Use `linear.mjs` only for things MCP doesn't expose:**
|
|
169
|
+
- `cycle-capacity` — velocity-based capacity % (used in cycle placement & rebalancing)
|
|
170
|
+
- `batch-move-to-cycle` / `batch-move-to-milestone` — rate-limit-aware bulk moves
|
|
171
|
+
- `add-dependency` / `remove-dependency` / `list-dependencies` — issue relations
|
|
172
|
+
- `add-initiative-link` — adding external links to initiatives
|
|
173
|
+
- `api` — raw GraphQL escape hatch
|
|
174
|
+
|
|
175
|
+
**Mapping** of common CLI → MCP equivalents lives in `MEMORY.md`. The sections below document the CLI for the fallback paths and for reference; prefer the MCP tool whenever one exists.
|
|
176
|
+
|
|
177
|
+
### The CLI
|
|
178
|
+
|
|
179
|
+
`bin/linear.mjs` wraps the Linear GraphQL API. It runs on bare `node` (v18.3+, no install) and reads `LINEAR_API_KEY` from the environment plus the workspace config from `~/.config/linctl/workspace.json`. There is nothing to source — every invocation loads config fresh.
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
node linear.mjs help # full command list
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Symbolic names (no UUIDs needed)
|
|
186
|
+
|
|
187
|
+
The CLI resolves friendly names against `workspace.json`, so you rarely need raw UUIDs:
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
--state todo | backlog | "in progress" | "in review" | done | canceled (or a UUID)
|
|
191
|
+
--assignee frontend | backend (or a UUID)
|
|
192
|
+
--labels bug,frontend,feature (comma-separated label names) (or UUIDs)
|
|
193
|
+
--priority 0-4 or none | urgent | high | medium | low
|
|
194
|
+
--cycle current (the active cycle) (or a UUID)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Any value that's already a UUID is passed through untouched. Project and milestone IDs are still UUIDs (pass them with `--project` / `--milestone`).
|
|
198
|
+
|
|
199
|
+
### Creating Issues
|
|
200
|
+
|
|
201
|
+
> **Important:** When assigning an issue to a cycle, always set `--state todo`. Issues default to Backlog, which doesn't work with cycles — they must be in Todo status.
|
|
202
|
+
>
|
|
203
|
+
> **Required placement rule:** Never create an issue without both `--project` and `--milestone`. If the right project does not exist, create it first. If the project exists but the right milestone does not, create the milestone first. Do not leave issues unscoped or unmilestoned.
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
# --state todo is required when using --cycle
|
|
207
|
+
# --project and --milestone are always required
|
|
208
|
+
node linear.mjs create-issue \
|
|
209
|
+
--title 'Add user profile page' \
|
|
210
|
+
--description 'Create /profile page with user info and settings' \
|
|
211
|
+
--priority high \
|
|
212
|
+
--state todo \
|
|
213
|
+
--assignee frontend \
|
|
214
|
+
--labels feature,frontend \
|
|
215
|
+
--estimate 3 \
|
|
216
|
+
--project 'project-uuid-here' \
|
|
217
|
+
--milestone 'milestone-uuid-here' \
|
|
218
|
+
--cycle current
|
|
219
|
+
|
|
220
|
+
# Create a bug report
|
|
221
|
+
node linear.mjs create-issue \
|
|
222
|
+
--title 'Fix: login redirect fails on Safari' \
|
|
223
|
+
--description 'Users on Safari not redirected after login. Reproduced on Safari 17.' \
|
|
224
|
+
--priority urgent \
|
|
225
|
+
--state todo \
|
|
226
|
+
--assignee frontend \
|
|
227
|
+
--labels bug,frontend \
|
|
228
|
+
--project 'project-uuid-here' \
|
|
229
|
+
--milestone 'milestone-uuid-here' \
|
|
230
|
+
--cycle current
|
|
231
|
+
|
|
232
|
+
# Long descriptions: pass a file instead of inline text (no shell-escaping)
|
|
233
|
+
node linear.mjs create-issue --title 'Investigate perf issue' --state todo \
|
|
234
|
+
--description-file ./issue-body.md \
|
|
235
|
+
--project 'project-uuid' --milestone 'milestone-uuid'
|
|
236
|
+
|
|
237
|
+
# If the project or milestone does not exist yet, create it before the issue
|
|
238
|
+
PROJECT_ID="$(node linear.mjs create-project --name '[CLIENT] Feature Area' --description 'Short description' | node -e "process.stdin.once('data',d=>console.log(JSON.parse(d).data.projectCreate.project.id))")"
|
|
239
|
+
MILESTONE_ID="$(node linear.mjs create-milestone "$PROJECT_ID" 'Phase 1' | node -e "process.stdin.once('data',d=>{const n=JSON.parse(d).data.projectMilestoneCreate.projectMilestone;console.log(n.id)})")"
|
|
240
|
+
node linear.mjs create-issue --title 'Investigate performance issue' --state todo \
|
|
241
|
+
--project "$PROJECT_ID" --milestone "$MILESTONE_ID" --cycle current
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Priority Values
|
|
245
|
+
- `0` = No priority
|
|
246
|
+
- `1` = Urgent
|
|
247
|
+
- `2` = High
|
|
248
|
+
- `3` = Medium
|
|
249
|
+
- `4` = Low
|
|
250
|
+
|
|
251
|
+
### Listing & Filtering Issues
|
|
252
|
+
```bash
|
|
253
|
+
# List all issues (pretty table by default)
|
|
254
|
+
node linear.mjs list-issues
|
|
255
|
+
|
|
256
|
+
# Filter by state type: backlog, unstarted, started, completed, canceled
|
|
257
|
+
node linear.mjs list-issues started
|
|
258
|
+
|
|
259
|
+
# List issues in current cycle
|
|
260
|
+
node linear.mjs list-cycle-issues
|
|
261
|
+
|
|
262
|
+
# Raw JSON output (for piping) — add --json to any list command
|
|
263
|
+
node linear.mjs list-issues --json
|
|
264
|
+
node linear.mjs list-issues started --json
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Updating Issues
|
|
268
|
+
```bash
|
|
269
|
+
# Move issue by status name
|
|
270
|
+
node linear.mjs move-issue "issue-uuid" "In Progress"
|
|
271
|
+
node linear.mjs move-issue "issue-uuid" "Done"
|
|
272
|
+
|
|
273
|
+
# Assign to a team member by role
|
|
274
|
+
node linear.mjs assign-issue "issue-uuid" frontend
|
|
275
|
+
node linear.mjs assign-issue "issue-uuid" backend
|
|
276
|
+
|
|
277
|
+
# General update — symbolic flags
|
|
278
|
+
node linear.mjs update-issue "issue-uuid" --priority urgent --state todo
|
|
279
|
+
|
|
280
|
+
# Or merge arbitrary raw JSON input with --raw
|
|
281
|
+
node linear.mjs update-issue "issue-uuid" --raw '{"priority":1}'
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Issue Dependencies
|
|
285
|
+
```bash
|
|
286
|
+
# Create a "blocks" dependency (backend blocks frontend)
|
|
287
|
+
node linear.mjs add-dependency "$BLOCKER_ISSUE_ID" "$BLOCKED_ISSUE_ID"
|
|
288
|
+
|
|
289
|
+
# List all dependencies for an issue (both directions)
|
|
290
|
+
node linear.mjs list-dependencies "$ISSUE_ID"
|
|
291
|
+
|
|
292
|
+
# Remove a dependency by relation UUID (get UUID from list-dependencies)
|
|
293
|
+
node linear.mjs remove-dependency "$RELATION_ID"
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Comments
|
|
297
|
+
```bash
|
|
298
|
+
# Add a comment to an issue
|
|
299
|
+
node linear.mjs add-comment "$ISSUE_ID" --body "Comment body text here"
|
|
300
|
+
|
|
301
|
+
# Long comment from a file (no shell-escaping)
|
|
302
|
+
node linear.mjs add-comment "$ISSUE_ID" --body-file ./comment.md
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
> **Note:** Always use `@` mentions when referring to team members in comments. Use the Linear `@` mention syntax with the team member's display name from `workspace.json`'s `roles` (e.g., `@<Frontend Lead Name>`, `@<Backend Lead Name>`) so they get properly notified.
|
|
306
|
+
|
|
307
|
+
### Searching
|
|
308
|
+
```bash
|
|
309
|
+
node linear.mjs search-issues "login bug"
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Projects & Milestones
|
|
313
|
+
```bash
|
|
314
|
+
# Create a new project (linked to an initiative)
|
|
315
|
+
node linear.mjs create-project --name "[KEY] Project Name" --initiative "$INITIATIVE_ID" --description "Short description"
|
|
316
|
+
|
|
317
|
+
# List all projects (pretty table with initiative, state, progress)
|
|
318
|
+
node linear.mjs list-projects
|
|
319
|
+
|
|
320
|
+
# List milestones within a project
|
|
321
|
+
node linear.mjs list-milestones "$PROJECT_ID"
|
|
322
|
+
|
|
323
|
+
# List issues grouped by milestone within a project
|
|
324
|
+
node linear.mjs list-project-issues "$PROJECT_ID"
|
|
325
|
+
|
|
326
|
+
# Raw JSON variants (for piping) — add --json
|
|
327
|
+
node linear.mjs list-projects --json
|
|
328
|
+
node linear.mjs list-milestones "$PROJECT_ID" --json
|
|
329
|
+
node linear.mjs list-project-issues "$PROJECT_ID" --limit 200 --json
|
|
330
|
+
|
|
331
|
+
# Create issue within a project/milestone
|
|
332
|
+
node linear.mjs create-issue \
|
|
333
|
+
--title 'Add feature X' \
|
|
334
|
+
--project 'project-uuid' \
|
|
335
|
+
--milestone 'milestone-uuid' \
|
|
336
|
+
--priority high \
|
|
337
|
+
--assignee frontend \
|
|
338
|
+
--labels feature
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### Initiatives
|
|
342
|
+
```bash
|
|
343
|
+
# Create a new initiative (= new client)
|
|
344
|
+
node linear.mjs create-initiative --name "ClientName" --description "Short description"
|
|
345
|
+
|
|
346
|
+
# List all initiatives (pretty table with ID, status, description)
|
|
347
|
+
node linear.mjs list-initiatives
|
|
348
|
+
|
|
349
|
+
# Get full initiative detail by name (case-insensitive)
|
|
350
|
+
node linear.mjs get-initiative-by-name "Northwind"
|
|
351
|
+
|
|
352
|
+
# Get full initiative detail by ID
|
|
353
|
+
node linear.mjs get-initiative "$INITIATIVE_ID"
|
|
354
|
+
|
|
355
|
+
# Update initiative content (markdown) or description
|
|
356
|
+
node linear.mjs update-initiative "$INITIATIVE_ID" --content-file ./initiative-notes.md
|
|
357
|
+
node linear.mjs update-initiative "$INITIATIVE_ID" --description "Short description"
|
|
358
|
+
|
|
359
|
+
# Add an external link (e.g., repo) as a resource on the initiative
|
|
360
|
+
node linear.mjs add-initiative-link "$INITIATIVE_ID" "https://github.com/org/repo" "GitHub Repo"
|
|
361
|
+
|
|
362
|
+
# Raw JSON of all initiatives
|
|
363
|
+
node linear.mjs list-initiatives --json
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Info Commands
|
|
367
|
+
```bash
|
|
368
|
+
node linear.mjs list-states # Workflow states
|
|
369
|
+
node linear.mjs list-members # Team members
|
|
370
|
+
node linear.mjs list-labels # Labels
|
|
371
|
+
node linear.mjs list-cycles # All cycles
|
|
372
|
+
node linear.mjs current-cycle-id # Current active cycle UUID
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## Issue Templates
|
|
378
|
+
|
|
379
|
+
> **Tech stack context:** All projects use Nuxt 4 + Nitro + Kysely + PostgreSQL + PrimeVue/Volt + Tailwind CSS v4. See `tech-stack.md` for full details.
|
|
380
|
+
|
|
381
|
+
### Issue Title Conventions
|
|
382
|
+
|
|
383
|
+
- **No client prefix** (e.g., ~~[GBX]~~) — the project name already identifies the client.
|
|
384
|
+
- **No domain prefix** (e.g., ~~UI:~~, ~~API:~~) — labels (`frontend`, `backend`) already cover this.
|
|
385
|
+
- Titles should be concise and describe the feature/fix directly (e.g., "Add provider create form", "Fix login redirect on Safari").
|
|
386
|
+
|
|
387
|
+
### Client Feature Request — Frontend
|
|
388
|
+
|
|
389
|
+
> **Important:** Do NOT guess which pages/components need updating. Check the client's repo (`app/pages/`, `app/components/`) to identify the correct files and routes. If the repo is not accessible, add a **## To Determine** section listing what needs to be verified before work begins (e.g., "Which page renders the jobs list? Check repo.").
|
|
390
|
+
|
|
391
|
+
```
|
|
392
|
+
Title: Feature description
|
|
393
|
+
Priority: High (2) or Medium (3)
|
|
394
|
+
Labels: feature, frontend
|
|
395
|
+
Estimate: S/M/L/XL
|
|
396
|
+
Description:
|
|
397
|
+
## Context
|
|
398
|
+
[Why does the client need this?]
|
|
399
|
+
|
|
400
|
+
## Requirements
|
|
401
|
+
- [ ] Requirement 1
|
|
402
|
+
- [ ] Requirement 2
|
|
403
|
+
|
|
404
|
+
## UI Notes
|
|
405
|
+
- Page/route: `/path` ← verified from repo, NOT guessed
|
|
406
|
+
- Components: [Which Volt components are relevant — VoltCard, VoltDataTable, etc.]
|
|
407
|
+
- Follow DESIGN_LANGUAGE.md (zinc palette, no decorative shadows)
|
|
408
|
+
|
|
409
|
+
## To Determine (if repo not checked)
|
|
410
|
+
- [ ] Which page/route handles this feature?
|
|
411
|
+
- [ ] Which existing components need modification?
|
|
412
|
+
|
|
413
|
+
## Acceptance Criteria
|
|
414
|
+
- [ ] What "done" looks like
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### Client Feature Request — Backend / API
|
|
418
|
+
|
|
419
|
+
> **Note:** Backend issues should describe *what* functionality is needed, not *how* to implement it. The Backend lead knows which endpoints to create, how to structure handlers, and what validation to add. Focus the description on the functionality the backend needs to support and any business rules or constraints.
|
|
420
|
+
|
|
421
|
+
```
|
|
422
|
+
Title: Feature description
|
|
423
|
+
Priority: High (2) or Medium (3)
|
|
424
|
+
Labels: feature, backend
|
|
425
|
+
Estimate: S/M/L/XL
|
|
426
|
+
Description:
|
|
427
|
+
## Context
|
|
428
|
+
[Why does the client need this? What problem does it solve for the client?]
|
|
429
|
+
|
|
430
|
+
## Functionality
|
|
431
|
+
- [What the backend needs to support — describe the behavior, not the implementation]
|
|
432
|
+
- [Business rules, constraints, edge cases]
|
|
433
|
+
- [What data needs to be stored, returned, or transformed]
|
|
434
|
+
- [Auth considerations if non-standard (e.g., public access, webhook)]
|
|
435
|
+
|
|
436
|
+
## Acceptance Criteria
|
|
437
|
+
- [ ] What "done" looks like from a functionality perspective
|
|
438
|
+
- [ ] Tests written
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### Fullstack Features — Split Into Separate Issues
|
|
442
|
+
|
|
443
|
+
When a feature requires both backend and frontend work, **always create separate issues** — one for backend and one for frontend. Link them using **Linear's dependency system** so the frontend issue is blocked by the backend issue.
|
|
444
|
+
|
|
445
|
+
> **Reminder:** For the frontend issue, verify affected pages/components from the client's repo. Don't guess file paths — check `app/pages/` and `app/components/` in the actual codebase.
|
|
446
|
+
|
|
447
|
+
This keeps issues focused, enables parallel assignment (the Backend lead on backend, the Frontend/PM lead on frontend), and makes progress tracking clearer. Using Linear dependencies (rather than just mentioning the dependency in the description) makes the blocking relationship visible in the UI, prevents the frontend issue from accidentally being started too early, and keeps the dependency machine-readable.
|
|
448
|
+
|
|
449
|
+
**Steps:**
|
|
450
|
+
1. Create the **backend issue** using the "Client Feature Request — Backend / API" template above (labels: `feature`, `backend`)
|
|
451
|
+
2. Create the **frontend issue** using the "Client Feature Request — Frontend" template above (labels: `feature`, `frontend`)
|
|
452
|
+
3. **Create the Linear dependency:** use `add-dependency` so the backend issue blocks the frontend issue
|
|
453
|
+
|
|
454
|
+
```bash
|
|
455
|
+
# After creating both issues, link them:
|
|
456
|
+
node linear.mjs add-dependency "$BACKEND_ISSUE_ID" "$FRONTEND_ISSUE_ID"
|
|
457
|
+
# Result: backend blocks frontend (frontend is blocked by backend)
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
**Example:** "Add admin button to complete all job tasks"
|
|
461
|
+
- **Backend issue:** Support marking all tasks for a job as complete in a single operation; admin-only, should be atomic
|
|
462
|
+
- **Frontend issue:** Admin-only button on job page, confirmation dialog, API call, toast
|
|
463
|
+
- **Dependency:** `node linear.mjs add-dependency "$BACKEND_ID" "$FRONTEND_ID"`
|
|
464
|
+
|
|
465
|
+
> **Note:** If the feature is simple enough that the backend is trivial (e.g., a single straightforward CRUD endpoint), it's acceptable to create one combined issue assigned to the person doing both. Use your judgement.
|
|
466
|
+
|
|
467
|
+
### Bug Report
|
|
468
|
+
```
|
|
469
|
+
Title: Fix: brief description of the bug
|
|
470
|
+
Priority: Urgent (1) or High (2)
|
|
471
|
+
Labels: bug, frontend|backend
|
|
472
|
+
Description:
|
|
473
|
+
## Bug
|
|
474
|
+
[What's happening vs. what should happen]
|
|
475
|
+
|
|
476
|
+
## Steps to Reproduce
|
|
477
|
+
1. Step 1
|
|
478
|
+
2. Step 2
|
|
479
|
+
|
|
480
|
+
## Environment
|
|
481
|
+
[Browser, OS, user account, etc.]
|
|
482
|
+
|
|
483
|
+
## Likely Location
|
|
484
|
+
- [File path if known, e.g., server/api/users/[id].get.ts or app/pages/users.vue]
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
### Backend / Data Modeling Task
|
|
488
|
+
|
|
489
|
+
> **Note:** Focus on *what* data needs to be modeled and *why*, not on prescribing specific schema details or endpoint structures. Include business context and constraints so the Backend lead can make the right design decisions.
|
|
490
|
+
|
|
491
|
+
```
|
|
492
|
+
Title: Description of the task
|
|
493
|
+
Priority: as appropriate
|
|
494
|
+
Labels: backend
|
|
495
|
+
Estimate: S/M/L/XL
|
|
496
|
+
Description:
|
|
497
|
+
## Objective
|
|
498
|
+
[What data model or API change is needed and why]
|
|
499
|
+
|
|
500
|
+
## Requirements
|
|
501
|
+
- [What data needs to be stored/tracked]
|
|
502
|
+
- [Relationships to existing data (e.g., "each job has many tasks")]
|
|
503
|
+
- [Business rules and constraints]
|
|
504
|
+
- [Any existing data that needs migrating]
|
|
505
|
+
|
|
506
|
+
## Acceptance Criteria
|
|
507
|
+
- [ ] What "done" looks like
|
|
508
|
+
- [ ] Tests written
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### Chore / Maintenance
|
|
512
|
+
```
|
|
513
|
+
Title: Chore: description
|
|
514
|
+
Priority: Medium (3) or Low (4)
|
|
515
|
+
Labels: chore, frontend|backend
|
|
516
|
+
Estimate: S/M/L
|
|
517
|
+
Description:
|
|
518
|
+
## What
|
|
519
|
+
[What needs to be done]
|
|
520
|
+
|
|
521
|
+
## Why
|
|
522
|
+
[Why it matters — tech debt, performance, DX, etc.]
|
|
523
|
+
|
|
524
|
+
## Files Affected
|
|
525
|
+
- [List key files/directories]
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
---
|
|
529
|
+
|
|
530
|
+
## Assignment Guidelines
|
|
531
|
+
|
|
532
|
+
| Issue Type | Default Assignee |
|
|
533
|
+
|-----------|-----------------|
|
|
534
|
+
| Kysely migrations, schema design, complex DB queries | Backend lead |
|
|
535
|
+
| Complex Nitro API handlers (transactions, multi-table) | Backend lead |
|
|
536
|
+
| Vue pages, Volt components, Tailwind styling, UX | Frontend/PM lead |
|
|
537
|
+
| Simple CRUD API endpoint (single table, straightforward) | Either (Frontend/PM lead can handle) |
|
|
538
|
+
| Client requirement gathering, design | Frontend/PM lead |
|
|
539
|
+
| Bug — Kysely/DB/server middleware | Backend lead |
|
|
540
|
+
| Bug — Vue/PrimeVue/Tailwind/client-side | Frontend/PM lead |
|
|
541
|
+
| Bug — fullstack | Discuss, assign based on root cause |
|
|
542
|
+
|
|
543
|
+
---
|
|
544
|
+
|
|
545
|
+
## Post-Organization: Update Initiative in Linear
|
|
546
|
+
|
|
547
|
+
**After organizing issues for a client (creating, triaging, updating statuses, or completing a sprint review), always update the corresponding initiative's `content` field in Linear.**
|
|
548
|
+
|
|
549
|
+
### What to Update
|
|
550
|
+
|
|
551
|
+
The initiative `content` field stores **client-level context only** — NOT data already tracked elsewhere in Linear. Update:
|
|
552
|
+
|
|
553
|
+
1. **Overview** — Client description, domain context, business purpose
|
|
554
|
+
2. **Repo structure** — Routes, components, API endpoints, key files
|
|
555
|
+
3. **Tech stack deviations** — Anything different from the standard Gallop template
|
|
556
|
+
4. **Domain concepts** — Key entities and business logic specific to the client
|
|
557
|
+
5. **Notes** — High-level observations, architectural decisions, gotchas
|
|
558
|
+
|
|
559
|
+
**Do NOT put in initiative content:** Team members (already in Linear), repo links (use `add-initiative-link` instead), project listings, milestone details, issue counts, progress percentages, remaining work, or any data already tracked in Linear's project/milestone/issue hierarchy.
|
|
560
|
+
|
|
561
|
+
### When to Update
|
|
562
|
+
|
|
563
|
+
- After creating a batch of new issues for a client
|
|
564
|
+
- After triaging/re-prioritizing a client's backlog
|
|
565
|
+
- After a sprint review or cycle close
|
|
566
|
+
- After marking significant issues as Done or Canceled
|
|
567
|
+
- Any time the initiative's content would be stale after your changes
|
|
568
|
+
|
|
569
|
+
### How to Get Current Data
|
|
570
|
+
|
|
571
|
+
Use the Linear CLI to query the initiative and pull fresh issue data:
|
|
572
|
+
```bash
|
|
573
|
+
# Get the initiative's current content
|
|
574
|
+
node linear.mjs get-initiative-by-name "ClientName"
|
|
575
|
+
# List all issues to see current statuses
|
|
576
|
+
node linear.mjs list-issues
|
|
577
|
+
# Or check cycle-specific progress
|
|
578
|
+
node linear.mjs list-cycle-issues
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
Then update the initiative's content in Linear (use a file for the markdown body):
|
|
582
|
+
```bash
|
|
583
|
+
node linear.mjs update-initiative "$INITIATIVE_ID" --content-file ./initiative-notes.md
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
---
|
|
587
|
+
|
|
588
|
+
## Project & Milestone Hierarchy
|
|
589
|
+
|
|
590
|
+
Linear organizes work in a top-down hierarchy: **Initiative → Project → Milestone → Issue**. Here's how the Gallop team uses each level.
|
|
591
|
+
|
|
592
|
+
### Initiative (= Client)
|
|
593
|
+
|
|
594
|
+
An **Initiative** represents a client engagement or internal program. Each client gets one initiative.
|
|
595
|
+
|
|
596
|
+
**The current client roster is not stored in this repo — fetch it live from Linear.** Initiatives are the source of truth for which clients exist, their descriptions, and their repo links:
|
|
597
|
+
|
|
598
|
+
- **List all clients:** `mcp__linear-server__list_initiatives`
|
|
599
|
+
- **Read a client's full details (overview, repo structure, domain notes):** `mcp__linear-server__get_initiative` — these live in the initiative's `content` field
|
|
600
|
+
- **Get a client's repo URL:** read the `links` array on the initiative
|
|
601
|
+
|
|
602
|
+
When you start any task that needs client context, query Linear instead of looking for a hardcoded list. This keeps the skill in sync as clients are added or removed without repo changes.
|
|
603
|
+
|
|
604
|
+
- One initiative can contain **multiple projects**
|
|
605
|
+
|
|
606
|
+
### Project (= Product / Workstream)
|
|
607
|
+
|
|
608
|
+
A **Project** is a distinct product, app, or major workstream within a client initiative. It groups related issues that ship together.
|
|
609
|
+
|
|
610
|
+
**Examples:**
|
|
611
|
+
- `[ACME] Billing System` — one self-contained product
|
|
612
|
+
- `[ACME] Analytics Demo` — separate product under the same client
|
|
613
|
+
- `[CLIENT] Migration Workstream` — the single workstream for that client
|
|
614
|
+
- `[CLIENT] Scheduling Platform` — the main product for that client
|
|
615
|
+
|
|
616
|
+
**When to create a new project:**
|
|
617
|
+
- The work has its own deployment, repo, or codebase
|
|
618
|
+
- It could be described independently to a stakeholder
|
|
619
|
+
- It has a distinct "done" state separate from other work
|
|
620
|
+
|
|
621
|
+
**Naming convention:** `[CLIENT_KEY] Project Name`
|
|
622
|
+
|
|
623
|
+
### Milestone (= Phase / Epic)
|
|
624
|
+
|
|
625
|
+
A **Milestone** is a phase or epic within a project — a meaningful chunk of progress that can be demoed or shipped incrementally.
|
|
626
|
+
|
|
627
|
+
**Examples within `[ACME] Billing System`:**
|
|
628
|
+
- `Core Billing` — create, edit, send invoices (done)
|
|
629
|
+
- `Quotes` — quote workflow, create/edit/convert to invoice
|
|
630
|
+
- `Payments` — payment methods, receipts, balance due display
|
|
631
|
+
|
|
632
|
+
**Examples within `[NW] Appointment Scheduling`:**
|
|
633
|
+
- `Providers Module` — list, create, edit, deactivate providers
|
|
634
|
+
- `Booking Requests` — request creation, accept/reject workflow
|
|
635
|
+
- `Scheduling & Calendar` — availability, scheduling UI
|
|
636
|
+
|
|
637
|
+
**When to create a milestone:**
|
|
638
|
+
- A logical group of 5–15 related issues
|
|
639
|
+
- Has a clear "phase complete" definition
|
|
640
|
+
- Can be reviewed/demoed as a unit
|
|
641
|
+
- Work within it is mostly sequential or tightly coupled
|
|
642
|
+
|
|
643
|
+
**Naming convention:** Short, descriptive noun phrase (no client key prefix needed since milestones live inside a project)
|
|
644
|
+
|
|
645
|
+
### Issue (= Task)
|
|
646
|
+
|
|
647
|
+
Individual work items live at the bottom of the hierarchy. Every issue belongs to a project and a milestone.
|
|
648
|
+
|
|
649
|
+
### Hierarchy in Practice
|
|
650
|
+
|
|
651
|
+
```
|
|
652
|
+
Initiative: Northwind
|
|
653
|
+
└── Project: [NW] Appointment Scheduling
|
|
654
|
+
├── Milestone: Providers Module
|
|
655
|
+
│ ├── ACME-101: Create providers list page
|
|
656
|
+
│ ├── ACME-102: Add provider create/edit form
|
|
657
|
+
│ └── ACME-103: Provider deactivation support
|
|
658
|
+
├── Milestone: Booking Requests
|
|
659
|
+
│ ├── ACME-110: Request creation form
|
|
660
|
+
│ └── ACME-111: Accept/reject API endpoints
|
|
661
|
+
└── Milestone: Notifications
|
|
662
|
+
└── ACME-120: Set up email service
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
### Guidelines for the Team
|
|
666
|
+
|
|
667
|
+
1. **Every issue must be placed into a cycle with Todo status.** **Do NOT default to the current/active cycle.** Follow this procedure: (a) Run `cycle-capacity` to see each cycle's capacity % (velocity-based, from last 3 completed cycles). (b) Starting from the earliest (current) cycle, find the first cycle that is **strictly under 100%** capacity. (c) If the current cycle is at or above 100%, **skip it** and use the next cycle with room. Assign the issue there via `--cycle`. **Always set `--state todo`** — issues in Backlog don't work with cycles. **Exception:** High priority or above (priority ≤ 2: Urgent, High) always go into the current active cycle regardless of capacity.
|
|
668
|
+
2. **Every issue must belong to a project and a milestone.** Never create orphan issues and never leave an issue outside a milestone.
|
|
669
|
+
3. **If the correct project does not exist, create it before creating the issue.** Do not park work in a generic team backlog while waiting to organize it later.
|
|
670
|
+
4. **If the correct milestone does not exist, create it before creating the issue.** Milestone creation is part of issue intake, not optional cleanup.
|
|
671
|
+
5. **Use milestones for sequencing.** Milestones can have target dates, making them useful for communicating delivery phases to clients.
|
|
672
|
+
6. **Track progress in Linear.** After creating/updating projects or milestones, update the initiative's content in Linear to reflect the current structure (see "Post-Organization: Update Initiative in Linear" below).
|
|
673
|
+
7. **When creating issues with the CLI**, use the `--project`, `--milestone`, and `--cycle` flags to place issues correctly in the hierarchy and cycle.
|
|
674
|
+
|
|
675
|
+
### CLI Examples
|
|
676
|
+
|
|
677
|
+
```bash
|
|
678
|
+
# List projects for the team
|
|
679
|
+
node linear.mjs list-projects
|
|
680
|
+
|
|
681
|
+
# List milestones within a project
|
|
682
|
+
node linear.mjs list-milestones "$PROJECT_ID"
|
|
683
|
+
|
|
684
|
+
# If needed, create the missing project or milestone before creating the issue
|
|
685
|
+
PROJECT_ID="$(node linear.mjs create-project --name "[CLIENT] Feature Area" --description "Short description" | node -e "process.stdin.once('data',d=>console.log(JSON.parse(d).data.projectCreate.project.id))")"
|
|
686
|
+
MILESTONE_ID="$(node linear.mjs create-milestone "$PROJECT_ID" "Phase 1" | node -e "process.stdin.once('data',d=>console.log(JSON.parse(d).data.projectMilestoneCreate.projectMilestone.id))")"
|
|
687
|
+
|
|
688
|
+
# Create an issue within a project and milestone (with cycle)
|
|
689
|
+
node linear.mjs create-issue \
|
|
690
|
+
--title 'Add provider create form' \
|
|
691
|
+
--description '...' \
|
|
692
|
+
--priority high \
|
|
693
|
+
--state todo \
|
|
694
|
+
--assignee frontend \
|
|
695
|
+
--labels feature,frontend \
|
|
696
|
+
--project 'project-uuid-here' \
|
|
697
|
+
--milestone 'milestone-uuid-here' \
|
|
698
|
+
--cycle current
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
---
|
|
702
|
+
|
|
703
|
+
## Project Refresh — Milestone Restructuring
|
|
704
|
+
|
|
705
|
+
When a project's milestone structure becomes outdated (or was never set up), use the **project refresh** workflow to reorganize milestones without losing or changing any issues.
|
|
706
|
+
|
|
707
|
+
### When to Refresh
|
|
708
|
+
|
|
709
|
+
- Project was created without milestones and has grown to 10+ issues
|
|
710
|
+
- Milestones were set up early but no longer match the actual work groupings
|
|
711
|
+
- A project pivot changed priorities and the old phases don't apply
|
|
712
|
+
- Too many issues are in "(No milestone)" and need proper grouping
|
|
713
|
+
- Milestones are too broad (30+ issues each) or too granular (1-2 issues each)
|
|
714
|
+
|
|
715
|
+
### Refresh Workflow
|
|
716
|
+
|
|
717
|
+
**Step 1: Audit the current state**
|
|
718
|
+
|
|
719
|
+
```bash
|
|
720
|
+
# Get the project ID
|
|
721
|
+
node linear.mjs list-projects
|
|
722
|
+
|
|
723
|
+
# See current milestones
|
|
724
|
+
node linear.mjs list-milestones "$PROJECT_ID"
|
|
725
|
+
|
|
726
|
+
# See all issues grouped by milestone (includes unmilestoned)
|
|
727
|
+
node linear.mjs list-project-issues "$PROJECT_ID" --limit 200
|
|
728
|
+
|
|
729
|
+
# Get raw JSON for scripting (includes issue UUIDs and milestone UUIDs)
|
|
730
|
+
node linear.mjs list-project-issues "$PROJECT_ID" --limit 200 --json
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
Review:
|
|
734
|
+
- How many issues per milestone? (ideal: 5–15)
|
|
735
|
+
- Are milestones thematically coherent?
|
|
736
|
+
- Are there many unmilestoned issues?
|
|
737
|
+
- Do completed milestones still have open issues?
|
|
738
|
+
- Are milestone names clear and descriptive?
|
|
739
|
+
|
|
740
|
+
**Step 2: Propose new milestone structure**
|
|
741
|
+
|
|
742
|
+
Present the proposed changes to the user before making any modifications:
|
|
743
|
+
- Which milestones to **keep** (unchanged)
|
|
744
|
+
- Which milestones to **rename** (same issues, better name) — **never rename milestones with target dates**
|
|
745
|
+
- Which milestones to **merge** (combine two sparse milestones)
|
|
746
|
+
- Which milestones to **split** (break an overloaded milestone)
|
|
747
|
+
- Which milestones to **create** (for unmilestoned issues or new groupings)
|
|
748
|
+
- Which milestones to **delete** (empty after reshuffling) — **never delete milestones with target dates**
|
|
749
|
+
- For each issue, which milestone it should end up in
|
|
750
|
+
|
|
751
|
+
**Present this as a before/after table so the user can approve.**
|
|
752
|
+
|
|
753
|
+
**Step 3: Execute the changes (after user approval)**
|
|
754
|
+
|
|
755
|
+
Order of operations matters — follow this sequence:
|
|
756
|
+
|
|
757
|
+
1. **Create new milestones** (need their IDs before moving issues)
|
|
758
|
+
```bash
|
|
759
|
+
node linear.mjs create-milestone "$PROJECT_ID" "New Milestone Name" --target-date "2025-06-01"
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
2. **Rename existing milestones** (safe, doesn't affect issues)
|
|
763
|
+
```bash
|
|
764
|
+
node linear.mjs update-milestone "$MILESTONE_ID" --name "Better Name"
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
3. **Move issues to their new milestones**
|
|
768
|
+
```bash
|
|
769
|
+
# One at a time
|
|
770
|
+
node linear.mjs set-issue-milestone "$ISSUE_ID" "$NEW_MILESTONE_ID"
|
|
771
|
+
|
|
772
|
+
# Or batch move
|
|
773
|
+
node linear.mjs batch-move-to-milestone "$NEW_MILESTONE_ID" "$ISSUE_1" "$ISSUE_2" "$ISSUE_3"
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
4. **Delete empty milestones** (only after all issues are moved out)
|
|
777
|
+
```bash
|
|
778
|
+
node linear.mjs delete-milestone "$EMPTY_MILESTONE_ID"
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
5. **Verify the result**
|
|
782
|
+
```bash
|
|
783
|
+
node linear.mjs list-project-issues "$PROJECT_ID" --limit 200
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
**Step 4: Update the initiative in Linear**
|
|
787
|
+
|
|
788
|
+
After restructuring, update the initiative's `content` field in Linear to reflect the new milestone structure.
|
|
789
|
+
|
|
790
|
+
### Safety Rules
|
|
791
|
+
|
|
792
|
+
- **No issue loss.** Every issue that existed before the refresh must exist after. Verify issue count before and after.
|
|
793
|
+
- **No status changes.** Don't change any issue's status, priority, assignee, labels, or estimate during a refresh. Only the milestone assignment changes.
|
|
794
|
+
- **No issue deletion.** Never delete or cancel issues as part of a refresh.
|
|
795
|
+
- **Delete milestones last.** Only delete a milestone after confirming it has zero issues.
|
|
796
|
+
- **Never delete or rename a dated milestone.** Milestones with target dates represent intentional commitments — they must stay intact (name and date unchanged). You may move issues out of them, but the milestone itself must not be deleted or renamed.
|
|
797
|
+
- **User approval required.** Always present the proposed restructuring plan and get explicit approval before executing any changes.
|
|
798
|
+
|
|
799
|
+
### CLI Reference (Milestone Operations)
|
|
800
|
+
|
|
801
|
+
```bash
|
|
802
|
+
# Create a milestone
|
|
803
|
+
node linear.mjs create-milestone "$PROJECT_ID" "Milestone Name" [--target-date YYYY-MM-DD]
|
|
804
|
+
|
|
805
|
+
# Rename / update a milestone
|
|
806
|
+
node linear.mjs update-milestone "$MILESTONE_ID" --name "New Name"
|
|
807
|
+
node linear.mjs update-milestone "$MILESTONE_ID" --target-date "2025-07-01"
|
|
808
|
+
node linear.mjs update-milestone "$MILESTONE_ID" --sort-order 5
|
|
809
|
+
|
|
810
|
+
# Delete a milestone (must be empty!)
|
|
811
|
+
node linear.mjs delete-milestone "$MILESTONE_ID"
|
|
812
|
+
|
|
813
|
+
# Move a single issue to a milestone
|
|
814
|
+
node linear.mjs set-issue-milestone "$ISSUE_ID" "$MILESTONE_ID"
|
|
815
|
+
|
|
816
|
+
# Remove issue from its milestone (set to unmilestoned)
|
|
817
|
+
node linear.mjs set-issue-milestone "$ISSUE_ID" none
|
|
818
|
+
|
|
819
|
+
# Batch move issues to a milestone
|
|
820
|
+
node linear.mjs batch-move-to-milestone "$MILESTONE_ID" "$ISSUE_1" "$ISSUE_2" "$ISSUE_3"
|
|
821
|
+
|
|
822
|
+
# Get raw JSON with issue/milestone UUIDs (for scripting)
|
|
823
|
+
node linear.mjs list-project-issues "$PROJECT_ID" --limit 200 --json
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
---
|
|
827
|
+
|
|
828
|
+
## Cycle Rebalance — Redistributing Issues Across Cycles
|
|
829
|
+
|
|
830
|
+
The **cycle rebalance** workflow redistributes issues so that cycles are filled **front-to-back**: the current cycle should be at **105% capacity**, overflow spills into the next cycle (also up to 105%), and so on. This applies in **both directions** — issues move later when a cycle is overloaded, and issues pull forward from later cycles when the current cycle has room.
|
|
831
|
+
|
|
832
|
+
**Capacity** is calculated using `cycle-capacity`: total estimate points in the cycle / average completed estimate points from the last 3 completed cycles (velocity).
|
|
833
|
+
|
|
834
|
+
### When to Rebalance
|
|
835
|
+
|
|
836
|
+
- After a cycle ends with incomplete issues that rolled into the next cycle
|
|
837
|
+
- When a cycle is over or under capacity
|
|
838
|
+
- When the user says "rebalance cycles", "redistribute issues", or similar
|
|
839
|
+
- During sprint planning when upcoming cycles look uneven
|
|
840
|
+
|
|
841
|
+
### Rebalance Workflow
|
|
842
|
+
|
|
843
|
+
**Step 1: Audit current cycle state**
|
|
844
|
+
|
|
845
|
+
```bash
|
|
846
|
+
# Check velocity-based capacity for all cycles
|
|
847
|
+
node linear.mjs cycle-capacity
|
|
848
|
+
|
|
849
|
+
# Overview: all active/upcoming cycles with issues grouped by project
|
|
850
|
+
node linear.mjs rebalance
|
|
851
|
+
|
|
852
|
+
# Raw data for analysis
|
|
853
|
+
node linear.mjs rebalance --json
|
|
854
|
+
```
|
|
855
|
+
|
|
856
|
+
Collect this data and analyze:
|
|
857
|
+
- **Velocity:** From `cycle-capacity` output (avg completed pts from last 3 cycles)
|
|
858
|
+
- **Capacity per cycle:** Each cycle's estimate points as a % of velocity
|
|
859
|
+
- **Target per cycle:** 105% of velocity (e.g., if velocity = 91, target = ~96 pts)
|
|
860
|
+
- **Which cycles are under 105%:** These need issues pulled forward from later cycles
|
|
861
|
+
- **Which cycles are over 105%:** These need issues pushed to later cycles
|
|
862
|
+
|
|
863
|
+
**Step 2: Analyze and plan the redistribution**
|
|
864
|
+
|
|
865
|
+
The goal is to **fill cycles front-to-back to 105%**:
|
|
866
|
+
|
|
867
|
+
1. Start with the **current (active) cycle**. Calculate its capacity.
|
|
868
|
+
2. If **under 105%** → pull movable issues forward from the next cycle(s) until at 105% (or no more movable issues exist).
|
|
869
|
+
3. If **over 105%** → push lowest-priority movable issues to the next cycle until at 105%.
|
|
870
|
+
4. Move to the **next cycle** and repeat.
|
|
871
|
+
5. Continue until all cycles are processed. The last cycle absorbs whatever remains.
|
|
872
|
+
|
|
873
|
+
**When pulling issues forward**, prefer (in order):
|
|
874
|
+
1. **Urgent/High priority** issues first — get important work done sooner
|
|
875
|
+
2. Issues whose **dependencies are already satisfied** (blocker is Done or in an earlier/same cycle)
|
|
876
|
+
3. Issues from **underrepresented clients** in the target cycle (balance client mix)
|
|
877
|
+
4. Issues in the **same milestone** as other issues already in the target cycle
|
|
878
|
+
|
|
879
|
+
**When pushing issues later**, prefer (in order):
|
|
880
|
+
1. **NEVER move High (2) or Urgent (1) priority issues to a later cycle** — they are time-sensitive and must stay in their current cycle or move earlier
|
|
881
|
+
2. **NEVER move issues with a due date** — due dates represent commitments; these issues are pinned to their current cycle (or can move earlier, never later)
|
|
882
|
+
3. **Low (4)** priority issues first — least impactful to delay
|
|
883
|
+
4. **Medium (3)** priority next
|
|
884
|
+
5. Issues with **no downstream dependents** (nothing blocked by them)
|
|
885
|
+
6. Issues from **overrepresented clients** in the current cycle
|
|
886
|
+
|
|
887
|
+
Apply these heuristics throughout:
|
|
888
|
+
|
|
889
|
+
#### Heuristic 1: Respect status — never move active work
|
|
890
|
+
- **Never move** issues that are `In Progress` or `In Review` — they stay in their current cycle
|
|
891
|
+
- **Todo** issues are movable (both forward and backward)
|
|
892
|
+
- **Backlog** issues in a cycle are movable (but should be set to Todo after moving)
|
|
893
|
+
|
|
894
|
+
#### Heuristic 2: Respect dependencies
|
|
895
|
+
- **Hard rule: a blocking issue must NEVER be in a later cycle than the issue it blocks.** If issue A blocks issue B, A must be in the same cycle as B or an earlier one. This is inviolable — never move a blocker to a later cycle than its dependent.
|
|
896
|
+
- Check dependencies with `node linear.mjs list-dependencies "$ISSUE_ID"` for any issue you plan to move
|
|
897
|
+
- When pulling an issue forward, also pull forward any of its blockers that are in a later cycle (or leave both)
|
|
898
|
+
- When pushing an issue later, ensure none of the issues it blocks are in the current or an earlier cycle — if they are, you cannot push this issue. Either push the dependent issues too, or leave the blocker in place.
|
|
899
|
+
|
|
900
|
+
#### Heuristic 3: Balance client work per cycle
|
|
901
|
+
- Each cycle should have a **roughly proportional mix** of client work — avoid "all Globex" or "all Northwind" cycles
|
|
902
|
+
- When choosing which issues to pull forward or push later, use client balance as a tiebreaker
|
|
903
|
+
- This ensures progress across all clients every sprint
|
|
904
|
+
|
|
905
|
+
#### Heuristic 4: Keep milestones together
|
|
906
|
+
- Issues in the same milestone should stay in the same cycle when possible — they're often sequentially dependent even if not formally linked
|
|
907
|
+
- If a milestone spans cycles, keep the split clean: don't scatter milestone issues across 3+ cycles
|
|
908
|
+
- When pulling forward, prefer pulling entire milestone groups together
|
|
909
|
+
|
|
910
|
+
#### Heuristic 5: Balance assignee load
|
|
911
|
+
- Each cycle should have a reasonable split between the Frontend/PM lead and the Backend lead
|
|
912
|
+
- Don't create a cycle where one person has 80% of the work and the other has 20%
|
|
913
|
+
- Consider that backend issues (Backend lead) often block frontend issues (Frontend/PM lead) — schedule accordingly
|
|
914
|
+
|
|
915
|
+
#### Heuristic 6: Estimate-aware balancing
|
|
916
|
+
- Use estimate points (not just issue count) for capacity calculations via `cycle-capacity`
|
|
917
|
+
- A cycle with 3 XL issues is heavier than one with 8 S issues
|
|
918
|
+
- Unestimated issues don't count toward capacity — note this when presenting the plan
|
|
919
|
+
|
|
920
|
+
**Step 3: Present the rebalance plan**
|
|
921
|
+
|
|
922
|
+
Present a clear before/after comparison:
|
|
923
|
+
|
|
924
|
+
```
|
|
925
|
+
VELOCITY: 91 pts (avg from C1=96, C2=80, C3=96)
|
|
926
|
+
TARGET PER CYCLE: ~96 pts (105%)
|
|
927
|
+
|
|
928
|
+
BEFORE:
|
|
929
|
+
Cycle 4 (active): 67 pts — 74% capacity
|
|
930
|
+
Cycle 5: 62 pts — 68% capacity
|
|
931
|
+
Cycle 6: 65 pts — 72% capacity
|
|
932
|
+
|
|
933
|
+
AFTER:
|
|
934
|
+
Cycle 4: 96 pts — 105% capacity (pulled 29 pts forward from C5)
|
|
935
|
+
Cycle 5: 96 pts — 105% capacity (lost 29 to C4, pulled 63 from C6)
|
|
936
|
+
Cycle 6: 2 pts — 2% capacity (pushed 63 to C5)
|
|
937
|
+
|
|
938
|
+
MOVES:
|
|
939
|
+
← ACME-342 "Build customer profiles list page" (est 3, Northwind) → Cycle 5 → Cycle 4
|
|
940
|
+
← ACME-441 "Show dependency indicators" (est 3, Globex) → Cycle 5 → Cycle 4
|
|
941
|
+
→ ACME-278 "Display audit log" (est 3, Globex) → Cycle 6 → Cycle 5
|
|
942
|
+
...
|
|
943
|
+
```
|
|
944
|
+
|
|
945
|
+
Include:
|
|
946
|
+
- Direction arrow: `←` for pulling forward, `→` for pushing later
|
|
947
|
+
- Which issues move, with identifier, title, priority, estimate, and project
|
|
948
|
+
- Why each issue was chosen to move
|
|
949
|
+
- Client distribution per cycle (before and after)
|
|
950
|
+
- Assignee balance per cycle (before and after)
|
|
951
|
+
- Any issues you considered moving but kept in place, and why
|
|
952
|
+
|
|
953
|
+
**Get explicit user approval before executing.**
|
|
954
|
+
|
|
955
|
+
**Step 4: Execute the moves (after approval)**
|
|
956
|
+
|
|
957
|
+
> **Important: Rate limiting.** The Linear API silently drops rapid-fire mutations. The `batch-move-to-cycle` command already inserts a 0.5s delay between calls and validates each response (reporting `success`/`fail` counts). For large rebalances (50+ moves), still process in groups of ~9 and verify between groups, since responses may report `success: true` while the mutation is silently discarded.
|
|
958
|
+
|
|
959
|
+
```bash
|
|
960
|
+
# Use the built-in batch command (includes delays and error reporting):
|
|
961
|
+
node linear.mjs batch-move-to-cycle "$TARGET_CYCLE_ID" "$ISSUE_1" "$ISSUE_2" "$ISSUE_3"
|
|
962
|
+
|
|
963
|
+
# "current" resolves to the active cycle:
|
|
964
|
+
node linear.mjs batch-move-to-cycle current "$ISSUE_1" "$ISSUE_2"
|
|
965
|
+
|
|
966
|
+
# For one-off moves:
|
|
967
|
+
node linear.mjs move-issue-to-cycle "$ISSUE_ID" "$CYCLE_ID"
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
**Step 5: Verify the result**
|
|
971
|
+
|
|
972
|
+
```bash
|
|
973
|
+
# Confirm the new capacity distribution
|
|
974
|
+
node linear.mjs cycle-capacity
|
|
975
|
+
|
|
976
|
+
# Confirm issue-level details
|
|
977
|
+
node linear.mjs rebalance
|
|
978
|
+
```
|
|
979
|
+
|
|
980
|
+
Review the output and confirm:
|
|
981
|
+
- Current cycle is at or near 105% (or as close as possible given movable issues)
|
|
982
|
+
- Each subsequent cycle is filled to 105% before spilling to the next
|
|
983
|
+
- No In Progress/In Review issues were moved
|
|
984
|
+
- Dependencies are still satisfied (blockers before blocked)
|
|
985
|
+
- Client mix is balanced across cycles
|
|
986
|
+
|
|
987
|
+
**Step 6: Update initiative in Linear if needed**
|
|
988
|
+
|
|
989
|
+
After rebalancing, update the initiative's content in Linear if cycle assignments or progress notes are tracked there.
|
|
990
|
+
|
|
991
|
+
### Safety Rules
|
|
992
|
+
|
|
993
|
+
- **No status changes.** Only the cycle assignment changes — never touch status, priority, assignee, labels, estimate, milestone, or project.
|
|
994
|
+
- **No issue deletion.** Never delete or cancel issues during a rebalance.
|
|
995
|
+
- **Don't move active work.** Issues in `In Progress` or `In Review` are untouchable.
|
|
996
|
+
- **Never push High or Urgent issues later.** High (priority 2) and Urgent (priority 1) issues must never be moved to a farther-out cycle — they are time-sensitive by definition. They can only stay put or be pulled forward.
|
|
997
|
+
- **Never push due-dated issues later.** Issues with a due date are pinned to their current cycle (or earlier). Due dates represent commitments — never move these to a farther-out cycle.
|
|
998
|
+
- **Respect dependencies.** A blocking issue must NEVER end up in a later cycle than the issue it blocks. Before moving any issue, check its dependencies — if it blocks something in cycle N, it cannot move to cycle N+1 or later.
|
|
999
|
+
- **User approval required.** Always present the full rebalance plan and get explicit approval before executing any moves.
|
|
1000
|
+
- **105% target is a soft cap.** It's okay if a cycle lands at 103% or 107% because the next movable issue would overshoot. The goal is "each cycle as close to 105% as possible, filled front-to-back," not mathematical perfection.
|
|
1001
|
+
|
|
1002
|
+
### CLI Reference (Cycle Rebalance Operations)
|
|
1003
|
+
|
|
1004
|
+
```bash
|
|
1005
|
+
# Full overview of cycles with issues grouped by project
|
|
1006
|
+
node linear.mjs rebalance
|
|
1007
|
+
|
|
1008
|
+
# Raw JSON for all active/upcoming cycles with incomplete issues
|
|
1009
|
+
node linear.mjs rebalance --json
|
|
1010
|
+
# (or: node linear.mjs list-cycle-issues-all)
|
|
1011
|
+
|
|
1012
|
+
# Raw JSON for a specific cycle's incomplete issues
|
|
1013
|
+
node linear.mjs list-cycle-issues-by-id "$CYCLE_ID"
|
|
1014
|
+
|
|
1015
|
+
# Move a single issue to a different cycle
|
|
1016
|
+
node linear.mjs move-issue-to-cycle "$ISSUE_ID" "$CYCLE_ID"
|
|
1017
|
+
|
|
1018
|
+
# Batch move multiple issues to a cycle (includes 0.5s delay between calls)
|
|
1019
|
+
# Reports success/fail counts. Keep batches ≤9 for reliability.
|
|
1020
|
+
node linear.mjs batch-move-to-cycle "$CYCLE_ID" "$ISSUE_1" "$ISSUE_2" "$ISSUE_3"
|
|
1021
|
+
|
|
1022
|
+
# Check dependencies before moving
|
|
1023
|
+
node linear.mjs list-dependencies "$ISSUE_ID"
|
|
1024
|
+
|
|
1025
|
+
# Verify after rebalancing
|
|
1026
|
+
node linear.mjs rebalance
|
|
1027
|
+
```
|
|
1028
|
+
|
|
1029
|
+
> **Rate limit note:** Linear's API can silently discard rapid mutations. The batch function includes a 0.5s delay between calls and validates each response. For large rebalances (50+ moves), process in groups of ~9 and verify between groups.
|
|
1030
|
+
|
|
1031
|
+
---
|
|
1032
|
+
|
|
1033
|
+
## Tips for a 2-Person Team
|
|
1034
|
+
|
|
1035
|
+
1. **Keep issues small.** If it's XL, break it down. Small issues keep momentum and make reviews easier.
|
|
1036
|
+
2. **Daily async check-in.** A quick message about what you're working on and if you're blocked.
|
|
1037
|
+
3. **Use "In Review" status.** It signals to the other person that something needs their eyes.
|
|
1038
|
+
4. **Don't overcommit cycles.** Leave ~20% buffer for bugs, client requests, and interruptions.
|
|
1039
|
+
5. **Projects identify the client.** No need for client prefixes in issue titles or client labels — the project name (e.g., `[GBX] Portal`) already provides that context.
|
|
1040
|
+
6. **Triage first.** New client requests go to Backlog, not straight into the sprint — unless truly urgent.
|