Skip to content

Add Agents tab to dashboard#2

Merged
jameswnl merged 4 commits into
mainfrom
add-agents-view
Apr 3, 2026
Merged

Add Agents tab to dashboard#2
jameswnl merged 4 commits into
mainfrom
add-agents-view

Conversation

@jameswnl

@jameswnl jameswnl commented Mar 30, 2026

Copy link
Copy Markdown
Owner

Summary

  • Browse custom agents from ~/.claude/agents/ (user-level) and .claude/agents/ (project-level)
  • Parses YAML frontmatter to display metadata: model, color, memory scope
  • New Agents view tab with expandable items, search, and summary bar
  • 9 new tests (130 total)

Test plan

  • Verify Agents tab appears between Skills and MCP Servers
  • Confirm user-level agents from ~/.claude/agents/ are listed
  • Check agent metadata badges (model, color, memory) render correctly
  • Expand an agent to see full prompt body
  • Search filters agents by name, description, and content
  • Run uv run pytest — all 130 tests pass

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added an Agents tab to browse custom agents at user and project levels.
    • Agents are searchable, filterable, and expandable with metadata badges (model, color, memory).
    • Dashboard API now includes agents data alongside other payloads.
  • Documentation

    • README updated with Agents support and a new screenshot.
  • Tests

    • Coverage extended for agent parsing, aggregation, UI presence, and API exposure.

- Collect agents from ~/.claude/agents/ (user-level) and
  .claude/agents/ (project-level) with YAML frontmatter parsing
- Display agent metadata: name, description, model, color, memory
- New Agents view tab between Skills and MCP Servers
- Expandable agent items showing full prompt body
- Search across agent names, descriptions, and content
- 9 new tests for agent collection and UI integration (130 total)
- Update README with agents feature and screenshot

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jameswnl

jameswnl commented Apr 3, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Apr 3, 2026

Copy link
Copy Markdown
✅ Actions performed

Full review triggered.

@coderabbitai

coderabbitai Bot commented Apr 3, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Agent support added: Markdown agent files with optional YAML frontmatter are parsed and aggregated from user and project agent directories via collect_all_agents(). The server caches agents JSON and exposes it in /api/data. The UI gains an "Agents" tab with searchable, collapsible listings and metadata badges.

Changes

Cohort / File(s) Summary
Documentation
README.md
Added Agents screenshot and feature note: browseable custom agents with metadata (model, color, memory).
Agent Data Collection
src/claude_dashboard/data.py
Added _read_agent_file() (parse .md with optional YAML frontmatter: name, description, model, color, memory, truncated body), _extract_agents_from_dir() to collect .md agents, and public collect_all_agents() aggregating user (~/.claude/agents) and project (<project>/.claude/agents) agents with deduplication. get_dir_fingerprint() now includes agent .md mtimes.
Server Integration
src/claude_dashboard/server.py
DashboardState caches agents_json; get() return signature changed to include agents_json (now (data_json, skills_json, agents_json, mcp_json, version)). /api/data now includes an agents field.
User Interface
src/claude_dashboard/ui.py
Added "Agents" tab and agents-view; client-side AGENTS state populated from /api/data. Added renderAgentItem() and renderAgents() with search/filter/highlight, collapsible user/project sections, anchor links, and metadata badges. New CSS classes for agent styling.
Tests
tests/test_server.py
Expanded tests to cover _read_agent_file, _extract_agents_from_dir, and collect_all_agents() (frontmatter/no-frontmatter, ignoring non-.md, empty/nonexistent dirs). Updated tests to unpack agents_json from DashboardState.get() and assert agents presence in /api/data and get_html() UI markers.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Server
    participant DataModule as Data Module
    participant FileSystem as File System

    Client->>Server: GET /api/data
    Server->>DataModule: collect_all_agents()
    DataModule->>FileSystem: scan ~/.claude/agents
    FileSystem-->>DataModule: list .md files
    loop for each user agent file
        DataModule->>FileSystem: read file
        FileSystem-->>DataModule: file contents
        DataModule->>DataModule: _read_agent_file() parse frontmatter/body
    end
    DataModule->>FileSystem: scan projects/*/.claude/agents
    FileSystem-->>DataModule: list project .md files
    loop for each project agent file
        DataModule->>FileSystem: read file
        FileSystem-->>DataModule: file contents
        DataModule->>DataModule: _read_agent_file() parse frontmatter/body
    end
    DataModule->>DataModule: aggregate & deduplicate -> {user, projects}
    DataModule-->>Server: return agents payload
    Server->>Server: cache agents_json in DashboardState
    Server-->>Client: respond with {version, data, skills, agents, mcp}
    Client->>Client: populate AGENTS and render Agents view
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I nibble frontmatter, name, and hue,
Parsing markdown, both old and new,
User and project nests neatly shown,
Badges sparkle where agents roam,
The dashboard springs forward—hop, hop, go! 🌿

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.82% 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 clearly and concisely summarizes the main change: adding a new Agents tab to the dashboard, which is the primary feature across all modified files.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch add-agents-view

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: 2

🧹 Nitpick comments (2)
tests/test_server.py (1)

393-393: Clean up unused tuple elements in test unpacking.

Line 393 and Line 1872 unpack variables that are never used; replace unused names with _-prefixed placeholders to keep lint output clean.

Also applies to: 1872-1872

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

In `@tests/test_server.py` at line 393, The tuple unpacking of s.get() assigns
variables that are not used (data_json, skills_json, agents_json, mcp_json,
version); update those unpackings to use underscore-prefixed placeholders for
unused elements (e.g., _data_json, _skills_json, _agents_json, _mcp_json,
_version) wherever s.get() is unpacked so linters stop reporting unused-variable
warnings (also apply the same change to the other occurrence of s.get()
unpacking).
src/claude_dashboard/data.py (1)

206-207: Narrow the exception type in agent file parsing.

Line 206 swallows all exceptions, which can hide parser bugs. Catch expected read/OS errors only.

Proposed fix
 def _read_agent_file(filepath):
     """Read an agent .md file and return its metadata from YAML frontmatter."""
     try:
         content = filepath.read_text(errors="replace")[:5000]
         meta = {"name": filepath.stem, "content": content}
@@
         return meta
-    except Exception:
+    except OSError:
         return None
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/claude_dashboard/data.py` around lines 206 - 207, Replace the broad
"except Exception: return None" in the agent file parsing block with a narrow
catch for expected I/O/parse errors (for example: except (OSError, IOError,
FileNotFoundError, UnicodeDecodeError) as e:) so only filesystem or decoding
issues return None; let unexpected exceptions propagate (or re-raise) so parser
bugs surface. Keep the return None behavior for those expected errors and
optionally log the caught error (include the exception variable e) for
debugging.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/claude_dashboard/server.py`:
- Around line 60-66: The agents cache is not covered by the directory
fingerprint so edits to ~/.claude/agents/*.md or .claude/agents/*.md won't
trigger refresh; update the fingerprinting logic (used by the watcher that
compares get_dir_fingerprint()) to include both user and project agent
directories and/or agent file globs, and ensure collect_all_agents() is
re-invoked when that fingerprint changes so self.agents_json (set inside the
with self.lock block) is refreshed automatically; reference
get_dir_fingerprint(), collect_all_agents(), and the self.agents_json assignment
to locate where to add those agent paths to the watched fingerprint set.

In `@src/claude_dashboard/ui.py`:
- Around line 1148-1159: Search currently only checks a.body for agent prompt
text so agents without frontmatter (which store text in a.content) are excluded;
update both places that build filtered (the userAgents filter and the
projectAgents p.agents filter) to include searchMatch(a.content || '', q) in the
predicate alongside a.body and a.description so renderable prompts in a.content
are matched by search.

---

Nitpick comments:
In `@src/claude_dashboard/data.py`:
- Around line 206-207: Replace the broad "except Exception: return None" in the
agent file parsing block with a narrow catch for expected I/O/parse errors (for
example: except (OSError, IOError, FileNotFoundError, UnicodeDecodeError) as e:)
so only filesystem or decoding issues return None; let unexpected exceptions
propagate (or re-raise) so parser bugs surface. Keep the return None behavior
for those expected errors and optionally log the caught error (include the
exception variable e) for debugging.

In `@tests/test_server.py`:
- Line 393: The tuple unpacking of s.get() assigns variables that are not used
(data_json, skills_json, agents_json, mcp_json, version); update those
unpackings to use underscore-prefixed placeholders for unused elements (e.g.,
_data_json, _skills_json, _agents_json, _mcp_json, _version) wherever s.get() is
unpacked so linters stop reporting unused-variable warnings (also apply the same
change to the other occurrence of s.get() unpacking).
🪄 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: 49c8b9ce-cb4f-47a3-980f-81ea5664febc

📥 Commits

Reviewing files that changed from the base of the PR and between 4ba79f5 and a52fb6b.

⛔ Files ignored due to path filters (1)
  • docs/agents.png is excluded by !**/*.png
