Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions skills/linear-cli/references/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ Description:

Options:

-h, --help - Show this help.
--workspace <slug> - Target workspace (uses credentials)
--variable <variable> - Variable in key=value format (coerces booleans, numbers, null; @file reads from path)
--variables-json <json> - JSON object of variables (merged with --variable, which takes precedence)
--paginate - Auto-paginate a single connection field using cursor pagination
-h, --help - Show this help.
--workspace <slug> - Target workspace (uses credentials)
--variable <variable> - Variable in key=value format (coerces booleans, numbers, null; @file reads from
path)
--variables-json <json> - JSON object of variables (merged with --variable, which takes precedence)
--paginate - Auto-paginate a single connection field using cursor pagination
--silent - Suppress response output (exit code still reflects errors)
```
1 change: 1 addition & 0 deletions skills/linear-cli/references/document.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ Options:
-c, --content <content> - New markdown content (inline)
-f, --content-file <path> - Read new content from file
--icon <icon> - New icon (emoji)
--project <project> - Attach to project (UUID, slug ID, or name)
-e, --edit - Open current content in $EDITOR for editing
--force - Update content even when document comments may lose inline anchors
```
Expand Down
206 changes: 100 additions & 106 deletions skills/linear-cli/references/issue.md

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions skills/linear-cli/references/milestone.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ Options:

Commands:

list - List milestones for a project
view, v <milestone> - View milestone details. By default lists the first 10 attached issues from the first page of 50; use --all to paginate the
full set.
create - Create a new project milestone
update <id> - Update an existing project milestone
list - List milestones for a project
view, v <milestone> - View milestone details. By default lists the first 10 attached issues from the
first page of 50; use --all to paginate the full set.
create - Create a new project milestone
update <id> - Update an existing project milestone
delete <id> - Delete a project milestone
```

Expand Down
60 changes: 31 additions & 29 deletions skills/linear-cli/references/project.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,25 +40,26 @@ Description:

Options:

-h, --help - Show this help.
--workspace <slug> - Target workspace (uses credentials)
-n, --name <name> - Project name (required)
-d, --description <description> - Project description (max 255 characters, enforced by Linear's API)
-f, --description-file <path> - Read project description from file (still subject to the 255-character API limit)
--content <markdown> - Project overview markdown
--content-file <path> - Read project overview markdown from a file
-t, --team <team> - Team key (required, can be repeated for multiple teams)
-l, --lead <lead> - Project lead (username, email, or @me)
-s, --status <status> - Project status (planned, started, paused, completed, canceled, backlog)
--start-date <startDate> - Start date (YYYY-MM-DD)
--target-date <targetDate> - Target completion date (YYYY-MM-DD)
--priority <priority> - Project priority (none, urgent, high, medium, low)
--label <label> - Project label associated with the project. May be repeated.
--member <user> - Project member (username, email, display name, or @me). May be repeated.
--icon <icon> - Project icon
--color <color> - Project color as a HEX string
--initiative <initiative> - Add to initiative immediately (ID, slug, or name)
-i, --interactive - Interactive mode (default if no flags provided)
-h, --help - Show this help.
--workspace <slug> - Target workspace (uses credentials)
-n, --name <name> - Project name (required)
-d, --description <description> - Project description (max 255 characters, enforced by Linear's API)
-f, --description-file <path> - Read project description from file (still subject to the 255-character API
limit)
--content <markdown> - Project overview markdown
--content-file <path> - Read project overview markdown from a file
-t, --team <team> - Team key (required, can be repeated for multiple teams)
-l, --lead <lead> - Project lead (username, email, or @me)
-s, --status <status> - Project status (planned, started, paused, completed, canceled, backlog)
--start-date <startDate> - Start date (YYYY-MM-DD)
--target-date <targetDate> - Target completion date (YYYY-MM-DD)
--priority <priority> - Project priority (none, urgent, high, medium, low)
--label <label> - Project label associated with the project. May be repeated.
--member <user> - Project member (username, email, display name, or @me). May be repeated.
--icon <icon> - Project icon
--color <color> - Project color as a HEX string
--initiative <initiative> - Add to initiative immediately (ID, slug, or name)
-i, --interactive - Interactive mode (default if no flags provided)
-j, --json - Output created project as JSON
```

Expand Down Expand Up @@ -116,16 +117,17 @@ Description:

Options:

