kantban-cli 0.1.35 → 0.1.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/gate-proxy-server.ts","../../src/lib/gate-proxy.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * gate-proxy-server.ts — MCP stdio server for gate enforcement.\n *\n * Spawned by the pipeline orchestrator as a child process. Reads gate config\n * from env vars, instantiates GateProxy, and exposes three MCP tools over\n * JSON-RPC on stdin/stdout:\n *\n * - kantban_run_gates — Run column gates and report results\n * - kantban_move_ticket — Run gates then forward move to KantBan API\n * - kantban_complete_task — Run gates then forward complete to KantBan API\n *\n * Uses raw JSON-RPC (no @modelcontextprotocol/sdk dependency).\n */\n\nimport { readFileSync } from 'node:fs';\nimport { createInterface } from 'node:readline';\nimport { parseGateConfig } from './gate-config.js';\nimport { runGates, formatGateErrors } from './gate-runner.js';\nimport { GateProxy } from './gate-proxy.js';\nimport { fetchWithRetry } from '../client.js';\nimport type { GateProxyDeps } from './gate-proxy.js';\n\n// ── UUID validation ──────────────────────────────────────────────────────\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nfunction validateUuid(value: unknown, name: string): string {\n if (typeof value !== 'string' || !UUID_RE.test(value)) {\n throw new Error(`Invalid ${name}: expected UUID, got \"${String(value)}\"`);\n }\n return value;\n}\n\n// ── env ──────────────────────────────────────────────────────────────────\n\nconst GATE_CONFIG_PATH = process.env['GATE_CONFIG_PATH'];\nconst COLUMN_ID = process.env['COLUMN_ID'];\nconst COLUMN_NAME = process.env['COLUMN_NAME'];\nconst PROJECT_ID = process.env['PROJECT_ID'];\nconst API_TOKEN = process.env['KANTBAN_API_TOKEN'];\nconst API_URL = process.env['KANTBAN_API_URL'];\n\nfunction requireEnv(name: string, value: string | undefined): string {\n if (!value) {\n process.stderr.write(`gate-proxy-server: missing required env var ${name}\\n`);\n process.exit(1);\n }\n return value;\n}\n\nconst gateConfigPath = requireEnv('GATE_CONFIG_PATH', GATE_CONFIG_PATH);\nconst columnId = requireEnv('COLUMN_ID', COLUMN_ID);\nconst columnName = requireEnv('COLUMN_NAME', COLUMN_NAME);\nconst projectId = requireEnv('PROJECT_ID', PROJECT_ID);\nconst apiToken = requireEnv('KANTBAN_API_TOKEN', API_TOKEN);\nconst apiUrl = requireEnv('KANTBAN_API_URL', API_URL);\n\n// ── gate config ──────────────────────────────────────────────────────────\n\nconst yamlContent = readFileSync(gateConfigPath, 'utf-8');\nconst gateConfig = parseGateConfig(yamlContent);\n\n// GATE_CWD overrides the YAML settings.cwd — set by the orchestrator to the\n// agent's worktree path so gates run against the agent's checked-out code,\n// not the root repo.\nconst GATE_CWD = process.env['GATE_CWD'];\nif (GATE_CWD) {\n if (!gateConfig.settings) gateConfig.settings = {};\n gateConfig.settings.cwd = GATE_CWD;\n}\n\n// ── API helpers ──────────────────────────────────────────────────────────\n\nfunction apiHeaders(): Record<string, string> {\n return {\n 'Authorization': `Bearer ${apiToken}`,\n 'Content-Type': 'application/json',\n 'X-KantBan-Via': 'cli-gate-proxy',\n };\n}\n\nasync function apiPost(path: string, body: Record<string, unknown>): Promise<unknown> {\n const url = new URL(path, apiUrl);\n const res = await fetchWithRetry(url.toString(), {\n method: 'POST',\n headers: apiHeaders(),\n body: JSON.stringify(body),\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`API error ${res.status}: ${text}`);\n }\n const json = (await res.json()) as { success: boolean; data: unknown };\n if (!json.success) throw new Error('API responded with success: false');\n return json.data;\n}\n\nasync function apiPatch(path: string, body: Record<string, unknown>): Promise<unknown> {\n const url = new URL(path, apiUrl);\n const res = await fetchWithRetry(url.toString(), {\n method: 'PATCH',\n headers: apiHeaders(),\n body: JSON.stringify(body),\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`API error ${res.status}: ${text}`);\n }\n const json = (await res.json()) as { success: boolean; data: unknown };\n if (!json.success) throw new Error('API responded with success: false');\n return json.data;\n}\n\nasync function apiGet(path: string, params?: Record<string, string>): Promise<unknown> {\n const url = new URL(path, apiUrl);\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n url.searchParams.set(k, v);\n }\n }\n const res = await fetchWithRetry(url.toString(), {\n headers: apiHeaders(),\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`API error ${res.status}: ${text}`);\n }\n const json = (await res.json()) as { success: boolean; data: unknown };\n if (!json.success) throw new Error('API responded with success: false');\n return json.data;\n}\n\n// ── GateProxy deps ──────────────────────────────────────────────────────\n\nconst deps: GateProxyDeps = {\n runGates,\n\n async forwardMoveTicket(args: Record<string, unknown>): Promise<unknown> {\n const ticketProjectId = validateUuid(args['projectId'], 'projectId');\n const ticketId = validateUuid(args['ticketId'], 'ticketId');\n const body = { ...args };\n delete body['projectId'];\n delete body['ticketId'];\n return apiPatch(`/projects/${ticketProjectId}/tickets/${ticketId}/move`, body);\n },\n\n async forwardCompleteTask(args: Record<string, unknown>): Promise<unknown> {\n const projectId = validateUuid(args['projectId'], 'projectId');\n const ticketId = validateUuid(args['ticketId'], 'ticketId');\n const body = { ...args };\n delete body['projectId'];\n delete body['ticketId'];\n return apiPost(`/projects/${projectId}/tickets/${ticketId}/complete`, body);\n },\n\n async getTicketGateWaivers(ticketId: string): Promise<string[]> {\n try {\n // Field values require both projectId and ticketId in the path.\n // projectId is injected as an env var (PROJECT_ID) at server startup.\n validateUuid(ticketId, 'ticketId');\n const data = (await apiGet(`/projects/${projectId}/tickets/${ticketId}/field-values`)) as\n Array<{ field_name: string; value: unknown }>;\n const waiver = data.find((fv) => fv.field_name === 'gate_waiver');\n if (!waiver) return [];\n if (Array.isArray(waiver.value)) {\n return waiver.value.filter((v): v is string => typeof v === 'string' && v.length > 0);\n }\n return [];\n } catch (err) {\n process.stderr.write(`gate-proxy-server: waiver fetch failed (running all gates): ${err instanceof Error ? err.message : String(err)}\\n`);\n return [];\n }\n },\n};\n\nconst proxy = new GateProxy(gateConfig, deps);\n\n// ── JSON-RPC types ──────────────────────────────────────────────────────\n\ninterface JsonRpcRequest {\n jsonrpc: '2.0';\n id: number | string;\n method: string;\n params?: Record<string, unknown>;\n}\n\ninterface JsonRpcResponse {\n jsonrpc: '2.0';\n id: number | string | null;\n result?: unknown;\n error?: { code: number; message: string; data?: unknown };\n}\n\n// ── Tool definitions ────────────────────────────────────────────────────\n\nconst TOOLS = [\n {\n name: 'kantban_run_gates',\n description:\n 'Run all configured gates for the current column and report results. ' +\n 'Use this to check gate status before attempting to move a ticket. ' +\n 'Pass ticketId to filter out waived gates for that ticket.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n ticketId: { type: 'string', description: 'Ticket ID (UUID) — filters out waived gates for this ticket' },\n },\n required: [] as string[],\n },\n },\n {\n name: 'kantban_move_ticket',\n description:\n 'Move a ticket to a different column. Gates are automatically enforced — ' +\n 'if any required gate fails, the move is blocked and failure details are returned.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n projectId: { type: 'string', description: 'Project ID (UUID)' },\n ticketId: { type: 'string', description: 'Ticket ID (UUID)' },\n columnId: { type: 'string', description: 'Target column ID (UUID)' },\n currentColumnName: { type: 'string', description: 'Current column name' },\n handoff: {\n type: 'object',\n description: 'Structured handoff data for the next pipeline stage',\n additionalProperties: true,\n },\n },\n required: ['projectId', 'ticketId', 'columnId'],\n },\n },\n {\n name: 'kantban_complete_task',\n description:\n 'Mark a ticket as complete. Gates are automatically enforced — ' +\n 'if any required gate fails, the completion is blocked and failure details are returned.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n projectId: { type: 'string', description: 'Project ID (UUID)' },\n ticketId: { type: 'string', description: 'Ticket ID (UUID)' },\n currentColumnName: { type: 'string', description: 'Current column name' },\n moveToColumn: { type: 'string', description: 'Column name to move the ticket to (e.g. \"Done\")' },\n completionComment: { type: 'string', description: 'Comment to add upon completion' },\n handoff: {\n type: 'object',\n description: 'Structured handoff data for the next pipeline stage',\n additionalProperties: true,\n },\n },\n required: ['projectId', 'ticketId'],\n },\n },\n];\n\n// ── Tool handlers ───────────────────────────────────────────────────────\n\nasync function handleToolCall(\n name: string,\n args: Record<string, unknown>,\n): Promise<{ content: Array<{ type: 'text'; text: string }>; isError?: boolean }> {\n try {\n switch (name) {\n case 'kantban_run_gates': {\n const ticketIdArg = args['ticketId'] as string | undefined;\n if (ticketIdArg) validateUuid(ticketIdArg, 'ticketId');\n const result = await proxy.handleRunGates(columnName, ticketIdArg);\n return {\n content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],\n };\n }\n\n case 'kantban_move_ticket': {\n const moveProjectId = validateUuid(args['projectId'], 'projectId');\n const moveTicketId = validateUuid(args['ticketId'], 'ticketId');\n const moveColumnId = validateUuid(args['columnId'], 'columnId');\n const result = await proxy.handleMoveTicket({\n projectId: moveProjectId,\n ticketId: moveTicketId,\n columnId: moveColumnId,\n currentColumnName: (args['currentColumnName'] as string | undefined) ?? columnName,\n args: {\n ...(args['handoff'] !== undefined ? { handoff: args['handoff'] } : {}),\n ...(args['column_id'] !== undefined ? { column_id: args['column_id'] } : {}),\n },\n });\n const isError = result.error === 'GATE_FAILURE';\n if (isError && result.results) {\n process.stderr.write(`gate-proxy-server: move blocked\\n${formatGateErrors(result.results)}`);\n }\n return {\n content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],\n ...(isError ? { isError: true } : {}),\n };\n }\n\n case 'kantban_complete_task': {\n const completeProjectId = validateUuid(args['projectId'], 'projectId');\n const completeTicketId = validateUuid(args['ticketId'], 'ticketId');\n const result = await proxy.handleCompleteTask({\n projectId: completeProjectId,\n ticketId: completeTicketId,\n currentColumnName: (args['currentColumnName'] as string | undefined) ?? columnName,\n args: {\n ...(args['moveToColumn'] !== undefined ? { moveToColumn: args['moveToColumn'] } : {}),\n ...(args['completionComment'] !== undefined ? { completionComment: args['completionComment'] } : {}),\n ...(args['handoff'] !== undefined ? { handoff: args['handoff'] } : {}),\n },\n });\n const isError = result.error === 'GATE_FAILURE';\n if (isError && result.results) {\n process.stderr.write(`gate-proxy-server: complete blocked\\n${formatGateErrors(result.results)}`);\n }\n return {\n content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],\n ...(isError ? { isError: true } : {}),\n };\n }\n\n default:\n return {\n content: [{ type: 'text', text: `Unknown tool: ${name}` }],\n isError: true,\n };\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n content: [{ type: 'text', text: `Error: ${message}` }],\n isError: true,\n };\n }\n}\n\n// ── JSON-RPC dispatch ───────────────────────────────────────────────────\n\nfunction sendResponse(response: JsonRpcResponse): void {\n const json = JSON.stringify(response);\n process.stdout.write(json + '\\n');\n}\n\nasync function handleMessage(msg: JsonRpcRequest): Promise<void> {\n const { id, method, params } = msg;\n\n switch (method) {\n case 'initialize': {\n sendResponse({\n jsonrpc: '2.0',\n id,\n result: {\n protocolVersion: '2024-11-05',\n capabilities: { tools: {} },\n serverInfo: {\n name: 'kantban-gates',\n version: '0.1.0',\n },\n },\n });\n return;\n }\n\n case 'notifications/initialized': {\n // Client acknowledgement — no response needed for notifications\n return;\n }\n\n case 'tools/list': {\n sendResponse({\n jsonrpc: '2.0',\n id,\n result: { tools: TOOLS },\n });\n return;\n }\n\n case 'tools/call': {\n const toolName = params?.['name'] as string | undefined;\n const toolArgs = (params?.['arguments'] as Record<string, unknown> | undefined) ?? {};\n\n if (!toolName) {\n sendResponse({\n jsonrpc: '2.0',\n id,\n error: { code: -32602, message: 'Missing tool name' },\n });\n return;\n }\n\n const result = await handleToolCall(toolName, toolArgs);\n sendResponse({\n jsonrpc: '2.0',\n id,\n result,\n });\n return;\n }\n\n case 'ping': {\n sendResponse({ jsonrpc: '2.0', id, result: {} });\n return;\n }\n\n default: {\n // Unknown method — return method not found per JSON-RPC spec\n if (method.startsWith('notifications/')) {\n // Notifications don't get responses\n return;\n }\n sendResponse({\n jsonrpc: '2.0',\n id,\n error: { code: -32601, message: `Method not found: ${method}` },\n });\n }\n }\n}\n\n// ── stdin reader ────────────────────────────────────────────────────────\n\nconst rl = createInterface({ input: process.stdin });\n\n// Serialize message processing to prevent concurrent double-moves\nlet messageQueue: Promise<void> = Promise.resolve();\n\nrl.on('line', (line: string) => {\n const trimmed = line.trim();\n if (!trimmed) return;\n\n let msg: JsonRpcRequest;\n try {\n msg = JSON.parse(trimmed) as JsonRpcRequest;\n } catch {\n sendResponse({\n jsonrpc: '2.0',\n id: null as unknown as number,\n error: { code: -32700, message: 'Parse error' },\n });\n return;\n }\n\n messageQueue = messageQueue.then(() => handleMessage(msg)).catch((err) => {\n process.stderr.write(`gate-proxy-server: unhandled error in message handler: ${err instanceof Error ? err.message : String(err)}\\n`);\n });\n});\n\nrl.on('close', () => {\n process.exit(0);\n});\n\n// Suppress unhandled rejection crashes — log to stderr and continue\nprocess.on('unhandledRejection', (err) => {\n process.stderr.write(\n `gate-proxy-server: unhandled rejection: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n});\n\nprocess.stderr.write(`gate-proxy-server: started (column=\"${columnName}\", gates=${gateConfig.default.length})\\n`);\n","import { resolveGatesForColumn, parseTimeout } from './gate-config.js';\nimport { formatGateErrors } from './gate-runner.js';\nimport type { GateConfig, GateDefinition, GateResult } from '@kantban/types';\n\nexport interface GateProxyDeps {\n runGates: (gates: GateDefinition[], options?: { cwd?: string; env?: Record<string, string>; totalTimeoutMs?: number }) => Promise<GateResult[]>;\n forwardMoveTicket: (args: Record<string, unknown>) => Promise<unknown>;\n forwardCompleteTask: (args: Record<string, unknown>) => Promise<unknown>;\n getTicketGateWaivers: (ticketId: string) => Promise<string[]>;\n}\n\nexport interface RunGatesResult {\n passed: boolean;\n results: GateResult[];\n}\n\nexport interface MoveArgs {\n projectId: string;\n ticketId: string;\n columnId: string;\n currentColumnName: string;\n args: Record<string, unknown>;\n}\n\nexport interface CompleteArgs {\n projectId: string;\n ticketId: string;\n currentColumnName: string;\n args: Record<string, unknown>;\n}\n\nexport interface InterceptResult {\n error?: 'GATE_FAILURE';\n message?: string;\n formatted?: string;\n results?: GateResult[];\n hint?: string;\n forwardResult?: unknown;\n}\n\nexport class GateProxy {\n private config: GateConfig;\n private deps: GateProxyDeps;\n\n constructor(config: GateConfig, deps: GateProxyDeps) {\n this.config = config;\n this.deps = deps;\n }\n\n private buildRunOptions(): { cwd?: string; env?: Record<string, string>; totalTimeoutMs?: number } {\n const opts: { cwd?: string; env?: Record<string, string>; totalTimeoutMs?: number } = {};\n if (this.config.settings?.cwd !== undefined) opts.cwd = this.config.settings.cwd;\n if (this.config.settings?.env !== undefined) opts.env = this.config.settings.env;\n if (this.config.settings?.total_timeout !== undefined) {\n opts.totalTimeoutMs = parseTimeout(this.config.settings.total_timeout);\n }\n return opts;\n }\n\n async handleRunGates(columnName: string, ticketId?: string): Promise<RunGatesResult> {\n const allGates = resolveGatesForColumn(this.config, columnName);\n\n let gates = allGates;\n if (ticketId) {\n const waivers = await this.deps.getTicketGateWaivers(ticketId);\n const waiverSet = new Set(waivers);\n gates = allGates.filter((g) => !waiverSet.has(g.name));\n }\n\n const results = await this.deps.runGates(gates, this.buildRunOptions());\n return {\n passed: results.filter((r) => r.required).every((r) => r.passed),\n results,\n };\n }\n\n async handleMoveTicket(move: MoveArgs): Promise<InterceptResult> {\n const allGates = resolveGatesForColumn(this.config, move.currentColumnName);\n\n // Filter out waived gates\n const waivers = await this.deps.getTicketGateWaivers(move.ticketId);\n const waiverSet = new Set(waivers);\n const gates = allGates.filter((g) => !waiverSet.has(g.name));\n\n let results: GateResult[];\n try {\n results = await this.deps.runGates(gates, this.buildRunOptions());\n } catch (err) {\n return {\n error: 'GATE_FAILURE',\n message: `Gate evaluation error: ${err instanceof Error ? err.message : String(err)}`,\n hint: 'Fix the gate environment and retry',\n };\n }\n\n const requiredFailures = results.filter((r) => r.required && !r.passed);\n if (requiredFailures.length > 0) {\n return {\n error: 'GATE_FAILURE',\n message: `Cannot move ticket — ${requiredFailures.length} required gate(s) failed`,\n formatted: formatGateErrors(results.filter((r) => r.required)),\n results,\n hint: 'Fix the failing gate(s) and try move_ticket again',\n };\n }\n\n const forwardResult = await this.deps.forwardMoveTicket({\n ...move.args,\n projectId: move.projectId,\n ticketId: move.ticketId,\n column_id: move.columnId,\n });\n\n return { forwardResult };\n }\n\n async handleCompleteTask(complete: CompleteArgs): Promise<InterceptResult> {\n const allGates = resolveGatesForColumn(this.config, complete.currentColumnName);\n const waivers = await this.deps.getTicketGateWaivers(complete.ticketId);\n const waiverSet = new Set(waivers);\n const gates = allGates.filter((g) => !waiverSet.has(g.name));\n\n let results: GateResult[];\n try {\n results = await this.deps.runGates(gates, this.buildRunOptions());\n } catch (err) {\n return {\n error: 'GATE_FAILURE',\n message: `Gate evaluation error: ${err instanceof Error ? err.message : String(err)}`,\n hint: 'Fix the gate environment and retry',\n };\n }\n\n const requiredFailures = results.filter((r) => r.required && !r.passed);\n if (requiredFailures.length > 0) {\n return {\n error: 'GATE_FAILURE',\n message: `Cannot complete task — ${requiredFailures.length} required gate(s) failed`,\n formatted: formatGateErrors(results.filter((r) => r.required)),\n results,\n hint: 'Fix the failing gate(s) and try complete_task again',\n };\n }\n\n const forwardResult = await this.deps.forwardCompleteTask({\n ...complete.args,\n projectId: complete.projectId,\n ticketId: complete.ticketId,\n });\n\n return { forwardResult };\n }\n}\n"],"mappings":"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","names":["deps","columnName","projectId"]}
1
+ {"version":3,"sources":["../../src/lib/gate-proxy-server.ts","../../src/lib/gate-proxy.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * gate-proxy-server.ts — MCP stdio server for gate enforcement.\n *\n * Spawned by the pipeline orchestrator as a child process. Reads gate config\n * from env vars, instantiates GateProxy, and exposes three MCP tools over\n * JSON-RPC on stdin/stdout:\n *\n * - kantban_run_gates — Run column gates and report results\n * - kantban_move_ticket — Run gates then forward move to KantBan API\n * - kantban_complete_task — Run gates then forward complete to KantBan API\n *\n * Uses raw JSON-RPC (no @modelcontextprotocol/sdk dependency).\n */\n\nimport { readFileSync } from 'node:fs';\nimport { createInterface } from 'node:readline';\nimport { parseGateConfig } from './gate-config.js';\nimport { runGates, formatGateErrors } from './gate-runner.js';\nimport { GateProxy } from './gate-proxy.js';\nimport { fetchWithRetry } from '../client.js';\nimport type { GateProxyDeps } from './gate-proxy.js';\n\n// ── UUID validation ──────────────────────────────────────────────────────\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nfunction validateUuid(value: unknown, name: string): string {\n if (typeof value !== 'string' || !UUID_RE.test(value)) {\n throw new Error(`Invalid ${name}: expected UUID, got \"${String(value)}\"`);\n }\n return value;\n}\n\n// ── env ──────────────────────────────────────────────────────────────────\n\nconst GATE_CONFIG_PATH = process.env['GATE_CONFIG_PATH'];\nconst COLUMN_ID = process.env['COLUMN_ID'];\nconst COLUMN_NAME = process.env['COLUMN_NAME'];\nconst PROJECT_ID = process.env['PROJECT_ID'];\nconst API_TOKEN = process.env['KANTBAN_API_TOKEN'];\nconst API_URL = process.env['KANTBAN_API_URL'];\n\nfunction requireEnv(name: string, value: string | undefined): string {\n if (!value) {\n process.stderr.write(`gate-proxy-server: missing required env var ${name}\\n`);\n process.exit(1);\n }\n return value;\n}\n\nconst gateConfigPath = requireEnv('GATE_CONFIG_PATH', GATE_CONFIG_PATH);\nconst columnId = requireEnv('COLUMN_ID', COLUMN_ID);\nconst columnName = requireEnv('COLUMN_NAME', COLUMN_NAME);\nconst projectId = requireEnv('PROJECT_ID', PROJECT_ID);\nconst apiToken = requireEnv('KANTBAN_API_TOKEN', API_TOKEN);\nconst apiUrl = requireEnv('KANTBAN_API_URL', API_URL);\n\n// ── gate config ──────────────────────────────────────────────────────────\n\nconst yamlContent = readFileSync(gateConfigPath, 'utf-8');\nconst gateConfig = parseGateConfig(yamlContent);\n\n// GATE_CWD overrides the YAML settings.cwd — set by the orchestrator to the\n// agent's worktree path so gates run against the agent's checked-out code,\n// not the root repo.\nconst GATE_CWD = process.env['GATE_CWD'];\nif (GATE_CWD) {\n if (!gateConfig.settings) gateConfig.settings = {};\n gateConfig.settings.cwd = GATE_CWD;\n}\n\n// ── API helpers ──────────────────────────────────────────────────────────\n\nfunction apiHeaders(): Record<string, string> {\n return {\n 'Authorization': `Bearer ${apiToken}`,\n 'Content-Type': 'application/json',\n 'X-KantBan-Via': 'cli-gate-proxy',\n };\n}\n\nasync function apiPost(path: string, body: Record<string, unknown>): Promise<unknown> {\n const url = new URL(path, apiUrl);\n const res = await fetchWithRetry(url.toString(), {\n method: 'POST',\n headers: apiHeaders(),\n body: JSON.stringify(body),\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`API error ${res.status}: ${text}`);\n }\n const json = (await res.json()) as { success: boolean; data: unknown };\n if (!json.success) throw new Error('API responded with success: false');\n return json.data;\n}\n\nasync function apiPatch(path: string, body: Record<string, unknown>): Promise<unknown> {\n const url = new URL(path, apiUrl);\n const res = await fetchWithRetry(url.toString(), {\n method: 'PATCH',\n headers: apiHeaders(),\n body: JSON.stringify(body),\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`API error ${res.status}: ${text}`);\n }\n const json = (await res.json()) as { success: boolean; data: unknown };\n if (!json.success) throw new Error('API responded with success: false');\n return json.data;\n}\n\nasync function apiGet(path: string, params?: Record<string, string>): Promise<unknown> {\n const url = new URL(path, apiUrl);\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n url.searchParams.set(k, v);\n }\n }\n const res = await fetchWithRetry(url.toString(), {\n headers: apiHeaders(),\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`API error ${res.status}: ${text}`);\n }\n const json = (await res.json()) as { success: boolean; data: unknown };\n if (!json.success) throw new Error('API responded with success: false');\n return json.data;\n}\n\n// ── GateProxy deps ──────────────────────────────────────────────────────\n\nconst deps: GateProxyDeps = {\n runGates,\n\n async forwardMoveTicket(args: Record<string, unknown>): Promise<unknown> {\n const ticketProjectId = validateUuid(args['projectId'], 'projectId');\n const ticketId = validateUuid(args['ticketId'], 'ticketId');\n const body = { ...args };\n delete body['projectId'];\n delete body['ticketId'];\n return apiPatch(`/projects/${ticketProjectId}/tickets/${ticketId}/move`, body);\n },\n\n async forwardCompleteTask(args: Record<string, unknown>): Promise<unknown> {\n const projectId = validateUuid(args['projectId'], 'projectId');\n const ticketId = validateUuid(args['ticketId'], 'ticketId');\n const body = { ...args };\n delete body['projectId'];\n delete body['ticketId'];\n return apiPost(`/projects/${projectId}/tickets/${ticketId}/complete`, body);\n },\n\n async getTicketGateWaivers(ticketId: string): Promise<string[]> {\n try {\n // Field values require both projectId and ticketId in the path.\n // projectId is injected as an env var (PROJECT_ID) at server startup.\n validateUuid(ticketId, 'ticketId');\n const data = (await apiGet(`/projects/${projectId}/tickets/${ticketId}/field-values`)) as\n Array<{ field_name: string; value: unknown }>;\n const waiver = data.find((fv) => fv.field_name === 'gate_waiver');\n if (!waiver) return [];\n if (Array.isArray(waiver.value)) {\n return waiver.value.filter((v): v is string => typeof v === 'string' && v.length > 0);\n }\n return [];\n } catch (err) {\n process.stderr.write(`gate-proxy-server: waiver fetch failed (running all gates): ${err instanceof Error ? err.message : String(err)}\\n`);\n return [];\n }\n },\n};\n\nconst proxy = new GateProxy(gateConfig, deps);\n\n// ── JSON-RPC types ──────────────────────────────────────────────────────\n\ninterface JsonRpcRequest {\n jsonrpc: '2.0';\n id: number | string;\n method: string;\n params?: Record<string, unknown>;\n}\n\ninterface JsonRpcResponse {\n jsonrpc: '2.0';\n id: number | string | null;\n result?: unknown;\n error?: { code: number; message: string; data?: unknown };\n}\n\n// ── Tool definitions ────────────────────────────────────────────────────\n\nconst TOOLS = [\n {\n name: 'kantban_run_gates',\n description:\n 'Run all configured gates for the current column and report results. ' +\n 'Use this to check gate status before attempting to move a ticket. ' +\n 'Pass ticketId to filter out waived gates for that ticket.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n ticketId: { type: 'string', description: 'Ticket ID (UUID) — filters out waived gates for this ticket' },\n },\n required: [] as string[],\n },\n },\n {\n name: 'kantban_move_ticket',\n description:\n 'Move a ticket to a different column. Gates are automatically enforced — ' +\n 'if any required gate fails, the move is blocked and failure details are returned.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n projectId: { type: 'string', description: 'Project ID (UUID)' },\n ticketId: { type: 'string', description: 'Ticket ID (UUID)' },\n columnId: { type: 'string', description: 'Target column ID (UUID)' },\n currentColumnName: { type: 'string', description: 'Current column name' },\n handoff: {\n type: 'object',\n description: 'Structured handoff data for the next pipeline stage',\n additionalProperties: true,\n },\n },\n required: ['projectId', 'ticketId', 'columnId'],\n },\n },\n {\n name: 'kantban_complete_task',\n description:\n 'Mark a ticket as complete. Gates are automatically enforced — ' +\n 'if any required gate fails, the completion is blocked and failure details are returned.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n projectId: { type: 'string', description: 'Project ID (UUID)' },\n ticketId: { type: 'string', description: 'Ticket ID (UUID)' },\n currentColumnName: { type: 'string', description: 'Current column name' },\n moveToColumn: { type: 'string', description: 'Column name to move the ticket to (e.g. \"Done\")' },\n completionComment: { type: 'string', description: 'Comment to add upon completion' },\n handoff: {\n type: 'object',\n description: 'Structured handoff data for the next pipeline stage',\n additionalProperties: true,\n },\n },\n required: ['projectId', 'ticketId'],\n },\n },\n];\n\n// ── Tool handlers ───────────────────────────────────────────────────────\n\nasync function handleToolCall(\n name: string,\n args: Record<string, unknown>,\n): Promise<{ content: Array<{ type: 'text'; text: string }>; isError?: boolean }> {\n try {\n switch (name) {\n case 'kantban_run_gates': {\n const ticketIdArg = args['ticketId'] as string | undefined;\n if (ticketIdArg) validateUuid(ticketIdArg, 'ticketId');\n const result = await proxy.handleRunGates(columnName, ticketIdArg);\n return {\n content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],\n };\n }\n\n case 'kantban_move_ticket': {\n const moveProjectId = validateUuid(args['projectId'], 'projectId');\n const moveTicketId = validateUuid(args['ticketId'], 'ticketId');\n const moveColumnId = validateUuid(args['columnId'], 'columnId');\n const result = await proxy.handleMoveTicket({\n projectId: moveProjectId,\n ticketId: moveTicketId,\n columnId: moveColumnId,\n currentColumnName: (args['currentColumnName'] as string | undefined) ?? columnName,\n args: {\n ...(args['handoff'] !== undefined ? { handoff: args['handoff'] } : {}),\n ...(args['column_id'] !== undefined ? { column_id: args['column_id'] } : {}),\n },\n });\n const isError = result.error === 'GATE_FAILURE';\n if (isError && result.results) {\n process.stderr.write(`gate-proxy-server: move blocked\\n${formatGateErrors(result.results)}`);\n }\n return {\n content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],\n ...(isError ? { isError: true } : {}),\n };\n }\n\n case 'kantban_complete_task': {\n const completeProjectId = validateUuid(args['projectId'], 'projectId');\n const completeTicketId = validateUuid(args['ticketId'], 'ticketId');\n const result = await proxy.handleCompleteTask({\n projectId: completeProjectId,\n ticketId: completeTicketId,\n currentColumnName: (args['currentColumnName'] as string | undefined) ?? columnName,\n args: {\n ...(args['moveToColumn'] !== undefined ? { moveToColumn: args['moveToColumn'] } : {}),\n ...(args['completionComment'] !== undefined ? { completionComment: args['completionComment'] } : {}),\n ...(args['handoff'] !== undefined ? { handoff: args['handoff'] } : {}),\n },\n });\n const isError = result.error === 'GATE_FAILURE';\n if (isError && result.results) {\n process.stderr.write(`gate-proxy-server: complete blocked\\n${formatGateErrors(result.results)}`);\n }\n return {\n content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],\n ...(isError ? { isError: true } : {}),\n };\n }\n\n default:\n return {\n content: [{ type: 'text', text: `Unknown tool: ${name}` }],\n isError: true,\n };\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n content: [{ type: 'text', text: `Error: ${message}` }],\n isError: true,\n };\n }\n}\n\n// ── JSON-RPC dispatch ───────────────────────────────────────────────────\n\nfunction sendResponse(response: JsonRpcResponse): void {\n const json = JSON.stringify(response);\n process.stdout.write(json + '\\n');\n}\n\nasync function handleMessage(msg: JsonRpcRequest): Promise<void> {\n const { id, method, params } = msg;\n\n switch (method) {\n case 'initialize': {\n sendResponse({\n jsonrpc: '2.0',\n id,\n result: {\n protocolVersion: '2024-11-05',\n capabilities: { tools: {} },\n serverInfo: {\n name: 'kantban-gates',\n version: '0.1.0',\n },\n },\n });\n return;\n }\n\n case 'notifications/initialized': {\n // Client acknowledgement — no response needed for notifications\n return;\n }\n\n case 'tools/list': {\n sendResponse({\n jsonrpc: '2.0',\n id,\n result: { tools: TOOLS },\n });\n return;\n }\n\n case 'tools/call': {\n const toolName = params?.['name'] as string | undefined;\n const toolArgs = (params?.['arguments'] as Record<string, unknown> | undefined) ?? {};\n\n if (!toolName) {\n sendResponse({\n jsonrpc: '2.0',\n id,\n error: { code: -32602, message: 'Missing tool name' },\n });\n return;\n }\n\n const result = await handleToolCall(toolName, toolArgs);\n sendResponse({\n jsonrpc: '2.0',\n id,\n result,\n });\n return;\n }\n\n case 'ping': {\n sendResponse({ jsonrpc: '2.0', id, result: {} });\n return;\n }\n\n default: {\n // Unknown method — return method not found per JSON-RPC spec\n if (method.startsWith('notifications/')) {\n // Notifications don't get responses\n return;\n }\n sendResponse({\n jsonrpc: '2.0',\n id,\n error: { code: -32601, message: `Method not found: ${method}` },\n });\n }\n }\n}\n\n// ── stdin reader ────────────────────────────────────────────────────────\n\nconst rl = createInterface({ input: process.stdin });\n\n// Serialize message processing to prevent concurrent double-moves\nlet messageQueue: Promise<void> = Promise.resolve();\n\nrl.on('line', (line: string) => {\n const trimmed = line.trim();\n if (!trimmed) return;\n\n let msg: JsonRpcRequest;\n try {\n msg = JSON.parse(trimmed) as JsonRpcRequest;\n } catch {\n sendResponse({\n jsonrpc: '2.0',\n id: null as unknown as number,\n error: { code: -32700, message: 'Parse error' },\n });\n return;\n }\n\n messageQueue = messageQueue.then(() => handleMessage(msg)).catch((err) => {\n process.stderr.write(`gate-proxy-server: unhandled error in message handler: ${err instanceof Error ? err.message : String(err)}\\n`);\n });\n});\n\nrl.on('close', () => {\n process.exit(0);\n});\n\n// Suppress unhandled rejection crashes — log to stderr and continue\nprocess.on('unhandledRejection', (err) => {\n process.stderr.write(\n `gate-proxy-server: unhandled rejection: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n});\n\nprocess.stderr.write(`gate-proxy-server: started (column=\"${columnName}\", gates=${gateConfig.default.length})\\n`);\n","import { resolveGatesForColumn, parseTimeout } from './gate-config.js';\nimport { formatGateErrors } from './gate-runner.js';\nimport type { GateConfig, GateDefinition, GateResult } from '@kantban/types';\n\nexport interface GateProxyDeps {\n runGates: (gates: GateDefinition[], options?: { cwd?: string; env?: Record<string, string>; totalTimeoutMs?: number }) => Promise<GateResult[]>;\n forwardMoveTicket: (args: Record<string, unknown>) => Promise<unknown>;\n forwardCompleteTask: (args: Record<string, unknown>) => Promise<unknown>;\n getTicketGateWaivers: (ticketId: string) => Promise<string[]>;\n}\n\nexport interface RunGatesResult {\n passed: boolean;\n results: GateResult[];\n}\n\nexport interface MoveArgs {\n projectId: string;\n ticketId: string;\n columnId: string;\n currentColumnName: string;\n args: Record<string, unknown>;\n}\n\nexport interface CompleteArgs {\n projectId: string;\n ticketId: string;\n currentColumnName: string;\n args: Record<string, unknown>;\n}\n\nexport interface InterceptResult {\n error?: 'GATE_FAILURE';\n message?: string;\n formatted?: string;\n results?: GateResult[];\n hint?: string;\n forwardResult?: unknown;\n}\n\nexport class GateProxy {\n private config: GateConfig;\n private deps: GateProxyDeps;\n\n constructor(config: GateConfig, deps: GateProxyDeps) {\n this.config = config;\n this.deps = deps;\n }\n\n private buildRunOptions(): { cwd?: string; env?: Record<string, string>; totalTimeoutMs?: number } {\n const opts: { cwd?: string; env?: Record<string, string>; totalTimeoutMs?: number } = {};\n if (this.config.settings?.cwd !== undefined) opts.cwd = this.config.settings.cwd;\n if (this.config.settings?.env !== undefined) opts.env = this.config.settings.env;\n if (this.config.settings?.total_timeout !== undefined) {\n opts.totalTimeoutMs = parseTimeout(this.config.settings.total_timeout);\n }\n return opts;\n }\n\n async handleRunGates(columnName: string, ticketId?: string): Promise<RunGatesResult> {\n const allGates = resolveGatesForColumn(this.config, columnName);\n\n let gates = allGates;\n if (ticketId) {\n const waivers = await this.deps.getTicketGateWaivers(ticketId);\n const waiverSet = new Set(waivers);\n gates = allGates.filter((g) => !waiverSet.has(g.name));\n }\n\n const results = await this.deps.runGates(gates, this.buildRunOptions());\n return {\n passed: results.filter((r) => r.required).every((r) => r.passed),\n results,\n };\n }\n\n async handleMoveTicket(move: MoveArgs): Promise<InterceptResult> {\n const allGates = resolveGatesForColumn(this.config, move.currentColumnName);\n\n // Filter out waived gates\n const waivers = await this.deps.getTicketGateWaivers(move.ticketId);\n const waiverSet = new Set(waivers);\n const gates = allGates.filter((g) => !waiverSet.has(g.name));\n\n let results: GateResult[];\n try {\n results = await this.deps.runGates(gates, this.buildRunOptions());\n } catch (err) {\n return {\n error: 'GATE_FAILURE',\n message: `Gate evaluation error: ${err instanceof Error ? err.message : String(err)}`,\n hint: 'Fix the gate environment and retry',\n };\n }\n\n const requiredFailures = results.filter((r) => r.required && !r.passed);\n if (requiredFailures.length > 0) {\n return {\n error: 'GATE_FAILURE',\n message: `Cannot move ticket — ${requiredFailures.length} required gate(s) failed`,\n formatted: formatGateErrors(results.filter((r) => r.required)),\n results,\n hint: 'Fix the failing gate(s) and try move_ticket again',\n };\n }\n\n const forwardResult = await this.deps.forwardMoveTicket({\n ...move.args,\n projectId: move.projectId,\n ticketId: move.ticketId,\n column_id: move.columnId,\n });\n\n return { forwardResult };\n }\n\n async handleCompleteTask(complete: CompleteArgs): Promise<InterceptResult> {\n const allGates = resolveGatesForColumn(this.config, complete.currentColumnName);\n const waivers = await this.deps.getTicketGateWaivers(complete.ticketId);\n const waiverSet = new Set(waivers);\n const gates = allGates.filter((g) => !waiverSet.has(g.name));\n\n let results: GateResult[];\n try {\n results = await this.deps.runGates(gates, this.buildRunOptions());\n } catch (err) {\n return {\n error: 'GATE_FAILURE',\n message: `Gate evaluation error: ${err instanceof Error ? err.message : String(err)}`,\n hint: 'Fix the gate environment and retry',\n };\n }\n\n const requiredFailures = results.filter((r) => r.required && !r.passed);\n if (requiredFailures.length > 0) {\n return {\n error: 'GATE_FAILURE',\n message: `Cannot complete task — ${requiredFailures.length} required gate(s) failed`,\n formatted: formatGateErrors(results.filter((r) => r.required)),\n results,\n hint: 'Fix the failing gate(s) and try complete_task again',\n };\n }\n\n const forwardResult = await this.deps.forwardCompleteTask({\n ...complete.args,\n projectId: complete.projectId,\n ticketId: complete.ticketId,\n });\n\n return { forwardResult };\n }\n}\n"],"mappings":"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","names":["deps","columnName","projectId"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runGates
3
- } from "./chunk-2P25AHSD.js";
3
+ } from "./chunk-GCDCGOWL.js";
4
4
  import {
5
5
  ClaudeProvider,
6
6
  RalphLoop,
@@ -12,14 +12,20 @@ import {
12
12
  generateMcpConfig,
13
13
  parseJsonFromLlmOutput,
14
14
  parseStuckDetectionResponse
15
- } from "./chunk-7HJZFR7Y.js";
15
+ } from "./chunk-YFBFQAFI.js";
16
16
  import {
17
17
  LoopCheckpointSchema,
18
18
  VerdictSchema,
19
19
  parseGateConfig,
20
20
  parseTimeout,
21
21
  resolveGatesForColumn
22
- } from "./chunk-ZTQJMXJM.js";
22
+ } from "./chunk-DAFLEMLK.js";
23
+ import {
24
+ IS_WINDOWS,
25
+ crossSpawnOptions,
26
+ killProcessTree,
27
+ normalizeEol
28
+ } from "./chunk-4VT3TGJ5.js";
23
29
 
