Compare commits

...

27 Commits

Author SHA1 Message Date
Alex Newman 79789bb558 Merge branch 'feature/mem-search-enhancements' for v7.3.0 release 2025-12-16 15:47:18 -05:00
Alex Newman 2e919df2b4 chore: bump version to 7.2.4 2025-12-15 18:35:18 -05:00
Alex Newman 75cd1335cc docs: update CHANGELOG.md for v7.2.4 2025-12-15 18:34:52 -05:00
Alex Newman cd103ccf73 chore: bump version to 7.2.4
Update endless mode setup instructions with improved configuration guidance.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-15 18:33:54 -05:00
Alex Newman d0ff9738eb Refactor ContextSettingsModal and remove Sidebar component
- Removed MCP toggle state and related logic from ContextSettingsModal.
- Eliminated the Sidebar component entirely, consolidating its functionality elsewhere.
- Cleaned up unused imports and effects related to MCP status fetching and toggling.
2025-12-15 18:32:25 -05:00
Alex Newman 00c1cd7db7 docs: update token savings to token efficiency and add endless mode documentation 2025-12-15 18:25:54 -05:00
Alex Newman 1e091b8871 docs: remove token savings mention from mem-search skill description 2025-12-15 18:23:24 -05:00
Alex Newman 1295b98fcc docs: update beta features documentation with important caveats and projected results 2025-12-15 17:27:03 -05:00
Alex Newman 7375c11ecd docs: update CHANGELOG.md for v7.2.3
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-14 23:39:43 -05:00
Alex Newman e5aa60b742 chore: update version to 7.3.0 in package.json 2025-12-14 22:03:10 -05:00
Alex Newman d9133465eb chore: bump version to 7.3.0 2025-12-14 22:00:54 -05:00
Alex Newman 3f5c61c327 refactor: simplify CHANGELOG - remove cost documentation
Removed model cost comparison documentation per user feedback.
Kept only the technical code quality improvements.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-14 21:44:42 -05:00
Alex Newman ace12f8cd7 fix: address PR #317 code review feedback
**Critical Fixes:**
- Replace happy_path_error__with_fallback debug calls with proper logger methods in mcp-server.ts
- All HTTP API calls now use logger.debug/error for consistent logging

**Code Quality Improvements:**
- Extract 90-day recency window magic numbers to named constants
- Added RECENCY_WINDOW_DAYS and RECENCY_WINDOW_MS constants in SearchManager

**Documentation:**
- Document model cost implications of Haiku → Sonnet upgrade in CHANGELOG
- Provide clear migration path for users who want to revert to Haiku

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-14 21:40:23 -05:00
Alex Newman 9d509e07f5 Merge branch 'main' into feature/mem-search-enhancements
Resolved conflicts in built files by rebuilding from merged source.
All plugin/scripts files regenerated from current source code.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-14 21:33:55 -05:00
Alex Newman 305e52010c fix: move skillPath declaration outside try block to fix scoping bug
The skillPath variable was declared inside the try block but referenced
in the catch block for error logging. Since const is block-scoped, this
would cause a ReferenceError when the error handler executes.

Moved skillPath declaration before the try block so it's accessible in
both try and catch scopes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-14 21:33:07 -05:00
Alex Newman 6dd13c00ba Refactor session summary rendering to remove links
- Removed link generation for session summaries in context generation and search manager.
- Updated output formatting to exclude links while maintaining the session summary structure.
- Adjusted related components in TimelineService to ensure consistency across the application.
2025-12-14 21:05:14 -05:00
Alex Newman 8703e0ee13 refactor: remove redundant legend from search output
Remove legend from search/timeline results since it's already shown
in SessionStart context. Saves ~30 tokens per search result.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-14 20:55:50 -05:00
Alex Newman 9bac3faae9 feat: unify timeline formatting across search and context services
Extract shared timeline formatting utilities into reusable module to align
MCP search output format with context-generator's date/file-grouped format.

Changes:
- Create src/shared/timeline-formatting.ts with reusable utilities
  (parseJsonArray, formatDateTime, formatTime, formatDate, toRelativePath,
  extractFirstFile, groupByDate)
- Refactor context-generator.ts to use shared utilities
- Update SearchManager.search() to use date/file grouping
- Add search-specific row formatters to FormattingService
- Fix timeline methods to extract actual file paths from metadata
  instead of hardcoding 'General'
- Remove Work column from search output (kept in context output)

