@cursor/july 0.1.54 → 0.1.61

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 (293) hide show
  1. package/dist/bin/agent-serve.js +30 -23
  2. package/dist/channels/github/github-channel.d.ts +12 -0
  3. package/dist/channels/github/github-channel.d.ts.map +1 -1
  4. package/dist/channels/github/github-channel.js +67 -15
  5. package/dist/channels/github/types.d.ts +27 -1
  6. package/dist/channels/github/types.d.ts.map +1 -1
  7. package/dist/channels/origin/cursor-account.d.ts +22 -0
  8. package/dist/channels/origin/cursor-account.d.ts.map +1 -0
  9. package/dist/channels/origin/cursor-account.js +35 -0
  10. package/dist/channels/origin/events.d.ts +10 -0
  11. package/dist/channels/origin/events.d.ts.map +1 -0
  12. package/dist/channels/origin/events.js +27 -0
  13. package/dist/channels/origin/index.d.ts +29 -0
  14. package/dist/channels/origin/index.d.ts.map +1 -0
  15. package/dist/channels/origin/index.js +28 -0
  16. package/dist/channels/origin/origin-channel.d.ts +68 -0
  17. package/dist/channels/origin/origin-channel.d.ts.map +1 -0
  18. package/dist/channels/origin/origin-channel.js +567 -0
  19. package/dist/channels/origin/origin-webhook.d.ts +38 -0
  20. package/dist/channels/origin/origin-webhook.d.ts.map +1 -0
  21. package/dist/channels/origin/origin-webhook.js +151 -0
  22. package/dist/channels/origin/types.d.ts +152 -0
  23. package/dist/channels/origin/types.d.ts.map +1 -0
  24. package/dist/channels/origin/types.js +1 -0
  25. package/dist/channels/slack/doctor.d.ts +17 -1
  26. package/dist/channels/slack/doctor.d.ts.map +1 -1
  27. package/dist/channels/slack/doctor.js +186 -10
  28. package/dist/docs/404.html +2 -2
  29. package/dist/docs/ab.html +4 -4
  30. package/dist/docs/assets/{app.DAKcDclS.js → app.CW3KJh72.js} +1 -1
  31. package/dist/docs/assets/chunks/@localSearchIndexroot.BuymfBAo.js +1 -0
  32. package/dist/docs/assets/chunks/{VPLocalSearchBox.BNIaCSfG.js → VPLocalSearchBox.Bk6EWpPj.js} +1 -1
  33. package/dist/docs/assets/chunks/{theme.B47GzJjc.js → theme.Bz9AodBA.js} +2 -2
  34. package/dist/docs/assets/{example-agents_fsd.md.DPz9ezO4.js → example-agents_fsd.md.ZeGEpAw_.js} +2 -2
  35. package/dist/docs/assets/{example-agents_fsd.md.DPz9ezO4.lean.js → example-agents_fsd.md.ZeGEpAw_.lean.js} +1 -1
  36. package/dist/docs/assets/{example-agents_security-reviewer.md.Dkf1gyo6.js → example-agents_security-reviewer.md.DBL1TwtP.js} +2 -2
  37. package/dist/docs/assets/{example-agents_security-reviewer.md.Dkf1gyo6.lean.js → example-agents_security-reviewer.md.DBL1TwtP.lean.js} +1 -1
  38. package/dist/docs/assets/guides_convert-automation.md.BhMzTkE5.js +12 -0
  39. package/dist/docs/assets/guides_convert-automation.md.BhMzTkE5.lean.js +1 -0
  40. package/dist/docs/assets/{guides_github.md.B8QKtIpw.js → guides_github.md.CLLQJKRB.js} +4 -4
  41. package/dist/docs/assets/{guides_github.md.B8QKtIpw.lean.js → guides_github.md.CLLQJKRB.lean.js} +1 -1
  42. package/dist/docs/assets/index.md.DhRHS_-L.js +5 -0
  43. package/dist/docs/assets/{index.md.COeoSkcF.lean.js → index.md.DhRHS_-L.lean.js} +1 -1
  44. package/dist/docs/assets/{reference_cli.md.-V6J7Cl8.js → reference_cli.md.Cl14Jfo3.js} +10 -2
  45. package/dist/docs/assets/{reference_cli.md.-V6J7Cl8.lean.js → reference_cli.md.Cl14Jfo3.lean.js} +1 -1
  46. package/dist/docs/assets/scaffolding-agents.md.CrWSA0Ko.js +1 -0
  47. package/dist/docs/assets/{scaffolding-agents.md.mkc3B_ZW.lean.js → scaffolding-agents.md.CrWSA0Ko.lean.js} +1 -1
  48. package/dist/docs/assets/templates_agentic-owners.md.BkTLORaU.js +14 -0
  49. package/dist/docs/assets/templates_agentic-owners.md.BkTLORaU.lean.js +1 -0
  50. package/dist/docs/assets/templates_demo.md.Bgd6MBaZ.js +4 -0
  51. package/dist/docs/assets/templates_demo.md.Bgd6MBaZ.lean.js +1 -0
  52. package/dist/docs/assets/templates_pr-autofixer.md.DcmoeUNZ.js +8 -0
  53. package/dist/docs/assets/templates_pr-autofixer.md.DcmoeUNZ.lean.js +1 -0
  54. package/dist/docs/assets/templates_security-reviewer.md.C0yIUaYs.js +2 -0
  55. package/dist/docs/assets/templates_security-reviewer.md.C0yIUaYs.lean.js +1 -0
  56. package/dist/docs/assets/templates_triage.md.DWuQ1bZz.js +9 -0
  57. package/dist/docs/assets/templates_triage.md.DWuQ1bZz.lean.js +1 -0
  58. package/dist/docs/building-with-agents.html +4 -4
  59. package/dist/docs/concepts.html +5 -5
  60. package/dist/docs/deployment.html +4 -4
  61. package/dist/docs/evals.html +4 -4
  62. package/dist/docs/example-agents/approval-buddy.html +4 -4
  63. package/dist/docs/example-agents/benny.html +4 -4
  64. package/dist/docs/example-agents/bugbot.html +4 -4
  65. package/dist/docs/example-agents/codebase-wiki.html +4 -4
  66. package/dist/docs/example-agents/codeowners-review.html +4 -4
  67. package/dist/docs/example-agents/concierge.html +4 -4
  68. package/dist/docs/example-agents/fsd.html +5 -5
  69. package/dist/docs/example-agents/index.html +4 -4
  70. package/dist/docs/example-agents/knowledge-base.html +4 -4
  71. package/dist/docs/example-agents/oncall.html +4 -4
  72. package/dist/docs/example-agents/security-reviewer.html +5 -5
  73. package/dist/docs/example-agents/slack-agent.html +4 -4
  74. package/dist/docs/example-agents/weather-agent.html +4 -4
  75. package/dist/docs/guides/agent-to-agent.html +4 -4
  76. package/dist/docs/guides/cloud-runtime.html +4 -4
  77. package/dist/docs/guides/convert-automation.html +36 -0
  78. package/dist/docs/guides/github.html +7 -7
  79. package/dist/docs/guides/human-in-the-loop.html +4 -4
  80. package/dist/docs/guides/mcp-oauth.html +4 -4
  81. package/dist/docs/guides/opentelemetry.html +4 -4
  82. package/dist/docs/guides/slack.html +4 -4
  83. package/dist/docs/guides/webhooks.html +4 -4
  84. package/dist/docs/hashmap.json +1 -1
  85. package/dist/docs/hillclimbing.html +4 -4
  86. package/dist/docs/index.html +6 -6
  87. package/dist/docs/quickstart.html +4 -4
  88. package/dist/docs/reference/agent-config.html +4 -4
  89. package/dist/docs/reference/artifacts.html +4 -4
  90. package/dist/docs/reference/channels.html +4 -4
  91. package/dist/docs/reference/cli.html +14 -6
  92. package/dist/docs/reference/connections.html +4 -4
  93. package/dist/docs/reference/hooks.html +4 -4
  94. package/dist/docs/reference/http-api.html +4 -4
  95. package/dist/docs/reference/instructions.html +4 -4
  96. package/dist/docs/reference/playground.html +4 -4
  97. package/dist/docs/reference/project-layout.html +4 -4
  98. package/dist/docs/reference/prompt.html +4 -4
  99. package/dist/docs/reference/schedules.html +4 -4
  100. package/dist/docs/reference/sessions.html +4 -4
  101. package/dist/docs/reference/skills.html +4 -4
  102. package/dist/docs/reference/subagents.html +4 -4
  103. package/dist/docs/reference/tools.html +4 -4
  104. package/dist/docs/scaffolding-agents.html +5 -5
  105. package/dist/docs/storage.html +4 -4
  106. package/dist/docs/templates/agentic-owners.html +38 -0
  107. package/dist/docs/templates/demo.html +28 -0
  108. package/dist/docs/templates/pr-autofixer.html +32 -0
  109. package/dist/docs/templates/security-reviewer.html +26 -0
  110. package/dist/docs/templates/triage.html +14 -16
  111. package/dist/docs/troubleshooting.html +4 -4
  112. package/dist/index.d.ts +3 -3
  113. package/dist/index.d.ts.map +1 -1
  114. package/dist/index.js +2 -2
  115. package/dist/internal/cli-ax.d.ts +2 -0
  116. package/dist/internal/cli-ax.d.ts.map +1 -1
  117. package/dist/internal/cli-ax.js +30 -1
  118. package/dist/internal/cli-deploy.js +1 -1
  119. package/dist/internal/cli-slack.d.ts +4 -0
  120. package/dist/internal/cli-slack.d.ts.map +1 -1
  121. package/dist/internal/cli-slack.js +46 -20
  122. package/dist/internal/cursor-event-relay.d.ts +59 -11
  123. package/dist/internal/cursor-event-relay.d.ts.map +1 -1
  124. package/dist/internal/cursor-event-relay.js +133 -26
  125. package/dist/internal/discovery.d.ts.map +1 -1
  126. package/dist/internal/discovery.js +11 -2
  127. package/dist/internal/distribution.d.ts.map +1 -1
  128. package/dist/internal/distribution.js +1 -0
  129. package/dist/internal/handleAgentServeTrigger.d.ts +33 -6
  130. package/dist/internal/handleAgentServeTrigger.d.ts.map +1 -1
  131. package/dist/internal/handleAgentServeTrigger.js +97 -19
  132. package/dist/internal/init-interview.d.ts +67 -0
  133. package/dist/internal/init-interview.d.ts.map +1 -0
  134. package/dist/internal/init-interview.js +300 -0
  135. package/dist/internal/init-project.d.ts +7 -0
  136. package/dist/internal/init-project.d.ts.map +1 -1
  137. package/dist/internal/init-project.js +99 -43
  138. package/dist/internal/mcp-endpoint.d.ts +7 -0
  139. package/dist/internal/mcp-endpoint.d.ts.map +1 -1
  140. package/dist/internal/mcp-endpoint.js +7 -0
  141. package/dist/internal/server.d.ts.map +1 -1
  142. package/dist/internal/server.js +159 -57
  143. package/dist/internal/session-engine.d.ts.map +1 -1
  144. package/dist/internal/session-engine.js +20 -21
  145. package/dist/internal/session-links.d.ts +14 -0
  146. package/dist/internal/session-links.d.ts.map +1 -0
  147. package/dist/internal/session-links.js +69 -0
  148. package/dist/internal/sessions-client.d.ts.map +1 -1
  149. package/dist/internal/sessions-client.js +1 -0
  150. package/dist/internal/slack-provision-client.js +1 -1
  151. package/dist/playground/assets/index-ClbuaLpm.css +1 -0
  152. package/dist/playground/assets/index-SqkR13k_.js +85 -0
  153. package/dist/playground/index.html +2 -2
  154. package/dist/types.d.ts +19 -12
  155. package/dist/types.d.ts.map +1 -1
  156. package/docs/README.md +15 -0
  157. package/docs/example-agents/fsd.md +3 -0
  158. package/docs/example-agents/security-reviewer.md +3 -0
  159. package/docs/guides/convert-automation.md +176 -0
  160. package/docs/guides/github.md +6 -2
  161. package/docs/reference/cli.md +36 -10
  162. package/docs/scaffolding-agents.md +3 -0
  163. package/docs/templates/agentic-owners.md +97 -0
  164. package/docs/templates/demo.md +84 -0
  165. package/docs/templates/pr-autofixer.md +136 -0
  166. package/docs/templates/security-reviewer.md +89 -0
  167. package/docs/templates/triage.md +59 -101
  168. package/package.json +9 -1
  169. package/skills/github/SKILL.md +4 -5
  170. package/src/bin/agent-serve.ts +13 -6
  171. package/src/channels/github/github-channel.ts +95 -18
  172. package/src/channels/github/types.ts +31 -0
  173. package/src/channels/origin/cursor-account.ts +64 -0
  174. package/src/channels/origin/events.ts +41 -0
  175. package/src/channels/origin/index.ts +29 -0
  176. package/src/channels/origin/origin-channel.ts +784 -0
  177. package/src/channels/origin/origin-webhook.ts +202 -0
  178. package/src/channels/origin/types.ts +186 -0
  179. package/src/channels/slack/doctor.ts +218 -5
  180. package/src/index.ts +3 -1
  181. package/src/internal/cli-ax.ts +33 -1
  182. package/src/internal/cli-deploy.ts +2 -2
  183. package/src/internal/cli-slack.ts +68 -19
  184. package/src/internal/cursor-event-relay.ts +193 -31
  185. package/src/internal/discovery.ts +15 -5
  186. package/src/internal/distribution.ts +1 -0
  187. package/src/internal/handleAgentServeTrigger.ts +129 -19
  188. package/src/internal/init-interview.ts +384 -0
  189. package/src/internal/init-project.ts +146 -46
  190. package/src/internal/mcp-endpoint.ts +7 -0
  191. package/src/internal/server.ts +188 -58
  192. package/src/internal/session-engine.ts +5 -21
  193. package/src/internal/session-links.ts +88 -0
  194. package/src/internal/sessions-client.ts +1 -0
  195. package/src/internal/slack-provision-client.ts +1 -1
  196. package/src/types.ts +19 -12
  197. package/templates/agentic-owners/README.md +73 -0
  198. package/templates/agentic-owners/agent/agent.ts +15 -0
  199. package/templates/agentic-owners/agent/channels/github.ts +85 -0
  200. package/templates/agentic-owners/agent/instructions.md +34 -0
  201. package/templates/agentic-owners/agent/lib/config.ts +13 -0
  202. package/templates/agentic-owners/agent/lib/github.ts +241 -0
  203. package/templates/agentic-owners/agent/lib/owners/README.md +50 -0
  204. package/templates/agentic-owners/agent/lib/owners/constants.ts +22 -0
  205. package/templates/agentic-owners/agent/lib/owners/decision.ts +485 -0
  206. package/templates/agentic-owners/agent/lib/owners/handles.ts +54 -0
  207. package/templates/agentic-owners/agent/lib/owners/match.ts +177 -0
  208. package/templates/agentic-owners/agent/lib/owners/matching.ts +95 -0
  209. package/templates/agentic-owners/agent/lib/owners/parse.ts +589 -0
  210. package/templates/agentic-owners/agent/lib/owners/policyMode.ts +65 -0
  211. package/templates/agentic-owners/agent/lib/owners/prompt.ts +66 -0
  212. package/templates/agentic-owners/agent/lib/owners/prompts/base-review.md +105 -0
  213. package/templates/agentic-owners/agent/lib/owners/types.ts +45 -0
  214. package/templates/agentic-owners/agent/lib/review.ts +777 -0
  215. package/templates/agentic-owners/agent/tools/record_review.ts +69 -0
  216. package/templates/agentic-owners/evals/evals.config.ts +5 -0
  217. package/templates/agentic-owners/evals/owners.eval.ts +116 -0
  218. package/templates/agentic-owners/package.json +20 -0
  219. package/templates/agentic-owners/tsconfig.json +12 -0
  220. package/templates/demo/README.md +33 -0
  221. package/templates/demo/agent/agent.ts +15 -0
  222. package/templates/demo/agent/channels/github.ts +50 -0
  223. package/templates/demo/agent/channels/queue.ts +30 -0
  224. package/templates/demo/agent/instructions.md +8 -0
  225. package/templates/demo/agent/lib/collect.ts +78 -0
  226. package/templates/demo/agent/tools/record_demo.ts +40 -0
  227. package/templates/demo/evals/demo.eval.ts +33 -0
  228. package/templates/demo/evals/evals.config.ts +5 -0
  229. package/templates/demo/package.json +18 -0
  230. package/templates/demo/tsconfig.json +12 -0
  231. package/templates/pr-autofixer/README.md +44 -0
  232. package/templates/pr-autofixer/agent/agent.ts +22 -0
  233. package/templates/pr-autofixer/agent/channels/drive.ts +40 -0
  234. package/templates/pr-autofixer/agent/channels/github.ts +167 -0
  235. package/templates/pr-autofixer/agent/channels/slack.ts +17 -0
  236. package/templates/pr-autofixer/agent/hooks/bind-agent.ts +19 -0
  237. package/templates/pr-autofixer/agent/instructions.md +83 -0
  238. package/templates/pr-autofixer/agent/lib/affinity.ts +33 -0
  239. package/templates/pr-autofixer/agent/lib/drive-schemas.ts +15 -0
  240. package/templates/pr-autofixer/agent/lib/drive.ts +79 -0
  241. package/templates/pr-autofixer/agent/lib/merge-watch.ts +123 -0
  242. package/templates/pr-autofixer/agent/lib/pr-state.ts +54 -0
  243. package/templates/pr-autofixer/agent/lib/repos.ts +19 -0
  244. package/templates/pr-autofixer/agent/lib/start-drive.ts +122 -0
  245. package/templates/pr-autofixer/agent/lib/webhook-buffer.ts +225 -0
  246. package/templates/pr-autofixer/agent/lib/webhook-routing.ts +231 -0
  247. package/templates/pr-autofixer/agent/storage.ts +7 -0
  248. package/templates/pr-autofixer/agent/tools/drive_pr.ts +22 -0
  249. package/templates/pr-autofixer/evals/coordinator.eval.ts +16 -0
  250. package/templates/pr-autofixer/evals/evals.config.ts +5 -0
  251. package/templates/pr-autofixer/init.json +25 -0
  252. package/templates/pr-autofixer/package.json +18 -0
  253. package/templates/pr-autofixer/tsconfig.json +12 -0
  254. package/templates/security-reviewer/README.md +31 -0
  255. package/templates/security-reviewer/agent/agent.ts +15 -0
  256. package/templates/security-reviewer/agent/channels/github.ts +46 -0
  257. package/templates/security-reviewer/agent/instructions.md +22 -0
  258. package/templates/security-reviewer/agent/lib/repos.ts +5 -0
  259. package/templates/security-reviewer/agent/tools/post_findings.ts +72 -0
  260. package/templates/security-reviewer/evals/evals.config.ts +5 -0
  261. package/templates/security-reviewer/evals/review.eval.ts +53 -0
  262. package/templates/security-reviewer/init.json +25 -0
  263. package/templates/security-reviewer/package.json +18 -0
  264. package/templates/security-reviewer/tsconfig.json +12 -0
  265. package/templates/triage/README.md +27 -29
  266. package/templates/triage/agent/agent.ts +1 -0
  267. package/templates/triage/agent/channels/queue.ts +47 -0
  268. package/templates/triage/agent/channels/webhook.ts +50 -0
  269. package/templates/triage/agent/instructions.md +24 -13
  270. package/templates/triage/agent/lib/actuate.ts +759 -0
  271. package/templates/triage/agent/lib/parse-queue-event.ts +168 -0
  272. package/templates/triage/agent/lib/tracker.ts +5 -0
  273. package/templates/triage/agent/lib/types.ts +52 -0
  274. package/templates/triage/agent/lib/verify-webhook.ts +81 -0
  275. package/templates/triage/agent/skills/triage-rubric.md +6 -5
  276. package/templates/triage/agent/tools/record_triage.ts +24 -5
  277. package/templates/triage/evals/triage.eval.ts +25 -3
  278. package/templates/triage/overlays/jira/README.md +21 -0
  279. package/templates/triage/overlays/jira/agent/lib/tracker.ts +8 -0
  280. package/templates/triage/overlays/jira/agent/mcp-connections/tracker.ts +7 -0
  281. package/templates/triage/overlays/linear/README.md +21 -0
  282. package/templates/triage/overlays/linear/agent/lib/tracker.ts +8 -0
  283. package/templates/triage/overlays/linear/agent/mcp-connections/tracker.ts +7 -0
  284. package/templates/triage/package.json +1 -1
  285. package/dist/docs/assets/chunks/@localSearchIndexroot.C4uxzYEU.js +0 -1
  286. package/dist/docs/assets/index.md.COeoSkcF.js +0 -5
  287. package/dist/docs/assets/scaffolding-agents.md.mkc3B_ZW.js +0 -1
  288. package/dist/docs/assets/templates_triage.md.CConZt1z.js +0 -11
  289. package/dist/docs/assets/templates_triage.md.CConZt1z.lean.js +0 -1
  290. package/dist/playground/assets/index-BHAPd9KT.js +0 -85
  291. package/dist/playground/assets/index-DpKH0HcT.css +0 -1
  292. package/templates/triage/agent/channels/intake.ts +0 -20
  293. package/templates/triage/agent/channels/slack.ts +0 -3
