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:
@@ -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> =======================================
|
||||
}
|
||||
@@ -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
@@ -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');
|
||||
}
|
||||
@@ -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
|
||||
// =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user