@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.
- package/dist/bin/agent-serve.js +30 -23
- package/dist/channels/github/github-channel.d.ts +12 -0
- package/dist/channels/github/github-channel.d.ts.map +1 -1
- package/dist/channels/github/github-channel.js +67 -15
- package/dist/channels/github/types.d.ts +27 -1
- package/dist/channels/github/types.d.ts.map +1 -1
- package/dist/channels/origin/cursor-account.d.ts +22 -0
- package/dist/channels/origin/cursor-account.d.ts.map +1 -0
- package/dist/channels/origin/cursor-account.js +35 -0
- package/dist/channels/origin/events.d.ts +10 -0
- package/dist/channels/origin/events.d.ts.map +1 -0
- package/dist/channels/origin/events.js +27 -0
- package/dist/channels/origin/index.d.ts +29 -0
- package/dist/channels/origin/index.d.ts.map +1 -0
- package/dist/channels/origin/index.js +28 -0
- package/dist/channels/origin/origin-channel.d.ts +68 -0
- package/dist/channels/origin/origin-channel.d.ts.map +1 -0
- package/dist/channels/origin/origin-channel.js +567 -0
- package/dist/channels/origin/origin-webhook.d.ts +38 -0
- package/dist/channels/origin/origin-webhook.d.ts.map +1 -0
- package/dist/channels/origin/origin-webhook.js +151 -0
- package/dist/channels/origin/types.d.ts +152 -0
- package/dist/channels/origin/types.d.ts.map +1 -0
- package/dist/channels/origin/types.js +1 -0
- package/dist/channels/slack/doctor.d.ts +17 -1
- package/dist/channels/slack/doctor.d.ts.map +1 -1
- package/dist/channels/slack/doctor.js +186 -10
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +4 -4
- package/dist/docs/assets/{app.CVQxhV85.js → app.CW3KJh72.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.BuymfBAo.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.QAPzJjql.js → VPLocalSearchBox.Bk6EWpPj.js} +1 -1
- package/dist/docs/assets/chunks/{theme.CSxTCOFu.js → theme.Bz9AodBA.js} +2 -2
- package/dist/docs/assets/{example-agents_fsd.md.DPz9ezO4.js → example-agents_fsd.md.ZeGEpAw_.js} +2 -2
- package/dist/docs/assets/{example-agents_fsd.md.DPz9ezO4.lean.js → example-agents_fsd.md.ZeGEpAw_.lean.js} +1 -1
- package/dist/docs/assets/{example-agents_security-reviewer.md.Dkf1gyo6.js → example-agents_security-reviewer.md.DBL1TwtP.js} +2 -2
- package/dist/docs/assets/{example-agents_security-reviewer.md.Dkf1gyo6.lean.js → example-agents_security-reviewer.md.DBL1TwtP.lean.js} +1 -1
- package/dist/docs/assets/guides_convert-automation.md.BhMzTkE5.js +12 -0
- package/dist/docs/assets/guides_convert-automation.md.BhMzTkE5.lean.js +1 -0
- package/dist/docs/assets/{guides_github.md.B8QKtIpw.js → guides_github.md.CLLQJKRB.js} +4 -4
- package/dist/docs/assets/{guides_github.md.B8QKtIpw.lean.js → guides_github.md.CLLQJKRB.lean.js} +1 -1
- package/dist/docs/assets/index.md.DhRHS_-L.js +5 -0
- package/dist/docs/assets/{index.md.COeoSkcF.lean.js → index.md.DhRHS_-L.lean.js} +1 -1
- package/dist/docs/assets/{reference_cli.md.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 +59 -11
- package/dist/internal/cursor-event-relay.d.ts.map +1 -1
- package/dist/internal/cursor-event-relay.js +133 -26
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +11 -2
- package/dist/internal/distribution.d.ts.map +1 -1
- package/dist/internal/distribution.js +1 -0
- package/dist/internal/handleAgentServeTrigger.d.ts +33 -6
- package/dist/internal/handleAgentServeTrigger.d.ts.map +1 -1
- package/dist/internal/handleAgentServeTrigger.js +97 -19
- package/dist/internal/init-interview.d.ts +67 -0
- package/dist/internal/init-interview.d.ts.map +1 -0
- package/dist/internal/init-interview.js +300 -0
- package/dist/internal/init-project.d.ts +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/server.d.ts.map +1 -1
- package/dist/internal/server.js +159 -57
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +20 -21
- package/dist/internal/session-links.d.ts +14 -0
- package/dist/internal/session-links.d.ts.map +1 -0
- package/dist/internal/session-links.js +69 -0
- package/dist/internal/sessions-client.d.ts.map +1 -1
- package/dist/internal/sessions-client.js +1 -0
- package/dist/playground/assets/index-ClbuaLpm.css +1 -0
- package/dist/playground/assets/{index-0oqXUkrR.js → index-SqkR13k_.js} +39 -39
- 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 +4 -5
- package/src/bin/agent-serve.ts +13 -6
- package/src/channels/github/github-channel.ts +95 -18
- package/src/channels/github/types.ts +31 -0
- package/src/channels/origin/cursor-account.ts +64 -0
- package/src/channels/origin/events.ts +41 -0
- package/src/channels/origin/index.ts +29 -0
- package/src/channels/origin/origin-channel.ts +784 -0
- package/src/channels/origin/origin-webhook.ts +202 -0
- package/src/channels/origin/types.ts +186 -0
- package/src/channels/slack/doctor.ts +218 -5
- package/src/index.ts +3 -1
- package/src/internal/cli-ax.ts +23 -0
- package/src/internal/cli-slack.ts +11 -1
- package/src/internal/cursor-event-relay.ts +193 -31
- package/src/internal/discovery.ts +15 -5
- package/src/internal/distribution.ts +1 -0
- package/src/internal/handleAgentServeTrigger.ts +129 -19
- package/src/internal/init-interview.ts +384 -0
- package/src/internal/init-project.ts +70 -15
- package/src/internal/mcp-endpoint.ts +7 -0
- package/src/internal/server.ts +188 -58
- package/src/internal/session-engine.ts +5 -21
- package/src/internal/session-links.ts +88 -0
- package/src/internal/sessions-client.ts +1 -0
- package/src/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,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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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(
|
|
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
|
|
77
|
-
if (
|
|
78
|
-
const dir = join(packageRootDir(), "templates", "triage");
|
|
96
|
+
const entry = TEMPLATES[resolved];
|
|
97
|
+
if (entry === undefined) {
|
|
79
98
|
return {
|
|
80
|
-
ok:
|
|
81
|
-
name:
|
|
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:
|
|
88
|
-
|
|
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) =>
|
|
366
|
-
|
|
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;
|