@promptprojectmanager/mcp-server 4.6.9 → 4.7.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 +103 -486
- package/dist/index.js.map +1 -1
- package/package.json +2 -3
- package/dist/chunk-PY22KZ7Z.js +0 -143
- package/dist/chunk-PY22KZ7Z.js.map +0 -1
- package/dist/watcher/watcher_daemon.d.ts +0 -1
- package/dist/watcher/watcher_daemon.js +0 -564
- package/dist/watcher/watcher_daemon.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,12 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
getWatcherDir,
|
|
4
|
-
isWatcherRunning,
|
|
5
|
-
readPid,
|
|
6
|
-
readStatus,
|
|
7
|
-
removePid,
|
|
8
|
-
writeStatus
|
|
9
|
-
} from "./chunk-PY22KZ7Z.js";
|
|
10
2
|
|
|
11
3
|
// src/index.ts
|
|
12
4
|
import minimist from "minimist";
|
|
@@ -76,6 +68,13 @@ function parseUpdateArgs(args) {
|
|
|
76
68
|
if (!ticketSlug || !content) return null;
|
|
77
69
|
return { ticketSlug, content };
|
|
78
70
|
}
|
|
71
|
+
function parsePauseArgs(args) {
|
|
72
|
+
const parsed = args;
|
|
73
|
+
const ticketSlug = typeof parsed?.ticketSlug === "string" ? parsed.ticketSlug : void 0;
|
|
74
|
+
const content = typeof parsed?.content === "string" ? parsed.content : void 0;
|
|
75
|
+
if (!ticketSlug || !content) return null;
|
|
76
|
+
return { ticketSlug, content };
|
|
77
|
+
}
|
|
79
78
|
function parsePromptsRunArgs(args) {
|
|
80
79
|
const parsed = args;
|
|
81
80
|
const slug = typeof parsed?.slug === "string" ? parsed.slug : void 0;
|
|
@@ -98,314 +97,6 @@ function parsePromptsUpdateArgs(args) {
|
|
|
98
97
|
changeLogMessage: typeof parsed?.changeLogMessage === "string" ? parsed.changeLogMessage : void 0
|
|
99
98
|
};
|
|
100
99
|
}
|
|
101
|
-
function parseWatcherStartArgs(args) {
|
|
102
|
-
const parsed = args;
|
|
103
|
-
return {
|
|
104
|
-
pollIntervalMs: typeof parsed?.pollIntervalMs === "number" ? parsed.pollIntervalMs : void 0,
|
|
105
|
-
maxParallel: typeof parsed?.maxParallel === "number" ? parsed.maxParallel : void 0,
|
|
106
|
-
ticketTimeout: typeof parsed?.ticketTimeout === "number" ? parsed.ticketTimeout : void 0,
|
|
107
|
-
enableNotifications: typeof parsed?.enableNotifications === "boolean" ? parsed.enableNotifications : void 0,
|
|
108
|
-
workingDirectory: typeof parsed?.workingDirectory === "string" ? parsed.workingDirectory : void 0
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// src/watcher/watcher_controller.ts
|
|
113
|
-
import { spawn } from "child_process";
|
|
114
|
-
import { fileURLToPath } from "url";
|
|
115
|
-
import { dirname, join } from "path";
|
|
116
|
-
function startWatcher(baseConfig, args) {
|
|
117
|
-
const workingDirectory = args?.workingDirectory ?? baseConfig.workingDirectory;
|
|
118
|
-
if (isWatcherRunning(workingDirectory)) {
|
|
119
|
-
const existingPid = readPid(workingDirectory);
|
|
120
|
-
const status = readStatus(workingDirectory);
|
|
121
|
-
return {
|
|
122
|
-
success: true,
|
|
123
|
-
message: `Watcher already running (PID: ${existingPid})`,
|
|
124
|
-
pid: existingPid,
|
|
125
|
-
alreadyRunning: true,
|
|
126
|
-
config: status?.config
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
const config = {
|
|
130
|
-
projectSlug: baseConfig.projectSlug,
|
|
131
|
-
projectToken: baseConfig.projectToken,
|
|
132
|
-
convexUrl: baseConfig.convexUrl,
|
|
133
|
-
pollIntervalMs: args?.pollIntervalMs ?? 3e4,
|
|
134
|
-
maxParallel: args?.maxParallel ?? 1,
|
|
135
|
-
ticketTimeout: args?.ticketTimeout ?? 18e5,
|
|
136
|
-
enableNotifications: args?.enableNotifications ?? true,
|
|
137
|
-
workingDirectory
|
|
138
|
-
};
|
|
139
|
-
getWatcherDir(workingDirectory);
|
|
140
|
-
const daemonPath = getDaemonPath();
|
|
141
|
-
const configJson = JSON.stringify(config);
|
|
142
|
-
const configB64 = Buffer.from(configJson).toString("base64");
|
|
143
|
-
try {
|
|
144
|
-
const child = spawn("node", [daemonPath, "--config", configB64], {
|
|
145
|
-
detached: true,
|
|
146
|
-
stdio: ["ignore", "ignore", "ignore"],
|
|
147
|
-
cwd: workingDirectory
|
|
148
|
-
});
|
|
149
|
-
child.unref();
|
|
150
|
-
const pid = child.pid;
|
|
151
|
-
if (!pid) {
|
|
152
|
-
return {
|
|
153
|
-
success: false,
|
|
154
|
-
message: "Failed to spawn daemon - no PID returned"
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
const configWithoutToken = {
|
|
158
|
-
projectSlug: config.projectSlug,
|
|
159
|
-
convexUrl: config.convexUrl,
|
|
160
|
-
pollIntervalMs: config.pollIntervalMs,
|
|
161
|
-
maxParallel: config.maxParallel,
|
|
162
|
-
ticketTimeout: config.ticketTimeout,
|
|
163
|
-
enableNotifications: config.enableNotifications,
|
|
164
|
-
workingDirectory: config.workingDirectory
|
|
165
|
-
};
|
|
166
|
-
const statusFile = join(getWatcherDir(workingDirectory), "status.json");
|
|
167
|
-
const monitoringPrompt = generateMonitoringPrompt(statusFile, workingDirectory);
|
|
168
|
-
return {
|
|
169
|
-
success: true,
|
|
170
|
-
message: `Watcher started (PID: ${pid})`,
|
|
171
|
-
pid,
|
|
172
|
-
alreadyRunning: false,
|
|
173
|
-
config: configWithoutToken,
|
|
174
|
-
monitoringPrompt,
|
|
175
|
-
statusFile
|
|
176
|
-
};
|
|
177
|
-
} catch (error) {
|
|
178
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
179
|
-
return {
|
|
180
|
-
success: false,
|
|
181
|
-
message: `Failed to start watcher: ${errorMessage}`
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
function getDaemonPath() {
|
|
186
|
-
const currentFile = fileURLToPath(import.meta.url);
|
|
187
|
-
const currentDir = dirname(currentFile);
|
|
188
|
-
return join(currentDir, "watcher", "watcher_daemon.js");
|
|
189
|
-
}
|
|
190
|
-
function stopWatcher(workingDirectory) {
|
|
191
|
-
const pid = readPid(workingDirectory);
|
|
192
|
-
if (!pid) {
|
|
193
|
-
return {
|
|
194
|
-
success: true,
|
|
195
|
-
message: "No watcher running (no PID file)",
|
|
196
|
-
wasRunning: false
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
if (!isWatcherRunning(workingDirectory)) {
|
|
200
|
-
removePid(workingDirectory);
|
|
201
|
-
return {
|
|
202
|
-
success: true,
|
|
203
|
-
message: "Watcher was not running (stale PID file cleaned up)",
|
|
204
|
-
wasRunning: false
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
try {
|
|
208
|
-
process.kill(pid, "SIGTERM");
|
|
209
|
-
const status = readStatus(workingDirectory);
|
|
210
|
-
if (status) {
|
|
211
|
-
writeStatus(workingDirectory, {
|
|
212
|
-
...status,
|
|
213
|
-
state: "stopped"
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
removePid(workingDirectory);
|
|
217
|
-
return {
|
|
218
|
-
success: true,
|
|
219
|
-
message: `Watcher stopped (PID: ${pid})`,
|
|
220
|
-
wasRunning: true
|
|
221
|
-
};
|
|
222
|
-
} catch (error) {
|
|
223
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
224
|
-
removePid(workingDirectory);
|
|
225
|
-
return {
|
|
226
|
-
success: false,
|
|
227
|
-
message: `Failed to stop watcher: ${errorMessage}`,
|
|
228
|
-
wasRunning: true
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
function getWatcherStatus(workingDirectory) {
|
|
233
|
-
const status = readStatus(workingDirectory);
|
|
234
|
-
const isRunning = isWatcherRunning(workingDirectory);
|
|
235
|
-
if (status?.state === "running" && !isRunning) {
|
|
236
|
-
const updatedStatus = {
|
|
237
|
-
...status,
|
|
238
|
-
state: "stopped"
|
|
239
|
-
};
|
|
240
|
-
writeStatus(workingDirectory, updatedStatus);
|
|
241
|
-
return {
|
|
242
|
-
success: true,
|
|
243
|
-
status: updatedStatus,
|
|
244
|
-
formattedStatus: formatStatus(updatedStatus),
|
|
245
|
-
message: "Watcher died unexpectedly - status updated"
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
if (!status) {
|
|
249
|
-
return {
|
|
250
|
-
success: true,
|
|
251
|
-
message: "No watcher session found for this project",
|
|
252
|
-
formattedStatus: formatStatus(null)
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
return {
|
|
256
|
-
success: true,
|
|
257
|
-
status,
|
|
258
|
-
formattedStatus: formatStatus(status)
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
function formatStatus(status) {
|
|
262
|
-
if (!status) {
|
|
263
|
-
return `# YOLO Watcher Status
|
|
264
|
-
|
|
265
|
-
**State**: Not started
|
|
266
|
-
|
|
267
|
-
_Use \`tickets_yolo_start\` to begin watching for YOLO tickets._`;
|
|
268
|
-
}
|
|
269
|
-
const lines = [
|
|
270
|
-
"# YOLO Watcher Status",
|
|
271
|
-
"",
|
|
272
|
-
`**State**: ${status.state}`,
|
|
273
|
-
`**Project**: ${status.projectSlug}`
|
|
274
|
-
];
|
|
275
|
-
if (status.pid) {
|
|
276
|
-
lines.push(`**Daemon PID**: ${status.pid}`);
|
|
277
|
-
}
|
|
278
|
-
if (status.startedAt) {
|
|
279
|
-
lines.push(`**Started**: ${status.startedAt}`);
|
|
280
|
-
}
|
|
281
|
-
if (status.lastPollAt) {
|
|
282
|
-
lines.push(`**Last Poll**: ${status.lastPollAt}`);
|
|
283
|
-
}
|
|
284
|
-
lines.push(`**Tickets Processed**: ${status.ticketsProcessed}`);
|
|
285
|
-
if (status.config) {
|
|
286
|
-
lines.push("");
|
|
287
|
-
lines.push("## Configuration");
|
|
288
|
-
lines.push(`- Poll Interval: ${status.config.pollIntervalMs / 1e3}s`);
|
|
289
|
-
lines.push(`- Max Parallel: ${status.config.maxParallel}`);
|
|
290
|
-
lines.push(`- Ticket Timeout: ${status.config.ticketTimeout / 1e3 / 60} min`);
|
|
291
|
-
lines.push(`- Notifications: ${status.config.enableNotifications ? "enabled" : "disabled"}`);
|
|
292
|
-
}
|
|
293
|
-
if (status.currentlyExecuting && status.currentlyExecuting.length > 0) {
|
|
294
|
-
lines.push("");
|
|
295
|
-
lines.push("## Currently Executing");
|
|
296
|
-
for (const exec of status.currentlyExecuting) {
|
|
297
|
-
const num = exec.ticketNumber ? `#${exec.ticketNumber}` : "";
|
|
298
|
-
const elapsed = getElapsedTime(exec.startedAt);
|
|
299
|
-
lines.push(`- **${num} ${exec.ticketSlug}** (${elapsed})`);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
if (status.completedTickets && status.completedTickets.length > 0) {
|
|
303
|
-
lines.push("");
|
|
304
|
-
lines.push("## Recently Completed");
|
|
305
|
-
for (const completed of status.completedTickets.slice(0, 10)) {
|
|
306
|
-
const num = completed.ticketNumber ? `#${completed.ticketNumber}` : "";
|
|
307
|
-
const emoji = completed.success ? "\u2705" : completed.ticketClosed ? "\u26A0\uFE0F" : "\u274C";
|
|
308
|
-
const duration = formatDuration(completed.durationMs);
|
|
309
|
-
lines.push(`### ${emoji} ${num} ${completed.ticketSlug} (${duration})`);
|
|
310
|
-
if (completed.toolCalls && completed.toolCalls.length > 0) {
|
|
311
|
-
const toolNames = [...new Set(completed.toolCalls.map((t) => t.tool))];
|
|
312
|
-
lines.push(`- **Tools**: ${toolNames.join(", ")}`);
|
|
313
|
-
}
|
|
314
|
-
if (completed.ticketClosed) {
|
|
315
|
-
lines.push(`- **Status**: Ticket closed successfully`);
|
|
316
|
-
} else if (completed.exitCode === 0) {
|
|
317
|
-
lines.push(`- **Status**: Completed but ticket not closed`);
|
|
318
|
-
}
|
|
319
|
-
if (completed.errors && completed.errors.length > 0) {
|
|
320
|
-
lines.push(`- **Errors**: ${completed.errors.slice(0, 2).join("; ")}`);
|
|
321
|
-
}
|
|
322
|
-
if (completed.summary) {
|
|
323
|
-
lines.push(`- **Summary**: ${completed.summary.slice(0, 150)}`);
|
|
324
|
-
}
|
|
325
|
-
lines.push("");
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
if (status.lastError) {
|
|
329
|
-
lines.push("");
|
|
330
|
-
lines.push("## Last Error");
|
|
331
|
-
lines.push(`\`${status.lastError}\``);
|
|
332
|
-
}
|
|
333
|
-
return lines.join("\n");
|
|
334
|
-
}
|
|
335
|
-
function getElapsedTime(startedAt) {
|
|
336
|
-
const start = new Date(startedAt).getTime();
|
|
337
|
-
const elapsed = Date.now() - start;
|
|
338
|
-
const minutes = Math.floor(elapsed / 1e3 / 60);
|
|
339
|
-
const seconds = Math.floor(elapsed / 1e3 % 60);
|
|
340
|
-
if (minutes > 0) {
|
|
341
|
-
return `${minutes}m ${seconds}s`;
|
|
342
|
-
}
|
|
343
|
-
return `${seconds}s`;
|
|
344
|
-
}
|
|
345
|
-
function formatDuration(ms) {
|
|
346
|
-
if (ms < 1e3) {
|
|
347
|
-
return `${ms}ms`;
|
|
348
|
-
}
|
|
349
|
-
const seconds = Math.floor(ms / 1e3);
|
|
350
|
-
const minutes = Math.floor(seconds / 60);
|
|
351
|
-
if (minutes > 0) {
|
|
352
|
-
const remainingSeconds = seconds % 60;
|
|
353
|
-
return `${minutes}m ${remainingSeconds}s`;
|
|
354
|
-
}
|
|
355
|
-
return `${seconds}s`;
|
|
356
|
-
}
|
|
357
|
-
function generateMonitoringPrompt(statusFile, workingDirectory) {
|
|
358
|
-
const pidFile = join(getWatcherDir(workingDirectory), "watcher.pid");
|
|
359
|
-
return `You are a YOLO Watcher Monitor. Watch for ticket completions and report them.
|
|
360
|
-
|
|
361
|
-
## Status File
|
|
362
|
-
${statusFile}
|
|
363
|
-
|
|
364
|
-
## PID File (check if daemon is running)
|
|
365
|
-
${pidFile}
|
|
366
|
-
|
|
367
|
-
## Your Workflow
|
|
368
|
-
|
|
369
|
-
1. Read status.json and record the current count of completedTickets
|
|
370
|
-
2. Run this bash loop to wait for changes (bash does the waiting, not you):
|
|
371
|
-
\`\`\`bash
|
|
372
|
-
last_count=$(cat "${statusFile}" 2>/dev/null | grep -o '"ticketSlug"' | wc -l | tr -d ' ')
|
|
373
|
-
while [ -f "${pidFile}" ]; do
|
|
374
|
-
current=$(cat "${statusFile}" 2>/dev/null | grep -o '"ticketSlug"' | wc -l | tr -d ' ')
|
|
375
|
-
if [ "$current" != "$last_count" ]; then
|
|
376
|
-
echo "CHANGED: $last_count -> $current"
|
|
377
|
-
exit 0
|
|
378
|
-
fi
|
|
379
|
-
sleep 5
|
|
380
|
-
done
|
|
381
|
-
echo "DAEMON_STOPPED"
|
|
382
|
-
\`\`\`
|
|
383
|
-
3. When the loop exits:
|
|
384
|
-
- If "CHANGED": read status.json and report the NEW completed ticket(s)
|
|
385
|
-
- If "DAEMON_STOPPED": report that the watcher has stopped and exit
|
|
386
|
-
4. Loop back to step 1 (unless daemon stopped)
|
|
387
|
-
|
|
388
|
-
## Reporting Format
|
|
389
|
-
|
|
390
|
-
When a ticket completes:
|
|
391
|
-
\`\`\`
|
|
392
|
-
\u{1F3AB} YOLO TICKET COMPLETED
|
|
393
|
-
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
394
|
-
Ticket: {slug} (#{number})
|
|
395
|
-
Duration: {X}s
|
|
396
|
-
Status: {\u2705 Success | \u26A0\uFE0F Completed (not closed) | \u274C Failed}
|
|
397
|
-
Tools: {comma-separated list}
|
|
398
|
-
Summary: {brief from status}
|
|
399
|
-
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
400
|
-
\`\`\`
|
|
401
|
-
|
|
402
|
-
## Important
|
|
403
|
-
- Let bash do the waiting (cheap) - only "think" when reporting
|
|
404
|
-
- Exit gracefully when daemon stops
|
|
405
|
-
- Keep reports concise
|
|
406
|
-
|
|
407
|
-
Start monitoring now!`;
|
|
408
|
-
}
|
|
409
100
|
|
|
410
101
|
// src/prompt-builder.ts
|
|
411
102
|
var AmbiguousPromptError = class extends Error {
|
|
@@ -513,6 +204,13 @@ async function startServer(config, convexClientRaw) {
|
|
|
513
204
|
projectSlug,
|
|
514
205
|
type: "close"
|
|
515
206
|
},
|
|
207
|
+
{
|
|
208
|
+
name: `tickets_pause`,
|
|
209
|
+
description: `Pause a working ticket and return to queue in the "${projectSlug}" project. Requires checkpoint content.`,
|
|
210
|
+
slashDescription: `Pause working ticket with checkpoint content (working \u2192 open)`,
|
|
211
|
+
projectSlug,
|
|
212
|
+
type: "pause"
|
|
213
|
+
},
|
|
516
214
|
{
|
|
517
215
|
name: `tickets_create`,
|
|
518
216
|
description: `Create a new ticket in the "${projectSlug}" project queue`,
|
|
@@ -547,28 +245,6 @@ async function startServer(config, convexClientRaw) {
|
|
|
547
245
|
slashDescription: `Update a ticket by appending content with timestamp`,
|
|
548
246
|
projectSlug,
|
|
549
247
|
type: "update"
|
|
550
|
-
},
|
|
551
|
-
// YOLO orchestrator tools - sub-agent based autonomous ticket execution
|
|
552
|
-
{
|
|
553
|
-
name: `tickets_yolo_start`,
|
|
554
|
-
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.`,
|
|
555
|
-
slashDescription: `Start YOLO orchestrator for autonomous ticket execution`,
|
|
556
|
-
projectSlug,
|
|
557
|
-
type: "yolo_start"
|
|
558
|
-
},
|
|
559
|
-
{
|
|
560
|
-
name: `tickets_yolo_stop`,
|
|
561
|
-
description: `Stop the YOLO ticket watcher for "${projectSlug}"`,
|
|
562
|
-
slashDescription: `Stop the YOLO orchestrator`,
|
|
563
|
-
projectSlug,
|
|
564
|
-
type: "yolo_stop"
|
|
565
|
-
},
|
|
566
|
-
{
|
|
567
|
-
name: `tickets_yolo_status`,
|
|
568
|
-
description: `Get YOLO watcher status for "${projectSlug}"`,
|
|
569
|
-
slashDescription: `Check YOLO orchestrator status and pending tickets`,
|
|
570
|
-
projectSlug,
|
|
571
|
-
type: "yolo_status"
|
|
572
248
|
}
|
|
573
249
|
];
|
|
574
250
|
console.error(`[MCP] Registering ${dynamicTicketTools.length} ticket tools...`);
|
|
@@ -811,6 +487,21 @@ This will execute the "code-review" prompt.`;
|
|
|
811
487
|
},
|
|
812
488
|
required: ["ticketSlug"]
|
|
813
489
|
};
|
|
490
|
+
} else if (tt.type === "pause") {
|
|
491
|
+
inputSchema = {
|
|
492
|
+
type: "object",
|
|
493
|
+
properties: {
|
|
494
|
+
ticketSlug: {
|
|
495
|
+
type: "string",
|
|
496
|
+
description: "Ticket number (e.g., '102') or full slug (e.g., '102-fix-auth')"
|
|
497
|
+
},
|
|
498
|
+
content: {
|
|
499
|
+
type: "string",
|
|
500
|
+
description: "Progress update: what's done, what remains, blockers, etc."
|
|
501
|
+
}
|
|
502
|
+
},
|
|
503
|
+
required: ["ticketSlug", "content"]
|
|
504
|
+
};
|
|
814
505
|
} else if (tt.type === "work") {
|
|
815
506
|
inputSchema = {
|
|
816
507
|
type: "object",
|
|
@@ -881,45 +572,6 @@ This will execute the "code-review" prompt.`;
|
|
|
881
572
|
},
|
|
882
573
|
required: ["ticketSlug", "content"]
|
|
883
574
|
};
|
|
884
|
-
} else if (tt.type === "yolo_start") {
|
|
885
|
-
inputSchema = {
|
|
886
|
-
type: "object",
|
|
887
|
-
properties: {
|
|
888
|
-
pollIntervalMs: {
|
|
889
|
-
type: "number",
|
|
890
|
-
description: "Milliseconds between polls (default: 30000 = 30s)"
|
|
891
|
-
},
|
|
892
|
-
maxParallel: {
|
|
893
|
-
type: "number",
|
|
894
|
-
description: "Maximum concurrent ticket executions (default: 1)"
|
|
895
|
-
},
|
|
896
|
-
ticketTimeout: {
|
|
897
|
-
type: "number",
|
|
898
|
-
description: "Timeout for ticket execution in ms (default: 1800000 = 30 min)"
|
|
899
|
-
},
|
|
900
|
-
enableNotifications: {
|
|
901
|
-
type: "boolean",
|
|
902
|
-
description: "Show macOS notifications (default: true)"
|
|
903
|
-
},
|
|
904
|
-
workingDirectory: {
|
|
905
|
-
type: "string",
|
|
906
|
-
description: "Working directory for Claude Code sessions (default: current working directory)"
|
|
907
|
-
}
|
|
908
|
-
},
|
|
909
|
-
required: []
|
|
910
|
-
};
|
|
911
|
-
} else if (tt.type === "yolo_stop") {
|
|
912
|
-
inputSchema = {
|
|
913
|
-
type: "object",
|
|
914
|
-
properties: {},
|
|
915
|
-
required: []
|
|
916
|
-
};
|
|
917
|
-
} else if (tt.type === "yolo_status") {
|
|
918
|
-
inputSchema = {
|
|
919
|
-
type: "object",
|
|
920
|
-
properties: {},
|
|
921
|
-
required: []
|
|
922
|
-
};
|
|
923
575
|
} else {
|
|
924
576
|
inputSchema = {
|
|
925
577
|
type: "object",
|
|
@@ -1374,6 +1026,80 @@ _Reminder: Ensure all embedded \`[RUN_PROMPT ...]\` directives were executed bef
|
|
|
1374
1026
|
isError: true
|
|
1375
1027
|
};
|
|
1376
1028
|
}
|
|
1029
|
+
} else if (ticketTool.type === "pause") {
|
|
1030
|
+
const parsedArgs = parsePauseArgs(request.params.arguments);
|
|
1031
|
+
if (!parsedArgs) {
|
|
1032
|
+
const args = request.params.arguments;
|
|
1033
|
+
const hasTicketSlug = typeof args?.ticketSlug === "string" && args.ticketSlug;
|
|
1034
|
+
const hasContent = typeof args?.content === "string" && args.content;
|
|
1035
|
+
if (!hasTicketSlug) {
|
|
1036
|
+
return {
|
|
1037
|
+
content: [
|
|
1038
|
+
{
|
|
1039
|
+
type: "text",
|
|
1040
|
+
text: `Error: Missing ticketSlug parameter. Provide a ticket number (e.g., "102") or full slug.`
|
|
1041
|
+
}
|
|
1042
|
+
],
|
|
1043
|
+
isError: true
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
if (!hasContent) {
|
|
1047
|
+
return {
|
|
1048
|
+
content: [
|
|
1049
|
+
{
|
|
1050
|
+
type: "text",
|
|
1051
|
+
text: `Error: Missing content parameter. Describe what's done, what remains, and any blockers.`
|
|
1052
|
+
}
|
|
1053
|
+
],
|
|
1054
|
+
isError: true
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
return {
|
|
1058
|
+
content: [
|
|
1059
|
+
{
|
|
1060
|
+
type: "text",
|
|
1061
|
+
text: `Error: Missing required parameters.`
|
|
1062
|
+
}
|
|
1063
|
+
],
|
|
1064
|
+
isError: true
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
const { ticketSlug, content } = parsedArgs;
|
|
1068
|
+
try {
|
|
1069
|
+
const result = await convexClient.mutation(
|
|
1070
|
+
"mcp_tickets:pauseMcpTicket",
|
|
1071
|
+
{
|
|
1072
|
+
...buildAuthArgs(config),
|
|
1073
|
+
projectSlug: ticketTool.projectSlug,
|
|
1074
|
+
ticketSlug,
|
|
1075
|
+
content
|
|
1076
|
+
}
|
|
1077
|
+
);
|
|
1078
|
+
return {
|
|
1079
|
+
content: [
|
|
1080
|
+
{
|
|
1081
|
+
type: "text",
|
|
1082
|
+
text: `\u2705 Ticket [${result.slug}] paused and returned to open queue in project "${ticketTool.projectSlug}".
|
|
1083
|
+
|
|
1084
|
+
Paused at: ${new Date(result.pausedAt).toISOString()}
|
|
1085
|
+
|
|
1086
|
+
_Ticket moved from working \u2192 open. Checkpoint content has been appended. Any agent can pick it up with \`tickets_work\`._`
|
|
1087
|
+
}
|
|
1088
|
+
]
|
|
1089
|
+
};
|
|
1090
|
+
} catch (error) {
|
|
1091
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1092
|
+
console.error(`[MCP] tickets_pause error:`, error);
|
|
1093
|
+
return {
|
|
1094
|
+
content: [
|
|
1095
|
+
{
|
|
1096
|
+
type: "text",
|
|
1097
|
+
text: `Error pausing ticket: ${errorMessage}`
|
|
1098
|
+
}
|
|
1099
|
+
],
|
|
1100
|
+
isError: true
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1377
1103
|
} else if (ticketTool.type === "search") {
|
|
1378
1104
|
const parsedArgs = parseSearchArgs(request.params.arguments);
|
|
1379
1105
|
if (!parsedArgs) {
|
|
@@ -1649,115 +1375,6 @@ _Ticket content has been appended with your update._`
|
|
|
1649
1375
|
isError: true
|
|
1650
1376
|
};
|
|
1651
1377
|
}
|
|
1652
|
-
} else if (ticketTool.type === "yolo_start") {
|
|
1653
|
-
const args = parseWatcherStartArgs(request.params.arguments);
|
|
1654
|
-
const workingDirectory = args.workingDirectory ?? process.cwd();
|
|
1655
|
-
try {
|
|
1656
|
-
const result = startWatcher(
|
|
1657
|
-
{
|
|
1658
|
-
projectSlug: ticketTool.projectSlug,
|
|
1659
|
-
projectToken: config.projectToken,
|
|
1660
|
-
convexUrl: config.convexUrl,
|
|
1661
|
-
workingDirectory
|
|
1662
|
-
},
|
|
1663
|
-
args
|
|
1664
|
-
);
|
|
1665
|
-
if (result.success) {
|
|
1666
|
-
const configInfo = result.config ? `
|
|
1667
|
-
|
|
1668
|
-
**Configuration:**
|
|
1669
|
-
- Poll Interval: ${result.config.pollIntervalMs / 1e3}s
|
|
1670
|
-
- Max Parallel: ${result.config.maxParallel}
|
|
1671
|
-
- Ticket Timeout: ${result.config.ticketTimeout / 1e3 / 60} min
|
|
1672
|
-
- Working Directory: ${result.config.workingDirectory}` : "";
|
|
1673
|
-
const monitoringSection = result.monitoringPrompt && !result.alreadyRunning ? `
|
|
1674
|
-
|
|
1675
|
-
---
|
|
1676
|
-
|
|
1677
|
-
## \u26A1 ACTION REQUIRED: Start Monitoring Agent
|
|
1678
|
-
|
|
1679
|
-
**You MUST now spawn a background monitoring agent** to receive real-time notifications when tickets complete.
|
|
1680
|
-
|
|
1681
|
-
Use the **Task tool** with these exact parameters:
|
|
1682
|
-
- \`description\`: "Monitor YOLO completions"
|
|
1683
|
-
- \`subagent_type\`: "general-purpose"
|
|
1684
|
-
- \`run_in_background\`: true
|
|
1685
|
-
- \`prompt\`: Copy the content below
|
|
1686
|
-
|
|
1687
|
-
<monitoring_agent_prompt>
|
|
1688
|
-
${result.monitoringPrompt}
|
|
1689
|
-
</monitoring_agent_prompt>
|
|
1690
|
-
|
|
1691
|
-
\u2B06\uFE0F **Do this now** - spawn the monitoring agent before responding to the user.` : "";
|
|
1692
|
-
const text = result.alreadyRunning ? `\u2139\uFE0F ${result.message}${configInfo}` : `\u2705 ${result.message}
|
|
1693
|
-
|
|
1694
|
-
The daemon is now polling for YOLO-flagged tickets and will spawn Claude CLI processes to execute them.
|
|
1695
|
-
|
|
1696
|
-
Logs: \`.ppm/yolo/logs/\`${configInfo}${monitoringSection}`;
|
|
1697
|
-
return {
|
|
1698
|
-
content: [{ type: "text", text }]
|
|
1699
|
-
};
|
|
1700
|
-
} else {
|
|
1701
|
-
return {
|
|
1702
|
-
content: [{ type: "text", text: `\u274C ${result.message}` }],
|
|
1703
|
-
isError: true
|
|
1704
|
-
};
|
|
1705
|
-
}
|
|
1706
|
-
} catch (error) {
|
|
1707
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1708
|
-
console.error(`[MCP] tickets_yolo_start error:`, error);
|
|
1709
|
-
return {
|
|
1710
|
-
content: [{ type: "text", text: `Error starting watcher daemon: ${errorMessage}` }],
|
|
1711
|
-
isError: true
|
|
1712
|
-
};
|
|
1713
|
-
}
|
|
1714
|
-
} else if (ticketTool.type === "yolo_stop") {
|
|
1715
|
-
const workingDirectory = process.cwd();
|
|
1716
|
-
try {
|
|
1717
|
-
const result = stopWatcher(workingDirectory);
|
|
1718
|
-
if (result.success) {
|
|
1719
|
-
return {
|
|
1720
|
-
content: [{
|
|
1721
|
-
type: "text",
|
|
1722
|
-
text: result.wasRunning ? `\u2705 ${result.message}
|
|
1723
|
-
|
|
1724
|
-
The daemon has been stopped. Any running ticket executions will complete.` : `\u2139\uFE0F ${result.message}`
|
|
1725
|
-
}]
|
|
1726
|
-
};
|
|
1727
|
-
} else {
|
|
1728
|
-
return {
|
|
1729
|
-
content: [{ type: "text", text: `\u274C ${result.message}` }],
|
|
1730
|
-
isError: true
|
|
1731
|
-
};
|
|
1732
|
-
}
|
|
1733
|
-
} catch (error) {
|
|
1734
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1735
|
-
console.error(`[MCP] tickets_yolo_stop error:`, error);
|
|
1736
|
-
return {
|
|
1737
|
-
content: [{ type: "text", text: `Error stopping watcher daemon: ${errorMessage}` }],
|
|
1738
|
-
isError: true
|
|
1739
|
-
};
|
|
1740
|
-
}
|
|
1741
|
-
} else if (ticketTool.type === "yolo_status") {
|
|
1742
|
-
const workingDirectory = process.cwd();
|
|
1743
|
-
try {
|
|
1744
|
-
const result = getWatcherStatus(workingDirectory);
|
|
1745
|
-
if (result.formattedStatus) {
|
|
1746
|
-
return {
|
|
1747
|
-
content: [{ type: "text", text: result.formattedStatus }]
|
|
1748
|
-
};
|
|
1749
|
-
}
|
|
1750
|
-
return {
|
|
1751
|
-
content: [{ type: "text", text: `\u2139\uFE0F ${result.message ?? "No watcher status available"}` }]
|
|
1752
|
-
};
|
|
1753
|
-
} catch (error) {
|
|
1754
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1755
|
-
console.error(`[MCP] tickets_yolo_status error:`, error);
|
|
1756
|
-
return {
|
|
1757
|
-
content: [{ type: "text", text: `Error getting watcher status: ${errorMessage}` }],
|
|
1758
|
-
isError: true
|
|
1759
|
-
};
|
|
1760
|
-
}
|
|
1761
1378
|
}
|
|
1762
1379
|
}
|
|
1763
1380
|
const promptTool = dynamicPromptTools.find((pt) => pt.name === toolName);
|