Metadata-Version: 2.4
Name: git-diary
Version: 0.1.0
Summary: Dev diary generator from git history — narrative, not stats.
Author: Gustavo Cremonez
License-Expression: MIT
Project-URL: Homepage, https://github.com/GustavoCremonez/git-diary
Project-URL: Repository, https://github.com/GustavoCremonez/git-diary
Keywords: git,diary,cli,developer-tools,narrative
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Operating System :: OS Independent
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Version Control :: Git
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: typer>=0.12
Requires-Dist: rich>=13
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Dynamic: license-file

<p align="center">
  <img src="https://img.shields.io/badge/Python-3.11+-3776AB?logo=python&logoColor=white" alt="Python">
  <img src="https://img.shields.io/badge/Status-MVP-green" alt="Status">
  <img src="https://img.shields.io/github/license/GustavoCremonez/git-diary" alt="License">
</p>

<h1 align="center">git-diary</h1>

<p align="center">
  <strong>Dev diary generator from git history — narrative, not stats.</strong><br>
  Automatic personal development journals that read like a story of your coding week.
</p>

---

## The Problem

Programmers don't keep diaries. It's tedious, you forget, and it feels like busywork. But Git already records everything: when you committed, which project, how many files changed, whether you were up at 3 AM. That data sits there in `git log` — raw, boring, and unreadable.

Existing tools fall into two camps:

- **Stats dashboards** (git-fame, GitHub contribution graph) — show numbers. "47 commits this week." Numbers are boring. They don't tell a story.
- **AI summaries** (diddo) — send your data to an LLM and get a paragraph back. Requires API keys, costs money, and the output is a summary, not a reflection.

**git-diary** fills the gap: it turns your Git history into a **narrative** — a personal diary that reads like a story of your coding week. No API keys, no cloud, no AI. Just your data, on your machine, in your timezone.

## What It Detects

