claude-flow-novice 2.16.0 → 2.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/.claude/cfn-extras/skills/GOOGLE_SHEETS_SKILLS_README.md +1 -1
  2. package/.claude/cfn-extras/skills/google-sheets-api-coordinator/SKILL.md +1 -1
  3. package/.claude/cfn-extras/skills/google-sheets-formula-builder/SKILL.md +1 -1
  4. package/.claude/cfn-extras/skills/google-sheets-progress/SKILL.md +1 -1
  5. package/.claude/commands/CFN_LOOP_FRONTEND.md +1 -1
  6. package/.claude/commands/cfn-loop-cli.md +124 -46
  7. package/.claude/commands/cfn-loop-frontend.md +1 -1
  8. package/.claude/commands/cfn-loop-task.md +2 -2
  9. package/.claude/commands/deprecated/cfn-loop.md +2 -2
  10. package/.claude/hooks/cfn-invoke-post-edit.sh +31 -5
  11. package/.claude/hooks/cfn-post-edit.config.json +9 -2
  12. package/.claude/root-claude-distribute/CFN-CLAUDE.md +1 -1
  13. package/.claude/skills/cfn-backlog-management/SKILL.md +1 -1
  14. package/.claude/skills/cfn-loop-orchestration/NORTH_STAR_INDEX.md +1 -1
  15. package/claude-assets/agents/cfn-dev-team/analysts/root-cause-analyst.md +2 -2
  16. package/claude-assets/agents/cfn-dev-team/architecture/base-template-generator.md +1 -1
  17. package/claude-assets/agents/cfn-dev-team/coordinators/cfn-frontend-coordinator.md +2 -2
  18. package/claude-assets/agents/cfn-dev-team/coordinators/handoff-coordinator.md +1 -1
  19. package/claude-assets/agents/cfn-dev-team/dev-ops/devops-engineer.md +1 -1
  20. package/claude-assets/agents/cfn-dev-team/dev-ops/docker-specialist.md +2 -2
  21. package/claude-assets/agents/cfn-dev-team/dev-ops/github-commit-agent.md +2 -2
  22. package/claude-assets/agents/cfn-dev-team/dev-ops/kubernetes-specialist.md +1 -1
  23. package/claude-assets/agents/cfn-dev-team/developers/api-gateway-specialist.md +1 -1
  24. package/claude-assets/agents/cfn-dev-team/developers/data/data-engineer.md +1 -1
  25. package/claude-assets/agents/cfn-dev-team/developers/database/database-architect.md +1 -1
  26. package/claude-assets/agents/cfn-dev-team/developers/frontend/typescript-specialist.md +1 -1
  27. package/claude-assets/agents/cfn-dev-team/developers/frontend/ui-designer.md +1 -1
  28. package/claude-assets/agents/cfn-dev-team/developers/graphql-specialist.md +1 -1
  29. package/claude-assets/agents/cfn-dev-team/documentation/pseudocode.md +1 -1
  30. package/claude-assets/agents/cfn-dev-team/product-owners/accessibility-advocate-persona.md +1 -1
  31. package/claude-assets/agents/cfn-dev-team/product-owners/cto-agent.md +1 -1
  32. package/claude-assets/agents/cfn-dev-team/product-owners/power-user-persona.md +1 -1
  33. package/claude-assets/agents/cfn-dev-team/reviewers/quality/security-specialist.md +1 -1
  34. package/claude-assets/agents/cfn-dev-team/testers/api-testing-specialist.md +1 -1
  35. package/claude-assets/agents/cfn-dev-team/testers/chaos-engineering-specialist.md +1 -1
  36. package/claude-assets/agents/cfn-dev-team/testers/contract-tester.md +1 -1
  37. package/claude-assets/agents/cfn-dev-team/testers/e2e/playwright-tester.md +1 -1
  38. package/claude-assets/agents/cfn-dev-team/testers/integration-tester.md +1 -1
  39. package/claude-assets/agents/cfn-dev-team/testers/load-testing-specialist.md +1 -1
  40. package/claude-assets/agents/cfn-dev-team/testers/mutation-testing-specialist.md +1 -1
  41. package/claude-assets/agents/cfn-dev-team/testers/unit/tdd-london-unit-swarm.md +1 -1
  42. package/claude-assets/agents/cfn-dev-team/utility/agent-builder.md +11 -0
  43. package/claude-assets/agents/cfn-dev-team/utility/analyst.md +1 -1
  44. package/claude-assets/agents/cfn-dev-team/utility/claude-code-expert.md +1 -1
  45. package/claude-assets/agents/cfn-dev-team/utility/epic-creator.md +1 -1
  46. package/claude-assets/agents/cfn-dev-team/utility/memory-leak-specialist.md +1 -1
  47. package/claude-assets/agents/cfn-dev-team/utility/researcher.md +1 -1
  48. package/claude-assets/agents/cfn-dev-team/utility/z-ai-specialist.md +1 -1
  49. package/claude-assets/agents/custom/cfn-docker-expert.md +1 -0
  50. package/claude-assets/agents/custom/cfn-loops-cli-expert.md +326 -17
  51. package/claude-assets/agents/custom/cfn-redis-operations.md +529 -529
  52. package/claude-assets/agents/custom/cfn-system-expert.md +1 -1
  53. package/claude-assets/agents/custom/trigger-dev-expert.md +369 -0
  54. package/claude-assets/agents/docker-team/micro-sprint-planner.md +747 -747
  55. package/claude-assets/agents/project-only-agents/npm-package-specialist.md +1 -1
  56. package/claude-assets/cfn-extras/skills/GOOGLE_SHEETS_SKILLS_README.md +1 -1
  57. package/claude-assets/cfn-extras/skills/google-sheets-api-coordinator/SKILL.md +1 -1
  58. package/claude-assets/cfn-extras/skills/google-sheets-formula-builder/SKILL.md +1 -1
  59. package/claude-assets/cfn-extras/skills/google-sheets-progress/SKILL.md +1 -1
  60. package/claude-assets/commands/CFN_LOOP_FRONTEND.md +1 -1
  61. package/claude-assets/commands/cfn-loop-cli.md +124 -46
  62. package/claude-assets/commands/cfn-loop-frontend.md +1 -1
  63. package/claude-assets/commands/cfn-loop-task.md +2 -2
  64. package/claude-assets/commands/deprecated/cfn-loop.md +2 -2
  65. package/claude-assets/hooks/GIT-HOOKS-USAGE-EXAMPLES.md +116 -0
  66. package/claude-assets/hooks/README-GIT-HOOKS.md +443 -0
  67. package/claude-assets/hooks/cfn-invoke-post-edit.sh +31 -5
  68. package/claude-assets/hooks/cfn-post-edit.config.json +9 -2
  69. package/claude-assets/hooks/install-git-hooks.sh +243 -0
  70. package/claude-assets/hooks/subagent-start.sh +98 -0
  71. package/claude-assets/hooks/subagent-stop.sh +93 -0
  72. package/claude-assets/hooks/validators/credential-scanner.sh +172 -0
  73. package/claude-assets/root-claude-distribute/CFN-CLAUDE.md +1 -1
  74. package/claude-assets/skills/cfn-backlog-management/SKILL.md +1 -1
  75. package/claude-assets/skills/cfn-dependency-ingestion/SKILL.md +41 -13
  76. package/claude-assets/skills/cfn-dependency-ingestion/ingest.sh +237 -0
  77. package/claude-assets/skills/cfn-dependency-ingestion/manifests/cli-mode-dependencies.txt +73 -0
  78. package/claude-assets/skills/cfn-dependency-ingestion/manifests/shared-dependencies.txt +57 -0
  79. package/claude-assets/skills/cfn-dependency-ingestion/manifests/trigger-dev-dependencies.txt +82 -0
  80. package/claude-assets/skills/cfn-dependency-ingestion/manifests/trigger-mode-dependencies.txt +80 -0
  81. package/claude-assets/skills/cfn-environment-sanitization/sanitize-environment.sh +14 -4
  82. package/claude-assets/skills/cfn-loop-orchestration/NORTH_STAR_INDEX.md +1 -1
  83. package/claude-assets/skills/cfn-provider-routing/SKILL.md +23 -0
  84. package/claude-assets/skills/docker-build/build.sh +1 -1
  85. package/dist/agent/skill-mcp-selector.js +2 -1
  86. package/dist/agent/skill-mcp-selector.js.map +1 -1
  87. package/dist/agents/agent-loader.js +165 -146
  88. package/dist/agents/agent-loader.js.map +1 -1
  89. package/dist/cli/agent-executor.js +470 -26
  90. package/dist/cli/agent-executor.js.map +1 -1
  91. package/dist/cli/agent-prompt-builder.js +2 -2
  92. package/dist/cli/agent-prompt-builder.js.map +1 -1
  93. package/dist/cli/agent-spawn.js +7 -4
  94. package/dist/cli/agent-spawn.js.map +1 -1
  95. package/dist/cli/agent-spawner.js +51 -4
  96. package/dist/cli/agent-spawner.js.map +1 -1
  97. package/dist/cli/agent-token-manager.js +2 -1
  98. package/dist/cli/agent-token-manager.js.map +1 -1
  99. package/dist/cli/anthropic-client.js +117 -11
  100. package/dist/cli/anthropic-client.js.map +1 -1
  101. package/dist/cli/cfn-context.js +2 -1
  102. package/dist/cli/cfn-context.js.map +1 -1
  103. package/dist/cli/cfn-metrics.js +2 -1
  104. package/dist/cli/cfn-metrics.js.map +1 -1
  105. package/dist/cli/cfn-redis.js +2 -1
  106. package/dist/cli/cfn-redis.js.map +1 -1
  107. package/dist/cli/cli-agent-context.js +2 -0
  108. package/dist/cli/cli-agent-context.js.map +1 -1
  109. package/dist/cli/config-manager.js +4 -252
  110. package/dist/cli/config-manager.js.map +1 -1
  111. package/dist/cli/conversation-fork-cleanup.js +2 -1
  112. package/dist/cli/conversation-fork-cleanup.js.map +1 -1
  113. package/dist/cli/conversation-fork.js +2 -1
  114. package/dist/cli/conversation-fork.js.map +1 -1
  115. package/dist/cli/coordination/agent-messaging.js +415 -0
  116. package/dist/cli/coordination/agent-messaging.js.map +1 -0
  117. package/dist/cli/coordination/wait-for-threshold.js +232 -0
  118. package/dist/cli/coordination/wait-for-threshold.js.map +1 -0
  119. package/dist/cli/iteration-history.js +2 -1
  120. package/dist/cli/iteration-history.js.map +1 -1
  121. package/dist/cli/process-lifecycle.js +5 -1
  122. package/dist/cli/process-lifecycle.js.map +1 -1
  123. package/dist/cli/spawn-agent-cli.js +41 -6
  124. package/dist/cli/spawn-agent-cli.js.map +1 -1
  125. package/dist/coordination/redis-waiting-mode.js +4 -0
  126. package/dist/coordination/redis-waiting-mode.js.map +1 -1
  127. package/dist/lib/artifact-registry.js +4 -0
  128. package/dist/lib/artifact-registry.js.map +1 -1
  129. package/dist/lib/connection-pool.js +390 -0
  130. package/dist/lib/connection-pool.js.map +1 -0
  131. package/dist/lib/environment-contract.js +258 -0
  132. package/dist/lib/environment-contract.js.map +1 -0
  133. package/dist/lib/query-optimizer.js +388 -0
  134. package/dist/lib/query-optimizer.js.map +1 -0
  135. package/dist/lib/result-cache.js +285 -0
  136. package/dist/lib/result-cache.js.map +1 -0
  137. package/dist/mcp/auth-middleware.js +2 -1
  138. package/dist/mcp/auth-middleware.js.map +1 -1
  139. package/dist/mcp/playwright-mcp-server-auth.js +2 -1
  140. package/dist/mcp/playwright-mcp-server-auth.js.map +1 -1
  141. package/package.json +3 -1
  142. package/scripts/build-agent-image.sh +1 -1
  143. package/scripts/cost-allocation-tracker.sh +632 -0
  144. package/scripts/docker-rebuild-all-agents.sh +2 -2
  145. package/scripts/reorganize-tests.sh +280 -0
  146. package/scripts/trigger-dev-setup.sh +12 -0
  147. package/tests/README.md +45 -0
  148. package/.claude/commands/cost-savings-status.md +0 -34
  149. package/.claude/commands/metrics-summary.md +0 -58
  150. package/claude-assets/agents/cfn-dev-team/dev-ops/monitoring-specialist.md +0 -768
  151. package/claude-assets/agents/custom/test-mcp-access.md +0 -24
  152. package/claude-assets/commands/cost-savings-status.md +0 -34
  153. package/claude-assets/commands/metrics-summary.md +0 -58
  154. package/tests/test-memory-leak-task-mode.sh +0 -435
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/cli/coordination/agent-messaging.ts"],"sourcesContent":["#!/usr/bin/env node\r\n/**\r\n * Agent Messaging - Bidirectional Redis Communication\r\n *\r\n * Enables Main Chat to send commands to running CLI agents, and agents to process them.\r\n *\r\n * Main Chat → Agent: LPUSH cfn:agent:{taskId}:{agentId}:commands\r\n * Agent → Main Chat: SET cfn:agent:{taskId}:{agentId}:status (response)\r\n *\r\n * Supported Commands:\r\n * - status: Request agent status update\r\n * - redirect: Redirect agent to new task/context\r\n * - abort: Request clean agent abort\r\n * - pause: Request agent pause for N seconds\r\n *\r\n * Usage (Main Chat - send command):\r\n * npx tsx src/cli/coordination/agent-messaging.ts send \\\r\n * --task-id <id> --agent-id <aid> --command status\r\n *\r\n * Usage (Agent - process commands):\r\n * npx tsx src/cli/coordination/agent-messaging.ts listen \\\r\n * --task-id <id> --agent-id <aid> --poll-interval 5\r\n */\r\n\r\nimport { createClient, RedisClientType } from 'redis';\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport type CommandType = 'status' | 'redirect' | 'abort' | 'pause' | 'custom';\r\n\r\nexport interface AgentCommand {\r\n id: string;\r\n type: CommandType;\r\n timestamp: string;\r\n payload?: Record<string, unknown>;\r\n replyTo?: string; // Optional key for response\r\n}\r\n\r\nexport interface AgentStatus {\r\n agentId: string;\r\n taskId: string;\r\n status: 'running' | 'paused' | 'aborting' | 'completed';\r\n timestamp: string;\r\n progress?: number;\r\n currentStep?: string;\r\n metadata?: Record<string, unknown>;\r\n}\r\n\r\nexport interface MessagingConfig {\r\n taskId: string;\r\n agentId: string;\r\n redisHost?: string;\r\n redisPort?: number;\r\n redisPassword?: string;\r\n}\r\n\r\n// ============================================================================\r\n// Redis Key Helpers\r\n// ============================================================================\r\n\r\nfunction getCommandsKey(taskId: string, agentId: string): string {\r\n return `cfn:agent:${taskId}:${agentId}:commands`;\r\n}\r\n\r\nfunction getStatusKey(taskId: string, agentId: string): string {\r\n return `cfn:agent:${taskId}:${agentId}:status`;\r\n}\r\n\r\nfunction getResponseKey(taskId: string, agentId: string, commandId: string): string {\r\n return `cfn:agent:${taskId}:${agentId}:response:${commandId}`;\r\n}\r\n\r\n// ============================================================================\r\n// Main Chat Side - Send Commands\r\n// ============================================================================\r\n\r\n/**\r\n * Send a command to an agent via Redis\r\n */\r\nexport async function sendCommand(\r\n config: MessagingConfig,\r\n command: Omit<AgentCommand, 'id' | 'timestamp'>\r\n): Promise<{ sent: boolean; commandId: string }> {\r\n const {\r\n taskId,\r\n agentId,\r\n redisHost = process.env.CFN_REDIS_HOST || 'localhost',\r\n redisPort = parseInt(process.env.CFN_REDIS_PORT || '6379', 10),\r\n redisPassword = process.env.CFN_REDIS_PASSWORD || undefined\r\n } = config;\r\n\r\n const client: RedisClientType = createClient({\r\n socket: { host: redisHost, port: redisPort },\r\n password: redisPassword || undefined\r\n });\r\n\r\n const commandId = `cmd-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;\r\n const fullCommand: AgentCommand = {\r\n ...command,\r\n id: commandId,\r\n timestamp: new Date().toISOString(),\r\n replyTo: getResponseKey(taskId, agentId, commandId)\r\n };\r\n\r\n try {\r\n await client.connect();\r\n\r\n const commandsKey = getCommandsKey(taskId, agentId);\r\n await client.lPush(commandsKey, JSON.stringify(fullCommand));\r\n\r\n console.log(`[agent-msg] Sent command ${command.type} to ${agentId}`);\r\n console.log(`[agent-msg] Commands key: ${commandsKey}`);\r\n console.log(`[agent-msg] Command ID: ${commandId}`);\r\n\r\n return { sent: true, commandId };\r\n } finally {\r\n await client.disconnect();\r\n }\r\n}\r\n\r\n/**\r\n * Wait for agent response to a command\r\n */\r\nexport async function waitForResponse(\r\n config: MessagingConfig,\r\n commandId: string,\r\n timeoutSeconds: number = 30\r\n): Promise<AgentStatus | null> {\r\n const {\r\n taskId,\r\n agentId,\r\n redisHost = process.env.CFN_REDIS_HOST || 'localhost',\r\n redisPort = parseInt(process.env.CFN_REDIS_PORT || '6379', 10),\r\n redisPassword = process.env.CFN_REDIS_PASSWORD || undefined\r\n } = config;\r\n\r\n const client: RedisClientType = createClient({\r\n socket: { host: redisHost, port: redisPort },\r\n password: redisPassword || undefined\r\n });\r\n\r\n try {\r\n await client.connect();\r\n\r\n const responseKey = getResponseKey(taskId, agentId, commandId);\r\n const result = await client.blPop(responseKey, timeoutSeconds);\r\n\r\n if (result) {\r\n return JSON.parse(result.element) as AgentStatus;\r\n }\r\n return null;\r\n } finally {\r\n await client.disconnect();\r\n }\r\n}\r\n\r\n/**\r\n * Get current agent status (non-blocking)\r\n */\r\nexport async function getAgentStatus(config: MessagingConfig): Promise<AgentStatus | null> {\r\n const {\r\n taskId,\r\n agentId,\r\n redisHost = process.env.CFN_REDIS_HOST || 'localhost',\r\n redisPort = parseInt(process.env.CFN_REDIS_PORT || '6379', 10),\r\n redisPassword = process.env.CFN_REDIS_PASSWORD || undefined\r\n } = config;\r\n\r\n const client: RedisClientType = createClient({\r\n socket: { host: redisHost, port: redisPort },\r\n password: redisPassword || undefined\r\n });\r\n\r\n try {\r\n await client.connect();\r\n\r\n const statusKey = getStatusKey(taskId, agentId);\r\n const status = await client.get(statusKey);\r\n\r\n if (status) {\r\n return JSON.parse(status) as AgentStatus;\r\n }\r\n return null;\r\n } finally {\r\n await client.disconnect();\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Agent Side - Process Commands\r\n// ============================================================================\r\n\r\nexport type CommandHandler = (command: AgentCommand) => Promise<AgentStatus | void>;\r\n\r\n/**\r\n * Agent command processor - polls for and processes commands\r\n */\r\nexport class AgentCommandProcessor {\r\n private client: RedisClientType | null = null;\r\n private running = false;\r\n private handlers: Map<CommandType, CommandHandler> = new Map();\r\n\r\n constructor(private config: MessagingConfig) {\r\n // Default handlers\r\n this.handlers.set('status', this.handleStatus.bind(this));\r\n this.handlers.set('abort', this.handleAbort.bind(this));\r\n this.handlers.set('pause', this.handlePause.bind(this));\r\n }\r\n\r\n /**\r\n * Register a custom command handler\r\n */\r\n onCommand(type: CommandType, handler: CommandHandler): void {\r\n this.handlers.set(type, handler);\r\n }\r\n\r\n /**\r\n * Start processing commands (non-blocking poll loop)\r\n */\r\n async start(pollIntervalSeconds: number = 5): Promise<void> {\r\n const {\r\n taskId,\r\n agentId,\r\n redisHost = process.env.CFN_REDIS_HOST || 'localhost',\r\n redisPort = parseInt(process.env.CFN_REDIS_PORT || '6379', 10),\r\n redisPassword = process.env.CFN_REDIS_PASSWORD || undefined\r\n } = this.config;\r\n\r\n this.client = createClient({\r\n socket: { host: redisHost, port: redisPort },\r\n password: redisPassword || undefined\r\n });\r\n\r\n await this.client.connect();\r\n this.running = true;\r\n\r\n const commandsKey = getCommandsKey(taskId, agentId);\r\n console.log(`[agent-processor] Started listening on ${commandsKey}`);\r\n\r\n // Non-blocking poll loop\r\n while (this.running) {\r\n try {\r\n // Short BLPOP to check for commands without blocking too long\r\n const result = await this.client.blPop(commandsKey, pollIntervalSeconds);\r\n\r\n if (result) {\r\n const command: AgentCommand = JSON.parse(result.element);\r\n console.log(`[agent-processor] Received command: ${command.type} (${command.id})`);\r\n\r\n // Process command\r\n const handler = this.handlers.get(command.type);\r\n if (handler) {\r\n const response = await handler(command);\r\n\r\n // Send response if handler returned status\r\n if (response && command.replyTo) {\r\n await this.client.lPush(command.replyTo, JSON.stringify(response));\r\n console.log(`[agent-processor] Sent response to ${command.replyTo}`);\r\n }\r\n } else {\r\n console.warn(`[agent-processor] No handler for command type: ${command.type}`);\r\n }\r\n }\r\n } catch (err) {\r\n if (this.running) {\r\n console.error(`[agent-processor] Error processing command:`, err);\r\n }\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Stop processing commands\r\n */\r\n async stop(): Promise<void> {\r\n this.running = false;\r\n if (this.client) {\r\n await this.client.disconnect();\r\n this.client = null;\r\n }\r\n console.log(`[agent-processor] Stopped`);\r\n }\r\n\r\n /**\r\n * Update agent status in Redis\r\n */\r\n async updateStatus(status: Partial<AgentStatus>): Promise<void> {\r\n if (!this.client) return;\r\n\r\n const { taskId, agentId } = this.config;\r\n const fullStatus: AgentStatus = {\r\n agentId,\r\n taskId,\r\n status: 'running',\r\n timestamp: new Date().toISOString(),\r\n ...status\r\n };\r\n\r\n const statusKey = getStatusKey(taskId, agentId);\r\n await this.client.set(statusKey, JSON.stringify(fullStatus) as string);\r\n // Expire after 1 hour\r\n await this.client.expire(statusKey, 3600);\r\n }\r\n\r\n // Default handlers\r\n\r\n private async handleStatus(command: AgentCommand): Promise<AgentStatus> {\r\n const { taskId, agentId } = this.config;\r\n return {\r\n agentId,\r\n taskId,\r\n status: 'running',\r\n timestamp: new Date().toISOString(),\r\n metadata: { respondingTo: command.id }\r\n };\r\n }\r\n\r\n private async handleAbort(_command: AgentCommand): Promise<AgentStatus> {\r\n const { taskId, agentId } = this.config;\r\n console.log(`[agent-processor] Abort requested, shutting down...`);\r\n\r\n // Schedule stop after response\r\n setTimeout(() => this.stop(), 100);\r\n\r\n return {\r\n agentId,\r\n taskId,\r\n status: 'aborting',\r\n timestamp: new Date().toISOString()\r\n };\r\n }\r\n\r\n private async handlePause(command: AgentCommand): Promise<AgentStatus> {\r\n const { taskId, agentId } = this.config;\r\n const pauseSeconds = (command.payload?.seconds as number) || 10;\r\n\r\n console.log(`[agent-processor] Pausing for ${pauseSeconds}s...`);\r\n await new Promise(resolve => setTimeout(resolve, pauseSeconds * 1000));\r\n console.log(`[agent-processor] Resumed after pause`);\r\n\r\n return {\r\n agentId,\r\n taskId,\r\n status: 'running',\r\n timestamp: new Date().toISOString(),\r\n metadata: { pausedFor: pauseSeconds }\r\n };\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// CLI Entry Point\r\n// ============================================================================\r\n\r\nfunction printHelp(): void {\r\n console.log(`\r\nAgent Messaging - Bidirectional Redis Communication\r\n\r\nUSAGE:\r\n # Send command to agent (Main Chat side)\r\n npx tsx src/cli/coordination/agent-messaging.ts send \\\\\r\n --task-id <id> --agent-id <aid> --command <type> [--payload <json>]\r\n\r\n # Listen for commands (Agent side)\r\n npx tsx src/cli/coordination/agent-messaging.ts listen \\\\\r\n --task-id <id> --agent-id <aid> [--poll-interval <seconds>]\r\n\r\n # Get agent status (Main Chat side)\r\n npx tsx src/cli/coordination/agent-messaging.ts status \\\\\r\n --task-id <id> --agent-id <aid>\r\n\r\nCOMMANDS:\r\n status Request agent status update\r\n redirect Redirect agent to new task (include --payload)\r\n abort Request clean agent abort\r\n pause Pause agent (include --payload '{\"seconds\": 10}')\r\n custom Custom command (include --payload)\r\n\r\nEXAMPLES:\r\n # Request status from agent\r\n npx tsx src/cli/coordination/agent-messaging.ts send \\\\\r\n --task-id cfn-cli-123 --agent-id backend-dev-456 --command status\r\n\r\n # Abort agent\r\n npx tsx src/cli/coordination/agent-messaging.ts send \\\\\r\n --task-id cfn-cli-123 --agent-id backend-dev-456 --command abort\r\n\r\n # Pause agent for 30 seconds\r\n npx tsx src/cli/coordination/agent-messaging.ts send \\\\\r\n --task-id cfn-cli-123 --agent-id backend-dev-456 \\\\\r\n --command pause --payload '{\"seconds\": 30}'\r\n\r\n # Redirect agent to new context\r\n npx tsx src/cli/coordination/agent-messaging.ts send \\\\\r\n --task-id cfn-cli-123 --agent-id backend-dev-456 \\\\\r\n --command redirect --payload '{\"newTask\": \"Focus on security tests\"}'\r\n\r\n # Start listening for commands (agent side)\r\n npx tsx src/cli/coordination/agent-messaging.ts listen \\\\\r\n --task-id cfn-cli-123 --agent-id backend-dev-456 --poll-interval 5\r\n`);\r\n}\r\n\r\nasync function main(): Promise<void> {\r\n const args = process.argv.slice(2);\r\n const action = args[0];\r\n\r\n if (!action || action === '--help' || action === '-h') {\r\n printHelp();\r\n process.exit(0);\r\n }\r\n\r\n // Parse common args\r\n let taskId: string | undefined;\r\n let agentId: string | undefined;\r\n let command: CommandType | undefined;\r\n let payload: Record<string, unknown> | undefined;\r\n let pollInterval = 5;\r\n\r\n for (let i = 1; i < args.length; i++) {\r\n const arg = args[i];\r\n const value = args[i + 1];\r\n\r\n switch (arg) {\r\n case '--task-id':\r\n case '-t':\r\n taskId = value;\r\n i++;\r\n break;\r\n case '--agent-id':\r\n case '-a':\r\n agentId = value;\r\n i++;\r\n break;\r\n case '--command':\r\n case '-c':\r\n command = value as CommandType;\r\n i++;\r\n break;\r\n case '--payload':\r\n case '-p':\r\n try {\r\n payload = JSON.parse(value);\r\n } catch {\r\n console.error('Invalid JSON payload');\r\n process.exit(1);\r\n }\r\n i++;\r\n break;\r\n case '--poll-interval':\r\n pollInterval = parseInt(value, 10);\r\n i++;\r\n break;\r\n }\r\n }\r\n\r\n if (!taskId || !agentId) {\r\n console.error('Error: --task-id and --agent-id are required');\r\n process.exit(1);\r\n }\r\n\r\n const config: MessagingConfig = { taskId, agentId };\r\n\r\n switch (action) {\r\n case 'send': {\r\n if (!command) {\r\n console.error('Error: --command is required for send action');\r\n process.exit(1);\r\n }\r\n const result = await sendCommand(config, { type: command, payload });\r\n console.log(JSON.stringify(result, null, 2));\r\n\r\n // Wait for response\r\n console.log('\\n[agent-msg] Waiting for response (30s timeout)...');\r\n const response = await waitForResponse(config, result.commandId, 30);\r\n if (response) {\r\n console.log('[agent-msg] Response received:');\r\n console.log(JSON.stringify(response, null, 2));\r\n } else {\r\n console.log('[agent-msg] No response received (timeout)');\r\n }\r\n break;\r\n }\r\n\r\n case 'listen': {\r\n const processor = new AgentCommandProcessor(config);\r\n\r\n // Handle shutdown signals\r\n process.on('SIGINT', async () => {\r\n console.log('\\n[agent-msg] Received SIGINT, stopping...');\r\n await processor.stop();\r\n process.exit(0);\r\n });\r\n\r\n await processor.start(pollInterval);\r\n break;\r\n }\r\n\r\n case 'status': {\r\n const status = await getAgentStatus(config);\r\n if (status) {\r\n console.log(JSON.stringify(status, null, 2));\r\n } else {\r\n console.log('No status available for agent');\r\n }\r\n break;\r\n }\r\n\r\n default:\r\n console.error(`Unknown action: ${action}`);\r\n printHelp();\r\n process.exit(1);\r\n }\r\n}\r\n\r\n// Run if called directly\r\nif (import.meta.url.endsWith(process.argv[1]?.replace(/\\\\/g, '/') || '')) {\r\n main().catch(err => {\r\n console.error('[agent-msg] Fatal error:', err);\r\n process.exit(2);\r\n });\r\n}\r\n"],"names":["createClient","getCommandsKey","taskId","agentId","getStatusKey","getResponseKey","commandId","sendCommand","config","command","redisHost","process","env","CFN_REDIS_HOST","redisPort","parseInt","CFN_REDIS_PORT","redisPassword","CFN_REDIS_PASSWORD","undefined","client","socket","host","port","password","Date","now","Math","random","toString","substring","fullCommand","id","timestamp","toISOString","replyTo","connect","commandsKey","lPush","JSON","stringify","console","log","type","sent","disconnect","waitForResponse","timeoutSeconds","responseKey","result","blPop","parse","element","getAgentStatus","statusKey","status","get","AgentCommandProcessor","running","handlers","Map","set","handleStatus","bind","handleAbort","handlePause","onCommand","handler","start","pollIntervalSeconds","response","warn","err","error","stop","updateStatus","fullStatus","expire","metadata","respondingTo","_command","setTimeout","pauseSeconds","payload","seconds","Promise","resolve","pausedFor","printHelp","main","args","argv","slice","action","exit","pollInterval","i","length","arg","value","processor","on","url","endsWith","replace","catch"],"mappings":"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"}
@@ -0,0 +1,232 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Wait for Threshold Completion - Parallel Agent Coordination
4
+ *
5
+ * Waits for N/M agents to complete (e.g., 3/4 = 75% threshold) before continuing.
6
+ * Enables parallel agent spawning with graceful degradation on partial completion.
7
+ *
8
+ * Usage:
9
+ * npx tsx src/cli/coordination/wait-for-threshold.ts \
10
+ * --task-id <id> \
11
+ * --total-agents <n> \
12
+ * --threshold <0.0-1.0> \
13
+ * --timeout <seconds>
14
+ *
15
+ * Example:
16
+ * # Wait for 3/4 agents (75%) with 120s timeout
17
+ * npx tsx src/cli/coordination/wait-for-threshold.ts \
18
+ * --task-id cfn-cli-12345 \
19
+ * --total-agents 4 \
20
+ * --threshold 0.75 \
21
+ * --timeout 120
22
+ */ import { createClient } from 'redis';
23
+ /**
24
+ * Wait for threshold completion of agents
25
+ *
26
+ * Uses Redis BLPOP with short timeouts to poll for completion signals
27
+ * while tracking progress toward the threshold.
28
+ */ export async function waitForThreshold(config) {
29
+ const { taskId, totalAgents, threshold, timeoutSeconds, redisHost = process.env.CFN_REDIS_HOST || 'localhost', redisPort = parseInt(process.env.CFN_REDIS_PORT || '6379', 10), redisPassword = process.env.CFN_REDIS_PASSWORD || undefined } = config;
30
+ const requiredCount = Math.ceil(totalAgents * threshold);
31
+ const signalKey = `cfn-completion:${taskId}`;
32
+ const completed = [];
33
+ const startTime = Date.now();
34
+ const timeoutMs = timeoutSeconds * 1000;
35
+ // Connect to Redis
36
+ const client = createClient({
37
+ socket: {
38
+ host: redisHost,
39
+ port: redisPort
40
+ },
41
+ password: redisPassword || undefined
42
+ });
43
+ try {
44
+ await client.connect();
45
+ console.log(`[wait-threshold] Connected to Redis at ${redisHost}:${redisPort}`);
46
+ console.log(`[wait-threshold] Waiting for ${requiredCount}/${totalAgents} agents (${(threshold * 100).toFixed(0)}% threshold)`);
47
+ console.log(`[wait-threshold] Signal key: ${signalKey}`);
48
+ console.log(`[wait-threshold] Timeout: ${timeoutSeconds}s`);
49
+ // Poll loop with short BLPOP timeouts
50
+ const pollIntervalSeconds = 5; // Check every 5 seconds
51
+ while(completed.length < requiredCount){
52
+ const elapsed = Date.now() - startTime;
53
+ // Check overall timeout
54
+ if (elapsed >= timeoutMs) {
55
+ console.log(`[wait-threshold] Timeout reached after ${(elapsed / 1000).toFixed(1)}s`);
56
+ break;
57
+ }
58
+ // Calculate remaining time for this poll
59
+ const remainingMs = timeoutMs - elapsed;
60
+ const pollTimeout = Math.min(pollIntervalSeconds, Math.ceil(remainingMs / 1000));
61
+ try {
62
+ // BLPOP with short timeout - returns null on timeout
63
+ const result = await client.blPop(signalKey, pollTimeout);
64
+ if (result) {
65
+ try {
66
+ const signal = JSON.parse(result.element);
67
+ completed.push(signal);
68
+ console.log(`[wait-threshold] Received signal ${completed.length}/${requiredCount}: ${signal.agentId} (${signal.status})`);
69
+ // Check if threshold met
70
+ if (completed.length >= requiredCount) {
71
+ console.log(`[wait-threshold] Threshold met! ${completed.length}/${totalAgents} agents completed`);
72
+ break;
73
+ }
74
+ } catch (parseErr) {
75
+ console.warn(`[wait-threshold] Failed to parse signal: ${result.element}`);
76
+ }
77
+ } else {
78
+ // Timeout on BLPOP - no signal received, continue polling
79
+ const elapsedSec = ((Date.now() - startTime) / 1000).toFixed(1);
80
+ console.log(`[wait-threshold] Polling... ${completed.length}/${requiredCount} completed (${elapsedSec}s elapsed)`);
81
+ }
82
+ } catch (blpopErr) {
83
+ // Redis error during BLPOP
84
+ console.error(`[wait-threshold] BLPOP error:`, blpopErr);
85
+ break;
86
+ }
87
+ }
88
+ const elapsedMs = Date.now() - startTime;
89
+ const thresholdMet = completed.length >= requiredCount;
90
+ return {
91
+ success: thresholdMet,
92
+ completed,
93
+ timedOut: !thresholdMet && Date.now() - startTime >= timeoutMs,
94
+ thresholdMet,
95
+ completedCount: completed.length,
96
+ requiredCount,
97
+ totalAgents,
98
+ elapsedMs
99
+ };
100
+ } finally{
101
+ await client.disconnect();
102
+ }
103
+ }
104
+ /**
105
+ * Parse CLI arguments
106
+ */ function parseArgs(args) {
107
+ const config = {
108
+ threshold: 0.75,
109
+ timeoutSeconds: 120
110
+ };
111
+ for(let i = 0; i < args.length; i++){
112
+ const arg = args[i];
113
+ const value = args[i + 1];
114
+ switch(arg){
115
+ case '--task-id':
116
+ case '-t':
117
+ config.taskId = value;
118
+ i++;
119
+ break;
120
+ case '--total-agents':
121
+ case '-n':
122
+ config.totalAgents = parseInt(value, 10);
123
+ i++;
124
+ break;
125
+ case '--threshold':
126
+ config.threshold = parseFloat(value);
127
+ i++;
128
+ break;
129
+ case '--timeout':
130
+ config.timeoutSeconds = parseInt(value, 10);
131
+ i++;
132
+ break;
133
+ case '--redis-host':
134
+ config.redisHost = value;
135
+ i++;
136
+ break;
137
+ case '--redis-port':
138
+ config.redisPort = parseInt(value, 10);
139
+ i++;
140
+ break;
141
+ case '--help':
142
+ case '-h':
143
+ printHelp();
144
+ process.exit(0);
145
+ }
146
+ }
147
+ // Validate required fields
148
+ if (!config.taskId) {
149
+ console.error('Error: --task-id is required');
150
+ return null;
151
+ }
152
+ if (!config.totalAgents || config.totalAgents < 1) {
153
+ console.error('Error: --total-agents must be a positive integer');
154
+ return null;
155
+ }
156
+ if (config.threshold < 0 || config.threshold > 1) {
157
+ console.error('Error: --threshold must be between 0.0 and 1.0');
158
+ return null;
159
+ }
160
+ return config;
161
+ }
162
+ function printHelp() {
163
+ console.log(`
164
+ Wait for Threshold Completion - Parallel Agent Coordination
165
+
166
+ USAGE:
167
+ npx tsx src/cli/coordination/wait-for-threshold.ts [OPTIONS]
168
+
169
+ OPTIONS:
170
+ -t, --task-id <id> Task ID for coordination (required)
171
+ -n, --total-agents <n> Total number of agents spawned (required)
172
+ --threshold <0.0-1.0> Completion threshold (default: 0.75 = 75%)
173
+ --timeout <seconds> Overall timeout (default: 120)
174
+ --redis-host <host> Redis host (default: localhost)
175
+ --redis-port <port> Redis port (default: 6379)
176
+ -h, --help Show this help message
177
+
178
+ EXAMPLES:
179
+ # Wait for 3/4 agents (75%) with 120s timeout
180
+ npx tsx src/cli/coordination/wait-for-threshold.ts \\
181
+ --task-id cfn-cli-12345 \\
182
+ --total-agents 4 \\
183
+ --threshold 0.75 \\
184
+ --timeout 120
185
+
186
+ # Wait for all agents (100%) with 300s timeout
187
+ npx tsx src/cli/coordination/wait-for-threshold.ts \\
188
+ --task-id cfn-cli-12345 \\
189
+ --total-agents 4 \\
190
+ --threshold 1.0 \\
191
+ --timeout 300
192
+
193
+ OUTPUT:
194
+ JSON result with completion status:
195
+ {
196
+ "success": true,
197
+ "thresholdMet": true,
198
+ "completedCount": 3,
199
+ "requiredCount": 3,
200
+ "totalAgents": 4,
201
+ "elapsedMs": 45000,
202
+ "completed": [...]
203
+ }
204
+ `);
205
+ }
206
+ /**
207
+ * CLI entry point
208
+ */ async function main() {
209
+ const config = parseArgs(process.argv.slice(2));
210
+ if (!config) {
211
+ console.error('Use --help for usage information');
212
+ process.exit(1);
213
+ }
214
+ try {
215
+ const result = await waitForThreshold(config);
216
+ // Output result as JSON for scripting
217
+ console.log('\n[wait-threshold] Result:');
218
+ console.log(JSON.stringify(result, null, 2));
219
+ // Exit with appropriate code
220
+ process.exit(result.success ? 0 : 1);
221
+ } catch (error) {
222
+ console.error('[wait-threshold] Fatal error:', error);
223
+ process.exit(2);
224
+ }
225
+ }
226
+ // Run if called directly
227
+ if (import.meta.url.endsWith(process.argv[1]?.replace(/\\/g, '/') || '')) {
228
+ main();
229
+ }
230
+ export { parseArgs };
231
+
232
+ //# sourceMappingURL=wait-for-threshold.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/cli/coordination/wait-for-threshold.ts"],"sourcesContent":["#!/usr/bin/env node\r\n/**\r\n * Wait for Threshold Completion - Parallel Agent Coordination\r\n *\r\n * Waits for N/M agents to complete (e.g., 3/4 = 75% threshold) before continuing.\r\n * Enables parallel agent spawning with graceful degradation on partial completion.\r\n *\r\n * Usage:\r\n * npx tsx src/cli/coordination/wait-for-threshold.ts \\\r\n * --task-id <id> \\\r\n * --total-agents <n> \\\r\n * --threshold <0.0-1.0> \\\r\n * --timeout <seconds>\r\n *\r\n * Example:\r\n * # Wait for 3/4 agents (75%) with 120s timeout\r\n * npx tsx src/cli/coordination/wait-for-threshold.ts \\\r\n * --task-id cfn-cli-12345 \\\r\n * --total-agents 4 \\\r\n * --threshold 0.75 \\\r\n * --timeout 120\r\n */\r\n\r\nimport { createClient, RedisClientType } from 'redis';\r\n\r\nexport interface ThresholdConfig {\r\n taskId: string;\r\n totalAgents: number;\r\n threshold: number; // 0.75 for 3/4\r\n timeoutSeconds: number;\r\n redisHost?: string;\r\n redisPort?: number;\r\n redisPassword?: string;\r\n}\r\n\r\nexport interface CompletionSignal {\r\n agentId: string;\r\n taskId: string;\r\n status: 'completed' | 'failed' | 'timeout';\r\n timestamp: string;\r\n confidence?: number;\r\n metadata?: Record<string, unknown>;\r\n}\r\n\r\nexport interface ThresholdResult {\r\n success: boolean;\r\n completed: CompletionSignal[];\r\n timedOut: boolean;\r\n thresholdMet: boolean;\r\n completedCount: number;\r\n requiredCount: number;\r\n totalAgents: number;\r\n elapsedMs: number;\r\n}\r\n\r\n/**\r\n * Wait for threshold completion of agents\r\n *\r\n * Uses Redis BLPOP with short timeouts to poll for completion signals\r\n * while tracking progress toward the threshold.\r\n */\r\nexport async function waitForThreshold(config: ThresholdConfig): Promise<ThresholdResult> {\r\n const {\r\n taskId,\r\n totalAgents,\r\n threshold,\r\n timeoutSeconds,\r\n redisHost = process.env.CFN_REDIS_HOST || 'localhost',\r\n redisPort = parseInt(process.env.CFN_REDIS_PORT || '6379', 10),\r\n redisPassword = process.env.CFN_REDIS_PASSWORD || undefined\r\n } = config;\r\n\r\n const requiredCount = Math.ceil(totalAgents * threshold);\r\n const signalKey = `cfn-completion:${taskId}`;\r\n const completed: CompletionSignal[] = [];\r\n const startTime = Date.now();\r\n const timeoutMs = timeoutSeconds * 1000;\r\n\r\n // Connect to Redis\r\n const client: RedisClientType = createClient({\r\n socket: { host: redisHost, port: redisPort },\r\n password: redisPassword || undefined\r\n });\r\n\r\n try {\r\n await client.connect();\r\n console.log(`[wait-threshold] Connected to Redis at ${redisHost}:${redisPort}`);\r\n console.log(`[wait-threshold] Waiting for ${requiredCount}/${totalAgents} agents (${(threshold * 100).toFixed(0)}% threshold)`);\r\n console.log(`[wait-threshold] Signal key: ${signalKey}`);\r\n console.log(`[wait-threshold] Timeout: ${timeoutSeconds}s`);\r\n\r\n // Poll loop with short BLPOP timeouts\r\n const pollIntervalSeconds = 5; // Check every 5 seconds\r\n\r\n while (completed.length < requiredCount) {\r\n const elapsed = Date.now() - startTime;\r\n\r\n // Check overall timeout\r\n if (elapsed >= timeoutMs) {\r\n console.log(`[wait-threshold] Timeout reached after ${(elapsed / 1000).toFixed(1)}s`);\r\n break;\r\n }\r\n\r\n // Calculate remaining time for this poll\r\n const remainingMs = timeoutMs - elapsed;\r\n const pollTimeout = Math.min(pollIntervalSeconds, Math.ceil(remainingMs / 1000));\r\n\r\n try {\r\n // BLPOP with short timeout - returns null on timeout\r\n const result = await client.blPop(signalKey, pollTimeout);\r\n\r\n if (result) {\r\n try {\r\n const signal: CompletionSignal = JSON.parse(result.element);\r\n completed.push(signal);\r\n\r\n console.log(`[wait-threshold] Received signal ${completed.length}/${requiredCount}: ${signal.agentId} (${signal.status})`);\r\n\r\n // Check if threshold met\r\n if (completed.length >= requiredCount) {\r\n console.log(`[wait-threshold] Threshold met! ${completed.length}/${totalAgents} agents completed`);\r\n break;\r\n }\r\n } catch (parseErr) {\r\n console.warn(`[wait-threshold] Failed to parse signal: ${result.element}`);\r\n }\r\n } else {\r\n // Timeout on BLPOP - no signal received, continue polling\r\n const elapsedSec = ((Date.now() - startTime) / 1000).toFixed(1);\r\n console.log(`[wait-threshold] Polling... ${completed.length}/${requiredCount} completed (${elapsedSec}s elapsed)`);\r\n }\r\n } catch (blpopErr) {\r\n // Redis error during BLPOP\r\n console.error(`[wait-threshold] BLPOP error:`, blpopErr);\r\n break;\r\n }\r\n }\r\n\r\n const elapsedMs = Date.now() - startTime;\r\n const thresholdMet = completed.length >= requiredCount;\r\n\r\n return {\r\n success: thresholdMet,\r\n completed,\r\n timedOut: !thresholdMet && (Date.now() - startTime) >= timeoutMs,\r\n thresholdMet,\r\n completedCount: completed.length,\r\n requiredCount,\r\n totalAgents,\r\n elapsedMs\r\n };\r\n\r\n } finally {\r\n await client.disconnect();\r\n }\r\n}\r\n\r\n/**\r\n * Parse CLI arguments\r\n */\r\nfunction parseArgs(args: string[]): ThresholdConfig | null {\r\n const config: Partial<ThresholdConfig> = {\r\n threshold: 0.75,\r\n timeoutSeconds: 120\r\n };\r\n\r\n for (let i = 0; i < args.length; i++) {\r\n const arg = args[i];\r\n const value = args[i + 1];\r\n\r\n switch (arg) {\r\n case '--task-id':\r\n case '-t':\r\n config.taskId = value;\r\n i++;\r\n break;\r\n case '--total-agents':\r\n case '-n':\r\n config.totalAgents = parseInt(value, 10);\r\n i++;\r\n break;\r\n case '--threshold':\r\n config.threshold = parseFloat(value);\r\n i++;\r\n break;\r\n case '--timeout':\r\n config.timeoutSeconds = parseInt(value, 10);\r\n i++;\r\n break;\r\n case '--redis-host':\r\n config.redisHost = value;\r\n i++;\r\n break;\r\n case '--redis-port':\r\n config.redisPort = parseInt(value, 10);\r\n i++;\r\n break;\r\n case '--help':\r\n case '-h':\r\n printHelp();\r\n process.exit(0);\r\n }\r\n }\r\n\r\n // Validate required fields\r\n if (!config.taskId) {\r\n console.error('Error: --task-id is required');\r\n return null;\r\n }\r\n if (!config.totalAgents || config.totalAgents < 1) {\r\n console.error('Error: --total-agents must be a positive integer');\r\n return null;\r\n }\r\n if (config.threshold! < 0 || config.threshold! > 1) {\r\n console.error('Error: --threshold must be between 0.0 and 1.0');\r\n return null;\r\n }\r\n\r\n return config as ThresholdConfig;\r\n}\r\n\r\nfunction printHelp(): void {\r\n console.log(`\r\nWait for Threshold Completion - Parallel Agent Coordination\r\n\r\nUSAGE:\r\n npx tsx src/cli/coordination/wait-for-threshold.ts [OPTIONS]\r\n\r\nOPTIONS:\r\n -t, --task-id <id> Task ID for coordination (required)\r\n -n, --total-agents <n> Total number of agents spawned (required)\r\n --threshold <0.0-1.0> Completion threshold (default: 0.75 = 75%)\r\n --timeout <seconds> Overall timeout (default: 120)\r\n --redis-host <host> Redis host (default: localhost)\r\n --redis-port <port> Redis port (default: 6379)\r\n -h, --help Show this help message\r\n\r\nEXAMPLES:\r\n # Wait for 3/4 agents (75%) with 120s timeout\r\n npx tsx src/cli/coordination/wait-for-threshold.ts \\\\\r\n --task-id cfn-cli-12345 \\\\\r\n --total-agents 4 \\\\\r\n --threshold 0.75 \\\\\r\n --timeout 120\r\n\r\n # Wait for all agents (100%) with 300s timeout\r\n npx tsx src/cli/coordination/wait-for-threshold.ts \\\\\r\n --task-id cfn-cli-12345 \\\\\r\n --total-agents 4 \\\\\r\n --threshold 1.0 \\\\\r\n --timeout 300\r\n\r\nOUTPUT:\r\n JSON result with completion status:\r\n {\r\n \"success\": true,\r\n \"thresholdMet\": true,\r\n \"completedCount\": 3,\r\n \"requiredCount\": 3,\r\n \"totalAgents\": 4,\r\n \"elapsedMs\": 45000,\r\n \"completed\": [...]\r\n }\r\n`);\r\n}\r\n\r\n/**\r\n * CLI entry point\r\n */\r\nasync function main(): Promise<void> {\r\n const config = parseArgs(process.argv.slice(2));\r\n\r\n if (!config) {\r\n console.error('Use --help for usage information');\r\n process.exit(1);\r\n }\r\n\r\n try {\r\n const result = await waitForThreshold(config);\r\n\r\n // Output result as JSON for scripting\r\n console.log('\\n[wait-threshold] Result:');\r\n console.log(JSON.stringify(result, null, 2));\r\n\r\n // Exit with appropriate code\r\n process.exit(result.success ? 0 : 1);\r\n } catch (error) {\r\n console.error('[wait-threshold] Fatal error:', error);\r\n process.exit(2);\r\n }\r\n}\r\n\r\n// Run if called directly\r\nif (import.meta.url.endsWith(process.argv[1]?.replace(/\\\\/g, '/') || '')) {\r\n main();\r\n}\r\n\r\nexport { parseArgs };\r\n"],"names":["createClient","waitForThreshold","config","taskId","totalAgents","threshold","timeoutSeconds","redisHost","process","env","CFN_REDIS_HOST","redisPort","parseInt","CFN_REDIS_PORT","redisPassword","CFN_REDIS_PASSWORD","undefined","requiredCount","Math","ceil","signalKey","completed","startTime","Date","now","timeoutMs","client","socket","host","port","password","connect","console","log","toFixed","pollIntervalSeconds","length","elapsed","remainingMs","pollTimeout","min","result","blPop","signal","JSON","parse","element","push","agentId","status","parseErr","warn","elapsedSec","blpopErr","error","elapsedMs","thresholdMet","success","timedOut","completedCount","disconnect","parseArgs","args","i","arg","value","parseFloat","printHelp","exit","main","argv","slice","stringify","url","endsWith","replace"],"mappings":"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"}
@@ -11,7 +11,8 @@
11
11
  * Sprint 3 - Phase 2 Implementation
