From cbb83a98f1d735f7d3a1fb84dc800eefdf0ca9e7 Mon Sep 17 00:00:00 2001 From: ryohidaka <39184410+ryohidaka@users.noreply.github.com> Date: Mon, 27 Apr 2026 09:48:33 +0900 Subject: [PATCH 1/2] feat(cli): add flag to initialize a git repository --- docs/guide/create.md | 2 ++ .../command-create-help/snap.txt | 6 +++++ .../cli/snap-tests-global/new-check/snap.txt | 2 ++ packages/cli/src/create/bin.ts | 20 +++++++++++++++- packages/cli/src/utils/git.ts | 11 +++++++++ packages/cli/src/utils/prompts.ts | 24 +++++++++++++++++++ 6 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 packages/cli/src/utils/git.ts diff --git a/docs/guide/create.md b/docs/guide/create.md index 315922b01f..103d47e48d 100644 --- a/docs/guide/create.md +++ b/docs/guide/create.md @@ -45,6 +45,8 @@ Run `vp create --list` to see the built-in templates and the common shorthand te - `--directory ` writes the generated project into a specific target directory - `--agent ` creates agent instructions files during scaffolding - `--editor ` writes editor config files +- `--git` initialize a git repository +- `--no-git` skips git repository initialization - `--hooks` enables pre-commit hook setup - `--no-hooks` skips hook setup - `--no-interactive` runs without prompts 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 ab69160242..5ff8f11dbb 100644 --- a/packages/cli/snap-tests-global/command-create-help/snap.txt +++ b/packages/cli/snap-tests-global/command-create-help/snap.txt @@ -17,6 +17,8 @@ Options: --directory DIR Target directory for the generated project. --agent NAME Write coding agent instructions to AGENTS.md, CLAUDE.md, etc. --editor NAME Write editor config files for the specified editor. + --git Initialize a git repository with an initial commit + --no-git Skip git repository initialization --hooks Set up pre-commit hooks (default in non-interactive mode) --no-hooks Skip pre-commit hooks setup --package-manager NAME Use specified package manager (pnpm, npm, yarn, bun) @@ -78,6 +80,8 @@ Options: --directory DIR Target directory for the generated project. --agent NAME Write coding agent instructions to AGENTS.md, CLAUDE.md, etc. --editor NAME Write editor config files for the specified editor. + --git Initialize a git repository with an initial commit + --no-git Skip git repository initialization --hooks Set up pre-commit hooks (default in non-interactive mode) --no-hooks Skip pre-commit hooks setup --package-manager NAME Use specified package manager (pnpm, npm, yarn, bun) @@ -139,6 +143,8 @@ Options: --directory DIR Target directory for the generated project. --agent NAME Write coding agent instructions to AGENTS.md, CLAUDE.md, etc. --editor NAME Write editor config files for the specified editor. + --git Initialize a git repository with an initial commit + --no-git Skip git repository initialization --hooks Set up pre-commit hooks (default in non-interactive mode) --no-hooks Skip pre-commit hooks setup --package-manager NAME Use specified package manager (pnpm, npm, yarn, bun) diff --git a/packages/cli/snap-tests-global/new-check/snap.txt b/packages/cli/snap-tests-global/new-check/snap.txt index 4602d3ef40..8266887b4b 100644 --- a/packages/cli/snap-tests-global/new-check/snap.txt +++ b/packages/cli/snap-tests-global/new-check/snap.txt @@ -17,6 +17,8 @@ Options: --directory DIR Target directory for the generated project. --agent NAME Write coding agent instructions to AGENTS.md, CLAUDE.md, etc. --editor NAME Write editor config files for the specified editor. + --git Initialize a git repository with an initial commit + --no-git Skip git repository initialization --hooks Set up pre-commit hooks (default in non-interactive mode) --no-hooks Skip pre-commit hooks setup --package-manager NAME Use specified package manager (pnpm, npm, yarn, bun) diff --git a/packages/cli/src/create/bin.ts b/packages/cli/src/create/bin.ts index 2897ec7fe0..00ad9e3050 100644 --- a/packages/cli/src/create/bin.ts +++ b/packages/cli/src/create/bin.ts @@ -29,6 +29,7 @@ import { writeAgentInstructions, } from '../utils/agent.ts'; import { detectExistingEditors, selectEditors, writeEditorConfigs } from '../utils/editor.ts'; +import { initGitRepository } from '../utils/git.ts'; import { renderCliDoc } from '../utils/help.ts'; import { displayRelative } from '../utils/path.ts'; import { @@ -36,6 +37,7 @@ import { defaultInteractive, downloadPackageManager, promptGitHooks, + promptGitInit, runViteFmt, runViteInstall, selectPackageManager, @@ -106,6 +108,8 @@ const helpMessage = renderCliDoc({ label: '--editor NAME', description: 'Write editor config files for the specified editor.', }, + { label: '--git', description: 'Initialize a git repository with an initial commit' }, + { label: '--no-git', description: 'Skip git repository initialization' }, { label: '--hooks', description: 'Set up pre-commit hooks (default in non-interactive mode)', @@ -235,11 +239,12 @@ function parseArgs() { verbose?: boolean; agent?: string | string[] | false; editor?: string; + git?: boolean; hooks?: boolean; 'package-manager'?: string; }>(viteArgs, { alias: { h: 'help' }, - boolean: ['help', 'list', 'all', 'interactive', 'hooks', 'verbose'], + boolean: ['help', 'list', 'all', 'interactive', 'hooks', 'verbose', 'git'], string: ['directory', 'agent', 'editor', 'package-manager'], default: { interactive: defaultInteractive() }, }); @@ -256,6 +261,7 @@ function parseArgs() { verbose: parsed.verbose || false, agent: parsed.agent, editor: parsed.editor, + git: parsed.git, hooks: parsed.hooks, packageManager: parsed['package-manager'], } as Options, @@ -747,6 +753,7 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h onCancel: () => cancelAndExit(), })); + const shouldSetupGit = await promptGitInit(options); if (!isMonorepo) { shouldSetupHooks = await promptGitHooks(options); } @@ -902,6 +909,10 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h workspaceInfo.rootDir = fullPath; updateCreateProgress('Integrating monorepo'); rewriteMonorepo(workspaceInfo, undefined, compactOutput); + if (shouldSetupGit) { + updateCreateProgress('Initializing git repository'); + await initGitRepository(fullPath); + } if (bundled?.monorepo) { // Wire `create.defaultTemplate: ''` into the new workspace's // vite.config.ts so a bare `vp create` from inside it opens the @@ -1137,6 +1148,9 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h await runViteFmt(workspaceInfo.rootDir, options.interactive, [projectDir], { silent: compactOutput, }); + if (shouldSetupGit) { + await initGitRepository(workspaceInfo.rootDir); + } } else { if (shouldMigrateLintFmtTools) { await installAndMigrate(fullPath); @@ -1148,6 +1162,10 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h addFrameworkShim(fullPath, framework); } } + if (shouldSetupGit) { + updateCreateProgress('Initializing git repository'); + await initGitRepository(fullPath); + } if (shouldSetupHooks) { installGitHooks(fullPath, compactOutput); } diff --git a/packages/cli/src/utils/git.ts b/packages/cli/src/utils/git.ts new file mode 100644 index 0000000000..fa16710397 --- /dev/null +++ b/packages/cli/src/utils/git.ts @@ -0,0 +1,11 @@ +import { runCommandSilently } from './command.ts'; + +export async function initGitRepository(cwd: string): Promise { + const result = await runCommandSilently({ + command: 'git', + args: ['init'], + cwd, + envs: process.env, + }); + return result.exitCode === 0; +} diff --git a/packages/cli/src/utils/prompts.ts b/packages/cli/src/utils/prompts.ts index 7f882b8ff8..9d14853d5a 100644 --- a/packages/cli/src/utils/prompts.ts +++ b/packages/cli/src/utils/prompts.ts @@ -215,6 +215,30 @@ export async function promptGitHooks(options: { return true; // non-interactive default } +export async function promptGitInit(options: { + git?: boolean; + interactive: boolean; +}): Promise { + if (options.git === false) { + return false; + } + if (options.git === true) { + return true; + } + if (options.interactive) { + const selected = await prompts.confirm({ + message: 'Initialize a git repository with an initial commit?', + initialValue: false, + }); + if (prompts.isCancel(selected)) { + cancelAndExit(); + return false; + } + return selected; + } + return false; // non-interactive default +} + export function defaultInteractive() { // If CI environment, use non-interactive mode by default return !process.env.CI && process.stdin.isTTY; From 9512eeb750e648e4bcc23841c980773e59e71fc5 Mon Sep 17 00:00:00 2001 From: ryohidaka <39184410+ryohidaka@users.noreply.github.com> Date: Mon, 27 Apr 2026 09:54:57 +0900 Subject: [PATCH 2/2] feat(cli): create initial commit after scaffold completes --- packages/cli/src/create/bin.ts | 12 +++++++++++- packages/cli/src/utils/git.ts | 16 ++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/create/bin.ts b/packages/cli/src/create/bin.ts index 00ad9e3050..bacf244639 100644 --- a/packages/cli/src/create/bin.ts +++ b/packages/cli/src/create/bin.ts @@ -29,7 +29,7 @@ import { writeAgentInstructions, } from '../utils/agent.ts'; import { detectExistingEditors, selectEditors, writeEditorConfigs } from '../utils/editor.ts'; -import { initGitRepository } from '../utils/git.ts'; +import { createInitialCommit, initGitRepository } from '../utils/git.ts'; import { renderCliDoc } from '../utils/help.ts'; import { displayRelative } from '../utils/path.ts'; import { @@ -933,6 +933,10 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h }); updateCreateProgress('Formatting code'); await runViteFmt(fullPath, options.interactive, undefined, { silent: compactOutput }); + if (shouldSetupGit) { + updateCreateProgress('Creating initial commit'); + await createInitialCommit(fullPath); + } clearCreateProgress(); showCreateSummary({ description: describeScaffold(selectedTemplateName, selectedTemplateArgs), @@ -1149,7 +1153,9 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h silent: compactOutput, }); if (shouldSetupGit) { + updateCreateProgress('Creating initial commit'); await initGitRepository(workspaceInfo.rootDir); + await createInitialCommit(workspaceInfo.rootDir); } } else { if (shouldMigrateLintFmtTools) { @@ -1177,6 +1183,10 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h }); updateCreateProgress('Formatting code'); await runViteFmt(fullPath, options.interactive, undefined, { silent: compactOutput }); + if (shouldSetupGit) { + updateCreateProgress('Creating initial commit'); + await createInitialCommit(fullPath); + } } clearCreateProgress(); diff --git a/packages/cli/src/utils/git.ts b/packages/cli/src/utils/git.ts index fa16710397..d58de4c090 100644 --- a/packages/cli/src/utils/git.ts +++ b/packages/cli/src/utils/git.ts @@ -9,3 +9,19 @@ export async function initGitRepository(cwd: string): Promise { }); return result.exitCode === 0; } + +export async function createInitialCommit(cwd: string): Promise { + await runCommandSilently({ + command: 'git', + args: ['add', '-A'], + cwd, + envs: process.env, + }); + const result = await runCommandSilently({ + command: 'git', + args: ['commit', '-m', 'Initial commit from Vite+'], + cwd, + envs: process.env, + }); + return result.exitCode === 0; +}