gawt 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gawt-0.1.0/.github/workflows/ci.yml +33 -0
- gawt-0.1.0/.github/workflows/release.yml +29 -0
- gawt-0.1.0/.gitignore +9 -0
- gawt-0.1.0/CHANGELOG.md +27 -0
- gawt-0.1.0/LICENSE +21 -0
- gawt-0.1.0/Makefile +16 -0
- gawt-0.1.0/PKG-INFO +198 -0
- gawt-0.1.0/README.md +170 -0
- gawt-0.1.0/pyproject.toml +60 -0
- gawt-0.1.0/scripts/install-skill.sh +29 -0
- gawt-0.1.0/skills/gitagent/AGENTS.md +440 -0
- gawt-0.1.0/skills/gitagent/SKILL.md +89 -0
- gawt-0.1.0/src/gitagent/__init__.py +3 -0
- gawt-0.1.0/src/gitagent/agents.py +79 -0
- gawt-0.1.0/src/gitagent/cli.py +405 -0
- gawt-0.1.0/src/gitagent/errors.py +5 -0
- gawt-0.1.0/src/gitagent/finalize.py +73 -0
- gawt-0.1.0/src/gitagent/gitwrap.py +123 -0
- gawt-0.1.0/src/gitagent/models.py +174 -0
- gawt-0.1.0/src/gitagent/proposals.py +94 -0
- gawt-0.1.0/src/gitagent/review.py +235 -0
- gawt-0.1.0/src/gitagent/session.py +132 -0
- gawt-0.1.0/src/gitagent/skill.py +54 -0
- gawt-0.1.0/src/gitagent/store.py +220 -0
- gawt-0.1.0/tests/conftest.py +41 -0
- gawt-0.1.0/tests/test_finalize.py +163 -0
- gawt-0.1.0/tests/test_proposals.py +188 -0
- gawt-0.1.0/tests/test_session.py +73 -0
- gawt-0.1.0/tests/test_skill.py +58 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
lint-test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
19
|
+
uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: ${{ matrix.python-version }}
|
|
22
|
+
|
|
23
|
+
- name: Install uv
|
|
24
|
+
run: pip install uv
|
|
25
|
+
|
|
26
|
+
- name: Install the project
|
|
27
|
+
run: uv pip install --system -e ".[dev]"
|
|
28
|
+
|
|
29
|
+
- name: Lint with ruff
|
|
30
|
+
run: ruff check src tests
|
|
31
|
+
|
|
32
|
+
- name: Test with pytest
|
|
33
|
+
run: pytest -q
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
id-token: write
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build-publish:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
environment: pypi
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Set up Python
|
|
18
|
+
uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.12"
|
|
21
|
+
|
|
22
|
+
- name: Install uv
|
|
23
|
+
run: pip install uv
|
|
24
|
+
|
|
25
|
+
- name: Build distributions
|
|
26
|
+
run: uv build
|
|
27
|
+
|
|
28
|
+
- name: Publish to PyPI (trusted publishing / OIDC)
|
|
29
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
gawt-0.1.0/.gitignore
ADDED
gawt-0.1.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-07-07
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Initial public release.
|
|
12
|
+
- `git worktree`-based agent isolation under `.gitagent/`.
|
|
13
|
+
- Session lifecycle: `init`, `start`, `status`, `log`, `abort`.
|
|
14
|
+
- Agent management: `spawn`, `list-agents`, `kill`.
|
|
15
|
+
- Proposals as patch + manifest: `propose`, `proposals`, `show`, `diff`.
|
|
16
|
+
- Superagent decisions: `accept`, `reject`, `revise`, `integrate` (with `git apply --3way` conflict detection).
|
|
17
|
+
- `finalize` producing a single squashed commit on the current branch and resetting `.gitagent` (never pushes).
|
|
18
|
+
- `--json` output on `status`, `log`, `list-agents`, `proposals`, `integrate` for LLM/orchestrator consumption.
|
|
19
|
+
- Append-only audit trail at `.gitagent/log.jsonl`.
|
|
20
|
+
- CI (ruff + pytest on Python 3.11–3.13) and trusted-publishing release workflow.
|
|
21
|
+
- Bundled `gitagent` agent skill (installable via `gitagent install-skill` or `make install-skill`).
|
|
22
|
+
|
|
23
|
+
### Notes
|
|
24
|
+
- The PyPI distribution is published as **`gawt`** (not `gitagent`) because the
|
|
25
|
+
name `gitagent` on PyPI is already taken by an unrelated project (a Tornado
|
|
26
|
+
HTTP webhook server, last released 2016). The installed command is still
|
|
27
|
+
`gitagent`. Install with `pipx install gawt` / `uv tool install gawt`.
|
gawt-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 David Florez Mazuera
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
gawt-0.1.0/Makefile
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
.PHONY: install-skill uninstall-skill test lint build
|
|
2
|
+
|
|
3
|
+
install-skill:
|
|
4
|
+
bash scripts/install-skill.sh
|
|
5
|
+
|
|
6
|
+
uninstall-skill:
|
|
7
|
+
rm -rf ~/.agents/skills/gitagent
|
|
8
|
+
|
|
9
|
+
test:
|
|
10
|
+
. .venv/bin/activate && pytest
|
|
11
|
+
|
|
12
|
+
lint:
|
|
13
|
+
. .venv/bin/activate && ruff check src tests
|
|
14
|
+
|
|
15
|
+
build:
|
|
16
|
+
. .venv/bin/activate && uv build
|
gawt-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gawt
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Agent workspace manager over Git: isolate, review and consolidate multi-agent changes into a single commit.
|
|
5
|
+
Project-URL: Homepage, https://github.com/david-fm/gawt
|
|
6
|
+
Project-URL: Repository, https://github.com/david-fm/gawt
|
|
7
|
+
Project-URL: Issues, https://github.com/david-fm/gawt/issues
|
|
8
|
+
Author: David Florez Mazuera
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: ai-agents,cli,git,llm,multi-agent,worktree
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Pre-processors
|
|
20
|
+
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Requires-Dist: rich>=13
|
|
23
|
+
Requires-Dist: typer>=0.12
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
26
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# gitagent
|
|
30
|
+
|
|
31
|
+
> Agent workspace manager over Git: isolate, review and consolidate multi-agent changes into a **single commit**.
|
|
32
|
+
|
|
33
|
+
`gitagent` is a lightweight CLI that wraps `git worktree` so a **superagent** can spawn isolated **subagents**, collect their work as **proposals** (patch + manifest), accept/reject/revise them, and finally produce **one clean commit** on the real repository — then clean everything up. It never pushes.
|
|
34
|
+
|
|
35
|
+
Two planes are kept strictly separate:
|
|
36
|
+
|
|
37
|
+
- **`.git`** — the source of truth. Untouched until `finalize`, which writes exactly one commit.
|
|
38
|
+
- **`.gitagent/`** — an ephemeral coordination layer: worktrees, patches, manifests, decisions, audit log. Discardable and reset after `finalize`/`abort`.
|
|
39
|
+
|
|
40
|
+
`gitagent` does **not** reimplement Git. Proposals are stored as `git diff` patches plus JSON metadata, never as a second history.
|
|
41
|
+
|
|
42
|
+
## Decision / application split (Option B)
|
|
43
|
+
|
|
44
|
+
`gitagent` cleanly separates **deciding** from **applying**:
|
|
45
|
+
|
|
46
|
+
| Command | Role |
|
|
47
|
+
|---|---|
|
|
48
|
+
| `accept <pid>` | Record the decision "approved". Does **not** apply. |
|
|
49
|
+
| `reject <pid>` | Record "rejected". |
|
|
50
|
+
| `revise <pid>` | Send back to the agent (new `pid` on re-propose). |
|
|
51
|
+
| `integrate` | **Apply** all accepted proposals onto the integration branch. Detects conflicts. |
|
|
52
|
+
| `finalize` | Calls `integrate` (if needed) + creates **one** commit on the current branch. |
|
|
53
|
+
|
|
54
|
+
So a minimal flow is: `accept` each → `finalize`. Run `integrate` standalone when you want to see conflicts *before* the final commit.
|
|
55
|
+
|
|
56
|
+
## Install
|
|
57
|
+
|
|
58
|
+
The PyPI distribution is **`gawt`** (the name `gitagent` on PyPI is a different,
|
|
59
|
+
unrelated project). The command it installs is still called `gitagent`.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# end user (isolated environment)
|
|
63
|
+
pipx install gawt
|
|
64
|
+
# or
|
|
65
|
+
uv tool install gawt
|
|
66
|
+
|
|
67
|
+
# from GitHub
|
|
68
|
+
pipx install "git+https://github.com/david-fm/gawt"
|
|
69
|
+
|
|
70
|
+
# local development
|
|
71
|
+
git clone https://github.com/david-fm/gawt && cd gawt
|
|
72
|
+
uv tool install . -e # or: pipx install -e .
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Requires Python 3.11+ and a working `git` on `PATH`.
|
|
76
|
+
|
|
77
|
+
## Quick start (Option B)
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
gitagent init
|
|
81
|
+
gitagent start --feature "auth-rate-limiting"
|
|
82
|
+
|
|
83
|
+
# each subagent gets an isolated worktree + ephemeral branch
|
|
84
|
+
gitagent spawn --id a_backend --role "implement limiter"
|
|
85
|
+
gitagent spawn --id a_tests --role "write tests"
|
|
86
|
+
|
|
87
|
+
# subagents work in their own folders, then propose (no commit to .git)
|
|
88
|
+
gitagent propose --agent a_backend --title "Token bucket limiter" --confidence 0.85
|
|
89
|
+
gitagent propose --agent a_tests --title "Limiter tests" --confidence 0.9
|
|
90
|
+
|
|
91
|
+
# superagent reviews and marks decisions
|
|
92
|
+
gitagent proposals
|
|
93
|
+
gitagent show p_7f3a
|
|
94
|
+
gitagent accept p_7f3a
|
|
95
|
+
gitagent revise p_9b2c --feedback "add edge cases"
|
|
96
|
+
# ... a_tests re-proposes (a new pid is generated) ...
|
|
97
|
+
gitagent accept p_9b2c
|
|
98
|
+
|
|
99
|
+
# OPTIONAL: pre-flight apply to surface conflicts before committing
|
|
100
|
+
gitagent integrate
|
|
101
|
+
|
|
102
|
+
# consolidate into ONE commit on the current branch, then reset
|
|
103
|
+
gitagent finalize --message "feat(auth): add rate limiting with tests"
|
|
104
|
+
# → 1 local commit on HEAD, .gitagent reset, no push
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Commands
|
|
108
|
+
|
|
109
|
+
| Command | Description |
|
|
110
|
+
| --- | --- |
|
|
111
|
+
| `init` | Create `.gitagent/` and add it to `.gitignore`. |
|
|
112
|
+
| `start --feature <name>` | Open a session anchored at current `HEAD`. |
|
|
113
|
+
| `status [--json]` | Show session, agents, proposals, integration state. |
|
|
114
|
+
| `log [--json]` | Append-only audit trail (`log.jsonl`). |
|
|
115
|
+
| `abort` | Discard everything: remove worktrees/branches, reset `.gitagent`. |
|
|
116
|
+
| `spawn --id <id> [--base <ref>] [--role ...]` | Create an isolated worktree for a subagent. |
|
|
117
|
+
| `list-agents [--json]` | List agents. |
|
|
118
|
+
| `kill <id>` | Remove an agent's worktree and ephemeral branch. |
|
|
119
|
+
| `propose --agent <id> --title ... [--summary ...] [--confidence 0..1]` | Capture worktree changes as a patch proposal. |
|
|
120
|
+
| `proposals [--json]` | List proposals + review state. |
|
|
121
|
+
| `show <id>` | Show a proposal's diff (color). |
|
|
122
|
+
| `diff <id>` | Print a proposal's raw diff (pipe-friendly, for LLMs). |
|
|
123
|
+
| `accept <id>` | Mark a proposal as accepted (decision only; does **not** apply). |
|
|
124
|
+
| `reject <id> [--reason ...]` | Reject a proposal (patch not applied). |
|
|
125
|
+
| `revise <id> --feedback ...` | Send a proposal back for another iteration. |
|
|
126
|
+
| `integrate [--json]` | Apply all accepted proposals onto the integration branch; detect conflicts. |
|
|
127
|
+
| `finalize --message ... [--sign] [--no-reset]` | Call `integrate` + produce one commit on the current branch + reset `.gitagent`. **Never pushes.** |
|
|
128
|
+
| `install-skill` | Install the bundled `gitagent` agent skill into `~/.agents/skills/gitagent`. |
|
|
129
|
+
|
|
130
|
+
## How it works
|
|
131
|
+
|
|
132
|
+
- **Isolation**: `git worktree add` gives every agent its own `HEAD`, index and working tree while sharing the object store — ideal for parallelism.
|
|
133
|
+
- **Proposals as patches**: `git -C <worktree> diff --cached <base_sha> --binary` is stored as `change.patch` and re-applied with `git apply --3way` on the integration worktree. Agent "commits" never pollute the final history.
|
|
134
|
+
- **Integration order = proposal creation order**: the first agent to `propose` integrates first. Plan who proposes first if their change is the "base" others depend on.
|
|
135
|
+
- **Single commit**: `finalize` does `git merge --squash <integration-branch>` on the current branch and creates exactly one commit, then removes all worktrees/branches and resets `.gitagent` (the audit log is kept).
|
|
136
|
+
- **Conflicts**: a conflicting patch is marked `conflict` at `integrate` time — the session stays alive so the superagent can `revise`, fix the integration worktree manually (then re-`accept` + re-`integrate`), or `abort`.
|
|
137
|
+
- **Concurrency**: file locks (`fcntl`) guard review-state writes; `accept` is safe to race (it only marks).
|
|
138
|
+
- **Audit**: every event is appended to `.gitagent/log.jsonl` (parseable by another agent/LLM).
|
|
139
|
+
- **Dual output**: humans get Rich tables; `--json` and `diff` give raw, pipe-friendly output for orchestration.
|
|
140
|
+
|
|
141
|
+
## Layout
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
.gitagent/
|
|
145
|
+
├── session.json # active session: id, feature, base_sha, state
|
|
146
|
+
├── agents/<agent-id>/
|
|
147
|
+
│ ├── meta.json # role, worktree path, ephemeral branch, state
|
|
148
|
+
│ └── worktree/ # git worktree for this agent
|
|
149
|
+
├── proposals/<proposal-id>/
|
|
150
|
+
│ ├── manifest.json # agent, base_sha, files, summary, confidence
|
|
151
|
+
│ ├── change.patch # git diff of the proposal
|
|
152
|
+
│ └── review.json # pending|accepted|integrated|rejected|revise|conflict + feedback
|
|
153
|
+
├── integration/worktree/ # integration branch under construction
|
|
154
|
+
├── locks/ # fcntl lockfiles for concurrent writes
|
|
155
|
+
└── log.jsonl # append-only audit trail
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Proposal states: `pending → accepted → integrated | conflict | revise | rejected`.
|
|
159
|
+
Session states: `open → integrating → finalized | aborted`.
|
|
160
|
+
|
|
161
|
+
## Installing the agent skill
|
|
162
|
+
|
|
163
|
+
`gitagent` ships with a **skill** so any AI agent can use it freely — it teaches another agent the full CLI surface, the Option B decision/application split, conflict resolution recipes, and the supervisor/coder/reviewer patterns.
|
|
164
|
+
|
|
165
|
+
The skill lives in the repo at `skills/gitagent/` (SKILL.md + AGENTS.md) and gets installed into the local opencode skills directory so future agent sessions can load it.
|
|
166
|
+
|
|
167
|
+
### From a gitagent install (pipx / pip / uv tool)
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
gitagent install-skill
|
|
171
|
+
# → installs bundled skill to ~/.agents/skills/gitagent
|
|
172
|
+
# → re-run to refresh after upgrading gitagent
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### From a git clone (developers / source installs)
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
git clone https://github.com/david-fm/gawt
|
|
179
|
+
cd gitagent
|
|
180
|
+
make install-skill
|
|
181
|
+
# (equivalent to: bash scripts/install-skill.sh)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Manual
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
cp -R skills/gitagent ~/.agents/skills/
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
The skill will be available to agents in **future** sessions. Re-run any of the above commands to refresh the installed copy.
|
|
191
|
+
|
|
192
|
+
## Design principle
|
|
193
|
+
|
|
194
|
+
> `finalize` **never** runs `git push`. It produces the local commit and stops. Pushing is entirely the user's responsibility.
|
|
195
|
+
|
|
196
|
+
## License
|
|
197
|
+
|
|
198
|
+
MIT © David Florez Mazuera
|
gawt-0.1.0/README.md
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# gitagent
|
|
2
|
+
|
|
3
|
+
> Agent workspace manager over Git: isolate, review and consolidate multi-agent changes into a **single commit**.
|
|
4
|
+
|
|
5
|
+
`gitagent` is a lightweight CLI that wraps `git worktree` so a **superagent** can spawn isolated **subagents**, collect their work as **proposals** (patch + manifest), accept/reject/revise them, and finally produce **one clean commit** on the real repository — then clean everything up. It never pushes.
|
|
6
|
+
|
|
7
|
+
Two planes are kept strictly separate:
|
|
8
|
+
|
|
9
|
+
- **`.git`** — the source of truth. Untouched until `finalize`, which writes exactly one commit.
|
|
10
|
+
- **`.gitagent/`** — an ephemeral coordination layer: worktrees, patches, manifests, decisions, audit log. Discardable and reset after `finalize`/`abort`.
|
|
11
|
+
|
|
12
|
+
`gitagent` does **not** reimplement Git. Proposals are stored as `git diff` patches plus JSON metadata, never as a second history.
|
|
13
|
+
|
|
14
|
+
## Decision / application split (Option B)
|
|
15
|
+
|
|
16
|
+
`gitagent` cleanly separates **deciding** from **applying**:
|
|
17
|
+
|
|
18
|
+
| Command | Role |
|
|
19
|
+
|---|---|
|
|
20
|
+
| `accept <pid>` | Record the decision "approved". Does **not** apply. |
|
|
21
|
+
| `reject <pid>` | Record "rejected". |
|
|
22
|
+
| `revise <pid>` | Send back to the agent (new `pid` on re-propose). |
|
|
23
|
+
| `integrate` | **Apply** all accepted proposals onto the integration branch. Detects conflicts. |
|
|
24
|
+
| `finalize` | Calls `integrate` (if needed) + creates **one** commit on the current branch. |
|
|
25
|
+
|
|
26
|
+
So a minimal flow is: `accept` each → `finalize`. Run `integrate` standalone when you want to see conflicts *before* the final commit.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
The PyPI distribution is **`gawt`** (the name `gitagent` on PyPI is a different,
|
|
31
|
+
unrelated project). The command it installs is still called `gitagent`.
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# end user (isolated environment)
|
|
35
|
+
pipx install gawt
|
|
36
|
+
# or
|
|
37
|
+
uv tool install gawt
|
|
38
|
+
|
|
39
|
+
# from GitHub
|
|
40
|
+
pipx install "git+https://github.com/david-fm/gawt"
|
|
41
|
+
|
|
42
|
+
# local development
|
|
43
|
+
git clone https://github.com/david-fm/gawt && cd gawt
|
|
44
|
+
uv tool install . -e # or: pipx install -e .
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Requires Python 3.11+ and a working `git` on `PATH`.
|
|
48
|
+
|
|
49
|
+
## Quick start (Option B)
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
gitagent init
|
|
53
|
+
gitagent start --feature "auth-rate-limiting"
|
|
54
|
+
|
|
55
|
+
# each subagent gets an isolated worktree + ephemeral branch
|
|
56
|
+
gitagent spawn --id a_backend --role "implement limiter"
|
|
57
|
+
gitagent spawn --id a_tests --role "write tests"
|
|
58
|
+
|
|
59
|
+
# subagents work in their own folders, then propose (no commit to .git)
|
|
60
|
+
gitagent propose --agent a_backend --title "Token bucket limiter" --confidence 0.85
|
|
61
|
+
gitagent propose --agent a_tests --title "Limiter tests" --confidence 0.9
|
|
62
|
+
|
|
63
|
+
# superagent reviews and marks decisions
|
|
64
|
+
gitagent proposals
|
|
65
|
+
gitagent show p_7f3a
|
|
66
|
+
gitagent accept p_7f3a
|
|
67
|
+
gitagent revise p_9b2c --feedback "add edge cases"
|
|
68
|
+
# ... a_tests re-proposes (a new pid is generated) ...
|
|
69
|
+
gitagent accept p_9b2c
|
|
70
|
+
|
|
71
|
+
# OPTIONAL: pre-flight apply to surface conflicts before committing
|
|
72
|
+
gitagent integrate
|
|
73
|
+
|
|
74
|
+
# consolidate into ONE commit on the current branch, then reset
|
|
75
|
+
gitagent finalize --message "feat(auth): add rate limiting with tests"
|
|
76
|
+
# → 1 local commit on HEAD, .gitagent reset, no push
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Commands
|
|
80
|
+
|
|
81
|
+
| Command | Description |
|
|
82
|
+
| --- | --- |
|
|
83
|
+
| `init` | Create `.gitagent/` and add it to `.gitignore`. |
|
|
84
|
+
| `start --feature <name>` | Open a session anchored at current `HEAD`. |
|
|
85
|
+
| `status [--json]` | Show session, agents, proposals, integration state. |
|
|
86
|
+
| `log [--json]` | Append-only audit trail (`log.jsonl`). |
|
|
87
|
+
| `abort` | Discard everything: remove worktrees/branches, reset `.gitagent`. |
|
|
88
|
+
| `spawn --id <id> [--base <ref>] [--role ...]` | Create an isolated worktree for a subagent. |
|
|
89
|
+
| `list-agents [--json]` | List agents. |
|
|
90
|
+
| `kill <id>` | Remove an agent's worktree and ephemeral branch. |
|
|
91
|
+
| `propose --agent <id> --title ... [--summary ...] [--confidence 0..1]` | Capture worktree changes as a patch proposal. |
|
|
92
|
+
| `proposals [--json]` | List proposals + review state. |
|
|
93
|
+
| `show <id>` | Show a proposal's diff (color). |
|
|
94
|
+
| `diff <id>` | Print a proposal's raw diff (pipe-friendly, for LLMs). |
|
|
95
|
+
| `accept <id>` | Mark a proposal as accepted (decision only; does **not** apply). |
|
|
96
|
+
| `reject <id> [--reason ...]` | Reject a proposal (patch not applied). |
|
|
97
|
+
| `revise <id> --feedback ...` | Send a proposal back for another iteration. |
|
|
98
|
+
| `integrate [--json]` | Apply all accepted proposals onto the integration branch; detect conflicts. |
|
|
99
|
+
| `finalize --message ... [--sign] [--no-reset]` | Call `integrate` + produce one commit on the current branch + reset `.gitagent`. **Never pushes.** |
|
|
100
|
+
| `install-skill` | Install the bundled `gitagent` agent skill into `~/.agents/skills/gitagent`. |
|
|
101
|
+
|
|
102
|
+
## How it works
|
|
103
|
+
|
|
104
|
+
- **Isolation**: `git worktree add` gives every agent its own `HEAD`, index and working tree while sharing the object store — ideal for parallelism.
|
|
105
|
+
- **Proposals as patches**: `git -C <worktree> diff --cached <base_sha> --binary` is stored as `change.patch` and re-applied with `git apply --3way` on the integration worktree. Agent "commits" never pollute the final history.
|
|
106
|
+
- **Integration order = proposal creation order**: the first agent to `propose` integrates first. Plan who proposes first if their change is the "base" others depend on.
|
|
107
|
+
- **Single commit**: `finalize` does `git merge --squash <integration-branch>` on the current branch and creates exactly one commit, then removes all worktrees/branches and resets `.gitagent` (the audit log is kept).
|
|
108
|
+
- **Conflicts**: a conflicting patch is marked `conflict` at `integrate` time — the session stays alive so the superagent can `revise`, fix the integration worktree manually (then re-`accept` + re-`integrate`), or `abort`.
|
|
109
|
+
- **Concurrency**: file locks (`fcntl`) guard review-state writes; `accept` is safe to race (it only marks).
|
|
110
|
+
- **Audit**: every event is appended to `.gitagent/log.jsonl` (parseable by another agent/LLM).
|
|
111
|
+
- **Dual output**: humans get Rich tables; `--json` and `diff` give raw, pipe-friendly output for orchestration.
|
|
112
|
+
|
|
113
|
+
## Layout
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
.gitagent/
|
|
117
|
+
├── session.json # active session: id, feature, base_sha, state
|
|
118
|
+
├── agents/<agent-id>/
|
|
119
|
+
│ ├── meta.json # role, worktree path, ephemeral branch, state
|
|
120
|
+
│ └── worktree/ # git worktree for this agent
|
|
121
|
+
├── proposals/<proposal-id>/
|
|
122
|
+
│ ├── manifest.json # agent, base_sha, files, summary, confidence
|
|
123
|
+
│ ├── change.patch # git diff of the proposal
|
|
124
|
+
│ └── review.json # pending|accepted|integrated|rejected|revise|conflict + feedback
|
|
125
|
+
├── integration/worktree/ # integration branch under construction
|
|
126
|
+
├── locks/ # fcntl lockfiles for concurrent writes
|
|
127
|
+
└── log.jsonl # append-only audit trail
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Proposal states: `pending → accepted → integrated | conflict | revise | rejected`.
|
|
131
|
+
Session states: `open → integrating → finalized | aborted`.
|
|
132
|
+
|
|
133
|
+
## Installing the agent skill
|
|
134
|
+
|
|
135
|
+
`gitagent` ships with a **skill** so any AI agent can use it freely — it teaches another agent the full CLI surface, the Option B decision/application split, conflict resolution recipes, and the supervisor/coder/reviewer patterns.
|
|
136
|
+
|
|
137
|
+
The skill lives in the repo at `skills/gitagent/` (SKILL.md + AGENTS.md) and gets installed into the local opencode skills directory so future agent sessions can load it.
|
|
138
|
+
|
|
139
|
+
### From a gitagent install (pipx / pip / uv tool)
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
gitagent install-skill
|
|
143
|
+
# → installs bundled skill to ~/.agents/skills/gitagent
|
|
144
|
+
# → re-run to refresh after upgrading gitagent
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### From a git clone (developers / source installs)
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
git clone https://github.com/david-fm/gawt
|
|
151
|
+
cd gitagent
|
|
152
|
+
make install-skill
|
|
153
|
+
# (equivalent to: bash scripts/install-skill.sh)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Manual
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
cp -R skills/gitagent ~/.agents/skills/
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The skill will be available to agents in **future** sessions. Re-run any of the above commands to refresh the installed copy.
|
|
163
|
+
|
|
164
|
+
## Design principle
|
|
165
|
+
|
|
166
|
+
> `finalize` **never** runs `git push`. It produces the local commit and stops. Pushing is entirely the user's responsibility.
|
|
167
|
+
|
|
168
|
+
## License
|
|
169
|
+
|
|
170
|
+
MIT © David Florez Mazuera
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "gawt"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Agent workspace manager over Git: isolate, review and consolidate multi-agent changes into a single commit."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = { text = "MIT" }
|
|
7
|
+
requires-python = ">=3.11"
|
|
8
|
+
authors = [{ name = "David Florez Mazuera" }]
|
|
9
|
+
keywords = ["git", "worktree", "ai-agents", "llm", "cli", "multi-agent"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"License :: OSI Approved :: MIT License",
|
|
12
|
+
"Programming Language :: Python :: 3",
|
|
13
|
+
"Programming Language :: Python :: 3.11",
|
|
14
|
+
"Programming Language :: Python :: 3.12",
|
|
15
|
+
"Programming Language :: Python :: 3.13",
|
|
16
|
+
"Environment :: Console",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Topic :: Software Development :: Version Control :: Git",
|
|
19
|
+
"Topic :: Software Development :: Pre-processors",
|
|
20
|
+
]
|
|
21
|
+
dependencies = [
|
|
22
|
+
"typer>=0.12",
|
|
23
|
+
"rich>=13",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.optional-dependencies]
|
|
27
|
+
dev = ["pytest>=8", "ruff>=0.5"]
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/david-fm/gawt"
|
|
31
|
+
Repository = "https://github.com/david-fm/gawt"
|
|
32
|
+
Issues = "https://github.com/david-fm/gawt/issues"
|
|
33
|
+
|
|
34
|
+
[project.scripts]
|
|
35
|
+
gitagent = "gitagent.cli:app"
|
|
36
|
+
|
|
37
|
+
[build-system]
|
|
38
|
+
requires = ["hatchling"]
|
|
39
|
+
build-backend = "hatchling.build"
|
|
40
|
+
|
|
41
|
+
[tool.hatch.build.targets.wheel]
|
|
42
|
+
packages = ["src/gitagent"]
|
|
43
|
+
artifacts = ["_skills"]
|
|
44
|
+
|
|
45
|
+
[tool.hatch.build.targets.wheel.force-include]
|
|
46
|
+
"skills/gitagent" = "gitagent/_skills/gitagent"
|
|
47
|
+
|
|
48
|
+
[tool.ruff]
|
|
49
|
+
line-length = 100
|
|
50
|
+
target-version = "py311"
|
|
51
|
+
|
|
52
|
+
[tool.ruff.lint]
|
|
53
|
+
select = ["E", "F", "I", "UP", "B", "SIM"]
|
|
54
|
+
|
|
55
|
+
[tool.ruff.lint.per-file-ignores]
|
|
56
|
+
"tests/*" = ["E501"]
|
|
57
|
+
|
|
58
|
+
[tool.pytest.ini_options]
|
|
59
|
+
testpaths = ["tests"]
|
|
60
|
+
addopts = "-q"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Install the gitagent agent skill into the local opencode skills directory.
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# bash scripts/install-skill.sh
|
|
6
|
+
#
|
|
7
|
+
# The skill is copied to ~/.agents/skills/gitagent/. After this, any agent
|
|
8
|
+
# you run in a future session that triggers the `gitagent` skill will see it.
|
|
9
|
+
#
|
|
10
|
+
# Re-run this script to refresh the installed copy (e.g. after pulling new
|
|
11
|
+
# changes to the skill in this repo).
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
|
|
14
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
15
|
+
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
16
|
+
SKILL_SRC="$REPO_DIR/skills/gitagent"
|
|
17
|
+
SKILL_DST="${HOME}/.agents/skills/gitagent"
|
|
18
|
+
|
|
19
|
+
if [ ! -d "$SKILL_SRC" ]; then
|
|
20
|
+
echo "error: skill source not found at $SKILL_SRC" >&2
|
|
21
|
+
echo "Run this from inside the gitagent repository." >&2
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
mkdir -p "$(dirname "$SKILL_DST")"
|
|
26
|
+
rm -rf "$SKILL_DST"
|
|
27
|
+
cp -R "$SKILL_SRC" "$SKILL_DST"
|
|
28
|
+
echo "Installed gitagent skill to $SKILL_DST"
|
|
29
|
+
echo "It will be available to agents in future sessions."
|