@subsimplicity/pi-extension-linear 0.0.1

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/src/tools.ts ADDED
@@ -0,0 +1,202 @@
1
+ import {
2
+ CommentIssueParams,
3
+ CreateIssueParams,
4
+ EmptyParams,
5
+ GetIssueParams,
6
+ ListStatesParams,
7
+ MyWorkParams,
8
+ SearchIssuesParams,
9
+ UpdateIssueParams,
10
+ } from "./schemas.ts";
11
+ import {
12
+ formatIssue,
13
+ formatIssueSearch,
14
+ formatMyWork,
15
+ formatStates,
16
+ formatTeams,
17
+ formatViewer,
18
+ } from "./format.ts";
19
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
20
+ import { LinearClient } from "./client.ts";
21
+ import { textResult } from "./results.ts";
22
+
23
+ export function registerLinearTools(pi: ExtensionAPI): void {
24
+ const client = new LinearClient();
25
+
26
+ pi.registerTool({
27
+ description:
28
+ "Check Linear API credentials and return the authenticated Linear user. Uses LINEAR_API_KEY or LINEAR_ACCESS_TOKEN.",
29
+ async execute(_toolCallId, _params, signal) {
30
+ const viewer = await client.viewer(signal);
31
+ return textResult(`Linear connected as ${formatViewer(viewer)}`, {
32
+ viewer,
33
+ });
34
+ },
35
+ label: "Linear Setup",
36
+ name: "linear_setup",
37
+ parameters: EmptyParams,
38
+ promptGuidelines: [
39
+ "Use linear_setup when diagnosing Linear authentication or before using Linear tools if credentials may be missing.",
40
+ ],
41
+ promptSnippet:
42
+ "Check Linear API credentials and identify the authenticated user",
43
+ });
44
+
45
+ pi.registerTool({
46
+ description:
47
+ "Fetch a Linear issue by UUID or identifier, including description, metadata, labels, project, assignee, delegate, and recent comments.",
48
+ async execute(_toolCallId, params: GetIssueParams, signal) {
49
+ const issue = await client.issue(
50
+ params.issueId,
51
+ params.commentsLimit,
52
+ signal,
53
+ );
54
+ return textResult(formatIssue(issue), { issue });
55
+ },
56
+ label: "Linear Get Issue",
57
+ name: "linear_get_issue",
58
+ parameters: GetIssueParams,
59
+ promptGuidelines: [
60
+ "Use linear_get_issue when the user references a Linear issue key such as ENG-123 or asks for issue details.",
61
+ "Use Linear issue descriptions and comments as planning context, but verify repository state with normal file and shell tools before making code changes.",
62
+ ],
63
+ promptSnippet: "Fetch a Linear issue with metadata and recent comments",
64
+ });
65
+
66
+ pi.registerTool({
67
+ description:
68
+ "Search Linear issues with focused filters for text, team, state, labels, priority, assignee, and project. Results are ordered by recently updated.",
69
+ async execute(_toolCallId, params: SearchIssuesParams, signal) {
70
+ const issues = await client.searchIssues(params, signal);
71
+ return textResult(
72
+ formatIssueSearch("Linear issue search results", issues),
73
+ {
74
+ issues,
75
+ },
76
+ );
77
+ },
78
+ label: "Linear Search Issues",
79
+ name: "linear_search_issues",
80
+ parameters: SearchIssuesParams,
81
+ promptGuidelines: [
82
+ "Use linear_search_issues instead of broad Linear API calls; prefer narrow filters and small limits to avoid wasting Linear rate limit and context.",
83
+ ],
84
+ promptSnippet: "Search Linear issues with focused filters",
85
+ });
86
+
87
+ pi.registerTool({
88
+ description:
89
+ "List Linear issues assigned to and recently created by the authenticated user.",
90
+ async execute(_toolCallId, params: MyWorkParams, signal) {
91
+ const work = await client.myWork(params.limit, signal);
92
+ return textResult(formatMyWork(work), work);
93
+ },
94
+ label: "Linear My Work",
95
+ name: "linear_my_work",
96
+ parameters: MyWorkParams,
97
+ promptGuidelines: [
98
+ "Use linear_my_work when the user asks what Linear work is currently assigned to them or recently created by them.",
99
+ ],
100
+ promptSnippet: "List Linear work for the authenticated user",
101
+ });
102
+
103
+ pi.registerTool({
104
+ description: "List available Linear teams with their keys and UUIDs.",
105
+ async execute(_toolCallId, _params, signal) {
106
+ const teams = await client.teams(signal);
107
+ return textResult(formatTeams(teams), { teams });
108
+ },
109
+ label: "Linear List Teams",
110
+ name: "linear_list_teams",
111
+ parameters: EmptyParams,
112
+ promptGuidelines: [
113
+ "Use linear_list_teams before creating issues if the target Linear team id or key is unclear.",
114
+ ],
115
+ promptSnippet: "List Linear teams and their keys/UUIDs",
116
+ });
117
+
118
+ pi.registerTool({
119
+ description:
120
+ "List workflow states for a Linear team by team UUID, key, or exact name.",
121
+ async execute(_toolCallId, params: ListStatesParams, signal) {
122
+ const states = await client.states(params.team, signal);
123
+ return textResult(formatStates(params.team, states), { states });
124
+ },
125
+ label: "Linear List States",
126
+ name: "linear_list_states",
127
+ parameters: ListStatesParams,
128
+ promptGuidelines: [
129
+ "Use linear_list_states before updating a Linear issue state unless the workflow state UUID is already known.",
130
+ ],
131
+ promptSnippet: "List workflow states for a Linear team",
132
+ });
133
+
134
+ pi.registerTool({
135
+ description:
136
+ "Create a Linear issue. Provide a team UUID, key, or exact name, plus title and optional markdown description/metadata.",
137
+ async execute(_toolCallId, params: CreateIssueParams, signal) {
138
+ const issue = await client.createIssue(params, signal);
139
+ return textResult(
140
+ `Created Linear issue:\n\n${formatIssueSearch("", [issue]).trim()}`,
141
+ {
142
+ issue,
143
+ },
144
+ );
145
+ },
146
+ label: "Linear Create Issue",
147
+ name: "linear_create_issue",
148
+ parameters: CreateIssueParams,
149
+ promptGuidelines: [
150
+ "Use linear_create_issue when the user asks to file or track work in Linear; ask for confirmation before creating issues if intent is ambiguous.",
151
+ "When creating Linear issues from coding findings, include concise reproduction context, expected behavior, and relevant file paths in the description.",
152
+ ],
153
+ promptSnippet: "Create a Linear issue",
154
+ });
155
+
156
+ pi.registerTool({
157
+ description:
158
+ "Update a Linear issue by UUID or identifier. Only provided fields are changed.",
159
+ async execute(_toolCallId, params: UpdateIssueParams, signal) {
160
+ const issue = await client.updateIssue(params, signal);
161
+ return textResult(
162
+ `Updated Linear issue:\n\n${formatIssueSearch("", [issue]).trim()}`,
163
+ {
164
+ issue,
165
+ },
166
+ );
167
+ },
168
+ label: "Linear Update Issue",
169
+ name: "linear_update_issue",
170
+ parameters: UpdateIssueParams,
171
+ promptGuidelines: [
172
+ "Use linear_update_issue for explicit user-requested Linear metadata changes. Do not mark work complete unless checks have passed or the user instructs it.",
173
+ ],
174
+ promptSnippet: "Update a Linear issue",
175
+ });
176
+
177
+ pi.registerTool({
178
+ description:
179
+ "Add a markdown comment to a Linear issue by UUID or identifier.",
180
+ async execute(_toolCallId, params: CommentIssueParams, signal) {
181
+ const comment = await client.commentIssue(
182
+ params.issueId,
183
+ params.body,
184
+ signal,
185
+ );
186
+ return textResult(
187
+ `Added Linear comment ${comment.id} to ${params.issueId}.`,
188
+ {
189
+ comment,
190
+ },
191
+ );
192
+ },
193
+ label: "Linear Comment Issue",
194
+ name: "linear_comment_issue",
195
+ parameters: CommentIssueParams,
196
+ promptGuidelines: [
197
+ "Use linear_comment_issue when the user asks to post an implementation summary, status update, question, or result to a Linear issue.",
198
+ "Before posting to Linear, keep comments concise and factual; include commands run and whether they passed when reporting implementation work.",
199
+ ],
200
+ promptSnippet: "Add a markdown comment to a Linear issue",
201
+ });
202
+ }
package/src/types.ts ADDED
@@ -0,0 +1,114 @@
1
+ export interface LinearUser {
2
+ displayName?: string | null;
3
+ email?: string | null;
4
+ id: string;
5
+ name?: string | null;
6
+ }
7
+
8
+ export interface LinearTeam {
9
+ id: string;
10
+ key: string;
11
+ name: string;
12
+ }
13
+
14
+ export interface LinearState {
15
+ id: string;
16
+ name: string;
17
+ position?: number | null;
18
+ type?: string | null;
19
+ }
20
+
21
+ export interface LinearLabel {
22
+ color?: string | null;
23
+ id: string;
24
+ name: string;
25
+ }
26
+
27
+ export interface LinearProject {
28
+ id: string;
29
+ name: string;
30
+ url?: string | null;
31
+ }
32
+
33
+ export interface LinearComment {
34
+ body: string;
35
+ createdAt: string;
36
+ id: string;
37
+ updatedAt?: string | null;
38
+ user?: LinearUser | null;
39
+ }
40
+
41
+ export interface LinearIssueSummary {
42
+ assignee?: LinearUser | null;
43
+ createdAt: string;
44
+ delegate?: LinearUser | null;
45
+ description?: string | null;
46
+ dueDate?: string | null;
47
+ estimate?: number | null;
48
+ id: string;
49
+ identifier: string;
50
+ labels?: { nodes: LinearLabel[] };
51
+ priority?: number | null;
52
+ priorityLabel?: string | null;
53
+ project?: LinearProject | null;
54
+ state?: LinearState | null;
55
+ team?: LinearTeam | null;
56
+ title: string;
57
+ updatedAt: string;
58
+ url: string;
59
+ }
60
+
61
+ export interface LinearIssue extends LinearIssueSummary {
62
+ comments?: { nodes: LinearComment[] };
63
+ creator?: LinearUser | null;
64
+ }
65
+
66
+ export type LinearViewer = LinearUser;
67
+
68
+ export type LinearSetupResult =
69
+ | { error: string; ok: false }
70
+ | { ok: true; viewer: LinearViewer };
71
+
72
+ export interface LinearSearchParams {
73
+ assigneeEmail?: string;
74
+ assigneeId?: string;
75
+ includeArchived?: boolean;
76
+ labels?: string[];
77
+ limit?: number;
78
+ priority?: number;
79
+ projectName?: string;
80
+ query?: string;
81
+ stateName?: string;
82
+ stateType?:
83
+ | "backlog"
84
+ | "canceled"
85
+ | "completed"
86
+ | "started"
87
+ | "triage"
88
+ | "unstarted";
89
+ team?: string;
90
+ }
91
+
92
+ export interface LinearCreateIssueParams {
93
+ assigneeId?: string;
94
+ description?: string;
95
+ dueDate?: string;
96
+ labelIds?: string[];
97
+ priority?: number;
98
+ projectId?: string;
99
+ stateId?: string;
100
+ team: string;
101
+ title: string;
102
+ }
103
+
104
+ export interface LinearUpdateIssueParams {
105
+ assigneeId?: string | null;
106
+ description?: string;
107
+ dueDate?: string | null;
108
+ issueId: string;
109
+ labelIds?: string[];
110
+ priority?: number | null;
111
+ projectId?: string | null;
112
+ stateId?: string;
113
+ title?: string;
114
+ }