claude-self-reflect 2.8.2 → 2.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -1
- package/installer/cli.js +63 -58
- package/installer/setup-wizard-docker.js +58 -10
- package/mcp-server/pyproject.toml +1 -1
- package/mcp-server/src/status.py +97 -62
- package/package.json +1 -1
- package/scripts/import-conversations-unified.py +13 -1
package/README.md
CHANGED
|
@@ -126,7 +126,7 @@ Files are categorized by age and processed with priority queuing to ensure newes
|
|
|
126
126
|
|
|
127
127
|
- **Docker Desktop** (macOS/Windows) or **Docker Engine** (Linux)
|
|
128
128
|
- **Node.js** 16+ (for the setup wizard)
|
|
129
|
-
- **Claude
|
|
129
|
+
- **Claude Code** CLI
|
|
130
130
|
|
|
131
131
|
## 📖 Documentation
|
|
132
132
|
|
|
@@ -231,6 +231,98 @@ docker compose run --rm importer python /app/scripts/delta-metadata-update-safe.
|
|
|
231
231
|
|
|
232
232
|
</details>
|
|
233
233
|
|
|
234
|
+
## 🔧 Troubleshooting
|
|
235
|
+
|
|
236
|
+
### Common Issues and Solutions
|
|
237
|
+
|
|
238
|
+
#### 1. "No collections created" after import
|
|
239
|
+
**Symptom**: Import runs but Qdrant shows no collections
|
|
240
|
+
**Cause**: Docker can't access Claude projects directory
|
|
241
|
+
**Solution**:
|
|
242
|
+
```bash
|
|
243
|
+
# Run diagnostics to identify the issue
|
|
244
|
+
claude-self-reflect doctor
|
|
245
|
+
|
|
246
|
+
# Fix: Re-run setup to set correct paths
|
|
247
|
+
claude-self-reflect setup
|
|
248
|
+
|
|
249
|
+
# Verify .env has full paths (no ~):
|
|
250
|
+
cat .env | grep CLAUDE_LOGS_PATH
|
|
251
|
+
# Should show: CLAUDE_LOGS_PATH=/Users/YOUR_NAME/.claude/projects
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
#### 2. MCP server shows "ERROR" but it's actually INFO
|
|
255
|
+
**Symptom**: `[ERROR] MCP server "claude-self-reflect" Server stderr: INFO Starting MCP server`
|
|
256
|
+
**Cause**: Claude Code displays all stderr output as errors
|
|
257
|
+
**Solution**: This is not an actual error - the MCP is working correctly. The INFO message confirms successful startup.
|
|
258
|
+
|
|
259
|
+
#### 3. "No JSONL files found"
|
|
260
|
+
**Symptom**: Setup can't find any conversation files
|
|
261
|
+
**Cause**: Claude Code hasn't been used yet or stores files elsewhere
|
|
262
|
+
**Solution**:
|
|
263
|
+
```bash
|
|
264
|
+
# Check if files exist
|
|
265
|
+
ls ~/.claude/projects/
|
|
266
|
+
|
|
267
|
+
# If empty, use Claude Code to create some conversations first
|
|
268
|
+
# The watcher will import them automatically
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
#### 4. Docker volume mount issues
|
|
272
|
+
**Symptom**: Import fails with permission errors
|
|
273
|
+
**Cause**: Docker can't access home directory
|
|
274
|
+
**Solution**:
|
|
275
|
+
```bash
|
|
276
|
+
# Ensure Docker has file sharing permissions
|
|
277
|
+
# macOS: Docker Desktop → Settings → Resources → File Sharing
|
|
278
|
+
# Add: /Users/YOUR_USERNAME/.claude
|
|
279
|
+
|
|
280
|
+
# Restart Docker and re-run setup
|
|
281
|
+
docker compose down
|
|
282
|
+
claude-self-reflect setup
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
#### 5. Qdrant not accessible
|
|
286
|
+
**Symptom**: Can't connect to localhost:6333
|
|
287
|
+
**Solution**:
|
|
288
|
+
```bash
|
|
289
|
+
# Start services
|
|
290
|
+
docker compose --profile mcp up -d
|
|
291
|
+
|
|
292
|
+
# Check if running
|
|
293
|
+
docker compose ps
|
|
294
|
+
|
|
295
|
+
# View logs for errors
|
|
296
|
+
docker compose logs qdrant
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Diagnostic Tools
|
|
300
|
+
|
|
301
|
+
Run comprehensive diagnostics:
|
|
302
|
+
```bash
|
|
303
|
+
claude-self-reflect doctor
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
This checks:
|
|
307
|
+
- Docker installation and configuration
|
|
308
|
+
- Environment variables and paths
|
|
309
|
+
- Claude projects and JSONL files
|
|
310
|
+
- Import status and collections
|
|
311
|
+
- Service health
|
|
312
|
+
|
|
313
|
+
### Still Having Issues?
|
|
314
|
+
|
|
315
|
+
1. Check the logs:
|
|
316
|
+
```bash
|
|
317
|
+
docker compose logs -f
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
2. Report issues with diagnostic output:
|
|
321
|
+
```bash
|
|
322
|
+
claude-self-reflect doctor > diagnostic.txt
|
|
323
|
+
```
|
|
324
|
+
Then include diagnostic.txt when [reporting issues](https://github.com/ramakay/claude-self-reflect/issues)
|
|
325
|
+
|
|
234
326
|
## 👥 Contributors
|
|
235
327
|
|
|
236
328
|
Special thanks to our contributors:
|
package/installer/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { spawn } from 'child_process';
|
|
3
|
+
import { spawn, execSync } from 'child_process';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
import { dirname, join } from 'path';
|
|
6
6
|
import fs from 'fs/promises';
|
|
@@ -76,71 +76,76 @@ async function status() {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
async function doctor() {
|
|
79
|
-
console.log('🔍
|
|
79
|
+
console.log('🔍 Running comprehensive diagnostics...\n');
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
81
|
+
// Use the new Python doctor script for comprehensive checks
|
|
82
|
+
const doctorScript = join(__dirname, '..', 'scripts', 'doctor.py');
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
// Check if Python is available
|
|
86
|
+
try {
|
|
87
|
+
execSync('python3 --version', { stdio: 'ignore' });
|
|
88
|
+
} catch {
|
|
89
|
+
console.log('❌ Python 3 is required but not found');
|
|
90
|
+
console.log(' Please install Python 3.10 or later');
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Run the doctor script
|
|
95
|
+
const child = spawn('python3', [doctorScript], {
|
|
96
|
+
stdio: 'inherit',
|
|
97
|
+
cwd: join(__dirname, '..')
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
child.on('exit', (code) => {
|
|
101
|
+
if (code === 0) {
|
|
102
|
+
console.log('\n✅ All checks passed!');
|
|
103
|
+
} else if (code === 2) {
|
|
104
|
+
console.log('\n⚠️ Some issues found - see recommendations above');
|
|
105
|
+
} else {
|
|
106
|
+
console.log('\n❌ Critical issues found - please address them first');
|
|
92
107
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
108
|
+
process.exit(code || 0);
|
|
109
|
+
});
|
|
110
|
+
} catch (error) {
|
|
111
|
+
// Fallback to basic checks if doctor script fails
|
|
112
|
+
console.log('⚠️ Could not run comprehensive diagnostics, using basic checks...\n');
|
|
113
|
+
|
|
114
|
+
const checks = [
|
|
115
|
+
{
|
|
116
|
+
name: 'Docker',
|
|
117
|
+
check: async () => {
|
|
118
|
+
try {
|
|
119
|
+
execSync('docker info', { stdio: 'ignore' });
|
|
120
|
+
return { passed: true, message: 'Docker is running' };
|
|
121
|
+
} catch {
|
|
122
|
+
return { passed: false, message: 'Docker not running or not installed' };
|
|
102
123
|
}
|
|
103
|
-
} catch {}
|
|
104
|
-
return { passed: false, message: 'Qdrant not accessible on localhost:6333' };
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
name: 'MCP Server',
|
|
109
|
-
check: async () => {
|
|
110
|
-
const mcpPath = join(__dirname, '..', 'mcp-server', 'pyproject.toml');
|
|
111
|
-
try {
|
|
112
|
-
await fs.access(mcpPath);
|
|
113
|
-
return { passed: true, message: 'MCP server files found' };
|
|
114
|
-
} catch {
|
|
115
|
-
return { passed: false, message: 'MCP server files not found' };
|
|
116
124
|
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
return { passed: false, message: '.env file missing VOYAGE_KEY' };
|
|
130
|
-
} catch {
|
|
131
|
-
return { passed: false, message: '.env file not found' };
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: 'Qdrant',
|
|
128
|
+
check: async () => {
|
|
129
|
+
try {
|
|
130
|
+
const response = await fetch('http://localhost:6333');
|
|
131
|
+
const data = await response.json();
|
|
132
|
+
if (data.title && data.title.includes('qdrant')) {
|
|
133
|
+
return { passed: true, message: `Qdrant ${data.version} is running` };
|
|
134
|
+
}
|
|
135
|
+
} catch {}
|
|
136
|
+
return { passed: false, message: 'Qdrant not accessible on localhost:6333' };
|
|
132
137
|
}
|
|
133
138
|
}
|
|
139
|
+
];
|
|
140
|
+
|
|
141
|
+
for (const check of checks) {
|
|
142
|
+
const result = await check.check();
|
|
143
|
+
const icon = result.passed ? '✅' : '❌';
|
|
144
|
+
console.log(`${icon} ${check.name}: ${result.message}`);
|
|
134
145
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
for (const check of checks) {
|
|
138
|
-
const result = await check.check();
|
|
139
|
-
const icon = result.passed ? '✅' : '❌';
|
|
140
|
-
console.log(`${icon} ${check.name}: ${result.message}`);
|
|
146
|
+
|
|
147
|
+
console.log('\n💡 Run "claude-self-reflect setup" to fix any issues');
|
|
141
148
|
}
|
|
142
|
-
|
|
143
|
-
console.log('\n💡 Run "claude-self-reflect setup" to fix any issues');
|
|
144
149
|
}
|
|
145
150
|
|
|
146
151
|
const ASCII_ART = `
|
|
@@ -191,6 +191,12 @@ async function configureEnvironment() {
|
|
|
191
191
|
if (!envContent.includes('CONFIG_PATH=')) {
|
|
192
192
|
envContent += `CONFIG_PATH=${userConfigDir}\n`;
|
|
193
193
|
}
|
|
194
|
+
// CRITICAL: Set CLAUDE_LOGS_PATH with expanded home directory
|
|
195
|
+
// Docker doesn't expand ~ in volume mounts
|
|
196
|
+
if (!envContent.includes('CLAUDE_LOGS_PATH=')) {
|
|
197
|
+
const claudeLogsPath = path.join(os.homedir(), '.claude', 'projects');
|
|
198
|
+
envContent += `CLAUDE_LOGS_PATH=${claudeLogsPath}\n`;
|
|
199
|
+
}
|
|
194
200
|
|
|
195
201
|
await fs.writeFile(envPath, envContent.trim() + '\n');
|
|
196
202
|
console.log('✅ Environment configured');
|
|
@@ -294,30 +300,36 @@ async function startDockerServices() {
|
|
|
294
300
|
}
|
|
295
301
|
|
|
296
302
|
async function configureClaude() {
|
|
297
|
-
console.log('\n🤖 Configuring Claude
|
|
303
|
+
console.log('\n🤖 Configuring Claude Code...');
|
|
298
304
|
|
|
299
|
-
|
|
305
|
+
// Use the clean wrapper if running locally, Docker script for Docker mode
|
|
306
|
+
const isDockerMode = process.env.USE_DOCKER_MCP === 'true';
|
|
307
|
+
const mcpScript = isDockerMode
|
|
308
|
+
? join(projectRoot, 'mcp-server', 'run-mcp-docker.sh')
|
|
309
|
+
: join(projectRoot, 'mcp-server', 'run-mcp-clean.sh');
|
|
300
310
|
|
|
301
|
-
|
|
302
|
-
|
|
311
|
+
if (isDockerMode) {
|
|
312
|
+
// Create a script that runs the MCP server in Docker
|
|
313
|
+
const scriptContent = `#!/bin/bash
|
|
303
314
|
# Run the MCP server in the Docker container with stdin attached
|
|
304
315
|
# Using python -u for unbuffered output
|
|
305
316
|
# Using the main module which properly supports local embeddings
|
|
306
317
|
docker exec -i claude-reflection-mcp python -u -m src
|
|
307
318
|
`;
|
|
308
|
-
|
|
309
|
-
|
|
319
|
+
|
|
320
|
+
await fs.writeFile(mcpScript, scriptContent, { mode: 0o755 });
|
|
321
|
+
}
|
|
310
322
|
|
|
311
323
|
// Check if Claude CLI is available
|
|
312
324
|
try {
|
|
313
325
|
safeExec('which', ['claude'], { stdio: 'ignore' });
|
|
314
326
|
|
|
315
|
-
console.log('🔧 Adding MCP to Claude
|
|
327
|
+
console.log('🔧 Adding MCP to Claude Code...');
|
|
316
328
|
try {
|
|
317
329
|
const mcpArgs = ['mcp', 'add', 'claude-self-reflect', mcpScript];
|
|
318
330
|
safeExec('claude', mcpArgs, { stdio: 'inherit' });
|
|
319
331
|
console.log('✅ MCP added successfully!');
|
|
320
|
-
console.log('\n⚠️ Please restart Claude
|
|
332
|
+
console.log('\n⚠️ Please restart Claude Code for changes to take effect.');
|
|
321
333
|
} catch {
|
|
322
334
|
console.log('⚠️ Could not add MCP automatically');
|
|
323
335
|
showManualConfig(mcpScript);
|
|
@@ -329,7 +341,7 @@ docker exec -i claude-reflection-mcp python -u -m src
|
|
|
329
341
|
}
|
|
330
342
|
|
|
331
343
|
function showManualConfig(mcpScript) {
|
|
332
|
-
console.log('\nAdd this to your Claude
|
|
344
|
+
console.log('\nAdd this to your Claude Code config manually:');
|
|
333
345
|
console.log('```json');
|
|
334
346
|
console.log(JSON.stringify({
|
|
335
347
|
"claude-self-reflect": {
|
|
@@ -342,6 +354,42 @@ function showManualConfig(mcpScript) {
|
|
|
342
354
|
async function importConversations() {
|
|
343
355
|
console.log('\n📚 Checking conversation baseline...');
|
|
344
356
|
|
|
357
|
+
// First check if Claude projects directory exists and has JSONL files
|
|
358
|
+
const claudeProjectsDir = path.join(os.homedir(), '.claude', 'projects');
|
|
359
|
+
let totalJsonlFiles = 0;
|
|
360
|
+
let projectCount = 0;
|
|
361
|
+
|
|
362
|
+
try {
|
|
363
|
+
if (fsSync.existsSync(claudeProjectsDir)) {
|
|
364
|
+
const projects = fsSync.readdirSync(claudeProjectsDir);
|
|
365
|
+
for (const project of projects) {
|
|
366
|
+
const projectPath = path.join(claudeProjectsDir, project);
|
|
367
|
+
if (fsSync.statSync(projectPath).isDirectory()) {
|
|
368
|
+
const jsonlFiles = fsSync.readdirSync(projectPath).filter(f => f.endsWith('.jsonl'));
|
|
369
|
+
if (jsonlFiles.length > 0) {
|
|
370
|
+
projectCount++;
|
|
371
|
+
totalJsonlFiles += jsonlFiles.length;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (totalJsonlFiles === 0) {
|
|
378
|
+
console.log('\n⚠️ No Claude conversation files found!');
|
|
379
|
+
console.log(` Checked: ${claudeProjectsDir}`);
|
|
380
|
+
console.log('\n This could mean:');
|
|
381
|
+
console.log(' • You haven\'t used Claude Code yet');
|
|
382
|
+
console.log(' • Claude stores conversations in a different location');
|
|
383
|
+
console.log(' • Permissions issue accessing the directory');
|
|
384
|
+
console.log('\n The watcher will monitor for new conversations.');
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
console.log(`✅ Found ${totalJsonlFiles} conversation files across ${projectCount} projects`);
|
|
389
|
+
} catch (e) {
|
|
390
|
+
console.log(`⚠️ Could not access Claude projects directory: ${e.message}`);
|
|
391
|
+
}
|
|
392
|
+
|
|
345
393
|
// Check if baseline exists by looking for imported files state
|
|
346
394
|
const configDir = path.join(os.homedir(), '.claude-self-reflect', 'config');
|
|
347
395
|
const stateFile = path.join(configDir, 'imported-files.json');
|
|
@@ -492,7 +540,7 @@ async function showFinalInstructions() {
|
|
|
492
540
|
console.log(' • Stop all: docker compose down');
|
|
493
541
|
|
|
494
542
|
console.log('\n🎯 Next Steps:');
|
|
495
|
-
console.log('1. Restart Claude
|
|
543
|
+
console.log('1. Restart Claude Code');
|
|
496
544
|
console.log('2. Look for "claude-self-reflect" in the MCP tools');
|
|
497
545
|
console.log('3. Try: "Search my past conversations about Python"');
|
|
498
546
|
|
package/mcp-server/src/status.py
CHANGED
|
@@ -6,57 +6,66 @@ Designed for <20ms execution time to support status bars and shell scripts.
|
|
|
6
6
|
|
|
7
7
|
import json
|
|
8
8
|
import time
|
|
9
|
+
import sys
|
|
9
10
|
from pathlib import Path
|
|
10
11
|
from collections import defaultdict
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
#
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def normalize_file_path(file_path: str) -> str:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
13
|
+
# Try to import shared utilities
|
|
14
|
+
try:
|
|
15
|
+
# Add scripts directory to path
|
|
16
|
+
scripts_dir = Path(__file__).parent.parent.parent / "scripts"
|
|
17
|
+
if scripts_dir.exists():
|
|
18
|
+
sys.path.insert(0, str(scripts_dir))
|
|
19
|
+
from shared_utils import (
|
|
20
|
+
normalize_file_path,
|
|
21
|
+
extract_project_name_from_path,
|
|
22
|
+
get_claude_projects_dir,
|
|
23
|
+
get_csr_config_dir
|
|
24
|
+
)
|
|
25
|
+
except ImportError:
|
|
26
|
+
# Fallback implementations if shared_utils is not available
|
|
27
|
+
def extract_project_name_from_path(file_path: str) -> str:
|
|
28
|
+
"""Extract project name from JSONL file path."""
|
|
29
|
+
path_obj = Path(file_path)
|
|
30
|
+
dir_name = path_obj.parent.name
|
|
31
|
+
|
|
32
|
+
if dir_name.startswith('-') and 'projects' in dir_name:
|
|
33
|
+
parts = dir_name.split('-')
|
|
34
|
+
try:
|
|
35
|
+
projects_idx = parts.index('projects')
|
|
36
|
+
if projects_idx + 1 < len(parts):
|
|
37
|
+
project_parts = parts[projects_idx + 1:]
|
|
38
|
+
return '-'.join(project_parts)
|
|
39
|
+
except ValueError:
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
return dir_name.lstrip('-')
|
|
43
|
+
|
|
44
|
+
def normalize_file_path(file_path: str) -> str:
|
|
45
|
+
"""Normalize file paths to handle Docker vs local path differences."""
|
|
46
|
+
if file_path.startswith("/logs/"):
|
|
47
|
+
projects_dir = str(Path.home() / ".claude" / "projects")
|
|
48
|
+
return file_path.replace("/logs/", projects_dir + "/", 1)
|
|
49
|
+
return file_path
|
|
50
|
+
|
|
51
|
+
def get_claude_projects_dir() -> Path:
|
|
52
|
+
"""Get Claude projects directory."""
|
|
53
|
+
import os
|
|
54
|
+
if 'CLAUDE_PROJECTS_DIR' in os.environ:
|
|
55
|
+
return Path(os.environ['CLAUDE_PROJECTS_DIR'])
|
|
56
|
+
return Path.home() / ".claude" / "projects"
|
|
57
|
+
|
|
58
|
+
def get_csr_config_dir() -> Path:
|
|
59
|
+
"""Get CSR config directory."""
|
|
60
|
+
import os
|
|
61
|
+
if 'CSR_CONFIG_DIR' in os.environ:
|
|
62
|
+
return Path(os.environ['CSR_CONFIG_DIR'])
|
|
63
|
+
return Path.home() / '.claude-self-reflect' / 'config'
|
|
55
64
|
|
|
56
65
|
|
|
57
66
|
def get_watcher_status() -> dict:
|
|
58
67
|
"""Get streaming watcher status if available."""
|
|
59
|
-
watcher_state_file =
|
|
68
|
+
watcher_state_file = get_csr_config_dir() / "csr-watcher.json"
|
|
60
69
|
|
|
61
70
|
if not watcher_state_file.exists():
|
|
62
71
|
return {"running": False, "status": "not configured"}
|
|
@@ -85,7 +94,7 @@ def get_status() -> dict:
|
|
|
85
94
|
Returns:
|
|
86
95
|
dict: JSON structure with overall and per-project indexing status, plus watcher status
|
|
87
96
|
"""
|
|
88
|
-
projects_dir =
|
|
97
|
+
projects_dir = get_claude_projects_dir()
|
|
89
98
|
project_stats = defaultdict(lambda: {"indexed": 0, "total": 0})
|
|
90
99
|
|
|
91
100
|
# Build a mapping of normalized file paths to project names
|
|
@@ -99,18 +108,24 @@ def get_status() -> dict:
|
|
|
99
108
|
project_stats[project_name]["total"] += 1
|
|
100
109
|
file_to_project[file_str] = project_name
|
|
101
110
|
|
|
111
|
+
# Track which files have been counted to avoid duplicates
|
|
112
|
+
counted_files = set()
|
|
113
|
+
|
|
102
114
|
# Read imported-files.json to count indexed files per project
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
115
|
+
config_dir = get_csr_config_dir()
|
|
116
|
+
imported_files_path = config_dir / "imported-files.json"
|
|
117
|
+
|
|
118
|
+
# Fallback to other locations if not found
|
|
119
|
+
if not imported_files_path.exists():
|
|
120
|
+
config_paths = [
|
|
121
|
+
Path(__file__).parent.parent.parent / "config" / "imported-files.json",
|
|
122
|
+
Path("/config/imported-files.json") # Docker path
|
|
123
|
+
]
|
|
124
|
+
|
|
125
|
+
for path in config_paths:
|
|
126
|
+
if path.exists():
|
|
127
|
+
imported_files_path = path
|
|
128
|
+
break
|
|
114
129
|
|
|
115
130
|
if imported_files_path:
|
|
116
131
|
try:
|
|
@@ -123,9 +138,10 @@ def get_status() -> dict:
|
|
|
123
138
|
# Count all files in imported_files object (they are all fully imported)
|
|
124
139
|
for file_path in imported_files.keys():
|
|
125
140
|
normalized_path = normalize_file_path(file_path)
|
|
126
|
-
if normalized_path in file_to_project:
|
|
141
|
+
if normalized_path in file_to_project and normalized_path not in counted_files:
|
|
127
142
|
project_name = file_to_project[normalized_path]
|
|
128
143
|
project_stats[project_name]["indexed"] += 1
|
|
144
|
+
counted_files.add(normalized_path)
|
|
129
145
|
|
|
130
146
|
# Also check file_metadata for partially imported files
|
|
131
147
|
file_metadata = data.get("file_metadata", {})
|
|
@@ -134,9 +150,10 @@ def get_status() -> dict:
|
|
|
134
150
|
# Only count if not already in imported_files
|
|
135
151
|
if file_path not in imported_files:
|
|
136
152
|
normalized_path = normalize_file_path(file_path)
|
|
137
|
-
if normalized_path in file_to_project:
|
|
153
|
+
if normalized_path in file_to_project and normalized_path not in counted_files:
|
|
138
154
|
project_name = file_to_project[normalized_path]
|
|
139
155
|
project_stats[project_name]["indexed"] += 1
|
|
156
|
+
counted_files.add(normalized_path)
|
|
140
157
|
|
|
141
158
|
# Also check stream_position if it contains file paths
|
|
142
159
|
stream_position = data.get("stream_position", {})
|
|
@@ -148,15 +165,33 @@ def get_status() -> dict:
|
|
|
148
165
|
# Only count if not already counted
|
|
149
166
|
if file_path not in imported_files:
|
|
150
167
|
normalized_path = normalize_file_path(file_path)
|
|
151
|
-
if normalized_path in file_to_project:
|
|
168
|
+
if normalized_path in file_to_project and normalized_path not in counted_files:
|
|
152
169
|
project_name = file_to_project[normalized_path]
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
project_stats[project_name]["indexed"] += 1
|
|
170
|
+
project_stats[project_name]["indexed"] += 1
|
|
171
|
+
counted_files.add(normalized_path)
|
|
156
172
|
except (json.JSONDecodeError, KeyError, OSError):
|
|
157
173
|
# If config file is corrupted or unreadable, continue with zero indexed counts
|
|
158
174
|
pass
|
|
159
175
|
|
|
176
|
+
# Also read csr-watcher.json to count files imported by the watcher
|
|
177
|
+
watcher_state_file = config_dir / "csr-watcher.json"
|
|
178
|
+
if watcher_state_file.exists():
|
|
179
|
+
try:
|
|
180
|
+
with open(watcher_state_file, 'r') as f:
|
|
181
|
+
watcher_data = json.load(f)
|
|
182
|
+
|
|
183
|
+
# Count files imported by the watcher
|
|
184
|
+
watcher_imports = watcher_data.get("imported_files", {})
|
|
185
|
+
for file_path in watcher_imports.keys():
|
|
186
|
+
normalized_path = normalize_file_path(file_path)
|
|
187
|
+
if normalized_path in file_to_project and normalized_path not in counted_files:
|
|
188
|
+
project_name = file_to_project[normalized_path]
|
|
189
|
+
project_stats[project_name]["indexed"] += 1
|
|
190
|
+
counted_files.add(normalized_path)
|
|
191
|
+
except (json.JSONDecodeError, KeyError, OSError):
|
|
192
|
+
# If watcher file is corrupted or unreadable, continue
|
|
193
|
+
pass
|
|
194
|
+
|
|
160
195
|
# Calculate overall stats
|
|
161
196
|
total_all = sum(p["total"] for p in project_stats.values())
|
|
162
197
|
indexed_all = sum(p["indexed"] for p in project_stats.values())
|
package/package.json
CHANGED
|
@@ -338,7 +338,19 @@ def main():
|
|
|
338
338
|
logger.info(f"Loaded state with {len(state.get('imported_files', {}))} previously imported files")
|
|
339
339
|
|
|
340
340
|
# Find all projects
|
|
341
|
-
|
|
341
|
+
# Use LOGS_DIR env var, or fall back to Claude projects directory, then /logs for Docker
|
|
342
|
+
logs_dir_env = os.getenv("LOGS_DIR")
|
|
343
|
+
if logs_dir_env:
|
|
344
|
+
logs_dir = Path(logs_dir_env)
|
|
345
|
+
elif (Path.home() / ".claude" / "projects").exists():
|
|
346
|
+
logs_dir = Path.home() / ".claude" / "projects"
|
|
347
|
+
else:
|
|
348
|
+
logs_dir = Path("/logs") # Docker fallback
|
|
349
|
+
|
|
350
|
+
if not logs_dir.exists():
|
|
351
|
+
logger.error(f"Projects directory not found: {logs_dir}")
|
|
352
|
+
sys.exit(1)
|
|
353
|
+
|
|
342
354
|
project_dirs = [d for d in logs_dir.iterdir() if d.is_dir()]
|
|
343
355
|
logger.info(f"Found {len(project_dirs)} projects to import")
|
|
344
356
|
|