@elnora-ai/linear 1.0.1 → 2.0.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 (302) hide show
  1. package/.claude-plugin/marketplace.json +7 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +25 -1
  4. package/README.md +275 -25
  5. package/agents/linear-issue-creator.md +135 -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 +190 -26
  10. package/commands/linear-cleanup.md +64 -29
  11. package/dist/cli.js +69 -1
  12. package/dist/cli.js.map +1 -1
  13. package/dist/client/auth.d.ts +10 -0
  14. package/dist/client/auth.d.ts.map +1 -1
  15. package/dist/client/auth.js +50 -3
  16. package/dist/client/auth.js.map +1 -1
  17. package/dist/client/linear-client.d.ts +7 -0
  18. package/dist/client/linear-client.d.ts.map +1 -1
  19. package/dist/client/linear-client.js +13 -1
  20. package/dist/client/linear-client.js.map +1 -1
  21. package/dist/commands/agent-activities.d.ts +3 -0
  22. package/dist/commands/agent-activities.d.ts.map +1 -0
  23. package/dist/commands/agent-activities.js +144 -0
  24. package/dist/commands/agent-activities.js.map +1 -0
  25. package/dist/commands/agent-sessions.d.ts +3 -0
  26. package/dist/commands/agent-sessions.d.ts.map +1 -0
  27. package/dist/commands/agent-sessions.js +132 -0
  28. package/dist/commands/agent-sessions.js.map +1 -0
  29. package/dist/commands/attachments.d.ts +3 -0
  30. package/dist/commands/attachments.d.ts.map +1 -0
  31. package/dist/commands/attachments.js +265 -0
  32. package/dist/commands/attachments.js.map +1 -0
  33. package/dist/commands/audit.d.ts +3 -0
  34. package/dist/commands/audit.d.ts.map +1 -0
  35. package/dist/commands/audit.js +73 -0
  36. package/dist/commands/audit.js.map +1 -0
  37. package/dist/commands/comments.d.ts +3 -0
  38. package/dist/commands/comments.d.ts.map +1 -0
  39. package/dist/commands/comments.js +107 -0
  40. package/dist/commands/comments.js.map +1 -0
  41. package/dist/commands/completion.d.ts +3 -0
  42. package/dist/commands/completion.d.ts.map +1 -0
  43. package/dist/commands/completion.js +62 -0
  44. package/dist/commands/completion.js.map +1 -0
  45. package/dist/commands/context.d.ts +3 -0
  46. package/dist/commands/context.d.ts.map +1 -0
  47. package/dist/commands/context.js +94 -0
  48. package/dist/commands/context.js.map +1 -0
  49. package/dist/commands/curator.d.ts +14 -0
  50. package/dist/commands/curator.d.ts.map +1 -1
  51. package/dist/commands/curator.js +97 -19
  52. package/dist/commands/curator.js.map +1 -1
  53. package/dist/commands/customer-needs.d.ts +3 -0
  54. package/dist/commands/customer-needs.d.ts.map +1 -0
  55. package/dist/commands/customer-needs.js +198 -0
  56. package/dist/commands/customer-needs.js.map +1 -0
  57. package/dist/commands/customers.d.ts +5 -0
  58. package/dist/commands/customers.d.ts.map +1 -0
  59. package/dist/commands/customers.js +201 -0
  60. package/dist/commands/customers.js.map +1 -0
  61. package/dist/commands/cycles.d.ts +3 -0
  62. package/dist/commands/cycles.d.ts.map +1 -0
  63. package/dist/commands/cycles.js +67 -0
  64. package/dist/commands/cycles.js.map +1 -0
  65. package/dist/commands/documents.d.ts +3 -0
  66. package/dist/commands/documents.d.ts.map +1 -0
  67. package/dist/commands/documents.js +105 -0
  68. package/dist/commands/documents.js.map +1 -0
  69. package/dist/commands/favorites.d.ts +3 -0
  70. package/dist/commands/favorites.d.ts.map +1 -0
  71. package/dist/commands/favorites.js +101 -0
  72. package/dist/commands/favorites.js.map +1 -0
  73. package/dist/commands/index.d.ts +30 -0
  74. package/dist/commands/index.d.ts.map +1 -1
  75. package/dist/commands/index.js +30 -0
  76. package/dist/commands/index.js.map +1 -1
  77. package/dist/commands/initiatives.d.ts +3 -0
  78. package/dist/commands/initiatives.d.ts.map +1 -0
  79. package/dist/commands/initiatives.js +106 -0
  80. package/dist/commands/initiatives.js.map +1 -0
  81. package/dist/commands/issues.d.ts +21 -0
  82. package/dist/commands/issues.d.ts.map +1 -0
  83. package/dist/commands/issues.js +1083 -0
  84. package/dist/commands/issues.js.map +1 -0
  85. package/dist/commands/labels.d.ts +3 -0
  86. package/dist/commands/labels.d.ts.map +1 -0
  87. package/dist/commands/labels.js +111 -0
  88. package/dist/commands/labels.js.map +1 -0
  89. package/dist/commands/milestones.d.ts +3 -0
  90. package/dist/commands/milestones.d.ts.map +1 -0
  91. package/dist/commands/milestones.js +94 -0
  92. package/dist/commands/milestones.js.map +1 -0
  93. package/dist/commands/notifications.d.ts +3 -0
  94. package/dist/commands/notifications.d.ts.map +1 -0
  95. package/dist/commands/notifications.js +130 -0
  96. package/dist/commands/notifications.js.map +1 -0
  97. package/dist/commands/project-labels.d.ts +3 -0
  98. package/dist/commands/project-labels.d.ts.map +1 -0
  99. package/dist/commands/project-labels.js +80 -0
  100. package/dist/commands/project-labels.js.map +1 -0
  101. package/dist/commands/project-relations.d.ts +3 -0
  102. package/dist/commands/project-relations.d.ts.map +1 -0
  103. package/dist/commands/project-relations.js +96 -0
  104. package/dist/commands/project-relations.js.map +1 -0
  105. package/dist/commands/projects.d.ts +3 -0
  106. package/dist/commands/projects.d.ts.map +1 -0
  107. package/dist/commands/projects.js +263 -0
  108. package/dist/commands/projects.js.map +1 -0
  109. package/dist/commands/quota.d.ts +3 -0
  110. package/dist/commands/quota.d.ts.map +1 -0
  111. package/dist/commands/quota.js +28 -0
  112. package/dist/commands/quota.js.map +1 -0
  113. package/dist/commands/reactions.d.ts +7 -0
  114. package/dist/commands/reactions.d.ts.map +1 -0
  115. package/dist/commands/reactions.js +53 -0
  116. package/dist/commands/reactions.js.map +1 -0
  117. package/dist/commands/relations.d.ts +3 -0
  118. package/dist/commands/relations.d.ts.map +1 -0
  119. package/dist/commands/relations.js +73 -0
  120. package/dist/commands/relations.js.map +1 -0
  121. package/dist/commands/states.d.ts +3 -0
  122. package/dist/commands/states.d.ts.map +1 -0
  123. package/dist/commands/states.js +52 -0
  124. package/dist/commands/states.js.map +1 -0
  125. package/dist/commands/status-updates.d.ts +3 -0
  126. package/dist/commands/status-updates.d.ts.map +1 -0
  127. package/dist/commands/status-updates.js +117 -0
  128. package/dist/commands/status-updates.js.map +1 -0
  129. package/dist/commands/sync.d.ts.map +1 -1
  130. package/dist/commands/sync.js +58 -18
  131. package/dist/commands/sync.js.map +1 -1
  132. package/dist/commands/teams.d.ts +3 -0
  133. package/dist/commands/teams.d.ts.map +1 -0
  134. package/dist/commands/teams.js +135 -0
  135. package/dist/commands/teams.js.map +1 -0
  136. package/dist/commands/templates.d.ts +3 -0
  137. package/dist/commands/templates.d.ts.map +1 -0
  138. package/dist/commands/templates.js +76 -0
  139. package/dist/commands/templates.js.map +1 -0
  140. package/dist/commands/users.d.ts +3 -0
  141. package/dist/commands/users.d.ts.map +1 -0
  142. package/dist/commands/users.js +40 -0
  143. package/dist/commands/users.js.map +1 -0
  144. package/dist/commands/views.d.ts +3 -0
  145. package/dist/commands/views.d.ts.map +1 -0
  146. package/dist/commands/views.js +177 -0
  147. package/dist/commands/views.js.map +1 -0
  148. package/dist/commands/webhooks.d.ts +3 -0
  149. package/dist/commands/webhooks.d.ts.map +1 -0
  150. package/dist/commands/webhooks.js +234 -0
  151. package/dist/commands/webhooks.js.map +1 -0
  152. package/dist/config/loader.d.ts.map +1 -1
  153. package/dist/config/loader.js +3 -0
  154. package/dist/config/loader.js.map +1 -1
  155. package/dist/config/types.d.ts +15 -1
  156. package/dist/config/types.d.ts.map +1 -1
  157. package/dist/config/types.js +1 -0
  158. package/dist/config/types.js.map +1 -1
  159. package/dist/curator/dispatch.d.ts +52 -0
  160. package/dist/curator/dispatch.d.ts.map +1 -0
  161. package/dist/curator/dispatch.js +144 -0
  162. package/dist/curator/dispatch.js.map +1 -0
  163. package/dist/curator/index.d.ts +5 -0
  164. package/dist/curator/index.d.ts.map +1 -0
  165. package/dist/curator/index.js +5 -0
  166. package/dist/curator/index.js.map +1 -0
  167. package/dist/curator/llm.d.ts +70 -0
  168. package/dist/curator/llm.d.ts.map +1 -0
  169. package/dist/curator/llm.js +107 -0
  170. package/dist/curator/llm.js.map +1 -0
  171. package/dist/curator/snapshot.d.ts +34 -0
  172. package/dist/curator/snapshot.d.ts.map +1 -0
  173. package/dist/curator/snapshot.js +127 -0
  174. package/dist/curator/snapshot.js.map +1 -0
  175. package/dist/curator/state.d.ts +50 -0
  176. package/dist/curator/state.d.ts.map +1 -0
  177. package/dist/curator/state.js +125 -0
  178. package/dist/curator/state.js.map +1 -0
  179. package/dist/lib/bulk-graphql.d.ts +144 -0
  180. package/dist/lib/bulk-graphql.d.ts.map +1 -0
  181. package/dist/lib/bulk-graphql.js +380 -0
  182. package/dist/lib/bulk-graphql.js.map +1 -0
  183. package/dist/lib/index.d.ts +2 -0
  184. package/dist/lib/index.d.ts.map +1 -0
  185. package/dist/lib/index.js +2 -0
  186. package/dist/lib/index.js.map +1 -0
  187. package/dist/output/cli.d.ts +17 -0
  188. package/dist/output/cli.d.ts.map +1 -0
  189. package/dist/output/cli.js +252 -0
  190. package/dist/output/cli.js.map +1 -0
  191. package/dist/output/formatter.d.ts +6 -0
  192. package/dist/output/formatter.d.ts.map +1 -1
  193. package/dist/output/formatter.js +10 -0
  194. package/dist/output/formatter.js.map +1 -1
  195. package/dist/output/index.d.ts +1 -0
  196. package/dist/output/index.d.ts.map +1 -1
  197. package/dist/output/index.js +1 -0
  198. package/dist/output/index.js.map +1 -1
  199. package/dist/scripts/sync-linear-templates.d.ts +26 -0
  200. package/dist/scripts/sync-linear-templates.d.ts.map +1 -0
  201. package/dist/scripts/sync-linear-templates.js +115 -0
  202. package/dist/scripts/sync-linear-templates.js.map +1 -0
  203. package/dist/signals/github-commits.d.ts +31 -0
  204. package/dist/signals/github-commits.d.ts.map +1 -0
  205. package/dist/signals/github-commits.js +127 -0
  206. package/dist/signals/github-commits.js.map +1 -0
  207. package/dist/signals/github-pr.d.ts +16 -0
  208. package/dist/signals/github-pr.d.ts.map +1 -0
  209. package/dist/signals/github-pr.js +98 -0
  210. package/dist/signals/github-pr.js.map +1 -0
  211. package/dist/signals/index.d.ts +4 -0
  212. package/dist/signals/index.d.ts.map +1 -1
  213. package/dist/signals/index.js +4 -0
  214. package/dist/signals/index.js.map +1 -1
  215. package/dist/signals/linear-issues.d.ts +20 -0
  216. package/dist/signals/linear-issues.d.ts.map +1 -0
  217. package/dist/signals/linear-issues.js +115 -0
  218. package/dist/signals/linear-issues.js.map +1 -0
  219. package/dist/signals/registry.d.ts +4 -3
  220. package/dist/signals/registry.d.ts.map +1 -1
  221. package/dist/signals/registry.js +33 -11
  222. package/dist/signals/registry.js.map +1 -1
  223. package/dist/signals/slack-messages.d.ts +20 -0
  224. package/dist/signals/slack-messages.d.ts.map +1 -0
  225. package/dist/signals/slack-messages.js +129 -0
  226. package/dist/signals/slack-messages.js.map +1 -0
  227. package/dist/utils/errors.d.ts +81 -0
  228. package/dist/utils/errors.d.ts.map +1 -0
  229. package/dist/utils/errors.js +110 -0
  230. package/dist/utils/errors.js.map +1 -0
  231. package/dist/utils/index.d.ts +9 -0
  232. package/dist/utils/index.d.ts.map +1 -0
  233. package/dist/utils/index.js +9 -0
  234. package/dist/utils/index.js.map +1 -0
  235. package/dist/utils/label-policy.d.ts +60 -0
  236. package/dist/utils/label-policy.d.ts.map +1 -0
  237. package/dist/utils/label-policy.js +103 -0
  238. package/dist/utils/label-policy.js.map +1 -0
  239. package/dist/utils/parse.d.ts +48 -0
  240. package/dist/utils/parse.d.ts.map +1 -0
  241. package/dist/utils/parse.js +133 -0
  242. package/dist/utils/parse.js.map +1 -0
  243. package/dist/utils/project-status.d.ts +6 -0
  244. package/dist/utils/project-status.d.ts.map +1 -0
  245. package/dist/utils/project-status.js +33 -0
  246. package/dist/utils/project-status.js.map +1 -0
  247. package/dist/utils/rate-limit.d.ts +24 -0
  248. package/dist/utils/rate-limit.d.ts.map +1 -0
  249. package/dist/utils/rate-limit.js +89 -0
  250. package/dist/utils/rate-limit.js.map +1 -0
  251. package/dist/utils/resolve.d.ts +84 -0
  252. package/dist/utils/resolve.d.ts.map +1 -0
  253. package/dist/utils/resolve.js +172 -0
  254. package/dist/utils/resolve.js.map +1 -0
  255. package/dist/utils/sleep.d.ts +2 -0
  256. package/dist/utils/sleep.d.ts.map +1 -0
  257. package/dist/utils/sleep.js +4 -0
  258. package/dist/utils/sleep.js.map +1 -0
  259. package/dist/utils/webhook-verify.d.ts +42 -0
  260. package/dist/utils/webhook-verify.d.ts.map +1 -0
  261. package/dist/utils/webhook-verify.js +65 -0
  262. package/dist/utils/webhook-verify.js.map +1 -0
  263. package/package.json +7 -2
  264. package/references/agent-description-template.md +31 -0
  265. package/references/cli-reference.md +227 -0
  266. package/references/curator-tiering-rules.md +78 -0
  267. package/references/label-policy.example.json +37 -0
  268. package/references/label-policy.placeholder.json +6 -0
  269. package/references/settings-template.md +30 -0
  270. package/references/signal-sources.example.json +0 -8
  271. package/references/sla-reference.md +70 -0
  272. package/references/template-index.md +34 -0
  273. package/references/workspace-labels.md +124 -0
  274. package/references/workspace-projects.md +56 -0
  275. package/references/workspace-routing.md +58 -0
  276. package/schemas/label-policy.json +72 -0
  277. package/scripts/postinstall.mjs +195 -0
  278. package/skills/linear-workspace/SKILL.md +65 -4
  279. package/templates/ACC-PRO-provision.md +74 -0
  280. package/templates/ACC-PRV-privileged.md +66 -0
  281. package/templates/ACC-QTR-review.md +77 -0
  282. package/templates/ACC-REV-revoke.md +67 -0
  283. package/templates/AI-USE-capability.md +111 -0
  284. package/templates/AUD-CAP-corrective.md +89 -0
  285. package/templates/AUD-INT-internal.md +92 -0
  286. package/templates/AUD-MGT-management.md +110 -0
  287. package/templates/CHG-MAJ-major.md +110 -0
  288. package/templates/CHG-SIG-significant.md +83 -0
  289. package/templates/CHG-STD-standard.md +47 -0
  290. package/templates/LRN-DOC-lessons.md +75 -0
  291. package/templates/OPS-BCK-backup.md +99 -0
  292. package/templates/OPS-DAT-data-mod.md +98 -0
  293. package/templates/RCA-DOC-root-cause.md +105 -0
  294. package/templates/RSK-ASS-assessment.md +87 -0
  295. package/templates/RSK-VND-vendor.md +113 -0
  296. package/templates/SEC-INC-incident.md +76 -0
  297. package/templates/SEC-PEN-pentest.md +58 -0
  298. package/templates/SEC-VLN-vulnerability.md +69 -0
  299. package/templates/SLA-AVL-availability.md +86 -0
  300. package/templates/SLA-OPS-operational.md +70 -0
  301. package/templates/agent-server-template/README.md +88 -0
  302. package/templates/agent-server-template/server.example.ts +185 -0
