gitgotchi 0.3.0__tar.gz → 0.4.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.
- gitgotchi-0.4.0/CHANGELOG.md +110 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/MANIFEST.in +1 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/PKG-INFO +64 -8
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/README.md +60 -6
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/pyproject.toml +5 -1
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/__init__.py +1 -1
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/cli.py +147 -10
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/db/models.py +27 -16
- gitgotchi-0.4.0/src/gitgotchi/db/state_manager.py +311 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/demo.py +8 -3
- gitgotchi-0.4.0/src/gitgotchi/doctor.py +190 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/haunt.py +87 -26
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/history.py +39 -4
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/hooks/installer.py +21 -7
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/hooks/post_commit.py +32 -5
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/paths.py +20 -20
- gitgotchi-0.4.0/src/gitgotchi/pet/possession.py +84 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/pet/renderer.py +31 -5
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/pet/states.py +16 -13
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/render/__init__.py +2 -1
- gitgotchi-0.4.0/src/gitgotchi/render/badge.py +99 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/render/card.py +19 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/render/forms.py +25 -9
- gitgotchi-0.4.0/src/gitgotchi/render/raster.py +341 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/seance/hauntings.py +45 -20
- gitgotchi-0.4.0/src/gitgotchi/statusline.py +191 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi.egg-info/PKG-INFO +64 -8
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi.egg-info/SOURCES.txt +5 -1
- gitgotchi-0.4.0/src/gitgotchi.egg-info/entry_points.txt +3 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi.egg-info/requires.txt +4 -1
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/tests/test_cli.py +26 -0
- gitgotchi-0.4.0/tests/test_features.py +78 -0
- gitgotchi-0.4.0/tests/test_haunt.py +130 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/tests/test_hauntings.py +17 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/tests/test_history.py +1 -1
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/tests/test_hooks.py +59 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/tests/test_possession.py +13 -0
- gitgotchi-0.4.0/tests/test_state_manager.py +174 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/tests/test_states.py +12 -0
- gitgotchi-0.4.0/tests/test_statusline.py +60 -0
- gitgotchi-0.3.0/CHANGELOG.md +0 -57
- gitgotchi-0.3.0/src/gitgotchi/db/migrations.py +0 -100
- gitgotchi-0.3.0/src/gitgotchi/db/state_manager.py +0 -383
- gitgotchi-0.3.0/src/gitgotchi/pet/possession.py +0 -56
- gitgotchi-0.3.0/src/gitgotchi/render/raster.py +0 -217
- gitgotchi-0.3.0/src/gitgotchi.egg-info/entry_points.txt +0 -2
- gitgotchi-0.3.0/tests/test_haunt.py +0 -54
- gitgotchi-0.3.0/tests/test_state_manager.py +0 -73
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/INSTALL.md +0 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/LICENSE +0 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/setup.cfg +0 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/__main__.py +0 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/db/__init__.py +0 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/hooks/__init__.py +0 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/pet/__init__.py +0 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/pet/quality.py +0 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/pet/theme.py +0 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/render/effects.py +0 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/render/font.py +0 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/render/scene.py +0 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/render/terminal.py +0 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/seance/__init__.py +0 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/seance/story_gen.py +0 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi.egg-info/dependency_links.txt +0 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi.egg-info/top_level.txt +0 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/tests/conftest.py +0 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/tests/test_quality.py +0 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/tests/test_render.py +0 -0
- {gitgotchi-0.3.0 → gitgotchi-0.4.0}/tests/test_story.py +0 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
### New
|
|
6
|
+
|
|
7
|
+
- `gitgotchi-statusline`: one line about your companion for Claude Code's status line, tmux or
|
|
8
|
+
Starship. It uses only the standard library, so it runs in about 50 ms, and prints nothing outside
|
|
9
|
+
a haunted repo. `gitgotchi statusline --setup` shows the config snippets.
|
|
10
|
+
- `gitgotchi badge`: an SVG README badge with a pixel portrait of your companion and its form.
|
|
11
|
+
- `gitgotchi config full|line|off [--global]`: choose how much the hook prints after each commit.
|
|
12
|
+
- `gitgotchi doctor`: checks git, the hook and its Python, the database, your terminal and the
|
|
13
|
+
API key, with a fix for each problem. It never prints the key.
|
|
14
|
+
- `gitgotchi card --gif`: an animated card (install `gitgotchi[gif]` for Pillow).
|
|
15
|
+
|
|
16
|
+
## 0.3.2
|
|
17
|
+
|
|
18
|
+
### Sharper sprites
|
|
19
|
+
|
|
20
|
+
- Every form is framed bigger, so it gets about 60% more pixels in the same space.
|
|
21
|
+
- Shading uses hue-shifted color ramps (cool shadows, warm highlights) lit from the upper front, with
|
|
22
|
+
soft contact shadows, so forms look round instead of striped.
|
|
23
|
+
- Glossy highlights on the pumpkin, the Demon and the Poltergeist's keycaps.
|
|
24
|
+
- Outlines pick up a little of the color they border, and dark inner lines separate parts that
|
|
25
|
+
overlap (arms, horns, keycaps).
|
|
26
|
+
- A cleanup pass removes one-pixel spurs, notches and stray shading pixels.
|
|
27
|
+
- The pumpkin's shadows are warm instead of muddy, the Demon has bigger curved horns, and the Angel
|
|
28
|
+
has feathered wings and a halo that stays in frame.
|
|
29
|
+
|
|
30
|
+
## 0.3.1
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- **Security:** GitGotchi loaded every variable from a repo's `.env` file, so a cloned repo could
|
|
35
|
+
inject git settings and run commands during `gitgotchi seance`. Now only `ANTHROPIC_API_KEY` is read.
|
|
36
|
+
- AI-agent detection no longer flags people: co-authors at Anthropic, OpenAI or Gemini, anyone named
|
|
37
|
+
Cline, or messages that merely mention ChatGPT. It now matches agent noreply addresses and bot accounts.
|
|
38
|
+
- The pet now lives in `.git/gitgotchi/`, so every worktree of a repo shares one pet (agents often
|
|
39
|
+
commit from worktrees). Existing pets are moved there automatically.
|
|
40
|
+
- `haunt` watches every local branch, doesn't feed commits that arrive by pull, groups bursts of
|
|
41
|
+
commits into one reaction, keeps running through a busy database, and no longer crashes on
|
|
42
|
+
brackets in commit messages.
|
|
43
|
+
- Amending a commit no longer counts it again.
|
|
44
|
+
- Running `adopt` while a commit is being processed could lose an update; writes are now atomic.
|
|
45
|
+
- `adopt` on an existing pet no longer puts it to sleep.
|
|
46
|
+
- The séance handles non-ASCII file names, renamed files, and more TODOs in big repos; the
|
|
47
|
+
witching hour uses each author's own timezone.
|
|
48
|
+
- Petting a dormant pet now really wakes it, and friendship grows even while it sleeps.
|
|
49
|
+
- The hook works when Python's path contains spaces or shell characters, runs before any `exit` in
|
|
50
|
+
an existing hook, and refuses to edit non-shell hooks.
|
|
51
|
+
- Repos in folders with `?` or `%` in their path work.
|
|
52
|
+
- `gitgotchi name` rejects empty and very long names; `card` reports the size it actually saved;
|
|
53
|
+
`adopt --limit` must be at least 1.
|
|
54
|
+
- Requires `typer>=0.15.4`: older versions crash with current `click`.
|
|
55
|
+
|
|
56
|
+
## 0.3.0 - Halloween
|
|
57
|
+
|
|
58
|
+
### New
|
|
59
|
+
|
|
60
|
+
- **3D pixel-art companion.** Every form is a 3D model, raymarched and cel-shaded into pixel art and
|
|
61
|
+
drawn in the terminal with true-color half blocks. It bobs, blinks and spins, on a night-sky stage.
|
|
62
|
+
- **Halloween all year.** Pumpkin replaces Egg, and everything got a new look and voice: cute early
|
|
63
|
+
forms, eerie Demon, Wraith and Poltergeist.
|
|
64
|
+
- **Possession.** Commits written by AI agents are recognised from their trailers, footers and bot
|
|
65
|
+
accounts. They fill a possession meter and unlock the new **Poltergeist** form.
|
|
66
|
+
- `gitgotchi haunt`: a live pane that reacts to every commit as it lands, with sparkles, possession
|
|
67
|
+
glitches for agent commits, and evolution animations.
|
|
68
|
+
- `gitgotchi adopt`: raise your companion on the history a repo already has.
|
|
69
|
+
- `gitgotchi card`: a shareable 1200x630 PNG.
|
|
70
|
+
- `gitgotchi seance`: ghost stories built from your repo's real history (oldest TODO, forgotten files,
|
|
71
|
+
biggest deletion, late-night commits, the first agent commit). `story` still works as an alias.
|
|
72
|
+
- `gitgotchi demo`: watch a throwaway repo get haunted.
|
|
73
|
+
|
|
74
|
+
### Changed
|
|
75
|
+
|
|
76
|
+
- The hook prints a single line when git isn't attached to a terminal, so agents running `git commit`
|
|
77
|
+
don't get the full pet in their output.
|
|
78
|
+
- Evolution plays an animation in real terminals.
|
|
79
|
+
- `numpy` is now a dependency.
|
|
80
|
+
- Databases from 0.2.0 are upgraded automatically.
|
|
81
|
+
|
|
82
|
+
## 0.2.0
|
|
83
|
+
|
|
84
|
+
### Fixed
|
|
85
|
+
|
|
86
|
+
- The package is now installed as `gitgotchi` instead of `src`. The old name collided with any
|
|
87
|
+
project that has its own `src/` package and made the post-commit hook fail there.
|
|
88
|
+
- `gitgotchi uninstall` now really removes the hook (it used to leave the command behind).
|
|
89
|
+
- Running a command from a subdirectory no longer creates a second, empty companion.
|
|
90
|
+
- `.gitgotchi/` is excluded from git automatically so the database can't be committed by accident.
|
|
91
|
+
- Mood now reflects how long it's been since the last commit when you check on your companion.
|
|
92
|
+
- Angel was unreachable; quality is now scored from each commit (see README).
|
|
93
|
+
- `gitgotchi story` uses a current Claude model and shows why when it falls back to a built-in story.
|
|
94
|
+
- `.env` files are actually loaded.
|
|
95
|
+
- `gitgotchi version` reports the real version.
|
|
96
|
+
|
|
97
|
+
### Changed
|
|
98
|
+
|
|
99
|
+
- Hooks respect `core.hooksPath` and git worktrees, and are kept alongside any existing hook.
|
|
100
|
+
Running `gitgotchi install` again updates an old hook in place.
|
|
101
|
+
- Requires `anthropic>=1.0` and `typer>=0.12`.
|
|
102
|
+
- Added `python -m gitgotchi`.
|
|
103
|
+
|
|
104
|
+
## 0.1.3
|
|
105
|
+
|
|
106
|
+
- PyPI README update.
|
|
107
|
+
|
|
108
|
+
## 0.1.0 - 0.1.2
|
|
109
|
+
|
|
110
|
+
- Initial release.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitgotchi
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: A 3D pixel-art Halloween spirit that haunts your git repo and reacts as you (and your AI agents) commit
|
|
5
5
|
Author-email: M K Sai Srikar <Srikarmks@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -28,9 +28,11 @@ Requires-Dist: rich>=13.7.0
|
|
|
28
28
|
Requires-Dist: gitpython>=3.1.40
|
|
29
29
|
Requires-Dist: anthropic>=1.0.0
|
|
30
30
|
Requires-Dist: sqlalchemy>=2.0.36
|
|
31
|
-
Requires-Dist: typer>=0.
|
|
31
|
+
Requires-Dist: typer>=0.15.4
|
|
32
32
|
Requires-Dist: python-dotenv>=1.0.0
|
|
33
33
|
Requires-Dist: numpy>=1.24
|
|
34
|
+
Provides-Extra: gif
|
|
35
|
+
Requires-Dist: pillow>=10.0; extra == "gif"
|
|
34
36
|
Provides-Extra: dev
|
|
35
37
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
36
38
|
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
@@ -73,7 +75,9 @@ gitgotchi haunt # open the live pane
|
|
|
73
75
|
```
|
|
74
76
|
|
|
75
77
|
Keep `gitgotchi haunt` open in a split next to the terminal where you or your agent are working. Every
|
|
76
|
-
commit gets a reaction, agent commits glitch the screen, and evolutions play out live.
|
|
78
|
+
commit gets a reaction, agent commits glitch the screen, and evolutions play out live. It watches every
|
|
79
|
+
local branch, so it sees commits from other worktrees too. Commits that arrive by `git pull` aren't yours,
|
|
80
|
+
so they don't feed your companion.
|
|
77
81
|
|
|
78
82
|

|
|
79
83
|
|
|
@@ -93,10 +97,55 @@ single line instead of the full pet, so it doesn't flood the agent's context.
|
|
|
93
97
|
gitgotchi card
|
|
94
98
|
```
|
|
95
99
|
|
|
96
|
-
Saves a 1200×630 card, sized for LinkedIn and other link previews
|
|
100
|
+
Saves a 1200×630 card, sized for LinkedIn and other link previews. Add `--gif` for one where your companion
|
|
101
|
+
moves (needs `pip install "gitgotchi[gif]"`).
|
|
97
102
|
|
|
98
103
|