-h, --help - Show this help.
--workspace <slug> - Target workspace (uses credentials)
-n, --name <name> - Project name
-d, --description <description> - Project description (max 255 characters, enforced by Linear's API)
-f, --description-file <path> - Read project description from file (still subject to the 255-character API limit)
-s, --status <status> - Status (planned, started, paused, completed, canceled, backlog)
-l, --lead <lead> - Project lead (username, email, or @me)
--start-date <startDate> - Start date (YYYY-MM-DD)
--target-date <targetDate> - Target date (YYYY-MM-DD)
-t, --team <team> - Team key (can be repeated for multiple teams)
-h, --help - Show this help.
--workspace <slug> - Target workspace (uses credentials)
-n, --name <name> - Project name
-d, --description <description> - Project description (max 255 characters, enforced by Linear's API)
-f, --description-file <path> - Read project description from file (still subject to the 255-character API
limit)
-s, --status <status> - Status (planned, started, paused, completed, canceled, backlog)
-l, --lead <lead> - Project lead (username, email, or @me)
--start-date <startDate> - Start date (YYYY-MM-DD)
--target-date <targetDate> - Target date (YYYY-MM-DD)
-t, --team <team> - Team key (can be repeated for multiple teams)
--label <label> - Replace the project's labels. May be repeated to set multiple labels.
```

Expand Down
2 changes: 1 addition & 1 deletion src/commands/issue/issue-create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -648,7 +648,7 @@ export const createCommand = new Command()
)
.option(
"--cycle <cycle:string>",
"Cycle name, number, or 'active'",
"Cycle name, number, 'active'/'now', 'next', 'previous', or a relative offset like +1 (use --cycle=-1 for negatives)",
)
.option(
"--no-use-default-template",
Expand Down
33 changes: 29 additions & 4 deletions src/commands/issue/issue-mine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { unicodeWidth } from "@std/cli"
import { rgb24 } from "@std/fmt/colors"
import { getOption } from "../../config.ts"
import {
colorCycleShort,
formatCycleShort,
getPriorityDisplay,
getTimeAgo,
padDisplay,
Expand Down Expand Up @@ -78,7 +80,7 @@ export const mineCommand = new Command()
)
.option(
"--cycle <cycle:string>",
"Filter by cycle name, number, or 'active'",
"Filter by cycle name, number, 'active'/'now', 'next', 'previous', or a relative offset like +1",
)
.option(
"--milestone <milestone:string>",
Expand Down Expand Up @@ -312,6 +314,15 @@ export const mineCommand = new Command()
),
)
const ESTIMATE_WIDTH = 1 // fixed width for estimate
const showCycleColumn = issues.some((issue) =>
issue.cycle != null || issue.team.cyclesEnabled
)
const cycleShorts = issues.map((issue) =>
formatCycleShort(issue.cycle, issue.team.activeCycle?.number)
)
const CYCLE_WIDTH = showCycleColumn
? Math.max(3, ...cycleShorts.map((c) => unicodeWidth(c.text)))
: 0
const STATE_WIDTH = Math.min(
20, // maximum width for state
Math.max(
Expand All @@ -334,12 +345,13 @@ export const mineCommand = new Command()
identifier: string
title: string
labels: string
cycleCell: string
state: string
timeAgo: string
estimate: number | null | undefined
}

const tableData: Array<TableRow> = issues.map((issue) => {
const tableData: Array<TableRow> = issues.map((issue, index) => {
let labels: string
if (issue.labels.nodes.length === 0) {
labels = " ".repeat(LABEL_WIDTH)
Expand Down Expand Up @@ -404,12 +416,19 @@ export const mineCommand = new Command()

const blockedStr = isIssueBlocked(issue) ? warning("⊘") : " "

const cycleShort = cycleShorts[index]
const cycleCell = colorCycleShort(cycleShort) +
" ".repeat(
Math.max(0, CYCLE_WIDTH - unicodeWidth(cycleShort.text)),
)

return {
priorityStr,
blockedStr,
identifier: issue.identifier,
title: issue.title,
labels,
cycleCell,
state: statePadded,
timeAgo,
estimate: issue.estimate,
Expand All @@ -418,7 +437,8 @@ export const mineCommand = new Command()

const fixed = PRIORITY_WIDTH + BLOCKED_WIDTH + ID_WIDTH +
UPDATED_WIDTH + SPACE_WIDTH +
LABEL_WIDTH + ESTIMATE_WIDTH + STATE_WIDTH + SPACE_WIDTH
LABEL_WIDTH + ESTIMATE_WIDTH + STATE_WIDTH + SPACE_WIDTH +
(showCycleColumn ? CYCLE_WIDTH + 1 : 0)
const PADDING = 1
const maxTitleWidth = Math.max(
...tableData.map((row) => unicodeWidth(row.title)),
Expand All @@ -432,6 +452,7 @@ export const mineCommand = new Command()
padDisplay("LABELS", LABEL_WIDTH),
padDisplay("B", BLOCKED_WIDTH),
padDisplay("E", ESTIMATE_WIDTH),
...(showCycleColumn ? [padDisplay("CYC", CYCLE_WIDTH)] : []),
padDisplay("STATE", STATE_WIDTH),
padDisplay(updatedHeader, UPDATED_WIDTH),
]
Expand All @@ -449,6 +470,7 @@ export const mineCommand = new Command()
identifier,
title,
labels,
cycleCell,
state,
timeAgo,
estimate,
Expand All @@ -458,11 +480,14 @@ export const mineCommand = new Command()
titleWidth,
)

const cycleSegment = showCycleColumn ? `${cycleCell} ` : ""
const issueLine = `${padDisplay(priorityStr, PRIORITY_WIDTH)} ${
padDisplay(identifier, ID_WIDTH)
} ${truncTitle} ${labels} ${padDisplay(blockedStr, BLOCKED_WIDTH)} ${
padDisplay(estimate?.toString() || "-", ESTIMATE_WIDTH)
} ${state} ${muted(padDisplay(timeAgo, UPDATED_WIDTH))}`
} ${cycleSegment}${state} ${
muted(padDisplay(timeAgo, UPDATED_WIDTH))
}`
outputLines.push(issueLine)
}

Expand Down
30 changes: 27 additions & 3 deletions src/commands/issue/issue-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { unicodeWidth } from "@std/cli"
import { rgb24 } from "@std/fmt/colors"
import { getOption } from "../../config.ts"
import {
colorCycleShort,
type CycleDisplayInfo,
formatCycleShort,
getPriorityDisplay,
getTimeAgo,
padDisplay,
Expand Down Expand Up @@ -86,7 +89,7 @@ export const queryCommand = new Command()
)
.option(
"--cycle <cycle:string>",
"Filter by cycle name, number, or 'active'",
"Filter by cycle name, number, 'active'/'now', 'next', 'previous', or a relative offset like +1",
)
.option(
"--milestone <milestone:string>",
Expand Down Expand Up @@ -333,6 +336,7 @@ export const queryCommand = new Command()
limit: limit === 0 ? 0 : limit,
projectId,
projectLabel,
cycleId,
labelNames,
createdAfter,
updatedAfter,
Expand Down Expand Up @@ -425,7 +429,12 @@ interface DisplayableIssue {
updatedAt: string
state: { name: string; color: string }
assignee?: { initials: string } | null
team?: { key: string }
team?: {
key: string
cyclesEnabled?: boolean
activeCycle?: { number: number } | null
}
cycle?: CycleDisplayInfo | null
labels: { nodes: Array<{ name: string; color: string }> }
inverseRelations?: {
nodes: Array<{
Expand Down Expand Up @@ -463,6 +472,15 @@ function formatIssueTable(
),
)
const estimateWidth = 1
const showCycleColumn = issues.some((i) =>
i.cycle != null || i.team?.cyclesEnabled === true
)
const cycleShorts = issues.map((i) =>
formatCycleShort(i.cycle, i.team?.activeCycle?.number)
)
const cycleWidth = showCycleColumn
? Math.max(3, ...cycleShorts.map((c) => unicodeWidth(c.text)))
: 0
const assigneeWidth = showAssigneeColumn ? 2 : 0
const stateWidth = Math.min(
20,
Expand All @@ -481,6 +499,7 @@ function formatIssueTable(
labelWidth,
blockedWidth,
estimateWidth,
...(showCycleColumn ? [cycleWidth] : []),
...(showAssigneeColumn ? [assigneeWidth] : []),
stateWidth,
updatedWidth,
Expand All @@ -499,14 +518,15 @@ function formatIssueTable(
padDisplay("LABELS", labelWidth),
padDisplay("B", blockedWidth),
padDisplay("E", estimateWidth),
...(showCycleColumn ? [padDisplay("CYC", cycleWidth)] : []),
...(showAssigneeColumn ? [padDisplay("A", assigneeWidth)] : []),
padDisplay("STATE", stateWidth),
padDisplay(updatedHeader, updatedWidth),
]

const outputLines = [header(headerCells.join(" "))]

for (const issue of issues) {
for (const [index, issue] of issues.entries()) {
const title = padDisplay(
truncateText(issue.title, titleWidth),
titleWidth,
Expand All @@ -522,6 +542,9 @@ function formatIssueTable(
padDisplay(getTimeAgo(new Date(issue.updatedAt)), updatedWidth),
)
const blockedCell = isIssueBlocked(issue) ? warning("⊘") : " "
const cycleShort = cycleShorts[index]
const cycleCell = colorCycleShort(cycleShort) +
" ".repeat(Math.max(0, cycleWidth - unicodeWidth(cycleShort.text)))
const cells = [
padDisplay(getPriorityDisplay(issue.priority), priorityWidth),
padDisplay(issue.identifier, idWidth),
Expand All @@ -530,6 +553,7 @@ function formatIssueTable(
formatLabels(issue.labels.nodes, labelWidth),
padDisplay(blockedCell, blockedWidth),
padDisplay(issue.estimate?.toString() || "-", estimateWidth),
...(showCycleColumn ? [cycleCell] : []),
...(showAssigneeColumn
? [
padDisplay(
Expand Down
Loading
Loading