@promptprojectmanager/mcp-server 4.5.7 → 4.6.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/dist/index.js +288 -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,282 @@ 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
|
+
## \u26A0\uFE0F CRITICAL RULE: STRICT SUB-AGENT MODE
|
|
142
|
+
|
|
143
|
+
**You are the ORCHESTRATOR, not the executor.**
|
|
144
|
+
|
|
145
|
+
You MUST NEVER execute ticket work directly. ALL ticket execution MUST go through Task sub-agents.
|
|
146
|
+
|
|
147
|
+
Your ONLY jobs are:
|
|
148
|
+
1. Poll for YOLO tickets
|
|
149
|
+
2. Claim tickets (transition to "working" status)
|
|
150
|
+
3. Spawn Task sub-agents to execute them
|
|
151
|
+
4. Report results when sub-agents complete
|
|
152
|
+
|
|
153
|
+
**FORBIDDEN**: Doing the ticket work yourself (no web searches, no code writing, no file edits for tickets)
|
|
154
|
+
**REQUIRED**: Always use the Task tool to spawn a sub-agent for EVERY ticket
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Current Configuration
|
|
159
|
+
| Setting | Value |
|
|
160
|
+
|---------|-------|
|
|
161
|
+
| Poll Interval | ${pollIntervalSec}s |
|
|
162
|
+
| Max Parallel | ${config.maxParallel} |
|
|
163
|
+
| Ticket Timeout | ${Math.round(config.ticketTimeout / 1e3 / 60)} minutes |
|
|
164
|
+
| Working Directory | ${config.workingDirectory} |
|
|
165
|
+
|
|
166
|
+
## Pending YOLO Tickets
|
|
167
|
+
${pendingTickets.length > 0 ? formatPendingTickets(pendingTickets) : "_No tickets with YOLO flag currently in queue._"}
|
|
168
|
+
|
|
169
|
+
${pendingTickets.length > 0 ? `
|
|
170
|
+
## \u{1F4E5} Execute These Tickets NOW
|
|
171
|
+
|
|
172
|
+
For EACH pending ticket, you MUST:
|
|
173
|
+
|
|
174
|
+
### Step 1: Claim the ticket
|
|
175
|
+
\`\`\`
|
|
176
|
+
tickets_work with ticketSlug: "{slug}"
|
|
177
|
+
\`\`\`
|
|
178
|
+
|
|
179
|
+
### Step 2: Spawn a Task sub-agent (REQUIRED - DO NOT SKIP)
|
|
180
|
+
\`\`\`
|
|
181
|
+
Task tool with:
|
|
182
|
+
description: "Execute YOLO ticket {slug}"
|
|
183
|
+
subagent_type: "general-purpose"
|
|
184
|
+
prompt: [use the template below]
|
|
185
|
+
run_in_background: ${config.maxParallel > 1 ? "true" : "false"}
|
|
186
|
+
\`\`\`
|
|
187
|
+
|
|
188
|
+
### Sub-Agent Prompt Template (copy this exactly):
|
|
189
|
+
|
|
190
|
+
\`\`\`
|
|
191
|
+
You are executing a YOLO ticket autonomously.
|
|
192
|
+
|
|
193
|
+
## Ticket: {ticketSlug}
|
|
194
|
+
|
|
195
|
+
{ticketContent}
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Your Instructions
|
|
200
|
+
|
|
201
|
+
1. Execute the task described above
|
|
202
|
+
2. When complete, close the ticket: tickets_close with ticketSlug: "{ticketSlug}"
|
|
203
|
+
3. If blocked, update the ticket: tickets_update with your findings
|
|
204
|
+
4. Return a brief summary of what you accomplished
|
|
205
|
+
\`\`\`
|
|
206
|
+
|
|
207
|
+
### Step 3: Report the result
|
|
208
|
+
After each sub-agent completes, report:
|
|
209
|
+
- \u2705 Success: what was done
|
|
210
|
+
- \u274C Failure: what went wrong
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
**START NOW**: Claim and spawn sub-agents for all ${pendingTickets.length} pending ticket(s) above.
|
|
215
|
+
` : `
|
|
216
|
+
## \u{1F504} No Tickets Ready
|
|
217
|
+
|
|
218
|
+
No YOLO tickets are currently pending. Options:
|
|
219
|
+
- Say **"poll"** to check for new tickets
|
|
220
|
+
- Say **"stop"** to exit orchestrator mode
|
|
221
|
+
`}
|
|
222
|
+
|
|
223
|
+
## Commands
|
|
224
|
+
| Command | Action |
|
|
225
|
+
|---------|--------|
|
|
226
|
+
| "poll" / "check" | Refresh the ticket list |
|
|
227
|
+
| "status" | Show orchestrator status |
|
|
228
|
+
| "stop" / "exit" | Stop the orchestrator |
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
_Orchestrator started at ${(/* @__PURE__ */ new Date()).toISOString()}_`;
|
|
233
|
+
}
|
|
234
|
+
function formatPendingTickets(tickets) {
|
|
235
|
+
return tickets.map((t, i) => {
|
|
236
|
+
const num = t.ticketNumber ? `#${t.ticketNumber}` : "";
|
|
237
|
+
const title = t.slug;
|
|
238
|
+
const preview = t.content.slice(0, 150).replace(/\n/g, " ");
|
|
239
|
+
return `### ${i + 1}. ${num} ${title}
|
|
240
|
+
> ${preview}${t.content.length > 150 ? "..." : ""}`;
|
|
241
|
+
}).join("\n\n");
|
|
242
|
+
}
|
|
243
|
+
function formatOrchestratorStatus(status, pendingTickets) {
|
|
244
|
+
if (!status) {
|
|
245
|
+
return `# YOLO Orchestrator Status
|
|
246
|
+
|
|
247
|
+
**State**: Not started
|
|
248
|
+
|
|
249
|
+
_Use \`tickets_yolo_start\` to begin orchestration._`;
|
|
250
|
+
}
|
|
251
|
+
const lines = [
|
|
252
|
+
"# YOLO Orchestrator Status",
|
|
253
|
+
"",
|
|
254
|
+
`**State**: ${status.state}`,
|
|
255
|
+
`**Project**: ${status.projectSlug}`
|
|
256
|
+
];
|
|
257
|
+
if (status.startedAt) {
|
|
258
|
+
lines.push(`**Started**: ${status.startedAt}`);
|
|
259
|
+
}
|
|
260
|
+
if (status.lastPollAt) {
|
|
261
|
+
lines.push(`**Last Poll**: ${status.lastPollAt}`);
|
|
262
|
+
}
|
|
263
|
+
lines.push(`**Tickets Processed**: ${status.ticketsProcessed}`);
|
|
264
|
+
if (status.currentlyExecuting && status.currentlyExecuting.length > 0) {
|
|
265
|
+
lines.push("");
|
|
266
|
+
lines.push("## Currently Executing (Sub-Agents)");
|
|
267
|
+
for (const exec of status.currentlyExecuting) {
|
|
268
|
+
const num = exec.ticketNumber ? `#${exec.ticketNumber}` : "";
|
|
269
|
+
const elapsed = getElapsedTime(exec.startedAt);
|
|
270
|
+
lines.push(`- **${num} ${exec.ticketSlug}** (${elapsed})`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
lines.push("");
|
|
274
|
+
lines.push("## Pending YOLO Tickets");
|
|
275
|
+
if (pendingTickets.length > 0) {
|
|
276
|
+
lines.push("");
|
|
277
|
+
lines.push("**Action Required**: Spawn Task sub-agents for these tickets:");
|
|
278
|
+
lines.push("");
|
|
279
|
+
for (const ticket of pendingTickets) {
|
|
280
|
+
const num = ticket.ticketNumber ? `#${ticket.ticketNumber}` : "";
|
|
281
|
+
lines.push(`- **${num} ${ticket.slug}**`);
|
|
282
|
+
}
|
|
283
|
+
} else {
|
|
284
|
+
lines.push("_No tickets pending_");
|
|
285
|
+
}
|
|
286
|
+
return lines.join("\n");
|
|
287
|
+
}
|
|
288
|
+
function getElapsedTime(startedAt) {
|
|
289
|
+
const start = new Date(startedAt).getTime();
|
|
290
|
+
const elapsed = Date.now() - start;
|
|
291
|
+
const minutes = Math.floor(elapsed / 1e3 / 60);
|
|
292
|
+
const seconds = Math.floor(elapsed / 1e3 % 60);
|
|
293
|
+
if (minutes > 0) {
|
|
294
|
+
return `${minutes}m ${seconds}s`;
|
|
295
|
+
}
|
|
296
|
+
return `${seconds}s`;
|
|
297
|
+
}
|
|
298
|
+
|
|
114
299
|
// 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) {
|
|
300
|
+
function startOrchestrator(baseConfig, args, pendingTickets) {
|
|
120
301
|
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 = {
|
|
302
|
+
const orchestratorConfig = {
|
|
133
303
|
projectSlug: baseConfig.projectSlug,
|
|
134
|
-
projectToken: baseConfig.projectToken,
|
|
135
|
-
convexUrl: baseConfig.convexUrl,
|
|
136
|
-
workingDirectory,
|
|
137
304
|
pollIntervalMs: args?.pollIntervalMs ?? 3e4,
|
|
138
305
|
maxParallel: args?.maxParallel ?? 1,
|
|
139
306
|
ticketTimeout: args?.ticketTimeout ?? 18e5,
|
|
140
|
-
|
|
307
|
+
workingDirectory
|
|
141
308
|
};
|
|
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
|
-
};
|
|
309
|
+
getWatcherDir(workingDirectory);
|
|
310
|
+
const initialStatus = {
|
|
311
|
+
state: "running",
|
|
312
|
+
projectSlug: baseConfig.projectSlug,
|
|
313
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
314
|
+
ticketsProcessed: 0,
|
|
315
|
+
currentlyExecuting: [],
|
|
316
|
+
config: {
|
|
317
|
+
projectSlug: baseConfig.projectSlug,
|
|
318
|
+
convexUrl: baseConfig.convexUrl,
|
|
319
|
+
pollIntervalMs: orchestratorConfig.pollIntervalMs,
|
|
320
|
+
maxParallel: orchestratorConfig.maxParallel,
|
|
321
|
+
ticketTimeout: orchestratorConfig.ticketTimeout,
|
|
322
|
+
enableNotifications: args?.enableNotifications ?? true,
|
|
323
|
+
workingDirectory
|
|
182
324
|
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
325
|
+
};
|
|
326
|
+
writeStatus(workingDirectory, initialStatus);
|
|
327
|
+
const instructions = buildOrchestratorInstructions(orchestratorConfig, pendingTickets);
|
|
328
|
+
return {
|
|
329
|
+
success: true,
|
|
330
|
+
message: "Orchestrator started",
|
|
331
|
+
instructions,
|
|
332
|
+
config: initialStatus.config,
|
|
333
|
+
alreadyRunning: false
|
|
334
|
+
};
|
|
190
335
|
}
|
|
191
|
-
|
|
192
|
-
const
|
|
193
|
-
if (!
|
|
336
|
+
function stopOrchestrator(workingDirectory) {
|
|
337
|
+
const status = readStatus(workingDirectory);
|
|
338
|
+
if (!status) {
|
|
194
339
|
return {
|
|
195
340
|
success: true,
|
|
196
|
-
message: "No
|
|
341
|
+
message: "No orchestrator session found",
|
|
197
342
|
wasRunning: false
|
|
198
343
|
};
|
|
199
344
|
}
|
|
200
|
-
if (
|
|
201
|
-
removePid(workingDirectory);
|
|
202
|
-
const status = readStatus(workingDirectory);
|
|
203
|
-
if (status) {
|
|
204
|
-
writeStatus(workingDirectory, { ...status, state: "stopped" });
|
|
205
|
-
}
|
|
345
|
+
if (status.state !== "running") {
|
|
206
346
|
return {
|
|
207
347
|
success: true,
|
|
208
|
-
message:
|
|
348
|
+
message: `Orchestrator was already ${status.state}`,
|
|
209
349
|
wasRunning: false
|
|
210
350
|
};
|
|
211
351
|
}
|
|
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
|
-
}
|
|
352
|
+
writeStatus(workingDirectory, {
|
|
353
|
+
...status,
|
|
354
|
+
state: "stopped"
|
|
355
|
+
});
|
|
356
|
+
return {
|
|
357
|
+
success: true,
|
|
358
|
+
message: "Orchestrator stopped",
|
|
359
|
+
wasRunning: true
|
|
360
|
+
};
|
|
245
361
|
}
|
|
246
|
-
function
|
|
362
|
+
function getOrchestratorStatus(workingDirectory, pendingTickets) {
|
|
247
363
|
const status = readStatus(workingDirectory);
|
|
364
|
+
const formattedStatus = formatOrchestratorStatus(status ?? null, pendingTickets);
|
|
248
365
|
if (!status) {
|
|
249
366
|
return {
|
|
250
367
|
success: true,
|
|
251
|
-
message: "No
|
|
368
|
+
message: "No orchestrator session has been started for this project",
|
|
369
|
+
pendingTickets,
|
|
370
|
+
formattedStatus
|
|
252
371
|
};
|
|
253
372
|
}
|
|
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
373
|
return {
|
|
267
374
|
success: true,
|
|
268
|
-
status
|
|
375
|
+
status,
|
|
376
|
+
pendingTickets,
|
|
377
|
+
formattedStatus
|
|
269
378
|
};
|
|
270
379
|
}
|
|
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
380
|
|
|
277
381
|
// src/prompt-builder.ts
|
|
278
382
|
var AmbiguousPromptError = class extends Error {
|
|
@@ -415,25 +519,25 @@ async function startServer(config, convexClientRaw) {
|
|
|
415
519
|
projectSlug,
|
|
416
520
|
type: "update"
|
|
417
521
|
},
|
|
418
|
-
// YOLO
|
|
522
|
+
// YOLO orchestrator tools - sub-agent based autonomous ticket execution
|
|
419
523
|
{
|
|
420
524
|
name: `tickets_yolo_start`,
|
|
421
525
|
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
|
|
526
|
+
slashDescription: `Start YOLO orchestrator for autonomous ticket execution`,
|
|
423
527
|
projectSlug,
|
|
424
528
|
type: "yolo_start"
|
|
425
529
|
},
|
|
426
530
|
{
|
|
427
531
|
name: `tickets_yolo_stop`,
|
|
428
532
|
description: `Stop the YOLO ticket watcher for "${projectSlug}"`,
|
|
429
|
-
slashDescription: `Stop the YOLO
|
|
533
|
+
slashDescription: `Stop the YOLO orchestrator`,
|
|
430
534
|
projectSlug,
|
|
431
535
|
type: "yolo_stop"
|
|
432
536
|
},
|
|
433
537
|
{
|
|
434
538
|
name: `tickets_yolo_status`,
|
|
435
539
|
description: `Get YOLO watcher status for "${projectSlug}"`,
|
|
436
|
-
slashDescription: `Check YOLO
|
|
540
|
+
slashDescription: `Check YOLO orchestrator status and pending tickets`,
|
|
437
541
|
projectSlug,
|
|
438
542
|
type: "yolo_status"
|
|
439
543
|
}
|
|
@@ -1520,33 +1624,31 @@ _Ticket content has been appended with your update._`
|
|
|
1520
1624
|
const args = parseWatcherStartArgs(request.params.arguments);
|
|
1521
1625
|
const workingDirectory = args.workingDirectory ?? process.cwd();
|
|
1522
1626
|
try {
|
|
1523
|
-
|
|
1627
|
+
let pendingTickets = [];
|
|
1628
|
+
try {
|
|
1629
|
+
pendingTickets = await convexClient.query(
|
|
1630
|
+
"mcp_tickets:listYoloTickets",
|
|
1631
|
+
{
|
|
1632
|
+
projectToken: config.projectToken,
|
|
1633
|
+
projectSlug: ticketTool.projectSlug
|
|
1634
|
+
}
|
|
1635
|
+
);
|
|
1636
|
+
} catch (queryError) {
|
|
1637
|
+
console.error(`[MCP] Failed to query YOLO tickets:`, queryError);
|
|
1638
|
+
}
|
|
1639
|
+
const result = startOrchestrator(
|
|
1524
1640
|
{
|
|
1525
1641
|
projectSlug: ticketTool.projectSlug,
|
|
1526
1642
|
projectToken: config.projectToken,
|
|
1527
1643
|
convexUrl: config.convexUrl,
|
|
1528
1644
|
workingDirectory
|
|
1529
1645
|
},
|
|
1530
|
-
args
|
|
1646
|
+
args,
|
|
1647
|
+
pendingTickets
|
|
1531
1648
|
);
|
|
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._");
|
|
1649
|
+
if (result.success && result.instructions) {
|
|
1548
1650
|
return {
|
|
1549
|
-
content: [{ type: "text", text:
|
|
1651
|
+
content: [{ type: "text", text: result.instructions }]
|
|
1550
1652
|
};
|
|
1551
1653
|
} else {
|
|
1552
1654
|
return {
|
|
@@ -1558,19 +1660,19 @@ _Ticket content has been appended with your update._`
|
|
|
1558
1660
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1559
1661
|
console.error(`[MCP] tickets_yolo_start error:`, error);
|
|
1560
1662
|
return {
|
|
1561
|
-
content: [{ type: "text", text: `Error starting
|
|
1663
|
+
content: [{ type: "text", text: `Error starting orchestrator: ${errorMessage}` }],
|
|
1562
1664
|
isError: true
|
|
1563
1665
|
};
|
|
1564
1666
|
}
|
|
1565
1667
|
} else if (ticketTool.type === "yolo_stop") {
|
|
1566
1668
|
const workingDirectory = process.cwd();
|
|
1567
1669
|
try {
|
|
1568
|
-
const result =
|
|
1670
|
+
const result = stopOrchestrator(workingDirectory);
|
|
1569
1671
|
if (result.success) {
|
|
1570
1672
|
return {
|
|
1571
1673
|
content: [{
|
|
1572
1674
|
type: "text",
|
|
1573
|
-
text: result.wasRunning ? `\u2705
|
|
1675
|
+
text: result.wasRunning ? `\u2705 Orchestrator stopped. You can stop processing tickets now.` : `\u2139\uFE0F ${result.message}`
|
|
1574
1676
|
}]
|
|
1575
1677
|
};
|
|
1576
1678
|
} else {
|
|
@@ -1583,62 +1685,39 @@ _Ticket content has been appended with your update._`
|
|
|
1583
1685
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1584
1686
|
console.error(`[MCP] tickets_yolo_stop error:`, error);
|
|
1585
1687
|
return {
|
|
1586
|
-
content: [{ type: "text", text: `Error stopping
|
|
1688
|
+
content: [{ type: "text", text: `Error stopping orchestrator: ${errorMessage}` }],
|
|
1587
1689
|
isError: true
|
|
1588
1690
|
};
|
|
1589
1691
|
}
|
|
1590
1692
|
} else if (ticketTool.type === "yolo_status") {
|
|
1591
1693
|
const workingDirectory = process.cwd();
|
|
1592
1694
|
try {
|
|
1593
|
-
|
|
1594
|
-
|
|
1695
|
+
let pendingTickets = [];
|
|
1696
|
+
try {
|
|
1697
|
+
pendingTickets = await convexClient.query(
|
|
1698
|
+
"mcp_tickets:listYoloTickets",
|
|
1699
|
+
{
|
|
1700
|
+
projectToken: config.projectToken,
|
|
1701
|
+
projectSlug: ticketTool.projectSlug
|
|
1702
|
+
}
|
|
1703
|
+
);
|
|
1704
|
+
} catch (queryError) {
|
|
1705
|
+
console.error(`[MCP] Failed to query YOLO tickets:`, queryError);
|
|
1706
|
+
}
|
|
1707
|
+
const result = getOrchestratorStatus(workingDirectory, pendingTickets);
|
|
1708
|
+
if (result.formattedStatus) {
|
|
1595
1709
|
return {
|
|
1596
|
-
content: [{ type: "text", text:
|
|
1710
|
+
content: [{ type: "text", text: result.formattedStatus }]
|
|
1597
1711
|
};
|
|
1598
1712
|
}
|
|
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
1713
|
return {
|
|
1635
|
-
content: [{ type: "text", text:
|
|
1714
|
+
content: [{ type: "text", text: `\u2139\uFE0F ${result.message ?? "No orchestrator status available"}` }]
|
|
1636
1715
|
};
|
|
1637
1716
|
} catch (error) {
|
|
1638
1717
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1639
1718
|
console.error(`[MCP] tickets_yolo_status error:`, error);
|
|
1640
1719
|
return {
|
|
1641
|
-
content: [{ type: "text", text: `Error getting
|
|
1720
|
+
content: [{ type: "text", text: `Error getting orchestrator status: ${errorMessage}` }],
|
|
1642
1721
|
isError: true
|
|
1643
1722
|
};
|
|
1644
1723
|
}
|