Skip to content

feat: add full execution#6

Merged
shinokada merged 10 commits intomainfrom
feat/full-execution
Mar 5, 2026
Merged

feat: add full execution#6
shinokada merged 10 commits intomainfrom
feat/full-execution

Conversation

@shinokada
Copy link
Copy Markdown
Owner

@shinokada shinokada commented Mar 5, 2026

Closes #

📑 Description

✅ Checks

  • My pull request adheres to the code style of this project
  • My code requires changes to the documentation
  • I have updated the documentation as required
  • All the tests have passed

ℹ Additional Information

Summary by CodeRabbit

  • New Features

    • Adds a complete project initialization flow: creates project folder, language-aware .gitignore, license selection, README, git init, interactive prompts, and final repository URL.
  • Behavior Changes

    • Repository creation no longer auto-pushes; pushing occurs as a separate step.
    • Failed bootstrap attempts now clean up partially created remote repos.
    • Improved input validation and dry-run messaging.
  • Chores

    • Added a CI Makefile target to run clean, lint, test, and build.
    • Added a new repository configuration field "bucket."

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 5, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8910e9a0-be45-4782-8beb-1e708ae1e8ef

📥 Commits

Reviewing files that changed from the base of the PR and between 2652f0d and d415519.

📒 Files selected for processing (1)
  • internal/files/gitignore.go

📝 Walkthrough

Walkthrough

Implements a full CLI project-init flow (creates project dir, .gitignore, README, optional LICENSE; inits Git, creates GitHub repo, commits and pushes), adds a ci Makefile target, adds directory: bucket under scoops.repository in .goreleaser.yaml, removes automatic push from gh repo create, and normalizes gitignore language handling.

Changes

Cohort / File(s) Summary
Release configuration
\.goreleaser.yaml
Adds directory: bucket under scoops.repository.
Build tooling
Makefile
Adds ci target (depends on clean-all, lint, test, build) and updates .PHONY to include ci.
CLI — project init flow
cmd/root.go
Implements end-to-end run() flow: resolves/creates project dir, writes .gitignore/README.md, prompts for LICENSE/visibility, initializes Git, creates GitHub repo (creation separate from push), and commits/pushes. Adds helpers (ensureGitignore, ensureLicense, ensureReadme, ensureGitRepo, createRemoteAndPush, ghAuthenticatedUser), tightens PreRunE validation, updates dry-run messaging, and adds gh CLI lookup with timeout.
GitHub repo helper
internal/repo/repo.go
Adds DeleteGitHubRepo(repoName string) error for cleanup; changes CreateGitHubRepo to create remote without --push (pushing moved to separate step).
Gitignore templates
internal/files/gitignore.go
Adds languageAliases map and NormalizeLanguage(lang string); normalizes language names, validates input, fetches exact .gitignore template with explicit HTTP error handling, streams response into a temp file and atomically renames to dest (removes previous minimal fallback).
Module deps
go.mod
Minor dependency/version adjustments.

Sequence Diagram(s)

