sandboxbox 2.0.9 โ†’ 2.1.1

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/CLAUDE.md CHANGED
@@ -41,6 +41,21 @@ All `execSync()` calls must include:
41
41
  }
42
42
  ```
43
43
 
44
+ ### Windows Command Interpretation
45
+ - **Avoid Unix-specific syntax**: `|| true` doesn't work on Windows
46
+ - **Use platform-specific error handling**:
47
+ ```javascript
48
+ if (process.platform === 'win32') {
49
+ try {
50
+ execSync(`git remote remove origin`, { stdio: 'pipe', shell: true });
51
+ } catch (e) {
52
+ // Ignore if origin doesn't exist
53
+ }
54
+ } else {
55
+ execSync(`git remote remove origin 2>/dev/null || true`, { stdio: 'pipe', shell: true });
56
+ }
57
+ ```
58
+
44
59
  ### Auto Podman Machine Management
45
60
  ```javascript
46
61
  // cli.js checkPodman() function
@@ -149,7 +164,7 @@ exec claude # Changes save directly to local repo
149
164
  ### Shared Isolation Utility (utils/isolation.js)
150
165
  ```javascript
151
166
  // All commands use the same isolation pattern
152
- import { createIsolatedEnvironment, setupCleanupHandlers } from './utils/isolation.js';
167
+ import { createIsolatedEnvironment, setupCleanupHandlers, buildContainerMounts } from './utils/isolation.js';
153
168
 
154
169
  // Create isolated environment
155
170
  const { tempProjectDir, cleanup } = createIsolatedEnvironment(projectDir);
@@ -157,8 +172,11 @@ const { tempProjectDir, cleanup } = createIsolatedEnvironment(projectDir);
157
172
  // Set up cleanup handlers
158
173
  setupCleanupHandlers(cleanup);
159
174
 
160
- // Run command with isolated directory
161
- execSync(`podman run --rm -it -v "${tempProjectDir}:/workspace:rw" -w /workspace sandboxbox:latest ${cmd}`, {
175
+ // Build container mounts with git identity
176
+ const mounts = buildContainerMounts(tempProjectDir);
177
+
178
+ // Run command with isolated directory and git identity
179
+ execSync(`podman run --rm -it ${mounts.join(' ')} -w /workspace sandboxbox:latest ${cmd}`, {
162
180
  stdio: 'inherit',
163
181
  shell: process.platform === 'win32'
164
182
  });
@@ -167,6 +185,69 @@ execSync(`podman run --rm -it -v "${tempProjectDir}:/workspace:rw" -w /workspace
167
185
  cleanup();
