@cursor/july 0.1.55 → 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 (261) 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.CVQxhV85.js → app.CW3KJh72.js} +1 -1
  31. package/dist/docs/assets/chunks/@localSearchIndexroot.BuymfBAo.js +1 -0
  32. package/dist/docs/assets/chunks/{VPLocalSearchBox.QAPzJjql.js → VPLocalSearchBox.Bk6EWpPj.js} +1 -1
  33. package/dist/docs/assets/chunks/{theme.CSxTCOFu.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.D_CCfG8R.js → reference_cli.md.Cl14Jfo3.js} +8 -2
  45. package/dist/docs/assets/{reference_cli.md.D_CCfG8R.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/building-with-agents.html +4 -4
  57. package/dist/docs/concepts.html +5 -5
  58. package/dist/docs/deployment.html +4 -4
  59. package/dist/docs/evals.html +4 -4
  60. package/dist/docs/example-agents/approval-buddy.html +4 -4
  61. package/dist/docs/example-agents/benny.html +4 -4
  62. package/dist/docs/example-agents/bugbot.html +4 -4
  63. package/dist/docs/example-agents/codebase-wiki.html +4 -4
  64. package/dist/docs/example-agents/codeowners-review.html +4 -4
  65. package/dist/docs/example-agents/concierge.html +4 -4
  66. package/dist/docs/example-agents/fsd.html +5 -5
  67. package/dist/docs/example-agents/index.html +4 -4
  68. package/dist/docs/example-agents/knowledge-base.html +4 -4
  69. package/dist/docs/example-agents/oncall.html +4 -4
  70. package/dist/docs/example-agents/security-reviewer.html +5 -5
  71. package/dist/docs/example-agents/slack-agent.html +4 -4
  72. package/dist/docs/example-agents/weather-agent.html +4 -4
  73. package/dist/docs/guides/agent-to-agent.html +4 -4
  74. package/dist/docs/guides/cloud-runtime.html +4 -4
  75. package/dist/docs/guides/convert-automation.html +36 -0
  76. package/dist/docs/guides/github.html +7 -7
  77. package/dist/docs/guides/human-in-the-loop.html +4 -4
  78. package/dist/docs/guides/mcp-oauth.html +4 -4
  79. package/dist/docs/guides/opentelemetry.html +4 -4
  80. package/dist/docs/guides/slack.html +4 -4
  81. package/dist/docs/guides/webhooks.html +4 -4
  82. package/dist/docs/hashmap.json +1 -1
  83. package/dist/docs/hillclimbing.html +4 -4
  84. package/dist/docs/index.html +6 -6
  85. package/dist/docs/quickstart.html +4 -4
  86. package/dist/docs/reference/agent-config.html +4 -4
  87. package/dist/docs/reference/artifacts.html +4 -4
  88. package/dist/docs/reference/channels.html +4 -4
  89. package/dist/docs/reference/cli.html +12 -6
  90. package/dist/docs/reference/connections.html +4 -4
  91. package/dist/docs/reference/hooks.html +4 -4
  92. package/dist/docs/reference/http-api.html +4 -4
  93. package/dist/docs/reference/instructions.html +4 -4
  94. package/dist/docs/reference/playground.html +4 -4
  95. package/dist/docs/reference/project-layout.html +4 -4
  96. package/dist/docs/reference/prompt.html +4 -4
  97. package/dist/docs/reference/schedules.html +4 -4
  98. package/dist/docs/reference/sessions.html +4 -4
  99. package/dist/docs/reference/skills.html +4 -4
  100. package/dist/docs/reference/subagents.html +4 -4
  101. package/dist/docs/reference/tools.html +4 -4
  102. package/dist/docs/scaffolding-agents.html +5 -5
  103. package/dist/docs/storage.html +4 -4
  104. package/dist/docs/templates/agentic-owners.html +38 -0
  105. package/dist/docs/templates/demo.html +28 -0
  106. package/dist/docs/templates/pr-autofixer.html +32 -0
  107. package/dist/docs/templates/security-reviewer.html +26 -0
  108. package/dist/docs/templates/triage.html +5 -5
  109. package/dist/docs/troubleshooting.html +4 -4
  110. package/dist/index.d.ts +3 -3
  111. package/dist/index.d.ts.map +1 -1
  112. package/dist/index.js +2 -2
  113. package/dist/internal/cli-ax.d.ts +2 -0
  114. package/dist/internal/cli-ax.d.ts.map +1 -1
  115. package/dist/internal/cli-ax.js +20 -0
  116. package/dist/internal/cli-slack.d.ts +2 -0
  117. package/dist/internal/cli-slack.d.ts.map +1 -1
  118. package/dist/internal/cli-slack.js +11 -2
  119. package/dist/internal/cursor-event-relay.d.ts +59 -11
  120. package/dist/internal/cursor-event-relay.d.ts.map +1 -1
  121. package/dist/internal/cursor-event-relay.js +133 -26
  122. package/dist/internal/discovery.d.ts.map +1 -1
  123. package/dist/internal/discovery.js +11 -2
  124. package/dist/internal/distribution.d.ts.map +1 -1
  125. package/dist/internal/distribution.js +1 -0
  126. package/dist/internal/handleAgentServeTrigger.d.ts +33 -6
  127. package/dist/internal/handleAgentServeTrigger.d.ts.map +1 -1
  128. package/dist/internal/handleAgentServeTrigger.js +97 -19
  129. package/dist/internal/init-interview.d.ts +67 -0
  130. package/dist/internal/init-interview.d.ts.map +1 -0
  131. package/dist/internal/init-interview.js +300 -0
  132. package/dist/internal/init-project.d.ts +4 -0
  133. package/dist/internal/init-project.d.ts.map +1 -1
  134. package/dist/internal/init-project.js +40 -16
  135. package/dist/internal/mcp-endpoint.d.ts +7 -0
  136. package/dist/internal/mcp-endpoint.d.ts.map +1 -1
  137. package/dist/internal/mcp-endpoint.js +7 -0
  138. package/dist/internal/server.d.ts.map +1 -1
  139. package/dist/internal/server.js +159 -57
  140. package/dist/internal/session-engine.d.ts.map +1 -1
  141. package/dist/internal/session-engine.js +20 -21
  142. package/dist/internal/session-links.d.ts +14 -0
  143. package/dist/internal/session-links.d.ts.map +1 -0
  144. package/dist/internal/session-links.js +69 -0
  145. package/dist/internal/sessions-client.d.ts.map +1 -1
  146. package/dist/internal/sessions-client.js +1 -0
  147. package/dist/playground/assets/index-ClbuaLpm.css +1 -0
  148. package/dist/playground/assets/{index-0oqXUkrR.js → index-SqkR13k_.js} +39 -39
  149. package/dist/playground/index.html +2 -2
  150. package/dist/types.d.ts +19 -12
  151. package/dist/types.d.ts.map +1 -1
  152. package/docs/README.md +15 -0
  153. package/docs/example-agents/fsd.md +3 -0
  154. package/docs/example-agents/security-reviewer.md +3 -0
  155. package/docs/guides/convert-automation.md +176 -0
  156. package/docs/guides/github.md +6 -2
  157. package/docs/reference/cli.md +34 -10
  158. package/docs/scaffolding-agents.md +3 -0
  159. package/docs/templates/agentic-owners.md +97 -0
  160. package/docs/templates/demo.md +84 -0
  161. package/docs/templates/pr-autofixer.md +136 -0
  162. package/docs/templates/security-reviewer.md +89 -0
  163. package/package.json +9 -1
  164. package/skills/github/SKILL.md +4 -5
  165. package/src/bin/agent-serve.ts +13 -6
  166. package/src/channels/github/github-channel.ts +95 -18
  167. package/src/channels/github/types.ts +31 -0
  168. package/src/channels/origin/cursor-account.ts +64 -0
  169. package/src/channels/origin/events.ts +41 -0
  170. package/src/channels/origin/index.ts +29 -0
  171. package/src/channels/origin/origin-channel.ts +784 -0
  172. package/src/channels/origin/origin-webhook.ts +202 -0
  173. package/src/channels/origin/types.ts +186 -0
  174. package/src/channels/slack/doctor.ts +218 -5
  175. package/src/index.ts +3 -1
  176. package/src/internal/cli-ax.ts +23 -0
  177. package/src/internal/cli-slack.ts +11 -1
  178. package/src/internal/cursor-event-relay.ts +193 -31
  179. package/src/internal/discovery.ts +15 -5
  180. package/src/internal/distribution.ts +1 -0
  181. package/src/internal/handleAgentServeTrigger.ts +129 -19
  182. package/src/internal/init-interview.ts +384 -0
  183. package/src/internal/init-project.ts +70 -15
  184. package/src/internal/mcp-endpoint.ts +7 -0
  185. package/src/internal/server.ts +188 -58
  186. package/src/internal/session-engine.ts +5 -21
  187. package/src/internal/session-links.ts +88 -0
  188. package/src/internal/sessions-client.ts +1 -0
  189. package/src/types.ts +19 -12
  190. package/templates/agentic-owners/README.md +73 -0
  191. package/templates/agentic-owners/agent/agent.ts +15 -0
  192. package/templates/agentic-owners/agent/channels/github.ts +85 -0
  193. package/templates/agentic-owners/agent/instructions.md +34 -0
  194. package/templates/agentic-owners/agent/lib/config.ts +13 -0
  195. package/templates/agentic-owners/agent/lib/github.ts +241 -0
  196. package/templates/agentic-owners/agent/lib/owners/README.md +50 -0
  197. package/templates/agentic-owners/agent/lib/owners/constants.ts +22 -0
  198. package/templates/agentic-owners/agent/lib/owners/decision.ts +485 -0
  199. package/templates/agentic-owners/agent/lib/owners/handles.ts +54 -0
  200. package/templates/agentic-owners/agent/lib/owners/match.ts +177 -0
  201. package/templates/agentic-owners/agent/lib/owners/matching.ts +95 -0
  202. package/templates/agentic-owners/agent/lib/owners/parse.ts +589 -0
  203. package/templates/agentic-owners/agent/lib/owners/policyMode.ts +65 -0
  204. package/templates/agentic-owners/agent/lib/owners/prompt.ts +66 -0
  205. package/templates/agentic-owners/agent/lib/owners/prompts/base-review.md +105 -0
  206. package/templates/agentic-owners/agent/lib/owners/types.ts +45 -0
  207. package/templates/agentic-owners/agent/lib/review.ts +777 -0
  208. package/templates/agentic-owners/agent/tools/record_review.ts +69 -0
  209. package/templates/agentic-owners/evals/evals.config.ts +5 -0
  210. package/templates/agentic-owners/evals/owners.eval.ts +116 -0
  211. package/templates/agentic-owners/package.json +20 -0
  212. package/templates/agentic-owners/tsconfig.json +12 -0
  213. package/templates/demo/README.md +33 -0
  214. package/templates/demo/agent/agent.ts +15 -0
  215. package/templates/demo/agent/channels/github.ts +50 -0
  216. package/templates/demo/agent/channels/queue.ts +30 -0
  217. package/templates/demo/agent/instructions.md +8 -0
  218. package/templates/demo/agent/lib/collect.ts +78 -0
  219. package/templates/demo/agent/tools/record_demo.ts +40 -0
  220. package/templates/demo/evals/demo.eval.ts +33 -0
  221. package/templates/demo/evals/evals.config.ts +5 -0
  222. package/templates/demo/package.json +18 -0
  223. package/templates/demo/tsconfig.json +12 -0
  224. package/templates/pr-autofixer/README.md +44 -0
  225. package/templates/pr-autofixer/agent/agent.ts +22 -0
  226. package/templates/pr-autofixer/agent/channels/drive.ts +40 -0
  227. package/templates/pr-autofixer/agent/channels/github.ts +167 -0
  228. package/templates/pr-autofixer/agent/channels/slack.ts +17 -0
  229. package/templates/pr-autofixer/agent/hooks/bind-agent.ts +19 -0
  230. package/templates/pr-autofixer/agent/instructions.md +83 -0
  231. package/templates/pr-autofixer/agent/lib/affinity.ts +33 -0
  232. package/templates/pr-autofixer/agent/lib/drive-schemas.ts +15 -0
  233. package/templates/pr-autofixer/agent/lib/drive.ts +79 -0
  234. package/templates/pr-autofixer/agent/lib/merge-watch.ts +123 -0
  235. package/templates/pr-autofixer/agent/lib/pr-state.ts +54 -0
  236. package/templates/pr-autofixer/agent/lib/repos.ts +19 -0
  237. package/templates/pr-autofixer/agent/lib/start-drive.ts +122 -0
  238. package/templates/pr-autofixer/agent/lib/webhook-buffer.ts +225 -0
  239. package/templates/pr-autofixer/agent/lib/webhook-routing.ts +231 -0
  240. package/templates/pr-autofixer/agent/storage.ts +7 -0
  241. package/templates/pr-autofixer/agent/tools/drive_pr.ts +22 -0
  242. package/templates/pr-autofixer/evals/coordinator.eval.ts +16 -0
  243. package/templates/pr-autofixer/evals/evals.config.ts +5 -0
  244. package/templates/pr-autofixer/init.json +25 -0
  245. package/templates/pr-autofixer/package.json +18 -0
  246. package/templates/pr-autofixer/tsconfig.json +12 -0
  247. package/templates/security-reviewer/README.md +31 -0
  248. package/templates/security-reviewer/agent/agent.ts +15 -0
  249. package/templates/security-reviewer/agent/channels/github.ts +46 -0
  250. package/templates/security-reviewer/agent/instructions.md +22 -0
  251. package/templates/security-reviewer/agent/lib/repos.ts +5 -0
  252. package/templates/security-reviewer/agent/tools/post_findings.ts +72 -0
  253. package/templates/security-reviewer/evals/evals.config.ts +5 -0
  254. package/templates/security-reviewer/evals/review.eval.ts +53 -0
  255. package/templates/security-reviewer/init.json +25 -0
  256. package/templates/security-reviewer/package.json +18 -0
  257. package/templates/security-reviewer/tsconfig.json +12 -0
  258. package/dist/docs/assets/chunks/@localSearchIndexroot.BFpWSo6D.js +0 -1
  259. package/dist/docs/assets/index.md.COeoSkcF.js +0 -5
  260. package/dist/docs/assets/scaffolding-agents.md.mkc3B_ZW.js +0 -1
  261. package/dist/playground/assets/index-tHCjJxv4.css +0 -1
@@ -0,0 +1,384 @@
1
+ /**
2
+ * Template `init.json`: questions `init` asks, and string replacements
3
+ * it applies while copying the scaffold.
4
+ */
5
+
6
+ import { readFile } from "node:fs/promises";
7
+ import { join } from "node:path";
8
+ import { createInterface } from "node:readline";
9
+ import { parseCursorGithubRepoScope } from "../channels/github/cursor-account.js";
10
+ import { describeError } from "./describe-error.js";
11
+
12
+ export const TEMPLATE_INIT_FILE = "init.json";
13
+
14
+ export interface TemplateQuestion {
15
+ id: string;
16
+ prompt: string;
17
+ hint?: string;
18
+ placeholder?: string;
19
+ default?: string;
20
+ choices?: readonly string[];
21
+ parse?: "owner-repo-list";
22
+ }
23
+
24
+ export interface TemplateReplace {
25
+ file: string;
26
+ find: string;
27
+ answer: string;
28
+ as?: "json-array" | "first";
29
+ template?: string;
30
+ }
31
+
32
+ export interface TemplateInitSpec {
33
+ questions: TemplateQuestion[];
34
+ replace: TemplateReplace[];
35
+ }
36
+
37
+ export function posixRel(rel: string): string {
38
+ return rel.replaceAll("\\", "/");
39
+ }
40
+
41
+ export function isTemplateMetaPath(rel: string): boolean {
42
+ const normalized = posixRel(rel);
43
+ const top = normalized.split("/")[0];
44
+ if (top === "overlays") {
45
+ return true;
46
+ }
47
+ return (
48
+ normalized === TEMPLATE_INIT_FILE ||
49
+ normalized.endsWith(`/${TEMPLATE_INIT_FILE}`)
50
+ );
51
+ }
52
+
53
+ export function templateFileHasReplacements(
54
+ spec: TemplateInitSpec,
55
+ rel: string
56
+ ): boolean {
57
+ const file = posixRel(rel);
58
+ return spec.replace.some((rule) => posixRel(rule.file) === file);
59
+ }
60
+
61
+ export async function loadTemplateInitSpec(
62
+ templateDir: string
63
+ ): Promise<TemplateInitSpec | undefined> {
64
+ try {
65
+ const text = await readFile(join(templateDir, TEMPLATE_INIT_FILE), "utf8");
66
+ return parseTemplateInitSpec(JSON.parse(text));
67
+ } catch (error) {
68
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") {
69
+ return undefined;
70
+ }
71
+ throw error;
72
+ }
73
+ }
74
+
75
+ export function parseTemplateInitSpec(value: unknown): TemplateInitSpec {
76
+ if (!isRecord(value) || !Array.isArray(value.questions)) {
77
+ throw new Error(`${TEMPLATE_INIT_FILE} must have a questions array`);
78
+ }
79
+ const questions = value.questions.map((item, index) =>
80
+ parseQuestion(item, index)
81
+ );
82
+ const replace = Array.isArray(value.replace)
83
+ ? value.replace.map((item, index) => parseReplace(item, index))
84
+ : [];
85
+ return { questions, replace };
86
+ }
87
+
88
+ export function parseInitVars(
89
+ flags: readonly string[] | undefined
90
+ ): Record<string, string> {
91
+ const vars: Record<string, string> = {};
92
+ if (flags === undefined) {
93
+ return vars;
94
+ }
95
+ for (const flag of flags) {
96
+ const eq = flag.indexOf("=");
97
+ if (eq <= 0) {
98
+ throw new Error(`--var must be id=value (got ${JSON.stringify(flag)})`);
99
+ }
100
+ vars[flag.slice(0, eq)] = flag.slice(eq + 1);
101
+ }
102
+ return vars;
103
+ }
104
+
105
+ export type ParseAnswersResult =
106
+ | { ok: true; answers: Record<string, string> }
107
+ | { ok: false; error: string };
108
+
109
+ export type RunTemplateInitResult =
110
+ | { ok: true; spec?: TemplateInitSpec; answers?: Record<string, string> }
111
+ | { ok: false; error: string };
112
+
113
+ /** Load `init.json`, apply `--var`, and prompt on a TTY. */
114
+ export async function runTemplateInit(input: {
115
+ templateDir: string;
116
+ vars?: readonly string[];
117
+ interactive: boolean;
118
+ }): Promise<RunTemplateInitResult> {
119
+ try {
120
+ const spec = await loadTemplateInitSpec(input.templateDir);
121
+ if (spec === undefined) {
122
+ return { ok: true };
123
+ }
124
+ const collected = await collectTemplateAnswers({
125
+ spec,
126
+ vars: parseInitVars(input.vars),
127
+ interactive: input.interactive,
128
+ });
129
+ if (!collected.ok) {
130
+ return collected;
131
+ }
132
+ return { ok: true, spec, answers: collected.answers };
133
+ } catch (error) {
134
+ return { ok: false, error: describeError(error) };
135
+ }
136
+ }
137
+
138
+ export function parseTemplateAnswers(
139
+ spec: TemplateInitSpec,
140
+ raw: Record<string, string>
141
+ ): ParseAnswersResult {
142
+ const answers: Record<string, string> = {};
143
+ for (const question of spec.questions) {
144
+ if (!(question.id in raw)) {
145
+ continue;
146
+ }
147
+ const parsed = parseQuestionValue(question, raw[question.id] ?? "");
148
+ if (!parsed.ok) {
149
+ return parsed;
150
+ }
151
+ answers[question.id] = parsed.value;
152
+ }
153
+ return { ok: true, answers };
154
+ }
155
+
156
+ export interface CollectAnswersInput {
157
+ spec: TemplateInitSpec;
158
+ vars?: Record<string, string>;
159
+ interactive: boolean;
160
+ ask?: (question: TemplateQuestion) => Promise<string>;
161
+ out?: (text: string) => void;
162
+ }
163
+
164
+ export async function collectTemplateAnswers(
165
+ input: CollectAnswersInput
166
+ ): Promise<ParseAnswersResult> {
167
+ const vars = input.vars ?? {};
168
+ const answers: Record<string, string> = {};
169
+ const ask = input.ask ?? promptTemplateQuestion;
170
+ const out = input.out ?? ((text) => process.stdout.write(text));
171
+
172
+ for (const question of input.spec.questions) {
173
+ if (question.id in vars) {
174
+ const parsed = parseQuestionValue(question, vars[question.id] ?? "");
175
+ if (!parsed.ok) {
176
+ return parsed;
177
+ }
178
+ answers[question.id] = parsed.value;
179
+ continue;
180
+ }
181
+ if (!input.interactive) {
182
+ continue;
183
+ }
184
+ for (;;) {
185
+ const line = await ask(question);
186
+ const value =
187
+ line.trim() === "" && question.default !== undefined
188
+ ? question.default
189
+ : line;
190
+ const parsed = parseQuestionValue(question, value);
191
+ if (parsed.ok) {
192
+ answers[question.id] = parsed.value;
193
+ break;
194
+ }
195
+ out(`${parsed.error}\n`);
196
+ }
197
+ }
198
+
199
+ return { ok: true, answers };
200
+ }
201
+
202
+ export function applyInitReplacements(
203
+ contents: string,
204
+ rel: string,
205
+ spec: TemplateInitSpec,
206
+ answers: Record<string, string>
207
+ ): string {
208
+ const file = posixRel(rel);
209
+ let next = contents;
210
+ for (const rule of spec.replace) {
211
+ if (posixRel(rule.file) !== file) {
212
+ continue;
213
+ }
214
+ const value = formatReplaceValue(rule, answers);
215
+ if (value === undefined) {
216
+ continue;
217
+ }
218
+ if (!next.includes(rule.find)) {
219
+ throw new Error(
220
+ `${TEMPLATE_INIT_FILE}: ${rule.find} not found in ${rel}`
221
+ );
222
+ }
223
+ next = replaceLiteral(next, rule.find, value);
224
+ }
225
+ return next;
226
+ }
227
+
228
+ export async function promptTemplateQuestion(
229
+ question: TemplateQuestion,
230
+ options: {
231
+ input?: NodeJS.ReadableStream;
232
+ output?: NodeJS.WritableStream;
233
+ } = {}
234
+ ): Promise<string> {
235
+ const input = options.input ?? process.stdin;
236
+ const output = options.output ?? process.stdout;
237
+ const rl = createInterface({ input, output });
238
+ const suffix = promptSuffix(question);
239
+ try {
240
+ if (question.hint !== undefined) {
241
+ output.write(` ${question.hint}\n`);
242
+ }
243
+ return await new Promise<string>((resolve) => {
244
+ rl.question(`${question.prompt}${suffix} `, resolve);
245
+ });
246
+ } finally {
247
+ rl.close();
248
+ }
249
+ }
250
+
251
+ function replaceLiteral(haystack: string, find: string, value: string): string {
252
+ return haystack.replaceAll(find, () => value);
253
+ }
254
+
255
+ function promptSuffix(question: TemplateQuestion): string {
256
+ if (question.choices !== undefined && question.choices.length > 0) {
257
+ return ` [${question.choices.join("/")}]`;
258
+ }
259
+ if (question.default !== undefined) {
260
+ return ` [${question.default}]`;
261
+ }
262
+ if (question.placeholder !== undefined) {
263
+ return ` (${question.placeholder})`;
264
+ }
265
+ return "";
266
+ }
267
+
268
+ function parseQuestionValue(
269
+ question: TemplateQuestion,
270
+ raw: string
271
+ ): { ok: true; value: string } | { ok: false; error: string } {
272
+ const trimmed = raw.trim();
273
+ if (trimmed === "") {
274
+ return { ok: false, error: `${question.id} is required.` };
275
+ }
276
+ if (question.choices !== undefined && question.choices.length > 0) {
277
+ if (!question.choices.includes(trimmed)) {
278
+ return {
279
+ ok: false,
280
+ error: `${question.id} must be one of ${question.choices.join(", ")}.`,
281
+ };
282
+ }
283
+ return { ok: true, value: trimmed };
284
+ }
285
+ if (question.parse === "owner-repo-list") {
286
+ try {
287
+ const scope = parseCursorGithubRepoScope(
288
+ trimmed.split(",").map((repo) => repo.trim())
289
+ );
290
+ return { ok: true, value: scope.repos.join(", ") };
291
+ } catch (error) {
292
+ return { ok: false, error: describeError(error) };
293
+ }
294
+ }
295
+ return { ok: true, value: trimmed };
296
+ }
297
+
298
+ function formatReplaceValue(
299
+ rule: TemplateReplace,
300
+ answers: Record<string, string>
301
+ ): string | undefined {
302
+ const raw = answers[rule.answer];
303
+ if (raw === undefined || raw === "") {
304
+ return undefined;
305
+ }
306
+ const parts = raw.split(",").map((part) => part.trim());
307
+ let value = raw;
308
+ if (rule.as === "json-array") {
309
+ value = `[${parts.map((part) => JSON.stringify(part)).join(", ")}]`;
310
+ } else if (rule.as === "first") {
311
+ const first = parts[0];
312
+ if (first === undefined || first === "") {
313
+ return undefined;
314
+ }
315
+ value = first;
316
+ }
317
+ if (rule.template !== undefined) {
318
+ return replaceLiteral(rule.template, "{{value}}", value);
319
+ }
320
+ return value;
321
+ }
322
+
323
+ function parseQuestion(value: unknown, index: number): TemplateQuestion {
324
+ if (!isRecord(value) || typeof value.id !== "string" || value.id === "") {
325
+ throw new Error(`${TEMPLATE_INIT_FILE} questions[${index}] needs an id`);
326
+ }
327
+ if (typeof value.prompt !== "string" || value.prompt === "") {
328
+ throw new Error(
329
+ `${TEMPLATE_INIT_FILE} questions[${value.id}] needs a prompt`
330
+ );
331
+ }
332
+ const question: TemplateQuestion = {
333
+ id: value.id,
334
+ prompt: value.prompt,
335
+ };
336
+ if (typeof value.hint === "string") {
337
+ question.hint = value.hint;
338
+ }
339
+ if (typeof value.placeholder === "string") {
340
+ question.placeholder = value.placeholder;
341
+ }
342
+ if (typeof value.default === "string") {
343
+ question.default = value.default;
344
+ }
345
+ if (Array.isArray(value.choices)) {
346
+ question.choices = value.choices.map((choice) => String(choice));
347
+ }
348
+ if (value.parse === "owner-repo-list") {
349
+ question.parse = "owner-repo-list";
350
+ }
351
+ return question;
352
+ }
353
+
354
+ function parseReplace(value: unknown, index: number): TemplateReplace {
355
+ if (
356
+ !isRecord(value) ||
357
+ typeof value.file !== "string" ||
358
+ value.file === "" ||
359
+ typeof value.find !== "string" ||
360
+ value.find === "" ||
361
+ typeof value.answer !== "string" ||
362
+ value.answer === ""
363
+ ) {
364
+ throw new Error(
365
+ `${TEMPLATE_INIT_FILE} replace[${index}] needs file, find, and answer`
366
+ );
367
+ }
368
+ const rule: TemplateReplace = {
369
+ file: value.file,
370
+ find: value.find,
371
+ answer: value.answer,
372
+ };
373
+ if (value.as === "json-array" || value.as === "first") {
374
+ rule.as = value.as;
375
+ }
376
+ if (typeof value.template === "string") {
377
+ rule.template = value.template;
378
+ }
379
+ return rule;
380
+ }
381
+
382
+ function isRecord(value: unknown): value is Record<string, unknown> {
383
+ return typeof value === "object" && value !== null && !Array.isArray(value);
384
+ }
@@ -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,
@@ -29,6 +37,9 @@ export interface InitProjectOptions {
29
37
  templateDir?: string;
30
38
  overlayDir?: string;
31
39
  templateName?: string;
40
+ /** Answers collected from `init.json` / `--var`. */
41
+ answers?: Record<string, string>;
42
+ initSpec?: TemplateInitSpec;
32
43
  }
