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.
- package/README.md +37 -0
- package/dist/client.d.ts +35 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +109 -0
- package/dist/client.js.map +1 -0
- package/dist/commands/context.d.ts +3 -0
- package/dist/commands/context.d.ts.map +1 -0
- package/dist/commands/context.js +27 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/cron.d.ts +3 -0
- package/dist/commands/cron.d.ts.map +1 -0
- package/dist/commands/cron.js +106 -0
- package/dist/commands/cron.js.map +1 -0
- package/dist/commands/pipeline.d.ts +4 -0
- package/dist/commands/pipeline.d.ts.map +1 -0
- package/dist/commands/pipeline.js +543 -0
- package/dist/commands/pipeline.js.map +1 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +135 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/work.d.ts +3 -0
- package/dist/commands/work.d.ts.map +1 -0
- package/dist/commands/work.js +76 -0
- package/dist/commands/work.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +65 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/event-queue.d.ts +28 -0
- package/dist/lib/event-queue.d.ts.map +1 -0
- package/dist/lib/event-queue.js +65 -0
- package/dist/lib/event-queue.js.map +1 -0
- package/dist/lib/logger.d.ts +20 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +52 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/mcp-config.d.ts +3 -0
- package/dist/lib/mcp-config.d.ts.map +1 -0
- package/dist/lib/mcp-config.js +49 -0
- package/dist/lib/mcp-config.js.map +1 -0
- package/dist/lib/orchestrator.d.ts +172 -0
- package/dist/lib/orchestrator.d.ts.map +1 -0
- package/dist/lib/orchestrator.js +315 -0
- package/dist/lib/orchestrator.js.map +1 -0
- package/dist/lib/prompt-composer.d.ts +102 -0
- package/dist/lib/prompt-composer.d.ts.map +1 -0
- package/dist/lib/prompt-composer.js +178 -0
- package/dist/lib/prompt-composer.js.map +1 -0
- package/dist/lib/ralph-loop.d.ts +47 -0
- package/dist/lib/ralph-loop.d.ts.map +1 -0
- package/dist/lib/ralph-loop.js +114 -0
- package/dist/lib/ralph-loop.js.map +1 -0
- package/dist/lib/ws-client.d.ts +28 -0
- package/dist/lib/ws-client.d.ts.map +1 -0
- package/dist/lib/ws-client.js +113 -0
- package/dist/lib/ws-client.js.map +1 -0
- 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
|
+
}
|