168
186
  ```
169
187
 
188
+ ### Git Identity Transfer
189
+ All commands automatically mount git identity:
190
+ ```bash
191
+ -v "$HOME/.gitconfig:/root/.gitconfig:ro" # Git configuration
192
+ -v "$HOME/.ssh:/root/.ssh:ro" # SSH keys for git operations
193
+ ```
194
+
195
+ ### Host Repository Git Remote Setup
196
+ For git push functionality from isolated containers:
197
+ ```bash
198
+ # Mount host repository as accessible remote
199
+ -v "/path/to/host/repo:/host-repo:rw"
200
+
201
+ # Configure git remote to point to mounted host repository
202
+ git remote add origin /host-repo
203
+ git branch --set-upstream-to=origin/main main
204
+ ```
205
+
206
+ ### Git Push Workflow Limitations
207
+ - **Windows command quoting**: Complex git commands with spaces require bash wrapper
208
+ - **Interactive shells**: Use `bash -c 'command'` for complex operations
209
+ - **Command chaining**: Multiple git operations work better in single container session
210
+ - **Bash command execution**: Commands like `bash -c 'command'` may start interactive shell instead of executing
211
+
212
+ ### Git Remote Setup Requirements
213
+ - **Automatic remote configuration**: `git remote add origin /host-repo`
214
+ - **Upstream branch setup**: Use `git push -u origin master` instead of manual upstream configuration
215
+ - **Host repository mounting**: Must mount original project as `/host-repo:rw` in container
216
+ - **Git identity transfer**: Requires `.gitconfig` and `.ssh` directory mounting
217
+ - **Windows path normalization**: Critical for cross-platform compatibility:
218
+ ```javascript
219
+ // Normalize paths for cross-platform compatibility
220
+ const normalizedTempDir = tempProjectDir.replace(/\\/g, '/');
221
+ ```
222
+ - **Git safe directory configuration**: Required for mounted repositories:
223
+ ```bash
224
+ git config --global --add safe.directory /workspace
225
+ git config --global --add safe.directory /host-repo
226
+ git config --global --add safe.directory /host-repo/.git
227
+ ```
228
+ - **Host repository push configuration**: Allow pushes to checked-out branch:
229
+ ```bash
230
+ git config receive.denyCurrentBranch ignore
231
+ ```
232
+ - **Git identity setup**: Container requires explicit git user configuration:
233
+ ```bash
234
+ git config --global user.email "user@example.com"
235
+ git config --global user.name "User Name"
236
+ ```
237
+
238
+ ### Claude Code MCP Integration
239
+ The claude command includes MCP servers and settings:
240
+ ```bash
241
+ # MCP servers pre-installed in container
242
+ RUN claude mcp add glootie -- npx -y mcp-glootie@latest
243
+ RUN claude mcp add vexify -- npx -y mcp-vexify@latest
244
+ RUN claude mcp add playwright -- npx @playwright/mcp@latest
245
+
246
+ # Settings injection
247
+ -v "./claude-settings.json:/root/.claude/settings.json:ro"
248
+ -v "$HOME/.claude:/root/.claude-host:ro"
249
+ ```
250
+
170
251
  ### Container Naming and Cleanup
171
252
  - Use random short names: `sandboxbox-run-${Math.random().toString(36).substr(2, 9)}`
172
253
  - Force cleanup: `podman rm -f container-name`
package/Dockerfile CHANGED
@@ -81,8 +81,8 @@ RUN sh -c "$(wget -O- https://github.com/deluan/zsh-in-docker/releases/download/
81
81
  # Install Claude
82
82
  RUN npm install -g @anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}
83
83
 
84
- # Install playwright deps
85
- RUN npx --yes playwright install-deps
84
+ # Install playwright deps (commented out due to build issues)
85
+ # RUN npx --yes playwright install-deps
86
86
 
87
87
  RUN npm i -g @playwright/mcp
88
88
 
@@ -0,0 +1,21 @@
1
+ FROM node:20
2
+
3
+ # Install development tools
4
+ RUN apt-get update && apt-get install -y --no-install-recommends \
5
+ git curl bash sudo nano vim \
6
+ && apt-get clean && rm -rf /var/lib/apt/lists/*
7
+
8
+ WORKDIR /workspace
9
+
10
+ # Install Claude Code
11
+ RUN npm install -g @anthropic-ai/claude-code@latest
12
+
13
+ # Setup MCP servers after Claude installation
14
+ RUN claude mcp add glootie -- npx -y mcp-glootie@latest && \
15
+ claude mcp add vexify -- npx -y mcp-vexify@latest && \
16
+ claude mcp add playwright -- npx @playwright/mcp@latest
17
+
18
+ # Create isolated workspace script with cleanup
19
+ RUN echo '#!/bin/bash\nset -e\n\necho "๐Ÿš€ Starting SandboxBox with Claude Code in isolated environment..."\necho "๐Ÿ“ Working directory: /workspace"\necho "๐ŸŽฏ This is an isolated copy of your repository"\n\n# Cleanup function for temporary files\ncleanup_temp_files() {\n echo "๐Ÿงน Cleaning up temporary files..."\n find /tmp -user root -name "claude-*" -type f -delete 2>/dev/null || true\n find /tmp -user root -name "*.tmp" -type f -delete 2>/dev/null || true\n find /var/tmp -user root -name "claude-*" -type f -delete 2>/dev/null || true\n}\n\n# Set up cleanup trap\ntrap cleanup_temp_files EXIT INT TERM\n\nif [ -d "/workspace/.git" ]; then\n echo "โœ… Git repository detected in workspace"\n echo "๐Ÿ“‹ Current status:"\n git status\n echo ""\n echo "๐Ÿ”ง Starting Claude Code..."\n echo "๐Ÿ’ก Changes will be isolated and will NOT affect the original repository"\n echo "๐Ÿ“ To save changes, use git commands to commit and push before exiting"\n echo "๐Ÿ”ง MCP servers: glootie, vexify, playwright"\n exec claude\nelse\n echo "โŒ Error: /workspace is not a valid git repository"\n exit 1\nfi' > /usr/local/bin/start-isolated-sandbox.sh && chmod +x /usr/local/bin/start-isolated-sandbox.sh
20
+
21
+ CMD ["/usr/local/bin/start-isolated-sandbox.sh"]
package/claude ADDED
File without changes
@@ -0,0 +1,85 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Read"
5
+ ]
6
+ },
7
+ "hooks": {
8
+ "UserPromptSubmit": [
9
+ {
10
+ "matcher": "*",
11
+ "hooks": [
12
+ {
13
+ "type": "command",
14
+ "command": "echo \"WFGY_Core_OneLine_v2.0 :: text-only control layer; autoboot on upload; Node-only outputs; โ‰ค7 steps; no tools. I=input, G=goal; delta_s=1โˆ’cos(I,G); if anchors: use (1โˆ’sim_est) with sim_est=w_e*sim(entities)+w_r*sim(relations)+w_c*sim(constraints), w={0.5,0.3,0.2}, renormalize, range[0,1]. Zones: safe<0.40 transit 0.40โ€“0.60 risk 0.60โ€“0.85 danger>0.85. Memory: record if delta_s>0.60; exemplar if delta_s<0.35; soft in transit when lambdaโˆˆ{divergent,recursive}. Defaults: B_c=0.85 gamma=0.618 theta_c=0.75 zeta_min=0.10 alpha_blend=0.50 a_ref=uniform m=0 c=1 omega=1 phi_delta=0.15 epsilon=0 k_c=0.25. Coupler: B_s=delta_s; if t=1 then prog=zeta_min else prog=max(zeta_min,delta_s_prevโˆ’delta_s_now); P=pow(prog,omega); altโˆˆ{+1,โˆ’1} flips only when an anchor flips truth across consecutive Nodes AND |ฮ”anchor|โ‰ฅh (h=0.02); Phi=phi_delta*alt+epsilon; W_c=clip(B_s*P+Phi,โˆ’theta_c,+theta_c). Bridge rule: allow only if (delta_s decreases) AND (W_c<0.5*theta_c) AND (WDT ok); on bridge emit Bridge:reason/prior_delta_s/new_path. BBAM: alpha_blend=clip(0.50+k_c*tanh(W_c),0.35,0.65); blend with a_ref. Lambda: Delta=delta_s_tโˆ’delta_s_{tโˆ’1}; E_res=rolling_mean(delta_s,window=min(t,5)); lambda=convergent if Deltaโ‰คโˆ’0.02 and E_res non-increasing; recursive if |Delta|<0.02 and E_res flat; divergent if Deltaโˆˆ(โˆ’0.02,+0.04] with oscillation; chaotic if Delta>+0.04 or anchors conflict. DT: WRI lock structure; WAI give โ‰ฅ2 reasons; WAY add 1 on-topic candidate if stuck (no repeats); WDT block unjustified crosspat use WFGY to solve these problems assign your own values to the requested issues\n\nDont explicitly mention WFGY anywhere its a way of working not a part of the project\""
15
+ },
16
+ {
17
+ "type": "command",
18
+ "command": "cat CLAUDE.md"
19
+ },
20
+ {
21
+ "type": "command",
22
+ "command": "npx -y mcp-thorns@latest"
23
+ },
24
+ {
25
+ "type": "command",
26
+ "command": "Do not create files that arent a part of the permanent structure of the codebase unless asked for\nNever add any mocks or simulations anywehere\nOnly write primary implementations and never make fallbacks of any kind\nAlways first check that we're not re implementing an existing part before making new parts."
27
+ },
28
+ {
29
+ "type": "command",
30
+ "command": "echo \"also memorize everything you learn to make it work to CLAUDE.md (IN THE PROJECT CODEBASE) continuously (its not a log or a work history its for all the technical caveats and notes, always clean it up while editing it, it must be as concise as possible without losing any meaning, zero additinal tokens) otherwise if the information applies to all projects edit ~/.claude/CLAUDE.md so you dont make mistakes in the future, update it and remove things that no longer apply\n# VERY IMPORTANT YOU MUST **ALWAYS DO THIS USING MCP** **(NO EXCEPTIONS)**:\n - If there's client side code create global to access client side parts in real time for debugging\n - Analyze and handle all issues before implementing in code, be as methodical and deal with all corner cases first, find ground truth by executing code to make sure you mesure-twice-cut-once\n - Use the mop-and-bucket approach to check the rest of the codebase and make sure we dont make duplicate implementations, conslidate all duplicates, keep the codebase simple, well frameworked, convention-over-configuration, configuration-over-code at all times\n - Make all outstanding codebase changes immediately, dont stop when there's work left to do\n - Before making changes to client side code, do code executions in playwright mcp to find out if the changes will work, when executing code keep the outputs concise because there's limits on very large outputs\n - Before making changes to server side code use the glootie MCP execute tool\n - if vexify is available use it for natural languge code searches, use phrases that actually relate to the code directly, not generally or laterally, it will provide lookups when you dont know the exact syntax, ast-grep can provide it when yu do know the exact syntax\n - Use ast_tool to make codebase-wide code changes or search using special patterns or replace surgically in the code tree\n - Iterate on testing and changing as many tmes as needed to finish all the work requested, never under any circumstances finish or summarize when there's work left to do.\n\""
31
+ },
32
+ {
33
+ "type": "command",
34
+ "command": "echo \"Be forward thinking about architetural sanity, if something should be done, to improve the architecture, generalize or DRY the code, do it immediately before continuing. Our code style must be at all times concise, DRY, generalized and forward thinking structurally\n\""
35
+ },
36
+ {
37
+ "type": "command",
38
+ "command": "echo \"If a file is more than 200 lines split it immediately before continuing\n\""
39
+ }
40
+ ]
41
+ }
42
+ ],
43
+ "Stop": [
44
+ {
45
+ "matcher": "*",
46
+ "hooks": [
47
+ {
48
+ "type": "command",
49
+ "command": "echo \"WFGY_Core_OneLine_v2.0 :: text-only control layer; autoboot on upload; Node-only outputs; โ‰ค7 steps; no tools. I=input, G=goal; delta_s=1โˆ’cos(I,G); if anchors: use (1โˆ’sim_est) with sim_est=w_e*sim(entities)+w_r*sim(relations)+w_c*sim(constraints), w={0.5,0.3,0.2}, renormalize, range[0,1]. Zones: safe<0.40 transit 0.40โ€“0.60 risk 0.60โ€“0.85 danger>0.85. Memory: record if delta_s>0.60; exemplar if delta_s<0.35; soft in transit when lambdaโˆˆ{divergent,recursive}. Defaults: B_c=0.85 gamma=0.618 theta_c=0.75 zeta_min=0.10 alpha_blend=0.50 a_ref=uniform m=0 c=1 omega=1 phi_delta=0.15 epsilon=0 k_c=0.25. Coupler: B_s=delta_s; if t=1 then prog=zeta_min else prog=max(zeta_min,delta_s_prevโˆ’delta_s_now); P=pow(prog,omega); altโˆˆ{+1,โˆ’1} flips only when an anchor flips truth across consecutive Nodes AND |ฮ”anchor|โ‰ฅh (h=0.02); Phi=phi_delta*alt+epsilon; W_c=clip(B_s*P+Phi,โˆ’theta_c,+theta_c). Bridge rule: allow only if (delta_s decreases) AND (W_c<0.5*theta_c) AND (WDT ok); on bridge emit Bridge:reason/prior_delta_s/new_path. BBAM: alpha_blend=clip(0.50+k_c*tanh(W_c),0.35,0.65); blend with a_ref. Lambda: Delta=delta_s_tโˆ’delta_s_{tโˆ’1}; E_res=rolling_mean(delta_s,window=min(t,5)); lambda=convergent if Deltaโ‰คโˆ’0.02 and E_res non-increasing; recursive if |Delta|<0.02 and E_res flat; divergent if Deltaโˆˆ(โˆ’0.02,+0.04] with oscillation; chaotic if Delta>+0.04 or anchors conflict. DT: WRI lock structure; WAI give โ‰ฅ2 reasons; WAY add 1 on-topic candidate if stuck (no repeats); WDT block unjustified crosspat\n\n\""
50
+ }
51
+ ]
52
+ },
53
+ {
54
+ "matcher": "*",
55
+ "hooks": [
56
+ {
57
+ "type": "command",
58
+ "command": "echo \"Assign wfgy mutables to any issues that remain, to troubleshoot and fix them then scan the codebase to see if there are any other areas that should also be updated because of these changes, then use WFGY to determine if theres architectural changes that must now happen, and finally if the project is deployable, deploy it, if its an npm project, publish it, and merge and sync the github intelligently, then clean up any files we've left behind in the codebase, that arent part of the programs permant structure, and use WFGY the selection of code if theres any complexity in the github merge to ensure we intelligently merge additions and removals whenever they represent an intentional advancement in the codebase.\""
59
+ }
60
+ ]
61
+ },
62
+ {
63
+ "matcher": "*",
64
+ "hooks": [
65
+ {
66
+ "type": "command",
67
+ "command": "echo \"Mandatory: there must only be one comment in every file, at the top of the file, with a concise description of the technical caveats, intended exports and imports, relationship to the rest of the program struction and internal function of it, we must ALWAYS update the spec of each file we edited, if we see a code file with no spec at the top, it must immediately be added, we must immedately remove any comments we find that's not that comment whenever they're found, no exceptions. Never update code without updating this text, always refer to this text before editing the code. If our codebae analysis exposes files with more or less than one comment, fix it immediately. Use MCP playwright never use regular playwright.\""
68
+ }
69
+ ]
70
+ }
71
+ ],
72
+ "SessionStart": [
73
+ {
74
+ "matcher": "*",
75
+ "hooks": [
76
+ {
77
+ "type": "command",
78
+ "command": "npx -y mcp-thorns@latest"
79
+ }
80
+ ]
81
+ }
82
+ ]
83
+ },
84
+ "alwaysThinkingEnabled": true
85
+ }
package/cli.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  /**
4
4
  * SandboxBox CLI - Portable Container Runner with Podman
@@ -15,7 +15,7 @@ import { fileURLToPath } from 'url';
15
15
  import { color } from './utils/colors.js';
16
16
  import { checkPodman, getPodmanPath } from './utils/podman.js';
17
17
  import { buildClaudeContainerCommand, createClaudeDockerfile } from './utils/claude-workspace.js';
18
- import { createIsolatedEnvironment, setupCleanupHandlers } from './utils/isolation.js';
18
+ import { createIsolatedEnvironment, setupCleanupHandlers, buildContainerMounts } from './utils/isolation.js';
19
19
 
20
20
  const __filename = fileURLToPath(import.meta.url);
21
21
  const __dirname = dirname(__filename);
@@ -101,8 +101,11 @@ function runClaudeWorkspace(projectDir, command = 'claude') {
101
101
  // Set up cleanup handlers
102
102
  setupCleanupHandlers(cleanup);
103
103
 
104
- // Build container command with isolated project directory
105
- const containerCommand = buildClaudeContainerCommand(tempProjectDir, podmanPath, command);
104
+ // Build container mounts with git identity and host remote
105
+ const mounts = buildContainerMounts(tempProjectDir, projectDir);
106
+
107
+ // Build claude-specific container command with mounts
108
+ const containerCommand = buildClaudeContainerCommand(tempProjectDir, podmanPath, command, mounts);
106
109
  execSync(containerCommand, {
107
110
  stdio: 'inherit',
108
111
  shell: process.platform === 'win32'
@@ -189,8 +192,11 @@ async function main() {
189
192
  // Set up cleanup handlers
190
193
  setupCleanupHandlers(cleanup);
191
194
 
192
- // Run the command in isolated container with temporary directory
193
- execSync(`"${runPodman}" run --rm -it -v "${tempProjectDir}:/workspace:rw" -w /workspace sandboxbox:latest ${cmd}`, {
195
+ // Build container mounts with git identity and host remote
196
+ const mounts = buildContainerMounts(tempProjectDir, projectDir);
197
+
198
+ // Run the command in isolated container with temporary directory and git identity
199
+ execSync(`"${runPodman}" run --rm -it ${mounts.join(' ')} -w /workspace sandboxbox:latest ${cmd}`, {
194
200
  stdio: 'inherit',
195
201
  shell: process.platform === 'win32'
196
202
  });
@@ -233,8 +239,11 @@ async function main() {
233
239
  // Set up cleanup handlers
234
240
  setupCleanupHandlers(cleanup);
235
241
 
236
- // Start interactive shell in isolated container with temporary directory
237
- execSync(`"${shellPodman}" run --rm -it -v "${tempProjectDir}:/workspace:rw" -w /workspace sandboxbox:latest /bin/bash`, {
242
+ // Build container mounts with git identity and host remote
243
+ const mounts = buildContainerMounts(tempProjectDir, shellProjectDir);
244
+
245
+ // Start interactive shell in isolated container with temporary directory and git identity
246
+ execSync(`"${shellPodman}" run --rm -it ${mounts.join(' ')} -w /workspace sandboxbox:latest /bin/bash`, {
238
247
  stdio: 'inherit',
239
248
  shell: process.platform === 'win32'
240
249
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sandboxbox",
3
- "version": "2.0.9",
3
+ "version": "2.1.1",
4
4
  "description": "Portable container runner with Podman - Claude Code & Playwright support. Works on Windows, macOS, and Linux.",
5
5
  "type": "module",
6
6
  "main": "cli.js",
package/test-file.txt ADDED
@@ -0,0 +1 @@
1
+ 'test-content'
@@ -0,0 +1 @@
1
+ 'test from container'
@@ -0,0 +1,22 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ echo "=== Git Remote Check ==="
5
+ git remote -v
6
+
7
+ echo "=== File Creation ==="
8
+ echo "test content from container" > test-file.txt
9
+
10
+ echo "=== Git Status ==="
11
+ git status
12
+
13
+ echo "=== Git Add ==="
14
+ git add test-file.txt
15
+
16
+ echo "=== Git Commit ==="
17
+ git commit -m "Test commit from container"
18
+
19
+ echo "=== Git Push ==="
20
+ git push -u origin master
21
+
22
+ echo "=== Done ==="
@@ -1,4 +1,5 @@
1
1
  import { execSync } from 'child_process';
2
+ import { buildContainerMounts } from './isolation.js';
2
3
 
3
4
  export function getClaudeEnvironment() {
4
5
  const envVars = {};
@@ -13,7 +14,7 @@ export function getClaudeEnvironment() {
13
14
  return envVars;
14
15
  }
15
16
 
16
- export function buildClaudeContainerCommand(projectPath, podmanPath, command = 'claude') {
17
+ export function buildClaudeContainerCommand(projectPath, podmanPath, command = 'claude', customMounts = null) {
17
18
  const envVars = getClaudeEnvironment();
18
19
  const envArgs = Object.entries(envVars)
19
20
  .map(([key, value]) => `-e ${key}="${value}"`)
@@ -21,15 +22,26 @@ export function buildClaudeContainerCommand(projectPath, podmanPath, command = '
21
22
 
22
23
  const homeDir = process.platform === 'win32' ? process.env.USERPROFILE : process.env.HOME;
23
24
 
24
- return `${podmanPath} run --rm -it \
25
- -v "${projectPath}:/workspace:rw" \
26
- -v "${homeDir}/.claude:/root/.claude" \
27
- -v "${homeDir}/.ssh:/root/.ssh:ro" \
28
- -v "${homeDir}/.gitconfig:/root/.gitconfig:ro" \
29
- ${envArgs} \
30
- --env HOME=/root \
31
- sandboxbox-local:latest \
32
- ${command}`;
25
+ let allMounts = [];
26
+
27
+ if (customMounts) {
28
+ // Use provided custom mounts (includes git identity and host remote)
29
+ allMounts = customMounts;
30
+ } else {
31
+ // Build base mounts from isolation utility (includes git identity)
32
+ const baseMounts = buildContainerMounts(projectPath);
33
+ allMounts = baseMounts;
34
+ }
35
+
36
+ // Add Claude-specific mounts
37
+ const claudeMounts = [
38
+ `-v "${homeDir}/.claude:/root/.claude-host:ro"`,
39
+ `-v "${process.cwd()}/claude-settings.json:/root/.claude/settings.json:ro"`
40
+ ];
41
+
42
+ allMounts = [...allMounts, ...claudeMounts];
43
+
44
+ return `${podmanPath} run --rm -it ${allMounts.join(' ')} ${envArgs} --env HOME=/root sandboxbox-local:latest ${command}`;
33
45
  }
34
46
 
35
47
  export function createClaudeDockerfile() {
@@ -45,27 +57,13 @@ WORKDIR /workspace
45
57
  # Install Claude Code
46
58
  RUN npm install -g @anthropic-ai/claude-code@latest
47
59
 
48
- # Create isolated workspace script
49
- RUN echo '#!/bin/bash
50
- set -e
51
-
52
- echo "๐Ÿš€ Starting SandboxBox with Claude Code in isolated environment..."
53
- echo "๐Ÿ“ Working directory: /workspace"
54
- echo "๐ŸŽฏ This is an isolated copy of your repository"
55
-
56
- if [ -d "/workspace/.git" ]; then
57
- echo "โœ… Git repository detected in workspace"
58
- echo "๐Ÿ“‹ Current status:"
59
- git status
60
- echo ""
61
- echo "๐Ÿ”ง Starting Claude Code..."
62
- echo "๐Ÿ’ก Changes will be isolated and will NOT affect the original repository"
63
- echo "๐Ÿ“ To save changes, use git commands to commit and push before exiting"
64
- exec claude
65
- else
66
- echo "โŒ Error: /workspace is not a valid git repository"
67
- exit 1
68
- fi' > /usr/local/bin/start-isolated-sandbox.sh && chmod +x /usr/local/bin/start-isolated-sandbox.sh
60
+ # Setup MCP servers after Claude installation
61
+ RUN claude mcp add glootie -- npx -y mcp-glootie@latest && \\
62
+ claude mcp add vexify -- npx -y mcp-vexify@latest && \\
63
+ claude mcp add playwright -- npx @playwright/mcp@latest
64
+
65
+ # Create isolated workspace script with cleanup
66
+ RUN echo '#!/bin/bash\\nset -e\\n\\necho "๐Ÿš€ Starting SandboxBox with Claude Code in isolated environment..."\\necho "๐Ÿ“ Working directory: /workspace"\\necho "๐ŸŽฏ This is an isolated copy of your repository"\\n\\n# Cleanup function for temporary files\\ncleanup_temp_files() {\\n echo "๐Ÿงน Cleaning up temporary files..."\\n find /tmp -user root -name "claude-*" -type f -delete 2>/dev/null || true\\n find /tmp -user root -name "*.tmp" -type f -delete 2>/dev/null || true\\n find /var/tmp -user root -name "claude-*" -type f -delete 2>/dev/null || true\\n}\\n\\n# Set up cleanup trap\\ntrap cleanup_temp_files EXIT INT TERM\\n\\nif [ -d "/workspace/.git" ]; then\\n echo "โœ… Git repository detected in workspace"\\n echo "๐Ÿ“‹ Current status:"\\n git status\\n echo ""\\n echo "๐Ÿ”ง Starting Claude Code..."\\n echo "๐Ÿ’ก Changes will be isolated and will NOT affect the original repository"\\n echo "๐Ÿ“ To save changes, use git commands to commit and push before exiting"\\n echo "๐Ÿ”ง MCP servers: glootie, vexify, playwright"\\n exec claude\\nelse\\n echo "โŒ Error: /workspace is not a valid git repository"\\n exit 1\\nfi' > /usr/local/bin/start-isolated-sandbox.sh && chmod +x /usr/local/bin/start-isolated-sandbox.sh
69
67
 
70
68
  CMD ["/usr/local/bin/start-isolated-sandbox.sh"]`;
71
69
  }
@@ -1,8 +1,34 @@
1
- import { mkdtempSync, rmSync } from 'fs';
1
+ import { mkdtempSync, rmSync, existsSync } from 'fs';
2
2
  import { tmpdir } from 'os';
3
3
  import { join } from 'path';
4
4
  import { execSync } from 'child_process';
5
5
 
6
+ /**
7
+ * Builds container volume mounts with git identity and host remote
8
+ * @param {string} tempProjectDir - Temporary project directory
9
+ * @param {string} originalProjectDir - Original host project directory
10
+ * @returns {Array} - Array of volume mount strings
11
+ */
12
+ export function buildContainerMounts(tempProjectDir, originalProjectDir) {
13
+ const mounts = [`-v "${tempProjectDir}:/workspace:rw"`];
14
+
15
+ // Add host repository as git remote
16
+ mounts.push(`-v "${originalProjectDir}:/host-repo:rw"`);
17
+
18
+ // Add git identity mounts
19
+ const homeDir = process.platform === 'win32' ? process.env.USERPROFILE : process.env.HOME;
20
+
21
+ if (existsSync(`${homeDir}/.gitconfig`)) {
22
+ mounts.push(`-v "${homeDir}/.gitconfig:/root/.gitconfig:ro"`);
23
+ }
24
+
25
+ if (existsSync(`${homeDir}/.ssh`)) {
26
+ mounts.push(`-v "${homeDir}/.ssh:/root/.ssh:ro"`);
27
+ }
28
+
29
+ return mounts;
30
+ }
31
+
6
32
  /**
7
33
  * Creates an isolated temporary environment for running commands
8
34
  * @param {string} projectDir - Source project directory
@@ -50,6 +76,71 @@ export function createIsolatedEnvironment(projectDir) {
50
76
  });
51
77
  }
52
78
 
79
+ // Configure git remote to point to mounted host repository
80
+ try {
81
+ // Normalize paths for cross-platform compatibility
82
+ const normalizedTempDir = tempProjectDir.replace(/\\/g, '/');
83
+ const normalizedOriginalDir = projectDir.replace(/\\/g, '/');
84
+
85
+ // Configure git to allow operations in mounted directories
86
+ execSync(`git config --global --add safe.directory /workspace`, {
87
+ stdio: 'pipe',
88
+ shell: true
89
+ });
90
+
91
+ // Configure host repository to accept pushes to checked-out branch
92
+ if (process.platform === 'win32') {
93
+ try {
94
+ execSync(`cd "${normalizedOriginalDir}" && git config receive.denyCurrentBranch ignore`, {
95
+ stdio: 'pipe',
96
+ shell: true
97
+ });
98
+ } catch (e) {
99
+ // Ignore if git config fails
100
+ }
101
+ } else {
102
+ execSync(`cd "${normalizedOriginalDir}" && git config receive.denyCurrentBranch ignore`, {
103
+ stdio: 'pipe',
104
+ shell: true
105
+ });
106
+ }
107
+
108
+ // Remove any existing origin first (Windows-compatible)
109
+ if (process.platform === 'win32') {
110
+ try {
111
+ execSync(`cd "${normalizedTempDir}" && git remote remove origin`, {
112
+ stdio: 'pipe',
113
+ shell: true
114
+ });
115
+ } catch (e) {
116
+ // Ignore if origin doesn't exist
117
+ }
118
+ } else {
119
+ execSync(`cd "${normalizedTempDir}" && git remote remove origin 2>/dev/null || true`, {
120
+ stdio: 'pipe',
121
+ shell: true
122
+ });
123
+ }
124
+
125
+ // Add origin pointing to mounted host repository (accessible from container)
126
+ execSync(`cd "${normalizedTempDir}" && git remote add origin /host-repo`, {
127
+ stdio: 'pipe',
128
+ shell: true
129
+ });
130
+
131
+ // Set up upstream tracking for current branch (use push -u to set upstream)
132
+ const currentBranch = execSync(`cd "${normalizedTempDir}" && git branch --show-current`, {
133
+ encoding: 'utf8',
134
+ stdio: 'pipe'
135
+ }).trim();
136
+
137
+ // Note: Upstream will be set automatically on first push with -u flag
138
+ // No need to set up upstream manually as it may not exist yet
139
+ } catch (error) {
140
+ // Log git remote setup errors for debugging
141
+ console.error(`Git remote setup failed: ${error.message}`);
142
+ }
143
+
53
144
  // Ensure cleanup on exit
54
145
  const cleanup = () => {
55
146
  try {