amagi-site-diff 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.
Files changed (26) hide show
  1. amagi_site_diff-0.1.0/LICENSE +21 -0
  2. amagi_site_diff-0.1.0/PKG-INFO +194 -0
  3. amagi_site_diff-0.1.0/README.md +171 -0
  4. amagi_site_diff-0.1.0/pyproject.toml +42 -0
  5. amagi_site_diff-0.1.0/setup.cfg +4 -0
  6. amagi_site_diff-0.1.0/src/amagi_site_diff/__init__.py +3 -0
  7. amagi_site_diff-0.1.0/src/amagi_site_diff/__main__.py +3 -0
  8. amagi_site_diff-0.1.0/src/amagi_site_diff/capture.py +271 -0
  9. amagi_site_diff-0.1.0/src/amagi_site_diff/cli.py +160 -0
  10. amagi_site_diff-0.1.0/src/amagi_site_diff/imagediff.py +152 -0
  11. amagi_site_diff-0.1.0/src/amagi_site_diff/locate.py +58 -0
  12. amagi_site_diff-0.1.0/src/amagi_site_diff/report.py +182 -0
  13. amagi_site_diff-0.1.0/src/amagi_site_diff/runner.py +262 -0
  14. amagi_site_diff-0.1.0/src/amagi_site_diff/textdiff.py +43 -0
  15. amagi_site_diff-0.1.0/src/amagi_site_diff/urls.py +131 -0
  16. amagi_site_diff-0.1.0/src/amagi_site_diff.egg-info/PKG-INFO +194 -0
  17. amagi_site_diff-0.1.0/src/amagi_site_diff.egg-info/SOURCES.txt +24 -0
  18. amagi_site_diff-0.1.0/src/amagi_site_diff.egg-info/dependency_links.txt +1 -0
  19. amagi_site_diff-0.1.0/src/amagi_site_diff.egg-info/entry_points.txt +2 -0
  20. amagi_site_diff-0.1.0/src/amagi_site_diff.egg-info/requires.txt +5 -0
  21. amagi_site_diff-0.1.0/src/amagi_site_diff.egg-info/top_level.txt +1 -0
  22. amagi_site_diff-0.1.0/tests/test_cli_e2e.py +151 -0
  23. amagi_site_diff-0.1.0/tests/test_imagediff.py +104 -0
  24. amagi_site_diff-0.1.0/tests/test_runner_report.py +196 -0
  25. amagi_site_diff-0.1.0/tests/test_textdiff_locate.py +69 -0
  26. amagi_site_diff-0.1.0/tests/test_urls.py +94 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Amagisolutionsgroup, Inc. (AmagiLabs)
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,194 @@
1
+ Metadata-Version: 2.4
2
+ Name: amagi-site-diff
3
+ Version: 0.1.0
4
+ Summary: Two base URLs in, one markdown report out: crawl both sites, screenshot every matching page with Playwright, and write pixel-diff images plus text diffs an AI agent can read.
5
+ Author-email: "AmagiLabs (Amagisolutionsgroup, Inc.)" <dev@amagisolutionsgroup.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/amagilabs/amagi-site-diff
8
+ Keywords: visual-regression,screenshot,diff,playwright,website,deploy,qa,cli,claude-code
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Environment :: Console
13
+ Classifier: Topic :: Software Development :: Testing
14
+ Classifier: Topic :: Internet :: WWW/HTTP :: Site Management
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: playwright>=1.40
19
+ Requires-Dist: Pillow>=10.0
20
+ Provides-Extra: dev
21
+ Requires-Dist: pytest>=7.0; extra == "dev"
22
+ Dynamic: license-file
23
+
24
+ # amagi-site-diff
25
+
26
+ See what changed between two versions of a website before your client does.
27
+ `amagi-site-diff` takes two base URLs (production and staging, or the old build and the new one),
28
+ crawls the same pages on both, screenshots each page in headless Chromium, and writes one
29
+ markdown report: pixel-diff images, changed regions named by page element, text diffs, new console
30
+ errors, broken requests, and pages that went missing.
31
+
32
+ It's for freelancers and agencies shipping redesigns, CMS migrations and theme updates who want a
33
+ page-by-page check before launch. The report is plain markdown, so you can read it yourself or hand
34
+ it to Claude.
35
+
36
+ ## Demo
37
+
38
+ ![demo](assets/demo.gif)
39
+
40
+ ## Install
41
+
42
+ ```
43
+ pipx install amagi-site-diff
44
+ ```
45
+
46
+ (Not yet published to PyPI. Until it is, install from a clone of this repo with
47
+ `pipx install .`.)
48
+
49
+ Requires Python 3.10+. The first run downloads Playwright's Chromium if it isn't
50
+ already installed. To use the Chrome or Edge you already have instead, pass `--channel chrome` or
51
+ `--channel msedge`.
52
+
53
+ ## Usage
54
+
55
+ ```
56
+ amagi-site-diff https://example.com https://staging.example.com
57
+ ```
58
+
59
+ A is "before", B is "after". Starting from `/`, it follows links on both sides, staying on each
60
+ site's own origin and path, and compares up to 20 pages. Either side can also be a local folder,
61
+ such as a static build, which is served on 127.0.0.1 for the run.
62
+
63
+ This repo ships two versions of a fictional bike shop site, so you can try it without touching a
64
+ real one:
65
+
66
+ ```
67
+ cd examples
68
+ amagi-site-diff site-v1 site-v2
69
+ ```
70
+
71
+ ```
72
+ A: site-v1 (local folder)
73
+ B: site-v2 (local folder)
74
+ Comparing up to 20 pages, following links...
75
+ / changed 1.61% pixels, 3 region(s); text +3 -3
76
+ /services/ changed 0.76% pixels, 3 region(s); text +1 -1; meta description; 2 new console error(s); 1 new failed request(s)
77
+ /about/ identical
78
+ /contact/ missing on B B: HTTP 404
79
+ /blog/ new on B A: HTTP 404
80
+ /blog/winter-chain-care/ new on B A: HTTP 404
81
+ 6 page(s) compared: 2 changed, 1 identical, 1 missing on B, 2 new on B
82
+ Report: site-diff-report/report.md
83
+ ```
84
+
85
+ ### The report
86
+
87
+ `site-diff-report/report.md` opens with a summary table, then one section for each page that
88
+ differs. Each section has a diff image: B faded to grey, changed pixels in red, and numbered
89
+ blue boxes around each changed region.
90
+
91
+ ![diff image for the home page](assets/diff-home.png)
92
+
93
+ The boxes are listed with the element under them on each side, followed by the text diff:
94
+
95
+ ```
96
+ 1. `160,214 225x42` (9,039 px): B `main > a.button` "Book a repair"; A `main > a.button` "See services and prices"
97
+ 2. `384,172 689x18` (5,885 px): B `main > p.lead:nth-of-type(1)` "Drop your bike off before 11 am and pick it up the same a..."; A `main > p.lead:nth-of-type(1)` "Drop your bike off before 10 am and pick it up the same a..."
98
+ ```
99
+
100
+ ```diff
101
+ -Drop your bike off before 10 am and pick it up the same afternoon. Flat tyres, brakes, gears and full tune-ups.
102
+ -See services and prices
103
+ +Drop your bike off before 11 am and pick it up the same afternoon. Flat tyres, brakes, gears and full tune-ups.
104
+ +Book a repair
105
+ ```
106
+
107
+ It also catches the problems that don't show up in a screenshot:
108
+
109
+ ```
110
+ - Meta description: "Repair prices." -> "Repair prices, labour included."
111
+
112
+ Console errors on B only:
113
+ - `Uncaught: bookingWidget is not defined`
114
+
115
+ Failed requests on B only:
116
+ - `404 /img/workshop.jpg`
117
+
118
+ ## Missing on B: `/contact/`
119
+ A serves this page and B does not. A's title: "Contact | Fernhill Bike Works".
120
+ B still links to it from: `/`, `/services/`, `/about/`, `/blog/`, `/blog/winter-chain-care/`. Those links are broken on B.
121
+ ```
122
+
123
+ (Trimmed. The full report has every changed page, region and image link.)
124
+
125
+ Next to it:
126
+
127
+ - `report.json`: the same results in machine-readable form.
128
+ - `pages/<page>/a.png`, `b.png`, `diff.png`: full-page screenshots and the diff image, written only
129
+ for pages that differ.
130
+
131
+ The images use relative links, so Claude Code can open them alongside the text. Point it at the
132
+ report ("read site-diff-report/report.md and tell me what will break for users"). The repo's
133
+ [SKILL.md](SKILL.md) teaches Claude how to run the tool and summarize the report.
134
+
135
+ ### Options
136
+
137
+ ```
138
+ amagi-site-diff A B --path /pricing --path /contact --no-crawl # only these pages
139
+ amagi-site-diff A B --max-pages 100 # crawl further (default 20)
140
+ amagi-site-diff A B --hide "#cookie-banner" --hide ".chat-widget" # hide noise on both sides
141
+ amagi-site-diff A B --viewport 390x844 # phone width (default 1280x800)
142
+ amagi-site-diff A B --viewport-only # first screenful instead of the full page
143
+ amagi-site-diff A B --threshold 40 # ignore smaller colour shifts (default 24 of 255)
144
+ amagi-site-diff A B --wait 1500 # extra settle time for slow widgets, in ms
145
+ amagi-site-diff A B --keep-query # treat ?page=2 as its own page
146
+ amagi-site-diff A B --out diff-report # report folder (default site-diff-report)
147
+ amagi-site-diff A B --exit-code # exit 1 when anything differs, for CI
148
+ ```
149
+
150
+ In Git Bash on Windows, write `--path pricing` without the leading slash. Git Bash rewrites
151
+ `/pricing` into a Windows file path before the tool sees it, and the tool stops with an error.
152
+
153
+ Exit codes: `0` done, `1` differences found (only with `--exit-code`), `2` bad arguments, the
154
+ browser failed to start, no page loaded on either side, or one site never responded at all
155
+ (a typo in the URL or a server that is down).
156
+
157
+ ## Limits
158
+
159
+ - **No logins.** Pages behind auth are compared as a logged-out visitor sees them.
160
+ - **Live content is noise.** Ads, rotating banners, A/B tests and live data change on every load.
161
+ Hide them with `--hide`. Animations and transitions are frozen before each screenshot.
162
+ - **Links only.** The crawler follows `<a href>` links. Pages reached through forms, JavaScript
163
+ navigation or only a sitemap need `--path`.
164
+ - **Chromium only.** It doesn't render in Firefox or Safari.
165
+ - **Compare what you're allowed to.** It loads every page it finds on both sites. Run it against
166
+ your own sites, or ones your client has asked you to check.
167
+
168
+ ## Why not BackstopJS, Playwright screenshots or Percy?
169
+
170
+ [BackstopJS](https://github.com/garris/BackstopJS) and Playwright's
171
+ [`toHaveScreenshot()`](https://playwright.dev/docs/test-snapshots) are built for ongoing visual
172
+ regression testing. You list scenarios or write a test for each page, store baseline images, and
173
+ compare future runs against them. Hosted services such as [Percy](https://percy.io) and
174
+ [Chromatic](https://www.chromatic.com) add review dashboards on top of the same model.
175
+
176
+ `amagi-site-diff` answers a one-off question: "what's different between these two sites right
177
+ now?" You give it two URLs. It finds the pages by crawling both, and writes a markdown file a person
178
+ or an AI assistant can read, with text diffs and element names next to the images.
179
+
180
+ ## If this saved you time
181
+
182
+ A star helps other freelancers and agencies find this. It costs nothing and means a lot.
183
+
184
+ ## More from AmagiLabs
185
+
186
+ - [amagi-legal-review-skills](https://github.com/amagilabs/amagi-legal-review-skills): Claude Code skills for
187
+ first-pass NDA/MSA/lease review.
188
+ - [amagi-watch](https://github.com/amagilabs/amagi-watch): turn a video URL into a transcript and
189
+ key-frame index Claude can read.
190
+ - [github.com/amagilabs](https://github.com/amagilabs): the rest of the AmagiLabs lab.
191
+
192
+ ## License
193
+
194
+ MIT, see [LICENSE](LICENSE). Copyright (c) 2026 AmagiLabs (Amagisolutionsgroup, Inc.).
@@ -0,0 +1,171 @@
1
+ # amagi-site-diff
2
+
3
+ See what changed between two versions of a website before your client does.
4
+ `amagi-site-diff` takes two base URLs (production and staging, or the old build and the new one),
5
+ crawls the same pages on both, screenshots each page in headless Chromium, and writes one
6
+ markdown report: pixel-diff images, changed regions named by page element, text diffs, new console
7
+ errors, broken requests, and pages that went missing.
8
+
9
+ It's for freelancers and agencies shipping redesigns, CMS migrations and theme updates who want a
10
+ page-by-page check before launch. The report is plain markdown, so you can read it yourself or hand
11
+ it to Claude.
12
+
13
+ ## Demo
14
+
15
+ ![demo](assets/demo.gif)
16
+
17
+ ## Install
18
+
19
+ ```
20
+ pipx install amagi-site-diff
21
+ ```
22
+
23
+ (Not yet published to PyPI. Until it is, install from a clone of this repo with
24
+ `pipx install .`.)
25
+
26
+ Requires Python 3.10+. The first run downloads Playwright's Chromium if it isn't
27
+ already installed. To use the Chrome or Edge you already have instead, pass `--channel chrome` or
28
+ `--channel msedge`.
29
+
30
+ ## Usage
31
+
32
+ ```
33
+ amagi-site-diff https://example.com https://staging.example.com
34
+ ```
35
+
36
+ A is "before", B is "after". Starting from `/`, it follows links on both sides, staying on each
37
+ site's own origin and path, and compares up to 20 pages. Either side can also be a local folder,
38
+ such as a static build, which is served on 127.0.0.1 for the run.
39
+
40
+ This repo ships two versions of a fictional bike shop site, so you can try it without touching a
41
+ real one:
42
+
43
+ ```
44
+ cd examples
45
+ amagi-site-diff site-v1 site-v2
46
+ ```
47
+
48
+ ```
49
+ A: site-v1 (local folder)
50
+ B: site-v2 (local folder)
51
+ Comparing up to 20 pages, following links...
52
+ / changed 1.61% pixels, 3 region(s); text +3 -3
53
+ /services/ changed 0.76% pixels, 3 region(s); text +1 -1; meta description; 2 new console error(s); 1 new failed request(s)
54
+ /about/ identical
55
+ /contact/ missing on B B: HTTP 404
56
+ /blog/ new on B A: HTTP 404
57
+ /blog/winter-chain-care/ new on B A: HTTP 404
58
+ 6 page(s) compared: 2 changed, 1 identical, 1 missing on B, 2 new on B
59
+ Report: site-diff-report/report.md
60
+ ```
61
+
62
+ ### The report
63
+
64
+ `site-diff-report/report.md` opens with a summary table, then one section for each page that
65
+ differs. Each section has a diff image: B faded to grey, changed pixels in red, and numbered
66
+ blue boxes around each changed region.
67
+
68
+ ![diff image for the home page](assets/diff-home.png)
69
+
70
+ The boxes are listed with the element under them on each side, followed by the text diff:
71
+
72
+ ```
73
+ 1. `160,214 225x42` (9,039 px): B `main > a.button` "Book a repair"; A `main > a.button` "See services and prices"
74
+ 2. `384,172 689x18` (5,885 px): B `main > p.lead:nth-of-type(1)` "Drop your bike off before 11 am and pick it up the same a..."; A `main > p.lead:nth-of-type(1)` "Drop your bike off before 10 am and pick it up the same a..."
75
+ ```
76
+
77
+ ```diff
78
+ -Drop your bike off before 10 am and pick it up the same afternoon. Flat tyres, brakes, gears and full tune-ups.
79
+ -See services and prices
80
+ +Drop your bike off before 11 am and pick it up the same afternoon. Flat tyres, brakes, gears and full tune-ups.
81
+ +Book a repair
82
+ ```
83
+
84
+ It also catches the problems that don't show up in a screenshot:
85
+
86
+ ```
87
+ - Meta description: "Repair prices." -> "Repair prices, labour included."
88
+
89
+ Console errors on B only:
90
+ - `Uncaught: bookingWidget is not defined`
91
+
92
+ Failed requests on B only:
93
+ - `404 /img/workshop.jpg`
94
+
95
+ ## Missing on B: `/contact/`
96
+ A serves this page and B does not. A's title: "Contact | Fernhill Bike Works".
97
+ B still links to it from: `/`, `/services/`, `/about/`, `/blog/`, `/blog/winter-chain-care/`. Those links are broken on B.
98
+ ```
99
+
100
+ (Trimmed. The full report has every changed page, region and image link.)
101
+
102
+ Next to it:
103
+
104
+ - `report.json`: the same results in machine-readable form.
105
+ - `pages/<page>/a.png`, `b.png`, `diff.png`: full-page screenshots and the diff image, written only
106
+ for pages that differ.
107
+
108
+ The images use relative links, so Claude Code can open them alongside the text. Point it at the
109
+ report ("read site-diff-report/report.md and tell me what will break for users"). The repo's
110
+ [SKILL.md](SKILL.md) teaches Claude how to run the tool and summarize the report.
111
+
112
+ ### Options
113
+
114
+ ```
115
+ amagi-site-diff A B --path /pricing --path /contact --no-crawl # only these pages
116
+ amagi-site-diff A B --max-pages 100 # crawl further (default 20)
117
+ amagi-site-diff A B --hide "#cookie-banner" --hide ".chat-widget" # hide noise on both sides
118
+ amagi-site-diff A B --viewport 390x844 # phone width (default 1280x800)
119
+ amagi-site-diff A B --viewport-only # first screenful instead of the full page
120
+ amagi-site-diff A B --threshold 40 # ignore smaller colour shifts (default 24 of 255)
121
+ amagi-site-diff A B --wait 1500 # extra settle time for slow widgets, in ms
122
+ amagi-site-diff A B --keep-query # treat ?page=2 as its own page
123
+ amagi-site-diff A B --out diff-report # report folder (default site-diff-report)
124
+ amagi-site-diff A B --exit-code # exit 1 when anything differs, for CI
125
+ ```
126
+
127
+ In Git Bash on Windows, write `--path pricing` without the leading slash. Git Bash rewrites
128
+ `/pricing` into a Windows file path before the tool sees it, and the tool stops with an error.
129
+
130
+ Exit codes: `0` done, `1` differences found (only with `--exit-code`), `2` bad arguments, the
131
+ browser failed to start, no page loaded on either side, or one site never responded at all
132
+ (a typo in the URL or a server that is down).
133
+
134
+ ## Limits
135
+
136
+ - **No logins.** Pages behind auth are compared as a logged-out visitor sees them.
137
+ - **Live content is noise.** Ads, rotating banners, A/B tests and live data change on every load.
138
+ Hide them with `--hide`. Animations and transitions are frozen before each screenshot.
139
+ - **Links only.** The crawler follows `<a href>` links. Pages reached through forms, JavaScript
140
+ navigation or only a sitemap need `--path`.
141
+ - **Chromium only.** It doesn't render in Firefox or Safari.
142
+ - **Compare what you're allowed to.** It loads every page it finds on both sites. Run it against
143
+ your own sites, or ones your client has asked you to check.
144
+
145
+ ## Why not BackstopJS, Playwright screenshots or Percy?
146
+
147
+ [BackstopJS](https://github.com/garris/BackstopJS) and Playwright's
148
+ [`toHaveScreenshot()`](https://playwright.dev/docs/test-snapshots) are built for ongoing visual
149
+ regression testing. You list scenarios or write a test for each page, store baseline images, and
150
+ compare future runs against them. Hosted services such as [Percy](https://percy.io) and
151
+ [Chromatic](https://www.chromatic.com) add review dashboards on top of the same model.
152
+
153
+ `amagi-site-diff` answers a one-off question: "what's different between these two sites right
154
+ now?" You give it two URLs. It finds the pages by crawling both, and writes a markdown file a person
155
+ or an AI assistant can read, with text diffs and element names next to the images.
156
+
157
+ ## If this saved you time
158
+
159
+ A star helps other freelancers and agencies find this. It costs nothing and means a lot.
160
+
161
+ ## More from AmagiLabs
162
+
163
+ - [amagi-legal-review-skills](https://github.com/amagilabs/amagi-legal-review-skills): Claude Code skills for
164
+ first-pass NDA/MSA/lease review.
165
+ - [amagi-watch](https://github.com/amagilabs/amagi-watch): turn a video URL into a transcript and
166
+ key-frame index Claude can read.
167
+ - [github.com/amagilabs](https://github.com/amagilabs): the rest of the AmagiLabs lab.
168
+
169
+ ## License
170
+
171
+ MIT, see [LICENSE](LICENSE). Copyright (c) 2026 AmagiLabs (Amagisolutionsgroup, Inc.).
@@ -0,0 +1,42 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "amagi-site-diff"
7
+ version = "0.1.0"
8
+ description = "Two base URLs in, one markdown report out: crawl both sites, screenshot every matching page with Playwright, and write pixel-diff images plus text diffs an AI agent can read."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "AmagiLabs (Amagisolutionsgroup, Inc.)", email = "dev@amagisolutionsgroup.com" }]
13
+ keywords = ["visual-regression", "screenshot", "diff", "playwright", "website", "deploy", "qa", "cli", "claude-code"]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: OS Independent",
18
+ "Environment :: Console",
19
+ "Topic :: Software Development :: Testing",
20
+ "Topic :: Internet :: WWW/HTTP :: Site Management",
21
+ ]
22
+ dependencies = [
23
+ "playwright>=1.40",
24
+ "Pillow>=10.0",
25
+ ]
26
+
27
+ [project.optional-dependencies]
28
+ dev = [
29
+ "pytest>=7.0",
30
+ ]
31
+
32
+ [project.scripts]
33
+ amagi-site-diff = "amagi_site_diff.cli:main"
34
+
35
+ [project.urls]
36
+ Homepage = "https://github.com/amagilabs/amagi-site-diff"
37
+
38
+ [tool.setuptools.packages.find]
39
+ where = ["src"]
40
+
41
+ [tool.pytest.ini_options]
42
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """amagi-site-diff: compare two versions of a website page by page."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,3 @@
1
+ from .cli import main
2
+
3
+ raise SystemExit(main())