[Config] Add coding guidelines and code-fixer agent

Major additions:
- Add CODING_GUIDELINES.md with "NO EMOJIS" rule
- Create code-fixer agent for automated violation fixes
- Add offline mode v2 hooks with local caching/queue
- Add periodic context save with invisible Task Scheduler setup
- Add agent coordination rules and database connection docs

Infrastructure:
- Update hooks: task-complete-v2, user-prompt-submit-v2
- Add periodic_save_check.py for auto-save every 5min
- Add PowerShell scripts: setup_periodic_save.ps1, update_to_invisible.ps1
- Add sync-contexts script for queue synchronization

Documentation:
- OFFLINE_MODE.md, PERIODIC_SAVE_INVISIBLE_SETUP.md
- Migration procedures and verification docs
- Fix flashing window guide

Updates:
- Update agent configs (backup, code-review, coding, database, gitea, testing)
- Update claude.md with coding guidelines reference
- Update .gitignore for new cache/queue directories

Status: Pre-automated-fixer baseline commit

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-17 12:51:43 -07:00
parent 390b10b32c
commit 25f3759ecc
52 changed files with 8692 additions and 53 deletions

View File

@@ -0,0 +1,5 @@
{
"active_seconds": 3240,
"last_update": "2026-01-17T19:51:24.350999+00:00",
"last_save": null
}

View File

@@ -0,0 +1,272 @@
# Agent Coordination Rules
**CRITICAL: Main Claude is a COORDINATOR, not an executor**
---
## Core Principle
**Main Claude Instance:**
- Coordinates work between user and agents
- Makes decisions and plans
- Presents concise results to user
- **NEVER performs database operations directly**
- **NEVER makes direct API calls to ClaudeTools API**
**Agents:**
- Execute specific tasks (database, coding, testing, etc.)
- Return concise summaries
- Preserve Main Claude's context space
---
## Database Operations - ALWAYS Use Database Agent
### ❌ WRONG (What I Was Doing)
```bash
# Main Claude making direct queries
ssh guru@172.16.3.30 "mysql -u claudetools ... SELECT ..."
curl http://172.16.3.30:8001/api/conversation-contexts ...
```
### ✅ CORRECT (What Should Happen)
```
Main Claude → Task tool → Database Agent → Returns summary
```
**Example:**
```
User: "How many contexts are saved?"
Main Claude: "Let me check the database"
Launches Database Agent with task: "Count conversation_contexts in database"
Database Agent: Queries database, returns: "7 contexts found"
Main Claude to User: "There are 7 contexts saved in the database"
```
---
## Agent Responsibilities
### Database Agent (`.claude/agents/database.md`)
**ONLY agent authorized for database operations**
**Handles:**
- All SELECT, INSERT, UPDATE, DELETE queries
- Context storage and retrieval
- Data validation and integrity
- Transaction management
- Query optimization
**Returns:** Concise summaries, not raw SQL results
**When to use:**
- Saving contexts to database
- Retrieving contexts from database
- Checking record counts
- Any database operation
### Coding Agent (`.claude/agents/coding.md`)
**Handles code writing and modifications**
**When to use:**
- Writing new code
- Modifying existing code
- Creating scripts
### Testing Agent (`.claude/agents/testing.md`)
**Handles test execution**
**When to use:**
- Running tests
- Executing validation scripts
- Performance testing
### Code Review Agent (`.claude/agents/code-review.md`)
**Reviews code quality**
**When to use:**
- After significant code changes
- Before committing
### Gitea Agent (`.claude/agents/gitea.md`)
**Handles Git operations**
**When to use:**
- Git commits
- Push to remote
- Branch management
### Backup Agent (`.claude/agents/backup.md`)
**Manages backups**
**When to use:**
- Creating backups
- Restoring data
- Backup verification
---
## Violation Examples from This Session
### ❌ Violation 1: Direct Database Queries
```bash
ssh guru@172.16.3.30 "mysql ... SELECT COUNT(*) FROM conversation_contexts"
```
**Should have been:** Database Agent task
### ❌ Violation 2: Direct API Calls
```bash
curl -X POST http://172.16.3.30:8001/api/conversation-contexts ...
```
**Should have been:** Database Agent task
### ❌ Violation 3: Direct Context Creation
```bash
curl ... -d '{"context_type": "session_summary", ...}'
```
**Should have been:** Database Agent task
---
## Correct Coordination Flow
### Example: Save Context to Database
**User Request:** "Save the current context"
**Main Claude Actions:**
1. ✅ Summarize what needs to be saved
2. ✅ Launch Database Agent with task:
```
"Save session context to database:
- Title: [summary]
- Dense summary: [compressed context]
- Tags: [relevant tags]
- Score: 8.5"
```
3. ✅ Receive agent response: "Context saved with ID abc-123"
4. ✅ Tell user: "Context saved successfully"
**What Main Claude Does NOT Do:**
- ❌ Make direct curl calls
- ❌ Make direct SQL queries
- ❌ Return raw database results to user
---
## Example: Retrieve Contexts
**User Request:** "What contexts do we have about offline mode?"
**Main Claude Actions:**
1. ✅ Launch Database Agent with task:
```
"Search conversation_contexts for entries related to 'offline mode'.
Return: titles, scores, and brief summaries of top 5 results"
```
2. ✅ Receive agent summary:
```
Found 3 contexts:
1. "Offline Mode Implementation" (score 9.5)
2. "Offline Mode Testing" (score 8.0)
3. "Offline Mode Documentation" (score 7.5)
```
3. ✅ Present to user in conversational format
**What Main Claude Does NOT Do:**
- ❌ Query API directly
- ❌ Show raw JSON responses
- ❌ Execute SQL
---
## Benefits of Agent Architecture
### Context Preservation
- Main Claude's context not polluted with raw data
- Can handle longer conversations
- Focus on coordination, not execution
### Separation of Concerns
- Database Agent handles data integrity
- Coding Agent handles code quality
- Main Claude handles user interaction
### Scalability
- Agents can run in parallel
- Each has full context window for their task
- Complex operations don't bloat main context
---
## Enforcement
### Before Making ANY Database Operation:
**Ask yourself:**
1. Am I about to query the database directly? → ❌ STOP
2. Am I about to call the ClaudeTools API? → ❌ STOP
3. Should the Database Agent handle this? → ✅ USE AGENT
### When to Launch Database Agent:
- Saving any data (contexts, tasks, sessions, etc.)
- Retrieving any data from database
- Counting records
- Searching contexts
- Updating existing records
- Deleting records
- Any SQL operation
---
## Going Forward
**Main Claude Responsibilities:**
- ✅ Coordinate with user
- ✅ Make decisions about what to do
- ✅ Launch appropriate agents
- ✅ Synthesize agent results for user
- ✅ Plan and design solutions
**Main Claude Does NOT:**
- ❌ Query database directly
- ❌ Make API calls to ClaudeTools API
- ❌ Execute code (unless simple demonstration)
- ❌ Run tests (use Testing Agent)
- ❌ Commit to git (use Gitea Agent)
---
## Quick Reference
| Operation | Handler |
|-----------|---------|
| Save context | Database Agent |
| Retrieve contexts | Database Agent |
| Count records | Database Agent |
| Write code | Coding Agent |
| Run tests | Testing Agent |
| Review code | Code Review Agent |
| Git operations | Gitea Agent |
| Backups | Backup Agent |
| **User interaction** | **Main Claude** |
| **Coordination** | **Main Claude** |
| **Decision making** | **Main Claude** |
---
**Remember: Main Claude = Coordinator, not Executor**
**When in doubt, use an agent!**
---
**Created:** 2026-01-17
**Purpose:** Ensure proper agent-based architecture
**Status:** Mandatory guideline for all future operations

View File

