kantban-cli 0.1.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 (58) hide show
  1. package/README.md +37 -0
  2. package/dist/client.d.ts +35 -0
  3. package/dist/client.d.ts.map +1 -0
  4. package/dist/client.js +109 -0
  5. package/dist/client.js.map +1 -0
  6. package/dist/commands/context.d.ts +3 -0
  7. package/dist/commands/context.d.ts.map +1 -0
  8. package/dist/commands/context.js +27 -0
  9. package/dist/commands/context.js.map +1 -0
  10. package/dist/commands/cron.d.ts +3 -0
  11. package/dist/commands/cron.d.ts.map +1 -0
  12. package/dist/commands/cron.js +106 -0
  13. package/dist/commands/cron.js.map +1 -0
  14. package/dist/commands/pipeline.d.ts +4 -0
  15. package/dist/commands/pipeline.d.ts.map +1 -0
  16. package/dist/commands/pipeline.js +543 -0
  17. package/dist/commands/pipeline.js.map +1 -0
  18. package/dist/commands/status.d.ts +3 -0
  19. package/dist/commands/status.d.ts.map +1 -0
  20. package/dist/commands/status.js +135 -0
  21. package/dist/commands/status.js.map +1 -0
  22. package/dist/commands/work.d.ts +3 -0
  23. package/dist/commands/work.d.ts.map +1 -0
  24. package/dist/commands/work.js +76 -0
  25. package/dist/commands/work.js.map +1 -0
  26. package/dist/index.d.ts +3 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +65 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/lib/event-queue.d.ts +28 -0
  31. package/dist/lib/event-queue.d.ts.map +1 -0
  32. package/dist/lib/event-queue.js +65 -0
  33. package/dist/lib/event-queue.js.map +1 -0
  34. package/dist/lib/logger.d.ts +20 -0
  35. package/dist/lib/logger.d.ts.map +1 -0
  36. package/dist/lib/logger.js +52 -0
  37. package/dist/lib/logger.js.map +1 -0
  38. package/dist/lib/mcp-config.d.ts +3 -0
  39. package/dist/lib/mcp-config.d.ts.map +1 -0
  40. package/dist/lib/mcp-config.js +49 -0
  41. package/dist/lib/mcp-config.js.map +1 -0
  42. package/dist/lib/orchestrator.d.ts +172 -0
  43. package/dist/lib/orchestrator.d.ts.map +1 -0
  44. package/dist/lib/orchestrator.js +315 -0
  45. package/dist/lib/orchestrator.js.map +1 -0
  46. package/dist/lib/prompt-composer.d.ts +102 -0
  47. package/dist/lib/prompt-composer.d.ts.map +1 -0
  48. package/dist/lib/prompt-composer.js +178 -0
  49. package/dist/lib/prompt-composer.js.map +1 -0
  50. package/dist/lib/ralph-loop.d.ts +47 -0
  51. package/dist/lib/ralph-loop.d.ts.map +1 -0
  52. package/dist/lib/ralph-loop.js +114 -0
  53. package/dist/lib/ralph-loop.js.map +1 -0
  54. package/dist/lib/ws-client.d.ts +28 -0
  55. package/dist/lib/ws-client.d.ts.map +1 -0
  56. package/dist/lib/ws-client.js +113 -0
  57. package/dist/lib/ws-client.js.map +1 -0
  58. package/package.json +49 -0
