@rafaeltab/sandcastle 0.13.0-fork.0
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/LICENSE +21 -0
- package/README.md +1410 -0
- package/dist/MountConfig-CmXclHA5.d.ts +26 -0
- package/dist/SandboxProvider-EkSMuBp8.d.ts +243 -0
- package/dist/chunk-62WN33RK.js +104 -0
- package/dist/chunk-62WN33RK.js.map +1 -0
- package/dist/chunk-BBMMM5CR.js +23 -0
- package/dist/chunk-BBMMM5CR.js.map +1 -0
- package/dist/chunk-BIWNFKGV.js +22 -0
- package/dist/chunk-BIWNFKGV.js.map +1 -0
- package/dist/chunk-CP3TYXZA.js +362 -0
- package/dist/chunk-CP3TYXZA.js.map +1 -0
- package/dist/chunk-DJRHWPEH.js +25569 -0
- package/dist/chunk-DJRHWPEH.js.map +1 -0
- package/dist/chunk-NGBM7T3E.js +76 -0
- package/dist/chunk-NGBM7T3E.js.map +1 -0
- package/dist/chunk-VOG34SRF.js +27012 -0
- package/dist/chunk-VOG34SRF.js.map +1 -0
- package/dist/index.d.ts +1051 -0
- package/dist/index.js +3485 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +19367 -0
- package/dist/main.js.map +1 -0
- package/dist/mountUtils-CCA-bbpK.d.ts +25 -0
- package/dist/sandboxes/daytona.d.ts +60 -0
- package/dist/sandboxes/daytona.js +136 -0
- package/dist/sandboxes/daytona.js.map +1 -0
- package/dist/sandboxes/docker.d.ts +110 -0
- package/dist/sandboxes/docker.js +9 -0
- package/dist/sandboxes/docker.js.map +1 -0
- package/dist/sandboxes/no-sandbox.d.ts +38 -0
- package/dist/sandboxes/no-sandbox.js +7 -0
- package/dist/sandboxes/no-sandbox.js.map +1 -0
- package/dist/sandboxes/podman.d.ts +124 -0
- package/dist/sandboxes/podman.js +299 -0
- package/dist/sandboxes/podman.js.map +1 -0
- package/dist/sandboxes/vercel.d.ts +104 -0
- package/dist/sandboxes/vercel.js +189 -0
- package/dist/sandboxes/vercel.js.map +1 -0
- package/dist/templates/blank/main.mts +12 -0
- package/dist/templates/blank/prompt.md +12 -0
- package/dist/templates/blank/template.json +4 -0
- package/dist/templates/parallel-planner/implement-prompt.md +62 -0
- package/dist/templates/parallel-planner/main.mts +204 -0
- package/dist/templates/parallel-planner/merge-prompt.md +26 -0
- package/dist/templates/parallel-planner/plan-prompt.md +37 -0
- package/dist/templates/parallel-planner/template.json +4 -0
- package/dist/templates/parallel-planner-with-review/CODING_STANDARDS.md +27 -0
- package/dist/templates/parallel-planner-with-review/implement-prompt.md +62 -0
- package/dist/templates/parallel-planner-with-review/main.mts +226 -0
- package/dist/templates/parallel-planner-with-review/merge-prompt.md +26 -0
- package/dist/templates/parallel-planner-with-review/plan-prompt.md +37 -0
- package/dist/templates/parallel-planner-with-review/review-prompt.md +55 -0
- package/dist/templates/parallel-planner-with-review/template.json +4 -0
- package/dist/templates/sequential-reviewer/CODING_STANDARDS.md +27 -0
- package/dist/templates/sequential-reviewer/implement-prompt.md +53 -0
- package/dist/templates/sequential-reviewer/main.mts +119 -0
- package/dist/templates/sequential-reviewer/review-prompt.md +55 -0
- package/dist/templates/sequential-reviewer/template.json +4 -0
- package/dist/templates/simple-loop/main.mts +50 -0
- package/dist/templates/simple-loop/prompt.md +53 -0
- package/dist/templates/simple-loop/template.json +4 -0
- package/package.json +107 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/sandboxes/vercel.ts"],"names":["result"],"mappings":"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","file":"vercel.js","sourcesContent":["/**\n * Vercel isolated sandbox provider — wraps `@vercel/sandbox` into a SandboxProvider.\n *\n * Usage:\n * import { vercel } from \"sandcastle/sandboxes/vercel\";\n * await run({ agent: claudeCode(\"claude-opus-4-8\"), sandbox: vercel() });\n */\n\nimport { execSync } from \"node:child_process\";\nimport { readFile, unlink, writeFile, mkdir, stat } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join, posix } from \"node:path\";\nimport { Writable } from \"node:stream\";\nimport {\n createIsolatedSandboxProvider,\n type ExecResult,\n type IsolatedSandboxHandle,\n type IsolatedSandboxProvider,\n} from \"../SandboxProvider.js\";\nimport { BoundedTail, MAX_TAIL_CHARS } from \"../boundedTail.js\";\nimport { redirectCommandStdinFromFile, withStdinFile } from \"./stdin-file.js\";\n\n/** Worktree directory inside the Vercel sandbox's default working directory. */\nconst VERCEL_WORKTREE_DIR = \"workspace\";\n\n/**\n * Options for creating a Vercel sandbox provider.\n *\n * All `@vercel/sandbox` `Sandbox.create()` options are accepted as pass-through,\n * plus Sandcastle-specific options for auth and branch strategy.\n */\nexport interface VercelOptions {\n /**\n * Vercel access token.\n *\n * Falls back to the SDK's default auth behavior, which reads\n * `VERCEL_OIDC_TOKEN` (recommended for Vercel-hosted environments) or\n * `VERCEL_TOKEN` from the environment.\n */\n readonly token?: string;\n\n // ---- Pass-through @vercel/sandbox Sandbox.create() options ----\n\n /**\n * The source of the sandbox (git repo, tarball, or snapshot).\n * Omit to start an empty sandbox.\n */\n readonly source?:\n | {\n type: \"git\";\n url: string;\n depth?: number;\n revision?: string;\n username?: string;\n password?: string;\n }\n | {\n type: \"tarball\";\n url: string;\n }\n | {\n type: \"snapshot\";\n snapshotId: string;\n };\n\n /** Array of port numbers to expose from the sandbox (up to 4). */\n readonly ports?: number[];\n\n /** Timeout in milliseconds before the sandbox auto-terminates. */\n readonly timeout?: number;\n\n /**\n * Resources to allocate to the sandbox.\n * Each vCPU gets 2048 MB of memory.\n */\n readonly resources?: {\n vcpus: number;\n };\n\n /**\n * The runtime of the sandbox (e.g. `\"node24\"`, `\"node22\"`, `\"python3.13\"`).\n * Defaults to `\"node24\"`.\n */\n readonly runtime?: string;\n\n /**\n * Network policy for the sandbox.\n * Defaults to full internet access if not specified.\n */\n readonly networkPolicy?: Record<string, unknown>;\n\n /**\n * Vercel project ID to associate sandbox operations with.\n */\n readonly projectId?: string;\n\n /**\n * Vercel team ID to associate sandbox operations with.\n */\n readonly teamId?: string;\n\n /**\n * Timeout in milliseconds (alias for `timeout`, kept for discoverability).\n */\n readonly timeoutMs?: number;\n\n /**\n * Sandbox template shorthand (e.g. `\"node-22\"`).\n * Maps to the `runtime` option.\n */\n readonly template?: string;\n\n /** Environment variables injected by this provider. Merged at launch time with env resolver and agent provider env. */\n readonly env?: Record<string, string>;\n\n /**\n * Maximum number of characters of streamed `exec` output retained per stream\n * (stdout and stderr) when an `onLine` callback is supplied (default: 64KiB).\n *\n * Output is delivered live to `onLine` regardless; this only bounds the tail\n * returned in `ExecResult`, preventing a long-running agent's output from\n * overflowing V8's max string length and crashing the run.\n */\n readonly maxOutputTailChars?: number;\n}\n\n/**\n * Create a Vercel isolated sandbox provider.\n *\n * The returned provider creates Vercel Firecracker microVM sandboxes via\n * the `@vercel/sandbox` SDK. Each sandbox is ephemeral — one sandbox per run.\n *\n * Requires `@vercel/sandbox` to be installed as a peer dependency.\n */\nexport const vercel = (options?: VercelOptions): IsolatedSandboxProvider =>\n createIsolatedSandboxProvider({\n name: \"vercel\",\n env: options?.env,\n create: async (createOptions): Promise<IsolatedSandboxHandle> => {\n const maxOutputTailChars = options?.maxOutputTailChars ?? MAX_TAIL_CHARS;\n // Dynamic import so the peer dependency is only loaded at runtime\n const { Sandbox } = await import(\"@vercel/sandbox\");\n\n const createParams: Record<string, unknown> = {};\n\n // Pass through SDK options\n if (options?.source) createParams.source = options.source;\n if (options?.ports) createParams.ports = options.ports;\n if (options?.resources) createParams.resources = options.resources;\n if (options?.networkPolicy)\n createParams.networkPolicy = options.networkPolicy;\n // runtime takes precedence over the template convenience alias\n const resolvedRuntime = options?.runtime ?? options?.template;\n if (resolvedRuntime) createParams.runtime = resolvedRuntime;\n\n // Timeout: prefer explicit timeout, fall back to timeoutMs alias\n const timeoutValue = options?.timeout ?? options?.timeoutMs;\n if (timeoutValue !== undefined) createParams.timeout = timeoutValue;\n\n // Merge provider env with Sandcastle env\n createParams.env = createOptions.env;\n\n // Auth: pass token and team/project IDs if provided\n if (options?.token) createParams.token = options.token;\n if (options?.projectId) createParams.projectId = options.projectId;\n if (options?.teamId) createParams.teamId = options.teamId;\n\n const sandbox = await Sandbox.create(\n createParams as Parameters<typeof Sandbox.create>[0],\n );\n\n let worktreePath: string;\n try {\n // Vercel SDK 2.x starts in /vercel/sandbox, while 3.x starts in\n // /vercel. Discover the default rather than depending on image layout.\n const pwdResult = await sandbox.runCommand({ cmd: \"pwd\" });\n const defaultCwd = (await pwdResult.stdout()).trim();\n if (pwdResult.exitCode !== 0 || !posix.isAbsolute(defaultCwd)) {\n const stderr = await pwdResult.stderr();\n throw new Error(\n `Could not determine Vercel sandbox working directory: ${stderr || defaultCwd}`,\n );\n }\n\n worktreePath = posix.join(defaultCwd, VERCEL_WORKTREE_DIR);\n const mkdirResult = await sandbox.runCommand({\n cmd: \"mkdir\",\n args: [\"-p\", worktreePath],\n });\n if (mkdirResult.exitCode !== 0) {\n throw new Error(\n `Could not create Vercel sandbox worktree directory: ${await mkdirResult.stderr()}`,\n );\n }\n } catch (error) {\n await sandbox.stop().catch(() => {});\n throw error;\n }\n\n const handle: IsolatedSandboxHandle = {\n worktreePath,\n\n exec: async (\n command: string,\n opts?: {\n onLine?: (line: string) => void;\n cwd?: string;\n sudo?: boolean;\n stdin?: string;\n },\n ): Promise<ExecResult> => {\n const stdin = opts?.stdin;\n\n return withStdinFile(\n stdin,\n {\n upload: (path, content) =>\n sandbox.writeFiles([{ path, content }]),\n remove: async (path) => {\n await sandbox.runCommand({\n cmd: \"rm\",\n args: [\"-f\", \"--\", path],\n });\n },\n },\n async (stdinPath) => {\n const commandToRun = stdinPath\n ? redirectCommandStdinFromFile(command, stdinPath)\n : command;\n\n if (opts?.onLine) {\n const onLine = opts.onLine;\n const stdoutTail = new BoundedTail(maxOutputTailChars, \"\\n\");\n const stderrTail = new BoundedTail(maxOutputTailChars, \"\");\n let partial = \"\";\n\n const stdoutWritable = new Writable({\n write(chunk, _encoding, callback) {\n const text = partial + chunk.toString();\n const lines = text.split(\"\\n\");\n partial = lines.pop() ?? \"\";\n for (const line of lines) {\n stdoutTail.push(line);\n onLine(line);\n }\n callback();\n },\n final(callback) {\n if (partial) {\n stdoutTail.push(partial);\n onLine(partial);\n partial = \"\";\n }\n callback();\n },\n });\n\n const stderrWritable = new Writable({\n write(chunk, _encoding, callback) {\n stderrTail.push(chunk.toString());\n callback();\n },\n });\n\n const result = await sandbox.runCommand({\n cmd: \"sh\",\n args: [\"-c\", commandToRun],\n cwd: opts?.cwd ?? worktreePath,\n stdout: stdoutWritable,\n stderr: stderrWritable,\n ...(opts?.sudo ? { sudo: true } : {}),\n });\n\n return {\n stdout: stdoutTail.toString(),\n stderr: stderrTail.toString(),\n exitCode: result.exitCode,\n };\n }\n\n const result = await sandbox.runCommand({\n cmd: \"sh\",\n args: [\"-c\", commandToRun],\n cwd: opts?.cwd ?? worktreePath,\n ...(opts?.sudo ? { sudo: true } : {}),\n });\n\n const stdout = await result.stdout();\n const stderr = await result.stderr();\n\n return {\n stdout,\n stderr,\n exitCode: result.exitCode,\n };\n },\n );\n },\n\n copyIn: async (\n hostPath: string,\n sandboxPath: string,\n ): Promise<void> => {\n const info = await stat(hostPath);\n if (info.isDirectory()) {\n const tarPath = join(\n tmpdir(),\n `sandcastle-copyin-${Date.now()}.tar.gz`,\n );\n execSync(`tar -czf \"${tarPath}\" -C \"${hostPath}\" .`);\n try {\n const tarContent = await readFile(tarPath);\n const sandboxTarPath = `/tmp/sandcastle-copyin-${Date.now()}.tar.gz`;\n await sandbox.writeFiles([\n { path: sandboxTarPath, content: tarContent },\n ]);\n await sandbox.runCommand({\n cmd: \"sh\",\n args: [\n \"-c\",\n `mkdir -p \"${sandboxPath}\" && tar -xzf \"${sandboxTarPath}\" -C \"${sandboxPath}\" && rm -f \"${sandboxTarPath}\"`,\n ],\n });\n } finally {\n await unlink(tarPath).catch(() => {});\n }\n } else {\n const content = await readFile(hostPath);\n await sandbox.writeFiles([{ path: sandboxPath, content }]);\n }\n },\n\n copyFileOut: async (\n sandboxPath: string,\n hostPath: string,\n ): Promise<void> => {\n const buffer = await sandbox.readFileToBuffer({\n path: sandboxPath,\n });\n if (!buffer) {\n throw new Error(`File not found in Vercel sandbox: ${sandboxPath}`);\n }\n await mkdir(dirname(hostPath), { recursive: true });\n await writeFile(hostPath, buffer);\n },\n\n close: async (): Promise<void> => {\n await sandbox.stop();\n },\n };\n\n return handle;\n },\n });\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { run, claudeCode } from "@ai-hero/sandcastle";
|
|
2
|
+
import { docker } from "@ai-hero/sandcastle/sandboxes/docker";
|
|
3
|
+
|
|
4
|
+
// Blank template: customize this to build your own orchestration.
|
|
5
|
+
// Run this with: npx tsx .sandcastle/main.mts
|
|
6
|
+
// Or add to package.json scripts: "sandcastle": "npx tsx .sandcastle/main.mts"
|
|
7
|
+
|
|
8
|
+
await run({
|
|
9
|
+
agent: claudeCode("claude-opus-4-8"),
|
|
10
|
+
sandbox: docker(),
|
|
11
|
+
promptFile: "./.sandcastle/prompt.md",
|
|
12
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Context
|
|
2
|
+
|
|
3
|
+
<!-- Use !`command` to pull in dynamic context. Commands run inside the sandbox. -->
|
|
4
|
+
<!-- Example: !`git log --oneline -10` or !`{{LIST_TASKS_COMMAND}}` -->
|
|
5
|
+
|
|
6
|
+
# Task
|
|
7
|
+
|
|
8
|
+
<!-- Describe what the agent should do. -->
|
|
9
|
+
|
|
10
|
+
# Done
|
|
11
|
+
|
|
12
|
+
<!-- When the task is complete, output <promise>COMPLETE</promise> to signal early termination. -->
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# TASK
|
|
2
|
+
|
|
3
|
+
Fix issue {{TASK_ID}}: {{ISSUE_TITLE}}
|
|
4
|
+
|
|
5
|
+
Pull in the issue using `{{VIEW_TASK_COMMAND}}`. If it has a parent PRD, pull that in too.
|
|
6
|
+
|
|
7
|
+
Only work on the issue specified.
|
|
8
|
+
|
|
9
|
+
Work on branch {{BRANCH}}. Make commits and run tests.
|
|
10
|
+
|
|
11
|
+
# CONTEXT
|
|
12
|
+
|
|
13
|
+
Here are the last 10 commits:
|
|
14
|
+
|
|
15
|
+
<recent-commits>
|
|
16
|
+
|
|
17
|
+
!`git log -n 10 --format="%H%n%ad%n%B---" --date=short`
|
|
18
|
+
|
|
19
|
+
</recent-commits>
|
|
20
|
+
|
|
21
|
+
# EXPLORATION
|
|
22
|
+
|
|
23
|
+
Explore the repo and fill your context window with relevant information that will allow you to complete the task.
|
|
24
|
+
|
|
25
|
+
Pay extra attention to test files that touch the relevant parts of the code.
|
|
26
|
+
|
|
27
|
+
# EXECUTION
|
|
28
|
+
|
|
29
|
+
If applicable, use RGR to complete the task.
|
|
30
|
+
|
|
31
|
+
1. RED: write one test
|
|
32
|
+
2. GREEN: write the implementation to pass that test
|
|
33
|
+
3. REPEAT until done
|
|
34
|
+
4. REFACTOR the code
|
|
35
|
+
|
|
36
|
+
# FEEDBACK LOOPS
|
|
37
|
+
|
|
38
|
+
Before committing, run `npm run typecheck` and `npm run test` to ensure the tests pass.
|
|
39
|
+
|
|
40
|
+
# COMMIT
|
|
41
|
+
|
|
42
|
+
Make a git commit. The commit message must:
|
|
43
|
+
|
|
44
|
+
1. Start with `RALPH:` prefix
|
|
45
|
+
2. Include task completed + PRD reference
|
|
46
|
+
3. Key decisions made
|
|
47
|
+
4. Files changed
|
|
48
|
+
5. Blockers or notes for next iteration
|
|
49
|
+
|
|
50
|
+
Keep it concise.
|
|
51
|
+
|
|
52
|
+
# THE ISSUE
|
|
53
|
+
|
|
54
|
+
If the task is not complete, leave a comment on the issue with what was done.
|
|
55
|
+
|
|
56
|
+
Do not close the issue - this will be done later.
|
|
57
|
+
|
|
58
|
+
Once complete, output <promise>COMPLETE</promise>.
|
|
59
|
+
|
|
60
|
+
# FINAL RULES
|
|
61
|
+
|
|
62
|
+
ONLY WORK ON A SINGLE TASK.
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// Parallel Planner — three-phase orchestration loop
|
|
2
|
+
//
|
|
3
|
+
// This template drives a multi-phase workflow:
|
|
4
|
+
// Phase 1 (Plan): An opus agent analyzes open issues, builds a dependency
|
|
5
|
+
// graph, and outputs a <plan> JSON listing unblocked issues
|
|
6
|
+
// with their target branch names.
|
|
7
|
+
// Phase 2 (Execute): N sonnet agents run in parallel via Promise.allSettled,
|
|
8
|
+
// each working a single issue on its own branch.
|
|
9
|
+
// Phase 3 (Merge): A sonnet agent merges all branches that produced commits.
|
|
10
|
+
//
|
|
11
|
+
// The outer loop repeats up to MAX_ITERATIONS times so that newly unblocked
|
|
12
|
+
// issues are picked up after each round of merges.
|
|
13
|
+
//
|
|
14
|
+
// Usage:
|
|
15
|
+
// npx tsx .sandcastle/main.mts
|
|
16
|
+
// Or add to package.json:
|
|
17
|
+
// "scripts": { "sandcastle": "npx tsx .sandcastle/main.mts" }
|
|
18
|
+
|
|
19
|
+
import * as sandcastle from "@ai-hero/sandcastle";
|
|
20
|
+
import { docker } from "@ai-hero/sandcastle/sandboxes/docker";
|
|
21
|
+
import { z } from "zod";
|
|
22
|
+
|
|
23
|
+
// The planner emits its plan as JSON inside <plan> tags; Output.object extracts
|
|
24
|
+
// and validates it against this schema. We use Zod here, but any Standard
|
|
25
|
+
// Schema validator works just as well — Valibot, ArkType, etc. See
|
|
26
|
+
// https://standardschema.dev.
|
|
27
|
+
const planSchema = z.object({
|
|
28
|
+
issues: z.array(
|
|
29
|
+
z.object({ id: z.string(), title: z.string(), branch: z.string() }),
|
|
30
|
+
),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// Configuration
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
// Maximum number of plan→execute→merge cycles before stopping.
|
|
38
|
+
// Raise this if your backlog is large; lower it for a quick smoke-test run.
|
|
39
|
+
const MAX_ITERATIONS = 10;
|
|
40
|
+
|
|
41
|
+
// Hooks run inside the sandbox before the agent starts each iteration.
|
|
42
|
+
// npm install ensures the sandbox always has fresh dependencies.
|
|
43
|
+
const hooks = {
|
|
44
|
+
sandbox: { onSandboxReady: [{ command: "npm install" }] },
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Copy node_modules from the host into the worktree before each sandbox
|
|
48
|
+
// starts. Avoids a full npm install from scratch; the hook above handles
|
|
49
|
+
// platform-specific binaries and any packages added since the last copy.
|
|
50
|
+
const copyToWorktree = ["node_modules"];
|
|
51
|
+
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Main loop
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) {
|
|
57
|
+
console.log(`\n=== Iteration ${iteration}/${MAX_ITERATIONS} ===\n`);
|
|
58
|
+
|
|
59
|
+
// -------------------------------------------------------------------------
|
|
60
|
+
// Phase 1: Plan
|
|
61
|
+
//
|
|
62
|
+
// The planning agent (opus, for deeper reasoning) reads the open issue list,
|
|
63
|
+
// builds a dependency graph, and selects the issues that can be worked in
|
|
64
|
+
// parallel right now (i.e., no blocking dependencies on other open issues).
|
|
65
|
+
//
|
|
66
|
+
// It outputs a <plan> JSON block — Output.object parses and validates it.
|
|
67
|
+
// -------------------------------------------------------------------------
|
|
68
|
+
const plan = await sandcastle.run({
|
|
69
|
+
hooks,
|
|
70
|
+
sandbox: docker(),
|
|
71
|
+
name: "planner",
|
|
72
|
+
// One iteration is enough: the planner just needs to read and reason,
|
|
73
|
+
// not write code. (Structured output requires maxIterations: 1.)
|
|
74
|
+
maxIterations: 1,
|
|
75
|
+
// Opus for planning: dependency analysis benefits from deeper reasoning.
|
|
76
|
+
agent: sandcastle.claudeCode("claude-opus-4-8"),
|
|
77
|
+
promptFile: "./.sandcastle/plan-prompt.md",
|
|
78
|
+
// Extract and validate the <plan> JSON into a typed object. Throws
|
|
79
|
+
// StructuredOutputError if the tag is missing, the JSON is malformed, or
|
|
80
|
+
// validation fails — which aborts the loop.
|
|
81
|
+
output: sandcastle.Output.object({ tag: "plan", schema: planSchema }),
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const issues = plan.output.issues;
|
|
85
|
+
|
|
86
|
+
if (issues.length === 0) {
|
|
87
|
+
// No unblocked work — either everything is done or everything is blocked.
|
|
88
|
+
console.log("No unblocked issues to work on. Exiting.");
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
console.log(
|
|
93
|
+
`Planning complete. ${issues.length} issue(s) to work in parallel:`,
|
|
94
|
+
);
|
|
95
|
+
for (const issue of issues) {
|
|
96
|
+
console.log(` ${issue.id}: ${issue.title} → ${issue.branch}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// -------------------------------------------------------------------------
|
|
100
|
+
// Phase 2: Execute
|
|
101
|
+
//
|
|
102
|
+
// Spawn one sonnet agent per issue, all running concurrently.
|
|
103
|
+
// Each agent works on its own branch so there are no conflicts during
|
|
104
|
+
// execution — merging happens in Phase 3.
|
|
105
|
+
//
|
|
106
|
+
// Promise.allSettled means one failing agent doesn't cancel the others.
|
|
107
|
+
// -------------------------------------------------------------------------
|
|
108
|
+
const settled = await Promise.allSettled(
|
|
109
|
+
issues.map((issue) =>
|
|
110
|
+
sandcastle.run({
|
|
111
|
+
hooks,
|
|
112
|
+
copyToWorktree,
|
|
113
|
+
// Each agent starts on its own branch via branchStrategy on run().
|
|
114
|
+
sandbox: docker(),
|
|
115
|
+
branchStrategy: { type: "branch", branch: issue.branch },
|
|
116
|
+
name: "implementer",
|
|
117
|
+
// Give each agent plenty of room to implement and iterate on tests.
|
|
118
|
+
maxIterations: 100,
|
|
119
|
+
// Sonnet for execution: fast and capable enough for typical issue work.
|
|
120
|
+
agent: sandcastle.claudeCode("claude-sonnet-4-6"),
|
|
121
|
+
promptFile: "./.sandcastle/implement-prompt.md",
|
|
122
|
+
// Prompt arguments substitute {{TASK_ID}}, {{ISSUE_TITLE}},
|
|
123
|
+
// and {{BRANCH}} placeholders in implement-prompt.md before the
|
|
124
|
+
// agent sees the prompt.
|
|
125
|
+
promptArgs: {
|
|
126
|
+
TASK_ID: issue.id,
|
|
127
|
+
ISSUE_TITLE: issue.title,
|
|
128
|
+
BRANCH: issue.branch,
|
|
129
|
+
},
|
|
130
|
+
}),
|
|
131
|
+
),
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
// Log any agents that threw (network error, sandbox crash, etc.).
|
|
135
|
+
for (const [i, outcome] of settled.entries()) {
|
|
136
|
+
if (outcome.status === "rejected") {
|
|
137
|
+
console.error(
|
|
138
|
+
` ✗ ${issues[i]!.id} (${issues[i]!.branch}) failed: ${outcome.reason}`,
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Only pass branches that actually produced commits to the merge phase.
|
|
144
|
+
// An agent that ran successfully but made no commits has nothing to merge.
|
|
145
|
+
const completedIssues = settled
|
|
146
|
+
.map((outcome, i) => ({ outcome, issue: issues[i]! }))
|
|
147
|
+
.filter(
|
|
148
|
+
(
|
|
149
|
+
entry,
|
|
150
|
+
): entry is {
|
|
151
|
+
outcome: PromiseFulfilledResult<
|
|
152
|
+
Awaited<ReturnType<typeof sandcastle.run>>
|
|
153
|
+
>;
|
|
154
|
+
issue: (typeof issues)[number];
|
|
155
|
+
} =>
|
|
156
|
+
entry.outcome.status === "fulfilled" &&
|
|
157
|
+
entry.outcome.value.commits.length > 0,
|
|
158
|
+
)
|
|
159
|
+
.map((entry) => entry.issue);
|
|
160
|
+
|
|
161
|
+
const completedBranches = completedIssues.map((i) => i.branch);
|
|
162
|
+
|
|
163
|
+
console.log(
|
|
164
|
+
`\nExecution complete. ${completedBranches.length} branch(es) with commits:`,
|
|
165
|
+
);
|
|
166
|
+
for (const branch of completedBranches) {
|
|
167
|
+
console.log(` ${branch}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (completedBranches.length === 0) {
|
|
171
|
+
// All agents ran but none made commits — nothing to merge this cycle.
|
|
172
|
+
console.log("No commits produced. Nothing to merge.");
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// -------------------------------------------------------------------------
|
|
177
|
+
// Phase 3: Merge
|
|
178
|
+
//
|
|
179
|
+
// One sonnet agent merges all completed branches into the current branch,
|
|
180
|
+
// resolving any conflicts and running tests to confirm everything still works.
|
|
181
|
+
//
|
|
182
|
+
// The {{BRANCHES}} and {{ISSUES}} prompt arguments are lists that the agent
|
|
183
|
+
// uses to know which branches to merge and which issues to close.
|
|
184
|
+
// -------------------------------------------------------------------------
|
|
185
|
+
await sandcastle.run({
|
|
186
|
+
hooks,
|
|
187
|
+
sandbox: docker(),
|
|
188
|
+
name: "merger",
|
|
189
|
+
maxIterations: 1,
|
|
190
|
+
// Sonnet is sufficient for merge conflict resolution.
|
|
191
|
+
agent: sandcastle.claudeCode("claude-sonnet-4-6"),
|
|
192
|
+
promptFile: "./.sandcastle/merge-prompt.md",
|
|
193
|
+
promptArgs: {
|
|
194
|
+
// A markdown list of branch names, one per line.
|
|
195
|
+
BRANCHES: completedBranches.map((b) => `- ${b}`).join("\n"),
|
|
196
|
+
// A markdown list of issue IDs and titles, one per line.
|
|
197
|
+
ISSUES: completedIssues.map((i) => `- ${i.id}: ${i.title}`).join("\n"),
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
console.log("\nBranches merged.");
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
console.log("\nAll done.");
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# TASK
|
|
2
|
+
|
|
3
|
+
Merge the following branches into the current branch:
|
|
4
|
+
|
|
5
|
+
{{BRANCHES}}
|
|
6
|
+
|
|
7
|
+
For each branch:
|
|
8
|
+
|
|
9
|
+
1. Run `git merge <branch> --no-edit`
|
|
10
|
+
2. If there are merge conflicts, resolve them intelligently by reading both sides and choosing the correct resolution
|
|
11
|
+
3. After resolving conflicts, run `npm run typecheck` and `npm run test` to verify everything works
|
|
12
|
+
4. If tests fail, fix the issues before proceeding to the next branch
|
|
13
|
+
|
|
14
|
+
After all branches are merged, make a single commit summarizing the merge.
|
|
15
|
+
|
|
16
|
+
# CLOSE ISSUES
|
|
17
|
+
|
|
18
|
+
For each branch that was merged, close its issue using the following command:
|
|
19
|
+
|
|
20
|
+
`{{CLOSE_TASK_COMMAND}}`
|
|
21
|
+
|
|
22
|
+
Here are all the issues:
|
|
23
|
+
|
|
24
|
+
{{ISSUES}}
|
|
25
|
+
|
|
26
|
+
Once you've merged everything you can, output <promise>COMPLETE</promise>.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# ISSUES
|
|
2
|
+
|
|
3
|
+
Here are the open issues in the repo:
|
|
4
|
+
|
|
5
|
+
<issues-json>
|
|
6
|
+
|
|
7
|
+
!`{{LIST_TASKS_COMMAND}}`
|
|
8
|
+
|
|
9
|
+
</issues-json>
|
|
10
|
+
|
|
11
|
+
The list above has already been filtered to issues ready for work.
|
|
12
|
+
|
|
13
|
+
# TASK
|
|
14
|
+
|
|
15
|
+
Analyze the open issues and build a dependency graph. For each issue, determine whether it **blocks** or **is blocked by** any other open issue.
|
|
16
|
+
|
|
17
|
+
An issue B is **blocked by** issue A if:
|
|
18
|
+
|
|
19
|
+
- B requires code or infrastructure that A introduces
|
|
20
|
+
- B and A modify overlapping files or modules, making concurrent work likely to produce merge conflicts
|
|
21
|
+
- B's requirements depend on a decision or API shape that A will establish
|
|
22
|
+
|
|
23
|
+
An issue is **unblocked** if it has zero blocking dependencies on other open issues.
|
|
24
|
+
|
|
25
|
+
For each unblocked issue, assign a branch name using the exact format `sandcastle/issue-{id}` (no slug or other suffix). This must be deterministic so that re-planning the same issue always produces the same branch name and accumulated progress is preserved.
|
|
26
|
+
|
|
27
|
+
# OUTPUT
|
|
28
|
+
|
|
29
|
+
Output your plan as a JSON object wrapped in `<plan>` tags:
|
|
30
|
+
|
|
31
|
+
<plan>
|
|
32
|
+
{"issues": [{"id": "42", "title": "Fix auth bug", "branch": "sandcastle/issue-42"}]}
|
|
33
|
+
</plan>
|
|
34
|
+
|
|
35
|
+
Include only unblocked issues. If every issue is blocked, include the single highest-priority candidate (the one with the fewest or weakest dependencies).
|
|
36
|
+
|
|
37
|
+
Always emit the `<plan>` tags, even when there is nothing to do. If there are no issues to work on at all, output `<plan>{"issues": []}</plan>` so the run can exit cleanly.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Coding Standards
|
|
2
|
+
|
|
3
|
+
<!-- Customize this file with your project's coding standards.
|
|
4
|
+
The reviewer agent loads it during code review via @.sandcastle/CODING_STANDARDS.md
|
|
5
|
+
so these standards are enforced during review without costing tokens during implementation. -->
|
|
6
|
+
|
|
7
|
+
## Style
|
|
8
|
+
|
|
9
|
+
<!-- Example:
|
|
10
|
+
- Use camelCase for variables and functions
|
|
11
|
+
- Use PascalCase for classes and types
|
|
12
|
+
- Prefer named exports over default exports
|
|
13
|
+
-->
|
|
14
|
+
|
|
15
|
+
## Testing
|
|
16
|
+
|
|
17
|
+
<!-- Example:
|
|
18
|
+
- Every public function must have at least one test
|
|
19
|
+
- Use descriptive test names that explain the expected behavior
|
|
20
|
+
-->
|
|
21
|
+
|
|
22
|
+
## Architecture
|
|
23
|
+
|
|
24
|
+
<!-- Example:
|
|
25
|
+
- Keep modules focused on a single responsibility
|
|
26
|
+
- Prefer composition over inheritance
|
|
27
|
+
-->
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# TASK
|
|
2
|
+
|
|
3
|
+
Fix issue {{TASK_ID}}: {{ISSUE_TITLE}}
|
|
4
|
+
|
|
5
|
+
Pull in the issue using `{{VIEW_TASK_COMMAND}}`. If it has a parent PRD, pull that in too.
|
|
6
|
+
|
|
7
|
+
Only work on the issue specified.
|
|
8
|
+
|
|
9
|
+
Work on branch {{BRANCH}}. Make commits and run tests.
|
|
10
|
+
|
|
11
|
+
# CONTEXT
|
|
12
|
+
|
|
13
|
+
Here are the last 10 commits:
|
|
14
|
+
|
|
15
|
+
<recent-commits>
|
|
16
|
+
|
|
17
|
+
!`git log -n 10 --format="%H%n%ad%n%B---" --date=short`
|
|
18
|
+
|
|
19
|
+
</recent-commits>
|
|
20
|
+
|
|
21
|
+
# EXPLORATION
|
|
22
|
+
|
|
23
|
+
Explore the repo and fill your context window with relevant information that will allow you to complete the task.
|
|
24
|
+
|
|
25
|
+
Pay extra attention to test files that touch the relevant parts of the code.
|
|
26
|
+
|
|
27
|
+
# EXECUTION
|
|
28
|
+
|
|
29
|
+
If applicable, use RGR to complete the task.
|
|
30
|
+
|
|
31
|
+
1. RED: write one test
|
|
32
|
+
2. GREEN: write the implementation to pass that test
|
|
33
|
+
3. REPEAT until done
|
|
34
|
+
4. REFACTOR the code
|
|
35
|
+
|
|
36
|
+
# FEEDBACK LOOPS
|
|
37
|
+
|
|
38
|
+
Before committing, run `npm run typecheck` and `npm run test` to ensure the tests pass.
|
|
39
|
+
|
|
40
|
+
# COMMIT
|
|
41
|
+
|
|
42
|
+
Make a git commit. The commit message must:
|
|
43
|
+
|
|
44
|
+
1. Start with `RALPH:` prefix
|
|
45
|
+
2. Include task completed + PRD reference
|
|
46
|
+
3. Key decisions made
|
|
47
|
+
4. Files changed
|
|
48
|
+
5. Blockers or notes for next iteration
|
|
49
|
+
|
|
50
|
+
Keep it concise.
|
|
51
|
+
|
|
52
|
+
# THE ISSUE
|
|
53
|
+
|
|
54
|
+
If the task is not complete, leave a comment on the issue with what was done.
|
|
55
|
+
|
|
56
|
+
Do not close the issue - this will be done later.
|
|
57
|
+
|
|
58
|
+
Once complete, output <promise>COMPLETE</promise>.
|
|
59
|
+
|
|
60
|
+
# FINAL RULES
|
|
61
|
+
|
|
62
|
+
ONLY WORK ON A SINGLE TASK.
|