Refactor settings management to use ~/.claude-mem/settings.json
- Updated paths in troubleshooting documentation to reflect new settings file location. - Modified diagnostics and reference files to read from ~/.claude-mem/settings.json. - Introduced getWorkerPort utility for cleaner worker port retrieval. - Enhanced ChromaSync and SDKAgent to load Python version and Claude path from settings. - Updated SettingsRoutes to validate new settings: CLAUDE_MEM_LOG_LEVEL and CLAUDE_MEM_PYTHON_VERSION. - Added early-settings module to load settings for logger and other early-stage modules. - Adjusted logger to use early-loaded log level setting. - Refactored paths to utilize early-loaded data directory setting.
This commit is contained in:
@@ -15,11 +15,12 @@ import {
|
||||
import { z } from 'zod';
|
||||
import { zodToJsonSchema } from 'zod-to-json-schema';
|
||||
import { silentDebug } from '../utils/silent-debug.js';
|
||||
import { getWorkerPort } from '../shared/worker-utils.js';
|
||||
|
||||
/**
|
||||
* Worker HTTP API configuration
|
||||
*/
|
||||
const WORKER_PORT = parseInt(process.env.CLAUDE_MEM_WORKER_PORT || '37777', 10);
|
||||
const WORKER_PORT = getWorkerPort();
|
||||
const WORKER_BASE_URL = `http://localhost:${WORKER_PORT}`;
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,6 +13,8 @@ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'
|
||||
import { ParsedObservation, ParsedSummary } from '../../sdk/parser.js';
|
||||
import { SessionStore } from '../sqlite/SessionStore.js';
|
||||
import { logger } from '../../utils/logger.js';
|
||||
import { SettingsDefaultsManager } from '../worker/settings/SettingsDefaultsManager.js';
|
||||
import { USER_SETTINGS_PATH } from '../../shared/paths.js';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
|
||||
@@ -96,7 +98,8 @@ export class ChromaSync {
|
||||
try {
|
||||
// Use Python 3.13 by default to avoid onnxruntime compatibility issues with Python 3.14+
|
||||
// See: https://github.com/thedotmack/claude-mem/issues/170 (Python 3.14 incompatibility)
|
||||
const pythonVersion = process.env.CLAUDE_MEM_PYTHON_VERSION || '3.13';
|
||||
const settings = SettingsDefaultsManager.loadFromFile(USER_SETTINGS_PATH);
|
||||
const pythonVersion = settings.CLAUDE_MEM_PYTHON_VERSION;
|
||||
const transport = new StdioClientTransport({
|
||||
command: 'uvx',
|
||||
args: [
|
||||
|
||||
@@ -18,6 +18,7 @@ import { silentDebug } from '../../utils/silent-debug.js';
|
||||
import { parseObservations, parseSummary } from '../../sdk/parser.js';
|
||||
import { buildInitPrompt, buildObservationPrompt, buildSummaryPrompt, buildContinuationPrompt } from '../../sdk/prompts.js';
|
||||
import { SettingsDefaultsManager } from './settings/SettingsDefaultsManager.js';
|
||||
import { USER_SETTINGS_PATH } from '../../shared/paths.js';
|
||||
import type { ActiveSession, SDKUserMessage, PendingMessage } from '../worker-types.js';
|
||||
|
||||
// Import Agent SDK (assumes it's installed)
|
||||
@@ -410,7 +411,8 @@ export class SDKAgent {
|
||||
* Find Claude executable (inline, called once per session)
|
||||
*/
|
||||
private findClaudeExecutable(): string {
|
||||
const claudePath = process.env.CLAUDE_CODE_PATH ||
|
||||
const settings = SettingsDefaultsManager.loadFromFile(USER_SETTINGS_PATH);
|
||||
const claudePath = settings.CLAUDE_CODE_PATH ||
|
||||
execSync(process.platform === 'win32' ? 'where claude' : 'which claude', { encoding: 'utf8', windowsHide: true })
|
||||
.trim().split('\n')[0].trim();
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ export class SettingsRoutes extends BaseRouteHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get environment settings (from ~/.claude/settings.json)
|
||||
* Get environment settings (from ~/.claude-mem/settings.json)
|
||||
*/
|
||||
private handleGetSettings = this.wrapHandler((req: Request, res: Response): void => {
|
||||
const settingsPath = path.join(homedir(), '.claude-mem', 'settings.json');
|
||||
@@ -54,7 +54,7 @@ export class SettingsRoutes extends BaseRouteHandler {
|
||||
});
|
||||
|
||||
/**
|
||||
* Update environment settings (in ~/.claude/settings.json) with validation
|
||||
* Update environment settings (in ~/.claude-mem/settings.json) with validation
|
||||
*/
|
||||
private handleUpdateSettings = this.wrapHandler((req: Request, res: Response): void => {
|
||||
// Validate CLAUDE_MEM_CONTEXT_OBSERVATIONS
|
||||
@@ -81,6 +81,30 @@ export class SettingsRoutes extends BaseRouteHandler {
|
||||
}
|
||||
}
|
||||
|
||||
// Validate CLAUDE_MEM_LOG_LEVEL
|
||||
if (req.body.CLAUDE_MEM_LOG_LEVEL) {
|
||||
const validLevels = ['DEBUG', 'INFO', 'WARN', 'ERROR', 'SILENT'];
|
||||
if (!validLevels.includes(req.body.CLAUDE_MEM_LOG_LEVEL.toUpperCase())) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: 'CLAUDE_MEM_LOG_LEVEL must be one of: DEBUG, INFO, WARN, ERROR, SILENT'
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate CLAUDE_MEM_PYTHON_VERSION (must be valid Python version format)
|
||||
if (req.body.CLAUDE_MEM_PYTHON_VERSION) {
|
||||
const pythonVersionRegex = /^3\.\d+$/;
|
||||
if (!pythonVersionRegex.test(req.body.CLAUDE_MEM_PYTHON_VERSION)) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: 'CLAUDE_MEM_PYTHON_VERSION must be in format "3.X" (e.g., "3.13")'
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate context settings
|
||||
const validation = this.validateContextSettings(req.body);
|
||||
if (!validation.valid) {
|
||||
@@ -108,15 +132,24 @@ export class SettingsRoutes extends BaseRouteHandler {
|
||||
'CLAUDE_MEM_MODEL',
|
||||
'CLAUDE_MEM_CONTEXT_OBSERVATIONS',
|
||||
'CLAUDE_MEM_WORKER_PORT',
|
||||
// System Configuration
|
||||
'CLAUDE_MEM_DATA_DIR',
|
||||
'CLAUDE_MEM_LOG_LEVEL',
|
||||
'CLAUDE_MEM_PYTHON_VERSION',
|
||||
'CLAUDE_CODE_PATH',
|
||||
// Token Economics
|
||||
'CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS',
|
||||
'CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS',
|
||||
'CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT',
|
||||
'CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT',
|
||||
// Observation Filtering
|
||||
'CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES',
|
||||
'CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS',
|
||||
// Display Configuration
|
||||
'CLAUDE_MEM_CONTEXT_FULL_COUNT',
|
||||
'CLAUDE_MEM_CONTEXT_FULL_FIELD',
|
||||
'CLAUDE_MEM_CONTEXT_SESSION_COUNT',
|
||||
// Feature Toggles
|
||||
'CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY',
|
||||
'CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE',
|
||||
];
|
||||
|
||||
@@ -6,12 +6,19 @@
|
||||
*/
|
||||
|
||||
import { readFileSync, existsSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { homedir } from 'os';
|
||||
import { DEFAULT_OBSERVATION_TYPES_STRING, DEFAULT_OBSERVATION_CONCEPTS_STRING } from '../../../constants/observation-metadata.js';
|
||||
|
||||
export interface SettingsDefaults {
|
||||
CLAUDE_MEM_MODEL: string;
|
||||
CLAUDE_MEM_CONTEXT_OBSERVATIONS: string;
|
||||
CLAUDE_MEM_WORKER_PORT: string;
|
||||
// System Configuration
|
||||
CLAUDE_MEM_DATA_DIR: string;
|
||||
CLAUDE_MEM_LOG_LEVEL: string;
|
||||
CLAUDE_MEM_PYTHON_VERSION: string;
|
||||
CLAUDE_CODE_PATH: string;
|
||||
// Token Economics
|
||||
CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS: string;
|
||||
CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS: string;
|
||||
@@ -37,6 +44,11 @@ export class SettingsDefaultsManager {
|
||||
CLAUDE_MEM_MODEL: 'claude-haiku-4-5',
|
||||
CLAUDE_MEM_CONTEXT_OBSERVATIONS: '50',
|
||||
CLAUDE_MEM_WORKER_PORT: '37777',
|
||||
// System Configuration
|
||||
CLAUDE_MEM_DATA_DIR: join(homedir(), '.claude-mem'),
|
||||
CLAUDE_MEM_LOG_LEVEL: 'INFO',
|
||||
CLAUDE_MEM_PYTHON_VERSION: '3.13',
|
||||
CLAUDE_CODE_PATH: '', // Empty means auto-detect via 'which claude'
|
||||
// Token Economics
|
||||
CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS: 'true',
|
||||
CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS: 'true',
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { join } from 'path';
|
||||
import { homedir } from 'os';
|
||||
import { existsSync, readFileSync } from 'fs';
|
||||
|
||||
const SETTINGS_PATH = join(homedir(), '.claude-mem', 'settings.json');
|
||||
|
||||
interface EarlySettings {
|
||||
CLAUDE_MEM_DATA_DIR?: string;
|
||||
CLAUDE_MEM_LOG_LEVEL?: string;
|
||||
CLAUDE_MEM_PYTHON_VERSION?: string;
|
||||
CLAUDE_CODE_PATH?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load settings for early-stage modules (paths, logger)
|
||||
* Falls back to env vars, then defaults
|
||||
*/
|
||||
export function loadEarlySetting(key: keyof EarlySettings, defaultValue: string): string {
|
||||
// Priority: settings.json > env var > default
|
||||
try {
|
||||
if (existsSync(SETTINGS_PATH)) {
|
||||
const data = JSON.parse(readFileSync(SETTINGS_PATH, 'utf-8'));
|
||||
const fileValue = data.env?.[key];
|
||||
if (fileValue !== undefined) return fileValue;
|
||||
}
|
||||
} catch {
|
||||
// Fail silently - fall through to env var
|
||||
}
|
||||
|
||||
return process.env[key] || defaultValue;
|
||||
}
|
||||
+3
-1
@@ -3,6 +3,7 @@ import { homedir } from 'os';
|
||||
import { existsSync, mkdirSync } from 'fs';
|
||||
import { execSync } from 'child_process';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { loadEarlySetting } from './early-settings.js';
|
||||
|
||||
// Get __dirname that works in both ESM (hooks) and CJS (worker) contexts
|
||||
function getDirname(): string {
|
||||
@@ -22,7 +23,8 @@ const _dirname = getDirname();
|
||||
*/
|
||||
|
||||
// Base directories
|
||||
export const DATA_DIR = process.env.CLAUDE_MEM_DATA_DIR || join(homedir(), '.claude-mem');
|
||||
export const DATA_DIR = loadEarlySetting('CLAUDE_MEM_DATA_DIR', join(homedir(), '.claude-mem'));
|
||||
// Note: CLAUDE_CONFIG_DIR is a Claude Code setting, not claude-mem, so leave as env var
|
||||
export const CLAUDE_CONFIG_DIR = process.env.CLAUDE_CONFIG_DIR || join(homedir(), '.claude');
|
||||
|
||||
// Data subdirectories
|
||||
|
||||
+3
-1
@@ -3,6 +3,8 @@
|
||||
* Provides readable, traceable logging with correlation IDs and data flow tracking
|
||||
*/
|
||||
|
||||
import { loadEarlySetting } from '../shared/early-settings.js';
|
||||
|
||||
export enum LogLevel {
|
||||
DEBUG = 0,
|
||||
INFO = 1,
|
||||
@@ -26,7 +28,7 @@ class Logger {
|
||||
|
||||
constructor() {
|
||||
// Parse log level from environment
|
||||
const envLevel = process.env.CLAUDE_MEM_LOG_LEVEL?.toUpperCase() || 'INFO';
|
||||
const envLevel = loadEarlySetting('CLAUDE_MEM_LOG_LEVEL', 'INFO').toUpperCase();
|
||||
this.level = LogLevel[envLevel as keyof typeof LogLevel] ?? LogLevel.INFO;
|
||||
|
||||
// Disable colors when output is not a TTY (e.g., PM2 logs)
|
||||
|
||||
Reference in New Issue
Block a user