@renseiai/plugin-linear 0.8.6
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/LICENSE +21 -0
- package/README.md +91 -0
- package/dist/src/__tests__/subpath-exports.test.d.ts +2 -0
- package/dist/src/__tests__/subpath-exports.test.d.ts.map +1 -0
- package/dist/src/__tests__/subpath-exports.test.js +136 -0
- package/dist/src/agent-client-project-repo.test.d.ts +2 -0
- package/dist/src/agent-client-project-repo.test.d.ts.map +1 -0
- package/dist/src/agent-client-project-repo.test.js +153 -0
- package/dist/src/agent-client.d.ts +261 -0
- package/dist/src/agent-client.d.ts.map +1 -0
- package/dist/src/agent-client.js +902 -0
- package/dist/src/agent-session.d.ts +303 -0
- package/dist/src/agent-session.d.ts.map +1 -0
- package/dist/src/agent-session.js +969 -0
- package/dist/src/checkbox-utils.d.ts +88 -0
- package/dist/src/checkbox-utils.d.ts.map +1 -0
- package/dist/src/checkbox-utils.js +120 -0
- package/dist/src/circuit-breaker.d.ts +76 -0
- package/dist/src/circuit-breaker.d.ts.map +1 -0
- package/dist/src/circuit-breaker.js +229 -0
- package/dist/src/circuit-breaker.test.d.ts +2 -0
- package/dist/src/circuit-breaker.test.d.ts.map +1 -0
- package/dist/src/circuit-breaker.test.js +292 -0
- package/dist/src/constants.d.ts +87 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.js +101 -0
- package/dist/src/defaults/auto-trigger.d.ts +35 -0
- package/dist/src/defaults/auto-trigger.d.ts.map +1 -0
- package/dist/src/defaults/auto-trigger.js +36 -0
- package/dist/src/defaults/index.d.ts +12 -0
- package/dist/src/defaults/index.d.ts.map +1 -0
- package/dist/src/defaults/index.js +11 -0
- package/dist/src/defaults/priority.d.ts +20 -0
- package/dist/src/defaults/priority.d.ts.map +1 -0
- package/dist/src/defaults/priority.js +38 -0
- package/dist/src/defaults/prompts.d.ts +42 -0
- package/dist/src/defaults/prompts.d.ts.map +1 -0
- package/dist/src/defaults/prompts.js +313 -0
- package/dist/src/defaults/prompts.test.d.ts +2 -0
- package/dist/src/defaults/prompts.test.d.ts.map +1 -0
- package/dist/src/defaults/prompts.test.js +263 -0
- package/dist/src/defaults/work-type-detection.d.ts +19 -0
- package/dist/src/defaults/work-type-detection.d.ts.map +1 -0
- package/dist/src/defaults/work-type-detection.js +98 -0
- package/dist/src/errors.d.ts +91 -0
- package/dist/src/errors.d.ts.map +1 -0
- package/dist/src/errors.js +173 -0
- package/dist/src/frontend-adapter.d.ts +168 -0
- package/dist/src/frontend-adapter.d.ts.map +1 -0
- package/dist/src/frontend-adapter.js +314 -0
- package/dist/src/frontend-adapter.test.d.ts +2 -0
- package/dist/src/frontend-adapter.test.d.ts.map +1 -0
- package/dist/src/frontend-adapter.test.js +545 -0
- package/dist/src/index.d.ts +32 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +35 -0
- package/dist/src/issue-tracker-adapter.d.ts +113 -0
- package/dist/src/issue-tracker-adapter.d.ts.map +1 -0
- package/dist/src/issue-tracker-adapter.js +169 -0
- package/dist/src/issue-tracker-proxy.d.ts +140 -0
- package/dist/src/issue-tracker-proxy.d.ts.map +1 -0
- package/dist/src/issue-tracker-proxy.js +10 -0
- package/dist/src/platform-adapter.d.ts +132 -0
- package/dist/src/platform-adapter.d.ts.map +1 -0
- package/dist/src/platform-adapter.js +260 -0
- package/dist/src/platform-adapter.test.d.ts +2 -0
- package/dist/src/platform-adapter.test.d.ts.map +1 -0
- package/dist/src/platform-adapter.test.js +468 -0
- package/dist/src/proxy-client.d.ts +103 -0
- package/dist/src/proxy-client.d.ts.map +1 -0
- package/dist/src/proxy-client.js +191 -0
- package/dist/src/rate-limiter.d.ts +64 -0
- package/dist/src/rate-limiter.d.ts.map +1 -0
- package/dist/src/rate-limiter.js +163 -0
- package/dist/src/rate-limiter.test.d.ts +2 -0
- package/dist/src/rate-limiter.test.d.ts.map +1 -0
- package/dist/src/rate-limiter.test.js +217 -0
- package/dist/src/retry.d.ts +59 -0
- package/dist/src/retry.d.ts.map +1 -0
- package/dist/src/retry.js +82 -0
- package/dist/src/retry.test.d.ts +2 -0
- package/dist/src/retry.test.d.ts.map +1 -0
- package/dist/src/retry.test.js +266 -0
- package/dist/src/tools/deployment-bridge.d.ts +34 -0
- package/dist/src/tools/deployment-bridge.d.ts.map +1 -0
- package/dist/src/tools/deployment-bridge.js +122 -0
- package/dist/src/tools/linear-plugin.d.ts +23 -0
- package/dist/src/tools/linear-plugin.d.ts.map +1 -0
- package/dist/src/tools/linear-plugin.js +175 -0
- package/dist/src/tools/linear-runner.d.ts +37 -0
- package/dist/src/tools/linear-runner.d.ts.map +1 -0
- package/dist/src/tools/linear-runner.js +810 -0
- package/dist/src/types.d.ts +492 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +148 -0
- package/dist/src/utils.d.ts +52 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/src/utils.js +277 -0
- package/dist/src/webhook-types.d.ts +308 -0
- package/dist/src/webhook-types.d.ts.map +1 -0
- package/dist/src/webhook-types.js +46 -0
- package/package.json +73 -0
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { LinearFrontendAdapter } from './frontend-adapter.js';
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Mock helpers
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
/**
|
|
7
|
+
* Create a mock Linear SDK Issue object.
|
|
8
|
+
*/
|
|
9
|
+
function mockLinearIssue(overrides = {}) {
|
|
10
|
+
const { id = 'issue-uuid-1', identifier = 'SUP-100', title = 'Test Issue', description = 'A test issue description', url = 'https://linear.app/team/issue/SUP-100', priority = 2, createdAt = new Date('2025-01-15T10:00:00Z'), stateName = 'Backlog', labels = ['Feature'], parentId = null, projectName = 'MyProject', } = overrides;
|
|
11
|
+
return {
|
|
12
|
+
id,
|
|
13
|
+
identifier,
|
|
14
|
+
title,
|
|
15
|
+
description,
|
|
16
|
+
url,
|
|
17
|
+
priority,
|
|
18
|
+
createdAt,
|
|
19
|
+
get state() {
|
|
20
|
+
return Promise.resolve(stateName ? { name: stateName } : null);
|
|
21
|
+
},
|
|
22
|
+
labels: () => Promise.resolve({ nodes: labels.map((name) => ({ name })) }),
|
|
23
|
+
get parent() {
|
|
24
|
+
return Promise.resolve(parentId ? { id: parentId } : null);
|
|
25
|
+
},
|
|
26
|
+
get project() {
|
|
27
|
+
return Promise.resolve(projectName ? { name: projectName } : null);
|
|
28
|
+
},
|
|
29
|
+
get team() {
|
|
30
|
+
return Promise.resolve({ id: 'team-1', name: 'Engineering' });
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Create a mock Linear SDK Comment object.
|
|
36
|
+
*/
|
|
37
|
+
function mockLinearComment(overrides = {}) {
|
|
38
|
+
const { id = 'comment-uuid-1', body = 'Test comment', userId = 'user-1', userName = 'Test User', createdAt = new Date('2025-01-15T12:00:00Z'), } = overrides;
|
|
39
|
+
return {
|
|
40
|
+
id,
|
|
41
|
+
body,
|
|
42
|
+
createdAt,
|
|
43
|
+
get user() {
|
|
44
|
+
return Promise.resolve({ id: userId, name: userName });
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Create a mock LinearAgentClient with all methods stubbed.
|
|
50
|
+
*/
|
|
51
|
+
function createMockClient() {
|
|
52
|
+
const mocks = {
|
|
53
|
+
getIssue: vi.fn(),
|
|
54
|
+
updateIssue: vi.fn(),
|
|
55
|
+
getTeamStatuses: vi.fn(),
|
|
56
|
+
updateIssueStatus: vi.fn(),
|
|
57
|
+
createComment: vi.fn(),
|
|
58
|
+
getIssueComments: vi.fn(),
|
|
59
|
+
createIssue: vi.fn(),
|
|
60
|
+
isParentIssue: vi.fn(),
|
|
61
|
+
isChildIssue: vi.fn(),
|
|
62
|
+
getSubIssues: vi.fn(),
|
|
63
|
+
getIssueRelations: vi.fn(),
|
|
64
|
+
createIssueRelation: vi.fn(),
|
|
65
|
+
createAgentSessionOnIssue: vi.fn(),
|
|
66
|
+
updateAgentSession: vi.fn(),
|
|
67
|
+
createAgentActivity: vi.fn(),
|
|
68
|
+
};
|
|
69
|
+
const linearClient = {
|
|
70
|
+
issues: vi.fn(),
|
|
71
|
+
issueLabels: vi.fn(),
|
|
72
|
+
};
|
|
73
|
+
const client = {
|
|
74
|
+
...mocks,
|
|
75
|
+
linearClient,
|
|
76
|
+
};
|
|
77
|
+
// Store the linearClient mock for direct access
|
|
78
|
+
mocks.linearClientIssues = linearClient.issues;
|
|
79
|
+
mocks.linearClientIssueLabels = linearClient.issueLabels;
|
|
80
|
+
return { client, mocks };
|
|
81
|
+
}
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
// Tests
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
describe('LinearFrontendAdapter', () => {
|
|
86
|
+
let adapter;
|
|
87
|
+
let mocks;
|
|
88
|
+
beforeEach(() => {
|
|
89
|
+
const { client, mocks: m } = createMockClient();
|
|
90
|
+
mocks = m;
|
|
91
|
+
adapter = new LinearFrontendAdapter(client);
|
|
92
|
+
});
|
|
93
|
+
// ========================================================================
|
|
94
|
+
// name
|
|
95
|
+
// ========================================================================
|
|
96
|
+
it('has name "linear"', () => {
|
|
97
|
+
expect(adapter.name).toBe('linear');
|
|
98
|
+
});
|
|
99
|
+
// ========================================================================
|
|
100
|
+
// Status mapping
|
|
101
|
+
// ========================================================================
|
|
102
|
+
describe('resolveStatus', () => {
|
|
103
|
+
const cases = [
|
|
104
|
+
['icebox', 'Icebox'],
|
|
105
|
+
['backlog', 'Backlog'],
|
|
106
|
+
['started', 'Started'],
|
|
107
|
+
['finished', 'Finished'],
|
|
108
|
+
['delivered', 'Delivered'],
|
|
109
|
+
['accepted', 'Accepted'],
|
|
110
|
+
['rejected', 'Rejected'],
|
|
111
|
+
['canceled', 'Canceled'],
|
|
112
|
+
];
|
|
113
|
+
it.each(cases)('maps "%s" to "%s"', (abstract, expected) => {
|
|
114
|
+
expect(adapter.resolveStatus(abstract)).toBe(expected);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
describe('abstractStatus', () => {
|
|
118
|
+
const cases = [
|
|
119
|
+
['Icebox', 'icebox'],
|
|
120
|
+
['Backlog', 'backlog'],
|
|
121
|
+
['Started', 'started'],
|
|
122
|
+
['Finished', 'finished'],
|
|
123
|
+
['Delivered', 'delivered'],
|
|
124
|
+
['Accepted', 'accepted'],
|
|
125
|
+
['Rejected', 'rejected'],
|
|
126
|
+
['Canceled', 'canceled'],
|
|
127
|
+
];
|
|
128
|
+
it.each(cases)('maps "%s" to "%s"', (native, expected) => {
|
|
129
|
+
expect(adapter.abstractStatus(native)).toBe(expected);
|
|
130
|
+
});
|
|
131
|
+
it('defaults unknown statuses to "backlog"', () => {
|
|
132
|
+
expect(adapter.abstractStatus('UnknownStatus')).toBe('backlog');
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
describe('status mapping round-trip', () => {
|
|
136
|
+
const allStatuses = [
|
|
137
|
+
'icebox', 'backlog', 'started', 'finished',
|
|
138
|
+
'delivered', 'accepted', 'rejected', 'canceled',
|
|
139
|
+
];
|
|
140
|
+
it.each(allStatuses)('round-trips "%s" through resolve -> abstract', (status) => {
|
|
141
|
+
const native = adapter.resolveStatus(status);
|
|
142
|
+
const roundTripped = adapter.abstractStatus(native);
|
|
143
|
+
expect(roundTripped).toBe(status);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
// ========================================================================
|
|
147
|
+
// getIssue
|
|
148
|
+
// ========================================================================
|
|
149
|
+
describe('getIssue', () => {
|
|
150
|
+
it('maps a Linear Issue to AbstractIssue', async () => {
|
|
151
|
+
const linearIssue = mockLinearIssue({
|
|
152
|
+
id: 'uuid-42',
|
|
153
|
+
identifier: 'SUP-42',
|
|
154
|
+
title: 'Implement feature X',
|
|
155
|
+
description: 'Full description here',
|
|
156
|
+
url: 'https://linear.app/team/issue/SUP-42',
|
|
157
|
+
priority: 1,
|
|
158
|
+
stateName: 'Started',
|
|
159
|
+
labels: ['Bug', 'Urgent'],
|
|
160
|
+
parentId: 'parent-uuid',
|
|
161
|
+
projectName: 'Alpha',
|
|
162
|
+
});
|
|
163
|
+
mocks.getIssue.mockResolvedValue(linearIssue);
|
|
164
|
+
const result = await adapter.getIssue('SUP-42');
|
|
165
|
+
expect(mocks.getIssue).toHaveBeenCalledWith('SUP-42');
|
|
166
|
+
expect(result).toEqual({
|
|
167
|
+
id: 'uuid-42',
|
|
168
|
+
identifier: 'SUP-42',
|
|
169
|
+
title: 'Implement feature X',
|
|
170
|
+
description: 'Full description here',
|
|
171
|
+
url: 'https://linear.app/team/issue/SUP-42',
|
|
172
|
+
status: 'started',
|
|
173
|
+
priority: 1,
|
|
174
|
+
labels: ['Bug', 'Urgent'],
|
|
175
|
+
parentId: 'parent-uuid',
|
|
176
|
+
project: 'Alpha',
|
|
177
|
+
createdAt: linearIssue.createdAt,
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
it('handles issue with no description, parent, or project', async () => {
|
|
181
|
+
const linearIssue = mockLinearIssue({
|
|
182
|
+
description: null,
|
|
183
|
+
parentId: null,
|
|
184
|
+
projectName: null,
|
|
185
|
+
});
|
|
186
|
+
mocks.getIssue.mockResolvedValue(linearIssue);
|
|
187
|
+
const result = await adapter.getIssue('SUP-100');
|
|
188
|
+
expect(result.description).toBeUndefined();
|
|
189
|
+
expect(result.parentId).toBeUndefined();
|
|
190
|
+
expect(result.project).toBeUndefined();
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
// ========================================================================
|
|
194
|
+
// transitionIssue
|
|
195
|
+
// ========================================================================
|
|
196
|
+
describe('transitionIssue', () => {
|
|
197
|
+
it('delegates to updateIssueStatus for standard statuses', async () => {
|
|
198
|
+
mocks.updateIssueStatus.mockResolvedValue(mockLinearIssue());
|
|
199
|
+
await adapter.transitionIssue('issue-1', 'started');
|
|
200
|
+
expect(mocks.updateIssueStatus).toHaveBeenCalledWith('issue-1', 'Started');
|
|
201
|
+
});
|
|
202
|
+
it('handles icebox status via updateIssue (not in LinearWorkflowStatus)', async () => {
|
|
203
|
+
const issue = mockLinearIssue();
|
|
204
|
+
mocks.getIssue.mockResolvedValue(issue);
|
|
205
|
+
mocks.getTeamStatuses.mockResolvedValue({ Icebox: 'state-icebox-id' });
|
|
206
|
+
mocks.updateIssue.mockResolvedValue(issue);
|
|
207
|
+
await adapter.transitionIssue('issue-1', 'icebox');
|
|
208
|
+
expect(mocks.updateIssue).toHaveBeenCalledWith('issue-1', { stateId: 'state-icebox-id' });
|
|
209
|
+
});
|
|
210
|
+
it.each([
|
|
211
|
+
['backlog', 'Backlog'],
|
|
212
|
+
['finished', 'Finished'],
|
|
213
|
+
['delivered', 'Delivered'],
|
|
214
|
+
['accepted', 'Accepted'],
|
|
215
|
+
['rejected', 'Rejected'],
|
|
216
|
+
['canceled', 'Canceled'],
|
|
217
|
+
])('transitions "%s" using updateIssueStatus with "%s"', async (abstract, native) => {
|
|
218
|
+
mocks.updateIssueStatus.mockResolvedValue(mockLinearIssue());
|
|
219
|
+
await adapter.transitionIssue('issue-1', abstract);
|
|
220
|
+
expect(mocks.updateIssueStatus).toHaveBeenCalledWith('issue-1', native);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
// ========================================================================
|
|
224
|
+
// createComment
|
|
225
|
+
// ========================================================================
|
|
226
|
+
describe('createComment', () => {
|
|
227
|
+
it('delegates to client.createComment', async () => {
|
|
228
|
+
mocks.createComment.mockResolvedValue(mockLinearComment());
|
|
229
|
+
await adapter.createComment('issue-1', 'Hello from adapter');
|
|
230
|
+
expect(mocks.createComment).toHaveBeenCalledWith('issue-1', 'Hello from adapter');
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
// ========================================================================
|
|
234
|
+
// getIssueComments
|
|
235
|
+
// ========================================================================
|
|
236
|
+
describe('getIssueComments', () => {
|
|
237
|
+
it('maps Linear comments to AbstractComment[]', async () => {
|
|
238
|
+
const comments = [
|
|
239
|
+
mockLinearComment({ id: 'c1', body: 'First', userId: 'u1', userName: 'Alice' }),
|
|
240
|
+
mockLinearComment({ id: 'c2', body: 'Second', userId: 'u2', userName: 'Bob' }),
|
|
241
|
+
];
|
|
242
|
+
mocks.getIssueComments.mockResolvedValue(comments);
|
|
243
|
+
const result = await adapter.getIssueComments('issue-1');
|
|
244
|
+
expect(result).toHaveLength(2);
|
|
245
|
+
expect(result[0]).toMatchObject({ id: 'c1', body: 'First', userId: 'u1', userName: 'Alice' });
|
|
246
|
+
expect(result[1]).toMatchObject({ id: 'c2', body: 'Second', userId: 'u2', userName: 'Bob' });
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
// ========================================================================
|
|
250
|
+
// isParentIssue
|
|
251
|
+
// ========================================================================
|
|
252
|
+
describe('isParentIssue', () => {
|
|
253
|
+
it('delegates to client.isParentIssue', async () => {
|
|
254
|
+
mocks.isParentIssue.mockResolvedValue(true);
|
|
255
|
+
const result = await adapter.isParentIssue('issue-1');
|
|
256
|
+
expect(mocks.isParentIssue).toHaveBeenCalledWith('issue-1');
|
|
257
|
+
expect(result).toBe(true);
|
|
258
|
+
});
|
|
259
|
+
it('returns false when issue has no children', async () => {
|
|
260
|
+
mocks.isParentIssue.mockResolvedValue(false);
|
|
261
|
+
const result = await adapter.isParentIssue('issue-1');
|
|
262
|
+
expect(result).toBe(false);
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
// ========================================================================
|
|
266
|
+
// getSubIssues
|
|
267
|
+
// ========================================================================
|
|
268
|
+
describe('getSubIssues', () => {
|
|
269
|
+
it('maps sub-issues to AbstractIssue[]', async () => {
|
|
270
|
+
const subIssues = [
|
|
271
|
+
mockLinearIssue({ id: 'sub-1', identifier: 'SUP-101', title: 'Sub 1', stateName: 'Backlog' }),
|
|
272
|
+
mockLinearIssue({ id: 'sub-2', identifier: 'SUP-102', title: 'Sub 2', stateName: 'Started' }),
|
|
273
|
+
];
|
|
274
|
+
mocks.getSubIssues.mockResolvedValue(subIssues);
|
|
275
|
+
const result = await adapter.getSubIssues('parent-1');
|
|
276
|
+
expect(mocks.getSubIssues).toHaveBeenCalledWith('parent-1');
|
|
277
|
+
expect(result).toHaveLength(2);
|
|
278
|
+
expect(result[0].identifier).toBe('SUP-101');
|
|
279
|
+
expect(result[0].status).toBe('backlog');
|
|
280
|
+
expect(result[1].identifier).toBe('SUP-102');
|
|
281
|
+
expect(result[1].status).toBe('started');
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
// ========================================================================
|
|
285
|
+
// createAgentSession
|
|
286
|
+
// ========================================================================
|
|
287
|
+
describe('createAgentSession', () => {
|
|
288
|
+
it('delegates to createAgentSessionOnIssue and returns sessionId', async () => {
|
|
289
|
+
mocks.createAgentSessionOnIssue.mockResolvedValue({
|
|
290
|
+
success: true,
|
|
291
|
+
sessionId: 'session-123',
|
|
292
|
+
});
|
|
293
|
+
const sessionId = await adapter.createAgentSession('issue-1', [
|
|
294
|
+
{ label: 'Dashboard', url: 'https://example.com/dash' },
|
|
295
|
+
]);
|
|
296
|
+
expect(mocks.createAgentSessionOnIssue).toHaveBeenCalledWith({
|
|
297
|
+
issueId: 'issue-1',
|
|
298
|
+
externalUrls: [{ label: 'Dashboard', url: 'https://example.com/dash' }],
|
|
299
|
+
});
|
|
300
|
+
expect(sessionId).toBe('session-123');
|
|
301
|
+
});
|
|
302
|
+
it('throws when sessionId is not returned', async () => {
|
|
303
|
+
mocks.createAgentSessionOnIssue.mockResolvedValue({
|
|
304
|
+
success: true,
|
|
305
|
+
sessionId: undefined,
|
|
306
|
+
});
|
|
307
|
+
await expect(adapter.createAgentSession('issue-1')).rejects.toThrow('Failed to create agent session on issue: issue-1');
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
// ========================================================================
|
|
311
|
+
// updateAgentSession
|
|
312
|
+
// ========================================================================
|
|
313
|
+
describe('updateAgentSession', () => {
|
|
314
|
+
it('delegates to client.updateAgentSession', async () => {
|
|
315
|
+
mocks.updateAgentSession.mockResolvedValue({ success: true });
|
|
316
|
+
await adapter.updateAgentSession('session-1', {
|
|
317
|
+
externalUrls: [{ label: 'Logs', url: 'https://example.com/logs' }],
|
|
318
|
+
plan: [{ content: 'Step 1', status: 'pending' }],
|
|
319
|
+
});
|
|
320
|
+
expect(mocks.updateAgentSession).toHaveBeenCalledWith({
|
|
321
|
+
sessionId: 'session-1',
|
|
322
|
+
externalUrls: [{ label: 'Logs', url: 'https://example.com/logs' }],
|
|
323
|
+
plan: [{ content: 'Step 1', status: 'pending' }],
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
// ========================================================================
|
|
328
|
+
// createActivity
|
|
329
|
+
// ========================================================================
|
|
330
|
+
describe('createActivity', () => {
|
|
331
|
+
it('wraps content as ThoughtActivityContent and delegates', async () => {
|
|
332
|
+
mocks.createAgentActivity.mockResolvedValue({ success: true });
|
|
333
|
+
await adapter.createActivity('session-1', 'thought', 'Analyzing the codebase');
|
|
334
|
+
expect(mocks.createAgentActivity).toHaveBeenCalledWith({
|
|
335
|
+
agentSessionId: 'session-1',
|
|
336
|
+
content: {
|
|
337
|
+
type: 'thought',
|
|
338
|
+
body: 'Analyzing the codebase',
|
|
339
|
+
},
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
// ========================================================================
|
|
344
|
+
// createIssue
|
|
345
|
+
// ========================================================================
|
|
346
|
+
describe('createIssue', () => {
|
|
347
|
+
it('creates an issue and maps result to AbstractIssue', async () => {
|
|
348
|
+
const createdIssue = mockLinearIssue({
|
|
349
|
+
id: 'new-uuid',
|
|
350
|
+
identifier: 'SUP-200',
|
|
351
|
+
title: 'New Issue',
|
|
352
|
+
stateName: 'Backlog',
|
|
353
|
+
});
|
|
354
|
+
mocks.getTeamStatuses.mockResolvedValue({ Backlog: 'state-backlog-id' });
|
|
355
|
+
mocks.createIssue.mockResolvedValue(createdIssue);
|
|
356
|
+
const result = await adapter.createIssue({
|
|
357
|
+
title: 'New Issue',
|
|
358
|
+
teamId: 'team-1',
|
|
359
|
+
description: 'Issue description',
|
|
360
|
+
status: 'backlog',
|
|
361
|
+
parentId: 'parent-1',
|
|
362
|
+
priority: 2,
|
|
363
|
+
});
|
|
364
|
+
expect(mocks.createIssue).toHaveBeenCalledWith({
|
|
365
|
+
title: 'New Issue',
|
|
366
|
+
teamId: 'team-1',
|
|
367
|
+
description: 'Issue description',
|
|
368
|
+
stateId: 'state-backlog-id',
|
|
369
|
+
parentId: 'parent-1',
|
|
370
|
+
priority: 2,
|
|
371
|
+
projectId: undefined,
|
|
372
|
+
});
|
|
373
|
+
expect(result.identifier).toBe('SUP-200');
|
|
374
|
+
expect(result.status).toBe('backlog');
|
|
375
|
+
});
|
|
376
|
+
it('creates an issue without status resolution when status not provided', async () => {
|
|
377
|
+
const createdIssue = mockLinearIssue({ id: 'new-uuid', identifier: 'SUP-201' });
|
|
378
|
+
mocks.createIssue.mockResolvedValue(createdIssue);
|
|
379
|
+
await adapter.createIssue({
|
|
380
|
+
title: 'Simple Issue',
|
|
381
|
+
teamId: 'team-1',
|
|
382
|
+
});
|
|
383
|
+
expect(mocks.getTeamStatuses).not.toHaveBeenCalled();
|
|
384
|
+
expect(mocks.createIssue).toHaveBeenCalledWith({
|
|
385
|
+
title: 'Simple Issue',
|
|
386
|
+
teamId: 'team-1',
|
|
387
|
+
description: undefined,
|
|
388
|
+
stateId: undefined,
|
|
389
|
+
parentId: undefined,
|
|
390
|
+
priority: undefined,
|
|
391
|
+
projectId: undefined,
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
});
|
|
395
|
+
// ========================================================================
|
|
396
|
+
// listIssuesByStatus
|
|
397
|
+
// ========================================================================
|
|
398
|
+
describe('listIssuesByStatus', () => {
|
|
399
|
+
it('queries LinearClient with project and status filters', async () => {
|
|
400
|
+
const issues = [
|
|
401
|
+
mockLinearIssue({ id: 'i1', identifier: 'SUP-10', stateName: 'Backlog' }),
|
|
402
|
+
mockLinearIssue({ id: 'i2', identifier: 'SUP-11', stateName: 'Backlog' }),
|
|
403
|
+
];
|
|
404
|
+
mocks.linearClientIssues.mockResolvedValue({ nodes: issues });
|
|
405
|
+
const result = await adapter.listIssuesByStatus('MyProject', 'backlog');
|
|
406
|
+
expect(mocks.linearClientIssues).toHaveBeenCalledWith({
|
|
407
|
+
filter: {
|
|
408
|
+
project: { name: { eq: 'MyProject' } },
|
|
409
|
+
state: { name: { eq: 'Backlog' } },
|
|
410
|
+
},
|
|
411
|
+
});
|
|
412
|
+
expect(result).toHaveLength(2);
|
|
413
|
+
expect(result[0].identifier).toBe('SUP-10');
|
|
414
|
+
expect(result[1].identifier).toBe('SUP-11');
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
// ========================================================================
|
|
418
|
+
// getUnblockedIssues
|
|
419
|
+
// ========================================================================
|
|
420
|
+
describe('getUnblockedIssues', () => {
|
|
421
|
+
it('returns only issues without incoming "blocks" relations', async () => {
|
|
422
|
+
const issues = [
|
|
423
|
+
mockLinearIssue({ id: 'i1', identifier: 'SUP-10', stateName: 'Backlog' }),
|
|
424
|
+
mockLinearIssue({ id: 'i2', identifier: 'SUP-11', stateName: 'Backlog' }),
|
|
425
|
+
mockLinearIssue({ id: 'i3', identifier: 'SUP-12', stateName: 'Backlog' }),
|
|
426
|
+
];
|
|
427
|
+
mocks.linearClientIssues.mockResolvedValue({ nodes: issues });
|
|
428
|
+
// i1 is unblocked, i2 is blocked, i3 is unblocked
|
|
429
|
+
mocks.getIssueRelations
|
|
430
|
+
.mockResolvedValueOnce({ relations: [], inverseRelations: [] })
|
|
431
|
+
.mockResolvedValueOnce({
|
|
432
|
+
relations: [],
|
|
433
|
+
inverseRelations: [{ id: 'rel-1', type: 'blocks', issueId: 'blocker-id', relatedIssueId: 'i2' }],
|
|
434
|
+
})
|
|
435
|
+
.mockResolvedValueOnce({ relations: [], inverseRelations: [] });
|
|
436
|
+
const result = await adapter.getUnblockedIssues('MyProject', 'backlog');
|
|
437
|
+
expect(result).toHaveLength(2);
|
|
438
|
+
expect(result[0].identifier).toBe('SUP-10');
|
|
439
|
+
expect(result[1].identifier).toBe('SUP-12');
|
|
440
|
+
});
|
|
441
|
+
it('returns all issues when none are blocked', async () => {
|
|
442
|
+
const issues = [
|
|
443
|
+
mockLinearIssue({ id: 'i1', identifier: 'SUP-10', stateName: 'Backlog' }),
|
|
444
|
+
];
|
|
445
|
+
mocks.linearClientIssues.mockResolvedValue({ nodes: issues });
|
|
446
|
+
mocks.getIssueRelations.mockResolvedValue({ relations: [], inverseRelations: [] });
|
|
447
|
+
const result = await adapter.getUnblockedIssues('MyProject', 'backlog');
|
|
448
|
+
expect(result).toHaveLength(1);
|
|
449
|
+
expect(result[0].identifier).toBe('SUP-10');
|
|
450
|
+
});
|
|
451
|
+
it('returns empty array when all issues are blocked', async () => {
|
|
452
|
+
const issues = [
|
|
453
|
+
mockLinearIssue({ id: 'i1', identifier: 'SUP-10', stateName: 'Backlog' }),
|
|
454
|
+
];
|
|
455
|
+
mocks.linearClientIssues.mockResolvedValue({ nodes: issues });
|
|
456
|
+
mocks.getIssueRelations.mockResolvedValue({
|
|
457
|
+
relations: [],
|
|
458
|
+
inverseRelations: [{ id: 'rel-1', type: 'blocks', issueId: 'blocker-id', relatedIssueId: 'i1' }],
|
|
459
|
+
});
|
|
460
|
+
const result = await adapter.getUnblockedIssues('MyProject', 'backlog');
|
|
461
|
+
expect(result).toHaveLength(0);
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
// ========================================================================
|
|
465
|
+
// isChildIssue
|
|
466
|
+
// ========================================================================
|
|
467
|
+
describe('isChildIssue', () => {
|
|
468
|
+
it('delegates to client.isChildIssue', async () => {
|
|
469
|
+
mocks.isChildIssue.mockResolvedValue(true);
|
|
470
|
+
const result = await adapter.isChildIssue('issue-1');
|
|
471
|
+
expect(mocks.isChildIssue).toHaveBeenCalledWith('issue-1');
|
|
472
|
+
expect(result).toBe(true);
|
|
473
|
+
});
|
|
474
|
+
it('returns false when issue has no parent', async () => {
|
|
475
|
+
mocks.isChildIssue.mockResolvedValue(false);
|
|
476
|
+
const result = await adapter.isChildIssue('issue-1');
|
|
477
|
+
expect(result).toBe(false);
|
|
478
|
+
});
|
|
479
|
+
});
|
|
480
|
+
// ========================================================================
|
|
481
|
+
// createBlockerIssue
|
|
482
|
+
// ========================================================================
|
|
483
|
+
describe('createBlockerIssue', () => {
|
|
484
|
+
it('creates a blocker issue in Icebox with blocking relation', async () => {
|
|
485
|
+
const sourceIssue = mockLinearIssue({ id: 'source-1' });
|
|
486
|
+
mocks.getIssue.mockResolvedValue(sourceIssue);
|
|
487
|
+
mocks.getTeamStatuses.mockResolvedValue({ Icebox: 'state-icebox-id', Backlog: 'state-backlog-id' });
|
|
488
|
+
mocks.linearClientIssueLabels.mockResolvedValue({
|
|
489
|
+
nodes: [{ id: 'label-1', name: 'Needs Human' }, { id: 'label-2', name: 'Bug' }],
|
|
490
|
+
});
|
|
491
|
+
const createdIssue = mockLinearIssue({
|
|
492
|
+
id: 'blocker-uuid',
|
|
493
|
+
identifier: 'SUP-300',
|
|
494
|
+
title: 'Human review needed',
|
|
495
|
+
stateName: 'Icebox',
|
|
496
|
+
});
|
|
497
|
+
mocks.createIssue.mockResolvedValue(createdIssue);
|
|
498
|
+
mocks.createIssueRelation.mockResolvedValue({ success: true, relationId: 'rel-1' });
|
|
499
|
+
const result = await adapter.createBlockerIssue('source-1', {
|
|
500
|
+
title: 'Human review needed',
|
|
501
|
+
description: 'This needs human review',
|
|
502
|
+
});
|
|
503
|
+
expect(mocks.createIssue).toHaveBeenCalledWith({
|
|
504
|
+
title: 'Human review needed',
|
|
505
|
+
description: 'This needs human review',
|
|
506
|
+
teamId: 'team-1',
|
|
507
|
+
projectId: undefined,
|
|
508
|
+
stateId: 'state-icebox-id',
|
|
509
|
+
labelIds: ['label-1'],
|
|
510
|
+
});
|
|
511
|
+
expect(mocks.createIssueRelation).toHaveBeenCalledWith({
|
|
512
|
+
issueId: 'blocker-uuid',
|
|
513
|
+
relatedIssueId: 'source-1',
|
|
514
|
+
type: 'blocks',
|
|
515
|
+
});
|
|
516
|
+
expect(result.identifier).toBe('SUP-300');
|
|
517
|
+
});
|
|
518
|
+
it('uses provided teamId instead of looking up from source issue', async () => {
|
|
519
|
+
mocks.getTeamStatuses.mockResolvedValue({ Icebox: 'state-icebox-id' });
|
|
520
|
+
mocks.linearClientIssueLabels.mockResolvedValue({ nodes: [] });
|
|
521
|
+
const createdIssue = mockLinearIssue({ id: 'blocker-uuid', identifier: 'SUP-301' });
|
|
522
|
+
mocks.createIssue.mockResolvedValue(createdIssue);
|
|
523
|
+
mocks.createIssueRelation.mockResolvedValue({ success: true, relationId: 'rel-1' });
|
|
524
|
+
await adapter.createBlockerIssue('source-1', {
|
|
525
|
+
title: 'Blocker',
|
|
526
|
+
teamId: 'explicit-team',
|
|
527
|
+
});
|
|
528
|
+
// Should NOT call getIssue since teamId was provided
|
|
529
|
+
expect(mocks.getIssue).not.toHaveBeenCalled();
|
|
530
|
+
expect(mocks.getTeamStatuses).toHaveBeenCalledWith('explicit-team');
|
|
531
|
+
});
|
|
532
|
+
it('creates issue without label when "Needs Human" label not found', async () => {
|
|
533
|
+
const sourceIssue = mockLinearIssue({ id: 'source-1' });
|
|
534
|
+
mocks.getIssue.mockResolvedValue(sourceIssue);
|
|
535
|
+
mocks.getTeamStatuses.mockResolvedValue({ Icebox: 'state-icebox-id' });
|
|
536
|
+
mocks.linearClientIssueLabels.mockResolvedValue({ nodes: [{ id: 'l1', name: 'Bug' }] });
|
|
537
|
+
const createdIssue = mockLinearIssue({ id: 'blocker-uuid', identifier: 'SUP-302' });
|
|
538
|
+
mocks.createIssue.mockResolvedValue(createdIssue);
|
|
539
|
+
mocks.createIssueRelation.mockResolvedValue({ success: true });
|
|
540
|
+
await adapter.createBlockerIssue('source-1', { title: 'Blocker' });
|
|
541
|
+
// Should not include labelIds since "Needs Human" was not found
|
|
542
|
+
expect(mocks.createIssue).toHaveBeenCalledWith(expect.not.objectContaining({ labelIds: expect.anything() }));
|
|
543
|
+
});
|
|
544
|
+
});
|
|
545
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type { AgentSessionState, AgentActivityType, AgentActivitySignal, AgentActivityContent, AgentActivityContentPayload, ThoughtActivityContent, ActionActivityContent, ResponseActivityContent, ElicitationActivityContent, ErrorActivityContent, PromptActivityContent, AgentActivityCreateInput, AgentActivityResult, CreateActivityOptions, AgentPlanItemState, AgentPlanItem, AgentPlan, AgentSignals, LinearAgentClientConfig, RetryConfig, LinearWorkflowStatus, StatusMapping, AgentSessionConfig, SessionOperationResult, AgentSessionExternalUrl, AgentSessionUpdateInput, AgentSessionUpdateResult, AgentSessionCreateOnIssueInput, AgentSessionCreateResult, AgentWorkType, IssueRelationType, IssueRelationCreateInput, IssueRelationResult, IssueRelationBatchResult, IssueRelationInfo, IssueRelationsResult, SubIssueGraphNode, SubIssueGraph, SubIssueStatus, RateLimiterStrategy, CircuitBreakerStrategy, CircuitBreakerConfig, LinearApiQuota, } from './types.js';
|
|
2
|
+
export { STATUS_WORK_TYPE_MAP, WORK_TYPE_START_STATUS, WORK_TYPE_COMPLETE_STATUS, WORK_TYPE_FAIL_STATUS, WORK_TYPE_ALLOWED_STATUSES, STATUS_VALID_WORK_TYPES, TERMINAL_STATUSES, WORK_TYPES_REQUIRING_WORKTREE, validateWorkTypeForStatus, getValidWorkTypesForStatus, } from './types.js';
|
|
3
|
+
export type { WorkTypeValidationResult } from './types.js';
|
|
4
|
+
export { LinearAgentError, LinearApiError, LinearRetryExhaustedError, LinearSessionError, LinearActivityError, LinearPlanError, LinearStatusTransitionError, AgentSpawnError, CircuitOpenError, isLinearAgentError, isRetryableError, isAgentSpawnError, isCircuitOpenError, } from './errors.js';
|
|
5
|
+
export { DEFAULT_RETRY_CONFIG, sleep, calculateDelay, withRetry, createRetryWrapper, } from './retry.js';
|
|
6
|
+
export type { RetryContext, RetryCallback, WithRetryOptions } from './retry.js';
|
|
7
|
+
export { LINEAR_COMMENT_MAX_LENGTH, TRUNCATION_MARKER, MAX_COMPLETION_COMMENTS, COMMENT_OVERHEAD, CONTINUATION_MARKER, getDefaultTeamId, getDefaultTeamName, LINEAR_PROJECTS, LINEAR_LABELS, ENVIRONMENT_ISSUE_TYPES, } from './constants.js';
|
|
8
|
+
export type { EnvironmentIssueType } from './constants.js';
|
|
9
|
+
export { truncateText, buildCompletionComment, splitContentIntoComments, buildCompletionComments, } from './utils.js';
|
|
10
|
+
export type { CommentChunk } from './utils.js';
|
|
11
|
+
export { parseCheckboxes, updateCheckbox, updateCheckboxByText, updateCheckboxes, hasCheckboxes, getCheckboxSummary, } from './checkbox-utils.js';
|
|
12
|
+
export type { CheckboxItem, CheckboxUpdate } from './checkbox-utils.js';
|
|
13
|
+
export { TokenBucket, DEFAULT_RATE_LIMIT_CONFIG, extractRetryAfterMs } from './rate-limiter.js';
|
|
14
|
+
export type { TokenBucketConfig } from './rate-limiter.js';
|
|
15
|
+
export { CircuitBreaker, DEFAULT_CIRCUIT_BREAKER_CONFIG } from './circuit-breaker.js';
|
|
16
|
+
export type { CircuitState } from './circuit-breaker.js';
|
|
17
|
+
export type { ProxyRequest, ProxyResponse, IssueTrackerMethod, SerializedIssue, SerializedComment, SerializedViewer, SerializedTeam, ProxyHealthStatus, } from './issue-tracker-proxy.js';
|
|
18
|
+
export { ProxyIssueTrackerClient, createProxyClientIfConfigured } from './proxy-client.js';
|
|
19
|
+
export type { ProxyClientConfig } from './proxy-client.js';
|
|
20
|
+
export { LinearAgentClient, createLinearAgentClient } from './agent-client.js';
|
|
21
|
+
export { AgentSession, createAgentSession } from './agent-session.js';
|
|
22
|
+
export * from './webhook-types.js';
|
|
23
|
+
export { LinearFrontendAdapter } from './frontend-adapter.js';
|
|
24
|
+
export type { AbstractStatus as LinearAbstractStatus, AbstractIssue as LinearAbstractIssue, AbstractComment as LinearAbstractComment, ExternalUrl as LinearExternalUrl, CreateIssueInput as LinearCreateIssueInput, UpdateSessionInput as LinearUpdateSessionInput, } from './frontend-adapter.js';
|
|
25
|
+
export { defaultGeneratePrompt, defaultBuildParentQAContext, defaultBuildParentAcceptanceContext, buildFailureContextBlock, WORK_RESULT_MARKER_INSTRUCTION, PR_SELECTION_GUIDANCE, defaultDetectWorkTypeFromPrompt, defaultGetPriority, defaultParseAutoTriggerConfig, type DefaultAutoTriggerConfig, type WorkflowContext, } from './defaults/index.js';
|
|
26
|
+
export { LinearPlatformAdapter } from './platform-adapter.js';
|
|
27
|
+
export type { GovernorIssue as LinearGovernorIssue } from './platform-adapter.js';
|
|
28
|
+
export { LinearIssueTrackerClient, createLinearStatusMappings } from './issue-tracker-adapter.js';
|
|
29
|
+
export { linearPlugin } from './tools/linear-plugin.js';
|
|
30
|
+
export { runLinear, parseLinearArgs } from './tools/linear-runner.js';
|
|
31
|
+
export type { LinearRunnerConfig, LinearRunnerResult } from './tools/linear-runner.js';
|
|
32
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Work type mappings for status-based routing
|
|
2
|
+
export { STATUS_WORK_TYPE_MAP, WORK_TYPE_START_STATUS, WORK_TYPE_COMPLETE_STATUS, WORK_TYPE_FAIL_STATUS, WORK_TYPE_ALLOWED_STATUSES, STATUS_VALID_WORK_TYPES, TERMINAL_STATUSES, WORK_TYPES_REQUIRING_WORKTREE, validateWorkTypeForStatus, getValidWorkTypesForStatus, } from './types.js';
|
|
3
|
+
// Errors
|
|
4
|
+
export { LinearAgentError, LinearApiError, LinearRetryExhaustedError, LinearSessionError, LinearActivityError, LinearPlanError, LinearStatusTransitionError, AgentSpawnError, CircuitOpenError, isLinearAgentError, isRetryableError, isAgentSpawnError, isCircuitOpenError, } from './errors.js';
|
|
5
|
+
// Retry utilities
|
|
6
|
+
export { DEFAULT_RETRY_CONFIG, sleep, calculateDelay, withRetry, createRetryWrapper, } from './retry.js';
|
|
7
|
+
// Constants
|
|
8
|
+
export { LINEAR_COMMENT_MAX_LENGTH, TRUNCATION_MARKER, MAX_COMPLETION_COMMENTS, COMMENT_OVERHEAD, CONTINUATION_MARKER, getDefaultTeamId, getDefaultTeamName, LINEAR_PROJECTS, LINEAR_LABELS, ENVIRONMENT_ISSUE_TYPES, } from './constants.js';
|
|
9
|
+
// Utilities
|
|
10
|
+
export { truncateText, buildCompletionComment, splitContentIntoComments, buildCompletionComments, } from './utils.js';
|
|
11
|
+
// Checkbox utilities
|
|
12
|
+
export { parseCheckboxes, updateCheckbox, updateCheckboxByText, updateCheckboxes, hasCheckboxes, getCheckboxSummary, } from './checkbox-utils.js';
|
|
13
|
+
// Rate limiter
|
|
14
|
+
export { TokenBucket, DEFAULT_RATE_LIMIT_CONFIG, extractRetryAfterMs } from './rate-limiter.js';
|
|
15
|
+
// Circuit breaker
|
|
16
|
+
export { CircuitBreaker, DEFAULT_CIRCUIT_BREAKER_CONFIG } from './circuit-breaker.js';
|
|
17
|
+
// Proxy client (routes calls through dashboard instead of Linear directly)
|
|
18
|
+
export { ProxyIssueTrackerClient, createProxyClientIfConfigured } from './proxy-client.js';
|
|
19
|
+
// Client
|
|
20
|
+
export { LinearAgentClient, createLinearAgentClient } from './agent-client.js';
|
|
21
|
+
// Session
|
|
22
|
+
export { AgentSession, createAgentSession } from './agent-session.js';
|
|
23
|
+
// Webhook types
|
|
24
|
+
export * from './webhook-types.js';
|
|
25
|
+
// Frontend adapter
|
|
26
|
+
export { LinearFrontendAdapter } from './frontend-adapter.js';
|
|
27
|
+
// Default implementations (prompt templates, work type detection, priority, auto-trigger)
|
|
28
|
+
export { defaultGeneratePrompt, defaultBuildParentQAContext, defaultBuildParentAcceptanceContext, buildFailureContextBlock, WORK_RESULT_MARKER_INSTRUCTION, PR_SELECTION_GUIDANCE, defaultDetectWorkTypeFromPrompt, defaultGetPriority, defaultParseAutoTriggerConfig, } from './defaults/index.js';
|
|
29
|
+
// Platform adapter (Governor event integration)
|
|
30
|
+
export { LinearPlatformAdapter } from './platform-adapter.js';
|
|
31
|
+
// Issue tracker adapter (implements core's IssueTrackerClient)
|
|
32
|
+
export { LinearIssueTrackerClient, createLinearStatusMappings } from './issue-tracker-adapter.js';
|
|
33
|
+
// Tool plugin and runner (moved from core)
|
|
34
|
+
export { linearPlugin } from './tools/linear-plugin.js';
|
|
35
|
+
export { runLinear, parseLinearArgs } from './tools/linear-runner.js';
|