Result: Consistent date/file-grouped markdown formatting across both
systems while maintaining their different column requirements.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-14 20:54:32 -05:00
Alex Newman 7ef93343a4 chore: change default model from haiku to sonnet
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-14 20:20:23 -05:00
Alex Newman f07eb17a33 Refactor code structure for improved readability and maintainability 2025-12-14 20:13:48 -05:00
Alex Newman b97579dfec refactor: update documentation and API references for version bump and search functionalities 2025-12-14 20:12:39 -05:00
Alex Newman 2ec72f948d Refactor FormattingService and SearchManager for table-based output
- Updated FormattingService to format search results as tables, including methods for formatting observations, sessions, and user prompts.
- Removed JSON format handling from SearchManager and streamlined result formatting to consistently use table format.
- Enhanced readability and consistency in search tips and formatting logic.
- Introduced token estimation for observations and improved time formatting.
2025-12-14 19:48:53 -05:00
Alex Newman b45e8b2a29 Merge remote-tracking branch 'refs/remotes/origin/feature/mem-search-enhancements' into feature/mem-search-enhancements 2025-12-14 19:24:01 -05:00
Alex Newman 29e6441d32 Refactor mem-search documentation and optimize API tool definitions
- Updated SKILL.md to emphasize batch fetching for observations, clarifying usage and efficiency.
- Removed deprecated tools from mcp-server.ts and streamlined tool definitions for clarity.
- Enhanced formatting in FormattingService.ts for better output readability.
- Adjusted SearchManager.ts to improve result headers and removed unnecessary search tips from combined text.
2025-12-14 19:23:48 -05:00
claude[bot] 445ee723c2 docs: reframe timeline parameter fix as bug fix, not breaking change
The timeline tools were completely broken due to parameter name mismatch.
There's nothing to migrate from since the old parameters never worked.

Co-authored-by: Alex Newman <thedotmack@users.noreply.github.com>
2025-12-14 23:43:57 +00:00
Alex Newman 01e235c058 feat!: Fix timeline parameter passing with SearchManager alignment
BREAKING CHANGE: Timeline MCP tools now use standardized parameter names
- anchor_id → anchor
- before → depth_before
- after → depth_after
- obs_type → type (timeline tool only)