|
|
99
104
|
|
|
105
|
+
For your README, `gitgotchi badge` saves a small SVG with your companion's portrait and form, and prints the
|
|
106
|
+
Markdown to paste in. Run it again whenever your companion evolves.
|
|
107
|
+
|
|
108
|
+
## 📟 In your status bar
|
|
109
|
+
|
|
110
|
+
`gitgotchi-statusline` prints one line about the companion in the current repo, for example
|
|
111
|
+
`👾 Boo the Poltergeist · 63% possessed · hungry`. It starts in about 50 ms and prints nothing outside a
|
|
112
|
+
haunted repo, so it's safe to leave on everywhere.
|
|
113
|
+
|
|
114
|
+
Claude Code: add this to `~/.claude/settings.json`
|
|
115
|
+
|
|
116
|
+
```json
|
|
117
|
+
"statusLine": {"type": "command", "command": "gitgotchi-statusline"}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
tmux: add this to `~/.tmux.conf`
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
set -g status-right '#(cd #{pane_current_path} && gitgotchi-statusline --plain)'
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`gitgotchi-statusline --setup` prints these snippets, plus one for Starship.
|
|
127
|
+
|
|
128
|
+
## 🔕 How much the hook says
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
gitgotchi config line # one line per commit in this repo
|
|
132
|
+
gitgotchi config off --global # silent everywhere
|
|
133
|
+
gitgotchi config full # the whole pet (the default)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
It's stored in git config as `gitgotchi.output`, so a repo's setting wins over the global one. When an agent
|
|
137
|
+
runs `git commit` without a terminal, the hook always keeps it to one line.
|
|
138
|
+
|
|
139
|
+
## 🩺 Something not working?
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
gitgotchi doctor
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Checks git, the hook and the Python it points at, the pet database, your terminal's colors and size, and
|
|
146
|
+
whether stories can use Claude, with a fix for anything that's wrong. Include its output when you open an
|
|
147
|
+
issue; it never shows your API key.
|
|
148
|
+
|
|
100
149
|
## 🔮 Séance
|
|
101
150
|
|
|
102
151
|
```bash
|
|
@@ -106,7 +155,9 @@ gitgotchi seance
|
|
|
106
155
|
Your companion digs through the history for its ghosts: the oldest TODO still in the code, the file
|
|
107
156
|
nobody has opened in ages, the biggest deletion, commits made between midnight and 4am, and the first
|
|
108
157
|
commit an AI agent wrote. Then it tells you a story about them. Set `ANTHROPIC_API_KEY` (or put it in a
|
|
109
|
-
`.env` file) to have Claude tell it; otherwise your companion tells it in its own words.
|
|
158
|
+
`.env` file) to have Claude tell it; otherwise your companion tells it in its own words. With a key set,
|
|
159
|
+
the findings (file names, TODO text and recent commit messages) are sent to the Anthropic API to write the
|
|
160
|
+
story. GitGotchi reads only `ANTHROPIC_API_KEY` from `.env` and ignores everything else in it.
|
|
110
161
|
|
|
111
162
|
## 🎮 All commands
|
|
112
163
|
|
|
@@ -118,7 +169,11 @@ commit an AI agent wrote. Then it tells you a story about them. Set `ANTHROPIC_A
|
|
|
118
169
|
| `gitgotchi haunt` | Live pane that reacts to every commit |
|
|
119
170
|
| `gitgotchi status` | Your companion and its stats |
|
|
120
171
|
| `gitgotchi seance` | Ghost stories from your repo's history |
|
|
121
|
-
| `gitgotchi card` | Save a shareable
|
|
172
|
+
| `gitgotchi card` | Save a shareable card (`--gif` for an animated one) |
|
|
173
|
+
| `gitgotchi badge` | Save a README badge with your companion's portrait |
|
|
174
|
+
| `gitgotchi-statusline` | One line for Claude Code, tmux or Starship |
|
|
175
|
+
| `gitgotchi config` | How much the hook prints: `full`, `line` or `off` |
|
|
176
|
+
| `gitgotchi doctor` | Check your setup and get fixes |
|
|
122
177
|
| `gitgotchi evolve` | Evolution paths and progress |
|
|
123
178
|
| `gitgotchi pet` | Give it some affection |
|
|
124
179
|
| `gitgotchi name <name>` | Name your companion |
|
|
@@ -158,8 +213,9 @@ also shows the image inline in terminals that support it. Set `GITGOTCHI_GRAPHIC
|
|
|
158
213
|
|
|
159
214
|
## 🗂️ Where state lives
|
|
160
215
|
|
|
161
|
-
Your companion
|
|
162
|
-
|
|
216
|
+
Your companion lives in `.git/gitgotchi/state.db`, inside the repository's git directory. It never
|
|
217
|
+
shows up in `git status`, can't be committed, and every `git worktree` of the repo shares the same pet, so
|
|
218
|
+
commits your agents make in their own worktrees count too.
|
|
163
219
|
|
|
164
220
|
## 🛠️ Development
|
|
165
221
|
|
|
@@ -32,7 +32,9 @@ gitgotchi haunt # open the live pane
|
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
Keep `gitgotchi haunt` open in a split next to the terminal where you or your agent are working. Every
|
|
35
|
-
commit gets a reaction, agent commits glitch the screen, and evolutions play out live.
|
|
35
|
+
commit gets a reaction, agent commits glitch the screen, and evolutions play out live. It watches every
|
|
36
|
+
local branch, so it sees commits from other worktrees too. Commits that arrive by `git pull` aren't yours,
|
|
37
|
+
so they don't feed your companion.
|
|
36
38
|
|
|
37
39
|

