@stilero/bankan 1.0.11 → 1.0.13
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/package.json +1 -1
- package/server/src/orchestrator.js +30 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stilero/bankan",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.13",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Run AI coding agents like a Kanban board. Plan, implement, review and ship code using parallel AI agents across your local repositories.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,6 +21,14 @@ const MAX_REVIEW_CYCLES = 3;
|
|
|
21
21
|
let pollTimer = null;
|
|
22
22
|
let signalTimer = null;
|
|
23
23
|
|
|
24
|
+
function stripAnsi(text) {
|
|
25
|
+
if (typeof text !== 'string') return text;
|
|
26
|
+
return text.replace(
|
|
27
|
+
/[\x1b\x9b][\[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><~]|\x1b\].*?(?:\x07|\x1b\\)|\r/g,
|
|
28
|
+
''
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
24
32
|
function escapePrompt(text) {
|
|
25
33
|
return text.replace(/'/g, "'\\''");
|
|
26
34
|
}
|
|
@@ -45,7 +53,7 @@ function buildAgentCommand(cliTool, prompt, mode = 'interactive') {
|
|
|
45
53
|
return buildCodexExecCommand(prompt, { captureLastMessage: false, sandbox: 'read-only' });
|
|
46
54
|
}
|
|
47
55
|
|
|
48
|
-
if (mode === 'print') {
|
|
56
|
+
if (mode === 'print' || mode === 'plan' || mode === 'review') {
|
|
49
57
|
return `claude --print '${escapePrompt(prompt)}'`;
|
|
50
58
|
}
|
|
51
59
|
|
|
@@ -105,11 +113,12 @@ function getImplementationCompletionState(agent, taskId) {
|
|
|
105
113
|
return { complete: false, blockedReason: null };
|
|
106
114
|
}
|
|
107
115
|
|
|
108
|
-
|
|
116
|
+
const cleanBuf = stripAnsi(buf);
|
|
117
|
+
if (cleanBuf.includes(completionMarker)) {
|
|
109
118
|
return { complete: true, blockedReason: null };
|
|
110
119
|
}
|
|
111
120
|
|
|
112
|
-
const blockedMatch =
|
|
121
|
+
const blockedMatch = cleanBuf.match(/=== BLOCKED: (.+?) ===/);
|
|
113
122
|
return { complete: false, blockedReason: blockedMatch ? blockedMatch[1] : null };
|
|
114
123
|
}
|
|
115
124
|
|
|
@@ -548,7 +557,7 @@ function onPlanComplete(agentId, taskId) {
|
|
|
548
557
|
if (!planner) return;
|
|
549
558
|
const bufStr = planner.getBufferString(100);
|
|
550
559
|
const captured = planner.cli === 'codex' ? readCapturedCodexMessage(bufStr) : null;
|
|
551
|
-
const sourceText = captured || bufStr;
|
|
560
|
+
const sourceText = captured || stripAnsi(bufStr);
|
|
552
561
|
|
|
553
562
|
// Extract plan text
|
|
554
563
|
const planText = getLastStructuredBlock(sourceText, '=== PLAN START ===', '=== PLAN END ===');
|
|
@@ -726,7 +735,7 @@ async function onReviewComplete(agentId, taskId) {
|
|
|
726
735
|
const bufStr = reviewer.getBufferString(100);
|
|
727
736
|
|
|
728
737
|
const captured = reviewer.cli === 'codex' ? readCapturedCodexMessage(bufStr) : null;
|
|
729
|
-
const sourceText = captured || bufStr;
|
|
738
|
+
const sourceText = captured || stripAnsi(bufStr);
|
|
730
739
|
const reviewText = getLastStructuredBlock(sourceText, '=== REVIEW START ===', '=== REVIEW END ===');
|
|
731
740
|
if (!reviewText) return;
|
|
732
741
|
const reviewResult = parseReviewResult(reviewText);
|
|
@@ -902,7 +911,7 @@ const TRUST_PROMPT_RE = /trust the files|Do you trust|allow.*to run in this/i;
|
|
|
902
911
|
|
|
903
912
|
function checkTrustPrompt(agent, buf) {
|
|
904
913
|
if (agent.status === 'blocked') return true; // already handled
|
|
905
|
-
if (!TRUST_PROMPT_RE.test(buf)) return false;
|
|
914
|
+
if (!TRUST_PROMPT_RE.test(stripAnsi(buf))) return false;
|
|
906
915
|
|
|
907
916
|
store.updateTask(agent.currentTask, {
|
|
908
917
|
status: 'blocked',
|
|
@@ -920,15 +929,16 @@ function checkSignals() {
|
|
|
920
929
|
for (const agent of agentManager.getAgentsByRole('plan')) {
|
|
921
930
|
if (agent.status === 'active' && agent.currentTask) {
|
|
922
931
|
const buf = agent.getBufferString(50);
|
|
932
|
+
const cleanBuf = stripAnsi(buf);
|
|
923
933
|
const planReady = agent.cli === 'codex'
|
|
924
934
|
? hasCodexStructuredOutput(buf, '=== PLAN END ===')
|
|
925
|
-
:
|
|
935
|
+
: cleanBuf.includes('=== PLAN END ===');
|
|
926
936
|
if (planReady) {
|
|
927
937
|
onPlanComplete(agent.id, agent.currentTask);
|
|
928
938
|
} else if (!checkTrustPrompt(agent, buf)) {
|
|
929
939
|
// Live plan streaming
|
|
930
|
-
if (!
|
|
931
|
-
const partial =
|
|
940
|
+
if (!cleanBuf.includes('=== PLAN END ===') && cleanBuf.includes('=== PLAN START ===')) {
|
|
941
|
+
const partial = cleanBuf.slice(cleanBuf.indexOf('=== PLAN START ==='));
|
|
932
942
|
if (!isPlanPlaceholder(partial)) {
|
|
933
943
|
bus.emit('plan:partial', { taskId: agent.currentTask, plan: partial });
|
|
934
944
|
}
|
|
@@ -975,7 +985,7 @@ function checkSignals() {
|
|
|
975
985
|
const buf = agent.getBufferString(50);
|
|
976
986
|
const reviewReady = agent.cli === 'codex'
|
|
977
987
|
? hasCodexStructuredOutput(buf, '=== REVIEW END ===')
|
|
978
|
-
: buf.includes('=== REVIEW END ===');
|
|
988
|
+
: stripAnsi(buf).includes('=== REVIEW END ===');
|
|
979
989
|
if (reviewReady) {
|
|
980
990
|
onReviewComplete(agent.id, agent.currentTask);
|
|
981
991
|
} else if (!checkTrustPrompt(agent, buf)) {
|
|
@@ -1063,12 +1073,13 @@ function pollLoop() {
|
|
|
1063
1073
|
const isImplementor = agent.id.startsWith('imp-');
|
|
1064
1074
|
const isReviewer = agent.id.startsWith('rev-');
|
|
1065
1075
|
|
|
1076
|
+
const cleanBuf = stripAnsi(buf);
|
|
1066
1077
|
if (isPlanner) {
|
|
1067
1078
|
const planReady = agent.cli === 'codex'
|
|
1068
1079
|
? hasCodexStructuredOutput(buf, '=== PLAN END ===')
|
|
1069
|
-
:
|
|
1080
|
+
: cleanBuf.includes('=== PLAN END ===');
|
|
1070
1081
|
const planText = planReady
|
|
1071
|
-
? getLastStructuredBlock(
|
|
1082
|
+
? getLastStructuredBlock(cleanBuf, '=== PLAN START ===', '=== PLAN END ===')
|
|
1072
1083
|
: null;
|
|
1073
1084
|
if (planText && !isPlanPlaceholder(planText)) {
|
|
1074
1085
|
onPlanComplete(agent.id, taskId);
|
|
@@ -1101,7 +1112,7 @@ function pollLoop() {
|
|
|
1101
1112
|
} else if (isReviewer) {
|
|
1102
1113
|
const reviewReady = agent.cli === 'codex'
|
|
1103
1114
|
? hasCodexStructuredOutput(buf, '=== REVIEW END ===')
|
|
1104
|
-
:
|
|
1115
|
+
: cleanBuf.includes('=== REVIEW END ===');
|
|
1105
1116
|
if (reviewReady) {
|
|
1106
1117
|
const reviewer = agentManager.get(agent.id);
|
|
1107
1118
|
const bufStr = reviewer?.getBufferString(100) || '';
|
|
@@ -1158,7 +1169,8 @@ bus.on('agent:unexpected-exit', ({ agentId, taskId }) => {
|
|
|
1158
1169
|
let authBlockedReason = null;
|
|
1159
1170
|
if (agent) {
|
|
1160
1171
|
const buf = agent.getBufferString(100);
|
|
1161
|
-
|
|
1172
|
+
const cleanBuf = stripAnsi(buf);
|
|
1173
|
+
authBlockedReason = getAuthBlockedReason(cleanBuf, agent.cli);
|
|
1162
1174
|
const isPlanner = agentId.startsWith('plan-');
|
|
1163
1175
|
const isImplementor = agentId.startsWith('imp-');
|
|
1164
1176
|
const isReviewer = agentId.startsWith('rev-');
|
|
@@ -1166,10 +1178,10 @@ bus.on('agent:unexpected-exit', ({ agentId, taskId }) => {
|
|
|
1166
1178
|
if (isPlanner) {
|
|
1167
1179
|
const planReady = agent.cli === 'codex'
|
|
1168
1180
|
? hasCodexStructuredOutput(buf, '=== PLAN END ===')
|
|
1169
|
-
:
|
|
1181
|
+
: cleanBuf.includes('=== PLAN END ===');
|
|
1170
1182
|
if (planReady) {
|
|
1171
1183
|
const captured = agent.cli === 'codex' ? readCapturedCodexMessage(buf, { remove: false }) : null;
|
|
1172
|
-
const sourceText = captured ||
|
|
1184
|
+
const sourceText = captured || cleanBuf;
|
|
1173
1185
|
const planText = getLastStructuredBlock(sourceText, '=== PLAN START ===', '=== PLAN END ===');
|
|
1174
1186
|
if (planText && !isPlanPlaceholder(planText)) {
|
|
1175
1187
|
onPlanComplete(agentId, taskId);
|
|
@@ -1189,10 +1201,10 @@ bus.on('agent:unexpected-exit', ({ agentId, taskId }) => {
|
|
|
1189
1201
|
} else if (isReviewer) {
|
|
1190
1202
|
const reviewReady = agent.cli === 'codex'
|
|
1191
1203
|
? hasCodexStructuredOutput(buf, '=== REVIEW END ===')
|
|
1192
|
-
:
|
|
1204
|
+
: cleanBuf.includes('=== REVIEW END ===');
|
|
1193
1205
|
if (reviewReady) {
|
|
1194
1206
|
const captured = agent.cli === 'codex' ? readCapturedCodexMessage(buf, { remove: false }) : null;
|
|
1195
|
-
const sourceText = captured ||
|
|
1207
|
+
const sourceText = captured || cleanBuf;
|
|
1196
1208
|
const reviewText = getLastStructuredBlock(sourceText, '=== REVIEW START ===', '=== REVIEW END ===');
|
|
1197
1209
|
const reviewResult = reviewText ? parseReviewResult(reviewText) : null;
|
|
1198
1210
|
if (reviewText && reviewResult && !isReviewResultPlaceholder(reviewText, reviewResult)) {
|