@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,31 @@
1
+ # Security reviewer
2
+
3
+ Reviews pull requests for exploitable bugs. Posts one review comment.
4
+
5
+ ```bash
6
+ npx @cursor/july init ./security-reviewer --template security-reviewer
7
+ ```
8
+
9
+ `init` asks which repos to watch. Skip the prompt with
10
+ `--var repos=owner/repo`.
11
+
12
+ ## Run
13
+
14
+ Connect GitHub in Cursor for those repos. Then sign the host in:
15
+
16
+ ```bash
17
+ agent-sdk login
18
+ agent-sdk dev
19
+ ```
20
+
21
+ Events and API calls use your Cursor GitHub connection. Replay a PR:
22
+
23
+ ```bash
24
+ agent-sdk github replay https://github.com/owner/repo/pull/1 --dir .
25
+ ```
26
+
27
+ ## Files
28
+
29
+ - `agent/lib/repos.ts`: watched repos
30
+ - `agent/instructions.md`: what counts as a finding
31
+ - `evals/review.eval.ts`: two smoke cases
@@ -0,0 +1,15 @@
1
+ import { defineAgent } from "@cursor/july";
2
+
3
+ export default defineAgent({
4
+ model: {
5
+ id: "grok-4.5",
6
+ params: [
7
+ { id: "effort", value: "high" },
8
+ { id: "fast", value: "true" },
9
+ ],
10
+ },
11
+ tools: ["read", "grep"],
12
+ local: {
13
+ sandbox: true,
14
+ },
15
+ });
@@ -0,0 +1,46 @@
1
+ import {
2
+ defaultGitHubPullRequestDispatch,
3
+ githubChannel,
4
+ } from "@cursor/july/channels/github";
5
+ import { REPOS, watches } from "../lib/repos.js";
6
+
7
+ const MAX_DIFF_CHARS = 150_000;
8
+
9
+ export default githubChannel({
10
+ cursorAccount: { repos: REPOS },
11
+ deliverReplies: false,
12
+ onComment: () => null,
13
+ onPullRequest: (ctx, pr) => {
14
+ if (!watches(ctx.repository.fullName) || pr.draft) {
15
+ return null;
16
+ }
17
+ const dispatch = defaultGitHubPullRequestDispatch(ctx, pr);
18
+ if (dispatch === null) {
19
+ return null;
20
+ }
21
+ return {
22
+ auth: dispatch.auth,
23
+ context: ["Read pr/diff.patch."],
24
+ async workspaceFiles() {
25
+ const octokit = await ctx.github.getOctokit();
26
+ const pull = {
27
+ owner: ctx.repository.owner,
28
+ repo: ctx.repository.name,
29
+ pull_number: pr.number,
30
+ };
31
+ const [meta, diff] = await Promise.all([
32
+ octokit.rest.pulls.get(pull),
33
+ octokit.rest.pulls.get({ ...pull, mediaType: { format: "diff" } }),
34
+ ]);
35
+ const text = String(diff.data);
36
+ return {
37
+ "pr/diff.patch":
38
+ text.length > MAX_DIFF_CHARS
39
+ ? `${text.slice(0, MAX_DIFF_CHARS)}\n…[truncated]\n`
40
+ : text,
41
+ "pr/head.sha": meta.data.head.sha,
42
+ };
43
+ },
44
+ };
45
+ },
46
+ });
@@ -0,0 +1,22 @@
1
+ # Security reviewer
2
+
3
+ You review pull requests for exploitable security bugs.
4
+
5
+ ## Review
6
+
7
+ The workspace has `pr/diff.patch`. Read it. Report only exploitable
8
+ issues: injection, authz bypass, secret leak, unsafe deserialization,
9
+ SSRF, RCE. Skip style, tests, and hypotheticals.
10
+
11
+ Call `post_findings` once with structured findings. Reply with the
12
+ count and one line per finding. Clean review: empty findings.
13
+
14
+ The host writes the GitHub comment. Verdict is CLEAN when the list
15
+ is empty.
16
+
17
+ ## Finding
18
+
19
+ - `severity`: `high`, `medium`, or `low`
20
+ - `file`, `line`: path and line in the diff
21
+ - `title`: short name
22
+ - `detail`: one-line impact and fix
@@ -0,0 +1,5 @@
1
+ export const REPOS: readonly string[] = ["owner/repo"];
2
+
3
+ export function watches(fullName: string): boolean {
4
+ return REPOS.some((repo) => repo.toLowerCase() === fullName.toLowerCase());
5
+ }
@@ -0,0 +1,72 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { parseGitHubPrRef } from "@cursor/july/channels/github";
4
+ import { defineTool } from "@cursor/july/tools";
5
+ import { z } from "zod";
6
+
7
+ const SHA = /^[0-9a-f]{40}$/i;
8
+
9
+ const finding = z.object({
10
+ severity: z.enum(["high", "medium", "low"]),
11
+ file: z.string().min(1).max(240),
12
+ line: z.number().int().positive(),
13
+ title: z.string().min(1).max(160),
14
+ detail: z.string().min(1).max(280),
15
+ });
16
+
17
+ type Finding = z.infer<typeof finding>;
18
+
19
+ export default defineTool({
20
+ description:
21
+ "Post the security review on this session's pull request. Call once. Empty findings = CLEAN.",
22
+ inputSchema: z.object({
23
+ findings: z
24
+ .array(finding)
25
+ .describe("Exploitable issues. Empty for CLEAN."),
26
+ }),
27
+ async execute({ findings }, ctx) {
28
+ if (ctx.session.purpose === "eval") {
29
+ return { posted: false, reason: "eval" };
30
+ }
31
+ const key = ctx.session.continuationKey ?? "";
32
+ if (!key.startsWith("github-pr:")) {
33
+ throw new Error("post_findings requires a GitHub pull request session");
34
+ }
35
+ const ref = parseGitHubPrRef(key.slice("github-pr:".length));
36
+ if (ref === undefined) {
37
+ throw new Error(`unrecognized PR: ${key}`);
38
+ }
39
+ const headSha = (
40
+ await readFile(join(ctx.workspaceDir, "pr/head.sha"), "utf8")
41
+ ).trim();
42
+ if (!SHA.test(headSha)) {
43
+ throw new Error("missing pr/head.sha");
44
+ }
45
+ const octokit = await ctx.host.github.getOctokit();
46
+ const response = await octokit.rest.pulls.createReview({
47
+ owner: ref.owner,
48
+ repo: ref.repo,
49
+ pull_number: ref.number,
50
+ commit_id: headSha,
51
+ body: reviewBody(findings),
52
+ event: "COMMENT",
53
+ });
54
+ return { posted: true, pr: ref.label, url: response.data.html_url };
55
+ },
56
+ });
57
+
58
+ function flatten(value: string): string {
59
+ return value.replace(/\s+/g, " ").trim();
60
+ }
61
+
62
+ function reviewBody(findings: Finding[]): string {
63
+ const verdict = findings.length === 0 ? "CLEAN" : "FINDINGS";
64
+ const lines = ["## Security review", "", `**Verdict:** ${verdict}`];
65
+ for (const item of findings) {
66
+ lines.push(
67
+ `- **${item.severity}** \`${flatten(item.file)}:${item.line}\` — ${flatten(item.title)}`
68
+ );
69
+ lines.push(` ${flatten(item.detail)}`);
70
+ }
71
+ return lines.join("\n");
72
+ }
@@ -0,0 +1,5 @@
1
+ import { defineEvalConfig } from "@cursor/july/evals";
2
+
3
+ export default defineEvalConfig({
4
+ maxConcurrency: 10,
5
+ });
@@ -0,0 +1,53 @@
1
+ import { defineEval, includes } from "@cursor/july/evals";
2
+
3
+ const OPENED = [
4
+ "GitHub pull request trigger: acme/api#12",
5
+ "Action: opened",
6
+ "PR: https://github.com/acme/api/pull/12",
7
+ "Title: Search results",
8
+ "Head: feat/search (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)",
9
+ "Base: main",
10
+ "Author: sam",
11
+ "",
12
+ "Read pr/diff.patch.",
13
+ ].join("\n");
14
+
15
+ export default defineEval({
16
+ tags: ["smoke"],
17
+ cases: [
18
+ {
19
+ id: "xss",
20
+ description: "Unescaped query reflection is posted as a finding.",
21
+ async test(t) {
22
+ await t.send(OPENED, {
23
+ workspaceFiles: {
24
+ "pr/diff.patch": [
25
+ "diff --git a/app/routes/search.ts b/app/routes/search.ts",
26
+ "+res.send(`<h1>Results for ${req.query.q}</h1>`);",
27
+ ].join("\n"),
28
+ },
29
+ });
30
+ t.succeeded();
31
+ t.calledTool("post_findings");
32
+ t.check(t.reply, includes(/xss|cross-site|injection|finding/i));
33
+ },
34
+ },
35
+ {
36
+ id: "clean",
37
+ description: "A docs-only change is posted as clean.",
38
+ async test(t) {
39
+ await t.send(OPENED.replace("Search results", "Docs"), {
40
+ workspaceFiles: {
41
+ "pr/diff.patch": [
42
+ "diff --git a/README.md b/README.md",
43
+ "+# API",
44
+ ].join("\n"),
45
+ },
46
+ });
47
+ t.succeeded();
48
+ t.calledTool("post_findings");
49
+ t.check(t.reply, includes(/clean|no findings|no vulnerabilities/i));
50
+ },
51
+ },
52
+ ],
53
+ });
@@ -0,0 +1,25 @@
1
+ {
2
+ "questions": [
3
+ {
4
+ "id": "repos",
5
+ "prompt": "Which GitHub repos should this agent review?",
6
+ "hint": "Comma-separated owner/repo. One GitHub owner.",
7
+ "placeholder": "acme/api, acme/web",
8
+ "parse": "owner-repo-list"
9
+ }
10
+ ],
11
+ "replace": [
12
+ {
13
+ "file": "agent/lib/repos.ts",
14
+ "find": "[\"owner/repo\"]",
15
+ "answer": "repos",
16
+ "as": "json-array"
17
+ },
18
+ {
19
+ "file": "README.md",
20
+ "find": "owner/repo",
21
+ "answer": "repos",
22
+ "as": "first"
23
+ }
24
+ ]
25
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "security-reviewer",
3
+ "private": true,
4
+ "type": "module",
5
+ "description": "Reviews pull requests for exploitable security bugs.",
6
+ "scripts": {
7
+ "check": "tsc --noEmit",
8
+ "dev": "agent-sdk dev"
9
+ },
10
+ "dependencies": {
11
+ "@cursor/july": "latest",
12
+ "zod": "^3.25.0"
13
+ },
14
+ "devDependencies": {
15
+ "@types/node": "^22.14.0",
16
+ "typescript": "^5.8.0"
17
+ }
18
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "noEmit": true,
8
+ "skipLibCheck": true,
9
+ "types": ["node"]
10
+ },
11
+ "include": ["agent/**/*", "evals/**/*"]
12
+ }
@@ -1,48 +1,46 @@
1
1
  # Triage agent
2
2
 
3
- A triage front door for your team. Send it bug reports, questions, and
4
- feedback; it classifies each report against a rubric, judges severity,
5
- records a structured verdict, and replies with the next step.
6
-
7
- Scaffolded with:
3
+ Comments on existing Linear or Jira issues. Classifies the ticket, writes
4
+ the next step, links a duplicate, and sets bug priority when the
5
+ connector can.
8
6
 
9
7
  ```bash
10
- npx @cursor/july init ./my-triage-agent --template triage
8
+ npx @cursor/july init ./my-triage-agent --template triage-linear
9
+ npx @cursor/july init ./my-triage-agent --template triage-jira
11
10
  ```
12
11
 
13
- ## Run it
12
+ ## Run
13
+
14
+ Connect the tracker in Cursor.
14
15
 
15
16
  ```bash
16
- agent-sdk login # once per machine
17
+ agent-sdk login
17
18
  agent-sdk dev
18
19
  ```
19
20
 
20
- Open the playground URL it prints. Chat directly, or use the `/report`
21
- slash command to submit a report the way an integration would
22
- (`POST /v1/channels/intake/report`).
21
+ In the playground, paste an issue identifier or run `/queue/issue`.
23
22
 
24
- ## Wire it to Slack
23
+ ```text
24
+ POST /<slug>/v1/channels/queue/issue
25
+ ```
25
26
 
26
- The Slack channel idles until its tokens exist. Create a Slack app and
27
- its env with:
27
+ Hosted Cursor aliases require `X-Agent-Alias-Token`. Linear and Jira
28
+ cannot send that header, so point create webhooks at a self-hosted
29
+ agent instead:
28
30
 
29
- ```bash
30
- agent-sdk slack setup
31
+ ```text
32
+ POST /<slug>/v1/channels/webhook
31
33
  ```
32
34
 
33
- Then @mention the bot or DM it. The setup-slack skill in the
34
- @cursor/july package has the full walkthrough.
35
+ Set `LINEAR_WEBHOOK_SECRET` or `JIRA_WEBHOOK_SECRET` to the signing
36
+ secret from the tracker. The identifier is the continuation key.
37
+ Questions and feedback get comments. A duplicate match gets a comment
38
+ on both issues. `agent-sdk eval` classifies and skips tracker writes.
35
39
 
36
- ## Make it yours
40
+ ## Files to edit
37
41
 
38
- - `agent/instructions.md` owns the flow every report follows.
39
- - `agent/skills/triage-rubric.md` owns the kinds, the tie-breakers, and
40
- the severity ladder. Edit this first; it is the judgment.
41
- - `agent/tools/record_triage.ts` owns the verdict shape. Point its
42
- `execute` at your tracker (Linear, GitHub Issues, a webhook) to file
43
- real tickets.
44
- - `evals/triage.eval.ts` holds three smoke cases. Run `agent-sdk eval`
45
- after every rubric change.
42
+ - `agent/skills/triage-rubric.md`: kinds, tie-breakers, severity
43
+ - `agent/instructions.md`: procedure around the rubric
44
+ - `evals/triage.eval.ts`: three smoke cases
46
45
 
47
- The full walkthrough, from scaffold through deploy, is the triage
48
- cookbook: run `agent-sdk docs` and open Templates, then Triage agent.
46
+ `agent/lib/actuate.ts` talks to the tracker. The connection is host-only.
@@ -8,6 +8,7 @@ export default defineAgent({
8
8
  { id: "fast", value: "true" },
9
9
  ],
10
10
  },
11
+ tools: ["read", "grep", "glob", "ls"],
11
12
  local: {
12
13
  sandbox: true,
13
14
  },
@@ -0,0 +1,47 @@
1
+ import { defineChannel, POST } from "@cursor/july/channels";
2
+ import { z } from "zod";
3
+ import {
4
+ formatQueuePrompt,
5
+ isTrackerWebhook,
6
+ parseQueueEvent,
7
+ } from "../lib/parse-queue-event.js";
8
+
9
+ export default defineChannel({
10
+ routes: [
11
+ POST("/issue", {
12
+ description: "Triage an existing tracker issue",
13
+ bodySchema: z.unknown(),
14
+ handler: async (_req, { send, waitUntil, body }) => {
15
+ const parsed = parseQueueEvent(body);
16
+ if (!parsed.ok) {
17
+ if ("ignored" in parsed) {
18
+ return Response.json({ ok: true, ignored: true });
19
+ }
20
+ return Response.json(
21
+ { ok: false, error: parsed.error },
22
+ { status: 400 }
23
+ );
24
+ }
25
+ const options = {
26
+ continuationToken: parsed.issue.identifier,
27
+ admission: "coalesce" as const,
28
+ ...(parsed.issue.title === "" ? {} : { title: parsed.issue.title }),
29
+ };
30
+ const prompt = formatQueuePrompt(parsed.issue);
31
+ if (isTrackerWebhook(body)) {
32
+ waitUntil(send(prompt, options));
33
+ return Response.json({
34
+ ok: true,
35
+ identifier: parsed.issue.identifier,
36
+ });
37
+ }
38
+ const session = await send(prompt, options);
39
+ return Response.json({
40
+ ok: true,
41
+ sessionId: session.id,
42
+ identifier: parsed.issue.identifier,
43
+ });
44
+ },
45
+ }),
46
+ ],
47
+ });
@@ -0,0 +1,50 @@
1
+ import { allowAll, defineChannel, POST } from "@cursor/july/channels";
2
+ import { z } from "zod";
3
+ import {
4
+ formatQueuePrompt,
5
+ parseQueueEvent,
6
+ } from "../lib/parse-queue-event.js";
7
+ import { verifyTrackerWebhook } from "../lib/verify-webhook.js";
8
+
9
+ export default defineChannel({
10
+ auth: [allowAll()],
11
+ routes: [
12
+ POST("/", {
13
+ description: "Signed Linear or Jira issue-created webhook",
14
+ bodySchema: z.unknown(),
15
+ handler: async (request, { send, waitUntil, body }) => {
16
+ const rawBody = await request.text();
17
+ const verified = verifyTrackerWebhook({
18
+ rawBody,
19
+ linearSignature: request.headers.get("linear-signature"),
20
+ hubSignature:
21
+ request.headers.get("x-hub-signature-256") ??
22
+ request.headers.get("x-hub-signature"),
23
+ linearSecret: process.env.LINEAR_WEBHOOK_SECRET,
24
+ jiraSecret: process.env.JIRA_WEBHOOK_SECRET,
25
+ nowMs: Date.now(),
26
+ });
27
+ if (!verified.ok) {
28
+ return Response.json({ ok: false }, { status: verified.status });
29
+ }
30
+ const parsed = parseQueueEvent(body);
31
+ if (!parsed.ok) {
32
+ return Response.json({ ok: true, ignored: true });
33
+ }
34
+ waitUntil(
35
+ send(formatQueuePrompt(parsed.issue), {
36
+ continuationToken: parsed.issue.identifier,
37
+ admission: "coalesce",
38
+ ...(parsed.issue.title === ""
39
+ ? {}
40
+ : { title: parsed.issue.title }),
41
+ })
42
+ );
43
+ return Response.json({
44
+ ok: true,
45
+ identifier: parsed.issue.identifier,
46
+ });
47
+ },
48
+ }),
49
+ ],
50
+ });
@@ -1,27 +1,38 @@
1
1
  # Triage agent
2
2
 
3
- You are the triage front door for this team. People send you reports:
4
- bug reports, questions, feedback, feature ideas. You classify each one,
5
- judge its severity, and hand back a verdict the team can act on.
3
+ You triage an existing Linear or Jira issue. The prompt includes its
4
+ identifier. Classify the issue, judge its severity, and record a verdict
5
+ that helps the team act on it.
6
6
 
7
7
  Read the `triage-rubric` skill before classifying. It defines the three
8
8
  kinds and the tie-breakers.
9
9
 
10
- ## Every report
10
+ ## Every issue
11
11
 
12
- 1. Read the report. If it is too thin to classify (no symptom, no
13
- question, no opinion), ask for the missing piece once. Otherwise do
14
- not ask; classify with what you have.
15
- 2. If a `memory/` directory exists in your workspace, grep the journal
16
- for similar past reports and say when one matches.
17
- 3. Call `record_triage` exactly once with your verdict.
18
- 4. Reply with the verdict: kind, severity, a one-line reason, and the
19
- next step. Keep the reply under six lines. No preamble.
12
+ 1. Read the identifier, title, description, URL, and reporter from the
13
+ prompt. Never invent missing fields.
14
+ 2. If the issue is too thin to classify, set `needsInfo` to true. Put the
15
+ exact missing detail in `nextStep`, then ask for it once in your reply.
16
+ Otherwise classify with what you have.
17
+ 3. If a `memory/` directory exists in your workspace, grep the journal
18
+ for similar issues. Set `duplicateOf` to the matching identifier when
19
+ the evidence is strong.
20
+ 4. Call `record_triage` exactly once. Pass the identifier from the
21
+ prompt with your verdict. The tool comments on the existing issue,
22
+ records duplicates, and sets bug priority when supported.
23
+ 5. Reply with the identifier, kind, severity, one-line reason, and next
24
+ step. If the tracker is not connected, tell the user to connect
25
+ Linear or Jira in Cursor. Keep the reply under eight lines. No
26
+ preamble.
20
27
 
21
28
  ## Boundaries
22
29
 
23
30
  - You classify and route. You do not fix bugs, write code, or promise
24
31
  timelines.
25
- - When a report mixes kinds, pick the dominant one and name the split
32
+ - You never create or file an issue. Native Slack @Cursor handles ticket
33
+ creation. This agent only enriches the issue in the prompt.
34
+ - Questions and feedback still go through `record_triage` so the issue
35
+ receives a comment.
36
+ - When an issue mixes kinds, pick the dominant one and name the split
26
37
  in your reason.
27
38
  - Never invent reporter details. Missing fields stay empty.