12
12
  */ import { execSync } from 'child_process';
13
13
  // Bug #6 Fix: Read Redis connection parameters from process.env
14
- const redisHost = process.env.CFN_REDIS_HOST || 'cfn-redis';
14
+ // FIX: Default to 'localhost' for CLI mode (host execution), not 'cfn-redis' (Docker)
15
+ const redisHost = process.env.CFN_REDIS_HOST || 'localhost';
15
16
  const redisPort = process.env.CFN_REDIS_PORT || '6379';
16
17
  /**
17
18
  * Load iteration history for an agent from Redis
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/iteration-history.ts"],"sourcesContent":["/**\r\n * Iteration History Management\r\n *\r\n * Loads and formats iteration history from Redis for CLI-spawned agents.\r\n * Enables agents to learn from previous attempts and feedback.\r\n *\r\n * Storage Pattern:\r\n * swarm:${TASK_ID}:${AGENT_ID}:result:iteration-${N} → Result text + confidence\r\n * swarm:${TASK_ID}:${AGENT_ID}:feedback:iteration-${N} → Validator feedback\r\n *\r\n * Sprint 3 - Phase 2 Implementation\r\n */\r\n\r\nimport { execSync } from 'child_process';\r\n\r\n// Bug #6 Fix: Read Redis connection parameters from process.env\r\nconst redisHost = process.env.CFN_REDIS_HOST || 'cfn-redis';\r\nconst redisPort = process.env.CFN_REDIS_PORT || '6379';\r\n\r\nexport interface IterationResult {\r\n iteration: number;\r\n result: string;\r\n confidence: number;\r\n timestamp: string;\r\n feedback?: string;\r\n}\r\n\r\n/**\r\n * Load iteration history for an agent from Redis\r\n *\r\n * @param taskId - Task identifier\r\n * @param agentId - Agent identifier\r\n * @param currentIteration - Current iteration number (loads 1 to N-1)\r\n * @returns Array of iteration results\r\n */\r\nexport async function loadIterationHistory(\r\n taskId: string,\r\n agentId: string,\r\n currentIteration: number\r\n): Promise<IterationResult[]> {\r\n const history: IterationResult[] = [];\r\n\r\n // Load previous iterations (1 to currentIteration - 1)\r\n for (let i = 1; i < currentIteration; i++) {\r\n try {\r\n // Load result data\r\n const resultKey = `swarm:${taskId}:${agentId}:result:iteration-${i}`;\r\n const resultJson = execSync(`redis-cli -h ${redisHost} -p ${redisPort} get \"${resultKey}\"`, { encoding: 'utf8' }).trim();\r\n\r\n if (resultJson === '(nil)' || !resultJson) {\r\n // No result for this iteration (shouldn't happen in normal flow)\r\n continue;\r\n }\r\n\r\n const resultData = JSON.parse(resultJson);\r\n\r\n // Load feedback data (may not exist for all iterations)\r\n const feedbackKey = `swarm:${taskId}:${agentId}:feedback:iteration-${i}`;\r\n let feedback: string | undefined;\r\n\r\n try {\r\n const feedbackJson = execSync(`redis-cli -h ${redisHost} -p ${redisPort} get \"${feedbackKey}\"`, { encoding: 'utf8' }).trim();\r\n if (feedbackJson !== '(nil)' && feedbackJson) {\r\n const feedbackData = JSON.parse(feedbackJson);\r\n feedback = feedbackData.feedback || feedbackData.comments;\r\n }\r\n } catch (err) {\r\n // Feedback may not exist for this iteration\r\n feedback = undefined;\r\n }\r\n\r\n history.push({\r\n iteration: i,\r\n result: resultData.result || resultData.output || '',\r\n confidence: resultData.confidence || 0,\r\n timestamp: resultData.timestamp || new Date().toISOString(),\r\n feedback\r\n });\r\n } catch (err) {\r\n console.error(`[iteration-history] Failed to load iteration ${i}:`, err);\r\n // Continue loading other iterations\r\n }\r\n }\r\n\r\n return history;\r\n}\r\n\r\n/**\r\n * Store iteration result in Redis\r\n *\r\n * @param taskId - Task identifier\r\n * @param agentId - Agent identifier\r\n * @param iteration - Iteration number\r\n * @param result - Result text/output\r\n * @param confidence - Confidence score (0.0-1.0)\r\n */\r\nexport async function storeIterationResult(\r\n taskId: string,\r\n agentId: string,\r\n iteration: number,\r\n result: string,\r\n confidence: number\r\n): Promise<void> {\r\n const resultKey = `swarm:${taskId}:${agentId}:result:iteration-${iteration}`;\r\n const resultData = {\r\n result,\r\n confidence,\r\n timestamp: new Date().toISOString(),\r\n iteration\r\n };\r\n\r\n const resultJson = JSON.stringify(resultData);\r\n\r\n try {\r\n // Store with 24 hour TTL\r\n execSync(`redis-cli -h ${redisHost} -p ${redisPort} setex \"${resultKey}\" 86400 '${resultJson.replace(/'/g, \"'\\\\''\")}'`, {\r\n encoding: 'utf8'\r\n });\r\n console.log(`[iteration-history] Stored result for iteration ${iteration}`);\r\n } catch (err) {\r\n console.error(`[iteration-history] Failed to store result:`, err);\r\n throw err;\r\n }\r\n}\r\n\r\n/**\r\n * Format iteration history as markdown for system prompt\r\n *\r\n * @param history - Array of iteration results\r\n * @param currentIteration - Current iteration number\r\n * @returns Formatted markdown string\r\n */\r\nexport function formatIterationHistory(\r\n history: IterationResult[],\r\n currentIteration: number\r\n): string {\r\n if (history.length === 0) {\r\n return `## Current Iteration: ${currentIteration}\r\n\r\nThis is your first attempt at this task. No previous iteration history available.\r\n`;\r\n }\r\n\r\n const sections: string[] = [];\r\n\r\n sections.push('## Iteration History');\r\n sections.push('');\r\n sections.push('Learn from your previous attempts and feedback:');\r\n sections.push('');\r\n\r\n // Format each iteration\r\n for (const iter of history) {\r\n sections.push(`### Iteration ${iter.iteration}`);\r\n sections.push('');\r\n\r\n sections.push('**Result:**');\r\n sections.push(iter.result.substring(0, 500)); // Truncate to 500 chars\r\n if (iter.result.length > 500) {\r\n sections.push('... (truncated)');\r\n }\r\n sections.push('');\r\n\r\n if (iter.feedback) {\r\n sections.push('**Feedback from Validators:**');\r\n sections.push(iter.feedback);\r\n sections.push('');\r\n }\r\n\r\n sections.push(`**Confidence:** ${iter.confidence.toFixed(2)}`);\r\n sections.push(`**Timestamp:** ${iter.timestamp}`);\r\n sections.push('');\r\n sections.push('---');\r\n sections.push('');\r\n }\r\n\r\n // Add current iteration context\r\n sections.push(`## Current Iteration: ${currentIteration}`);\r\n sections.push('');\r\n\r\n if (history.length > 0) {\r\n const lastIteration = history[history.length - 1];\r\n if (lastIteration.feedback) {\r\n sections.push('**Your Task:** Address the feedback from the previous iteration:');\r\n sections.push('');\r\n sections.push(lastIteration.feedback);\r\n sections.push('');\r\n } else {\r\n sections.push(`**Your Task:** Improve upon iteration ${lastIteration.iteration} (confidence: ${lastIteration.confidence.toFixed(2)})`);\r\n sections.push('');\r\n }\r\n }\r\n\r\n return sections.join('\\n');\r\n}\r\n\r\n/**\r\n * Check if iteration history exists for an agent\r\n *\r\n * @param taskId - Task identifier\r\n * @param agentId - Agent identifier\r\n * @returns True if any iteration history exists\r\n */\r\nexport async function hasIterationHistory(\r\n taskId: string,\r\n agentId: string\r\n): Promise<boolean> {\r\n try {\r\n const pattern = `swarm:${taskId}:${agentId}:result:iteration-*`;\r\n const keys = execSync(`redis-cli -h ${redisHost} -p ${redisPort} --scan --pattern \"${pattern}\"`, { encoding: 'utf8' }).trim();\r\n return keys.length > 0;\r\n } catch (err) {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Get the latest iteration number for an agent\r\n *\r\n * @param taskId - Task identifier\r\n * @param agentId - Agent identifier\r\n * @returns Latest iteration number (0 if no history)\r\n */\r\nexport async function getLatestIteration(\r\n taskId: string,\r\n agentId: string\r\n): Promise<number> {\r\n try {\r\n const pattern = `swarm:${taskId}:${agentId}:result:iteration-*`;\r\n const keys = execSync(`redis-cli -h ${redisHost} -p ${redisPort} --scan --pattern \"${pattern}\"`, { encoding: 'utf8' })\r\n .trim()\r\n .split('\\n')\r\n .filter((k) => k.length > 0);\r\n\r\n if (keys.length === 0) return 0;\r\n\r\n // Extract iteration numbers and find max\r\n const iterations = keys.map((key) => {\r\n const match = key.match(/iteration-(\\d+)$/);\r\n return match ? parseInt(match[1], 10) : 0;\r\n });\r\n\r\n return Math.max(...iterations);\r\n } catch (err) {\r\n return 0;\r\n }\r\n}\r\n"],"names":["execSync","redisHost","process","env","CFN_REDIS_HOST","redisPort","CFN_REDIS_PORT","loadIterationHistory","taskId","agentId","currentIteration","history","i","resultKey","resultJson","encoding","trim","resultData","JSON","parse","feedbackKey","feedback","feedbackJson","feedbackData","comments","err","undefined","push","iteration","result","output","confidence","timestamp","Date","toISOString","console","error","storeIterationResult","stringify","replace","log","formatIterationHistory","length","sections","iter","substring","toFixed","lastIteration","join","hasIterationHistory","pattern","keys","getLatestIteration","split","filter","k","iterations","map","key","match","parseInt","Math","max"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/cli/iteration-history.ts"],"sourcesContent":["/**\r\n * Iteration History Management\r\n *\r\n * Loads and formats iteration history from Redis for CLI-spawned agents.\r\n * Enables agents to learn from previous attempts and feedback.\r\n *\r\n * Storage Pattern:\r\n * swarm:${TASK_ID}:${AGENT_ID}:result:iteration-${N} → Result text + confidence\r\n * swarm:${TASK_ID}:${AGENT_ID}:feedback:iteration-${N} → Validator feedback\r\n *\r\n * Sprint 3 - Phase 2 Implementation\r\n */\r\n\r\nimport { execSync } from 'child_process';\r\n\r\n// Bug #6 Fix: Read Redis connection parameters from process.env\r\n// FIX: Default to 'localhost' for CLI mode (host execution), not 'cfn-redis' (Docker)\r\nconst redisHost = process.env.CFN_REDIS_HOST || 'localhost';\r\nconst redisPort = process.env.CFN_REDIS_PORT || '6379';\r\n\r\nexport interface IterationResult {\r\n iteration: number;\r\n result: string;\r\n confidence: number;\r\n timestamp: string;\r\n feedback?: string;\r\n}\r\n\r\n/**\r\n * Load iteration history for an agent from Redis\r\n *\r\n * @param taskId - Task identifier\r\n * @param agentId - Agent identifier\r\n * @param currentIteration - Current iteration number (loads 1 to N-1)\r\n * @returns Array of iteration results\r\n */\r\nexport async function loadIterationHistory(\r\n taskId: string,\r\n agentId: string,\r\n currentIteration: number\r\n): Promise<IterationResult[]> {\r\n const history: IterationResult[] = [];\r\n\r\n // Load previous iterations (1 to currentIteration - 1)\r\n for (let i = 1; i < currentIteration; i++) {\r\n try {\r\n // Load result data\r\n const resultKey = `swarm:${taskId}:${agentId}:result:iteration-${i}`;\r\n const resultJson = execSync(`redis-cli -h ${redisHost} -p ${redisPort} get \"${resultKey}\"`, { encoding: 'utf8' }).trim();\r\n\r\n if (resultJson === '(nil)' || !resultJson) {\r\n // No result for this iteration (shouldn't happen in normal flow)\r\n continue;\r\n }\r\n\r\n const resultData = JSON.parse(resultJson);\r\n\r\n // Load feedback data (may not exist for all iterations)\r\n const feedbackKey = `swarm:${taskId}:${agentId}:feedback:iteration-${i}`;\r\n let feedback: string | undefined;\r\n\r\n try {\r\n const feedbackJson = execSync(`redis-cli -h ${redisHost} -p ${redisPort} get \"${feedbackKey}\"`, { encoding: 'utf8' }).trim();\r\n if (feedbackJson !== '(nil)' && feedbackJson) {\r\n const feedbackData = JSON.parse(feedbackJson);\r\n feedback = feedbackData.feedback || feedbackData.comments;\r\n }\r\n } catch (err) {\r\n // Feedback may not exist for this iteration\r\n feedback = undefined;\r\n }\r\n\r\n history.push({\r\n iteration: i,\r\n result: resultData.result || resultData.output || '',\r\n confidence: resultData.confidence || 0,\r\n timestamp: resultData.timestamp || new Date().toISOString(),\r\n feedback\r\n });\r\n } catch (err) {\r\n console.error(`[iteration-history] Failed to load iteration ${i}:`, err);\r\n // Continue loading other iterations\r\n }\r\n }\r\n\r\n return history;\r\n}\r\n\r\n/**\r\n * Store iteration result in Redis\r\n *\r\n * @param taskId - Task identifier\r\n * @param agentId - Agent identifier\r\n * @param iteration - Iteration number\r\n * @param result - Result text/output\r\n * @param confidence - Confidence score (0.0-1.0)\r\n */\r\nexport async function storeIterationResult(\r\n taskId: string,\r\n agentId: string,\r\n iteration: number,\r\n result: string,\r\n confidence: number\r\n): Promise<void> {\r\n const resultKey = `swarm:${taskId}:${agentId}:result:iteration-${iteration}`;\r\n const resultData = {\r\n result,\r\n confidence,\r\n timestamp: new Date().toISOString(),\r\n iteration\r\n };\r\n\r\n const resultJson = JSON.stringify(resultData);\r\n\r\n try {\r\n // Store with 24 hour TTL\r\n execSync(`redis-cli -h ${redisHost} -p ${redisPort} setex \"${resultKey}\" 86400 '${resultJson.replace(/'/g, \"'\\\\''\")}'`, {\r\n encoding: 'utf8'\r\n });\r\n console.log(`[iteration-history] Stored result for iteration ${iteration}`);\r\n } catch (err) {\r\n console.error(`[iteration-history] Failed to store result:`, err);\r\n throw err;\r\n }\r\n}\r\n\r\n/**\r\n * Format iteration history as markdown for system prompt\r\n *\r\n * @param history - Array of iteration results\r\n * @param currentIteration - Current iteration number\r\n * @returns Formatted markdown string\r\n */\r\nexport function formatIterationHistory(\r\n history: IterationResult[],\r\n currentIteration: number\r\n): string {\r\n if (history.length === 0) {\r\n return `## Current Iteration: ${currentIteration}\r\n\r\nThis is your first attempt at this task. No previous iteration history available.\r\n`;\r\n }\r\n\r\n const sections: string[] = [];\r\n\r\n sections.push('## Iteration History');\r\n sections.push('');\r\n sections.push('Learn from your previous attempts and feedback:');\r\n sections.push('');\r\n\r\n // Format each iteration\r\n for (const iter of history) {\r\n sections.push(`### Iteration ${iter.iteration}`);\r\n sections.push('');\r\n\r\n sections.push('**Result:**');\r\n sections.push(iter.result.substring(0, 500)); // Truncate to 500 chars\r\n if (iter.result.length > 500) {\r\n sections.push('... (truncated)');\r\n }\r\n sections.push('');\r\n\r\n if (iter.feedback) {\r\n sections.push('**Feedback from Validators:**');\r\n sections.push(iter.feedback);\r\n sections.push('');\r\n }\r\n\r\n sections.push(`**Confidence:** ${iter.confidence.toFixed(2)}`);\r\n sections.push(`**Timestamp:** ${iter.timestamp}`);\r\n sections.push('');\r\n sections.push('---');\r\n sections.push('');\r\n }\r\n\r\n // Add current iteration context\r\n sections.push(`## Current Iteration: ${currentIteration}`);\r\n sections.push('');\r\n\r\n if (history.length > 0) {\r\n const lastIteration = history[history.length - 1];\r\n if (lastIteration.feedback) {\r\n sections.push('**Your Task:** Address the feedback from the previous iteration:');\r\n sections.push('');\r\n sections.push(lastIteration.feedback);\r\n sections.push('');\r\n } else {\r\n sections.push(`**Your Task:** Improve upon iteration ${lastIteration.iteration} (confidence: ${lastIteration.confidence.toFixed(2)})`);\r\n sections.push('');\r\n }\r\n }\r\n\r\n return sections.join('\\n');\r\n}\r\n\r\n/**\r\n * Check if iteration history exists for an agent\r\n *\r\n * @param taskId - Task identifier\r\n * @param agentId - Agent identifier\r\n * @returns True if any iteration history exists\r\n */\r\nexport async function hasIterationHistory(\r\n taskId: string,\r\n agentId: string\r\n): Promise<boolean> {\r\n try {\r\n const pattern = `swarm:${taskId}:${agentId}:result:iteration-*`;\r\n const keys = execSync(`redis-cli -h ${redisHost} -p ${redisPort} --scan --pattern \"${pattern}\"`, { encoding: 'utf8' }).trim();\r\n return keys.length > 0;\r\n } catch (err) {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Get the latest iteration number for an agent\r\n *\r\n * @param taskId - Task identifier\r\n * @param agentId - Agent identifier\r\n * @returns Latest iteration number (0 if no history)\r\n */\r\nexport async function getLatestIteration(\r\n taskId: string,\r\n agentId: string\r\n): Promise<number> {\r\n try {\r\n const pattern = `swarm:${taskId}:${agentId}:result:iteration-*`;\r\n const keys = execSync(`redis-cli -h ${redisHost} -p ${redisPort} --scan --pattern \"${pattern}\"`, { encoding: 'utf8' })\r\n .trim()\r\n .split('\\n')\r\n .filter((k) => k.length > 0);\r\n\r\n if (keys.length === 0) return 0;\r\n\r\n // Extract iteration numbers and find max\r\n const iterations = keys.map((key) => {\r\n const match = key.match(/iteration-(\\d+)$/);\r\n return match ? parseInt(match[1], 10) : 0;\r\n });\r\n\r\n return Math.max(...iterations);\r\n } catch (err) {\r\n return 0;\r\n }\r\n}\r\n"],"names":["execSync","redisHost","process","env","CFN_REDIS_HOST","redisPort","CFN_REDIS_PORT","loadIterationHistory","taskId","agentId","currentIteration","history","i","resultKey","resultJson","encoding","trim","resultData","JSON","parse","feedbackKey","feedback","feedbackJson","feedbackData","comments","err","undefined","push","iteration","result","output","confidence","timestamp","Date","toISOString","console","error","storeIterationResult","stringify","replace","log","formatIterationHistory","length","sections","iter","substring","toFixed","lastIteration","join","hasIterationHistory","pattern","keys","getLatestIteration","split","filter","k","iterations","map","key","match","parseInt","Math","max"],"mappings":"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"}
@@ -1,6 +1,10 @@
1
1
  import { EventEmitter } from "events";
