Skip to content

feat: Agent Teams TUI dashboard and message delivery on exit#23

Merged
Joao208 merged 2 commits into
mainfrom
joaobarros-/-agent-teams-ui-and-message-delivery
Mar 31, 2026
Merged

feat: Agent Teams TUI dashboard and message delivery on exit#23
Joao208 merged 2 commits into
mainfrom
joaobarros-/-agent-teams-ui-and-message-delivery

Conversation

@Joao208

@Joao208 Joao208 commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

What

TUI dashboard para visualizar agent teams em tempo real e entrega automática de mensagens pendentes quando um teammate termina a execução.

Changes

agent-teams-lead (spawner)

  • Quando o processo de um teammate termina (exit), lê messages.json e loga todas as mensagens pendentes no team.log
  • wait_for_team agora retorna pending_messages com mensagens direcionadas ao lead que ainda não foram lidas (tanto no sucesso quanto no timeout)

agent-teams-ui (novo package)

TUI interativa com Ink (React no terminal) com 4 tabs:

  • Team: teammates com status, contagem de tasks [done/total], task atual com duração
  • Board: kanban com colunas (Pending | In Progress | Blocked | Done), seleção com j/k, detail view com Enter mostrando description, criteria, summary, files, notes
  • Messages: estilo grupo WhatsApp com separadores de data, agrupamento por remetente, kind tags
  • Chat: stdout/stderr dos agentes como conversa, filtro por agente com f, scroll com j/k, g/G top/bottom

Header com:

  • Timer ao vivo
  • Progress bar [done/total tasks X%]
  • Indicadores de atividade nova por tab (*)

Auto-refresh via chokidar watching .agent-teams/.

How to test

cd packages/agent-teams-ui
npx tsx scripts/seed.ts
npx tsx src/index.tsx __demo__

Summary by CodeRabbit

  • New Features
    • Introduced a terminal UI application for monitoring agent teams, displaying team status, task management, messages, and activity logs in real-time
    • Added automatic refresh monitoring as workspace state changes
    • Enhanced message delivery to surface pending messages to team leads and departing members

@coderabbitai

coderabbitai Bot commented Mar 31, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@Joao208 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 5 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 2 minutes and 5 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3741ce93-b0a5-42b9-b31d-e30ec48c9918

📥 Commits

Reviewing files that changed from the base of the PR and between a4f5867 and 16c9121.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (16)
  • README.md
  • packages/agent-teams-lead/src/spawner.ts
  • packages/agent-teams-lead/src/store.ts
  • packages/agent-teams-lead/src/tools.ts
  • packages/agent-teams-ui/README.md
  • packages/agent-teams-ui/package.json
  • packages/agent-teams-ui/src/App.tsx
  • packages/agent-teams-ui/src/components/ChatView.tsx
  • packages/agent-teams-ui/src/components/Header.tsx
  • packages/agent-teams-ui/src/components/MessagesView.tsx
  • packages/agent-teams-ui/src/components/TasksView.tsx
  • packages/agent-teams-ui/src/components/TeamView.tsx
  • packages/agent-teams-ui/src/index.tsx
  • packages/agent-teams-ui/src/store.ts
  • packages/agent-teams-ui/src/theme.ts
  • packages/agent-teams-ui/tsconfig.json
📝 Walkthrough

Walkthrough

This PR introduces a message delivery mechanism in the spawner to handle pending messages when teammates exit, updates the tools to report unread messages directed to "lead", and adds a new terminal UI package with components for displaying team state, tasks, messages, and activity logs.

Changes

Cohort / File(s) Summary
Pending Message Delivery
packages/agent-teams-lead/src/spawner.ts, packages/agent-teams-lead/src/tools.ts
Added deliverPendingMessages() method to read and filter messages from .agent-teams/messages.json on teammate exit; updated waitForTeam tool to fetch and include unread messages directed to "lead" in results.
UI Package Configuration
packages/agent-teams-ui/package.json, packages/agent-teams-ui/tsconfig.json
Created package manifest with ESM/TypeScript build configuration, CLI binary entry point, and dependencies (ink, react, chokidar, tsx).
UI Core & State
packages/agent-teams-ui/src/index.tsx, packages/agent-teams-ui/src/App.tsx, packages/agent-teams-ui/src/store.ts, packages/agent-teams-ui/src/theme.ts
Added executable entrypoint, main App component with state loading and file watching, data models (Teammate, Team, Task, Message, Artifact, TeamState), and theme constants.
UI Components
packages/agent-teams-ui/src/components/Header.tsx, packages/agent-teams-ui/src/components/TeamView.tsx, packages/agent-teams-ui/src/components/TasksView.tsx, packages/agent-teams-ui/src/components/MessagesView.tsx, packages/agent-teams-ui/src/components/ChatView.tsx
Added tab-based navigation header with progress tracking, team member status view, kanban-style task grid with detail modal, message display with sender grouping, and scrollable activity log with source-based coloring and filtering.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Poem

