ralph-cli-sandboxed 0.4.0 → 0.4.2

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.
Files changed (80) hide show
  1. package/README.md +30 -0
  2. package/dist/commands/action.js +47 -20
  3. package/dist/commands/chat.d.ts +1 -1
  4. package/dist/commands/chat.js +325 -62
  5. package/dist/commands/config.js +2 -1
  6. package/dist/commands/daemon.d.ts +2 -5
  7. package/dist/commands/daemon.js +118 -49
  8. package/dist/commands/docker.js +110 -73
  9. package/dist/commands/fix-config.js +2 -1
  10. package/dist/commands/fix-prd.js +2 -2
  11. package/dist/commands/help.js +19 -3
  12. package/dist/commands/init.js +78 -17
  13. package/dist/commands/listen.js +116 -5
  14. package/dist/commands/logo.d.ts +5 -0
  15. package/dist/commands/logo.js +41 -0
  16. package/dist/commands/notify.js +1 -1
  17. package/dist/commands/once.js +19 -9
  18. package/dist/commands/prd.js +20 -2
  19. package/dist/commands/run.js +111 -27
  20. package/dist/commands/slack.d.ts +10 -0
  21. package/dist/commands/slack.js +333 -0
  22. package/dist/config/responder-presets.json +69 -0
  23. package/dist/index.js +6 -1
  24. package/dist/providers/discord.d.ts +82 -0
  25. package/dist/providers/discord.js +697 -0
  26. package/dist/providers/slack.d.ts +79 -0
  27. package/dist/providers/slack.js +715 -0
  28. package/dist/providers/telegram.d.ts +30 -0
  29. package/dist/providers/telegram.js +190 -7
  30. package/dist/responders/claude-code-responder.d.ts +48 -0
  31. package/dist/responders/claude-code-responder.js +203 -0
  32. package/dist/responders/cli-responder.d.ts +62 -0
  33. package/dist/responders/cli-responder.js +298 -0
  34. package/dist/responders/llm-responder.d.ts +135 -0
  35. package/dist/responders/llm-responder.js +582 -0
  36. package/dist/templates/macos-scripts.js +2 -4
  37. package/dist/templates/prompts.js +4 -2
  38. package/dist/tui/ConfigEditor.js +42 -5
  39. package/dist/tui/components/ArrayEditor.js +1 -1
  40. package/dist/tui/components/EditorPanel.js +10 -6
  41. package/dist/tui/components/HelpPanel.d.ts +1 -1
  42. package/dist/tui/components/HelpPanel.js +1 -1
  43. package/dist/tui/components/JsonSnippetEditor.js +8 -5
  44. package/dist/tui/components/KeyValueEditor.js +69 -5
  45. package/dist/tui/components/LLMProvidersEditor.d.ts +22 -0
  46. package/dist/tui/components/LLMProvidersEditor.js +357 -0
  47. package/dist/tui/components/ObjectEditor.js +1 -1
  48. package/dist/tui/components/Preview.js +1 -1
  49. package/dist/tui/components/RespondersEditor.d.ts +22 -0
  50. package/dist/tui/components/RespondersEditor.js +437 -0
  51. package/dist/tui/components/SectionNav.js +27 -3
  52. package/dist/tui/utils/presets.js +15 -2
  53. package/dist/utils/chat-client.d.ts +33 -4
  54. package/dist/utils/chat-client.js +20 -1
  55. package/dist/utils/config.d.ts +100 -1
  56. package/dist/utils/config.js +78 -1
  57. package/dist/utils/daemon-actions.d.ts +19 -0
  58. package/dist/utils/daemon-actions.js +111 -0
  59. package/dist/utils/daemon-client.d.ts +21 -0
  60. package/dist/utils/daemon-client.js +28 -1
  61. package/dist/utils/llm-client.d.ts +82 -0
  62. package/dist/utils/llm-client.js +185 -0
  63. package/dist/utils/message-queue.js +6 -6
  64. package/dist/utils/notification.d.ts +10 -2
  65. package/dist/utils/notification.js +111 -4
  66. package/dist/utils/prd-validator.js +60 -19
  67. package/dist/utils/prompt.js +22 -12
  68. package/dist/utils/responder-logger.d.ts +47 -0
  69. package/dist/utils/responder-logger.js +129 -0
  70. package/dist/utils/responder-presets.d.ts +92 -0
  71. package/dist/utils/responder-presets.js +156 -0
  72. package/dist/utils/responder.d.ts +88 -0
  73. package/dist/utils/responder.js +207 -0
  74. package/dist/utils/stream-json.js +6 -6
  75. package/docs/CHAT-CLIENTS.md +520 -0
  76. package/docs/CHAT-RESPONDERS.md +785 -0
  77. package/docs/DEVELOPMENT.md +25 -0
  78. package/docs/USEFUL_ACTIONS.md +815 -0
  79. package/docs/chat-architecture.md +251 -0
  80. package/package.json +14 -1
@@ -2,9 +2,16 @@ import { existsSync, watch } from "fs";
2
2
  import { spawn } from "child_process";
3
3
  import { loadConfig, getRalphDir, isRunningInContainer } from "../utils/config.js";
4
4
  import { getMessagesPath, readMessages, getPendingMessages, respondToMessage, cleanupOldMessages, initializeMessages, } from "../utils/message-queue.js";
5
+ import { getDefaultActions } from "../utils/daemon-actions.js";
5
6
  // Telegram client for sending messages (lazy loaded)
6
7
  let telegramClient = null;
7
8
  let telegramConfig = null;
9
+ // Slack client for sending messages (lazy loaded)
10
+ let slackClient = null;
11
+ let slackConfig = null;
12
+ // Discord client for sending messages (lazy loaded)
13
+ let discordClient = null;
14
+ let discordConfig = null;
8
15
  /**
9
16
  * Check if Telegram is enabled (has token and not explicitly disabled).
10
17
  */
@@ -15,6 +22,31 @@ function isTelegramEnabled(config) {
15
22
  return false;
16
23
  return true;
17
24
  }
25
+ /**
26
+ * Check if Slack is enabled (has required tokens and not explicitly disabled).
27
+ */
28
+ function isSlackEnabled(config) {
29
+ if (!config.chat?.slack?.botToken)
30
+ return false;
31
+ if (!config.chat?.slack?.appToken)
32
+ return false;
33
+ if (!config.chat?.slack?.signingSecret)
34
+ return false;
35
+ if (config.chat.slack.enabled === false)
36
+ return false;
37
+ return true;
38
+ }
39
+ /**
40
+ * Check if Discord is enabled (has token and not explicitly disabled).
41
+ */
42
+ function isDiscordEnabled(config) {
43
+ if (!config.chat?.discord?.botToken)
44
+ return false;
45
+ if (config.chat.discord.enabled === false)
46
+ return false;
47
+ return true;
48
+ }
49
+ // Note: getDefaultActions moved to utils/daemon-actions.ts for sharing with action.ts
18
50
  /**
19
51
  * Initialize Telegram client if configured.
20
52
  */
@@ -26,6 +58,28 @@ async function initTelegramClient(config) {
26
58
  telegramClient = createTelegramClient(telegramConfig, false);
27
59
  }
28
60
  }
61
+ /**
62
+ * Initialize Slack client if configured.
63
+ */
64
+ async function initSlackClient(config) {
65
+ if (isSlackEnabled(config)) {
66
+ slackConfig = config.chat.slack;
67
+ // Dynamic import to avoid circular dependency
68
+ const { createSlackClient } = await import("../providers/slack.js");
69
+ slackClient = createSlackClient(slackConfig, false);
70
+ }
71
+ }
72
+ /**
73
+ * Initialize Discord client if configured.
74
+ */
75
+ async function initDiscordClient(config) {
76
+ if (isDiscordEnabled(config)) {
77
+ discordConfig = config.chat.discord;
78
+ // Dynamic import to avoid circular dependency
79
+ const { createDiscordClient } = await import("../providers/discord.js");
80
+ discordClient = createDiscordClient(discordConfig, false);
81
+ }
82
+ }
29
83
  /**
30
84
  * Send a message via Telegram if configured.
31
85
  */
@@ -49,56 +103,48 @@ async function sendTelegramMessage(message) {
49
103
  }
50
104
  }
51
105
  /**
52
- * Default actions available to the sandbox.
106
+ * Send a message via Slack if configured.
53
107
  */
54
- function getDefaultActions(config) {
55
- const actions = {
56
- ping: {
57
- command: "echo pong",
58
- description: "Health check - responds with 'pong'",
59
- },
60
- };
61
- // Add notify action based on notifications config
62
- if (config.notifications?.provider === "ntfy" && config.notifications.ntfy?.topic) {
63
- const server = config.notifications.ntfy.server || "https://ntfy.sh";
64
- const topic = config.notifications.ntfy.topic;
65
- actions.notify = {
66
- command: "curl", // Placeholder - ntfyUrl triggers special handling
67
- description: `Send notification via ntfy to ${topic}`,
68
- ntfyUrl: `${server}/${topic}`,
69
- };
108
+ async function sendSlackMessage(message) {
109
+ if (!slackClient || !slackConfig) {
110
+ return { success: false, error: "Slack not configured" };
70
111
  }
71
- else if (config.notifications?.provider === "command" && config.notifications.command) {
72
- actions.notify = {
73
- command: config.notifications.command,
74
- description: "Send notification to host",
75
- };
112
+ try {
113
+ // Send to all allowed channel IDs, or fail if none configured
114
+ const channelIds = slackConfig.allowedChannelIds;
115
+ if (!channelIds || channelIds.length === 0) {
116
+ return { success: false, error: "No channel IDs configured for Slack" };
117
+ }
118
+ for (const channelId of channelIds) {
119
+ await slackClient.sendMessage(channelId, message);
120
+ }
121
+ return { success: true };
76
122
  }
77
- else if (config.notifyCommand) {
78
- // Fallback to deprecated notifyCommand
79
- actions.notify = {
80
- command: config.notifyCommand,
81
- description: "Send notification to host",
82
- };
123
+ catch (err) {
124
+ return { success: false, error: err instanceof Error ? err.message : "Unknown error" };
83
125
  }
84
- // Add telegram_notify action if Telegram is enabled
85
- if (isTelegramEnabled(config)) {
86
- actions.telegram_notify = {
87
- command: "__telegram__", // Special marker for Telegram handling
88
- description: "Send notification via Telegram",
89
- };
126
+ }
127
+ /**
128
+ * Send a message via Discord if configured.
129
+ */
130
+ async function sendDiscordMessage(message) {
131
+ if (!discordClient || !discordConfig) {
132
+ return { success: false, error: "Discord not configured" };
133
+ }
134
+ try {
135
+ // Send to all allowed channel IDs, or fail if none configured
136
+ const channelIds = discordConfig.allowedChannelIds;
137
+ if (!channelIds || channelIds.length === 0) {
138
+ return { success: false, error: "No channel IDs configured for Discord" };
139
+ }
140
+ for (const channelId of channelIds) {
141
+ await discordClient.sendMessage(channelId, message);
142
+ }
143
+ return { success: true };
144
+ }
145
+ catch (err) {
146
+ return { success: false, error: err instanceof Error ? err.message : "Unknown error" };
90
147
  }
91
- // Add chat_status action for querying PRD status from container
92
- actions.chat_status = {
93
- command: "ralph prd status --json 2>/dev/null || echo '{}'",
94
- description: "Get PRD status as JSON",
95
- };
96
- // Add chat_add action for adding PRD tasks from container
97
- actions.chat_add = {
98
- command: "ralph add",
99
- description: "Add a new task to the PRD",
100
- };
101
- return actions;
102
148
  }
103
149
  /**
104
150
  * Execute an action command with arguments.
@@ -116,6 +162,26 @@ async function executeAction(action, args = []) {
116
162
  error: result.error,
117
163
  };
118
164
  }
165
+ // Special handling for Slack
166
+ if (action.command === "__slack__") {
167
+ const message = args.join(" ") || "Ralph notification";
168
+ const result = await sendSlackMessage(message);
169
+ return {
170
+ success: result.success,
171
+ output: result.success ? "Sent to Slack" : "",
172
+ error: result.error,
173
+ };
174
+ }
175
+ // Special handling for Discord
176
+ if (action.command === "__discord__") {
177
+ const message = args.join(" ") || "Ralph notification";
178
+ const result = await sendDiscordMessage(message);
179
+ return {
180
+ success: result.success,
181
+ output: result.success ? "Sent to Discord" : "",
182
+ error: result.error,
183
+ };
184
+ }
119
185
  return new Promise((resolve) => {
120
186
  let fullCommand;
121
187
  const message = args.join(" ") || "";
@@ -132,9 +198,10 @@ async function executeAction(action, args = []) {
132
198
  else {
133
199
  // Command can use $RALPH_MESSAGE env var
134
200
  // Also pass args for backwards compatibility
135
- fullCommand = args.length > 0
136
- ? `${action.command} ${args.map(a => `"${a.replace(/"/g, '\\"')}"`).join(" ")}`
137
- : action.command;
201
+ fullCommand =
202
+ args.length > 0
203
+ ? `${action.command} ${args.map((a) => `"${a.replace(/"/g, '\\"')}"`).join(" ")}`
204
+ : action.command;
138
205
  }
139
206
  const proc = spawn(fullCommand, [], {
140
207
  stdio: ["ignore", "pipe", "pipe"],
@@ -203,8 +270,10 @@ async function startDaemon(debug) {
203
270
  }
204
271
  const config = loadConfig();
205
272
  const daemonConfig = config.daemon || {};
206
- // Initialize Telegram client if configured
273
+ // Initialize chat clients if configured
207
274
  await initTelegramClient(config);
275
+ await initSlackClient(config);
276
+ await initDiscordClient(config);
208
277
  // Merge default and configured actions
209
278
  const defaultActions = getDefaultActions(config);
210
279
  const configuredActions = daemonConfig.actions || {};