sequenceDiagram
    participant User as "User"
    participant CLI as "CLI (cmd/root.go)"
    participant FS as "File System"
    participant Prompt as "Prompts"
    participant Git as "Git"
    participant GH as "GitHub (gh)"
    rect rgba(200,230,201,0.5)
    User->>CLI: run init command
    CLI->>CLI: resolve target dir & project name
    end
    rect rgba(187,222,251,0.5)
    CLI->>FS: create project dir, write `.gitignore`, `README.md`
    CLI->>Prompt: ask for LICENSE choice & visibility
    Prompt-->>CLI: license selection / visibility
    CLI->>FS: write `LICENSE` (if chosen)
    end
    rect rgba(255,224,178,0.5)
    CLI->>Git: init repo
    CLI->>GH: create repository (without `--push`)
    GH-->>CLI: returns repo URL
    CLI->>Git: commit & push branch
    Git-->>CLI: pushed
    end
    CLI->>User: display success with repo URL
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped through code at dawn's new light,
I stitched a gitignore, tuned CI just right.
I birthed a repo, then pushed with glee,
A README, license, and a tidy tree.
Carrots of commits — hooray for me!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title "feat: add full execution" clearly and accurately reflects the main objective of the changeset, which replaces placeholder execution with a complete end-to-end implementation across multiple files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/full-execution

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/root.go`:
- Around line 143-152: At each of the four file existence checks (the blocks
using os.Stat for gitignorePath, licensePath, readmePath, and the .git repoPath)
don't treat any non-nil err as "exists"; instead first test if err != nil and if
so return or propagate a descriptive error (e.g., return fmt.Errorf("stat %s:
%w", <pathVar>, err)), then only if os.IsNotExist(err) perform creation (call
files.FetchGitignore, files.FetchLicense, files.FetchReadme or run git init) and
otherwise, when err == nil and !quiet, print the "already exists, skipping"
message; update the four checks to follow this pattern so permission/I/O errors
are surfaced rather than masked.
- Around line 224-238: The CLI parses --branch and --message but never uses them
when pushing; update run() to pass those values to the git operation by calling
repo.CommitAndPush with the parsed branch and message (or modify
repo.CreateGitHubRepo to accept branch/message and forward them to
repo.CommitAndPush). Specifically, replace or augment the current call to
repo.CreateGitHubRepo(dir, repoName, visibility, description) so that after
creating the remote you invoke repo.CommitAndPush(dir, repoName, branch,
message) (using the branch and message variables defined for the flags) so
user-specified -b/-m values are applied.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 86c4ffb9-c95d-43dc-a334-ff633097e719

📥 Commits

Reviewing files that changed from the base of the PR and between aab71e6 and fad3243.

📒 Files selected for processing (3)
  • .goreleaser.yaml
  • Makefile
  • cmd/root.go

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/root.go`:
- Around line 241-246: The visibility variable is left empty when neither flag
is set, causing gh repo create to enter interactive mode; set an explicit
default (e.g., "public" or "private") where visibility is initialized so it's
never empty, update the code that builds the gh repo create invocation and the
CreateGitHubRepo call to always pass this explicit visibility value (referencing
the visibility variable and the CreateGitHubRepo function), and ensure the
default choice is documented in the flag handling so non-interactive workflows
never trigger prompts.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c1f7ee6c-6e8e-43e4-b193-8bb2a4cb1793

📥 Commits

Reviewing files that changed from the base of the PR and between fad3243 and 3a58b57.

📒 Files selected for processing (2)
  • cmd/root.go
  • internal/repo/repo.go

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
cmd/root.go (1)

120-130: Consider normalizing all paths to absolute.

When directory != ".", relative paths like ../myproject remain relative. While filepath.Base() extracts the name correctly, subsequent operations could behave unexpectedly if any internal function changes the working directory.

