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
6,052 changes: 4,440 additions & 1,612 deletions docs/cast-issue-create.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1,467 changes: 1,060 additions & 407 deletions docs/cast-issue-start.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
60 changes: 59 additions & 1 deletion skills/linear-cli/references/issue.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ Commands:
comment - Manage issue comments
attach <issueId> <filepath> - Attach a file to an issue
link <urlOrIssueId> [url] - Link a URL to an issue
relation - Manage issue relations (dependencies)
relation - Manage issue relations (dependencies)
agent-session - Manage agent sessions for an issue
```

## Subcommands
Expand Down Expand Up @@ -520,3 +521,60 @@ Options:
-h, --help - Show this help.
-w, --workspace <slug> - Target workspace (uses credentials)
```

### agent-session

> Manage agent sessions for an issue

```
Usage: linear issue agent-session

Description:

Manage agent sessions for an issue

Options:

-h, --help - Show this help.
-w, --workspace <slug> - Target workspace (uses credentials)

Commands:

list [issueId] - List agent sessions for an issue
view, v <sessionId> - View agent session details
```

#### agent-session subcommands

##### list

```
Usage: linear issue agent-session list [issueId]

Description:

List agent sessions for an issue

Options:

-h, --help - Show this help.
-w, --workspace <slug> - Target workspace (uses credentials)
-j, --json - Output as JSON
--status <status> - Filter by status (pending, active, complete, awaitingInput, error, stale)
```

##### view

```
Usage: linear issue agent-session view <sessionId>

Description:

View agent session details

Options:

-h, --help - Show this help.
-w, --workspace <slug> - Target workspace (uses credentials)
-j, --json - Output as JSON
```
152 changes: 152 additions & 0 deletions src/commands/issue/issue-agent-session-list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { Command } from "@cliffy/command"
import { unicodeWidth } from "@std/cli"
import { green, yellow } from "@std/fmt/colors"
import { gql } from "../../__codegen__/gql.ts"
import { getGraphQLClient } from "../../utils/graphql.ts"
import { padDisplay, truncateText } from "../../utils/display.ts"
import { getIssueIdentifier } from "../../utils/linear.ts"
import { shouldShowSpinner } from "../../utils/hyperlink.ts"
import { header, muted } from "../../utils/styling.ts"
import { handleError, ValidationError } from "../../utils/errors.ts"

const GetIssueAgentSessions = gql(`
query GetIssueAgentSessions($issueId: String!) {
issue(id: $issueId) {
comments(first: 100) {
nodes {
agentSession {
id
status
type
createdAt
startedAt
endedAt
summary
creator {
name
}
appUser {
name
}
}
}
}
}
}
`)

function formatStatus(status: string): string {
switch (status) {
case "active":
return green(padDisplay("active", 13))
case "pending":
return yellow(padDisplay("pending", 13))
case "awaitingInput":
return yellow(padDisplay("awaitingInput", 13))
case "complete":
return muted(padDisplay("complete", 13))
case "error":
return padDisplay("error", 13)
case "stale":
return muted(padDisplay("stale", 13))
default:
return padDisplay(status, 13)
}
}

function formatDate(dateString: string): string {
return dateString.slice(0, 10)
}

export const agentSessionListCommand = new Command()
.name("list")
.description("List agent sessions for an issue")
.arguments("[issueId:string]")
.option("-j, --json", "Output as JSON")
.option(
"--status <status:string>",
"Filter by status (pending, active, complete, awaitingInput, error, stale)",
)
.action(async ({ json, status }, issueId) => {
try {
const resolvedIdentifier = await getIssueIdentifier(issueId)
if (!resolvedIdentifier) {
throw new ValidationError(
"Could not determine issue ID",
{ suggestion: "Please provide an issue ID like 'ENG-123'." },
)
}

const { Spinner } = await import("@std/cli/unstable-spinner")
const showSpinner = shouldShowSpinner()
const spinner = showSpinner ? new Spinner() : null
spinner?.start()

const client = getGraphQLClient()
const result = await client.request(GetIssueAgentSessions, {
issueId: resolvedIdentifier,
})
spinner?.stop()

let sessions = (result.issue?.comments?.nodes || [])
.map((c) => c.agentSession)
.filter((s): s is NonNullable<typeof s> => s != null)

if (status) {
sessions = sessions.filter((s) => s.status === status)
}

if (json) {
console.log(JSON.stringify(sessions, null, 2))
return
}

if (sessions.length === 0) {
console.log("No agent sessions found for this issue.")
return
}

const { columns } = Deno.stdout.isTerminal()
? Deno.consoleSize()
: { columns: 120 }

const STATUS_WIDTH = 13
const DATE_WIDTH = 10
const AGENT_WIDTH = Math.max(
5,
...sessions.map((s) => unicodeWidth(s.appUser.name)),
)
const SPACE_WIDTH = 3

const fixed = STATUS_WIDTH + DATE_WIDTH + AGENT_WIDTH + SPACE_WIDTH
const PADDING = 1
const availableWidth = Math.max(columns - PADDING - fixed, 10)

const headerCells = [
padDisplay("STATUS", STATUS_WIDTH),
padDisplay("AGENT", AGENT_WIDTH),
padDisplay("CREATED", DATE_WIDTH),
"SUMMARY",
]

console.log(header(headerCells.join(" ")))

for (const session of sessions) {
const summaryText = session.summary
? truncateText(
session.summary.replace(/\n/g, " "),
availableWidth,
)
: muted("--")

const line = `${formatStatus(session.status)} ${
padDisplay(session.appUser.name, AGENT_WIDTH)
} ${
padDisplay(formatDate(session.createdAt), DATE_WIDTH)
} ${summaryText}`
console.log(line)
}
} catch (error) {
handleError(error, "Failed to list agent sessions")
}
})
Loading
Loading