Refactor context configuration and settings handling
- Updated context configuration loading path from ~/.claude/settings.json to ~/.claude-mem/settings.json. - Modified the extractPriorMessages function to focus on retrieving the last assistant message only, removing user message extraction. - Enhanced output formatting for displaying prior assistant messages in the context hook. - Added new settings related to token economics and observation filtering in the useSettings hook.
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+59
-79
@@ -52,7 +52,7 @@ interface ContextConfig {
|
||||
|
||||
/**
|
||||
* Load all context configuration settings
|
||||
* Priority: ~/.claude/settings.json > env var > defaults
|
||||
* Priority: ~/.claude-mem/settings.json > env var > defaults
|
||||
*/
|
||||
function loadContextConfig(): ContextConfig {
|
||||
const defaults = {
|
||||
@@ -71,7 +71,7 @@ function loadContextConfig(): ContextConfig {
|
||||
};
|
||||
|
||||
try {
|
||||
const settingsPath = path.join(homedir(), '.claude', 'settings.json');
|
||||
const settingsPath = path.join(homedir(), '.claude-mem', 'settings.json');
|
||||
if (!existsSync(settingsPath)) return defaults;
|
||||
|
||||
const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
||||
@@ -221,11 +221,11 @@ function renderSummaryField(label: string, value: string | null, color: string,
|
||||
|
||||
// Helper: Convert cwd path to dashed format for transcript directory name
|
||||
function cwdToDashed(cwd: string): string {
|
||||
// Remove leading slash and convert remaining slashes to dashes
|
||||
return cwd.replace(/^\//, '').replace(/\//g, '-');
|
||||
// Convert all slashes to dashes (including leading slash)
|
||||
return cwd.replace(/\//g, '-');
|
||||
}
|
||||
|
||||
// Helper: Extract last user and assistant messages from transcript file
|
||||
// Helper: Extract last assistant message from transcript file
|
||||
function extractPriorMessages(transcriptPath: string): { userMessage: string; assistantMessage: string } {
|
||||
try {
|
||||
if (!existsSync(transcriptPath)) {
|
||||
@@ -238,16 +238,23 @@ function extractPriorMessages(transcriptPath: string): { userMessage: string; as
|
||||
}
|
||||
|
||||
const lines = content.split('\n').filter(line => line.trim());
|
||||
let lastUserMessage = '';
|
||||
|
||||
// Find the last assistant message by filtering for assistant type and taking the last one
|
||||
let lastAssistantMessage = '';
|
||||
|
||||
// Parse JSONL backwards to find last user and assistant messages
|
||||
// Iterate backwards to find the most recent assistant message with text content
|
||||
for (let i = lines.length - 1; i >= 0; i--) {
|
||||
try {
|
||||
const entry = JSON.parse(lines[i]);
|
||||
const line = lines[i];
|
||||
|
||||
// Find last assistant message
|
||||
if (!lastAssistantMessage && entry.type === 'assistant' && entry.message?.content) {
|
||||
// Quick check if this line is an assistant message
|
||||
if (!line.includes('"type":"assistant"')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const entry = JSON.parse(line);
|
||||
|
||||
if (entry.type === 'assistant' && entry.message?.content && Array.isArray(entry.message.content)) {
|
||||
let text = '';
|
||||
for (const block of entry.message.content) {
|
||||
if (block.type === 'text') {
|
||||
@@ -258,35 +265,18 @@ function extractPriorMessages(transcriptPath: string): { userMessage: string; as
|
||||
text = text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, '').trim();
|
||||
if (text) {
|
||||
lastAssistantMessage = text;
|
||||
break; // Found it, stop searching
|
||||
}
|
||||
}
|
||||
|
||||
// Find last user message
|
||||
if (!lastUserMessage && entry.type === 'user' && entry.message?.content) {
|
||||
let text = '';
|
||||
for (const block of entry.message.content) {
|
||||
if (block.type === 'text') {
|
||||
text += block.text;
|
||||
}
|
||||
}
|
||||
if (text) {
|
||||
lastUserMessage = text;
|
||||
}
|
||||
}
|
||||
|
||||
// Stop once we have both
|
||||
if (lastUserMessage && lastAssistantMessage) {
|
||||
break;
|
||||
}
|
||||
} catch (parseError) {
|
||||
// Skip malformed lines
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return { userMessage: lastUserMessage, assistantMessage: lastAssistantMessage };
|
||||
return { userMessage: '', assistantMessage: lastAssistantMessage };
|
||||
} catch (error) {
|
||||
logger.debug('HOOK', `Failed to extract prior messages from ${transcriptPath}:`, {}, error as Error);
|
||||
logger.failure('HOOK', `Failed to extract prior messages from transcript`, { transcriptPath }, error as Error);
|
||||
return { userMessage: '', assistantMessage: '' };
|
||||
}
|
||||
}
|
||||
@@ -361,6 +351,8 @@ async function contextHook(input?: SessionStartInput, useColors: boolean = false
|
||||
// Retrieve prior session messages if enabled
|
||||
let priorUserMessage = '';
|
||||
let priorAssistantMessage = '';
|
||||
// let debugInfo: string[] = [];
|
||||
|
||||
if (config.showLastMessage && observations.length > 0) {
|
||||
try {
|
||||
const currentSessionId = input?.session_id;
|
||||
@@ -375,13 +367,27 @@ async function contextHook(input?: SessionStartInput, useColors: boolean = false
|
||||
const dashedCwd = cwdToDashed(cwd);
|
||||
const transcriptPath = path.join(homedir(), '.claude', 'projects', dashedCwd, `${priorSessionId}.jsonl`);
|
||||
|
||||
// debugInfo.push(`📋 Prior Message Retrieval:`);
|
||||
// debugInfo.push(` Session ID: ${priorSessionId}`);
|
||||
// debugInfo.push(` Transcript: ${transcriptPath}`);
|
||||
// debugInfo.push(` Exists: ${existsSync(transcriptPath)}`);
|
||||
|
||||
// Extract messages from transcript
|
||||
const messages = extractPriorMessages(transcriptPath);
|
||||
priorUserMessage = messages.userMessage;
|
||||
priorAssistantMessage = messages.assistantMessage;
|
||||
}
|
||||
|
||||
// if (!priorUserMessage && !priorAssistantMessage) {
|
||||
// debugInfo.push(` ⚠️ No messages extracted from transcript`);
|
||||
// } else {
|
||||
// debugInfo.push(` ✅ Found user message: ${!!priorUserMessage}`);
|
||||
// debugInfo.push(` ✅ Found assistant message: ${!!priorAssistantMessage}`);
|
||||
// }
|
||||
} // else {
|
||||
// debugInfo.push(`📋 Prior Message Retrieval: No prior session found (all observations from current session)`);
|
||||
// }
|
||||
} catch (error) {
|
||||
logger.debug('HOOK', 'Failed to retrieve prior session messages:', {}, error as Error);
|
||||
// debugInfo.push(`📋 Prior Message Retrieval Error: ${(error as Error).message}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,37 +419,6 @@ async function contextHook(input?: SessionStartInput, useColors: boolean = false
|
||||
output.push('');
|
||||
}
|
||||
|
||||
// Previously section (last messages from prior session)
|
||||
if (priorUserMessage || priorAssistantMessage) {
|
||||
if (useColors) {
|
||||
output.push(`${colors.bright}${colors.magenta}📋 Previously${colors.reset}`);
|
||||
output.push('');
|
||||
if (priorUserMessage) {
|
||||
output.push(`${colors.dim}User: ${priorUserMessage}${colors.reset}`);
|
||||
output.push('');
|
||||
}
|
||||
if (priorAssistantMessage) {
|
||||
output.push(`${colors.dim}Assistant: ${priorAssistantMessage}${colors.reset}`);
|
||||
output.push('');
|
||||
}
|
||||
output.push(`${colors.gray}${'─'.repeat(60)}${colors.reset}`);
|
||||
output.push('');
|
||||
} else {
|
||||
output.push(`**📋 Previously**`);
|
||||
output.push('');
|
||||
if (priorUserMessage) {
|
||||
output.push(`User: ${priorUserMessage}`);
|
||||
output.push('');
|
||||
}
|
||||
if (priorAssistantMessage) {
|
||||
output.push(`Assistant: ${priorAssistantMessage}`);
|
||||
output.push('');
|
||||
}
|
||||
output.push('---');
|
||||
output.push('');
|
||||
}
|
||||
}
|
||||
|
||||
// Chronological Timeline
|
||||
if (timelineObs.length > 0) {
|
||||
// Legend/Key
|
||||
@@ -789,25 +764,21 @@ async function contextHook(input?: SessionStartInput, useColors: boolean = false
|
||||
output.push(...renderSummaryField('Next Steps', mostRecentSummary.next_steps, colors.magenta, useColors));
|
||||
}
|
||||
|
||||
// Show last message from previous session if enabled
|
||||
// Note: last_assistant_message field would need to be added to session_summaries table
|
||||
// For now, this is a placeholder for the feature
|
||||
if (config.showLastMessage && mostRecentSummary) {
|
||||
// This would require the last_assistant_message field to be populated
|
||||
// The field exists but may not be populated yet in the current implementation
|
||||
const lastMessage = (mostRecentSummary as any).last_assistant_message;
|
||||
if (lastMessage) {
|
||||
// Previously section (last assistant message from prior session) - positioned at bottom for chronological sense
|
||||
if (priorAssistantMessage) {
|
||||
output.push('');
|
||||
output.push('---');
|
||||
output.push('');
|
||||
if (useColors) {
|
||||
output.push(`${colors.bright}${colors.magenta}📋 Previously${colors.reset}`);
|
||||
output.push('');
|
||||
if (useColors) {
|
||||
output.push(`${colors.bright}${colors.magenta}💬 Last Message from Previous Session${colors.reset}`);
|
||||
output.push(`${colors.dim}${lastMessage}${colors.reset}`);
|
||||
} else {
|
||||
output.push(`**💬 Last Message from Previous Session**`);
|
||||
output.push('');
|
||||
output.push(lastMessage);
|
||||
}
|
||||
output.push(`${colors.dim}A: ${priorAssistantMessage}${colors.reset}`);
|
||||
} else {
|
||||
output.push(`**📋 Previously**`);
|
||||
output.push('');
|
||||
output.push(`A: ${priorAssistantMessage}`);
|
||||
}
|
||||
output.push('');
|
||||
}
|
||||
|
||||
// Footer with token savings message (only show if token economics is visible)
|
||||
@@ -823,6 +794,15 @@ async function contextHook(input?: SessionStartInput, useColors: boolean = false
|
||||
}
|
||||
|
||||
db.close();
|
||||
|
||||
// Add debug info directly to output
|
||||
// if (debugInfo.length > 0) {
|
||||
// output.push('');
|
||||
// output.push('---');
|
||||
// output.push('');
|
||||
// output.push(...debugInfo);
|
||||
// }
|
||||
|
||||
return output.join('\n').trimEnd();
|
||||
}
|
||||
|
||||
|
||||
@@ -889,7 +889,7 @@ export class WorkerService {
|
||||
*/
|
||||
private handleGetSettings(req: Request, res: Response): void {
|
||||
try {
|
||||
const settingsPath = path.join(homedir(), '.claude', 'settings.json');
|
||||
const settingsPath = path.join(homedir(), '.claude-mem', 'settings.json');
|
||||
|
||||
if (!existsSync(settingsPath)) {
|
||||
// Return defaults if file doesn't exist
|
||||
@@ -986,7 +986,7 @@ export class WorkerService {
|
||||
}
|
||||
|
||||
// Read existing settings
|
||||
const settingsPath = path.join(homedir(), '.claude', 'settings.json');
|
||||
const settingsPath = path.join(homedir(), '.claude-mem', 'settings.json');
|
||||
let settings: any = { env: {} };
|
||||
|
||||
if (existsSync(settingsPath)) {
|
||||
|
||||
@@ -17,7 +17,26 @@ export function useSettings() {
|
||||
setSettings({
|
||||
CLAUDE_MEM_MODEL: data.CLAUDE_MEM_MODEL || DEFAULT_SETTINGS.CLAUDE_MEM_MODEL,
|
||||
CLAUDE_MEM_CONTEXT_OBSERVATIONS: data.CLAUDE_MEM_CONTEXT_OBSERVATIONS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_OBSERVATIONS,
|
||||
CLAUDE_MEM_WORKER_PORT: data.CLAUDE_MEM_WORKER_PORT || DEFAULT_SETTINGS.CLAUDE_MEM_WORKER_PORT
|
||||
CLAUDE_MEM_WORKER_PORT: data.CLAUDE_MEM_WORKER_PORT || DEFAULT_SETTINGS.CLAUDE_MEM_WORKER_PORT,
|
||||
|
||||
// Token Economics Display
|
||||
CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS: data.CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS,
|
||||
CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS: data.CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS,
|
||||
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT: data.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT,
|
||||
CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT: data.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT,
|
||||
|
||||
// Observation Filtering
|
||||
CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES: data.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES,
|
||||
CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS: data.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS,
|
||||
|
||||
// Display Configuration
|
||||
CLAUDE_MEM_CONTEXT_FULL_COUNT: data.CLAUDE_MEM_CONTEXT_FULL_COUNT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_FULL_COUNT,
|
||||
CLAUDE_MEM_CONTEXT_FULL_FIELD: data.CLAUDE_MEM_CONTEXT_FULL_FIELD || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_FULL_FIELD,
|
||||
CLAUDE_MEM_CONTEXT_SESSION_COUNT: data.CLAUDE_MEM_CONTEXT_SESSION_COUNT || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SESSION_COUNT,
|
||||
|
||||
// Feature Toggles
|
||||
CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY: data.CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY,
|
||||
CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE: data.CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE,
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
|
||||
Reference in New Issue
Block a user