♻️ Optional improvement
 func run() error {
 	// Resolve directory
 	dir := directory
-	if dir == "." {
-		var err error
-		dir, err = os.Getwd()
+	if !filepath.IsAbs(dir) {
+		wd, err := os.Getwd()
 		if err != nil {
 			return fmt.Errorf("could not get current directory: %w", err)
 		}
+		dir = filepath.Join(wd, dir)
 	}
+	dir = filepath.Clean(dir)
 	repoName := filepath.Base(dir)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/root.go` around lines 120 - 130, The code in run() currently only
resolves directory when directory == ".", leaving relative paths (e.g.,
"../myproject") unchanged; update run() to normalize directory to an absolute
path by calling filepath.Abs on the resolved dir value (the variable dir derived
from directory) before using filepath.Base; ensure you handle and return any
error from filepath.Abs so repoName := filepath.Base(dir) always receives an
absolute path.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/root.go`:
- Around line 164-189: The license prompt currently always calls
prompts.PromptSelect (using licenseOptions) even when the quiet flag is set;
change the logic in the block that handles missing LICENSE so that it first
checks the quiet boolean and skips prompting when quiet is true (e.g., print
"Skipping LICENSE." only when !quiet or silently return), or alternatively read
a provided --license flag before prompting; implement this by gating the call to
prompts.PromptSelect and the subsequent switch on the choice with an if !quiet {
... } block (references: prompts.PromptSelect, licenseOptions, FetchLicenseText,
licensePath, quiet).
- Around line 246-264: The printed GitHub URL uses the local "username" variable
which may not match the gh CLI authenticated account; update the post-create
messaging in cmd/root.go to determine the actual remote owner used by gh before
printing: either query the authenticated user via gh (e.g., run `gh api user
--jq .login`) and use that value when formatting the URL, or avoid embedding the
username and print a relative repo path (e.g., "github.com/<owner>/<repo>"
replaced by just "<repoName>" or "github.com/<repoName>"). Adjust the code
around the calls to repo.CreateGitHubRepo and the final fmt.Printf so it uses
the retrieved gh login (or the relative form) instead of the existing username
variable; ensure any errors from the gh query are handled and fall back
gracefully to the current behavior.

---

Nitpick comments:
In `@cmd/root.go`:
- Around line 120-130: The code in run() currently only resolves directory when
directory == ".", leaving relative paths (e.g., "../myproject") unchanged;
update run() to normalize directory to an absolute path by calling filepath.Abs
on the resolved dir value (the variable dir derived from directory) before using
filepath.Base; ensure you handle and return any error from filepath.Abs so
repoName := filepath.Base(dir) always receives an absolute path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d81d86f0-f8a7-4068-82de-d3c03806c10a

📥 Commits

Reviewing files that changed from the base of the PR and between 3a58b57 and 3453ae8.

📒 Files selected for processing (1)
  • cmd/root.go

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/root.go`:
- Around line 268-275: ghAuthenticatedUser currently runs the external "gh api
user" command with exec.Command and no deadline, which can hang indefinitely;
change it to use a context with a timeout (e.g. context.WithTimeout) and
exec.CommandContext so the process is killed if it exceeds the deadline, handle
the context error from cmd.Run (or cmd.Wait) and return empty string on
timeout/failure, and ensure Stdout is still captured from the command created in
ghAuthenticatedUser so the trimmed login is returned on success.
- Around line 259-262: The success message hardcodes "github.com"; update the
two fmt.Printf calls that print the repository URL to use the actual GitHub host
instead of the literal "github.com" by retrieving the configured hostname from
the GitHub client/config (use the existing variable that represents the host or
hostname from authentication/config, e.g., a ghHost or client.BaseURL/Host
value) and format the URL as "https://{host}/{ghUser}/{repoName}" (falling back
to printing just repoName if no host is available); change the fmt.Printf
invocations referenced here to use that host variable rather than the hardcoded
domain.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 20307164-ee5a-4bed-b381-7a1e8b708801

📥 Commits

Reviewing files that changed from the base of the PR and between 3453ae8 and a984327.

📒 Files selected for processing (1)
  • cmd/root.go

cmd/root.go Outdated
Comment on lines +259 to +262
fmt.Printf("✓ Done! Repository available at https://github.com/%s/%s\n", ghUser, repoName)
} else {
fmt.Printf("✓ Done! Repository: %s\n", repoName)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read the file around lines 259-262 to see the context
sed -n '250,270p' cmd/root.go

Repository: shinokada/gitstart

Length of output: 712


🏁 Script executed:

#!/bin/bash
# Search for github.com references in the file
rg -n 'github\.com' cmd/root.go

Repository: shinokada/gitstart

Length of output: 342


🏁 Script executed:

#!/bin/bash
# Search for any host/domain configuration or variables
rg -n 'host|domain|endpoint' cmd/root.go -i

Repository: shinokada/gitstart

Length of output: 44


🏁 Script executed:

#!/bin/bash
# Look for ghUser and repoName variable definitions to understand their content
rg -n -B5 -A5 'ghUser.*=' cmd/root.go | head -60

Repository: shinokada/gitstart

Length of output: 463


🏁 Script executed:

#!/bin/bash
# Search for all gh CLI invocations to understand capabilities
rg -n 'gh"' cmd/root.go -A2

Repository: shinokada/gitstart

Length of output: 187


🏁 Script executed:

#!/bin/bash
# Search for GitHub host/domain configuration across entire codebase
rg -n 'host|domain' . --type go | grep -v node_modules | head -30

Repository: shinokada/gitstart

Length of output: 44


🏁 Script executed:

#!/bin/bash
# Check if there's a config file or any host-related setup
fd -type f '\.gh|config' . | head -20

Repository: shinokada/gitstart

Length of output: 232


🏁 Script executed:

#!/bin/bash
# Look at the broader context around ghAuthenticatedUser to understand its purpose
sed -n '255,280p' cmd/root.go

Repository: shinokada/gitstart

Length of output: 772


🏁 Script executed:

#!/bin/bash
# Check if gh CLI has a way to get the configured host
# Look at the README or docs for this project
find . -name "README*" -o -name "*.md" | head -5 | xargs cat 2>/dev/null | head -100

Repository: shinokada/gitstart

Length of output: 3826


🏁 Script executed:

#!/bin/bash
# Search for any gh host configuration or enterprise setup in codebase
rg -n 'api\.github|GITHUB_HOST|gh.*host' . --type go

Repository: shinokada/gitstart

Length of output: 159


🏁 Script executed:

#!/bin/bash
# Check if gh CLI is used elsewhere and how
rg -n 'exec.Command.*"gh"' .

Repository: shinokada/gitstart

Length of output: 139


🏁 Script executed:

#!/bin/bash
# Look at the complete file structure to understand the project better
ls -la

Repository: shinokada/gitstart

Length of output: 1091


🏁 Script executed:

#!/bin/bash
# Check if gh CLI can retrieve the current host
# Search for any references to gh config or host retrieval
rg -n 'gh.*config|host|GITHUB' . --type go

Repository: shinokada/gitstart

Length of output: 44


🏁 Script executed:

#!/bin/bash
# Search the README for any mention of GitHub Enterprise or alternate hosts
cat README.md | grep -i "enterprise\|host\|instance" || echo "No matches found"

Repository: shinokada/gitstart

Length of output: 78


🏁 Script executed:

#!/bin/bash
# Check if repo.CreateRepository uses gh repo create which may handle different hosts
rg -n 'CreateRepository|gh repo create' -B5 -A10 internal/repo/

Repository: shinokada/gitstart

Length of output: 44


🏁 Script executed:

#!/bin/bash
# Check if gh repo create command in the codebase uses any hostname configuration
find . -name "*.go" -exec grep -l "repo.Create\|gh.*repo" {} \;

Repository: shinokada/gitstart

Length of output: 99


🏁 Script executed:

#!/bin/bash
# Look at the repo package to see how gh repo create is called
cat internal/repo/repo.go

Repository: shinokada/gitstart

Length of output: 2478


🏁 Script executed:

#!/bin/bash
# Check if there are any flags or environment variables related to host in the codebase
rg -n '\-\-hostname|GITHUB_HOST|GH_HOST' .

Repository: shinokada/gitstart

Length of output: 44


🏁 Script executed:

#!/bin/bash
# Look at the full root.go to understand the complete context and see if there's any host configuration
wc -l cmd/root.go && head -50 cmd/root.go

Repository: shinokada/gitstart

Length of output: 1169


🏁 Script executed:

#!/bin/bash
# Verify if gh CLI can retrieve the current configured host
# This would be the correct way to get the host dynamically
gh config get host 2>&1 || echo "Command may not be available in test environment"

Repository: shinokada/gitstart

Length of output: 136


🏁 Script executed:

#!/bin/bash
# Check GitHub CLI documentation or common usage for getting the current host
# Search for any existing patterns in the codebase that might retrieve host info
rg -n 'gh.*config|gh.*api.*host' .

Repository: shinokada/gitstart

Length of output: 44


🏁 Script executed:

#!/bin/bash
# Verify the exact gh CLI capabilities by checking what gh repo create outputs
# Look at git history to see if this issue has been discussed
git log --oneline --all | grep -i "host\|url\|enterprise\|github.com" | head -10

Repository: shinokada/gitstart

Length of output: 157


🏁 Script executed:

#!/bin/bash
# Check the gh CLI documentation or API to see if we can retrieve the current host
# The gh CLI stores configuration, let's check if there's a way to get hostname
rg -n 'hostname|GH_HOST' README.md || echo "Not in README"

Repository: shinokada/gitstart

Length of output: 75


🏁 Script executed:

#!/bin/bash
# Look at the recent git commit that mentions "gh auth user for URL"
git show a984327 --no-patch --format="%B%n%n" && git show a984327 --stat

Repository: shinokada/gitstart

Length of output: 2510


🏁 Script executed:

#!/bin/bash
# Check if gh repo create accepts a hostname flag or if the host comes from environment
# Search for how gh repo create is invoked in the code
rg -n 'gh.*repo.*create' -B3 -A3 internal/repo/repo.go

Repository: shinokada/gitstart

Length of output: 487


🏁 Script executed:

#!/bin/bash
# Check GitHub CLI documentation for getting the current host
# Look for any gh command that might return the configured host
gh api user --jq 'env' 2>&1 | head -5 || echo "Checking alternative methods..."
gh config list 2>&1 | head -10 || echo "gh config list not available"

Repository: shinokada/gitstart

Length of output: 388


🏁 Script executed:

#!/bin/bash
# Verify the actual behavior - gh repo create works with any configured host
# and the URL output from gh would use the correct host
# Let's see what information is available from gh
gh repo view --json url 2>&1 || echo "Need an existing repo to test"

Repository: shinokada/gitstart

Length of output: 190


🏁 Script executed:

#!/bin/bash
# Check if there's a way to query the current host from gh CLI configuration
env | grep -i github || echo "No GitHub-related env vars"

Repository: shinokada/gitstart

Length of output: 88


Success message hardcodes github.com, which fails for GitHub Enterprise or other configured hosts.

When users authenticate gh to a GitHub Enterprise instance or alternative host via gh auth login --hostname, the repository is created on that host but the success message incorrectly prints https://github.com/%s/%s. The printed URL will be wrong for non-default hosts.

Suggested fix
-		if ghUser != "" {
-			fmt.Printf("✓ Done! Repository available at https://github.com/%s/%s\n", ghUser, repoName)
+		if ghUser != "" {
+			fmt.Printf("✓ Done! Repository created: %s/%s\n", ghUser, repoName)
 		} else {
 			fmt.Printf("✓ Done! Repository: %s\n", repoName)
 		}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fmt.Printf("✓ Done! Repository available at https://github.com/%s/%s\n", ghUser, repoName)
} else {
fmt.Printf("✓ Done! Repository: %s\n", repoName)
}
fmt.Printf("✓ Done! Repository created: %s/%s\n", ghUser, repoName)
} else {
fmt.Printf("✓ Done! Repository: %s\n", repoName)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/root.go` around lines 259 - 262, The success message hardcodes
"github.com"; update the two fmt.Printf calls that print the repository URL to
use the actual GitHub host instead of the literal "github.com" by retrieving the
configured hostname from the GitHub client/config (use the existing variable
that represents the host or hostname from authentication/config, e.g., a ghHost
or client.BaseURL/Host value) and format the URL as
"https://{host}/{ghUser}/{repoName}" (falling back to printing just repoName if
no host is available); change the fmt.Printf invocations referenced here to use
that host variable rather than the hardcoded domain.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cmd/root.go (1)

35-39: ⚠️ Potential issue | 🟡 Minor

Validate --branch early to avoid downstream git command failure.

Since branch is now used in Line [252], an empty/whitespace branch should be rejected in PreRunE instead of failing later during git execution.

Proposed fix
 	PreRunE: func(cmd *cobra.Command, args []string) error {
 		if private && public {
 			return fmt.Errorf("flags --private and --public are mutually exclusive")
 		}
+		if strings.TrimSpace(branch) == "" {
+			return fmt.Errorf("flag --branch cannot be empty")
+		}
 		return nil
 	},

Also applies to: 252-253

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/root.go` around lines 35 - 39, In PreRunE validate the branch flag early:
check the branch variable (trim whitespace) and if it's empty return an error
(e.g., "flag --branch must not be empty or whitespace") so downstream git calls
won't fail; update the PreRunE function in cmd/root.go to perform this
validation alongside the existing private/public check and return an error if
branch is invalid.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/root.go`:
- Around line 145-157: The code calls files.FetchGitignore regardless of whether
the optional language flag is set; update the .gitignore creation branch so
files.FetchGitignore is only invoked when language is non-empty: check the
language variable before calling files.FetchGitignore (and only print "Creating
.gitignore..." when language != ""), otherwise skip fetching the gitignore and
do not error—use gitignorePath, files.FetchGitignore, language and quiet to gate
the operation accordingly.

---

Outside diff comments:
In `@cmd/root.go`:
- Around line 35-39: In PreRunE validate the branch flag early: check the branch
variable (trim whitespace) and if it's empty return an error (e.g., "flag
--branch must not be empty or whitespace") so downstream git calls won't fail;
update the PreRunE function in cmd/root.go to perform this validation alongside
the existing private/public check and return an error if branch is invalid.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1509cea1-e120-44d3-8631-f04edddeef3c

📥 Commits

Reviewing files that changed from the base of the PR and between a984327 and 29abe99.

📒 Files selected for processing (1)
  • cmd/root.go

@shinokada
Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 5, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
cmd/root.go (1)

39-41: Normalize branch once in PreRunE (not just validate it).

Line 39 uses strings.TrimSpace(branch) only for the emptiness check, but the original untrimmed value is still used later (Line 259). Assign the trimmed value back so downstream operations use a sanitized branch name.

Suggested diff
-		if strings.TrimSpace(branch) == "" {
+		branch = strings.TrimSpace(branch)
+		if branch == "" {
 			return fmt.Errorf("flag --branch cannot be empty")
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/root.go` around lines 39 - 41, In PreRunE, normalize the branch variable
by assigning the trimmed value back before validation (replace the one-off
strings.TrimSpace(branch) check with branch = strings.TrimSpace(branch)); then
validate the now-normalized branch (e.g., if branch == "" return error) so all
downstream uses of the branch variable (the later code that references branch)
operate on the sanitized value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/root.go`:
- Around line 150-163: Trim the language input once and use the trimmed value
for all checks and the fetch call: replace usages of strings.TrimSpace(language)
and the later files.FetchGitignore(language, gitignorePath) with a single
trimmed variable (e.g., trimmedLang) and call files.FetchGitignore(trimmedLang,
gitignorePath); ensure the empty-check, os.Stat branch, and error handling
reference this trimmed variable instead of the original language.

---

Nitpick comments:
In `@cmd/root.go`:
- Around line 39-41: In PreRunE, normalize the branch variable by assigning the
trimmed value back before validation (replace the one-off
strings.TrimSpace(branch) check with branch = strings.TrimSpace(branch)); then
validate the now-normalized branch (e.g., if branch == "" return error) so all
downstream uses of the branch variable (the later code that references branch)
operate on the sanitized value.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8ac9274c-ed23-4d93-805a-1d6510a0daa2

📥 Commits

Reviewing files that changed from the base of the PR and between 29abe99 and db2aa47.

📒 Files selected for processing (1)
  • cmd/root.go

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cmd/root.go (1)

66-87: ⚠️ Potential issue | 🟡 Minor

Dry-run output is inaccurate for --quiet license behavior.

Dry-run always says it would prompt/create LICENSE, but runtime skips license prompting in quiet mode (Lines 178-180). This can mislead automation users.

💡 Suggested fix
-			prompts.DryRunPrompt("Would prompt for and create LICENSE file")
+			if quiet {
+				prompts.DryRunPrompt("Would skip LICENSE creation in quiet mode")
+			} else {
+				prompts.DryRunPrompt("Would prompt for and create LICENSE file")
+			}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/root.go` around lines 66 - 87, The dry-run block always shows "Would
prompt for and create LICENSE file" even though the actual behavior skips
license prompting when quiet is true; update the dry-run output generation in
the same routine that writes the prompts.DryRunPrompt lines to conditionally add
the license-related DryRunPrompt only when the quiet flag is false (i.e., mirror
the runtime check that skips license prompting), referencing the dry-run prompt
code that emits "Would prompt for and create LICENSE file" and the quiet boolean
so the dry-run accurately reflects runtime behavior.
🧹 Nitpick comments (1)
cmd/root.go (1)

127-275: run() is doing too much; split into step-level helpers.

This function is now orchestration + step logic + user messaging. Extracting steps (e.g., ensureGitignore, ensureLicense, ensureReadme, ensureGitRepo, createRemoteAndPush) will reduce complexity and improve testability.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/root.go` around lines 127 - 275, The run() function mixes orchestration,
step logic, and user messaging—extract each major block into focused helper
functions to simplify testing and readability: create ensureGitignore(dir,
language, quiet) to encapsulate the .gitignore logic (using files.FetchGitignore
and os.Stat), ensureLicense(dir, quiet, description) to handle LICENSE selection
and files.FetchLicenseText (preserve prompts.PromptSelect flow),
ensureReadme(dir, repoName, description, quiet) for README.md creation
(files.CreateReadme), ensureGitRepo(dir, quiet) for initializing the .git repo
(repo.InitGitRepo), and createRemoteAndPush(dir, repoName, visibility,
description, branch, message, quiet) to create the GitHub repo and commit/push
(repo.CreateGitHubRepo and repo.CommitAndPush); move all user-facing prints out
of the helpers or make them controlled by the quiet flag so run() becomes a
simple orchestrator that calls these helpers in order, returns any errors they
propagate, and keeps final ghAuthenticatedUser lookup and success message
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/root.go`:
- Around line 151-165: The lang normalization only trims whitespace and should
be converted to the exact gitignore template name (or mapped from common
aliases) before calling files.FetchGitignore to avoid silent fallback; update
the code around the lang variable (where lang := strings.TrimSpace(language)) to
normalize/canonicalize values (e.g., map "python" -> "Python", "javascript" or
"nodejs" -> "Node", "node" -> "Node", etc.) or implement a small
normalizeLanguage(lang string) helper and pass the normalized value to
files.FetchGitignore(gitignoreLang, gitignorePath); keep the existing quiet
prints and error handling but ensure FetchGitignore receives the canonical
template name so GitHub lookups return the intended template.

---

Outside diff comments:
In `@cmd/root.go`:
- Around line 66-87: The dry-run block always shows "Would prompt for and create
LICENSE file" even though the actual behavior skips license prompting when quiet
is true; update the dry-run output generation in the same routine that writes
the prompts.DryRunPrompt lines to conditionally add the license-related
DryRunPrompt only when the quiet flag is false (i.e., mirror the runtime check
that skips license prompting), referencing the dry-run prompt code that emits
"Would prompt for and create LICENSE file" and the quiet boolean so the dry-run
accurately reflects runtime behavior.

---

Nitpick comments:
In `@cmd/root.go`:
- Around line 127-275: The run() function mixes orchestration, step logic, and
user messaging—extract each major block into focused helper functions to
simplify testing and readability: create ensureGitignore(dir, language, quiet)
to encapsulate the .gitignore logic (using files.FetchGitignore and os.Stat),
ensureLicense(dir, quiet, description) to handle LICENSE selection and
files.FetchLicenseText (preserve prompts.PromptSelect flow), ensureReadme(dir,
repoName, description, quiet) for README.md creation (files.CreateReadme),
ensureGitRepo(dir, quiet) for initializing the .git repo (repo.InitGitRepo), and
createRemoteAndPush(dir, repoName, visibility, description, branch, message,
quiet) to create the GitHub repo and commit/push (repo.CreateGitHubRepo and
repo.CommitAndPush); move all user-facing prints out of the helpers or make them
controlled by the quiet flag so run() becomes a simple orchestrator that calls
these helpers in order, returns any errors they propagate, and keeps final
ghAuthenticatedUser lookup and success message unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 79304d5c-6781-4fe3-9509-712b4c52b0a0

📥 Commits

Reviewing files that changed from the base of the PR and between db2aa47 and a733ed0.

📒 Files selected for processing (1)
  • cmd/root.go

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cmd/root.go (1)

35-43: ⚠️ Potential issue | 🟠 Major

Add message validation to PreRunE to fail fast before executing setup steps.

The message flag is passed directly to git commit (internal/repo/repo.go:47) without prior validation in PreRunE. A whitespace-only message would bypass all validation and fail only after expensive setup steps (directory creation, git initialization, GitHub repository creation) complete.

Proposed fix
 	PreRunE: func(cmd *cobra.Command, args []string) error {
 		if private && public {
 			return fmt.Errorf("flags --private and --public are mutually exclusive")
 		}
 		branch = strings.TrimSpace(branch)
 		if branch == "" {
 			return fmt.Errorf("flag --branch cannot be empty")
 		}
+		message = strings.TrimSpace(message)
+		if message == "" {
+			return fmt.Errorf("flag --message cannot be empty")
+		}
 		return nil
 	},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/root.go` around lines 35 - 43, PreRunE currently validates flags
private/public and branch but does not validate the message flag; add a
TrimSpace check for the message variable in PreRunE (alongside existing branch
validation) and return an error like "flag --message cannot be empty" when
message == "" so we fail fast before running setup steps; reference the PreRunE
function and the message variable (which is later used by
internal/repo/repo.go:47 for git commit) to locate where to add this check.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/files/gitignore.go`:
- Around line 33-43: NormalizeLanguage currently returns the original raw lang
on blank/whitespace input which can lead to invalid downstream URLs; change it
to return an explicit empty string for blank input by adding an early check
(e.g. if lower == "" return "") before the alias/title-case logic in
NormalizeLanguage, and apply the same explicit-empty return fix to the other
normalization helper that currently returns the raw input for blank values so
callers can validate/fail fast.

---

Outside diff comments:
In `@cmd/root.go`:
- Around line 35-43: PreRunE currently validates flags private/public and branch
but does not validate the message flag; add a TrimSpace check for the message
variable in PreRunE (alongside existing branch validation) and return an error
like "flag --message cannot be empty" when message == "" so we fail fast before
running setup steps; reference the PreRunE function and the message variable
(which is later used by internal/repo/repo.go:47 for git commit) to locate where
to add this check.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 71643828-f696-43be-9932-ae9926a32852

📥 Commits

Reviewing files that changed from the base of the PR and between a733ed0 and 87fb3d5.

📒 Files selected for processing (2)
  • cmd/root.go
  • internal/files/gitignore.go

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
internal/files/gitignore.go (1)

53-53: Update URL to use main instead of master.

The github/gitignore repository now uses main as its default branch. Hardcoding master is outdated; switch to main for consistency with the repository's current state.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/files/gitignore.go` at line 53, The URL construction currently
hardcodes the branch "master" in the string assigned to the variable url
(fmt.Sprintf("https://raw.githubusercontent.com/github/gitignore/master/%s.gitignore",
normalized)); update that string to use "main" instead of "master" so the
repository's current default branch is referenced (i.e., change "master" →
"main" in the url format that uses normalized).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/files/gitignore.go`:
- Around line 68-74: The current write in the function (using os.Create(dest)
and io.Copy(f, resp.Body)) can leave a partial/corrupt .gitignore if the copy
fails; change the implementation to write atomically by creating a temporary
file in the same directory (e.g., via os.CreateTemp(dir, "gitignore-*")), copy
resp.Body into that temp file, call Sync/Close on the temp file, then atomically
rename the temp file to dest with os.Rename; update error handling to remove the
temp file on failure and ensure the existing dest is only replaced on successful
completion (adjust the code around the io.Copy/Close calls in ensureGitignore /
the function that uses resp.Body).

---

Nitpick comments:
In `@internal/files/gitignore.go`:
- Line 53: The URL construction currently hardcodes the branch "master" in the
string assigned to the variable url
(fmt.Sprintf("https://raw.githubusercontent.com/github/gitignore/master/%s.gitignore",
normalized)); update that string to use "main" instead of "master" so the
repository's current default branch is referenced (i.e., change "master" →
"main" in the url format that uses normalized).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 347d8c26-0c5a-4677-9da8-36b5cdd8c49e

📥 Commits

Reviewing files that changed from the base of the PR and between 87fb3d5 and 2652f0d.

📒 Files selected for processing (3)
  • cmd/root.go
  • internal/files/gitignore.go
  • internal/repo/repo.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/repo/repo.go

@shinokada shinokada merged commit 7c4bb04 into main Mar 5, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant