@promptprojectmanager/mcp-server 4.5.1 → 4.5.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.
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
|
+
export { }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/watcher/watcher_daemon.ts","../../src/watcher/watcher_spawn.ts","../../src/watcher/watcher_poll.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * Watcher Daemon Entry Point\n *\n * Standalone Node.js process that runs the YOLO ticket watcher.\n * Spawned as a detached child process by the controller.\n *\n * Usage: node watcher_daemon.js --config <base64-encoded-config>\n *\n * The daemon:\n * 1. Parses config from CLI argument\n * 2. Initializes Convex HTTP client\n * 3. Writes PID and initial status\n * 4. Starts polling loop\n * 5. Handles graceful shutdown on SIGTERM/SIGINT\n */\n\nimport { ConvexHttpClient } from \"convex/browser\";\nimport type { WatcherConfig, WatcherStatus } from \"./watcher_types.js\";\nimport { writePid, writeStatus, removePid, updateStatus, readStatus } from \"./watcher_state.js\";\nimport { pollAndExecute, checkTimeouts } from \"./watcher_poll.js\";\n\n// ============================================================================\n// Config Parsing\n// ============================================================================\n\n/**\n * Parse configuration from CLI arguments.\n *\n * Expects: --config <base64-encoded-json>\n */\nfunction parseConfigFromArgs(): WatcherConfig | null {\n const args = process.argv.slice(2);\n const configIndex = args.indexOf(\"--config\");\n\n if (configIndex === -1 || !args[configIndex + 1]) {\n console.error(\"Usage: watcher_daemon --config <base64-encoded-config>\");\n return null;\n }\n\n try {\n const base64Config = args[configIndex + 1];\n const jsonConfig = Buffer.from(base64Config, \"base64\").toString(\"utf-8\");\n return JSON.parse(jsonConfig) as WatcherConfig;\n } catch (error) {\n console.error(\"Failed to parse config:\", error);\n return null;\n }\n}\n\n// ============================================================================\n// Main Daemon Logic\n// ============================================================================\n\n/**\n * Main entry point for the daemon.\n */\nasync function main(): Promise<void> {\n log(\"Watcher daemon starting...\");\n\n // Parse config\n const config = parseConfigFromArgs();\n if (!config) {\n process.exit(1);\n }\n\n log(`Project: ${config.projectSlug}`);\n log(`Working directory: ${config.workingDirectory}`);\n log(`Poll interval: ${config.pollIntervalMs}ms`);\n log(`Max parallel: ${config.maxParallel}`);\n\n // Initialize Convex client\n const convexClient = new ConvexHttpClient(config.convexUrl);\n\n // Write PID file\n writePid(config.workingDirectory, process.pid);\n log(`PID file written: ${process.pid}`);\n\n // Write initial status\n const initialStatus: WatcherStatus = {\n state: \"running\",\n pid: process.pid,\n projectSlug: config.projectSlug,\n startedAt: new Date().toISOString(),\n ticketsProcessed: 0,\n currentlyExecuting: [],\n config: {\n projectSlug: config.projectSlug,\n convexUrl: config.convexUrl,\n pollIntervalMs: config.pollIntervalMs,\n maxParallel: config.maxParallel,\n ticketTimeout: config.ticketTimeout,\n enableNotifications: config.enableNotifications,\n workingDirectory: config.workingDirectory,\n },\n };\n writeStatus(config.workingDirectory, initialStatus);\n log(\"Status file written\");\n\n // Setup graceful shutdown\n let isShuttingDown = false;\n let pollInterval: ReturnType<typeof setInterval> | null = null;\n\n const shutdown = async (signal: string): Promise<void> => {\n if (isShuttingDown) return;\n isShuttingDown = true;\n\n log(`Received ${signal}, shutting down gracefully...`);\n\n // Stop polling\n if (pollInterval) {\n clearInterval(pollInterval);\n pollInterval = null;\n }\n\n // Update status to stopped\n const currentStatus = readStatus(config.workingDirectory);\n if (currentStatus) {\n updateStatus(config.workingDirectory, {\n ...currentStatus,\n state: \"stopped\",\n });\n }\n\n // Remove PID file\n removePid(config.workingDirectory);\n log(\"Cleanup complete, exiting\");\n\n process.exit(0);\n };\n\n process.on(\"SIGTERM\", () => shutdown(\"SIGTERM\"));\n process.on(\"SIGINT\", () => shutdown(\"SIGINT\"));\n process.on(\"SIGHUP\", () => shutdown(\"SIGHUP\"));\n\n // Handle uncaught errors\n process.on(\"uncaughtException\", (error) => {\n log(`Uncaught exception: ${error.message}`);\n const currentStatus = readStatus(config.workingDirectory);\n if (currentStatus) {\n updateStatus(config.workingDirectory, {\n ...currentStatus,\n state: \"error\",\n lastError: error.message,\n });\n }\n process.exit(1);\n });\n\n process.on(\"unhandledRejection\", (reason) => {\n const message = reason instanceof Error ? reason.message : String(reason);\n log(`Unhandled rejection: ${message}`);\n const currentStatus = readStatus(config.workingDirectory);\n if (currentStatus) {\n updateStatus(config.workingDirectory, {\n ...currentStatus,\n state: \"error\",\n lastError: message,\n });\n }\n process.exit(1);\n });\n\n // Start polling loop\n log(\"Starting polling loop...\");\n\n const poll = async (): Promise<void> => {\n if (isShuttingDown) return;\n\n try {\n // Check for timeouts first\n checkTimeouts(config);\n\n // Poll for new tickets\n await pollAndExecute(config, convexClient);\n\n // Update lastPollAt\n const currentStatus = readStatus(config.workingDirectory);\n if (currentStatus) {\n updateStatus(config.workingDirectory, {\n ...currentStatus,\n lastPollAt: new Date().toISOString(),\n });\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n log(`Poll error: ${message}`);\n // Don't crash on poll errors, just log and continue\n }\n };\n\n // Run initial poll immediately\n await poll();\n\n // Schedule recurring polls\n pollInterval = setInterval(poll, config.pollIntervalMs);\n\n log(\"Watcher daemon running\");\n}\n\n// ============================================================================\n// Logging\n// ============================================================================\n\n/**\n * Log a message with timestamp prefix.\n */\nfunction log(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [daemon] ${message}`);\n}\n\n// ============================================================================\n// Entry Point\n// ============================================================================\n\nmain().catch((error) => {\n console.error(\"Fatal error:\", error);\n process.exit(1);\n});\n","/**\n * Terminal Spawner\n *\n * Spawns new Terminal.app windows with Claude Code for autonomous ticket execution.\n * Uses AppleScript via osascript to create visible terminal windows.\n *\n * This allows users to:\n * - Watch the AI execute tickets in real-time\n * - Intervene if needed (the terminal is interactive)\n * - See all output without digging through logs\n */\n\nimport { exec } from \"child_process\";\nimport { promisify } from \"util\";\nimport type { SpawnResult } from \"./watcher_types.js\";\n\nconst execAsync = promisify(exec);\n\n// ============================================================================\n// Terminal Spawning\n// ============================================================================\n\n/**\n * Spawn a new Terminal.app window running Claude Code with the given prompt.\n *\n * Uses AppleScript to:\n * 1. Open Terminal.app (or bring to front)\n * 2. Create a new window/tab\n * 3. Set the window title to identify the ticket\n * 4. Run Claude Code with the ticket content as a prompt\n *\n * @param options - Spawn configuration\n * @returns Result indicating success or failure\n */\nexport async function spawnTerminalWithClaude(options: {\n ticketSlug: string;\n ticketNumber?: number;\n ticketContent: string;\n workingDirectory: string;\n projectSlug: string;\n}): Promise<SpawnResult> {\n const { ticketSlug, ticketNumber, ticketContent, workingDirectory, projectSlug } = options;\n\n // Build display title for terminal window\n const displayTitle = ticketNumber\n ? `PPM YOLO: #${ticketNumber} ${ticketSlug}`\n : `PPM YOLO: ${ticketSlug}`;\n\n // Build the Claude Code command\n // Using --dangerously-skip-permissions for autonomous YOLO execution\n // The ticket content is passed as the prompt (-p flag)\n const claudePrompt = buildClaudePrompt(ticketSlug, ticketContent, projectSlug);\n\n // Escape for shell - we'll pass via heredoc to avoid escaping issues\n const escapedWorkDir = escapeForShell(workingDirectory);\n\n // Build the command that will run in Terminal\n // We use a heredoc to pass the prompt to avoid shell escaping issues\n const terminalCommand = buildTerminalCommand(escapedWorkDir, claudePrompt, ticketSlug, projectSlug);\n\n // Build AppleScript to spawn Terminal window\n const appleScript = buildAppleScript(terminalCommand, displayTitle);\n\n try {\n await execAsync(`osascript -e '${appleScript}'`);\n return { success: true };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n return { success: false, error: `Failed to spawn terminal: ${errorMessage}` };\n }\n}\n\n// ============================================================================\n// Prompt Building\n// ============================================================================\n\n/**\n * Build the prompt that will be passed to Claude Code.\n *\n * The prompt includes:\n * - Context about being in YOLO mode\n * - The ticket content\n * - Instructions to close the ticket when done\n */\nfunction buildClaudePrompt(ticketSlug: string, ticketContent: string, projectSlug: string): string {\n return `You are executing a ticket in YOLO mode (autonomous execution).\n\n## Ticket: ${ticketSlug}\n\n${ticketContent}\n\n---\n\n## Instructions\n\n1. Execute all tasks described in the ticket above\n2. When ALL tasks are complete, close the ticket using: \\`tickets_close\\` tool with ticketSlug: \"${ticketSlug}\"\n3. If you encounter errors that prevent completion, update the ticket with your findings using: \\`tickets_update\\` tool\n\nProject: ${projectSlug}\nMode: YOLO (autonomous - you have full permissions)`;\n}\n\n// ============================================================================\n// Shell/AppleScript Utilities\n// ============================================================================\n\n/**\n * Build the command that runs inside Terminal.\n *\n * Uses a heredoc to pass the prompt, avoiding complex escaping issues.\n * The command:\n * 1. Changes to the working directory\n * 2. Runs Claude Code with the prompt\n * 3. Keeps the terminal open after completion for review\n */\nfunction buildTerminalCommand(\n workDir: string,\n prompt: string,\n ticketSlug: string,\n projectSlug: string\n): string {\n // Escape the prompt for use in a shell heredoc\n // We need to escape backslashes and ensure no unescaped single quotes\n const escapedPrompt = prompt\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/'/g, \"'\\\\''\");\n\n // Build a shell script that:\n // 1. CDs to the working directory\n // 2. Writes the prompt to a temp file (avoids argument length limits)\n // 3. Runs Claude with the prompt file\n // 4. Cleans up the temp file\n // 5. Shows completion message\n return `cd ${workDir} && \\\\\nPROMPT_FILE=$(mktemp) && \\\\\ncat > \"$PROMPT_FILE\" << 'YOLO_PROMPT_EOF'\n${escapedPrompt}\nYOLO_PROMPT_EOF\nclaude --dangerously-skip-permissions -p \"$(cat $PROMPT_FILE)\" && \\\\\nrm -f \"$PROMPT_FILE\" && \\\\\necho \"\" && \\\\\necho \"════════════════════════════════════════════════════════════\" && \\\\\necho \" YOLO execution complete for: ${ticketSlug}\" && \\\\\necho \" Project: ${projectSlug}\" && \\\\\necho \"════════════════════════════════════════════════════════════\" && \\\\\necho \"\" && \\\\\necho \"Press any key to close this terminal...\" && \\\\\nread -n 1`;\n}\n\n/**\n * Build AppleScript to spawn a new Terminal window.\n *\n * The script:\n * 1. Activates Terminal.app\n * 2. Creates a new window (do script without \"in\" creates new window)\n * 3. Sets the window title\n */\nfunction buildAppleScript(command: string, title: string): string {\n // Escape for AppleScript string\n const escapedCommand = command\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\n');\n\n const escapedTitle = title\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"');\n\n return `\ntell application \"Terminal\"\n activate\n do script \"${escapedCommand}\"\n set custom title of front window to \"${escapedTitle}\"\nend tell\n `.trim();\n}\n\n/**\n * Escape a string for use in shell commands\n */\nfunction escapeForShell(str: string): string {\n // Wrap in single quotes and escape any single quotes in the string\n return `'${str.replace(/'/g, \"'\\\\''\")}'`;\n}\n\n// ============================================================================\n// Notifications\n// ============================================================================\n\n/**\n * Show a macOS notification\n *\n * Uses osascript to display a notification banner.\n *\n * @param title - Notification title\n * @param message - Notification body text\n * @param subtitle - Optional subtitle\n */\nexport async function showNotification(\n title: string,\n message: string,\n subtitle?: string\n): Promise<void> {\n const subtitlePart = subtitle ? ` subtitle \"${escapeAppleScriptString(subtitle)}\"` : '';\n\n const script = `display notification \"${escapeAppleScriptString(message)}\" with title \"${escapeAppleScriptString(title)}\"${subtitlePart}`;\n\n try {\n await execAsync(`osascript -e '${script}'`);\n } catch {\n // Notifications are best-effort - don't fail if they don't work\n }\n}\n\n/**\n * Escape a string for use in AppleScript\n */\nfunction escapeAppleScriptString(str: string): string {\n return str\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"');\n}\n","/**\n * Watcher Polling Logic\n *\n * Handles the core polling loop for the YOLO watcher:\n * 1. Query Convex for open tickets with yolo flag\n * 2. Claim the ticket (move to working status)\n * 3. Spawn a Terminal with Claude Code to execute\n * 4. Track execution state\n */\n\nimport type { ConvexHttpClient } from \"convex/browser\";\nimport type {\n WatcherConfig,\n ExecutingTicket,\n} from \"./watcher_types.js\";\nimport { spawnTerminalWithClaude, showNotification } from \"./watcher_spawn.js\";\nimport { updateStatus, readStatus } from \"./watcher_state.js\";\n\n// ============================================================================\n// Types for Convex API\n// ============================================================================\n\n/**\n * Helper type for Convex HTTP client with string-based function references.\n * Copied from server.ts to maintain consistency with how we call Convex\n * functions using string-based names (e.g., \"mcp_tickets:workMcpTicket\").\n */\ntype ConvexClientWithStringRefs = ConvexHttpClient & {\n mutation: <T>(name: string, args: Record<string, unknown>) => Promise<T>;\n query: <T>(name: string, args: Record<string, unknown>) => Promise<T>;\n};\n\n/**\n * Response from listYoloTickets query\n */\ninterface YoloTicketItem {\n slug: string;\n ticketNumber?: number;\n content: string;\n flattenedContent: string;\n}\n\n/**\n * Response from workMcpTicket mutation\n */\ninterface WorkTicketResult {\n slug: string;\n ticketNumber?: number;\n status: string;\n content: string;\n startedAt?: number;\n remainingTickets: number;\n wasOpened: boolean;\n}\n\n// ============================================================================\n// Polling Implementation\n// ============================================================================\n\n/**\n * Execute a single poll cycle.\n *\n * This function:\n * 1. Checks if we have capacity for more tickets\n * 2. Queries for open tickets with yolo=true\n * 3. Claims a ticket via workMcpTicket mutation\n * 4. Spawns a Terminal with Claude Code\n * 5. Updates the status file\n *\n * @param config - Watcher configuration\n * @param convexClient - Convex HTTP client\n * @returns Updated list of currently executing tickets\n */\nexport async function pollAndExecute(\n config: WatcherConfig,\n convexClientRaw: ConvexHttpClient\n): Promise<ExecutingTicket[]> {\n // Cast to typed client for string-based function references\n const convexClient = convexClientRaw as ConvexClientWithStringRefs;\n\n const currentStatus = readStatus(config.workingDirectory);\n const currentlyExecuting = currentStatus?.currentlyExecuting ?? [];\n\n // Check capacity\n if (currentlyExecuting.length >= config.maxParallel) {\n log(`At max parallel (${config.maxParallel}), skipping poll`);\n return currentlyExecuting;\n }\n\n // Query for YOLO tickets (open + yolo flag)\n log(\"Polling for YOLO tickets...\");\n\n let yoloTickets: YoloTicketItem[];\n try {\n yoloTickets = await convexClient.query<YoloTicketItem[]>(\n \"mcp_tickets:listYoloTickets\",\n {\n projectToken: config.projectToken,\n projectSlug: config.projectSlug,\n }\n );\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Unknown error\";\n log(`Error querying YOLO tickets: ${message}`);\n return currentlyExecuting;\n }\n\n if (yoloTickets.length === 0) {\n log(\"No YOLO tickets found\");\n return currentlyExecuting;\n }\n\n log(`Found ${yoloTickets.length} YOLO ticket(s)`);\n\n // Get the first ticket to execute\n const ticketToExecute = yoloTickets[0];\n\n // Claim the ticket (moves to working status)\n log(`Claiming ticket: ${ticketToExecute.slug}`);\n\n let workResult: WorkTicketResult | null;\n try {\n workResult = await convexClient.mutation<WorkTicketResult | null>(\n \"mcp_tickets:workMcpTicket\",\n {\n projectToken: config.projectToken,\n projectSlug: config.projectSlug,\n ticketSlug: ticketToExecute.slug,\n }\n );\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Unknown error\";\n log(`Error claiming ticket: ${message}`);\n return currentlyExecuting;\n }\n\n if (!workResult) {\n log(`Ticket ${ticketToExecute.slug} was already claimed or doesn't exist`);\n return currentlyExecuting;\n }\n\n log(`Ticket claimed: ${workResult.slug} (status: ${workResult.status})`);\n\n // Show notification if enabled\n if (config.enableNotifications) {\n const displayName = workResult.ticketNumber\n ? `#${workResult.ticketNumber} ${workResult.slug}`\n : workResult.slug;\n\n await showNotification(\n \"PPM YOLO\",\n `Executing ticket: ${displayName}`,\n config.projectSlug\n );\n }\n\n // Spawn Terminal with Claude Code\n log(`Spawning terminal for: ${workResult.slug}`);\n\n const spawnResult = await spawnTerminalWithClaude({\n ticketSlug: workResult.slug,\n ticketNumber: workResult.ticketNumber,\n ticketContent: workResult.content,\n workingDirectory: config.workingDirectory,\n projectSlug: config.projectSlug,\n });\n\n if (!spawnResult.success) {\n log(`Failed to spawn terminal: ${spawnResult.error}`);\n // Note: Ticket is now in \"working\" status but terminal didn't spawn\n // This is logged for manual intervention\n return currentlyExecuting;\n }\n\n log(`Terminal spawned successfully for: ${workResult.slug}`);\n\n // Add to currently executing list\n const newExecuting: ExecutingTicket = {\n ticketSlug: workResult.slug,\n ticketNumber: workResult.ticketNumber,\n startedAt: new Date().toISOString(),\n };\n\n const updatedExecuting = [...currentlyExecuting, newExecuting];\n\n // Update status\n const status = readStatus(config.workingDirectory);\n if (status) {\n updateStatus(config.workingDirectory, {\n ...status,\n lastPollAt: new Date().toISOString(),\n ticketsProcessed: (status.ticketsProcessed ?? 0) + 1,\n currentlyExecuting: updatedExecuting,\n });\n }\n\n return updatedExecuting;\n}\n\n/**\n * Remove a ticket from the currently executing list.\n *\n * Called when a ticket execution completes (either success or timeout).\n *\n * @param config - Watcher configuration\n * @param ticketSlug - Slug of the ticket to remove\n */\nexport function removeFromExecuting(\n config: WatcherConfig,\n ticketSlug: string\n): void {\n const status = readStatus(config.workingDirectory);\n if (!status) return;\n\n const updatedExecuting = (status.currentlyExecuting ?? []).filter(\n (t) => t.ticketSlug !== ticketSlug\n );\n\n updateStatus(config.workingDirectory, {\n ...status,\n currentlyExecuting: updatedExecuting,\n });\n}\n\n/**\n * Check for timed-out ticket executions.\n *\n * Any ticket that has been executing longer than ticketTimeout\n * is removed from the executing list (but stays in \"working\" status).\n *\n * @param config - Watcher configuration\n * @returns List of timed-out ticket slugs\n */\nexport function checkTimeouts(config: WatcherConfig): string[] {\n const status = readStatus(config.workingDirectory);\n if (!status || !status.currentlyExecuting) return [];\n\n const now = Date.now();\n const timedOut: string[] = [];\n\n for (const executing of status.currentlyExecuting) {\n const startedAt = new Date(executing.startedAt).getTime();\n const elapsed = now - startedAt;\n\n if (elapsed > config.ticketTimeout) {\n timedOut.push(executing.ticketSlug);\n log(`Ticket ${executing.ticketSlug} timed out after ${Math.round(elapsed / 1000 / 60)} minutes`);\n }\n }\n\n // Remove timed-out tickets from executing list\n if (timedOut.length > 0) {\n const updatedExecuting = (status.currentlyExecuting ?? []).filter(\n (t) => !timedOut.includes(t.ticketSlug)\n );\n\n updateStatus(config.workingDirectory, {\n ...status,\n currentlyExecuting: updatedExecuting,\n });\n }\n\n return timedOut;\n}\n\n// ============================================================================\n// Logging\n// ============================================================================\n\n/**\n * Log a message with timestamp prefix.\n * Output goes to stderr since daemon redirects stdout.\n */\nfunction log(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [watcher] ${message}`);\n}\n"],"mappings":"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","names":["log"]}
|
|
1
|
+
{"version":3,"sources":["../../src/watcher/watcher_daemon.ts","../../src/watcher/watcher_spawn.ts","../../src/watcher/watcher_poll.ts"],"sourcesContent":["/**\n * Watcher Daemon Entry Point\n *\n * Standalone Node.js process that runs the YOLO ticket watcher.\n * Spawned as a detached child process by the controller.\n *\n * Usage: node watcher_daemon.js --config <base64-encoded-config>\n *\n * The daemon:\n * 1. Parses config from CLI argument\n * 2. Initializes Convex HTTP client\n * 3. Writes PID and initial status\n * 4. Starts polling loop\n * 5. Handles graceful shutdown on SIGTERM/SIGINT\n */\n\nimport { ConvexHttpClient } from \"convex/browser\";\nimport type { WatcherConfig, WatcherStatus } from \"./watcher_types.js\";\nimport { writePid, writeStatus, removePid, updateStatus, readStatus } from \"./watcher_state.js\";\nimport { pollAndExecute, checkTimeouts } from \"./watcher_poll.js\";\n\n// ============================================================================\n// Config Parsing\n// ============================================================================\n\n/**\n * Parse configuration from CLI arguments.\n *\n * Expects: --config <base64-encoded-json>\n */\nfunction parseConfigFromArgs(): WatcherConfig | null {\n const args = process.argv.slice(2);\n const configIndex = args.indexOf(\"--config\");\n\n if (configIndex === -1 || !args[configIndex + 1]) {\n console.error(\"Usage: watcher_daemon --config <base64-encoded-config>\");\n return null;\n }\n\n try {\n const base64Config = args[configIndex + 1];\n const jsonConfig = Buffer.from(base64Config, \"base64\").toString(\"utf-8\");\n return JSON.parse(jsonConfig) as WatcherConfig;\n } catch (error) {\n console.error(\"Failed to parse config:\", error);\n return null;\n }\n}\n\n// ============================================================================\n// Main Daemon Logic\n// ============================================================================\n\n/**\n * Main entry point for the daemon.\n */\nasync function main(): Promise<void> {\n log(\"Watcher daemon starting...\");\n\n // Parse config\n const config = parseConfigFromArgs();\n if (!config) {\n process.exit(1);\n }\n\n log(`Project: ${config.projectSlug}`);\n log(`Working directory: ${config.workingDirectory}`);\n log(`Poll interval: ${config.pollIntervalMs}ms`);\n log(`Max parallel: ${config.maxParallel}`);\n\n // Initialize Convex client\n const convexClient = new ConvexHttpClient(config.convexUrl);\n\n // Write PID file\n writePid(config.workingDirectory, process.pid);\n log(`PID file written: ${process.pid}`);\n\n // Write initial status\n const initialStatus: WatcherStatus = {\n state: \"running\",\n pid: process.pid,\n projectSlug: config.projectSlug,\n startedAt: new Date().toISOString(),\n ticketsProcessed: 0,\n currentlyExecuting: [],\n config: {\n projectSlug: config.projectSlug,\n convexUrl: config.convexUrl,\n pollIntervalMs: config.pollIntervalMs,\n maxParallel: config.maxParallel,\n ticketTimeout: config.ticketTimeout,\n enableNotifications: config.enableNotifications,\n workingDirectory: config.workingDirectory,\n },\n };\n writeStatus(config.workingDirectory, initialStatus);\n log(\"Status file written\");\n\n // Setup graceful shutdown\n let isShuttingDown = false;\n let pollInterval: ReturnType<typeof setInterval> | null = null;\n\n const shutdown = async (signal: string): Promise<void> => {\n if (isShuttingDown) return;\n isShuttingDown = true;\n\n log(`Received ${signal}, shutting down gracefully...`);\n\n // Stop polling\n if (pollInterval) {\n clearInterval(pollInterval);\n pollInterval = null;\n }\n\n // Update status to stopped\n const currentStatus = readStatus(config.workingDirectory);\n if (currentStatus) {\n updateStatus(config.workingDirectory, {\n ...currentStatus,\n state: \"stopped\",\n });\n }\n\n // Remove PID file\n removePid(config.workingDirectory);\n log(\"Cleanup complete, exiting\");\n\n process.exit(0);\n };\n\n process.on(\"SIGTERM\", () => shutdown(\"SIGTERM\"));\n process.on(\"SIGINT\", () => shutdown(\"SIGINT\"));\n process.on(\"SIGHUP\", () => shutdown(\"SIGHUP\"));\n\n // Handle uncaught errors\n process.on(\"uncaughtException\", (error) => {\n log(`Uncaught exception: ${error.message}`);\n const currentStatus = readStatus(config.workingDirectory);\n if (currentStatus) {\n updateStatus(config.workingDirectory, {\n ...currentStatus,\n state: \"error\",\n lastError: error.message,\n });\n }\n process.exit(1);\n });\n\n process.on(\"unhandledRejection\", (reason) => {\n const message = reason instanceof Error ? reason.message : String(reason);\n log(`Unhandled rejection: ${message}`);\n const currentStatus = readStatus(config.workingDirectory);\n if (currentStatus) {\n updateStatus(config.workingDirectory, {\n ...currentStatus,\n state: \"error\",\n lastError: message,\n });\n }\n process.exit(1);\n });\n\n // Start polling loop\n log(\"Starting polling loop...\");\n\n const poll = async (): Promise<void> => {\n if (isShuttingDown) return;\n\n try {\n // Check for timeouts first\n checkTimeouts(config);\n\n // Poll for new tickets\n await pollAndExecute(config, convexClient);\n\n // Update lastPollAt\n const currentStatus = readStatus(config.workingDirectory);\n if (currentStatus) {\n updateStatus(config.workingDirectory, {\n ...currentStatus,\n lastPollAt: new Date().toISOString(),\n });\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n log(`Poll error: ${message}`);\n // Don't crash on poll errors, just log and continue\n }\n };\n\n // Run initial poll immediately\n await poll();\n\n // Schedule recurring polls\n pollInterval = setInterval(poll, config.pollIntervalMs);\n\n log(\"Watcher daemon running\");\n}\n\n// ============================================================================\n// Logging\n// ============================================================================\n\n/**\n * Log a message with timestamp prefix.\n */\nfunction log(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [daemon] ${message}`);\n}\n\n// ============================================================================\n// Entry Point\n// ============================================================================\n\nmain().catch((error) => {\n console.error(\"Fatal error:\", error);\n process.exit(1);\n});\n","/**\n * Terminal Spawner\n *\n * Spawns new Terminal.app windows with Claude Code for autonomous ticket execution.\n * Uses AppleScript via osascript to create visible terminal windows.\n *\n * This allows users to:\n * - Watch the AI execute tickets in real-time\n * - Intervene if needed (the terminal is interactive)\n * - See all output without digging through logs\n */\n\nimport { exec } from \"child_process\";\nimport { promisify } from \"util\";\nimport type { SpawnResult } from \"./watcher_types.js\";\n\nconst execAsync = promisify(exec);\n\n// ============================================================================\n// Terminal Spawning\n// ============================================================================\n\n/**\n * Spawn a new Terminal.app window running Claude Code with the given prompt.\n *\n * Uses AppleScript to:\n * 1. Open Terminal.app (or bring to front)\n * 2. Create a new window/tab\n * 3. Set the window title to identify the ticket\n * 4. Run Claude Code with the ticket content as a prompt\n *\n * @param options - Spawn configuration\n * @returns Result indicating success or failure\n */\nexport async function spawnTerminalWithClaude(options: {\n ticketSlug: string;\n ticketNumber?: number;\n ticketContent: string;\n workingDirectory: string;\n projectSlug: string;\n}): Promise<SpawnResult> {\n const { ticketSlug, ticketNumber, ticketContent, workingDirectory, projectSlug } = options;\n\n // Build display title for terminal window\n const displayTitle = ticketNumber\n ? `PPM YOLO: #${ticketNumber} ${ticketSlug}`\n : `PPM YOLO: ${ticketSlug}`;\n\n // Build the Claude Code command\n // Using --dangerously-skip-permissions for autonomous YOLO execution\n // The ticket content is passed as the prompt (-p flag)\n const claudePrompt = buildClaudePrompt(ticketSlug, ticketContent, projectSlug);\n\n // Escape for shell - we'll pass via heredoc to avoid escaping issues\n const escapedWorkDir = escapeForShell(workingDirectory);\n\n // Build the command that will run in Terminal\n // We use a heredoc to pass the prompt to avoid shell escaping issues\n const terminalCommand = buildTerminalCommand(escapedWorkDir, claudePrompt, ticketSlug, projectSlug);\n\n // Build AppleScript to spawn Terminal window\n const appleScript = buildAppleScript(terminalCommand, displayTitle);\n\n try {\n await execAsync(`osascript -e '${appleScript}'`);\n return { success: true };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n return { success: false, error: `Failed to spawn terminal: ${errorMessage}` };\n }\n}\n\n// ============================================================================\n// Prompt Building\n// ============================================================================\n\n/**\n * Build the prompt that will be passed to Claude Code.\n *\n * The prompt includes:\n * - Context about being in YOLO mode\n * - The ticket content\n * - Instructions to close the ticket when done\n */\nfunction buildClaudePrompt(ticketSlug: string, ticketContent: string, projectSlug: string): string {\n return `You are executing a ticket in YOLO mode (autonomous execution).\n\n## Ticket: ${ticketSlug}\n\n${ticketContent}\n\n---\n\n## Instructions\n\n1. Execute all tasks described in the ticket above\n2. When ALL tasks are complete, close the ticket using: \\`tickets_close\\` tool with ticketSlug: \"${ticketSlug}\"\n3. If you encounter errors that prevent completion, update the ticket with your findings using: \\`tickets_update\\` tool\n\nProject: ${projectSlug}\nMode: YOLO (autonomous - you have full permissions)`;\n}\n\n// ============================================================================\n// Shell/AppleScript Utilities\n// ============================================================================\n\n/**\n * Build the command that runs inside Terminal.\n *\n * Uses a heredoc to pass the prompt, avoiding complex escaping issues.\n * The command:\n * 1. Changes to the working directory\n * 2. Runs Claude Code with the prompt\n * 3. Keeps the terminal open after completion for review\n */\nfunction buildTerminalCommand(\n workDir: string,\n prompt: string,\n ticketSlug: string,\n projectSlug: string\n): string {\n // Escape the prompt for use in a shell heredoc\n // We need to escape backslashes and ensure no unescaped single quotes\n const escapedPrompt = prompt\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/'/g, \"'\\\\''\");\n\n // Build a shell script that:\n // 1. CDs to the working directory\n // 2. Writes the prompt to a temp file (avoids argument length limits)\n // 3. Runs Claude with the prompt file\n // 4. Cleans up the temp file\n // 5. Shows completion message\n return `cd ${workDir} && \\\\\nPROMPT_FILE=$(mktemp) && \\\\\ncat > \"$PROMPT_FILE\" << 'YOLO_PROMPT_EOF'\n${escapedPrompt}\nYOLO_PROMPT_EOF\nclaude --dangerously-skip-permissions -p \"$(cat $PROMPT_FILE)\" && \\\\\nrm -f \"$PROMPT_FILE\" && \\\\\necho \"\" && \\\\\necho \"════════════════════════════════════════════════════════════\" && \\\\\necho \" YOLO execution complete for: ${ticketSlug}\" && \\\\\necho \" Project: ${projectSlug}\" && \\\\\necho \"════════════════════════════════════════════════════════════\" && \\\\\necho \"\" && \\\\\necho \"Press any key to close this terminal...\" && \\\\\nread -n 1`;\n}\n\n/**\n * Build AppleScript to spawn a new Terminal window.\n *\n * The script:\n * 1. Activates Terminal.app\n * 2. Creates a new window (do script without \"in\" creates new window)\n * 3. Sets the window title\n */\nfunction buildAppleScript(command: string, title: string): string {\n // Escape for AppleScript string\n const escapedCommand = command\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\n');\n\n const escapedTitle = title\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"');\n\n return `\ntell application \"Terminal\"\n activate\n do script \"${escapedCommand}\"\n set custom title of front window to \"${escapedTitle}\"\nend tell\n `.trim();\n}\n\n/**\n * Escape a string for use in shell commands\n */\nfunction escapeForShell(str: string): string {\n // Wrap in single quotes and escape any single quotes in the string\n return `'${str.replace(/'/g, \"'\\\\''\")}'`;\n}\n\n// ============================================================================\n// Notifications\n// ============================================================================\n\n/**\n * Show a macOS notification\n *\n * Uses osascript to display a notification banner.\n *\n * @param title - Notification title\n * @param message - Notification body text\n * @param subtitle - Optional subtitle\n */\nexport async function showNotification(\n title: string,\n message: string,\n subtitle?: string\n): Promise<void> {\n const subtitlePart = subtitle ? ` subtitle \"${escapeAppleScriptString(subtitle)}\"` : '';\n\n const script = `display notification \"${escapeAppleScriptString(message)}\" with title \"${escapeAppleScriptString(title)}\"${subtitlePart}`;\n\n try {\n await execAsync(`osascript -e '${script}'`);\n } catch {\n // Notifications are best-effort - don't fail if they don't work\n }\n}\n\n/**\n * Escape a string for use in AppleScript\n */\nfunction escapeAppleScriptString(str: string): string {\n return str\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"');\n}\n","/**\n * Watcher Polling Logic\n *\n * Handles the core polling loop for the YOLO watcher:\n * 1. Query Convex for open tickets with yolo flag\n * 2. Claim the ticket (move to working status)\n * 3. Spawn a Terminal with Claude Code to execute\n * 4. Track execution state\n */\n\nimport type { ConvexHttpClient } from \"convex/browser\";\nimport type {\n WatcherConfig,\n ExecutingTicket,\n} from \"./watcher_types.js\";\nimport { spawnTerminalWithClaude, showNotification } from \"./watcher_spawn.js\";\nimport { updateStatus, readStatus } from \"./watcher_state.js\";\n\n// ============================================================================\n// Types for Convex API\n// ============================================================================\n\n/**\n * Helper type for Convex HTTP client with string-based function references.\n * Copied from server.ts to maintain consistency with how we call Convex\n * functions using string-based names (e.g., \"mcp_tickets:workMcpTicket\").\n */\ntype ConvexClientWithStringRefs = ConvexHttpClient & {\n mutation: <T>(name: string, args: Record<string, unknown>) => Promise<T>;\n query: <T>(name: string, args: Record<string, unknown>) => Promise<T>;\n};\n\n/**\n * Response from listYoloTickets query\n */\ninterface YoloTicketItem {\n slug: string;\n ticketNumber?: number;\n content: string;\n flattenedContent: string;\n}\n\n/**\n * Response from workMcpTicket mutation\n */\ninterface WorkTicketResult {\n slug: string;\n ticketNumber?: number;\n status: string;\n content: string;\n startedAt?: number;\n remainingTickets: number;\n wasOpened: boolean;\n}\n\n// ============================================================================\n// Polling Implementation\n// ============================================================================\n\n/**\n * Execute a single poll cycle.\n *\n * This function:\n * 1. Checks if we have capacity for more tickets\n * 2. Queries for open tickets with yolo=true\n * 3. Claims a ticket via workMcpTicket mutation\n * 4. Spawns a Terminal with Claude Code\n * 5. Updates the status file\n *\n * @param config - Watcher configuration\n * @param convexClient - Convex HTTP client\n * @returns Updated list of currently executing tickets\n */\nexport async function pollAndExecute(\n config: WatcherConfig,\n convexClientRaw: ConvexHttpClient\n): Promise<ExecutingTicket[]> {\n // Cast to typed client for string-based function references\n const convexClient = convexClientRaw as ConvexClientWithStringRefs;\n\n const currentStatus = readStatus(config.workingDirectory);\n const currentlyExecuting = currentStatus?.currentlyExecuting ?? [];\n\n // Check capacity\n if (currentlyExecuting.length >= config.maxParallel) {\n log(`At max parallel (${config.maxParallel}), skipping poll`);\n return currentlyExecuting;\n }\n\n // Query for YOLO tickets (open + yolo flag)\n log(\"Polling for YOLO tickets...\");\n\n let yoloTickets: YoloTicketItem[];\n try {\n yoloTickets = await convexClient.query<YoloTicketItem[]>(\n \"mcp_tickets:listYoloTickets\",\n {\n projectToken: config.projectToken,\n projectSlug: config.projectSlug,\n }\n );\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Unknown error\";\n log(`Error querying YOLO tickets: ${message}`);\n return currentlyExecuting;\n }\n\n if (yoloTickets.length === 0) {\n log(\"No YOLO tickets found\");\n return currentlyExecuting;\n }\n\n log(`Found ${yoloTickets.length} YOLO ticket(s)`);\n\n // Get the first ticket to execute\n const ticketToExecute = yoloTickets[0];\n\n // Claim the ticket (moves to working status)\n log(`Claiming ticket: ${ticketToExecute.slug}`);\n\n let workResult: WorkTicketResult | null;\n try {\n workResult = await convexClient.mutation<WorkTicketResult | null>(\n \"mcp_tickets:workMcpTicket\",\n {\n projectToken: config.projectToken,\n projectSlug: config.projectSlug,\n ticketSlug: ticketToExecute.slug,\n }\n );\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Unknown error\";\n log(`Error claiming ticket: ${message}`);\n return currentlyExecuting;\n }\n\n if (!workResult) {\n log(`Ticket ${ticketToExecute.slug} was already claimed or doesn't exist`);\n return currentlyExecuting;\n }\n\n log(`Ticket claimed: ${workResult.slug} (status: ${workResult.status})`);\n\n // Show notification if enabled\n if (config.enableNotifications) {\n const displayName = workResult.ticketNumber\n ? `#${workResult.ticketNumber} ${workResult.slug}`\n : workResult.slug;\n\n await showNotification(\n \"PPM YOLO\",\n `Executing ticket: ${displayName}`,\n config.projectSlug\n );\n }\n\n // Spawn Terminal with Claude Code\n log(`Spawning terminal for: ${workResult.slug}`);\n\n const spawnResult = await spawnTerminalWithClaude({\n ticketSlug: workResult.slug,\n ticketNumber: workResult.ticketNumber,\n ticketContent: workResult.content,\n workingDirectory: config.workingDirectory,\n projectSlug: config.projectSlug,\n });\n\n if (!spawnResult.success) {\n log(`Failed to spawn terminal: ${spawnResult.error}`);\n // Note: Ticket is now in \"working\" status but terminal didn't spawn\n // This is logged for manual intervention\n return currentlyExecuting;\n }\n\n log(`Terminal spawned successfully for: ${workResult.slug}`);\n\n // Add to currently executing list\n const newExecuting: ExecutingTicket = {\n ticketSlug: workResult.slug,\n ticketNumber: workResult.ticketNumber,\n startedAt: new Date().toISOString(),\n };\n\n const updatedExecuting = [...currentlyExecuting, newExecuting];\n\n // Update status\n const status = readStatus(config.workingDirectory);\n if (status) {\n updateStatus(config.workingDirectory, {\n ...status,\n lastPollAt: new Date().toISOString(),\n ticketsProcessed: (status.ticketsProcessed ?? 0) + 1,\n currentlyExecuting: updatedExecuting,\n });\n }\n\n return updatedExecuting;\n}\n\n/**\n * Remove a ticket from the currently executing list.\n *\n * Called when a ticket execution completes (either success or timeout).\n *\n * @param config - Watcher configuration\n * @param ticketSlug - Slug of the ticket to remove\n */\nexport function removeFromExecuting(\n config: WatcherConfig,\n ticketSlug: string\n): void {\n const status = readStatus(config.workingDirectory);\n if (!status) return;\n\n const updatedExecuting = (status.currentlyExecuting ?? []).filter(\n (t) => t.ticketSlug !== ticketSlug\n );\n\n updateStatus(config.workingDirectory, {\n ...status,\n currentlyExecuting: updatedExecuting,\n });\n}\n\n/**\n * Check for timed-out ticket executions.\n *\n * Any ticket that has been executing longer than ticketTimeout\n * is removed from the executing list (but stays in \"working\" status).\n *\n * @param config - Watcher configuration\n * @returns List of timed-out ticket slugs\n */\nexport function checkTimeouts(config: WatcherConfig): string[] {\n const status = readStatus(config.workingDirectory);\n if (!status || !status.currentlyExecuting) return [];\n\n const now = Date.now();\n const timedOut: string[] = [];\n\n for (const executing of status.currentlyExecuting) {\n const startedAt = new Date(executing.startedAt).getTime();\n const elapsed = now - startedAt;\n\n if (elapsed > config.ticketTimeout) {\n timedOut.push(executing.ticketSlug);\n log(`Ticket ${executing.ticketSlug} timed out after ${Math.round(elapsed / 1000 / 60)} minutes`);\n }\n }\n\n // Remove timed-out tickets from executing list\n if (timedOut.length > 0) {\n const updatedExecuting = (status.currentlyExecuting ?? []).filter(\n (t) => !timedOut.includes(t.ticketSlug)\n );\n\n updateStatus(config.workingDirectory, {\n ...status,\n currentlyExecuting: updatedExecuting,\n });\n }\n\n return timedOut;\n}\n\n// ============================================================================\n// Logging\n// ============================================================================\n\n/**\n * Log a message with timestamp prefix.\n * Output goes to stderr since daemon redirects stdout.\n */\nfunction log(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [watcher] ${message}`);\n}\n"],"mappings":"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","names":["log"]}
|
package/package.json
CHANGED