Repository navigation
feat: add atmos pro commit for server-side commits via GitHub App - #2298
Conversation
Add new `atmos pro commit` command that sends changed files to Atmos Pro, which creates commits server-side using its GitHub App installation. This ensures commits trigger CI workflows (unlike GITHUB_TOKEN commits). Features: - Flexible staging: --add "*.tf" for patterns, --all/-A for everything, or commit whatever is already staged - Built-in loop prevention via GITHUB_ACTOR detection - Client-side validation: path safety, file size limits, change count - OIDC authentication reusing existing Atmos Pro auth flow Also introduces `atmos-pro` blog tag and retroactively tags existing Atmos Pro changelog entries. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dependency Review✅ No vulnerabilities or license issues found.Snapshot WarningsEnsure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice. Scanned Files
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds an Changes
Sequence Diagram(s)sequenceDiagram
participant User as CLI User
participant Cmd as Cmd\n(pro commit)
participant Exec as ExecuteCommit\n(Orchestrator)
participant Git as Git\n(Local)
participant API as AtmosProAPIClient
participant GH as GitHub
User->>Cmd: atmos pro commit --message "..." --add "*.tf"
Cmd->>Cmd: validate flags & init config
Cmd->>Exec: ExecuteCommit(cfg, message, ...)
Exec->>Exec: loop-prevention (atmos-pro[bot])
Exec->>Exec: validate message/comment/branch
Exec->>Git: git add (if requested)
Exec->>Git: git diff --cached --name-only --diff-filter=AM/D
Exec->>Exec: filter & validate paths (no .github/, no .., no absolute)
Exec->>Git: read files, base64-encode (skip >2MiB)
Exec->>Exec: build CommitRequest DTO
Exec->>API: CreateCommit(request)
API->>API: retry & refresh OIDC on 401
API->>GH: POST /git/commit (OIDC auth)
GH->>API: 2xx CommitResponse { data.sha }
API->>Exec: return SHA
Exec->>User: write SHA to stdout
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
pkg/pro/dtos/commit_test.go (1)
67-98: Prefer table-driven cases for the response variants.Current coverage is good; a table-driven shape will trim duplication and make future API-field additions easier to maintain.
♻️ Suggested refactor
func TestCommitResponse(t *testing.T) { - t.Run("successful response deserializes correctly", func(t *testing.T) { - body := `{ - "success": true, - "status": 200, - "data": { "sha": "abc123def456" } - }` - - var resp CommitResponse - err := json.Unmarshal([]byte(body), &resp) - require.NoError(t, err) - assert.True(t, resp.Success) - assert.Equal(t, 200, resp.Status) - assert.Equal(t, "abc123def456", resp.Data.SHA) - }) - - t.Run("error response deserializes correctly", func(t *testing.T) { - body := `{ - "success": false, - "status": 400, - "errorMessage": "validation failed", - "traceId": "trace-123" - }` - - var resp CommitResponse - err := json.Unmarshal([]byte(body), &resp) - require.NoError(t, err) - assert.False(t, resp.Success) - assert.Equal(t, "validation failed", resp.ErrorMessage) - assert.Equal(t, "trace-123", resp.TraceID) - }) + cases := []struct { + name string + body string + success bool + status int + sha string + errorMessage string + traceID string + }{ + { + name: "success", + body: `{"success":true,"status":200,"data":{"sha":"abc123def456"}}`, + success: true, status: 200, sha: "abc123def456", + }, + { + name: "error", + body: `{"success":false,"status":400,"errorMessage":"validation failed","traceId":"trace-123"}`, + success: false, status: 400, errorMessage: "validation failed", traceID: "trace-123", + }, + } + + for _, tc := range cases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + var resp CommitResponse + err := json.Unmarshal([]byte(tc.body), &resp) + require.NoError(t, err) + assert.Equal(t, tc.success, resp.Success) + assert.Equal(t, tc.status, resp.Status) + assert.Equal(t, tc.sha, resp.Data.SHA) + assert.Equal(t, tc.errorMessage, resp.ErrorMessage) + assert.Equal(t, tc.traceID, resp.TraceID) + }) + } }As per coding guidelines,
**/*_test.go: Use table-driven tests for testing multiple scenarios in Go.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/pro/dtos/commit_test.go` around lines 67 - 98, TestCommitResponse duplicates similar subtests; refactor it into a table-driven test that iterates over a slice of cases (with fields like name, body, wantSuccess, wantStatus, wantSHA, wantErrorMessage, wantTraceID), then for each case call t.Run(case.name, func(t *testing.T) { json.Unmarshal into CommitResponse, require.NoError, and assert the expected fields (Success, Status, Data.SHA, ErrorMessage, TraceID) accordingly }); update TestCommitResponse to use this table so adding new response fields later only requires extending the case struct.pkg/pro/api_client_commit.go (3)
67-69: Status code range allows 3xx redirects as success.The condition
resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusBadRequesttreats 3xx (redirects) as successful responses. For a JSON API endpoint, you'd typically want only 2xx as success:resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoicesIn practice this is low risk since Go's
http.Clientfollows redirects by default, but the semantics are clearer if restricted to 2xx.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/pro/api_client_commit.go` around lines 67 - 69, The success check for HTTP responses in the commit API is too broad and treats 3xx redirects as success; update the condition that currently uses resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusBadRequest to instead restrict success to only 2xx (for example use resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices) so that only 2xx responses bypass buildCommitAPIError; adjust the check near where resp.StatusCode is evaluated and buildCommitAPIError(resp, body) is returned.
22-47: Consider adding performance tracking.
CreateCommitis a public function performing network I/O. The coding guidelines recommend addingdefer perf.Track(nil, "pro.CreateCommit")()for observability.♻️ Suggested addition after line 22
func (c *AtmosProAPIClient) CreateCommit(dto *dtos.CommitRequest) (*dtos.CommitResponse, error) { + defer perf.Track(nil, "pro.CreateCommit")() + if dto == nil {You'll need to import
"github.com/cloudposse/atmos/pkg/perf"as well.As per coding guidelines: "Add
defer perf.Track(atmosConfig, "pkg.FuncName")()plus blank line to all public functions. Usenilif no atmosConfig param."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/pro/api_client_commit.go` around lines 22 - 47, Add performance tracking to the public CreateCommit method by inserting a deferred perf.Track call at the start of the function (use defer perf.Track(nil, "pro.CreateCommit")() and a blank line after it) and add the import for "github.com/cloudposse/atmos/pkg/perf"; update the CreateCommit function (method name CreateCommit on AtmosProAPIClient) to include this defer immediately after the nil-check so the network I/O is measured.
3-15: Import grouping: logger belongs with other Atmos packages.The
logimport fromgithub.com/cloudposse/atmos/pkg/loggeris an Atmos package but sits alone in group 2. Per guidelines, allcloudposse/atmospackages should be in group 3 together.♻️ Suggested fix
import ( "bytes" "encoding/json" "errors" "fmt" "io" "net/http" - log "github.com/cloudposse/atmos/pkg/logger" - errUtils "github.com/cloudposse/atmos/errors" + log "github.com/cloudposse/atmos/pkg/logger" "github.com/cloudposse/atmos/pkg/pro/dtos" )As per coding guidelines: "Organize imports in three groups separated by blank lines, sorted alphabetically: 1) Go stdlib, 2) 3rd-party (NOT cloudposse/atmos), 3) Atmos packages."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/pro/api_client_commit.go` around lines 3 - 15, Move the log import into the Atmos package import group so all cloudposse/atmos packages are grouped together; specifically, reorder the imports so standard library imports (bytes, encoding/json, errors, fmt, io, net/http) come first, third-party packages (e.g., any non-cloudposse imports) second, and then the Atmos packages group contains log ("github.com/cloudposse/atmos/pkg/logger"), errUtils ("github.com/cloudposse/atmos/errors"), and dtos ("github.com/cloudposse/atmos/pkg/pro/dtos") together, alphabetized within each group.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@pkg/pro/api_client_commit.go`:
- Around line 50-60: sendCommitRequest currently calls c.HTTPClient.Do without
guarding for a nil HTTPClient which can panic; update sendCommitRequest to
mirror sendInstancesRequest by selecting a client variable (use c.HTTPClient if
non-nil, otherwise http.DefaultClient) and call client.Do(req) so the method
safely falls back when c.HTTPClient is nil; reference function name
sendCommitRequest and the existing pattern in sendInstancesRequest to implement
the nil-guard/fallback.
---
Nitpick comments:
In `@pkg/pro/api_client_commit.go`:
- Around line 67-69: The success check for HTTP responses in the commit API is
too broad and treats 3xx redirects as success; update the condition that
currently uses resp.StatusCode < http.StatusOK || resp.StatusCode >=
http.StatusBadRequest to instead restrict success to only 2xx (for example use
resp.StatusCode < http.StatusOK || resp.StatusCode >=
http.StatusMultipleChoices) so that only 2xx responses bypass
buildCommitAPIError; adjust the check near where resp.StatusCode is evaluated
and buildCommitAPIError(resp, body) is returned.
- Around line 22-47: Add performance tracking to the public CreateCommit method
by inserting a deferred perf.Track call at the start of the function (use defer
perf.Track(nil, "pro.CreateCommit")() and a blank line after it) and add the
import for "github.com/cloudposse/atmos/pkg/perf"; update the CreateCommit
function (method name CreateCommit on AtmosProAPIClient) to include this defer
immediately after the nil-check so the network I/O is measured.
- Around line 3-15: Move the log import into the Atmos package import group so
all cloudposse/atmos packages are grouped together; specifically, reorder the
imports so standard library imports (bytes, encoding/json, errors, fmt, io,
net/http) come first, third-party packages (e.g., any non-cloudposse imports)
second, and then the Atmos packages group contains log
("github.com/cloudposse/atmos/pkg/logger"), errUtils
("github.com/cloudposse/atmos/errors"), and dtos
("github.com/cloudposse/atmos/pkg/pro/dtos") together, alphabetized within each
group.
In `@pkg/pro/dtos/commit_test.go`:
- Around line 67-98: TestCommitResponse duplicates similar subtests; refactor it
into a table-driven test that iterates over a slice of cases (with fields like
name, body, wantSuccess, wantStatus, wantSHA, wantErrorMessage, wantTraceID),
then for each case call t.Run(case.name, func(t *testing.T) { json.Unmarshal
into CommitResponse, require.NoError, and assert the expected fields (Success,
Status, Data.SHA, ErrorMessage, TraceID) accordingly }); update
TestCommitResponse to use this table so adding new response fields later only
requires extending the case struct.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: fa3536d8-18d8-454b-a8e4-ca8ae7cc46de
📒 Files selected for processing (17)
cmd/pro_commit.goerrors/errors.gopkg/config/load.gopkg/pro/api_client_commit.gopkg/pro/api_client_commit_test.gopkg/pro/commit.gopkg/pro/commit_test.gopkg/pro/dtos/commit.gopkg/pro/dtos/commit_test.gopkg/schema/pro.gowebsite/blog/2025-10-28-pro-instances-api-query-params.mdwebsite/blog/2026-03-19-instance-status-upload.mdxwebsite/blog/2026-03-25-chunked-stack-uploads.mdxwebsite/blog/2026-04-07-pro-commit.mdxwebsite/blog/tags.ymlwebsite/docs/cli/commands/pro/pro-commit.mdxwebsite/src/data/roadmap.js
👮 Files not reviewed due to content moderation or server errors (8)
- website/src/data/roadmap.js
- website/docs/cli/commands/pro/pro-commit.mdx
- website/blog/2026-04-07-pro-commit.mdx
- pkg/pro/api_client_commit_test.go
- cmd/pro_commit.go
- errors/errors.go
- pkg/pro/commit_test.go
- pkg/pro/commit.go
Matches the defensive pattern used in sendInstancesRequest to prevent nil pointer dereference when HTTPClient is unset. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use yaml:"-" json:"-" tags so the runtime-only GITHUB_HEAD_REF env var doesn't appear in serialized config output, which was breaking golden snapshot tests in CI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #2298 +/- ##
==========================================
+ Coverage 77.19% 77.26% +0.06%
==========================================
Files 1070 1074 +4
Lines 101556 101888 +332
==========================================
+ Hits 78398 78719 +321
+ Misses 18839 18826 -13
- Partials 4319 4343 +24
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
filepath.IsAbs("/etc/passwd") returns false on Windows since it expects
drive-letter paths. Add explicit "/" prefix check so the security
validation works cross-platform.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
🧹 Nitpick comments (3)
pkg/pro/commit.go (3)
331-340: Consider graceful handling when file disappears between git diff and stat.If a staged file is deleted after
git diff --cachedbut beforeos.Stat, the entire commit fails. A warning-and-skip approach (like the size check) would be more resilient.♻️ Optional: skip missing files gracefully
info, err := os.Stat(p) if err != nil { - return nil, fmt.Errorf("failed to stat file %s: %w", p, err) + if os.IsNotExist(err) { + ui.Warning(fmt.Sprintf("Skipping %s: file no longer exists", p)) + continue + } + return nil, fmt.Errorf("%w: %s: %w", errUtils.ErrFailedToReadFile, p, err) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/pro/commit.go` around lines 331 - 340, In the loop that iterates over paths (where os.Stat is called for each path and compares info.Size() to maxFileSizeBytes), change the error handling so that if os.Stat returns an os.IsNotExist error you log a warning via ui.Warning (similar to the size-skip message) and continue to the next path instead of returning a fatal error; for other os.Stat errors preserve the current behavior and return the wrapped error. Ensure you reference the same variables/functions (paths, os.Stat, ui.Warning, maxFileSizeBytes) so the change is localized to that check.
313-316: Path traversal check is overly broad.
strings.Contains(p, "..")would reject legitimate filenames likefoo..barorconfig..json. Consider checking for actual path components instead.♻️ Suggested refinement
// Reject path traversal. -if strings.Contains(p, "..") { +// Check for ".." as a path component (start, middle, or end). +normalizedPath := filepath.ToSlash(p) +if strings.HasPrefix(normalizedPath, "../") || + strings.Contains(normalizedPath, "/../") || + strings.HasSuffix(normalizedPath, "/..") || + normalizedPath == ".." { return fmt.Errorf("%w: path traversal not allowed: %s", errUtils.ErrCommitInvalidFilePath, p) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/pro/commit.go` around lines 313 - 316, The current check uses strings.Contains(p, "..") which falsely rejects valid names like "foo..bar"; instead normalize and inspect path components: call path.Clean (or filepath.Clean if OS-specific) on p, ensure the cleaned path is not absolute and then split the cleaned path into components and reject only if any component equals "..". Update the validation in the same location that returns errUtils.ErrCommitInvalidFilePath (the block currently using strings.Contains(p, "..")) to perform these component-level checks and preserve the same error return when traversal is detected.
331-345: Error wrapping should use static errors per guidelines.The
fmt.Errorfcalls here use dynamic error strings without wrapping a static sentinel fromerrors/errors.go. This deviates from the project's error handling conventions.♻️ Proposed fix using static errors
info, err := os.Stat(p) if err != nil { - return nil, fmt.Errorf("failed to stat file %s: %w", p, err) + return nil, fmt.Errorf("%w: %s: %w", errUtils.ErrFailedToReadFile, p, err) } ... contents, err := os.ReadFile(p) if err != nil { - return nil, fmt.Errorf("failed to read file %s: %w", p, err) + return nil, fmt.Errorf("%w: %s: %w", errUtils.ErrFailedToReadFile, p, err) }If
ErrFailedToReadFiledoesn't exist yet, you'd add it toerrors/errors.go. As per coding guidelines, all errors must be wrapped using static errors.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/pro/commit.go` around lines 331 - 345, Replace the dynamic fmt.Errorf usages in the loop that calls os.Stat and os.ReadFile with wrapping of package-level sentinel errors (e.g. errors.ErrFailedToStatFile and errors.ErrFailedToReadFile) per project convention: use fmt.Errorf("%w: failed to stat file %s: %v", errors.ErrFailedToStatFile, p, err) for the os.Stat error and fmt.Errorf("%w: failed to read file %s: %v", errors.ErrFailedToReadFile, p, err) for the os.ReadFile error; if those sentinel errors don't exist add them to errors/errors.go (e.g. var ErrFailedToStatFile = errors.New("failed to stat file") and var ErrFailedToReadFile = errors.New("failed to read file")) and keep the ui.Warning unchanged for the maxFileSizeBytes check.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@pkg/pro/commit.go`:
- Around line 331-340: In the loop that iterates over paths (where os.Stat is
called for each path and compares info.Size() to maxFileSizeBytes), change the
error handling so that if os.Stat returns an os.IsNotExist error you log a
warning via ui.Warning (similar to the size-skip message) and continue to the
next path instead of returning a fatal error; for other os.Stat errors preserve
the current behavior and return the wrapped error. Ensure you reference the same
variables/functions (paths, os.Stat, ui.Warning, maxFileSizeBytes) so the change
is localized to that check.
- Around line 313-316: The current check uses strings.Contains(p, "..") which
falsely rejects valid names like "foo..bar"; instead normalize and inspect path
components: call path.Clean (or filepath.Clean if OS-specific) on p, ensure the
cleaned path is not absolute and then split the cleaned path into components and
reject only if any component equals "..". Update the validation in the same
location that returns errUtils.ErrCommitInvalidFilePath (the block currently
using strings.Contains(p, "..")) to perform these component-level checks and
preserve the same error return when traversal is detected.
- Around line 331-345: Replace the dynamic fmt.Errorf usages in the loop that
calls os.Stat and os.ReadFile with wrapping of package-level sentinel errors
(e.g. errors.ErrFailedToStatFile and errors.ErrFailedToReadFile) per project
convention: use fmt.Errorf("%w: failed to stat file %s: %v",
errors.ErrFailedToStatFile, p, err) for the os.Stat error and fmt.Errorf("%w:
failed to read file %s: %v", errors.ErrFailedToReadFile, p, err) for the
os.ReadFile error; if those sentinel errors don't exist add them to
errors/errors.go (e.g. var ErrFailedToStatFile = errors.New("failed to stat
file") and var ErrFailedToReadFile = errors.New("failed to read file")) and keep
the ui.Warning unchanged for the maxFileSizeBytes check.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 04414076-5ea9-46bc-86ac-8119fa0cab0e
📒 Files selected for processing (1)
pkg/pro/commit.go
Add unit tests for previously uncovered commit.go functions: resolveBranch, validateChangeCount, buildDeletions, stageFiles, detectChanges, gitDiffCachedNames, and buildChanges. Uses isolated temp git repos for git-dependent tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Tip Atmos Pro
No affected stacks workflow was detected for this pull request. |
…ro commit` Move autofix logic to atmos-pro.yaml, using atmos toolchain for gofumpt and `atmos pro commit` for server-side commits via GitHub App. Stub autofix.yml to a no-op pointing to Atmos Pro. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Important Cloud Posse Engineering Team Review RequiredThis pull request modifies files that require Cloud Posse's review. Please be patient, and a core maintainer will review your changes. To expedite this process, reach out to us on Slack in the |
Use ghcr.io/cloudposse/atmos:latest container image so atmos is on PATH. Install pre-commit via Debian package instead of pip. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Snapshots were stale after log badge padding change (Padding 0,2 → 0,1) and error H1 box-style rendering update. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The shouldRenderStyled() change enables Glamour markdown rendering when CI=true, which breaks tests asserting on literal markdown text. Fix by setting NO_COLOR=1 in affected tests and using the formatter's own terminal color profile instead of lipgloss's process-wide singleton. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
regex101.com intermittently returns 504 Gateway Timeout in CI, causing the Check Markdown Links job to fail. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The test hardcoded "/tmp/test-workspace" which becomes "\tmp\test-workspace" on Windows after filepath.Clean, causing the Contains assertion to fail. Use t.TempDir() instead. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of regenerating snapshots to match CI-styled output, set NO_COLOR=1 on all log-level validation tests for consistent plain text output across environments. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Warning Release Documentation RequiredThis PR is labeled
|
|
These changes were released in v1.215.0-rc.8. |
what
atmos pro commitCLI command that sends changed files to Atmos Pro, which creates commits server-side using its GitHub App installation — ensuring commits trigger CI workflows (unlikeGITHUB_TOKENcommits)--add "*.tf"for patterns,--all/-Afor everything, or commit whatever is already stagedatmos-pro[bot]and exits early.github/rejection, traversal prevention), file size limits (2 MiB), max 200 changed filespkg/pro/api_client.goatmos-problog tag and retroactively tags 3 existing Atmos Pro changelog entrieswhy
GITHUB_TOKENin GitHub Actions don't trigger subsequent workflow runs — this is a deliberate GitHub limitation that blocks autofix patterns (e.g.,terraform fmt+ commit)references
pkg/pro/api_client.go(NewAtmosProAPIClientFromEnv)POST /api/v1/git/commitSummary by CodeRabbit
New Features
atmos pro commitfor server-side GitHub commits with staging flags (--message,--comment,--add,--all), loop prevention, branch checks, file-size and changed-files safety limits, and stdout commit SHA.Documentation
Usability
Tests
Chores
pro commit.