@promptprojectmanager/mcp-server 4.5.7 → 4.6.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/index.js +264 -209
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-ATAMNB7A.js +0 -111
- package/dist/chunk-ATAMNB7A.js.map +0 -1
- package/dist/watcher/watcher_daemon.d.ts +0 -2
- package/dist/watcher/watcher_daemon.js +0 -338
- package/dist/watcher/watcher_daemon.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,14 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
getLogFile,
|
|
4
|
-
getWatcherDir,
|
|
5
|
-
isProcessRunning,
|
|
6
|
-
isWatcherRunning,
|
|
7
|
-
readPid,
|
|
8
|
-
readStatus,
|
|
9
|
-
removePid,
|
|
10
|
-
writeStatus
|
|
11
|
-
} from "./chunk-ATAMNB7A.js";
|
|
12
2
|
|
|
13
3
|
// src/index.ts
|
|
14
4
|
import minimist from "minimist";
|
|
@@ -111,168 +101,258 @@ function parseWatcherStartArgs(args) {
|
|
|
111
101
|
};
|
|
112
102
|
}
|
|
113
103
|
|
|
104
|
+
// src/watcher/watcher_state.ts
|
|
105
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
|
|
106
|
+
import { join } from "path";
|
|
107
|
+
function getWatcherDir(workingDirectory) {
|
|
108
|
+
const dir = join(workingDirectory, ".ppm", "yolo");
|
|
109
|
+
if (!existsSync(dir)) {
|
|
110
|
+
mkdirSync(dir, { recursive: true });
|
|
111
|
+
}
|
|
112
|
+
return dir;
|
|
113
|
+
}
|
|
114
|
+
function getStatusFile(workingDirectory) {
|
|
115
|
+
return join(getWatcherDir(workingDirectory), "status.json");
|
|
116
|
+
}
|
|
117
|
+
function writeStatus(workingDirectory, status) {
|
|
118
|
+
const statusFile = getStatusFile(workingDirectory);
|
|
119
|
+
writeFileSync(statusFile, JSON.stringify(status, null, 2), "utf-8");
|
|
120
|
+
}
|
|
121
|
+
function readStatus(workingDirectory) {
|
|
122
|
+
const statusFile = getStatusFile(workingDirectory);
|
|
123
|
+
if (!existsSync(statusFile)) {
|
|
124
|
+
return void 0;
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
const content = readFileSync(statusFile, "utf-8");
|
|
128
|
+
return JSON.parse(content);
|
|
129
|
+
} catch {
|
|
130
|
+
return void 0;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// src/watcher/watcher_orchestrator.ts
|
|
135
|
+
function buildOrchestratorInstructions(config, pendingTickets) {
|
|
136
|
+
const pollIntervalSec = Math.round(config.pollIntervalMs / 1e3);
|
|
137
|
+
return `# \u{1F916} YOLO Orchestrator Active
|
|
138
|
+
|
|
139
|
+
You are now the **YOLO Orchestrator** for project \`${config.projectSlug}\`.
|
|
140
|
+
|
|
141
|
+
## Current Configuration
|
|
142
|
+
- **Poll Interval**: ${pollIntervalSec}s
|
|
143
|
+
- **Max Parallel**: ${config.maxParallel}
|
|
144
|
+
- **Ticket Timeout**: ${Math.round(config.ticketTimeout / 1e3 / 60)} minutes
|
|
145
|
+
- **Working Directory**: ${config.workingDirectory}
|
|
146
|
+
|
|
147
|
+
## Pending YOLO Tickets
|
|
148
|
+
${pendingTickets.length > 0 ? formatPendingTickets(pendingTickets) : "_No tickets with YOLO flag currently in queue._"}
|
|
149
|
+
|
|
150
|
+
## Your Job
|
|
151
|
+
|
|
152
|
+
${pendingTickets.length > 0 ? `
|
|
153
|
+
### \u{1F4E5} Execute Pending Tickets
|
|
154
|
+
|
|
155
|
+
For each pending ticket above:
|
|
156
|
+
|
|
157
|
+
1. **Claim it** using \`tickets_work\` with the ticket slug
|
|
158
|
+
2. **Spawn a Task sub-agent** to execute it:
|
|
159
|
+
\`\`\`
|
|
160
|
+
Task tool with:
|
|
161
|
+
- subagent_type: "general-purpose"
|
|
162
|
+
- prompt: The ticket content (what needs to be done)
|
|
163
|
+
- run_in_background: true (for parallel execution)
|
|
164
|
+
\`\`\`
|
|
165
|
+
3. **Report** the result when the sub-agent completes
|
|
166
|
+
|
|
167
|
+
### Sub-Agent Prompt Template
|
|
168
|
+
|
|
169
|
+
When spawning a sub-agent, use this prompt structure:
|
|
170
|
+
|
|
171
|
+
\`\`\`
|
|
172
|
+
Execute this ticket autonomously:
|
|
173
|
+
|
|
174
|
+
## Ticket: {ticketSlug}
|
|
175
|
+
|
|
176
|
+
{ticketContent}
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
When complete:
|
|
181
|
+
1. Use \`tickets_close\` to close the ticket with slug "{ticketSlug}"
|
|
182
|
+
2. Provide a brief summary of what was accomplished
|
|
183
|
+
|
|
184
|
+
If you encounter blocking issues, use \`tickets_update\` to log your findings.
|
|
185
|
+
\`\`\`
|
|
186
|
+
` : `
|
|
187
|
+
### \u{1F504} No Tickets Ready
|
|
188
|
+
|
|
189
|
+
No YOLO tickets are currently pending. You can:
|
|
190
|
+
- Say **"poll"** or **"check"** to refresh the ticket list
|
|
191
|
+
- Say **"stop"** to exit orchestrator mode
|
|
192
|
+
- Wait for tickets to be flagged with YOLO in the PPM UI
|
|
193
|
+
`}
|
|
194
|
+
|
|
195
|
+
## Commands
|
|
196
|
+
|
|
197
|
+
- **"poll"** or **"continue"** - Check for new YOLO tickets
|
|
198
|
+
- **"status"** - Show current orchestrator status
|
|
199
|
+
- **"stop"** or **"exit"** - Stop the orchestrator
|
|
200
|
+
|
|
201
|
+
## Status Tracking
|
|
202
|
+
|
|
203
|
+
Use \`tickets_yolo_status\` to:
|
|
204
|
+
- See pending YOLO tickets
|
|
205
|
+
- Check currently executing tickets
|
|
206
|
+
- View processed ticket count
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
_Orchestrator started at ${(/* @__PURE__ */ new Date()).toISOString()}_
|
|
211
|
+
_Say "poll" to check for tickets, "stop" to exit._`;
|
|
212
|
+
}
|
|
213
|
+
function formatPendingTickets(tickets) {
|
|
214
|
+
return tickets.map((t, i) => {
|
|
215
|
+
const num = t.ticketNumber ? `#${t.ticketNumber}` : "";
|
|
216
|
+
const title = t.slug;
|
|
217
|
+
const preview = t.content.slice(0, 100).replace(/\n/g, " ");
|
|
218
|
+
return `${i + 1}. **${num} ${title}**
|
|
219
|
+
_${preview}${t.content.length > 100 ? "..." : ""}_`;
|
|
220
|
+
}).join("\n\n");
|
|
221
|
+
}
|
|
222
|
+
function formatOrchestratorStatus(status, pendingTickets) {
|
|
223
|
+
if (!status) {
|
|
224
|
+
return `# YOLO Orchestrator Status
|
|
225
|
+
|
|
226
|
+
**State**: Not started
|
|
227
|
+
|
|
228
|
+
_Use \`tickets_yolo_start\` to begin orchestration._`;
|
|
229
|
+
}
|
|
230
|
+
const lines = [
|
|
231
|
+
"# YOLO Orchestrator Status",
|
|
232
|
+
"",
|
|
233
|
+
`**State**: ${status.state}`,
|
|
234
|
+
`**Project**: ${status.projectSlug}`
|
|
235
|
+
];
|
|
236
|
+
if (status.startedAt) {
|
|
237
|
+
lines.push(`**Started**: ${status.startedAt}`);
|
|
238
|
+
}
|
|
239
|
+
if (status.lastPollAt) {
|
|
240
|
+
lines.push(`**Last Poll**: ${status.lastPollAt}`);
|
|
241
|
+
}
|
|
242
|
+
lines.push(`**Tickets Processed**: ${status.ticketsProcessed}`);
|
|
243
|
+
if (status.currentlyExecuting && status.currentlyExecuting.length > 0) {
|
|
244
|
+
lines.push("");
|
|
245
|
+
lines.push("## Currently Executing");
|
|
246
|
+
for (const exec of status.currentlyExecuting) {
|
|
247
|
+
const num = exec.ticketNumber ? `#${exec.ticketNumber}` : "";
|
|
248
|
+
const elapsed = getElapsedTime(exec.startedAt);
|
|
249
|
+
lines.push(`- **${num} ${exec.ticketSlug}** (${elapsed})`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
lines.push("");
|
|
253
|
+
lines.push("## Pending YOLO Tickets");
|
|
254
|
+
if (pendingTickets.length > 0) {
|
|
255
|
+
for (const ticket of pendingTickets) {
|
|
256
|
+
const num = ticket.ticketNumber ? `#${ticket.ticketNumber}` : "";
|
|
257
|
+
lines.push(`- **${num} ${ticket.slug}**`);
|
|
258
|
+
}
|
|
259
|
+
} else {
|
|
260
|
+
lines.push("_No tickets pending_");
|
|
261
|
+
}
|
|
262
|
+
return lines.join("\n");
|
|
263
|
+
}
|
|
264
|
+
function getElapsedTime(startedAt) {
|
|
265
|
+
const start = new Date(startedAt).getTime();
|
|
266
|
+
const elapsed = Date.now() - start;
|
|
267
|
+
const minutes = Math.floor(elapsed / 1e3 / 60);
|
|
268
|
+
const seconds = Math.floor(elapsed / 1e3 % 60);
|
|
269
|
+
if (minutes > 0) {
|
|
270
|
+
return `${minutes}m ${seconds}s`;
|
|
271
|
+
}
|
|
272
|
+
return `${seconds}s`;
|
|
273
|
+
}
|
|
274
|
+
|
|
114
275
|
// src/watcher/watcher_controller.ts
|
|
115
|
-
|
|
116
|
-
import { openSync, appendFileSync } from "fs";
|
|
117
|
-
import { dirname, join } from "path";
|
|
118
|
-
import { fileURLToPath } from "url";
|
|
119
|
-
async function startWatcher(baseConfig, args) {
|
|
276
|
+
function startOrchestrator(baseConfig, args, pendingTickets) {
|
|
120
277
|
const workingDirectory = args?.workingDirectory ?? baseConfig.workingDirectory;
|
|
121
|
-
|
|
122
|
-
const existingPid = readPid(workingDirectory);
|
|
123
|
-
const existingStatus = readStatus(workingDirectory);
|
|
124
|
-
return {
|
|
125
|
-
success: true,
|
|
126
|
-
message: "Watcher is already running",
|
|
127
|
-
pid: existingPid,
|
|
128
|
-
alreadyRunning: true,
|
|
129
|
-
config: existingStatus?.config
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
const config = {
|
|
278
|
+
const orchestratorConfig = {
|
|
133
279
|
projectSlug: baseConfig.projectSlug,
|
|
134
|
-
projectToken: baseConfig.projectToken,
|
|
135
|
-
convexUrl: baseConfig.convexUrl,
|
|
136
|
-
workingDirectory,
|
|
137
280
|
pollIntervalMs: args?.pollIntervalMs ?? 3e4,
|
|
138
281
|
maxParallel: args?.maxParallel ?? 1,
|
|
139
282
|
ticketTimeout: args?.ticketTimeout ?? 18e5,
|
|
140
|
-
|
|
283
|
+
workingDirectory
|
|
141
284
|
};
|
|
142
|
-
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
});
|
|
158
|
-
child.unref();
|
|
159
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
160
|
-
const pid = readPid(workingDirectory);
|
|
161
|
-
if (pid && isProcessRunning(pid)) {
|
|
162
|
-
return {
|
|
163
|
-
success: true,
|
|
164
|
-
message: "Watcher started successfully",
|
|
165
|
-
pid,
|
|
166
|
-
alreadyRunning: false,
|
|
167
|
-
config: {
|
|
168
|
-
projectSlug: config.projectSlug,
|
|
169
|
-
convexUrl: config.convexUrl,
|
|
170
|
-
pollIntervalMs: config.pollIntervalMs,
|
|
171
|
-
maxParallel: config.maxParallel,
|
|
172
|
-
ticketTimeout: config.ticketTimeout,
|
|
173
|
-
enableNotifications: config.enableNotifications,
|
|
174
|
-
workingDirectory: config.workingDirectory
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
} else {
|
|
178
|
-
return {
|
|
179
|
-
success: false,
|
|
180
|
-
message: "Daemon process started but failed to initialize. Check logs in .ppm/yolo/"
|
|
181
|
-
};
|
|
285
|
+
getWatcherDir(workingDirectory);
|
|
286
|
+
const initialStatus = {
|
|
287
|
+
state: "running",
|
|
288
|
+
projectSlug: baseConfig.projectSlug,
|
|
289
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
290
|
+
ticketsProcessed: 0,
|
|
291
|
+
currentlyExecuting: [],
|
|
292
|
+
config: {
|
|
293
|
+
projectSlug: baseConfig.projectSlug,
|
|
294
|
+
convexUrl: baseConfig.convexUrl,
|
|
295
|
+
pollIntervalMs: orchestratorConfig.pollIntervalMs,
|
|
296
|
+
maxParallel: orchestratorConfig.maxParallel,
|
|
297
|
+
ticketTimeout: orchestratorConfig.ticketTimeout,
|
|
298
|
+
enableNotifications: args?.enableNotifications ?? true,
|
|
299
|
+
workingDirectory
|
|
182
300
|
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
301
|
+
};
|
|
302
|
+
writeStatus(workingDirectory, initialStatus);
|
|
303
|
+
const instructions = buildOrchestratorInstructions(orchestratorConfig, pendingTickets);
|
|
304
|
+
return {
|
|
305
|
+
success: true,
|
|
306
|
+
message: "Orchestrator started",
|
|
307
|
+
instructions,
|
|
308
|
+
config: initialStatus.config,
|
|
309
|
+
alreadyRunning: false
|
|
310
|
+
};
|
|
190
311
|
}
|
|
191
|
-
|
|
192
|
-
const
|
|
193
|
-
if (!
|
|
312
|
+
function stopOrchestrator(workingDirectory) {
|
|
313
|
+
const status = readStatus(workingDirectory);
|
|
314
|
+
if (!status) {
|
|
194
315
|
return {
|
|
195
316
|
success: true,
|
|
196
|
-
message: "No
|
|
317
|
+
message: "No orchestrator session found",
|
|
197
318
|
wasRunning: false
|
|
198
319
|
};
|
|
199
320
|
}
|
|
200
|
-
if (
|
|
201
|
-
removePid(workingDirectory);
|
|
202
|
-
const status = readStatus(workingDirectory);
|
|
203
|
-
if (status) {
|
|
204
|
-
writeStatus(workingDirectory, { ...status, state: "stopped" });
|
|
205
|
-
}
|
|
321
|
+
if (status.state !== "running") {
|
|
206
322
|
return {
|
|
207
323
|
success: true,
|
|
208
|
-
message:
|
|
324
|
+
message: `Orchestrator was already ${status.state}`,
|
|
209
325
|
wasRunning: false
|
|
210
326
|
};
|
|
211
327
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if (isProcessRunning(pid)) {
|
|
222
|
-
try {
|
|
223
|
-
process.kill(pid, "SIGKILL");
|
|
224
|
-
} catch {
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
removePid(workingDirectory);
|
|
228
|
-
const status = readStatus(workingDirectory);
|
|
229
|
-
if (status) {
|
|
230
|
-
writeStatus(workingDirectory, { ...status, state: "stopped" });
|
|
231
|
-
}
|
|
232
|
-
return {
|
|
233
|
-
success: true,
|
|
234
|
-
message: "Watcher stopped successfully",
|
|
235
|
-
wasRunning: true
|
|
236
|
-
};
|
|
237
|
-
} catch (error) {
|
|
238
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
239
|
-
return {
|
|
240
|
-
success: false,
|
|
241
|
-
message: `Failed to stop watcher: ${errorMessage}`,
|
|
242
|
-
wasRunning: true
|
|
243
|
-
};
|
|
244
|
-
}
|
|
328
|
+
writeStatus(workingDirectory, {
|
|
329
|
+
...status,
|
|
330
|
+
state: "stopped"
|
|
331
|
+
});
|
|
332
|
+
return {
|
|
333
|
+
success: true,
|
|
334
|
+
message: "Orchestrator stopped",
|
|
335
|
+
wasRunning: true
|
|
336
|
+
};
|
|
245
337
|
}
|
|
246
|
-
function
|
|
338
|
+
function getOrchestratorStatus(workingDirectory, pendingTickets) {
|
|
247
339
|
const status = readStatus(workingDirectory);
|
|
340
|
+
const formattedStatus = formatOrchestratorStatus(status ?? null, pendingTickets);
|
|
248
341
|
if (!status) {
|
|
249
342
|
return {
|
|
250
343
|
success: true,
|
|
251
|
-
message: "No
|
|
344
|
+
message: "No orchestrator session has been started for this project",
|
|
345
|
+
pendingTickets,
|
|
346
|
+
formattedStatus
|
|
252
347
|
};
|
|
253
348
|
}
|
|
254
|
-
if (status.state === "running" && status.pid) {
|
|
255
|
-
if (!isProcessRunning(status.pid)) {
|
|
256
|
-
const updatedStatus = { ...status, state: "stopped" };
|
|
257
|
-
writeStatus(workingDirectory, updatedStatus);
|
|
258
|
-
removePid(workingDirectory);
|
|
259
|
-
return {
|
|
260
|
-
success: true,
|
|
261
|
-
status: updatedStatus,
|
|
262
|
-
message: "Watcher process has stopped unexpectedly"
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
349
|
return {
|
|
267
350
|
success: true,
|
|
268
|
-
status
|
|
351
|
+
status,
|
|
352
|
+
pendingTickets,
|
|
353
|
+
formattedStatus
|
|
269
354
|
};
|
|
270
355
|
}
|
|
271
|
-
function getDaemonPath() {
|
|
272
|
-
const currentFilePath = fileURLToPath(import.meta.url);
|
|
273
|
-
const currentDir = dirname(currentFilePath);
|
|
274
|
-
return join(currentDir, "watcher", "watcher_daemon.js");
|
|
275
|
-
}
|
|
276
356
|
|
|
277
357
|
// src/prompt-builder.ts
|
|
278
358
|
var AmbiguousPromptError = class extends Error {
|
|
@@ -415,25 +495,25 @@ async function startServer(config, convexClientRaw) {
|
|
|
415
495
|
projectSlug,
|
|
416
496
|
type: "update"
|
|
417
497
|
},
|
|
418
|
-
// YOLO
|
|
498
|
+
// YOLO orchestrator tools - sub-agent based autonomous ticket execution
|
|
419
499
|
{
|
|
420
500
|
name: `tickets_yolo_start`,
|
|
421
501
|
description: `Start the YOLO ticket watcher for "${projectSlug}". Polls for open tickets with yolo flag and spawns Terminal windows with Claude Code to execute them autonomously.`,
|
|
422
|
-
slashDescription: `Start YOLO
|
|
502
|
+
slashDescription: `Start YOLO orchestrator for autonomous ticket execution`,
|
|
423
503
|
projectSlug,
|
|
424
504
|
type: "yolo_start"
|
|
425
505
|
},
|
|
426
506
|
{
|
|
427
507
|
name: `tickets_yolo_stop`,
|
|
428
508
|
description: `Stop the YOLO ticket watcher for "${projectSlug}"`,
|
|
429
|
-
slashDescription: `Stop the YOLO
|
|
509
|
+
slashDescription: `Stop the YOLO orchestrator`,
|
|
430
510
|
projectSlug,
|
|
431
511
|
type: "yolo_stop"
|
|
432
512
|
},
|
|
433
513
|
{
|
|
434
514
|
name: `tickets_yolo_status`,
|
|
435
515
|
description: `Get YOLO watcher status for "${projectSlug}"`,
|
|
436
|
-
slashDescription: `Check YOLO
|
|
516
|
+
slashDescription: `Check YOLO orchestrator status and pending tickets`,
|
|
437
517
|
projectSlug,
|
|
438
518
|
type: "yolo_status"
|
|
439
519
|
}
|
|
@@ -1520,33 +1600,31 @@ _Ticket content has been appended with your update._`
|
|
|
1520
1600
|
const args = parseWatcherStartArgs(request.params.arguments);
|
|
1521
1601
|
const workingDirectory = args.workingDirectory ?? process.cwd();
|
|
1522
1602
|
try {
|
|
1523
|
-
|
|
1603
|
+
let pendingTickets = [];
|
|
1604
|
+
try {
|
|
1605
|
+
pendingTickets = await convexClient.query(
|
|
1606
|
+
"mcp_tickets:listYoloTickets",
|
|
1607
|
+
{
|
|
1608
|
+
projectToken: config.projectToken,
|
|
1609
|
+
projectSlug: ticketTool.projectSlug
|
|
1610
|
+
}
|
|
1611
|
+
);
|
|
1612
|
+
} catch (queryError) {
|
|
1613
|
+
console.error(`[MCP] Failed to query YOLO tickets:`, queryError);
|
|
1614
|
+
}
|
|
1615
|
+
const result = startOrchestrator(
|
|
1524
1616
|
{
|
|
1525
1617
|
projectSlug: ticketTool.projectSlug,
|
|
1526
1618
|
projectToken: config.projectToken,
|
|
1527
1619
|
convexUrl: config.convexUrl,
|
|
1528
1620
|
workingDirectory
|
|
1529
1621
|
},
|
|
1530
|
-
args
|
|
1622
|
+
args,
|
|
1623
|
+
pendingTickets
|
|
1531
1624
|
);
|
|
1532
|
-
if (result.success) {
|
|
1533
|
-
const statusLines = [
|
|
1534
|
-
result.alreadyRunning ? `\u2139\uFE0F Watcher is already running (PID: ${result.pid})` : `\u2705 Watcher started (PID: ${result.pid})`
|
|
1535
|
-
];
|
|
1536
|
-
if (result.config) {
|
|
1537
|
-
statusLines.push("");
|
|
1538
|
-
statusLines.push("**Configuration:**");
|
|
1539
|
-
statusLines.push(` Poll Interval: ${result.config.pollIntervalMs / 1e3}s`);
|
|
1540
|
-
statusLines.push(` Max Parallel: ${result.config.maxParallel}`);
|
|
1541
|
-
statusLines.push(` Ticket Timeout: ${result.config.ticketTimeout / 1e3 / 60} min`);
|
|
1542
|
-
statusLines.push(` Notifications: ${result.config.enableNotifications ? "enabled" : "disabled"}`);
|
|
1543
|
-
statusLines.push(` Working Directory: ${result.config.workingDirectory}`);
|
|
1544
|
-
}
|
|
1545
|
-
statusLines.push("");
|
|
1546
|
-
statusLines.push("_The watcher will poll for open tickets with the YOLO flag and spawn Claude Code terminals to execute them._");
|
|
1547
|
-
statusLines.push("_Use `tickets_yolo_status` to check progress, `tickets_yolo_stop` to stop._");
|
|
1625
|
+
if (result.success && result.instructions) {
|
|
1548
1626
|
return {
|
|
1549
|
-
content: [{ type: "text", text:
|
|
1627
|
+
content: [{ type: "text", text: result.instructions }]
|
|
1550
1628
|
};
|
|
1551
1629
|
} else {
|
|
1552
1630
|
return {
|
|
@@ -1558,19 +1636,19 @@ _Ticket content has been appended with your update._`
|
|
|
1558
1636
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1559
1637
|
console.error(`[MCP] tickets_yolo_start error:`, error);
|
|
1560
1638
|
return {
|
|
1561
|
-
content: [{ type: "text", text: `Error starting
|
|
1639
|
+
content: [{ type: "text", text: `Error starting orchestrator: ${errorMessage}` }],
|
|
1562
1640
|
isError: true
|
|
1563
1641
|
};
|
|
1564
1642
|
}
|
|
1565
1643
|
} else if (ticketTool.type === "yolo_stop") {
|
|
1566
1644
|
const workingDirectory = process.cwd();
|
|
1567
1645
|
try {
|
|
1568
|
-
const result =
|
|
1646
|
+
const result = stopOrchestrator(workingDirectory);
|
|
1569
1647
|
if (result.success) {
|
|
1570
1648
|
return {
|
|
1571
1649
|
content: [{
|
|
1572
1650
|
type: "text",
|
|
1573
|
-
text: result.wasRunning ? `\u2705
|
|
1651
|
+
text: result.wasRunning ? `\u2705 Orchestrator stopped. You can stop processing tickets now.` : `\u2139\uFE0F ${result.message}`
|
|
1574
1652
|
}]
|
|
1575
1653
|
};
|
|
1576
1654
|
} else {
|
|
@@ -1583,62 +1661,39 @@ _Ticket content has been appended with your update._`
|
|
|
1583
1661
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1584
1662
|
console.error(`[MCP] tickets_yolo_stop error:`, error);
|
|
1585
1663
|
return {
|
|
1586
|
-
content: [{ type: "text", text: `Error stopping
|
|
1664
|
+
content: [{ type: "text", text: `Error stopping orchestrator: ${errorMessage}` }],
|
|
1587
1665
|
isError: true
|
|
1588
1666
|
};
|
|
1589
1667
|
}
|
|
1590
1668
|
} else if (ticketTool.type === "yolo_status") {
|
|
1591
1669
|
const workingDirectory = process.cwd();
|
|
1592
1670
|
try {
|
|
1593
|
-
|
|
1594
|
-
|
|
1671
|
+
let pendingTickets = [];
|
|
1672
|
+
try {
|
|
1673
|
+
pendingTickets = await convexClient.query(
|
|
1674
|
+
"mcp_tickets:listYoloTickets",
|
|
1675
|
+
{
|
|
1676
|
+
projectToken: config.projectToken,
|
|
1677
|
+
projectSlug: ticketTool.projectSlug
|
|
1678
|
+
}
|
|
1679
|
+
);
|
|
1680
|
+
} catch (queryError) {
|
|
1681
|
+
console.error(`[MCP] Failed to query YOLO tickets:`, queryError);
|
|
1682
|
+
}
|
|
1683
|
+
const result = getOrchestratorStatus(workingDirectory, pendingTickets);
|
|
1684
|
+
if (result.formattedStatus) {
|
|
1595
1685
|
return {
|
|
1596
|
-
content: [{ type: "text", text:
|
|
1686
|
+
content: [{ type: "text", text: result.formattedStatus }]
|
|
1597
1687
|
};
|
|
1598
1688
|
}
|
|
1599
|
-
const status = result.status;
|
|
1600
|
-
const lines = [
|
|
1601
|
-
`# YOLO Watcher Status: ${status.projectSlug}`,
|
|
1602
|
-
"",
|
|
1603
|
-
`**State:** ${status.state.toUpperCase()}`
|
|
1604
|
-
];
|
|
1605
|
-
if (status.pid) {
|
|
1606
|
-
lines.push(`**PID:** ${status.pid}`);
|
|
1607
|
-
}
|
|
1608
|
-
if (status.startedAt) {
|
|
1609
|
-
lines.push(`**Started:** ${status.startedAt}`);
|
|
1610
|
-
}
|
|
1611
|
-
if (status.lastPollAt) {
|
|
1612
|
-
lines.push(`**Last Poll:** ${status.lastPollAt}`);
|
|
1613
|
-
}
|
|
1614
|
-
lines.push(`**Tickets Processed:** ${status.ticketsProcessed}`);
|
|
1615
|
-
if (status.currentlyExecuting && status.currentlyExecuting.length > 0) {
|
|
1616
|
-
lines.push("");
|
|
1617
|
-
lines.push("**Currently Executing:**");
|
|
1618
|
-
for (const exec of status.currentlyExecuting) {
|
|
1619
|
-
const displayName = exec.ticketNumber ? `#${exec.ticketNumber} ${exec.ticketSlug}` : exec.ticketSlug;
|
|
1620
|
-
lines.push(` \u2022 ${displayName} (started: ${exec.startedAt})`);
|
|
1621
|
-
}
|
|
1622
|
-
}
|
|
1623
|
-
if (status.lastError) {
|
|
1624
|
-
lines.push("");
|
|
1625
|
-
lines.push(`**Last Error:** ${status.lastError}`);
|
|
1626
|
-
}
|
|
1627
|
-
if (status.config) {
|
|
1628
|
-
lines.push("");
|
|
1629
|
-
lines.push("**Configuration:**");
|
|
1630
|
-
lines.push(` Poll Interval: ${status.config.pollIntervalMs / 1e3}s`);
|
|
1631
|
-
lines.push(` Max Parallel: ${status.config.maxParallel}`);
|
|
1632
|
-
lines.push(` Working Directory: ${status.config.workingDirectory}`);
|
|
1633
|
-
}
|
|
1634
1689
|
return {
|
|
1635
|
-
content: [{ type: "text", text:
|
|
1690
|
+
content: [{ type: "text", text: `\u2139\uFE0F ${result.message ?? "No orchestrator status available"}` }]
|
|
1636
1691
|
};
|
|
1637
1692
|
} catch (error) {
|
|
1638
1693
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1639
1694
|
console.error(`[MCP] tickets_yolo_status error:`, error);
|
|
1640
1695
|
return {
|
|
1641
|
-
content: [{ type: "text", text: `Error getting
|
|
1696
|
+
content: [{ type: "text", text: `Error getting orchestrator status: ${errorMessage}` }],
|
|
1642
1697
|
isError: true
|
|
1643
1698
|
};
|
|
1644
1699
|
}
|