Improved entire enable flow on folders that are not git repos yet#978
Improved entire enable flow on folders that are not git repos yet#978
Conversation
When `entire enable` runs in a folder that isn't a git repository, prompt to `git init` one, then optionally create a matching GitHub repository via the gh CLI: pick owner (user or org), suggest a repo name from the folder, check availability, pick visibility, confirm initial commit message, and push. Falls back to local-only if gh is missing or unauthenticated. Flags (all optional; each suppresses its matching prompt): - `--init-repo` / `--no-init-repo`: accept or decline the git init prompt - `--repo-name`, `--repo-owner`, `--repo-visibility` - `--no-github`: local git init only - `--initial-commit-message` Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: b4cbdcf4b56c
The initial commit created by `entire enable` bootstrap could fail in two
common fresh-machine scenarios:
- No global `user.name`/`user.email` configured yet — git commit would
refuse with "please tell me who you are".
- `commit.gpgsign=true` inherited from a global config with no working
signer — git commit would fail trying to sign.
Fix both:
- New `ensureGitIdentity` runs before the initial commit: reuses identity
already set at any scope; otherwise sources name + email from
`gh api user` (falling back to `{id}+{login}@users.noreply.github.com`
when the email is private); otherwise prompts interactively or errors
non-interactively with a pointer to `git config --global`. Values are
written to the local repo config only; global state is never touched.
- The bootstrap commit always runs with `-c commit.gpgsign=false` so a
broken or unavailable signer does not break the first commit. The
user's global signing preference for subsequent commits is unchanged.
Includes real-git regression tests that isolate HOME/GIT_CONFIG_* to
prove the fix on a truly fresh environment.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: f6d5ccfe5742
Before: `entire enable --init-repo --no-init-repo` silently honored `--no-init-repo` and fell through to the legacy "Not a git repository" error. Automation could mistakenly pass both and never see a flag error. Now: cobra's MarkFlagsMutuallyExclusive surfaces a clear error at parse time. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 096f98a24b33
There was a problem hiding this comment.
Pull request overview
This PR extends entire enable to support bootstrapping a brand-new folder into a usable git repository (including an initial commit) and optionally creating/pushing to a matching GitHub repository via the gh CLI, instead of immediately failing when no git repo is present.
Changes:
- Add a GitHub bootstrap flow (
git init, optionalgh repo create, initial commit with identity +commit.gpgsign=falsehandling). - Add
entire enableflags to drive the bootstrap flow non-interactively (--init-repo,--no-github,--repo-*, etc.). - Add unit + regression-style tests covering slugification/validation, gh helpers, bootstrap paths, and fresh-machine git identity behavior.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
cmd/entire/cli/setup_github.go |
Implements the new bootstrap flow (git init + optional gh repo creation + initial commit + identity handling). |
cmd/entire/cli/setup_github_test.go |
Adds tests for repo name handling, gh helpers, bootstrap behavior, and real-git regressions. |
cmd/entire/cli/setup.go |
Hooks the bootstrap flow into entire enable and adds new bootstrap-related flags. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 7023b4e. Configure here.
The bootstrap ran `git add -A` + `git commit` before agent setup, so the initial commit only captured files the user already had in the folder. Everything `entire enable` writes afterwards — `.entire/settings.json`, `.entire/.gitignore`, `.claude/agents/*`, `.claude/settings.json`, agent hooks — landed as untracked files, which is a bad first impression and forces the user to stage and commit them separately. Split the bootstrap into two phases around agent setup: - `runGitHubBootstrapInit` runs before: `git init`, ensure git identity, gather owner/name/visibility via gh, resolve commit message. All prompts happen here so the main setup output isn't interleaved with input. - `runGitHubBootstrapFinalize` runs after: `git add -A` + `git commit` + `gh repo create --source=. --push`. By this point the `.entire/`, `.claude/`, and hook files written by setup are on disk, so they land in the initial commit. The finalize step runs via a deferred closure on a named return so every early-return path in `enable` (non-interactive `--agent`, already configured, etc.) still triggers it on success. On setup failure the finalize is skipped — the user can fix the issue and rerun; any partial state is just untracked files. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 14f3e6df5d51
`gh repo create --push` invoked the newly installed `pre-push` hook, which pushed `entire/checkpoints/v1` alongside the default branch. GitHub then picked `entire/checkpoints/v1` as the repository's default branch because it was one of the refs in the initial push. Split the step into `gh repo create` (no push) followed by an explicit `git push --no-verify -u origin HEAD`. `--no-verify` bypasses the pre-push hook for this one push; the checkpoint branch is skipped, which is safe because a fresh repo has no sessions to checkpoint yet. Subsequent pushes still go through the hook normally. Verified against a real GitHub repo: only `main` lands on the remote, and GitHub's default branch is `main`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: e207ae75dc18
Before diving into owner/name/visibility prompts, ask a simple "Create a matching repository on GitHub?" yes/no so users have an obvious path to local-only without needing to know the --no-github flag. The prompt is skipped when the user has already expressed intent via any of --repo-name / --repo-owner / --repo-visibility, and in non-interactive contexts the default stays "yes" to preserve the documented happy path for automation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 6f71edf427dc
Before this change, Ctrl+C at any bootstrap prompt after `git init` had already run surfaced the misleading "Not a git repository. Please run 'entire enable' from within a git repository." error. The bootstrap shared a single `errBootstrapDeclined` sentinel for both "user said no before init" and "user aborted a prompt after init". Split into two sentinels: - `errBootstrapDeclined` stays as "user declined before init" — setup.go keeps the legacy message. - `errBootstrapInterrupted` is new: used by every prompt that happens after `git init` (owner, name, visibility, commit message, identity, GitHub-confirm). setup.go now prints "Bootstrap cancelled. A local git repository has been initialized but setup didn't complete. Run `entire enable` again to continue." and returns a silent error. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 334deadad1ea
Replace the freeform commit-message input with a 3-option select:
> Commit with default message "Initial commit"
Customize message...
Skip — I'll commit manually later
If the user picks "Skip", we still create the GitHub repo (if
requested), but we don't run `git add`/`git commit`/`git push` and we
print the exact commands to finish up manually. This handles the case
where a user wants to review what setup wrote (e.g. adjust the
generated `.entire/` files, add a `.gitignore`) before their first
commit — previously the only escape was Ctrl+C, which aborted the
whole flow.
Also adds a `--skip-initial-commit` flag for non-interactive parity
with the other prompts. `--skip-initial-commit` and
`--initial-commit-message` are mutually exclusive.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: c2187640bce8
Address four UX issues with the bootstrap's console output:
1. Fix ordering — "Ready." and the "Note: Session checkpoints require
at least one commit" hint from the enable flow printed before the
bootstrap's commit + push, which was both misleading ("not ready
yet") and inaccurate ("about to commit right now"). A new
SuppressDoneMessage option lets the bootstrap flow tell the enable
flow to skip those; the bootstrap finalize prints its own summary
ending in "Done."
2. Quiet the initial push — `git push -q` silences the verbose
"Enumerating objects..." / "Compressing objects..." / "Writing
objects..." progress. Errors still come through.
3. Capture `gh repo create` output instead of streaming it. The tool's
own "✓ Created repository..." / "✓ Added remote..." lines
duplicated what we print ourselves; capturing keeps the output
single-voice. Stderr is surfaced on failure via ghRunnerErr.
4. Section headers — light "━ Setting up git repository" / "━ Enabling
Entire" / "━ Publishing to GitHub" / "━ Finalizing" separators
between the three phases, giving visual structure to what was a
wall of mixed output.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: f6504427d0bc
Addresses code-review feedback on #978: - Drop `RunInteractive` from `bootstrapRunner`, `execRunner`, and `fakeRunner`. Since `gh repo create` and `git push` both moved to captured-stdout calls (`RunInDir`) in the output-polish commit, nothing calls `RunInteractive` anymore. Removing it also eliminates the `execRunner.RunInteractive` issue where streams were pinned to `os.Stdin/Stdout/Stderr` rather than cobra's configured writers, and the `fakeRunner.RunInteractive` issue where unregistered calls returned nil silently. - Drop the unused `w io.Writer` parameter from `resolveVisibility` and `resolveCommitMessage`. The `_ = w` placeholder made it look intentional; it wasn't. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 53160af9e311
Addresses #978 review: ensureGitIdentity previously wrote both `user.name` and `user.email` to local config whenever either one was missing. A user with `user.name` set globally but no `user.email` would have their configured name silently replaced by the gh-derived value. Now we: - Read existing `user.name` and `user.email` independently. - Pass both through to resolveGitIdentity along with any gh-sourced fallbacks; per-field, use the existing value if set, else the gh value, else prompt (interactive) or error (non-interactive). - Write only the fields that weren't already configured. A partial global config is preserved exactly as-is. Also prompts only for the fields that are still missing after the gh/existing fallbacks, so a user who's missing just the email doesn't have to re-type their name. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: e2c9b7aae2ed
Addresses remaining #978 review feedback: - **Regex**: GitHub allows repo names starting with `.`, `-`, or `_` (`.github` being the canonical example). The previous regex `^[A-Za-z0-9][A-Za-z0-9._-]*$` rejected valid names when passed via `--repo-name` or entered interactively. Loosened to allow any of the permitted characters as the leading char; `validateRepoName` explicitly rejects `.` and `..` as those are path-reserved. - **Non-interactive dup output**: `confirmInitRepo` no longer prints "Not a git repository. Pass --init-repo..." to stdout when non-interactive. The caller (enable RunE) owns the single "Not a git repository" line on stderr, now extended with the `--init-repo` hint. - **Test flakiness**: `TestBootstrap_FreshMachine_NoIdentity_RealGit` previously tried to make `gh` unavailable by stomping on PATH, then restoring common dirs like `/opt/homebrew/bin` that commonly contain `gh`. Replaced with a `ghFailingRunner` wrapper that forces `gh` calls to fail while letting real `git` calls through, plus `GH_TOKEN`/`GITHUB_TOKEN` unsets as defense-in-depth. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 5b72a2385094

