* docs: add folder index generator plan RFC for auto-generating folder-level CLAUDE.md files with observation timelines. Includes IDE symlink support and root CLAUDE.md integration. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: implement folder index generator (Phase 1) Add automatic CLAUDE.md generation for folders containing observed files. This enables IDE context providers to access relevant memory observations. Core modules: - FolderDiscovery: Extract folders from observation file paths - FolderTimelineCompiler: Compile chronological timeline per folder - ClaudeMdGenerator: Write CLAUDE.md with tag-based content replacement - FolderIndexOrchestrator: Coordinate regeneration on observation save Integration: - Event-driven regeneration after observation save in ResponseProcessor - HTTP endpoints for folder discovery, timeline, and manual generation - Settings for enabling/configuring folder index behavior The <claude-mem-context> tag wrapping ensures: - Manual CLAUDE.md content is preserved - Auto-generated content won't be recursively observed - Clean separation between user and system content 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add updateFolderClaudeMd function to CursorHooksInstaller Adds function to update CLAUDE.md files for folders touched by observations. Uses existing /api/search/by-file endpoint, preserves content outside <claude-mem-context> tags, and writes atomically via temp file + rename. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: hook updateFolderClaudeMd into ResponseProcessor Calls updateFolderClaudeMd after observation save to update folder-level CLAUDE.md files. Uses fire-and-forget pattern with error logging. Extracts file paths from saved observations and workspace path from registry. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add timeline formatting for folder CLAUDE.md files Implements formatTimelineForClaudeMd function that transforms API response into compact markdown table format. Converts emojis to text labels, handles ditto marks for timestamps, and groups under "Recent" header. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: remove old folder-index implementation Deletes redundant folder-index services that were replaced by the simpler updateFolderClaudeMd approach in CursorHooksInstaller.ts. Removed: - src/services/folder-index/ directory (5 files) - FolderIndexRoutes.ts - folder-index settings from SettingsDefaultsManager - folder-index route registration from worker-service 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add worktree-aware project filtering for unified timelines Detect git worktrees and show both parent repo and worktree observations in the session start timeline. When running in a worktree, the context now includes observations from both projects, interleaved chronologically. - Add detectWorktree() utility to identify worktree directories - Add getProjectContext() to return parent + worktree projects - Update context hook to pass multi-project queries - Add queryObservationsMulti() and querySummariesMulti() for IN clauses - Maintain backward compatibility with single-project queries 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> * fix: restructure logging to prove session correctness and reduce noise Add critical logging at each stage of the session lifecycle to prove the session ID chain (contentSessionId → sessionDbId → memorySessionId) stays aligned. New logs include CREATED, ENQUEUED, CLAIMED, MEMORY_ID_CAPTURED, STORING, and STORED. Move intermediate migration and backfill progress logs to DEBUG level to reduce noise, keeping only essential initialization and completion logs at INFO level. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> * refactor: extract folder CLAUDE.md utils to shared location Moves folder CLAUDE.md utilities from CursorHooksInstaller to a new shared utils file. Removes Cursor registry dependency - file paths from observations are already absolute, no workspace lookup needed. New file: src/utils/claude-md-utils.ts - replaceTaggedContent() - preserves user content outside tags - writeClaudeMdToFolder() - atomic writes with tag preservation - formatTimelineForClaudeMd() - API response to compact markdown - updateFolderClaudeMdFiles() - orchestrates folder updates 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: trigger folder CLAUDE.md updates when observations are saved The folder CLAUDE.md update was previously only triggered in syncAndBroadcastSummary, but summaries run with observationCount=0 (observations are saved separately). Moved the update logic to syncAndBroadcastObservations where file paths are available. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * all the claudes * test: add unit tests for claude-md-utils pure functions Add 11 tests covering replaceTaggedContent and formatTimelineForClaudeMd: - replaceTaggedContent: empty content, tag replacement, appending, partial tags - formatTimelineForClaudeMd: empty input, parsing, ditto marks, session IDs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: add integration tests for file operation functions Add 9 tests for writeClaudeMdToFolder and updateFolderClaudeMdFiles: - writeClaudeMdToFolder: folder creation, content preservation, nested dirs, atomic writes - updateFolderClaudeMdFiles: empty skip, fetch/write, deduplication, error handling 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: add unit tests for timeline-formatting utilities Add 14 tests for extractFirstFile and groupByDate functions: - extractFirstFile: relative paths, fallback to files_read, null handling, invalid JSON - groupByDate: empty arrays, date grouping, chronological sorting, item preservation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore: rebuild plugin scripts with merged features * docs: add project-specific CLAUDE.md with architecture and development notes * fix: exclude project root from auto-generated CLAUDE.md updates Skip folders containing .git directory when auto-updating subfolder CLAUDE.md files. This ensures: 1. Root CLAUDE.md remains user-managed and untouched by the system 2. SessionStart context injection stays pristine throughout the session 3. Subfolder CLAUDE.md files continue to receive live context updates 4. Cleaner separation between user-authored root docs and auto-generated folder indexes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: prevent crash from resuming stale SDK sessions on worker restart When the worker restarts, it was incorrectly passing the `resume` parameter to INIT prompts (lastPromptNumber=1) when a memorySessionId existed from a previous SDK session. This caused "Claude Code process exited with code 1" crashes because the SDK tried to resume into a session that no longer exists. Root cause: The resume condition only checked `hasRealMemorySessionId` but did not verify that this was a CONTINUATION prompt (lastPromptNumber > 1). Fix: Add `session.lastPromptNumber > 1` check to the resume condition: - Before: `...(hasRealMemorySessionId && { resume: session.memorySessionId })` - After: `...(hasRealMemorySessionId && session.lastPromptNumber > 1 && { resume: ... })` Also added: - Enhanced debug logging that warns when skipping resume for INIT prompts - Unit tests in tests/sdk-agent-resume.test.ts (9 test cases) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: properly handle Chroma MCP connection errors Previously, ensureCollection() caught ALL errors from chroma_get_collection_info and assumed they meant "collection doesn't exist", triggering unnecessary collection creation attempts. Connection errors like "Not connected" or "MCP error -32000: Connection closed" would cascade into failed creation attempts. Similarly, queryChroma() would silently return empty results when the MCP call failed, masking the underlying connection problem. Changes: - ensureCollection(): Detect connection errors and re-throw immediately instead of attempting collection creation - queryChroma(): Wrap MCP call in try-catch and throw connection errors instead of returning empty results - Both methods reset connection state (connected=false, client=null) on connection errors so subsequent operations can attempt to reconnect 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * pushed * fix: scope regenerate-claude-md.ts to current working directory Critical bug fix: The script was querying ALL observations from the database across ALL projects ever recorded (1396+ folders), then attempting to write CLAUDE.md files everywhere including other projects, non-existent paths, and ignored directories. Changes: - Use git ls-files to discover folders (respects .gitignore automatically) - Filter database query to current project only (by folder name) - Use relative paths for database queries (matches storage format) - Add --clean flag to remove auto-generated CLAUDE.md files - Add fallback directory walker for non-git repos Now correctly scopes to 26 folders with observations instead of 1396+. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs and adjustments * fix: cleanup mode strips tags instead of deleting files blindly The cleanup mode was incorrectly deleting entire files that contained <claude-mem-context> tags. The correct behavior (per original design): 1. Strip the <claude-mem-context>...</claude-mem-context> section 2. If empty after stripping → delete the file 3. If has remaining content → save the stripped version Now properly preserves user content in CLAUDE.md files while removing only the auto-generated sections. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * deleted some files * chore: regenerate folder CLAUDE.md files with fixed script Regenerated 23 folder CLAUDE.md files using the corrected script that: - Scopes to current working directory only - Uses git ls-files to respect .gitignore - Filters by project name 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Update CLAUDE.md files for January 5, 2026 - Regenerated and staged 23 CLAUDE.md files with a mix of new and modified content. - Fixed cleanup mode to properly strip tags instead of deleting files blindly. - Cleaned up empty CLAUDE.md files from various directories, including ~/.claude and ~/Scripts. - Conducted dry-run cleanup that identified a significant reduction in auto-generated CLAUDE.md files. - Removed the isAutoGeneratedClaudeMd function due to incorrect file deletion behavior. * feat: use settings for observation limit in batch regeneration script Replace hard-coded limit of 10 with configurable CLAUDE_MEM_CONTEXT_OBSERVATIONS setting (default: 50). This allows users to control how many observations appear in folder CLAUDE.md files. Changes: - Import SettingsDefaultsManager and load settings at script startup - Use OBSERVATION_LIMIT constant derived from settings at both call sites - Remove stale default parameter from findObservationsByFolder function 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: use settings for observation limit in event-driven updates Replace hard-coded limit of 10 in updateFolderClaudeMdFiles with configurable CLAUDE_MEM_CONTEXT_OBSERVATIONS setting (default: 50). Changes: - Import SettingsDefaultsManager and os module - Load settings at function start (once, not in loop) - Use limit from settings in API call 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: Implement configurable observation limits and enhance search functionality - Added configurable observation limits to batch regeneration scripts. - Enhanced SearchManager to handle folder queries and normalize parameters. - Introduced methods to check for direct child files in observations and sessions. - Updated SearchOptions interface to include isFolder flag for filtering. - Improved code quality with comprehensive reviews and anti-pattern checks. - Cleaned up auto-generated CLAUDE.md files across various directories. - Documented recent changes and improvements in CLAUDE.md files. * build asset * Project Context from Claude-Mem auto-added (can be auto removed at any time) * CLAUDE.md updates * fix: resolve CLAUDE.md files to correct directory in worktree setups When using git worktrees, CLAUDE.md files were being written relative to the worker's process.cwd() instead of the actual project directory. This fix threads the project's cwd from message processing through to the file writing utilities, ensuring CLAUDE.md files are created in the correct project directory regardless of where the worker was started. Changes: - Add projectRoot parameter to updateFolderClaudeMdFiles for path resolution - Thread projectRoot through ResponseProcessor call chain - Track lastCwd from messages in SDKAgent, GeminiAgent, OpenRouterAgent - Add tests for relative/absolute path handling with projectRoot 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * more project context updates * context updates * planning context * feat: add CLI infrastructure for unified hook architecture (Phase 1) - Add src/cli/types.ts with NormalizedHookInput, HookResult, PlatformAdapter, EventHandler interfaces - Add src/cli/stdin-reader.ts with readJsonFromStdin() extracted from save-hook.ts pattern 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add platform adapters for unified hook CLI (Phase 2) - Add claude-code adapter mapping session_id, tool_name, etc. - Add cursor adapter mapping conversation_id, workspace_roots, result_json - Add raw adapter for testing/passthrough - Add getPlatformAdapter() factory function 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add event handlers for unified hook CLI (Phase 3) - Add context handler (GET /api/context/inject) - Add session-init handler (POST /api/sessions/init) - Add observation handler (POST /api/sessions/observations) - Add summarize handler (POST /api/sessions/summarize) - Add user-message handler (stderr output, exit code 3) - Add file-edit handler for Cursor afterFileEdit events - Add getEventHandler() factory function All handlers copy exact HTTP calls from original hooks. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add hook command dispatcher (Phase 4) - Add src/cli/hook-command.ts dispatching stdin to adapters and handlers - Add 'hook' case to worker-service.ts CLI switch - Usage: bun worker-service.cjs hook <platform> <event> 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: update hooks.json to use unified CLI (Phase 5) - Change context-hook.js to: hook claude-code context - Change new-hook.js to: hook claude-code session-init - Change save-hook.js to: hook claude-code observation - Change summary-hook.js to: hook claude-code summarize - Change user-message-hook.js to: hook claude-code user-message All hooks now route through unified CLI: bun worker-service.cjs hook <platform> <event> 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: update Cursor integration to use unified CLI (Phase 6) - Update CursorHooksInstaller to generate unified CLI commands - Use node instead of shell scripts for Cursor hooks - Add platform field to NormalizedHookInput for handler decisions - Skip SDK agent init for Cursor platform (not supported) - Fix file-edit handler to use 'write_file' tool name Cursor event mapping: - beforeSubmitPrompt → session-init, context - afterMCPExecution/afterShellExecution → observation - afterFileEdit → file-edit - stop → summarize 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: remove old hook files, complete unified CLI migration (Phase 7) Deleted files: - src/hooks/context-hook.ts, new-hook.ts, save-hook.ts, summary-hook.ts, user-message-hook.ts - cursor-hooks/*.sh and *.ps1 shell scripts - plugin/scripts/*-hook.js built files Modified: - scripts/build-hooks.js: removed hook build loop Build now produces only: worker-service.cjs, mcp-server.cjs, context-generator.cjs All hooks route through: bun worker-service.cjs hook <platform> <event> 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: handle undefined stdin in platform adapters for SessionStart hooks SessionStart hooks don't receive stdin data from Claude Code, causing the adapters to crash when trying to access properties on undefined. Added null coalescing to handle empty input gracefully. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * context update * fix: replace deprecated WMIC with PowerShell for Windows 11 compatibility Fixes #625 Changes: - Replace WMIC process queries with PowerShell Get-Process and Get-CimInstance - WMIC is deprecated in Windows 11 and causes terminal tab accumulation - PowerShell provides simpler output format (just PIDs, not "ProcessId=1234") - Update tests to match new PowerShell output parsing logic Benefits: - Windows 11 compatibility (WMIC removal planned) - Fixes terminal window accumulation issue on Windows - Cleaner, more maintainable parsing logic - Same security validation (PID > 0, integer checks) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix: graceful exit strategy to prevent Windows Terminal tab accumulation (#625) Problem: Windows Terminal keeps tabs open when processes exit with code 1, leading to tab accumulation during worker lifecycle operations (start, stop, restart, version mismatches, port conflicts). This created a poor user experience with dozens of orphaned terminal tabs. Solution: Implemented graceful exit strategy using exit code 0 for all expected failure scenarios. The wrapper and plugin handle restart logic, so child processes don't need to signal failure with non-zero exit codes. Key Changes: - worker-service.ts: Changed all process.exit(1) to process.exit(0) in: - Port conflict scenarios - Version mismatch recovery - Daemon spawn failures - Health check timeouts - Restart failures - Worker startup errors - mcp-server.ts: Changed fatal error exit from 1 to 0 - ProcessManager.ts: Changed signal handler error exit from 1 to 0 - hook-command.ts: Changed hook error exit code from 1 to 2 (BLOCKING_ERROR) to ensure users see error messages (per Claude Code docs, exit 1 only shows in verbose mode) All exits include explanatory comments documenting the graceful exit strategy. Fixes #625 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * docs: update CLAUDE.md activity logs Auto-generated context updates from work on issue #625: - Windows 11 WMIC migration - PowerShell process enumeration - Graceful exit strategy implementation - PR creation for Windows Terminal tab fix Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * docs: update activity logs in CLAUDE.md files across multiple directories * chore: update CLAUDE.md files with recent activity and documentation improvements - Adjusted dates and entries in CLAUDE.md for various components including reports and services. - Added detailed activity logs for worker services, CLI commands, and server interactions. - Documented exit code strategy in CLAUDE.md to clarify graceful exit philosophy. - Extracted PowerShell timeout constant and updated related documentation and tests. - Enhanced log level audit strategy and clarified logging patterns across services. * polish: extract PowerShell timeout constant and document exit code strategy - Extract magic number 60000ms to HOOK_TIMEOUTS.POWERSHELL_COMMAND (10000ms) - Reduce PowerShell timeout from 60s to 10s per review feedback - Document exit code strategy in CLAUDE.md - Add test coverage for new constant Addresses review feedback from PR #628 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * build assets --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+13
-105
@@ -3,126 +3,34 @@
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Oct 25, 2025
|
||||
### Dec 10, 2025
|
||||
|
||||
**settings-cli.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #2415 | 4:06 PM | 🟣 | Settings System with Multi-Interface Configuration Management | ~516 |
|
||||
| #2414 | " | 🟣 | Multi-Interface Settings System Implementation | ~498 |
|
||||
| #2413 | " | 🟣 | Settings System with Schema, Service, and CLI Implementation | ~428 |
|
||||
| #23825 | 11:12 PM | ✅ | Worker Port Set to 38888 for Migration Phase | ~283 |
|
||||
| #23824 | " | 🔵 | Worker Port Sourced from getWorkerPort() Utility | ~247 |
|
||||
| #23816 | 10:52 PM | 🟣 | Worker CLI Command Interface Created | ~325 |
|
||||
|
||||
### Dec 11, 2025
|
||||
|
||||
**worker-cli.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #24076 | 3:16 PM | 🔵 | 45 files modified with nearly equal additions and deletions during Bun migration | ~443 |
|
||||
| #24061 | 2:58 PM | 🔴 | Added explicit process.exit() to worker CLI commands | ~274 |
|
||||
| #23961 | 1:59 PM | 🔵 | CLI Module Contains Worker Command Interface | ~205 |
|
||||
| #23949 | 1:45 PM | 🔴 | CLI Worker Management Unified to Settings-Based Port | ~373 |
|
||||
| #23947 | 1:40 PM | 🔵 | Comprehensive Port Configuration Audit Complete | ~532 |
|
||||
| #23939 | 1:38 PM | 🔵 | worker-cli.ts Hardcoded MIGRATION_PORT Usage | ~372 |
|
||||
| #23934 | 1:36 PM | 🔵 | Port 38888 Hardcoded in Two Migration Files | ~341 |
|
||||
| #23933 | " | 🔵 | Comprehensive Port 37777 References Across Documentation and Code | ~427 |
|
||||
| #23931 | " | 🔵 | Comprehensive Port Usage Mapping Across Codebase | ~449 |
|
||||
|
||||
**cli**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #23986 | 2:04 PM | 🔵 | Code Quality Audit Completed - 25 Issues Identified Across Six Principles | ~602 |
|
||||
| #24060 | 2:58 PM | 🔴 | Worker CLI Start Command Exit Behavior Fixed | ~232 |
|
||||
|
||||
### Dec 12, 2025
|
||||
|
||||
**worker-cli.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #24394 | 7:24 PM | 🟣 | Phase 6 Agent Completed Git Commit and PR Update | ~444 |
|
||||
| #24391 | " | ✅ | Committed Complete Migration from better-sqlite3 to bun:sqlite | ~436 |
|
||||
| #24390 | 7:23 PM | ✅ | Staged All 19 Modified Files for Git Commit | ~301 |
|
||||
| #24389 | " | 🟣 | Phase 5 Agent Completed Full System Verification | ~726 |
|
||||
| #24388 | 7:22 PM | 🟣 | Phase 5 Complete - All Verification Passed for Production Deployment | ~600 |
|
||||
| #24387 | " | 🔵 | Uncommitted Changes Identified Across Documentation and Core Services | ~368 |
|
||||
| #24369 | 7:10 PM | 🔵 | Worker CLI Process Management Architecture | ~242 |
|
||||
| #24328 | 6:51 PM | 🔵 | Worker CLI Switch Statement Structure Confirmed | ~634 |
|
||||
| #24327 | " | ⚖️ | Exploration Phase Plan for PR #248 Fixes | ~695 |
|
||||
| #24324 | 6:50 PM | ⚖️ | Pre-Merge Scope Definition and Implementation Path | ~651 |
|
||||
| #24322 | 6:49 PM | 🔵 | Missing Break Statement in Worker CLI Switch Case | ~542 |
|
||||
| #24320 | " | ⚖️ | PR #248 Issue Prioritization Strategy | ~616 |
|
||||
| #24319 | 6:48 PM | 🔵 | PR #248 Review Status: PM2 to Bun Migration Assessment | ~736 |
|
||||
| #24276 | 5:20 PM | 🔵 | worker-cli.ts provides command-line interface for worker management | ~529 |
|
||||
| #24359 | 7:00 PM | 🟣 | Phase 1 Critical Code Fixes Completed via Agent Task | ~441 |
|
||||
| #24358 | 6:59 PM | ✅ | Completed Phase 1 Code Fixes for better-sqlite3 Migration | ~385 |
|
||||
| #24348 | 6:57 PM | 🔴 | Added Defensive Break Statement to worker-cli.ts Restart Case | ~269 |
|
||||
| #24345 | " | 🔵 | worker-cli.ts Missing Break Statement in Switch Case | ~318 |
|
||||
|
||||
### Dec 13, 2025
|
||||
### Dec 14, 2025
|
||||
|
||||
**worker-cli.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #25370 | 9:26 PM | 🔵 | Worker CLI Process Management Interface | ~429 |
|
||||
| #25343 | 9:17 PM | 🔵 | Console.error Used for Migration Progress and Error Logging | ~451 |
|
||||
| #25321 | 9:12 PM | 🔵 | Console.error Usage Found in 29 Files | ~366 |
|
||||
| #24757 | 4:46 PM | 🔵 | Worker CLI Provides Direct Interface to ProcessManager | ~342 |
|
||||
|
||||
### Dec 16, 2025
|
||||
|
||||
**worker-cli.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #27825 | 6:56 PM | 🔵 | CLI Commands Rely on ProcessManager PID File Methods | ~301 |
|
||||
| #27373 | 3:15 PM | 🔵 | Worker CLI Command Interface | ~331 |
|
||||
|
||||
### Dec 17, 2025
|
||||
|
||||
**worker-cli.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #28930 | 7:30 PM | 🔵 | Worker CLI Distribution and Build System | ~275 |
|
||||
| #28929 | " | 🔵 | ProcessManager Usage Across Codebase | ~319 |
|
||||
|
||||
### Dec 21, 2025
|
||||
|
||||
**worker-cli.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #31603 | 8:21 PM | 🔵 | Complete Console.* Statement Audit Across Codebase | ~813 |
|
||||
| #31599 | 8:19 PM | 🔵 | 136 console logging statements found in TypeScript source files | ~538 |
|
||||
|
||||
### Dec 24, 2025
|
||||
|
||||
**worker-cli.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #32344 | 8:42 PM | 🔵 | Timeline Reveals Cleanup Hook Caused Data Loss via Premature SDK Agent Termination | ~627 |
|
||||
| #32317 | 8:41 PM | 🔄 | Removed unreachable break statement from worker CLI start command | ~204 |
|
||||
| #32191 | 7:38 PM | 🔴 | Worker CLI now outputs HOOK_STANDARD_RESPONSE format | ~277 |
|
||||
| #32189 | 7:37 PM | 🔴 | Worker CLI Now Outputs Standard Hook Response Format | ~306 |
|
||||
| #32068 | 3:23 PM | 🔵 | Worker CLI Supports Start/Stop/Restart/Status Commands | ~275 |
|
||||
|
||||
### Dec 25, 2025
|
||||
|
||||
**worker-cli.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #32456 | 5:41 PM | ✅ | Completed merge of main branch into feature/titans-phase1-3 | ~354 |
|
||||
|
||||
### Dec 26, 2025
|
||||
|
||||
**worker-cli.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #32855 | 7:04 PM | 🔄 | Consolidated worker process management into single service | ~322 |
|
||||
|
||||
### Dec 28, 2025
|
||||
|
||||
**worker-cli.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #33370 | 3:47 PM | 🔵 | ToxMox Wrapper Architecture Deleted December 26, Six Days After Implementation | ~506 |
|
||||
| #33284 | 3:07 PM | 🔄 | Consolidated Worker Lifecycle Management (-580 Lines) | ~327 |
|
||||
|
||||
### Jan 5, 2026
|
||||
|
||||
**types.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #38071 | 9:52 PM | 🟣 | Unified CLI Phase 1 - Infrastructure Foundation with Type System and Stdin Reader | ~451 |
|
||||
| #26766 | 11:30 PM | ⚖️ | Root Cause Identified: Missing Post-Install Worker Restart Trigger in Plugin Update Flow | ~604 |
|
||||
| #26722 | 11:23 PM | 🔵 | Worker CLI TypeScript Source Shows Simple ProcessManager Delegation | ~394 |
|
||||
| #26721 | " | 🔵 | Worker CLI Source Code Shows Simple Restart Logic Without Delays | ~425 |
|
||||
</claude-mem-context>
|
||||
@@ -0,0 +1,7 @@
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
*No recent activity*
|
||||
</claude-mem-context>
|
||||
@@ -0,0 +1,24 @@
|
||||
import type { PlatformAdapter, NormalizedHookInput, HookResult } from '../types.js';
|
||||
|
||||
// Maps Claude Code stdin format (session_id, cwd, tool_name, etc.)
|
||||
// SessionStart hooks receive no stdin, so we must handle undefined input gracefully
|
||||
export const claudeCodeAdapter: PlatformAdapter = {
|
||||
normalizeInput(raw) {
|
||||
const r = (raw ?? {}) as any;
|
||||
return {
|
||||
sessionId: r.session_id,
|
||||
cwd: r.cwd ?? process.cwd(),
|
||||
prompt: r.prompt,
|
||||
toolName: r.tool_name,
|
||||
toolInput: r.tool_input,
|
||||
toolResponse: r.tool_response,
|
||||
transcriptPath: r.transcript_path,
|
||||
};
|
||||
},
|
||||
formatOutput(result) {
|
||||
if (result.hookSpecificOutput) {
|
||||
return { hookSpecificOutput: result.hookSpecificOutput };
|
||||
}
|
||||
return { continue: result.continue ?? true, suppressOutput: result.suppressOutput ?? true };
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
import type { PlatformAdapter, NormalizedHookInput, HookResult } from '../types.js';
|
||||
|
||||
// Maps Cursor stdin format - field names differ from Claude Code
|
||||
// Cursor uses: conversation_id, workspace_roots[], result_json, command/output
|
||||
// Handle undefined input gracefully for hooks that don't receive stdin
|
||||
export const cursorAdapter: PlatformAdapter = {
|
||||
normalizeInput(raw) {
|
||||
const r = (raw ?? {}) as any;
|
||||
// Cursor-specific: shell commands come as command/output instead of tool_name/input/response
|
||||
const isShellCommand = !!r.command && !r.tool_name;
|
||||
return {
|
||||
sessionId: r.conversation_id || r.generation_id, // conversation_id preferred
|
||||
cwd: r.workspace_roots?.[0] ?? process.cwd(), // First workspace root
|
||||
prompt: r.prompt,
|
||||
toolName: isShellCommand ? 'Bash' : r.tool_name,
|
||||
toolInput: isShellCommand ? { command: r.command } : r.tool_input,
|
||||
toolResponse: isShellCommand ? { output: r.output } : r.result_json, // result_json not tool_response
|
||||
transcriptPath: undefined, // Cursor doesn't provide transcript
|
||||
// Cursor-specific fields for file edits
|
||||
filePath: r.file_path,
|
||||
edits: r.edits,
|
||||
};
|
||||
},
|
||||
formatOutput(result) {
|
||||
// Cursor expects simpler response - just continue flag
|
||||
return { continue: result.continue ?? true };
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
import type { PlatformAdapter } from '../types.js';
|
||||
import { claudeCodeAdapter } from './claude-code.js';
|
||||
import { cursorAdapter } from './cursor.js';
|
||||
import { rawAdapter } from './raw.js';
|
||||
|
||||
export function getPlatformAdapter(platform: string): PlatformAdapter {
|
||||
switch (platform) {
|
||||
case 'claude-code': return claudeCodeAdapter;
|
||||
case 'cursor': return cursorAdapter;
|
||||
case 'raw': return rawAdapter;
|
||||
default: throw new Error(`Unknown platform: ${platform}`);
|
||||
}
|
||||
}
|
||||
|
||||
export { claudeCodeAdapter, cursorAdapter, rawAdapter };
|
||||
@@ -0,0 +1,22 @@
|
||||
import type { PlatformAdapter, NormalizedHookInput, HookResult } from '../types.js';
|
||||
|
||||
// Raw adapter passes through with minimal transformation - useful for testing
|
||||
export const rawAdapter: PlatformAdapter = {
|
||||
normalizeInput(raw) {
|
||||
const r = raw as any;
|
||||
return {
|
||||
sessionId: r.sessionId ?? r.session_id ?? 'unknown',
|
||||
cwd: r.cwd ?? process.cwd(),
|
||||
prompt: r.prompt,
|
||||
toolName: r.toolName ?? r.tool_name,
|
||||
toolInput: r.toolInput ?? r.tool_input,
|
||||
toolResponse: r.toolResponse ?? r.tool_response,
|
||||
transcriptPath: r.transcriptPath ?? r.transcript_path,
|
||||
filePath: r.filePath ?? r.file_path,
|
||||
edits: r.edits,
|
||||
};
|
||||
},
|
||||
formatOutput(result) {
|
||||
return result;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
*No recent activity*
|
||||
</claude-mem-context>
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Context Handler - SessionStart
|
||||
*
|
||||
* Extracted from context-hook.ts - calls worker to generate context.
|
||||
* Returns context as hookSpecificOutput for Claude Code to inject.
|
||||
*/
|
||||
|
||||
import type { EventHandler, NormalizedHookInput, HookResult } from '../types.js';
|
||||
import { ensureWorkerRunning, getWorkerPort } from '../../shared/worker-utils.js';
|
||||
import { getProjectContext } from '../../utils/project-name.js';
|
||||
|
||||
export const contextHandler: EventHandler = {
|
||||
async execute(input: NormalizedHookInput): Promise<HookResult> {
|
||||
// Ensure worker is running before any other logic
|
||||
await ensureWorkerRunning();
|
||||
|
||||
const cwd = input.cwd ?? process.cwd();
|
||||
const context = getProjectContext(cwd);
|
||||
const port = getWorkerPort();
|
||||
|
||||
// Pass all projects (parent + worktree if applicable) for unified timeline
|
||||
const projectsParam = context.allProjects.join(',');
|
||||
const url = `http://127.0.0.1:${port}/api/context/inject?projects=${encodeURIComponent(projectsParam)}`;
|
||||
|
||||
// Note: Removed AbortSignal.timeout due to Windows Bun cleanup issue (libuv assertion)
|
||||
// Worker service has its own timeouts, so client-side timeout is redundant
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Context generation failed: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.text();
|
||||
const additionalContext = result.trim();
|
||||
|
||||
return {
|
||||
hookSpecificOutput: {
|
||||
hookEventName: 'SessionStart',
|
||||
additionalContext
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* File Edit Handler - Cursor-specific afterFileEdit
|
||||
*
|
||||
* Handles file edit observations from Cursor IDE.
|
||||
* Similar to observation handler but with file-specific metadata.
|
||||
*/
|
||||
|
||||
import type { EventHandler, NormalizedHookInput, HookResult } from '../types.js';
|
||||
import { ensureWorkerRunning, getWorkerPort } from '../../shared/worker-utils.js';
|
||||
import { logger } from '../../utils/logger.js';
|
||||
|
||||
export const fileEditHandler: EventHandler = {
|
||||
async execute(input: NormalizedHookInput): Promise<HookResult> {
|
||||
// Ensure worker is running before any other logic
|
||||
await ensureWorkerRunning();
|
||||
|
||||
const { sessionId, cwd, filePath, edits } = input;
|
||||
|
||||
if (!filePath) {
|
||||
throw new Error('fileEditHandler requires filePath');
|
||||
}
|
||||
|
||||
const port = getWorkerPort();
|
||||
|
||||
logger.dataIn('HOOK', `FileEdit: ${filePath}`, {
|
||||
workerPort: port,
|
||||
editCount: edits?.length ?? 0
|
||||
});
|
||||
|
||||
// Validate required fields before sending to worker
|
||||
if (!cwd) {
|
||||
throw new Error(`Missing cwd in FileEdit hook input for session ${sessionId}, file ${filePath}`);
|
||||
}
|
||||
|
||||
// Send to worker as an observation with file edit metadata
|
||||
// The observation handler on the worker will process this appropriately
|
||||
const response = await fetch(`http://127.0.0.1:${port}/api/sessions/observations`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
contentSessionId: sessionId,
|
||||
tool_name: 'write_file',
|
||||
tool_input: { filePath, edits },
|
||||
tool_response: { success: true },
|
||||
cwd
|
||||
})
|
||||
// Note: Removed signal to avoid Windows Bun cleanup issue (libuv assertion)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`File edit observation storage failed: ${response.status}`);
|
||||
}
|
||||
|
||||
logger.debug('HOOK', 'File edit observation sent successfully', { filePath });
|
||||
|
||||
return { continue: true, suppressOutput: true };
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Event Handler Factory
|
||||
*
|
||||
* Returns the appropriate handler for a given event type.
|
||||
*/
|
||||
|
||||
import type { EventHandler } from '../types.js';
|
||||
import { contextHandler } from './context.js';
|
||||
import { sessionInitHandler } from './session-init.js';
|
||||
import { observationHandler } from './observation.js';
|
||||
import { summarizeHandler } from './summarize.js';
|
||||
import { userMessageHandler } from './user-message.js';
|
||||
import { fileEditHandler } from './file-edit.js';
|
||||
|
||||
export type EventType =
|
||||
| 'context' // SessionStart - inject context
|
||||
| 'session-init' // UserPromptSubmit - initialize session
|
||||
| 'observation' // PostToolUse - save observation
|
||||
| 'summarize' // Stop - generate summary
|
||||
| 'user-message' // SessionStart (parallel) - display to user
|
||||
| 'file-edit'; // Cursor afterFileEdit
|
||||
|
||||
const handlers: Record<EventType, EventHandler> = {
|
||||
'context': contextHandler,
|
||||
'session-init': sessionInitHandler,
|
||||
'observation': observationHandler,
|
||||
'summarize': summarizeHandler,
|
||||
'user-message': userMessageHandler,
|
||||
'file-edit': fileEditHandler
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the event handler for a given event type.
|
||||
*
|
||||
* @param eventType The type of event to handle
|
||||
* @returns The appropriate EventHandler
|
||||
* @throws Error if event type is not recognized
|
||||
*/
|
||||
export function getEventHandler(eventType: EventType): EventHandler {
|
||||
const handler = handlers[eventType];
|
||||
if (!handler) {
|
||||
throw new Error(`Unknown event type: ${eventType}`);
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
// Re-export individual handlers for direct access if needed
|
||||
export { contextHandler } from './context.js';
|
||||
export { sessionInitHandler } from './session-init.js';
|
||||
export { observationHandler } from './observation.js';
|
||||
export { summarizeHandler } from './summarize.js';
|
||||
export { userMessageHandler } from './user-message.js';
|
||||
export { fileEditHandler } from './file-edit.js';
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Observation Handler - PostToolUse
|
||||
*
|
||||
* Extracted from save-hook.ts - sends tool usage to worker for storage.
|
||||
*/
|
||||
|
||||
import type { EventHandler, NormalizedHookInput, HookResult } from '../types.js';
|
||||
import { ensureWorkerRunning, getWorkerPort } from '../../shared/worker-utils.js';
|
||||
import { logger } from '../../utils/logger.js';
|
||||
|
||||
export const observationHandler: EventHandler = {
|
||||
async execute(input: NormalizedHookInput): Promise<HookResult> {
|
||||
// Ensure worker is running before any other logic
|
||||
await ensureWorkerRunning();
|
||||
|
||||
const { sessionId, cwd, toolName, toolInput, toolResponse } = input;
|
||||
|
||||
if (!toolName) {
|
||||
throw new Error('observationHandler requires toolName');
|
||||
}
|
||||
|
||||
const port = getWorkerPort();
|
||||
|
||||
const toolStr = logger.formatTool(toolName, toolInput);
|
||||
|
||||
logger.dataIn('HOOK', `PostToolUse: ${toolStr}`, {
|
||||
workerPort: port
|
||||
});
|
||||
|
||||
// Validate required fields before sending to worker
|
||||
if (!cwd) {
|
||||
throw new Error(`Missing cwd in PostToolUse hook input for session ${sessionId}, tool ${toolName}`);
|
||||
}
|
||||
|
||||
// Send to worker - worker handles privacy check and database operations
|
||||
const response = await fetch(`http://127.0.0.1:${port}/api/sessions/observations`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
contentSessionId: sessionId,
|
||||
tool_name: toolName,
|
||||
tool_input: toolInput,
|
||||
tool_response: toolResponse,
|
||||
cwd
|
||||
})
|
||||
// Note: Removed signal to avoid Windows Bun cleanup issue (libuv assertion)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Observation storage failed: ${response.status}`);
|
||||
}
|
||||
|
||||
logger.debug('HOOK', 'Observation sent successfully', { toolName });
|
||||
|
||||
return { continue: true, suppressOutput: true };
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Session Init Handler - UserPromptSubmit
|
||||
*
|
||||
* Extracted from new-hook.ts - initializes session and starts SDK agent.
|
||||
*/
|
||||
|
||||
import type { EventHandler, NormalizedHookInput, HookResult } from '../types.js';
|
||||
import { ensureWorkerRunning, getWorkerPort } from '../../shared/worker-utils.js';
|
||||
import { getProjectName } from '../../utils/project-name.js';
|
||||
import { logger } from '../../utils/logger.js';
|
||||
|
||||
export const sessionInitHandler: EventHandler = {
|
||||
async execute(input: NormalizedHookInput): Promise<HookResult> {
|
||||
// Ensure worker is running before any other logic
|
||||
await ensureWorkerRunning();
|
||||
|
||||
const { sessionId, cwd, prompt } = input;
|
||||
|
||||
if (!prompt) {
|
||||
throw new Error('sessionInitHandler requires prompt');
|
||||
}
|
||||
|
||||
const project = getProjectName(cwd);
|
||||
const port = getWorkerPort();
|
||||
|
||||
logger.debug('HOOK', 'session-init: Calling /api/sessions/init', { contentSessionId: sessionId, project });
|
||||
|
||||
// Initialize session via HTTP - handles DB operations and privacy checks
|
||||
const initResponse = await fetch(`http://127.0.0.1:${port}/api/sessions/init`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
contentSessionId: sessionId,
|
||||
project,
|
||||
prompt
|
||||
})
|
||||
// Note: Removed signal to avoid Windows Bun cleanup issue (libuv assertion)
|
||||
});
|
||||
|
||||
if (!initResponse.ok) {
|
||||
throw new Error(`Session initialization failed: ${initResponse.status}`);
|
||||
}
|
||||
|
||||
const initResult = await initResponse.json() as {
|
||||
sessionDbId: number;
|
||||
promptNumber: number;
|
||||
skipped?: boolean;
|
||||
reason?: string;
|
||||
};
|
||||
const sessionDbId = initResult.sessionDbId;
|
||||
const promptNumber = initResult.promptNumber;
|
||||
|
||||
logger.debug('HOOK', 'session-init: Received from /api/sessions/init', { sessionDbId, promptNumber, skipped: initResult.skipped });
|
||||
|
||||
// Debug-level alignment log for detailed tracing
|
||||
logger.debug('HOOK', `[ALIGNMENT] Hook Entry | contentSessionId=${sessionId} | prompt#=${promptNumber} | sessionDbId=${sessionDbId}`);
|
||||
|
||||
// Check if prompt was entirely private (worker performs privacy check)
|
||||
if (initResult.skipped && initResult.reason === 'private') {
|
||||
logger.info('HOOK', `INIT_COMPLETE | sessionDbId=${sessionDbId} | promptNumber=${promptNumber} | skipped=true | reason=private`, {
|
||||
sessionId: sessionDbId
|
||||
});
|
||||
return { continue: true, suppressOutput: true };
|
||||
}
|
||||
|
||||
// Only initialize SDK agent for Claude Code (not Cursor)
|
||||
// Cursor doesn't use the SDK agent - it only needs session/observation storage
|
||||
if (input.platform !== 'cursor' && sessionDbId) {
|
||||
// Strip leading slash from commands for memory agent
|
||||
// /review 101 -> review 101 (more semantic for observations)
|
||||
const cleanedPrompt = prompt.startsWith('/') ? prompt.substring(1) : prompt;
|
||||
|
||||
logger.debug('HOOK', 'session-init: Calling /sessions/{sessionDbId}/init', { sessionDbId, promptNumber });
|
||||
|
||||
// Initialize SDK agent session via HTTP (starts the agent!)
|
||||
const response = await fetch(`http://127.0.0.1:${port}/sessions/${sessionDbId}/init`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ userPrompt: cleanedPrompt, promptNumber })
|
||||
// Note: Removed signal to avoid Windows Bun cleanup issue (libuv assertion)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`SDK agent start failed: ${response.status}`);
|
||||
}
|
||||
} else if (input.platform === 'cursor') {
|
||||
logger.debug('HOOK', 'session-init: Skipping SDK agent init for Cursor platform', { sessionDbId, promptNumber });
|
||||
}
|
||||
|
||||
logger.info('HOOK', `INIT_COMPLETE | sessionDbId=${sessionDbId} | promptNumber=${promptNumber} | project=${project}`, {
|
||||
sessionId: sessionDbId
|
||||
});
|
||||
|
||||
return { continue: true, suppressOutput: true };
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Summarize Handler - Stop
|
||||
*
|
||||
* Extracted from summary-hook.ts - sends summary request to worker.
|
||||
* Transcript parsing stays in the hook because only the hook has access to
|
||||
* the transcript file path.
|
||||
*/
|
||||
|
||||
import type { EventHandler, NormalizedHookInput, HookResult } from '../types.js';
|
||||
import { ensureWorkerRunning, getWorkerPort } from '../../shared/worker-utils.js';
|
||||
import { logger } from '../../utils/logger.js';
|
||||
import { extractLastMessage } from '../../shared/transcript-parser.js';
|
||||
|
||||
export const summarizeHandler: EventHandler = {
|
||||
async execute(input: NormalizedHookInput): Promise<HookResult> {
|
||||
// Ensure worker is running before any other logic
|
||||
await ensureWorkerRunning();
|
||||
|
||||
const { sessionId, transcriptPath } = input;
|
||||
|
||||
const port = getWorkerPort();
|
||||
|
||||
// Validate required fields before processing
|
||||
if (!transcriptPath) {
|
||||
throw new Error(`Missing transcriptPath in Stop hook input for session ${sessionId}`);
|
||||
}
|
||||
|
||||
// Extract last assistant message from transcript (the work Claude did)
|
||||
// Note: "user" messages in transcripts are mostly tool_results, not actual user input.
|
||||
// The user's original request is already stored in user_prompts table.
|
||||
const lastAssistantMessage = extractLastMessage(transcriptPath, 'assistant', true);
|
||||
|
||||
logger.dataIn('HOOK', 'Stop: Requesting summary', {
|
||||
workerPort: port,
|
||||
hasLastAssistantMessage: !!lastAssistantMessage
|
||||
});
|
||||
|
||||
// Send to worker - worker handles privacy check and database operations
|
||||
const response = await fetch(`http://127.0.0.1:${port}/api/sessions/summarize`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
contentSessionId: sessionId,
|
||||
last_assistant_message: lastAssistantMessage
|
||||
})
|
||||
// Note: Removed signal to avoid Windows Bun cleanup issue (libuv assertion)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
// Return standard response even on failure (matches original behavior)
|
||||
return { continue: true, suppressOutput: true };
|
||||
}
|
||||
|
||||
logger.debug('HOOK', 'Summary request sent successfully');
|
||||
|
||||
return { continue: true, suppressOutput: true };
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* User Message Handler - SessionStart (parallel)
|
||||
*
|
||||
* Extracted from user-message-hook.ts - displays context info to user via stderr.
|
||||
* Uses exit code 3 to show user message without injecting into Claude's context.
|
||||
*/
|
||||
|
||||
import { basename } from 'path';
|
||||
import type { EventHandler, NormalizedHookInput, HookResult } from '../types.js';
|
||||
import { ensureWorkerRunning, getWorkerPort } from '../../shared/worker-utils.js';
|
||||
import { HOOK_EXIT_CODES } from '../../shared/hook-constants.js';
|
||||
|
||||
export const userMessageHandler: EventHandler = {
|
||||
async execute(input: NormalizedHookInput): Promise<HookResult> {
|
||||
// Ensure worker is running
|
||||
await ensureWorkerRunning();
|
||||
|
||||
const port = getWorkerPort();
|
||||
const project = basename(input.cwd ?? process.cwd());
|
||||
|
||||
// Fetch formatted context directly from worker API
|
||||
// Note: Removed AbortSignal.timeout to avoid Windows Bun cleanup issue (libuv assertion)
|
||||
const response = await fetch(
|
||||
`http://127.0.0.1:${port}/api/context/inject?project=${encodeURIComponent(project)}&colors=true`,
|
||||
{ method: 'GET' }
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch context: ${response.status}`);
|
||||
}
|
||||
|
||||
const output = await response.text();
|
||||
|
||||
// Write to stderr for user visibility (Claude Code UI shows stderr)
|
||||
console.error(
|
||||
"\n\n" + String.fromCodePoint(0x1F4DD) + " Claude-Mem Context Loaded\n" +
|
||||
" " + String.fromCodePoint(0x2139, 0xFE0F) + " Note: This appears as stderr but is informational only\n\n" +
|
||||
output +
|
||||
"\n\n" + String.fromCodePoint(0x1F4A1) + " New! Wrap all or part of any message with <private> ... </private> to prevent storing sensitive information in your observation history.\n" +
|
||||
"\n" + String.fromCodePoint(0x1F4AC) + " Community https://discord.gg/J4wttp9vDu" +
|
||||
`\n` + String.fromCodePoint(0x1F4FA) + ` Watch live in browser http://localhost:${port}/\n`
|
||||
);
|
||||
|
||||
return { exitCode: HOOK_EXIT_CODES.USER_MESSAGE_ONLY };
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
import { readJsonFromStdin } from './stdin-reader.js';
|
||||
import { getPlatformAdapter } from './adapters/index.js';
|
||||
import { getEventHandler } from './handlers/index.js';
|
||||
import { HOOK_EXIT_CODES } from '../shared/hook-constants.js';
|
||||
|
||||
export async function hookCommand(platform: string, event: string): Promise<void> {
|
||||
try {
|
||||
const adapter = getPlatformAdapter(platform);
|
||||
const handler = getEventHandler(event);
|
||||
|
||||
const rawInput = await readJsonFromStdin();
|
||||
const input = adapter.normalizeInput(rawInput);
|
||||
input.platform = platform; // Inject platform for handler-level decisions
|
||||
const result = await handler.execute(input);
|
||||
const output = adapter.formatOutput(result);
|
||||
|
||||
console.log(JSON.stringify(output));
|
||||
process.exit(result.exitCode ?? HOOK_EXIT_CODES.SUCCESS);
|
||||
} catch (error) {
|
||||
console.error(`Hook error: ${error}`);
|
||||
// Use exit code 2 (blocking error) so users see the error message
|
||||
// Exit code 1 only shows in verbose mode per Claude Code docs
|
||||
process.exit(HOOK_EXIT_CODES.BLOCKING_ERROR); // = 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Stdin reading utility extracted from hook patterns
|
||||
// See src/hooks/save-hook.ts for the original pattern
|
||||
|
||||
export async function readJsonFromStdin(): Promise<unknown> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let input = '';
|
||||
process.stdin.on('data', (chunk) => input += chunk);
|
||||
process.stdin.on('end', () => {
|
||||
try {
|
||||
resolve(input.trim() ? JSON.parse(input) : undefined);
|
||||
} catch (e) {
|
||||
reject(new Error(`Failed to parse hook input: ${e}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
export interface NormalizedHookInput {
|
||||
sessionId: string;
|
||||
cwd: string;
|
||||
platform?: string; // 'claude-code' or 'cursor'
|
||||
prompt?: string;
|
||||
toolName?: string;
|
||||
toolInput?: unknown;
|
||||
toolResponse?: unknown;
|
||||
transcriptPath?: string;
|
||||
// Cursor-specific fields
|
||||
filePath?: string; // afterFileEdit
|
||||
edits?: unknown[]; // afterFileEdit
|
||||
}
|
||||
|
||||
export interface HookResult {
|
||||
continue?: boolean;
|
||||
suppressOutput?: boolean;
|
||||
hookSpecificOutput?: { hookEventName: string; additionalContext: string };
|
||||
exitCode?: number;
|
||||
}
|
||||
|
||||
export interface PlatformAdapter {
|
||||
normalizeInput(raw: unknown): NormalizedHookInput;
|
||||
formatOutput(result: HookResult): unknown;
|
||||
}
|
||||
|
||||
export interface EventHandler {
|
||||
execute(input: NormalizedHookInput): Promise<HookResult>;
|
||||
}
|
||||
Reference in New Issue
Block a user