fix(platform): verify full Xcode installation for xed command

The xed command requires full Xcode.app, not just Command Line Tools.
This fix adds validation to ensure Xcode is properly configured before
offering it as an editor option.

Changes:
- Added isXcodeFullyInstalled() to check xcode-select points to Xcode.app
- Added helpful warning when Xcode is installed but xcode-select points to CLT
- Users see clear instructions on how to fix the configuration

Fixes issue where xed would fail with "tool 'xed' requires Xcode" error
when only Command Line Tools are configured via xcode-select.
This commit is contained in:
Shirone
2026-01-11 19:04:39 +01:00
parent 01cf81a105
commit 1b9acb1395

View File

@@ -13,6 +13,7 @@ import { homedir } from 'os';
import { join } from 'path';
import { access } from 'fs/promises';
import type { EditorInfo } from '@automaker/types';
import { createLogger } from '@automaker/utils';
const execFileAsync = promisify(execFile);
@@ -20,6 +21,9 @@ const execFileAsync = promisify(execFile);
const isWindows = process.platform === 'win32';
const isMac = process.platform === 'darwin';
// Logger for editor detection
const logger = createLogger('editor');
// Cache with TTL for editor detection
let cachedEditors: EditorInfo[] | null = null;
let cacheTimestamp: number = 0;
@@ -118,6 +122,43 @@ const SUPPORTED_EDITORS: EditorDefinition[] = [
},
];
/**
* Check if Xcode is fully installed (not just Command Line Tools)
* xed command requires full Xcode.app, not just CLT
*/
async function isXcodeFullyInstalled(): Promise<boolean> {
if (!isMac) return false;
try {
// Check if xcode-select points to full Xcode, not just CommandLineTools
const { stdout } = await execFileAsync('xcode-select', ['-p']);
const devPath = stdout.trim();
// Full Xcode path: /Applications/Xcode.app/Contents/Developer
// Command Line Tools: /Library/Developer/CommandLineTools
const isPointingToXcode = devPath.includes('Xcode.app');
if (!isPointingToXcode && devPath.includes('CommandLineTools')) {
// Check if xed command exists (indicates CLT are installed)
const xedExists = await commandExists('xed');
// Check if Xcode.app actually exists
const xcodeAppPath = await findMacApp('Xcode');
if (xedExists && xcodeAppPath) {
logger.warn(
'Xcode is installed but xcode-select is pointing to Command Line Tools. ' +
'To use Xcode as an editor, run: sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer'
);
}
}
return isPointingToXcode;
} catch {
return false;
}
}
/**
* Try to find an editor - checks CLI first, then macOS app bundle
* Returns EditorInfo if found, null otherwise
@@ -128,6 +169,13 @@ async function findEditor(definition: EditorDefinition): Promise<EditorInfo | nu
return null;
}
// Special handling for Xcode: verify full installation, not just xed command
if (definition.name === 'Xcode') {
if (!(await isXcodeFullyInstalled())) {
return null;
}
}
// Try CLI command first (works on all platforms)
const cliCandidates = [definition.cliCommand, ...(definition.cliAliases ?? [])];
for (const cliCommand of cliCandidates) {