🐰 Pending messages hop through the door,
A UI blooms with tabs galore—
Teams dance, tasks flow, chat logs glow,
Watched files trigger the UI show!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the two main objectives: introducing the Agent Teams TUI dashboard and implementing message delivery on process exit.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch joaobarros-/-agent-teams-ui-and-message-delivery

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (7)
packages/agent-teams-ui/src/store.ts (1)

73-75: Avoid fully silent fallback on read/parse failures.

Right now malformed files are indistinguishable from “no data”, which makes triage hard. Consider logging a short warning (stderr is enough) before returning fallback.

Proposed adjustment
 async function readJson<T>(filePath: string, fallback: T): Promise<T> {
   if (!existsSync(filePath)) return fallback;
   try {
     const raw = await readFile(filePath, "utf-8");
     return JSON.parse(raw) as T;
-  } catch {
+  } catch (error) {
+    process.stderr.write(
+      `[agent-teams-ui] failed to read/parse ${filePath}: ${String(error)}\n`
+    );
     return fallback;
   }
 }

 async function readLog(filePath: string): Promise<string[]> {
   if (!existsSync(filePath)) return [];
   try {
     const raw = await readFile(filePath, "utf-8");
     return raw.split("\n").filter(Boolean);
-  } catch {
+  } catch (error) {
+    process.stderr.write(
+      `[agent-teams-ui] failed to read ${filePath}: ${String(error)}\n`
+    );
     return [];
   }
 }

Also applies to: 83-85

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/agent-teams-ui/src/store.ts` around lines 73 - 75, The catch blocks
that simply "return fallback" swallow parse/read errors and make failures
indistinguishable from missing data; update those catch handlers (the ones
returning the fallback variable) to log a short warning to stderr (e.g.,
console.warn or process.stderr.write) that includes a brief context message and
the caught error before returning fallback so malformed files are visible during
triage.
packages/agent-teams-ui/src/components/ChatView.tsx (2)

83-84: g/G key bindings appear inverted from vim conventions.

In vim, g (or gg) goes to the top and G goes to the bottom. Here, g sets scrollOffset to max (showing oldest entries at top), and G sets it to 0 (showing newest at bottom). If the intent is to show "top of log" vs "bottom of log", consider whether users expect vim-style navigation.

If intentional (newest = bottom = G), a brief comment would help clarify.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/agent-teams-ui/src/components/ChatView.tsx` around lines 83 - 84,
The keybindings in ChatView.tsx are inverted versus vim: change the handlers so
pressing "g" moves to the top (setScrollOffset(0)) and pressing "G" moves to the
bottom (setScrollOffset(Math.max(0, filtered.length - viewHeight))); update the
two lines that currently call setScrollOffset for input === "g" and input ===
"G" accordingly (or, if the current behavior was intentional, replace with an
inline comment next to the setScrollOffset calls explaining the non-vim
convention to avoid confusion).

97-120: Mutation during render works but is non-idiomatic.

