Metadata-Version: 2.4
Name: smart-autocommit
Version: 0.4.1
Summary: Smart git auto-commit: deterministic gates decide what may enter history; the LLM only writes messages and researches unknown paths — and never blocks a commit.
Project-URL: Homepage, https://github.com/crhan/smart-autocommit
Project-URL: Source, https://github.com/crhan/smart-autocommit
Project-URL: Changelog, https://github.com/crhan/smart-autocommit/blob/main/CHANGELOG.md
Author-email: crhan <crhan123@gmail.com>
License: MIT
License-File: LICENSE
Keywords: autocommit,automation,cli,git,llm,systemd
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Version Control :: Git
Classifier: Topic :: Utilities
Requires-Python: >=3.11
Description-Content-Type: text/markdown

# smart-autocommit (`sac`)

[![PyPI](https://img.shields.io/pypi/v/smart-autocommit)](https://pypi.org/project/smart-autocommit/)
[![Python](https://img.shields.io/pypi/pyversions/smart-autocommit)](https://pypi.org/project/smart-autocommit/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

智能 git 自动提交。**由确定性规则决定哪些内容可以进入历史;LLM 只负责写提交信息、研究陌生路径——而且它永远不会阻止一次提交。**

传统的自动提交(`cron` + `git add -A`,或手工维护的白名单)有两个慢性病:

- **静态白名单会腐烂。** 仓库结构一变就得手工改名单——漏一个就没备份,多加一个就是噪音。
- **`add -A` 太莽。** 它把依赖目录、缓存、嵌套仓库、孤儿数据一股脑塞进历史——而 **git 历史是单向门,删不干净。**

`sac` 用**六道确定性闸门后的受控 `add -A`** 取代白名单,再把两件需要判断、又能容错的活(写提交信息、判定陌生路径)交给一个 *始终可选* 的 LLM。

```
tracked changes ─┐
                 ├─▶ 6 deterministic gates ─▶ git commit ─▶ git push
untracked entries┘        (zero LLM)            ▲
                                                │ commit message: LLM → template → snapshot
unknown top-level paths ──▶ research (LLM) ──▶ suggestion + warning (never auto-applied)
```

## 为什么安全

- **LLM 永远不会阻止提交。** 任何 LLM/网络/超时/错误都会静默降级到纯确定性路径。全局开关(`message.enabled` / `research.enabled` 设为 `false`)让你回到纯规则模式。
- **陌生路径默认*排除*,而非纳入。** 全新的顶层目录会被扣下并标记——绝不自动提交进历史。
- **LLM 只能看到 `--stat` / `--name-status`**(文件名 + 增删行数),永远看不到 diff 正文——所以机密*内容*不会泄进提交信息、也不会被发送出去。
- **闸门是目录级的**,不是单文件级——因为真正的垃圾是成千上万的小文件。
- **推送永不 `--force`。** 远端分叉时保留你的本地提交,并请求人工介入。
- **推送白名单。** 设了 `push_allowed_hosts` 后,sac 拒绝推送到其他任何地方——带机密的配置仓库不会被推到错误的远端。
- **来源标记。** 每次自动提交都带 `auto(sac): ` 主题前缀和 `Auto-committed-by:` trailer,在 `git log` 里永远不会被误认为人工提交。

## 六道闸门

| # | 闸门 | 拦住什么 |
|---|------|---------|
| 1 | **denylist** | `.gitignore` 预过滤垃圾;可选的引擎 glob denylist 再加一层纵深防御 |
| 2 | **嵌套仓库跳过** | 含 `.git` 的路径会变成污染父仓库的 gitlink |
| 3 | **陌生顶层路径** | 不在已知集合里的新顶层段——排除 + 研究(核心安全闸门) |
| 4 | **目录大小 / 文件数** | 超过大小或文件数上限的新条目(对条目本身度量) |
| 5 | **敏感文件** | 只有 `--stat`/`--name-status` 会到达 LLM;形似敏感的文件名会被标记 |
| 6 | **批量删除** | 暂存的删除过多则中止提交(防止整个目录被清空) |

## 安装

需要 Python ≥ 3.11 和 `git`。最省事:

```sh
pip install smart-autocommit
# 或隔离安装到 PATH:uv tool install smart-autocommit  ·  pipx install smart-autocommit
```

从仓库 checkout 安装,用于系统服务托管:

```sh
./install.sh                              # 安装 `sac` + 写入 ~/.config/smart-autocommit/config.json
```

## 使用

### 就地模式(像 git 一样)——零注册

```sh
cd /any/git/repo
sac --dry-run        # 显示决策 + 提交信息,什么都不提交
sac                  # 对你所在的这个仓库做智能提交
sac init             # 放一个 .smart-autocommit.json,让策略随仓库走
```

### 纳管模式——一份配置,多个仓库,无人值守

```sh
sac --all                       # 处理中心配置里每个启用的仓库
sac --repo dotfiles             # 只处理其中一个
sac --all --dry-run             # 预演整批
```

用 [`systemd/`](systemd/) 里的 systemd 用户 timer 定时运行(见 [ARCHITECTURE.md](ARCHITECTURE.md#service-mode))。

### CI / 脚本

```sh
sac --repo build-artifacts --json    # 结构化结果输出到 stdout
echo "exit: $?"                      # 0 成功 · 1 某仓库未过闸门 · 2 用法/配置错误
```

## 配置(三层)

优先级从低到高:**内置默认 → 中心 `defaults` → 仓库本地 `.smart-autocommit.json` → 仓库的 `repos[]` 条目 → CLI 标志。**

```jsonc
{
  "defaults": {
    "push": true, "remote": "origin", "branch": "main",
    "gates": { "max_dir_size_mb": 5, "max_dir_files": 200, "max_deletes": 20, "denylist": [] },
    "message":  { "enabled": true, "language": "en", "provider": "default", "timeout": 45 },
    "research": { "enabled": true, "provider": "default", "timeout": 120, "skip_on_dry_run": true }
  },
  "providers": {
    "default": { "type": "openai", "base_url": "https://api.openai.com/v1",
                 "model": "gpt-4o-mini", "api_key_env": "OPENAI_API_KEY" }
  },
  "repos": [
    { "name": "dotfiles", "path": "/home/me/dotfiles" }
  ]
}
```

什么都不设的话,`sac` 会从环境变量构建一个 OpenAI 兼容的 provider(`SAC_API_KEY`/`OPENAI_API_KEY`、`SAC_BASE_URL`、`SAC_MODEL`)。没有 key → 提交信息回退到模板,提交照常发生。

**Provider 可插拔**(`base_url` + `model` + `apiKey`):任何 OpenAI 兼容端点用 `openai`,阿里云 `bl` CLI 用 `bailian`,任意命令用 `command`。陌生路径研究用一个 coding agent(`agent`,例如 `pi`),或默认用同一个 chat 模型、基于引擎收集的只读证据来判断。见 [`config.example.json`](config.example.json) 和 [ARCHITECTURE.md](ARCHITECTURE.md)。

> **注意:** 可选的 `agent` 研究 provider 会运行一个带文件读取工具的真实 coding agent,所以它*不在*“只有文件名到达 LLM”的保证范围内——它在调查陌生路径时可能读取文件内容。默认的 `chat` 研究器不会(它只看到引擎收集的元数据)。仅在可以接受把文件内容发给你的模型时,才使用 `agent`。

## 许可证

MIT——见 [LICENSE](LICENSE)。
