@cursor/july 0.1.55 → 0.1.62
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 +13 -1
- package/dist/channels/github/github-channel.d.ts.map +1 -1
- package/dist/channels/github/github-channel.js +81 -16
- package/dist/channels/github/types.d.ts +29 -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.CVQxhV85.js → app.B-biogkN.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.5ozYNf74.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.QAPzJjql.js → VPLocalSearchBox.DZqKtFZS.js} +1 -1
- package/dist/docs/assets/chunks/{theme.CSxTCOFu.js → theme.BwC5zP2r.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.D_CCfG8R.js → reference_cli.md.Cl14Jfo3.js} +8 -2
- package/dist/docs/assets/{reference_cli.md.D_CCfG8R.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/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 +12 -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 +5 -5
- 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 +20 -0
- package/dist/internal/cli-slack.d.ts +2 -0
- package/dist/internal/cli-slack.d.ts.map +1 -1
- package/dist/internal/cli-slack.js +11 -2
- package/dist/internal/cursor-event-relay.d.ts +64 -12
- package/dist/internal/cursor-event-relay.d.ts.map +1 -1
- package/dist/internal/cursor-event-relay.js +138 -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 +4 -0
- package/dist/internal/init-project.d.ts.map +1 -1
- package/dist/internal/init-project.js +40 -16
- 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/mcp-oauth.d.ts +0 -4
- package/dist/internal/mcp-oauth.d.ts.map +1 -1
- package/dist/internal/mcp-oauth.js +0 -4
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +160 -58
- 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/playground/assets/index-B4rcOgeP.css +1 -0
- package/dist/playground/assets/{index-0oqXUkrR.js → index-DXcY42Jv.js} +42 -42
- 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 +34 -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/package.json +9 -1
- package/skills/github/SKILL.md +6 -6
- package/src/bin/agent-serve.ts +13 -6
- package/src/channels/github/github-channel.ts +113 -20
- package/src/channels/github/types.ts +33 -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 +23 -0
- package/src/internal/cli-slack.ts +11 -1
- package/src/internal/cursor-event-relay.ts +201 -32
- 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 +70 -15
- package/src/internal/mcp-endpoint.ts +7 -0
- package/src/internal/mcp-oauth.ts +0 -4
- package/src/internal/server.ts +190 -60
- 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/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/dist/docs/assets/chunks/@localSearchIndexroot.BFpWSo6D.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/playground/assets/index-tHCjJxv4.css +0 -1
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type { HostContext } from "@cursor/july";
|
|
2
|
+
import type { SendMessageFn, SendMessageOptions } from "@cursor/july/channels";
|
|
3
|
+
import {
|
|
4
|
+
cloudOptionsFor,
|
|
5
|
+
continuationTokenFor,
|
|
6
|
+
driveMessage,
|
|
7
|
+
parsePrRef,
|
|
8
|
+
} from "./drive.js";
|
|
9
|
+
import { armMergeConflictWatch } from "./merge-watch.js";
|
|
10
|
+
import { lookupPrAgent } from "./affinity.js";
|
|
11
|
+
import { fetchPrState, isTerminalPrState } from "./pr-state.js";
|
|
12
|
+
|
|
13
|
+
export type StartDriveErrorCode = "missing_pr" | "unrecognized_pr";
|
|
14
|
+
|
|
15
|
+
export interface StartDriveError {
|
|
16
|
+
ok: false;
|
|
17
|
+
code: StartDriveErrorCode;
|
|
18
|
+
error: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface StartDriveSuccess {
|
|
22
|
+
ok: true;
|
|
23
|
+
pr: string;
|
|
24
|
+
sessionId: string | null;
|
|
25
|
+
continuationToken: string | null;
|
|
26
|
+
bcId: string | null;
|
|
27
|
+
agentUrl: string | null;
|
|
28
|
+
status: string;
|
|
29
|
+
result: string | null;
|
|
30
|
+
mergeConflictReminderId: string | null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type StartDriveResult = StartDriveError | StartDriveSuccess;
|
|
34
|
+
|
|
35
|
+
export interface StartDriveArgs {
|
|
36
|
+
pr: string;
|
|
37
|
+
hint?: string;
|
|
38
|
+
send: SendMessageFn;
|
|
39
|
+
host: HostContext;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function agentUrlFor(bcId: string | null): string | null {
|
|
43
|
+
return bcId === null ? null : `https://cursor.com/agents/${bcId}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function startDrive(
|
|
47
|
+
args: StartDriveArgs
|
|
48
|
+
): Promise<StartDriveResult> {
|
|
49
|
+
const prRaw = args.pr.trim();
|
|
50
|
+
if (prRaw === "") {
|
|
51
|
+
return {
|
|
52
|
+
ok: false,
|
|
53
|
+
code: "missing_pr",
|
|
54
|
+
error: 'Body must include "pr": a GitHub PR URL or owner/repo#N.',
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
const pr = parsePrRef(prRaw);
|
|
58
|
+
if (pr === undefined) {
|
|
59
|
+
return {
|
|
60
|
+
ok: false,
|
|
61
|
+
code: "unrecognized_pr",
|
|
62
|
+
error: `Unrecognized PR reference: ${args.pr}`,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const hint = args.hint?.trim() || undefined;
|
|
67
|
+
const prState = await fetchPrState({ github: args.host.github, pr });
|
|
68
|
+
|
|
69
|
+
if (isTerminalPrState(prState)) {
|
|
70
|
+
return {
|
|
71
|
+
ok: true,
|
|
72
|
+
pr: pr.label,
|
|
73
|
+
sessionId: null,
|
|
74
|
+
continuationToken: null,
|
|
75
|
+
bcId: null,
|
|
76
|
+
agentUrl: null,
|
|
77
|
+
status: "finished",
|
|
78
|
+
result: `PR ${pr.label} is already ${prState.toLowerCase()}. Nothing to drive.`,
|
|
79
|
+
mergeConflictReminderId: null,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const continuationToken = continuationTokenFor(pr);
|
|
84
|
+
const boundAgent = await lookupPrAgent(pr, args.host.kv);
|
|
85
|
+
const sendOptions: SendMessageOptions = {
|
|
86
|
+
continuationToken,
|
|
87
|
+
title: `PR ${pr.label}`,
|
|
88
|
+
cloud: cloudOptionsFor(pr),
|
|
89
|
+
sdkAgentId: boundAgent,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const session = await args.send(driveMessage(pr, { hint }), sendOptions);
|
|
93
|
+
|
|
94
|
+
let reminderId: string | null = null;
|
|
95
|
+
if (args.host.reminders !== undefined) {
|
|
96
|
+
const reminder = await armMergeConflictWatch({
|
|
97
|
+
reminders: args.host.reminders,
|
|
98
|
+
pr,
|
|
99
|
+
channelId: "drive",
|
|
100
|
+
continuationToken,
|
|
101
|
+
}).catch(() => undefined);
|
|
102
|
+
if (reminder !== undefined) {
|
|
103
|
+
reminderId = reminder.id;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const bcId = session.info.sdkAgentId?.startsWith("bc-")
|
|
108
|
+
? session.info.sdkAgentId
|
|
109
|
+
: null;
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
ok: true,
|
|
113
|
+
pr: pr.label,
|
|
114
|
+
sessionId: session.id,
|
|
115
|
+
continuationToken,
|
|
116
|
+
bcId,
|
|
117
|
+
agentUrl: agentUrlFor(bcId),
|
|
118
|
+
status: "started",
|
|
119
|
+
result: null,
|
|
120
|
+
mergeConflictReminderId: reminderId,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import type { HostKvApi } from "@cursor/july/kv";
|
|
2
|
+
|
|
3
|
+
export const WEBHOOK_BUFFER_KV_KEY: string = "webhook-buffer";
|
|
4
|
+
|
|
5
|
+
export interface BufferedWebhookEvent {
|
|
6
|
+
eventName: string;
|
|
7
|
+
action?: string;
|
|
8
|
+
prUrl: string;
|
|
9
|
+
owner: string;
|
|
10
|
+
repo: string;
|
|
11
|
+
number: number;
|
|
12
|
+
headSha?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface WebhookBufferOptions {
|
|
16
|
+
debounceMs?: number;
|
|
17
|
+
onFlush: (event: BufferedWebhookEvent) => void | Promise<void>;
|
|
18
|
+
kv: HostKvApi;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface BufferEntry {
|
|
22
|
+
latest: BufferedWebhookEvent;
|
|
23
|
+
count: number;
|
|
24
|
+
timer: ReturnType<typeof setTimeout> | undefined;
|
|
25
|
+
deadlineMs: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface SnapshotEntry {
|
|
29
|
+
latest: BufferedWebhookEvent;
|
|
30
|
+
count: number;
|
|
31
|
+
deadlineMs: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class WebhookBuffer {
|
|
35
|
+
private readonly entries = new Map<string, BufferEntry>();
|
|
36
|
+
private readonly inFlight = new Set<string>();
|
|
37
|
+
private readonly debounceMs: number;
|
|
38
|
+
private readonly onFlush: WebhookBufferOptions["onFlush"];
|
|
39
|
+
private readonly kv: HostKvApi;
|
|
40
|
+
private persistChain: Promise<void> = Promise.resolve();
|
|
41
|
+
private generation = 0;
|
|
42
|
+
|
|
43
|
+
constructor(options: WebhookBufferOptions) {
|
|
44
|
+
this.debounceMs = options.debounceMs ?? 3000;
|
|
45
|
+
this.onFlush = options.onFlush;
|
|
46
|
+
this.kv = options.kv;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static keyFor(
|
|
50
|
+
event: Pick<BufferedWebhookEvent, "owner" | "repo" | "number">
|
|
51
|
+
): string {
|
|
52
|
+
return `${event.owner}/${event.repo}#${event.number}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async enqueue(event: BufferedWebhookEvent): Promise<void> {
|
|
56
|
+
const key = WebhookBuffer.keyFor(event);
|
|
57
|
+
const existing = this.entries.get(key);
|
|
58
|
+
if (existing === undefined) {
|
|
59
|
+
this.entries.set(key, {
|
|
60
|
+
latest: event,
|
|
61
|
+
count: 1,
|
|
62
|
+
timer: undefined,
|
|
63
|
+
deadlineMs: Date.now() + this.debounceMs,
|
|
64
|
+
});
|
|
65
|
+
} else {
|
|
66
|
+
existing.latest = event;
|
|
67
|
+
existing.count += 1;
|
|
68
|
+
existing.deadlineMs = Date.now() + this.debounceMs;
|
|
69
|
+
}
|
|
70
|
+
await this.persist();
|
|
71
|
+
this.schedule(key);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async restore(): Promise<number> {
|
|
75
|
+
const snapshot = await this.kv.get(WEBHOOK_BUFFER_KV_KEY);
|
|
76
|
+
if (
|
|
77
|
+
!isJsonObject(snapshot) ||
|
|
78
|
+
snapshot.version !== 1 ||
|
|
79
|
+
!isJsonObject(snapshot.entries)
|
|
80
|
+
) {
|
|
81
|
+
return 0;
|
|
82
|
+
}
|
|
83
|
+
let restored = 0;
|
|
84
|
+
for (const [key, entry] of Object.entries(snapshot.entries)) {
|
|
85
|
+
if (!isValidSnapshotEntry(entry)) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
this.entries.set(key, {
|
|
89
|
+
latest: entry.latest,
|
|
90
|
+
count: Math.max(1, entry.count),
|
|
91
|
+
timer: undefined,
|
|
92
|
+
deadlineMs: entry.deadlineMs,
|
|
93
|
+
});
|
|
94
|
+
this.schedule(key);
|
|
95
|
+
restored += 1;
|
|
96
|
+
}
|
|
97
|
+
return restored;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async discard(key: string): Promise<void> {
|
|
101
|
+
const entry = this.entries.get(key);
|
|
102
|
+
if (entry === undefined) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
clearTimeout(entry.timer);
|
|
106
|
+
this.entries.delete(key);
|
|
107
|
+
await this.persist();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async clear(): Promise<void> {
|
|
111
|
+
this.generation += 1;
|
|
112
|
+
for (const entry of this.entries.values()) {
|
|
113
|
+
clearTimeout(entry.timer);
|
|
114
|
+
}
|
|
115
|
+
this.entries.clear();
|
|
116
|
+
this.inFlight.clear();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private schedule(key: string): void {
|
|
120
|
+
const entry = this.entries.get(key);
|
|
121
|
+
if (entry === undefined) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
clearTimeout(entry.timer);
|
|
125
|
+
const delay = Math.max(0, entry.deadlineMs - Date.now());
|
|
126
|
+
entry.timer = setTimeout(() => {
|
|
127
|
+
void this.flush(key);
|
|
128
|
+
}, delay);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private async flush(key: string): Promise<void> {
|
|
132
|
+
const entry = this.entries.get(key);
|
|
133
|
+
if (entry === undefined) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (this.inFlight.has(key)) {
|
|
138
|
+
entry.deadlineMs = Date.now() + this.debounceMs;
|
|
139
|
+
await this.persist();
|
|
140
|
+
this.schedule(key);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
entry.timer = undefined;
|
|
145
|
+
this.inFlight.add(key);
|
|
146
|
+
const generation = this.generation;
|
|
147
|
+
try {
|
|
148
|
+
const event = entry.latest;
|
|
149
|
+
const count = entry.count;
|
|
150
|
+
await this.onFlush(event);
|
|
151
|
+
if (generation !== this.generation) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const current = this.entries.get(key);
|
|
155
|
+
if (
|
|
156
|
+
current !== undefined &&
|
|
157
|
+
current.latest === event &&
|
|
158
|
+
current.count === count
|
|
159
|
+
) {
|
|
160
|
+
this.entries.delete(key);
|
|
161
|
+
}
|
|
162
|
+
await this.persist();
|
|
163
|
+
} catch (error) {
|
|
164
|
+
if (generation !== this.generation) {
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
entry.deadlineMs = Date.now() + this.debounceMs;
|
|
168
|
+
await this.persist();
|
|
169
|
+
this.schedule(key);
|
|
170
|
+
throw error;
|
|
171
|
+
} finally {
|
|
172
|
+
if (generation === this.generation) {
|
|
173
|
+
this.inFlight.delete(key);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private persist(): Promise<void> {
|
|
179
|
+
const entries: Record<string, SnapshotEntry> = {};
|
|
180
|
+
for (const [key, entry] of this.entries) {
|
|
181
|
+
entries[key] = {
|
|
182
|
+
latest: entry.latest,
|
|
183
|
+
count: entry.count,
|
|
184
|
+
deadlineMs: entry.deadlineMs,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
const write = (): Promise<void> =>
|
|
188
|
+
this.kv.put(
|
|
189
|
+
WEBHOOK_BUFFER_KV_KEY,
|
|
190
|
+
JSON.parse(
|
|
191
|
+
JSON.stringify({
|
|
192
|
+
version: 1,
|
|
193
|
+
updatedAt: new Date().toISOString(),
|
|
194
|
+
entries,
|
|
195
|
+
})
|
|
196
|
+
)
|
|
197
|
+
);
|
|
198
|
+
const next = this.persistChain.then(write, write);
|
|
199
|
+
this.persistChain = next.then(
|
|
200
|
+
() => undefined,
|
|
201
|
+
() => undefined
|
|
202
|
+
);
|
|
203
|
+
return next;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function isJsonObject(value: unknown): value is Record<string, unknown> {
|
|
208
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function isValidSnapshotEntry(entry: unknown): entry is SnapshotEntry {
|
|
212
|
+
if (!isJsonObject(entry) || !isJsonObject(entry.latest)) {
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
const latest = entry.latest;
|
|
216
|
+
return (
|
|
217
|
+
typeof latest.eventName === "string" &&
|
|
218
|
+
typeof latest.prUrl === "string" &&
|
|
219
|
+
typeof latest.owner === "string" &&
|
|
220
|
+
typeof latest.repo === "string" &&
|
|
221
|
+
typeof latest.number === "number" &&
|
|
222
|
+
typeof entry.count === "number" &&
|
|
223
|
+
typeof entry.deadlineMs === "number"
|
|
224
|
+
);
|
|
225
|
+
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
GitHubCheckRunEvent,
|
|
3
|
+
GitHubCheckSuiteEvent,
|
|
4
|
+
GitHubEventContext,
|
|
5
|
+
GitHubPullRequestEvent,
|
|
6
|
+
GitHubRawEvent,
|
|
7
|
+
GitHubStatusEvent,
|
|
8
|
+
} from "@cursor/july/channels/github";
|
|
9
|
+
import type { BufferedWebhookEvent } from "./webhook-buffer.js";
|
|
10
|
+
|
|
11
|
+
const PULL_REQUEST_ACTIONS = new Set([
|
|
12
|
+
"reopened",
|
|
13
|
+
"synchronize",
|
|
14
|
+
"edited",
|
|
15
|
+
"ready_for_review",
|
|
16
|
+
"review_requested",
|
|
17
|
+
"converted_to_draft",
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
const CHECK_SUITE_CONCLUSIONS = new Set([
|
|
21
|
+
"success",
|
|
22
|
+
"failure",
|
|
23
|
+
"timed_out",
|
|
24
|
+
"action_required",
|
|
25
|
+
"startup_failure",
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
const CHECK_RUN_FAILURE_CONCLUSIONS = new Set([
|
|
29
|
+
"failure",
|
|
30
|
+
"timed_out",
|
|
31
|
+
"action_required",
|
|
32
|
+
"startup_failure",
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
export function shouldBufferPullRequest(pr: GitHubPullRequestEvent): boolean {
|
|
36
|
+
return PULL_REQUEST_ACTIONS.has(pr.action);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function shouldBufferStatus(status: GitHubStatusEvent): boolean {
|
|
40
|
+
return status.state === "failure" || status.state === "error";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function shouldBufferCheckSuite(suite: GitHubCheckSuiteEvent): boolean {
|
|
44
|
+
return (
|
|
45
|
+
suite.action === "completed" &&
|
|
46
|
+
suite.conclusion != null &&
|
|
47
|
+
CHECK_SUITE_CONCLUSIONS.has(suite.conclusion)
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function shouldBufferCheckRun(run: GitHubCheckRunEvent): boolean {
|
|
52
|
+
return (
|
|
53
|
+
run.action === "completed" &&
|
|
54
|
+
run.conclusion != null &&
|
|
55
|
+
CHECK_RUN_FAILURE_CONCLUSIONS.has(run.conclusion)
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function shouldBufferRawEvent(event: GitHubRawEvent): boolean {
|
|
60
|
+
if (event.eventName === "pull_request_review") {
|
|
61
|
+
return event.action === "submitted" || event.action === "dismissed";
|
|
62
|
+
}
|
|
63
|
+
if (event.eventName === "pull_request_review_thread") {
|
|
64
|
+
return event.action === "resolved" || event.action === "unresolved";
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function fromConversation(
|
|
70
|
+
ctx: GitHubEventContext,
|
|
71
|
+
fields: {
|
|
72
|
+
eventName: string;
|
|
73
|
+
action?: string;
|
|
74
|
+
headSha?: string;
|
|
75
|
+
}
|
|
76
|
+
): BufferedWebhookEvent | null {
|
|
77
|
+
if (ctx.conversation.kind !== "pull_request") {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
if (ctx.conversation.number <= 0) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
eventName: fields.eventName,
|
|
85
|
+
action: fields.action,
|
|
86
|
+
prUrl: ctx.conversation.url,
|
|
87
|
+
owner: ctx.repository.owner,
|
|
88
|
+
repo: ctx.repository.name,
|
|
89
|
+
number: ctx.conversation.number,
|
|
90
|
+
headSha: fields.headSha,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function resolvePrFromCommitSha(input: {
|
|
95
|
+
ctx: GitHubEventContext;
|
|
96
|
+
sha: string;
|
|
97
|
+
}): Promise<{ number: number; url: string } | undefined> {
|
|
98
|
+
try {
|
|
99
|
+
const octokit = await input.ctx.github.getOctokit();
|
|
100
|
+
const { data: pulls } = await octokit.request(
|
|
101
|
+
"GET /repos/{owner}/{repo}/commits/{commit_sha}/pulls",
|
|
102
|
+
{
|
|
103
|
+
owner: input.ctx.repository.owner,
|
|
104
|
+
repo: input.ctx.repository.name,
|
|
105
|
+
commit_sha: input.sha,
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
const open = pulls.find((pull) => pull.state === "open");
|
|
109
|
+
if (open === undefined) {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
number: open.number,
|
|
114
|
+
url: open.html_url,
|
|
115
|
+
};
|
|
116
|
+
} catch {
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function fromSha(input: {
|
|
122
|
+
ctx: GitHubEventContext;
|
|
123
|
+
sha: string;
|
|
124
|
+
eligible: boolean;
|
|
125
|
+
eventName: string;
|
|
126
|
+
action?: string;
|
|
127
|
+
}): Promise<BufferedWebhookEvent | null> {
|
|
128
|
+
if (!input.eligible) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
const direct = fromConversation(input.ctx, {
|
|
132
|
+
eventName: input.eventName,
|
|
133
|
+
action: input.action,
|
|
134
|
+
headSha: input.sha,
|
|
135
|
+
});
|
|
136
|
+
if (direct !== null) {
|
|
137
|
+
return direct;
|
|
138
|
+
}
|
|
139
|
+
const resolved = await resolvePrFromCommitSha({
|
|
140
|
+
ctx: input.ctx,
|
|
141
|
+
sha: input.sha,
|
|
142
|
+
});
|
|
143
|
+
if (resolved === undefined) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
eventName: input.eventName,
|
|
148
|
+
action: input.action,
|
|
149
|
+
prUrl: resolved.url,
|
|
150
|
+
owner: input.ctx.repository.owner,
|
|
151
|
+
repo: input.ctx.repository.name,
|
|
152
|
+
number: resolved.number,
|
|
153
|
+
headSha: input.sha,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function bufferedEventFromPr(input: {
|
|
158
|
+
ctx: GitHubEventContext;
|
|
159
|
+
pr: GitHubPullRequestEvent;
|
|
160
|
+
}): BufferedWebhookEvent | null {
|
|
161
|
+
if (!shouldBufferPullRequest(input.pr)) {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
eventName: "pull_request",
|
|
166
|
+
action: input.pr.action,
|
|
167
|
+
prUrl: input.pr.url,
|
|
168
|
+
owner: input.ctx.repository.owner,
|
|
169
|
+
repo: input.ctx.repository.name,
|
|
170
|
+
number: input.pr.number,
|
|
171
|
+
headSha: input.pr.headSha,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function bufferedEventFromComment(input: {
|
|
176
|
+
ctx: GitHubEventContext;
|
|
177
|
+
eventName: string;
|
|
178
|
+
}): BufferedWebhookEvent | null {
|
|
179
|
+
return fromConversation(input.ctx, { eventName: input.eventName });
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function bufferedEventFromCheckSuite(input: {
|
|
183
|
+
ctx: GitHubEventContext;
|
|
184
|
+
suite: GitHubCheckSuiteEvent;
|
|
185
|
+
}): Promise<BufferedWebhookEvent | null> {
|
|
186
|
+
return fromSha({
|
|
187
|
+
ctx: input.ctx,
|
|
188
|
+
sha: input.suite.headSha,
|
|
189
|
+
eligible: shouldBufferCheckSuite(input.suite),
|
|
190
|
+
eventName: "check_suite",
|
|
191
|
+
action: input.suite.action,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function bufferedEventFromCheckRun(input: {
|
|
196
|
+
ctx: GitHubEventContext;
|
|
197
|
+
run: GitHubCheckRunEvent;
|
|
198
|
+
}): Promise<BufferedWebhookEvent | null> {
|
|
199
|
+
return fromSha({
|
|
200
|
+
ctx: input.ctx,
|
|
201
|
+
sha: input.run.headSha,
|
|
202
|
+
eligible: shouldBufferCheckRun(input.run),
|
|
203
|
+
eventName: "check_run",
|
|
204
|
+
action: input.run.action,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export function bufferedEventFromStatus(input: {
|
|
209
|
+
ctx: GitHubEventContext;
|
|
210
|
+
status: GitHubStatusEvent;
|
|
211
|
+
}): Promise<BufferedWebhookEvent | null> {
|
|
212
|
+
return fromSha({
|
|
213
|
+
ctx: input.ctx,
|
|
214
|
+
sha: input.status.sha,
|
|
215
|
+
eligible: shouldBufferStatus(input.status),
|
|
216
|
+
eventName: "status",
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function bufferedEventFromRaw(input: {
|
|
221
|
+
ctx: GitHubEventContext;
|
|
222
|
+
event: GitHubRawEvent;
|
|
223
|
+
}): BufferedWebhookEvent | null {
|
|
224
|
+
if (!shouldBufferRawEvent(input.event)) {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
return fromConversation(input.ctx, {
|
|
228
|
+
eventName: input.event.eventName,
|
|
229
|
+
action: input.event.action,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { defineTool } from "@cursor/july/tools";
|
|
2
|
+
import type { ToolExecuteResult } from "@cursor/july/tools";
|
|
3
|
+
import { driveStartBodySchema } from "../lib/drive-schemas.js";
|
|
4
|
+
import { startDrive } from "../lib/start-drive.js";
|
|
5
|
+
|
|
6
|
+
export default defineTool({
|
|
7
|
+
description:
|
|
8
|
+
"Start PR autofixer on a GitHub pull request. Attaches the PR to a Cursor cloud agent and returns as soon as the session exists. bcId / agentUrl are included when already bound; otherwise they fill in from agent.bound. Triage continues asynchronously. Call whenever the user wants to drive, triage, or unblock a PR. Do not review the PR yourself from chat; there is no checkout here.",
|
|
9
|
+
inputSchema: driveStartBodySchema,
|
|
10
|
+
async execute({ pr, hint }, ctx): Promise<ToolExecuteResult> {
|
|
11
|
+
const result = await startDrive({
|
|
12
|
+
pr,
|
|
13
|
+
hint,
|
|
14
|
+
send: (message, options) => ctx.send("drive", message, options),
|
|
15
|
+
host: ctx.host,
|
|
16
|
+
});
|
|
17
|
+
if (!result.ok) {
|
|
18
|
+
return { ok: false, error: result.error, code: result.code };
|
|
19
|
+
}
|
|
20
|
+
return result;
|
|
21
|
+
},
|
|
22
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineEval } from "@cursor/july/evals";
|
|
2
|
+
|
|
3
|
+
export default defineEval({
|
|
4
|
+
tags: ["smoke"],
|
|
5
|
+
cases: [
|
|
6
|
+
{
|
|
7
|
+
id: "no-pr",
|
|
8
|
+
description: "Chat without a PR does not start a cloud drive.",
|
|
9
|
+
async test(t) {
|
|
10
|
+
await t.send("What can you help with?");
|
|
11
|
+
t.succeeded();
|
|
12
|
+
t.calledTool("drive_pr", { count: 0 });
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"questions": [
|
|
3
|
+
{
|
|
4
|
+
"id": "repos",
|
|
5
|
+
"prompt": "GitHub repositories Cursor should relay SCM events for",
|
|
6
|
+
"hint": "Comma-separated owner/repo. One owner. Up to 20.",
|
|
7
|
+
"placeholder": "acme/widgets, acme/api",
|
|
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": "agent/channels/slack.ts",
|
|
20
|
+
"find": "acme/widgets",
|
|
21
|
+
"answer": "repos",
|
|
22
|
+
"as": "first"
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pr-autofixer",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "PR autofixer: attach a pull request to a Cursor cloud agent and keep it moving toward merge.",
|
|
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
|
+
}
|