The lastSource variable is mutated during the map() call (line 120). While this works because rendering is synchronous, it can confuse readers expecting functional-style React. Consider pre-computing a grouped structure if readability becomes a concern, but this is fine for now.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/agent-teams-ui/src/components/ChatView.tsx` around lines 97 - 120,
The render mutates lastSource during visible.map (lastSource variable) which is
non-idiomatic; remove lastSource and compute showName functionally by comparing
the current entry with the previous one (e.g., use the map index: showName = i
=== 0 || visible[i - 1].source !== entry.source) inside the visible.map
callback, keeping getAgentColor(entry.source, agentColors) usage unchanged (or
alternatively precompute a grouped/partitioned structure before rendering if you
prefer grouping).
packages/agent-teams-ui/src/App.tsx (3)

71-78: File watcher cleanup may not complete before unmount.

watcher.close() returns a Promise<void> in chokidar 4.x, but the cleanup function returns void. While React doesn't await cleanup functions, the watcher may not fully close before the component unmounts in fast remount scenarios.

Consider handling this explicitly if cleanup timing matters:

♻️ Optional: explicit async cleanup pattern
   return () => { watcher.close(); };
+  // Note: watcher.close() is async in chokidar 4.x
+  // For strict cleanup, store watcher in ref and handle in separate effect
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/agent-teams-ui/src/App.tsx` around lines 71 - 78, The cleanup
currently calls watcher.close() synchronously in the useEffect return but
chokidar's watcher.close() returns a Promise<void>, so the watcher may not
finish closing before unmount; update the useEffect cleanup to call
watcher.close() and handle the returned Promise (e.g., call
watcher.close().catch(() => {}) or create an async IIFE to await
watcher.close()) to ensure errors are swallowed/logged and the close promise is
handled; locate the useEffect, watcher variable and the watch(...) call in
App.tsx (references: useEffect, watcher, watch, refresh, workspacePath, join)
and modify the cleanup to handle the Promise returned by watcher.close().

91-98: Arrow key navigation restricted on certain tabs.

The arrow key tab switching is disabled when on tasks or chat tabs (lines 91, 95). This is presumably to avoid conflicts with in-tab navigation (j/k scrolling). Consider adding a comment to clarify this intentional UX decision for future maintainers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/agent-teams-ui/src/App.tsx` around lines 91 - 98, The arrow-key
handling in the keyboard shortcut block (checking key.tab, key.rightArrow,
key.leftArrow, using TABS, tab, and switchTab) deliberately skips switching when
tab === "tasks" or tab === "chat" to avoid conflicting with in-tab j/k
scrolling; add a concise inline comment above both conditional checks explaining
this UX decision so future maintainers understand the intentional restriction
and don't remove it accidentally.

49-52: Task status comparison relies on index-based matching.

Line 50 compares prev.tasks[i]?.status !== t.status using array indices. If tasks are reordered between refreshes, this would detect false-positive status changes. For activity indicators this is acceptable, but for precise change detection, consider comparing by task ID.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/agent-teams-ui/src/App.tsx` around lines 49 - 52, The current
comparison in next.tasks.some((t, i) => prev.tasks[i]?.status !== t.status) uses
index-based matching and can false-positive when tasks are reordered; change it
to match tasks by a stable identifier (e.g., task.id) before comparing statuses:
build a lookup for prev.tasks keyed by id (or use find by id) and then set
activity.team = true if any next task's status differs from the prev task with
the same id. Update the logic around next.tasks.some, prev.tasks, and
activity.team to use the id-based comparison.
packages/agent-teams-lead/src/spawner.ts (1)

136-141: Fire-and-forget async call in synchronous event handler.

deliverPendingMessages is an async method but is called without await in the exit handler. This means any errors would be unhandled promise rejections (though the method has its own try/catch). Additionally, the process cleanup on lines 139-140 proceeds immediately without waiting for message delivery to complete.

If message delivery should complete before cleanup, consider awaiting. Otherwise, the current pattern is acceptable since errors are already caught internally.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/agent-teams-lead/src/spawner.ts` around lines 136 - 141, The exit
handler on proc (proc.on("exit", ...)) calls the async method
deliverPendingMessages(teammate.id, teammate.name) without awaiting it, so
cleanup (this.processes.delete and this.cleanupAgentConfig) runs immediately;
decide to await delivery if it must finish before cleanup by turning the handler
into an async flow (or chain the promise) and await deliverPendingMessages
before calling this.processes.delete(teammate.id) and
this.cleanupAgentConfig(configPath); otherwise, explicitly document the
fire-and-forget intent and keep current ordering. Ensure you reference
deliverPendingMessages, this.processes.delete, and this.cleanupAgentConfig in
the change so the message delivery completes (or is intentionally non-blocking)
prior to cleanup.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/agent-teams-lead/src/tools.ts`:
- Around line 213-216: The pending_messages returned by waitForTeam are never
acknowledged, so leadMessages from this.store.getMessages({ to: "lead",
unread_by: "lead" }) will be returned repeatedly; update waitForTeam to
acknowledge those messages after including them in pending_messages by calling
the store's message-acknowledgement API (e.g., markMessageRead /
markMessagesRead / updateMessage to clear unread_by for each message id) on the
leadMessages array (or their ids) so subsequent calls won't return the same
messages; ensure the acknowledgement runs only after messages are successfully
included in the response and handle any errors from the store acknowledgement
path.

