@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stilero/bankan",
3
- "version": "1.0.11",
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
- if (buf.includes(completionMarker)) {
116
+ const cleanBuf = stripAnsi(buf);
117
+ if (cleanBuf.includes(completionMarker)) {
109
118
  return { complete: true, blockedReason: null };
110
119
  }
111
120
 
112
- const blockedMatch = buf.match(/=== BLOCKED: (.+?) ===/);
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
- : buf.includes('=== PLAN END ===');
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 (!buf.includes('=== PLAN END ===') && buf.includes('=== PLAN START ===')) {
931
- const partial = buf.slice(buf.indexOf('=== PLAN START ==='));
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
- : buf.includes('=== PLAN END ===');
1080
+ : cleanBuf.includes('=== PLAN END ===');
1070
1081
  const planText = planReady
1071
- ? getLastStructuredBlock(buf, '=== PLAN START ===', '=== PLAN END ===')
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
- : buf.includes('=== REVIEW END ===');
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
- authBlockedReason = getAuthBlockedReason(buf, agent.cli);
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
- : buf.includes('=== PLAN END ===');
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 || buf;
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
- : buf.includes('=== REVIEW END ===');
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 || buf;
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)) {