# Configuration Guide

This guide explains not only what each `smart-commit CLI` setting does, but also how to choose a good value for it.

If you are new to the project, read [`getting-started.md`](./getting-started.md) first. Then use this page when you start asking:

- which fields are actually required
- which defaults are safe
- which settings you should change first
- which settings are easy to misconfigure

## Start Here

For most first-time users, the real minimum is:

- `connection.baseUrl`
- `connection.apiKey`
- `connection.model`
- `git.autoCommit`
- `git.autoPush`

Use this as your safe starting point:

```json
{
  "smartCommitCli": {
    "connection": {
      "baseUrl": "https://api.openai.com/v1",
      "apiKey": "env:SMART_COMMIT_API_KEY",
      "model": "gpt-5"
    },
    "git": {
      "autoCommit": false,
      "autoPush": false
    }
  }
}
```

This gives you a review-first workflow with no commit or push side effects.

## Recommended Format

Use the canonical JSON format:

```json
{
  "smartCommitCli": {
    "connection": {
      "baseUrl": "https://api.openai.com/v1",
      "apiKey": "env:SMART_COMMIT_API_KEY",
      "model": "gpt-5"
    }
  }
}
```

Legacy `smartCommit.*` keys are still accepted for migration, but they should be treated as compatibility input only.

## How Configuration Is Resolved

The CLI merges config from high to low precedence:

1. CLI arguments
2. environment variables
3. `smartCommitCli` in a JSON config file
4. legacy `smartCommit.*`
5. built-in defaults

Example:

- config file sets `git.autoPush = true`
- environment variable sets `SMART_COMMIT_AUTO_PUSH=false`
- CLI passes `--auto-push`

Final result:

- `git.autoPush = true` from CLI

This means the command line always wins, which is useful for temporarily overriding a shared config file.

## `env:VAR_NAME` References

String values that begin with `env:` are resolved from the current process environment after merge and before validation.

Example:

```json
{
  "smartCommitCli": {
    "connection": {
      "apiKey": "env:SMART_COMMIT_API_KEY"
    }
  }
}
```

Required shell setup:

```bash
export SMART_COMMIT_API_KEY="your-api-key"
```

This pattern is recommended for secrets because:

- the same config file can work across machines
- secrets stay out of committed config files
- `config resolve` will still show the final merged config with secret redaction

## The Most Important Warning

The built-in defaults are not always the safest rollout defaults.

In particular, the built-in config defaults are:

- `git.autoStageWhenNothingStaged = true`
- `git.autoCommit = true`
- `git.autoPush = true`

These may be fine for a mature automated workflow, but they are too aggressive for first rollout.

For first use, explicitly set:

```json
{
  "smartCommitCli": {
    "git": {
      "autoCommit": false,
      "autoPush": false
    }
  }
}
```

## Safe Team Rollout Example

Recommended early team setup:

```json
{
  "smartCommitCli": {
    "connection": {
      "baseUrl": "https://api.openai.com/v1",
      "apiKey": "env:SMART_COMMIT_API_KEY",
      "model": "gpt-5",
      "llmResponseCorrectionRetryCount": 1
    },
    "review": {
      "threshold": 6,
      "language": "zh"
    },
    "git": {
      "autoStageWhenNothingStaged": true,
      "autoCommit": false,
      "autoPush": false
    },
    "passHistory": {
      "enabled": true,
      "writeStage": "review_passed",
      "dirPath": ".smart-commit-cli",
      "maxEntries": 3000
    },
    "output": {
      "format": "json",
      "logLevel": "info"
    }
  }
}
```

This is a practical team default because it:

- keeps the rollout safe
- stores successful run history
- stays friendly to hooks and automation

## Field-By-Field Decision Guide

This section is the main reference for how to choose values.

### `connection.*`

These settings tell the CLI how to reach your AI endpoint. `bridge` requires all three of `baseUrl`, `apiKey`, and `model`.