In `@packages/agent-teams-ui/package.json`:
- Around line 7-9: The package's CLI binary "agent-teams-ui" points to
"./dist/index.js" but the TypeScript compile step removes the shebang from
src/index.tsx, breaking global installs; update the package.json "build" script
to preserve the shebang after tsc (for example, add a post-build step to prepend
"#!/usr/bin/env node" to dist/index.js) or switch the build pipeline to a
bundler (e.g., esbuild) configured to preserve shebangs so that the generated
dist/index.js retains the interpreter line.

In `@packages/agent-teams-ui/src/components/Header.tsx`:
- Around line 40-41: The progress bar width calculation can produce negative
values when width < 40; update the logic around barW and filled to clamp
negative sizes to zero: compute an available width as Math.max(0, width - 40)
and then set barW = Math.min(20, available) so barW is never negative, then
recompute filled = Math.round((barW * pct) / 100) and ensure filled is bounded
between 0 and barW before using symbols.line.repeat(filled); change references
in this block (barW, filled, width, pct, symbols.line.repeat) accordingly.

In `@packages/agent-teams-ui/src/components/MessagesView.tsx`:
- Around line 73-76: The sender grouping isn't reset at date boundaries because
showName is only comparing msg.from_name to lastFrom; update the showName
calculation in MessagesView (the variable showName that uses msg.from_name and
lastFrom) to also consider showDate so that the first message of a new date
always shows the sender label (e.g., set showName to true when msg.from_name !==
lastFrom OR showDate), then continue to update lastFrom as before.

In `@packages/agent-teams-ui/src/components/TasksView.tsx`:
- Around line 89-90: The column width calculation can produce a negative value
(causing symbols.line.repeat to throw) when the terminal is very narrow; update
the computation of colWidth (derived from totalWidth, COLUMNS and
stdout?.columns) to clamp to a non-negative value (e.g., use Math.max(0,
Math.floor(...)) or ensure totalWidth >= required minimum) and also guard any
use of symbols.line.repeat by passing Math.max(0, colWidth) so repeats never
receive a negative count; change references to totalWidth, colWidth, COLUMNS and
the repeat call to use this clamped value.
- Around line 96-101: The allTasks aggregation in TasksView.tsx is built in the
wrong order (in_progress -> pending -> blocked -> completed) which mismatches
the rendered columns (pending -> in_progress -> blocked -> completed); update
the allTasks construction (and the other similar allTasks occurrence) to concat
filters in the UI order: pending, in_progress, blocked, completed so
highlight/traversal aligns with the grid rendering (reference the allTasks
variable in the TasksView component).
- Around line 111-113: The down-arrow handler can set selectedIdx negative when
allTasks is empty because allTasks.length - 1 is -1; update the handler in the
block that checks key.downArrow || input === "j" to guard or clamp using the
non-negative max index (e.g., compute maxIndex = Math.max(0, allTasks.length -
1) or early-return when allTasks.length === 0) and then call setSelectedIdx(i =>
Math.min(maxIndex, i + 1)); reference the variables and functions:
key.downArrow, input === "j", allTasks, maxIndex (new), and setSelectedIdx to
locate where to change.

In `@packages/agent-teams-ui/src/components/TeamView.tsx`:
- Around line 62-65: The "waiting for tasks" message is shown too broadly;
change the JSX condition that renders the Box (currently using !current &&
doneCount === 0) to also require totalAssigned === 0 so only teammates with no
assigned tasks show "waiting for tasks"; locate the conditional around the
Box/Text rendering in TeamView.tsx and update it to check current, doneCount,
and totalAssigned (use the totalAssigned variable used elsewhere in this
component) before rendering that message.

In `@packages/agent-teams-ui/src/index.tsx`:
- Line 11: The catch on waitUntilExit() currently swallows errors by calling
process.exit(0); change it to surface failures by logging the error and exiting
with a non-zero code or rethrowing: update the promise rejection handler for
waitUntilExit() to capture the error (e.g., err) and call process.exit(1) after
logging (or rethrow) instead of exiting with 0 so startup/runtime failures are
visible to CI; locate the waitUntilExit() call and the process.exit usage in
index.tsx to make this change.

---

Nitpick comments:
In `@packages/agent-teams-lead/src/spawner.ts`:
- Around line 136-141: The exit handler on proc (proc.on("exit", ...)) calls the
async method deliverPendingMessages(teammate.id, teammate.name) without awaiting
it, so cleanup (this.processes.delete and this.cleanupAgentConfig) runs
immediately; decide to await delivery if it must finish before cleanup by
turning the handler into an async flow (or chain the promise) and await
deliverPendingMessages before calling this.processes.delete(teammate.id) and
this.cleanupAgentConfig(configPath); otherwise, explicitly document the
fire-and-forget intent and keep current ordering. Ensure you reference
deliverPendingMessages, this.processes.delete, and this.cleanupAgentConfig in
the change so the message delivery completes (or is intentionally non-blocking)
prior to cleanup.

In `@packages/agent-teams-ui/src/App.tsx`:
- Around line 71-78: The cleanup currently calls watcher.close() synchronously
in the useEffect return but chokidar's watcher.close() returns a Promise<void>,
so the watcher may not finish closing before unmount; update the useEffect
cleanup to call watcher.close() and handle the returned Promise (e.g., call
watcher.close().catch(() => {}) or create an async IIFE to await
watcher.close()) to ensure errors are swallowed/logged and the close promise is
handled; locate the useEffect, watcher variable and the watch(...) call in
App.tsx (references: useEffect, watcher, watch, refresh, workspacePath, join)
and modify the cleanup to handle the Promise returned by watcher.close().
- Around line 91-98: The arrow-key handling in the keyboard shortcut block
(checking key.tab, key.rightArrow, key.leftArrow, using TABS, tab, and
switchTab) deliberately skips switching when tab === "tasks" or tab === "chat"
to avoid conflicting with in-tab j/k scrolling; add a concise inline comment
above both conditional checks explaining this UX decision so future maintainers
understand the intentional restriction and don't remove it accidentally.
- Around line 49-52: The current comparison in next.tasks.some((t, i) =>
prev.tasks[i]?.status !== t.status) uses index-based matching and can
false-positive when tasks are reordered; change it to match tasks by a stable
identifier (e.g., task.id) before comparing statuses: build a lookup for
prev.tasks keyed by id (or use find by id) and then set activity.team = true if
any next task's status differs from the prev task with the same id. Update the
logic around next.tasks.some, prev.tasks, and activity.team to use the id-based
comparison.

In `@packages/agent-teams-ui/src/components/ChatView.tsx`:
- Around line 83-84: The keybindings in ChatView.tsx are inverted versus vim:
change the handlers so pressing "g" moves to the top (setScrollOffset(0)) and
pressing "G" moves to the bottom (setScrollOffset(Math.max(0, filtered.length -
viewHeight))); update the two lines that currently call setScrollOffset for
input === "g" and input === "G" accordingly (or, if the current behavior was
intentional, replace with an inline comment next to the setScrollOffset calls
explaining the non-vim convention to avoid confusion).
- Around line 97-120: The render mutates lastSource during visible.map
(lastSource variable) which is non-idiomatic; remove lastSource and compute
showName functionally by comparing the current entry with the previous one
(e.g., use the map index: showName = i === 0 || visible[i - 1].source !==
entry.source) inside the visible.map callback, keeping
getAgentColor(entry.source, agentColors) usage unchanged (or alternatively
precompute a grouped/partitioned structure before rendering if you prefer
grouping).

In `@packages/agent-teams-ui/src/store.ts`:
- Around line 73-75: The catch blocks that simply "return fallback" swallow
parse/read errors and make failures indistinguishable from missing data; update
those catch handlers (the ones returning the fallback variable) to log a short
warning to stderr (e.g., console.warn or process.stderr.write) that includes a
brief context message and the caught error before returning fallback so
malformed files are visible during triage.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 44228c72-692b-467a-88f9-42bfc3392c48