33
44
 
34
45
  export type InitFileAction = "create" | "exist";
@@ -48,9 +59,18 @@ export interface InitProjectResult {
48
59
  skipped: string[];
49
60
  }
50
61
 
51
- const TRIAGE_OVERLAYS: Readonly<Record<string, string>> = {
52
- "triage-linear": "linear",
53
- "triage-jira": "jira",
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" },
54
74
  };
55
75
 
56
76
  function canonicalTemplateName(name: string): string {
@@ -58,7 +78,7 @@ function canonicalTemplateName(name: string): string {
58
78
  }
59
79
 
60
80
  export function availableTemplates(): string[] {
61
- return Object.keys(TRIAGE_OVERLAYS).sort();
81
+ return Object.keys(TEMPLATES).sort();
62
82
  }
63
83
 
64
84
  export type TemplateResolution =
@@ -73,19 +93,22 @@ export function resolveTemplate(name: string): TemplateResolution {
73
93
  };
74
94
  }
75
95
  const resolved = canonicalTemplateName(name);
76
- const overlay = TRIAGE_OVERLAYS[resolved];
77
- if (overlay !== undefined) {
78
- const dir = join(packageRootDir(), "templates", "triage");
96
+ const entry = TEMPLATES[resolved];
97
+ if (entry === undefined) {
79
98
  return {
80
- ok: true,
81
- name: resolved,
82
- dir,
83
- overlayDir: join(dir, "overlays", overlay),
99
+ ok: false,
100
+ message: `unknown template "${name}" (available: ${availableTemplates().join(", ")})`,
84
101
  };
85
102
  }
103
+ const dir = join(packageRootDir(), "templates", entry.dir);
86
104
  return {
87
- ok: false,
88
- message: `unknown template "${name}" (available: ${availableTemplates().join(", ")})`,
105
+ ok: true,
106
+ name: resolved,
107
+ dir,
108
+ overlayDir:
109
+ entry.overlay === undefined
110
+ ? undefined
111
+ : join(dir, "overlays", entry.overlay),
89
112
  };
90
113
  }
