Skip to content

feat(claude): sync Claude Code session title to HAPI web UI#394

Open
koenzhao wants to merge 1 commit intotiann:mainfrom
koenzhao:feat/claude-title-sync
Open

feat(claude): sync Claude Code session title to HAPI web UI#394
koenzhao wants to merge 1 commit intotiann:mainfrom
koenzhao:feat/claude-title-sync

Conversation

@koenzhao
Copy link
Copy Markdown

@koenzhao koenzhao commented Apr 4, 2026

Summary

  • Intercept custom-title events from Claude Code's JSONL session files and update HAPI session metadata.name
  • When a user runs /rename in Claude Code, the title automatically syncs to the HAPI web UI within ~3 seconds
  • On session startup, the latest title from JSONL history is applied immediately

Motivation

Currently, HAPI relies on its own mcp__hapi__change_title MCP tool (via system prompt injection) to set session titles. This means Claude Code's native /rename command has no effect on the HAPI web UI — they are two separate naming systems.

This PR makes Claude Code the single source of truth for session titles by reading custom-title events directly from the JSONL files.

Changes

  • cli/src/claude/utils/sessionScanner.ts — Added onTitleChange callback; readSessionLog now detects custom-title events before Zod schema validation and returns them separately
  • cli/src/claude/claudeLocalLauncher.ts — Wires onTitleChange to session.client.updateMetadata to set metadata.name

Test plan

  • Start a Claude session via hapi claude, verify session appears in web UI
  • Run /rename my-session in Claude Code, verify title updates in web UI within ~3s
  • Resume a renamed session, verify title persists on startup
  • Start a fresh session (no prior rename), verify no errors

Intercept `custom-title` events from Claude Code's JSONL session files
and update HAPI session metadata.name, so /rename in Claude Code
automatically reflects in the HAPI web UI.

- Add onTitleChange callback to session scanner
- Detect custom-title events in readSessionLog before schema validation
- Apply latest title on session startup from JSONL history
- Emit title changes during live scanning (within 3s poll interval)
Copy link
Copy Markdown

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Findings

  • [Major] Pending-session title events can overwrite the active session title. findSessionFiles() still scans pendingSessions during Claude session rollover, and parseSessionFile() now forwards every custom-title it sees. A late rename written to the previous JSONL file can therefore replace metadata.name for the new active Claude session. Evidence cli/src/claude/utils/sessionScanner.ts:104, cli/src/claude/utils/sessionScanner.ts:123.

Summary
Review mode: initial
1 finding. Static review only. Residual risk: the new title-sync path has no regression coverage for startup hydration or session rollover in cli/src/claude/utils/sessionScanner.test.ts:41.

Testing
Not run (automation; bun is not installed in this runner).

HAPI Bot

}
const { events, totalLines } = await readSessionLog(filePath, cursor);
const { events, titleChanges, totalLines } = await readSessionLog(filePath, cursor);
for (const title of titleChanges) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[MAJOR] This applies custom-title updates from every scanned file, not just the current Claude session. During /clear or resume, findSessionFiles() still includes pendingSessions, so a late rename in the previous JSONL file can overwrite metadata.name for the new active session.

Suggested fix:

const { events, titleChanges, totalLines } = await readSessionLog(filePath, cursor)
if (sessionId === this.currentSessionId) {
    for (const title of titleChanges) {
        logger.debug(`[SESSION_SCANNER] Title change: ${title}`)
        this.onTitleChange?.(title)
    }
}

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