@ai-hero/sandcastle 0.4.7 → 0.4.8

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 (104) hide show
  1. package/README.md +152 -27
  2. package/dist/CopyToWorktree.d.ts +9 -0
  3. package/dist/CopyToWorktree.d.ts.map +1 -0
  4. package/dist/{CopyToWorkspace.js → CopyToWorktree.js} +9 -3
  5. package/dist/CopyToWorktree.js.map +1 -0
  6. package/dist/DockerLifecycle.d.ts +2 -0
  7. package/dist/DockerLifecycle.d.ts.map +1 -1
  8. package/dist/DockerLifecycle.js +7 -0
  9. package/dist/DockerLifecycle.js.map +1 -1
  10. package/dist/ErrorHandler.d.ts.map +1 -1
  11. package/dist/ErrorHandler.js +20 -2
  12. package/dist/ErrorHandler.js.map +1 -1
  13. package/dist/InitService.d.ts +6 -1
  14. package/dist/InitService.d.ts.map +1 -1
  15. package/dist/InitService.js +90 -56
  16. package/dist/InitService.js.map +1 -1
  17. package/dist/MountConfig.d.ts +13 -2
  18. package/dist/MountConfig.d.ts.map +1 -1
  19. package/dist/Orchestrator.d.ts +1 -1
  20. package/dist/Orchestrator.d.ts.map +1 -1
  21. package/dist/Orchestrator.js +6 -7
  22. package/dist/Orchestrator.js.map +1 -1
  23. package/dist/PromptPreprocessor.d.ts +2 -2
  24. package/dist/PromptPreprocessor.d.ts.map +1 -1
  25. package/dist/PromptPreprocessor.js +7 -2
  26. package/dist/PromptPreprocessor.js.map +1 -1
  27. package/dist/SandboxFactory.d.ts +7 -9
  28. package/dist/SandboxFactory.d.ts.map +1 -1
  29. package/dist/SandboxFactory.js +21 -23
  30. package/dist/SandboxFactory.js.map +1 -1
  31. package/dist/SandboxLifecycle.d.ts.map +1 -1
  32. package/dist/SandboxLifecycle.js +33 -9
  33. package/dist/SandboxLifecycle.js.map +1 -1
  34. package/dist/SandboxProvider.d.ts +7 -7
  35. package/dist/SandboxProvider.d.ts.map +1 -1
  36. package/dist/WorktreeManager.d.ts +3 -3
  37. package/dist/WorktreeManager.d.ts.map +1 -1
  38. package/dist/WorktreeManager.js +14 -3
  39. package/dist/WorktreeManager.js.map +1 -1
  40. package/dist/createSandbox.d.ts +24 -1
  41. package/dist/createSandbox.d.ts.map +1 -1
  42. package/dist/createSandbox.js +227 -131
  43. package/dist/createSandbox.js.map +1 -1
  44. package/dist/createWorktree.d.ts +121 -0
  45. package/dist/createWorktree.d.ts.map +1 -0
  46. package/dist/createWorktree.js +314 -0
  47. package/dist/createWorktree.js.map +1 -0
  48. package/dist/errors.d.ts +101 -6
  49. package/dist/errors.d.ts.map +1 -1
  50. package/dist/errors.js +38 -3
  51. package/dist/errors.js.map +1 -1
  52. package/dist/index.d.ts +2 -0
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +1 -0
  55. package/dist/index.js.map +1 -1
  56. package/dist/interactive.d.ts +1 -1
  57. package/dist/interactive.d.ts.map +1 -1
  58. package/dist/interactive.js +54 -45
  59. package/dist/interactive.js.map +1 -1
  60. package/dist/run.d.ts +1 -1
  61. package/dist/run.d.ts.map +1 -1
  62. package/dist/run.js +5 -5
  63. package/dist/run.js.map +1 -1
  64. package/dist/sandboxes/daytona.js +4 -4
  65. package/dist/sandboxes/daytona.js.map +1 -1
  66. package/dist/sandboxes/docker.d.ts +9 -0
  67. package/dist/sandboxes/docker.d.ts.map +1 -1
  68. package/dist/sandboxes/docker.js +15 -5
  69. package/dist/sandboxes/docker.js.map +1 -1
  70. package/dist/sandboxes/no-sandbox.js +4 -4
  71. package/dist/sandboxes/no-sandbox.js.map +1 -1
  72. package/dist/sandboxes/podman.d.ts +9 -0
  73. package/dist/sandboxes/podman.d.ts.map +1 -1
  74. package/dist/sandboxes/podman.js +21 -5
  75. package/dist/sandboxes/podman.js.map +1 -1
  76. package/dist/sandboxes/test-isolated.js +5 -5
  77. package/dist/sandboxes/test-isolated.js.map +1 -1
  78. package/dist/sandboxes/vercel.js +7 -7
  79. package/dist/sandboxes/vercel.js.map +1 -1
  80. package/dist/startSandbox.d.ts +7 -6
  81. package/dist/startSandbox.d.ts.map +1 -1
  82. package/dist/startSandbox.js +38 -19
  83. package/dist/startSandbox.js.map +1 -1
  84. package/dist/syncIn.js +7 -7
  85. package/dist/syncIn.js.map +1 -1
  86. package/dist/syncOut.js +6 -6
  87. package/dist/syncOut.js.map +1 -1
  88. package/dist/templates/parallel-planner/implement-prompt.md +2 -2
  89. package/dist/templates/parallel-planner/main.mts +2 -6
  90. package/dist/templates/parallel-planner/merge-prompt.md +5 -1
  91. package/dist/templates/parallel-planner-with-review/implement-prompt.md +2 -2
  92. package/dist/templates/parallel-planner-with-review/main.mts +28 -62
  93. package/dist/templates/parallel-planner-with-review/merge-prompt.md +5 -1
  94. package/dist/templates/sequential-reviewer/main.mts +3 -3
  95. package/dist/templates/simple-loop/main.mts +2 -2
  96. package/package.json +1 -1
  97. package/dist/CopyToWorkspace.d.ts +0 -8
  98. package/dist/CopyToWorkspace.d.ts.map +0 -1
  99. package/dist/CopyToWorkspace.js.map +0 -1
  100. package/dist/templates/blank/.env.example +0 -5
  101. package/dist/templates/parallel-planner/.env.example +0 -5
  102. package/dist/templates/parallel-planner-with-review/.env.example +0 -5
  103. package/dist/templates/sequential-reviewer/.env.example +0 -5
  104. package/dist/templates/simple-loop/.env.example +0 -5
