@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.
- package/dist/bin/agent-serve.js +30 -23
- package/dist/channels/github/github-channel.d.ts +12 -0
- package/dist/channels/github/github-channel.d.ts.map +1 -1
- package/dist/channels/github/github-channel.js +67 -15
- package/dist/channels/github/types.d.ts +27 -1
- package/dist/channels/github/types.d.ts.map +1 -1
- package/dist/channels/origin/cursor-account.d.ts +22 -0
- package/dist/channels/origin/cursor-account.d.ts.map +1 -0
- package/dist/channels/origin/cursor-account.js +35 -0
- package/dist/channels/origin/events.d.ts +10 -0
- package/dist/channels/origin/events.d.ts.map +1 -0
- package/dist/channels/origin/events.js +27 -0
- package/dist/channels/origin/index.d.ts +29 -0
- package/dist/channels/origin/index.d.ts.map +1 -0
- package/dist/channels/origin/index.js +28 -0
- package/dist/channels/origin/origin-channel.d.ts +68 -0
- package/dist/channels/origin/origin-channel.d.ts.map +1 -0
- package/dist/channels/origin/origin-channel.js +567 -0
- package/dist/channels/origin/origin-webhook.d.ts +38 -0
- package/dist/channels/origin/origin-webhook.d.ts.map +1 -0
- package/dist/channels/origin/origin-webhook.js +151 -0
- package/dist/channels/origin/types.d.ts +152 -0
- package/dist/channels/origin/types.d.ts.map +1 -0
- package/dist/channels/origin/types.js +1 -0
- package/dist/channels/slack/doctor.d.ts +17 -1
- package/dist/channels/slack/doctor.d.ts.map +1 -1
- package/dist/channels/slack/doctor.js +186 -10
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +4 -4
- package/dist/docs/assets/{app.DAKcDclS.js → app.CW3KJh72.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.BuymfBAo.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.BNIaCSfG.js → VPLocalSearchBox.Bk6EWpPj.js} +1 -1
- package/dist/docs/assets/chunks/{theme.B47GzJjc.js → theme.Bz9AodBA.js} +2 -2
- package/dist/docs/assets/{example-agents_fsd.md.DPz9ezO4.js → example-agents_fsd.md.ZeGEpAw_.js} +2 -2
- package/dist/docs/assets/{example-agents_fsd.md.DPz9ezO4.lean.js → example-agents_fsd.md.ZeGEpAw_.lean.js} +1 -1
- package/dist/docs/assets/{example-agents_security-reviewer.md.Dkf1gyo6.js → example-agents_security-reviewer.md.DBL1TwtP.js} +2 -2
- package/dist/docs/assets/{example-agents_security-reviewer.md.Dkf1gyo6.lean.js → example-agents_security-reviewer.md.DBL1TwtP.lean.js} +1 -1
- package/dist/docs/assets/guides_convert-automation.md.BhMzTkE5.js +12 -0
- package/dist/docs/assets/guides_convert-automation.md.BhMzTkE5.lean.js +1 -0
- package/dist/docs/assets/{guides_github.md.B8QKtIpw.js → guides_github.md.CLLQJKRB.js} +4 -4
- package/dist/docs/assets/{guides_github.md.B8QKtIpw.lean.js → guides_github.md.CLLQJKRB.lean.js} +1 -1
- package/dist/docs/assets/index.md.DhRHS_-L.js +5 -0
- package/dist/docs/assets/{index.md.COeoSkcF.lean.js → index.md.DhRHS_-L.lean.js} +1 -1
- package/dist/docs/assets/{reference_cli.md.-V6J7Cl8.js → reference_cli.md.Cl14Jfo3.js} +10 -2
- package/dist/docs/assets/{reference_cli.md.-V6J7Cl8.lean.js → reference_cli.md.Cl14Jfo3.lean.js} +1 -1
- package/dist/docs/assets/scaffolding-agents.md.CrWSA0Ko.js +1 -0
- package/dist/docs/assets/{scaffolding-agents.md.mkc3B_ZW.lean.js → scaffolding-agents.md.CrWSA0Ko.lean.js} +1 -1
- package/dist/docs/assets/templates_agentic-owners.md.BkTLORaU.js +14 -0
- package/dist/docs/assets/templates_agentic-owners.md.BkTLORaU.lean.js +1 -0
- package/dist/docs/assets/templates_demo.md.Bgd6MBaZ.js +4 -0
- package/dist/docs/assets/templates_demo.md.Bgd6MBaZ.lean.js +1 -0
- package/dist/docs/assets/templates_pr-autofixer.md.DcmoeUNZ.js +8 -0
- package/dist/docs/assets/templates_pr-autofixer.md.DcmoeUNZ.lean.js +1 -0
- package/dist/docs/assets/templates_security-reviewer.md.C0yIUaYs.js +2 -0
- package/dist/docs/assets/templates_security-reviewer.md.C0yIUaYs.lean.js +1 -0
- package/dist/docs/assets/templates_triage.md.DWuQ1bZz.js +9 -0
- package/dist/docs/assets/templates_triage.md.DWuQ1bZz.lean.js +1 -0
- package/dist/docs/building-with-agents.html +4 -4
- package/dist/docs/concepts.html +5 -5
- package/dist/docs/deployment.html +4 -4
- package/dist/docs/evals.html +4 -4
- package/dist/docs/example-agents/approval-buddy.html +4 -4
- package/dist/docs/example-agents/benny.html +4 -4
- package/dist/docs/example-agents/bugbot.html +4 -4
- package/dist/docs/example-agents/codebase-wiki.html +4 -4
- package/dist/docs/example-agents/codeowners-review.html +4 -4
- package/dist/docs/example-agents/concierge.html +4 -4
- package/dist/docs/example-agents/fsd.html +5 -5
- package/dist/docs/example-agents/index.html +4 -4
- package/dist/docs/example-agents/knowledge-base.html +4 -4
- package/dist/docs/example-agents/oncall.html +4 -4
- package/dist/docs/example-agents/security-reviewer.html +5 -5
- package/dist/docs/example-agents/slack-agent.html +4 -4
- package/dist/docs/example-agents/weather-agent.html +4 -4
- package/dist/docs/guides/agent-to-agent.html +4 -4
- package/dist/docs/guides/cloud-runtime.html +4 -4
- package/dist/docs/guides/convert-automation.html +36 -0
- package/dist/docs/guides/github.html +7 -7
- package/dist/docs/guides/human-in-the-loop.html +4 -4
- package/dist/docs/guides/mcp-oauth.html +4 -4
- package/dist/docs/guides/opentelemetry.html +4 -4
- package/dist/docs/guides/slack.html +4 -4
- package/dist/docs/guides/webhooks.html +4 -4
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +4 -4
- package/dist/docs/index.html +6 -6
- package/dist/docs/quickstart.html +4 -4
- package/dist/docs/reference/agent-config.html +4 -4
- package/dist/docs/reference/artifacts.html +4 -4
- package/dist/docs/reference/channels.html +4 -4
- package/dist/docs/reference/cli.html +14 -6
- package/dist/docs/reference/connections.html +4 -4
- package/dist/docs/reference/hooks.html +4 -4
- package/dist/docs/reference/http-api.html +4 -4
- package/dist/docs/reference/instructions.html +4 -4
- package/dist/docs/reference/playground.html +4 -4
- package/dist/docs/reference/project-layout.html +4 -4
- package/dist/docs/reference/prompt.html +4 -4
- package/dist/docs/reference/schedules.html +4 -4
- package/dist/docs/reference/sessions.html +4 -4
- package/dist/docs/reference/skills.html +4 -4
- package/dist/docs/reference/subagents.html +4 -4
- package/dist/docs/reference/tools.html +4 -4
- package/dist/docs/scaffolding-agents.html +5 -5
- package/dist/docs/storage.html +4 -4
- package/dist/docs/templates/agentic-owners.html +38 -0
- package/dist/docs/templates/demo.html +28 -0
- package/dist/docs/templates/pr-autofixer.html +32 -0
- package/dist/docs/templates/security-reviewer.html +26 -0
- package/dist/docs/templates/triage.html +14 -16
- package/dist/docs/troubleshooting.html +4 -4
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/internal/cli-ax.d.ts +2 -0
- package/dist/internal/cli-ax.d.ts.map +1 -1
- package/dist/internal/cli-ax.js +30 -1
- package/dist/internal/cli-deploy.js +1 -1
- package/dist/internal/cli-slack.d.ts +4 -0
- package/dist/internal/cli-slack.d.ts.map +1 -1
- package/dist/internal/cli-slack.js +46 -20
- package/dist/internal/cursor-event-relay.d.ts +59 -11
- package/dist/internal/cursor-event-relay.d.ts.map +1 -1
- package/dist/internal/cursor-event-relay.js +133 -26
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +11 -2
- package/dist/internal/distribution.d.ts.map +1 -1
- package/dist/internal/distribution.js +1 -0
- package/dist/internal/handleAgentServeTrigger.d.ts +33 -6
- package/dist/internal/handleAgentServeTrigger.d.ts.map +1 -1
- package/dist/internal/handleAgentServeTrigger.js +97 -19
- package/dist/internal/init-interview.d.ts +67 -0
- package/dist/internal/init-interview.d.ts.map +1 -0
- package/dist/internal/init-interview.js +300 -0
- package/dist/internal/init-project.d.ts +7 -0
- package/dist/internal/init-project.d.ts.map +1 -1
- package/dist/internal/init-project.js +99 -43
- package/dist/internal/mcp-endpoint.d.ts +7 -0
- package/dist/internal/mcp-endpoint.d.ts.map +1 -1
- package/dist/internal/mcp-endpoint.js +7 -0
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +159 -57
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +20 -21
- package/dist/internal/session-links.d.ts +14 -0
- package/dist/internal/session-links.d.ts.map +1 -0
- package/dist/internal/session-links.js +69 -0
- package/dist/internal/sessions-client.d.ts.map +1 -1
- package/dist/internal/sessions-client.js +1 -0
- package/dist/internal/slack-provision-client.js +1 -1
- package/dist/playground/assets/index-ClbuaLpm.css +1 -0
- package/dist/playground/assets/index-SqkR13k_.js +85 -0
- package/dist/playground/index.html +2 -2
- package/dist/types.d.ts +19 -12
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +15 -0
- package/docs/example-agents/fsd.md +3 -0
- package/docs/example-agents/security-reviewer.md +3 -0
- package/docs/guides/convert-automation.md +176 -0
- package/docs/guides/github.md +6 -2
- package/docs/reference/cli.md +36 -10
- package/docs/scaffolding-agents.md +3 -0
- package/docs/templates/agentic-owners.md +97 -0
- package/docs/templates/demo.md +84 -0
- package/docs/templates/pr-autofixer.md +136 -0
- package/docs/templates/security-reviewer.md +89 -0
- package/docs/templates/triage.md +59 -101
- package/package.json +9 -1
- package/skills/github/SKILL.md +4 -5
- package/src/bin/agent-serve.ts +13 -6
- package/src/channels/github/github-channel.ts +95 -18
- package/src/channels/github/types.ts +31 -0
- package/src/channels/origin/cursor-account.ts +64 -0
- package/src/channels/origin/events.ts +41 -0
- package/src/channels/origin/index.ts +29 -0
- package/src/channels/origin/origin-channel.ts +784 -0
- package/src/channels/origin/origin-webhook.ts +202 -0
- package/src/channels/origin/types.ts +186 -0
- package/src/channels/slack/doctor.ts +218 -5
- package/src/index.ts +3 -1
- package/src/internal/cli-ax.ts +33 -1
- package/src/internal/cli-deploy.ts +2 -2
- package/src/internal/cli-slack.ts +68 -19
- package/src/internal/cursor-event-relay.ts +193 -31
- package/src/internal/discovery.ts +15 -5
- package/src/internal/distribution.ts +1 -0
- package/src/internal/handleAgentServeTrigger.ts +129 -19
- package/src/internal/init-interview.ts +384 -0
- package/src/internal/init-project.ts +146 -46
- package/src/internal/mcp-endpoint.ts +7 -0
- package/src/internal/server.ts +188 -58
- package/src/internal/session-engine.ts +5 -21
- package/src/internal/session-links.ts +88 -0
- package/src/internal/sessions-client.ts +1 -0
- package/src/internal/slack-provision-client.ts +1 -1
- package/src/types.ts +19 -12
- package/templates/agentic-owners/README.md +73 -0
- package/templates/agentic-owners/agent/agent.ts +15 -0
- package/templates/agentic-owners/agent/channels/github.ts +85 -0
- package/templates/agentic-owners/agent/instructions.md +34 -0
- package/templates/agentic-owners/agent/lib/config.ts +13 -0
- package/templates/agentic-owners/agent/lib/github.ts +241 -0
- package/templates/agentic-owners/agent/lib/owners/README.md +50 -0
- package/templates/agentic-owners/agent/lib/owners/constants.ts +22 -0
- package/templates/agentic-owners/agent/lib/owners/decision.ts +485 -0
- package/templates/agentic-owners/agent/lib/owners/handles.ts +54 -0
- package/templates/agentic-owners/agent/lib/owners/match.ts +177 -0
- package/templates/agentic-owners/agent/lib/owners/matching.ts +95 -0
- package/templates/agentic-owners/agent/lib/owners/parse.ts +589 -0
- package/templates/agentic-owners/agent/lib/owners/policyMode.ts +65 -0
- package/templates/agentic-owners/agent/lib/owners/prompt.ts +66 -0
- package/templates/agentic-owners/agent/lib/owners/prompts/base-review.md +105 -0
- package/templates/agentic-owners/agent/lib/owners/types.ts +45 -0
- package/templates/agentic-owners/agent/lib/review.ts +777 -0
- package/templates/agentic-owners/agent/tools/record_review.ts +69 -0
- package/templates/agentic-owners/evals/evals.config.ts +5 -0
- package/templates/agentic-owners/evals/owners.eval.ts +116 -0
- package/templates/agentic-owners/package.json +20 -0
- package/templates/agentic-owners/tsconfig.json +12 -0
- package/templates/demo/README.md +33 -0
- package/templates/demo/agent/agent.ts +15 -0
- package/templates/demo/agent/channels/github.ts +50 -0
- package/templates/demo/agent/channels/queue.ts +30 -0
- package/templates/demo/agent/instructions.md +8 -0
- package/templates/demo/agent/lib/collect.ts +78 -0
- package/templates/demo/agent/tools/record_demo.ts +40 -0
- package/templates/demo/evals/demo.eval.ts +33 -0
- package/templates/demo/evals/evals.config.ts +5 -0
- package/templates/demo/package.json +18 -0
- package/templates/demo/tsconfig.json +12 -0
- package/templates/pr-autofixer/README.md +44 -0
- package/templates/pr-autofixer/agent/agent.ts +22 -0
- package/templates/pr-autofixer/agent/channels/drive.ts +40 -0
- package/templates/pr-autofixer/agent/channels/github.ts +167 -0
- package/templates/pr-autofixer/agent/channels/slack.ts +17 -0
- package/templates/pr-autofixer/agent/hooks/bind-agent.ts +19 -0
- package/templates/pr-autofixer/agent/instructions.md +83 -0
- package/templates/pr-autofixer/agent/lib/affinity.ts +33 -0
- package/templates/pr-autofixer/agent/lib/drive-schemas.ts +15 -0
- package/templates/pr-autofixer/agent/lib/drive.ts +79 -0
- package/templates/pr-autofixer/agent/lib/merge-watch.ts +123 -0
- package/templates/pr-autofixer/agent/lib/pr-state.ts +54 -0
- package/templates/pr-autofixer/agent/lib/repos.ts +19 -0
- package/templates/pr-autofixer/agent/lib/start-drive.ts +122 -0
- package/templates/pr-autofixer/agent/lib/webhook-buffer.ts +225 -0
- package/templates/pr-autofixer/agent/lib/webhook-routing.ts +231 -0
- package/templates/pr-autofixer/agent/storage.ts +7 -0
- package/templates/pr-autofixer/agent/tools/drive_pr.ts +22 -0
- package/templates/pr-autofixer/evals/coordinator.eval.ts +16 -0
- package/templates/pr-autofixer/evals/evals.config.ts +5 -0
- package/templates/pr-autofixer/init.json +25 -0
- package/templates/pr-autofixer/package.json +18 -0
- package/templates/pr-autofixer/tsconfig.json +12 -0
- package/templates/security-reviewer/README.md +31 -0
- package/templates/security-reviewer/agent/agent.ts +15 -0
- package/templates/security-reviewer/agent/channels/github.ts +46 -0
- package/templates/security-reviewer/agent/instructions.md +22 -0
- package/templates/security-reviewer/agent/lib/repos.ts +5 -0
- package/templates/security-reviewer/agent/tools/post_findings.ts +72 -0
- package/templates/security-reviewer/evals/evals.config.ts +5 -0
- package/templates/security-reviewer/evals/review.eval.ts +53 -0
- package/templates/security-reviewer/init.json +25 -0
- package/templates/security-reviewer/package.json +18 -0
- package/templates/security-reviewer/tsconfig.json +12 -0
- package/templates/triage/README.md +27 -29
- package/templates/triage/agent/agent.ts +1 -0
- package/templates/triage/agent/channels/queue.ts +47 -0
- package/templates/triage/agent/channels/webhook.ts +50 -0
- package/templates/triage/agent/instructions.md +24 -13
- package/templates/triage/agent/lib/actuate.ts +759 -0
- package/templates/triage/agent/lib/parse-queue-event.ts +168 -0
- package/templates/triage/agent/lib/tracker.ts +5 -0
- package/templates/triage/agent/lib/types.ts +52 -0
- package/templates/triage/agent/lib/verify-webhook.ts +81 -0
- package/templates/triage/agent/skills/triage-rubric.md +6 -5
- package/templates/triage/agent/tools/record_triage.ts +24 -5
- package/templates/triage/evals/triage.eval.ts +25 -3
- package/templates/triage/overlays/jira/README.md +21 -0
- package/templates/triage/overlays/jira/agent/lib/tracker.ts +8 -0
- package/templates/triage/overlays/jira/agent/mcp-connections/tracker.ts +7 -0
- package/templates/triage/overlays/linear/README.md +21 -0
- package/templates/triage/overlays/linear/agent/lib/tracker.ts +8 -0
- package/templates/triage/overlays/linear/agent/mcp-connections/tracker.ts +7 -0
- package/templates/triage/package.json +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.C4uxzYEU.js +0 -1
- package/dist/docs/assets/index.md.COeoSkcF.js +0 -5
- package/dist/docs/assets/scaffolding-agents.md.mkc3B_ZW.js +0 -1
- package/dist/docs/assets/templates_triage.md.CConZt1z.js +0 -11
- package/dist/docs/assets/templates_triage.md.CConZt1z.lean.js +0 -1
- package/dist/playground/assets/index-BHAPd9KT.js +0 -85
- package/dist/playground/assets/index-DpKH0HcT.css +0 -1
- package/templates/triage/agent/channels/intake.ts +0 -20
- 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,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,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
|
+
}
|
|
@@ -1,48 +1,46 @@
|
|
|
1
1
|
# Triage agent
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
|
12
|
+
## Run
|
|
13
|
+
|
|
14
|
+
Connect the tracker in Cursor.
|
|
14
15
|
|
|
15
16
|
```bash
|
|
16
|
-
agent-sdk login
|
|
17
|
+
agent-sdk login
|
|
17
18
|
agent-sdk dev
|
|
18
19
|
```
|
|
19
20
|
|
|
20
|
-
|
|
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
|
-
|
|
23
|
+
```text
|
|
24
|
+
POST /<slug>/v1/channels/queue/issue
|
|
25
|
+
```
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
```
|
|
30
|
-
|
|
31
|
+
```text
|
|
32
|
+
POST /<slug>/v1/channels/webhook
|
|
31
33
|
```
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
##
|
|
40
|
+
## Files to edit
|
|
37
41
|
|
|
38
|
-
- `agent/
|
|
39
|
-
- `agent/
|
|
40
|
-
|
|
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
|
-
|
|
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.
|
|
@@ -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
|
|
4
|
-
|
|
5
|
-
|
|
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
|
|
10
|
+
## Every issue
|
|
11
11
|
|
|
12
|
-
1. Read the
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
3.
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
-
|
|
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.
|