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.
Files changed (69) hide show
  1. gitgotchi-0.4.0/CHANGELOG.md +110 -0
  2. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/MANIFEST.in +1 -0
  3. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/PKG-INFO +64 -8
  4. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/README.md +60 -6
  5. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/pyproject.toml +5 -1
  6. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/__init__.py +1 -1
  7. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/cli.py +147 -10
  8. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/db/models.py +27 -16
  9. gitgotchi-0.4.0/src/gitgotchi/db/state_manager.py +311 -0
  10. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/demo.py +8 -3
  11. gitgotchi-0.4.0/src/gitgotchi/doctor.py +190 -0
  12. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/haunt.py +87 -26
  13. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/history.py +39 -4
  14. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/hooks/installer.py +21 -7
  15. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/hooks/post_commit.py +32 -5
  16. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/paths.py +20 -20
  17. gitgotchi-0.4.0/src/gitgotchi/pet/possession.py +84 -0
  18. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/pet/renderer.py +31 -5
  19. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/pet/states.py +16 -13
  20. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/render/__init__.py +2 -1
  21. gitgotchi-0.4.0/src/gitgotchi/render/badge.py +99 -0
  22. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/render/card.py +19 -0
  23. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/render/forms.py +25 -9
  24. gitgotchi-0.4.0/src/gitgotchi/render/raster.py +341 -0
  25. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/seance/hauntings.py +45 -20
  26. gitgotchi-0.4.0/src/gitgotchi/statusline.py +191 -0
  27. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi.egg-info/PKG-INFO +64 -8
  28. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi.egg-info/SOURCES.txt +5 -1
  29. gitgotchi-0.4.0/src/gitgotchi.egg-info/entry_points.txt +3 -0
  30. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi.egg-info/requires.txt +4 -1
  31. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/tests/test_cli.py +26 -0
  32. gitgotchi-0.4.0/tests/test_features.py +78 -0
  33. gitgotchi-0.4.0/tests/test_haunt.py +130 -0
  34. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/tests/test_hauntings.py +17 -0
  35. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/tests/test_history.py +1 -1
  36. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/tests/test_hooks.py +59 -0
  37. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/tests/test_possession.py +13 -0
  38. gitgotchi-0.4.0/tests/test_state_manager.py +174 -0
  39. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/tests/test_states.py +12 -0
  40. gitgotchi-0.4.0/tests/test_statusline.py +60 -0
  41. gitgotchi-0.3.0/CHANGELOG.md +0 -57
  42. gitgotchi-0.3.0/src/gitgotchi/db/migrations.py +0 -100
  43. gitgotchi-0.3.0/src/gitgotchi/db/state_manager.py +0 -383
  44. gitgotchi-0.3.0/src/gitgotchi/pet/possession.py +0 -56
  45. gitgotchi-0.3.0/src/gitgotchi/render/raster.py +0 -217
  46. gitgotchi-0.3.0/src/gitgotchi.egg-info/entry_points.txt +0 -2
  47. gitgotchi-0.3.0/tests/test_haunt.py +0 -54
  48. gitgotchi-0.3.0/tests/test_state_manager.py +0 -73
  49. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/INSTALL.md +0 -0
  50. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/LICENSE +0 -0
  51. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/setup.cfg +0 -0
  52. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/__main__.py +0 -0
  53. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/db/__init__.py +0 -0
  54. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/hooks/__init__.py +0 -0
  55. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/pet/__init__.py +0 -0
  56. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/pet/quality.py +0 -0
  57. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/pet/theme.py +0 -0
  58. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/render/effects.py +0 -0
  59. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/render/font.py +0 -0
  60. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/render/scene.py +0 -0
  61. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/render/terminal.py +0 -0
  62. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/seance/__init__.py +0 -0
  63. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi/seance/story_gen.py +0 -0
  64. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi.egg-info/dependency_links.txt +0 -0
  65. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/src/gitgotchi.egg-info/top_level.txt +0 -0
  66. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/tests/conftest.py +0 -0
  67. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/tests/test_quality.py +0 -0
  68. {gitgotchi-0.3.0 → gitgotchi-0.4.0}/tests/test_render.py +0 -0
  69. {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.
@@ -7,3 +7,4 @@ global-exclude __pycache__
7
7
  global-exclude *.py[co]
8
8
  prune docs
9
9
  prune previews
10
+ prune site
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitgotchi
3
- Version: 0.3.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.12.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
  ![The haunt pane reacting to commits](https://raw.githubusercontent.com/Srikarmk/Gitgotchi/main/docs/haunt.gif)
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
  ![A sample card](https://raw.githubusercontent.com/Srikarmk/Gitgotchi/main/docs/card.png)
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 PNG |
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 is stored in `.gitgotchi/state.db` at the root of the repository. GitGotchi adds
162
- `.gitgotchi/` to `.git/info/exclude`, so it never shows up in `git status` and is never committed.
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
  ![The haunt pane reacting to commits](https://raw.githubusercontent.com/Srikarmk/Gitgotchi/main/docs/haunt.gif)
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
  ![A sample card](https://raw.githubusercontent.com/Srikarmk/Gitgotchi/main/docs/card.png)
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 PNG |
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 is stored in `.gitgotchi/state.db` at the root of the repository. GitGotchi adds
121
- `.gitgotchi/` to `.git/info/exclude`, so it never shows up in `git status` and is never committed.
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.12.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,3 +1,3 @@
1
1
  """GitGotchi - A Halloween-inspired terminal companion for your git repository."""
2
2
 
3
- __version__ = "0.3.0"
3
+ __version__ = "0.4.0"
@@ -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 find_dotenv, load_dotenv
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
- load_dotenv(find_dotenv(usecwd=True))
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(Path("gitgotchi-card.png"), "--output", "-o", help="Where to save the PNG"),
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 as a PNG."""
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
- image = make_card(stats, repo_name, state_manager.agent_counts())
311
- output.write_bytes(encode_png(upscale(image, max(1, min(12, scale)))))
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
- console.print(Text(f"\n🖼️ Saved {output} ({image.shape[1] * scale}x{image.shape[0] * scale})", style=theme.ORANGE))
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"[![GitGotchi]({link})](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."""