@@ -0,0 +1,178 @@
1
+ export function composePrompt(columnContext, ticketContext, meta) {
2
+ const parts = [];
3
+ // 0. System preamble — tells the agent who it is and what to do
4
+ const hasUnresolvedBlockers = ticketContext.ticket_links.some((l) => l.direction === 'inward' && l.link_type === 'blocks' && !l.resolved);
5
+ parts.push(`# Pipeline Agent Instructions
6
+
7
+ You are a pipeline automation agent processing ticket #${String(ticketContext.ticket.ticket_number)}: "${ticketContext.ticket.title}".
8
+
9
+ ## Your Goal
10
+ Advance this ticket through the pipeline. Your success criteria:
11
+ 1. Complete the work described in the ticket and column prompt below
12
+ 2. Move the ticket to the next column using \`${ticketContext.tool_prefix}move_ticket\`
13
+ 3. Or mark it complete using \`${ticketContext.tool_prefix}complete_task\`
14
+
15
+ ## Iteration ${meta.iteration} of ${meta.maxIterations}
16
+ ${meta.iteration >= meta.maxIterations - 1 ? '**FINAL ITERATIONS** — prioritize moving the ticket NOW or it will be marked stalled.' : `You have ${meta.maxIterations - meta.iteration} iterations remaining. Make meaningful progress each iteration.`}
17
+ ${meta.gutterCount > 0 ? `
18
+ ## ⚠ Progress Warning
19
+ No meaningful progress detected for ${meta.gutterCount} consecutive iteration(s).
20
+ ${meta.gutterThreshold - meta.gutterCount} iteration(s) remain before this loop is terminated as stalled.
21
+
22
+ You MUST change approach. What you've been doing is not working. Consider:
23
+ - Breaking the problem into smaller steps
24
+ - Setting a field value to record partial progress
25
+ - Creating a comment explaining what's blocking you
26
+ - Asking for help via a signal
27
+ ` : ''}
28
+ ## Available Tools (prefix: ${ticketContext.tool_prefix})
29
+ - **${ticketContext.tool_prefix}check_transition** — ALWAYS call this before moving. Returns allowed/blocked with recovery steps.
30
+ Params: \`{ projectId, boardId, ticketId, targetColumnId }\`
31
+ - **${ticketContext.tool_prefix}move_ticket** — Move ticket to a new column. Include handoff data for the next agent.
32
+ Params: \`{ projectId, ticketId, column_id, handoff: { branch?, commit_sha?, build_status?, notes? } }\`
33
+ - **${ticketContext.tool_prefix}complete_task** — Move to a done column with handoff data.
34
+ Params: \`{ projectId, ticketId, handoff: { ... } }\`
35
+ - **${ticketContext.tool_prefix}set_field_value** — Set a required field before moving (check transition rules).
36
+ Params: \`{ projectId, ticketId, fieldId, value }\`
37
+ - **${ticketContext.tool_prefix}create_signal** — Leave knowledge for future agents at any scope.
38
+ - **${ticketContext.tool_prefix}create_comment** — Add progress notes to the ticket.
39
+
40
+ ## Iteration Summary (Required)
41
+ Before finishing your work this iteration, you MUST create a comment using \`${ticketContext.tool_prefix}create_comment\` summarizing:
42
+ - What you accomplished
43
+ - What remains to be done
44
+ - Any blockers or issues encountered
45
+
46
+ This comment is your handoff to the next iteration. Without it, the next iteration starts blind.
47
+
48
+ ## Critical Rules
49
+ 1. **Always call ${ticketContext.tool_prefix}check_transition before ${ticketContext.tool_prefix}move_ticket** — moves that violate workflow rules will fail.
50
+ 2. **If the ticket has UNRESOLVED blockers, do not attempt to move it.** Create a comment explaining you are waiting, then stop.
51
+ 3. **Include handoff data** when moving — the next agent needs context (branch name, build status, etc.).
52
+ 4. **If you cannot make progress**, create a comment explaining why and stop. Do not burn iterations.
53
+ ${hasUnresolvedBlockers ? '\n**WARNING: This ticket has UNRESOLVED blockers. Do NOT attempt to move it. Document your status and stop.**\n' : ''}
54
+ ---
55
+ `);
56
+ // 1. Column prompt document (the PROMPT.md equivalent)
57
+ if (columnContext.prompt_document?.content) {
58
+ parts.push(columnContext.prompt_document.content);
59
+ }
60
+ // 2. Ticket details
61
+ parts.push(`## Current Ticket\n`);
62
+ parts.push(`**Title:** ${ticketContext.ticket.title}`);
63
+ parts.push(`**Ticket ID:** ${ticketContext.ticket.id}`);
64
+ parts.push(`**Ticket Number:** ${String(ticketContext.ticket.ticket_number)}`);
65
+ if (ticketContext.ticket.description) {
66
+ parts.push(`\n${ticketContext.ticket.description}`);
67
+ }
68
+ // Assignee and column position
69
+ if (ticketContext.ticket.assignee) {
70
+ parts.push(`**Assignee:** ${ticketContext.ticket.assignee.name}`);
71
+ }
72
+ if (ticketContext.ticket.column) {
73
+ parts.push(`**Current Column:** ${ticketContext.ticket.column.name} (${ticketContext.ticket.column.type})`);
74
+ }
75
+ if (ticketContext.ticket.backward_transitions > 0) {
76
+ parts.push(`**Backward Transitions:** ${String(ticketContext.ticket.backward_transitions)}`);
77
+ }
78
+ // 3. Field values
79
+ if (ticketContext.field_values.length > 0) {
80
+ parts.push(`\n## Field Values\n`);
81
+ for (const fv of ticketContext.field_values) {
82
+ parts.push(`- **${fv.field_name}:** ${fv.value !== null ? JSON.stringify(fv.value) : '(not set)'}`);
83
+ }
84
+ }
85
+ // Parent/children
86
+ if (ticketContext.parent) {
87
+ parts.push(`\n## Parent Ticket\n`);
88
+ parts.push(`- #${String(ticketContext.parent.ticket_number)}: ${ticketContext.parent.title}`);
89
+ }
90
+ if (ticketContext.children.length > 0) {
91
+ parts.push(`\n## Child Tickets\n`);
92
+ for (const child of ticketContext.children) {
93
+ parts.push(`- #${String(child.ticket_number)}: ${child.title}${child.column_name ? ` (${child.column_name})` : ''}`);
94
+ }
95
+ }
96
+ // 4. Transition history (handoff chain)
97
+ if (ticketContext.transitions.length > 0) {
98
+ parts.push(`\n## Transition History\n`);
99
+ for (const t of ticketContext.transitions) {
100
+ let line = `- ${t.from ?? 'Backlog'} → ${t.to}`;
101
+ if (t.handoff)
102
+ line += ` (handoff: ${JSON.stringify(t.handoff)})`;
103
+ parts.push(line);
104
+ }
105
+ }
106
+ // Ticket links
107
+ if (ticketContext.ticket_links.length > 0) {
108
+ parts.push(`\n## Ticket Links\n`);
109
+ const blockers = ticketContext.ticket_links.filter(l => l.direction === 'inward' && l.link_type === 'blocks');
110
+ const blocking = ticketContext.ticket_links.filter(l => l.direction === 'outward' && l.link_type === 'blocks');
111
+ const related = ticketContext.ticket_links.filter(l => l.link_type === 'relates_to');
112
+ if (blockers.length > 0) {
113
+ parts.push(`**Blocked by:**`);
114
+ for (const l of blockers) {
115
+ const status = l.resolved ? '(resolved)' : '⚠ UNRESOLVED';
116
+ parts.push(`- #${String(l.ticket_number)}: ${l.title} [${l.column_name ?? 'backlog'}] ${status}`);
117
+ }
118
+ }
119
+ if (blocking.length > 0) {
120
+ parts.push(`**Blocks:**`);
121
+ for (const l of blocking) {
122
+ parts.push(`- #${String(l.ticket_number)}: ${l.title} [${l.column_name ?? 'backlog'}]`);
123
+ }
124
+ }
125
+ if (related.length > 0) {
126
+ parts.push(`**Related:**`);
127
+ for (const l of related) {
128
+ parts.push(`- #${String(l.ticket_number)}: ${l.title}`);
129
+ }
130
+ }
131
+ }
132
+ // 5. Composed signals (guardrails)
133
+ if (ticketContext.signals.length > 0) {
134
+ parts.push(`\n## Signals (Guardrails)\n`);
135
+ for (const s of ticketContext.signals) {
136
+ parts.push(`- ${s}`);
137
+ }
138
+ }
139
+ // Comments
140
+ if (ticketContext.comments.length > 0) {
141
+ parts.push(`\n## Comments\n`);
142
+ for (const c of ticketContext.comments) {
143
+ parts.push(`**${c.author}** (${c.created_at}):\n${c.body}\n`);
144
+ }
145
+ }
146
+ // 6. Transition rules
147
+ if (ticketContext.transition_rules) {
148
+ parts.push(`\n## Transition Rules\n`);
149
+ parts.push(ticketContext.transition_rules);
150
+ }
151
+ // Dependency requirements
152
+ if (ticketContext.dependency_requirements && ticketContext.dependency_requirements !== 'No dependency or field requirements configured.') {
153
+ parts.push(`\n## Dependency & Field Requirements\n`);
154
+ parts.push(ticketContext.dependency_requirements);
155
+ }
156
+ // 7. Linked documents
157
+ if (ticketContext.linked_documents.length > 0) {
158
+ parts.push(`\n## Linked Documents\n`);
159
+ for (const doc of ticketContext.linked_documents) {
160
+ if (doc.content) {
161
+ parts.push(`### ${doc.title}\n`);
162
+ parts.push(doc.content);
163
+ }
164
+ else if (doc.truncated) {
165
+ parts.push(`- ${doc.title} (document too large — read via kantban_get_document)`);
166
+ }
167
+ }
168
+ }
169
+ // 8. Iteration metadata
170
+ parts.push(`\n## Pipeline Metadata\n`);
171
+ parts.push(`- Iteration: ${meta.iteration} / ${meta.maxIterations}`);
172
+ parts.push(`- Project ID: ${meta.projectId}`);
173
+ parts.push(`- Tool Prefix: ${ticketContext.tool_prefix}`);
174
+ parts.push(`- Column: ${columnContext.column.name}`);
175
+ parts.push(`- Goal: ${columnContext.column.goal ?? 'No goal set'}`);
176
+ return parts.join('\n');
177
+ }
178
+ //# sourceMappingURL=prompt-composer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-composer.js","sourceRoot":"","sources":["../../src/lib/prompt-composer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import { type ColumnContext, type TicketContext } from './prompt-composer.js';
2
+ import type { TicketFingerprint } from '@kantban/types';
3
+ export interface LoopConfig {
4
+ maxIterations: number;
5
+ gutterThreshold: number;
6
+ modelPreference?: string;
7
+ maxBudgetUsd?: number | null;
8
+ worktreeEnabled?: boolean;
9
+ worktreePattern?: string;
10
+ postMoveRetryDelayMs?: number;
11
+ }
12
+ export interface LoopResult {
13
+ reason: 'moved' | 'max_iterations' | 'stalled' | 'stopped' | 'error';
14
+ iterations: number;
15
+ gutterCount: number;
16
+ lastError?: string;
17
+ }
18
+ export interface RalphLoopDeps {
19
+ fetchTicketContext: (ticketId: string) => Promise<TicketContext>;
20
+ fetchColumnContext: (columnId: string) => Promise<ColumnContext>;
21
+ fetchFingerprint: (ticketId: string) => Promise<TicketFingerprint>;
22
+ invokeClaudeP: (prompt: string, options: ClaudeInvokeOptions) => Promise<{
23
+ exitCode: number;
24
+ output: string;
25
+ }>;
26
+ mcpConfigPath: string;
27
+ projectId: string;
28
+ }
29
+ export interface ClaudeInvokeOptions {
30
+ mcpConfigPath: string;
31
+ model?: string | undefined;
32
+ maxBudgetUsd?: number | null | undefined;
33
+ worktree?: string | undefined;
34
+ }
35
+ export declare class RalphLoop {
36
+ private ticketId;
37
+ private columnId;
38
+ private config;
39
+ private deps;
40
+ private stopped;
41
+ private currentIteration;
42
+ constructor(ticketId: string, columnId: string, config: LoopConfig, deps: RalphLoopDeps);
43
+ stop(): void;
44
+ get iteration(): number;
45
+ run(): Promise<LoopResult>;
46
+ }
47
+ //# sourceMappingURL=ralph-loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ralph-loop.d.ts","sourceRoot":"","sources":["../../src/lib/ralph-loop.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC7F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,OAAO,GAAG,gBAAgB,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;IACrE,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;IACjE,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;IACjE,gBAAgB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACnE,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/G,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/B;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,IAAI,CAAgB;IAC5B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,gBAAgB,CAAK;gBAEjB,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa;IAOvF,IAAI,IAAI,IAAI;IAIZ,IAAI,SAAS,IAAI,MAAM,CAEtB;IAEK,GAAG,IAAI,OAAO,CAAC,UAAU,CAAC;CA2FjC"}
@@ -0,0 +1,114 @@
1
+ import { composePrompt } from './prompt-composer.js';
2
+ export class RalphLoop {
3
+ ticketId;
4
+ columnId;
5
+ config;
6
+ deps;
7
+ stopped = false;
8
+ currentIteration = 0;
9
+ constructor(ticketId, columnId, config, deps) {
10
+ this.ticketId = ticketId;
11
+ this.columnId = columnId;
12
+ this.config = config;
13
+ this.deps = deps;
14
+ }
15
+ stop() {
16
+ this.stopped = true;
17
+ }
18
+ get iteration() {
19
+ return this.currentIteration;
20
+ }
21
+ async run() {
22
+ let gutterCount = 0;
23
+ let lastFingerprint = null;
24
+ for (let i = 1; i <= this.config.maxIterations; i++) {
25
+ if (this.stopped)
26
+ return { reason: 'stopped', iterations: i - 1, gutterCount };
27
+ this.currentIteration = i;
28
+ try {
29
+ // On first iteration, snapshot baseline fingerprint for gutter detection
30
+ if (!lastFingerprint) {
31
+ lastFingerprint = await this.deps.fetchFingerprint(this.ticketId);
32
+ }
33
+ // Fetch fresh context
34
+ const [ticketCtx, columnCtx] = await Promise.all([
35
+ this.deps.fetchTicketContext(this.ticketId),
36
+ this.deps.fetchColumnContext(this.columnId),
37
+ ]);
38
+ // Compose prompt
39
+ const prompt = composePrompt(columnCtx, ticketCtx, {
40
+ iteration: i,
41
+ maxIterations: this.config.maxIterations,
42
+ projectId: this.deps.projectId,
43
+ gutterCount,
44
+ gutterThreshold: this.config.gutterThreshold,
45
+ });
46
+ // Invoke Claude
47
+ if (this.stopped)
48
+ return { reason: 'stopped', iterations: i - 1, gutterCount };
49
+ const { exitCode, output } = await this.deps.invokeClaudeP(prompt, {
50
+ mcpConfigPath: this.deps.mcpConfigPath,
51
+ model: this.config.modelPreference,
52
+ maxBudgetUsd: this.config.maxBudgetUsd,
53
+ worktree: this.config.worktreeEnabled
54
+ ? this.config.worktreePattern?.replace('{ticket_number}', String(ticketCtx.ticket.ticket_number))
55
+ : undefined,
56
+ });
57
+ // Check exit code — non-zero means Claude crashed or was killed
58
+ if (exitCode !== 0) {
59
+ const snippet = output.slice(-200);
60
+ return { reason: 'error', iterations: i, gutterCount, lastError: `non-zero exit code: ${exitCode}. Last output: ${snippet}` };
61
+ }
62
+ // Check post-iteration state with retry for async MCP lag
63
+ const retryDelayMs = this.config.postMoveRetryDelayMs ?? 1500;
64
+ let afterFp = await this.deps.fetchFingerprint(this.ticketId);
65
+ if (afterFp.column_id === this.columnId) {
66
+ // Ticket still in same column — retry in case MCP move is still in flight
67
+ for (let retry = 0; retry < 2; retry++) {
68
+ await new Promise((r) => setTimeout(r, retryDelayMs));
69
+ afterFp = await this.deps.fetchFingerprint(this.ticketId);
70
+ if (afterFp.column_id !== this.columnId)
71
+ break;
72
+ }
73
+ }
74
+ // Did ticket move?
75
+ if (afterFp.column_id !== this.columnId) {
76
+ return { reason: 'moved', iterations: i, gutterCount };
77
+ }
78
+ // Gutter detection — a ticket is "in the gutter" when the agent
79
+ // isn't making meaningful progress. We compare the fingerprint
80
+ // BUT we ignore signal_count because a dependency-blocked agent
81
+ // can create a new "still waiting" signal every iteration, which
82
+ // tricks the old check into thinking progress was made.
83
+ if (lastFingerprint && fingerprintsEqualIgnoringSignals(lastFingerprint, afterFp)) {
84
+ gutterCount++;
85
+ if (gutterCount >= this.config.gutterThreshold) {
86
+ return { reason: 'stalled', iterations: i, gutterCount };
87
+ }
88
+ }
89
+ else {
90
+ gutterCount = 0;
91
+ }
92
+ lastFingerprint = afterFp;
93
+ }
94
+ catch (err) {
95
+ const message = err instanceof Error ? err.message : String(err);
96
+ // If ticket was deleted, the context fetch will 404
97
+ if (message.includes('404')) {
98
+ return { reason: 'moved', iterations: i, gutterCount };
99
+ }
100
+ return { reason: 'error', iterations: i, gutterCount, lastError: message };
101
+ }
102
+ }
103
+ return { reason: 'max_iterations', iterations: this.config.maxIterations, gutterCount };
104
+ }
105
+ }
106
+ function fingerprintsEqualIgnoringSignals(a, b) {
107
+ // Only field_value_count is a reliable progress indicator.
108
+ // signal_count and comment_count both inflate when a dependency-blocked
109
+ // agent posts "still waiting" messages each iteration.
110
+ // Column change is detected separately (exits as 'moved').
111
+ return a.column_id === b.column_id
112
+ && a.field_value_count === b.field_value_count;
113
+ }
114
+ //# sourceMappingURL=ralph-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ralph-loop.js","sourceRoot":"","sources":["../../src/lib/ralph-loop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { KantBanCLIClient } from '../client.js';
2
+ export interface WsClientOptions {
3
+ boardId: string;
4
+ projectId: string;
5
+ onEvent: (event: {
6
+ type: string;
7
+ payload: Record<string, unknown>;
8
+ }) => void;
9
+ onConnect: () => void;
10
+ onDisconnect: () => void;
11
+ }
12
+ export declare class PipelineWsClient {
13
+ private ws;
14
+ private client;
15
+ private options;
16
+ private reconnectTimer;
17
+ private reconnectAttempts;
18
+ private maxReconnectAttempts;
19
+ private pingTimer;
20
+ private stopped;
21
+ constructor(client: KantBanCLIClient, options: WsClientOptions);
22
+ connect(): Promise<void>;
23
+ stop(): void;
24
+ private send;
25
+ private cleanup;
26
+ private scheduleReconnect;
27
+ }
28
+ //# sourceMappingURL=ws-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws-client.d.ts","sourceRoot":"","sources":["../../src/lib/ws-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,KAAK,IAAI,CAAC;IAC7E,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,YAAY,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,oBAAoB,CAAM;IAClC,OAAO,CAAC,SAAS,CAA+C;IAChE,OAAO,CAAC,OAAO,CAAS;gBAEZ,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,eAAe;IAKxD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAiE9B,IAAI,IAAI,IAAI;IASZ,OAAO,CAAC,IAAI;IAMZ,OAAO,CAAC,OAAO;IAWf,OAAO,CAAC,iBAAiB;CAY1B"}
@@ -0,0 +1,113 @@
1
+ import WebSocket from 'ws';
2
+ export class PipelineWsClient {
3
+ ws = null;
4
+ client;
5
+ options;
6
+ reconnectTimer = null;
7
+ reconnectAttempts = 0;
8
+ maxReconnectAttempts = 10;
9
+ pingTimer = null;
10
+ stopped = false;
11
+ constructor(client, options) {
12
+ this.client = client;
13
+ this.options = options;
14
+ }
15
+ async connect() {
16
+ // Close any existing connection before creating a new one
17
+ if (this.ws) {
18
+ try {
19
+ this.ws.removeAllListeners();
20
+ this.ws.close();
21
+ }
22
+ catch { /* ignore */ }
23
+ this.ws = null;
24
+ }
25
+ this.cleanup();
26
+ // Get WS ticket
27
+ const { ticket } = await this.client.post('/ws-ticket');
28
+ // Build WS URL
29
+ const wsUrl = this.client.baseUrl.replace(/^http/, 'ws') + `/ws?ticket=${ticket}`;
30
+ return new Promise((resolve, reject) => {
31
+ let resolved = false;
32
+ this.ws = new WebSocket(wsUrl);
33
+ let subscribed = false;
34
+ this.ws.on('open', () => {
35
+ this.reconnectAttempts = 0;
36
+ // Start heartbeat
37
+ this.pingTimer = setInterval(() => this.send({ type: 'ping', payload: {} }), 30000);
38
+ // NOTE: board:subscribe is deferred until the first server message
39
+ // to avoid a race where the message arrives before the server's
40
+ // on('message') handler is attached (behind async setup).
41
+ });
42
+ this.ws.on('message', (data) => {
43
+ try {
44
+ const event = JSON.parse(data.toString());
45
+ // Send board:subscribe after the first server message proves the handler is ready
46
+ if (!subscribed) {
47
+ subscribed = true;
48
+ this.send({ type: 'board:subscribe', payload: { boardId: this.options.boardId, projectId: this.options.projectId } });
49
+ this.options.onConnect();
50
+ if (!resolved) {
51
+ resolved = true;
52
+ resolve();
53
+ }
54
+ }
55
+ this.options.onEvent(event);
56
+ }
57
+ catch (err) {
58
+ console.error('WS message parse error:', err);
59
+ }
60
+ });
61
+ this.ws.on('close', () => {
62
+ this.cleanup();
63
+ this.options.onDisconnect();
64
+ if (!this.stopped)
65
+ this.scheduleReconnect();
66
+ });
67
+ this.ws.on('error', (err) => {
68
+ if (!resolved) {
69
+ resolved = true;
70
+ reject(err);
71
+ }
72
+ // Otherwise reconnect will handle it
73
+ });
74
+ });
75
+ }
76
+ stop() {
77
+ this.stopped = true;
78
+ this.cleanup();
79
+ if (this.ws) {
80
+ try {
81
+ this.ws.removeAllListeners();
82
+ this.ws.close();
83
+ }
84
+ catch { /* ignore */ }
85
+ this.ws = null;
86
+ }
87
+ }
88
+ send(data) {
89
+ if (this.ws?.readyState === WebSocket.OPEN) {
90
+ this.ws.send(JSON.stringify(data));
91
+ }
92
+ }
93
+ cleanup() {
94
+ if (this.pingTimer) {
95
+ clearInterval(this.pingTimer);
96
+ this.pingTimer = null;
97
+ }
98
+ if (this.reconnectTimer) {
99
+ clearTimeout(this.reconnectTimer);
100
+ this.reconnectTimer = null;
101
+ }
102
+ }
103
+ scheduleReconnect() {
104
+ if (this.reconnectAttempts >= this.maxReconnectAttempts)
105
+ return;
106
+ const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
107
+ this.reconnectAttempts++;
108
+ this.reconnectTimer = setTimeout(() => void this.connect().catch((err) => {
109
+ console.error('WS reconnect failed:', err instanceof Error ? err.message : String(err));
110
+ }), delay);
111
+ }
112
+ }
113
+ //# sourceMappingURL=ws-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws-client.js","sourceRoot":"","sources":["../../src/lib/ws-client.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "kantban-cli",
3
+ "version": "0.1.1",
4
+ "description": "CLI for KantBan pipeline orchestration — bridges KantBan boards to local Claude Code sessions",
5
+ "type": "module",
6
+ "license": "UNLICENSED",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/Meltero/cantban.git",
10
+ "directory": "packages/cli"
11
+ },
12
+ "keywords": [
13
+ "kantban",
14
+ "cli",
15
+ "kanban",
16
+ "pipeline",
17
+ "claude",
18
+ "ai"
19
+ ],
20
+ "bin": {
21
+ "kantban": "./dist/index.js"
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "!dist/__tests__",
26
+ "README.md"
27
+ ],
28
+ "scripts": {
29
+ "build": "tsc",
30
+ "dev": "tsc --watch",
31
+ "test": "vitest run",
32
+ "typecheck": "tsc --noEmit",
33
+ "postbuild": "chmod +x dist/index.js",
34
+ "prepublishOnly": "tsc"
35
+ },
36
+ "dependencies": {
37
+ "ws": "^8.20.0",
38
+ "zod": "^3.25.0"
39
+ },
40
+ "devDependencies": {
41
+ "@kantban/types": "workspace:^",
42
+ "@types/node": "^22.0.0",
43
+ "@types/ws": "^8.18.1",
44
+ "typescript": "^5.8.0"
45
+ },
46
+ "engines": {
47
+ "node": ">=22"
48
+ }
49
+ }