91
114
 
@@ -207,6 +230,9 @@ export async function initProject(
207
230
  (rel) => !overlaySet.has(rel)
208
231
  );
209
232
  const templateName = options.templateName ?? basename(templateDir);
233
+ const initSpec =
234
+ options.initSpec ?? (await loadTemplateInitSpec(templateDir));
235
+ const answers = options.answers ?? {};
210
236
  for (const rel of corePaths) {
211
237
  await writeTemplateFile({
212
238
  write,
@@ -214,6 +240,8 @@ export async function initProject(
214
240
  rel,
215
241
  projectName,
216
242
  templateName,
243
+ initSpec,
244
+ answers,
217
245
  });
218
246
  }
219
247
  if (overlayDir !== undefined) {
@@ -224,6 +252,8 @@ export async function initProject(
224
252
  rel,
225
253
  projectName,
226
254
  templateName,
255
+ initSpec,
256
+ answers,
227
257
  });
228
258
  }
229
259
  }
@@ -362,8 +392,10 @@ async function listTemplateFiles(root: string): Promise<string[]> {
362
392
  });
363
393
  return entries
364
394
  .filter((entry) => entry.isFile())
365
- .map((entry) => relative(root, join(entry.parentPath, entry.name)))
366
- .filter((rel) => rel.split(/[\\/]/)[0] !== "overlays")
395
+ .map((entry) =>
396
+ posixRel(relative(root, join(entry.parentPath, entry.name)))
397
+ )
398
+ .filter((rel) => !isTemplateMetaPath(rel))
367
399
  .sort();
368
400
  }
369
401
 
@@ -373,6 +405,8 @@ async function writeTemplateFile(input: {
373
405
  rel: string;
374
406
  projectName: string;
375
407
  templateName: string;
408
+ initSpec?: TemplateInitSpec;
409
+ answers: Record<string, string>;
376
410
  }): Promise<void> {
377
411
  const source = join(input.sourceDir, input.rel);
378
412
  if (input.rel === "package.json") {
@@ -387,6 +421,21 @@ async function writeTemplateFile(input: {
387
421
  await input.write(input.rel, `${JSON.stringify(packageJson, null, 2)}\n`);
388
422
  return;
389
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
+ }
390
439
  await input.write(input.rel, await readFile(source));
391
440
  }
392
441
 
@@ -784,8 +833,14 @@ export function printInitGuide(): void {
784
833
 
785
834
  Scaffold a project, then open the playground:
786
835
  npx ${PACKAGE_NAME} init ./my-agent # installs deps + links agent-sdk onto PATH
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
787
838
  npx ${PACKAGE_NAME} init ./my-triage --template triage-linear # comment on Linear issues
788
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
789
844
  cd ./my-agent && agent-sdk dev # or: npx ${PACKAGE_NAME} dev
790
845
 
791
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;