@@ -0,0 +1,428 @@
# ClaudeTools - Coding Guidelines
## General Principles
These guidelines ensure code quality, consistency, and maintainability across the ClaudeTools project.
---
## Character Encoding and Text
### NO EMOJIS - EVER
**Rule:** Never use emojis in any code files, including:
- Python scripts (.py)
- PowerShell scripts (.ps1)
- Bash scripts (.sh)
- Configuration files
- Documentation within code
- Log messages
- Output strings
**Rationale:**
- Emojis cause encoding issues (UTF-8 vs ASCII)
- PowerShell parsing errors with special Unicode characters
- Cross-platform compatibility problems
- Terminal rendering inconsistencies
- Version control diff issues
**Instead of emojis, use:**
```powershell
# BAD - causes parsing errors
Write-Host "✓ Success!"
Write-Host "⚠ Warning!"
# GOOD - ASCII text markers
Write-Host "[OK] Success!"
Write-Host "[SUCCESS] Task completed!"
Write-Host "[WARNING] Check settings!"
Write-Host "[ERROR] Failed to connect!"
```
**Allowed in:**
- User-facing web UI (where Unicode is properly handled)
- Database content (with proper UTF-8 encoding)
- Markdown documentation (README.md, etc.) - use sparingly
---
## Python Code Standards
### Style
- Follow PEP 8 style guide
- Use 4 spaces for indentation (no tabs)
- Maximum line length: 100 characters (relaxed from 79)
- Use type hints for function parameters and return values
### Imports
```python
# Standard library imports
import os
import sys
from datetime import datetime
# Third-party imports
from fastapi import FastAPI
from sqlalchemy import Column
# Local imports
from api.models import User
from api.utils import encrypt_data
```
### Naming Conventions
- Classes: `PascalCase` (e.g., `UserService`, `CredentialModel`)
- Functions/methods: `snake_case` (e.g., `get_user`, `create_session`)
- Constants: `UPPER_SNAKE_CASE` (e.g., `API_BASE_URL`, `MAX_RETRIES`)
- Private methods: `_leading_underscore` (e.g., `_internal_helper`)
---
## PowerShell Code Standards
### Style
- Use 4 spaces for indentation
- Use PascalCase for variables: `$TaskName`, `$PythonPath`
- Use approved verbs for functions: `Get-`, `Set-`, `New-`, `Remove-`
### Error Handling
```powershell
# Always use -ErrorAction for cmdlets that might fail
$Task = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
if (-not $Task) {
Write-Host "[ERROR] Task not found"
exit 1
}
```
### Output
```powershell
# Use clear status markers
Write-Host "[INFO] Starting process..."
Write-Host "[SUCCESS] Task completed"
Write-Host "[ERROR] Failed to connect"
Write-Host "[WARNING] Configuration missing"
```
---
## Bash Script Standards
### Style
- Use 2 spaces for indentation
- Always use `#!/bin/bash` shebang
- Quote all variables: `"$variable"` not `$variable`
- Use `set -e` for error handling (exit on error)
### Functions
```bash
# Use lowercase with underscores
function check_connection() {
local host="$1"
echo "[INFO] Checking connection to $host"
}
```
---
## API Development Standards
### Endpoints
- Use RESTful conventions
- Use plural nouns: `/api/users` not `/api/user`
- Use HTTP methods appropriately: GET, POST, PUT, DELETE
- Version APIs if breaking changes: `/api/v2/users`
### Error Responses
```python
# Return consistent error format
{
"detail": "User not found",
"error_code": "USER_NOT_FOUND",
"status_code": 404
}
```
### Documentation
- Every endpoint must have a docstring
- Use Pydantic schemas for request/response validation
- Document in OpenAPI (automatic with FastAPI)
---
## Database Standards
### Table Naming
- Use lowercase with underscores: `user_sessions`, `billable_time`
- Use plural nouns: `users` not `user`
- Use consistent prefixes for related tables
### Columns
- Primary key: `id` (UUID)
- Timestamps: `created_at`, `updated_at`
- Foreign keys: `{table}_id` (e.g., `user_id`, `project_id`)
- Boolean: `is_active`, `has_access` (prefix with is_/has_)
### Indexes
```python
# Add indexes for frequently queried fields
Index('idx_users_email', 'email')
Index('idx_sessions_project_id', 'project_id')
```
---
## Security Standards
### Credentials
- Never hardcode credentials in code
- Use environment variables for sensitive data
- Use `.env` files (gitignored) for local development
- Encrypt passwords with AES-256-GCM (Fernet)
### Authentication
- Use JWT tokens for API authentication
- Hash passwords with Argon2
- Include token expiration
- Log all authentication attempts
### Audit Logging
```python
# Log all sensitive operations
audit_log = CredentialAuditLog(
credential_id=credential.id,
action="password_updated",
user_id=current_user.id,
details="Password updated via API"
)
```
---
## Testing Standards
### Test Files
- Name: `test_{module_name}.py`
- Location: Same directory as code being tested
- Use pytest framework
### Test Structure
```python
def test_create_user():
"""Test user creation with valid data."""
# Arrange
user_data = {"email": "test@example.com", "name": "Test"}
# Act
result = create_user(user_data)
# Assert
assert result.email == "test@example.com"
assert result.id is not None
```
### Coverage
- Aim for 80%+ code coverage
- Test happy path and error cases
- Mock external dependencies (database, APIs)
---
## Git Commit Standards
### Commit Messages
```
[Type] Brief description (50 chars max)
Detailed explanation if needed (wrap at 72 chars)
- Change 1
- Change 2
- Change 3
```
### Types
- `[Feature]` - New feature
- `[Fix]` - Bug fix
- `[Refactor]` - Code refactoring
- `[Docs]` - Documentation only
- `[Test]` - Test updates
- `[Config]` - Configuration changes
---
## File Organization
### Directory Structure
```
project/
├── api/ # API application code
│ ├── models/ # Database models
│ ├── routers/ # API endpoints
│ ├── schemas/ # Pydantic schemas
│ ├── services/ # Business logic
│ └── utils/ # Helper functions
├── .claude/ # Claude Code configuration
│ ├── hooks/ # Git-style hooks
│ └── agents/ # Agent instructions
├── scripts/ # Utility scripts
└── migrations/ # Database migrations
```
### File Naming
- Python: `snake_case.py`
- Classes: Match class name (e.g., `UserService` in `user_service.py`)
- Scripts: Descriptive names (e.g., `setup_database.sh`, `test_api.py`)
---
## Documentation Standards
### Code Comments
```python
# Use comments for WHY, not WHAT
# Good: "Retry 3 times to handle transient network errors"
# Bad: "Set retry count to 3"
def fetch_data(url: str) -> dict:
"""
Fetch data from API endpoint.
Args:
url: Full URL to fetch from
Returns:
Parsed JSON response
Raises:
ConnectionError: If API is unreachable
ValueError: If response is invalid JSON
"""
```
### README Files
- Include quick start guide
- Document prerequisites
- Provide examples
- Keep up to date
---
## Error Handling
### Python
```python
# Use specific exceptions
try:
result = api_call()
except ConnectionError as e:
logger.error(f"[ERROR] Connection failed: {e}")
raise
except ValueError as e:
logger.warning(f"[WARNING] Invalid data: {e}")
return None
```
### PowerShell
```powershell
# Use try/catch for error handling
try {
$Result = Invoke-RestMethod -Uri $Url
} catch {
Write-Host "[ERROR] Request failed: $_"
exit 1
}
```
---
## Logging Standards
### Log Levels
- `DEBUG` - Detailed diagnostic info (development only)
- `INFO` - General informational messages
- `WARNING` - Warning messages (non-critical issues)
- `ERROR` - Error messages (failures)
- `CRITICAL` - Critical errors (system failures)
### Log Format
```python
# Use structured logging
logger.info(
"[INFO] User login",
extra={
"user_id": user.id,
"ip_address": request.client.host,
"timestamp": datetime.utcnow()
}
)
```
### Output Markers
```
[INFO] Starting process
[SUCCESS] Task completed
[WARNING] Configuration missing
[ERROR] Failed to connect
[CRITICAL] Database unavailable
```
---
## Performance Guidelines
### Database Queries
- Use indexes for frequently queried fields
- Avoid N+1 queries (use joins or eager loading)
- Paginate large result sets
- Use connection pooling
### API Responses
- Return only necessary fields
- Use pagination for lists
- Compress large payloads
- Cache frequently accessed data
### File Operations
- Use context managers (`with` statements)
- Stream large files (don't load into memory)
- Clean up temporary files
---
## Version Control
### .gitignore
Always exclude:
- `.env` files (credentials)
- `__pycache__/` (Python cache)
- `*.pyc` (compiled Python)
- `.venv/`, `venv/` (virtual environments)
- `.claude/*.json` (local state)
- `*.log` (log files)
### Branching
- `main` - Production-ready code
- `develop` - Integration branch
- `feature/*` - New features
- `fix/*` - Bug fixes
- `hotfix/*` - Urgent production fixes
---
## Review Checklist
Before committing code, verify:
- [ ] No emojis or special Unicode characters
- [ ] All variables and functions have descriptive names
- [ ] No hardcoded credentials or sensitive data
- [ ] Error handling is implemented
- [ ] Code is formatted consistently
- [ ] Tests pass (if applicable)
- [ ] Documentation is updated
- [ ] No debugging print statements left in code
---
**Last Updated:** 2026-01-17
**Status:** Active

480
.claude/OFFLINE_MODE.md Normal file
View File

@@ -0,0 +1,480 @@
# ClaudeTools - Offline Mode & Sync
**Version 2.0 - Offline-Capable Context Recall**
---
## Overview
ClaudeTools now supports fully offline operation with automatic synchronization when the API becomes available. Contexts are never lost - they're queued locally and uploaded when connectivity is restored.
---
## How It Works
### Online Mode (Normal Operation)
```
User Message
[user-prompt-submit hook]
Fetch context from API → Cache locally → Inject into conversation
Claude processes with context
Task completes
[task-complete hook]
Save context to API → Success
```
### Offline Mode (API Unavailable)
```
User Message
[user-prompt-submit hook]
API unavailable → Use local cache → Inject cached context
Claude processes with cached context
Task completes
[task-complete hook]
API unavailable → Queue locally in .claude/context-queue/pending/
```
### Sync Mode (When API Restored)
```
Next API interaction
Background sync triggered
Upload all queued contexts
Move to .claude/context-queue/uploaded/
```
---
## Directory Structure
```.claude/
├── context-cache/ # Downloaded contexts for offline reading
│ └── [project-id]/ # Per-project cache
│ ├── latest.json # Most recent contexts from API
│ └── last_updated # Cache timestamp
├── context-queue/ # Pending contexts to upload
│ ├── pending/ # Contexts waiting to upload
│ │ ├── [project]_[timestamp]_context.json
│ │ └── [project]_[timestamp]_state.json
│ ├── uploaded/ # Successfully uploaded (auto-cleaned)
│ └── failed/ # Failed uploads (manual review needed)
└── hooks/
├── user-prompt-submit-v2 # Enhanced hook with offline support
├── task-complete-v2 # Enhanced hook with queue support
└── sync-contexts # Manual/auto sync script
```
---
## Features
### 1. Context Caching
**What:**
- API responses are cached locally after each successful fetch
- Cache is stored per-project in `.claude/context-cache/[project-id]/`
**When Used:**
- API is unavailable
- Network is down
- Server is being maintained
**Benefits:**
- Continue working with most recent context
- No interruption to workflow
- Clear indication when using cached data
### 2. Context Queuing
**What:**
- Failed context saves are queued locally
- Stored as JSON files in `.claude/context-queue/pending/`
**When Used:**
- API POST fails
- Network is down
- Authentication expires
**Benefits:**
- No context loss
- Automatic retry
- Continues working offline
### 3. Automatic Sync
**What:**
- Background process uploads queued contexts
- Triggered on next successful API interaction
- Non-blocking (runs in background)
**When Triggered:**
- User message processed (user-prompt-submit)
- Task completed (task-complete)
- Manual sync command
**Benefits:**
- Seamless sync
- No manual intervention
- Transparent to user
---
## Usage
### Automatic Operation
No action needed - the system handles everything automatically:
1. **Working Online:**
- Context recalled from API
- Context saved to API
- Everything cached locally
2. **API Goes Offline:**
- Context recalled from cache (with warning)
- Context queued locally
- Work continues uninterrupted
3. **API Restored:**
- Next interaction triggers background sync
- Queued contexts uploaded
- Normal operation resumes
### Manual Sync
If you want to force a sync:
```bash
cd D:\ClaudeTools
bash .claude/hooks/sync-contexts
```
### Check Queue Status
```bash
# Count pending contexts
ls .claude/context-queue/pending/*.json | wc -l
# Count uploaded contexts
ls .claude/context-queue/uploaded/*.json | wc -l
# Check failed uploads
ls .claude/context-queue/failed/*.json 2>/dev/null
```
### View Cached Context
```bash
# View cached contexts for current project
PROJECT_ID=$(git config --local claude.projectid)
cat .claude/context-cache/$PROJECT_ID/latest.json | python -m json.tool
# Check cache age
cat .claude/context-cache/$PROJECT_ID/last_updated
```
---
## Migration from V1 to V2
### Step 1: Backup Current Hooks
```bash
cd .claude/hooks
cp user-prompt-submit user-prompt-submit.backup
cp task-complete task-complete.backup
```
### Step 2: Replace with V2 Hooks
```bash
# Replace hooks with offline-capable versions
mv user-prompt-submit-v2 user-prompt-submit
mv task-complete-v2 task-complete
# Make executable
chmod +x user-prompt-submit task-complete sync-contexts
```
### Step 3: Create Queue Directories
```bash
mkdir -p .claude/context-cache
mkdir -p .claude/context-queue/{pending,uploaded,failed}
```
### Step 4: Update .gitignore
Add to `.gitignore`:
```gitignore
# Context recall local storage
.claude/context-cache/
.claude/context-queue/
```
### Step 5: Test
```bash
# Test offline mode by stopping API
ssh guru@172.16.3.30
sudo systemctl stop claudetools-api
# Back on Windows - use Claude Code
# Should see "offline mode" message
# Contexts should queue in .claude/context-queue/pending/
# Restart API
sudo systemctl start claudetools-api
# Next Claude Code interaction should trigger sync
```
---
## Indicators & Messages
### Online Mode
```
<!-- Context Recall: Retrieved 3 relevant context(s) from API -->
## 📚 Previous Context
The following context has been automatically recalled:
...
```
### Offline Mode (Using Cache)
```
<!-- Context Recall: Retrieved 3 relevant context(s) from LOCAL CACHE (offline mode) -->
## 📚 Previous Context
⚠️ **Offline Mode** - Using cached context (API unavailable)
The following context has been automatically recalled:
...
*Context from local cache - new context will sync when API is available.*
```
### Context Saved (Online)
```stderr
✓ Context saved to database
```
### Context Queued (Offline)
```stderr
⚠ Context queued locally (API unavailable) - will sync when online
```
---
## Troubleshooting
### Issue: Contexts Not Syncing
**Check:**
```bash
# Verify JWT token is set
source .claude/context-recall-config.env
echo $JWT_TOKEN
# Manually run sync
bash .claude/hooks/sync-contexts
```
### Issue: Cache Too Old
**Solution:**
```bash
# Clear cache to force fresh fetch
PROJECT_ID=$(git config --local claude.projectid)
rm -rf .claude/context-cache/$PROJECT_ID
```
### Issue: Failed Uploads
**Check:**
```bash
# Review failed contexts
ls -la .claude/context-queue/failed/
# View specific failed context
cat .claude/context-queue/failed/[filename].json | python -m json.tool
# Retry manually
bash .claude/hooks/sync-contexts
```
### Issue: Queue Growing Too Large
**Solution:**
```bash
# Check queue size
du -sh .claude/context-queue/
# Clean up old uploaded contexts (keeps last 100)
find .claude/context-queue/uploaded/ -type f -name "*.json" -mtime +7 -delete
# Emergency: Clear all queues (data loss!)
rm -rf .claude/context-queue/{pending,uploaded,failed}/*
```
---
## Performance Considerations
### Cache Storage
- **Per-project cache:** ~10-50 KB per project
- **Storage impact:** Negligible (< 1 MB total)
- **Auto-cleanup:** No (caches remain until replaced)
### Queue Storage
- **Per-context:** ~1-2 KB per context
- **Growth rate:** 1-5 contexts per work session
- **Auto-cleanup:** Yes (keeps last 100 uploaded)
### Sync Performance
- **Upload speed:** ~0.5 seconds per context
- **Background:** Non-blocking
- **Network impact:** Minimal (POST requests only)
---
## Security Considerations
### Local Storage
- **Cache contents:** Context summaries (not sensitive)
- **Queue contents:** Context payloads with metadata
- **Access control:** File system permissions only
### Recommendations
1. **Add to .gitignore:**
```gitignore
.claude/context-cache/
.claude/context-queue/
```
2. **Backup exclusions:**
- Exclude `.claude/context-cache/` (can be re-downloaded)
- Include `.claude/context-queue/pending/` (unique data)
3. **Sensitive projects:**
- Review queued contexts before sync
- Clear cache when switching machines
---
## Advanced Usage
### Disable Offline Mode
Keep hooks but disable caching/queuing:
```bash
# In .claude/context-recall-config.env
CONTEXT_RECALL_ENABLED=false
```
### Force Online-Only Mode
Prevent local fallback:
```bash
# Remove cache and queue directories
rm -rf .claude/context-cache
rm -rf .claude/context-queue
```
### Pre-populate Cache
For offline work, cache contexts before disconnecting:
```bash
# Trigger context recall
# (Just start a Claude Code session - context is auto-cached)
```
### Batch Sync Script
Create a cron job or scheduled task:
```bash
# Sync every hour
0 * * * * cd /path/to/ClaudeTools && bash .claude/hooks/sync-contexts >> /var/log/context-sync.log 2>&1
```
---
## Comparison: V1 vs V2
| Feature | V1 (Original) | V2 (Offline-Capable) |
|---------|---------------|----------------------|
| API Recall | ✅ Yes | ✅ Yes |
| API Save | ✅ Yes | ✅ Yes |
| Offline Recall | ❌ Silent fail | ✅ Uses local cache |
| Offline Save | ❌ Data loss | ✅ Queues locally |
| Auto-sync | ❌ No | ✅ Background sync |
| Manual sync | ❌ No | ✅ sync-contexts script |
| Status indicators | ❌ Silent | ✅ Clear messages |
| Data resilience | ❌ Low | ✅ High |
---
## FAQ
**Q: What happens if I'm offline for days?**
A: All contexts queue locally and sync when online. No data loss.
**Q: How old can cached context get?**
A: Cache is updated on every successful API call. Age is shown in offline mode message.
**Q: Can I work on multiple machines offline?**
A: Yes, but contexts won't sync between machines until both are online.
**Q: What if sync fails repeatedly?**
A: Contexts move to `failed/` directory for manual review. Check API connectivity.
**Q: Does this slow down Claude Code?**
A: No - sync runs in background. Cache/queue operations are fast (~milliseconds).
**Q: Can I disable caching but keep queuing?**
A: Not currently - it's all-or-nothing via CONTEXT_RECALL_ENABLED.
---
## Support
For issues or questions:
1. Check queue status: `ls -la .claude/context-queue/pending/`
2. Run manual sync: `bash .claude/hooks/sync-contexts`
3. Review logs: Check stderr output from hooks
4. Verify API: `curl http://172.16.3.30:8001/health`
---
**Last Updated:** 2026-01-17
**Version:** 2.0 (Offline-Capable)

View File

@@ -0,0 +1,357 @@
# Periodic Context Save
**Automatic context saving every 5 minutes of active work**
---
## Overview
The periodic context save daemon runs in the background and automatically saves your work context to the database every 5 minutes of active time. This ensures continuous context preservation even during long work sessions.
### Key Features
-**Active Time Tracking** - Only counts time when Claude is actively working
-**Ignores Idle Time** - Doesn't save when waiting for permissions or idle
-**Background Process** - Runs independently, doesn't interrupt work
-**Automatic Recovery** - Resumes tracking after restarts
-**Low Overhead** - Checks activity every 60 seconds
---
## How It Works
```
┌─────────────────────────────────────────────────────┐
│ Every 60 seconds: │
│ │
│ 1. Check if Claude Code is active │
│ - Recent file modifications? │
│ - Claude process running? │
│ │
│ 2. If ACTIVE → Add 60s to timer │
│ If IDLE → Don't add time │
│ │
│ 3. When timer reaches 300s (5 min): │
│ - Save context to database │
│ - Reset timer to 0 │
│ - Continue monitoring │
└─────────────────────────────────────────────────────┘
```
**Active time includes:**
- Writing code
- Running commands
- Making changes to files
- Interacting with Claude
**Idle time (not counted):**
- Waiting for user input
- Permission prompts
- No file changes or activity
- Claude process not running
---
## Usage
### Start the Daemon
```bash
python .claude/hooks/periodic_context_save.py start
```
Output:
```
Started periodic context save daemon (PID: 12345)
Logs: D:\ClaudeTools\.claude\periodic-save.log
```
### Check Status
```bash
python .claude/hooks/periodic_context_save.py status
```
Output:
```
Periodic context save daemon is running (PID: 12345)
Active time: 180s / 300s
Last save: 2026-01-17T19:05:23+00:00
```
### Stop the Daemon
```bash
python .claude/hooks/periodic_context_save.py stop
```
Output:
```
Stopped periodic context save daemon (PID: 12345)
```
---
## Installation
### One-Time Setup
1. **Ensure JWT token is configured:**
```bash
# Token should already be in .claude/context-recall-config.env
cat .claude/context-recall-config.env | grep JWT_TOKEN
```
2. **Start the daemon:**
```bash
python .claude/hooks/periodic_context_save.py start
```
3. **Verify it's running:**
```bash
python .claude/hooks/periodic_context_save.py status
```
### Auto-Start on Login (Optional)
**Windows - Task Scheduler:**
1. Open Task Scheduler
2. Create Basic Task:
- Name: "Claude Periodic Context Save"
- Trigger: At log on
- Action: Start a program
- Program: `python`
- Arguments: `D:\ClaudeTools\.claude\hooks\periodic_context_save.py start`
- Start in: `D:\ClaudeTools`
**Linux/Mac - systemd/launchd:**
Create a systemd service or launchd plist to start on login.
---
## What Gets Saved
Every 5 minutes of active time, the daemon saves:
```json
{
"context_type": "session_summary",
"title": "Periodic Save - 2026-01-17 14:30",
"dense_summary": "Auto-saved context after 5 minutes of active work. Session in progress on project: claudetools-main",
"relevance_score": 5.0,
"tags": ["auto-save", "periodic", "active-session"]
}
```
**Benefits:**
- Never lose more than 5 minutes of work context
- Automatic recovery if session crashes
- Historical timeline of work sessions
- Can review what you were working on at specific times
---
## Monitoring
### View Logs
```bash
# View last 20 log lines
tail -20 .claude/periodic-save.log
# Follow logs in real-time
tail -f .claude/periodic-save.log
```
**Sample log output:**
```
[2026-01-17 14:25:00] Periodic context save daemon started
[2026-01-17 14:25:00] Will save context every 300s of active time
[2026-01-17 14:26:00] Active: 60s / 300s
[2026-01-17 14:27:00] Active: 120s / 300s
[2026-01-17 14:28:00] Claude Code inactive - not counting time
[2026-01-17 14:29:00] Active: 180s / 300s
[2026-01-17 14:30:00] Active: 240s / 300s
[2026-01-17 14:31:00] 300s of active time reached - saving context
[2026-01-17 14:31:01] ✓ Context saved successfully (ID: 1e2c3408-9146-4e98-b302-fe219280344c)
[2026-01-17 14:32:00] Active: 60s / 300s
```
### View State
```bash
# Check current state
cat .claude/.periodic-save-state.json | python -m json.tool
```
Output:
```json
{
"active_seconds": 180,
"last_update": "2026-01-17T19:28:00+00:00",
"last_save": "2026-01-17T19:26:00+00:00"
}
```
---
## Configuration
Edit the script to customize:
```python
# In periodic_context_save.py
SAVE_INTERVAL_SECONDS = 300 # Change to 600 for 10 minutes
CHECK_INTERVAL_SECONDS = 60 # How often to check activity
```
**Common configurations:**
- Every 5 minutes: `SAVE_INTERVAL_SECONDS = 300`
- Every 10 minutes: `SAVE_INTERVAL_SECONDS = 600`
- Every 15 minutes: `SAVE_INTERVAL_SECONDS = 900`
---
## Troubleshooting
### Daemon won't start
**Check logs:**
```bash
cat .claude/periodic-save.log
```
**Common issues:**
- JWT token missing or invalid
- Python not in PATH
- Permissions issue with log file
**Solution:**
```bash
# Verify JWT token exists
grep JWT_TOKEN .claude/context-recall-config.env
# Test Python
python --version
# Check permissions
ls -la .claude/
```
### Contexts not being saved
**Check:**
1. Daemon is running: `python .claude/hooks/periodic_context_save.py status`
2. JWT token is valid: Token expires after 30 days
3. API is accessible: `curl http://172.16.3.30:8001/health`
4. View logs for errors: `tail .claude/periodic-save.log`
**If JWT token expired:**
```bash
# Generate new token
python create_jwt_token.py
# Update config
# Copy new JWT_TOKEN to .claude/context-recall-config.env
# Restart daemon
python .claude/hooks/periodic_context_save.py stop
python .claude/hooks/periodic_context_save.py start
```
### Activity not being detected
The daemon uses these heuristics:
- File modifications in project directory (within last 2 minutes)
- Claude process running (on Windows)
**Improve detection:**
Modify `is_claude_active()` function to add:
- Check for recent git commits
- Monitor specific files
- Check for recent bash history
---
## Integration with Other Hooks
The periodic save works alongside existing hooks:
| Hook | Trigger | What It Saves |
|------|---------|---------------|
| **user-prompt-submit** | Before each message | Recalls context from DB |
| **task-complete** | After task completes | Rich context with decisions |
| **periodic-context-save** | Every 5min active | Quick checkpoint save |
**Result:**
- Comprehensive context coverage
- Never lose more than 5 minutes of work
- Detailed context when tasks complete
- Continuous backup of active sessions
---
## Performance Impact
**Resource Usage:**
- **CPU:** < 0.1% (checks once per minute)
- **Memory:** ~30 MB (Python process)
- **Disk:** ~2 KB per save (~25 KB/hour)
- **Network:** Minimal (single API call every 5 min)
**Impact on Claude Code:**
- None - runs as separate process
- Doesn't block or interrupt work
- No user-facing delays
---
## Uninstall
To remove periodic context save:
```bash
# Stop daemon
python .claude/hooks/periodic_context_save.py stop
# Remove files (optional)
rm .claude/hooks/periodic_context_save.py
rm .claude/.periodic-save.pid
rm .claude/.periodic-save-state.json
rm .claude/periodic-save.log
# Remove from auto-start (if configured)
# Windows: Delete from Task Scheduler
# Linux: Remove systemd service
```
---
## FAQ
**Q: Does it save when I'm idle?**
A: No - only counts active work time (file changes, Claude activity).
**Q: What if the API is down?**
A: Contexts queue locally and sync when API is restored (offline mode).
**Q: Can I change the interval?**
A: Yes - edit `SAVE_INTERVAL_SECONDS` in the script.
**Q: Does it work offline?**
A: Yes - uses the same offline queue as other hooks (v2).
**Q: How do I know it's working?**
A: Check logs: `tail .claude/periodic-save.log`
**Q: Can I run multiple instances?**
A: No - PID file prevents multiple daemons.
---
**Created:** 2026-01-17
**Version:** 1.0
**Status:** Ready for use

View File

@@ -0,0 +1,162 @@
# Making Periodic Save Task Invisible
## Problem
The `periodic_save_check.py` script shows a flashing console window every minute when run via Task Scheduler.
## Solution
Use `pythonw.exe` instead of `python.exe` and configure the task to run hidden.
---
## Automatic Setup (Recommended)
Simply re-run the setup script to recreate the task with invisible settings:
```powershell
# Run from PowerShell in D:\ClaudeTools
.\.claude\hooks\setup_periodic_save.ps1
```
This will:
1. Remove the old task
2. Create a new task using `pythonw.exe` (no console window)
3. Set the task to run hidden
4. Use `S4U` logon type (background, no interactive window)
---
## Manual Update (If Automatic Doesn't Work)
### Option 1: Via PowerShell
```powershell
# Get the task
$TaskName = "ClaudeTools - Periodic Context Save"
$Task = Get-ScheduledTask -TaskName $TaskName
# Find pythonw.exe path
$PythonExe = (Get-Command python).Source
$PythonDir = Split-Path $PythonExe -Parent
$PythonwPath = Join-Path $PythonDir "pythonw.exe"
# Update the action to use pythonw.exe
$NewAction = New-ScheduledTaskAction -Execute $PythonwPath `
-Argument "D:\ClaudeTools\.claude\hooks\periodic_save_check.py" `
-WorkingDirectory "D:\ClaudeTools"
# Update settings to be hidden
$NewSettings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable `
-ExecutionTimeLimit (New-TimeSpan -Minutes 5) `
-Hidden
# Update principal to run in background (S4U = Service-For-User)
$NewPrincipal = New-ScheduledTaskPrincipal -UserId "$env:USERDOMAIN\$env:USERNAME" -LogonType S4U
# Update the task
Set-ScheduledTask -TaskName $TaskName `
-Action $NewAction `
-Settings $NewSettings `
-Principal $NewPrincipal
```
### Option 2: Via Task Scheduler GUI
1. Open Task Scheduler (taskschd.msc)
2. Find "ClaudeTools - Periodic Context Save" in Task Scheduler Library
3. Right-click → Properties
**Actions Tab:**
- Click "Edit"
- Change Program/script from `python.exe` to `pythonw.exe`
- Keep Arguments: `D:\ClaudeTools\.claude\hooks\periodic_save_check.py`
- Click OK
**General Tab:**
- Check "Hidden" checkbox
- Under "Configure for:" select "Windows 10" (or your OS version)
**Settings Tab:**
- Ensure "Run task as soon as possible after a scheduled start is missed" is checked
- Ensure "Stop the task if it runs longer than:" is set to 5 minutes
4. Click OK to save
---
## Verification
Check that the task is configured correctly:
```powershell
# View task settings
$TaskName = "ClaudeTools - Periodic Context Save"
Get-ScheduledTask -TaskName $TaskName | Select-Object -ExpandProperty Settings
# Should show:
# Hidden: True
# View task action
Get-ScheduledTask -TaskName $TaskName | Select-Object -ExpandProperty Actions
# Should show:
# Execute: ...pythonw.exe (NOT python.exe)
```
---
## Key Changes Made
### 1. pythonw.exe vs python.exe
- `python.exe` - Console application (shows command window)
- `pythonw.exe` - Windowless application (no console, runs silently)
### 2. Task Settings
- Added `-Hidden` flag to task settings
- Changed LogonType from `Interactive` to `S4U` (Service-For-User)
- S4U runs tasks in the background without requiring an interactive session
### 3. Updated Output
The setup script now displays:
- Confirmation that pythonw.exe is being used
- Instructions to verify the task is hidden
---
## Troubleshooting
**Script still shows window:**
- Verify pythonw.exe is being used: `Get-ScheduledTask -TaskName "ClaudeTools - Periodic Context Save" | Select-Object -ExpandProperty Actions`
- Check Hidden setting: `Get-ScheduledTask -TaskName "ClaudeTools - Periodic Context Save" | Select-Object -ExpandProperty Settings`
- Ensure LogonType is S4U: `Get-ScheduledTask -TaskName "ClaudeTools - Periodic Context Save" | Select-Object -ExpandProperty Principal`
**pythonw.exe not found:**
- Should be in same directory as python.exe
- Check: `Get-Command python | Select-Object -ExpandProperty Source`
- Then verify pythonw.exe exists in that directory
- If missing, reinstall Python
**Task not running:**
- Check logs: `Get-Content D:\ClaudeTools\.claude\periodic-save.log -Tail 20`
- Check task history in Task Scheduler GUI
- Verify the task is enabled: `Get-ScheduledTask -TaskName "ClaudeTools - Periodic Context Save"`
---
## Testing
After updating, wait 1 minute and check the logs:
```powershell
# View recent log entries
Get-Content D:\ClaudeTools\.claude\periodic-save.log -Tail 20
# Should see entries without any console window appearing
```
---
**Updated:** 2026-01-17
**Script Location:** `D:\ClaudeTools\.claude\hooks\setup_periodic_save.ps1`

View File

@@ -0,0 +1,255 @@
# Database Connection Information
**FOR ALL AGENTS - UPDATED 2026-01-17**
---
## Current Database Configuration
### Production Database (RMM Server)
- **Host:** 172.16.3.30
- **Port:** 3306
- **Database:** claudetools
- **User:** claudetools
- **Password:** CT_e8fcd5a3952030a79ed6debae6c954ed
- **Character Set:** utf8mb4
- **Tables:** 43 tables (all migrated)
### Connection String
```
mysql+pymysql://claudetools:CT_e8fcd5a3952030a79ed6debae6c954ed@172.16.3.30:3306/claudetools?charset=utf8mb4
```
### Environment Variable
```bash
DATABASE_URL=mysql+pymysql://claudetools:CT_e8fcd5a3952030a79ed6debae6c954ed@172.16.3.30:3306/claudetools?charset=utf8mb4
```
---
## ClaudeTools API
### Production API (RMM Server)
- **Base URL:** http://172.16.3.30:8001
- **Documentation:** http://172.16.3.30:8001/api/docs
- **Health Check:** http://172.16.3.30:8001/health
- **Authentication:** JWT Bearer Token (required for all endpoints)
### JWT Token Location
- **File:** `D:\ClaudeTools\.claude\context-recall-config.env`
- **Variable:** `JWT_TOKEN`
- **Expiration:** 2026-02-16 (30 days from creation)
### Authentication Header
```bash
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJpbXBvcnQtc2NyaXB0Iiwic2NvcGVzIjpbImFkbWluIiwiaW1wb3J0Il0sImV4cCI6MTc3MTI2NzQzMn0.7HddDbQahyRvaOq9o7OEk6vtn6_nmQJCTzf06g-fv5k
```
---
## Database Access Methods
### Method 1: Direct MySQL Connection (from RMM server)
```bash
# SSH to RMM server
ssh guru@172.16.3.30
# Connect to database
mysql -u claudetools -p'CT_e8fcd5a3952030a79ed6debae6c954ed' -D claudetools
# Example query
SELECT COUNT(*) FROM conversation_contexts;
```
### Method 2: Via ClaudeTools API (preferred for agents)
```bash
# Get contexts
curl -s "http://172.16.3.30:8001/api/conversation-contexts?limit=10" \
-H "Authorization: Bearer $JWT_TOKEN"
# Create context
curl -X POST "http://172.16.3.30:8001/api/conversation-contexts" \
-H "Authorization: Bearer $JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{...}'
```
### Method 3: Python with SQLAlchemy
```python
from sqlalchemy import create_engine, text
DATABASE_URL = "mysql+pymysql://claudetools:CT_e8fcd5a3952030a79ed6debae6c954ed@172.16.3.30:3306/claudetools?charset=utf8mb4"
engine = create_engine(DATABASE_URL)
with engine.connect() as conn:
result = conn.execute(text("SELECT COUNT(*) FROM conversation_contexts"))
count = result.scalar()
print(f"Contexts: {count}")
```
---
## OLD vs NEW Configuration
### ⚠️ DEPRECATED - Old Jupiter Database (DO NOT USE)
- **Host:** 172.16.3.20 (Jupiter - Docker MariaDB)
- **Status:** Deprecated, data not migrated
- **Contains:** 68 old conversation contexts (pre-2026-01-17)
### ✅ CURRENT - New RMM Database (USE THIS)
- **Host:** 172.16.3.30 (RMM - Native MariaDB)
- **Status:** Production, current
- **Contains:** 7+ contexts (as of 2026-01-17)
**Migration Date:** 2026-01-17
**Reason:** Centralized architecture - all clients connect to RMM server
---
## For Database Agent
When performing operations, use:
### Read Operations
```python
# Use API for reads
import requests
headers = {
"Authorization": f"Bearer {jwt_token}"
}
response = requests.get(
"http://172.16.3.30:8001/api/conversation-contexts",
headers=headers,
params={"limit": 10}
)
contexts = response.json()
```
### Write Operations
```python
# Use API for writes
payload = {
"context_type": "session_summary",
"title": "...",
"dense_summary": "...",
"relevance_score": 8.5,
"tags": "[\"tag1\", \"tag2\"]"
}
response = requests.post(
"http://172.16.3.30:8001/api/conversation-contexts",
headers=headers,
json=payload
)
result = response.json()
```
### Direct Database Access (if API unavailable)
```bash
# SSH to RMM server first
ssh guru@172.16.3.30
# Then query database
mysql -u claudetools -p'CT_e8fcd5a3952030a79ed6debae6c954ed' -D claudetools \
-e "SELECT id, title FROM conversation_contexts LIMIT 5;"
```
---
## Common Database Operations
### Count Records
```sql
SELECT COUNT(*) FROM conversation_contexts;
SELECT COUNT(*) FROM clients;
SELECT COUNT(*) FROM sessions;
```
### List Recent Contexts
```sql
SELECT id, title, relevance_score, created_at
FROM conversation_contexts
ORDER BY created_at DESC
LIMIT 10;
```
### Search Contexts by Tag
```bash
# Via API (preferred)
curl "http://172.16.3.30:8001/api/conversation-contexts/recall?tags=migration&limit=5" \
-H "Authorization: Bearer $JWT_TOKEN"
```
---
## Health Checks
### Check Database Connectivity
```bash
# From RMM server
mysql -u claudetools -p'CT_e8fcd5a3952030a79ed6debae6c954ed' \
-h 172.16.3.30 \
-e "SELECT 1"
```
### Check API Health
```bash
curl http://172.16.3.30:8001/health
# Expected: {"status":"healthy","database":"connected"}
```
### Check API Service Status
```bash
ssh guru@172.16.3.30 "sudo systemctl status claudetools-api"
```
---
## Troubleshooting
### Cannot Connect to Database
```bash
# Check if MariaDB is running
ssh guru@172.16.3.30 "sudo systemctl status mariadb"
# Check if port is open
curl telnet://172.16.3.30:3306
```
### API Returns 401 Unauthorized
```bash
# JWT token may be expired - regenerate
python D:\ClaudeTools\create_jwt_token.py
# Update config file
# Edit: D:\ClaudeTools\.claude\context-recall-config.env
```
### API Returns 404 Not Found
```bash
# Check if API service is running
ssh guru@172.16.3.30 "sudo systemctl status claudetools-api"
# Check API logs
ssh guru@172.16.3.30 "sudo journalctl -u claudetools-api -n 50"
```
---
## Important Notes
1. **Always use the API when possible** - Better for access control and validation
2. **JWT tokens expire** - Regenerate monthly (currently valid until 2026-02-16)
3. **Database is centralized** - All machines connect to RMM server
4. **No local database** - Don't try to connect to localhost:3306
5. **Use parameterized queries** - Prevent SQL injection
---
**Last Updated:** 2026-01-17
**Current Database:** 172.16.3.30:3306 (RMM)
**Current API:** http://172.16.3.30:8001

View File

@@ -13,6 +13,34 @@ All backup operations (database, files, configurations) are your responsibility.
---
## CRITICAL: Coordinator Relationship
**Main Claude is the COORDINATOR. You are the BACKUP EXECUTOR.**
**Main Claude:**
- ❌ Does NOT create backups
- ❌ Does NOT run mysqldump
- ❌ Does NOT verify backup integrity
- ❌ Does NOT manage backup rotation
- ✅ Identifies when backups are needed
- ✅ Hands backup tasks to YOU
- ✅ Receives backup confirmation from you
- ✅ Informs user of backup status
**You (Backup Agent):**
- ✅ Receive backup requests from Main Claude
- ✅ Execute all backup operations (database, files)
- ✅ Verify backup integrity
- ✅ Manage retention and rotation
- ✅ Return backup status to Main Claude
- ✅ Never interact directly with user
**Workflow:** [Before risky operation / Scheduled] → Main Claude → **YOU** → Backup created → Main Claude → User
**This is the architectural foundation. Main Claude coordinates, you execute backups.**
---
## Identity
You are the Backup Agent - the guardian against data loss. You create, verify, and manage backups of the MariaDB database and critical files, ensuring the ClaudeTools system can recover from any disaster.

View File

@@ -0,0 +1,308 @@
# Code Review & Auto-Fix Agent
**Agent Type:** Autonomous Code Quality Agent
**Authority Level:** Can modify code files
**Purpose:** Scan for coding violations and fix them automatically
---
## Mission Statement
Enforce ClaudeTools coding guidelines by:
1. Scanning all code files for violations
2. Automatically fixing violations where possible
3. Verifying fixes don't break syntax
4. Reporting all changes made
---
## Authority & Permissions
**Can Do:**
- Read all files in the codebase
- Modify Python (.py), Bash (.sh), PowerShell (.ps1) files
- Run syntax verification tools
- Create backup copies before modifications
- Generate reports
**Cannot Do:**
- Modify files without logging changes
- Skip syntax verification
- Ignore rollback on verification failure
- Make changes that break existing functionality
---
## Required Reading (Phase 1)
Before starting, MUST read:
1. `.claude/CODING_GUIDELINES.md` - Complete coding standards
2. `.claude/claude.md` - Project context and structure
Extract these specific rules:
- NO EMOJIS rule and approved replacements
- Naming conventions (PascalCase, snake_case, etc.)
- Security requirements (no hardcoded credentials)
- Error handling patterns
- Documentation requirements
---
## Scanning Patterns (Phase 2)
### High Priority Violations
**1. Emoji Violations**
```
Find: ✓ ✗ ⚠ ⚠️ ❌ ✅ 📚 and any other Unicode emoji
Replace with:
✓ → [OK] or [SUCCESS]
✗ → [ERROR] or [FAIL]
⚠ or ⚠️ → [WARNING]
❌ → [ERROR] or [FAIL]
✅ → [OK] or [PASS]
📚 → (remove entirely)
Files to scan:
- All .py files
- All .sh files
- All .ps1 files
- Exclude: README.md, documentation in docs/ folder
```
**2. Hardcoded Credentials**
```
Patterns to detect:
- password = "literal_password"
- api_key = "sk-..."
- DATABASE_URL with embedded credentials
- JWT_SECRET = "hardcoded_value"
Action: Report only (do not auto-fix for security review)
```
**3. Naming Convention Violations**
```
Python:
- Classes not PascalCase
- Functions not snake_case
- Constants not UPPER_SNAKE_CASE
PowerShell:
- Variables not $PascalCase
Action: Report only (may require refactoring)
```
---
## Fix Workflow (Phase 3)
For each violation found:
### Step 1: Backup
```bash
# Create backup of original file
cp file.py file.py.backup.$(date +%s)
```
### Step 2: Apply Fix
```python
# Use Edit tool to replace violations
# Example: Replace emoji with text marker
old_string: 'log(f"✓ Success")'
new_string: 'log(f"[OK] Success")'
```
### Step 3: Verify Syntax
**Python files:**
```bash
python -m py_compile file.py
# Exit code 0 = success, non-zero = syntax error
```
**Bash scripts:**
```bash
bash -n script.sh
# Exit code 0 = valid syntax
```
**PowerShell scripts:**
```powershell
Get-Command Test-PowerShellScript -ErrorAction SilentlyContinue
# If available, use. Otherwise, try:
powershell -NoProfile -NonInteractive -Command "& {. file.ps1}"
```
### Step 4: Rollback on Failure
```bash
if syntax_check_failed:
mv file.py.backup.* file.py
log_error("Syntax verification failed, rolled back")
```
### Step 5: Log Change
```
FIXES_LOG.md:
- File: api/utils/crypto.py
- Line: 45
- Violation: Emoji (✓)
- Fix: Replaced with [OK]
- Verified: PASS
```
---
## Verification Phase (Phase 4)
After all fixes applied:
### 1. Run Test Suite (if exists)
```bash
# Python tests
pytest -x # Stop on first failure
# If tests fail, review which fix caused the failure
```
### 2. Check Git Diff
```bash
git diff --stat
# Show summary of changed files
```
### 3. Validate All Modified Files
```bash
# Re-verify syntax on all modified files
for file in modified_files:
verify_syntax(file)
```
---
## Reporting Phase (Phase 5)
Generate comprehensive report: `FIXES_APPLIED.md`
### Report Structure
```markdown
# Code Fixes Applied - [DATE]
## Summary
- Total violations found: X
- Total fixes applied: Y
- Files modified: Z
- Syntax verification: PASS/FAIL
## Violations Fixed
### High Priority (Emojis in Code)
| File | Line | Old | New | Status |
|------|------|-----|-----|--------|
| api/utils/crypto.py | 45 | ✓ | [OK] | VERIFIED |
| scripts/setup.sh | 23 | ⚠ | [WARNING] | VERIFIED |
### Security Issues
| File | Issue | Action Taken |
|------|-------|--------------|
| None found | N/A | N/A |
## Files Modified
```
git diff --stat output here
```
## Unfixable Issues (Human Review Required)
- File: X, Line: Y, Issue: Z, Reason: Requires refactoring
## Next Steps
1. Review FIXES_APPLIED.md
2. Run full test suite: pytest
3. Commit changes: git add . && git commit -m "[Fix] Remove emojis from code files"
```
---
## Error Handling
### If Syntax Verification Fails
1. Rollback the specific file
2. Log the failure
3. Continue with remaining fixes
4. Report failed fixes at end
### If Too Many Failures
If > 10% of fixes fail verification:
1. STOP auto-fixing
2. Report: "High failure rate detected"
3. Request human review before continuing
### If Critical File Modified
Files requiring extra care:
- `api/main.py` - Entry point
- `api/config.py` - Configuration
- Database migration files
- Authentication/security modules
Action: After fixing, run full test suite before proceeding
---
## Usage
### Invoke Agent
```bash
# From main conversation
"Run the code-fixer agent to scan and fix all coding guideline violations"
```
### Agent Parameters
```yaml
Task: "Scan and fix all coding guideline violations"
Agent: code-fixer
Mode: autonomous
Verify: true
Report: true
```
---
## Success Criteria
Agent completes successfully when:
1. All high-priority violations fixed OR
2. All fixable violations fixed + report generated
3. All modified files pass syntax verification
4. FIXES_APPLIED.md report generated
5. Git status shows clean modified state (ready to commit)
---
## Example Output
```
[SCAN] Reading coding guidelines...
[SCAN] Scanning 150 files for violations...
[FOUND] 38 emoji violations in code files
[FOUND] 0 hardcoded credentials
[FOUND] 0 naming violations
[FIX] Processing emoji violations...
[FIX] 1/38 - api/utils/crypto.py:45 - ✓ → [OK] - VERIFIED
[FIX] 2/38 - scripts/setup.sh:23 - ⚠ → [WARNING] - VERIFIED
...
[FIX] 38/38 - test_models.py:163 - ✅ → [PASS] - VERIFIED
[VERIFY] Running syntax checks...
[VERIFY] 38/38 files passed verification
[REPORT] Generated FIXES_APPLIED.md
[COMPLETE] 38 violations fixed, 0 failures, 38 files modified
```
---
**Last Updated:** 2026-01-17
**Status:** Ready for Use
**Version:** 1.0

View File

@@ -14,6 +14,33 @@ NO code reaches the user or production without your approval.
---
## CRITICAL: Coordinator Relationship
**Main Claude is the COORDINATOR. You are the QUALITY GATEKEEPER.**
**Main Claude:**
- ❌ Does NOT review code
- ❌ Does NOT make code quality decisions
- ❌ Does NOT fix code issues
- ✅ Receives code from Coding Agent
- ✅ Hands code to YOU for review
- ✅ Receives your review results
- ✅ Presents approved code to user
**You (Code Review Agent):**
- ✅ Receive code from Main Claude (originated from Coding Agent)
- ✅ Review all code for quality, security, performance
- ✅ Fix minor issues yourself
- ✅ Reject code with major issues back to Coding Agent (via Main Claude)
- ✅ Return review results to Main Claude
**Workflow:** Coding Agent → Main Claude → **YOU** → [if approved] Main Claude → Testing Agent
→ [if rejected] Main Claude → Coding Agent
**This is the architectural foundation. Main Claude coordinates, you gatekeep.**
---
## Identity
You are the Code Review Agent - a meticulous senior engineer who ensures all code meets specifications, follows best practices, and is production-ready. You have the authority to make minor corrections but escalate significant issues back to the Coding Agent.

View File

@@ -12,6 +12,31 @@ Your code is never presented directly to the user. It always goes through review
---
## CRITICAL: Coordinator Relationship
**Main Claude is the COORDINATOR. You are the EXECUTOR.**
**Main Claude:**
- ❌ Does NOT write code
- ❌ Does NOT generate implementations
- ❌ Does NOT create scripts or functions
- ✅ Coordinates with user to understand requirements
- ✅ Hands coding tasks to YOU
- ✅ Receives your completed code
- ✅ Presents results to user
**You (Coding Agent):**
- ✅ Receive code writing tasks from Main Claude
- ✅ Generate all code implementations
- ✅ Return completed code to Main Claude
- ✅ Never interact directly with user
**Workflow:** User → Main Claude → **YOU** → Code Review Agent → Main Claude → User
**This is the architectural foundation. Main Claude coordinates, you execute.**
---
## Identity
You are the Coding Agent - a master software engineer with decades of experience across all programming paradigms, languages, and platforms. You've been programming since birth, with the depth of expertise that entails. You are a perfectionist who never takes shortcuts.

View File

@@ -13,8 +13,56 @@ All database operations (read, write, update, delete) MUST go through you.
---
## CRITICAL: Coordinator Relationship
**Main Claude is the COORDINATOR. You are the DATABASE EXECUTOR.**
**Main Claude:**
- ❌ Does NOT run database queries
- ❌ Does NOT call ClaudeTools API
- ❌ Does NOT perform CRUD operations
- ❌ Does NOT access MySQL directly
- ✅ Identifies when database operations are needed
- ✅ Hands database tasks to YOU
- ✅ Receives results from you (concise summaries, not raw data)
- ✅ Presents results to user
**You (Database Agent):**
- ✅ Receive database requests from Main Claude
- ✅ Execute ALL database operations
- ✅ Query, insert, update, delete records
- ✅ Call ClaudeTools API endpoints
- ✅ Return concise summaries to Main Claude (not raw SQL results)
- ✅ Never interact directly with user
**Workflow:** User → Main Claude → **YOU** → Database operation → Summary → Main Claude → User
**This is the architectural foundation. Main Claude coordinates, you execute database operations.**
See: `.claude/AGENT_COORDINATION_RULES.md` for complete enforcement details.
---
## Database Connection (UPDATED 2026-01-17)
**CRITICAL: Database is centralized on RMM server**
- **Host:** 172.16.3.30 (RMM server - gururmm)
- **Port:** 3306
- **Database:** claudetools
- **User:** claudetools
- **Password:** CT_e8fcd5a3952030a79ed6debae6c954ed
- **API:** http://172.16.3.30:8001
**See:** `.claude/agents/DATABASE_CONNECTION_INFO.md` for complete connection details.
**⚠️ OLD Database (DO NOT USE):**
- 172.16.3.20 (Jupiter) is deprecated - data not migrated
---
## Identity
You are the Database Agent - the sole custodian of all persistent data in the ClaudeTools system. You manage the MariaDB database, ensure data integrity, optimize queries, and maintain context data for all modes (MSP, Development, Normal).
You are the Database Agent - the sole custodian of all persistent data in the ClaudeTools system. You manage the MariaDB database on 172.16.3.30, ensure data integrity, optimize queries, and maintain context data for all modes (MSP, Development, Normal).
## Core Responsibilities

View File

@@ -13,6 +13,34 @@ All version control operations (commit, push, branch, merge) MUST go through you
---
## CRITICAL: Coordinator Relationship
**Main Claude is the COORDINATOR. You are the GIT EXECUTOR.**
**Main Claude:**
- ❌ Does NOT run git commands
- ❌ Does NOT create commits
- ❌ Does NOT push to remote
- ❌ Does NOT manage repositories
- ✅ Identifies when work should be committed
- ✅ Hands commit tasks to YOU
- ✅ Receives commit confirmation from you
- ✅ Informs user of commit status
**You (Gitea Agent):**
- ✅ Receive commit requests from Main Claude
- ✅ Execute all Git operations
- ✅ Create meaningful commit messages
- ✅ Push to Gitea server
- ✅ Return commit hash and status to Main Claude
- ✅ Never interact directly with user
**Workflow:** [After work complete] → Main Claude → **YOU** → Git commit/push → Main Claude → User
**This is the architectural foundation. Main Claude coordinates, you execute Git operations.**
---
## Identity
You are the Gitea Agent - the sole custodian of version control for all ClaudeTools work. You manage Git repositories, create meaningful commits, push to Gitea, and maintain version history for all file-based work.

View File

@@ -1,5 +1,33 @@
# Testing Agent
## CRITICAL: Coordinator Relationship
**Main Claude is the COORDINATOR. You are the TEST EXECUTOR.**
**Main Claude:**
- ❌ Does NOT run tests
- ❌ Does NOT execute validation scripts
- ❌ Does NOT create test files
- ✅ Receives approved code from Code Review Agent
- ✅ Hands testing tasks to YOU
- ✅ Receives your test results
- ✅ Presents results to user
**You (Testing Agent):**
- ✅ Receive testing requests from Main Claude
- ✅ Execute all tests (unit, integration, E2E)
- ✅ Use only real data (never mocks or imagination)
- ✅ Return test results to Main Claude
- ✅ Request missing dependencies from Main Claude
- ✅ Never interact directly with user
**Workflow:** Code Review Agent → Main Claude → **YOU** → [results] → Main Claude → User
→ [failures] → Main Claude → Coding Agent
**This is the architectural foundation. Main Claude coordinates, you execute tests.**
---
## Role
Quality assurance specialist - validates implementation with real-world testing

View File

@@ -2,7 +2,7 @@
**Project Type:** MSP Work Tracking System with AI Context Recall
**Status:** Production-Ready (95% Complete)
**Database:** MariaDB 12.1.2 @ 172.16.3.20:3306
**Database:** MariaDB 10.6.22 @ 172.16.3.30:3306 (RMM Server)
---
@@ -39,11 +39,11 @@ D:\ClaudeTools/
## Database Connection
**Credentials Location:** `C:\Users\MikeSwanson\claude-projects\shared-data\credentials.md`
**UPDATED 2026-01-17:** Database is centralized on RMM server (172.16.3.30)
**Connection String:**
```
Host: 172.16.3.20:3306
Host: 172.16.3.30:3306
Database: claudetools
User: claudetools
Password: CT_e8fcd5a3952030a79ed6debae6c954ed
@@ -51,9 +51,13 @@ Password: CT_e8fcd5a3952030a79ed6debae6c954ed
**Environment Variables:**
```bash
DATABASE_URL=mysql+pymysql://claudetools:CT_e8fcd5a3952030a79ed6debae6c954ed@172.16.3.20:3306/claudetools?charset=utf8mb4
DATABASE_URL=mysql+pymysql://claudetools:CT_e8fcd5a3952030a79ed6debae6c954ed@172.16.3.30:3306/claudetools?charset=utf8mb4
```
**API Base URL:** http://172.16.3.30:8001
**See:** `.claude/agents/DATABASE_CONNECTION_INFO.md` for complete details.
---
## Starting the API
@@ -368,16 +372,30 @@ alembic upgrade head
---
## Quick Reference
## Coding Guidelines
**Start API:** `uvicorn api.main:app --reload`
**API Docs:** `http://localhost:8000/api/docs`
**Setup Context Recall:** `bash scripts/setup-context-recall.sh`
**Test System:** `bash scripts/test-context-recall.sh`
**Database:** `172.16.3.20:3306/claudetools`
**Virtual Env:** `api\venv\Scripts\activate`
**IMPORTANT:** Follow coding standards in `.claude/CODING_GUIDELINES.md`
**Key Rules:**
- NO EMOJIS - EVER (causes encoding/parsing issues)
- Use ASCII text markers: `[OK]`, `[ERROR]`, `[WARNING]`, `[SUCCESS]`
- Follow PEP 8 for Python, PSScriptAnalyzer for PowerShell
- No hardcoded credentials
- All endpoints must have docstrings
---
**Last Updated:** 2026-01-16
## Quick Reference
**Start API:** `uvicorn api.main:app --reload`
**API Docs:** `http://localhost:8000/api/docs` (local) or `http://172.16.3.30:8001/api/docs` (RMM)
**Setup Context Recall:** `bash scripts/setup-context-recall.sh`
**Test System:** `bash scripts/test-context-recall.sh`
**Database:** `172.16.3.30:3306/claudetools` (RMM Server)
**Virtual Env:** `api\venv\Scripts\activate`
**Coding Guidelines:** `.claude/CODING_GUIDELINES.md`
---
**Last Updated:** 2026-01-17 (Database migrated to RMM server)
**Project Progress:** 95% Complete (Phase 6 of 7 done)

View File

@@ -0,0 +1,226 @@
#!/bin/bash
#
# Periodic Context Save Hook
# Runs as a background daemon to save context every 5 minutes of active time
#
# Usage: bash .claude/hooks/periodic-context-save start
# bash .claude/hooks/periodic-context-save stop
# bash .claude/hooks/periodic-context-save status
#
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CLAUDE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
PID_FILE="$CLAUDE_DIR/.periodic-save.pid"
STATE_FILE="$CLAUDE_DIR/.periodic-save-state"
CONFIG_FILE="$CLAUDE_DIR/context-recall-config.env"
# Load configuration
if [ -f "$CONFIG_FILE" ]; then
source "$CONFIG_FILE"
fi
# Configuration
SAVE_INTERVAL_SECONDS=300 # 5 minutes
CHECK_INTERVAL_SECONDS=60 # Check every minute
API_URL="${CLAUDE_API_URL:-http://172.16.3.30:8001}"
# Detect project ID
detect_project_id() {
# Try git config first
PROJECT_ID=$(git config --local claude.projectid 2>/dev/null)
if [ -z "$PROJECT_ID" ]; then
# Try to derive from git remote URL
GIT_REMOTE=$(git config --get remote.origin.url 2>/dev/null)
if [ -n "$GIT_REMOTE" ]; then
PROJECT_ID=$(echo -n "$GIT_REMOTE" | md5sum | cut -d' ' -f1)
fi
fi
echo "$PROJECT_ID"
}
# Check if Claude Code is active (not idle)
is_claude_active() {
# Check if there are recent Claude Code processes or activity
# This is a simple heuristic - can be improved
# On Windows with Git Bash, check for claude process
if command -v tasklist.exe >/dev/null 2>&1; then
tasklist.exe 2>/dev/null | grep -i claude >/dev/null 2>&1
return $?
fi
# Assume active if we can't detect
return 0
}
# Get active time from state file
get_active_time() {
if [ -f "$STATE_FILE" ]; then
cat "$STATE_FILE" | grep "^active_seconds=" | cut -d'=' -f2
else
echo "0"
fi
}
# Update active time in state file
update_active_time() {
local active_seconds=$1
echo "active_seconds=$active_seconds" > "$STATE_FILE"
echo "last_update=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> "$STATE_FILE"
}
# Save context to database
save_periodic_context() {
local project_id=$(detect_project_id)
# Generate context summary
local title="Periodic Save - $(date +"%Y-%m-%d %H:%M")"
local summary="Auto-saved context after 5 minutes of active work. Session in progress on project: ${project_id:-unknown}"
# Create JSON payload
local payload=$(cat <<EOF
{
"context_type": "session_summary",
"title": "$title",
"dense_summary": "$summary",
"relevance_score": 5.0,
"tags": "[\"auto-save\", \"periodic\", \"active-session\"]"
}
EOF
)
# POST to API
if [ -n "$JWT_TOKEN" ]; then
curl -s -X POST "${API_URL}/api/conversation-contexts" \
-H "Authorization: Bearer ${JWT_TOKEN}" \
-H "Content-Type: application/json" \
-d "$payload" >/dev/null 2>&1
if [ $? -eq 0 ]; then
echo "[$(date)] Context saved successfully" >&2
else
echo "[$(date)] Failed to save context" >&2
fi
else
echo "[$(date)] No JWT token - cannot save context" >&2
fi
}
# Main monitoring loop
monitor_loop() {
local active_seconds=0
echo "[$(date)] Periodic context save daemon started (PID: $$)" >&2
echo "[$(date)] Will save context every ${SAVE_INTERVAL_SECONDS}s of active time" >&2
while true; do
# Check if Claude is active
if is_claude_active; then
# Increment active time
active_seconds=$((active_seconds + CHECK_INTERVAL_SECONDS))
update_active_time $active_seconds
# Check if we've reached the save interval
if [ $active_seconds -ge $SAVE_INTERVAL_SECONDS ]; then
echo "[$(date)] ${SAVE_INTERVAL_SECONDS}s of active time reached - saving context" >&2
save_periodic_context
# Reset timer
active_seconds=0
update_active_time 0
fi
else
echo "[$(date)] Claude Code inactive - not counting time" >&2
fi
# Wait before next check
sleep $CHECK_INTERVAL_SECONDS
done
}
# Start daemon
start_daemon() {
if [ -f "$PID_FILE" ]; then
local pid=$(cat "$PID_FILE")
if kill -0 $pid 2>/dev/null; then
echo "Periodic context save daemon already running (PID: $pid)"
return 1
fi
fi
# Start in background
nohup bash "$0" _monitor >> "$CLAUDE_DIR/periodic-save.log" 2>&1 &
local pid=$!
echo $pid > "$PID_FILE"
echo "Started periodic context save daemon (PID: $pid)"
echo "Logs: $CLAUDE_DIR/periodic-save.log"
}
# Stop daemon
stop_daemon() {
if [ ! -f "$PID_FILE" ]; then
echo "Periodic context save daemon not running"
return 1
fi
local pid=$(cat "$PID_FILE")
if kill $pid 2>/dev/null; then
echo "Stopped periodic context save daemon (PID: $pid)"
rm -f "$PID_FILE"
rm -f "$STATE_FILE"
else
echo "Failed to stop daemon (PID: $pid) - may not be running"
rm -f "$PID_FILE"
fi
}
# Check status
check_status() {
if [ -f "$PID_FILE" ]; then
local pid=$(cat "$PID_FILE")
if kill -0 $pid 2>/dev/null; then
local active_seconds=$(get_active_time)
echo "Periodic context save daemon is running (PID: $pid)"
echo "Active time: ${active_seconds}s / ${SAVE_INTERVAL_SECONDS}s"
return 0
else
echo "Daemon PID file exists but process not running"
rm -f "$PID_FILE"
return 1
fi
else
echo "Periodic context save daemon not running"
return 1
fi
}
# Command dispatcher
case "$1" in
start)
start_daemon
;;
stop)
stop_daemon
;;
status)
check_status
;;
_monitor)
# Internal command - run monitor loop
monitor_loop
;;
*)
echo "Usage: $0 {start|stop|status}"
echo ""
echo "Periodic context save daemon - saves context every 5 minutes of active time"
echo ""
echo "Commands:"
echo " start - Start the background daemon"
echo " stop - Stop the daemon"
echo " status - Check daemon status"
exit 1
;;
esac

