ralph-cli-sandboxed 0.6.0 → 0.6.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/chat.js +320 -2
- package/dist/commands/docker.js +22 -18
- package/dist/providers/slack.js +1 -1
- package/dist/utils/chat-client.js +1 -0
- package/package.json +1 -1
package/dist/commands/chat.js
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { existsSync, readFileSync, writeFileSync, watch } from "fs";
|
|
6
6
|
import { join, basename, extname } from "path";
|
|
7
|
-
import { spawn } from "child_process";
|
|
7
|
+
import { execSync, spawn } from "child_process";
|
|
8
8
|
import YAML from "yaml";
|
|
9
|
-
import { loadConfig, getRalphDir, isRunningInContainer, getPrdFiles } from "../utils/config.js";
|
|
9
|
+
import { loadConfig, getRalphDir, isRunningInContainer, getPrdFiles, loadBranchState, getProjectName as getConfigProjectName } from "../utils/config.js";
|
|
10
10
|
import { createTelegramClient } from "../providers/telegram.js";
|
|
11
11
|
import { createSlackClient } from "../providers/slack.js";
|
|
12
12
|
import { createDiscordClient } from "../providers/discord.js";
|
|
@@ -163,6 +163,290 @@ function addPrdTask(description) {
|
|
|
163
163
|
return false;
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
|
+
/**
|
|
167
|
+
* Get the current git branch in the project directory.
|
|
168
|
+
*/
|
|
169
|
+
function getBaseBranch() {
|
|
170
|
+
try {
|
|
171
|
+
return execSync("git rev-parse --abbrev-ref HEAD", {
|
|
172
|
+
encoding: "utf-8",
|
|
173
|
+
cwd: process.cwd(),
|
|
174
|
+
}).trim();
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return "main";
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Check if a git branch exists.
|
|
182
|
+
*/
|
|
183
|
+
function branchExists(branch) {
|
|
184
|
+
try {
|
|
185
|
+
execSync(`git rev-parse --verify "${branch}"`, {
|
|
186
|
+
stdio: "pipe",
|
|
187
|
+
cwd: process.cwd(),
|
|
188
|
+
});
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Convert branch name to worktree directory name.
|
|
197
|
+
*/
|
|
198
|
+
function branchToWorktreeName(branch) {
|
|
199
|
+
const projectName = getConfigProjectName();
|
|
200
|
+
return `${projectName}_${branch.replace(/\//g, "-")}`;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Handle /branch list — show branches from PRD grouped by branch field.
|
|
204
|
+
*/
|
|
205
|
+
async function handleBranchList(chatId, client, state) {
|
|
206
|
+
const prdFiles = getPrdFiles();
|
|
207
|
+
if (prdFiles.none || !prdFiles.primary) {
|
|
208
|
+
await client.sendMessage(chatId, `${state.projectName}: No PRD file found.`);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
const content = readFileSync(prdFiles.primary, "utf-8");
|
|
212
|
+
const items = parsePrdContent(prdFiles.primary, content);
|
|
213
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
214
|
+
await client.sendMessage(chatId, `${state.projectName}: No PRD items found.`);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const activeBranch = loadBranchState();
|
|
218
|
+
// Group items by branch
|
|
219
|
+
const branchGroups = new Map();
|
|
220
|
+
const noBranchItems = [];
|
|
221
|
+
for (const item of items) {
|
|
222
|
+
if (item.branch) {
|
|
223
|
+
const group = branchGroups.get(item.branch) || [];
|
|
224
|
+
group.push(item);
|
|
225
|
+
branchGroups.set(item.branch, group);
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
noBranchItems.push(item);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (branchGroups.size === 0 && noBranchItems.length === 0) {
|
|
232
|
+
await client.sendMessage(chatId, `${state.projectName}: No PRD items found.`);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const lines = [`${state.projectName}: Branches\n`];
|
|
236
|
+
const sortedBranches = [...branchGroups.keys()].sort();
|
|
237
|
+
for (const branchName of sortedBranches) {
|
|
238
|
+
const branchItems = branchGroups.get(branchName);
|
|
239
|
+
const passing = branchItems.filter((e) => e.passes === true).length;
|
|
240
|
+
const total = branchItems.length;
|
|
241
|
+
const allPassing = passing === total;
|
|
242
|
+
const isActive = activeBranch?.currentBranch === branchName;
|
|
243
|
+
const icon = allPassing ? "[OK]" : "[ ]";
|
|
244
|
+
const active = isActive ? " << active" : "";
|
|
245
|
+
lines.push(` ${icon} ${branchName} ${passing}/${total}${active}`);
|
|
246
|
+
}
|
|
247
|
+
if (noBranchItems.length > 0) {
|
|
248
|
+
const passing = noBranchItems.filter((e) => e.passes === true).length;
|
|
249
|
+
const total = noBranchItems.length;
|
|
250
|
+
const icon = passing === total ? "[OK]" : "[ ]";
|
|
251
|
+
lines.push(` ${icon} (no branch) ${passing}/${total}`);
|
|
252
|
+
}
|
|
253
|
+
await client.sendMessage(chatId, lines.join("\n"));
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Handle /branch pr <name> — add a PRD item to create a pull request.
|
|
257
|
+
*/
|
|
258
|
+
async function handleBranchPr(args, chatId, client, state) {
|
|
259
|
+
const branchName = args[0];
|
|
260
|
+
if (!branchName) {
|
|
261
|
+
const usage = client.provider === "slack"
|
|
262
|
+
? "/ralph branch pr <branch-name>"
|
|
263
|
+
: "/branch pr <branch-name>";
|
|
264
|
+
await client.sendMessage(chatId, `${state.projectName}: Usage: ${usage}`);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
if (!branchExists(branchName)) {
|
|
268
|
+
await client.sendMessage(chatId, `${state.projectName}: Branch "${branchName}" does not exist.`);
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
const baseBranch = getBaseBranch();
|
|
272
|
+
const prdFiles = getPrdFiles();
|
|
273
|
+
if (prdFiles.none || !prdFiles.primary) {
|
|
274
|
+
await client.sendMessage(chatId, `${state.projectName}: No PRD file found.`);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
const content = readFileSync(prdFiles.primary, "utf-8");
|
|
278
|
+
const items = parsePrdContent(prdFiles.primary, content);
|
|
279
|
+
if (!Array.isArray(items)) {
|
|
280
|
+
await client.sendMessage(chatId, `${state.projectName}: Failed to parse PRD file.`);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
items.push({
|
|
284
|
+
category: "feature",
|
|
285
|
+
description: `Create a pull request from \`${branchName}\` into \`${baseBranch}\``,
|
|
286
|
+
steps: [
|
|
287
|
+
`Ensure all changes on \`${branchName}\` are committed`,
|
|
288
|
+
`Push \`${branchName}\` to the remote if not already pushed`,
|
|
289
|
+
`Create a pull request from \`${branchName}\` into \`${baseBranch}\` using the appropriate tool (e.g. gh pr create)`,
|
|
290
|
+
"Include a descriptive title and summary of the changes in the PR",
|
|
291
|
+
],
|
|
292
|
+
passes: false,
|
|
293
|
+
branch: branchName,
|
|
294
|
+
});
|
|
295
|
+
const ext = extname(prdFiles.primary).toLowerCase();
|
|
296
|
+
if (ext === ".yaml" || ext === ".yml") {
|
|
297
|
+
writeFileSync(prdFiles.primary, YAML.stringify(items));
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
writeFileSync(prdFiles.primary, JSON.stringify(items, null, 2) + "\n");
|
|
301
|
+
}
|
|
302
|
+
await client.sendMessage(chatId, `${state.projectName}: Added PRD entry: Create PR for ${branchName} -> ${baseBranch}`);
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Handle /branch merge <name> — merge branch into base branch.
|
|
306
|
+
* Skips confirmation (user explicitly typed the command in chat).
|
|
307
|
+
*/
|
|
308
|
+
async function handleBranchMerge(args, chatId, client, state) {
|
|
309
|
+
const branchName = args[0];
|
|
310
|
+
if (!branchName) {
|
|
311
|
+
const usage = client.provider === "slack"
|
|
312
|
+
? "/ralph branch merge <branch-name>"
|
|
313
|
+
: "/branch merge <branch-name>";
|
|
314
|
+
await client.sendMessage(chatId, `${state.projectName}: Usage: ${usage}`);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
if (!branchExists(branchName)) {
|
|
318
|
+
await client.sendMessage(chatId, `${state.projectName}: Branch "${branchName}" does not exist.`);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
const baseBranch = getBaseBranch();
|
|
322
|
+
const cwd = process.cwd();
|
|
323
|
+
try {
|
|
324
|
+
execSync(`git merge "${branchName}" --no-edit`, { stdio: "pipe", cwd });
|
|
325
|
+
await client.sendMessage(chatId, `${state.projectName}: Merged "${branchName}" into "${baseBranch}".`);
|
|
326
|
+
}
|
|
327
|
+
catch {
|
|
328
|
+
// Check for merge conflicts
|
|
329
|
+
let conflictingFiles = [];
|
|
330
|
+
try {
|
|
331
|
+
const status = execSync("git status --porcelain", { encoding: "utf-8", cwd });
|
|
332
|
+
conflictingFiles = status
|
|
333
|
+
.split("\n")
|
|
334
|
+
.filter((line) => line.startsWith("UU") || line.startsWith("AA") || line.startsWith("DD") ||
|
|
335
|
+
line.startsWith("AU") || line.startsWith("UA") || line.startsWith("DU") ||
|
|
336
|
+
line.startsWith("UD"))
|
|
337
|
+
.map((line) => line.substring(3).trim());
|
|
338
|
+
}
|
|
339
|
+
catch {
|
|
340
|
+
// Ignore status errors
|
|
341
|
+
}
|
|
342
|
+
if (conflictingFiles.length > 0) {
|
|
343
|
+
// Abort the merge
|
|
344
|
+
try {
|
|
345
|
+
execSync("git merge --abort", { stdio: "pipe", cwd });
|
|
346
|
+
}
|
|
347
|
+
catch {
|
|
348
|
+
// Ignore abort errors
|
|
349
|
+
}
|
|
350
|
+
await client.sendMessage(chatId, `${state.projectName}: Merge conflict! Conflicting files:\n${conflictingFiles.join("\n")}\nMerge aborted.`);
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
try {
|
|
354
|
+
execSync("git merge --abort", { stdio: "pipe", cwd });
|
|
355
|
+
}
|
|
356
|
+
catch {
|
|
357
|
+
// Ignore
|
|
358
|
+
}
|
|
359
|
+
await client.sendMessage(chatId, `${state.projectName}: Merge of "${branchName}" failed. Merge aborted.`);
|
|
360
|
+
}
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
// Clean up worktree if it exists
|
|
364
|
+
const dirName = branchToWorktreeName(branchName);
|
|
365
|
+
const config = loadConfig();
|
|
366
|
+
const worktreesPath = config.docker?.worktreesPath;
|
|
367
|
+
if (worktreesPath) {
|
|
368
|
+
const worktreePath = join(worktreesPath, dirName);
|
|
369
|
+
if (existsSync(worktreePath)) {
|
|
370
|
+
try {
|
|
371
|
+
execSync(`git worktree remove "${worktreePath}"`, { stdio: "pipe", cwd });
|
|
372
|
+
}
|
|
373
|
+
catch {
|
|
374
|
+
// Non-critical, ignore
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Handle /branch delete <name> — delete branch, worktree, and untag PRD items.
|
|
381
|
+
* Skips confirmation (user explicitly typed the command in chat).
|
|
382
|
+
*/
|
|
383
|
+
async function handleBranchDelete(args, chatId, client, state) {
|
|
384
|
+
const branchName = args[0];
|
|
385
|
+
if (!branchName) {
|
|
386
|
+
const usage = client.provider === "slack"
|
|
387
|
+
? "/ralph branch delete <branch-name>"
|
|
388
|
+
: "/branch delete <branch-name>";
|
|
389
|
+
await client.sendMessage(chatId, `${state.projectName}: Usage: ${usage}`);
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
if (!branchExists(branchName)) {
|
|
393
|
+
await client.sendMessage(chatId, `${state.projectName}: Branch "${branchName}" does not exist.`);
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
const cwd = process.cwd();
|
|
397
|
+
const results = [];
|
|
398
|
+
// Step 1: Remove worktree if it exists
|
|
399
|
+
const dirName = branchToWorktreeName(branchName);
|
|
400
|
+
const config = loadConfig();
|
|
401
|
+
const worktreesPath = config.docker?.worktreesPath;
|
|
402
|
+
if (worktreesPath) {
|
|
403
|
+
const worktreePath = join(worktreesPath, dirName);
|
|
404
|
+
if (existsSync(worktreePath)) {
|
|
405
|
+
try {
|
|
406
|
+
execSync(`git worktree remove "${worktreePath}" --force`, { stdio: "pipe", cwd });
|
|
407
|
+
results.push("Worktree removed.");
|
|
408
|
+
}
|
|
409
|
+
catch {
|
|
410
|
+
results.push("Warning: Could not remove worktree.");
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
// Step 2: Delete the git branch
|
|
415
|
+
try {
|
|
416
|
+
execSync(`git branch -D "${branchName}"`, { stdio: "pipe", cwd });
|
|
417
|
+
results.push("Branch deleted.");
|
|
418
|
+
}
|
|
419
|
+
catch {
|
|
420
|
+
results.push("Warning: Could not delete git branch.");
|
|
421
|
+
}
|
|
422
|
+
// Step 3: Remove branch tag from PRD items
|
|
423
|
+
const prdFiles = getPrdFiles();
|
|
424
|
+
if (!prdFiles.none && prdFiles.primary) {
|
|
425
|
+
const content = readFileSync(prdFiles.primary, "utf-8");
|
|
426
|
+
const items = parsePrdContent(prdFiles.primary, content);
|
|
427
|
+
if (Array.isArray(items)) {
|
|
428
|
+
const taggedCount = items.filter((e) => e.branch === branchName).length;
|
|
429
|
+
if (taggedCount > 0) {
|
|
430
|
+
const updatedItems = items.map((item) => {
|
|
431
|
+
if (item.branch === branchName) {
|
|
432
|
+
const { branch: _, ...rest } = item;
|
|
433
|
+
return rest;
|
|
434
|
+
}
|
|
435
|
+
return item;
|
|
436
|
+
});
|
|
437
|
+
const ext = extname(prdFiles.primary).toLowerCase();
|
|
438
|
+
if (ext === ".yaml" || ext === ".yml") {
|
|
439
|
+
writeFileSync(prdFiles.primary, YAML.stringify(updatedItems));
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
442
|
+
writeFileSync(prdFiles.primary, JSON.stringify(updatedItems, null, 2) + "\n");
|
|
443
|
+
}
|
|
444
|
+
results.push(`${taggedCount} PRD item(s) untagged.`);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
await client.sendMessage(chatId, `${state.projectName}: Deleted "${branchName}". ${results.join(" ")}`);
|
|
449
|
+
}
|
|
166
450
|
/**
|
|
167
451
|
* Execute a shell command and return the output.
|
|
168
452
|
*/
|
|
@@ -428,6 +712,37 @@ async function handleCommand(command, client, config, state, debug) {
|
|
|
428
712
|
}
|
|
429
713
|
break;
|
|
430
714
|
}
|
|
715
|
+
case "branch": {
|
|
716
|
+
const subCmd = args[0]?.toLowerCase();
|
|
717
|
+
const branchArgs = args.slice(1);
|
|
718
|
+
switch (subCmd) {
|
|
719
|
+
case "list":
|
|
720
|
+
await handleBranchList(chatId, client, state);
|
|
721
|
+
break;
|
|
722
|
+
case "pr":
|
|
723
|
+
await handleBranchPr(branchArgs, chatId, client, state);
|
|
724
|
+
break;
|
|
725
|
+
case "merge":
|
|
726
|
+
await handleBranchMerge(branchArgs, chatId, client, state);
|
|
727
|
+
break;
|
|
728
|
+
case "delete":
|
|
729
|
+
await handleBranchDelete(branchArgs, chatId, client, state);
|
|
730
|
+
break;
|
|
731
|
+
default: {
|
|
732
|
+
const usage = client.provider === "slack"
|
|
733
|
+
? `/ralph branch list - List branches
|
|
734
|
+
/ralph branch pr <name> - Add PRD item to create PR
|
|
735
|
+
/ralph branch merge <name> - Merge branch into base
|
|
736
|
+
/ralph branch delete <name> - Delete branch and worktree`
|
|
737
|
+
: `/branch list - List branches
|
|
738
|
+
/branch pr <name> - Add PRD item to create PR
|
|
739
|
+
/branch merge <name> - Merge branch into base
|
|
740
|
+
/branch delete <name> - Delete branch and worktree`;
|
|
741
|
+
await client.sendMessage(chatId, `${state.projectName}: ${usage}`);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
break;
|
|
745
|
+
}
|
|
431
746
|
case "help": {
|
|
432
747
|
const isSlack = client.provider === "slack";
|
|
433
748
|
const helpText = isSlack
|
|
@@ -438,6 +753,7 @@ async function handleCommand(command, client, config, state, debug) {
|
|
|
438
753
|
/ralph add [desc] - Add task
|
|
439
754
|
/ralph exec [cmd] - Shell command
|
|
440
755
|
/ralph action [name] - Run action
|
|
756
|
+
/ralph branch ... - Manage branches
|
|
441
757
|
/ralph <prompt> - Run Claude Code`
|
|
442
758
|
: `/help - This help
|
|
443
759
|
/status - PRD progress
|
|
@@ -446,6 +762,7 @@ async function handleCommand(command, client, config, state, debug) {
|
|
|
446
762
|
/add [desc] - Add task
|
|
447
763
|
/exec [cmd] - Shell command
|
|
448
764
|
/action [name] - Run action
|
|
765
|
+
/branch ... - Manage branches
|
|
449
766
|
/claude [prompt] - Run Claude Code`;
|
|
450
767
|
await client.sendMessage(chatId, helpText);
|
|
451
768
|
break;
|
|
@@ -640,6 +957,7 @@ async function startChat(config, debug) {
|
|
|
640
957
|
console.log(" /ralph add ... - Add new task to PRD");
|
|
641
958
|
console.log(" /ralph exec ... - Execute shell command");
|
|
642
959
|
console.log(" /ralph action ... - Run daemon action");
|
|
960
|
+
console.log(" /ralph branch ... - Manage branches");
|
|
643
961
|
console.log(" /ralph <prompt> - Run Claude Code with prompt");
|
|
644
962
|
}
|
|
645
963
|
else {
|
package/dist/commands/docker.js
CHANGED
|
@@ -187,26 +187,30 @@ RUN sh -c "$(wget -O- https://github.com/deluan/zsh-in-docker/releases/download/
|
|
|
187
187
|
-a "export HISTFILE=/commandhistory/.zsh_history" \\
|
|
188
188
|
-a 'alias ll="ls -la"'
|
|
189
189
|
|
|
190
|
-
#
|
|
190
|
+
# Copy oh-my-zsh config to node user (after oh-my-zsh to avoid override)
|
|
191
191
|
RUN cp -r /root/.oh-my-zsh /home/node/.oh-my-zsh && chown -R node:node /home/node/.oh-my-zsh && \\
|
|
192
192
|
cp /root/.zshrc /home/node/.zshrc && chown node:node /home/node/.zshrc && \\
|
|
193
|
-
sed -i 's|/root/.oh-my-zsh|/home/node/.oh-my-zsh|g' /home/node/.zshrc
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
193
|
+
sed -i 's|/root/.oh-my-zsh|/home/node/.oh-my-zsh|g' /home/node/.zshrc
|
|
194
|
+
|
|
195
|
+
# Set custom prompt and Ralph ASCII art banner
|
|
196
|
+
RUN cat >> /home/node/.zshrc <<'RALPH_BANNER'
|
|
197
|
+
PROMPT="%K{yellow}%F{black}[ralph]%f%k%K{yellow}%F{black}%d%f%k\\$ "
|
|
198
|
+
|
|
199
|
+
# Ralph ASCII art banner
|
|
200
|
+
if [ -z "$RALPH_BANNER_SHOWN" ]; then
|
|
201
|
+
export RALPH_BANNER_SHOWN=1
|
|
202
|
+
echo ""
|
|
203
|
+
echo "\\033[38;2;255;245;157m██████╗ █████╗ ██╗ ██████╗ ██╗ ██╗ ██████╗██╗ ██╗\\033[0m"
|
|
204
|
+
echo "\\033[38;2;255;238;88m██╔══██╗██╔══██╗██║ ██╔══██╗██║ ██║ ██╔════╝██║ ██║\\033[0m"
|
|
205
|
+
echo "\\033[38;2;255;235;59m██████╔╝███████║██║ ██████╔╝███████║ ██║ ██║ ██║ sandboxed\\033[0m"
|
|
206
|
+
echo "\\033[38;2;253;216;53m██╔══██╗██╔══██║██║ ██╔═══╝ ██╔══██║ ██║ ██║ ██║\\033[0m"
|
|
207
|
+
echo "\\033[38;2;251;192;45m██║ ██║██║ ██║███████╗██║ ██║ ██║ ╚██████╗███████╗██║\\033[0m"
|
|
208
|
+
echo "\\033[38;2;249;168;37m╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝╚══════╝╚═╝\\033[0m"
|
|
209
|
+
RALPH_VERSION=$(ralph --version 2>/dev/null | head -1 || echo "unknown")
|
|
210
|
+
echo "\\033[38;5;248mv$RALPH_VERSION\\033[0m"
|
|
211
|
+
echo ""
|
|
212
|
+
fi
|
|
213
|
+
RALPH_BANNER
|
|
210
214
|
|
|
211
215
|
${cliSnippet}
|
|
212
216
|
|
package/dist/providers/slack.js
CHANGED
|
@@ -475,7 +475,7 @@ export class SlackChatClient {
|
|
|
475
475
|
// Handle the unified /ralph command
|
|
476
476
|
// Subcommands: help, status, run, stop, add, exec, action
|
|
477
477
|
// Anything else is treated as a prompt for Claude
|
|
478
|
-
const knownSubcommands = ["help", "status", "run", "stop", "add", "exec", "action"];
|
|
478
|
+
const knownSubcommands = ["help", "status", "run", "stop", "add", "exec", "action", "branch"];
|
|
479
479
|
if (this.debug) {
|
|
480
480
|
console.log(`[slack] Registering command: /ralph`);
|
|
481
481
|
}
|