improvements

This commit is contained in:
iliya 2026-02-27 15:58:38 +02:00
parent 427c5478e1
commit 995b6ef843
10 changed files with 48 additions and 42 deletions

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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) {

View file

@ -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;

View file

@ -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<boolean> {
@ -61,7 +61,7 @@ function expandWindowsBinaryNames(binaryName: string): string[] {
}
async function collectNvmCandidates(): Promise<string[]> {
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')

View file

@ -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);

View file

@ -5,6 +5,7 @@ import {
extractBaseDir,
getAutoDetectedClaudeBasePath,
getClaudeBasePath,
getHomeDir,
getProjectsBasePath,
getTasksBasePath,
getTeamsBasePath,
@ -2439,7 +2440,7 @@ export class TeamProvisioningService {
private async buildProvisioningEnv(): Promise<ProvisioningEnvResolution> {
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 =

View file

@ -228,24 +228,24 @@ export const KanbanTaskCard = ({
#{task.id}
</Badge>
{task.owner ? <MemberBadge name={task.owner} color={colorMap.get(task.owner)} /> : null}
{task.needsClarification ? (
<span
className={`inline-flex shrink-0 items-center gap-1 rounded-full px-1.5 py-0.5 text-[10px] font-medium ${
task.needsClarification === 'user'
? 'bg-red-500/15 text-red-400'
: 'bg-blue-500/15 text-blue-400'
}`}
>
<HelpCircle size={10} />
{task.needsClarification === 'user' ? 'Awaiting user' : 'Awaiting lead'}
</span>
) : null}
{!compact && (
<h5 className="min-w-0 truncate text-sm font-medium text-[var(--color-text)]">
{task.subject}
</h5>
)}
</div>
{task.needsClarification ? (
<span
className={`mt-1 inline-flex items-center gap-1 rounded-full px-1.5 py-0.5 text-[10px] font-medium ${
task.needsClarification === 'user'
? 'bg-red-500/15 text-red-400'
: 'bg-blue-500/15 text-blue-400'
}`}
>
<HelpCircle size={10} />
{task.needsClarification === 'user' ? 'Awaiting user' : 'Awaiting lead'}
</span>
) : null}
{compact && (
<h5 className="mt-1 truncate text-sm font-medium text-[var(--color-text)]">
{task.subject}

View file

@ -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');