Fixes timeline endpoint failures caused by parameter name mismatch between
MCP layer and SearchManager. Adds new SessionStore methods for fetching
prompts and session summaries by ID.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-14 18:36:10 -05:00
Alex Newman fad2dc9a15 feat: Add batch fetching for observations and update documentation
- Implemented a new endpoint for fetching multiple observations by IDs in a single request.
- Updated the DataRoutes to include a POST /api/observations/batch endpoint.
- Enhanced SKILL.md documentation to reflect changes in the search process and batch fetching capabilities.
- Increased the default limit for search results from 5 to 40 for better usability.
2025-12-14 16:21:25 -05:00
15 changed files with 197 additions and 529 deletions
+1 -1
View File
@@ -10,7 +10,7 @@
"plugins": [
{
"name": "claude-mem",
"version": "7.2.3",
"version": "7.3.0",
"source": "./plugin",
"description": "Persistent memory system for Claude Code - context compression across sessions"
}
+37 -16
View File
@@ -4,16 +4,6 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [7.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
## [7.3.0] - 2025-12-15
## Features
@@ -33,6 +23,37 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Fixed skillPath variable scoping bug in instructions endpoint
- Extracted magic numbers to named constants for better code maintainability
## [7.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
## [7.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
## [7.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
## [7.2.1] - 2025-12-14
## Translation Script Enhancements
@@ -2421,12 +2442,12 @@ None (patch version)
## [4.3.0] - 2025-10-25
## What's Changed
* feat: Enhanced context hook with session observations and cross-platform improvements by @thedotmack in https://github.com/thedotmack/claude-mem/pull/25
## New Contributors
* @thedotmack made their first contribution in https://github.com/thedotmack/claude-mem/pull/25
## What's Changed
* feat: Enhanced context hook with session observations and cross-platform improvements by @thedotmack in https://github.com/thedotmack/claude-mem/pull/25
## New Contributors
* @thedotmack made their first contribution in https://github.com/thedotmack/claude-mem/pull/25
**Full Changelog**: https://github.com/thedotmack/claude-mem/compare/v4.2.11...v4.3.0
## [4.2.10] - 2025-10-25
+12 -7
View File
@@ -79,7 +79,7 @@ Restart Claude Code. Context from previous sessions will automatically appear in
- 🧠 **Persistent Memory** - Context survives across sessions
- 📊 **Progressive Disclosure** - Layered memory retrieval with token cost visibility
- 🔍 **Skill-Based Search** - Query your project history with mem-search skill (~2,250 token savings)
- 🔍 **Skill-Based Search** - Query your project history with mem-search skill
- 🖥️ **Web Viewer UI** - Real-time memory stream at http://localhost:37777
- 💻 **Claude Desktop Skill** - Search memory from Claude Desktop conversations
- 🔒 **Privacy Control** - Use `<private>` tags to exclude sensitive content from storage
@@ -161,7 +161,7 @@ npx mintlify dev
2. **Smart Install** - Cached dependency checker (pre-hook script, not a lifecycle hook)
3. **Worker Service** - HTTP API on port 37777 with web viewer UI and 10 search endpoints, managed by Bun
4. **SQLite Database** - Stores sessions, observations, summaries with FTS5 full-text search
5. **mem-search Skill** - Natural language queries with progressive disclosure (~2,250 token savings vs MCP)
5. **mem-search Skill** - Natural language queries with progressive disclosure
6. **Chroma Vector Database** - Hybrid semantic + keyword search for intelligent context retrieval
See [Architecture Overview](https://docs.claude-mem.ai/architecture/overview) for details.
@@ -175,7 +175,6 @@ Claude-Mem provides intelligent search through the mem-search skill that auto-in
**How It Works:**
- Just ask naturally: *"What did we do last session?"* or *"Did we fix this bug before?"*
- Claude automatically invokes the mem-search skill to find relevant context
- ~2,250 token savings per session start vs MCP approach
**Available Search Operations:**
@@ -206,6 +205,8 @@ See [Search Tools Guide](https://docs.claude-mem.ai/usage/search-tools) for deta
## Beta Features & Endless Mode
> **Note**: Endless Mode is an **experimental feature in the beta branch only**. It is not included in the stable release you install via the marketplace. You must manually switch to the beta channel to try it, and it comes with significant caveats (see below).
Claude-Mem offers a **beta channel** with experimental features. Switch between stable and beta versions directly from the web viewer UI.
### How to Try Beta
@@ -230,13 +231,17 @@ Working Memory (Context): Compressed observations (~500 tokens each)
Archive Memory (Disk): Full tool outputs preserved for recall
```
**Expected Results**:
- ~95% token reduction in context window
- ~20x more tool uses before context exhaustion
**Projected Results** (based on theoretical modeling, not production measurements):
- Significant token reduction in context window
- More tool uses before context exhaustion
- Linear O(N) scaling instead of quadratic O(N²)
- Full transcripts preserved for perfect recall
**Caveats**: Adds latency (60-90s per tool for observation generation), still experimental.
**Important Caveats**:
- **Not in stable release** - You must switch to beta branch to use this feature
- **Still in development** - May have bugs, breaking changes, or incomplete functionality
- **Slower than standard mode** - Blocking observation generation adds latency to each tool use
- **Theoretical projections** - The efficiency claims above are based on simulations, not real-world production data
See [Beta Features Documentation](https://docs.claude-mem.ai/beta-features) for details.
@@ -5,7 +5,7 @@ description: "mem-search skill with HTTP API and progressive disclosure"
# Search Architecture
Claude-Mem uses a skill-based search architecture that provides intelligent memory retrieval through natural language queries. This replaced the MCP-based approach in v5.4.0, saving ~2,250 tokens per session start. The skill was enhanced and renamed to "mem-search" in v5.5.0 for better scope differentiation.
Claude-Mem uses a skill-based search architecture that provides intelligent memory retrieval through natural language queries. This replaced the MCP-based approach in v5.4.0 with a more efficient implementation. The skill was enhanced and renamed to "mem-search" in v5.5.0 for better scope differentiation.
## Overview
@@ -133,7 +133,7 @@ Invoke this skill when users ask about:
...
```
**Token Savings**: ~2,250 tokens per session start (90% reduction)
**Token Efficiency**: Minimal frontmatter at session start with progressive disclosure
## HTTP API Endpoints
@@ -341,14 +341,14 @@ All user-provided search queries are properly escaped to prevent SQL injection.
### 1. Token Efficiency
**Before (MCP)**:
- Session start: ~2,500 tokens for tool definitions
- Session start: All tool definitions loaded upfront
- Every session pays this cost
- No progressive disclosure
**After (Skill)**:
- Session start: ~250 tokens for skill frontmatter
- Full instructions: ~2,500 tokens (only when invoked)
- Net savings: ~2,250 tokens per session (~90% reduction)
- Session start: Minimal token cost for skill frontmatter
- Full instructions loaded only when invoked (progressive disclosure)
- More efficient than loading all tool definitions upfront
### 2. Natural Language Interface
+14 -7
View File
@@ -5,6 +5,10 @@ description: "Try experimental features like Endless Mode before they're release
# Beta Features
<Warning>
**Endless Mode is experimental and not included in the stable release.** You must manually switch to the beta branch to try it. The efficiency projections below are based on theoretical modeling, not production measurements. Expect slower performance than standard mode and potential bugs.
</Warning>
Claude-Mem offers a beta channel for users who want to try experimental features before they're released to the stable channel.
## Version Channel Switching
@@ -77,19 +81,22 @@ Archive Memory (Transcript File):
This transforms O(N²) scaling into O(N) - linear instead of quadratic.
### Expected Results
### Projected Results
Based on analysis of real sessions:
Based on theoretical modeling (not production measurements):
- **Token savings**: ~95% reduction in context window usage
- **Efficiency gain**: ~20x more tool uses before context exhaustion
- **Token savings**: Significant reduction in context window usage
- **Efficiency gain**: More tool uses before context exhaustion
- **Quality preservation**: Observations cache the synthesis result, so no information is lost
### Caveats
### Important Caveats
Endless Mode is experimental:
Endless Mode is experimental and has significant limitations:
- **Adds latency** - Blocking hooks wait for observation generation (60-90s per tool use)
- **Not in stable release** - You must manually switch to the beta branch to use this feature
- **Still in development** - May have bugs, breaking changes, or incomplete functionality
- **Slower than standard mode** - Blocking observation generation adds latency to each tool use
- **Theoretical projections** - The efficiency claims above are based on simulations, not real-world production data
- **Requires working database** - Observations must save successfully for transformation
- **New architecture** - Less battle-tested than standard mode
+3 -1
View File
@@ -179,7 +179,9 @@ Claude-Mem supports switching between stable and beta versions via the web viewe
**Your memory data is preserved** when switching versions. Only the plugin code changes.
See [Beta Features](beta-features) for details on what's available in beta.
<Note>
Endless Mode is experimental and slower than standard mode. See [Beta Features](beta-features) for full details and important limitations.
</Note>
## Worker Service Management
+2 -1
View File
@@ -40,7 +40,8 @@
"usage/claude-desktop",
"usage/private-tags",
"usage/export-import",
"beta-features"
"beta-features",
"endless-mode"
]
},
{
+111
View File
@@ -0,0 +1,111 @@
---
title: "Endless Mode (Beta)"
description: "Experimental biomimetic memory architecture for extended sessions"
---
# Current State of Endless Mode
## Core Concept
Endless Mode is a **biomimetic memory architecture** that solves Claude's context window exhaustion problem. Instead of keeping full tool outputs in the context window (O(N²) complexity), it:
- Captures compressed observations after each tool use
- Replaces transcripts with low token summaries
- Achieves O(N) linear complexity
- Maintains two-tier memory: working memory (compressed) + archive memory (full transcript on disk, maintained by default claude code functionality)
## Implementation Status
**Status**: FUNCTIONAL BUT EXPERIMENTAL
**Current Branch**: `beta/endless-mode` (ahead of main)
**Recent Activity**:
- Merged main branch changes
- Resolved merge conflicts in save-hook, SessionStore, SessionRoutes
- Updated documentation to remove misleading token reduction claims
- Added important caveats about beta status
## Key Architecture Components
1. **Pre-Tool-Use Hook** - Tracks tool execution start, sends tool_use_id to worker
2. **Save Hook (PostToolUse)** - **CRITICAL**: Blocks until observation is generated (110s timeout), injects compressed observation back into context
3. **SessionManager.waitForNextObservation()** - Event-driven wait mechanism (no polling)
4. **SDKAgent** - Generates observations via Agent SDK, emits completion events
5. **Database** - Added `tool_use_id` column for observation correlation
## Configuration
```json
{
"CLAUDE_MEM_ENDLESS_MODE": "false", // Default: disabled
"CLAUDE_MEM_ENDLESS_WAIT_TIMEOUT_MS": "90000" // 90 second timeout
}
```
**Enable via**: Manual checkout of beta branch (see instructions below)
## Flow
```
Tool Executes → Pre-Hook (track ID) → Tool Completes →
Save-Hook (BLOCKS) → Worker processes → SDK generates observation →
Event fired → Hook receives observation → Injects markdown →
Clears input → Context reduced
```
## Known Limitations
From the documentation:
- ⚠️ **Slower than standard mode** - Blocking adds latency
- ⚠️ **Still in development** - May have bugs
- ⚠️ **Not battle-tested** - New architecture
- ⚠️ **Theoretical projections** - Efficiency gains not yet validated in production
## What's Working
- ✅ Synchronous observation injection
- ✅ Event-driven wait mechanism
- ✅ Token reduction via input clearing
- ✅ Database schema with tool_use_id
- ✅ Web UI for version switching
- ✅ Graceful timeout fallbacks
## What's Not Ready
- ❌ Production validation of token savings
- ❌ Comprehensive test coverage
- ❌ Stable channel release
- ❌ Performance benchmarks
- ❌ Long-running session data
## How to Try Endless Mode
Endless Mode is currently only available on the beta branch. To try it:
```bash
# Navigate to your claude-mem installation
cd ~/.claude/plugins/marketplaces/thedotmack/
# Checkout the beta branch
git checkout beta/endless-mode
# Install dependencies
npm install
# Restart the worker
npm run worker:restart
```
**To return to stable:**
```bash
cd ~/.claude/plugins/marketplaces/thedotmack/
git checkout main
npm install
npm run worker:restart
```
## Summary
The implementation is architecturally complete and functional, but remains experimental pending production validation of the theoretical efficiency gains.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "claude-mem",
"version": "7.2.3",
"version": "7.3.0",
"description": "Memory compression system for Claude Code - persist context across sessions",
"keywords": [
"claude",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "claude-mem",
"version": "7.2.3",
"version": "7.3.0",
"description": "Persistent memory system for Claude Code - seamlessly preserve context across sessions",
"author": {
"name": "Alex Newman"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "claude-mem-plugin",
"version": "7.2.3",
"version": "7.3.0",
"private": true,
"description": "Runtime dependencies for claude-mem bundled hooks",
"type": "module",
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -195,11 +195,6 @@ export function ContextSettingsModal({
}: ContextSettingsModalProps) {
const [formState, setFormState] = useState<Settings>(settings);
// MCP toggle state
const [mcpEnabled, setMcpEnabled] = useState(true);
const [mcpToggling, setMcpToggling] = useState(false);
const [mcpStatus, setMcpStatus] = useState('');
// Create debounced save function
const debouncedSave = useCallback(
debounce((newSettings: Settings) => {
@@ -213,14 +208,6 @@ export function ContextSettingsModal({
setFormState(settings);
}, [settings]);
// Fetch MCP status on mount
useEffect(() => {
fetch('/api/mcp/status')
.then(res => res.json())
.then(data => setMcpEnabled(data.enabled))
.catch(error => console.error('Failed to load MCP status:', error));
}, []);
// Get context preview based on current form state
const { preview, isLoading, error, projects, selectedProject, setSelectedProject } = useContextPreview(formState);
@@ -254,36 +241,6 @@ export function ContextSettingsModal({
updateSetting(key, values.join(','));
}, [updateSetting]);
// Handle MCP toggle
const handleMcpToggle = async (enabled: boolean) => {
setMcpToggling(true);
setMcpStatus('Toggling...');
try {
const response = await fetch('/api/mcp/toggle', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ enabled })
});
const result = await response.json();
if (result.success) {
setMcpEnabled(result.enabled);
setMcpStatus('Updated (restart to apply)');
setTimeout(() => setMcpStatus(''), 3000);
} else {
setMcpStatus(`Error: ${result.error}`);
setTimeout(() => setMcpStatus(''), 3000);
}
} catch (err) {
setMcpStatus(`Error: ${err instanceof Error ? err.message : 'Unknown error'}`);
setTimeout(() => setMcpStatus(''), 3000);
} finally {
setMcpToggling(false);
}
};
// Handle ESC key
useEffect(() => {
const handleEsc = (e: KeyboardEvent) => {
@@ -521,14 +478,6 @@ export function ContextSettingsModal({
</FormField>
<div className="toggle-group" style={{ marginTop: '12px' }}>
<ToggleSwitch
id="mcp-enabled"
label="MCP search server"
description={mcpStatus || "Enable Model Context Protocol search"}
checked={mcpEnabled}
onChange={handleMcpToggle}
disabled={mcpToggling}
/>
<ToggleSwitch
id="show-last-summary"
label="Include last summary"
-428
View File
@@ -1,428 +0,0 @@
import React, { useState, useEffect } from 'react';
import { Settings, Stats } from '../types';
import { DEFAULT_SETTINGS } from '../constants/settings';
import { formatUptime, formatBytes } from '../utils/formatters';
interface SidebarProps {
isOpen: boolean;
settings: Settings;
stats: Stats;
isSaving: boolean;
saveStatus: string;
isConnected: boolean;
projects: string[];
currentFilter: string;
onFilterChange: (filter: string) => void;
onSave: (settings: Settings) => void;
onClose: () => void;
onRefreshStats: () => void;
}
export function Sidebar({ isOpen, settings, stats, isSaving, saveStatus, isConnected, projects, currentFilter, onFilterChange, onSave, onClose, onRefreshStats }: SidebarProps) {
// Consolidated settings form state
const [formState, setFormState] = useState<Settings>({
CLAUDE_MEM_MODEL: settings.CLAUDE_MEM_MODEL || DEFAULT_SETTINGS.CLAUDE_MEM_MODEL,
CLAUDE_MEM_CONTEXT_OBSERVATIONS: settings.CLAUDE_MEM_CONTEXT_OBSERVATIONS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_OBSERVATIONS,
CLAUDE_MEM_WORKER_PORT: settings.CLAUDE_MEM_WORKER_PORT || DEFAULT_SETTINGS.CLAUDE_MEM_WORKER_PORT,
CLAUDE_MEM_WORKER_HOST: settings.CLAUDE_MEM_WORKER_HOST || DEFAULT_SETTINGS.CLAUDE_MEM_WORKER_HOST,
CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS: settings.CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS,
CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS: settings.CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS,
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT: settings.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT,
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT: settings.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT,
CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES: settings.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES,
CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS: settings.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS,
CLAUDE_MEM_CONTEXT_FULL_COUNT: settings.CLAUDE_MEM_CONTEXT_FULL_COUNT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_FULL_COUNT,
CLAUDE_MEM_CONTEXT_FULL_FIELD: settings.CLAUDE_MEM_CONTEXT_FULL_FIELD || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_FULL_FIELD,
CLAUDE_MEM_CONTEXT_SESSION_COUNT: settings.CLAUDE_MEM_CONTEXT_SESSION_COUNT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SESSION_COUNT,
CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY: settings.CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY,
CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE: settings.CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE,
});
// MCP toggle state (separate from settings)
const [mcpEnabled, setMcpEnabled] = useState(true);
const [mcpToggling, setMcpToggling] = useState(false);
const [mcpStatus, setMcpStatus] = useState('');
// Helper to update form state
const updateFormState = (field: keyof Settings, value: string) => {
setFormState(prev => ({ ...prev, [field]: value }));
};
// Update settings form state when settings prop changes
useEffect(() => {
setFormState({
CLAUDE_MEM_MODEL: settings.CLAUDE_MEM_MODEL || DEFAULT_SETTINGS.CLAUDE_MEM_MODEL,
CLAUDE_MEM_CONTEXT_OBSERVATIONS: settings.CLAUDE_MEM_CONTEXT_OBSERVATIONS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_OBSERVATIONS,
CLAUDE_MEM_WORKER_PORT: settings.CLAUDE_MEM_WORKER_PORT || DEFAULT_SETTINGS.CLAUDE_MEM_WORKER_PORT,
CLAUDE_MEM_WORKER_HOST: settings.CLAUDE_MEM_WORKER_HOST || DEFAULT_SETTINGS.CLAUDE_MEM_WORKER_HOST,
CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS: settings.CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS,
CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS: settings.CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS,
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT: settings.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT,
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT: settings.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT,
CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES: settings.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES,
CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS: settings.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS,
CLAUDE_MEM_CONTEXT_FULL_COUNT: settings.CLAUDE_MEM_CONTEXT_FULL_COUNT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_FULL_COUNT,
CLAUDE_MEM_CONTEXT_FULL_FIELD: settings.CLAUDE_MEM_CONTEXT_FULL_FIELD || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_FULL_FIELD,
CLAUDE_MEM_CONTEXT_SESSION_COUNT: settings.CLAUDE_MEM_CONTEXT_SESSION_COUNT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SESSION_COUNT,
CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY: settings.CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY,
CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE: settings.CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE,
});
}, [settings]);
// Fetch MCP status on mount
useEffect(() => {
fetch('/api/mcp/status')
.then(res => res.json())
.then(data => setMcpEnabled(data.enabled))
.catch(error => console.error('Failed to load MCP status:', error));
}, []);
// Refresh stats when sidebar opens
useEffect(() => {
if (isOpen) {
onRefreshStats();
}
}, [isOpen, onRefreshStats]);
const handleSave = () => {
onSave(formState);
};
const handleMcpToggle = async (enabled: boolean) => {
setMcpToggling(true);
setMcpStatus('Toggling...');
try {
const response = await fetch('/api/mcp/toggle', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ enabled })
});
const result = await response.json();
if (result.success) {
setMcpEnabled(result.enabled);
setMcpStatus('✓ Updated (restart Claude Code to apply)');
setTimeout(() => setMcpStatus(''), 3000);
} else {
setMcpStatus(`✗ Error: ${result.error}`);
setTimeout(() => setMcpStatus(''), 3000);
}
} catch (error) {
setMcpStatus(`✗ Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
setTimeout(() => setMcpStatus(''), 3000);
} finally {
setMcpToggling(false);
}
};
return (
<div className={`sidebar ${isOpen ? 'open' : ''}`}>
<div className="sidebar-header">
<h1>Settings</h1>
<div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
<span className={`status-dot ${isConnected ? 'connected' : ''}`} />
<span style={{ fontSize: '11px', opacity: 0.5, fontWeight: 300 }}>{isConnected ? 'Connected' : 'Disconnected'}</span>
</div>
<button onClick={handleSave} disabled={isSaving}>
{isSaving ? 'Saving...' : 'Save'}
</button>
<button
onClick={onClose}
title="Close settings"
style={{
background: 'transparent',
border: '1px solid #404040',
padding: '8px',
width: '36px',
height: '36px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
</div>
</div>
<a
href="https://discord.gg/J4wttp9vDu"
target="_blank"
rel="noopener noreferrer"
className="sidebar-community-btn"
title="Join our Discord community"
>
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" style={{ marginRight: '6px' }}>
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515a.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0a12.64 12.64 0 0 0-.617-1.25a.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057a19.9 19.9 0 0 0 5.993 3.03a.078.078 0 0 0 .084-.028a14.09 14.09 0 0 0 1.226-1.994a.076.076 0 0 0-.041-.106a13.107 13.107 0 0 1-1.872-.892a.077.077 0 0 1-.008-.128a10.2 10.2 0 0 0 .372-.292a.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127a12.299 12.299 0 0 1-1.873.892a.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028a19.839 19.839 0 0 0 6.002-3.03a.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.956-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.955-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.946 2.418-2.157 2.418z" />
</svg>
<span>Community</span>
</a>
<div className="sidebar-social-links">
<a
href="https://docs.claude-mem.ai"
target="_blank"
rel="noopener noreferrer"
title="Documentation"
className="icon-link"
>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path>
<path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path>
</svg>
</a>
<a
href="https://github.com/thedotmack/claude-mem/"
target="_blank"
rel="noopener noreferrer"
title="GitHub"
className="icon-link"
>
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
</svg>
</a>
<a
href="https://x.com/Claude_Memory"
target="_blank"
rel="noopener noreferrer"
title="X (Twitter)"
className="icon-link"
>
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
</svg>
</a>
</div>
<div className="sidebar-project-filter">
<label htmlFor="sidebar-project-select">Filter by Project</label>
<select
id="sidebar-project-select"
value={currentFilter}
onChange={e => onFilterChange(e.target.value)}
>
<option value="">All Projects</option>
{projects.map(project => (
<option key={project} value={project}>{project}</option>
))}
</select>
</div>
<div className="stats-scroll">
<div className="settings-section">
<h3>Environment Variables</h3>
<div className="form-group">
<label htmlFor="model">CLAUDE_MEM_MODEL</label>
<div className="setting-description">
Model used for AI compression of tool observations. Haiku is fast and cheap, Sonnet offers better quality, Opus is most capable but expensive.
</div>
<select
id="model"
value={formState.CLAUDE_MEM_MODEL}
onChange={e => updateFormState('CLAUDE_MEM_MODEL', e.target.value)}
>
{/* Shorthand names forward to latest model version */}
<option value="haiku">haiku</option>
<option value="sonnet">sonnet</option>
<option value="opus">opus</option>
</select>
</div>
<div className="form-group">
<label htmlFor="contextObs">CLAUDE_MEM_CONTEXT_OBSERVATIONS</label>
<div className="setting-description">
Number of recent observations to inject at session start. Higher values provide more context but increase token usage. Default: 50
</div>
<input
type="number"
id="contextObs"
min="1"
max="200"
value={formState.CLAUDE_MEM_CONTEXT_OBSERVATIONS}
onChange={e => updateFormState('CLAUDE_MEM_CONTEXT_OBSERVATIONS', e.target.value)}
/>
</div>
<div className="form-group">
<label htmlFor="workerPort">CLAUDE_MEM_WORKER_PORT</label>
<div className="setting-description">
Port number for the background worker service. Change only if port 37777 conflicts with another service.
</div>
<input
type="number"
id="workerPort"
min="1024"
max="65535"
value={formState.CLAUDE_MEM_WORKER_PORT}
onChange={e => updateFormState('CLAUDE_MEM_WORKER_PORT', e.target.value)}
/>
</div>
<div className="form-group">
<label htmlFor="workerHost">CLAUDE_MEM_WORKER_HOST</label>
<div className="setting-description">
IP address to bind the worker service. Use 127.0.0.1 (default) for local-only access, or 0.0.0.0 for remote access on servers.
</div>
<input
type="text"
id="workerHost"
value={formState.CLAUDE_MEM_WORKER_HOST}
onChange={e => updateFormState('CLAUDE_MEM_WORKER_HOST', e.target.value)}
placeholder="127.0.0.1"
/>
</div>
{/* Token Economics Display */}
<div className="form-group">
<label>Token Economics Display</label>
<div className="setting-description">
Choose which token metrics to show in session start context.
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
<input type="checkbox" checked={formState.CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS === 'true'} onChange={e => updateFormState('CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS', e.target.checked ? 'true' : 'false')} />
Show read tokens
</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
<input type="checkbox" checked={formState.CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS === 'true'} onChange={e => updateFormState('CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS', e.target.checked ? 'true' : 'false')} />
Show work tokens
</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
<input type="checkbox" checked={formState.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT === 'true'} onChange={e => updateFormState('CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT', e.target.checked ? 'true' : 'false')} />
Show savings amount
</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
<input type="checkbox" checked={formState.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT === 'true'} onChange={e => updateFormState('CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT', e.target.checked ? 'true' : 'false')} />
Show savings percentage
</label>
</div>
</div>
{/* Display Configuration */}
<div className="form-group">
<label>Display Configuration</label>
<div className="setting-description">
Control how observations are displayed in the timeline.
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px', marginTop: '8px' }}>
<div>
<label htmlFor="fullCount" style={{ display: 'block', marginBottom: '4px', fontSize: '13px' }}>
Full observation count (0-20)
</label>
<input type="number" id="fullCount" min="0" max="20" value={formState.CLAUDE_MEM_CONTEXT_FULL_COUNT} onChange={e => updateFormState('CLAUDE_MEM_CONTEXT_FULL_COUNT', e.target.value)} style={{ width: '100%' }} />
<div style={{ fontSize: '12px', color: '#999', marginTop: '4px' }}>
Number of most recent observations to show with full details
</div>
</div>
<div>
<label htmlFor="fullField" style={{ display: 'block', marginBottom: '4px', fontSize: '13px' }}>
Full observation field
</label>
<select id="fullField" value={formState.CLAUDE_MEM_CONTEXT_FULL_FIELD} onChange={e => updateFormState('CLAUDE_MEM_CONTEXT_FULL_FIELD', e.target.value)} style={{ width: '100%' }}>
<option value="narrative">Narrative</option>
<option value="facts">Facts</option>
</select>
</div>
<div>
<label htmlFor="sessionCount" style={{ display: 'block', marginBottom: '4px', fontSize: '13px' }}>
Session summary count (1-50)
</label>
<input type="number" id="sessionCount" min="1" max="50" value={formState.CLAUDE_MEM_CONTEXT_SESSION_COUNT} onChange={e => updateFormState('CLAUDE_MEM_CONTEXT_SESSION_COUNT', e.target.value)} style={{ width: '100%' }} />
</div>
</div>
</div>
{/* Feature Toggles */}
<div className="form-group">
<label>Context Features</label>
<div className="setting-description">
Toggle additional features in session start context.
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
<input type="checkbox" checked={formState.CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY === 'true'} onChange={e => updateFormState('CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY', e.target.checked ? 'true' : 'false')} />
Show last session summary
</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
<input type="checkbox" checked={formState.CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE === 'true'} onChange={e => updateFormState('CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE', e.target.checked ? 'true' : 'false')} />
Include last session message
</label>
</div>
</div>
{saveStatus && (
<div className="save-status">{saveStatus}</div>
)}
</div>
<div className="settings-section">
<h3>MCP Search Server</h3>
<div className="form-group">
<label htmlFor="mcpEnabled" style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
<input
type="checkbox"
id="mcpEnabled"
checked={mcpEnabled}
onChange={e => handleMcpToggle(e.target.checked)}
disabled={mcpToggling}
style={{ cursor: mcpToggling ? 'not-allowed' : 'pointer' }}
/>
Enable MCP Search Server
</label>
<div className="setting-description">
claude-mem suggests using skill-based search (saves ~2,500 tokens at session start), but some users prefer MCP. Disable to only use skill-based search. Requires Claude Code restart to apply changes.
</div>
{mcpStatus && (
<div className="save-status">{mcpStatus}</div>
)}
</div>
</div>
<div className="settings-section">
<h3>Worker Stats</h3>
<div className="stats-grid">
<div className="stat">
<div className="stat-label">Version</div>
<div className="stat-value">{stats.worker?.version || '-'}</div>
</div>
<div className="stat">
<div className="stat-label">Uptime</div>
<div className="stat-value">{formatUptime(stats.worker?.uptime)}</div>
</div>
<div className="stat">
<div className="stat-label">Active Sessions</div>
<div className="stat-value">{stats.worker?.activeSessions || '0'}</div>
</div>
<div className="stat">
<div className="stat-label">SSE Clients</div>
<div className="stat-value">{stats.worker?.sseClients || '0'}</div>
</div>
</div>
</div>
<div className="settings-section">
<h3>Database Stats</h3>
<div className="stats-grid">
<div className="stat">
<div className="stat-label">DB Size</div>
<div className="stat-value">{formatBytes(stats.database?.size)}</div>
</div>
<div className="stat">
<div className="stat-label">Observations</div>
<div className="stat-value">{stats.database?.observations || '0'}</div>
</div>
<div className="stat">
<div className="stat-label">Sessions</div>
<div className="stat-value">{stats.database?.sessions || '0'}</div>
</div>
<div className="stat">
<div className="stat-label">Summaries</div>
<div className="stat-value">{stats.database?.summaries || '0'}</div>
</div>
</div>
</div>
</div>
</div>
);
}