@sudocode-ai/local-server 0.1.0

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.
Files changed (130) hide show
  1. package/README.md +19 -0
  2. package/dist/cli.d.ts +7 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +105 -0
  5. package/dist/cli.js.map +7 -0
  6. package/dist/execution/engine/engine.d.ts +103 -0
  7. package/dist/execution/engine/engine.d.ts.map +1 -0
  8. package/dist/execution/engine/simple-engine.d.ts +190 -0
  9. package/dist/execution/engine/simple-engine.d.ts.map +1 -0
  10. package/dist/execution/engine/types.d.ts +116 -0
  11. package/dist/execution/engine/types.d.ts.map +1 -0
  12. package/dist/execution/output/ag-ui-adapter.d.ts +176 -0
  13. package/dist/execution/output/ag-ui-adapter.d.ts.map +1 -0
  14. package/dist/execution/output/ag-ui-integration.d.ts +96 -0
  15. package/dist/execution/output/ag-ui-integration.d.ts.map +1 -0
  16. package/dist/execution/output/claude-code-output-processor.d.ts +321 -0
  17. package/dist/execution/output/claude-code-output-processor.d.ts.map +1 -0
  18. package/dist/execution/output/index.d.ts +18 -0
  19. package/dist/execution/output/index.d.ts.map +1 -0
  20. package/dist/execution/output/types.d.ts +421 -0
  21. package/dist/execution/output/types.d.ts.map +1 -0
  22. package/dist/execution/process/builders/claude.d.ts +86 -0
  23. package/dist/execution/process/builders/claude.d.ts.map +1 -0
  24. package/dist/execution/process/index.d.ts +15 -0
  25. package/dist/execution/process/index.d.ts.map +1 -0
  26. package/dist/execution/process/manager.d.ts +133 -0
  27. package/dist/execution/process/manager.d.ts.map +1 -0
  28. package/dist/execution/process/simple-manager.d.ts +102 -0
  29. package/dist/execution/process/simple-manager.d.ts.map +1 -0
  30. package/dist/execution/process/types.d.ts +105 -0
  31. package/dist/execution/process/types.d.ts.map +1 -0
  32. package/dist/execution/process/utils.d.ts +53 -0
  33. package/dist/execution/process/utils.d.ts.map +1 -0
  34. package/dist/execution/resilience/circuit-breaker.d.ts +170 -0
  35. package/dist/execution/resilience/circuit-breaker.d.ts.map +1 -0
  36. package/dist/execution/resilience/executor.d.ts +109 -0
  37. package/dist/execution/resilience/executor.d.ts.map +1 -0
  38. package/dist/execution/resilience/index.d.ts +14 -0
  39. package/dist/execution/resilience/index.d.ts.map +1 -0
  40. package/dist/execution/resilience/resilient-executor.d.ts +86 -0
  41. package/dist/execution/resilience/resilient-executor.d.ts.map +1 -0
  42. package/dist/execution/resilience/retry.d.ts +161 -0
  43. package/dist/execution/resilience/retry.d.ts.map +1 -0
  44. package/dist/execution/resilience/types.d.ts +226 -0
  45. package/dist/execution/resilience/types.d.ts.map +1 -0
  46. package/dist/execution/transport/event-buffer.d.ts +119 -0
  47. package/dist/execution/transport/event-buffer.d.ts.map +1 -0
  48. package/dist/execution/transport/index.d.ts +10 -0
  49. package/dist/execution/transport/index.d.ts.map +1 -0
  50. package/dist/execution/transport/sse-transport.d.ts +146 -0
  51. package/dist/execution/transport/sse-transport.d.ts.map +1 -0
  52. package/dist/execution/transport/transport-manager.d.ts +176 -0
  53. package/dist/execution/transport/transport-manager.d.ts.map +1 -0
  54. package/dist/execution/workflow/index.d.ts +13 -0
  55. package/dist/execution/workflow/index.d.ts.map +1 -0
  56. package/dist/execution/workflow/linear-orchestrator.d.ts +216 -0
  57. package/dist/execution/workflow/linear-orchestrator.d.ts.map +1 -0
  58. package/dist/execution/workflow/memory-storage.d.ts +54 -0
  59. package/dist/execution/workflow/memory-storage.d.ts.map +1 -0
  60. package/dist/execution/workflow/orchestrator.d.ts +158 -0
  61. package/dist/execution/workflow/orchestrator.d.ts.map +1 -0
  62. package/dist/execution/workflow/types.d.ts +172 -0
  63. package/dist/execution/workflow/types.d.ts.map +1 -0
  64. package/dist/execution/workflow/utils.d.ts +89 -0
  65. package/dist/execution/workflow/utils.d.ts.map +1 -0
  66. package/dist/execution/worktree/config.d.ts +74 -0
  67. package/dist/execution/worktree/config.d.ts.map +1 -0
  68. package/dist/execution/worktree/git-cli.d.ts +151 -0
  69. package/dist/execution/worktree/git-cli.d.ts.map +1 -0
  70. package/dist/execution/worktree/index.d.ts +16 -0
  71. package/dist/execution/worktree/index.d.ts.map +1 -0
  72. package/dist/execution/worktree/manager.d.ts +184 -0
  73. package/dist/execution/worktree/manager.d.ts.map +1 -0
  74. package/dist/execution/worktree/types.d.ts +90 -0
  75. package/dist/execution/worktree/types.d.ts.map +1 -0
  76. package/dist/index.d.ts +8 -0
  77. package/dist/index.d.ts.map +1 -0
  78. package/dist/index.js +104 -0
  79. package/dist/index.js.map +7 -0
  80. package/dist/public/assets/index-C4SmlXoo.js +568 -0
  81. package/dist/public/assets/index-C4SmlXoo.js.map +1 -0
  82. package/dist/public/assets/index-DE59j7ti.css +1 -0
  83. package/dist/public/assets/react-vendor-LX0UoTxg.js +60 -0
  84. package/dist/public/assets/react-vendor-LX0UoTxg.js.map +1 -0
  85. package/dist/public/assets/ui-vendor-_cxVHaqZ.js +54 -0
  86. package/dist/public/assets/ui-vendor-_cxVHaqZ.js.map +1 -0
  87. package/dist/public/favicon.ico +0 -0
  88. package/dist/public/index.html +16 -0
  89. package/dist/public/logo.png +0 -0
  90. package/dist/routes/executions-stream.d.ts +24 -0
  91. package/dist/routes/executions-stream.d.ts.map +1 -0
  92. package/dist/routes/executions.d.ts +19 -0
  93. package/dist/routes/executions.d.ts.map +1 -0
  94. package/dist/routes/feedback.d.ts +7 -0
  95. package/dist/routes/feedback.d.ts.map +1 -0
  96. package/dist/routes/issues.d.ts +7 -0
  97. package/dist/routes/issues.d.ts.map +1 -0
  98. package/dist/routes/relationships.d.ts +7 -0
  99. package/dist/routes/relationships.d.ts.map +1 -0
  100. package/dist/routes/specs.d.ts +7 -0
  101. package/dist/routes/specs.d.ts.map +1 -0
  102. package/dist/services/db.d.ts +33 -0
  103. package/dist/services/db.d.ts.map +1 -0
  104. package/dist/services/execution-lifecycle.d.ts +108 -0
  105. package/dist/services/execution-lifecycle.d.ts.map +1 -0
  106. package/dist/services/execution-service.d.ts +185 -0
  107. package/dist/services/execution-service.d.ts.map +1 -0
  108. package/dist/services/executions.d.ts +59 -0
  109. package/dist/services/executions.d.ts.map +1 -0
  110. package/dist/services/export.d.ts +24 -0
  111. package/dist/services/export.d.ts.map +1 -0
  112. package/dist/services/feedback.d.ts +40 -0
  113. package/dist/services/feedback.d.ts.map +1 -0
  114. package/dist/services/issues.d.ts +27 -0
  115. package/dist/services/issues.d.ts.map +1 -0
  116. package/dist/services/prompt-template-engine.d.ts +108 -0
  117. package/dist/services/prompt-template-engine.d.ts.map +1 -0
  118. package/dist/services/prompt-templates.d.ts +97 -0
  119. package/dist/services/prompt-templates.d.ts.map +1 -0
  120. package/dist/services/relationships.d.ts +35 -0
  121. package/dist/services/relationships.d.ts.map +1 -0
  122. package/dist/services/specs.d.ts +27 -0
  123. package/dist/services/specs.d.ts.map +1 -0
  124. package/dist/services/watcher.d.ts +50 -0
  125. package/dist/services/watcher.d.ts.map +1 -0
  126. package/dist/services/websocket.d.ts +127 -0
  127. package/dist/services/websocket.d.ts.map +1 -0
  128. package/dist/utils/sudocode-dir.d.ts +6 -0
  129. package/dist/utils/sudocode-dir.d.ts.map +1 -0
  130. package/package.json +74 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/services/issues.ts", "../src/services/specs.ts", "../src/index.ts", "../src/services/db.ts", "../src/services/prompt-templates.ts", "../src/services/prompt-template-engine.ts", "../src/services/execution-lifecycle.ts", "../src/execution/worktree/manager.ts", "../src/execution/worktree/types.ts", "../src/execution/worktree/git-cli.ts", "../src/execution/worktree/config.ts", "../src/services/executions.ts", "../src/services/execution-service.ts", "../src/execution/process/simple-manager.ts", "../../node_modules/nanoid/index.js", "../src/execution/process/utils.ts", "../src/execution/process/builders/claude.ts", "../src/execution/engine/simple-engine.ts", "../src/execution/resilience/circuit-breaker.ts", "../src/execution/resilience/retry.ts", "../src/execution/resilience/types.ts", "../src/execution/resilience/resilient-executor.ts", "../src/execution/workflow/utils.ts", "../src/execution/workflow/linear-orchestrator.ts", "../src/execution/output/claude-code-output-processor.ts", "../src/execution/output/ag-ui-adapter.ts", "../src/execution/output/ag-ui-integration.ts", "../src/routes/issues.ts", "../src/services/websocket.ts", "../src/utils/sudocode-dir.ts", "../src/services/export.ts", "../src/routes/specs.ts", "../src/routes/relationships.ts", "../src/services/relationships.ts", "../src/routes/feedback.ts", "../src/services/feedback.ts", "../src/routes/executions.ts", "../src/routes/executions-stream.ts", "../src/execution/transport/sse-transport.ts", "../src/execution/transport/event-buffer.ts", "../src/execution/transport/transport-manager.ts", "../src/services/watcher.ts"],
4
+ "sourcesContent": ["/**\n * Issues service - wraps CLI operations for API use\n */\n\nimport type Database from \"better-sqlite3\";\nimport {\n getIssue,\n listIssues,\n createIssue,\n updateIssue,\n deleteIssue,\n type CreateIssueInput,\n type UpdateIssueInput,\n type ListIssuesOptions,\n} from \"@sudocode-ai/cli/dist/operations/index.js\";\nimport type { Issue } from \"@sudocode-ai/types\";\n\n/**\n * Get all issues with optional filtering\n */\nexport function getAllIssues(\n db: Database.Database,\n options?: ListIssuesOptions\n): Issue[] {\n return listIssues(db, options || {});\n}\n\n/**\n * Get a single issue by ID\n */\nexport function getIssueById(db: Database.Database, id: string): Issue | null {\n return getIssue(db, id);\n}\n\n/**\n * Create a new issue\n */\nexport function createNewIssue(\n db: Database.Database,\n input: CreateIssueInput\n): Issue {\n return createIssue(db, input);\n}\n\n/**\n * Update an existing issue\n */\nexport function updateExistingIssue(\n db: Database.Database,\n id: string,\n input: UpdateIssueInput\n): Issue {\n return updateIssue(db, id, input);\n}\n\n/**\n * Delete an issue\n */\nexport function deleteExistingIssue(\n db: Database.Database,\n id: string\n): boolean {\n return deleteIssue(db, id);\n}\n", "/**\n * Specs service - wraps CLI operations for API use\n */\n\nimport type Database from \"better-sqlite3\";\nimport {\n getSpec,\n listSpecs,\n createSpec,\n updateSpec,\n deleteSpec,\n type CreateSpecInput,\n type UpdateSpecInput,\n type ListSpecsOptions,\n} from \"@sudocode-ai/cli/dist/operations/index.js\";\nimport type { Spec } from \"@sudocode-ai/types\";\n\n/**\n * Get all specs with optional filtering\n */\nexport function getAllSpecs(\n db: Database.Database,\n options?: ListSpecsOptions\n): Spec[] {\n return listSpecs(db, options || {});\n}\n\n/**\n * Get a single spec by ID\n */\nexport function getSpecById(db: Database.Database, id: string): Spec | null {\n return getSpec(db, id);\n}\n\n/**\n * Create a new spec\n */\nexport function createNewSpec(\n db: Database.Database,\n input: CreateSpecInput\n): Spec {\n return createSpec(db, input);\n}\n\n/**\n * Update an existing spec\n */\nexport function updateExistingSpec(\n db: Database.Database,\n id: string,\n input: UpdateSpecInput\n): Spec {\n return updateSpec(db, id, input);\n}\n\n/**\n * Delete a spec\n */\nexport function deleteExistingSpec(db: Database.Database, id: string): boolean {\n return deleteSpec(db, id);\n}\n", "import express, { Request, Response } from \"express\";\nimport cors from \"cors\";\nimport dotenv from \"dotenv\";\nimport * as path from \"path\";\nimport * as http from \"http\";\nimport { fileURLToPath } from \"url\";\nimport { readFileSync, existsSync } from \"fs\";\nimport type Database from \"better-sqlite3\";\n\n// ES Module __dirname equivalent\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\nimport { initDatabase, getDatabaseInfo } from \"./services/db.js\";\nimport { ExecutionLifecycleService } from \"./services/execution-lifecycle.js\";\nimport { ExecutionService } from \"./services/execution-service.js\";\nimport { WorktreeManager } from \"./execution/worktree/manager.js\";\nimport { getWorktreeConfig } from \"./execution/worktree/config.js\";\nimport { createIssuesRouter } from \"./routes/issues.js\";\nimport { createSpecsRouter } from \"./routes/specs.js\";\nimport { createRelationshipsRouter } from \"./routes/relationships.js\";\nimport { createFeedbackRouter } from \"./routes/feedback.js\";\nimport { createExecutionsRouter } from \"./routes/executions.js\";\nimport { createExecutionStreamRoutes } from \"./routes/executions-stream.js\";\nimport { TransportManager } from \"./execution/transport/transport-manager.js\";\nimport { getIssueById } from \"./services/issues.js\";\nimport { getSpecById } from \"./services/specs.js\";\nimport {\n startServerWatcher,\n type ServerWatcherControl,\n} from \"./services/watcher.js\";\nimport {\n initWebSocketServer,\n getWebSocketStats,\n shutdownWebSocketServer,\n broadcastIssueUpdate,\n broadcastSpecUpdate,\n} from \"./services/websocket.js\";\n\n// Load environment variables\ndotenv.config();\n\nconst app = express();\nconst DEFAULT_PORT = 3000;\nconst MAX_PORT_ATTEMPTS = 20;\n\n// Falls back to current directory for development/testing\nconst SUDOCODE_DIR =\n process.env.SUDOCODE_DIR || path.join(process.cwd(), \".sudocode\");\nconst DB_PATH = path.join(SUDOCODE_DIR, \"cache.db\");\n// TODO: Include sudocode install package for serving static files.\n\n// Derive repo root from SUDOCODE_DIR (which is <repo>/.sudocode)\n// This ensures consistency across database and execution paths\nconst REPO_ROOT = path.dirname(SUDOCODE_DIR);\n\n// Initialize database and transport manager\nlet db!: Database.Database;\nlet watcher: ServerWatcherControl | null = null;\nlet transportManager!: TransportManager;\nlet executionService: ExecutionService | null = null;\n\n// Async initialization function\nasync function initialize() {\n try {\n console.log(`Initializing database at: ${DB_PATH}`);\n db = initDatabase({ path: DB_PATH });\n const info = getDatabaseInfo(db);\n console.log(`Database initialized with ${info.tables.length} tables`);\n if (!info.hasCliTables) {\n // TODO: Automatically import and sync.\n console.warn(\n \"Warning: CLI tables not found. Run 'sudocode sync' to initialize the database.\"\n );\n }\n\n // Initialize transport manager for SSE streaming\n transportManager = new TransportManager();\n console.log(\"Transport manager initialized\");\n\n // Initialize execution service globally for cleanup on shutdown\n executionService = new ExecutionService(\n db,\n REPO_ROOT,\n undefined,\n transportManager\n );\n console.log(\"Execution service initialized\");\n\n // Cleanup orphaned worktrees on startup (if configured)\n const worktreeConfig = getWorktreeConfig(REPO_ROOT);\n if (worktreeConfig.cleanupOrphanedWorktreesOnStartup) {\n try {\n console.log(\"Cleaning up orphaned worktrees...\");\n const worktreeManager = new WorktreeManager(worktreeConfig);\n const lifecycleService = new ExecutionLifecycleService(\n db,\n REPO_ROOT,\n worktreeManager\n );\n await lifecycleService.cleanupOrphanedWorktrees();\n console.log(\"Orphaned worktree cleanup complete\");\n } catch (error) {\n console.error(\"Failed to cleanup orphaned worktrees:\", error);\n // Don't exit - this is best-effort cleanup\n }\n }\n } catch (error) {\n console.error(\"Failed to initialize database:\", error);\n process.exit(1);\n }\n}\n\n// Run initialization\nawait initialize();\n\n// Start file watcher (enabled by default, disable with WATCH=false)\nconst WATCH_ENABLED = process.env.WATCH !== \"false\";\nconst SYNC_JSONL_TO_MARKDOWN = process.env.SYNC_JSONL_TO_MARKDOWN === \"true\";\nif (WATCH_ENABLED) {\n try {\n watcher = startServerWatcher({\n db,\n baseDir: SUDOCODE_DIR,\n debounceDelay: parseInt(process.env.WATCH_DEBOUNCE || \"2000\", 10),\n syncJSONLToMarkdown: SYNC_JSONL_TO_MARKDOWN,\n onFileChange: (info) => {\n console.log(\n `[server] File change detected: ${info.entityType || \"unknown\"} ${\n info.entityId || \"\"\n }`\n );\n\n // Broadcast WebSocket updates for issue and spec changes\n if (info.entityType === \"issue\" && info.entityId) {\n if (info.entityId === \"*\") {\n // Wildcard update (JSONL file changed) - broadcast to all issue subscribers\n broadcastIssueUpdate(\"*\", \"updated\", null);\n } else {\n // Specific issue update - fetch and broadcast the specific issue\n const issue = getIssueById(db, info.entityId);\n if (issue) {\n broadcastIssueUpdate(info.entityId, \"updated\", issue);\n }\n }\n } else if (info.entityType === \"spec\" && info.entityId) {\n if (info.entityId === \"*\") {\n // Wildcard update (JSONL file changed) - broadcast to all spec subscribers\n broadcastSpecUpdate(\"*\", \"updated\", null);\n } else {\n // Specific spec update - fetch and broadcast the specific spec\n const spec = getSpecById(db, info.entityId);\n if (spec) {\n broadcastSpecUpdate(info.entityId, \"updated\", spec);\n }\n }\n }\n },\n });\n console.log(`[server] File watcher started on: ${SUDOCODE_DIR}`);\n } catch (error) {\n console.error(\"Failed to start file watcher:\", error);\n console.warn(\n \"Continuing without file watcher. Set WATCH=false to suppress this warning.\"\n );\n }\n}\n\n// Middleware\napp.use(cors());\napp.use(express.json());\n\n// API Routes\napp.use(\"/api/issues\", createIssuesRouter(db));\napp.use(\"/api/specs\", createSpecsRouter(db));\napp.use(\"/api/relationships\", createRelationshipsRouter(db));\napp.use(\"/api/feedback\", createFeedbackRouter(db));\n// Mount execution routes (must be before stream routes to avoid conflicts)\napp.use(\n \"/api\",\n createExecutionsRouter(db, REPO_ROOT, transportManager, executionService!)\n);\napp.use(\"/api/executions\", createExecutionStreamRoutes(transportManager));\n\n// Health check endpoint\napp.get(\"/health\", (_req: Request, res: Response) => {\n const dbInfo = getDatabaseInfo(db);\n res.status(200).json({\n status: \"ok\",\n timestamp: new Date().toISOString(),\n uptime: process.uptime(),\n database: {\n path: DB_PATH,\n tables: dbInfo.tables.length,\n hasCliTables: dbInfo.hasCliTables,\n },\n });\n});\n\n// Version endpoint - returns versions of all packages\napp.get(\"/api/version\", (_req: Request, res: Response) => {\n try {\n // Read package.json files - going up from server/dist to project root\n const projectRoot = path.join(__dirname, \"../..\");\n const cliPackagePath = path.join(projectRoot, \"cli/package.json\");\n const serverPackagePath = path.join(projectRoot, \"server/package.json\");\n const frontendPackagePath = path.join(projectRoot, \"frontend/package.json\");\n\n const cliPackage = JSON.parse(readFileSync(cliPackagePath, \"utf-8\"));\n const serverPackage = JSON.parse(readFileSync(serverPackagePath, \"utf-8\"));\n const frontendPackage = JSON.parse(\n readFileSync(frontendPackagePath, \"utf-8\")\n );\n\n res.status(200).json({\n cli: cliPackage.version,\n server: serverPackage.version,\n frontend: frontendPackage.version,\n });\n } catch (error) {\n console.error(\"Failed to read version information:\", error);\n res.status(500).json({ error: \"Failed to read version information\" });\n }\n});\n\n// Config endpoint - returns sudocode configuration\napp.get(\"/api/config\", (_req: Request, res: Response) => {\n try {\n const configPath = path.join(SUDOCODE_DIR, \"config.json\");\n const config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n res.status(200).json(config);\n } catch (error) {\n console.error(\"Failed to read config:\", error);\n res.status(500).json({ error: \"Failed to read config\" });\n }\n});\n\n// WebSocket stats endpoint\napp.get(\"/ws/stats\", (_req: Request, res: Response) => {\n const stats = getWebSocketStats();\n res.status(200).json(stats);\n});\n\n// Serve static frontend\n// In development: ../../../frontend/dist (workspace)\n// In production: ./public (bundled with server package in dist/public)\nconst isDev =\n process.env.NODE_ENV !== \"production\" &&\n existsSync(path.join(__dirname, \"../../../frontend/dist\"));\nconst frontendPath = isDev\n ? path.join(__dirname, \"../../../frontend/dist\")\n : path.join(__dirname, \"public\");\nconsole.log(`[server] Serving static frontend from: ${frontendPath}`);\n\n// Serve static files\napp.use(express.static(frontendPath));\n\n// SPA fallback - serve index.html for all non-API/non-WS routes\napp.get(\"*\", (req: Request, res: Response) => {\n // Skip API and WebSocket routes\n if (\n req.path.startsWith(\"/api\") ||\n req.path.startsWith(\"/ws\") ||\n req.path.startsWith(\"/health\")\n ) {\n res.status(404).json({ error: \"Not found\" });\n } else {\n res.sendFile(path.join(frontendPath, \"index.html\"));\n }\n});\n\n// Create HTTP server\nconst server = http.createServer(app);\n\n/**\n * Attempts to start the server on the given port, incrementing if unavailable.\n * Only scans for ports if no explicit PORT was provided.\n */\nasync function startServer(\n initialPort: number,\n maxAttempts: number\n): Promise<number> {\n const explicitPort = process.env.PORT;\n const shouldScan = !explicitPort;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n const port = initialPort + attempt;\n\n try {\n await new Promise<void>((resolve, reject) => {\n const errorHandler = (err: NodeJS.ErrnoException) => {\n server.removeListener(\"error\", errorHandler);\n server.removeListener(\"listening\", listeningHandler);\n reject(err);\n };\n\n const listeningHandler = () => {\n server.removeListener(\"error\", errorHandler);\n resolve();\n };\n\n server.once(\"error\", errorHandler);\n server.once(\"listening\", listeningHandler);\n server.listen(port);\n });\n\n // Success! Return the port we successfully bound to\n return port;\n } catch (err) {\n const error = err as NodeJS.ErrnoException;\n\n if (error.code === \"EADDRINUSE\") {\n if (!shouldScan) {\n // Explicit port was specified and it's in use - fail immediately\n throw new Error(\n `Port ${port} is already in use. Please specify a different PORT.`\n );\n }\n\n // Port is in use, try next one if we have attempts left\n if (attempt < maxAttempts - 1) {\n console.log(`Port ${port} is already in use, trying ${port + 1}...`);\n continue;\n } else {\n throw new Error(\n `Could not find an available port after ${maxAttempts} attempts (${initialPort}-${port})`\n );\n }\n } else {\n // Some other error - fail immediately\n throw error;\n }\n }\n }\n\n throw new Error(`Could not start server after ${maxAttempts} attempts`);\n}\n\n// Start listening with port scanning\nconst startPort = process.env.PORT\n ? parseInt(process.env.PORT, 10)\n : DEFAULT_PORT;\nconst actualPort = await startServer(startPort, MAX_PORT_ATTEMPTS);\n\n// Initialize WebSocket server AFTER successfully binding to a port\ninitWebSocketServer(server, \"/ws\");\n\n// Format URLs as clickable links with color\nconst httpUrl = `http://localhost:${actualPort}`;\nconst wsUrl = `ws://localhost:${actualPort}/ws`;\n\n// ANSI escape codes for green color and clickable links\nconst green = \"\\u001b[32m\";\nconst bold = \"\\u001b[1m\";\nconst reset = \"\\u001b[0m\";\nconst makeClickable = (url: string, text: string) =>\n `\\u001b]8;;${url}\\u001b\\\\${text}\\u001b]8;;\\u001b\\\\`;\n\nconsole.log(`WebSocket server available at: ${makeClickable(wsUrl, wsUrl)}`);\nconsole.log(\n `${bold}${green}sudocode local server running on: ${makeClickable(httpUrl, httpUrl)}${reset}`\n);\n\n// Error handlers for debugging\nprocess.on(\"uncaughtException\", (error) => {\n console.error(\"Uncaught exception:\", error);\n console.error(\"Stack trace:\", error.stack);\n});\n\nprocess.on(\"unhandledRejection\", (reason, promise) => {\n console.error(\"Unhandled rejection at:\", promise);\n console.error(\"Reason:\", reason);\n});\n\n// Graceful shutdown\nprocess.on(\"SIGINT\", async () => {\n console.log(\"\\nShutting down server...\");\n\n // Shutdown execution service (cancel active executions)\n if (executionService) {\n await executionService.shutdown();\n }\n\n // Stop file watcher\n if (watcher) {\n await watcher.stop();\n }\n\n // Shutdown WebSocket server\n await shutdownWebSocketServer();\n\n // Shutdown transport manager\n if (transportManager) {\n transportManager.shutdown();\n console.log(\"Transport manager shutdown complete\");\n }\n\n // Close database\n db.close();\n\n // Close HTTP server\n server.close(() => {\n console.log(\"Server closed\");\n process.exit(0);\n });\n\n // Force exit after 10 seconds if graceful shutdown hangs\n setTimeout(() => {\n console.error(\"Shutdown timeout - forcing exit\");\n process.exit(1);\n }, 10000);\n});\n\nprocess.on(\"SIGTERM\", async () => {\n console.log(\"\\nShutting down server...\");\n\n // Shutdown execution service (cancel active executions)\n if (executionService) {\n await executionService.shutdown();\n }\n\n // Stop file watcher\n if (watcher) {\n await watcher.stop();\n }\n\n // Shutdown WebSocket server\n await shutdownWebSocketServer();\n\n // Shutdown transport manager\n if (transportManager) {\n transportManager.shutdown();\n console.log(\"Transport manager shutdown complete\");\n }\n\n // Close database\n db.close();\n\n // Close HTTP server\n server.close(() => {\n console.log(\"Server closed\");\n process.exit(0);\n });\n});\n\nexport default app;\nexport { db, transportManager };\n", "/**\n * Database service for sudocode server\n * Uses shared schema from @sudocode-ai/types\n */\n\nimport Database from \"better-sqlite3\";\nimport * as path from \"path\";\nimport * as fs from \"fs\";\nimport {\n EXECUTIONS_TABLE,\n EXECUTIONS_INDEXES,\n PROMPT_TEMPLATES_TABLE,\n PROMPT_TEMPLATES_INDEXES,\n EXECUTION_LOGS_TABLE,\n EXECUTION_LOGS_INDEXES,\n} from \"@sudocode-ai/types/schema\";\nimport { initializeDefaultTemplates } from \"./prompt-templates.js\";\n\n/**\n * Database configuration\n */\nexport interface DatabaseConfig {\n path: string;\n readOnly?: boolean;\n}\n\n/**\n * Initialize database with CLI schema + server extensions\n */\nexport function initDatabase(config: DatabaseConfig): Database.Database {\n const { path: dbPath, readOnly = false } = config;\n\n // Ensure directory exists\n const dir = path.dirname(dbPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n // Open database\n const db = new Database(dbPath, {\n readonly: readOnly,\n fileMustExist: false,\n });\n\n // Don't modify schema if read-only\n if (readOnly) {\n return db;\n }\n\n // Configure database\n db.pragma(\"journal_mode = WAL\");\n db.pragma(\"foreign_keys = ON\");\n db.pragma(\"synchronous = NORMAL\");\n db.pragma(\"temp_store = MEMORY\");\n\n // Create server-specific tables\n db.exec(EXECUTIONS_TABLE);\n db.exec(PROMPT_TEMPLATES_TABLE);\n db.exec(EXECUTION_LOGS_TABLE);\n\n // Create indexes\n db.exec(EXECUTIONS_INDEXES);\n db.exec(PROMPT_TEMPLATES_INDEXES);\n db.exec(EXECUTION_LOGS_INDEXES);\n\n // Initialize default prompt templates\n initializeDefaultTemplates(db);\n\n return db;\n}\n\n/**\n * Check if database has CLI tables\n */\nexport function hasCliTables(db: Database.Database): boolean {\n const result = db\n .prepare(\n `\n SELECT COUNT(*) as count\n FROM sqlite_master\n WHERE type='table'\n AND name IN ('specs', 'issues', 'relationships', 'tags')\n `\n )\n .get() as { count: number };\n\n return result.count === 4;\n}\n\n/**\n * Get database info\n */\nexport function getDatabaseInfo(db: Database.Database) {\n const tables = db\n .prepare(\n `\n SELECT name\n FROM sqlite_master\n WHERE type='table'\n ORDER BY name\n `\n )\n .all() as { name: string }[];\n\n const version = db.prepare(\"PRAGMA user_version\").get() as {\n user_version: number;\n };\n\n return {\n tables: tables.map((t) => t.name),\n version: version.user_version,\n hasCliTables: hasCliTables(db),\n };\n}\n\n/**\n * Close database connection\n */\nexport function closeDatabase(db: Database.Database): void {\n db.close();\n}\n", "/**\n * Prompt Templates Service\n *\n * Manages prompt templates in the database, including:\n * - Seeding default templates\n * - Loading templates by ID or type\n * - Validating template syntax\n *\n * @module services/prompt-templates\n */\n\nimport type Database from 'better-sqlite3';\nimport { randomUUID } from 'crypto';\nimport { PromptTemplateEngine } from './prompt-template-engine.js';\n\n/**\n * Prompt template record from database\n */\nexport interface PromptTemplate {\n id: string;\n name: string;\n description: string | null;\n type: 'issue' | 'spec' | 'custom';\n template: string;\n variables: string; // JSON array of variable names\n is_default: number; // 0 or 1\n created_at: number;\n updated_at: number;\n}\n\n/**\n * Default issue template\n *\n * Renders an issue into an executable prompt with:\n * - Issue title and description\n * - Related specifications\n * - Feedback from previous execution attempts\n */\nconst DEFAULT_ISSUE_TEMPLATE = `Fix issue {{issueId}}: {{title}}\n\n## Description\n{{description}}\n\n{{#if relatedSpecs}}\n## Related Specifications\n{{#each relatedSpecs}}\n- [[{{id}}]]: {{title}}\n{{/each}}\n{{/if}}\n\n{{#if feedback}}\n## Feedback from Previous Attempts\n{{#each feedback}}\n- {{content}} (from {{issueId}})\n{{/each}}\n{{/if}}\n\nPlease implement a solution for this issue. Make sure to:\n1. Read and understand the issue requirements\n2. Check related specifications for context\n3. Write clean, well-tested code\n4. Update documentation if needed\n`;\n\n/**\n * Initialize default prompt templates in the database\n *\n * This function is idempotent - it will only insert templates if they don't exist.\n * Should be called during database initialization.\n *\n * @param db - Database instance\n */\nexport function initializeDefaultTemplates(db: Database.Database): void {\n const engine = new PromptTemplateEngine();\n\n // Validate default template\n const validation = engine.validate(DEFAULT_ISSUE_TEMPLATE);\n if (!validation.valid) {\n throw new Error(\n `Default issue template has invalid syntax: ${validation.errors.join(', ')}`\n );\n }\n\n // Check if default issue template already exists\n const existing = db\n .prepare(\n `\n SELECT id FROM prompt_templates\n WHERE type = 'issue' AND is_default = 1\n `\n )\n .get() as { id: string } | undefined;\n\n if (existing) {\n // Default template already exists, skip initialization\n return;\n }\n\n // Insert default issue template\n const templateId = randomUUID();\n const variables = JSON.stringify([\n 'issueId',\n 'title',\n 'description',\n 'relatedSpecs',\n 'feedback',\n ]);\n\n db.prepare(\n `\n INSERT INTO prompt_templates (\n id, name, description, type, template, variables, is_default,\n created_at, updated_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n `\n ).run(\n templateId,\n 'Default Issue Template',\n 'Renders an issue into an executable prompt with related specs and feedback',\n 'issue',\n DEFAULT_ISSUE_TEMPLATE,\n variables,\n 1, // is_default\n new Date().toISOString(),\n new Date().toISOString()\n );\n}\n\n/**\n * Get default template for a specific type\n *\n * @param db - Database instance\n * @param type - Template type (issue, spec, custom)\n * @returns Default template or null if not found\n */\nexport function getDefaultTemplate(\n db: Database.Database,\n type: 'issue' | 'spec' | 'custom'\n): PromptTemplate | null {\n const template = db\n .prepare(\n `\n SELECT * FROM prompt_templates\n WHERE type = ? AND is_default = 1\n LIMIT 1\n `\n )\n .get(type) as PromptTemplate | undefined;\n\n return template || null;\n}\n\n/**\n * Get template by ID\n *\n * @param db - Database instance\n * @param templateId - Template ID\n * @returns Template or null if not found\n */\nexport function getTemplateById(\n db: Database.Database,\n templateId: string\n): PromptTemplate | null {\n const template = db\n .prepare('SELECT * FROM prompt_templates WHERE id = ?')\n .get(templateId) as PromptTemplate | undefined;\n\n return template || null;\n}\n\n/**\n * List all templates, optionally filtered by type\n *\n * @param db - Database instance\n * @param type - Optional type filter\n * @returns Array of templates\n */\nexport function listTemplates(\n db: Database.Database,\n type?: 'issue' | 'spec' | 'custom'\n): PromptTemplate[] {\n if (type) {\n return db\n .prepare(\n `\n SELECT * FROM prompt_templates\n WHERE type = ?\n ORDER BY is_default DESC, name ASC\n `\n )\n .all(type) as PromptTemplate[];\n }\n\n return db\n .prepare(\n `\n SELECT * FROM prompt_templates\n ORDER BY is_default DESC, name ASC\n `\n )\n .all() as PromptTemplate[];\n}\n\n/**\n * Create a new custom template\n *\n * @param db - Database instance\n * @param params - Template parameters\n * @returns Created template\n */\nexport function createTemplate(\n db: Database.Database,\n params: {\n name: string;\n description?: string;\n type: 'issue' | 'spec' | 'custom';\n template: string;\n variables: string[];\n isDefault?: boolean;\n }\n): PromptTemplate {\n // Validate template syntax\n const engine = new PromptTemplateEngine();\n const validation = engine.validate(params.template);\n if (!validation.valid) {\n throw new Error(`Invalid template syntax: ${validation.errors.join(', ')}`);\n }\n\n const templateId = randomUUID();\n const now = new Date().toISOString();\n\n db.prepare(\n `\n INSERT INTO prompt_templates (\n id, name, description, type, template, variables, is_default,\n created_at, updated_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n `\n ).run(\n templateId,\n params.name,\n params.description || null,\n params.type,\n params.template,\n JSON.stringify(params.variables),\n params.isDefault ? 1 : 0,\n now,\n now\n );\n\n return getTemplateById(db, templateId)!;\n}\n\n/**\n * Update an existing template\n *\n * @param db - Database instance\n * @param templateId - Template ID\n * @param updates - Fields to update\n * @returns Updated template or null if not found\n */\nexport function updateTemplate(\n db: Database.Database,\n templateId: string,\n updates: {\n name?: string;\n description?: string;\n template?: string;\n variables?: string[];\n isDefault?: boolean;\n }\n): PromptTemplate | null {\n const existing = getTemplateById(db, templateId);\n if (!existing) {\n return null;\n }\n\n // Validate template syntax if template is being updated\n if (updates.template) {\n const engine = new PromptTemplateEngine();\n const validation = engine.validate(updates.template);\n if (!validation.valid) {\n throw new Error(\n `Invalid template syntax: ${validation.errors.join(', ')}`\n );\n }\n }\n\n const fields: string[] = [];\n const values: any[] = [];\n\n if (updates.name !== undefined) {\n fields.push('name = ?');\n values.push(updates.name);\n }\n if (updates.description !== undefined) {\n fields.push('description = ?');\n values.push(updates.description);\n }\n if (updates.template !== undefined) {\n fields.push('template = ?');\n values.push(updates.template);\n }\n if (updates.variables !== undefined) {\n fields.push('variables = ?');\n values.push(JSON.stringify(updates.variables));\n }\n if (updates.isDefault !== undefined) {\n fields.push('is_default = ?');\n values.push(updates.isDefault ? 1 : 0);\n }\n\n if (fields.length === 0) {\n return existing;\n }\n\n fields.push('updated_at = ?');\n values.push(new Date().toISOString());\n values.push(templateId);\n\n db.prepare(\n `\n UPDATE prompt_templates\n SET ${fields.join(', ')}\n WHERE id = ?\n `\n ).run(...values);\n\n return getTemplateById(db, templateId);\n}\n\n/**\n * Delete a template\n *\n * @param db - Database instance\n * @param templateId - Template ID\n * @returns True if deleted, false if not found\n */\nexport function deleteTemplate(\n db: Database.Database,\n templateId: string\n): boolean {\n const result = db\n .prepare('DELETE FROM prompt_templates WHERE id = ?')\n .run(templateId);\n\n return result.changes > 0;\n}\n", "/**\n * Prompt Template Engine\n *\n * Simple template engine with Handlebars-like syntax for rendering prompts.\n * Supports variable substitution, conditionals, and loops.\n *\n * @module services/prompt-template-engine\n */\n\n/**\n * PromptTemplateEngine - Render templates with context variables\n *\n * Template Syntax:\n * - Variables: {{variable}} or {{object.nested.path}}\n * - Conditionals: {{#if variable}}content{{/if}}\n * - Loops: {{#each array}}content{{/each}}\n *\n * @example\n * ```typescript\n * const engine = new PromptTemplateEngine();\n * const result = engine.render(\n * 'Hello {{name}}!',\n * { name: 'World' }\n * );\n * // Result: 'Hello World!'\n * ```\n */\nexport class PromptTemplateEngine {\n /**\n * Render template with context variables\n *\n * @param template - Template string with {{variable}} syntax\n * @param context - Object containing variable values\n * @returns Rendered template string\n *\n * @example\n * ```typescript\n * const template = `\n * Issue: {{issue.title}}\n * {{#if issue.description}}\n * Description: {{issue.description}}\n * {{/if}}\n * {{#each specs}}\n * - [[{{id}}]]: {{title}}\n * {{/each}}\n * `;\n *\n * const context = {\n * issue: { title: 'Bug fix', description: 'Fix auth' },\n * specs: [{ id: 'SPEC-001', title: 'Auth' }]\n * };\n *\n * engine.render(template, context);\n * ```\n */\n render(template: string, context: Record<string, any>): string {\n let result = template;\n\n // Process in order: loops, conditionals, then variables\n // Use a while loop to handle nested structures by processing innermost first\n\n // 1. Handle loops: {{#each array}}...{{/each}}\n // Process innermost loops first by finding balanced tags\n let previousResult = '';\n while (result !== previousResult) {\n previousResult = result;\n result = this.replaceLoop(result, context);\n }\n\n // 2. Handle conditionals: {{#if variable}}...{{/if}}\n previousResult = '';\n while (result !== previousResult) {\n previousResult = result;\n result = this.replaceConditional(result, context);\n }\n\n // 3. Replace simple variables: {{variable}}\n result = result.replace(/\\{\\{([^}#/]+)\\}\\}/g, (match, key) => {\n const value = this.getValue(context, key.trim());\n return value !== undefined ? String(value) : match;\n });\n\n return result;\n }\n\n /**\n * Replace one {{#each}} loop with balanced tags\n * Processes innermost loop first\n */\n private replaceLoop(template: string, context: Record<string, any>): string {\n const match = this.findBalancedTag(template, 'each');\n if (!match) return template;\n\n const { key, content, fullMatch } = match;\n const array = this.getValue(context, key.trim());\n\n if (!Array.isArray(array)) {\n return template.replace(fullMatch, '');\n }\n\n const replacement = array\n .map((item) => this.render(content, item))\n .join('');\n\n return template.replace(fullMatch, replacement);\n }\n\n /**\n * Replace one {{#if}} conditional with balanced tags\n */\n private replaceConditional(template: string, context: Record<string, any>): string {\n const match = this.findBalancedTag(template, 'if');\n if (!match) return template;\n\n const { key, content, fullMatch } = match;\n const value = this.getValue(context, key.trim());\n const replacement = value ? content : '';\n\n return template.replace(fullMatch, replacement);\n }\n\n /**\n * Find a balanced {{#tagName}} ... {{/tagName}} pair\n * Returns the FIRST match (outermost, will be processed recursively)\n */\n private findBalancedTag(\n template: string,\n tagName: string\n ): { key: string; content: string; fullMatch: string } | null {\n const openPattern = `\\\\{\\\\{#${tagName}\\\\s+([^}]+)\\\\}\\\\}`;\n const closePattern = `\\\\{\\\\{\\\\/${tagName}\\\\}\\\\}`;\n const openRegex = new RegExp(openPattern);\n const match = openRegex.exec(template);\n\n if (!match) return null;\n\n const openIndex = match.index;\n const key = match[1];\n let depth = 1;\n let i = openIndex + match[0].length;\n const contentStart = i;\n\n // Find the matching closing tag by counting depth\n while (i < template.length && depth > 0) {\n // Check for nested opening tags\n const nestedOpen = template.slice(i).match(new RegExp(`^${openPattern}`));\n if (nestedOpen) {\n depth++;\n i += nestedOpen[0].length;\n continue;\n }\n\n // Check for closing tags\n const close = template.slice(i).match(new RegExp(`^${closePattern}`));\n if (close) {\n depth--;\n if (depth === 0) {\n const content = template.slice(contentStart, i);\n const fullMatch = template.slice(openIndex, i + close[0].length);\n return { key, content, fullMatch };\n }\n i += close[0].length;\n continue;\n }\n\n i++;\n }\n\n return null;\n }\n\n /**\n * Get nested value from context using dot notation\n *\n * @param context - Context object\n * @param path - Dot-separated path (e.g., 'user.name')\n * @returns Value at path or undefined\n *\n * @example\n * ```typescript\n * getValue({ user: { name: 'Alice' } }, 'user.name')\n * // Returns: 'Alice'\n * ```\n */\n private getValue(context: Record<string, any>, path: string): any {\n const keys = path.split('.');\n let value: any = context;\n\n for (const key of keys) {\n if (value === null || value === undefined) return undefined;\n value = value[key];\n }\n\n return value;\n }\n\n /**\n * Validate template syntax\n *\n * Checks for:\n * - Balanced {{#if}} / {{/if}} tags\n * - Balanced {{#each}} / {{/each}} tags\n *\n * @param template - Template string to validate\n * @returns Validation result with errors if any\n *\n * @example\n * ```typescript\n * engine.validate('{{#if x}}content{{/if}}')\n * // Returns: { valid: true, errors: [] }\n *\n * engine.validate('{{#if x}}content')\n * // Returns: { valid: false, errors: ['Unbalanced {{#if}} tags'] }\n * ```\n */\n validate(template: string): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n // Check for balanced {{#if}} tags\n const ifCount = (template.match(/\\{\\{#if/g) || []).length;\n const endIfCount = (template.match(/\\{\\{\\/if\\}\\}/g) || []).length;\n if (ifCount !== endIfCount) {\n errors.push(`Unbalanced {{#if}} tags (${ifCount} vs ${endIfCount})`);\n }\n\n // Check for balanced {{#each}} tags\n const eachCount = (template.match(/\\{\\{#each/g) || []).length;\n const endEachCount = (template.match(/\\{\\{\\/each\\}\\}/g) || []).length;\n if (eachCount !== endEachCount) {\n errors.push(\n `Unbalanced {{#each}} tags (${eachCount} vs ${endEachCount})`\n );\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n }\n}\n", "/**\n * Execution Lifecycle Service\n *\n * Centralized service for managing execution lifecycle with worktree integration.\n * Coordinates between WorktreeManager and execution database services.\n *\n * @module services/execution-lifecycle\n */\n\nimport path from \"path\";\nimport type Database from \"better-sqlite3\";\nimport type { AgentType, Execution } from \"@sudocode-ai/types\";\nimport {\n WorktreeManager,\n type IWorktreeManager,\n} from \"../execution/worktree/manager.js\";\nimport { getWorktreeConfig } from \"../execution/worktree/config.js\";\nimport { createExecution, getExecution } from \"./executions.js\";\nimport { randomUUID } from \"crypto\";\n\n/**\n * Parameters for creating an execution with worktree\n */\nexport interface CreateExecutionWithWorktreeParams {\n issueId: string;\n issueTitle: string;\n agentType: AgentType;\n targetBranch: string;\n repoPath: string;\n mode?: string;\n prompt?: string;\n config?: string; // JSON string of execution configuration\n}\n\n/**\n * Result of creating an execution with worktree\n */\nexport interface CreateExecutionWithWorktreeResult {\n execution: Execution;\n worktreePath: string;\n branchName: string;\n}\n\n/**\n * ExecutionLifecycleService\n *\n * Manages the full lifecycle of executions with worktree support:\n * - Creating executions with isolated worktrees\n * - Cleaning up executions and associated worktrees\n * - Handling orphaned worktrees\n */\nexport class ExecutionLifecycleService {\n private worktreeManager: IWorktreeManager;\n private db: Database.Database;\n private repoPath: string;\n\n /**\n * Create a new ExecutionLifecycleService\n *\n * @param db - Database instance\n * @param repoPath - Path to the git repository\n * @param worktreeManager - Optional worktree manager (defaults to new instance)\n */\n constructor(\n db: Database.Database,\n repoPath: string,\n worktreeManager?: IWorktreeManager\n ) {\n this.db = db;\n this.repoPath = repoPath;\n\n // Load config and create worktree manager if not provided\n if (worktreeManager) {\n this.worktreeManager = worktreeManager;\n } else {\n const config = getWorktreeConfig(repoPath);\n this.worktreeManager = new WorktreeManager(config);\n }\n }\n\n /**\n * Create an execution with an isolated worktree\n *\n * Creates a worktree first, then creates the execution record.\n * If worktree creation fails, no execution is created.\n * If execution creation fails, the worktree is cleaned up.\n *\n * @param params - Execution creation parameters\n * @returns Execution with worktree information\n * @throws Error if creation fails\n */\n async createExecutionWithWorktree(\n params: CreateExecutionWithWorktreeParams\n ): Promise<CreateExecutionWithWorktreeResult> {\n const { issueId, issueTitle, agentType, targetBranch, repoPath } = params;\n\n // Validation 1: Check for existing active execution for this issue\n const existingExecution = this.db\n .prepare(\n `SELECT id FROM executions\n WHERE issue_id = ?\n AND status = 'running'\n AND worktree_path IS NOT NULL`\n )\n .get(issueId) as { id: string } | undefined;\n\n if (existingExecution) {\n throw new Error(\n `Active execution already exists for issue ${issueId}: ${existingExecution.id}`\n );\n }\n\n // Validation 2: Validate git repository\n const isValidRepo = await this.worktreeManager.isValidRepo(repoPath);\n if (!isValidRepo) {\n throw new Error(`Not a git repository: ${repoPath}`);\n }\n\n // Validation 3: Validate target branch exists\n const branches = await this.worktreeManager.listBranches(repoPath);\n if (!branches.includes(targetBranch)) {\n throw new Error(`Target branch does not exist: ${targetBranch}`);\n }\n\n const config = this.worktreeManager.getConfig();\n\n // Generate execution ID\n const executionId = randomUUID();\n\n // Determine branch name based on autoCreateBranches setting\n let branchName: string;\n if (config.autoCreateBranches) {\n // Generate branch name: {branchPrefix}/{execution-id}/{sanitized-issue-title}\n const sanitizedTitle = sanitizeForBranchName(issueTitle);\n branchName = `${config.branchPrefix}/${executionId.substring(0, 8)}/${sanitizedTitle}`;\n } else {\n // Use target branch directly when not auto-creating branches\n branchName = targetBranch;\n }\n\n // Generate worktree path: {repoPath}/{worktreeStoragePath}/{execution-id}\n const worktreePath = path.join(\n repoPath,\n config.worktreeStoragePath,\n executionId\n );\n\n let worktreeCreated = false;\n\n try {\n // Step 1: Create worktree\n await this.worktreeManager.createWorktree({\n repoPath,\n branchName,\n worktreePath,\n baseBranch: targetBranch,\n createBranch: config.autoCreateBranches,\n });\n\n worktreeCreated = true;\n\n // Step 2: Create execution record in database\n const execution = createExecution(this.db, {\n id: executionId,\n issue_id: issueId,\n agent_type: agentType,\n mode: params.mode,\n prompt: params.prompt,\n config: params.config,\n target_branch: targetBranch,\n branch_name: branchName,\n worktree_path: worktreePath,\n });\n\n return {\n execution,\n worktreePath,\n branchName,\n };\n } catch (error) {\n // If worktree was created but execution creation failed, cleanup worktree\n if (worktreeCreated) {\n try {\n await this.worktreeManager.cleanupWorktree(worktreePath, repoPath);\n } catch (cleanupError) {\n // Log cleanup error but throw original error\n console.error(\n `Failed to cleanup worktree after execution creation failure:`,\n cleanupError\n );\n }\n }\n\n // Re-throw the original error\n throw error;\n }\n }\n\n /**\n * Check if an execution should be cleaned up based on its config\n *\n * @param executionId - ID of execution to check\n * @returns true if should cleanup, false otherwise\n */\n shouldCleanupExecution(executionId: string): boolean {\n const execution = getExecution(this.db, executionId);\n\n if (!execution) {\n return false;\n }\n\n // Check if cleanupMode is set to 'manual'\n if (execution.config) {\n try {\n const config = JSON.parse(execution.config);\n if (config.cleanupMode === \"manual\" || config.cleanupMode === \"never\") {\n return false;\n }\n } catch (error) {\n console.error(\n `Failed to parse execution config for ${executionId}:`,\n error\n );\n // Default to cleanup on parse error\n }\n }\n\n return true;\n }\n\n /**\n * Clean up an execution and its associated worktree\n *\n * Removes the worktree from filesystem and git metadata.\n * Branch deletion is controlled by autoDeleteBranches config.\n * Respects the cleanupMode configuration from execution config.\n *\n * IMPORTANT: The worktree_path is NEVER cleared from the database.\n * This allows follow-up executions to find and reuse the same worktree path.\n * The filesystem worktree is deleted, but the path remains in the DB as a historical record.\n *\n * @param executionId - ID of execution to cleanup\n * @throws Error if cleanup fails\n */\n async cleanupExecution(executionId: string): Promise<void> {\n // Check if we should cleanup based on config\n if (!this.shouldCleanupExecution(executionId)) {\n return;\n }\n\n // Get execution from database\n const execution = getExecution(this.db, executionId);\n\n if (!execution) {\n // Execution doesn't exist, nothing to cleanup\n return;\n }\n\n // If execution has a worktree path, clean up the filesystem worktree\n // but KEEP the worktree_path in the database for follow-up executions\n if (execution.worktree_path) {\n try {\n await this.worktreeManager.cleanupWorktree(\n execution.worktree_path,\n this.repoPath\n );\n // NOTE: We do NOT set worktree_path to null in the database\n // Follow-up executions need this path to recreate the worktree\n } catch (error) {\n // Log error but don't fail - cleanup is best-effort\n console.error(\n `Failed to cleanup worktree for execution ${executionId}:`,\n error\n );\n }\n }\n }\n\n /**\n * Clean up orphaned worktrees\n *\n * Finds worktrees that are registered in git but don't have\n * corresponding execution records, or vice versa.\n * Also cleans up worktrees for finished executions (completed/failed/stopped).\n */\n async cleanupOrphanedWorktrees(): Promise<void> {\n const repoPath = this.repoPath;\n const config = this.worktreeManager.getConfig();\n\n try {\n // List all worktrees from git\n const worktrees = await this.worktreeManager.listWorktrees(repoPath);\n\n // Filter to worktrees in our storage path\n const managedWorktrees = worktrees.filter((w) =>\n w.path.includes(config.worktreeStoragePath)\n );\n\n // For each managed worktree, check if it has a corresponding execution\n for (const worktree of managedWorktrees) {\n const worktreePath = worktree.path;\n\n // Try to extract execution ID from path\n const executionId = path.basename(worktreePath);\n\n // Check if execution exists in database\n const execution = getExecution(this.db, executionId);\n\n if (!execution) {\n // Orphaned worktree - cleanup\n console.log(\n `Cleaning up orphaned worktree: ${worktreePath} (no execution found)`\n );\n try {\n await this.worktreeManager.cleanupWorktree(worktreePath, repoPath);\n } catch (error) {\n console.error(\n `Failed to cleanup orphaned worktree ${worktreePath}:`,\n error\n );\n }\n } else if (\n execution.status === \"completed\" ||\n execution.status === \"failed\" ||\n execution.status === \"stopped\"\n ) {\n // Execution is finished but worktree still exists\n // Check if we should cleanup based on execution config\n if (!this.shouldCleanupExecution(executionId)) {\n console.log(\n `Skipping cleanup for finished execution ${executionId} (manual cleanup mode)`\n );\n continue;\n }\n\n console.log(\n `Cleaning up worktree for finished execution ${executionId} (status: ${execution.status})`\n );\n try {\n await this.worktreeManager.cleanupWorktree(worktreePath, repoPath);\n // NOTE: We do NOT set worktree_path to null in the database\n // Follow-up executions need this path to recreate the worktree\n } catch (error) {\n console.error(\n `Failed to cleanup worktree for finished execution ${executionId}:`,\n error\n );\n }\n }\n }\n } catch (error) {\n console.error(\n `Failed to cleanup orphaned worktrees in ${repoPath}:`,\n error\n );\n }\n }\n}\n\n/**\n * Sanitize a string to be safe for use in git branch names\n *\n * - Converts to lowercase\n * - Replaces spaces and slashes with hyphens\n * - Removes special characters\n * - Limits length to 50 characters\n *\n * @param str - String to sanitize\n * @returns Sanitized string safe for branch names\n */\nexport function sanitizeForBranchName(str: string): string {\n return (\n str\n .toLowerCase()\n // Replace spaces and slashes with hyphens\n .replace(/[\\s/]+/g, \"-\")\n // Remove special characters (keep alphanumeric, hyphens, underscores)\n .replace(/[^a-z0-9\\-_]/g, \"\")\n // Remove consecutive hyphens\n .replace(/-+/g, \"-\")\n // Remove leading/trailing hyphens\n .replace(/^-+|-+$/g, \"\")\n // Limit length\n .substring(0, 50)\n );\n}\n", "/**\n * Worktree Manager\n *\n * Manages git worktrees for session isolation.\n *\n * WORKTREE ISOLATION:\n * ===================\n * Each worktree created by this manager is completely isolated from the main\n * repository. This prevents race conditions and unexpected modifications during\n * concurrent executions.\n *\n * Isolation is achieved through:\n * 1. Local database (.sudocode/cache.db) in each worktree\n * 2. Synced JSONL files with latest state (including uncommitted changes)\n * 3. Claude config (.claude/config.json) that forces MCP to use local database\n *\n * Key Benefit: Multiple executions can run concurrently without interfering\n * with each other or the main repository. All MCP/CLI operations in a worktree\n * stay contained within that worktree.\n *\n * See setupWorktreeEnvironment() method for detailed implementation.\n *\n * @module execution/worktree/manager\n */\n\nimport { Mutex } from \"async-mutex\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport type {\n WorktreeCreateParams,\n WorktreeConfig,\n WorktreeInfo,\n} from \"./types.js\";\nimport { WorktreeError, WorktreeErrorCode } from \"./types.js\";\nimport { GitCli, type IGitCli } from \"./git-cli.js\";\nimport { initDatabase } from \"@sudocode-ai/cli/dist/db.js\";\nimport { importFromJSONL } from \"@sudocode-ai/cli/dist/import.js\";\nimport { execSync } from \"child_process\";\n\n/**\n * IWorktreeManager - Interface for worktree management\n *\n * Provides methods for creating, validating, and cleaning up git worktrees.\n * Implementations must handle race conditions with locking.\n */\nexport interface IWorktreeManager {\n /**\n * Create a new worktree for a session\n *\n * @param params - Worktree creation parameters\n * @returns Promise that resolves when worktree is created\n * @throws WorktreeError if creation fails\n */\n createWorktree(params: WorktreeCreateParams): Promise<void>;\n\n /**\n * Ensure worktree exists, recreating if necessary\n * Uses locking to prevent race conditions\n *\n * @param repoPath - Path to the main git repository\n * @param branchName - Branch name for the worktree\n * @param worktreePath - Where the worktree should exist\n * @returns Promise that resolves when worktree exists\n * @throws WorktreeError if ensure fails\n */\n ensureWorktreeExists(\n repoPath: string,\n branchName: string,\n worktreePath: string\n ): Promise<void>;\n\n /**\n * Clean up a worktree (filesystem + git metadata)\n *\n * @param worktreePath - Path to the worktree to cleanup\n * @param repoPath - Optional path to repository (will try to infer if not provided)\n * @returns Promise that resolves when cleanup is complete\n * @throws WorktreeError if cleanup fails\n */\n cleanupWorktree(worktreePath: string, repoPath?: string): Promise<void>;\n\n /**\n * Check if worktree is properly set up\n * Validates both filesystem existence and git metadata registration\n *\n * @param repoPath - Path to the main git repository\n * @param worktreePath - Path to check\n * @returns Promise resolving to true if valid, false otherwise\n */\n isWorktreeValid(repoPath: string, worktreePath: string): Promise<boolean>;\n\n /**\n * List all worktrees for a repository\n *\n * @param repoPath - Path to the main git repository\n * @returns Promise resolving to list of worktree info\n * @throws WorktreeError if listing fails\n */\n listWorktrees(repoPath: string): Promise<WorktreeInfo[]>;\n\n /**\n * Get the current configuration\n *\n * @returns Current worktree configuration\n */\n getConfig(): WorktreeConfig;\n\n /**\n * Check if a path is a valid git repository\n *\n * @param repoPath - Path to check\n * @returns Promise resolving to true if valid repo, false otherwise\n */\n isValidRepo(repoPath: string): Promise<boolean>;\n\n /**\n * List all branches in a repository\n *\n * @param repoPath - Path to the git repository\n * @returns Promise resolving to array of branch names\n */\n listBranches(repoPath: string): Promise<string[]>;\n}\n\n/**\n * WorktreeManager - Implementation of IWorktreeManager\n *\n * Manages git worktrees with proper locking, cleanup, and error recovery.\n * Uses git CLI commands for reliability.\n */\nexport class WorktreeManager implements IWorktreeManager {\n /** Per-path locks to prevent concurrent operations */\n private locks = new Map<string, Mutex>();\n\n /** Configuration loaded from .sudocode/config.json */\n private config: WorktreeConfig;\n\n /** Git CLI wrapper */\n private git: IGitCli;\n\n /**\n * Create a new WorktreeManager\n *\n * @param config - Worktree configuration\n * @param git - Optional git CLI implementation (defaults to GitCli)\n */\n constructor(config: WorktreeConfig, git?: IGitCli) {\n this.config = config;\n this.git = git || new GitCli();\n }\n\n /**\n * Get or create a lock for a specific path\n *\n * @param path - Path to lock\n * @returns Mutex for the path\n */\n private getLock(path: string): Mutex {\n let lock = this.locks.get(path);\n if (!lock) {\n lock = new Mutex();\n this.locks.set(path, lock);\n }\n return lock;\n }\n\n async createWorktree(params: WorktreeCreateParams): Promise<void> {\n const {\n repoPath,\n branchName,\n worktreePath,\n baseBranch: _baseBranch,\n createBranch,\n commitSha,\n } = params;\n\n try {\n // 1. Create branch if requested\n if (createBranch) {\n // Use the specified commit SHA or the current HEAD commit SHA to branch from\n const targetCommit =\n commitSha || (await this.git.getCurrentCommit(repoPath));\n await this.git.createBranch(repoPath, branchName, targetCommit);\n }\n\n // 2. Create parent directory if needed\n const parentDir = path.dirname(worktreePath);\n if (!fs.existsSync(parentDir)) {\n fs.mkdirSync(parentDir, { recursive: true });\n }\n\n // 3. Call git worktree add\n await this.git.worktreeAdd(repoPath, worktreePath, branchName);\n\n // 4. Apply sparse-checkout if configured\n if (\n this.config.enableSparseCheckout &&\n this.config.sparseCheckoutPatterns\n ) {\n await this.git.configureSparseCheckout(\n worktreePath,\n this.config.sparseCheckoutPatterns\n );\n }\n\n // 5. Validate creation\n if (!fs.existsSync(worktreePath)) {\n throw new WorktreeError(\n `Worktree creation succeeded but path does not exist: ${worktreePath}`,\n WorktreeErrorCode.REPOSITORY_ERROR\n );\n }\n\n // 6. Setup isolated worktree environment\n // This is critical for preventing the worktree from modifying the main repository.\n // It creates a local database, syncs JSONL files, and configures Claude to use\n // the local environment. See setupWorktreeEnvironment() for detailed explanation.\n await this.setupWorktreeEnvironment(repoPath, worktreePath);\n } catch (error) {\n if (error instanceof WorktreeError) {\n throw error;\n }\n throw new WorktreeError(\n `Failed to create worktree: ${error}`,\n WorktreeErrorCode.REPOSITORY_ERROR,\n error as Error\n );\n }\n }\n\n /**\n * Setup isolated environment for worktree\n *\n * WORKTREE ISOLATION ARCHITECTURE:\n * ================================\n * Problem: Previously, MCP/CLI tools running in worktrees would search upward\n * and find the main repository's database, causing race conditions and\n * unexpected modifications to the main repo during execution.\n *\n * Solution: Each worktree gets its own isolated environment:\n * - Local database (.sudocode/cache.db in worktree)\n * - Synced JSONL files with latest state (including uncommitted changes)\n * - Claude config that forces MCP to use the local database\n *\n * Benefits:\n * - Worktree operations never affect main repository\n * - Multiple executions can run concurrently without conflicts\n * - Worktree gets consistent state (newly created issues are available)\n * - Easy to inspect/debug worktree state after execution\n *\n * Flow:\n * 1. Git creates worktree \u2192 checks out files from committed git tree\n * 2. This method runs \u2192 copies latest JSONL (including uncommitted changes)\n * 3. Initializes local DB from JSONL \u2192 worktree has complete state\n * 4. Creates .claude/config.json \u2192 MCP uses local DB via env vars\n * 5. Claude runs \u2192 all MCP operations stay in worktree\n * 6. (Future) Merge worktree changes back to main after execution\n *\n * @param repoPath - Path to the main git repository\n * @param worktreePath - Path to the worktree directory\n */\n private async setupWorktreeEnvironment(\n repoPath: string,\n worktreePath: string\n ): Promise<void> {\n console.debug(\n \"[WorktreeManager] Setting up isolated worktree environment\",\n {\n repoPath,\n worktreePath,\n }\n );\n\n const mainSudocodeDir = path.join(repoPath, \".sudocode\");\n const worktreeSudocodeDir = path.join(worktreePath, \".sudocode\");\n\n console.debug(\"[WorktreeManager] Directory paths\", {\n mainSudocodeDir,\n worktreeSudocodeDir,\n });\n\n // Ensure .sudocode directory exists in worktree\n if (!fs.existsSync(worktreeSudocodeDir)) {\n fs.mkdirSync(worktreeSudocodeDir, { recursive: true });\n console.debug(\n `[WorktreeManager] Created .sudocode directory in worktree: ${worktreeSudocodeDir}`\n );\n } else {\n console.debug(\n `[WorktreeManager] .sudocode directory already exists in worktree`\n );\n }\n\n // STEP 1: Copy uncommitted JSONL files from main repo to worktree\n // ================================================================\n // Why: Git worktree checkout only gets committed files from git history.\n // If the user created new issues/specs before starting the execution,\n // those changes are in the main repo's JSONL files but not committed.\n // We need to copy them so the worktree has the complete, up-to-date state.\n //\n // Example: User creates ISSUE-144, starts execution immediately.\n // - Git tree: has 138 issues (old state)\n // - Main JSONL: has 140 issues (includes ISSUE-144, uncommitted)\n // - Without this copy: worktree would only have 138 issues, ISSUE-144 missing!\n // - With this copy: worktree gets all 140 issues, execution can reference ISSUE-144\n const jsonlFiles = [\"issues.jsonl\", \"specs.jsonl\"];\n for (const file of jsonlFiles) {\n const mainFile = path.join(mainSudocodeDir, file);\n const worktreeFile = path.join(worktreeSudocodeDir, file);\n\n console.debug(`[WorktreeManager] Processing ${file}`, {\n mainFile,\n worktreeFile,\n mainFileExists: fs.existsSync(mainFile),\n });\n\n if (fs.existsSync(mainFile)) {\n const mainStats = fs.statSync(mainFile);\n fs.copyFileSync(mainFile, worktreeFile);\n const worktreeStats = fs.statSync(worktreeFile);\n console.debug(\n `[WorktreeManager] Synced ${file} from main repo to worktree`,\n {\n mainFileSize: mainStats.size,\n worktreeFileSize: worktreeStats.size,\n }\n );\n } else {\n console.debug(\n `[WorktreeManager] Main file does not exist: ${mainFile}`\n );\n }\n }\n\n // STEP 2: Copy config.json\n // ========================\n // Copy sudocode configuration to maintain consistency\n const mainConfig = path.join(mainSudocodeDir, \"config.json\");\n const worktreeConfig = path.join(worktreeSudocodeDir, \"config.json\");\n if (fs.existsSync(mainConfig)) {\n fs.copyFileSync(mainConfig, worktreeConfig);\n console.debug(\n `[WorktreeManager] Copied config.json from main repo to worktree`\n );\n } else {\n console.debug(`[WorktreeManager] No config.json to copy from main repo`);\n }\n\n // STEP 3: Initialize local database in worktree\n // ==============================================\n // Create a brand new SQLite database in the worktree and import the JSONL\n // files we just copied. This gives the worktree its own isolated database\n // with the complete current state.\n //\n // Important: This database is completely separate from the main repo's DB.\n // All MCP/CLI operations in the worktree will use THIS database, not the main one.\n\n const worktreeDbPath = path.join(worktreeSudocodeDir, \"cache.db\");\n\n // Initialize database with CLI's initDatabase (creates all tables)\n const db = initDatabase({ path: worktreeDbPath, verbose: false });\n\n try {\n await importFromJSONL(db, {\n inputDir: worktreeSudocodeDir,\n });\n console.debug(\n `[WorktreeManager] Successfully initialized local database in worktree at ${worktreeDbPath}`\n );\n\n // Verify database was created\n if (fs.existsSync(worktreeDbPath)) {\n const dbStats = fs.statSync(worktreeDbPath);\n console.debug(\"[WorktreeManager] Database file created\", {\n path: worktreeDbPath,\n size: dbStats.size,\n });\n } else {\n console.error(\n \"[WorktreeManager] ERROR: Database file was not created!\"\n );\n }\n } catch (error) {\n console.error(\"[WorktreeManager] Failed to initialize database\", error);\n throw error;\n } finally {\n db.close();\n }\n console.debug(\"[WorktreeManager] Worktree environment setup complete\");\n }\n\n async ensureWorktreeExists(\n repoPath: string,\n branchName: string,\n worktreePath: string\n ): Promise<void> {\n // Get lock for this specific path\n const lock = this.getLock(worktreePath);\n const release = await lock.acquire();\n\n try {\n // Check if already exists and valid\n if (await this.isWorktreeValid(repoPath, worktreePath)) {\n return;\n }\n\n // Recreate worktree\n await this.recreateWorktree(repoPath, branchName, worktreePath);\n } finally {\n release();\n }\n }\n\n async cleanupWorktree(\n worktreePath: string,\n repoPath?: string\n ): Promise<void> {\n // Get lock for this specific path\n const lock = this.getLock(worktreePath);\n const release = await lock.acquire();\n\n try {\n // Infer repoPath if not provided (try to find from worktree)\n const effectiveRepoPath =\n repoPath || (await this.inferRepoPath(worktreePath));\n\n if (!effectiveRepoPath) {\n // Can't determine repo path, just cleanup the directory\n if (fs.existsSync(worktreePath)) {\n fs.rmSync(worktreePath, { recursive: true, force: true });\n }\n return;\n }\n\n // Get worktree info to find branch name (for optional deletion)\n let branchName: string | undefined;\n try {\n const worktrees = await this.git.worktreeList(effectiveRepoPath);\n\n // Normalize paths for comparison (resolves symlinks like /var -> /private/var on macOS)\n const normalizedWorktreePath = fs.realpathSync(worktreePath);\n const worktreeInfo = worktrees.find((w) => {\n try {\n const normalizedGitPath = fs.realpathSync(w.path);\n return normalizedGitPath === normalizedWorktreePath;\n } catch {\n // If path doesn't exist, try direct comparison\n return w.path === worktreePath;\n }\n });\n\n if (worktreeInfo) {\n branchName = worktreeInfo.branch;\n }\n } catch (error) {\n // Ignore errors, branch deletion is optional\n }\n\n // 1. Remove git worktree registration\n try {\n await this.git.worktreeRemove(effectiveRepoPath, worktreePath, true);\n } catch (error) {\n // Worktree might already be removed or invalid, continue cleanup\n }\n\n // 2. Force cleanup metadata directory\n const worktreeName = path.basename(worktreePath);\n const metadataPath = path.join(\n effectiveRepoPath,\n \".git\",\n \"worktrees\",\n worktreeName\n );\n if (fs.existsSync(metadataPath)) {\n fs.rmSync(metadataPath, { recursive: true, force: true });\n }\n\n // 3. Remove filesystem directory\n if (fs.existsSync(worktreePath)) {\n fs.rmSync(worktreePath, { recursive: true, force: true });\n }\n\n // 4. Prune stale worktree entries\n try {\n await this.git.worktreePrune(effectiveRepoPath);\n } catch (error) {\n // Prune is best-effort, continue even if it fails\n }\n\n // 5. Delete branch if configured\n if (\n this.config.autoDeleteBranches &&\n branchName &&\n branchName !== \"(detached)\"\n ) {\n try {\n await this.git.deleteBranch(effectiveRepoPath, branchName, true);\n } catch (error) {\n // Branch deletion is optional, don't fail the cleanup\n }\n }\n } finally {\n release();\n }\n }\n\n async isWorktreeValid(\n repoPath: string,\n worktreePath: string\n ): Promise<boolean> {\n try {\n // 1. Check filesystem path exists\n if (!fs.existsSync(worktreePath)) {\n return false;\n }\n\n // 2. Check worktree is registered in git metadata\n const worktrees = await this.git.worktreeList(repoPath);\n\n // Normalize paths for comparison (resolves symlinks like /var -> /private/var on macOS)\n const normalizedWorktreePath = fs.realpathSync(worktreePath);\n const isRegistered = worktrees.some((w) => {\n try {\n const normalizedGitPath = fs.realpathSync(w.path);\n return normalizedGitPath === normalizedWorktreePath;\n } catch {\n // If path doesn't exist, try direct comparison\n return w.path === worktreePath;\n }\n });\n\n return isRegistered;\n } catch (error) {\n // On any error, consider invalid\n return false;\n }\n }\n\n async listWorktrees(repoPath: string): Promise<WorktreeInfo[]> {\n return await this.git.worktreeList(repoPath);\n }\n\n getConfig(): WorktreeConfig {\n return { ...this.config };\n }\n\n async isValidRepo(repoPath: string): Promise<boolean> {\n return this.git.isValidRepo(repoPath);\n }\n\n async listBranches(repoPath: string): Promise<string[]> {\n return this.git.listBranches(repoPath);\n }\n\n /**\n * Recreate a worktree (internal method)\n *\n * @param repoPath - Path to repository\n * @param branchName - Branch name\n * @param worktreePath - Worktree path\n */\n private async recreateWorktree(\n repoPath: string,\n branchName: string,\n worktreePath: string\n ): Promise<void> {\n // 1. Comprehensive cleanup of existing worktree\n await this.cleanupWorktree(worktreePath, repoPath);\n\n // 2. Create parent directory if needed\n const parentDir = path.dirname(worktreePath);\n if (!fs.existsSync(parentDir)) {\n fs.mkdirSync(parentDir, { recursive: true });\n }\n\n // 3. Create worktree with retry logic\n let lastError: Error | undefined;\n const maxRetries = 1;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n await this.git.worktreeAdd(repoPath, worktreePath, branchName);\n\n // Apply sparse-checkout if configured\n if (\n this.config.enableSparseCheckout &&\n this.config.sparseCheckoutPatterns\n ) {\n await this.git.configureSparseCheckout(\n worktreePath,\n this.config.sparseCheckoutPatterns\n );\n }\n\n // Validate creation\n if (!fs.existsSync(worktreePath)) {\n throw new WorktreeError(\n `Worktree creation succeeded but path does not exist: ${worktreePath}`,\n WorktreeErrorCode.REPOSITORY_ERROR\n );\n }\n\n // Setup isolated worktree environment (see setupWorktreeEnvironment for details)\n await this.setupWorktreeEnvironment(repoPath, worktreePath);\n\n return; // Success!\n } catch (error) {\n lastError = error as Error;\n\n if (attempt < maxRetries) {\n // Cleanup metadata and try again\n const worktreeName = path.basename(worktreePath);\n const metadataPath = path.join(\n repoPath,\n \".git\",\n \"worktrees\",\n worktreeName\n );\n if (fs.existsSync(metadataPath)) {\n fs.rmSync(metadataPath, { recursive: true, force: true });\n }\n }\n }\n }\n\n // All retries failed\n throw new WorktreeError(\n `Failed to recreate worktree after ${maxRetries + 1} attempts: ${lastError}`,\n WorktreeErrorCode.REPOSITORY_ERROR,\n lastError\n );\n }\n\n /**\n * Infer git repository path from a worktree\n * Uses git rev-parse --git-common-dir\n *\n * @param worktreePath - Path to worktree\n * @returns Repository path or undefined\n */\n private async inferRepoPath(\n worktreePath: string\n ): Promise<string | undefined> {\n try {\n if (!fs.existsSync(worktreePath)) {\n return undefined;\n }\n\n // Try to use git to find the common git directory\n const gitCommonDir = execSync(\"git rev-parse --git-common-dir\", {\n cwd: worktreePath,\n encoding: \"utf8\",\n }).trim();\n\n // git-common-dir gives us the .git directory\n // We need the working directory (parent of .git)\n const gitDirPath = path.resolve(worktreePath, gitCommonDir);\n if (path.basename(gitDirPath) === \".git\") {\n return path.dirname(gitDirPath);\n }\n\n return gitDirPath;\n } catch (error) {\n return undefined;\n }\n }\n}\n", "/**\n * Worktree Types\n *\n * Type definitions for worktree management system.\n *\n * @module execution/worktree/types\n */\n\n/**\n * Worktree creation parameters\n */\nexport interface WorktreeCreateParams {\n /** Path to the main git repository */\n repoPath: string;\n /** Branch name for the worktree */\n branchName: string;\n /** Where to create the worktree */\n worktreePath: string;\n /** Branch to base the new branch on */\n baseBranch: string;\n /** Whether to create the branch */\n createBranch: boolean;\n /** Commit SHA to branch from */\n commitSha?: string;\n}\n\n/**\n * Worktree information returned from git worktree list\n */\nexport interface WorktreeInfo {\n /** Path to the worktree */\n path: string;\n /** Branch name */\n branch: string;\n /** Git commit hash */\n commit: string;\n /** Whether this is the main worktree */\n isMain: boolean;\n /** Whether the worktree is locked */\n isLocked: boolean;\n /** Reason for lock (if locked) */\n lockReason?: string;\n}\n\n/**\n * Worktree configuration (will be implemented in ISSUE-111)\n * Placeholder type for now\n */\nexport interface WorktreeConfig {\n /** Where to store worktrees */\n worktreeStoragePath: string;\n /** Auto-create branches for new sessions */\n autoCreateBranches: boolean;\n /** Auto-delete branches when session is cleaned up */\n autoDeleteBranches: boolean;\n /** Use sparse-checkout for worktrees */\n enableSparseCheckout: boolean;\n /** Patterns for sparse-checkout */\n sparseCheckoutPatterns?: string[];\n /** Branch naming prefix */\n branchPrefix: string;\n /** Cleanup orphaned worktrees on server startup */\n cleanupOrphanedWorktreesOnStartup: boolean;\n}\n\n/**\n * Worktree manager errors\n */\nexport class WorktreeError extends Error {\n constructor(\n message: string,\n public code: WorktreeErrorCode,\n public cause?: Error\n ) {\n super(message);\n this.name = \"WorktreeError\";\n }\n}\n\nexport enum WorktreeErrorCode {\n /** Git operation failed */\n GIT_ERROR = \"GIT_ERROR\",\n /** Worktree path already exists */\n PATH_EXISTS = \"PATH_EXISTS\",\n /** Worktree path not found */\n PATH_NOT_FOUND = \"PATH_NOT_FOUND\",\n /** Invalid path */\n INVALID_PATH = \"INVALID_PATH\",\n /** Branch not found */\n BRANCH_NOT_FOUND = \"BRANCH_NOT_FOUND\",\n /** Repository error */\n REPOSITORY_ERROR = \"REPOSITORY_ERROR\",\n /** Configuration error */\n CONFIG_ERROR = \"CONFIG_ERROR\",\n /** Locking error */\n LOCK_ERROR = \"LOCK_ERROR\",\n /** Cleanup failed */\n CLEANUP_FAILED = \"CLEANUP_FAILED\",\n}\n", "/**\n * Git CLI Wrapper\n *\n * Provides a wrapper around git CLI commands for worktree operations.\n * Uses git CLI directly for reliability (recommended over libgit2/nodegit).\n *\n * @module execution/worktree/git-cli\n */\n\nimport { execSync } from 'child_process';\nimport type { WorktreeInfo } from './types.js';\nimport { WorktreeError, WorktreeErrorCode } from './types.js';\n\n/**\n * IGitCli - Interface for git CLI operations\n */\nexport interface IGitCli {\n /**\n * Add a new worktree\n * Equivalent to: git worktree add <path> <branch>\n *\n * @param repoPath - Path to the main git repository\n * @param worktreePath - Path where worktree will be created\n * @param branch - Branch name for the worktree\n * @param force - Force creation even if path exists\n */\n worktreeAdd(\n repoPath: string,\n worktreePath: string,\n branch: string,\n force?: boolean\n ): Promise<void>;\n\n /**\n * Remove a worktree\n * Equivalent to: git worktree remove <path> --force\n *\n * @param repoPath - Path to the main git repository\n * @param worktreePath - Path to worktree to remove\n * @param force - Force removal even if worktree is dirty\n */\n worktreeRemove(\n repoPath: string,\n worktreePath: string,\n force?: boolean\n ): Promise<void>;\n\n /**\n * Prune worktree metadata\n * Equivalent to: git worktree prune\n *\n * @param repoPath - Path to the main git repository\n */\n worktreePrune(repoPath: string): Promise<void>;\n\n /**\n * List all worktrees\n * Equivalent to: git worktree list --porcelain\n *\n * @param repoPath - Path to the main git repository\n * @returns Array of worktree information\n */\n worktreeList(repoPath: string): Promise<WorktreeInfo[]>;\n\n /**\n * Create a branch\n * Equivalent to: git branch <name> <base>\n *\n * @param repoPath - Path to the git repository\n * @param branchName - Name of the new branch\n * @param baseBranchOrCommit - Base branch or commit SHA to branch from\n */\n createBranch(\n repoPath: string,\n branchName: string,\n baseBranchOrCommit: string\n ): Promise<void>;\n\n /**\n * Delete a branch\n * Equivalent to: git branch -d <name>\n *\n * @param repoPath - Path to the git repository\n * @param branchName - Name of the branch to delete\n * @param force - Force deletion (use -D instead of -d)\n */\n deleteBranch(\n repoPath: string,\n branchName: string,\n force?: boolean\n ): Promise<void>;\n\n /**\n * Configure sparse-checkout for a worktree\n * Equivalent to: git sparse-checkout set <patterns>\n *\n * @param worktreePath - Path to the worktree\n * @param patterns - Sparse checkout patterns\n */\n configureSparseCheckout(\n worktreePath: string,\n patterns: string[]\n ): Promise<void>;\n\n /**\n * Check if a path is a valid git repository\n * Equivalent to: git rev-parse --git-dir\n *\n * @param repoPath - Path to check\n * @returns Promise resolving to true if valid repo, false otherwise\n */\n isValidRepo(repoPath: string): Promise<boolean>;\n\n /**\n * List all branches in a repository\n * Equivalent to: git branch --list --all --format='%(refname:short)'\n *\n * @param repoPath - Path to the git repository\n * @returns Promise resolving to array of branch names\n */\n listBranches(repoPath: string): Promise<string[]>;\n\n /**\n * Get current HEAD commit SHA\n * Equivalent to: git rev-parse HEAD\n *\n * @param repoPath - Path to the git repository\n * @returns Promise resolving to the current HEAD commit SHA\n */\n getCurrentCommit(repoPath: string): Promise<string>;\n}\n\n/**\n * GitCli - Implementation of IGitCli using child_process\n */\nexport class GitCli implements IGitCli {\n /**\n * Execute a git command\n *\n * @param command - Git command to execute\n * @param cwd - Working directory\n * @returns Command output\n * @throws WorktreeError on failure\n */\n protected execGit(command: string, cwd: string): string {\n try {\n return execSync(command, {\n cwd,\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n } catch (error: any) {\n const stderr = error.stderr?.toString() || '';\n const stdout = error.stdout?.toString() || '';\n const message = stderr || stdout || error.message || 'Unknown git error';\n\n throw new WorktreeError(\n `Git command failed: ${command}\\n${message}`,\n WorktreeErrorCode.GIT_ERROR,\n error\n );\n }\n }\n\n /**\n * Escape shell argument\n *\n * @param arg - Argument to escape\n * @returns Escaped argument\n */\n private escapeShellArg(arg: string): string {\n // Escape single quotes and wrap in single quotes\n return `'${arg.replace(/'/g, \"'\\\\''\")}'`;\n }\n\n async worktreeAdd(\n repoPath: string,\n worktreePath: string,\n branch: string,\n force = false\n ): Promise<void> {\n const escapedPath = this.escapeShellArg(worktreePath);\n const escapedBranch = this.escapeShellArg(branch);\n const forceFlag = force ? '--force' : '';\n\n const command = `git worktree add ${forceFlag} ${escapedPath} ${escapedBranch}`.trim();\n this.execGit(command, repoPath);\n }\n\n async worktreeRemove(\n repoPath: string,\n worktreePath: string,\n force = false\n ): Promise<void> {\n const escapedPath = this.escapeShellArg(worktreePath);\n const forceFlag = force ? '--force' : '';\n\n const command = `git worktree remove ${forceFlag} ${escapedPath}`.trim();\n this.execGit(command, repoPath);\n }\n\n async worktreePrune(repoPath: string): Promise<void> {\n this.execGit('git worktree prune', repoPath);\n }\n\n async worktreeList(repoPath: string): Promise<WorktreeInfo[]> {\n const output = this.execGit('git worktree list --porcelain', repoPath);\n return this.parseWorktreeList(output);\n }\n\n /**\n * Parse output from git worktree list --porcelain\n *\n * Format:\n * worktree /path/to/worktree\n * HEAD abc123...\n * branch refs/heads/branch-name\n * locked reason (optional)\n * prunable reason (optional)\n *\n * @param output - Output from git worktree list --porcelain\n * @returns Array of WorktreeInfo\n */\n private parseWorktreeList(output: string): WorktreeInfo[] {\n const worktrees: WorktreeInfo[] = [];\n const lines = output.split('\\n').filter((line) => line.trim());\n\n let currentWorktree: Partial<WorktreeInfo> | null = null;\n\n for (const line of lines) {\n if (line.startsWith('worktree ')) {\n // Start a new worktree entry\n if (currentWorktree && currentWorktree.path) {\n worktrees.push(this.finalizeWorktreeInfo(currentWorktree));\n }\n currentWorktree = {\n path: line.substring('worktree '.length).trim(),\n isMain: false,\n isLocked: false,\n };\n } else if (line.startsWith('HEAD ')) {\n if (currentWorktree) {\n currentWorktree.commit = line.substring('HEAD '.length).trim();\n }\n } else if (line.startsWith('branch ')) {\n if (currentWorktree) {\n const branchRef = line.substring('branch '.length).trim();\n // Extract branch name from refs/heads/branch-name\n currentWorktree.branch = branchRef.replace('refs/heads/', '');\n }\n } else if (line.startsWith('bare')) {\n if (currentWorktree) {\n currentWorktree.isMain = true;\n }\n } else if (line.startsWith('locked ')) {\n if (currentWorktree) {\n currentWorktree.isLocked = true;\n currentWorktree.lockReason = line.substring('locked '.length).trim();\n }\n }\n }\n\n // Add the last worktree\n if (currentWorktree && currentWorktree.path) {\n worktrees.push(this.finalizeWorktreeInfo(currentWorktree));\n }\n\n return worktrees;\n }\n\n /**\n * Finalize worktree info with defaults\n *\n * @param partial - Partial worktree info\n * @returns Complete WorktreeInfo\n */\n private finalizeWorktreeInfo(partial: Partial<WorktreeInfo>): WorktreeInfo {\n return {\n path: partial.path || '',\n branch: partial.branch || '(detached)',\n commit: partial.commit || '',\n isMain: partial.isMain || false,\n isLocked: partial.isLocked || false,\n lockReason: partial.lockReason,\n };\n }\n\n async createBranch(\n repoPath: string,\n branchName: string,\n baseBranchOrCommit: string\n ): Promise<void> {\n const escapedBranch = this.escapeShellArg(branchName);\n const escapedBase = this.escapeShellArg(baseBranchOrCommit);\n\n const command = `git branch ${escapedBranch} ${escapedBase}`;\n this.execGit(command, repoPath);\n }\n\n async deleteBranch(\n repoPath: string,\n branchName: string,\n force = false\n ): Promise<void> {\n const escapedBranch = this.escapeShellArg(branchName);\n const flag = force ? '-D' : '-d';\n\n const command = `git branch ${flag} ${escapedBranch}`;\n this.execGit(command, repoPath);\n }\n\n async configureSparseCheckout(\n worktreePath: string,\n patterns: string[]\n ): Promise<void> {\n // Enable sparse-checkout\n this.execGit('git sparse-checkout init --cone', worktreePath);\n\n // Set patterns\n const escapedPatterns = patterns.map((p) => this.escapeShellArg(p)).join(' ');\n const command = `git sparse-checkout set ${escapedPatterns}`;\n this.execGit(command, worktreePath);\n }\n\n async isValidRepo(repoPath: string): Promise<boolean> {\n try {\n this.execGit('git rev-parse --git-dir', repoPath);\n return true;\n } catch (error) {\n return false;\n }\n }\n\n async listBranches(repoPath: string): Promise<string[]> {\n const output = this.execGit(\n `git branch --list --all --format='%(refname:short)'`,\n repoPath\n );\n\n return output\n .split('\\n')\n .map((line) => line.trim())\n .filter((line) => line.length > 0)\n .map((branch) => {\n // Remove 'origin/' prefix if present for remote branches\n // This gives us both local and remote branch names in a consistent format\n return branch.replace(/^remotes\\/origin\\//, '');\n });\n }\n\n async getCurrentCommit(repoPath: string): Promise<string> {\n const output = this.execGit('git rev-parse HEAD', repoPath);\n return output.trim();\n }\n}\n", "/**\n * Worktree Configuration\n *\n * Loads and validates worktree configuration from .sudocode/config.json\n *\n * @module execution/worktree/config\n */\n\nimport fs from 'fs';\nimport path from 'path';\nimport type { WorktreeConfig } from './types.js';\n\n/**\n * Raw configuration structure from .sudocode/config.json\n */\ninterface RawConfig {\n worktree?: Partial<WorktreeConfig>;\n [key: string]: unknown;\n}\n\n/**\n * Validation result\n */\ninterface ValidationResult {\n config: WorktreeConfig;\n warnings: string[];\n}\n\n/**\n * Default worktree configuration\n */\nexport const DEFAULT_WORKTREE_CONFIG: WorktreeConfig = {\n worktreeStoragePath: '.sudocode/worktrees',\n autoCreateBranches: true,\n autoDeleteBranches: false,\n enableSparseCheckout: false,\n sparseCheckoutPatterns: undefined,\n branchPrefix: 'sudocode',\n cleanupOrphanedWorktreesOnStartup: true,\n};\n\n/**\n * Validate and normalize worktree configuration\n *\n * @param rawConfig - Raw configuration object from config.json\n * @returns Validated configuration with warnings\n */\nexport function validateWorktreeConfig(\n rawConfig: Partial<WorktreeConfig>\n): ValidationResult {\n const warnings: string[] = [];\n const config: WorktreeConfig = { ...DEFAULT_WORKTREE_CONFIG };\n\n // Validate worktreeStoragePath\n if (rawConfig.worktreeStoragePath !== undefined) {\n if (typeof rawConfig.worktreeStoragePath === 'string') {\n config.worktreeStoragePath = rawConfig.worktreeStoragePath;\n } else {\n warnings.push(\n `Invalid worktreeStoragePath: must be a string. Using default: ${DEFAULT_WORKTREE_CONFIG.worktreeStoragePath}`\n );\n }\n }\n\n // Validate autoCreateBranches\n if (rawConfig.autoCreateBranches !== undefined) {\n if (typeof rawConfig.autoCreateBranches === 'boolean') {\n config.autoCreateBranches = rawConfig.autoCreateBranches;\n } else {\n warnings.push(\n `Invalid autoCreateBranches: must be a boolean. Using default: ${DEFAULT_WORKTREE_CONFIG.autoCreateBranches}`\n );\n }\n }\n\n // Validate autoDeleteBranches\n if (rawConfig.autoDeleteBranches !== undefined) {\n if (typeof rawConfig.autoDeleteBranches === 'boolean') {\n config.autoDeleteBranches = rawConfig.autoDeleteBranches;\n } else {\n warnings.push(\n `Invalid autoDeleteBranches: must be a boolean. Using default: ${DEFAULT_WORKTREE_CONFIG.autoDeleteBranches}`\n );\n }\n }\n\n // Validate enableSparseCheckout\n if (rawConfig.enableSparseCheckout !== undefined) {\n if (typeof rawConfig.enableSparseCheckout === 'boolean') {\n config.enableSparseCheckout = rawConfig.enableSparseCheckout;\n } else {\n warnings.push(\n `Invalid enableSparseCheckout: must be a boolean. Using default: ${DEFAULT_WORKTREE_CONFIG.enableSparseCheckout}`\n );\n }\n }\n\n // Validate sparseCheckoutPatterns\n if (rawConfig.sparseCheckoutPatterns !== undefined) {\n if (\n Array.isArray(rawConfig.sparseCheckoutPatterns) &&\n rawConfig.sparseCheckoutPatterns.every((p: unknown) => typeof p === 'string')\n ) {\n config.sparseCheckoutPatterns = rawConfig.sparseCheckoutPatterns;\n } else {\n warnings.push(\n 'Invalid sparseCheckoutPatterns: must be an array of strings. Ignoring value.'\n );\n config.sparseCheckoutPatterns = undefined;\n }\n }\n\n // Validate branchPrefix\n if (rawConfig.branchPrefix !== undefined) {\n if (typeof rawConfig.branchPrefix === 'string') {\n // Validate git branch name characters (basic validation)\n if (isValidGitBranchPrefix(rawConfig.branchPrefix)) {\n config.branchPrefix = rawConfig.branchPrefix;\n } else {\n warnings.push(\n `Invalid branchPrefix: contains invalid git branch name characters. Using default: ${DEFAULT_WORKTREE_CONFIG.branchPrefix}`\n );\n }\n } else {\n warnings.push(\n `Invalid branchPrefix: must be a string. Using default: ${DEFAULT_WORKTREE_CONFIG.branchPrefix}`\n );\n }\n }\n\n // Validate cleanupOrphanedWorktreesOnStartup\n if (rawConfig.cleanupOrphanedWorktreesOnStartup !== undefined) {\n if (typeof rawConfig.cleanupOrphanedWorktreesOnStartup === 'boolean') {\n config.cleanupOrphanedWorktreesOnStartup =\n rawConfig.cleanupOrphanedWorktreesOnStartup;\n } else {\n warnings.push(\n `Invalid cleanupOrphanedWorktreesOnStartup: must be a boolean. Using default: ${DEFAULT_WORKTREE_CONFIG.cleanupOrphanedWorktreesOnStartup}`\n );\n }\n }\n\n return { config, warnings };\n}\n\n/**\n * Validate git branch prefix characters\n * Basic validation - checks for common invalid characters\n *\n * @param prefix - Branch prefix to validate\n * @returns True if valid\n */\nfunction isValidGitBranchPrefix(prefix: string): boolean {\n // Git branch names cannot contain: .., @{, \\, ^, ~, :, ?, *, [, space, or control characters\n // Also cannot start with / or end with .lock\n const invalidPatterns = [\n /\\.\\./,\n /@\\{/,\n /\\\\/,\n /\\^/,\n /~/,\n /:/,\n /\\?/,\n /\\*/,\n /\\[/,\n /\\s/,\n // eslint-disable-next-line no-control-regex\n /[\\x00-\\x1f\\x7f]/,\n ];\n\n if (prefix.startsWith('/') || prefix.endsWith('.lock')) {\n return false;\n }\n\n return !invalidPatterns.some((pattern) => pattern.test(prefix));\n}\n\n/**\n * Load worktree configuration from .sudocode/config.json\n *\n * @param projectRoot - Path to project root (default: current working directory)\n * @returns Validated configuration with warnings\n */\nexport function loadWorktreeConfig(\n projectRoot: string = process.cwd()\n): ValidationResult {\n const configPath = path.join(projectRoot, '.sudocode', 'config.json');\n\n // If config file doesn't exist, return defaults\n if (!fs.existsSync(configPath)) {\n return {\n config: DEFAULT_WORKTREE_CONFIG,\n warnings: [\n `Config file not found at ${configPath}. Using default configuration.`,\n ],\n };\n }\n\n try {\n const rawFileContent = fs.readFileSync(configPath, 'utf-8');\n const rawConfig: RawConfig = JSON.parse(rawFileContent);\n\n // Extract worktree section\n const worktreeConfig = rawConfig.worktree || {};\n\n // Validate and return\n return validateWorktreeConfig(worktreeConfig);\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n return {\n config: DEFAULT_WORKTREE_CONFIG,\n warnings: [\n `Failed to load config from ${configPath}: ${errorMessage}. Using default configuration.`,\n ],\n };\n }\n}\n\n/**\n * Singleton instance of worktree configuration\n */\nlet cachedConfig: WorktreeConfig | null = null;\nlet cachedProjectRoot: string | null = null;\n\n/**\n * Get worktree configuration (singleton pattern)\n * Caches the configuration per project root\n *\n * @param projectRoot - Path to project root (default: current working directory)\n * @param forceReload - Force reload from disk\n * @returns Worktree configuration\n */\nexport function getWorktreeConfig(\n projectRoot: string = process.cwd(),\n forceReload = false\n): WorktreeConfig {\n // Return cached config if same project root and not forcing reload\n if (!forceReload && cachedConfig && cachedProjectRoot === projectRoot) {\n return cachedConfig;\n }\n\n // Load and validate configuration\n const { config, warnings } = loadWorktreeConfig(projectRoot);\n\n // Log warnings\n if (warnings.length > 0) {\n console.warn('[Worktree Config] Configuration warnings:');\n warnings.forEach((warning) => console.warn(` - ${warning}`));\n }\n\n // Cache the configuration\n cachedConfig = config;\n cachedProjectRoot = projectRoot;\n\n return config;\n}\n\n/**\n * Clear the cached configuration (useful for testing)\n */\nexport function clearWorktreeConfigCache(): void {\n cachedConfig = null;\n cachedProjectRoot = null;\n}\n\n/**\n * Update worktree configuration in .sudocode/config.json\n * Preserves other configuration sections\n *\n * @param updates - Partial configuration to update\n * @param projectRoot - Path to project root (default: current working directory)\n * @returns Validation result with updated config\n * @throws Error if unable to write config file\n */\nexport function updateWorktreeConfig(\n updates: Partial<WorktreeConfig>,\n projectRoot: string = process.cwd()\n): ValidationResult {\n const configPath = path.join(projectRoot, '.sudocode', 'config.json');\n\n // Load existing config (or create new one)\n let existingConfig: RawConfig = {};\n\n if (fs.existsSync(configPath)) {\n try {\n const rawFileContent = fs.readFileSync(configPath, 'utf-8');\n existingConfig = JSON.parse(rawFileContent);\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n throw new Error(\n `Failed to read existing config from ${configPath}: ${errorMessage}`\n );\n }\n } else {\n // Create .sudocode directory if it doesn't exist\n const sudocodeDir = path.dirname(configPath);\n if (!fs.existsSync(sudocodeDir)) {\n fs.mkdirSync(sudocodeDir, { recursive: true });\n }\n }\n\n // Merge updates with existing worktree config\n const mergedWorktreeConfig = {\n ...(existingConfig.worktree || {}),\n ...updates,\n };\n\n // Validate merged config\n const validationResult = validateWorktreeConfig(mergedWorktreeConfig);\n\n // Update config object\n existingConfig.worktree = validationResult.config;\n\n // Write to file\n try {\n fs.writeFileSync(configPath, JSON.stringify(existingConfig, null, 2), 'utf-8');\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n throw new Error(`Failed to write config to ${configPath}: ${errorMessage}`);\n }\n\n // Invalidate cache\n if (cachedProjectRoot === projectRoot) {\n cachedConfig = null;\n cachedProjectRoot = null;\n }\n\n return validationResult;\n}\n\n/**\n * Set a single worktree configuration property\n *\n * @param key - Configuration key to set\n * @param value - Value to set\n * @param projectRoot - Path to project root (default: current working directory)\n * @returns Validation result with updated config\n */\nexport function setWorktreeConfigProperty<K extends keyof WorktreeConfig>(\n key: K,\n value: WorktreeConfig[K],\n projectRoot: string = process.cwd()\n): ValidationResult {\n return updateWorktreeConfig({ [key]: value }, projectRoot);\n}\n\n/**\n * Reset worktree configuration to defaults\n *\n * @param projectRoot - Path to project root (default: current working directory)\n * @returns Validation result with default config\n */\nexport function resetWorktreeConfig(\n projectRoot: string = process.cwd()\n): ValidationResult {\n return updateWorktreeConfig(DEFAULT_WORKTREE_CONFIG, projectRoot);\n}\n", "/**\n * Executions service - database operations for agent executions\n */\n\nimport type Database from \"better-sqlite3\";\nimport type { Execution, AgentType, ExecutionStatus } from \"@sudocode-ai/types\";\nimport { randomUUID } from \"crypto\";\n\n/**\n * Input for creating a new execution\n */\nexport interface CreateExecutionInput {\n id?: string; // Optional, auto-generated if not provided\n issue_id: string | null; // Optional, can be null for executions not tied to an issue\n agent_type: AgentType;\n mode?: string; // Execution mode ('worktree' | 'local')\n prompt?: string; // Rendered prompt\n config?: string; // JSON string of execution configuration\n before_commit?: string;\n target_branch: string; // Required for worktree integration\n branch_name: string; // Required for worktree integration\n worktree_path?: string; // Optional, set after worktree creation\n}\n\n/**\n * Input for updating an execution\n */\nexport interface UpdateExecutionInput {\n status?: ExecutionStatus;\n completed_at?: string | null;\n exit_code?: number | null;\n error_message?: string | null;\n after_commit?: string | null;\n target_branch?: string | null;\n worktree_path?: string | null;\n session_id?: string | null;\n summary?: string | null;\n}\n\n/**\n * Create a new execution\n */\nexport function createExecution(\n db: Database.Database,\n input: CreateExecutionInput\n): Execution {\n const id = input.id || randomUUID();\n const now = new Date().toISOString();\n\n // Get issue_uuid if issue_id is provided\n let issue_uuid: string | null = null;\n if (input.issue_id) {\n const issue = db\n .prepare(`SELECT uuid FROM issues WHERE id = ?`)\n .get(input.issue_id) as { uuid: string } | undefined;\n if (issue) {\n issue_uuid = issue.uuid;\n }\n }\n\n const stmt = db.prepare(`\n INSERT INTO executions (\n id,\n issue_id,\n issue_uuid,\n agent_type,\n mode,\n prompt,\n config,\n status,\n started_at,\n before_commit,\n target_branch,\n branch_name,\n worktree_path,\n created_at,\n updated_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n stmt.run(\n id,\n input.issue_id,\n issue_uuid,\n input.agent_type,\n input.mode || null,\n input.prompt || null,\n input.config || null,\n \"running\" as ExecutionStatus,\n now,\n input.before_commit || null,\n input.target_branch,\n input.branch_name,\n input.worktree_path || null,\n now,\n now\n );\n\n const execution = getExecution(db, id);\n if (!execution) {\n throw new Error(`Failed to create execution with id ${id}`);\n }\n\n return execution;\n}\n\n/**\n * Get an execution by ID\n */\nexport function getExecution(\n db: Database.Database,\n id: string\n): Execution | null {\n const stmt = db.prepare(`\n SELECT * FROM executions WHERE id = ?\n `);\n\n const row = stmt.get(id) as Execution | undefined;\n return row || null;\n}\n\n/**\n * Get all executions for an issue\n */\nexport function getExecutionsByIssueId(\n db: Database.Database,\n issue_id: string\n): Execution[] {\n const stmt = db.prepare(`\n SELECT * FROM executions\n WHERE issue_id = ?\n ORDER BY started_at DESC\n `);\n\n return stmt.all(issue_id) as Execution[];\n}\n\n/**\n * Update an execution\n */\nexport function updateExecution(\n db: Database.Database,\n id: string,\n input: UpdateExecutionInput\n): Execution {\n const execution = getExecution(db, id);\n if (!execution) {\n throw new Error(`Execution not found: ${id}`);\n }\n\n const updates: string[] = [];\n const values: any[] = [];\n\n if (input.status !== undefined) {\n updates.push(\"status = ?\");\n values.push(input.status);\n }\n\n if (input.completed_at !== undefined) {\n updates.push(\"completed_at = ?\");\n values.push(input.completed_at);\n }\n\n if (input.exit_code !== undefined) {\n updates.push(\"exit_code = ?\");\n values.push(input.exit_code);\n }\n\n if (input.error_message !== undefined) {\n updates.push(\"error_message = ?\");\n values.push(input.error_message);\n }\n\n if (input.after_commit !== undefined) {\n updates.push(\"after_commit = ?\");\n values.push(input.after_commit);\n }\n\n if (input.target_branch !== undefined) {\n updates.push(\"target_branch = ?\");\n values.push(input.target_branch);\n }\n\n if (input.worktree_path !== undefined) {\n updates.push(\"worktree_path = ?\");\n values.push(input.worktree_path);\n }\n\n if (input.session_id !== undefined) {\n updates.push(\"session_id = ?\");\n values.push(input.session_id);\n }\n\n if (input.summary !== undefined) {\n updates.push(\"summary = ?\");\n values.push(input.summary);\n }\n\n // Always update updated_at\n updates.push(\"updated_at = ?\");\n values.push(new Date().toISOString());\n\n if (updates.length === 1) {\n // Only updated_at, no other changes\n return execution;\n }\n\n values.push(id);\n\n const stmt = db.prepare(`\n UPDATE executions\n SET ${updates.join(\", \")}\n WHERE id = ?\n `);\n\n stmt.run(...values);\n\n const updated = getExecution(db, id);\n if (!updated) {\n throw new Error(`Failed to update execution ${id}`);\n }\n\n return updated;\n}\n\n/**\n * Delete an execution\n */\nexport function deleteExecution(db: Database.Database, id: string): boolean {\n const stmt = db.prepare(`\n DELETE FROM executions WHERE id = ?\n `);\n\n const result = stmt.run(id);\n return result.changes > 0;\n}\n\n/**\n * Get all executions with optional status filter\n */\nexport function getAllExecutions(\n db: Database.Database,\n status?: ExecutionStatus\n): Execution[] {\n if (status) {\n const stmt = db.prepare(`\n SELECT * FROM executions\n WHERE status = ?\n ORDER BY started_at DESC\n `);\n return stmt.all(status) as Execution[];\n } else {\n const stmt = db.prepare(`\n SELECT * FROM executions\n ORDER BY started_at DESC\n `);\n return stmt.all() as Execution[];\n }\n}\n", "/**\n * Execution Service\n *\n * High-level service for managing issue-to-execution transformations.\n * Coordinates between template rendering, worktree management, and workflow execution.\n *\n * @module services/execution-service\n */\n\nimport type Database from \"better-sqlite3\";\nimport type { Execution } from \"@sudocode-ai/types\";\nimport { PromptTemplateEngine } from \"./prompt-template-engine.js\";\nimport { ExecutionLifecycleService } from \"./execution-lifecycle.js\";\nimport {\n createExecution,\n getExecution,\n updateExecution,\n} from \"./executions.js\";\nimport { getDefaultTemplate, getTemplateById } from \"./prompt-templates.js\";\nimport { randomUUID } from \"crypto\";\nimport { SimpleProcessManager } from \"../execution/process/simple-manager.js\";\nimport { SimpleExecutionEngine } from \"../execution/engine/simple-engine.js\";\nimport { ResilientExecutor } from \"../execution/resilience/resilient-executor.js\";\nimport { LinearOrchestrator } from \"../execution/workflow/linear-orchestrator.js\";\nimport type { WorkflowDefinition } from \"../execution/workflow/types.js\";\nimport { createAgUiSystem } from \"../execution/output/ag-ui-integration.js\";\nimport type { AgUiEventAdapter } from \"../execution/output/ag-ui-adapter.js\";\nimport type { TransportManager } from \"../execution/transport/transport-manager.js\";\n\n/**\n * Configuration for creating an execution\n */\nexport interface ExecutionConfig {\n mode?: \"worktree\" | \"local\";\n model?: string;\n timeout?: number;\n baseBranch?: string;\n branchName?: string;\n checkpointInterval?: number;\n continueOnStepFailure?: boolean;\n captureFileChanges?: boolean;\n captureToolCalls?: boolean;\n}\n\n/**\n * Template variable context for rendering\n */\nexport interface TemplateContext {\n issueId: string;\n title: string;\n description: string;\n relatedSpecs?: Array<{ id: string; title: string }>;\n feedback?: Array<{ issueId: string; content: string }>;\n}\n\n/**\n * Result from prepareExecution - preview before starting\n */\nexport interface ExecutionPrepareResult {\n renderedPrompt: string;\n issue: {\n id: string;\n title: string;\n content: string;\n };\n relatedSpecs: Array<{ id: string; title: string }>;\n defaultConfig: ExecutionConfig;\n warnings?: string[];\n errors?: string[];\n}\n\n/**\n * ExecutionService\n *\n * Manages the full lifecycle of issue-based executions:\n * - Preparing execution with template rendering\n * - Creating and starting executions with worktree isolation\n * - Creating follow-up executions that reuse worktrees\n * - Canceling and cleaning up executions\n */\nexport class ExecutionService {\n private db: Database.Database;\n private templateEngine: PromptTemplateEngine;\n private lifecycleService: ExecutionLifecycleService;\n private repoPath: string;\n private transportManager?: TransportManager;\n private activeOrchestrators = new Map<string, LinearOrchestrator>();\n\n /**\n * Create a new ExecutionService\n *\n * @param db - Database instance\n * @param repoPath - Path to the git repository\n * @param lifecycleService - Optional execution lifecycle service (creates one if not provided)\n * @param transportManager - Optional transport manager for SSE streaming\n */\n constructor(\n db: Database.Database,\n repoPath: string,\n lifecycleService?: ExecutionLifecycleService,\n transportManager?: TransportManager\n ) {\n this.db = db;\n this.repoPath = repoPath;\n this.templateEngine = new PromptTemplateEngine();\n this.lifecycleService =\n lifecycleService || new ExecutionLifecycleService(db, repoPath);\n this.transportManager = transportManager;\n }\n\n /**\n * Prepare execution - load issue, render template, return preview\n *\n * This method loads the issue and related context, renders the template,\n * and returns a preview for the user to review before starting execution.\n *\n * @param issueId - ID of issue to prepare execution for\n * @param options - Optional template and config overrides\n * @returns Execution prepare result with rendered prompt and context\n */\n async prepareExecution(\n issueId: string,\n options?: {\n templateId?: string;\n config?: Partial<ExecutionConfig>;\n }\n ): Promise<ExecutionPrepareResult> {\n // 1. Load issue\n const issue = this.db\n .prepare(\"SELECT * FROM issues WHERE id = ?\")\n .get(issueId) as\n | { id: string; title: string; content: string }\n | undefined;\n\n if (!issue) {\n throw new Error(`Issue ${issueId} not found`);\n }\n\n // 2. Load related specs (via implements/references relationships)\n const relatedSpecs = this.db\n .prepare(\n `\n SELECT DISTINCT s.id, s.title\n FROM specs s\n JOIN relationships r ON r.to_id = s.id AND r.to_type = 'spec'\n WHERE r.from_id = ? AND r.from_type = 'issue'\n AND r.relationship_type IN ('implements', 'references')\n ORDER BY s.title\n `\n )\n .all(issueId) as Array<{ id: string; title: string }>;\n\n // 3. Build context for template rendering\n const context: TemplateContext = {\n issueId: issue.id,\n title: issue.title,\n description: issue.content,\n relatedSpecs:\n relatedSpecs.length > 0\n ? relatedSpecs.map((s) => ({\n id: s.id,\n title: s.title,\n }))\n : undefined,\n };\n\n // 4. Get template (use custom template if provided, otherwise default)\n let template: string;\n if (options?.templateId) {\n const customTemplate = getTemplateById(this.db, options.templateId);\n if (!customTemplate) {\n throw new Error(`Template ${options.templateId} not found`);\n }\n template = customTemplate.template;\n } else {\n const defaultTemplate = getDefaultTemplate(this.db, \"issue\");\n if (!defaultTemplate) {\n throw new Error(\"Default issue template not found\");\n }\n template = defaultTemplate.template;\n }\n\n // 5. Render template\n const renderedPrompt = this.templateEngine.render(template, context);\n\n // 6. Get default config\n const defaultConfig: ExecutionConfig = {\n mode: \"worktree\",\n model: \"claude-sonnet-4\",\n baseBranch: \"main\",\n checkpointInterval: 1,\n continueOnStepFailure: false,\n captureFileChanges: true,\n captureToolCalls: true,\n ...options?.config,\n };\n\n // 7. Validate\n const warnings: string[] = [];\n const errors: string[] = [];\n\n if (!renderedPrompt.trim()) {\n errors.push(\"Rendered prompt is empty\");\n }\n\n return {\n renderedPrompt,\n issue: {\n id: issue.id,\n title: issue.title,\n content: issue.content,\n },\n relatedSpecs,\n defaultConfig,\n warnings,\n errors,\n };\n }\n\n /**\n * Create and start execution\n *\n * Creates an execution record, sets up worktree (if needed), and starts\n * workflow execution. Returns the execution record immediately while\n * workflow runs in the background.\n *\n * @param issueId - ID of issue to execute\n * @param config - Execution configuration\n * @param prompt - Rendered prompt to execute\n * @returns Created execution record\n */\n async createExecution(\n issueId: string,\n config: ExecutionConfig,\n prompt: string\n ): Promise<Execution> {\n // 1. Validate\n if (!prompt.trim()) {\n throw new Error(\"Prompt cannot be empty\");\n }\n\n const issue = this.db\n .prepare(\"SELECT * FROM issues WHERE id = ?\")\n .get(issueId) as { id: string; title: string } | undefined;\n\n if (!issue) {\n throw new Error(`Issue ${issueId} not found`);\n }\n\n // 2. Determine execution mode and create execution with worktree\n const mode = config.mode || \"worktree\";\n let execution: Execution;\n let workDir: string;\n\n if (mode === \"worktree\") {\n // Create execution with isolated worktree\n const result = await this.lifecycleService.createExecutionWithWorktree({\n issueId,\n issueTitle: issue.title,\n agentType: \"claude-code\",\n targetBranch: config.baseBranch || \"main\",\n repoPath: this.repoPath,\n mode: mode,\n prompt: prompt,\n config: JSON.stringify(config),\n });\n\n execution = result.execution;\n workDir = result.worktreePath;\n } else {\n // Local mode - create execution without worktree\n const executionId = randomUUID();\n execution = createExecution(this.db, {\n id: executionId,\n issue_id: issueId,\n agent_type: \"claude-code\",\n mode: mode,\n prompt: prompt,\n config: JSON.stringify(config),\n target_branch: config.baseBranch || \"main\",\n branch_name: config.baseBranch || \"main\",\n });\n workDir = this.repoPath;\n }\n\n // 3. Build WorkflowDefinition\n const workflow: WorkflowDefinition = {\n id: `workflow-${execution.id}`,\n steps: [\n {\n id: \"execute-issue\",\n taskType: \"issue\",\n prompt,\n taskConfig: {\n model: config.model || \"claude-sonnet-4\",\n timeout: config.timeout,\n captureFileChanges: config.captureFileChanges ?? true,\n captureToolCalls: config.captureToolCalls ?? true,\n },\n },\n ],\n config: {\n checkpointInterval: config.checkpointInterval ?? 1,\n continueOnStepFailure: config.continueOnStepFailure ?? false,\n timeout: config.timeout,\n },\n metadata: {\n workDir,\n issueId,\n executionId: execution.id,\n },\n };\n\n // 4. Create execution engine stack\n const processManager = new SimpleProcessManager({\n executablePath: \"claude\",\n args: [\n \"--print\",\n \"--output-format\",\n \"stream-json\",\n \"--dangerously-skip-permissions\",\n ],\n });\n\n let engine = new SimpleExecutionEngine(processManager, {\n maxConcurrent: 1, // One task at a time for issue execution\n });\n\n let executor = new ResilientExecutor(engine);\n\n // 5. Create AG-UI system (processor + adapter) if transport manager is available\n let agUiAdapter: AgUiEventAdapter | undefined;\n if (this.transportManager) {\n const agUiSystem = createAgUiSystem(execution.id);\n agUiAdapter = agUiSystem.adapter;\n\n // Connect adapter to transport for SSE streaming\n this.transportManager.connectAdapter(agUiAdapter, execution.id);\n\n // Connect processor to execution engine for real-time output parsing\n // Buffer for incomplete lines (stream-json can split mid-line)\n let lineBuffer = \"\";\n\n engine = new SimpleExecutionEngine(processManager, {\n maxConcurrent: 1,\n // TODO: Factor out this logic for DRY principles.\n onOutput: (data, type) => {\n if (type === \"stdout\") {\n // Append new data to buffer\n lineBuffer += data.toString();\n\n // Process complete lines (ending with \\n)\n let newlineIndex;\n while ((newlineIndex = lineBuffer.indexOf(\"\\n\")) !== -1) {\n const line = lineBuffer.slice(0, newlineIndex);\n lineBuffer = lineBuffer.slice(newlineIndex + 1);\n\n if (line.trim()) {\n agUiSystem.processor.processLine(line).catch((err) => {\n console.error(\n \"[ExecutionService] Error processing output line:\",\n {\n error: err instanceof Error ? err.message : String(err),\n line: line.slice(0, 100), // Log first 100 chars for debugging\n }\n );\n });\n }\n }\n }\n },\n });\n executor = new ResilientExecutor(engine);\n }\n\n // 6. Create LinearOrchestrator\n const orchestrator = new LinearOrchestrator(\n executor,\n undefined, // No storage/checkpointing for now\n agUiAdapter,\n this.lifecycleService\n );\n\n // 7. Register event handlers to update execution status in database\n orchestrator.onWorkflowStart(() => {\n try {\n updateExecution(this.db, execution.id, {\n status: \"running\",\n });\n } catch (error) {\n console.error(\n \"[ExecutionService] Failed to update execution status to running\",\n {\n executionId: execution.id,\n error: error instanceof Error ? error.message : String(error),\n }\n );\n }\n });\n\n orchestrator.onWorkflowComplete(() => {\n console.log(\"[ExecutionService] Workflow completed successfully\", {\n executionId: execution.id,\n });\n try {\n updateExecution(this.db, execution.id, {\n status: \"completed\",\n completed_at: new Date().toISOString(),\n });\n } catch (error) {\n console.error(\n \"[ExecutionService] Failed to update execution status to completed\",\n {\n executionId: execution.id,\n error: error instanceof Error ? error.message : String(error),\n note: \"Execution may have been deleted (e.g., due to CASCADE DELETE from issue deletion)\",\n }\n );\n }\n // Remove orchestrator from active map\n this.activeOrchestrators.delete(execution.id);\n });\n\n orchestrator.onWorkflowFailed((_executionId, error) => {\n console.error(\"[ExecutionService] Workflow failed\", {\n executionId: execution.id,\n error: error.message,\n stack: error.stack,\n });\n try {\n updateExecution(this.db, execution.id, {\n status: \"failed\",\n completed_at: new Date().toISOString(),\n error_message: error.message,\n });\n } catch (updateError) {\n console.error(\n \"[ExecutionService] Failed to update execution status to failed\",\n {\n executionId: execution.id,\n error:\n updateError instanceof Error\n ? updateError.message\n : String(updateError),\n note: \"Execution may have been deleted (e.g., due to CASCADE DELETE from issue deletion)\",\n }\n );\n }\n // Remove orchestrator from active map\n this.activeOrchestrators.delete(execution.id);\n });\n\n // 8. Start workflow execution (non-blocking)\n orchestrator.startWorkflow(workflow, workDir, {\n checkpointInterval: config.checkpointInterval,\n executionId: execution.id,\n });\n\n // 9. Store orchestrator for later cancellation\n this.activeOrchestrators.set(execution.id, orchestrator);\n\n return execution;\n }\n\n /**\n * Create follow-up execution - reuse worktree from previous execution\n *\n * Creates a new execution that reuses the worktree from a previous execution,\n * appending feedback or additional context to the prompt.\n *\n * @param executionId - ID of previous execution to follow up on\n * @param feedback - Additional feedback/context to append to prompt\n * @returns Created follow-up execution record\n */\n async createFollowUp(\n executionId: string,\n feedback: string\n ): Promise<Execution> {\n // 1. Get previous execution\n const prevExecution = getExecution(this.db, executionId);\n if (!prevExecution) {\n throw new Error(`Execution ${executionId} not found`);\n }\n\n if (!prevExecution.worktree_path) {\n throw new Error(\n `Cannot create follow-up: execution ${executionId} has no worktree`\n );\n }\n\n // Check if worktree still exists on filesystem, recreate if needed\n if (this.lifecycleService) {\n const fs = await import(\"fs\");\n const worktreeExists = fs.existsSync(prevExecution.worktree_path);\n\n if (!worktreeExists) {\n console.log(\n `Recreating worktree for follow-up execution: ${prevExecution.worktree_path}`\n );\n\n // Recreate the worktree using the same path and branch\n const worktreeManager = (this.lifecycleService as any).worktreeManager;\n await worktreeManager.createWorktree({\n repoPath: this.repoPath,\n branchName: prevExecution.branch_name,\n worktreePath: prevExecution.worktree_path,\n baseBranch: prevExecution.target_branch,\n createBranch: false, // Branch already exists, just recreate worktree\n });\n }\n }\n\n // 2. Validate that previous execution has an issue_id\n if (!prevExecution.issue_id) {\n throw new Error(\"Previous execution must have an issue_id for follow-up\");\n }\n\n // 3. Prepare execution to get rendered prompt\n const prepareResult = await this.prepareExecution(prevExecution.issue_id);\n\n // 4. Append feedback to prompt\n const followUpPrompt = `${prepareResult.renderedPrompt}\n\n## Follow-up Feedback\n${feedback}\n\nPlease continue working on this issue, taking into account the feedback above.`;\n\n // 5. Create new execution record that references previous execution\n const newExecutionId = randomUUID();\n const newExecution = createExecution(this.db, {\n id: newExecutionId,\n issue_id: prevExecution.issue_id,\n agent_type: \"claude-code\",\n target_branch: prevExecution.target_branch,\n branch_name: prevExecution.branch_name,\n // TODO: Handle case where worktree has been deleted.\n worktree_path: prevExecution.worktree_path, // Reuse same worktree\n config: prevExecution.config || undefined, // Preserve config (including cleanupMode) from previous execution\n });\n\n // 5. Build WorkflowDefinition\n const workflow: WorkflowDefinition = {\n id: `workflow-${newExecution.id}`,\n steps: [\n {\n id: \"execute-followup\",\n taskType: \"issue\",\n prompt: followUpPrompt,\n taskConfig: {\n model: \"claude-sonnet-4\",\n captureFileChanges: true,\n captureToolCalls: true,\n },\n },\n ],\n config: {\n checkpointInterval: 1,\n continueOnStepFailure: false,\n },\n metadata: {\n workDir: prevExecution.worktree_path,\n issueId: prevExecution.issue_id,\n executionId: newExecution.id,\n followUpOf: executionId,\n },\n };\n\n // 6. Create execution engine stack\n const processManager = new SimpleProcessManager({\n executablePath: \"claude\",\n args: [\n \"--print\",\n \"--output-format\",\n \"stream-json\",\n \"--dangerously-skip-permissions\",\n ],\n });\n\n let engine = new SimpleExecutionEngine(processManager, {\n maxConcurrent: 1,\n });\n\n let executor = new ResilientExecutor(engine);\n\n // 7. Create AG-UI system (processor + adapter) if transport manager is available\n let agUiAdapter: AgUiEventAdapter | undefined;\n if (this.transportManager) {\n const agUiSystem = createAgUiSystem(newExecution.id);\n agUiAdapter = agUiSystem.adapter;\n this.transportManager.connectAdapter(agUiAdapter, newExecution.id);\n\n // Connect processor to execution engine for real-time output parsing\n // Buffer for incomplete lines (stream-json can split mid-line)\n let lineBuffer = \"\";\n\n engine = new SimpleExecutionEngine(processManager, {\n maxConcurrent: 1,\n // TODO: Factor out this logic for DRY principles.\n onOutput: (data, type) => {\n if (type === \"stdout\") {\n // Append new data to buffer\n lineBuffer += data.toString();\n\n // Process complete lines (ending with \\n)\n let newlineIndex;\n while ((newlineIndex = lineBuffer.indexOf(\"\\n\")) !== -1) {\n const line = lineBuffer.slice(0, newlineIndex);\n lineBuffer = lineBuffer.slice(newlineIndex + 1);\n\n if (line.trim()) {\n agUiSystem.processor.processLine(line).catch((err) => {\n console.error(\n \"[ExecutionService] Error processing output line:\",\n {\n error: err instanceof Error ? err.message : String(err),\n line: line.slice(0, 100), // Log first 100 chars for debugging\n }\n );\n });\n }\n }\n }\n },\n });\n executor = new ResilientExecutor(engine);\n }\n\n // 8. Create LinearOrchestrator\n const orchestrator = new LinearOrchestrator(\n executor,\n undefined,\n agUiAdapter,\n this.lifecycleService\n );\n\n // 9. Register event handlers\n orchestrator.onWorkflowStart(() => {\n try {\n updateExecution(this.db, newExecution.id, {\n status: \"running\",\n });\n } catch (error) {\n console.error(\n \"[ExecutionService] Failed to update follow-up execution status to running\",\n {\n executionId: newExecution.id,\n error: error instanceof Error ? error.message : String(error),\n }\n );\n }\n });\n\n orchestrator.onWorkflowComplete(() => {\n try {\n updateExecution(this.db, newExecution.id, {\n status: \"completed\",\n completed_at: new Date().toISOString(),\n });\n } catch (error) {\n console.error(\n \"[ExecutionService] Failed to update follow-up execution status to completed\",\n {\n executionId: newExecution.id,\n error: error instanceof Error ? error.message : String(error),\n }\n );\n }\n this.activeOrchestrators.delete(newExecution.id);\n });\n\n orchestrator.onWorkflowFailed((_execId, error) => {\n try {\n updateExecution(this.db, newExecution.id, {\n status: \"failed\",\n completed_at: new Date().toISOString(),\n error_message: error.message,\n });\n } catch (updateError) {\n console.error(\n \"[ExecutionService] Failed to update follow-up execution status to failed\",\n {\n executionId: newExecution.id,\n error:\n updateError instanceof Error\n ? updateError.message\n : String(updateError),\n }\n );\n }\n this.activeOrchestrators.delete(newExecution.id);\n });\n\n // 10. Start workflow execution (non-blocking)\n orchestrator.startWorkflow(workflow, prevExecution.worktree_path, {\n checkpointInterval: 1,\n executionId: newExecution.id,\n });\n\n // 11. Store orchestrator for later cancellation\n this.activeOrchestrators.set(newExecution.id, orchestrator);\n\n return newExecution;\n }\n\n /**\n * Cancel a running execution\n *\n * Stops the workflow execution and marks the execution as cancelled.\n * Optionally cleans up the worktree based on config.\n *\n * @param executionId - ID of execution to cancel\n */\n async cancelExecution(executionId: string): Promise<void> {\n const execution = getExecution(this.db, executionId);\n if (!execution) {\n throw new Error(`Execution ${executionId} not found`);\n }\n\n if (execution.status !== \"running\") {\n throw new Error(`Cannot cancel execution in ${execution.status} state`);\n }\n\n // Get orchestrator from active map\n const orchestrator = this.activeOrchestrators.get(executionId);\n if (orchestrator) {\n // Cancel via orchestrator\n await orchestrator.cancelWorkflow(executionId);\n // Remove from active map\n this.activeOrchestrators.delete(executionId);\n }\n\n // Update status in database (orchestrator.cancelWorkflow doesn't emit events for DB update)\n updateExecution(this.db, executionId, {\n status: \"stopped\",\n completed_at: new Date().toISOString(),\n });\n }\n\n /**\n * Clean up execution resources\n *\n * Removes the worktree and associated files. This is called automatically\n * on workflow completion, or can be called manually.\n *\n * @param executionId - ID of execution to clean up\n */\n async cleanupExecution(executionId: string): Promise<void> {\n await this.lifecycleService.cleanupExecution(executionId);\n }\n\n /**\n * Check if worktree exists in filesystem for an execution\n *\n * @param executionId - ID of execution to check\n * @returns true if worktree exists, false otherwise\n */\n async worktreeExists(executionId: string): Promise<boolean> {\n const execution = getExecution(this.db, executionId);\n if (!execution || !execution.worktree_path) {\n return false;\n }\n\n const fs = await import(\"fs\");\n return fs.existsSync(execution.worktree_path);\n }\n\n /**\n * Delete worktree for an execution\n *\n * Manually deletes the worktree for a specific execution, regardless of\n * cleanupMode configuration. This allows users to manually cleanup worktrees\n * when they're configured for manual cleanup.\n *\n * @param executionId - ID of execution whose worktree to delete\n * @throws Error if execution not found, has no worktree, or worktree doesn't exist\n */\n async deleteWorktree(executionId: string): Promise<void> {\n const execution = getExecution(this.db, executionId);\n if (!execution) {\n throw new Error(`Execution ${executionId} not found`);\n }\n\n if (!execution.worktree_path) {\n throw new Error(`Execution ${executionId} has no worktree to delete`);\n }\n\n // Check if worktree exists in the filesystem\n const fs = await import(\"fs\");\n const worktreeExists = fs.existsSync(execution.worktree_path);\n\n if (!worktreeExists) {\n throw new Error(\n `Worktree does not exist in filesystem: ${execution.worktree_path}`\n );\n }\n\n // TODO: Cancel any running execution.\n\n // Get worktree manager from lifecycle service\n const worktreeManager = (this.lifecycleService as any).worktreeManager;\n\n // Clean up the worktree\n await worktreeManager.cleanupWorktree(\n execution.worktree_path,\n this.repoPath\n );\n }\n\n /**\n * Shutdown execution service - cancel all active executions\n *\n * This is called during server shutdown to gracefully terminate\n * all running executions before the server exits.\n */\n async shutdown(): Promise<void> {\n const cancelPromises: Promise<void>[] = [];\n\n // Cancel all active orchestrators\n for (const [\n executionId,\n orchestrator,\n ] of this.activeOrchestrators.entries()) {\n cancelPromises.push(\n orchestrator.cancelWorkflow(executionId).catch((error) => {\n console.error(\"[ExecutionService] Error canceling execution\", {\n executionId,\n error: error.message,\n });\n })\n );\n }\n\n // Wait for all cancellations to complete (with timeout)\n await Promise.race([\n Promise.all(cancelPromises),\n new Promise((resolve) => setTimeout(resolve, 5000)), // 5 second timeout\n ]);\n }\n\n /**\n * List all executions for an issue\n *\n * Returns all executions associated with a specific issue,\n * ordered by creation time (most recent first).\n *\n * @param issueId - ID of issue to list executions for\n * @returns Array of executions for the issue\n */\n listExecutions(issueId: string): Execution[] {\n const executions = this.db\n .prepare(\n `\n SELECT * FROM executions\n WHERE issue_id = ?\n ORDER BY created_at DESC\n `\n )\n .all(issueId) as Execution[];\n\n return executions;\n }\n\n /**\n * Get a single execution by ID\n *\n * @param executionId - ID of execution to retrieve\n * @returns Execution or null if not found\n */\n getExecution(executionId: string): Execution | null {\n return getExecution(this.db, executionId);\n }\n}\n", "/**\n * Simple Process Manager Implementation\n *\n * A straightforward implementation of IProcessManager that spawns a fresh\n * process for each task. This is the \"simple first\" approach that will later\n * be upgraded to support process pooling.\n *\n * Supports any CLI tool/agent (Claude Code, Codex, Gemini CLI, etc.)\n *\n * Key features:\n * - One process per task (no pooling)\n * - Event-based I/O streaming\n * - Graceful termination (SIGTERM \u2192 SIGKILL)\n * - Automatic cleanup\n * - Metrics tracking\n * - Agent-agnostic design\n *\n * @module execution/process/simple-manager\n */\n\nimport { spawn } from \"child_process\";\nimport type {\n ManagedProcess,\n ProcessConfig,\n ProcessMetrics,\n OutputHandler,\n ErrorHandler,\n} from \"./types.js\";\nimport type { IProcessManager } from \"./manager.js\";\nimport { generateId } from \"./utils.js\";\n\n/**\n * Simple process manager that spawns one process per task\n *\n * This implementation follows the \"simple first\" principle - it provides\n * a production-ready process manager without the complexity of pooling.\n *\n * Works with any CLI tool/agent by accepting executable path and args.\n *\n * @example\n * ```typescript\n * // Claude Code example\n * const manager = new SimpleProcessManager({\n * executablePath: 'claude',\n * args: ['--print', '--output-format', 'stream-json'],\n * });\n *\n * const process = await manager.acquireProcess({\n * executablePath: 'claude',\n * args: ['--print', '--output-format', 'stream-json'],\n * workDir: '/path/to/project',\n * timeout: 300000,\n * });\n *\n * // Codex example\n * const codexProcess = await manager.acquireProcess({\n * executablePath: 'codex',\n * args: ['--mode', 'agent', '--json'],\n * workDir: '/path/to/project',\n * });\n * ```\n */\nexport class SimpleProcessManager implements IProcessManager {\n private _activeProcesses = new Map<string, ManagedProcess>();\n private _cleanupTimers = new Map<string, NodeJS.Timeout>();\n private _metrics: ProcessMetrics = {\n totalSpawned: 0,\n currentlyActive: 0,\n totalCompleted: 0,\n totalFailed: 0,\n averageDuration: 0,\n };\n\n /**\n * Create a new SimpleProcessManager\n *\n * @param defaultConfig - Default configuration to merge with per-process config\n */\n constructor(private readonly _defaultConfig: Partial<ProcessConfig> = {}) {}\n\n async acquireProcess(config: ProcessConfig): Promise<ManagedProcess> {\n // Merge with default config\n const mergedConfig = { ...this._defaultConfig, ...config };\n\n // Spawn the process\n const childProcess = this.spawnProcess(mergedConfig);\n\n // Validate process spawned successfully\n if (!childProcess.pid) {\n // Suppress error event to prevent uncaughtException\n childProcess.once(\"error\", () => {\n // Error is expected when process fails to spawn\n });\n throw new Error(\"Failed to spawn process: no PID assigned\");\n }\n\n // Generate unique ID for this process\n const id = generateId(\"process\");\n\n // Create managed process object\n const managedProcess: ManagedProcess = {\n id,\n pid: childProcess.pid,\n status: \"busy\",\n spawnedAt: new Date(),\n lastActivity: new Date(),\n exitCode: null,\n signal: null,\n process: childProcess,\n streams: {\n stdout: childProcess.stdout!,\n stderr: childProcess.stderr!,\n stdin: childProcess.stdin!,\n },\n metrics: {\n totalDuration: 0,\n tasksCompleted: 0,\n successRate: 1.0,\n },\n };\n\n // Track the process\n this._activeProcesses.set(id, managedProcess);\n\n // Update metrics\n this._metrics.totalSpawned++;\n this._metrics.currentlyActive++;\n\n // Set up event handlers for lifecycle management\n this.setupProcessHandlers(managedProcess, mergedConfig);\n\n return managedProcess;\n }\n\n /**\n * Spawn a process with the given configuration\n *\n * @param config - Process configuration\n * @returns ChildProcess instance\n */\n private spawnProcess(config: ProcessConfig): ReturnType<typeof spawn> {\n const childProcess = spawn(config.executablePath, config.args, {\n cwd: config.workDir,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env: {\n ...process.env,\n ...config.env,\n },\n });\n\n return childProcess;\n }\n\n /**\n * Set up event handlers for a managed process\n *\n * Handles lifecycle events:\n * - exit: Process terminated normally or abnormally\n * - error: Process encountered an error\n * - stdout/stderr data: Track activity for idle detection\n *\n * @param managedProcess - The managed process to set up handlers for\n * @param config - Process configuration (for timeout)\n */\n private setupProcessHandlers(\n managedProcess: ManagedProcess,\n config: ProcessConfig\n ): void {\n const { process: childProcess, id } = managedProcess;\n let timeoutHandle: NodeJS.Timeout | null = null;\n\n // Set up timeout if configured\n if (config.timeout) {\n timeoutHandle = setTimeout(() => {\n // Terminate process on timeout using graceful termination\n if (managedProcess.status === \"busy\") {\n this.terminateProcess(id).catch(() => {\n // Ignore errors during timeout termination\n });\n }\n }, config.timeout);\n }\n\n // Exit event handler\n childProcess.once(\"exit\", (code, signal) => {\n // Clear timeout\n if (timeoutHandle) {\n clearTimeout(timeoutHandle);\n }\n\n // Update process state\n managedProcess.exitCode = code;\n managedProcess.signal = signal;\n managedProcess.status = code === 0 ? \"completed\" : \"crashed\";\n\n // Calculate duration\n const duration = Date.now() - managedProcess.spawnedAt.getTime();\n managedProcess.metrics.totalDuration = duration;\n\n // Update global metrics\n this._metrics.currentlyActive--;\n if (code === 0) {\n this._metrics.totalCompleted++;\n } else {\n this._metrics.totalFailed++;\n }\n\n // Calculate average duration\n const totalProcesses =\n this._metrics.totalCompleted + this._metrics.totalFailed;\n if (totalProcesses > 0) {\n const currentTotal =\n this._metrics.averageDuration * (totalProcesses - 1);\n this._metrics.averageDuration =\n (currentTotal + duration) / totalProcesses;\n }\n\n // Clean up stdio streams to prevent event loop hang\n managedProcess.streams.stdin.destroy();\n managedProcess.streams.stdout.destroy();\n managedProcess.streams.stderr.destroy();\n\n // Schedule cleanup (delete from activeProcesses after 5s delay)\n const cleanupTimer = setTimeout(() => {\n this._activeProcesses.delete(id);\n this._cleanupTimers.delete(id);\n }, 5000);\n this._cleanupTimers.set(id, cleanupTimer);\n });\n\n // Error event handler\n childProcess.once(\"error\", (error) => {\n void error; // Error is logged but not used here\n\n // Clear timeout\n if (timeoutHandle) {\n clearTimeout(timeoutHandle);\n }\n\n // Update process state\n managedProcess.status = \"crashed\";\n\n // Update global metrics\n this._metrics.currentlyActive--;\n this._metrics.totalFailed++;\n });\n\n // stdout data handler - track activity\n childProcess.stdout?.on(\"data\", () => {\n managedProcess.lastActivity = new Date();\n });\n\n // stderr data handler - track activity\n childProcess.stderr?.on(\"data\", () => {\n managedProcess.lastActivity = new Date();\n });\n }\n\n async releaseProcess(processId: string): Promise<void> {\n await this.terminateProcess(processId);\n }\n\n async terminateProcess(\n processId: string,\n signal: NodeJS.Signals = \"SIGTERM\"\n ): Promise<void> {\n const managed = this._activeProcesses.get(processId);\n if (!managed) {\n return; // Process not found, nothing to terminate\n }\n\n // If already terminated, do nothing (idempotent)\n if (managed.exitCode !== null) {\n return;\n }\n\n // Update status to terminating\n managed.status = \"terminating\";\n\n // Try graceful shutdown first\n managed.process.kill(signal);\n\n // Wait for process to exit (with 2 second timeout)\n const exitPromise = new Promise<void>((resolve) => {\n if (managed.exitCode !== null) {\n resolve();\n } else {\n managed.process.once(\"exit\", () => resolve());\n }\n });\n\n const timeoutPromise = new Promise<void>((resolve) => {\n setTimeout(resolve, 2000);\n });\n\n await Promise.race([exitPromise, timeoutPromise]);\n\n // Force kill if still running\n if (managed.exitCode === null) {\n managed.process.kill(\"SIGKILL\");\n\n // Wait for SIGKILL to take effect (with timeout)\n await Promise.race([\n new Promise<void>((resolve) => {\n if (managed.exitCode !== null) {\n resolve();\n } else {\n managed.process.once(\"exit\", () => resolve());\n }\n }),\n new Promise<void>((resolve) => setTimeout(resolve, 1000)),\n ]);\n }\n }\n\n async sendInput(processId: string, input: string): Promise<void> {\n const managed = this._activeProcesses.get(processId);\n if (!managed) {\n throw new Error(`Process ${processId} not found`);\n }\n\n return new Promise((resolve, reject) => {\n managed.streams.stdin.write(input, (error) => {\n if (error) reject(error);\n else resolve();\n });\n });\n }\n\n /**\n * Close stdin stream for a process\n *\n * Signals EOF to the process, useful for programs that wait for stdin to close.\n *\n * @param processId - ID of the process\n */\n closeInput(processId: string): void {\n const managed = this._activeProcesses.get(processId);\n if (!managed) {\n throw new Error(`Process ${processId} not found`);\n }\n\n managed.streams.stdin.end();\n }\n\n onOutput(processId: string, handler: OutputHandler): void {\n const managed = this._activeProcesses.get(processId);\n if (!managed) {\n throw new Error(`Process ${processId} not found`);\n }\n\n managed.streams.stdout.on(\"data\", (data: Buffer) => {\n handler(data, \"stdout\");\n });\n\n managed.streams.stderr.on(\"data\", (data: Buffer) => {\n handler(data, \"stderr\");\n });\n }\n\n onError(processId: string, handler: ErrorHandler): void {\n const managed = this._activeProcesses.get(processId);\n if (!managed) {\n throw new Error(`Process ${processId} not found`);\n }\n\n managed.process.on(\"error\", (error: Error) => {\n handler(error);\n });\n }\n\n getProcess(processId: string): ManagedProcess | null {\n return this._activeProcesses.get(processId) || null;\n }\n\n getActiveProcesses(): ManagedProcess[] {\n return Array.from(this._activeProcesses.values());\n }\n\n getMetrics(): ProcessMetrics {\n // Return a copy to prevent external mutation\n return { ...this._metrics };\n }\n\n async shutdown(): Promise<void> {\n // Terminate all active processes first\n const processIds = Array.from(this._activeProcesses.keys());\n await Promise.all(\n processIds.map((id) => this.terminateProcess(id, \"SIGTERM\"))\n );\n\n // Clear all pending cleanup timers (including ones scheduled by exit handlers)\n for (const [id, timer] of this._cleanupTimers.entries()) {\n clearTimeout(timer);\n this._cleanupTimers.delete(id);\n }\n }\n}\n", "import crypto from 'crypto'\nimport { urlAlphabet } from './url-alphabet/index.js'\nconst POOL_SIZE_MULTIPLIER = 128\nlet pool, poolOffset\nlet fillPool = bytes => {\n if (!pool || pool.length < bytes) {\n pool = Buffer.allocUnsafe(bytes * POOL_SIZE_MULTIPLIER)\n crypto.randomFillSync(pool)\n poolOffset = 0\n } else if (poolOffset + bytes > pool.length) {\n crypto.randomFillSync(pool)\n poolOffset = 0\n }\n poolOffset += bytes\n}\nlet random = bytes => {\n fillPool((bytes |= 0))\n return pool.subarray(poolOffset - bytes, poolOffset)\n}\nlet customRandom = (alphabet, defaultSize, getRandom) => {\n let mask = (2 << (31 - Math.clz32((alphabet.length - 1) | 1))) - 1\n let step = Math.ceil((1.6 * mask * defaultSize) / alphabet.length)\n return (size = defaultSize) => {\n let id = ''\n while (true) {\n let bytes = getRandom(step)\n let i = step\n while (i--) {\n id += alphabet[bytes[i] & mask] || ''\n if (id.length === size) return id\n }\n }\n }\n}\nlet customAlphabet = (alphabet, size = 21) =>\n customRandom(alphabet, size, random)\nlet nanoid = (size = 21) => {\n fillPool((size |= 0))\n let id = ''\n for (let i = poolOffset - size; i < poolOffset; i++) {\n id += urlAlphabet[pool[i] & 63]\n }\n return id\n}\nexport { nanoid, customAlphabet, customRandom, urlAlphabet, random }\n", "/**\n * Process Layer Utilities\n *\n * Helper functions for the Process Layer including ID generation,\n * formatting, and validation utilities.\n *\n * @module execution/process/utils\n */\n\nimport { customAlphabet } from 'nanoid';\n\n/**\n * Generate a unique process ID with a prefix\n *\n * Creates URL-safe, unique identifiers for processes. Uses nanoid for\n * cryptographically strong random IDs.\n *\n * @param prefix - Prefix for the ID (e.g., 'process', 'task')\n * @returns Unique ID string in format: `{prefix}-{randomId}`\n *\n * @example\n * ```typescript\n * const id = generateId('process');\n * // Returns: 'process-a1b2c3d4'\n * ```\n */\nexport function generateId(prefix: string): string {\n // Use nanoid with custom alphabet (alphanumeric, lowercase)\n // 10 characters gives us ~1.5 million years to collision at 1000 IDs/hour\n const nanoid = customAlphabet('0123456789abcdefghijklmnopqrstuvwxyz', 10);\n return `${prefix}-${nanoid()}`;\n}\n\n/**\n * Format duration in milliseconds to human-readable string\n *\n * @param ms - Duration in milliseconds\n * @returns Formatted duration string\n *\n * @example\n * ```typescript\n * formatDuration(1500); // \"1.5s\"\n * formatDuration(65000); // \"1m 5s\"\n * ```\n */\nexport function formatDuration(ms: number): string {\n if (ms < 1000) {\n return `${ms}ms`;\n }\n\n const seconds = Math.floor(ms / 1000);\n if (seconds < 60) {\n return `${seconds}s`;\n }\n\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = seconds % 60;\n if (minutes < 60) {\n return remainingSeconds > 0\n ? `${minutes}m ${remainingSeconds}s`\n : `${minutes}m`;\n }\n\n const hours = Math.floor(minutes / 60);\n const remainingMinutes = minutes % 60;\n return remainingMinutes > 0\n ? `${hours}h ${remainingMinutes}m`\n : `${hours}h`;\n}\n\n/**\n * Validate that a signal name is valid for Node.js\n *\n * @param signal - Signal name to validate\n * @returns True if signal is valid\n */\nexport function isValidSignal(signal: string): boolean {\n const validSignals = [\n 'SIGTERM',\n 'SIGKILL',\n 'SIGINT',\n 'SIGHUP',\n 'SIGQUIT',\n 'SIGABRT',\n ];\n return validSignals.includes(signal);\n}\n\n/**\n * Format error message from process exit\n *\n * @param exitCode - Process exit code\n * @param signal - Signal that terminated the process\n * @returns Formatted error message\n */\nexport function formatProcessError(\n exitCode: number | null,\n signal: string | null\n): string {\n if (signal) {\n return `Process terminated by signal: ${signal}`;\n }\n if (exitCode !== null && exitCode !== 0) {\n return `Process exited with code: ${exitCode}`;\n }\n return 'Process exited unexpectedly';\n}\n", "/**\n * Claude Code Configuration Builder\n *\n * Utility for building ProcessConfig specific to Claude Code CLI.\n * Provides type-safe configuration for Claude Code's flags and options.\n *\n * @module execution/process/builders/claude\n */\n\nimport type { ProcessConfig } from '../types.js';\n\n/**\n * Configuration options specific to Claude Code CLI\n */\nexport interface ClaudeCodeConfig {\n /**\n * Path to Claude Code CLI executable\n * @default 'claude'\n */\n claudePath?: string;\n\n /**\n * Working directory for the process\n */\n workDir: string;\n\n /**\n * Run in non-interactive print mode\n * @default false\n */\n print?: boolean;\n\n /**\n * Output format (stream-json recommended for parsing)\n * @default 'text'\n */\n outputFormat?: 'stream-json' | 'json' | 'text';\n\n /**\n * Enable verbose output (required for stream-json with print mode)\n * @default false\n */\n verbose?: boolean;\n\n /**\n * Skip permission prompts\n * @default false\n */\n dangerouslySkipPermissions?: boolean;\n\n /**\n * Permission mode setting\n */\n permissionMode?: string;\n\n /**\n * Environment variables to pass to the process\n */\n env?: Record<string, string>;\n\n /**\n * Maximum execution time in milliseconds\n */\n timeout?: number;\n\n /**\n * Maximum idle time before cleanup (pool only)\n */\n idleTimeout?: number;\n\n /**\n * Retry configuration for failed spawns\n */\n retry?: {\n maxAttempts: number;\n backoffMs: number;\n };\n}\n\n/**\n * Build a generic ProcessConfig from Claude Code specific configuration\n *\n * @param config - Claude Code specific configuration\n * @returns Generic ProcessConfig that can be used with any ProcessManager\n *\n * @example\n * ```typescript\n * const config = buildClaudeConfig({\n * workDir: '/path/to/project',\n * print: true,\n * outputFormat: 'stream-json',\n * dangerouslySkipPermissions: true,\n * });\n *\n * const process = await manager.acquireProcess(config);\n * ```\n */\nexport function buildClaudeConfig(config: ClaudeCodeConfig): ProcessConfig {\n const args: string[] = [];\n\n // Add --print flag for non-interactive mode\n if (config.print) {\n args.push('--print');\n }\n\n // Add --output-format flag\n if (config.outputFormat) {\n args.push('--output-format', config.outputFormat);\n }\n\n // Add --verbose flag (required for stream-json with print mode)\n if (config.verbose || (config.print && config.outputFormat === 'stream-json')) {\n args.push('--verbose');\n }\n\n // Add --dangerously-skip-permissions flag\n if (config.dangerouslySkipPermissions) {\n args.push('--dangerously-skip-permissions');\n }\n\n // Add --permission-mode flag if specified\n if (config.permissionMode) {\n args.push('--permission-mode', config.permissionMode);\n }\n\n return {\n executablePath: config.claudePath || 'claude',\n args,\n workDir: config.workDir,\n env: config.env,\n timeout: config.timeout,\n idleTimeout: config.idleTimeout,\n retry: config.retry,\n };\n}\n", "/**\n * Simple Execution Engine\n *\n * Queue-based execution engine that spawns a process per task\n * with concurrency limits. Implements the \"simple first\" approach.\n *\n * @module execution/engine/simple-engine\n */\n\nimport type { IExecutionEngine } from \"./engine.js\";\nimport type {\n ExecutionTask,\n ExecutionResult,\n TaskStatus,\n EngineMetrics,\n TaskCompleteHandler,\n TaskFailedHandler,\n EngineConfig,\n RunningTask,\n TaskResolver,\n} from \"./types.js\";\nimport type { IProcessManager } from \"../process/manager.js\";\nimport { buildClaudeConfig } from \"../process/builders/claude.js\";\nimport type { ManagedProcess } from \"../process/types.js\";\n\n/**\n * SimpleExecutionEngine - Queue-based task execution with concurrency control\n *\n * Key features:\n * - FIFO queue for task ordering\n * - Configurable concurrency limit (default: 3)\n * - Automatic retry on failure\n * - Event emission for task lifecycle\n * - Promise-based waiting\n * - Graceful shutdown\n */\nexport class SimpleExecutionEngine implements IExecutionEngine {\n // Task queue (FIFO)\n private taskQueue: ExecutionTask[] = [];\n\n // Running tasks tracking\n private runningTasks = new Map<string, RunningTask>();\n\n // Completed task results\n private completedResults = new Map<string, ExecutionResult>();\n\n // Promise resolvers for waiting (supports multiple waiters per task)\n private taskResolvers = new Map<string, TaskResolver[]>();\n\n // Engine metrics\n private metrics: EngineMetrics;\n\n // Event handlers\n private completeHandlers: TaskCompleteHandler[] = [];\n private failedHandlers: TaskFailedHandler[] = [];\n\n // Track active polling intervals for cleanup\n private pollingIntervals = new Map<string, NodeJS.Timeout>();\n\n /**\n * Create a new SimpleExecutionEngine\n *\n * @param processManager - Process manager for spawning Claude processes\n * @param config - Engine configuration options\n */\n constructor(\n private _processManager: IProcessManager,\n private _config: EngineConfig = {}\n ) {\n // Initialize metrics\n this.metrics = {\n maxConcurrent: _config.maxConcurrent ?? 3,\n currentlyRunning: 0,\n availableSlots: _config.maxConcurrent ?? 3,\n queuedTasks: 0,\n completedTasks: 0,\n failedTasks: 0,\n averageDuration: 0,\n successRate: 1.0,\n throughput: 0,\n totalProcessesSpawned: 0,\n activeProcesses: 0,\n };\n }\n\n /**\n * Submit a single task for execution\n *\n * Adds task to queue and attempts to start execution if capacity available.\n *\n * @param task - The task to execute\n * @returns Promise resolving to the task ID\n */\n async submitTask(task: ExecutionTask): Promise<string> {\n // Add to queue\n this.taskQueue.push(task);\n this.metrics.queuedTasks++;\n\n // Try to start immediately if capacity available\n this.processQueue();\n\n return task.id;\n }\n\n /**\n * Submit multiple tasks for execution\n *\n * @param tasks - Array of tasks to execute\n * @returns Promise resolving to array of task IDs\n */\n async submitTasks(tasks: ExecutionTask[]): Promise<string[]> {\n const ids: string[] = [];\n\n for (const task of tasks) {\n const id = await this.submitTask(task);\n ids.push(id);\n }\n\n return ids;\n }\n\n /**\n * Process the task queue\n *\n * Dequeues tasks and starts execution while capacity is available.\n * Checks dependencies before execution and re-queues if not met.\n *\n * @private\n */\n private processQueue(): void {\n // Track if we've made any progress in this pass\n let tasksProcessed = 0;\n const initialQueueSize = this.taskQueue.length;\n\n // Check if we have capacity and tasks to process\n while (\n this.taskQueue.length > 0 &&\n this.runningTasks.size < this.metrics.maxConcurrent &&\n tasksProcessed < initialQueueSize // Prevent infinite loop\n ) {\n const task = this.taskQueue.shift()!;\n this.metrics.queuedTasks--;\n tasksProcessed++;\n\n // Check if any dependency has failed\n if (this.hasFailedDependency(task)) {\n // Fail this task immediately - don't wait for failed dependencies\n this.handleTaskFailure(\n task.id,\n new Error(`Task ${task.id} failed: one or more dependencies failed`)\n );\n continue; // Process next task\n }\n\n // Check if all dependencies are met\n if (!this.areDependenciesMet(task)) {\n // Re-queue at end - dependencies not yet completed\n this.taskQueue.push(task);\n this.metrics.queuedTasks++;\n continue; // Try next task in queue\n }\n\n // Track task as running and update capacity\n this.trackTaskStart(task);\n\n // Start execution\n this.executeTask(task).catch((error) => {\n this.handleTaskFailure(task.id, error);\n });\n }\n }\n\n /**\n * Check if all task dependencies are met\n *\n * @param task - Task to check\n * @returns True if all dependencies completed successfully\n * @private\n */\n private areDependenciesMet(task: ExecutionTask): boolean {\n // No dependencies means dependencies are met\n if (task.dependencies.length === 0) {\n return true;\n }\n\n // Check each dependency\n for (const depId of task.dependencies) {\n const result = this.completedResults.get(depId);\n\n // Dependency not completed yet\n if (!result) {\n return false;\n }\n\n // Dependency completed but failed\n if (!result.success) {\n return false;\n }\n }\n\n // All dependencies completed successfully\n return true;\n }\n\n /**\n * Check if any task dependency has failed\n *\n * @param task - Task to check\n * @returns True if any dependency failed\n * @private\n */\n private hasFailedDependency(task: ExecutionTask): boolean {\n for (const depId of task.dependencies) {\n const result = this.completedResults.get(depId);\n\n // If dependency completed but failed, return true\n if (result && !result.success) {\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * Track task as running and update capacity metrics\n *\n * @param task - Task to start tracking\n * @private\n */\n private trackTaskStart(task: ExecutionTask): void {\n // Get attempt from metadata if this is a retry\n const attempt = (task.metadata?._retryAttempt as number) || 1;\n\n // Add to running tasks\n const runningTask: RunningTask = {\n task,\n process: null as any,\n startedAt: new Date(),\n attempt,\n };\n this.runningTasks.set(task.id, runningTask);\n\n // Update metrics\n this.metrics.currentlyRunning = this.runningTasks.size;\n this.metrics.availableSlots =\n this.metrics.maxConcurrent - this.runningTasks.size;\n }\n\n /**\n * Track task completion and release capacity\n *\n * @param taskId - ID of completed task\n * @private\n */\n private trackTaskComplete(taskId: string): void {\n // Remove from running tasks\n this.runningTasks.delete(taskId);\n\n // Update metrics\n this.metrics.currentlyRunning = this.runningTasks.size;\n this.metrics.availableSlots =\n this.metrics.maxConcurrent - this.runningTasks.size;\n\n // Try to process more tasks now that capacity is available\n this.processQueue();\n }\n\n /**\n * Execute a task\n *\n * Acquires a process, sends the prompt, collects output, and builds the result.\n *\n * @param task - Task to execute\n * @private\n */\n private async executeTask(task: ExecutionTask): Promise<void> {\n const startTime = new Date();\n let managedProcess: ManagedProcess | null = null;\n let output = \"\";\n let errorOutput = \"\";\n\n try {\n // Build process configuration\n const processConfig = buildClaudeConfig({\n claudePath: this._config.claudePath,\n workDir: task.workDir,\n print: true,\n outputFormat: \"stream-json\",\n dangerouslySkipPermissions: true,\n env: task.config.env,\n timeout: task.config.timeout,\n });\n\n // Acquire process from manager\n managedProcess = await this._processManager.acquireProcess(processConfig);\n\n // Update running task with process reference\n const runningTask = this.runningTasks.get(task.id);\n if (runningTask) {\n runningTask.process = managedProcess;\n }\n\n // Set up output collection\n this._processManager.onOutput(managedProcess.id, (data, type) => {\n if (type === \"stdout\") {\n output += data.toString();\n } else {\n errorOutput += data.toString();\n }\n\n // Call optional output handler for real-time processing\n if (this._config.onOutput) {\n this._config.onOutput(data, type);\n }\n });\n\n // Set up error handler\n this._processManager.onError(managedProcess.id, (error) => {\n errorOutput += `Process error: ${error.message}\\n`;\n });\n\n // Send the prompt to the process\n await this._processManager.sendInput(managedProcess.id, task.prompt);\n\n // Close stdin to signal EOF (Claude Code waits for stdin to close in --print mode)\n this._processManager.closeInput(managedProcess.id);\n\n // Wait for process to complete\n await this.waitForProcessExit(managedProcess, task.config.timeout);\n\n // Build execution result\n const endTime = new Date();\n const result: ExecutionResult = {\n taskId: task.id,\n executionId: managedProcess.id,\n success: managedProcess.exitCode === 0,\n exitCode: managedProcess.exitCode ?? -1,\n output,\n error: errorOutput || undefined,\n startedAt: startTime,\n completedAt: endTime,\n duration: endTime.getTime() - startTime.getTime(),\n metadata: this.parseMetadata(output),\n };\n\n // Store result\n this.completedResults.set(task.id, result);\n\n // Update metrics\n this.metrics.completedTasks++;\n\n // Resolve promises for all waiters\n const resolvers = this.taskResolvers.get(task.id);\n if (resolvers) {\n resolvers.forEach((resolver) => resolver.resolve(result));\n this.taskResolvers.delete(task.id);\n }\n\n // Emit completion event\n for (const handler of this.completeHandlers) {\n handler(result);\n }\n\n // Release capacity\n this.trackTaskComplete(task.id);\n } catch (error) {\n // Handle execution failure\n this.handleTaskFailure(task.id, error as Error);\n } finally {\n // Clean up process\n if (managedProcess) {\n try {\n await this._processManager.releaseProcess(managedProcess.id);\n } catch {\n // Ignore cleanup errors\n }\n }\n }\n }\n\n /**\n * Wait for a process to exit\n *\n * @param process - The managed process to wait for\n * @param timeoutMs - Optional timeout in milliseconds\n * @private\n */\n private async waitForProcessExit(\n process: ManagedProcess,\n timeoutMs?: number\n ): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n // Check if already exited\n if (process.exitCode !== null) {\n resolve();\n return;\n }\n\n // Set up exit listener\n const checkInterval = setInterval(() => {\n // Fetch fresh process state from manager to avoid stale references\n const currentProcess = this._processManager.getProcess(process.id);\n if (!currentProcess || currentProcess.exitCode !== null) {\n clearInterval(checkInterval);\n this.pollingIntervals.delete(process.id);\n\n if (!currentProcess) {\n reject(new Error(\"Process not found\"));\n } else if (currentProcess.status === \"crashed\") {\n reject(\n new Error(\n `Process crashed with exit code ${currentProcess.exitCode}`\n )\n );\n } else {\n resolve();\n }\n }\n }, 10); // Poll every 10ms for responsive detection\n\n // Track this interval for cleanup\n this.pollingIntervals.set(process.id, checkInterval);\n\n // Set timeout if configured\n if (timeoutMs) {\n setTimeout(() => {\n clearInterval(checkInterval);\n this.pollingIntervals.delete(process.id);\n reject(new Error(\"Process execution timeout\"));\n }, timeoutMs);\n }\n });\n }\n\n /**\n * Parse metadata from stream-json output\n *\n * Extracts tools used, files changed, tokens, etc. from the output.\n *\n * @param output - Raw output from Claude Code\n * @returns Parsed metadata\n * @private\n */\n private parseMetadata(_output: string): ExecutionResult[\"metadata\"] {\n // Stub for now - will enhance in future issues\n // TODO: Parse stream-json output for metadata\n return {\n toolsUsed: [],\n filesChanged: [],\n tokensUsed: 0,\n cost: 0,\n };\n }\n\n /**\n * Handle task failure\n *\n * Implements automatic retry logic if maxRetries is configured.\n * Re-queues failed tasks at front of queue for priority retry.\n *\n * @param taskId - ID of failed task\n * @param error - Error that occurred\n * @private\n */\n private handleTaskFailure(_taskId: string, _error: Error): void {\n // Get running task to check retry eligibility\n const runningTask = this.runningTasks.get(_taskId);\n\n if (runningTask) {\n const task = runningTask.task;\n const maxRetries = task.config.maxRetries;\n const currentAttempt = runningTask.attempt;\n\n // Check if we should retry\n if (maxRetries !== undefined && currentAttempt <= maxRetries) {\n // Create retry task with incremented attempt\n const retryTask: ExecutionTask = {\n ...task,\n metadata: {\n ...task.metadata,\n _retryAttempt: currentAttempt + 1,\n },\n };\n\n // Re-queue at front for priority retry\n this.taskQueue.unshift(retryTask);\n this.metrics.queuedTasks++;\n\n // Release capacity so retry can start\n this.trackTaskComplete(_taskId);\n\n // Don't emit failure or store result yet - will retry\n // Note: Don't call processQueue() here, it will be called by trackTaskComplete\n return;\n }\n }\n\n // No retries left or maxRetries not configured - proceed with final failure\n\n // Create a failed execution result\n const now = new Date();\n const failedResult: ExecutionResult = {\n taskId: _taskId,\n executionId: `failed-${_taskId}`,\n success: false,\n exitCode: -1,\n output: \"\",\n error: _error.message,\n startedAt: now,\n completedAt: now,\n duration: 0,\n metadata: {\n toolsUsed: [],\n filesChanged: [],\n tokensUsed: 0,\n cost: 0,\n },\n };\n\n // Store the failed result so dependent tasks can check it\n this.completedResults.set(_taskId, failedResult);\n\n // Update metrics\n this.metrics.failedTasks++;\n\n // Release capacity\n this.trackTaskComplete(_taskId);\n\n // Reject promises for all waiters\n const resolvers = this.taskResolvers.get(_taskId);\n if (resolvers) {\n resolvers.forEach((resolver) => resolver.reject(_error));\n this.taskResolvers.delete(_taskId);\n }\n\n // Emit event\n for (const handler of this.failedHandlers) {\n handler(_taskId, _error);\n }\n }\n\n /**\n * Cancel a queued or running task\n *\n * Removes task from queue if still queued, or terminates process if running.\n * Idempotent - safe to call multiple times or for non-existent tasks.\n *\n * @param taskId - ID of task to cancel\n */\n async cancelTask(taskId: string): Promise<void> {\n // Check if task is in queue\n const queueIndex = this.taskQueue.findIndex((t) => t.id === taskId);\n if (queueIndex >= 0) {\n // Remove from queue\n this.taskQueue.splice(queueIndex, 1);\n this.metrics.queuedTasks--;\n return; // Task cancelled before execution\n }\n\n // Check if task is currently running\n const runningTask = this.runningTasks.get(taskId);\n if (runningTask) {\n // Clear polling interval if exists\n const interval = this.pollingIntervals.get(runningTask.process.id);\n if (interval) {\n clearInterval(interval);\n this.pollingIntervals.delete(runningTask.process.id);\n }\n\n // Terminate the running process\n try {\n await this._processManager.terminateProcess(runningTask.process.id);\n } catch (error) {\n // Ignore termination errors - process may already be dead\n }\n\n // Clean up running task tracking\n this.runningTasks.delete(taskId);\n\n // Update metrics\n this.metrics.currentlyRunning = this.runningTasks.size;\n this.metrics.availableSlots =\n this.metrics.maxConcurrent - this.runningTasks.size;\n\n // Process queue to start next task\n this.processQueue();\n\n return; // Task cancelled during execution\n }\n\n // Task not found in queue or running - either completed or never existed\n // This is idempotent, so we just return without error\n }\n\n /**\n * Get current status of a task\n */\n getTaskStatus(taskId: string): TaskStatus | null {\n // Check completed\n const result = this.completedResults.get(taskId);\n if (result) {\n return { state: \"completed\", result };\n }\n\n // Check running\n const running = this.runningTasks.get(taskId);\n if (running) {\n return {\n state: \"running\",\n processId: running.process.id,\n startedAt: running.startedAt,\n };\n }\n\n // Check queued\n const queuePos = this.taskQueue.findIndex((t) => t.id === taskId);\n if (queuePos >= 0) {\n return { state: \"queued\", position: queuePos };\n }\n\n return null;\n }\n\n /**\n * Wait for a task to complete\n *\n * Returns a promise that resolves with the task result when complete.\n * Supports multiple concurrent waiters for the same task.\n *\n * @param taskId - ID of the task to wait for\n * @returns Promise resolving to the execution result\n */\n async waitForTask(taskId: string): Promise<ExecutionResult> {\n // Check if already completed\n const existing = this.completedResults.get(taskId);\n if (existing) return existing;\n\n // Wait for completion - support multiple concurrent waiters\n return new Promise((resolve, reject) => {\n const resolvers = this.taskResolvers.get(taskId) || [];\n resolvers.push({ resolve, reject });\n this.taskResolvers.set(taskId, resolvers);\n });\n }\n\n /**\n * Wait for multiple tasks to complete\n *\n * Returns a promise that resolves when all tasks complete.\n *\n * @param taskIds - Array of task IDs to wait for\n * @returns Promise resolving to array of execution results\n */\n async waitForTasks(taskIds: string[]): Promise<ExecutionResult[]> {\n return Promise.all(taskIds.map((id) => this.waitForTask(id)));\n }\n\n /**\n * Get current engine metrics\n *\n * Returns defensive copy of current metrics.\n *\n * @returns Current engine metrics\n */\n getMetrics(): EngineMetrics {\n // Return defensive copy\n return { ...this.metrics };\n }\n\n /**\n * Register handler for task completion events\n */\n onTaskComplete(handler: TaskCompleteHandler): void {\n this.completeHandlers.push(handler);\n }\n\n /**\n * Register handler for task failure events\n */\n onTaskFailed(handler: TaskFailedHandler): void {\n this.failedHandlers.push(handler);\n }\n\n /**\n * Gracefully shutdown the engine\n *\n * Cancels all running tasks, clears the queue, and shuts down the process manager.\n * Idempotent - safe to call multiple times.\n */\n async shutdown(): Promise<void> {\n // Clear the task queue (prevent new tasks from starting)\n this.taskQueue = [];\n this.metrics.queuedTasks = 0;\n\n // Cancel all running tasks\n const runningTaskIds = Array.from(this.runningTasks.keys());\n await Promise.all(runningTaskIds.map((id) => this.cancelTask(id)));\n\n // Clear all polling intervals\n for (const [processId, interval] of this.pollingIntervals.entries()) {\n clearInterval(interval);\n this.pollingIntervals.delete(processId);\n }\n\n // Shutdown the process manager\n await this._processManager.shutdown();\n\n // Clear all internal state\n this.runningTasks.clear();\n this.completedResults.clear();\n this.taskResolvers.clear();\n this.completeHandlers = [];\n this.failedHandlers = [];\n\n // Reset metrics\n this.metrics.currentlyRunning = 0;\n this.metrics.availableSlots = this.metrics.maxConcurrent;\n }\n}\n", "/**\n * Circuit Breaker Implementation\n *\n * Implements the circuit breaker pattern for preventing cascading failures.\n * Tracks failures and successes, automatically opening when thresholds are\n * exceeded and recovering through half-open state.\n *\n * @module execution/resilience/circuit-breaker\n */\n\nimport type { CircuitBreaker, CircuitState } from './types.js';\n\n/**\n * CircuitBreakerManager - Manages multiple circuit breakers\n *\n * Maintains a collection of circuit breakers, typically one per task type\n * or service. Provides methods for checking state, recording outcomes,\n * and managing circuit lifecycle.\n *\n * @example\n * ```typescript\n * const manager = new CircuitBreakerManager();\n * const breaker = manager.getOrCreate('issue-executor', {\n * failureThreshold: 5,\n * successThreshold: 2,\n * timeout: 60000,\n * });\n *\n * if (manager.canExecute('issue-executor')) {\n * // Execute task\n * const success = await executeTask();\n * if (success) {\n * manager.recordSuccess('issue-executor');\n * } else {\n * manager.recordFailure('issue-executor', new Error('Task failed'));\n * }\n * }\n * ```\n */\nexport class CircuitBreakerManager {\n private breakers = new Map<string, CircuitBreaker>();\n\n /**\n * Get an existing circuit breaker by name\n *\n * @param name - Circuit breaker name\n * @returns Circuit breaker or null if not found\n */\n get(name: string): CircuitBreaker | null {\n return this.breakers.get(name) || null;\n }\n\n /**\n * Get or create a circuit breaker\n *\n * If a circuit breaker with the given name exists, returns it.\n * Otherwise creates a new one with the provided configuration.\n *\n * @param name - Circuit breaker name (typically task type)\n * @param config - Configuration for new circuit breaker\n * @returns Circuit breaker instance\n */\n getOrCreate(\n name: string,\n config: CircuitBreaker['config'] = {\n failureThreshold: 5,\n successThreshold: 2,\n timeout: 60000,\n }\n ): CircuitBreaker {\n let breaker = this.breakers.get(name);\n\n if (!breaker) {\n breaker = {\n name,\n state: 'closed',\n config,\n metrics: {\n totalRequests: 0,\n failedRequests: 0,\n successfulRequests: 0,\n },\n };\n\n this.breakers.set(name, breaker);\n }\n\n return breaker;\n }\n\n /**\n * Check if a circuit breaker allows execution\n *\n * Returns false if circuit is open and timeout hasn't elapsed yet.\n * Returns true for closed and half-open states.\n *\n * @param name - Circuit breaker name\n * @returns True if execution is allowed\n */\n canExecute(name: string): boolean {\n const breaker = this.breakers.get(name);\n if (!breaker) {\n return true; // No breaker means no restriction\n }\n\n if (breaker.state === 'open') {\n // Check if timeout has elapsed to transition to half-open\n if (this.shouldTransitionToHalfOpen(breaker)) {\n breaker.state = 'half-open';\n return true;\n }\n return false;\n }\n\n return true; // closed or half-open allows execution\n }\n\n /**\n * Record a successful execution\n *\n * Updates metrics and may transition circuit from half-open to closed\n * if success threshold is met.\n *\n * @param name - Circuit breaker name\n */\n recordSuccess(name: string): void {\n const breaker = this.breakers.get(name);\n if (!breaker) {\n return;\n }\n\n breaker.metrics.totalRequests++;\n breaker.metrics.successfulRequests++;\n breaker.metrics.lastSuccessTime = new Date();\n\n // If in half-open state, check if we should close\n if (breaker.state === 'half-open') {\n // Count recent successes (simplified: use total for now)\n // In production, you'd track a sliding window of recent attempts\n const recentSuccesses = this.getConsecutiveSuccesses(breaker);\n\n if (recentSuccesses >= breaker.config.successThreshold) {\n breaker.state = 'closed';\n breaker.metrics.failedRequests = 0; // Reset failure count\n }\n }\n }\n\n /**\n * Record a failed execution\n *\n * Updates metrics and may transition circuit from closed/half-open to open\n * if failure threshold is met.\n *\n * @param name - Circuit breaker name\n * @param error - Error that occurred\n */\n recordFailure(name: string, error: Error): void {\n const breaker = this.breakers.get(name);\n if (!breaker) {\n return;\n }\n\n void error; // Error logged but not used in current implementation\n\n breaker.metrics.totalRequests++;\n breaker.metrics.failedRequests++;\n breaker.metrics.lastFailureTime = new Date();\n\n // Check if we should open the circuit\n if (breaker.state === 'closed') {\n if (breaker.metrics.failedRequests >= breaker.config.failureThreshold) {\n breaker.state = 'open';\n }\n } else if (breaker.state === 'half-open') {\n // Any failure in half-open state reopens the circuit\n breaker.state = 'open';\n }\n }\n\n /**\n * Reset a circuit breaker to closed state\n *\n * Clears all failure counts and returns circuit to closed state.\n * Useful for manual recovery or after fixing underlying issues.\n *\n * @param name - Circuit breaker name\n */\n reset(name: string): void {\n const breaker = this.breakers.get(name);\n if (!breaker) {\n return;\n }\n\n breaker.state = 'closed';\n breaker.metrics.failedRequests = 0;\n breaker.metrics.successfulRequests = 0;\n }\n\n /**\n * Get all circuit breakers\n *\n * @returns Map of circuit breaker name to breaker instance\n */\n getAll(): Map<string, CircuitBreaker> {\n return new Map(this.breakers);\n }\n\n /**\n * Check if enough time has passed to transition from open to half-open\n *\n * @param breaker - Circuit breaker to check\n * @returns True if timeout has elapsed\n * @private\n */\n private shouldTransitionToHalfOpen(breaker: CircuitBreaker): boolean {\n if (!breaker.metrics.lastFailureTime) {\n return true;\n }\n\n const timeSinceFailure =\n Date.now() - breaker.metrics.lastFailureTime.getTime();\n\n return timeSinceFailure >= breaker.config.timeout;\n }\n\n /**\n * Get count of consecutive successes\n *\n * In a production implementation, this would track a sliding window\n * of recent attempts. For simplicity, we use total successful requests.\n *\n * @param breaker - Circuit breaker to check\n * @returns Number of consecutive successes\n * @private\n */\n private getConsecutiveSuccesses(breaker: CircuitBreaker): number {\n // Simplified: In production, track recent attempts in a circular buffer\n // For now, use successful requests as a proxy\n return breaker.metrics.successfulRequests;\n }\n}\n\n/**\n * Create a new circuit breaker instance\n *\n * Helper function to create a properly configured circuit breaker.\n *\n * @param name - Circuit breaker name\n * @param config - Circuit breaker configuration\n * @returns New circuit breaker instance\n *\n * @example\n * ```typescript\n * const breaker = createCircuitBreaker('api-calls', {\n * failureThreshold: 10,\n * successThreshold: 3,\n * timeout: 30000,\n * });\n * ```\n */\nexport function createCircuitBreaker(\n name: string,\n config: CircuitBreaker['config'] = {\n failureThreshold: 5,\n successThreshold: 2,\n timeout: 60000,\n }\n): CircuitBreaker {\n return {\n name,\n state: 'closed',\n config,\n metrics: {\n totalRequests: 0,\n failedRequests: 0,\n successfulRequests: 0,\n },\n };\n}\n\n/**\n * Check if a circuit breaker is in a specific state\n *\n * @param breaker - Circuit breaker to check\n * @param state - State to check for\n * @returns True if breaker is in the specified state\n */\nexport function isInState(\n breaker: CircuitBreaker,\n state: CircuitState\n): boolean {\n return breaker.state === state;\n}\n\n/**\n * Get the current state of a circuit breaker\n *\n * @param breaker - Circuit breaker to check\n * @returns Current state\n */\nexport function getState(breaker: CircuitBreaker): CircuitState {\n return breaker.state;\n}\n\n/**\n * Calculate failure rate for a circuit breaker\n *\n * @param breaker - Circuit breaker to analyze\n * @returns Failure rate (0-1) or 0 if no requests\n */\nexport function getFailureRate(breaker: CircuitBreaker): number {\n if (breaker.metrics.totalRequests === 0) {\n return 0;\n }\n\n return breaker.metrics.failedRequests / breaker.metrics.totalRequests;\n}\n\n/**\n * Calculate success rate for a circuit breaker\n *\n * @param breaker - Circuit breaker to analyze\n * @returns Success rate (0-1) or 0 if no requests\n */\nexport function getSuccessRate(breaker: CircuitBreaker): number {\n if (breaker.metrics.totalRequests === 0) {\n return 0;\n }\n\n return breaker.metrics.successfulRequests / breaker.metrics.totalRequests;\n}\n", "/**\n * Retry Logic and Backoff Strategies\n *\n * Implements retry logic with various backoff strategies including\n * exponential, linear, and fixed delays with optional jitter.\n *\n * @module execution/resilience/retry\n */\n\nimport type { RetryPolicy, ExecutionAttempt } from './types.js';\nimport type { ExecutionResult } from '../engine/types.js';\n\n/**\n * Calculate backoff delay for a given attempt\n *\n * Supports multiple backoff strategies:\n * - Exponential: baseDelay * 2^(attempt-1) - e.g., 1s, 2s, 4s, 8s, 16s\n * - Linear: baseDelay * attempt - e.g., 1s, 2s, 3s, 4s, 5s\n * - Fixed: constant baseDelay - e.g., 1s, 1s, 1s, 1s, 1s\n *\n * Applies maxDelay cap and optional jitter to prevent thundering herd.\n *\n * @param attempt - Attempt number (1-indexed)\n * @param config - Backoff configuration from retry policy\n * @returns Delay in milliseconds\n *\n * @example\n * ```typescript\n * // Exponential backoff with jitter\n * const delay = calculateBackoff(3, {\n * type: 'exponential',\n * baseDelayMs: 1000,\n * maxDelayMs: 30000,\n * jitter: true,\n * });\n * // Returns ~4000ms \u00B1 10% jitter\n * ```\n */\nexport function calculateBackoff(\n attempt: number,\n config: RetryPolicy['backoff']\n): number {\n let delay: number;\n\n // Calculate base delay based on strategy\n switch (config.type) {\n case 'exponential':\n // 2^(attempt-1) * baseDelay\n // attempt 1: 2^0 = 1x, attempt 2: 2^1 = 2x, attempt 3: 2^2 = 4x\n delay = config.baseDelayMs * Math.pow(2, attempt - 1);\n break;\n\n case 'linear':\n // attempt * baseDelay\n // attempt 1: 1x, attempt 2: 2x, attempt 3: 3x\n delay = config.baseDelayMs * attempt;\n break;\n\n case 'fixed':\n // constant baseDelay\n delay = config.baseDelayMs;\n break;\n\n default:\n // TypeScript should prevent this, but handle gracefully\n delay = config.baseDelayMs;\n }\n\n // Enforce maximum delay cap\n delay = Math.min(delay, config.maxDelayMs);\n\n // Add jitter if configured (\u00B110% randomness)\n if (config.jitter) {\n const jitterAmount = delay * 0.1; // 10% of delay\n const jitterOffset = Math.random() * jitterAmount * 2 - jitterAmount;\n delay += jitterOffset;\n\n // Ensure delay stays positive and doesn't exceed max after jitter\n delay = Math.max(0, Math.min(delay, config.maxDelayMs));\n }\n\n return Math.floor(delay);\n}\n\n/**\n * Check if an error should trigger a retry\n *\n * Matches error message against the list of retryable error patterns\n * defined in the retry policy.\n *\n * @param error - Error that occurred\n * @param policy - Retry policy with retryable error patterns\n * @returns True if error should be retried\n *\n * @example\n * ```typescript\n * const error = new Error('Connection timeout');\n * const shouldRetry = isRetryableError(error, {\n * retryableErrors: ['timeout', 'ECONNREFUSED'],\n * // ... other policy fields\n * });\n * // Returns true\n * ```\n */\nexport function isRetryableError(error: Error, policy: RetryPolicy): boolean {\n const errorMessage = error.message || '';\n\n // Check if error message contains any retryable pattern\n for (const retryablePattern of policy.retryableErrors) {\n if (errorMessage.includes(retryablePattern)) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Check if an exit code should trigger a retry\n *\n * Matches exit code against the list of retryable exit codes\n * defined in the retry policy.\n *\n * @param exitCode - Process exit code\n * @param policy - Retry policy with retryable exit codes\n * @returns True if exit code should be retried\n *\n * @example\n * ```typescript\n * const shouldRetry = isRetryableExitCode(1, {\n * retryableExitCodes: [1, 137],\n * // ... other policy fields\n * });\n * // Returns true\n * ```\n */\nexport function isRetryableExitCode(\n exitCode: number,\n policy: RetryPolicy\n): boolean {\n return policy.retryableExitCodes.includes(exitCode);\n}\n\n/**\n * Check if an execution result should trigger a retry\n *\n * Checks both the exit code and error message (if present) to determine\n * if the execution should be retried.\n *\n * @param result - Execution result from task execution\n * @param policy - Retry policy\n * @returns True if execution should be retried\n *\n * @example\n * ```typescript\n * const result: ExecutionResult = {\n * taskId: 'task-1',\n * executionId: 'proc-123',\n * success: false,\n * exitCode: 1,\n * error: 'Connection timeout',\n * // ... other fields\n * };\n *\n * const shouldRetry = isRetryableResult(result, policy);\n * // Returns true if exitCode is retryable OR error contains retryable pattern\n * ```\n */\nexport function isRetryableResult(\n result: ExecutionResult,\n policy: RetryPolicy\n): boolean {\n // Check exit code\n if (result.exitCode !== undefined && result.exitCode !== null) {\n if (isRetryableExitCode(result.exitCode, policy)) {\n return true;\n }\n }\n\n // Check error message\n if (result.error) {\n const error = new Error(result.error);\n if (isRetryableError(error, policy)) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Promise-based sleep utility\n *\n * Returns a promise that resolves after the specified delay.\n * Useful for implementing retry backoff.\n *\n * @param ms - Delay in milliseconds\n * @returns Promise that resolves after delay\n *\n * @example\n * ```typescript\n * console.log('Starting...');\n * await sleep(1000);\n * console.log('1 second later');\n * ```\n */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Create an execution attempt record\n *\n * Helper function to create a properly structured ExecutionAttempt object.\n *\n * @param attemptNumber - Attempt number (1-indexed)\n * @param success - Whether the attempt succeeded\n * @param options - Optional fields for the attempt\n * @returns ExecutionAttempt object\n */\nexport function createAttempt(\n attemptNumber: number,\n success: boolean,\n options: {\n error?: Error;\n exitCode?: number;\n duration?: number;\n willRetry?: boolean;\n nextRetryAt?: Date;\n } = {}\n): ExecutionAttempt {\n const now = new Date();\n\n return {\n attemptNumber,\n startedAt: now,\n completedAt: options.duration !== undefined ? now : undefined,\n duration: options.duration,\n success,\n error: options.error,\n exitCode: options.exitCode,\n willRetry: options.willRetry || false,\n nextRetryAt: options.nextRetryAt,\n };\n}\n\n/**\n * Calculate total delay from all retry attempts\n *\n * Sums up all the backoff delays that would be applied for a given\n * number of retry attempts. Useful for timeout calculations.\n *\n * @param maxAttempts - Maximum number of attempts\n * @param backoffConfig - Backoff configuration\n * @returns Total delay in milliseconds\n *\n * @example\n * ```typescript\n * const totalDelay = calculateTotalRetryDelay(3, {\n * type: 'exponential',\n * baseDelayMs: 1000,\n * maxDelayMs: 30000,\n * jitter: false,\n * });\n * // Returns 7000ms (1s + 2s + 4s)\n * ```\n */\nexport function calculateTotalRetryDelay(\n maxAttempts: number,\n backoffConfig: RetryPolicy['backoff']\n): number {\n let totalDelay = 0;\n\n // Calculate delay for each attempt (excluding the first one)\n for (let attempt = 2; attempt <= maxAttempts; attempt++) {\n totalDelay += calculateBackoff(attempt, backoffConfig);\n }\n\n return totalDelay;\n}\n", "/**\n * Resilience Layer Types\n *\n * Core types for the Resilience Layer (Layer 3) that adds retry logic,\n * circuit breakers, and fault tolerance to task execution.\n *\n * @module execution/resilience/types\n */\n\nimport type { ExecutionResult } from '../engine/types.js';\n\n/**\n * RetryPolicy - Configuration for retry behavior\n */\nexport interface RetryPolicy {\n /**\n * Maximum number of retry attempts (0 = no retry)\n */\n maxAttempts: number;\n\n /**\n * Backoff strategy configuration\n */\n backoff: {\n /**\n * Type of backoff strategy\n * - exponential: delay = baseDelay * 2^(attempt-1)\n * - linear: delay = baseDelay * attempt\n * - fixed: delay = baseDelay (constant)\n */\n type: 'exponential' | 'linear' | 'fixed';\n\n /**\n * Initial delay in milliseconds\n */\n baseDelayMs: number;\n\n /**\n * Maximum delay cap in milliseconds\n */\n maxDelayMs: number;\n\n /**\n * Add randomness to prevent thundering herd\n * Adds \u00B110% jitter to delay\n */\n jitter: boolean;\n };\n\n /**\n * Error types/messages that should trigger a retry\n * Examples: 'ECONNREFUSED', 'timeout', 'network'\n */\n retryableErrors: string[];\n\n /**\n * Exit codes that should trigger a retry\n * Examples: [1, 137] for generic error and SIGKILL\n */\n retryableExitCodes: number[];\n\n /**\n * Optional callback to determine if circuit breaker should open\n */\n shouldOpenCircuit?: (error: Error, attempts: number) => boolean;\n}\n\n/**\n * CircuitState - State of a circuit breaker\n */\nexport type CircuitState = 'closed' | 'open' | 'half-open';\n\n/**\n * CircuitBreaker - Circuit breaker for preventing cascading failures\n */\nexport interface CircuitBreaker {\n /**\n * Unique name for this circuit breaker\n * Typically matches the task type (e.g., 'issue', 'spec', 'custom')\n */\n name: string;\n\n /**\n * Current state of the circuit\n * - closed: Normal operation, requests pass through\n * - open: Too many failures, requests rejected\n * - half-open: Testing if service recovered, limited requests allowed\n */\n state: CircuitState;\n\n /**\n * Circuit breaker configuration\n */\n config: {\n /**\n * Number of consecutive failures before opening circuit\n */\n failureThreshold: number;\n\n /**\n * Number of consecutive successes in half-open to close circuit\n */\n successThreshold: number;\n\n /**\n * Time to wait before transitioning from open to half-open (ms)\n */\n timeout: number;\n };\n\n /**\n * Circuit breaker metrics\n */\n metrics: {\n /**\n * Total requests processed\n */\n totalRequests: number;\n\n /**\n * Total failed requests\n */\n failedRequests: number;\n\n /**\n * Total successful requests\n */\n successfulRequests: number;\n\n /**\n * Timestamp of last failure\n */\n lastFailureTime?: Date;\n\n /**\n * Timestamp of last success\n */\n lastSuccessTime?: Date;\n };\n}\n\n/**\n * ExecutionAttempt - Record of a single execution attempt\n */\nexport interface ExecutionAttempt {\n /**\n * Attempt number (1-indexed)\n */\n attemptNumber: number;\n\n /**\n * When this attempt started\n */\n startedAt: Date;\n\n /**\n * When this attempt completed (if finished)\n */\n completedAt?: Date;\n\n /**\n * Duration of this attempt in milliseconds\n */\n duration?: number;\n\n /**\n * Whether this attempt succeeded\n */\n success: boolean;\n\n /**\n * Error that occurred during this attempt\n */\n error?: Error;\n\n /**\n * Exit code from this attempt\n */\n exitCode?: number;\n\n /**\n * Whether another retry will be attempted after this\n */\n willRetry: boolean;\n\n /**\n * When the next retry will occur (if willRetry is true)\n */\n nextRetryAt?: Date;\n}\n\n/**\n * ResilientExecutionResult - Enhanced execution result with retry information\n *\n * Extends the base ExecutionResult with detailed retry tracking\n */\nexport interface ResilientExecutionResult extends ExecutionResult {\n /**\n * All execution attempts made for this task\n */\n attempts: ExecutionAttempt[];\n\n /**\n * Total number of attempts made\n */\n totalAttempts: number;\n\n /**\n * The final attempt (may be success or failure)\n */\n finalAttempt: ExecutionAttempt;\n\n /**\n * Human-readable reason for failure (if failed)\n */\n failureReason?: string;\n\n /**\n * Whether the circuit breaker prevented execution\n */\n circuitBreakerTriggered?: boolean;\n}\n\n/**\n * RetryMetrics - Aggregate metrics for retry behavior\n */\nexport interface RetryMetrics {\n /**\n * Total number of retries attempted across all tasks\n */\n totalRetries: number;\n\n /**\n * Number of retries that eventually succeeded\n */\n successfulRetries: number;\n\n /**\n * Number of retries that ultimately failed\n */\n failedRetries: number;\n\n /**\n * Average number of attempts needed for successful tasks\n */\n averageAttemptsToSuccess: number;\n\n /**\n * Circuit breakers by name\n */\n circuitBreakers: Map<string, CircuitBreaker>;\n}\n\n/**\n * RetryAttemptHandler - Callback for retry attempt events\n */\nexport type RetryAttemptHandler = (\n taskId: string,\n attempt: ExecutionAttempt\n) => void;\n\n/**\n * CircuitOpenHandler - Callback for circuit breaker open events\n */\nexport type CircuitOpenHandler = (\n circuitName: string,\n breaker: CircuitBreaker\n) => void;\n\n/**\n * Default retry policy for resilient execution\n */\nexport const DEFAULT_RETRY_POLICY: RetryPolicy = {\n maxAttempts: 3,\n backoff: {\n type: 'exponential',\n baseDelayMs: 1000,\n maxDelayMs: 30000,\n jitter: true,\n },\n retryableErrors: [\n 'ECONNREFUSED',\n 'ETIMEDOUT',\n 'ENOTFOUND',\n 'timeout',\n 'network',\n 'Process execution timeout',\n ],\n retryableExitCodes: [1, 137], // Generic error, SIGKILL\n};\n", "/**\n * Resilient Executor Implementation\n *\n * Implements resilient task execution with retry logic and circuit breakers.\n * Wraps the Engine Layer with fault tolerance mechanisms.\n *\n * @module execution/resilience/resilient-executor\n */\n\nimport type { IExecutionEngine } from \"../engine/engine.js\";\nimport type { ExecutionTask, ExecutionResult } from \"../engine/types.js\";\nimport type { IResilientExecutor } from \"./executor.js\";\nimport type {\n RetryPolicy,\n CircuitBreaker,\n RetryMetrics,\n ResilientExecutionResult,\n ExecutionAttempt,\n RetryAttemptHandler,\n CircuitOpenHandler,\n} from \"./types.js\";\n\nimport { CircuitBreakerManager } from \"./circuit-breaker.js\";\nimport {\n calculateBackoff,\n isRetryableResult,\n sleep,\n createAttempt,\n} from \"./retry.js\";\nimport { DEFAULT_RETRY_POLICY } from \"./types.js\";\n\n/**\n * ResilientExecutor - Main implementation of resilient task execution\n *\n * Provides retry logic and circuit breaker protection for task execution.\n * Wraps an IExecutionEngine with fault tolerance mechanisms.\n *\n * @example\n * ```typescript\n * const engine = new SimpleExecutionEngine(processManager);\n * const executor = new ResilientExecutor(engine);\n *\n * const task: ExecutionTask = {\n * id: 'task-1',\n * type: 'issue',\n * prompt: 'Fix the bug in authentication',\n * workDir: '/path/to/project',\n * priority: 0,\n * dependencies: [],\n * createdAt: new Date(),\n * config: {},\n * };\n *\n * const result = await executor.executeTask(task);\n * console.log(`Completed after ${result.totalAttempts} attempts`);\n * ```\n */\nexport class ResilientExecutor implements IResilientExecutor {\n private _engine: IExecutionEngine;\n private _circuitManager: CircuitBreakerManager;\n private _defaultPolicy: RetryPolicy;\n private _metrics: RetryMetrics;\n\n // Event handlers\n private _retryHandlers: RetryAttemptHandler[] = [];\n private _circuitOpenHandlers: CircuitOpenHandler[] = [];\n\n constructor(\n engine: IExecutionEngine,\n defaultPolicy: RetryPolicy = DEFAULT_RETRY_POLICY\n ) {\n this._engine = engine;\n this._circuitManager = new CircuitBreakerManager();\n this._defaultPolicy = defaultPolicy;\n this._metrics = {\n totalRetries: 0,\n successfulRetries: 0,\n failedRetries: 0,\n averageAttemptsToSuccess: 0,\n circuitBreakers: new Map<string, CircuitBreaker>(),\n };\n }\n\n /**\n * Execute a single task with retry and circuit breaker protection\n */\n async executeTask(\n task: ExecutionTask,\n policy?: RetryPolicy\n ): Promise<ResilientExecutionResult> {\n const retryPolicy = policy || this._defaultPolicy;\n const circuitBreakerName = task.type; // Use task type as circuit breaker name\n const attempts: ExecutionAttempt[] = [];\n\n // Get or create circuit breaker for this task type\n const breaker = this._circuitManager.getOrCreate(circuitBreakerName, {\n failureThreshold: 5,\n successThreshold: 2,\n timeout: 60000,\n });\n\n // Attempt execution with retries\n for (\n let attemptNumber = 1;\n attemptNumber <= retryPolicy.maxAttempts;\n attemptNumber++\n ) {\n // Check circuit breaker before execution\n if (!this._circuitManager.canExecute(circuitBreakerName)) {\n // Call circuit open handlers\n this._circuitOpenHandlers.forEach((handler) => {\n handler(circuitBreakerName, breaker);\n });\n\n // Return result indicating circuit breaker triggered\n const circuitOpenResult: ResilientExecutionResult = {\n taskId: task.id,\n executionId: \"circuit-breaker-open\",\n success: false,\n exitCode: -1,\n output: \"\",\n error: `Circuit breaker is open for ${circuitBreakerName}`,\n startedAt: new Date(),\n completedAt: new Date(),\n duration: 0,\n attempts: [],\n totalAttempts: 0,\n finalAttempt: createAttempt(0, false, {\n error: new Error(\n `Circuit breaker is open for ${circuitBreakerName}`\n ),\n }),\n circuitBreakerTriggered: true,\n };\n return circuitOpenResult;\n }\n\n const attemptStart = new Date();\n\n try {\n // Submit task to engine and wait for result\n const taskId = await this._engine.submitTask(task);\n const result = await this._engine.waitForTask(taskId);\n\n const attemptDuration = Date.now() - attemptStart.getTime();\n\n // Check if execution was successful\n if (result.success) {\n // Success! Record in circuit breaker and return\n this._circuitManager.recordSuccess(circuitBreakerName);\n\n const successAttempt = createAttempt(attemptNumber, true, {\n exitCode: result.exitCode,\n duration: attemptDuration,\n });\n attempts.push(successAttempt);\n\n // Update metrics for successful retry\n if (attemptNumber > 1) {\n this._metrics.successfulRetries++;\n this._updateAverageAttempts(attemptNumber);\n }\n\n return this._createResilientResult(result, attempts);\n }\n\n // Execution completed but failed - check if retryable\n const isRetryable = isRetryableResult(result, retryPolicy);\n const willRetry =\n isRetryable && attemptNumber < retryPolicy.maxAttempts;\n\n const failureAttempt = createAttempt(attemptNumber, false, {\n error: new Error(result.error || \"Task failed\"),\n exitCode: result.exitCode,\n duration: attemptDuration,\n willRetry,\n });\n\n if (willRetry) {\n const backoffDelay = calculateBackoff(\n attemptNumber + 1,\n retryPolicy.backoff\n );\n failureAttempt.nextRetryAt = new Date(Date.now() + backoffDelay);\n\n // Call retry attempt handlers\n this._retryHandlers.forEach((handler) => {\n handler(task.id, failureAttempt);\n });\n\n this._metrics.totalRetries++;\n attempts.push(failureAttempt);\n\n // Wait before retry\n await sleep(backoffDelay);\n continue;\n }\n\n // Not retryable or max attempts reached - record failure\n attempts.push(failureAttempt);\n this._circuitManager.recordFailure(\n circuitBreakerName,\n new Error(result.error || \"Task failed\")\n );\n\n // Update metrics for failed retries\n // Count all retry attempts in this failed task\n if (attemptNumber > 1) {\n this._metrics.failedRetries += attemptNumber - 1;\n }\n\n return this._createResilientResult(result, attempts);\n } catch (error) {\n // Engine execution error (not a task failure)\n const attemptDuration = Date.now() - attemptStart.getTime();\n const err = error instanceof Error ? error : new Error(String(error));\n\n const isRetryable = retryPolicy.retryableErrors.some((pattern) =>\n err.message.includes(pattern)\n );\n const willRetry =\n isRetryable && attemptNumber < retryPolicy.maxAttempts;\n\n const errorAttempt = createAttempt(attemptNumber, false, {\n error: err,\n duration: attemptDuration,\n willRetry,\n });\n\n if (willRetry) {\n const backoffDelay = calculateBackoff(\n attemptNumber + 1,\n retryPolicy.backoff\n );\n errorAttempt.nextRetryAt = new Date(Date.now() + backoffDelay);\n\n // Call retry attempt handlers\n this._retryHandlers.forEach((handler) => {\n handler(task.id, errorAttempt);\n });\n\n this._metrics.totalRetries++;\n attempts.push(errorAttempt);\n\n // Wait before retry\n await sleep(backoffDelay);\n continue;\n }\n\n // Not retryable or max attempts reached\n attempts.push(errorAttempt);\n this._circuitManager.recordFailure(circuitBreakerName, err);\n\n // Update metrics for failed retries\n // Count all retry attempts in this failed task\n if (attemptNumber > 1) {\n this._metrics.failedRetries += attemptNumber - 1;\n }\n\n throw err;\n }\n }\n\n // Should never reach here, but TypeScript needs it\n throw new Error(\"Unexpected state: exceeded max attempts without return\");\n }\n\n /**\n * Execute multiple tasks with retry and circuit breaker protection\n */\n async executeTasks(\n tasks: ExecutionTask[],\n policy?: RetryPolicy\n ): Promise<ResilientExecutionResult[]> {\n // Execute all tasks in parallel\n const promises = tasks.map((task) => this.executeTask(task, policy));\n return Promise.all(promises);\n }\n\n /**\n * Get circuit breaker by name\n */\n getCircuitBreaker(name: string): CircuitBreaker | null {\n return this._circuitManager.get(name);\n }\n\n /**\n * Reset a circuit breaker to closed state\n */\n resetCircuitBreaker(name: string): void {\n this._circuitManager.reset(name);\n }\n\n /**\n * Get aggregate retry metrics\n */\n getRetryMetrics(): RetryMetrics {\n // Update circuit breakers in metrics\n this._metrics.circuitBreakers = this._circuitManager.getAll();\n\n // Return defensive copy\n return {\n ...this._metrics,\n circuitBreakers: new Map(this._metrics.circuitBreakers),\n };\n }\n\n /**\n * Register handler for retry attempt events\n */\n onRetryAttempt(handler: RetryAttemptHandler): void {\n this._retryHandlers.push(handler);\n }\n\n /**\n * Register handler for circuit breaker open events\n */\n onCircuitOpen(handler: CircuitOpenHandler): void {\n this._circuitOpenHandlers.push(handler);\n }\n\n /**\n * Helper to create ResilientExecutionResult from engine result\n * @private\n */\n private _createResilientResult(\n result: ExecutionResult,\n attempts: ExecutionAttempt[]\n ): ResilientExecutionResult {\n const finalAttempt = attempts[attempts.length - 1];\n\n return {\n ...result,\n attempts,\n totalAttempts: attempts.length,\n finalAttempt,\n failureReason: result.success ? undefined : result.error,\n };\n }\n\n /**\n * Helper to update average attempts to success metric\n * @private\n */\n private _updateAverageAttempts(attemptCount: number): void {\n const totalSuccessful = this._metrics.successfulRetries;\n const previousAverage = this._metrics.averageAttemptsToSuccess;\n const previousTotal = previousAverage * (totalSuccessful - 1);\n\n this._metrics.averageAttemptsToSuccess =\n (previousTotal + attemptCount) / totalSuccessful;\n }\n}\n", "/**\n * Workflow Utility Functions\n *\n * Helper functions for template rendering, path extraction, and ID generation.\n *\n * @module execution/workflow/utils\n */\n\n/**\n * Generate a unique ID with a given prefix\n *\n * @param prefix - Prefix for the ID (e.g., 'execution', 'checkpoint')\n * @returns Unique ID string\n *\n * @example\n * ```typescript\n * const id = generateId('execution');\n * // Returns: 'execution-1234567890-abc123'\n * ```\n */\nexport function generateId(prefix = 'id'): string {\n const timestamp = Date.now();\n const random = Math.random().toString(36).substring(2, 9);\n return `${prefix}-${timestamp}-${random}`;\n}\n\n/**\n * Render a template string by replacing {{variable}} placeholders with context values\n *\n * @param template - Template string with {{variable}} placeholders\n * @param context - Context object containing variable values\n * @returns Rendered string with variables replaced\n *\n * @example\n * ```typescript\n * const context = { name: 'World', greeting: 'Hello' };\n * const result = renderTemplate('{{greeting}} {{name}}!', context);\n * // Returns: 'Hello World!'\n * ```\n */\nexport function renderTemplate(\n template: string,\n context: Record<string, any>\n): string {\n let rendered = template;\n\n // Replace all {{variable}} placeholders with values from context\n const placeholderRegex = /\\{\\{(\\w+(?:\\.\\w+)*)\\}\\}/g;\n\n rendered = rendered.replace(placeholderRegex, (match, path) => {\n const value = extractValue(context, path);\n\n // If value is undefined or null, leave placeholder as-is\n if (value === undefined || value === null) {\n return match;\n }\n\n // Convert value to string\n return String(value);\n });\n\n return rendered;\n}\n\n/**\n * Extract a value from an object using a dot-notation path\n *\n * @param obj - Object to extract value from\n * @param path - Dot-notation path (e.g., 'user.profile.name')\n * @returns Extracted value or undefined if path doesn't exist\n *\n * @example\n * ```typescript\n * const obj = { user: { profile: { name: 'Alice' } } };\n * const name = extractValue(obj, 'user.profile.name');\n * // Returns: 'Alice'\n * ```\n */\nexport function extractValue(obj: any, path: string): any {\n if (obj === null || obj === undefined) {\n return undefined;\n }\n\n // Handle simple paths (no dots)\n if (!path.includes('.')) {\n return obj[path];\n }\n\n // Split path and traverse\n const parts = path.split('.');\n let value: any = obj;\n\n for (const part of parts) {\n if (value === null || value === undefined) {\n return undefined;\n }\n value = value[part];\n }\n\n return value;\n}\n\n/**\n * Merge two context objects, with the second overriding the first\n *\n * @param base - Base context object\n * @param updates - Updates to merge into base\n * @returns New merged context object\n *\n * @example\n * ```typescript\n * const base = { a: 1, b: 2 };\n * const updates = { b: 3, c: 4 };\n * const merged = mergeContext(base, updates);\n * // Returns: { a: 1, b: 3, c: 4 }\n * ```\n */\nexport function mergeContext(\n base: Record<string, any>,\n updates: Record<string, any>\n): Record<string, any> {\n return {\n ...base,\n ...updates,\n };\n}\n\n/**\n * Evaluate a condition string in the context\n *\n * @param condition - Condition string to evaluate (e.g., '{{isEnabled}}')\n * @param context - Context object\n * @returns Boolean result of condition\n *\n * @example\n * ```typescript\n * const context = { isEnabled: true };\n * const result = evaluateCondition('{{isEnabled}}', context);\n * // Returns: true\n * ```\n */\nexport function evaluateCondition(\n condition: string,\n context: Record<string, any>\n): boolean {\n // Render the condition template\n const rendered = renderTemplate(condition, context);\n\n // If template still contains placeholders, variables are missing - treat as false (fail-safe)\n if (rendered.includes('{{') && rendered.includes('}}')) {\n return false;\n }\n\n // Simple boolean evaluation\n // True if: 'true', '1', non-empty string\n // False if: 'false', '0', '', or falsy value\n if (rendered === 'true' || rendered === '1') {\n return true;\n }\n if (rendered === 'false' || rendered === '0' || rendered === '') {\n return false;\n }\n\n // For other values, check if they're truthy\n return Boolean(rendered);\n}\n\n/**\n * Create initial workflow context\n *\n * @param initialValues - Initial context values\n * @returns New context object\n */\nexport function createContext(\n initialValues: Record<string, any> = {}\n): Record<string, any> {\n return {\n ...initialValues,\n };\n}\n", "/**\n * Linear Workflow Orchestrator Implementation\n *\n * Executes workflow steps sequentially with state management and checkpointing.\n *\n * @module execution/workflow/linear-orchestrator\n */\n\nimport type {\n IWorkflowOrchestrator,\n IWorkflowStorage,\n} from \"./orchestrator.js\";\nimport type { IResilientExecutor } from \"../resilience/executor.js\";\nimport type { ResilientExecutionResult } from \"../resilience/types.js\";\nimport type { ExecutionTask } from \"../engine/types.js\";\nimport type {\n WorkflowDefinition,\n WorkflowStep,\n WorkflowExecution,\n WorkflowCheckpoint,\n WorkflowResult,\n StepStatus,\n WorkflowStartHandler,\n WorkflowCompleteHandler,\n WorkflowFailedHandler,\n StepStartHandler,\n StepCompleteHandler,\n StepFailedHandler,\n WorkflowCheckpointHandler,\n WorkflowResumeHandler,\n WorkflowPauseHandler,\n WorkflowCancelHandler,\n} from \"./types.js\";\nimport {\n renderTemplate,\n generateId,\n extractValue,\n evaluateCondition,\n} from \"./utils.js\";\nimport type { AgUiEventAdapter } from \"../output/ag-ui-adapter.js\";\nimport type { ExecutionLifecycleService } from \"../../services/execution-lifecycle.js\";\n\n/**\n * LinearOrchestrator - Sequential workflow execution with state management\n *\n * Implements the IWorkflowOrchestrator interface to provide:\n * - Sequential step execution\n * - State persistence via checkpoints\n * - Crash recovery and resumption\n * - Event-driven monitoring\n */\nexport class LinearOrchestrator implements IWorkflowOrchestrator {\n // Internal state\n private _executions = new Map<string, WorkflowExecution>();\n private _cleanedUpExecutions = new Set<string>(); // Track which executions have been cleaned up\n private _storage?: IWorkflowStorage;\n private _executor: IResilientExecutor;\n private _agUiAdapter?: AgUiEventAdapter;\n private _lifecycleService?: ExecutionLifecycleService;\n\n // Event handlers\n private _workflowStartHandlers: WorkflowStartHandler[] = [];\n private _workflowCompleteHandlers: WorkflowCompleteHandler[] = [];\n private _workflowFailedHandlers: WorkflowFailedHandler[] = [];\n private _stepStartHandlers: StepStartHandler[] = [];\n private _stepCompleteHandlers: StepCompleteHandler[] = [];\n private _stepFailedHandlers: StepFailedHandler[] = [];\n private _checkpointHandlers: WorkflowCheckpointHandler[] = [];\n private _resumeHandlers: WorkflowResumeHandler[] = [];\n private _pauseHandlers: WorkflowPauseHandler[] = [];\n private _cancelHandlers: WorkflowCancelHandler[] = [];\n\n /**\n * Create a new LinearOrchestrator\n *\n * @param executor - Resilient executor for running tasks\n * @param storage - Optional storage for checkpoints\n * @param agUiAdapter - Optional AG-UI event adapter for real-time event streaming\n * @param lifecycleService - Optional execution lifecycle service for worktree management\n */\n constructor(\n executor: IResilientExecutor,\n storage?: IWorkflowStorage,\n agUiAdapter?: AgUiEventAdapter,\n lifecycleService?: ExecutionLifecycleService\n ) {\n this._executor = executor;\n this._storage = storage;\n this._agUiAdapter = agUiAdapter;\n this._lifecycleService = lifecycleService;\n }\n\n /**\n * Start a new workflow execution\n *\n * @param workflow - Workflow definition to execute\n * @param workDir - Working directory for task execution\n * @param options - Execution options (executionId is required)\n * @returns Promise resolving to execution ID\n */\n async startWorkflow(\n workflow: WorkflowDefinition,\n workDir: string,\n options: {\n executionId: string;\n checkpointInterval?: number;\n initialContext?: Record<string, any>;\n }\n ): Promise<string> {\n // 1. Create execution\n const execution: WorkflowExecution = {\n executionId: options.executionId,\n workflowId: workflow.id,\n definition: workflow,\n status: \"pending\",\n currentStepIndex: 0,\n context: options?.initialContext || workflow.initialContext || {},\n stepResults: [],\n startedAt: new Date(),\n };\n\n // 2. Store execution\n this._executions.set(execution.executionId, execution);\n\n // 3. Start execution in background (non-blocking)\n this._executeWorkflow(\n workflow,\n execution,\n workDir,\n options?.checkpointInterval\n ).catch((error) => {\n execution.status = \"failed\";\n execution.completedAt = new Date();\n execution.error = error.message;\n\n // Emit workflow failed event\n this._workflowFailedHandlers.forEach((handler) => {\n handler(execution.executionId, error);\n });\n\n // Emit AG-UI RUN_ERROR event\n if (this._agUiAdapter) {\n this._agUiAdapter.emitRunError(error.message, error.stack);\n }\n\n // Cleanup execution resources (worktree)\n this._cleanupExecution(execution).catch(() => {\n // Error already logged in _cleanupExecution\n });\n });\n\n // 4. Return execution ID immediately\n return execution.executionId;\n }\n\n /**\n * Resume a workflow from a checkpoint\n *\n * @param executionId - Execution ID to resume\n * @param options - Resume options\n * @returns Promise resolving to execution ID\n */\n async resumeWorkflow(\n executionId: string,\n options?: {\n checkpointInterval?: number;\n }\n ): Promise<string> {\n if (!this._storage) {\n throw new Error(\"Cannot resume workflow: no storage configured\");\n }\n\n // Load checkpoint\n const checkpoint = await this._storage.loadCheckpoint(executionId);\n if (!checkpoint) {\n throw new Error(`No checkpoint found for execution ${executionId}`);\n }\n\n // Restore execution state\n const execution: WorkflowExecution = {\n workflowId: checkpoint.workflowId,\n executionId: checkpoint.executionId,\n definition: checkpoint.definition,\n status: \"running\",\n currentStepIndex: checkpoint.state.currentStepIndex,\n context: { ...checkpoint.state.context },\n stepResults: [...checkpoint.state.stepResults],\n startedAt: checkpoint.state.startedAt,\n resumedAt: new Date(),\n };\n\n this._executions.set(executionId, execution);\n\n // Emit resume event\n this._resumeHandlers.forEach((handler) => {\n handler(executionId, checkpoint);\n });\n\n // Get workDir from workflow metadata or use default\n const workDir = checkpoint.definition.metadata?.workDir || process.cwd();\n\n // Continue execution from saved point\n this._executeWorkflow(\n checkpoint.definition,\n execution,\n workDir as string,\n options?.checkpointInterval\n ).catch((error) => {\n execution.status = \"failed\";\n execution.completedAt = new Date();\n execution.error = error.message;\n\n // Emit workflow failed event\n this._workflowFailedHandlers.forEach((handler) => {\n handler(execution.executionId, error);\n });\n\n // Emit AG-UI RUN_ERROR event\n if (this._agUiAdapter) {\n this._agUiAdapter.emitRunError(error.message, error.stack);\n }\n\n // Cleanup execution resources (worktree)\n this._cleanupExecution(execution).catch(() => {\n // Error already logged in _cleanupExecution\n });\n });\n\n return executionId;\n }\n\n /**\n * Pause a running workflow\n *\n * @param executionId - Execution ID to pause\n */\n async pauseWorkflow(executionId: string): Promise<void> {\n const execution = this._executions.get(executionId);\n if (!execution) {\n return; // Silently ignore non-existent executions\n }\n\n if (execution.status !== \"running\") {\n throw new Error(`Cannot pause workflow in ${execution.status} state`);\n }\n\n execution.status = \"paused\";\n execution.pausedAt = new Date();\n\n // Wait for any currently executing step to complete and update state\n // This prevents race condition where step is submitted to executor but result not yet saved\n // 250ms is enough for typical step execution (MockResilientExecutor uses 200ms in tests)\n await new Promise((resolve) => setTimeout(resolve, 250));\n\n // Now save checkpoint with current state\n if (this._storage) {\n await this._saveCheckpoint(execution);\n }\n\n // Emit pause event\n this._pauseHandlers.forEach((handler) => {\n handler(executionId);\n });\n }\n\n /**\n * Cancel a running workflow\n *\n * @param executionId - Execution ID to cancel\n */\n async cancelWorkflow(executionId: string): Promise<void> {\n const execution = this._executions.get(executionId);\n if (!execution) {\n return; // Silently ignore non-existent executions\n }\n\n if ([\"completed\", \"cancelled\"].includes(execution.status)) {\n return; // Already done\n }\n\n execution.status = \"cancelled\";\n execution.completedAt = new Date();\n\n // Create final checkpoint (currentStepIndex is already up-to-date)\n if (this._storage) {\n await this._saveCheckpoint(execution);\n }\n\n // Emit cancel event\n this._cancelHandlers.forEach((handler) => {\n handler(executionId);\n });\n\n // Cleanup execution resources (worktree)\n await this._cleanupExecution(execution);\n }\n\n /**\n * Get current execution state\n *\n * @param executionId - Execution ID to query\n * @returns Execution state or null if not found\n */\n getExecution(executionId: string): WorkflowExecution | null {\n return this._executions.get(executionId) || null;\n }\n\n /**\n * Get status of a specific step\n *\n * @param executionId - Execution ID\n * @param stepId - Step ID to query\n * @returns Step status or null if not found\n */\n getStepStatus(executionId: string, stepId: string): StepStatus | null {\n const execution = this._executions.get(executionId);\n if (!execution) {\n return null;\n }\n\n // Find step index\n const stepIndex = execution.definition.steps.findIndex(\n (s) => s.id === stepId\n );\n if (stepIndex === -1) {\n return null;\n }\n\n // Determine status based on execution state\n let status: StepStatus[\"status\"];\n const result = execution.stepResults[stepIndex];\n\n // Check if result exists first (step has been executed)\n if (result !== undefined) {\n // Step has a result, so it's either completed or failed\n status = result.success ? \"completed\" : \"failed\";\n } else if (stepIndex === execution.currentStepIndex) {\n // Currently executing (no result yet)\n status = \"running\";\n } else {\n // Not yet executed\n status = \"pending\";\n }\n\n return {\n stepId,\n status,\n result,\n attempts: 1, // TODO: Track actual attempts\n };\n }\n\n /**\n * Wait for workflow to complete\n *\n * @param executionId - Execution ID to wait for\n * @returns Promise resolving to workflow execution\n */\n async waitForWorkflow(executionId: string): Promise<WorkflowExecution> {\n const execution = this._executions.get(executionId);\n if (!execution) {\n throw new Error(`Workflow execution ${executionId} not found`);\n }\n\n // If already completed/failed/cancelled, return immediately\n if ([\"completed\", \"failed\", \"cancelled\"].includes(execution.status)) {\n return execution;\n }\n\n // Wait for completion by polling\n return new Promise((resolve, reject) => {\n const checkInterval = setInterval(() => {\n const current = this._executions.get(executionId);\n if (!current) {\n clearInterval(checkInterval);\n reject(new Error(`Workflow execution ${executionId} not found`));\n return;\n }\n\n if ([\"completed\", \"failed\", \"cancelled\"].includes(current.status)) {\n clearInterval(checkInterval);\n resolve(current);\n }\n }, 100);\n\n // Timeout after 5 minutes\n setTimeout(() => {\n clearInterval(checkInterval);\n reject(new Error(`Timeout waiting for workflow ${executionId}`));\n }, 300000);\n });\n }\n\n /**\n * List all checkpoints for a workflow\n *\n * @param workflowId - Optional workflow ID to filter by\n * @returns Promise resolving to list of checkpoints\n */\n async listCheckpoints(workflowId?: string): Promise<WorkflowCheckpoint[]> {\n if (!this._storage) {\n return [];\n }\n\n return this._storage.listCheckpoints(workflowId);\n }\n\n /**\n * Register handler for workflow start events\n */\n onWorkflowStart(handler: WorkflowStartHandler): void {\n this._workflowStartHandlers.push(handler);\n }\n\n /**\n * Register handler for workflow completion events\n */\n onWorkflowComplete(handler: WorkflowCompleteHandler): void {\n this._workflowCompleteHandlers.push(handler);\n }\n\n /**\n * Register handler for workflow failure events\n */\n onWorkflowFailed(handler: WorkflowFailedHandler): void {\n this._workflowFailedHandlers.push(handler);\n }\n\n /**\n * Register handler for step start events\n */\n onStepStart(handler: StepStartHandler): void {\n this._stepStartHandlers.push(handler);\n }\n\n /**\n * Register handler for step completion events\n */\n onStepComplete(handler: StepCompleteHandler): void {\n this._stepCompleteHandlers.push(handler);\n }\n\n /**\n * Register handler for step failure events\n */\n onStepFailed(handler: StepFailedHandler): void {\n this._stepFailedHandlers.push(handler);\n }\n\n /**\n * Register handler for checkpoint events\n */\n onCheckpoint(handler: WorkflowCheckpointHandler): void {\n this._checkpointHandlers.push(handler);\n }\n\n /**\n * Register handler for resume events\n */\n onResume(handler: WorkflowResumeHandler): void {\n this._resumeHandlers.push(handler);\n }\n\n /**\n * Register handler for pause events\n */\n onPause(handler: WorkflowPauseHandler): void {\n this._pauseHandlers.push(handler);\n }\n\n /**\n * Register handler for cancel events\n */\n onCancel(handler: WorkflowCancelHandler): void {\n this._cancelHandlers.push(handler);\n }\n\n /**\n * Execute workflow (main execution loop)\n *\n * @param workflow - Workflow definition\n * @param execution - Workflow execution state\n * @param workDir - Working directory for task execution\n * @param checkpointInterval - Optional checkpoint interval (in steps)\n * @returns Promise that resolves when workflow completes\n * @private\n */\n private async _executeWorkflow(\n workflow: WorkflowDefinition,\n execution: WorkflowExecution,\n workDir: string,\n checkpointInterval?: number\n ): Promise<void> {\n // 1. Set status to running\n execution.status = \"running\";\n\n // Emit workflow start event\n this._workflowStartHandlers.forEach((handler) => {\n handler(execution.executionId, workflow.id);\n });\n\n // Emit AG-UI RUN_STARTED event\n if (this._agUiAdapter) {\n this._agUiAdapter.emitRunStarted({\n workflowId: workflow.id,\n executionId: execution.executionId,\n });\n }\n\n // 2. Execute steps sequentially\n for (let i = execution.currentStepIndex; i < workflow.steps.length; i++) {\n const step = workflow.steps[i];\n\n // Check if paused or cancelled before starting each step\n // Note: Status can be changed externally via pauseWorkflow/cancelWorkflow\n if ([\"paused\", \"cancelled\"].includes(execution.status)) {\n return;\n }\n\n // Skip steps that have already been executed (for resumed workflows)\n if (execution.stepResults[i] && execution.stepResults[i].success) {\n // Update currentStepIndex to skip completed step\n execution.currentStepIndex = i + 1;\n continue;\n }\n\n // Check dependencies\n if (!this._areDependenciesMet(step, execution)) {\n const error = new Error(`Dependencies not met for step ${step.id}`);\n\n // Emit step failed event\n this._stepFailedHandlers.forEach((handler) => {\n handler(execution.executionId, step.id, error);\n });\n\n if (!workflow.config?.continueOnStepFailure) {\n execution.status = \"failed\";\n execution.completedAt = new Date();\n execution.error = error.message;\n\n // Emit workflow failed event\n this._workflowFailedHandlers.forEach((handler) => {\n handler(execution.executionId, error);\n });\n\n // Cleanup execution resources (worktree)\n await this._cleanupExecution(execution);\n return;\n }\n // Update currentStepIndex to point to next step before continuing\n execution.currentStepIndex = i + 1;\n continue;\n }\n\n // Check condition\n if (!this._shouldExecuteStep(step, execution.context)) {\n // Step condition not met, skip it\n // Update currentStepIndex to point to next step before continuing\n execution.currentStepIndex = i + 1;\n continue;\n }\n\n // Emit step start event\n this._stepStartHandlers.forEach((handler) => {\n handler(execution.executionId, step.id, i);\n });\n\n // Emit AG-UI STEP_STARTED event\n if (this._agUiAdapter) {\n this._agUiAdapter.emitStepStarted(\n step.id,\n step.taskType || `Step ${i + 1}`\n );\n }\n\n // Execute step\n try {\n const result = await this._executeStep(step, execution, workDir);\n\n // Store result\n execution.stepResults[i] = result;\n\n // Check if step failed\n if (!result.success) {\n const error = new Error(result.error || `Step ${step.id} failed`);\n\n // Emit step failed event\n this._stepFailedHandlers.forEach((handler) => {\n handler(execution.executionId, step.id, error);\n });\n\n if (!workflow.config?.continueOnStepFailure) {\n execution.status = \"failed\";\n execution.completedAt = new Date();\n execution.error = error.message;\n\n // Emit workflow failed event\n this._workflowFailedHandlers.forEach((handler) => {\n handler(execution.executionId, error);\n });\n\n // Cleanup execution resources (worktree)\n await this._cleanupExecution(execution);\n return;\n }\n // Update currentStepIndex to point to next step before continuing\n execution.currentStepIndex = i + 1;\n continue;\n }\n\n // Apply output mapping (only for successful steps)\n this._applyOutputMapping(step, result, execution.context);\n\n // Emit step complete event\n this._stepCompleteHandlers.forEach((handler) => {\n handler(execution.executionId, step.id, result);\n });\n\n // Emit AG-UI STEP_FINISHED event (success)\n if (this._agUiAdapter) {\n this._agUiAdapter.emitStepFinished(step.id, \"success\", result.output);\n }\n\n // Update currentStepIndex to point to next step after successful completion\n execution.currentStepIndex = i + 1;\n\n // Checkpoint if configured\n if (\n checkpointInterval &&\n this._storage &&\n (i + 1) % checkpointInterval === 0\n ) {\n await this._saveCheckpoint(execution);\n }\n } catch (error) {\n // Emit step failed event\n this._stepFailedHandlers.forEach((handler) => {\n handler(execution.executionId, step.id, error as Error);\n });\n\n // Emit AG-UI STEP_FINISHED event (error)\n if (this._agUiAdapter) {\n this._agUiAdapter.emitStepFinished(step.id, \"error\");\n }\n\n if (!workflow.config?.continueOnStepFailure) {\n execution.status = \"failed\";\n execution.completedAt = new Date();\n execution.error = (error as Error).message;\n\n // Emit workflow failed event\n this._workflowFailedHandlers.forEach((handler) => {\n handler(execution.executionId, error as Error);\n });\n\n // Emit AG-UI RUN_ERROR event\n if (this._agUiAdapter) {\n this._agUiAdapter.emitRunError(\n (error as Error).message,\n (error as Error).stack\n );\n }\n\n // Cleanup execution resources (worktree)\n await this._cleanupExecution(execution);\n return;\n }\n // Update currentStepIndex when continuing after error (for checkpoint consistency)\n execution.currentStepIndex = i + 1;\n }\n }\n\n // 3. Workflow completed\n execution.status = \"completed\";\n execution.completedAt = new Date();\n\n // 4. Emit workflow complete event\n const result: WorkflowResult = {\n executionId: execution.executionId,\n success: execution.stepResults.every((r) => r.success),\n completedSteps: execution.stepResults.filter((r) => r.success).length,\n failedSteps: execution.stepResults.filter((r) => !r.success).length,\n skippedSteps: 0, // TODO: Track skipped steps properly\n outputs: execution.context,\n duration: execution.completedAt.getTime() - execution.startedAt.getTime(),\n };\n\n this._workflowCompleteHandlers.forEach((handler) => {\n handler(execution.executionId, result);\n });\n\n // Emit AG-UI RUN_FINISHED event\n if (this._agUiAdapter) {\n this._agUiAdapter.emitRunFinished(result);\n }\n\n // 5. Cleanup execution resources (worktree)\n await this._cleanupExecution(execution);\n }\n\n /**\n * Save checkpoint to storage\n *\n * @param execution - Workflow execution to checkpoint\n * @private\n */\n private async _saveCheckpoint(execution: WorkflowExecution): Promise<void> {\n if (!this._storage) {\n return;\n }\n\n const checkpoint: WorkflowCheckpoint = {\n workflowId: execution.workflowId,\n executionId: execution.executionId,\n definition: execution.definition,\n state: {\n status: execution.status,\n currentStepIndex: execution.currentStepIndex,\n context: execution.context,\n stepResults: execution.stepResults,\n error: execution.error,\n startedAt: execution.startedAt,\n completedAt: execution.completedAt,\n },\n createdAt: new Date(),\n };\n\n await this._storage.saveCheckpoint(checkpoint);\n\n // Emit checkpoint event\n this._checkpointHandlers.forEach((handler) => {\n handler(checkpoint);\n });\n }\n\n /**\n * Clean up execution resources (worktree)\n * Ensures cleanup is called only once per execution\n *\n * @param execution - Workflow execution to cleanup\n * @private\n */\n private async _cleanupExecution(execution: WorkflowExecution): Promise<void> {\n // Skip if no lifecycle service or no execution ID\n if (!this._lifecycleService || !execution.executionId) {\n return;\n }\n\n // Skip if already cleaned up\n if (this._cleanedUpExecutions.has(execution.executionId)) {\n return;\n }\n\n // Mark as cleaned up before calling cleanup (to prevent race conditions)\n this._cleanedUpExecutions.add(execution.executionId);\n\n try {\n await this._lifecycleService.cleanupExecution(execution.executionId);\n } catch (error) {\n // Log error but don't fail\n console.error(\n `Failed to cleanup execution ${execution.executionId}:`,\n error\n );\n }\n }\n\n /**\n * Execute a single workflow step\n *\n * @param step - Workflow step to execute\n * @param execution - Current workflow execution state\n * @param workDir - Working directory for task execution\n * @returns Promise resolving to execution result\n * @private\n */\n private async _executeStep(\n step: WorkflowStep,\n execution: WorkflowExecution,\n workDir: string\n ): Promise<ResilientExecutionResult> {\n // 1. Render prompt template with context\n const prompt = renderTemplate(step.prompt, execution.context);\n\n // 2. Build execution task\n const task: ExecutionTask = {\n id: generateId(\"task\"),\n type: step.taskType,\n prompt,\n workDir,\n priority: 0,\n dependencies: [],\n createdAt: new Date(),\n config: step.taskConfig || {},\n };\n\n // 3. Execute with resilience (includes retry logic)\n const result = await this._executor.executeTask(task, step.retryPolicy);\n\n return result;\n }\n\n /**\n * Apply output mapping from step result to workflow context\n *\n * @param step - Workflow step with output mapping\n * @param result - Execution result from step\n * @param context - Workflow context to update\n * @private\n */\n private _applyOutputMapping(\n step: WorkflowStep,\n result: ResilientExecutionResult,\n context: Record<string, any>\n ): void {\n if (!step.outputMapping) {\n return;\n }\n\n // Map each output from result to context\n for (const [contextKey, resultPath] of Object.entries(step.outputMapping)) {\n const value = extractValue(result, resultPath);\n context[contextKey] = value;\n }\n }\n\n /**\n * Check if all step dependencies are met\n *\n * @param step - Workflow step to check\n * @param execution - Current workflow execution state\n * @returns True if all dependencies are met, false otherwise\n * @private\n */\n private _areDependenciesMet(\n step: WorkflowStep,\n execution: WorkflowExecution\n ): boolean {\n if (!step.dependencies || step.dependencies.length === 0) {\n return true; // No dependencies\n }\n\n // Check if all dependencies are in completed steps\n for (const depId of step.dependencies) {\n const depIndex = execution.definition.steps.findIndex(\n (s) => s.id === depId\n );\n if (depIndex === -1) {\n // Dependency not found in workflow\n return false;\n }\n\n if (depIndex >= execution.currentStepIndex) {\n // Dependency hasn't been executed yet\n return false;\n }\n\n const depResult = execution.stepResults[depIndex];\n if (!depResult || !depResult.success) {\n // Dependency failed or hasn't completed\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * Evaluate a step condition\n *\n * @param step - Workflow step with condition\n * @param context - Workflow context\n * @returns True if condition evaluates to true or no condition exists\n * @private\n */\n private _shouldExecuteStep(\n step: WorkflowStep,\n context: Record<string, any>\n ): boolean {\n if (!step.condition) {\n return true; // No condition means always execute\n }\n\n return evaluateCondition(step.condition, context);\n }\n}\n", "/**\n * Claude Code Output Processor\n *\n * Implements the IOutputProcessor interface specifically for parsing\n * Claude Code's stream-json output format in real-time.\n *\n * This processor is tailored to Claude Code's CLI output and handles its\n * specific message structure, tool calling patterns, and metadata format.\n *\n * @module execution/output/claude-code-output-processor\n */\n\nimport type {\n IOutputProcessor,\n OutputMessage,\n ProcessingMetrics,\n ToolCall,\n FileChange,\n ToolCallHandler,\n FileChangeHandler,\n ProgressHandler,\n ErrorHandler,\n MessageType,\n MessageHandler,\n UsageHandler,\n} from \"./types.js\";\n\n/**\n * ClaudeCodeOutputProcessor - Parses Claude Code's stream-json output\n *\n * This processor is specifically designed for Claude Code's output format.\n * It handles line-by-line parsing of Claude's stream-json messages,\n * extracting tool calls, file changes, and usage metrics in real-time.\n *\n * **Claude Code Specific Features:**\n * - Parses Claude's message.content array structure\n * - Handles tool_use and tool_result content types\n * - Extracts usage metrics from result messages\n * - Processes Claude-specific error formats\n *\n * @example\n * ```typescript\n * const processor = new ClaudeCodeOutputProcessor();\n *\n * processor.onToolCall((toolCall) => {\n * console.log(`Claude called tool: ${toolCall.name}`, toolCall.input);\n * });\n *\n * // Process Claude Code's stream-json output\n * await processor.processLine('{\"type\":\"assistant\",\"message\":{\"content\":[...]}}');\n * const metrics = processor.getMetrics();\n * ```\n */\nexport class ClaudeCodeOutputProcessor implements IOutputProcessor {\n // Internal state\n private _metrics: ProcessingMetrics;\n private _toolCalls: Map<string, ToolCall>;\n private _fileChanges: FileChange[];\n\n // Event handlers\n private _toolCallHandlers: ToolCallHandler[] = [];\n private _fileChangeHandlers: FileChangeHandler[] = [];\n private _progressHandlers: ProgressHandler[] = [];\n private _errorHandlers: ErrorHandler[] = [];\n private _messageHandlers: MessageHandler[] = [];\n private _usageHandlers: UsageHandler[] = [];\n\n // Processing state\n private _lineNumber = 0;\n\n constructor() {\n // Initialize metrics with empty state\n this._metrics = {\n totalMessages: 0,\n toolCalls: [],\n fileChanges: [],\n usage: {\n inputTokens: 0,\n outputTokens: 0,\n cacheTokens: 0,\n totalTokens: 0,\n cost: 0,\n provider: \"anthropic\",\n model: \"claude\", // Will be updated when we parse actual model info\n },\n errors: [],\n startedAt: new Date(),\n lastUpdate: new Date(),\n };\n\n this._toolCalls = new Map();\n this._fileChanges = [];\n }\n\n /**\n * Process a single line of Claude Code's stream-json output\n *\n * Parses the JSON line and routes to appropriate handler based on message type.\n * Handles malformed JSON gracefully and skips empty lines.\n *\n * @param line - Raw JSON line from Claude Code CLI output\n */\n async processLine(line: string): Promise<void> {\n this._lineNumber++;\n\n // Skip empty lines\n const trimmed = line.trim();\n if (!trimmed) {\n return;\n }\n\n try {\n // Parse JSON\n const data = JSON.parse(trimmed);\n\n // Detect message type (Claude-specific structure)\n const messageType = this._detectMessageType(data);\n\n // Create OutputMessage\n const message: OutputMessage = this._parseMessage(data, messageType);\n\n // Update metrics\n this._metrics.totalMessages++;\n this._metrics.lastUpdate = new Date();\n\n // Route to message-specific handlers\n switch (message.type) {\n case \"tool_use\":\n this._handleToolUse(message);\n break;\n case \"tool_result\":\n this._handleToolResult(message);\n break;\n case \"text\":\n this._handleText(message);\n break;\n case \"usage\":\n this._handleUsage(message);\n break;\n case \"error\":\n this._handleError(message);\n break;\n case \"unknown\":\n // Already tracked in metrics, no special handling needed\n break;\n }\n\n // Emit progress event\n this._emitProgress();\n } catch (error) {\n // Handle malformed JSON\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n const errorInfo = {\n message: `Failed to parse Claude Code output line ${this._lineNumber}: ${errorMessage}`,\n timestamp: new Date(),\n details: {\n line: trimmed,\n error: errorMessage,\n lineNumber: this._lineNumber,\n },\n };\n\n this._metrics.errors.push(errorInfo);\n this._emitError(errorInfo);\n }\n }\n\n /**\n * Get current processing metrics\n *\n * @returns Current aggregate metrics\n */\n getMetrics(): ProcessingMetrics {\n return {\n ...this._metrics,\n // Return current arrays (not references to internal state)\n toolCalls: [...this._metrics.toolCalls],\n fileChanges: [...this._metrics.fileChanges],\n errors: [...this._metrics.errors],\n };\n }\n\n /**\n * Get all tool calls recorded during processing\n *\n * @returns Array of all tool calls\n */\n getToolCalls(): ToolCall[] {\n return Array.from(this._toolCalls.values());\n }\n\n /**\n * Get all file changes detected during processing\n *\n * @returns Array of all file changes\n */\n getFileChanges(): FileChange[] {\n return [...this._fileChanges];\n }\n\n /**\n * Register a callback for tool call events\n *\n * @param handler - Function to call when a tool is invoked\n */\n onToolCall(handler: ToolCallHandler): void {\n this._toolCallHandlers.push(handler);\n }\n\n /**\n * Register a callback for file change events\n *\n * @param handler - Function to call when a file is modified\n */\n onFileChange(handler: FileChangeHandler): void {\n this._fileChangeHandlers.push(handler);\n }\n\n /**\n * Register a callback for progress update events\n *\n * @param handler - Function to call when metrics are updated\n */\n onProgress(handler: ProgressHandler): void {\n this._progressHandlers.push(handler);\n }\n\n /**\n * Register a callback for error events\n *\n * @param handler - Function to call when an error occurs\n */\n onError(handler: ErrorHandler): void {\n this._errorHandlers.push(handler);\n }\n\n /**\n * Register a callback for message events\n *\n * @param handler - Function to call when a text message is received\n */\n onMessage(handler: import(\"./types.js\").MessageHandler): void {\n this._messageHandlers.push(handler);\n }\n\n /**\n * Register a callback for usage metric updates\n *\n * @param handler - Function to call when usage metrics are updated\n */\n onUsage(handler: import(\"./types.js\").UsageHandler): void {\n this._usageHandlers.push(handler);\n }\n\n // ============================================================================\n // Query Methods - Data Aggregation and Filtering\n // ============================================================================\n\n /**\n * Get tool calls filtered by tool name\n *\n * @param toolName - Name of the tool to filter by (e.g., 'Bash', 'Read', 'Write')\n * @returns Array of tool calls matching the tool name\n *\n * @example\n * ```typescript\n * const bashCalls = processor.getToolCallsByName('Bash');\n * console.log(`Executed ${bashCalls.length} bash commands`);\n * ```\n */\n getToolCallsByName(toolName: string): ToolCall[] {\n return this.getToolCalls().filter((call) => call.name === toolName);\n }\n\n /**\n * Get file changes filtered by file path\n *\n * @param path - File path to filter by (exact match)\n * @returns Array of file changes to the specified path\n *\n * @example\n * ```typescript\n * const changes = processor.getFileChangesByPath('src/index.ts');\n * console.log(`File modified ${changes.length} times`);\n * ```\n */\n getFileChangesByPath(path: string): FileChange[] {\n return this.getFileChanges().filter((change) => change.path === path);\n }\n\n /**\n * Get file changes filtered by operation type\n *\n * @param operation - Operation type to filter by ('read', 'write', 'edit')\n * @returns Array of file changes with the specified operation\n *\n * @example\n * ```typescript\n * const writes = processor.getFileChangesByOperation('write');\n * console.log(`Wrote to ${writes.length} files`);\n * ```\n */\n getFileChangesByOperation(\n operation: \"read\" | \"write\" | \"edit\"\n ): FileChange[] {\n return this.getFileChanges().filter(\n (change) => change.operation === operation\n );\n }\n\n /**\n * Get only failed tool calls\n *\n * @returns Array of tool calls with status='error'\n *\n * @example\n * ```typescript\n * const failures = processor.getFailedToolCalls();\n * failures.forEach(call => {\n * console.error(`${call.name} failed:`, call.error);\n * });\n * ```\n */\n getFailedToolCalls(): ToolCall[] {\n return this.getToolCalls().filter((call) => call.status === \"error\");\n }\n\n /**\n * Get only successful tool calls\n *\n * @returns Array of tool calls with status='success'\n *\n * @example\n * ```typescript\n * const successes = processor.getSuccessfulToolCalls();\n * console.log(`${successes.length} tool calls succeeded`);\n * ```\n */\n getSuccessfulToolCalls(): ToolCall[] {\n return this.getToolCalls().filter((call) => call.status === \"success\");\n }\n\n /**\n * Get total cost of execution in USD\n *\n * @returns Total cost based on token usage\n *\n * @example\n * ```typescript\n * const cost = processor.getTotalCost();\n * console.log(`Execution cost: $${cost.toFixed(2)}`);\n * ```\n */\n getTotalCost(): number {\n return this._metrics.usage.cost || 0;\n }\n\n /**\n * Get execution summary with aggregate statistics\n *\n * Provides a high-level overview of the execution including:\n * - Tool call counts by type\n * - File operation counts by type\n * - Success rates\n * - Token usage and costs\n * - Duration\n *\n * @returns Complete execution summary\n *\n * @example\n * ```typescript\n * const summary = processor.getExecutionSummary();\n * console.log('Execution Summary:', {\n * duration: `${summary.duration}ms`,\n * cost: `$${summary.totalCost.toFixed(2)}`,\n * successRate: `${summary.successRate.toFixed(1)}%`,\n * toolCalls: summary.toolCallsByType,\n * });\n * ```\n */\n getExecutionSummary(): import(\"./types.js\").ExecutionSummary {\n const toolCalls = this.getToolCalls();\n const fileChanges = this.getFileChanges();\n\n // Calculate tool calls by type\n const toolCallsByType: Record<string, number> = {};\n for (const call of toolCalls) {\n toolCallsByType[call.name] = (toolCallsByType[call.name] || 0) + 1;\n }\n\n // Calculate file operations by type\n const fileOperationsByType: Record<string, number> = {};\n for (const change of fileChanges) {\n fileOperationsByType[change.operation] =\n (fileOperationsByType[change.operation] || 0) + 1;\n }\n\n // Calculate success rate\n const completedCalls = toolCalls.filter(\n (call) => call.status === \"success\" || call.status === \"error\"\n );\n const successfulCalls = toolCalls.filter(\n (call) => call.status === \"success\"\n );\n const successRate =\n completedCalls.length > 0\n ? (successfulCalls.length / completedCalls.length) * 100\n : 0;\n\n // Calculate duration\n const endTime = this._metrics.endedAt || new Date();\n const duration = endTime.getTime() - this._metrics.startedAt.getTime();\n\n return {\n totalMessages: this._metrics.totalMessages,\n toolCallsByType,\n fileOperationsByType,\n successRate,\n totalTokens: {\n input: this._metrics.usage.inputTokens,\n output: this._metrics.usage.outputTokens,\n cache: this._metrics.usage.cacheTokens,\n },\n totalCost: this._metrics.usage.cost || 0,\n duration,\n startTime: this._metrics.startedAt,\n endTime: this._metrics.endedAt,\n };\n }\n\n // ============================================================================\n // Private methods - Message Type Handlers\n // ============================================================================\n\n /**\n * Handle tool_use message from Claude Code\n *\n * Creates a new ToolCall entry with pending status and emits onToolCall event.\n * Also detects potential file operations based on tool name.\n *\n * @param message - Parsed tool_use message\n */\n private _handleToolUse(message: OutputMessage): void {\n if (message.type !== \"tool_use\") return;\n\n const toolCall: ToolCall = {\n id: message.id,\n name: message.name,\n input: message.input,\n status: \"pending\",\n timestamp: message.timestamp,\n };\n\n // Store in map for fast lookup\n this._toolCalls.set(toolCall.id, toolCall);\n\n // Update metrics\n this._metrics.toolCalls.push(toolCall);\n\n // Emit tool call event\n for (const handler of this._toolCallHandlers) {\n try {\n handler(toolCall);\n } catch (error) {\n console.error(\"Tool call handler error:\", error);\n }\n }\n }\n\n /**\n * Handle tool_result message from Claude Code\n *\n * Updates the corresponding ToolCall with result/error status.\n * Detects file changes from Read/Write/Edit tools and emits onFileChange event.\n *\n * @param message - Parsed tool_result message\n */\n private _handleToolResult(message: OutputMessage): void {\n if (message.type !== \"tool_result\") return;\n\n // Find the corresponding tool call\n const toolCall = this._toolCalls.get(message.toolUseId);\n if (!toolCall) {\n // Tool call not found - might be from before we started processing\n return;\n }\n\n // Update tool call status\n toolCall.status = message.isError ? \"error\" : \"success\";\n toolCall.result = message.result;\n toolCall.error = message.isError ? String(message.result) : undefined;\n toolCall.completedAt = message.timestamp;\n\n // Detect file changes from file operation tools\n const fileOperationTools = [\"Read\", \"Write\", \"Edit\", \"Glob\"];\n if (fileOperationTools.includes(toolCall.name)) {\n const fileChange = this._detectFileChange(toolCall, message);\n if (fileChange) {\n this._fileChanges.push(fileChange);\n this._metrics.fileChanges.push(fileChange);\n\n // Emit file change event\n for (const handler of this._fileChangeHandlers) {\n try {\n handler(fileChange);\n } catch (error) {\n console.error(\"File change handler error:\", error);\n }\n }\n }\n }\n }\n\n /**\n * Handle text message from Claude Code\n *\n * Emits message events to registered handlers.\n *\n * @param message - Parsed text message\n */\n private _handleText(message: OutputMessage): void {\n if (message.type !== \"text\") return;\n\n // Emit message event to all registered handlers\n for (const handler of this._messageHandlers) {\n try {\n handler(message);\n } catch (error) {\n console.error(\"Message handler error:\", error);\n }\n }\n }\n\n /**\n * Handle usage message from Claude Code\n *\n * Updates usage metrics with token counts and calculates cost.\n * Emits usage events to registered handlers.\n *\n * @param message - Parsed usage message\n */\n private _handleUsage(message: OutputMessage): void {\n if (message.type !== \"usage\") return;\n\n // Aggregate usage metrics\n this._metrics.usage.inputTokens += message.tokens.input;\n this._metrics.usage.outputTokens += message.tokens.output;\n this._metrics.usage.cacheTokens += message.tokens.cache;\n this._metrics.usage.totalTokens =\n this._metrics.usage.inputTokens + this._metrics.usage.outputTokens;\n\n // Calculate cost (Claude Sonnet 4 pricing as of 2025)\n // Input: $3/million tokens, Output: $15/million tokens, Cache: $0.30/million tokens\n const inputCost = (this._metrics.usage.inputTokens / 1_000_000) * 3.0;\n const outputCost = (this._metrics.usage.outputTokens / 1_000_000) * 15.0;\n const cacheCost = (this._metrics.usage.cacheTokens / 1_000_000) * 0.3;\n this._metrics.usage.cost = inputCost + outputCost + cacheCost;\n\n // Emit usage event to all registered handlers\n for (const handler of this._usageHandlers) {\n try {\n handler(this._metrics.usage);\n } catch (error) {\n console.error(\"Usage handler error:\", error);\n }\n }\n }\n\n /**\n * Handle error message from Claude Code\n *\n * Tracks error in metrics and emits onError event.\n *\n * @param message - Parsed error message\n */\n private _handleError(message: OutputMessage): void {\n if (message.type !== \"error\") return;\n\n const errorInfo = {\n message: message.message,\n timestamp: message.timestamp,\n details: message.details,\n };\n\n // Already added to metrics.errors in processLine's catch block if it's a parse error\n // Add this error if it's from Claude Code itself\n if (!this._metrics.errors.find((e) => e.timestamp === message.timestamp)) {\n this._metrics.errors.push(errorInfo);\n }\n\n // Emit error event\n this._emitError(errorInfo);\n }\n\n /**\n * Detect file change from a tool call\n *\n * Analyzes tool calls to file operation tools (Read/Write/Edit) and\n * extracts file change information.\n *\n * @param toolCall - The completed tool call\n * @param message - The tool result message\n * @returns FileChange object if file operation detected, null otherwise\n */\n private _detectFileChange(\n toolCall: ToolCall,\n message: OutputMessage\n ): FileChange | null {\n // Extract file path from tool input\n const filePath = toolCall.input.file_path || toolCall.input.path;\n if (!filePath || typeof filePath !== \"string\") {\n return null;\n }\n\n // Map tool name to operation\n let operation: FileChange[\"operation\"];\n switch (toolCall.name) {\n case \"Read\":\n case \"Glob\": // Glob reads files too\n operation = \"read\";\n break;\n case \"Write\":\n operation = \"write\";\n break;\n case \"Edit\":\n operation = \"edit\";\n break;\n default:\n return null;\n }\n\n return {\n path: filePath,\n operation,\n timestamp: message.timestamp,\n toolCallId: toolCall.id,\n metadata: {\n toolName: toolCall.name,\n success: toolCall.status === \"success\",\n },\n };\n }\n\n // ============================================================================\n // Private methods - Claude Code Specific Parsing\n // ============================================================================\n\n /**\n * Detect the type of message from parsed Claude Code JSON data\n *\n * Claude Code uses a specific structure with type and message.content fields.\n * This method understands Claude's output format.\n *\n * @param data - Parsed JSON object from Claude Code\n * @returns Message type\n */\n private _detectMessageType(data: any): MessageType {\n // Check for explicit type field\n if (data.type === \"error\") {\n return \"error\";\n }\n\n // Check for result message (contains usage information)\n if (data.type === \"result\" && data.usage) {\n return \"usage\";\n }\n\n // Check message content for tool_use or tool_result\n // Claude structures content as an array or single object\n if (data.message?.content) {\n const content = Array.isArray(data.message.content)\n ? data.message.content[0]\n : data.message.content;\n\n if (content?.type === \"tool_use\") {\n return \"tool_use\";\n }\n\n if (content?.type === \"tool_result\") {\n return \"tool_result\";\n }\n\n // Text content\n if (typeof content === \"string\" || content?.type === \"text\") {\n return \"text\";\n }\n }\n\n // Default to unknown\n return \"unknown\";\n }\n\n /**\n * Parse raw Claude Code JSON data into an OutputMessage\n *\n * @param data - Parsed JSON object from Claude Code\n * @param type - Detected message type\n * @returns Structured OutputMessage\n */\n private _parseMessage(data: any, type: MessageType): OutputMessage {\n const timestamp = new Date();\n\n switch (type) {\n case \"text\": {\n const content = this._extractTextContent(data);\n return {\n type: \"text\",\n content,\n timestamp,\n metadata: { raw: data, source: \"claude-code\" },\n };\n }\n\n case \"tool_use\": {\n const toolUse = this._extractToolUse(data);\n return {\n type: \"tool_use\",\n id: toolUse.id,\n name: toolUse.name,\n input: toolUse.input,\n timestamp,\n metadata: { raw: data, source: \"claude-code\" },\n };\n }\n\n case \"tool_result\": {\n const toolResult = this._extractToolResult(data);\n return {\n type: \"tool_result\",\n toolUseId: toolResult.toolUseId,\n result: toolResult.result,\n isError: toolResult.isError,\n timestamp,\n metadata: { raw: data, source: \"claude-code\" },\n };\n }\n\n case \"usage\": {\n const usage = this._extractUsage(data);\n return {\n type: \"usage\",\n tokens: usage,\n timestamp,\n metadata: { raw: data, source: \"claude-code\" },\n };\n }\n\n case \"error\": {\n return {\n type: \"error\",\n message: data.error?.message || data.message || \"Unknown error\",\n details: data.error || data,\n timestamp,\n metadata: { raw: data, source: \"claude-code\" },\n };\n }\n\n case \"unknown\":\n default: {\n return {\n type: \"unknown\",\n raw: JSON.stringify(data),\n timestamp,\n metadata: { raw: data, source: \"claude-code\" },\n };\n }\n }\n }\n\n /**\n * Extract text content from Claude Code message data\n *\n * Claude can return content as a string or an array of content blocks.\n * This method handles both formats.\n */\n private _extractTextContent(data: any): string {\n if (typeof data.message?.content === \"string\") {\n return data.message.content;\n }\n\n if (Array.isArray(data.message?.content)) {\n const textContent = data.message.content\n .filter((item: any) => item.type === \"text\" || typeof item === \"string\")\n .map((item: any) => (typeof item === \"string\" ? item : item.text))\n .join(\"\");\n return textContent;\n }\n\n return \"\";\n }\n\n /**\n * Extract tool use information from Claude Code message data\n *\n * Claude includes tool_use blocks in the message.content array.\n */\n private _extractToolUse(data: any): {\n id: string;\n name: string;\n input: Record<string, any>;\n } {\n const content = Array.isArray(data.message?.content)\n ? data.message.content.find((c: any) => c.type === \"tool_use\")\n : data.message?.content;\n\n return {\n id: content?.id || \"unknown\",\n name: content?.name || \"unknown\",\n input: content?.input || {},\n };\n }\n\n /**\n * Extract tool result information from Claude Code message data\n *\n * Claude includes tool_result blocks with execution results.\n */\n private _extractToolResult(data: any): {\n toolUseId: string;\n result: any;\n isError: boolean;\n } {\n const content = Array.isArray(data.message?.content)\n ? data.message.content.find((c: any) => c.type === \"tool_result\")\n : data.message?.content;\n\n return {\n toolUseId: content?.tool_use_id || \"unknown\",\n result: content?.content || content?.result || null,\n isError: content?.is_error || false,\n };\n }\n\n /**\n * Extract usage information from Claude Code result message\n *\n * Claude provides token usage in result messages with detailed breakdowns.\n */\n private _extractUsage(data: any): {\n input: number;\n output: number;\n cache: number;\n } {\n const usage = data.usage || {};\n return {\n input: usage.input_tokens || 0,\n output: usage.output_tokens || 0,\n cache:\n usage.cache_creation_input_tokens || usage.cache_read_input_tokens || 0,\n };\n }\n\n /**\n * Emit progress event to all registered handlers\n */\n private _emitProgress(): void {\n const metrics = this.getMetrics();\n for (const handler of this._progressHandlers) {\n try {\n handler(metrics);\n } catch (error) {\n // Ignore handler errors to prevent cascade failures\n console.error(\"Progress handler error:\", error);\n }\n }\n }\n\n /**\n * Emit error event to all registered handlers\n */\n private _emitError(error: {\n message: string;\n timestamp: Date;\n details?: any;\n }): void {\n for (const handler of this._errorHandlers) {\n try {\n handler(error);\n } catch (handlerError) {\n // Ignore handler errors to prevent cascade failures\n console.error(\"Error handler error:\", handlerError);\n }\n }\n }\n}\n", "/**\n * AG-UI Event Adapter\n *\n * Transforms SPEC-007 output processing events into AG-UI protocol events.\n * This adapter subscribes to events from IOutputProcessor and emits standardized\n * AG-UI events that can be consumed by frontends via SSE or WebSocket transports.\n *\n * @module execution/output/ag-ui-adapter\n */\n\nimport {\n EventType,\n type RunStartedEvent,\n type RunFinishedEvent,\n type RunErrorEvent,\n type StepStartedEvent,\n type StepFinishedEvent,\n type ToolCallStartEvent,\n type ToolCallArgsEvent,\n type ToolCallEndEvent,\n type ToolCallResultEvent,\n type TextMessageStartEvent,\n type TextMessageContentEvent,\n type TextMessageEndEvent,\n type StateDeltaEvent,\n type StateSnapshotEvent,\n type CustomEvent,\n type State,\n} from \"@ag-ui/core\";\n\nimport {\n IOutputProcessor,\n ToolCall,\n FileChange,\n ProcessingMetrics,\n ToolCallHandler,\n FileChangeHandler,\n ProgressHandler,\n ErrorHandler,\n} from \"./types.js\";\n\n/**\n * Event listener callback type\n *\n * Accepts any AG-UI event type from @ag-ui/core\n */\nexport type AgUiEventListener = (\n event:\n | RunStartedEvent\n | RunFinishedEvent\n | RunErrorEvent\n | StepStartedEvent\n | StepFinishedEvent\n | ToolCallStartEvent\n | ToolCallArgsEvent\n | ToolCallEndEvent\n | ToolCallResultEvent\n | TextMessageStartEvent\n | TextMessageContentEvent\n | TextMessageEndEvent\n | StateDeltaEvent\n | StateSnapshotEvent\n | CustomEvent\n) => void;\n\n/**\n * AgUiEventAdapter - Transforms SPEC-007 events to AG-UI events\n *\n * This adapter bridges the gap between the Output Processing Layer (SPEC-007)\n * and the AG-UI protocol (SPEC-009). It subscribes to output processor events\n * and emits corresponding AG-UI protocol events.\n *\n * @example\n * ```typescript\n * const adapter = new AgUiEventAdapter('run-123');\n * adapter.onEvent((event) => {\n * console.log('AG-UI event:', event);\n * // Send to SSE transport or WebSocket\n * });\n *\n * const processor = new ClaudeCodeOutputProcessor();\n * adapter.connectToProcessor(processor);\n * adapter.emitRunStarted({ model: 'claude-sonnet-4' });\n * ```\n */\nexport class AgUiEventAdapter {\n private runId: string;\n private threadId: string;\n private listeners: Set<AgUiEventListener> = new Set();\n private processor: IOutputProcessor | null = null;\n private currentState: any = {};\n private activeToolCalls: Map<\n string,\n { startTime: number; messageId: string }\n > = new Map();\n private messageCounter: number = 0;\n\n /**\n * Create a new AG-UI event adapter\n *\n * @param runId - Unique identifier for this execution run\n * @param threadId - Thread identifier (defaults to runId if not provided)\n */\n constructor(runId: string, threadId?: string) {\n this.runId = runId;\n this.threadId = threadId || runId;\n }\n\n /**\n * Connect to an output processor and subscribe to its events\n *\n * @param processor - The output processor to subscribe to\n */\n connectToProcessor(processor: IOutputProcessor): void {\n this.processor = processor;\n\n // Subscribe to all SPEC-007 events\n processor.onToolCall(this.handleToolCall.bind(this));\n processor.onFileChange(this.handleFileChange.bind(this));\n processor.onProgress(this.handleProgress.bind(this));\n processor.onError(this.handleError.bind(this));\n processor.onMessage(this.handleMessage.bind(this));\n processor.onUsage(this.handleUsage.bind(this));\n }\n\n /**\n * Register an event listener\n *\n * @param listener - Callback to invoke when AG-UI events are emitted\n */\n onEvent(listener: AgUiEventListener): void {\n this.listeners.add(listener);\n }\n\n /**\n * Remove an event listener\n *\n * @param listener - Callback to remove\n */\n offEvent(listener: AgUiEventListener): void {\n this.listeners.delete(listener);\n }\n\n /**\n * Emit RUN_STARTED event\n *\n * @param metadata - Optional run metadata (model, config, etc.)\n */\n emitRunStarted(metadata?: Record<string, any>): void {\n const event: RunStartedEvent = {\n type: EventType.RUN_STARTED,\n threadId: this.threadId,\n runId: this.runId,\n timestamp: Date.now(),\n ...(metadata && { rawEvent: metadata }),\n };\n this.emit(event);\n this.emitStateSnapshot();\n }\n\n /**\n * Emit RUN_FINISHED event\n *\n * @param result - Optional result data\n */\n emitRunFinished(result?: any): void {\n const event: RunFinishedEvent = {\n type: EventType.RUN_FINISHED,\n threadId: this.threadId,\n runId: this.runId,\n timestamp: Date.now(),\n ...(result && { result }),\n };\n this.emit(event);\n }\n\n /**\n * Emit a state snapshot with current execution state\n */\n emitStateSnapshot(): void {\n const metrics = this.processor?.getMetrics();\n\n const event: StateSnapshotEvent = {\n type: EventType.STATE_SNAPSHOT,\n timestamp: Date.now(),\n snapshot: {\n ...this.currentState,\n ...(metrics && {\n totalMessages: metrics.totalMessages,\n toolCallCount: metrics.toolCalls.length,\n fileChangeCount: metrics.fileChanges.length,\n errorCount: metrics.errors.length,\n usage: {\n inputTokens: metrics.usage.inputTokens,\n outputTokens: metrics.usage.outputTokens,\n totalTokens: metrics.usage.totalTokens,\n },\n }),\n },\n };\n this.emit(event);\n }\n\n /**\n * Handle tool call events from SPEC-007\n *\n * Transforms a single ToolCall into a sequence of AG-UI events:\n * - TOOL_CALL_START (when tool is invoked)\n * - TOOL_CALL_ARGS (with input parameters)\n * - TOOL_CALL_END (when tool completes)\n * - TOOL_CALL_RESULT (with result/error)\n */\n private handleToolCall: ToolCallHandler = (toolCall: ToolCall) => {\n const toolCallId = toolCall.id;\n const timestamp = Date.now();\n\n // Check if this is a new tool call or an update to existing one\n if (!this.activeToolCalls.has(toolCallId)) {\n // New tool call - emit START and ARGS\n const messageId = `msg-${this.messageCounter++}`;\n this.activeToolCalls.set(toolCallId, {\n startTime: Date.now(),\n messageId,\n });\n\n const startEvent: ToolCallStartEvent = {\n type: EventType.TOOL_CALL_START,\n timestamp,\n toolCallId,\n toolCallName: toolCall.name,\n };\n this.emit(startEvent);\n\n const argsEvent: ToolCallArgsEvent = {\n type: EventType.TOOL_CALL_ARGS,\n timestamp,\n toolCallId,\n delta: JSON.stringify(toolCall.input),\n };\n this.emit(argsEvent);\n }\n\n // If tool call is complete (success or error), emit END and RESULT\n if (toolCall.status === \"success\" || toolCall.status === \"error\") {\n const toolInfo = this.activeToolCalls.get(toolCallId);\n const duration = toolInfo ? Date.now() - toolInfo.startTime : undefined;\n\n const endEvent: ToolCallEndEvent = {\n type: EventType.TOOL_CALL_END,\n timestamp,\n toolCallId,\n ...(duration !== undefined && { rawEvent: { duration } }),\n };\n this.emit(endEvent);\n\n if (toolInfo) {\n const resultEvent: ToolCallResultEvent = {\n type: EventType.TOOL_CALL_RESULT,\n timestamp,\n messageId: toolInfo.messageId,\n toolCallId,\n content:\n toolCall.status === \"success\"\n ? typeof toolCall.result === \"string\"\n ? toolCall.result\n : JSON.stringify(toolCall.result)\n : toolCall.error || \"Tool call failed\",\n };\n this.emit(resultEvent);\n }\n\n this.activeToolCalls.delete(toolCallId);\n\n // Emit state delta with updated tool call count\n this.emitStateDelta({\n toolCallCount: this.processor?.getToolCalls().length || 0,\n });\n }\n };\n\n /**\n * Handle file change events from SPEC-007\n *\n * Transforms FileChange into a CUSTOM event with file operation details\n */\n private handleFileChange: FileChangeHandler = (fileChange: FileChange) => {\n const event: CustomEvent = {\n type: EventType.CUSTOM,\n timestamp: Date.now(),\n name: \"file_change\",\n value: {\n path: fileChange.path,\n operation: fileChange.operation,\n toolCallId: fileChange.toolCallId,\n changes: fileChange.changes,\n },\n };\n this.emit(event);\n\n // Emit state delta with updated file change count\n this.emitStateDelta({\n fileChangeCount: this.processor?.getFileChanges().length || 0,\n });\n };\n\n /**\n * Handle progress updates from SPEC-007\n *\n * Transforms ProcessingMetrics into STATE_DELTA events\n */\n private handleProgress: ProgressHandler = (metrics: ProcessingMetrics) => {\n this.emitStateDelta({\n totalMessages: metrics.totalMessages,\n toolCallCount: metrics.toolCalls.length,\n fileChangeCount: metrics.fileChanges.length,\n errorCount: metrics.errors.length,\n usage: {\n inputTokens: metrics.usage.inputTokens,\n outputTokens: metrics.usage.outputTokens,\n totalTokens: metrics.usage.totalTokens,\n },\n });\n };\n\n /**\n * Handle error events from SPEC-007\n *\n * Transforms errors into RUN_ERROR events\n */\n private handleError: ErrorHandler = (error: {\n message: string;\n timestamp: Date;\n details?: any;\n }) => {\n const event: RunErrorEvent = {\n type: EventType.RUN_ERROR,\n timestamp: error.timestamp.getTime(),\n message: error.message,\n ...(error.details && { rawEvent: { details: error.details } }),\n };\n this.emit(event);\n\n // Emit state delta with updated error count\n this.emitStateDelta({\n errorCount: this.processor?.getMetrics().errors.length || 0,\n });\n };\n\n /**\n * Handle message events\n *\n * Transforms text messages into TEXT_MESSAGE_* AG-UI events.\n * For now, we emit the full message content in a single event.\n */\n private handleMessage: import(\"./types.js\").MessageHandler = (\n message: import(\"./types.js\").OutputMessage\n ) => {\n if (message.type !== \"text\") return;\n\n const messageId = `msg-${this.messageCounter++}`;\n const timestamp = Date.now();\n\n // Emit TEXT_MESSAGE_START\n const startEvent: TextMessageStartEvent = {\n type: EventType.TEXT_MESSAGE_START,\n timestamp,\n messageId,\n role: \"assistant\",\n };\n this.emit(startEvent);\n\n // Emit TEXT_MESSAGE_CONTENT with the full message\n const contentEvent: TextMessageContentEvent = {\n type: EventType.TEXT_MESSAGE_CONTENT,\n timestamp,\n messageId,\n delta: message.content,\n };\n this.emit(contentEvent);\n\n // Emit TEXT_MESSAGE_END\n const endEvent: TextMessageEndEvent = {\n type: EventType.TEXT_MESSAGE_END,\n timestamp,\n messageId,\n };\n this.emit(endEvent);\n };\n\n /**\n * Handle usage metric updates\n *\n * Transforms usage metrics into CUSTOM usage events and updates state.\n */\n private handleUsage: import(\"./types.js\").UsageHandler = (\n usage: import(\"./types.js\").UsageMetrics\n ) => {\n const timestamp = Date.now();\n\n // Emit USAGE event as CUSTOM event\n const usageEvent: CustomEvent = {\n type: EventType.CUSTOM,\n timestamp,\n name: \"USAGE_UPDATE\",\n value: {\n inputTokens: usage.inputTokens,\n outputTokens: usage.outputTokens,\n cacheTokens: usage.cacheTokens,\n totalTokens: usage.totalTokens,\n cost: usage.cost,\n provider: usage.provider,\n model: usage.model,\n },\n };\n this.emit(usageEvent);\n\n // Update state with latest usage\n this.emitStateDelta({\n usage: {\n inputTokens: usage.inputTokens,\n outputTokens: usage.outputTokens,\n totalTokens: usage.totalTokens,\n },\n });\n };\n\n /**\n * Emit a state delta with partial state updates\n *\n * @param updates - Partial state updates to apply\n */\n private emitStateDelta(updates: Record<string, any>): void {\n // Update current state\n this.currentState = { ...this.currentState, ...updates };\n\n // Convert updates to JSON Patch operations\n const delta = Object.entries(updates).map(([key, value]) => ({\n op: \"replace\" as const,\n path: `/${key}`,\n value,\n }));\n\n const event: StateDeltaEvent = {\n type: EventType.STATE_DELTA,\n timestamp: Date.now(),\n delta,\n };\n this.emit(event);\n }\n\n /**\n * Emit an AG-UI event to all registered listeners\n *\n * @param event - The event to emit\n */\n private emit(\n event:\n | RunStartedEvent\n | RunFinishedEvent\n | RunErrorEvent\n | StepStartedEvent\n | StepFinishedEvent\n | ToolCallStartEvent\n | ToolCallArgsEvent\n | ToolCallEndEvent\n | ToolCallResultEvent\n | TextMessageStartEvent\n | TextMessageContentEvent\n | TextMessageEndEvent\n | StateDeltaEvent\n | StateSnapshotEvent\n | CustomEvent\n ): void {\n this.listeners.forEach((listener) => {\n try {\n listener(event);\n } catch (error) {\n console.error(\"Error in AG-UI event listener:\", error);\n }\n });\n }\n\n /**\n * Emit STEP_STARTED event for workflow step execution\n *\n * @param stepId - Unique identifier for the step (stored in rawEvent)\n * @param stepName - Human-readable name of the step\n */\n emitStepStarted(stepId: string, stepName: string): void {\n const event: StepStartedEvent = {\n type: EventType.STEP_STARTED,\n timestamp: Date.now(),\n stepName,\n rawEvent: {\n runId: this.runId,\n stepId,\n },\n };\n this.emit(event);\n }\n\n /**\n * Emit STEP_FINISHED event for workflow step completion\n *\n * @param stepId - Unique identifier for the step (stored in rawEvent)\n * @param status - Status of the step ('success' or 'error', stored in rawEvent)\n * @param output - Optional output data from the step (stored in rawEvent)\n */\n emitStepFinished(\n stepId: string,\n status: \"success\" | \"error\",\n output?: any\n ): void {\n const event: StepFinishedEvent = {\n type: EventType.STEP_FINISHED,\n timestamp: Date.now(),\n stepName: stepId, // Use stepId as stepName for now\n rawEvent: {\n runId: this.runId,\n stepId,\n status,\n ...(output && { output }),\n },\n };\n this.emit(event);\n }\n\n /**\n * Emit RUN_ERROR event when workflow execution fails\n *\n * @param message - Error message\n * @param stack - Optional error stack trace\n * @param code - Optional error code\n */\n emitRunError(message: string, stack?: string, code?: string): void {\n const event: RunErrorEvent = {\n type: EventType.RUN_ERROR,\n timestamp: Date.now(),\n message,\n ...(code && { code }),\n ...(stack && { rawEvent: { stack } }),\n };\n this.emit(event);\n }\n\n /**\n * Get current adapter state\n *\n * @returns Current state object\n */\n getState(): State {\n return { ...this.currentState };\n }\n\n /**\n * Get the run ID\n *\n * @returns The run ID this adapter is tracking\n */\n getRunId(): string {\n return this.runId;\n }\n}\n", "/**\n * AG-UI Integration Helpers\n *\n * Factory functions and helpers to wire SPEC-007 output processing with SPEC-009 AG-UI streaming.\n * Simplifies the integration between ClaudeCodeOutputProcessor and AgUiEventAdapter.\n *\n * @module execution/output/ag-ui-integration\n */\n\nimport { ClaudeCodeOutputProcessor } from './claude-code-output-processor.js';\nimport { AgUiEventAdapter } from './ag-ui-adapter.js';\nimport type { IOutputProcessor } from './types.js';\n\n/**\n * AG-UI System containing processor and adapter\n *\n * The processor handles parsing Claude Code output and emitting events.\n * The adapter transforms those events into AG-UI protocol events.\n */\nexport interface AgUiSystem {\n /** Output processor for parsing Claude Code stream-json */\n processor: IOutputProcessor;\n /** AG-UI event adapter for broadcasting events */\n adapter: AgUiEventAdapter;\n}\n\n/**\n * Create a complete AG-UI system with processor and adapter pre-wired\n *\n * This is the recommended way to create an AG-UI streaming system.\n * It automatically:\n * - Creates a ClaudeCodeOutputProcessor\n * - Creates an AgUiEventAdapter with the provided runId\n * - Wires the processor to emit events through the adapter\n *\n * @param runId - Unique identifier for this execution run\n * @param threadId - Optional thread ID (defaults to runId)\n * @returns Complete AG-UI system ready to process output\n *\n * @example\n * ```typescript\n * const { processor, adapter } = createAgUiSystem('run-123');\n *\n * // Wire adapter to transport for SSE streaming\n * transportManager.connectAdapter(adapter, 'run-123');\n *\n * // Process Claude Code output\n * await processor.processLine('{\"type\":\"assistant\",...}');\n *\n * // Events automatically flow: processor -> adapter -> transport -> SSE clients\n * ```\n */\nexport function createAgUiSystem(\n runId: string,\n threadId?: string\n): AgUiSystem {\n const processor = new ClaudeCodeOutputProcessor();\n const adapter = new AgUiEventAdapter(runId, threadId);\n\n // Wire processor events to adapter\n adapter.connectToProcessor(processor);\n\n return { processor, adapter };\n}\n\n/**\n * Manually wire an output processor to an AG-UI adapter\n *\n * Use this when you need more control over processor/adapter creation.\n * This function handles all the event handler registration.\n *\n * @param processor - Output processor to wire\n * @param adapter - AG-UI adapter to receive events\n *\n * @example\n * ```typescript\n * const processor = new ClaudeCodeOutputProcessor();\n * const adapter = new AgUiEventAdapter('run-123');\n *\n * // Manually wire them together\n * wireManually(processor, adapter);\n *\n * // Now processor events will flow to adapter\n * ```\n */\nexport function wireManually(\n processor: IOutputProcessor,\n adapter: AgUiEventAdapter\n): void {\n adapter.connectToProcessor(processor);\n}\n\n/**\n * Create an AG-UI system with custom processor implementation\n *\n * Use this when you have a custom output processor that implements IOutputProcessor.\n * The processor will be wired to a new AgUiEventAdapter.\n *\n * @param processor - Custom output processor implementation\n * @param runId - Unique identifier for this execution run\n * @param threadId - Optional thread ID (defaults to runId)\n * @returns AG-UI system with your custom processor\n *\n * @example\n * ```typescript\n * class CustomProcessor implements IOutputProcessor {\n * // ... your implementation\n * }\n *\n * const processor = new CustomProcessor();\n * const { processor, adapter } = createAgUiSystemWithProcessor(\n * processor,\n * 'run-123'\n * );\n * ```\n */\nexport function createAgUiSystemWithProcessor(\n processor: IOutputProcessor,\n runId: string,\n threadId?: string\n): AgUiSystem {\n const adapter = new AgUiEventAdapter(runId, threadId);\n adapter.connectToProcessor(processor);\n\n return { processor, adapter };\n}\n", "/**\n * Issues API routes (mapped to /api/issues)\n */\n\nimport { Router, Request, Response } from \"express\";\nimport type Database from \"better-sqlite3\";\nimport {\n getAllIssues,\n getIssueById,\n createNewIssue,\n updateExistingIssue,\n deleteExistingIssue,\n} from \"../services/issues.js\";\nimport { generateIssueId } from \"@sudocode-ai/cli/dist/id-generator.js\";\nimport { broadcastIssueUpdate } from \"../services/websocket.js\";\nimport { getSudocodeDir } from \"../utils/sudocode-dir.js\";\nimport { triggerExport, syncEntityToMarkdown } from \"../services/export.js\";\n\nexport function createIssuesRouter(db: Database.Database): Router {\n const router = Router();\n\n /**\n * GET /api/issues - List all issues\n */\n router.get(\"/\", (req: Request, res: Response) => {\n try {\n // Parse query parameters for filtering\n const options: any = {};\n\n if (req.query.status) {\n options.status = req.query.status as string;\n }\n if (req.query.priority) {\n options.priority = parseInt(req.query.priority as string, 10);\n }\n if (req.query.assignee) {\n options.assignee = req.query.assignee as string;\n }\n // Default to excluding archived unless explicitly specified\n options.archived =\n req.query.archived !== undefined\n ? req.query.archived === \"true\"\n : false;\n if (req.query.limit) {\n options.limit = parseInt(req.query.limit as string, 10);\n }\n if (req.query.offset) {\n options.offset = parseInt(req.query.offset as string, 10);\n }\n\n const issues = getAllIssues(db, options);\n\n res.json({\n success: true,\n data: issues,\n });\n } catch (error) {\n console.error(\"Error listing issues:\", error);\n res.status(500).json({\n success: false,\n data: null,\n error_data: error instanceof Error ? error.message : String(error),\n message: \"Failed to list issues\",\n });\n }\n });\n\n /**\n * GET /api/issues/:id - Get a specific issue\n */\n router.get(\"/:id\", (req: Request, res: Response) => {\n try {\n const { id } = req.params;\n const issue = getIssueById(db, id);\n\n if (!issue) {\n res.status(404).json({\n success: false,\n data: null,\n message: `Issue not found: ${id}`,\n });\n return;\n }\n\n res.json({\n success: true,\n data: issue,\n });\n } catch (error) {\n console.error(\"Error getting issue:\", error);\n res.status(500).json({\n success: false,\n data: null,\n error_data: error instanceof Error ? error.message : String(error),\n message: \"Failed to get issue\",\n });\n }\n });\n\n /**\n * POST /api/issues - Create a new issue\n */\n router.post(\"/\", (req: Request, res: Response) => {\n try {\n const { title, content, status, priority, assignee, parent_id } =\n req.body;\n\n // Validate required fields\n if (!title || typeof title !== \"string\") {\n res.status(400).json({\n success: false,\n data: null,\n message: \"Title is required and must be a string\",\n });\n return;\n }\n\n if (title.length > 500) {\n res.status(400).json({\n success: false,\n data: null,\n message: \"Title must be 500 characters or less\",\n });\n return;\n }\n\n // Generate new issue ID\n const outputDir = getSudocodeDir();\n const id = generateIssueId(db, outputDir);\n\n // Create issue using CLI operation\n const issue = createNewIssue(db, {\n id,\n title,\n content: content || \"\",\n status: status || \"open\",\n priority: priority !== undefined ? priority : 2,\n assignee: assignee || undefined,\n parent_id: parent_id || undefined,\n });\n\n // Trigger export to JSONL files\n triggerExport(db);\n\n // Sync this specific issue to its markdown file (don't wait for it)\n syncEntityToMarkdown(db, issue.id, \"issue\").catch((error) => {\n console.error(`Failed to sync issue ${issue.id} to markdown:`, error);\n });\n\n // Broadcast issue creation to WebSocket clients\n broadcastIssueUpdate(issue.id, \"created\", issue);\n\n res.status(201).json({\n success: true,\n data: issue,\n });\n } catch (error) {\n console.error(\"Error creating issue:\", error);\n res.status(500).json({\n success: false,\n data: null,\n error_data: error instanceof Error ? error.message : String(error),\n message: \"Failed to create issue\",\n });\n }\n });\n\n /**\n * PUT /api/issues/:id - Update an existing issue\n */\n router.put(\"/:id\", (req: Request, res: Response) => {\n try {\n const { id } = req.params;\n const {\n title,\n content,\n status,\n priority,\n assignee,\n parent_id,\n archived,\n } = req.body;\n\n // Validate that at least one field is provided\n if (\n title === undefined &&\n content === undefined &&\n status === undefined &&\n priority === undefined &&\n assignee === undefined &&\n parent_id === undefined &&\n archived === undefined\n ) {\n res.status(400).json({\n success: false,\n data: null,\n message: \"At least one field must be provided for update\",\n });\n return;\n }\n\n // Validate title length if provided\n if (\n title !== undefined &&\n typeof title === \"string\" &&\n title.length > 500\n ) {\n res.status(400).json({\n success: false,\n data: null,\n message: \"Title must be 500 characters or less\",\n });\n return;\n }\n\n // Build update input\n const updateInput: any = {};\n if (title !== undefined) updateInput.title = title;\n if (content !== undefined) updateInput.content = content;\n if (status !== undefined) updateInput.status = status;\n if (priority !== undefined) updateInput.priority = priority;\n if (assignee !== undefined) updateInput.assignee = assignee;\n if (parent_id !== undefined) updateInput.parent_id = parent_id;\n if (archived !== undefined) {\n updateInput.archived = archived;\n updateInput.archived_at = archived ? new Date().toISOString() : null;\n }\n\n // Update issue using CLI operation\n const issue = updateExistingIssue(db, id, updateInput);\n\n // Trigger export to JSONL files\n triggerExport(db);\n\n // Sync this specific issue to its markdown file (don't wait for it)\n syncEntityToMarkdown(db, issue.id, \"issue\").catch((error) => {\n console.error(`Failed to sync issue ${issue.id} to markdown:`, error);\n });\n\n // Broadcast issue update to WebSocket clients\n broadcastIssueUpdate(issue.id, \"updated\", issue);\n\n res.json({\n success: true,\n data: issue,\n });\n } catch (error) {\n console.error(\"Error updating issue:\", error);\n\n // Handle \"not found\" errors\n if (error instanceof Error && error.message.includes(\"not found\")) {\n res.status(404).json({\n success: false,\n data: null,\n message: error.message,\n });\n return;\n }\n\n res.status(500).json({\n success: false,\n data: null,\n error_data: error instanceof Error ? error.message : String(error),\n message: \"Failed to update issue\",\n });\n }\n });\n\n /**\n * DELETE /api/issues/:id - Delete an issue\n */\n router.delete(\"/:id\", (req: Request, res: Response) => {\n try {\n const { id } = req.params;\n\n // Check if issue exists first\n const existingIssue = getIssueById(db, id);\n if (!existingIssue) {\n res.status(404).json({\n success: false,\n data: null,\n message: `Issue not found: ${id}`,\n });\n return;\n }\n\n // Delete issue using CLI operation\n const deleted = deleteExistingIssue(db, id);\n\n if (deleted) {\n // Trigger export to JSONL files\n triggerExport(db);\n\n // Broadcast issue deletion to WebSocket clients\n broadcastIssueUpdate(id, \"deleted\", { id });\n\n res.json({\n success: true,\n data: {\n id,\n deleted: true,\n },\n });\n } else {\n res.status(500).json({\n success: false,\n data: null,\n message: \"Failed to delete issue\",\n });\n }\n } catch (error) {\n console.error(\"Error deleting issue:\", error);\n res.status(500).json({\n success: false,\n data: null,\n error_data: error instanceof Error ? error.message : String(error),\n message: \"Failed to delete issue\",\n });\n }\n });\n\n return router;\n}\n", "import { WebSocketServer, WebSocket, RawData } from \"ws\";\nimport * as http from \"http\";\nimport { randomUUID } from \"crypto\";\n\nconst LOG_CONNECTIONS = false;\n\n/**\n * WebSocket client information\n */\ninterface Client {\n id: string;\n ws: WebSocket;\n subscriptions: Set<string>; // e.g., 'issue:ISSUE-001', 'spec:SPEC-001', 'all'\n isAlive: boolean;\n connectedAt: Date;\n}\n\n/**\n * Message types that clients can send to the server\n */\ninterface ClientMessage {\n type: \"subscribe\" | \"unsubscribe\" | \"ping\";\n entity_type?: \"issue\" | \"spec\" | \"all\";\n entity_id?: string;\n}\n\n/**\n * Message types that the server sends to clients\n */\nexport interface ServerMessage {\n type:\n | \"issue_created\"\n | \"issue_updated\"\n | \"issue_deleted\"\n | \"spec_created\"\n | \"spec_updated\"\n | \"spec_deleted\"\n | \"feedback_created\"\n | \"feedback_updated\"\n | \"feedback_deleted\"\n | \"relationship_created\"\n | \"relationship_deleted\"\n | \"pong\"\n | \"error\"\n | \"subscribed\"\n | \"unsubscribed\";\n data?: any;\n message?: string;\n subscription?: string;\n}\n\n/**\n * WebSocket server manager\n */\nclass WebSocketManager {\n private wss: WebSocketServer | null = null;\n private clients: Map<string, Client> = new Map();\n private heartbeatInterval: NodeJS.Timeout | null = null;\n private readonly HEARTBEAT_INTERVAL = 30000; // 30 seconds\n\n /**\n * Initialize the WebSocket server\n */\n init(server: http.Server, path: string = \"/ws\"): void {\n if (this.wss) {\n console.warn(\"[websocket] WebSocket server already initialized\");\n return;\n }\n\n this.wss = new WebSocketServer({ server, path });\n console.log(`[websocket] WebSocket server initialized on path: ${path}`);\n\n this.wss.on(\"connection\", this.handleConnection.bind(this));\n this.startHeartbeat();\n }\n\n /**\n * Handle new WebSocket connection\n */\n private handleConnection(ws: WebSocket, _req: http.IncomingMessage): void {\n const clientId = randomUUID();\n const client: Client = {\n id: clientId,\n ws,\n subscriptions: new Set(),\n isAlive: true,\n connectedAt: new Date(),\n };\n\n this.clients.set(clientId, client);\n if (LOG_CONNECTIONS) {\n console.log(\n `[websocket] Client connected: ${clientId} (total: ${this.clients.size})`\n );\n }\n\n // Set up event handlers\n ws.on(\"message\", (data: RawData) => this.handleMessage(clientId, data));\n ws.on(\"close\", () => this.handleDisconnection(clientId));\n ws.on(\"error\", (error) => this.handleError(clientId, error));\n ws.on(\"pong\", () => this.handlePong(clientId));\n\n // Send welcome message\n this.sendToClient(clientId, {\n type: \"pong\",\n message: \"Connected to sudocode server\",\n });\n }\n\n /**\n * Handle client disconnection\n */\n private handleDisconnection(clientId: string): void {\n const client = this.clients.get(clientId);\n if (client) {\n if (LOG_CONNECTIONS) {\n console.log(\n `[websocket] Client disconnected: ${clientId} (subscriptions: ${client.subscriptions.size})`\n );\n }\n this.clients.delete(clientId);\n }\n }\n\n /**\n * Handle client errors\n */\n private handleError(clientId: string, error: Error): void {\n console.error(`[websocket] Client error (${clientId}):`, error.message);\n }\n\n /**\n * Handle pong response from client\n */\n private handlePong(clientId: string): void {\n const client = this.clients.get(clientId);\n if (client) {\n client.isAlive = true;\n }\n }\n\n /**\n * Handle incoming messages from clients\n */\n private handleMessage(clientId: string, data: RawData): void {\n const client = this.clients.get(clientId);\n if (!client) {\n return;\n }\n\n try {\n const message: ClientMessage = JSON.parse(data.toString());\n\n switch (message.type) {\n case \"subscribe\":\n this.handleSubscribe(clientId, message);\n break;\n\n case \"unsubscribe\":\n this.handleUnsubscribe(clientId, message);\n break;\n\n case \"ping\":\n this.sendToClient(clientId, { type: \"pong\" });\n break;\n\n default:\n this.sendToClient(clientId, {\n type: \"error\",\n message: `Unknown message type: ${(message as any).type}`,\n });\n }\n } catch (error) {\n console.error(\n `[websocket] Failed to parse message from ${clientId}:`,\n error\n );\n this.sendToClient(clientId, {\n type: \"error\",\n message: \"Invalid message format\",\n });\n }\n }\n\n /**\n * Handle subscription request\n */\n private handleSubscribe(clientId: string, message: ClientMessage): void {\n const client = this.clients.get(clientId);\n if (!client) {\n return;\n }\n\n let subscription: string;\n\n if (message.entity_type === \"all\") {\n // Subscribe to all updates\n subscription = \"all\";\n } else if (message.entity_type && message.entity_id) {\n // Subscribe to a specific entity\n subscription = `${message.entity_type}:${message.entity_id}`;\n } else if (message.entity_type) {\n // Subscribe to all entities of a type\n subscription = `${message.entity_type}:*`;\n } else {\n this.sendToClient(clientId, {\n type: \"error\",\n message: \"Invalid subscription request\",\n });\n return;\n }\n\n client.subscriptions.add(subscription);\n if (LOG_CONNECTIONS) {\n console.log(\n `[websocket] Client ${clientId} subscribed to: ${subscription}`\n );\n }\n\n this.sendToClient(clientId, {\n type: \"subscribed\",\n subscription,\n message: `Subscribed to ${subscription}`,\n });\n }\n\n /**\n * Handle unsubscription request\n */\n private handleUnsubscribe(clientId: string, message: ClientMessage): void {\n const client = this.clients.get(clientId);\n if (!client) {\n return;\n }\n\n let subscription: string;\n\n if (message.entity_type === \"all\") {\n subscription = \"all\";\n } else if (message.entity_type && message.entity_id) {\n subscription = `${message.entity_type}:${message.entity_id}`;\n } else if (message.entity_type) {\n subscription = `${message.entity_type}:*`;\n } else {\n this.sendToClient(clientId, {\n type: \"error\",\n message: \"Invalid unsubscription request\",\n });\n return;\n }\n\n client.subscriptions.delete(subscription);\n if (LOG_CONNECTIONS) {\n console.log(\n `[websocket] Client ${clientId} unsubscribed from: ${subscription}`\n );\n }\n\n this.sendToClient(clientId, {\n type: \"unsubscribed\",\n subscription,\n message: `Unsubscribed from ${subscription}`,\n });\n }\n\n /**\n * Send a message to a specific client\n */\n private sendToClient(clientId: string, message: ServerMessage): void {\n const client = this.clients.get(clientId);\n if (!client || client.ws.readyState !== WebSocket.OPEN) {\n return;\n }\n\n try {\n client.ws.send(JSON.stringify(message));\n } catch (error) {\n console.error(\n `[websocket] Failed to send message to ${clientId}:`,\n error\n );\n }\n }\n\n /**\n * Broadcast a message to all subscribed clients\n */\n broadcast(\n entityType: \"issue\" | \"spec\",\n entityId: string,\n message: ServerMessage\n ): void {\n const subscription = `${entityType}:${entityId}`;\n const typeSubscription = `${entityType}:*`;\n let sentCount = 0;\n\n this.clients.forEach((client) => {\n if (client.ws.readyState !== WebSocket.OPEN) {\n return;\n }\n\n // Check if client is subscribed to this specific entity, entity type, or all\n if (\n client.subscriptions.has(subscription) ||\n client.subscriptions.has(typeSubscription) ||\n client.subscriptions.has(\"all\")\n ) {\n try {\n client.ws.send(JSON.stringify(message));\n sentCount++;\n } catch (error) {\n console.error(\n `[websocket] Failed to broadcast to ${client.id}:`,\n error\n );\n }\n }\n });\n\n if (sentCount > 0) {\n console.log(\n `[websocket] Broadcasted ${message.type} for ${subscription} to ${sentCount} clients`\n );\n }\n }\n\n /**\n * Broadcast a relationship or feedback update\n * These don't have a specific entity type in the subscription, so we broadcast to 'all' subscribers\n */\n broadcastGeneric(message: ServerMessage): void {\n let sentCount = 0;\n\n this.clients.forEach((client) => {\n if (client.ws.readyState !== WebSocket.OPEN) {\n return;\n }\n\n // Only send to clients subscribed to 'all'\n if (client.subscriptions.has(\"all\")) {\n try {\n client.ws.send(JSON.stringify(message));\n sentCount++;\n } catch (error) {\n console.error(\n `[websocket] Failed to broadcast to ${client.id}:`,\n error\n );\n }\n }\n });\n\n if (sentCount > 0) {\n console.log(\n `[websocket] Broadcasted ${message.type} to ${sentCount} clients`\n );\n }\n }\n\n /**\n * Start the heartbeat mechanism to detect dead connections\n */\n private startHeartbeat(): void {\n if (this.heartbeatInterval) {\n return;\n }\n\n this.heartbeatInterval = setInterval(() => {\n this.clients.forEach((client, clientId) => {\n if (!client.isAlive) {\n console.log(`[websocket] Terminating dead connection: ${clientId}`);\n client.ws.terminate();\n this.clients.delete(clientId);\n return;\n }\n\n client.isAlive = false;\n try {\n client.ws.ping();\n } catch (error) {\n console.error(`[websocket] Failed to ping ${clientId}:`, error);\n }\n });\n }, this.HEARTBEAT_INTERVAL);\n\n console.log(\n `[websocket] Heartbeat started (interval: ${this.HEARTBEAT_INTERVAL}ms)`\n );\n }\n\n /**\n * Stop the heartbeat mechanism\n */\n private stopHeartbeat(): void {\n if (this.heartbeatInterval) {\n clearInterval(this.heartbeatInterval);\n this.heartbeatInterval = null;\n console.log(\"[websocket] Heartbeat stopped\");\n }\n }\n\n /**\n * Get statistics about connected clients\n */\n getStats(): {\n totalClients: number;\n clients: Array<{\n id: string;\n subscriptions: string[];\n connectedAt: string;\n isAlive: boolean;\n }>;\n } {\n return {\n totalClients: this.clients.size,\n clients: Array.from(this.clients.values()).map((client) => ({\n id: client.id,\n subscriptions: Array.from(client.subscriptions),\n connectedAt: client.connectedAt.toISOString(),\n isAlive: client.isAlive,\n })),\n };\n }\n\n /**\n * Gracefully shutdown the WebSocket server\n */\n async shutdown(): Promise<void> {\n console.log(\"[websocket] Shutting down WebSocket server...\");\n\n this.stopHeartbeat();\n\n // Close all client connections\n this.clients.forEach((client) => {\n try {\n client.ws.close(1000, \"Server shutting down\");\n } catch (error) {\n console.error(`[websocket] Error closing client ${client.id}:`, error);\n }\n });\n\n this.clients.clear();\n\n // Close the WebSocket server\n if (this.wss) {\n return new Promise((resolve) => {\n this.wss!.close(() => {\n console.log(\"[websocket] WebSocket server closed\");\n this.wss = null;\n resolve();\n });\n });\n }\n }\n}\n\n// Export singleton instance\nexport const websocketManager = new WebSocketManager();\n\n/**\n * Initialize the WebSocket server\n */\nexport function initWebSocketServer(server: http.Server, path?: string): void {\n websocketManager.init(server, path);\n}\n\n/**\n * Broadcast issue updates to subscribed clients\n */\nexport function broadcastIssueUpdate(\n issueId: string,\n action: \"created\" | \"updated\" | \"deleted\",\n data?: any\n): void {\n websocketManager.broadcast(\"issue\", issueId, {\n type: `issue_${action}` as any,\n data,\n });\n}\n\n/**\n * Broadcast spec updates to subscribed clients\n */\nexport function broadcastSpecUpdate(\n specId: string,\n action: \"created\" | \"updated\" | \"deleted\",\n data?: any\n): void {\n websocketManager.broadcast(\"spec\", specId, {\n type: `spec_${action}` as any,\n data,\n });\n}\n\n/**\n * Broadcast feedback updates to subscribed clients\n */\nexport function broadcastFeedbackUpdate(\n action: \"created\" | \"updated\" | \"deleted\",\n data?: any\n): void {\n websocketManager.broadcastGeneric({\n type: `feedback_${action}` as any,\n data,\n });\n}\n\n/**\n * Broadcast relationship updates to subscribed clients\n */\nexport function broadcastRelationshipUpdate(\n action: \"created\" | \"deleted\",\n data?: any\n): void {\n websocketManager.broadcastGeneric({\n type: `relationship_${action}` as any,\n data,\n });\n}\n\n/**\n * Get WebSocket server statistics\n */\nexport function getWebSocketStats() {\n return websocketManager.getStats();\n}\n\n/**\n * Shutdown the WebSocket server\n */\nexport async function shutdownWebSocketServer(): Promise<void> {\n await websocketManager.shutdown();\n}\n", "/**\n * Utility to get the SUDOCODE directory path\n * Respects SUDOCODE_DIR environment variable for testing\n */\n\nimport * as path from \"path\";\n\nexport function getSudocodeDir(): string {\n return process.env.SUDOCODE_DIR || path.join(process.cwd(), \".sudocode\");\n}\n", "/**\n * Export service - handles syncing database to JSONL and Markdown files\n */\n\nimport type Database from \"better-sqlite3\";\nimport { exportToJSONL } from \"@sudocode-ai/cli/dist/export.js\";\nimport { syncJSONLToMarkdown } from \"@sudocode-ai/cli/dist/sync.js\";\nimport { getSudocodeDir } from \"../utils/sudocode-dir.js\";\nimport * as path from \"path\";\n\n// Global debouncer state\nlet exportDebouncer: {\n db: Database.Database;\n timeoutId: NodeJS.Timeout | null;\n pending: boolean;\n} | null = null;\n\n/**\n * Initialize or get the export debouncer\n */\nfunction getExportDebouncer(db: Database.Database) {\n if (!exportDebouncer) {\n exportDebouncer = {\n db,\n timeoutId: null,\n pending: false,\n };\n }\n return exportDebouncer;\n}\n\n/**\n * Execute the full export (JSONL only)\n * Note: Markdown files are not updated here to avoid triggering mass file changes.\n * Markdown updates should happen through the watcher's reverse sync if enabled,\n * or through explicit sync commands.\n */\nasync function executeFullExport(db: Database.Database): Promise<void> {\n const outputDir = getSudocodeDir();\n\n // Export to JSONL only\n await exportToJSONL(db, { outputDir });\n\n // Note: We don't sync to markdown here because:\n // 1. It would update ALL markdown files on every change (inefficient)\n // 2. The watcher can handle reverse sync if enabled (JSONL \u2192 Markdown)\n // 3. For API updates, the markdown file will be updated when the watcher\n // detects the JSONL change and does reverse sync\n}\n\n/**\n * Sync a single entity to its markdown file\n */\nexport async function syncEntityToMarkdown(\n db: Database.Database,\n entityId: string,\n entityType: \"spec\" | \"issue\"\n): Promise<void> {\n const outputDir = getSudocodeDir();\n\n if (entityType === \"issue\") {\n const { getIssueById } = await import(\"./issues.js\");\n const issue = getIssueById(db, entityId);\n if (issue) {\n const mdPath = path.join(outputDir, \"issues\", `${issue.id}.md`);\n await syncJSONLToMarkdown(db, issue.id, \"issue\", mdPath);\n }\n } else {\n const { getSpecById } = await import(\"./specs.js\");\n const spec = getSpecById(db, entityId);\n if (spec) {\n const mdPath = spec.file_path\n ? path.join(outputDir, spec.file_path)\n : path.join(outputDir, \"specs\", `${spec.id}.md`);\n await syncJSONLToMarkdown(db, spec.id, \"spec\", mdPath);\n }\n }\n}\n\n/**\n * Trigger an export to JSONL and Markdown files (debounced)\n * This should be called after any database modifications\n */\nexport function triggerExport(db: Database.Database): void {\n const debouncer = getExportDebouncer(db);\n debouncer.pending = true;\n\n if (debouncer.timeoutId) {\n clearTimeout(debouncer.timeoutId);\n }\n\n debouncer.timeoutId = setTimeout(async () => {\n try {\n await executeFullExport(db);\n } catch (error) {\n console.error(\"Export failed:\", error);\n } finally {\n // Reset debouncer state after export completes\n debouncer.pending = false;\n debouncer.timeoutId = null;\n }\n }, 2000); // 2 second debounce\n}\n\n/**\n * Execute export immediately (bypass debouncing)\n * Exports to both JSONL and Markdown files\n */\nexport async function executeExportNow(db: Database.Database): Promise<void> {\n await executeFullExport(db);\n}\n\n/**\n * Cleanup the export debouncer (cancel pending exports and reset)\n * Should be called when closing the database or during test cleanup\n */\nexport function cleanupExport(): void {\n if (exportDebouncer) {\n if (exportDebouncer.timeoutId) {\n clearTimeout(exportDebouncer.timeoutId);\n }\n exportDebouncer = null;\n }\n}\n", "/**\n * Specs API routes (mapped to /api/specs)\n */\n\nimport { Router, Request, Response } from \"express\";\nimport type Database from \"better-sqlite3\";\nimport {\n getAllSpecs,\n getSpecById,\n createNewSpec,\n updateExistingSpec,\n deleteExistingSpec,\n} from \"../services/specs.js\";\nimport { generateSpecId } from \"@sudocode-ai/cli/dist/id-generator.js\";\nimport { broadcastSpecUpdate } from \"../services/websocket.js\";\nimport { getSudocodeDir } from \"../utils/sudocode-dir.js\";\nimport { triggerExport, syncEntityToMarkdown } from \"../services/export.js\";\nimport * as path from \"path\";\n\nexport function createSpecsRouter(db: Database.Database): Router {\n const router = Router();\n\n /**\n * GET /api/specs - List all specs\n */\n router.get(\"/\", (req: Request, res: Response) => {\n try {\n // Parse query parameters for filtering\n const options: any = {};\n\n if (req.query.priority) {\n options.priority = parseInt(req.query.priority as string, 10);\n }\n // Default to excluding archived unless explicitly specified\n options.archived =\n req.query.archived !== undefined\n ? req.query.archived === \"true\"\n : false;\n if (req.query.limit) {\n options.limit = parseInt(req.query.limit as string, 10);\n }\n if (req.query.offset) {\n options.offset = parseInt(req.query.offset as string, 10);\n }\n\n const specs = getAllSpecs(db, options);\n\n res.json({\n success: true,\n data: specs,\n });\n } catch (error) {\n console.error(\"Error listing specs:\", error);\n res.status(500).json({\n success: false,\n data: null,\n error_data: error instanceof Error ? error.message : String(error),\n message: \"Failed to list specs\",\n });\n }\n });\n\n /**\n * GET /api/specs/:id - Get a specific spec\n */\n router.get(\"/:id\", (req: Request, res: Response) => {\n try {\n const { id } = req.params;\n const spec = getSpecById(db, id);\n\n if (!spec) {\n res.status(404).json({\n success: false,\n data: null,\n message: `Spec not found: ${id}`,\n });\n return;\n }\n\n res.json({\n success: true,\n data: spec,\n });\n } catch (error) {\n console.error(\"Error getting spec:\", error);\n res.status(500).json({\n success: false,\n data: null,\n error_data: error instanceof Error ? error.message : String(error),\n message: \"Failed to get spec\",\n });\n }\n });\n\n /**\n * POST /api/specs - Create a new spec\n */\n router.post(\"/\", (req: Request, res: Response) => {\n try {\n const { title, content, priority, parent_id } = req.body;\n\n // Validate required fields\n if (!title || typeof title !== \"string\") {\n res.status(400).json({\n success: false,\n data: null,\n message: \"Title is required and must be a string\",\n });\n return;\n }\n\n if (title.length > 500) {\n res.status(400).json({\n success: false,\n data: null,\n message: \"Title must be 500 characters or less\",\n });\n return;\n }\n\n // Generate new spec ID\n const outputDir = getSudocodeDir();\n const id = generateSpecId(db, outputDir);\n\n // Generate file path for the spec\n const file_path = path.join(outputDir, \"specs\", `${id}.md`);\n\n // Create spec using CLI operation\n const spec = createNewSpec(db, {\n id,\n title,\n file_path,\n content: content || \"\",\n priority: priority !== undefined ? priority : 2,\n parent_id: parent_id || undefined,\n });\n\n // Trigger export to JSONL files\n triggerExport(db);\n\n // Sync this specific spec to its markdown file (don't wait for it)\n syncEntityToMarkdown(db, spec.id, \"spec\").catch((error) => {\n console.error(`Failed to sync spec ${spec.id} to markdown:`, error);\n });\n\n // Broadcast spec creation to WebSocket clients\n broadcastSpecUpdate(spec.id, \"created\", spec);\n\n res.status(201).json({\n success: true,\n data: spec,\n });\n } catch (error) {\n console.error(\"Error creating spec:\", error);\n res.status(500).json({\n success: false,\n data: null,\n error_data: error instanceof Error ? error.message : String(error),\n message: \"Failed to create spec\",\n });\n }\n });\n\n /**\n * PUT /api/specs/:id - Update an existing spec\n */\n router.put(\"/:id\", (req: Request, res: Response) => {\n try {\n const { id } = req.params;\n const { title, content, priority, parent_id, archived } = req.body;\n\n // Validate that at least one field is provided\n if (\n title === undefined &&\n content === undefined &&\n priority === undefined &&\n parent_id === undefined &&\n archived === undefined\n ) {\n res.status(400).json({\n success: false,\n data: null,\n message: \"At least one field must be provided for update\",\n });\n return;\n }\n\n // Validate title length if provided\n if (\n title !== undefined &&\n typeof title === \"string\" &&\n title.length > 500\n ) {\n res.status(400).json({\n success: false,\n data: null,\n message: \"Title must be 500 characters or less\",\n });\n return;\n }\n\n // Build update input\n const updateInput: any = {};\n if (title !== undefined) updateInput.title = title;\n if (content !== undefined) updateInput.content = content;\n if (priority !== undefined) updateInput.priority = priority;\n if (parent_id !== undefined) updateInput.parent_id = parent_id;\n if (archived !== undefined) {\n updateInput.archived = archived;\n updateInput.archived_at = archived ? new Date().toISOString() : null;\n }\n\n // Update spec using CLI operation\n const spec = updateExistingSpec(db, id, updateInput);\n\n // Trigger export to JSONL files\n triggerExport(db);\n\n // Sync this specific spec to its markdown file (don't wait for it)\n syncEntityToMarkdown(db, spec.id, \"spec\").catch((error) => {\n console.error(`Failed to sync spec ${spec.id} to markdown:`, error);\n });\n\n // Broadcast spec update to WebSocket clients\n broadcastSpecUpdate(spec.id, \"updated\", spec);\n\n res.json({\n success: true,\n data: spec,\n });\n } catch (error) {\n console.error(\"Error updating spec:\", error);\n\n // Handle \"not found\" errors\n if (error instanceof Error && error.message.includes(\"not found\")) {\n res.status(404).json({\n success: false,\n data: null,\n message: error.message,\n });\n return;\n }\n\n res.status(500).json({\n success: false,\n data: null,\n error_data: error instanceof Error ? error.message : String(error),\n message: \"Failed to update spec\",\n });\n }\n });\n\n /**\n * DELETE /api/specs/:id - Delete a spec\n */\n router.delete(\"/:id\", (req: Request, res: Response) => {\n try {\n const { id } = req.params;\n\n // Check if spec exists first\n const existingSpec = getSpecById(db, id);\n if (!existingSpec) {\n res.status(404).json({\n success: false,\n data: null,\n message: `Spec not found: ${id}`,\n });\n return;\n }\n\n // Delete spec using CLI operation\n const deleted = deleteExistingSpec(db, id);\n\n if (deleted) {\n // Trigger export to JSONL files\n triggerExport(db);\n\n // Broadcast spec deletion to WebSocket clients\n broadcastSpecUpdate(id, \"deleted\", { id });\n\n res.json({\n success: true,\n data: {\n id,\n deleted: true,\n },\n });\n } else {\n res.status(500).json({\n success: false,\n data: null,\n message: \"Failed to delete spec\",\n });\n }\n } catch (error) {\n console.error(\"Error deleting spec:\", error);\n res.status(500).json({\n success: false,\n data: null,\n error_data: error instanceof Error ? error.message : String(error),\n message: \"Failed to delete spec\",\n });\n }\n });\n\n return router;\n}\n", "/**\n * Relationships API routes (mapped to /api/relationships)\n */\n\nimport { Router, Request, Response } from \"express\";\nimport type Database from \"better-sqlite3\";\nimport type { EntityType, RelationshipType } from \"@sudocode-ai/types\";\nimport {\n createRelationship,\n deleteRelationship,\n getEntityRelationships,\n getEntityOutgoingRelationships,\n getEntityIncomingRelationships,\n} from \"../services/relationships.js\";\nimport { broadcastRelationshipUpdate } from \"../services/websocket.js\";\nimport { triggerExport } from \"../services/export.js\";\n\nexport function createRelationshipsRouter(db: Database.Database): Router {\n const router = Router();\n\n /**\n * GET /api/relationships/:entity_type/:entity_id - Get all relationships for an entity\n */\n router.get(\"/:entity_type/:entity_id\", (req: Request, res: Response) => {\n try {\n const { entity_type, entity_id } = req.params;\n\n // Validate entity_type\n if (entity_type !== \"spec\" && entity_type !== \"issue\") {\n res.status(400).json({\n success: false,\n data: null,\n message: \"Invalid entity_type. Must be 'spec' or 'issue'\",\n });\n return;\n }\n\n const relationships = getEntityRelationships(\n db,\n entity_id,\n entity_type as EntityType\n );\n\n res.json({\n success: true,\n data: relationships,\n });\n } catch (error) {\n console.error(\"Error getting relationships:\", error);\n res.status(500).json({\n success: false,\n data: null,\n error_data: error instanceof Error ? error.message : String(error),\n message: \"Failed to get relationships\",\n });\n }\n });\n\n /**\n * GET /api/relationships/:entity_type/:entity_id/outgoing - Get outgoing relationships\n */\n router.get(\n \"/:entity_type/:entity_id/outgoing\",\n (req: Request, res: Response) => {\n try {\n const { entity_type, entity_id } = req.params;\n const { relationship_type } = req.query;\n\n // Validate entity_type\n if (entity_type !== \"spec\" && entity_type !== \"issue\") {\n res.status(400).json({\n success: false,\n data: null,\n message: \"Invalid entity_type. Must be 'spec' or 'issue'\",\n });\n return;\n }\n\n const relationships = getEntityOutgoingRelationships(\n db,\n entity_id,\n entity_type as EntityType,\n relationship_type as RelationshipType | undefined\n );\n\n res.json({\n success: true,\n data: relationships,\n });\n } catch (error) {\n console.error(\"Error getting outgoing relationships:\", error);\n res.status(500).json({\n success: false,\n data: null,\n error_data: error instanceof Error ? error.message : String(error),\n message: \"Failed to get outgoing relationships\",\n });\n }\n }\n );\n\n /**\n * GET /api/relationships/:entity_type/:entity_id/incoming - Get incoming relationships\n */\n router.get(\n \"/:entity_type/:entity_id/incoming\",\n (req: Request, res: Response) => {\n try {\n const { entity_type, entity_id } = req.params;\n const { relationship_type } = req.query;\n\n // Validate entity_type\n if (entity_type !== \"spec\" && entity_type !== \"issue\") {\n res.status(400).json({\n success: false,\n data: null,\n message: \"Invalid entity_type. Must be 'spec' or 'issue'\",\n });\n return;\n }\n\n const relationships = getEntityIncomingRelationships(\n db,\n entity_id,\n entity_type as EntityType,\n relationship_type as RelationshipType | undefined\n );\n\n res.json({\n success: true,\n data: relationships,\n });\n } catch (error) {\n console.error(\"Error getting incoming relationships:\", error);\n res.status(500).json({\n success: false,\n data: null,\n error_data: error instanceof Error ? error.message : String(error),\n message: \"Failed to get incoming relationships\",\n });\n }\n }\n );\n\n /**\n * POST /api/relationships - Create a new relationship\n */\n router.post(\"/\", (req: Request, res: Response) => {\n try {\n const {\n from_id,\n from_type,\n to_id,\n to_type,\n relationship_type,\n metadata,\n } = req.body;\n\n // Validate required fields\n if (!from_id || typeof from_id !== \"string\") {\n res.status(400).json({\n success: false,\n data: null,\n message: \"from_id is required and must be a string\",\n });\n return;\n }\n\n if (!from_type || (from_type !== \"spec\" && from_type !== \"issue\")) {\n res.status(400).json({\n success: false,\n data: null,\n message: \"from_type is required and must be 'spec' or 'issue'\",\n });\n return;\n }\n\n if (!to_id || typeof to_id !== \"string\") {\n res.status(400).json({\n success: false,\n data: null,\n message: \"to_id is required and must be a string\",\n });\n return;\n }\n\n if (!to_type || (to_type !== \"spec\" && to_type !== \"issue\")) {\n res.status(400).json({\n success: false,\n data: null,\n message: \"to_type is required and must be 'spec' or 'issue'\",\n });\n return;\n }\n\n if (!relationship_type || typeof relationship_type !== \"string\") {\n res.status(400).json({\n success: false,\n data: null,\n message: \"relationship_type is required and must be a string\",\n });\n return;\n }\n\n // Validate relationship_type\n const validTypes = [\n \"blocks\",\n \"related\",\n \"discovered-from\",\n \"implements\",\n \"references\",\n \"depends-on\",\n ];\n if (!validTypes.includes(relationship_type)) {\n res.status(400).json({\n success: false,\n data: null,\n message: `Invalid relationship_type. Must be one of: ${validTypes.join(\", \")}`,\n });\n return;\n }\n\n // Create relationship using CLI operation\n const relationship = createRelationship(db, {\n from_id,\n from_type: from_type as EntityType,\n to_id,\n to_type: to_type as EntityType,\n relationship_type: relationship_type as RelationshipType,\n metadata: metadata || null,\n });\n\n // Broadcast relationship creation to WebSocket clients\n broadcastRelationshipUpdate(\"created\", relationship);\n\n // Trigger export to sync JSONL files\n triggerExport(db);\n\n res.status(201).json({\n success: true,\n data: relationship,\n });\n } catch (error) {\n console.error(\"Error creating relationship:\", error);\n\n // Handle specific errors\n if (error instanceof Error) {\n if (error.message.includes(\"not found\")) {\n res.status(404).json({\n success: false,\n data: null,\n message: error.message,\n });\n return;\n }\n\n if (error.message.includes(\"already exists\")) {\n res.status(409).json({\n success: false,\n data: null,\n message: error.message,\n });\n return;\n }\n }\n\n res.status(500).json({\n success: false,\n data: null,\n error_data: error instanceof Error ? error.message : String(error),\n message: \"Failed to create relationship\",\n });\n }\n });\n\n /**\n * DELETE /api/relationships - Delete a specific relationship\n */\n router.delete(\"/\", (req: Request, res: Response) => {\n try {\n const { from_id, from_type, to_id, to_type, relationship_type } =\n req.body;\n\n // Validate required fields\n if (!from_id || typeof from_id !== \"string\") {\n res.status(400).json({\n success: false,\n data: null,\n message: \"from_id is required and must be a string\",\n });\n return;\n }\n\n if (!from_type || (from_type !== \"spec\" && from_type !== \"issue\")) {\n res.status(400).json({\n success: false,\n data: null,\n message: \"from_type is required and must be 'spec' or 'issue'\",\n });\n return;\n }\n\n if (!to_id || typeof to_id !== \"string\") {\n res.status(400).json({\n success: false,\n data: null,\n message: \"to_id is required and must be a string\",\n });\n return;\n }\n\n if (!to_type || (to_type !== \"spec\" && to_type !== \"issue\")) {\n res.status(400).json({\n success: false,\n data: null,\n message: \"to_type is required and must be 'spec' or 'issue'\",\n });\n return;\n }\n\n if (!relationship_type || typeof relationship_type !== \"string\") {\n res.status(400).json({\n success: false,\n data: null,\n message: \"relationship_type is required and must be a string\",\n });\n return;\n }\n\n // Delete relationship using CLI operation\n const deleted = deleteRelationship(\n db,\n from_id,\n from_type as EntityType,\n to_id,\n to_type as EntityType,\n relationship_type as RelationshipType\n );\n\n if (deleted) {\n // Broadcast relationship deletion to WebSocket clients\n broadcastRelationshipUpdate(\"deleted\", {\n from_id,\n from_type,\n to_id,\n to_type,\n relationship_type,\n });\n\n // Trigger export to sync JSONL files\n triggerExport(db);\n\n res.json({\n success: true,\n data: {\n from_id,\n from_type,\n to_id,\n to_type,\n relationship_type,\n deleted: true,\n },\n });\n } else {\n res.status(404).json({\n success: false,\n data: null,\n message: \"Relationship not found\",\n });\n }\n } catch (error) {\n console.error(\"Error deleting relationship:\", error);\n res.status(500).json({\n success: false,\n data: null,\n error_data: error instanceof Error ? error.message : String(error),\n message: \"Failed to delete relationship\",\n });\n }\n });\n\n return router;\n}\n", "/**\n * Service layer for Relationships API\n * Wraps CLI operations for managing relationships between specs and issues\n */\n\nimport type Database from \"better-sqlite3\";\nimport type {\n Relationship,\n EntityType,\n RelationshipType,\n} from \"@sudocode-ai/types\";\nimport {\n addRelationship,\n getRelationship,\n removeRelationship,\n getOutgoingRelationships,\n getIncomingRelationships,\n getAllRelationships,\n type CreateRelationshipInput,\n} from \"@sudocode-ai/cli/dist/operations/relationships.js\";\n\n/**\n * Create a new relationship between entities\n */\nexport function createRelationship(\n db: Database.Database,\n input: CreateRelationshipInput\n): Relationship {\n return addRelationship(db, input);\n}\n\n/**\n * Get a specific relationship\n */\nexport function getSpecificRelationship(\n db: Database.Database,\n from_id: string,\n from_type: EntityType,\n to_id: string,\n to_type: EntityType,\n relationship_type: RelationshipType\n): Relationship | null {\n return getRelationship(\n db,\n from_id,\n from_type,\n to_id,\n to_type,\n relationship_type\n );\n}\n\n/**\n * Delete a relationship\n */\nexport function deleteRelationship(\n db: Database.Database,\n from_id: string,\n from_type: EntityType,\n to_id: string,\n to_type: EntityType,\n relationship_type: RelationshipType\n): boolean {\n return removeRelationship(\n db,\n from_id,\n from_type,\n to_id,\n to_type,\n relationship_type\n );\n}\n\n/**\n * Get all outgoing relationships from an entity\n */\nexport function getEntityOutgoingRelationships(\n db: Database.Database,\n entity_id: string,\n entity_type: EntityType,\n relationship_type?: RelationshipType\n): Relationship[] {\n return getOutgoingRelationships(\n db,\n entity_id,\n entity_type,\n relationship_type\n );\n}\n\n/**\n * Get all incoming relationships to an entity\n */\nexport function getEntityIncomingRelationships(\n db: Database.Database,\n entity_id: string,\n entity_type: EntityType,\n relationship_type?: RelationshipType\n): Relationship[] {\n return getIncomingRelationships(\n db,\n entity_id,\n entity_type,\n relationship_type\n );\n}\n\n/**\n * Get all relationships for an entity (both incoming and outgoing)\n */\nexport function getEntityRelationships(\n db: Database.Database,\n entity_id: string,\n entity_type: EntityType\n): { outgoing: Relationship[]; incoming: Relationship[] } {\n return getAllRelationships(db, entity_id, entity_type);\n}\n", "/**\n * Feedback API routes (mapped to /api/feedback)\n */\n\nimport { Router, Request, Response } from \"express\";\nimport type Database from \"better-sqlite3\";\nimport type { FeedbackType, FeedbackAnchor } from \"@sudocode-ai/types\";\nimport {\n createNewFeedback,\n getFeedbackById,\n updateExistingFeedback,\n deleteExistingFeedback,\n getAllFeedback,\n} from \"../services/feedback.js\";\nimport { broadcastFeedbackUpdate } from \"../services/websocket.js\";\n\nexport function createFeedbackRouter(db: Database.Database): Router {\n const router = Router();\n\n /**\n * GET /api/feedback - List all feedback with optional filters\n * Query params: spec_id, issue_id, feedback_type, dismissed, limit, offset\n */\n router.get(\"/\", (req: Request, res: Response) => {\n try {\n const options: any = {};\n\n if (req.query.spec_id) {\n options.spec_id = req.query.spec_id as string;\n }\n if (req.query.issue_id) {\n options.issue_id = req.query.issue_id as string;\n }\n if (req.query.feedback_type) {\n options.feedback_type = req.query.feedback_type as FeedbackType;\n }\n if (req.query.dismissed !== undefined) {\n options.dismissed = req.query.dismissed === \"true\";\n }\n if (req.query.limit) {\n options.limit = parseInt(req.query.limit as string, 10);\n }\n if (req.query.offset) {\n options.offset = parseInt(req.query.offset as string, 10);\n }\n\n const feedback = getAllFeedback(db, options);\n\n res.json({\n success: true,\n data: feedback,\n });\n } catch (error) {\n console.error(\"Error listing feedback:\", error);\n res.status(500).json({\n success: false,\n data: null,\n error_data: error instanceof Error ? error.message : String(error),\n message: \"Failed to list feedback\",\n });\n }\n });\n\n /**\n * GET /api/feedback/:id - Get a specific feedback entry\n */\n router.get(\"/:id\", (req: Request, res: Response) => {\n try {\n const { id } = req.params;\n const feedback = getFeedbackById(db, id);\n\n if (!feedback) {\n res.status(404).json({\n success: false,\n data: null,\n message: `Feedback not found: ${id}`,\n });\n return;\n }\n\n res.json({\n success: true,\n data: feedback,\n });\n } catch (error) {\n console.error(\"Error getting feedback:\", error);\n res.status(500).json({\n success: false,\n data: null,\n error_data: error instanceof Error ? error.message : String(error),\n message: \"Failed to get feedback\",\n });\n }\n });\n\n /**\n * POST /api/feedback - Create a new feedback entry\n */\n router.post(\"/\", (req: Request, res: Response) => {\n try {\n const {\n issue_id,\n spec_id,\n feedback_type,\n content,\n agent,\n anchor,\n dismissed,\n } = req.body;\n\n // Validate required fields\n if (!issue_id || typeof issue_id !== \"string\") {\n res.status(400).json({\n success: false,\n data: null,\n message: \"issue_id is required and must be a string\",\n });\n return;\n }\n\n if (!spec_id || typeof spec_id !== \"string\") {\n res.status(400).json({\n success: false,\n data: null,\n message: \"spec_id is required and must be a string\",\n });\n return;\n }\n\n if (!feedback_type || typeof feedback_type !== \"string\") {\n res.status(400).json({\n success: false,\n data: null,\n message: \"feedback_type is required and must be a string\",\n });\n return;\n }\n\n // Validate feedback_type\n const validTypes = [\"comment\", \"suggestion\", \"request\"];\n if (!validTypes.includes(feedback_type)) {\n res.status(400).json({\n success: false,\n data: null,\n message: `Invalid feedback_type. Must be one of: ${validTypes.join(\", \")}`,\n });\n return;\n }\n\n if (!content || typeof content !== \"string\") {\n res.status(400).json({\n success: false,\n data: null,\n message: \"content is required and must be a string\",\n });\n return;\n }\n\n // Validate anchor if provided\n if (anchor !== undefined && anchor !== null) {\n if (typeof anchor !== \"object\") {\n res.status(400).json({\n success: false,\n data: null,\n message: \"anchor must be an object if provided\",\n });\n return;\n }\n\n // Validate anchor structure if provided\n if (anchor.anchor_status) {\n const validAnchorStatuses = [\"valid\", \"relocated\", \"stale\"];\n if (!validAnchorStatuses.includes(anchor.anchor_status)) {\n res.status(400).json({\n success: false,\n data: null,\n message: `Invalid anchor.anchor_status. Must be one of: ${validAnchorStatuses.join(\", \")}`,\n });\n return;\n }\n }\n }\n\n // Create feedback using CLI operation\n const feedback = createNewFeedback(db, {\n issue_id,\n spec_id,\n feedback_type: feedback_type as FeedbackType,\n content,\n agent: agent || undefined,\n anchor: anchor as FeedbackAnchor | undefined,\n dismissed: dismissed || false,\n });\n\n // Broadcast feedback creation to WebSocket clients\n broadcastFeedbackUpdate(\"created\", feedback);\n\n res.status(201).json({\n success: true,\n data: feedback,\n });\n } catch (error) {\n console.error(\"Error creating feedback:\", error);\n\n // Handle specific errors\n if (error instanceof Error) {\n if (error.message.includes(\"not found\")) {\n res.status(404).json({\n success: false,\n data: null,\n message: error.message,\n });\n return;\n }\n\n if (error.message.includes(\"Constraint violation\")) {\n res.status(409).json({\n success: false,\n data: null,\n message: error.message,\n });\n return;\n }\n }\n\n res.status(500).json({\n success: false,\n data: null,\n error_data: error instanceof Error ? error.message : String(error),\n message: \"Failed to create feedback\",\n });\n }\n });\n\n /**\n * PUT /api/feedback/:id - Update an existing feedback entry\n */\n router.put(\"/:id\", (req: Request, res: Response) => {\n try {\n const { id } = req.params;\n const { content, dismissed, anchor } = req.body;\n\n // Validate that at least one field is provided\n if (\n content === undefined &&\n dismissed === undefined &&\n anchor === undefined\n ) {\n res.status(400).json({\n success: false,\n data: null,\n message: \"At least one field must be provided for update\",\n });\n return;\n }\n\n // Validate anchor if provided\n if (anchor !== undefined) {\n if (typeof anchor !== \"object\") {\n res.status(400).json({\n success: false,\n data: null,\n message: \"anchor must be an object\",\n });\n return;\n }\n\n if (!anchor.anchor_status || typeof anchor.anchor_status !== \"string\") {\n res.status(400).json({\n success: false,\n data: null,\n message: \"anchor.anchor_status is required and must be a string\",\n });\n return;\n }\n\n const validAnchorStatuses = [\"valid\", \"relocated\", \"stale\"];\n if (!validAnchorStatuses.includes(anchor.anchor_status)) {\n res.status(400).json({\n success: false,\n data: null,\n message: `Invalid anchor.anchor_status. Must be one of: ${validAnchorStatuses.join(\", \")}`,\n });\n return;\n }\n }\n\n // Build update input\n const updateInput: any = {};\n if (content !== undefined) updateInput.content = content;\n if (dismissed !== undefined) updateInput.dismissed = dismissed;\n if (anchor !== undefined) updateInput.anchor = anchor as FeedbackAnchor;\n\n // Update feedback using CLI operation\n const feedback = updateExistingFeedback(db, id, updateInput);\n\n // Broadcast feedback update to WebSocket clients\n broadcastFeedbackUpdate(\"updated\", feedback);\n\n res.json({\n success: true,\n data: feedback,\n });\n } catch (error) {\n console.error(\"Error updating feedback:\", error);\n\n // Handle \"not found\" errors\n if (error instanceof Error && error.message.includes(\"not found\")) {\n res.status(404).json({\n success: false,\n data: null,\n message: error.message,\n });\n return;\n }\n\n res.status(500).json({\n success: false,\n data: null,\n error_data: error instanceof Error ? error.message : String(error),\n message: \"Failed to update feedback\",\n });\n }\n });\n\n /**\n * DELETE /api/feedback/:id - Delete a feedback entry\n */\n router.delete(\"/:id\", (req: Request, res: Response) => {\n try {\n const { id } = req.params;\n\n // Check if feedback exists first\n const existingFeedback = getFeedbackById(db, id);\n if (!existingFeedback) {\n res.status(404).json({\n success: false,\n data: null,\n message: `Feedback not found: ${id}`,\n });\n return;\n }\n\n // Delete feedback using CLI operation\n const deleted = deleteExistingFeedback(db, id);\n\n if (deleted) {\n // Broadcast feedback deletion to WebSocket clients\n broadcastFeedbackUpdate(\"deleted\", { id });\n\n res.json({\n success: true,\n data: {\n id,\n deleted: true,\n },\n });\n } else {\n res.status(500).json({\n success: false,\n data: null,\n message: \"Failed to delete feedback\",\n });\n }\n } catch (error) {\n console.error(\"Error deleting feedback:\", error);\n res.status(500).json({\n success: false,\n data: null,\n error_data: error instanceof Error ? error.message : String(error),\n message: \"Failed to delete feedback\",\n });\n }\n });\n\n return router;\n}\n", "/**\n * Service layer for Feedback API\n * Wraps CLI operations for managing issue feedback on specs\n */\n\nimport type Database from \"better-sqlite3\";\nimport type { IssueFeedback } from \"@sudocode-ai/types\";\nimport {\n createFeedback,\n getFeedback,\n updateFeedback,\n deleteFeedback,\n listFeedback,\n getFeedbackForSpec,\n getFeedbackForIssue,\n dismissFeedback,\n type CreateFeedbackInput,\n type UpdateFeedbackInput,\n type ListFeedbackOptions,\n} from \"@sudocode-ai/cli/dist/operations/feedback.js\";\n\n/**\n * Create a new feedback entry\n */\nexport function createNewFeedback(\n db: Database.Database,\n input: CreateFeedbackInput\n): IssueFeedback {\n return createFeedback(db, input);\n}\n\n/**\n * Get a specific feedback by ID\n */\nexport function getFeedbackById(\n db: Database.Database,\n id: string\n): IssueFeedback | null {\n return getFeedback(db, id);\n}\n\n/**\n * Update an existing feedback entry\n */\nexport function updateExistingFeedback(\n db: Database.Database,\n id: string,\n input: UpdateFeedbackInput\n): IssueFeedback {\n return updateFeedback(db, id, input);\n}\n\n/**\n * Delete a feedback entry\n */\nexport function deleteExistingFeedback(\n db: Database.Database,\n id: string\n): boolean {\n return deleteFeedback(db, id);\n}\n\n/**\n * List feedback entries with optional filters\n */\nexport function getAllFeedback(\n db: Database.Database,\n options?: ListFeedbackOptions\n): IssueFeedback[] {\n return listFeedback(db, options || {});\n}\n\n/**\n * Get all feedback for a specific spec\n */\nexport function getSpecFeedback(\n db: Database.Database,\n spec_id: string\n): IssueFeedback[] {\n return getFeedbackForSpec(db, spec_id);\n}\n\n/**\n * Get all feedback for a specific issue\n */\nexport function getIssueFeedback(\n db: Database.Database,\n issue_id: string\n): IssueFeedback[] {\n return getFeedbackForIssue(db, issue_id);\n}\n\n/**\n * Dismiss a feedback entry\n */\nexport function dismissExistingFeedback(\n db: Database.Database,\n id: string\n): IssueFeedback {\n return dismissFeedback(db, id);\n}\n", "/**\n * Executions API routes (mapped to /api)\n *\n * Provides REST API for managing issue executions.\n */\n\nimport { Router, Request, Response } from \"express\";\nimport type Database from \"better-sqlite3\";\nimport { ExecutionService } from \"../services/execution-service.js\";\nimport type { TransportManager } from \"../execution/transport/transport-manager.js\";\n\n/**\n * Create executions router\n *\n * @param db - Database instance\n * @param repoPath - Path to git repository\n * @param transportManager - Optional transport manager for SSE streaming\n * @returns Express router with execution endpoints\n */\nexport function createExecutionsRouter(\n db: Database.Database,\n repoPath: string,\n transportManager?: TransportManager,\n executionService?: ExecutionService\n): Router {\n const router = Router();\n const service =\n executionService ||\n new ExecutionService(db, repoPath, undefined, transportManager);\n\n /**\n * POST /api/issues/:issueId/executions/prepare\n *\n * Prepare an execution - render template and show preview\n */\n router.post(\n \"/issues/:issueId/executions/prepare\",\n async (req: Request, res: Response) => {\n try {\n const { issueId } = req.params;\n const options = req.body || {};\n const result = await service.prepareExecution(issueId, options);\n\n res.json({\n success: true,\n data: result,\n });\n } catch (error) {\n console.error(\"[API Route] ERROR: Failed to prepare execution:\", error);\n res.status(500).json({\n success: false,\n data: null,\n error_data: error instanceof Error ? error.message : String(error),\n message: \"Failed to prepare execution\",\n });\n }\n }\n );\n\n /**\n * POST /api/issues/:issueId/executions\n *\n * Create and start a new execution\n */\n router.post(\n \"/issues/:issueId/executions\",\n async (req: Request, res: Response) => {\n try {\n const { issueId } = req.params;\n const { config, prompt } = req.body;\n\n // Validate required fields\n if (!prompt) {\n res.status(400).json({\n success: false,\n data: null,\n message: \"Prompt is required\",\n });\n return;\n }\n\n const execution = await service.createExecution(\n issueId,\n config || {},\n prompt\n );\n\n res.status(201).json({\n success: true,\n data: execution,\n });\n } catch (error) {\n console.error(\"[API Route] ERROR: Failed to create execution:\", error);\n\n // Handle specific error cases\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n const statusCode = errorMessage.includes(\"not found\") ? 404 : 500;\n\n res.status(statusCode).json({\n success: false,\n data: null,\n error_data: errorMessage,\n message: \"Failed to create execution\",\n });\n }\n }\n );\n\n /**\n * GET /api/executions/:executionId\n *\n * Get a specific execution by ID\n */\n router.get(\"/executions/:executionId\", (req: Request, res: Response) => {\n try {\n const { executionId } = req.params;\n const execution = service.getExecution(executionId);\n\n if (!execution) {\n res.status(404).json({\n success: false,\n data: null,\n message: `Execution not found: ${executionId}`,\n });\n return;\n }\n\n res.json({\n success: true,\n data: execution,\n });\n } catch (error) {\n console.error(\"Error getting execution:\", error);\n res.status(500).json({\n success: false,\n data: null,\n error_data: error instanceof Error ? error.message : String(error),\n message: \"Failed to get execution\",\n });\n }\n });\n\n /**\n * GET /api/issues/:issueId/executions\n *\n * List all executions for an issue\n */\n router.get(\"/issues/:issueId/executions\", (req: Request, res: Response) => {\n try {\n const { issueId } = req.params;\n const executions = service.listExecutions(issueId);\n\n res.json({\n success: true,\n data: executions,\n });\n } catch (error) {\n console.error(\"Error listing executions:\", error);\n res.status(500).json({\n success: false,\n data: null,\n error_data: error instanceof Error ? error.message : String(error),\n message: \"Failed to list executions\",\n });\n }\n });\n\n /**\n * POST /api/executions/:executionId/follow-up\n *\n * Create a follow-up execution that reuses the parent's worktree\n */\n router.post(\n \"/executions/:executionId/follow-up\",\n async (req: Request, res: Response) => {\n try {\n const { executionId } = req.params;\n const { feedback } = req.body;\n\n // Validate required fields\n if (!feedback) {\n res.status(400).json({\n success: false,\n data: null,\n message: \"Feedback is required\",\n });\n return;\n }\n\n const followUpExecution = await service.createFollowUp(\n executionId,\n feedback\n );\n\n res.status(201).json({\n success: true,\n data: followUpExecution,\n });\n } catch (error) {\n console.error(\"Error creating follow-up execution:\", error);\n\n // Handle specific error cases\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n const statusCode =\n errorMessage.includes(\"not found\") ||\n errorMessage.includes(\"no worktree\")\n ? 404\n : 500;\n\n res.status(statusCode).json({\n success: false,\n data: null,\n error_data: errorMessage,\n message: \"Failed to create follow-up execution\",\n });\n }\n }\n );\n\n /**\n * DELETE /api/executions/:executionId\n *\n * Cancel a running execution\n */\n router.delete(\n \"/executions/:executionId\",\n async (req: Request, res: Response) => {\n try {\n const { executionId } = req.params;\n\n await service.cancelExecution(executionId);\n\n res.json({\n success: true,\n data: { executionId },\n message: \"Execution cancelled successfully\",\n });\n } catch (error) {\n console.error(\"Error cancelling execution:\", error);\n\n // Handle specific error cases\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n const statusCode = errorMessage.includes(\"not found\") ? 404 : 500;\n\n res.status(statusCode).json({\n success: false,\n data: null,\n error_data: errorMessage,\n message: \"Failed to cancel execution\",\n });\n }\n }\n );\n\n /**\n * GET /api/executions/:executionId/worktree\n *\n * Check if worktree exists for an execution\n */\n router.get(\n \"/executions/:executionId/worktree\",\n async (req: Request, res: Response) => {\n try {\n const { executionId } = req.params;\n\n const exists = await service.worktreeExists(executionId);\n\n res.json({\n success: true,\n data: { exists },\n });\n } catch (error) {\n console.error(\"Error checking worktree:\", error);\n\n res.status(500).json({\n success: false,\n data: null,\n error_data: error instanceof Error ? error.message : String(error),\n message: \"Failed to check worktree status\",\n });\n }\n }\n );\n\n /**\n * DELETE /api/executions/:executionId/worktree\n *\n * Delete the worktree for an execution\n */\n router.delete(\n \"/executions/:executionId/worktree\",\n async (req: Request, res: Response) => {\n try {\n const { executionId } = req.params;\n\n await service.deleteWorktree(executionId);\n\n res.json({\n success: true,\n data: { executionId },\n message: \"Worktree deleted successfully\",\n });\n } catch (error) {\n console.error(\"Error deleting worktree:\", error);\n\n // Handle specific error cases\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n let statusCode = 500;\n\n if (errorMessage.includes(\"not found\")) {\n statusCode = 404;\n } else if (\n errorMessage.includes(\"has no worktree\") ||\n errorMessage.includes(\"Cannot delete worktree\")\n ) {\n statusCode = 400;\n }\n\n res.status(statusCode).json({\n success: false,\n data: null,\n error_data: errorMessage,\n message: \"Failed to delete worktree\",\n });\n }\n }\n );\n\n return router;\n}\n", "/**\n * Execution Stream Routes\n *\n * SSE endpoint for streaming execution events to clients.\n * Integrates with TransportManager to broadcast AG-UI events.\n *\n * @module routes/executions-stream\n */\n\nimport { Router } from \"express\";\nimport type { Request, Response } from \"express\";\nimport { randomUUID } from \"crypto\";\nimport type { TransportManager } from \"../execution/transport/transport-manager.js\";\n\n/**\n * Create execution stream routes\n *\n * @param transportManager - Transport manager for SSE connections\n * @returns Express router with SSE endpoints\n *\n * @example\n * ```typescript\n * const router = createExecutionStreamRoutes(transportManager);\n * app.use('/api/executions', router);\n * ```\n */\nexport function createExecutionStreamRoutes(\n transportManager: TransportManager\n): Router {\n const router = Router();\n\n /**\n * SSE endpoint for execution event stream\n *\n * GET /api/executions/:executionId/stream\n *\n * Establishes SSE connection and streams execution events to client.\n * Events are filtered to only include events for the specified execution.\n *\n * @param executionId - Execution ID to stream events for\n */\n router.get(\"/:executionId/stream\", (req: Request, res: Response) => {\n const { executionId } = req.params;\n\n // TODO: Add authentication/authorization check\n // Verify user has permission to access this execution\n\n // Generate unique client ID\n const clientId = randomUUID();\n\n // Get buffered events for replay\n const bufferedEvents = transportManager.getBufferedEvents(executionId);\n const replayEvents = bufferedEvents.map((buffered) => ({\n event: buffered.event.type,\n data: buffered.event,\n }));\n\n // Establish SSE connection through transport manager\n // This will set appropriate headers, send connection acknowledgment, and replay buffered events\n transportManager\n .getSseTransport()\n .handleConnection(clientId, res, executionId, replayEvents);\n });\n\n return router;\n}\n", "/**\n * Server-Sent Events (SSE) Transport Layer\n *\n * Provides real-time streaming of AG-UI events to frontend clients using SSE.\n * Supports connection management, heartbeat, and run-specific event filtering.\n *\n * @module execution/transport/sse-transport\n */\n\nimport type { Response } from \"express\";\n\n/**\n * SSE Client Connection\n *\n * Represents an active SSE connection with a client\n */\ninterface SseClient {\n /** Unique client identifier */\n clientId: string;\n /** Express response object for sending events */\n response: Response;\n /** Optional run ID for filtering events */\n runId?: string;\n /** When the connection was established */\n connectedAt: Date;\n /** Last time data was sent to this client */\n lastActivity: Date;\n}\n\n/**\n * SSE Event to send to clients\n *\n * Follows the SSE specification format\n */\ninterface SseEvent {\n /** Event type (optional, defaults to 'message') */\n event?: string;\n /** Event data (will be JSON stringified) */\n data: any;\n /** Event ID (optional) */\n id?: string;\n}\n\n/**\n * SseTransport - Server-Sent Events transport for AG-UI events\n *\n * Manages SSE connections and streams AG-UI protocol events to connected clients.\n * Supports multiple concurrent connections, heartbeat mechanism, and run-specific filtering.\n *\n * @example\n * ```typescript\n * const transport = new SseTransport();\n *\n * // Handle new connection\n * app.get('/api/events', (req, res) => {\n * const clientId = req.query.clientId as string;\n * const runId = req.query.runId as string | undefined;\n * transport.handleConnection(clientId, res, runId);\n * });\n *\n * // Send event to specific client\n * transport.sendToClient('client-123', {\n * event: 'TOOL_CALL_START',\n * data: { type: 'TOOL_CALL_START', toolCallId: 'tool-1' }\n * });\n *\n * // Broadcast to all clients watching a specific run\n * transport.broadcastToRun('run-123', {\n * event: 'RUN_FINISHED',\n * data: { type: 'RUN_FINISHED', runId: 'run-123' }\n * });\n * ```\n */\nexport class SseTransport {\n private clients: Map<string, SseClient> = new Map();\n private heartbeatInterval: NodeJS.Timeout | null = null;\n private readonly HEARTBEAT_INTERVAL_MS = 30000; // 30 seconds\n\n /**\n * Create a new SSE transport instance\n */\n constructor() {\n this.startHeartbeat();\n }\n\n /**\n * Handle a new SSE connection from a client\n *\n * Sets up proper SSE headers, registers the client, and handles cleanup on disconnect.\n *\n * @param clientId - Unique identifier for this client\n * @param res - Express response object\n * @param runId - Optional run ID to filter events\n * @param replayEvents - Optional: buffered events to replay to this client\n */\n handleConnection(\n clientId: string,\n res: Response,\n runId?: string,\n replayEvents?: Array<{ event: string; data: any }>\n ): void {\n // Set SSE headers\n res.setHeader(\"Content-Type\", \"text/event-stream\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.setHeader(\"Connection\", \"keep-alive\");\n res.setHeader(\"X-Accel-Buffering\", \"no\"); // Disable nginx buffering\n\n // Enable CORS for SSE (if needed)\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n\n // Disable compression for SSE\n res.flushHeaders();\n\n const client: SseClient = {\n clientId,\n response: res,\n runId,\n connectedAt: new Date(),\n lastActivity: new Date(),\n };\n\n this.clients.set(clientId, client);\n\n // Send initial connection acknowledgment\n this.sendToClient(clientId, {\n event: \"connected\",\n data: {\n clientId,\n runId,\n timestamp: Date.now(),\n },\n });\n\n // Replay buffered events if provided\n if (replayEvents && replayEvents.length > 0) {\n for (const event of replayEvents) {\n this.sendToClient(clientId, event);\n }\n }\n\n // Handle client disconnect\n res.on(\"close\", () => {\n this.removeClient(clientId);\n });\n }\n\n /**\n * Send an event to a specific client\n *\n * @param clientId - Target client ID\n * @param event - Event to send\n * @returns true if sent successfully, false if client not found\n */\n sendToClient(clientId: string, event: SseEvent): boolean {\n const client = this.clients.get(clientId);\n if (!client) {\n return false;\n }\n\n return this.writeEvent(client, event);\n }\n\n /**\n * Broadcast an event to all connected clients\n *\n * @param event - Event to broadcast\n * @returns Number of clients that received the event\n */\n broadcast(event: SseEvent): number {\n let sentCount = 0;\n\n for (const client of this.clients.values()) {\n if (this.writeEvent(client, event)) {\n sentCount++;\n }\n }\n\n return sentCount;\n }\n\n /**\n * Broadcast an event to all clients watching a specific run\n *\n * @param runId - Target run ID\n * @param event - Event to send\n * @returns Number of clients that received the event\n */\n broadcastToRun(runId: string, event: SseEvent): number {\n let sentCount = 0;\n let matchingClients = 0;\n\n for (const client of this.clients.values()) {\n if (client.runId === runId) {\n matchingClients++;\n if (this.writeEvent(client, event)) {\n sentCount++;\n }\n }\n }\n\n return sentCount;\n }\n\n /**\n * Remove a client connection\n *\n * @param clientId - Client to remove\n * @returns true if client was removed, false if not found\n */\n removeClient(clientId: string): boolean {\n const client = this.clients.get(clientId);\n if (!client) {\n return false;\n }\n\n // Try to close the response if it's still open\n try {\n if (!client.response.writableEnded) {\n client.response.end();\n }\n } catch (error) {\n // Connection already closed, ignore\n }\n\n this.clients.delete(clientId);\n return true;\n }\n\n /**\n * Get the number of active connections\n *\n * @returns Number of connected clients\n */\n getClientCount(): number {\n return this.clients.size;\n }\n\n /**\n * Get the number of clients watching a specific run\n *\n * @param runId - Run ID to check\n * @returns Number of clients watching this run\n */\n getRunClientCount(runId: string): number {\n let count = 0;\n for (const client of this.clients.values()) {\n if (client.runId === runId) {\n count++;\n }\n }\n return count;\n }\n\n /**\n * Get all active client IDs\n *\n * @returns Array of client IDs\n */\n getClientIds(): string[] {\n return Array.from(this.clients.keys());\n }\n\n /**\n * Shutdown the transport and close all connections\n */\n shutdown(): void {\n // Stop heartbeat\n if (this.heartbeatInterval) {\n clearInterval(this.heartbeatInterval);\n this.heartbeatInterval = null;\n }\n\n // Close all client connections\n for (const clientId of this.clients.keys()) {\n this.removeClient(clientId);\n }\n }\n\n /**\n * Write an event to a client connection\n *\n * Formats the event according to SSE specification and writes it to the response.\n *\n * @param client - Target client\n * @param event - Event to write\n * @returns true if successful, false if write failed\n */\n private writeEvent(client: SseClient, event: SseEvent): boolean {\n try {\n const { response } = client;\n\n // Check if response is still writable\n if (response.writableEnded || !response.writable) {\n this.removeClient(client.clientId);\n return false;\n }\n\n // Format SSE message\n let message = \"\";\n\n // Add event type if specified\n if (event.event) {\n message += `event: ${event.event}\\n`;\n }\n\n // Add event ID if specified\n if (event.id) {\n message += `id: ${event.id}\\n`;\n }\n\n // Add data (JSON stringify if object)\n const dataString =\n typeof event.data === \"string\"\n ? event.data\n : JSON.stringify(event.data);\n\n // SSE spec requires data to be on separate lines if multiline\n const dataLines = dataString.split(\"\\n\");\n for (const line of dataLines) {\n message += `data: ${line}\\n`;\n }\n\n // SSE messages end with double newline\n message += \"\\n\";\n\n // Write to response\n response.write(message);\n\n // Update last activity\n client.lastActivity = new Date();\n\n return true;\n } catch (error) {\n // Connection error, remove client\n this.removeClient(client.clientId);\n return false;\n }\n }\n\n /**\n * Start the heartbeat mechanism\n *\n * Sends periodic ping events to keep connections alive\n */\n private startHeartbeat(): void {\n this.heartbeatInterval = setInterval(() => {\n const pingEvent: SseEvent = {\n event: \"ping\",\n data: { timestamp: Date.now() },\n };\n\n // Send ping to all clients\n for (const client of this.clients.values()) {\n this.writeEvent(client, pingEvent);\n }\n }, this.HEARTBEAT_INTERVAL_MS);\n }\n}\n", "/**\n * Execution Event Buffer\n *\n * Stores AG-UI events for executions so they can be replayed to late-joining clients\n * or retrieved for historical display.\n *\n * @module execution/transport/event-buffer\n */\n\nimport type { AgUiEvent } from \"./transport-manager.js\";\n\n/**\n * Buffered event with metadata\n */\nexport interface BufferedEvent {\n /** The AG-UI event */\n event: AgUiEvent;\n /** When the event was buffered */\n timestamp: number;\n /** Event sequence number within the execution */\n sequenceNumber: number;\n}\n\n/**\n * Event buffer for a single execution\n */\nexport interface ExecutionEventBuffer {\n /** Execution/run ID */\n executionId: string;\n /** Buffered events in order */\n events: BufferedEvent[];\n /** Next sequence number */\n nextSequence: number;\n /** When the buffer was created */\n createdAt: number;\n /** When the buffer was last updated */\n lastUpdatedAt: number;\n}\n\n/**\n * EventBuffer - In-memory storage for execution events\n *\n * Stores events for active executions and allows replay when clients connect.\n * Events are automatically pruned after executions complete to prevent memory leaks.\n */\nexport class EventBuffer {\n private buffers = new Map<string, ExecutionEventBuffer>();\n private readonly MAX_EVENTS_PER_EXECUTION = 10000; // Prevent unbounded growth\n private readonly RETENTION_MS = 1000 * 60 * 60; // 1 hour after last update\n\n /**\n * Add an event to the buffer for an execution\n *\n * @param executionId - Execution ID\n * @param event - AG-UI event to buffer\n */\n addEvent(executionId: string, event: AgUiEvent): void {\n let buffer = this.buffers.get(executionId);\n\n if (!buffer) {\n buffer = {\n executionId,\n events: [],\n nextSequence: 0,\n createdAt: Date.now(),\n lastUpdatedAt: Date.now(),\n };\n this.buffers.set(executionId, buffer);\n console.log(\"[EventBuffer] Created buffer for execution\", {\n executionId,\n timestamp: new Date().toISOString(),\n });\n }\n\n // Add event with sequence number\n const bufferedEvent: BufferedEvent = {\n event,\n timestamp: Date.now(),\n sequenceNumber: buffer.nextSequence++,\n };\n\n buffer.events.push(bufferedEvent);\n buffer.lastUpdatedAt = Date.now();\n\n // Enforce max events limit\n if (buffer.events.length > this.MAX_EVENTS_PER_EXECUTION) {\n // Remove oldest 10% of events\n const toRemove = Math.floor(this.MAX_EVENTS_PER_EXECUTION * 0.1);\n buffer.events.splice(0, toRemove);\n console.warn(\n \"[EventBuffer] Buffer size limit reached, removing oldest events\",\n {\n executionId,\n removedCount: toRemove,\n remainingCount: buffer.events.length,\n }\n );\n }\n\n console.log(\"[EventBuffer] Event added to buffer\", {\n executionId,\n eventType: event.type,\n sequenceNumber: bufferedEvent.sequenceNumber,\n totalEvents: buffer.events.length,\n });\n }\n\n /**\n * Get all buffered events for an execution\n *\n * @param executionId - Execution ID\n * @param fromSequence - Optional: only return events >= this sequence number\n * @returns Array of buffered events, or empty array if no buffer exists\n */\n getEvents(executionId: string, fromSequence?: number): BufferedEvent[] {\n const buffer = this.buffers.get(executionId);\n if (!buffer) {\n return [];\n }\n\n if (fromSequence !== undefined) {\n return buffer.events.filter((e) => e.sequenceNumber >= fromSequence);\n }\n\n return [...buffer.events];\n }\n\n /**\n * Check if a buffer exists for an execution\n *\n * @param executionId - Execution ID\n * @returns true if buffer exists\n */\n hasBuffer(executionId: string): boolean {\n return this.buffers.has(executionId);\n }\n\n /**\n * Get buffer metadata without events\n *\n * @param executionId - Execution ID\n * @returns Buffer metadata or null if not found\n */\n getBufferInfo(\n executionId: string\n ): Omit<ExecutionEventBuffer, \"events\"> | null {\n const buffer = this.buffers.get(executionId);\n if (!buffer) {\n return null;\n }\n\n return {\n executionId: buffer.executionId,\n nextSequence: buffer.nextSequence,\n createdAt: buffer.createdAt,\n lastUpdatedAt: buffer.lastUpdatedAt,\n };\n }\n\n /**\n * Remove a buffer for an execution\n *\n * @param executionId - Execution ID\n * @returns true if buffer was removed, false if it didn't exist\n */\n removeBuffer(executionId: string): boolean {\n const existed = this.buffers.has(executionId);\n if (existed) {\n this.buffers.delete(executionId);\n console.log(\"[EventBuffer] Buffer removed\", {\n executionId,\n timestamp: new Date().toISOString(),\n });\n }\n return existed;\n }\n\n /**\n * Clear all buffers\n */\n clearAll(): void {\n const count = this.buffers.size;\n this.buffers.clear();\n console.log(\"[EventBuffer] All buffers cleared\", { count });\n }\n\n /**\n * Prune stale buffers that haven't been updated recently\n *\n * Should be called periodically to prevent memory leaks.\n *\n * @returns Number of buffers pruned\n */\n pruneStale(): number {\n const now = Date.now();\n const threshold = now - this.RETENTION_MS;\n let pruned = 0;\n\n for (const [executionId, buffer] of this.buffers.entries()) {\n if (buffer.lastUpdatedAt < threshold) {\n this.buffers.delete(executionId);\n pruned++;\n }\n }\n\n if (pruned > 0) {\n console.log(\"[EventBuffer] Pruned stale buffers\", {\n count: pruned,\n remaining: this.buffers.size,\n });\n }\n\n return pruned;\n }\n\n /**\n * Get total number of buffers\n *\n * @returns Number of active buffers\n */\n getBufferCount(): number {\n return this.buffers.size;\n }\n\n /**\n * Get total number of events across all buffers\n *\n * @returns Total event count\n */\n getTotalEventCount(): number {\n let total = 0;\n for (const buffer of this.buffers.values()) {\n total += buffer.events.length;\n }\n return total;\n }\n\n /**\n * Get statistics about buffer usage\n *\n * @returns Buffer statistics\n */\n getStats(): {\n bufferCount: number;\n totalEvents: number;\n avgEventsPerBuffer: number;\n oldestBuffer: number | null;\n newestBuffer: number | null;\n } {\n const bufferCount = this.buffers.size;\n const totalEvents = this.getTotalEventCount();\n\n let oldest: number | null = null;\n let newest: number | null = null;\n\n for (const buffer of this.buffers.values()) {\n if (oldest === null || buffer.createdAt < oldest) {\n oldest = buffer.createdAt;\n }\n if (newest === null || buffer.createdAt > newest) {\n newest = buffer.createdAt;\n }\n }\n\n return {\n bufferCount,\n totalEvents,\n avgEventsPerBuffer: bufferCount > 0 ? totalEvents / bufferCount : 0,\n oldestBuffer: oldest,\n newestBuffer: newest,\n };\n }\n}\n", "/**\n * Transport Manager\n *\n * Coordinates between AG-UI adapter and SSE transport layer.\n * Acts as a facade that routes adapter events to appropriate transport methods.\n *\n * @module execution/transport/transport-manager\n */\n\nimport type { AgUiEventAdapter } from \"../output/ag-ui-adapter.js\";\nimport type {\n RunStartedEvent,\n RunFinishedEvent,\n RunErrorEvent,\n StepStartedEvent,\n StepFinishedEvent,\n ToolCallStartEvent,\n ToolCallArgsEvent,\n ToolCallEndEvent,\n ToolCallResultEvent,\n TextMessageStartEvent,\n TextMessageContentEvent,\n TextMessageEndEvent,\n StateDeltaEvent,\n StateSnapshotEvent,\n CustomEvent,\n} from \"@ag-ui/core\";\nimport { SseTransport } from \"./sse-transport.js\";\nimport { EventBuffer } from \"./event-buffer.js\";\n\n/**\n * Union type for all AG-UI events\n */\nexport type AgUiEvent =\n | RunStartedEvent\n | RunFinishedEvent\n | RunErrorEvent\n | StepStartedEvent\n | StepFinishedEvent\n | ToolCallStartEvent\n | ToolCallArgsEvent\n | ToolCallEndEvent\n | ToolCallResultEvent\n | TextMessageStartEvent\n | TextMessageContentEvent\n | TextMessageEndEvent\n | StateDeltaEvent\n | StateSnapshotEvent\n | CustomEvent;\n\n/**\n * TransportManager - Coordinates AG-UI events with SSE transport\n *\n * Manages the connection between AG-UI adapters and SSE transport,\n * routing events to appropriate broadcast methods based on run filtering.\n *\n * @example\n * ```typescript\n * const manager = new TransportManager();\n * const adapter = new AgUiEventAdapter('run-123');\n *\n * // Connect adapter to transport\n * manager.connectAdapter(adapter, 'run-123');\n *\n * // Events from adapter are now automatically broadcast via SSE\n * ```\n */\nexport class TransportManager {\n private sseTransport: SseTransport;\n private eventBuffer: EventBuffer;\n private adapterListeners: Map<AgUiEventAdapter, (event: AgUiEvent) => void> =\n new Map();\n private pruneInterval: NodeJS.Timeout | null = null;\n\n /**\n * Create a new transport manager\n *\n * Initializes the SSE transport layer and event buffer\n */\n constructor() {\n this.sseTransport = new SseTransport();\n this.eventBuffer = new EventBuffer();\n\n // Start periodic pruning of stale buffers (every 15 minutes)\n this.pruneInterval = setInterval(\n () => {\n this.eventBuffer.pruneStale();\n },\n 15 * 60 * 1000\n );\n }\n\n /**\n * Connect AG-UI adapter to SSE transport\n *\n * Subscribes to adapter's event stream and routes events to SSE transport.\n * If runId is provided, events are broadcast only to clients watching that run.\n * Otherwise, events are broadcast to all connected clients.\n *\n * @param adapter - AG-UI event adapter to connect\n * @param runId - Optional run ID for filtering events\n *\n * @example\n * ```typescript\n * const adapter = new AgUiEventAdapter('run-123');\n * manager.connectAdapter(adapter, 'run-123');\n * ```\n */\n connectAdapter(adapter: AgUiEventAdapter, runId?: string): void {\n // Create listener function\n const listener = (event: AgUiEvent) => {\n // Buffer the event if runId is provided\n if (runId) {\n this.eventBuffer.addEvent(runId, event);\n this.broadcastToRun(runId, event);\n } else {\n this.broadcast(event);\n }\n };\n\n // Store listener for cleanup\n this.adapterListeners.set(adapter, listener);\n\n // Subscribe to adapter events\n adapter.onEvent(listener);\n }\n\n /**\n * Disconnect AG-UI adapter from transport\n *\n * Removes the adapter's event listener and stops broadcasting its events.\n *\n * @param adapter - AG-UI event adapter to disconnect\n * @returns true if adapter was disconnected, false if not found\n */\n disconnectAdapter(adapter: AgUiEventAdapter): boolean {\n const listener = this.adapterListeners.get(adapter);\n if (!listener) {\n return false;\n }\n\n adapter.offEvent(listener);\n this.adapterListeners.delete(adapter);\n return true;\n }\n\n /**\n * Broadcast event to all connected clients\n *\n * @param event - AG-UI event to broadcast\n * @returns Number of clients that received the event\n *\n * @example\n * ```typescript\n * manager.broadcast({\n * type: EventType.RUN_STARTED,\n * runId: 'run-123',\n * threadId: 'run-123',\n * timestamp: Date.now()\n * });\n * ```\n */\n broadcast(event: AgUiEvent): number {\n return this.sseTransport.broadcast({\n event: event.type,\n data: event,\n });\n }\n\n /**\n * Broadcast event to clients watching a specific run\n *\n * @param runId - Target run ID\n * @param event - AG-UI event to broadcast\n * @returns Number of clients that received the event\n *\n * @example\n * ```typescript\n * manager.broadcastToRun('run-123', {\n * type: EventType.TOOL_CALL_START,\n * toolCallId: 'tool-1',\n * toolCallName: 'Read',\n * timestamp: Date.now()\n * });\n * ```\n */\n broadcastToRun(runId: string, event: AgUiEvent): number {\n const clientCount = this.sseTransport.broadcastToRun(runId, {\n event: event.type,\n data: event,\n });\n return clientCount;\n }\n\n /**\n * Get underlying SSE transport\n *\n * Provides access to the SSE transport for route handlers\n * that need to manage client connections.\n *\n * @returns SSE transport instance\n *\n * @example\n * ```typescript\n * const transport = manager.getSseTransport();\n *\n * // Handle SSE connection in route\n * app.get('/api/events', (req, res) => {\n * transport.handleConnection(clientId, res, runId);\n * });\n * ```\n */\n getSseTransport(): SseTransport {\n return this.sseTransport;\n }\n\n /**\n * Get number of connected adapters\n *\n * @returns Number of active adapter connections\n */\n getAdapterCount(): number {\n return this.adapterListeners.size;\n }\n\n /**\n * Get buffered events for an execution\n *\n * Returns all events that have been buffered for the specified execution.\n * Useful for replaying events to late-joining clients.\n *\n * @param runId - Target run ID\n * @param fromSequence - Optional: only return events >= this sequence number\n * @returns Array of buffered events\n *\n * @example\n * ```typescript\n * const events = manager.getBufferedEvents('run-123');\n * for (const buffered of events) {\n * console.log(buffered.event.type, buffered.sequenceNumber);\n * }\n * ```\n */\n getBufferedEvents(runId: string, fromSequence?: number) {\n return this.eventBuffer.getEvents(runId, fromSequence);\n }\n\n /**\n * Check if events are buffered for an execution\n *\n * @param runId - Target run ID\n * @returns true if events are buffered\n */\n hasBufferedEvents(runId: string): boolean {\n return this.eventBuffer.hasBuffer(runId);\n }\n\n /**\n * Get buffer statistics\n *\n * @returns Buffer statistics\n */\n getBufferStats() {\n return this.eventBuffer.getStats();\n }\n\n /**\n * Shutdown transport manager\n *\n * Disconnects all adapters and shuts down SSE transport.\n * Closes all client connections and releases resources.\n */\n shutdown(): void {\n // Stop pruning interval\n if (this.pruneInterval) {\n clearInterval(this.pruneInterval);\n this.pruneInterval = null;\n }\n\n // Disconnect all adapters\n for (const adapter of this.adapterListeners.keys()) {\n this.disconnectAdapter(adapter);\n }\n\n // Shutdown SSE transport\n this.sseTransport.shutdown();\n\n // Clear event buffers\n this.eventBuffer.clearAll();\n }\n}\n", "/**\n * File watcher service for the server\n * Reuses the CLI watcher with server-specific callbacks\n */\n\nimport type Database from \"better-sqlite3\";\nimport {\n startWatcher as startCliWatcher,\n type WatcherStats,\n} from \"@sudocode-ai/cli/dist/watcher.js\";\n\nexport interface ServerWatcherOptions {\n /**\n * Database instance\n */\n db: Database.Database;\n /**\n * Base directory to watch (e.g., .sudocode)\n */\n baseDir: string;\n /**\n * Debounce delay in milliseconds (default: 2000)\n */\n debounceDelay?: number;\n /**\n * Enable reverse sync (JSONL \u2192 Markdown) when JSONL files change (default: false)\n */\n syncJSONLToMarkdown?: boolean;\n /**\n * Callback for file change events\n * This will be used to broadcast WebSocket updates\n */\n onFileChange?: (info: {\n filePath: string;\n event: \"add\" | \"change\" | \"unlink\";\n entityType?: \"spec\" | \"issue\";\n entityId?: string;\n }) => void;\n}\n\nexport interface ServerWatcherControl {\n /**\n * Stop watching files\n */\n stop: () => Promise<void>;\n /**\n * Get watcher statistics\n */\n getStats: () => WatcherStats;\n}\n\n/**\n * Start the file watcher for the server\n * This wraps the CLI watcher with server-specific logging and callbacks\n */\nexport function startServerWatcher(\n options: ServerWatcherOptions\n): ServerWatcherControl {\n const {\n db,\n baseDir,\n debounceDelay = 2000,\n syncJSONLToMarkdown = false,\n onFileChange,\n } = options;\n\n console.log(`[watcher] Starting file watcher for ${baseDir}`);\n console.log(`[watcher] Debounce delay: ${debounceDelay}ms`);\n if (syncJSONLToMarkdown) {\n console.log(`[watcher] Reverse sync (JSONL \u2192 Markdown) enabled`);\n }\n\n // Start the CLI watcher with server-specific callbacks\n // TODO: Migrate away from parsing messages and start a watcher directly instead of using the CLI watcher.\n const control = startCliWatcher({\n db,\n baseDir,\n debounceDelay,\n syncJSONLToMarkdown,\n onLog: (message) => {\n console.log(message);\n\n // Extract entity info from log messages if available\n // Log format: \"[watch] <event> <path>\" or \"[watch] Synced <type> <id> (<action>)\"\n if (onFileChange) {\n // TODO: Use something more robust than regex parsing here.\n\n // Match markdown sync log: \"[watch] Synced issue ISSUE-001 (updated)\"\n const syncMatch = message.match(\n /\\[watch\\] Synced (spec|issue) ([A-Z]+-\\d+) \\((created|updated)\\)/\n );\n if (syncMatch) {\n const [, entityType, entityId] = syncMatch;\n onFileChange({\n filePath: \"\", // Path not available in sync message\n event: \"change\",\n entityType: entityType as \"spec\" | \"issue\",\n entityId,\n });\n return; // Early return to avoid double-processing\n }\n\n // Match JSONL file change: \"[watch] change issues.jsonl\" or \"[watch] change specs.jsonl\"\n const jsonlChangeMatch = message.match(\n /\\[watch\\] change (issues|specs)\\.jsonl/\n );\n if (jsonlChangeMatch) {\n const [, entityType] = jsonlChangeMatch;\n // For JSONL changes, we don't know which specific entity changed\n // so we broadcast a generic update that will trigger a refetch\n onFileChange({\n filePath: `${entityType}.jsonl`,\n event: \"change\",\n entityType: (entityType === \"issues\" ? \"issue\" : \"spec\") as\n | \"spec\"\n | \"issue\",\n entityId: \"*\", // Wildcard to indicate \"any entity of this type\"\n });\n }\n }\n },\n onError: (error) => {\n console.error(`[watcher] Error: ${error.message}`);\n },\n });\n\n return {\n stop: async () => {\n console.log(\"[watcher] Stopping file watcher...\");\n await control.stop();\n },\n getStats: control.getStats,\n };\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["issues_exports", "__export", "createNewIssue", "deleteExistingIssue", "getAllIssues", "getIssueById", "updateExistingIssue", "getIssue", "listIssues", "createIssue", "updateIssue", "deleteIssue", "db", "options", "id", "input", "init_issues", "__esmMin", "specs_exports", "__export", "createNewSpec", "deleteExistingSpec", "getAllSpecs", "getSpecById", "updateExistingSpec", "getSpec", "listSpecs", "createSpec", "updateSpec", "deleteSpec", "db", "options", "id", "input", "init_specs", "__esmMin", "express", "cors", "dotenv", "path", "http", "fileURLToPath", "readFileSync", "existsSync", "Database", "path", "fs", "EXECUTIONS_TABLE", "EXECUTIONS_INDEXES", "PROMPT_TEMPLATES_TABLE", "PROMPT_TEMPLATES_INDEXES", "EXECUTION_LOGS_TABLE", "EXECUTION_LOGS_INDEXES", "randomUUID", "PromptTemplateEngine", "template", "context", "result", "previousResult", "match", "key", "value", "content", "fullMatch", "array", "replacement", "item", "tagName", "openPattern", "closePattern", "openIndex", "depth", "i", "contentStart", "nestedOpen", "close", "path", "keys", "errors", "ifCount", "endIfCount", "eachCount", "endEachCount", "DEFAULT_ISSUE_TEMPLATE", "initializeDefaultTemplates", "db", "validation", "PromptTemplateEngine", "templateId", "randomUUID", "variables", "getDefaultTemplate", "type", "getTemplateById", "initDatabase", "config", "dbPath", "readOnly", "dir", "db", "Database", "EXECUTIONS_TABLE", "PROMPT_TEMPLATES_TABLE", "EXECUTION_LOGS_TABLE", "EXECUTIONS_INDEXES", "PROMPT_TEMPLATES_INDEXES", "EXECUTION_LOGS_INDEXES", "initializeDefaultTemplates", "hasCliTables", "getDatabaseInfo", "tables", "version", "t", "path", "Mutex", "fs", "path", "WorktreeError", "message", "code", "cause", "execSync", "GitCli", "command", "cwd", "execSync", "error", "stderr", "stdout", "message", "WorktreeError", "arg", "repoPath", "worktreePath", "branch", "force", "escapedPath", "escapedBranch", "output", "worktrees", "lines", "line", "currentWorktree", "branchRef", "partial", "branchName", "baseBranchOrCommit", "escapedBase", "patterns", "p", "initDatabase", "importFromJSONL", "execSync", "WorktreeManager", "config", "git", "GitCli", "path", "lock", "Mutex", "params", "repoPath", "branchName", "worktreePath", "_baseBranch", "createBranch", "commitSha", "targetCommit", "parentDir", "fs", "WorktreeError", "error", "mainSudocodeDir", "worktreeSudocodeDir", "jsonlFiles", "file", "mainFile", "worktreeFile", "mainStats", "worktreeStats", "mainConfig", "worktreeConfig", "worktreeDbPath", "db", "dbStats", "release", "effectiveRepoPath", "worktrees", "normalizedWorktreePath", "worktreeInfo", "w", "worktreeName", "metadataPath", "lastError", "maxRetries", "attempt", "gitCommonDir", "gitDirPath", "fs", "path", "DEFAULT_WORKTREE_CONFIG", "validateWorktreeConfig", "rawConfig", "warnings", "config", "p", "isValidGitBranchPrefix", "prefix", "invalidPatterns", "pattern", "loadWorktreeConfig", "projectRoot", "configPath", "rawFileContent", "worktreeConfig", "error", "errorMessage", "cachedConfig", "cachedProjectRoot", "getWorktreeConfig", "forceReload", "warning", "randomUUID", "createExecution", "db", "input", "id", "now", "issue_uuid", "issue", "execution", "getExecution", "updateExecution", "db", "id", "input", "execution", "getExecution", "updates", "values", "updated", "randomUUID", "ExecutionLifecycleService", "db", "repoPath", "worktreeManager", "config", "getWorktreeConfig", "WorktreeManager", "params", "issueId", "issueTitle", "agentType", "targetBranch", "existingExecution", "executionId", "branchName", "sanitizedTitle", "sanitizeForBranchName", "worktreePath", "path", "worktreeCreated", "createExecution", "error", "cleanupError", "execution", "getExecution", "managedWorktrees", "w", "worktree", "str", "randomUUID", "spawn", "crypto", "POOL_SIZE_MULTIPLIER", "pool", "poolOffset", "fillPool", "bytes", "crypto", "random", "customRandom", "alphabet", "defaultSize", "getRandom", "mask", "step", "size", "id", "i", "customAlphabet", "generateId", "prefix", "nanoid", "customAlphabet", "SimpleProcessManager", "_defaultConfig", "config", "mergedConfig", "childProcess", "id", "generateId", "managedProcess", "spawn", "timeoutHandle", "code", "signal", "duration", "totalProcesses", "currentTotal", "cleanupTimer", "error", "processId", "managed", "exitPromise", "resolve", "timeoutPromise", "input", "reject", "handler", "data", "processIds", "timer", "buildClaudeConfig", "config", "args", "SimpleExecutionEngine", "_processManager", "_config", "task", "tasks", "ids", "id", "tasksProcessed", "initialQueueSize", "error", "depId", "result", "attempt", "runningTask", "taskId", "startTime", "managedProcess", "output", "errorOutput", "processConfig", "buildClaudeConfig", "data", "type", "endTime", "resolvers", "resolver", "handler", "process", "timeoutMs", "resolve", "reject", "checkInterval", "currentProcess", "_output", "_taskId", "_error", "maxRetries", "currentAttempt", "retryTask", "now", "failedResult", "queueIndex", "t", "interval", "running", "queuePos", "existing", "taskIds", "runningTaskIds", "processId", "CircuitBreakerManager", "name", "config", "breaker", "error", "calculateBackoff", "attempt", "config", "delay", "jitterAmount", "jitterOffset", "isRetryableError", "error", "policy", "errorMessage", "retryablePattern", "isRetryableExitCode", "exitCode", "isRetryableResult", "result", "sleep", "ms", "resolve", "createAttempt", "attemptNumber", "success", "options", "now", "DEFAULT_RETRY_POLICY", "ResilientExecutor", "engine", "defaultPolicy", "DEFAULT_RETRY_POLICY", "CircuitBreakerManager", "task", "policy", "retryPolicy", "circuitBreakerName", "attempts", "breaker", "attemptNumber", "handler", "createAttempt", "attemptStart", "taskId", "result", "attemptDuration", "successAttempt", "willRetry", "isRetryableResult", "failureAttempt", "backoffDelay", "calculateBackoff", "sleep", "error", "err", "pattern", "errorAttempt", "tasks", "promises", "name", "finalAttempt", "attemptCount", "totalSuccessful", "previousTotal", "generateId", "prefix", "timestamp", "random", "renderTemplate", "template", "context", "rendered", "placeholderRegex", "match", "path", "value", "extractValue", "obj", "parts", "part", "evaluateCondition", "condition", "context", "rendered", "renderTemplate", "LinearOrchestrator", "executor", "storage", "agUiAdapter", "lifecycleService", "workflow", "workDir", "options", "execution", "error", "handler", "executionId", "checkpoint", "resolve", "stepId", "stepIndex", "s", "status", "result", "reject", "checkInterval", "current", "workflowId", "checkpointInterval", "step", "r", "prompt", "renderTemplate", "task", "generateId", "context", "contextKey", "resultPath", "value", "extractValue", "depId", "depIndex", "depResult", "evaluateCondition", "ClaudeCodeOutputProcessor", "line", "trimmed", "data", "messageType", "message", "error", "errorMessage", "errorInfo", "handler", "toolName", "call", "path", "change", "operation", "toolCalls", "fileChanges", "toolCallsByType", "fileOperationsByType", "completedCalls", "successfulCalls", "successRate", "duration", "toolCall", "fileChange", "inputCost", "outputCost", "cacheCost", "e", "filePath", "content", "type", "timestamp", "toolUse", "toolResult", "item", "c", "usage", "metrics", "handlerError", "EventType", "AgUiEventAdapter", "runId", "threadId", "processor", "listener", "metadata", "event", "result", "metrics", "toolCall", "toolCallId", "timestamp", "messageId", "startEvent", "argsEvent", "toolInfo", "duration", "endEvent", "resultEvent", "fileChange", "error", "message", "contentEvent", "usage", "usageEvent", "updates", "delta", "key", "value", "stepId", "stepName", "status", "output", "stack", "code", "createAgUiSystem", "runId", "threadId", "processor", "ClaudeCodeOutputProcessor", "adapter", "AgUiEventAdapter", "ExecutionService", "db", "repoPath", "lifecycleService", "transportManager", "PromptTemplateEngine", "ExecutionLifecycleService", "issueId", "options", "issue", "relatedSpecs", "context", "s", "template", "customTemplate", "getTemplateById", "defaultTemplate", "getDefaultTemplate", "renderedPrompt", "defaultConfig", "warnings", "errors", "config", "prompt", "mode", "execution", "workDir", "result", "executionId", "randomUUID", "createExecution", "workflow", "processManager", "SimpleProcessManager", "engine", "SimpleExecutionEngine", "executor", "ResilientExecutor", "agUiAdapter", "agUiSystem", "createAgUiSystem", "lineBuffer", "data", "type", "newlineIndex", "line", "err", "orchestrator", "LinearOrchestrator", "updateExecution", "error", "_executionId", "updateError", "feedback", "prevExecution", "getExecution", "followUpPrompt", "newExecutionId", "newExecution", "_execId", "cancelPromises", "resolve", "init_issues", "Router", "generateIssueId", "WebSocketServer", "WebSocket", "randomUUID", "LOG_CONNECTIONS", "WebSocketManager", "server", "path", "ws", "_req", "clientId", "client", "data", "error", "message", "subscription", "entityType", "entityId", "typeSubscription", "sentCount", "resolve", "websocketManager", "initWebSocketServer", "broadcastIssueUpdate", "issueId", "action", "broadcastSpecUpdate", "specId", "broadcastFeedbackUpdate", "broadcastRelationshipUpdate", "getWebSocketStats", "shutdownWebSocketServer", "path", "getSudocodeDir", "exportToJSONL", "syncJSONLToMarkdown", "path", "exportDebouncer", "getExportDebouncer", "db", "executeFullExport", "outputDir", "getSudocodeDir", "exportToJSONL", "syncEntityToMarkdown", "entityId", "entityType", "getIssueById", "issue", "mdPath", "syncJSONLToMarkdown", "getSpecById", "spec", "triggerExport", "debouncer", "error", "createIssuesRouter", "db", "router", "Router", "req", "res", "options", "issues", "getAllIssues", "error", "id", "issue", "getIssueById", "title", "content", "status", "priority", "assignee", "parent_id", "outputDir", "getSudocodeDir", "generateIssueId", "createNewIssue", "triggerExport", "syncEntityToMarkdown", "broadcastIssueUpdate", "archived", "updateInput", "updateExistingIssue", "deleteExistingIssue", "init_specs", "Router", "generateSpecId", "path", "createSpecsRouter", "db", "router", "Router", "req", "res", "options", "specs", "getAllSpecs", "error", "id", "spec", "getSpecById", "title", "content", "priority", "parent_id", "outputDir", "getSudocodeDir", "generateSpecId", "file_path", "createNewSpec", "triggerExport", "syncEntityToMarkdown", "broadcastSpecUpdate", "archived", "updateInput", "updateExistingSpec", "deleteExistingSpec", "Router", "addRelationship", "getRelationship", "removeRelationship", "getOutgoingRelationships", "getIncomingRelationships", "getAllRelationships", "createRelationship", "db", "input", "deleteRelationship", "db", "from_id", "from_type", "to_id", "to_type", "relationship_type", "removeRelationship", "getEntityOutgoingRelationships", "entity_id", "entity_type", "getOutgoingRelationships", "getEntityIncomingRelationships", "getIncomingRelationships", "getEntityRelationships", "getAllRelationships", "createRelationshipsRouter", "db", "router", "Router", "req", "res", "entity_type", "entity_id", "relationships", "getEntityRelationships", "error", "relationship_type", "getEntityOutgoingRelationships", "getEntityIncomingRelationships", "from_id", "from_type", "to_id", "to_type", "metadata", "validTypes", "relationship", "createRelationship", "broadcastRelationshipUpdate", "triggerExport", "deleteRelationship", "Router", "createFeedback", "getFeedback", "updateFeedback", "deleteFeedback", "listFeedback", "getFeedbackForSpec", "getFeedbackForIssue", "dismissFeedback", "createNewFeedback", "db", "input", "getFeedbackById", "id", "updateExistingFeedback", "deleteExistingFeedback", "getAllFeedback", "options", "createFeedbackRouter", "db", "router", "Router", "req", "res", "options", "feedback", "getAllFeedback", "error", "id", "getFeedbackById", "issue_id", "spec_id", "feedback_type", "content", "agent", "anchor", "dismissed", "validTypes", "validAnchorStatuses", "createNewFeedback", "broadcastFeedbackUpdate", "updateInput", "updateExistingFeedback", "deleteExistingFeedback", "Router", "createExecutionsRouter", "db", "repoPath", "transportManager", "executionService", "router", "Router", "service", "ExecutionService", "req", "res", "issueId", "options", "result", "error", "config", "prompt", "execution", "errorMessage", "statusCode", "executionId", "executions", "feedback", "followUpExecution", "exists", "Router", "randomUUID", "createExecutionStreamRoutes", "transportManager", "router", "req", "res", "executionId", "clientId", "replayEvents", "buffered", "SseTransport", "clientId", "res", "runId", "replayEvents", "client", "event", "sentCount", "matchingClients", "count", "response", "message", "dataLines", "line", "pingEvent", "EventBuffer", "executionId", "event", "buffer", "bufferedEvent", "toRemove", "fromSequence", "e", "existed", "count", "threshold", "pruned", "total", "bufferCount", "totalEvents", "oldest", "newest", "TransportManager", "SseTransport", "EventBuffer", "adapter", "runId", "listener", "event", "fromSequence", "init_issues", "init_specs", "startCliWatcher", "startServerWatcher", "options", "db", "baseDir", "debounceDelay", "syncJSONLToMarkdown", "onFileChange", "control", "message", "syncMatch", "entityType", "entityId", "jsonlChangeMatch", "error", "__filename", "fileURLToPath", "__dirname", "dotenv", "app", "express", "DEFAULT_PORT", "MAX_PORT_ATTEMPTS", "SUDOCODE_DIR", "DB_PATH", "REPO_ROOT", "db", "watcher", "transportManager", "executionService", "initialize", "initDatabase", "info", "getDatabaseInfo", "TransportManager", "ExecutionService", "worktreeConfig", "getWorktreeConfig", "worktreeManager", "WorktreeManager", "ExecutionLifecycleService", "error", "WATCH_ENABLED", "SYNC_JSONL_TO_MARKDOWN", "startServerWatcher", "broadcastIssueUpdate", "issue", "getIssueById", "broadcastSpecUpdate", "spec", "getSpecById", "cors", "createIssuesRouter", "createSpecsRouter", "createRelationshipsRouter", "createFeedbackRouter", "createExecutionsRouter", "createExecutionStreamRoutes", "_req", "res", "dbInfo", "projectRoot", "cliPackagePath", "serverPackagePath", "frontendPackagePath", "cliPackage", "readFileSync", "serverPackage", "frontendPackage", "configPath", "config", "stats", "getWebSocketStats", "isDev", "existsSync", "frontendPath", "req", "server", "startServer", "initialPort", "maxAttempts", "shouldScan", "attempt", "port", "resolve", "reject", "errorHandler", "err", "listeningHandler", "startPort", "actualPort", "initWebSocketServer", "httpUrl", "wsUrl", "green", "bold", "reset", "makeClickable", "url", "text", "reason", "promise", "shutdownWebSocketServer", "index_default"]
7
+ }