@promptprojectmanager/mcp-server 4.6.0 → 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.
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
+ }) : x)(function(x) {
5
+ if (typeof require !== "undefined") return require.apply(this, arguments);
6
+ throw Error('Dynamic require of "' + x + '" is not supported');
7
+ });
8
+
9
+ // src/watcher/watcher_state.ts
10
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
11
+ import { join } from "path";
12
+ function getWatcherDir(workingDirectory) {
13
+ const dir = join(workingDirectory, ".ppm", "yolo");
14
+ if (!existsSync(dir)) {
15
+ mkdirSync(dir, { recursive: true });
16
+ }
17
+ return dir;
18
+ }
19
+ function getPidFile(workingDirectory) {
20
+ return join(getWatcherDir(workingDirectory), "watcher.pid");
21
+ }
22
+ function getStatusFile(workingDirectory) {
23
+ return join(getWatcherDir(workingDirectory), "status.json");
24
+ }
25
+ function getLogFile(workingDirectory) {
26
+ return join(getWatcherDir(workingDirectory), "watcher.log");
27
+ }
28
+ function getLogsDir(workingDirectory) {
29
+ const dir = join(getWatcherDir(workingDirectory), "logs");
30
+ if (!existsSync(dir)) {
31
+ mkdirSync(dir, { recursive: true });
32
+ }
33
+ return dir;
34
+ }
35
+ function getTicketLogFile(workingDirectory, ticketSlug) {
36
+ const safeSlug = ticketSlug.replace(/[^a-zA-Z0-9_-]/g, "_");
37
+ return join(getLogsDir(workingDirectory), `${safeSlug}.log`);
38
+ }
39
+ function readTicketLog(workingDirectory, ticketSlug) {
40
+ const logFile = getTicketLogFile(workingDirectory, ticketSlug);
41
+ if (!existsSync(logFile)) {
42
+ return void 0;
43
+ }
44
+ try {
45
+ return readFileSync(logFile, "utf-8");
46
+ } catch {
47
+ return void 0;
48
+ }
49
+ }
50
+ function writePid(workingDirectory, pid) {
51
+ const pidFile = getPidFile(workingDirectory);
52
+ writeFileSync(pidFile, pid.toString(), "utf-8");
53
+ }
54
+ function readPid(workingDirectory) {
55
+ const pidFile = getPidFile(workingDirectory);
56
+ if (!existsSync(pidFile)) {
57
+ return void 0;
58
+ }
59
+ try {
60
+ const content = readFileSync(pidFile, "utf-8").trim();
61
+ const pid = parseInt(content, 10);
62
+ return isNaN(pid) ? void 0 : pid;
63
+ } catch {
64
+ return void 0;
65
+ }
66
+ }
67
+ function removePid(workingDirectory) {
68
+ const pidFile = getPidFile(workingDirectory);
69
+ if (existsSync(pidFile)) {
70
+ try {
71
+ unlinkSync(pidFile);
72
+ } catch {
73
+ }
74
+ }
75
+ }
76
+ function isProcessRunning(pid) {
77
+ try {
78
+ process.kill(pid, 0);
79
+ return true;
80
+ } catch {
81
+ return false;
82
+ }
83
+ }
84
+ function isWatcherRunning(workingDirectory) {
85
+ const pid = readPid(workingDirectory);
86
+ if (pid === void 0) {
87
+ return false;
88
+ }
89
+ if (isProcessRunning(pid)) {
90
+ return true;
91
+ }
92
+ removePid(workingDirectory);
93
+ return false;
94
+ }
95
+ function writeStatus(workingDirectory, status) {
96
+ const statusFile = getStatusFile(workingDirectory);
97
+ writeFileSync(statusFile, JSON.stringify(status, null, 2), "utf-8");
98
+ }
99
+ function readStatus(workingDirectory) {
100
+ const statusFile = getStatusFile(workingDirectory);
101
+ if (!existsSync(statusFile)) {
102
+ return void 0;
103
+ }
104
+ try {
105
+ const content = readFileSync(statusFile, "utf-8");
106
+ return JSON.parse(content);
107
+ } catch {
108
+ return void 0;
109
+ }
110
+ }
111
+ var MAX_COMPLETED_TICKETS = 20;
112
+ function addCompletedTicket(workingDirectory, completed) {
113
+ const status = readStatus(workingDirectory);
114
+ if (!status) return;
115
+ const completedTickets = [
116
+ completed,
117
+ ...status.completedTickets ?? []
118
+ ].slice(0, MAX_COMPLETED_TICKETS);
119
+ const currentlyExecuting = (status.currentlyExecuting ?? []).filter(
120
+ (t) => t.ticketSlug !== completed.ticketSlug
121
+ );
122
+ writeStatus(workingDirectory, {
123
+ ...status,
124
+ completedTickets,
125
+ currentlyExecuting
126
+ });
127
+ }
128
+
129
+ export {
130
+ __require,
131
+ getWatcherDir,
132
+ getLogFile,
133
+ getTicketLogFile,
134
+ readTicketLog,
135
+ writePid,
136
+ readPid,
137
+ removePid,
138
+ isWatcherRunning,
139
+ writeStatus,
140
+ readStatus,
141
+ addCompletedTicket
142
+ };
143
+ //# sourceMappingURL=chunk-PY22KZ7Z.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/watcher/watcher_state.ts"],"sourcesContent":["/**\n * Watcher State Management\n *\n * Handles file-based state persistence for the YOLO watcher daemon.\n * State is stored in .ppm/yolo/ within the project directory.\n *\n * Files:\n * - watcher.pid: Process ID of running daemon\n * - status.json: Full watcher status\n * - watcher.log: Daemon log output\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from \"fs\";\nimport { join } from \"path\";\nimport type { WatcherStatus, CompletedTicket } from \"./watcher_types.js\";\n\n// ============================================================================\n// Path Utilities\n// ============================================================================\n\n/**\n * Get the watcher state directory for a project.\n * Creates the directory if it doesn't exist.\n *\n * @param workingDirectory - Project root directory\n * @returns Path to .ppm/yolo/ directory\n */\nexport function getWatcherDir(workingDirectory: string): string {\n const dir = join(workingDirectory, \".ppm\", \"yolo\");\n\n // Ensure directory exists\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n return dir;\n}\n\n/**\n * Get path to the PID file\n */\nexport function getPidFile(workingDirectory: string): string {\n return join(getWatcherDir(workingDirectory), \"watcher.pid\");\n}\n\n/**\n * Get path to the status JSON file\n */\nexport function getStatusFile(workingDirectory: string): string {\n return join(getWatcherDir(workingDirectory), \"status.json\");\n}\n\n/**\n * Get path to the daemon log file\n */\nexport function getLogFile(workingDirectory: string): string {\n return join(getWatcherDir(workingDirectory), \"watcher.log\");\n}\n\n/**\n * Get the logs directory for ticket execution logs.\n * Creates the directory if it doesn't exist.\n *\n * @param workingDirectory - Project root directory\n * @returns Path to .ppm/yolo/logs/ directory\n */\nexport function getLogsDir(workingDirectory: string): string {\n const dir = join(getWatcherDir(workingDirectory), \"logs\");\n\n // Ensure directory exists\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n return dir;\n}\n\n/**\n * Get path to a specific ticket's log file.\n *\n * @param workingDirectory - Project root directory\n * @param ticketSlug - Ticket identifier (e.g., \"483-test-joke\")\n * @returns Path to log file (e.g., .ppm/yolo/logs/483-test-joke.log)\n */\nexport function getTicketLogFile(workingDirectory: string, ticketSlug: string): string {\n // Sanitize slug to be safe for filesystem\n const safeSlug = ticketSlug.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n return join(getLogsDir(workingDirectory), `${safeSlug}.log`);\n}\n\n/**\n * Read a ticket's execution log file.\n *\n * @param workingDirectory - Project root directory\n * @param ticketSlug - Ticket identifier\n * @returns Log file contents or undefined if not found\n */\nexport function readTicketLog(workingDirectory: string, ticketSlug: string): string | undefined {\n const logFile = getTicketLogFile(workingDirectory, ticketSlug);\n\n if (!existsSync(logFile)) {\n return undefined;\n }\n\n try {\n return readFileSync(logFile, \"utf-8\");\n } catch {\n return undefined;\n }\n}\n\n// ============================================================================\n// PID Management\n// ============================================================================\n\n/**\n * Write the daemon's PID to the PID file\n *\n * @param workingDirectory - Project root directory\n * @param pid - Process ID to write\n */\nexport function writePid(workingDirectory: string, pid: number): void {\n const pidFile = getPidFile(workingDirectory);\n writeFileSync(pidFile, pid.toString(), \"utf-8\");\n}\n\n/**\n * Read the daemon's PID from the PID file\n *\n * @param workingDirectory - Project root directory\n * @returns PID if file exists, undefined otherwise\n */\nexport function readPid(workingDirectory: string): number | undefined {\n const pidFile = getPidFile(workingDirectory);\n\n if (!existsSync(pidFile)) {\n return undefined;\n }\n\n try {\n const content = readFileSync(pidFile, \"utf-8\").trim();\n const pid = parseInt(content, 10);\n return isNaN(pid) ? undefined : pid;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Remove the PID file\n *\n * @param workingDirectory - Project root directory\n */\nexport function removePid(workingDirectory: string): void {\n const pidFile = getPidFile(workingDirectory);\n\n if (existsSync(pidFile)) {\n try {\n unlinkSync(pidFile);\n } catch {\n // Ignore errors during cleanup\n }\n }\n}\n\n/**\n * Check if a process with the given PID is running\n *\n * Uses process.kill(pid, 0) which doesn't actually send a signal\n * but throws if the process doesn't exist.\n *\n * @param pid - Process ID to check\n * @returns true if process is running\n */\nexport function isProcessRunning(pid: number): boolean {\n try {\n // Signal 0 doesn't kill the process, just checks if it exists\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Check if the watcher daemon is currently running\n *\n * Reads the PID file and verifies the process is still alive.\n * Cleans up stale PID file if process is not running.\n *\n * @param workingDirectory - Project root directory\n * @returns true if watcher is running\n */\nexport function isWatcherRunning(workingDirectory: string): boolean {\n const pid = readPid(workingDirectory);\n\n if (pid === undefined) {\n return false;\n }\n\n if (isProcessRunning(pid)) {\n return true;\n }\n\n // Stale PID file - process is not running\n // Clean up the file\n removePid(workingDirectory);\n return false;\n}\n\n// ============================================================================\n// Status Management\n// ============================================================================\n\n/**\n * Write watcher status to the status file\n *\n * @param workingDirectory - Project root directory\n * @param status - Status object to persist\n */\nexport function writeStatus(workingDirectory: string, status: WatcherStatus): void {\n const statusFile = getStatusFile(workingDirectory);\n writeFileSync(statusFile, JSON.stringify(status, null, 2), \"utf-8\");\n}\n\n/**\n * Read watcher status from the status file\n *\n * @param workingDirectory - Project root directory\n * @returns Status object if file exists and is valid, undefined otherwise\n */\nexport function readStatus(workingDirectory: string): WatcherStatus | undefined {\n const statusFile = getStatusFile(workingDirectory);\n\n if (!existsSync(statusFile)) {\n return undefined;\n }\n\n try {\n const content = readFileSync(statusFile, \"utf-8\");\n return JSON.parse(content) as WatcherStatus;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Update specific fields in the status file\n *\n * Reads current status, merges updates, and writes back.\n * Creates new status if none exists.\n *\n * @param workingDirectory - Project root directory\n * @param updates - Partial status updates to apply\n */\nexport function updateStatus(\n workingDirectory: string,\n updates: Partial<WatcherStatus>\n): void {\n const current = readStatus(workingDirectory);\n\n if (current) {\n writeStatus(workingDirectory, { ...current, ...updates });\n } else if (updates.state && updates.projectSlug && updates.config) {\n // Create new status with required fields\n writeStatus(workingDirectory, {\n state: updates.state,\n projectSlug: updates.projectSlug,\n ticketsProcessed: updates.ticketsProcessed ?? 0,\n currentlyExecuting: updates.currentlyExecuting ?? [],\n config: updates.config,\n ...updates,\n });\n }\n}\n\n/**\n * Remove the status file\n *\n * @param workingDirectory - Project root directory\n */\nexport function removeStatus(workingDirectory: string): void {\n const statusFile = getStatusFile(workingDirectory);\n\n if (existsSync(statusFile)) {\n try {\n unlinkSync(statusFile);\n } catch {\n // Ignore errors during cleanup\n }\n }\n}\n\n// ============================================================================\n// Completed Tickets Tracking\n// ============================================================================\n\n/** Maximum number of completed tickets to keep in status */\nconst MAX_COMPLETED_TICKETS = 20;\n\n/**\n * Add a completed ticket to the status.\n *\n * Keeps the last N completed tickets for status display.\n * Older entries are removed to prevent unbounded growth.\n *\n * @param workingDirectory - Project root directory\n * @param completed - Completed ticket information\n */\nexport function addCompletedTicket(\n workingDirectory: string,\n completed: CompletedTicket\n): void {\n const status = readStatus(workingDirectory);\n if (!status) return;\n\n // Add to completed list, keeping only last N\n const completedTickets = [\n completed,\n ...(status.completedTickets ?? []),\n ].slice(0, MAX_COMPLETED_TICKETS);\n\n // Remove from currently executing if present\n const currentlyExecuting = (status.currentlyExecuting ?? []).filter(\n (t) => t.ticketSlug !== completed.ticketSlug\n );\n\n writeStatus(workingDirectory, {\n ...status,\n completedTickets,\n currentlyExecuting,\n });\n}\n\n// ============================================================================\n// Cleanup\n// ============================================================================\n\n/**\n * Clean up all watcher state files\n *\n * Used when stopping the watcher or cleaning up after errors.\n *\n * @param workingDirectory - Project root directory\n */\nexport function cleanupWatcherState(workingDirectory: string): void {\n removePid(workingDirectory);\n // Note: We keep status.json for debugging - it shows last known state\n // The status file's state field is set to 'stopped' instead\n}\n"],"mappings":"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","names":[]}