From 995b6ef8439adc23438461431432a84ad130278f Mon Sep 17 00:00:00 2001 From: iliya Date: Fri, 27 Feb 2026 15:58:38 +0200 Subject: [PATCH] improvements --- .../services/infrastructure/ConfigManager.ts | 5 ++-- .../infrastructure/NotificationManager.ts | 4 ++-- .../infrastructure/SshConfigParser.ts | 6 ++--- .../infrastructure/SshConnectionManager.ts | 19 ++++++++------- src/main/services/parsing/ClaudeMdReader.ts | 5 ++-- .../services/team/ClaudeBinaryResolver.ts | 10 ++++---- src/main/services/team/FileContentResolver.ts | 10 ++++++-- .../services/team/TeamProvisioningService.ts | 3 ++- .../components/team/kanban/KanbanTaskCard.tsx | 24 +++++++++---------- test/main/utils/pathValidation.test.ts | 4 ++-- 10 files changed, 48 insertions(+), 42 deletions(-) diff --git a/src/main/services/infrastructure/ConfigManager.ts b/src/main/services/infrastructure/ConfigManager.ts index d1775f6f..64f1dca8 100644 --- a/src/main/services/infrastructure/ConfigManager.ts +++ b/src/main/services/infrastructure/ConfigManager.ts @@ -9,11 +9,10 @@ * - Handle JSON parse errors gracefully */ -import { setClaudeBasePathOverride } from '@main/utils/pathDecoder'; +import { getHomeDir, setClaudeBasePathOverride } from '@main/utils/pathDecoder'; import { validateRegexPattern } from '@main/utils/regexValidation'; import { createLogger } from '@shared/utils/logger'; import * as fs from 'fs'; -import * as os from 'os'; import * as path from 'path'; import { DEFAULT_TRIGGERS, TriggerManager } from './TriggerManager'; @@ -23,7 +22,7 @@ import type { SshConnectionProfile } from '@shared/types/api'; const logger = createLogger('Service:ConfigManager'); -const CONFIG_DIR = path.join(os.homedir(), '.claude'); +const CONFIG_DIR = path.join(getHomeDir(), '.claude'); const CONFIG_FILENAME = 'claude-devtools-config.json'; const DEFAULT_CONFIG_PATH = path.join(CONFIG_DIR, CONFIG_FILENAME); diff --git a/src/main/services/infrastructure/NotificationManager.ts b/src/main/services/infrastructure/NotificationManager.ts index a92db953..b68683a4 100644 --- a/src/main/services/infrastructure/NotificationManager.ts +++ b/src/main/services/infrastructure/NotificationManager.ts @@ -12,11 +12,11 @@ * - Emit IPC events to renderer: notification:new, notification:updated */ +import { getHomeDir } from '@main/utils/pathDecoder'; import { createLogger } from '@shared/utils/logger'; import { type BrowserWindow, Notification } from 'electron'; import { EventEmitter } from 'events'; import * as fs from 'fs'; -import * as os from 'os'; import * as path from 'path'; import { type DetectedError } from '../error/ErrorMessageBuilder'; @@ -77,7 +77,7 @@ const MAX_NOTIFICATIONS = 100; const THROTTLE_MS = 5000; /** Path to notifications storage file */ -const NOTIFICATIONS_PATH = path.join(os.homedir(), '.claude', 'claude-devtools-notifications.json'); +const NOTIFICATIONS_PATH = path.join(getHomeDir(), '.claude', 'claude-devtools-notifications.json'); // ============================================================================= // NotificationManager Class diff --git a/src/main/services/infrastructure/SshConfigParser.ts b/src/main/services/infrastructure/SshConfigParser.ts index d49f80ee..a25ef7c5 100644 --- a/src/main/services/infrastructure/SshConfigParser.ts +++ b/src/main/services/infrastructure/SshConfigParser.ts @@ -8,9 +8,9 @@ * - Gracefully handle missing/unreadable files */ +import { getHomeDir } from '@main/utils/pathDecoder'; import { createLogger } from '@shared/utils/logger'; import * as fs from 'fs'; -import * as os from 'os'; import * as path from 'path'; import SSHConfig from 'ssh-config'; @@ -22,7 +22,7 @@ export class SshConfigParser { private configPath: string; constructor(configPath?: string) { - this.configPath = configPath ?? path.join(os.homedir(), '.ssh', 'config'); + this.configPath = configPath ?? path.join(getHomeDir(), '.ssh', 'config'); } /** @@ -151,7 +151,7 @@ export class SshConfigParser { } const pattern = match[1].trim(); - const expandedPattern = pattern.replace(/^~/, os.homedir()); + const expandedPattern = pattern.replace(/^~/, getHomeDir()); try { // Handle glob-like patterns by checking if the path contains wildcards diff --git a/src/main/services/infrastructure/SshConnectionManager.ts b/src/main/services/infrastructure/SshConnectionManager.ts index f8fdf48f..152bf060 100644 --- a/src/main/services/infrastructure/SshConnectionManager.ts +++ b/src/main/services/infrastructure/SshConnectionManager.ts @@ -9,6 +9,7 @@ * - Handle reconnection on errors */ +import { getHomeDir } from '@main/utils/pathDecoder'; import { createLogger } from '@shared/utils/logger'; import { execFile } from 'child_process'; import { EventEmitter } from 'events'; @@ -267,7 +268,7 @@ export class SshConnectionManager extends EventEmitter { break; case 'privateKey': { - const keyPath = config.privateKeyPath ?? path.join(os.homedir(), '.ssh', 'id_rsa'); + const keyPath = config.privateKeyPath ?? path.join(getHomeDir(), '.ssh', 'id_rsa'); try { const keyData = await fs.promises.readFile(keyPath, 'utf8'); connectConfig.privateKey = keyData; @@ -347,15 +348,15 @@ export class SshConnectionManager extends EventEmitter { const knownPaths = [ // 1Password SSH agent path.join( - os.homedir(), + getHomeDir(), 'Library', 'Group Containers', '2BUA8C4S2C.com.1password', 'agent.sock' ), - path.join(os.homedir(), '.1password', 'agent.sock'), + path.join(getHomeDir(), '.1password', 'agent.sock'), // Common user agent socket - path.join(os.homedir(), '.ssh', 'agent.sock'), + path.join(getHomeDir(), '.ssh', 'agent.sock'), ]; // Linux: add system paths @@ -395,8 +396,8 @@ export class SshConnectionManager extends EventEmitter { // The config parser already told us there's an identity file. // Try common identity file locations from config const configKeyPaths = [ - path.join(os.homedir(), '.ssh', 'id_ed25519'), - path.join(os.homedir(), '.ssh', 'id_rsa'), + path.join(getHomeDir(), '.ssh', 'id_ed25519'), + path.join(getHomeDir(), '.ssh', 'id_rsa'), ]; for (const keyPath of configKeyPaths) { try { @@ -417,9 +418,9 @@ export class SshConnectionManager extends EventEmitter { // Try default key files const defaultKeys = [ - path.join(os.homedir(), '.ssh', 'id_ed25519'), - path.join(os.homedir(), '.ssh', 'id_rsa'), - path.join(os.homedir(), '.ssh', 'id_ecdsa'), + path.join(getHomeDir(), '.ssh', 'id_ed25519'), + path.join(getHomeDir(), '.ssh', 'id_rsa'), + path.join(getHomeDir(), '.ssh', 'id_ecdsa'), ]; for (const keyPath of defaultKeys) { diff --git a/src/main/services/parsing/ClaudeMdReader.ts b/src/main/services/parsing/ClaudeMdReader.ts index e78447c7..14430a3a 100644 --- a/src/main/services/parsing/ClaudeMdReader.ts +++ b/src/main/services/parsing/ClaudeMdReader.ts @@ -8,10 +8,9 @@ * - Support tilde (~) expansion to home directory */ -import { encodePath, getClaudeBasePath } from '@main/utils/pathDecoder'; +import { encodePath, getClaudeBasePath, getHomeDir } from '@main/utils/pathDecoder'; import { countTokens } from '@main/utils/tokenizer'; import { createLogger } from '@shared/utils/logger'; -import { app } from 'electron'; import * as path from 'path'; import { LocalFileSystemProvider } from '../infrastructure/LocalFileSystemProvider'; @@ -48,7 +47,7 @@ export interface ClaudeMdReadResult { */ function expandTilde(filePath: string): string { if (filePath.startsWith('~')) { - const homeDir = app.getPath('home'); + const homeDir = getHomeDir(); return path.join(homeDir, filePath.slice(1)); } return filePath; diff --git a/src/main/services/team/ClaudeBinaryResolver.ts b/src/main/services/team/ClaudeBinaryResolver.ts index a897975c..f1a39934 100644 --- a/src/main/services/team/ClaudeBinaryResolver.ts +++ b/src/main/services/team/ClaudeBinaryResolver.ts @@ -1,5 +1,5 @@ +import { getHomeDir } from '@main/utils/pathDecoder'; import * as fs from 'fs'; -import * as os from 'os'; import * as path from 'path'; async function isExecutable(filePath: string): Promise { @@ -61,7 +61,7 @@ function expandWindowsBinaryNames(binaryName: string): string[] { } async function collectNvmCandidates(): Promise { - const nvmNodeRoot = path.join(os.homedir(), '.nvm', 'versions', 'node'); + const nvmNodeRoot = path.join(getHomeDir(), '.nvm', 'versions', 'node'); let versions: string[]; try { versions = await fs.promises.readdir(nvmNodeRoot); @@ -172,9 +172,9 @@ export class ClaudeBinaryResolver { const candidateDirs: string[] = [ // Native binary installation path (claude install) - path.join(os.homedir(), '.local', 'bin'), - path.join(os.homedir(), '.npm-global', 'bin'), - path.join(os.homedir(), '.npm', 'bin'), + path.join(getHomeDir(), '.local', 'bin'), + path.join(getHomeDir(), '.npm-global', 'bin'), + path.join(getHomeDir(), '.npm', 'bin'), process.platform === 'win32' ? process.env.APPDATA ? path.join(process.env.APPDATA, 'npm') diff --git a/src/main/services/team/FileContentResolver.ts b/src/main/services/team/FileContentResolver.ts index 4b1f86fb..0cfb6dfa 100644 --- a/src/main/services/team/FileContentResolver.ts +++ b/src/main/services/team/FileContentResolver.ts @@ -1,3 +1,4 @@ +import { getHomeDir } from '@main/utils/pathDecoder'; import { createLogger } from '@shared/utils/logger'; import { diffLines } from 'diff'; import { createReadStream } from 'fs'; @@ -257,8 +258,13 @@ export class FileContentResolver { if (!backupFileName) continue; // Construct the file-history path - const homeDir = process.env.HOME || process.env.USERPROFILE || ''; - const historyPath = path.join(homeDir, '.claude', 'file-history', sessionId, backupFileName); + const historyPath = path.join( + getHomeDir(), + '.claude', + 'file-history', + sessionId, + backupFileName + ); try { await access(historyPath); diff --git a/src/main/services/team/TeamProvisioningService.ts b/src/main/services/team/TeamProvisioningService.ts index 8c1922ba..d84a0def 100644 --- a/src/main/services/team/TeamProvisioningService.ts +++ b/src/main/services/team/TeamProvisioningService.ts @@ -5,6 +5,7 @@ import { extractBaseDir, getAutoDetectedClaudeBasePath, getClaudeBasePath, + getHomeDir, getProjectsBasePath, getTasksBasePath, getTeamsBasePath, @@ -2439,7 +2440,7 @@ export class TeamProvisioningService { private async buildProvisioningEnv(): Promise { const shellEnv = await resolveInteractiveShellEnv(); - const home = shellEnv.HOME?.trim() || process.env.HOME?.trim() || os.homedir(); + const home = shellEnv.HOME?.trim() || process.env.HOME?.trim() || getHomeDir(); const user = shellEnv.USER?.trim() || process.env.USER?.trim() || os.userInfo().username; const shell = shellEnv.SHELL?.trim() || process.env.SHELL?.trim() || '/bin/zsh'; const xdgConfigHome = diff --git a/src/renderer/components/team/kanban/KanbanTaskCard.tsx b/src/renderer/components/team/kanban/KanbanTaskCard.tsx index 5b6ecc68..b9d3345d 100644 --- a/src/renderer/components/team/kanban/KanbanTaskCard.tsx +++ b/src/renderer/components/team/kanban/KanbanTaskCard.tsx @@ -228,24 +228,24 @@ export const KanbanTaskCard = ({ #{task.id} {task.owner ? : null} - {task.needsClarification ? ( - - - {task.needsClarification === 'user' ? 'Awaiting user' : 'Awaiting lead'} - - ) : null} {!compact && (
{task.subject}
)} + {task.needsClarification ? ( + + + {task.needsClarification === 'user' ? 'Awaiting user' : 'Awaiting lead'} + + ) : null} {compact && (
{task.subject} diff --git a/test/main/utils/pathValidation.test.ts b/test/main/utils/pathValidation.test.ts index 430e1e6e..1423b086 100644 --- a/test/main/utils/pathValidation.test.ts +++ b/test/main/utils/pathValidation.test.ts @@ -7,7 +7,7 @@ import * as os from 'os'; import * as path from 'path'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; -import { setClaudeBasePathOverride } from '../../../src/main/utils/pathDecoder'; +import { getHomeDir, setClaudeBasePathOverride } from '../../../src/main/utils/pathDecoder'; import { isPathWithinAllowedDirectories, @@ -17,7 +17,7 @@ import { } from '../../../src/main/utils/pathValidation'; describe('pathValidation', () => { - const homeDir = os.homedir(); + const homeDir = getHomeDir(); const claudeDir = path.join(homeDir, '.claude'); const testProjectPath = path.resolve('/home/user/my-project');