ralph-cli-sandboxed 0.2.9 → 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.
Files changed (73) hide show
  1. package/README.md +99 -15
  2. package/dist/commands/action.d.ts +7 -0
  3. package/dist/commands/action.js +276 -0
  4. package/dist/commands/chat.d.ts +8 -0
  5. package/dist/commands/chat.js +701 -0
  6. package/dist/commands/config.d.ts +1 -0
  7. package/dist/commands/config.js +51 -0
  8. package/dist/commands/daemon.d.ts +23 -0
  9. package/dist/commands/daemon.js +422 -0
  10. package/dist/commands/docker.js +82 -4
  11. package/dist/commands/fix-config.d.ts +4 -0
  12. package/dist/commands/fix-config.js +388 -0
  13. package/dist/commands/help.js +80 -0
  14. package/dist/commands/init.js +135 -1
  15. package/dist/commands/listen.d.ts +8 -0
  16. package/dist/commands/listen.js +280 -0
  17. package/dist/commands/notify.d.ts +7 -0
  18. package/dist/commands/notify.js +165 -0
  19. package/dist/commands/once.js +8 -8
  20. package/dist/commands/prd.js +2 -2
  21. package/dist/commands/run.js +25 -12
  22. package/dist/config/languages.json +4 -0
  23. package/dist/index.js +14 -0
  24. package/dist/providers/telegram.d.ts +39 -0
  25. package/dist/providers/telegram.js +256 -0
  26. package/dist/templates/macos-scripts.d.ts +42 -0
  27. package/dist/templates/macos-scripts.js +448 -0
  28. package/dist/tui/ConfigEditor.d.ts +7 -0
  29. package/dist/tui/ConfigEditor.js +313 -0
  30. package/dist/tui/components/ArrayEditor.d.ts +22 -0
  31. package/dist/tui/components/ArrayEditor.js +193 -0
  32. package/dist/tui/components/BooleanToggle.d.ts +19 -0
  33. package/dist/tui/components/BooleanToggle.js +43 -0
  34. package/dist/tui/components/EditorPanel.d.ts +50 -0
  35. package/dist/tui/components/EditorPanel.js +232 -0
  36. package/dist/tui/components/HelpPanel.d.ts +13 -0
  37. package/dist/tui/components/HelpPanel.js +69 -0
  38. package/dist/tui/components/JsonSnippetEditor.d.ts +24 -0
  39. package/dist/tui/components/JsonSnippetEditor.js +380 -0
  40. package/dist/tui/components/KeyValueEditor.d.ts +34 -0
  41. package/dist/tui/components/KeyValueEditor.js +261 -0
  42. package/dist/tui/components/ObjectEditor.d.ts +23 -0
  43. package/dist/tui/components/ObjectEditor.js +227 -0
  44. package/dist/tui/components/PresetSelector.d.ts +23 -0
  45. package/dist/tui/components/PresetSelector.js +58 -0
  46. package/dist/tui/components/Preview.d.ts +18 -0
  47. package/dist/tui/components/Preview.js +190 -0
  48. package/dist/tui/components/ScrollableContainer.d.ts +38 -0
  49. package/dist/tui/components/ScrollableContainer.js +77 -0
  50. package/dist/tui/components/SectionNav.d.ts +31 -0
  51. package/dist/tui/components/SectionNav.js +130 -0
  52. package/dist/tui/components/StringEditor.d.ts +21 -0
  53. package/dist/tui/components/StringEditor.js +29 -0
  54. package/dist/tui/hooks/useConfig.d.ts +16 -0
  55. package/dist/tui/hooks/useConfig.js +89 -0
  56. package/dist/tui/hooks/useTerminalSize.d.ts +21 -0
  57. package/dist/tui/hooks/useTerminalSize.js +48 -0
  58. package/dist/tui/utils/presets.d.ts +52 -0
  59. package/dist/tui/utils/presets.js +191 -0
  60. package/dist/tui/utils/validation.d.ts +49 -0
  61. package/dist/tui/utils/validation.js +198 -0
  62. package/dist/utils/chat-client.d.ts +144 -0
  63. package/dist/utils/chat-client.js +102 -0
  64. package/dist/utils/config.d.ts +52 -0
  65. package/dist/utils/daemon-client.d.ts +36 -0
  66. package/dist/utils/daemon-client.js +70 -0
  67. package/dist/utils/message-queue.d.ts +58 -0
  68. package/dist/utils/message-queue.js +133 -0
  69. package/dist/utils/notification.d.ts +28 -1
  70. package/dist/utils/notification.js +146 -20
  71. package/docs/MACOS-DEVELOPMENT.md +435 -0
  72. package/docs/RALPH-SETUP-TEMPLATE.md +262 -0
  73. package/package.json +6 -1
package/dist/index.js CHANGED
@@ -10,6 +10,13 @@ import { prd, prdAdd, prdList, prdStatus, prdToggle, prdClean, parseListArgs } f
10
10
  import { docker } from "./commands/docker.js";
11
11
  import { prompt } from "./commands/prompt.js";
12
12
  import { fixPrd } from "./commands/fix-prd.js";
13
+ import { fixConfig } from "./commands/fix-config.js";
14
+ import { daemon } from "./commands/daemon.js";
15
+ import { notify } from "./commands/notify.js";
16
+ import { chat } from "./commands/chat.js";
17
+ import { listen } from "./commands/listen.js";
18
+ import { config } from "./commands/config.js";
19
+ import { action } from "./commands/action.js";
13
20
  const __filename = fileURLToPath(import.meta.url);
14
21
  const __dirname = dirname(__filename);
15
22
  function getPackageInfo() {
@@ -25,7 +32,14 @@ const commands = {
25
32
  prd,
26
33
  prompt,
27
34
  docker,
35
+ daemon,
36
+ notify,
37
+ chat,
38
+ listen,
39
+ config,
40
+ action,
28
41
  "fix-prd": (args) => fixPrd(args),
42
+ "fix-config": (args) => fixConfig(args),
29
43
  // Top-level PRD commands (shortcuts)
30
44
  add: () => prdAdd(),
31
45
  list: (args) => {
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Telegram chat client implementation.
3
+ * Uses the Telegram Bot API with long polling to receive messages.
4
+ */
5
+ import { ChatClient, ChatCommandHandler, ChatMessageHandler, TelegramSettings, SendMessageOptions } from "../utils/chat-client.js";
6
+ export declare class TelegramChatClient implements ChatClient {
7
+ readonly provider: "telegram";
8
+ private settings;
9
+ private connected;
10
+ private polling;
11
+ private lastUpdateId;
12
+ private pollingTimeout;
13
+ private debug;
14
+ constructor(settings: TelegramSettings, debug?: boolean);
15
+ /**
16
+ * Make a request to the Telegram Bot API.
17
+ */
18
+ private apiRequest;
19
+ /**
20
+ * Check if a chat ID is allowed.
21
+ */
22
+ private isChatAllowed;
23
+ /**
24
+ * Start long polling for updates.
25
+ */
26
+ private poll;
27
+ connect(onCommand: ChatCommandHandler, onMessage?: ChatMessageHandler): Promise<void>;
28
+ sendMessage(chatId: string, text: string, options?: SendMessageOptions): Promise<void>;
29
+ /**
30
+ * Answer a callback query (acknowledge button press).
31
+ */
32
+ answerCallbackQuery(callbackQueryId: string, text?: string): Promise<void>;
33
+ disconnect(): Promise<void>;
34
+ isConnected(): boolean;
35
+ }
36
+ /**
37
+ * Create a Telegram chat client from settings.
38
+ */
39
+ export declare function createTelegramClient(settings: TelegramSettings, debug?: boolean): ChatClient;
@@ -0,0 +1,256 @@
1
+ /**
2
+ * Telegram chat client implementation.
3
+ * Uses the Telegram Bot API with long polling to receive messages.
4
+ */
5
+ import https from "https";
6
+ import { parseCommand, } from "../utils/chat-client.js";
7
+ export class TelegramChatClient {
8
+ provider = "telegram";
9
+ settings;
10
+ connected = false;
11
+ polling = false;
12
+ lastUpdateId = 0;
13
+ pollingTimeout = null;
14
+ debug;
15
+ constructor(settings, debug = false) {
16
+ this.settings = settings;
17
+ this.debug = debug;
18
+ }
19
+ /**
20
+ * Make a request to the Telegram Bot API.
21
+ */
22
+ async apiRequest(method, body) {
23
+ return new Promise((resolve, reject) => {
24
+ const url = `https://api.telegram.org/bot${this.settings.botToken}/${method}`;
25
+ const options = {
26
+ method: body ? "POST" : "GET",
27
+ headers: body
28
+ ? {
29
+ "Content-Type": "application/json",
30
+ }
31
+ : undefined,
32
+ };
33
+ const req = https.request(url, options, (res) => {
34
+ let data = "";
35
+ res.on("data", (chunk) => {
36
+ data += chunk;
37
+ });
38
+ res.on("end", () => {
39
+ try {
40
+ const response = JSON.parse(data);
41
+ if (response.ok && response.result !== undefined) {
42
+ resolve(response.result);
43
+ }
44
+ else {
45
+ reject(new Error(`Telegram API error: ${response.description || "Unknown error"} (code: ${response.error_code})`));
46
+ }
47
+ }
48
+ catch (err) {
49
+ reject(new Error(`Failed to parse Telegram response: ${data}`));
50
+ }
51
+ });
52
+ });
53
+ req.on("error", (err) => {
54
+ reject(new Error(`Telegram request failed: ${err.message}`));
55
+ });
56
+ if (body) {
57
+ req.write(JSON.stringify(body));
58
+ }
59
+ req.end();
60
+ });
61
+ }
62
+ /**
63
+ * Check if a chat ID is allowed.
64
+ */
65
+ isChatAllowed(chatId) {
66
+ // If no allowed chat IDs specified, allow all
67
+ if (!this.settings.allowedChatIds || this.settings.allowedChatIds.length === 0) {
68
+ return true;
69
+ }
70
+ return this.settings.allowedChatIds.includes(chatId);
71
+ }
72
+ /**
73
+ * Start long polling for updates.
74
+ */
75
+ async poll(onCommand, onMessage) {
76
+ if (!this.polling)
77
+ return;
78
+ try {
79
+ const updates = await this.apiRequest("getUpdates", {
80
+ offset: this.lastUpdateId + 1,
81
+ timeout: 30, // Long polling timeout in seconds
82
+ });
83
+ for (const update of updates) {
84
+ this.lastUpdateId = update.update_id;
85
+ // Handle callback queries (button presses)
86
+ if (update.callback_query) {
87
+ const callbackQuery = update.callback_query;
88
+ const chatId = callbackQuery.message?.chat?.id
89
+ ? String(callbackQuery.message.chat.id)
90
+ : null;
91
+ if (chatId && this.isChatAllowed(chatId) && callbackQuery.data) {
92
+ // Acknowledge the callback query
93
+ try {
94
+ await this.answerCallbackQuery(callbackQuery.id);
95
+ }
96
+ catch (err) {
97
+ if (this.debug) {
98
+ console.error(`[telegram] Failed to answer callback query: ${err}`);
99
+ }
100
+ }
101
+ // Create a synthetic message from the callback data
102
+ // The callback_data is the command string (e.g., "/run feature")
103
+ const message = {
104
+ text: callbackQuery.data,
105
+ chatId,
106
+ senderId: String(callbackQuery.from.id),
107
+ senderName: [callbackQuery.from.first_name, callbackQuery.from.last_name]
108
+ .filter(Boolean)
109
+ .join(" "),
110
+ timestamp: new Date(),
111
+ raw: update,
112
+ };
113
+ // Parse and execute the command
114
+ const command = parseCommand(message.text, message);
115
+ if (command) {
116
+ try {
117
+ await onCommand(command);
118
+ }
119
+ catch (err) {
120
+ if (this.debug) {
121
+ console.error(`[telegram] Callback command error: ${err}`);
122
+ }
123
+ await this.sendMessage(chatId, `Error executing command: ${err instanceof Error ? err.message : "Unknown error"}`);
124
+ }
125
+ }
126
+ }
127
+ continue;
128
+ }
129
+ if (update.message?.text) {
130
+ const chatId = String(update.message.chat.id);
131
+ // Check if chat is allowed
132
+ if (!this.isChatAllowed(chatId)) {
133
+ if (this.debug) {
134
+ console.log(`[telegram] Ignoring message from unauthorized chat: ${chatId}`);
135
+ }
136
+ continue;
137
+ }
138
+ const message = {
139
+ text: update.message.text,
140
+ chatId,
141
+ senderId: update.message.from ? String(update.message.from.id) : undefined,
142
+ senderName: update.message.from
143
+ ? [update.message.from.first_name, update.message.from.last_name].filter(Boolean).join(" ")
144
+ : undefined,
145
+ timestamp: new Date(update.message.date * 1000),
146
+ raw: update,
147
+ };
148
+ // Call raw message handler if provided
149
+ if (onMessage) {
150
+ try {
151
+ await onMessage(message);
152
+ }
153
+ catch (err) {
154
+ if (this.debug) {
155
+ console.error(`[telegram] Message handler error: ${err}`);
156
+ }
157
+ }
158
+ }
159
+ // Try to parse as a command
160
+ const command = parseCommand(message.text, message);
161
+ if (command) {
162
+ try {
163
+ await onCommand(command);
164
+ }
165
+ catch (err) {
166
+ if (this.debug) {
167
+ console.error(`[telegram] Command handler error: ${err}`);
168
+ }
169
+ // Send error message to chat
170
+ await this.sendMessage(chatId, `Error executing command: ${err instanceof Error ? err.message : "Unknown error"}`);
171
+ }
172
+ }
173
+ }
174
+ }
175
+ }
176
+ catch (err) {
177
+ if (this.debug) {
178
+ console.error(`[telegram] Poll error: ${err}`);
179
+ }
180
+ // Wait a bit before retrying on error
181
+ await new Promise((resolve) => setTimeout(resolve, 5000));
182
+ }
183
+ // Schedule next poll
184
+ if (this.polling) {
185
+ this.pollingTimeout = setTimeout(() => this.poll(onCommand, onMessage), 100);
186
+ }
187
+ }
188
+ async connect(onCommand, onMessage) {
189
+ if (this.connected) {
190
+ throw new Error("Already connected");
191
+ }
192
+ // Verify bot token by calling getMe
193
+ try {
194
+ const me = await this.apiRequest("getMe");
195
+ if (this.debug) {
196
+ console.log(`[telegram] Connected as @${me.username || me.first_name} (ID: ${me.id})`);
197
+ }
198
+ }
199
+ catch (err) {
200
+ throw new Error(`Failed to connect to Telegram: ${err instanceof Error ? err.message : "Unknown error"}`);
201
+ }
202
+ this.connected = true;
203
+ this.polling = true;
204
+ // Start polling
205
+ this.poll(onCommand, onMessage);
206
+ }
207
+ async sendMessage(chatId, text, options) {
208
+ if (!this.connected) {
209
+ throw new Error("Not connected");
210
+ }
211
+ const body = {
212
+ chat_id: chatId,
213
+ text,
214
+ parse_mode: "HTML",
215
+ };
216
+ // Convert generic InlineButton format to Telegram's InlineKeyboardMarkup
217
+ if (options?.inlineKeyboard && options.inlineKeyboard.length > 0) {
218
+ const inlineKeyboard = options.inlineKeyboard.map((row) => row.map((button) => ({
219
+ text: button.text,
220
+ callback_data: button.callbackData,
221
+ url: button.url,
222
+ })));
223
+ body.reply_markup = { inline_keyboard: inlineKeyboard };
224
+ }
225
+ await this.apiRequest("sendMessage", body);
226
+ }
227
+ /**
228
+ * Answer a callback query (acknowledge button press).
229
+ */
230
+ async answerCallbackQuery(callbackQueryId, text) {
231
+ if (!this.connected) {
232
+ throw new Error("Not connected");
233
+ }
234
+ await this.apiRequest("answerCallbackQuery", {
235
+ callback_query_id: callbackQueryId,
236
+ text,
237
+ });
238
+ }
239
+ async disconnect() {
240
+ this.polling = false;
241
+ this.connected = false;
242
+ if (this.pollingTimeout) {
243
+ clearTimeout(this.pollingTimeout);
244
+ this.pollingTimeout = null;
245
+ }
246
+ }
247
+ isConnected() {
248
+ return this.connected;
249
+ }
250
+ }
251
+ /**
252
+ * Create a Telegram chat client from settings.
253
+ */
254
+ export function createTelegramClient(settings, debug = false) {
255
+ return new TelegramChatClient(settings, debug);
256
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * macOS/Swift development script templates
3
+ * These scripts are generated when Swift + SwiftUI is selected during ralph init
4
+ */
5
+ /**
6
+ * Generate gen_xcode.sh script content
7
+ * This script generates an Xcode project from a Swift package, supporting SwiftUI apps
8
+ *
9
+ * Usage: ./scripts/gen_xcode.sh [project_name]
10
+ *
11
+ * @param projectName - Default project name (can be overridden by script argument)
12
+ */
13
+ export declare function generateGenXcodeScript(projectName?: string): string;
14
+ /**
15
+ * Check if the selected technologies include SwiftUI
16
+ */
17
+ export declare function hasSwiftUI(technologies: string[]): boolean;
18
+ /**
19
+ * Check if the selected technologies include Fastlane
20
+ */
21
+ export declare function hasFastlane(technologies: string[]): boolean;
22
+ /**
23
+ * Generate Fastfile template for macOS/iOS deployment
24
+ * This creates a standard Fastfile with beta and release lanes
25
+ *
26
+ * @param projectName - Name of the Xcode project/app
27
+ */
28
+ export declare function generateFastfile(projectName?: string): string;
29
+ /**
30
+ * Generate Appfile template for Fastlane
31
+ * This contains app-specific configuration like bundle ID and Apple ID
32
+ *
33
+ * @param projectName - Name of the Xcode project/app
34
+ */
35
+ export declare function generateAppfile(projectName?: string): string;
36
+ /**
37
+ * Generate README section for Fastlane setup
38
+ * This provides documentation on how to use Fastlane with the project
39
+ *
40
+ * @param projectName - Name of the Xcode project/app
41
+ */
42
+ export declare function generateFastlaneReadmeSection(projectName?: string): string;