@vincemakes/kiso-tui-cells 0.48.0 → 0.50.0

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.
@@ -162,6 +162,10 @@ export type BodyCell = {
162
162
  * the target, the reason in the W4 parentheses idiom, NO timing
163
163
  * metadata (the call never ran). */
164
164
  reason: string | null;
165
+ /** O1: the write created its file (the result's "created" tag)
166
+ * — the input no longer cites "absent" when the host binds
167
+ * the observed revision. */
168
+ created?: boolean;
165
169
  /** A5: the approval verdict — the permission_decided event bound
166
170
  * into the cell (no free-standing ` approved` orphan row). The
167
171
  * settled head row aggregates name + status + decidedBy in ONE
@@ -639,9 +639,10 @@ function settledMeta(c) {
639
639
  if (c.name === "write_file" && parsed !== null && typeof parsed.content === "string") {
640
640
  return `+${countLines(parsed.content)}`;
641
641
  }
642
- if (c.name === "edit_file" && parsed !== null && typeof parsed.search === "string" && typeof parsed.replace === "string") {
643
- const added = countLines(parsed.replace);
644
- const removed = countLines(parsed.search);
642
+ const hunks = c.name === "edit_file" && parsed !== null ? hunksOf(parsed) : null;
643
+ if (hunks !== null) {
644
+ const added = hunks.reduce((n, h) => n + countLines(h.replace), 0);
645
+ const removed = hunks.reduce((n, h) => n + countLines(h.search), 0);
645
646
  if (added + removed > 0)
646
647
  return `+${added} -${removed}`;
647
648
  }
@@ -1817,8 +1818,8 @@ function editDiffOf(c) {
1817
1818
  if (c.name !== "edit_file" && c.name !== "write_file")
1818
1819
  return null;
1819
1820
  const known = editDiffMemo.get(c);
1820
- if (known !== undefined)
1821
- return known;
1821
+ if (known !== undefined && known.created === (c.created === true))
1822
+ return known.out;
1822
1823
  let input = null;
1823
1824
  try {
1824
1825
  const v = JSON.parse(c.inputFull);
@@ -1839,14 +1840,15 @@ function editDiffOf(c) {
1839
1840
  else if (input !== null && typeof input.content === "string") {
1840
1841
  const content = input.content.replace(/\n$/, "");
1841
1842
  const all = content === "" ? [] : content.split("\n");
1842
- // a write that cites "absent" creates the file; any other rewrites
1843
- // one whose old text is not in the log, so its lines are shown as
1844
- // they now read, not as additions
1845
- const created = input.expectedRevision === "absent";
1843
+ // a write that cites "absent" (or, with the observed revision
1844
+ // bound by the host, whose result is tagged "created") creates the
1845
+ // file; any other rewrites one whose old text is not in the log, so
1846
+ // its lines are shown as they now read, not as additions
1847
+ const created = input.expectedRevision === "absent" || c.created === true;
1846
1848
  const n = `${all.length} line${all.length === 1 ? "" : "s"}`;
1847
1849
  out = { lines: all.map((text) => ({ kind: created ? "+" : " ", text })), meta: created ? `new file \u00b7 ${n}` : `rewrote \u00b7 ${n}`, cap: WRITE_HEAD };
1848
1850
  }
1849
- editDiffMemo.set(c, out);
1851
+ editDiffMemo.set(c, { created: c.created === true, out });
1850
1852
  return out;
1851
1853
  }
1852
1854
  /** The TOOL's OWN truncation note (W10) — a different fact from the
package/dist/diff.d.ts CHANGED
@@ -85,9 +85,12 @@ export declare function editFileDiff(oldContent: string, search: string, replace
85
85
  /** write_file: a new file is all +; an existing file diffs row-level
86
86
  * against its old content. */
87
87
  export declare function writeFileDiff(oldContent: string | null, newContent: string): DiffResult;
88
- /** The hunks an edit_file call carries: its `edits`, or its one
89
- * `search`/`replace` pair. Null when the input has neither (the tool
90
- * refuses such a call, so there is nothing to draw). */
88
+ /** The hunks an edit_file call carries, in either vocabulary the tool
89
+ * takes (ADR-0061): `edits` of `{oldText, newText}` or of legacy
90
+ * `{search, replace}`, or the legacy top-level pair. A log written before
91
+ * the rename keeps the old names, so a reader must take both. Null when
92
+ * the tool would refuse the shape (nothing, or a hunk mixing the two) —
93
+ * there is nothing to draw. */
91
94
  export declare function hunksOf(input: Record<string, unknown>): Hunk[] | null;
92
95
  /** Graphite §6 (R2a) — what an edit CHANGED, drawn from the call's own
93
96
  * hunks: per hunk a line LCS of `search` against `replace`, the lines
package/dist/diff.js CHANGED
@@ -152,21 +152,30 @@ export function writeFileDiff(oldContent, newContent) {
152
152
  const lines = withContext(lcsDiff(oldContent.split("\n"), newContent.split("\n")));
153
153
  return { lines, ...stats(lines), outcome: "diff" };
154
154
  }
155
- /** The hunks an edit_file call carries: its `edits`, or its one
156
- * `search`/`replace` pair. Null when the input has neither (the tool
157
- * refuses such a call, so there is nothing to draw). */
155
+ /** The hunks an edit_file call carries, in either vocabulary the tool
156
+ * takes (ADR-0061): `edits` of `{oldText, newText}` or of legacy
157
+ * `{search, replace}`, or the legacy top-level pair. A log written before
158
+ * the rename keeps the old names, so a reader must take both. Null when
159
+ * the tool would refuse the shape (nothing, or a hunk mixing the two) —
160
+ * there is nothing to draw. */
158
161
  export function hunksOf(input) {
159
- const pair = (x) => {
162
+ const pair = (x, current) => {
160
163
  if (typeof x !== "object" || x === null)
161
164
  return null;
162
- const { search, replace } = x;
165
+ const { oldText, newText, search, replace } = x;
166
+ const hasCurrent = oldText !== undefined || newText !== undefined;
167
+ const hasLegacy = search !== undefined || replace !== undefined;
168
+ if (hasCurrent && (hasLegacy || !current))
169
+ return null;
170
+ if (hasCurrent)
171
+ return typeof oldText === "string" && typeof newText === "string" ? { search: oldText, replace: newText } : null;
163
172
  return typeof search === "string" && typeof replace === "string" ? { search, replace } : null;
164
173
  };
165
174
  if (Array.isArray(input.edits)) {
166
- const hunks = input.edits.map(pair);
175
+ const hunks = input.edits.map((h) => pair(h, true));
167
176
  return hunks.length > 0 && hunks.every((h) => h !== null) ? hunks : null;
168
177
  }
169
- const one = pair(input);
178
+ const one = pair(input, false);
170
179
  return one === null ? null : [one];
171
180
  }
172
181
  /** Graphite §6 (R2a) — what an edit CHANGED, drawn from the call's own
package/dist/render.js CHANGED
@@ -6,6 +6,7 @@
6
6
  * dependencies (the tui-cells package has none).
7
7
  */
8
8
  import { charWidth, displayWidth, visibleWidth, widthCut } from "./width.js";
9
+ import { hunksOf } from "./diff.js";
9
10
  import { bg, breathRamp, fg, graphiteColours, mix, terminalTier } from "./graphite.js";
10
11
  const BASE = { bold: "\x1b[1m", dim: "\x1b[2m", red: "\x1b[31m", green: "\x1b[32m", warn: "\x1b[33m", italic: "\x1b[3m", italicEnd: "\x1b[23m", underline: "\x1b[4m", underlineEnd: "\x1b[24m", rv: "\x1b[7m", rvEnd: "\x1b[27m", reset: "\x1b[0m" };
11
12
  /**
@@ -364,8 +365,9 @@ function toolSummaryDetail(name, input, result) {
364
365
  }
365
366
  case "edit_file": {
366
367
  const path = String(input.path ?? "?");
367
- const removed = lines(String(input.search ?? ""));
368
- const added = lines(String(input.replace ?? ""));
368
+ const hunks = hunksOf(input) ?? [];
369
+ const removed = hunks.reduce((n, h) => n + lines(h.search), 0);
370
+ const added = hunks.reduce((n, h) => n + lines(h.replace), 0);
369
371
  return `${path} (+${added} -${removed})`;
370
372
  }
371
373
  case "shell": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-tui-cells",
3
- "version": "0.48.0",
3
+ "version": "0.50.0",
4
4
  "description": "kiso tui-cells \u2014 the components cell renderer (components, diff, width, the render slice). Zero runtime dependencies: input is data, output is bytes.",
5
5
  "type": "module",
6
6
  "license": "MIT",