@@ -15,7 +15,11 @@ After all branches are merged, make a single commit summarizing the merge.
15
15
 
16
16
  # CLOSE ISSUES
17
17
 
18
- For each branch that was merged, close its issue. Here are all the issues:
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:
19
23
 
20
24
  {{ISSUES}}
21
25
 
@@ -6,7 +6,7 @@ Pull in the issue using `{{VIEW_TASK_COMMAND}}`. If it has a parent PRD, pull th
6
6
 
7
7
  Only work on the issue specified.
8
8
 
9
- Work on branch {{BRANCH}}. Make commits, run tests, and close the issue when done.
9
+ Work on branch {{BRANCH}}. Make commits, run tests, and close the issue when done using `{{CLOSE_TASK_COMMAND}}`.
10
10
 
11
11
  # CONTEXT
12
12
 
@@ -51,7 +51,7 @@ Keep it concise.
51
51
 
52
52
  # THE ISSUE
53
53
 
54
- If the task is not complete, leave a comment on the GitHub issue with what was done.
54
+ If the task is not complete, leave a comment on the issue with what was done.
55
55
 
56
56
  Do not close the issue - this will be done later.
57
57
 
@@ -41,10 +41,7 @@ const hooks = {
41
41
  // Copy node_modules from the host into the worktree before each sandbox
42
42
  // starts. Avoids a full npm install from scratch; the hook above handles
43
43
  // platform-specific binaries and any packages added since the last copy.
44
- const copyToWorkspace = ["node_modules"];
45
-
46
- // Cap the number of concurrent sandboxes to avoid resource exhaustion.
47
- const MAX_CONCURRENCY = 4;
44
+ const copyToWorktree = ["node_modules"];
48
45
 
49
46
  // ---------------------------------------------------------------------------
50
47
  // Main loop
@@ -64,9 +61,7 @@ for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) {
64
61
  // -------------------------------------------------------------------------
65
62
  const plan = await sandcastle.run({
66
63
  hooks,
67
- copyToWorkspace,
68
64
  sandbox: docker(),
69
- branchStrategy: { type: "merge-to-head" },
70
65
  name: "planner",
71
66
  // One iteration is enough: the planner just needs to read and reason,
72
67
  // not write code.
@@ -109,75 +104,48 @@ for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) {
109
104
  // and reviewer share the same sandbox instance per branch. The implementer
110
105
  // runs first; if it produces commits, the reviewer runs in the same sandbox.
111
106
  //
112
- // A semaphore limits concurrency to MAX_CONCURRENCY sandboxes at once.
113
107
  // Promise.allSettled means one failing pipeline doesn't cancel the others.
114
108
  // -------------------------------------------------------------------------
115
109
 
116
- // Simple semaphore for concurrency limiting
117
- let running = 0;
118
- const waiting: Array<() => void> = [];
119
- const acquire = (): Promise<void> => {
120
- if (running < MAX_CONCURRENCY) {
121
- running++;
122
- return Promise.resolve();
123
- }
124
- return new Promise<void>((resolve) => {
125
- waiting.push(() => {
126
- running++;
127
- resolve();
128
- });
129
- });
130
- };
131
- const release = () => {
132
- running--;
133
- const next = waiting.shift();
134
- if (next) next();
135
- };
136
-
137
110
  const settled = await Promise.allSettled(
138
111
  issues.map(async (issue) => {
139
- await acquire();
112
+ const sandbox = await sandcastle.createSandbox({
113
+ branch: issue.branch,
114
+ sandbox: docker(),
115
+ hooks,
116
+ copyToWorktree,
117
+ });
118
+
140
119
  try {
141
- const sandbox = await sandcastle.createSandbox({
142
- branch: issue.branch,
143
- sandbox: docker(),
144
- hooks,
145
- copyToWorkspace,
120
+ // Run the implementer
121
+ const implement = await sandbox.run({
122
+ name: "implementer",
123
+ maxIterations: 100,
124
+ agent: sandcastle.claudeCode("claude-sonnet-4-6"),
125
+ promptFile: "./.sandcastle/implement-prompt.md",
126
+ promptArgs: {
127
+ TASK_ID: issue.id,
128
+ ISSUE_TITLE: issue.title,
129
+ BRANCH: issue.branch,
130
+ },
146
131
  });
147
132
 
148
- try {
149
- // Run the implementer
150
- const implement = await sandbox.run({
151
- name: "implementer",
152
- maxIterations: 100,
133
+ // Only review if the implementer produced commits
134
+ if (implement.commits.length > 0) {
135
+ await sandbox.run({
136
+ name: "reviewer",
137
+ maxIterations: 1,
153
138
  agent: sandcastle.claudeCode("claude-sonnet-4-6"),
154
- promptFile: "./.sandcastle/implement-prompt.md",
139
+ promptFile: "./.sandcastle/review-prompt.md",
155
140
  promptArgs: {
156
- TASK_ID: issue.id,
157
- ISSUE_TITLE: issue.title,
158
141
  BRANCH: issue.branch,
159
142
  },
160
143
  });
161
-
162
- // Only review if the implementer produced commits
163
- if (implement.commits.length > 0) {
164
- await sandbox.run({
165
- name: "reviewer",
166
- maxIterations: 1,
167
- agent: sandcastle.claudeCode("claude-sonnet-4-6"),
168
- promptFile: "./.sandcastle/review-prompt.md",
169
- promptArgs: {
170
- BRANCH: issue.branch,
171
- },
172
- });
173
- }
174
-
175
- return implement;
176
- } finally {
177
- await sandbox.close();
178
144
  }
145
+
146
+ return implement;
179
147
  } finally {
180
- release();
148
+ await sandbox.close();
181
149
  }
182
150
  }),
183
151
  );
@@ -228,9 +196,7 @@ for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) {
228
196
  // -------------------------------------------------------------------------
229
197
  await sandcastle.run({
230
198
  hooks,
231
- copyToWorkspace,
232
199
  sandbox: docker(),
233
- branchStrategy: { type: "merge-to-head" },
234
200
  name: "merger",
235
201
  maxIterations: 1,
236
202
  agent: sandcastle.claudeCode("claude-sonnet-4-6"),
@@ -15,7 +15,11 @@ After all branches are merged, make a single commit summarizing the merge.
15
15
 
16
16
  # CLOSE ISSUES
17
17
 
18
- For each branch that was merged, close its issue. Here are all the issues:
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:
19
23
 
20
24
  {{ISSUES}}
21
25
 
@@ -36,7 +36,7 @@ const hooks = {
36
36
  // Copy node_modules from the host into the worktree before each sandbox
37
37
  // starts. Avoids a full npm install from scratch; the hook above handles
38
38
  // platform-specific binaries and any packages added since the last copy.
39
- const copyToWorkspace = ["node_modules"];
39
+ const copyToWorktree = ["node_modules"];
40
40
 
41
41
  // ---------------------------------------------------------------------------
42
42
  // Main loop
@@ -57,7 +57,7 @@ for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) {
57
57
  // -------------------------------------------------------------------------
58
58
  const implement = await sandcastle.run({
59
59
  hooks,
60
- copyToWorkspace,
60
+ copyToWorktree,
61
61
  sandbox: docker(),
62
62
  branchStrategy: { type: "merge-to-head" },
63
63
  name: "implementer",
@@ -86,7 +86,7 @@ for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) {
86
86
  // -------------------------------------------------------------------------
87
87
  await sandcastle.run({
88
88
  hooks,
89
- copyToWorkspace,
89
+ copyToWorktree,
90
90
  sandbox: docker(),
91
91
  branchStrategy: { type: "branch", branch },
92
92
  name: "reviewer",
@@ -28,7 +28,7 @@ await run({
28
28
 
29
29
  // Branch strategy — merge-to-head creates a temporary branch for the agent
30
30
  // to work on, then merges the result back to HEAD when the run completes.
31
- // This is required when using copyToWorkspace, since head mode bind-mounts
31
+ // This is required when using copyToWorktree, since head mode bind-mounts
32
32
  // the host directory directly (no worktree to copy into).
33
33
  branchStrategy: { type: "merge-to-head" },
34
34
 
@@ -36,7 +36,7 @@ await run({
36
36
  // starts. This avoids a full npm install from scratch on every iteration.
37
37
  // The onSandboxReady hook still runs npm install as a safety net to handle
38
38
  // platform-specific binaries and any packages added since the last copy.
39
- copyToWorkspace: ["node_modules"],
39
+ copyToWorktree: ["node_modules"],
40
40
 
41
41
  // Lifecycle hooks — commands that run inside the sandbox at specific points.
42
42
  hooks: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-hero/sandcastle",
3
- "version": "0.4.7",
3
+ "version": "0.4.8",
4
4
  "description": "CLI for orchestrating AI agents in isolated sandbox environments",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,8 +0,0 @@
1
- import { Effect } from "effect";
2
- /**
3
- * Copy files and directories from the host repo root to the worktree root,
4
- * using `cp -R --reflink=auto` for copy-on-write when the filesystem supports it.
5
- * Missing paths are silently skipped.
6
- */
7
- export declare const copyToWorkspace: (paths: string[], hostRepoDir: string, worktreePath: string) => Effect.Effect<void, never, never>;
8
- //# sourceMappingURL=CopyToWorkspace.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"CopyToWorkspace.d.ts","sourceRoot":"","sources":["../src/CopyToWorkspace.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC;;;;GAIG;AACH,eAAO,MAAM,eAAe,mGAyBxB,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"CopyToWorkspace.js","sourceRoot":"","sources":["../src/CopyToWorkspace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,KAAe,EACf,WAAmB,EACnB,YAAoB,EACQ,EAAE,CAC9B,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,KAAK,MAAM,YAAY,IAAI,KAAK,EAAE,CAAC;QACjC,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;QAC5C,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACrB,SAAS;QACX,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;QAC9C,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAc,CAAC,MAAM,EAAE,EAAE;YAC1C,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,gBAAgB,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE;gBAC5D,IAAI,KAAK,EAAE,CAAC;oBACV,0DAA0D;oBAC1D,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE;wBACrC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;oBACpC,CAAC,CAAC,CAAC;gBACL,CAAC;qBAAM,CAAC;oBACN,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;gBACpC,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -1,5 +0,0 @@
1
- # Anthropic API key
2
- # If you want to use your Claude subscription instead of an API key, see https://github.com/mattpocock/sandcastle/issues/191
3
- ANTHROPIC_API_KEY=
4
- # GitHub personal access token
5
- GH_TOKEN=
@@ -1,5 +0,0 @@
1
- # Anthropic API key
2
- # If you want to use your Claude subscription instead of an API key, see https://github.com/mattpocock/sandcastle/issues/191
3
- ANTHROPIC_API_KEY=
4
- # GitHub personal access token
5
- GH_TOKEN=
@@ -1,5 +0,0 @@
1
- # Anthropic API key
2
- # If you want to use your Claude subscription instead of an API key, see https://github.com/mattpocock/sandcastle/issues/191
3
- ANTHROPIC_API_KEY=
4
- # GitHub personal access token
5
- GH_TOKEN=
@@ -1,5 +0,0 @@
1
- # Anthropic API key
2
- # If you want to use your Claude subscription instead of an API key, see https://github.com/mattpocock/sandcastle/issues/191
3
- ANTHROPIC_API_KEY=
4
- # GitHub personal access token
5
- GH_TOKEN=
@@ -1,5 +0,0 @@
1
- # Anthropic API key
2
- # If you want to use your Claude subscription instead of an API key, see https://github.com/mattpocock/sandcastle/issues/191
3
- ANTHROPIC_API_KEY=
4
- # GitHub personal access token
5
- GH_TOKEN=