Refactor: Remove unused logging and path management utilities

- Removed the rollingLog import and its usage in doctor.ts.
- Deleted the animatedRainbow function from install.ts.
- Removed the log following implementation in logs.ts.
- Deleted the PathResolver class and its related methods in paths.ts.
- Removed the SettingsManager class and its associated methods in settings.ts.
- Deleted the JSONLStorageProvider class and its methods in storage.ts.
- Removed the CompressionError class from types.ts.
- Cleaned up the Platform utility by removing unnecessary methods related to shell and file permissions.
This commit is contained in:
Alex Newman
2025-10-15 20:53:22 -04:00
parent 2608fb180e
commit 4489249ecc
9 changed files with 56 additions and 550 deletions
-91
View File
@@ -1,91 +0,0 @@
import { sep, basename } from 'path';
import { PathDiscovery } from '../services/path-discovery.js';
/**
* PathResolver utility for managing claude-mem file system paths
* Now delegates to PathDiscovery service for centralized path management
*/
export class PathResolver {
private pathDiscovery: PathDiscovery;
// <Block> 1.1 ====================================
constructor() {
this.pathDiscovery = PathDiscovery.getInstance();
}
// </Block> =======================================
// <Block> 1.2 ====================================
getConfigDir(): string {
return this.pathDiscovery.getDataDirectory();
}
// </Block> =======================================
// <Block> 1.3 ====================================
getIndexDir(): string {
return this.pathDiscovery.getIndexDirectory();
}
// </Block> =======================================
// <Block> 1.4 ====================================
getIndexPath(): string {
return this.pathDiscovery.getIndexPath();
}
// </Block> =======================================
// <Block> 1.5 ====================================
getArchiveDir(): string {
return this.pathDiscovery.getArchivesDirectory();
}
// </Block> =======================================
// <Block> 1.6 ====================================
getProjectArchiveDir(projectName: string): string {
return this.pathDiscovery.getProjectArchiveDirectory(projectName);
}
// </Block> =======================================
// <Block> 1.7 ====================================
getLogsDir(): string {
return this.pathDiscovery.getLogsDirectory();
}
// </Block> =======================================
// <Block> 1.8 ====================================
static ensureDirectory(dirPath: string): void {
PathDiscovery.getInstance().ensureDirectory(dirPath);
}
// </Block> =======================================
// <Block> 1.9 ====================================
static ensureDirectories(dirPaths: string[]): void {
PathDiscovery.getInstance().ensureDirectories(dirPaths);
}
// </Block> =======================================
// <Block> 1.10 ===================================
static extractProjectName(transcriptPath: string): string {
return PathDiscovery.extractProjectName(transcriptPath);
}
// <Block> 1.11 ===================================
/**
* DRY utility function: Canonical source for getting the current project prefix
* Replaces all instances of path.basename(process.cwd()) across the codebase
* @returns The current project directory name, sanitized for use as a prefix
*/
static getCurrentProjectPrefix(): string {
return PathDiscovery.getCurrentProjectName();
}
// </Block> =======================================
// <Block> 1.12 ===================================
/**
* DRY utility function: Gets raw project name without sanitization
* For use in contexts where original directory name is needed (e.g., display)
* @returns The current project directory name as-is
*/
static getCurrentProjectName(): string {
return PathDiscovery.getCurrentProjectName();
}
// </Block> =======================================
}
-98
View File
@@ -1,98 +0,0 @@
import { readFileSync, existsSync } from 'fs';
import { join } from 'path';
import { PathResolver } from './paths.js';
import type { Settings } from './types.js';
/**
* Settings utilities for managing ~/.claude-mem/settings.json
*/
export class SettingsManager {
private static settingsPath: string;
private static cachedSettings: Settings | null = null;
static {
const pathResolver = new PathResolver();
this.settingsPath = join(pathResolver.getConfigDir(), 'settings.json');
}
/**
* Safely read settings.json with error handling
* Returns empty object if file doesn't exist or is malformed
*/
static readSettings(): Settings {
// Return cached settings if available
if (this.cachedSettings !== null) {
return this.cachedSettings;
}
try {
if (existsSync(this.settingsPath)) {
const content = readFileSync(this.settingsPath, 'utf-8');
const settings = JSON.parse(content) as Settings;
this.cachedSettings = settings;
return settings;
}
} catch {
// File is malformed or unreadable - return empty settings
}
// File doesn't exist or failed to read
const emptySettings: Settings = {};
this.cachedSettings = emptySettings;
return emptySettings;
}
/**
* Get a specific setting value with optional fallback
*/
static getSetting<K extends keyof Settings>(
key: K,
fallback?: Settings[K]
): Settings[K] | undefined {
const settings = this.readSettings();
return settings[key] ?? fallback;
}
/**
* Get the Claude binary path from settings
* Falls back to 'claude' if not found or settings don't exist
*/
static getClaudePath(): string {
const claudePath = this.getSetting('claudePath', 'claude');
return claudePath as string;
}
/**
* Clear cached settings (useful for testing or after settings changes)
*/
static clearCache(): void {
this.cachedSettings = null;
}
}
/**
* Convenience function to get Claude binary path
* Can be imported directly for simple use cases
*/
export function getClaudePath(): string {
return SettingsManager.getClaudePath();
}
/**
* Convenience function to read all settings
* Can be imported directly for simple use cases
*/
export function readSettings(): Settings {
return SettingsManager.readSettings();
}
/**
* Convenience function to get a specific setting
* Can be imported directly for simple use cases
*/
export function getSetting<K extends keyof Settings>(
key: K,
fallback?: Settings[K]
): Settings[K] | undefined {
return SettingsManager.getSetting(key, fallback);
}
+3 -217
View File
@@ -1,5 +1,3 @@
import fs from 'fs';
import { PathDiscovery } from '../services/path-discovery.js';
import {
createStores,
SessionStore,
@@ -166,237 +164,25 @@ export class SQLiteStorageProvider implements IStorageProvider {
}
}
/**
* JSONL-based storage provider (legacy fallback)
*/
export class JSONLStorageProvider implements IStorageProvider {
public readonly backend = 'jsonl';
private pathDiscovery = PathDiscovery.getInstance();
async isAvailable(): Promise<boolean> {
try {
// Ensure data directory exists
const dataDir = this.pathDiscovery.getDataDirectory();
fs.mkdirSync(dataDir, { recursive: true });
return true;
} catch {
return false;
}
}
private appendToIndex(obj: any): void {
const indexPath = this.pathDiscovery.getIndexPath();
fs.appendFileSync(indexPath, JSON.stringify(obj) + '\\n', 'utf8');
}
async createSession(session: SessionInput): Promise<void> {
const sessionRecord = {
type: 'session',
session_id: session.session_id,
project: session.project,
timestamp: session.created_at
};
this.appendToIndex(sessionRecord);
}
async getSession(): Promise<null> {
// Not supported in JSONL mode
return null;
}
async hasSession(sessionId: string): Promise<boolean> {
const sessionIds = await this.getAllSessionIds();
return sessionIds.has(sessionId);
}
async getAllSessionIds(): Promise<Set<string>> {
const indexPath = this.pathDiscovery.getIndexPath();
if (!fs.existsSync(indexPath)) {
return new Set();
}
const content = fs.readFileSync(indexPath, 'utf-8');
const lines = content.trim().split('\\n').filter(line => line.trim());
const sessionIds = new Set<string>();
for (const line of lines) {
try {
const obj = JSON.parse(line);
if (obj.session_id) {
sessionIds.add(obj.session_id);
}
} catch {
// Skip malformed JSON
}
}
return sessionIds;
}
async getRecentSessions(): Promise<SessionRow[]> {
// Not fully supported in JSONL mode - return empty array
return [];
}
async getRecentSessionsForProject(): Promise<SessionRow[]> {
// Not fully supported in JSONL mode - return empty array
return [];
}
async createMemory(memory: MemoryInput): Promise<void> {
const memoryRecord = {
type: 'memory',
text: memory.text,
document_id: memory.document_id,
keywords: memory.keywords,
session_id: memory.session_id,
project: memory.project,
timestamp: memory.created_at,
archive: memory.archive_basename
};
this.appendToIndex(memoryRecord);
}
async createMemories(memories: MemoryInput[]): Promise<void> {
for (const memory of memories) {
await this.createMemory(memory);
}
}
async getRecentMemories(): Promise<MemoryRow[]> {
// Not fully supported in JSONL mode - return empty array
return [];
}
async getRecentMemoriesForProject(): Promise<MemoryRow[]> {
// Not fully supported in JSONL mode - return empty array
return [];
}
async hasDocumentId(documentId: string): Promise<boolean> {
const indexPath = this.pathDiscovery.getIndexPath();
if (!fs.existsSync(indexPath)) {
return false;
}
const content = fs.readFileSync(indexPath, 'utf-8');
const lines = content.trim().split('\\n').filter(line => line.trim());
for (const line of lines) {
try {
const obj = JSON.parse(line);
if (obj.type === 'memory' && obj.document_id === documentId) {
return true;
}
} catch {
// Skip malformed JSON
}
}
return false;
}
async createOverview(overview: OverviewInput): Promise<void> {
const overviewRecord = {
type: 'overview',
content: overview.content,
session_id: overview.session_id,
project: overview.project,
timestamp: overview.created_at
};
this.appendToIndex(overviewRecord);
}
async upsertOverview(overview: OverviewInput): Promise<void> {
// Just append in JSONL mode (no real upsert)
await this.createOverview(overview);
}
async getRecentOverviews(): Promise<OverviewRow[]> {
// Not fully supported in JSONL mode - return empty array
return [];
}
async getRecentOverviewsForProject(): Promise<OverviewRow[]> {
// Not fully supported in JSONL mode - return empty array
return [];
}
async createDiagnostic(diagnostic: DiagnosticInput): Promise<void> {
const diagnosticRecord = {
type: 'diagnostic',
message: diagnostic.message,
session_id: diagnostic.session_id,
project: diagnostic.project,
timestamp: diagnostic.created_at
};
this.appendToIndex(diagnosticRecord);
}
}
/**
* Storage provider factory and singleton
* Storage provider singleton
*/
let storageProvider: IStorageProvider | null = null;
/**
* Get the configured storage provider
* Get the configured storage provider (always SQLite)
*/
export async function getStorageProvider(): Promise<IStorageProvider> {
if (storageProvider) {
return storageProvider;
}
// Try SQLite first
const sqliteProvider = new SQLiteStorageProvider();
if (await sqliteProvider.isAvailable()) {
storageProvider = sqliteProvider;
return storageProvider;
}
// Fall back to JSONL
const jsonlProvider = new JSONLStorageProvider();
if (await jsonlProvider.isAvailable()) {
storageProvider = jsonlProvider;
return storageProvider;
}
throw new Error('No storage backend available');
}
/**
* Force a specific storage provider (useful for testing)
*/
export function setStorageProvider(provider: IStorageProvider): void {
storageProvider = provider;
}
/**
* Check if SQLite migration is needed
*/
export async function needsMigration(): Promise<boolean> {
const pathDiscovery = PathDiscovery.getInstance();
const indexPath = pathDiscovery.getIndexPath();
// If JSONL exists but SQLite is not available, migration is needed
if (fs.existsSync(indexPath)) {
const sqliteProvider = new SQLiteStorageProvider();
const sqliteAvailable = await sqliteProvider.isAvailable();
if (!sqliteAvailable) {
return true;
}
// Check if SQLite has data
try {
const stores = await createStores();
const sessionCount = stores.sessions.count();
return sessionCount === 0; // Needs migration if SQLite is empty
} catch {
return true;
}
}
return false;
throw new Error('SQLite storage backend unavailable');
}
-18
View File
@@ -5,24 +5,6 @@
* Only includes types that are actively imported and used.
*/
// =============================================================================
// ERROR CLASSES
// =============================================================================
/**
* Custom error class for compression failures
*/
export class CompressionError extends Error {
constructor(
message: string,
public transcriptPath: string,
public stage: 'reading' | 'analyzing' | 'compressing' | 'writing'
) {
super(message);
this.name = 'CompressionError';
}
}
// =============================================================================
// CONFIGURATION TYPES
// =============================================================================