24
30
  // src/commands/pipeline.ts
25
31
  import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4, readFileSync as readFileSync3, unlinkSync as unlinkSync3, existsSync as existsSync3, appendFileSync as appendFileSync2 } from "fs";
@@ -51,25 +57,25 @@ function isPlausibleRemoteUrl(url) {
51
57
  }
52
58
  function ensureWorktreeRemote(worktreePath) {
53
59
  try {
54
- const remotes = execFileSync("git", ["-C", worktreePath, "remote"], {
60
+ const remotes = normalizeEol(execFileSync("git", ["-C", worktreePath, "remote"], {
55
61
  stdio: "pipe",
56
62
  encoding: "utf-8"
57
- }).trim();
63
+ })).trim();
58
64
  if (remotes.split("\n").includes("origin")) {
59
- const currentUrl = execFileSync("git", ["-C", worktreePath, "remote", "get-url", "origin"], {
65
+ const currentUrl = normalizeEol(execFileSync("git", ["-C", worktreePath, "remote", "get-url", "origin"], {
60
66
  stdio: "pipe",
61
67
  encoding: "utf-8"
62
- }).trim();
68
+ })).trim();
63
69
  if (isPlausibleRemoteUrl(currentUrl)) return;
64
70
  console.error(`[worktree] Invalid origin URL in ${worktreePath}: "${currentUrl}" \u2014 fixing`);
65
71
  execFileSync("git", ["-C", worktreePath, "remote", "remove", "origin"], {
66
72
  stdio: "pipe"
67
73
  });
68
74
  }
69
- const originUrl = execFileSync("git", ["remote", "get-url", "origin"], {
75
+ const originUrl = normalizeEol(execFileSync("git", ["remote", "get-url", "origin"], {
70
76
  stdio: "pipe",
71
77
  encoding: "utf-8"
72
- }).trim();
78
+ })).trim();
73
79
  if (originUrl && isPlausibleRemoteUrl(originUrl)) {
74
80
  execFileSync("git", ["-C", worktreePath, "remote", "add", "origin", originUrl], {
75
81
  stdio: "pipe"
@@ -114,7 +120,7 @@ async function findWorktreeForBranch(exec, branch) {
114
120
  const { stdout } = await execPromise(exec, "git", ["worktree", "list", "--porcelain"]);
115
121
  const targetRef = `refs/heads/${branch}`;
116
122
  let currentPath = null;
117
- for (const line of stdout.split("\n")) {
123
+ for (const line of normalizeEol(stdout).split("\n")) {
118
124
  if (line.startsWith("worktree ")) currentPath = line.slice("worktree ".length);
119
125
  if (line.startsWith("branch ") && line.slice("branch ".length) === targetRef && currentPath) {
120
126
  return currentPath;
@@ -2116,7 +2122,7 @@ var RunMemory = class {
2116
2122
  }
2117
2123
  try {
2118
2124
  const content = await this.deps.getDocument(this._documentId);
2119
- const lines = content.split("\n");
2125
+ const lines = normalizeEol(content).split("\n");
2120
2126
  if (lines.length > DEFAULT_COMPACTION_THRESHOLD) {
2121
2127
  const truncated = lines.slice(-DEFAULT_COMPACTION_THRESHOLD);
2122
2128
  return "[Run memory truncated \u2014 compaction needed]\n" + truncated.join("\n");
@@ -2137,7 +2143,7 @@ var RunMemory = class {
2137
2143
  try {
2138
2144
  const content = await this.deps.getDocument(this._documentId);
2139
2145
  if (!content) return false;
2140
- return content.split("\n").length > threshold;
2146
+ return normalizeEol(content).split("\n").length > threshold;
2141
2147
  } catch {
2142
2148
  return false;
2143
2149
  }
@@ -2768,13 +2774,16 @@ function isAlive(pid) {
2768
2774
  function readManifest() {
2769
2775
  try {
2770
2776
  return fs.readFileSync(MANIFEST_PATH, 'utf-8')
2771
- .split('\\n')
2777
+ .split(/\\r?\\n/)
2772
2778
  .map(l => parseInt(l.trim(), 10))
2773
2779
  .filter(p => !isNaN(p) && p > 0);
2774
2780
  } catch { return []; }
2775
2781
  }
2776
2782
 
2777
2783
  function killPid(pid, signal) {
2784
+ if (process.platform === 'win32') {
2785
+ try { require('child_process').execFileSync('taskkill', ['/pid', String(pid), '/t', '/f'], { stdio: 'pipe' }); return; } catch { /* fall through */ }
2786
+ }
2778
2787
  try { process.kill(pid, signal); } catch { /* already dead */ }
2779
2788
  }
2780
2789
 
@@ -2782,14 +2791,14 @@ function cleanup() {
2782
2791
  const pids = readManifest();
2783
2792
  for (const pid of pids) {
2784
2793
  killPid(pid, 'SIGTERM');
2785
- try { process.kill(-pid, 'SIGTERM'); } catch { /* ignore */ }
2794
+ if (process.platform !== 'win32') { try { process.kill(-pid, 'SIGTERM'); } catch { /* ignore */ } }
2786
2795
  }
2787
2796
 
2788
2797
  setTimeout(() => {
2789
2798
  for (const pid of pids) {
2790
2799
  if (isAlive(pid)) {
2791
2800
  killPid(pid, 'SIGKILL');
2792
- try { process.kill(-pid, 'SIGKILL'); } catch { /* ignore */ }
2801
+ if (process.platform !== 'win32') { try { process.kill(-pid, 'SIGKILL'); } catch { /* ignore */ } }
2793
2802
  }
2794
2803
  }
2795
2804
 
@@ -2833,7 +2842,8 @@ function spawnReaper(config) {
2833
2842
  const script = buildReaperScript(config);
2834
2843
  const child = spawn(process.execPath, ["-e", script], {
2835
2844
  detached: true,
2836
- stdio: "ignore"
2845
+ stdio: "ignore",
2846
+ windowsHide: true
2837
2847
  });
2838
2848
  child.unref();
2839
2849
  if (child.pid) {
@@ -2844,7 +2854,7 @@ function spawnReaper(config) {
2844
2854
  function killReaper(reaperPidPath) {
2845
2855
  try {
2846
2856
  const pid = parseInt(
2847
- readFileSync(reaperPidPath, "utf-8").trim(),
2857
+ readFileSync(reaperPidPath, "utf-8").replace(/\r\n/g, "\n").trim(),
2848
2858
  10
2849
2859
  );
2850
2860
  if (pid && !isNaN(pid)) {
@@ -3153,7 +3163,7 @@ var ProviderRegistry = class {
3153
3163
 
3154
3164
  // src/providers/codex-provider.ts
3155
3165
  import { spawn as spawn2, execFileSync as execFileSync2 } from "child_process";
3156
- import { existsSync } from "fs";
3166
+ import { existsSync, rmSync as rmSync2 } from "fs";
3157
3167
 
3158
3168
  // src/providers/codex-jsonl-parser.ts
3159
3169
  var CodexJsonlParser = class {
@@ -3311,7 +3321,7 @@ var CodexProvider = class {
3311
3321
  });
3312
3322
  } catch {
3313
3323
  try {
3314
- execFileSync2("rm", ["-rf", request.workingDirectory], { stdio: "pipe" });
3324
+ rmSync2(request.workingDirectory, { recursive: true, force: true });
3315
3325
  execFileSync2("git", ["worktree", "add", "-b", request.workingDirectory, request.workingDirectory, "HEAD"], {
3316
3326
  stdio: "pipe"
3317
3327
  });
@@ -3333,7 +3343,8 @@ var CodexProvider = class {
3333
3343
  return new Promise((resolve) => {
3334
3344
  const child = spawn2("codex", args, {
3335
3345
  stdio: ["ignore", "pipe", "pipe"],
3336
- env: { ...process.env, ...env }
3346
+ env: { ...process.env, ...env },
3347
+ ...crossSpawnOptions()
3337
3348
  });
3338
3349
  const parser = new CodexJsonlParser();
3339
3350
  parser.onEvent = (event) => request.onStreamEvent?.(event);
@@ -3359,10 +3370,7 @@ var CodexProvider = class {
3359
3370
  } catch {
3360
3371
  }
3361
3372
  killTimer = setTimeout(() => {
3362
- try {
3363
- child.kill("SIGKILL");
3364
- } catch {
3365
- }
3373
+ if (child.pid) killProcessTree(child.pid, "SIGKILL");
3366
3374
  }, 5e3);
3367
3375
  }, CODEX_TIMEOUT_MS);
3368
3376
  let resolved = false;
@@ -3451,7 +3459,7 @@ var CodexProvider = class {
3451
3459
 
3452
3460
  // src/providers/gemini-provider.ts
3453
3461
  import { spawn as spawn3, execFileSync as execFileSync3 } from "child_process";
3454
- import { existsSync as existsSync2, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2 } from "fs";
3462
+ import { existsSync as existsSync2, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, rmSync as rmSync3 } from "fs";
3455
3463
  import { join as join2, dirname } from "path";
3456
3464
  import { homedir } from "os";
3457
3465
  import { fileURLToPath } from "url";
@@ -3681,7 +3689,7 @@ var GeminiProvider = class _GeminiProvider {
3681
3689
  });
3682
3690
  } catch {
3683
3691
  try {
3684
- execFileSync3("rm", ["-rf", request.workingDirectory], { stdio: "pipe" });
3692
+ rmSync3(request.workingDirectory, { recursive: true, force: true });
3685
3693
  execFileSync3("git", ["worktree", "add", "-b", request.workingDirectory, request.workingDirectory, "HEAD"], {
3686
3694
  stdio: "pipe"
3687
3695
  });
@@ -3708,7 +3716,8 @@ var GeminiProvider = class _GeminiProvider {
3708
3716
  return new Promise((resolve) => {
3709
3717
  const child = spawn3("gemini", args, {
3710
3718
  stdio: ["pipe", "pipe", "pipe"],
3711
- cwd: cwd || void 0
3719
+ cwd: cwd || void 0,
3720
+ ...crossSpawnOptions()
3712
3721
  });
3713
3722
  const parser = new GeminiJsonlParser();
3714
3723
  parser.onEvent = (event) => request.onStreamEvent?.(event);
@@ -3724,7 +3733,7 @@ var GeminiProvider = class _GeminiProvider {
3724
3733
  child.stderr?.on("data", (chunk) => {
3725
3734
  const text = chunk.toString();
3726
3735
  stderr += text;
3727
- const lines = text.split("\n").filter((l) => l.trim());
3736
+ const lines = normalizeEol(text).split("\n").filter((l) => l.trim());
3728
3737
  if (lines.length) lastStderrLine = lines[lines.length - 1];
3729
3738
  });
3730
3739
  child.stdin?.end();
@@ -3754,10 +3763,7 @@ var GeminiProvider = class _GeminiProvider {
3754
3763
  } catch {
3755
3764
  }
3756
3765
  killTimer = setTimeout(() => {
3757
- try {
3758
- child.kill("SIGKILL");
3759
- } catch {
3760
- }
3766
+ if (child.pid) killProcessTree(child.pid, "SIGKILL");
3761
3767
  }, 5e3);
3762
3768
  }, GEMINI_TIMEOUT_MS);
3763
3769
  let resolved = false;
@@ -3841,14 +3847,16 @@ var GeminiProvider = class _GeminiProvider {
3841
3847
  hookConfig.allowedTools = tr.allowedTools ? translateToolNames(tr.allowedTools) : null;
3842
3848
  hookConfig.disallowedTools = tr.disallowedTools ? translateToolNames(tr.disallowedTools) : null;
3843
3849
  if (tr.tools !== void 0) hookConfig.builtinToolsMode = tr.tools;
3844
- const wrapperPath = join2(dir, ".kantban-hook-before-tool.sh");
3850
+ const ext = IS_WINDOWS ? ".cmd" : ".sh";
3851
+ const wrapperPath = join2(dir, `.kantban-hook-before-tool${ext}`);
3845
3852
  writeFileSync3(wrapperPath, this.generateHookWrapper(hookPath, "BeforeToolSelection", join2(dir, ".kantban-hook-config.json")), { mode: 493 });
3846
3853
  hooks.BeforeToolSelection = [{ matcher: "*", hooks: [{ type: "command", command: wrapperPath, timeout: 3e4 }] }];
3847
3854
  }
3848
3855
  if (request.maxTurns && request.mcpConfig) {
3849
3856
  hookConfig.maxTurns = request.maxTurns;
3850
3857
  hookConfig.turnFile = join2(dir, ".kantban-turn-counter");
3851
- const wrapperPath = join2(dir, ".kantban-hook-after-agent.sh");
3858
+ const ext = IS_WINDOWS ? ".cmd" : ".sh";
3859
+ const wrapperPath = join2(dir, `.kantban-hook-after-agent${ext}`);
3852
3860
  writeFileSync3(wrapperPath, this.generateHookWrapper(hookPath, "AfterAgent", join2(dir, ".kantban-hook-config.json")), { mode: 493 });
3853
3861
  hooks.AfterAgent = [{ matcher: "*", hooks: [{ type: "command", command: wrapperPath, timeout: 3e4 }] }];
3854
3862
  }
@@ -3881,6 +3889,18 @@ var GeminiProvider = class _GeminiProvider {
3881
3889
  /** Generate a per-session bash wrapper that invokes the Node hook script.
3882
3890
  * Gemini CLI ignores `args` in hook definitions, so we embed all arguments. */
3883
3891
  generateHookWrapper(hookScript, event, configFile) {
3892
+ if (IS_WINDOWS) {
3893
+ return [
3894
+ "@echo off",
3895
+ 'set "STDIN_FILE=%TEMP%\\kantban-hook-%RANDOM%%RANDOM%.tmp"',
3896
+ 'findstr "^" > "%STDIN_FILE%"',
3897
+ `node "${hookScript}" "${event}" "${configFile}" < "%STDIN_FILE%"`,
3898
+ 'set "EXIT_CODE=%ERRORLEVEL%"',
3899
+ 'del /f /q "%STDIN_FILE%" >nul 2>&1',
3900
+ "exit /b %EXIT_CODE%",
3901
+ ""
3902
+ ].join("\r\n");
3903
+ }
3884
3904
  return `#!/bin/bash
3885
3905
  # KantBan pipeline hook wrapper \u2014 Gemini CLI does not pass args, so we embed them.
3886
3906
  STDIN_FILE=$(mktemp)
@@ -3894,7 +3914,7 @@ node "${hookScript}" "${event}" "${configFile}" < "$STDIN_FILE"
3894
3914
  * which contains lines like "YOLO mode is enabled..." and "Loaded cached credentials."
3895
3915
  * that corrupt JSON parsing in downstream consumers (advisor, light-call). */
3896
3916
  static stripCliPreamble(text) {
3897
- const lines = text.split("\n");
3917
+ const lines = normalizeEol(text).split("\n");
3898
3918
  const jsonStart = lines.findIndex((line) => {
3899
3919
  const trimmed = line.trim();
3900
3920
  return trimmed.startsWith("{") || trimmed.startsWith("[");
@@ -4057,7 +4077,7 @@ function childManifestPath(boardId) {
4057
4077
  }
4058
4078
  function readChildManifest(boardId) {
4059
4079
  try {
4060
- const contents = readFileSync3(childManifestPath(boardId), "utf-8");
4080
+ const contents = normalizeEol(readFileSync3(childManifestPath(boardId), "utf-8"));
4061
4081
  return contents.split("\n").map((l) => l.trim()).filter((l) => l !== "").map(Number).filter((n) => !isNaN(n) && n > 0);
4062
4082
  } catch {
4063
4083
  return [];
@@ -4077,7 +4097,7 @@ function cleanupOrphanedProcesses(boardId) {
4077
4097
  if (stalePid && stalePid !== process.pid) {
4078
4098
  try {
4079
4099
  process.kill(stalePid, 0);
4080
- process.kill(stalePid, "SIGTERM");
4100
+ killProcessTree(stalePid, "SIGTERM");
4081
4101
  console.log(`Killed stale orchestrator (PID ${String(stalePid)})`);
4082
4102
  } catch {
4083
4103
  }
@@ -4091,7 +4111,7 @@ function cleanupOrphanedProcesses(boardId) {
4091
4111
  for (const pid of manifestPids) {
4092
4112
  try {
4093
4113
  process.kill(pid, 0);
4094
- process.kill(pid, "SIGTERM");
4114
+ killProcessTree(pid, "SIGTERM");
4095
4115
  } catch {
4096
4116
  }
4097
4117
  }
@@ -4105,7 +4125,7 @@ function cleanupOrphanedProcesses(boardId) {
4105
4125
  if (reaperPid && !isNaN(reaperPid)) {
4106
4126
  try {
4107
4127
  process.kill(reaperPid, 0);
4108
- process.kill(reaperPid, "SIGTERM");
4128
+ killProcessTree(reaperPid, "SIGTERM");
4109
4129
  console.log(`Killed stale reaper (PID ${String(reaperPid)})`);
4110
4130
  } catch {
4111
4131
  }
@@ -4257,7 +4277,7 @@ async function runPipeline(client, args) {
4257
4277
  // Run memory enrichment — inject accumulated discoveries into each iteration prompt
4258
4278
  fetchRunMemoryContent: mem ? async () => {
4259
4279
  const content = await mem.getContent();
4260
- const lines = content.split("\n");
4280
+ const lines = normalizeEol(content).split("\n");
4261
4281
  if (lines.length > 500) {
4262
4282
  return lines.slice(-500).join("\n");
4263
4283
  }
@@ -4671,12 +4691,14 @@ Received ${signal}. Shutting down gracefully...`);
4671
4691
  process.exit(1);
4672
4692
  });
4673
4693
  });
4674
- process.on("SIGHUP", () => {
4675
- shutdown("SIGHUP").catch((err) => {
4676
- console.error("Error during shutdown:", err);
4677
- process.exit(1);
4694
+ if (!IS_WINDOWS) {
4695
+ process.on("SIGHUP", () => {
4696
+ shutdown("SIGHUP").catch((err) => {
4697
+ console.error("Error during shutdown:", err);
4698
+ process.exit(1);
4699
+ });
4678
4700
  });
4679
- });
4701
+ }
4680
4702
  cleanupOrphanedProcesses(opts.boardId);
4681
4703
  writePidFile(opts.boardId);
4682
4704
  logger.orchestrator(`PID file written: ${String(process.pid)}`);
@@ -4855,13 +4877,8 @@ async function stopPipeline(args) {
4855
4877
  return;
4856
4878
  }
4857
4879
  try {
4858
- try {
4859
- process.kill(-pid, "SIGTERM");
4860
- console.log(`Sent SIGTERM to pipeline process group (pgid ${String(pid)}) for board ${boardId}.`);
4861
- } catch {
4862
- process.kill(pid, "SIGTERM");
4863
- console.log(`Sent SIGTERM to pipeline process ${String(pid)} for board ${boardId}.`);
4864
- }
4880
+ killProcessTree(pid, "SIGTERM");
4881
+ console.log(`Sent SIGTERM to pipeline process ${String(pid)} for board ${boardId}.`);
4865
4882
  } catch (err) {
4866
4883
  const message = err instanceof Error ? err.message : String(err);
4867
4884
  console.error(`Failed to stop pipeline (PID ${String(pid)}): ${message}`);
@@ -4874,4 +4891,4 @@ export {
4874
4891
  runPipeline,
4875
4892
  stopPipeline
4876
4893
  };
4877
- //# sourceMappingURL=pipeline-ZI7Y25IA.js.map
4894
+ //# sourceMappingURL=pipeline-N7Z43P6P.js.map