@lazyingart/agintiflow 0.17.3 → 0.17.5

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": "@lazyingart/agintiflow",
3
- "version": "0.17.3",
3
+ "version": "0.17.5",
4
4
  "type": "module",
5
5
  "description": "AgInTiFlow is a resumable Playwright website-control agent with OpenAI-compatible tool calling.",
6
6
  "license": "Apache-2.0",
package/public/app.js CHANGED
@@ -901,7 +901,7 @@ function renderWorkspacePanel(workspace = lastWorkspace, activity = lastWorkspac
901
901
  const label = blocked ? t("blockedLabel") : t("changedLabel");
902
902
  const path = escapeHtml(item.path || "");
903
903
  const reason = blocked ? `<div class="subtle">${escapeHtml(item.reason || "")}</div>` : "";
904
- const diff = item.diff ? `<pre class="change-diff">${escapeHtml(item.diff).slice(0, 1200)}</pre>` : "";
904
+ const diff = item.diff ? `<pre class="change-diff">${renderDiffHtml(item.diff, 1200)}</pre>` : "";
905
905
  const hashes =
906
906
  item.beforeHash || item.afterHash
907
907
  ? `<div class="change-meta"><span>before=${escapeHtml((item.beforeHash || "new").slice(0, 10))}</span><span>after=${escapeHtml((item.afterHash || "").slice(0, 10))}</span></div>`
@@ -1123,6 +1123,22 @@ function escapeHtml(value) {
1123
1123
  );
1124
1124
  }
1125
1125
 
