Metadata-Version: 2.4
Name: commitar
Version: 1.0.1
Summary: Secure AI-powered Git commit and message generator
License-Expression: MIT
Project-URL: Repository, https://github.com/argolo/commitar
Project-URL: Homepage, https://argolo.dev
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: typer>=0.12
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == "dev"
Requires-Dist: mypy>=1.11; extra == "dev"
Requires-Dist: pip-audit>=2.7; extra == "dev"
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: pytest-cov>=5; extra == "dev"
Requires-Dist: ruff>=0.6; extra == "dev"
Dynamic: license-file

# Commitar

**Commitar** is a Python CLI that generates [Conventional Commits](https://www.conventionalcommits.org/) messages with AI and can optionally create Git commits. Its core principle is preserving the staging area: when the index already has changes, only those changes are considered.

Portuguese documentation is available in [README-PT.md](README-PT.md).

## Features

- Safe preview by default: no changes are made without `--output commit`.
- One-line Conventional Commit messages.
- OpenAI, Gemini, and Ollama support.
- File, directory, or whole-repository scope.
- Staging isolation, including partially staged files.
- TOML, environment-variable, and flag configuration.
- Colored feedback, a generation spinner, and elapsed time per message.
- Configurable AI timeout; the default is 60 seconds.

## Requirements

- Python 3.11 or later.
- Git installed and a Git repository initialized.
- A configured AI provider:
  - OpenAI: `OPENAI_API_KEY`;
  - Gemini: `GEMINI_API_KEY`;
  - Ollama: a local service running with an installed model.

## Installation

```bash
pip3 install commitar
```

For development, install the test dependencies too:

```bash
python -m pip install -e '.[dev]'
commitar --help
```

## Quick start

Configure your Git identity if necessary:

```bash
git config user.name "Your Name"
git config user.email "you@example.com"
```

Preview tracked changes:

```bash
commitar
```

When there is no staging, Commitar generates a preview for each changed file. The preview lists the source, files, suggested message, and generation time.

Untracked files are ignored for safety. Include them explicitly:

```bash
commitar --include-added
```

Create commits after a single confirmation:

```bash
commitar src/ --output commit
```

For automation, skip the interaction:

```bash
commitar src/ --output commit --yes
```

To create one commit from files explicitly selected with `git add`:

```bash
git add src/moon_service.py tests/test_moon_service.py
commitar --output commit
```

With existing staging, Commitar exclusively uses `git diff --cached`, generates one message, and creates a commit containing exactly the files in the index.

Use `--message` to skip the AI provider call. The value must be a valid one-line Conventional Commit:

```bash
commitar app.py --include-added \
  --message "feat: add moon phase endpoint" \
  --output commit --yes
```

## Staging safety

The behavior is deliberately conservative.

| Situation | Behavior |
| --- | --- |
| Staged files exist and `commitar` runs without a `PATH` | One message is generated from `git diff --cached`; the commit contains exactly the current index. |
| Staged files exist and a `PATH` is supplied | The command fails without changing the repository. |
| No staging and no `PATH` is supplied | One group is created for each changed file. |
| No staging and a file/directory is supplied | One group contains that file or all eligible files in the directory. |
| A file is partially staged | Only its index version is committed; remaining worktree changes stay intact. |

In path mode, Commitar verifies that the index remains empty before each commit. If another process changes staging, the operation stops to prevent changes from being mixed.

## Command reference

```text
commitar [OPTIONS] [PATH]
commitar config init [OPTIONS]
commitar config show [OPTIONS]
```

| Option | Description |
| --- | --- |
| `PATH` | A file or directory inside the worktree. With no value, one changed file is grouped at a time. |
| `--output preview` | Displays the preview; this is the default. |
| `--output commit` | Requests confirmation and creates commits. |
| `--dry-run` | Alias for `--output preview`. |
| `--yes`, `-y` | Does not request confirmation in `commit` mode. |
| `--message TEXT` | Uses a manual message without calling the AI. |
| `--include-added` | Includes untracked files. |
| `--provider` | Selects `openai`, `gemini`, or `ollama`. |
| `--model` | Sets the selected provider model. |
| `--timeout-seconds` | Temporarily overrides the AI timeout. |
| `--max-input-tokens` | Limits the estimated number of tokens in the full prompt sent to the AI. |
| `--context-window-tokens` | Sets the shared input and reserved-output context window. |
| `--config PATH` | Loads an additional TOML file, taking precedence over default files. |

Use `commitar --help` and `commitar config --help` for current CLI details.

## Configuration

Create a configuration template in the repository root:

```bash
commitar config init
```

Example `.commitar.toml`:

```toml
[ai]
provider = "ollama"
models = ["gemma4:e4b", "qwen2.5-coder:14b"]
endpoint = "http://localhost:11434/api/generate"
max_input_tokens = 12000
context_window_tokens = 32768
max_output_tokens = 80
temperature = 0.2
timeout_seconds = 60

[commit]
language = "pt-BR"
format = "conventional"
include_added = false
output = "preview"
confirm = true

[limits]
max_files_per_request = 50
max_diff_bytes = 100000
```

`models` accepts up to three models in preference order. If the AI returns an invalid message, Commitar tries the next model, for up to three attempts. `model = "name"` remains supported for a single model; `--model` takes precedence for the current run.

The configuration precedence, from highest to lowest, is: CLI flags; the `--config` file; `COMMITAR_*` environment variables; `.commitar.toml` in the repository root; `~/.config/commitar/config.toml`; and built-in defaults.

Supported variables are `COMMITAR_PROVIDER`, `COMMITAR_MODEL`, `COMMITAR_LANGUAGE`, `COMMITAR_OUTPUT`, `COMMITAR_ENDPOINT`, `COMMITAR_MAX_DIFF_BYTES`, and `COMMITAR_TIMEOUT_SECONDS`.

Credentials are never read from TOML. Use `OPENAI_API_KEY` or `GEMINI_API_KEY`; Ollama normally does not need a key for local use.

## AI providers

### Ollama

| Model | Recommended use |
| --- | --- |
| `gemma4:e4b` | A lighter option for general use and resource-constrained machines. |
| `qwen2.5-coder:14b` | A code-focused option for machines with more memory and processing capacity. |

```bash
ollama pull gemma4:e4b
ollama serve
commitar --provider ollama --model gemma4:e4b
```

The default endpoint is `http://localhost:11434/api/generate`.

### OpenAI

```bash
export OPENAI_API_KEY="..."
commitar --provider openai --model gpt-5-mini
```

### Gemini

```bash
export GEMINI_API_KEY="..."
commitar --provider gemini --model gemini-2.5-flash
```

## Context limits

The context is controlled by four settings: `max_files_per_request` (maximum files per group), `max_diff_bytes` (maximum diff size), `max_input_tokens` (estimated full-prompt limit), and `context_window_tokens` (total input/output window).

`max_input_tokens + max_output_tokens` cannot exceed `context_window_tokens`. Input counting is a conservative estimate independent of the provider tokenizer. In Ollama, the context window is sent as `num_ctx` and the output limit as `num_predict`. OpenAI and Gemini validate the window locally before sending the output limit.

If the diff exceeds its byte or token limit, Commitar produces a deterministic summary of diff metadata. If that still exceeds a limit, the command fails explicitly; content is never silently truncated.

## FastAPI example

The [example/](example/) directory contains an asynchronous FastAPI API that returns the approximate Moon phase for an ISO date:

```bash
python -m pip install fastapi uvicorn
uvicorn example.app:app --reload
curl 'http://127.0.0.1:8000/moon-phase?date=2026-07-28'
curl 'http://127.0.0.1:8000/next-moon-phase?phase=lua-cheia&from_date=2026-07-28'
```

`/next-moon-phase` accepts `lua-nova`, `crescente`, `quarto-crescente`, `gibosa-crescente`, `lua-cheia`, `gibosa-minguante`, `quarto-minguante`, and `minguante`. The calculation is an approximation based on the average synodic month.

## Development and testing

Use the `Makefile` to standardize local tasks:

```bash
make help
```

| Command | Purpose |
| --- | --- |
| `make venv` | Creates the local virtual environment in `.venv`. |
| `make shell` | Opens a shell with the virtual environment enabled; run `exit` to leave it. |
| `make install` | Installs Commitar in editable mode and all development dependencies. |
| `make clear` | Deletes Git-ignored files with `git clean -Xdf`, including `.venv`, `.env`, caches, and builds. |
| `make build` | Generates the wheel and source distribution in `dist/`. |
| `make test` | Runs the pytest suite. |
| `make coverage` | Runs tests and shows uncovered lines. |
| `make lint` | Checks style, imports, and common issues with Ruff. |
| `make format` | Formats code with Ruff. |
| `make typecheck` | Runs static analysis with mypy. |
| `make audit` | Checks dependencies for known vulnerabilities with pip-audit. |
| `make check` | Runs linting, type checking, tests, and the audit. |

```bash
make venv
make install
make check
make build
```

> **Warning:** `make clear` is destructive to ignored files. It also removes virtual environments and local files such as `.env`; back up needed local data.

## Troubleshooting

| Message / symptom | Recommended action |
| --- | --- |
| `No tracked file changes were found...` | Only new files exist. Run `commitar --include-added` to include them in the preview. |
| `There are no eligible changed files in this scope.` | There are no changes in the supplied scope; modify a tracked file or provide another path. |
| `There are staged changes...` | Run without a `PATH` to commit only the index, or clear/commit staging before supplying a path. |
| Provider timeout | Commitar retries up to three times across configured models. Check the service and increase `timeout_seconds`. |
| Credential error | Set `OPENAI_API_KEY` or `GEMINI_API_KEY` in the environment. |
| Invalid AI response | After three invalid attempts, the current group is skipped and execution continues with the next files. |

## License

Distributed under the [MIT License](LICENSE). A project by André Argôlo ([argolo.dev](https://argolo.dev)).

Repository: [github.com/argolo/commitar](https://github.com/argolo/commitar).
