Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/frontend/src/main/ipc-handlers/env-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { parseEnvFile } from './utils';
import { getClaudeCliInvocation, getClaudeCliInvocationAsync } from '../claude-cli-utils';
import { debugError } from '../../shared/utils/debug-logger';
import { getSpawnOptions, getSpawnCommand } from '../env-utils';
import { getMemoriesDir } from '../config-paths';

// GitLab environment variable keys
const GITLAB_ENV_KEYS = {
Expand Down Expand Up @@ -336,7 +337,7 @@ ${existingVars['OLLAMA_EMBEDDING_DIM'] ? `OLLAMA_EMBEDDING_DIM=${existingVars['O

# LadybugDB Database (embedded - no Docker required)
${existingVars['GRAPHITI_DATABASE'] ? `GRAPHITI_DATABASE=${existingVars['GRAPHITI_DATABASE']}` : '# GRAPHITI_DATABASE=auto_claude_memory'}
${existingVars['GRAPHITI_DB_PATH'] ? `GRAPHITI_DB_PATH=${existingVars['GRAPHITI_DB_PATH']}` : '# GRAPHITI_DB_PATH=~/.auto-claude/memories'}
${existingVars['GRAPHITI_DB_PATH'] ? `GRAPHITI_DB_PATH=${existingVars['GRAPHITI_DB_PATH']}` : `# GRAPHITI_DB_PATH=${getMemoriesDir()}`}
`;

return content;
Expand Down
16 changes: 16 additions & 0 deletions apps/frontend/src/main/ipc-handlers/memory-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,22 @@ export function registerMemoryHandlers(): void {
}
);

// Get platform-specific memories directory path
ipcMain.handle(
IPC_CHANNELS.MEMORY_GET_DIR,
async (): Promise<IPCResult<string>> => {
try {
const memoriesDir = getDefaultDbPath();
return { success: true, data: memoriesDir };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to get memories directory',
};
}
}
);

// Test memory database connection
ipcMain.handle(
IPC_CHANNELS.MEMORY_TEST_CONNECTION,
Expand Down
93 changes: 92 additions & 1 deletion apps/frontend/src/main/memory-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,7 @@ export class MemoryService {
if (!data.databaseExists) {
return {
success: false,
message: `Database not found at ${data.databasePath}/${data.database}`,
message: `Database not found at ${path.join(data.databasePath, data.database)}`,
};
}

Expand Down Expand Up @@ -744,11 +744,97 @@ export function isKuzuAvailable(): boolean {
return scriptPath !== null;
}

/**
* Check if LadybugDB (real_ladybug) Python package is installed
* Returns detailed status about the installation
*/
export interface LadybugInstallStatus {
installed: boolean;
pythonAvailable: boolean;
error?: string;
}

let ladybugInstallCache: LadybugInstallStatus | null = null;

export function checkLadybugInstalled(): LadybugInstallStatus {
// Return cached result if available (avoid repeated slow checks)
if (ladybugInstallCache !== null) {
return ladybugInstallCache;
}

const pythonCmd = findPythonCommand();
if (!pythonCmd) {
ladybugInstallCache = {
installed: false,
pythonAvailable: false,
error: 'Python not found. Please install Python 3.12 or later.',
};
return ladybugInstallCache;
}

try {
const [cmd, args] = parsePythonCommand(pythonCmd);
const checkArgs = [...args, '-c', 'import real_ladybug; print("OK")'];

const { spawnSync } = require('child_process');
const result = spawnSync(cmd, checkArgs, {
encoding: 'utf-8',
timeout: 10000,
windowsHide: true,
});
Comment on lines +779 to +784
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Use static import instead of dynamic require.

spawnSync should be imported at the top of the file alongside spawn from child_process rather than using a dynamic require call. This is cleaner and more consistent with the existing imports.

♻️ Proposed fix

At the top of the file (line 10), update the import:

-import { spawn } from 'child_process';
+import { spawn, spawnSync } from 'child_process';

Then at line 779:

-    const { spawnSync } = require('child_process');
-    const result = spawnSync(cmd, checkArgs, {
+    const result = spawnSync(cmd, checkArgs, {
🤖 Prompt for AI Agents
In `@apps/frontend/src/main/memory-service.ts` around lines 779 - 784, The code
uses a dynamic require to get spawnSync; replace that with a static import at
the top of the module alongside spawn from 'child_process' and remove the
require call where spawnSync is used (the block that currently declares const {
spawnSync } = require('child_process'); and then calls spawnSync). Update
imports to statically import { spawn, spawnSync } from 'child_process' so both
symbols are available for the code that calls spawnSync(cmd, checkArgs, ...).


if (result.status === 0 && result.stdout?.includes('OK')) {
ladybugInstallCache = {
installed: true,
pythonAvailable: true,
};
} else {
// Parse error to provide helpful message
const stderr = result.stderr || '';
let error = 'LadybugDB (real_ladybug) is not installed.';

if (stderr.includes('ModuleNotFoundError') || stderr.includes('No module named')) {
error =
'LadybugDB (real_ladybug) is not installed. ' +
'On Windows, this may require Visual Studio Build Tools to compile.';
} else if (stderr.includes('WinError 2') || stderr.includes('system cannot find')) {
error =
'Failed to build LadybugDB. ' +
'Please install Visual Studio Build Tools with C++ workload from: ' +
'https://visualstudio.microsoft.com/visual-cpp-build-tools/';
}

ladybugInstallCache = {
installed: false,
pythonAvailable: true,
error,
};
}
} catch (err) {
ladybugInstallCache = {
installed: false,
pythonAvailable: true,
error: `Failed to check LadybugDB: ${err instanceof Error ? err.message : String(err)}`,
};
}

return ladybugInstallCache;
}

/**
* Clear the LadybugDB installation cache (useful after installation attempt)
*/
export function clearLadybugInstallCache(): void {
ladybugInstallCache = null;
}

/**
* Get memory service status
*/
export interface MemoryServiceStatus {
kuzuInstalled: boolean;
ladybugInstalled: boolean;
ladybugError?: string;
databasePath: string;
databaseExists: boolean;
databases: string[];
Expand All @@ -765,8 +851,13 @@ export function getMemoryServiceStatus(dbPath?: string): MemoryServiceStatus {
const pythonAvailable = findPythonCommand() !== null;
const scriptAvailable = getQueryScriptPath() !== null;

// Check if LadybugDB is actually installed
const ladybugStatus = checkLadybugInstalled();

return {
kuzuInstalled: pythonAvailable && scriptAvailable,
ladybugInstalled: ladybugStatus.installed,
ladybugError: ladybugStatus.error,
databasePath: basePath,
databaseExists: databases.length > 0,
databases,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,53 @@ describe('claude-integration-handler - Helper Functions', () => {
});
});

describe('escapeShellCommand', () => {
it('should add & call operator for PowerShell on Windows', async () => {
mockPlatform('win32');
const { escapeShellCommand } = await import('../claude-integration-handler');

// PowerShell needs & to execute commands with -- flags
// Without &, PowerShell interprets -- as the decrement operator
const result = escapeShellCommand('C:\\Users\\test\\claude.exe', 'powershell');
expect(result).toBe('& "C:\\Users\\test\\claude.exe"');
});

it('should NOT add & call operator for cmd.exe on Windows', async () => {
mockPlatform('win32');
const { escapeShellCommand } = await import('../claude-integration-handler');

const result = escapeShellCommand('C:\\Users\\test\\claude.exe', 'cmd');
expect(result).toBe('"C:\\Users\\test\\claude.exe"');
expect(result).not.toContain('&');
});

it('should default to cmd.exe style when shellType is undefined on Windows', async () => {
mockPlatform('win32');
const { escapeShellCommand } = await import('../claude-integration-handler');

const result = escapeShellCommand('C:\\Users\\test\\claude.exe');
expect(result).toBe('"C:\\Users\\test\\claude.exe"');
expect(result).not.toContain('&');
});

it('should use single quotes on Unix', async () => {
mockPlatform('darwin');
const { escapeShellCommand } = await import('../claude-integration-handler');

const result = escapeShellCommand('/usr/local/bin/claude');
expect(result).toBe("'/usr/local/bin/claude'");
});

it('should escape special characters in PowerShell path', async () => {
mockPlatform('win32');
const { escapeShellCommand } = await import('../claude-integration-handler');

// Paths with special chars like % should be escaped
const result = escapeShellCommand('C:\\Users\\test%user\\claude.exe', 'powershell');
expect(result).toBe('& "C:\\Users\\test%%user\\claude.exe"');
});
});
Comment on lines +646 to +691
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add Linux coverage for escapeShellCommand tests.

These new tests exercise Windows and macOS, but not Linux. Please add a Linux case to satisfy the “all three platforms” test rule.

✅ Suggested test addition
     it('should use single quotes on Unix', async () => {
       mockPlatform('darwin');
       const { escapeShellCommand } = await import('../claude-integration-handler');

       const result = escapeShellCommand('/usr/local/bin/claude');
       expect(result).toBe("'/usr/local/bin/claude'");
     });
+
+    it('should use single quotes on Linux', async () => {
+      mockPlatform('linux');
+      const { escapeShellCommand } = await import('../claude-integration-handler');
+
+      const result = escapeShellCommand('/usr/local/bin/claude');
+      expect(result).toBe("'/usr/local/bin/claude'");
+    });

As per coding guidelines, please ensure platform-specific tests cover Windows, macOS, and Linux.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
describe('escapeShellCommand', () => {
it('should add & call operator for PowerShell on Windows', async () => {
mockPlatform('win32');
const { escapeShellCommand } = await import('../claude-integration-handler');
// PowerShell needs & to execute commands with -- flags
// Without &, PowerShell interprets -- as the decrement operator
const result = escapeShellCommand('C:\\Users\\test\\claude.exe', 'powershell');
expect(result).toBe('& "C:\\Users\\test\\claude.exe"');
});
it('should NOT add & call operator for cmd.exe on Windows', async () => {
mockPlatform('win32');
const { escapeShellCommand } = await import('../claude-integration-handler');
const result = escapeShellCommand('C:\\Users\\test\\claude.exe', 'cmd');
expect(result).toBe('"C:\\Users\\test\\claude.exe"');
expect(result).not.toContain('&');
});
it('should default to cmd.exe style when shellType is undefined on Windows', async () => {
mockPlatform('win32');
const { escapeShellCommand } = await import('../claude-integration-handler');
const result = escapeShellCommand('C:\\Users\\test\\claude.exe');
expect(result).toBe('"C:\\Users\\test\\claude.exe"');
expect(result).not.toContain('&');
});
it('should use single quotes on Unix', async () => {
mockPlatform('darwin');
const { escapeShellCommand } = await import('../claude-integration-handler');
const result = escapeShellCommand('/usr/local/bin/claude');
expect(result).toBe("'/usr/local/bin/claude'");
});
it('should escape special characters in PowerShell path', async () => {
mockPlatform('win32');
const { escapeShellCommand } = await import('../claude-integration-handler');
// Paths with special chars like % should be escaped
const result = escapeShellCommand('C:\\Users\\test%user\\claude.exe', 'powershell');
expect(result).toBe('& "C:\\Users\\test%%user\\claude.exe"');
});
});
describe('escapeShellCommand', () => {
it('should add & call operator for PowerShell on Windows', async () => {
mockPlatform('win32');
const { escapeShellCommand } = await import('../claude-integration-handler');
// PowerShell needs & to execute commands with -- flags
// Without &, PowerShell interprets -- as the decrement operator
const result = escapeShellCommand('C:\\Users\\test\\claude.exe', 'powershell');
expect(result).toBe('& "C:\\Users\\test\\claude.exe"');
});
it('should NOT add & call operator for cmd.exe on Windows', async () => {
mockPlatform('win32');
const { escapeShellCommand } = await import('../claude-integration-handler');
const result = escapeShellCommand('C:\\Users\\test\\claude.exe', 'cmd');
expect(result).toBe('"C:\\Users\\test\\claude.exe"');
expect(result).not.toContain('&');
});
it('should default to cmd.exe style when shellType is undefined on Windows', async () => {
mockPlatform('win32');
const { escapeShellCommand } = await import('../claude-integration-handler');
const result = escapeShellCommand('C:\\Users\\test\\claude.exe');
expect(result).toBe('"C:\\Users\\test\\claude.exe"');
expect(result).not.toContain('&');
});
it('should use single quotes on Unix', async () => {
mockPlatform('darwin');
const { escapeShellCommand } = await import('../claude-integration-handler');
const result = escapeShellCommand('/usr/local/bin/claude');
expect(result).toBe("'/usr/local/bin/claude'");
});
it('should use single quotes on Linux', async () => {
mockPlatform('linux');
const { escapeShellCommand } = await import('../claude-integration-handler');
const result = escapeShellCommand('/usr/local/bin/claude');
expect(result).toBe("'/usr/local/bin/claude'");
});
it('should escape special characters in PowerShell path', async () => {
mockPlatform('win32');
const { escapeShellCommand } = await import('../claude-integration-handler');
// Paths with special chars like % should be escaped
const result = escapeShellCommand('C:\\Users\\test%user\\claude.exe', 'powershell');
expect(result).toBe('& "C:\\Users\\test%%user\\claude.exe"');
});
});
🤖 Prompt for AI Agents
In `@apps/frontend/src/main/terminal/__tests__/claude-integration-handler.test.ts`
around lines 646 - 691, Add a Linux-specific unit test to the escapeShellCommand
suite: inside the existing describe('escapeShellCommand') block add a test that
calls mockPlatform('linux'), imports escapeShellCommand from
../claude-integration-handler, calls escapeShellCommand('/usr/local/bin/claude')
and asserts the result equals "'/usr/local/bin/claude'"; this mirrors the macOS
(darwin) case and ensures Linux coverage alongside Windows and macOS.


describe('finalizeClaudeInvoke', () => {
it('should set terminal title to "Claude" for default profile when terminal has default name', async () => {
const { finalizeClaudeInvoke } = await import('../claude-integration-handler');
Expand Down
25 changes: 19 additions & 6 deletions apps/frontend/src/main/terminal/claude-integration-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import * as OutputParser from './output-parser';
import * as SessionHandler from './session-handler';
import * as PtyManager from './pty-manager';
import { debugLog, debugError } from '../../shared/utils/debug-logger';
import { escapeShellArg, escapeForWindowsDoubleQuote, buildCdCommand } from '../../shared/utils/shell-escape';
import { escapeShellArg, escapeForWindowsDoubleQuote, buildCdCommand, type WindowsShellType } from '../../shared/utils/shell-escape';
import { getClaudeCliInvocation, getClaudeCliInvocationAsync } from '../claude-cli-utils';
import { isWindows } from '../platform';
import type {
Expand Down Expand Up @@ -94,16 +94,29 @@ function buildPathPrefix(pathEnv: string): string {
* On Windows, wraps in double quotes for cmd.exe. Since the value is inside
* double quotes, we use escapeForWindowsDoubleQuote() (only escapes embedded
* double quotes as ""). Caret escaping is NOT used inside double quotes.
*
* For PowerShell, adds the call operator (&) before the command to prevent
* PowerShell from interpreting -- flags as the pre-decrement operator.
*
* On Unix/macOS, wraps in single quotes for bash.
*
* @param cmd - The command to escape
* @param shellType - On Windows, specify 'powershell' or 'cmd' for correct syntax.
* @returns The escaped command safe for use in shell commands
*/
function escapeShellCommand(cmd: string): string {
export function escapeShellCommand(cmd: string, shellType?: WindowsShellType): string {
if (isWindows()) {
// Windows: Wrap in double quotes and escape only embedded double quotes
// Inside double quotes, caret is literal, so use escapeForWindowsDoubleQuote()
const escapedCmd = escapeForWindowsDoubleQuote(cmd);

if (shellType === 'powershell') {
// PowerShell: Use call operator (&) to execute the command
// Without &, PowerShell interprets "--flag" as pre-decrement operator
return `& "${escapedCmd}"`;
}

// cmd.exe: Just wrap in double quotes
return `"${escapedCmd}"`;
Comment on lines +97 to 120
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "*integration-handler*" | head -20

Repository: AndyMik90/Auto-Claude

Length of output: 207


🏁 Script executed:

fd "escapeForWindowsDoubleQuote" --type f

Repository: AndyMik90/Auto-Claude

Length of output: 47


🏁 Script executed:

fd "claude-integration-handler" --type f

Repository: AndyMik90/Auto-Claude

Length of output: 203


🏁 Script executed:

cat -n apps/frontend/src/main/terminal/claude-integration-handler.ts | sed -n '90,125p'

Repository: AndyMik90/Auto-Claude

Length of output: 1710


🏁 Script executed:

rg "escapeForWindowsDoubleQuote" -A 10 -B 2

Repository: AndyMik90/Auto-Claude

Length of output: 16341


🏁 Script executed:

cat -n apps/frontend/src/main/terminal/__tests__/claude-integration-handler.test.ts | grep -A 50 "escapeShellCommand"

Repository: AndyMik90/Auto-Claude

Length of output: 4461


🏁 Script executed:

cat -n apps/frontend/src/shared/utils/shell-escape.ts | head -80

Repository: AndyMik90/Auto-Claude

Length of output: 3514


🏁 Script executed:

cat -n apps/frontend/src/shared/utils/shell-escape.ts | sed -n '79,130p'

Repository: AndyMik90/Auto-Claude

Length of output: 2593


🏁 Script executed:

# Check if there are any PowerShell-specific escaping functions in the codebase
rg "PowerShell|powershell" -A 3 -B 3 apps/frontend/src/shared/utils/shell-escape.ts

Repository: AndyMik90/Auto-Claude

Length of output: 1024


🏁 Script executed:

cat -n apps/frontend/src/shared/utils/shell-escape.ts | sed -n '130,145p'

Repository: AndyMik90/Auto-Claude

Length of output: 948


🏁 Script executed:

# Let me check the complete escapeForWindowsDoubleQuote function
cat -n apps/frontend/src/shared/utils/shell-escape.ts | sed -n '130,150p'

Repository: AndyMik90/Auto-Claude

Length of output: 1165


🏁 Script executed:

# Check how escapeShellCommand is used in the codebase
rg "escapeShellCommand" -B 2 -A 2 --type ts --type tsx

Repository: AndyMik90/Auto-Claude

Length of output: 92


🏁 Script executed:

# Let me verify the PowerShell-specific test more thoroughly
cat -n apps/frontend/src/main/terminal/__tests__/claude-integration-handler.test.ts | sed -n '683,691p'

Repository: AndyMik90/Auto-Claude

Length of output: 549


🏁 Script executed:

# Let me search for more context about how PowerShell handles special characters
rg "PowerShell" -A 5 -B 5 apps/frontend/src

Repository: AndyMik90/Auto-Claude

Length of output: 37372


🏁 Script executed:

# Check if there are any other tests that validate PowerShell escaping with special characters
rg "powershell" apps/frontend/src/main/terminal/__tests__/claude-integration-handler.test.ts -A 3 -B 3

Repository: AndyMik90/Auto-Claude

Length of output: 675


Use PowerShell-safe escaping when shellType === 'powershell'.

In PowerShell, double-quoted strings trigger variable expansion ($var), command substitution, and backtick escaping. Using escapeForWindowsDoubleQuote() for PowerShell leaves $ and backticks unescaped, allowing unintended interpolation. Paths with % are also unnecessarily escaped as %% (valid only in cmd.exe).

Switch to single-quote escaping for PowerShell, which prevents all expansion. Handle only embedded single quotes by doubling them:

🔧 Suggested fix
export function escapeShellCommand(cmd: string, shellType?: WindowsShellType): string {
  if (isWindows()) {
    if (shellType === 'powershell') {
      // PowerShell: Use single quotes to prevent variable expansion, backtick interpretation
      const escapedCmd = cmd
        .replace(/\r/g, '')
        .replace(/\n/g, '')
        .replace(/'/g, "''");
      return `& '${escapedCmd}'`;
    }

    // cmd.exe: Wrap in double quotes and escape embedded double quotes/percents
    const escapedCmd = escapeForWindowsDoubleQuote(cmd);
    return `"${escapedCmd}"`;
  }
  // Unix/macOS: Wrap in single quotes for bash
  return escapeShellArg(cmd);
}
🤖 Prompt for AI Agents
In `@apps/frontend/src/main/terminal/claude-integration-handler.ts` around lines
97 - 120, The PowerShell branch in escapeShellCommand should use PowerShell-safe
single-quote quoting instead of escapeForWindowsDoubleQuote: when isWindows()
and shellType === 'powershell', produce a single-quoted string (handle embedded
single quotes by doubling them) and prefix with the call operator (&) so the
result is & '...'; do not apply cmd.exe-specific percent (%) escaping or
double-quote escaping (escapeForWindowsDoubleQuote) in this branch—leave
escapeForWindowsDoubleQuote for the cmd.exe branch only.

}
// Unix/macOS: Wrap in single quotes for bash
Expand Down Expand Up @@ -571,7 +584,7 @@ export function invokeClaude(

const cwdCommand = buildCdCommand(cwd, terminal.shellType);
const { command: claudeCmd, env: claudeEnv } = getClaudeCliInvocation();
const escapedClaudeCmd = escapeShellCommand(claudeCmd);
const escapedClaudeCmd = escapeShellCommand(claudeCmd, terminal.shellType);
const pathPrefix = buildPathPrefix(claudeEnv.PATH || '');
const needsEnvOverride = profileId && profileId !== previousProfileId;

Expand Down Expand Up @@ -676,7 +689,7 @@ export function resumeClaude(
SessionHandler.releaseSessionId(terminal.id);

const { command: claudeCmd, env: claudeEnv } = getClaudeCliInvocation();
const escapedClaudeCmd = escapeShellCommand(claudeCmd);
const escapedClaudeCmd = escapeShellCommand(claudeCmd, terminal.shellType);
const pathPrefix = buildPathPrefix(claudeEnv.PATH || '');

// Always use --continue which resumes the most recent session in the current directory.
Expand Down Expand Up @@ -793,7 +806,7 @@ export async function invokeClaudeAsync(
if (timeoutId) clearTimeout(timeoutId);
});

const escapedClaudeCmd = escapeShellCommand(claudeCmd);
const escapedClaudeCmd = escapeShellCommand(claudeCmd, terminal.shellType);
const pathPrefix = buildPathPrefix(claudeEnv.PATH || '');
const needsEnvOverride = profileId && profileId !== previousProfileId;

Expand Down Expand Up @@ -907,7 +920,7 @@ export async function resumeClaudeAsync(
if (timeoutId) clearTimeout(timeoutId);
});

const escapedClaudeCmd = escapeShellCommand(claudeCmd);
const escapedClaudeCmd = escapeShellCommand(claudeCmd, terminal.shellType);
const pathPrefix = buildPathPrefix(claudeEnv.PATH || '');

// Always use --continue which resumes the most recent session in the current directory.
Expand Down
4 changes: 4 additions & 0 deletions apps/frontend/src/preload/api/project-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export interface ProjectAPI {
getMemoryInfrastructureStatus: (dbPath?: string) => Promise<IPCResult<InfrastructureStatus>>;
listMemoryDatabases: (dbPath?: string) => Promise<IPCResult<string[]>>;
testMemoryConnection: (dbPath?: string, database?: string) => Promise<IPCResult<GraphitiValidationResult>>;
getMemoriesDir: () => Promise<IPCResult<string>>;

// Graphiti Validation Operations
validateLLMApiKey: (provider: string, apiKey: string) => Promise<IPCResult<GraphitiValidationResult>>;
Expand Down Expand Up @@ -223,6 +224,9 @@ export const createProjectAPI = (): ProjectAPI => ({
testMemoryConnection: (dbPath?: string, database?: string): Promise<IPCResult<GraphitiValidationResult>> =>
ipcRenderer.invoke(IPC_CHANNELS.MEMORY_TEST_CONNECTION, dbPath, database),

getMemoriesDir: (): Promise<IPCResult<string>> =>
ipcRenderer.invoke(IPC_CHANNELS.MEMORY_GET_DIR),

// Graphiti Validation Operations
validateLLMApiKey: (provider: string, apiKey: string): Promise<IPCResult<GraphitiValidationResult>> =>
ipcRenderer.invoke(IPC_CHANNELS.GRAPHITI_VALIDATE_LLM, provider, apiKey),
Expand Down
18 changes: 16 additions & 2 deletions apps/frontend/src/renderer/components/context/MemoriesTab.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useMemo } from 'react';
import { useState, useMemo, useEffect } from 'react';
import {
RefreshCw,
Database,
Expand Down Expand Up @@ -79,6 +79,20 @@ export function MemoriesTab({
}: MemoriesTabProps) {
const [localSearchQuery, setLocalSearchQuery] = useState('');
const [activeFilter, setActiveFilter] = useState<FilterCategory>('all');
const [memoriesDir, setMemoriesDir] = useState<string>('');

// Fetch platform-specific memories directory path for display
useEffect(() => {
window.electronAPI.getMemoriesDir()
.then((result) => {
if (result.success && result.data) {
setMemoriesDir(result.data);
}
})
.catch((err) => {
console.error('Failed to get memories directory:', err);
});
}, []);

// Calculate memory counts by category
const memoryCounts = useMemo(() => {
Expand Down Expand Up @@ -146,7 +160,7 @@ export function MemoriesTab({
<>
<div className="grid gap-3 sm:grid-cols-2 text-sm">
<InfoItem label="Database" value={memoryStatus.database || 'auto_claude_memory'} />
<InfoItem label="Path" value={memoryStatus.dbPath || '~/.auto-claude/memories'} />
<InfoItem label="Path" value={memoryStatus.dbPath || memoriesDir || 'memories directory'} />
</div>

{/* Memory Stats Summary */}
Expand Down
Loading