agentic-loop 3.13.0 → 3.14.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 (57) hide show
  1. package/.claude/skills/idea/SKILL.md +56 -0
  2. package/.claude/skills/loopgram/SKILL.md +19 -0
  3. package/.claude/skills/prd/SKILL.md +2 -0
  4. package/README.md +1 -0
  5. package/bin/ralph.sh +17 -11
  6. package/dist/loopgram/claude.d.ts +18 -0
  7. package/dist/loopgram/claude.d.ts.map +1 -0
  8. package/dist/loopgram/claude.js +89 -0
  9. package/dist/loopgram/claude.js.map +1 -0
  10. package/dist/loopgram/context-search.d.ts +26 -0
  11. package/dist/loopgram/context-search.d.ts.map +1 -0
  12. package/dist/loopgram/context-search.js +175 -0
  13. package/dist/loopgram/context-search.js.map +1 -0
  14. package/dist/loopgram/conversation.d.ts +39 -0
  15. package/dist/loopgram/conversation.d.ts.map +1 -0
  16. package/dist/loopgram/conversation.js +158 -0
  17. package/dist/loopgram/conversation.js.map +1 -0
  18. package/dist/loopgram/index.d.ts +3 -0
  19. package/dist/loopgram/index.d.ts.map +1 -0
  20. package/dist/loopgram/index.js +246 -0
  21. package/dist/loopgram/index.js.map +1 -0
  22. package/dist/loopgram/loop-monitor.d.ts +16 -0
  23. package/dist/loopgram/loop-monitor.d.ts.map +1 -0
  24. package/dist/loopgram/loop-monitor.js +149 -0
  25. package/dist/loopgram/loop-monitor.js.map +1 -0
  26. package/dist/loopgram/loop-runner.d.ts +28 -0
  27. package/dist/loopgram/loop-runner.d.ts.map +1 -0
  28. package/dist/loopgram/loop-runner.js +157 -0
  29. package/dist/loopgram/loop-runner.js.map +1 -0
  30. package/dist/loopgram/prd-generator.d.ts +37 -0
  31. package/dist/loopgram/prd-generator.d.ts.map +1 -0
  32. package/dist/loopgram/prd-generator.js +134 -0
  33. package/dist/loopgram/prd-generator.js.map +1 -0
  34. package/dist/loopgram/saver.d.ts +9 -0
  35. package/dist/loopgram/saver.d.ts.map +1 -0
  36. package/dist/loopgram/saver.js +35 -0
  37. package/dist/loopgram/saver.js.map +1 -0
  38. package/dist/loopgram/types.d.ts +37 -0
  39. package/dist/loopgram/types.d.ts.map +1 -0
  40. package/dist/loopgram/types.js +5 -0
  41. package/dist/loopgram/types.js.map +1 -0
  42. package/package.json +6 -2
  43. package/ralph/hooks/common.sh +89 -0
  44. package/ralph/hooks/warn-debug.sh +14 -32
  45. package/ralph/hooks/warn-empty-catch.sh +13 -29
  46. package/ralph/hooks/warn-secrets.sh +19 -37
  47. package/ralph/hooks/warn-urls.sh +17 -33
  48. package/ralph/loop.sh +5 -2
  49. package/ralph/prd-check.sh +35 -8
  50. package/ralph/setup/quick-setup.sh +25 -12
  51. package/ralph/setup/ui.sh +0 -42
  52. package/ralph/setup.sh +71 -46
  53. package/ralph/utils.sh +167 -31
  54. package/templates/config/fastmcp.json +6 -1
  55. package/templates/config/fullstack.json +8 -0
  56. package/templates/config/node.json +8 -0
  57. package/templates/config/python.json +8 -0
