Skip to content

fix(#843): session-manager hooks — ecc-sessions dir + hookSpecificOutput format#948

Closed
anuragg-saxenaa wants to merge 4 commits intoaffaan-m:mainfrom
anuragg-saxenaa:fix/843-session-manager-hooks
Closed

fix(#843): session-manager hooks — ecc-sessions dir + hookSpecificOutput format#948
anuragg-saxenaa wants to merge 4 commits intoaffaan-m:mainfrom
anuragg-saxenaa:fix/843-session-manager-hooks

Conversation

@anuragg-saxenaa
Copy link
Copy Markdown
Contributor

@anuragg-saxenaa anuragg-saxenaa commented Mar 26, 2026

Summary

Fixes #843 — session hooks installed but not working out of the box.

Two root causes identified and fixed:

Fix 1: Session directory collision with Claude Code cleanup

Claude Code performs cleanup on its built-in ~/.claude/sessions/ directory on exit, which removes files written by the session-end hook before the next session can read them.

Fix: Renamed the sessions directory from sessions to ecc-sessions so ECC-managed files live outside Claude Code's cleanup scope.

// Before
return path.join(getClaudeDir(), 'sessions');
// After
return path.join(getClaudeDir(), 'ecc-sessions');

Fix 2: SessionStart hook output format

Claude Code's SessionStart hooks require JSON output with a hookSpecificOutput envelope. Plain console.log(string) output is silently discarded — no context is injected.

Fix: Updated session-start.js to use process.stdout.write(JSON.stringify({hookSpecificOutput: {hookEventName, additionalContext}})) for context injection.

Test plan

  • Install ECC, run a Claude session, end it
  • Verify ~/.claude/ecc-sessions/ contains a session file after session ends
  • Start a new session — verify previous session context appears in Claude's context window
  • Check ~/.claude/ecc-sessions/ still exists (not cleaned up by Claude Code)

Summary by cubic

Makes session hooks work out of the box by moving session storage out of Claude Code’s cleanup, deduping recent sessions across directories, and emitting a single hookSpecificOutput JSON with accumulated context at session start. Fixes #843.

  • Bug Fixes
    • Store session data in ~/.claude/session-data/ and search both canonical and legacy sessions dirs to dedupe recent files, avoiding cleanup-related losses.
    • Accumulate context (previous summary, learned skills, aliases, project info) and emit exactly one hookSpecificOutput JSON via a safe final write; remove early stdout writes and sanitize session IDs for valid, stable filenames.

Written for commit 2546f96. Summary will update on new commits.

Summary by CodeRabbit

  • Refactor

    • Canonicalized and reorganized session storage locations with backwards-compatible search of legacy data.
    • Session startup now emits a single structured JSON payload containing combined session/context info instead of scattered text output.
    • Session IDs and derived identifiers are now sanitized for safety and consistency.
  • Bug Fixes

    • Safer handling of standard output and process termination to avoid hangs on errors.

…ormat

- Rename sessions dir from 'sessions' to 'ecc-sessions' to prevent
  Claude Code's built-in cleanup from deleting session-end hook files
- Update session-start.js to output hookSpecificOutput JSON format
  so context is actually injected into Claude's session (plain stdout
  strings are silently discarded by SessionStart hooks)
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 26, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 82793f3a-c4fe-46b4-9eac-2d551a35f318

📥 Commits

Reviewing files that changed from the base of the PR and between 2115d67 and 2546f96.

📒 Files selected for processing (2)
  • scripts/hooks/session-start.js
  • scripts/lib/utils.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • scripts/hooks/session-start.js
  • scripts/lib/utils.js

📝 Walkthrough

Walkthrough

Reworks session-start hook output and session-file discovery: session-start now accumulates context, emits a single JSON payload via safe stdout writes and avoids blocking exit; session-file search deduplicates across canonical and legacy session dirs; utilities add sanitized session ID logic and new session-directory helpers.

Changes

Cohort / File(s) Summary
Session Start Hook
scripts/hooks/session-start.js
Added dedupeRecentSessions(searchDirs) to search across multiple session dirs and pick the newest file; accumulate injected context into additionalContextParts; emit a single structured JSON payload (hookSpecificOutput with hookEventName: 'SessionStart' and combined additionalContext) via new writeSessionStartPayload(additionalContext) which writes to process.stdout with async error handling; replaced process.exit(0) with process.exitCode = 0.
Session utilities & dirs
scripts/lib/utils.js
Changed canonical sessions path from .../sessions to .../session-data; added getLegacySessionsDir() and getSessionSearchDirs() (canonical-first, deduped search order); added sanitizeSessionId(raw) (normalization, char replacement, reserved-name handling, SHA‑256 short suffix for non-ASCII/reserved cases) and updated getSessionIdShort() to sanitize derived values; expanded exports and added crypto usage.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped through ~/.claude at dawn,

found sessions safe, no files withdrawn.
One JSON bloom, all contexts sewn,
stdout whispers, exit gently shown.
A rabbit cheers — the hooks now hum!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR addresses all three coding requirements from issue #843: sessions directory has been changed from ~/.claude/sessions to ~/.claude/session-data (via ecc-sessions approach), SessionStart hook now emits correct JSON envelope via process.stdout.write(), and session id sanitization handles file naming properly. However, the installer deployment of scripts/lib/ (Bug 1) is not addressed in the code changes shown. Verify that the installer/hooks-runtime module has been updated to deploy scripts/hooks/*.js and scripts/lib/ files on install, ensuring fresh installations don't fail on missing dependencies.
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title references the main issue (#843) and clearly identifies the two primary fixes: moving sessions to a new directory and fixing the hookSpecificOutput format, which directly align with the changeset.
Out of Scope Changes check ✅ Passed Code changes are focused on session directory handling, session ID sanitization, and hookSpecificOutput JSON formatting. The sanitization improvements and deduplication logic between legacy and new directories are reasonable extensions to support the migration, with no apparent out-of-scope changes.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 2 files


Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Add one-off context when rerunning by tagging @cubic-dev-ai with guidance or docs links (including llms.txt)
  • Ask questions if you need clarification on any suggestion

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 26, 2026

Greptile Summary

This PR fixes two root causes that prevented the session-manager hooks from working out of the box: (1) ECC session files were stored in ~/.claude/sessions/, which Claude Code's own cleanup removes on exit, and (2) the SessionStart hook was writing plain strings to stdout instead of the required hookSpecificOutput JSON envelope.

Changes:

  • scripts/lib/utils.js: Renames the canonical sessions directory from sessions to session-data (constant SESSION_DATA_DIR_NAME), adds getLegacySessionsDir() and getSessionSearchDirs() for backward-compatible migration, and introduces sanitizeSessionId() with Windows-reserved-name protection and SHA-256 disambiguation for non-ASCII inputs.
  • scripts/hooks/session-start.js: Accumulates all context parts (previous session summary, project info) into an array and emits a single hookSpecificOutput JSON payload at the end via writeSessionStartPayload, replacing the old multi-output() calls. Adds dedupeRecentSessions() to merge results from both the new and legacy directories, preferring the canonical path on tie.

Notable discrepancy: The PR title and description consistently reference ecc-sessions as the new directory name, but the actual constant and runtime path is session-data (~/.claude/session-data/). Users following the documented test plan will not find the expected directory.

Confidence Score: 4/5

Safe to merge with one targeted fix: reconcile the ecc-sessions vs session-data naming across the PR description, title, and test plan.

The two core bug fixes are correctly implemented and the prior P0 double-JSON-write concern is resolved. Remaining comments are non-blocking P2s with no data-loss, security, or runtime correctness issues.

Both files are low-risk; only the naming discrepancy between the PR description and the actual session-data constant needs attention.

Important Files Changed

Filename Overview
scripts/hooks/session-start.js Rewrites hook to collect all context into a single hookSpecificOutput JSON envelope and adds dedupeRecentSessions for cross-directory deduplication. The core fix (single JSON payload) is correct; minor concern around always emitting the payload even when additionalContext is empty.
scripts/lib/utils.js Renames the canonical sessions directory to session-data, adds legacy sessions dir for migration, and introduces sanitizeSessionId with Windows-reserved-name handling and non-ASCII disambiguation via SHA-256 suffix. The PR description says the new directory is ecc-sessions but the constant is session-data — a documentation/title mismatch.

Reviews (3): Last reviewed commit: "refactor(session-start): dedupe sessions..." | Re-trigger Greptile

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

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)
scripts/lib/utils.js (1)

34-37: Extract 'ecc-sessions' into a shared constant/config key.

This works functionally, but the directory name is now a magic string. Pull it into a module-level constant (or config) to avoid drift across hook scripts.

Proposed refactor
+const ECC_SESSIONS_DIRNAME = 'ecc-sessions';
+
 function getSessionsDir() {
   // Use 'ecc-sessions' instead of 'sessions' to avoid Claude Code's built-in
   // session directory cleanup which removes files written by session-end hooks.
   // See: https://github.com/affaan-m/everything-claude-code/issues/843
-  return path.join(getClaudeDir(), 'ecc-sessions');
+  return path.join(getClaudeDir(), ECC_SESSIONS_DIRNAME);
 }

As per coding guidelines, "Do not use hardcoded values; use constants or configuration instead".

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

In `@scripts/lib/utils.js` around lines 34 - 37, Replace the magic string
'ecc-sessions' with a shared constant: add a module-level constant (e.g., const
ECC_SESSIONS_DIR = 'ecc-sessions') or import it from a config module, then use
path.join(getClaudeDir(), ECC_SESSIONS_DIR) in the function that returns the
sessions path; update any other scripts that rely on the same literal to use the
new constant to avoid drift (reference the return using getClaudeDir() in
scripts/lib/utils.js).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/hooks/session-start.js`:
- Around line 54-55: Replace the forced process.exit(0) calls so stdout can
flush after writing the JSON payload: locate the occurrences of process.exit(0)
in scripts/hooks/session-start.js (the two places referenced in the review) and
change the first one to return (so the function returns and lets the event loop
finish flushing output) and change the second one to set process.exitCode = 0
(so the process exits with success without forcing immediate termination); keep
the JSON write (process.stdout.write(JSON.stringify(payload) + '\n')) unchanged.
- Around line 95-102: The hook currently writes two separate JSON objects via
process.stdout.write (one for the previous session summary and one as
projPayload with hookSpecificOutput), so consolidate by accumulating all
additionalContext strings into a single hookSpecificOutput.additionalContext
array (e.g., collect the previous session summary context and the detected
project information into the same array) and then emit one JSON object once all
contexts are gathered by calling
process.stdout.write(JSON.stringify(projPayload) + '\n'); update
projPayload.hookSpecificOutput to hold an array instead of a single string so
both contexts are included in the single output.

---

Nitpick comments:
In `@scripts/lib/utils.js`:
- Around line 34-37: Replace the magic string 'ecc-sessions' with a shared
constant: add a module-level constant (e.g., const ECC_SESSIONS_DIR =
'ecc-sessions') or import it from a config module, then use
path.join(getClaudeDir(), ECC_SESSIONS_DIR) in the function that returns the
sessions path; update any other scripts that rely on the same literal to use the
new constant to avoid drift (reference the return using getClaudeDir() in
scripts/lib/utils.js).
🪄 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: 7af093f4-853f-428a-9d06-8d9b72fe4f5b

📥 Commits

Reviewing files that changed from the base of the PR and between 678fb6f and 2115d67.

📒 Files selected for processing (2)
  • scripts/hooks/session-start.js
  • scripts/lib/utils.js

@affaan-m
Copy link
Copy Markdown
Owner

thanks for the PR. quick triage: maintainer review pending. if there are docs, screenshots, or repro steps, please drop them here.

…envelope

- Rename sessions dir from 'sessions' to 'ecc-sessions' to avoid
  Claude Code's built-in cleanup which deletes session-end hook files
- Emit exactly one JSON object with hookSpecificOutput at the end
  (was writing two separate JSON objects in the happy path)
- Accumulate all context into one additionalContext string
- Fixes CodeRabbit review comments on PR affaan-m#948
…t end

Resolves conflict markers from linter re-introducing broken early-JSON pattern.
Both the session content and project detection blocks now defer to the
emitContext() call at the end, which produces exactly one JSON envelope.

Parent commit: 25a1c45
…ns dirs

- Use dedupeRecentSessions() to merge results from both session directories
  without duplicating by filename; canonical dir wins on tie
- Replace inline getSessionSearchDirs with explicit import
- Swap process.stdout.write for writeSessionStartPayload() Promise wrapper
  to handle stream errors gracefully
- Align with linter auto-fix on PR affaan-m#948 review
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="scripts/lib/utils.js">

<violation number="1" location="scripts/lib/utils.js:17">
P2: Backward compatibility regression: previous `~/.claude/ecc-sessions` data is no longer searched after directory rename, so existing sessions may appear missing after upgrade.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@affaan-m
Copy link
Copy Markdown
Owner

Closing as already landed on current main. The session-start payload/session-data work and the utils session-directory/sanitization changes from this PR are already present upstream in scripts/hooks/session-start.js and scripts/lib/utils.js, so keeping this branch open would duplicate code that has already shipped.

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.

session-manager scripts don't work

2 participants