2
2
  import { readFile } from "fs/promises";
3
- import { join } from "path";
3
+ import { join, dirname } from "path";
4
+ import { fileURLToPath } from "url";
5
+ // ESM-compatible __dirname
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = dirname(__filename);
4
8
  export var ProcessType = /*#__PURE__*/ function(ProcessType) {
5
9
  ProcessType["EVENT_BUS"] = "EVENT_BUS";
6
10
  ProcessType["ORCHESTRATOR"] = "ORCHESTRATOR";
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/process-lifecycle.ts"],"sourcesContent":["import { EventEmitter } from \"events\";\nimport { readFile } from \"fs/promises\";\nimport { join } from \"path\";\n\nexport enum ProcessType {\n EVENT_BUS = \"EVENT_BUS\",\n ORCHESTRATOR = \"ORCHESTRATOR\",\n MEMORY_MANAGER = \"MEMORY_MANAGER\",\n TERMINAL_POOL = \"TERMINAL_POOL\",\n COORDINATOR = \"COORDINATOR\",\n MCP_SERVER = \"MCP_SERVER\",\n}\n\nexport enum ProcessStatus {\n STOPPED = \"STOPPED\",\n STARTING = \"STARTING\",\n RUNNING = \"RUNNING\",\n STOPPING = \"STOPPING\",\n ERROR = \"ERROR\",\n}\n\nexport interface ProcessInfo {\n id: string;\n name: string;\n type: ProcessType;\n status: ProcessStatus;\n startTime?: number;\n pid?: number;\n metrics?: ProcessMetrics;\n}\n\nexport interface ProcessMetrics {\n lastError?: string;\n uptime?: number;\n memoryUsage?: number;\n cpuUsage?: number;\n}\n\nexport interface SystemStats {\n totalProcesses: number;\n runningProcesses: number;\n stoppedProcesses: number;\n errorProcesses: number;\n systemUptime: number;\n totalMemory: number;\n totalCpu: number;\n}\n\nexport interface ProcessLifecycleConfig {\n processTypes: {\n [key in ProcessType]: {\n dependencies: ProcessType[];\n restartPolicy: \"always\" | \"on-failure\" | \"never\";\n };\n };\n monitoringConfig: {\n pollingInterval: number;\n healthCheckTimeout: number;\n metricCollectionEnabled: boolean;\n };\n}\n\nexport class ProcessLifecycleManager extends EventEmitter {\n private processes: Map<string, ProcessInfo> = new Map();\n private config: ProcessLifecycleConfig;\n private configPath: string;\n\n constructor(\n configPath: string = join(\n __dirname,\n \"..\",\n \"..\",\n \".claude\",\n \"skills\",\n \"process-lifecycle\",\n \"config.json\",\n ),\n ) {\n super();\n this.configPath = configPath;\n }\n\n async initialize(): Promise<void> {\n try {\n const configData = await readFile(this.configPath, \"utf-8\");\n this.config = JSON.parse(configData);\n this.initializeProcesses();\n this.emit(\"initialized\", { config: this.config });\n } catch (error) {\n this.emit(\"error\", { component: \"ProcessLifecycleManager\", error });\n throw error;\n }\n }\n\n private initializeProcesses(): void {\n const processDefinitions: ProcessInfo[] = Object.entries(\n this.config.processTypes,\n ).map(([type, details]) => ({\n id: type,\n name: this.formatProcessName(type),\n type: type as ProcessType,\n status: ProcessStatus.STOPPED,\n }));\n\n for (const process of processDefinitions) {\n this.processes.set(process.id, process);\n }\n }\n\n private formatProcessName(type: string): string {\n return type\n .split(\"_\")\n .map((word) => word[0] + word.slice(1).toLowerCase())\n .join(\" \");\n }\n\n async startProcess(processId: string): Promise<void> {\n const process = this.processes.get(processId);\n if (!process) {\n throw new Error(`Unknown process: ${processId}`);\n }\n\n if (process.status === ProcessStatus.RUNNING) {\n throw new Error(`Process ${processId} is already running`);\n }\n\n // Validate dependencies\n const processConfig = this.config.processTypes[process.type as ProcessType];\n const missingDependencies = processConfig.dependencies.filter(\n (dep) => this.processes.get(dep)?.status !== ProcessStatus.RUNNING,\n );\n\n if (missingDependencies.length > 0) {\n throw new Error(\n `Missing dependencies for ${processId}: ${missingDependencies.join(\", \")}`,\n );\n }\n\n this.updateProcessStatus(processId, ProcessStatus.STARTING);\n\n try {\n // Simulated process start - in real implementation, this would start actual processes\n process.startTime = Date.now();\n process.pid = process.id.charCodeAt(0); // Simulated PID\n\n this.updateProcessStatus(processId, ProcessStatus.RUNNING);\n this.emit(\"processStarted\", { processId, process });\n } catch (error) {\n this.updateProcessStatus(processId, ProcessStatus.ERROR);\n process.metrics = {\n ...process.metrics,\n lastError: (error as Error).message,\n };\n this.emit(\"processError\", { processId, error });\n throw error;\n }\n }\n\n async stopProcess(processId: string): Promise<void> {\n const process = this.processes.get(processId);\n if (!process || process.status !== ProcessStatus.RUNNING) {\n throw new Error(`Process ${processId} is not running`);\n }\n\n // Check if any running processes depend on this one\n const dependentProcesses = Array.from(this.processes.values()).filter(\n (p) =>\n this.config.processTypes[p.type as ProcessType].dependencies.includes(\n process.type as ProcessType,\n ) && p.status === ProcessStatus.RUNNING,\n );\n\n if (dependentProcesses.length > 0) {\n throw new Error(\n `Cannot stop ${processId}, dependent processes are running: ${dependentProcesses.map((p) => p.id).join(\", \")}`,\n );\n }\n\n this.updateProcessStatus(processId, ProcessStatus.STOPPING);\n\n try {\n // Simulated process stop\n this.updateProcessStatus(processId, ProcessStatus.STOPPED);\n this.emit(\"processStopped\", { processId });\n } catch (error) {\n this.updateProcessStatus(processId, ProcessStatus.ERROR);\n this.emit(\"processError\", { processId, error });\n throw error;\n }\n }\n\n async restartProcess(processId: string): Promise<void> {\n await this.stopProcess(processId);\n await new Promise((resolve) => setTimeout(resolve, 1000)); // Brief delay\n await this.startProcess(processId);\n }\n\n async startAll(): Promise<void> {\n // Start in dependency order\n const startOrder = Object.values(ProcessType);\n\n for (const processId of startOrder) {\n try {\n await this.startProcess(processId);\n } catch (error) {\n console.error(\n `Failed to start ${processId}:`,\n (error as Error).message,\n );\n // Continue with other processes\n }\n }\n }\n\n async stopAll(): Promise<void> {\n // Stop in reverse dependency order\n const stopOrder = Object.values(ProcessType).reverse();\n\n for (const processId of stopOrder) {\n const process = this.processes.get(processId);\n if (process && process.status === ProcessStatus.RUNNING) {\n try {\n await this.stopProcess(processId);\n } catch (error) {\n console.error(\n `Failed to stop ${processId}:`,\n (error as Error).message,\n );\n }\n }\n }\n }\n\n getProcess(processId: string): ProcessInfo | undefined {\n return this.processes.get(processId);\n }\n\n getAllProcesses(): ProcessInfo[] {\n return Array.from(this.processes.values());\n }\n\n getSystemStats(): SystemStats {\n const processes = this.getAllProcesses();\n const runningProcesses = processes.filter(\n (p) => p.status === ProcessStatus.RUNNING,\n );\n const stoppedProcesses = processes.filter(\n (p) => p.status === ProcessStatus.STOPPED,\n );\n const errorProcesses = processes.filter(\n (p) => p.status === ProcessStatus.ERROR,\n );\n\n return {\n totalProcesses: processes.length,\n runningProcesses: runningProcesses.length,\n stoppedProcesses: stoppedProcesses.length,\n errorProcesses: errorProcesses.length,\n systemUptime: this.getSystemUptime(),\n totalMemory: this.getTotalMemoryUsage(),\n totalCpu: this.getTotalCpuUsage(),\n };\n }\n\n private updateProcessStatus(processId: string, status: ProcessStatus): void {\n const process = this.processes.get(processId);\n if (process) {\n process.status = status;\n this.emit(\"statusChanged\", { processId, status });\n }\n }\n\n private getSystemUptime(): number {\n const firstRunningProcess = Array.from(this.processes.values()).find(\n (p) => p.status === ProcessStatus.RUNNING && p.startTime,\n );\n\n return firstRunningProcess && firstRunningProcess.startTime\n ? Date.now() - firstRunningProcess.startTime\n : 0;\n }\n\n private getTotalMemoryUsage(): number {\n // Placeholder for actual memory monitoring\n return 0;\n }\n\n private getTotalCpuUsage(): number {\n // Placeholder for actual CPU monitoring\n return 0;\n }\n\n async getProcessLogs(\n processId: string,\n lines: number = 50,\n ): Promise<string[]> {\n // Placeholder for actual logging system\n return [\n `[${new Date().toISOString()}] Process ${processId} started`,\n `[${new Date().toISOString()}] Process ${processId} is running normally`,\n ];\n }\n}\n"],"names":["EventEmitter","readFile","join","ProcessType","ProcessStatus","ProcessLifecycleManager","processes","Map","config","configPath","__dirname","initialize","configData","JSON","parse","initializeProcesses","emit","error","component","processDefinitions","Object","entries","processTypes","map","type","details","id","name","formatProcessName","status","process","set","split","word","slice","toLowerCase","startProcess","processId","get","Error","processConfig","missingDependencies","dependencies","filter","dep","length","updateProcessStatus","startTime","Date","now","pid","charCodeAt","metrics","lastError","message","stopProcess","dependentProcesses","Array","from","values","p","includes","restartProcess","Promise","resolve","setTimeout","startAll","startOrder","console","stopAll","stopOrder","reverse","getProcess","getAllProcesses","getSystemStats","runningProcesses","stoppedProcesses","errorProcesses","totalProcesses","systemUptime","getSystemUptime","totalMemory","getTotalMemoryUsage","totalCpu","getTotalCpuUsage","firstRunningProcess","find","getProcessLogs","lines","toISOString"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/cli/process-lifecycle.ts"],"sourcesContent":["import { EventEmitter } from \"events\";\nimport { readFile } from \"fs/promises\";\nimport { join, dirname } from \"path\";\nimport { fileURLToPath } from \"url\";\n\n// ESM-compatible __dirname\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\nexport enum ProcessType {\n EVENT_BUS = \"EVENT_BUS\",\n ORCHESTRATOR = \"ORCHESTRATOR\",\n MEMORY_MANAGER = \"MEMORY_MANAGER\",\n TERMINAL_POOL = \"TERMINAL_POOL\",\n COORDINATOR = \"COORDINATOR\",\n MCP_SERVER = \"MCP_SERVER\",\n}\n\nexport enum ProcessStatus {\n STOPPED = \"STOPPED\",\n STARTING = \"STARTING\",\n RUNNING = \"RUNNING\",\n STOPPING = \"STOPPING\",\n ERROR = \"ERROR\",\n}\n\nexport interface ProcessInfo {\n id: string;\n name: string;\n type: ProcessType;\n status: ProcessStatus;\n startTime?: number;\n pid?: number;\n metrics?: ProcessMetrics;\n}\n\nexport interface ProcessMetrics {\n lastError?: string;\n uptime?: number;\n memoryUsage?: number;\n cpuUsage?: number;\n}\n\nexport interface SystemStats {\n totalProcesses: number;\n runningProcesses: number;\n stoppedProcesses: number;\n errorProcesses: number;\n systemUptime: number;\n totalMemory: number;\n totalCpu: number;\n}\n\nexport interface ProcessLifecycleConfig {\n processTypes: {\n [key in ProcessType]: {\n dependencies: ProcessType[];\n restartPolicy: \"always\" | \"on-failure\" | \"never\";\n };\n };\n monitoringConfig: {\n pollingInterval: number;\n healthCheckTimeout: number;\n metricCollectionEnabled: boolean;\n };\n}\n\nexport class ProcessLifecycleManager extends EventEmitter {\n private processes: Map<string, ProcessInfo> = new Map();\n private config: ProcessLifecycleConfig;\n private configPath: string;\n\n constructor(\n configPath: string = join(\n __dirname,\n \"..\",\n \"..\",\n \".claude\",\n \"skills\",\n \"process-lifecycle\",\n \"config.json\",\n ),\n ) {\n super();\n this.configPath = configPath;\n }\n\n async initialize(): Promise<void> {\n try {\n const configData = await readFile(this.configPath, \"utf-8\");\n this.config = JSON.parse(configData);\n this.initializeProcesses();\n this.emit(\"initialized\", { config: this.config });\n } catch (error) {\n this.emit(\"error\", { component: \"ProcessLifecycleManager\", error });\n throw error;\n }\n }\n\n private initializeProcesses(): void {\n const processDefinitions: ProcessInfo[] = Object.entries(\n this.config.processTypes,\n ).map(([type, details]) => ({\n id: type,\n name: this.formatProcessName(type),\n type: type as ProcessType,\n status: ProcessStatus.STOPPED,\n }));\n\n for (const process of processDefinitions) {\n this.processes.set(process.id, process);\n }\n }\n\n private formatProcessName(type: string): string {\n return type\n .split(\"_\")\n .map((word) => word[0] + word.slice(1).toLowerCase())\n .join(\" \");\n }\n\n async startProcess(processId: string): Promise<void> {\n const process = this.processes.get(processId);\n if (!process) {\n throw new Error(`Unknown process: ${processId}`);\n }\n\n if (process.status === ProcessStatus.RUNNING) {\n throw new Error(`Process ${processId} is already running`);\n }\n\n // Validate dependencies\n const processConfig = this.config.processTypes[process.type as ProcessType];\n const missingDependencies = processConfig.dependencies.filter(\n (dep) => this.processes.get(dep)?.status !== ProcessStatus.RUNNING,\n );\n\n if (missingDependencies.length > 0) {\n throw new Error(\n `Missing dependencies for ${processId}: ${missingDependencies.join(\", \")}`,\n );\n }\n\n this.updateProcessStatus(processId, ProcessStatus.STARTING);\n\n try {\n // Simulated process start - in real implementation, this would start actual processes\n process.startTime = Date.now();\n process.pid = process.id.charCodeAt(0); // Simulated PID\n\n this.updateProcessStatus(processId, ProcessStatus.RUNNING);\n this.emit(\"processStarted\", { processId, process });\n } catch (error) {\n this.updateProcessStatus(processId, ProcessStatus.ERROR);\n process.metrics = {\n ...process.metrics,\n lastError: (error as Error).message,\n };\n this.emit(\"processError\", { processId, error });\n throw error;\n }\n }\n\n async stopProcess(processId: string): Promise<void> {\n const process = this.processes.get(processId);\n if (!process || process.status !== ProcessStatus.RUNNING) {\n throw new Error(`Process ${processId} is not running`);\n }\n\n // Check if any running processes depend on this one\n const dependentProcesses = Array.from(this.processes.values()).filter(\n (p) =>\n this.config.processTypes[p.type as ProcessType].dependencies.includes(\n process.type as ProcessType,\n ) && p.status === ProcessStatus.RUNNING,\n );\n\n if (dependentProcesses.length > 0) {\n throw new Error(\n `Cannot stop ${processId}, dependent processes are running: ${dependentProcesses.map((p) => p.id).join(\", \")}`,\n );\n }\n\n this.updateProcessStatus(processId, ProcessStatus.STOPPING);\n\n try {\n // Simulated process stop\n this.updateProcessStatus(processId, ProcessStatus.STOPPED);\n this.emit(\"processStopped\", { processId });\n } catch (error) {\n this.updateProcessStatus(processId, ProcessStatus.ERROR);\n this.emit(\"processError\", { processId, error });\n throw error;\n }\n }\n\n async restartProcess(processId: string): Promise<void> {\n await this.stopProcess(processId);\n await new Promise((resolve) => setTimeout(resolve, 1000)); // Brief delay\n await this.startProcess(processId);\n }\n\n async startAll(): Promise<void> {\n // Start in dependency order\n const startOrder = Object.values(ProcessType);\n\n for (const processId of startOrder) {\n try {\n await this.startProcess(processId);\n } catch (error) {\n console.error(\n `Failed to start ${processId}:`,\n (error as Error).message,\n );\n // Continue with other processes\n }\n }\n }\n\n async stopAll(): Promise<void> {\n // Stop in reverse dependency order\n const stopOrder = Object.values(ProcessType).reverse();\n\n for (const processId of stopOrder) {\n const process = this.processes.get(processId);\n if (process && process.status === ProcessStatus.RUNNING) {\n try {\n await this.stopProcess(processId);\n } catch (error) {\n console.error(\n `Failed to stop ${processId}:`,\n (error as Error).message,\n );\n }\n }\n }\n }\n\n getProcess(processId: string): ProcessInfo | undefined {\n return this.processes.get(processId);\n }\n\n getAllProcesses(): ProcessInfo[] {\n return Array.from(this.processes.values());\n }\n\n getSystemStats(): SystemStats {\n const processes = this.getAllProcesses();\n const runningProcesses = processes.filter(\n (p) => p.status === ProcessStatus.RUNNING,\n );\n const stoppedProcesses = processes.filter(\n (p) => p.status === ProcessStatus.STOPPED,\n );\n const errorProcesses = processes.filter(\n (p) => p.status === ProcessStatus.ERROR,\n );\n\n return {\n totalProcesses: processes.length,\n runningProcesses: runningProcesses.length,\n stoppedProcesses: stoppedProcesses.length,\n errorProcesses: errorProcesses.length,\n systemUptime: this.getSystemUptime(),\n totalMemory: this.getTotalMemoryUsage(),\n totalCpu: this.getTotalCpuUsage(),\n };\n }\n\n private updateProcessStatus(processId: string, status: ProcessStatus): void {\n const process = this.processes.get(processId);\n if (process) {\n process.status = status;\n this.emit(\"statusChanged\", { processId, status });\n }\n }\n\n private getSystemUptime(): number {\n const firstRunningProcess = Array.from(this.processes.values()).find(\n (p) => p.status === ProcessStatus.RUNNING && p.startTime,\n );\n\n return firstRunningProcess && firstRunningProcess.startTime\n ? Date.now() - firstRunningProcess.startTime\n : 0;\n }\n\n private getTotalMemoryUsage(): number {\n // Placeholder for actual memory monitoring\n return 0;\n }\n\n private getTotalCpuUsage(): number {\n // Placeholder for actual CPU monitoring\n return 0;\n }\n\n async getProcessLogs(\n processId: string,\n lines: number = 50,\n ): Promise<string[]> {\n // Placeholder for actual logging system\n return [\n `[${new Date().toISOString()}] Process ${processId} started`,\n `[${new Date().toISOString()}] Process ${processId} is running normally`,\n ];\n }\n}\n"],"names":["EventEmitter","readFile","join","dirname","fileURLToPath","__filename","url","__dirname","ProcessType","ProcessStatus","ProcessLifecycleManager","processes","Map","config","configPath","initialize","configData","JSON","parse","initializeProcesses","emit","error","component","processDefinitions","Object","entries","processTypes","map","type","details","id","name","formatProcessName","status","process","set","split","word","slice","toLowerCase","startProcess","processId","get","Error","processConfig","missingDependencies","dependencies","filter","dep","length","updateProcessStatus","startTime","Date","now","pid","charCodeAt","metrics","lastError","message","stopProcess","dependentProcesses","Array","from","values","p","includes","restartProcess","Promise","resolve","setTimeout","startAll","startOrder","console","stopAll","stopOrder","reverse","getProcess","getAllProcesses","getSystemStats","runningProcesses","stoppedProcesses","errorProcesses","totalProcesses","systemUptime","getSystemUptime","totalMemory","getTotalMemoryUsage","totalCpu","getTotalCpuUsage","firstRunningProcess","find","getProcessLogs","lines","toISOString"],"mappings":"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"}
@@ -11,7 +11,11 @@
11
11
  * spawn-agent-cli --version
