@promptprojectmanager/mcp-server 4.6.1 → 4.6.2
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/chunk-PY22KZ7Z.js +143 -0
- package/dist/chunk-PY22KZ7Z.js.map +1 -0
- package/dist/index.js +208 -261
- package/dist/index.js.map +1 -1
- package/dist/watcher/watcher_daemon.d.ts +1 -0
- package/dist/watcher/watcher_daemon.js +437 -0
- package/dist/watcher/watcher_daemon.js.map +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
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";
|
|
2
10
|
|
|
3
11
|
// src/index.ts
|
|
4
12
|
import minimist from "minimist";
|
|
@@ -101,159 +109,168 @@ function parseWatcherStartArgs(args) {
|
|
|
101
109
|
};
|
|
102
110
|
}
|
|
103
111
|
|
|
104
|
-
// src/watcher/
|
|
105
|
-
import {
|
|
106
|
-
import {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
+
return {
|
|
167
|
+
success: true,
|
|
168
|
+
message: `Watcher started (PID: ${pid})`,
|
|
169
|
+
pid,
|
|
170
|
+
alreadyRunning: false,
|
|
171
|
+
config: configWithoutToken
|
|
172
|
+
};
|
|
173
|
+
} catch (error) {
|
|
174
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
175
|
+
return {
|
|
176
|
+
success: false,
|
|
177
|
+
message: `Failed to start watcher: ${errorMessage}`
|
|
178
|
+
};
|
|
111
179
|
}
|
|
112
|
-
return dir;
|
|
113
|
-
}
|
|
114
|
-
function getStatusFile(workingDirectory) {
|
|
115
|
-
return join(getWatcherDir(workingDirectory), "status.json");
|
|
116
180
|
}
|
|
117
|
-
function
|
|
118
|
-
const
|
|
119
|
-
|
|
181
|
+
function getDaemonPath() {
|
|
182
|
+
const currentFile = fileURLToPath(import.meta.url);
|
|
183
|
+
const currentDir = dirname(currentFile);
|
|
184
|
+
return join(currentDir, "watcher", "watcher_daemon.js");
|
|
120
185
|
}
|
|
121
|
-
function
|
|
122
|
-
const
|
|
123
|
-
if (!
|
|
124
|
-
return
|
|
186
|
+
function stopWatcher(workingDirectory) {
|
|
187
|
+
const pid = readPid(workingDirectory);
|
|
188
|
+
if (!pid) {
|
|
189
|
+
return {
|
|
190
|
+
success: true,
|
|
191
|
+
message: "No watcher running (no PID file)",
|
|
192
|
+
wasRunning: false
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
if (!isWatcherRunning(workingDirectory)) {
|
|
196
|
+
removePid(workingDirectory);
|
|
197
|
+
return {
|
|
198
|
+
success: true,
|
|
199
|
+
message: "Watcher was not running (stale PID file cleaned up)",
|
|
200
|
+
wasRunning: false
|
|
201
|
+
};
|
|
125
202
|
}
|
|
126
203
|
try {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
204
|
+
process.kill(pid, "SIGTERM");
|
|
205
|
+
const status = readStatus(workingDirectory);
|
|
206
|
+
if (status) {
|
|
207
|
+
writeStatus(workingDirectory, {
|
|
208
|
+
...status,
|
|
209
|
+
state: "stopped"
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
removePid(workingDirectory);
|
|
213
|
+
return {
|
|
214
|
+
success: true,
|
|
215
|
+
message: `Watcher stopped (PID: ${pid})`,
|
|
216
|
+
wasRunning: true
|
|
217
|
+
};
|
|
218
|
+
} catch (error) {
|
|
219
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
220
|
+
removePid(workingDirectory);
|
|
221
|
+
return {
|
|
222
|
+
success: false,
|
|
223
|
+
message: `Failed to stop watcher: ${errorMessage}`,
|
|
224
|
+
wasRunning: true
|
|
225
|
+
};
|
|
131
226
|
}
|
|
132
227
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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");
|
|
228
|
+
function getWatcherStatus(workingDirectory) {
|
|
229
|
+
const status = readStatus(workingDirectory);
|
|
230
|
+
const isRunning = isWatcherRunning(workingDirectory);
|
|
231
|
+
if (status?.state === "running" && !isRunning) {
|
|
232
|
+
const updatedStatus = {
|
|
233
|
+
...status,
|
|
234
|
+
state: "stopped"
|
|
235
|
+
};
|
|
236
|
+
writeStatus(workingDirectory, updatedStatus);
|
|
237
|
+
return {
|
|
238
|
+
success: true,
|
|
239
|
+
status: updatedStatus,
|
|
240
|
+
formattedStatus: formatStatus(updatedStatus),
|
|
241
|
+
message: "Watcher died unexpectedly - status updated"
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
if (!status) {
|
|
245
|
+
return {
|
|
246
|
+
success: true,
|
|
247
|
+
message: "No watcher session found for this project",
|
|
248
|
+
formattedStatus: formatStatus(null)
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
success: true,
|
|
253
|
+
status,
|
|
254
|
+
formattedStatus: formatStatus(status)
|
|
255
|
+
};
|
|
242
256
|
}
|
|
243
|
-
function
|
|
257
|
+
function formatStatus(status) {
|
|
244
258
|
if (!status) {
|
|
245
|
-
return `# YOLO
|
|
259
|
+
return `# YOLO Watcher Status
|
|
246
260
|
|
|
247
261
|
**State**: Not started
|
|
248
262
|
|
|
249
|
-
_Use \`tickets_yolo_start\` to begin
|
|
263
|
+
_Use \`tickets_yolo_start\` to begin watching for YOLO tickets._`;
|
|
250
264
|
}
|
|
251
265
|
const lines = [
|
|
252
|
-
"# YOLO
|
|
266
|
+
"# YOLO Watcher Status",
|
|
253
267
|
"",
|
|
254
268
|
`**State**: ${status.state}`,
|
|
255
269
|
`**Project**: ${status.projectSlug}`
|
|
256
270
|
];
|
|
271
|
+
if (status.pid) {
|
|
272
|
+
lines.push(`**Daemon PID**: ${status.pid}`);
|
|
273
|
+
}
|
|
257
274
|
if (status.startedAt) {
|
|
258
275
|
lines.push(`**Started**: ${status.startedAt}`);
|
|
259
276
|
}
|
|
@@ -261,27 +278,38 @@ _Use \`tickets_yolo_start\` to begin orchestration._`;
|
|
|
261
278
|
lines.push(`**Last Poll**: ${status.lastPollAt}`);
|
|
262
279
|
}
|
|
263
280
|
lines.push(`**Tickets Processed**: ${status.ticketsProcessed}`);
|
|
281
|
+
if (status.config) {
|
|
282
|
+
lines.push("");
|
|
283
|
+
lines.push("## Configuration");
|
|
284
|
+
lines.push(`- Poll Interval: ${status.config.pollIntervalMs / 1e3}s`);
|
|
285
|
+
lines.push(`- Max Parallel: ${status.config.maxParallel}`);
|
|
286
|
+
lines.push(`- Ticket Timeout: ${status.config.ticketTimeout / 1e3 / 60} min`);
|
|
287
|
+
lines.push(`- Notifications: ${status.config.enableNotifications ? "enabled" : "disabled"}`);
|
|
288
|
+
}
|
|
264
289
|
if (status.currentlyExecuting && status.currentlyExecuting.length > 0) {
|
|
265
290
|
lines.push("");
|
|
266
|
-
lines.push("## Currently Executing
|
|
291
|
+
lines.push("## Currently Executing");
|
|
267
292
|
for (const exec of status.currentlyExecuting) {
|
|
268
293
|
const num = exec.ticketNumber ? `#${exec.ticketNumber}` : "";
|
|
269
294
|
const elapsed = getElapsedTime(exec.startedAt);
|
|
270
295
|
lines.push(`- **${num} ${exec.ticketSlug}** (${elapsed})`);
|
|
271
296
|
}
|
|
272
297
|
}
|
|
273
|
-
|
|
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:");
|
|
298
|
+
if (status.completedTickets && status.completedTickets.length > 0) {
|
|
278
299
|
lines.push("");
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
300
|
+
lines.push("## Recently Completed");
|
|
301
|
+
for (const completed of status.completedTickets.slice(0, 10)) {
|
|
302
|
+
const num = completed.ticketNumber ? `#${completed.ticketNumber}` : "";
|
|
303
|
+
const emoji = completed.success ? "\u2705" : "\u274C";
|
|
304
|
+
const duration = formatDuration(completed.durationMs);
|
|
305
|
+
const summary = completed.summary ? ` - ${completed.summary.slice(0, 50)}` : "";
|
|
306
|
+
lines.push(`- ${emoji} **${num} ${completed.ticketSlug}** (${duration})${summary}`);
|
|
282
307
|
}
|
|
283
|
-
}
|
|
284
|
-
|
|
308
|
+
}
|
|
309
|
+
if (status.lastError) {
|
|
310
|
+
lines.push("");
|
|
311
|
+
lines.push("## Last Error");
|
|
312
|
+
lines.push(`\`${status.lastError}\``);
|
|
285
313
|
}
|
|
286
314
|
return lines.join("\n");
|
|
287
315
|
}
|
|
@@ -295,87 +323,17 @@ function getElapsedTime(startedAt) {
|
|
|
295
323
|
}
|
|
296
324
|
return `${seconds}s`;
|
|
297
325
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
const workingDirectory = args?.workingDirectory ?? baseConfig.workingDirectory;
|
|
302
|
-
const orchestratorConfig = {
|
|
303
|
-
projectSlug: baseConfig.projectSlug,
|
|
304
|
-
pollIntervalMs: args?.pollIntervalMs ?? 3e4,
|
|
305
|
-
maxParallel: args?.maxParallel ?? 1,
|
|
306
|
-
ticketTimeout: args?.ticketTimeout ?? 18e5,
|
|
307
|
-
workingDirectory
|
|
308
|
-
};
|
|
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
|
|
324
|
-
}
|
|
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
|
-
};
|
|
335
|
-
}
|
|
336
|
-
function stopOrchestrator(workingDirectory) {
|
|
337
|
-
const status = readStatus(workingDirectory);
|
|
338
|
-
if (!status) {
|
|
339
|
-
return {
|
|
340
|
-
success: true,
|
|
341
|
-
message: "No orchestrator session found",
|
|
342
|
-
wasRunning: false
|
|
343
|
-
};
|
|
344
|
-
}
|
|
345
|
-
if (status.state !== "running") {
|
|
346
|
-
return {
|
|
347
|
-
success: true,
|
|
348
|
-
message: `Orchestrator was already ${status.state}`,
|
|
349
|
-
wasRunning: false
|
|
350
|
-
};
|
|
326
|
+
function formatDuration(ms) {
|
|
327
|
+
if (ms < 1e3) {
|
|
328
|
+
return `${ms}ms`;
|
|
351
329
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
success: true,
|
|
358
|
-
message: "Orchestrator stopped",
|
|
359
|
-
wasRunning: true
|
|
360
|
-
};
|
|
361
|
-
}
|
|
362
|
-
function getOrchestratorStatus(workingDirectory, pendingTickets) {
|
|
363
|
-
const status = readStatus(workingDirectory);
|
|
364
|
-
const formattedStatus = formatOrchestratorStatus(status ?? null, pendingTickets);
|
|
365
|
-
if (!status) {
|
|
366
|
-
return {
|
|
367
|
-
success: true,
|
|
368
|
-
message: "No orchestrator session has been started for this project",
|
|
369
|
-
pendingTickets,
|
|
370
|
-
formattedStatus
|
|
371
|
-
};
|
|
330
|
+
const seconds = Math.floor(ms / 1e3);
|
|
331
|
+
const minutes = Math.floor(seconds / 60);
|
|
332
|
+
if (minutes > 0) {
|
|
333
|
+
const remainingSeconds = seconds % 60;
|
|
334
|
+
return `${minutes}m ${remainingSeconds}s`;
|
|
372
335
|
}
|
|
373
|
-
return {
|
|
374
|
-
success: true,
|
|
375
|
-
status,
|
|
376
|
-
pendingTickets,
|
|
377
|
-
formattedStatus
|
|
378
|
-
};
|
|
336
|
+
return `${seconds}s`;
|
|
379
337
|
}
|
|
380
338
|
|
|
381
339
|
// src/prompt-builder.ts
|
|
@@ -1624,31 +1582,30 @@ _Ticket content has been appended with your update._`
|
|
|
1624
1582
|
const args = parseWatcherStartArgs(request.params.arguments);
|
|
1625
1583
|
const workingDirectory = args.workingDirectory ?? process.cwd();
|
|
1626
1584
|
try {
|
|
1627
|
-
|
|
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(
|
|
1585
|
+
const result = startWatcher(
|
|
1640
1586
|
{
|
|
1641
1587
|
projectSlug: ticketTool.projectSlug,
|
|
1642
1588
|
projectToken: config.projectToken,
|
|
1643
1589
|
convexUrl: config.convexUrl,
|
|
1644
1590
|
workingDirectory
|
|
1645
1591
|
},
|
|
1646
|
-
args
|
|
1647
|
-
pendingTickets
|
|
1592
|
+
args
|
|
1648
1593
|
);
|
|
1649
|
-
if (result.success
|
|
1594
|
+
if (result.success) {
|
|
1595
|
+
const configInfo = result.config ? `
|
|
1596
|
+
|
|
1597
|
+
**Configuration:**
|
|
1598
|
+
- Poll Interval: ${result.config.pollIntervalMs / 1e3}s
|
|
1599
|
+
- Max Parallel: ${result.config.maxParallel}
|
|
1600
|
+
- Ticket Timeout: ${result.config.ticketTimeout / 1e3 / 60} min
|
|
1601
|
+
- Working Directory: ${result.config.workingDirectory}` : "";
|
|
1602
|
+
const text = result.alreadyRunning ? `\u2139\uFE0F ${result.message}${configInfo}` : `\u2705 ${result.message}
|
|
1603
|
+
|
|
1604
|
+
The daemon is now polling for YOLO-flagged tickets and will spawn Claude CLI processes to execute them.
|
|
1605
|
+
|
|
1606
|
+
Logs: \`.ppm/yolo/logs/\`${configInfo}`;
|
|
1650
1607
|
return {
|
|
1651
|
-
content: [{ type: "text", text
|
|
1608
|
+
content: [{ type: "text", text }]
|
|
1652
1609
|
};
|
|
1653
1610
|
} else {
|
|
1654
1611
|
return {
|
|
@@ -1660,19 +1617,21 @@ _Ticket content has been appended with your update._`
|
|
|
1660
1617
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1661
1618
|
console.error(`[MCP] tickets_yolo_start error:`, error);
|
|
1662
1619
|
return {
|
|
1663
|
-
content: [{ type: "text", text: `Error starting
|
|
1620
|
+
content: [{ type: "text", text: `Error starting watcher daemon: ${errorMessage}` }],
|
|
1664
1621
|
isError: true
|
|
1665
1622
|
};
|
|
1666
1623
|
}
|
|
1667
1624
|
} else if (ticketTool.type === "yolo_stop") {
|
|
1668
1625
|
const workingDirectory = process.cwd();
|
|
1669
1626
|
try {
|
|
1670
|
-
const result =
|
|
1627
|
+
const result = stopWatcher(workingDirectory);
|
|
1671
1628
|
if (result.success) {
|
|
1672
1629
|
return {
|
|
1673
1630
|
content: [{
|
|
1674
1631
|
type: "text",
|
|
1675
|
-
text: result.wasRunning ? `\u2705
|
|
1632
|
+
text: result.wasRunning ? `\u2705 ${result.message}
|
|
1633
|
+
|
|
1634
|
+
The daemon has been stopped. Any running ticket executions will complete.` : `\u2139\uFE0F ${result.message}`
|
|
1676
1635
|
}]
|
|
1677
1636
|
};
|
|
1678
1637
|
} else {
|
|
@@ -1685,39 +1644,27 @@ _Ticket content has been appended with your update._`
|
|
|
1685
1644
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1686
1645
|
console.error(`[MCP] tickets_yolo_stop error:`, error);
|
|
1687
1646
|
return {
|
|
1688
|
-
content: [{ type: "text", text: `Error stopping
|
|
1647
|
+
content: [{ type: "text", text: `Error stopping watcher daemon: ${errorMessage}` }],
|
|
1689
1648
|
isError: true
|
|
1690
1649
|
};
|
|
1691
1650
|
}
|
|
1692
1651
|
} else if (ticketTool.type === "yolo_status") {
|
|
1693
1652
|
const workingDirectory = process.cwd();
|
|
1694
1653
|
try {
|
|
1695
|
-
|
|
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);
|
|
1654
|
+
const result = getWatcherStatus(workingDirectory);
|
|
1708
1655
|
if (result.formattedStatus) {
|
|
1709
1656
|
return {
|
|
1710
1657
|
content: [{ type: "text", text: result.formattedStatus }]
|
|
1711
1658
|
};
|
|
1712
1659
|
}
|
|
1713
1660
|
return {
|
|
1714
|
-
content: [{ type: "text", text: `\u2139\uFE0F ${result.message ?? "No
|
|
1661
|
+
content: [{ type: "text", text: `\u2139\uFE0F ${result.message ?? "No watcher status available"}` }]
|
|
1715
1662
|
};
|
|
1716
1663
|
} catch (error) {
|
|
1717
1664
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1718
1665
|
console.error(`[MCP] tickets_yolo_status error:`, error);
|
|
1719
1666
|
return {
|
|
1720
|
-
content: [{ type: "text", text: `Error getting
|
|
1667
|
+
content: [{ type: "text", text: `Error getting watcher status: ${errorMessage}` }],
|
|
1721
1668
|
isError: true
|
|
1722
1669
|
};
|
|
1723
1670
|
}
|