@supaku/agentfactory-linear 0.1.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/dist/src/agent-client.d.ts +195 -0
- package/dist/src/agent-client.d.ts.map +1 -0
- package/dist/src/agent-client.js +548 -0
- package/dist/src/agent-session.d.ts +284 -0
- package/dist/src/agent-session.d.ts.map +1 -0
- package/dist/src/agent-session.js +875 -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/constants.d.ts +77 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.js +87 -0
- package/dist/src/errors.d.ts +79 -0
- package/dist/src/errors.d.ts.map +1 -0
- package/dist/src/errors.js +155 -0
- package/dist/src/index.d.ts +15 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +16 -0
- package/dist/src/retry.d.ts +43 -0
- package/dist/src/retry.d.ts.map +1 -0
- package/dist/src/retry.js +73 -0
- package/dist/src/types.d.ts +412 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +121 -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/package.json +59 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Linear API Utilities
|
|
3
|
+
*
|
|
4
|
+
* Helper functions for working with Linear API
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Truncate a string to a maximum length, adding truncation marker if needed
|
|
8
|
+
*/
|
|
9
|
+
export declare function truncateText(text: string, maxLength?: number): string;
|
|
10
|
+
/**
|
|
11
|
+
* Build a completion comment with smart truncation.
|
|
12
|
+
* Prioritizes: summary > plan status > session ID
|
|
13
|
+
*
|
|
14
|
+
* If the full comment exceeds maxLength:
|
|
15
|
+
* 1. First, truncate plan items to show only states
|
|
16
|
+
* 2. If still too long, truncate the summary
|
|
17
|
+
*/
|
|
18
|
+
export declare function buildCompletionComment(summary: string, planItems: Array<{
|
|
19
|
+
state: string;
|
|
20
|
+
title: string;
|
|
21
|
+
}>, sessionId: string | null, maxLength?: number): string;
|
|
22
|
+
/**
|
|
23
|
+
* Represents a chunk of content split for multiple comments
|
|
24
|
+
*/
|
|
25
|
+
export interface CommentChunk {
|
|
26
|
+
body: string;
|
|
27
|
+
partNumber: number;
|
|
28
|
+
totalParts: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Split content into multiple comment chunks
|
|
32
|
+
*
|
|
33
|
+
* Splitting strategy:
|
|
34
|
+
* 1. Reserve space for part markers
|
|
35
|
+
* 2. Split at paragraph boundaries first
|
|
36
|
+
* 3. If paragraph too long, split at sentence boundaries
|
|
37
|
+
* 4. If sentence too long, split at word boundaries
|
|
38
|
+
* 5. Never split inside code blocks
|
|
39
|
+
*/
|
|
40
|
+
export declare function splitContentIntoComments(content: string, maxLength?: number, maxComments?: number): CommentChunk[];
|
|
41
|
+
/**
|
|
42
|
+
* Build completion comments with smart splitting.
|
|
43
|
+
* Returns multiple comment chunks if content exceeds max length.
|
|
44
|
+
*
|
|
45
|
+
* For backward compatibility, maintains the same header/footer structure
|
|
46
|
+
* as buildCompletionComment, but splits long content across multiple comments.
|
|
47
|
+
*/
|
|
48
|
+
export declare function buildCompletionComments(summary: string, planItems: Array<{
|
|
49
|
+
state: string;
|
|
50
|
+
title: string;
|
|
51
|
+
}>, sessionId: string | null, maxLength?: number): CommentChunk[];
|
|
52
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH;;GAEG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,MAAkC,GAC5C,MAAM,CAOR;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,EAClD,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,SAAS,GAAE,MAAkC,GAC5C,MAAM,CA6DR;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;CACnB;AAkED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,SAAS,GAAE,MAAkC,EAC7C,WAAW,GAAE,MAAgC,GAC5C,YAAY,EAAE,CAqDhB;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,EAClD,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,SAAS,GAAE,MAAkC,GAC5C,YAAY,EAAE,CAiGhB"}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Linear API Utilities
|
|
3
|
+
*
|
|
4
|
+
* Helper functions for working with Linear API
|
|
5
|
+
*/
|
|
6
|
+
import { LINEAR_COMMENT_MAX_LENGTH, TRUNCATION_MARKER, MAX_COMPLETION_COMMENTS, COMMENT_OVERHEAD, CONTINUATION_MARKER, } from './constants';
|
|
7
|
+
/**
|
|
8
|
+
* Truncate a string to a maximum length, adding truncation marker if needed
|
|
9
|
+
*/
|
|
10
|
+
export function truncateText(text, maxLength = LINEAR_COMMENT_MAX_LENGTH) {
|
|
11
|
+
if (text.length <= maxLength) {
|
|
12
|
+
return text;
|
|
13
|
+
}
|
|
14
|
+
const truncateAt = maxLength - TRUNCATION_MARKER.length;
|
|
15
|
+
return text.substring(0, truncateAt) + TRUNCATION_MARKER;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Build a completion comment with smart truncation.
|
|
19
|
+
* Prioritizes: summary > plan status > session ID
|
|
20
|
+
*
|
|
21
|
+
* If the full comment exceeds maxLength:
|
|
22
|
+
* 1. First, truncate plan items to show only states
|
|
23
|
+
* 2. If still too long, truncate the summary
|
|
24
|
+
*/
|
|
25
|
+
export function buildCompletionComment(summary, planItems, sessionId, maxLength = LINEAR_COMMENT_MAX_LENGTH) {
|
|
26
|
+
const stateEmoji = {
|
|
27
|
+
pending: '\u{2B1C}',
|
|
28
|
+
inProgress: '\u{1F504}',
|
|
29
|
+
completed: '\u{2705}',
|
|
30
|
+
canceled: '\u{274C}',
|
|
31
|
+
};
|
|
32
|
+
// Build static parts
|
|
33
|
+
const header = '## Agent Work Complete\n\n';
|
|
34
|
+
const planHeader = '\n\n### Final Plan Status\n\n';
|
|
35
|
+
const footer = `\n\n---\n*Session ID: ${sessionId ?? 'unknown'}*`;
|
|
36
|
+
// Full plan status
|
|
37
|
+
const fullPlanStatus = planItems
|
|
38
|
+
.map((item) => `${stateEmoji[item.state] ?? '\u{2B1C}'} ${item.title}`)
|
|
39
|
+
.join('\n');
|
|
40
|
+
// Abbreviated plan status (just emoji counts)
|
|
41
|
+
const completedCount = planItems.filter((i) => i.state === 'completed').length;
|
|
42
|
+
const pendingCount = planItems.filter((i) => i.state === 'pending').length;
|
|
43
|
+
const canceledCount = planItems.filter((i) => i.state === 'canceled').length;
|
|
44
|
+
const abbreviatedPlanStatus = [
|
|
45
|
+
`\u{2705} ${completedCount} completed`,
|
|
46
|
+
pendingCount > 0 ? `\u{2B1C} ${pendingCount} pending` : null,
|
|
47
|
+
canceledCount > 0 ? `\u{274C} ${canceledCount} canceled` : null,
|
|
48
|
+
]
|
|
49
|
+
.filter(Boolean)
|
|
50
|
+
.join(' | ');
|
|
51
|
+
// Try full comment first
|
|
52
|
+
const fullComment = header + summary + planHeader + fullPlanStatus + footer;
|
|
53
|
+
if (fullComment.length <= maxLength) {
|
|
54
|
+
return fullComment;
|
|
55
|
+
}
|
|
56
|
+
// Try with abbreviated plan
|
|
57
|
+
const abbreviatedComment = header + summary + planHeader + abbreviatedPlanStatus + footer;
|
|
58
|
+
if (abbreviatedComment.length <= maxLength) {
|
|
59
|
+
return abbreviatedComment;
|
|
60
|
+
}
|
|
61
|
+
// Need to truncate summary
|
|
62
|
+
const fixedLength = header.length +
|
|
63
|
+
planHeader.length +
|
|
64
|
+
abbreviatedPlanStatus.length +
|
|
65
|
+
footer.length +
|
|
66
|
+
TRUNCATION_MARKER.length;
|
|
67
|
+
const availableForSummary = maxLength - fixedLength;
|
|
68
|
+
if (availableForSummary > 100) {
|
|
69
|
+
// Only truncate if we have reasonable space
|
|
70
|
+
const truncatedSummary = summary.substring(0, availableForSummary) + TRUNCATION_MARKER;
|
|
71
|
+
return header + truncatedSummary + planHeader + abbreviatedPlanStatus + footer;
|
|
72
|
+
}
|
|
73
|
+
// Extreme case: even the fixed parts are too long, just truncate everything
|
|
74
|
+
return truncateText(fullComment, maxLength);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Check if a position is inside a code block
|
|
78
|
+
*/
|
|
79
|
+
function isInsideCodeBlock(text, position) {
|
|
80
|
+
let insideCodeBlock = false;
|
|
81
|
+
let i = 0;
|
|
82
|
+
while (i < position && i < text.length) {
|
|
83
|
+
if (text.slice(i, i + 3) === '```') {
|
|
84
|
+
insideCodeBlock = !insideCodeBlock;
|
|
85
|
+
i += 3;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
i++;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return insideCodeBlock;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Find a safe split point in text that doesn't break code blocks
|
|
95
|
+
*/
|
|
96
|
+
function findSafeSplitPoint(text, targetLength) {
|
|
97
|
+
if (text.length <= targetLength) {
|
|
98
|
+
return text.length;
|
|
99
|
+
}
|
|
100
|
+
// Try to split at paragraph boundary first
|
|
101
|
+
const paragraphBoundary = text.lastIndexOf('\n\n', targetLength);
|
|
102
|
+
if (paragraphBoundary > targetLength * 0.5 && !isInsideCodeBlock(text, paragraphBoundary)) {
|
|
103
|
+
return paragraphBoundary;
|
|
104
|
+
}
|
|
105
|
+
// Try to split at sentence boundary
|
|
106
|
+
const sentenceEnd = text.lastIndexOf('. ', targetLength);
|
|
107
|
+
if (sentenceEnd > targetLength * 0.5 && !isInsideCodeBlock(text, sentenceEnd)) {
|
|
108
|
+
return sentenceEnd + 1; // Include the period
|
|
109
|
+
}
|
|
110
|
+
// Try to split at newline
|
|
111
|
+
const newline = text.lastIndexOf('\n', targetLength);
|
|
112
|
+
if (newline > targetLength * 0.5 && !isInsideCodeBlock(text, newline)) {
|
|
113
|
+
return newline;
|
|
114
|
+
}
|
|
115
|
+
// Try to split at word boundary
|
|
116
|
+
const wordBoundary = text.lastIndexOf(' ', targetLength);
|
|
117
|
+
if (wordBoundary > targetLength * 0.3 && !isInsideCodeBlock(text, wordBoundary)) {
|
|
118
|
+
return wordBoundary;
|
|
119
|
+
}
|
|
120
|
+
// If we're inside a code block, find the end of it
|
|
121
|
+
if (isInsideCodeBlock(text, targetLength)) {
|
|
122
|
+
// Look for code block end after targetLength
|
|
123
|
+
const codeBlockEnd = text.indexOf('```', targetLength);
|
|
124
|
+
if (codeBlockEnd !== -1 && codeBlockEnd < targetLength * 1.5) {
|
|
125
|
+
// Include the closing fence and newline
|
|
126
|
+
const afterFence = text.indexOf('\n', codeBlockEnd + 3);
|
|
127
|
+
return afterFence !== -1 ? afterFence : codeBlockEnd + 3;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Last resort: split at target length
|
|
131
|
+
return targetLength;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Split content into multiple comment chunks
|
|
135
|
+
*
|
|
136
|
+
* Splitting strategy:
|
|
137
|
+
* 1. Reserve space for part markers
|
|
138
|
+
* 2. Split at paragraph boundaries first
|
|
139
|
+
* 3. If paragraph too long, split at sentence boundaries
|
|
140
|
+
* 4. If sentence too long, split at word boundaries
|
|
141
|
+
* 5. Never split inside code blocks
|
|
142
|
+
*/
|
|
143
|
+
export function splitContentIntoComments(content, maxLength = LINEAR_COMMENT_MAX_LENGTH, maxComments = MAX_COMPLETION_COMMENTS) {
|
|
144
|
+
// Account for overhead (part markers, continuation markers)
|
|
145
|
+
const effectiveMaxLength = maxLength - COMMENT_OVERHEAD;
|
|
146
|
+
if (content.length <= effectiveMaxLength) {
|
|
147
|
+
return [{ body: content, partNumber: 1, totalParts: 1 }];
|
|
148
|
+
}
|
|
149
|
+
const chunks = [];
|
|
150
|
+
let remaining = content;
|
|
151
|
+
while (remaining.length > 0 && chunks.length < maxComments) {
|
|
152
|
+
// Reserve space for continuation marker if not the last chunk
|
|
153
|
+
const reserveForContinuation = remaining.length > effectiveMaxLength
|
|
154
|
+
? CONTINUATION_MARKER.length
|
|
155
|
+
: 0;
|
|
156
|
+
const chunkMaxLength = effectiveMaxLength - reserveForContinuation;
|
|
157
|
+
if (remaining.length <= chunkMaxLength) {
|
|
158
|
+
chunks.push(remaining);
|
|
159
|
+
remaining = '';
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
const splitPoint = findSafeSplitPoint(remaining, chunkMaxLength);
|
|
163
|
+
const chunk = remaining.slice(0, splitPoint).trimEnd();
|
|
164
|
+
chunks.push(chunk);
|
|
165
|
+
remaining = remaining.slice(splitPoint).trimStart();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// If we hit max comments and still have content, append truncation to last chunk
|
|
169
|
+
if (remaining.length > 0 && chunks.length > 0) {
|
|
170
|
+
chunks[chunks.length - 1] += TRUNCATION_MARKER;
|
|
171
|
+
}
|
|
172
|
+
const totalParts = chunks.length;
|
|
173
|
+
return chunks.map((chunk, index) => {
|
|
174
|
+
const partNumber = index + 1;
|
|
175
|
+
const isLastPart = partNumber === totalParts;
|
|
176
|
+
// Add part marker for multi-part comments
|
|
177
|
+
let body = chunk;
|
|
178
|
+
if (totalParts > 1) {
|
|
179
|
+
const partMarker = `\n\n---\n*Part ${partNumber}/${totalParts}*`;
|
|
180
|
+
if (!isLastPart) {
|
|
181
|
+
body = chunk + CONTINUATION_MARKER + partMarker;
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
body = chunk + partMarker;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return { body, partNumber, totalParts };
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Build completion comments with smart splitting.
|
|
192
|
+
* Returns multiple comment chunks if content exceeds max length.
|
|
193
|
+
*
|
|
194
|
+
* For backward compatibility, maintains the same header/footer structure
|
|
195
|
+
* as buildCompletionComment, but splits long content across multiple comments.
|
|
196
|
+
*/
|
|
197
|
+
export function buildCompletionComments(summary, planItems, sessionId, maxLength = LINEAR_COMMENT_MAX_LENGTH) {
|
|
198
|
+
const stateEmoji = {
|
|
199
|
+
pending: '\u{2B1C}',
|
|
200
|
+
inProgress: '\u{1F504}',
|
|
201
|
+
completed: '\u{2705}',
|
|
202
|
+
canceled: '\u{274C}',
|
|
203
|
+
};
|
|
204
|
+
// Build static parts
|
|
205
|
+
const header = '## Agent Work Complete\n\n';
|
|
206
|
+
const planHeader = '\n\n### Final Plan Status\n\n';
|
|
207
|
+
const footer = `\n\n---\n*Session ID: ${sessionId ?? 'unknown'}*`;
|
|
208
|
+
// Full plan status
|
|
209
|
+
const fullPlanStatus = planItems
|
|
210
|
+
.map((item) => `${stateEmoji[item.state] ?? '\u{2B1C}'} ${item.title}`)
|
|
211
|
+
.join('\n');
|
|
212
|
+
// Abbreviated plan status (just emoji counts)
|
|
213
|
+
const completedCount = planItems.filter((i) => i.state === 'completed').length;
|
|
214
|
+
const pendingCount = planItems.filter((i) => i.state === 'pending').length;
|
|
215
|
+
const canceledCount = planItems.filter((i) => i.state === 'canceled').length;
|
|
216
|
+
const abbreviatedPlanStatus = [
|
|
217
|
+
`\u{2705} ${completedCount} completed`,
|
|
218
|
+
pendingCount > 0 ? `\u{2B1C} ${pendingCount} pending` : null,
|
|
219
|
+
canceledCount > 0 ? `\u{274C} ${canceledCount} canceled` : null,
|
|
220
|
+
]
|
|
221
|
+
.filter(Boolean)
|
|
222
|
+
.join(' | ');
|
|
223
|
+
// Try full comment first (single comment)
|
|
224
|
+
const fullComment = header + summary + planHeader + fullPlanStatus + footer;
|
|
225
|
+
if (fullComment.length <= maxLength) {
|
|
226
|
+
return [{ body: fullComment, partNumber: 1, totalParts: 1 }];
|
|
227
|
+
}
|
|
228
|
+
// Try with abbreviated plan (still single comment)
|
|
229
|
+
const abbreviatedComment = header + summary + planHeader + abbreviatedPlanStatus + footer;
|
|
230
|
+
if (abbreviatedComment.length <= maxLength) {
|
|
231
|
+
return [{ body: abbreviatedComment, partNumber: 1, totalParts: 1 }];
|
|
232
|
+
}
|
|
233
|
+
// Need to split into multiple comments
|
|
234
|
+
// First comment gets header + beginning of summary
|
|
235
|
+
// Middle comments get summary continuation
|
|
236
|
+
// Last comment gets end of summary + plan status + footer
|
|
237
|
+
const fixedSuffixLength = planHeader.length + abbreviatedPlanStatus.length + footer.length;
|
|
238
|
+
const headerLength = header.length;
|
|
239
|
+
// Split the summary into chunks
|
|
240
|
+
const summaryChunks = splitContentIntoComments(summary, maxLength - COMMENT_OVERHEAD - Math.max(headerLength, fixedSuffixLength), MAX_COMPLETION_COMMENTS);
|
|
241
|
+
// Build final comments
|
|
242
|
+
const result = [];
|
|
243
|
+
const totalParts = summaryChunks.length;
|
|
244
|
+
for (let i = 0; i < summaryChunks.length; i++) {
|
|
245
|
+
const isFirst = i === 0;
|
|
246
|
+
const isLast = i === summaryChunks.length - 1;
|
|
247
|
+
const partNumber = i + 1;
|
|
248
|
+
let body = '';
|
|
249
|
+
if (isFirst) {
|
|
250
|
+
body += header;
|
|
251
|
+
}
|
|
252
|
+
body += summaryChunks[i].body;
|
|
253
|
+
// Remove the part marker from the chunk (we'll add our own)
|
|
254
|
+
if (totalParts > 1) {
|
|
255
|
+
body = body.replace(/\n\n---\n\*Part \d+\/\d+\*$/, '');
|
|
256
|
+
body = body.replace(new RegExp(escapeRegExp(CONTINUATION_MARKER), 'g'), '');
|
|
257
|
+
}
|
|
258
|
+
if (isLast) {
|
|
259
|
+
body += planHeader + abbreviatedPlanStatus + footer;
|
|
260
|
+
}
|
|
261
|
+
// Add part marker for multi-part comments
|
|
262
|
+
if (totalParts > 1) {
|
|
263
|
+
if (!isLast) {
|
|
264
|
+
body += CONTINUATION_MARKER;
|
|
265
|
+
}
|
|
266
|
+
body += `\n\n---\n*Part ${partNumber}/${totalParts}*`;
|
|
267
|
+
}
|
|
268
|
+
result.push({ body, partNumber, totalParts });
|
|
269
|
+
}
|
|
270
|
+
return result;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Escape special regex characters in a string
|
|
274
|
+
*/
|
|
275
|
+
function escapeRegExp(string) {
|
|
276
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
277
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@supaku/agentfactory-linear",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Linear issue tracker integration for AgentFactory — status transitions, agent sessions, work routing",
|
|
6
|
+
"author": "Supaku (https://supaku.com)",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=22.0.0"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/supaku/agentfactory",
|
|
14
|
+
"directory": "packages/linear"
|
|
15
|
+
},
|
|
16
|
+
"homepage": "https://github.com/supaku/agentfactory/tree/main/packages/linear",
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/supaku/agentfactory/issues"
|
|
19
|
+
},
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"linear",
|
|
25
|
+
"issue-tracker",
|
|
26
|
+
"agent",
|
|
27
|
+
"workflow",
|
|
28
|
+
"automation"
|
|
29
|
+
],
|
|
30
|
+
"main": "./dist/src/index.js",
|
|
31
|
+
"module": "./dist/src/index.js",
|
|
32
|
+
"types": "./dist/src/index.d.ts",
|
|
33
|
+
"exports": {
|
|
34
|
+
".": {
|
|
35
|
+
"types": "./dist/src/index.d.ts",
|
|
36
|
+
"import": "./dist/src/index.js"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist",
|
|
41
|
+
"README.md",
|
|
42
|
+
"LICENSE"
|
|
43
|
+
],
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@linear/sdk": "^70.0.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "^22.5.4",
|
|
49
|
+
"typescript": "^5.7.3",
|
|
50
|
+
"vitest": "^3.2.3"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsc",
|
|
54
|
+
"typecheck": "tsc --noEmit",
|
|
55
|
+
"test": "vitest run",
|
|
56
|
+
"test:watch": "vitest",
|
|
57
|
+
"clean": "rm -rf dist"
|
|
58
|
+
}
|
|
59
|
+
}
|