|
|
38
40
|
|
|
@@ -52,10 +54,55 @@ single line instead of the full pet, so it doesn't flood the agent's context.
|
|
|
52
54
|
gitgotchi card
|
|
53
55
|
```
|
|
54
56
|
|
|
55
|
-
Saves a 1200×630 card, sized for LinkedIn and other link previews
|
|
57
|
+
Saves a 1200×630 card, sized for LinkedIn and other link previews. Add `--gif` for one where your companion
|
|
58
|
+
moves (needs `pip install "gitgotchi[gif]"`).
|
|
56
59
|
|
|
57
60
|

|
|
58
61
|
|
|
62
|
+
For your README, `gitgotchi badge` saves a small SVG with your companion's portrait and form, and prints the
|
|
63
|
+
Markdown to paste in. Run it again whenever your companion evolves.
|
|
64
|
+
|
|
65
|
+
## 📟 In your status bar
|
|
66
|
+
|
|
67
|
+
`gitgotchi-statusline` prints one line about the companion in the current repo, for example
|
|
68
|
+
`👾 Boo the Poltergeist · 63% possessed · hungry`. It starts in about 50 ms and prints nothing outside a
|
|
69
|
+
haunted repo, so it's safe to leave on everywhere.
|
|
70
|
+
|
|
71
|
+
Claude Code: add this to `~/.claude/settings.json`
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
"statusLine": {"type": "command", "command": "gitgotchi-statusline"}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
tmux: add this to `~/.tmux.conf`
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
set -g status-right '#(cd #{pane_current_path} && gitgotchi-statusline --plain)'
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`gitgotchi-statusline --setup` prints these snippets, plus one for Starship.
|
|
84
|
+
|
|
85
|
+
## 🔕 How much the hook says
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
gitgotchi config line # one line per commit in this repo
|
|
89
|
+
gitgotchi config off --global # silent everywhere
|
|
90
|
+
gitgotchi config full # the whole pet (the default)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
It's stored in git config as `gitgotchi.output`, so a repo's setting wins over the global one. When an agent
|
|
94
|
+
runs `git commit` without a terminal, the hook always keeps it to one line.
|
|
95
|
+
|
|
96
|
+
## 🩺 Something not working?
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
gitgotchi doctor
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Checks git, the hook and the Python it points at, the pet database, your terminal's colors and size, and
|
|
103
|
+
whether stories can use Claude, with a fix for anything that's wrong. Include its output when you open an
|
|
104
|
+
issue; it never shows your API key.
|
|
105
|
+
|
|
59
106
|
## 🔮 Séance
|
|
60
107
|
|
|
61
108
|
```bash
|
|
@@ -65,7 +112,9 @@ gitgotchi seance
|
|
|
65
112
|
Your companion digs through the history for its ghosts: the oldest TODO still in the code, the file
|
|
66
113
|
nobody has opened in ages, the biggest deletion, commits made between midnight and 4am, and the first
|
|
67
114
|
commit an AI agent wrote. Then it tells you a story about them. Set `ANTHROPIC_API_KEY` (or put it in a
|
|
68
|
-
`.env` file) to have Claude tell it; otherwise your companion tells it in its own words.
|
|
115
|
+
`.env` file) to have Claude tell it; otherwise your companion tells it in its own words. With a key set,
|
|
116
|
+
the findings (file names, TODO text and recent commit messages) are sent to the Anthropic API to write the
|
|
117
|
+
story. GitGotchi reads only `ANTHROPIC_API_KEY` from `.env` and ignores everything else in it.
|
|
69
118
|
|
|
70
119
|
## 🎮 All commands
|
|
71
120
|
|
|
@@ -77,7 +126,11 @@ commit an AI agent wrote. Then it tells you a story about them. Set `ANTHROPIC_A
|
|
|
77
126
|
| `gitgotchi haunt` | Live pane that reacts to every commit |
|
|
78
127
|
| `gitgotchi status` | Your companion and its stats |
|
|
79
128
|
| `gitgotchi seance` | Ghost stories from your repo's history |
|
|
80
|
-
| `gitgotchi card` | Save a shareable
|
|
129
|
+
| `gitgotchi card` | Save a shareable card (`--gif` for an animated one) |
|
|
130
|
+
| `gitgotchi badge` | Save a README badge with your companion's portrait |
|
|
131
|
+
| `gitgotchi-statusline` | One line for Claude Code, tmux or Starship |
|
|
132
|
+
| `gitgotchi config` | How much the hook prints: `full`, `line` or `off` |
|
|
133
|
+
| `gitgotchi doctor` | Check your setup and get fixes |
|
|
81
134
|
| `gitgotchi evolve` | Evolution paths and progress |
|
|
82
135
|
| `gitgotchi pet` | Give it some affection |
|
|
83
136
|
| `gitgotchi name <name>` | Name your companion |
|
|
@@ -117,8 +170,9 @@ also shows the image inline in terminals that support it. Set `GITGOTCHI_GRAPHIC
|
|
|
117
170
|
|
|
118
171
|
## 🗂️ Where state lives
|
|
119
172
|
|
|
120
|
-
Your companion
|
|
121
|
-
|
|
173
|
+
Your companion lives in `.git/gitgotchi/state.db`, inside the repository's git directory. It never
|
|
174
|
+
shows up in `git status`, can't be committed, and every `git worktree` of the repo shares the same pet, so
|
|
175
|
+
commits your agents make in their own worktrees count too.
|
|
122
176
|
|
|
123
177
|
## 🛠️ Development
|
|
124
178
|
|
|
@@ -44,12 +44,15 @@ dependencies = [
|
|
|
44
44
|
"gitpython>=3.1.40",
|
|
45
45
|
"anthropic>=1.0.0",
|
|
46
46
|
"sqlalchemy>=2.0.36",
|
|
47
|
-
"typer>=0.
|
|
47
|
+
"typer>=0.15.4",
|
|
48
48
|
"python-dotenv>=1.0.0",
|
|
49
49
|
"numpy>=1.24",
|
|
50
50
|
]
|
|
51
51
|
|
|
52
52
|
[project.optional-dependencies]
|
|
53
|
+
gif = [
|
|
54
|
+
"pillow>=10.0",
|
|
55
|
+
]
|
|
53
56
|
dev = [
|
|
54
57
|
"pytest>=8.0",
|
|
55
58
|
"pytest-cov>=5.0",
|
|
@@ -60,6 +63,7 @@ dev = [
|
|
|
60
63
|
|
|
61
64
|
[project.scripts]
|
|
62
65
|
gitgotchi = "gitgotchi.cli:cli"
|
|
66
|
+
gitgotchi-statusline = "gitgotchi.statusline:main"
|
|
63
67
|
|
|
64
68
|
[project.urls]
|
|
65
69
|
Homepage = "https://github.com/Srikarmk/Gitgotchi"
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"""Main CLI interface."""
|
|
2
|
+
import os
|
|
2
3
|
import random
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
from typing import Optional
|
|
5
6
|
|
|
6
7
|
import typer
|
|
7
|
-
from dotenv import
|
|
8
|
+
from dotenv import dotenv_values, find_dotenv
|
|
8
9
|
from rich.panel import Panel
|
|
9
10
|
from rich.table import Table
|
|
10
11
|
from rich.text import Text
|
|
@@ -24,10 +25,24 @@ app = typer.Typer(
|
|
|
24
25
|
|
|
25
26
|
def cli():
|
|
26
27
|
"""Entry point for console script."""
|
|
27
|
-
|
|
28
|
+
load_api_key()
|
|
28
29
|
app()
|
|
29
30
|
|
|
30
31
|
|
|
32
|
+
def load_api_key() -> None:
|
|
33
|
+
"""Pick up ANTHROPIC_API_KEY from a .env file if it isn't already set.
|
|
34
|
+
|
|
35
|
+
Only that one variable is read: a repo's .env could otherwise inject
|
|
36
|
+
settings such as GIT_CONFIG_* into the git commands GitGotchi runs.
|
|
37
|
+
"""
|
|
38
|
+
if os.environ.get("ANTHROPIC_API_KEY"):
|
|
39
|
+
return
|
|
40
|
+
path = find_dotenv(usecwd=True)
|
|
41
|
+
key = dotenv_values(path).get("ANTHROPIC_API_KEY") if path else None
|
|
42
|
+
if key:
|
|
43
|
+
os.environ["ANTHROPIC_API_KEY"] = key
|
|
44
|
+
|
|
45
|
+
|
|
31
46
|
def load_state() -> StateManager:
|
|
32
47
|
try:
|
|
33
48
|
return StateManager()
|
|
@@ -106,6 +121,9 @@ def pet():
|
|
|
106
121
|
raise typer.Exit(1)
|
|
107
122
|
|
|
108
123
|
|
|
124
|
+
MAX_NAME_LENGTH = 30
|
|
125
|
+
|
|
126
|
+
|
|
109
127
|
@app.command()
|
|
110
128
|
def name(new_name: str):
|
|
111
129
|
"""Give your companion a name."""
|
|
@@ -113,6 +131,11 @@ def name(new_name: str):
|
|
|
113
131
|
state_manager = load_state()
|
|
114
132
|
pet_stats = state_manager.get_pet_state()
|
|
115
133
|
|
|
134
|
+
new_name = " ".join(new_name.split())
|
|
135
|
+
if not new_name or len(new_name) > MAX_NAME_LENGTH:
|
|
136
|
+
render_error(f"Names need between 1 and {MAX_NAME_LENGTH} characters.")
|
|
137
|
+
raise typer.Exit(1)
|
|
138
|
+
|
|
116
139
|
old_name = pet_stats.pet_name
|
|
117
140
|
pet_stats.pet_name = new_name
|
|
118
141
|
state_manager.save_pet_state(pet_stats)
|
|
@@ -245,7 +268,7 @@ def story():
|
|
|
245
268
|
|
|
246
269
|
@app.command()
|
|
247
270
|
def adopt(
|
|
248
|
-
limit: Optional[int] = typer.Option(None, help="Only replay the most recent N commits"),
|
|
271
|
+
limit: Optional[int] = typer.Option(None, min=1, help="Only replay the most recent N commits"),
|
|
249
272
|
reset: bool = typer.Option(False, "--reset", help="Start the pet over before replaying"),
|
|
250
273
|
):
|
|
251
274
|
"""Raise your companion on this repo's existing history."""
|
|
@@ -294,30 +317,64 @@ def adopt(
|
|
|
294
317
|
|
|
295
318
|
@app.command()
|
|
296
319
|
def card(
|
|
297
|
-
output: Path = typer.Option(
|
|
320
|
+
output: Optional[Path] = typer.Option(None, "--output", "-o", help="Where to save it (default gitgotchi-card.png, or .gif with --gif)"),
|
|
298
321
|
scale: int = typer.Option(5, help="Pixel size; 5 gives 1200x630"),
|
|
322
|
+
gif: bool = typer.Option(False, "--gif", help="Save an animated GIF (needs: pip install 'gitgotchi[gif]')"),
|
|
299
323
|
):
|
|
300
|
-
"""Save a shareable card of your companion
|
|
324
|
+
"""Save a shareable card of your companion."""
|
|
301
325
|
from gitgotchi.paths import require_repo
|
|
302
|
-
from gitgotchi.render.card import make_card
|
|
326
|
+
from gitgotchi.render.card import make_card, make_card_gif
|
|
303
327
|
from gitgotchi.render.scene import encode_png, upscale
|
|
304
328
|
from gitgotchi.render.terminal import graphics_protocol, inline_image
|
|
305
329
|
|
|
306
330
|
state_manager = load_state()
|
|
307
331
|
stats = state_manager.get_pet_state()
|
|
308
332
|
repo_name = Path(require_repo().working_tree_dir).name
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
output
|
|
333
|
+
agents = state_manager.agent_counts()
|
|
334
|
+
scale = max(1, min(12, scale))
|
|
335
|
+
output = output or Path("gitgotchi-card.gif" if gif else "gitgotchi-card.png")
|
|
336
|
+
|
|
337
|
+
image = make_card(stats, repo_name, agents)
|
|
338
|
+
if gif:
|
|
339
|
+
try:
|
|
340
|
+
data = make_card_gif(stats, repo_name, agents, scale=scale)
|
|
341
|
+
except ImportError:
|
|
342
|
+
render_error("Animated cards need Pillow: pip install 'gitgotchi[gif]'")
|
|
343
|
+
raise typer.Exit(1)
|
|
344
|
+
output.write_bytes(data)
|
|
345
|
+
else:
|
|
346
|
+
output.write_bytes(encode_png(upscale(image, scale)))
|
|
312
347
|
|
|
313
348
|
protocol = graphics_protocol()
|
|
314
349
|
if protocol and console.is_terminal:
|
|
315
350
|
console.file.write(inline_image(image, min(console.width - 2, 96), protocol))
|
|
316
351
|
console.file.flush()
|
|
317
|
-
|
|
352
|
+
size = f"{image.shape[1] * scale}x{image.shape[0] * scale}"
|
|
353
|
+
console.print(Text(f"\n🖼️ Saved {output} ({size})", style=theme.ORANGE))
|
|
318
354
|
console.print(Text("Share it, and tag the spirits that wrote your code.\n", style=theme.DIM))
|
|
319
355
|
|
|
320
356
|
|
|
357
|
+
@app.command()
|
|
358
|
+
def badge(
|
|
359
|
+
output: Path = typer.Option(Path("gitgotchi-badge.svg"), "--output", "-o", help="Where to save the SVG"),
|
|
360
|
+
):
|
|
361
|
+
"""Save a README badge with your companion's portrait and form."""
|
|
362
|
+
from gitgotchi.render.badge import make_badge
|
|
363
|
+
|
|
364
|
+
from gitgotchi.paths import require_repo
|
|
365
|
+
|
|
366
|
+
stats = load_state().get_pet_state()
|
|
367
|
+
output.write_text(make_badge(stats), encoding="utf-8")
|
|
368
|
+
root = Path(require_repo().working_tree_dir).resolve()
|
|
369
|
+
try:
|
|
370
|
+
link = output.resolve().relative_to(root).as_posix()
|
|
371
|
+
except ValueError: # saved outside the repo
|
|
372
|
+
link = output.as_posix()
|
|
373
|
+
console.print(Text(f"\n🏷️ Saved {output}. Commit it and add this to your README:", style=theme.ORANGE))
|
|
374
|
+
console.print(Text(f"[](https://github.com/Srikarmk/Gitgotchi)\n", style=theme.BONE))
|
|
375
|
+
console.print(Text("Run `gitgotchi badge` again whenever your companion evolves.\n", style=theme.DIM))
|
|
376
|
+
|
|
377
|
+
|
|
321
378
|
@app.command()
|
|
322
379
|
def haunt(fps: int = typer.Option(12, help="Frames per second")):
|
|
323
380
|
"""Open a live pane where your companion reacts to every commit.
|
|
@@ -348,6 +405,86 @@ def demo():
|
|
|
348
405
|
console.print(Text("🎃 That's GitGotchi. Run `gitgotchi install` in a real repo to adopt your own.", style=theme.ORANGE))
|
|
349
406
|
|
|
350
407
|
|
|
408
|
+
@app.command()
|
|
409
|
+
def config(
|
|
410
|
+
output: Optional[str] = typer.Argument(None, help="full, line or off"),
|
|
411
|
+
global_: bool = typer.Option(False, "--global", help="Set it for every repo instead of this one"),
|
|
412
|
+
):
|
|
413
|
+
"""Choose how much the hook prints after each commit.
|
|
414
|
+
|
|
415
|
+
full: the whole pet (default). line: one line. off: nothing.
|
|
416
|
+
Stored in git config as gitgotchi.output.
|
|
417
|
+
"""
|
|
418
|
+
from gitgotchi.hooks.post_commit import configured_output
|
|
419
|
+
from gitgotchi.paths import find_repo
|
|
420
|
+
from gitgotchi.pet.renderer import OUTPUT_STYLES
|
|
421
|
+
|
|
422
|
+
repo = find_repo()
|
|
423
|
+
if output is None:
|
|
424
|
+
if repo is None:
|
|
425
|
+
render_error("No git repository found here. Run `git init` first.")
|
|
426
|
+
raise typer.Exit(1)
|
|
427
|
+
console.print(Text(f"gitgotchi.output = {configured_output(repo)}", style=theme.ORANGE))
|
|
428
|
+
return
|
|
429
|
+
|
|
430
|
+
output = output.strip().lower()
|
|
431
|
+
if output not in OUTPUT_STYLES:
|
|
432
|
+
render_error(f"Pick one of: {', '.join(OUTPUT_STYLES)}")
|
|
433
|
+
raise typer.Exit(1)
|
|
434
|
+
if not global_ and repo is None:
|
|
435
|
+
render_error("No git repository here. Use --global to set it everywhere.")
|
|
436
|
+
raise typer.Exit(1)
|
|
437
|
+
|
|
438
|
+
if global_:
|
|
439
|
+
from git import Git
|
|
440
|
+
|
|
441
|
+
Git().config("--global", "gitgotchi.output", output)
|
|
442
|
+
else:
|
|
443
|
+
repo.git.config("gitgotchi.output", output)
|
|
444
|
+
where = "every repo" if global_ else "this repo"
|
|
445
|
+
console.print(Text(f"🕯️ Hook output set to {output} for {where}.", style=theme.ORANGE))
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
@app.command()
|
|
449
|
+
def doctor():
|
|
450
|
+
"""Check your setup and explain how to fix anything that's wrong."""
|
|
451
|
+
from gitgotchi.doctor import FAIL, WARN, run_checks
|
|
452
|
+
|
|
453
|
+
checks = run_checks(console)
|
|
454
|
+
marks = {"ok": ("✓", theme.GREEN), WARN: ("!", theme.ORANGE), FAIL: ("✗", theme.BLOOD)}
|
|
455
|
+
table = Table.grid(padding=(0, 2))
|
|
456
|
+
table.add_column(no_wrap=True)
|
|
457
|
+
table.add_column(no_wrap=True, style="bold")
|
|
458
|
+
table.add_column()
|
|
459
|
+
for check in checks:
|
|
460
|
+
mark, color = marks[check.status]
|
|
461
|
+
detail = Text(check.detail, overflow="fold")
|
|
462
|
+
if check.fix and check.status != "ok":
|
|
463
|
+
detail.append(f"\n→ {check.fix}", style=color)
|
|
464
|
+
table.add_row(Text(mark, style=f"bold {color}"), check.name, detail)
|
|
465
|
+
console.print(Panel(table, title="🩺 GitGotchi doctor", border_style=theme.PURPLE, expand=False))
|
|
466
|
+
|
|
467
|
+
failures = [c for c in checks if c.status == FAIL]
|
|
468
|
+
if failures:
|
|
469
|
+
console.print(Text(f"{len(failures)} problem{'s' if len(failures) > 1 else ''} to fix.", style=theme.BLOOD))
|
|
470
|
+
raise typer.Exit(1)
|
|
471
|
+
console.print(Text("Everything needed is working.", style=theme.GREEN))
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
@app.command()
|
|
475
|
+
def statusline(
|
|
476
|
+
plain: bool = typer.Option(False, "--plain", help="No colors (for tmux or Starship)"),
|
|
477
|
+
setup: bool = typer.Option(False, "--setup", help="Show how to add it to Claude Code, tmux and Starship"),
|
|
478
|
+
):
|
|
479
|
+
"""Print one line about your companion, for status bars and prompts.
|
|
480
|
+
|
|
481
|
+
For status bars, use the faster `gitgotchi-statusline` command instead.
|
|
482
|
+
"""
|
|
483
|
+
from gitgotchi.statusline import main
|
|
484
|
+
|
|
485
|
+
raise typer.Exit(main((["--plain"] if plain else []) + (["--setup"] if setup else [])))
|
|
486
|
+
|
|
487
|
+
|
|
351
488
|
@app.command()
|
|
352
489
|
def version():
|
|
353
490
|
"""Show GitGotchi version."""
|