📒 Files selected for processing (5)
  • README.md
  • src/claude_dashboard/data.py
  • src/claude_dashboard/server.py
  • src/claude_dashboard/ui.py
  • tests/test_server.py

Comment thread src/claude_dashboard/server.py
Comment thread src/claude_dashboard/ui.py Outdated
…eptions

- Add agent directories to get_dir_fingerprint() for auto-refresh
- Fix agent search to include a.content for agents without frontmatter
- Narrow except Exception to except OSError in _read_agent_file
- Use _-prefixed names for unused tuple elements in tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@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: 2

🧹 Nitpick comments (1)
src/claude_dashboard/data.py (1)

406-407: Avoid blanket except Exception: pass in new fingerprint branches.

Catching all exceptions here can silently hide non-filesystem bugs. Prefer narrowing to expected filesystem errors (OSError) and optionally logging at debug level.

🔧 Proposed fix
-    except Exception:
+    except OSError:
         pass
@@
-    except Exception:
+    except OSError:
         pass

Also applies to: 422-423

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

In `@src/claude_dashboard/data.py` around lines 406 - 407, Replace the blanket
"except Exception: pass" in the new fingerprint branches with a narrow catch for
filesystem errors (e.g., OSError) and log the exception at debug level, and let
all other unexpected exceptions bubble up (or re-raise them); specifically
locate the two try/except blocks that currently use "except Exception: pass"
(the occurrences around lines 406-407 and 422-423 in
src/claude_dashboard/data.py) and change them to "except OSError as e:
logger.debug(...)" (or the module logger) while removing the silent swallow of
non-filesystem errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/claude_dashboard/data.py`:
- Around line 410-415: The fingerprint path is reconstructed incorrectly using
string ops on project_dir.name which can diverge from dirname_to_path(...) and
cause missed agent updates; in the loop over PROJECTS_DIR.iterdir() (variables
project_dir, real_path, agents_dir) replace the manual "/" +
project_dir.name.lstrip("-").replace("-", "/") logic with a call to the
canonical dirname_to_path(project_dir.name) (or the existing dirname_to_path
function used elsewhere) and ensure it returns the same leading slash/format
expected by agents_dir so that agents_dir =
Path(dirname_to_path(project_dir.name)) / ".claude" / "agents" correctly detects
changes.

In `@tests/test_server.py`:
- Line 381: The tuple unpacking assigns unused names which triggers lint
warnings; change the unpack of s.get() so intentionally-unused elements use
underscore placeholders (e.g., replace names like skills_json, agents_json,
mcp_json, version with _ or _<n> as appropriate) while keeping the used variable
data_json; update the assignment at the call site of s.get() to only bind
data_json and use underscores for other tuple positions to match the style used
later at line 393.

---

Nitpick comments:
In `@src/claude_dashboard/data.py`:
- Around line 406-407: Replace the blanket "except Exception: pass" in the new
fingerprint branches with a narrow catch for filesystem errors (e.g., OSError)
and log the exception at debug level, and let all other unexpected exceptions
bubble up (or re-raise them); specifically locate the two try/except blocks that
currently use "except Exception: pass" (the occurrences around lines 406-407 and
422-423 in src/claude_dashboard/data.py) and change them to "except OSError as
e: logger.debug(...)" (or the module logger) while removing the silent swallow
of non-filesystem errors.
🪄 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: 7b4fd4d7-8722-443e-bcdc-2ebfaa726654

📥 Commits

Reviewing files that changed from the base of the PR and between a52fb6b and eee77c0.

📒 Files selected for processing (3)
  • src/claude_dashboard/data.py
  • src/claude_dashboard/ui.py
  • tests/test_server.py
✅ Files skipped from review due to trivial changes (1)
  • src/claude_dashboard/ui.py

Comment thread src/claude_dashboard/data.py
Comment thread tests/test_server.py Outdated
- Use dirname_to_path() instead of manual string replacement for
  consistent project path derivation in fingerprint
- Prefix unused tuple elements with _ in test_dashboard_state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@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: 2

🧹 Nitpick comments (1)
tests/test_server.py (1)

1837-1856: Add fingerprint regression tests for agent file mutations

Good coverage for collect_all_agents(), but the PR also adds fingerprint-driven refresh behavior for agent files (src/claude_dashboard/data.py Line 396-423). A focused test for user/project agent file create/update would protect the watcher refresh gate from regressions.

🧪 Suggested test additions
+def test_get_dir_fingerprint_detects_user_agent_change(tmp_path, monkeypatch):
+    claude_dir = tmp_path / "claude"
+    agents_dir = claude_dir / "agents"
+    agents_dir.mkdir(parents=True)
+    monkeypatch.setattr("claude_dashboard.data.CLAUDE_DIR", claude_dir)
+    monkeypatch.setattr("claude_dashboard.data.PROJECTS_DIR", tmp_path / "projects")
+
+    fp1 = get_dir_fingerprint()
+    (agents_dir / "a.md").write_text("---\nname: a\n---\nPrompt")
+    fp2 = get_dir_fingerprint()
+    assert fp1 != fp2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/claude_dashboard/data.py`:
- Around line 400-401: The loops that iterate user_agents_dir.iterdir() are
non-deterministic; change both iterations (the one at user_agents_dir.iterdir()
around the fingerprint/hash logic and the later loop at lines ~416-417) to
iterate over a deterministic, sorted list (e.g.,
sorted(user_agents_dir.iterdir(), key=lambda p: p.name or str(p))) so files are
processed in a stable order before hashing/processing; update references in
src/claude_dashboard/data.py where user_agents_dir.iterdir() is used (the two
occurrences) to use the sorted iterator instead.
- Around line 397-407: The current fingerprint-gathering block in
src/claude_dashboard/data.py silently swallows all exceptions (broad except
Exception: pass), which can hide scan failures and prevent watcher_thread() in
src/claude_dashboard/server.py from detecting changes; replace the broad excepts
around the directory scan with targeted exception handling (e.g., catch and
handle OSError, PermissionError) and/or log unexpected exceptions with
logger.exception (do not only pass), so failures are visible and the code
continues safely; ensure the logic around user_agents_dir, CLAUDE_DIR, and the
f.stat() call still appends valid entries but surfaces any unexpected errors
instead of suppressing them.
🪄 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: 36c0f973-754a-4b94-9b48-ff9e41da5134

📥 Commits

Reviewing files that changed from the base of the PR and between eee77c0 and a1c3361.

📒 Files selected for processing (2)
  • src/claude_dashboard/data.py
  • tests/test_server.py

Comment thread src/claude_dashboard/data.py
Comment thread src/claude_dashboard/data.py Outdated
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@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: 1

♻️ Duplicate comments (1)
src/claude_dashboard/data.py (1)

396-423: ⚠️ Potential issue | 🟠 Major

Silent exception swallowing can stall agent refresh detection.

The broad except Exception: pass blocks (lines 406-407, 422-423) hide all scan failures. Because watcher_thread() only refreshes when the fingerprint changes (per server.py:84-96), silently skipped scans can leave stale agent data indefinitely.

🔧 Proposed fix
+import logging
+
+logger = logging.getLogger(__name__)
+
 def get_dir_fingerprint():
     """Get a hash of mtimes of all relevant files to detect changes."""
     parts = []
     ...
     # Track user-level agent files
     try:
         user_agents_dir = CLAUDE_DIR / "agents"
         if user_agents_dir.is_dir():
             for f in sorted(user_agents_dir.iterdir()):
                 if f.is_file() and f.suffix == ".md":
                     try:
                         parts.append(f"{f}:{f.stat().st_mtime}")
                     except OSError:
                         pass
