@elnora-ai/linear 1.0.1 → 1.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.
Files changed (299) hide show
  1. package/.claude-plugin/marketplace.json +7 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +13 -1
  4. package/README.md +116 -26
  5. package/agents/linear-issue-creator.md +129 -17
  6. package/agents/linear-issue-reviewer.md +122 -23
  7. package/agents/linear-issue-updater.md +137 -25
  8. package/agents/linear-state-curator.md +173 -0
  9. package/agents/linear-url-to-issues.md +189 -26
  10. package/commands/linear-cleanup.md +64 -29
  11. package/dist/cli.js +64 -1
  12. package/dist/cli.js.map +1 -1
  13. package/dist/client/auth.d.ts.map +1 -1
  14. package/dist/client/auth.js +13 -2
  15. package/dist/client/auth.js.map +1 -1
  16. package/dist/client/linear-client.d.ts +7 -0
  17. package/dist/client/linear-client.d.ts.map +1 -1
  18. package/dist/client/linear-client.js +13 -1
  19. package/dist/client/linear-client.js.map +1 -1
  20. package/dist/commands/agent-activities.d.ts +3 -0
  21. package/dist/commands/agent-activities.d.ts.map +1 -0
  22. package/dist/commands/agent-activities.js +144 -0
  23. package/dist/commands/agent-activities.js.map +1 -0
  24. package/dist/commands/agent-sessions.d.ts +3 -0
  25. package/dist/commands/agent-sessions.d.ts.map +1 -0
  26. package/dist/commands/agent-sessions.js +132 -0
  27. package/dist/commands/agent-sessions.js.map +1 -0
  28. package/dist/commands/attachments.d.ts +3 -0
  29. package/dist/commands/attachments.d.ts.map +1 -0
  30. package/dist/commands/attachments.js +265 -0
  31. package/dist/commands/attachments.js.map +1 -0
  32. package/dist/commands/audit.d.ts +3 -0
  33. package/dist/commands/audit.d.ts.map +1 -0
  34. package/dist/commands/audit.js +73 -0
  35. package/dist/commands/audit.js.map +1 -0
  36. package/dist/commands/comments.d.ts +3 -0
  37. package/dist/commands/comments.d.ts.map +1 -0
  38. package/dist/commands/comments.js +107 -0
  39. package/dist/commands/comments.js.map +1 -0
  40. package/dist/commands/completion.d.ts +3 -0
  41. package/dist/commands/completion.d.ts.map +1 -0
  42. package/dist/commands/completion.js +62 -0
  43. package/dist/commands/completion.js.map +1 -0
  44. package/dist/commands/context.d.ts +3 -0
  45. package/dist/commands/context.d.ts.map +1 -0
  46. package/dist/commands/context.js +94 -0
  47. package/dist/commands/context.js.map +1 -0
  48. package/dist/commands/curator.d.ts +14 -0
  49. package/dist/commands/curator.d.ts.map +1 -1
  50. package/dist/commands/curator.js +97 -19
  51. package/dist/commands/curator.js.map +1 -1
  52. package/dist/commands/customer-needs.d.ts +3 -0
  53. package/dist/commands/customer-needs.d.ts.map +1 -0
  54. package/dist/commands/customer-needs.js +198 -0
  55. package/dist/commands/customer-needs.js.map +1 -0
  56. package/dist/commands/customers.d.ts +5 -0
  57. package/dist/commands/customers.d.ts.map +1 -0
  58. package/dist/commands/customers.js +201 -0
  59. package/dist/commands/customers.js.map +1 -0
  60. package/dist/commands/cycles.d.ts +3 -0
  61. package/dist/commands/cycles.d.ts.map +1 -0
  62. package/dist/commands/cycles.js +67 -0
  63. package/dist/commands/cycles.js.map +1 -0
  64. package/dist/commands/documents.d.ts +3 -0
  65. package/dist/commands/documents.d.ts.map +1 -0
  66. package/dist/commands/documents.js +105 -0
  67. package/dist/commands/documents.js.map +1 -0
  68. package/dist/commands/favorites.d.ts +3 -0
  69. package/dist/commands/favorites.d.ts.map +1 -0
  70. package/dist/commands/favorites.js +101 -0
  71. package/dist/commands/favorites.js.map +1 -0
  72. package/dist/commands/index.d.ts +30 -0
  73. package/dist/commands/index.d.ts.map +1 -1
  74. package/dist/commands/index.js +30 -0
  75. package/dist/commands/index.js.map +1 -1
  76. package/dist/commands/initiatives.d.ts +3 -0
  77. package/dist/commands/initiatives.d.ts.map +1 -0
  78. package/dist/commands/initiatives.js +106 -0
  79. package/dist/commands/initiatives.js.map +1 -0
  80. package/dist/commands/issues.d.ts +21 -0
  81. package/dist/commands/issues.d.ts.map +1 -0
  82. package/dist/commands/issues.js +993 -0
  83. package/dist/commands/issues.js.map +1 -0
  84. package/dist/commands/labels.d.ts +3 -0
  85. package/dist/commands/labels.d.ts.map +1 -0
  86. package/dist/commands/labels.js +111 -0
  87. package/dist/commands/labels.js.map +1 -0
  88. package/dist/commands/milestones.d.ts +3 -0
  89. package/dist/commands/milestones.d.ts.map +1 -0
  90. package/dist/commands/milestones.js +94 -0
  91. package/dist/commands/milestones.js.map +1 -0
  92. package/dist/commands/notifications.d.ts +3 -0
  93. package/dist/commands/notifications.d.ts.map +1 -0
  94. package/dist/commands/notifications.js +130 -0
  95. package/dist/commands/notifications.js.map +1 -0
  96. package/dist/commands/project-labels.d.ts +3 -0
  97. package/dist/commands/project-labels.d.ts.map +1 -0
  98. package/dist/commands/project-labels.js +80 -0
  99. package/dist/commands/project-labels.js.map +1 -0
  100. package/dist/commands/project-relations.d.ts +3 -0
  101. package/dist/commands/project-relations.d.ts.map +1 -0
  102. package/dist/commands/project-relations.js +96 -0
  103. package/dist/commands/project-relations.js.map +1 -0
  104. package/dist/commands/projects.d.ts +3 -0
  105. package/dist/commands/projects.d.ts.map +1 -0
  106. package/dist/commands/projects.js +263 -0
  107. package/dist/commands/projects.js.map +1 -0
  108. package/dist/commands/quota.d.ts +3 -0
  109. package/dist/commands/quota.d.ts.map +1 -0
  110. package/dist/commands/quota.js +28 -0
  111. package/dist/commands/quota.js.map +1 -0
  112. package/dist/commands/reactions.d.ts +7 -0
  113. package/dist/commands/reactions.d.ts.map +1 -0
  114. package/dist/commands/reactions.js +53 -0
  115. package/dist/commands/reactions.js.map +1 -0
  116. package/dist/commands/relations.d.ts +3 -0
  117. package/dist/commands/relations.d.ts.map +1 -0
  118. package/dist/commands/relations.js +73 -0
  119. package/dist/commands/relations.js.map +1 -0
  120. package/dist/commands/states.d.ts +3 -0
  121. package/dist/commands/states.d.ts.map +1 -0
  122. package/dist/commands/states.js +52 -0
  123. package/dist/commands/states.js.map +1 -0
  124. package/dist/commands/status-updates.d.ts +3 -0
  125. package/dist/commands/status-updates.d.ts.map +1 -0
  126. package/dist/commands/status-updates.js +117 -0
  127. package/dist/commands/status-updates.js.map +1 -0
  128. package/dist/commands/sync.d.ts.map +1 -1
  129. package/dist/commands/sync.js +58 -18
  130. package/dist/commands/sync.js.map +1 -1
  131. package/dist/commands/teams.d.ts +3 -0
  132. package/dist/commands/teams.d.ts.map +1 -0
  133. package/dist/commands/teams.js +135 -0
  134. package/dist/commands/teams.js.map +1 -0
  135. package/dist/commands/templates.d.ts +3 -0
  136. package/dist/commands/templates.d.ts.map +1 -0
  137. package/dist/commands/templates.js +76 -0
  138. package/dist/commands/templates.js.map +1 -0
  139. package/dist/commands/users.d.ts +3 -0
  140. package/dist/commands/users.d.ts.map +1 -0
  141. package/dist/commands/users.js +40 -0
  142. package/dist/commands/users.js.map +1 -0
  143. package/dist/commands/views.d.ts +3 -0
  144. package/dist/commands/views.d.ts.map +1 -0
  145. package/dist/commands/views.js +177 -0
  146. package/dist/commands/views.js.map +1 -0
  147. package/dist/commands/webhooks.d.ts +3 -0
  148. package/dist/commands/webhooks.d.ts.map +1 -0
  149. package/dist/commands/webhooks.js +234 -0
  150. package/dist/commands/webhooks.js.map +1 -0
  151. package/dist/config/loader.d.ts.map +1 -1
  152. package/dist/config/loader.js +3 -0
  153. package/dist/config/loader.js.map +1 -1
  154. package/dist/config/types.d.ts +15 -1
  155. package/dist/config/types.d.ts.map +1 -1
  156. package/dist/config/types.js +1 -0
  157. package/dist/config/types.js.map +1 -1
  158. package/dist/curator/dispatch.d.ts +52 -0
  159. package/dist/curator/dispatch.d.ts.map +1 -0
  160. package/dist/curator/dispatch.js +144 -0
  161. package/dist/curator/dispatch.js.map +1 -0
  162. package/dist/curator/index.d.ts +5 -0
  163. package/dist/curator/index.d.ts.map +1 -0
  164. package/dist/curator/index.js +5 -0
  165. package/dist/curator/index.js.map +1 -0
  166. package/dist/curator/llm.d.ts +70 -0
  167. package/dist/curator/llm.d.ts.map +1 -0
  168. package/dist/curator/llm.js +107 -0
  169. package/dist/curator/llm.js.map +1 -0
  170. package/dist/curator/snapshot.d.ts +34 -0
  171. package/dist/curator/snapshot.d.ts.map +1 -0
  172. package/dist/curator/snapshot.js +127 -0
  173. package/dist/curator/snapshot.js.map +1 -0
  174. package/dist/curator/state.d.ts +50 -0
  175. package/dist/curator/state.d.ts.map +1 -0
  176. package/dist/curator/state.js +125 -0
  177. package/dist/curator/state.js.map +1 -0
  178. package/dist/lib/bulk-graphql.d.ts +144 -0
  179. package/dist/lib/bulk-graphql.d.ts.map +1 -0
  180. package/dist/lib/bulk-graphql.js +380 -0
  181. package/dist/lib/bulk-graphql.js.map +1 -0
  182. package/dist/lib/index.d.ts +2 -0
  183. package/dist/lib/index.d.ts.map +1 -0
  184. package/dist/lib/index.js +2 -0
  185. package/dist/lib/index.js.map +1 -0
  186. package/dist/output/cli.d.ts +17 -0
  187. package/dist/output/cli.d.ts.map +1 -0
  188. package/dist/output/cli.js +252 -0
  189. package/dist/output/cli.js.map +1 -0
  190. package/dist/output/formatter.d.ts +6 -0
  191. package/dist/output/formatter.d.ts.map +1 -1
  192. package/dist/output/formatter.js +10 -0
  193. package/dist/output/formatter.js.map +1 -1
  194. package/dist/output/index.d.ts +1 -0
  195. package/dist/output/index.d.ts.map +1 -1
  196. package/dist/output/index.js +1 -0
  197. package/dist/output/index.js.map +1 -1
  198. package/dist/scripts/sync-linear-templates.d.ts +26 -0
  199. package/dist/scripts/sync-linear-templates.d.ts.map +1 -0
  200. package/dist/scripts/sync-linear-templates.js +115 -0
  201. package/dist/scripts/sync-linear-templates.js.map +1 -0
  202. package/dist/signals/github-commits.d.ts +31 -0
  203. package/dist/signals/github-commits.d.ts.map +1 -0
  204. package/dist/signals/github-commits.js +127 -0
  205. package/dist/signals/github-commits.js.map +1 -0
  206. package/dist/signals/github-pr.d.ts +16 -0
  207. package/dist/signals/github-pr.d.ts.map +1 -0
  208. package/dist/signals/github-pr.js +98 -0
  209. package/dist/signals/github-pr.js.map +1 -0
  210. package/dist/signals/index.d.ts +4 -0
  211. package/dist/signals/index.d.ts.map +1 -1
  212. package/dist/signals/index.js +4 -0
  213. package/dist/signals/index.js.map +1 -1
  214. package/dist/signals/linear-issues.d.ts +20 -0
  215. package/dist/signals/linear-issues.d.ts.map +1 -0
  216. package/dist/signals/linear-issues.js +115 -0
  217. package/dist/signals/linear-issues.js.map +1 -0
  218. package/dist/signals/registry.d.ts +4 -3
  219. package/dist/signals/registry.d.ts.map +1 -1
  220. package/dist/signals/registry.js +33 -11
  221. package/dist/signals/registry.js.map +1 -1
  222. package/dist/signals/slack-messages.d.ts +20 -0
  223. package/dist/signals/slack-messages.d.ts.map +1 -0
  224. package/dist/signals/slack-messages.js +129 -0
  225. package/dist/signals/slack-messages.js.map +1 -0
  226. package/dist/utils/errors.d.ts +63 -0
  227. package/dist/utils/errors.d.ts.map +1 -0
  228. package/dist/utils/errors.js +94 -0
  229. package/dist/utils/errors.js.map +1 -0
  230. package/dist/utils/index.d.ts +9 -0
  231. package/dist/utils/index.d.ts.map +1 -0
  232. package/dist/utils/index.js +9 -0
  233. package/dist/utils/index.js.map +1 -0
  234. package/dist/utils/label-policy.d.ts +53 -0
  235. package/dist/utils/label-policy.d.ts.map +1 -0
  236. package/dist/utils/label-policy.js +93 -0
  237. package/dist/utils/label-policy.js.map +1 -0
  238. package/dist/utils/parse.d.ts +48 -0
  239. package/dist/utils/parse.d.ts.map +1 -0
  240. package/dist/utils/parse.js +133 -0
  241. package/dist/utils/parse.js.map +1 -0
  242. package/dist/utils/project-status.d.ts +6 -0
  243. package/dist/utils/project-status.d.ts.map +1 -0
  244. package/dist/utils/project-status.js +33 -0
  245. package/dist/utils/project-status.js.map +1 -0
  246. package/dist/utils/rate-limit.d.ts +24 -0
  247. package/dist/utils/rate-limit.d.ts.map +1 -0
  248. package/dist/utils/rate-limit.js +89 -0
  249. package/dist/utils/rate-limit.js.map +1 -0
  250. package/dist/utils/resolve.d.ts +84 -0
  251. package/dist/utils/resolve.d.ts.map +1 -0
  252. package/dist/utils/resolve.js +172 -0
  253. package/dist/utils/resolve.js.map +1 -0
  254. package/dist/utils/sleep.d.ts +2 -0
  255. package/dist/utils/sleep.d.ts.map +1 -0
  256. package/dist/utils/sleep.js +4 -0
  257. package/dist/utils/sleep.js.map +1 -0
  258. package/dist/utils/webhook-verify.d.ts +42 -0
  259. package/dist/utils/webhook-verify.d.ts.map +1 -0
  260. package/dist/utils/webhook-verify.js +65 -0
  261. package/dist/utils/webhook-verify.js.map +1 -0
  262. package/package.json +4 -1
  263. package/references/agent-description-template.md +31 -0
  264. package/references/cli-reference.md +227 -0
  265. package/references/curator-tiering-rules.md +76 -0
  266. package/references/label-policy.example.json +37 -0
  267. package/references/label-policy.placeholder.json +6 -0
  268. package/references/settings-template.md +30 -0
  269. package/references/sla-reference.md +70 -0
  270. package/references/template-index.md +34 -0
  271. package/references/workspace-labels.md +124 -0
  272. package/references/workspace-projects.md +56 -0
  273. package/references/workspace-routing.md +58 -0
  274. package/schemas/label-policy.json +72 -0
  275. package/skills/linear-workspace/SKILL.md +65 -4
  276. package/templates/ACC-PRO-provision.md +74 -0
  277. package/templates/ACC-PRV-privileged.md +66 -0
  278. package/templates/ACC-QTR-review.md +77 -0
  279. package/templates/ACC-REV-revoke.md +67 -0
  280. package/templates/AI-USE-capability.md +111 -0
  281. package/templates/AUD-CAP-corrective.md +89 -0
  282. package/templates/AUD-INT-internal.md +92 -0
  283. package/templates/AUD-MGT-management.md +110 -0
  284. package/templates/CHG-MAJ-major.md +110 -0
  285. package/templates/CHG-SIG-significant.md +83 -0
  286. package/templates/CHG-STD-standard.md +47 -0
  287. package/templates/LRN-DOC-lessons.md +75 -0
  288. package/templates/OPS-BCK-backup.md +99 -0
  289. package/templates/OPS-DAT-data-mod.md +98 -0
  290. package/templates/RCA-DOC-root-cause.md +105 -0
  291. package/templates/RSK-ASS-assessment.md +87 -0
  292. package/templates/RSK-VND-vendor.md +113 -0
  293. package/templates/SEC-INC-incident.md +76 -0
  294. package/templates/SEC-PEN-pentest.md +58 -0
  295. package/templates/SEC-VLN-vulnerability.md +69 -0
  296. package/templates/SLA-AVL-availability.md +86 -0
  297. package/templates/SLA-OPS-operational.md +70 -0
  298. package/templates/agent-server-template/README.md +88 -0
  299. package/templates/agent-server-template/server.example.ts +185 -0
