Skip to content
Open
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
9 changes: 5 additions & 4 deletions skills/linear-cli/references/issue.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,11 @@ Options:
--all-states - Show issues from all states
--assignee <assignee> - Filter by assignee (username)
-A, --all-assignees - Show issues for all assignees
-U, --unassigned - Show only unassigned issues
--sort <sort> - Sort order (can also be set via LINEAR_ISSUE_SORT) (Values: "manual", "priority")
--team <team> - Team to list issues for (if not your default team)
--project <project> - Filter by project name
-U, --unassigned - Show only unassigned issues
--sort <sort> - Sort order (can also be set via LINEAR_ISSUE_SORT) (Values: "manual", "priority")
--team <team> - Team to list issues for (if not your default team)
--all-teams - Show issues from all teams in the workspace
--project <project> - Filter by project name
--limit <limit> - Maximum number of issues to fetch (default: 50, use 0 for unlimited) (Default: 50)
-w, --web - Open in web browser
-a, --app - Open in Linear.app
Expand Down
25 changes: 20 additions & 5 deletions src/commands/issue/issue-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ export const listCommand = new Command()
"--team <team:string>",
"Team to list issues for (if not your default team)",
)
.option(
"--all-teams",
"Show issues from all teams in the workspace",
)
.option(
"--project <project:string>",
"Filter by project name",
Expand All @@ -101,6 +105,7 @@ export const listCommand = new Command()
app,
allStates,
team,
allTeams,
project,
limit,
pager,
Expand Down Expand Up @@ -131,6 +136,10 @@ export const listCommand = new Command()
throw new ValidationError("Cannot use --all-states with --state flag")
}

if (allTeams && team) {
throw new ValidationError("Cannot use --all-teams with --team flag")
}

const sort = sortFlag ||
getOption("issue_sort") as "manual" | "priority" | undefined
if (!sort) {
Expand All @@ -143,11 +152,17 @@ export const listCommand = new Command()
`Sort must be one of: ${SortType.values().join(", ")}`,
)
}
const teamKey = team || getTeamKey()
if (!teamKey) {
throw new ValidationError(
"Could not determine team key from directory name or team flag",
)

let teamKey: string | undefined
if (allTeams) {
teamKey = undefined
} else {
teamKey = team || getTeamKey()
if (!teamKey) {
throw new ValidationError(
"Could not determine team key from directory name or team flag",
)
}
}

let projectId: string | undefined
Expand Down
8 changes: 5 additions & 3 deletions src/utils/linear.ts
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ export async function fetchParentIssueData(parentId: string): Promise<
}

export async function fetchIssuesForState(
teamKey: string,
teamKey: string | undefined,
state: string[] | undefined,
assignee?: string,
unassigned = false,
Expand All @@ -410,8 +410,10 @@ export async function fetchIssuesForState(
)
}

const filter: IssueFilter = {
team: { key: { eq: teamKey } },
const filter: IssueFilter = {}

if (teamKey) {
filter.team = { key: { eq: teamKey } }
}

if (state) {
Expand Down
12 changes: 12 additions & 0 deletions test/commands/issue/__snapshots__/issue-list.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Options:
-U, --unassigned - Show only unassigned issues
--sort <sort> - Sort order (can also be set via LINEAR_ISSUE_SORT) (Values: \\x1b[32m"manual"\\x1b[39m, \\x1b[32m"priority"\\x1b[39m)
--team <team> - Team to list issues for (if not your default team)
--all-teams - Show issues from all teams in the workspace
--project <project> - Filter by project name
--limit <limit> - Maximum number of issues to fetch (default: 50, use 0 for unlimited) (Default: \\x1b[33m50\\x1b[39m)
-w, --web - Open in web browser
Expand All @@ -30,3 +31,14 @@ Options:
stderr:
""
`;

snapshot[`Issue List Command - All Teams 1`] = `
stdout:
"◌ ID TITLE LABELS E STATE UPDATED
⚠⚠⚠ BACKEND-123 Fix authentication bug bug 3 In Progress 1 hour ago
▄▆█ FRONTEND-456 Update user interface feature 5 To Do 1 day ago
--- SEC-789 Security audit review - Backlog 7 days ago
"
stderr:
""
`;
136 changes: 136 additions & 0 deletions test/commands/issue/issue-list.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { snapshotTest } from "@cliffy/testing"
import { listCommand } from "../../../src/commands/issue/issue-list.ts"
import { commonDenoArgs } from "../../utils/test-helpers.ts"
import { MockLinearServer } from "../../utils/mock_linear_server.ts"

// Test help output
await snapshotTest({
Expand All @@ -13,3 +14,138 @@ await snapshotTest({
await listCommand.parse()
},
})

// Test --all-teams flag
await snapshotTest({
name: "Issue List Command - All Teams",
meta: import.meta,
colors: false,
args: ["--all-teams", "--no-pager", "--sort", "manual"],
denoArgs: commonDenoArgs,
async fn() {
// Use a fixed date for deterministic snapshot tests
const baseDate = new Date("2024-01-15T12:00:00Z")
const server = new MockLinearServer([
{
queryName: "GetIssuesForState",
response: {
data: {
issues: {
nodes: [
{
id: "issue-1",
identifier: "BACKEND-123",
title: "Fix authentication bug",
priority: 1,
estimate: 3,
assignee: {
initials: "JD",
},
state: {
id: "state-1",
name: "In Progress",
color: "#3b82f6",
},
labels: {
nodes: [
{
id: "label-1",
name: "bug",
color: "#ef4444",
},
],
},
updatedAt: new Date(baseDate.getTime() - 1000 * 60 * 60)
.toISOString(), // 1 hour ago
},
{
id: "issue-2",
identifier: "FRONTEND-456",
title: "Update user interface",
priority: 2,
estimate: 5,
assignee: {
initials: "AS",
},
state: {
id: "state-2",
name: "To Do",
color: "#6b7280",
},
labels: {
nodes: [
{
id: "label-2",
name: "feature",
color: "#10b981",
},
],
},
updatedAt: new Date(baseDate.getTime() - 1000 * 60 * 60 * 24)
.toISOString(), // 1 day ago
},
{
id: "issue-3",
identifier: "SEC-789",
title: "Security audit review",
priority: 0,
estimate: null,
assignee: null,
state: {
id: "state-3",
name: "Backlog",
color: "#94a3b8",
},
labels: {
nodes: [],
},
updatedAt: new Date(
baseDate.getTime() - 1000 * 60 * 60 * 24 * 7,
).toISOString(), // 1 week ago
},
],
pageInfo: {
hasNextPage: false,
endCursor: null,
},
},
},
},
},
])

try {
await server.start()
Deno.env.set("LINEAR_GRAPHQL_ENDPOINT", server.getEndpoint())
Deno.env.set("LINEAR_API_KEY", "Bearer test-token")

// Mock the current date to make time calculations deterministic
const originalDate = globalThis.Date
// @ts-ignore: Mocking Date for testing
globalThis.Date = class extends originalDate {
// deno-lint-ignore constructor-super
constructor(...args: unknown[]) {
if (args.length === 0) {
super(baseDate.getTime())
} else {
// @ts-ignore: Mocking Date for testing
super(...args)
}
}

static override now() {
return baseDate.getTime()
}
}

await listCommand.parse()

// Restore original Date
globalThis.Date = originalDate
} finally {
await server.stop()
Deno.env.delete("LINEAR_GRAPHQL_ENDPOINT")
Deno.env.delete("LINEAR_API_KEY")
}
},
})