12
12
  */ import { AgentSpawner } from './agent-spawner';
13
13
  import { readFileSync } from 'fs';
14
- import { resolve } from 'path';
14
+ import { resolve, dirname } from 'path';
15
+ import { fileURLToPath } from 'url';
16
+ // ESM-compatible __dirname
17
+ const __filename = fileURLToPath(import.meta.url);
18
+ const __dirname = dirname(__filename);
15
19
  /**
16
20
  * Parse command-line arguments
17
21
  */ function parseArgs(args) {
@@ -40,6 +44,8 @@ import { resolve } from 'path';
40
44
  parsed.provider = args[++i];
41
45
  } else if (arg === '--model') {
42
46
  parsed.model = args[++i];
47
+ } else if (arg === '--prompt') {
48
+ parsed.prompt = args[++i];
43
49
  } else if (!arg.startsWith('-')) {
44
50
  if (!parsed.agentType) {
45
51
  parsed.agentType = arg;
@@ -66,6 +72,7 @@ OPTIONS:
66
72
  -m, --mode <mode> Mode: mvp, standard, enterprise (default: standard)
67
73
  -p, --provider <p> Provider: zai, anthropic, etc. (default: auto-detect)
68
74
  --model <model> Explicit model name (default: auto-detect)
75
+ --prompt <text> Task prompt/description to pass to agent
69
76
  --foreground Run in foreground (default: background)
70
77
  --background Run in background (default)
71
78
  --json Output result as JSON
@@ -101,6 +108,31 @@ ENVIRONMENT VARIABLES:
101
108
  console.log('spawn-agent-cli v1.0.0');
102
109
  }
103
110
  }