@@ -0,0 +1,252 @@
1
+ // CLI output helpers for elnora-linear commands.
2
+ //
3
+ // Provides a single contract every command-handler uses:
4
+ // - outputSuccess(data) → stdout, exit 0
5
+ // - outputError(err) inside handleAsyncCommand → stderr, exit 1+
6
+ //
7
+ // Supports JSON (default), table (`--output table`), and CSV (`--output csv`)
8
+ // formats, optional pretty-printing (`--pretty`), and field filtering
9
+ // (`--fields name,email`). JSON is compact by default — agents are the primary
10
+ // consumer and pretty-printing burns ~30% extra tokens for nothing.
11
+ import { CliError, EXIT_CODES, ValidationError } from "../utils/errors.js";
12
+ import { withRateLimit } from "../utils/rate-limit.js";
13
+ import { redactSecrets } from "./formatter.js";
14
+ let prettyMode = false;
15
+ export function setPrettyMode(value) {
16
+ prettyMode = value;
17
+ }
18
+ let outputFormat = "json";
19
+ export function setOutputFormat(value) {
20
+ const valid = ["json", "table", "csv"];
21
+ if (!valid.includes(value)) {
22
+ throw new ValidationError(`Invalid --output value: "${value}". Must be one of: ${valid.join(", ")}.`);
23
+ }
24
+ outputFormat = value;
25
+ }
26
+ let fieldFilter = null;
27
+ export function setFields(value) {
28
+ const fields = value
29
+ .split(",")
30
+ .map((f) => f.trim())
31
+ .filter(Boolean);
32
+ if (fields.length === 0) {
33
+ throw new ValidationError(`Invalid --fields value: "${value}". Provide comma-separated field names.`);
34
+ }
35
+ fieldFilter = fields;
36
+ }
37
+ export function getFields() {
38
+ return fieldFilter;
39
+ }
40
+ /** Test-only: reset module-level state between tests. */
41
+ export function _resetOutputState() {
42
+ prettyMode = false;
43
+ outputFormat = "json";
44
+ fieldFilter = null;
45
+ }
46
+ function findDataArray(data) {
47
+ if (typeof data !== "object" || data === null || Array.isArray(data))
48
+ return null;
49
+ const obj = data;
50
+ for (const key of Object.keys(obj)) {
51
+ if (Array.isArray(obj[key]) && obj[key].length > 0) {
52
+ const first = obj[key][0];
53
+ if (typeof first === "object" && first !== null) {
54
+ return { key, rows: obj[key] };
55
+ }
56
+ }
57
+ }
58
+ return null;
59
+ }
60
+ function formatCell(value) {
61
+ if (value === null || value === undefined)
62
+ return "";
63
+ if (typeof value === "object")
64
+ return JSON.stringify(value);
65
+ return String(value);
66
+ }
67
+ function unionKeys(rows) {
68
+ const seen = new Set();
69
+ const ordered = [];
70
+ for (const row of rows) {
71
+ for (const k of Object.keys(row)) {
72
+ if (!seen.has(k)) {
73
+ seen.add(k);
74
+ ordered.push(k);
75
+ }
76
+ }
77
+ }
78
+ return ordered;
79
+ }
80
+ function outputTable(data) {
81
+ const found = findDataArray(data);
82
+ if (!found) {
83
+ process.stderr.write("Warning: --output table requested but response is not a list. Falling back to JSON.\n");
84
+ process.stdout.write(`${JSON.stringify(data, null, 2)}\n`);
85
+ return;
86
+ }
87
+ const { rows } = found;
88
+ const keys = unionKeys(rows);
89
+ const cells = rows.map((row) => keys.map((k) => formatCell(row[k])));
90
+ const MAX_COL_WIDTH = 60;
91
+ const widths = keys.map((k, i) => Math.min(MAX_COL_WIDTH, Math.max(k.length, ...cells.map((row) => row[i].length))));
92
+ function truncateCell(value, maxWidth) {
93
+ return value.length > maxWidth ? `${value.slice(0, maxWidth - 3)}...` : value;
94
+ }
95
+ const header = keys.map((k, i) => k.toUpperCase().padEnd(widths[i])).join(" ");
96
+ const separator = widths.map((w) => "-".repeat(w)).join(" ");
97
+ process.stdout.write(`${header}\n`);
98
+ process.stdout.write(`${separator}\n`);
99
+ for (const row of cells) {
100
+ process.stdout.write(`${row.map((c, i) => truncateCell(c, widths[i]).padEnd(widths[i])).join(" ")}\n`);
101
+ }
102
+ const obj = data;
103
+ const meta = [];
104
+ for (const [k, v] of Object.entries(obj)) {
105
+ if (k !== found.key && typeof v !== "object") {
106
+ meta.push(`${k}: ${v}`);
107
+ }
108
+ }
109
+ if (meta.length > 0) {
110
+ process.stdout.write(`\n${meta.join(" | ")}\n`);
111
+ }
112
+ }
113
+ function csvEscape(value) {
114
+ if (value.includes(",") || value.includes('"') || value.includes("\n")) {
115
+ return `"${value.replace(/"/g, '""')}"`;
116
+ }
117
+ return value;
118
+ }
119
+ function outputCsv(data) {
120
+ const found = findDataArray(data);
121
+ if (!found) {
122
+ process.stderr.write("Warning: --output csv requested but response is not a list. Falling back to JSON.\n");
123
+ process.stdout.write(`${JSON.stringify(data, null, 2)}\n`);
124
+ return;
125
+ }
126
+ const { rows } = found;
127
+ const keys = unionKeys(rows);
128
+ process.stdout.write(`${keys.map((k) => csvEscape(k)).join(",")}\n`);
129
+ for (const row of rows) {
130
+ process.stdout.write(`${keys.map((k) => csvEscape(formatCell(row[k]))).join(",")}\n`);
131
+ }
132
+ }
133
+ function pickFields(row, fields) {
134
+ const filtered = {};
135
+ for (const field of fields) {
136
+ if (field in row)
137
+ filtered[field] = row[field];
138
+ }
139
+ return filtered;
140
+ }
141
+ function applyFieldFilter(data) {
142
+ if (!fieldFilter)
143
+ return data;
144
+ const found = findDataArray(data);
145
+ if (found) {
146
+ if (found.rows.length > 0) {
147
+ const availableFields = Object.keys(found.rows[0]);
148
+ const missingFields = fieldFilter.filter((f) => !(f in found.rows[0]));
149
+ if (missingFields.length === fieldFilter.length) {
150
+ throw new ValidationError(`--fields requested only non-existent field(s): ${missingFields.join(", ")}. Available: ${availableFields.join(", ")}`);
151
+ }
152
+ if (missingFields.length > 0) {
153
+ process.stderr.write(`Warning: --fields requested non-existent field(s): ${missingFields.join(", ")}. Available: ${availableFields.join(", ")}\n`);
154
+ }
155
+ }
156
+ const filteredRows = found.rows.map((row) => pickFields(row, fieldFilter));
157
+ const obj = { ...data };
158
+ obj[found.key] = filteredRows;
159
+ return obj;
160
+ }
161
+ if (typeof data === "object" && data !== null && !Array.isArray(data)) {
162
+ const obj = data;
163
+ const availableFields = Object.keys(obj);
164
+ const missingFields = fieldFilter.filter((f) => !(f in obj));
165
+ if (missingFields.length === fieldFilter.length) {
166
+ throw new ValidationError(`--fields requested only non-existent field(s): ${missingFields.join(", ")}. Available: ${availableFields.join(", ")}`);
167
+ }
168
+ if (missingFields.length > 0) {
169
+ process.stderr.write(`Warning: --fields requested non-existent field(s): ${missingFields.join(", ")}. Available: ${availableFields.join(", ")}\n`);
170
+ }
171
+ return pickFields(obj, fieldFilter);
172
+ }
173
+ process.stderr.write("Warning: --fields requested but response is not a list or object. Field filter ignored.\n");
174
+ return data;
175
+ }
176
+ export function outputSuccess(data) {
177
+ const filtered = applyFieldFilter(data);
178
+ switch (outputFormat) {
179
+ case "table":
180
+ outputTable(filtered);
181
+ break;
182
+ case "csv":
183
+ outputCsv(filtered);
184
+ break;
185
+ default:
186
+ process.stdout.write(`${prettyMode ? JSON.stringify(filtered, null, 2) : JSON.stringify(filtered)}\n`);
187
+ }
188
+ }
189
+ const AUDIT_PII_METADATA_KEYS = new Set(["apiKeyId", "apiKeyLabel", "oauthClientId", "oauthClientName"]);
190
+ /**
191
+ * Mask PII / sensitive identifiers in a single Linear audit log entry.
192
+ * Returns a NEW object — never mutates the input.
193
+ */
194
+ export function redactAuditEntry(entry) {
195
+ const out = { ...entry };
196
+ if ("ip" in out && out.ip != null && out.ip !== "") {
197
+ out.ip = "[REDACTED]";
198
+ }
199
+ if (out.metadata && typeof out.metadata === "object" && !Array.isArray(out.metadata)) {
200
+ const md = { ...out.metadata };
201
+ for (const key of AUDIT_PII_METADATA_KEYS) {
202
+ if (key in md && md[key] != null && md[key] !== "") {
203
+ md[key] = "[REDACTED]";
204
+ }
205
+ }
206
+ out.metadata = md;
207
+ }
208
+ return out;
209
+ }
210
+ export function outputError(error) {
211
+ if (error instanceof CliError) {
212
+ const payload = {
213
+ error: redactSecrets(error.userMessage),
214
+ };
215
+ if (error.suggestion) {
216
+ payload.suggestion = error.suggestion;
217
+ }
218
+ if (error.data) {
219
+ for (const [k, v] of Object.entries(error.data)) {
220
+ if (!(k in payload))
221
+ payload[k] = v;
222
+ }
223
+ }
224
+ process.stderr.write(`${JSON.stringify(payload, null, 2)}\n`);
225
+ }
226
+ else if (error instanceof Error) {
227
+ const payload = {
228
+ error: redactSecrets(error.message),
229
+ type: error.constructor.name,
230
+ };
231
+ if (process.env.LINEAR_CLI_DEBUG) {
232
+ payload.stack = redactSecrets(error.stack ?? "");
233
+ }
234
+ process.stderr.write(`${JSON.stringify(payload, null, 2)}\n`);
235
+ }
236
+ else {
237
+ process.stderr.write(`${JSON.stringify({ error: redactSecrets(String(error)) }, null, 2)}\n`);
238
+ }
239
+ }
240
+ export function handleAsyncCommand(fn) {
241
+ return (async (...args) => {
242
+ try {
243
+ await withRateLimit(() => fn(...args));
244
+ }
245
+ catch (error) {
246
+ outputError(error);
247
+ const code = error instanceof CliError ? error.exitCode : EXIT_CODES.GENERAL;
248
+ process.exit(code);
249
+ }
250
+ });
251
+ }
252
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/output/cli.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,9 @@
1
+ /**
2
+ * Redact API keys and secrets from a string. Defensive — used in error
3
+ * envelopes that may echo back fragments of request bodies or upstream HTTP
4
+ * responses. Covers Linear (api/oauth/webhook) and Anthropic API keys.
5
+ */
6
+ export declare function redactSecrets(text: string): string;
1
7
  export type OutputMode = "text" | "json";