1126
+ function renderDiffHtml(value, maxChars = 1200) {
1127
+ return String(value || "")
1128
+ .slice(0, maxChars)
1129
+ .split(/\r?\n/)
1130
+ .map((line) => {
1131
+ const escaped = escapeHtml(line);
1132
+ if (/^\+(?!\+\+)/.test(line)) return `<span class="diff-add">${escaped}</span>`;
1133
+ if (/^-(?!--)/.test(line)) return `<span class="diff-del">${escaped}</span>`;
1134
+ if (/^\+\+\+/.test(line)) return `<span class="diff-file-add">${escaped}</span>`;
1135
+ if (/^---/.test(line)) return `<span class="diff-file-del">${escaped}</span>`;
1136
+ if (/^@@/.test(line)) return `<span class="diff-hunk">${escaped}</span>`;
1137
+ return escaped;
1138
+ })
1139
+ .join("\n");
1140
+ }
1141
+
1126
1142
  function safeLinkHref(value) {
1127
1143
  const raw = String(value || "").trim();
1128
1144
  try {
package/public/styles.css CHANGED
@@ -481,6 +481,20 @@ button.danger {
481
481
  overflow-wrap: anywhere;
482
482
  }
483
483
 
484
+ .change-diff .diff-add,
485
+ .change-diff .diff-file-add {
486
+ color: #86efac;
487
+ }
488
+
489
+ .change-diff .diff-del,
490
+ .change-diff .diff-file-del {
491
+ color: #fca5a5;
492
+ }
493
+
494
+ .change-diff .diff-hunk {
495
+ color: #67e8f9;
496
+ }
497
+
484
498
  .sandbox-card h2 {
485
499
  margin: 0 0 4px;
486
500
  font-size: 1.05rem;
@@ -4,7 +4,7 @@ import fs from "node:fs/promises";
4
4
  import os from "node:os";
5
5
  import path from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
- import { buildPromptLayout, classifyEscapeAction, stripMarkdown } from "../src/interactive-cli.js";
7
+ import { buildPromptLayout, classifyEscapeAction, formatWorkspaceChange, stripMarkdown } from "../src/interactive-cli.js";
8
8
 
9
9
  const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
10
10
  const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-cli-chat-"));
@@ -85,6 +85,21 @@ try {
85
85
  if (!renderedDiff.includes("-old") || !renderedDiff.includes("+new")) {
86
86
  throw new Error("terminal markdown renderer dropped patch diff lines");
87
87
  }
88
+ const renderedPatchEvent = formatWorkspaceChange({
89
+ toolName: "apply_patch",
90
+ path: "test_cli.py",
91
+ beforeHash: "aaaaaaaa11111111",
92
+ afterHash: "bbbbbbbb22222222",
93
+ diff: ["--- a/test_cli.py", "+++ b/test_cli.py", "@@ line 10 @@", "-old line", "+new line"].join("\n"),
94
+ });
95
+ if (
96
+ renderedPatchEvent.label !== "patch" ||
97
+ !renderedPatchEvent.summary.includes("test_cli.py") ||
98
+ !renderedPatchEvent.lines.join("\n").includes("-old line") ||
99
+ !renderedPatchEvent.lines.join("\n").includes("+new line")
100
+ ) {
101
+ throw new Error("workspace patch event formatter did not preserve red/green diff content");
102
+ }
88
103
 
89
104
  const promptLayout = buildPromptLayout(`${"x".repeat(180)}\nsecond line`, 95, 80, 24);
90
105
  const promptText = promptLayout.renderedRows
@@ -185,7 +200,27 @@ try {
185
200
  {
186
201
  ok: true,
187
202
  projectRoot: tempRoot,
188
- checks: ["markdown-render", "markdown-table-no-duplicate", "patch-diff-render", "prompt-layout", "user-prompt-label", "escape-policy", "live-input-status-layout", "agent-response-gutter", "aginti-md", "instructions-command", "skills-command", "slash-prefix-autoselect", "instructions-chat-edit", "interactive-chat", "mock-file-write", "run-status", "resume-latest", "resume-history-full"],
203
+ checks: [
204
+ "markdown-render",
205
+ "markdown-table-no-duplicate",
206
+ "patch-diff-render",
207
+ "workspace-patch-event-render",
208
+ "prompt-layout",
209
+ "user-prompt-label",
210
+ "escape-policy",
211
+ "live-input-status-layout",
212
+ "agent-response-gutter",
213
+ "aginti-md",
214
+ "instructions-command",
215
+ "skills-command",
216
+ "slash-prefix-autoselect",
217
+ "instructions-chat-edit",
218
+ "interactive-chat",
219
+ "mock-file-write",
220
+ "run-status",
221
+ "resume-latest",
222
+ "resume-history-full",
223
+ ],
189
224
  },
190
225
  null,
191
226
  2
@@ -107,7 +107,7 @@ function boxedLine(content, width) {
107
107
  function secretInputDisplay({ value, selectedPreview, placeholder }) {
108
108
  if (value) return color(`${"•".repeat(Math.min(value.length, 24))} (${value.length} chars)`, ansi.inputBg);
109
109
  if (selectedPreview) return `${color(` ${selectedPreview} `, ansi.selected, ansi.bold)} ${color("selected", ansi.yellow)}`;
110
- return color(placeholder || "paste key here", ansi.dim);
110
+ return color(String(placeholder || "").padEnd(30, " "), ansi.inputBg);
111
111
  }
112
112
 
113
113
  function renderSecretBox({ title, helpText, statusText, currentPreview, value, selected, actionText }) {
@@ -119,7 +119,7 @@ function renderSecretBox({ title, helpText, statusText, currentPreview, value, s
119
119
  boxedLine(color(compactLine(title, inner), ansi.bold, ansi.cyan), width),
120
120
  helpText ? boxedLine(`${color("key", ansi.muted)} ${compactLine(helpText, inner - 6)}`, width) : "",
121
121
  statusText ? boxedLine(`${color("status", ansi.muted)} ${compactLine(statusText, inner - 8)}`, width) : "",
122
- boxedLine(`${color("input", ansi.muted)} ${secretInputDisplay({ value, selectedPreview, placeholder: "hidden input" })}`, width),
122
+ boxedLine(`${color("input", ansi.muted)} ${secretInputDisplay({ value, selectedPreview })}`, width),
123
123
  boxedLine(color(compactLine(actionText, inner), ansi.dim), width),
124
124
  `${color("╰", ansi.border)}${color("─", ansi.border).repeat(width - 2)}${color("╯", ansi.border)}`,
125
125
  ].filter(Boolean);
@@ -356,6 +356,36 @@ function printAgentMessage(text) {
356
356
  for (const line of lines) outputLine(`${responsePrefix()}${line}`);
357
357
  }
358
358
 
359
+ export function formatWorkspaceChange(change = {}) {
360
+ const toolName = String(change.toolName || change.action || "change");
361
+ const path = String(change.path || "");
362
+ const summary = [
363
+ toolName,
364
+ path,
365
+ change.created ? "created" : "",
366
+ change.beforeHash ? `before=${String(change.beforeHash).slice(0, 8)}` : "before=new",
367
+ change.afterHash ? `after=${String(change.afterHash).slice(0, 8)}` : "",
368
+ ]
369
+ .filter(Boolean)
370
+ .join(" ");
371
+ const diff = String(change.diff || "").trim();
372
+ const renderedDiff = diff ? stripMarkdown(`Diff:\n${diff}`).split("\n") : [];
373
+ return {
374
+ label: toolName === "apply_patch" || toolName.startsWith("apply_patch") ? "patch" : "write",
375
+ summary,
376
+ lines: renderedDiff,
377
+ };
378
+ }
379
+
380
+ function printWorkspaceChange(change = {}) {
381
+ if (!change?.diff) return;
382
+ const formatted = formatWorkspaceChange(change);
383
+ const bg = formatted.label === "patch" ? ansi.magenta : ansi.systemBg;
384
+ outputLine(`${label(formatted.label, bg)} ${compactLine(formatted.summary, 92)}`);
385
+ const gutter = `${color(" | ", bg)} `;
386
+ for (const line of formatted.lines) outputLine(`${gutter}${line}`);
387
+ }
388
+
359
389
  function printPreviewBlock(role, text, { time = "", bg = ansi.systemBg, maxLines = 5 } = {}) {
360
390
  const header = [label(role, bg).trimEnd(), time ? color(time, ansi.dim) : ""].filter(Boolean).join(" ");
361
391
  outputLine(header);
@@ -1795,6 +1825,8 @@ async function runPrompt(prompt, state, packageDir) {
1795
1825
  printStatusEvent(state, "tool", data.toolName || "unknown");
1796
1826
  } else if (type === "tool.completed") {
1797
1827
  printStatusEvent(state, "tool_done", data.toolName || "unknown");
1828
+ } else if (type === "file.changed") {
1829
+ printWorkspaceChange(data);
1798
1830
  } else if (type === "tool.blocked") {
1799
1831
  printStatusEvent(state, "tool_blocked", data.toolName || data.reason || "unknown");
1800
1832
  } else if (type === "loop.guard") {