111
+ /**
112
+ * Phase 1: Mode Prefix Function for CLI/Trigger.dev Collision Mitigation
113
+ *
114
+ * Generates task ID with mode prefix to prevent Redis key collisions between
115
+ * CLI mode and Trigger.dev Docker mode. Both modes share identical Redis coordination
116
+ * patterns and must use isolated namespaces.
117
+ *
118
+ * @param rawTaskId - Original task ID without prefix
119
+ * @param mode - Execution mode: 'cli' for CLI mode, 'trigger' for Trigger.dev
120
+ * @returns Prefixed task ID in format "MODE:rawTaskId"
121
+ *
122
+ * Example:
123
+ * generateTaskId('task-123', 'cli') => 'cli:task-123'
124
+ * generateTaskId('task-123', 'trigger') => 'trigger:task-123'
125
+ *
126
+ * Redis Key Isolation (After):
127
+ * CLI: cfn:task:cli:task-123:status
128
+ * Trigger: cfn:task:trigger:task-123:status
129
+ */ function generateTaskId(rawTaskId, mode) {
130
+ // Don't add prefix if task ID already has a namespace prefix
131
+ if (/^[a-z]+:/.test(rawTaskId)) {
132
+ return rawTaskId;
133
+ }
134
+ return `${mode}:${rawTaskId}`;
135
+ }
104
136
  /**
105
137
  * Validate CLI arguments
106
138
  */ function validateArgs(args) {
@@ -112,7 +144,8 @@ ENVIRONMENT VARIABLES:
112
144
  if (!taskId) {
113
145
  errors.push('Task ID is required (--task-id or TASK_ID env var)');
114
146
  }
115
- if (args.taskId && !/^[a-zA-Z0-9_.-]{1,64}$/.test(args.taskId)) {
147
+ // Allow optional namespace prefix (e.g., "cli:", "task:") for coordination routing
148
+ if (taskId && !/^([a-z]+:)?[a-zA-Z0-9_.-]{1,64}$/.test(taskId)) {
116
149
  errors.push('Invalid task ID format');
117
150
  }
118
151
  if (args.iteration && (args.iteration < 1 || !Number.isInteger(args.iteration))) {
@@ -173,17 +206,19 @@ ENVIRONMENT VARIABLES:
173
206
  // Create spawner
174
207
  const projectRoot = process.env.PROJECT_ROOT || process.cwd();
175
208
  const spawner = new AgentSpawner(projectRoot);
176
- // Build config
209
+ // Build config with CLI mode prefix for Redis key isolation (Phase 1)
210
+ const prefixedTaskId = generateTaskId(taskId, 'cli');
177
211
  const config = {
178
212
  agentType: args.agentType,
179
- taskId: taskId,
213
+ taskId: prefixedTaskId,
180
214
  iteration: args.iteration || 1,
181
215
  mode: args.mode || 'standard',
182
216
  provider: args.provider,
183
217
  model: args.model,
218
+ prompt: args.prompt,
184
219
  background: args.background !== false,
185
220
  env: {
186
- TASK_ID: taskId
221
+ TASK_ID: prefixedTaskId
187
222
  }
188
223
  };
189
224
  try {
@@ -204,6 +239,6 @@ main().catch((error)=>{
204
239
  console.error('Unexpected error:', error);
205
240
  process.exit(2);
206
241
  });
207
- export { parseArgs, validateArgs, formatOutput };
242
+ export { parseArgs, validateArgs, formatOutput, generateTaskId };
208
243
 
209
244
  //# sourceMappingURL=spawn-agent-cli.js.map