2
8
  export interface FormattedIssue {
3
9
  identifier: string;
@@ -1 +1 @@
1
- {"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../../src/output/formatter.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;AAEzC,MAAM,WAAW,cAAc;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAeD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,CAejE;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,CAEjE;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,EAAE,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CAE/E"}
1
+ {"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../../src/output/formatter.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIlD;AAED,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;AAEzC,MAAM,WAAW,cAAc;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAeD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,CAejE;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,CAEjE;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,EAAE,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CAE/E"}
@@ -1,5 +1,15 @@
1
1
  // Output formatters for issue lists.
2
2
  // Text mode = aligned columns for humans. JSON mode = pretty-printed JSON for scripts / agents.
3
+ /**
4
+ * Redact API keys and secrets from a string. Defensive — used in error
5
+ * envelopes that may echo back fragments of request bodies or upstream HTTP
6
+ * responses. Covers Linear (api/oauth/webhook) and Anthropic API keys.
7
+ */
8
+ export function redactSecrets(text) {
9
+ return text
10
+ .replace(/lin_(api|oauth|wh)_[a-zA-Z0-9_-]+/g, "lin_$1_[REDACTED]")
11
+ .replace(/\bsk-ant-[a-zA-Z0-9_-]+/g, "sk-ant-[REDACTED]");
12
+ }
3
13
  const COLUMN_WIDTHS = { identifier: 10, state: 14, assignee: 24 };
4
14
  const TITLE_MAX = 70;
5
15
  function truncate(text, max) {
@@ -1 +1 @@
1
- {"version":3,"file":"formatter.js","sourceRoot":"","sources":["../../src/output/formatter.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,gGAAgG;AAgBhG,MAAM,aAAa,GAAG,EAAE,UAAU,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAW,CAAC;AAC3E,MAAM,SAAS,GAAG,EAAE,CAAC;AAErB,SAAS,QAAQ,CAAC,IAAY,EAAE,GAAW;IAC1C,IAAI,IAAI,CAAC,MAAM,IAAI,GAAG;QAAE,OAAO,IAAI,CAAC;IACpC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC;AACrC,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY,EAAE,KAAa;IAC5C,IAAI,IAAI,CAAC,MAAM,IAAI,KAAK;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC;IAChE,OAAO,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAwB;IACxD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,kBAAkB,CAAC;IACnD,MAAM,MAAM,GACX,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,UAAU,CAAC;QACxC,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,KAAK,CAAC;QACtC,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC,QAAQ,CAAC;QAC5C,OAAO,CAAC;IACT,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CACtB,CAAC,KAAK,EAAE,EAAE,CACT,QAAQ,CAAC,KAAK,CAAC,UAAU,EAAE,aAAa,CAAC,UAAU,CAAC;QACpD,QAAQ,CAAC,KAAK,CAAC,KAAK,IAAI,GAAG,EAAE,aAAa,CAAC,KAAK,CAAC;QACjD,QAAQ,CAAC,KAAK,CAAC,QAAQ,IAAI,GAAG,EAAE,aAAa,CAAC,QAAQ,CAAC;QACvD,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,SAAS,CAAC,CACjC,CAAC;IACF,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAwB;IACxD,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAwB,EAAE,IAAgB;IACtE,OAAO,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;AAC9E,CAAC"}
1
+ {"version":3,"file":"formatter.js","sourceRoot":"","sources":["../../src/output/formatter.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,gGAAgG;AAEhG;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACzC,OAAO,IAAI;SACT,OAAO,CAAC,oCAAoC,EAAE,mBAAmB,CAAC;SAClE,OAAO,CAAC,0BAA0B,EAAE,mBAAmB,CAAC,CAAC;AAC5D,CAAC;AAgBD,MAAM,aAAa,GAAG,EAAE,UAAU,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAW,CAAC;AAC3E,MAAM,SAAS,GAAG,EAAE,CAAC;AAErB,SAAS,QAAQ,CAAC,IAAY,EAAE,GAAW;IAC1C,IAAI,IAAI,CAAC,MAAM,IAAI,GAAG;QAAE,OAAO,IAAI,CAAC;IACpC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC;AACrC,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY,EAAE,KAAa;IAC5C,IAAI,IAAI,CAAC,MAAM,IAAI,KAAK;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC;IAChE,OAAO,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAwB;IACxD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,kBAAkB,CAAC;IACnD,MAAM,MAAM,GACX,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,UAAU,CAAC;QACxC,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,KAAK,CAAC;QACtC,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC,QAAQ,CAAC;QAC5C,OAAO,CAAC;IACT,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CACtB,CAAC,KAAK,EAAE,EAAE,CACT,QAAQ,CAAC,KAAK,CAAC,UAAU,EAAE,aAAa,CAAC,UAAU,CAAC;QACpD,QAAQ,CAAC,KAAK,CAAC,KAAK,IAAI,GAAG,EAAE,aAAa,CAAC,KAAK,CAAC;QACjD,QAAQ,CAAC,KAAK,CAAC,QAAQ,IAAI,GAAG,EAAE,aAAa,CAAC,QAAQ,CAAC;QACvD,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,SAAS,CAAC,CACjC,CAAC;IACF,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAwB;IACxD,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAwB,EAAE,IAAgB;IACtE,OAAO,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;AAC9E,CAAC"}
@@ -1,2 +1,3 @@
1
+ export * from "./cli.js";
1
2
  export * from "./formatter.js";
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/output/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/output/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC"}
@@ -1,2 +1,3 @@
1
+ export * from "./cli.js";
1
2
  export * from "./formatter.js";
2
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/output/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/output/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,26 @@
1
+ import type { LinearClient } from "@linear/sdk";
2
+ export interface SyncOptions {
3
+ templatesDir: string;
4
+ team: string;
5
+ dryRun: boolean;
6
+ yes: boolean;
7
+ }
8
+ export interface PlanEntry {
9
+ filename: string;
10
+ name: string;
11
+ action: "create" | "update" | "skip";
12
+ reason?: string;
13
+ templateId?: string;
14
+ }
15
+ export interface SyncResult {
16
+ created: number;
17
+ updated: number;
18
+ skipped: number;
19
+ errors: {
20
+ filename: string;
21
+ error: string;
22
+ }[];
23
+ plan: PlanEntry[];
24
+ }
25
+ export declare function syncTemplates(client: LinearClient, opts: SyncOptions): Promise<SyncResult>;
26
+ //# sourceMappingURL=sync-linear-templates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-linear-templates.d.ts","sourceRoot":"","sources":["../../src/scripts/sync-linear-templates.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,YAAY,EAAY,MAAM,aAAa,CAAC;AAI1D,MAAM,WAAW,WAAW;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,GAAG,EAAE,OAAO,CAAC;CACb;AAED,MAAM,WAAW,SAAS;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC9C,IAAI,EAAE,SAAS,EAAE,CAAC;CAClB;AAyGD,wBAAsB,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAUhG"}
@@ -0,0 +1,115 @@
1
+ // sync-linear-templates — push .md compliance templates into Linear as native
2
+ // issue templates so they appear in the Linear UI's template picker for
3
+ // everyone, not just CLI users.
4
+ //
5
+ // Match strategy:
6
+ // 1) by source-footer in template.description (`<!-- elnora-linear-template-source: <filename> -->`)
7
+ // 2) by exact template.name match (legacy / first-time match)
8
+ //
9
+ // Naming: `templates/SEC-VLN-vulnerability.md` → name `Template: SEC-VLN-vulnerability`.
10
+ import { readdirSync, readFileSync } from "node:fs";
11
+ import { basename, extname, join } from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+ const SOURCE_TAG_RE = /<!--\s*elnora-linear-template-source:\s*(.+?)\s*-->/i;
14
+ /**
15
+ * Default `<package-root>/templates/`. Resolves to dist/scripts/ when running
16
+ * the published package and src/scripts/ during local development; both are
17
+ * three levels under the package root.
18
+ */
19
+ function defaultTemplatesDir() {
20
+ const scriptDir = fileURLToPath(new URL(".", import.meta.url));
21
+ return join(scriptDir, "..", "..", "templates");
22
+ }
23
+ function readTemplateFiles(dir) {
24
+ const files = readdirSync(dir).filter((f) => extname(f) === ".md");
25
+ return files.map((file) => {
26
+ const body = readFileSync(join(dir, file), "utf-8");
27
+ const stem = basename(file, ".md");
28
+ return { filename: file, name: `Template: ${stem}`, body };
29
+ });
30
+ }
31
+ function buildDescription(filename, body) {
32
+ const firstLine = body.split("\n").find((l) => l.trim().length > 0) ?? "";
33
+ const summary = firstLine.replace(/^#+\s*/, "").trim();
34
+ return `${summary}\n\n<!-- elnora-linear-template-source: ${filename} -->`;
35
+ }
36
+ function findExistingMatch(existing, filename, name) {
37
+ for (const t of existing) {
38
+ const desc = t.description ?? "";
39
+ const m = desc.match(SOURCE_TAG_RE);
40
+ if (m && m[1] === filename)
41
+ return t;
42
+ }
43
+ return existing.find((t) => t.name === name);
44
+ }
45
+ async function planSync(client, opts) {
46
+ const teams = await client.teams({ first: 250 });
47
+ const team = teams.nodes.find((t) => t.name.toLowerCase() === opts.team.toLowerCase() || t.key.toLowerCase() === opts.team.toLowerCase());
48
+ if (!team) {
49
+ throw new Error(`Team "${opts.team}" not found. Use --team to override.`);
50
+ }
51
+ const all = ((await client.templates) ?? []);
52
+ const issueTemplates = all.filter((t) => t.type === "issue");
53
+ const local = readTemplateFiles(opts.templatesDir);
54
+ const plan = local.map((t) => {
55
+ const match = findExistingMatch(issueTemplates, t.filename, t.name);
56
+ if (match) {
57
+ return { filename: t.filename, name: t.name, action: "update", templateId: match.id };
58
+ }
59
+ return { filename: t.filename, name: t.name, action: "create" };
60
+ });
61
+ return { plan, teamId: team.id };
62
+ }
63
+ async function applyPlan(client, opts, plan, teamId) {
64
+ const result = { created: 0, updated: 0, skipped: 0, errors: [], plan };
65
+ const local = readTemplateFiles(opts.templatesDir);
66
+ const byFilename = new Map(local.map((t) => [t.filename, t]));
67
+ for (const entry of plan) {
68
+ const tpl = byFilename.get(entry.filename);
69
+ if (!tpl) {
70
+ result.errors.push({ filename: entry.filename, error: "template file disappeared mid-run" });
71
+ continue;
72
+ }
73
+ const description = buildDescription(tpl.filename, tpl.body);
74
+ try {
75
+ if (entry.action === "create") {
76
+ await client.createTemplate({
77
+ name: tpl.name,
78
+ type: "issue",
79
+ teamId,
80
+ description,
81
+ templateData: { description: tpl.body },
82
+ });
83
+ result.created += 1;
84
+ }
85
+ else if (entry.action === "update" && entry.templateId) {
86
+ await client.updateTemplate(entry.templateId, {
87
+ name: tpl.name,
88
+ description,
89
+ templateData: { description: tpl.body },
90
+ });
91
+ result.updated += 1;
92
+ }
93
+ else {
94
+ result.skipped += 1;
95
+ }
96
+ }
97
+ catch (e) {
98
+ const msg = e instanceof Error ? e.message : String(e);
99
+ result.errors.push({ filename: entry.filename, error: msg });
100
+ }
101
+ }
102
+ return result;
103
+ }
104
+ export async function syncTemplates(client, opts) {
105
+ const resolved = {
106
+ ...opts,
107
+ templatesDir: opts.templatesDir || defaultTemplatesDir(),
108
+ };
109
+ const { plan, teamId } = await planSync(client, resolved);
110
+ if (resolved.dryRun) {
111
+ return { created: 0, updated: 0, skipped: 0, errors: [], plan };
112
+ }
113
+ return applyPlan(client, resolved, plan, teamId);
114
+ }
115
+ //# sourceMappingURL=sync-linear-templates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-linear-templates.js","sourceRoot":"","sources":["../../src/scripts/sync-linear-templates.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { LinearConfig } from "../config/types.js";
2
+ import type { Signal, SignalSourceContext, SignalSourceImpl } from "./types.js";
3
+ export interface GithubCommitsConfig {
4
+ type: "github_commits";
5
+ name: string;
6
+ enabled?: boolean;
7
+ repos?: string[];
8
+ lookback_days?: number;
9
+ }
10
+ interface CommitRow {
11
+ sha: string;
12
+ author_email: string;
13
+ message: string;
14
+ subject: string;
15
+ }
16
+ declare function buildTeamRegex(teamKeys: string[]): RegExp;
17
+ export declare function extractIssueIds(message: string, re: RegExp): string[];
18
+ declare function parseGitLog(stdout: string): CommitRow[];
19
+ export declare class GithubCommitsSource implements SignalSourceImpl {
20
+ readonly config: GithubCommitsConfig;
21
+ private readonly linearConfig;
22
+ constructor(config: GithubCommitsConfig, linearConfig: LinearConfig);
23
+ collect(ctx: SignalSourceContext): Promise<Signal[]>;
24
+ }
25
+ export declare const _internal: {
26
+ buildTeamRegex: typeof buildTeamRegex;
27
+ extractIssueIds: typeof extractIssueIds;
28
+ parseGitLog: typeof parseGitLog;
29
+ };
30
+ export {};
31
+ //# sourceMappingURL=github-commits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-commits.d.ts","sourceRoot":"","sources":["../../src/signals/github-commits.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,MAAM,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAOhF,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,SAAS;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,iBAAS,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAIlD;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,CASrE;AAED,iBAAS,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,CAehD;AAED,qBAAa,mBAAoB,YAAW,gBAAgB;IAC3D,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;gBAEhC,MAAM,EAAE,mBAAmB,EAAE,YAAY,EAAE,YAAY;IAK7D,OAAO,CAAC,GAAG,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CA0E1D;AAED,eAAO,MAAM,SAAS;;;;CAAmD,CAAC"}
@@ -0,0 +1,127 @@
1
+ // `github_commits` signal source.
2
+ //
3
+ // Shells out to `git log` on each configured repo and parses commits within
4
+ // the lookback window. Issue identifiers (e.g. ENG-101) are extracted from
5
+ // commit messages via the team-prefix regex derived from references/teams.json.
6
+ //
7
+ // The repo entries reference `references/repos.json` for the `local_path`. If
8
+ // a repo has no local_path, the source emits one warning signal per missing
9
+ // path and continues.
10
+ import { execFile } from "node:child_process";
11
+ import { existsSync } from "node:fs";
12
+ import { promisify } from "node:util";
13
+ const runCmd = promisify(execFile);
14
+ const DEFAULT_LOOKBACK_DAYS = 14;
15
+ const MAX_BUFFER = 10_000_000;
16
+ const TIMEOUT_MS = 30_000;
17
+ function buildTeamRegex(teamKeys) {
18
+ if (teamKeys.length === 0)
19
+ return /\b([A-Z]{2,5})-(\d+)\b/g;
20
+ const escaped = teamKeys.map((k) => k.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&")).join("|");
21
+ return new RegExp(`\\b(${escaped})-(\\d+)\\b`, "g");
22
+ }
23
+ export function extractIssueIds(message, re) {
24
+ const out = new Set();
25
+ re.lastIndex = 0;
26
+ let m = re.exec(message);
27
+ while (m !== null) {
28
+ out.add(`${m[1]}-${m[2]}`);
29
+ m = re.exec(message);
30
+ }
31
+ return [...out];
32
+ }
33
+ function parseGitLog(stdout) {
34
+ if (!stdout.trim())
35
+ return [];
36
+ const rows = [];
37
+ for (const block of stdout.split("\x1e")) {
38
+ const trimmed = block.trim();
39
+ if (!trimmed)
40
+ continue;
41
+ const [sha, email, subject, body = ""] = trimmed.split("\x1f");
42
+ rows.push({
43
+ sha,
44
+ author_email: email,
45
+ subject,
46
+ message: body ? `${subject}\n${body}` : subject,
47
+ });
48
+ }
49
+ return rows;
50
+ }
51
+ export class GithubCommitsSource {
52
+ config;
53
+ linearConfig;
54
+ constructor(config, linearConfig) {
55
+ this.config = config;
56
+ this.linearConfig = linearConfig;
57
+ }
58
+ async collect(ctx) {
59
+ const receivedAt = ctx.now.toISOString();
60
+ const lookback = this.config.lookback_days ?? DEFAULT_LOOKBACK_DAYS;
61
+ const teamKeys = this.linearConfig.teams.teams.map((t) => t.key);
62
+ const issueRegex = buildTeamRegex(teamKeys);
63
+ const repoNames = this.config.repos ?? this.linearConfig.repos.repos.map((r) => r.name);
64
+ const reposByName = new Map(this.linearConfig.repos.repos.map((r) => [r.name, r]));
65
+ const signals = [];
66
+ for (const name of repoNames) {
67
+ const repo = reposByName.get(name);
68
+ if (!repo?.local_path || !existsSync(repo.local_path)) {
69
+ signals.push({
70
+ source: this.config.name,
71
+ type: this.config.type,
72
+ payload: { warning: `repo "${name}" has no local_path (or path does not exist); skipped` },
73
+ receivedAt,
74
+ });
75
+ continue;
76
+ }
77
+ let rows;
78
+ try {
79
+ const { stdout } = await runCmd("git", ["-C", repo.local_path, "log", `--since=${lookback} days ago`, "--format=%H%x1f%ae%x1f%s%x1f%b%x1e"], { timeout: TIMEOUT_MS, maxBuffer: MAX_BUFFER });
80
+ rows = parseGitLog(stdout);
81
+ }
82
+ catch (err) {
83
+ signals.push({
84
+ source: this.config.name,
85
+ type: this.config.type,
86
+ payload: { warning: `git log failed for "${name}": ${err.message}` },
87
+ receivedAt,
88
+ });
89
+ continue;
90
+ }
91
+ for (const row of rows) {
92
+ const ids = extractIssueIds(row.message, issueRegex);
93
+ if (ids.length === 0) {
94
+ signals.push({
95
+ source: this.config.name,
96
+ type: this.config.type,
97
+ payload: {
98
+ repo: name,
99
+ sha: row.sha,
100
+ author_email: row.author_email,
101
+ subject: row.subject,
102
+ },
103
+ receivedAt,
104
+ });
105
+ continue;
106
+ }
107
+ for (const id of ids) {
108
+ signals.push({
109
+ source: this.config.name,
110
+ type: this.config.type,
111
+ issueIdentifier: id,
112
+ payload: {
113
+ repo: name,
114
+ sha: row.sha,
115
+ author_email: row.author_email,
116
+ subject: row.subject,
117
+ },
118
+ receivedAt,
119
+ });
120
+ }
121
+ }
122
+ }
123
+ return signals;
124
+ }
125
+ }
126
+ export const _internal = { buildTeamRegex, extractIssueIds, parseGitLog };
127
+ //# sourceMappingURL=github-commits.js.map