ralph-cli-sandboxed 0.2.5 → 0.2.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.
- package/README.md +29 -66
- package/dist/commands/docker.js +228 -25
- package/dist/commands/help.js +1 -0
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.js +127 -71
- package/dist/config/languages.json +1 -1
- package/dist/utils/config.d.ts +30 -0
- package/dist/utils/prompt.d.ts +1 -1
- package/dist/utils/prompt.js +8 -2
- package/docs/DEVELOPMENT.md +161 -0
- package/docs/DOCKER.md +225 -0
- package/docs/HOW-TO-WRITE-PRDs.md +4 -2
- package/docs/PRD-GENERATOR.md +2 -1
- package/docs/SECURITY.md +78 -0
- package/docs/run-state-machine.md +73 -64
- package/package.json +1 -1
package/dist/commands/init.js
CHANGED
|
@@ -14,118 +14,174 @@ const PROMPT_FILE = "prompt.md";
|
|
|
14
14
|
const PRD_FILE = "prd.json";
|
|
15
15
|
const PROGRESS_FILE = "progress.txt";
|
|
16
16
|
const PRD_GUIDE_FILE = "HOW-TO-WRITE-PRDs.md";
|
|
17
|
-
export async function init(
|
|
17
|
+
export async function init(args) {
|
|
18
18
|
const cwd = process.cwd();
|
|
19
19
|
const ralphDir = join(cwd, RALPH_DIR);
|
|
20
|
+
const useDefaults = args.includes("-y") || args.includes("--yes");
|
|
20
21
|
console.log("Initializing ralph in current directory...\n");
|
|
21
22
|
// Check for existing .ralph directory
|
|
22
23
|
if (existsSync(ralphDir)) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
if (!useDefaults) {
|
|
25
|
+
const reinit = await promptConfirm(".ralph/ directory already exists. Re-initialize?");
|
|
26
|
+
if (!reinit) {
|
|
27
|
+
console.log("Aborted.");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
27
30
|
}
|
|
28
31
|
}
|
|
29
32
|
else {
|
|
30
33
|
mkdirSync(ralphDir, { recursive: true });
|
|
31
34
|
console.log(`Created ${RALPH_DIR}/`);
|
|
32
35
|
}
|
|
33
|
-
// Step 1: Select CLI provider (first)
|
|
34
36
|
const CLI_PROVIDERS = getCliProviders();
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
const selectedProviderName = await promptSelectWithArrows("Select your AI CLI provider:", providerNames);
|
|
38
|
-
const selectedProviderIndex = providerNames.indexOf(selectedProviderName);
|
|
39
|
-
const selectedCliProviderKey = providerKeys[selectedProviderIndex];
|
|
40
|
-
const selectedProvider = CLI_PROVIDERS[selectedCliProviderKey];
|
|
37
|
+
const LANGUAGES = getLanguages();
|
|
38
|
+
let selectedCliProviderKey;
|
|
41
39
|
let cliConfig;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
const customPromptArgs = customPromptArgsInput.trim() ? customPromptArgsInput.trim().split(/\s+/) : [];
|
|
40
|
+
let selectedKey;
|
|
41
|
+
let selectedTechnologies = [];
|
|
42
|
+
let checkCommand;
|
|
43
|
+
let testCommand;
|
|
44
|
+
if (useDefaults) {
|
|
45
|
+
// Use defaults: Claude CLI + Node.js
|
|
46
|
+
selectedCliProviderKey = "claude";
|
|
47
|
+
const provider = CLI_PROVIDERS[selectedCliProviderKey];
|
|
51
48
|
cliConfig = {
|
|
52
|
-
command:
|
|
53
|
-
args:
|
|
54
|
-
yoloArgs:
|
|
55
|
-
promptArgs:
|
|
49
|
+
command: provider.command,
|
|
50
|
+
args: provider.defaultArgs,
|
|
51
|
+
yoloArgs: provider.yoloArgs.length > 0 ? provider.yoloArgs : undefined,
|
|
52
|
+
promptArgs: provider.promptArgs ?? [],
|
|
56
53
|
};
|
|
54
|
+
selectedKey = "node";
|
|
55
|
+
const config = LANGUAGES[selectedKey];
|
|
56
|
+
checkCommand = config.checkCommand;
|
|
57
|
+
testCommand = config.testCommand;
|
|
58
|
+
console.log(`Using defaults: ${CLI_PROVIDERS[selectedCliProviderKey].name} + ${LANGUAGES[selectedKey].name}`);
|
|
57
59
|
}
|
|
58
60
|
else {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
selectedTechnologies = await promptMultiSelectWithArrows("Select your technology stack (optional):", techOptions);
|
|
82
|
-
// Convert display names back to just technology names for predefined options
|
|
83
|
-
selectedTechnologies = selectedTechnologies.map(sel => {
|
|
84
|
-
const idx = techOptions.indexOf(sel);
|
|
85
|
-
return idx >= 0 ? techNames[idx] : sel;
|
|
86
|
-
});
|
|
87
|
-
if (selectedTechnologies.length > 0) {
|
|
88
|
-
console.log(`\nSelected technologies: ${selectedTechnologies.join(", ")}`);
|
|
61
|
+
// Step 1: Select CLI provider (first)
|
|
62
|
+
const providerKeys = Object.keys(CLI_PROVIDERS);
|
|
63
|
+
const providerNames = providerKeys.map(k => `${CLI_PROVIDERS[k].name} - ${CLI_PROVIDERS[k].description}`);
|
|
64
|
+
const selectedProviderName = await promptSelectWithArrows("Select your AI CLI provider:", providerNames);
|
|
65
|
+
const selectedProviderIndex = providerNames.indexOf(selectedProviderName);
|
|
66
|
+
selectedCliProviderKey = providerKeys[selectedProviderIndex];
|
|
67
|
+
const selectedProvider = CLI_PROVIDERS[selectedCliProviderKey];
|
|
68
|
+
// Handle custom CLI provider
|
|
69
|
+
if (selectedCliProviderKey === "custom") {
|
|
70
|
+
const customCommand = await promptInput("\nEnter your CLI command: ");
|
|
71
|
+
const customArgsInput = await promptInput("Enter default arguments (space-separated): ");
|
|
72
|
+
const customArgs = customArgsInput.trim() ? customArgsInput.trim().split(/\s+/) : [];
|
|
73
|
+
const customYoloArgsInput = await promptInput("Enter yolo/auto-approve arguments (space-separated): ");
|
|
74
|
+
const customYoloArgs = customYoloArgsInput.trim() ? customYoloArgsInput.trim().split(/\s+/) : [];
|
|
75
|
+
const customPromptArgsInput = await promptInput("Enter prompt arguments (e.g., -p for flag-based, leave empty for positional): ");
|
|
76
|
+
const customPromptArgs = customPromptArgsInput.trim() ? customPromptArgsInput.trim().split(/\s+/) : [];
|
|
77
|
+
cliConfig = {
|
|
78
|
+
command: customCommand || "claude",
|
|
79
|
+
args: customArgs,
|
|
80
|
+
yoloArgs: customYoloArgs.length > 0 ? customYoloArgs : undefined,
|
|
81
|
+
promptArgs: customPromptArgs,
|
|
82
|
+
};
|
|
89
83
|
}
|
|
90
84
|
else {
|
|
91
|
-
|
|
85
|
+
cliConfig = {
|
|
86
|
+
command: selectedProvider.command,
|
|
87
|
+
args: selectedProvider.defaultArgs,
|
|
88
|
+
yoloArgs: selectedProvider.yoloArgs.length > 0 ? selectedProvider.yoloArgs : undefined,
|
|
89
|
+
promptArgs: selectedProvider.promptArgs ?? [],
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
console.log(`\nSelected CLI provider: ${CLI_PROVIDERS[selectedCliProviderKey].name}`);
|
|
93
|
+
// Step 2: Select language (second)
|
|
94
|
+
const languageKeys = Object.keys(LANGUAGES);
|
|
95
|
+
const languageNames = languageKeys.map(k => `${LANGUAGES[k].name} - ${LANGUAGES[k].description}`);
|
|
96
|
+
const selectedName = await promptSelectWithArrows("Select your project language/runtime:", languageNames);
|
|
97
|
+
const selectedIndex = languageNames.indexOf(selectedName);
|
|
98
|
+
selectedKey = languageKeys[selectedIndex];
|
|
99
|
+
const config = LANGUAGES[selectedKey];
|
|
100
|
+
console.log(`\nSelected language: ${config.name}`);
|
|
101
|
+
// Step 3: Select technology stack if available (third)
|
|
102
|
+
if (config.technologies && config.technologies.length > 0) {
|
|
103
|
+
const techOptions = config.technologies.map(t => `${t.name} - ${t.description}`);
|
|
104
|
+
const techNames = config.technologies.map(t => t.name);
|
|
105
|
+
selectedTechnologies = await promptMultiSelectWithArrows("Select your technology stack (optional):", techOptions);
|
|
106
|
+
// Convert display names back to just technology names for predefined options
|
|
107
|
+
selectedTechnologies = selectedTechnologies.map(sel => {
|
|
108
|
+
const idx = techOptions.indexOf(sel);
|
|
109
|
+
return idx >= 0 ? techNames[idx] : sel;
|
|
110
|
+
});
|
|
111
|
+
if (selectedTechnologies.length > 0) {
|
|
112
|
+
console.log(`\nSelected technologies: ${selectedTechnologies.join(", ")}`);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
console.log("\nNo technologies selected.");
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Allow custom commands for "none" language
|
|
119
|
+
checkCommand = config.checkCommand;
|
|
120
|
+
testCommand = config.testCommand;
|
|
121
|
+
if (selectedKey === "none") {
|
|
122
|
+
checkCommand = await promptInput("\nEnter your type/build check command: ") || checkCommand;
|
|
123
|
+
testCommand = await promptInput("Enter your test command: ") || testCommand;
|
|
92
124
|
}
|
|
93
|
-
}
|
|
94
|
-
// Allow custom commands for "none" language
|
|
95
|
-
let checkCommand = config.checkCommand;
|
|
96
|
-
let testCommand = config.testCommand;
|
|
97
|
-
if (selectedKey === "none") {
|
|
98
|
-
checkCommand = await promptInput("\nEnter your type/build check command: ") || checkCommand;
|
|
99
|
-
testCommand = await promptInput("Enter your test command: ") || testCommand;
|
|
100
125
|
}
|
|
101
126
|
const finalConfig = {
|
|
102
|
-
...
|
|
127
|
+
...LANGUAGES[selectedKey],
|
|
103
128
|
checkCommand,
|
|
104
129
|
testCommand,
|
|
105
130
|
};
|
|
106
131
|
// Generate image name from directory name
|
|
107
132
|
const projectName = basename(cwd).toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
108
133
|
const imageName = `ralph-${projectName}`;
|
|
109
|
-
// Write config file
|
|
134
|
+
// Write config file with all available options (defaults or empty values)
|
|
110
135
|
const configData = {
|
|
136
|
+
// Required fields
|
|
111
137
|
language: selectedKey,
|
|
112
138
|
checkCommand: finalConfig.checkCommand,
|
|
113
139
|
testCommand: finalConfig.testCommand,
|
|
114
140
|
imageName,
|
|
141
|
+
// CLI configuration
|
|
115
142
|
cli: cliConfig,
|
|
116
143
|
cliProvider: selectedCliProviderKey,
|
|
144
|
+
// Optional fields with defaults/empty values for discoverability
|
|
145
|
+
notifyCommand: "",
|
|
146
|
+
technologies: selectedTechnologies.length > 0 ? selectedTechnologies : [],
|
|
147
|
+
javaVersion: selectedKey === "java" ? 21 : null,
|
|
148
|
+
// Docker configuration options
|
|
149
|
+
docker: {
|
|
150
|
+
ports: [],
|
|
151
|
+
volumes: [],
|
|
152
|
+
environment: {},
|
|
153
|
+
git: {
|
|
154
|
+
name: "",
|
|
155
|
+
email: "",
|
|
156
|
+
},
|
|
157
|
+
packages: [],
|
|
158
|
+
buildCommands: {
|
|
159
|
+
root: [],
|
|
160
|
+
node: [],
|
|
161
|
+
},
|
|
162
|
+
startCommand: "",
|
|
163
|
+
asciinema: {
|
|
164
|
+
enabled: false,
|
|
165
|
+
autoRecord: false,
|
|
166
|
+
outputDir: ".recordings",
|
|
167
|
+
},
|
|
168
|
+
firewall: {
|
|
169
|
+
allowedDomains: [],
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
// Claude-specific configuration (MCP servers and skills)
|
|
173
|
+
claude: {
|
|
174
|
+
mcpServers: {},
|
|
175
|
+
skills: [],
|
|
176
|
+
},
|
|
117
177
|
};
|
|
118
|
-
// Add technologies if any were selected
|
|
119
|
-
if (selectedTechnologies.length > 0) {
|
|
120
|
-
configData.technologies = selectedTechnologies;
|
|
121
|
-
}
|
|
122
178
|
const configPath = join(ralphDir, CONFIG_FILE);
|
|
123
179
|
writeFileSync(configPath, JSON.stringify(configData, null, 2) + "\n");
|
|
124
180
|
console.log(`\nCreated ${RALPH_DIR}/${CONFIG_FILE}`);
|
|
125
181
|
// Write prompt file (ask if exists) - uses template with $variables
|
|
126
182
|
const prompt = generatePromptTemplate();
|
|
127
183
|
const promptPath = join(ralphDir, PROMPT_FILE);
|
|
128
|
-
if (existsSync(promptPath)) {
|
|
184
|
+
if (existsSync(promptPath) && !useDefaults) {
|
|
129
185
|
const overwritePrompt = await promptConfirm(`${RALPH_DIR}/${PROMPT_FILE} already exists. Overwrite?`);
|
|
130
186
|
if (overwritePrompt) {
|
|
131
187
|
writeFileSync(promptPath, prompt + "\n");
|
|
@@ -137,7 +193,7 @@ export async function init(_args) {
|
|
|
137
193
|
}
|
|
138
194
|
else {
|
|
139
195
|
writeFileSync(promptPath, prompt + "\n");
|
|
140
|
-
console.log(
|
|
196
|
+
console.log(`${existsSync(promptPath) ? "Updated" : "Created"} ${RALPH_DIR}/${PROMPT_FILE}`);
|
|
141
197
|
}
|
|
142
198
|
// Create PRD if not exists
|
|
143
199
|
const prdPath = join(ralphDir, PRD_FILE);
|
|
@@ -259,7 +259,7 @@
|
|
|
259
259
|
"checkCommand": "swift build",
|
|
260
260
|
"testCommand": "swift test",
|
|
261
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
|
|
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 \\\n SWIFT_PLATFORM=\"ubuntu2204\"; \\\n SWIFT_FILE=\"swift-5.10-RELEASE-ubuntu22.04\"; \\\n else \\\n SWIFT_PLATFORM=\"ubuntu2204-aarch64\"; \\\n SWIFT_FILE=\"swift-5.10-RELEASE-ubuntu22.04-aarch64\"; \\\n fi && \\\n curl -fsSL https://download.swift.org/swift-5.10-release/${SWIFT_PLATFORM}/swift-5.10-RELEASE/${SWIFT_FILE}.tar.gz | tar -xz -C /opt && \\\n mv /opt/${SWIFT_FILE} /opt/swift\nENV PATH=\"/opt/swift/usr/bin:$PATH\""
|
|
263
263
|
},
|
|
264
264
|
"technologies": [
|
|
265
265
|
{ "name": "Vapor", "description": "Server-side Swift web framework" },
|
package/dist/utils/config.d.ts
CHANGED
|
@@ -5,6 +5,22 @@ export interface CliConfig {
|
|
|
5
5
|
promptArgs?: string[];
|
|
6
6
|
modelArgs?: string[];
|
|
7
7
|
}
|
|
8
|
+
export interface McpServerConfig {
|
|
9
|
+
command: string;
|
|
10
|
+
args?: string[];
|
|
11
|
+
env?: Record<string, string>;
|
|
12
|
+
}
|
|
13
|
+
export interface SkillConfig {
|
|
14
|
+
name: string;
|
|
15
|
+
description: string;
|
|
16
|
+
instructions: string;
|
|
17
|
+
userInvocable?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface AsciinemaConfig {
|
|
20
|
+
enabled: boolean;
|
|
21
|
+
autoRecord?: boolean;
|
|
22
|
+
outputDir?: string;
|
|
23
|
+
}
|
|
8
24
|
export interface RalphConfig {
|
|
9
25
|
language: string;
|
|
10
26
|
checkCommand: string;
|
|
@@ -23,6 +39,20 @@ export interface RalphConfig {
|
|
|
23
39
|
name?: string;
|
|
24
40
|
email?: string;
|
|
25
41
|
};
|
|
42
|
+
packages?: string[];
|
|
43
|
+
buildCommands?: {
|
|
44
|
+
root?: string[];
|
|
45
|
+
node?: string[];
|
|
46
|
+
};
|
|
47
|
+
startCommand?: string;
|
|
48
|
+
asciinema?: AsciinemaConfig;
|
|
49
|
+
firewall?: {
|
|
50
|
+
allowedDomains?: string[];
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
claude?: {
|
|
54
|
+
mcpServers?: Record<string, McpServerConfig>;
|
|
55
|
+
skills?: SkillConfig[];
|
|
26
56
|
};
|
|
27
57
|
}
|
|
28
58
|
export declare const DEFAULT_CLI_CONFIG: CliConfig;
|
package/dist/utils/prompt.d.ts
CHANGED
|
@@ -5,6 +5,6 @@ export declare function createPrompt(): {
|
|
|
5
5
|
export declare function promptSelectWithArrows(message: string, options: string[]): Promise<string>;
|
|
6
6
|
export declare function promptInput(message: string): Promise<string>;
|
|
7
7
|
export declare function promptSelect(message: string, options: string[]): Promise<string>;
|
|
8
|
-
export declare function promptConfirm(message: string): Promise<boolean>;
|
|
8
|
+
export declare function promptConfirm(message: string, defaultValue?: boolean): Promise<boolean>;
|
|
9
9
|
export declare function promptMultiSelect(message: string, options: string[]): Promise<string[]>;
|
|
10
10
|
export declare function promptMultiSelectWithArrows(message: string, options: string[]): Promise<string[]>;
|
package/dist/utils/prompt.js
CHANGED
|
@@ -96,11 +96,17 @@ export async function promptSelect(message, options) {
|
|
|
96
96
|
console.log("Invalid selection.");
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
-
export async function promptConfirm(message) {
|
|
99
|
+
export async function promptConfirm(message, defaultValue = true) {
|
|
100
100
|
const prompt = createPrompt();
|
|
101
|
+
const hint = defaultValue ? "(Y/n)" : "(y/N)";
|
|
101
102
|
while (true) {
|
|
102
|
-
const answer = await prompt.question(`${message}
|
|
103
|
+
const answer = await prompt.question(`${message} ${hint}: `);
|
|
103
104
|
const normalized = answer.trim().toLowerCase();
|
|
105
|
+
// Empty input returns default
|
|
106
|
+
if (normalized === "") {
|
|
107
|
+
prompt.close();
|
|
108
|
+
return defaultValue;
|
|
109
|
+
}
|
|
104
110
|
if (normalized === "y" || normalized === "yes") {
|
|
105
111
|
prompt.close();
|
|
106
112
|
return true;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# Development
|
|
2
|
+
|
|
3
|
+
Guide for contributing to ralph-cli-sandboxed.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Clone the repository
|
|
9
|
+
git clone https://github.com/choas/ralph-cli-sandboxed
|
|
10
|
+
cd ralph-cli-sandboxed
|
|
11
|
+
|
|
12
|
+
# Install dependencies
|
|
13
|
+
npm install
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Development Mode
|
|
17
|
+
|
|
18
|
+
Run ralph directly from TypeScript source without building:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm run dev -- <args>
|
|
22
|
+
|
|
23
|
+
# Examples:
|
|
24
|
+
npm run dev -- --version
|
|
25
|
+
npm run dev -- list
|
|
26
|
+
npm run dev -- once
|
|
27
|
+
npm run dev -- help
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
This uses `tsx` to run TypeScript directly, allowing you to test changes immediately.
|
|
31
|
+
|
|
32
|
+
## Building
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# Build for distribution
|
|
36
|
+
npm run build
|
|
37
|
+
|
|
38
|
+
# This runs:
|
|
39
|
+
# 1. tsc - Compiles TypeScript to dist/
|
|
40
|
+
# 2. Copies config files to dist/config/
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Project Structure
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
ralph-cli-sandboxed/
|
|
47
|
+
├── src/
|
|
48
|
+
│ ├── index.ts # CLI entry point
|
|
49
|
+
│ ├── commands/ # Command implementations
|
|
50
|
+
│ │ ├── init.ts # ralph init
|
|
51
|
+
│ │ ├── run.ts # ralph run
|
|
52
|
+
│ │ ├── once.ts # ralph once
|
|
53
|
+
│ │ ├── prd.ts # PRD management commands
|
|
54
|
+
│ │ ├── docker.ts # Docker commands
|
|
55
|
+
│ │ ├── prompt.ts # ralph prompt
|
|
56
|
+
│ │ ├── fix-prd.ts # ralph fix-prd
|
|
57
|
+
│ │ └── help.ts # ralph help
|
|
58
|
+
│ ├── utils/
|
|
59
|
+
│ │ ├── config.ts # Configuration loading
|
|
60
|
+
│ │ ├── prd-validator.ts # PRD validation and recovery
|
|
61
|
+
│ │ └── prompt.ts # Interactive prompts
|
|
62
|
+
│ ├── templates/
|
|
63
|
+
│ │ └── prompts.ts # Prompt template generation
|
|
64
|
+
│ └── config/
|
|
65
|
+
│ ├── languages.json # Language configurations
|
|
66
|
+
│ └── cli-providers.json # CLI provider configurations
|
|
67
|
+
├── docs/ # Documentation
|
|
68
|
+
├── dist/ # Compiled output (generated)
|
|
69
|
+
└── package.json
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Adding a New Language
|
|
73
|
+
|
|
74
|
+
Edit `src/config/languages.json`:
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"languages": {
|
|
79
|
+
"your-language": {
|
|
80
|
+
"name": "Your Language",
|
|
81
|
+
"description": "Description here",
|
|
82
|
+
"checkCommand": "your-check-command",
|
|
83
|
+
"testCommand": "your-test-command",
|
|
84
|
+
"docker": {
|
|
85
|
+
"install": "# Installation commands for Dockerfile"
|
|
86
|
+
},
|
|
87
|
+
"technologies": [
|
|
88
|
+
{ "name": "Framework", "description": "Description" }
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Adding a New CLI Provider
|
|
96
|
+
|
|
97
|
+
Edit `src/config/cli-providers.json`:
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"providers": {
|
|
102
|
+
"your-cli": {
|
|
103
|
+
"name": "Your CLI",
|
|
104
|
+
"description": "Description",
|
|
105
|
+
"command": "cli-command",
|
|
106
|
+
"defaultArgs": [],
|
|
107
|
+
"yoloArgs": ["--auto-approve-flag"],
|
|
108
|
+
"promptArgs": ["--prompt"],
|
|
109
|
+
"docker": {
|
|
110
|
+
"install": "# Installation commands"
|
|
111
|
+
},
|
|
112
|
+
"envVars": ["YOUR_API_KEY"],
|
|
113
|
+
"modelArgs": ["--model"]
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Testing Changes
|
|
120
|
+
|
|
121
|
+
Since ralph automates AI agents, testing requires caution:
|
|
122
|
+
|
|
123
|
+
1. **Use a test project** - Create a sample project to test changes
|
|
124
|
+
2. **Use `ralph once`** - Run single iterations for testing
|
|
125
|
+
3. **Check output** - Review `.ralph/progress.txt` and git commits
|
|
126
|
+
|
|
127
|
+
## Platform-Specific Dependencies
|
|
128
|
+
|
|
129
|
+
The `node_modules` folder contains platform-specific binaries. If you switch between environments:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# When switching between host and container
|
|
133
|
+
rm -rf node_modules && npm install
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Or use a separate volume for container node_modules:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
docker run -v $(pwd):/workspace -v /workspace/node_modules your-image
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Code Style
|
|
143
|
+
|
|
144
|
+
- TypeScript with ES2022 target
|
|
145
|
+
- Node.js 18+ required
|
|
146
|
+
- Use async/await for asynchronous operations
|
|
147
|
+
- Keep functions focused and small
|
|
148
|
+
|
|
149
|
+
## Submitting Changes
|
|
150
|
+
|
|
151
|
+
1. Fork the repository
|
|
152
|
+
2. Create a feature branch
|
|
153
|
+
3. Make your changes
|
|
154
|
+
4. Test thoroughly
|
|
155
|
+
5. Submit a pull request
|
|
156
|
+
|
|
157
|
+
## Requirements
|
|
158
|
+
|
|
159
|
+
- Node.js 18+
|
|
160
|
+
- npm
|
|
161
|
+
- Docker (for testing container functionality)
|