📥 Commits

Reviewing files that changed from the base of the PR and between 1efee9b and e6151b9.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (14)
  • packages/agent-teams-lead/src/spawner.ts
  • packages/agent-teams-lead/src/tools.ts
  • packages/agent-teams-ui/package.json
  • packages/agent-teams-ui/scripts/seed.ts
  • packages/agent-teams-ui/src/App.tsx
  • packages/agent-teams-ui/src/components/ChatView.tsx
  • packages/agent-teams-ui/src/components/Header.tsx
  • packages/agent-teams-ui/src/components/MessagesView.tsx
  • packages/agent-teams-ui/src/components/TasksView.tsx
  • packages/agent-teams-ui/src/components/TeamView.tsx
  • packages/agent-teams-ui/src/index.tsx
  • packages/agent-teams-ui/src/store.ts
  • packages/agent-teams-ui/src/theme.ts
  • packages/agent-teams-ui/tsconfig.json

Comment thread packages/agent-teams-lead/src/tools.ts
Comment thread packages/agent-teams-ui/package.json
Comment on lines +40 to +41
const barW = Math.min(20, width - 40);
const filled = Math.round((barW * pct) / 100);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Progress bar width could be negative on very narrow terminals.

If width < 40, then barW becomes negative (e.g., Math.min(20, 30 - 40) = -10). This would cause filled to be negative and symbols.line.repeat(filled) to throw or produce empty output.

🛡️ Proposed fix
-  const barW = Math.min(20, width - 40);
+  const barW = Math.max(0, Math.min(20, width - 40));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const barW = Math.min(20, width - 40);
const filled = Math.round((barW * pct) / 100);
const barW = Math.max(0, Math.min(20, width - 40));
const filled = Math.round((barW * pct) / 100);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/agent-teams-ui/src/components/Header.tsx` around lines 40 - 41, The
progress bar width calculation can produce negative values when width < 40;
update the logic around barW and filled to clamp negative sizes to zero: compute
an available width as Math.max(0, width - 40) and then set barW = Math.min(20,
available) so barW is never negative, then recompute filled = Math.round((barW *
pct) / 100) and ensure filled is bounded between 0 and barW before using
symbols.line.repeat(filled); change references in this block (barW, filled,
width, pct, symbols.line.repeat) accordingly.

Comment thread packages/agent-teams-ui/src/components/MessagesView.tsx
Comment thread packages/agent-teams-ui/src/components/TasksView.tsx Outdated
Comment thread packages/agent-teams-ui/src/components/TasksView.tsx
Comment thread packages/agent-teams-ui/src/components/TasksView.tsx
Comment thread packages/agent-teams-ui/src/components/TeamView.tsx Outdated
Comment thread packages/agent-teams-ui/src/index.tsx Outdated
@Joao208 Joao208 force-pushed the joaobarros-/-agent-teams-ui-and-message-delivery branch from e6151b9 to a4f5867 Compare March 31, 2026 00:46
- Add TUI dashboard (Ink) with 4 tabs: Team, Board, Messages, Chat
- Board: kanban-style columns with task selection (j/k) and detail view (Enter)
- Messages: WhatsApp-style group chat with date separators and sender grouping
- Chat: agent stdout as conversation with filter by agent (f) and scroll (j/k)
- Header: live timer, progress bar, new activity indicators per tab
- Deliver pending messages to teammates on CLI exit (spawner)
- Include lead messages in wait_for_team response
- Add seed script for demo data
@Joao208 Joao208 force-pushed the joaobarros-/-agent-teams-ui-and-message-delivery branch from a4f5867 to eaea139 Compare March 31, 2026 00:48
- Ack lead messages after returning them in wait_for_team
- Add shebang preservation to build step
- Clamp progress bar and column widths for narrow terminals
- Reset sender grouping at date boundaries in messages
- Fix allTasks order to match rendered column order
- Guard selectedIdx against empty task list
- Fix 'waiting for tasks' condition to check totalAssigned
- Exit with code 1 on TUI errors
- Fix g/G keybindings to match vim convention
@Joao208 Joao208 merged commit bb63260 into main Mar 31, 2026
4 checks passed
@Joao208 Joao208 deleted the joaobarros-/-agent-teams-ui-and-message-delivery branch March 31, 2026 00:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant