@ai-hero/sandcastle 0.6.4 → 0.6.6

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 (227) hide show
  1. package/README.md +125 -54
  2. package/dist/{MountConfig.d.ts → MountConfig-CmXclHA5.d.ts} +3 -2
  3. package/dist/{SandboxProvider.d.ts → SandboxProvider-EkSMuBp8.d.ts} +25 -39
  4. package/dist/chunk-72UVAC7B.js +99 -0
  5. package/dist/chunk-72UVAC7B.js.map +1 -0
  6. package/dist/chunk-BIWNFKGV.js +22 -0
  7. package/dist/chunk-BIWNFKGV.js.map +1 -0
  8. package/dist/chunk-NGBM7T3E.js +76 -0
  9. package/dist/chunk-NGBM7T3E.js.map +1 -0
  10. package/dist/chunk-Q5W3WQVU.js +25569 -0
  11. package/dist/chunk-Q5W3WQVU.js.map +1 -0
  12. package/dist/chunk-UPDEQ2U7.js +362 -0
  13. package/dist/chunk-UPDEQ2U7.js.map +1 -0
  14. package/dist/chunk-Z7O2WNRU.js +26934 -0
  15. package/dist/chunk-Z7O2WNRU.js.map +1 -0
  16. package/dist/index.d.ts +920 -22
  17. package/dist/index.js +3212 -9
  18. package/dist/index.js.map +1 -1
  19. package/dist/main.d.ts +0 -2
  20. package/dist/main.js +19256 -13
  21. package/dist/main.js.map +1 -1
  22. package/dist/mountUtils-CCA-bbpK.d.ts +25 -0
  23. package/dist/sandboxes/daytona.d.ts +8 -5
  24. package/dist/sandboxes/daytona.js +118 -124
  25. package/dist/sandboxes/daytona.js.map +1 -1
  26. package/dist/sandboxes/docker.d.ts +10 -8
  27. package/dist/sandboxes/docker.js +8 -255
  28. package/dist/sandboxes/docker.js.map +1 -1
  29. package/dist/sandboxes/no-sandbox.d.ts +7 -4
  30. package/dist/sandboxes/no-sandbox.js +6 -114
  31. package/dist/sandboxes/no-sandbox.js.map +1 -1
  32. package/dist/sandboxes/podman.d.ts +10 -8
  33. package/dist/sandboxes/podman.js +287 -297
  34. package/dist/sandboxes/podman.js.map +1 -1
  35. package/dist/sandboxes/vercel.d.ts +7 -4
  36. package/dist/sandboxes/vercel.js +144 -165
  37. package/dist/sandboxes/vercel.js.map +1 -1
  38. package/package.json +15 -14
  39. package/dist/AgentProvider.d.ts +0 -134
  40. package/dist/AgentProvider.d.ts.map +0 -1
  41. package/dist/AgentProvider.js +0 -647
  42. package/dist/AgentProvider.js.map +0 -1
  43. package/dist/AgentStreamEmitter.d.ts +0 -36
  44. package/dist/AgentStreamEmitter.d.ts.map +0 -1
  45. package/dist/AgentStreamEmitter.js +0 -21
  46. package/dist/AgentStreamEmitter.js.map +0 -1
  47. package/dist/CopyToWorktree.d.ts +0 -15
  48. package/dist/CopyToWorktree.d.ts.map +0 -1
  49. package/dist/CopyToWorktree.js +0 -60
  50. package/dist/CopyToWorktree.js.map +0 -1
  51. package/dist/Display.d.ts +0 -58
  52. package/dist/Display.d.ts.map +0 -1
  53. package/dist/Display.js +0 -142
  54. package/dist/Display.js.map +0 -1
  55. package/dist/DockerLifecycle.d.ts +0 -54
  56. package/dist/DockerLifecycle.d.ts.map +0 -1
  57. package/dist/DockerLifecycle.js +0 -123
  58. package/dist/DockerLifecycle.js.map +0 -1
  59. package/dist/EnvResolver.d.ts +0 -11
  60. package/dist/EnvResolver.d.ts.map +0 -1
  61. package/dist/EnvResolver.js +0 -63
  62. package/dist/EnvResolver.js.map +0 -1
  63. package/dist/ErrorHandler.d.ts +0 -15
  64. package/dist/ErrorHandler.d.ts.map +0 -1
  65. package/dist/ErrorHandler.js +0 -85
  66. package/dist/ErrorHandler.js.map +0 -1
  67. package/dist/InitService.d.ts +0 -63
  68. package/dist/InitService.d.ts.map +0 -1
  69. package/dist/InitService.js +0 -733
  70. package/dist/InitService.js.map +0 -1
  71. package/dist/MountConfig.d.ts.map +0 -1
  72. package/dist/MountConfig.js +0 -7
  73. package/dist/MountConfig.js.map +0 -1
  74. package/dist/Orchestrator.d.ts +0 -56
  75. package/dist/Orchestrator.d.ts.map +0 -1
  76. package/dist/Orchestrator.js +0 -293
  77. package/dist/Orchestrator.js.map +0 -1
  78. package/dist/Output.d.ts +0 -107
  79. package/dist/Output.d.ts.map +0 -1
  80. package/dist/Output.js +0 -95
  81. package/dist/Output.js.map +0 -1
  82. package/dist/PodmanLifecycle.d.ts +0 -17
  83. package/dist/PodmanLifecycle.d.ts.map +0 -1
  84. package/dist/PodmanLifecycle.js +0 -45
  85. package/dist/PodmanLifecycle.js.map +0 -1
  86. package/dist/PromptArgumentSubstitution.d.ts +0 -32
  87. package/dist/PromptArgumentSubstitution.d.ts.map +0 -1
  88. package/dist/PromptArgumentSubstitution.js +0 -104
  89. package/dist/PromptArgumentSubstitution.js.map +0 -1
  90. package/dist/PromptPreprocessor.d.ts +0 -15
  91. package/dist/PromptPreprocessor.d.ts.map +0 -1
  92. package/dist/PromptPreprocessor.js +0 -55
  93. package/dist/PromptPreprocessor.js.map +0 -1
  94. package/dist/PromptResolver.d.ts +0 -21
  95. package/dist/PromptResolver.d.ts.map +0 -1
  96. package/dist/PromptResolver.js +0 -27
  97. package/dist/PromptResolver.js.map +0 -1
  98. package/dist/RecoveryMessage.d.ts +0 -15
  99. package/dist/RecoveryMessage.d.ts.map +0 -1
  100. package/dist/RecoveryMessage.js +0 -81
  101. package/dist/RecoveryMessage.js.map +0 -1
  102. package/dist/SandboxFactory.d.ts +0 -90
  103. package/dist/SandboxFactory.d.ts.map +0 -1
  104. package/dist/SandboxFactory.js +0 -324
  105. package/dist/SandboxFactory.js.map +0 -1
  106. package/dist/SandboxLifecycle.d.ts +0 -65
  107. package/dist/SandboxLifecycle.d.ts.map +0 -1
  108. package/dist/SandboxLifecycle.js +0 -296
  109. package/dist/SandboxLifecycle.js.map +0 -1
  110. package/dist/SandboxProvider.d.ts.map +0 -1
  111. package/dist/SandboxProvider.js +0 -28
  112. package/dist/SandboxProvider.js.map +0 -1
  113. package/dist/SessionStore.d.ts +0 -110
  114. package/dist/SessionStore.d.ts.map +0 -1
  115. package/dist/SessionStore.js +0 -330
  116. package/dist/SessionStore.js.map +0 -1
  117. package/dist/TextDeltaBuffer.d.ts +0 -24
  118. package/dist/TextDeltaBuffer.d.ts.map +0 -1
  119. package/dist/TextDeltaBuffer.js +0 -68
  120. package/dist/TextDeltaBuffer.js.map +0 -1
  121. package/dist/WorktreeManager.d.ts +0 -79
  122. package/dist/WorktreeManager.d.ts.map +0 -1
  123. package/dist/WorktreeManager.js +0 -283
  124. package/dist/WorktreeManager.js.map +0 -1
  125. package/dist/boundedTail.d.ts +0 -48
  126. package/dist/boundedTail.d.ts.map +0 -1
  127. package/dist/boundedTail.js +0 -64
  128. package/dist/boundedTail.js.map +0 -1
  129. package/dist/cli.d.ts +0 -30
  130. package/dist/cli.d.ts.map +0 -1
  131. package/dist/cli.js +0 -309
  132. package/dist/cli.js.map +0 -1
  133. package/dist/createSandbox.d.ts +0 -154
  134. package/dist/createSandbox.d.ts.map +0 -1
  135. package/dist/createSandbox.js +0 -476
  136. package/dist/createSandbox.js.map +0 -1
  137. package/dist/createWorktree.d.ts +0 -154
  138. package/dist/createWorktree.d.ts.map +0 -1
  139. package/dist/createWorktree.js +0 -391
  140. package/dist/createWorktree.js.map +0 -1
  141. package/dist/errors.d.ts +0 -227
  142. package/dist/errors.d.ts.map +0 -1
  143. package/dist/errors.js +0 -81
  144. package/dist/errors.js.map +0 -1
  145. package/dist/extractStructuredOutput.d.ts +0 -23
  146. package/dist/extractStructuredOutput.d.ts.map +0 -1
  147. package/dist/extractStructuredOutput.js +0 -102
  148. package/dist/extractStructuredOutput.js.map +0 -1
  149. package/dist/index.d.ts.map +0 -1
  150. package/dist/interactive.d.ts +0 -74
  151. package/dist/interactive.d.ts.map +0 -1
  152. package/dist/interactive.js +0 -279
  153. package/dist/interactive.js.map +0 -1
  154. package/dist/main.d.ts.map +0 -1
  155. package/dist/mergeProviderEnv.d.ts +0 -13
  156. package/dist/mergeProviderEnv.d.ts.map +0 -1
  157. package/dist/mergeProviderEnv.js +0 -23
  158. package/dist/mergeProviderEnv.js.map +0 -1
  159. package/dist/mountUtils.d.ts +0 -146
  160. package/dist/mountUtils.d.ts.map +0 -1
  161. package/dist/mountUtils.js +0 -301
  162. package/dist/mountUtils.js.map +0 -1
  163. package/dist/raceAbortSignal.d.ts +0 -18
  164. package/dist/raceAbortSignal.d.ts.map +0 -1
  165. package/dist/raceAbortSignal.js +0 -32
  166. package/dist/raceAbortSignal.js.map +0 -1
  167. package/dist/resolveCwd.d.ts +0 -24
  168. package/dist/resolveCwd.d.ts.map +0 -1
  169. package/dist/resolveCwd.js +0 -32
  170. package/dist/resolveCwd.js.map +0 -1
  171. package/dist/resumePrecheck.d.ts +0 -26
  172. package/dist/resumePrecheck.d.ts.map +0 -1
  173. package/dist/resumePrecheck.js +0 -40
  174. package/dist/resumePrecheck.js.map +0 -1
  175. package/dist/run.d.ts +0 -216
  176. package/dist/run.d.ts.map +0 -1
  177. package/dist/run.js +0 -313
  178. package/dist/run.js.map +0 -1
  179. package/dist/sandboxExec.d.ts +0 -12
  180. package/dist/sandboxExec.d.ts.map +0 -1
  181. package/dist/sandboxExec.js +0 -26
  182. package/dist/sandboxExec.js.map +0 -1
  183. package/dist/sandboxes/daytona.d.ts.map +0 -1
  184. package/dist/sandboxes/docker.d.ts.map +0 -1
  185. package/dist/sandboxes/no-sandbox.d.ts.map +0 -1
  186. package/dist/sandboxes/podman.d.ts.map +0 -1
  187. package/dist/sandboxes/test-bind-mount.d.ts +0 -17
  188. package/dist/sandboxes/test-bind-mount.d.ts.map +0 -1
  189. package/dist/sandboxes/test-bind-mount.js +0 -92
  190. package/dist/sandboxes/test-bind-mount.js.map +0 -1
  191. package/dist/sandboxes/test-isolated.d.ts +0 -17
  192. package/dist/sandboxes/test-isolated.d.ts.map +0 -1
  193. package/dist/sandboxes/test-isolated.js +0 -98
  194. package/dist/sandboxes/test-isolated.js.map +0 -1
  195. package/dist/sandboxes/vercel.d.ts.map +0 -1
  196. package/dist/shutdownRegistry.d.ts +0 -30
  197. package/dist/shutdownRegistry.d.ts.map +0 -1
  198. package/dist/shutdownRegistry.js +0 -73
  199. package/dist/shutdownRegistry.js.map +0 -1
  200. package/dist/startSandbox.d.ts +0 -50
  201. package/dist/startSandbox.d.ts.map +0 -1
  202. package/dist/startSandbox.js +0 -117
  203. package/dist/startSandbox.js.map +0 -1
  204. package/dist/syncIn.d.ts +0 -24
  205. package/dist/syncIn.d.ts.map +0 -1
  206. package/dist/syncIn.js +0 -107
  207. package/dist/syncIn.js.map +0 -1
  208. package/dist/syncOut.d.ts +0 -27
  209. package/dist/syncOut.d.ts.map +0 -1
  210. package/dist/syncOut.js +0 -271
  211. package/dist/syncOut.js.map +0 -1
  212. package/dist/templates.d.ts +0 -2
  213. package/dist/templates.d.ts.map +0 -1
  214. package/dist/templates.js +0 -26
  215. package/dist/templates.js.map +0 -1
  216. package/dist/terminalCleanup.d.ts +0 -30
  217. package/dist/terminalCleanup.d.ts.map +0 -1
  218. package/dist/terminalCleanup.js +0 -37
  219. package/dist/terminalCleanup.js.map +0 -1
  220. package/dist/testSandbox.d.ts +0 -8
  221. package/dist/testSandbox.d.ts.map +0 -1
  222. package/dist/testSandbox.js +0 -109
  223. package/dist/testSandbox.js.map +0 -1
  224. package/dist/testSetup.d.ts +0 -2
  225. package/dist/testSetup.d.ts.map +0 -1
  226. package/dist/testSetup.js +0 -29
  227. package/dist/testSetup.js.map +0 -1
