kaizen-agent 1.40.2 → 1.40.3

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.
@@ -146,6 +146,9 @@ test("split: a header row holding a `#`, a year or a date cell is still the head
146
146
  for (const head of ["#\tRequest\tStatus", "No\tRequest\tStatus\t2026", "No\tRequest\tStatus\t2026-09-30", "No\tRequest\tStatus\t17-Sep-2026", "\tRequest\tStatus"]) {
147
147
  const body = `${head}\n1\tFix login page\tOpen\n2\tAdd export\tDone\n`;
148
148
  expect(sift("sheet", body)).toEqual({ items: [{ text: "Fix login page", notes: "1 · Open", row: 2 }], done: 1 });
149
+ // a Done task merged over two rows: the export leaves NO and status blank below
150
+ const merged = "NO\tRequest\tStatus\n1\tExport button missing\tDone\n\tExport keeps old filter\t\n2\tAdd dark mode\t\n";
151
+ expect(sift("sheet", merged)).toEqual({ items: [{ text: "Add dark mode", notes: "2", row: 4 }], done: 2 });
149
152
  }
150
153
  // A numbered row is data even when one of its cells is a header word said once.
151
154
  expect(split("sheet", "1\tIssue\tLogin fails on Safari\n2\tBug\tExport drops rows\n3\tBug\tSearch is slow\n").map((i) => i.text))
package/cli/sources.ts CHANGED
@@ -507,10 +507,15 @@ export function sift(kind: Kind, body: string, pics?: Map<number | string, strin
507
507
  const head = filled.slice(0, 5).find((row) => !/^\d{1,3}[.)]?$/.test(row.find(Boolean) ?? "") && (named(row, HEADER) >= 0 || named(row, STATUS) >= 0));
508
508
  const data = head ? filled.slice(filled.indexOf(head) + 1) : filled;
509
509
  const status = head ? named(head, STATUS) : -1;
510
+ // A task merged over several rows has its number and status in the top row only:
511
+ // the export leaves both blank below, so such a row takes the status above it.
512
+ let above = "";
513
+ const state = new Map(data.map((row) => { const own = row[status] ?? ""; if (own || row[0]) above = own; return [row, own || above]; }));
514
+ const finished = (row: string[]) => status >= 0 && DONE.test(state.get(row) ?? "");
510
515
  // One column for the whole sheet, the one holding the most text that reads like a
511
516
  // request. Never a cell per row, which takes `Open` or a name.
512
517
  // Finished rows do not vote: their replies outweigh the requests still open.
513
- const open = data.filter((row) => status < 0 || !DONE.test(row[status] ?? ""));
518
+ const open = data.filter((row) => !finished(row));
514
519
  const text = (i: number) => i === status ? 0 : open.reduce((sum, row) => sum + (wordy(row[i] ?? "") ? row[i]!.length : 0), 0);
515
520
  let wordiest = -1;
516
521
  for (let i = 0, most = 0; i < Math.max(0, ...data.map((row) => row.length)); i++) if (text(i) > most) { most = text(i); wordiest = i; }
@@ -524,7 +529,7 @@ export function sift(kind: Kind, body: string, pics?: Map<number | string, strin
524
529
  const items = data.flatMap((row) => {
525
530
  const text = row[col] ?? "";
526
531
  if (!text || (known < 0 && !wordy(text))) return [];
527
- if (status >= 0 && DONE.test(row[status] ?? "")) { done++; return []; }
532
+ if (finished(row)) { done++; return []; }
528
533
  const rest = row.filter((c, i) => c && i !== col).join(" · ");
529
534
  // The export keeps every row from the top, so a row's index is its number less one.
530
535
  return [with_({ text, ...(rest ? { notes: rest } : {}), row: cells.indexOf(row) + 1 }, cells.indexOf(row) + 1)];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kaizen-agent",
3
- "version": "1.40.2",
3
+ "version": "1.40.3",
4
4
  "description": "Staged workflow for AI agents — code, writing, research, operations: plan, human approval, build, independent review, bounded fix loop.",
5
5
  "type": "module",
6
6
  "bin": {