@@ -0,0 +1,168 @@
1
+ import type { QueueIssue } from "./types.js";
2
+
3
+ export const ISSUE_ID: RegExp = /\b[A-Z][A-Z0-9]{0,9}-\d+\b/;
4
+ export const LINEAR_URL: RegExp =
5
+ /https:\/\/linear\.app\/(?:[^/\s"'<>]+\/)?issue\/[A-Z][A-Z0-9]{0,9}-\d+[^\s"'<>]*/i;
6
+ export const JIRA_URL: RegExp =
7
+ /https:\/\/\S+\/(?:browse\/[A-Z][A-Z0-9]{0,9}-\d+|jira\/software\/[^\s"'<>]*selectedIssue=[A-Z][A-Z0-9]{0,9}-\d+)/i;
8
+
9
+ export type ParseResult =
10
+ | { ok: true; issue: QueueIssue }
11
+ | { ok: false; ignored: true }
12
+ | { ok: false; error: string };
13
+
14
+ export function isIssueId(value: string): boolean {
15
+ const match = value.match(ISSUE_ID);
16
+ return match !== null && match[0] === value;
17
+ }
18
+
19
+ export function pinIssueIdentifier(
20
+ continuationKey: string | null | undefined,
21
+ fallback: string
22
+ ): string {
23
+ if (typeof continuationKey === "string" && isIssueId(continuationKey)) {
24
+ return continuationKey;
25
+ }
26
+ return fallback;
27
+ }
28
+
29
+ export function isTrackerWebhook(body: unknown): boolean {
30
+ return (
31
+ isRecord(body) &&
32
+ (typeof body["type"] === "string" || typeof body["webhookEvent"] === "string")
33
+ );
34
+ }
35
+
36
+ export function parseQueueEvent(body: unknown): ParseResult {
37
+ if (!isRecord(body)) {
38
+ return { ok: false, error: "unsupported queue event" };
39
+ }
40
+
41
+ if (typeof body["webhookEvent"] === "string") {
42
+ if (body["webhookEvent"] !== "jira:issue_created") {
43
+ return { ok: false, ignored: true };
44
+ }
45
+ return parseJiraIssue(body);
46
+ }
47
+
48
+ if (typeof body["type"] === "string") {
49
+ if (body["type"] !== "Issue" || body["action"] !== "create") {
50
+ return { ok: false, ignored: true };
51
+ }
52
+ return parseLinearIssue(body);
53
+ }
54
+
55
+ if (typeof body["identifier"] === "string") {
56
+ return queueIssue({
57
+ identifier: body["identifier"],
58
+ title: stringValue(body["title"]),
59
+ description: descriptionValue(body["description"]),
60
+ url: stringValue(body["url"]),
61
+ reporter: reporterValue(body["reporter"]),
62
+ });
63
+ }
64
+
65
+ return { ok: false, error: "identifier is required" };
66
+ }
67
+
68
+ export function formatQueuePrompt(issue: QueueIssue): string {
69
+ return [
70
+ "Triage this existing issue.",
71
+ "",
72
+ `identifier: ${issue.identifier}`,
73
+ `title: ${issue.title}`,
74
+ `description: ${issue.description}`,
75
+ `url: ${issue.url}`,
76
+ `reporter: ${issue.reporter ?? ""}`,
77
+ ].join("\n");
78
+ }
79
+
80
+ function parseLinearIssue(body: Record<string, unknown>): ParseResult {
81
+ const data = body["data"];
82
+ if (!isRecord(data)) {
83
+ return { ok: false, ignored: true };
84
+ }
85
+ const identifier =
86
+ stringValue(data["identifier"]) || stringValue(data["key"]);
87
+ if (identifier.match(ISSUE_ID)?.[0] === undefined) {
88
+ return { ok: false, ignored: true };
89
+ }
90
+ return queueIssue({
91
+ identifier,
92
+ title: stringValue(data["title"]),
93
+ description: descriptionValue(data["description"]),
94
+ url: stringValue(data["url"]),
95
+ reporter: reporterValue(data["creator"]),
96
+ });
97
+ }
98
+
99
+ function parseJiraIssue(body: Record<string, unknown>): ParseResult {
100
+ const issue = body["issue"];
101
+ if (!isRecord(issue)) {
102
+ return { ok: false, ignored: true };
103
+ }
104
+ const fields = isRecord(issue["fields"]) ? issue["fields"] : {};
105
+ const identifier = stringValue(issue["key"]);
106
+ if (identifier.match(ISSUE_ID)?.[0] === undefined) {
107
+ return { ok: false, ignored: true };
108
+ }
109
+ return queueIssue({
110
+ identifier,
111
+ title: stringValue(fields["summary"]),
112
+ description: descriptionValue(fields["description"]),
113
+ url: stringValue(issue["self"]),
114
+ reporter: reporterValue(fields["reporter"]),
115
+ });
116
+ }
117
+
118
+ function queueIssue(input: QueueIssue): ParseResult {
119
+ const identifier = input.identifier.match(ISSUE_ID)?.[0];
120
+ if (identifier === undefined) {
121
+ return { ok: false, error: "identifier is required" };
122
+ }
123
+ return { ok: true, issue: { ...input, identifier } };
124
+ }
125
+
126
+ function isRecord(value: unknown): value is Record<string, unknown> {
127
+ return value !== null && typeof value === "object" && !Array.isArray(value);
128
+ }
129
+
130
+ function stringValue(value: unknown): string {
131
+ return typeof value === "string" ? value : "";
132
+ }
133
+
134
+ export function richTextValue(value: unknown): string {
135
+ if (typeof value === "string") {
136
+ return value;
137
+ }
138
+ if (Array.isArray(value)) {
139
+ return value.map(richTextValue).filter(Boolean).join("\n");
140
+ }
141
+ if (!isRecord(value)) {
142
+ return "";
143
+ }
144
+ if (typeof value["text"] === "string") {
145
+ return value["text"];
146
+ }
147
+ return richTextValue(value["content"]);
148
+ }
149
+
150
+ function descriptionValue(value: unknown): string {
151
+ return richTextValue(value);
152
+ }
153
+
154
+ function reporterValue(value: unknown): string | undefined {
155
+ if (typeof value === "string" && value !== "") {
156
+ return value;
157
+ }
158
+ if (!isRecord(value)) {
159
+ return undefined;
160
+ }
161
+ for (const key of ["displayName", "name", "email", "emailAddress"]) {
162
+ const candidate = value[key];
163
+ if (typeof candidate === "string" && candidate !== "") {
164
+ return candidate;
165
+ }
166
+ }
167
+ return undefined;
168
+ }
@@ -0,0 +1,5 @@
1
+ import type { TrackerConfig } from "./types.js";
2
+
3
+ const tracker: TrackerConfig = { kind: "none" };
4
+
5
+ export default tracker;
@@ -0,0 +1,52 @@
1
+ export type TriageKind = "bug" | "question" | "feedback";
2
+
3
+ export type TriageSeverity = "low" | "medium" | "high" | "urgent";
4
+
5
+ export interface QueueIssue {
6
+ identifier: string;
7
+ url: string;
8
+ title: string;
9
+ description: string;
10
+ reporter?: string;
11
+ }
12
+
13
+ export interface TriageVerdict {
14
+ identifier: string;
15
+ kind: TriageKind;
16
+ severity: TriageSeverity;
17
+ title: string;
18
+ summary: string;
19
+ nextStep: string;
20
+ reporter?: string;
21
+ duplicateOf?: string;
22
+ needsInfo?: boolean;
23
+ }
24
+
25
+ export interface TrackerIssue {
26
+ identifier: string;
27
+ url: string;
28
+ }
29
+
30
+ export type QueueAction =
31
+ | { op: "commented" }
32
+ | { op: "prioritized"; severity: TriageSeverity }
33
+ | { op: "duplicate_of"; of: string }
34
+ | { op: "needs_info" };
35
+
36
+ export type TrackerResult =
37
+ | { status: "not_connected" }
38
+ | { status: "skipped"; reason: "eval" }
39
+ | { status: "acted"; issue: TrackerIssue; actions: QueueAction[] }
40
+ | { status: "failed"; message: string };
41
+
42
+ export interface RecordTriageResult {
43
+ verdict: TriageVerdict;
44
+ evaluatedAt: string;
45
+ tracker: TrackerResult;
46
+ }
47
+
48
+ export type TrackerKind = "none" | "linear" | "jira";
49
+
50
+ export type TrackerConfig =
51
+ | { kind: "none" }
52
+ | { kind: "linear" | "jira"; connectionName: string };
@@ -0,0 +1,81 @@
1
+ import { createHmac, timingSafeEqual } from "node:crypto";
2
+
3
+ const REPLAY_WINDOW_MS = 60_000;
4
+
5
+ export type VerifyWebhookResult =
6
+ | { ok: true }
7
+ | { ok: false; status: 401 };
8
+
9
+ export function verifyTrackerWebhook(input: {
10
+ rawBody: string;
11
+ linearSignature: string | null;
12
+ hubSignature: string | null;
13
+ linearSecret: string | undefined;
14
+ jiraSecret: string | undefined;
15
+ nowMs: number;
16
+ }): VerifyWebhookResult {
17
+ if (input.linearSignature !== null && input.linearSignature !== "") {
18
+ if (
19
+ input.linearSecret === undefined ||
20
+ input.linearSecret === "" ||
21
+ !hmacHexEqual(input.linearSecret, input.rawBody, input.linearSignature)
22
+ ) {
23
+ return { ok: false, status: 401 };
24
+ }
25
+ return linearReplayFresh(input.rawBody, input.nowMs);
26
+ }
27
+
28
+ if (input.hubSignature !== null && input.hubSignature !== "") {
29
+ if (
30
+ input.jiraSecret === undefined ||
31
+ input.jiraSecret === "" ||
32
+ !hmacHexEqual(input.jiraSecret, input.rawBody, input.hubSignature)
33
+ ) {
34
+ return { ok: false, status: 401 };
35
+ }
36
+ return { ok: true };
37
+ }
38
+
39
+ return { ok: false, status: 401 };
40
+ }
41
+
42
+ function linearReplayFresh(
43
+ rawBody: string,
44
+ nowMs: number
45
+ ): VerifyWebhookResult {
46
+ let body: unknown;
47
+ try {
48
+ body = JSON.parse(rawBody);
49
+ } catch {
50
+ return { ok: false, status: 401 };
51
+ }
52
+ if (body === null || typeof body !== "object" || Array.isArray(body)) {
53
+ return { ok: false, status: 401 };
54
+ }
55
+ const timestamp = (body as { webhookTimestamp?: unknown }).webhookTimestamp;
56
+ if (typeof timestamp !== "number" || !Number.isFinite(timestamp)) {
57
+ return { ok: false, status: 401 };
58
+ }
59
+ if (Math.abs(nowMs - timestamp) > REPLAY_WINDOW_MS) {
60
+ return { ok: false, status: 401 };
61
+ }
62
+ return { ok: true };
63
+ }
64
+
65
+ function hmacHexEqual(secret: string, rawBody: string, header: string): boolean {
66
+ const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
67
+ const received = header.replace(/^sha256=/i, "").trim().toLowerCase();
68
+ let expectedBytes: Buffer;
69
+ let receivedBytes: Buffer;
70
+ try {
71
+ expectedBytes = Buffer.from(expected, "hex");
72
+ receivedBytes = Buffer.from(received, "hex");
73
+ } catch {
74
+ return false;
75
+ }
76
+ return (
77
+ expectedBytes.length > 0 &&
78
+ expectedBytes.length === receivedBytes.length &&
79
+ timingSafeEqual(expectedBytes, receivedBytes)
80
+ );
81
+ }
@@ -1,13 +1,14 @@
1
1
  ---
2
- description: Definitions and tie-breakers for classifying a report as bug, question, or feedback, plus the severity ladder.
2
+ description: Definitions and tie-breakers for classifying an issue as bug, question, or feedback, plus the severity ladder.
3
3
  ---
4
4
 
5
5
  # Triage rubric
6
6
 
7
7
  ## Kinds
8
8
 
9
- - **bug**: something is broken, erroring, slow, or behaving wrong right
10
- now, and the reporter wants it diagnosed or fixed. Usually has a
9
+ - **bug**: the issue says something is broken, erroring, slow, or
10
+ behaving wrong now, and the reporter wants it diagnosed or fixed. It
11
+ usually has a
11
12
  symptom, an error, an id, a timestamp, or steps.
12
13
  - **question**: the reporter wants to know something that has an answer
13
14
  (how does X work, is Y expected, who owns Z). Someone has to look it
@@ -23,7 +24,7 @@ description: Definitions and tie-breakers for classifying a report as bug, quest
23
24
  - A question about a specific failure that already happened is a bug.
24
25
  "Why did my export fail yesterday?" needs an investigation, not a
25
26
  lookup.
26
- - A bug report that also suggests a fix is a bug.
27
+ - An issue that reports a bug and suggests a fix is a bug.
27
28
  - A known bug with a workaround that only works sometimes is a bug. The
28
29
  unreliable workaround is the thing to explain.
29
30
  - A feature idea phrased as a question ("could we have dark mode?") is
@@ -39,5 +40,5 @@ description: Definitions and tie-breakers for classifying a report as bug, quest
39
40
  costs time.
40
41
  - **low**: cosmetic, minor friction, questions, and most feedback.
41
42
 
42
- Questions and feedback default to low unless the report shows real
43
+ Questions and feedback default to low unless the issue shows real
43
44
  user impact.
@@ -1,23 +1,42 @@
1
1
  import { defineTool } from "@cursor/july/tools";
2
2
  import { z } from "zod";
3
+ import { actuate } from "../lib/actuate.js";
4
+ import tracker from "../lib/tracker.js";
3
5
 
4
6
  export default defineTool({
5
7
  description:
6
- "Record the structured triage verdict for the current report. Call exactly once per report, before replying.",
8
+ "Record the verdict for the current tracker issue and enrich it in place with a comment, priority, or duplicate link. Call exactly once per issue. Never create a ticket.",
7
9
  inputSchema: z.object({
10
+ identifier: z
11
+ .string()
12
+ .describe("Identifier of the existing tracker issue."),
8
13
  kind: z.enum(["bug", "question", "feedback"]),
9
14
  severity: z.enum(["low", "medium", "high", "urgent"]),
10
15
  title: z
11
16
  .string()
12
17
  .max(80)
13
- .describe("Short, imperative summary of the report."),
18
+ .describe("Short title for the issue."),
14
19
  summary: z
15
20
  .string()
16
- .describe("What the reporter said, in two sentences or less."),
21
+ .describe("What the issue says, in two sentences or less."),
17
22
  nextStep: z.string().describe("The single next action for the team."),
18
23
  reporter: z.string().optional().describe("Who reported it, when known."),
24
+ duplicateOf: z
25
+ .string()
26
+ .optional()
27
+ .describe("Identifier of a matching existing issue."),
28
+ needsInfo: z
29
+ .boolean()
30
+ .optional()
31
+ .describe("Whether the issue needs more information."),
19
32
  }),
20
- async execute(verdict) {
21
- return { recorded: true, at: new Date().toISOString(), verdict };
33
+ async execute(verdict, ctx) {
34
+ return actuate({
35
+ verdict,
36
+ mcp: ctx.host.mcp,
37
+ tracker,
38
+ purpose: ctx.session.purpose,
39
+ issueKey: ctx.session.continuationKey,
40
+ });
22
41
  },
23
42
  });
@@ -1,4 +1,5 @@
1
1
  import { defineEval, includes } from "@cursor/july/evals";
2
+ import { formatQueuePrompt } from "../agent/lib/parse-queue-event.js";
2
3
 
3
4
  export default defineEval({
4
5
  tags: ["smoke"],
@@ -8,7 +9,14 @@ export default defineEval({
8
9
  description: "A concrete failure is recorded as a bug.",
9
10
  async test(t) {
10
11
  await t.send(
11
- "Export to CSV has failed with a 500 since this morning. Request id 41ac. Retrying does not help."
12
+ formatQueuePrompt({
13
+ identifier: "TRI-101",
14
+ title: "CSV export returns 500",
15
+ description:
16
+ "Export to CSV has failed with a 500 since this morning. Request id 41ac. Retrying does not help.",
17
+ url: "https://linear.app/acme/issue/TRI-101",
18
+ reporter: "Sam",
19
+ })
12
20
  );
13
21
  t.succeeded();
14
22
  t.calledTool("record_triage");
@@ -20,7 +28,14 @@ export default defineEval({
20
28
  description: "A how-does-it-work ask is recorded as a question.",
21
29
  async test(t) {
22
30
  await t.send(
23
- "How do I rotate the API keys for the staging environment?"
31
+ formatQueuePrompt({
32
+ identifier: "TRI-102",
33
+ title: "Rotate staging API keys",
34
+ description:
35
+ "How do I rotate the API keys for the staging environment?",
36
+ url: "https://linear.app/acme/issue/TRI-102",
37
+ reporter: "Morgan",
38
+ })
24
39
  );
25
40
  t.succeeded();
26
41
  t.calledTool("record_triage");
@@ -32,7 +47,14 @@ export default defineEval({
32
47
  description: "A suggestion is recorded as feedback.",
33
48
  async test(t) {
34
49
  await t.send(
35
- "The onboarding wizard has too many steps. You should collapse the profile pages into one."
50
+ formatQueuePrompt({
51
+ identifier: "TRI-103",
52
+ title: "Collapse onboarding profile pages",
53
+ description:
54
+ "The onboarding wizard has too many steps. You should collapse the profile pages into one.",
55
+ url: "https://linear.app/acme/issue/TRI-103",
56
+ reporter: "Riley",
57
+ })
36
58
  );
37
59
  t.succeeded();
38
60
  t.calledTool("record_triage");
@@ -0,0 +1,21 @@
1
+ # Triage agent for Jira
2
+
3
+ Comments on existing Jira issues. Scaffold with:
4
+
5
+ ```bash
6
+ npx @cursor/july init ./my-triage-agent --template triage-jira
7
+ ```
8
+
9
+ Connect Jira in Cursor, then:
10
+
11
+ ```bash
12
+ agent-sdk login
13
+ agent-sdk dev
14
+ ```
15
+
16
+ Paste an issue identifier in the playground. To receive Jira
17
+ `issue_created` webhooks, self-host, set `JIRA_WEBHOOK_SECRET`, and
18
+ point the webhook at `POST /<slug>/v1/channels/webhook`.
19
+
20
+ Edit `agent/skills/triage-rubric.md` for your severity ladder.
21
+ `agent-sdk eval` skips Jira writes.
@@ -0,0 +1,8 @@
1
+ import type { TrackerConfig } from "./types.js";
2
+
3
+ const tracker: TrackerConfig = {
4
+ kind: "jira",
5
+ connectionName: "tracker",
6
+ };
7
+
8
+ export default tracker;
@@ -0,0 +1,7 @@
1
+ import { defineConnection } from "@cursor/july/connections";
2
+
3
+ export default defineConnection({
4
+ cursorAccount: true,
5
+ servers: ["Jira"],
6
+ hostOnly: true,
7
+ });
@@ -0,0 +1,21 @@
1
+ # Triage agent for Linear
2
+
3
+ Comments on existing Linear issues. Scaffold with:
4
+
5
+ ```bash
6
+ npx @cursor/july init ./my-triage-agent --template triage-linear
7
+ ```
8
+
9
+ Connect Linear in Cursor, then:
10
+
11
+ ```bash
12
+ agent-sdk login
13
+ agent-sdk dev
14
+ ```
15
+
16
+ Paste an issue identifier in the playground. To receive Linear Issue
17
+ create webhooks, self-host, set `LINEAR_WEBHOOK_SECRET`, and point the
18
+ webhook at `POST /<slug>/v1/channels/webhook`.
19
+
20
+ Edit `agent/skills/triage-rubric.md` for your severity ladder.
21
+ `agent-sdk eval` skips Linear writes.
@@ -0,0 +1,8 @@
1
+ import type { TrackerConfig } from "./types.js";
2
+
3
+ const tracker: TrackerConfig = {
4
+ kind: "linear",
5
+ connectionName: "tracker",
6
+ };
7
+
8
+ export default tracker;
@@ -0,0 +1,7 @@
1
+ import { defineConnection } from "@cursor/july/connections";
2
+
3
+ export default defineConnection({
4
+ cursorAccount: true,
5
+ servers: ["Linear", "Linear MCP"],
6
+ hostOnly: true,
7
+ });
@@ -2,7 +2,7 @@
2
2
  "name": "triage-agent",
3
3
  "private": true,
4
4
  "type": "module",
5
- "description": "Triage agent: classifies bug reports, questions, and feedback into structured verdicts.",
5
+ "description": "Comments on existing Linear or Jira issues.",
6
6
  "scripts": {
7
7
  "check": "tsc --noEmit",
8
8
  "dev": "agent-sdk dev"