| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import * as fs from 'fs/promises'; |
| import * as path from 'path'; |
| import * as os from 'os'; |
| import { createLogger } from '@automaker/utils'; |
| import type { |
| CursorCliConfigFile, |
| CursorCliPermissions, |
| CursorPermissionProfile, |
| } from '@automaker/types'; |
| import { |
| CURSOR_STRICT_PROFILE, |
| CURSOR_DEVELOPMENT_PROFILE, |
| CURSOR_PERMISSION_PROFILES, |
| } from '@automaker/types'; |
|
|
| const logger = createLogger('CursorConfigService'); |
|
|
| |
| |
| |
| export function getGlobalConfigPath(): string { |
| |
| |
| |
| const xdgConfig = process.env.XDG_CONFIG_HOME; |
| const cursorConfigDir = process.env.CURSOR_CONFIG_DIR; |
|
|
| if (cursorConfigDir) { |
| return path.join(cursorConfigDir, 'cli-config.json'); |
| } |
|
|
| if (process.platform === 'linux' && xdgConfig) { |
| return path.join(xdgConfig, 'cursor', 'cli-config.json'); |
| } |
|
|
| return path.join(os.homedir(), '.cursor', 'cli-config.json'); |
| } |
|
|
| |
| |
| |
| export function getProjectConfigPath(projectPath: string): string { |
| return path.join(projectPath, '.cursor', 'cli.json'); |
| } |
|
|
| |
| |
| |
| export async function readGlobalConfig(): Promise<CursorCliConfigFile | null> { |
| const configPath = getGlobalConfigPath(); |
|
|
| try { |
| const content = await fs.readFile(configPath, 'utf-8'); |
| const config = JSON.parse(content) as CursorCliConfigFile; |
| logger.debug('Read global Cursor config from:', configPath); |
| return config; |
| } catch (error) { |
| if ((error as NodeJS.ErrnoException).code === 'ENOENT') { |
| logger.debug('Global Cursor config not found at:', configPath); |
| return null; |
| } |
| logger.error('Failed to read global Cursor config:', error); |
| throw error; |
| } |
| } |
|
|
| |
| |
| |
| export async function writeGlobalConfig(config: CursorCliConfigFile): Promise<void> { |
| const configPath = getGlobalConfigPath(); |
| const configDir = path.dirname(configPath); |
|
|
| |
| await fs.mkdir(configDir, { recursive: true }); |
|
|
| |
| await fs.writeFile(configPath, JSON.stringify(config, null, 2)); |
| logger.info('Wrote global Cursor config to:', configPath); |
| } |
|
|
| |
| |
| |
| export async function readProjectConfig(projectPath: string): Promise<CursorCliConfigFile | null> { |
| const configPath = getProjectConfigPath(projectPath); |
|
|
| try { |
| const content = await fs.readFile(configPath, 'utf-8'); |
| const config = JSON.parse(content) as CursorCliConfigFile; |
| logger.debug('Read project Cursor config from:', configPath); |
| return config; |
| } catch (error) { |
| if ((error as NodeJS.ErrnoException).code === 'ENOENT') { |
| logger.debug('Project Cursor config not found at:', configPath); |
| return null; |
| } |
| logger.error('Failed to read project Cursor config:', error); |
| throw error; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| export async function writeProjectConfig( |
| projectPath: string, |
| config: CursorCliConfigFile |
| ): Promise<void> { |
| const configPath = getProjectConfigPath(projectPath); |
| const configDir = path.dirname(configPath); |
|
|
| |
| await fs.mkdir(configDir, { recursive: true }); |
|
|
| |
| const projectConfig = { |
| permissions: config.permissions, |
| }; |
|
|
| await fs.writeFile(configPath, JSON.stringify(projectConfig, null, 2)); |
| logger.info('Wrote project Cursor config to:', configPath); |
| } |
|
|
| |
| |
| |
| export async function deleteProjectConfig(projectPath: string): Promise<void> { |
| const configPath = getProjectConfigPath(projectPath); |
|
|
| try { |
| await fs.unlink(configPath); |
| logger.info('Deleted project Cursor config:', configPath); |
| } catch (error) { |
| if ((error as NodeJS.ErrnoException).code !== 'ENOENT') { |
| throw error; |
| } |
| } |
| } |
|
|
| |
| |
| |
| |
| export async function getEffectivePermissions( |
| projectPath?: string |
| ): Promise<CursorCliPermissions | null> { |
| |
| if (projectPath) { |
| const projectConfig = await readProjectConfig(projectPath); |
| if (projectConfig?.permissions) { |
| return projectConfig.permissions; |
| } |
| } |
|
|
| |
| const globalConfig = await readGlobalConfig(); |
| return globalConfig?.permissions || null; |
| } |
|
|
| |
| |
| |
| export async function applyProfileToProject( |
| projectPath: string, |
| profileId: CursorPermissionProfile |
| ): Promise<void> { |
| const profile = CURSOR_PERMISSION_PROFILES.find((p) => p.id === profileId); |
|
|
| if (!profile) { |
| throw new Error(`Unknown permission profile: ${profileId}`); |
| } |
|
|
| await writeProjectConfig(projectPath, { |
| version: 1, |
| permissions: profile.permissions, |
| }); |
|
|
| logger.info(`Applied "${profile.name}" profile to project:`, projectPath); |
| } |
|
|
| |
| |
| |
| export async function applyProfileGlobally(profileId: CursorPermissionProfile): Promise<void> { |
| const profile = CURSOR_PERMISSION_PROFILES.find((p) => p.id === profileId); |
|
|
| if (!profile) { |
| throw new Error(`Unknown permission profile: ${profileId}`); |
| } |
|
|
| |
| const existingConfig = await readGlobalConfig(); |
|
|
| await writeGlobalConfig({ |
| version: 1, |
| ...existingConfig, |
| permissions: profile.permissions, |
| }); |
|
|
| logger.info(`Applied "${profile.name}" profile globally`); |
| } |
|
|
| |
| |
| |
| export function detectProfile( |
| permissions: CursorCliPermissions | null |
| ): CursorPermissionProfile | null { |
| if (!permissions) { |
| return null; |
| } |
|
|
| |
| for (const profile of CURSOR_PERMISSION_PROFILES) { |
| const allowMatch = |
| JSON.stringify(profile.permissions.allow.sort()) === JSON.stringify(permissions.allow.sort()); |
| const denyMatch = |
| JSON.stringify(profile.permissions.deny.sort()) === JSON.stringify(permissions.deny.sort()); |
|
|
| if (allowMatch && denyMatch) { |
| return profile.id; |
| } |
| } |
|
|
| return 'custom'; |
| } |
|
|
| |
| |
| |
| export function generateExampleConfig(profileId: CursorPermissionProfile = 'development'): string { |
| const profile = |
| CURSOR_PERMISSION_PROFILES.find((p) => p.id === profileId) || CURSOR_DEVELOPMENT_PROFILE; |
|
|
| const config: CursorCliConfigFile = { |
| version: 1, |
| permissions: profile.permissions, |
| }; |
|
|
| return JSON.stringify(config, null, 2); |
| } |
|
|
| |
| |
| |
| export async function hasProjectConfig(projectPath: string): Promise<boolean> { |
| const configPath = getProjectConfigPath(projectPath); |
|
|
| try { |
| await fs.access(configPath); |
| return true; |
| } catch { |
| return false; |
| } |
| } |
|
|
| |
| |
| |
| export function getAvailableProfiles() { |
| return CURSOR_PERMISSION_PROFILES; |
| } |
|
|
| |
| export { CURSOR_STRICT_PROFILE, CURSOR_DEVELOPMENT_PROFILE }; |
|
|