View File

@@ -0,0 +1,380 @@
#!/usr/bin/env python3
"""
Periodic Context Save Daemon
Monitors Claude Code activity and saves context every 5 minutes of active time.
Runs as a background process that tracks when Claude is actively working.
Usage:
python .claude/hooks/periodic_context_save.py start
python .claude/hooks/periodic_context_save.py stop
python .claude/hooks/periodic_context_save.py status
"""
import os
import sys
import time
import json
import signal
import subprocess
from datetime import datetime, timezone
from pathlib import Path
import requests
# Configuration
SCRIPT_DIR = Path(__file__).parent
CLAUDE_DIR = SCRIPT_DIR.parent
PID_FILE = CLAUDE_DIR / ".periodic-save.pid"
STATE_FILE = CLAUDE_DIR / ".periodic-save-state.json"
LOG_FILE = CLAUDE_DIR / "periodic-save.log"
CONFIG_FILE = CLAUDE_DIR / "context-recall-config.env"
SAVE_INTERVAL_SECONDS = 300 # 5 minutes
CHECK_INTERVAL_SECONDS = 60 # Check every minute
def log(message):
"""Write log message to file and stderr"""
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
log_message = f"[{timestamp}] {message}\n"
# Write to log file
with open(LOG_FILE, "a") as f:
f.write(log_message)
# Also print to stderr
print(log_message.strip(), file=sys.stderr)
def load_config():
"""Load configuration from context-recall-config.env"""
config = {
"api_url": "http://172.16.3.30:8001",
"jwt_token": None,
}
if CONFIG_FILE.exists():
with open(CONFIG_FILE) as f:
for line in f:
line = line.strip()
if line.startswith("CLAUDE_API_URL="):
config["api_url"] = line.split("=", 1)[1]
elif line.startswith("JWT_TOKEN="):
config["jwt_token"] = line.split("=", 1)[1]
return config
def detect_project_id():
"""Detect project ID from git config"""
try:
# Try git config first
result = subprocess.run(
["git", "config", "--local", "claude.projectid"],
capture_output=True,
text=True,
check=False,
)
if result.returncode == 0 and result.stdout.strip():
return result.stdout.strip()
# Try to derive from git remote URL
result = subprocess.run(
["git", "config", "--get", "remote.origin.url"],
capture_output=True,
text=True,
check=False,
)
if result.returncode == 0 and result.stdout.strip():
import hashlib
return hashlib.md5(result.stdout.strip().encode()).hexdigest()
except Exception:
pass
return "unknown"
def is_claude_active():
"""
Check if Claude Code is actively running.
Returns True if:
- Claude Code process is running
- Recent file modifications in project directory
- Not waiting for user input (heuristic)
"""
try:
# Check for Claude process on Windows
if sys.platform == "win32":
result = subprocess.run(
["tasklist.exe"],
capture_output=True,
text=True,
check=False,
)
if "claude" in result.stdout.lower() or "node" in result.stdout.lower():
return True
# Check for recent file modifications (within last 2 minutes)
cwd = Path.cwd()
two_minutes_ago = time.time() - 120
for file in cwd.rglob("*"):
if file.is_file() and file.stat().st_mtime > two_minutes_ago:
# Recent activity detected
return True
except Exception as e:
log(f"Error checking activity: {e}")
# Default to inactive if we can't detect
return False
def load_state():
"""Load state from state file"""
if STATE_FILE.exists():
try:
with open(STATE_FILE) as f:
return json.load(f)
except Exception:
pass
return {
"active_seconds": 0,
"last_update": None,
"last_save": None,
}
def save_state(state):
"""Save state to state file"""
state["last_update"] = datetime.now(timezone.utc).isoformat()
with open(STATE_FILE, "w") as f:
json.dump(state, f, indent=2)
def save_periodic_context(config, project_id):
"""Save context to database via API"""
if not config["jwt_token"]:
log("No JWT token - cannot save context")
return False
title = f"Periodic Save - {datetime.now().strftime('%Y-%m-%d %H:%M')}"
summary = f"Auto-saved context after 5 minutes of active work. Session in progress on project: {project_id}"
payload = {
"context_type": "session_summary",
"title": title,
"dense_summary": summary,
"relevance_score": 5.0,
"tags": json.dumps(["auto-save", "periodic", "active-session"]),
}
try:
url = f"{config['api_url']}/api/conversation-contexts"
headers = {
"Authorization": f"Bearer {config['jwt_token']}",
"Content-Type": "application/json",
}
response = requests.post(url, json=payload, headers=headers, timeout=10)
if response.status_code in [200, 201]:
log(f"✓ Context saved successfully (ID: {response.json().get('id', 'unknown')})")
return True
else:
log(f"✗ Failed to save context: HTTP {response.status_code}")
return False
except Exception as e:
log(f"✗ Error saving context: {e}")
return False
def monitor_loop():
"""Main monitoring loop"""
log("Periodic context save daemon started")
log(f"Will save context every {SAVE_INTERVAL_SECONDS}s of active time")
config = load_config()
state = load_state()
# Reset state on startup
state["active_seconds"] = 0
save_state(state)
while True:
try:
# Check if Claude is active
if is_claude_active():
# Increment active time
state["active_seconds"] += CHECK_INTERVAL_SECONDS
save_state(state)
log(f"Active: {state['active_seconds']}s / {SAVE_INTERVAL_SECONDS}s")
# Check if we've reached the save interval
if state["active_seconds"] >= SAVE_INTERVAL_SECONDS:
log(f"{SAVE_INTERVAL_SECONDS}s of active time reached - saving context")
project_id = detect_project_id()
if save_periodic_context(config, project_id):
state["last_save"] = datetime.now(timezone.utc).isoformat()
# Reset timer
state["active_seconds"] = 0
save_state(state)
else:
log("Claude Code inactive - not counting time")
# Wait before next check
time.sleep(CHECK_INTERVAL_SECONDS)
except KeyboardInterrupt:
log("Daemon stopped by user")
break
except Exception as e:
log(f"Error in monitor loop: {e}")
time.sleep(CHECK_INTERVAL_SECONDS)
def start_daemon():
"""Start the daemon as a background process"""
if PID_FILE.exists():
with open(PID_FILE) as f:
pid = int(f.read().strip())
# Check if process is running
try:
os.kill(pid, 0) # Signal 0 checks if process exists
print(f"Periodic context save daemon already running (PID: {pid})")
return 1
except OSError:
# Process not running, remove stale PID file
PID_FILE.unlink()
# Start daemon process
if sys.platform == "win32":
# On Windows, use subprocess.Popen with DETACHED_PROCESS
import subprocess
CREATE_NO_WINDOW = 0x08000000
process = subprocess.Popen(
[sys.executable, __file__, "_monitor"],
creationflags=subprocess.DETACHED_PROCESS | CREATE_NO_WINDOW,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
else:
# On Unix, fork
import subprocess
process = subprocess.Popen(
[sys.executable, __file__, "_monitor"],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
# Save PID
with open(PID_FILE, "w") as f:
f.write(str(process.pid))
print(f"Started periodic context save daemon (PID: {process.pid})")
print(f"Logs: {LOG_FILE}")
return 0
def stop_daemon():
"""Stop the daemon"""
if not PID_FILE.exists():
print("Periodic context save daemon not running")
return 1
with open(PID_FILE) as f:
pid = int(f.read().strip())
try:
if sys.platform == "win32":
# On Windows, use taskkill
subprocess.run(["taskkill", "/F", "/PID", str(pid)], check=True)
else:
# On Unix, use kill
os.kill(pid, signal.SIGTERM)
print(f"Stopped periodic context save daemon (PID: {pid})")
PID_FILE.unlink()
if STATE_FILE.exists():
STATE_FILE.unlink()
return 0
except Exception as e:
print(f"Failed to stop daemon (PID: {pid}): {e}")
PID_FILE.unlink()
return 1
def check_status():
"""Check daemon status"""
if not PID_FILE.exists():
print("Periodic context save daemon not running")
return 1
with open(PID_FILE) as f:
pid = int(f.read().strip())
# Check if process is running
try:
os.kill(pid, 0)
except OSError:
print("Daemon PID file exists but process not running")
PID_FILE.unlink()
return 1
state = load_state()
active_seconds = state.get("active_seconds", 0)
print(f"Periodic context save daemon is running (PID: {pid})")
print(f"Active time: {active_seconds}s / {SAVE_INTERVAL_SECONDS}s")
if state.get("last_save"):
print(f"Last save: {state['last_save']}")
return 0
def main():
"""Main entry point"""
if len(sys.argv) < 2:
print("Usage: python periodic_context_save.py {start|stop|status}")
print()
print("Periodic context save daemon - saves context every 5 minutes of active time")
print()
print("Commands:")
print(" start - Start the background daemon")
print(" stop - Stop the daemon")
print(" status - Check daemon status")
return 1
command = sys.argv[1]
if command == "start":
return start_daemon()
elif command == "stop":
return stop_daemon()
elif command == "status":
return check_status()
elif command == "_monitor":
# Internal command - run monitor loop
monitor_loop()
return 0
else:
print(f"Unknown command: {command}")
return 1
if __name__ == "__main__":
sys.exit(main())

View File

@@ -0,0 +1,232 @@
#!/usr/bin/env python3
"""
Periodic Context Save - Windows Task Scheduler Version
This script is designed to be called every minute by Windows Task Scheduler.
It tracks active time and saves context every 5 minutes of activity.
Usage:
Schedule this to run every minute via Task Scheduler:
python .claude/hooks/periodic_save_check.py
"""
import os
import sys
import json
import subprocess
from datetime import datetime, timezone
from pathlib import Path
import requests
# Configuration
SCRIPT_DIR = Path(__file__).parent
CLAUDE_DIR = SCRIPT_DIR.parent
PROJECT_ROOT = CLAUDE_DIR.parent
STATE_FILE = CLAUDE_DIR / ".periodic-save-state.json"
LOG_FILE = CLAUDE_DIR / "periodic-save.log"
CONFIG_FILE = CLAUDE_DIR / "context-recall-config.env"
SAVE_INTERVAL_SECONDS = 300 # 5 minutes
def log(message):
"""Write log message"""
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
log_message = f"[{timestamp}] {message}\n"
try:
with open(LOG_FILE, "a") as f:
f.write(log_message)
except:
pass # Silent fail if can't write log
def load_config():
"""Load configuration from context-recall-config.env"""
config = {
"api_url": "http://172.16.3.30:8001",
"jwt_token": None,
}
if CONFIG_FILE.exists():
with open(CONFIG_FILE) as f:
for line in f:
line = line.strip()
if line.startswith("CLAUDE_API_URL="):
config["api_url"] = line.split("=", 1)[1]
elif line.startswith("JWT_TOKEN="):
config["jwt_token"] = line.split("=", 1)[1]
return config
def detect_project_id():
"""Detect project ID from git config"""
try:
os.chdir(PROJECT_ROOT)
# Try git config first
result = subprocess.run(
["git", "config", "--local", "claude.projectid"],
capture_output=True,
text=True,
check=False,
cwd=PROJECT_ROOT,
)
if result.returncode == 0 and result.stdout.strip():
return result.stdout.strip()
# Try to derive from git remote URL
result = subprocess.run(
["git", "config", "--get", "remote.origin.url"],
capture_output=True,
text=True,
check=False,
cwd=PROJECT_ROOT,
)
if result.returncode == 0 and result.stdout.strip():
import hashlib
return hashlib.md5(result.stdout.strip().encode()).hexdigest()
except Exception:
pass
return "unknown"
def is_claude_active():
"""Check if Claude Code is actively running"""
try:
# Check for Claude Code process
result = subprocess.run(
["tasklist.exe"],
capture_output=True,
text=True,
check=False,
)
# Look for claude, node, or other indicators
output_lower = result.stdout.lower()
if any(proc in output_lower for proc in ["claude", "node.exe", "code.exe"]):
# Also check for recent file modifications
import time
two_minutes_ago = time.time() - 120
# Check a few common directories for recent activity
for check_dir in [PROJECT_ROOT, PROJECT_ROOT / "api", PROJECT_ROOT / ".claude"]:
if check_dir.exists():
for file in check_dir.rglob("*"):
if file.is_file():
try:
if file.stat().st_mtime > two_minutes_ago:
return True
except:
continue
except Exception as e:
log(f"Error checking activity: {e}")
return False
def load_state():
"""Load state from state file"""
if STATE_FILE.exists():
try:
with open(STATE_FILE) as f:
return json.load(f)
except Exception:
pass
return {
"active_seconds": 0,
"last_check": None,
"last_save": None,
}
def save_state(state):
"""Save state to state file"""
state["last_check"] = datetime.now(timezone.utc).isoformat()
try:
with open(STATE_FILE, "w") as f:
json.dump(state, f, indent=2)
except:
pass # Silent fail
def save_periodic_context(config, project_id):
"""Save context to database via API"""
if not config["jwt_token"]:
log("No JWT token - cannot save context")
return False
title = f"Periodic Save - {datetime.now().strftime('%Y-%m-%d %H:%M')}"
summary = f"Auto-saved context after {SAVE_INTERVAL_SECONDS // 60} minutes of active work. Session in progress on project: {project_id}"
payload = {
"context_type": "session_summary",
"title": title,
"dense_summary": summary,
"relevance_score": 5.0,
"tags": json.dumps(["auto-save", "periodic", "active-session", project_id]),
}
try:
url = f"{config['api_url']}/api/conversation-contexts"
headers = {
"Authorization": f"Bearer {config['jwt_token']}",
"Content-Type": "application/json",
}
response = requests.post(url, json=payload, headers=headers, timeout=10)
if response.status_code in [200, 201]:
context_id = response.json().get('id', 'unknown')
log(f"✓ Context saved (ID: {context_id}, Active time: {SAVE_INTERVAL_SECONDS}s)")
return True
else:
log(f"✗ Failed to save: HTTP {response.status_code}")
return False
except Exception as e:
log(f"✗ Error saving context: {e}")
return False
def main():
"""Main entry point - called every minute by Task Scheduler"""
config = load_config()
state = load_state()
# Check if Claude is active
if is_claude_active():
# Increment active time (60 seconds per check)
state["active_seconds"] += 60
# Check if we've reached the save interval
if state["active_seconds"] >= SAVE_INTERVAL_SECONDS:
log(f"{SAVE_INTERVAL_SECONDS}s active time reached - saving context")
project_id = detect_project_id()
if save_periodic_context(config, project_id):
state["last_save"] = datetime.now(timezone.utc).isoformat()
# Reset timer
state["active_seconds"] = 0
save_state(state)
else:
# Not active - don't increment timer but save state
save_state(state)
return 0
if __name__ == "__main__":
try:
sys.exit(main())
except Exception as e:
log(f"Fatal error: {e}")
sys.exit(1)

View File

@@ -0,0 +1,11 @@
@echo off
REM Windows wrapper for periodic context save
REM Can be run from Task Scheduler every minute
cd /d D:\ClaudeTools
REM Run the check-and-save script
python .claude\hooks\periodic_save_check.py
REM Exit silently
exit /b 0

View File

@@ -0,0 +1,69 @@
# Setup Periodic Context Save - Windows Task Scheduler
# This script creates a scheduled task to run periodic_save_check.py every minute
# Uses pythonw.exe to run without console window
$TaskName = "ClaudeTools - Periodic Context Save"
$ScriptPath = "D:\ClaudeTools\.claude\hooks\periodic_save_check.py"
$WorkingDir = "D:\ClaudeTools"
# Use pythonw.exe instead of python.exe to run without console window
$PythonExe = (Get-Command python).Source
$PythonDir = Split-Path $PythonExe -Parent
$PythonwPath = Join-Path $PythonDir "pythonw.exe"
# Fallback to python.exe if pythonw.exe doesn't exist (shouldn't happen)
if (-not (Test-Path $PythonwPath)) {
Write-Warning "pythonw.exe not found at $PythonwPath, falling back to python.exe"
$PythonwPath = $PythonExe
}
# Check if task already exists
$ExistingTask = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
if ($ExistingTask) {
Write-Host "Task '$TaskName' already exists. Removing old task..."
Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false
}
# Create action to run Python script with pythonw.exe (no console window)
$Action = New-ScheduledTaskAction -Execute $PythonwPath `
-Argument $ScriptPath `
-WorkingDirectory $WorkingDir
# Create trigger to run every minute (indefinitely)
$Trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 1)
# Create settings - Hidden and DisallowStartIfOnBatteries set to false
$Settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable `
-ExecutionTimeLimit (New-TimeSpan -Minutes 5) `
-Hidden
# Create principal (run as current user, no window)
$Principal = New-ScheduledTaskPrincipal -UserId "$env:USERDOMAIN\$env:USERNAME" -LogonType S4U
# Register the task
Register-ScheduledTask -TaskName $TaskName `
-Action $Action `
-Trigger $Trigger `
-Settings $Settings `
-Principal $Principal `
-Description "Automatically saves Claude Code context every 5 minutes of active work"
Write-Host "[SUCCESS] Scheduled task created successfully!"
Write-Host ""
Write-Host "Task Name: $TaskName"
Write-Host "Runs: Every 1 minute (HIDDEN - no console window)"
Write-Host "Action: Checks activity and saves context every 5 minutes"
Write-Host "Executable: $PythonwPath (pythonw.exe = no window)"
Write-Host ""
Write-Host "To verify task is hidden:"
Write-Host " Get-ScheduledTask -TaskName '$TaskName' | Select-Object -ExpandProperty Settings"
Write-Host ""
Write-Host "To remove:"
Write-Host " Unregister-ScheduledTask -TaskName '$TaskName' -Confirm:`$false"
Write-Host ""
Write-Host "View logs:"
Write-Host ' Get-Content D:\ClaudeTools\.claude\periodic-save.log -Tail 20'

110
.claude/hooks/sync-contexts Normal file
View File

@@ -0,0 +1,110 @@
#!/bin/bash
#
# Sync Queued Contexts to Database
# Uploads any locally queued contexts to the central API
# Can be run manually or called automatically by hooks
#
# Usage: bash .claude/hooks/sync-contexts
#
# Load configuration
CLAUDE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
CONFIG_FILE="$CLAUDE_DIR/context-recall-config.env"
if [ -f "$CONFIG_FILE" ]; then
source "$CONFIG_FILE"
fi
# Default values
API_URL="${CLAUDE_API_URL:-http://172.16.3.30:8001}"
QUEUE_DIR="$CLAUDE_DIR/context-queue"
PENDING_DIR="$QUEUE_DIR/pending"
UPLOADED_DIR="$QUEUE_DIR/uploaded"
FAILED_DIR="$QUEUE_DIR/failed"
# Exit if no JWT token
if [ -z "$JWT_TOKEN" ]; then
echo "ERROR: No JWT token available" >&2
exit 1
fi
# Create directories if they don't exist
mkdir -p "$PENDING_DIR" "$UPLOADED_DIR" "$FAILED_DIR" 2>/dev/null
# Check if there are any pending files
PENDING_COUNT=$(find "$PENDING_DIR" -type f -name "*.json" 2>/dev/null | wc -l)
if [ "$PENDING_COUNT" -eq 0 ]; then
# No pending contexts to sync
exit 0
fi
echo "==================================="
echo "Syncing Queued Contexts"
echo "==================================="
echo "Found $PENDING_COUNT pending context(s)"
echo ""
# Process each pending file
SUCCESS_COUNT=0
FAIL_COUNT=0
for QUEUE_FILE in "$PENDING_DIR"/*.json; do
# Skip if no files match
[ -e "$QUEUE_FILE" ] || continue
FILENAME=$(basename "$QUEUE_FILE")
echo "Processing: $FILENAME"
# Read the payload
PAYLOAD=$(cat "$QUEUE_FILE")
# Determine endpoint based on filename
if [[ "$FILENAME" == *"_state.json" ]]; then
ENDPOINT="${API_URL}/api/project-states"
else
ENDPOINT="${API_URL}/api/conversation-contexts"
fi
# Try to POST to API
RESPONSE=$(curl -s --max-time 10 -w "\n%{http_code}" \
-X POST "$ENDPOINT" \
-H "Authorization: Bearer ${JWT_TOKEN}" \
-H "Content-Type: application/json" \
-d "$PAYLOAD" 2>/dev/null)
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "201" ]; then
# Success - move to uploaded directory
mv "$QUEUE_FILE" "$UPLOADED_DIR/"
echo " ✓ Uploaded successfully"
((SUCCESS_COUNT++))
else
# Failed - move to failed directory for manual review
mv "$QUEUE_FILE" "$FAILED_DIR/"
echo " ✗ Upload failed (HTTP $HTTP_CODE) - moved to failed/"
((FAIL_COUNT++))
fi
done
echo ""
echo "==================================="
echo "Sync Complete"
echo "==================================="
echo "Successful: $SUCCESS_COUNT"
echo "Failed: $FAIL_COUNT"
echo ""
# Clean up old uploaded files (keep last 100)
UPLOADED_COUNT=$(find "$UPLOADED_DIR" -type f -name "*.json" 2>/dev/null | wc -l)
if [ "$UPLOADED_COUNT" -gt 100 ]; then
echo "Cleaning up old uploaded contexts (keeping last 100)..."
find "$UPLOADED_DIR" -type f -name "*.json" -printf '%T@ %p\n' | \
sort -n | \
head -n -100 | \
cut -d' ' -f2- | \
xargs rm -f
fi
exit 0

View File

@@ -1,13 +1,14 @@
#!/bin/bash
#
# Claude Code Hook: task-complete
# Claude Code Hook: task-complete (v2 - with offline support)
# Runs AFTER a task is completed
# Saves conversation context to the database for future recall
# FALLBACK: Queues locally when API is unavailable, syncs later
#
# Expected environment variables:
# CLAUDE_PROJECT_ID - UUID of the current project
# JWT_TOKEN - Authentication token for API
# CLAUDE_API_URL - API base URL (default: http://localhost:8000)
# CLAUDE_API_URL - API base URL (default: http://172.16.3.30:8001)
# CONTEXT_RECALL_ENABLED - Set to "false" to disable (default: true)
# TASK_SUMMARY - Summary of completed task (auto-generated by Claude)
# TASK_FILES - Files modified during task (comma-separated)
@@ -20,9 +21,15 @@ if [ -f "$CONFIG_FILE" ]; then
fi
# Default values
API_URL="${CLAUDE_API_URL:-http://localhost:8000}"
API_URL="${CLAUDE_API_URL:-http://172.16.3.30:8001}"
ENABLED="${CONTEXT_RECALL_ENABLED:-true}"
# Local storage paths
CLAUDE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
QUEUE_DIR="$CLAUDE_DIR/context-queue"
PENDING_DIR="$QUEUE_DIR/pending"
UPLOADED_DIR="$QUEUE_DIR/uploaded"
# Exit early if disabled
if [ "$ENABLED" != "true" ]; then
exit 0
@@ -42,13 +49,17 @@ else
PROJECT_ID="$CLAUDE_PROJECT_ID"
fi
# Exit if no project ID or JWT token
if [ -z "$PROJECT_ID" ] || [ -z "$JWT_TOKEN" ]; then
# Exit if no project ID
if [ -z "$PROJECT_ID" ]; then
exit 0
fi
# Create queue directories if they don't exist
mkdir -p "$PENDING_DIR" "$UPLOADED_DIR" 2>/dev/null
# Gather task information
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
TIMESTAMP_FILENAME=$(date -u +"%Y%m%d_%H%M%S")
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
GIT_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "none")
@@ -104,13 +115,6 @@ CONTEXT_PAYLOAD=$(cat <<EOF
EOF
)
# POST to conversation-contexts endpoint
RESPONSE=$(curl -s --max-time 5 \
-X POST "${API_URL}/api/conversation-contexts" \
-H "Authorization: Bearer ${JWT_TOKEN}" \
-H "Content-Type: application/json" \
-d "$CONTEXT_PAYLOAD" 2>/dev/null)
# Update project state
PROJECT_STATE_PAYLOAD=$(cat <<EOF
{
@@ -126,15 +130,53 @@ PROJECT_STATE_PAYLOAD=$(cat <<EOF
EOF
)
curl -s --max-time 5 \
-X POST "${API_URL}/api/project-states" \
-H "Authorization: Bearer ${JWT_TOKEN}" \
-H "Content-Type: application/json" \
-d "$PROJECT_STATE_PAYLOAD" 2>/dev/null >/dev/null
# Try to POST to API if we have a JWT token
API_SUCCESS=false
if [ -n "$JWT_TOKEN" ]; then
RESPONSE=$(curl -s --max-time 5 -w "\n%{http_code}" \
-X POST "${API_URL}/api/conversation-contexts" \
-H "Authorization: Bearer ${JWT_TOKEN}" \
-H "Content-Type: application/json" \
-d "$CONTEXT_PAYLOAD" 2>/dev/null)
# Log success (optional - comment out for silent operation)
if [ -n "$RESPONSE" ]; then
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
RESPONSE_BODY=$(echo "$RESPONSE" | sed '$d')
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "201" ]; then
API_SUCCESS=true
# Also update project state
curl -s --max-time 5 \
-X POST "${API_URL}/api/project-states" \
-H "Authorization: Bearer ${JWT_TOKEN}" \
-H "Content-Type: application/json" \
-d "$PROJECT_STATE_PAYLOAD" 2>/dev/null >/dev/null
fi
fi
# If API call failed, queue locally
if [ "$API_SUCCESS" = "false" ]; then
# Save context to pending queue
QUEUE_FILE="$PENDING_DIR/${PROJECT_ID}_${TIMESTAMP_FILENAME}_context.json"
echo "$CONTEXT_PAYLOAD" > "$QUEUE_FILE"
# Save project state to pending queue
STATE_QUEUE_FILE="$PENDING_DIR/${PROJECT_ID}_${TIMESTAMP_FILENAME}_state.json"
echo "$PROJECT_STATE_PAYLOAD" > "$STATE_QUEUE_FILE"
echo "⚠ Context queued locally (API unavailable) - will sync when online" >&2
# Try to sync in background (opportunistic)
if [ -n "$JWT_TOKEN" ]; then
bash "$(dirname "${BASH_SOURCE[0]}")/sync-contexts" >/dev/null 2>&1 &
fi
else
echo "✓ Context saved to database" >&2
# Trigger background sync of any queued items
if [ -n "$JWT_TOKEN" ]; then
bash "$(dirname "${BASH_SOURCE[0]}")/sync-contexts" >/dev/null 2>&1 &
fi
fi
exit 0

View File

@@ -0,0 +1,182 @@
#!/bin/bash
#
# Claude Code Hook: task-complete (v2 - with offline support)
# Runs AFTER a task is completed
# Saves conversation context to the database for future recall
# FALLBACK: Queues locally when API is unavailable, syncs later
#
# Expected environment variables:
# CLAUDE_PROJECT_ID - UUID of the current project
# JWT_TOKEN - Authentication token for API
# CLAUDE_API_URL - API base URL (default: http://172.16.3.30:8001)
# CONTEXT_RECALL_ENABLED - Set to "false" to disable (default: true)
# TASK_SUMMARY - Summary of completed task (auto-generated by Claude)
# TASK_FILES - Files modified during task (comma-separated)
#
# Load configuration if exists
CONFIG_FILE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/context-recall-config.env"
if [ -f "$CONFIG_FILE" ]; then
source "$CONFIG_FILE"
fi
# Default values
API_URL="${CLAUDE_API_URL:-http://172.16.3.30:8001}"
ENABLED="${CONTEXT_RECALL_ENABLED:-true}"
# Local storage paths
CLAUDE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
QUEUE_DIR="$CLAUDE_DIR/context-queue"
PENDING_DIR="$QUEUE_DIR/pending"
UPLOADED_DIR="$QUEUE_DIR/uploaded"
# Exit early if disabled
if [ "$ENABLED" != "true" ]; then
exit 0
fi
# Detect project ID (same logic as user-prompt-submit)
if [ -z "$CLAUDE_PROJECT_ID" ]; then
PROJECT_ID=$(git config --local claude.projectid 2>/dev/null)
if [ -z "$PROJECT_ID" ]; then
GIT_REMOTE=$(git config --get remote.origin.url 2>/dev/null)
if [ -n "$GIT_REMOTE" ]; then
PROJECT_ID=$(echo -n "$GIT_REMOTE" | md5sum | cut -d' ' -f1)
fi
fi
else
PROJECT_ID="$CLAUDE_PROJECT_ID"
fi
# Exit if no project ID
if [ -z "$PROJECT_ID" ]; then
exit 0
fi
# Create queue directories if they don't exist
mkdir -p "$PENDING_DIR" "$UPLOADED_DIR" 2>/dev/null
# Gather task information
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
TIMESTAMP_FILENAME=$(date -u +"%Y%m%d_%H%M%S")
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
GIT_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "none")
# Get recent git changes
CHANGED_FILES=$(git diff --name-only HEAD~1 2>/dev/null | head -10 | tr '\n' ',' | sed 's/,$//')
if [ -z "$CHANGED_FILES" ]; then
CHANGED_FILES="${TASK_FILES:-}"
fi
# Create task summary
if [ -z "$TASK_SUMMARY" ]; then
# Generate basic summary from git log if no summary provided
TASK_SUMMARY=$(git log -1 --pretty=format:"%s" 2>/dev/null || echo "Task completed")
fi
# Build context payload
CONTEXT_TITLE="Session: ${TIMESTAMP}"
CONTEXT_TYPE="session_summary"
RELEVANCE_SCORE=7.0
# Create dense summary
DENSE_SUMMARY="Task completed on branch '${GIT_BRANCH}' (commit: ${GIT_COMMIT}).
Summary: ${TASK_SUMMARY}
Modified files: ${CHANGED_FILES:-none}
Timestamp: ${TIMESTAMP}"
# Escape JSON strings
escape_json() {
echo "$1" | python3 -c "import sys, json; print(json.dumps(sys.stdin.read())[1:-1])"
}
ESCAPED_TITLE=$(escape_json "$CONTEXT_TITLE")
ESCAPED_SUMMARY=$(escape_json "$DENSE_SUMMARY")
# Save context to database
CONTEXT_PAYLOAD=$(cat <<EOF
{
"project_id": "${PROJECT_ID}",
"context_type": "${CONTEXT_TYPE}",
"title": ${ESCAPED_TITLE},
"dense_summary": ${ESCAPED_SUMMARY},
"relevance_score": ${RELEVANCE_SCORE},
"metadata": {
"git_branch": "${GIT_BRANCH}",
"git_commit": "${GIT_COMMIT}",
"files_modified": "${CHANGED_FILES}",
"timestamp": "${TIMESTAMP}"
}
}
EOF
)
# Update project state
PROJECT_STATE_PAYLOAD=$(cat <<EOF
{
"project_id": "${PROJECT_ID}",
"state_data": {
"last_task_completion": "${TIMESTAMP}",
"last_git_commit": "${GIT_COMMIT}",
"last_git_branch": "${GIT_BRANCH}",
"recent_files": "${CHANGED_FILES}"
},
"state_type": "task_completion"
}
EOF
)
# Try to POST to API if we have a JWT token
API_SUCCESS=false
if [ -n "$JWT_TOKEN" ]; then
RESPONSE=$(curl -s --max-time 5 -w "\n%{http_code}" \
-X POST "${API_URL}/api/conversation-contexts" \
-H "Authorization: Bearer ${JWT_TOKEN}" \
-H "Content-Type: application/json" \
-d "$CONTEXT_PAYLOAD" 2>/dev/null)
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
RESPONSE_BODY=$(echo "$RESPONSE" | sed '$d')
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "201" ]; then
API_SUCCESS=true
# Also update project state
curl -s --max-time 5 \
-X POST "${API_URL}/api/project-states" \
-H "Authorization: Bearer ${JWT_TOKEN}" \
-H "Content-Type: application/json" \
-d "$PROJECT_STATE_PAYLOAD" 2>/dev/null >/dev/null
fi
fi
# If API call failed, queue locally
if [ "$API_SUCCESS" = "false" ]; then
# Save context to pending queue
QUEUE_FILE="$PENDING_DIR/${PROJECT_ID}_${TIMESTAMP_FILENAME}_context.json"
echo "$CONTEXT_PAYLOAD" > "$QUEUE_FILE"
# Save project state to pending queue
STATE_QUEUE_FILE="$PENDING_DIR/${PROJECT_ID}_${TIMESTAMP_FILENAME}_state.json"
echo "$PROJECT_STATE_PAYLOAD" > "$STATE_QUEUE_FILE"
echo "⚠ Context queued locally (API unavailable) - will sync when online" >&2
# Try to sync in background (opportunistic)
if [ -n "$JWT_TOKEN" ]; then
bash "$(dirname "${BASH_SOURCE[0]}")/sync-contexts" >/dev/null 2>&1 &
fi
else
echo "✓ Context saved to database" >&2
# Trigger background sync of any queued items
if [ -n "$JWT_TOKEN" ]; then
bash "$(dirname "${BASH_SOURCE[0]}")/sync-contexts" >/dev/null 2>&1 &
fi
fi
exit 0

View File

@@ -0,0 +1,140 @@
#!/bin/bash
#
# Claude Code Hook: task-complete
# Runs AFTER a task is completed
# Saves conversation context to the database for future recall
#
# Expected environment variables:
# CLAUDE_PROJECT_ID - UUID of the current project
# JWT_TOKEN - Authentication token for API
# CLAUDE_API_URL - API base URL (default: http://localhost:8000)
# CONTEXT_RECALL_ENABLED - Set to "false" to disable (default: true)
# TASK_SUMMARY - Summary of completed task (auto-generated by Claude)
# TASK_FILES - Files modified during task (comma-separated)
#
# Load configuration if exists
CONFIG_FILE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/context-recall-config.env"
if [ -f "$CONFIG_FILE" ]; then
source "$CONFIG_FILE"
fi
# Default values
API_URL="${CLAUDE_API_URL:-http://localhost:8000}"
ENABLED="${CONTEXT_RECALL_ENABLED:-true}"
# Exit early if disabled
if [ "$ENABLED" != "true" ]; then
exit 0
fi
# Detect project ID (same logic as user-prompt-submit)
if [ -z "$CLAUDE_PROJECT_ID" ]; then
PROJECT_ID=$(git config --local claude.projectid 2>/dev/null)
if [ -z "$PROJECT_ID" ]; then
GIT_REMOTE=$(git config --get remote.origin.url 2>/dev/null)
if [ -n "$GIT_REMOTE" ]; then
PROJECT_ID=$(echo -n "$GIT_REMOTE" | md5sum | cut -d' ' -f1)
fi
fi
else
PROJECT_ID="$CLAUDE_PROJECT_ID"
fi
# Exit if no project ID or JWT token
if [ -z "$PROJECT_ID" ] || [ -z "$JWT_TOKEN" ]; then
exit 0
fi
# Gather task information
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
GIT_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "none")
# Get recent git changes
CHANGED_FILES=$(git diff --name-only HEAD~1 2>/dev/null | head -10 | tr '\n' ',' | sed 's/,$//')
if [ -z "$CHANGED_FILES" ]; then
CHANGED_FILES="${TASK_FILES:-}"
fi
# Create task summary
if [ -z "$TASK_SUMMARY" ]; then
# Generate basic summary from git log if no summary provided
TASK_SUMMARY=$(git log -1 --pretty=format:"%s" 2>/dev/null || echo "Task completed")
fi
# Build context payload
CONTEXT_TITLE="Session: ${TIMESTAMP}"
CONTEXT_TYPE="session_summary"
RELEVANCE_SCORE=7.0
# Create dense summary
DENSE_SUMMARY="Task completed on branch '${GIT_BRANCH}' (commit: ${GIT_COMMIT}).
Summary: ${TASK_SUMMARY}
Modified files: ${CHANGED_FILES:-none}
Timestamp: ${TIMESTAMP}"
# Escape JSON strings
escape_json() {
echo "$1" | python3 -c "import sys, json; print(json.dumps(sys.stdin.read())[1:-1])"
}
ESCAPED_TITLE=$(escape_json "$CONTEXT_TITLE")
ESCAPED_SUMMARY=$(escape_json "$DENSE_SUMMARY")
# Save context to database
CONTEXT_PAYLOAD=$(cat <<EOF
{
"project_id": "${PROJECT_ID}",
"context_type": "${CONTEXT_TYPE}",
"title": ${ESCAPED_TITLE},
"dense_summary": ${ESCAPED_SUMMARY},
"relevance_score": ${RELEVANCE_SCORE},
"metadata": {
"git_branch": "${GIT_BRANCH}",
"git_commit": "${GIT_COMMIT}",
"files_modified": "${CHANGED_FILES}",
"timestamp": "${TIMESTAMP}"
}
}
EOF
)
# POST to conversation-contexts endpoint
RESPONSE=$(curl -s --max-time 5 \
-X POST "${API_URL}/api/conversation-contexts" \
-H "Authorization: Bearer ${JWT_TOKEN}" \
-H "Content-Type: application/json" \
-d "$CONTEXT_PAYLOAD" 2>/dev/null)
# Update project state
PROJECT_STATE_PAYLOAD=$(cat <<EOF
{
"project_id": "${PROJECT_ID}",
"state_data": {
"last_task_completion": "${TIMESTAMP}",
"last_git_commit": "${GIT_COMMIT}",
"last_git_branch": "${GIT_BRANCH}",
"recent_files": "${CHANGED_FILES}"
},
"state_type": "task_completion"
}
EOF
)
curl -s --max-time 5 \
-X POST "${API_URL}/api/project-states" \
-H "Authorization: Bearer ${JWT_TOKEN}" \
-H "Content-Type: application/json" \
-d "$PROJECT_STATE_PAYLOAD" 2>/dev/null >/dev/null
# Log success (optional - comment out for silent operation)
if [ -n "$RESPONSE" ]; then
echo "✓ Context saved to database" >&2
fi
exit 0

View File

@@ -0,0 +1,85 @@
# Quick Update - Make Existing Periodic Save Task Invisible
# This script updates the existing task to run without showing a window
$TaskName = "ClaudeTools - Periodic Context Save"
Write-Host "Updating task '$TaskName' to run invisibly..."
Write-Host ""
# Check if task exists
$Task = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
if (-not $Task) {
Write-Host "ERROR: Task '$TaskName' not found."
Write-Host "Run setup_periodic_save.ps1 to create it first."
exit 1
}
# Find pythonw.exe path
$PythonExe = (Get-Command python).Source
$PythonDir = Split-Path $PythonExe -Parent
$PythonwPath = Join-Path $PythonDir "pythonw.exe"
if (-not (Test-Path $PythonwPath)) {
Write-Host "ERROR: pythonw.exe not found at $PythonwPath"
Write-Host "Please reinstall Python to get pythonw.exe"
exit 1
}
Write-Host "Found pythonw.exe at: $PythonwPath"
# Update the action to use pythonw.exe
$NewAction = New-ScheduledTaskAction -Execute $PythonwPath `
-Argument "D:\ClaudeTools\.claude\hooks\periodic_save_check.py" `
-WorkingDirectory "D:\ClaudeTools"
# Update settings to be hidden
$NewSettings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable `
-ExecutionTimeLimit (New-TimeSpan -Minutes 5) `
-Hidden
# Update principal to run in background (S4U = Service-For-User)
$NewPrincipal = New-ScheduledTaskPrincipal -UserId "$env:USERDOMAIN\$env:USERNAME" -LogonType S4U
# Get existing trigger (preserve it)
$ExistingTrigger = $Task.Triggers
# Update the task
Set-ScheduledTask -TaskName $TaskName `
-Action $NewAction `
-Settings $NewSettings `
-Principal $NewPrincipal `
-Trigger $ExistingTrigger | Out-Null
Write-Host ""
Write-Host "[SUCCESS] Task updated successfully!"
Write-Host ""
Write-Host "Changes made:"
Write-Host " 1. Changed executable: python.exe -> pythonw.exe"
Write-Host " 2. Set task to Hidden"
Write-Host " 3. Changed LogonType: Interactive -> S4U (background)"
Write-Host ""
Write-Host "Verification:"
# Show current settings
$UpdatedTask = Get-ScheduledTask -TaskName $TaskName
$Settings = $UpdatedTask.Settings
$Action = $UpdatedTask.Actions[0]
$Principal = $UpdatedTask.Principal
Write-Host " Executable: $($Action.Execute)"
Write-Host " Hidden: $($Settings.Hidden)"
Write-Host " LogonType: $($Principal.LogonType)"
Write-Host ""
if ($Settings.Hidden -and $Action.Execute -like "*pythonw.exe" -and $Principal.LogonType -eq "S4U") {
Write-Host "[OK] All settings correct - task will run invisibly!"
} else {
Write-Host "[WARNING] Some settings may not be correct - please verify manually"
}
Write-Host ""
Write-Host "The task will now run invisibly without showing any console window."
Write-Host ""

View File

@@ -1,13 +1,14 @@
#!/bin/bash
#
# Claude Code Hook: user-prompt-submit
# Claude Code Hook: user-prompt-submit (v2 - with offline support)
# Runs BEFORE each user message is processed
# Injects relevant context from the database into the conversation
# FALLBACK: Uses local cache when API is unavailable
#
# Expected environment variables:
# CLAUDE_PROJECT_ID - UUID of the current project
# JWT_TOKEN - Authentication token for API
# CLAUDE_API_URL - API base URL (default: http://localhost:8000)
# CLAUDE_API_URL - API base URL (default: http://172.16.3.30:8001)
# CONTEXT_RECALL_ENABLED - Set to "false" to disable (default: true)
# MIN_RELEVANCE_SCORE - Minimum score for context (default: 5.0)
# MAX_CONTEXTS - Maximum number of contexts to retrieve (default: 10)
@@ -20,11 +21,16 @@ if [ -f "$CONFIG_FILE" ]; then
fi
# Default values
API_URL="${CLAUDE_API_URL:-http://localhost:8000}"
API_URL="${CLAUDE_API_URL:-http://172.16.3.30:8001}"
ENABLED="${CONTEXT_RECALL_ENABLED:-true}"
MIN_SCORE="${MIN_RELEVANCE_SCORE:-5.0}"
MAX_ITEMS="${MAX_CONTEXTS:-10}"
# Local storage paths
CLAUDE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
CACHE_DIR="$CLAUDE_DIR/context-cache"
QUEUE_DIR="$CLAUDE_DIR/context-queue"
# Exit early if disabled
if [ "$ENABLED" != "true" ]; then
exit 0
@@ -53,41 +59,74 @@ if [ -z "$PROJECT_ID" ]; then
exit 0
fi
# Exit if no JWT token
if [ -z "$JWT_TOKEN" ]; then
exit 0
# Create cache directory if it doesn't exist
PROJECT_CACHE_DIR="$CACHE_DIR/$PROJECT_ID"
mkdir -p "$PROJECT_CACHE_DIR" 2>/dev/null
# Try to sync any queued contexts first (opportunistic)
if [ -d "$QUEUE_DIR/pending" ] && [ -n "$JWT_TOKEN" ]; then
bash "$(dirname "${BASH_SOURCE[0]}")/sync-contexts" >/dev/null 2>&1 &
fi
# Build API request URL
RECALL_URL="${API_URL}/api/conversation-contexts/recall"
QUERY_PARAMS="project_id=${PROJECT_ID}&limit=${MAX_ITEMS}&min_relevance_score=${MIN_SCORE}"
# Fetch context from API (with timeout and error handling)
CONTEXT_RESPONSE=$(curl -s --max-time 3 \
"${RECALL_URL}?${QUERY_PARAMS}" \
-H "Authorization: Bearer ${JWT_TOKEN}" \
-H "Accept: application/json" 2>/dev/null)
# Try to fetch context from API (with timeout and error handling)
API_AVAILABLE=false
if [ -n "$JWT_TOKEN" ]; then
CONTEXT_RESPONSE=$(curl -s --max-time 3 \
"${RECALL_URL}?${QUERY_PARAMS}" \
-H "Authorization: Bearer ${JWT_TOKEN}" \
-H "Accept: application/json" 2>/dev/null)
# Check if request was successful
if [ $? -ne 0 ] || [ -z "$CONTEXT_RESPONSE" ]; then
# Silent failure - API unavailable
exit 0
if [ $? -eq 0 ] && [ -n "$CONTEXT_RESPONSE" ]; then
# Check if response is valid JSON (not an error)
echo "$CONTEXT_RESPONSE" | python3 -c "import sys, json; json.load(sys.stdin)" 2>/dev/null
if [ $? -eq 0 ]; then
API_AVAILABLE=true
# Save to cache for offline use
echo "$CONTEXT_RESPONSE" > "$PROJECT_CACHE_DIR/latest.json"
echo "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" > "$PROJECT_CACHE_DIR/last_updated"
fi
fi
fi
# Parse and format context (expects JSON array of context objects)
# Example response: [{"title": "...", "dense_summary": "...", "relevance_score": 8.5}, ...]
# Fallback to local cache if API unavailable
if [ "$API_AVAILABLE" = "false" ]; then
if [ -f "$PROJECT_CACHE_DIR/latest.json" ]; then
CONTEXT_RESPONSE=$(cat "$PROJECT_CACHE_DIR/latest.json")
CACHE_AGE="unknown"
if [ -f "$PROJECT_CACHE_DIR/last_updated" ]; then
CACHE_AGE=$(cat "$PROJECT_CACHE_DIR/last_updated")
fi
echo "<!-- Using cached context (API unavailable) - Last updated: $CACHE_AGE -->" >&2
else
# No cache available, exit silently
exit 0
fi
fi
# Parse and format context
CONTEXT_COUNT=$(echo "$CONTEXT_RESPONSE" | grep -o '"id"' | wc -l)
if [ "$CONTEXT_COUNT" -gt 0 ]; then
echo "<!-- Context Recall: Retrieved $CONTEXT_COUNT relevant context(s) -->"
if [ "$API_AVAILABLE" = "true" ]; then
echo "<!-- Context Recall: Retrieved $CONTEXT_COUNT relevant context(s) from API -->"
else
echo "<!-- Context Recall: Retrieved $CONTEXT_COUNT relevant context(s) from LOCAL CACHE (offline mode) -->"
fi
echo ""
echo "## 📚 Previous Context"
echo ""
echo "The following context has been automatically recalled from previous sessions:"
if [ "$API_AVAILABLE" = "false" ]; then
echo "⚠️ **Offline Mode** - Using cached context (API unavailable)"
echo ""
fi
echo "The following context has been automatically recalled:"
echo ""
# Extract and format each context entry
# Note: This uses simple text parsing. For production, consider using jq if available.
echo "$CONTEXT_RESPONSE" | python3 -c "
import sys, json
try:
@@ -111,7 +150,11 @@ except:
" 2>/dev/null
echo ""
echo "*This context was automatically injected to help maintain continuity across sessions.*"
if [ "$API_AVAILABLE" = "true" ]; then
echo "*Context automatically injected to maintain continuity across sessions.*"
else
echo "*Context from local cache - new context will sync when API is available.*"
fi
echo ""
fi

View File

@@ -0,0 +1,162 @@
#!/bin/bash
#
# Claude Code Hook: user-prompt-submit (v2 - with offline support)
# Runs BEFORE each user message is processed
# Injects relevant context from the database into the conversation
# FALLBACK: Uses local cache when API is unavailable
#
# Expected environment variables:
# CLAUDE_PROJECT_ID - UUID of the current project
# JWT_TOKEN - Authentication token for API
# CLAUDE_API_URL - API base URL (default: http://172.16.3.30:8001)
# CONTEXT_RECALL_ENABLED - Set to "false" to disable (default: true)
# MIN_RELEVANCE_SCORE - Minimum score for context (default: 5.0)
# MAX_CONTEXTS - Maximum number of contexts to retrieve (default: 10)
#
# Load configuration if exists
CONFIG_FILE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/context-recall-config.env"
if [ -f "$CONFIG_FILE" ]; then
source "$CONFIG_FILE"
fi
# Default values
API_URL="${CLAUDE_API_URL:-http://172.16.3.30:8001}"
ENABLED="${CONTEXT_RECALL_ENABLED:-true}"
MIN_SCORE="${MIN_RELEVANCE_SCORE:-5.0}"
MAX_ITEMS="${MAX_CONTEXTS:-10}"
# Local storage paths
CLAUDE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
CACHE_DIR="$CLAUDE_DIR/context-cache"
QUEUE_DIR="$CLAUDE_DIR/context-queue"
# Exit early if disabled
if [ "$ENABLED" != "true" ]; then
exit 0
fi
# Detect project ID from git repo if not set
if [ -z "$CLAUDE_PROJECT_ID" ]; then
# Try to get from git config
PROJECT_ID=$(git config --local claude.projectid 2>/dev/null)
if [ -z "$PROJECT_ID" ]; then
# Try to derive from git remote URL
GIT_REMOTE=$(git config --get remote.origin.url 2>/dev/null)
if [ -n "$GIT_REMOTE" ]; then
# Hash the remote URL to create a consistent ID
PROJECT_ID=$(echo -n "$GIT_REMOTE" | md5sum | cut -d' ' -f1)
fi
fi
else
PROJECT_ID="$CLAUDE_PROJECT_ID"
fi
# Exit if no project ID available
if [ -z "$PROJECT_ID" ]; then
# Silent exit - no context available
exit 0
fi
# Create cache directory if it doesn't exist
PROJECT_CACHE_DIR="$CACHE_DIR/$PROJECT_ID"
mkdir -p "$PROJECT_CACHE_DIR" 2>/dev/null
# Try to sync any queued contexts first (opportunistic)
if [ -d "$QUEUE_DIR/pending" ] && [ -n "$JWT_TOKEN" ]; then
bash "$(dirname "${BASH_SOURCE[0]}")/sync-contexts" >/dev/null 2>&1 &
fi
# Build API request URL
RECALL_URL="${API_URL}/api/conversation-contexts/recall"
QUERY_PARAMS="project_id=${PROJECT_ID}&limit=${MAX_ITEMS}&min_relevance_score=${MIN_SCORE}"
# Try to fetch context from API (with timeout and error handling)
API_AVAILABLE=false
if [ -n "$JWT_TOKEN" ]; then
CONTEXT_RESPONSE=$(curl -s --max-time 3 \
"${RECALL_URL}?${QUERY_PARAMS}" \
-H "Authorization: Bearer ${JWT_TOKEN}" \
-H "Accept: application/json" 2>/dev/null)
if [ $? -eq 0 ] && [ -n "$CONTEXT_RESPONSE" ]; then
# Check if response is valid JSON (not an error)
echo "$CONTEXT_RESPONSE" | python3 -c "import sys, json; json.load(sys.stdin)" 2>/dev/null
if [ $? -eq 0 ]; then
API_AVAILABLE=true
# Save to cache for offline use
echo "$CONTEXT_RESPONSE" > "$PROJECT_CACHE_DIR/latest.json"
echo "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" > "$PROJECT_CACHE_DIR/last_updated"
fi
fi
fi
# Fallback to local cache if API unavailable
if [ "$API_AVAILABLE" = "false" ]; then
if [ -f "$PROJECT_CACHE_DIR/latest.json" ]; then
CONTEXT_RESPONSE=$(cat "$PROJECT_CACHE_DIR/latest.json")
CACHE_AGE="unknown"
if [ -f "$PROJECT_CACHE_DIR/last_updated" ]; then
CACHE_AGE=$(cat "$PROJECT_CACHE_DIR/last_updated")
fi
echo "<!-- Using cached context (API unavailable) - Last updated: $CACHE_AGE -->" >&2
else
# No cache available, exit silently
exit 0
fi
fi
# Parse and format context
CONTEXT_COUNT=$(echo "$CONTEXT_RESPONSE" | grep -o '"id"' | wc -l)
if [ "$CONTEXT_COUNT" -gt 0 ]; then
if [ "$API_AVAILABLE" = "true" ]; then
echo "<!-- Context Recall: Retrieved $CONTEXT_COUNT relevant context(s) from API -->"
else
echo "<!-- Context Recall: Retrieved $CONTEXT_COUNT relevant context(s) from LOCAL CACHE (offline mode) -->"
fi
echo ""
echo "## 📚 Previous Context"
echo ""
if [ "$API_AVAILABLE" = "false" ]; then
echo "⚠️ **Offline Mode** - Using cached context (API unavailable)"
echo ""
fi
echo "The following context has been automatically recalled:"
echo ""
# Extract and format each context entry
echo "$CONTEXT_RESPONSE" | python3 -c "
import sys, json
try:
contexts = json.load(sys.stdin)
if isinstance(contexts, list):
for i, ctx in enumerate(contexts, 1):
title = ctx.get('title', 'Untitled')
summary = ctx.get('dense_summary', '')
score = ctx.get('relevance_score', 0)
ctx_type = ctx.get('context_type', 'unknown')
print(f'### {i}. {title} (Score: {score}/10)')
print(f'*Type: {ctx_type}*')
print()
print(summary)
print()
print('---')
print()
except:
pass
" 2>/dev/null
echo ""
if [ "$API_AVAILABLE" = "true" ]; then
echo "*Context automatically injected to maintain continuity across sessions.*"
else
echo "*Context from local cache - new context will sync when API is available.*"
fi
echo ""
fi
# Exit successfully
exit 0

View File

@@ -0,0 +1,119 @@
#!/bin/bash
#
# Claude Code Hook: user-prompt-submit
# Runs BEFORE each user message is processed
# Injects relevant context from the database into the conversation
#
# Expected environment variables:
# CLAUDE_PROJECT_ID - UUID of the current project
# JWT_TOKEN - Authentication token for API
# CLAUDE_API_URL - API base URL (default: http://localhost:8000)
# CONTEXT_RECALL_ENABLED - Set to "false" to disable (default: true)
# MIN_RELEVANCE_SCORE - Minimum score for context (default: 5.0)
# MAX_CONTEXTS - Maximum number of contexts to retrieve (default: 10)
#
# Load configuration if exists
CONFIG_FILE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/context-recall-config.env"
if [ -f "$CONFIG_FILE" ]; then
source "$CONFIG_FILE"
fi
# Default values
API_URL="${CLAUDE_API_URL:-http://localhost:8000}"
ENABLED="${CONTEXT_RECALL_ENABLED:-true}"
MIN_SCORE="${MIN_RELEVANCE_SCORE:-5.0}"
MAX_ITEMS="${MAX_CONTEXTS:-10}"
# Exit early if disabled
if [ "$ENABLED" != "true" ]; then
exit 0
fi
# Detect project ID from git repo if not set
if [ -z "$CLAUDE_PROJECT_ID" ]; then
# Try to get from git config
PROJECT_ID=$(git config --local claude.projectid 2>/dev/null)
if [ -z "$PROJECT_ID" ]; then
# Try to derive from git remote URL
GIT_REMOTE=$(git config --get remote.origin.url 2>/dev/null)
if [ -n "$GIT_REMOTE" ]; then
# Hash the remote URL to create a consistent ID
PROJECT_ID=$(echo -n "$GIT_REMOTE" | md5sum | cut -d' ' -f1)
fi
fi
else
PROJECT_ID="$CLAUDE_PROJECT_ID"
fi
# Exit if no project ID available
if [ -z "$PROJECT_ID" ]; then
# Silent exit - no context available
exit 0
fi
# Exit if no JWT token
if [ -z "$JWT_TOKEN" ]; then
exit 0
fi
# Build API request URL
RECALL_URL="${API_URL}/api/conversation-contexts/recall"
QUERY_PARAMS="project_id=${PROJECT_ID}&limit=${MAX_ITEMS}&min_relevance_score=${MIN_SCORE}"
# Fetch context from API (with timeout and error handling)
CONTEXT_RESPONSE=$(curl -s --max-time 3 \
"${RECALL_URL}?${QUERY_PARAMS}" \
-H "Authorization: Bearer ${JWT_TOKEN}" \
-H "Accept: application/json" 2>/dev/null)
# Check if request was successful
if [ $? -ne 0 ] || [ -z "$CONTEXT_RESPONSE" ]; then
# Silent failure - API unavailable
exit 0
fi
# Parse and format context (expects JSON array of context objects)
# Example response: [{"title": "...", "dense_summary": "...", "relevance_score": 8.5}, ...]
CONTEXT_COUNT=$(echo "$CONTEXT_RESPONSE" | grep -o '"id"' | wc -l)
if [ "$CONTEXT_COUNT" -gt 0 ]; then
echo "<!-- Context Recall: Retrieved $CONTEXT_COUNT relevant context(s) -->"
echo ""
echo "## 📚 Previous Context"
echo ""
echo "The following context has been automatically recalled from previous sessions:"
echo ""
# Extract and format each context entry
# Note: This uses simple text parsing. For production, consider using jq if available.
echo "$CONTEXT_RESPONSE" | python3 -c "
import sys, json
try:
contexts = json.load(sys.stdin)
if isinstance(contexts, list):
for i, ctx in enumerate(contexts, 1):
title = ctx.get('title', 'Untitled')
summary = ctx.get('dense_summary', '')
score = ctx.get('relevance_score', 0)
ctx_type = ctx.get('context_type', 'unknown')
print(f'### {i}. {title} (Score: {score}/10)')
print(f'*Type: {ctx_type}*')
print()
print(summary)
print()
print('---')
print()
except:
pass
" 2>/dev/null
echo ""
echo "*This context was automatically injected to help maintain continuity across sessions.*"
echo ""
fi
# Exit successfully
exit 0