@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
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import { spawn } from "node:child_process";
6
- import { existsSync, readdirSync, readFileSync } from "node:fs";
6
+ import { existsSync, readFileSync } from "node:fs";
7
7
  import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
8
8
  import { basename, dirname, join, relative, resolve } from "node:path";
9
9
  import type { ModelSetting } from "../types.js";
@@ -13,6 +13,14 @@ import {
13
13
  packageRootDir,
14
14
  packageVersion,
15
15
  } from "./distribution.js";
16
+ import {
17
+ applyInitReplacements,
18
+ isTemplateMetaPath,
19
+ loadTemplateInitSpec,
20
+ posixRel,
21
+ type TemplateInitSpec,
22
+ templateFileHasReplacements,
23
+ } from "./init-interview.js";
16
24
  import {
17
25
  makePalette,
18
26
  stdoutPalette,
@@ -27,6 +35,11 @@ export interface InitProjectOptions {
27
35
  /** Model id override written into agent.ts. */
28
36
  model?: string;
29
37
  templateDir?: string;
38
+ overlayDir?: string;
39
+ templateName?: string;
40
+ /** Answers collected from `init.json` / `--var`. */
41
+ answers?: Record<string, string>;
42
+ initSpec?: TemplateInitSpec;
30
43
  }
31
44
 
32
45
  export type InitFileAction = "create" | "exist";
@@ -46,23 +59,30 @@ export interface InitProjectResult {
46
59
  skipped: string[];
47
60
  }
48
61
 
62
+ type TemplateEntry = {
63
+ dir: string;
64
+ overlay?: string;
65
+ };
66
+
67
+ const TEMPLATES: Readonly<Record<string, TemplateEntry>> = {
68
+ "agentic-owners": { dir: "agentic-owners" },
69
+ demo: { dir: "demo" },
70
+ "pr-autofixer": { dir: "pr-autofixer" },
71
+ "security-reviewer": { dir: "security-reviewer" },
72
+ "triage-linear": { dir: "triage", overlay: "linear" },
73
+ "triage-jira": { dir: "triage", overlay: "jira" },
74
+ };
75
+
76
+ function canonicalTemplateName(name: string): string {
77
+ return name === "triage" ? "triage-linear" : name;
78
+ }
79
+
49
80
  export function availableTemplates(): string[] {
50
- const templatesDir = join(packageRootDir(), "templates");
51
- try {
52
- return readdirSync(templatesDir, { withFileTypes: true })
53
- .filter((entry) => entry.isDirectory())
54
- .map((entry) => entry.name)
55
- .sort();
56
- } catch (error) {
57
- if (hasErrorCode(error, "ENOENT")) {
58
- return [];
59
- }
60
- throw error;
61
- }
81
+ return Object.keys(TEMPLATES).sort();
62
82
  }
63
83
 
64
84
  export type TemplateResolution =
65
- | { ok: true; name: string; dir: string }
85
+ | { ok: true; name: string; dir: string; overlayDir?: string }
66
86
  | { ok: false; message: string };
67
87
 
68
88
  export function resolveTemplate(name: string): TemplateResolution {
@@ -72,21 +92,23 @@ export function resolveTemplate(name: string): TemplateResolution {
72
92
  message: "template name must match /^[a-z][a-z0-9-]*$/",
73
93
  };
74
94
  }
75
- const available = availableTemplates();
76
- if (!available.includes(name)) {
77
- const detail =
78
- available.length === 0
79
- ? "no templates ship with this build"
80
- : `available: ${available.join(", ")}`;
95
+ const resolved = canonicalTemplateName(name);
96
+ const entry = TEMPLATES[resolved];
97
+ if (entry === undefined) {
81
98
  return {
82
99
  ok: false,
83
- message: `unknown template "${name}" (${detail})`,
100
+ message: `unknown template "${name}" (available: ${availableTemplates().join(", ")})`,
84
101
  };
85
102
  }
103
+ const dir = join(packageRootDir(), "templates", entry.dir);
86
104
  return {
87
105
  ok: true,
88
- name,
89
- dir: join(packageRootDir(), "templates", name),
106
+ name: resolved,
107
+ dir,
108
+ overlayDir:
109
+ entry.overlay === undefined
110
+ ? undefined
111
+ : join(dir, "overlays", entry.overlay),
90
112
  };
91
113
  }
92
114
 
@@ -121,6 +143,13 @@ export function checkInitTarget(
121
143
  if (marker === template) {
122
144
  return { ok: true };
123
145
  }
146
+ if (
147
+ marker !== undefined &&
148
+ template !== undefined &&
149
+ canonicalTemplateName(marker) === canonicalTemplateName(template)
150
+ ) {
151
+ return { ok: true };
152
+ }
124
153
  if (template === undefined) {
125
154
  return {
126
155
  ok: false,
@@ -194,28 +223,38 @@ export async function initProject(
194
223
 
195
224
  if (options.templateDir !== undefined) {
196
225
  const templateDir = options.templateDir;
197
- const entries = await readdir(templateDir, {
198
- recursive: true,
199
- withFileTypes: true,
200
- });
201
- const relativePaths = entries
202
- .filter((entry) => entry.isFile())
203
- .map((entry) => relative(templateDir, join(entry.parentPath, entry.name)))
204
- .sort();
205
- for (const rel of relativePaths) {
206
- const source = join(templateDir, rel);
207
- if (rel === "package.json") {
208
- const packageJson: {
209
- name: string;
210
- dependencies: Record<string, string>;
211
- [key: string]: unknown;
212
- } = JSON.parse(await readFile(source, "utf8"));
213
- packageJson.name = projectName;
214
- packageJson.dependencies[PACKAGE_NAME] = `^${packageVersion()}`;
215
- packageJson.cursorAgentTemplate = basename(templateDir);
216
- await write(rel, `${JSON.stringify(packageJson, null, 2)}\n`);
217
- } else {
218
- await write(rel, await readFile(source));
226
+ const overlayDir = options.overlayDir;
227
+ const overlayPaths = overlayDir ? await listTemplateFiles(overlayDir) : [];
228
+ const overlaySet = new Set(overlayPaths);
229
+ const corePaths = (await listTemplateFiles(templateDir)).filter(
230
+ (rel) => !overlaySet.has(rel)
231
+ );
232
+ const templateName = options.templateName ?? basename(templateDir);
233
+ const initSpec =
234
+ options.initSpec ?? (await loadTemplateInitSpec(templateDir));
235
+ const answers = options.answers ?? {};
236
+ for (const rel of corePaths) {
237
+ await writeTemplateFile({
238
+ write,
239
+ sourceDir: templateDir,
240
+ rel,
241
+ projectName,
242
+ templateName,
243
+ initSpec,
244
+ answers,
245
+ });
246
+ }
247
+ if (overlayDir !== undefined) {
248
+ for (const rel of overlayPaths) {
249
+ await writeTemplateFile({
250
+ write,
251
+ sourceDir: overlayDir,
252
+ rel,
253
+ projectName,
254
+ templateName,
255
+ initSpec,
256
+ answers,
257
+ });
219
258
  }
220
259
  }
221
260
  return {
@@ -346,6 +385,60 @@ export default memoryHook();
346
385
  };
347
386
  }
348
387
 
388
+ async function listTemplateFiles(root: string): Promise<string[]> {
389
+ const entries = await readdir(root, {
390
+ recursive: true,
391
+ withFileTypes: true,
392
+ });
393
+ return entries
394
+ .filter((entry) => entry.isFile())
395
+ .map((entry) =>
396
+ posixRel(relative(root, join(entry.parentPath, entry.name)))
397
+ )
398
+ .filter((rel) => !isTemplateMetaPath(rel))
399
+ .sort();
400
+ }
401
+
402
+ async function writeTemplateFile(input: {
403
+ write: (rel: string, contents: string | Uint8Array) => Promise<void>;
404
+ sourceDir: string;
405
+ rel: string;
406
+ projectName: string;
407
+ templateName: string;
408
+ initSpec?: TemplateInitSpec;
409
+ answers: Record<string, string>;
410
+ }): Promise<void> {
411
+ const source = join(input.sourceDir, input.rel);
412
+ if (input.rel === "package.json") {
413
+ const packageJson: {
414
+ name: string;
415
+ dependencies: Record<string, string>;
416
+ [key: string]: unknown;
417
+ } = JSON.parse(await readFile(source, "utf8"));
418
+ packageJson.name = input.projectName;
419
+ packageJson.dependencies[PACKAGE_NAME] = `^${packageVersion()}`;
420
+ packageJson.cursorAgentTemplate = input.templateName;
421
+ await input.write(input.rel, `${JSON.stringify(packageJson, null, 2)}\n`);
422
+ return;
423
+ }
424
+ if (
425
+ input.initSpec !== undefined &&
426
+ templateFileHasReplacements(input.initSpec, input.rel)
427
+ ) {
428
+ await input.write(
429
+ input.rel,
430
+ applyInitReplacements(
431
+ await readFile(source, "utf8"),
432
+ input.rel,
433
+ input.initSpec,
434
+ input.answers
435
+ )
436
+ );
437
+ return;
438
+ }
439
+ await input.write(input.rel, await readFile(source));
440
+ }
441
+
349
442
  export function isEexist(error: unknown): boolean {
350
443
  return hasErrorCode(error, "EEXIST");
351
444
  }
@@ -740,7 +833,14 @@ export function printInitGuide(): void {
740
833
 
741
834
  Scaffold a project, then open the playground:
742
835
  npx ${PACKAGE_NAME} init ./my-agent # installs deps + links agent-sdk onto PATH
743
- npx ${PACKAGE_NAME} init ./my-triage --template triage # start from a template (agent-sdk templates: triage)
836
+ npx ${PACKAGE_NAME} init ./my-demo --template demo # record a PR walkthrough
837
+ npx ${PACKAGE_NAME} init ./my-reviewer --template security-reviewer # review PRs for security bugs
838
+ npx ${PACKAGE_NAME} init ./my-triage --template triage-linear # comment on Linear issues
839
+ npx ${PACKAGE_NAME} init ./my-triage --template triage-jira # comment on Jira issues
840
+ npx ${PACKAGE_NAME} init ./my-owners --template agentic-owners # review PRs via owners policies
841
+ npx ${PACKAGE_NAME} init ./pr-autofixer --template pr-autofixer # fix PRs on a cloud VM
842
+ # TTY asks for GitHub repos. Scripts pass --var:
843
+ # --var repos=acme/widgets,acme/api
744
844
  cd ./my-agent && agent-sdk dev # or: npx ${PACKAGE_NAME} dev
745
845
 
746
846
  Recommended loop once the project exists (from the project directory):
@@ -82,6 +82,13 @@ interface ConnectableMcpServer {
82
82
  * transport per POST, JSON response mode, spec-compliant 405 on other
83
83
  * methods. Shared by the per-agent MCP endpoint and the Cursor account
84
84
  * bridge so the subtle transport/error plumbing exists once.
85
+ *
86
+ * Origin MCP in `backend/server/src/origin/mcp/originMcpHttpHandler.ts`
87
+ * is not a 1:1 copy: it races `request.signal` so a tool that ignores abort
88
+ * cannot hang, while this helper has no such backstop and still leaks an
89
+ * unsettled promise. backend/server cannot import `@cursor/july` internals,
90
+ * so Origin diverges on purpose. Do not extract a shared package, and do
91
+ * not port the abort race here, in that stack.
85
92
  */
86
93
  export async function handleStatelessMcpRequest(args: {
87
94
  request: Request;
@@ -17,7 +17,6 @@ import { join } from "node:path";
17
17
  import {
18
18
  type CursorGithubRepoScope,
19
19
  createCursorAccountGithubTokenResolver,
20
- cursorGithubPermissionsForChannel,
21
20
  type GithubCursorAccountRuntime,
22
21
  getGithubCursorAccountRuntime,
23
22
  parseCursorGithubRepoScope,
@@ -25,6 +24,12 @@ import {
25
24
  resolveCursorGithubProxyUrl,
26
25
  } from "../channels/github/cursor-account.js";
27
26
  import { findGitHubChannelTargets } from "../channels/github/forward.js";
27
+ import {
28
+ getOriginCursorAccountRuntime,
29
+ type OriginCursorAccountRuntime,
30
+ parseCursorOriginRepoScope,
31
+ } from "../channels/origin/cursor-account.js";
32
+ import { findOriginChannelTargets } from "../channels/origin/origin-channel.js";
28
33
  import {
29
34
  dispatchCursorSlackEnvelope,
30
35
  getSlackCursorAccountRuntime,
@@ -59,9 +64,11 @@ import {
59
64
  import { CursorGithubCredentialProvider } from "./cursor/github-credentials.js";
60
65
  import {
61
66
  CursorEventRelay,
67
+ type CursorScmSubscription,
62
68
  formatCursorScmDispatchOutcome,
63
69
  formatCursorScmEventLog,
64
70
  scmMetadataToGitHubPayload,
71
+ scmMetadataToOriginPayload,
65
72
  } from "./cursor-event-relay.js";
66
73
  import {
67
74
  type CursorSlackAgentRegistration,
@@ -89,6 +96,7 @@ import {
89
96
  } from "./eval-run-store.js";
90
97
  import {
91
98
  collectGitHubTriggerTargets,
99
+ collectOriginTriggerTargets,
92
100
  handleAgentServeTrigger,
93
101
  } from "./handleAgentServeTrigger.js";
94
102
  import { createHostPlatforms, warmupHostGitHubAuth } from "./host-platforms.js";
@@ -151,11 +159,7 @@ import {
151
159
  } from "./resolved-connections.js";
152
160
  import { Router } from "./router.js";
153
161
  import { ScheduleRunner, UnknownScheduleError } from "./schedule-runner.js";
154
- import {
155
- type AgentRunner,
156
- CursorSdkRunner,
157
- resolveServeWorkspaceScanCacheTtlMs,
158
- } from "./sdk-runner.js";
162
+ import { type AgentRunner, CursorSdkRunner } from "./sdk-runner.js";
159
163
  import {
160
164
  SessionAuthError,
161
165
  SessionBusyError,
@@ -267,11 +271,20 @@ export async function startServer(
267
271
  }
268
272
 
269
273
  const cursorGithubTargets = collectCursorGithubTargets(mounts);
274
+ const cursorOriginTargets = collectCursorOriginTargets(mounts);
270
275
  const cursorGithubConfig = resolveCursorGithubEventConfig({
271
276
  configured: options.cursorEvents,
272
277
  targets: cursorGithubTargets,
273
278
  });
274
- const effectiveCursorEvents = cursorGithubConfig.cursorEvents;
279
+ const cursorOriginScope = resolveCursorOriginRepoScope({
280
+ explicitRepos: options.cursorEvents?.repos ?? [],
281
+ targets: cursorOriginTargets,
282
+ });
283
+ const effectiveCursorEvents =
284
+ cursorGithubConfig.cursorEvents ??
285
+ (cursorOriginScope === undefined
286
+ ? undefined
287
+ : { repos: [...cursorOriginScope.repos] });
275
288
 
276
289
  // One resolution for every Cursor credential consumer (SDK runner, account
277
290
  // MCP bridge, SCM event relay, GitHub credentials): explicit option →
@@ -379,17 +392,11 @@ export async function startServer(
379
392
  "[agent-sdk] warning: --allow-anonymous admits every HTTP caller as the same principal; use only on a trusted network (e.g. Tailscale) and prefer --bearer-token for shared hosts"
380
393
  );
381
394
  }
382
- const workspaceScanCacheTtlMs = resolveServeWorkspaceScanCacheTtlMs({
383
- dev,
384
- });
385
395
  const runner =
386
396
  options.runner ??
387
397
  new CursorSdkRunner({
388
398
  ...(accountKey === undefined ? {} : { apiKey: accountKey.apiKey }),
389
399
  logger,
390
- ...(workspaceScanCacheTtlMs === undefined
391
- ? {}
392
- : { workspaceScanCacheTtlMs }),
393
400
  });
394
401
 
395
402
  const router = new Router();
@@ -684,7 +691,7 @@ export async function startServer(
684
691
  // job (backend `/github_webhook` already verified); we only admit loopback
685
692
  // so this route is not a public unsigned endpoint. Agents decide whether
686
693
  // to run via their onPullRequest / onComment / … hooks.
687
- const collectTriggerTargets = () =>
694
+ const collectGitHubTargets = () =>
688
695
  collectGitHubTriggerTargets({
689
696
  mounts: runtimes.map((runtime) => ({
690
697
  slug: runtime.slug,
@@ -693,6 +700,15 @@ export async function startServer(
693
700
  buildChannelHandlerArgs({ runtime, channelId }),
694
701
  })),
695
702
  });
703
+ const collectOriginTargets = () =>
704
+ collectOriginTriggerTargets({
705
+ mounts: runtimes.map((runtime) => ({
706
+ slug: runtime.slug,
707
+ channels: runtime.project.channels,
708
+ buildHandlerArgs: (channelId) =>
709
+ buildChannelHandlerArgs({ runtime, channelId }),
710
+ })),
711
+ });
696
712
  router.add({
697
713
  method: "POST",
698
714
  pattern: "/v1/webhooks/github",
@@ -704,7 +720,7 @@ export async function startServer(
704
720
  bodyText,
705
721
  eventName: request.headers.get("x-github-event"),
706
722
  deliveryId: request.headers.get("x-github-delivery") ?? undefined,
707
- targets: collectTriggerTargets(),
723
+ targets: collectGitHubTargets(),
708
724
  });
709
725
  const status = result.ok ? 202 : (result.targets[0]?.status ?? 500);
710
726
  return jsonResponse(result, { status });
@@ -743,13 +759,17 @@ export async function startServer(
743
759
  // Cursor SCM events relay (opt-in: serve({ cursorEvents }) / --cursor-events).
744
760
  const cursorEventRelay = startCursorEventRelay({
745
761
  config: effectiveCursorEvents,
762
+ githubRepos: cursorGithubConfig.cursorEvents?.repos ?? [],
763
+ originRepos: cursorOriginScope?.repos ?? [],
746
764
  stateRoot: options.stateRoot,
747
765
  mounts,
748
766
  auth: cursorEventsAuth,
749
767
  logger,
750
- collectTriggerTargets,
768
+ collectGitHubTargets,
769
+ collectOriginTargets,
751
770
  cursorAccountOnly:
752
- options.cursorEvents === undefined && cursorGithubTargets.length > 0,
771
+ options.cursorEvents === undefined &&
772
+ (cursorGithubTargets.length > 0 || cursorOriginTargets.length > 0),
753
773
  });
754
774
 
755
775
  const server = createServer((req, res) => {
@@ -1759,37 +1779,73 @@ function requireCursorEventsAuth(
1759
1779
  /**
1760
1780
  * Opt-in Cursor SCM events relay. Credentials are already required by
1761
1781
  * {@link requireCursorEventsAuth}; the remaining unusable configs (no repos,
1762
- * no github channel) warn and leave the relay off — serve still starts.
1782
+ * no github/origin channel) warn and leave the relay off — serve still starts.
1783
+ *
1784
+ * GitHub and Origin subscriptions are registered independently so mirrored
1785
+ * `owner/name` slugs do not mix. `--cursor-events --repo` feeds GitHub
1786
+ * channels; Origin is subscribed only when an `originChannel({ cursorAccount })`
1787
+ * opted in.
1763
1788
  */
1764
1789
  function startCursorEventRelay(input: {
1765
1790
  config: ServeOptions["cursorEvents"];
1791
+ githubRepos: readonly string[];
1792
+ originRepos: readonly string[];
1766
1793
  stateRoot: string | undefined;
1767
1794
  mounts: AgentMount[];
1768
1795
  auth: CursorEventsAuth | undefined;
1769
1796
  logger: (line: string) => void;
1770
- collectTriggerTargets: () => ReturnType<typeof collectGitHubTriggerTargets>;
1797
+ collectGitHubTargets: () => ReturnType<typeof collectGitHubTriggerTargets>;
1798
+ collectOriginTargets: () => ReturnType<typeof collectOriginTriggerTargets>;
1771
1799
  cursorAccountOnly: boolean;
1772
1800
  }): CursorEventRelay | undefined {
1773
- const { config, stateRoot, mounts, auth, logger, collectTriggerTargets } =
1774
- input;
1801
+ const { config, stateRoot, mounts, auth, logger } = input;
1775
1802
  if (config === undefined || auth === undefined) {
1776
1803
  return undefined;
1777
1804
  }
1778
- const repos = [...new Set(config.repos.map((repo) => repo.trim()))].filter(
1779
- (repo) => repo.length > 0
1780
- );
1781
- if (repos.length === 0) {
1782
- logger(
1783
- "[agent-sdk] cursor-events: no repos configured (pass --repo owner/name); relay disabled"
1784
- );
1785
- return undefined;
1786
- }
1787
1805
  const githubTargets = findGitHubChannelTargets(mounts);
1788
- const events = [...new Set(githubTargets.flatMap((target) => target.events))];
1789
- if (events.length === 0) {
1790
- logger(
1791
- "[agent-sdk] cursor-events: no mounted agent declares a github channel; relay disabled"
1792
- );
1806
+ const originTargets = findOriginChannelTargets(mounts);
1807
+ const githubEvents = [
1808
+ ...new Set(githubTargets.flatMap((target) => target.events)),
1809
+ ];
1810
+ const originEvents = [
1811
+ ...new Set(originTargets.flatMap((target) => target.events)),
1812
+ ];
1813
+ const githubRepos = [
1814
+ ...new Set(input.githubRepos.map((repo) => repo.trim().toLowerCase())),
1815
+ ].filter((repo) => repo.length > 0);
1816
+ const originRepos = [
1817
+ ...new Set(input.originRepos.map((repo) => repo.trim().toLowerCase())),
1818
+ ].filter((repo) => repo.length > 0);
1819
+
1820
+ const subscriptions: CursorScmSubscription[] = [];
1821
+ if (githubEvents.length > 0 && githubRepos.length > 0) {
1822
+ for (const repo of githubRepos) {
1823
+ subscriptions.push({
1824
+ provider: "github",
1825
+ repo,
1826
+ events: githubEvents,
1827
+ });
1828
+ }
1829
+ }
1830
+ if (originEvents.length > 0 && originRepos.length > 0) {
1831
+ for (const repo of originRepos) {
1832
+ subscriptions.push({
1833
+ provider: "origin",
1834
+ repo,
1835
+ events: originEvents,
1836
+ });
1837
+ }
1838
+ }
1839
+ if (subscriptions.length === 0) {
1840
+ if (githubRepos.length === 0 && originRepos.length === 0) {
1841
+ logger(
1842
+ "[agent-sdk] cursor-events: no repos configured (pass --repo owner/name); relay disabled"
1843
+ );
1844
+ } else {
1845
+ logger(
1846
+ "[agent-sdk] cursor-events: no mounted agent declares a github or origin channel; relay disabled"
1847
+ );
1848
+ }
1793
1849
  return undefined;
1794
1850
  }
1795
1851
  const baseStateRoot =
@@ -1798,15 +1854,36 @@ function startCursorEventRelay(input: {
1798
1854
  const relay = new CursorEventRelay({
1799
1855
  baseUrl: auth.baseUrl,
1800
1856
  apiKey: auth.apiKey,
1801
- repos,
1802
- events,
1857
+ subscriptions,
1803
1858
  stateDir: join(baseStateRoot, "cursor-events"),
1804
1859
  logger,
1805
- // Stream is compact metadata (Sand-style); stub a GitHub body from the
1806
- // envelope — rich pullRequest meta when present for onPullRequest Zod.
1807
1860
  dispatch: async (event) => {
1861
+ if (event.provider === "origin") {
1862
+ const targets = input.collectOriginTargets().filter((target) => {
1863
+ const cursorAccount = target.options.cursorAccount;
1864
+ if (
1865
+ input.cursorAccountOnly &&
1866
+ cursorAccount !== true &&
1867
+ typeof cursorAccount !== "object"
1868
+ ) {
1869
+ return false;
1870
+ }
1871
+ return typeof cursorAccount !== "object"
1872
+ ? true
1873
+ : cursorAccount.repos.some(
1874
+ (repo) => repo.toLowerCase() === event.repo.toLowerCase()
1875
+ );
1876
+ });
1877
+ const result = await handleAgentServeTrigger({
1878
+ type: "origin",
1879
+ bodyText: JSON.stringify(scmMetadataToOriginPayload(event)),
1880
+ targets,
1881
+ });
1882
+ logCursorScmDispatch(logger, event, result);
1883
+ return;
1884
+ }
1808
1885
  if (event.provider !== "github") return;
1809
- const targets = collectTriggerTargets().filter((target) => {
1886
+ const targets = input.collectGitHubTargets().filter((target) => {
1810
1887
  const cursorAccount = target.options.cursorAccount;
1811
1888
  if (
1812
1889
  input.cursorAccountOnly &&
@@ -1828,27 +1905,35 @@ function startCursorEventRelay(input: {
1828
1905
  deliveryId: event.deliveryId,
1829
1906
  targets,
1830
1907
  });
1831
- const outcomes = result.targets
1832
- .map((target) => formatCursorScmDispatchOutcome(target))
1833
- .join(", ");
1834
- const rejected = result.targets.filter((target) => target.status >= 400);
1835
- if (rejected.length > 0) {
1836
- logger(
1837
- `[agent-sdk] cursor-events: dropped ${formatCursorScmEventLog(event)} before hook: ${rejected.map(formatCursorScmDispatchOutcome).join(", ")}`
1838
- );
1839
- }
1840
- logger(
1841
- `[agent-sdk] cursor-events: dispatched ${formatCursorScmEventLog(event)}${outcomes === "" ? "" : ` → ${outcomes}`}`
1842
- );
1908
+ logCursorScmDispatch(logger, event, result);
1843
1909
  },
1844
1910
  });
1845
1911
  relay.start();
1846
1912
  logger(
1847
- `[agent-sdk] cursor-events: relaying ${events.length} event kind(s) for ${repos.join(", ")} from ${auth.baseUrl}`
1913
+ `[agent-sdk] cursor-events: relaying ${subscriptions.length} subscription(s) from ${auth.baseUrl}`
1848
1914
  );
1849
1915
  return relay;
1850
1916
  }
1851
1917
 
1918
+ function logCursorScmDispatch(
1919
+ logger: (line: string) => void,
1920
+ event: Parameters<typeof formatCursorScmEventLog>[0],
1921
+ result: Awaited<ReturnType<typeof handleAgentServeTrigger>>
1922
+ ): void {
1923
+ const outcomes = result.targets
1924
+ .map((target) => formatCursorScmDispatchOutcome(target))
1925
+ .join(", ");
1926
+ const rejected = result.targets.filter((target) => target.status >= 400);
1927
+ if (rejected.length > 0) {
1928
+ logger(
1929
+ `[agent-sdk] cursor-events: dropped ${formatCursorScmEventLog(event)} before hook: ${rejected.map(formatCursorScmDispatchOutcome).join(", ")}`
1930
+ );
1931
+ }
1932
+ logger(
1933
+ `[agent-sdk] cursor-events: dispatched ${formatCursorScmEventLog(event)}${outcomes === "" ? "" : ` → ${outcomes}`}`
1934
+ );
1935
+ }
1936
+
1852
1937
  interface CursorGithubTarget {
1853
1938
  slug: string;
1854
1939
  channelId: string;
@@ -1893,11 +1978,8 @@ function resolveCursorGithubEventConfig(input: {
1893
1978
  ]),
1894
1979
  ];
1895
1980
  const permissions = resolveCursorGithubPermissions(
1896
- input.targets.map((target) =>
1897
- cursorGithubPermissionsForChannel(
1898
- target.accountRuntime.options,
1899
- target.accountRuntime.scope?.permissions
1900
- )
1981
+ input.targets.map(
1982
+ (target) => target.accountRuntime.scope?.permissions ?? "pr-write"
1901
1983
  )
1902
1984
  );
1903
1985
  const scope = parseCursorGithubRepoScope(repos, permissions);
@@ -1910,6 +1992,54 @@ function resolveCursorGithubEventConfig(input: {
1910
1992
  };
1911
1993
  }
1912
1994
 
1995
+ interface CursorOriginTarget {
1996
+ slug: string;
1997
+ channelId: string;
1998
+ accountRuntime: OriginCursorAccountRuntime;
1999
+ }
2000
+
2001
+ function collectCursorOriginTargets(
2002
+ mounts: AgentMount[]
2003
+ ): CursorOriginTarget[] {
2004
+ const targets: CursorOriginTarget[] = [];
2005
+ for (const mount of mounts) {
2006
+ for (const channel of mount.project.channels) {
2007
+ const accountRuntime = getOriginCursorAccountRuntime(channel.definition);
2008
+ if (accountRuntime !== undefined) {
2009
+ targets.push({
2010
+ slug: mount.slug,
2011
+ channelId: channel.id,
2012
+ accountRuntime,
2013
+ });
2014
+ }
2015
+ }
2016
+ }
2017
+ return targets;
2018
+ }
2019
+
2020
+ /**
2021
+ * Origin cursorAccount repos. `true` takes the host's explicit `--repo`
2022
+ * list (not GitHub channel-declared repos). `{ repos }` is self-contained.
2023
+ */
2024
+ function resolveCursorOriginRepoScope(input: {
2025
+ explicitRepos: readonly string[];
2026
+ targets: CursorOriginTarget[];
2027
+ }): ReturnType<typeof parseCursorOriginRepoScope> | undefined {
2028
+ if (input.targets.length === 0) {
2029
+ return undefined;
2030
+ }
2031
+ const wantsHostRepos = input.targets.some(
2032
+ (target) => target.accountRuntime.options.cursorAccount === true
2033
+ );
2034
+ const repos = [
2035
+ ...(wantsHostRepos ? input.explicitRepos : []),
2036
+ ...input.targets.flatMap(
2037
+ (target) => target.accountRuntime.scope?.repos ?? []
2038
+ ),
2039
+ ];
2040
+ return parseCursorOriginRepoScope(repos);
2041
+ }
2042
+
1913
2043
  interface CursorSlackTarget {
1914
2044
  runtime: MountedRuntime;
1915
2045
  channelId: string;