@@ -0,0 +1,158 @@
1
+ import { chat, summarize, extractTitle } from './claude.js';
2
+ import { saveIdea, slugify } from './saver.js';
3
+ // Store conversation history per chat (group or DM)
4
+ const conversations = new Map();
5
+ // Store loaded context per chat (from /context command)
6
+ const conversationContexts = new Map();
7
+ // Max history length (20 exchanges = 40 messages)
8
+ const MAX_HISTORY_LENGTH = 40;
9
+ /**
10
+ * Set context for a conversation (from /context command)
11
+ */
12
+ export function setConversationContext(chatId, context) {
13
+ conversationContexts.set(chatId, context);
14
+ }
15
+ /**
16
+ * Get context for a conversation
17
+ */
18
+ export function getConversationContext(chatId) {
19
+ return conversationContexts.get(chatId);
20
+ }
21
+ /**
22
+ * Get conversation history for a chat, creating if needed
23
+ */
24
+ export function getHistory(chatId) {
25
+ if (!conversations.has(chatId)) {
26
+ conversations.set(chatId, []);
27
+ }
28
+ return conversations.get(chatId);
29
+ }
30
+ /**
31
+ * Clear conversation history for a chat
32
+ */
33
+ export function clearConversation(chatId) {
34
+ conversations.delete(chatId);
35
+ conversationContexts.delete(chatId);
36
+ }
37
+ /**
38
+ * Handle incoming text messages
39
+ */
40
+ export async function handleMessage(ctx, config, project) {
41
+ const chatId = ctx.chat?.id;
42
+ const message = ctx.message;
43
+ if (!chatId || !message || !('text' in message)) {
44
+ return;
45
+ }
46
+ const userMessage = message.text;
47
+ const history = getHistory(chatId);
48
+ // Add user message to history
49
+ history.push({ role: 'user', content: userMessage });
50
+ // Build project context if available
51
+ let projectContext = project
52
+ ? `**Project:** ${project.name}\n**Description:** ${project.description || 'No description'}\n**Path:** ${project.path}`
53
+ : undefined;
54
+ // Add loaded context from /context command if available
55
+ const loadedContext = getConversationContext(chatId);
56
+ if (loadedContext && projectContext) {
57
+ projectContext += `\n\n## Codebase Context\n${loadedContext}`;
58
+ }
59
+ else if (loadedContext) {
60
+ projectContext = `## Codebase Context\n${loadedContext}`;
61
+ }
62
+ try {
63
+ // Get response from Claude with project context
64
+ const response = await chat(history, config.anthropic.model, projectContext);
65
+ // Add assistant response to history
66
+ history.push({ role: 'assistant', content: response });
67
+ // Trim history if too long
68
+ if (history.length > MAX_HISTORY_LENGTH) {
69
+ history.splice(0, 2);
70
+ }
71
+ await ctx.reply(response);
72
+ }
73
+ catch (error) {
74
+ console.error('Error calling Claude:', error);
75
+ await ctx.reply('Sorry, I had trouble processing that. Please try again.');
76
+ }
77
+ }
78
+ /**
79
+ * Handle /save command - summarize and save the conversation
80
+ */
81
+ export async function handleSaveCommand(ctx, projectPath, config) {
82
+ const chatId = ctx.chat?.id;
83
+ if (!chatId) {
84
+ return;
85
+ }
86
+ if (!projectPath) {
87
+ await ctx.reply('This group is not configured for a project. ' +
88
+ 'Add this group ID to ~/.config/ralph/brainstorm.json');
89
+ return;
90
+ }
91
+ const history = getHistory(chatId);
92
+ if (history.length < 2) {
93
+ await ctx.reply('Nothing to save yet. Start a conversation first!');
94
+ return;
95
+ }
96
+ try {
97
+ await ctx.reply('Summarizing conversation...');
98
+ // Generate summary
99
+ const summary = await summarize(history, config.anthropic.model);
100
+ // Extract title and create filename
101
+ const title = extractTitle(summary);
102
+ const filename = slugify(title);
103
+ // Save to project
104
+ const filepath = saveIdea(projectPath, filename, summary);
105
+ // Report success
106
+ const shortSummary = summary.length > 500 ? summary.slice(0, 500) + '...' : summary;
107
+ await ctx.reply(`āœ… Saved to ${filepath}\n\n${shortSummary}`);
108
+ // Clear conversation after saving
109
+ clearConversation(chatId);
110
+ }
111
+ catch (error) {
112
+ console.error('Error saving idea:', error);
113
+ await ctx.reply('Sorry, I had trouble saving that. Please try again.');
114
+ }
115
+ }
116
+ /**
117
+ * Handle /clear command - clear conversation history
118
+ */
119
+ export function handleClearCommand(ctx) {
120
+ const chatId = ctx.chat?.id;
121
+ if (chatId) {
122
+ clearConversation(chatId);
123
+ ctx.reply('Conversation cleared. Start fresh!');
124
+ }
125
+ }
126
+ /**
127
+ * Handle /projects command - list configured projects
128
+ */
129
+ export function handleProjectsCommand(ctx, config) {
130
+ const projects = Object.values(config.projects);
131
+ if (projects.length === 0) {
132
+ ctx.reply('No projects configured yet. Add them to ~/.config/ralph/brainstorm.json');
133
+ return;
134
+ }
135
+ const list = projects.map((p) => `• ${p.name}: ${p.path}`).join('\n');
136
+ ctx.reply(`Configured projects:\n${list}`);
137
+ }
138
+ /**
139
+ * Handle /status command - show current conversation status
140
+ */
141
+ export function handleStatusCommand(ctx, projectPath, config) {
142
+ const chatId = ctx.chat?.id;
143
+ if (!chatId) {
144
+ return;
145
+ }
146
+ const history = getHistory(chatId);
147
+ const project = projectPath
148
+ ? Object.values(config.projects).find((p) => p.path === projectPath)
149
+ : null;
150
+ const lines = [
151
+ `šŸ“Š **Status**`,
152
+ `Messages: ${history.length}`,
153
+ `Project: ${project?.name || 'Not configured'}`,
154
+ `Model: ${config.anthropic.model}`,
155
+ ];
156
+ ctx.reply(lines.join('\n'));
157
+ }
158
+ //# sourceMappingURL=conversation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation.js","sourceRoot":"","sources":["../../src/loopgram/conversation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env tsx
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/loopgram/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,246 @@
1
+ #!/usr/bin/env tsx
2
+ import { Telegraf } from 'telegraf';
3
+ import { readFileSync, existsSync } from 'fs';
4
+ import { join } from 'path';
5
+ import { handleMessage, handleSaveCommand, handleClearCommand, handleProjectsCommand, handleStatusCommand, setConversationContext, getConversationContext, getHistory, clearConversation, } from './conversation.js';
6
+ import { parseProgressFile, formatLoopStatus } from './loop-monitor.js';
7
+ import { getTopicContext } from './context-search.js';
8
+ import { generateStories, appendToPRD } from './prd-generator.js';
9
+ import { startLoop, stopLoop } from './loop-runner.js';
10
+ // Load configuration
11
+ function loadConfig() {
12
+ const configPath = join(process.env.HOME || '', '.config/ralph/loopgram.json');
13
+ if (!existsSync(configPath)) {
14
+ console.error(`Config file not found: ${configPath}`);
15
+ console.error('Create it with your Telegram user ID and project mappings.');
16
+ console.error('See setup instructions in the README.');
17
+ process.exit(1);
18
+ }
19
+ try {
20
+ const content = readFileSync(configPath, 'utf-8');
21
+ return JSON.parse(content);
22
+ }
23
+ catch (error) {
24
+ console.error(`Error reading config: ${error}`);
25
+ process.exit(1);
26
+ }
27
+ }
28
+ // Get project config for a chat ID
29
+ function getProject(chatId, config) {
30
+ return config.projects[chatId] || null;
31
+ }
32
+ // Get project path for a chat ID
33
+ function getProjectPath(chatId, config) {
34
+ const project = config.projects[chatId];
35
+ return project?.path || null;
36
+ }
37
+ // Main entry point
38
+ async function main() {
39
+ // Check for bot token
40
+ const token = process.env.TELEGRAM_BOT_TOKEN;
41
+ if (!token) {
42
+ console.error('TELEGRAM_BOT_TOKEN environment variable is required');
43
+ console.error('Set it in ~/.config/ralph/secrets and source the file');
44
+ process.exit(1);
45
+ }
46
+ // Check for Anthropic API key
47
+ if (!process.env.ANTHROPIC_API_KEY) {
48
+ console.error('ANTHROPIC_API_KEY environment variable is required');
49
+ console.error('Set it in ~/.config/ralph/secrets and source the file');
50
+ process.exit(1);
51
+ }
52
+ const config = loadConfig();
53
+ const bot = new Telegraf(token);
54
+ // Security middleware: only respond to allowed users
55
+ bot.use((ctx, next) => {
56
+ const userId = ctx.from?.id.toString();
57
+ if (!userId || !config.telegram.allowedUserIds.includes(userId)) {
58
+ // Silently ignore messages from unauthorized users
59
+ return;
60
+ }
61
+ return next();
62
+ });
63
+ // Logging middleware: log unknown group IDs for easy config setup
64
+ bot.use((ctx, next) => {
65
+ const chatId = ctx.chat?.id.toString();
66
+ if (chatId && !config.projects[chatId]) {
67
+ console.log(`Unknown chat ID: ${chatId} - add to config to enable`);
68
+ }
69
+ return next();
70
+ });
71
+ // Command handlers
72
+ bot.command('save', (ctx) => {
73
+ const chatId = ctx.chat?.id.toString();
74
+ const projectPath = chatId ? getProjectPath(chatId, config) : null;
75
+ return handleSaveCommand(ctx, projectPath, config);
76
+ });
77
+ bot.command('clear', handleClearCommand);
78
+ bot.command('projects', (ctx) => handleProjectsCommand(ctx, config));
79
+ bot.command('status', (ctx) => {
80
+ const chatId = ctx.chat?.id.toString();
81
+ const projectPath = chatId ? getProjectPath(chatId, config) : null;
82
+ return handleStatusCommand(ctx, projectPath, config);
83
+ });
84
+ bot.command('help', (ctx) => {
85
+ ctx.reply(`šŸ“± **Loopgram**\n\n` +
86
+ `Your mobile connection to agentic-loop.\n\n` +
87
+ `**Workflow:**\n` +
88
+ `/context <topic> - Load codebase context\n` +
89
+ `(chat about your idea)\n` +
90
+ `/prd - Generate stories from conversation\n` +
91
+ `/start-loop - Start Ralph to execute stories\n` +
92
+ `/loop - Check Ralph progress\n` +
93
+ `/stop-loop - Stop Ralph loop\n\n` +
94
+ `**Other:**\n` +
95
+ `/save - Save conversation as idea file\n` +
96
+ `/clear - Clear conversation history\n` +
97
+ `/status - Show current session info\n` +
98
+ `/projects - List configured projects\n` +
99
+ `/help - Show this message`);
100
+ });
101
+ // /loop command - check Ralph loop status
102
+ bot.command('loop', (ctx) => {
103
+ const chatId = ctx.chat?.id.toString();
104
+ const project = chatId ? getProject(chatId, config) : null;
105
+ if (!project) {
106
+ ctx.reply('This chat is not configured for a project.');
107
+ return;
108
+ }
109
+ const status = parseProgressFile(project.path);
110
+ if (!status) {
111
+ ctx.reply(`No Ralph loop running in ${project.name}. Start one with /start-loop`);
112
+ return;
113
+ }
114
+ ctx.reply(formatLoopStatus(status, project.name));
115
+ });
116
+ // /prd command - generate stories from conversation
117
+ bot.command('prd', async (ctx) => {
118
+ const chatId = ctx.chat?.id;
119
+ const chatIdStr = chatId?.toString();
120
+ const project = chatIdStr ? getProject(chatIdStr, config) : null;
121
+ if (!project || !chatId) {
122
+ ctx.reply('This chat is not configured for a project.');
123
+ return;
124
+ }
125
+ const history = getHistory(chatId);
126
+ if (history.length < 2) {
127
+ ctx.reply('Nothing to generate. Start a conversation first!');
128
+ return;
129
+ }
130
+ await ctx.reply('šŸ”„ Generating stories from conversation...');
131
+ try {
132
+ const context = getConversationContext(chatId);
133
+ const { featureName, featureDescription, stories } = await generateStories(history, context, config.anthropic.model);
134
+ const { added, total } = appendToPRD(project.path, featureName, featureDescription, stories);
135
+ // Format story list
136
+ const storyList = stories.map((s) => `• ${s.title}`).join('\n');
137
+ await ctx.reply(`āœ… Added ${added} stories to PRD (${total} total)\n\n` +
138
+ `**${featureName}**\n${storyList}\n\n` +
139
+ `Use /start-loop to execute.`);
140
+ // Clear conversation after generating PRD
141
+ clearConversation(chatId);
142
+ }
143
+ catch (error) {
144
+ console.error('PRD generation error:', error);
145
+ await ctx.reply('Error generating stories. Check the logs.');
146
+ }
147
+ });
148
+ // /start-loop command - start Ralph loop
149
+ bot.command('start_loop', (ctx) => {
150
+ const chatId = ctx.chat?.id.toString();
151
+ const project = chatId ? getProject(chatId, config) : null;
152
+ if (!project) {
153
+ ctx.reply('This chat is not configured for a project.');
154
+ return;
155
+ }
156
+ const result = startLoop(project.path);
157
+ if (result.success) {
158
+ ctx.reply(`šŸš€ ${result.message}`);
159
+ }
160
+ else {
161
+ ctx.reply(`āŒ ${result.message}`);
162
+ }
163
+ });
164
+ // /stop-loop command - stop Ralph loop
165
+ bot.command('stop_loop', (ctx) => {
166
+ const chatId = ctx.chat?.id.toString();
167
+ const project = chatId ? getProject(chatId, config) : null;
168
+ if (!project) {
169
+ ctx.reply('This chat is not configured for a project.');
170
+ return;
171
+ }
172
+ const result = stopLoop(project.path);
173
+ if (result.success) {
174
+ ctx.reply(`āœ… ${result.message}`);
175
+ }
176
+ else {
177
+ ctx.reply(`āŒ ${result.message}`);
178
+ }
179
+ });
180
+ // /context command - search codebase for a topic
181
+ bot.command('context', async (ctx) => {
182
+ const chatId = ctx.chat?.id.toString();
183
+ const project = chatId ? getProject(chatId, config) : null;
184
+ if (!project) {
185
+ ctx.reply('This chat is not configured for a project.');
186
+ return;
187
+ }
188
+ // Extract topic from command
189
+ const text = ctx.message?.text || '';
190
+ const topic = text.replace(/^\/context\s*/i, '').trim();
191
+ if (!topic) {
192
+ ctx.reply('Usage: /context <topic>\nExample: /context authentication');
193
+ return;
194
+ }
195
+ await ctx.reply(`šŸ” Searching ${project.name} for "${topic}"...`);
196
+ try {
197
+ const { summary, filesFound, searchTerms } = await getTopicContext(project.path, project.name, topic, config.anthropic.model);
198
+ // Store this context for the conversation
199
+ if (chatId) {
200
+ setConversationContext(parseInt(chatId), summary);
201
+ }
202
+ let response = `šŸ” Searched for: ${searchTerms.join(', ')}\n\n`;
203
+ response += `šŸ“ Found ${filesFound.length} files:\n`;
204
+ response += filesFound.slice(0, 5).map(f => `• ${f}`).join('\n');
205
+ if (filesFound.length > 5) {
206
+ response += `\n...and ${filesFound.length - 5} more`;
207
+ }
208
+ response += `\n\n${summary}`;
209
+ response += `\n\nšŸ’” Context loaded! Now brainstorm away.`;
210
+ await ctx.reply(response);
211
+ }
212
+ catch (error) {
213
+ console.error('Context search error:', error);
214
+ await ctx.reply('Error searching codebase. Check the logs.');
215
+ }
216
+ });
217
+ // Handle regular text messages
218
+ bot.on('text', (ctx) => {
219
+ const chatId = ctx.chat?.id.toString();
220
+ const project = chatId ? getProject(chatId, config) : null;
221
+ return handleMessage(ctx, config, project);
222
+ });
223
+ // Start the bot
224
+ console.log('šŸ“± Loopgram starting...');
225
+ console.log(`Model: ${config.anthropic.model}`);
226
+ console.log(`Configured projects: ${Object.values(config.projects)
227
+ .map((p) => p.name)
228
+ .join(', ') || 'none'}`);
229
+ console.log(`Allowed users: ${config.telegram.allowedUserIds.length} configured`);
230
+ await bot.launch();
231
+ console.log('āœ… Loopgram running! Send messages in Telegram.');
232
+ // Graceful shutdown
233
+ process.once('SIGINT', () => {
234
+ console.log('\nShutting down...');
235
+ bot.stop('SIGINT');
236
+ });
237
+ process.once('SIGTERM', () => {
238
+ console.log('\nShutting down...');
239
+ bot.stop('SIGTERM');
240
+ });
241
+ }
242
+ main().catch((error) => {
243
+ console.error('Fatal error:', error);
244
+ process.exit(1);
245
+ });
246
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/loopgram/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { LoopStatus } from './types.js';
2
+ /**
3
+ * Parse the Ralph progress file to get loop status
4
+ * Format: [ISO-timestamp] STATUS story_id message
5
+ * Statuses: COMPLETED, FAILED, CLI_CRASH, TIMEOUT, BLOCKED
6
+ */
7
+ export declare function parseProgressFile(projectPath: string): LoopStatus | null;
8
+ /**
9
+ * Format loop status for Telegram message
10
+ */
11
+ export declare function formatLoopStatus(status: LoopStatus, projectName: string): string;
12
+ /**
13
+ * Watch a project's progress file for changes
14
+ */
15
+ export declare function watchProgress(projectPath: string, _projectName: string, onUpdate: (status: LoopStatus, changeType: 'started' | 'completed' | 'error' | 'update') => void): () => void;
16
+ //# sourceMappingURL=loop-monitor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loop-monitor.d.ts","sourceRoot":"","sources":["../../src/loopgram/loop-monitor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAK7C;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CA6ExE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAyBhF;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,GAAG,WAAW,GAAG,OAAO,GAAG,QAAQ,KAAK,IAAI,GAC/F,MAAM,IAAI,CAiDZ"}
@@ -0,0 +1,149 @@
1
+ import { readFileSync, existsSync, watchFile, unwatchFile } from 'fs';
2
+ import { join } from 'path';
3
+ // Track last known state per project to detect changes
4
+ const lastState = new Map();
5
+ /**
6
+ * Parse the Ralph progress file to get loop status
7
+ * Format: [ISO-timestamp] STATUS story_id message
8
+ * Statuses: COMPLETED, FAILED, CLI_CRASH, TIMEOUT, BLOCKED
9
+ */
10
+ export function parseProgressFile(projectPath) {
11
+ const progressPath = join(projectPath, '.ralph/progress.txt');
12
+ if (!existsSync(progressPath)) {
13
+ return null;
14
+ }
15
+ try {
16
+ const content = readFileSync(progressPath, 'utf-8');
17
+ const lines = content.split('\n').filter(Boolean);
18
+ // Only look at recent lines (last 50)
19
+ const recentLines = lines.slice(-50);
20
+ const errors = [];
21
+ const completedStoryIds = new Set();
22
+ let currentStory = null;
23
+ let lastUpdate = '';
24
+ let isRunning = false;
25
+ let hasRalphActivity = false;
26
+ // Ralph log format: [timestamp] STATUS story_id message
27
+ const logPattern = /^\[([^\]]+)\]\s+(COMPLETED|FAILED|CLI_CRASH|TIMEOUT|BLOCKED)\s+(\S+)(.*)$/;
28
+ for (const line of recentLines) {
29
+ const match = line.match(logPattern);
30
+ if (match) {
31
+ hasRalphActivity = true;
32
+ const [, timestamp, status, storyId, message] = match;
33
+ lastUpdate = timestamp;
34
+ if (status === 'COMPLETED') {
35
+ completedStoryIds.add(storyId);
36
+ if (currentStory === storyId) {
37
+ currentStory = null;
38
+ }
39
+ }
40
+ else if (status === 'FAILED' || status === 'CLI_CRASH' || status === 'TIMEOUT') {
41
+ errors.push(`${status} ${storyId}${message}`.trim());
42
+ currentStory = storyId; // Still working on this story
43
+ isRunning = true;
44
+ }
45
+ }
46
+ // Check for loop start/end markers
47
+ if (line.includes('Loop starting') || line.includes('Starting loop')) {
48
+ isRunning = true;
49
+ hasRalphActivity = true;
50
+ }
51
+ if (line.includes('Loop complete') || line.includes('All stories done')) {
52
+ isRunning = false;
53
+ }
54
+ // Try to get total stories
55
+ const totalMatch = line.match(/(\d+)\s*(?:stories|story)/i);
56
+ if (totalMatch) {
57
+ // Don't overwrite with smaller numbers
58
+ }
59
+ }
60
+ // If no Ralph activity found, return null
61
+ if (!hasRalphActivity) {
62
+ return null;
63
+ }
64
+ return {
65
+ isRunning,
66
+ currentStory,
67
+ completedStories: completedStoryIds.size,
68
+ totalStories: 0, // Would need to read PRD to know this
69
+ lastUpdate,
70
+ errors: errors.slice(-3),
71
+ };
72
+ }
73
+ catch (error) {
74
+ console.error('Error parsing progress file:', error);
75
+ return null;
76
+ }
77
+ }
78
+ /**
79
+ * Format loop status for Telegram message
80
+ */
81
+ export function formatLoopStatus(status, projectName) {
82
+ const statusEmoji = status.isRunning ? 'šŸ”„' : 'āœ…';
83
+ const progress = status.totalStories > 0
84
+ ? `${status.completedStories}/${status.totalStories}`
85
+ : `${status.completedStories} done`;
86
+ let message = `${statusEmoji} **${projectName}**\n`;
87
+ message += `Progress: ${progress}\n`;
88
+ if (status.currentStory) {
89
+ message += `Current: ${status.currentStory}\n`;
90
+ }
91
+ if (status.lastUpdate) {
92
+ message += `Updated: ${status.lastUpdate}\n`;
93
+ }
94
+ if (status.errors.length > 0) {
95
+ message += `\nāš ļø Recent errors:\n`;
96
+ for (const err of status.errors) {
97
+ message += `• ${err.substring(0, 100)}...\n`;
98
+ }
99
+ }
100
+ return message;
101
+ }
102
+ /**
103
+ * Watch a project's progress file for changes
104
+ */
105
+ export function watchProgress(projectPath, _projectName, onUpdate) {
106
+ const progressPath = join(projectPath, '.ralph/progress.txt');
107
+ if (!existsSync(progressPath)) {
108
+ return () => { }; // No-op cleanup
109
+ }
110
+ // Store initial state
111
+ const initialContent = readFileSync(progressPath, 'utf-8');
112
+ lastState.set(projectPath, initialContent);
113
+ const checkForChanges = () => {
114
+ try {
115
+ const currentContent = readFileSync(progressPath, 'utf-8');
116
+ const previousContent = lastState.get(projectPath) || '';
117
+ if (currentContent !== previousContent) {
118
+ lastState.set(projectPath, currentContent);
119
+ const status = parseProgressFile(projectPath);
120
+ if (!status)
121
+ return;
122
+ // Determine change type
123
+ let changeType = 'update';
124
+ const newLines = currentContent.substring(previousContent.length);
125
+ if (newLines.includes('āœ“') || newLines.includes('completed')) {
126
+ changeType = 'completed';
127
+ }
128
+ else if (newLines.includes('āœ—') || newLines.includes('ERROR')) {
129
+ changeType = 'error';
130
+ }
131
+ else if (newLines.includes('Starting') || newLines.includes('ā–¶')) {
132
+ changeType = 'started';
133
+ }
134
+ onUpdate(status, changeType);
135
+ }
136
+ }
137
+ catch (error) {
138
+ // File might be temporarily unavailable during write
139
+ }
140
+ };
141
+ // Watch for changes
142
+ watchFile(progressPath, { interval: 5000 }, checkForChanges);
143
+ // Return cleanup function
144
+ return () => {
145
+ unwatchFile(progressPath);
146
+ lastState.delete(projectPath);
147
+ };
148
+ }
149
+ //# sourceMappingURL=loop-monitor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loop-monitor.js","sourceRoot":"","sources":["../../src/loopgram/loop-monitor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Check if a Ralph loop is currently running for a project
3
+ */
4
+ export declare function isLoopRunning(projectPath: string): boolean;
5
+ /**
6
+ * Start a Ralph loop for a project
7
+ */
8
+ export declare function startLoop(projectPath: string): {
9
+ success: boolean;
10
+ message: string;
11
+ pid?: number;
12
+ };
13
+ /**
14
+ * Stop a running Ralph loop
15
+ */
16
+ export declare function stopLoop(projectPath: string): {
17
+ success: boolean;
18
+ message: string;
19
+ };
20
+ /**
21
+ * Get loop process info
22
+ */
23
+ export declare function getLoopInfo(projectPath: string): {
24
+ running: boolean;
25
+ pid?: number;
26
+ logTail?: string;
27
+ };
28
+ //# sourceMappingURL=loop-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loop-runner.d.ts","sourceRoot":"","sources":["../../src/loopgram/loop-runner.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAsB1D;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAyDA;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG;IAC7C,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB,CA0CA;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG;IAChD,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CA2BA"}