ralph-cli-sandboxed 0.3.0 → 0.4.0
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/action.d.ts +7 -0
- package/dist/commands/action.js +276 -0
- package/dist/commands/chat.js +95 -7
- package/dist/commands/config.js +6 -18
- package/dist/commands/fix-config.d.ts +4 -0
- package/dist/commands/fix-config.js +388 -0
- package/dist/commands/help.js +17 -0
- package/dist/commands/init.js +89 -2
- package/dist/commands/listen.js +50 -9
- package/dist/commands/prd.js +2 -2
- package/dist/config/languages.json +4 -0
- package/dist/index.js +4 -0
- package/dist/providers/telegram.d.ts +6 -2
- package/dist/providers/telegram.js +68 -2
- package/dist/templates/macos-scripts.d.ts +42 -0
- package/dist/templates/macos-scripts.js +448 -0
- package/dist/tui/ConfigEditor.d.ts +7 -0
- package/dist/tui/ConfigEditor.js +313 -0
- package/dist/tui/components/ArrayEditor.d.ts +22 -0
- package/dist/tui/components/ArrayEditor.js +193 -0
- package/dist/tui/components/BooleanToggle.d.ts +19 -0
- package/dist/tui/components/BooleanToggle.js +43 -0
- package/dist/tui/components/EditorPanel.d.ts +50 -0
- package/dist/tui/components/EditorPanel.js +232 -0
- package/dist/tui/components/HelpPanel.d.ts +13 -0
- package/dist/tui/components/HelpPanel.js +69 -0
- package/dist/tui/components/JsonSnippetEditor.d.ts +24 -0
- package/dist/tui/components/JsonSnippetEditor.js +380 -0
- package/dist/tui/components/KeyValueEditor.d.ts +34 -0
- package/dist/tui/components/KeyValueEditor.js +261 -0
- package/dist/tui/components/ObjectEditor.d.ts +23 -0
- package/dist/tui/components/ObjectEditor.js +227 -0
- package/dist/tui/components/PresetSelector.d.ts +23 -0
- package/dist/tui/components/PresetSelector.js +58 -0
- package/dist/tui/components/Preview.d.ts +18 -0
- package/dist/tui/components/Preview.js +190 -0
- package/dist/tui/components/ScrollableContainer.d.ts +38 -0
- package/dist/tui/components/ScrollableContainer.js +77 -0
- package/dist/tui/components/SectionNav.d.ts +31 -0
- package/dist/tui/components/SectionNav.js +130 -0
- package/dist/tui/components/StringEditor.d.ts +21 -0
- package/dist/tui/components/StringEditor.js +29 -0
- package/dist/tui/hooks/useConfig.d.ts +16 -0
- package/dist/tui/hooks/useConfig.js +89 -0
- package/dist/tui/hooks/useTerminalSize.d.ts +21 -0
- package/dist/tui/hooks/useTerminalSize.js +48 -0
- package/dist/tui/utils/presets.d.ts +52 -0
- package/dist/tui/utils/presets.js +191 -0
- package/dist/tui/utils/validation.d.ts +49 -0
- package/dist/tui/utils/validation.js +198 -0
- package/dist/utils/chat-client.d.ts +31 -1
- package/dist/utils/chat-client.js +27 -1
- package/dist/utils/config.d.ts +7 -2
- package/docs/MACOS-DEVELOPMENT.md +435 -0
- package/package.json +1 -1
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execute an action from config.json - works both inside and outside containers.
|
|
3
|
+
*
|
|
4
|
+
* Inside container: Uses file-based message queue to communicate with host daemon
|
|
5
|
+
* Outside container: Executes the command directly
|
|
6
|
+
*/
|
|
7
|
+
export declare function action(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { spawn } from "child_process";
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
|
+
import { loadConfig, isRunningInContainer } from "../utils/config.js";
|
|
4
|
+
import { getMessagesPath, sendMessage, waitForResponse, } from "../utils/message-queue.js";
|
|
5
|
+
/**
|
|
6
|
+
* Execute an action from config.json - works both inside and outside containers.
|
|
7
|
+
*
|
|
8
|
+
* Inside container: Uses file-based message queue to communicate with host daemon
|
|
9
|
+
* Outside container: Executes the command directly
|
|
10
|
+
*/
|
|
11
|
+
export async function action(args) {
|
|
12
|
+
// Parse arguments
|
|
13
|
+
let actionName;
|
|
14
|
+
let actionArgs = [];
|
|
15
|
+
let debug = false;
|
|
16
|
+
let showList = false;
|
|
17
|
+
for (let i = 0; i < args.length; i++) {
|
|
18
|
+
const arg = args[i];
|
|
19
|
+
if (arg === "--debug" || arg === "-d") {
|
|
20
|
+
debug = true;
|
|
21
|
+
}
|
|
22
|
+
else if (arg === "--help" || arg === "-h") {
|
|
23
|
+
showHelp();
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
else if (arg === "--list" || arg === "-l") {
|
|
27
|
+
showList = true;
|
|
28
|
+
}
|
|
29
|
+
else if (!arg.startsWith("-")) {
|
|
30
|
+
if (!actionName) {
|
|
31
|
+
actionName = arg;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
// Collect remaining args as action arguments
|
|
35
|
+
actionArgs = args.slice(i);
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// Load config
|
|
41
|
+
let config;
|
|
42
|
+
try {
|
|
43
|
+
config = loadConfig();
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
console.error("Failed to load config. Run 'ralph init' first.");
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
// Get available actions from daemon config
|
|
50
|
+
const daemonActions = config.daemon?.actions || {};
|
|
51
|
+
const actionNames = Object.keys(daemonActions);
|
|
52
|
+
// If --list or no action specified, show available actions
|
|
53
|
+
if (showList || !actionName) {
|
|
54
|
+
if (actionNames.length === 0) {
|
|
55
|
+
console.log("No actions configured.");
|
|
56
|
+
console.log("");
|
|
57
|
+
console.log("Configure actions in .ralph/config.json:");
|
|
58
|
+
console.log(' {');
|
|
59
|
+
console.log(' "daemon": {');
|
|
60
|
+
console.log(' "actions": {');
|
|
61
|
+
console.log(' "build": {');
|
|
62
|
+
console.log(' "command": "./scripts/build.sh",');
|
|
63
|
+
console.log(' "description": "Build the project"');
|
|
64
|
+
console.log(' }');
|
|
65
|
+
console.log(' }');
|
|
66
|
+
console.log(' }');
|
|
67
|
+
console.log(' }');
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
console.log("Available actions:");
|
|
71
|
+
console.log("");
|
|
72
|
+
for (const [name, actionConfig] of Object.entries(daemonActions)) {
|
|
73
|
+
const desc = actionConfig.description || actionConfig.command;
|
|
74
|
+
console.log(` ${name.padEnd(20)} ${desc}`);
|
|
75
|
+
}
|
|
76
|
+
console.log("");
|
|
77
|
+
console.log("Run an action: ralph action <name> [args...]");
|
|
78
|
+
}
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// Validate action exists
|
|
82
|
+
if (!daemonActions[actionName]) {
|
|
83
|
+
console.error(`Unknown action: ${actionName}`);
|
|
84
|
+
console.error("");
|
|
85
|
+
if (actionNames.length > 0) {
|
|
86
|
+
console.error(`Available actions: ${actionNames.join(", ")}`);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
console.error("No actions configured in .ralph/config.json");
|
|
90
|
+
}
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
const actionConfig = daemonActions[actionName];
|
|
94
|
+
const inContainer = isRunningInContainer();
|
|
95
|
+
if (debug) {
|
|
96
|
+
console.log(`[action] Name: ${actionName}`);
|
|
97
|
+
console.log(`[action] Args: ${actionArgs.join(" ") || "(none)"}`);
|
|
98
|
+
console.log(`[action] Command: ${actionConfig.command}`);
|
|
99
|
+
console.log(`[action] In container: ${inContainer}`);
|
|
100
|
+
}
|
|
101
|
+
if (inContainer) {
|
|
102
|
+
// Inside container - use file-based message queue to execute on host
|
|
103
|
+
await executeViaQueue(actionName, actionArgs, debug);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
// Outside container - execute directly
|
|
107
|
+
await executeDirectly(actionConfig.command, actionArgs, debug);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Execute action via message queue (when running inside container).
|
|
112
|
+
*/
|
|
113
|
+
async function executeViaQueue(actionName, args, debug) {
|
|
114
|
+
const messagesPath = getMessagesPath(true);
|
|
115
|
+
if (!existsSync(messagesPath)) {
|
|
116
|
+
const ralphDir = "/workspace/.ralph";
|
|
117
|
+
if (!existsSync(ralphDir)) {
|
|
118
|
+
console.error("Error: .ralph directory not mounted in container.");
|
|
119
|
+
console.error("Make sure the container is started with 'ralph docker run'.");
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Send message via file queue
|
|
124
|
+
const messageId = sendMessage(messagesPath, "sandbox", actionName, args.length > 0 ? args : undefined);
|
|
125
|
+
if (debug) {
|
|
126
|
+
console.log(`[action] Sent message: ${messageId}`);
|
|
127
|
+
}
|
|
128
|
+
console.log(`Executing action: ${actionName}`);
|
|
129
|
+
console.log("Waiting for daemon response...");
|
|
130
|
+
// Wait for response with longer timeout for actions that may take time
|
|
131
|
+
const response = await waitForResponse(messagesPath, messageId, 60000);
|
|
132
|
+
if (!response) {
|
|
133
|
+
console.error("No response from daemon (timeout).");
|
|
134
|
+
console.error("");
|
|
135
|
+
console.error("Make sure the daemon is running on the host:");
|
|
136
|
+
console.error(" ralph daemon start");
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
if (debug) {
|
|
140
|
+
console.log(`[action] Response: ${JSON.stringify(response)}`);
|
|
141
|
+
}
|
|
142
|
+
// Display output
|
|
143
|
+
if (response.output) {
|
|
144
|
+
console.log("");
|
|
145
|
+
console.log(response.output);
|
|
146
|
+
}
|
|
147
|
+
if (response.success) {
|
|
148
|
+
console.log("");
|
|
149
|
+
console.log(`Action '${actionName}' completed successfully.`);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
console.error("");
|
|
153
|
+
console.error(`Action '${actionName}' failed: ${response.error || "Unknown error"}`);
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Execute action directly on host (when running outside container).
|
|
159
|
+
*/
|
|
160
|
+
async function executeDirectly(command, args, debug) {
|
|
161
|
+
return new Promise((resolve, reject) => {
|
|
162
|
+
// Build full command with arguments
|
|
163
|
+
let fullCommand = command;
|
|
164
|
+
if (args.length > 0) {
|
|
165
|
+
fullCommand = `${command} ${args.map(a => `"${a.replace(/"/g, '\\"')}"`).join(" ")}`;
|
|
166
|
+
}
|
|
167
|
+
if (debug) {
|
|
168
|
+
console.log(`[action] Executing: ${fullCommand}`);
|
|
169
|
+
}
|
|
170
|
+
console.log(`Executing: ${fullCommand}`);
|
|
171
|
+
console.log("");
|
|
172
|
+
const proc = spawn(fullCommand, [], {
|
|
173
|
+
stdio: ["inherit", "pipe", "pipe"],
|
|
174
|
+
shell: true,
|
|
175
|
+
cwd: process.cwd(),
|
|
176
|
+
});
|
|
177
|
+
// Stream stdout in real-time
|
|
178
|
+
proc.stdout.on("data", (data) => {
|
|
179
|
+
process.stdout.write(data);
|
|
180
|
+
});
|
|
181
|
+
// Stream stderr in real-time
|
|
182
|
+
proc.stderr.on("data", (data) => {
|
|
183
|
+
process.stderr.write(data);
|
|
184
|
+
});
|
|
185
|
+
proc.on("close", (code) => {
|
|
186
|
+
if (code === 0) {
|
|
187
|
+
console.log("");
|
|
188
|
+
console.log("Action completed successfully.");
|
|
189
|
+
resolve();
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
console.error("");
|
|
193
|
+
console.error(`Action failed with exit code: ${code}`);
|
|
194
|
+
process.exit(code || 1);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
proc.on("error", (err) => {
|
|
198
|
+
console.error(`Failed to execute action: ${err.message}`);
|
|
199
|
+
reject(err);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
function showHelp() {
|
|
204
|
+
console.log(`
|
|
205
|
+
ralph action - Execute host actions from config.json
|
|
206
|
+
|
|
207
|
+
USAGE:
|
|
208
|
+
ralph action [name] [args...] Execute an action
|
|
209
|
+
ralph action --list List available actions
|
|
210
|
+
ralph action --help Show this help
|
|
211
|
+
|
|
212
|
+
OPTIONS:
|
|
213
|
+
-l, --list List all configured actions
|
|
214
|
+
-d, --debug Show debug output
|
|
215
|
+
-h, --help Show this help message
|
|
216
|
+
|
|
217
|
+
DESCRIPTION:
|
|
218
|
+
This command executes actions defined in the daemon.actions section of
|
|
219
|
+
.ralph/config.json. When running inside a container, the action is
|
|
220
|
+
executed on the host via the daemon. When running on the host directly,
|
|
221
|
+
the action is executed locally.
|
|
222
|
+
|
|
223
|
+
Actions are useful for triggering host operations like:
|
|
224
|
+
- Building Xcode projects (requires host Xcode installation)
|
|
225
|
+
- Running deployment scripts
|
|
226
|
+
- Executing platform-specific tools not available in the container
|
|
227
|
+
|
|
228
|
+
CONFIGURATION:
|
|
229
|
+
Define actions in .ralph/config.json:
|
|
230
|
+
|
|
231
|
+
{
|
|
232
|
+
"daemon": {
|
|
233
|
+
"actions": {
|
|
234
|
+
"build": {
|
|
235
|
+
"command": "./scripts/build.sh",
|
|
236
|
+
"description": "Build the project"
|
|
237
|
+
},
|
|
238
|
+
"deploy": {
|
|
239
|
+
"command": "./scripts/deploy.sh --env staging",
|
|
240
|
+
"description": "Deploy to staging"
|
|
241
|
+
},
|
|
242
|
+
"gen_xcode": {
|
|
243
|
+
"command": "swift package generate-xcodeproj",
|
|
244
|
+
"description": "Generate Xcode project from Swift package"
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
EXAMPLES:
|
|
251
|
+
# List available actions
|
|
252
|
+
ralph action --list
|
|
253
|
+
ralph action
|
|
254
|
+
|
|
255
|
+
# Execute an action
|
|
256
|
+
ralph action build
|
|
257
|
+
ralph action deploy --env production
|
|
258
|
+
ralph action gen_xcode
|
|
259
|
+
|
|
260
|
+
# Execute with arguments
|
|
261
|
+
ralph action build --release
|
|
262
|
+
ralph action deploy staging
|
|
263
|
+
|
|
264
|
+
SETUP FOR CONTAINER USAGE:
|
|
265
|
+
1. Define actions in .ralph/config.json
|
|
266
|
+
2. Start the daemon on the host: ralph daemon start
|
|
267
|
+
3. Run the container: ralph docker run
|
|
268
|
+
4. From inside the container: ralph action build
|
|
269
|
+
|
|
270
|
+
NOTES:
|
|
271
|
+
- Actions configured in daemon.actions are automatically available to the daemon
|
|
272
|
+
- When in a container, the daemon must be running to process action requests
|
|
273
|
+
- Actions have a 60-second timeout when executed via the daemon
|
|
274
|
+
- Output is streamed in real-time when executing directly on host
|
|
275
|
+
`);
|
|
276
|
+
}
|
package/dist/commands/chat.js
CHANGED
|
@@ -7,7 +7,7 @@ import { join, basename } from "path";
|
|
|
7
7
|
import { spawn } from "child_process";
|
|
8
8
|
import { loadConfig, getRalphDir, isRunningInContainer } from "../utils/config.js";
|
|
9
9
|
import { createTelegramClient } from "../providers/telegram.js";
|
|
10
|
-
import { generateProjectId, formatStatusMessage, } from "../utils/chat-client.js";
|
|
10
|
+
import { generateProjectId, formatStatusMessage, formatStatusForChat, } from "../utils/chat-client.js";
|
|
11
11
|
import { getMessagesPath, sendMessage, waitForResponse, } from "../utils/message-queue.js";
|
|
12
12
|
const CHAT_STATE_FILE = "chat-state.json";
|
|
13
13
|
/**
|
|
@@ -74,6 +74,35 @@ function getPrdStatus() {
|
|
|
74
74
|
return { complete: 0, total: 0, incomplete: 0 };
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Get open (incomplete) categories from the PRD.
|
|
79
|
+
* Returns unique categories that have at least one incomplete task.
|
|
80
|
+
*/
|
|
81
|
+
function getOpenCategories() {
|
|
82
|
+
const ralphDir = getRalphDir();
|
|
83
|
+
const prdPath = join(ralphDir, "prd.json");
|
|
84
|
+
if (!existsSync(prdPath)) {
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
const content = readFileSync(prdPath, "utf-8");
|
|
89
|
+
const items = JSON.parse(content);
|
|
90
|
+
if (!Array.isArray(items)) {
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
// Get unique categories that have incomplete tasks
|
|
94
|
+
const openCategories = new Set();
|
|
95
|
+
for (const item of items) {
|
|
96
|
+
if (item.passes !== true && item.category) {
|
|
97
|
+
openCategories.add(item.category);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return Array.from(openCategories);
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
}
|
|
77
106
|
/**
|
|
78
107
|
* Add a new task to the PRD.
|
|
79
108
|
*/
|
|
@@ -163,18 +192,22 @@ async function handleCommand(command, client, config, state, debug) {
|
|
|
163
192
|
}
|
|
164
193
|
switch (cmd) {
|
|
165
194
|
case "run": {
|
|
195
|
+
// Check for optional category filter
|
|
196
|
+
const category = args.length > 0 ? args[0] : undefined;
|
|
166
197
|
// Check PRD status first (from host)
|
|
167
198
|
const prdStatus = getPrdStatus();
|
|
168
199
|
if (prdStatus.incomplete === 0) {
|
|
169
200
|
await client.sendMessage(chatId, `${state.projectName}: All tasks already complete (${prdStatus.complete}/${prdStatus.total})`);
|
|
170
201
|
return;
|
|
171
202
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
203
|
+
const categoryInfo = category ? ` (category: ${category})` : "";
|
|
204
|
+
await client.sendMessage(chatId, `${state.projectName}: Starting ralph run${categoryInfo} (${prdStatus.incomplete} tasks remaining)...`);
|
|
205
|
+
// Send run command to sandbox with optional category argument
|
|
206
|
+
const runArgs = category ? [category] : [];
|
|
207
|
+
const response = await sendToSandbox("run", runArgs, debug, 10000);
|
|
175
208
|
if (response) {
|
|
176
209
|
if (response.success) {
|
|
177
|
-
await client.sendMessage(chatId, `${state.projectName}: Ralph run started in sandbox`);
|
|
210
|
+
await client.sendMessage(chatId, `${state.projectName}: Ralph run started in sandbox${categoryInfo}`);
|
|
178
211
|
}
|
|
179
212
|
else {
|
|
180
213
|
await client.sendMessage(chatId, `${state.projectName}: Failed to start: ${response.error}`);
|
|
@@ -190,16 +223,32 @@ async function handleCommand(command, client, config, state, debug) {
|
|
|
190
223
|
case "status": {
|
|
191
224
|
// Try sandbox first, fall back to host
|
|
192
225
|
const response = await sendToSandbox("status", [], debug, 5000);
|
|
226
|
+
let statusMessage;
|
|
193
227
|
if (response?.success && response.output) {
|
|
194
|
-
|
|
228
|
+
// Strip ANSI codes and progress bar for clean chat output
|
|
229
|
+
const cleanedOutput = formatStatusForChat(response.output);
|
|
230
|
+
statusMessage = `${state.projectName}:\n${cleanedOutput}`;
|
|
195
231
|
}
|
|
196
232
|
else {
|
|
197
233
|
// Fall back to host status
|
|
198
234
|
const prdStatus = getPrdStatus();
|
|
199
235
|
const status = prdStatus.incomplete === 0 ? "completed" : "idle";
|
|
200
236
|
const details = `Progress: ${prdStatus.complete}/${prdStatus.total} tasks complete`;
|
|
201
|
-
|
|
237
|
+
statusMessage = formatStatusMessage(state.projectName, status, details);
|
|
238
|
+
}
|
|
239
|
+
// Get open categories and create inline buttons (max 4)
|
|
240
|
+
const openCategories = getOpenCategories();
|
|
241
|
+
let inlineKeyboard;
|
|
242
|
+
if (openCategories.length > 0 && openCategories.length <= 4) {
|
|
243
|
+
// Create a row of buttons, one per category
|
|
244
|
+
inlineKeyboard = [
|
|
245
|
+
openCategories.map((category) => ({
|
|
246
|
+
text: `▶ Run ${category}`,
|
|
247
|
+
callbackData: `/run ${category}`,
|
|
248
|
+
})),
|
|
249
|
+
];
|
|
202
250
|
}
|
|
251
|
+
await client.sendMessage(chatId, statusMessage, { inlineKeyboard });
|
|
203
252
|
break;
|
|
204
253
|
}
|
|
205
254
|
case "add": {
|
|
@@ -295,6 +344,41 @@ async function handleCommand(command, client, config, state, debug) {
|
|
|
295
344
|
}
|
|
296
345
|
break;
|
|
297
346
|
}
|
|
347
|
+
case "claude": {
|
|
348
|
+
if (args.length === 0) {
|
|
349
|
+
await client.sendMessage(chatId, `${state.projectName}: Usage: /claude [prompt]`);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
const prompt = args.join(" ");
|
|
353
|
+
await client.sendMessage(chatId, `⏳ ${state.projectName}: Running Claude Code...\n(this may take a few minutes)`);
|
|
354
|
+
// Send claude command to sandbox with longer timeout (5 minutes)
|
|
355
|
+
const response = await sendToSandbox("claude", args, debug, 300000);
|
|
356
|
+
if (response) {
|
|
357
|
+
let output = response.output || response.error || "(no output)";
|
|
358
|
+
// Truncate long output
|
|
359
|
+
if (output.length > 2000) {
|
|
360
|
+
output = output.substring(0, 2000) + "\n...(truncated)";
|
|
361
|
+
}
|
|
362
|
+
if (response.success) {
|
|
363
|
+
await client.sendMessage(chatId, `✅ ${state.projectName}: Claude Code DONE\n\n${output}`);
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
// Check for version mismatch (sandbox has old version without /claude support)
|
|
367
|
+
if (response.error?.includes("Unknown action: claude")) {
|
|
368
|
+
await client.sendMessage(chatId, `❌ ${state.projectName}: Claude Code failed - sandbox needs update.\n` +
|
|
369
|
+
`The sandbox listener doesn't support /claude. Rebuild your Docker container:\n` +
|
|
370
|
+
` ralph docker build --no-cache`);
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
await client.sendMessage(chatId, `❌ ${state.projectName}: Claude Code FAILED\n\n${output}`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
await client.sendMessage(chatId, `❌ ${state.projectName}: No response from sandbox. Is 'ralph listen' running?`);
|
|
379
|
+
}
|
|
380
|
+
break;
|
|
381
|
+
}
|
|
298
382
|
case "help": {
|
|
299
383
|
const helpText = `
|
|
300
384
|
/run - Start automation
|
|
@@ -302,6 +386,7 @@ async function handleCommand(command, client, config, state, debug) {
|
|
|
302
386
|
/add [desc] - Add task
|
|
303
387
|
/exec [cmd] - Shell command
|
|
304
388
|
/action [name] - Run action
|
|
389
|
+
/claude [prompt] - Run Claude Code
|
|
305
390
|
/help - This help
|
|
306
391
|
`.trim();
|
|
307
392
|
await client.sendMessage(chatId, helpText);
|
|
@@ -365,6 +450,7 @@ async function startChat(config, debug) {
|
|
|
365
450
|
console.log(" /add ... - Add new task to PRD");
|
|
366
451
|
console.log(" /exec ... - Execute shell command");
|
|
367
452
|
console.log(" /action ... - Run daemon action");
|
|
453
|
+
console.log(" /claude ... - Run Claude Code with prompt (YOLO mode)");
|
|
368
454
|
console.log(" /help - Show help");
|
|
369
455
|
console.log("");
|
|
370
456
|
console.log("Press Ctrl+C to stop the daemon.");
|
|
@@ -538,6 +624,7 @@ CHAT COMMANDS:
|
|
|
538
624
|
/add [desc] - Add new task to PRD
|
|
539
625
|
/exec [cmd] - Execute shell command
|
|
540
626
|
/action [name] - Run daemon action (e.g., /action build)
|
|
627
|
+
/claude [prompt] - Run Claude Code with prompt in YOLO mode
|
|
541
628
|
/stop - Stop running ralph process
|
|
542
629
|
/help - Show help
|
|
543
630
|
|
|
@@ -580,6 +667,7 @@ EXAMPLES:
|
|
|
580
667
|
/exec npm test # Run npm test
|
|
581
668
|
/action build # Run build action
|
|
582
669
|
/action deploy # Run deploy action
|
|
670
|
+
/claude Fix the login bug # Run Claude Code with prompt
|
|
583
671
|
`);
|
|
584
672
|
return;
|
|
585
673
|
}
|
package/dist/commands/config.js
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
2
|
-
import { render
|
|
3
|
-
import {
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { render } from "ink";
|
|
3
|
+
import { getPaths } from "../utils/config.js";
|
|
4
4
|
import { existsSync } from "fs";
|
|
5
|
-
|
|
6
|
-
function ConfigEditorApp({ config }) {
|
|
7
|
-
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { color: "cyan", bold: true, children: "ralph config" }), _jsx(Text, { children: "TUI Config Editor (work in progress)" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "Language: " }), _jsx(Text, { children: config.language })] }), _jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: "Check: " }), _jsx(Text, { children: config.checkCommand })] }), _jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: "Test: " }), _jsx(Text, { children: config.testCommand })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Press Ctrl+C to exit" }) })] }));
|
|
8
|
-
}
|
|
5
|
+
import { ConfigEditor } from "../tui/ConfigEditor.js";
|
|
9
6
|
export async function config(args) {
|
|
10
7
|
const subcommand = args[0];
|
|
11
8
|
if (subcommand === "help" || subcommand === "--help" || subcommand === "-h") {
|
|
@@ -18,17 +15,8 @@ export async function config(args) {
|
|
|
18
15
|
console.error("Error: .ralph/config.json not found. Run 'ralph init' first.");
|
|
19
16
|
process.exit(1);
|
|
20
17
|
}
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
try {
|
|
24
|
-
configData = loadConfig();
|
|
25
|
-
}
|
|
26
|
-
catch (error) {
|
|
27
|
-
console.error("Error loading config:", error instanceof Error ? error.message : "Unknown error");
|
|
28
|
-
process.exit(1);
|
|
29
|
-
}
|
|
30
|
-
// Render Ink app
|
|
31
|
-
const { waitUntilExit } = render(_jsx(ConfigEditorApp, { config: configData }));
|
|
18
|
+
// Render Ink app with ConfigEditor
|
|
19
|
+
const { waitUntilExit } = render(_jsx(ConfigEditor, {}));
|
|
32
20
|
await waitUntilExit();
|
|
33
21
|
}
|
|
34
22
|
function showConfigHelp() {
|