-    except Exception:
-        pass
+    except OSError as e:
+        logger.debug("Skipping user agent fingerprint scan: %s", e)
+
     # Track project-level agent files
     try:
         for project_dir in sorted(PROJECTS_DIR.iterdir()):
             if not project_dir.is_dir():
                 continue
             project_path = dirname_to_path(project_dir.name)
             agents_dir = Path(project_path) / ".claude" / "agents"
             if agents_dir.is_dir():
                 for f in sorted(agents_dir.iterdir()):
                     if f.is_file() and f.suffix == ".md":
                         try:
                             parts.append(f"{f}:{f.stat().st_mtime}")
                         except OSError:
                             pass
-    except Exception:
-        pass
+    except OSError as e:
+        logger.debug("Skipping project agent fingerprint scan: %s", e)
+
     return hashlib.md5("|".join(parts).encode()).hexdigest()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/claude_dashboard/data.py` around lines 396 - 423, The code currently
swallows all exceptions in the user/global agent scan blocks (around
user_agents_dir and PROJECTS_DIR loops), which can hide failures and prevent
watcher_thread() from detecting changes; replace the broad "except Exception:
pass" with specific exception handling (e.g., catch OSError, FileNotFoundError,
PermissionError) and log the exception so failures are visible, and ensure the
loops continue scanning other entries after an error (keep the inner try/except
for f.stat() but log on failure and do not suppress unexpected errors silently);
refer to user_agents_dir, PROJECTS_DIR, dirname_to_path, parts, and
watcher_thread when making these changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/claude_dashboard/data.py`:
- Around line 186-208: The YAML frontmatter parsing in _read_agent_file is
fragile; replace the manual line-by-line partitioning with a proper YAML parse:
import yaml (pyyaml) and call yaml.safe_load(frontmatter) to get a dict, then
map allowed keys ("name","description","model","color","memory") into meta and
set meta["body"] = body[:3000]; handle yaml.YAMLError by logging/returning None
or falling back to the current basic parse, and keep the existing OSError
handling; ensure you add pyyaml as a dependency and sanitize/truncate values as
before.

---

Duplicate comments:
In `@src/claude_dashboard/data.py`:
- Around line 396-423: The code currently swallows all exceptions in the
user/global agent scan blocks (around user_agents_dir and PROJECTS_DIR loops),
which can hide failures and prevent watcher_thread() from detecting changes;
replace the broad "except Exception: pass" with specific exception handling
(e.g., catch OSError, FileNotFoundError, PermissionError) and log the exception
so failures are visible, and ensure the loops continue scanning other entries
after an error (keep the inner try/except for f.stat() but log on failure and do
not suppress unexpected errors silently); refer to user_agents_dir,
PROJECTS_DIR, dirname_to_path, parts, and watcher_thread when making these
changes.
🪄 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: c9b377d8-c892-4ea9-bb20-0a03983a2753

📥 Commits

Reviewing files that changed from the base of the PR and between a1c3361 and 77d117e.

📒 Files selected for processing (1)
  • src/claude_dashboard/data.py

Comment thread src/claude_dashboard/data.py
@jameswnl jameswnl merged commit 0f37207 into main Apr 3, 2026
5 checks passed
@jameswnl jameswnl deleted the add-agents-view branch April 3, 2026 21:01
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