hoshi-astro 1.7.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.
- hoshi_astro-1.7.0/.github/workflows/ci.yml +29 -0
- hoshi_astro-1.7.0/.github/workflows/release.yml +83 -0
- hoshi_astro-1.7.0/.gitignore +17 -0
- hoshi_astro-1.7.0/CHANGELOG.md +1 -0
- hoshi_astro-1.7.0/CLAUDE.md +213 -0
- hoshi_astro-1.7.0/LICENSE +21 -0
- hoshi_astro-1.7.0/PKG-INFO +204 -0
- hoshi_astro-1.7.0/README.md +161 -0
- hoshi_astro-1.7.0/docs/sdk.md +107 -0
- hoshi_astro-1.7.0/events/compute.json +21 -0
- hoshi_astro-1.7.0/hoshi/__init__.py +133 -0
- hoshi_astro-1.7.0/hoshi/adb.py +249 -0
- hoshi_astro-1.7.0/hoshi/aspects.py +164 -0
- hoshi_astro-1.7.0/hoshi/chart.py +381 -0
- hoshi_astro-1.7.0/hoshi/cli.py +671 -0
- hoshi_astro-1.7.0/hoshi/dignities.py +162 -0
- hoshi_astro-1.7.0/hoshi/ephemeris.py +369 -0
- hoshi_astro-1.7.0/hoshi/houses.py +258 -0
- hoshi_astro-1.7.0/hoshi/info.py +865 -0
- hoshi_astro-1.7.0/hoshi/output.py +1410 -0
- hoshi_astro-1.7.0/hoshi/points.py +156 -0
- hoshi_astro-1.7.0/hoshi/py.typed +0 -0
- hoshi_astro-1.7.0/hoshi/store.py +100 -0
- hoshi_astro-1.7.0/hoshi/utils.py +25 -0
- hoshi_astro-1.7.0/hoshi/zodiac.py +213 -0
- hoshi_astro-1.7.0/packages/hoshi-api/CHANGELOG.md +53 -0
- hoshi_astro-1.7.0/packages/hoshi-api/Dockerfile +55 -0
- hoshi_astro-1.7.0/packages/hoshi-api/README.md +81 -0
- hoshi_astro-1.7.0/packages/hoshi-api/hoshi_api/__init__.py +0 -0
- hoshi_astro-1.7.0/packages/hoshi-api/hoshi_api/app.py +84 -0
- hoshi_astro-1.7.0/packages/hoshi-api/hoshi_api/lambda_handler.py +7 -0
- hoshi_astro-1.7.0/packages/hoshi-api/hoshi_api/main.py +11 -0
- hoshi_astro-1.7.0/packages/hoshi-api/hoshi_api/mcp_server.py +295 -0
- hoshi_astro-1.7.0/packages/hoshi-api/hoshi_api/routes/__init__.py +0 -0
- hoshi_astro-1.7.0/packages/hoshi-api/hoshi_api/routes/charts.py +232 -0
- hoshi_astro-1.7.0/packages/hoshi-api/hoshi_api/routes/info.py +63 -0
- hoshi_astro-1.7.0/packages/hoshi-api/pyproject.toml +53 -0
- hoshi_astro-1.7.0/packages/hoshi-api/tests/__init__.py +0 -0
- hoshi_astro-1.7.0/packages/hoshi-api/tests/conftest.py +97 -0
- hoshi_astro-1.7.0/packages/hoshi-api/tests/test_charts.py +102 -0
- hoshi_astro-1.7.0/packages/hoshi-api/tests/test_info.py +66 -0
- hoshi_astro-1.7.0/packages/hoshi-api/tests/test_mcp.py +124 -0
- hoshi_astro-1.7.0/packages/hoshi-ui/CHANGELOG.md +83 -0
- hoshi_astro-1.7.0/packages/hoshi-ui/README.md +31 -0
- hoshi_astro-1.7.0/packages/hoshi-ui/hoshi_ui/__init__.py +0 -0
- hoshi_astro-1.7.0/packages/hoshi-ui/hoshi_ui/main.py +95 -0
- hoshi_astro-1.7.0/packages/hoshi-ui/hoshi_ui/screens/__init__.py +0 -0
- hoshi_astro-1.7.0/packages/hoshi-ui/hoshi_ui/screens/chart_detail.py +231 -0
- hoshi_astro-1.7.0/packages/hoshi-ui/hoshi_ui/screens/chart_list.py +54 -0
- hoshi_astro-1.7.0/packages/hoshi-ui/hoshi_ui/screens/chart_picker.py +63 -0
- hoshi_astro-1.7.0/packages/hoshi-ui/hoshi_ui/screens/compare.py +159 -0
- hoshi_astro-1.7.0/packages/hoshi-ui/hoshi_ui/screens/info_modal.py +121 -0
- hoshi_astro-1.7.0/packages/hoshi-ui/hoshi_ui/screens/info_picker.py +71 -0
- hoshi_astro-1.7.0/packages/hoshi-ui/hoshi_ui/screens/transits.py +132 -0
- hoshi_astro-1.7.0/packages/hoshi-ui/hoshi_ui/widgets/__init__.py +0 -0
- hoshi_astro-1.7.0/packages/hoshi-ui/hoshi_ui/widgets/body_display.py +267 -0
- hoshi_astro-1.7.0/packages/hoshi-ui/hoshi_ui/widgets/body_table.py +171 -0
- hoshi_astro-1.7.0/packages/hoshi-ui/pyproject.toml +51 -0
- hoshi_astro-1.7.0/packages/hoshi-ui/tests/__init__.py +0 -0
- hoshi_astro-1.7.0/packages/hoshi-ui/tests/conftest.py +39 -0
- hoshi_astro-1.7.0/packages/hoshi-ui/tests/test_app.py +37 -0
- hoshi_astro-1.7.0/packages/hoshi-ui/tests/test_chart_navigation.py +140 -0
- hoshi_astro-1.7.0/packages/hoshi-ui/tests/test_hover_tooltips.py +150 -0
- hoshi_astro-1.7.0/pyproject.toml +87 -0
- hoshi_astro-1.7.0/samconfig.toml +24 -0
- hoshi_astro-1.7.0/template.yaml +41 -0
- hoshi_astro-1.7.0/tests/__init__.py +0 -0
- hoshi_astro-1.7.0/tests/conftest.py +45 -0
- hoshi_astro-1.7.0/tests/test_adb.py +248 -0
- hoshi_astro-1.7.0/tests/test_api.py +25 -0
- hoshi_astro-1.7.0/tests/test_aspects.py +179 -0
- hoshi_astro-1.7.0/tests/test_chart.py +242 -0
- hoshi_astro-1.7.0/tests/test_cli.py +152 -0
- hoshi_astro-1.7.0/tests/test_dignities.py +86 -0
- hoshi_astro-1.7.0/tests/test_ephemeris.py +461 -0
- hoshi_astro-1.7.0/tests/test_houses.py +163 -0
- hoshi_astro-1.7.0/tests/test_info.py +229 -0
- hoshi_astro-1.7.0/tests/test_output.py +142 -0
- hoshi_astro-1.7.0/tests/test_points.py +99 -0
- hoshi_astro-1.7.0/tests/test_store.py +131 -0
- hoshi_astro-1.7.0/tests/test_zodiac.py +195 -0
- hoshi_astro-1.7.0/uv.lock +2649 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Install uv
|
|
20
|
+
uses: astral-sh/setup-uv@v6
|
|
21
|
+
|
|
22
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
23
|
+
run: uv python install ${{ matrix.python-version }}
|
|
24
|
+
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: uv sync --python ${{ matrix.python-version }}
|
|
27
|
+
|
|
28
|
+
- name: Run tests
|
|
29
|
+
run: uv run --python ${{ matrix.python-version }} pytest
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_run:
|
|
5
|
+
workflows: ["CI"]
|
|
6
|
+
branches: [main]
|
|
7
|
+
types: [completed]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
release:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
concurrency: release
|
|
13
|
+
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
|
14
|
+
permissions:
|
|
15
|
+
id-token: write
|
|
16
|
+
contents: write
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
with:
|
|
21
|
+
fetch-depth: 0
|
|
22
|
+
|
|
23
|
+
- name: Install uv
|
|
24
|
+
uses: astral-sh/setup-uv@v6
|
|
25
|
+
|
|
26
|
+
- name: Set up Python
|
|
27
|
+
run: uv python install 3.13
|
|
28
|
+
|
|
29
|
+
- name: Install dependencies
|
|
30
|
+
run: uv sync
|
|
31
|
+
|
|
32
|
+
- name: Release hoshi
|
|
33
|
+
id: release-hoshi
|
|
34
|
+
uses: python-semantic-release/python-semantic-release@v10
|
|
35
|
+
with:
|
|
36
|
+
directory: .
|
|
37
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
38
|
+
|
|
39
|
+
- name: Release hoshi-api
|
|
40
|
+
id: release-hoshi-api
|
|
41
|
+
uses: python-semantic-release/python-semantic-release@v10
|
|
42
|
+
with:
|
|
43
|
+
directory: packages/hoshi-api
|
|
44
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
45
|
+
|
|
46
|
+
- name: Release hoshi-ui
|
|
47
|
+
id: release-hoshi-ui
|
|
48
|
+
uses: python-semantic-release/python-semantic-release@v10
|
|
49
|
+
with:
|
|
50
|
+
directory: packages/hoshi-ui
|
|
51
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
52
|
+
|
|
53
|
+
- name: Publish hoshi to GitHub Release
|
|
54
|
+
uses: python-semantic-release/publish-action@v10
|
|
55
|
+
if: steps.release-hoshi.outputs.released == 'true'
|
|
56
|
+
with:
|
|
57
|
+
directory: .
|
|
58
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
59
|
+
tag: ${{ steps.release-hoshi.outputs.tag }}
|
|
60
|
+
|
|
61
|
+
- name: Build hoshi for PyPI
|
|
62
|
+
run: uv build
|
|
63
|
+
if: steps.release-hoshi.outputs.released == 'true'
|
|
64
|
+
|
|
65
|
+
- name: Publish hoshi to PyPI
|
|
66
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
67
|
+
if: steps.release-hoshi.outputs.released == 'true'
|
|
68
|
+
|
|
69
|
+
- name: Publish hoshi-api to GitHub Release
|
|
70
|
+
uses: python-semantic-release/publish-action@v10
|
|
71
|
+
if: steps.release-hoshi-api.outputs.released == 'true'
|
|
72
|
+
with:
|
|
73
|
+
directory: packages/hoshi-api
|
|
74
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
75
|
+
tag: ${{ steps.release-hoshi-api.outputs.tag }}
|
|
76
|
+
|
|
77
|
+
- name: Publish hoshi-ui to GitHub Release
|
|
78
|
+
uses: python-semantic-release/publish-action@v10
|
|
79
|
+
if: steps.release-hoshi-ui.outputs.released == 'true'
|
|
80
|
+
with:
|
|
81
|
+
directory: packages/hoshi-ui
|
|
82
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
83
|
+
tag: ${{ steps.release-hoshi-ui.outputs.tag }}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Python-generated files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[oc]
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
wheels/
|
|
7
|
+
*.egg-info
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv
|
|
11
|
+
|
|
12
|
+
.history
|
|
13
|
+
|
|
14
|
+
# Runtime caches (Horizons responses, JPL ephemeris)
|
|
15
|
+
.chiron_cache.json
|
|
16
|
+
.lunar_cache.json
|
|
17
|
+
*.bsp
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# CHANGELOG
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Goal
|
|
6
|
+
|
|
7
|
+
Hoshi is a Python CLI for astrological charting, with a focus on real-sky astrology. Real-sky charts use IAU constellation boundaries (13 signs of unequal width, including Ophiuchus) rather than the traditional 12-sign tropical wheel — this makes charting significantly harder to do by hand, which is where Hoshi helps. Planetary positions come from [Skyfield](https://rhodesmill.org/skyfield/) (JPL ephemerides) for accuracy.
|
|
8
|
+
|
|
9
|
+
## Repo contents
|
|
10
|
+
|
|
11
|
+
- `pyproject.toml` / `.python-version` — packaging and pinned interpreter. Requires Python 3.11+ (CI tests 3.11–3.13). The root `pyproject.toml` also configures a **uv workspace** (`[tool.uv.workspace]`) with members under `packages/`.
|
|
12
|
+
- `hoshi/` — core Python package. Exposed as the `hoshi` console script via `[project.scripts]`; build backend is `hatchling`. After dependency changes, run `uv sync` to reinstall.
|
|
13
|
+
- `packages/hoshi-api/` — FastAPI REST API package (workspace member). Depends on `hoshi` as a workspace dependency. Run with `uv run --package hoshi-api hoshi-api` (starts uvicorn on port 8000). Tests in `packages/hoshi-api/tests/`.
|
|
14
|
+
- `charts/` — user-saved charts (one JSON file per chart). Names are normalized to lowercase on save. Not a cache — treat as user data. Persists only inputs; charts are recomputed on `hoshi chart show`.
|
|
15
|
+
- `~/.cache/hoshi/chiron.json` — per-minute cache of Horizons OBSERVER responses for Chiron. Safe to delete.
|
|
16
|
+
- `~/.cache/hoshi/lunar.json` — per-minute cache of Horizons ELEMENTS responses for the Moon (true nodes and true Lilith). Safe to delete.
|
|
17
|
+
|
|
18
|
+
## Package modules (`hoshi/`)
|
|
19
|
+
|
|
20
|
+
| Module | Purpose |
|
|
21
|
+
|--------|---------|
|
|
22
|
+
| `ephemeris.py` | Skyfield positions (`positions(when, include_chiron=...)`), Horizons HTTP fetch (`HorizonsError` on failure), shared `timescale()`/`cache_dir()`, JSON cache helpers, `ecliptic_precession()` |
|
|
23
|
+
| `zodiac.py` | IAU real-sky boundaries, tropical/sidereal placements (`Placement.for_mode(lon, mode, ...)`), `ZodiacMode` enum, `TROP_SIGNS`/`SIGN_ATTRS` sign tables |
|
|
24
|
+
| `houses.py` | Placidus, Porphyry, Equal, Arc-13 house cusps; angle computation (shared `_ramc_obliquity`/`_mc_from_ramc`) |
|
|
25
|
+
| `points.py` | True lunar nodes, Black Moon Lilith, Hermetic lots |
|
|
26
|
+
| `chart.py` | `Chart.build()` / `Chart.from_input()` — assembles all bodies; `Chart.bodies()`/`Chart.body(id)` uniform `BodyRef` iteration; `uncertain_signs()`; `Placed.for_longitude(...)` and `Placed.placement(mode)`; `location_known`/`time_known` flags; `house` is `None` when location unknown |
|
|
27
|
+
| `aspects.py` | Aspect definitions and orbs; `compute_aspects()`, `compute_inter_aspects()` |
|
|
28
|
+
| `dignities.py` | Planetary dignities table, element/modality tally |
|
|
29
|
+
| `output.py` | Pydantic output models for every command (`ChartOutput`, `TransitsOutput`, etc.) with `.build()` classmethods that assemble models from SDK types. Shared by CLI and API. |
|
|
30
|
+
| `store.py` | Save/load/list/delete named charts in `./charts/` |
|
|
31
|
+
| `utils.py` | Shared utilities (`fuzzy_match`) used by both CLI and API |
|
|
32
|
+
| `adb.py` | Astro-Databank import via MediaWiki API; `adb_to_chart_input()` fetches + parses `ASTRODATABANK_dma` template into `ChartInput`; coordinate/time/timezone converters; `ADBError` on failure |
|
|
33
|
+
| `cli.py` | Typer entry point — all `hoshi chart` subcommands |
|
|
34
|
+
|
|
35
|
+
## Public SDK surface
|
|
36
|
+
|
|
37
|
+
Hoshi is usable as a library, not just a CLI. The top-level `hoshi` package
|
|
38
|
+
(`hoshi/__init__.py`) re-exports the stable domain types and functions with an
|
|
39
|
+
explicit `__all__`; **import from `hoshi`, not the submodules**, when consuming
|
|
40
|
+
Hoshi from another project. A `py.typed` marker ships so downstream
|
|
41
|
+
type-checkers see the annotations. See `docs/sdk.md` for a worked example.
|
|
42
|
+
|
|
43
|
+
Guidance when changing the package:
|
|
44
|
+
- New public constructs go in `__all__` (and ideally `docs/sdk.md`).
|
|
45
|
+
- Keep computation in the SDK layer (`chart.py`, `zodiac.py`, …); `cli.py`
|
|
46
|
+
should only handle argument parsing and display. `Chart.from_input()`,
|
|
47
|
+
`Chart.bodies()`, and `uncertain_signs()` exist so the CLI doesn't own domain
|
|
48
|
+
logic — prefer extending these over adding logic to `cli.py`.
|
|
49
|
+
- `Chart.from_input(ChartInput)` substitutes placeholder coordinates for an
|
|
50
|
+
unknown location and records `location_known` / `time_known` on the chart so
|
|
51
|
+
consumers can drive their own suppression.
|
|
52
|
+
- Zodiac mode is the `ZodiacMode` StrEnum (in `zodiac.py`); functions accept it
|
|
53
|
+
or its plain-string value interchangeably.
|
|
54
|
+
|
|
55
|
+
## CLI commands
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
hoshi chart add NAME DATE [TIME] [--lat] [--lon] [--tz] [--mode] [--houses] [--details] [--aspects] [--group-by] [--cusps] [--force]
|
|
59
|
+
hoshi chart show NAME|DATE [TIME] [--lat --lon] ... [--format table|json] [--compare-houses]
|
|
60
|
+
hoshi chart cusps NAME|DATE [TIME] [--lat --lon] ... [--mode] [--houses]
|
|
61
|
+
hoshi chart transits NAME [DATE [TIME]] [--tz] [--mode] [--houses] [--details] [--aspects] [--natal] [--group-by]
|
|
62
|
+
hoshi chart compare NAME1 NAME2 [--mode] [--houses] [--aspects] [--details] [--group-by]
|
|
63
|
+
hoshi chart import SOURCE [NAME] [--force] [--mode] [--houses] [--details] [--aspects] [--group-by] [--cusps] [--format]
|
|
64
|
+
hoshi chart list
|
|
65
|
+
hoshi chart delete NAME [--yes]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Optional birth data and uncertainty
|
|
69
|
+
|
|
70
|
+
`ChartInput` stores `time`, `lat`, and `lon` as `str | None` and `float | None` respectively — all three are optional. Store whatever is known; omit the rest.
|
|
71
|
+
|
|
72
|
+
When displaying a chart with missing data:
|
|
73
|
+
- **Time unknown** — noon UTC is used for computation; all planet sign/degree/lon cells render in **yellow** as a warning; `⚠` note is printed above the table. Moon sign is particularly uncertain (moves ~13°/day).
|
|
74
|
+
- **Location unknown** — angles (ASC/MC/etc.), house numbers, and Hermetic lots are **suppressed entirely**; `⚠` note is printed.
|
|
75
|
+
- **Both unknown** — both sets of rules apply.
|
|
76
|
+
|
|
77
|
+
`ChartInput` properties `time_known` and `location_known` drive all suppression logic in `cli.py`. `--compare-houses` and `chart cusps` require both to be known and error if not.
|
|
78
|
+
|
|
79
|
+
## Three zodiac modes
|
|
80
|
+
|
|
81
|
+
- `'realsky'` — IAU real-sky boundaries (13 signs incl. Ophiuchus, unequal widths). Default. See `IAU[]` in `hoshi/zodiac.py`.
|
|
82
|
+
- `'tropical'` — standard 12 equal 30° signs from the vernal equinox.
|
|
83
|
+
- `'vedic'` — sidereal, Lahiri ayanamsa offset applied.
|
|
84
|
+
|
|
85
|
+
## Reference frame: of-date, not J2000
|
|
86
|
+
|
|
87
|
+
All ecliptic longitudes are in the **equinox of date** frame (measured from
|
|
88
|
+
the vernal equinox at the chart moment), matching standard astrological
|
|
89
|
+
convention. This requires explicit `epoch=t` in Skyfield calls —
|
|
90
|
+
**`ecliptic_latlon()` default returns J2000**, contrary to what the Skyfield
|
|
91
|
+
docs suggest. The two differ by precession (~50.3″/year).
|
|
92
|
+
|
|
93
|
+
The IAU constellation table is J2000-fixed. `Placement.realsky()` accepts a
|
|
94
|
+
`precession` argument (degrees since J2000 from `ecliptic_precession(when)`)
|
|
95
|
+
to shift the boundaries into the of-date frame before the sign lookup.
|
|
96
|
+
`Placed.for_longitude()` threads this through automatically when built via
|
|
97
|
+
`Chart.build()`.
|
|
98
|
+
|
|
99
|
+
## Horizons-backed bodies
|
|
100
|
+
|
|
101
|
+
Skyfield/jplephem can't read JPL Horizons' small-body SPKs (SPK data type 21
|
|
102
|
+
is unsupported). Two bodies use Horizons HTTP APIs directly:
|
|
103
|
+
|
|
104
|
+
- **Chiron** (`_chiron_position` in `hoshi/ephemeris.py`) — Horizons
|
|
105
|
+
**OBSERVER** ephemeris at chart time + 1 minute for ecliptic lon/lat and
|
|
106
|
+
retrograde state.
|
|
107
|
+
- **True lunar nodes & Black Moon Lilith** (`lunar_elements` in
|
|
108
|
+
`hoshi/points.py`) — Horizons **ELEMENTS** API at chart time, parses
|
|
109
|
+
OM (ascending node) and W (argument of perigee) for the Moon. True Lilith
|
|
110
|
+
= OM + W + 180°.
|
|
111
|
+
|
|
112
|
+
Shared HTTP/SSL/cache helpers (`horizons_fetch`, `json_cache_get/put`) live
|
|
113
|
+
in `hoshi/ephemeris.py`. The SSL context uses `certifi` because macOS
|
|
114
|
+
stdlib `urllib` ships with an incomplete root bundle.
|
|
115
|
+
|
|
116
|
+
## Aspects
|
|
117
|
+
|
|
118
|
+
Five major aspects (4° orb), four minor (2° orb), three micro (1° orb). See
|
|
119
|
+
`hoshi/aspects.py`. Single-chart aspects via `compute_aspects(chart, details)`;
|
|
120
|
+
synastry inter-aspects via `compute_inter_aspects(chart_a, chart_b, details)`.
|
|
121
|
+
Without `--details`, only planets + Asc are included. Axis pairs (Asc/Dsc,
|
|
122
|
+
MC/IC, etc.) are filtered from single-chart aspects.
|
|
123
|
+
|
|
124
|
+
`--group-by` controls grouping for both bodies and aspects: `category`
|
|
125
|
+
(default — bodies by kind, aspects by Major/Minor/Micro), `sign`, `house`,
|
|
126
|
+
or `planet` (aspects only).
|
|
127
|
+
|
|
128
|
+
## Dignities
|
|
129
|
+
|
|
130
|
+
`hoshi/dignities.py` holds the domicile/exaltation/detriment/fall table for
|
|
131
|
+
the 13-sign real-sky scheme (Chiron domicile = Ophiuchus). Assignments follow
|
|
132
|
+
standard conventions adapted for 13 signs.
|
|
133
|
+
`element_modality_tally()` returns separate primary (planets) and total (all
|
|
134
|
+
bodies) counts.
|
|
135
|
+
|
|
136
|
+
## REST API (`packages/hoshi-api/`)
|
|
137
|
+
|
|
138
|
+
A FastAPI application exposing the same chart functionality as the CLI via
|
|
139
|
+
JSON endpoints. It imports the `hoshi` SDK and uses the `.build()` classmethods
|
|
140
|
+
on the output models — the same models that back `--format json` in the CLI.
|
|
141
|
+
|
|
142
|
+
**Running:** `uv run --package hoshi-api hoshi-api` (starts on `0.0.0.0:8000`
|
|
143
|
+
with auto-reload). Interactive docs at `/docs`.
|
|
144
|
+
|
|
145
|
+
**Route overview:**
|
|
146
|
+
|
|
147
|
+
| Method | Path | Purpose |
|
|
148
|
+
|--------|------|---------|
|
|
149
|
+
| `POST` | `/charts` | Save a new chart |
|
|
150
|
+
| `GET` | `/charts` | List saved charts |
|
|
151
|
+
| `POST` | `/charts/compute` | Compute a one-off chart (no save) |
|
|
152
|
+
| `POST` | `/charts/import` | Import from Astro-Databank |
|
|
153
|
+
| `GET` | `/charts/{name}` | Show a saved chart |
|
|
154
|
+
| `DELETE` | `/charts/{name}` | Delete a saved chart |
|
|
155
|
+
| `GET` | `/charts/{name}/cusps` | House cusps |
|
|
156
|
+
| `GET` | `/charts/{name}/transits` | Transits against natal |
|
|
157
|
+
| `GET` | `/charts/{name}/compare/{other}` | Synastry |
|
|
158
|
+
| `GET` | `/info/{category}` | List reference info |
|
|
159
|
+
| `GET` | `/info/{category}/{name}` | Single item detail |
|
|
160
|
+
|
|
161
|
+
All route handlers are synchronous (`def`, not `async def`) so uvicorn
|
|
162
|
+
dispatches them to a thread pool — blocking Horizons/Skyfield calls work
|
|
163
|
+
correctly without async wrappers.
|
|
164
|
+
|
|
165
|
+
**Testing:** `uv run --package hoshi-api pytest packages/hoshi-api/tests/`
|
|
166
|
+
|
|
167
|
+
## Conventions
|
|
168
|
+
|
|
169
|
+
- Use `uv` for dependency and run management (`uv add`, `uv run`).
|
|
170
|
+
|
|
171
|
+
## Commit message format
|
|
172
|
+
|
|
173
|
+
This repo uses [Conventional Commits](https://www.conventionalcommits.org/) for
|
|
174
|
+
`python-semantic-release` to automate versioning. Every commit message must
|
|
175
|
+
follow this format:
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
<type>[optional scope]: <description>
|
|
179
|
+
|
|
180
|
+
[optional body]
|
|
181
|
+
|
|
182
|
+
[optional footer(s)]
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Types** (only these trigger releases):
|
|
186
|
+
- `fix:` — bug fix → **patch** bump (0.0.x)
|
|
187
|
+
- `feat:` — new feature → **minor** bump (0.x.0)
|
|
188
|
+
- Append `!` after the type/scope (e.g. `feat!:`) or add a `BREAKING CHANGE:` footer → **major** bump (x.0.0)
|
|
189
|
+
|
|
190
|
+
**Types** (no release, but still use them):
|
|
191
|
+
- `chore:` — maintenance, dependency updates
|
|
192
|
+
- `docs:` — documentation only
|
|
193
|
+
- `refactor:` — code restructuring with no behavior change
|
|
194
|
+
- `test:` — adding or updating tests
|
|
195
|
+
- `ci:` — CI/CD changes
|
|
196
|
+
- `style:` — formatting, whitespace
|
|
197
|
+
|
|
198
|
+
**Scope** is optional and names the area of change, e.g. `feat(cli):`, `fix(ephemeris):`.
|
|
199
|
+
|
|
200
|
+
Keep the first line under 72 characters. Use the body for additional context
|
|
201
|
+
when the "why" isn't obvious from the summary line alone.
|
|
202
|
+
|
|
203
|
+
**Always ask for confirmation before creating a commit.** Show the proposed
|
|
204
|
+
commit message and list of staged files, then wait for approval.
|
|
205
|
+
|
|
206
|
+
## Code change checklist
|
|
207
|
+
|
|
208
|
+
Before considering any code change complete, verify all of the following:
|
|
209
|
+
|
|
210
|
+
1. **Lint and format** — run `uv run ruff check --fix .` and `uv run ruff format .` to fix lint issues and enforce consistent formatting.
|
|
211
|
+
2. **Tests are written** — new or changed functionality must have corresponding tests.
|
|
212
|
+
3. **All tests pass** — run the full test suite (`uv run pytest`) and confirm zero failures.
|
|
213
|
+
4. **Documentation is updated** — update this file (CLAUDE.md), CLI help text, and any other relevant docs to reflect the change.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Trey Gonsoulin
|
|
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,204 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hoshi-astro
|
|
3
|
+
Version: 1.7.0
|
|
4
|
+
Summary: Real-sky astrology birth chart CLI.
|
|
5
|
+
Project-URL: Homepage, https://github.com/trey-gonsoulin/hoshi
|
|
6
|
+
Project-URL: Repository, https://github.com/trey-gonsoulin/hoshi
|
|
7
|
+
Project-URL: Changelog, https://github.com/trey-gonsoulin/hoshi/blob/main/CHANGELOG.md
|
|
8
|
+
License: MIT License
|
|
9
|
+
|
|
10
|
+
Copyright (c) 2026 Trey Gonsoulin
|
|
11
|
+
|
|
12
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
in the Software without restriction, including without limitation the rights
|
|
15
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
furnished to do so, subject to the following conditions:
|
|
18
|
+
|
|
19
|
+
The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
copies or substantial portions of the Software.
|
|
21
|
+
|
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
SOFTWARE.
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
31
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
32
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
33
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
34
|
+
Requires-Python: >=3.11
|
|
35
|
+
Requires-Dist: certifi>=2026.6.17
|
|
36
|
+
Requires-Dist: geopy>=2.0
|
|
37
|
+
Requires-Dist: pydantic>=2.8
|
|
38
|
+
Requires-Dist: pyyaml>=6.0.2
|
|
39
|
+
Requires-Dist: rich>=10.0.0
|
|
40
|
+
Requires-Dist: skyfield>=1.49
|
|
41
|
+
Requires-Dist: typer>=0.12.4
|
|
42
|
+
Description-Content-Type: text/markdown
|
|
43
|
+
|
|
44
|
+
# Hoshi
|
|
45
|
+
|
|
46
|
+
A CLI for astrological charting, with a focus on real-sky astrology. Inspired by [Nuastro](https://nuastro.com).
|
|
47
|
+
|
|
48
|
+
Traditional astrology divides the sky into 12 equal 30° signs. Real-sky astrology uses the actual IAU constellation boundaries — 13 unequal signs along the ecliptic, including Ophiuchus — which makes charting by hand significantly more difficult. Hoshi handles the calculation, using JPL-grade ephemerides ([Skyfield](https://rhodesmill.org/skyfield/) DE421 for planets, JPL Horizons for Chiron and the lunar nodes).
|
|
49
|
+
|
|
50
|
+
## Setup
|
|
51
|
+
|
|
52
|
+
Requires Python 3.11+ and [uv](https://docs.astral.sh/uv/).
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
uv sync
|
|
56
|
+
uv tool install --editable . # puts `hoshi` on your PATH (~/.local/bin/hoshi)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`--editable` means source changes take effect immediately. If you change `pyproject.toml` dependencies, re-run with `--reinstall`. To remove: `uv tool uninstall hoshi`.
|
|
60
|
+
|
|
61
|
+
On first run, Skyfield downloads `de421.bsp` (~17 MB) into the current directory. Chiron and lunar (node/Lilith) lookups hit JPL Horizons and are cached in `~/.cache/hoshi/`.
|
|
62
|
+
|
|
63
|
+
## Commands
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
hoshi chart add NAME DATE [TIME] [--lat LAT] [--lon LON] [--tz TZ] [--mode MODE] [--houses SYS]
|
|
67
|
+
[--details] [--aspects] [--group-by category|sign|house]
|
|
68
|
+
[--cusps] [--force]
|
|
69
|
+
hoshi chart show NAME|DATE [TIME] [--lat LAT --lon LON] ...
|
|
70
|
+
[--format table|json] [--compare-houses]
|
|
71
|
+
hoshi chart cusps NAME|DATE [TIME] [--lat LAT --lon LON] ... [--mode MODE] [--houses SYS]
|
|
72
|
+
hoshi chart transits NAME [DATE [TIME]] [--tz TZ] [--mode MODE] [--houses SYS]
|
|
73
|
+
[--details] [--aspects] [--natal]
|
|
74
|
+
hoshi chart compare NAME1 NAME2 [--mode MODE] [--houses SYS] [--aspects] [--details]
|
|
75
|
+
hoshi chart list
|
|
76
|
+
hoshi chart delete NAME [--yes]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`NAME|DATE` — pass a saved chart name, or a `YYYY-MM-DD` date with `--lat`/`--lon` for a one-off chart.
|
|
80
|
+
|
|
81
|
+
`--lat` and `--lon` are optional for `chart add` — omit either or both if the birth location is unknown. If time is also omitted, only the date is stored. Placements computed from unknown inputs are shown in yellow with a warning; angles and houses are omitted entirely when time or location is unknown.
|
|
82
|
+
|
|
83
|
+
## Options
|
|
84
|
+
|
|
85
|
+
### `--mode`
|
|
86
|
+
|
|
87
|
+
| Mode | Description |
|
|
88
|
+
|------|-------------|
|
|
89
|
+
| `realsky` (default) | IAU real-sky boundaries — 13 unequal signs including Ophiuchus |
|
|
90
|
+
| `tropical` | Standard 12 equal 30° signs from the vernal equinox |
|
|
91
|
+
| `vedic` | Sidereal: tropical longitudes minus the Lahiri ayanamsa |
|
|
92
|
+
|
|
93
|
+
### `--houses`
|
|
94
|
+
|
|
95
|
+
| System | Description |
|
|
96
|
+
|--------|-------------|
|
|
97
|
+
| `porphyry` (default) | Trisects the quadrants |
|
|
98
|
+
| `equal` | Equal 30° houses from the Ascendant |
|
|
99
|
+
| `placidus` | Time-based Placidus division |
|
|
100
|
+
| `arc13` | 13-arc wheel with widths matching the IAU constellation boundaries |
|
|
101
|
+
|
|
102
|
+
### `--details`
|
|
103
|
+
|
|
104
|
+
Expands the display to include all six angles (Asc, MC, IC, Dsc, Vertex, Antivertex), true lunar nodes, Black Moon Lilith, seven Hermetic lots, planetary dignities, and element/modality tallies.
|
|
105
|
+
|
|
106
|
+
### `--aspects`
|
|
107
|
+
|
|
108
|
+
Prints aspect tables grouped by type (Major / Minor / Micro). Without `--details`, aspects are computed for planets + Ascendant only; with `--details`, all chart bodies are included.
|
|
109
|
+
|
|
110
|
+
### `--group-by sign|house`
|
|
111
|
+
|
|
112
|
+
Alternative layout grouping bodies by zodiac sign or house instead of category. `--group-by house` labels each house with its cusp sign and includes empty houses.
|
|
113
|
+
|
|
114
|
+
### `--format json`
|
|
115
|
+
|
|
116
|
+
Outputs a mode-specific JSON summary instead of the Rich table. Includes body placements, house assignments, and cusp longitudes. Useful for scripting or piping into other tools. When time or location is unknown, the JSON includes a `"warnings"` array and an `"approximate": true` flag on affected body entries.
|
|
117
|
+
|
|
118
|
+
## Bodies
|
|
119
|
+
|
|
120
|
+
### Planets
|
|
121
|
+
Sun, Moon, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto, Chiron
|
|
122
|
+
|
|
123
|
+
### Angles
|
|
124
|
+
Ascendant, Midheaven (MC), Imum Coeli (IC), Descendant, Vertex, Antivertex
|
|
125
|
+
|
|
126
|
+
### Calculated points
|
|
127
|
+
- **N.Node / S.Node** — True (osculating) lunar nodes from JPL Horizons
|
|
128
|
+
- **Lilith** — True Black Moon Lilith: ascending node + argument of perigee + 180°
|
|
129
|
+
|
|
130
|
+
### Hermetic lots
|
|
131
|
+
Fortune, Spirit, Eros, Necessity, Courage, Victory, Nemesis
|
|
132
|
+
|
|
133
|
+
## Aspects
|
|
134
|
+
|
|
135
|
+
| Class | Aspects | Orb |
|
|
136
|
+
|-------|---------|-----|
|
|
137
|
+
| Major | Conjunction (0°), Opposition (180°), Trine (120°), Square (90°), Sextile (60°) | 4° |
|
|
138
|
+
| Minor | Inconjunct (150°), Semi-sextile (30°), Semi-square (45°), Sesquiquadrate (135°) | 2° |
|
|
139
|
+
| Micro | Quintile (72°), Bi-quintile (144°), Septile (360°/7) | 1° |
|
|
140
|
+
|
|
141
|
+
## Dignities
|
|
142
|
+
|
|
143
|
+
The Planets table in `--details` shows a dignity indicator per planet:
|
|
144
|
+
|
|
145
|
+
| Symbol | Dignity |
|
|
146
|
+
|--------|---------|
|
|
147
|
+
| ⊕ | Domicile |
|
|
148
|
+
| △ | Exaltation |
|
|
149
|
+
| ▽ | Detriment |
|
|
150
|
+
| ✕ | Fall |
|
|
151
|
+
|
|
152
|
+
Assignments follow standard conventions adapted for the 13-sign real-sky scheme (Chiron domicile = Ophiuchus).
|
|
153
|
+
|
|
154
|
+
## Transits
|
|
155
|
+
|
|
156
|
+
```sh
|
|
157
|
+
hoshi chart transits person # current moment vs natal
|
|
158
|
+
hoshi chart transits person 2026-01-01 # specific date (noon UTC)
|
|
159
|
+
hoshi chart transits person 2026-01-01 14:30 --tz America/Chicago
|
|
160
|
+
hoshi chart transits person --natal # side-by-side natal vs transit table
|
|
161
|
+
hoshi chart transits person --aspects # inter-aspects (natal × transit)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Computes current (or specified date) planetary positions and places them against the saved natal chart. The house column (`H`) always shows which **natal house** each transiting planet falls in, using the natal chart's cusps.
|
|
165
|
+
|
|
166
|
+
`--natal` adds a side-by-side table showing each body's natal sign/degree alongside its current transit sign/degree. `--details` and `--aspects` work the same as in other commands.
|
|
167
|
+
|
|
168
|
+
## Synastry
|
|
169
|
+
|
|
170
|
+
```sh
|
|
171
|
+
hoshi chart compare person1 person2 --aspects
|
|
172
|
+
hoshi chart compare person1 person2 --aspects --details
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Computes inter-aspects between two saved charts. Body selection respects `--details` the same way single-chart aspects do.
|
|
176
|
+
|
|
177
|
+
## Accuracy notes
|
|
178
|
+
|
|
179
|
+
### Reference frame
|
|
180
|
+
|
|
181
|
+
All ecliptic longitudes are in the **equinox of date** frame, matching standard astrological convention. Skyfield's `ecliptic_latlon()` defaults to J2000, so `epoch=t` is passed explicitly. The IAU constellation boundaries are J2000-fixed; Hoshi applies the precession offset at lookup time so sign assignments stay consistent with the of-date planet positions.
|
|
182
|
+
|
|
183
|
+
### Per-body accuracy
|
|
184
|
+
|
|
185
|
+
- **Planets**: Skyfield + JPL DE421 — sub-arcsecond
|
|
186
|
+
- **Chiron**: Horizons OBSERVER ephemeris, cached per minute
|
|
187
|
+
- **Nodes / Lilith**: Horizons ELEMENTS API (Moon osculating elements), cached per minute
|
|
188
|
+
- **Angles / cusps**: standard formulas with a linear obliquity model — accurate to under 1′ for 20th–21st century dates
|
|
189
|
+
- **Lahiri ayanamsa**: linear approximation anchored at J2000 — adequate for chart display
|
|
190
|
+
|
|
191
|
+
## Project layout
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
hoshi/
|
|
195
|
+
ephemeris.py Skyfield positions, Horizons fetch, JSON cache helpers
|
|
196
|
+
zodiac.py IAU real-sky boundaries, tropical and sidereal placements
|
|
197
|
+
houses.py Placidus, Porphyry, Equal, Arc-13 cusps; angles
|
|
198
|
+
points.py True lunar nodes, Black Moon Lilith, Hermetic lots
|
|
199
|
+
chart.py Chart.build() — assembles all bodies
|
|
200
|
+
aspects.py Aspect detection (single-chart and inter-chart)
|
|
201
|
+
dignities.py Dignities table, element/modality tallies
|
|
202
|
+
store.py JSON persistence for named charts (./charts/)
|
|
203
|
+
cli.py Typer entry point
|
|
204
|
+
```
|