@renseiai/agentfactory-linear 0.8.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/LICENSE +21 -0
- package/README.md +91 -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 +37 -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 +310 -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 +93 -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 +28 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +30 -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/types.d.ts +492 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +143 -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 +70 -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,28 @@
|
|
|
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
|
+
//# 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,30 @@
|
|
|
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';
|