ralph-cli-sandboxed 0.2.3 → 0.2.5
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 +62 -20
- package/dist/commands/run.js +80 -14
- package/dist/config/cli-providers.json +2 -2
- package/dist/config/languages.json +243 -2
- package/dist/utils/config.d.ts +9 -0
- package/package.json +1 -1
package/dist/commands/docker.js
CHANGED
|
@@ -33,9 +33,24 @@ function getCliProviderSnippet(cliProvider) {
|
|
|
33
33
|
}
|
|
34
34
|
return provider.docker.install;
|
|
35
35
|
}
|
|
36
|
-
function generateDockerfile(language, javaVersion, cliProvider) {
|
|
36
|
+
function generateDockerfile(language, javaVersion, cliProvider, dockerConfig) {
|
|
37
37
|
const languageSnippet = getLanguageSnippet(language, javaVersion);
|
|
38
38
|
const cliSnippet = getCliProviderSnippet(cliProvider);
|
|
39
|
+
// Build git config section if configured
|
|
40
|
+
let gitConfigSection = '';
|
|
41
|
+
if (dockerConfig?.git && (dockerConfig.git.name || dockerConfig.git.email)) {
|
|
42
|
+
const gitCommands = [];
|
|
43
|
+
if (dockerConfig.git.name) {
|
|
44
|
+
gitCommands.push(`git config --global user.name "${dockerConfig.git.name}"`);
|
|
45
|
+
}
|
|
46
|
+
if (dockerConfig.git.email) {
|
|
47
|
+
gitCommands.push(`git config --global user.email "${dockerConfig.git.email}"`);
|
|
48
|
+
}
|
|
49
|
+
gitConfigSection = `
|
|
50
|
+
# Configure git identity
|
|
51
|
+
RUN ${gitCommands.join(' \\\n && ')}
|
|
52
|
+
`;
|
|
53
|
+
}
|
|
39
54
|
return `# Ralph CLI Sandbox Environment
|
|
40
55
|
# Based on Claude Code devcontainer
|
|
41
56
|
# Generated by ralph-cli
|
|
@@ -133,6 +148,7 @@ RUN echo 'alias ll="ls -la"' >> /etc/bash.bashrc && \\
|
|
|
133
148
|
|
|
134
149
|
# Switch to non-root user
|
|
135
150
|
USER node
|
|
151
|
+
${gitConfigSection}
|
|
136
152
|
WORKDIR /workspace
|
|
137
153
|
|
|
138
154
|
# Default to zsh
|
|
@@ -218,7 +234,40 @@ iptables -I OUTPUT -p tcp --dport 80 -m set --match-set allowed_ips dst -j ACCEP
|
|
|
218
234
|
echo "Firewall initialized. Only allowed destinations are accessible."
|
|
219
235
|
echo "Allowed: GitHub, npm, Anthropic API, local network"
|
|
220
236
|
`;
|
|
221
|
-
function generateDockerCompose(imageName) {
|
|
237
|
+
function generateDockerCompose(imageName, dockerConfig) {
|
|
238
|
+
// Build ports section if configured
|
|
239
|
+
let portsSection = '';
|
|
240
|
+
if (dockerConfig?.ports && dockerConfig.ports.length > 0) {
|
|
241
|
+
const portLines = dockerConfig.ports.map(port => ` - "${port}"`).join('\n');
|
|
242
|
+
portsSection = ` ports:\n${portLines}\n`;
|
|
243
|
+
}
|
|
244
|
+
// Build volumes array: base volumes + custom volumes
|
|
245
|
+
const baseVolumes = [
|
|
246
|
+
' # Mount project root (two levels up from .ralph/docker/)',
|
|
247
|
+
' - ../..:/workspace',
|
|
248
|
+
" # Mount host's ~/.claude for Pro/Max OAuth credentials",
|
|
249
|
+
' - ${HOME}/.claude:/home/node/.claude',
|
|
250
|
+
` - ${imageName}-history:/commandhistory`,
|
|
251
|
+
];
|
|
252
|
+
if (dockerConfig?.volumes && dockerConfig.volumes.length > 0) {
|
|
253
|
+
const customVolumeLines = dockerConfig.volumes.map(vol => ` - ${vol}`);
|
|
254
|
+
baseVolumes.push(...customVolumeLines);
|
|
255
|
+
}
|
|
256
|
+
const volumesSection = baseVolumes.join('\n');
|
|
257
|
+
// Build environment section if configured
|
|
258
|
+
let environmentSection = '';
|
|
259
|
+
if (dockerConfig?.environment && Object.keys(dockerConfig.environment).length > 0) {
|
|
260
|
+
const envLines = Object.entries(dockerConfig.environment)
|
|
261
|
+
.map(([key, value]) => ` - ${key}=${value}`)
|
|
262
|
+
.join('\n');
|
|
263
|
+
environmentSection = ` environment:\n${envLines}\n`;
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
// Keep the commented placeholder for users who don't have config
|
|
267
|
+
environmentSection = ` # Uncomment to use API key instead of OAuth:
|
|
268
|
+
# environment:
|
|
269
|
+
# - ANTHROPIC_API_KEY=\${ANTHROPIC_API_KEY}\n`;
|
|
270
|
+
}
|
|
222
271
|
return `# Ralph CLI Docker Compose
|
|
223
272
|
# Generated by ralph-cli
|
|
224
273
|
|
|
@@ -228,16 +277,9 @@ services:
|
|
|
228
277
|
build:
|
|
229
278
|
context: .
|
|
230
279
|
dockerfile: Dockerfile
|
|
231
|
-
volumes:
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
# Mount host's ~/.claude for Pro/Max OAuth credentials
|
|
235
|
-
- \${HOME}/.claude:/home/node/.claude
|
|
236
|
-
- ${imageName}-history:/commandhistory
|
|
237
|
-
# Uncomment to use API key instead of OAuth:
|
|
238
|
-
# environment:
|
|
239
|
-
# - ANTHROPIC_API_KEY=\${ANTHROPIC_API_KEY}
|
|
240
|
-
working_dir: /workspace
|
|
280
|
+
${portsSection} volumes:
|
|
281
|
+
${volumesSection}
|
|
282
|
+
${environmentSection} working_dir: /workspace
|
|
241
283
|
stdin_open: true
|
|
242
284
|
tty: true
|
|
243
285
|
cap_add:
|
|
@@ -255,7 +297,7 @@ dist
|
|
|
255
297
|
.git
|
|
256
298
|
*.log
|
|
257
299
|
`;
|
|
258
|
-
async function generateFiles(ralphDir, language, imageName, force = false, javaVersion, cliProvider) {
|
|
300
|
+
async function generateFiles(ralphDir, language, imageName, force = false, javaVersion, cliProvider, dockerConfig) {
|
|
259
301
|
const dockerDir = join(ralphDir, DOCKER_DIR);
|
|
260
302
|
// Create docker directory
|
|
261
303
|
if (!existsSync(dockerDir)) {
|
|
@@ -263,9 +305,9 @@ async function generateFiles(ralphDir, language, imageName, force = false, javaV
|
|
|
263
305
|
console.log(`Created ${DOCKER_DIR}/`);
|
|
264
306
|
}
|
|
265
307
|
const files = [
|
|
266
|
-
{ name: "Dockerfile", content: generateDockerfile(language, javaVersion, cliProvider) },
|
|
308
|
+
{ name: "Dockerfile", content: generateDockerfile(language, javaVersion, cliProvider, dockerConfig) },
|
|
267
309
|
{ name: "init-firewall.sh", content: FIREWALL_SCRIPT },
|
|
268
|
-
{ name: "docker-compose.yml", content: generateDockerCompose(imageName) },
|
|
310
|
+
{ name: "docker-compose.yml", content: generateDockerCompose(imageName, dockerConfig) },
|
|
269
311
|
{ name: ".dockerignore", content: DOCKERIGNORE },
|
|
270
312
|
];
|
|
271
313
|
for (const file of files) {
|
|
@@ -356,7 +398,7 @@ function getCliProviderConfig(cliProvider) {
|
|
|
356
398
|
modelConfig: provider.modelConfig,
|
|
357
399
|
};
|
|
358
400
|
}
|
|
359
|
-
async function runContainer(ralphDir, imageName, language, javaVersion, cliProvider) {
|
|
401
|
+
async function runContainer(ralphDir, imageName, language, javaVersion, cliProvider, dockerConfig) {
|
|
360
402
|
const dockerDir = join(ralphDir, DOCKER_DIR);
|
|
361
403
|
const dockerfileExists = existsSync(join(dockerDir, "Dockerfile"));
|
|
362
404
|
const hasImage = await imageExists(imageName);
|
|
@@ -364,7 +406,7 @@ async function runContainer(ralphDir, imageName, language, javaVersion, cliProvi
|
|
|
364
406
|
if (!dockerfileExists || !hasImage) {
|
|
365
407
|
if (!dockerfileExists) {
|
|
366
408
|
console.log("Docker folder not found. Initializing docker setup...\n");
|
|
367
|
-
await generateFiles(ralphDir, language, imageName, true, javaVersion, cliProvider);
|
|
409
|
+
await generateFiles(ralphDir, language, imageName, true, javaVersion, cliProvider, dockerConfig);
|
|
368
410
|
console.log("");
|
|
369
411
|
}
|
|
370
412
|
if (!hasImage) {
|
|
@@ -612,7 +654,7 @@ export async function dockerInit(silent = false) {
|
|
|
612
654
|
console.log(`CLI provider: ${config.cliProvider}`);
|
|
613
655
|
}
|
|
614
656
|
console.log(`Image name: ${imageName}\n`);
|
|
615
|
-
await generateFiles(ralphDir, config.language, imageName, true, config.javaVersion, config.cliProvider);
|
|
657
|
+
await generateFiles(ralphDir, config.language, imageName, true, config.javaVersion, config.cliProvider, config.docker);
|
|
616
658
|
if (!silent) {
|
|
617
659
|
console.log(`
|
|
618
660
|
Docker files generated in .ralph/docker/
|
|
@@ -700,7 +742,7 @@ INSTALLING PACKAGES (works with Docker & Podman):
|
|
|
700
742
|
await buildImage(ralphDir);
|
|
701
743
|
break;
|
|
702
744
|
case "run":
|
|
703
|
-
await runContainer(ralphDir, imageName, config.language, config.javaVersion, config.cliProvider);
|
|
745
|
+
await runContainer(ralphDir, imageName, config.language, config.javaVersion, config.cliProvider, config.docker);
|
|
704
746
|
break;
|
|
705
747
|
case "clean":
|
|
706
748
|
await cleanImage(imageName, ralphDir);
|
|
@@ -719,7 +761,7 @@ INSTALLING PACKAGES (works with Docker & Podman):
|
|
|
719
761
|
console.log(`CLI provider: ${config.cliProvider}`);
|
|
720
762
|
}
|
|
721
763
|
console.log(`Image name: ${imageName}\n`);
|
|
722
|
-
await generateFiles(ralphDir, config.language, imageName, force, config.javaVersion, config.cliProvider);
|
|
764
|
+
await generateFiles(ralphDir, config.language, imageName, force, config.javaVersion, config.cliProvider, config.docker);
|
|
723
765
|
console.log(`
|
|
724
766
|
Docker files generated in .ralph/docker/
|
|
725
767
|
|
package/dist/commands/run.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn } from "child_process";
|
|
2
|
-
import { readFileSync, writeFileSync, unlinkSync } from "fs";
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync, unlinkSync } from "fs";
|
|
3
3
|
import { join } from "path";
|
|
4
4
|
import { checkFilesExist, loadConfig, loadPrompt, getPaths, getCliConfig, requireContainer } from "../utils/config.js";
|
|
5
5
|
import { resolvePromptVariables } from "../templates/prompts.js";
|
|
@@ -29,6 +29,48 @@ function createFilteredPrd(prdPath, baseDir, category) {
|
|
|
29
29
|
hasIncomplete: filteredItems.length > 0
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Syncs passes flags from prd-tasks.json back to prd.json.
|
|
34
|
+
* If the LLM marked any item as passes: true in prd-tasks.json,
|
|
35
|
+
* find the matching item in prd.json and update it.
|
|
36
|
+
* Returns the number of items synced.
|
|
37
|
+
*/
|
|
38
|
+
function syncPassesFromTasks(tasksPath, prdPath) {
|
|
39
|
+
// Check if tasks file exists
|
|
40
|
+
if (!existsSync(tasksPath)) {
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const tasksContent = readFileSync(tasksPath, "utf-8");
|
|
45
|
+
const tasks = JSON.parse(tasksContent);
|
|
46
|
+
const prdContent = readFileSync(prdPath, "utf-8");
|
|
47
|
+
const prd = JSON.parse(prdContent);
|
|
48
|
+
let synced = 0;
|
|
49
|
+
// Find tasks that were marked as passing
|
|
50
|
+
for (const task of tasks) {
|
|
51
|
+
if (task.passes === true) {
|
|
52
|
+
// Find matching item in prd by description
|
|
53
|
+
const match = prd.find(item => item.description === task.description ||
|
|
54
|
+
item.description.includes(task.description) ||
|
|
55
|
+
task.description.includes(item.description));
|
|
56
|
+
if (match && !match.passes) {
|
|
57
|
+
match.passes = true;
|
|
58
|
+
synced++;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Write back if any items were synced
|
|
63
|
+
if (synced > 0) {
|
|
64
|
+
writeFileSync(prdPath, JSON.stringify(prd, null, 2) + "\n");
|
|
65
|
+
console.log(`\x1b[32mSynced ${synced} completed item(s) from prd-tasks.json to prd.json\x1b[0m`);
|
|
66
|
+
}
|
|
67
|
+
return synced;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// Ignore errors - the validation step will handle any issues
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
32
74
|
async function runIteration(prompt, paths, sandboxed, filteredPrdPath, cliConfig, debug, model) {
|
|
33
75
|
return new Promise((resolve, reject) => {
|
|
34
76
|
let output = "";
|
|
@@ -223,8 +265,8 @@ export async function run(args) {
|
|
|
223
265
|
});
|
|
224
266
|
const paths = getPaths();
|
|
225
267
|
const cliConfig = getCliConfig(config);
|
|
226
|
-
//
|
|
227
|
-
const
|
|
268
|
+
// Progress tracking: stop only if no tasks complete after N iterations
|
|
269
|
+
const MAX_ITERATIONS_WITHOUT_PROGRESS = 3;
|
|
228
270
|
// Get requested iteration count (may be adjusted dynamically)
|
|
229
271
|
const requestedIterations = parseInt(filteredArgs[0]) || Infinity;
|
|
230
272
|
// Container is required, so always run with skip-permissions
|
|
@@ -252,21 +294,19 @@ export async function run(args) {
|
|
|
252
294
|
let consecutiveFailures = 0;
|
|
253
295
|
let lastExitCode = 0;
|
|
254
296
|
let iterationCount = 0;
|
|
297
|
+
// Progress tracking for --all mode
|
|
298
|
+
// Progress = tasks completed OR new tasks added (allows ralph to expand the PRD)
|
|
299
|
+
const initialCounts = countPrdItems(paths.prd, category);
|
|
300
|
+
let lastCompletedCount = initialCounts.complete;
|
|
301
|
+
let lastTotalCount = initialCounts.total;
|
|
302
|
+
let iterationsWithoutProgress = 0;
|
|
255
303
|
try {
|
|
256
304
|
while (true) {
|
|
257
305
|
iterationCount++;
|
|
258
|
-
// Dynamic iteration limit: recalculate based on current incomplete count
|
|
259
|
-
// This allows the limit to expand if tasks are added during the run
|
|
260
306
|
const currentCounts = countPrdItems(paths.prd, category);
|
|
261
|
-
const dynamicMaxIterations = currentCounts.incomplete + ITERATION_SAFETY_MARGIN;
|
|
262
307
|
// Check if we should stop (not in loop mode)
|
|
263
|
-
if (!loopMode) {
|
|
264
|
-
if (
|
|
265
|
-
console.log(`\nStopping: reached iteration limit (${dynamicMaxIterations}) with ${currentCounts.incomplete} tasks remaining.`);
|
|
266
|
-
console.log("This may indicate tasks are not completing. Check the PRD and progress.");
|
|
267
|
-
break;
|
|
268
|
-
}
|
|
269
|
-
if (!allMode && iterationCount > Math.min(requestedIterations, dynamicMaxIterations)) {
|
|
308
|
+
if (!loopMode && !allMode) {
|
|
309
|
+
if (iterationCount > requestedIterations) {
|
|
270
310
|
break;
|
|
271
311
|
}
|
|
272
312
|
}
|
|
@@ -278,7 +318,7 @@ export async function run(args) {
|
|
|
278
318
|
console.log(`Iteration ${iterationCount}`);
|
|
279
319
|
}
|
|
280
320
|
else {
|
|
281
|
-
console.log(`Iteration ${iterationCount} of ${
|
|
321
|
+
console.log(`Iteration ${iterationCount} of ${requestedIterations}`);
|
|
282
322
|
}
|
|
283
323
|
console.log(`${"=".repeat(50)}\n`);
|
|
284
324
|
// Load a valid copy of the PRD before handing to the LLM
|
|
@@ -342,6 +382,9 @@ export async function run(args) {
|
|
|
342
382
|
}
|
|
343
383
|
}
|
|
344
384
|
const { exitCode, output } = await runIteration(prompt, paths, sandboxed, filteredPrdPath, cliConfig, debug, model);
|
|
385
|
+
// Sync any completed items from prd-tasks.json back to prd.json
|
|
386
|
+
// This catches cases where the LLM updated prd-tasks.json instead of prd.json
|
|
387
|
+
syncPassesFromTasks(filteredPrdPath, paths.prd);
|
|
345
388
|
// Clean up temp file after each iteration
|
|
346
389
|
try {
|
|
347
390
|
unlinkSync(filteredPrdPath);
|
|
@@ -352,6 +395,29 @@ export async function run(args) {
|
|
|
352
395
|
filteredPrdPath = null;
|
|
353
396
|
// Validate and recover PRD if the LLM corrupted it
|
|
354
397
|
validateAndRecoverPrd(paths.prd, validPrd);
|
|
398
|
+
// Track progress for --all mode: stop if no progress after N iterations
|
|
399
|
+
// Progress = tasks completed OR new tasks added (allows ralph to expand the PRD)
|
|
400
|
+
if (allMode) {
|
|
401
|
+
const progressCounts = countPrdItems(paths.prd, category);
|
|
402
|
+
const tasksCompleted = progressCounts.complete > lastCompletedCount;
|
|
403
|
+
const tasksAdded = progressCounts.total > lastTotalCount;
|
|
404
|
+
if (tasksCompleted || tasksAdded) {
|
|
405
|
+
// Progress made - reset counter
|
|
406
|
+
iterationsWithoutProgress = 0;
|
|
407
|
+
lastCompletedCount = progressCounts.complete;
|
|
408
|
+
lastTotalCount = progressCounts.total;
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
iterationsWithoutProgress++;
|
|
412
|
+
}
|
|
413
|
+
if (iterationsWithoutProgress >= MAX_ITERATIONS_WITHOUT_PROGRESS) {
|
|
414
|
+
console.log(`\nStopping: no progress after ${MAX_ITERATIONS_WITHOUT_PROGRESS} consecutive iterations.`);
|
|
415
|
+
console.log(`(No tasks completed and no new tasks added)`);
|
|
416
|
+
console.log(`Status: ${progressCounts.complete}/${progressCounts.total} complete, ${progressCounts.incomplete} remaining.`);
|
|
417
|
+
console.log("Check the PRD and task definitions for issues.");
|
|
418
|
+
break;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
355
421
|
if (exitCode !== 0) {
|
|
356
422
|
console.error(`\n${cliConfig.command} exited with code ${exitCode}`);
|
|
357
423
|
// Track consecutive failures to detect persistent errors (e.g., missing API key)
|
|
@@ -98,8 +98,8 @@
|
|
|
98
98
|
"description": "Sourcegraph's AMP coding agent",
|
|
99
99
|
"command": "amp",
|
|
100
100
|
"defaultArgs": [],
|
|
101
|
-
"yoloArgs": ["--
|
|
102
|
-
"promptArgs": [],
|
|
101
|
+
"yoloArgs": ["--dangerously-allow-all"],
|
|
102
|
+
"promptArgs": ["-x"],
|
|
103
103
|
"docker": {
|
|
104
104
|
"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",
|
|
105
105
|
"note": "Check 'amp --help' for available flags"
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
{ "name": "Hono", "description": "Lightweight web framework" },
|
|
14
14
|
{ "name": "Drizzle ORM", "description": "TypeScript ORM" },
|
|
15
15
|
{ "name": "Prisma", "description": "Type-safe database ORM" },
|
|
16
|
+
{ "name": "React", "description": "UI component library" },
|
|
17
|
+
{ "name": "Tailwind CSS", "description": "Utility-first CSS framework" },
|
|
18
|
+
{ "name": "tRPC", "description": "End-to-end typesafe APIs" },
|
|
19
|
+
{ "name": "Zod", "description": "TypeScript-first schema validation" },
|
|
16
20
|
{ "name": "SQLite", "description": "Embedded SQL database" },
|
|
17
21
|
{ "name": "PostgreSQL", "description": "Advanced SQL database" }
|
|
18
22
|
]
|
|
@@ -30,11 +34,19 @@
|
|
|
30
34
|
{ "name": "Fastify", "description": "High-performance web framework" },
|
|
31
35
|
{ "name": "NestJS", "description": "Progressive Node.js framework" },
|
|
32
36
|
{ "name": "Next.js", "description": "React framework for production" },
|
|
37
|
+
{ "name": "Remix", "description": "Full stack web framework" },
|
|
33
38
|
{ "name": "Prisma", "description": "Type-safe database ORM" },
|
|
34
39
|
{ "name": "TypeORM", "description": "TypeScript ORM" },
|
|
40
|
+
{ "name": "Drizzle ORM", "description": "TypeScript ORM with SQL-like syntax" },
|
|
41
|
+
{ "name": "tRPC", "description": "End-to-end typesafe APIs" },
|
|
42
|
+
{ "name": "Zod", "description": "TypeScript-first schema validation" },
|
|
43
|
+
{ "name": "Turborepo", "description": "High-performance monorepo build system" },
|
|
44
|
+
{ "name": "SWC", "description": "Fast TypeScript/JavaScript compiler" },
|
|
35
45
|
{ "name": "Jest", "description": "JavaScript testing framework" },
|
|
36
46
|
{ "name": "Vitest", "description": "Fast unit testing framework" },
|
|
47
|
+
{ "name": "Playwright", "description": "End-to-end testing framework" },
|
|
37
48
|
{ "name": "PostgreSQL", "description": "Advanced SQL database" },
|
|
49
|
+
{ "name": "MySQL", "description": "Popular SQL database" },
|
|
38
50
|
{ "name": "MongoDB", "description": "NoSQL document database" },
|
|
39
51
|
{ "name": "Redis", "description": "In-memory data store" }
|
|
40
52
|
]
|
|
@@ -54,7 +66,16 @@
|
|
|
54
66
|
{ "name": "SQLAlchemy", "description": "SQL toolkit and ORM" },
|
|
55
67
|
{ "name": "Pydantic", "description": "Data validation library" },
|
|
56
68
|
{ "name": "Celery", "description": "Distributed task queue" },
|
|
69
|
+
{ "name": "LangChain", "description": "Framework for LLM applications" },
|
|
70
|
+
{ "name": "Alembic", "description": "Database migration tool" },
|
|
71
|
+
{ "name": "Poetry", "description": "Dependency management and packaging" },
|
|
72
|
+
{ "name": "Ruff", "description": "Fast Python linter and formatter" },
|
|
73
|
+
{ "name": "Uvicorn", "description": "ASGI web server" },
|
|
74
|
+
{ "name": "Typer", "description": "CLI application framework" },
|
|
75
|
+
{ "name": "Polars", "description": "Fast DataFrame library" },
|
|
76
|
+
{ "name": "uv", "description": "Fast Python package installer and resolver" },
|
|
57
77
|
{ "name": "PostgreSQL", "description": "Advanced SQL database" },
|
|
78
|
+
{ "name": "MySQL", "description": "Popular SQL database" },
|
|
58
79
|
{ "name": "Redis", "description": "In-memory data store" },
|
|
59
80
|
{ "name": "pytest", "description": "Testing framework" }
|
|
60
81
|
]
|
|
@@ -74,7 +95,14 @@
|
|
|
74
95
|
{ "name": "Fiber", "description": "Express-inspired web framework" },
|
|
75
96
|
{ "name": "GORM", "description": "ORM library for Go" },
|
|
76
97
|
{ "name": "sqlx", "description": "SQL extensions for Go" },
|
|
98
|
+
{ "name": "Chi", "description": "Lightweight, idiomatic HTTP router" },
|
|
99
|
+
{ "name": "Cobra", "description": "CLI application framework" },
|
|
100
|
+
{ "name": "Viper", "description": "Configuration management library" },
|
|
101
|
+
{ "name": "Wire", "description": "Compile-time dependency injection" },
|
|
102
|
+
{ "name": "testify", "description": "Testing toolkit with assertions and mocks" },
|
|
77
103
|
{ "name": "PostgreSQL", "description": "Advanced SQL database" },
|
|
104
|
+
{ "name": "MongoDB", "description": "NoSQL document database" },
|
|
105
|
+
{ "name": "MySQL", "description": "Popular SQL database" },
|
|
78
106
|
{ "name": "Redis", "description": "In-memory data store" }
|
|
79
107
|
]
|
|
80
108
|
},
|
|
@@ -94,7 +122,13 @@
|
|
|
94
122
|
{ "name": "SQLx", "description": "Async SQL toolkit" },
|
|
95
123
|
{ "name": "Tokio", "description": "Async runtime" },
|
|
96
124
|
{ "name": "Serde", "description": "Serialization framework" },
|
|
97
|
-
{ "name": "
|
|
125
|
+
{ "name": "Tauri", "description": "Desktop app framework" },
|
|
126
|
+
{ "name": "SeaORM", "description": "Async ORM for Rust" },
|
|
127
|
+
{ "name": "Clap", "description": "Command-line argument parser" },
|
|
128
|
+
{ "name": "Tracing", "description": "Application-level tracing framework" },
|
|
129
|
+
{ "name": "PostgreSQL", "description": "Advanced SQL database" },
|
|
130
|
+
{ "name": "Redis", "description": "In-memory data store" },
|
|
131
|
+
{ "name": "MongoDB", "description": "NoSQL document database" }
|
|
98
132
|
]
|
|
99
133
|
},
|
|
100
134
|
"java": {
|
|
@@ -113,9 +147,16 @@
|
|
|
113
147
|
{ "name": "Micronaut", "description": "Modern JVM framework" },
|
|
114
148
|
{ "name": "Hibernate", "description": "ORM framework" },
|
|
115
149
|
{ "name": "JPA", "description": "Java Persistence API" },
|
|
150
|
+
{ "name": "Gradle", "description": "Build automation tool" },
|
|
151
|
+
{ "name": "Lombok", "description": "Boilerplate code reduction library" },
|
|
152
|
+
{ "name": "MapStruct", "description": "Bean mapping code generator" },
|
|
153
|
+
{ "name": "Flyway", "description": "Database migration tool" },
|
|
154
|
+
{ "name": "Testcontainers", "description": "Integration testing with containers" },
|
|
116
155
|
{ "name": "JUnit", "description": "Testing framework" },
|
|
117
156
|
{ "name": "PostgreSQL", "description": "Advanced SQL database" },
|
|
118
|
-
{ "name": "MySQL", "description": "Popular SQL database" }
|
|
157
|
+
{ "name": "MySQL", "description": "Popular SQL database" },
|
|
158
|
+
{ "name": "Redis", "description": "In-memory data store" },
|
|
159
|
+
{ "name": "MongoDB", "description": "NoSQL document database" }
|
|
119
160
|
]
|
|
120
161
|
},
|
|
121
162
|
"kotlin": {
|
|
@@ -138,10 +179,210 @@
|
|
|
138
179
|
{ "name": "Kotest", "description": "Kotlin testing framework" },
|
|
139
180
|
{ "name": "kotlinx.coroutines", "description": "Coroutines for async programming" },
|
|
140
181
|
{ "name": "kotlinx.serialization", "description": "Multiplatform serialization" },
|
|
182
|
+
{ "name": "Arrow", "description": "Functional companion to Kotlin's standard library" },
|
|
183
|
+
{ "name": "Mockk", "description": "Mocking library for Kotlin" },
|
|
184
|
+
{ "name": "Flyway", "description": "Database migration tool" },
|
|
185
|
+
{ "name": "Compose Multiplatform", "description": "Declarative UI framework for Kotlin" },
|
|
186
|
+
{ "name": "PostgreSQL", "description": "Advanced SQL database" },
|
|
187
|
+
{ "name": "MySQL", "description": "Popular SQL database" },
|
|
188
|
+
{ "name": "Redis", "description": "In-memory data store" },
|
|
189
|
+
{ "name": "MongoDB", "description": "NoSQL document database" }
|
|
190
|
+
]
|
|
191
|
+
},
|
|
192
|
+
"csharp": {
|
|
193
|
+
"name": "C#/.NET",
|
|
194
|
+
"description": "C# with .NET SDK",
|
|
195
|
+
"checkCommand": "dotnet build",
|
|
196
|
+
"testCommand": "dotnet test",
|
|
197
|
+
"docker": {
|
|
198
|
+
"install": "# Install .NET SDK 8.0\nRUN apt-get update && apt-get install -y dotnet-sdk-8.0 && rm -rf /var/lib/apt/lists/*"
|
|
199
|
+
},
|
|
200
|
+
"technologies": [
|
|
201
|
+
{ "name": "ASP.NET Core", "description": "Web framework for .NET" },
|
|
202
|
+
{ "name": "Blazor", "description": "Web UI framework using C#" },
|
|
203
|
+
{ "name": "Entity Framework Core", "description": "Modern ORM for .NET" },
|
|
204
|
+
{ "name": "Dapper", "description": "Simple object mapper for .NET" },
|
|
205
|
+
{ "name": "xUnit", "description": "Unit testing framework" },
|
|
206
|
+
{ "name": "NUnit", "description": "Testing framework for .NET" },
|
|
141
207
|
{ "name": "PostgreSQL", "description": "Advanced SQL database" },
|
|
208
|
+
{ "name": "SQL Server", "description": "Microsoft SQL database" },
|
|
209
|
+
{ "name": "Redis", "description": "In-memory data store" },
|
|
142
210
|
{ "name": "MongoDB", "description": "NoSQL document database" }
|
|
143
211
|
]
|
|
144
212
|
},
|
|
213
|
+
"ruby": {
|
|
214
|
+
"name": "Ruby",
|
|
215
|
+
"description": "Ruby with Bundler",
|
|
216
|
+
"checkCommand": "bundle exec rubocop --fail-level error",
|
|
217
|
+
"testCommand": "bundle exec rspec",
|
|
218
|
+
"docker": {
|
|
219
|
+
"install": "# Install Ruby via rbenv\nRUN apt-get update && apt-get install -y \\\n rbenv \\\n ruby-build \\\n && rm -rf /var/lib/apt/lists/*\nRUN rbenv install 3.3.0 && rbenv global 3.3.0\nENV PATH=\"/root/.rbenv/shims:/root/.rbenv/bin:$PATH\"\nRUN gem install bundler"
|
|
220
|
+
},
|
|
221
|
+
"technologies": [
|
|
222
|
+
{ "name": "Ruby on Rails", "description": "Full-stack web framework" },
|
|
223
|
+
{ "name": "Sinatra", "description": "Lightweight web framework" },
|
|
224
|
+
{ "name": "Hanami", "description": "Modern Ruby web framework" },
|
|
225
|
+
{ "name": "ActiveRecord", "description": "ORM for Ruby" },
|
|
226
|
+
{ "name": "Sequel", "description": "Database toolkit for Ruby" },
|
|
227
|
+
{ "name": "RSpec", "description": "BDD testing framework" },
|
|
228
|
+
{ "name": "Minitest", "description": "Testing framework" },
|
|
229
|
+
{ "name": "PostgreSQL", "description": "Advanced SQL database" },
|
|
230
|
+
{ "name": "MySQL", "description": "Popular SQL database" },
|
|
231
|
+
{ "name": "Redis", "description": "In-memory data store" },
|
|
232
|
+
{ "name": "Sidekiq", "description": "Background job processor" }
|
|
233
|
+
]
|
|
234
|
+
},
|
|
235
|
+
"php": {
|
|
236
|
+
"name": "PHP",
|
|
237
|
+
"description": "PHP with Composer",
|
|
238
|
+
"checkCommand": "composer validate && php -l",
|
|
239
|
+
"testCommand": "vendor/bin/phpunit",
|
|
240
|
+
"docker": {
|
|
241
|
+
"install": "# Install PHP and Composer\nRUN apt-get update && apt-get install -y \\\n php \\\n php-cli \\\n php-mbstring \\\n php-xml \\\n php-curl \\\n php-zip \\\n php-pgsql \\\n php-mysql \\\n php-redis \\\n unzip \\\n && rm -rf /var/lib/apt/lists/*\n# Install Composer\nRUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer"
|
|
242
|
+
},
|
|
243
|
+
"technologies": [
|
|
244
|
+
{ "name": "Laravel", "description": "Full-stack PHP framework" },
|
|
245
|
+
{ "name": "Symfony", "description": "Modular PHP framework" },
|
|
246
|
+
{ "name": "Slim", "description": "Micro framework for PHP" },
|
|
247
|
+
{ "name": "Doctrine ORM", "description": "PHP database abstraction" },
|
|
248
|
+
{ "name": "Eloquent", "description": "Laravel's ORM" },
|
|
249
|
+
{ "name": "PHPUnit", "description": "Testing framework for PHP" },
|
|
250
|
+
{ "name": "Pest", "description": "Elegant PHP testing framework" },
|
|
251
|
+
{ "name": "PostgreSQL", "description": "Advanced SQL database" },
|
|
252
|
+
{ "name": "MySQL", "description": "Popular SQL database" },
|
|
253
|
+
{ "name": "Redis", "description": "In-memory data store" }
|
|
254
|
+
]
|
|
255
|
+
},
|
|
256
|
+
"swift": {
|
|
257
|
+
"name": "Swift",
|
|
258
|
+
"description": "Swift with Swift Package Manager",
|
|
259
|
+
"checkCommand": "swift build",
|
|
260
|
+
"testCommand": "swift test",
|
|
261
|
+
"docker": {
|
|
262
|
+
"install": "# Install Swift toolchain\nRUN apt-get update && apt-get install -y \\\n binutils \\\n git \\\n gnupg2 \\\n libc6-dev \\\n libcurl4-openssl-dev \\\n libedit2 \\\n libgcc-11-dev \\\n libpython3-dev \\\n libsqlite3-0 \\\n libstdc++-11-dev \\\n libxml2-dev \\\n libz3-dev \\\n pkg-config \\\n tzdata \\\n unzip \\\n zlib1g-dev \\\n && rm -rf /var/lib/apt/lists/*\nRUN ARCH=$(dpkg --print-architecture) && \\\n if [ \"$ARCH\" = \"amd64\" ]; then SWIFT_ARCH=\"x86_64\"; else SWIFT_ARCH=\"aarch64\"; fi && \\\n curl -fsSL https://download.swift.org/swift-5.10-release/ubuntu2204/swift-5.10-RELEASE/swift-5.10-RELEASE-ubuntu22.04-${SWIFT_ARCH}.tar.gz | tar -xz -C /opt\nENV PATH=\"/opt/swift-5.10-RELEASE-ubuntu22.04/usr/bin:$PATH\""
|
|
263
|
+
},
|
|
264
|
+
"technologies": [
|
|
265
|
+
{ "name": "Vapor", "description": "Server-side Swift web framework" },
|
|
266
|
+
{ "name": "Hummingbird", "description": "Lightweight Swift web framework" },
|
|
267
|
+
{ "name": "Fluent ORM", "description": "Swift ORM for Vapor" },
|
|
268
|
+
{ "name": "SwiftNIO", "description": "Event-driven network framework" },
|
|
269
|
+
{ "name": "XCTest", "description": "Swift testing framework" },
|
|
270
|
+
{ "name": "PostgreSQL", "description": "Advanced SQL database" },
|
|
271
|
+
{ "name": "SQLite", "description": "Embedded SQL database" },
|
|
272
|
+
{ "name": "Redis", "description": "In-memory data store" }
|
|
273
|
+
]
|
|
274
|
+
},
|
|
275
|
+
"elixir": {
|
|
276
|
+
"name": "Elixir",
|
|
277
|
+
"description": "Elixir with Mix build tool",
|
|
278
|
+
"checkCommand": "mix compile --warnings-as-errors",
|
|
279
|
+
"testCommand": "mix test",
|
|
280
|
+
"docker": {
|
|
281
|
+
"install": "# Install Erlang and Elixir\nRUN apt-get update && apt-get install -y \\\n erlang \\\n elixir \\\n && rm -rf /var/lib/apt/lists/*\nRUN mix local.hex --force && mix local.rebar --force"
|
|
282
|
+
},
|
|
283
|
+
"technologies": [
|
|
284
|
+
{ "name": "Phoenix", "description": "Web framework for Elixir" },
|
|
285
|
+
{ "name": "Ecto", "description": "Database wrapper and query generator" },
|
|
286
|
+
{ "name": "LiveView", "description": "Real-time server-rendered HTML" },
|
|
287
|
+
{ "name": "Oban", "description": "Background job processing" },
|
|
288
|
+
{ "name": "ExUnit", "description": "Built-in testing framework" },
|
|
289
|
+
{ "name": "PostgreSQL", "description": "Advanced SQL database" },
|
|
290
|
+
{ "name": "Redis", "description": "In-memory data store" }
|
|
291
|
+
]
|
|
292
|
+
},
|
|
293
|
+
"scala": {
|
|
294
|
+
"name": "Scala",
|
|
295
|
+
"description": "Scala with sbt build tool",
|
|
296
|
+
"checkCommand": "sbt compile",
|
|
297
|
+
"testCommand": "sbt test",
|
|
298
|
+
"docker": {
|
|
299
|
+
"install": "# Install Java and sbt\nRUN apt-get update && apt-get install -y \\\n openjdk-17-jdk \\\n apt-transport-https \\\n gnupg \\\n && rm -rf /var/lib/apt/lists/*\nENV JAVA_HOME=\"/usr/lib/jvm/java-17-openjdk-$(dpkg --print-architecture)\"\n# Install sbt\nRUN echo \"deb https://repo.scala-sbt.org/scalasbt/debian all main\" | tee /etc/apt/sources.list.d/sbt.list && \\\n curl -sL \"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x2EE0EA64E40A89B84B2DF73499E82A75642AC823\" | gpg --dearmor -o /etc/apt/trusted.gpg.d/sbt.gpg && \\\n apt-get update && apt-get install -y sbt && rm -rf /var/lib/apt/lists/*"
|
|
300
|
+
},
|
|
301
|
+
"technologies": [
|
|
302
|
+
{ "name": "Play Framework", "description": "High-velocity web framework" },
|
|
303
|
+
{ "name": "Akka HTTP", "description": "HTTP server and client toolkit" },
|
|
304
|
+
{ "name": "http4s", "description": "Typeful, functional HTTP library" },
|
|
305
|
+
{ "name": "Slick", "description": "Functional relational mapping" },
|
|
306
|
+
{ "name": "Doobie", "description": "Functional JDBC layer" },
|
|
307
|
+
{ "name": "ScalaTest", "description": "Testing framework for Scala" },
|
|
308
|
+
{ "name": "Specs2", "description": "Software specification library" },
|
|
309
|
+
{ "name": "PostgreSQL", "description": "Advanced SQL database" },
|
|
310
|
+
{ "name": "Kafka", "description": "Distributed event streaming platform" },
|
|
311
|
+
{ "name": "Spark", "description": "Unified analytics engine" }
|
|
312
|
+
]
|
|
313
|
+
},
|
|
314
|
+
"zig": {
|
|
315
|
+
"name": "Zig",
|
|
316
|
+
"description": "Zig programming language",
|
|
317
|
+
"checkCommand": "zig build",
|
|
318
|
+
"testCommand": "zig build test",
|
|
319
|
+
"docker": {
|
|
320
|
+
"install": "# Install Zig compiler\nRUN ARCH=$(dpkg --print-architecture) && \\\n if [ \"$ARCH\" = \"amd64\" ]; then ZIG_ARCH=\"x86_64\"; else ZIG_ARCH=\"aarch64\"; fi && \\\n curl -fsSL https://ziglang.org/download/0.11.0/zig-linux-${ZIG_ARCH}-0.11.0.tar.xz | tar -xJ -C /opt && \\\n ln -s /opt/zig-linux-${ZIG_ARCH}-0.11.0/zig /usr/local/bin/zig"
|
|
321
|
+
},
|
|
322
|
+
"technologies": [
|
|
323
|
+
{ "name": "std.http", "description": "Zig standard library HTTP server/client" },
|
|
324
|
+
{ "name": "SQLite", "description": "Embedded SQL database" },
|
|
325
|
+
{ "name": "PostgreSQL", "description": "Advanced SQL database" }
|
|
326
|
+
]
|
|
327
|
+
},
|
|
328
|
+
"haskell": {
|
|
329
|
+
"name": "Haskell",
|
|
330
|
+
"description": "Haskell with Stack build tool",
|
|
331
|
+
"checkCommand": "stack build",
|
|
332
|
+
"testCommand": "stack test",
|
|
333
|
+
"docker": {
|
|
334
|
+
"install": "# Install GHC and Stack\nRUN apt-get update && apt-get install -y \\\n curl \\\n gcc \\\n g++ \\\n make \\\n libffi-dev \\\n libgmp-dev \\\n libtinfo-dev \\\n zlib1g-dev \\\n && rm -rf /var/lib/apt/lists/*\n# Install Stack\nRUN curl -sSL https://get.haskellstack.org/ | sh\n# Setup GHC via Stack\nRUN stack setup"
|
|
335
|
+
},
|
|
336
|
+
"technologies": [
|
|
337
|
+
{ "name": "Servant", "description": "Type-level web API framework" },
|
|
338
|
+
{ "name": "Yesod", "description": "Full-stack Haskell web framework" },
|
|
339
|
+
{ "name": "Scotty", "description": "Lightweight web framework" },
|
|
340
|
+
{ "name": "Persistent", "description": "Type-safe database access" },
|
|
341
|
+
{ "name": "Esqueleto", "description": "Type-safe EDSL for SQL queries" },
|
|
342
|
+
{ "name": "HSpec", "description": "BDD-style testing framework" },
|
|
343
|
+
{ "name": "QuickCheck", "description": "Property-based testing library" },
|
|
344
|
+
{ "name": "PostgreSQL", "description": "Advanced SQL database" }
|
|
345
|
+
]
|
|
346
|
+
},
|
|
347
|
+
"clojure": {
|
|
348
|
+
"name": "Clojure",
|
|
349
|
+
"description": "Clojure with Leiningen build tool",
|
|
350
|
+
"checkCommand": "lein check",
|
|
351
|
+
"testCommand": "lein test",
|
|
352
|
+
"docker": {
|
|
353
|
+
"install": "# Install Java and Leiningen\nRUN apt-get update && apt-get install -y \\\n openjdk-17-jdk \\\n && rm -rf /var/lib/apt/lists/*\nENV JAVA_HOME=\"/usr/lib/jvm/java-17-openjdk-$(dpkg --print-architecture)\"\n# Install Leiningen\nRUN curl -fsSL https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein -o /usr/local/bin/lein && \\\n chmod +x /usr/local/bin/lein && \\\n lein"
|
|
354
|
+
},
|
|
355
|
+
"technologies": [
|
|
356
|
+
{ "name": "Ring", "description": "HTTP server abstraction" },
|
|
357
|
+
{ "name": "Compojure", "description": "Routing library for Ring" },
|
|
358
|
+
{ "name": "Pedestal", "description": "Server-side web framework" },
|
|
359
|
+
{ "name": "next.jdbc", "description": "Modern Clojure JDBC wrapper" },
|
|
360
|
+
{ "name": "HoneySQL", "description": "SQL as Clojure data structures" },
|
|
361
|
+
{ "name": "clojure.test", "description": "Built-in testing framework" },
|
|
362
|
+
{ "name": "Midje", "description": "Testing framework with mocking" },
|
|
363
|
+
{ "name": "PostgreSQL", "description": "Advanced SQL database" },
|
|
364
|
+
{ "name": "Datomic", "description": "Immutable database system" }
|
|
365
|
+
]
|
|
366
|
+
},
|
|
367
|
+
"deno": {
|
|
368
|
+
"name": "Deno (TypeScript)",
|
|
369
|
+
"description": "Deno runtime with TypeScript",
|
|
370
|
+
"checkCommand": "deno check **/*.ts",
|
|
371
|
+
"testCommand": "deno test",
|
|
372
|
+
"docker": {
|
|
373
|
+
"install": "# Install Deno\nRUN curl -fsSL https://deno.land/install.sh | sh\nENV DENO_INSTALL=\"/root/.deno\"\nENV PATH=\"${DENO_INSTALL}/bin:${PATH}\""
|
|
374
|
+
},
|
|
375
|
+
"technologies": [
|
|
376
|
+
{ "name": "Fresh", "description": "Next-gen web framework for Deno" },
|
|
377
|
+
{ "name": "Oak", "description": "Middleware framework for Deno" },
|
|
378
|
+
{ "name": "Hono", "description": "Lightweight web framework" },
|
|
379
|
+
{ "name": "Drizzle ORM", "description": "TypeScript ORM" },
|
|
380
|
+
{ "name": "Deno KV", "description": "Built-in key-value database" },
|
|
381
|
+
{ "name": "PostgreSQL", "description": "Advanced SQL database" },
|
|
382
|
+
{ "name": "SQLite", "description": "Embedded SQL database" },
|
|
383
|
+
{ "name": "Redis", "description": "In-memory data store" }
|
|
384
|
+
]
|
|
385
|
+
},
|
|
145
386
|
"none": {
|
|
146
387
|
"name": "None (custom)",
|
|
147
388
|
"description": "Custom configuration",
|
package/dist/utils/config.d.ts
CHANGED
|
@@ -15,6 +15,15 @@ export interface RalphConfig {
|
|
|
15
15
|
javaVersion?: number;
|
|
16
16
|
cli?: CliConfig;
|
|
17
17
|
cliProvider?: string;
|
|
18
|
+
docker?: {
|
|
19
|
+
ports?: string[];
|
|
20
|
+
volumes?: string[];
|
|
21
|
+
environment?: Record<string, string>;
|
|
22
|
+
git?: {
|
|
23
|
+
name?: string;
|
|
24
|
+
email?: string;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
18
27
|
}
|
|
19
28
|
export declare const DEFAULT_CLI_CONFIG: CliConfig;
|
|
20
29
|
export declare function getCliConfig(config: RalphConfig): CliConfig;
|