openclaw-linear 0.3.0 → 0.5.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 +132 -55
- package/dist/event-router.d.ts +6 -0
- package/dist/event-router.d.ts.map +1 -1
- package/dist/event-router.js +216 -48
- package/dist/event-router.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +105 -5
- package/dist/index.js.map +1 -1
- package/dist/linear-api.d.ts +13 -0
- package/dist/linear-api.d.ts.map +1 -0
- package/dist/linear-api.js +120 -0
- package/dist/linear-api.js.map +1 -0
- package/dist/tools/linear-comment-tool.d.ts +3 -0
- package/dist/tools/linear-comment-tool.d.ts.map +1 -0
- package/dist/tools/linear-comment-tool.js +113 -0
- package/dist/tools/linear-comment-tool.js.map +1 -0
- package/dist/tools/linear-issue-tool.d.ts +3 -0
- package/dist/tools/linear-issue-tool.d.ts.map +1 -0
- package/dist/tools/linear-issue-tool.js +240 -0
- package/dist/tools/linear-issue-tool.js.map +1 -0
- package/dist/tools/linear-project-tool.d.ts +3 -0
- package/dist/tools/linear-project-tool.d.ts.map +1 -0
- package/dist/tools/linear-project-tool.js +126 -0
- package/dist/tools/linear-project-tool.js.map +1 -0
- package/dist/tools/linear-relation-tool.d.ts +3 -0
- package/dist/tools/linear-relation-tool.d.ts.map +1 -0
- package/dist/tools/linear-relation-tool.js +148 -0
- package/dist/tools/linear-relation-tool.js.map +1 -0
- package/dist/tools/linear-team-tool.d.ts +3 -0
- package/dist/tools/linear-team-tool.d.ts.map +1 -0
- package/dist/tools/linear-team-tool.js +64 -0
- package/dist/tools/linear-team-tool.js.map +1 -0
- package/dist/{queue-tool.d.ts → tools/queue-tool.d.ts} +1 -1
- package/dist/tools/queue-tool.d.ts.map +1 -0
- package/dist/{queue-tool.js → tools/queue-tool.js} +19 -4
- package/dist/tools/queue-tool.js.map +1 -0
- package/dist/webhook-handler.d.ts +1 -0
- package/dist/webhook-handler.d.ts.map +1 -1
- package/dist/webhook-handler.js +1 -0
- package/dist/webhook-handler.js.map +1 -1
- package/dist/work-queue.d.ts +14 -8
- package/dist/work-queue.d.ts.map +1 -1
- package/dist/work-queue.js +112 -107
- package/dist/work-queue.js.map +1 -1
- package/openclaw.plugin.json +11 -1
- package/package.json +2 -2
- package/skills/linear/SKILL.md +126 -0
- package/dist/queue-tool.d.ts.map +0 -1
- package/dist/queue-tool.js.map +0 -1
- package/skills/linear-queue/SKILL.md +0 -47
package/dist/index.js
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
import { createWebhookHandler } from "./webhook-handler.js";
|
|
2
2
|
import { createEventRouter } from "./event-router.js";
|
|
3
3
|
import { InboxQueue } from "./work-queue.js";
|
|
4
|
-
import { createQueueTool } from "./queue-tool.js";
|
|
4
|
+
import { createQueueTool } from "./tools/queue-tool.js";
|
|
5
|
+
import { setApiKey } from "./linear-api.js";
|
|
6
|
+
import { createIssueTool } from "./tools/linear-issue-tool.js";
|
|
7
|
+
import { createCommentTool } from "./tools/linear-comment-tool.js";
|
|
8
|
+
import { createTeamTool } from "./tools/linear-team-tool.js";
|
|
9
|
+
import { createProjectTool } from "./tools/linear-project-tool.js";
|
|
10
|
+
import { createRelationTool } from "./tools/linear-relation-tool.js";
|
|
5
11
|
const CHANNEL_ID = "linear";
|
|
6
12
|
const DEFAULT_DEBOUNCE_MS = 30_000;
|
|
7
13
|
const EVENT_LABELS = {
|
|
8
14
|
"issue.assigned": "Assigned",
|
|
9
15
|
"issue.unassigned": "Unassigned",
|
|
10
16
|
"issue.reassigned": "Reassigned",
|
|
17
|
+
"issue.removed": "Removed",
|
|
18
|
+
"issue.state_removed": "State Removed",
|
|
19
|
+
"issue.state_readded": "State Re-added",
|
|
20
|
+
"issue.priority_changed": "Priority Changed",
|
|
11
21
|
"comment.mention": "Mentioned",
|
|
12
22
|
};
|
|
13
23
|
export function formatConsolidatedMessage(actions) {
|
|
@@ -47,8 +57,13 @@ async function dispatchConsolidatedActions(actions, api, queue) {
|
|
|
47
57
|
},
|
|
48
58
|
});
|
|
49
59
|
// Write to queue deterministically — no LLM involved
|
|
50
|
-
const
|
|
51
|
-
|
|
60
|
+
const entries = actions.map((a) => ({
|
|
61
|
+
id: a.identifier,
|
|
62
|
+
event: a.event,
|
|
63
|
+
summary: a.issueLabel,
|
|
64
|
+
issuePriority: a.issuePriority,
|
|
65
|
+
}));
|
|
66
|
+
const added = await queue.enqueue(entries);
|
|
52
67
|
if (added === 0) {
|
|
53
68
|
api.logger.info("[linear] All notifications deduped — skipping agent dispatch");
|
|
54
69
|
return;
|
|
@@ -89,6 +104,12 @@ let activeDebouncer;
|
|
|
89
104
|
const activeDebouncerKeys = new Set();
|
|
90
105
|
export function activate(api) {
|
|
91
106
|
api.logger.info("Linear plugin activated");
|
|
107
|
+
const linearApiKey = api.pluginConfig?.["apiKey"];
|
|
108
|
+
if (typeof linearApiKey !== "string" || !linearApiKey) {
|
|
109
|
+
api.logger.error("[linear] apiKey is not configured — plugin is inert");
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
setApiKey(linearApiKey);
|
|
92
113
|
const webhookSecret = api.pluginConfig?.["webhookSecret"];
|
|
93
114
|
if (typeof webhookSecret !== "string" || !webhookSecret) {
|
|
94
115
|
api.logger.error("[linear] webhookSecret is not configured — plugin is inert");
|
|
@@ -104,14 +125,81 @@ export function activate(api) {
|
|
|
104
125
|
const debounceMs = (typeof rawDebounceMs === "number" && rawDebounceMs > 0)
|
|
105
126
|
? rawDebounceMs
|
|
106
127
|
: DEFAULT_DEBOUNCE_MS;
|
|
128
|
+
const core = api.runtime;
|
|
129
|
+
const cfg = api.config;
|
|
107
130
|
const queuePath = api.resolvePath("queue/inbox.jsonl");
|
|
108
131
|
const queue = new InboxQueue(queuePath);
|
|
132
|
+
// Recover any stale in_progress items from a previous crash
|
|
133
|
+
queue.recover().then((count) => {
|
|
134
|
+
if (count > 0) {
|
|
135
|
+
api.logger.info(`[linear] Recovered ${count} stale in_progress queue item(s)`);
|
|
136
|
+
}
|
|
137
|
+
}).catch((err) => {
|
|
138
|
+
api.logger.error(`[linear] Queue recovery failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
139
|
+
});
|
|
109
140
|
api.registerTool(createQueueTool(queue));
|
|
110
|
-
|
|
141
|
+
api.registerTool(createIssueTool());
|
|
142
|
+
api.registerTool(createCommentTool());
|
|
143
|
+
api.registerTool(createTeamTool());
|
|
144
|
+
api.registerTool(createProjectTool());
|
|
145
|
+
api.registerTool(createRelationTool());
|
|
146
|
+
// Auto-wake: after a "complete" action, dispatch a fresh session if items remain
|
|
147
|
+
api.on("after_tool_call", async (event) => {
|
|
148
|
+
if (event.toolName !== "linear_queue")
|
|
149
|
+
return;
|
|
150
|
+
if (event.params.action !== "complete")
|
|
151
|
+
return;
|
|
152
|
+
if (event.error)
|
|
153
|
+
return;
|
|
154
|
+
const remaining = await queue.peek();
|
|
155
|
+
if (remaining.length === 0)
|
|
156
|
+
return;
|
|
157
|
+
const remainingCount = remaining.length;
|
|
158
|
+
const peerId = `queue-wake-${Date.now()}`;
|
|
159
|
+
const route = core.channel.routing.resolveAgentRoute({
|
|
160
|
+
cfg,
|
|
161
|
+
channel: CHANNEL_ID,
|
|
162
|
+
accountId: "default",
|
|
163
|
+
peer: { kind: "direct", id: peerId },
|
|
164
|
+
});
|
|
165
|
+
const body = `${remainingCount} item(s) remaining in queue. Use the linear_queue tool to continue processing.`;
|
|
166
|
+
const ctx = core.channel.reply.finalizeInboundContext({
|
|
167
|
+
Body: body,
|
|
168
|
+
BodyForAgent: body,
|
|
169
|
+
RawBody: body,
|
|
170
|
+
CommandBody: body,
|
|
171
|
+
From: `${CHANNEL_ID}:${peerId}`,
|
|
172
|
+
To: `${CHANNEL_ID}:${route.agentId ?? "default"}`,
|
|
173
|
+
SessionKey: route.sessionKey,
|
|
174
|
+
AccountId: route.accountId ?? "default",
|
|
175
|
+
ChatType: "direct",
|
|
176
|
+
ConversationLabel: `Linear: queue check (${remainingCount} remaining)`,
|
|
177
|
+
SenderId: peerId,
|
|
178
|
+
Provider: CHANNEL_ID,
|
|
179
|
+
Surface: CHANNEL_ID,
|
|
180
|
+
OriginatingChannel: CHANNEL_ID,
|
|
181
|
+
OriginatingTo: `${CHANNEL_ID}:${peerId}`,
|
|
182
|
+
});
|
|
183
|
+
core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
184
|
+
ctx,
|
|
185
|
+
cfg,
|
|
186
|
+
dispatcherOptions: {
|
|
187
|
+
deliver: async () => { },
|
|
188
|
+
onError: (err) => {
|
|
189
|
+
api.logger.error(`[linear] Queue wake error: ${err instanceof Error ? err.message : String(err)}`);
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
}).catch((err) => {
|
|
193
|
+
api.logger.error(`[linear] Queue wake dispatch failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
const stateActions = api.pluginConfig?.["stateActions"] ?? undefined;
|
|
197
|
+
const routeEvent = createEventRouter({
|
|
111
198
|
agentMapping,
|
|
112
199
|
logger: api.logger,
|
|
113
200
|
eventFilter: eventFilter.length ? eventFilter : undefined,
|
|
114
201
|
teamIds: teamIds.length ? teamIds : undefined,
|
|
202
|
+
stateActions,
|
|
115
203
|
});
|
|
116
204
|
const debouncer = api.runtime.channel.debounce.createInboundDebouncer({
|
|
117
205
|
debounceMs,
|
|
@@ -129,13 +217,25 @@ export function activate(api) {
|
|
|
129
217
|
webhookSecret,
|
|
130
218
|
logger: api.logger,
|
|
131
219
|
onEvent: (event) => {
|
|
132
|
-
const actions =
|
|
220
|
+
const actions = routeEvent(event);
|
|
133
221
|
for (const action of actions) {
|
|
134
222
|
api.logger.info(`[event-router] ${action.type} agent=${action.agentId} event=${action.event}: ${action.detail}`);
|
|
135
223
|
if (action.type === "wake") {
|
|
136
224
|
activeDebouncerKeys.add(action.agentId);
|
|
137
225
|
debouncer.enqueue(action);
|
|
138
226
|
}
|
|
227
|
+
if (action.type === "notify") {
|
|
228
|
+
queue
|
|
229
|
+
.enqueue([
|
|
230
|
+
{
|
|
231
|
+
id: action.identifier,
|
|
232
|
+
event: action.event,
|
|
233
|
+
summary: action.issueLabel,
|
|
234
|
+
issuePriority: action.issuePriority,
|
|
235
|
+
},
|
|
236
|
+
])
|
|
237
|
+
.catch((err) => api.logger.error(`[linear] Notify enqueue error: ${err instanceof Error ? err.message : String(err)}`));
|
|
238
|
+
}
|
|
139
239
|
}
|
|
140
240
|
},
|
|
141
241
|
});
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAqB,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare function setApiKey(key: string): void;
|
|
2
|
+
/** Reset API key (for testing). */
|
|
3
|
+
export declare function _resetApiKey(): void;
|
|
4
|
+
export declare function graphql<T>(query: string, variables?: Record<string, unknown>): Promise<T>;
|
|
5
|
+
export declare function resolveIssueId(identifier: string): Promise<string>;
|
|
6
|
+
/** Reset issue ID cache (for testing). */
|
|
7
|
+
export declare function _resetIssueIdCache(): void;
|
|
8
|
+
export declare function resolveTeamId(key: string): Promise<string>;
|
|
9
|
+
export declare function resolveStateId(teamId: string, stateName: string): Promise<string>;
|
|
10
|
+
export declare function resolveUserId(nameOrEmail: string): Promise<string>;
|
|
11
|
+
export declare function resolveLabelIds(teamId: string, names: string[]): Promise<string[]>;
|
|
12
|
+
export declare function resolveProjectId(name: string): Promise<string>;
|
|
13
|
+
//# sourceMappingURL=linear-api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear-api.d.ts","sourceRoot":"","sources":["../src/linear-api.ts"],"names":[],"mappings":"AAIA,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE3C;AAED,mCAAmC;AACnC,wBAAgB,YAAY,IAAI,IAAI,CAEnC;AAED,wBAAsB,OAAO,CAAC,CAAC,EAC7B,KAAK,EAAE,MAAM,EACb,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,OAAO,CAAC,CAAC,CAAC,CA4BZ;AAMD,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA8BxE;AAED,0CAA0C;AAC1C,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgBhE;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAgBjB;AAED,wBAAsB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgBxE;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EAAE,GACd,OAAO,CAAC,MAAM,EAAE,CAAC,CAyBnB;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgBpE"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
const API_URL = "https://api.linear.app/graphql";
|
|
2
|
+
let apiKey;
|
|
3
|
+
export function setApiKey(key) {
|
|
4
|
+
apiKey = key;
|
|
5
|
+
}
|
|
6
|
+
/** Reset API key (for testing). */
|
|
7
|
+
export function _resetApiKey() {
|
|
8
|
+
apiKey = undefined;
|
|
9
|
+
}
|
|
10
|
+
export async function graphql(query, variables) {
|
|
11
|
+
if (!apiKey) {
|
|
12
|
+
throw new Error("Linear API key not set — call setApiKey() first");
|
|
13
|
+
}
|
|
14
|
+
const res = await fetch(API_URL, {
|
|
15
|
+
method: "POST",
|
|
16
|
+
headers: {
|
|
17
|
+
Authorization: apiKey,
|
|
18
|
+
"Content-Type": "application/json",
|
|
19
|
+
},
|
|
20
|
+
body: JSON.stringify({ query, variables }),
|
|
21
|
+
});
|
|
22
|
+
if (!res.ok) {
|
|
23
|
+
throw new Error(`Linear API HTTP ${res.status}: ${res.statusText}`);
|
|
24
|
+
}
|
|
25
|
+
const json = (await res.json());
|
|
26
|
+
if (json.errors?.length) {
|
|
27
|
+
throw new Error(`Linear API error: ${json.errors[0].message}`);
|
|
28
|
+
}
|
|
29
|
+
return json.data;
|
|
30
|
+
}
|
|
31
|
+
// --- Name/ID resolution helpers ---
|
|
32
|
+
const issueIdCache = new Map();
|
|
33
|
+
export async function resolveIssueId(identifier) {
|
|
34
|
+
const cached = issueIdCache.get(identifier);
|
|
35
|
+
if (cached)
|
|
36
|
+
return cached;
|
|
37
|
+
const match = identifier.match(/^([A-Za-z]+)-(\d+)$/);
|
|
38
|
+
if (!match) {
|
|
39
|
+
throw new Error(`Invalid issue identifier format: ${identifier} (expected e.g. ENG-123)`);
|
|
40
|
+
}
|
|
41
|
+
const [, teamKey, numStr] = match;
|
|
42
|
+
const num = parseInt(numStr, 10);
|
|
43
|
+
const data = await graphql(`query($teamKey: String!, $num: Float!) {
|
|
44
|
+
issues(filter: { team: { key: { eq: $teamKey } }, number: { eq: $num } }) {
|
|
45
|
+
nodes { id }
|
|
46
|
+
}
|
|
47
|
+
}`, { teamKey: teamKey.toUpperCase(), num });
|
|
48
|
+
if (data.issues.nodes.length === 0) {
|
|
49
|
+
throw new Error(`Issue ${identifier} not found`);
|
|
50
|
+
}
|
|
51
|
+
const id = data.issues.nodes[0].id;
|
|
52
|
+
issueIdCache.set(identifier, id);
|
|
53
|
+
return id;
|
|
54
|
+
}
|
|
55
|
+
/** Reset issue ID cache (for testing). */
|
|
56
|
+
export function _resetIssueIdCache() {
|
|
57
|
+
issueIdCache.clear();
|
|
58
|
+
}
|
|
59
|
+
export async function resolveTeamId(key) {
|
|
60
|
+
const data = await graphql(`query($key: String!) {
|
|
61
|
+
teams(filter: { key: { eq: $key } }) {
|
|
62
|
+
nodes { id }
|
|
63
|
+
}
|
|
64
|
+
}`, { key: key.toUpperCase() });
|
|
65
|
+
if (data.teams.nodes.length === 0) {
|
|
66
|
+
throw new Error(`Team with key "${key}" not found`);
|
|
67
|
+
}
|
|
68
|
+
return data.teams.nodes[0].id;
|
|
69
|
+
}
|
|
70
|
+
export async function resolveStateId(teamId, stateName) {
|
|
71
|
+
const data = await graphql(`query($teamId: String!, $name: String!) {
|
|
72
|
+
workflowStates(filter: { team: { id: { eq: $teamId } }, name: { eqIgnoreCase: $name } }) {
|
|
73
|
+
nodes { id }
|
|
74
|
+
}
|
|
75
|
+
}`, { teamId, name: stateName });
|
|
76
|
+
if (data.workflowStates.nodes.length === 0) {
|
|
77
|
+
throw new Error(`Workflow state "${stateName}" not found`);
|
|
78
|
+
}
|
|
79
|
+
return data.workflowStates.nodes[0].id;
|
|
80
|
+
}
|
|
81
|
+
export async function resolveUserId(nameOrEmail) {
|
|
82
|
+
const data = await graphql(`query($term: String!) {
|
|
83
|
+
users(filter: { or: [{ name: { eqIgnoreCase: $term } }, { email: { eq: $term } }] }) {
|
|
84
|
+
nodes { id }
|
|
85
|
+
}
|
|
86
|
+
}`, { term: nameOrEmail });
|
|
87
|
+
if (data.users.nodes.length === 0) {
|
|
88
|
+
throw new Error(`User "${nameOrEmail}" not found`);
|
|
89
|
+
}
|
|
90
|
+
return data.users.nodes[0].id;
|
|
91
|
+
}
|
|
92
|
+
export async function resolveLabelIds(teamId, names) {
|
|
93
|
+
const data = await graphql(`query($teamId: String!) {
|
|
94
|
+
issueLabels(filter: { team: { id: { eq: $teamId } } }) {
|
|
95
|
+
nodes { id name }
|
|
96
|
+
}
|
|
97
|
+
}`, { teamId });
|
|
98
|
+
const labelMap = new Map(data.issueLabels.nodes.map((l) => [l.name.toLowerCase(), l.id]));
|
|
99
|
+
const ids = [];
|
|
100
|
+
for (const name of names) {
|
|
101
|
+
const id = labelMap.get(name.toLowerCase());
|
|
102
|
+
if (!id) {
|
|
103
|
+
throw new Error(`Label "${name}" not found in team`);
|
|
104
|
+
}
|
|
105
|
+
ids.push(id);
|
|
106
|
+
}
|
|
107
|
+
return ids;
|
|
108
|
+
}
|
|
109
|
+
export async function resolveProjectId(name) {
|
|
110
|
+
const data = await graphql(`query($name: String!) {
|
|
111
|
+
projects(filter: { name: { eqIgnoreCase: $name } }) {
|
|
112
|
+
nodes { id name }
|
|
113
|
+
}
|
|
114
|
+
}`, { name });
|
|
115
|
+
if (data.projects.nodes.length === 0) {
|
|
116
|
+
throw new Error(`Project "${name}" not found`);
|
|
117
|
+
}
|
|
118
|
+
return data.projects.nodes[0].id;
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=linear-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear-api.js","sourceRoot":"","sources":["../src/linear-api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear-comment-tool.d.ts","sourceRoot":"","sources":["../../src/tools/linear-comment-tool.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAqCxD,wBAAgB,iBAAiB,IAAI,YAAY,CA4BhD"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import { jsonResult } from "openclaw/plugin-sdk";
|
|
3
|
+
import { graphql, resolveIssueId } from "../linear-api.js";
|
|
4
|
+
const Params = Type.Object({
|
|
5
|
+
action: Type.Unsafe({
|
|
6
|
+
type: "string",
|
|
7
|
+
enum: ["list", "add", "update"],
|
|
8
|
+
description: "list: get all comments on an issue. " +
|
|
9
|
+
"add: post a new comment. " +
|
|
10
|
+
"update: edit an existing comment.",
|
|
11
|
+
}),
|
|
12
|
+
issueId: Type.Optional(Type.String({
|
|
13
|
+
description: "Issue identifier (e.g. ENG-123). Required for list and add.",
|
|
14
|
+
})),
|
|
15
|
+
commentId: Type.Optional(Type.String({
|
|
16
|
+
description: "Comment ID. Required for update.",
|
|
17
|
+
})),
|
|
18
|
+
body: Type.Optional(Type.String({
|
|
19
|
+
description: "Comment body (markdown). Required for add and update.",
|
|
20
|
+
})),
|
|
21
|
+
parentCommentId: Type.Optional(Type.String({
|
|
22
|
+
description: "Parent comment ID for threading a reply (used with add).",
|
|
23
|
+
})),
|
|
24
|
+
});
|
|
25
|
+
export function createCommentTool() {
|
|
26
|
+
return {
|
|
27
|
+
name: "linear_comment",
|
|
28
|
+
label: "Linear Comment",
|
|
29
|
+
description: "Manage comments on Linear issues. Actions: list, add, update.",
|
|
30
|
+
parameters: Params,
|
|
31
|
+
async execute(_toolCallId, params) {
|
|
32
|
+
try {
|
|
33
|
+
switch (params.action) {
|
|
34
|
+
case "list":
|
|
35
|
+
return await listComments(params);
|
|
36
|
+
case "add":
|
|
37
|
+
return await addComment(params);
|
|
38
|
+
case "update":
|
|
39
|
+
return await updateComment(params);
|
|
40
|
+
default:
|
|
41
|
+
return jsonResult({
|
|
42
|
+
error: `Unknown action: ${params.action}`,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
return jsonResult({
|
|
48
|
+
error: `linear_comment error: ${err instanceof Error ? err.message : String(err)}`,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
async function listComments(params) {
|
|
55
|
+
if (!params.issueId) {
|
|
56
|
+
return jsonResult({ error: "issueId is required for list" });
|
|
57
|
+
}
|
|
58
|
+
const id = await resolveIssueId(params.issueId);
|
|
59
|
+
const data = await graphql(`query($id: String!) {
|
|
60
|
+
issue(id: $id) {
|
|
61
|
+
comments(first: 100) {
|
|
62
|
+
nodes {
|
|
63
|
+
id
|
|
64
|
+
body
|
|
65
|
+
createdAt
|
|
66
|
+
updatedAt
|
|
67
|
+
user { id name }
|
|
68
|
+
parent { id }
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}`, { id });
|
|
73
|
+
return jsonResult({ comments: data.issue.comments.nodes });
|
|
74
|
+
}
|
|
75
|
+
async function addComment(params) {
|
|
76
|
+
if (!params.issueId) {
|
|
77
|
+
return jsonResult({ error: "issueId is required for add" });
|
|
78
|
+
}
|
|
79
|
+
if (!params.body) {
|
|
80
|
+
return jsonResult({ error: "body is required for add" });
|
|
81
|
+
}
|
|
82
|
+
const issueId = await resolveIssueId(params.issueId);
|
|
83
|
+
const input = {
|
|
84
|
+
issueId,
|
|
85
|
+
body: params.body,
|
|
86
|
+
};
|
|
87
|
+
if (params.parentCommentId) {
|
|
88
|
+
input.parentId = params.parentCommentId;
|
|
89
|
+
}
|
|
90
|
+
const data = await graphql(`mutation($input: CommentCreateInput!) {
|
|
91
|
+
commentCreate(input: $input) {
|
|
92
|
+
success
|
|
93
|
+
comment { id body }
|
|
94
|
+
}
|
|
95
|
+
}`, { input });
|
|
96
|
+
return jsonResult(data.commentCreate);
|
|
97
|
+
}
|
|
98
|
+
async function updateComment(params) {
|
|
99
|
+
if (!params.commentId) {
|
|
100
|
+
return jsonResult({ error: "commentId is required for update" });
|
|
101
|
+
}
|
|
102
|
+
if (!params.body) {
|
|
103
|
+
return jsonResult({ error: "body is required for update" });
|
|
104
|
+
}
|
|
105
|
+
const data = await graphql(`mutation($id: String!, $input: CommentUpdateInput!) {
|
|
106
|
+
commentUpdate(id: $id, input: $input) {
|
|
107
|
+
success
|
|
108
|
+
comment { id body }
|
|
109
|
+
}
|
|
110
|
+
}`, { id: params.commentId, input: { body: params.body } });
|
|
111
|
+
return jsonResult(data.commentUpdate);
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=linear-comment-tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear-comment-tool.js","sourceRoot":"","sources":["../../src/tools/linear-comment-tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear-issue-tool.d.ts","sourceRoot":"","sources":["../../src/tools/linear-issue-tool.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AA0ExD,wBAAgB,eAAe,IAAI,YAAY,CAgC9C"}
|