| Field | Built-in default | Recommended first value | When to change it | Common mistake |
| --- | --- | --- | --- | --- |
| `connection.baseUrl` | empty string | `https://api.openai.com/v1` or your internal OpenAI-compatible gateway | Change it when you use a proxy, gateway, or non-default compatible endpoint | Leaving it empty and expecting `bridge` to work |
| `connection.apiKey` | empty string | `env:SMART_COMMIT_API_KEY` | Change it only if you intentionally inject secrets another way | Putting raw secrets into committed config files |
| `connection.model` | empty string | A model name that your chosen `baseUrl` actually serves | Change it when you switch cost, quality, or provider capabilities | Using a model name unsupported by your gateway |
| `connection.requestTimeoutMs` | `1000000` | Leave default first | Change it only if your provider is too slow or your requests time out unexpectedly | Setting it below `5000`, which fails validation |
| `connection.llmResponseCorrectionRetryCount` | `1` | `1` | Change it when you want `0..5` repair or regeneration retries for protocol, format, or language validation failures | Expecting it to retry provider, network, HTTP, or timeout failures |
| `connection.extraHeaders` | `{}` | Leave empty first | Add values only if your proxy or API gateway requires custom headers | Adding provider-specific headers that your endpoint does not expect |

Practical guidance:

- if you do not know what to choose, ask one question first: "What OpenAI-compatible base URL and model name does our environment support?"
- if you use an internal gateway, use the gateway's supported model list instead of guessing
- `connection.llmResponseCorrectionRetryCount` applies to review-response repair and commit-message regeneration only when the model output fails local protocol validation; it does not hide transport or provider errors

### `review.*`

These settings control review quality, language, and pass-or-block behavior.

| Field | Built-in default | Recommended first value | When to change it | Common mistake |
| --- | --- | --- | --- | --- |
| `review.threshold` | `6` | `6` | Lower it if you get too many false blocks; raise it if you want stricter gating | Misreading the rule: `score <= threshold` blocks, `score > threshold` passes |
| `review.language` | `zh` | `zh` or `en`, depending on your team | Change it when your team wants review output in a different language | Assuming it changes Git behavior instead of review text |
| `review.maxDiffChars` | `100000` | Leave default first | Lower it if very large diffs are too slow or costly | Setting it below `1000`, which fails validation |
| `review.skill.id` | `code-review` | `code-review` | Change it when your repo needs domain-specific built-in review guidance | Using an unsupported id |
| `review.skill.path` | empty string | Leave empty first | Set it when your team has a custom review rules file | Setting both a custom path and then wondering why the built-in id does not matter |
| `review.skill.promptTuning` | empty string | Leave empty first | Add a short custom instruction when a full skill file is too heavy | Putting large policy documents into a tiny tuning string |

Supported built-in review skill ids:

- `code-review`
- `frontend-code-review`
- `mobile-code-review`
- `c-code-review`
- `python-code-review`
- `golang-code-review`
- `java-code-review`
- `cpp-code-review`
- `csharp-code-review`
- `rust-code-review`
- `php-code-review`

Built-in review skills load bundled guidance and references from the CLI package. They also share a diff-domain classifier that can fall back to generic review rules when the staged diff does not match the selected domain. If you set `review.skill.path`, the CLI keeps the current file-based custom-skill behavior and does not switch to the bundled built-in skill assets for that run.

When to choose a custom skill path:

- your team has a fixed review checklist
- you need security or compliance-specific review rules
- you want domain rules that are too long for `promptTuning`

### `commitMessage.*`

These settings control where the commit message comes from and how it is validated.

| Field | Built-in default | Recommended first value | When to change it | Common mistake |
| --- | --- | --- | --- | --- |
| `commitMessage.input` | empty string | Leave empty first | Set it when you already know the exact message you want | Setting it and expecting auto-generation to replace it |
| `commitMessage.language` | `zh` | `zh` or `en` | Change it to match your team's commit language | Mixing a team English convention with a non-English expectation |
| `commitMessage.structure` | `subjectOnly` | `subjectOnly` | Change it when your team wants optional commit bodies or footers | Expecting body/footer to be allowed while the default single-line mode is still active |
| `commitMessage.autoGenerate` | `true` | `true` | Set `false` only if users always provide a message themselves | Turning it off with no provided message, which causes a commit-message-required error |
| `commitMessage.hybridGenerate` | `false` | `false` | Set `true` when users provide a draft and want AI to refine it | Enabling it without understanding that it uses `commitMessage.input` as a draft |
| `commitMessage.validation.protocol` | `none` | `none` first, then `conventional` if your team standardizes on it | Change it when your team enforces a commit style | Enabling validation before the team is ready |
| `commitMessage.validation.pattern` | empty string | Leave empty first | Add a JavaScript regex only when your policy needs extra format checks | Writing an invalid regex pattern |
| `commitMessage.validation.extractTicketIdFromBranch` | `true` | `true` if your branches contain ticket ids | Set `false` if your branch naming does not include tickets | Expecting ticket extraction from branches that have no ticket ids |
| `commitMessage.validation.requireTicketIdInMessage` | `false` | `false` first | Set `true` only if your process really requires ticket ids in commit messages | Enforcing it before branch naming and generation are aligned |
| `commitMessage.skill.id` | `conventional` | `conventional` | Change it when you prefer another built-in commit style | Using a commit skill id that does not match your validation expectations |
| `commitMessage.skill.path` | empty string | Leave empty first | Set it when your team has its own commit-message policy file | Assuming a custom path still validates against the built-in id set |
| `commitMessage.skill.promptTuning` | empty string | Leave empty first | Add light style nudges without creating a full skill file | Cramming a full handbook into one short string |

How `hybridGenerate` actually works:

- if `commitMessage.input` is present and `hybridGenerate=false`, the CLI validates and uses the provided message directly
- if `commitMessage.input` is present and `hybridGenerate=true`, the CLI uses your draft as input to AI and returns a refined message
- if `commitMessage.input` is empty and `autoGenerate=true`, the CLI generates from the diff
- if `commitMessage.input` is empty and `autoGenerate=false`, the CLI errors because no message source exists

Supported commit message structures:

- `subjectOnly`: exactly one non-empty subject line
- `subjectBody`: a subject plus optional body separated by one blank line
- `subjectBodyFooter`: a subject plus optional body and optional footer/trailer lines

Protocol, language, ticket, and regex validation apply to the subject. Ticket injection from the branch preserves any accepted body or footer.

Supported validation protocols:

- `none`
- `conventional`
- `semantic`
- `gitmoji`

Supported built-in commit message skill ids:

- `conventional`
- `semantic`
- `gitmoji`

### `git.*`

These settings control whether the CLI changes Git state.

| Field | Built-in default | Recommended first value | When to change it | Common mistake |
| --- | --- | --- | --- | --- |
| `git.autoStageWhenNothingStaged` | `true` | `true` for convenience or `false` for stricter control | Set `false` if your team wants users to explicitly stage exact files | Forgetting it is enabled and wondering why unstaged files were included |
| `git.autoCommit` | `true` | `false` | Set `true` only after review-only mode is stable | Leaving the default on during first rollout |
| `git.autoPush` | `true` | `false` | Set `true` only after your team accepts automatic push behavior | Letting first rollout push unexpectedly |
| `git.pushTimeoutMs` | `90000` | Leave default first | Increase it only if pushes are slow in your environment | Setting it below `5000`, which fails validation |

Practical rollout advice:

- if you care more about safety than convenience, keep both `autoCommit` and `autoPush` off
- if you care more about speed than strict staging hygiene, keep `autoStageWhenNothingStaged=true`

### `passHistory.*`

These settings control local storage for successful run history.

| Field | Built-in default | Recommended first value | When to change it | Common mistake |
| --- | --- | --- | --- | --- |
| `passHistory.enabled` | `false` | `true` if you want reporting later | Turn it on when you want local historical reporting | Forgetting to enable it and then expecting `report generate` to summarize past work |
| `passHistory.writeStage` | `review_passed` | `review_passed` | Change it when you want pass-history records to start only after commit or push success | Assuming it changes the stored `eventType`, when it actually changes the earliest write point |
| `passHistory.dirPath` | empty string | Leave empty first or set `.smart-commit-cli` explicitly | Change it only if you want a custom location | Thinking empty means broken, when it actually falls back to the repo-local default directory |
| `passHistory.maxEntries` | `3000` | `3000` | Change it only if you need much shorter or longer retention | Setting it to `0` or a non-integer, which fails validation |

If `passHistory.dirPath` is empty, the CLI automatically stores pass history under the repository's `.smart-commit-cli` directory.

`passHistory.writeStage` means "the earliest successful stage that is allowed to create a record." After a record exists, later successful stages update the same record instead of appending duplicates, so the stored `eventType` always shows the furthest successful stage reached by that run.

- `review_passed` writes immediately after review success and can later upgrade to `commit_completed` or `commit_push_completed`
- `commit_completed` waits for a successful local commit and still preserves that record if a later push cannot start, fails, or times out
- `commit_push_completed` waits for both commit and push success; if push never succeeds, no record is written

### `reporting.*`

These settings control report generation behavior.

| Field | Built-in default | Recommended first value | When to change it | Common mistake |
| --- | --- | --- | --- | --- |
| `reporting.language` | `zh` | `zh` or `en` | Change it when your team wants report output in another language | Assuming this changes review or commit-message language too |
| `reporting.weekStartsOn` | `monday` | `monday` | Change it to `sunday` only if your reporting convention starts weeks on Sunday | Picking a week start different from your team's reporting habit |
| `reporting.outputDirPath` | empty string | Leave empty first | Change it only when you want reports in a different directory | Thinking empty means invalid, when it actually falls back to the repo-local default directory |
| `reporting.prompt` | empty string | Leave empty first | Add it when you want reports to emphasize business outcomes or another angle | Writing a very large prompt when a short focus instruction would do |
| `reporting.ai.enabled` | `false` | `false` first | Enable it when you want richer AI-rendered reports instead of local-only rendering | Turning it on before confirming connection settings are valid |

If `reporting.outputDirPath` is empty, the CLI writes reports to `.smart-commit-cli/reports` under the repository root.

### `pullRequestSummary.*`

These settings control optional Markdown PR summary generation after a passing `bridge` run.

| Field | Built-in default | Recommended first value | When to change it | Common mistake |
| --- | --- | --- | --- | --- |
| `pullRequestSummary.enabled` | `false` | `false` first | Set `true` when you want a PR description after review success | Expecting it to run for dry-run, blocked review, or failed commit/push paths |
| `pullRequestSummary.language` | `zh` | Match your reviewer language | Change it independently from review and commit-message language | Assuming it changes review output language |
| `pullRequestSummary.outputDirPath` | empty string | Leave empty first | Set it when your automation needs a specific artifact location | Thinking empty means invalid; it writes under `.smart-commit-cli/pr-summaries` |
| `pullRequestSummary.prompt` | empty string | Leave empty first | Add concise team PR template guidance | Asking it to invent tests, reviewers, links, or review findings |

The generated summary uses only the reviewed staged diff, commit message, branch, changed files, and run facts. Review scores, thresholds, findings, and suggestions are treated as internal gate data and are not included in the PR description.

### `output.*`

These settings control how results are presented.

| Field | Built-in default | Recommended first value | When to change it | Common mistake |
| --- | --- | --- | --- | --- |
| `output.format` | `json` | `json` for automation, `text` for interactive terminal use | Change it based on whether a human or a script will read stdout | Using `text` in a hook or script that expects structured JSON |
| `output.logLevel` | `info` | `info` | Use `debug` when diagnosing behavior, `warn` when you only want warnings and errors | Leaving `debug` on in normal automation and creating noisy logs |

## Supported Value Sets

These are the validated option sets enforced by the CLI.

### Languages

Supported output and commit-message languages:

- `zh`
- `en`
- `de`
- `fr`
- `es`
- `it`
- `pt`
- `tr`
- `id`
- `hi`
- `ru`
- `ko`
- `ja`

### Commit message validation protocols

- `none`
- `conventional`
- `semantic`
- `gitmoji`

### Report week start

- `monday`
- `sunday`

### Output format

- `json`
- `text`

### Log levels

- `debug`
- `info`
- `warn`
- `error`

## Common CLI Flags

Most practical flags:

```bash
--repo
--config
--output
--base-url
--api-key
--model
--threshold
--llm-response-correction-retry-count
--review-language
--code-review-skill-id
--commit-language
--commit-message-structure
--commit-message
--pass-history-write-stage
--enable-pull-request-summary
--pull-request-summary-language
--pull-request-summary-output-dir
--pull-request-summary-prompt
--no-commit
--no-push
--dry-run
```

Common reporting flags:

```bash
--period
--report-language
--report-output-dir
--report-ai
```

## Common Environment Variables

Most practical ones:

```bash
SMART_COMMIT_API_KEY
SMART_COMMIT_BASE_URL
SMART_COMMIT_MODEL
SMART_COMMIT_THRESHOLD
SMART_COMMIT_LLM_RESPONSE_CORRECTION_RETRY_COUNT
SMART_COMMIT_REVIEW_LANGUAGE
SMART_COMMIT_CODE_REVIEW_SKILL_ID
SMART_COMMIT_COMMIT_LANGUAGE
SMART_COMMIT_COMMIT_MESSAGE_STRUCTURE
SMART_COMMIT_AUTO_COMMIT
SMART_COMMIT_AUTO_PUSH
SMART_COMMIT_PASS_HISTORY_WRITE_STAGE
SMART_COMMIT_REPORT_LANGUAGE
SMART_COMMIT_REPORT_OUTPUT_DIR
SMART_COMMIT_ENABLE_PULL_REQUEST_SUMMARY
SMART_COMMIT_PULL_REQUEST_SUMMARY_LANGUAGE
SMART_COMMIT_PULL_REQUEST_SUMMARY_OUTPUT_DIR
SMART_COMMIT_PULL_REQUEST_SUMMARY_PROMPT
```

Useful boolean environment variables accept:

- `true`
- `false`
- `1`
- `0`

## Command-Specific Notes

### `config resolve`

Use this command to inspect the final merged config:

```bash
smart-commit config resolve --config ./smart-commit.json
```

This is the fastest way to catch:

- missing environment variables
- invalid booleans or numbers
- invalid protocol values
- invalid regex patterns
- unsupported skill ids

### `bridge`

`bridge` requires:

- `--repo`
- a valid repository path inside Git
- a valid connection configuration
- staged changes, unless auto-stage makes input available

Safest first run:

```bash
smart-commit bridge --repo . --config ./smart-commit.json --no-commit --no-push
```

### `report generate`

`report generate` requires:

- `--repo`
- a valid repository path inside Git

Supported periods:

- `daily`
- `yesterday`
- `weekly`
- `monthly`
- `quarterly`
- `yearly`

If `--period` is omitted, it defaults to `weekly`. `yesterday` always uses the previous natural local day.

## Validation Workflow

Before integrating the CLI into hooks or automation, use this sequence:

1. create or update `smart-commit.json`
2. run `smart-commit config resolve --config ./smart-commit.json`
3. run `smart-commit bridge --repo . --config ./smart-commit.json --dry-run`
4. run `smart-commit bridge --repo . --config ./smart-commit.json --no-commit --no-push`
5. only then consider enabling auto-commit or auto-push

This sequence catches configuration mistakes before they become Git side effects.

## Common Mistakes

### I set `env:SMART_COMMIT_API_KEY`, but validation fails

Make sure the environment variable exists in the same shell session:

```bash
export SMART_COMMIT_API_KEY="your-api-key"
```

### I expected `--config` to be mandatory everywhere

It is recommended, but not strictly required.

If omitted, the CLI still resolves from:

- CLI flags
- environment variables
- built-in defaults

Use `--config` whenever you want predictable, reusable team configuration.

### I enabled `autoCommit` or `autoPush` too early

For first rollout, keep this block safe:

```json
{
  "smartCommitCli": {
    "git": {
      "autoCommit": false,
      "autoPush": false
    }
  }
}
```

Then verify the workflow in review-only mode first.

### I still do not know what to change first

Use this order:

1. make `connection.baseUrl`, `connection.apiKey`, and `connection.model` valid
2. set `git.autoCommit=false`
3. set `git.autoPush=false`
4. leave everything else near the defaults
5. only tune `review.threshold`, languages, and reporting after you have one successful run

## Where To Go Next

- safe onboarding flow: [`getting-started.md`](./getting-started.md)
- broader CLI overview: [`../README.md`](../README.md)
- integration patterns: [`integrations.md`](./integrations.md)
- machine-facing contracts: [`contracts.md`](./contracts.md)
