diff --git a/packages/cli/snap-tests-global/command-create-help/snap.txt b/packages/cli/snap-tests-global/command-create-help/snap.txt index b3275ff3a5..1a361bfa39 100644 --- a/packages/cli/snap-tests-global/command-create-help/snap.txt +++ b/packages/cli/snap-tests-global/command-create-help/snap.txt @@ -14,7 +14,7 @@ Arguments: Options: --directory DIR Target directory for the generated project. - --agent NAME Create an agent instructions file for the specified agent. + --agent NAME Write coding agent instructions to AGENTS.md, CLAUDE.md, etc. --editor NAME Write editor config files for the specified editor. --hooks Set up pre-commit hooks (default in non-interactive mode) --no-hooks Skip pre-commit hooks setup @@ -70,7 +70,7 @@ Arguments: Options: --directory DIR Target directory for the generated project. - --agent NAME Create an agent instructions file for the specified agent. + --agent NAME Write coding agent instructions to AGENTS.md, CLAUDE.md, etc. --editor NAME Write editor config files for the specified editor. --hooks Set up pre-commit hooks (default in non-interactive mode) --no-hooks Skip pre-commit hooks setup @@ -126,7 +126,7 @@ Arguments: Options: --directory DIR Target directory for the generated project. - --agent NAME Create an agent instructions file for the specified agent. + --agent NAME Write coding agent instructions to AGENTS.md, CLAUDE.md, etc. --editor NAME Write editor config files for the specified editor. --hooks Set up pre-commit hooks (default in non-interactive mode) --no-hooks Skip pre-commit hooks setup diff --git a/packages/cli/snap-tests-global/migration-check/snap.txt b/packages/cli/snap-tests-global/migration-check/snap.txt index 89ce2f9db6..9710209153 100644 --- a/packages/cli/snap-tests-global/migration-check/snap.txt +++ b/packages/cli/snap-tests-global/migration-check/snap.txt @@ -9,8 +9,8 @@ Arguments: PATH Target directory to migrate (default: current directory) Options: - --agent NAME Write agent instructions file into the project (e.g. chatgpt, claude, opencode). - --no-agent Skip writing agent instructions file + --agent NAME Write coding agent instructions to AGENTS.md, CLAUDE.md, etc. + --no-agent Skip writing coding agent instructions --editor NAME Write editor config files into the project. --no-editor Skip writing editor config files --hooks Set up pre-commit hooks (default in non-interactive mode) diff --git a/packages/cli/snap-tests-global/migration-lintstagedrc-json/snap.txt b/packages/cli/snap-tests-global/migration-lintstagedrc-json/snap.txt index 58eaf6caf2..62c8b3c729 100644 --- a/packages/cli/snap-tests-global/migration-lintstagedrc-json/snap.txt +++ b/packages/cli/snap-tests-global/migration-lintstagedrc-json/snap.txt @@ -9,8 +9,8 @@ Arguments: PATH Target directory to migrate (default: current directory) Options: - --agent NAME Write agent instructions file into the project (e.g. chatgpt, claude, opencode). - --no-agent Skip writing agent instructions file + --agent NAME Write coding agent instructions to AGENTS.md, CLAUDE.md, etc. + --no-agent Skip writing coding agent instructions --editor NAME Write editor config files into the project. --no-editor Skip writing editor config files --hooks Set up pre-commit hooks (default in non-interactive mode) diff --git a/packages/cli/snap-tests-global/new-check/snap.txt b/packages/cli/snap-tests-global/new-check/snap.txt index 7948819fa7..2037e5a0d1 100644 --- a/packages/cli/snap-tests-global/new-check/snap.txt +++ b/packages/cli/snap-tests-global/new-check/snap.txt @@ -14,7 +14,7 @@ Arguments: Options: --directory DIR Target directory for the generated project. - --agent NAME Create an agent instructions file for the specified agent. + --agent NAME Write coding agent instructions to AGENTS.md, CLAUDE.md, etc. --editor NAME Write editor config files for the specified editor. --hooks Set up pre-commit hooks (default in non-interactive mode) --no-hooks Skip pre-commit hooks setup diff --git a/packages/cli/src/create/bin.ts b/packages/cli/src/create/bin.ts index 9560b1a7af..028ea7c3b4 100644 --- a/packages/cli/src/create/bin.ts +++ b/packages/cli/src/create/bin.ts @@ -80,7 +80,7 @@ const helpMessage = renderCliDoc({ { label: '--directory DIR', description: 'Target directory for the generated project.' }, { label: '--agent NAME', - description: 'Create an agent instructions file for the specified agent.', + description: 'Write coding agent instructions to AGENTS.md, CLAUDE.md, etc.', }, { label: '--editor NAME', diff --git a/packages/cli/src/migration/bin.ts b/packages/cli/src/migration/bin.ts index c396639038..f0fd7b4f5e 100644 --- a/packages/cli/src/migration/bin.ts +++ b/packages/cli/src/migration/bin.ts @@ -224,10 +224,9 @@ const helpMessage = renderCliDoc({ rows: [ { label: '--agent NAME', - description: - 'Write agent instructions file into the project (e.g. chatgpt, claude, opencode).', + description: 'Write coding agent instructions to AGENTS.md, CLAUDE.md, etc.', }, - { label: '--no-agent', description: 'Skip writing agent instructions file' }, + { label: '--no-agent', description: 'Skip writing coding agent instructions' }, { label: '--editor NAME', description: 'Write editor config files into the project.', diff --git a/packages/cli/src/utils/__tests__/agent.spec.ts b/packages/cli/src/utils/__tests__/agent.spec.ts index 6e84ed7a50..51b0ee20b8 100644 --- a/packages/cli/src/utils/__tests__/agent.spec.ts +++ b/packages/cli/src/utils/__tests__/agent.spec.ts @@ -11,6 +11,7 @@ import { hasExistingAgentInstructions, replaceMarkedAgentInstructionsSection, resolveAgentTargetPaths, + selectAgentTargetPaths, writeAgentInstructions, } from '../agent.js'; import { pkgRoot } from '../path.js'; @@ -252,13 +253,19 @@ async function createProjectDir() { } describe('resolveAgentTargetPaths', () => { - it('resolves comma-separated agent names and deduplicates target paths', () => { + it('resolves legacy agent names and deduplicates target paths', () => { expect(resolveAgentTargetPaths('claude,amp,opencode,chatgpt')).toEqual([ 'CLAUDE.md', 'AGENTS.md', ]); }); + it('resolves file names directly', () => { + expect( + resolveAgentTargetPaths(['AGENTS.md', 'CLAUDE.md', '.github/copilot-instructions.md']), + ).toEqual(['AGENTS.md', 'CLAUDE.md', '.github/copilot-instructions.md']); + }); + it('resolves repeated --agent values and trims whitespace', () => { expect(resolveAgentTargetPaths([' claude ', ' amp, opencode ', 'codex'])).toEqual([ 'CLAUDE.md', @@ -272,6 +279,40 @@ describe('resolveAgentTargetPaths', () => { }); }); +describe('selectAgentTargetPaths', () => { + it('prompts with file-based targets and agent hints', async () => { + const multiselectSpy = vi.spyOn(prompts, 'multiselect').mockResolvedValue(['agents', 'claude']); + + await expect( + selectAgentTargetPaths({ + interactive: true, + onCancel: vi.fn(), + }), + ).resolves.toEqual(['AGENTS.md', 'CLAUDE.md']); + + expect(multiselectSpy).toHaveBeenCalledWith( + expect.objectContaining({ + message: expect.stringContaining( + 'Which coding agent instruction files should Vite+ create?', + ), + initialValues: ['agents'], + options: expect.arrayContaining([ + expect.objectContaining({ + label: 'AGENTS.md', + value: 'agents', + hint: expect.stringContaining('Codex'), + }), + expect.objectContaining({ + label: 'CLAUDE.md', + value: 'claude', + hint: 'Claude Code', + }), + ]), + }), + ); + }); +}); + describe('detectExistingAgentTargetPath', () => { it('detects all existing regular agent files', async () => { const dir = await createProjectDir(); diff --git a/packages/cli/src/utils/agent.ts b/packages/cli/src/utils/agent.ts index 53a0b893e8..d5aa6853a5 100644 --- a/packages/cli/src/utils/agent.ts +++ b/packages/cli/src/utils/agent.ts @@ -164,37 +164,75 @@ export function detectAgents(root: string): AgentConfig[] { // --- Backward-compatible exports --- -const AGENT_ALIASES: Record = { - chatgpt: 'chatgpt-codex', - codex: 'chatgpt-codex', -}; - export const AGENTS = [ - { id: 'chatgpt-codex', label: 'ChatGPT (Codex)', targetPath: 'AGENTS.md' }, - { id: 'claude', label: 'Claude Code', targetPath: 'CLAUDE.md' }, - { id: 'gemini', label: 'Gemini CLI', targetPath: 'GEMINI.md' }, + { + id: 'agents', + label: 'AGENTS.md', + targetPath: 'AGENTS.md', + hint: 'Codex, Amp, OpenCode, and similar agents', + aliases: [ + 'agents.md', + 'chatgpt', + 'chatgpt-codex', + 'codex', + 'amp', + 'kilo', + 'kilo-code', + 'kiro', + 'kiro-cli', + 'opencode', + 'other', + ], + }, + { + id: 'claude', + label: 'CLAUDE.md', + targetPath: 'CLAUDE.md', + hint: 'Claude Code', + aliases: ['claude.md', 'claude-code'], + }, + { + id: 'gemini', + label: 'GEMINI.md', + targetPath: 'GEMINI.md', + hint: 'Gemini CLI', + aliases: ['gemini.md', 'gemini-cli'], + }, { id: 'copilot', - label: 'GitHub Copilot', + label: '.github/copilot-instructions.md', targetPath: '.github/copilot-instructions.md', + hint: 'GitHub Copilot', + aliases: ['github-copilot', 'copilot-instructions.md'], + }, + { + id: 'cursor', + label: '.cursor/rules/viteplus.mdc', + targetPath: '.cursor/rules/viteplus.mdc', + hint: 'Cursor', + aliases: ['viteplus.mdc'], }, - { id: 'cursor', label: 'Cursor', targetPath: '.cursor/rules/viteplus.mdc' }, { id: 'jetbrains', - label: 'JetBrains AI Assistant', + label: '.aiassistant/rules/viteplus.md', targetPath: '.aiassistant/rules/viteplus.md', + hint: 'JetBrains AI Assistant', + aliases: ['jetbrains', 'jetbrains-ai-assistant', 'aiassistant', 'viteplus.md'], }, - { id: 'amp', label: 'Amp', targetPath: 'AGENTS.md' }, - { id: 'kiro', label: 'Kiro', targetPath: 'AGENTS.md' }, - { id: 'opencode', label: 'OpenCode', targetPath: 'AGENTS.md' }, - { id: 'other', label: 'Other', targetPath: 'AGENTS.md' }, ] as const; type AgentSelection = string | string[] | false; +const AGENT_DEFAULT_ID = 'agents'; const AGENT_STANDARD_PATH = 'AGENTS.md'; const AGENT_INSTRUCTIONS_START_MARKER = ''; const AGENT_INSTRUCTIONS_END_MARKER = ''; +const AGENT_ALIASES = Object.fromEntries( + AGENTS.flatMap((option) => + (option.aliases ?? []).map((alias) => [normalizeAgentName(alias), option.id]), + ), +) as Record; + export async function selectAgentTargetPaths({ interactive, agent, @@ -211,18 +249,13 @@ export async function selectAgentTargetPaths({ if (interactive && !agent) { const selectedAgents = await prompts.multiselect({ - message: - 'Which agents are you using?\n ' + - styleText( - 'gray', - 'Writes an instruction file for each selected agent to help it understand `vp` commands and the project workflow.', - ), + message: 'Which coding agent instruction files should Vite+ create?', options: AGENTS.map((option) => ({ label: option.label, value: option.id, - hint: option.targetPath, + hint: option.hint, })), - initialValues: ['chatgpt-codex'], + initialValues: [AGENT_DEFAULT_ID], required: false, }); @@ -237,7 +270,7 @@ export async function selectAgentTargetPaths({ return resolveAgentTargetPaths(selectedAgents); } - return resolveAgentTargetPaths(agent ?? 'other'); + return resolveAgentTargetPaths(agent ?? AGENT_DEFAULT_ID); } export async function selectAgentTargetPath({ @@ -359,9 +392,12 @@ function resolveSingleAgentTargetPath(agent: string) { const resolved = alias ? normalizeAgentName(alias) : normalized; const match = AGENTS.find( (option) => - normalizeAgentName(option.id) === resolved || normalizeAgentName(option.label) === resolved, + normalizeAgentName(option.id) === resolved || + normalizeAgentName(option.label) === resolved || + normalizeAgentName(option.targetPath) === resolved || + option.aliases?.some((candidate) => normalizeAgentName(candidate) === resolved), ); - return match?.targetPath ?? AGENTS[AGENTS.length - 1].targetPath; + return match?.targetPath ?? AGENT_STANDARD_PATH; } export interface AgentConflictInfo {