ralph-cli-sandboxed 0.2.0 → 0.2.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/dist/commands/docker.js +65 -9
- package/dist/commands/init.js +4 -0
- package/dist/commands/once.js +4 -3
- package/dist/commands/run.js +4 -1
- package/dist/config/cli-providers.json +11 -4
- package/dist/templates/prompts.d.ts +1 -0
- package/dist/utils/config.d.ts +1 -0
- package/dist/utils/config.js +22 -1
- package/dist/utils/prompt.js +2 -0
- package/package.json +5 -4
package/dist/commands/docker.js
CHANGED
|
@@ -29,7 +29,7 @@ function getCliProviderSnippet(cliProvider) {
|
|
|
29
29
|
const provider = cliProvidersJson.providers[providerKey];
|
|
30
30
|
if (!provider || !provider.docker) {
|
|
31
31
|
// Default to Claude Code CLI if provider not found
|
|
32
|
-
return "# Install Claude Code CLI\nRUN curl -fsSL https://claude.ai/install.sh | bash";
|
|
32
|
+
return "# Install Claude Code CLI (as node user so it installs to /home/node/.local/bin)\nRUN su - node -c 'curl -fsSL https://claude.ai/install.sh | bash' \\\n && echo 'export PATH=\"$HOME/.local/bin:$PATH\"' >> /home/node/.zshrc";
|
|
33
33
|
}
|
|
34
34
|
return provider.docker.install;
|
|
35
35
|
}
|
|
@@ -291,9 +291,13 @@ async function buildImage(ralphDir) {
|
|
|
291
291
|
process.exit(1);
|
|
292
292
|
}
|
|
293
293
|
console.log("Building Docker image...\n");
|
|
294
|
+
// Get image name for compose project name
|
|
295
|
+
const config = loadConfig();
|
|
296
|
+
const imageName = config.imageName || `ralph-${basename(process.cwd()).toLowerCase().replace(/[^a-z0-9-]/g, "-")}`;
|
|
294
297
|
return new Promise((resolve, reject) => {
|
|
295
298
|
// Use --no-cache and --pull to ensure we always get the latest CLI versions
|
|
296
|
-
|
|
299
|
+
// Use -p to set unique project name per ralph project
|
|
300
|
+
const proc = spawn("docker", ["compose", "-p", imageName, "build", "--no-cache", "--pull"], {
|
|
297
301
|
cwd: dockerDir,
|
|
298
302
|
stdio: "inherit",
|
|
299
303
|
});
|
|
@@ -329,6 +333,27 @@ async function imageExists(imageName) {
|
|
|
329
333
|
});
|
|
330
334
|
});
|
|
331
335
|
}
|
|
336
|
+
// Get CLI provider configuration
|
|
337
|
+
function getCliProviderConfig(cliProvider) {
|
|
338
|
+
const cliProvidersJson = getCliProvidersJson();
|
|
339
|
+
const providerKey = cliProvider || "claude";
|
|
340
|
+
const provider = cliProvidersJson.providers[providerKey];
|
|
341
|
+
if (!provider) {
|
|
342
|
+
// Default to Claude Code CLI if provider not found
|
|
343
|
+
return {
|
|
344
|
+
name: "Claude Code",
|
|
345
|
+
command: "claude",
|
|
346
|
+
yoloArgs: ["--dangerously-skip-permissions"],
|
|
347
|
+
envVars: ["ANTHROPIC_API_KEY"],
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
return {
|
|
351
|
+
name: provider.name,
|
|
352
|
+
command: provider.command,
|
|
353
|
+
yoloArgs: provider.yoloArgs || [],
|
|
354
|
+
envVars: provider.envVars || [],
|
|
355
|
+
};
|
|
356
|
+
}
|
|
332
357
|
async function runContainer(ralphDir, imageName, language, javaVersion, cliProvider) {
|
|
333
358
|
const dockerDir = join(ralphDir, DOCKER_DIR);
|
|
334
359
|
const dockerfileExists = existsSync(join(dockerDir, "Dockerfile"));
|
|
@@ -346,9 +371,37 @@ async function runContainer(ralphDir, imageName, language, javaVersion, cliProvi
|
|
|
346
371
|
console.log("");
|
|
347
372
|
}
|
|
348
373
|
}
|
|
374
|
+
// Get CLI provider info for the startup note
|
|
375
|
+
const cliConfig = getCliProviderConfig(cliProvider);
|
|
376
|
+
const yoloCommand = cliConfig.yoloArgs.length > 0
|
|
377
|
+
? `${cliConfig.command} ${cliConfig.yoloArgs.join(" ")}`
|
|
378
|
+
: cliConfig.command;
|
|
349
379
|
console.log("Starting Docker container...\n");
|
|
380
|
+
// Show note about yolo mode and credentials
|
|
381
|
+
console.log("IMPORTANT: Getting Started");
|
|
382
|
+
console.log("-".repeat(40));
|
|
383
|
+
console.log("");
|
|
384
|
+
console.log("To run ralph automation, you might need to activate YOLO mode");
|
|
385
|
+
console.log("which allows the AI to execute commands without prompts.");
|
|
386
|
+
console.log("");
|
|
387
|
+
console.log(`CLI Provider: ${cliConfig.name}`);
|
|
388
|
+
console.log(`Yolo command: ${yoloCommand}`);
|
|
389
|
+
console.log("");
|
|
390
|
+
console.log("Before running 'ralph run' or 'ralph once', ensure your");
|
|
391
|
+
console.log("credentials are configured:");
|
|
392
|
+
console.log("");
|
|
393
|
+
if (cliConfig.envVars.length > 0) {
|
|
394
|
+
console.log("Required environment variables:");
|
|
395
|
+
for (const envVar of cliConfig.envVars) {
|
|
396
|
+
console.log(` - ${envVar}`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
console.log("");
|
|
400
|
+
console.log("Set them in docker-compose.yml or export before running.");
|
|
401
|
+
console.log("");
|
|
350
402
|
return new Promise((resolve, reject) => {
|
|
351
|
-
|
|
403
|
+
// Use -p to set unique project name per ralph project
|
|
404
|
+
const proc = spawn("docker", ["compose", "-p", imageName, "run", "--rm", "ralph"], {
|
|
352
405
|
cwd: dockerDir,
|
|
353
406
|
stdio: "inherit",
|
|
354
407
|
});
|
|
@@ -371,8 +424,9 @@ async function cleanImage(imageName, ralphDir) {
|
|
|
371
424
|
// First, stop any running containers via docker compose
|
|
372
425
|
if (existsSync(join(dockerDir, "docker-compose.yml"))) {
|
|
373
426
|
// Stop running containers first
|
|
427
|
+
// Use -p to target only this project's resources
|
|
374
428
|
await new Promise((resolve) => {
|
|
375
|
-
const proc = spawn("docker", ["compose", "stop", "--timeout", "5"], {
|
|
429
|
+
const proc = spawn("docker", ["compose", "-p", imageName, "stop", "--timeout", "5"], {
|
|
376
430
|
cwd: dockerDir,
|
|
377
431
|
stdio: "inherit",
|
|
378
432
|
});
|
|
@@ -384,8 +438,9 @@ async function cleanImage(imageName, ralphDir) {
|
|
|
384
438
|
});
|
|
385
439
|
});
|
|
386
440
|
// Remove containers, volumes, networks, and local images
|
|
441
|
+
// Use -p to target only this project's resources
|
|
387
442
|
await new Promise((resolve) => {
|
|
388
|
-
const proc = spawn("docker", ["compose", "down", "--rmi", "local", "-v", "--remove-orphans", "--timeout", "5"], {
|
|
443
|
+
const proc = spawn("docker", ["compose", "-p", imageName, "down", "--rmi", "local", "-v", "--remove-orphans", "--timeout", "5"], {
|
|
389
444
|
cwd: dockerDir,
|
|
390
445
|
stdio: "inherit",
|
|
391
446
|
});
|
|
@@ -398,9 +453,10 @@ async function cleanImage(imageName, ralphDir) {
|
|
|
398
453
|
});
|
|
399
454
|
});
|
|
400
455
|
}
|
|
401
|
-
// Find and forcibly remove any containers using volumes with our
|
|
456
|
+
// Find and forcibly remove any containers using volumes with our project name pattern
|
|
402
457
|
// This handles orphaned containers from previous runs or pods
|
|
403
|
-
|
|
458
|
+
// Project name is now imageName (via -p flag), so volumes are named ${imageName}_*
|
|
459
|
+
const volumePattern = imageName;
|
|
404
460
|
await new Promise((resolve) => {
|
|
405
461
|
// List all containers (including stopped) and filter by volume name pattern
|
|
406
462
|
const proc = spawn("docker", ["ps", "-aq", "--filter", `volume=${volumePattern}`], {
|
|
@@ -510,8 +566,8 @@ async function cleanImage(imageName, ralphDir) {
|
|
|
510
566
|
resolve();
|
|
511
567
|
});
|
|
512
568
|
});
|
|
513
|
-
// Clean up project-specific network (
|
|
514
|
-
const networkName =
|
|
569
|
+
// Clean up project-specific network (project name is imageName via -p flag)
|
|
570
|
+
const networkName = `${imageName}_default`;
|
|
515
571
|
await new Promise((resolve) => {
|
|
516
572
|
const proc = spawn("docker", ["network", "rm", networkName], {
|
|
517
573
|
stdio: ["ignore", "ignore", "ignore"], // Suppress output - network may not exist
|
package/dist/commands/init.js
CHANGED
|
@@ -45,10 +45,13 @@ export async function init(_args) {
|
|
|
45
45
|
const customArgs = customArgsInput.trim() ? customArgsInput.trim().split(/\s+/) : [];
|
|
46
46
|
const customYoloArgsInput = await promptInput("Enter yolo/auto-approve arguments (space-separated): ");
|
|
47
47
|
const customYoloArgs = customYoloArgsInput.trim() ? customYoloArgsInput.trim().split(/\s+/) : [];
|
|
48
|
+
const customPromptArgsInput = await promptInput("Enter prompt arguments (e.g., -p for flag-based, leave empty for positional): ");
|
|
49
|
+
const customPromptArgs = customPromptArgsInput.trim() ? customPromptArgsInput.trim().split(/\s+/) : [];
|
|
48
50
|
cliConfig = {
|
|
49
51
|
command: customCommand || "claude",
|
|
50
52
|
args: customArgs,
|
|
51
53
|
yoloArgs: customYoloArgs.length > 0 ? customYoloArgs : undefined,
|
|
54
|
+
promptArgs: customPromptArgs,
|
|
52
55
|
};
|
|
53
56
|
}
|
|
54
57
|
else {
|
|
@@ -56,6 +59,7 @@ export async function init(_args) {
|
|
|
56
59
|
command: selectedProvider.command,
|
|
57
60
|
args: selectedProvider.defaultArgs,
|
|
58
61
|
yoloArgs: selectedProvider.yoloArgs.length > 0 ? selectedProvider.yoloArgs : undefined,
|
|
62
|
+
promptArgs: selectedProvider.promptArgs ?? [],
|
|
59
63
|
};
|
|
60
64
|
}
|
|
61
65
|
console.log(`\nSelected CLI provider: ${CLI_PROVIDERS[selectedCliProviderKey].name}`);
|
package/dist/commands/once.js
CHANGED
|
@@ -18,16 +18,17 @@ export async function once(_args) {
|
|
|
18
18
|
// Build CLI arguments: config args + yolo args + prompt args
|
|
19
19
|
// Use yoloArgs from config if available, otherwise default to Claude's --dangerously-skip-permissions
|
|
20
20
|
const yoloArgs = cliConfig.yoloArgs ?? ["--dangerously-skip-permissions"];
|
|
21
|
+
const promptArgs = cliConfig.promptArgs ?? ["-p"];
|
|
22
|
+
const promptValue = `@${paths.prd} @${paths.progress} ${prompt}`;
|
|
21
23
|
const cliArgs = [
|
|
22
24
|
...(cliConfig.args ?? []),
|
|
23
25
|
...yoloArgs,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
...promptArgs,
|
|
27
|
+
promptValue,
|
|
26
28
|
];
|
|
27
29
|
return new Promise((resolve, reject) => {
|
|
28
30
|
const proc = spawn(cliConfig.command, cliArgs, {
|
|
29
31
|
stdio: "inherit",
|
|
30
|
-
shell: true,
|
|
31
32
|
});
|
|
32
33
|
proc.on("close", (code) => {
|
|
33
34
|
if (code !== 0) {
|
package/dist/commands/run.js
CHANGED
|
@@ -39,7 +39,10 @@ async function runIteration(prompt, paths, sandboxed, filteredPrdPath, cliConfig
|
|
|
39
39
|
cliArgs.push(...yoloArgs);
|
|
40
40
|
}
|
|
41
41
|
// Use the filtered PRD (only incomplete items) for the prompt
|
|
42
|
-
|
|
42
|
+
// promptArgs specifies flags to use (e.g., ["-p"] for Claude, [] for positional)
|
|
43
|
+
const promptArgs = cliConfig.promptArgs ?? ["-p"];
|
|
44
|
+
const promptValue = `@${filteredPrdPath} @${paths.progress} ${prompt}`;
|
|
45
|
+
cliArgs.push(...promptArgs, promptValue);
|
|
43
46
|
const proc = spawn(cliConfig.command, cliArgs, {
|
|
44
47
|
stdio: ["inherit", "pipe", "inherit"],
|
|
45
48
|
});
|
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
"command": "claude",
|
|
7
7
|
"defaultArgs": ["--permission-mode", "acceptEdits"],
|
|
8
8
|
"yoloArgs": ["--dangerously-skip-permissions"],
|
|
9
|
+
"promptArgs": ["-p"],
|
|
9
10
|
"docker": {
|
|
10
|
-
"install": "# Install Claude Code CLI\nRUN curl -fsSL https://claude.ai/install.sh | bash"
|
|
11
|
+
"install": "# Install Claude Code CLI (as node user so it installs to /home/node/.local/bin)\nRUN su - node -c 'curl -fsSL https://claude.ai/install.sh | bash' \\\n && echo 'export PATH=\"$HOME/.local/bin:$PATH\"' >> /home/node/.zshrc"
|
|
11
12
|
},
|
|
12
13
|
"envVars": ["ANTHROPIC_API_KEY"],
|
|
13
14
|
"credentialMount": "~/.claude:/home/node/.claude"
|
|
@@ -18,6 +19,7 @@
|
|
|
18
19
|
"command": "aider",
|
|
19
20
|
"defaultArgs": ["--yes"],
|
|
20
21
|
"yoloArgs": ["--yes-always"],
|
|
22
|
+
"promptArgs": ["--message"],
|
|
21
23
|
"docker": {
|
|
22
24
|
"install": "# Install Aider (requires Python)\nRUN apt-get update && apt-get install -y python3 python3-pip && rm -rf /var/lib/apt/lists/* \\\n && pip3 install --break-system-packages --no-cache-dir aider-chat",
|
|
23
25
|
"note": "Check 'aider --help' for available flags"
|
|
@@ -31,6 +33,7 @@
|
|
|
31
33
|
"command": "codex",
|
|
32
34
|
"defaultArgs": ["--approval-mode", "suggest"],
|
|
33
35
|
"yoloArgs": ["--approval-mode", "full-auto"],
|
|
36
|
+
"promptArgs": [],
|
|
34
37
|
"docker": {
|
|
35
38
|
"install": "# Install OpenAI Codex CLI\nRUN npm install -g @openai/codex",
|
|
36
39
|
"note": "Check 'codex --help' for available flags"
|
|
@@ -44,11 +47,12 @@
|
|
|
44
47
|
"command": "gemini",
|
|
45
48
|
"defaultArgs": [],
|
|
46
49
|
"yoloArgs": ["-y"],
|
|
50
|
+
"promptArgs": [],
|
|
47
51
|
"docker": {
|
|
48
52
|
"install": "# Install Google Gemini CLI\nRUN npm install -g @google/gemini-cli",
|
|
49
53
|
"note": "Check 'gemini --help' for available flags"
|
|
50
54
|
},
|
|
51
|
-
"envVars": ["
|
|
55
|
+
"envVars": ["GEMINI_API_KEY", "GOOGLE_API_KEY"],
|
|
52
56
|
"credentialMount": "~/.gemini:/home/node/.gemini"
|
|
53
57
|
},
|
|
54
58
|
"opencode": {
|
|
@@ -57,8 +61,9 @@
|
|
|
57
61
|
"command": "opencode",
|
|
58
62
|
"defaultArgs": [],
|
|
59
63
|
"yoloArgs": ["--yolo"],
|
|
64
|
+
"promptArgs": [],
|
|
60
65
|
"docker": {
|
|
61
|
-
"install": "# Install OpenCode\nRUN curl -fsSL https://opencode.ai/install | bash \\\n && echo 'export PATH=\"$HOME/.opencode/bin:$PATH\"' >> /home/node/.zshrc",
|
|
66
|
+
"install": "# Install OpenCode (as node user)\nRUN su - node -c 'curl -fsSL https://opencode.ai/install | bash' \\\n && echo 'export PATH=\"$HOME/.opencode/bin:$PATH\"' >> /home/node/.zshrc",
|
|
62
67
|
"note": "Check 'opencode --help' for available flags"
|
|
63
68
|
},
|
|
64
69
|
"envVars": ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GOOGLE_API_KEY"],
|
|
@@ -70,8 +75,9 @@
|
|
|
70
75
|
"command": "amp",
|
|
71
76
|
"defaultArgs": [],
|
|
72
77
|
"yoloArgs": ["--yolo"],
|
|
78
|
+
"promptArgs": [],
|
|
73
79
|
"docker": {
|
|
74
|
-
"install": "# Install AMP CLI\nRUN curl -fsSL https://ampcode.com/install.sh | bash \\\n && echo 'export PATH=\"$HOME/.amp/bin:$PATH\"' >> /home/node/.zshrc",
|
|
80
|
+
"install": "# Install AMP CLI (as node user)\nRUN su - node -c 'curl -fsSL https://ampcode.com/install.sh | bash' \\\n && echo 'export PATH=\"$HOME/.amp/bin:$PATH\"' >> /home/node/.zshrc",
|
|
75
81
|
"note": "Check 'amp --help' for available flags"
|
|
76
82
|
},
|
|
77
83
|
"envVars": ["ANTHROPIC_API_KEY", "OPENAI_API_KEY"],
|
|
@@ -83,6 +89,7 @@
|
|
|
83
89
|
"command": "",
|
|
84
90
|
"defaultArgs": [],
|
|
85
91
|
"yoloArgs": [],
|
|
92
|
+
"promptArgs": [],
|
|
86
93
|
"docker": {
|
|
87
94
|
"install": "# Custom CLI - add your installation commands here"
|
|
88
95
|
},
|
package/dist/utils/config.d.ts
CHANGED
package/dist/utils/config.js
CHANGED
|
@@ -3,9 +3,30 @@ import { join } from "path";
|
|
|
3
3
|
export const DEFAULT_CLI_CONFIG = {
|
|
4
4
|
command: "claude",
|
|
5
5
|
args: ["--permission-mode", "acceptEdits"],
|
|
6
|
+
promptArgs: ["-p"],
|
|
6
7
|
};
|
|
8
|
+
// Lazy import to avoid circular dependency
|
|
9
|
+
let _getCliProviders = null;
|
|
7
10
|
export function getCliConfig(config) {
|
|
8
|
-
|
|
11
|
+
const cliConfig = config.cli ?? DEFAULT_CLI_CONFIG;
|
|
12
|
+
// If promptArgs is already set, use it
|
|
13
|
+
if (cliConfig.promptArgs !== undefined) {
|
|
14
|
+
return cliConfig;
|
|
15
|
+
}
|
|
16
|
+
// Look up promptArgs from cliProvider if available
|
|
17
|
+
if (config.cliProvider) {
|
|
18
|
+
if (!_getCliProviders) {
|
|
19
|
+
// Dynamic import to avoid circular dependency
|
|
20
|
+
_getCliProviders = require("../templates/prompts.js").getCliProviders;
|
|
21
|
+
}
|
|
22
|
+
const providers = _getCliProviders();
|
|
23
|
+
const provider = providers[config.cliProvider];
|
|
24
|
+
if (provider?.promptArgs !== undefined) {
|
|
25
|
+
return { ...cliConfig, promptArgs: provider.promptArgs };
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// Default to -p for backwards compatibility
|
|
29
|
+
return { ...cliConfig, promptArgs: ["-p"] };
|
|
9
30
|
}
|
|
10
31
|
const RALPH_DIR = ".ralph";
|
|
11
32
|
const CONFIG_FILE = "config.json";
|
package/dist/utils/prompt.js
CHANGED
|
@@ -68,6 +68,7 @@ export async function promptSelectWithArrows(message, options) {
|
|
|
68
68
|
if (process.stdin.isTTY) {
|
|
69
69
|
process.stdin.setRawMode(false);
|
|
70
70
|
}
|
|
71
|
+
process.stdin.pause();
|
|
71
72
|
process.stdout.write("\x1B[?25h"); // Show cursor
|
|
72
73
|
};
|
|
73
74
|
process.stdin.on("data", onKeypress);
|
|
@@ -230,6 +231,7 @@ export async function promptMultiSelectWithArrows(message, options) {
|
|
|
230
231
|
if (process.stdin.isTTY) {
|
|
231
232
|
process.stdin.setRawMode(false);
|
|
232
233
|
}
|
|
234
|
+
process.stdin.pause();
|
|
233
235
|
process.stdout.write("\x1B[?25h"); // Show cursor
|
|
234
236
|
};
|
|
235
237
|
process.stdin.on("data", onKeypress);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ralph-cli-sandboxed",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "AI-driven development automation CLI for Claude Code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -13,11 +13,12 @@
|
|
|
13
13
|
"README.md"
|
|
14
14
|
],
|
|
15
15
|
"scripts": {
|
|
16
|
-
"build": "tsc && npm run copy-config",
|
|
16
|
+
"build": "./node_modules/.bin/tsc && npm run copy-config",
|
|
17
17
|
"copy-config": "mkdir -p dist/config && cp src/config/*.json dist/config/",
|
|
18
18
|
"dev": "npx tsx src/index.ts",
|
|
19
|
-
"typecheck": "tsc --noEmit",
|
|
20
|
-
"prepublishOnly": "npm run build"
|
|
19
|
+
"typecheck": "./node_modules/.bin/tsc --noEmit",
|
|
20
|
+
"prepublishOnly": "npm run build",
|
|
21
|
+
"prepare": "npm run build"
|
|
21
22
|
},
|
|
22
23
|
"dependencies": {
|
|
23
24
|
"readline": "^1.3.0"
|