Metadata-Version: 2.4
Name: commitdev
Version: 0.1.1
Summary: Turn git commits into content, automatically.
Author: CommitDev
License-Expression: MIT
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: typer>=0.12
Requires-Dist: requests>=2.31
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"

# CommitDev CLI

The client side of CommitDev: a small Python package that lets a tagged
git commit message (e.g. `[post][linkedin] Added JWT auth`) trigger content
generation, **without** that tag ever ending up in your permanent git
history.

## What this package does

When you write a commit like:

```
git commit -m "[draft][linkedin][tone=technical] Added JWT authentication"
```

Two things happen automatically, before the commit is even finalized:

1. **The tags get stripped from the message.** What actually gets saved to
   your repo is just `Added JWT authentication` — clean, no brackets.
2. **CommitDev gets told what the tags were.** The tags (`draft`,
   `linkedin`, `tone=technical`) and the commit's SHA get sent to the
   CommitDev API, so it knows what kind of draft to generate for that
   commit once it later sees the (now tag-free) push on GitHub.

This only works on text that matches CommitDev's known tag vocabulary
(`post`, `draft`, `linkedin`, `tone=...`, etc.) — unrelated bracketed text
developers already use, like `[JIRA-456]` or `[WIP]`, is left untouched.

## How it works (the mechanism)

Git hooks are small scripts that git runs automatically at certain points
in the commit process. This package installs two of them:

- **`commit-msg`** — runs *before* the commit object exists, with the
  message you just typed. This is the only point where the message can
  still be edited, so this is where tag-stripping happens.
- **`post-commit`** — runs right *after* the commit exists, so this is the
  only point where the final commit SHA is known. This is where the
  tags + SHA get reported to CommitDev.

Because these are real git hooks (not a separate command you have to
remember to type), they fire no matter how you commit — terminal, VS Code,
or any other git client.

The two files actually installed into `.git/hooks/` aren't the Python
logic itself — they're tiny shell shims:

```sh
#!/bin/sh
exec commitdev hook commit-msg "$@"
```

This calls back into whichever `commitdev` is on your PATH, so the hook
always runs in the right Python environment, regardless of which `python3`
git happens to find. It also means the same shim works unmodified on
Windows, since Git for Windows runs hooks through its bundled `sh`.

A fire-and-forget design choice: the API call in `post-commit` has a short
timeout and silently swallows any network error. A slow or unreachable
CommitDev server should never block or break a developer's commit.

## Package layout

```
commitdev/
├── main.py            entry point — wires up `login`, `logout`, `whoami`, `init`,
│                      and the hidden `hook` commands the installed shims call
├── installer.py       `commitdev init` — writes the two shims into .git/hooks/
├── config.py          reads/writes ~/.commitdev/config.json (the saved auth token)
├── commands/auth.py   `login` / `logout` / `whoami` — GitHub Device Flow auth
└── hooks/
    ├── commit_msg.py  strips known tags from the commit message
    └── post_commit.py reports tags + SHA to the CommitDev API
tests/
└── test_hooks.py      automated tests for the above
```

## What's still a placeholder

This package is the **client** half only — there's no real CommitDev
backend yet, so two things are intentionally stubbed and marked `TODO` in
the code:

- `GITHUB_CLIENT_ID` in `commands/auth.py` — needs a real GitHub OAuth App
  (with Device Flow enabled) before `commitdev login` can fully work.
- The API URLs (`api.commitdev.com/...`) — needs a real server before the
  `login` exchange and the `post-commit` report actually go anywhere.

Everything else — hook installation, tag stripping, and the local
fire-and-forget reporting logic — works today, independent of the backend.

## Install

```
pip install -e .
```

This installs the package and adds a `commitdev` command to your terminal.

## Manual test (no backend needed)

This exercises everything except the actual network calls, since there's
no live server yet.

```
# 1. Make a throwaway test repo — don't use a real project for this
mkdir test-repo && cd test-repo
git init

# 2. Install the hooks into it
commitdev init
# -> should print "commit-msg: installed." and "post-commit: installed."

# 3. Make a tagged commit
git commit --allow-empty -m "[draft][linkedin] testing this out"

# 4. Check what actually got saved
git log -1
# -> message should read "testing this out", no brackets

# 5. Confirm tags were captured for reporting
cat .git/commitdev_pending.json
# -> {"tags": ["draft", "linkedin"]}   (gets deleted after post-commit runs)

# 6. Confirm unrelated brackets are left alone
git commit --allow-empty -m "[JIRA-456] Fix login bug"
git log -1
# -> message is unchanged, [JIRA-456] still there

# 7. Confirm a re-run of init doesn't duplicate anything
commitdev init
# -> should print "already installed" for both hooks
```

## Automated tests

```
pip install -e ".[dev]"
pytest
```

`tests/test_hooks.py` covers, against the real package code in a real
throwaway git repo:

- `commitdev init` installs both hooks, correctly, and is safe to re-run
- known tags are stripped from the message; unrelated bracketed text isn't
- commits with no tags are left completely untouched, no extra files created
- `post-commit` never throws or blocks a commit when no one is logged in

These were also verified manually before being checked in.

## Try the real auth flow

This part needs network access and a real GitHub OAuth App client ID
(currently a placeholder), so it won't fully complete yet — but you can
confirm the flow starts correctly:

```
commitdev login
# -> should print a GitHub URL + a one-time code, and open your browser
```

## Recognized commit tags

`[post]` `[draft]` `[regenerate]` `[schedule]` · `[linkedin]` `[x]` `[devto]`
`[hashnode]` · `[tone=...]` `[length=...]` · `[feature]` `[bugfix]`
`[refactor]` `[performance]` `[security]` `[docs]` `[test]`