@@ -0,0 +1,132 @@
1
+ // `elnora-linear agent-sessions` — Linear agent framework session lifecycle.
2
+ //
3
+ // Sessions are auto-created by Linear when an agent is @mentioned or assigned
4
+ // an issue. The CLI can also create them explicitly for testing the activity
5
+ // emitter.
6
+ import { getClient } from "../client/index.js";
7
+ import { handleAsyncCommand, outputSuccess } from "../output/index.js";
8
+ import { CliError, findIssueByIdentifier, parseLimit } from "../utils/index.js";
9
+ async function formatSession(s) {
10
+ const [appUser, creator, issue, comment] = await Promise.all([s.appUser, s.creator, s.issue, s.comment]);
11
+ return {
12
+ id: s.id,
13
+ appUser: appUser?.name ?? null,
14
+ creator: creator?.name ?? null,
15
+ issue: issue?.identifier ?? null,
16
+ comment: comment?.id ?? null,
17
+ type: s.type ?? null,
18
+ status: s.status ?? null,
19
+ startedAt: s.startedAt ?? null,
20
+ endedAt: s.endedAt ?? null,
21
+ dismissedAt: s.dismissedAt ?? null,
22
+ externalLink: s.externalLink ?? null,
23
+ createdAt: s.createdAt,
24
+ };
25
+ }
26
+ export function setupAgentSessionsCommand(program) {
27
+ const sessions = program.command("agent-sessions").description("Manage Linear agent sessions (agent framework)");
28
+ sessions
29
+ .command("list")
30
+ .description("List agent sessions, newest first")
31
+ .option("--limit <n>", "Max results", "50")
32
+ .action(handleAsyncCommand(async (opts) => {
33
+ const client = await getClient();
34
+ const conn = await client.agentSessions({ first: parseLimit(opts.limit) });
35
+ const items = await Promise.all(conn.nodes.map(formatSession));
36
+ outputSuccess({ sessions: items, count: items.length });
37
+ }));
38
+ sessions
39
+ .command("get <id>")
40
+ .description("Get details of a single agent session")
41
+ .action(handleAsyncCommand(async (id) => {
42
+ const client = await getClient();
43
+ const session = await client.agentSession(id);
44
+ outputSuccess(await formatSession(session));
45
+ }));
46
+ sessions
47
+ .command("create-on-issue <issueId>")
48
+ .description("Create an agent session on an issue (issueId can be ENG-123 or UUID)")
49
+ .option("--external-link <url>", "External agent-hosted page URL")
50
+ .action(handleAsyncCommand(async (issueId, opts) => {
51
+ const client = await getClient();
52
+ const issue = await findIssueByIdentifier(client, issueId);
53
+ const input = { issueId: issue.id };
54
+ if (opts.externalLink)
55
+ input.externalLink = opts.externalLink;
56
+ const payload = await client.agentSessionCreateOnIssue(input);
57
+ if (!payload.success)
58
+ throw new CliError("Failed to create agent session");
59
+ const session = await payload.agentSession;
60
+ outputSuccess({
61
+ created: true,
62
+ session: session ? await formatSession(session) : null,
63
+ });
64
+ }));
65
+ sessions
66
+ .command("create-on-comment <commentId>")
67
+ .description("Create an agent session on a comment thread (commentId is a UUID)")
68
+ .option("--external-link <url>", "External agent-hosted page URL")
69
+ .action(handleAsyncCommand(async (commentId, opts) => {
70
+ const client = await getClient();
71
+ const input = { commentId };
72
+ if (opts.externalLink)
73
+ input.externalLink = opts.externalLink;
74
+ const payload = await client.agentSessionCreateOnComment(input);
75
+ if (!payload.success)
76
+ throw new CliError("Failed to create agent session");
77
+ const session = await payload.agentSession;
78
+ outputSuccess({
79
+ created: true,
80
+ session: session ? await formatSession(session) : null,
81
+ });
82
+ }));
83
+ sessions
84
+ .command("update <id>")
85
+ .description("Update an agent session (replace plan, externalLink, etc — owner-app only)")
86
+ .option("--external-link <url>", "Replace external link")
87
+ .option("--plan <jsonString>", "Replace plan (JSON object as string)")
88
+ .action(handleAsyncCommand(async (id, opts) => {
89
+ const client = await getClient();
90
+ const update = {};
91
+ if (opts.externalLink)
92
+ update.externalLink = opts.externalLink;
93
+ if (opts.plan) {
94
+ try {
95
+ update.plan = JSON.parse(opts.plan);
96
+ }
97
+ catch (e) {
98
+ const msg = e instanceof Error ? e.message : String(e);
99
+ throw new CliError(`Invalid --plan JSON: ${msg}`);
100
+ }
101
+ }
102
+ const payload = await client.updateAgentSession(id, update);
103
+ if (!payload.success)
104
+ throw new CliError("Failed to update agent session");
105
+ const session = await payload.agentSession;
106
+ outputSuccess({
107
+ updated: true,
108
+ session: session ? await formatSession(session) : null,
109
+ });
110
+ }));
111
+ sessions
112
+ .command("update-external-url <id>")
113
+ .description("Add/remove/replace external URLs on a session")
114
+ .option("--external-link <url>", "Replace primary external link")
115
+ .option("--add <url>", "Add a single external URL (label = url)")
116
+ .option("--remove <url>", "Remove a single external URL by URL string")
117
+ .action(handleAsyncCommand(async (id, opts) => {
118
+ const client = await getClient();
119
+ const input = {};
120
+ if (opts.externalLink)
121
+ input.externalLink = opts.externalLink;
122
+ if (opts.add)
123
+ input.addedExternalUrls = [{ url: opts.add, label: opts.add }];
124
+ if (opts.remove)
125
+ input.removedExternalUrls = [opts.remove];
126
+ const payload = await client.agentSessionUpdateExternalUrl(id, input);
127
+ if (!payload.success)
128
+ throw new CliError("Failed to update external URL");
129
+ outputSuccess({ updated: true, id });
130
+ }));
131
+ }
132
+ //# sourceMappingURL=agent-sessions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-sessions.js","sourceRoot":"","sources":["../../src/commands/agent-sessions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from "commander";
2
+ export declare function setupAttachmentsCommand(program: Command): void;
3
+ //# sourceMappingURL=attachments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachments.d.ts","sourceRoot":"","sources":["../../src/commands/attachments.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmCzC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgQ9D"}
@@ -0,0 +1,265 @@
1
+ // `elnora-linear attachments` — manage issue attachments + integration links.
2
+ //
3
+ // `upload` enforces a path safety check: --file must resolve (via realpathSync)
4
+ // under the configured upload root (default $LINEAR_UPLOAD_ROOT or cwd).
5
+ // Symlinks pointing out of the root are rejected. A prompt-injected agent
6
+ // cannot use `attachments upload` to exfiltrate ~/.ssh/id_rsa, ~/.aws/creds,
7
+ // etc.
8
+ import { readFileSync, realpathSync } from "node:fs";
9
+ import { sep as pathSep, resolve as resolvePath } from "node:path";
10
+ import { getClient } from "../client/index.js";
11
+ import { handleAsyncCommand, outputSuccess } from "../output/index.js";
12
+ import { CliError, findIssueByIdentifier, requireYes, ValidationError } from "../utils/index.js";
13
+ function resolveUploadPath(filePath, allowRoot) {
14
+ const root = resolvePath(allowRoot ?? process.env.LINEAR_UPLOAD_ROOT ?? process.cwd());
15
+ const resolvedFile = resolvePath(filePath);
16
+ let realFile;
17
+ let realRoot;
18
+ try {
19
+ realFile = realpathSync(resolvedFile);
20
+ }
21
+ catch (e) {
22
+ const msg = e instanceof Error ? e.message : String(e);
23
+ throw new CliError(`Cannot resolve file "${filePath}": ${msg}`, {
24
+ suggestion: "Check that the file path is correct and you have read permission.",
25
+ });
26
+ }
27
+ try {
28
+ realRoot = realpathSync(root);
29
+ }
30
+ catch (e) {
31
+ const msg = e instanceof Error ? e.message : String(e);
32
+ throw new CliError(`Upload root "${root}" does not exist: ${msg}`);
33
+ }
34
+ if (realFile !== realRoot && !realFile.startsWith(realRoot + pathSep)) {
35
+ throw new ValidationError(`File "${filePath}" resolves to "${realFile}" which is outside the allowed upload root "${realRoot}".`, "Move the file under the allowed root, set LINEAR_UPLOAD_ROOT, or pass --allow-root <path>.");
36
+ }
37
+ return realFile;
38
+ }
39
+ export function setupAttachmentsCommand(program) {
40
+ const attachments = program.command("attachments").description("Manage issue attachments");
41
+ attachments
42
+ .command("get <id>")
43
+ .description("Get attachment details by ID")
44
+ .action(handleAsyncCommand(async (id) => {
45
+ const client = await getClient();
46
+ const attachment = await client.attachment(id);
47
+ outputSuccess({
48
+ id: attachment.id,
49
+ title: attachment.title ?? null,
50
+ subtitle: attachment.subtitle ?? null,
51
+ url: attachment.url,
52
+ metadata: attachment.metadata ?? null,
53
+ createdAt: attachment.createdAt,
54
+ });
55
+ }));
56
+ attachments
57
+ .command("list <issueId>")
58
+ .description("List attachments on an issue")
59
+ .action(handleAsyncCommand(async (issueId) => {
60
+ const client = await getClient();
61
+ const issue = await findIssueByIdentifier(client, issueId);
62
+ const result = await issue.attachments();
63
+ const items = result.nodes.map((a) => ({
64
+ id: a.id,
65
+ title: a.title ?? null,
66
+ subtitle: a.subtitle ?? null,
67
+ url: a.url,
68
+ createdAt: a.createdAt,
69
+ }));
70
+ outputSuccess({ attachments: items, count: items.length });
71
+ }));
72
+ attachments
73
+ .command("create <issueId>")
74
+ .description("Create an attachment on an issue")
75
+ .requiredOption("--url <url>", "Attachment URL")
76
+ .requiredOption("--title <title>", "Attachment title")
77
+ .option("--subtitle <subtitle>", "Attachment subtitle")
78
+ .option("--icon <icon>", "Icon URL or emoji")
79
+ .action(handleAsyncCommand(async (issueId, opts) => {
80
+ const client = await getClient();
81
+ const issue = await findIssueByIdentifier(client, issueId);
82
+ const input = { issueId: issue.id, url: opts.url, title: opts.title };
83
+ if (opts.subtitle)
84
+ input.subtitle = opts.subtitle;
85
+ if (opts.icon)
86
+ input.iconUrl = opts.icon;
87
+ const payload = await client.createAttachment(input);
88
+ if (!payload.success)
89
+ throw new CliError("Failed to create attachment");
90
+ const attachment = await payload.attachment;
91
+ outputSuccess({
92
+ created: true,
93
+ attachment: attachment ? { id: attachment.id, title: attachment.title, url: attachment.url } : null,
94
+ });
95
+ }));
96
+ attachments
97
+ .command("upload <issueId>")
98
+ .description("Upload a file as attachment. File must live under the allowed root (default: cwd).")
99
+ .requiredOption("--file <path>", "Local file path (must be under allowed root)")
100
+ .option("--filename <name>", "Filename (e.g., screenshot.png) — required")
101
+ .option("--content-type <mime>", "MIME type (e.g., image/png) — required")
102
+ .option("--title <title>", "Attachment title")
103
+ .option("--allow-root <path>", "Override allowed upload root (default: $LINEAR_UPLOAD_ROOT or cwd)")
104
+ .action(handleAsyncCommand(async (issueId, opts) => {
105
+ const filePath = resolveUploadPath(opts.file, opts.allowRoot);
106
+ if (!opts.filename)
107
+ throw new ValidationError("--filename is required.");
108
+ if (!opts.contentType)
109
+ throw new ValidationError("--content-type is required.");
110
+ const client = await getClient();
111
+ const issue = await findIssueByIdentifier(client, issueId);
112
+ let fileContent;
113
+ try {
114
+ fileContent = readFileSync(filePath);
115
+ }
116
+ catch (e) {
117
+ const msg = e instanceof Error ? e.message : String(e);
118
+ throw new CliError(`Cannot read file "${opts.file}": ${msg}`, {
119
+ suggestion: "Check that the file path is correct and you have read permission.",
120
+ });
121
+ }
122
+ const MAX_UPLOAD_SIZE = 100 * 1024 * 1024;
123
+ if (fileContent.length > MAX_UPLOAD_SIZE) {
124
+ throw new CliError(`File too large (${fileContent.length} bytes, max ${MAX_UPLOAD_SIZE})`);
125
+ }
126
+ const uploadPayload = await client.fileUpload(opts.contentType, opts.filename, fileContent.length);
127
+ if (!uploadPayload.success)
128
+ throw new CliError("Failed to get upload URL");
129
+ const uploadFile = uploadPayload.uploadFile;
130
+ if (!uploadFile)
131
+ throw new CliError("No upload URL returned");
132
+ if (!uploadFile.uploadUrl.startsWith("https://")) {
133
+ throw new CliError("Upload URL must use HTTPS");
134
+ }
135
+ const allowedHeaderKeys = new Set([
136
+ "content-type",
137
+ "content-disposition",
138
+ "cache-control",
139
+ "x-amz-acl",
140
+ "x-goog-content-length-range",
141
+ "x-goog-acl",
142
+ ]);
143
+ const headers = { "Content-Type": opts.contentType };
144
+ for (const header of uploadFile.headers) {
145
+ if (allowedHeaderKeys.has(header.key.toLowerCase())) {
146
+ headers[header.key] = header.value;
147
+ }
148
+ }
149
+ const uploadBody = Uint8Array.from(fileContent);
150
+ // Bound the upload to UPLOAD_TIMEOUT_MS so a hung S3/GCS PUT can't
151
+ // stall the CLI indefinitely. 100MB max body / 5 min ≈ 280 KB/s,
152
+ // safely below the slowest plausible connection we'd want to wait on.
153
+ const UPLOAD_TIMEOUT_MS = 5 * 60 * 1000;
154
+ let response;
155
+ try {
156
+ response = await fetch(uploadFile.uploadUrl, {
157
+ method: "PUT",
158
+ headers,
159
+ body: uploadBody,
160
+ signal: AbortSignal.timeout(UPLOAD_TIMEOUT_MS),
161
+ });
162
+ }
163
+ catch (err) {
164
+ const msg = err instanceof Error ? err.message : String(err);
165
+ throw new CliError(`Upload failed: ${msg}`);
166
+ }
167
+ if (!response.ok)
168
+ throw new CliError(`Upload failed: ${response.statusText}`);
169
+ const attachInput = {
170
+ issueId: issue.id,
171
+ url: uploadFile.assetUrl,
172
+ title: opts.title ?? opts.filename,
173
+ };
174
+ const attachPayload = await client.createAttachment(attachInput);
175
+ if (!attachPayload.success) {
176
+ throw new CliError("File was uploaded but could not be attached to the issue.", {
177
+ suggestion: `The file is available at ${uploadFile.assetUrl}. Try creating the attachment manually.`,
178
+ });
179
+ }
180
+ outputSuccess({ uploaded: true, url: uploadFile.assetUrl });
181
+ }));
182
+ attachments
183
+ .command("delete <id>")
184
+ .description("Permanently delete an attachment (irreversible — requires --yes)")
185
+ .option("--yes", "Confirm permanent deletion")
186
+ .action(handleAsyncCommand(async (id, opts) => {
187
+ requireYes(opts, `permanently delete attachment ${id}`);
188
+ const client = await getClient();
189
+ const payload = await client.deleteAttachment(id);
190
+ outputSuccess({ deleted: payload.success });
191
+ }));
192
+ async function outputLinkResult(payload, integration) {
193
+ if (!payload.success)
194
+ throw new CliError(`Failed to link ${integration} attachment`);
195
+ const attachment = await payload.attachment;
196
+ outputSuccess({
197
+ linked: true,
198
+ integration,
199
+ attachment: attachment ? { id: attachment.id, title: attachment.title, url: attachment.url } : null,
200
+ });
201
+ }
202
+ attachments
203
+ .command("link-github-pr <issueId>")
204
+ .description("Link a GitHub pull request as an integration-aware attachment")
205
+ .requiredOption("--url <url>", "GitHub pull request URL")
206
+ .action(handleAsyncCommand(async (issueId, opts) => {
207
+ const client = await getClient();
208
+ const issue = await findIssueByIdentifier(client, issueId);
209
+ const payload = await client.attachmentLinkGitHubPR(issue.id, opts.url);
210
+ await outputLinkResult(payload, "github-pr");
211
+ }));
212
+ attachments
213
+ .command("link-slack <issueId>")
214
+ .description("Link a Slack message as an integration-aware attachment")
215
+ .requiredOption("--url <url>", "Slack permalink (https://*.slack.com/archives/...)")
216
+ .action(handleAsyncCommand(async (issueId, opts) => {
217
+ const client = await getClient();
218
+ const issue = await findIssueByIdentifier(client, issueId);
219
+ const payload = await client.attachmentLinkSlack(issue.id, opts.url);
220
+ await outputLinkResult(payload, "slack");
221
+ }));
222
+ attachments
223
+ .command("link-jira <issueId>")
224
+ .description("Link a Jira issue as an integration-aware attachment")
225
+ .requiredOption("--jira-issue-id <id>", "Jira issue ID")
226
+ .action(handleAsyncCommand(async (issueId, opts) => {
227
+ const client = await getClient();
228
+ const issue = await findIssueByIdentifier(client, issueId);
229
+ const payload = await client.attachmentLinkJiraIssue(issue.id, opts.jiraIssueId);
230
+ await outputLinkResult(payload, "jira");
231
+ }));
232
+ attachments
233
+ .command("link-url <issueId>")
234
+ .description("Link an arbitrary URL as an integration-aware attachment (dedup on issueId+url)")
235
+ .requiredOption("--url <url>", "URL to link")
236
+ .action(handleAsyncCommand(async (issueId, opts) => {
237
+ const client = await getClient();
238
+ const issue = await findIssueByIdentifier(client, issueId);
239
+ const payload = await client.attachmentLinkURL(issue.id, opts.url);
240
+ await outputLinkResult(payload, "url");
241
+ }));
242
+ attachments
243
+ .command("link-discord <issueId>")
244
+ .description("Link a Discord message as an integration-aware attachment")
245
+ .requiredOption("--channel-id <id>", "Discord channel ID")
246
+ .requiredOption("--message-id <id>", "Discord message ID")
247
+ .requiredOption("--url <url>", "Discord message URL")
248
+ .action(handleAsyncCommand(async (issueId, opts) => {
249
+ const client = await getClient();
250
+ const issue = await findIssueByIdentifier(client, issueId);
251
+ const payload = await client.attachmentLinkDiscord(opts.channelId, issue.id, opts.messageId, opts.url);
252
+ await outputLinkResult(payload, "discord");
253
+ }));
254
+ attachments
255
+ .command("link-zendesk <issueId>")
256
+ .description("Link a Zendesk ticket as an integration-aware attachment")
257
+ .requiredOption("--ticket-id <id>", "Zendesk ticket ID")
258
+ .action(handleAsyncCommand(async (issueId, opts) => {
259
+ const client = await getClient();
260
+ const issue = await findIssueByIdentifier(client, issueId);
261
+ const payload = await client.attachmentLinkZendesk(issue.id, opts.ticketId);
262
+ await outputLinkResult(payload, "zendesk");
263
+ }));
264
+ }
265
+ //# sourceMappingURL=attachments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachments.js","sourceRoot":"","sources":["../../src/commands/attachments.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from "commander";
2
+ export declare function setupAuditCommand(program: Command): void;
3
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/commands/audit.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQzC,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA8ExD"}
@@ -0,0 +1,73 @@
1
+ // `elnora-linear audit` — read-only access to Linear's audit log.
2
+ //
3
+ // Useful for SOC 2 / compliance evidence pulls. IPs and key IDs/labels redacted
4
+ // by default; opt in via `--unsafe-include-pii` and treat the output as
5
+ // confidential.
6
+ import { getClient } from "../client/index.js";
7
+ import { handleAsyncCommand, outputSuccess, redactAuditEntry } from "../output/index.js";
8
+ import { parseLimit, ValidationError } from "../utils/index.js";
9
+ export function setupAuditCommand(program) {
10
+ const audit = program.command("audit").description("Read Linear audit log entries (compliance / SOC 2 evidence)");
11
+ audit
12
+ .command("entries")
13
+ .description("List audit entries, newest first. IPs and key IDs redacted by default.")
14
+ .option("--limit <n>", "Max results", "50")
15
+ .option("--since <iso>", "Only entries created after this ISO timestamp")
16
+ .option("--type <type>", "Filter by audit entry type (see `audit types`)")
17
+ .option("--unsafe-include-pii", "Return raw IPs, API key IDs/labels, and OAuth client names. Treat output as confidential.")
18
+ .action(handleAsyncCommand(async (opts) => {
19
+ const client = await getClient();
20
+ const filter = {};
21
+ if (opts.since) {
22
+ const d = new Date(String(opts.since));
23
+ if (Number.isNaN(d.getTime())) {
24
+ throw new ValidationError(`Invalid --since: "${opts.since}". Use an ISO timestamp.`);
25
+ }
26
+ filter.createdAt = { gt: d };
27
+ }
28
+ if (opts.type) {
29
+ filter.type = { eq: String(opts.type) };
30
+ }
31
+ const vars = {
32
+ first: parseLimit(typeof opts.limit === "string" ? opts.limit : undefined),
33
+ };
34
+ if (Object.keys(filter).length > 0) {
35
+ vars.filter = filter;
36
+ }
37
+ const conn = await client.auditEntries(vars);
38
+ const entries = await Promise.all(conn.nodes.map(async (e) => {
39
+ const actor = await e.actor;
40
+ return {
41
+ id: e.id,
42
+ type: e.type,
43
+ actor: actor?.name ?? null,
44
+ actorId: e.actorId ?? null,
45
+ countryCode: e.countryCode ?? null,
46
+ ip: e.ip ?? null,
47
+ createdAt: e.createdAt,
48
+ metadata: e.metadata ?? null,
49
+ };
50
+ }));
51
+ if (opts.unsafeIncludePii) {
52
+ process.stderr.write("Warning: --unsafe-include-pii is on; output contains IPs and API key IDs. Treat as confidential.\n");
53
+ outputSuccess({ entries, count: entries.length });
54
+ return;
55
+ }
56
+ const redacted = entries.map((e) => redactAuditEntry(e));
57
+ outputSuccess({ entries: redacted, count: redacted.length });
58
+ }));
59
+ audit
60
+ .command("types")
61
+ .description("List available audit entry types")
62
+ .option("--prefix <str>", "Filter types by case-sensitive prefix")
63
+ .action(handleAsyncCommand(async (opts) => {
64
+ const client = await getClient();
65
+ const types = await client.auditEntryTypes;
66
+ const filtered = opts.prefix ? types.filter((t) => t.type.startsWith(opts.prefix)) : types;
67
+ outputSuccess({
68
+ types: filtered.map((t) => ({ type: t.type, description: t.description })),
69
+ count: filtered.length,
70
+ });
71
+ }));
72
+ }
73
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/commands/audit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from "commander";
2
+ export declare function setupCommentsCommand(program: Command): void;
3
+ //# sourceMappingURL=comments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comments.d.ts","sourceRoot":"","sources":["../../src/commands/comments.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASzC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA0H3D"}
@@ -0,0 +1,107 @@
1
+ // `elnora-linear comments` — manage issue comments.
2
+ //
3
+ // `list` uses raw GraphQL with an embedded user{name} subquery to avoid N+1
4
+ // round-trips when rendering threads.
5
+ import { getClient } from "../client/index.js";
6
+ import { gqlRequest } from "../lib/bulk-graphql.js";
7
+ import { handleAsyncCommand, outputSuccess } from "../output/index.js";
8
+ import { CliError, findIssueByIdentifier, NotFoundError, requireYes } from "../utils/index.js";
9
+ export function setupCommentsCommand(program) {
10
+ const comments = program.command("comments").description("Manage issue comments");
11
+ comments
12
+ .command("list <issueId>")
13
+ .description("List comments on an issue")
14
+ .action(handleAsyncCommand(async (issueId) => {
15
+ const m = issueId.match(/^([A-Z]+)-(\d+)$/);
16
+ const filter = m
17
+ ? { team: { key: { eq: m[1] } }, number: { eq: parseInt(m[2], 10) } }
18
+ : { id: { eq: issueId } };
19
+ const res = await gqlRequest(`query($filter: IssueFilter!) {
20
+ issues(first: 1, filter: $filter) {
21
+ nodes {
22
+ comments(first: 100) {
23
+ nodes { id body createdAt updatedAt user { name } }
24
+ }
25
+ }
26
+ }
27
+ }`, { filter });
28
+ if (res.errors) {
29
+ throw new CliError(`comments list: ${res.errors.map((e) => e.message).join("; ")}`);
30
+ }
31
+ const issue = res.data?.issues.nodes[0];
32
+ if (!issue)
33
+ throw new NotFoundError("Issue", issueId);
34
+ const rows = issue.comments.nodes.map((c) => ({
35
+ id: c.id,
36
+ author: c.user?.name ?? null,
37
+ body: c.body,
38
+ createdAt: c.createdAt,
39
+ updatedAt: c.updatedAt,
40
+ }));
41
+ outputSuccess({ comments: rows, count: rows.length });
42
+ }));
43
+ comments
44
+ .command("create <issueId>")
45
+ .description("Add a comment to an issue")
46
+ .requiredOption("--body <text>", "Comment text (markdown)")
47
+ .action(handleAsyncCommand(async (issueId, opts) => {
48
+ const client = await getClient();
49
+ const issue = await findIssueByIdentifier(client, issueId);
50
+ const input = { issueId: issue.id, body: opts.body };
51
+ const payload = await client.createComment(input);
52
+ if (!payload.success)
53
+ throw new CliError("Failed to create comment");
54
+ const comment = await payload.comment;
55
+ outputSuccess({
56
+ created: true,
57
+ comment: comment ? { id: comment.id, body: comment.body } : null,
58
+ });
59
+ }));
60
+ comments
61
+ .command("update <commentId>")
62
+ .description("Update an existing comment")
63
+ .requiredOption("--body <text>", "New comment text (markdown)")
64
+ .action(handleAsyncCommand(async (commentId, opts) => {
65
+ const client = await getClient();
66
+ const update = { body: opts.body };
67
+ const payload = await client.updateComment(commentId, update);
68
+ if (!payload.success)
69
+ throw new CliError("Failed to update comment");
70
+ const comment = await payload.comment;
71
+ outputSuccess({
72
+ updated: true,
73
+ comment: comment ? { id: comment.id, body: comment.body } : null,
74
+ });
75
+ }));
76
+ comments
77
+ .command("delete <commentId>")
78
+ .description("Permanently delete a comment (irreversible — requires --yes)")
79
+ .option("--yes", "Confirm permanent deletion")
80
+ .action(handleAsyncCommand(async (commentId, opts) => {
81
+ requireYes(opts, `permanently delete comment ${commentId}`);
82
+ const client = await getClient();
83
+ const payload = await client.deleteComment(commentId);
84
+ outputSuccess({ deleted: payload.success });
85
+ }));
86
+ comments
87
+ .command("resolve <commentId>")
88
+ .description("Mark a comment thread as resolved")
89
+ .action(handleAsyncCommand(async (commentId) => {
90
+ const client = await getClient();
91
+ const payload = await client.commentResolve(commentId);
92
+ if (!payload.success)
93
+ throw new CliError("Failed to resolve comment");
94
+ outputSuccess({ resolved: true, id: commentId });
95
+ }));
96
+ comments
97
+ .command("unresolve <commentId>")
98
+ .description("Reopen a resolved comment thread")
99
+ .action(handleAsyncCommand(async (commentId) => {
100
+ const client = await getClient();
101
+ const payload = await client.commentUnresolve(commentId);
102
+ if (!payload.success)
103
+ throw new CliError("Failed to unresolve comment");
104
+ outputSuccess({ unresolved: true, id: commentId });
105
+ }));
106
+ }
107
+ //# sourceMappingURL=comments.js.map