@@ -1,733 +0,0 @@
1
- import { FileSystem } from "@effect/platform";
2
- import { Effect } from "effect";
3
- import { dirname, join } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- import { SANDBOX_REPO_DIR } from "./SandboxFactory.js";
6
- const GITIGNORE = `.env
7
- logs/
8
- worktrees/
9
- `;
10
- /**
11
- * Filename of the setup prompt scaffolded for the `custom` issue tracker.
12
- * Both the per-agent `setupCommand` and the in-scaffold sentinels point at it,
13
- * so it is defined once here.
14
- */
15
- const SETUP_ISSUE_TRACKER_DOC = "SETUP_ISSUE_TRACKER.md";
16
- const SETUP_ISSUE_TRACKER_PATH = `.sandcastle/${SETUP_ISSUE_TRACKER_DOC}`;
17
- const TEMPLATES = [
18
- {
19
- name: "blank",
20
- description: "Bare scaffold — write your own prompt and orchestration",
21
- },
22
- {
23
- name: "simple-loop",
24
- description: "Picks issues one by one and closes them",
25
- },
26
- {
27
- name: "sequential-reviewer",
28
- description: "Implements issues one by one, with a code review step after each",
29
- },
30
- {
31
- name: "parallel-planner",
32
- description: "Plans parallelizable issues, executes on separate branches, merges",
33
- },
34
- {
35
- name: "parallel-planner-with-review",
36
- description: "Plans parallelizable issues, executes with per-branch review, merges",
37
- },
38
- ];
39
- export const listTemplates = () => TEMPLATES;
40
- const CLAUDE_CODE_DOCKERFILE = `FROM node:22-bookworm
41
-
42
- # Install system dependencies
43
- RUN apt-get update && apt-get install -y \\
44
- git \\
45
- curl \\
46
- jq \\
47
- && rm -rf /var/lib/apt/lists/*
48
-
49
- {{ISSUE_TRACKER_TOOLS}}
50
-
51
- # Build-args for UID/GID alignment: sandcastle docker build-image
52
- # defaults these to the host user's UID/GID so image-built files
53
- # and bind-mounted files share an owner without runtime chown.
54
- ARG AGENT_UID=1000
55
- ARG AGENT_GID=1000
56
-
57
- # Rename the base image's "node" user to "agent" and align UID/GID.
58
- RUN groupmod -o -g $AGENT_GID node && usermod -o -u $AGENT_UID -g $AGENT_GID -d /home/agent -m -l agent node
59
- USER \${AGENT_UID}:\${AGENT_GID}
60
-
61
- # Install Claude Code CLI
62
- RUN curl -fsSL https://claude.ai/install.sh | bash
63
-
64
- # Add Claude to PATH
65
- ENV PATH="/home/agent/.local/bin:$PATH"
66
-
67
- WORKDIR /home/agent
68
-
69
- # In worktree sandbox mode, Sandcastle bind-mounts the git worktree at ${SANDBOX_REPO_DIR}
70
- # and overrides the working directory to ${SANDBOX_REPO_DIR} at container start.
71
- # Structure your Dockerfile so that ${SANDBOX_REPO_DIR} can serve as the project root.
72
- ENTRYPOINT ["sleep", "infinity"]
73
- `;
74
- const PI_DOCKERFILE = `FROM node:22-bookworm
75
-
76
- # Install system dependencies
77
- RUN apt-get update && apt-get install -y \\
78
- git \\
79
- curl \\
80
- jq \\
81
- && rm -rf /var/lib/apt/lists/*
82
-
83
- {{ISSUE_TRACKER_TOOLS}}
84
-
85
- # Build-args for UID/GID alignment: sandcastle docker build-image
86
- # defaults these to the host user's UID/GID so image-built files
87
- # and bind-mounted files share an owner without runtime chown.
88
- ARG AGENT_UID=1000
89
- ARG AGENT_GID=1000
90
-
91
- # Rename the base image's "node" user to "agent" and align UID/GID.
92
- RUN groupmod -o -g $AGENT_GID node && usermod -o -u $AGENT_UID -g $AGENT_GID -d /home/agent -m -l agent node
93
-
94
- # Install pi coding agent (run as root before USER agent)
95
- RUN npm install -g @mariozechner/pi-coding-agent
96
-
97
- USER \${AGENT_UID}:\${AGENT_GID}
98
-
99
- WORKDIR /home/agent
100
-
101
- # In worktree sandbox mode, Sandcastle bind-mounts the git worktree at ${SANDBOX_REPO_DIR}
102
- # and overrides the working directory to ${SANDBOX_REPO_DIR} at container start.
103
- # Structure your Dockerfile so that ${SANDBOX_REPO_DIR} can serve as the project root.
104
- ENTRYPOINT ["sleep", "infinity"]
105
- `;
106
- const CODEX_DOCKERFILE = `FROM node:22-bookworm
107
-
108
- # Install system dependencies
109
- RUN apt-get update && apt-get install -y \\
110
- git \\
111
- curl \\
112
- jq \\
113
- && rm -rf /var/lib/apt/lists/*
114
-
115
- {{ISSUE_TRACKER_TOOLS}}
116
-
117
- # Build-args for UID/GID alignment: sandcastle docker build-image
118
- # defaults these to the host user's UID/GID so image-built files
119
- # and bind-mounted files share an owner without runtime chown.
120
- ARG AGENT_UID=1000
121
- ARG AGENT_GID=1000
122
-
123
- # Rename the base image's "node" user to "agent" and align UID/GID.
124
- RUN groupmod -o -g $AGENT_GID node && usermod -o -u $AGENT_UID -g $AGENT_GID -d /home/agent -m -l agent node
125
-
126
- # Install Codex CLI (run as root before USER agent)
127
- RUN npm install -g @openai/codex
128
-
129
- USER \${AGENT_UID}:\${AGENT_GID}
130
-
131
- WORKDIR /home/agent
132
-
133
- # In worktree sandbox mode, Sandcastle bind-mounts the git worktree at ${SANDBOX_REPO_DIR}
134
- # and overrides the working directory to ${SANDBOX_REPO_DIR} at container start.
135
- # Structure your Dockerfile so that ${SANDBOX_REPO_DIR} can serve as the project root.
136
- ENTRYPOINT ["sleep", "infinity"]
137
- `;
138
- const CURSOR_DOCKERFILE = `FROM node:22-bookworm
139
-
140
- # Install system dependencies
141
- RUN apt-get update && apt-get install -y \\
142
- git \\
143
- curl \\
144
- jq \\
145
- && rm -rf /var/lib/apt/lists/*
146
-
147
- {{ISSUE_TRACKER_TOOLS}}
148
-
149
- # Build-args for UID/GID alignment: sandcastle docker build-image
150
- # defaults these to the host user's UID/GID so image-built files
151
- # and bind-mounted files share an owner without runtime chown.
152
- ARG AGENT_UID=1000
153
- ARG AGENT_GID=1000
154
-
155
- # Rename the base image's "node" user to "agent" and align UID/GID.
156
- RUN groupmod -g $AGENT_GID node && usermod -u $AGENT_UID -g $AGENT_GID -d /home/agent -m -l agent node
157
- USER \${AGENT_UID}:\${AGENT_GID}
158
-
159
- # Install Cursor Agent CLI
160
- RUN curl https://cursor.com/install -fsS | bash
161
-
162
- # Add Cursor CLI to PATH
163
- ENV PATH="/home/agent/.local/bin:$PATH"
164
-
165
- WORKDIR /home/agent
166
-
167
- # In worktree sandbox mode, Sandcastle bind-mounts the git worktree at ${SANDBOX_REPO_DIR}
168
- # and overrides the working directory to ${SANDBOX_REPO_DIR} at container start.
169
- # Structure your Dockerfile so that ${SANDBOX_REPO_DIR} can serve as the project root.
170
- ENTRYPOINT ["sleep", "infinity"]
171
- `;
172
- const OPENCODE_DOCKERFILE = `FROM node:22-bookworm
173
-
174
- # Install system dependencies
175
- RUN apt-get update && apt-get install -y \\
176
- git \\
177
- curl \\
178
- jq \\
179
- && rm -rf /var/lib/apt/lists/*
180
-
181
- {{ISSUE_TRACKER_TOOLS}}
182
-
183
- # Build-args for UID/GID alignment: sandcastle docker build-image
184
- # defaults these to the host user's UID/GID so image-built files
185
- # and bind-mounted files share an owner without runtime chown.
186
- ARG AGENT_UID=1000
187
- ARG AGENT_GID=1000
188
-
189
- # Rename the base image's "node" user to "agent" and align UID/GID.
190
- RUN groupmod -o -g $AGENT_GID node && usermod -o -u $AGENT_UID -g $AGENT_GID -d /home/agent -m -l agent node
191
-
192
- # Install OpenCode CLI (run as root before USER agent)
193
- RUN npm install -g opencode-ai@latest
194
-
195
- USER \${AGENT_UID}:\${AGENT_GID}
196
-
197
- WORKDIR /home/agent
198
-
199
- # In worktree sandbox mode, Sandcastle bind-mounts the git worktree at \${SANDBOX_REPO_DIR}
200
- # and overrides the working directory to \${SANDBOX_REPO_DIR} at container start.
201
- # Structure your Dockerfile so that \${SANDBOX_REPO_DIR} can serve as the project root.
202
- ENTRYPOINT ["sleep", "infinity"]
203
- `;
204
- const COPILOT_DOCKERFILE = `FROM node:22-bookworm
205
-
206
- # Install system dependencies
207
- RUN apt-get update && apt-get install -y \\
208
- git \\
209
- curl \\
210
- jq \\
211
- && rm -rf /var/lib/apt/lists/*
212
-
213
- {{ISSUE_TRACKER_TOOLS}}
214
-
215
- # Build-args for UID/GID alignment: sandcastle docker build-image
216
- # defaults these to the host user's UID/GID so image-built files
217
- # and bind-mounted files share an owner without runtime chown.
218
- ARG AGENT_UID=1000
219
- ARG AGENT_GID=1000
220
-
221
- # Rename the base image's "node" user to "agent" and align UID/GID.
222
- RUN groupmod -o -g $AGENT_GID node && usermod -o -u $AGENT_UID -g $AGENT_GID -d /home/agent -m -l agent node
223
-
224
- # Install GitHub Copilot CLI (run as root before USER agent)
225
- RUN npm install -g @github/copilot
226
-
227
- USER \${AGENT_UID}:\${AGENT_GID}
228
-
229
- WORKDIR /home/agent
230
-
231
- # In worktree sandbox mode, Sandcastle bind-mounts the git worktree at \${SANDBOX_REPO_DIR}
232
- # and overrides the working directory to \${SANDBOX_REPO_DIR} at container start.
233
- # Structure your Dockerfile so that \${SANDBOX_REPO_DIR} can serve as the project root.
234
- ENTRYPOINT ["sleep", "infinity"]
235
- `;
236
- const AGENT_REGISTRY = [
237
- {
238
- name: "claude-code",
239
- label: "Claude Code",
240
- defaultModel: "claude-opus-4-7",
241
- factoryImport: "claudeCode",
242
- dockerfileTemplate: CLAUDE_CODE_DOCKERFILE,
243
- envExample: `# Anthropic API key
244
- # If you want to use your Claude subscription instead of an API key, see https://github.com/mattpocock/sandcastle/issues/191
245
- ANTHROPIC_API_KEY=`,
246
- setupCommand: `claude "$(cat ${SETUP_ISSUE_TRACKER_PATH})"`,
247
- },
248
- {
249
- name: "pi",
250
- label: "Pi",
251
- defaultModel: "claude-sonnet-4-6",
252
- factoryImport: "pi",
253
- dockerfileTemplate: PI_DOCKERFILE,
254
- envExample: `# Anthropic API key
255
- ANTHROPIC_API_KEY=`,
256
- setupCommand: `pi "$(cat ${SETUP_ISSUE_TRACKER_PATH})"`,
257
- },
258
- {
259
- name: "codex",
260
- label: "Codex",
261
- defaultModel: "gpt-5.4-mini",
262
- factoryImport: "codex",
263
- dockerfileTemplate: CODEX_DOCKERFILE,
264
- envExample: `# OpenAI API key
265
- OPENAI_KEY=`,
266
- setupCommand: `codex "$(cat ${SETUP_ISSUE_TRACKER_PATH})"`,
267
- },
268
- {
269
- name: "cursor",
270
- label: "Cursor",
271
- defaultModel: "composer-2",
272
- factoryImport: "cursor",
273
- dockerfileTemplate: CURSOR_DOCKERFILE,
274
- envExample: `# Cursor API key (recommended)
275
- # You can also pass --api-key directly to the agent CLI.
276
- CURSOR_API_KEY=`,
277
- setupCommand: `agent "$(cat ${SETUP_ISSUE_TRACKER_PATH})"`,
278
- },
279
- {
280
- name: "opencode",
281
- label: "OpenCode",
282
- defaultModel: "opencode/big-pickle",
283
- factoryImport: "opencode",
284
- dockerfileTemplate: OPENCODE_DOCKERFILE,
285
- envExample: `# OpenCode API key
286
- OPENCODE_API_KEY=`,
287
- setupCommand: `opencode -p "$(cat ${SETUP_ISSUE_TRACKER_PATH})"`,
288
- },
289
- {
290
- name: "copilot",
291
- label: "GitHub Copilot CLI",
292
- defaultModel: "claude-sonnet-4.5",
293
- factoryImport: "copilot",
294
- dockerfileTemplate: COPILOT_DOCKERFILE,
295
- envExample: `# GitHub token with the "Copilot Requests" permission
296
- # (a fine-grained PAT, or any token from \`gh auth login\`).
297
- # COPILOT_GITHUB_TOKEN takes precedence over GH_TOKEN and GITHUB_TOKEN.
298
- GITHUB_TOKEN=`,
299
- setupCommand: `copilot -i "$(cat ${SETUP_ISSUE_TRACKER_PATH})"`,
300
- },
301
- ];
302
- export const listAgents = () => AGENT_REGISTRY;
303
- const GITHUB_CLI_TOOLS = `# Install GitHub CLI
304
- RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \\
305
- | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \\
306
- && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \\
307
- | tee /etc/apt/sources.list.d/github-cli.list > /dev/null \\
308
- && apt-get update && apt-get install -y gh \\
309
- && rm -rf /var/lib/apt/lists/*`;
310
- const BEADS_TOOLS = `# Install system dependencies for Beads
311
- RUN apt-get update && apt-get install -y \\
312
- dpkg-dev \\
313
- libicu72 \\
314
- && rm -rf /var/lib/apt/lists/* \\
315
- && ARCH_DIR=$(dpkg-architecture -qDEB_HOST_MULTIARCH) \\
316
- && for lib in /usr/lib/$ARCH_DIR/libicu*.so.72; do \\
317
- ln -s "$lib" "\${lib%.72}.74"; \\
318
- done
319
-
320
- RUN curl -fsSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash
321
-
322
- RUN corepack enable`;
323
- // Sentinels baked into the scaffold for the `custom` issue tracker. The
324
- // project ships deliberately broken-until-configured; the setup agent finds
325
- // and replaces these markers in place (see SETUP_ISSUE_TRACKER.md). Defined as
326
- // shared constants so the registry entry and the setup doc stay in sync.
327
- const CUSTOM_LIST_TASKS_SENTINEL = `echo 'No issue tracker configured — run ${SETUP_ISSUE_TRACKER_PATH} through your coding agent.' >&2; exit 1`;
328
- const CUSTOM_VIEW_TASK_MARKER = `<view command — see ${SETUP_ISSUE_TRACKER_PATH}>`;
329
- const CUSTOM_CLOSE_TASK_MARKER = `<close command — see ${SETUP_ISSUE_TRACKER_PATH}>`;
330
- const CUSTOM_TRACKER_TOOLS = `# TODO: install your issue tracker's CLI here. See ${SETUP_ISSUE_TRACKER_PATH}`;
331
- const CUSTOM_ENV_EXAMPLE = `# TODO: add any env vars your issue tracker needs (e.g. an API token).
332
- # See ${SETUP_ISSUE_TRACKER_PATH}`;
333
- const ISSUE_TRACKER_REGISTRY = [
334
- {
335
- name: "github-issues",
336
- label: "GitHub Issues",
337
- templateArgs: {
338
- LIST_TASKS_COMMAND: `gh issue list --state open --label Sandcastle --limit 100 --json number,title,body,labels,comments --jq '[.[] | {number, title, body, labels: [.labels[].name], comments: [.comments[].body]}]'`,
339
- VIEW_TASK_COMMAND: "gh issue view <ID>",
340
- CLOSE_TASK_COMMAND: `gh issue close <ID> --comment "Completed by Sandcastle"`,
341
- ISSUE_TRACKER_TOOLS: GITHUB_CLI_TOOLS,
342
- },
343
- envExample: `# GitHub personal access token — the agent uses it to read and manage GitHub Issues
344
- # Create a fine-grained token: https://github.com/settings/personal-access-tokens/new
345
- # Required repository permissions: Issues (Read and write) and Metadata (Read)
346
- GH_TOKEN=`,
347
- },
348
- {
349
- name: "beads",
350
- label: "Beads",
351
- templateArgs: {
352
- LIST_TASKS_COMMAND: "bd ready --json",
353
- VIEW_TASK_COMMAND: "bd show <ID>",
354
- CLOSE_TASK_COMMAND: `bd close <ID> --reason="Completed by Sandcastle"`,
355
- ISSUE_TRACKER_TOOLS: BEADS_TOOLS,
356
- },
357
- envExample: "",
358
- },
359
- {
360
- name: "custom",
361
- label: "Custom",
362
- templateArgs: {
363
- // The only real shell expression: PromptPreprocessor fails the run on a
364
- // non-zero exit and surfaces stderr, so this is the single enforcement
365
- // point that keeps the scaffold broken until the user configures it.
366
- LIST_TASKS_COMMAND: CUSTOM_LIST_TASKS_SENTINEL,
367
- // Inline text markers — replaced by the setup agent, never executed.
368
- VIEW_TASK_COMMAND: CUSTOM_VIEW_TASK_MARKER,
369
- CLOSE_TASK_COMMAND: CUSTOM_CLOSE_TASK_MARKER,
370
- ISSUE_TRACKER_TOOLS: CUSTOM_TRACKER_TOOLS,
371
- },
372
- envExample: CUSTOM_ENV_EXAMPLE,
373
- },
374
- ];
375
- export const listIssueTrackers = () => ISSUE_TRACKER_REGISTRY;
376
- export const getIssueTracker = (name) => ISSUE_TRACKER_REGISTRY.find((b) => b.name === name);
377
- export const getAgent = (name) => AGENT_REGISTRY.find((a) => a.name === name);
378
- const SANDBOX_PROVIDER_REGISTRY = [
379
- {
380
- name: "docker",
381
- label: "Docker",
382
- containerfileName: "Dockerfile",
383
- cliNamespace: "docker",
384
- },
385
- {
386
- name: "podman",
387
- label: "Podman",
388
- containerfileName: "Containerfile",
389
- cliNamespace: "podman",
390
- },
391
- ];
392
- export const listSandboxProviders = () => SANDBOX_PROVIDER_REGISTRY;
393
- export const getSandboxProvider = (name) => SANDBOX_PROVIDER_REGISTRY.find((p) => p.name === name);
394
- // ---------------------------------------------------------------------------
395
- // Next steps
396
- // ---------------------------------------------------------------------------
397
- export function getNextStepsLines(template, mainFilename, issueTracker, agent) {
398
- // The custom issue tracker scaffolds a broken-until-configured project, so
399
- // its next steps are about running the setup prompt — not the template's
400
- // normal "set env vars and go" flow. This branch wins over template-specific
401
- // steps regardless of the chosen template.
402
- if (issueTracker.name === "custom") {
403
- return [
404
- "Next steps:",
405
- "1. Your custom issue tracker isn't wired up yet — runs hard-fail until you configure it.",
406
- `2. Feed the setup prompt to ${agent.label} on your host to finish wiring it up:`,
407
- ` ${agent.setupCommand}`,
408
- ` (Runs on the host — you need the ${agent.label} CLI installed locally, since the sandbox image isn't built yet.)`,
409
- `3. Follow .sandcastle/${SETUP_ISSUE_TRACKER_DOC} to edit the scaffolded files in place, build the image, and verify.`,
410
- ];
411
- }
412
- if (template === "blank") {
413
- return [
414
- "Next steps:",
415
- `1. Set the required env vars in .sandcastle/.env (see .sandcastle/.env.example)`,
416
- " If you want to use your Claude subscription instead of an API key, see https://github.com/mattpocock/sandcastle/issues/191",
417
- "2. Read and customize .sandcastle/prompt.md to describe what you want the agent to do",
418
- `3. Customize .sandcastle/${mainFilename} — it uses the JS API (\`run()\`) to control how the agent runs`,
419
- `4. Add "sandcastle": "npx tsx .sandcastle/${mainFilename}" to your package.json scripts`,
420
- "5. Run `npm run sandcastle` to start the agent",
421
- ];
422
- }
423
- else {
424
- const hasReviewer = template.includes("review");
425
- const usesPlanSchema = template.includes("planner");
426
- let step = 1;
427
- const lines = [
428
- "Next steps:",
429
- `${step++}. Set the required env vars in .sandcastle/.env (see .sandcastle/.env.example)`,
430
- " If you want to use your Claude subscription instead of an API key, see https://github.com/mattpocock/sandcastle/issues/191",
431
- `${step++}. Add "sandcastle": "npx tsx .sandcastle/${mainFilename}" to your package.json scripts`,
432
- `${step++}. Templates use \`copyToWorktree: ["node_modules"]\` to copy your host node_modules into the sandbox for fast startup — the \`npm install\` in the onSandboxReady hook is a safety net for platform-specific binaries. Adjust both if you use a different package manager`,
433
- ];
434
- if (usesPlanSchema) {
435
- lines.push(`${step++}. Install a schema validator for the planner's \`<plan>\` output — the template uses Zod (\`npm install zod\`), but Valibot, ArkType, or any Standard Schema library works (https://standardschema.dev)`);
436
- }
437
- lines.push(`${step++}. Read and customize the prompt files in .sandcastle/ — they shape what the agent does`);
438
- if (hasReviewer) {
439
- lines.push(`${step++}. Customize .sandcastle/CODING_STANDARDS.md with your project's standards — the reviewer agent loads it during review`);
440
- }
441
- lines.push(`${step++}. Run \`npm run sandcastle\` to start the agent`);
442
- return lines;
443
- }
444
- }
445
- // ---------------------------------------------------------------------------
446
- // Scaffolding helpers
447
- // ---------------------------------------------------------------------------
448
- function getTemplatesDir() {
449
- const thisFile = fileURLToPath(import.meta.url);
450
- return join(dirname(thisFile), "templates");
451
- }
452
- const getTemplateDir = (templateName) => Effect.gen(function* () {
453
- const template = TEMPLATES.find((t) => t.name === templateName);
454
- if (!template) {
455
- const names = TEMPLATES.map((t) => t.name).join(", ");
456
- yield* Effect.fail(new Error(`Unknown template: "${templateName}". Available: ${names}`));
457
- }
458
- return join(getTemplatesDir(), templateName);
459
- });
460
- const COMPILED_FILE_EXTENSIONS = [
461
- ".js",
462
- ".js.map",
463
- ".d.ts",
464
- ".d.ts.map",
465
- ".mjs",
466
- ".mjs.map",
467
- ".d.mts",
468
- ".d.mts.map",
469
- ];
470
- const copyTemplateFiles = (templateDir, destDir, mainFilename) => Effect.gen(function* () {
471
- const fs = yield* FileSystem.FileSystem;
472
- const files = yield* fs
473
- .readDirectory(templateDir)
474
- .pipe(Effect.mapError((e) => new Error(e.message)));
475
- yield* Effect.all(files
476
- .filter((f) => f !== "template.json" &&
477
- f !== ".env.example" &&
478
- !COMPILED_FILE_EXTENSIONS.some((ext) => f.endsWith(ext)))
479
- .map((f) => {
480
- const destName = f === "main.mts" ? mainFilename : f;
481
- return fs
482
- .copyFile(join(templateDir, f), join(destDir, destName))
483
- .pipe(Effect.mapError((e) => new Error(e.message)));
484
- }), { concurrency: "unbounded" });
485
- });
486
- /**
487
- * Replace the agent factory and sandbox provider in a scaffolded main.ts.
488
- *
489
- * Templates use `claudeCode` as the default agent factory and `docker` as the
490
- * default sandbox provider. When a different agent, model, or sandbox provider
491
- * is selected, this function rewrites the imports and factory calls.
492
- */
493
- const rewriteMainTs = (configDir, agent, model, sandboxProvider, mainFilename) => Effect.gen(function* () {
494
- const fs = yield* FileSystem.FileSystem;
495
- const mainTsPath = join(configDir, mainFilename);
496
- const exists = yield* fs
497
- .exists(mainTsPath)
498
- .pipe(Effect.mapError((e) => new Error(e.message)));
499
- if (!exists)
500
- return;
501
- let content = yield* fs
502
- .readFileString(mainTsPath)
503
- .pipe(Effect.mapError((e) => new Error(e.message)));
504
- // Templates use main.mts as the canonical filename in comments.
505
- // When the target is main.ts, rewrite those references.
506
- if (mainFilename === "main.ts") {
507
- content = content.replace(/main\.mts/g, "main.ts");
508
- }
509
- // Replace factory function name in imports (e.g. claudeCode → pi)
510
- // and all factory calls with the correct model.
511
- // Templates always use claudeCode as the placeholder factory.
512
- content = content.replace(/\bclaudeCode\b/g, agent.factoryImport);
513
- // Replace model strings in factory calls: factoryImport("any-model")
514
- const factoryCallRe = new RegExp(`${agent.factoryImport}\\(["']([^"']+)["']\\)`, "g");
515
- content = content.replace(factoryCallRe, `${agent.factoryImport}("${model}")`);
516
- // Replace the sandbox provider. Templates always use `docker` as the
517
- // placeholder, where the registry name doubles as both the factory function
518
- // name and the `/sandboxes/<name>` import subpath segment. A single
519
- // case-sensitive word-boundary replace therefore rewrites the named import,
520
- // the import subpath, and every factory call site — and is a no-op when
521
- // docker is selected.
522
- content = content.replace(/\bdocker\b/g, sandboxProvider.name);
523
- yield* fs
524
- .writeFileString(mainTsPath, content)
525
- .pipe(Effect.mapError((e) => new Error(e.message)));
526
- });
527
- /**
528
- * When the user opted out of the Sandcastle label, strip ` --label Sandcastle`
529
- * from all `.md` files in the scaffolded config directory so that `gh issue list`
530
- * commands work without a label filter.
531
- */
532
- const rewritePromptFiles = (configDir) => Effect.gen(function* () {
533
- const fs = yield* FileSystem.FileSystem;
534
- const files = yield* fs
535
- .readDirectory(configDir)
536
- .pipe(Effect.mapError((e) => new Error(e.message)));
537
- const mdFiles = files.filter((f) => f.endsWith(".md"));
538
- yield* Effect.all(mdFiles.map((f) => Effect.gen(function* () {
539
- const filePath = join(configDir, f);
540
- const content = yield* fs
541
- .readFileString(filePath)
542
- .pipe(Effect.mapError((e) => new Error(e.message)));
543
- const updated = content.replace(/ --label Sandcastle/g, "");
544
- if (updated !== content) {
545
- yield* fs
546
- .writeFileString(filePath, updated)
547
- .pipe(Effect.mapError((e) => new Error(e.message)));
548
- }
549
- })), { concurrency: "unbounded" });
550
- });
551
- /** Text file extensions eligible for `{{KEY}}` template argument substitution. */
552
- const TEXT_FILE_EXTENSIONS = new Set([
553
- ".md",
554
- ".txt",
555
- ".env",
556
- ".example",
557
- // Dockerfile / Containerfile have no extension — handled by name check below
558
- ]);
559
- const isTextFile = (filename) => {
560
- if (filename === "Dockerfile" ||
561
- filename === "Containerfile" ||
562
- filename === ".gitignore")
563
- return true;
564
- const dotIdx = filename.lastIndexOf(".");
565
- if (dotIdx === -1)
566
- return false;
567
- return TEXT_FILE_EXTENSIONS.has(filename.slice(dotIdx));
568
- };
569
- /**
570
- * Replace `{{KEY}}` template arguments from the issue tracker's
571
- * `templateArgs` map in all text files in the scaffolded config directory.
572
- */
573
- const substituteTemplateArgs = (configDir, issueTracker) => Effect.gen(function* () {
574
- const fs = yield* FileSystem.FileSystem;
575
- const files = yield* fs
576
- .readDirectory(configDir)
577
- .pipe(Effect.mapError((e) => new Error(e.message)));
578
- const textFiles = files.filter(isTextFile);
579
- yield* Effect.all(textFiles.map((f) => Effect.gen(function* () {
580
- const filePath = join(configDir, f);
581
- let content = yield* fs
582
- .readFileString(filePath)
583
- .pipe(Effect.mapError((e) => new Error(e.message)));
584
- const original = content;
585
- for (const [key, value] of Object.entries(issueTracker.templateArgs)) {
586
- content = content.replace(new RegExp(`\\{\\{${key}\\}\\}`, "g"), value);
587
- }
588
- if (content !== original) {
589
- yield* fs
590
- .writeFileString(filePath, content)
591
- .pipe(Effect.mapError((e) => new Error(e.message)));
592
- }
593
- })), { concurrency: "unbounded" });
594
- });
595
- /**
596
- * Build the `SETUP_ISSUE_TRACKER.md` prompt scaffolded for the `custom` issue
597
- * tracker. It addresses the user's coding agent and walks it through wiring up
598
- * the tracker by editing the scaffolded files in place. The build command is
599
- * provider-parameterized so it names the actual CLI namespace (docker/podman).
600
- */
601
- const buildSetupIssueTrackerDoc = (cliNamespace) => `# Set up your custom issue tracker
602
-
603
- You are a coding agent. Finish wiring up the **custom issue tracker** for this Sandcastle project. It was scaffolded in a deliberately broken-until-configured state: until you complete the steps below, every Sandcastle run hard-fails with a pointer back to this file.
604
-
605
- ## Goal
606
-
607
- Wire up the issue tracker so the scaffolded prompts can **list**, **view**, and **close** tasks. There is no runtime abstraction to implement — the tracker commands are baked into the scaffolded files, so you edit those files **in place**.
608
-
609
- ## 1. Interview the user
610
-
611
- Ask the user:
612
-
613
- - Which issue tracker do they use (e.g. Jira, Linear, a GitHub repo other than this one, an internal API)?
614
- - How should the sandbox authenticate — a CLI that is already logged in, or an API token? If a token, what is the environment variable name?
615
-
616
- ## 2. Produce three commands
617
-
618
- Work out, together with the user, the shell commands for:
619
-
620
- - **list** — print all open tasks **as JSON** (match the shape the built-in trackers emit: an array of objects, each with at least an id/number, title, and body). This is what the agent reads at the start of every iteration.
621
- - **view** \`<ID>\` — show a single task by id.
622
- - **close** \`<ID>\` — close a single task by id.
623
-
624
- ## 3. Edit the scaffolded files in place
625
-
626
- - **Dockerfile / Containerfile** — replace the line
627
-
628
- \`\`\`
629
- ${CUSTOM_TRACKER_TOOLS}
630
- \`\`\`
631
-
632
- with the install steps for your tracker's CLI (if it needs one).
633
-
634
- - **Prompt files (\`.sandcastle/*.md\`)** — replace the sentinel
635
-
636
- \`\`\`
637
- ${CUSTOM_LIST_TASKS_SENTINEL}
638
- \`\`\`
639
-
640
- with your **list** command. In the prompt file the sentinel sits inside a Sandcastle **shell expression** — a leading \`!\` followed by the command in backticks — whose output is injected into the prompt before each run. Keep that \`!\` and the surrounding backticks; replace only the command between them, and **remove the \`exit 1\`** (leaving it keeps every run hard-failing). Then replace the \`${CUSTOM_VIEW_TASK_MARKER}\` and \`${CUSTOM_CLOSE_TASK_MARKER}\` markers with your **view** and **close** commands.
641
-
642
- - **\`.env.example\`** — replace the \`# TODO\` block with the real env var(s) your tracker needs, then tell the user to set them in \`.sandcastle/.env\`.
643
-
644
- ## 4. Build the image
645
-
646
- Once the files are wired up, build the sandbox image:
647
-
648
- \`\`\`
649
- sandcastle ${cliNamespace} build-image
650
- \`\`\`
651
-
652
- ## 5. Verify
653
-
654
- Run your **list** command inside the built image and confirm it returns the open tasks as JSON. If it errors, fix the command or the auth and rebuild.
655
- `;
656
- /**
657
- * Detect whether the project's package.json has `"type": "module"`.
658
- * If so, we can use plain `.ts`; otherwise we use `.mts` to ensure ESM.
659
- */
660
- const detectMainFilename = (repoDir) => Effect.gen(function* () {
661
- const fs = yield* FileSystem.FileSystem;
662
- const pkgPath = join(repoDir, "package.json");
663
- const exists = yield* fs
664
- .exists(pkgPath)
665
- .pipe(Effect.orElseSucceed(() => false));
666
- if (!exists)
667
- return "main.mts";
668
- const content = yield* fs
669
- .readFileString(pkgPath)
670
- .pipe(Effect.orElseSucceed(() => ""));
671
- try {
672
- const pkg = JSON.parse(content);
673
- return pkg["type"] === "module" ? "main.ts" : "main.mts";
674
- }
675
- catch {
676
- return "main.mts";
677
- }
678
- });
679
- export const scaffold = (repoDir, options) => Effect.gen(function* () {
680
- const { agent, model, templateName = "blank", createLabel = true, issueTracker = ISSUE_TRACKER_REGISTRY[0], // default: github-issues
681
- sandboxProvider = SANDBOX_PROVIDER_REGISTRY[0], // default: docker
682
- } = options;
683
- const fs = yield* FileSystem.FileSystem;
684
- const configDir = join(repoDir, ".sandcastle");
685
- const exists = yield* fs
686
- .exists(configDir)
687
- .pipe(Effect.mapError((e) => new Error(e.message)));
688
- if (exists) {
689
- yield* Effect.fail(new Error(".sandcastle/ directory already exists. Remove it first if you want to re-initialize."));
690
- }
691
- const mainFilename = yield* detectMainFilename(repoDir);
692
- yield* fs
693
- .makeDirectory(configDir, { recursive: false })
694
- .pipe(Effect.mapError((e) => new Error(e.message)));
695
- const templateDir = yield* getTemplateDir(templateName);
696
- // Build .env.example from agent + issue tracker env blocks
697
- const envExampleParts = [agent.envExample];
698
- if (issueTracker.envExample) {
699
- envExampleParts.push(issueTracker.envExample);
700
- }
701
- const envExampleContent = envExampleParts.join("\n") + "\n";
702
- yield* Effect.all([
703
- fs
704
- .writeFileString(join(configDir, sandboxProvider.containerfileName), agent.dockerfileTemplate)
705
- .pipe(Effect.mapError((e) => new Error(e.message))),
706
- fs
707
- .writeFileString(join(configDir, ".gitignore"), GITIGNORE)
708
- .pipe(Effect.mapError((e) => new Error(e.message))),
709
- fs
710
- .writeFileString(join(configDir, ".env.example"), envExampleContent)
711
- .pipe(Effect.mapError((e) => new Error(e.message))),
712
- copyTemplateFiles(templateDir, configDir, mainFilename),
713
- ], { concurrency: "unbounded" });
714
- // Rewrite main file with the selected agent factory, model, and sandbox provider
715
- yield* rewriteMainTs(configDir, agent, model, sandboxProvider, mainFilename);
716
- // Replace issue tracker template arguments in all text files (must run before label stripping)
717
- yield* substituteTemplateArgs(configDir, issueTracker);
718
- // Strip --label Sandcastle from prompt files when the user declined label creation
719
- if (!createLabel) {
720
- yield* rewritePromptFiles(configDir);
721
- }
722
- // For the custom issue tracker, drop the setup prompt the user feeds to
723
- // their coding agent. Written after substituteTemplateArgs so it isn't
724
- // clobbered and references the resolved sentinel markers the agent finds
725
- // (not the {{KEY}} names, which are gone by now).
726
- if (issueTracker.name === "custom") {
727
- yield* fs
728
- .writeFileString(join(configDir, SETUP_ISSUE_TRACKER_DOC), buildSetupIssueTrackerDoc(sandboxProvider.cliNamespace))
729
- .pipe(Effect.mapError((e) => new Error(e.message)));
730
- }
731
- return { mainFilename };
732
- });
733
- //# sourceMappingURL=InitService.js.map