agentic-loop 3.12.3 → 3.14.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/.claude/skills/loopgram/SKILL.md +19 -0
- package/README.md +1 -0
- package/bin/ralph.sh +17 -11
- package/dist/loopgram/claude.d.ts +18 -0
- package/dist/loopgram/claude.d.ts.map +1 -0
- package/dist/loopgram/claude.js +89 -0
- package/dist/loopgram/claude.js.map +1 -0
- package/dist/loopgram/context-search.d.ts +26 -0
- package/dist/loopgram/context-search.d.ts.map +1 -0
- package/dist/loopgram/context-search.js +175 -0
- package/dist/loopgram/context-search.js.map +1 -0
- package/dist/loopgram/conversation.d.ts +39 -0
- package/dist/loopgram/conversation.d.ts.map +1 -0
- package/dist/loopgram/conversation.js +158 -0
- package/dist/loopgram/conversation.js.map +1 -0
- package/dist/loopgram/index.d.ts +3 -0
- package/dist/loopgram/index.d.ts.map +1 -0
- package/dist/loopgram/index.js +246 -0
- package/dist/loopgram/index.js.map +1 -0
- package/dist/loopgram/loop-monitor.d.ts +16 -0
- package/dist/loopgram/loop-monitor.d.ts.map +1 -0
- package/dist/loopgram/loop-monitor.js +149 -0
- package/dist/loopgram/loop-monitor.js.map +1 -0
- package/dist/loopgram/loop-runner.d.ts +28 -0
- package/dist/loopgram/loop-runner.d.ts.map +1 -0
- package/dist/loopgram/loop-runner.js +157 -0
- package/dist/loopgram/loop-runner.js.map +1 -0
- package/dist/loopgram/prd-generator.d.ts +37 -0
- package/dist/loopgram/prd-generator.d.ts.map +1 -0
- package/dist/loopgram/prd-generator.js +134 -0
- package/dist/loopgram/prd-generator.js.map +1 -0
- package/dist/loopgram/saver.d.ts +9 -0
- package/dist/loopgram/saver.d.ts.map +1 -0
- package/dist/loopgram/saver.js +35 -0
- package/dist/loopgram/saver.js.map +1 -0
- package/dist/loopgram/types.d.ts +37 -0
- package/dist/loopgram/types.d.ts.map +1 -0
- package/dist/loopgram/types.js +5 -0
- package/dist/loopgram/types.js.map +1 -0
- package/package.json +6 -2
- package/ralph/hooks/common.sh +89 -0
- package/ralph/hooks/warn-debug.sh +14 -32
- package/ralph/hooks/warn-empty-catch.sh +13 -29
- package/ralph/hooks/warn-secrets.sh +19 -37
- package/ralph/hooks/warn-urls.sh +17 -33
- package/ralph/init.sh +30 -0
- package/ralph/loop.sh +5 -2
- package/ralph/setup/ui.sh +0 -42
- package/ralph/setup.sh +69 -45
- package/ralph/utils.sh +167 -31
- package/templates/config/fastmcp.json +5 -5
- package/templates/hooks/warn-uv-python.sh +38 -0
|
@@ -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"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import { existsSync, writeFileSync, readFileSync, unlinkSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
const PID_FILE = '.ralph/loop.pid';
|
|
5
|
+
/**
|
|
6
|
+
* Check if a Ralph loop is currently running for a project
|
|
7
|
+
*/
|
|
8
|
+
export function isLoopRunning(projectPath) {
|
|
9
|
+
const pidFile = join(projectPath, PID_FILE);
|
|
10
|
+
if (!existsSync(pidFile)) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
const pid = parseInt(readFileSync(pidFile, 'utf-8').trim());
|
|
15
|
+
// Check if process is still running
|
|
16
|
+
process.kill(pid, 0);
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
// Process not running, clean up stale PID file
|
|
21
|
+
try {
|
|
22
|
+
unlinkSync(pidFile);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// Ignore cleanup errors
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Start a Ralph loop for a project
|
|
32
|
+
*/
|
|
33
|
+
export function startLoop(projectPath) {
|
|
34
|
+
// Check if PRD exists
|
|
35
|
+
const prdPath = join(projectPath, '.ralph/prd.json');
|
|
36
|
+
if (!existsSync(prdPath)) {
|
|
37
|
+
return {
|
|
38
|
+
success: false,
|
|
39
|
+
message: 'No PRD found. Use /prd to create stories first.',
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
// Check if already running
|
|
43
|
+
if (isLoopRunning(projectPath)) {
|
|
44
|
+
return {
|
|
45
|
+
success: false,
|
|
46
|
+
message: 'Loop already running. Use /loop to check status.',
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
// Start Ralph loop in background
|
|
51
|
+
const logFile = join(projectPath, '.ralph/loop.log');
|
|
52
|
+
// Use npx agentic-loop run
|
|
53
|
+
const child = spawn('npx', ['agentic-loop', 'run'], {
|
|
54
|
+
cwd: projectPath,
|
|
55
|
+
detached: true,
|
|
56
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
57
|
+
env: {
|
|
58
|
+
...process.env,
|
|
59
|
+
// Ensure Claude runs non-interactively
|
|
60
|
+
CLAUDE_CODE_ENTRYPOINT: 'cli',
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
// Write output to log file
|
|
64
|
+
const logStream = require('fs').createWriteStream(logFile, { flags: 'a' });
|
|
65
|
+
child.stdout?.pipe(logStream);
|
|
66
|
+
child.stderr?.pipe(logStream);
|
|
67
|
+
// Save PID
|
|
68
|
+
const pidFile = join(projectPath, PID_FILE);
|
|
69
|
+
writeFileSync(pidFile, child.pid.toString());
|
|
70
|
+
// Detach child process
|
|
71
|
+
child.unref();
|
|
72
|
+
return {
|
|
73
|
+
success: true,
|
|
74
|
+
message: `Loop started (PID: ${child.pid}). Use /loop to check progress.`,
|
|
75
|
+
pid: child.pid,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
return {
|
|
80
|
+
success: false,
|
|
81
|
+
message: `Failed to start loop: ${error}`,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Stop a running Ralph loop
|
|
87
|
+
*/
|
|
88
|
+
export function stopLoop(projectPath) {
|
|
89
|
+
const pidFile = join(projectPath, PID_FILE);
|
|
90
|
+
if (!existsSync(pidFile)) {
|
|
91
|
+
return {
|
|
92
|
+
success: false,
|
|
93
|
+
message: 'No loop running.',
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const pid = parseInt(readFileSync(pidFile, 'utf-8').trim());
|
|
98
|
+
// Kill the process and its children
|
|
99
|
+
try {
|
|
100
|
+
// Kill process group (negative PID)
|
|
101
|
+
process.kill(-pid, 'SIGTERM');
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// Try killing just the process
|
|
105
|
+
process.kill(pid, 'SIGTERM');
|
|
106
|
+
}
|
|
107
|
+
// Clean up PID file
|
|
108
|
+
unlinkSync(pidFile);
|
|
109
|
+
return {
|
|
110
|
+
success: true,
|
|
111
|
+
message: 'Loop stopped.',
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
// Clean up PID file even if kill failed
|
|
116
|
+
try {
|
|
117
|
+
unlinkSync(pidFile);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// Ignore
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
success: false,
|
|
124
|
+
message: `Error stopping loop: ${error}`,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Get loop process info
|
|
130
|
+
*/
|
|
131
|
+
export function getLoopInfo(projectPath) {
|
|
132
|
+
const pidFile = join(projectPath, PID_FILE);
|
|
133
|
+
const logFile = join(projectPath, '.ralph/loop.log');
|
|
134
|
+
const running = isLoopRunning(projectPath);
|
|
135
|
+
let pid;
|
|
136
|
+
let logTail;
|
|
137
|
+
if (existsSync(pidFile)) {
|
|
138
|
+
try {
|
|
139
|
+
pid = parseInt(readFileSync(pidFile, 'utf-8').trim());
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
// Ignore
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (existsSync(logFile)) {
|
|
146
|
+
try {
|
|
147
|
+
// Get last 500 chars of log
|
|
148
|
+
const content = readFileSync(logFile, 'utf-8');
|
|
149
|
+
logTail = content.slice(-500);
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// Ignore
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return { running, pid, logTail };
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=loop-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loop-runner.js","sourceRoot":"","sources":["../../src/loopgram/loop-runner.ts"],"names":[],"mappings":"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"}
|