Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ uv sync --extra dev
uv run pytest
```

## Usage (Phases 0–2)
## Usage (Phases 0–3)

```bash
uv run gitmate --help
Expand All @@ -32,12 +32,16 @@ uv run gitmate config set ignore_globs '*.gen.py,*.tmp' # comma-separated, empt
uv run gitmate config set max_context_tokens 8000 # override model context limit
uv run gitmate config set reserved_output_tokens 1500 # reserve headroom for model reply
uv run gitmate config set template_overhead 600 # reserve headroom for prompt template
uv run gitmate config set cache_dir /path/to/cache # custom prompt cache directory
uv run gitmate debug-diff # staged diff: per-file table + cleaned patch
uv run gitmate debug-diff --summary # per-file stats table only, no patch text
uv run gitmate debug-diff --base main # branch-vs-main diff (PR path)
```

`commit`, `pr-summary`, `changelog`, and `doc` are stubbed until their phases
Phase 3 introduces the `LLMProvider` abstraction, backed by `GeminiProvider`
(targeting `gemini-3.5-flash-lite` with tenacity exponential backoff retries)
and wrapped by `CachedProvider` (hash-keyed disk caching via `diskcache`).
`commit`, `pr-summary`, `changelog`, and `doc` remain stubbed until their phases
land — see `AGENTS.md` and the phased roadmap for details.

## Diagrams
Expand All @@ -48,6 +52,6 @@ files in the draw.io desktop app to edit):
| Diagram | Source | Export |
|---|---|---|
| System architecture (Phases 0–8, gemini-3.5-flash-lite) | [`docs/architecture.drawio`](docs/architecture.drawio) | [`docs/architecture.png`](docs/architecture.png) |
| Class diagram (Phases 0–2: config, diff extraction, token budgeting) | [`docs/class-diagram.drawio`](docs/class-diagram.drawio) | [`docs/class-diagram.png`](docs/class-diagram.png) |
| Class diagram (Phases 0–3: config, diff extraction, token budgeting, providers & caching) | [`docs/class-diagram.drawio`](docs/class-diagram.drawio) | [`docs/class-diagram.png`](docs/class-diagram.png) |
| Use cases (implemented + planned) | [`docs/use-case.drawio`](docs/use-case.drawio) | [`docs/use-case.png`](docs/use-case.png) |
| `config` command sequences | [`docs/sequence.drawio`](docs/sequence.drawio) | [`docs/sequence.png`](docs/sequence.png) |
91 changes: 81 additions & 10 deletions docs/class-diagram.drawio
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<mxfile host="drawio" version="26.0.0">
<diagram name="class-diagram">
<mxGraphModel dx="1200" dy="800" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1500" pageHeight="1020" math="0" shadow="0">
<mxGraphModel dx="1200" dy="900" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1500" pageHeight="1150" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>

<!-- Header -->
<mxCell id="title" value="gitmate — Architecture &amp; Class Diagram (Phases 0–2)" style="text;html=1;align=left;verticalAlign=middle;fontStyle=1;fontSize=22;" vertex="1" parent="1">
<mxCell id="title" value="gitmate — Architecture &amp; Class Diagram (Phases 0–3)" style="text;html=1;align=left;verticalAlign=middle;fontStyle=1;fontSize=22;" vertex="1" parent="1">
<mxGeometry x="40" y="20" width="800" height="34" as="geometry"/>
</mxCell>
<mxCell id="subtitle" value="config.py + diff_extractor.py + token_budget.py + cli.py · Strict typing, I/O behind protocols, offline-first verification" style="text;html=1;align=left;verticalAlign=middle;fontSize=12;fontColor=#555555;" vertex="1" parent="1">
<mxCell id="subtitle" value="config.py + diff_extractor.py + token_budget.py + providers/ + cli.py · Strict typing, I/O behind protocols, offline-first verification" style="text;html=1;align=left;verticalAlign=middle;fontSize=12;fontColor=#555555;" vertex="1" parent="1">
<mxGeometry x="40" y="54" width="1100" height="22" as="geometry"/>
</mxCell>

<!-- Phase 0: Config & Secrets -->
<mxCell id="cfg_t" value="&lt;b&gt;GitmateConfig&lt;/b&gt;&lt;br/&gt;«dataclass»" style="rounded=1;whiteSpace=wrap;html=1;align=center;fontSize=13;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="50" y="90" width="260" height="40" as="geometry"/>
</mxCell>
<mxCell id="cfg_b" value="+ model: str = DEFAULT_MODEL&lt;br/&gt;+ commit_style: str = &quot;conventional&quot;&lt;br/&gt;+ budget_cap_usd: float | None = None&lt;br/&gt;+ ignore_globs: list[str]&lt;br/&gt;+ max_context_tokens: int | None = None&lt;br/&gt;+ reserved_output_tokens: int = 2048&lt;br/&gt;+ template_overhead: int = 500" style="rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;spacingLeft=8;spacingTop=6;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="50" y="130" width="260" height="120" as="geometry"/>
<mxCell id="cfg_b" value="+ model: str = DEFAULT_MODEL&lt;br/&gt;+ commit_style: str = &quot;conventional&quot;&lt;br/&gt;+ budget_cap_usd: float | None = None&lt;br/&gt;+ ignore_globs: list[str]&lt;br/&gt;+ max_context_tokens: int | None = None&lt;br/&gt;+ reserved_output_tokens: int = 2048&lt;br/&gt;+ template_overhead: int = 500&lt;br/&gt;+ cache_dir: str | None = None" style="rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;spacingLeft=8;spacingTop=6;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="50" y="130" width="260" height="135" as="geometry"/>
</mxCell>

<mxCell id="ss_t" value="&lt;b&gt;SecretStore&lt;/b&gt;&lt;br/&gt;«protocol»" style="rounded=1;whiteSpace=wrap;html=1;align=center;fontSize=13;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1">
Expand Down Expand Up @@ -83,8 +83,8 @@
<mxCell id="tbm_t" value="&lt;b&gt;TokenBudgetManager&lt;/b&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=center;fontSize=13;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="50" y="520" width="260" height="35" as="geometry"/>
</mxCell>
<mxCell id="tbm_b" value="+ counter: TokenCounter&lt;br/&gt;+ model: str = DEFAULT_MODEL&lt;br/&gt;+ context_window: int = 1_048_576&lt;br/&gt;+ reserved_output_tokens: int = 2048&lt;br/&gt;+ default_template_overhead: int = 500&lt;br/&gt;+ count_diff_tokens(diffs) -&amp;gt; int&lt;br/&gt;+ assess(diffs, overhead) -&amp;gt; BudgetDecision" style="rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;spacingLeft=8;spacingTop=6;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="50" y="555" width="260" height="130" as="geometry"/>
<mxCell id="tbm_b" value="+ counter: TokenCounter&lt;br/&gt;+ model: str = DEFAULT_MODEL&lt;br/&gt;+ context_window: int = 1_048_576&lt;br/&gt;+ reserved_output_tokens: int = 2048&lt;br/&gt;+ default_template_overhead: int = 500&lt;br/&gt;+ count_diff_tokens(diffs) -&amp;gt; int&lt;br/&gt;+ assess(diffs, overhead) -&amp;gt; BudgetDecision&lt;br/&gt;+ from_config(cfg, counter) -&amp;gt; TokenBudgetManager" style="rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;spacingLeft=8;spacingTop=6;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="50" y="555" width="260" height="145" as="geometry"/>
</mxCell>

<mxCell id="tc_t" value="&lt;b&gt;TokenCounter&lt;/b&gt;&lt;br/&gt;«protocol»" style="rounded=1;whiteSpace=wrap;html=1;align=center;fontSize=13;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1">
Expand Down Expand Up @@ -122,17 +122,67 @@
<mxGeometry x="930" y="680" width="180" height="35" as="geometry"/>
</mxCell>

<!-- Phase 3: Provider Abstraction & Cache Layer -->
<mxCell id="llmp_t" value="&lt;b&gt;LLMProvider&lt;/b&gt;&lt;br/&gt;«protocol»" style="rounded=1;whiteSpace=wrap;html=1;align=center;fontSize=13;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1">
<mxGeometry x="50" y="740" width="260" height="35" as="geometry"/>
</mxCell>
<mxCell id="llmp_b" value="+ generate(prompt: str, model: str) -&amp;gt; LLMResponse" style="rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;spacingLeft=8;spacingTop=6;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1">
<mxGeometry x="50" y="775" width="260" height="40" as="geometry"/>
</mxCell>

<mxCell id="gem_t" value="&lt;b&gt;GeminiProvider&lt;/b&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=center;fontSize=12;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="50" y="850" width="260" height="30" as="geometry"/>
</mxCell>
<mxCell id="gem_b" value="- _client: genai.Client | None&lt;br/&gt;- _api_key: str | None&lt;br/&gt;+ generate(prompt, model) -&amp;gt; LLMResponse&lt;br/&gt;- _generate_with_retry() via tenacity&lt;br/&gt;- _is_transient(exc) -&amp;gt; bool" style="rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;spacingLeft=8;spacingTop=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="50" y="880" width="260" height="85" as="geometry"/>
</mxCell>

<mxCell id="cp_t" value="&lt;b&gt;CachedProvider&lt;/b&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=center;fontSize=12;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="360" y="740" width="250" height="30" as="geometry"/>
</mxCell>
<mxCell id="cp_b" value="- _provider: LLMProvider&lt;br/&gt;- _cache: Cache (diskcache)&lt;br/&gt;- _ttl: float | None&lt;br/&gt;- _template_version: str = &quot;1&quot;&lt;br/&gt;+ generate(prompt, model) -&amp;gt; LLMResponse&lt;br/&gt;+ close() -&amp;gt; None" style="rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;spacingLeft=8;spacingTop=4;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="360" y="770" width="250" height="100" as="geometry"/>
</mxCell>

<mxCell id="ck_t" value="&lt;b&gt;cache_key()&lt;/b&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=center;fontSize=11;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="360" y="900" width="250" height="25" as="geometry"/>
</mxCell>
<mxCell id="ck_b" value="sha256(version + \x00 + model + \x00 + prompt)" style="rounded=0;whiteSpace=wrap;html=1;align=center;verticalAlign=middle;fontSize=10;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="360" y="925" width="250" height="30" as="geometry"/>
</mxCell>

<mxCell id="llmr_t" value="&lt;b&gt;LLMResponse&lt;/b&gt;&lt;br/&gt;«dataclass»" style="rounded=1;whiteSpace=wrap;html=1;align=center;fontSize=13;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="660" y="740" width="230" height="35" as="geometry"/>
</mxCell>
<mxCell id="llmr_b" value="+ text: str&lt;br/&gt;+ model: str&lt;br/&gt;+ input_tokens: int | None = None&lt;br/&gt;+ output_tokens: int | None = None" style="rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;spacingLeft=8;spacingTop=6;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="660" y="775" width="230" height="75" as="geometry"/>
</mxCell>

<mxCell id="pu_t" value="&lt;b&gt;ProviderUnavailable&lt;/b&gt;&lt;br/&gt;«extends Exception»" style="rounded=1;whiteSpace=wrap;html=1;align=center;fontSize=12;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="1">
<mxGeometry x="930" y="740" width="190" height="30" as="geometry"/>
</mxCell>
<mxCell id="pu_b" value="raised when backend fails retries" style="rounded=0;whiteSpace=wrap;html=1;align=center;verticalAlign=middle;fontSize=10;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="1">
<mxGeometry x="930" y="770" width="190" height="25" as="geometry"/>
</mxCell>

<mxCell id="rpe_t" value="&lt;b&gt;RetryableProviderError&lt;/b&gt;&lt;br/&gt;«extends Exception»" style="rounded=1;whiteSpace=wrap;html=1;align=center;fontSize=12;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="1">
<mxGeometry x="930" y="820" width="190" height="30" as="geometry"/>
</mxCell>
<mxCell id="rpe_b" value="internal transient error marker" style="rounded=0;whiteSpace=wrap;html=1;align=center;verticalAlign=middle;fontSize=10;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="1">
<mxGeometry x="930" y="850" width="190" height="25" as="geometry"/>
</mxCell>

<!-- CLI Box -->
<mxCell id="cli_t" value="&lt;b&gt;gitmate CLI&lt;/b&gt;&lt;br/&gt;Typer app · commands only, no business logic" style="rounded=1;whiteSpace=wrap;html=1;align=center;fontSize=13;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="1100" y="160" width="340" height="40" as="geometry"/>
</mxCell>
<mxCell id="cli_b" value="&lt;b&gt;Commands implemented:&lt;/b&gt;&lt;br/&gt;• config set-key()&lt;br/&gt;• config show()&lt;br/&gt;• config set(field, value)&lt;br/&gt;• debug-diff(--base, --from-ref, --to-ref, --summary)&lt;br/&gt;&lt;br/&gt;&lt;b&gt;Stubs for future phases:&lt;/b&gt;&lt;br/&gt;• commit() (Phase 5)&lt;br/&gt;• pr-summary() (Phase 7)&lt;br/&gt;• changelog() (Phase 7)&lt;br/&gt;• doc()" style="rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;spacingLeft=10;spacingTop=6;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxCell id="cli_b" value="&lt;b&gt;Commands implemented:&lt;/b&gt;&lt;br/&gt;• config set-key()&lt;br/&gt;• config show()&lt;br/&gt;• config set(field, value) [incl. cache_dir]&lt;br/&gt;• debug-diff(--base, --from-ref, --to-ref, --summary)&lt;br/&gt;&lt;br/&gt;&lt;b&gt;Stubs for future phases:&lt;/b&gt;&lt;br/&gt;• commit() (Phase 5)&lt;br/&gt;• pr-summary() (Phase 7)&lt;br/&gt;• changelog() (Phase 7)&lt;br/&gt;• doc()" style="rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;spacingLeft=10;spacingTop=6;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="1100" y="200" width="340" height="190" as="geometry"/>
</mxCell>

<!-- Legend -->
<mxCell id="legend" value="Legend" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=#666666;verticalAlign=top;fontStyle=1;fontSize=13;" vertex="1" parent="1">
<mxGeometry x="50" y="760" width="1060" height="60" as="geometry"/>
<mxGeometry x="50" y="1010" width="1060" height="60" as="geometry"/>
</mxCell>
<mxCell id="leg1" value="" style="rounded=0;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="legend">
<mxGeometry x="20" y="30" width="30" height="16" as="geometry"/>
Expand Down Expand Up @@ -181,7 +231,7 @@
<mxCell id="e_tbm_bd" value="produces" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;fontSize=11;endArrow=open;endFill=0;dashed=1;exitX=1;exitY=0.8;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="tbm_b" target="bd_b">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="340" y="659"/>
<mxPoint x="340" y="671"/>
<mxPoint x="340" y="630"/>
</Array>
</mxGeometry>
Expand All @@ -193,6 +243,27 @@
<mxGeometry relative="1" as="geometry"/>
</mxCell>

<!-- Phase 3 Connectors -->
<mxCell id="e_gem_llmp" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=0;dashed=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="gem_t" target="llmp_b">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="e_cp_llmp" value="wraps" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;fontSize=11;endArrow=open;endFill=0;dashed=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="cp_t" target="llmp_t">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="e_llmp_llmr" value="produces" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;fontSize=11;endArrow=open;endFill=0;dashed=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="llmp_b" target="llmr_t">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="330" y="795"/>
<mxPoint x="330" y="725"/>
<mxPoint x="640" y="725"/>
<mxPoint x="640" y="757"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e_ck_cp" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;endArrow=open;endFill=0;dashed=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="ck_t" target="cp_b">
<mxGeometry relative="1" as="geometry"/>
</mxCell>

</root>
</mxGraphModel>
</diagram>
Expand Down
Binary file modified docs/class-diagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading