gitgotchi 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,78 @@
1
+ Metadata-Version: 2.4
2
+ Name: gitgotchi
3
+ Version: 0.1.0
4
+ Summary: A Stardew Valley-inspired terminal companion for your git repository
5
+ Home-page: https://github.com/yourusername/gitgotchi
6
+ Author: GitGotchi Team
7
+ Author-email: GitGotchi Team <gitgotchi@example.com>
8
+ License: MIT
9
+ Project-URL: Homepage, https://github.com/yourusername/gitgotchi
10
+ Project-URL: Repository, https://github.com/yourusername/gitgotchi
11
+ Project-URL: Issues, https://github.com/yourusername/gitgotchi/issues
12
+ Project-URL: Documentation, https://github.com/yourusername/gitgotchi#readme
13
+ Keywords: git,terminal,pet,companion,stardew-valley,ai,tamagotchi,devtools,hackathon,kiro
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Topic :: Software Development :: Version Control :: Git
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: rich>=13.7.0
26
+ Requires-Dist: gitpython>=3.1.40
27
+ Requires-Dist: anthropic>=0.34.0
28
+ Requires-Dist: sqlalchemy>=2.0.36
29
+ Requires-Dist: typer>=0.9.0
30
+ Requires-Dist: python-dotenv>=1.0.0
31
+ Provides-Extra: dev
32
+ Requires-Dist: pytest>=7.4.3; extra == "dev"
33
+ Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
34
+ Requires-Dist: black>=23.0.0; extra == "dev"
35
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
36
+ Dynamic: author
37
+ Dynamic: home-page
38
+ Dynamic: license-file
39
+ Dynamic: requires-python
40
+
41
+ # 🎃 GitGotchi - Your Friendly Dev Companion 👻
42
+
43
+ A Stardew Valley-inspired terminal companion that lives in your git repository!
44
+
45
+ ## ✨ Features
46
+ - Adorable pixel art companion that evolves based on your coding habits
47
+ - Warm, encouraging stories about your code history
48
+ - Automatic feeding through git hooks
49
+ - Friendship system inspired by Stardew Valley
50
+ - Cozy terminal UI with sparkles and hearts
51
+ - Fast evolution - see changes every 2-3 commits!
52
+
53
+ ## 🚀 Quick Start
54
+ ```bash
55
+ pip install -r requirements.txt
56
+ python gitgotchi.py install # Install git hooks
57
+ python gitgotchi.py status # Meet your companion!
58
+ ```
59
+
60
+ ## 🎮 Commands
61
+ - `gitgotchi status` - See your companion
62
+ - `gitgotchi pet` - Give them affection
63
+ - `gitgotchi story` - Hear a code story
64
+ - `gitgotchi evolve` - Check evolution progress
65
+ - `gitgotchi name <name>` - Give your companion a name
66
+
67
+ ## 🌟 Evolution Forms (Fast Track!)
68
+ - 🥚 Egg (0-2 commits)
69
+ - 👻 Ghost (3-5 commits)
70
+ - 👼 Angel (6+ commits, high quality)
71
+ - 😈 Demon (6+ commits, chaotic)
72
+ - 🧟 Zombie (4+ reverts, needs coffee)
73
+ - 🌙 Wraith (15+ commits, ultimate form)
74
+
75
+ ## 🎨 Theme
76
+ GitGotchi is inspired by Stardew Valley's cozy, friendly aesthetic. Your companion is a warm, magical spirit - not scary! Think Junimos, sparkles, and autumn leaves.
77
+
78
+ Built with ❤️ for Kiroween Hackathon 2024
@@ -0,0 +1,23 @@
1
+ gitgotchi-0.1.0.dist-info/licenses/LICENSE,sha256=6k9l52nYfuS1Ktvxos5VGH5kVc8oxgykbKSzmh9tzqo,1092
2
+ src/__init__.py,sha256=l_V_FObzZCzbJFX4e2u13AnYZEdE29dtdpSqYwfiMTk,284
3
+ src/cli.py,sha256=WerXS_SX6of7SHn3u5JCpHE-XeI59CX6FX9dezV-6U4,5258
4
+ src/db/__init__.py,sha256=LjqUEYU9TURCkPMkKAot0T9khEHOrkGZwcShXhuFobo,24
5
+ src/db/migrations.py,sha256=fsKnfgU8uu7R0aHdmqq89dR2adXwRk2On_28Ypn56a4,3029
6
+ src/db/models.py,sha256=QdXHkxR9ZaONaiup5q6NYvv6cxTw-HUntrDipzcv_CM,3573
7
+ src/db/state_manager.py,sha256=Xr_L_qRC9x58ac0M4tkIZWUfV7XuIKN7zJFB0JIvLQQ,8871
8
+ src/hooks/__init__.py,sha256=iJfxk3coNJu-Rd9_QU73lEaCW6cQBE2aJiq-idAzK40,25
9
+ src/hooks/installer.py,sha256=nSKvigoOVtMibOiKQa3u7mz7TNGt0V_fGX0655Uf9ZY,3638
10
+ src/hooks/post_commit.py,sha256=pYEG9cVtZvb_NYN3rALpEGT_6IcDMbMUMMx52NvznnY,2188
11
+ src/pet/__init__.py,sha256=O2wViywLlqHFo5BLYsoR5eKM55N6gzWaPeNJq9zCTpE,19
12
+ src/pet/evolution.py,sha256=sSsBBhysL3Vw0lgZvlTH2Xt8mFmwMnz4UdbpaijpQFY,92
13
+ src/pet/renderer.py,sha256=1mYX787b0s2IkEz08Ylo4T4I3NiBCW0v9Sl3Di_F9hg,5440
14
+ src/pet/states.py,sha256=XNREk3wxkWWzzBuNHneuouQwd0mQxSv-U6JtjYPzk_Y,4352
15
+ src/seance/__init__.py,sha256=Dxose1-VsMTJPZgaNSzNIplDsFcNcSYEs0FWmVDw9Oo,43
16
+ src/seance/git_analyzer.py,sha256=vkVphOCyA5D49Dq_AXobXHopFoEtK4jnXfeYeb8O1bA,93
17
+ src/seance/story_gen.py,sha256=-L3xfaFw_FcRRkha7sxXLRlJnnzliCkGt-BwBYxqKRk,6659
18
+ src/seance/templates.py,sha256=lmdkucuSrjZXmho94gIN6qouwTh2ATnAHOE0wj1n5cQ,42
19
+ gitgotchi-0.1.0.dist-info/METADATA,sha256=Tg5je-Cpb-eJ8ZOyw04etNtlHRdPKwSzkgWZ9uQ1OpE,3034
20
+ gitgotchi-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
21
+ gitgotchi-0.1.0.dist-info/entry_points.txt,sha256=YU3rE0GiGuTPqgV3iy7gowLRS8Kuoe9U5h0f0hw_-DY,42
22
+ gitgotchi-0.1.0.dist-info/top_level.txt,sha256=74rtVfumQlgAPzR5_2CgYN24MB0XARCg0t-gzk6gTrM,4
23
+ gitgotchi-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ gitgotchi = src.cli:cli
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 GitGotchi Team
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.
@@ -0,0 +1 @@
1
+ src
src/__init__.py ADDED
@@ -0,0 +1,13 @@
1
+ """
2
+ GitGotchi - A Stardew Valley-inspired terminal companion for your git repository.
3
+
4
+ Built for Kiroween Hackathon 2024.
5
+ """
6
+
7
+ __version__ = "0.1.0"
8
+ __author__ = "GitGotchi Team"
9
+ __email__ = "gitgotchi@example.com"
10
+
11
+ from src.cli import app, cli
12
+
13
+ __all__ = ["app", "cli"]
src/cli.py ADDED
@@ -0,0 +1,182 @@
1
+ """Main CLI interface."""
2
+ import typer
3
+ from rich.console import Console
4
+ from rich.panel import Panel
5
+
6
+ from src.db.state_manager import StateManager
7
+ from src.hooks.installer import install_hooks, uninstall_hooks
8
+ from src.pet.renderer import render_error, render_pet
9
+
10
+ app = typer.Typer(help="✨ GitGotchi - Your friendly dev companion")
11
+ console = Console()
12
+
13
+
14
+ def cli():
15
+ """Entry point for console script."""
16
+ app()
17
+
18
+
19
+ @app.command()
20
+ def status():
21
+ """Show your companion's current status."""
22
+ try:
23
+ state_manager = StateManager()
24
+ pet_stats = state_manager.get_pet_state()
25
+ render_pet(pet_stats, show_stats=True)
26
+ except Exception as e:
27
+ render_error(f"Failed to load companion state: {str(e)}")
28
+ raise typer.Exit(1)
29
+
30
+
31
+ @app.command()
32
+ def install():
33
+ """Install GitGotchi git hooks for automatic feeding."""
34
+ if install_hooks():
35
+ console.print(
36
+ "\n[green]✨ Git hooks installed! Your companion will be fed automatically on commits.[/green]"
37
+ )
38
+ console.print("[dim]Make a commit to meet your new friend...[/dim]\n")
39
+ else:
40
+ raise typer.Exit(1)
41
+
42
+
43
+ @app.command()
44
+ def uninstall():
45
+ """Remove GitGotchi git hooks."""
46
+ if uninstall_hooks():
47
+ console.print(
48
+ "\n[yellow]👋 Git hooks removed. You can still feed manually with 'gitgotchi feed'.[/yellow]\n"
49
+ )
50
+ else:
51
+ raise typer.Exit(1)
52
+
53
+
54
+ @app.command()
55
+ def pet():
56
+ """Pet your companion (boosts mood slightly)."""
57
+ try:
58
+ state_manager = StateManager()
59
+ pet_stats = state_manager.get_pet_state()
60
+
61
+ # Interact with pet
62
+ message = pet_stats.interact()
63
+
64
+ # Save updated state
65
+ state_manager.save_pet_state(pet_stats)
66
+
67
+ # Show response
68
+ console.print(Panel(message, border_style="cyan", title="💛 Interaction"))
69
+ render_pet(pet_stats, show_stats=False)
70
+
71
+ except Exception as e:
72
+ render_error(f"Failed to interact: {str(e)}")
73
+ raise typer.Exit(1)
74
+
75
+
76
+ @app.command()
77
+ def name(new_name: str):
78
+ """Give your companion a name."""
79
+ try:
80
+ state_manager = StateManager()
81
+ pet_stats = state_manager.get_pet_state()
82
+
83
+ old_name = pet_stats.pet_name
84
+ pet_stats.pet_name = new_name
85
+
86
+ state_manager.save_pet_state(pet_stats)
87
+
88
+ console.print(
89
+ f"\n[green]✨ Your companion is now named {new_name}![/green]"
90
+ )
91
+ if old_name != "Spirit":
92
+ console.print(f"[dim](Previously known as {old_name})[/dim]\n")
93
+
94
+ except Exception as e:
95
+ render_error(f"Failed to rename companion: {str(e)}")
96
+ raise typer.Exit(1)
97
+
98
+
99
+ @app.command()
100
+ def evolve():
101
+ """Check evolution progress and requirements."""
102
+ try:
103
+ state_manager = StateManager()
104
+ pet_stats = state_manager.get_pet_state()
105
+
106
+ # Calculate progress
107
+ current_form = pet_stats.current_form.value
108
+ commits = pet_stats.total_commits
109
+ quality = pet_stats.quality_score
110
+
111
+ console.print(
112
+ Panel(
113
+ f"""[cyan]Current Form:[/cyan] {current_form}
114
+ [cyan]Total Commits:[/cyan] {commits}
115
+ [cyan]Quality Score:[/cyan] {quality:.1f}/100
116
+ [cyan]Friend Level:[/cyan] {"❤️" * int(pet_stats.friend_level)} ({pet_stats.friend_level}/10)
117
+
118
+ [yellow]Evolution Requirements:[/yellow]
119
+ • Egg → Ghost: 3 commits (you have {commits})
120
+ • Ghost → Angel: 6 commits + quality > 70
121
+ • Ghost → Demon: 6 commits + quality < 30
122
+ • Any → Zombie: 4 reverts (you have {pet_stats.reverts})
123
+ • Any → Wraith: 15 commits (ultimate form!)
124
+ """,
125
+ title="🌟 Evolution Progress",
126
+ border_style="cyan",
127
+ )
128
+ )
129
+
130
+ except Exception as e:
131
+ render_error(f"Failed to check evolution: {str(e)}")
132
+ raise typer.Exit(1)
133
+
134
+
135
+ @app.command()
136
+ def story():
137
+ """Hear a story about your code's history."""
138
+ try:
139
+ from src.seance.story_gen import StoryGenerator
140
+
141
+ state_manager = StateManager()
142
+ pet_stats = state_manager.get_pet_state()
143
+
144
+ generator = StoryGenerator()
145
+
146
+ # Generate milestone story
147
+ context = {
148
+ "total_commits": pet_stats.total_commits,
149
+ "total_lines": pet_stats.lines_added + pet_stats.lines_deleted,
150
+ "days_together": (
151
+ pet_stats.last_fed - pet_stats.last_fed
152
+ ).days, # Simplified
153
+ }
154
+
155
+ story_text = generator.generate_story(
156
+ "milestone", context, pet_stats.current_form.value
157
+ )
158
+
159
+ console.print(
160
+ Panel(
161
+ story_text,
162
+ title=f"📖 {pet_stats.pet_name}'s Story",
163
+ border_style="cyan",
164
+ padding=(1, 2),
165
+ )
166
+ )
167
+
168
+ except Exception as e:
169
+ render_error(f"Failed to generate story: {str(e)}")
170
+ raise typer.Exit(1)
171
+
172
+
173
+ @app.command()
174
+ def version():
175
+ """Show GitGotchi version."""
176
+ console.print(
177
+ "GitGotchi v0.1.0 - Your friendly dev companion ✨", style="bold cyan"
178
+ )
179
+
180
+
181
+ if __name__ == "__main__":
182
+ app()
src/db/__init__.py ADDED
@@ -0,0 +1 @@
1
+ """Database module."""
src/db/migrations.py ADDED
@@ -0,0 +1,100 @@
1
+ """Database migration utilities."""
2
+ from pathlib import Path
3
+ from typing import Optional
4
+
5
+ from sqlalchemy import text
6
+ from sqlalchemy.orm import Session
7
+
8
+ from src.db.models import get_engine
9
+
10
+
11
+ class MigrationManager:
12
+ """Handles database schema migrations."""
13
+
14
+ def __init__(self, db_path: str = ".gitgotchi/state.db") -> None:
15
+ """Initialize migration manager.
16
+
17
+ Args:
18
+ db_path: Path to SQLite database file
19
+ """
20
+ self.db_path = db_path
21
+ self.engine = get_engine(db_path)
22
+
23
+ def get_schema_version(self) -> int:
24
+ """Get current schema version.
25
+
26
+ Returns:
27
+ Schema version number, 0 if not set
28
+ """
29
+ try:
30
+ with Session(self.engine) as session:
31
+ result = session.execute(
32
+ text("SELECT version FROM schema_version LIMIT 1")
33
+ )
34
+ row = result.fetchone()
35
+ return row[0] if row else 0
36
+ except Exception:
37
+ return 0
38
+
39
+ def set_schema_version(self, version: int) -> None:
40
+ """Set schema version.
41
+
42
+ Args:
43
+ version: Version number to set
44
+ """
45
+ with Session(self.engine) as session:
46
+ # Create table if not exists
47
+ session.execute(
48
+ text(
49
+ """
50
+ CREATE TABLE IF NOT EXISTS schema_version (
51
+ version INTEGER PRIMARY KEY
52
+ )
53
+ """
54
+ )
55
+ )
56
+
57
+ # Update or insert version
58
+ session.execute(text("DELETE FROM schema_version"))
59
+ session.execute(
60
+ text("INSERT INTO schema_version (version) VALUES (:version)"),
61
+ {"version": version},
62
+ )
63
+ session.commit()
64
+
65
+ def backup_database(self) -> Optional[str]:
66
+ """Create backup of database.
67
+
68
+ Returns:
69
+ Path to backup file if successful, None otherwise
70
+ """
71
+ try:
72
+ db_file = Path(self.db_path)
73
+ if not db_file.exists():
74
+ return None
75
+
76
+ backup_path = db_file.with_suffix(".db.backup")
77
+ backup_path.write_bytes(db_file.read_bytes())
78
+ return str(backup_path)
79
+ except Exception:
80
+ return None
81
+
82
+ def run_migrations(self) -> None:
83
+ """Run all pending migrations."""
84
+ current_version = self.get_schema_version()
85
+
86
+ # Define migrations
87
+ migrations = [
88
+ self._migration_v1_initial,
89
+ # Add future migrations here
90
+ ]
91
+
92
+ for version, migration in enumerate(migrations, start=1):
93
+ if version > current_version:
94
+ self.backup_database()
95
+ migration()
96
+ self.set_schema_version(version)
97
+
98
+ def _migration_v1_initial(self) -> None:
99
+ """Initial schema - handled by SQLAlchemy create_all."""
100
+ pass
src/db/models.py ADDED
@@ -0,0 +1,92 @@
1
+ """SQLite schemas."""
2
+ from datetime import datetime
3
+ from typing import Optional
4
+
5
+ from sqlalchemy import Float, Integer, String, Text, create_engine
6
+ from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session
7
+
8
+
9
+ class Base(DeclarativeBase):
10
+ """Base class for all models."""
11
+
12
+ pass
13
+
14
+
15
+ class PetState(Base):
16
+ """Singleton table storing current pet state (one row per repo)."""
17
+
18
+ __tablename__ = "pet_state"
19
+
20
+ id: Mapped[int] = mapped_column(Integer, primary_key=True)
21
+ pet_name: Mapped[str] = mapped_column(String(100), default="Spirit")
22
+ total_commits: Mapped[int] = mapped_column(Integer, default=0)
23
+ lines_added: Mapped[int] = mapped_column(Integer, default=0)
24
+ lines_deleted: Mapped[int] = mapped_column(Integer, default=0)
25
+ merge_conflicts: Mapped[int] = mapped_column(Integer, default=0)
26
+ reverts: Mapped[int] = mapped_column(Integer, default=0)
27
+ quality_score: Mapped[float] = mapped_column(Float, default=50.0)
28
+ evolution_points: Mapped[int] = mapped_column(Integer, default=0)
29
+ friend_level: Mapped[float] = mapped_column(Float, default=1.0)
30
+ last_fed: Mapped[datetime] = mapped_column(default=datetime.now)
31
+ current_mood: Mapped[str] = mapped_column(String(20), default="content")
32
+ current_form: Mapped[str] = mapped_column(String(20), default="egg")
33
+ created_at: Mapped[datetime] = mapped_column(default=datetime.now)
34
+ updated_at: Mapped[datetime] = mapped_column(
35
+ default=datetime.now, onupdate=datetime.now
36
+ )
37
+
38
+
39
+ class CommitHistory(Base):
40
+ """Log of all tracked commits."""
41
+
42
+ __tablename__ = "commit_history"
43
+
44
+ id: Mapped[int] = mapped_column(Integer, primary_key=True)
45
+ commit_hash: Mapped[str] = mapped_column(String(40), unique=True, index=True)
46
+ author: Mapped[str] = mapped_column(String(255))
47
+ message: Mapped[str] = mapped_column(Text)
48
+ timestamp: Mapped[datetime] = mapped_column(index=True)
49
+ lines_added: Mapped[int] = mapped_column(Integer, default=0)
50
+ lines_deleted: Mapped[int] = mapped_column(Integer, default=0)
51
+ files_changed: Mapped[int] = mapped_column(Integer, default=0)
52
+ is_merge: Mapped[bool] = mapped_column(default=False)
53
+ is_revert: Mapped[bool] = mapped_column(default=False)
54
+ quality_impact: Mapped[float] = mapped_column(Float, default=0.0)
55
+ processed_at: Mapped[datetime] = mapped_column(default=datetime.now)
56
+
57
+
58
+ class StoryMemory(Base):
59
+ """Cache of generated stories."""
60
+
61
+ __tablename__ = "story_memory"
62
+
63
+ id: Mapped[int] = mapped_column(Integer, primary_key=True)
64
+ commit_hash: Mapped[str] = mapped_column(String(40), index=True)
65
+ story_text: Mapped[str] = mapped_column(Text)
66
+ story_type: Mapped[str] = mapped_column(String(50))
67
+ pet_form_at_time: Mapped[str] = mapped_column(String(20))
68
+ pet_mood_at_time: Mapped[str] = mapped_column(String(20))
69
+ generated_at: Mapped[datetime] = mapped_column(default=datetime.now)
70
+ tokens_used: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
71
+
72
+
73
+ def get_engine(db_path: str = ".gitgotchi/state.db"):
74
+ """Create database engine.
75
+
76
+ Args:
77
+ db_path: Path to SQLite database file
78
+
79
+ Returns:
80
+ SQLAlchemy engine instance
81
+ """
82
+ return create_engine(f"sqlite:///{db_path}", echo=False)
83
+
84
+
85
+ def init_db(db_path: str = ".gitgotchi/state.db") -> None:
86
+ """Initialize database schema.
87
+
88
+ Args:
89
+ db_path: Path to SQLite database file
90
+ """
91
+ engine = get_engine(db_path)
92
+ Base.metadata.create_all(engine)