| import { mkdir, open, unlink } from 'fs/promises' |
| import { join } from 'path' |
| import type { SettingSource } from 'src/utils/settings/constants.js' |
| import { getManagedFilePath } from 'src/utils/settings/managedPath.js' |
| import type { AgentMemoryScope } from '../../tools/AgentTool/agentMemory.js' |
| import { |
| type AgentDefinition, |
| isBuiltInAgent, |
| isPluginAgent, |
| } from '../../tools/AgentTool/loadAgentsDir.js' |
| import { getCwd } from '../../utils/cwd.js' |
| import type { EffortValue } from '../../utils/effort.js' |
| import { getClaudeConfigHomeDir } from '../../utils/envUtils.js' |
| import { getErrnoCode } from '../../utils/errors.js' |
| import { AGENT_PATHS } from './types.js' |
|
|
| |
| |
| |
| export function formatAgentAsMarkdown( |
| agentType: string, |
| whenToUse: string, |
| tools: string[] | undefined, |
| systemPrompt: string, |
| color?: string, |
| model?: string, |
| memory?: AgentMemoryScope, |
| effort?: EffortValue, |
| ): string { |
| |
| |
| |
| |
| const escapedWhenToUse = whenToUse |
| .replace(/\\/g, '\\\\') |
| .replace(/"/g, '\\"') |
| .replace(/\n/g, '\\\\n') |
|
|
| |
| const isAllTools = |
| tools === undefined || (tools.length === 1 && tools[0] === '*') |
| const toolsLine = isAllTools ? '' : `\ntools: ${tools.join(', ')}` |
| const modelLine = model ? `\nmodel: ${model}` : '' |
| const effortLine = effort !== undefined ? `\neffort: ${effort}` : '' |
| const colorLine = color ? `\ncolor: ${color}` : '' |
| const memoryLine = memory ? `\nmemory: ${memory}` : '' |
|
|
| return `--- |
| name: ${agentType} |
| description: "${escapedWhenToUse}"${toolsLine}${modelLine}${effortLine}${colorLine}${memoryLine} |
| --- |
| |
| ${systemPrompt} |
| ` |
| } |
|
|
| |
| |
| |
| function getAgentDirectoryPath(location: SettingSource): string { |
| switch (location) { |
| case 'flagSettings': |
| throw new Error(`Cannot get directory path for ${location} agents`) |
| case 'userSettings': |
| return join(getClaudeConfigHomeDir(), AGENT_PATHS.AGENTS_DIR) |
| case 'projectSettings': |
| return join(getCwd(), AGENT_PATHS.FOLDER_NAME, AGENT_PATHS.AGENTS_DIR) |
| case 'policySettings': |
| return join( |
| getManagedFilePath(), |
| AGENT_PATHS.FOLDER_NAME, |
| AGENT_PATHS.AGENTS_DIR, |
| ) |
| case 'localSettings': |
| return join(getCwd(), AGENT_PATHS.FOLDER_NAME, AGENT_PATHS.AGENTS_DIR) |
| } |
| } |
|
|
| function getRelativeAgentDirectoryPath(location: SettingSource): string { |
| switch (location) { |
| case 'projectSettings': |
| return join('.', AGENT_PATHS.FOLDER_NAME, AGENT_PATHS.AGENTS_DIR) |
| default: |
| return getAgentDirectoryPath(location) |
| } |
| } |
|
|
| |
| |
| |
| |
| export function getNewAgentFilePath(agent: { |
| source: SettingSource |
| agentType: string |
| }): string { |
| const dirPath = getAgentDirectoryPath(agent.source) |
| return join(dirPath, `${agent.agentType}.md`) |
| } |
|
|
| |
| |
| |
| |
| export function getActualAgentFilePath(agent: AgentDefinition): string { |
| if (agent.source === 'built-in') { |
| return 'Built-in' |
| } |
| if (agent.source === 'plugin') { |
| throw new Error('Cannot get file path for plugin agents') |
| } |
|
|
| const dirPath = getAgentDirectoryPath(agent.source) |
| const filename = agent.filename || agent.agentType |
| return join(dirPath, `${filename}.md`) |
| } |
|
|
| |
| |
| |
| |
| export function getNewRelativeAgentFilePath(agent: { |
| source: SettingSource | 'built-in' |
| agentType: string |
| }): string { |
| if (agent.source === 'built-in') { |
| return 'Built-in' |
| } |
| const dirPath = getRelativeAgentDirectoryPath(agent.source) |
| return join(dirPath, `${agent.agentType}.md`) |
| } |
|
|
| |
| |
| |
| export function getActualRelativeAgentFilePath(agent: AgentDefinition): string { |
| if (isBuiltInAgent(agent)) { |
| return 'Built-in' |
| } |
| if (isPluginAgent(agent)) { |
| return `Plugin: ${agent.plugin || 'Unknown'}` |
| } |
| if (agent.source === 'flagSettings') { |
| return 'CLI argument' |
| } |
|
|
| const dirPath = getRelativeAgentDirectoryPath(agent.source) |
| const filename = agent.filename || agent.agentType |
| return join(dirPath, `${filename}.md`) |
| } |
|
|
| |
| |
| |
| async function ensureAgentDirectoryExists( |
| source: SettingSource, |
| ): Promise<string> { |
| const dirPath = getAgentDirectoryPath(source) |
| await mkdir(dirPath, { recursive: true }) |
| return dirPath |
| } |
|
|
| |
| |
| |
| |
| export async function saveAgentToFile( |
| source: SettingSource | 'built-in', |
| agentType: string, |
| whenToUse: string, |
| tools: string[] | undefined, |
| systemPrompt: string, |
| checkExists = true, |
| color?: string, |
| model?: string, |
| memory?: AgentMemoryScope, |
| effort?: EffortValue, |
| ): Promise<void> { |
| if (source === 'built-in') { |
| throw new Error('Cannot save built-in agents') |
| } |
|
|
| await ensureAgentDirectoryExists(source) |
| const filePath = getNewAgentFilePath({ source, agentType }) |
|
|
| const content = formatAgentAsMarkdown( |
| agentType, |
| whenToUse, |
| tools, |
| systemPrompt, |
| color, |
| model, |
| memory, |
| effort, |
| ) |
| try { |
| await writeFileAndFlush(filePath, content, checkExists ? 'wx' : 'w') |
| } catch (e: unknown) { |
| if (getErrnoCode(e) === 'EEXIST') { |
| throw new Error(`Agent file already exists: ${filePath}`) |
| } |
| throw e |
| } |
| } |
|
|
| |
| |
| |
| export async function updateAgentFile( |
| agent: AgentDefinition, |
| newWhenToUse: string, |
| newTools: string[] | undefined, |
| newSystemPrompt: string, |
| newColor?: string, |
| newModel?: string, |
| newMemory?: AgentMemoryScope, |
| newEffort?: EffortValue, |
| ): Promise<void> { |
| if (agent.source === 'built-in') { |
| throw new Error('Cannot update built-in agents') |
| } |
|
|
| const filePath = getActualAgentFilePath(agent) |
|
|
| const content = formatAgentAsMarkdown( |
| agent.agentType, |
| newWhenToUse, |
| newTools, |
| newSystemPrompt, |
| newColor, |
| newModel, |
| newMemory, |
| newEffort, |
| ) |
|
|
| await writeFileAndFlush(filePath, content) |
| } |
|
|
| |
| |
| |
| export async function deleteAgentFromFile( |
| agent: AgentDefinition, |
| ): Promise<void> { |
| if (agent.source === 'built-in') { |
| throw new Error('Cannot delete built-in agents') |
| } |
|
|
| const filePath = getActualAgentFilePath(agent) |
|
|
| try { |
| await unlink(filePath) |
| } catch (e: unknown) { |
| const code = getErrnoCode(e) |
| if (code !== 'ENOENT') { |
| throw e |
| } |
| } |
| } |
|
|
| async function writeFileAndFlush( |
| filePath: string, |
| content: string, |
| flag: 'w' | 'wx' = 'w', |
| ): Promise<void> { |
| const handle = await open(filePath, flag) |
| try { |
| await handle.writeFile(content, { encoding: 'utf-8' }) |
| await handle.datasync() |
| } finally { |
| await handle.close() |
| } |
| } |
|
|