| Pattern | What It Means | Example |
|---|---|---|
| **Streak** | Consecutive days with at least one commit | "You committed 5 days in a row" |
| **Flow state** | 2+ commits within 1 hour in the same repo (sliding window) | "You hit flow at 14:00 on project X" |
| **Long nights** | Commits between 00:00 and 05:00 (author's local time) | "2 late-night commits — you were up late" |

## How It Works

1. **Scans** a workspace folder for git repositories (1 level deep).
2. **Collects** commits per repo with metadata: author, date/time, files changed, insertions, deletions.
3. **Detects** developer behavior patterns: streaks, flow sessions, late-night commits.
4. **Generates** narrative markdown organized by date and repo.
5. **Saves** to `~/.git-diary/YYYY-MM.md` and **renders** to terminal with Rich (colored panel + markdown).

## Privacy by Default

git-diary **never** displays raw commit messages unless you explicitly opt in with `--full-messages`. Commit messages can contain passwords, tokens, client names, or frustrated notes you'd never share. The default output shows only:

```
- [14:30] Commit a1b2c3d4 (3 files, +45/-12)
```

Not the message itself. Your secrets stay in your local Git, not in your diary file.

## Why Use It

- **Zero friction.** The diary generates itself from Git data you already produce. Nothing to remember, nothing to annotate.
- **Reflection, not metrics.** "Today you worked 3h on project X, hit flow at 14:00, and made 2 late-night commits." That's a story — not "47 commits, 312 insertions."
- **Local and private.** No API, no cloud, no telemetry. Runs on your machine, reads your local Git, writes to your home directory.
- **Timezone-aware.** Streaks, late-night detection, and flow sessions use the author's original timezone — not UTC. A commit at 23:00 in São Paulo is a 23:00 commit, not 02:00 the next day in UTC.

## When to Use It

- **Friday wrap-up.** Run `git-diary generate --scope week` before closing the laptop. See a narrative of your entire week, per project, with streaks and flow sessions.
- **Monday morning.** Run `git-diary show`. Remember where you left off without opening Git or scrolling through commits.
- **Monthly retrospective.** After a month of use, you have 30 files in `~/.git-diary/`. Read through them to spot patterns: "Every Thursday I code late" or "Project X sat untouched for two weeks."
- **Sharing progress.** The generated markdown is an honest draft of what actually happened — not a padded PR list.
- **Self-awareness.** Am I coding too many late nights? Is my streak broken? The diary answers without you having to track it manually.

## When NOT to Use It

- **Team reporting.** It's a personal tool. For team-wide metrics, use GitHub Insights or Linear.
- **DORA metrics.** If you need lead time, deployment frequency, or MTTR, use a DevOps-specific tool. git-diary is narrative, not KPI.
- **Manual journaling.** If you want to write your own reflections by hand, use [daylog](https://github.com/daylog) or a notebook. git-diary is for people who won't keep a diary manually.

## Installation

```bash
# Recommended: pipx (isolated environment, no dependency conflicts)
pipx install git-diary

# Alternative: pip
pip install git-diary
```

## Quick Start

```bash
# Generate today's diary from your code folder
git-diary generate --workspace ~/code

# Generate weekly digest
git-diary generate --scope week --workspace ~/code

# Display the latest diary in terminal
git-diary show
```

## All Commands with Examples

### `git-diary generate`

Generates a dev diary from recent git activity. Scans the workspace for repos, collects commits, detects patterns, and writes a markdown file.

#### Basic usage

```bash
# Generate today's diary (default scope = day)
git-diary generate

# Generate from a specific workspace
git-diary generate --workspace ~/projects

# Generate weekly digest (last 7 days)
git-diary generate --scope week

# Generate weekly digest from a specific workspace
git-diary generate --scope week --workspace ~/code

# Include raw commit messages in output (privacy opt-in)
git-diary generate --full-messages

# Combine all options
git-diary generate --scope week --workspace ~/code --full-messages
```

#### All options

| Option | Type | Default | Description |
|---|---|---|---|
| `--scope` | `day` \| `week` | `day` | Time window: `day` = today only, `week` = last 7 days |
| `--workspace` | `path` | current directory | Root directory to scan for git repositories (1 level deep) |
| `--full-messages` | `flag` | `false` | Include raw commit messages in output. Off by default for privacy. |

#### Output files

| Scope | Filename | Location |
|---|---|---|
| `day` (default) | `YYYY-MM.md` | `~/.git-diary/` |
| `week` | `YYYY-MM-week.md` | `~/.git-diary/` |

Files are overwritten on each run (latest diary wins). Use `git-diary show` to display the most recent one.

#### Example output

```
+--------------------------------- Git Diary ---------------------------------+
|                                    Diary                                    |
|                                                                             |
| _Covering August 01, 2026 to August 02, 2026_                              |
|                                                                             |
| ## Highlights                                                               |
|                                                                             |
| - **Streak:** 2 days                                                        |
| - Flow sessions: 1                                                          |
| - Late-night commits: 1                                                     |
|                                                                             |
| ## Per-Repo Activity                                                        |
|                                                                             |
| ### August 02, 2026                                                         |
|                                                                             |
| #### `my-project`                                                           |
|                                                                             |
| - [14:30] Commit a1b2c3d4 (3 files, +45/-12)                                |
| - [15:10] Commit e5f6a7b8 (1 file, +8/-0)                                   |
|                                                                             |
| ### August 01, 2026                                                         |
|                                                                             |
| #### `my-project`                                                           |
|                                                                             |
| - [02:15] Commit 9a8b7c6d (2 files, +20/-5) (late-night)                    |
+-----------------------------------------------------------------------------+
```

With `--full-messages`, the commit lines show the raw message instead:

```
- [14:30] feat: add login page
- [15:10] fix: null pointer in parser
- [02:15] wip: late night hacking (late-night)
```

### `git-diary show`

Displays the most recently generated diary in the terminal with Rich rendering. Picks the latest file by modification time (not filename), so it works correctly whether you last generated a daily or weekly diary.

```bash
# Show the latest diary
git-diary show
```

If no diary exists yet:

```
No diary found. Run `git-diary generate` first.
```

### `git-diary --version`

```bash
git-diary --version
# Output: git-diary 0.1.0
```

### `git-diary --help`

```bash
git-diary --help
```

```
Usage: git-diary [OPTIONS] COMMAND [ARGS]...

  Dev diary generator from git history — narrative, not stats.

Options:
  --version  Show version and exit.
  --help     Show this message and exit.

Commands:
  generate  Generate a dev diary from recent git activity.
  show      Display the latest generated diary in the terminal.
```

### Command help

```bash
git-diary generate --help
git-diary show --help
```

## Comparison

| Feature | git-diary | diddo (16★) | git-fame | GitHub Insights |
|---|---|---|---|---|
| Auto-generated diary | Yes | No (AI summary) | No | No |
| Flow state detection | Yes | No | No | No |
| Streak tracking | Yes | No | No | No |
| Late-night detection | Yes | No | No | No |
| Narrative output | Markdown + Rich | Terminal stats | Terminal stats | Web dashboard |
| Zero config | Yes | No | No | No |
| No API keys | Yes | No (Claude/GPT) | Yes (GitHub API) | No (GitHub API) |
| Local-only | Yes | No | Yes | No |
| Privacy-first | Yes (messages hidden) | No | Yes | No |
| Timezone-aware | Yes (author-local) | Unknown | No | No |

## Architecture

```
src/git_diary/
  __main__.py          # Typer CLI entry point (generate, show, --version)
  config.py            # DiaryScope enum, diary_dir(), scope_cutoff()
  workspace/
    __init__.py        # discover_repos() — scan workspace for git repos
  commits/
    __init__.py        # Commit dataclass, collect_commits() — git log parser
  narrative/
    analytics.py       # compute_streak(), detect_flow_state(), detect_long_nights()
  render/
    markdown.py        # generate_markdown() — narrative markdown output
    terminal.py        # render_terminal() — Rich panel rendering
```

### Design Decisions

- **Author-local timezone for analytics.** Commits retain the author's original timezone offset from `%aI`. Streaks, late-night detection, and flow state reflect the developer's actual working hours, not UTC.
- **Null-byte separator in git log parsing.** Commit messages can contain `||` or any printable character but never `\x00`. Using null-byte as field separator prevents parser corruption.
- **Privacy-first output.** Raw commit messages are hidden by default (`--full-messages` opt-in). Default shows only hash + metadata.
- **Workspace self-detection.** If `--workspace` points directly to a git repo (not a parent folder of repos), it's treated as the only repo. No need to point to the parent directory.
- **Sliding window for flow state.** Each adjacent commit pair must be within the gap threshold. This correctly captures continuous coding sessions rather than anchor-based detection which can split or merge sessions incorrectly.
- **`git log` without `--all`.** Only the current branch (HEAD) is scanned. Abandoned branches, stashes, and dangling commits don't leak into your diary narrative.
- **Subprocess timeout.** `git log` calls have a 30-second timeout to prevent hangs on corrupted or network-mounted repositories.
- **`--since` delegation to Git.** Date filtering is passed to `git log --since` rather than filtering in Python after collecting all commits. This avoids parsing unnecessary history.

## Development

```bash
# Clone and setup
git clone https://github.com/GustavoCremonez/git-diary
cd git-diary
python -m venv .venv
source .venv/bin/activate   # Linux/macOS
# .venv\Scripts\activate    # Windows
pip install -e ".[dev]"

# Run tests
pytest                          # 42 tests
pytest -v                       # verbose output
pytest --cov=git_diary          # with coverage

# Lint & format
ruff check src tests            # lint
ruff format src tests           # format

# Build
python -m build                 # sdist + wheel
```

### Tech Stack

- **Python 3.11+** — type hints, StrEnum, datetime improvements
- **Typer 0.27** — CLI framework with auto-generated help
- **Rich 15** — terminal rendering with panels, markdown, syntax highlighting
- **Ruff 0.16** — fast linter + formatter (replaces flake8, isort, black)
- **pytest 9** — test framework with fixtures, parametrize, coverage

### Test Suite

| Module | Tests | Coverage |
|---|---|---|
| `test_workspace.py` | 8 | discover_repos, self-detection, error handling |
| `test_commits.py` | 7 | collect_commits, shortstat parsing, since filter |
| `test_narrative_analytics.py` | 11 | streak, flow state (sliding), long nights |
| `test_markdown.py` | 7 | narrative generation, privacy, cross-repo flow |
| `test_cli.py` | 7 | generate, show, --scope, --workspace, --full-messages |
| `test_terminal.py` | 2 | Rich rendering with injected console |
| **Total** | **42** | |

## Roadmap

### Done

- [x] Workspace scanner (1 level deep, sorted, canonicalized, self-detection)
- [x] Commit parser (git log + shortstat, null-byte separator, timeout)
- [x] Streak detection (consecutive days, author-local timezone)
- [x] Flow state detection (sliding window, per-repo)
- [x] Long nights detection (author-local timezone)
- [x] Markdown narrative generator (highlights + per-repo daily breakdown)
- [x] Rich terminal output (panel with markdown rendering)
- [x] CLI: generate, show, --scope day|week, --workspace, --full-messages
- [x] Privacy-first (messages hidden by default)
- [x] Workspace self-detection (point directly to a repo)
- [x] Scope-aware output files (day vs week)
- [x] `--since` delegation to Git (avoid parsing unnecessary history)
- [x] 42 tests, ruff lint clean, wheel build

### Next

- [ ] `git-diary show` in shell profile (`.bashrc`/`$PROFILE`) — zero-friction retention
- [ ] Conventional commits parser (feat/fix/docs/chore narrative)
- [ ] Config file `~/.git-diary/config.toml` (default workspace, ignore repos)
- [ ] Hot spots with real file paths (`git diff --name-only`)
- [ ] Output pagination (repos with 500+ commits)
- [ ] `--since` and `--until` date range options

### Post-MVP

- [ ] `--format json` (structured output for integrations)
- [ ] `--ignore repo1,repo2`
- [ ] Submodule/worktree detection + warning
- [ ] Rust rewrite (`clap` + `git2`) for single-binary distribution
- [ ] Publish to PyPI (`pipx install git-diary`)
- [ ] Weekly hook (cron/script) to open diary in editor
- [ ] Integration tests with real git repo fixtures

## License

[MIT](LICENSE)
