Skip to content

fix: index CLI-created Claude sessions into Web UI#93

Closed
t2ance wants to merge 1 commit intoOpenLAIR:mainfrom
t2ance:fix/cli-session-discovery
Closed

fix: index CLI-created Claude sessions into Web UI#93
t2ance wants to merge 1 commit intoOpenLAIR:mainfrom
t2ance:fix/cli-session-discovery

Conversation

@t2ance
Copy link
Copy Markdown
Collaborator

@t2ance t2ance commented Mar 25, 2026

Summary

  • Sessions created via the claude CLI were not visible in the Web UI because they were written directly to disk (.jsonl files) without being indexed into the session_metadata database table
  • On startup, reconcile all registered projects to index any existing CLI sessions
  • In the file watcher, accumulate .jsonl change events during the debounce window and call reconcileClaudeSessionIndex() for each before broadcasting the project update

Fixes the CLI session portion of #86.

Root Cause

Web UI sessions are indexed immediately via recordIndexedSession() in claude-sdk.js when the first message arrives. CLI sessions bypass the dr-claw server entirely -- the claude binary writes .jsonl directly to ~/.claude/projects/. The file watcher detects these changes but only calls getProjects(), which reads from the DB index and never parses .jsonl files. So CLI sessions remain invisible.

Test plan

  • Register a project via Web UI (existing workspace)
  • Open a CLI session in that project directory (cd /path/to/project && claude)
  • Chat briefly, then exit the CLI session
  • Verify the CLI session appears in the Web UI without manual refresh
  • Restart the dr-claw server and verify CLI sessions still appear
  • Verify existing Web UI sessions are not affected (display name, starred status preserved)
  • Create two CLI sessions rapidly (within 1 second) and verify both appear

Generated with Claude Code

Sessions created via the `claude` CLI command were not visible in the
Web UI because they were written directly to disk (.jsonl files) without
being indexed into the session_metadata database. The Web UI only queries
the database, so these sessions were invisible.

Two changes:
1. On startup, reconcile all registered projects to index any existing
   CLI sessions into the database.
2. In the file watcher, when a new .jsonl is detected under
   ~/.claude/projects/, call reconcileClaudeSessionIndex() to index
   the session before broadcasting the project update.

Fixes the CLI session portion of OpenLAIR#86.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@Zhang-Henry Zhang-Henry left a comment

Choose a reason for hiding this comment

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

Code Review

Adds two hooks to index CLI-created Claude sessions: a startup reconcile loop across all projects, and accumulation of .jsonl file-watcher events into reconcileClaudeSessionIndex calls. The approach is sound and well-scoped. A few issues need attention.


Should Fix

1. Single reconcileClaudeSessionIndex failure aborts the entire batch
server/index.js (~L194-200): eventsToProcess is spliced off the global array before the loop. If any single call throws (transient DB lock, malformed JSONL), the exception propagates, getProjects() never runs, and the spliced events are permanently lost. Wrap the loop body in its own try/catch:

for (const { projectName, sessionId } of eventsToProcess) {
    try {
        await reconcileClaudeSessionIndex(projectName, sessionId);
    } catch (err) {
        console.error(`[WARN] Failed to reconcile session ${sessionId} for ${projectName}:`, err.message);
    }
}

2. Same issue in the startup reconcile loop
server/index.js (~L2999-3010): If reconciliation fails for project N, projects N+1 through end are never reconciled. Move try/catch inside the loop and log the failing project name. Also, the success log shows total project count even if some failed.

3. pendingClaudeSessionEvents not cleared on watcher reinit
If setupProjectsWatcher is ever called again (e.g., after a chokidar error), the module-level array retains events from the previous lifecycle and they will be replayed. Add pendingClaudeSessionEvents = []; alongside the projectsWatchers = [] reset.


Minor

  • projectName is derived from path.basename(path.dirname(filePath)) without validation. If chokidar ever emits a path with .. segments, path.join(homedir, '.claude', 'projects', '..') resolves outside the intended scope. Add a guard: if (!projectName || projectName === '..' || projectName === '.') continue;
  • sessionId is only checked for agent- prefix. Temp files like .#session.jsonl would pass through. A basic alphanumeric/hyphen guard would be cleaner.
  • console.error('[WARN] ...') should be console.warn for consistency with the rest of the codebase (e.g., line 1979).

Positive

  • pendingClaudeSessionEvents.splice(0) atomically drains the array — correct pattern for concurrent accumulation.
  • agent- file filtering matches the existing pattern in projects.js.
  • Startup reconcile correctly calls without targetSessionId to trigger a full scan.
  • project.name guard is good defensive coding.
  • Change is appropriately scoped to server/index.js without mutating the reconcile logic itself.

t2ance added a commit to t2ance/dr-claw that referenced this pull request Apr 7, 2026
Address code review feedback from OpenLAIR#93:
- Wrap reconcile loops in per-item try/catch to prevent single failure
  from aborting the batch
- Clear pendingClaudeSessionEvents on watcher reinit
- Add path traversal guard for projectName (reject "." and "..")
- Filter temp files with alphanumeric/hyphen regex on sessionId
- Use console.warn instead of console.error for warnings
- Track reconciled count separately from total project count

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
t2ance added a commit to t2ance/dr-claw that referenced this pull request Apr 7, 2026
Address code review feedback from OpenLAIR#93:
- Wrap reconcile loops in per-item try/catch to prevent single failure
  from aborting the batch
- Clear pendingClaudeSessionEvents on watcher reinit
- Add path traversal guard for projectName (reject "." and "..")
- Filter temp files with alphanumeric/hyphen regex on sessionId
- Use console.warn instead of console.error for warnings
- Track reconciled count separately from total project count

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
t2ance added a commit to t2ance/dr-claw that referenced this pull request Apr 7, 2026
Address code review feedback from OpenLAIR#93:
- Wrap reconcile loops in per-item try/catch to prevent single failure
  from aborting the batch
- Clear pendingClaudeSessionEvents on watcher reinit
- Add path traversal guard for projectName (reject "." and "..")
- Filter temp files with alphanumeric/hyphen regex on sessionId
- Use console.warn instead of console.error for warnings
- Track reconciled count separately from total project count

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
t2ance added a commit to t2ance/dr-claw that referenced this pull request Apr 7, 2026
Address code review feedback from OpenLAIR#93:
- Wrap reconcile loops in per-item try/catch to prevent single failure
  from aborting the batch
- Clear pendingClaudeSessionEvents on watcher reinit
- Add path traversal guard for projectName (reject "." and "..")
- Filter temp files with alphanumeric/hyphen regex on sessionId
- Use console.warn instead of console.error for warnings
- Track reconciled count separately from total project count

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@t2ance
Copy link
Copy Markdown
Collaborator Author

t2ance commented Apr 8, 2026

Superseded by #142 — rebased on latest main with all review feedback addressed.

@t2ance t2ance closed this Apr 8, 2026
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.

2 participants