improvements
This commit is contained in:
parent
427c5478e1
commit
995b6ef843
10 changed files with 48 additions and 42 deletions
|
|
@ -9,11 +9,10 @@
|
||||||
* - Handle JSON parse errors gracefully
|
* - 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 { validateRegexPattern } from '@main/utils/regexValidation';
|
||||||
import { createLogger } from '@shared/utils/logger';
|
import { createLogger } from '@shared/utils/logger';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as os from 'os';
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { DEFAULT_TRIGGERS, TriggerManager } from './TriggerManager';
|
import { DEFAULT_TRIGGERS, TriggerManager } from './TriggerManager';
|
||||||
|
|
@ -23,7 +22,7 @@ import type { SshConnectionProfile } from '@shared/types/api';
|
||||||
|
|
||||||
const logger = createLogger('Service:ConfigManager');
|
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 CONFIG_FILENAME = 'claude-devtools-config.json';
|
||||||
const DEFAULT_CONFIG_PATH = path.join(CONFIG_DIR, CONFIG_FILENAME);
|
const DEFAULT_CONFIG_PATH = path.join(CONFIG_DIR, CONFIG_FILENAME);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,11 @@
|
||||||
* - Emit IPC events to renderer: notification:new, notification:updated
|
* - Emit IPC events to renderer: notification:new, notification:updated
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { getHomeDir } from '@main/utils/pathDecoder';
|
||||||
import { createLogger } from '@shared/utils/logger';
|
import { createLogger } from '@shared/utils/logger';
|
||||||
import { type BrowserWindow, Notification } from 'electron';
|
import { type BrowserWindow, Notification } from 'electron';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as os from 'os';
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { type DetectedError } from '../error/ErrorMessageBuilder';
|
import { type DetectedError } from '../error/ErrorMessageBuilder';
|
||||||
|
|
@ -77,7 +77,7 @@ const MAX_NOTIFICATIONS = 100;
|
||||||
const THROTTLE_MS = 5000;
|
const THROTTLE_MS = 5000;
|
||||||
|
|
||||||
/** Path to notifications storage file */
|
/** 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
|
// NotificationManager Class
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,9 @@
|
||||||
* - Gracefully handle missing/unreadable files
|
* - Gracefully handle missing/unreadable files
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { getHomeDir } from '@main/utils/pathDecoder';
|
||||||
import { createLogger } from '@shared/utils/logger';
|
import { createLogger } from '@shared/utils/logger';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as os from 'os';
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import SSHConfig from 'ssh-config';
|
import SSHConfig from 'ssh-config';
|
||||||
|
|
||||||
|
|
@ -22,7 +22,7 @@ export class SshConfigParser {
|
||||||
private configPath: string;
|
private configPath: string;
|
||||||
|
|
||||||
constructor(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 pattern = match[1].trim();
|
||||||
const expandedPattern = pattern.replace(/^~/, os.homedir());
|
const expandedPattern = pattern.replace(/^~/, getHomeDir());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Handle glob-like patterns by checking if the path contains wildcards
|
// Handle glob-like patterns by checking if the path contains wildcards
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
* - Handle reconnection on errors
|
* - Handle reconnection on errors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { getHomeDir } from '@main/utils/pathDecoder';
|
||||||
import { createLogger } from '@shared/utils/logger';
|
import { createLogger } from '@shared/utils/logger';
|
||||||
import { execFile } from 'child_process';
|
import { execFile } from 'child_process';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
|
|
@ -267,7 +268,7 @@ export class SshConnectionManager extends EventEmitter {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'privateKey': {
|
case 'privateKey': {
|
||||||
const keyPath = config.privateKeyPath ?? path.join(os.homedir(), '.ssh', 'id_rsa');
|
const keyPath = config.privateKeyPath ?? path.join(getHomeDir(), '.ssh', 'id_rsa');
|
||||||
try {
|
try {
|
||||||
const keyData = await fs.promises.readFile(keyPath, 'utf8');
|
const keyData = await fs.promises.readFile(keyPath, 'utf8');
|
||||||
connectConfig.privateKey = keyData;
|
connectConfig.privateKey = keyData;
|
||||||
|
|
@ -347,15 +348,15 @@ export class SshConnectionManager extends EventEmitter {
|
||||||
const knownPaths = [
|
const knownPaths = [
|
||||||
// 1Password SSH agent
|
// 1Password SSH agent
|
||||||
path.join(
|
path.join(
|
||||||
os.homedir(),
|
getHomeDir(),
|
||||||
'Library',
|
'Library',
|
||||||
'Group Containers',
|
'Group Containers',
|
||||||
'2BUA8C4S2C.com.1password',
|
'2BUA8C4S2C.com.1password',
|
||||||
'agent.sock'
|
'agent.sock'
|
||||||
),
|
),
|
||||||
path.join(os.homedir(), '.1password', 'agent.sock'),
|
path.join(getHomeDir(), '.1password', 'agent.sock'),
|
||||||
// Common user agent socket
|
// Common user agent socket
|
||||||
path.join(os.homedir(), '.ssh', 'agent.sock'),
|
path.join(getHomeDir(), '.ssh', 'agent.sock'),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Linux: add system paths
|
// Linux: add system paths
|
||||||
|
|
@ -395,8 +396,8 @@ export class SshConnectionManager extends EventEmitter {
|
||||||
// The config parser already told us there's an identity file.
|
// The config parser already told us there's an identity file.
|
||||||
// Try common identity file locations from config
|
// Try common identity file locations from config
|
||||||
const configKeyPaths = [
|
const configKeyPaths = [
|
||||||
path.join(os.homedir(), '.ssh', 'id_ed25519'),
|
path.join(getHomeDir(), '.ssh', 'id_ed25519'),
|
||||||
path.join(os.homedir(), '.ssh', 'id_rsa'),
|
path.join(getHomeDir(), '.ssh', 'id_rsa'),
|
||||||
];
|
];
|
||||||
for (const keyPath of configKeyPaths) {
|
for (const keyPath of configKeyPaths) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -417,9 +418,9 @@ export class SshConnectionManager extends EventEmitter {
|
||||||
|
|
||||||
// Try default key files
|
// Try default key files
|
||||||
const defaultKeys = [
|
const defaultKeys = [
|
||||||
path.join(os.homedir(), '.ssh', 'id_ed25519'),
|
path.join(getHomeDir(), '.ssh', 'id_ed25519'),
|
||||||
path.join(os.homedir(), '.ssh', 'id_rsa'),
|
path.join(getHomeDir(), '.ssh', 'id_rsa'),
|
||||||
path.join(os.homedir(), '.ssh', 'id_ecdsa'),
|
path.join(getHomeDir(), '.ssh', 'id_ecdsa'),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const keyPath of defaultKeys) {
|
for (const keyPath of defaultKeys) {
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,9 @@
|
||||||
* - Support tilde (~) expansion to home directory
|
* - 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 { countTokens } from '@main/utils/tokenizer';
|
||||||
import { createLogger } from '@shared/utils/logger';
|
import { createLogger } from '@shared/utils/logger';
|
||||||
import { app } from 'electron';
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { LocalFileSystemProvider } from '../infrastructure/LocalFileSystemProvider';
|
import { LocalFileSystemProvider } from '../infrastructure/LocalFileSystemProvider';
|
||||||
|
|
@ -48,7 +47,7 @@ export interface ClaudeMdReadResult {
|
||||||
*/
|
*/
|
||||||
function expandTilde(filePath: string): string {
|
function expandTilde(filePath: string): string {
|
||||||
if (filePath.startsWith('~')) {
|
if (filePath.startsWith('~')) {
|
||||||
const homeDir = app.getPath('home');
|
const homeDir = getHomeDir();
|
||||||
return path.join(homeDir, filePath.slice(1));
|
return path.join(homeDir, filePath.slice(1));
|
||||||
}
|
}
|
||||||
return filePath;
|
return filePath;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
|
import { getHomeDir } from '@main/utils/pathDecoder';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as os from 'os';
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
async function isExecutable(filePath: string): Promise<boolean> {
|
async function isExecutable(filePath: string): Promise<boolean> {
|
||||||
|
|
@ -61,7 +61,7 @@ function expandWindowsBinaryNames(binaryName: string): string[] {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function collectNvmCandidates(): Promise<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[];
|
let versions: string[];
|
||||||
try {
|
try {
|
||||||
versions = await fs.promises.readdir(nvmNodeRoot);
|
versions = await fs.promises.readdir(nvmNodeRoot);
|
||||||
|
|
@ -172,9 +172,9 @@ export class ClaudeBinaryResolver {
|
||||||
|
|
||||||
const candidateDirs: string[] = [
|
const candidateDirs: string[] = [
|
||||||
// Native binary installation path (claude install)
|
// Native binary installation path (claude install)
|
||||||
path.join(os.homedir(), '.local', 'bin'),
|
path.join(getHomeDir(), '.local', 'bin'),
|
||||||
path.join(os.homedir(), '.npm-global', 'bin'),
|
path.join(getHomeDir(), '.npm-global', 'bin'),
|
||||||
path.join(os.homedir(), '.npm', 'bin'),
|
path.join(getHomeDir(), '.npm', 'bin'),
|
||||||
process.platform === 'win32'
|
process.platform === 'win32'
|
||||||
? process.env.APPDATA
|
? process.env.APPDATA
|
||||||
? path.join(process.env.APPDATA, 'npm')
|
? path.join(process.env.APPDATA, 'npm')
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { getHomeDir } from '@main/utils/pathDecoder';
|
||||||
import { createLogger } from '@shared/utils/logger';
|
import { createLogger } from '@shared/utils/logger';
|
||||||
import { diffLines } from 'diff';
|
import { diffLines } from 'diff';
|
||||||
import { createReadStream } from 'fs';
|
import { createReadStream } from 'fs';
|
||||||
|
|
@ -257,8 +258,13 @@ export class FileContentResolver {
|
||||||
if (!backupFileName) continue;
|
if (!backupFileName) continue;
|
||||||
|
|
||||||
// Construct the file-history path
|
// Construct the file-history path
|
||||||
const homeDir = process.env.HOME || process.env.USERPROFILE || '';
|
const historyPath = path.join(
|
||||||
const historyPath = path.join(homeDir, '.claude', 'file-history', sessionId, backupFileName);
|
getHomeDir(),
|
||||||
|
'.claude',
|
||||||
|
'file-history',
|
||||||
|
sessionId,
|
||||||
|
backupFileName
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await access(historyPath);
|
await access(historyPath);
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import {
|
||||||
extractBaseDir,
|
extractBaseDir,
|
||||||
getAutoDetectedClaudeBasePath,
|
getAutoDetectedClaudeBasePath,
|
||||||
getClaudeBasePath,
|
getClaudeBasePath,
|
||||||
|
getHomeDir,
|
||||||
getProjectsBasePath,
|
getProjectsBasePath,
|
||||||
getTasksBasePath,
|
getTasksBasePath,
|
||||||
getTeamsBasePath,
|
getTeamsBasePath,
|
||||||
|
|
@ -2439,7 +2440,7 @@ export class TeamProvisioningService {
|
||||||
|
|
||||||
private async buildProvisioningEnv(): Promise<ProvisioningEnvResolution> {
|
private async buildProvisioningEnv(): Promise<ProvisioningEnvResolution> {
|
||||||
const shellEnv = await resolveInteractiveShellEnv();
|
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 user = shellEnv.USER?.trim() || process.env.USER?.trim() || os.userInfo().username;
|
||||||
const shell = shellEnv.SHELL?.trim() || process.env.SHELL?.trim() || '/bin/zsh';
|
const shell = shellEnv.SHELL?.trim() || process.env.SHELL?.trim() || '/bin/zsh';
|
||||||
const xdgConfigHome =
|
const xdgConfigHome =
|
||||||
|
|
|
||||||
|
|
@ -228,24 +228,24 @@ export const KanbanTaskCard = ({
|
||||||
#{task.id}
|
#{task.id}
|
||||||
</Badge>
|
</Badge>
|
||||||
{task.owner ? <MemberBadge name={task.owner} color={colorMap.get(task.owner)} /> : null}
|
{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 && (
|
{!compact && (
|
||||||
<h5 className="min-w-0 truncate text-sm font-medium text-[var(--color-text)]">
|
<h5 className="min-w-0 truncate text-sm font-medium text-[var(--color-text)]">
|
||||||
{task.subject}
|
{task.subject}
|
||||||
</h5>
|
</h5>
|
||||||
)}
|
)}
|
||||||
</div>
|
</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 && (
|
{compact && (
|
||||||
<h5 className="mt-1 truncate text-sm font-medium text-[var(--color-text)]">
|
<h5 className="mt-1 truncate text-sm font-medium text-[var(--color-text)]">
|
||||||
{task.subject}
|
{task.subject}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import * as os from 'os';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
import { setClaudeBasePathOverride } from '../../../src/main/utils/pathDecoder';
|
import { getHomeDir, setClaudeBasePathOverride } from '../../../src/main/utils/pathDecoder';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
isPathWithinAllowedDirectories,
|
isPathWithinAllowedDirectories,
|
||||||
|
|
@ -17,7 +17,7 @@ import {
|
||||||
} from '../../../src/main/utils/pathValidation';
|
} from '../../../src/main/utils/pathValidation';
|
||||||
|
|
||||||
describe('pathValidation', () => {
|
describe('pathValidation', () => {
|
||||||
const homeDir = os.homedir();
|
const homeDir = getHomeDir();
|
||||||
const claudeDir = path.join(homeDir, '.claude');
|
const claudeDir = path.join(homeDir, '.claude');
|
||||||
const testProjectPath = path.resolve('/home/user/my-project');
|
const testProjectPath = path.resolve('/home/user/my-project');
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue