@lazyingart/agintiflow 0.12.6 → 0.12.7

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/README.md CHANGED
@@ -67,7 +67,7 @@ aginti
67
67
  aginti chat
68
68
  ```
69
69
 
70
- Inside chat, type normal requests such as `write a small Python CLI app with tests`. The default is Docker workspace mode with approved package installs, so coding, plotting, and LaTeX tasks can set up project-local tools without touching the host. Use `/help` for commands, `/login` or `/auth` to paste a provider key, `/instructions` to inspect `AGINTI.md`, `/latex on` for PDF work, `/docker off` only when you intentionally want host mode, `/sessions` to list project runs, and `/resume latest` or `/resume <session-id>` to continue work. Type `/` then Tab for command completion. `Ctrl+J` inserts a new line in the colored input panel, Enter sends, arrow keys move through wrapped multiline input, and `Ctrl+A`/`Ctrl+E` jump to the current line start/end. During an active run, Enter sends the draft as an ASAP pipe message (`→`) and Tab queues it for after the run (`↳`); ASAP messages are consumed first, Alt+Up edits the last piped message, and Shift+Left edits the last after-finish queued message. The input panel always shows the current `cwd` footer. Assistant responses start on a fresh line after the `aginti>` header with a colored response gutter and render common Markdown, including headings, inline code, bold text, lists, quotes, code fences, and tables. Resuming a session prints a compact recent-chat preview before the prompt. Esc or Ctrl+C stops the active run cleanly and prints the resume command.
70
+ Inside chat, type normal requests such as `write a small Python CLI app with tests`. The default is Docker workspace mode with approved package installs, so coding, plotting, and LaTeX tasks can set up project-local tools without touching the host. Use `/help` for commands, `/login` or `/auth` to paste a provider key, `/instructions` to inspect `AGINTI.md`, `/latex on` for PDF work, `/docker off` only when you intentionally want host mode, `/sessions` to list project runs, and `/resume latest` or `/resume <session-id>` to continue work. Type `/` then Tab for command completion. `Ctrl+J` inserts a new line in the colored input panel, Enter sends, arrow keys move through wrapped multiline input, and `Ctrl+A`/`Ctrl+E` jump to the current line start/end. During an active run, Enter sends the draft as an ASAP pipe message (`→`) and Tab queues it for after the run (`↳`); ASAP messages are consumed first, Alt+Up edits the last piped message, and Shift+Left edits the last after-finish queued message. The input panel always shows the current `cwd` footer and a single live status row, so long goals and tool updates are compacted instead of flooding the transcript. Assistant responses start on a fresh line after the `aginti>` header with a colored response gutter and render common Markdown, including headings, inline code, bold text, lists, quotes, code fences, tables, and red/green patch diff lines. Resuming a session prints a compact recent-chat preview before the prompt. Esc or Ctrl+C stops the active run cleanly and prints the resume command.
71
71
 
72
72
  `aginti init` creates `AGINTI.md` at the project root. This is the editable project-instruction file for both CLI and web runs, similar to `AGENTS.md` or project memory in other agents. Keep durable preferences, commands, and constraints there, but never secrets. You can edit it manually or ask in chat, for example: `update AGINTI.md to remember that this project uses pytest and npm run check`.
73
73
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyingart/agintiflow",
3
- "version": "0.12.6",
3
+ "version": "0.12.7",
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",
@@ -69,6 +69,22 @@ try {
69
69
  if (!renderedMarkdown.includes("Check") || !renderedMarkdown.includes("Present")) {
70
70
  throw new Error("terminal markdown renderer dropped table content");
71
71
  }
72
+ if ((renderedMarkdown.match(/Present/g) || []).length !== 1) {
73
+ throw new Error("terminal markdown renderer duplicated table rows");
74
+ }
75
+ const renderedDiff = stripMarkdown(
76
+ [
77
+ "Diff:",
78
+ "--- a/example.txt",
79
+ "+++ b/example.txt",
80
+ "@@ line 1 @@",
81
+ "-old",
82
+ "+new",
83
+ ].join("\n")
84
+ );
85
+ if (!renderedDiff.includes("-old") || !renderedDiff.includes("+new")) {
86
+ throw new Error("terminal markdown renderer dropped patch diff lines");
87
+ }
72
88
 
73
89
  const promptLayout = buildPromptLayout(`${"x".repeat(180)}\nsecond line`, 95, 80, 24);
74
90
  const visibleLengths = promptLayout.renderedRows.map((line) =>
@@ -86,13 +102,14 @@ try {
86
102
  }
87
103
  const queuedPromptLayout = buildPromptLayout("follow up", 9, 90, 24, {
88
104
  commandCwd: "/tmp/aginti-project",
105
+ statusLine: "running · tool: apply_patch with a very long request that should be compacted in the panel",
89
106
  pendingAsap: [{ content: "apply this to the running task" }],
90
107
  pendingQueued: [{ content: "run this after the current task" }],
91
108
  });
92
109
  const queuedText = queuedPromptLayout.renderedRows
93
110
  .map((line) => line.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, ""))
94
111
  .join("\n");
95
- if (!queuedText.includes("→ apply this") || !queuedText.includes("↳ run this") || !queuedText.includes("cwd /tmp/aginti-project")) {
112
+ if (!queuedText.includes("run running · tool: apply_patch") || !queuedText.includes("→ apply this") || !queuedText.includes("↳ run this") || !queuedText.includes("cwd /tmp/aginti-project")) {
96
113
  throw new Error("terminal prompt layout did not render live input queue and cwd footer");
97
114
  }
98
115
 
@@ -119,8 +136,8 @@ try {
119
136
  if (!result.stdout.includes("Interactive agent chat")) {
120
137
  throw new Error("interactive chat did not print its banner");
121
138
  }
122
- if (!result.stdout.includes("status=running workingOn=") || !result.stdout.includes("status=idle session=")) {
123
- throw new Error("interactive chat did not print simple run status updates");
139
+ if (!result.stdout.includes("status=idle session=")) {
140
+ throw new Error("interactive chat did not print final run status");
124
141
  }
125
142
  if (!/aginti>\s*\r?\n\s*\|\s+Mock run complete\./.test(result.stdout)) {
126
143
  throw new Error("assistant response did not render with a fresh-line response gutter");
@@ -139,7 +156,7 @@ try {
139
156
  {
140
157
  ok: true,
141
158
  projectRoot: tempRoot,
142
- checks: ["markdown-render", "prompt-layout", "live-input-layout", "agent-response-gutter", "aginti-md", "instructions-command", "instructions-chat-edit", "interactive-chat", "mock-file-write", "run-status", "resume-latest", "resume-history-preview"],
159
+ checks: ["markdown-render", "markdown-table-no-duplicate", "patch-diff-render", "prompt-layout", "live-input-status-layout", "agent-response-gutter", "aginti-md", "instructions-command", "instructions-chat-edit", "interactive-chat", "mock-file-write", "run-status", "resume-latest", "resume-history-preview"],
143
160
  },
144
161
  null,
145
162
  2
@@ -28,6 +28,7 @@ const ansi = {
28
28
  userBg: "\x1b[48;5;24m\x1b[38;5;231m",
29
29
  agentBg: "\x1b[48;5;29m\x1b[38;5;231m",
30
30
  responseBg: "\x1b[48;5;25m\x1b[38;5;231m",
31
+ statusBg: "\x1b[48;5;23m\x1b[38;5;231m",
31
32
  systemBg: "\x1b[48;5;236m\x1b[38;5;245m",
32
33
  };
33
34
  const brandPalette = ["\x1b[38;5;45m", "\x1b[38;5;81m", "\x1b[38;5;86m", "\x1b[38;5;118m", "\x1b[38;5;226m"];
@@ -152,6 +153,7 @@ function compactLine(value = "", limit = 96) {
152
153
  export function stripMarkdown(text) {
153
154
  const lines = String(text || "").split(/\r?\n/);
154
155
  let inFence = false;
156
+ let diffContext = 0;
155
157
  const rendered = [];
156
158
 
157
159
  for (let index = 0; index < lines.length; index += 1) {
@@ -166,6 +168,15 @@ export function stripMarkdown(text) {
166
168
  continue;
167
169
  }
168
170
 
171
+ if (/^\s*Diff:\s*$/i.test(line)) diffContext = 80;
172
+ const patchLine = renderPatchLine(line, { active: diffContext > 0 || inFence });
173
+ if (patchLine) {
174
+ rendered.push(patchLine);
175
+ diffContext = 80;
176
+ continue;
177
+ }
178
+ if (diffContext > 0) diffContext -= 1;
179
+
169
180
  if (!inFence) {
170
181
  if (/^\s*[-*_]{3,}\s*$/.test(line)) {
171
182
  rendered.push(color("-".repeat(42), ansi.dim));
@@ -203,6 +214,17 @@ export function stripMarkdown(text) {
203
214
  return rendered.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd();
204
215
  }
205
216
 
217
+ function renderPatchLine(line = "", { active = false } = {}) {
218
+ const value = String(line || "");
219
+ if (/^diff --git\s+/.test(value)) return color(value, ansi.bold, ansi.cyan);
220
+ if (/^@@\s+/.test(value)) return color(value, ansi.cyan);
221
+ if (/^---\s+a\//.test(value)) return color(value, ansi.red);
222
+ if (/^\+\+\+\s+b\//.test(value)) return color(value, ansi.green);
223
+ if (active && /^\+(?!\+\+)/.test(value)) return color(value, ansi.green);
224
+ if (active && /^-(?!--)/.test(value)) return color(value, ansi.red);
225
+ return "";
226
+ }
227
+
206
228
  function splitMarkdownTableRow(line = "") {
207
229
  const trimmed = String(line || "").trim();
208
230
  if (!trimmed.includes("|")) return null;
@@ -519,6 +541,11 @@ export function buildPromptLayout(buffer = "", cursor = 0, width = terminalWidth
519
541
  const renderedRows = [];
520
542
  let renderedCursorRow = cursorRow - visible.start;
521
543
 
544
+ if (options.statusLine) {
545
+ renderedRows.push(panelLine(` run ${compactLine(options.statusLine, lineWidth - 8)}`, ansi.statusBg, lineWidth));
546
+ renderedCursorRow += 1;
547
+ }
548
+
522
549
  const pendingAsap = Array.isArray(options.pendingAsap) ? options.pendingAsap : [];
523
550
  const pendingQueued = Array.isArray(options.pendingQueued) ? options.pendingQueued : [];
524
551
  for (const item of pendingAsap.slice(-4)) {
@@ -887,6 +914,7 @@ class LiveRunInput {
887
914
  this.preferredColumn = null;
888
915
  this.pendingAsap = [];
889
916
  this.pendingQueued = [];
917
+ this.statusLine = "";
890
918
  this.wasRaw = Boolean(input.isRaw);
891
919
  this.started = false;
892
920
  this.handler = this.handleKey.bind(this);
@@ -952,6 +980,7 @@ class LiveRunInput {
952
980
  }
953
981
  this.rendered = renderPromptBuffer(this.buffer, this.cursor, this.rendered, {
954
982
  commandCwd: this.commandCwd,
983
+ statusLine: this.statusLine,
955
984
  pendingAsap: this.pendingAsap,
956
985
  pendingQueued: this.pendingQueued,
957
986
  });
@@ -972,9 +1001,17 @@ class LiveRunInput {
972
1001
  this.redraw();
973
1002
  }
974
1003
 
1004
+ setStatus(value = "") {
1005
+ const nextStatus = compactLine(value, Math.max(terminalWidth() - 16, 36));
1006
+ if (this.statusLine === nextStatus) return;
1007
+ this.statusLine = nextStatus;
1008
+ this.redraw();
1009
+ }
1010
+
975
1011
  moveVertical(direction) {
976
1012
  const layout = buildPromptLayout(this.buffer, this.cursor, terminalWidth(), terminalHeight(), {
977
1013
  commandCwd: this.commandCwd,
1014
+ statusLine: this.statusLine,
978
1015
  pendingAsap: this.pendingAsap,
979
1016
  pendingQueued: this.pendingQueued,
980
1017
  });
@@ -1215,8 +1252,14 @@ async function printResumeHistory(state, { limit = 8 } = {}) {
1215
1252
  }
1216
1253
 
1217
1254
  function printStatusEvent(state, label, details = "") {
1218
- state.lastEvent = details ? `${label}: ${details}` : label;
1219
- printSystemLine(`status=${state.status || "running"} ${state.lastEvent}`);
1255
+ const safeDetails = compactLine(details, 72);
1256
+ state.lastEvent = safeDetails ? `${label}: ${safeDetails}` : label;
1257
+ const statusText = `${state.status || "running"} · ${state.lastEvent}`;
1258
+ if (activeRunInput) {
1259
+ activeRunInput.setStatus(statusText);
1260
+ return;
1261
+ }
1262
+ printSystemLine(`status=${statusText}`);
1220
1263
  }
1221
1264
 
1222
1265
  function attachRunInterrupts(controller) {
@@ -1595,15 +1638,19 @@ async function runPrompt(prompt, state, packageDir) {
1595
1638
 
1596
1639
  state.sessionId = config.resume || config.sessionId || state.sessionId;
1597
1640
  state.status = "running";
1598
- state.activeGoal = prompt.replace(/\s+/g, " ").slice(0, 120);
1641
+ state.activeGoal = compactLine(prompt, 84);
1599
1642
  state.lastEvent = "";
1600
- printSystemLine(`session=${state.sessionId}`);
1601
- printSystemLine(`status=running workingOn=${state.activeGoal}`);
1602
1643
 
1603
1644
  const store = new SessionStore(config.sessionsDir, state.sessionId);
1604
1645
  const liveInput = new LiveRunInput({ state, store, controller });
1605
1646
  const liveStarted = liveInput.start();
1606
1647
  const detachInterrupts = liveStarted ? () => {} : attachRunInterrupts(controller);
1648
+ if (liveStarted) {
1649
+ liveInput.setStatus(`running · ${state.activeGoal}`);
1650
+ } else {
1651
+ printSystemLine(`session=${state.sessionId}`);
1652
+ printSystemLine(`status=running workingOn=${state.activeGoal}`);
1653
+ }
1607
1654
  let result;
1608
1655
  let queuedAfterFinish = [];
1609
1656
  try {
@@ -1619,6 +1666,9 @@ async function runPrompt(prompt, state, packageDir) {
1619
1666
  printHeading(text);
1620
1667
  } else if (options.error) {
1621
1668
  outputLine(`${label("error", ansi.systemBg)} ${stripMarkdown(text)}`);
1669
+ } else if (options.kind === "meta" && liveStarted) {
1670
+ const trimmed = String(text || "").trim();
1671
+ if (trimmed) liveInput.setStatus(trimmed);
1622
1672
  } else {
1623
1673
  printSystemLine(text);
1624
1674
  }