@pome-sh/cli 0.21.5 → 0.21.7

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.
@@ -0,0 +1,628 @@
1
+ import { z, ZodError } from 'zod';
2
+
3
+ // ../packages/twin-linear/dist/src/errors.js
4
+ var LinearTwinError = class extends Error {
5
+ status;
6
+ code;
7
+ extensions;
8
+ constructor(status, code, message, extensions = {}) {
9
+ super(message);
10
+ this.status = status;
11
+ this.code = code;
12
+ this.extensions = extensions;
13
+ this.name = "LinearTwinError";
14
+ }
15
+ toGraphQLError() {
16
+ return {
17
+ message: this.message,
18
+ extensions: {
19
+ code: this.code,
20
+ http: { status: this.status },
21
+ ...this.extensions
22
+ }
23
+ };
24
+ }
25
+ };
26
+ function unauthorizedEnvelope(message = "Authentication required") {
27
+ return {
28
+ status: 401,
29
+ body: {
30
+ errors: [
31
+ {
32
+ message,
33
+ extensions: { code: "AUTHENTICATION_ERROR", http: { status: 401 } }
34
+ }
35
+ ]
36
+ }
37
+ };
38
+ }
39
+ function unsupportedEnvelope(method, path) {
40
+ return {
41
+ status: 501,
42
+ body: {
43
+ message: `Unsupported Linear twin route: ${method} ${path}`,
44
+ errors: [
45
+ {
46
+ message: "Not implemented",
47
+ extensions: { code: "UNIMPLEMENTED", http: { status: 501 } }
48
+ }
49
+ ],
50
+ fidelity: "unsupported",
51
+ method,
52
+ path
53
+ }
54
+ };
55
+ }
56
+ function linearErrorEnvelope(error) {
57
+ if (error instanceof LinearTwinError) {
58
+ return {
59
+ status: error.status,
60
+ body: { errors: [error.toGraphQLError()] }
61
+ };
62
+ }
63
+ if (error instanceof ZodError || error instanceof Error && error.name === "ZodError") {
64
+ const message = error instanceof ZodError ? error.issues[0]?.message ?? "Invalid request" : "Invalid request";
65
+ return {
66
+ status: 400,
67
+ body: {
68
+ errors: [
69
+ {
70
+ message,
71
+ extensions: { code: "BAD_USER_INPUT", http: { status: 400 } }
72
+ }
73
+ ]
74
+ }
75
+ };
76
+ }
77
+ if (error instanceof SyntaxError) {
78
+ return {
79
+ status: 400,
80
+ body: {
81
+ errors: [
82
+ {
83
+ message: "Invalid JSON",
84
+ extensions: { code: "BAD_USER_INPUT", http: { status: 400 } }
85
+ }
86
+ ]
87
+ }
88
+ };
89
+ }
90
+ if (error instanceof Error && error.name === "UnknownToolError") {
91
+ return {
92
+ status: 404,
93
+ body: {
94
+ errors: [
95
+ {
96
+ message: error.message,
97
+ extensions: { code: "NOT_FOUND", http: { status: 404 } }
98
+ }
99
+ ]
100
+ }
101
+ };
102
+ }
103
+ return {
104
+ status: 500,
105
+ body: {
106
+ errors: [
107
+ {
108
+ message: error instanceof Error ? error.message : "Internal Server Error",
109
+ extensions: { code: "INTERNAL_SERVER_ERROR", http: { status: 500 } }
110
+ }
111
+ ]
112
+ }
113
+ };
114
+ }
115
+ function badUserInput(message, extensions = {}) {
116
+ throw new LinearTwinError(400, "BAD_USER_INPUT", message, extensions);
117
+ }
118
+ function notFound(message) {
119
+ throw new LinearTwinError(404, "NOT_FOUND", message);
120
+ }
121
+
122
+ // ../packages/twin-linear/dist/src/types.js
123
+ var DEFAULT_LINEAR_CLOCK = "2026-07-21T00:00:00.000Z";
124
+ var DEFAULT_LINEAR_EMAIL = "admin@pome-twin.test";
125
+ var DEFAULT_LINEAR_TOKEN = "lin_test_admin";
126
+ var LINEAR_PROVIDER_TOKEN_PREFIX = "lin_pome_";
127
+ var DEFAULT_LINEAR_SID = "standalone";
128
+ var DEFAULT_LINEAR_PORT = 3337;
129
+ var DEFAULT_SCOPES = ["read", "write", "issues:create", "comments:create", "admin"];
130
+ var TITLE_MAX_BYTES = 512;
131
+ var BODY_MAX_BYTES = 65536;
132
+ var GRAPHQL_QUERY_MAX_BYTES = 1e5;
133
+ var GRAPHQL_SELECTION_DEPTH_MAX = 20;
134
+ var MCP_PAGE_DEFAULT = 50;
135
+ var MCP_PAGE_MAX = 250;
136
+ var RELAY_PAGE_DEFAULT = 50;
137
+ var RELAY_PAGE_MAX = 250;
138
+ var STATE_EXPORT_CAP = 2e3;
139
+ var OAUTH_CODE_TTL_SECONDS = 600;
140
+ var ACCESS_TOKEN_TTL_SECONDS = 3600;
141
+
142
+ // ../packages/twin-linear/dist/src/webhook-url.js
143
+ function webhookUrlError(url) {
144
+ let parsed;
145
+ try {
146
+ parsed = new URL(url);
147
+ } catch {
148
+ return `Invalid webhook URL: ${url}`;
149
+ }
150
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
151
+ return `Webhook URL must use http or https: ${url}`;
152
+ }
153
+ if (parsed.username || parsed.password) {
154
+ return `Webhook URL must not include credentials`;
155
+ }
156
+ return null;
157
+ }
158
+ function assertWebhookUrl(url) {
159
+ const error = webhookUrlError(url);
160
+ if (error)
161
+ badUserInput(error);
162
+ return url;
163
+ }
164
+ var datetime = z.string().datetime({ offset: true });
165
+ var email = z.string().trim().email().transform((v) => v.toLowerCase());
166
+ var id = z.string().min(1).max(128);
167
+ var scopesField = z.union([z.array(z.string().min(1).max(64)).max(50), z.string().max(500)]).optional();
168
+ var stateType = z.enum(["backlog", "unstarted", "started", "completed", "canceled"]);
169
+ var linearSeedSchema = z.object({
170
+ clock: datetime.default(DEFAULT_LINEAR_CLOCK),
171
+ defaultSid: z.string().min(1).max(128).default(DEFAULT_LINEAR_SID),
172
+ baseUrl: z.string().url().default("http://127.0.0.1:3337"),
173
+ strictScopes: z.boolean().default(false),
174
+ organization: z.object({
175
+ id: id.optional(),
176
+ name: z.string().min(1).max(200).optional(),
177
+ urlKey: z.string().min(1).max(100).optional()
178
+ }).strict().optional(),
179
+ users: z.array(z.object({
180
+ id: id.optional(),
181
+ email,
182
+ name: z.string().min(1).max(200).optional(),
183
+ displayName: z.string().min(1).max(200).optional(),
184
+ avatarUrl: z.string().url().nullable().optional(),
185
+ active: z.boolean().default(true),
186
+ admin: z.boolean().default(false),
187
+ app: z.boolean().default(false)
188
+ }).strict()).max(500).default([]),
189
+ teams: z.array(z.object({
190
+ id: id.optional(),
191
+ key: z.string().min(1).max(20).regex(/^[A-Z][A-Z0-9]*$/),
192
+ name: z.string().min(1).max(200),
193
+ description: z.string().max(2e3).nullable().optional(),
194
+ private: z.boolean().default(false),
195
+ states: z.array(z.object({
196
+ id: id.optional(),
197
+ name: z.string().min(1).max(100),
198
+ type: stateType.optional(),
199
+ position: z.number().int().nonnegative().optional()
200
+ }).strict()).max(50).optional()
201
+ }).strict()).max(50).default([]),
202
+ labels: z.array(z.object({
203
+ id: id.optional(),
204
+ name: z.string().min(1).max(100),
205
+ color: z.string().max(32).optional(),
206
+ description: z.string().max(2e3).nullable().optional(),
207
+ team: z.string().min(1).max(128).optional()
208
+ }).strict()).max(500).default([]),
209
+ projects: z.array(z.object({
210
+ id: id.optional(),
211
+ name: z.string().min(1).max(200),
212
+ description: z.string().max(1e4).nullable().optional(),
213
+ state: z.enum(["planned", "started", "completed", "canceled"]).default("planned"),
214
+ team: z.string().min(1).max(128).optional()
215
+ }).strict()).max(200).default([]),
216
+ cycles: z.array(z.object({
217
+ id: id.optional(),
218
+ team: z.string().min(1).max(128),
219
+ name: z.string().min(1).max(200),
220
+ number: z.number().int().positive().optional(),
221
+ startsAt: datetime.nullable().optional(),
222
+ endsAt: datetime.nullable().optional()
223
+ }).strict()).max(200).default([]),
224
+ issues: z.array(z.object({
225
+ id: id.optional(),
226
+ team: z.string().min(1).max(128),
227
+ title: z.string().min(1).max(512),
228
+ description: z.string().max(65536).nullable().optional(),
229
+ priority: z.union([z.literal(0), z.literal(1), z.literal(2), z.literal(3), z.literal(4)]).default(0),
230
+ state: z.string().min(1).max(128).optional(),
231
+ assignee: z.string().min(1).max(200).optional(),
232
+ creator: z.string().min(1).max(200).optional(),
233
+ delegate: z.string().min(1).max(200).optional(),
234
+ project: z.string().min(1).max(200).optional(),
235
+ cycle: z.string().min(1).max(200).optional(),
236
+ parent: z.string().min(1).max(200).optional(),
237
+ estimate: z.number().int().nonnegative().nullable().optional(),
238
+ labels: z.array(z.string().min(1).max(100)).max(50).default([]),
239
+ dueDate: z.string().max(32).nullable().optional(),
240
+ createdAt: datetime.optional(),
241
+ updatedAt: datetime.optional()
242
+ }).strict()).max(5e3).default([]),
243
+ comments: z.array(z.object({
244
+ id: id.optional(),
245
+ issue: z.string().min(1).max(128),
246
+ body: z.string().min(1).max(65536),
247
+ parent: z.string().min(1).max(128).optional(),
248
+ user: z.string().min(1).max(200).optional(),
249
+ createdAt: datetime.optional()
250
+ }).strict()).max(2e4).default([]),
251
+ documents: z.array(z.object({
252
+ id: id.optional(),
253
+ title: z.string().min(1).max(512),
254
+ content: z.string().max(65536).nullable().optional(),
255
+ slug: z.string().min(1).max(200).optional(),
256
+ project: z.string().min(1).max(200).optional(),
257
+ team: z.string().min(1).max(128).optional(),
258
+ issue: z.string().min(1).max(200).optional(),
259
+ cycle: z.string().min(1).max(200).optional(),
260
+ icon: z.string().max(64).nullable().optional(),
261
+ color: z.string().max(32).nullable().optional(),
262
+ creator: z.string().min(1).max(200).optional(),
263
+ createdAt: datetime.optional(),
264
+ updatedAt: datetime.optional()
265
+ }).strict()).max(500).default([]),
266
+ oauthApps: z.array(z.object({
267
+ id: id.optional(),
268
+ clientId: z.string().min(1).max(200),
269
+ clientSecret: z.string().min(1).max(500),
270
+ name: z.string().min(1).max(200),
271
+ redirectUris: z.array(z.string().url()).min(1).max(20),
272
+ scopes: scopesField,
273
+ actor: z.enum(["user", "app"]).default("user"),
274
+ assignable: z.boolean().default(false),
275
+ mentionable: z.boolean().default(false),
276
+ appUserId: z.string().min(1).max(128).nullable().optional()
277
+ }).strict()).max(20).default([]),
278
+ tokens: z.array(z.object({
279
+ token: z.string().min(1).max(500),
280
+ type: z.enum(["personal", "oauth_access", "client_credentials"]).default("personal"),
281
+ user: z.string().min(1).max(200).optional(),
282
+ app: z.string().min(1).max(200).optional(),
283
+ scopes: scopesField,
284
+ actor: z.enum(["user", "app"]).optional(),
285
+ sid: z.string().min(1).max(128).optional(),
286
+ expiresAt: datetime.nullable().optional()
287
+ }).strict()).max(50).default([]),
288
+ webhooks: z.array(z.object({
289
+ id: id.optional(),
290
+ label: z.string().min(1).max(200).optional(),
291
+ url: z.string().url(),
292
+ resourceTypes: scopesField,
293
+ team: z.string().min(1).max(128).optional(),
294
+ allPublicTeams: z.boolean().optional(),
295
+ secret: z.string().max(500).nullable().optional(),
296
+ enabled: z.boolean().default(true)
297
+ }).strict()).max(50).default([])
298
+ }).strict().superRefine((seed, ctx) => {
299
+ const teamKeys = new Set(seed.teams.map((t) => t.key));
300
+ const teamIds = new Set(seed.teams.map((t) => t.id).filter(Boolean));
301
+ const userEmails = new Set(seed.users.map((u) => u.email));
302
+ const userIds = new Set(seed.users.map((u) => u.id).filter(Boolean));
303
+ const labelNames = new Set(seed.labels.map((l) => l.name));
304
+ const projectNames = new Set(seed.projects.map((p) => p.name));
305
+ const cycleNames = new Set(seed.cycles.map((c) => c.name));
306
+ const issueTitlesByTeam = /* @__PURE__ */ new Map();
307
+ const resolveTeam = (ref) => teamKeys.has(ref) || teamIds.has(ref);
308
+ const resolveUser = (ref) => userEmails.has(ref.toLowerCase()) || userIds.has(ref) || seed.users.some((u) => u.name === ref || u.displayName === ref);
309
+ for (const team of seed.teams) {
310
+ const stateNames = /* @__PURE__ */ new Set();
311
+ for (const state of team.states ?? []) {
312
+ if (stateNames.has(state.name)) {
313
+ ctx.addIssue({ code: "custom", message: `Duplicate workflow state ${state.name} on team ${team.key}` });
314
+ }
315
+ stateNames.add(state.name);
316
+ }
317
+ }
318
+ for (const label of seed.labels) {
319
+ if (label.team && !resolveTeam(label.team)) {
320
+ ctx.addIssue({ code: "custom", message: `Label team not found: ${label.team}` });
321
+ }
322
+ }
323
+ for (const project of seed.projects) {
324
+ if (project.team && !resolveTeam(project.team)) {
325
+ ctx.addIssue({ code: "custom", message: `Project team not found: ${project.team}` });
326
+ }
327
+ }
328
+ for (const cycle of seed.cycles) {
329
+ if (!resolveTeam(cycle.team)) {
330
+ ctx.addIssue({ code: "custom", message: `Cycle team not found: ${cycle.team}` });
331
+ }
332
+ }
333
+ for (const issue of seed.issues) {
334
+ if (!resolveTeam(issue.team)) {
335
+ ctx.addIssue({ code: "custom", message: `Issue team not found: ${issue.team}` });
336
+ }
337
+ if (issue.assignee && !resolveUser(issue.assignee)) {
338
+ ctx.addIssue({ code: "custom", message: `Issue assignee not found: ${issue.assignee}` });
339
+ }
340
+ if (issue.creator && !resolveUser(issue.creator)) {
341
+ ctx.addIssue({ code: "custom", message: `Issue creator not found: ${issue.creator}` });
342
+ }
343
+ if (issue.delegate && !resolveUser(issue.delegate)) {
344
+ ctx.addIssue({ code: "custom", message: `Issue delegate not found: ${issue.delegate}` });
345
+ }
346
+ if (issue.project && !projectNames.has(issue.project) && !seed.projects.some((p) => p.id === issue.project)) {
347
+ ctx.addIssue({ code: "custom", message: `Issue project not found: ${issue.project}` });
348
+ }
349
+ if (issue.cycle && !cycleNames.has(issue.cycle) && !seed.cycles.some((c) => c.id === issue.cycle || String(c.number) === issue.cycle)) {
350
+ ctx.addIssue({ code: "custom", message: `Issue cycle not found: ${issue.cycle}` });
351
+ }
352
+ for (const label of issue.labels) {
353
+ if (!labelNames.has(label) && !seed.labels.some((l) => l.id === label)) {
354
+ ctx.addIssue({ code: "custom", message: `Issue label not found: ${label}` });
355
+ }
356
+ }
357
+ if (issue.state) {
358
+ const team = seed.teams.find((t) => t.key === issue.team || t.id === issue.team);
359
+ const states = team?.states ?? [];
360
+ const defaultNames = ["Backlog", "Todo", "In Progress", "Done", "Canceled"];
361
+ const ok = states.some((s) => s.name === issue.state || s.id === issue.state) || defaultNames.includes(issue.state);
362
+ if (!ok && states.length > 0) {
363
+ ctx.addIssue({ code: "custom", message: `Issue state not found: ${issue.state}` });
364
+ }
365
+ }
366
+ const titles = issueTitlesByTeam.get(issue.team) ?? /* @__PURE__ */ new Set();
367
+ if (titles.has(issue.title)) {
368
+ ctx.addIssue({ code: "custom", message: `Duplicate issue title on team ${issue.team}: ${issue.title}` });
369
+ }
370
+ titles.add(issue.title);
371
+ issueTitlesByTeam.set(issue.team, titles);
372
+ }
373
+ for (const comment of seed.comments) {
374
+ const issueOk = seed.issues.some((i) => i.id === comment.issue || i.title === comment.issue) || /^[A-Z]+-\d+$/.test(comment.issue);
375
+ if (!issueOk) {
376
+ ctx.addIssue({ code: "custom", message: `Comment issue not found: ${comment.issue}` });
377
+ }
378
+ if (comment.user && !resolveUser(comment.user)) {
379
+ ctx.addIssue({ code: "custom", message: `Comment user not found: ${comment.user}` });
380
+ }
381
+ }
382
+ for (const app of seed.oauthApps) {
383
+ for (const uri of app.redirectUris) {
384
+ try {
385
+ new URL(uri);
386
+ } catch {
387
+ ctx.addIssue({ code: "custom", message: `Invalid OAuth redirect URI: ${uri}` });
388
+ }
389
+ }
390
+ if (app.appUserId && !resolveUser(app.appUserId)) {
391
+ ctx.addIssue({ code: "custom", message: `OAuth app user not found: ${app.appUserId}` });
392
+ }
393
+ }
394
+ for (const token of seed.tokens) {
395
+ if (token.user && !resolveUser(token.user)) {
396
+ ctx.addIssue({ code: "custom", message: `Token user not found: ${token.user}` });
397
+ }
398
+ if (token.app && !seed.oauthApps.some((a) => a.clientId === token.app || a.id === token.app)) {
399
+ ctx.addIssue({ code: "custom", message: `Token app not found: ${token.app}` });
400
+ }
401
+ }
402
+ for (const webhook of seed.webhooks) {
403
+ const urlError = webhookUrlError(webhook.url);
404
+ if (urlError) {
405
+ ctx.addIssue({ code: "custom", message: urlError });
406
+ }
407
+ if (webhook.team && !resolveTeam(webhook.team)) {
408
+ ctx.addIssue({ code: "custom", message: `Webhook team not found: ${webhook.team}` });
409
+ }
410
+ }
411
+ });
412
+ function parseSeed(input) {
413
+ return linearSeedSchema.parse(input);
414
+ }
415
+ function loadSeedFromEnv(env = process.env) {
416
+ const raw = env.POME_SEED_JSON;
417
+ if (!raw)
418
+ return parseSeed(defaultSeedState());
419
+ let parsed;
420
+ try {
421
+ parsed = JSON.parse(raw);
422
+ } catch (error) {
423
+ throw new Error(`POME_SEED_JSON is not valid JSON: ${error.message}`);
424
+ }
425
+ return parseSeed(parsed);
426
+ }
427
+ function defaultSeedState() {
428
+ return {
429
+ clock: DEFAULT_LINEAR_CLOCK,
430
+ defaultSid: DEFAULT_LINEAR_SID,
431
+ baseUrl: "http://127.0.0.1:3337",
432
+ strictScopes: false,
433
+ organization: {
434
+ id: "org_pome",
435
+ name: "Pome Twin",
436
+ urlKey: "pome-twin"
437
+ },
438
+ users: [
439
+ {
440
+ id: "user_admin",
441
+ email: DEFAULT_LINEAR_EMAIL,
442
+ name: "Admin User",
443
+ displayName: "Admin",
444
+ admin: true,
445
+ active: true
446
+ },
447
+ {
448
+ id: "user_dev",
449
+ email: "dev@pome-twin.test",
450
+ name: "Developer",
451
+ displayName: "Dev",
452
+ admin: false,
453
+ active: true
454
+ },
455
+ {
456
+ id: "user_agent",
457
+ email: "agent@pome-twin.test",
458
+ name: "Pome Agent",
459
+ displayName: "Pome Agent",
460
+ admin: false,
461
+ active: true,
462
+ app: true
463
+ }
464
+ ],
465
+ teams: [
466
+ {
467
+ id: "team_eng",
468
+ key: "ENG",
469
+ name: "Engineering",
470
+ description: "Default engineering team",
471
+ private: false,
472
+ states: [
473
+ { id: "state_backlog", name: "Backlog", type: "backlog", position: 0 },
474
+ { id: "state_todo", name: "Todo", type: "unstarted", position: 1 },
475
+ { id: "state_progress", name: "In Progress", type: "started", position: 2 },
476
+ { id: "state_done", name: "Done", type: "completed", position: 3 },
477
+ { id: "state_canceled", name: "Canceled", type: "canceled", position: 4 }
478
+ ]
479
+ }
480
+ ],
481
+ labels: [
482
+ { id: "label_bug", name: "Bug", color: "#d92d20", team: "ENG", description: "Defect" },
483
+ { id: "label_feature", name: "Feature", color: "#2563eb", team: "ENG", description: "New work" },
484
+ { id: "label_agent", name: "Agent", color: "#0f766e", team: "ENG", description: "Agent triage" }
485
+ ],
486
+ projects: [
487
+ {
488
+ id: "project_local",
489
+ name: "Local Twin",
490
+ description: "Agent evaluation workspace",
491
+ state: "started",
492
+ team: "ENG"
493
+ }
494
+ ],
495
+ cycles: [
496
+ {
497
+ id: "cycle_1",
498
+ team: "ENG",
499
+ name: "Cycle 1",
500
+ number: 1,
501
+ startsAt: "2026-07-14T00:00:00.000Z",
502
+ endsAt: "2026-07-28T00:00:00.000Z"
503
+ }
504
+ ],
505
+ issues: [
506
+ {
507
+ id: "issue_backlog",
508
+ team: "ENG",
509
+ title: "Triage inbox for agent eval",
510
+ description: "Backlog item waiting for triage.",
511
+ priority: 2,
512
+ state: "Backlog",
513
+ assignee: "dev@pome-twin.test",
514
+ creator: DEFAULT_LINEAR_EMAIL,
515
+ project: "Local Twin",
516
+ cycle: "Cycle 1",
517
+ labels: ["Agent"],
518
+ createdAt: "2026-07-20T10:00:00.000Z",
519
+ updatedAt: "2026-07-20T10:00:00.000Z"
520
+ },
521
+ {
522
+ id: "issue_todo",
523
+ team: "ENG",
524
+ title: "Ship Linear twin GraphQL surface",
525
+ description: "Implement issue CRUD + comments for agent testing.",
526
+ priority: 3,
527
+ state: "Todo",
528
+ assignee: "dev@pome-twin.test",
529
+ creator: DEFAULT_LINEAR_EMAIL,
530
+ project: "Local Twin",
531
+ cycle: "Cycle 1",
532
+ labels: ["Feature"],
533
+ createdAt: "2026-07-20T12:00:00.000Z",
534
+ updatedAt: "2026-07-20T14:00:00.000Z"
535
+ },
536
+ {
537
+ id: "issue_progress",
538
+ team: "ENG",
539
+ title: "Wire MCP tools to commands",
540
+ description: "Keep GraphQL and MCP on the same LinearDomain layer.",
541
+ priority: 2,
542
+ state: "In Progress",
543
+ assignee: DEFAULT_LINEAR_EMAIL,
544
+ creator: DEFAULT_LINEAR_EMAIL,
545
+ project: "Local Twin",
546
+ cycle: "Cycle 1",
547
+ labels: ["Feature", "Agent"],
548
+ createdAt: "2026-07-20T16:00:00.000Z",
549
+ updatedAt: "2026-07-21T00:00:00.000Z"
550
+ },
551
+ {
552
+ id: "issue_done",
553
+ team: "ENG",
554
+ title: "Seed multi-issue agent world",
555
+ description: "Default seed covers backlog/todo/progress/done.",
556
+ priority: 1,
557
+ state: "Done",
558
+ assignee: "dev@pome-twin.test",
559
+ creator: DEFAULT_LINEAR_EMAIL,
560
+ project: "Local Twin",
561
+ labels: ["Bug"],
562
+ createdAt: "2026-07-19T09:00:00.000Z",
563
+ updatedAt: "2026-07-20T18:00:00.000Z"
564
+ }
565
+ ],
566
+ comments: [
567
+ {
568
+ id: "comment_1",
569
+ issue: "Ship Linear twin GraphQL surface",
570
+ body: "Starting with viewer + issues queries.",
571
+ user: DEFAULT_LINEAR_EMAIL,
572
+ createdAt: "2026-07-20T13:00:00.000Z"
573
+ },
574
+ {
575
+ id: "comment_2",
576
+ issue: "Wire MCP tools to commands",
577
+ body: "MCP list_issues should match GraphQL issues.",
578
+ user: "dev@pome-twin.test",
579
+ createdAt: "2026-07-20T17:00:00.000Z"
580
+ },
581
+ {
582
+ id: "comment_3",
583
+ issue: "Triage inbox for agent eval",
584
+ body: "@Pome Agent please prioritize this.",
585
+ user: DEFAULT_LINEAR_EMAIL,
586
+ createdAt: "2026-07-20T11:00:00.000Z"
587
+ }
588
+ ],
589
+ oauthApps: [
590
+ {
591
+ id: "oauth_app_1",
592
+ clientId: "lin_example_client_id",
593
+ clientSecret: "example_client_secret",
594
+ name: "Pome Linear App",
595
+ redirectUris: ["http://localhost:3000/api/auth/callback/linear"],
596
+ scopes: [...DEFAULT_SCOPES],
597
+ actor: "user",
598
+ assignable: true,
599
+ mentionable: true,
600
+ appUserId: "user_agent"
601
+ }
602
+ ],
603
+ tokens: [
604
+ {
605
+ token: DEFAULT_LINEAR_TOKEN,
606
+ type: "personal",
607
+ user: DEFAULT_LINEAR_EMAIL,
608
+ scopes: [...DEFAULT_SCOPES],
609
+ actor: "user",
610
+ sid: DEFAULT_LINEAR_SID
611
+ }
612
+ ],
613
+ webhooks: [
614
+ {
615
+ id: "webhook_1",
616
+ label: "Sample webhook",
617
+ url: "http://127.0.0.1:9999/linear-hooks",
618
+ resourceTypes: ["Issue", "Comment"],
619
+ team: "ENG",
620
+ allPublicTeams: false,
621
+ secret: "whsec_test_linear",
622
+ enabled: true
623
+ }
624
+ ]
625
+ };
626
+ }
627
+
628
+ export { ACCESS_TOKEN_TTL_SECONDS, BODY_MAX_BYTES, DEFAULT_LINEAR_CLOCK, DEFAULT_LINEAR_EMAIL, DEFAULT_LINEAR_PORT, DEFAULT_LINEAR_SID, DEFAULT_LINEAR_TOKEN, DEFAULT_SCOPES, GRAPHQL_QUERY_MAX_BYTES, GRAPHQL_SELECTION_DEPTH_MAX, LINEAR_PROVIDER_TOKEN_PREFIX, LinearTwinError, MCP_PAGE_DEFAULT, MCP_PAGE_MAX, OAUTH_CODE_TTL_SECONDS, RELAY_PAGE_DEFAULT, RELAY_PAGE_MAX, STATE_EXPORT_CAP, TITLE_MAX_BYTES, assertWebhookUrl, badUserInput, defaultSeedState, linearErrorEnvelope, linearSeedSchema, loadSeedFromEnv, notFound, parseSeed, unauthorizedEnvelope, unsupportedEnvelope, webhookUrlError };
@@ -1,17 +1,16 @@
1
1
  import { newGroupId, reassuranceBox, twinReadyLine, trialsHeaderLine, trialLine, summaryLines, evaluatingLine, criterionPhrase } from './chunk-RGZBC7NF.js';
