Compare commits

..

3 Commits

Author SHA1 Message Date
b60501e80f Add AGENTS.md with development principles and beads workflow docs
Project-level guidance for Claude Code including:
- Development principles: simplicity, third-party library preference,
  extensible architecture, loose DRY, clear architecture
- Beads Rust (br) CLI workflow: essential commands, robot mode flags,
  session protocol checklist, and best practices
- Key concepts: dependency tracking, priority levels (P0-P4), issue
  types, and blocking relationships

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 09:34:35 -05:00
a51c134da7 Harden API layer: encode session IDs and validate export payload
Session fetch (useSession.ts):
- Wrap the session ID in encodeURIComponent before interpolating
  into the fetch URL. Session IDs can contain characters like '+'
  or '/' that would corrupt the path without encoding.

Export route (export.ts):
- Add validation that redactedMessageUuids, when present, is an
  array. Previously only visibleMessageUuids was checked, so a
  malformed redactedMessageUuids value (e.g. a string or object)
  would silently pass validation and potentially cause downstream
  type errors in the HTML exporter.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 09:34:29 -05:00
4c5d6dd4c8 Extend search to match tool input content, not just message body
The search index (app.tsx) and the per-message match check
(SessionViewer.tsx) now also search msg.toolInput when present.
This means searching for a file path, command, or argument that
appears in a tool call's input will correctly highlight and
navigate to that message, rather than dimming it as a non-match.

Both locations use the same compound condition so the match index
and the visual dimming/focus logic stay in sync.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 09:34:20 -05:00
5 changed files with 110 additions and 4 deletions

97
AGENTS.md Normal file
View File

@@ -0,0 +1,97 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
### Development Principles
Find the simplest solution that meets all acceptance criteria.
Use third party libraries whenever there's a well-maintained, active, and widely adopted solution (for example, date-fns for TS date math)
Build extensible pieces of logic that can easily be integrated with other pieces.
DRY principles should be loosely held.
Architecture MUST be clear and well thought-out. Ask the user for clarification whenever ambiguity is discovered around architecture, or you think a better approach than planned exists.
Isolate development CLAUDE files/skills/agents/etc from the tools and prompts that will be used by the app. There should be no pollution of external Claude config with what the Ghost AI Assistant sees and uses.
## Beads Rust Workflow Integration
This project uses [beads_viewer](https://github.com/Dicklesworthstone/beads_viewer) for issue tracking. Issues are stored in `.beads/` and tracked in git.
### Essential Commands
```bash
# View issues (launches TUI - NOT FOR AGENT USE, human only)
bv
# CLI commands for agents (use --json for machine-readable output)
br ready --json # Show issues ready to work (no blockers)
br list --status=open --json # All open issues
br show <id> --json # Full issue details with dependencies
br create --title="..." --type=task --priority=2
br update <id> --status=in_progress
br close <id> --reason="Completed"
br close <id1> <id2> # Close multiple issues at once
br sync # Commit and push changes
```
### Robot Mode (Agent-Optimized bv Commands)
Use `bv --robot-*` flags for structured JSON output optimized for AI agents:
```bash
# Essential robot commands
bv --robot-triage # THE MEGA-COMMAND: unified analysis, recommendations, health
bv --robot-next # Single top recommendation (minimal output)
bv --robot-plan # Dependency-respecting execution plan
bv --robot-priority # Priority recommendations with reasoning
bv --robot-insights # Deep graph analysis (PageRank, bottlenecks, etc.)
# File impact analysis (check before editing)
bv --robot-impact <file> # Risk assessment for modifying files
bv --robot-file-beads <path> # What beads have touched this file?
bv --robot-file-hotspots # High-churn files (conflict zones)
bv --robot-related <bead-id> # Find related beads
# Filtering options (work with most robot commands)
bv --robot-triage --robot-by-label=backend
bv --robot-priority --robot-min-confidence=0.7
bv --robot-insights --label=api # Scope to label subgraph
```
Run `bv -robot-help` for complete robot mode documentation.
### Workflow Pattern
1. **Start**: Run `br ready` to find actionable work
2. **Claim**: Use `br update <id> --status=in_progress`
3. **Work**: Implement the task
4. **Complete**: Use `br close <id>`
5. **Sync**: Always run `br sync` at session end
### Key Concepts
- **Dependencies**: Issues can block other issues. `br ready` shows only unblocked work.
- **Priority**: P0=critical, P1=high, P2=medium, P3=low, P4=backlog (use numbers, not words)
- **Types**: task, bug, feature, epic, question, docs
- **Blocking**: `br dep add <issue> <depends-on>` to add dependencies
### Session Protocol
**Before ending any session, run this checklist:**
```bash
git status # Check what changed
git add <files> # Stage code changes
br sync # Commit beads changes
git commit -m "..." # Commit code
br sync # Commit any new beads changes
git push # Push to remote
```
### Best Practices
- Check `br ready` at session start to find available work
- Update status as you work (in_progress → closed)
- Create new issues with `br create` when you discover tasks
- Use descriptive titles and set appropriate priority/type
- Always `br sync` before ending session
<!-- end-br-agent-instructions -->

View File

@@ -30,7 +30,12 @@ export function App() {
if (!filters.searchQuery) return [];
const lq = filters.searchQuery.toLowerCase();
return filteredMessages.reduce<number[]>((acc, msg, i) => {
if (msg.content.toLowerCase().includes(lq)) acc.push(i);
if (
msg.content.toLowerCase().includes(lq) ||
(msg.toolInput && msg.toolInput.toLowerCase().includes(lq))
) {
acc.push(i);
}
return acc;
}, []);
}, [filteredMessages, filters.searchQuery]);

View File

@@ -197,9 +197,11 @@ export function SessionViewer({
);
}
const msg = item.message;
const lq = searchQuery ? searchQuery.toLowerCase() : "";
const isMatch =
searchQuery &&
msg.content.toLowerCase().includes(searchQuery.toLowerCase());
(msg.content.toLowerCase().includes(lq) ||
(msg.toolInput && msg.toolInput.toLowerCase().includes(lq)));
const isDimmed = searchQuery && !isMatch;
const isFocused = item.messageIndex === focusedIndex;
return (

View File

@@ -42,7 +42,7 @@ export function useSession(): SessionState {
setSessionLoading(true);
setSessionError(null);
try {
const res = await fetch(`/api/sessions/${id}`);
const res = await fetch(`/api/sessions/${encodeURIComponent(id)}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
setCurrentSession(data);

View File

@@ -10,7 +10,9 @@ exportRouter.post("/", async (req, res) => {
if (
!exportReq?.session?.messages ||
!Array.isArray(exportReq.session.messages) ||
!Array.isArray(exportReq.visibleMessageUuids)
!Array.isArray(exportReq.visibleMessageUuids) ||
(exportReq.redactedMessageUuids !== undefined &&
!Array.isArray(exportReq.redactedMessageUuids))
) {
res.status(400).json({ error: "Invalid export request: missing session, messages, or visibleMessageUuids" });
return;