Refactor settings management to use SettingsDefaultsManager

- Introduced SettingsDefaultsManager to centralize default settings and loading logic.
- Updated context-generator, SDKAgent, SettingsRoutes, and worker-utils to utilize the new manager for loading settings.
- Removed redundant code for reading settings from files and environment variables.
- Ensured fallback to default values when settings file is missing or invalid.
This commit is contained in:
Alex Newman
2025-12-07 22:15:26 -05:00
parent 9cb4b9d02a
commit f494d3b168
13 changed files with 312 additions and 275 deletions
+4 -13
View File
@@ -11,13 +11,13 @@
import { execSync } from 'child_process';
import { homedir } from 'os';
import path from 'path';
import { existsSync, readFileSync } from 'fs';
import { DatabaseManager } from './DatabaseManager.js';
import { SessionManager } from './SessionManager.js';
import { logger } from '../../utils/logger.js';
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 type { ActiveSession, SDKUserMessage, PendingMessage } from '../worker-types.js';
// Import Agent SDK (assumes it's installed)
@@ -425,17 +425,8 @@ export class SDKAgent {
* Get model ID from settings or environment
*/
private getModelId(): string {
try {
const settingsPath = path.join(homedir(), '.claude-mem', 'settings.json');
if (existsSync(settingsPath)) {
const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
const modelId = settings.env?.CLAUDE_MEM_MODEL;
if (modelId) return modelId;
}
} catch {
// Fall through to env var or default
}
return process.env.CLAUDE_MEM_MODEL || 'claude-haiku-4-5';
const settingsPath = path.join(homedir(), '.claude-mem', 'settings.json');
const settings = SettingsDefaultsManager.loadFromFile(settingsPath);
return settings.CLAUDE_MEM_MODEL;
}
}
@@ -16,12 +16,11 @@ import { getBranchInfo, switchBranch, pullUpdates } from '../../BranchManager.js
import {
OBSERVATION_TYPES,
OBSERVATION_CONCEPTS,
DEFAULT_OBSERVATION_TYPES_STRING,
DEFAULT_OBSERVATION_CONCEPTS_STRING,
ObservationType,
ObservationConcept
} from '../../../../constants/observation-metadata.js';
import { BaseRouteHandler } from '../BaseRouteHandler.js';
import { SettingsDefaultsManager } from '../../settings/SettingsDefaultsManager.js';
export class SettingsRoutes extends BaseRouteHandler {
constructor(
@@ -50,56 +49,8 @@ export class SettingsRoutes extends BaseRouteHandler {
*/
private handleGetSettings = this.wrapHandler((req: Request, res: Response): void => {
const settingsPath = path.join(homedir(), '.claude-mem', 'settings.json');
if (!existsSync(settingsPath)) {
// Return defaults if file doesn't exist
res.json({
CLAUDE_MEM_MODEL: 'claude-haiku-4-5',
CLAUDE_MEM_CONTEXT_OBSERVATIONS: '50',
CLAUDE_MEM_WORKER_PORT: '37777',
// Token Economics
CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS: 'true',
CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS: 'true',
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT: 'true',
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT: 'true',
// Observation Filtering
CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES: DEFAULT_OBSERVATION_TYPES_STRING,
CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS: DEFAULT_OBSERVATION_CONCEPTS_STRING,
// Display Configuration
CLAUDE_MEM_CONTEXT_FULL_COUNT: '5',
CLAUDE_MEM_CONTEXT_FULL_FIELD: 'narrative',
CLAUDE_MEM_CONTEXT_SESSION_COUNT: '10',
// Feature Toggles
CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY: 'true',
CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE: 'false',
});
return;
}
const settingsData = readFileSync(settingsPath, 'utf-8');
const settings = JSON.parse(settingsData);
const env = settings.env || {};
res.json({
CLAUDE_MEM_MODEL: env.CLAUDE_MEM_MODEL || 'claude-haiku-4-5',
CLAUDE_MEM_CONTEXT_OBSERVATIONS: env.CLAUDE_MEM_CONTEXT_OBSERVATIONS || '50',
CLAUDE_MEM_WORKER_PORT: env.CLAUDE_MEM_WORKER_PORT || '37777',
// Token Economics
CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS: env.CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS || 'true',
CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS: env.CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS || 'true',
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT: env.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT || 'true',
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT: env.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT || 'true',
// Observation Filtering
CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES: env.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES || DEFAULT_OBSERVATION_TYPES_STRING,
CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS: env.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS || DEFAULT_OBSERVATION_CONCEPTS_STRING,
// Display Configuration
CLAUDE_MEM_CONTEXT_FULL_COUNT: env.CLAUDE_MEM_CONTEXT_FULL_COUNT || '5',
CLAUDE_MEM_CONTEXT_FULL_FIELD: env.CLAUDE_MEM_CONTEXT_FULL_FIELD || 'narrative',
CLAUDE_MEM_CONTEXT_SESSION_COUNT: env.CLAUDE_MEM_CONTEXT_SESSION_COUNT || '10',
// Feature Toggles
CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY: env.CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY || 'true',
CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE: env.CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE || 'false',
});
const settings = SettingsDefaultsManager.loadFromFile(settingsPath);
res.json(settings);
});
/**
@@ -0,0 +1,110 @@
/**
* SettingsDefaultsManager
*
* Single source of truth for all default configuration values.
* Provides methods to get defaults with optional environment variable overrides.
*/
import { readFileSync, existsSync } from 'fs';
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;
// Token Economics
CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS: string;
CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS: string;
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT: string;
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT: string;
// Observation Filtering
CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES: string;
CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS: string;
// Display Configuration
CLAUDE_MEM_CONTEXT_FULL_COUNT: string;
CLAUDE_MEM_CONTEXT_FULL_FIELD: string;
CLAUDE_MEM_CONTEXT_SESSION_COUNT: string;
// Feature Toggles
CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY: string;
CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE: string;
}
export class SettingsDefaultsManager {
/**
* Default values for all settings
*/
private static readonly DEFAULTS: SettingsDefaults = {
CLAUDE_MEM_MODEL: 'claude-haiku-4-5',
CLAUDE_MEM_CONTEXT_OBSERVATIONS: '50',
CLAUDE_MEM_WORKER_PORT: '37777',
// Token Economics
CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS: 'true',
CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS: 'true',
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT: 'true',
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT: 'true',
// Observation Filtering
CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES: DEFAULT_OBSERVATION_TYPES_STRING,
CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS: DEFAULT_OBSERVATION_CONCEPTS_STRING,
// Display Configuration
CLAUDE_MEM_CONTEXT_FULL_COUNT: '5',
CLAUDE_MEM_CONTEXT_FULL_FIELD: 'narrative',
CLAUDE_MEM_CONTEXT_SESSION_COUNT: '10',
// Feature Toggles
CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY: 'true',
CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE: 'false',
};
/**
* Get all defaults as an object
*/
static getAllDefaults(): SettingsDefaults {
return { ...this.DEFAULTS };
}
/**
* Get a default value with optional environment variable override
*/
static get(key: keyof SettingsDefaults): string {
return process.env[key] || this.DEFAULTS[key];
}
/**
* Get an integer default value with optional environment variable override
*/
static getInt(key: keyof SettingsDefaults): number {
const value = this.get(key);
return parseInt(value, 10);
}
/**
* Get a boolean default value with optional environment variable override
*/
static getBool(key: keyof SettingsDefaults): boolean {
const value = this.get(key);
return value === 'true';
}
/**
* Load settings from file with fallback to defaults
* Returns merged settings with defaults as fallback
*/
static loadFromFile(settingsPath: string): SettingsDefaults {
if (!existsSync(settingsPath)) {
return this.getAllDefaults();
}
const settingsData = readFileSync(settingsPath, 'utf-8');
const settings = JSON.parse(settingsData);
const env = settings.env || {};
// Merge file settings with defaults
const result: SettingsDefaults = { ...this.DEFAULTS };
for (const key of Object.keys(this.DEFAULTS) as Array<keyof SettingsDefaults>) {
if (env[key] !== undefined) {
result[key] = env[key];
}
}
return result;
}
}