fix(enable): make GitHub repo creation and push explicit opt-ins · Entire
fix(enable): make GitHub repo creation and push explicit opt-ins
06b38b5→main·
computermode·4d ago·3 files·+147 added/-56 removed
Previously the bootstrap seeded push := useGitHub && commit (true) and
useGitHub := !opts.NoGitHub (true), flipping them to No only via the
interactive prompt. So a non-interactive run (e.g. entire enable --init-repo
in CI) created a GitHub repo and pushed the directory's contents with no
explicit opt-in — the interactive default-No was not the real default.
Now both are explicit opt-ins:
- Create the GitHub repo only on an explicit signal (--repo-*, --push, --yes) or an interactive "yes". Non-interactive with no signal stays local-only and never probes gh.
- Push only on --push/--yes or an interactive "yes"; otherwise the repo is
created but left unpushed, with manual
git pushguidance printed.
Adds a --push flag (implies creating the remote; mutually exclusive with --no-github and --skip-initial-commit) and updates --yes help to mention push.
Tests updated to reflect the new default (no-flags stays local; repo flags create-without-push) and cover the --push path and flag exclusivity.
Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
Sessions
01KXE4XZ12S5VJGDTTVCSQG0SNView transcript
Changes
3
- cmd/entire/cli
- Msetup.go+4/-1
- Msetup_github.go+59/-41
- Msetup_github_test.go+84/-14
934 unmodified lines
935
936
937
938
938
939
940
941
3 unmodified lines
945
946
947
948
949
950
951
952
953
954
955
956
957
934 unmodified lines
cmd.Flags().BoolVar(&opts.AbsoluteGitHookPath, flagAbsoluteGitHookPath, false, "Embed full binary path in git hooks (for GUI git clients that don't source shell profiles)")
cmd.Flags().BoolVar(&opts.SearchSkill, flagSearchSkill, false, "Install the optional Entire search skill for selected agent(s)")
cmd.Flags().BoolVar(&opts.AgentHelpSkill, flagAgentHelpSkill, false, "Install the stable Entire agent-help skill (points agents at `entire agent-help`) for selected agent(s)")
cmd.Flags().BoolVarP(&opts.Yes, "yes", "y", false, "Accept all defaults without prompting (in a non-repo directory: init git, create private GitHub repo, commit; then enable all agents and accept telemetry)")
cmd.Flags().BoolVarP(&opts.Yes, "yes", "y", false, "Accept all defaults without prompting (in a non-repo directory: init git, create private GitHub repo, commit, and push; then enable all agents and accept telemetry)")
addInsecureHTTPAuthFlag(cmd, &insecureHTTPAuth)
// Bootstrap flags for non-git-repo folders.
3 unmodified lines
cmd.Flags().StringVar(&bootstrapOpts.RepoOwner, "repo-owner", "", "GitHub user or organization login for the new repo")
cmd.Flags().StringVar(&bootstrapOpts.RepoVisibility, "repo-visibility", "", "GitHub repository visibility: public, private, or internal")
cmd.Flags().BoolVar(&bootstrapOpts.NoGitHub, "no-github", false, "Initialize local git repo only; skip creating a GitHub remote")
cmd.Flags().BoolVar(&bootstrapOpts.Push, "push", false, "When bootstrapping a new repo, push the initial commit to the created GitHub remote (implies creating the remote; without it the repo is created but not pushed)")
cmd.Flags().StringVar(&bootstrapOpts.InitialCommitMessage, "initial-commit-message", "", "Commit message for the initial commit when bootstrapping a new repo")
cmd.Flags().BoolVar(&bootstrapOpts.SkipInitialCommit, "skip-initial-commit", false, "Don't create the initial commit when bootstrapping a new repo")
cmd.MarkFlagsMutuallyExclusive("init-repo", "no-init-repo")
cmd.MarkFlagsMutuallyExclusive("initial-commit-message", "skip-initial-commit")
cmd.MarkFlagsMutuallyExclusive("push", "no-github")
cmd.MarkFlagsMutuallyExclusive("push", "skip-initial-commit")
// Provide a helpful error when --agent is used without a value
defaultFlagErr := cmd.FlagErrorFunc()
Mcmd/entire/cli/setup.go+4/-1
41 unmodified lines
42
43
44
45
46
45
46
47
48
49
50
51
52
53
54
110 unmodified lines
165
166
167
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
26 unmodified lines
226
227
228
223
224
225
226
227
228
229
230
231
232
233
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
235
246
247
248
93 unmodified lines
342
343
344
345
346
347
348
349
350
351
352
353
354
355
41 unmodified lines
// still created, but nothing is pushed.
SkipInitialCommit bool
// Yes accepts all defaults without prompting: init repo, create GitHub
// repo under the user's account (private), default commit message.
// Explicit flags (--no-github, --repo-owner, etc.) take precedence.
// repo under the user's account (private), default commit message, and
// push. Explicit flags (--no-github, --repo-owner, etc.) take precedence.
Yes bool
// Push opts into pushing the initial commit to the created GitHub remote
// without prompting. Pushing is otherwise an explicit, separate opt-in
// (interactive "yes" or --yes). Implies creating the remote.
Push bool
}
// bootstrapRunner executes external commands. Tests override this to avoid
110 unmodified lines
paths.ClearWorktreeRootCache()
fmt.Fprintln(w, " ✓ Initialized empty git repository")
// Step 3: decide whether to create a GitHub repo. If gh is missing or the
// user passed --no-github, we skip that branch but still bootstrap the
// local repo.
useGitHub := !opts.NoGitHub
if useGitHub {
if !ghAvailable(ctx, runner) {
fmt.Fprintln(errW, "gh CLI not found. Install it from https://cli.github.com/ and run `gh auth login` to add a GitHub remote.")
fmt.Fprintln(errW, "Continuing with local initialization only.")
useGitHub = false
} else if !ghAuthenticated(ctx, runner) {
fmt.Fprintln(errW, "gh CLI is not authenticated. Run `gh auth login` to add a GitHub remote.")
fmt.Fprintln(errW, "Continuing with local initialization only.")
useGitHub = false
}
}
// Step 3b: ask a simple yes/no before diving into owner/name/visibility
// prompts. Skip the confirm when any gh-specific flag is set (the flag
// implies intent) or when we're non-interactive (keep the documented
// happy path: default to yes).
if useGitHub && !opts.Yes && !ghFlagsProvided(opts) && interactive.CanPromptInteractively() {
confirmed, err := confirmCreateGitHubRepo(cwd)
if err != nil {
return nil, err
}
if !confirmed {
useGitHub = false
// Step 3: decide whether to create a GitHub repo. Creating a remote is an
// explicit opt-in: it happens only on an explicit signal (repo flags,
// --push, or --yes) or an interactive "yes". A non-interactive run with no
// such signal stays local-only — we never create a repo on the user's
// behalf. --no-github always wins.
useGitHub := false
if !opts.NoGitHub {
explicit := ghCreateRequested(opts)
// Only probe gh (and warn about a missing/unauthenticated CLI) when the
// user actually wants a GitHub repo — explicitly, or via the confirm
// prompt we're about to show interactively.
if explicit || interactive.CanPromptInteractively() {
switch {
case !ghAvailable(ctx, runner):
fmt.Fprintln(errW, "gh CLI not found. Install it from https://cli.github.com/ and run `gh auth login` to add a GitHub remote.")
fmt.Fprintln(errW, "Continuing with local initialization only.")
case !ghAuthenticated(ctx, runner):
fmt.Fprintln(errW, "gh CLI is not authenticated. Run `gh auth login` to add a GitHub remote.")
fmt.Fprintln(errW, "Continuing with local initialization only.")
case explicit:
useGitHub = true
default:
// Interactive with no explicit signal: prompt, defaulting to No.
confirmed, err := confirmCreateGitHubRepo(cwd)
if err != nil {
return nil, err
}
useGitHub = confirmed
}
}
}
// Step 6: separate guard for the push. Pushing publishes the directory's
// contents to the remote — a distinct outward-facing action from creating
// the repo — so confirm it on its own and default to No. Only relevant
// when we'll create a GitHub repo and have a commit to push. Gated like
// the create confirm: explicit flags / --yes / non-interactive keep the
// documented happy path (push).
push := useGitHub && commit
if push && !opts.Yes && !ghFlagsProvided(opts) && interactive.CanPromptInteractively() {
confirmed, err := confirmPushToRemote(fullName)
if err != nil {
return nil, err
// Step 6: pushing is also an explicit opt-in, separate from creating the
// repo. Publishing the directory's contents is a distinct outward-facing
// action, so it happens only on an explicit signal (--push or --yes) or an
// interactive "yes". Otherwise the repo is created but left unpushed. Only
// relevant when we'll create a GitHub repo and have a commit to push.
push := false
if useGitHub && commit {
switch {
case opts.Yes || opts.Push:
push = true
case interactive.CanPromptInteractively():
confirmed, err := confirmPushToRemote(fullName)
if err != nil {
return nil, err
}
push = confirmed
}
push = confirmed
}
return &bootstrapState{
93 unmodified lines
return opts.RepoName != "" || opts.RepoOwner != "" || opts.RepoVisibility != ""
}
// ghCreateRequested reports whether the caller has explicitly opted into
// creating a GitHub repo without an interactive prompt: --yes, --push (which
// needs a remote to push to), or any repo-targeting flag. When false and the
// session is non-interactive, the bootstrap stays local-only.
func ghCreateRequested(opts GitHubBootstrapOptions) bool {
return opts.Yes || opts.Push || ghFlagsProvided(opts)
}
// confirmCreateGitHubRepo asks the user whether they want to also create
// a matching GitHub repository. Interactive-only; callers gate on
// interactive.CanPromptInteractively. Pushing to the repo is confirmed