Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 92c4d245c4 | |||
| a2ab45a461 | |||
| 1d68e299dc | |||
| b987789754 | |||
| e91868a7f6 | |||
| 644cccd3e1 | |||
| e6df88bf42 | |||
| ef823a89c1 | |||
| b169e18de0 | |||
| e822d34342 | |||
| 5590a02dc5 | |||
| 601596f5cb | |||
| 817c4348b1 | |||
| 5963850775 |
+101
-66
@@ -3,101 +3,136 @@
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Dec 28, 2025
|
||||
### Oct 25, 2025
|
||||
|
||||
**marketplace.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #33520 | 10:46 PM | 🔵 | Marketplace configuration lists claude-mem plugin metadata | ~240 |
|
||||
| #33486 | 10:35 PM | ✅ | Committed Version Bump to 8.2.6 | ~212 |
|
||||
| #33484 | " | ✅ | Verified Version Synchronization at 8.2.6 | ~173 |
|
||||
| #33482 | 10:34 PM | ✅ | Updated Marketplace Plugin Version to 8.2.6 | ~159 |
|
||||
| #33479 | " | 🔵 | Marketplace Configuration for Plugin Distribution | ~242 |
|
||||
| #33477 | " | 🔵 | Current Version Across Package Files is 8.2.5 | ~185 |
|
||||
| #33311 | 3:09 PM | ✅ | Version 8.2.3 Release Deployed with Worker Stability Improvements | ~434 |
|
||||
| #33300 | 3:08 PM | ✅ | Version 8.2.4 Released with Full Automation Pipeline | ~357 |
|
||||
| #33281 | 3:07 PM | ✅ | Released v8.2.1 with Worker Lifecycle Hardening | ~332 |
|
||||
| #2374 | 2:55 PM | ✅ | Marketplace metadata version synchronized to 4.2.11 | ~157 |
|
||||
|
||||
### Dec 29, 2025
|
||||
### Oct 27, 2025
|
||||
|
||||
**marketplace.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34368 | 11:30 PM | ✅ | Bumped version to 8.5.1 for patch release | ~218 |
|
||||
| #34351 | 11:13 PM | ✅ | Version 8.5.0 Release Committed to Git | ~402 |
|
||||
| #34349 | 11:12 PM | ✅ | Version 8.5.0 Synchronized Across All Files | ~220 |
|
||||
| #33997 | 7:10 PM | ✅ | Version Bumped to 8.2.10 | ~179 |
|
||||
| #33994 | " | ✅ | Version bump to 8.2.10 for claude-mem plugin | ~186 |
|
||||
| #33992 | " | 🔵 | Version Consistency Verified Across Package Files | ~133 |
|
||||
| #33956 | 6:45 PM | 🔵 | Version 8.2.9 Release Verification Complete | ~227 |
|
||||
| #33948 | 6:42 PM | 🔵 | Current Version State Across Project Files | ~195 |
|
||||
| #33840 | 4:24 PM | ✅ | Version 8.2.8 Changes Committed to Git | ~233 |
|
||||
| #33838 | 4:23 PM | ✅ | Patch Release Version Bump to 8.2.8 Completed | ~216 |
|
||||
| #33836 | " | 🔵 | Current Version State Before Patch Release | ~125 |
|
||||
| #2757 | 1:23 AM | 🟣 | Released v4.3.3 with Configurable Session Display and First-Time Setup UX | ~391 |
|
||||
|
||||
### Dec 31, 2025
|
||||
### Nov 4, 2025
|
||||
|
||||
**marketplace.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34723 | 4:51 PM | ✅ | Version 8.5.2 Release Committed and Tagged | ~198 |
|
||||
| #34721 | " | ✅ | Version 8.5.2 Synchronized Across All Configuration Files | ~186 |
|
||||
| #34719 | " | ✅ | Marketplace Version Bumped to 8.5.2 | ~169 |
|
||||
| #34716 | 4:50 PM | 🔵 | Current Version Before Patch Release | ~193 |
|
||||
| #34715 | " | 🔵 | Version 8.5.1 confirmed across all package configuration files | ~181 |
|
||||
| #3706 | 9:47 PM | ✅ | Marketplace Plugin Version Synchronized to 5.0.2 | ~162 |
|
||||
| #3655 | 3:43 PM | ✅ | Version bumped to 5.0.1 across project | ~354 |
|
||||
|
||||
### Jan 1, 2026
|
||||
### Nov 5, 2025
|
||||
|
||||
**marketplace.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #35680 | 11:43 PM | 🟣 | Automated version 8.5.3 release workflow completed | ~470 |
|
||||
| #35674 | 11:41 PM | 🔵 | Release staged files identified for version 8.5.3 | ~287 |
|
||||
| #35673 | " | ✅ | Staged version files for commit | ~155 |
|
||||
| #35671 | " | ✅ | Bumped claude-mem plugin version to 8.5.3 | ~137 |
|
||||
| #35669 | 11:40 PM | 🔵 | Claude plugin marketplace configuration discovered | ~235 |
|
||||
| #4068 | 10:58 PM | ✅ | Committed v5.1.0 release with comprehensive release notes | ~486 |
|
||||
| #4066 | 10:57 PM | ✅ | Updated marketplace.json version to 5.1.0 | ~192 |
|
||||
| #3739 | 2:24 PM | ✅ | Updated version to 5.0.3 across project manifests | ~322 |
|
||||
|
||||
### Jan 2, 2026
|
||||
### Nov 6, 2025
|
||||
|
||||
**marketplace.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #35944 | 2:57 PM | 🔵 | Current Project Version 8.5.4 Verified | ~200 |
|
||||
| #35926 | 2:53 PM | ✅ | Committed Version Bump to 8.5.4 | ~210 |
|
||||
| #35924 | " | ✅ | Verified Version Consistency Across All Files | ~186 |
|
||||
| #35922 | 2:52 PM | ✅ | Updated Marketplace Version to 8.5.4 | ~150 |
|
||||
| #35918 | " | 🔵 | Marketplace Configuration Structure | ~200 |
|
||||
| #35917 | " | 🔵 | Current Version Identified as 8.5.3 | ~170 |
|
||||
| #35905 | 2:49 PM | 🔵 | Current Version is 8.5.3 Across All Package Files | ~193 |
|
||||
| #4099 | 1:13 PM | 🟣 | Theme Toggle for Light/Dark Mode | ~253 |
|
||||
| #4096 | " | ✅ | Marketplace Metadata Version Sync | ~179 |
|
||||
| #4092 | 1:12 PM | 🔵 | Marketplace Configuration for Claude-Mem Plugin | ~194 |
|
||||
| #4078 | 12:50 PM | 🔴 | Fixed PM2 ENOENT error on Windows systems | ~286 |
|
||||
| #4075 | 12:49 PM | ✅ | Marketplace plugin version synchronized to 5.1.1 | ~189 |
|
||||
|
||||
### Jan 3, 2026
|
||||
### Nov 7, 2025
|
||||
|
||||
**marketplace.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #36669 | 11:37 PM | ✅ | Merge conflicts resolved automatically - only 5 metadata files modified | ~345 |
|
||||
| #4612 | 6:33 PM | ✅ | Version Bumped to 5.2.0 Across All Package Metadata | ~359 |
|
||||
| #4598 | 6:31 PM | ✅ | PR #69 Merged: cleanup/worker Branch Integration | ~469 |
|
||||
| #4298 | 11:54 AM | 🔴 | Fixed PostToolUse Hook Schema Compliance | ~310 |
|
||||
| #4295 | 11:53 AM | ✅ | Synchronized Plugin Marketplace Version to 5.1.4 | ~188 |
|
||||
|
||||
### Jan 4, 2026
|
||||
### Nov 8, 2025
|
||||
|
||||
**marketplace.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #36924 | 2:25 AM | ✅ | Merged fix/pr-538-followups branch into main with comprehensive updates | ~481 |
|
||||
| #36922 | " | ✅ | Version Bump Committed to Git | ~261 |
|
||||
| #36920 | " | 🔵 | Version 8.5.8 already set across all configuration files | ~203 |
|
||||
| #36918 | " | ✅ | Version bumped to 8.5.8 in marketplace.json | ~216 |
|
||||
| #36916 | 2:24 AM | 🔵 | Current version identified in marketplace configuration | ~224 |
|
||||
| #36912 | " | 🔵 | Current version identified as 8.5.7 across all package files | ~190 |
|
||||
| #36700 | 12:00 AM | ✅ | Committed Version 8.5.7 Across All Package Files | ~260 |
|
||||
| #36699 | " | ✅ | Version Bumped to 8.5.7 Across All Package Files | ~271 |
|
||||
| #36698 | " | ✅ | Marketplace Plugin Version Updated to 8.5.7 | ~257 |
|
||||
| #5150 | 7:37 PM | 🟣 | Troubleshooting Skill Added to Claude-Mem Plugin | ~427 |
|
||||
| #5133 | 7:29 PM | ✅ | Version 5.2.3 Released with Build Process | ~487 |
|
||||
|
||||
### Jan 5, 2026
|
||||
### Nov 9, 2025
|
||||
|
||||
**marketplace.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #38093 | 10:44 PM | ✅ | Committed version bump to 9.0.0 | ~217 |
|
||||
| #38090 | " | ✅ | Version bumped to 9.0.0 in marketplace.json | ~157 |
|
||||
| #38087 | 10:43 PM | 🔵 | Current version identified as 8.5.10 across all version files | ~200 |
|
||||
| #5941 | 7:14 PM | ✅ | Marketplace Version Updated to 5.4.0 | ~157 |
|
||||
|
||||
### Nov 10, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #6341 | 1:49 PM | ✅ | Version Bumped to 5.4.1 | ~239 |
|
||||
|
||||
### Nov 11, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #6602 | 1:51 PM | ✅ | Version 5.4.5 Released to GitHub | ~279 |
|
||||
| #6601 | " | ✅ | Version Patch Bump 5.4.4 to 5.4.5 | ~233 |
|
||||
|
||||
### Nov 14, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #8212 | 3:06 PM | 🔵 | Version Consistency Verification Across Multiple Configuration Files | ~238 |
|
||||
|
||||
### Nov 25, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #14882 | 1:32 PM | 🔵 | Marketplace Configuration Defines Plugin Version and Source Directory | ~366 |
|
||||
|
||||
### Nov 30, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #18064 | 10:52 PM | ✅ | Bumped version to 6.3.7 in marketplace.json | ~179 |
|
||||
| #18060 | 10:51 PM | 🔵 | Read marketplace.json plugin manifest | ~190 |
|
||||
|
||||
### Dec 1, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #18428 | 3:33 PM | 🔵 | Version Conflict in Marketplace Configuration | ~191 |
|
||||
|
||||
### Dec 4, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #20049 | 3:23 PM | ✅ | Updated marketplace.json version to 6.5.2 | ~203 |
|
||||
|
||||
### Dec 9, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #22559 | 1:08 AM | ✅ | Version 7.0.3 committed to repository | ~261 |
|
||||
| #22551 | 1:07 AM | ✅ | Marketplace metadata updated to version 7.0.3 | ~179 |
|
||||
|
||||
### Dec 10, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #23440 | 2:25 PM | ✅ | Marketplace Configuration Updated to 7.0.8 | ~188 |
|
||||
|
||||
### Dec 14, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #26799 | 11:39 PM | ✅ | Marketplace Manifest Version Updated to 7.2.3 | ~248 |
|
||||
| #26796 | " | ✅ | Version Bumped to 7.2.3 in marketplace.json | ~259 |
|
||||
| #26792 | 11:38 PM | 🔵 | Current Version Confirmed as 7.2.2 Across All Configuration Files | ~291 |
|
||||
|
||||
### Dec 16, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #28306 | 10:08 PM | 🔵 | Marketplace Configuration Also Shows Version 7.3.3 | ~220 |
|
||||
| #27555 | 4:48 PM | ✅ | Version bump committed to main branch | ~242 |
|
||||
| #27553 | " | ✅ | Version consistency verified across all configuration files | ~195 |
|
||||
| #27551 | 4:47 PM | ✅ | Marketplace.json version updated to 7.3.1 | ~207 |
|
||||
</claude-mem-context>
|
||||
@@ -10,7 +10,7 @@
|
||||
"plugins": [
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "9.0.1",
|
||||
"version": "9.0.4",
|
||||
"source": "./plugin",
|
||||
"description": "Persistent memory system for Claude Code - context compression across sessions"
|
||||
}
|
||||
|
||||
+2
-20
@@ -3,27 +3,9 @@
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Dec 21, 2025
|
||||
### Nov 3, 2025
|
||||
|
||||
**test-analysis-report.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #31743 | 10:36 PM | 🔵 | PR #412 proposes mode system with inheritance and multilingual support | ~523 |
|
||||
|
||||
### Dec 22, 2025
|
||||
|
||||
**test-analysis-report.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #31865 | 6:56 PM | ✅ | 開発ドキュメントのクリーンアップをコミット | ~150 |
|
||||
| #31864 | " | ✅ | 計画ドキュメントと分析ファイルの削除 | ~142 |
|
||||
| #31859 | 6:55 PM | ✅ | 計画ドキュメントファイルの削除 | ~109 |
|
||||
| #31855 | 6:53 PM | ✅ | テストスイートの大規模削除とテスト分析レポートの追加 | ~197 |
|
||||
|
||||
### Dec 25, 2025
|
||||
|
||||
**settings.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #32602 | 8:42 PM | 🔵 | Identified potential settings configuration files | ~224 |
|
||||
| #3366 | 3:40 PM | 🔵 | Claude Mem MCP Search Architecture and Timeline Tool Capabilities | ~438 |
|
||||
</claude-mem-context>
|
||||
@@ -5,30 +5,16 @@
|
||||
|
||||
### Oct 25, 2025
|
||||
|
||||
**changelog.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #2491 | 10:59 PM | 🟣 | Added changelog viewer slash command | ~290 |
|
||||
| #2484 | 6:33 PM | 🔴 | Removed slash commands from incorrect root .claude/commands directory | ~268 |
|
||||
|
||||
### Jan 10, 2026
|
||||
|
||||
**terminal-shortcut.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #2480 | 6:32 PM | 🟣 | Cross-platform terminal shortcut command for claude-mem CLI | ~459 |
|
||||
|
||||
**setup-alias.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #2479 | 6:29 PM | 🟣 | Shell Alias Setup Slash Command | ~423 |
|
||||
|
||||
**version-bump.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #2358 | 1:07 PM | 🟣 | Created version-bump slash command for automated version updates | ~361 |
|
||||
|
||||
### Jan 1, 2026
|
||||
|
||||
**anti-pattern-czar.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #35638 | 11:17 PM | 🟣 | Anti-Pattern Czar Custom Command Created | ~583 |
|
||||
| #39054 | 3:45 PM | 🔄 | Development commands removed from root .claude directory | ~249 |
|
||||
| #39053 | " | 🟣 | Added development commands to plugin distribution | ~276 |
|
||||
| #39051 | 3:44 PM | 🔵 | Development commands confirmed in .claude/commands/ | ~315 |
|
||||
| #39049 | " | 🔵 | Development commands located in .claude/commands/ directory | ~293 |
|
||||
</claude-mem-context>
|
||||
@@ -0,0 +1,223 @@
|
||||
# Plan: PR #628 Polish Items
|
||||
|
||||
**PR**: #628 - Windows Terminal Tab Accumulation & Windows 11 Compatibility
|
||||
**Status**: APPROVED by 3 reviewers with minor suggestions
|
||||
**Branch**: `feature/no-more-hook-files`
|
||||
|
||||
---
|
||||
|
||||
## Phase 0: Documentation Discovery (Completed by Orchestrator)
|
||||
|
||||
### Allowed APIs and Patterns
|
||||
|
||||
**Exit Code Constants** - `src/shared/hook-constants.ts:18-23`:
|
||||
```typescript
|
||||
export const HOOK_EXIT_CODES = {
|
||||
SUCCESS: 0,
|
||||
FAILURE: 1,
|
||||
BLOCKING_ERROR: 2,
|
||||
} as const;
|
||||
```
|
||||
|
||||
**Timeout Constants** - `src/shared/hook-constants.ts:1-8`:
|
||||
```typescript
|
||||
export const HOOK_TIMEOUTS = {
|
||||
DEFAULT: 300000,
|
||||
HEALTH_CHECK: 30000,
|
||||
WORKER_STARTUP_WAIT: 1000,
|
||||
WORKER_STARTUP_RETRIES: 300,
|
||||
PRE_RESTART_SETTLE_DELAY: 2000,
|
||||
WINDOWS_MULTIPLIER: 1.5
|
||||
} as const;
|
||||
```
|
||||
|
||||
**Platform Timeout Function** - `src/services/infrastructure/ProcessManager.ts:70-73`:
|
||||
```typescript
|
||||
export function getPlatformTimeout(baseMs: number): number {
|
||||
const WINDOWS_MULTIPLIER = 2.0;
|
||||
return process.platform === 'win32' ? Math.round(baseMs * WINDOWS_MULTIPLIER) : baseMs;
|
||||
}
|
||||
```
|
||||
|
||||
**Migration Guide Pattern** - `docs/public/architecture/pm2-to-bun-migration.mdx`:
|
||||
- Uses MDX format with frontmatter
|
||||
- Starts with `<Note>` for historical context
|
||||
- Uses `<AccordionGroup>` for before/after comparisons
|
||||
- Includes executive summary, key benefits, migration impact sections
|
||||
|
||||
**Exit Code Documentation** - `private/context/claude-code/exit-codes.md`:
|
||||
- Defines exit code 0, 2, and other behaviors
|
||||
- Per-hook event behavior table
|
||||
|
||||
### Files to Modify
|
||||
|
||||
| File | Change | Lines |
|
||||
|------|--------|-------|
|
||||
| `src/services/infrastructure/ProcessManager.ts` | Add POWERSHELL_TIMEOUT constant, reduce from 60000 to 10000 | 93, 123, 175, 241 |
|
||||
| `src/shared/hook-constants.ts` | Add POWERSHELL_TIMEOUT constant | After line 8 |
|
||||
| `CLAUDE.md` | Document exit code strategy | Architecture section |
|
||||
|
||||
### Anti-Patterns to Avoid
|
||||
|
||||
- DO NOT invent new exit code values (only 0, 1, 2 exist)
|
||||
- DO NOT change Windows multiplier (1.5x in hooks, 2.0x in ProcessManager - they serve different purposes)
|
||||
- DO NOT add upper bound PID validation (not in existing pattern, reviewers marked as "nice to have")
|
||||
- DO NOT create migration guide for Cursor (shell scripts still exist in cursor-hooks/, not removed)
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Extract PowerShell Timeout Constant
|
||||
|
||||
### What to Implement
|
||||
|
||||
Add a `POWERSHELL_TIMEOUT` constant to centralize the magic number `60000` and reduce to `10000` (10 seconds) as recommended by reviewers.
|
||||
|
||||
### Documentation References
|
||||
|
||||
1. Copy constant pattern from `src/shared/hook-constants.ts:1-8`
|
||||
2. Copy usage pattern from `src/services/infrastructure/ProcessManager.ts:93`
|
||||
|
||||
### Implementation Steps
|
||||
|
||||
1. **Add constant to hook-constants.ts** after line 8:
|
||||
```typescript
|
||||
POWERSHELL_COMMAND: 10000, // PowerShell process enumeration (10s - typically completes in <1s)
|
||||
```
|
||||
|
||||
2. **Import and use in ProcessManager.ts**:
|
||||
- Import `HOOK_TIMEOUTS` from `../../shared/hook-constants.js`
|
||||
- Replace `{ timeout: 60000 }` with `{ timeout: HOOK_TIMEOUTS.POWERSHELL_COMMAND }` at lines 93, 123, 175, 241
|
||||
|
||||
### Verification Checklist
|
||||
|
||||
- [ ] `grep -n "60000" src/services/infrastructure/ProcessManager.ts` returns 0 matches
|
||||
- [ ] `grep -n "POWERSHELL_COMMAND" src/services/infrastructure/ProcessManager.ts` returns 4 matches
|
||||
- [ ] `npm run build` succeeds
|
||||
- [ ] `npm test` passes (22/22 PowerShell tests still pass)
|
||||
|
||||
### Anti-Pattern Guards
|
||||
|
||||
- DO NOT use `getPlatformTimeout()` for PowerShell commands (they already run only on Windows)
|
||||
- DO NOT change timeout values in other files (only ProcessManager.ts uses PowerShell)
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Document Exit Code Strategy in CLAUDE.md
|
||||
|
||||
### What to Implement
|
||||
|
||||
Add an "Exit Code Strategy" section to the main CLAUDE.md to explain the graceful exit philosophy adopted in this PR.
|
||||
|
||||
### Documentation References
|
||||
|
||||
1. Copy exit code definitions from `private/context/claude-code/exit-codes.md`
|
||||
2. Follow format of existing CLAUDE.md sections
|
||||
|
||||
### Implementation Steps
|
||||
|
||||
1. **Add section after "File Locations"** in `/Users/alexnewman/Scripts/claude-mem/CLAUDE.md`:
|
||||
|
||||
```markdown
|
||||
## Exit Code Strategy
|
||||
|
||||
Claude-mem hooks use specific exit codes per Claude Code's hook contract:
|
||||
|
||||
- **Exit 0**: Success or graceful shutdown (Windows Terminal closes tabs)
|
||||
- **Exit 1**: Non-blocking error (stderr shown to user, continues)
|
||||
- **Exit 2**: Blocking error (stderr fed to Claude for processing)
|
||||
|
||||
**Philosophy**: Worker/hook errors exit with code 0 to prevent Windows Terminal tab accumulation. The wrapper/plugin layer handles restart logic. ERROR-level logging is maintained for diagnostics.
|
||||
|
||||
See `private/context/claude-code/exit-codes.md` for full hook behavior matrix.
|
||||
```
|
||||
|
||||
### Verification Checklist
|
||||
|
||||
- [ ] `grep -n "Exit Code Strategy" CLAUDE.md` returns 1 match
|
||||
- [ ] Section appears after "File Locations" section
|
||||
- [ ] No duplicate sections added
|
||||
|
||||
### Anti-Pattern Guards
|
||||
|
||||
- DO NOT copy the full exit-codes.md table (keep it brief, reference the source)
|
||||
- DO NOT change actual exit code behavior in code files
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Update Tests for New Timeout Constant
|
||||
|
||||
### What to Implement
|
||||
|
||||
Add test coverage for the new `POWERSHELL_COMMAND` timeout constant.
|
||||
|
||||
### Documentation References
|
||||
|
||||
1. Copy test pattern from `tests/hook-constants.test.ts:26-48`
|
||||
|
||||
### Implementation Steps
|
||||
|
||||
1. **Add test to hook-constants.test.ts** after line 42:
|
||||
```typescript
|
||||
test('POWERSHELL_COMMAND timeout is 10000ms', () => {
|
||||
expect(HOOK_TIMEOUTS.POWERSHELL_COMMAND).toBe(10000);
|
||||
});
|
||||
```
|
||||
|
||||
### Verification Checklist
|
||||
|
||||
- [ ] `npm test -- tests/hook-constants.test.ts` passes
|
||||
- [ ] New test appears in test output
|
||||
- [ ] All 22 PowerShell parsing tests still pass
|
||||
|
||||
### Anti-Pattern Guards
|
||||
|
||||
- DO NOT modify PowerShell parsing tests (they test parsing, not timeouts)
|
||||
- DO NOT add integration tests for actual PowerShell execution (out of scope)
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Final Verification
|
||||
|
||||
### Verification Checklist
|
||||
|
||||
1. **Build passes**: `npm run build`
|
||||
2. **All tests pass**: `npm test`
|
||||
3. **No magic numbers remain**: `grep -rn "60000" src/services/infrastructure/ProcessManager.ts` returns 0
|
||||
4. **Exit code documentation exists**: `grep -n "Exit Code Strategy" CLAUDE.md` returns 1
|
||||
5. **Constant is used**: `grep -rn "POWERSHELL_COMMAND" src/` returns multiple matches
|
||||
|
||||
### Anti-Pattern Grep Checks
|
||||
|
||||
- [ ] `grep -rn "timeout: 60000" src/` returns 0 matches (no hardcoded 60s timeouts in ProcessManager)
|
||||
- [ ] `grep -rn "process.exit(3)" src/` returns 0 matches (exit code 3 not used)
|
||||
|
||||
### Commit Message Template
|
||||
|
||||
```
|
||||
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>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
| Phase | Description | Files Changed | Verification |
|
||||
|-------|-------------|---------------|--------------|
|
||||
| 0 | Documentation Discovery | N/A | Patterns identified |
|
||||
| 1 | Extract PowerShell timeout | hook-constants.ts, ProcessManager.ts | grep + build + test |
|
||||
| 2 | Document exit strategy | CLAUDE.md | grep |
|
||||
| 3 | Add test coverage | hook-constants.test.ts | npm test |
|
||||
| 4 | Final verification | N/A | All checks pass |
|
||||
|
||||
**Estimated Changes**: ~20 lines added/modified across 4 files
|
||||
**Risk Level**: Low (constants extraction, documentation only)
|
||||
**Breaking Changes**: None
|
||||
@@ -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>
|
||||
+73
-34
@@ -2,6 +2,79 @@
|
||||
|
||||
All notable changes to claude-mem.
|
||||
|
||||
## [v9.0.3] - 2026-01-10
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
### Hook Framework JSON Status Output (#655)
|
||||
|
||||
Fixed an issue where the worker service startup wasn't producing proper JSON status output for the Claude Code hook framework. This caused hooks to appear stuck or unresponsive during worker initialization.
|
||||
|
||||
**Changes:**
|
||||
- Added `buildStatusOutput()` function for generating structured JSON status output
|
||||
- Worker now outputs JSON with `status`, `message`, and `continue` fields on stdout
|
||||
- Proper exit code 0 ensures Windows Terminal compatibility (no tab accumulation)
|
||||
- `continue: true` flag ensures Claude Code continues processing after hook execution
|
||||
|
||||
**Technical Details:**
|
||||
- Extracted status output generation into a pure, testable function
|
||||
- Added comprehensive test coverage in `tests/infrastructure/worker-json-status.test.ts`
|
||||
- 23 passing tests covering unit, CLI integration, and hook framework compatibility
|
||||
|
||||
## Housekeeping
|
||||
|
||||
- Removed obsolete error handling baseline file
|
||||
|
||||
## [v9.0.2] - 2026-01-10
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **Windows Terminal Tab Accumulation (#625, #628)**: Fixed terminal tab accumulation on Windows by implementing graceful exit strategy. All expected failure scenarios (port conflicts, version mismatches, health check timeouts) now exit with code 0 instead of code 1.
|
||||
- **Windows 11 Compatibility (#625)**: Replaced deprecated WMIC commands with PowerShell `Get-Process` and `Get-CimInstance` for process enumeration. WMIC is being removed from Windows 11.
|
||||
|
||||
## Maintenance
|
||||
|
||||
- **Removed Obsolete CLAUDE.md Files**: Cleaned up auto-generated CLAUDE.md files from `~/.claude/plans/` and `~/.claude/plugins/marketplaces/` directories.
|
||||
|
||||
---
|
||||
|
||||
**Full Changelog**: https://github.com/thedotmack/claude-mem/compare/v9.0.1...v9.0.2
|
||||
|
||||
## [v9.0.1] - 2026-01-08
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
### Claude Code 2.1.1 Compatibility
|
||||
- Fixed hook architecture for compatibility with Claude Code 2.1.0/2.1.1
|
||||
- Context is now injected silently via SessionStart hook
|
||||
- Removed deprecated `user-message-hook` (no longer used in CC 2.1.0+)
|
||||
|
||||
### Path Validation for CLAUDE.md Distribution
|
||||
- Added `isValidPathForClaudeMd()` to reject malformed paths:
|
||||
- Tilde paths (`~`) that Node.js doesn't expand
|
||||
- URLs (`http://`, `https://`)
|
||||
- Paths with spaces (likely command text or PR references)
|
||||
- Paths with `#` (GitHub issue/PR references)
|
||||
- Relative paths that escape project boundary
|
||||
- Cleaned up 12 invalid CLAUDE.md files created by bug artifacts
|
||||
- Updated `.gitignore` to prevent future accidents
|
||||
|
||||
### Log-Level Audit
|
||||
- Promoted 38+ WARN messages to ERROR level for improved debugging:
|
||||
- Parser: observation type errors, data contamination
|
||||
- SDK/Agents: empty init responses (Gemini, OpenRouter)
|
||||
- Worker/Queue: session recovery, auto-recovery failures
|
||||
- Chroma: sync failures, search failures
|
||||
- SQLite: search failures
|
||||
- Session/Generator: failures, missing context
|
||||
- Infrastructure: shutdown, process management failures
|
||||
|
||||
## Internal Changes
|
||||
- Removed hardcoded fake token counts from context injection
|
||||
- Standardized Claude Code 2.1.0 note wording across documentation
|
||||
|
||||
**Full Changelog**: https://github.com/thedotmack/claude-mem/compare/v9.0.0...v9.0.1
|
||||
|
||||
## [v9.0.0] - 2026-01-06
|
||||
|
||||
## 🚀 Live Context System
|
||||
@@ -1230,37 +1303,3 @@ Fixed unbounded database growth in the `pending_messages` table by implementing
|
||||
|
||||
**Full Changelog**: https://github.com/thedotmack/claude-mem/compare/v7.2.4...v7.3.0
|
||||
|
||||
## [v7.2.4] - 2025-12-15
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Documentation
|
||||
- Updated endless mode setup instructions with improved configuration guidance for better user experience
|
||||
|
||||
**Full Changelog**: https://github.com/thedotmack/claude-mem/compare/v7.2.3...v7.2.4
|
||||
|
||||
## [v7.2.3] - 2025-12-15
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **Fix MCP server failures on plugin updates**: Add 2-second pre-restart delay in `ensureWorkerVersionMatches()` to give files time to sync before killing the old worker. This prevents the race condition where the worker restart happened too quickly after plugin file updates, causing "Worker service connection failed" errors.
|
||||
|
||||
## Changes
|
||||
|
||||
- Add `PRE_RESTART_SETTLE_DELAY` constant (2000ms) to `hook-constants.ts`
|
||||
- Add delay before `ProcessManager.restart()` call in `worker-utils.ts`
|
||||
- Fix pre-existing bug where `port` variable was undefined in error logging
|
||||
|
||||
---
|
||||
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
||||
|
||||
## [v7.2.2] - 2025-12-15
|
||||
|
||||
## Changes
|
||||
|
||||
- **Refactor:** Consolidate mem-search skill, remove desktop-skill duplication
|
||||
- Delete separate `desktop-skill/` directory (was outdated)
|
||||
- Generate `mem-search.zip` during build from `plugin/skills/mem-search/`
|
||||
- Update docs with correct MCP tool list and new download path
|
||||
- Single source of truth for Claude Desktop skill
|
||||
|
||||
|
||||
@@ -41,6 +41,18 @@ Settings are managed in `~/.claude-mem/settings.json`. The file is auto-created
|
||||
- **Database**: `~/.claude-mem/claude-mem.db`
|
||||
- **Chroma**: `~/.claude-mem/chroma/`
|
||||
|
||||
## Exit Code Strategy
|
||||
|
||||
Claude-mem hooks use specific exit codes per Claude Code's hook contract:
|
||||
|
||||
- **Exit 0**: Success or graceful shutdown (Windows Terminal closes tabs)
|
||||
- **Exit 1**: Non-blocking error (stderr shown to user, continues)
|
||||
- **Exit 2**: Blocking error (stderr fed to Claude for processing)
|
||||
|
||||
**Philosophy**: Worker/hook errors exit with code 0 to prevent Windows Terminal tab accumulation. The wrapper/plugin layer handles restart logic. ERROR-level logging is maintained for diagnostics.
|
||||
|
||||
See `private/context/claude-code/exit-codes.md` for full hook behavior matrix.
|
||||
|
||||
## Requirements
|
||||
|
||||
- **Bun** (all platforms - auto-installed if missing)
|
||||
|
||||
@@ -1,192 +0,0 @@
|
||||
# Common utility functions for Cursor hooks (PowerShell)
|
||||
# Dot-source this file in hook scripts: . "$PSScriptRoot\common.ps1"
|
||||
# Note: ErrorActionPreference should be set in each script, not globally here
|
||||
|
||||
# Get worker port from settings with validation
|
||||
function Get-WorkerPort {
|
||||
$settingsPath = Join-Path $env:USERPROFILE ".claude-mem\settings.json"
|
||||
$port = 37777
|
||||
|
||||
if (Test-Path $settingsPath) {
|
||||
try {
|
||||
$settings = Get-Content $settingsPath -Raw | ConvertFrom-Json
|
||||
if ($settings.CLAUDE_MEM_WORKER_PORT) {
|
||||
$parsedPort = [int]$settings.CLAUDE_MEM_WORKER_PORT
|
||||
if ($parsedPort -ge 1 -and $parsedPort -le 65535) {
|
||||
$port = $parsedPort
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
# Ignore parse errors, use default
|
||||
}
|
||||
}
|
||||
|
||||
return $port
|
||||
}
|
||||
|
||||
# Ensure worker is running with retries
|
||||
function Test-WorkerReady {
|
||||
param(
|
||||
[int]$Port = 37777,
|
||||
[int]$MaxRetries = 75
|
||||
)
|
||||
|
||||
for ($i = 0; $i -lt $MaxRetries; $i++) {
|
||||
try {
|
||||
$response = Invoke-RestMethod -Uri "http://127.0.0.1:$Port/api/readiness" -Method Get -TimeoutSec 1 -ErrorAction Stop
|
||||
return $true
|
||||
} catch {
|
||||
Start-Sleep -Milliseconds 200
|
||||
}
|
||||
}
|
||||
|
||||
return $false
|
||||
}
|
||||
|
||||
# Get project name from workspace root
|
||||
function Get-ProjectName {
|
||||
param([string]$WorkspaceRoot)
|
||||
|
||||
if ([string]::IsNullOrEmpty($WorkspaceRoot)) {
|
||||
return "unknown-project"
|
||||
}
|
||||
|
||||
# Handle Windows drive root (e.g., "C:\")
|
||||
if ($WorkspaceRoot -match '^([A-Za-z]):\\?$') {
|
||||
return "drive-$($Matches[1].ToUpper())"
|
||||
}
|
||||
|
||||
$projectName = Split-Path $WorkspaceRoot -Leaf
|
||||
if ([string]::IsNullOrEmpty($projectName)) {
|
||||
return "unknown-project"
|
||||
}
|
||||
|
||||
return $projectName
|
||||
}
|
||||
|
||||
# URL encode a string
|
||||
function Get-UrlEncodedString {
|
||||
param([string]$String)
|
||||
|
||||
if ([string]::IsNullOrEmpty($String)) {
|
||||
return ""
|
||||
}
|
||||
|
||||
return [System.Uri]::EscapeDataString($String)
|
||||
}
|
||||
|
||||
# Check if string is empty or null
|
||||
function Test-IsEmpty {
|
||||
param([string]$String)
|
||||
|
||||
return [string]::IsNullOrEmpty($String) -or $String -eq "null" -or $String -eq "empty"
|
||||
}
|
||||
|
||||
# Safely read JSON from stdin with error handling
|
||||
function Read-JsonInput {
|
||||
try {
|
||||
$input = [Console]::In.ReadToEnd()
|
||||
if ([string]::IsNullOrEmpty($input)) {
|
||||
return @{}
|
||||
}
|
||||
return $input | ConvertFrom-Json -ErrorAction Stop
|
||||
} catch {
|
||||
return @{}
|
||||
}
|
||||
}
|
||||
|
||||
# Safely get JSON field with fallback
|
||||
function Get-JsonField {
|
||||
param(
|
||||
[PSObject]$Json,
|
||||
[string]$Field,
|
||||
[string]$Fallback = ""
|
||||
)
|
||||
|
||||
if ($null -eq $Json) {
|
||||
return $Fallback
|
||||
}
|
||||
|
||||
# Handle array access syntax (e.g., "workspace_roots[0]")
|
||||
if ($Field -match '^(.+)\[(\d+)\]$') {
|
||||
$arrayField = $Matches[1]
|
||||
$index = [int]$Matches[2]
|
||||
|
||||
if ($Json.PSObject.Properties.Name -contains $arrayField) {
|
||||
$array = $Json.$arrayField
|
||||
if ($null -ne $array -and $array.Count -gt $index) {
|
||||
$value = $array[$index]
|
||||
if (-not (Test-IsEmpty $value)) {
|
||||
return $value
|
||||
}
|
||||
}
|
||||
}
|
||||
return $Fallback
|
||||
}
|
||||
|
||||
# Simple field access
|
||||
if ($Json.PSObject.Properties.Name -contains $Field) {
|
||||
$value = $Json.$Field
|
||||
if (-not (Test-IsEmpty $value)) {
|
||||
return $value
|
||||
}
|
||||
}
|
||||
|
||||
return $Fallback
|
||||
}
|
||||
|
||||
# Convert object to JSON string (compact)
|
||||
function ConvertTo-JsonCompact {
|
||||
param([object]$Object)
|
||||
|
||||
return $Object | ConvertTo-Json -Compress -Depth 10
|
||||
}
|
||||
|
||||
# Send HTTP POST request (fire-and-forget style)
|
||||
function Send-HttpPostAsync {
|
||||
param(
|
||||
[string]$Uri,
|
||||
[object]$Body
|
||||
)
|
||||
|
||||
try {
|
||||
$bodyJson = ConvertTo-JsonCompact $Body
|
||||
Start-Job -ScriptBlock {
|
||||
param($u, $b)
|
||||
try {
|
||||
Invoke-RestMethod -Uri $u -Method Post -Body $b -ContentType "application/json" -TimeoutSec 5 -ErrorAction SilentlyContinue | Out-Null
|
||||
} catch {}
|
||||
} -ArgumentList $Uri, $bodyJson | Out-Null
|
||||
} catch {
|
||||
# Ignore errors - fire and forget
|
||||
}
|
||||
}
|
||||
|
||||
# Send HTTP POST request (synchronous)
|
||||
function Send-HttpPost {
|
||||
param(
|
||||
[string]$Uri,
|
||||
[object]$Body
|
||||
)
|
||||
|
||||
try {
|
||||
$bodyJson = ConvertTo-JsonCompact $Body
|
||||
Invoke-RestMethod -Uri $u -Method Post -Body $bodyJson -ContentType "application/json" -TimeoutSec 5 -ErrorAction SilentlyContinue | Out-Null
|
||||
} catch {
|
||||
# Ignore errors
|
||||
}
|
||||
}
|
||||
|
||||
# Get HTTP response
|
||||
function Get-HttpResponse {
|
||||
param(
|
||||
[string]$Uri,
|
||||
[int]$TimeoutSec = 5
|
||||
)
|
||||
|
||||
try {
|
||||
return Invoke-RestMethod -Uri $Uri -Method Get -TimeoutSec $TimeoutSec -ErrorAction Stop
|
||||
} catch {
|
||||
return $null
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Common utility functions for Cursor hooks
|
||||
# Source this file in hook scripts: source "$(dirname "$0")/common.sh"
|
||||
|
||||
# Check if required commands exist
|
||||
check_dependencies() {
|
||||
local missing=()
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
missing+=("jq")
|
||||
fi
|
||||
|
||||
if ! command -v curl >/dev/null 2>&1; then
|
||||
missing+=("curl")
|
||||
fi
|
||||
|
||||
if [ ${#missing[@]} -gt 0 ]; then
|
||||
echo "Error: Missing required dependencies: ${missing[*]}" >&2
|
||||
echo "Please install: ${missing[*]}" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Safely read JSON from stdin with error handling
|
||||
read_json_input() {
|
||||
local input
|
||||
input=$(cat 2>/dev/null || echo "{}")
|
||||
|
||||
# Validate JSON
|
||||
if ! echo "$input" | jq empty 2>/dev/null; then
|
||||
# Invalid JSON - return empty object
|
||||
echo "{}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$input"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Get worker port from settings with validation
|
||||
get_worker_port() {
|
||||
local data_dir="${HOME}/.claude-mem"
|
||||
local settings_file="${data_dir}/settings.json"
|
||||
local port="37777"
|
||||
|
||||
if [ -f "$settings_file" ]; then
|
||||
local parsed_port
|
||||
parsed_port=$(jq -r '.CLAUDE_MEM_WORKER_PORT // "37777"' "$settings_file" 2>/dev/null || echo "37777")
|
||||
|
||||
# Validate port is a number between 1-65535
|
||||
if [[ "$parsed_port" =~ ^[0-9]+$ ]] && [ "$parsed_port" -ge 1 ] && [ "$parsed_port" -le 65535 ]; then
|
||||
port="$parsed_port"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "$port"
|
||||
}
|
||||
|
||||
# Ensure worker is running with retries
|
||||
ensure_worker_running() {
|
||||
local port="${1:-37777}"
|
||||
local max_retries="${2:-75}" # 15 seconds total (75 * 0.2s)
|
||||
local retry_count=0
|
||||
|
||||
while [ $retry_count -lt $max_retries ]; do
|
||||
if curl -s -f "http://127.0.0.1:${port}/api/readiness" >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
sleep 0.2
|
||||
retry_count=$((retry_count + 1))
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# URL encode a string (basic implementation)
|
||||
url_encode() {
|
||||
local string="$1"
|
||||
# Use printf to URL encode
|
||||
printf '%s' "$string" | jq -sRr @uri
|
||||
}
|
||||
|
||||
# Get project name from workspace root
|
||||
get_project_name() {
|
||||
local workspace_root="$1"
|
||||
|
||||
if [ -z "$workspace_root" ]; then
|
||||
echo "unknown-project"
|
||||
return
|
||||
fi
|
||||
|
||||
# Use basename, fallback to unknown-project
|
||||
local project_name
|
||||
project_name=$(basename "$workspace_root" 2>/dev/null || echo "unknown-project")
|
||||
|
||||
# Handle edge case: empty basename (root directory)
|
||||
if [ -z "$project_name" ]; then
|
||||
# Check if it's a Windows drive root
|
||||
if [[ "$workspace_root" =~ ^[A-Za-z]:\\?$ ]]; then
|
||||
local drive_letter
|
||||
drive_letter=$(echo "$workspace_root" | grep -oE '^[A-Za-z]' | tr '[:lower:]' '[:upper:]')
|
||||
echo "drive-${drive_letter}"
|
||||
else
|
||||
echo "unknown-project"
|
||||
fi
|
||||
else
|
||||
echo "$project_name"
|
||||
fi
|
||||
}
|
||||
|
||||
# Safely extract JSON field with fallback
|
||||
# Supports both simple fields (e.g., "conversation_id") and array access (e.g., "workspace_roots[0]")
|
||||
json_get() {
|
||||
local json="$1"
|
||||
local field="$2"
|
||||
local fallback="${3:-}"
|
||||
|
||||
local value
|
||||
|
||||
# Handle array access syntax (e.g., "workspace_roots[0]")
|
||||
if [[ "$field" =~ ^(.+)\[([0-9]+)\]$ ]]; then
|
||||
local array_field="${BASH_REMATCH[1]}"
|
||||
local index="${BASH_REMATCH[2]}"
|
||||
value=$(echo "$json" | jq -r --arg f "$array_field" --arg i "$index" --arg fb "$fallback" '.[$f] // [] | .[$i | tonumber] // $fb' 2>/dev/null || echo "$fallback")
|
||||
else
|
||||
# Simple field access
|
||||
value=$(echo "$json" | jq -r --arg f "$field" --arg fb "$fallback" '.[$f] // $fb' 2>/dev/null || echo "$fallback")
|
||||
fi
|
||||
|
||||
echo "$value"
|
||||
}
|
||||
|
||||
# Check if string is empty or null
|
||||
is_empty() {
|
||||
local str="$1"
|
||||
[ -z "$str" ] || [ "$str" = "null" ] || [ "$str" = "empty" ]
|
||||
}
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
# Context Hook for Cursor (beforeSubmitPrompt) - PowerShell
|
||||
# Ensures worker is running and refreshes context before prompt submission
|
||||
#
|
||||
# Context is updated in BOTH places:
|
||||
# - Here (beforeSubmitPrompt): Fresh context at session start
|
||||
# - stop hook (session-summary.ps1): Updated context after observations are made
|
||||
|
||||
$ErrorActionPreference = "SilentlyContinue"
|
||||
|
||||
# Source common utilities
|
||||
$commonPath = Join-Path $PSScriptRoot "common.ps1"
|
||||
if (Test-Path $commonPath) {
|
||||
. $commonPath
|
||||
} else {
|
||||
Write-Output '{"continue": true}'
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Read JSON input from stdin
|
||||
$input = Read-JsonInput
|
||||
|
||||
# Extract workspace root
|
||||
$workspaceRoot = Get-JsonField $input "workspace_roots[0]" ""
|
||||
if (Test-IsEmpty $workspaceRoot) {
|
||||
$workspaceRoot = Get-Location
|
||||
}
|
||||
|
||||
# Get project name
|
||||
$projectName = Get-ProjectName $workspaceRoot
|
||||
|
||||
# Get worker port from settings
|
||||
$workerPort = Get-WorkerPort
|
||||
|
||||
# Ensure worker is running (with retries)
|
||||
# This primes the worker before the session starts
|
||||
if (Test-WorkerReady -Port $workerPort) {
|
||||
# Refresh context file with latest observations
|
||||
$projectEncoded = Get-UrlEncodedString $projectName
|
||||
$contextUri = "http://127.0.0.1:$workerPort/api/context/inject?project=$projectEncoded"
|
||||
$context = Get-HttpResponse -Uri $contextUri
|
||||
|
||||
if (-not [string]::IsNullOrEmpty($context)) {
|
||||
$rulesDir = Join-Path $workspaceRoot ".cursor\rules"
|
||||
$rulesFile = Join-Path $rulesDir "claude-mem-context.mdc"
|
||||
|
||||
# Create rules directory if it doesn't exist
|
||||
if (-not (Test-Path $rulesDir)) {
|
||||
New-Item -ItemType Directory -Path $rulesDir -Force | Out-Null
|
||||
}
|
||||
|
||||
# Write context as a Cursor rule with alwaysApply: true
|
||||
$ruleContent = @"
|
||||
---
|
||||
alwaysApply: true
|
||||
description: "Claude-mem context from past sessions (auto-updated)"
|
||||
---
|
||||
|
||||
# Memory Context from Past Sessions
|
||||
|
||||
The following context is from claude-mem, a persistent memory system that tracks your coding sessions.
|
||||
|
||||
$context
|
||||
|
||||
---
|
||||
*Updated after last session. Use claude-mem's MCP search tools for more detailed queries.*
|
||||
"@
|
||||
|
||||
Set-Content -Path $rulesFile -Value $ruleContent -Encoding UTF8 -Force
|
||||
}
|
||||
}
|
||||
|
||||
# Allow prompt to continue
|
||||
Write-Output '{"continue": true}'
|
||||
exit 0
|
||||
@@ -1,70 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Context Hook for Cursor (beforeSubmitPrompt)
|
||||
# Ensures worker is running and refreshes context before prompt submission
|
||||
#
|
||||
# Context is updated in BOTH places:
|
||||
# - Here (beforeSubmitPrompt): Fresh context at session start
|
||||
# - stop hook (session-summary.sh): Updated context after observations are made
|
||||
|
||||
# Source common utilities
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/common.sh" 2>/dev/null || {
|
||||
echo '{"continue": true}'
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Check dependencies (non-blocking)
|
||||
check_dependencies >/dev/null 2>&1 || true
|
||||
|
||||
# Read JSON input from stdin
|
||||
input=$(read_json_input)
|
||||
|
||||
# Extract workspace root
|
||||
workspace_root=$(json_get "$input" "workspace_roots[0]" "")
|
||||
if is_empty "$workspace_root"; then
|
||||
workspace_root=$(pwd)
|
||||
fi
|
||||
|
||||
# Get project name
|
||||
project_name=$(get_project_name "$workspace_root")
|
||||
|
||||
# Get worker port from settings
|
||||
worker_port=$(get_worker_port)
|
||||
|
||||
# Ensure worker is running (with retries)
|
||||
# This primes the worker before the session starts
|
||||
if ensure_worker_running "$worker_port" >/dev/null 2>&1; then
|
||||
# Refresh context file with latest observations
|
||||
project_encoded=$(url_encode "$project_name")
|
||||
context=$(curl -s -f "http://127.0.0.1:${worker_port}/api/context/inject?project=${project_encoded}" 2>/dev/null || echo "")
|
||||
|
||||
if [ -n "$context" ]; then
|
||||
rules_dir="${workspace_root}/.cursor/rules"
|
||||
rules_file="${rules_dir}/claude-mem-context.mdc"
|
||||
|
||||
# Create rules directory if it doesn't exist
|
||||
mkdir -p "$rules_dir" 2>/dev/null || true
|
||||
|
||||
# Write context as a Cursor rule with alwaysApply: true
|
||||
cat > "$rules_file" 2>/dev/null << EOF
|
||||
---
|
||||
alwaysApply: true
|
||||
description: "Claude-mem context from past sessions (auto-updated)"
|
||||
---
|
||||
|
||||
# Memory Context from Past Sessions
|
||||
|
||||
The following context is from claude-mem, a persistent memory system that tracks your coding sessions.
|
||||
|
||||
${context}
|
||||
|
||||
---
|
||||
*Updated after last session. Use claude-mem's MCP search tools for more detailed queries.*
|
||||
EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
# Allow prompt to continue
|
||||
echo '{"continue": true}'
|
||||
exit 0
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Installation script for claude-mem Cursor hooks
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
INSTALL_TYPE="${1:-user}" # 'user' (recommended) or 'project'
|
||||
|
||||
echo "Installing claude-mem Cursor hooks (${INSTALL_TYPE} level)..."
|
||||
|
||||
case "$INSTALL_TYPE" in
|
||||
"project")
|
||||
if [ ! -d ".cursor" ]; then
|
||||
mkdir -p .cursor
|
||||
fi
|
||||
TARGET_DIR=".cursor"
|
||||
HOOKS_DIR=".cursor/hooks"
|
||||
;;
|
||||
"user")
|
||||
TARGET_DIR="${HOME}/.cursor"
|
||||
HOOKS_DIR="${HOME}/.cursor/hooks"
|
||||
;;
|
||||
*)
|
||||
echo "Invalid install type: $INSTALL_TYPE"
|
||||
echo "Usage: $0 [user (recommended)|project]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Create hooks directory
|
||||
mkdir -p "$HOOKS_DIR"
|
||||
|
||||
# Copy hook scripts
|
||||
echo "Copying hook scripts..."
|
||||
cp "$SCRIPT_DIR"/*.sh "$HOOKS_DIR/"
|
||||
chmod +x "$HOOKS_DIR"/*.sh
|
||||
|
||||
# Copy hooks.json
|
||||
echo "Copying hooks.json..."
|
||||
cp "$SCRIPT_DIR/hooks.json" "$TARGET_DIR/hooks.json"
|
||||
|
||||
# Update paths in hooks.json if needed
|
||||
# Use portable sed approach that works on both BSD (macOS) and GNU (Linux) sed
|
||||
if [ "$INSTALL_TYPE" = "project" ]; then
|
||||
# For project-level, paths should be relative
|
||||
# Create temp file, modify, then move (portable across sed variants)
|
||||
tmp_file=$(mktemp)
|
||||
sed 's|\./cursor-hooks/|\./\.cursor/hooks/|g' "$TARGET_DIR/hooks.json" > "$tmp_file"
|
||||
mv "$tmp_file" "$TARGET_DIR/hooks.json"
|
||||
else
|
||||
# For user-level, use absolute paths
|
||||
tmp_file=$(mktemp)
|
||||
sed "s|\./cursor-hooks/|${HOOKS_DIR}/|g" "$TARGET_DIR/hooks.json" > "$tmp_file"
|
||||
mv "$tmp_file" "$TARGET_DIR/hooks.json"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "✓ Installation complete!"
|
||||
echo ""
|
||||
echo "Hooks installed to: $TARGET_DIR/hooks.json"
|
||||
echo "Scripts installed to: $HOOKS_DIR"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Ensure claude-mem worker is running:"
|
||||
echo " cd ~/.claude/plugins/marketplaces/thedotmack && npm run worker:start"
|
||||
echo ""
|
||||
echo "2. Restart Cursor to load the hooks"
|
||||
echo ""
|
||||
echo "3. Check Cursor Settings → Hooks tab to verify hooks are active"
|
||||
echo ""
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
# Save File Edit Hook for Cursor (PowerShell)
|
||||
# Captures file edits made by the agent
|
||||
# Maps file edits to claude-mem observations
|
||||
|
||||
$ErrorActionPreference = "SilentlyContinue"
|
||||
|
||||
# Source common utilities
|
||||
$commonPath = Join-Path $PSScriptRoot "common.ps1"
|
||||
if (Test-Path $commonPath) {
|
||||
. $commonPath
|
||||
} else {
|
||||
Write-Warning "common.ps1 not found, using fallback functions"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Read JSON input from stdin with error handling
|
||||
$input = Read-JsonInput
|
||||
|
||||
# Extract common fields with safe fallbacks
|
||||
$conversationId = Get-JsonField $input "conversation_id" ""
|
||||
$generationId = Get-JsonField $input "generation_id" ""
|
||||
$filePath = Get-JsonField $input "file_path" ""
|
||||
$workspaceRoot = Get-JsonField $input "workspace_roots[0]" ""
|
||||
|
||||
# Fallback to current directory if no workspace root
|
||||
if (Test-IsEmpty $workspaceRoot) {
|
||||
$workspaceRoot = Get-Location
|
||||
}
|
||||
|
||||
# Exit if no file_path
|
||||
if (Test-IsEmpty $filePath) {
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Use conversation_id as session_id, fallback to generation_id
|
||||
$sessionId = $conversationId
|
||||
if (Test-IsEmpty $sessionId) {
|
||||
$sessionId = $generationId
|
||||
}
|
||||
|
||||
# Exit if no session_id available
|
||||
if (Test-IsEmpty $sessionId) {
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Get worker port from settings with validation
|
||||
$workerPort = Get-WorkerPort
|
||||
|
||||
# Extract edits array, defaulting to [] if invalid
|
||||
$edits = @()
|
||||
if ($input.PSObject.Properties.Name -contains "edits") {
|
||||
$edits = $input.edits
|
||||
if ($null -eq $edits -or -not ($edits -is [array])) {
|
||||
$edits = @()
|
||||
}
|
||||
}
|
||||
|
||||
# Exit if no edits
|
||||
if ($edits.Count -eq 0) {
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Create a summary of the edits for the observation
|
||||
$editSummaries = @()
|
||||
foreach ($edit in $edits) {
|
||||
$oldStr = ""
|
||||
$newStr = ""
|
||||
|
||||
if ($edit.PSObject.Properties.Name -contains "old_string") {
|
||||
$oldStr = $edit.old_string
|
||||
if ($oldStr.Length -gt 50) {
|
||||
$oldStr = $oldStr.Substring(0, 50) + "..."
|
||||
}
|
||||
}
|
||||
|
||||
if ($edit.PSObject.Properties.Name -contains "new_string") {
|
||||
$newStr = $edit.new_string
|
||||
if ($newStr.Length -gt 50) {
|
||||
$newStr = $newStr.Substring(0, 50) + "..."
|
||||
}
|
||||
}
|
||||
|
||||
$editSummaries += "$oldStr → $newStr"
|
||||
}
|
||||
|
||||
$editSummary = $editSummaries -join "; "
|
||||
if ([string]::IsNullOrEmpty($editSummary)) {
|
||||
$editSummary = "File edited"
|
||||
}
|
||||
|
||||
# Treat file edits as a "write_file" tool usage
|
||||
$toolInput = @{
|
||||
file_path = $filePath
|
||||
edits = $edits
|
||||
}
|
||||
|
||||
$toolResponse = @{
|
||||
success = $true
|
||||
summary = $editSummary
|
||||
}
|
||||
|
||||
$payload = @{
|
||||
contentSessionId = $sessionId
|
||||
tool_name = "write_file"
|
||||
tool_input = $toolInput
|
||||
tool_response = $toolResponse
|
||||
cwd = $workspaceRoot
|
||||
}
|
||||
|
||||
# Ensure worker is running (with retries like claude-mem hooks)
|
||||
if (-not (Test-WorkerReady -Port $workerPort)) {
|
||||
# Worker not ready - exit gracefully (don't block Cursor)
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Send observation to claude-mem worker (fire-and-forget)
|
||||
$uri = "http://127.0.0.1:$workerPort/api/sessions/observations"
|
||||
|
||||
try {
|
||||
$bodyJson = ConvertTo-JsonCompact $payload
|
||||
Invoke-RestMethod -Uri $uri -Method Post -Body $bodyJson -ContentType "application/json" -TimeoutSec 5 -ErrorAction SilentlyContinue | Out-Null
|
||||
} catch {
|
||||
# Ignore errors - don't block Cursor
|
||||
}
|
||||
|
||||
exit 0
|
||||
@@ -1,112 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Save File Edit Hook for Cursor
|
||||
# Captures file edits made by the agent
|
||||
# Maps file edits to claude-mem observations
|
||||
|
||||
# Source common utilities
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/common.sh" 2>/dev/null || {
|
||||
echo "Warning: common.sh not found, using fallback functions" >&2
|
||||
}
|
||||
|
||||
# Check dependencies (non-blocking)
|
||||
check_dependencies >/dev/null 2>&1 || true
|
||||
|
||||
# Read JSON input from stdin with error handling
|
||||
input=$(read_json_input)
|
||||
|
||||
# Extract common fields with safe fallbacks
|
||||
conversation_id=$(json_get "$input" "conversation_id" "")
|
||||
generation_id=$(json_get "$input" "generation_id" "")
|
||||
file_path=$(json_get "$input" "file_path" "")
|
||||
workspace_root=$(json_get "$input" "workspace_roots[0]" "")
|
||||
|
||||
# Fallback to current directory if no workspace root
|
||||
if is_empty "$workspace_root"; then
|
||||
workspace_root=$(pwd)
|
||||
fi
|
||||
|
||||
# Exit if no file_path
|
||||
if is_empty "$file_path"; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Use conversation_id as session_id, fallback to generation_id
|
||||
session_id="$conversation_id"
|
||||
if is_empty "$session_id"; then
|
||||
session_id="$generation_id"
|
||||
fi
|
||||
|
||||
# Exit if no session_id available
|
||||
if is_empty "$session_id"; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Get worker port from settings with validation
|
||||
worker_port=$(get_worker_port)
|
||||
|
||||
# Extract edits array, defaulting to [] if invalid
|
||||
edits=$(echo "$input" | jq -c '.edits // []' 2>/dev/null || echo "[]")
|
||||
|
||||
# Validate edits is a valid JSON array
|
||||
if ! echo "$edits" | jq 'type == "array"' 2>/dev/null | grep -q true; then
|
||||
edits="[]"
|
||||
fi
|
||||
|
||||
# Exit if no edits
|
||||
if [ "$edits" = "[]" ] || is_empty "$edits"; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Create a summary of the edits for the observation (with error handling)
|
||||
edit_summary=$(echo "$edits" | jq -r '[.[] | "\(.old_string[0:50] // "")... → \(.new_string[0:50] // "")..."] | join("; ")' 2>/dev/null || echo "File edited")
|
||||
|
||||
# Treat file edits as a "write_file" tool usage
|
||||
tool_input=$(jq -n \
|
||||
--arg path "$file_path" \
|
||||
--argjson edits "$edits" \
|
||||
'{
|
||||
file_path: $path,
|
||||
edits: $edits
|
||||
}' 2>/dev/null || echo '{}')
|
||||
|
||||
tool_response=$(jq -n \
|
||||
--arg summary "$edit_summary" \
|
||||
'{
|
||||
success: true,
|
||||
summary: $summary
|
||||
}' 2>/dev/null || echo '{}')
|
||||
|
||||
payload=$(jq -n \
|
||||
--arg sessionId "$session_id" \
|
||||
--arg cwd "$workspace_root" \
|
||||
--argjson toolInput "$tool_input" \
|
||||
--argjson toolResponse "$tool_response" \
|
||||
'{
|
||||
contentSessionId: $sessionId,
|
||||
tool_name: "write_file",
|
||||
tool_input: $toolInput,
|
||||
tool_response: $toolResponse,
|
||||
cwd: $cwd
|
||||
}' 2>/dev/null)
|
||||
|
||||
# Exit if payload creation failed
|
||||
if [ -z "$payload" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Ensure worker is running (with retries like claude-mem hooks)
|
||||
if ! ensure_worker_running "$worker_port"; then
|
||||
# Worker not ready - exit gracefully (don't block Cursor)
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Send observation to claude-mem worker (fire-and-forget)
|
||||
curl -s -X POST \
|
||||
"http://127.0.0.1:${worker_port}/api/sessions/observations" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$payload" \
|
||||
>/dev/null 2>&1 || true
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
# Save Observation Hook for Cursor (PowerShell)
|
||||
# Captures MCP tool usage and shell command execution
|
||||
# Maps to claude-mem's save-hook functionality
|
||||
|
||||
$ErrorActionPreference = "SilentlyContinue"
|
||||
|
||||
# Source common utilities
|
||||
$commonPath = Join-Path $PSScriptRoot "common.ps1"
|
||||
if (Test-Path $commonPath) {
|
||||
. $commonPath
|
||||
} else {
|
||||
Write-Warning "common.ps1 not found, using fallback functions"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Read JSON input from stdin with error handling
|
||||
$input = Read-JsonInput
|
||||
|
||||
# Extract common fields with safe fallbacks
|
||||
$conversationId = Get-JsonField $input "conversation_id" ""
|
||||
$generationId = Get-JsonField $input "generation_id" ""
|
||||
$workspaceRoot = Get-JsonField $input "workspace_roots[0]" ""
|
||||
|
||||
# Fallback to current directory if no workspace root
|
||||
if (Test-IsEmpty $workspaceRoot) {
|
||||
$workspaceRoot = Get-Location
|
||||
}
|
||||
|
||||
# Use conversation_id as session_id (stable across turns), fallback to generation_id
|
||||
$sessionId = $conversationId
|
||||
if (Test-IsEmpty $sessionId) {
|
||||
$sessionId = $generationId
|
||||
}
|
||||
|
||||
# Exit if no session_id available
|
||||
if (Test-IsEmpty $sessionId) {
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Get worker port from settings with validation
|
||||
$workerPort = Get-WorkerPort
|
||||
|
||||
# Determine hook type and extract relevant data
|
||||
$hookEvent = Get-JsonField $input "hook_event_name" ""
|
||||
|
||||
$payload = $null
|
||||
|
||||
if ($hookEvent -eq "afterMCPExecution") {
|
||||
# MCP tool execution
|
||||
$toolName = Get-JsonField $input "tool_name" ""
|
||||
|
||||
if (Test-IsEmpty $toolName) {
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Extract tool_input and tool_response, defaulting to {} if invalid
|
||||
$toolInput = @{}
|
||||
$toolResponse = @{}
|
||||
|
||||
if ($input.PSObject.Properties.Name -contains "tool_input") {
|
||||
$toolInput = $input.tool_input
|
||||
if ($null -eq $toolInput) { $toolInput = @{} }
|
||||
}
|
||||
|
||||
if ($input.PSObject.Properties.Name -contains "result_json") {
|
||||
$toolResponse = $input.result_json
|
||||
if ($null -eq $toolResponse) { $toolResponse = @{} }
|
||||
}
|
||||
|
||||
# Prepare observation payload
|
||||
$payload = @{
|
||||
contentSessionId = $sessionId
|
||||
tool_name = $toolName
|
||||
tool_input = $toolInput
|
||||
tool_response = $toolResponse
|
||||
cwd = $workspaceRoot
|
||||
}
|
||||
|
||||
} elseif ($hookEvent -eq "afterShellExecution") {
|
||||
# Shell command execution
|
||||
$command = Get-JsonField $input "command" ""
|
||||
|
||||
if (Test-IsEmpty $command) {
|
||||
exit 0
|
||||
}
|
||||
|
||||
$output = Get-JsonField $input "output" ""
|
||||
|
||||
# Treat shell commands as "Bash" tool usage
|
||||
$toolInput = @{ command = $command }
|
||||
$toolResponse = @{ output = $output }
|
||||
|
||||
$payload = @{
|
||||
contentSessionId = $sessionId
|
||||
tool_name = "Bash"
|
||||
tool_input = $toolInput
|
||||
tool_response = $toolResponse
|
||||
cwd = $workspaceRoot
|
||||
}
|
||||
|
||||
} else {
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Exit if payload creation failed
|
||||
if ($null -eq $payload) {
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Ensure worker is running (with retries like claude-mem hooks)
|
||||
if (-not (Test-WorkerReady -Port $workerPort)) {
|
||||
# Worker not ready - exit gracefully (don't block Cursor)
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Send observation to claude-mem worker (fire-and-forget)
|
||||
$uri = "http://127.0.0.1:$workerPort/api/sessions/observations"
|
||||
|
||||
try {
|
||||
$bodyJson = ConvertTo-JsonCompact $payload
|
||||
Invoke-RestMethod -Uri $uri -Method Post -Body $bodyJson -ContentType "application/json" -TimeoutSec 5 -ErrorAction SilentlyContinue | Out-Null
|
||||
} catch {
|
||||
# Ignore errors - don't block Cursor
|
||||
}
|
||||
|
||||
exit 0
|
||||
@@ -1,129 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Save Observation Hook for Cursor
|
||||
# Captures MCP tool usage and shell command execution
|
||||
# Maps to claude-mem's save-hook functionality
|
||||
|
||||
# Source common utilities
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/common.sh" 2>/dev/null || {
|
||||
echo "Warning: common.sh not found, using fallback functions" >&2
|
||||
}
|
||||
|
||||
# Check dependencies (non-blocking)
|
||||
check_dependencies >/dev/null 2>&1 || true
|
||||
|
||||
# Read JSON input from stdin with error handling
|
||||
input=$(read_json_input)
|
||||
|
||||
# Extract common fields with safe fallbacks
|
||||
conversation_id=$(json_get "$input" "conversation_id" "")
|
||||
generation_id=$(json_get "$input" "generation_id" "")
|
||||
workspace_root=$(json_get "$input" "workspace_roots[0]" "")
|
||||
|
||||
# Fallback to current directory if no workspace root
|
||||
if is_empty "$workspace_root"; then
|
||||
workspace_root=$(pwd)
|
||||
fi
|
||||
|
||||
# Use conversation_id as session_id (stable across turns), fallback to generation_id
|
||||
session_id="$conversation_id"
|
||||
if is_empty "$session_id"; then
|
||||
session_id="$generation_id"
|
||||
fi
|
||||
|
||||
# Exit if no session_id available
|
||||
if is_empty "$session_id"; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Get worker port from settings with validation
|
||||
worker_port=$(get_worker_port)
|
||||
|
||||
# Determine hook type and extract relevant data
|
||||
hook_event=$(json_get "$input" "hook_event_name" "")
|
||||
|
||||
if [ "$hook_event" = "afterMCPExecution" ]; then
|
||||
# MCP tool execution
|
||||
tool_name=$(json_get "$input" "tool_name" "")
|
||||
|
||||
if is_empty "$tool_name"; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Extract tool_input and tool_response, defaulting to {} if invalid
|
||||
tool_input=$(echo "$input" | jq -c '.tool_input // {}' 2>/dev/null || echo "{}")
|
||||
tool_response=$(echo "$input" | jq -c '.result_json // {}' 2>/dev/null || echo "{}")
|
||||
|
||||
# Validate JSON
|
||||
if ! echo "$tool_input" | jq empty 2>/dev/null; then
|
||||
tool_input="{}"
|
||||
fi
|
||||
if ! echo "$tool_response" | jq empty 2>/dev/null; then
|
||||
tool_response="{}"
|
||||
fi
|
||||
|
||||
# Prepare observation payload
|
||||
payload=$(jq -n \
|
||||
--arg sessionId "$session_id" \
|
||||
--arg toolName "$tool_name" \
|
||||
--argjson toolInput "$tool_input" \
|
||||
--argjson toolResponse "$tool_response" \
|
||||
--arg cwd "$workspace_root" \
|
||||
'{
|
||||
contentSessionId: $sessionId,
|
||||
tool_name: $toolName,
|
||||
tool_input: $toolInput,
|
||||
tool_response: $toolResponse,
|
||||
cwd: $cwd
|
||||
}' 2>/dev/null)
|
||||
|
||||
elif [ "$hook_event" = "afterShellExecution" ]; then
|
||||
# Shell command execution
|
||||
command=$(json_get "$input" "command" "")
|
||||
|
||||
if is_empty "$command"; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
output=$(json_get "$input" "output" "")
|
||||
|
||||
# Treat shell commands as "Bash" tool usage
|
||||
tool_input=$(jq -n --arg cmd "$command" '{command: $cmd}' 2>/dev/null || echo '{}')
|
||||
tool_response=$(jq -n --arg out "$output" '{output: $out}' 2>/dev/null || echo '{}')
|
||||
|
||||
payload=$(jq -n \
|
||||
--arg sessionId "$session_id" \
|
||||
--arg cwd "$workspace_root" \
|
||||
--argjson toolInput "$tool_input" \
|
||||
--argjson toolResponse "$tool_response" \
|
||||
'{
|
||||
contentSessionId: $sessionId,
|
||||
tool_name: "Bash",
|
||||
tool_input: $toolInput,
|
||||
tool_response: $toolResponse,
|
||||
cwd: $cwd
|
||||
}' 2>/dev/null)
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Exit if payload creation failed
|
||||
if [ -z "$payload" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Ensure worker is running (with retries like claude-mem hooks)
|
||||
if ! ensure_worker_running "$worker_port"; then
|
||||
# Worker not ready - exit gracefully (don't block Cursor)
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Send observation to claude-mem worker (fire-and-forget)
|
||||
curl -s -X POST \
|
||||
"http://127.0.0.1:${worker_port}/api/sessions/observations" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$payload" \
|
||||
>/dev/null 2>&1 || true
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
# Session Initialization Hook for Cursor (PowerShell)
|
||||
# Maps to claude-mem's new-hook functionality
|
||||
# Initializes a new session when a prompt is submitted
|
||||
#
|
||||
# NOTE: This hook runs as part of beforeSubmitPrompt and MUST output valid JSON
|
||||
# with at least {"continue": true} to allow prompt submission.
|
||||
|
||||
$ErrorActionPreference = "SilentlyContinue"
|
||||
|
||||
# Source common utilities
|
||||
$commonPath = Join-Path $PSScriptRoot "common.ps1"
|
||||
if (Test-Path $commonPath) {
|
||||
. $commonPath
|
||||
} else {
|
||||
# Fallback - output continue and exit
|
||||
Write-Output '{"continue": true}'
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Read JSON input from stdin with error handling
|
||||
$input = Read-JsonInput
|
||||
|
||||
# Extract common fields with safe fallbacks
|
||||
$conversationId = Get-JsonField $input "conversation_id" ""
|
||||
$generationId = Get-JsonField $input "generation_id" ""
|
||||
$prompt = Get-JsonField $input "prompt" ""
|
||||
$workspaceRoot = Get-JsonField $input "workspace_roots[0]" ""
|
||||
|
||||
# Fallback to current directory if no workspace root
|
||||
if (Test-IsEmpty $workspaceRoot) {
|
||||
$workspaceRoot = Get-Location
|
||||
}
|
||||
|
||||
# Get project name from workspace root
|
||||
$projectName = Get-ProjectName $workspaceRoot
|
||||
|
||||
# Use conversation_id as session_id (stable across turns), fallback to generation_id
|
||||
$sessionId = $conversationId
|
||||
if (Test-IsEmpty $sessionId) {
|
||||
$sessionId = $generationId
|
||||
}
|
||||
|
||||
# Exit gracefully if no session_id available (still allow prompt)
|
||||
if (Test-IsEmpty $sessionId) {
|
||||
Write-Output '{"continue": true}'
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Get worker port from settings with validation
|
||||
$workerPort = Get-WorkerPort
|
||||
|
||||
# Ensure worker is running (with retries like claude-mem hooks)
|
||||
if (-not (Test-WorkerReady -Port $workerPort)) {
|
||||
# Worker not ready - still allow prompt to continue
|
||||
Write-Output '{"continue": true}'
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Strip leading slash from commands for memory agent (parity with new-hook.ts)
|
||||
# /review 101 → review 101 (more semantic for observations)
|
||||
$cleanedPrompt = $prompt
|
||||
if (-not [string]::IsNullOrEmpty($prompt) -and $prompt.StartsWith("/")) {
|
||||
$cleanedPrompt = $prompt.Substring(1)
|
||||
}
|
||||
|
||||
# Initialize session via HTTP - handles DB operations and privacy checks
|
||||
$payload = @{
|
||||
contentSessionId = $sessionId
|
||||
project = $projectName
|
||||
prompt = $cleanedPrompt
|
||||
}
|
||||
|
||||
# Send request to worker (fire-and-forget, don't wait for response)
|
||||
$uri = "http://127.0.0.1:$workerPort/api/sessions/init"
|
||||
Send-HttpPostAsync -Uri $uri -Body $payload
|
||||
|
||||
# Always allow prompt to continue
|
||||
Write-Output '{"continue": true}'
|
||||
exit 0
|
||||
@@ -1,93 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Session Initialization Hook for Cursor
|
||||
# Maps to claude-mem's new-hook functionality
|
||||
# Initializes a new session when a prompt is submitted
|
||||
#
|
||||
# NOTE: This hook runs as part of beforeSubmitPrompt and MUST output valid JSON
|
||||
# with at least {"continue": true} to allow prompt submission.
|
||||
|
||||
# Source common utilities
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/common.sh" 2>/dev/null || {
|
||||
# Fallback - output continue and exit
|
||||
echo '{"continue": true}'
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Check dependencies (non-blocking - just warn)
|
||||
check_dependencies >/dev/null 2>&1 || true
|
||||
|
||||
# Read JSON input from stdin with error handling
|
||||
input=$(read_json_input)
|
||||
|
||||
# Extract common fields with safe fallbacks
|
||||
conversation_id=$(json_get "$input" "conversation_id" "")
|
||||
generation_id=$(json_get "$input" "generation_id" "")
|
||||
prompt=$(json_get "$input" "prompt" "")
|
||||
workspace_root=$(json_get "$input" "workspace_roots[0]" "")
|
||||
|
||||
# Fallback to current directory if no workspace root
|
||||
if is_empty "$workspace_root"; then
|
||||
workspace_root=$(pwd)
|
||||
fi
|
||||
|
||||
# Get project name from workspace root
|
||||
project_name=$(get_project_name "$workspace_root")
|
||||
|
||||
# Use conversation_id as session_id (stable across turns), fallback to generation_id
|
||||
session_id="$conversation_id"
|
||||
if is_empty "$session_id"; then
|
||||
session_id="$generation_id"
|
||||
fi
|
||||
|
||||
# Exit gracefully if no session_id available (still allow prompt)
|
||||
if is_empty "$session_id"; then
|
||||
echo '{"continue": true}'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Get worker port from settings with validation
|
||||
worker_port=$(get_worker_port)
|
||||
|
||||
# Ensure worker is running (with retries like claude-mem hooks)
|
||||
if ! ensure_worker_running "$worker_port"; then
|
||||
# Worker not ready - still allow prompt to continue
|
||||
echo '{"continue": true}'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Strip leading slash from commands for memory agent (parity with new-hook.ts)
|
||||
# /review 101 → review 101 (more semantic for observations)
|
||||
cleaned_prompt="$prompt"
|
||||
if [ -n "$prompt" ] && [ "${prompt:0:1}" = "/" ]; then
|
||||
cleaned_prompt="${prompt:1}"
|
||||
fi
|
||||
|
||||
# Initialize session via HTTP - handles DB operations and privacy checks
|
||||
payload=$(jq -n \
|
||||
--arg sessionId "$session_id" \
|
||||
--arg project "$project_name" \
|
||||
--arg promptText "$cleaned_prompt" \
|
||||
'{
|
||||
contentSessionId: $sessionId,
|
||||
project: $project,
|
||||
prompt: $promptText
|
||||
}' 2>/dev/null)
|
||||
|
||||
# Exit if payload creation failed (still allow prompt)
|
||||
if [ -z "$payload" ]; then
|
||||
echo '{"continue": true}'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Send request to worker (fire-and-forget, don't wait for response)
|
||||
curl -s -X POST \
|
||||
"http://127.0.0.1:${worker_port}/api/sessions/init" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$payload" \
|
||||
>/dev/null 2>&1 &
|
||||
|
||||
# Always allow prompt to continue
|
||||
echo '{"continue": true}'
|
||||
exit 0
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
# Session Summary Hook for Cursor (stop) - PowerShell
|
||||
# Called when agent loop ends
|
||||
#
|
||||
# This hook:
|
||||
# 1. Generates session summary
|
||||
# 2. Updates context file for next session
|
||||
#
|
||||
# Output: Empty JSON {} or {"followup_message": "..."} for auto-iteration
|
||||
|
||||
$ErrorActionPreference = "SilentlyContinue"
|
||||
|
||||
# Source common utilities
|
||||
$commonPath = Join-Path $PSScriptRoot "common.ps1"
|
||||
if (Test-Path $commonPath) {
|
||||
. $commonPath
|
||||
} else {
|
||||
Write-Output '{}'
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Read JSON input from stdin with error handling
|
||||
$input = Read-JsonInput
|
||||
|
||||
# Extract common fields with safe fallbacks
|
||||
$conversationId = Get-JsonField $input "conversation_id" ""
|
||||
$generationId = Get-JsonField $input "generation_id" ""
|
||||
$workspaceRoot = Get-JsonField $input "workspace_roots[0]" ""
|
||||
$status = Get-JsonField $input "status" "completed"
|
||||
|
||||
# Fallback workspace to current directory
|
||||
if (Test-IsEmpty $workspaceRoot) {
|
||||
$workspaceRoot = Get-Location
|
||||
}
|
||||
|
||||
# Get project name
|
||||
$projectName = Get-ProjectName $workspaceRoot
|
||||
|
||||
# Use conversation_id as session_id, fallback to generation_id
|
||||
$sessionId = $conversationId
|
||||
if (Test-IsEmpty $sessionId) {
|
||||
$sessionId = $generationId
|
||||
}
|
||||
|
||||
# Exit if no session_id available
|
||||
if (Test-IsEmpty $sessionId) {
|
||||
Write-Output '{}'
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Get worker port from settings with validation
|
||||
$workerPort = Get-WorkerPort
|
||||
|
||||
# Ensure worker is running (with retries)
|
||||
if (-not (Test-WorkerReady -Port $workerPort)) {
|
||||
Write-Output '{}'
|
||||
exit 0
|
||||
}
|
||||
|
||||
# 1. Request summary generation (fire-and-forget)
|
||||
# Note: Cursor doesn't provide transcript_path like Claude Code does,
|
||||
# so we can't extract last_user_message and last_assistant_message.
|
||||
$summaryPayload = @{
|
||||
contentSessionId = $sessionId
|
||||
last_user_message = ""
|
||||
last_assistant_message = ""
|
||||
}
|
||||
|
||||
$summaryUri = "http://127.0.0.1:$workerPort/api/sessions/summarize"
|
||||
Send-HttpPostAsync -Uri $summaryUri -Body $summaryPayload
|
||||
|
||||
# 2. Update context file for next session
|
||||
# Fetch fresh context (includes observations from this session)
|
||||
$projectEncoded = Get-UrlEncodedString $projectName
|
||||
$contextUri = "http://127.0.0.1:$workerPort/api/context/inject?project=$projectEncoded"
|
||||
$context = Get-HttpResponse -Uri $contextUri
|
||||
|
||||
if (-not [string]::IsNullOrEmpty($context)) {
|
||||
$rulesDir = Join-Path $workspaceRoot ".cursor\rules"
|
||||
$rulesFile = Join-Path $rulesDir "claude-mem-context.mdc"
|
||||
|
||||
# Create rules directory if it doesn't exist
|
||||
if (-not (Test-Path $rulesDir)) {
|
||||
New-Item -ItemType Directory -Path $rulesDir -Force | Out-Null
|
||||
}
|
||||
|
||||
# Write context as a Cursor rule with alwaysApply: true
|
||||
$ruleContent = @"
|
||||
---
|
||||
alwaysApply: true
|
||||
description: "Claude-mem context from past sessions (auto-updated)"
|
||||
---
|
||||
|
||||
# Memory Context from Past Sessions
|
||||
|
||||
The following context is from claude-mem, a persistent memory system that tracks your coding sessions.
|
||||
|
||||
$context
|
||||
|
||||
---
|
||||
*Updated after last session. Use claude-mem's MCP search tools for more detailed queries.*
|
||||
"@
|
||||
|
||||
Set-Content -Path $rulesFile -Value $ruleContent -Encoding UTF8 -Force
|
||||
}
|
||||
|
||||
# Output empty JSON - no followup message
|
||||
Write-Output '{}'
|
||||
exit 0
|
||||
@@ -1,111 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Session Summary Hook for Cursor (stop)
|
||||
# Called when agent loop ends
|
||||
#
|
||||
# This hook:
|
||||
# 1. Generates session summary
|
||||
# 2. Updates context file for next session
|
||||
#
|
||||
# Output: Empty JSON {} or {"followup_message": "..."} for auto-iteration
|
||||
|
||||
# Source common utilities
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/common.sh" 2>/dev/null || {
|
||||
echo '{}'
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Check dependencies (non-blocking)
|
||||
check_dependencies >/dev/null 2>&1 || true
|
||||
|
||||
# Read JSON input from stdin with error handling
|
||||
input=$(read_json_input)
|
||||
|
||||
# Extract common fields with safe fallbacks
|
||||
conversation_id=$(json_get "$input" "conversation_id" "")
|
||||
generation_id=$(json_get "$input" "generation_id" "")
|
||||
workspace_root=$(json_get "$input" "workspace_roots[0]" "")
|
||||
status=$(json_get "$input" "status" "completed")
|
||||
|
||||
# Fallback workspace to current directory
|
||||
if is_empty "$workspace_root"; then
|
||||
workspace_root=$(pwd)
|
||||
fi
|
||||
|
||||
# Get project name
|
||||
project_name=$(get_project_name "$workspace_root")
|
||||
|
||||
# Use conversation_id as session_id, fallback to generation_id
|
||||
session_id="$conversation_id"
|
||||
if is_empty "$session_id"; then
|
||||
session_id="$generation_id"
|
||||
fi
|
||||
|
||||
# Exit if no session_id available
|
||||
if is_empty "$session_id"; then
|
||||
echo '{}'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Get worker port from settings with validation
|
||||
worker_port=$(get_worker_port)
|
||||
|
||||
# Ensure worker is running (with retries)
|
||||
if ! ensure_worker_running "$worker_port"; then
|
||||
echo '{}'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 1. Request summary generation (fire-and-forget)
|
||||
# Note: Cursor doesn't provide transcript_path like Claude Code does,
|
||||
# so we can't extract last_user_message and last_assistant_message.
|
||||
payload=$(jq -n \
|
||||
--arg sessionId "$session_id" \
|
||||
'{
|
||||
contentSessionId: $sessionId,
|
||||
last_user_message: "",
|
||||
last_assistant_message: ""
|
||||
}' 2>/dev/null)
|
||||
|
||||
if [ -n "$payload" ]; then
|
||||
curl -s -X POST \
|
||||
"http://127.0.0.1:${worker_port}/api/sessions/summarize" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$payload" \
|
||||
>/dev/null 2>&1 &
|
||||
fi
|
||||
|
||||
# 2. Update context file for next session
|
||||
# Fetch fresh context (includes observations from this session)
|
||||
project_encoded=$(url_encode "$project_name")
|
||||
context=$(curl -s -f "http://127.0.0.1:${worker_port}/api/context/inject?project=${project_encoded}" 2>/dev/null || echo "")
|
||||
|
||||
if [ -n "$context" ]; then
|
||||
rules_dir="${workspace_root}/.cursor/rules"
|
||||
rules_file="${rules_dir}/claude-mem-context.mdc"
|
||||
|
||||
# Create rules directory if it doesn't exist
|
||||
mkdir -p "$rules_dir" 2>/dev/null || true
|
||||
|
||||
# Write context as a Cursor rule with alwaysApply: true
|
||||
cat > "$rules_file" 2>/dev/null << EOF
|
||||
---
|
||||
alwaysApply: true
|
||||
description: "Claude-mem context from past sessions (auto-updated)"
|
||||
---
|
||||
|
||||
# Memory Context from Past Sessions
|
||||
|
||||
The following context is from claude-mem, a persistent memory system that tracks your coding sessions.
|
||||
|
||||
${context}
|
||||
|
||||
---
|
||||
*Updated after last session. Use claude-mem's MCP search tools for more detailed queries.*
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Output empty JSON - no followup message
|
||||
echo '{}'
|
||||
exit 0
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
# User Message Hook for Cursor (PowerShell)
|
||||
# Displays context information to the user
|
||||
# Maps to claude-mem's user-message-hook functionality
|
||||
# Note: Cursor doesn't have a direct equivalent, but we can output to stderr
|
||||
# for visibility in Cursor's output channels
|
||||
#
|
||||
# This is an OPTIONAL hook. It can be added to beforeSubmitPrompt if desired,
|
||||
# but may be verbose since it runs on every prompt submission.
|
||||
|
||||
$ErrorActionPreference = "SilentlyContinue"
|
||||
|
||||
# Read JSON input from stdin (if any)
|
||||
$inputJson = $null
|
||||
try {
|
||||
$inputText = [Console]::In.ReadToEnd()
|
||||
if (-not [string]::IsNullOrEmpty($inputText)) {
|
||||
$inputJson = $inputText | ConvertFrom-Json -ErrorAction SilentlyContinue
|
||||
}
|
||||
} catch {
|
||||
$inputJson = $null
|
||||
}
|
||||
|
||||
# Extract workspace root
|
||||
$workspaceRoot = ""
|
||||
if ($null -ne $inputJson -and $inputJson.PSObject.Properties.Name -contains "workspace_roots") {
|
||||
$wsRoots = $inputJson.workspace_roots
|
||||
if ($null -ne $wsRoots -and $wsRoots.Count -gt 0) {
|
||||
$workspaceRoot = $wsRoots[0]
|
||||
}
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrEmpty($workspaceRoot)) {
|
||||
$workspaceRoot = Get-Location
|
||||
}
|
||||
|
||||
# Get project name
|
||||
$projectName = Split-Path $workspaceRoot -Leaf
|
||||
if ([string]::IsNullOrEmpty($projectName)) {
|
||||
$projectName = "unknown-project"
|
||||
}
|
||||
|
||||
# Get worker port from settings
|
||||
$settingsPath = Join-Path $env:USERPROFILE ".claude-mem\settings.json"
|
||||
$workerPort = 37777
|
||||
|
||||
if (Test-Path $settingsPath) {
|
||||
try {
|
||||
$settings = Get-Content $settingsPath -Raw | ConvertFrom-Json
|
||||
if ($settings.CLAUDE_MEM_WORKER_PORT) {
|
||||
$workerPort = [int]$settings.CLAUDE_MEM_WORKER_PORT
|
||||
}
|
||||
} catch {
|
||||
# Use default
|
||||
}
|
||||
}
|
||||
|
||||
# Ensure worker is running
|
||||
$maxRetries = 75
|
||||
$workerReady = $false
|
||||
|
||||
for ($i = 0; $i -lt $maxRetries; $i++) {
|
||||
try {
|
||||
$response = Invoke-RestMethod -Uri "http://127.0.0.1:$workerPort/api/readiness" -Method Get -TimeoutSec 1 -ErrorAction Stop
|
||||
$workerReady = $true
|
||||
break
|
||||
} catch {
|
||||
Start-Sleep -Milliseconds 200
|
||||
}
|
||||
}
|
||||
|
||||
# If worker not ready, exit silently
|
||||
if (-not $workerReady) {
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Fetch formatted context from worker API (with colors)
|
||||
$projectEncoded = [System.Uri]::EscapeDataString($projectName)
|
||||
$contextUrl = "http://127.0.0.1:$workerPort/api/context/inject?project=$projectEncoded&colors=true"
|
||||
|
||||
$output = $null
|
||||
try {
|
||||
$output = Invoke-RestMethod -Uri $contextUrl -Method Get -TimeoutSec 5 -ErrorAction Stop
|
||||
} catch {
|
||||
$output = $null
|
||||
}
|
||||
|
||||
# Output to stderr for visibility (parity with user-message-hook.ts)
|
||||
# Note: Cursor may not display stderr the same way Claude Code does,
|
||||
# but this is the best we can do without direct UI integration
|
||||
if (-not [string]::IsNullOrEmpty($output)) {
|
||||
[Console]::Error.WriteLine("")
|
||||
[Console]::Error.WriteLine("📝 Claude-Mem Context Loaded")
|
||||
[Console]::Error.WriteLine(" ℹ️ Viewing context from past sessions")
|
||||
[Console]::Error.WriteLine("")
|
||||
[Console]::Error.WriteLine($output)
|
||||
[Console]::Error.WriteLine("")
|
||||
[Console]::Error.WriteLine("💡 Tip: Wrap content with <private> ... </private> to prevent storing sensitive information.")
|
||||
[Console]::Error.WriteLine("💬 Community: https://discord.gg/J4wttp9vDu")
|
||||
[Console]::Error.WriteLine("📺 Web Viewer: http://localhost:$workerPort/")
|
||||
[Console]::Error.WriteLine("")
|
||||
}
|
||||
|
||||
exit 0
|
||||
@@ -1,70 +0,0 @@
|
||||
#!/bin/bash
|
||||
# User Message Hook for Cursor
|
||||
# Displays context information to the user
|
||||
# Maps to claude-mem's user-message-hook functionality
|
||||
# Note: Cursor doesn't have a direct equivalent, but we can output to stderr
|
||||
# for visibility in Cursor's output channels
|
||||
#
|
||||
# This is an OPTIONAL hook. It can be added to beforeSubmitPrompt if desired,
|
||||
# but may be verbose since it runs on every prompt submission.
|
||||
|
||||
# Read JSON input from stdin (if any)
|
||||
input=$(cat 2>/dev/null || echo "{}")
|
||||
|
||||
# Extract workspace root
|
||||
workspace_root=$(echo "$input" | jq -r '.workspace_roots[0] // empty' 2>/dev/null || echo "")
|
||||
|
||||
if [ -z "$workspace_root" ]; then
|
||||
workspace_root=$(pwd)
|
||||
fi
|
||||
|
||||
# Get project name
|
||||
project_name=$(basename "$workspace_root" 2>/dev/null || echo "unknown-project")
|
||||
|
||||
# Get worker port from settings
|
||||
data_dir="${HOME}/.claude-mem"
|
||||
settings_file="${data_dir}/settings.json"
|
||||
worker_port="37777"
|
||||
|
||||
if [ -f "$settings_file" ]; then
|
||||
worker_port=$(jq -r '.CLAUDE_MEM_WORKER_PORT // "37777"' "$settings_file" 2>/dev/null || echo "37777")
|
||||
fi
|
||||
|
||||
# Ensure worker is running
|
||||
max_retries=75
|
||||
retry_count=0
|
||||
while [ $retry_count -lt $max_retries ]; do
|
||||
if curl -s -f "http://127.0.0.1:${worker_port}/api/readiness" > /dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 0.2
|
||||
retry_count=$((retry_count + 1))
|
||||
done
|
||||
|
||||
# If worker not ready, exit silently
|
||||
if [ $retry_count -eq $max_retries ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Fetch formatted context from worker API (with colors)
|
||||
context_url="http://127.0.0.1:${worker_port}/api/context/inject?project=${project_name}&colors=true"
|
||||
output=$(curl -s -f "$context_url" 2>/dev/null || echo "")
|
||||
|
||||
# Output to stderr for visibility (parity with user-message-hook.ts)
|
||||
# Note: Cursor may not display stderr the same way Claude Code does,
|
||||
# but this is the best we can do without direct UI integration
|
||||
if [ -n "$output" ]; then
|
||||
echo "" >&2
|
||||
echo "📝 Claude-Mem Context Loaded" >&2
|
||||
echo " ℹ️ Viewing context from past sessions" >&2
|
||||
echo "" >&2
|
||||
echo "$output" >&2
|
||||
echo "" >&2
|
||||
echo "💡 Tip: Wrap content with <private> ... </private> to prevent storing sensitive information." >&2
|
||||
echo "💬 Community: https://discord.gg/J4wttp9vDu" >&2
|
||||
echo "📺 Web Viewer: http://localhost:${worker_port}/" >&2
|
||||
echo "" >&2
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
||||
+52
-46
@@ -1,5 +1,4 @@
|
||||
🌐 هذه ترجمة آلية. نرحب بالتصحيحات من المجتمع!
|
||||
|
||||
<section dir="rtl">
|
||||
<h1 align="center">
|
||||
<br>
|
||||
<a href="https://github.com/thedotmack/claude-mem">
|
||||
@@ -43,7 +42,8 @@
|
||||
<a href="README.no.md">🇳🇴 Norsk</a>
|
||||
</p>
|
||||
|
||||
<h4 align="center">نظام ضغط الذاكرة المستمرة المبني لـ <a href="https://claude.com/claude-code" target="_blank">Claude Code</a>.</h4>
|
||||
<h4 align="center">أداة إضافية لـ <a href="https://claude.com/claude-code" target="_blank">Claude Code</a> تعمل على أتمتة تسجيل معلومات الجلسات السابقه، وضغطها, ثم حقن السياق ذي الصلة في الجلسات المستقبلية.
|
||||
</h4>
|
||||
|
||||
<p align="center">
|
||||
<a href="LICENSE">
|
||||
@@ -80,25 +80,27 @@
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="#البدء-السريع">البدء السريع</a> •
|
||||
<a href="#كيف-يعمل">كيف يعمل</a> •
|
||||
<a href="#أدوات-البحث-mcp">أدوات البحث</a> •
|
||||
<a href="#التوثيق">التوثيق</a> •
|
||||
<a href="#الإعدادات">الإعدادات</a> •
|
||||
<a href="#استكشاف-الأخطاء-وإصلاحها">استكشاف الأخطاء وإصلاحها</a> •
|
||||
<a href="#الترخيص">الترخيص</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
يحافظ Claude-Mem بسلاسة على السياق عبر الجلسات من خلال التقاط الملاحظات حول استخدام الأدوات تلقائيًا، وإنشاء ملخصات دلالية، وإتاحتها للجلسات المستقبلية. هذا يمكّن Claude من الحفاظ على استمرارية المعرفة حول المشاريع حتى بعد انتهاء الجلسات أو إعادة الاتصال.
|
||||
<a href="#بداية-سريعة">بداية سريعة</a> •
|
||||
<a href="#كيف-يعمل">كيف يعمل</a> •
|
||||
<a href="#أدوات-البحث-mcp-search-tools">أدوات البحث</a> •
|
||||
<a href="#المستندات">التوثيق</a> •
|
||||
<a href="#الإعدادات">الإعدادات</a> •
|
||||
<a href="#استكشاف-الأخطاء-وإصلاحها">استكشاف الأخطاء وإصلاحها</a> •
|
||||
<a href="#الترخيص-license">الترخيص</a>
|
||||
</p>
|
||||
|
||||
<p align="center" dir="rtl">
|
||||
Claude-Mem هو نظام متطور مصمم لضغط وحفظ الذاكرة لسياق عمل Claude Code. وظيفته الأساسية هي جعل "كلود" يتذكر ما فعله في جلسات العمل السابقة بسلاسة، عبر تسجيل تحركاته، وإنشاء ملخصات ذكية، واستدعائها في الجلسات المستقبلية. هذا يضمن عدم ضياع سياق المشروع حتى لو أغلقت البرنامج وفتحته لاحقاً.
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
## البدء السريع
|
||||
## بداية سريعة
|
||||
|
||||
ابدأ جلسة Claude Code جديدة في الطرفية وأدخل الأوامر التالية:
|
||||
للبدء، افتح "Claude Code" في مبنى الأوامر (Terminal) واكتب الأوامر التالية:
|
||||
<div dir="ltr" align="left">
|
||||
|
||||
```
|
||||
> /plugin marketplace add thedotmack/claude-mem
|
||||
@@ -106,24 +108,26 @@
|
||||
> /plugin install claude-mem
|
||||
```
|
||||
|
||||
أعد تشغيل Claude Code. سيظهر السياق من الجلسات السابقة تلقائيًا في الجلسات الجديدة.
|
||||
</div>
|
||||
|
||||
بمجرد إعادة تشغيل Claude Code، سيتم استدعاء السياق من الجلسات السابقة تلقائيا عند الحاجة.
|
||||
|
||||
**الميزات الرئيسية:**
|
||||
|
||||
- 🧠 **ذاكرة مستمرة** - يبقى السياق عبر الجلسات
|
||||
- 📊 **الكشف التدريجي** - استرجاع الذاكرة بطبقات مع رؤية تكلفة الرموز
|
||||
- 🔍 **بحث قائم على المهارات** - استعلم عن تاريخ مشروعك باستخدام مهارة mem-search
|
||||
- 🖥️ **واجهة مستخدم ويب** - بث الذاكرة المباشر على http://localhost:37777
|
||||
- 💻 **مهارة Claude Desktop** - ابحث في الذاكرة من محادثات Claude Desktop
|
||||
- 🔒 **التحكم في الخصوصية** - استخدم وسوم `<private>` لاستبعاد المحتوى الحساس من التخزين
|
||||
- ⚙️ **إعدادات السياق** - تحكم دقيق في السياق الذي يتم حقنه
|
||||
- 🤖 **تشغيل تلقائي** - لا يتطلب تدخلاً يدويًا
|
||||
- 🔗 **الاستشهادات** - رجوع إلى الملاحظات السابقة باستخدام المعرفات (الوصول عبر http://localhost:37777/api/observation/{id} أو عرض الكل في عارض الويب على http://localhost:37777)
|
||||
- 🧪 **قناة تجريبية** - جرّب الميزات التجريبية مثل Endless Mode عبر تبديل الإصدار
|
||||
- 🧠 **ذاكرة مستديمه**: سياق عملك لا ينتهي بانتهاء الجلسة، بل ينتقل معك للجلسة التالية.
|
||||
- 📊 **الكشف التدريجي** (Progressive Disclosure): نظام ذكي يستدعي المعلومات على طبقات، مما يمنحك رؤية واضحة لاستهلاك الـ "Tokens" (التكلفة).
|
||||
- 🔍 **بحث سريع** - استعلم عن سجل مشروعك باستخدام خاصية `mem-search`.
|
||||
- 🖥️ **واجهة مستخدم ويب** - رؤية معلومات الذاكرة مع تحديث فوري عبر المتصفح من خلال الرابط: http://localhost:37777
|
||||
- 💻 **تكامل مع Claude Desktop** - إمكانية البحث في الذاكرة مباشرة من واجهة Claude المكتبية
|
||||
- 🔒 **التحكم في الخصوصية** - دعم وسم `<private>` لمنع النظام من تخزين أي معلومات حساسة.
|
||||
- ⚙️ **إعدادات السياق** - تحكم دقيق في السياق (context) التي سيتم حقنها في سياق المحادثة.
|
||||
- 🤖 **أتمتة كاملة:** - النظام يعمل في الخلفية دون الحاجة لتدخل يدوي منك.
|
||||
- 🔗 **الاستشهادات** - رجوع إلى الملاحظات السابقة باستخدام (http://localhost:37777/api/observation/{id} أو عرض جميع المعلومات على http://localhost:37777)
|
||||
- 🧪 **مزايا التجريبيه** - تجربة مميزات مثل "الوضع اللانهائي" (Endless Mode).
|
||||
|
||||
---
|
||||
|
||||
## التوثيق
|
||||
## المستندات
|
||||
|
||||
📚 **[عرض التوثيق الكامل](docs/)** - تصفح مستندات markdown على GitHub
|
||||
|
||||
@@ -131,7 +135,7 @@
|
||||
|
||||
- **[دليل التثبيت](https://docs.claude-mem.ai/installation)** - البدء السريع والتثبيت المتقدم
|
||||
- **[دليل الاستخدام](https://docs.claude-mem.ai/usage/getting-started)** - كيف يعمل Claude-Mem تلقائيًا
|
||||
- **[أدوات البحث](https://docs.claude-mem.ai/usage/search-tools)** - استعلم عن تاريخ مشروعك باللغة الطبيعية
|
||||
- **[أدوات البحث](https://docs.claude-mem.ai/usage/search-tools)** - استعلم عن سجل مشروعك بلغتك
|
||||
- **[الميزات التجريبية](https://docs.claude-mem.ai/beta-features)** - جرّب الميزات التجريبية مثل Endless Mode
|
||||
|
||||
### أفضل الممارسات
|
||||
@@ -142,9 +146,9 @@
|
||||
### البنية المعمارية
|
||||
|
||||
- **[نظرة عامة](https://docs.claude-mem.ai/architecture/overview)** - مكونات النظام وتدفق البيانات
|
||||
- **[تطور البنية المعمارية](https://docs.claude-mem.ai/architecture-evolution)** - الرحلة من v3 إلى v5
|
||||
- **[بنية الخطافات](https://docs.claude-mem.ai/hooks-architecture)** - كيف يستخدم Claude-Mem خطافات دورة الحياة
|
||||
- **[مرجع الخطافات](https://docs.claude-mem.ai/architecture/hooks)** - شرح 7 سكريبتات خطافات
|
||||
- **[تطور البنية المعمارية](https://docs.claude-mem.ai/architecture-evolution)** - تطور المعمارية من v3 إلى v5
|
||||
- **[بنية برامج الربط (Hooks)](https://docs.claude-mem.ai/hooks-architecture)** - كيف يستخدم Claude-Mem خطافات دورة الحياة
|
||||
- **[مرجع برامج الربط (Hooks)](https://docs.claude-mem.ai/architecture/hooks)** - شرح 7 سكريبتات خطافات
|
||||
- **[خدمة العامل](https://docs.claude-mem.ai/architecture/worker-service)** - HTTP API وإدارة Bun
|
||||
- **[قاعدة البيانات](https://docs.claude-mem.ai/architecture/database)** - مخطط SQLite وبحث FTS5
|
||||
- **[بنية البحث](https://docs.claude-mem.ai/architecture/search-architecture)** - البحث المختلط مع قاعدة بيانات المتجهات Chroma
|
||||
@@ -161,24 +165,23 @@
|
||||
|
||||
**المكونات الأساسية:**
|
||||
|
||||
1. **5 خطافات دورة الحياة** - SessionStart، UserPromptSubmit، PostToolUse، Stop، SessionEnd (6 سكريبتات خطافات)
|
||||
2. **تثبيت ذكي** - فاحص التبعيات المخزنة مؤقتًا (سكريبت ما قبل الخطاف، ليس خطاف دورة حياة)
|
||||
1. **5 برامج ربط (Hooks)** - SessionStart، UserPromptSubmit، PostToolUse، Stop، SessionEnd
|
||||
2. **تثبيت ذكي** - فاحص التبعيات المخزنة مؤقتًا
|
||||
3. **خدمة العامل** - HTTP API على المنفذ 37777 مع واجهة مستخدم عارض الويب و10 نقاط نهاية للبحث، تديرها Bun
|
||||
4. **قاعدة بيانات SQLite** - تخزن الجلسات، الملاحظات، الملخصات
|
||||
5. **مهارة mem-search** - استعلامات اللغة الطبيعية مع الكشف التدريجي
|
||||
6. **قاعدة بيانات المتجهات Chroma** - البحث المختلط الدلالي + الكلمات المفتاحية لاسترجاع السياق الذكي
|
||||
6. **قاعدة بيانات المتجهات Chroma** - البحث الدلالي الهجين + الكلمات المفتاحية لاسترجاع السياق الذكي
|
||||
|
||||
انظر [نظرة عامة على البنية المعمارية](https://docs.claude-mem.ai/architecture/overview) للتفاصيل.
|
||||
|
||||
---
|
||||
|
||||
## مهارة mem-search
|
||||
|
||||
## أدوات البحث (MCP Search Tools)
|
||||
يوفر Claude-Mem بحثًا ذكيًا من خلال مهارة mem-search التي تُستدعى تلقائيًا عندما تسأل عن العمل السابق:
|
||||
|
||||
**كيف يعمل:**
|
||||
- فقط اسأل بشكل طبيعي: *"ماذا فعلنا في الجلسة الأخيرة؟"* أو *"هل أصلحنا هذا الخطأ من قبل؟"*
|
||||
- يستدعي Claude تلقائيًا مهارة mem-search للعثور على السياق ذي الصلة
|
||||
- يستدعي Claude تلقائيًا خاصية mem-search للعثور على السياق ذي الصلة
|
||||
|
||||
**عمليات البحث المتاحة:**
|
||||
|
||||
@@ -193,7 +196,7 @@
|
||||
9. **الجدول الزمني حسب الاستعلام** - البحث عن الملاحظات والحصول على سياق الجدول الزمني حول أفضل تطابق
|
||||
10. **مساعدة API** - الحصول على توثيق API البحث
|
||||
|
||||
**أمثلة على استعلامات اللغة الطبيعية:**
|
||||
**أمثلة على الاستعلامات:**
|
||||
|
||||
```
|
||||
"What bugs did we fix last session?"
|
||||
@@ -219,8 +222,7 @@
|
||||
|
||||
- **Node.js**: 18.0.0 أو أعلى
|
||||
- **Claude Code**: أحدث إصدار مع دعم الإضافات
|
||||
- **Bun**: بيئة تشغيل JavaScript ومدير العمليات (يُثبت تلقائيًا إذا كان مفقودًا)
|
||||
- **uv**: مدير حزم Python للبحث المتجهي (يُثبت تلقائيًا إذا كان مفقودًا)
|
||||
- **Bun & uv**: (يتم تثبيتهما تلقائياً) لإدارة العمليات والبحث المتجه.
|
||||
- **SQLite 3**: للتخزين المستمر (مدمج)
|
||||
|
||||
---
|
||||
@@ -241,7 +243,7 @@
|
||||
|
||||
## استكشاف الأخطاء وإصلاحها
|
||||
|
||||
إذا واجهت مشكلات، صِف المشكلة لـ Claude وستقوم مهارة troubleshoot تلقائيًا بتشخيصها وتوفير الإصلاحات.
|
||||
إذا واجهت مشكلة، اشرحها لـ Claude وسيقوم بتشغيل خاصية troubleshoot لإصلاحها ذاتياً.
|
||||
|
||||
انظر **[دليل استكشاف الأخطاء وإصلاحها](https://docs.claude-mem.ai/troubleshooting)** للمشكلات الشائعة والحلول.
|
||||
|
||||
@@ -250,27 +252,29 @@
|
||||
## تقارير الأخطاء
|
||||
|
||||
أنشئ تقارير أخطاء شاملة باستخدام المولّد الآلي:
|
||||
<div align=left>
|
||||
|
||||
```bash
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack
|
||||
npm run bug-report
|
||||
```
|
||||
</div>
|
||||
|
||||
## المساهمة
|
||||
|
||||
المساهمات مرحب بها! يُرجى:
|
||||
|
||||
1. عمل Fork للمستودع
|
||||
2. إنشاء فرع ميزة
|
||||
1. عمل Fork للمشروع (Repository)
|
||||
2. إنشاء فرع (branch)
|
||||
3. إجراء التغييرات مع الاختبارات
|
||||
4. تحديث التوثيق
|
||||
4. تحديث المستندات عند الحاجه
|
||||
5. تقديم Pull Request
|
||||
|
||||
انظر [دليل التطوير](https://docs.claude-mem.ai/development) لسير عمل المساهمة.
|
||||
|
||||
---
|
||||
|
||||
## الترخيص
|
||||
## الترخيص (License)
|
||||
|
||||
هذا المشروع مرخص بموجب **ترخيص GNU Affero العام الإصدار 3.0** (AGPL-3.0).
|
||||
|
||||
@@ -298,4 +302,6 @@ npm run bug-report
|
||||
|
||||
---
|
||||
|
||||
**مبني باستخدام Claude Agent SDK** | **مدعوم بواسطة Claude Code** | **صُنع باستخدام TypeScript**
|
||||
**مبني باستخدام Claude Agent SDK** | **مدعوم بواسطة Claude Code** | **صُنع باستخدام TypeScript**
|
||||
|
||||
</section>
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
# Context Hook Investigation Report
|
||||
|
||||
**Date:** 2026-01-05
|
||||
**Branch:** `feature/no-more-hook-files`
|
||||
**Status:** Partial fix committed, additional issues identified
|
||||
|
||||
## Problem
|
||||
|
||||
User reported no startup context appearing when testing the new unified CLI hook architecture.
|
||||
|
||||
## Root Cause Identified
|
||||
|
||||
**SessionStart hooks don't receive stdin data from Claude Code.**
|
||||
|
||||
The unified CLI architecture assumed all hooks receive stdin JSON data. When `readJsonFromStdin()` returns `undefined` for SessionStart, the platform adapters crashed:
|
||||
|
||||
```
|
||||
TypeError: undefined is not an object (evaluating 'e.session_id')
|
||||
```
|
||||
|
||||
**Location:** `src/cli/adapters/claude-code.ts:6` and `src/cli/adapters/cursor.ts:7`
|
||||
|
||||
The adapters did `const r = raw as any;` then accessed `r.session_id`, which fails when `raw` is `undefined`.
|
||||
|
||||
## Fix Applied
|
||||
|
||||
Changed both adapters to handle undefined input:
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
const r = raw as any;
|
||||
|
||||
// After
|
||||
const r = (raw ?? {}) as any;
|
||||
```
|
||||
|
||||
**Commit:** `78c2a0ef` - Pushed to `feature/no-more-hook-files`
|
||||
|
||||
## Additional Issue Discovered (Not Yet Fixed)
|
||||
|
||||
There's a **path mismatch** in the hooks.json that may cause issues:
|
||||
|
||||
- hooks.json references: `${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs`
|
||||
- Actual file location: `${CLAUDE_PLUGIN_ROOT}/plugin/scripts/worker-service.cjs`
|
||||
|
||||
The marketplace sync copies the whole repo structure, so files end up in a `plugin/` subdirectory. Need to verify what `CLAUDE_PLUGIN_ROOT` resolves to and whether the paths are correct.
|
||||
|
||||
## Verification Needed
|
||||
|
||||
1. Start a new Claude Code session and verify context appears
|
||||
2. Check that `CLAUDE_PLUGIN_ROOT` points to correct directory
|
||||
3. Verify hooks.json paths match actual file locations
|
||||
|
||||
## Files Changed
|
||||
|
||||
- `src/cli/adapters/claude-code.ts` - Added null coalescing for stdin
|
||||
- `src/cli/adapters/cursor.ts` - Added null coalescing for stdin
|
||||
- `plugin/scripts/worker-service.cjs` - Rebuilt with fix
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Test the fix in a live Claude Code session
|
||||
2. Investigate the `CLAUDE_PLUGIN_ROOT` path resolution
|
||||
3. Fix paths in hooks.json if needed
|
||||
+4
-24
@@ -3,35 +3,15 @@
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Jan 5, 2026
|
||||
### Jan 3, 2026
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #37962 | 8:18 PM | 🔴 | Fixed SessionStart hook crash when stdin is undefined | ~440 |
|
||||
| #36651 | 11:03 PM | 🔵 | Critical Design Decision Documented: Memory Session ID Must Never Equal Content Session ID | ~481 |
|
||||
|
||||
### Jan 7, 2026
|
||||
### Jan 8, 2026
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #38475 | 10:31 PM | ⚖️ | Log Level Philosophy: Error-Adjacent Messages Promoted to ERROR | ~412 |
|
||||
| #38467 | 10:29 PM | ⚖️ | Log Level Audit Strategy: Tighten ERROR Messages for Runtime Issue Discovery | ~464 |
|
||||
| #38445 | 10:26 PM | 🔵 | DEBUG Level Logging: SessionRoutes Line 211 Error-During-Recovery Pattern | ~500 |
|
||||
| #38442 | 10:25 PM | 🔵 | Log Audit Contains 409 Source File Log Entries | ~293 |
|
||||
| #38441 | " | 🔵 | DEBUG-level logging patterns for diagnostics and non-critical operations | ~595 |
|
||||
| #38439 | " | 🔵 | Log Audit Shows SessionRoutes.ts Has Two WARN Messages for Generator Failures | ~493 |
|
||||
| #38438 | " | 🔵 | WARN Level Log Patterns: Graceful Degradation and Fallback Behavior | ~539 |
|
||||
| #38437 | 10:24 PM | 🔵 | Claude-mem core functionality and logging patterns identified | ~710 |
|
||||
| #38428 | " | 🔵 | Log level audit report structure and content examined | ~559 |
|
||||
| #38425 | " | ⚖️ | Log Level Architecture: Fail-Critical Over Fail-Fast for Chroma | ~467 |
|
||||
| #38416 | 10:22 PM | 🔵 | ChromaDB Is Critical Not Optional - Log Audit Findings Challenged | ~405 |
|
||||
| #38405 | 10:07 PM | ⚖️ | DEBUG Log Level Analysis - One Message Requires WARN Promotion | ~819 |
|
||||
| #38404 | 10:06 PM | ⚖️ | Log Level Audit Analysis - WARN to ERROR Promotion Criteria Established | ~769 |
|
||||
| #38403 | 10:04 PM | 🔵 | Log Level Audit - INFO and DEBUG Level Messages Catalogued | ~688 |
|
||||
| #38402 | 10:03 PM | 🔵 | Log Level Audit Report Analysis - Critical Error Messages Identified | ~642 |
|
||||
| #38401 | 10:00 PM | 🔵 | Enhanced Audit Report Reveals Error Logging Patterns and Message Extraction Issues | ~498 |
|
||||
| #38394 | 9:58 PM | ✅ | Created Log Level Audit Report Documentation | ~319 |
|
||||
| #38393 | " | ✅ | Enhanced Log Audit Report Format with Component Tags and Full Logger Calls | ~393 |
|
||||
| #38386 | 9:56 PM | ✅ | Log Audit Report Generated and Saved to Documentation | ~442 |
|
||||
| #38385 | " | ✅ | Log Level Audit Report Saved to Documentation | ~379 |
|
||||
| #38251 | 7:46 PM | 🔵 | Comprehensive Windows Platform Issues Report | ~982 |
|
||||
| #38731 | 6:49 PM | 🟣 | Comprehensive Sonnet vs Opus Behavioral Analysis Report Generated and Saved | ~700 |
|
||||
</claude-mem-context>
|
||||
@@ -1,688 +0,0 @@
|
||||
🔍 Scanning for error handling anti-patterns...
|
||||
|
||||
Found 80 TypeScript files
|
||||
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
ERROR HANDLING ANTI-PATTERNS DETECTED
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
Found 153 anti-patterns:
|
||||
🔴 CRITICAL: 26
|
||||
🟠 HIGH: 47
|
||||
🟡 MEDIUM: 80
|
||||
|
||||
🔴 CRITICAL ISSUES (Fix immediately - these cause silent failures):
|
||||
─────────────────────────────────────────────────────────────
|
||||
|
||||
📁 src/utils/transcript-parser.ts:44
|
||||
❌ NO_LOGGING_IN_CATCH
|
||||
Catch block has no logging - errors occur invisibly.
|
||||
|
||||
Code:
|
||||
} catch (error) {
|
||||
// [POSSIBLY RELEVANT]: Parse errors accumulated in parseErrors array for batch access, logging each line would be excessive
|
||||
this.parseErrors.push({
|
||||
lineNumber: index + 1,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
... (2 more lines)
|
||||
|
||||
📁 src/shared/timeline-formatting.ts:18
|
||||
❌ NO_LOGGING_IN_CATCH
|
||||
Catch block has no logging - errors occur invisibly.
|
||||
|
||||
Code:
|
||||
} catch (err) {
|
||||
// [POSSIBLY RELEVANT]: Expected JSON parse failures for malformed data fields, too frequent to log
|
||||
return [];
|
||||
}
|
||||
|
||||
📁 src/shared/paths.ts:105
|
||||
❌ NO_LOGGING_IN_CATCH
|
||||
Catch block has no logging - errors occur invisibly.
|
||||
|
||||
Code:
|
||||
} catch (error) {
|
||||
// [POSSIBLY RELEVANT]: Expected when not in git repo or git unavailable, common fallback path
|
||||
return basename(process.cwd());
|
||||
}
|
||||
|
||||
📁 src/sdk/prompts.ts:98
|
||||
❌ NO_LOGGING_IN_CATCH
|
||||
Catch block has no logging - errors occur invisibly.
|
||||
|
||||
Code:
|
||||
} catch (error) {
|
||||
// [POSSIBLY RELEVANT]: Expected JSON parse failures for plain string tool inputs, normal fallback
|
||||
toolInput = obs.tool_input;
|
||||
}
|
||||
|
||||
📁 src/sdk/prompts.ts:105
|
||||
❌ NO_LOGGING_IN_CATCH
|
||||
Catch block has no logging - errors occur invisibly.
|
||||
|
||||
Code:
|
||||
} catch (error) {
|
||||
// [POSSIBLY RELEVANT]: Expected JSON parse failures for plain string tool outputs, normal fallback
|
||||
toolOutput = obs.tool_output;
|
||||
}
|
||||
|
||||
📁 src/services/worker-service.ts:68
|
||||
❌ CATCH_AND_CONTINUE_CRITICAL_PATH
|
||||
Critical path continues after error - may cause silent data corruption.
|
||||
|
||||
Code:
|
||||
} catch (error) {
|
||||
// [POSSIBLY RELEVANT]: PID file cleanup is non-critical, log full error and continue shutdown
|
||||
logger.warn('SYSTEM', 'Failed to remove PID file', { path: PID_FILE }, error as Error);
|
||||
}
|
||||
|
||||
📁 src/services/worker-service.ts:131
|
||||
❌ CATCH_AND_CONTINUE_CRITICAL_PATH
|
||||
Critical path continues after error - may cause silent data corruption.
|
||||
|
||||
Code:
|
||||
} catch (error) {
|
||||
// [POSSIBLY RELEVANT]: Context update is non-critical, log full error and continue
|
||||
logger.warn('CURSOR', 'Failed to update context file', { projectName }, error as Error);
|
||||
}
|
||||
|
||||
📁 src/services/worker-service.ts:152
|
||||
❌ NO_LOGGING_IN_CATCH
|
||||
Catch block has no logging - errors occur invisibly.
|
||||
|
||||
Code:
|
||||
} catch (error) {
|
||||
// [POSSIBLY RELEVANT]: Expected failure when port is free, called frequently for health checks
|
||||
return false;
|
||||
}
|
||||
|
||||
📁 src/services/worker-service.ts:165
|
||||
❌ CATCH_AND_CONTINUE_CRITICAL_PATH
|
||||
Critical path continues after error - may cause silent data corruption.
|
||||
|
||||
Code:
|
||||
} catch (error) {
|
||||
// [POSSIBLY RELEVANT]: Expected failures during startup health check, will retry
|
||||
logger.debug('SYSTEM', 'Service not ready yet, will retry', { port }, error as Error);
|
||||
}
|
||||
|
||||
📁 src/services/worker-service.ts:368
|
||||
❌ CATCH_AND_CONTINUE_CRITICAL_PATH
|
||||
Critical path continues after error - may cause silent data corruption.
|
||||
|
||||
Code:
|
||||
} catch (error) {
|
||||
// [POSSIBLY RELEVANT]: Shutdown must complete, log error and exit with failure code
|
||||
logger.error('SYSTEM', 'Error during shutdown', {}, error as Error);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
📁 src/services/worker-service.ts:623
|
||||
❌ CATCH_AND_CONTINUE_CRITICAL_PATH
|
||||
Critical path continues after error - may cause silent data corruption.
|
||||
|
||||
Code:
|
||||
} catch (error) {
|
||||
// [POSSIBLY RELEVANT]: Process may have already exited during cleanup, expected failure
|
||||
logger.debug('SYSTEM', 'Failed to kill process, may have already exited', { pid }, error as Error);
|
||||
}
|
||||
|
||||
📁 src/services/worker-service.ts:632
|
||||
❌ CATCH_AND_CONTINUE_CRITICAL_PATH
|
||||
Critical path continues after error - may cause silent data corruption.
|
||||
|
||||
Code:
|
||||
} catch (error) {
|
||||
// [POSSIBLY RELEVANT]: Process may have already exited during cleanup, expected failure
|
||||
logger.debug('SYSTEM', 'Process already exited', { pid }, error as Error);
|
||||
}
|
||||
|
||||
📁 src/services/worker-service.ts:838
|
||||
❌ CATCH_AND_CONTINUE_CRITICAL_PATH
|
||||
Critical path continues after error - may cause silent data corruption.
|
||||
|
||||
Code:
|
||||
} catch (error) {
|
||||
// Recovery is best-effort - skip failed sessions and continue with others
|
||||
logger.warn('SYSTEM', `Failed to process session ${sessionDbId}`, {}, error as Error);
|
||||
result.sessionsSkipped++;
|
||||
}
|
||||
|
||||
📁 src/services/worker-service.ts:987
|
||||
❌ CATCH_AND_CONTINUE_CRITICAL_PATH
|
||||
Critical path continues after error - may cause silent data corruption.
|
||||
|
||||
Code:
|
||||
} catch {
|
||||
// Process may have already exited - continue shutdown
|
||||
logger.debug('SYSTEM', 'Process already exited during force kill', { pid });
|
||||
}
|
||||
|
||||
📁 src/services/worker-service.ts:1004
|
||||
❌ NO_LOGGING_IN_CATCH
|
||||
Catch block has no logging - errors occur invisibly.
|
||||
|
||||
Code:
|
||||
} catch (error) {
|
||||
// Expected: process has exited
|
||||
// Not logging - this is called in a tight loop during cleanup
|
||||
return false;
|
||||
}
|
||||
|
||||
📁 src/services/worker-service.ts:1095
|
||||
❌ EMPTY_CATCH
|
||||
Empty catch block - errors are silently swallowed. User will waste hours debugging.
|
||||
|
||||
Code:
|
||||
} catch {
|
||||
// Start fresh if corrupt
|
||||
}
|
||||
|
||||
📁 src/services/worker-service.ts:1301
|
||||
❌ EMPTY_CATCH
|
||||
Empty catch block - errors are silently swallowed. User will waste hours debugging.
|
||||
|
||||
Code:
|
||||
} catch {
|
||||
// CLI not found
|
||||
}
|
||||
|
||||
📁 src/services/worker-service.ts:1413
|
||||
❌ CATCH_AND_CONTINUE_CRITICAL_PATH
|
||||
Critical path continues after error - may cause silent data corruption.
|
||||
|
||||
Code:
|
||||
} catch (error) {
|
||||
// Start fresh if corrupt
|
||||
logger.warn('SYSTEM', 'Corrupt mcp.json, creating new config', { path: mcpJsonPath, error: error instanceof Error ? error.message : String(error) });
|
||||
config = { mcpServers: {} };
|
||||
}
|
||||
|
||||
📁 src/services/worker-service.ts:1670
|
||||
❌ EMPTY_CATCH
|
||||
Empty catch block - errors are silently swallowed. User will waste hours debugging.
|
||||
|
||||
Code:
|
||||
} catch {
|
||||
// Worker not running - that's ok, context will be generated after first session
|
||||
}
|
||||
|
||||
📁 src/services/worker-service.ts:2050
|
||||
❌ PROMISE_CATCH_NO_LOGGING
|
||||
Promise .catch() without logging - errors are silently swallowed.
|
||||
|
||||
Code:
|
||||
.catch((error) => {
|
||||
logger.failure('SYSTEM', 'Worker failed to start', {}, error as Error);
|
||||
removePidFile();
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
📁 src/services/worker/SDKAgent.ts:545
|
||||
❌ CATCH_AND_CONTINUE_CRITICAL_PATH
|
||||
Critical path continues after error - may cause silent data corruption.
|
||||
|
||||
Code:
|
||||
} catch (error) {
|
||||
// [POSSIBLY RELEVANT]: Expected failure when claude not in PATH, falls through to clear error message below
|
||||
logger.debug('SDK', 'Claude executable auto-detection failed', {}, error as Error);
|
||||
}
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:1403
|
||||
❌ NO_LOGGING_IN_CATCH
|
||||
Catch block has no logging - errors occur invisibly.
|
||||
|
||||
Code:
|
||||
} catch (error) {
|
||||
// [POSSIBLY RELEVANT]: Expected JSON parse failures for plain string file lists, normal fallback
|
||||
if (summary.files_read.trim()) {
|
||||
lines.push(`**Files Read:** ${summary.files_read}`);
|
||||
}
|
||||
... (1 more lines)
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:1418
|
||||
❌ NO_LOGGING_IN_CATCH
|
||||
Catch block has no logging - errors occur invisibly.
|
||||
|
||||
Code:
|
||||
} catch (error) {
|
||||
// [POSSIBLY RELEVANT]: Expected JSON parse failures for plain string file lists, normal fallback
|
||||
if (summary.files_edited.trim()) {
|
||||
lines.push(`**Files Edited:** ${summary.files_edited}`);
|
||||
}
|
||||
... (1 more lines)
|
||||
|
||||
📁 src/services/worker/PaginationHelper.ts:54
|
||||
❌ NO_LOGGING_IN_CATCH
|
||||
Catch block has no logging - errors occur invisibly.
|
||||
|
||||
Code:
|
||||
} catch (err) {
|
||||
// [POSSIBLY RELEVANT]: Expected JSON parse failures for plain string file paths, normal fallback
|
||||
return filePathsStr;
|
||||
}
|
||||
|
||||
📁 src/services/context-generator.ts:202
|
||||
❌ NO_LOGGING_IN_CATCH
|
||||
Catch block has no logging - errors occur invisibly.
|
||||
|
||||
Code:
|
||||
} catch (parseError) {
|
||||
// [POSSIBLY RELEVANT]: Expected malformed JSON lines in transcript, logging each would be excessive
|
||||
continue;
|
||||
}
|
||||
|
||||
📁 src/services/context-generator.ts:226
|
||||
❌ NO_LOGGING_IN_CATCH
|
||||
Catch block has no logging - errors occur invisibly.
|
||||
|
||||
Code:
|
||||
} catch (error: any) {
|
||||
if (error.code === 'ERR_DLOPEN_FAILED') {
|
||||
unlinkSync(VERSION_MARKER_PATH);
|
||||
} catch (unlinkError) {
|
||||
// [POSSIBLY RELEVANT]: Marker file may not exist during first run, expected cleanup failure
|
||||
... (1 more lines)
|
||||
|
||||
🟠 HIGH PRIORITY:
|
||||
─────────────────────────────────────────────────────────────
|
||||
|
||||
📁 src/ui/viewer/hooks/useSSE.ts:50 - LARGE_TRY_BLOCK
|
||||
Try block has 33 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/ui/viewer/hooks/usePagination.ts:54 - LARGE_TRY_BLOCK
|
||||
Try block has 19 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/ui/viewer/hooks/useSettings.ts:64 - LARGE_TRY_BLOCK
|
||||
Try block has 14 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/ui/viewer/hooks/useContextPreview.ts:47 - LARGE_TRY_BLOCK
|
||||
Try block has 11 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/bin/import-xml-observations.ts:62 - LARGE_TRY_BLOCK
|
||||
Try block has 12 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/bin/import-xml-observations.ts:134 - LARGE_TRY_BLOCK
|
||||
Try block has 15 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/bin/import-xml-observations.ts:167 - LARGE_TRY_BLOCK
|
||||
Try block has 13 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/servers/mcp-server.ts:52 - LARGE_TRY_BLOCK
|
||||
Try block has 14 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/servers/mcp-server.ts:97 - LARGE_TRY_BLOCK
|
||||
Try block has 21 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/sqlite/SessionStore.ts:55 - LARGE_TRY_BLOCK
|
||||
Try block has 67 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/sqlite/SessionStore.ts:227 - LARGE_TRY_BLOCK
|
||||
Try block has 38 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/sqlite/SessionStore.ts:346 - LARGE_TRY_BLOCK
|
||||
Try block has 40 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/sqlite/SessionStore.ts:427 - LARGE_TRY_BLOCK
|
||||
Try block has 43 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/sqlite/SessionStore.ts:495 - LARGE_TRY_BLOCK
|
||||
Try block has 15 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/sqlite/SessionStore.ts:532 - LARGE_TRY_BLOCK
|
||||
Try block has 35 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/sqlite/SessionStore.ts:599 - LARGE_TRY_BLOCK
|
||||
Try block has 13 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/sqlite/SessionStore.ts:1550 - LARGE_TRY_BLOCK
|
||||
Try block has 27 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/worker-service.ts:438 - LARGE_TRY_BLOCK
|
||||
Try block has 16 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/worker-service.ts:526 - LARGE_TRY_BLOCK
|
||||
Try block has 11 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/worker-service.ts:666 - LARGE_TRY_BLOCK
|
||||
Try block has 56 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/worker-service.ts:814 - LARGE_TRY_BLOCK
|
||||
Try block has 15 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/worker-service.ts:1638 - LARGE_TRY_BLOCK
|
||||
Try block has 24 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/worker-service.ts:1753 - LARGE_TRY_BLOCK
|
||||
Try block has 28 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/sync/ChromaSync.ts:99 - LARGE_TRY_BLOCK
|
||||
Try block has 28 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/sync/ChromaSync.ts:344 - LARGE_TRY_BLOCK
|
||||
Try block has 14 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/sync/ChromaSync.ts:534 - LARGE_TRY_BLOCK
|
||||
Try block has 32 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/sync/ChromaSync.ts:609 - LARGE_TRY_BLOCK
|
||||
Try block has 106 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/worker/GeminiAgent.ts:144 - LARGE_TRY_BLOCK
|
||||
Try block has 76 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/worker/BranchManager.ts:120 - LARGE_TRY_BLOCK
|
||||
Try block has 13 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/worker/BranchManager.ts:268 - LARGE_TRY_BLOCK
|
||||
Try block has 21 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:120 - LARGE_TRY_BLOCK
|
||||
Try block has 43 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:382 - LARGE_TRY_BLOCK
|
||||
Try block has 13 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:642 - LARGE_TRY_BLOCK
|
||||
Try block has 22 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:726 - LARGE_TRY_BLOCK
|
||||
Try block has 18 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:818 - LARGE_TRY_BLOCK
|
||||
Try block has 14 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:888 - LARGE_TRY_BLOCK
|
||||
Try block has 16 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:958 - LARGE_TRY_BLOCK
|
||||
Try block has 16 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:1028 - LARGE_TRY_BLOCK
|
||||
Try block has 16 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:1098 - LARGE_TRY_BLOCK
|
||||
Try block has 16 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:1181 - LARGE_TRY_BLOCK
|
||||
Try block has 17 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:1282 - LARGE_TRY_BLOCK
|
||||
Try block has 16 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:1493 - LARGE_TRY_BLOCK
|
||||
Try block has 147 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:1725 - LARGE_TRY_BLOCK
|
||||
Try block has 15 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/worker/OpenRouterAgent.ts:104 - LARGE_TRY_BLOCK
|
||||
Try block has 77 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/worker/http/routes/SessionRoutes.ts:151 - LARGE_TRY_BLOCK
|
||||
Try block has 13 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/worker/http/routes/SessionRoutes.ts:185 - LARGE_TRY_BLOCK
|
||||
Try block has 20 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
📁 src/services/context-generator.ts:182 - LARGE_TRY_BLOCK
|
||||
Try block has 15 lines - too broad. Multiple errors lumped together.
|
||||
|
||||
🟡 MEDIUM PRIORITY:
|
||||
─────────────────────────────────────────────────────────────
|
||||
|
||||
📁 src/ui/viewer/hooks/useStats.ts:13 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/ui/viewer/hooks/useSSE.ts:93 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/ui/viewer/hooks/useTheme.ts:19 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/ui/viewer/hooks/useTheme.ts:64 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/ui/viewer/hooks/usePagination.ts:84 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/ui/viewer/hooks/useContextPreview.ts:31 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/ui/viewer/hooks/useContextPreview.ts:60 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/bin/import-xml-observations.ts:152 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/bin/import-xml-observations.ts:183 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/bin/import-xml-observations.ts:329 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/bin/import-xml-observations.ts:361 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/utils/logger.ts:55 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/utils/logger.ts:74 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/utils/logger.ts:269 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/shared/timeline-formatting.ts:18 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/shared/SettingsDefaultsManager.ts:152 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/shared/paths.ts:105 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/sdk/prompts.ts:98 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/sdk/prompts.ts:105 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/servers/mcp-server.ts:76 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/servers/mcp-server.ts:123 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/servers/mcp-server.ts:269 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/sqlite/SessionStore.ts:138 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/sqlite/SessionStore.ts:278 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/sqlite/SessionStore.ts:399 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/sqlite/SessionStore.ts:482 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/sqlite/SessionStore.ts:520 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/sqlite/SessionStore.ts:575 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/sqlite/SessionStore.ts:619 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/sqlite/SessionStore.ts:1489 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/sqlite/SessionStore.ts:1521 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/sqlite/SessionStore.ts:1577 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker-service.ts:59 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker-service.ts:68 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker-service.ts:131 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker-service.ts:152 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker-service.ts:165 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker-service.ts:185 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker-service.ts:368 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker-service.ts:623 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker-service.ts:632 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker-service.ts:743 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker-service.ts:838 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker-service.ts:963 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker-service.ts:1004 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker-service.ts:1797 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/queue/SessionQueueProcessor.ts:31 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/sync/ChromaSync.ts:578 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/sync/ChromaSync.ts:808 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker/SettingsManager.ts:45 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker/BranchManager.ts:138 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker/BranchManager.ts:243 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker/BranchManager.ts:300 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker/SDKAgent.ts:545 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:185 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:398 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:676 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:754 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:838 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:912 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:982 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:1052 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:1127 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:1214 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:1311 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:1403 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:1418 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:1700 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker/SearchManager.ts:1745 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker/PaginationHelper.ts:54 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker/http/BaseRouteHandler.ts:28 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker/http/routes/SettingsRoutes.ts:76 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker/http/routes/SessionRoutes.ts:165 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker/SessionManager.ts:208 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/worker/SessionManager.ts:256 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/domain/ModeManager.ts:146 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/domain/ModeManager.ts:163 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/domain/ModeManager.ts:173 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/context-generator.ts:202 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
📁 src/services/context-generator.ts:226 - GENERIC_CATCH
|
||||
Catch block handles all errors identically - no error type discrimination.
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
REMINDER: Every try-catch must answer these questions:
|
||||
1. What SPECIFIC error am I catching? (Name it)
|
||||
2. Show me documentation proving this error can occur
|
||||
3. Why can't this error be prevented?
|
||||
4. What will the catch block DO? (Log + rethrow? Fallback?)
|
||||
5. Why shouldn't this error propagate to the caller?
|
||||
|
||||
To approve an anti-pattern, add: // [APPROVED OVERRIDE]: reason
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
|
||||
❌ FAILED: 26 critical error handling anti-patterns must be fixed.
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "9.0.1",
|
||||
"version": "9.0.4",
|
||||
"description": "Memory compression system for Claude Code - persist context across sessions",
|
||||
"keywords": [
|
||||
"claude",
|
||||
|
||||
@@ -3,101 +3,21 @@
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Dec 28, 2025
|
||||
### Nov 6, 2025
|
||||
|
||||
**plugin.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #33503 | 10:43 PM | 🔵 | Plugin metadata contains no skill or MCP server definitions | ~216 |
|
||||
| #33486 | 10:35 PM | ✅ | Committed Version Bump to 8.2.6 | ~212 |
|
||||
| #33484 | " | ✅ | Verified Version Synchronization at 8.2.6 | ~173 |
|
||||
| #33483 | " | ✅ | Synchronized Plugin Metadata Version to 8.2.6 | ~183 |
|
||||
| #33478 | 10:34 PM | 🔵 | Plugin Metadata Configuration Structure | ~195 |
|
||||
| #33477 | " | 🔵 | Current Version Across Package Files is 8.2.5 | ~185 |
|
||||
| #4091 | 1:12 PM | 🔵 | Claude Plugin Configuration Structure | ~170 |
|
||||
|
||||
### Dec 29, 2025
|
||||
### Nov 9, 2025
|
||||
|
||||
**plugin.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34368 | 11:30 PM | ✅ | Bumped version to 8.5.1 for patch release | ~218 |
|
||||
| #34351 | 11:13 PM | ✅ | Version 8.5.0 Release Committed to Git | ~402 |
|
||||
| #34349 | 11:12 PM | ✅ | Version 8.5.0 Synchronized Across All Files | ~220 |
|
||||
| #33997 | 7:10 PM | ✅ | Version Bumped to 8.2.10 | ~179 |
|
||||
| #33995 | " | ✅ | Version Bump to 8.2.10 in plugin.json | ~250 |
|
||||
| #33992 | " | 🔵 | Version Consistency Verified Across Package Files | ~133 |
|
||||
| #33956 | 6:45 PM | 🔵 | Version 8.2.9 Release Verification Complete | ~227 |
|
||||
| #33950 | 6:42 PM | ✅ | Version Sync to 8.2.9 in Plugin Manifest | ~172 |
|
||||
| #33948 | " | 🔵 | Current Version State Across Project Files | ~195 |
|
||||
| #33854 | 4:33 PM | 🔵 | Worker service build and execution architecture mapped | ~509 |
|
||||
| #33840 | 4:24 PM | ✅ | Version 8.2.8 Changes Committed to Git | ~233 |
|
||||
| #33838 | 4:23 PM | ✅ | Patch Release Version Bump to 8.2.8 Completed | ~216 |
|
||||
| #33836 | " | 🔵 | Current Version State Before Patch Release | ~125 |
|
||||
| #5739 | 4:43 PM | 🔵 | Plugin Metadata Configuration | ~199 |
|
||||
|
||||
### Dec 31, 2025
|
||||
### Dec 8, 2025
|
||||
|
||||
**plugin.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34723 | 4:51 PM | ✅ | Version 8.5.2 Release Committed and Tagged | ~198 |
|
||||
| #34722 | " | ✅ | Synchronized version to 8.5.2 in plugin metadata | ~194 |
|
||||
| #34721 | " | ✅ | Version 8.5.2 Synchronized Across All Configuration Files | ~186 |
|
||||
| #34718 | 4:50 PM | 🔵 | Plugin Configuration Version Check | ~191 |
|
||||
| #34715 | " | 🔵 | Version 8.5.1 confirmed across all package configuration files | ~181 |
|
||||
|
||||
### Jan 1, 2026
|
||||
|
||||
**plugin.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #35680 | 11:43 PM | 🟣 | Automated version 8.5.3 release workflow completed | ~470 |
|
||||
| #35674 | 11:41 PM | 🔵 | Release staged files identified for version 8.5.3 | ~287 |
|
||||
| #35673 | " | ✅ | Staged version files for commit | ~155 |
|
||||
| #35672 | " | ✅ | Version bumped to 8.5.3 in plugin configuration | ~231 |
|
||||
| #35670 | 11:40 PM | 🔵 | Current plugin version identified as 8.5.2 | ~240 |
|
||||
|
||||
### Jan 2, 2026
|
||||
|
||||
**plugin.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #35944 | 2:57 PM | 🔵 | Current Project Version 8.5.4 Verified | ~200 |
|
||||
| #35926 | 2:53 PM | ✅ | Committed Version Bump to 8.5.4 | ~210 |
|
||||
| #35924 | " | ✅ | Verified Version Consistency Across All Files | ~186 |
|
||||
| #35923 | " | ✅ | Updated Plugin Configuration Version to 8.5.4 | ~167 |
|
||||
| #35920 | 2:52 PM | 🔵 | Plugin.json Version Configuration | ~200 |
|
||||
| #35917 | " | 🔵 | Current Version Identified as 8.5.3 | ~170 |
|
||||
| #35905 | 2:49 PM | 🔵 | Current Version is 8.5.3 Across All Package Files | ~193 |
|
||||
|
||||
### Jan 3, 2026
|
||||
|
||||
**plugin.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #36669 | 11:37 PM | ✅ | Merge conflicts resolved automatically - only 5 metadata files modified | ~345 |
|
||||
|
||||
### Jan 4, 2026
|
||||
|
||||
**plugin.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #36924 | 2:25 AM | ✅ | Merged fix/pr-538-followups branch into main with comprehensive updates | ~481 |
|
||||
| #36922 | " | ✅ | Version Bump Committed to Git | ~261 |
|
||||
| #36920 | " | 🔵 | Version 8.5.8 already set across all configuration files | ~203 |
|
||||
| #36917 | 2:24 AM | ✅ | Version bumped to 8.5.8 in plugin.json | ~204 |
|
||||
| #36912 | " | 🔵 | Current version identified as 8.5.7 across all package files | ~190 |
|
||||
| #36700 | 12:00 AM | ✅ | Committed Version 8.5.7 Across All Package Files | ~260 |
|
||||
| #36699 | " | ✅ | Version Bumped to 8.5.7 Across All Package Files | ~271 |
|
||||
| #36697 | " | ✅ | Version Bumped to 8.5.7 for Patch Release | ~243 |
|
||||
|
||||
### Jan 5, 2026
|
||||
|
||||
**plugin.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #38093 | 10:44 PM | ✅ | Committed version bump to 9.0.0 | ~217 |
|
||||
| #38091 | " | ✅ | Version bumped to 9.0.0 in plugin.json | ~187 |
|
||||
| #38087 | 10:43 PM | 🔵 | Current version identified as 8.5.10 across all version files | ~200 |
|
||||
| #37548 | 4:48 PM | ✅ | Issue #543 Analysis Report Created for Slash Command Availability | ~540 |
|
||||
| #37532 | 4:43 PM | 🔵 | Plugin Metadata Missing Slash Command Registration | ~372 |
|
||||
| #22284 | 9:41 PM | 🔵 | Claude Plugin Metadata Configuration | ~183 |
|
||||
</claude-mem-context>
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "9.0.1",
|
||||
"version": "9.0.4",
|
||||
"description": "Persistent memory system for Claude Code - seamlessly preserve context across sessions",
|
||||
"author": {
|
||||
"name": "Alex Newman"
|
||||
|
||||
+2
-81
@@ -3,88 +3,9 @@
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Dec 29, 2025
|
||||
### Jan 10, 2026
|
||||
|
||||
**package.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34351 | 11:13 PM | ✅ | Version 8.5.0 Release Committed to Git | ~402 |
|
||||
| #34350 | 11:12 PM | ✅ | Version 8.5.0 Build Completed Successfully | ~425 |
|
||||
| #34214 | 10:07 PM | 🔵 | Cursor Integration Feature Set Discovered via Memory Search | ~427 |
|
||||
| #34208 | 10:00 PM | ✅ | claude-mem v8.2.10 built and synced to marketplace | ~416 |
|
||||
|
||||
### Dec 30, 2025
|
||||
|
||||
**package.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34476 | 2:25 PM | ✅ | V2 Branch Builds Successfully Despite TypeScript Errors | ~316 |
|
||||
| #34451 | 2:20 PM | ✅ | Successful Build of Claude-Mem v8.5.1 Components | ~346 |
|
||||
|
||||
### Dec 31, 2025
|
||||
|
||||
**package.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34724 | 4:51 PM | ✅ | Built claude-mem v8.5.2 with issue 499 fix | ~287 |
|
||||
| #34710 | 4:48 PM | ✅ | Built and deployed claude-mem v8.5.1 to marketplace | ~371 |
|
||||
|
||||
### Jan 1, 2026
|
||||
|
||||
**package.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #35680 | 11:43 PM | 🟣 | Automated version 8.5.3 release workflow completed | ~470 |
|
||||
| #35613 | 10:57 PM | ✅ | Build System Compiled Updated Middleware | ~320 |
|
||||
| #35427 | 6:32 PM | ✅ | Claude-Mem Project Built Successfully | ~319 |
|
||||
| #35397 | 5:23 PM | ✅ | Build System Successfully Compiled All Components | ~282 |
|
||||
| #35343 | 3:00 PM | ✅ | Phase 1 Git Status Shows Modified Files | ~315 |
|
||||
|
||||
### Jan 2, 2026
|
||||
|
||||
**package.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #35982 | 5:09 PM | ✅ | Built and deployed claude-mem version 8.5.4 with LogsModal UI component | ~295 |
|
||||
| #35976 | 4:48 PM | ✅ | Claude-mem build and marketplace sync completed | ~335 |
|
||||
| #35925 | 2:53 PM | ✅ | Built Project for Version 8.5.4 Release | ~294 |
|
||||
| #35815 | 2:26 PM | ✅ | Claude-mem plugin built and deployed to marketplace | ~381 |
|
||||
|
||||
### Jan 3, 2026
|
||||
|
||||
**package.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #36687 | 11:53 PM | ✅ | MCP SDK and esbuild Dependencies Updated | ~332 |
|
||||
| #36669 | 11:37 PM | ✅ | Merge conflicts resolved automatically - only 5 metadata files modified | ~345 |
|
||||
|
||||
### Jan 4, 2026
|
||||
|
||||
**package.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #36926 | 2:26 AM | ✅ | Git rebase aborted and working directory restored | ~242 |
|
||||
| #36924 | 2:25 AM | ✅ | Merged fix/pr-538-followups branch into main with comprehensive updates | ~481 |
|
||||
| #36827 | 1:03 AM | ✅ | Branch diff shows 1,293 insertions and 98 deletions across 15 files | ~464 |
|
||||
| #36701 | 12:01 AM | ✅ | Built Version 8.5.7 Plugin Artifacts | ~406 |
|
||||
|
||||
### Jan 5, 2026
|
||||
|
||||
**package.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #38092 | 10:44 PM | ✅ | Built version 9.0.0 with complete plugin bundle | ~329 |
|
||||
|
||||
**.mcp.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #37545 | 4:47 PM | ✅ | Issue #544 Analysis Report Created for mem-search Skill Messaging Problem | ~480 |
|
||||
|
||||
### Jan 7, 2026
|
||||
|
||||
**package.json**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #38186 | 7:33 PM | 🟣 | Claude-mem plugin v9.0.0 built and deployed to marketplace | ~327 |
|
||||
| #38120 | 5:46 PM | 🔴 | Rebuilt all plugin hooks and worker service | ~278 |
|
||||
| #39048 | 3:44 PM | 🔵 | Plugin directory contains commands folder | ~276 |
|
||||
</claude-mem-context>
|
||||
@@ -0,0 +1,18 @@
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Oct 25, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #2437 | 4:32 PM | 🟣 | Slash Command Files Created for Quick Settings Toggling | ~478 |
|
||||
|
||||
### Jan 10, 2026
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #39052 | 3:44 PM | 🟣 | Commands added to plugin distribution | ~268 |
|
||||
| #39050 | " | 🔵 | Plugin commands directory is empty | ~255 |
|
||||
</claude-mem-context>
|
||||
@@ -28,4 +28,10 @@
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #32309 | 3:09 PM | 🔵 | Claude-mem hooks system configuration structure | ~435 |
|
||||
|
||||
### Jan 9, 2026
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #38802 | 5:11 PM | 🔵 | Claude-Mem Hook Configuration Architecture | ~450 |
|
||||
</claude-mem-context>
|
||||
@@ -0,0 +1,92 @@
|
||||
# Bugfix Sprint: 2026-01-10
|
||||
|
||||
## Critical Priority (Blocks Users)
|
||||
|
||||
### #646 - Plugin bricks Claude Code - stdin fstat EINVAL crash
|
||||
- **Impact**: Plugin completely bricks Claude Code on Linux. Users cannot recover without manual config editing.
|
||||
- **Root Cause**: Bun's stdin handling causes fstat EINVAL when reading piped input
|
||||
- **Related Discussion**: Check if this is a Bun upstream issue
|
||||
- [ ] Investigate stdin handling in hook scripts
|
||||
- [ ] Test on Linux with stdin piping
|
||||
- [ ] Implement fix
|
||||
|
||||
### #623 - Crash-recovery loop when memory_session_id not captured
|
||||
- **Impact**: Infinite loop consuming API tokens, growing queue unbounded
|
||||
- **Root Cause**: memory_session_id not captured, causes repeated crash-recovery
|
||||
- [ ] Add null/undefined check for memory_session_id
|
||||
- [ ] Add circuit breaker for crash-recovery attempts
|
||||
|
||||
## High Priority
|
||||
|
||||
### #638 - Worker startup missing JSON output causes hooks to appear stuck
|
||||
- **Impact**: UI appears stuck during worker startup
|
||||
- [ ] Ensure worker startup emits proper JSON status
|
||||
- [ ] Add progress feedback to hook output
|
||||
|
||||
### #641/#609 - CLAUDE.md files in subdirectories
|
||||
- **Impact**: CLAUDE.md files scattered throughout project directories
|
||||
- **Note**: This is a documented feature request that was never implemented (setting exists but doesn't work)
|
||||
- [ ] Implement the `disableSubdirectoryCLAUDEmd` setting properly
|
||||
- [ ] Or change default behavior to not create subdirectory files
|
||||
|
||||
### #635 - JSON parsing error prevents folder context generation
|
||||
- **Impact**: Folder context not generating, breaks context injection
|
||||
- **Root Cause**: String spread instead of array spread
|
||||
- [ ] Fix the JSON parsing logic
|
||||
- [ ] Add proper error handling
|
||||
|
||||
## Medium Priority
|
||||
|
||||
### #582 - Tilde paths create literal ~ directories
|
||||
- **Impact**: Directories named "~" created instead of expanding to home
|
||||
- [ ] Use path expansion for tilde in all path operations
|
||||
- [ ] Audit all path handling code
|
||||
|
||||
### #642/#643 - ChromaDB search fails due to initialization timing
|
||||
- **Impact**: Search fails with JSON parse error
|
||||
- **Note**: #643 is a duplicate of #642
|
||||
- [ ] Fix initialization timing issue
|
||||
- [ ] Add proper async/await handling
|
||||
|
||||
### #626 - HealthMonitor hardcodes ~/.claude path
|
||||
- **Impact**: Fails for users with custom config directories
|
||||
- [ ] Use configurable path instead of hardcoded ~/.claude
|
||||
- [ ] Respect CLAUDE_CONFIG_DIR or similar env var
|
||||
|
||||
### #598 - Too many messages pollute conversation history
|
||||
- **Impact**: Hook messages clutter conversation
|
||||
- [ ] Reduce verbosity of hook messages
|
||||
- [ ] Make message frequency configurable
|
||||
|
||||
## Low Priority (Code Quality)
|
||||
|
||||
### #648 - Empty catch blocks swallow JSON parse errors
|
||||
- [ ] Add proper error logging to catch blocks in SessionSearch.ts
|
||||
|
||||
### #649 - Inconsistent logging in CursorHooksInstaller
|
||||
- [ ] Replace console.log with structured logger
|
||||
- **Note**: 177 console.log calls across 20 files identified
|
||||
|
||||
## Won't Fix / Not a Bug
|
||||
|
||||
### #632 - Feature request for disabling CLAUDE.md in subdirectories
|
||||
- **Note**: Covered by #641 implementation
|
||||
|
||||
### #633 - Help request about Cursor integration
|
||||
- **Note**: Documentation/support issue, not a bug
|
||||
|
||||
### #640 - Arabic README translation
|
||||
- **Note**: Documentation PR, not a bugfix
|
||||
|
||||
### #624 - Beta testing strategy proposal
|
||||
- **Note**: Enhancement proposal, not a bugfix
|
||||
|
||||
## Already Fixed in v9.0.2
|
||||
|
||||
- Windows Terminal tab accumulation (#625/#628)
|
||||
- Windows 11 compatibility - WMIC to PowerShell migration
|
||||
- Claude Code 2.1.1 compatibility + path validation (#614)
|
||||
|
||||
---
|
||||
|
||||
**Recommended Approach**: Fix #646 first (critical blocker), then #623 (crash loop), then work through high priority issues in order of impact.
|
||||
@@ -17,7 +17,12 @@
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/context-hook.js\"",
|
||||
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code context",
|
||||
"timeout": 60
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code user-message",
|
||||
"timeout": 60
|
||||
}
|
||||
]
|
||||
@@ -33,7 +38,7 @@
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/new-hook.js\"",
|
||||
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code session-init",
|
||||
"timeout": 60
|
||||
}
|
||||
]
|
||||
@@ -50,7 +55,7 @@
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/save-hook.js\"",
|
||||
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code observation",
|
||||
"timeout": 120
|
||||
}
|
||||
]
|
||||
@@ -66,7 +71,7 @@
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/summary-hook.js\"",
|
||||
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code summarize",
|
||||
"timeout": 120
|
||||
}
|
||||
]
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claude-mem-plugin",
|
||||
"version": "9.0.0",
|
||||
"version": "9.0.3",
|
||||
"private": true,
|
||||
"description": "Runtime dependencies for claude-mem bundled hooks",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/usr/bin/env bun
|
||||
import{stdin as A}from"process";import C from"path";import{homedir as z}from"os";import{readFileSync as q}from"fs";import{appendFileSync as j,existsSync as h,mkdirSync as H,readFileSync as G}from"fs";import{join as T}from"path";import{homedir as K}from"os";var S=(i=>(i[i.DEBUG=0]="DEBUG",i[i.INFO=1]="INFO",i[i.WARN=2]="WARN",i[i.ERROR=3]="ERROR",i[i.SILENT=4]="SILENT",i))(S||{}),U=T(K(),".claude-mem"),M=class{level=null;useColor;logFilePath=null;logFileInitialized=!1;constructor(){this.useColor=process.stdout.isTTY??!1}ensureLogFileInitialized(){if(!this.logFileInitialized){this.logFileInitialized=!0;try{let t=T(U,"logs");h(t)||H(t,{recursive:!0});let r=new Date().toISOString().split("T")[0];this.logFilePath=T(t,`claude-mem-${r}.log`)}catch(t){console.error("[LOGGER] Failed to initialize log file:",t),this.logFilePath=null}}}getLevel(){if(this.level===null)try{let t=T(U,"settings.json");if(h(t)){let r=G(t,"utf-8"),n=(JSON.parse(r).CLAUDE_MEM_LOG_LEVEL||"INFO").toUpperCase();this.level=S[n]??1}else this.level=1}catch{this.level=1}return this.level}correlationId(t,r){return`obs-${t}-${r}`}sessionId(t){return`session-${t}`}formatData(t){if(t==null)return"";if(typeof t=="string")return t;if(typeof t=="number"||typeof t=="boolean")return t.toString();if(typeof t=="object"){if(t instanceof Error)return this.getLevel()===0?`${t.message}
|
||||
${t.stack}`:t.message;if(Array.isArray(t))return`[${t.length} items]`;let r=Object.keys(t);return r.length===0?"{}":r.length<=3?JSON.stringify(t):`{${r.length} keys: ${r.slice(0,3).join(", ")}...}`}return String(t)}formatTool(t,r){if(!r)return t;let e=r;if(typeof r=="string")try{e=JSON.parse(r)}catch{e=r}if(t==="Bash"&&e.command)return`${t}(${e.command})`;if(e.file_path)return`${t}(${e.file_path})`;if(e.notebook_path)return`${t}(${e.notebook_path})`;if(t==="Glob"&&e.pattern)return`${t}(${e.pattern})`;if(t==="Grep"&&e.pattern)return`${t}(${e.pattern})`;if(e.url)return`${t}(${e.url})`;if(e.query)return`${t}(${e.query})`;if(t==="Task"){if(e.subagent_type)return`${t}(${e.subagent_type})`;if(e.description)return`${t}(${e.description})`}return t==="Skill"&&e.skill?`${t}(${e.skill})`:t==="LSP"&&e.operation?`${t}(${e.operation})`:t}formatTimestamp(t){let r=t.getFullYear(),e=String(t.getMonth()+1).padStart(2,"0"),n=String(t.getDate()).padStart(2,"0"),i=String(t.getHours()).padStart(2,"0"),s=String(t.getMinutes()).padStart(2,"0"),E=String(t.getSeconds()).padStart(2,"0"),c=String(t.getMilliseconds()).padStart(3,"0");return`${r}-${e}-${n} ${i}:${s}:${E}.${c}`}log(t,r,e,n,i){if(t<this.getLevel())return;this.ensureLogFileInitialized();let s=this.formatTimestamp(new Date),E=S[t].padEnd(5),c=r.padEnd(6),a="";n?.correlationId?a=`[${n.correlationId}] `:n?.sessionId&&(a=`[session-${n.sessionId}] `);let _="";i!=null&&(i instanceof Error?_=this.getLevel()===0?`
|
||||
${i.message}
|
||||
${i.stack}`:` ${i.message}`:this.getLevel()===0&&typeof i=="object"?_=`
|
||||
`+JSON.stringify(i,null,2):_=" "+this.formatData(i));let p="";if(n){let{sessionId:d,memorySessionId:at,correlationId:lt,...R}=n;Object.keys(R).length>0&&(p=` {${Object.entries(R).map(([x,b])=>`${x}=${b}`).join(", ")}}`)}let D=`[${s}] [${E}] [${c}] ${a}${e}${p}${_}`;if(this.logFilePath)try{j(this.logFilePath,D+`
|
||||
`,"utf8")}catch(d){process.stderr.write(`[LOGGER] Failed to write to log file: ${d}
|
||||
`)}else process.stderr.write(D+`
|
||||
`)}debug(t,r,e,n){this.log(0,t,r,e,n)}info(t,r,e,n){this.log(1,t,r,e,n)}warn(t,r,e,n){this.log(2,t,r,e,n)}error(t,r,e,n){this.log(3,t,r,e,n)}dataIn(t,r,e,n){this.info(t,`\u2192 ${r}`,e,n)}dataOut(t,r,e,n){this.info(t,`\u2190 ${r}`,e,n)}success(t,r,e,n){this.info(t,`\u2713 ${r}`,e,n)}failure(t,r,e,n){this.error(t,`\u2717 ${r}`,e,n)}timing(t,r,e,n){this.info(t,`\u23F1 ${r}`,n,{duration:`${e}ms`})}happyPathError(t,r,e,n,i=""){let a=((new Error().stack||"").split(`
|
||||
`)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),_=a?`${a[1].split("/").pop()}:${a[2]}`:"unknown",p={...e,location:_};return this.warn(t,`[HAPPY-PATH] ${r}`,p,n),i}},l=new M;var m={DEFAULT:3e5,HEALTH_CHECK:3e4,WORKER_STARTUP_WAIT:1e3,WORKER_STARTUP_RETRIES:300,PRE_RESTART_SETTLE_DELAY:2e3,WINDOWS_MULTIPLIER:1.5};function N(o){return process.platform==="win32"?Math.round(o*m.WINDOWS_MULTIPLIER):o}import{readFileSync as X,writeFileSync as P,existsSync as y,mkdirSync as V}from"fs";import{join as B,dirname as Y}from"path";import{homedir as J}from"os";var I="bugfix,feature,refactor,discovery,decision,change",k="how-it-works,why-it-exists,what-changed,problem-solution,gotcha,pattern,trade-off";var g=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-sonnet-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_WORKER_HOST:"127.0.0.1",CLAUDE_MEM_SKIP_TOOLS:"ListMcpResourcesTool,SlashCommand,Skill,TodoWrite,AskUserQuestion",CLAUDE_MEM_PROVIDER:"claude",CLAUDE_MEM_GEMINI_API_KEY:"",CLAUDE_MEM_GEMINI_MODEL:"gemini-2.5-flash-lite",CLAUDE_MEM_GEMINI_RATE_LIMITING_ENABLED:"true",CLAUDE_MEM_OPENROUTER_API_KEY:"",CLAUDE_MEM_OPENROUTER_MODEL:"xiaomi/mimo-v2-flash:free",CLAUDE_MEM_OPENROUTER_SITE_URL:"",CLAUDE_MEM_OPENROUTER_APP_NAME:"claude-mem",CLAUDE_MEM_OPENROUTER_MAX_CONTEXT_MESSAGES:"20",CLAUDE_MEM_OPENROUTER_MAX_TOKENS:"100000",CLAUDE_MEM_DATA_DIR:B(J(),".claude-mem"),CLAUDE_MEM_LOG_LEVEL:"INFO",CLAUDE_MEM_PYTHON_VERSION:"3.13",CLAUDE_CODE_PATH:"",CLAUDE_MEM_MODE:"code",CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT:"true",CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES:I,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:k,CLAUDE_MEM_CONTEXT_FULL_COUNT:"5",CLAUDE_MEM_CONTEXT_FULL_FIELD:"narrative",CLAUDE_MEM_CONTEXT_SESSION_COUNT:"10",CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY:"true",CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE:"false"};static getAllDefaults(){return{...this.DEFAULTS}}static get(t){return this.DEFAULTS[t]}static getInt(t){let r=this.get(t);return parseInt(r,10)}static getBool(t){return this.get(t)==="true"}static loadFromFile(t){try{if(!y(t)){let s=this.getAllDefaults();try{let E=Y(t);y(E)||V(E,{recursive:!0}),P(t,JSON.stringify(s,null,2),"utf-8"),console.log("[SETTINGS] Created settings file with defaults:",t)}catch(E){console.warn("[SETTINGS] Failed to create settings file, using in-memory defaults:",t,E)}return s}let r=X(t,"utf-8"),e=JSON.parse(r),n=e;if(e.env&&typeof e.env=="object"){n=e.env;try{P(t,JSON.stringify(n,null,2),"utf-8"),console.log("[SETTINGS] Migrated settings file from nested to flat schema:",t)}catch(s){console.warn("[SETTINGS] Failed to auto-migrate settings file:",t,s)}}let i={...this.DEFAULTS};for(let s of Object.keys(this.DEFAULTS))n[s]!==void 0&&(i[s]=n[s]);return i}catch(r){return console.warn("[SETTINGS] Failed to load settings, using defaults:",t,r),this.getAllDefaults()}}};function $(o={}){let{port:t,includeSkillFallback:r=!1,customPrefix:e,actualError:n}=o,i=e||"Worker service connection failed.",s=t?` (port ${t})`:"",E=`${i}${s}
|
||||
|
||||
`;return E+=`To restart the worker:
|
||||
`,E+=`1. Exit Claude Code completely
|
||||
`,E+=`2. Run: npm run worker:restart
|
||||
`,E+="3. Restart Claude Code",r&&(E+=`
|
||||
|
||||
If that doesn't work, try: /troubleshoot`),n&&(E=`Worker Error: ${n}
|
||||
|
||||
${E}`),E}var Q=C.join(z(),".claude","plugins","marketplaces","thedotmack"),It=N(m.HEALTH_CHECK),O=null;function u(){if(O!==null)return O;let o=C.join(g.get("CLAUDE_MEM_DATA_DIR"),"settings.json"),t=g.loadFromFile(o);return O=parseInt(t.CLAUDE_MEM_WORKER_PORT,10),O}async function Z(){let o=u();return(await fetch(`http://127.0.0.1:${o}/api/readiness`)).ok}function tt(){let o=C.join(Q,"package.json");return JSON.parse(q(o,"utf-8")).version}async function et(){let o=u(),t=await fetch(`http://127.0.0.1:${o}/api/version`);if(!t.ok)throw new Error(`Failed to get worker version: ${t.status}`);return(await t.json()).version}async function rt(){let o=tt(),t=await et();o!==t&&l.debug("SYSTEM","Version check",{pluginVersion:o,workerVersion:t,note:"Mismatch will be auto-restarted by worker-service start command"})}async function v(){for(let r=0;r<75;r++){try{if(await Z()){await rt();return}}catch(e){l.debug("SYSTEM","Worker health check failed, will retry",{attempt:r+1,maxRetries:75,error:e instanceof Error?e.message:String(e)})}await new Promise(e=>setTimeout(e,200))}throw new Error($({port:u(),customPrefix:"Worker did not become ready within 15 seconds."}))}import it from"path";import{statSync as nt,readFileSync as ot}from"fs";import L from"path";var f={isWorktree:!1,worktreeName:null,parentRepoPath:null,parentProjectName:null};function W(o){let t=L.join(o,".git"),r;try{r=nt(t)}catch{return f}if(!r.isFile())return f;let e;try{e=ot(t,"utf-8").trim()}catch{return f}let n=e.match(/^gitdir:\s*(.+)$/);if(!n)return f;let s=n[1].match(/^(.+)[/\\]\.git[/\\]worktrees[/\\]([^/\\]+)$/);if(!s)return f;let E=s[1],c=L.basename(o),a=L.basename(E);return{isWorktree:!0,worktreeName:c,parentRepoPath:E,parentProjectName:a}}function st(o){if(!o||o.trim()==="")return l.warn("PROJECT_NAME","Empty cwd provided, using fallback",{cwd:o}),"unknown-project";let t=it.basename(o);if(t===""){if(process.platform==="win32"){let e=o.match(/^([A-Z]):\\/i);if(e){let i=`drive-${e[1].toUpperCase()}`;return l.info("PROJECT_NAME","Drive root detected",{cwd:o,projectName:i}),i}}return l.warn("PROJECT_NAME","Root directory detected, using fallback",{cwd:o}),"unknown-project"}return t}function F(o){let t=st(o);if(!o)return{primary:t,parent:null,isWorktree:!1,allProjects:[t]};let r=W(o);return r.isWorktree&&r.parentProjectName?{primary:t,parent:r.parentProjectName,isWorktree:!0,allProjects:[r.parentProjectName,t]}:{primary:t,parent:null,isWorktree:!1,allProjects:[t]}}async function w(o){await v();let t=o?.cwd??process.cwd(),r=F(t),e=u(),n=r.allProjects.join(","),i=`http://127.0.0.1:${e}/api/context/inject?projects=${encodeURIComponent(n)}`,s=await fetch(i);if(!s.ok)throw new Error(`Context generation failed: ${s.status}`);return(await s.text()).trim()}var Et=process.argv.includes("--colors");if(A.isTTY||Et)w(void 0).then(o=>{console.log(o),process.exit(0)});else{let o="";A.on("data",t=>o+=t),A.on("end",async()=>{let t;try{t=o.trim()?JSON.parse(o):void 0}catch(e){throw new Error(`Failed to parse hook input: ${e instanceof Error?e.message:String(e)}`)}let r=await w(t);console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:r}})),process.exit(0)})}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,19 +0,0 @@
|
||||
#!/usr/bin/env bun
|
||||
import{stdin as b}from"process";var T=JSON.stringify({continue:!0,suppressOutput:!0});import L from"path";import{homedir as Y}from"os";import{readFileSync as J}from"fs";import{appendFileSync as F,existsSync as R,mkdirSync as H,readFileSync as x}from"fs";import{join as f}from"path";import{homedir as j}from"os";var S=(i=>(i[i.DEBUG=0]="DEBUG",i[i.INFO=1]="INFO",i[i.WARN=2]="WARN",i[i.ERROR=3]="ERROR",i[i.SILENT=4]="SILENT",i))(S||{}),h=f(j(),".claude-mem"),m=class{level=null;useColor;logFilePath=null;logFileInitialized=!1;constructor(){this.useColor=process.stdout.isTTY??!1}ensureLogFileInitialized(){if(!this.logFileInitialized){this.logFileInitialized=!0;try{let t=f(h,"logs");R(t)||H(t,{recursive:!0});let r=new Date().toISOString().split("T")[0];this.logFilePath=f(t,`claude-mem-${r}.log`)}catch(t){console.error("[LOGGER] Failed to initialize log file:",t),this.logFilePath=null}}}getLevel(){if(this.level===null)try{let t=f(h,"settings.json");if(R(t)){let r=x(t,"utf-8"),n=(JSON.parse(r).CLAUDE_MEM_LOG_LEVEL||"INFO").toUpperCase();this.level=S[n]??1}else this.level=1}catch{this.level=1}return this.level}correlationId(t,r){return`obs-${t}-${r}`}sessionId(t){return`session-${t}`}formatData(t){if(t==null)return"";if(typeof t=="string")return t;if(typeof t=="number"||typeof t=="boolean")return t.toString();if(typeof t=="object"){if(t instanceof Error)return this.getLevel()===0?`${t.message}
|
||||
${t.stack}`:t.message;if(Array.isArray(t))return`[${t.length} items]`;let r=Object.keys(t);return r.length===0?"{}":r.length<=3?JSON.stringify(t):`{${r.length} keys: ${r.slice(0,3).join(", ")}...}`}return String(t)}formatTool(t,r){if(!r)return t;let e=r;if(typeof r=="string")try{e=JSON.parse(r)}catch{e=r}if(t==="Bash"&&e.command)return`${t}(${e.command})`;if(e.file_path)return`${t}(${e.file_path})`;if(e.notebook_path)return`${t}(${e.notebook_path})`;if(t==="Glob"&&e.pattern)return`${t}(${e.pattern})`;if(t==="Grep"&&e.pattern)return`${t}(${e.pattern})`;if(e.url)return`${t}(${e.url})`;if(e.query)return`${t}(${e.query})`;if(t==="Task"){if(e.subagent_type)return`${t}(${e.subagent_type})`;if(e.description)return`${t}(${e.description})`}return t==="Skill"&&e.skill?`${t}(${e.skill})`:t==="LSP"&&e.operation?`${t}(${e.operation})`:t}formatTimestamp(t){let r=t.getFullYear(),e=String(t.getMonth()+1).padStart(2,"0"),n=String(t.getDate()).padStart(2,"0"),i=String(t.getHours()).padStart(2,"0"),E=String(t.getMinutes()).padStart(2,"0"),s=String(t.getSeconds()).padStart(2,"0"),c=String(t.getMilliseconds()).padStart(3,"0");return`${r}-${e}-${n} ${i}:${E}:${s}.${c}`}log(t,r,e,n,i){if(t<this.getLevel())return;this.ensureLogFileInitialized();let E=this.formatTimestamp(new Date),s=S[t].padEnd(5),c=r.padEnd(6),l="";n?.correlationId?l=`[${n.correlationId}] `:n?.sessionId&&(l=`[session-${n.sessionId}] `);let _="";i!=null&&(i instanceof Error?_=this.getLevel()===0?`
|
||||
${i.message}
|
||||
${i.stack}`:` ${i.message}`:this.getLevel()===0&&typeof i=="object"?_=`
|
||||
`+JSON.stringify(i,null,2):_=" "+this.formatData(i));let u="";if(n){let{sessionId:d,memorySessionId:nt,correlationId:ot,...D}=n;Object.keys(D).length>0&&(u=` {${Object.entries(D).map(([w,W])=>`${w}=${W}`).join(", ")}}`)}let A=`[${E}] [${s}] [${c}] ${l}${e}${u}${_}`;if(this.logFilePath)try{F(this.logFilePath,A+`
|
||||
`,"utf8")}catch(d){process.stderr.write(`[LOGGER] Failed to write to log file: ${d}
|
||||
`)}else process.stderr.write(A+`
|
||||
`)}debug(t,r,e,n){this.log(0,t,r,e,n)}info(t,r,e,n){this.log(1,t,r,e,n)}warn(t,r,e,n){this.log(2,t,r,e,n)}error(t,r,e,n){this.log(3,t,r,e,n)}dataIn(t,r,e,n){this.info(t,`\u2192 ${r}`,e,n)}dataOut(t,r,e,n){this.info(t,`\u2190 ${r}`,e,n)}success(t,r,e,n){this.info(t,`\u2713 ${r}`,e,n)}failure(t,r,e,n){this.error(t,`\u2717 ${r}`,e,n)}timing(t,r,e,n){this.info(t,`\u23F1 ${r}`,n,{duration:`${e}ms`})}happyPathError(t,r,e,n,i=""){let l=((new Error().stack||"").split(`
|
||||
`)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),_=l?`${l[1].split("/").pop()}:${l[2]}`:"unknown",u={...e,location:_};return this.warn(t,`[HAPPY-PATH] ${r}`,u,n),i}},a=new m;var M={DEFAULT:3e5,HEALTH_CHECK:3e4,WORKER_STARTUP_WAIT:1e3,WORKER_STARTUP_RETRIES:300,PRE_RESTART_SETTLE_DELAY:2e3,WINDOWS_MULTIPLIER:1.5};function N(o){return process.platform==="win32"?Math.round(o*M.WINDOWS_MULTIPLIER):o}import{readFileSync as K,writeFileSync as k,existsSync as P,mkdirSync as G}from"fs";import{join as X,dirname as V}from"path";import{homedir as B}from"os";var I="bugfix,feature,refactor,discovery,decision,change",U="how-it-works,why-it-exists,what-changed,problem-solution,gotcha,pattern,trade-off";var g=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-sonnet-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_WORKER_HOST:"127.0.0.1",CLAUDE_MEM_SKIP_TOOLS:"ListMcpResourcesTool,SlashCommand,Skill,TodoWrite,AskUserQuestion",CLAUDE_MEM_PROVIDER:"claude",CLAUDE_MEM_GEMINI_API_KEY:"",CLAUDE_MEM_GEMINI_MODEL:"gemini-2.5-flash-lite",CLAUDE_MEM_GEMINI_RATE_LIMITING_ENABLED:"true",CLAUDE_MEM_OPENROUTER_API_KEY:"",CLAUDE_MEM_OPENROUTER_MODEL:"xiaomi/mimo-v2-flash:free",CLAUDE_MEM_OPENROUTER_SITE_URL:"",CLAUDE_MEM_OPENROUTER_APP_NAME:"claude-mem",CLAUDE_MEM_OPENROUTER_MAX_CONTEXT_MESSAGES:"20",CLAUDE_MEM_OPENROUTER_MAX_TOKENS:"100000",CLAUDE_MEM_DATA_DIR:X(B(),".claude-mem"),CLAUDE_MEM_LOG_LEVEL:"INFO",CLAUDE_MEM_PYTHON_VERSION:"3.13",CLAUDE_CODE_PATH:"",CLAUDE_MEM_MODE:"code",CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT:"true",CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES:I,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:U,CLAUDE_MEM_CONTEXT_FULL_COUNT:"5",CLAUDE_MEM_CONTEXT_FULL_FIELD:"narrative",CLAUDE_MEM_CONTEXT_SESSION_COUNT:"10",CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY:"true",CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE:"false"};static getAllDefaults(){return{...this.DEFAULTS}}static get(t){return this.DEFAULTS[t]}static getInt(t){let r=this.get(t);return parseInt(r,10)}static getBool(t){return this.get(t)==="true"}static loadFromFile(t){try{if(!P(t)){let E=this.getAllDefaults();try{let s=V(t);P(s)||G(s,{recursive:!0}),k(t,JSON.stringify(E,null,2),"utf-8"),console.log("[SETTINGS] Created settings file with defaults:",t)}catch(s){console.warn("[SETTINGS] Failed to create settings file, using in-memory defaults:",t,s)}return E}let r=K(t,"utf-8"),e=JSON.parse(r),n=e;if(e.env&&typeof e.env=="object"){n=e.env;try{k(t,JSON.stringify(n,null,2),"utf-8"),console.log("[SETTINGS] Migrated settings file from nested to flat schema:",t)}catch(E){console.warn("[SETTINGS] Failed to auto-migrate settings file:",t,E)}}let i={...this.DEFAULTS};for(let E of Object.keys(this.DEFAULTS))n[E]!==void 0&&(i[E]=n[E]);return i}catch(r){return console.warn("[SETTINGS] Failed to load settings, using defaults:",t,r),this.getAllDefaults()}}};function y(o={}){let{port:t,includeSkillFallback:r=!1,customPrefix:e,actualError:n}=o,i=e||"Worker service connection failed.",E=t?` (port ${t})`:"",s=`${i}${E}
|
||||
|
||||
`;return s+=`To restart the worker:
|
||||
`,s+=`1. Exit Claude Code completely
|
||||
`,s+=`2. Run: npm run worker:restart
|
||||
`,s+="3. Restart Claude Code",r&&(s+=`
|
||||
|
||||
If that doesn't work, try: /troubleshoot`),n&&(s=`Worker Error: ${n}
|
||||
|
||||
${s}`),s}var z=L.join(Y(),".claude","plugins","marketplaces","thedotmack"),Rt=N(M.HEALTH_CHECK),O=null;function p(){if(O!==null)return O;let o=L.join(g.get("CLAUDE_MEM_DATA_DIR"),"settings.json"),t=g.loadFromFile(o);return O=parseInt(t.CLAUDE_MEM_WORKER_PORT,10),O}async function q(){let o=p();return(await fetch(`http://127.0.0.1:${o}/api/readiness`)).ok}function Q(){let o=L.join(z,"package.json");return JSON.parse(J(o,"utf-8")).version}async function Z(){let o=p(),t=await fetch(`http://127.0.0.1:${o}/api/version`);if(!t.ok)throw new Error(`Failed to get worker version: ${t.status}`);return(await t.json()).version}async function tt(){let o=Q(),t=await Z();o!==t&&a.debug("SYSTEM","Version check",{pluginVersion:o,workerVersion:t,note:"Mismatch will be auto-restarted by worker-service start command"})}async function $(){for(let r=0;r<75;r++){try{if(await q()){await tt();return}}catch(e){a.debug("SYSTEM","Worker health check failed, will retry",{attempt:r+1,maxRetries:75,error:e instanceof Error?e.message:String(e)})}await new Promise(e=>setTimeout(e,200))}throw new Error(y({port:p(),customPrefix:"Worker did not become ready within 15 seconds."}))}import et from"path";function v(o){if(!o||o.trim()==="")return a.warn("PROJECT_NAME","Empty cwd provided, using fallback",{cwd:o}),"unknown-project";let t=et.basename(o);if(t===""){if(process.platform==="win32"){let e=o.match(/^([A-Z]):\\/i);if(e){let i=`drive-${e[1].toUpperCase()}`;return a.info("PROJECT_NAME","Drive root detected",{cwd:o,projectName:i}),i}}return a.warn("PROJECT_NAME","Root directory detected, using fallback",{cwd:o}),"unknown-project"}return t}async function rt(o){if(await $(),!o)throw new Error("newHook requires input");let{session_id:t,cwd:r,prompt:e}=o,n=v(r),i=p();a.debug("HOOK","new-hook: Calling /api/sessions/init",{contentSessionId:t,project:n});let E=await fetch(`http://127.0.0.1:${i}/api/sessions/init`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({contentSessionId:t,project:n,prompt:e})});if(!E.ok)throw new Error(`Session initialization failed: ${E.status}`);let s=await E.json(),c=s.sessionDbId,l=s.promptNumber;if(a.debug("HOOK","new-hook: Received from /api/sessions/init",{sessionDbId:c,promptNumber:l,skipped:s.skipped}),a.debug("HOOK",`[ALIGNMENT] Hook Entry | contentSessionId=${t} | prompt#=${l} | sessionDbId=${c}`),s.skipped&&s.reason==="private"){a.info("HOOK",`INIT_COMPLETE | sessionDbId=${c} | promptNumber=${l} | skipped=true | reason=private`,{sessionId:c}),console.log(T);return}let _=e.startsWith("/")?e.substring(1):e;a.debug("HOOK","new-hook: Calling /sessions/{sessionDbId}/init",{sessionDbId:c,promptNumber:l});let u=await fetch(`http://127.0.0.1:${i}/sessions/${c}/init`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({userPrompt:_,promptNumber:l})});if(!u.ok)throw new Error(`SDK agent start failed: ${u.status}`);a.info("HOOK",`INIT_COMPLETE | sessionDbId=${c} | promptNumber=${l} | project=${n}`,{sessionId:c}),console.log(T)}var C="";b.on("data",o=>C+=o);b.on("end",async()=>{try{let o;try{o=C?JSON.parse(C):void 0}catch(t){throw new Error(`Failed to parse hook input: ${t instanceof Error?t.message:String(t)}`)}await rt(o)}catch(o){a.error("HOOK","new-hook failed",{},o)}finally{process.exit(0)}});
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/usr/bin/env bun
|
||||
import{stdin as v}from"process";var d=JSON.stringify({continue:!0,suppressOutput:!0});import{appendFileSync as H,existsSync as U,mkdirSync as W,readFileSync as b}from"fs";import{join as T}from"path";import{homedir as x}from"os";var f=(o=>(o[o.DEBUG=0]="DEBUG",o[o.INFO=1]="INFO",o[o.WARN=2]="WARN",o[o.ERROR=3]="ERROR",o[o.SILENT=4]="SILENT",o))(f||{}),R=T(x(),".claude-mem"),M=class{level=null;useColor;logFilePath=null;logFileInitialized=!1;constructor(){this.useColor=process.stdout.isTTY??!1}ensureLogFileInitialized(){if(!this.logFileInitialized){this.logFileInitialized=!0;try{let t=T(R,"logs");U(t)||W(t,{recursive:!0});let r=new Date().toISOString().split("T")[0];this.logFilePath=T(t,`claude-mem-${r}.log`)}catch(t){console.error("[LOGGER] Failed to initialize log file:",t),this.logFilePath=null}}}getLevel(){if(this.level===null)try{let t=T(R,"settings.json");if(U(t)){let r=b(t,"utf-8"),n=(JSON.parse(r).CLAUDE_MEM_LOG_LEVEL||"INFO").toUpperCase();this.level=f[n]??1}else this.level=1}catch{this.level=1}return this.level}correlationId(t,r){return`obs-${t}-${r}`}sessionId(t){return`session-${t}`}formatData(t){if(t==null)return"";if(typeof t=="string")return t;if(typeof t=="number"||typeof t=="boolean")return t.toString();if(typeof t=="object"){if(t instanceof Error)return this.getLevel()===0?`${t.message}
|
||||
${t.stack}`:t.message;if(Array.isArray(t))return`[${t.length} items]`;let r=Object.keys(t);return r.length===0?"{}":r.length<=3?JSON.stringify(t):`{${r.length} keys: ${r.slice(0,3).join(", ")}...}`}return String(t)}formatTool(t,r){if(!r)return t;let e=r;if(typeof r=="string")try{e=JSON.parse(r)}catch{e=r}if(t==="Bash"&&e.command)return`${t}(${e.command})`;if(e.file_path)return`${t}(${e.file_path})`;if(e.notebook_path)return`${t}(${e.notebook_path})`;if(t==="Glob"&&e.pattern)return`${t}(${e.pattern})`;if(t==="Grep"&&e.pattern)return`${t}(${e.pattern})`;if(e.url)return`${t}(${e.url})`;if(e.query)return`${t}(${e.query})`;if(t==="Task"){if(e.subagent_type)return`${t}(${e.subagent_type})`;if(e.description)return`${t}(${e.description})`}return t==="Skill"&&e.skill?`${t}(${e.skill})`:t==="LSP"&&e.operation?`${t}(${e.operation})`:t}formatTimestamp(t){let r=t.getFullYear(),e=String(t.getMonth()+1).padStart(2,"0"),n=String(t.getDate()).padStart(2,"0"),o=String(t.getHours()).padStart(2,"0"),E=String(t.getMinutes()).padStart(2,"0"),i=String(t.getSeconds()).padStart(2,"0"),a=String(t.getMilliseconds()).padStart(3,"0");return`${r}-${e}-${n} ${o}:${E}:${i}.${a}`}log(t,r,e,n,o){if(t<this.getLevel())return;this.ensureLogFileInitialized();let E=this.formatTimestamp(new Date),i=f[t].padEnd(5),a=r.padEnd(6),l="";n?.correlationId?l=`[${n.correlationId}] `:n?.sessionId&&(l=`[session-${n.sessionId}] `);let c="";o!=null&&(o instanceof Error?c=this.getLevel()===0?`
|
||||
${o.message}
|
||||
${o.stack}`:` ${o.message}`:this.getLevel()===0&&typeof o=="object"?c=`
|
||||
`+JSON.stringify(o,null,2):c=" "+this.formatData(o));let O="";if(n){let{sessionId:D,memorySessionId:et,correlationId:rt,...m}=n;Object.keys(m).length>0&&(O=` {${Object.entries(m).map(([F,w])=>`${F}=${w}`).join(", ")}}`)}let C=`[${E}] [${i}] [${a}] ${l}${e}${O}${c}`;if(this.logFilePath)try{H(this.logFilePath,C+`
|
||||
`,"utf8")}catch(D){process.stderr.write(`[LOGGER] Failed to write to log file: ${D}
|
||||
`)}else process.stderr.write(C+`
|
||||
`)}debug(t,r,e,n){this.log(0,t,r,e,n)}info(t,r,e,n){this.log(1,t,r,e,n)}warn(t,r,e,n){this.log(2,t,r,e,n)}error(t,r,e,n){this.log(3,t,r,e,n)}dataIn(t,r,e,n){this.info(t,`\u2192 ${r}`,e,n)}dataOut(t,r,e,n){this.info(t,`\u2190 ${r}`,e,n)}success(t,r,e,n){this.info(t,`\u2713 ${r}`,e,n)}failure(t,r,e,n){this.error(t,`\u2717 ${r}`,e,n)}timing(t,r,e,n){this.info(t,`\u23F1 ${r}`,n,{duration:`${e}ms`})}happyPathError(t,r,e,n,o=""){let l=((new Error().stack||"").split(`
|
||||
`)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),c=l?`${l[1].split("/").pop()}:${l[2]}`:"unknown",O={...e,location:c};return this.warn(t,`[HAPPY-PATH] ${r}`,O,n),o}},_=new M;import L from"path";import{homedir as B}from"os";import{readFileSync as Y}from"fs";var p={DEFAULT:3e5,HEALTH_CHECK:3e4,WORKER_STARTUP_WAIT:1e3,WORKER_STARTUP_RETRIES:300,PRE_RESTART_SETTLE_DELAY:2e3,WINDOWS_MULTIPLIER:1.5};function I(s){return process.platform==="win32"?Math.round(s*p.WINDOWS_MULTIPLIER):s}import{readFileSync as G,writeFileSync as y,existsSync as P,mkdirSync as K}from"fs";import{join as X,dirname as V}from"path";import{homedir as j}from"os";var h="bugfix,feature,refactor,discovery,decision,change",N="how-it-works,why-it-exists,what-changed,problem-solution,gotcha,pattern,trade-off";var g=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-sonnet-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_WORKER_HOST:"127.0.0.1",CLAUDE_MEM_SKIP_TOOLS:"ListMcpResourcesTool,SlashCommand,Skill,TodoWrite,AskUserQuestion",CLAUDE_MEM_PROVIDER:"claude",CLAUDE_MEM_GEMINI_API_KEY:"",CLAUDE_MEM_GEMINI_MODEL:"gemini-2.5-flash-lite",CLAUDE_MEM_GEMINI_RATE_LIMITING_ENABLED:"true",CLAUDE_MEM_OPENROUTER_API_KEY:"",CLAUDE_MEM_OPENROUTER_MODEL:"xiaomi/mimo-v2-flash:free",CLAUDE_MEM_OPENROUTER_SITE_URL:"",CLAUDE_MEM_OPENROUTER_APP_NAME:"claude-mem",CLAUDE_MEM_OPENROUTER_MAX_CONTEXT_MESSAGES:"20",CLAUDE_MEM_OPENROUTER_MAX_TOKENS:"100000",CLAUDE_MEM_DATA_DIR:X(j(),".claude-mem"),CLAUDE_MEM_LOG_LEVEL:"INFO",CLAUDE_MEM_PYTHON_VERSION:"3.13",CLAUDE_CODE_PATH:"",CLAUDE_MEM_MODE:"code",CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT:"true",CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES:h,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:N,CLAUDE_MEM_CONTEXT_FULL_COUNT:"5",CLAUDE_MEM_CONTEXT_FULL_FIELD:"narrative",CLAUDE_MEM_CONTEXT_SESSION_COUNT:"10",CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY:"true",CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE:"false"};static getAllDefaults(){return{...this.DEFAULTS}}static get(t){return this.DEFAULTS[t]}static getInt(t){let r=this.get(t);return parseInt(r,10)}static getBool(t){return this.get(t)==="true"}static loadFromFile(t){try{if(!P(t)){let E=this.getAllDefaults();try{let i=V(t);P(i)||K(i,{recursive:!0}),y(t,JSON.stringify(E,null,2),"utf-8"),console.log("[SETTINGS] Created settings file with defaults:",t)}catch(i){console.warn("[SETTINGS] Failed to create settings file, using in-memory defaults:",t,i)}return E}let r=G(t,"utf-8"),e=JSON.parse(r),n=e;if(e.env&&typeof e.env=="object"){n=e.env;try{y(t,JSON.stringify(n,null,2),"utf-8"),console.log("[SETTINGS] Migrated settings file from nested to flat schema:",t)}catch(E){console.warn("[SETTINGS] Failed to auto-migrate settings file:",t,E)}}let o={...this.DEFAULTS};for(let E of Object.keys(this.DEFAULTS))n[E]!==void 0&&(o[E]=n[E]);return o}catch(r){return console.warn("[SETTINGS] Failed to load settings, using defaults:",t,r),this.getAllDefaults()}}};function $(s={}){let{port:t,includeSkillFallback:r=!1,customPrefix:e,actualError:n}=s,o=e||"Worker service connection failed.",E=t?` (port ${t})`:"",i=`${o}${E}
|
||||
|
||||
`;return i+=`To restart the worker:
|
||||
`,i+=`1. Exit Claude Code completely
|
||||
`,i+=`2. Run: npm run worker:restart
|
||||
`,i+="3. Restart Claude Code",r&&(i+=`
|
||||
|
||||
If that doesn't work, try: /troubleshoot`),n&&(i=`Worker Error: ${n}
|
||||
|
||||
${i}`),i}var J=L.join(B(),".claude","plugins","marketplaces","thedotmack"),mt=I(p.HEALTH_CHECK),S=null;function u(){if(S!==null)return S;let s=L.join(g.get("CLAUDE_MEM_DATA_DIR"),"settings.json"),t=g.loadFromFile(s);return S=parseInt(t.CLAUDE_MEM_WORKER_PORT,10),S}async function z(){let s=u();return(await fetch(`http://127.0.0.1:${s}/api/readiness`)).ok}function q(){let s=L.join(J,"package.json");return JSON.parse(Y(s,"utf-8")).version}async function Q(){let s=u(),t=await fetch(`http://127.0.0.1:${s}/api/version`);if(!t.ok)throw new Error(`Failed to get worker version: ${t.status}`);return(await t.json()).version}async function Z(){let s=q(),t=await Q();s!==t&&_.debug("SYSTEM","Version check",{pluginVersion:s,workerVersion:t,note:"Mismatch will be auto-restarted by worker-service start command"})}async function k(){for(let r=0;r<75;r++){try{if(await z()){await Z();return}}catch(e){_.debug("SYSTEM","Worker health check failed, will retry",{attempt:r+1,maxRetries:75,error:e instanceof Error?e.message:String(e)})}await new Promise(e=>setTimeout(e,200))}throw new Error($({port:u(),customPrefix:"Worker did not become ready within 15 seconds."}))}async function tt(s){if(await k(),!s)throw new Error("saveHook requires input");let{session_id:t,cwd:r,tool_name:e,tool_input:n,tool_response:o}=s,E=u(),i=_.formatTool(e,n);if(_.dataIn("HOOK",`PostToolUse: ${i}`,{workerPort:E}),!r)throw new Error(`Missing cwd in PostToolUse hook input for session ${t}, tool ${e}`);let a=await fetch(`http://127.0.0.1:${E}/api/sessions/observations`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({contentSessionId:t,tool_name:e,tool_input:n,tool_response:o,cwd:r})});if(!a.ok)throw new Error(`Observation storage failed: ${a.status}`);_.debug("HOOK","Observation sent successfully",{toolName:e}),console.log(d)}var A="";v.on("data",s=>A+=s);v.on("end",async()=>{try{let s;try{s=A?JSON.parse(A):void 0}catch(t){throw new Error(`Failed to parse hook input: ${t instanceof Error?t.message:String(t)}`)}await tt(s)}catch(s){_.error("HOOK","save-hook failed",{},s)}finally{process.exit(0)}});
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/usr/bin/env bun
|
||||
import{stdin as v}from"process";var T=JSON.stringify({continue:!0,suppressOutput:!0});import{appendFileSync as H,existsSync as R,mkdirSync as W,readFileSync as b}from"fs";import{join as O}from"path";import{homedir as G}from"os";var p=(o=>(o[o.DEBUG=0]="DEBUG",o[o.INFO=1]="INFO",o[o.WARN=2]="WARN",o[o.ERROR=3]="ERROR",o[o.SILENT=4]="SILENT",o))(p||{}),U=O(G(),".claude-mem"),M=class{level=null;useColor;logFilePath=null;logFileInitialized=!1;constructor(){this.useColor=process.stdout.isTTY??!1}ensureLogFileInitialized(){if(!this.logFileInitialized){this.logFileInitialized=!0;try{let t=O(U,"logs");R(t)||W(t,{recursive:!0});let r=new Date().toISOString().split("T")[0];this.logFilePath=O(t,`claude-mem-${r}.log`)}catch(t){console.error("[LOGGER] Failed to initialize log file:",t),this.logFilePath=null}}}getLevel(){if(this.level===null)try{let t=O(U,"settings.json");if(R(t)){let r=b(t,"utf-8"),n=(JSON.parse(r).CLAUDE_MEM_LOG_LEVEL||"INFO").toUpperCase();this.level=p[n]??1}else this.level=1}catch{this.level=1}return this.level}correlationId(t,r){return`obs-${t}-${r}`}sessionId(t){return`session-${t}`}formatData(t){if(t==null)return"";if(typeof t=="string")return t;if(typeof t=="number"||typeof t=="boolean")return t.toString();if(typeof t=="object"){if(t instanceof Error)return this.getLevel()===0?`${t.message}
|
||||
${t.stack}`:t.message;if(Array.isArray(t))return`[${t.length} items]`;let r=Object.keys(t);return r.length===0?"{}":r.length<=3?JSON.stringify(t):`{${r.length} keys: ${r.slice(0,3).join(", ")}...}`}return String(t)}formatTool(t,r){if(!r)return t;let e=r;if(typeof r=="string")try{e=JSON.parse(r)}catch{e=r}if(t==="Bash"&&e.command)return`${t}(${e.command})`;if(e.file_path)return`${t}(${e.file_path})`;if(e.notebook_path)return`${t}(${e.notebook_path})`;if(t==="Glob"&&e.pattern)return`${t}(${e.pattern})`;if(t==="Grep"&&e.pattern)return`${t}(${e.pattern})`;if(e.url)return`${t}(${e.url})`;if(e.query)return`${t}(${e.query})`;if(t==="Task"){if(e.subagent_type)return`${t}(${e.subagent_type})`;if(e.description)return`${t}(${e.description})`}return t==="Skill"&&e.skill?`${t}(${e.skill})`:t==="LSP"&&e.operation?`${t}(${e.operation})`:t}formatTimestamp(t){let r=t.getFullYear(),e=String(t.getMonth()+1).padStart(2,"0"),n=String(t.getDate()).padStart(2,"0"),o=String(t.getHours()).padStart(2,"0"),E=String(t.getMinutes()).padStart(2,"0"),i=String(t.getSeconds()).padStart(2,"0"),_=String(t.getMilliseconds()).padStart(3,"0");return`${r}-${e}-${n} ${o}:${E}:${i}.${_}`}log(t,r,e,n,o){if(t<this.getLevel())return;this.ensureLogFileInitialized();let E=this.formatTimestamp(new Date),i=p[t].padEnd(5),_=r.padEnd(6),a="";n?.correlationId?a=`[${n.correlationId}] `:n?.sessionId&&(a=`[session-${n.sessionId}] `);let l="";o!=null&&(o instanceof Error?l=this.getLevel()===0?`
|
||||
${o.message}
|
||||
${o.stack}`:` ${o.message}`:this.getLevel()===0&&typeof o=="object"?l=`
|
||||
`+JSON.stringify(o,null,2):l=" "+this.formatData(o));let f="";if(n){let{sessionId:D,memorySessionId:st,correlationId:ot,...d}=n;Object.keys(d).length>0&&(f=` {${Object.entries(d).map(([F,x])=>`${F}=${x}`).join(", ")}}`)}let C=`[${E}] [${i}] [${_}] ${a}${e}${f}${l}`;if(this.logFilePath)try{H(this.logFilePath,C+`
|
||||
`,"utf8")}catch(D){process.stderr.write(`[LOGGER] Failed to write to log file: ${D}
|
||||
`)}else process.stderr.write(C+`
|
||||
`)}debug(t,r,e,n){this.log(0,t,r,e,n)}info(t,r,e,n){this.log(1,t,r,e,n)}warn(t,r,e,n){this.log(2,t,r,e,n)}error(t,r,e,n){this.log(3,t,r,e,n)}dataIn(t,r,e,n){this.info(t,`\u2192 ${r}`,e,n)}dataOut(t,r,e,n){this.info(t,`\u2190 ${r}`,e,n)}success(t,r,e,n){this.info(t,`\u2713 ${r}`,e,n)}failure(t,r,e,n){this.error(t,`\u2717 ${r}`,e,n)}timing(t,r,e,n){this.info(t,`\u23F1 ${r}`,n,{duration:`${e}ms`})}happyPathError(t,r,e,n,o=""){let a=((new Error().stack||"").split(`
|
||||
`)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),l=a?`${a[1].split("/").pop()}:${a[2]}`:"unknown",f={...e,location:l};return this.warn(t,`[HAPPY-PATH] ${r}`,f,n),o}},c=new M;import L from"path";import{homedir as Y}from"os";import{readFileSync as J}from"fs";var m={DEFAULT:3e5,HEALTH_CHECK:3e4,WORKER_STARTUP_WAIT:1e3,WORKER_STARTUP_RETRIES:300,PRE_RESTART_SETTLE_DELAY:2e3,WINDOWS_MULTIPLIER:1.5};function h(s){return process.platform==="win32"?Math.round(s*m.WINDOWS_MULTIPLIER):s}import{readFileSync as K,writeFileSync as N,existsSync as $,mkdirSync as X}from"fs";import{join as V,dirname as j}from"path";import{homedir as B}from"os";var I="bugfix,feature,refactor,discovery,decision,change",y="how-it-works,why-it-exists,what-changed,problem-solution,gotcha,pattern,trade-off";var g=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-sonnet-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_WORKER_HOST:"127.0.0.1",CLAUDE_MEM_SKIP_TOOLS:"ListMcpResourcesTool,SlashCommand,Skill,TodoWrite,AskUserQuestion",CLAUDE_MEM_PROVIDER:"claude",CLAUDE_MEM_GEMINI_API_KEY:"",CLAUDE_MEM_GEMINI_MODEL:"gemini-2.5-flash-lite",CLAUDE_MEM_GEMINI_RATE_LIMITING_ENABLED:"true",CLAUDE_MEM_OPENROUTER_API_KEY:"",CLAUDE_MEM_OPENROUTER_MODEL:"xiaomi/mimo-v2-flash:free",CLAUDE_MEM_OPENROUTER_SITE_URL:"",CLAUDE_MEM_OPENROUTER_APP_NAME:"claude-mem",CLAUDE_MEM_OPENROUTER_MAX_CONTEXT_MESSAGES:"20",CLAUDE_MEM_OPENROUTER_MAX_TOKENS:"100000",CLAUDE_MEM_DATA_DIR:V(B(),".claude-mem"),CLAUDE_MEM_LOG_LEVEL:"INFO",CLAUDE_MEM_PYTHON_VERSION:"3.13",CLAUDE_CODE_PATH:"",CLAUDE_MEM_MODE:"code",CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT:"true",CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES:I,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:y,CLAUDE_MEM_CONTEXT_FULL_COUNT:"5",CLAUDE_MEM_CONTEXT_FULL_FIELD:"narrative",CLAUDE_MEM_CONTEXT_SESSION_COUNT:"10",CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY:"true",CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE:"false"};static getAllDefaults(){return{...this.DEFAULTS}}static get(t){return this.DEFAULTS[t]}static getInt(t){let r=this.get(t);return parseInt(r,10)}static getBool(t){return this.get(t)==="true"}static loadFromFile(t){try{if(!$(t)){let E=this.getAllDefaults();try{let i=j(t);$(i)||X(i,{recursive:!0}),N(t,JSON.stringify(E,null,2),"utf-8"),console.log("[SETTINGS] Created settings file with defaults:",t)}catch(i){console.warn("[SETTINGS] Failed to create settings file, using in-memory defaults:",t,i)}return E}let r=K(t,"utf-8"),e=JSON.parse(r),n=e;if(e.env&&typeof e.env=="object"){n=e.env;try{N(t,JSON.stringify(n,null,2),"utf-8"),console.log("[SETTINGS] Migrated settings file from nested to flat schema:",t)}catch(E){console.warn("[SETTINGS] Failed to auto-migrate settings file:",t,E)}}let o={...this.DEFAULTS};for(let E of Object.keys(this.DEFAULTS))n[E]!==void 0&&(o[E]=n[E]);return o}catch(r){return console.warn("[SETTINGS] Failed to load settings, using defaults:",t,r),this.getAllDefaults()}}};function k(s={}){let{port:t,includeSkillFallback:r=!1,customPrefix:e,actualError:n}=s,o=e||"Worker service connection failed.",E=t?` (port ${t})`:"",i=`${o}${E}
|
||||
|
||||
`;return i+=`To restart the worker:
|
||||
`,i+=`1. Exit Claude Code completely
|
||||
`,i+=`2. Run: npm run worker:restart
|
||||
`,i+="3. Restart Claude Code",r&&(i+=`
|
||||
|
||||
If that doesn't work, try: /troubleshoot`),n&&(i=`Worker Error: ${n}
|
||||
|
||||
${i}`),i}var z=L.join(Y(),".claude","plugins","marketplaces","thedotmack"),Ut=h(m.HEALTH_CHECK),S=null;function u(){if(S!==null)return S;let s=L.join(g.get("CLAUDE_MEM_DATA_DIR"),"settings.json"),t=g.loadFromFile(s);return S=parseInt(t.CLAUDE_MEM_WORKER_PORT,10),S}async function q(){let s=u();return(await fetch(`http://127.0.0.1:${s}/api/readiness`)).ok}function Q(){let s=L.join(z,"package.json");return JSON.parse(J(s,"utf-8")).version}async function Z(){let s=u(),t=await fetch(`http://127.0.0.1:${s}/api/version`);if(!t.ok)throw new Error(`Failed to get worker version: ${t.status}`);return(await t.json()).version}async function tt(){let s=Q(),t=await Z();s!==t&&c.debug("SYSTEM","Version check",{pluginVersion:s,workerVersion:t,note:"Mismatch will be auto-restarted by worker-service start command"})}async function P(){for(let r=0;r<75;r++){try{if(await q()){await tt();return}}catch(e){c.debug("SYSTEM","Worker health check failed, will retry",{attempt:r+1,maxRetries:75,error:e instanceof Error?e.message:String(e)})}await new Promise(e=>setTimeout(e,200))}throw new Error(k({port:u(),customPrefix:"Worker did not become ready within 15 seconds."}))}import{readFileSync as et,existsSync as rt}from"fs";function w(s,t,r=!1){if(!s||!rt(s))throw new Error(`Transcript path missing or file does not exist: ${s}`);let e=et(s,"utf-8").trim();if(!e)throw new Error(`Transcript file exists but is empty: ${s}`);let n=e.split(`
|
||||
`),o=!1;for(let E=n.length-1;E>=0;E--){let i=JSON.parse(n[E]);if(i.type===t&&(o=!0,i.message?.content)){let _="",a=i.message.content;if(typeof a=="string")_=a;else if(Array.isArray(a))_=a.filter(l=>l.type==="text").map(l=>l.text).join(`
|
||||
`);else throw new Error(`Unknown message content format in transcript. Type: ${typeof a}`);return r&&(_=_.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g,""),_=_.replace(/\n{3,}/g,`
|
||||
|
||||
`).trim()),_}}if(!o)throw new Error(`No message found for role '${t}' in transcript: ${s}`);return""}async function nt(s){if(await P(),!s)throw new Error("summaryHook requires input");let{session_id:t}=s,r=u();if(!s.transcript_path)throw new Error(`Missing transcript_path in Stop hook input for session ${t}`);let e=w(s.transcript_path,"assistant",!0);c.dataIn("HOOK","Stop: Requesting summary",{workerPort:r,hasLastAssistantMessage:!!e});let n=await fetch(`http://127.0.0.1:${r}/api/sessions/summarize`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({contentSessionId:t,last_assistant_message:e})});if(!n.ok)throw console.log(T),new Error(`Summary generation failed: ${n.status}`);c.debug("HOOK","Summary request sent successfully"),console.log(T)}var A="";v.on("data",s=>A+=s);v.on("end",async()=>{try{let s;try{s=A?JSON.parse(A):void 0}catch(t){throw new Error(`Failed to parse hook input: ${t instanceof Error?t.message:String(t)}`)}await nt(s)}catch(s){c.error("HOOK","summary-hook failed",{},s)}finally{process.exit(0)}});
|
||||
@@ -1,30 +0,0 @@
|
||||
#!/usr/bin/env bun
|
||||
import{basename as Z}from"path";import p from"path";import{homedir as j}from"os";import{readFileSync as B}from"fs";import{appendFileSync as w,existsSync as R,mkdirSync as W,readFileSync as b}from"fs";import{join as T}from"path";import{homedir as x}from"os";var M=(o=>(o[o.DEBUG=0]="DEBUG",o[o.INFO=1]="INFO",o[o.WARN=2]="WARN",o[o.ERROR=3]="ERROR",o[o.SILENT=4]="SILENT",o))(M||{}),U=T(x(),".claude-mem"),S=class{level=null;useColor;logFilePath=null;logFileInitialized=!1;constructor(){this.useColor=process.stdout.isTTY??!1}ensureLogFileInitialized(){if(!this.logFileInitialized){this.logFileInitialized=!0;try{let t=T(U,"logs");R(t)||W(t,{recursive:!0});let r=new Date().toISOString().split("T")[0];this.logFilePath=T(t,`claude-mem-${r}.log`)}catch(t){console.error("[LOGGER] Failed to initialize log file:",t),this.logFilePath=null}}}getLevel(){if(this.level===null)try{let t=T(U,"settings.json");if(R(t)){let r=b(t,"utf-8"),n=(JSON.parse(r).CLAUDE_MEM_LOG_LEVEL||"INFO").toUpperCase();this.level=M[n]??1}else this.level=1}catch{this.level=1}return this.level}correlationId(t,r){return`obs-${t}-${r}`}sessionId(t){return`session-${t}`}formatData(t){if(t==null)return"";if(typeof t=="string")return t;if(typeof t=="number"||typeof t=="boolean")return t.toString();if(typeof t=="object"){if(t instanceof Error)return this.getLevel()===0?`${t.message}
|
||||
${t.stack}`:t.message;if(Array.isArray(t))return`[${t.length} items]`;let r=Object.keys(t);return r.length===0?"{}":r.length<=3?JSON.stringify(t):`{${r.length} keys: ${r.slice(0,3).join(", ")}...}`}return String(t)}formatTool(t,r){if(!r)return t;let e=r;if(typeof r=="string")try{e=JSON.parse(r)}catch{e=r}if(t==="Bash"&&e.command)return`${t}(${e.command})`;if(e.file_path)return`${t}(${e.file_path})`;if(e.notebook_path)return`${t}(${e.notebook_path})`;if(t==="Glob"&&e.pattern)return`${t}(${e.pattern})`;if(t==="Grep"&&e.pattern)return`${t}(${e.pattern})`;if(e.url)return`${t}(${e.url})`;if(e.query)return`${t}(${e.query})`;if(t==="Task"){if(e.subagent_type)return`${t}(${e.subagent_type})`;if(e.description)return`${t}(${e.description})`}return t==="Skill"&&e.skill?`${t}(${e.skill})`:t==="LSP"&&e.operation?`${t}(${e.operation})`:t}formatTimestamp(t){let r=t.getFullYear(),e=String(t.getMonth()+1).padStart(2,"0"),n=String(t.getDate()).padStart(2,"0"),o=String(t.getHours()).padStart(2,"0"),E=String(t.getMinutes()).padStart(2,"0"),s=String(t.getSeconds()).padStart(2,"0"),g=String(t.getMilliseconds()).padStart(3,"0");return`${r}-${e}-${n} ${o}:${E}:${s}.${g}`}log(t,r,e,n,o){if(t<this.getLevel())return;this.ensureLogFileInitialized();let E=this.formatTimestamp(new Date),s=M[t].padEnd(5),g=r.padEnd(6),_="";n?.correlationId?_=`[${n.correlationId}] `:n?.sessionId&&(_=`[session-${n.sessionId}] `);let a="";o!=null&&(o instanceof Error?a=this.getLevel()===0?`
|
||||
${o.message}
|
||||
${o.stack}`:` ${o.message}`:this.getLevel()===0&&typeof o=="object"?a=`
|
||||
`+JSON.stringify(o,null,2):a=" "+this.formatData(o));let u="";if(n){let{sessionId:D,memorySessionId:rt,correlationId:nt,...m}=n;Object.keys(m).length>0&&(u=` {${Object.entries(m).map(([P,F])=>`${P}=${F}`).join(", ")}}`)}let A=`[${E}] [${s}] [${g}] ${_}${e}${u}${a}`;if(this.logFilePath)try{w(this.logFilePath,A+`
|
||||
`,"utf8")}catch(D){process.stderr.write(`[LOGGER] Failed to write to log file: ${D}
|
||||
`)}else process.stderr.write(A+`
|
||||
`)}debug(t,r,e,n){this.log(0,t,r,e,n)}info(t,r,e,n){this.log(1,t,r,e,n)}warn(t,r,e,n){this.log(2,t,r,e,n)}error(t,r,e,n){this.log(3,t,r,e,n)}dataIn(t,r,e,n){this.info(t,`\u2192 ${r}`,e,n)}dataOut(t,r,e,n){this.info(t,`\u2190 ${r}`,e,n)}success(t,r,e,n){this.info(t,`\u2713 ${r}`,e,n)}failure(t,r,e,n){this.error(t,`\u2717 ${r}`,e,n)}timing(t,r,e,n){this.info(t,`\u23F1 ${r}`,n,{duration:`${e}ms`})}happyPathError(t,r,e,n,o=""){let _=((new Error().stack||"").split(`
|
||||
`)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),a=_?`${_[1].split("/").pop()}:${_[2]}`:"unknown",u={...e,location:a};return this.warn(t,`[HAPPY-PATH] ${r}`,u,n),o}},f=new S;var L={DEFAULT:3e5,HEALTH_CHECK:3e4,WORKER_STARTUP_WAIT:1e3,WORKER_STARTUP_RETRIES:300,PRE_RESTART_SETTLE_DELAY:2e3,WINDOWS_MULTIPLIER:1.5};function d(i){return process.platform==="win32"?Math.round(i*L.WINDOWS_MULTIPLIER):i}import{readFileSync as G,writeFileSync as N,existsSync as y,mkdirSync as H}from"fs";import{join as K,dirname as X}from"path";import{homedir as V}from"os";var h="bugfix,feature,refactor,discovery,decision,change",I="how-it-works,why-it-exists,what-changed,problem-solution,gotcha,pattern,trade-off";var l=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-sonnet-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_WORKER_HOST:"127.0.0.1",CLAUDE_MEM_SKIP_TOOLS:"ListMcpResourcesTool,SlashCommand,Skill,TodoWrite,AskUserQuestion",CLAUDE_MEM_PROVIDER:"claude",CLAUDE_MEM_GEMINI_API_KEY:"",CLAUDE_MEM_GEMINI_MODEL:"gemini-2.5-flash-lite",CLAUDE_MEM_GEMINI_RATE_LIMITING_ENABLED:"true",CLAUDE_MEM_OPENROUTER_API_KEY:"",CLAUDE_MEM_OPENROUTER_MODEL:"xiaomi/mimo-v2-flash:free",CLAUDE_MEM_OPENROUTER_SITE_URL:"",CLAUDE_MEM_OPENROUTER_APP_NAME:"claude-mem",CLAUDE_MEM_OPENROUTER_MAX_CONTEXT_MESSAGES:"20",CLAUDE_MEM_OPENROUTER_MAX_TOKENS:"100000",CLAUDE_MEM_DATA_DIR:K(V(),".claude-mem"),CLAUDE_MEM_LOG_LEVEL:"INFO",CLAUDE_MEM_PYTHON_VERSION:"3.13",CLAUDE_CODE_PATH:"",CLAUDE_MEM_MODE:"code",CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT:"true",CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES:h,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:I,CLAUDE_MEM_CONTEXT_FULL_COUNT:"5",CLAUDE_MEM_CONTEXT_FULL_FIELD:"narrative",CLAUDE_MEM_CONTEXT_SESSION_COUNT:"10",CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY:"true",CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE:"false"};static getAllDefaults(){return{...this.DEFAULTS}}static get(t){return this.DEFAULTS[t]}static getInt(t){let r=this.get(t);return parseInt(r,10)}static getBool(t){return this.get(t)==="true"}static loadFromFile(t){try{if(!y(t)){let E=this.getAllDefaults();try{let s=X(t);y(s)||H(s,{recursive:!0}),N(t,JSON.stringify(E,null,2),"utf-8"),console.log("[SETTINGS] Created settings file with defaults:",t)}catch(s){console.warn("[SETTINGS] Failed to create settings file, using in-memory defaults:",t,s)}return E}let r=G(t,"utf-8"),e=JSON.parse(r),n=e;if(e.env&&typeof e.env=="object"){n=e.env;try{N(t,JSON.stringify(n,null,2),"utf-8"),console.log("[SETTINGS] Migrated settings file from nested to flat schema:",t)}catch(E){console.warn("[SETTINGS] Failed to auto-migrate settings file:",t,E)}}let o={...this.DEFAULTS};for(let E of Object.keys(this.DEFAULTS))n[E]!==void 0&&(o[E]=n[E]);return o}catch(r){return console.warn("[SETTINGS] Failed to load settings, using defaults:",t,r),this.getAllDefaults()}}};function $(i={}){let{port:t,includeSkillFallback:r=!1,customPrefix:e,actualError:n}=i,o=e||"Worker service connection failed.",E=t?` (port ${t})`:"",s=`${o}${E}
|
||||
|
||||
`;return s+=`To restart the worker:
|
||||
`,s+=`1. Exit Claude Code completely
|
||||
`,s+=`2. Run: npm run worker:restart
|
||||
`,s+="3. Restart Claude Code",r&&(s+=`
|
||||
|
||||
If that doesn't work, try: /troubleshoot`),n&&(s=`Worker Error: ${n}
|
||||
|
||||
${s}`),s}var Y=p.join(j(),".claude","plugins","marketplaces","thedotmack"),mt=d(L.HEALTH_CHECK),O=null;function c(){if(O!==null)return O;let i=p.join(l.get("CLAUDE_MEM_DATA_DIR"),"settings.json"),t=l.loadFromFile(i);return O=parseInt(t.CLAUDE_MEM_WORKER_PORT,10),O}async function J(){let i=c();return(await fetch(`http://127.0.0.1:${i}/api/readiness`)).ok}function z(){let i=p.join(Y,"package.json");return JSON.parse(B(i,"utf-8")).version}async function q(){let i=c(),t=await fetch(`http://127.0.0.1:${i}/api/version`);if(!t.ok)throw new Error(`Failed to get worker version: ${t.status}`);return(await t.json()).version}async function Q(){let i=z(),t=await q();i!==t&&f.debug("SYSTEM","Version check",{pluginVersion:i,workerVersion:t,note:"Mismatch will be auto-restarted by worker-service start command"})}async function k(){for(let r=0;r<75;r++){try{if(await J()){await Q();return}}catch(e){f.debug("SYSTEM","Worker health check failed, will retry",{attempt:r+1,maxRetries:75,error:e instanceof Error?e.message:String(e)})}await new Promise(e=>setTimeout(e,200))}throw new Error($({port:c(),customPrefix:"Worker did not become ready within 15 seconds."}))}await k();var v=c(),tt=Z(process.cwd()),C=await fetch(`http://127.0.0.1:${v}/api/context/inject?project=${encodeURIComponent(tt)}&colors=true`,{method:"GET"});if(!C.ok)throw new Error(`Failed to fetch context: ${C.status}`);var et=await C.text();console.error(`
|
||||
|
||||
\u{1F4DD} Claude-Mem Context Loaded
|
||||
\u2139\uFE0F Note: This appears as stderr but is informational only
|
||||
|
||||
`+et+`
|
||||
|
||||
\u{1F4A1} New! Wrap all or part of any message with <private> ... </private> to prevent storing sensitive information in your observation history.
|
||||
|
||||
\u{1F4AC} Community https://discord.gg/J4wttp9vDu
|
||||
\u{1F4FA} Watch live in browser http://localhost:${v}/
|
||||
`);process.exit(1);
|
||||
+153
-129
File diff suppressed because one or more lines are too long
@@ -1,2 +1,2 @@
|
||||
#!/usr/bin/env bun
|
||||
"use strict";var m=Object.create;var w=Object.defineProperty;var u=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var f=Object.getPrototypeOf,x=Object.prototype.hasOwnProperty;var g=(e,i,n,o)=>{if(i&&typeof i=="object"||typeof i=="function")for(let s of I(i))!x.call(e,s)&&s!==n&&w(e,s,{get:()=>i[s],enumerable:!(o=u(i,s))||o.enumerable});return e};var k=(e,i,n)=>(n=e!=null?m(f(e)):{},g(i||!e||!e.__esModule?w(n,"default",{value:e,enumerable:!0}):n,e));var c=require("child_process"),p=k(require("path"),1),y=process.platform==="win32",P=__dirname,l=p.default.join(P,"worker-service.cjs"),t=null,a=!1;function r(e){let i=new Date().toISOString();console.log(`[${i}] [wrapper] ${e}`)}function h(){r(`Spawning inner worker: ${l}`),t=(0,c.spawn)(process.execPath,[l],{stdio:["inherit","inherit","inherit","ipc"],env:{...process.env,CLAUDE_MEM_MANAGED:"true"},cwd:p.default.dirname(l)}),t.on("message",async e=>{(e.type==="restart"||e.type==="shutdown")&&(r(`${e.type} requested by inner`),a=!0,await d(),r("Exiting wrapper"),process.exit(0))}),t.on("exit",(e,i)=>{r(`Inner exited with code=${e}, signal=${i}`),t=null,a||(r("Inner exited unexpectedly, wrapper exiting (hooks will restart if needed)"),process.exit(e??1))}),t.on("error",e=>{r(`Inner error: ${e.message}`)})}async function d(){if(!t||!t.pid){r("No inner process to kill");return}let e=t.pid;if(r(`Killing inner process tree (pid=${e})`),y)try{(0,c.execSync)(`taskkill /PID ${e} /T /F`,{timeout:1e4,stdio:"ignore"}),r(`taskkill completed for pid=${e}`)}catch(i){r(`taskkill failed (process may be dead): ${i}`)}else{t.kill("SIGTERM");let i=new Promise(o=>{if(!t){o();return}t.on("exit",()=>o())}),n=new Promise(o=>setTimeout(()=>o(),5e3));await Promise.race([i,n]),t&&!t.killed&&(r("Inner did not exit gracefully, force killing"),t.kill("SIGKILL"))}await S(e,5e3),t=null,r("Inner process terminated")}async function S(e,i){let n=Date.now();for(;Date.now()-n<i;)try{process.kill(e,0),await new Promise(o=>setTimeout(o,100))}catch{return}r(`Timeout waiting for process ${e} to exit`)}process.on("SIGTERM",async()=>{r("Wrapper received SIGTERM"),a=!0,await d(),process.exit(0)});process.on("SIGINT",async()=>{r("Wrapper received SIGINT"),a=!0,await d(),process.exit(0)});r("Wrapper starting");h();
|
||||
"use strict";var m=Object.create;var w=Object.defineProperty;var u=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var f=Object.getPrototypeOf,x=Object.prototype.hasOwnProperty;var g=(e,i,n,o)=>{if(i&&typeof i=="object"||typeof i=="function")for(let s of I(i))!x.call(e,s)&&s!==n&&w(e,s,{get:()=>i[s],enumerable:!(o=u(i,s))||o.enumerable});return e};var k=(e,i,n)=>(n=e!=null?m(f(e)):{},g(i||!e||!e.__esModule?w(n,"default",{value:e,enumerable:!0}):n,e));var c=require("child_process"),p=k(require("path"),1),y=process.platform==="win32",P=__dirname,l=p.default.join(P,"worker-service.cjs"),t=null,a=!1;function r(e){let i=new Date().toISOString();console.log(`[${i}] [wrapper] ${e}`)}function h(){r(`Spawning inner worker: ${l}`),t=(0,c.spawn)(process.execPath,[l],{stdio:["inherit","inherit","inherit","ipc"],env:{...process.env,CLAUDE_MEM_MANAGED:"true"},cwd:p.default.dirname(l)}),t.on("message",async e=>{(e.type==="restart"||e.type==="shutdown")&&(r(`${e.type} requested by inner`),a=!0,await d(),r("Exiting wrapper"),process.exit(0))}),t.on("exit",(e,i)=>{r(`Inner exited with code=${e}, signal=${i}`),t=null,a||(r("Inner exited unexpectedly, wrapper exiting (hooks will restart if needed)"),process.exit(e??0))}),t.on("error",e=>{r(`Inner error: ${e.message}`)})}async function d(){if(!t||!t.pid){r("No inner process to kill");return}let e=t.pid;if(r(`Killing inner process tree (pid=${e})`),y)try{(0,c.execSync)(`taskkill /PID ${e} /T /F`,{timeout:1e4,stdio:"ignore"}),r(`taskkill completed for pid=${e}`)}catch(i){r(`taskkill failed (process may be dead): ${i}`)}else{t.kill("SIGTERM");let i=new Promise(o=>{if(!t){o();return}t.on("exit",()=>o())}),n=new Promise(o=>setTimeout(()=>o(),5e3));await Promise.race([i,n]),t&&!t.killed&&(r("Inner did not exit gracefully, force killing"),t.kill("SIGKILL"))}await S(e,5e3),t=null,r("Inner process terminated")}async function S(e,i){let n=Date.now();for(;Date.now()-n<i;)try{process.kill(e,0),await new Promise(o=>setTimeout(o,100))}catch{return}r(`Timeout waiting for process ${e} to exit`)}process.on("SIGTERM",async()=>{r("Wrapper received SIGTERM"),a=!0,await d(),process.exit(0)});process.on("SIGINT",async()=>{r("Wrapper received SIGINT"),a=!0,await d(),process.exit(0)});r("Wrapper starting");h();
|
||||
|
||||
+48
-123
@@ -3,135 +3,60 @@
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Dec 25, 2025
|
||||
### Nov 5, 2025
|
||||
|
||||
**viewer-bundle.js**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #32794 | 9:51 PM | ✅ | Built claude-mem v8.2.0 production artifacts | ~376 |
|
||||
| #32789 | 9:49 PM | 🟣 | Gemini AI Provider Integration Merged to Main | ~409 |
|
||||
| #32654 | 8:51 PM | 🔵 | Identified multiple files related to queue recovery | ~375 |
|
||||
| #32643 | 8:46 PM | ✅ | Plugin Build and Marketplace Synchronization | ~336 |
|
||||
| #32600 | 8:42 PM | 🔵 | Identified potential UI element for billing toggle | ~146 |
|
||||
| #32559 | 8:18 PM | 🔵 | Listed files changed in the current branch | ~169 |
|
||||
| #32458 | 5:42 PM | ✅ | Rebuilt and synchronized plugin files after merge | ~286 |
|
||||
| #3910 | 8:28 PM | ✅ | Refined stats counter visual design | ~343 |
|
||||
| #3909 | " | 🟣 | Added clarifying descriptions to settings UI | ~335 |
|
||||
| #3812 | 6:08 PM | 🟣 | Enhanced card typography and centered content layout | ~358 |
|
||||
|
||||
### Nov 8, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #5133 | 7:29 PM | ✅ | Version 5.2.3 Released with Build Process | ~487 |
|
||||
| #4916 | 1:49 PM | ⚖️ | Claude Mem Pro Premium Offering Implementation Plan Finalized | ~946 |
|
||||
| #4902 | 1:35 PM | 🟣 | Claude Mem Pro Premium Project Initialization | ~679 |
|
||||
| #4901 | 1:31 PM | ⚖️ | Premium claude-mem Project Architecture and Planning | ~797 |
|
||||
|
||||
### Dec 1, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #18480 | 3:39 PM | ✅ | Successfully Rebuilt Plugin After Merge Conflict Resolution | ~294 |
|
||||
|
||||
### Dec 4, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #20052 | 3:23 PM | ✅ | Built and deployed version 6.5.2 to marketplace | ~321 |
|
||||
|
||||
### Dec 9, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #22557 | 1:08 AM | ✅ | Build completed for version 7.0.3 | ~342 |
|
||||
|
||||
### Dec 10, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #23444 | 2:25 PM | 🟣 | Build Pipeline Execution Successful | ~293 |
|
||||
|
||||
### Dec 16, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #27554 | 4:48 PM | ✅ | Project built successfully with version 7.3.1 | ~306 |
|
||||
|
||||
### Dec 26, 2025
|
||||
|
||||
**viewer-bundle.js**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #32985 | 11:24 PM | ✅ | Built claude-mem version 8.2.2 release artifacts | ~299 |
|
||||
| #32975 | 11:04 PM | ✅ | Build and sync pipeline completed successfully | ~208 |
|
||||
| #32864 | 7:30 PM | ✅ | Built and deployed claude-mem v8.2.0 to marketplace | ~375 |
|
||||
| #32861 | 7:08 PM | ✅ | Built claude-mem v8.2.0 production artifacts | ~360 |
|
||||
|
||||
### Dec 27, 2025
|
||||
|
||||
**viewer-bundle.js**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #33053 | 5:28 PM | ✅ | Built claude-mem 8.2.3 release artifacts | ~315 |
|
||||
|
||||
### Dec 28, 2025
|
||||
|
||||
**viewer-bundle.js**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #33670 | 11:52 PM | ✅ | Built and deployed memory session ID fix to marketplace | ~288 |
|
||||
| #33649 | 11:43 PM | ✅ | Build and Deployment System Synced to Marketplace | ~450 |
|
||||
| #33612 | 11:22 PM | ✅ | Built and deployed claude-mem version 8.2.6 with mem-search skill | ~300 |
|
||||
| #33603 | 11:20 PM | ✅ | Claude-mem plugin build and marketplace sync completed | ~425 |
|
||||
| #33485 | 10:35 PM | ✅ | Built Project with Version 8.2.6 Across All Components | ~303 |
|
||||
| #33279 | 3:07 PM | ✅ | Changed Default OpenRouter Model to Free Tier Option | ~285 |
|
||||
|
||||
### Dec 29, 2025
|
||||
|
||||
**viewer-bundle.js**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34350 | 11:12 PM | ✅ | Version 8.5.0 Build Completed Successfully | ~425 |
|
||||
| #34208 | 10:00 PM | ✅ | claude-mem v8.2.10 built and synced to marketplace | ~416 |
|
||||
| #34163 | 9:38 PM | ✅ | Project rebuilt with updated interactive setup wizard | ~326 |
|
||||
| #34092 | 9:02 PM | ✅ | Built claude-mem project with updated interactive setup wizard | ~452 |
|
||||
| #33996 | 7:10 PM | 🟣 | Built claude-mem v8.2.10 with all hooks and services | ~387 |
|
||||
| #33951 | 6:42 PM | ✅ | Project Build Completed for Version 8.2.9 | ~326 |
|
||||
| #33877 | 5:02 PM | 🟣 | Phase 1 build process successfully tested with worker source copying | ~372 |
|
||||
| #33839 | 4:24 PM | ✅ | Build Executed Successfully for Version 8.2.8 | ~313 |
|
||||
| #33833 | 4:17 PM | ✅ | Rebuilt Project With Early Signal Handler Registration | ~353 |
|
||||
| #33818 | 4:10 PM | ✅ | Project Rebuilt Successfully After PR 489 Review Fixes | ~338 |
|
||||
| #33802 | 3:55 PM | ✅ | Project Built Successfully for Version 8.2.7 | ~258 |
|
||||
|
||||
### Dec 30, 2025
|
||||
|
||||
**viewer-bundle.js**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34476 | 2:25 PM | ✅ | V2 Branch Builds Successfully Despite TypeScript Errors | ~316 |
|
||||
| #34451 | 2:20 PM | ✅ | Successful Build of Claude-Mem v8.5.1 Components | ~346 |
|
||||
|
||||
### Dec 31, 2025
|
||||
|
||||
**viewer-bundle.js**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34724 | 4:51 PM | ✅ | Built claude-mem v8.5.2 with issue 499 fix | ~287 |
|
||||
| #34710 | 4:48 PM | ✅ | Built and deployed claude-mem v8.5.1 to marketplace | ~371 |
|
||||
|
||||
### Jan 1, 2026
|
||||
|
||||
**viewer-bundle.js**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #35613 | 10:57 PM | ✅ | Build System Compiled Updated Middleware | ~320 |
|
||||
| #35427 | 6:32 PM | ✅ | Claude-Mem Project Built Successfully | ~319 |
|
||||
| #35397 | 5:23 PM | ✅ | Build System Successfully Compiled All Components | ~282 |
|
||||
| #35373 | 3:05 PM | ✅ | V2 Migration Build Successful | ~305 |
|
||||
|
||||
### Jan 2, 2026
|
||||
|
||||
**viewer-bundle.js**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #35982 | 5:09 PM | ✅ | Built and deployed claude-mem version 8.5.4 with LogsModal UI component | ~295 |
|
||||
| #35976 | 4:48 PM | ✅ | Claude-mem build and marketplace sync completed | ~335 |
|
||||
| #35925 | 2:53 PM | ✅ | Built Project for Version 8.5.4 Release | ~294 |
|
||||
| #35901 | 2:49 PM | 🔵 | PR #525 File Changes Summary | ~376 |
|
||||
| #35815 | 2:26 PM | ✅ | Claude-mem plugin built and deployed to marketplace | ~381 |
|
||||
| #35776 | 1:24 PM | ✅ | Successful Build of ChromaSync Bugfix | ~246 |
|
||||
|
||||
### Jan 3, 2026
|
||||
|
||||
**viewer-bundle.js**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #36687 | 11:53 PM | ✅ | MCP SDK and esbuild Dependencies Updated | ~332 |
|
||||
|
||||
### Jan 4, 2026
|
||||
|
||||
**viewer-bundle.js**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #36701 | 12:01 AM | ✅ | Built Version 8.5.7 Plugin Artifacts | ~406 |
|
||||
|
||||
### Jan 5, 2026
|
||||
|
||||
**viewer-bundle.js**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #38092 | 10:44 PM | ✅ | Built version 9.0.0 with complete plugin bundle | ~329 |
|
||||
| #37990 | 9:00 PM | 🔵 | CLAUDE_MEM_WORKER_HOST setting used across 19 files | ~289 |
|
||||
|
||||
**CLAUDE.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #38078 | 9:54 PM | ✅ | CLAUDE.md Documentation Cleanup - 1,233 Lines Removed Across 18 Files | ~590 |
|
||||
|
||||
### Jan 7, 2026
|
||||
|
||||
**viewer-bundle.js**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #38186 | 7:33 PM | 🟣 | Claude-mem plugin v9.0.0 built and deployed to marketplace | ~327 |
|
||||
| #38120 | 5:46 PM | 🔴 | Rebuilt all plugin hooks and worker service | ~278 |
|
||||
| #32983 | 11:04 PM | 🟣 | Complete build and deployment pipeline executed | ~260 |
|
||||
| #32965 | 10:53 PM | 🔵 | Found plugin/ui/viewer.html - potential styling source | ~201 |
|
||||
| #32966 | " | 🔵 | viewer.html contains modal CSS including modal-header and modal-body | ~218 |
|
||||
| #32967 | " | 🔵 | ContextSettingsModal.tsx uses CSS classes defined in viewer.html | ~218 |
|
||||
| #32968 | " | 🔵 | Need to add CSS for footer to viewer.html | ~223 |
|
||||
</claude-mem-context>
|
||||
+2
-44
@@ -12,14 +12,6 @@ import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const HOOKS = [
|
||||
{ name: 'context-hook', source: 'src/hooks/context-hook.ts' },
|
||||
{ name: 'new-hook', source: 'src/hooks/new-hook.ts' },
|
||||
{ name: 'save-hook', source: 'src/hooks/save-hook.ts' },
|
||||
{ name: 'summary-hook', source: 'src/hooks/summary-hook.ts' },
|
||||
{ name: 'user-message-hook', source: 'src/hooks/user-message-hook.ts' }
|
||||
];
|
||||
|
||||
const WORKER_SERVICE = {
|
||||
name: 'worker-service',
|
||||
source: 'src/services/worker-service.ts'
|
||||
@@ -159,45 +151,11 @@ async function buildHooks() {
|
||||
const contextGenStats = fs.statSync(`${hooksDir}/${CONTEXT_GENERATOR.name}.cjs`);
|
||||
console.log(`✓ context-generator built (${(contextGenStats.size / 1024).toFixed(2)} KB)`);
|
||||
|
||||
// Build each hook
|
||||
for (const hook of HOOKS) {
|
||||
console.log(`\n🔧 Building ${hook.name}...`);
|
||||
|
||||
const outfile = `${hooksDir}/${hook.name}.js`;
|
||||
|
||||
await build({
|
||||
entryPoints: [hook.source],
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
target: 'node18',
|
||||
format: 'esm',
|
||||
outfile,
|
||||
minify: true,
|
||||
external: ['bun:sqlite'],
|
||||
define: {
|
||||
'__DEFAULT_PACKAGE_VERSION__': `"${version}"`
|
||||
},
|
||||
banner: {
|
||||
js: '#!/usr/bin/env bun'
|
||||
}
|
||||
});
|
||||
|
||||
// Make executable
|
||||
fs.chmodSync(outfile, 0o755);
|
||||
|
||||
// Check file size
|
||||
const stats = fs.statSync(outfile);
|
||||
const sizeInKB = (stats.size / 1024).toFixed(2);
|
||||
console.log(`✓ ${hook.name} built (${sizeInKB} KB)`);
|
||||
}
|
||||
|
||||
console.log('\n✅ All hooks, worker service, and MCP server built successfully!');
|
||||
console.log('\n✅ Worker service, MCP server, and context generator built successfully!');
|
||||
console.log(` Output: ${hooksDir}/`);
|
||||
console.log(` - Hooks: *-hook.js`);
|
||||
console.log(` - Worker: worker-service.cjs`);
|
||||
console.log(` - MCP Server: mcp-server.cjs`);
|
||||
console.log('\n💡 Note: Dependencies will be auto-installed on first hook execution');
|
||||
console.log('📝 Cursor hooks are in cursor-hooks/ (no build needed - plain shell scripts)');
|
||||
console.log(` - Context Generator: context-generator.cjs`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n❌ Build failed:', error.message);
|
||||
|
||||
+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>;
|
||||
}
|
||||
+6
-1
@@ -25,7 +25,6 @@
|
||||
| #23075 | 6:26 PM | ✅ | Deleted Expired Announcement Code from user-message-hook.ts | ~354 |
|
||||
| #23074 | " | ✅ | Replaced Verbose Manual Mode Help with Error in cleanup-hook.ts | ~222 |
|
||||
| #23073 | " | ✅ | Removed cwd from cleanup-hook Debug Logging | ~177 |
|
||||
| #23072 | " | ✅ | Simplified SessionEndInput Interface in cleanup-hook.ts | ~236 |
|
||||
|
||||
### Dec 10, 2025
|
||||
|
||||
@@ -97,4 +96,10 @@
|
||||
| #38173 | 7:25 PM | 🔵 | Standard Hook Response Pattern for Non-SessionStart Hooks | ~343 |
|
||||
| #38172 | 7:22 PM | 🔵 | Claude Code Hook Output Architecture Clarified - Exit Code Pattern is Correct for User-Only Display | ~523 |
|
||||
| #38170 | 7:21 PM | 🔵 | User-Message-Hook TypeScript Source Shows Exit Code 1 Strategy for User-Only Display | ~203 |
|
||||
|
||||
### Jan 9, 2026
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #38783 | 4:59 PM | 🔵 | Standard Hook Response Pattern | ~263 |
|
||||
</claude-mem-context>
|
||||
@@ -1,76 +0,0 @@
|
||||
/**
|
||||
* Context Hook - SessionStart
|
||||
*
|
||||
* Pure HTTP client - calls worker to generate context.
|
||||
* This allows the hook to run under any runtime (Node.js or Bun) since it has no
|
||||
* native module dependencies.
|
||||
*/
|
||||
|
||||
import { stdin } from "process";
|
||||
import { ensureWorkerRunning, getWorkerPort } from "../shared/worker-utils.js";
|
||||
import { HOOK_TIMEOUTS } from "../shared/hook-constants.js";
|
||||
import { getProjectContext } from "../utils/project-name.js";
|
||||
import { logger } from "../utils/logger.js";
|
||||
|
||||
export interface SessionStartInput {
|
||||
session_id: string;
|
||||
transcript_path: string;
|
||||
cwd: string;
|
||||
hook_event_name?: string;
|
||||
}
|
||||
|
||||
async function contextHook(input?: SessionStartInput): Promise<string> {
|
||||
// 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();
|
||||
return result.trim();
|
||||
}
|
||||
|
||||
// Entry Point - handle stdin/stdout
|
||||
const forceColors = process.argv.includes("--colors");
|
||||
|
||||
if (stdin.isTTY || forceColors) {
|
||||
contextHook(undefined).then((text) => {
|
||||
console.log(text);
|
||||
process.exit(0);
|
||||
});
|
||||
} else {
|
||||
let input = "";
|
||||
stdin.on("data", (chunk) => (input += chunk));
|
||||
stdin.on("end", async () => {
|
||||
let parsed: SessionStartInput | undefined;
|
||||
try {
|
||||
parsed = input.trim() ? JSON.parse(input) : undefined;
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to parse hook input: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
const text = await contextHook(parsed);
|
||||
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
hookSpecificOutput: {
|
||||
hookEventName: "SessionStart",
|
||||
additionalContext: text,
|
||||
},
|
||||
})
|
||||
);
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
import { stdin } from 'process';
|
||||
import { STANDARD_HOOK_RESPONSE } from './hook-response.js';
|
||||
import { ensureWorkerRunning, getWorkerPort } from '../shared/worker-utils.js';
|
||||
import { getProjectName } from '../utils/project-name.js';
|
||||
import { logger } from '../utils/logger.js';
|
||||
|
||||
export interface UserPromptSubmitInput {
|
||||
session_id: string;
|
||||
cwd: string;
|
||||
prompt: string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* New Hook Main Logic
|
||||
*/
|
||||
async function newHook(input?: UserPromptSubmitInput): Promise<void> {
|
||||
// Ensure worker is running before any other logic
|
||||
await ensureWorkerRunning();
|
||||
|
||||
if (!input) {
|
||||
throw new Error('newHook requires input');
|
||||
}
|
||||
|
||||
const { session_id, cwd, prompt } = input;
|
||||
const project = getProjectName(cwd);
|
||||
const port = getWorkerPort();
|
||||
|
||||
logger.debug('HOOK', 'new-hook: Calling /api/sessions/init', { contentSessionId: session_id, 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: session_id,
|
||||
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();
|
||||
const sessionDbId = initResult.sessionDbId;
|
||||
const promptNumber = initResult.promptNumber;
|
||||
|
||||
logger.debug('HOOK', 'new-hook: Received from /api/sessions/init', { sessionDbId, promptNumber, skipped: initResult.skipped });
|
||||
|
||||
// Debug-level alignment log for detailed tracing
|
||||
logger.debug('HOOK', `[ALIGNMENT] Hook Entry | contentSessionId=${session_id} | 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
|
||||
});
|
||||
console.log(STANDARD_HOOK_RESPONSE);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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', 'new-hook: 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}`);
|
||||
}
|
||||
|
||||
logger.info('HOOK', `INIT_COMPLETE | sessionDbId=${sessionDbId} | promptNumber=${promptNumber} | project=${project}`, {
|
||||
sessionId: sessionDbId
|
||||
});
|
||||
|
||||
console.log(STANDARD_HOOK_RESPONSE);
|
||||
}
|
||||
|
||||
// Entry Point
|
||||
let input = '';
|
||||
stdin.on('data', (chunk) => input += chunk);
|
||||
stdin.on('end', async () => {
|
||||
try {
|
||||
let parsed: UserPromptSubmitInput | undefined;
|
||||
try {
|
||||
parsed = input ? JSON.parse(input) : undefined;
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to parse hook input: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
await newHook(parsed);
|
||||
} catch (error) {
|
||||
logger.error('HOOK', 'new-hook failed', {}, error as Error);
|
||||
} finally {
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
@@ -1,89 +0,0 @@
|
||||
/**
|
||||
* Save Hook - PostToolUse
|
||||
*
|
||||
* Pure HTTP client - sends data to worker, worker handles all database operations
|
||||
* including privacy checks. This allows the hook to run under any runtime
|
||||
* (Node.js or Bun) since it has no native module dependencies.
|
||||
*/
|
||||
|
||||
import { stdin } from 'process';
|
||||
import { STANDARD_HOOK_RESPONSE } from './hook-response.js';
|
||||
import { logger } from '../utils/logger.js';
|
||||
import { ensureWorkerRunning, getWorkerPort } from '../shared/worker-utils.js';
|
||||
import { HOOK_TIMEOUTS } from '../shared/hook-constants.js';
|
||||
|
||||
export interface PostToolUseInput {
|
||||
session_id: string;
|
||||
cwd: string;
|
||||
tool_name: string;
|
||||
tool_input: any;
|
||||
tool_response: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save Hook Main Logic - Fire-and-forget HTTP client
|
||||
*/
|
||||
async function saveHook(input?: PostToolUseInput): Promise<void> {
|
||||
// Ensure worker is running before any other logic
|
||||
await ensureWorkerRunning();
|
||||
|
||||
if (!input) {
|
||||
throw new Error('saveHook requires input');
|
||||
}
|
||||
|
||||
const { session_id, cwd, tool_name, tool_input, tool_response } = input;
|
||||
|
||||
const port = getWorkerPort();
|
||||
|
||||
const toolStr = logger.formatTool(tool_name, tool_input);
|
||||
|
||||
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 ${session_id}, tool ${tool_name}`);
|
||||
}
|
||||
|
||||
// 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: session_id,
|
||||
tool_name,
|
||||
tool_input,
|
||||
tool_response,
|
||||
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: tool_name });
|
||||
|
||||
console.log(STANDARD_HOOK_RESPONSE);
|
||||
}
|
||||
|
||||
// Entry Point
|
||||
let input = '';
|
||||
stdin.on('data', (chunk) => input += chunk);
|
||||
stdin.on('end', async () => {
|
||||
try {
|
||||
let parsed: PostToolUseInput | undefined;
|
||||
try {
|
||||
parsed = input ? JSON.parse(input) : undefined;
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to parse hook input: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
await saveHook(parsed);
|
||||
} catch (error) {
|
||||
logger.error('HOOK', 'save-hook failed', {}, error as Error);
|
||||
} finally {
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
@@ -1,93 +0,0 @@
|
||||
/**
|
||||
* Summary Hook - Stop
|
||||
*
|
||||
* Pure HTTP client - sends data to worker, worker handles all database operations
|
||||
* including privacy checks. This allows the hook to run under any runtime
|
||||
* (Node.js or Bun) since it has no native module dependencies.
|
||||
*
|
||||
* Transcript parsing stays in the hook because only the hook has access to
|
||||
* the transcript file path.
|
||||
*/
|
||||
|
||||
import { stdin } from 'process';
|
||||
import { STANDARD_HOOK_RESPONSE } from './hook-response.js';
|
||||
import { logger } from '../utils/logger.js';
|
||||
import { ensureWorkerRunning, getWorkerPort } from '../shared/worker-utils.js';
|
||||
import { HOOK_TIMEOUTS } from '../shared/hook-constants.js';
|
||||
import { extractLastMessage } from '../shared/transcript-parser.js';
|
||||
|
||||
export interface StopInput {
|
||||
session_id: string;
|
||||
cwd: string;
|
||||
transcript_path: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Summary Hook Main Logic - Fire-and-forget HTTP client
|
||||
*/
|
||||
async function summaryHook(input?: StopInput): Promise<void> {
|
||||
// Ensure worker is running before any other logic
|
||||
await ensureWorkerRunning();
|
||||
|
||||
if (!input) {
|
||||
throw new Error('summaryHook requires input');
|
||||
}
|
||||
|
||||
const { session_id } = input;
|
||||
|
||||
const port = getWorkerPort();
|
||||
|
||||
// Validate required fields before processing
|
||||
if (!input.transcript_path) {
|
||||
throw new Error(`Missing transcript_path in Stop hook input for session ${session_id}`);
|
||||
}
|
||||
|
||||
// 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(input.transcript_path, '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: session_id,
|
||||
last_assistant_message: lastAssistantMessage
|
||||
})
|
||||
// Note: Removed signal to avoid Windows Bun cleanup issue (libuv assertion)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.log(STANDARD_HOOK_RESPONSE);
|
||||
throw new Error(`Summary generation failed: ${response.status}`);
|
||||
}
|
||||
|
||||
logger.debug('HOOK', 'Summary request sent successfully');
|
||||
|
||||
console.log(STANDARD_HOOK_RESPONSE);
|
||||
}
|
||||
|
||||
// Entry Point
|
||||
let input = '';
|
||||
stdin.on('data', (chunk) => input += chunk);
|
||||
stdin.on('end', async () => {
|
||||
try {
|
||||
let parsed: StopInput | undefined;
|
||||
try {
|
||||
parsed = input ? JSON.parse(input) : undefined;
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to parse hook input: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
await summaryHook(parsed);
|
||||
} catch (error) {
|
||||
logger.error('HOOK', 'summary-hook failed', {}, error as Error);
|
||||
} finally {
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
@@ -1,47 +0,0 @@
|
||||
/**
|
||||
* User Message Hook - SessionStart
|
||||
*
|
||||
* @deprecated This hook is no longer used as of Claude Code 2.1.0 (ultrathink update).
|
||||
* SessionStart hooks no longer display any user-visible messages in the Claude Code UI.
|
||||
* Context is still injected via hookSpecificOutput.additionalContext in context-hook.ts,
|
||||
* but users don't see any startup output.
|
||||
*
|
||||
* This file is kept for reference but is not registered in hooks.json.
|
||||
*
|
||||
* Historical behavior:
|
||||
* - Displayed context information to the user via stderr
|
||||
* - Ran in parallel with context-hook to show users what context was loaded
|
||||
* - Used stderr + exit code 1 to display to user only without adding to Claude's context
|
||||
*/
|
||||
import { basename } from "path";
|
||||
import { ensureWorkerRunning, getWorkerPort } from "../shared/worker-utils.js";
|
||||
|
||||
// Ensure worker is running
|
||||
await ensureWorkerRunning();
|
||||
|
||||
const port = getWorkerPort();
|
||||
const project = basename(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();
|
||||
|
||||
console.error(
|
||||
"\n\n📝 Claude-Mem Context Loaded\n" +
|
||||
" ℹ️ Note: This appears as stderr but is informational only\n\n" +
|
||||
output +
|
||||
"\n\n💡 New! Wrap all or part of any message with <private> ... </private> to prevent storing sensitive information in your observation history.\n" +
|
||||
"\n💬 Community https://discord.gg/J4wttp9vDu" +
|
||||
`\n📺 Watch live in browser http://localhost:${port}/\n`
|
||||
);
|
||||
|
||||
process.exit(1); // Exit code 1 for SessionStart = show stderr to user only
|
||||
+94
-1
@@ -3,5 +3,98 @@
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
*No recent activity*
|
||||
### Nov 6, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #4185 | 10:25 PM | 🔴 | Prefixed unused id parameters with underscore in filter callbacks | ~299 |
|
||||
|
||||
### Nov 8, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #5539 | 10:20 PM | 🔵 | Harsh critical audit of context-hook reveals systematic anti-patterns | ~3154 |
|
||||
| #5497 | 9:29 PM | 🔵 | Harsh critical audit of context-hook reveals systematic anti-patterns | ~2815 |
|
||||
|
||||
### Nov 9, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #5757 | 5:16 PM | 🔵 | MCP search server exposes 9 tools consuming ~2,000-3,000 tokens per session | ~421 |
|
||||
| #5754 | 5:14 PM | 🔵 | MCP search server provides 9 search tools with hybrid semantic/FTS5 | ~402 |
|
||||
|
||||
### Nov 10, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #6250 | 12:54 PM | 🔵 | MCP Search Server Connection Failure Reported | ~329 |
|
||||
|
||||
### Nov 17, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #10744 | 11:47 PM | ✅ | Search Query Parameter Made Optional for Filter-Only Queries | ~373 |
|
||||
| #10572 | 7:47 PM | 🟣 | Unified cross-type search with search_everything tool | ~501 |
|
||||
| #10571 | 7:46 PM | 🔵 | Search server architecture and hybrid search implementation | ~553 |
|
||||
|
||||
### Nov 18, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #11462 | 7:55 PM | 🔵 | Ready to Apply Fix to Contextualize Handler | ~261 |
|
||||
| #11460 | " | 🔴 | Identified Root Cause of Contextualize Endpoint Bug | ~413 |
|
||||
| #11454 | 7:54 PM | 🔵 | Unified Search Handler Shows Correct Pattern for Filter-Only Queries | ~334 |
|
||||
| #11447 | " | 🔵 | Contextualize Handler Calls Search Methods with Query='*' | ~279 |
|
||||
| #11432 | 7:52 PM | 🔵 | Contextualize Handler Formats Results with Sections | ~286 |
|
||||
| #11431 | 7:51 PM | 🔵 | Confirmed Empty Results Trigger in Contextualize Handler | ~289 |
|
||||
| #11430 | " | 🔵 | Contextualize Handler Implementation Uses Search Methods | ~424 |
|
||||
| #11429 | " | 🔵 | Search Server Defines Six Main Search Tools | ~358 |
|
||||
| #11428 | " | 🔵 | Contextualize Tool Definition Found in Search Server | ~357 |
|
||||
| #11332 | 3:55 PM | 🔵 | Comprehensive FTS5 Removal Audit Completed for Architecture Migration | ~792 |
|
||||
| #11206 | 3:01 PM | 🔵 | mem-search skill architecture and migration details retrieved in full format | ~538 |
|
||||
| #11181 | 4:09 AM | 🔵 | Store methods for ID-based lookups exist but not exposed as MCP tools | ~495 |
|
||||
| #11013 | 2:12 AM | 🔵 | Search Server Implements Three-Path Query Strategy with ChromaDB Primary and FTS5 Fallback | ~462 |
|
||||
|
||||
### Nov 28, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #16711 | 4:34 PM | 🟣 | include_inactive Parameter Extracted in Search Handler | ~369 |
|
||||
| #16710 | " | 🔵 | Search Tool Schema Definition with Type and Filter Parameters | ~527 |
|
||||
| #16708 | " | 🔵 | Search Server MCP Tool Architecture and ChromaDB Integration | ~491 |
|
||||
| #16682 | 4:10 PM | 🔵 | Comprehensive Exploration Task Completed on Observation System | ~601 |
|
||||
|
||||
### Dec 14, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #26238 | 8:28 PM | 🔵 | MCP Server Architecture Maps Tools to Worker API Endpoints | ~355 |
|
||||
| #26138 | 7:55 PM | ✅ | Updated Comment to Reference progressive_description Tool | ~238 |
|
||||
| #26137 | " | ✅ | Completed Tool Description Minimization - All 9 Tools Updated | ~335 |
|
||||
| #26136 | " | ✅ | Minimized Get Session Tool Description | ~218 |
|
||||
| #26135 | " | ✅ | Minimized Get Batch Observations Tool Description | ~258 |
|
||||
| #26134 | " | ✅ | Minimized Get Observation Tool Description | ~228 |
|
||||
| #26133 | " | ✅ | Minimized Get Context Timeline Tool Description | ~245 |
|
||||
| #26132 | 7:54 PM | ✅ | Minimized Get Recent Context Tool Description | ~214 |
|
||||
| #26131 | " | ✅ | Minimized Timeline Tool Description | ~232 |
|
||||
| #26130 | " | ✅ | Minimized Search Tool Description | ~235 |
|
||||
| #26129 | " | ✅ | Renamed progressive_ix Tool to progressive_description with Minimized Description | ~296 |
|
||||
| #26128 | " | ✅ | Renamed Tool Endpoint Mapping from progressive_ix to progressive_description | ~229 |
|
||||
| #26127 | " | ✅ | Completed Format Parameter Removal from All Four MCP Tools | ~318 |
|
||||
| #26126 | 7:53 PM | ✅ | Removed Format Parameter from Get Recent Context Tool Schema | ~244 |
|
||||
| #26125 | " | ✅ | Removed Format Parameter from Timeline Tool Schema | ~248 |
|
||||
| #26124 | " | ✅ | Removed Format Parameter from Search Tool Schema | ~283 |
|
||||
| #26123 | " | 🔵 | Current MCP Server Tool Schema Analysis | ~337 |
|
||||
| #25815 | 5:31 PM | 🔵 | Comprehensive MCP Server and SKILL.md Structure Analysis | ~575 |
|
||||
| #25807 | 5:30 PM | 🔵 | MCP Server Architecture with 14 HTTP-Delegating Tools | ~545 |
|
||||
| #25788 | 5:15 PM | 🔵 | MCP Server Capabilities and Request Handlers | ~256 |
|
||||
|
||||
### Dec 17, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #29078 | 10:16 PM | ✅ | Updated get_recent_context tool schema to accept dynamic parameters | ~318 |
|
||||
| #29077 | 10:15 PM | ✅ | Updated timeline tool schema to accept dynamic parameters | ~292 |
|
||||
| #29076 | " | ✅ | Updated search tool schema to accept dynamic parameters | ~315 |
|
||||
| #28923 | 7:28 PM | 🔵 | MCP Server Architecture: Thin HTTP Wrapper Pattern | ~402 |
|
||||
</claude-mem-context>
|
||||
@@ -310,5 +310,7 @@ async function main() {
|
||||
|
||||
main().catch((error) => {
|
||||
logger.error('SYSTEM', 'Fatal error', undefined, error);
|
||||
process.exit(1);
|
||||
// Exit gracefully: Windows Terminal won't keep tab open on exit 0
|
||||
// The wrapper/plugin will handle restart logic if needed
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
+50
-25
@@ -3,36 +3,61 @@
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Jan 5, 2026
|
||||
### Dec 10, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #37989 | 8:39 PM | 🔵 | Worker Service CLI Commands | ~370 |
|
||||
| #37988 | " | 🔵 | Worker Service Hook Command Integration | ~356 |
|
||||
| #37943 | 8:09 PM | 🔵 | Worker service provides hook command interface | ~372 |
|
||||
| #37936 | 8:08 PM | 🔵 | Worker service module located in budapest architecture | ~173 |
|
||||
| #37837 | 6:55 PM | 🔵 | Worker Service CLI Structure and Extension Pattern | ~536 |
|
||||
| #37836 | 6:54 PM | 🔵 | Worker Service HTTP API Architecture and Hook Integration Pattern | ~639 |
|
||||
| #37830 | 6:50 PM | ⚖️ | Architectural Refactoring Plan: Unified CLI Hook System | ~631 |
|
||||
| #37812 | 6:43 PM | 🔵 | WorkerService Architecture and CLI Entry Points | ~447 |
|
||||
| #37738 | 6:17 PM | 🔵 | ActiveSession and PendingMessage Structures Analyzed for Phase 3 | ~327 |
|
||||
| #37701 | 6:01 PM | 🔵 | Complete cwd data flow traced from hooks through observation processing | ~447 |
|
||||
| #37691 | 5:55 PM | 🔵 | ActiveSession interface contains project name but no project root path | ~346 |
|
||||
| #37418 | 1:04 AM | 🔴 | Fixed CLAUDE.md duplicate content by filtering to direct children only | ~469 |
|
||||
| #37391 | 12:48 AM | ✅ | Staged 23 CLAUDE.md files with mix of new and modified content | ~400 |
|
||||
| #37390 | 12:47 AM | ✅ | Regenerated 23 CLAUDE.md files in budapest workspace | ~365 |
|
||||
| #23832 | 11:15 PM | 🔵 | Current worker-service.ts Lacks Admin Endpoints | ~393 |
|
||||
|
||||
### Jan 7, 2026
|
||||
### Dec 14, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #38467 | 10:29 PM | ⚖️ | Log Level Audit Strategy: Tighten ERROR Messages for Runtime Issue Discovery | ~464 |
|
||||
| #38446 | 10:26 PM | 🔵 | Worker Service Logs Startup Recovery Failures at WARN Level | ~419 |
|
||||
| #38444 | 10:25 PM | 🔵 | Worker service auto-recovery error handling pattern | ~393 |
|
||||
| #38431 | 10:24 PM | 🔵 | ERROR-Level Logging Used Across 21 Source Files | ~480 |
|
||||
| #38404 | 10:06 PM | ⚖️ | Log Level Audit Analysis - WARN to ERROR Promotion Criteria Established | ~769 |
|
||||
| #38354 | 9:10 PM | 🔵 | Provider Selection Architecture and Default Settings Location | ~478 |
|
||||
| #38352 | 9:09 PM | 🔵 | Worker Service Environment Inheritance and Process Spawning Mechanisms | ~469 |
|
||||
| #38269 | 7:53 PM | 🔵 | Worker Types Shared Type Definitions | ~847 |
|
||||
| #38256 | 7:49 PM | 🔵 | Worker Service Orchestration Architecture | ~1166 |
|
||||
| #26740 | 11:26 PM | 🔵 | Worker Service Refactored to Orchestrator with Background Initialization | ~421 |
|
||||
| #26739 | 11:25 PM | 🔵 | Worker Service Architecture Uses Domain Services and Background Initialization | ~438 |
|
||||
| #26255 | 8:31 PM | 🔵 | Context Generator Timeline Rendering Logic Details File Grouping Implementation | ~397 |
|
||||
| #26251 | 8:30 PM | 🔵 | Worker Service Orchestrates Domain Services and Route Handlers | ~292 |
|
||||
| #26246 | 8:29 PM | 🔵 | Context Generator Implements Rich Date-Grouped Timeline Format | ~468 |
|
||||
|
||||
### Dec 17, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #28548 | 4:49 PM | 🔵 | Worker service cleanup method uses Unix-specific process management | ~323 |
|
||||
| #28446 | 4:23 PM | 🔵 | Worker Service Refactored to Orchestrator Pattern | ~529 |
|
||||
|
||||
### Dec 18, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #29340 | 3:11 PM | ✅ | Constructor Initialization Comment Updated | ~267 |
|
||||
| #29339 | " | ✅ | Class Member Comment Updated in WorkerService | ~267 |
|
||||
| #29338 | " | ✅ | Service Import Comment Updated | ~222 |
|
||||
| #29337 | 3:10 PM | ✅ | Terminology Update in Worker Service Documentation | ~268 |
|
||||
| #29239 | 12:11 AM | 🔵 | Worker Service Refactored as Domain-Driven Orchestrator | ~477 |
|
||||
|
||||
### Dec 20, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #30808 | 6:05 PM | 🔴 | Fixed worker readiness check to fail on initialization errors | ~315 |
|
||||
| #30800 | 6:03 PM | 🔵 | Dual Error Logging in Background Initialization | ~367 |
|
||||
| #30799 | " | 🔵 | Background Initialization Invocation Pattern | ~365 |
|
||||
| #30797 | " | 🔵 | Background Initialization Sequence and Error Handler Confirmed | ~450 |
|
||||
| #30795 | 6:02 PM | 🔵 | Readiness Endpoint Returns 503 During Initialization | ~397 |
|
||||
| #30793 | " | 🔵 | Dual Initialization State Tracking Pattern | ~388 |
|
||||
| #30791 | " | 🔵 | Worker Service Constructor Defers SearchRoutes Initialization | ~387 |
|
||||
| #30790 | " | 🔵 | Initialization Promise Resolver Pattern Located | ~321 |
|
||||
| #30788 | " | 🔵 | Worker Service Initialization Resolves Promise Despite Errors | ~388 |
|
||||
|
||||
### Jan 1, 2026
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #35654 | 11:29 PM | ✅ | Added APPROVED OVERRIDE annotation for instruction loading HTTP route error handler | ~339 |
|
||||
| #35651 | 11:28 PM | ✅ | Added APPROVED OVERRIDE annotation for shutdown error handler with process.exit | ~354 |
|
||||
| #35649 | " | ✅ | Added APPROVED OVERRIDE annotation for readiness check retry loop error handling | ~374 |
|
||||
| #35647 | " | ✅ | Added APPROVED OVERRIDE annotation for port availability probe error handling | ~327 |
|
||||
| #35646 | " | ✅ | Added APPROVED OVERRIDE annotation for Cursor context file update error handling | ~342 |
|
||||
| #35643 | 11:27 PM | ✅ | Added APPROVED OVERRIDE annotation for PID file cleanup error handling | ~320 |
|
||||
</claude-mem-context>
|
||||
@@ -3,5 +3,10 @@
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
*No recent activity*
|
||||
### Jan 4, 2026
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #36864 | 1:52 AM | 🔵 | ProcessManager Module Imports Reviewed | ~245 |
|
||||
| #36860 | 1:50 AM | 🔵 | ProcessManager Source Code Reviewed for WMIC Implementation | ~608 |
|
||||
</claude-mem-context>
|
||||
@@ -14,6 +14,7 @@ import { existsSync, writeFileSync, readFileSync, unlinkSync, mkdirSync } from '
|
||||
import { exec, execSync, spawn } from 'child_process';
|
||||
import { promisify } from 'util';
|
||||
import { logger } from '../../utils/logger.js';
|
||||
import { HOOK_TIMEOUTS } from '../../shared/hook-constants.js';
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
@@ -88,16 +89,16 @@ export async function getChildProcesses(parentPid: number): Promise<number[]> {
|
||||
}
|
||||
|
||||
try {
|
||||
const cmd = `wmic process where "parentprocessid=${parentPid}" get processid /format:list`;
|
||||
const { stdout } = await execAsync(cmd, { timeout: 60000 });
|
||||
// PowerShell Get-Process instead of WMIC (deprecated in Windows 11)
|
||||
const cmd = `powershell -NoProfile -NonInteractive -Command "Get-Process | Where-Object { \\$_.ParentProcessId -eq ${parentPid} } | Select-Object -ExpandProperty Id"`;
|
||||
const { stdout } = await execAsync(cmd, { timeout: HOOK_TIMEOUTS.POWERSHELL_COMMAND });
|
||||
// PowerShell outputs just numbers (one per line), simpler than WMIC's "ProcessId=1234" format
|
||||
return stdout
|
||||
.trim()
|
||||
.split('\n')
|
||||
.map(line => {
|
||||
const match = line.match(/ProcessId=(\d+)/i);
|
||||
return match ? parseInt(match[1], 10) : NaN;
|
||||
})
|
||||
.filter(n => !isNaN(n) && Number.isInteger(n) && n > 0);
|
||||
.map(line => line.trim())
|
||||
.filter(line => line.length > 0 && /^\d+$/.test(line))
|
||||
.map(line => parseInt(line, 10))
|
||||
.filter(pid => pid > 0);
|
||||
} catch (error) {
|
||||
// Shutdown cleanup - failure is non-critical, continue without child process cleanup
|
||||
logger.error('SYSTEM', 'Failed to enumerate child processes', { parentPid }, error as Error);
|
||||
@@ -120,7 +121,7 @@ export async function forceKillProcess(pid: number): Promise<void> {
|
||||
try {
|
||||
if (process.platform === 'win32') {
|
||||
// /T kills entire process tree, /F forces termination
|
||||
await execAsync(`taskkill /PID ${pid} /T /F`, { timeout: 60000 });
|
||||
await execAsync(`taskkill /PID ${pid} /T /F`, { timeout: HOOK_TIMEOUTS.POWERSHELL_COMMAND });
|
||||
} else {
|
||||
process.kill(pid, 'SIGKILL');
|
||||
}
|
||||
@@ -170,24 +171,26 @@ export async function cleanupOrphanedProcesses(): Promise<void> {
|
||||
|
||||
try {
|
||||
if (isWindows) {
|
||||
// Windows: Use WMIC to find chroma-mcp processes (avoids PowerShell $_ issues in Git Bash/WSL)
|
||||
const cmd = `wmic process where "name like '%python%' and commandline like '%chroma-mcp%'" get processid /format:list`;
|
||||
const { stdout } = await execAsync(cmd, { timeout: 60000 });
|
||||
// Windows: Use PowerShell Get-CimInstance instead of WMIC (deprecated in Windows 11)
|
||||
const cmd = `powershell -NoProfile -NonInteractive -Command "Get-CimInstance Win32_Process | Where-Object { \\$_.Name -like '*python*' -and \\$_.CommandLine -like '*chroma-mcp*' } | Select-Object -ExpandProperty ProcessId"`;
|
||||
const { stdout } = await execAsync(cmd, { timeout: HOOK_TIMEOUTS.POWERSHELL_COMMAND });
|
||||
|
||||
if (!stdout.trim()) {
|
||||
logger.debug('SYSTEM', 'No orphaned chroma-mcp processes found (Windows)');
|
||||
return;
|
||||
}
|
||||
|
||||
const lines = stdout.trim().split('\n');
|
||||
// PowerShell outputs just numbers (one per line), simpler than WMIC's "ProcessId=1234" format
|
||||
const lines = stdout
|
||||
.split('\n')
|
||||
.map(line => line.trim())
|
||||
.filter(line => line.length > 0 && /^\d+$/.test(line));
|
||||
|
||||
for (const line of lines) {
|
||||
const match = line.match(/ProcessId=(\d+)/i);
|
||||
if (match) {
|
||||
const pid = parseInt(match[1], 10);
|
||||
// SECURITY: Validate PID is positive integer before adding to list
|
||||
if (!isNaN(pid) && Number.isInteger(pid) && pid > 0) {
|
||||
pids.push(pid);
|
||||
}
|
||||
const pid = parseInt(line, 10);
|
||||
// SECURITY: Validate PID is positive integer before adding to list
|
||||
if (!isNaN(pid) && Number.isInteger(pid) && pid > 0) {
|
||||
pids.push(pid);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -236,7 +239,7 @@ export async function cleanupOrphanedProcesses(): Promise<void> {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
execSync(`taskkill /PID ${pid} /T /F`, { timeout: 60000, stdio: 'ignore' });
|
||||
execSync(`taskkill /PID ${pid} /T /F`, { timeout: HOOK_TIMEOUTS.POWERSHELL_COMMAND, stdio: 'ignore' });
|
||||
} catch (error) {
|
||||
// [ANTI-PATTERN IGNORED]: Cleanup loop - process may have exited, continue to next PID
|
||||
logger.debug('SYSTEM', 'Failed to kill process, may have already exited', { pid }, error as Error);
|
||||
@@ -306,7 +309,9 @@ export function createSignalHandler(
|
||||
} catch (error) {
|
||||
// Top-level signal handler - log any shutdown error and exit
|
||||
logger.error('SYSTEM', 'Error during shutdown', {}, error as Error);
|
||||
process.exit(1);
|
||||
// Exit gracefully: Windows Terminal won't keep tab open on exit 0
|
||||
// Even on shutdown errors, exit cleanly to prevent tab accumulation
|
||||
process.exit(0);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -171,6 +171,28 @@ export function findMcpServerPath(): string | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find worker-service.cjs path for unified CLI
|
||||
* Searches in order: marketplace install, source repo
|
||||
*/
|
||||
export function findWorkerServicePath(): string | null {
|
||||
const possiblePaths = [
|
||||
// Marketplace install location
|
||||
path.join(homedir(), '.claude', 'plugins', 'marketplaces', 'thedotmack', 'plugin', 'scripts', 'worker-service.cjs'),
|
||||
// Development/source location (relative to built worker-service.cjs in plugin/scripts/)
|
||||
path.join(path.dirname(__filename), 'worker-service.cjs'),
|
||||
// Alternative dev location
|
||||
path.join(process.cwd(), 'plugin', 'scripts', 'worker-service.cjs'),
|
||||
];
|
||||
|
||||
for (const p of possiblePaths) {
|
||||
if (existsSync(p)) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the target directory for Cursor hooks based on install target
|
||||
*/
|
||||
@@ -261,13 +283,11 @@ export function configureCursorMcp(target: CursorInstallTarget): number {
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Install Cursor hooks
|
||||
* Install Cursor hooks using unified CLI
|
||||
* No longer copies shell scripts - uses node CLI directly
|
||||
*/
|
||||
export async function installCursorHooks(sourceDir: string, target: CursorInstallTarget): Promise<number> {
|
||||
const platform = detectPlatform();
|
||||
const scriptExt = getScriptExtension();
|
||||
|
||||
console.log(`\nInstalling Claude-Mem Cursor hooks (${target} level, ${platform})...\n`);
|
||||
export async function installCursorHooks(_sourceDir: string, target: CursorInstallTarget): Promise<number> {
|
||||
console.log(`\nInstalling Claude-Mem Cursor hooks (${target} level)...\n`);
|
||||
|
||||
const targetDir = getTargetDir(target);
|
||||
if (!targetDir) {
|
||||
@@ -275,52 +295,30 @@ export async function installCursorHooks(sourceDir: string, target: CursorInstal
|
||||
return 1;
|
||||
}
|
||||
|
||||
const hooksDir = path.join(targetDir, 'hooks');
|
||||
// Find the worker-service.cjs path
|
||||
const workerServicePath = findWorkerServicePath();
|
||||
if (!workerServicePath) {
|
||||
console.error('Could not find worker-service.cjs');
|
||||
console.error(' Expected at: ~/.claude/plugins/marketplaces/thedotmack/plugin/scripts/worker-service.cjs');
|
||||
return 1;
|
||||
}
|
||||
|
||||
const workspaceRoot = process.cwd();
|
||||
|
||||
try {
|
||||
// Create directories
|
||||
mkdirSync(hooksDir, { recursive: true });
|
||||
// Create target directory
|
||||
mkdirSync(targetDir, { recursive: true });
|
||||
|
||||
// Determine which scripts to copy based on platform
|
||||
const commonScript = platform === 'windows' ? 'common.ps1' : 'common.sh';
|
||||
const hookScripts = [
|
||||
`session-init${scriptExt}`,
|
||||
`context-inject${scriptExt}`,
|
||||
`save-observation${scriptExt}`,
|
||||
`save-file-edit${scriptExt}`,
|
||||
`session-summary${scriptExt}`
|
||||
];
|
||||
|
||||
const scripts = [commonScript, ...hookScripts];
|
||||
|
||||
for (const script of scripts) {
|
||||
const srcPath = path.join(sourceDir, script);
|
||||
const dstPath = path.join(hooksDir, script);
|
||||
|
||||
if (existsSync(srcPath)) {
|
||||
const content = readFileSync(srcPath, 'utf-8');
|
||||
// Unix scripts need execute permission; Windows PowerShell doesn't need it
|
||||
const mode = platform === 'windows' ? undefined : 0o755;
|
||||
writeFileSync(dstPath, content, mode ? { mode } : undefined);
|
||||
console.log(` Copied ${script}`);
|
||||
} else {
|
||||
console.warn(` ${script} not found in source`);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate hooks.json with correct paths and platform-appropriate commands
|
||||
// Generate hooks.json with unified CLI commands
|
||||
const hooksJsonPath = path.join(targetDir, 'hooks.json');
|
||||
const hookPrefix = target === 'project' ? './.cursor/hooks/' : `${hooksDir}/`;
|
||||
|
||||
// For PowerShell, we need to invoke via powershell.exe
|
||||
const makeHookCommand = (scriptName: string) => {
|
||||
const scriptPath = `${hookPrefix}${scriptName}${scriptExt}`;
|
||||
if (platform === 'windows') {
|
||||
// PowerShell execution: use -ExecutionPolicy Bypass to ensure scripts run
|
||||
return `powershell.exe -ExecutionPolicy Bypass -File "${scriptPath}"`;
|
||||
}
|
||||
return scriptPath;
|
||||
// Use the absolute path to worker-service.cjs
|
||||
// Escape backslashes for JSON on Windows
|
||||
const escapedWorkerPath = workerServicePath.replace(/\\/g, '\\\\');
|
||||
|
||||
// Helper to create hook command using unified CLI
|
||||
const makeHookCommand = (command: string) => {
|
||||
return `node "${escapedWorkerPath}" hook cursor ${command}`;
|
||||
};
|
||||
|
||||
const hooksJson: CursorHooksJson = {
|
||||
@@ -328,25 +326,26 @@ export async function installCursorHooks(sourceDir: string, target: CursorInstal
|
||||
hooks: {
|
||||
beforeSubmitPrompt: [
|
||||
{ command: makeHookCommand('session-init') },
|
||||
{ command: makeHookCommand('context-inject') }
|
||||
{ command: makeHookCommand('context') }
|
||||
],
|
||||
afterMCPExecution: [
|
||||
{ command: makeHookCommand('save-observation') }
|
||||
{ command: makeHookCommand('observation') }
|
||||
],
|
||||
afterShellExecution: [
|
||||
{ command: makeHookCommand('save-observation') }
|
||||
{ command: makeHookCommand('observation') }
|
||||
],
|
||||
afterFileEdit: [
|
||||
{ command: makeHookCommand('save-file-edit') }
|
||||
{ command: makeHookCommand('file-edit') }
|
||||
],
|
||||
stop: [
|
||||
{ command: makeHookCommand('session-summary') }
|
||||
{ command: makeHookCommand('summarize') }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
writeFileSync(hooksJsonPath, JSON.stringify(hooksJson, null, 2));
|
||||
console.log(` Created hooks.json (${platform} mode)`);
|
||||
console.log(` Created hooks.json (unified CLI mode)`);
|
||||
console.log(` Worker service: ${workerServicePath}`);
|
||||
|
||||
// For project-level: create initial context file
|
||||
if (target === 'project') {
|
||||
@@ -357,7 +356,7 @@ export async function installCursorHooks(sourceDir: string, target: CursorInstal
|
||||
Installation complete!
|
||||
|
||||
Hooks installed to: ${targetDir}/hooks.json
|
||||
Scripts installed to: ${hooksDir}
|
||||
Using unified CLI: node worker-service.cjs hook cursor <command>
|
||||
|
||||
Next steps:
|
||||
1. Start claude-mem worker: claude-mem start
|
||||
@@ -453,7 +452,7 @@ export function uninstallCursorHooks(target: CursorInstallTarget): number {
|
||||
const hooksDir = path.join(targetDir, 'hooks');
|
||||
const hooksJsonPath = path.join(targetDir, 'hooks.json');
|
||||
|
||||
// Remove hook scripts for both platforms (in case user switches platforms)
|
||||
// Remove legacy shell scripts if they exist (from old installations)
|
||||
const bashScripts = ['common.sh', 'session-init.sh', 'context-inject.sh',
|
||||
'save-observation.sh', 'save-file-edit.sh', 'session-summary.sh'];
|
||||
const psScripts = ['common.ps1', 'session-init.ps1', 'context-inject.ps1',
|
||||
@@ -465,7 +464,7 @@ export function uninstallCursorHooks(target: CursorInstallTarget): number {
|
||||
const scriptPath = path.join(hooksDir, script);
|
||||
if (existsSync(scriptPath)) {
|
||||
unlinkSync(scriptPath);
|
||||
console.log(` Removed ${script}`);
|
||||
console.log(` Removed legacy script: ${script}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -527,32 +526,36 @@ export function checkCursorHooksStatus(): number {
|
||||
console.log(`${loc.name}: Installed`);
|
||||
console.log(` Config: ${hooksJson}`);
|
||||
|
||||
// Detect which platform's scripts are installed
|
||||
const bashScripts = ['session-init.sh', 'context-inject.sh', 'save-observation.sh'];
|
||||
const psScripts = ['session-init.ps1', 'context-inject.ps1', 'save-observation.ps1'];
|
||||
// Check if using unified CLI mode or legacy shell scripts
|
||||
try {
|
||||
const hooksContent = JSON.parse(readFileSync(hooksJson, 'utf-8'));
|
||||
const firstCommand = hooksContent?.hooks?.beforeSubmitPrompt?.[0]?.command || '';
|
||||
|
||||
const hasBash = bashScripts.some(s => existsSync(path.join(hooksDir, s)));
|
||||
const hasPs = psScripts.some(s => existsSync(path.join(hooksDir, s)));
|
||||
if (firstCommand.includes('worker-service.cjs') && firstCommand.includes('hook cursor')) {
|
||||
console.log(` Mode: Unified CLI (node worker-service.cjs)`);
|
||||
} else {
|
||||
// Detect legacy shell scripts
|
||||
const bashScripts = ['session-init.sh', 'context-inject.sh', 'save-observation.sh'];
|
||||
const psScripts = ['session-init.ps1', 'context-inject.ps1', 'save-observation.ps1'];
|
||||
|
||||
if (hasBash && hasPs) {
|
||||
console.log(` Platform: Both (bash + PowerShell)`);
|
||||
} else if (hasBash) {
|
||||
console.log(` Platform: Unix (bash)`);
|
||||
} else if (hasPs) {
|
||||
console.log(` Platform: Windows (PowerShell)`);
|
||||
} else {
|
||||
console.log(` No hook scripts found`);
|
||||
}
|
||||
const hasBash = bashScripts.some(s => existsSync(path.join(hooksDir, s)));
|
||||
const hasPs = psScripts.some(s => existsSync(path.join(hooksDir, s)));
|
||||
|
||||
// Check for appropriate scripts based on current platform
|
||||
const platform = detectPlatform();
|
||||
const scripts = platform === 'windows' ? psScripts : bashScripts;
|
||||
const missing = scripts.filter(s => !existsSync(path.join(hooksDir, s)));
|
||||
|
||||
if (missing.length > 0) {
|
||||
console.log(` Missing ${platform} scripts: ${missing.join(', ')}`);
|
||||
} else {
|
||||
console.log(` Scripts: All present for ${platform}`);
|
||||
if (hasBash || hasPs) {
|
||||
console.log(` Mode: Legacy shell scripts (consider reinstalling for unified CLI)`);
|
||||
if (hasBash && hasPs) {
|
||||
console.log(` Platform: Both (bash + PowerShell)`);
|
||||
} else if (hasBash) {
|
||||
console.log(` Platform: Unix (bash)`);
|
||||
} else if (hasPs) {
|
||||
console.log(` Platform: Windows (PowerShell)`);
|
||||
}
|
||||
} else {
|
||||
console.log(` Mode: Unknown configuration`);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
console.log(` Mode: Unable to parse hooks.json`);
|
||||
}
|
||||
|
||||
// Check for context file (project only)
|
||||
|
||||
@@ -77,6 +77,30 @@ import { LogsRoutes } from './worker/http/routes/LogsRoutes.js';
|
||||
// Re-export updateCursorContextForProject for SDK agents
|
||||
export { updateCursorContextForProject };
|
||||
|
||||
/**
|
||||
* Build JSON status output for hook framework communication.
|
||||
* This is a pure function extracted for testability.
|
||||
*
|
||||
* @param status - 'ready' for successful startup, 'error' for failures
|
||||
* @param message - Optional error message (only included when provided)
|
||||
* @returns JSON object with continue, suppressOutput, status, and optionally message
|
||||
*/
|
||||
export interface StatusOutput {
|
||||
continue: true;
|
||||
suppressOutput: true;
|
||||
status: 'ready' | 'error';
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export function buildStatusOutput(status: 'ready' | 'error', message?: string): StatusOutput {
|
||||
return {
|
||||
continue: true,
|
||||
suppressOutput: true,
|
||||
status,
|
||||
...(message && { message })
|
||||
};
|
||||
}
|
||||
|
||||
export class WorkerService {
|
||||
private server: Server;
|
||||
private startTime: number = Date.now();
|
||||
@@ -622,6 +646,14 @@ async function main() {
|
||||
const command = process.argv[2];
|
||||
const port = getWorkerPort();
|
||||
|
||||
// Helper for JSON status output in 'start' command
|
||||
// Exit code 0 ensures Windows Terminal doesn't keep tabs open
|
||||
function exitWithStatus(status: 'ready' | 'error', message?: string): never {
|
||||
const output = buildStatusOutput(status, message);
|
||||
console.log(JSON.stringify(output));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
switch (command) {
|
||||
case 'start': {
|
||||
if (await waitForHealth(port, 1000)) {
|
||||
@@ -636,12 +668,12 @@ async function main() {
|
||||
const freed = await waitForPortFree(port, getPlatformTimeout(15000));
|
||||
if (!freed) {
|
||||
logger.error('SYSTEM', 'Port did not free up after shutdown for version mismatch restart', { port });
|
||||
process.exit(1);
|
||||
exitWithStatus('error', 'Port did not free after version mismatch restart');
|
||||
}
|
||||
removePidFile();
|
||||
} else {
|
||||
logger.info('SYSTEM', 'Worker already running and healthy');
|
||||
process.exit(0);
|
||||
exitWithStatus('ready');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -651,17 +683,17 @@ async function main() {
|
||||
const healthy = await waitForHealth(port, getPlatformTimeout(15000));
|
||||
if (healthy) {
|
||||
logger.info('SYSTEM', 'Worker is now healthy');
|
||||
process.exit(0);
|
||||
exitWithStatus('ready');
|
||||
}
|
||||
logger.error('SYSTEM', 'Port in use but worker not responding to health checks');
|
||||
process.exit(1);
|
||||
exitWithStatus('error', 'Port in use but worker not responding');
|
||||
}
|
||||
|
||||
logger.info('SYSTEM', 'Starting worker daemon');
|
||||
const pid = spawnDaemon(__filename, port);
|
||||
if (pid === undefined) {
|
||||
logger.error('SYSTEM', 'Failed to spawn worker daemon');
|
||||
process.exit(1);
|
||||
exitWithStatus('error', 'Failed to spawn worker daemon');
|
||||
}
|
||||
|
||||
writePidFile({ pid, port, startedAt: new Date().toISOString() });
|
||||
@@ -670,11 +702,11 @@ async function main() {
|
||||
if (!healthy) {
|
||||
removePidFile();
|
||||
logger.error('SYSTEM', 'Worker failed to start (health check timeout)');
|
||||
process.exit(1);
|
||||
exitWithStatus('error', 'Worker failed to start (health check timeout)');
|
||||
}
|
||||
|
||||
logger.info('SYSTEM', 'Worker started successfully');
|
||||
process.exit(0);
|
||||
exitWithStatus('ready');
|
||||
}
|
||||
|
||||
case 'stop': {
|
||||
@@ -694,14 +726,18 @@ async function main() {
|
||||
const freed = await waitForPortFree(port, getPlatformTimeout(15000));
|
||||
if (!freed) {
|
||||
logger.error('SYSTEM', 'Port did not free up after shutdown, aborting restart', { port });
|
||||
process.exit(1);
|
||||
// Exit gracefully: Windows Terminal won't keep tab open on exit 0
|
||||
// The wrapper/plugin will handle restart logic if needed
|
||||
process.exit(0);
|
||||
}
|
||||
removePidFile();
|
||||
|
||||
const pid = spawnDaemon(__filename, port);
|
||||
if (pid === undefined) {
|
||||
logger.error('SYSTEM', 'Failed to spawn worker daemon during restart');
|
||||
process.exit(1);
|
||||
// Exit gracefully: Windows Terminal won't keep tab open on exit 0
|
||||
// The wrapper/plugin will handle restart logic if needed
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
writePidFile({ pid, port, startedAt: new Date().toISOString() });
|
||||
@@ -710,7 +746,9 @@ async function main() {
|
||||
if (!healthy) {
|
||||
removePidFile();
|
||||
logger.error('SYSTEM', 'Worker failed to restart');
|
||||
process.exit(1);
|
||||
// Exit gracefully: Windows Terminal won't keep tab open on exit 0
|
||||
// The wrapper/plugin will handle restart logic if needed
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
logger.info('SYSTEM', 'Worker restarted successfully');
|
||||
@@ -737,13 +775,29 @@ async function main() {
|
||||
process.exit(cursorResult);
|
||||
}
|
||||
|
||||
case 'hook': {
|
||||
const platform = process.argv[3];
|
||||
const event = process.argv[4];
|
||||
if (!platform || !event) {
|
||||
console.error('Usage: claude-mem hook <platform> <event>');
|
||||
console.error('Platforms: claude-code, cursor, raw');
|
||||
console.error('Events: context, session-init, observation, summarize, user-message');
|
||||
process.exit(1);
|
||||
}
|
||||
const { hookCommand } = await import('../cli/hook-command.js');
|
||||
await hookCommand(platform, event);
|
||||
break;
|
||||
}
|
||||
|
||||
case '--daemon':
|
||||
default: {
|
||||
const worker = new WorkerService();
|
||||
worker.start().catch((error) => {
|
||||
logger.failure('SYSTEM', 'Worker failed to start', {}, error as Error);
|
||||
removePidFile();
|
||||
process.exit(1);
|
||||
// Exit gracefully: Windows Terminal won't keep tab open on exit 0
|
||||
// The wrapper/plugin will handle restart logic if needed
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+112
-36
@@ -3,47 +3,123 @@
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Jan 5, 2026
|
||||
### Dec 10, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #37751 | 6:21 PM | 🔴 | OpenRouterAgent Fully Updated with projectRoot Threading | ~307 |
|
||||
| #37748 | " | ✅ | OpenRouterAgent Observation Handler Updated with lastCwd | ~276 |
|
||||
| #37746 | 6:20 PM | ✅ | GeminiAgent Summary Handler Updated with lastCwd | ~278 |
|
||||
| #37745 | " | ✅ | OpenRouterAgent Init Prompt Call Updated with undefined projectRoot | ~299 |
|
||||
| #37744 | " | ✅ | GeminiAgent First processAgentResponse Updated with lastCwd | ~274 |
|
||||
| #37743 | " | ✅ | Added lastCwd Tracking to OpenRouterAgent | ~265 |
|
||||
| #37742 | 6:19 PM | ✅ | Phase 3 Started: Added lastCwd Tracking to GeminiAgent | ~291 |
|
||||
| #37741 | " | 🔵 | OpenRouterAgent Structure Analyzed for Phase 3 Implementation | ~367 |
|
||||
| #37740 | " | 🔵 | SDKAgent Structure Analyzed for Phase 3 Implementation | ~430 |
|
||||
| #37739 | 6:18 PM | 🔵 | GeminiAgent Structure Analyzed for Phase 3 Implementation | ~381 |
|
||||
| #37737 | 6:16 PM | 🔵 | Identified processAgentResponse Callers for Phase 3 | ~275 |
|
||||
| #37701 | 6:01 PM | 🔵 | Complete cwd data flow traced from hooks through observation processing | ~447 |
|
||||
| #37421 | 1:06 AM | 🔵 | Identified by-file API endpoint usage across codebase | ~406 |
|
||||
| #37416 | 1:02 AM | 🔴 | Fixed find_by_file Parameter Handling for Comma-Separated Values | ~375 |
|
||||
| #37415 | " | 🟣 | SearchManager Parameter Normalization Enhanced for Folder Queries | ~406 |
|
||||
| #37414 | 1:01 AM | 🔵 | SearchManager findByFile Uses filePath Parameter for File Search | ~429 |
|
||||
| #37413 | " | 🔵 | Hybrid Search Strategy: Metadata-First with Semantic Ranking | ~475 |
|
||||
| #37391 | 12:48 AM | ✅ | Staged 23 CLAUDE.md files with mix of new and modified content | ~400 |
|
||||
| #37390 | 12:47 AM | ✅ | Regenerated 23 CLAUDE.md files in budapest workspace | ~365 |
|
||||
| #23673 | 8:36 PM | ✅ | Add Project Filter Parameter to Session and Prompt Hydration in Search | ~306 |
|
||||
| #23596 | 5:54 PM | ⚖️ | Import/Export Bug Fix Priority and Scope | ~415 |
|
||||
| #23595 | 5:53 PM | 🔴 | SearchManager Returns Wrong Format for Empty Results | ~320 |
|
||||
| #23594 | " | 🔵 | SearchManager Search Method Control Flow | ~313 |
|
||||
| #23591 | 5:51 PM | 🔵 | SearchManager JSON Response Structure | ~231 |
|
||||
| #23590 | " | 🔵 | Import/Export Feature Status Review | ~490 |
|
||||
| #23583 | 5:50 PM | 🔵 | SearchManager Hybrid Search Architecture | ~495 |
|
||||
|
||||
### Jan 7, 2026
|
||||
### Dec 13, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #38467 | 10:29 PM | ⚖️ | Log Level Audit Strategy: Tighten ERROR Messages for Runtime Issue Discovery | ~464 |
|
||||
| #38437 | 10:24 PM | 🔵 | Claude-mem core functionality and logging patterns identified | ~710 |
|
||||
| #38433 | " | 🔵 | SessionManager Uses ERROR Logging for Database Persistence Failures | ~503 |
|
||||
| #38431 | " | 🔵 | ERROR-Level Logging Used Across 21 Source Files | ~480 |
|
||||
| #38425 | " | ⚖️ | Log Level Architecture: Fail-Critical Over Fail-Fast for Chroma | ~467 |
|
||||
| #38418 | 10:22 PM | 🔵 | ChromaDB Operates as Optional Subsystem with Graceful Degradation | ~586 |
|
||||
| #38416 | " | 🔵 | ChromaDB Is Critical Not Optional - Log Audit Findings Challenged | ~405 |
|
||||
| #38405 | 10:07 PM | ⚖️ | DEBUG Log Level Analysis - One Message Requires WARN Promotion | ~819 |
|
||||
| #38404 | 10:06 PM | ⚖️ | Log Level Audit Analysis - WARN to ERROR Promotion Criteria Established | ~769 |
|
||||
| #38372 | 9:43 PM | 🔵 | Logging Anti-Pattern Found: Critical Errors Logged as DEBUG Instead of ERROR | ~464 |
|
||||
| #38354 | 9:10 PM | 🔵 | Provider Selection Architecture and Default Settings Location | ~478 |
|
||||
| #38353 | " | 🔵 | Session ID Architecture and Message Yielding for SDK Interaction | ~536 |
|
||||
| #38253 | 7:47 PM | 🔵 | SDK Agent Implementation with Resume Logic | ~1036 |
|
||||
| #38250 | 7:46 PM | 🔵 | Session Manager Event-Driven Architecture | ~865 |
|
||||
| #38248 | 7:45 PM | 🔵 | OpenRouter Agent Implementation | ~851 |
|
||||
| #25191 | 8:04 PM | 🔵 | ChromaSync Instantiated in DatabaseManager Constructor | ~315 |
|
||||
|
||||
### Dec 14, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #26263 | 8:32 PM | 🔵 | SearchManager Timeline Methods Use Rich Formatting, Search Method Uses Flat Tables | ~464 |
|
||||
| #26243 | 8:29 PM | 🔵 | FormattingService Provides Basic Table Format Without Dates or File Grouping | ~390 |
|
||||
| #26240 | " | 🔵 | SearchManager Formats Results as Tables, Timeline Uses Rich Date-Grouped Format | ~416 |
|
||||
| #26108 | 7:43 PM | ✅ | changes() Method Format Logic Removed | ~401 |
|
||||
| #26107 | " | ✅ | changes() Method Format Parameter Removed | ~317 |
|
||||
| #26106 | 7:42 PM | ✅ | decisions() Method Format Logic Removed | ~405 |
|
||||
| #26105 | " | ✅ | decisions() Method Format Parameter Removed | ~310 |
|
||||
| #26104 | " | ✅ | Main search() Method Format Handling Removed | ~430 |
|
||||
| #26103 | 7:41 PM | ✅ | FormattingService.ts Rewritten to Table Format | ~457 |
|
||||
| #26102 | " | 🔵 | SearchManager.ts Format Parameter Removal Status | ~478 |
|
||||
|
||||
### Dec 15, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #27043 | 6:04 PM | 🔵 | Subagent confirms no version switcher UI exists, only orphaned backend infrastructure | ~539 |
|
||||
| #27041 | 6:03 PM | 🔵 | Branch switching code isolated to two backend files, no frontend UI components | ~473 |
|
||||
| #27037 | 6:02 PM | 🔵 | Branch switching functionality exists in SettingsRoutes with UI switcher removal intent | ~463 |
|
||||
|
||||
### Dec 16, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #27727 | 5:45 PM | 🔵 | SearchManager returns raw data arrays when format=json is specified | ~349 |
|
||||
|
||||
### Dec 17, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #28473 | 4:25 PM | 🔵 | PaginationHelper LIMIT+1 Trick and Project Path Sanitization | ~499 |
|
||||
| #28458 | 4:24 PM | 🔵 | SDK Agent Observer-Only Event-Driven Query Loop | ~513 |
|
||||
| #28455 | " | 🔵 | Event-Driven Session Manager with Zero-Latency Queuing | ~566 |
|
||||
|
||||
### Dec 18, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #29240 | 12:12 AM | 🔵 | SDK Agent Event-Driven Query Loop with Tool Restrictions | ~507 |
|
||||
|
||||
### Dec 20, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #31100 | 8:01 PM | 🔵 | Summary and Memory Message Generation in SDK Agent | ~324 |
|
||||
|
||||
### Dec 25, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #32616 | 8:43 PM | 🔵 | Comprehensive analysis of "enable billing" setting and its impact on rate limiting | ~533 |
|
||||
| #32599 | 8:40 PM | 🔄 | Added validation and explicit default for Gemini model configuration | ~393 |
|
||||
| #32598 | " | 🔵 | Gemini configuration loaded from settings or environment variables | ~363 |
|
||||
| #32591 | 8:38 PM | 🔴 | Removed Unsupported Gemini Model from Agent | ~282 |
|
||||
| #32583 | " | 🔵 | Gemini Agent Implementation Details | ~434 |
|
||||
| #32543 | 7:29 PM | 🔄 | Rate limiting applied conditionally based on billing status | ~164 |
|
||||
| #32542 | " | 🔄 | Query Gemini now accepts billing status | ~163 |
|
||||
| #32541 | " | 🔄 | Gemini config now includes billing status | ~182 |
|
||||
| #32540 | " | 🔄 | Rate limiting logic refactored for Gemini billing | ~164 |
|
||||
|
||||
### Dec 26, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #32949 | 10:55 PM | 🔵 | Complete settings persistence flow for Xiaomi MIMO v2 Flash model | ~320 |
|
||||
| #32948 | 10:53 PM | 🔵 | OpenRouterAgent uses CLAUDE_MEM_OPENROUTER_MODEL setting with Xiaomi as default | ~183 |
|
||||
|
||||
### Dec 27, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #33215 | 9:06 PM | 🔵 | SessionManager Implements Event-Driven Lifecycle with Database-First Persistence and Auto-Initialization | ~853 |
|
||||
| #33214 | " | 🔵 | SDKAgent Implements Event-Driven Query Loop with Init/Continuation Prompt Selection | ~769 |
|
||||
|
||||
### Dec 28, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #33551 | 11:00 PM | 🔵 | GeminiAgent Does Not Implement Resume Functionality | ~307 |
|
||||
| #33550 | " | 🔵 | OpenRouterAgent Does Not Implement Resume Functionality | ~294 |
|
||||
| #33549 | 10:59 PM | 🔴 | SDKAgent Now Checks memorySessionId Differs From contentSessionId Before Resume | ~419 |
|
||||
| #33547 | " | 🔵 | All Agents Call storeObservation with contentSessionId Instead of memorySessionId | ~407 |
|
||||
| #33543 | 10:56 PM | 🔵 | SDKAgent Already Implements Memory Session ID Capture and Resume Logic | ~467 |
|
||||
| #33542 | " | 🔵 | SessionManager Already Uses Renamed Session ID Fields | ~390 |
|
||||
|
||||
### Dec 30, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #34504 | 2:31 PM | 🔵 | SDKAgent V2 Message Handling and Processing Flow Detailed | ~583 |
|
||||
| #34459 | 2:23 PM | 🔵 | Complete SDKAgent V2 Architecture with Comprehensive Message Processing | ~619 |
|
||||
| #34453 | 2:21 PM | 🔵 | Memory Agent Configured as Observer-Only | ~379 |
|
||||
|
||||
### Jan 4, 2026
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #36853 | 1:49 AM | 🔵 | GeminiAgent Implementation Reviewed for Model Support | ~555 |
|
||||
</claude-mem-context>
|
||||
@@ -4,6 +4,7 @@ export const HOOK_TIMEOUTS = {
|
||||
WORKER_STARTUP_WAIT: 1000,
|
||||
WORKER_STARTUP_RETRIES: 300,
|
||||
PRE_RESTART_SETTLE_DELAY: 2000, // Give files time to sync before restart
|
||||
POWERSHELL_COMMAND: 10000, // PowerShell process enumeration (10s - typically completes in <1s)
|
||||
WINDOWS_MULTIPLIER: 1.5 // Platform-specific adjustment
|
||||
} as const;
|
||||
|
||||
|
||||
+54
-1
@@ -3,5 +3,58 @@
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
*No recent activity*
|
||||
### Nov 5, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #4035 | 10:24 PM | 🔵 | logger.ts file exists but is empty | ~220 |
|
||||
|
||||
### Nov 10, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #6521 | 5:43 PM | 🔵 | Code Review: Enhanced HTTP Logging and Double Entries Bug Fix | ~482 |
|
||||
|
||||
### Nov 17, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #10019 | 12:14 AM | 🔵 | TranscriptParser Utility: JSONL Parsing with Type-Safe Entry Filtering | ~569 |
|
||||
|
||||
### Nov 23, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #14626 | 6:25 PM | 🔵 | Stop Hook Summary Not in Transcript Validator Schema | ~359 |
|
||||
|
||||
### Nov 28, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #17238 | 11:34 PM | 🔵 | Existing TranscriptParser TypeScript implementation handles nested message structure | ~493 |
|
||||
|
||||
### Dec 5, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #20407 | 7:20 PM | 🔵 | Tag stripping utilities implement dual-tag privacy system with ReDoS protection | ~415 |
|
||||
|
||||
### Dec 8, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #22310 | 9:46 PM | 🟣 | Complete Hook Lifecycle Documentation Generated | ~603 |
|
||||
| #22306 | 9:45 PM | 🔵 | Dual-Tag Privacy System with ReDoS Protection | ~461 |
|
||||
|
||||
### Dec 14, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #25691 | 4:24 PM | 🔵 | happy_path_error__with_fallback utility logs errors to silent.log and returns fallback values | ~460 |
|
||||
|
||||
### Dec 20, 2025
|
||||
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #30883 | 6:38 PM | 🔵 | Tag-Stripping DRY Violation Analysis | ~152 |
|
||||
</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>
|
||||
@@ -47,6 +47,10 @@ describe('hook-constants', () => {
|
||||
it('should define WINDOWS_MULTIPLIER', () => {
|
||||
expect(HOOK_TIMEOUTS.WINDOWS_MULTIPLIER).toBe(1.5);
|
||||
});
|
||||
|
||||
it('should define POWERSHELL_COMMAND timeout as 10000ms', () => {
|
||||
expect(HOOK_TIMEOUTS.POWERSHELL_COMMAND).toBe(10000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('HOOK_EXIT_CODES', () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'bun:test';
|
||||
|
||||
/**
|
||||
* Tests for WMIC output parsing logic used in Windows process enumeration.
|
||||
* Tests for PowerShell output parsing logic used in Windows process enumeration.
|
||||
*
|
||||
* This tests the parsing behavior directly since mocking promisified exec
|
||||
* is unreliable across module boundaries. The parsing logic matches exactly
|
||||
@@ -9,16 +9,14 @@ import { describe, it, expect, beforeEach, afterEach } from 'bun:test';
|
||||
*/
|
||||
|
||||
// Extract the parsing logic from ProcessManager for direct testing
|
||||
// This matches the implementation in src/services/infrastructure/ProcessManager.ts lines 93-100
|
||||
function parseWmicOutput(stdout: string): number[] {
|
||||
// This matches the implementation in src/services/infrastructure/ProcessManager.ts lines 95-100
|
||||
function parsePowerShellOutput(stdout: string): number[] {
|
||||
return stdout
|
||||
.trim()
|
||||
.split('\n')
|
||||
.map(line => {
|
||||
const match = line.match(/ProcessId=(\d+)/i);
|
||||
return match ? parseInt(match[1], 10) : NaN;
|
||||
})
|
||||
.filter(n => !isNaN(n) && Number.isInteger(n) && n > 0);
|
||||
.map(line => line.trim())
|
||||
.filter(line => line.length > 0 && /^\d+$/.test(line))
|
||||
.map(line => parseInt(line, 10))
|
||||
.filter(pid => pid > 0);
|
||||
}
|
||||
|
||||
// Validate parent PID - matches ProcessManager.getChildProcesses() lines 85-88
|
||||
@@ -26,131 +24,119 @@ function isValidParentPid(parentPid: number): boolean {
|
||||
return Number.isInteger(parentPid) && parentPid > 0;
|
||||
}
|
||||
|
||||
describe('WMIC output parsing (Windows)', () => {
|
||||
describe('parseWmicOutput - ProcessId format parsing', () => {
|
||||
it('should parse ProcessId=12345 format correctly', () => {
|
||||
const stdout = 'ProcessId=12345\r\nProcessId=67890\r\n';
|
||||
describe('PowerShell output parsing (Windows)', () => {
|
||||
describe('parsePowerShellOutput - simple number format parsing', () => {
|
||||
it('should parse simple number format correctly', () => {
|
||||
const stdout = '12345\r\n67890\r\n';
|
||||
|
||||
const result = parseWmicOutput(stdout);
|
||||
const result = parsePowerShellOutput(stdout);
|
||||
|
||||
expect(result).toEqual([12345, 67890]);
|
||||
});
|
||||
|
||||
it('should parse single PID from WMIC output', () => {
|
||||
const stdout = 'ProcessId=54321\r\n';
|
||||
it('should parse single PID from PowerShell output', () => {
|
||||
const stdout = '54321\r\n';
|
||||
|
||||
const result = parseWmicOutput(stdout);
|
||||
const result = parsePowerShellOutput(stdout);
|
||||
|
||||
expect(result).toEqual([54321]);
|
||||
});
|
||||
|
||||
it('should handle WMIC output with mixed case', () => {
|
||||
// WMIC output can vary in case on different Windows versions
|
||||
const stdout = 'PROCESSID=11111\r\nprocessid=22222\r\nProcessId=33333\r\n';
|
||||
|
||||
const result = parseWmicOutput(stdout);
|
||||
|
||||
expect(result).toEqual([11111, 22222, 33333]);
|
||||
});
|
||||
|
||||
it('should handle empty WMIC output', () => {
|
||||
it('should handle empty PowerShell output', () => {
|
||||
const stdout = '';
|
||||
|
||||
const result = parseWmicOutput(stdout);
|
||||
const result = parsePowerShellOutput(stdout);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle WMIC output with only whitespace', () => {
|
||||
it('should handle PowerShell output with only whitespace', () => {
|
||||
const stdout = ' \r\n \r\n';
|
||||
|
||||
const result = parseWmicOutput(stdout);
|
||||
const result = parsePowerShellOutput(stdout);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should filter invalid PIDs from WMIC output', () => {
|
||||
const stdout = 'ProcessId=12345\r\nProcessId=invalid\r\nProcessId=67890\r\n';
|
||||
it('should filter invalid PIDs from PowerShell output', () => {
|
||||
const stdout = '12345\r\ninvalid\r\n67890\r\n';
|
||||
|
||||
const result = parseWmicOutput(stdout);
|
||||
const result = parsePowerShellOutput(stdout);
|
||||
|
||||
expect(result).toEqual([12345, 67890]);
|
||||
});
|
||||
|
||||
it('should filter negative PIDs from WMIC output', () => {
|
||||
// Negative PIDs won't match the regex /ProcessId=(\d+)/i (only digits)
|
||||
const stdout = 'ProcessId=12345\r\nProcessId=-1\r\nProcessId=67890\r\n';
|
||||
it('should filter negative PIDs from PowerShell output', () => {
|
||||
const stdout = '12345\r\n-1\r\n67890\r\n';
|
||||
|
||||
const result = parseWmicOutput(stdout);
|
||||
const result = parsePowerShellOutput(stdout);
|
||||
|
||||
expect(result).toEqual([12345, 67890]);
|
||||
});
|
||||
|
||||
it('should filter zero PIDs from WMIC output', () => {
|
||||
// Zero is filtered out by the n > 0 check
|
||||
const stdout = 'ProcessId=0\r\nProcessId=12345\r\n';
|
||||
it('should filter zero PIDs from PowerShell output', () => {
|
||||
const stdout = '0\r\n12345\r\n';
|
||||
|
||||
const result = parseWmicOutput(stdout);
|
||||
const result = parsePowerShellOutput(stdout);
|
||||
|
||||
expect(result).toEqual([12345]);
|
||||
});
|
||||
|
||||
it('should handle WMIC output with extra lines and noise', () => {
|
||||
const stdout = '\r\n\r\nProcessId=12345\r\n\r\nSome other output\r\nProcessId=67890\r\n\r\n';
|
||||
it('should handle PowerShell output with extra lines and noise', () => {
|
||||
const stdout = '\r\n\r\n12345\r\n\r\nSome other output\r\n67890\r\n\r\n';
|
||||
|
||||
const result = parseWmicOutput(stdout);
|
||||
const result = parsePowerShellOutput(stdout);
|
||||
|
||||
expect(result).toEqual([12345, 67890]);
|
||||
});
|
||||
|
||||
it('should handle Windows line endings (CRLF)', () => {
|
||||
const stdout = 'ProcessId=111\r\nProcessId=222\r\nProcessId=333\r\n';
|
||||
const stdout = '111\r\n222\r\n333\r\n';
|
||||
|
||||
const result = parseWmicOutput(stdout);
|
||||
const result = parsePowerShellOutput(stdout);
|
||||
|
||||
expect(result).toEqual([111, 222, 333]);
|
||||
});
|
||||
|
||||
it('should handle Unix line endings (LF)', () => {
|
||||
const stdout = 'ProcessId=111\nProcessId=222\nProcessId=333\n';
|
||||
const stdout = '111\n222\n333\n';
|
||||
|
||||
const result = parseWmicOutput(stdout);
|
||||
const result = parsePowerShellOutput(stdout);
|
||||
|
||||
expect(result).toEqual([111, 222, 333]);
|
||||
});
|
||||
|
||||
it('should handle lines with extra equals signs', () => {
|
||||
const stdout = 'ProcessId=12345\r\nSomeOther=value=with=equals\r\nProcessId=67890\r\n';
|
||||
|
||||
const result = parseWmicOutput(stdout);
|
||||
|
||||
expect(result).toEqual([12345, 67890]);
|
||||
});
|
||||
|
||||
it('should handle very large PIDs', () => {
|
||||
// Windows PIDs can be large but are still 32-bit integers
|
||||
const stdout = 'ProcessId=2147483647\r\n';
|
||||
const stdout = '2147483647\r\n';
|
||||
|
||||
const result = parseWmicOutput(stdout);
|
||||
const result = parsePowerShellOutput(stdout);
|
||||
|
||||
expect(result).toEqual([2147483647]);
|
||||
});
|
||||
|
||||
it('should handle typical WMIC list format output', () => {
|
||||
// Real WMIC output often has blank lines and extra spacing
|
||||
it('should handle typical PowerShell output with blank lines and extra spacing', () => {
|
||||
const stdout = `
|
||||
|
||||
ProcessId=1234
|
||||
1234
|
||||
|
||||
|
||||
ProcessId=5678
|
||||
5678
|
||||
|
||||
`;
|
||||
|
||||
const result = parseWmicOutput(stdout);
|
||||
const result = parsePowerShellOutput(stdout);
|
||||
|
||||
expect(result).toEqual([1234, 5678]);
|
||||
});
|
||||
|
||||
it('should filter lines with text and numbers mixed', () => {
|
||||
const stdout = '12345\r\nPID: 67890\r\n11111\r\n';
|
||||
|
||||
const result = parsePowerShellOutput(stdout);
|
||||
|
||||
expect(result).toEqual([12345, 11111]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('parent PID validation', () => {
|
||||
|
||||
@@ -0,0 +1,446 @@
|
||||
/**
|
||||
* Tests for worker JSON status output structure
|
||||
*
|
||||
* Tests the buildStatusOutput pure function extracted from worker-service.ts
|
||||
* to ensure JSON output matches the hook framework contract.
|
||||
*
|
||||
* Also tests CLI output capture for the 'start' command to verify
|
||||
* actual JSON output matches expected structure.
|
||||
*
|
||||
* No mocks needed - tests a pure function directly and captures real CLI output.
|
||||
*/
|
||||
import { describe, it, expect } from 'bun:test';
|
||||
import { spawnSync } from 'child_process';
|
||||
import { existsSync } from 'fs';
|
||||
import path from 'path';
|
||||
import { buildStatusOutput, StatusOutput } from '../../src/services/worker-service.js';
|
||||
|
||||
const WORKER_SCRIPT = path.join(__dirname, '../../plugin/scripts/worker-service.cjs');
|
||||
|
||||
/**
|
||||
* Run worker CLI command and return stdout + exit code
|
||||
* Uses spawnSync for synchronous output capture
|
||||
*/
|
||||
function runWorkerStart(): { stdout: string; exitCode: number } {
|
||||
const result = spawnSync('bun', [WORKER_SCRIPT, 'start'], {
|
||||
encoding: 'utf-8',
|
||||
timeout: 60000
|
||||
});
|
||||
return { stdout: result.stdout?.trim() || '', exitCode: result.status || 0 };
|
||||
}
|
||||
|
||||
describe('worker-json-status', () => {
|
||||
describe('buildStatusOutput', () => {
|
||||
describe('ready status', () => {
|
||||
it('should return valid JSON with required fields for ready status', () => {
|
||||
const result = buildStatusOutput('ready');
|
||||
|
||||
expect(result.status).toBe('ready');
|
||||
expect(result.continue).toBe(true);
|
||||
expect(result.suppressOutput).toBe(true);
|
||||
});
|
||||
|
||||
it('should not include message field when not provided', () => {
|
||||
const result = buildStatusOutput('ready');
|
||||
|
||||
expect(result.message).toBeUndefined();
|
||||
expect('message' in result).toBe(false);
|
||||
});
|
||||
|
||||
it('should include message field when explicitly provided for ready status', () => {
|
||||
const result = buildStatusOutput('ready', 'Worker started successfully');
|
||||
|
||||
expect(result.status).toBe('ready');
|
||||
expect(result.message).toBe('Worker started successfully');
|
||||
});
|
||||
});
|
||||
|
||||
describe('error status', () => {
|
||||
it('should return valid JSON with required fields for error status', () => {
|
||||
const result = buildStatusOutput('error');
|
||||
|
||||
expect(result.status).toBe('error');
|
||||
expect(result.continue).toBe(true);
|
||||
expect(result.suppressOutput).toBe(true);
|
||||
});
|
||||
|
||||
it('should include message field when provided for error status', () => {
|
||||
const result = buildStatusOutput('error', 'Port in use but worker not responding');
|
||||
|
||||
expect(result.status).toBe('error');
|
||||
expect(result.message).toBe('Port in use but worker not responding');
|
||||
});
|
||||
|
||||
it('should handle various error messages correctly', () => {
|
||||
const errorMessages = [
|
||||
'Port did not free after version mismatch restart',
|
||||
'Failed to spawn worker daemon',
|
||||
'Worker failed to start (health check timeout)'
|
||||
];
|
||||
|
||||
for (const msg of errorMessages) {
|
||||
const result = buildStatusOutput('error', msg);
|
||||
expect(result.message).toBe(msg);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('required fields always present', () => {
|
||||
it('should always include continue: true', () => {
|
||||
expect(buildStatusOutput('ready').continue).toBe(true);
|
||||
expect(buildStatusOutput('error').continue).toBe(true);
|
||||
expect(buildStatusOutput('ready', 'msg').continue).toBe(true);
|
||||
expect(buildStatusOutput('error', 'msg').continue).toBe(true);
|
||||
});
|
||||
|
||||
it('should always include suppressOutput: true', () => {
|
||||
expect(buildStatusOutput('ready').suppressOutput).toBe(true);
|
||||
expect(buildStatusOutput('error').suppressOutput).toBe(true);
|
||||
expect(buildStatusOutput('ready', 'msg').suppressOutput).toBe(true);
|
||||
expect(buildStatusOutput('error', 'msg').suppressOutput).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('JSON serialization', () => {
|
||||
it('should produce valid JSON when stringified', () => {
|
||||
const readyResult = buildStatusOutput('ready');
|
||||
const errorResult = buildStatusOutput('error', 'Test error message');
|
||||
|
||||
expect(() => JSON.stringify(readyResult)).not.toThrow();
|
||||
expect(() => JSON.stringify(errorResult)).not.toThrow();
|
||||
|
||||
const parsedReady = JSON.parse(JSON.stringify(readyResult));
|
||||
expect(parsedReady.status).toBe('ready');
|
||||
expect(parsedReady.continue).toBe(true);
|
||||
|
||||
const parsedError = JSON.parse(JSON.stringify(errorResult));
|
||||
expect(parsedError.status).toBe('error');
|
||||
expect(parsedError.message).toBe('Test error message');
|
||||
});
|
||||
|
||||
it('should match expected JSON structure for hook framework', () => {
|
||||
const readyOutput = JSON.stringify(buildStatusOutput('ready'));
|
||||
const errorOutput = JSON.stringify(buildStatusOutput('error', 'error msg'));
|
||||
|
||||
// Verify exact structure (order may vary, but content must match)
|
||||
const parsedReady = JSON.parse(readyOutput);
|
||||
expect(parsedReady).toEqual({
|
||||
continue: true,
|
||||
suppressOutput: true,
|
||||
status: 'ready'
|
||||
});
|
||||
|
||||
const parsedError = JSON.parse(errorOutput);
|
||||
expect(parsedError).toEqual({
|
||||
continue: true,
|
||||
suppressOutput: true,
|
||||
status: 'error',
|
||||
message: 'error msg'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('type safety', () => {
|
||||
it('should only accept valid status values', () => {
|
||||
// TypeScript ensures these are the only valid values at compile time
|
||||
// This runtime test validates the behavior
|
||||
const readyResult: StatusOutput = buildStatusOutput('ready');
|
||||
const errorResult: StatusOutput = buildStatusOutput('error');
|
||||
|
||||
expect(['ready', 'error']).toContain(readyResult.status);
|
||||
expect(['ready', 'error']).toContain(errorResult.status);
|
||||
});
|
||||
|
||||
it('should have correct type structure', () => {
|
||||
const result = buildStatusOutput('ready');
|
||||
|
||||
// Verify literal types
|
||||
expect(result.continue).toBe(true as const);
|
||||
expect(result.suppressOutput).toBe(true as const);
|
||||
});
|
||||
});
|
||||
|
||||
describe('edge cases', () => {
|
||||
it('should handle empty string message', () => {
|
||||
// Empty string is falsy, so message should NOT be included
|
||||
const result = buildStatusOutput('error', '');
|
||||
expect('message' in result).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle message with special characters', () => {
|
||||
const specialMessage = 'Error: "quoted" & special <chars>';
|
||||
const result = buildStatusOutput('error', specialMessage);
|
||||
expect(result.message).toBe(specialMessage);
|
||||
|
||||
// Verify it serializes correctly
|
||||
const parsed = JSON.parse(JSON.stringify(result));
|
||||
expect(parsed.message).toBe(specialMessage);
|
||||
});
|
||||
|
||||
it('should handle very long message', () => {
|
||||
const longMessage = 'A'.repeat(10000);
|
||||
const result = buildStatusOutput('error', longMessage);
|
||||
expect(result.message).toBe(longMessage);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('start command JSON output', () => {
|
||||
describe('when worker already healthy', () => {
|
||||
it('should output valid JSON with status: ready', () => {
|
||||
// Skip if worker script doesn't exist (not built)
|
||||
if (!existsSync(WORKER_SCRIPT)) {
|
||||
console.log('Skipping CLI test - worker script not built');
|
||||
return;
|
||||
}
|
||||
|
||||
const { stdout, exitCode } = runWorkerStart();
|
||||
|
||||
// The start command always exits with 0 (Windows Terminal compatibility)
|
||||
expect(exitCode).toBe(0);
|
||||
|
||||
// Should output valid JSON
|
||||
expect(() => JSON.parse(stdout)).not.toThrow();
|
||||
|
||||
const parsed = JSON.parse(stdout);
|
||||
|
||||
// Verify required fields per hook framework contract
|
||||
expect(parsed.continue).toBe(true);
|
||||
expect(parsed.suppressOutput).toBe(true);
|
||||
expect(['ready', 'error']).toContain(parsed.status);
|
||||
});
|
||||
|
||||
it('should match expected JSON structure when worker is healthy', () => {
|
||||
if (!existsSync(WORKER_SCRIPT)) {
|
||||
console.log('Skipping CLI test - worker script not built');
|
||||
return;
|
||||
}
|
||||
|
||||
const { stdout } = runWorkerStart();
|
||||
const parsed = JSON.parse(stdout);
|
||||
|
||||
// When worker is already healthy, status should be 'ready'
|
||||
// (or 'error' if something unexpected happens)
|
||||
if (parsed.status === 'ready') {
|
||||
// Ready status should not include message unless explicitly set
|
||||
expect(parsed.continue).toBe(true);
|
||||
expect(parsed.suppressOutput).toBe(true);
|
||||
} else if (parsed.status === 'error') {
|
||||
// Error status may include a message explaining the failure
|
||||
expect(typeof parsed.message).toBe('string');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('error scenarios', () => {
|
||||
// These tests require complex setup (mocking ports, killing processes)
|
||||
// Skipped for now - the pure function tests above cover the JSON structure
|
||||
it.skip('should output JSON with status: error when port in use but not responding', () => {
|
||||
// Would require: start a non-worker server on the port, then call start
|
||||
});
|
||||
|
||||
it.skip('should output JSON with status: error on spawn failure', () => {
|
||||
// Would require: mock spawnDaemon to fail
|
||||
});
|
||||
|
||||
it.skip('should output JSON with status: error on health check timeout', () => {
|
||||
// Would require: start worker that never becomes healthy
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Claude Code hook framework compatibility tests
|
||||
*
|
||||
* These tests verify that the worker 'start' command output conforms to
|
||||
* Claude Code's hook output contract. Key requirements:
|
||||
*
|
||||
* 1. Exit code 0 - Required for Windows Terminal compatibility (prevents
|
||||
* tab accumulation from spawned processes)
|
||||
*
|
||||
* 2. JSON on stdout - Claude Code parses stdout as JSON. Logs must go to
|
||||
* stderr to avoid breaking JSON parsing.
|
||||
*
|
||||
* 3. `continue: true` - CRITICAL: This field tells Claude Code to continue
|
||||
* processing. If missing or false, Claude Code stops after the hook.
|
||||
* Per docs: "If continue is false, Claude stops processing after the
|
||||
* hooks run."
|
||||
*
|
||||
* 4. `suppressOutput: true` - Hides output from transcript mode (Ctrl-R).
|
||||
* Optional but recommended for non-user-facing status.
|
||||
*
|
||||
* Reference: private/context/claude-code/hooks.md
|
||||
*/
|
||||
describe('Claude Code hook framework compatibility', () => {
|
||||
/**
|
||||
* Windows Terminal compatibility requirement
|
||||
*
|
||||
* When hooks run in Windows Terminal, each spawned process can open a
|
||||
* new tab. Exit code 0 tells the terminal the process completed
|
||||
* successfully and prevents tab accumulation.
|
||||
*
|
||||
* Even for error states (worker failed to start), we exit 0 and
|
||||
* communicate the error via JSON { status: 'error', message: '...' }
|
||||
*/
|
||||
it('should always exit with code 0', () => {
|
||||
if (!existsSync(WORKER_SCRIPT)) {
|
||||
console.log('Skipping CLI test - worker script not built');
|
||||
return;
|
||||
}
|
||||
|
||||
const { exitCode } = runWorkerStart();
|
||||
|
||||
// Per Windows Terminal compatibility requirement, exit code is always 0
|
||||
// Error states are communicated via JSON status field, not exit codes
|
||||
expect(exitCode).toBe(0);
|
||||
});
|
||||
|
||||
/**
|
||||
* JSON must go to stdout, not stderr
|
||||
*
|
||||
* Claude Code parses stdout as JSON for hook output. Any non-JSON on
|
||||
* stdout breaks parsing. Logs, warnings, and debug info must go to
|
||||
* stderr.
|
||||
*
|
||||
* Structure: { status, continue, suppressOutput, message? }
|
||||
*/
|
||||
it('should output JSON on stdout (not stderr)', () => {
|
||||
if (!existsSync(WORKER_SCRIPT)) {
|
||||
console.log('Skipping CLI test - worker script not built');
|
||||
return;
|
||||
}
|
||||
|
||||
const result = spawnSync('bun', [WORKER_SCRIPT, 'start'], {
|
||||
encoding: 'utf-8',
|
||||
timeout: 60000
|
||||
});
|
||||
|
||||
const stdout = result.stdout?.trim() || '';
|
||||
const stderr = result.stderr?.trim() || '';
|
||||
|
||||
// stdout should contain valid JSON
|
||||
expect(() => JSON.parse(stdout)).not.toThrow();
|
||||
|
||||
// stderr should NOT contain the JSON output (it may have logs)
|
||||
// The JSON structure should only appear in stdout
|
||||
const parsed = JSON.parse(stdout);
|
||||
expect(parsed).toHaveProperty('status');
|
||||
expect(parsed).toHaveProperty('continue');
|
||||
|
||||
// Verify stderr doesn't accidentally contain the JSON output
|
||||
if (stderr) {
|
||||
try {
|
||||
const stderrParsed = JSON.parse(stderr);
|
||||
// If stderr parses as JSON with our structure, that's wrong
|
||||
expect(stderrParsed).not.toHaveProperty('suppressOutput');
|
||||
} catch {
|
||||
// stderr is not JSON, which is expected (logs, etc.)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* JSON must be parseable as valid JSON
|
||||
*
|
||||
* This seems obvious but is critical - any extraneous output (console.log
|
||||
* statements, warnings, etc.) will break JSON parsing and cause Claude
|
||||
* Code to fail processing the hook output.
|
||||
*/
|
||||
it('should be parseable as valid JSON', () => {
|
||||
if (!existsSync(WORKER_SCRIPT)) {
|
||||
console.log('Skipping CLI test - worker script not built');
|
||||
return;
|
||||
}
|
||||
|
||||
const { stdout } = runWorkerStart();
|
||||
|
||||
// Should not throw on parse
|
||||
let parsed: unknown;
|
||||
expect(() => {
|
||||
parsed = JSON.parse(stdout);
|
||||
}).not.toThrow();
|
||||
|
||||
// Should be an object, not a string, array, etc.
|
||||
expect(typeof parsed).toBe('object');
|
||||
expect(parsed).not.toBeNull();
|
||||
expect(Array.isArray(parsed)).toBe(false);
|
||||
});
|
||||
|
||||
/**
|
||||
* `continue: true` is CRITICAL
|
||||
*
|
||||
* From Claude Code docs: "If continue is false, Claude stops processing
|
||||
* after the hooks run."
|
||||
*
|
||||
* For SessionStart hooks (which start the worker), we MUST return
|
||||
* continue: true so Claude Code continues to process the user's prompt.
|
||||
* If we returned continue: false, Claude would stop immediately after
|
||||
* starting the worker and never respond to the user.
|
||||
*
|
||||
* This is why continue: true is a required literal in our StatusOutput
|
||||
* type - it can never be false.
|
||||
*/
|
||||
it('should always include continue: true (required for Claude Code to proceed)', () => {
|
||||
if (!existsSync(WORKER_SCRIPT)) {
|
||||
console.log('Skipping CLI test - worker script not built');
|
||||
return;
|
||||
}
|
||||
|
||||
const { stdout } = runWorkerStart();
|
||||
const parsed = JSON.parse(stdout);
|
||||
|
||||
// continue: true is CRITICAL - without it, Claude Code stops processing
|
||||
// This is not optional; it must always be true for our hooks
|
||||
expect(parsed.continue).toBe(true);
|
||||
|
||||
// Also verify it's the literal `true`, not a truthy value
|
||||
expect(parsed.continue).toStrictEqual(true);
|
||||
});
|
||||
|
||||
/**
|
||||
* suppressOutput hides from transcript mode
|
||||
*
|
||||
* When suppressOutput: true, the hook output doesn't appear in transcript
|
||||
* mode (Ctrl-R). This is useful for status messages that aren't relevant
|
||||
* to the user's conversation history.
|
||||
*
|
||||
* For the worker start command, we suppress output since "worker started"
|
||||
* is infrastructure noise, not conversation content.
|
||||
*/
|
||||
it('should include suppressOutput: true to hide from transcript mode', () => {
|
||||
if (!existsSync(WORKER_SCRIPT)) {
|
||||
console.log('Skipping CLI test - worker script not built');
|
||||
return;
|
||||
}
|
||||
|
||||
const { stdout } = runWorkerStart();
|
||||
const parsed = JSON.parse(stdout);
|
||||
|
||||
// suppressOutput prevents infrastructure noise from polluting transcript
|
||||
expect(parsed.suppressOutput).toBe(true);
|
||||
});
|
||||
|
||||
/**
|
||||
* status field communicates outcome
|
||||
*
|
||||
* The status field tells Claude Code (and debugging tools) whether the
|
||||
* hook succeeded. Valid values: 'ready' | 'error'
|
||||
*
|
||||
* Unlike exit codes (which are always 0), status can indicate failure.
|
||||
* This allows Claude Code to potentially take remedial action or log
|
||||
* the issue.
|
||||
*/
|
||||
it('should include a valid status field', () => {
|
||||
if (!existsSync(WORKER_SCRIPT)) {
|
||||
console.log('Skipping CLI test - worker script not built');
|
||||
return;
|
||||
}
|
||||
|
||||
const { stdout } = runWorkerStart();
|
||||
const parsed = JSON.parse(stdout);
|
||||
|
||||
expect(parsed).toHaveProperty('status');
|
||||
expect(['ready', 'error']).toContain(parsed.status);
|
||||
});
|
||||
});
|
||||
});
|
||||
+1
-100
@@ -5,106 +5,7 @@
|
||||
|
||||
### Dec 5, 2025
|
||||
|
||||
**full-lifecycle.test.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #20670 | 8:48 PM | 🔵 | Full Lifecycle Integration Test Analysis | ~428 |
|
||||
| #20664 | 8:46 PM | 🔵 | Test Suite Verification Complete | ~238 |
|
||||
| #20651 | 8:43 PM | 🟣 | Comprehensive Test Suite Merged | ~312 |
|
||||
| #20447 | 7:39 PM | 🟣 | Full Lifecycle Integration Test Suite Created | ~665 |
|
||||
|
||||
### Dec 7, 2025
|
||||
|
||||
**full-lifecycle.test.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #21829 | 11:05 PM | 🔄 | Massive refactor adds 8,671 lines and removes 5,585 lines across 60 files | ~619 |
|
||||
|
||||
### Dec 8, 2025
|
||||
|
||||
**full-lifecycle.test.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #22081 | 6:28 PM | ✅ | Clean Test Suite Execution Without Integration Tests | ~399 |
|
||||
| #22080 | 6:27 PM | ✅ | Full Test Suite Execution Results | ~456 |
|
||||
| #22039 | 6:01 PM | 🔵 | Discovered comprehensive TypeScript test suite with 11 test files | ~355 |
|
||||
|
||||
### Dec 9, 2025
|
||||
|
||||
**full-lifecycle.test.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #22606 | 11:33 AM | 🔵 | Hardcoded port 37777 found across multiple codebase locations | ~371 |
|
||||
|
||||
### Dec 11, 2025
|
||||
|
||||
**full-lifecycle.test.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #24076 | 3:16 PM | 🔵 | 45 files modified with nearly equal additions and deletions during Bun migration | ~443 |
|
||||
| #23951 | 1:45 PM | 🔄 | Integration Test Updated to Use Dynamic Port Configuration | ~272 |
|
||||
| #23933 | 1:36 PM | 🔵 | Comprehensive Port 37777 References Across Documentation and Code | ~427 |
|
||||
|
||||
### Dec 12, 2025
|
||||
|
||||
**full-lifecycle.test.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #24318 | 5:48 PM | 🟣 | Phase 6 Complete: Comprehensive Bun Worker Testing Passed | ~486 |
|
||||
| #24317 | 5:47 PM | 🔵 | Test Suite Passes with 42 Tests Across 7 Files | ~326 |
|
||||
| #24126 | 12:27 AM | 🟣 | Comprehensive Bun Runtime Migration Statistics | ~287 |
|
||||
|
||||
### Dec 13, 2025
|
||||
|
||||
**hook-execution-environments.test.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #25551 | 11:32 PM | 🟣 | Enhanced error handling system merged into main branch | ~471 |
|
||||
| #25521 | 11:05 PM | 🟣 | Hook Execution Environment Tests Created | ~529 |
|
||||
|
||||
**full-lifecycle.test.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #25518 | 10:37 PM | 🔵 | Integration Test Coverage Analyzed | ~502 |
|
||||
| #25499 | 10:30 PM | 🔵 | Full Test Suite Passes with 52 Tests | ~350 |
|
||||
|
||||
### Dec 14, 2025
|
||||
|
||||
**context-inject-early.test.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #25595 | 2:33 PM | ✅ | PR #310 merged into main branch | ~292 |
|
||||
|
||||
### Dec 18, 2025
|
||||
|
||||
**hook-execution-environments.test.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #29603 | 5:37 PM | 🔵 | Full Test Suite Results - 120/121 Tests Passing | ~424 |
|
||||
|
||||
### Dec 20, 2025
|
||||
|
||||
**hook-execution-environments.test.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #30494 | 4:43 PM | 🔵 | Test Suite Status with Bun Path Resolution Failures | ~330 |
|
||||
|
||||
### Dec 21, 2025
|
||||
|
||||
**hook-execution-environments.test.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #31371 | 6:08 PM | ✅ | Removed Five Incomplete Test Files | ~180 |
|
||||
|
||||
**full-lifecycle.test.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #31370 | 6:07 PM | ✅ | Removed 9 Mock-Based Test Files | ~286 |
|
||||
|
||||
### Dec 22, 2025
|
||||
|
||||
**context-inject-early.test.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #31855 | 6:53 PM | ✅ | テストスイートの大規模削除とテスト分析レポートの追加 | ~197 |
|
||||
| #20721 | 9:02 PM | 🔵 | Full Lifecycle Integration Test Suite | ~563 |
|
||||
</claude-mem-context>
|
||||
+1
-38
@@ -3,42 +3,5 @@
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
### Jan 3, 2026
|
||||
|
||||
**server.test.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #36583 | 9:57 PM | 🟣 | Server Test Suite Created | ~485 |
|
||||
|
||||
**error-handler.test.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #36581 | 9:57 PM | 🟣 | ErrorHandler Test Suite Created for Phase 6 | ~444 |
|
||||
|
||||
### Jan 4, 2026
|
||||
|
||||
**error-handler.test.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #36841 | 1:43 AM | 🔵 | Error Handler Testing with Custom Error Classes | ~445 |
|
||||
|
||||
### Jan 5, 2026
|
||||
|
||||
**CLAUDE.md**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #38078 | 9:54 PM | ✅ | CLAUDE.md Documentation Cleanup - 1,233 Lines Removed Across 18 Files | ~590 |
|
||||
|
||||
**server.test.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #38018 | 9:14 PM | 🟣 | Added comprehensive test coverage for worktree detection and projectRoot path resolution | ~450 |
|
||||
| #37758 | 6:25 PM | ⚖️ | Integration Test Design for Four Critical Testing Gaps | ~729 |
|
||||
| #37710 | 6:11 PM | 🔵 | Test Audit: server.test.ts Integration Tests with Minimal Mocking for HTTP Server | ~582 |
|
||||
|
||||
**error-handler.test.ts**
|
||||
| ID | Time | T | Title | Read |
|
||||
|----|------|---|-------|------|
|
||||
| #37629 | 5:36 PM | 🔵 | Comprehensive Testing Patterns Documentation Generated | ~629 |
|
||||
| #37625 | 5:35 PM | 🔵 | Bun Test Pattern for Error Classes and Mock Restoration | ~573 |
|
||||
*No recent activity*
|
||||
</claude-mem-context>
|
||||
Reference in New Issue
Block a user