saga-cli 0.1.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.
@@ -0,0 +1,38 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ lint:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+ - name: Install uv
14
+ uses: astral-sh/setup-uv@v5
15
+ - name: Install dev tools
16
+ run: uv sync --group dev
17
+ - name: Ruff lint
18
+ run: uv run ruff check .
19
+ - name: Ruff format
20
+ run: uv run ruff format --check .
21
+ - name: Type check
22
+ run: uv run ty check .
23
+
24
+ test:
25
+ runs-on: ubuntu-latest
26
+ strategy:
27
+ matrix:
28
+ python-version: ["3.11", "3.12", "3.13"]
29
+ steps:
30
+ - uses: actions/checkout@v4
31
+ - name: Install uv
32
+ uses: astral-sh/setup-uv@v5
33
+ with:
34
+ python-version: ${{ matrix.python-version }}
35
+ - name: Install
36
+ run: uv sync --group dev
37
+ - name: Run tests
38
+ run: uv run python -m pytest -q
@@ -0,0 +1,16 @@
1
+ *.egg-info/
2
+ __pycache__/
3
+ .pytest_cache/
4
+ .coverage
5
+ .venv/
6
+ .DS_Store
7
+ .ruff_cache
8
+ *.pid
9
+ *.pyc
10
+ *.pyo
11
+ *.pyd
12
+ **/.vscode/*
13
+ !**/.vscode/extensions.json
14
+
15
+ dist/
16
+ saga.html
@@ -0,0 +1,46 @@
1
+ # Contributing
2
+
3
+ Thanks for your interest in Saga.
4
+
5
+ ## Development setup
6
+
7
+ ```sh
8
+ git clone https://github.com/JakeBeresford/saga
9
+ cd saga
10
+ uv pip install -e . pytest # or: pip install -e . pytest
11
+ ```
12
+
13
+ Run the tests:
14
+
15
+ ```sh
16
+ python -m pytest
17
+ ```
18
+
19
+ Lint, format, and type-check (install the dev tools with `uv sync --group dev`):
20
+
21
+ ```sh
22
+ uv run ruff check . # lint
23
+ uv run ruff format . # auto-format
24
+ uv run ty check . # type check
25
+ ```
26
+
27
+ To try the CLI against a real repo while developing, install it as an editable
28
+ tool and run it from inside any git checkout:
29
+
30
+ ```sh
31
+ uv tool install --editable .
32
+ saga --base main --head my-feature
33
+ ```
34
+
35
+ ## Pull requests
36
+
37
+ - Keep changes focused and match the existing style.
38
+ - Add or update tests for any behavior change — the core model logic in
39
+ `saga/model.py` is unit-tested in `tests/test_model.py`.
40
+ - Make sure `python -m pytest` passes and `ruff`/`ty` are clean before opening a
41
+ PR. CI runs the suite on Python 3.11–3.13 plus a lint/format/type-check job.
42
+
43
+ ## Reporting issues
44
+
45
+ Open an issue at https://github.com/JakeBeresford/saga/issues with steps to
46
+ reproduce, the command you ran, and what you expected to happen.
saga_cli-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jake Beresford
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,158 @@
1
+ Metadata-Version: 2.4
2
+ Name: saga-cli
3
+ Version: 0.1.0
4
+ Summary: Generate a self-contained static HTML saga of a git diff — a chapter-by-chapter guided tour of a change.
5
+ Project-URL: Homepage, https://github.com/JakeBeresford/saga
6
+ Project-URL: Repository, https://github.com/JakeBeresford/saga
7
+ Project-URL: Issues, https://github.com/JakeBeresford/saga/issues
8
+ Author: Jake Beresford
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: code-review,diff,documentation,git,html,llm
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: Documentation
20
+ Classifier: Topic :: Software Development :: Version Control :: Git
21
+ Requires-Python: >=3.11
22
+ Requires-Dist: anthropic
23
+ Requires-Dist: instructor
24
+ Requires-Dist: openai
25
+ Requires-Dist: typer>=0.26.8
26
+ Description-Content-Type: text/markdown
27
+
28
+ # Saga
29
+
30
+ Generate a **chapter-by-chapter guided tour** of a code change as a single,
31
+ self-contained static HTML page, a _saga_ of your diff. It partitions a diff into
32
+ ordered chapters that tell one coherent story, each with a plain-language narration
33
+ and just the hunks that belong to it. Large PRs become easy to review without
34
+ losing the thread.
35
+
36
+ The output is one HTML file with everything inlined (diff2html, syntax highlighting,
37
+ the data). Open it offline, email it, commit it, or drop it on any static host.
38
+
39
+ [**See an example saga**](https://jakeberesford.github.io/saga/example.html).
40
+
41
+ ## Requirements
42
+
43
+ - Python 3.11+
44
+ - `git`
45
+ - An API key for your chosen provider, in the standard environment variable:
46
+ `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, or `OPENROUTER_API_KEY`
47
+
48
+ Generation is one structured LLM call made through [`instructor`](https://python.useinstructor.com).
49
+
50
+ ## Install
51
+
52
+ Install once and the `saga` command is available from any repo:
53
+
54
+ ```sh
55
+ uv tool install saga-cli # recommended
56
+ # or
57
+ pipx install saga-cli
58
+ # or, into the current environment
59
+ pip install saga-cli
60
+ ```
61
+
62
+ Installing from a local checkout instead? Point the installer at this directory,
63
+ e.g. `uv tool install /path/to/saga`. To upgrade: `uv tool install --force saga-cli`.
64
+
65
+ ## Usage
66
+
67
+ From inside the repo you want to review:
68
+
69
+ ```sh
70
+ saga --base main --head my-feature -o saga.html --open
71
+ ```
72
+
73
+ Run it from anywhere with `--repo`:
74
+
75
+ ```sh
76
+ saga --repo ~/src/some-project --base main --head my-feature -o out.html
77
+ ```
78
+
79
+ | Flag | Default | Meaning |
80
+ | ---------------------- | --------------------------- | -------------------------------------------------------------------------------------------------- |
81
+ | `--base` | `main` | Base ref to diff against |
82
+ | `--head` | current branch | Head ref to walk through |
83
+ | `--intent PATH` | — | Optional plan/spec describing the change's intent, for plan-aware narration and deviation flagging |
84
+ | `--model` | `anthropic/claude-opus-4-8` | `provider/model` string (see [Providers](#providers)); also `$SAGA_MODEL` |
85
+ | `-o, --output` | `saga.html` | Output file |
86
+ | `--repo` | cwd | A path inside the target git repo |
87
+ | `--open` / `--no-open` | on | Open the result in a browser (on by default; `--no-open` to disable) |
88
+
89
+ ## Providers
90
+
91
+ The model is a single `provider/model` string, dispatched through `instructor`.
92
+ Choose it with `--model` or the `SAGA_MODEL` environment variable, and set
93
+ the matching API key:
94
+
95
+ | Provider | `--model` example | API key env var |
96
+ | ---------- | ---------------------------------------- | -------------------- |
97
+ | Anthropic | `anthropic/claude-opus-4-8` | `ANTHROPIC_API_KEY` |
98
+ | OpenAI | `openai/gpt-4o` | `OPENAI_API_KEY` |
99
+ | OpenRouter | `openrouter/anthropic/claude-3.5-sonnet` | `OPENROUTER_API_KEY` |
100
+
101
+ ```sh
102
+ export SAGA_MODEL=openai/gpt-4o
103
+ export OPENAI_API_KEY=sk-…
104
+ saga --base main --head my-feature -o saga.html
105
+ ```
106
+
107
+ ## As a Claude Code skill
108
+
109
+ The `skills/` directory contains two Claude Code skills. To install both:
110
+
111
+ ```sh
112
+ cp -R "$(pwd)/skills" ~/.claude/skills/saga
113
+ ```
114
+
115
+ - **`saga`** — say **"/saga"** (or "give me a walkthrough of this branch") to generate a
116
+ saga. It resolves the base/head refs and runs the tool for you.
117
+ - **`saga-comments`** — say **"/saga-comments"** (or "address the saga comments") to read an
118
+ exported `saga.comments.json` and act on the reviewer's feedback in code.
119
+
120
+ ## Reviewing: comments
121
+
122
+ The saga page is also a lightweight review surface. Open `saga.html` and leave three
123
+ kinds of comments — **inline** (click a line's number in any chapter's diff), **per-file**
124
+ (the "💬 File comment" control in each file header), and one **overall** review comment
125
+ (the box at the top of the Chapters list). Comments are drafted in your browser's
126
+ `localStorage`, so they survive a reload.
127
+
128
+ When you're done, click **Export comments** to download a `saga.comments.json` sidecar next
129
+ to the HTML. (Export is disabled in the [hosted example](https://jakeberesford.github.io/saga/example.html)
130
+ so it never writes a file — every other part of the review UX works.) Two commands consume it:
131
+
132
+ ```sh
133
+ # Post everything as a single PENDING review on the PR (you submit it on GitHub).
134
+ saga comments push --comments saga.comments.json
135
+
136
+ # Emit the comments as JSON on stdout — for a coding agent to act on.
137
+ saga comments read --comments saga.comments.json
138
+ ```
139
+
140
+ `push` uses the `gh` CLI: it finds the open PR for the sidecar's branch and creates one
141
+ pending review (inline → line comments, per-file → a note anchored to the file's first
142
+ changed line, overall → the review body). Nothing is submitted until you review and submit
143
+ it on GitHub. Requires the [`gh`](https://cli.github.com) CLI, authenticated.
144
+
145
+ ## How it works
146
+
147
+ 1. `diff.py` computes `git diff base...head` (no checkout) and the commit list.
148
+ 2. `model.py` splits the diff into stable-id hunks (`h0, h1, …`).
149
+ 3. `generate.py` sends the labeled diff + commits (+ optional intent) to the chosen
150
+ model via `instructor`, which returns chapters as schema-validated JSON. Coverage
151
+ is **re-validated in code** — every hunk must belong to a chapter or generation fails.
152
+ 4. `render.py` reconstructs each chapter's diff and inlines everything into one
153
+ self-contained HTML file.
154
+
155
+ ## Not included (yet)
156
+
157
+ - Support for Local LLMs via ollama / LM Studio
158
+ - A GitHub Action to auto-generate saga on PRs.
@@ -0,0 +1,131 @@
1
+ # Saga
2
+
3
+ Generate a **chapter-by-chapter guided tour** of a code change as a single,
4
+ self-contained static HTML page, a _saga_ of your diff. It partitions a diff into
5
+ ordered chapters that tell one coherent story, each with a plain-language narration
6
+ and just the hunks that belong to it. Large PRs become easy to review without
7
+ losing the thread.
8
+
9
+ The output is one HTML file with everything inlined (diff2html, syntax highlighting,
10
+ the data). Open it offline, email it, commit it, or drop it on any static host.
11
+
12
+ [**See an example saga**](https://jakeberesford.github.io/saga/example.html).
13
+
14
+ ## Requirements
15
+
16
+ - Python 3.11+
17
+ - `git`
18
+ - An API key for your chosen provider, in the standard environment variable:
19
+ `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, or `OPENROUTER_API_KEY`
20
+
21
+ Generation is one structured LLM call made through [`instructor`](https://python.useinstructor.com).
22
+
23
+ ## Install
24
+
25
+ Install once and the `saga` command is available from any repo:
26
+
27
+ ```sh
28
+ uv tool install saga-cli # recommended
29
+ # or
30
+ pipx install saga-cli
31
+ # or, into the current environment
32
+ pip install saga-cli
33
+ ```
34
+
35
+ Installing from a local checkout instead? Point the installer at this directory,
36
+ e.g. `uv tool install /path/to/saga`. To upgrade: `uv tool install --force saga-cli`.
37
+
38
+ ## Usage
39
+
40
+ From inside the repo you want to review:
41
+
42
+ ```sh
43
+ saga --base main --head my-feature -o saga.html --open
44
+ ```
45
+
46
+ Run it from anywhere with `--repo`:
47
+
48
+ ```sh
49
+ saga --repo ~/src/some-project --base main --head my-feature -o out.html
50
+ ```
51
+
52
+ | Flag | Default | Meaning |
53
+ | ---------------------- | --------------------------- | -------------------------------------------------------------------------------------------------- |
54
+ | `--base` | `main` | Base ref to diff against |
55
+ | `--head` | current branch | Head ref to walk through |
56
+ | `--intent PATH` | — | Optional plan/spec describing the change's intent, for plan-aware narration and deviation flagging |
57
+ | `--model` | `anthropic/claude-opus-4-8` | `provider/model` string (see [Providers](#providers)); also `$SAGA_MODEL` |
58
+ | `-o, --output` | `saga.html` | Output file |
59
+ | `--repo` | cwd | A path inside the target git repo |
60
+ | `--open` / `--no-open` | on | Open the result in a browser (on by default; `--no-open` to disable) |
61
+
62
+ ## Providers
63
+
64
+ The model is a single `provider/model` string, dispatched through `instructor`.
65
+ Choose it with `--model` or the `SAGA_MODEL` environment variable, and set
66
+ the matching API key:
67
+
68
+ | Provider | `--model` example | API key env var |
69
+ | ---------- | ---------------------------------------- | -------------------- |
70
+ | Anthropic | `anthropic/claude-opus-4-8` | `ANTHROPIC_API_KEY` |
71
+ | OpenAI | `openai/gpt-4o` | `OPENAI_API_KEY` |
72
+ | OpenRouter | `openrouter/anthropic/claude-3.5-sonnet` | `OPENROUTER_API_KEY` |
73
+
74
+ ```sh
75
+ export SAGA_MODEL=openai/gpt-4o
76
+ export OPENAI_API_KEY=sk-…
77
+ saga --base main --head my-feature -o saga.html
78
+ ```
79
+
80
+ ## As a Claude Code skill
81
+
82
+ The `skills/` directory contains two Claude Code skills. To install both:
83
+
84
+ ```sh
85
+ cp -R "$(pwd)/skills" ~/.claude/skills/saga
86
+ ```
87
+
88
+ - **`saga`** — say **"/saga"** (or "give me a walkthrough of this branch") to generate a
89
+ saga. It resolves the base/head refs and runs the tool for you.
90
+ - **`saga-comments`** — say **"/saga-comments"** (or "address the saga comments") to read an
91
+ exported `saga.comments.json` and act on the reviewer's feedback in code.
92
+
93
+ ## Reviewing: comments
94
+
95
+ The saga page is also a lightweight review surface. Open `saga.html` and leave three
96
+ kinds of comments — **inline** (click a line's number in any chapter's diff), **per-file**
97
+ (the "💬 File comment" control in each file header), and one **overall** review comment
98
+ (the box at the top of the Chapters list). Comments are drafted in your browser's
99
+ `localStorage`, so they survive a reload.
100
+
101
+ When you're done, click **Export comments** to download a `saga.comments.json` sidecar next
102
+ to the HTML. (Export is disabled in the [hosted example](https://jakeberesford.github.io/saga/example.html)
103
+ so it never writes a file — every other part of the review UX works.) Two commands consume it:
104
+
105
+ ```sh
106
+ # Post everything as a single PENDING review on the PR (you submit it on GitHub).
107
+ saga comments push --comments saga.comments.json
108
+
109
+ # Emit the comments as JSON on stdout — for a coding agent to act on.
110
+ saga comments read --comments saga.comments.json
111
+ ```
112
+
113
+ `push` uses the `gh` CLI: it finds the open PR for the sidecar's branch and creates one
114
+ pending review (inline → line comments, per-file → a note anchored to the file's first
115
+ changed line, overall → the review body). Nothing is submitted until you review and submit
116
+ it on GitHub. Requires the [`gh`](https://cli.github.com) CLI, authenticated.
117
+
118
+ ## How it works
119
+
120
+ 1. `diff.py` computes `git diff base...head` (no checkout) and the commit list.
121
+ 2. `model.py` splits the diff into stable-id hunks (`h0, h1, …`).
122
+ 3. `generate.py` sends the labeled diff + commits (+ optional intent) to the chosen
123
+ model via `instructor`, which returns chapters as schema-validated JSON. Coverage
124
+ is **re-validated in code** — every hunk must belong to a chapter or generation fails.
125
+ 4. `render.py` reconstructs each chapter's diff and inlines everything into one
126
+ self-contained HTML file.
127
+
128
+ ## Not included (yet)
129
+
130
+ - Support for Local LLMs via ollama / LM Studio
131
+ - A GitHub Action to auto-generate saga on PRs.
@@ -0,0 +1,34 @@
1
+ # Releasing
2
+
3
+ Releases are published to PyPI **manually, by the sole maintainer**. There is
4
+ no CI publish workflow by design — nothing but a local machine holding the
5
+ maintainer's token can push a release.
6
+
7
+ ## One-time setup
8
+
9
+ 1. On [PyPI](https://pypi.org): create the account, enable **2FA**, and remain
10
+ the **sole Owner** of `saga-cli`. Do not add other Owners or Maintainers.
11
+ 2. For the **first** upload the project does not exist yet, so create an
12
+ **account-scoped** API token, publish once (below), then immediately create a
13
+ **project-scoped** token for `saga-cli` and delete the account-scoped one.
14
+ 3. Store the token locally only — e.g. `~/.pypirc`, or export it per release:
15
+ ```sh
16
+ export UV_PUBLISH_TOKEN=pypi-…
17
+ ```
18
+
19
+ ## Cutting a release
20
+
21
+ ```sh
22
+ # 1. Bump the version in pyproject.toml, commit, and tag
23
+ git tag v0.1.0 && git push origin main --tags
24
+
25
+ # 2. Build clean and publish
26
+ rm -rf dist
27
+ uv build
28
+ uv publish # uses UV_PUBLISH_TOKEN, or ~/.pypirc
29
+
30
+ # 3. Cut a matching GitHub Release for the tag (changelog / notes)
31
+ ```
32
+
33
+ Verify the upload with `pip index versions saga-cli` or by visiting the PyPI
34
+ project page.