nightshift-mcp 2.0.1 → 2.1.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.
- package/dist/agent-spawner.d.ts +9 -0
- package/dist/agent-spawner.d.ts.map +1 -1
- package/dist/agent-spawner.js +88 -5
- package/dist/agent-spawner.js.map +1 -1
- package/dist/daemon.d.ts +19 -5
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +212 -35
- package/dist/daemon.js.map +1 -1
- package/dist/database.d.ts +2 -2
- package/dist/database.d.ts.map +1 -1
- package/dist/database.js +4 -4
- package/dist/database.js.map +1 -1
- package/dist/index.js +219 -1146
- package/dist/index.js.map +1 -1
- package/dist/orchestrator.d.ts +10 -1
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +190 -8
- package/dist/orchestrator.js.map +1 -1
- package/dist/pipeline.d.ts +61 -0
- package/dist/pipeline.d.ts.map +1 -0
- package/dist/pipeline.js +227 -0
- package/dist/pipeline.js.map +1 -0
- package/dist/project-context.d.ts +28 -0
- package/dist/project-context.d.ts.map +1 -0
- package/dist/project-context.js +45 -0
- package/dist/project-context.js.map +1 -0
- package/dist/run-logger.d.ts +61 -0
- package/dist/run-logger.d.ts.map +1 -0
- package/dist/run-logger.js +219 -0
- package/dist/run-logger.js.map +1 -0
- package/dist/tools/agents.d.ts +3 -0
- package/dist/tools/agents.d.ts.map +1 -1
- package/dist/tools/agents.js +148 -5
- package/dist/tools/agents.js.map +1 -1
- package/dist/tools/bugs.d.ts.map +1 -1
- package/dist/tools/bugs.js +48 -22
- package/dist/tools/bugs.js.map +1 -1
- package/dist/tools/chat.d.ts.map +1 -1
- package/dist/tools/chat.js +47 -20
- package/dist/tools/chat.js.map +1 -1
- package/dist/tools/prd.d.ts.map +1 -1
- package/dist/tools/prd.js +67 -31
- package/dist/tools/prd.js.map +1 -1
- package/dist/tools/progress.d.ts.map +1 -1
- package/dist/tools/progress.js +22 -7
- package/dist/tools/progress.js.map +1 -1
- package/dist/tools/utility.js +2 -2
- package/dist/tools/utility.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,45 +4,58 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import * as fs from "fs";
|
|
6
6
|
import * as path from "path";
|
|
7
|
-
import { ChatManager } from "./chat-manager.js";
|
|
8
|
-
import { RalphManager } from "./ralph-manager.js";
|
|
9
|
-
import { WorkflowManager } from "./workflow-manager.js";
|
|
10
|
-
import { ContextStore } from "./context-store.js";
|
|
11
|
-
import { TraceManager } from "./trace-manager.js";
|
|
12
7
|
import { spawnAgent, spawnAgentBackground, getAvailableAgents, getAgentStatus, initAgentRegistry, } from "./agent-spawner.js";
|
|
13
8
|
import { toolRegistry } from "./tool-registry.js";
|
|
14
9
|
import { allTools, toolCounts } from "./tools/index.js";
|
|
10
|
+
import { createProjectManagers, getProjectFilesReport, validateProjectPath, } from "./project-context.js";
|
|
15
11
|
// Get project path from environment, command line argument, or default to current directory
|
|
16
|
-
|
|
12
|
+
let PROJECT_PATH = process.env.ROBOT_CHAT_PROJECT_PATH || process.argv[2] || process.cwd();
|
|
17
13
|
/**
|
|
18
14
|
* Tool registration mode:
|
|
19
|
-
* - 'minimal':
|
|
20
|
-
* - 'hybrid': Meta-tools + individual tools
|
|
21
|
-
* - 'legacy':
|
|
15
|
+
* - 'minimal': Meta-tools + set_project_path (3 tools, ~250 tokens)
|
|
16
|
+
* - 'hybrid': Meta-tools + individual tools + set_project_path - default
|
|
17
|
+
* - 'legacy': Individual tools + set_project_path
|
|
22
18
|
*/
|
|
23
19
|
const REGISTRATION_MODE = (process.env.NIGHTSHIFT_TOOLS || "hybrid");
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const contextStore = new ContextStore(PROJECT_PATH);
|
|
30
|
-
const traceManager = new TraceManager(PROJECT_PATH);
|
|
20
|
+
let chatManager;
|
|
21
|
+
let ralphManager;
|
|
22
|
+
let workflowManager;
|
|
23
|
+
let contextStore;
|
|
24
|
+
let traceManager;
|
|
31
25
|
// Tool execution context
|
|
32
|
-
const toolContext = {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
26
|
+
const toolContext = {};
|
|
27
|
+
function syncToolContext() {
|
|
28
|
+
toolContext.chatManager = chatManager;
|
|
29
|
+
toolContext.ralphManager = ralphManager;
|
|
30
|
+
toolContext.workflowManager = workflowManager;
|
|
31
|
+
toolContext.contextStore = contextStore;
|
|
32
|
+
toolContext.traceManager = traceManager;
|
|
33
|
+
toolContext.projectPath = PROJECT_PATH;
|
|
34
|
+
}
|
|
35
|
+
function reinitializeProjectState(projectPath) {
|
|
36
|
+
const resolvedPath = validateProjectPath(projectPath);
|
|
37
|
+
const files = getProjectFilesReport(resolvedPath);
|
|
38
|
+
const managers = createProjectManagers(resolvedPath);
|
|
39
|
+
PROJECT_PATH = managers.projectPath;
|
|
40
|
+
chatManager = managers.chatManager;
|
|
41
|
+
ralphManager = managers.ralphManager;
|
|
42
|
+
workflowManager = managers.workflowManager;
|
|
43
|
+
contextStore = managers.contextStore;
|
|
44
|
+
traceManager = managers.traceManager;
|
|
45
|
+
initAgentRegistry(PROJECT_PATH);
|
|
46
|
+
syncToolContext();
|
|
47
|
+
return {
|
|
48
|
+
projectPath: PROJECT_PATH,
|
|
49
|
+
files,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
reinitializeProjectState(PROJECT_PATH);
|
|
40
53
|
// Register all tools with the registry
|
|
41
54
|
toolRegistry.registerAll(allTools);
|
|
42
55
|
// Create MCP server
|
|
43
56
|
const server = new McpServer({
|
|
44
57
|
name: "nightshift",
|
|
45
|
-
version: "1.0
|
|
58
|
+
version: "2.1.0",
|
|
46
59
|
});
|
|
47
60
|
// Valid message types for schema validation
|
|
48
61
|
const MESSAGE_TYPES = [
|
|
@@ -89,6 +102,38 @@ function registerMetaTools() {
|
|
|
89
102
|
};
|
|
90
103
|
});
|
|
91
104
|
}
|
|
105
|
+
function registerProjectPathTool() {
|
|
106
|
+
server.tool("set_project_path", "Set the active project path for this MCP server session and reinitialize all project managers.", {
|
|
107
|
+
projectPath: z.string().describe("Absolute or relative path to the project directory"),
|
|
108
|
+
}, async ({ projectPath }) => {
|
|
109
|
+
try {
|
|
110
|
+
const result = reinitializeProjectState(projectPath);
|
|
111
|
+
return {
|
|
112
|
+
content: [
|
|
113
|
+
{
|
|
114
|
+
type: "text",
|
|
115
|
+
text: JSON.stringify({
|
|
116
|
+
message: "Project path updated successfully.",
|
|
117
|
+
projectPath: result.projectPath,
|
|
118
|
+
files: result.files,
|
|
119
|
+
}, null, 2),
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
return {
|
|
126
|
+
content: [
|
|
127
|
+
{
|
|
128
|
+
type: "text",
|
|
129
|
+
text: `Error setting project path: ${error instanceof Error ? error.message : String(error)}`,
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
isError: true,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
92
137
|
// ============================================
|
|
93
138
|
// Individual Tool Registration (Legacy)
|
|
94
139
|
// ============================================
|
|
@@ -111,50 +156,12 @@ function registerIndividualTools() {
|
|
|
111
156
|
.enum(MESSAGE_TYPES)
|
|
112
157
|
.optional()
|
|
113
158
|
.describe("Filter messages by type"),
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
});
|
|
121
|
-
if (messages.length === 0) {
|
|
122
|
-
return {
|
|
123
|
-
content: [
|
|
124
|
-
{
|
|
125
|
-
type: "text",
|
|
126
|
-
text: "No messages found in robot chat.",
|
|
127
|
-
},
|
|
128
|
-
],
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
const formatted = messages.map((m) => ({
|
|
132
|
-
agent: m.agent,
|
|
133
|
-
timestamp: m.timestamp,
|
|
134
|
-
type: m.type,
|
|
135
|
-
content: m.content,
|
|
136
|
-
lineNumber: m.lineNumber,
|
|
137
|
-
}));
|
|
138
|
-
return {
|
|
139
|
-
content: [
|
|
140
|
-
{
|
|
141
|
-
type: "text",
|
|
142
|
-
text: JSON.stringify(formatted, null, 2),
|
|
143
|
-
},
|
|
144
|
-
],
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
catch (error) {
|
|
148
|
-
return {
|
|
149
|
-
content: [
|
|
150
|
-
{
|
|
151
|
-
type: "text",
|
|
152
|
-
text: `Error reading chat: ${error instanceof Error ? error.message : String(error)}`,
|
|
153
|
-
},
|
|
154
|
-
],
|
|
155
|
-
isError: true,
|
|
156
|
-
};
|
|
157
|
-
}
|
|
159
|
+
projectPath: z
|
|
160
|
+
.string()
|
|
161
|
+
.optional()
|
|
162
|
+
.describe("Optional project path override for this call only"),
|
|
163
|
+
}, async ({ limit, agent, type, projectPath }) => {
|
|
164
|
+
return toolRegistry.execute("read_robot_chat", { limit, agent, type, projectPath }, toolContext);
|
|
158
165
|
});
|
|
159
166
|
// Tool: write_robot_chat
|
|
160
167
|
server.tool("write_robot_chat", "Write a message to the robot chat file. The message will be formatted with your agent name, current timestamp, and message type.", {
|
|
@@ -167,76 +174,18 @@ function registerIndividualTools() {
|
|
|
167
174
|
content: z
|
|
168
175
|
.string()
|
|
169
176
|
.describe("Message content. For FAILOVER_NEEDED, include: Status, Current Task, Progress, Files Modified."),
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
});
|
|
177
|
-
return {
|
|
178
|
-
content: [
|
|
179
|
-
{
|
|
180
|
-
type: "text",
|
|
181
|
-
text: `Message written successfully:\n\n${message.raw}`,
|
|
182
|
-
},
|
|
183
|
-
],
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
catch (error) {
|
|
187
|
-
return {
|
|
188
|
-
content: [
|
|
189
|
-
{
|
|
190
|
-
type: "text",
|
|
191
|
-
text: `Error writing message: ${error instanceof Error ? error.message : String(error)}`,
|
|
192
|
-
},
|
|
193
|
-
],
|
|
194
|
-
isError: true,
|
|
195
|
-
};
|
|
196
|
-
}
|
|
177
|
+
projectPath: z
|
|
178
|
+
.string()
|
|
179
|
+
.optional()
|
|
180
|
+
.describe("Optional project path override for this call only"),
|
|
181
|
+
}, async ({ agent, type, content, projectPath }) => {
|
|
182
|
+
return toolRegistry.execute("write_robot_chat", { agent, type, content, projectPath }, toolContext);
|
|
197
183
|
});
|
|
198
184
|
// Tool: check_failovers
|
|
199
|
-
server.tool("check_failovers", "Find unclaimed FAILOVER_NEEDED messages. Use this to see if another agent needs help continuing a task.", {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
return {
|
|
204
|
-
content: [
|
|
205
|
-
{
|
|
206
|
-
type: "text",
|
|
207
|
-
text: "No unclaimed failovers found.",
|
|
208
|
-
},
|
|
209
|
-
],
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
const formatted = failovers.map((f) => ({
|
|
213
|
-
requestingAgent: f.requestingAgent,
|
|
214
|
-
timestamp: f.message.timestamp,
|
|
215
|
-
task: f.task || "Not specified",
|
|
216
|
-
progress: f.progress || "Not specified",
|
|
217
|
-
filesModified: f.filesModified || [],
|
|
218
|
-
fullContent: f.message.content,
|
|
219
|
-
}));
|
|
220
|
-
return {
|
|
221
|
-
content: [
|
|
222
|
-
{
|
|
223
|
-
type: "text",
|
|
224
|
-
text: `Found ${failovers.length} unclaimed failover(s):\n\n${JSON.stringify(formatted, null, 2)}`,
|
|
225
|
-
},
|
|
226
|
-
],
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
catch (error) {
|
|
230
|
-
return {
|
|
231
|
-
content: [
|
|
232
|
-
{
|
|
233
|
-
type: "text",
|
|
234
|
-
text: `Error checking failovers: ${error instanceof Error ? error.message : String(error)}`,
|
|
235
|
-
},
|
|
236
|
-
],
|
|
237
|
-
isError: true,
|
|
238
|
-
};
|
|
239
|
-
}
|
|
185
|
+
server.tool("check_failovers", "Find unclaimed FAILOVER_NEEDED messages. Use this to see if another agent needs help continuing a task.", {
|
|
186
|
+
projectPath: z.string().optional().describe("Optional project path override for this call only"),
|
|
187
|
+
}, async ({ projectPath }) => {
|
|
188
|
+
return toolRegistry.execute("check_failovers", { projectPath }, toolContext);
|
|
240
189
|
});
|
|
241
190
|
// Tool: claim_failover
|
|
242
191
|
server.tool("claim_failover", "Claim a failover request by posting a FAILOVER_CLAIMED message. Use this after checking for failovers with check_failovers.", {
|
|
@@ -250,89 +199,24 @@ function registerIndividualTools() {
|
|
|
250
199
|
.string()
|
|
251
200
|
.optional()
|
|
252
201
|
.describe("Description of the task you're continuing (optional)"),
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
type: "text",
|
|
260
|
-
text: `Failover claimed successfully:\n\n${message.raw}`,
|
|
261
|
-
},
|
|
262
|
-
],
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
catch (error) {
|
|
266
|
-
return {
|
|
267
|
-
content: [
|
|
268
|
-
{
|
|
269
|
-
type: "text",
|
|
270
|
-
text: `Error claiming failover: ${error instanceof Error ? error.message : String(error)}`,
|
|
271
|
-
},
|
|
272
|
-
],
|
|
273
|
-
isError: true,
|
|
274
|
-
};
|
|
275
|
-
}
|
|
202
|
+
projectPath: z
|
|
203
|
+
.string()
|
|
204
|
+
.optional()
|
|
205
|
+
.describe("Optional project path override for this call only"),
|
|
206
|
+
}, async ({ agent, originalAgent, task, projectPath }) => {
|
|
207
|
+
return toolRegistry.execute("claim_failover", { agent, originalAgent, task, projectPath }, toolContext);
|
|
276
208
|
});
|
|
277
209
|
// Tool: get_chat_path
|
|
278
|
-
server.tool("get_chat_path", "Get the path to the robot chat file. Useful for debugging or manual inspection.", {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
content: [
|
|
283
|
-
{
|
|
284
|
-
type: "text",
|
|
285
|
-
text: `Robot chat file: ${chatPath}`,
|
|
286
|
-
},
|
|
287
|
-
],
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
catch (error) {
|
|
291
|
-
return {
|
|
292
|
-
content: [
|
|
293
|
-
{
|
|
294
|
-
type: "text",
|
|
295
|
-
text: `Error getting chat path: ${error instanceof Error ? error.message : String(error)}`,
|
|
296
|
-
},
|
|
297
|
-
],
|
|
298
|
-
isError: true,
|
|
299
|
-
};
|
|
300
|
-
}
|
|
210
|
+
server.tool("get_chat_path", "Get the path to the robot chat file. Useful for debugging or manual inspection.", {
|
|
211
|
+
projectPath: z.string().optional().describe("Optional project path override for this call only"),
|
|
212
|
+
}, async ({ projectPath }) => {
|
|
213
|
+
return toolRegistry.execute("get_chat_path", { projectPath }, toolContext);
|
|
301
214
|
});
|
|
302
215
|
// Tool: list_agents
|
|
303
|
-
server.tool("list_agents", "List all agents who have posted to the chat, with their last activity time and message count.", {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
return {
|
|
308
|
-
content: [
|
|
309
|
-
{
|
|
310
|
-
type: "text",
|
|
311
|
-
text: "No agents have posted yet.",
|
|
312
|
-
},
|
|
313
|
-
],
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
return {
|
|
317
|
-
content: [
|
|
318
|
-
{
|
|
319
|
-
type: "text",
|
|
320
|
-
text: JSON.stringify(agents, null, 2),
|
|
321
|
-
},
|
|
322
|
-
],
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
catch (error) {
|
|
326
|
-
return {
|
|
327
|
-
content: [
|
|
328
|
-
{
|
|
329
|
-
type: "text",
|
|
330
|
-
text: `Error listing agents: ${error instanceof Error ? error.message : String(error)}`,
|
|
331
|
-
},
|
|
332
|
-
],
|
|
333
|
-
isError: true,
|
|
334
|
-
};
|
|
335
|
-
}
|
|
216
|
+
server.tool("list_agents", "List all agents who have posted to the chat, with their last activity time and message count.", {
|
|
217
|
+
projectPath: z.string().optional().describe("Optional project path override for this call only"),
|
|
218
|
+
}, async ({ projectPath }) => {
|
|
219
|
+
return toolRegistry.execute("list_agents", { projectPath }, toolContext);
|
|
336
220
|
});
|
|
337
221
|
// Tool: watch_chat
|
|
338
222
|
server.tool("watch_chat", "Get new messages since a specific cursor (line number). Use this for polling/watching the chat. Returns new messages and the updated cursor.", {
|
|
@@ -340,56 +224,12 @@ function registerIndividualTools() {
|
|
|
340
224
|
.number()
|
|
341
225
|
.optional()
|
|
342
226
|
.describe("Line number cursor from previous watch call. Omit to get current cursor without messages."),
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
content: [
|
|
350
|
-
{
|
|
351
|
-
type: "text",
|
|
352
|
-
text: JSON.stringify({
|
|
353
|
-
cursor: currentCursor,
|
|
354
|
-
messages: [],
|
|
355
|
-
info: "Use this cursor value in your next watch_chat call to get new messages.",
|
|
356
|
-
}, null, 2),
|
|
357
|
-
},
|
|
358
|
-
],
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
const newMessages = chatManager.getMessagesSince(cursor);
|
|
362
|
-
const formatted = newMessages.map((m) => ({
|
|
363
|
-
agent: m.agent,
|
|
364
|
-
timestamp: m.timestamp,
|
|
365
|
-
type: m.type,
|
|
366
|
-
content: m.content,
|
|
367
|
-
lineNumber: m.lineNumber,
|
|
368
|
-
}));
|
|
369
|
-
return {
|
|
370
|
-
content: [
|
|
371
|
-
{
|
|
372
|
-
type: "text",
|
|
373
|
-
text: JSON.stringify({
|
|
374
|
-
cursor: currentCursor,
|
|
375
|
-
messageCount: newMessages.length,
|
|
376
|
-
messages: formatted,
|
|
377
|
-
}, null, 2),
|
|
378
|
-
},
|
|
379
|
-
],
|
|
380
|
-
};
|
|
381
|
-
}
|
|
382
|
-
catch (error) {
|
|
383
|
-
return {
|
|
384
|
-
content: [
|
|
385
|
-
{
|
|
386
|
-
type: "text",
|
|
387
|
-
text: `Error watching chat: ${error instanceof Error ? error.message : String(error)}`,
|
|
388
|
-
},
|
|
389
|
-
],
|
|
390
|
-
isError: true,
|
|
391
|
-
};
|
|
392
|
-
}
|
|
227
|
+
projectPath: z
|
|
228
|
+
.string()
|
|
229
|
+
.optional()
|
|
230
|
+
.describe("Optional project path override for this call only"),
|
|
231
|
+
}, async ({ cursor, projectPath }) => {
|
|
232
|
+
return toolRegistry.execute("watch_chat", { cursor, projectPath }, toolContext);
|
|
393
233
|
});
|
|
394
234
|
// Tool: archive_chat
|
|
395
235
|
server.tool("archive_chat", "Archive old messages to a separate file, keeping only recent messages in the main chat. Useful for managing chat file size.", {
|
|
@@ -397,39 +237,12 @@ function registerIndividualTools() {
|
|
|
397
237
|
.number()
|
|
398
238
|
.optional()
|
|
399
239
|
.describe("Number of recent messages to keep in main chat (default: 50)"),
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
{
|
|
407
|
-
type: "text",
|
|
408
|
-
text: "No messages to archive. Chat file is small enough.",
|
|
409
|
-
},
|
|
410
|
-
],
|
|
411
|
-
};
|
|
412
|
-
}
|
|
413
|
-
return {
|
|
414
|
-
content: [
|
|
415
|
-
{
|
|
416
|
-
type: "text",
|
|
417
|
-
text: `Archived ${result.archived} messages to: ${result.archiveFile}`,
|
|
418
|
-
},
|
|
419
|
-
],
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
catch (error) {
|
|
423
|
-
return {
|
|
424
|
-
content: [
|
|
425
|
-
{
|
|
426
|
-
type: "text",
|
|
427
|
-
text: `Error archiving chat: ${error instanceof Error ? error.message : String(error)}`,
|
|
428
|
-
},
|
|
429
|
-
],
|
|
430
|
-
isError: true,
|
|
431
|
-
};
|
|
432
|
-
}
|
|
240
|
+
projectPath: z
|
|
241
|
+
.string()
|
|
242
|
+
.optional()
|
|
243
|
+
.describe("Optional project path override for this call only"),
|
|
244
|
+
}, async ({ keepRecent, projectPath }) => {
|
|
245
|
+
return toolRegistry.execute("archive_chat", { keepRecent, projectPath }, toolContext);
|
|
433
246
|
});
|
|
434
247
|
// ============================================
|
|
435
248
|
// Agent Spawning Tools
|
|
@@ -482,236 +295,34 @@ function registerIndividualTools() {
|
|
|
482
295
|
.number()
|
|
483
296
|
.optional()
|
|
484
297
|
.describe("Timeout in seconds (default: 300 = 5 minutes)"),
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
exitCode: result.exitCode,
|
|
514
|
-
error: result.error,
|
|
515
|
-
}, null, 2),
|
|
516
|
-
},
|
|
517
|
-
],
|
|
518
|
-
isError: !result.success,
|
|
519
|
-
};
|
|
520
|
-
}
|
|
521
|
-
catch (error) {
|
|
522
|
-
return {
|
|
523
|
-
content: [
|
|
524
|
-
{
|
|
525
|
-
type: "text",
|
|
526
|
-
text: `Error spawning agent: ${error instanceof Error ? error.message : String(error)}`,
|
|
527
|
-
},
|
|
528
|
-
],
|
|
529
|
-
isError: true,
|
|
530
|
-
};
|
|
531
|
-
}
|
|
532
|
-
});
|
|
533
|
-
// Tool: spawn_agent_background
|
|
534
|
-
server.tool("spawn_agent_background", "Spawn an AI agent in the background (non-blocking). Returns immediately with the output file path. Use this when you want agents to work in parallel.", {
|
|
535
|
-
agent: z.enum(AGENT_TYPES).describe("Which agent to spawn (claude, codex, gemini, vibe, or goose)"),
|
|
536
|
-
prompt: z.string().describe("The prompt/task to send to the agent"),
|
|
537
|
-
}, async ({ agent, prompt }) => {
|
|
538
|
-
try {
|
|
539
|
-
const result = spawnAgentBackground({
|
|
540
|
-
agent: agent,
|
|
541
|
-
prompt,
|
|
542
|
-
projectPath: PROJECT_PATH,
|
|
543
|
-
});
|
|
544
|
-
// Post to chat
|
|
545
|
-
chatManager.writeMessage({
|
|
546
|
-
agent: "NightShift",
|
|
547
|
-
type: "INFO",
|
|
548
|
-
content: `Spawned ${agent} in background (PID: ${result.pid})\nOutput: ${result.outputFile}`,
|
|
549
|
-
});
|
|
550
|
-
return {
|
|
551
|
-
content: [
|
|
552
|
-
{
|
|
553
|
-
type: "text",
|
|
554
|
-
text: JSON.stringify({
|
|
555
|
-
agent,
|
|
556
|
-
pid: result.pid,
|
|
557
|
-
outputFile: result.outputFile,
|
|
558
|
-
status: "running in background",
|
|
559
|
-
}, null, 2),
|
|
560
|
-
},
|
|
561
|
-
],
|
|
562
|
-
};
|
|
563
|
-
}
|
|
564
|
-
catch (error) {
|
|
565
|
-
return {
|
|
566
|
-
content: [
|
|
567
|
-
{
|
|
568
|
-
type: "text",
|
|
569
|
-
text: `Error spawning background agent: ${error instanceof Error ? error.message : String(error)}`,
|
|
570
|
-
},
|
|
571
|
-
],
|
|
572
|
-
isError: true,
|
|
573
|
-
};
|
|
574
|
-
}
|
|
575
|
-
});
|
|
576
|
-
// Tool: delegate_story
|
|
577
|
-
server.tool("delegate_story", "Delegate a user story to another AI agent. Claims the story, spawns the agent with full context, and tracks completion. The spawned agent works autonomously.", {
|
|
578
|
-
agent: z.enum(AGENT_TYPES).describe("Which agent to delegate to"),
|
|
579
|
-
storyId: z.string().optional().describe("Specific story ID (defaults to next available)"),
|
|
580
|
-
background: z.boolean().optional().describe("Run in background (default: false)"),
|
|
581
|
-
}, async ({ agent, storyId, background }) => {
|
|
582
|
-
try {
|
|
583
|
-
// Get the story
|
|
584
|
-
let story;
|
|
585
|
-
if (storyId) {
|
|
586
|
-
const prd = ralphManager.readPRD();
|
|
587
|
-
story = prd?.userStories.find((s) => s.id === storyId && !s.passes);
|
|
588
|
-
}
|
|
589
|
-
else {
|
|
590
|
-
story = ralphManager.getNextStory();
|
|
591
|
-
}
|
|
592
|
-
if (!story) {
|
|
593
|
-
const summary = ralphManager.getCompletionSummary();
|
|
594
|
-
if (summary.incomplete === 0) {
|
|
595
|
-
return {
|
|
596
|
-
content: [
|
|
597
|
-
{
|
|
598
|
-
type: "text",
|
|
599
|
-
text: "<promise>COMPLETE</promise>\nAll stories are complete!",
|
|
600
|
-
},
|
|
601
|
-
],
|
|
602
|
-
};
|
|
603
|
-
}
|
|
604
|
-
return {
|
|
605
|
-
content: [
|
|
606
|
-
{
|
|
607
|
-
type: "text",
|
|
608
|
-
text: storyId ? `Story ${storyId} not found or already complete.` : "No stories available.",
|
|
609
|
-
},
|
|
610
|
-
],
|
|
611
|
-
isError: true,
|
|
612
|
-
};
|
|
613
|
-
}
|
|
614
|
-
// Read progress for context
|
|
615
|
-
const progress = ralphManager.readProgress();
|
|
616
|
-
const recentChat = chatManager.readMessages({ limit: 5 });
|
|
617
|
-
// Build delegation prompt
|
|
618
|
-
const delegationPrompt = `You are an autonomous coding agent. Complete this user story:
|
|
619
|
-
|
|
620
|
-
## Story: ${story.id} - ${story.title}
|
|
621
|
-
|
|
622
|
-
${story.description}
|
|
623
|
-
|
|
624
|
-
### Acceptance Criteria:
|
|
625
|
-
${story.acceptanceCriteria.map((c) => `- ${c}`).join("\n")}
|
|
626
|
-
|
|
627
|
-
### Context from Progress File:
|
|
628
|
-
${progress ? progress.substring(0, 2000) : "No previous progress."}
|
|
629
|
-
|
|
630
|
-
### Recent Chat:
|
|
631
|
-
${recentChat.map((m) => `[${m.agent}] ${m.type}: ${m.content.substring(0, 200)}`).join("\n")}
|
|
632
|
-
|
|
633
|
-
### Instructions:
|
|
634
|
-
1. Implement the acceptance criteria
|
|
635
|
-
2. Run quality checks (typecheck, lint, test)
|
|
636
|
-
3. Commit with message: "feat: ${story.id} - ${story.title}"
|
|
637
|
-
4. Use the nightshift MCP tools to:
|
|
638
|
-
- Post STATUS_UPDATE messages as you work
|
|
639
|
-
- Use complete_story when done (include learnings)
|
|
640
|
-
- Post FAILOVER_NEEDED if you hit limits
|
|
641
|
-
|
|
642
|
-
Begin implementation now.`;
|
|
643
|
-
// Post delegation to chat
|
|
644
|
-
chatManager.writeMessage({
|
|
645
|
-
agent: "NightShift",
|
|
646
|
-
type: "TASK_ASSIGNMENT",
|
|
647
|
-
content: `Delegating ${story.id} to ${agent}:\n${story.title}`,
|
|
648
|
-
});
|
|
649
|
-
if (background) {
|
|
650
|
-
const result = spawnAgentBackground({
|
|
651
|
-
agent: agent,
|
|
652
|
-
prompt: delegationPrompt,
|
|
653
|
-
projectPath: PROJECT_PATH,
|
|
654
|
-
});
|
|
655
|
-
return {
|
|
656
|
-
content: [
|
|
657
|
-
{
|
|
658
|
-
type: "text",
|
|
659
|
-
text: JSON.stringify({
|
|
660
|
-
status: "delegated",
|
|
661
|
-
agent,
|
|
662
|
-
story: story.id,
|
|
663
|
-
title: story.title,
|
|
664
|
-
background: true,
|
|
665
|
-
pid: result.pid,
|
|
666
|
-
outputFile: result.outputFile,
|
|
667
|
-
}, null, 2),
|
|
668
|
-
},
|
|
669
|
-
],
|
|
670
|
-
};
|
|
671
|
-
}
|
|
672
|
-
else {
|
|
673
|
-
const result = await spawnAgent({
|
|
674
|
-
agent: agent,
|
|
675
|
-
prompt: delegationPrompt,
|
|
676
|
-
projectPath: PROJECT_PATH,
|
|
677
|
-
timeout: 10 * 60 * 1000, // 10 minutes for story work
|
|
678
|
-
});
|
|
679
|
-
return {
|
|
680
|
-
content: [
|
|
681
|
-
{
|
|
682
|
-
type: "text",
|
|
683
|
-
text: JSON.stringify({
|
|
684
|
-
status: result.success ? "completed" : "failed",
|
|
685
|
-
agent,
|
|
686
|
-
story: story.id,
|
|
687
|
-
title: story.title,
|
|
688
|
-
output: result.output,
|
|
689
|
-
error: result.error,
|
|
690
|
-
}, null, 2),
|
|
691
|
-
},
|
|
692
|
-
],
|
|
693
|
-
isError: !result.success,
|
|
694
|
-
};
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
catch (error) {
|
|
698
|
-
return {
|
|
699
|
-
content: [
|
|
700
|
-
{
|
|
701
|
-
type: "text",
|
|
702
|
-
text: `Error delegating story: ${error instanceof Error ? error.message : String(error)}`,
|
|
703
|
-
},
|
|
704
|
-
],
|
|
705
|
-
isError: true,
|
|
706
|
-
};
|
|
707
|
-
}
|
|
708
|
-
});
|
|
709
|
-
// Tool: delegate_research
|
|
710
|
-
server.tool("delegate_research", "Delegate a research or planning task to Gemini. Gemini excels at read-only tasks: codebase analysis, architecture planning, code review, and documentation. Returns findings that can inform implementation decisions.", {
|
|
711
|
-
task: z.string().describe("The research/planning task (e.g., 'Analyze authentication patterns in codebase', 'Review PR for security issues', 'Plan architecture for feature X')"),
|
|
712
|
-
context: z.string().optional().describe("Additional context to provide"),
|
|
713
|
-
background: z.boolean().optional().describe("Run in background (default: false)"),
|
|
714
|
-
}, async ({ task, context, background }) => {
|
|
298
|
+
role: z.string().optional().describe("Agent role for audit trail (e.g., 'planner', 'coder', 'fixer', 'verifier', 'reviewer')"),
|
|
299
|
+
storyId: z.string().optional().describe("Story ID being worked on (for run log tracking)"),
|
|
300
|
+
}, async ({ agent, prompt, timeout, role, storyId }) => {
|
|
301
|
+
return toolRegistry.execute("spawn_agent", { agent, prompt, timeout, role, storyId }, toolContext);
|
|
302
|
+
});
|
|
303
|
+
// Tool: spawn_agent_background
|
|
304
|
+
server.tool("spawn_agent_background", "Spawn an AI agent in the background (non-blocking). Returns immediately with the output file path. Use this when you want agents to work in parallel.", {
|
|
305
|
+
agent: z.enum(AGENT_TYPES).describe("Which agent to spawn (claude, codex, gemini, vibe, or goose)"),
|
|
306
|
+
prompt: z.string().describe("The prompt/task to send to the agent"),
|
|
307
|
+
role: z.string().optional().describe("Agent role for audit trail (e.g., 'planner', 'coder', 'fixer', 'verifier')"),
|
|
308
|
+
}, async ({ agent, prompt, role }) => {
|
|
309
|
+
return toolRegistry.execute("spawn_agent_background", { agent, prompt, role }, toolContext);
|
|
310
|
+
});
|
|
311
|
+
// Tool: delegate_story
|
|
312
|
+
server.tool("delegate_story", "Delegate a user story to another AI agent. Claims the story, spawns the agent with full context, and tracks completion. The spawned agent works autonomously.", {
|
|
313
|
+
agent: z.enum(AGENT_TYPES).describe("Which agent to delegate to"),
|
|
314
|
+
storyId: z.string().optional().describe("Specific story ID (defaults to next available)"),
|
|
315
|
+
background: z.boolean().optional().describe("Run in background (default: false)"),
|
|
316
|
+
role: z.string().optional().describe("Agent role for audit trail (default: 'implementer')"),
|
|
317
|
+
}, async ({ agent, storyId, background, role }) => {
|
|
318
|
+
return toolRegistry.execute("delegate_story", { agent, storyId, background, role }, toolContext);
|
|
319
|
+
});
|
|
320
|
+
// Tool: delegate_research
|
|
321
|
+
server.tool("delegate_research", "Delegate a research or planning task to Gemini. Gemini excels at read-only tasks: codebase analysis, architecture planning, code review, and documentation. Returns findings that can inform implementation decisions.", {
|
|
322
|
+
task: z.string().describe("The research/planning task (e.g., 'Analyze authentication patterns in codebase', 'Review PR for security issues', 'Plan architecture for feature X')"),
|
|
323
|
+
context: z.string().optional().describe("Additional context to provide"),
|
|
324
|
+
background: z.boolean().optional().describe("Run in background (default: false)"),
|
|
325
|
+
}, async ({ task, context, background }) => {
|
|
715
326
|
try {
|
|
716
327
|
// Read codebase context
|
|
717
328
|
const progress = ralphManager.readProgress();
|
|
@@ -803,261 +414,61 @@ Focus on analysis and recommendations. Do NOT attempt to write or modify files.`
|
|
|
803
414
|
content: [
|
|
804
415
|
{
|
|
805
416
|
type: "text",
|
|
806
|
-
text: `Error delegating research: ${error instanceof Error ? error.message : String(error)}`,
|
|
807
|
-
},
|
|
808
|
-
],
|
|
809
|
-
isError: true,
|
|
810
|
-
};
|
|
811
|
-
}
|
|
812
|
-
});
|
|
813
|
-
// ============================================
|
|
814
|
-
// Ralph PRD/Progress Tools
|
|
815
|
-
// ============================================
|
|
816
|
-
// Tool: read_prd
|
|
817
|
-
server.tool("read_prd", "Read the PRD (Product Requirements Document) file. Returns structured task list with user stories and their completion status.", {}, async () => {
|
|
818
|
-
try {
|
|
819
|
-
if (!ralphManager.hasPRD()) {
|
|
820
|
-
return {
|
|
821
|
-
content: [
|
|
822
|
-
{
|
|
823
|
-
type: "text",
|
|
824
|
-
text: "No prd.json found in project. Create one to use Ralph-style task management.",
|
|
825
|
-
},
|
|
826
|
-
],
|
|
827
|
-
};
|
|
828
|
-
}
|
|
829
|
-
const prd = ralphManager.readPRD();
|
|
830
|
-
const summary = ralphManager.getCompletionSummary();
|
|
831
|
-
return {
|
|
832
|
-
content: [
|
|
833
|
-
{
|
|
834
|
-
type: "text",
|
|
835
|
-
text: JSON.stringify({ prd, summary }, null, 2),
|
|
836
|
-
},
|
|
837
|
-
],
|
|
838
|
-
};
|
|
839
|
-
}
|
|
840
|
-
catch (error) {
|
|
841
|
-
return {
|
|
842
|
-
content: [
|
|
843
|
-
{
|
|
844
|
-
type: "text",
|
|
845
|
-
text: `Error reading PRD: ${error instanceof Error ? error.message : String(error)}`,
|
|
846
|
-
},
|
|
847
|
-
],
|
|
848
|
-
isError: true,
|
|
849
|
-
};
|
|
850
|
-
}
|
|
851
|
-
});
|
|
852
|
-
// Tool: get_next_story
|
|
853
|
-
server.tool("get_next_story", "Get the next user story to work on (highest priority incomplete story). Use this to claim your next task.", {}, async () => {
|
|
854
|
-
try {
|
|
855
|
-
const story = ralphManager.getNextStory();
|
|
856
|
-
if (!story) {
|
|
857
|
-
const summary = ralphManager.getCompletionSummary();
|
|
858
|
-
if (summary.total === 0) {
|
|
859
|
-
return {
|
|
860
|
-
content: [
|
|
861
|
-
{
|
|
862
|
-
type: "text",
|
|
863
|
-
text: "No PRD found or no stories defined.",
|
|
864
|
-
},
|
|
865
|
-
],
|
|
866
|
-
};
|
|
867
|
-
}
|
|
868
|
-
return {
|
|
869
|
-
content: [
|
|
870
|
-
{
|
|
871
|
-
type: "text",
|
|
872
|
-
text: "<promise>COMPLETE</promise>\nAll stories are complete!",
|
|
873
|
-
},
|
|
874
|
-
],
|
|
875
|
-
};
|
|
876
|
-
}
|
|
877
|
-
return {
|
|
878
|
-
content: [
|
|
879
|
-
{
|
|
880
|
-
type: "text",
|
|
881
|
-
text: JSON.stringify(story, null, 2),
|
|
882
|
-
},
|
|
883
|
-
],
|
|
884
|
-
};
|
|
885
|
-
}
|
|
886
|
-
catch (error) {
|
|
887
|
-
return {
|
|
888
|
-
content: [
|
|
889
|
-
{
|
|
890
|
-
type: "text",
|
|
891
|
-
text: `Error getting next story: ${error instanceof Error ? error.message : String(error)}`,
|
|
892
|
-
},
|
|
893
|
-
],
|
|
894
|
-
isError: true,
|
|
895
|
-
};
|
|
896
|
-
}
|
|
897
|
-
});
|
|
898
|
-
// Tool: mark_story_complete
|
|
899
|
-
server.tool("mark_story_complete", "Mark a user story as complete (sets passes: true). Use after successfully implementing and testing a story.", {
|
|
900
|
-
storyId: z.string().describe("The story ID to mark complete (e.g., 'US-001')"),
|
|
901
|
-
notes: z.string().optional().describe("Optional notes about the implementation"),
|
|
902
|
-
}, async ({ storyId, notes }) => {
|
|
903
|
-
try {
|
|
904
|
-
const story = ralphManager.markStoryComplete(storyId, notes);
|
|
905
|
-
if (!story) {
|
|
906
|
-
return {
|
|
907
|
-
content: [
|
|
908
|
-
{
|
|
909
|
-
type: "text",
|
|
910
|
-
text: `Story ${storyId} not found in PRD.`,
|
|
911
|
-
},
|
|
912
|
-
],
|
|
913
|
-
isError: true,
|
|
914
|
-
};
|
|
915
|
-
}
|
|
916
|
-
const summary = ralphManager.getCompletionSummary();
|
|
917
|
-
return {
|
|
918
|
-
content: [
|
|
919
|
-
{
|
|
920
|
-
type: "text",
|
|
921
|
-
text: `Story ${storyId} marked complete!\n\nProgress: ${summary.complete}/${summary.total} (${summary.percentComplete}%)${summary.incomplete === 0 ? "\n\n<promise>COMPLETE</promise>\nAll stories are complete!" : ""}`,
|
|
922
|
-
},
|
|
923
|
-
],
|
|
924
|
-
};
|
|
925
|
-
}
|
|
926
|
-
catch (error) {
|
|
927
|
-
return {
|
|
928
|
-
content: [
|
|
929
|
-
{
|
|
930
|
-
type: "text",
|
|
931
|
-
text: `Error marking story complete: ${error instanceof Error ? error.message : String(error)}`,
|
|
932
|
-
},
|
|
933
|
-
],
|
|
934
|
-
isError: true,
|
|
935
|
-
};
|
|
936
|
-
}
|
|
937
|
-
});
|
|
938
|
-
// Tool: get_incomplete_stories
|
|
939
|
-
server.tool("get_incomplete_stories", "Get all incomplete stories from the PRD, sorted by priority.", {}, async () => {
|
|
940
|
-
try {
|
|
941
|
-
const stories = ralphManager.getIncompleteStories();
|
|
942
|
-
if (stories.length === 0) {
|
|
943
|
-
return {
|
|
944
|
-
content: [
|
|
945
|
-
{
|
|
946
|
-
type: "text",
|
|
947
|
-
text: "No incomplete stories found. All done!",
|
|
948
|
-
},
|
|
949
|
-
],
|
|
950
|
-
};
|
|
951
|
-
}
|
|
952
|
-
return {
|
|
953
|
-
content: [
|
|
954
|
-
{
|
|
955
|
-
type: "text",
|
|
956
|
-
text: JSON.stringify(stories, null, 2),
|
|
957
|
-
},
|
|
958
|
-
],
|
|
959
|
-
};
|
|
960
|
-
}
|
|
961
|
-
catch (error) {
|
|
962
|
-
return {
|
|
963
|
-
content: [
|
|
964
|
-
{
|
|
965
|
-
type: "text",
|
|
966
|
-
text: `Error getting incomplete stories: ${error instanceof Error ? error.message : String(error)}`,
|
|
967
|
-
},
|
|
968
|
-
],
|
|
969
|
-
isError: true,
|
|
970
|
-
};
|
|
971
|
-
}
|
|
972
|
-
});
|
|
973
|
-
// Tool: read_progress
|
|
974
|
-
server.tool("read_progress", "Read the progress.txt file containing learnings and patterns from previous iterations.", {}, async () => {
|
|
975
|
-
try {
|
|
976
|
-
const content = ralphManager.readProgress();
|
|
977
|
-
if (!content) {
|
|
978
|
-
return {
|
|
979
|
-
content: [
|
|
980
|
-
{
|
|
981
|
-
type: "text",
|
|
982
|
-
text: "No progress.txt found. One will be created when you append progress.",
|
|
983
|
-
},
|
|
984
|
-
],
|
|
985
|
-
};
|
|
986
|
-
}
|
|
987
|
-
return {
|
|
988
|
-
content: [
|
|
989
|
-
{
|
|
990
|
-
type: "text",
|
|
991
|
-
text: content,
|
|
992
|
-
},
|
|
993
|
-
],
|
|
994
|
-
};
|
|
995
|
-
}
|
|
996
|
-
catch (error) {
|
|
997
|
-
return {
|
|
998
|
-
content: [
|
|
999
|
-
{
|
|
1000
|
-
type: "text",
|
|
1001
|
-
text: `Error reading progress: ${error instanceof Error ? error.message : String(error)}`,
|
|
1002
|
-
},
|
|
1003
|
-
],
|
|
1004
|
-
isError: true,
|
|
1005
|
-
};
|
|
1006
|
-
}
|
|
1007
|
-
});
|
|
1008
|
-
// Tool: append_progress
|
|
1009
|
-
server.tool("append_progress", "Append a progress entry to progress.txt. Include what was implemented, files changed, and learnings for future iterations.", {
|
|
1010
|
-
content: z.string().describe("Progress entry content including: what was implemented, files changed, learnings/gotchas discovered"),
|
|
1011
|
-
}, async ({ content }) => {
|
|
1012
|
-
try {
|
|
1013
|
-
ralphManager.appendProgress(content);
|
|
1014
|
-
return {
|
|
1015
|
-
content: [
|
|
1016
|
-
{
|
|
1017
|
-
type: "text",
|
|
1018
|
-
text: "Progress entry added successfully.",
|
|
1019
|
-
},
|
|
1020
|
-
],
|
|
1021
|
-
};
|
|
1022
|
-
}
|
|
1023
|
-
catch (error) {
|
|
1024
|
-
return {
|
|
1025
|
-
content: [
|
|
1026
|
-
{
|
|
1027
|
-
type: "text",
|
|
1028
|
-
text: `Error appending progress: ${error instanceof Error ? error.message : String(error)}`,
|
|
417
|
+
text: `Error delegating research: ${error instanceof Error ? error.message : String(error)}`,
|
|
1029
418
|
},
|
|
1030
419
|
],
|
|
1031
420
|
isError: true,
|
|
1032
421
|
};
|
|
1033
422
|
}
|
|
1034
423
|
});
|
|
424
|
+
// ============================================
|
|
425
|
+
// Ralph PRD/Progress Tools
|
|
426
|
+
// ============================================
|
|
427
|
+
// Tool: read_prd
|
|
428
|
+
server.tool("read_prd", "Read the PRD (Product Requirements Document) file. Returns structured task list with user stories and their completion status.", {
|
|
429
|
+
projectPath: z.string().optional().describe("Optional project path override for this call only"),
|
|
430
|
+
}, async ({ projectPath }) => {
|
|
431
|
+
return toolRegistry.execute("read_prd", { projectPath }, toolContext);
|
|
432
|
+
});
|
|
433
|
+
// Tool: get_next_story
|
|
434
|
+
server.tool("get_next_story", "Get the next user story to work on (highest priority incomplete story). Use this to claim your next task.", {
|
|
435
|
+
projectPath: z.string().optional().describe("Optional project path override for this call only"),
|
|
436
|
+
}, async ({ projectPath }) => {
|
|
437
|
+
return toolRegistry.execute("get_next_story", { projectPath }, toolContext);
|
|
438
|
+
});
|
|
439
|
+
// Tool: mark_story_complete
|
|
440
|
+
server.tool("mark_story_complete", "Mark a user story as complete (sets passes: true). Use after successfully implementing and testing a story.", {
|
|
441
|
+
storyId: z.string().describe("The story ID to mark complete (e.g., 'US-001')"),
|
|
442
|
+
notes: z.string().optional().describe("Optional notes about the implementation"),
|
|
443
|
+
projectPath: z.string().optional().describe("Optional project path override for this call only"),
|
|
444
|
+
}, async ({ storyId, notes, projectPath }) => {
|
|
445
|
+
return toolRegistry.execute("mark_story_complete", { storyId, notes, projectPath }, toolContext);
|
|
446
|
+
});
|
|
447
|
+
// Tool: get_incomplete_stories
|
|
448
|
+
server.tool("get_incomplete_stories", "Get all incomplete stories from the PRD, sorted by priority.", {
|
|
449
|
+
projectPath: z.string().optional().describe("Optional project path override for this call only"),
|
|
450
|
+
}, async ({ projectPath }) => {
|
|
451
|
+
return toolRegistry.execute("get_incomplete_stories", { projectPath }, toolContext);
|
|
452
|
+
});
|
|
453
|
+
// Tool: read_progress
|
|
454
|
+
server.tool("read_progress", "Read the progress.txt file containing learnings and patterns from previous iterations.", {
|
|
455
|
+
projectPath: z.string().optional().describe("Optional project path override for this call only"),
|
|
456
|
+
}, async ({ projectPath }) => {
|
|
457
|
+
return toolRegistry.execute("read_progress", { projectPath }, toolContext);
|
|
458
|
+
});
|
|
459
|
+
// Tool: append_progress
|
|
460
|
+
server.tool("append_progress", "Append a progress entry to progress.txt. Include what was implemented, files changed, and learnings for future iterations.", {
|
|
461
|
+
content: z.string().describe("Progress entry content including: what was implemented, files changed, learnings/gotchas discovered"),
|
|
462
|
+
projectPath: z.string().optional().describe("Optional project path override for this call only"),
|
|
463
|
+
}, async ({ content, projectPath }) => {
|
|
464
|
+
return toolRegistry.execute("append_progress", { content, projectPath }, toolContext);
|
|
465
|
+
});
|
|
1035
466
|
// Tool: add_codebase_pattern
|
|
1036
467
|
server.tool("add_codebase_pattern", "Add a reusable pattern to the Codebase Patterns section of progress.txt. Use for general patterns future iterations should know.", {
|
|
1037
468
|
pattern: z.string().describe("A reusable pattern (e.g., 'Use sql<number> template for aggregations')"),
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
return {
|
|
1042
|
-
content: [
|
|
1043
|
-
{
|
|
1044
|
-
type: "text",
|
|
1045
|
-
text: `Pattern added: ${pattern}`,
|
|
1046
|
-
},
|
|
1047
|
-
],
|
|
1048
|
-
};
|
|
1049
|
-
}
|
|
1050
|
-
catch (error) {
|
|
1051
|
-
return {
|
|
1052
|
-
content: [
|
|
1053
|
-
{
|
|
1054
|
-
type: "text",
|
|
1055
|
-
text: `Error adding pattern: ${error instanceof Error ? error.message : String(error)}`,
|
|
1056
|
-
},
|
|
1057
|
-
],
|
|
1058
|
-
isError: true,
|
|
1059
|
-
};
|
|
1060
|
-
}
|
|
469
|
+
projectPath: z.string().optional().describe("Optional project path override for this call only"),
|
|
470
|
+
}, async ({ pattern, projectPath }) => {
|
|
471
|
+
return toolRegistry.execute("add_codebase_pattern", { pattern, projectPath }, toolContext);
|
|
1061
472
|
});
|
|
1062
473
|
// =============================================================================
|
|
1063
474
|
// Workflow Management Tools
|
|
@@ -1429,6 +840,12 @@ Focus on analysis and recommendations. Do NOT attempt to write or modify files.`
|
|
|
1429
840
|
};
|
|
1430
841
|
}
|
|
1431
842
|
});
|
|
843
|
+
// Tool: get_run_stats
|
|
844
|
+
server.tool("get_run_stats", "Get token usage and performance stats from the run log.", {
|
|
845
|
+
limit: z.number().optional().describe("Number of recent runs to show in detail (default: 10)"),
|
|
846
|
+
}, async ({ limit }) => {
|
|
847
|
+
return toolRegistry.execute("get_run_stats", { limit }, toolContext);
|
|
848
|
+
});
|
|
1432
849
|
// =============================================================================
|
|
1433
850
|
// Story Management Tools
|
|
1434
851
|
// =============================================================================
|
|
@@ -1436,78 +853,9 @@ Focus on analysis and recommendations. Do NOT attempt to write or modify files.`
|
|
|
1436
853
|
server.tool("claim_story", "Claim a story to work on and notify other agents via chat. Combines getting next story and posting STORY_CLAIMED message.", {
|
|
1437
854
|
agent: z.string().describe("Your agent name (e.g., 'Claude', 'Codex', 'Gemini')"),
|
|
1438
855
|
storyId: z.string().optional().describe("Specific story ID to claim (optional - defaults to next available)"),
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
if (storyId) {
|
|
1443
|
-
const prd = ralphManager.readPRD();
|
|
1444
|
-
story = prd?.userStories.find((s) => s.id === storyId && !s.passes);
|
|
1445
|
-
if (!story) {
|
|
1446
|
-
return {
|
|
1447
|
-
content: [
|
|
1448
|
-
{
|
|
1449
|
-
type: "text",
|
|
1450
|
-
text: `Story ${storyId} not found or already complete.`,
|
|
1451
|
-
},
|
|
1452
|
-
],
|
|
1453
|
-
isError: true,
|
|
1454
|
-
};
|
|
1455
|
-
}
|
|
1456
|
-
}
|
|
1457
|
-
else {
|
|
1458
|
-
story = ralphManager.getNextStory();
|
|
1459
|
-
}
|
|
1460
|
-
if (!story) {
|
|
1461
|
-
return {
|
|
1462
|
-
content: [
|
|
1463
|
-
{
|
|
1464
|
-
type: "text",
|
|
1465
|
-
text: "<promise>COMPLETE</promise>\nAll stories are complete!",
|
|
1466
|
-
},
|
|
1467
|
-
],
|
|
1468
|
-
};
|
|
1469
|
-
}
|
|
1470
|
-
// Assign in workflow state (atomic operation to prevent race conditions)
|
|
1471
|
-
const assignResult = await workflowManager.assignStory(story.id, agent);
|
|
1472
|
-
if (!assignResult.success) {
|
|
1473
|
-
return {
|
|
1474
|
-
content: [
|
|
1475
|
-
{
|
|
1476
|
-
type: "text",
|
|
1477
|
-
text: assignResult.existingAgent
|
|
1478
|
-
? `Story ${story.id} is already being worked on by ${assignResult.existingAgent}.`
|
|
1479
|
-
: assignResult.error || `Failed to assign story ${story.id} - it may have been claimed by another agent.`,
|
|
1480
|
-
},
|
|
1481
|
-
],
|
|
1482
|
-
isError: true,
|
|
1483
|
-
};
|
|
1484
|
-
}
|
|
1485
|
-
// Post claim message to chat
|
|
1486
|
-
await chatManager.writeMessage({
|
|
1487
|
-
agent,
|
|
1488
|
-
type: "STORY_CLAIMED",
|
|
1489
|
-
content: `Claiming story: ${story.id} - ${story.title}\n\nDescription: ${story.description}\n\nAcceptance Criteria:\n${story.acceptanceCriteria.map((c) => `- ${c}`).join("\n")}`,
|
|
1490
|
-
});
|
|
1491
|
-
return {
|
|
1492
|
-
content: [
|
|
1493
|
-
{
|
|
1494
|
-
type: "text",
|
|
1495
|
-
text: `Claimed story ${story.id}: ${story.title}\n\n${JSON.stringify(story, null, 2)}`,
|
|
1496
|
-
},
|
|
1497
|
-
],
|
|
1498
|
-
};
|
|
1499
|
-
}
|
|
1500
|
-
catch (error) {
|
|
1501
|
-
return {
|
|
1502
|
-
content: [
|
|
1503
|
-
{
|
|
1504
|
-
type: "text",
|
|
1505
|
-
text: `Error claiming story: ${error instanceof Error ? error.message : String(error)}`,
|
|
1506
|
-
},
|
|
1507
|
-
],
|
|
1508
|
-
isError: true,
|
|
1509
|
-
};
|
|
1510
|
-
}
|
|
856
|
+
projectPath: z.string().optional().describe("Optional project path override for this call only"),
|
|
857
|
+
}, async ({ agent, storyId, projectPath }) => {
|
|
858
|
+
return toolRegistry.execute("claim_story", { agent, storyId, projectPath }, toolContext);
|
|
1511
859
|
});
|
|
1512
860
|
// Tool: complete_story
|
|
1513
861
|
server.tool("complete_story", "Mark a story complete and notify other agents via chat. Combines marking complete and posting STORY_COMPLETE message.", {
|
|
@@ -1516,241 +864,32 @@ Focus on analysis and recommendations. Do NOT attempt to write or modify files.`
|
|
|
1516
864
|
summary: z.string().describe("Brief summary of what was implemented"),
|
|
1517
865
|
filesModified: z.array(z.string()).optional().describe("List of files modified"),
|
|
1518
866
|
learnings: z.string().optional().describe("Learnings/gotchas for future iterations"),
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
const isOwner = workflowManager.isAgentOwner(storyId, agent);
|
|
1523
|
-
const existingAssignment = workflowManager.isStoryAssigned(storyId);
|
|
1524
|
-
if (existingAssignment && !isOwner) {
|
|
1525
|
-
return {
|
|
1526
|
-
content: [
|
|
1527
|
-
{
|
|
1528
|
-
type: "text",
|
|
1529
|
-
text: `Cannot complete story ${storyId} - it is assigned to ${existingAssignment.agent}, not ${agent}.`,
|
|
1530
|
-
},
|
|
1531
|
-
],
|
|
1532
|
-
isError: true,
|
|
1533
|
-
};
|
|
1534
|
-
}
|
|
1535
|
-
const story = ralphManager.markStoryComplete(storyId, summary);
|
|
1536
|
-
if (!story) {
|
|
1537
|
-
return {
|
|
1538
|
-
content: [
|
|
1539
|
-
{
|
|
1540
|
-
type: "text",
|
|
1541
|
-
text: `Story ${storyId} not found.`,
|
|
1542
|
-
},
|
|
1543
|
-
],
|
|
1544
|
-
isError: true,
|
|
1545
|
-
};
|
|
1546
|
-
}
|
|
1547
|
-
// Mark assignment as completed in workflow state
|
|
1548
|
-
await workflowManager.completeAssignment(storyId);
|
|
1549
|
-
// Build progress entry
|
|
1550
|
-
let progressEntry = `Story: ${storyId} - ${story.title}\nAgent: ${agent}\n\n${summary}`;
|
|
1551
|
-
if (filesModified && filesModified.length > 0) {
|
|
1552
|
-
progressEntry += `\n\nFiles Modified:\n${filesModified.map((f) => `- ${f}`).join("\n")}`;
|
|
1553
|
-
}
|
|
1554
|
-
if (learnings) {
|
|
1555
|
-
progressEntry += `\n\n**Learnings for future iterations:**\n${learnings}`;
|
|
1556
|
-
}
|
|
1557
|
-
// Append to progress file
|
|
1558
|
-
ralphManager.appendProgress(progressEntry);
|
|
1559
|
-
// Post completion message to chat
|
|
1560
|
-
let chatContent = `Completed: ${storyId} - ${story.title}\n\n${summary}`;
|
|
1561
|
-
if (filesModified && filesModified.length > 0) {
|
|
1562
|
-
chatContent += `\n\nFiles: ${filesModified.join(", ")}`;
|
|
1563
|
-
}
|
|
1564
|
-
await chatManager.writeMessage({
|
|
1565
|
-
agent,
|
|
1566
|
-
type: "STORY_COMPLETE",
|
|
1567
|
-
content: chatContent,
|
|
1568
|
-
});
|
|
1569
|
-
// Auto-create savepoint
|
|
1570
|
-
const savepoint = ralphManager.createSavepoint(storyId, `Completed: ${storyId} - ${story.title}`);
|
|
1571
|
-
const prdSummary = ralphManager.getCompletionSummary();
|
|
1572
|
-
const allWorkDone = ralphManager.isAllWorkComplete();
|
|
1573
|
-
// If all work is done, notify READY_TO_TEST
|
|
1574
|
-
if (allWorkDone) {
|
|
1575
|
-
await chatManager.writeMessage({
|
|
1576
|
-
agent: "NightShift",
|
|
1577
|
-
type: "READY_TO_TEST",
|
|
1578
|
-
content: `All stories and bugs complete!\n\nStories: ${prdSummary.complete}/${prdSummary.total}\n\nReady for human review and testing.`,
|
|
1579
|
-
});
|
|
1580
|
-
}
|
|
1581
|
-
let response = `Story ${storyId} completed!\n\nProgress: ${prdSummary.complete}/${prdSummary.total} (${prdSummary.percentComplete}%)`;
|
|
1582
|
-
if (savepoint.success) {
|
|
1583
|
-
response += `\nSavepoint: ${savepoint.tag}`;
|
|
1584
|
-
}
|
|
1585
|
-
if (allWorkDone) {
|
|
1586
|
-
response += "\n\n<promise>COMPLETE</promise>\nAll work complete! Ready to test.";
|
|
1587
|
-
}
|
|
1588
|
-
return {
|
|
1589
|
-
content: [
|
|
1590
|
-
{
|
|
1591
|
-
type: "text",
|
|
1592
|
-
text: response,
|
|
1593
|
-
},
|
|
1594
|
-
],
|
|
1595
|
-
};
|
|
1596
|
-
}
|
|
1597
|
-
catch (error) {
|
|
1598
|
-
return {
|
|
1599
|
-
content: [
|
|
1600
|
-
{
|
|
1601
|
-
type: "text",
|
|
1602
|
-
text: `Error completing story: ${error instanceof Error ? error.message : String(error)}`,
|
|
1603
|
-
},
|
|
1604
|
-
],
|
|
1605
|
-
isError: true,
|
|
1606
|
-
};
|
|
1607
|
-
}
|
|
867
|
+
projectPath: z.string().optional().describe("Optional project path override for this call only"),
|
|
868
|
+
}, async ({ agent, storyId, summary, filesModified, learnings, projectPath }) => {
|
|
869
|
+
return toolRegistry.execute("complete_story", { agent, storyId, summary, filesModified, learnings, projectPath }, toolContext);
|
|
1608
870
|
});
|
|
1609
871
|
// =============================================================================
|
|
1610
872
|
// Bug Management Tools
|
|
1611
873
|
// =============================================================================
|
|
1612
874
|
// Tool: read_bugs
|
|
1613
|
-
server.tool("read_bugs", "Read the bugs.json file. Returns list of bugs with their fixed status.", {
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
content: [
|
|
1618
|
-
{
|
|
1619
|
-
type: "text",
|
|
1620
|
-
text: "No bugs.json found. Create one to track bugs.",
|
|
1621
|
-
},
|
|
1622
|
-
],
|
|
1623
|
-
};
|
|
1624
|
-
}
|
|
1625
|
-
const bugList = ralphManager.readBugs();
|
|
1626
|
-
const summary = ralphManager.getBugsSummary();
|
|
1627
|
-
return {
|
|
1628
|
-
content: [
|
|
1629
|
-
{
|
|
1630
|
-
type: "text",
|
|
1631
|
-
text: JSON.stringify({ bugs: bugList, summary }, null, 2),
|
|
1632
|
-
},
|
|
1633
|
-
],
|
|
1634
|
-
};
|
|
1635
|
-
}
|
|
1636
|
-
catch (error) {
|
|
1637
|
-
return {
|
|
1638
|
-
content: [
|
|
1639
|
-
{
|
|
1640
|
-
type: "text",
|
|
1641
|
-
text: `Error reading bugs: ${error instanceof Error ? error.message : String(error)}`,
|
|
1642
|
-
},
|
|
1643
|
-
],
|
|
1644
|
-
isError: true,
|
|
1645
|
-
};
|
|
1646
|
-
}
|
|
875
|
+
server.tool("read_bugs", "Read the bugs.json file. Returns list of bugs with their fixed status.", {
|
|
876
|
+
projectPath: z.string().optional().describe("Optional project path override for this call only"),
|
|
877
|
+
}, async ({ projectPath }) => {
|
|
878
|
+
return toolRegistry.execute("read_bugs", { projectPath }, toolContext);
|
|
1647
879
|
});
|
|
1648
880
|
// Tool: get_next_bug
|
|
1649
|
-
server.tool("get_next_bug", "Get the next bug to fix (highest priority unfixed bug).", {
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
const summary = ralphManager.getBugsSummary();
|
|
1654
|
-
if (summary.total === 0) {
|
|
1655
|
-
return {
|
|
1656
|
-
content: [
|
|
1657
|
-
{
|
|
1658
|
-
type: "text",
|
|
1659
|
-
text: "No bugs.json found or no bugs defined.",
|
|
1660
|
-
},
|
|
1661
|
-
],
|
|
1662
|
-
};
|
|
1663
|
-
}
|
|
1664
|
-
return {
|
|
1665
|
-
content: [
|
|
1666
|
-
{
|
|
1667
|
-
type: "text",
|
|
1668
|
-
text: "All bugs are fixed!",
|
|
1669
|
-
},
|
|
1670
|
-
],
|
|
1671
|
-
};
|
|
1672
|
-
}
|
|
1673
|
-
return {
|
|
1674
|
-
content: [
|
|
1675
|
-
{
|
|
1676
|
-
type: "text",
|
|
1677
|
-
text: JSON.stringify(bug, null, 2),
|
|
1678
|
-
},
|
|
1679
|
-
],
|
|
1680
|
-
};
|
|
1681
|
-
}
|
|
1682
|
-
catch (error) {
|
|
1683
|
-
return {
|
|
1684
|
-
content: [
|
|
1685
|
-
{
|
|
1686
|
-
type: "text",
|
|
1687
|
-
text: `Error getting next bug: ${error instanceof Error ? error.message : String(error)}`,
|
|
1688
|
-
},
|
|
1689
|
-
],
|
|
1690
|
-
isError: true,
|
|
1691
|
-
};
|
|
1692
|
-
}
|
|
881
|
+
server.tool("get_next_bug", "Get the next bug to fix (highest priority unfixed bug).", {
|
|
882
|
+
projectPath: z.string().optional().describe("Optional project path override for this call only"),
|
|
883
|
+
}, async ({ projectPath }) => {
|
|
884
|
+
return toolRegistry.execute("get_next_bug", { projectPath }, toolContext);
|
|
1693
885
|
});
|
|
1694
886
|
// Tool: claim_bug
|
|
1695
887
|
server.tool("claim_bug", "Claim a bug to work on and notify other agents via chat.", {
|
|
1696
888
|
agent: z.string().describe("Your agent name"),
|
|
1697
889
|
bugId: z.string().optional().describe("Specific bug ID to claim (optional - defaults to next available)"),
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
if (bugId) {
|
|
1702
|
-
bug = ralphManager.getBug(bugId);
|
|
1703
|
-
if (!bug || bug.fixed) {
|
|
1704
|
-
return {
|
|
1705
|
-
content: [
|
|
1706
|
-
{
|
|
1707
|
-
type: "text",
|
|
1708
|
-
text: `Bug ${bugId} not found or already fixed.`,
|
|
1709
|
-
},
|
|
1710
|
-
],
|
|
1711
|
-
isError: true,
|
|
1712
|
-
};
|
|
1713
|
-
}
|
|
1714
|
-
}
|
|
1715
|
-
else {
|
|
1716
|
-
bug = ralphManager.getNextBug();
|
|
1717
|
-
}
|
|
1718
|
-
if (!bug) {
|
|
1719
|
-
return {
|
|
1720
|
-
content: [
|
|
1721
|
-
{
|
|
1722
|
-
type: "text",
|
|
1723
|
-
text: "No unfixed bugs available.",
|
|
1724
|
-
},
|
|
1725
|
-
],
|
|
1726
|
-
};
|
|
1727
|
-
}
|
|
1728
|
-
// Post claim message to chat
|
|
1729
|
-
await chatManager.writeMessage({
|
|
1730
|
-
agent,
|
|
1731
|
-
type: "BUG_CLAIMED",
|
|
1732
|
-
content: `Claiming bug: ${bug.id} - ${bug.title}\n\nDescription: ${bug.description}${bug.stepsToReproduce ? `\n\nSteps to reproduce:\n${bug.stepsToReproduce.map((s) => `- ${s}`).join("\n")}` : ""}`,
|
|
1733
|
-
});
|
|
1734
|
-
return {
|
|
1735
|
-
content: [
|
|
1736
|
-
{
|
|
1737
|
-
type: "text",
|
|
1738
|
-
text: `Claimed bug ${bug.id}: ${bug.title}\n\n${JSON.stringify(bug, null, 2)}`,
|
|
1739
|
-
},
|
|
1740
|
-
],
|
|
1741
|
-
};
|
|
1742
|
-
}
|
|
1743
|
-
catch (error) {
|
|
1744
|
-
return {
|
|
1745
|
-
content: [
|
|
1746
|
-
{
|
|
1747
|
-
type: "text",
|
|
1748
|
-
text: `Error claiming bug: ${error instanceof Error ? error.message : String(error)}`,
|
|
1749
|
-
},
|
|
1750
|
-
],
|
|
1751
|
-
isError: true,
|
|
1752
|
-
};
|
|
1753
|
-
}
|
|
890
|
+
projectPath: z.string().optional().describe("Optional project path override for this call only"),
|
|
891
|
+
}, async ({ agent, bugId, projectPath }) => {
|
|
892
|
+
return toolRegistry.execute("claim_bug", { agent, bugId, projectPath }, toolContext);
|
|
1754
893
|
});
|
|
1755
894
|
// Tool: mark_bug_fixed
|
|
1756
895
|
server.tool("mark_bug_fixed", "Mark a bug as fixed, create savepoint, and notify via chat. If all work is done, sends READY_TO_TEST.", {
|
|
@@ -1758,76 +897,9 @@ Focus on analysis and recommendations. Do NOT attempt to write or modify files.`
|
|
|
1758
897
|
bugId: z.string().describe("The bug ID to mark fixed"),
|
|
1759
898
|
summary: z.string().describe("Brief summary of the fix"),
|
|
1760
899
|
filesModified: z.array(z.string()).optional().describe("List of files modified"),
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
if (!bug) {
|
|
1765
|
-
return {
|
|
1766
|
-
content: [
|
|
1767
|
-
{
|
|
1768
|
-
type: "text",
|
|
1769
|
-
text: `Bug ${bugId} not found.`,
|
|
1770
|
-
},
|
|
1771
|
-
],
|
|
1772
|
-
isError: true,
|
|
1773
|
-
};
|
|
1774
|
-
}
|
|
1775
|
-
// Log to progress
|
|
1776
|
-
let progressEntry = `Bug Fixed: ${bugId} - ${bug.title}\nAgent: ${agent}\n\n${summary}`;
|
|
1777
|
-
if (filesModified && filesModified.length > 0) {
|
|
1778
|
-
progressEntry += `\n\nFiles Modified:\n${filesModified.map((f) => `- ${f}`).join("\n")}`;
|
|
1779
|
-
}
|
|
1780
|
-
ralphManager.appendProgress(progressEntry);
|
|
1781
|
-
// Post to chat
|
|
1782
|
-
let chatContent = `Fixed: ${bugId} - ${bug.title}\n\n${summary}`;
|
|
1783
|
-
if (filesModified && filesModified.length > 0) {
|
|
1784
|
-
chatContent += `\n\nFiles: ${filesModified.join(", ")}`;
|
|
1785
|
-
}
|
|
1786
|
-
await chatManager.writeMessage({
|
|
1787
|
-
agent,
|
|
1788
|
-
type: "BUG_FIXED",
|
|
1789
|
-
content: chatContent,
|
|
1790
|
-
});
|
|
1791
|
-
// Auto-create savepoint
|
|
1792
|
-
const savepoint = ralphManager.createSavepoint(bugId, `Fixed: ${bugId} - ${bug.title}`);
|
|
1793
|
-
const bugsSummary = ralphManager.getBugsSummary();
|
|
1794
|
-
const allWorkDone = ralphManager.isAllWorkComplete();
|
|
1795
|
-
// If all work is done, notify READY_TO_TEST
|
|
1796
|
-
if (allWorkDone) {
|
|
1797
|
-
const prdSummary = ralphManager.getCompletionSummary();
|
|
1798
|
-
await chatManager.writeMessage({
|
|
1799
|
-
agent: "NightShift",
|
|
1800
|
-
type: "READY_TO_TEST",
|
|
1801
|
-
content: `All stories and bugs complete!\n\nStories: ${prdSummary.complete}/${prdSummary.total}\nBugs: ${bugsSummary.fixed}/${bugsSummary.total}\n\nReady for human review and testing.`,
|
|
1802
|
-
});
|
|
1803
|
-
}
|
|
1804
|
-
let response = `Bug ${bugId} fixed!\n\nBugs: ${bugsSummary.fixed}/${bugsSummary.total} (${bugsSummary.percentFixed}%)`;
|
|
1805
|
-
if (savepoint.success) {
|
|
1806
|
-
response += `\nSavepoint: ${savepoint.tag}`;
|
|
1807
|
-
}
|
|
1808
|
-
if (allWorkDone) {
|
|
1809
|
-
response += "\n\n<promise>COMPLETE</promise>\nAll work complete! Ready to test.";
|
|
1810
|
-
}
|
|
1811
|
-
return {
|
|
1812
|
-
content: [
|
|
1813
|
-
{
|
|
1814
|
-
type: "text",
|
|
1815
|
-
text: response,
|
|
1816
|
-
},
|
|
1817
|
-
],
|
|
1818
|
-
};
|
|
1819
|
-
}
|
|
1820
|
-
catch (error) {
|
|
1821
|
-
return {
|
|
1822
|
-
content: [
|
|
1823
|
-
{
|
|
1824
|
-
type: "text",
|
|
1825
|
-
text: `Error marking bug fixed: ${error instanceof Error ? error.message : String(error)}`,
|
|
1826
|
-
},
|
|
1827
|
-
],
|
|
1828
|
-
isError: true,
|
|
1829
|
-
};
|
|
1830
|
-
}
|
|
900
|
+
projectPath: z.string().optional().describe("Optional project path override for this call only"),
|
|
901
|
+
}, async ({ agent, bugId, summary, filesModified, projectPath }) => {
|
|
902
|
+
return toolRegistry.execute("mark_bug_fixed", { agent, bugId, summary, filesModified, projectPath }, toolContext);
|
|
1831
903
|
});
|
|
1832
904
|
// =============================================================================
|
|
1833
905
|
// Savepoint Tools
|
|
@@ -1962,7 +1034,7 @@ Focus on analysis and recommendations. Do NOT attempt to write or modify files.`
|
|
|
1962
1034
|
const issues = [];
|
|
1963
1035
|
const suggestions = [];
|
|
1964
1036
|
// Check project path
|
|
1965
|
-
checks.push(`
|
|
1037
|
+
checks.push(`Current project path: ${PROJECT_PATH}`);
|
|
1966
1038
|
// Check for prd.json
|
|
1967
1039
|
if (ralphManager.hasPRD()) {
|
|
1968
1040
|
const summary = ralphManager.getCompletionSummary();
|
|
@@ -2118,7 +1190,7 @@ args = ["${PROJECT_PATH}"]
|
|
|
2118
1190
|
const fixes = [];
|
|
2119
1191
|
diagnostics.push(`# NightShift Debug Report\n`);
|
|
2120
1192
|
diagnostics.push(`Timestamp: ${new Date().toISOString()}`);
|
|
2121
|
-
diagnostics.push(`
|
|
1193
|
+
diagnostics.push(`Current project path: ${PROJECT_PATH}\n`);
|
|
2122
1194
|
// 1. Check file system
|
|
2123
1195
|
diagnostics.push(`## File System Checks`);
|
|
2124
1196
|
// Check project directory exists and is writable
|
|
@@ -2371,7 +1443,7 @@ ${c.dim} The responsible kind of multi-agent chaos.${c.reset}
|
|
|
2371
1443
|
${c.yellow}${c.bold} ⚡ Project${c.reset}
|
|
2372
1444
|
${c.dim}Path:${c.reset} ${PROJECT_PATH}
|
|
2373
1445
|
${c.dim}Chat:${c.reset} .robot-chat/chat.txt
|
|
2374
|
-
${c.dim}Mode:${c.reset} ${REGISTRATION_MODE} (${toolCounts.total} actions via ${REGISTRATION_MODE === "minimal" ? "
|
|
1446
|
+
${c.dim}Mode:${c.reset} ${REGISTRATION_MODE} (${toolCounts.total} actions via ${REGISTRATION_MODE === "minimal" ? "3" : REGISTRATION_MODE === "legacy" ? toolCounts.total + 1 : toolCounts.total + 3} tools)
|
|
2375
1447
|
`;
|
|
2376
1448
|
const agentSection = `
|
|
2377
1449
|
${c.green}${c.bold} 🤖 Agents${c.reset} ${c.dim}(${installed.length}/${agents.length} available)${c.reset}
|
|
@@ -2408,18 +1480,19 @@ ${c.dim} Ready for multi-agent collaboration!${c.reset}
|
|
|
2408
1480
|
}
|
|
2409
1481
|
// Register tools based on mode
|
|
2410
1482
|
function registerTools() {
|
|
1483
|
+
registerProjectPathTool();
|
|
2411
1484
|
switch (REGISTRATION_MODE) {
|
|
2412
1485
|
case "minimal":
|
|
2413
|
-
//
|
|
1486
|
+
// Meta-tools + set_project_path
|
|
2414
1487
|
registerMetaTools();
|
|
2415
1488
|
break;
|
|
2416
1489
|
case "legacy":
|
|
2417
|
-
//
|
|
1490
|
+
// Individual tools + set_project_path
|
|
2418
1491
|
registerIndividualTools();
|
|
2419
1492
|
break;
|
|
2420
1493
|
case "hybrid":
|
|
2421
1494
|
default:
|
|
2422
|
-
//
|
|
1495
|
+
// Meta-tools + individual tools + set_project_path
|
|
2423
1496
|
registerMetaTools();
|
|
2424
1497
|
registerIndividualTools();
|
|
2425
1498
|
break;
|