slab-cli 0.19.0__tar.gz → 0.20.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.
- {slab_cli-0.19.0 → slab_cli-0.20.0}/PKG-INFO +2 -2
- {slab_cli-0.19.0 → slab_cli-0.20.0}/pyproject.toml +3 -2
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/display.py +38 -4
- {slab_cli-0.19.0 → slab_cli-0.20.0}/README.md +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/__init__.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/banner.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/browse.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/client.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/commands/__init__.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/commands/breaks.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/commands/catalog.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/commands/collection.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/commands/custom_sets.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/commands/export.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/commands/lots.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/commands/pricing.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/commands/registry.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/commands/setup.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/commands/update.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/config.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/context.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/flags.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/help.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/main.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/paging.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/picker.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/platforms/__init__.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/platforms/base.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/platforms/posix.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/platforms/windows.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/prompts.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/sources.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/theme.py +0 -0
- {slab_cli-0.19.0 → slab_cli-0.20.0}/src/slab_cli/updates.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: slab-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.20.0
|
|
4
4
|
Summary: CLI for the slab trading-card API — catalog, collect, and track your cards from the terminal.
|
|
5
5
|
Author: dev_jeb
|
|
6
|
-
Requires-Dist: slab-schemas>=0.
|
|
6
|
+
Requires-Dist: slab-schemas>=0.35.0
|
|
7
7
|
Requires-Dist: httpx>=0.27
|
|
8
8
|
Requires-Dist: rich>=13.0
|
|
9
9
|
Requires-Dist: inquirerpy>=0.3
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "slab-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.20.0"
|
|
4
4
|
description = "CLI for the slab trading-card API — catalog, collect, and track your cards from the terminal."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [{name = "dev_jeb"}]
|
|
@@ -11,7 +11,8 @@ dependencies = [
|
|
|
11
11
|
# grading-desk models `slab card grade` / `slab collection grade` parse responses into.
|
|
12
12
|
# 0.34 reframes the grading desk from verdicts to break-even readings (BreakEvenRegion,
|
|
13
13
|
# GradingDesk.break_even) — the desk shows data, never tells the user to grade.
|
|
14
|
-
|
|
14
|
+
# 0.35 adds Liquidity/LiquidityLabel, which the market view's Sells column renders.
|
|
15
|
+
"slab-schemas>=0.35.0",
|
|
15
16
|
"httpx>=0.27",
|
|
16
17
|
"rich>=13.0",
|
|
17
18
|
"InquirerPy>=0.3",
|
|
@@ -20,7 +20,8 @@ from slab_schemas.community import (
|
|
|
20
20
|
from slab_schemas.custom_sets import CustomSetDetail, CustomSetOut, CustomSetSearchResult
|
|
21
21
|
from slab_schemas.dashboard import CatalogStats, DashboardStats, HighlightCard, LabeledCount
|
|
22
22
|
from slab_schemas.grading_desk import CollectionGradingDesk, GradingDesk
|
|
23
|
-
from slab_schemas.enums import BreakEvenRegion, Grade, SealedFormat
|
|
23
|
+
from slab_schemas.enums import BreakEvenRegion, Grade, LiquidityLabel, SealedFormat
|
|
24
|
+
from slab_schemas.liquidity import Liquidity
|
|
24
25
|
from slab_schemas.pricing import CardComps, CardMarket, PortfolioSummary, SetTopCards
|
|
25
26
|
from slab_schemas.sealed import SealedPriceHistory, SealedProductOut
|
|
26
27
|
from slab_schemas.timeseries import CardPriceHistory
|
|
@@ -971,7 +972,8 @@ def render_card_market(m: CardMarket) -> None:
|
|
|
971
972
|
|
|
972
973
|
# The Δ columns are FMV (appraisal) drift, not the window's sales — FMV smooths 90 days of
|
|
973
974
|
# comps, so these lag the live market (two-lane rule, CLAUDE.md). Headers say FMV so a
|
|
974
|
-
# "+$5" cell reads as the appraisal moving, not as last week's market.
|
|
975
|
+
# "+$5" cell reads as the appraisal moving, not as last week's market. "Sells" is the other
|
|
976
|
+
# lane: real sale cadence from comps (liquidity), so the price and its pace sit side by side.
|
|
975
977
|
table = slab_table([
|
|
976
978
|
("Grade", {"style": "slab.value"}),
|
|
977
979
|
("FMV", {"justify": "right"}),
|
|
@@ -979,6 +981,7 @@ def render_card_market(m: CardMarket) -> None:
|
|
|
979
981
|
("FMV Δ7d", {"justify": "right"}),
|
|
980
982
|
("FMV Δ30d", {"justify": "right"}),
|
|
981
983
|
("Comps", {"justify": "right", "style": "slab.dim"}),
|
|
984
|
+
("Sells", {"style": "slab.dim"}),
|
|
982
985
|
])
|
|
983
986
|
|
|
984
987
|
for pp in sorted(m.price_points, key=lambda p: (p.finish or "", p.grade_key)):
|
|
@@ -990,14 +993,39 @@ def render_card_market(m: CardMarket) -> None:
|
|
|
990
993
|
if pp.price_low is not None and pp.price_high is not None:
|
|
991
994
|
rng = f"${pp.price_low:,.0f} – ${pp.price_high:,.0f}"
|
|
992
995
|
table.add_row(label, fmv, rng, signed_money(pp.price_change_7d),
|
|
993
|
-
signed_money(pp.price_change_30d), str(pp.sample_size)
|
|
996
|
+
signed_money(pp.price_change_30d), str(pp.sample_size),
|
|
997
|
+
_liquidity_text(pp.liquidity))
|
|
994
998
|
|
|
995
999
|
window = m.price_points[0].window_days
|
|
996
1000
|
console.print(heading("Market Price", f"{window}-day window, as of {m.as_of_date}"))
|
|
997
1001
|
console.print(table)
|
|
1002
|
+
if any(pp.liquidity for pp in m.price_points):
|
|
1003
|
+
console.print(
|
|
1004
|
+
" [slab.dim]Sells = how often this exact printing and grade actually sells — the "
|
|
1005
|
+
"typical gap between real sales in the last 90 days, or the past year's count for "
|
|
1006
|
+
"slow movers. A rare seller isn't bad; it means the price is an estimate you may "
|
|
1007
|
+
"wait to realize.[/]"
|
|
1008
|
+
)
|
|
998
1009
|
console.print()
|
|
999
1010
|
|
|
1000
1011
|
|
|
1012
|
+
def _liquidity_text(liq: Liquidity | None) -> str:
|
|
1013
|
+
"""A liquidity reading as one short table cell — the pace, then the datum behind it.
|
|
1014
|
+
Data statements only, mirroring the `liquidity.*` glossary."""
|
|
1015
|
+
if liq is None:
|
|
1016
|
+
return "—"
|
|
1017
|
+
L = LiquidityLabel
|
|
1018
|
+
if liq.median_days_between_sales is not None:
|
|
1019
|
+
gap = liq.median_days_between_sales
|
|
1020
|
+
word = {L.sells_daily: "~daily", L.sells_weekly: "~weekly"}.get(liq.label, "~monthly")
|
|
1021
|
+
return f"{word} (every {gap}d)" if gap > 0 else f"{word} (several/day)"
|
|
1022
|
+
if liq.label == L.too_new_to_say:
|
|
1023
|
+
return f"too new to say ({liq.observed_days}d watched)"
|
|
1024
|
+
if liq.label == L.no_recent_sales:
|
|
1025
|
+
return "none this year"
|
|
1026
|
+
return f"{liq.sales_365d}×/year"
|
|
1027
|
+
|
|
1028
|
+
|
|
1001
1029
|
def _format_label(fmt: str) -> str:
|
|
1002
1030
|
"""SealedFormat value -> display label: hobby_box -> Hobby Box."""
|
|
1003
1031
|
return fmt.replace("_", " ").title()
|
|
@@ -1201,15 +1229,21 @@ def render_grading_desk(d: GradingDesk) -> None:
|
|
|
1201
1229
|
("Sells for", {"justify": "right", "style": "slab.foil"}),
|
|
1202
1230
|
("You clear", {"justify": "right"}),
|
|
1203
1231
|
("Sales", {"justify": "right", "style": "slab.dim"}),
|
|
1232
|
+
("Sells", {"style": "slab.dim"}),
|
|
1204
1233
|
])
|
|
1205
1234
|
for l in d.lanes:
|
|
1206
1235
|
n = f"{l.sample_size}!" if l.low_confidence else str(l.sample_size)
|
|
1207
|
-
table.add_row(l.grade_key, money(l.fair_market_value), signed_money(l.payoff), n
|
|
1236
|
+
table.add_row(l.grade_key, money(l.fair_market_value), signed_money(l.payoff), n,
|
|
1237
|
+
_liquidity_text(l.liquidity))
|
|
1208
1238
|
console.print(heading("Payoff per Grade", "after the fee, vs staying raw"))
|
|
1209
1239
|
console.print(table)
|
|
1210
1240
|
if any(l.low_confidence for l in d.lanes):
|
|
1211
1241
|
console.print(" [slab.dim]! = priced off very few sales — an anecdote, "
|
|
1212
1242
|
"not a market.[/]")
|
|
1243
|
+
if any(l.liquidity for l in d.lanes):
|
|
1244
|
+
console.print(" [slab.dim]Sells = how often that grade actually trades — a payoff "
|
|
1245
|
+
"at a grade that sells twice a year is a longer wait than one that "
|
|
1246
|
+
"sells weekly.[/]")
|
|
1213
1247
|
|
|
1214
1248
|
console.print()
|
|
1215
1249
|
console.print(f" {_desk_reading_line(d)}")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|