kantban-cli 0.1.15 → 0.1.16

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 +0,0 @@
1
- {"version":3,"sources":["../src/commands/cron.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport type { KantBanCLIClient } from '../client.js';\nimport { generateMcpConfig, cleanupMcpConfig } from '../lib/mcp-config.js';\nimport { RalphLoop, type RalphLoopDeps, type LoopConfig, type ClaudeInvokeOptions } from '../lib/ralph-loop.js';\nimport type { ColumnContext, TicketContext } from '../lib/prompt-composer.js';\nimport type { TicketFingerprint } from '@kantban/types';\n\nconst execFileAsync = promisify(execFile);\n\nfunction parseDuration(input: string): number {\n const match = input.match(/^(\\d+)(s|m|h)$/);\n if (!match) throw new Error(`Invalid duration: ${input}. Use format like 5m, 30s, 1h`);\n const value = Number(match[1]);\n const unit = match[2];\n switch (unit) {\n case 's': return value * 1000;\n case 'm': return value * 60 * 1000;\n case 'h': return value * 60 * 60 * 1000;\n default: throw new Error(`Unknown unit: ${unit}`);\n }\n}\n\nexport async function runCron(client: KantBanCLIClient, args: string[]): Promise<void> {\n const columnId = args[0];\n if (!columnId) {\n console.error('Usage: kantban cron <column-id> [--interval 5m]');\n process.exit(1);\n }\n\n // Parse interval\n let intervalMs = 5 * 60 * 1000; // default 5m\n const intervalIdx = args.indexOf('--interval');\n const intervalArg = intervalIdx !== -1 ? args[intervalIdx + 1] : undefined;\n if (intervalArg) {\n intervalMs = parseDuration(intervalArg);\n }\n\n // Resolve project ID\n const projectId = process.env['KANTBAN_PROJECT_ID'];\n if (!projectId) {\n console.error('Error: KANTBAN_PROJECT_ID required');\n process.exit(1);\n }\n\n // Generate MCP config (stable path keyed by column)\n const mcpConfigPath = generateMcpConfig(client.baseUrl, client.token, `cron-${columnId}`);\n\n // invokeClaudeP helper (same pattern as pipeline.ts)\n async function invokeClaudeP(prompt: string, options: ClaudeInvokeOptions): Promise<{ exitCode: number; output: string; toolCallCount: number; tokensIn: number; tokensOut: number }> {\n const claudeArgs = ['-p', prompt, '--mcp-config', options.mcpConfigPath, '--dangerously-skip-permissions'];\n if (options.model) claudeArgs.push('--model', options.model);\n try {\n const { stdout } = await execFileAsync('claude', claudeArgs, { maxBuffer: 10 * 1024 * 1024 });\n return { exitCode: 0, output: stdout, toolCallCount: 0, tokensIn: 0, tokensOut: 0 };\n } catch (err: unknown) {\n const execErr = err as { code?: number; stdout?: string; message?: string };\n return { exitCode: execErr.code ?? 1, output: execErr.stdout ?? execErr.message ?? '', toolCallCount: 0, tokensIn: 0, tokensOut: 0 };\n }\n }\n\n let stopped = false;\n\n // Graceful shutdown\n const shutdown = () => {\n stopped = true;\n cleanupMcpConfig(mcpConfigPath);\n console.log('\\nCron stopped.');\n process.exit(0);\n };\n process.on('SIGTERM', shutdown);\n process.on('SIGINT', shutdown);\n\n console.log(`Cron: column ${columnId}, interval ${String(intervalMs / 1000)}s`);\n console.log('Press Ctrl+C to stop.\\n');\n\n // Main cron tick\n const tick = async () => {\n if (stopped) return;\n\n try {\n // Fetch column scope\n const columnScope = await client.get<ColumnContext>(\n `/projects/${projectId}/pipeline-context`,\n { columnId },\n );\n\n const tickets = columnScope.tickets ?? [];\n if (tickets.length === 0) {\n console.log(`[${new Date().toLocaleTimeString('en-US', { hour12: false })}] No tickets in column. Sleeping...`);\n return;\n }\n\n console.log(`[${new Date().toLocaleTimeString('en-US', { hour12: false })}] Processing ${String(tickets.length)} ticket(s)...`);\n\n // Process each ticket sequentially (one iteration each for cron)\n for (const ticket of tickets) {\n if (stopped) break;\n\n const deps: RalphLoopDeps = {\n fetchTicketContext: (tid) => client.get<TicketContext>(\n `/projects/${projectId}/pipeline-context`, { ticketId: tid },\n ),\n fetchColumnContext: (cid) => client.get<ColumnContext>(\n `/projects/${projectId}/pipeline-context`, { columnId: cid },\n ),\n fetchFingerprint: (tid) => client.getFingerprint(projectId, tid) as Promise<TicketFingerprint>,\n invokeClaudeP,\n mcpConfigPath,\n projectId,\n };\n\n const config: LoopConfig = { maxIterations: 1, gutterThreshold: 1 };\n\n const loop = new RalphLoop(ticket.id, columnId, config, deps);\n const result = await loop.run();\n console.log(` ${String(ticket.ticket_number)}: ${result.reason} (${String(result.iterations)} iter)`);\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error(`Cron tick error: ${message}`);\n }\n };\n\n // Run immediately, then on interval\n await tick();\n setInterval(() => void tick(), intervalMs);\n}\n"],"mappings":"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","names":[]}