Summary
Lets
entire enablebootstrap a brand-new folder all the way to a working repo —git init, agent setup, initial commit, and a matching GitHub repository via theghCLI — instead of exiting with "Not a git repository".Flow
All prompts go through
NewAccessibleForm, soACCESSIBLE=1worksas expected.
Flow details worth calling out
we source
user.name/user.emailfromgh api user(falling backto the GitHub no-reply address when the user's email is private),
then prompt, then fail with a pointer to
git config --global.Values are written to the local repo config only. The initial commit
also runs with
-c commit.gpgsign=falseso a broken or absent signerdoesn't block the first commit.
would otherwise push
entire/checkpoints/v1alongsidemainon thefirst push, which GitHub then picks as the repo's default branch. The
initial push uses
git push -q --no-verify -u origin HEAD; onlymainlands on the remote and the checkpoint metadata branch ispushed normally on subsequent pushes.
git init, identity, gather GHchoices) runs before agent setup; phase 2 (
git add -A+ commit +gh repo create+ push) runs after, so the initial commit captures.entire/,.claude/, agent hooks, and every other file setupwrites.
ghmissing or unauthenticated → print a hint (brew install gh && gh auth login) and fall through to local-only bootstrap.git init→ legacy "Not a git repository" error.git init→ new "Bootstrap cancelled. A local git repository has been initialized but setup didn't complete. Runentire enableagain to continue." message.git add -A && git commit && git pushcommands to finish.Flags
All optional; each suppresses the matching interactive prompt, so
automation can drive the flow end-to-end without a TTY.
--init-repogit initprompt--no-init-repogit initprompt (mutually exclusive with--init-repo)--repo-name <name>--repo-owner <owner>--repo-visibility <public|private|internal>internalonly valid for orgs--no-githubgit init+ initial commit only; skip GitHub--initial-commit-message <msg>--skip-initial-commit--initial-commit-message)Setting any of
--repo-name/--repo-owner/--repo-visibilityimplicitly answers "yes" to the GitHub-confirm prompt.
Notes for reviewers
ghis shelled out viaexec.Commandrather than pulled in as a Godep. All calls go through a
bootstrapRunnerinterface so unit testsdon't hit the network.
huh.NewSelect(default /customize / skip) rather than a freeform input. "Skip" was the only
freeform input where a skip shortcut made sense — repo name and
identity have no sensible "no value" semantics — so we didn't try to
generalize it.
default/custom/skip → various flag combinations) is covered by unit
tests with a mocked
bootstrapRunner. Two integration-style tests(
TestBootstrap_FreshMachine_RealGit,TestBootstrap_FreshMachine_NoIdentity_RealGit) run realgit init/git commitagainst a temp dir isolated viaHOME+GIT_CONFIG_GLOBALto prove the identity + gpgsign fixes end-to-end.Test plan
mise run check(fmt + lint + unit + integration + Vogon canary + roger-roger external-agent canary) — already green locallyentire enablewith no flags walks through prompts and pushes to GitHub; default branch on the remote ismain, noentire/checkpoints/v1on the remoteentire enable --init-repo --no-github --initial-commit-message "init" --agent claude-codecompletes non-interactively, no GitHub call, one commitentire enable --init-repo --no-github --skip-initial-commitleaves files unstaged and prints the manual-commit hint.git/still in placeentire enable --init-repo --no-init-repo→ cobra's mutually-exclusive errorentire enablebehaves exactly as before (bootstrap skipped entirely)gh→ hint printed, localgit init+ initial commit still succeed