2
2
  import { DemoCapacityError, capacityLabel, parseCapacityMarker, capacityKindFrom } from './chunk-ZX4WNSZ5.js';
3
- import { runTask, demoTaskPath, DEMO_TASK_NAME, DEMO_REPO } from './chunk-NWXONLFF.js';
3
+ import { runTask, demoTaskPath, DEMO_TASK_NAME, DEMO_REPO } from './chunk-WR7KP3LE.js';
4
4
  import { getAvailablePort } from './chunk-XDU6TD4O.js';
5
5
  import './chunk-CBFKZZBR.js';
6
- import { createHostedClient, parseTaskFile, uploadRunBlobs, scoreFromFinalizeResponse, scoreStatus, outcomeOf } from './chunk-CLGRZY53.js';
6
+ import { createHostedClient, parseTaskFile, uploadRunBlobs, scoreFromFinalizeResponse, scoreStatus, outcomeOf } from './chunk-4ILBTPO4.js';
7
7
  import './chunk-NW7HGA2K.js';
8
8
  import { HostedQuotaError, HostedOrchError } from './chunk-PQYIAA6K.js';
9
- import './chunk-OKDJRKJV.js';
10
- import './chunk-WVWV7DLA.js';
11
- import './chunk-APAS34RJ.js';
12
- import './chunk-NY55QTVQ.js';
13
- import { bootTwin } from './chunk-JQQPZQOJ.js';
14
- import './chunk-MBM7LY7E.js';
9
+ import './chunk-SGDUD7KK.js';
10
+ import './chunk-NJ246QPJ.js';
11
+ import './chunk-ZKID2HS3.js';
12
+ import { bootTwin } from './chunk-M3X6OT4N.js';
13
+ import './chunk-A3IDZXJY.js';
15
14
  import './chunk-LZIPGCQJ.js';
16
15
  import './chunk-VBATFCWR.js';
17
16
  import './chunk-SG6ZTIMT.js';
@@ -1,14 +1,13 @@
1
1
  import { newGroupId, criterionPhrase } from './chunk-RGZBC7NF.js';
2
- import { runTaskHosted, resolveRunAgentIdentity } from './chunk-OMVRPIBP.js';
2
+ import { runTaskHosted, resolveRunAgentIdentity } from './chunk-JULH5ECU.js';
3
3
  import './chunk-KUVTL4NZ.js';
4
- import { createHostedClient, parseTaskFile, outcomeOf } from './chunk-CLGRZY53.js';
4
+ import { createHostedClient, parseTaskFile, outcomeOf } from './chunk-4ILBTPO4.js';
5
5
  import './chunk-NW7HGA2K.js';
6
6
  import { HostedQuotaError, HostedTrialError } from './chunk-PQYIAA6K.js';
7
- import './chunk-OKDJRKJV.js';
8
- import './chunk-WVWV7DLA.js';
9
- import './chunk-APAS34RJ.js';
10
- import './chunk-NY55QTVQ.js';
11
- import './chunk-MBM7LY7E.js';
7
+ import './chunk-SGDUD7KK.js';
8
+ import './chunk-NJ246QPJ.js';
9
+ import './chunk-ZKID2HS3.js';
10
+ import './chunk-A3IDZXJY.js';
12
11
  import './chunk-LZIPGCQJ.js';
13
12
  import './chunk-VBATFCWR.js';
14
13
  import './chunk-SG6ZTIMT.js';