Skip to main content

AppMates 🚀

CI codecov Python 3.11+ License: MIT Checked with mypy Ruff

Catch App Store and Google Play rejections before the stores do.

You upload ten screenshots, wait, and App Store Connect replies IMAGE_TOOL_FAILURE with no explanation. The actual cause is usually mundane — a stray alpha channel, a resolution that was valid in 2019, a screenshot two pixels too tall for Play's aspect ratio rule. AppMates finds those in milliseconds, tells you exactly which rule you broke, and can repair the files for you.

$ appmates validate-screenshots ./store/ios/en-US
Auto-detected target store: apple (override with --store)

  File                Size        Device            Findings
 ─────────────────────────────────────────────────────────────────────────────
  screen-01.png       1242×2688   iPhone 6.5"       ✗ APPLE_ALPHA_CHANNEL
                      2.9 MB      (legacy)            Image has an alpha channel (mode RGBA).
                                                      → Flatten onto an opaque background.
                                                    ! APPLE_LEGACY_SIZE
                                                      1242x2688 is absent from Apple's current table.
                                                      → Current equivalent is iPhone 6.5" (1284x2778).

  FAIL  10 file(s) checked against apple · 10 error(s) · 10 warning(s)
$ appmates fix-screenshots ./store/ios/en-US --out ./store/ios/en-US-fixed
$ appmates validate-screenshots ./store/ios/en-US-fixed --strict
  PASS  10 file(s) checked against apple · 0 error(s) · 0 warning(s)

Why it exists

Three things break releases, and all three are mechanically checkable:

Problem What AppMates does
Screenshots that violate a store rule Validates format, alpha channel, colour space, exact resolution, aspect ratio, file weight — and repairs all of them
Listing text over a character limit Checks title/subtitle/description/keywords against both stores' limits, and warns before translation pushes you over
Google Play's 12-testers-for-14-days gate Tracks the streak day by day, including the dips that silently reset your clock
A keyword field quietly wasting a third of its budget Audits the 100 characters Apple never shows anyone, and rebuilds them
Building an app into a market that cannot be won Scores a niche from the public catalogue before you write any code
Designing screenshots with no idea what the field does Summarises competitors' screenshot conventions, and saves theirs for reference
No idea whether your listing is indexed at all Reports your position per keyword and tracks it over time, locally
Launching into the one storefront where the term is locked Scores the same keyword across 14 storefronts and ranks them

Install

pip install -e ".[dev]"

Or with uv, which is what CI uses:

uv sync --extra dev

Try it without installing anything:

uvx --from . appmates specs

Commands

validate-screenshots

appmates validate-screenshots ./screenshots
appmates validate-screenshots ./screenshots --store google --strict
appmates validate-screenshots ./screenshots --json | jq '.assets[].findings[].code'
appmates validate-screenshots ./screenshots --ignore APPLE_LEGACY_SIZE

--store defaults to auto. App Store and Play screenshots are genuinely different assets — every current Apple iPhone size violates Play's "long side ≤ 2× short side" rule — so validating one directory against both always produces noise. AppMates picks whichever store the files actually fit and tells you which it chose. Use --store both when you want the cross-check anyway.

fix-screenshots

appmates fix-screenshots ./screenshots                      # dry run: prints the plan
appmates fix-screenshots ./screenshots --out ./fixed        # writes repaired copies
appmates fix-screenshots ./screenshots --out ./fixed \
    --target apple-iphone-6.9 --background "#0B0B0F"

Flattens alpha onto an opaque background, converts to sRGB, resizes to the nearest valid spec size, and re-encodes anything over the weight limit.

Your originals are never touched. The fixer refuses to write into its own source directory, and refuses a non-empty output directory without --force. Resizing letterboxes rather than stretches — a distorted UI screenshot looks broken to reviewers.

check-testers

appmates check-testers --days-passed 9 --active-testers 12
appmates check-testers --from-file tester-history.json
appmates check-testers -d 20 -t 12 --release-pending
╭─ Google Play · Closed testing ───────────────────────────────╮
│          Status  NOT YET ELIGIBLE                            │
│         Testers  12 / 12                                     │
│ Continuous days  9 / 14                                      │
│        Progress  ████████████░░░░░░░░  64.3%                 │
│  Projected date  2026-07-31                                  │
╰──────────────────────────────────────────────────────────────╯

The requirement is 12 testers opted in continuously for 14 days. The word that costs people two weeks is continuously: if your count drops to 11 on day nine, Google restarts the clock. Pass a day-by-day timeline with --from-file and AppMates detects the reset:

[
  { "date": "2026-07-01", "opted_in": 12 },
  { "date": "2026-07-02", "opted_in": 11 },
  { "date": "2026-07-03", "opted_in": 13 }
]

The flat --days-passed form cannot see a dip by construction, and the output says so rather than quietly implying you are fine.

niche

appmates niche "habit tracker" "morning routine" --country fr
appmates niche "sourdough starter log" --leaders
appmates niche "budget app" --json | jq '.keywords[0].verdict'
Keyword                     score  verdict     credible competitors
bouldering training log      76.1  OPEN         3
sourdough starter log        66.3  OPEN         1
gratitude journal            39.2  CONTESTED   32
habit tracker                30.8  LOCKED      49
photo editor                 16.2  LOCKED     149

Answers the question the established ASO tools structurally cannot: should I build this at all? Rank trackers and keyword optimisers assume a live app — you cannot track the ranking of something that does not exist. This runs before the first line of code, on the public App Store catalogue, with no account and no API key.

There is deliberately no "search volume" number. Apple has never published per-keyword search counts. The Search Ads popularity index that other tools relabel as volume is a relative score with no published methodology, and in September 2025 the number of US keywords scoring above the floor dropped 77% in four days when Apple changed something and told nobody. Every number AppMates prints is something it counted.

Six signals, each shown with its raw observation so you can disagree with the weighting and still trust the facts:

Signal What it observes Why it matters
Leader entrenchment median ratings of the top 10 You can out-design a competitor in a quarter; you cannot out-accumulate ten years of reviews
Credible competitors apps with >1,000 ratings Not how many apps exist — how many actually hold the position
Incumbent neglect median days since the leaders shipped A neglected market is an opening; weekly releases mean a funded team defending it
User dissatisfaction median stars of the leaders 4.9★ incumbents give users no reason to switch; 3.6★ is a product opening
Keyword under-targeting share of leaders with the term in their name Leaders ranking incidentally means the term is takeable by simply targeting it
Publisher concentration share of slots held by repeat publishers One studio holding several slots is cross-promotion you cannot outspend

The weights, curves and thresholds all live in core/specs/market.yaml with the reasoning written next to each number. Edit them and re-run.

On the raw result count. An earlier revision scored saturation from the number of apps the store returns. Measured across eight keywords spanning photo editor to sourdough starter log, it returned 162–191 every time — a standard deviation of 9, because the store pads every result page with loosely related apps. Three unrelated markets scored 45, 46 and 47. Counting only apps with real traction separates the same keywords by 1 to 149. The result count is still reported as context, but it is not scored.

markets

appmates markets "habit tracker"
appmates markets "budget app" --countries us,fr,de,jp,br
appmates markets "gratitude journal" --json | jq '.best_country'
habit tracker across 8 storefront(s)

  Storefront            Score              Verdict     Credible rivals
  France          FR       51  ██████░░░░  CONTESTED                12
  Italy           IT       50  ██████░░░░  CONTESTED                15
  Japan           JP       48  ██████░░░░  CONTESTED                23
  Germany         DE       41  █████░░░░░  CONTESTED                36
  United States   US       31  ████░░░░░░  LOCKED                   49

  This term is CONTESTED in France and LOCKED in United States.
  Which storefront you lead with is a bigger lever here than anything you
  can do to the listing itself.

Every ASO tool asks how hard is this keyword? as though there were one answer. There are 175 storefronts and the answer differs in each — difficulty tracks the language a term is searched in more than the size of the country. "habit tracker" faces 49 credible rivals in the US and 12 in France.

That makes it a launch decision — which locale to write first, which language to localise screenshots into — and almost nobody asks it before shipping. It uses the same six signals as niche, one storefront at a time.

The summary keys off whether the verdict changes, not the numeric spread. A 19-point gap that moves a term from LOCKED to CONTESTED changes what you should do; a 30-point gap entirely inside CONTESTED does not. An earlier version summarised on the spread alone and reported "about as hard everywhere" for a term that was locked in the US and contested in France.

This is deliberately slow: one request per storefront against a free endpoint that rate-limits, spaced politely apart. A 14-country sweep takes about a minute the first time, then comes from cache. A storefront that fails is recorded and skipped rather than aborting the sweep.

app

appmates app 1438388363
appmates app com.example.myapp --country fr --json
Habit Tracker

  Publisher       Inner Grow Limited
  Version         2.14.18 · shipped 2026-07-10
  Rating          4.79★ from 144,369
  Size            321.7 MB
  Languages       10 — EN, FR, DE, IT, JA, PT, RU, ZH, ES, KO

Listing health
  ✓  App name within 30 characters      13 of 30 characters used.
  ?  At least three iPhone screenshots  The catalogue returned iPad screenshots but not
                                        iPhone ones, which it does for about half of apps.
  ✓  Recently updated                   Last shipped 17 days ago.
  ✗  Downloadable over cellular         321.7 MB.
                                        → Over 200 MB needs Wi-Fi unless the user has
                                          opted in — friction at the moment they decided
                                          to install.

  89/100 — 8 of 9 answerable checks passed, 2 could not be answered

Eleven checks over one published listing: name length, description, screenshot count and aspect ratio, update recency, release notes, localisation, download size, rating volume.

The interesting part is what it refuses to conclude. A check that cannot be answered is marked ? and excluded from the score — an app whose screenshots the catalogue happened to withhold must not rank below one whose it happened to return. That distinction needs the device list: an app that supports iPhone but exposed no iPhone screenshots has almost certainly shipped them, while an iPad-only app genuinely has none. Without it, the first would be reported as a defect and a developer would go "fix" something that was never broken.

Two further limits are stated rather than worked around. Screenshot URLs serve a downscaled image that preserves the aspect ratio but not the resolution, so the device family is inferred and the uploaded pixel size is never claimed. Subtitles and the keyword field are not public at all, so neither is checked.

In the web app this is the Overview page, and the app you load there becomes the app the whole session is about — its id prefills the rank check, its name prefills the competitor comparison and the keyword builder.

competitors

appmates competitors "habit tracker" --top 10
appmates competitors "gratitude journal" --screenshots
appmates competitors "gratitude journal" --download ./rivals --width 300
appmates competitors "budget app" --country fr --json
habit tracker · US · 190 results

  #   App                        Ratings   Stars   Updated   iPhone   iPad
  1   Habit Tracker              144,343     4.8       16d        0     10
  2   Habit Tracker - HabitKit     2,274     4.9       55d      n/a    n/a
  3   Onrise: Habit Tracker        2,495     4.8      187d        7      0
  4   Productive - Habit Tracker  91,098     4.6      129d        7      7

  What this field does with screenshots
  Median iPhone screenshots   7
  Count distribution          3×1 · 7×3
  Mostly portrait             4 of 5
  Ship iPad screenshots       3 of 5
  Using all 10 slots          0

Who you are up against for a term, and how they present. The niche summary is the useful part: knowing the field ships a median of seven portrait screenshots tells you more than any single competitor does.

Dimensions and orientation are read from the screenshot URLs themselves — the catalogue encodes the served size in the last path segment — so describing a competitor's whole gallery costs no bandwidth. --download saves them for offline reference.

Screenshot availability is partial, and the tool says so. Across a 55-app sample the catalogue exposed iPhone screenshots for 47% of apps and some set for 55%. Apps it withheld are marked n/a rather than shown as having none, and they are excluded from the medians instead of counted as zeros — folding them in would halve every figure for a reason that has nothing to do with your competitors. iPhone and iPad counts are separate for the same reason: an app can expose ten iPad screenshots and no iPhone ones.

--terms: what the field agrees on

appmates competitors "gratitude journal" --terms --mine "Kaizen: Gratitude"
  Term        Consensus   In app names   In descriptions   Yours
  habit              92            9/10             10/10     yes
  tracker            70            8/10              3/10     yes
  goal               30            2/10              7/10       —
  routine            18            1/10              5/10       —

  Not in your listing: goal, routine, daily, streak
  appmates keywords --title '…' -t 'goal' -t 'routine' -t 'daily'

Closes the loop: find the vocabulary your rivals share, then feed what you lack straight into keywords.

Terms are counted once per app, not per occurrence — otherwise one verbose description outvotes the field. App-name usage weighs four times description usage, because a word inside a 30-character name is a deliberate decision while the same word in paragraph four may be prose. Plural variants are merged, since a field where every app says both "habit" and "habits" would otherwise report each at half strength and neither would rank.

This sees roughly half of what rivals target. The public catalogue exposes app names and descriptions. It does not expose subtitles or keyword fields. Treat the result as a floor on their vocabulary, not the whole of it.

rank

appmates rank 1438388363 "habit tracker" "daily habits"
appmates rank com.example.myapp "budget" --country fr
appmates rank 1438388363 "habit tracker" --history ./ranks.jsonl
Habit Tracker · US · ranked for 3/3 term(s)

  Keyword           Position   Movement   Since
  habit tracker           #1        ▲ 3   2026-07-20
  daily habits            #1        ▲ 3   2026-07-20
  streak counter          #4        ▲ 3   2026-07-20

What this measures. The public catalogue returns results in its own relevance order, and this reports your app's place in that order. It is a real, repeatable signal, and it moves when your metadata moves.

What it is not. The App Store app serves search through a different path, with personalisation and paid placements, so the two can disagree. This is a directional measure of how your listing is indexed — not the number a given user would see. Tools that present a figure like this as the App Store ranking are overstating what any public endpoint can tell them.

--history appends each run to a local JSON Lines file and reports movement against it. There is no server, so that file is the record; appending never rewrites earlier lines, and one malformed line cannot discard the rest.

keywords

appmates keywords --title "Kaizen: Habit Tracker" --subtitle "Build daily routines" \
    --field "habit, habits, habit tracker, daily, app, free, productivity, streak" \
    -t "habit tracker" -t "gratitude journal"

appmates keywords listing.toml --json
appmates keywords --title "Kaizen" -t "habit tracker" -t "morning streak" --build
  Budget  ███████████████████████░░░░░░░  77/100  · 58 wasted

  ✗ ASO_KEYWORD_SPACES        -9c   9 space(s) in the keyword field.
  ✗ ASO_DUPLICATE_OF_TITLE    -8c   'tracker' is already in the title.
  ! ASO_CATEGORY_WORD        -13c   'productivity' is already indexed from your category.
  ! ASO_PLURAL_PAIR           -7c   'habits' and 'habit' differ only by pluralisation.

  Target phrase        Reachable   Indexed from
  habit tracker        yes         title (habit, tracker)
  gratitude journal    no          missing: gratitude, journal

  suggested  routine,morning,streak,gratitude,journal,habits
  47/100 characters, 30 reclaimed

The 100-character keyword field is the most technical artefact in a listing and the only one never shown to a user — so nothing about it is self-correcting. A listing with a third of its budget wasted looks exactly like a perfect one.

Every rule follows from one documented Apple behaviour: the app name, subtitle and keyword field are indexed as a single pool of words, and matches are formed by combining them. Three consequences do most of the work:

  • A phrase is reachable when every one of its words is in the pool. The phrase itself never has to appear. habit,tracker ranks for "habit tracker".
  • A word already in your title is already indexed. Repeating it costs characters and buys no reach.
  • Every space is overhead, since Apple does the combining. habit tracker and habit,tracker index identically; the second is a character shorter.

--build packs the minimum set of words covering your target phrases into the budget, dropping anything already in the title, and emits a bare field for piping. Target order is treated as priority, so the phrases you listed first survive truncation.

On the waste figure. Costs are deduplicated per word. One word commonly trips several rules at once — habit can be both a duplicate within the field and already present in the title — but deleting it reclaims its characters once, not once per rule. Summing the findings would inflate the total by exactly the double-counting this tool exists to call out.

The word lists and severities live in core/specs/aso.yaml.

validate-metadata

appmates validate-metadata listing.toml
appmates validate-metadata listing.json --store apple --strict
[[locales]]
locale = "en-US"
title = "Kaizen: Habits"
subtitle = "Small steps, big change"
short_description = "Track habits and build streaks."
description = "Kaizen helps you build lasting habits."
keywords = "habit,streak,focus,routine"

Checks Apple (30/30/170/4000/100) and Play (30/80/4000) limits, flags fields above 90% of their limit because translations run longer than English, and catches the Apple keyword traps — spaces after commas waste your 100-character budget, and repeating title words wastes indexing. Takes every locale in the file at once — check a [[locales]] block per language in one run rather than one file per language.

submission-check

appmates submission-check --screenshots ./store/ios --metadata listing.toml
appmates submission-check --keywords "habit,streak,focus"

One go/no-go answer, composed from whichever of validate-screenshots, validate-metadata and keywords you give inputs for — the same three engines, so nothing here is a new rule. Runs only what you point it at: give just --keywords to audit a field with no listing file yet. When --metadata supplies a listing and --keywords doesn't override it, the keyword audit reads the title, subtitle and keyword field straight out of that locale, rather than a second copy typed in separately.

pricing

appmates pricing 4.99
appmates pricing 4.99 --model uniform --countries us,gb,in

Suggests a starting price per storefront from one base price — a read-only sanity check, never a push to any API. ppp_tier (the default) scales the price down in lower-income storefronts using coarse World Bank income-group bands; uniform reprints the same price everywhere for comparison. The bands are deliberately coarse: exact purchasing-power or Big Mac Index data drifts throughout the year and nothing here is positioned to keep it current, so a three-band approximation that's honest about being one beats a precise-looking number that quietly goes stale. Tiers live in core/specs/pricing.yaml.

asc — App Store Connect, optional

appmates asc status                                          # is a key configured and accepted?
appmates asc pull com.example.app --out listing.json         # real listing text + current prices
appmates asc push-metadata com.example.app listing.json      # dry run: prints the diff, sends nothing
appmates asc push-metadata com.example.app listing.json --yes # sends it

Everything else in AppMates works with zero account and zero credentials — this is the one command family that doesn't, because reading or writing your real App Store Connect listing needs your real API key. Three things make that safe to opt into:

Your private key never leaves your machine. It is read from disk once, used to sign a short-lived (15-minute) request token, and that's it — never logged, never cached, never sent anywhere but straight to api.appstoreconnect.apple.com. AppMates' web dashboard and Cloudflare backend are never in this path, ever; there is nothing to trust beyond Apple itself. See core/clients/appstore_connect.py for exactly what that covers.

  • Credentials live in .env, never a flag. Copy .env.example, fill in LAUNCHPILOT_APP_STORE_KEY_ID / _ISSUER_ID / _PRIVATE_KEY_PATH from a key generated at Users and Access → Integrations. A CLI flag would sit in shell history and ps output; an env var doesn't.
  • pull is read-only. It writes the exact {"locales": [...]} shape validate-metadata and submission-check already read — paste it straight into the web dashboard's multi-locale checker, no conversion.
  • push-metadata is a dry run by default. It always prints the field-by-field diff first; nothing is sent until you pass --yes. It also runs the same checks validate-metadata does before sending anything, and refuses to push a listing with an error-level finding unless you also pass --force.
  • Pricing is pull-only, on purpose. asc pull shows what's live per territory next to what appmates pricing suggests, but does not push a price schedule — that API references specific price-point ids per territory rather than a plain number, and a mistake there is a real, live price in front of real customers. Set it by hand in App Store Connect until this path has been proven against more accounts than the one it was built against.

specs

appmates specs --store apple
appmates specs --json

Prints the bundled specification catalogue with a last_verified date and the source URL for each store, so you can see how stale the data is.

Use it in CI

Every command exits 0 clean, 1 on findings, 2 on a usage error.

- name: Validate store assets
  run: |
    appmates validate-screenshots ./store/ios --store apple --strict
    appmates validate-metadata ./store/listing.toml

--json emits the same Pydantic models the planned HTTP API will return, so anything you build against it now keeps working later.

Web checker (no install, no upload)

Every tool above also runs in the browser, with no install, no account and no server of ours in the path. The offline tools read file headers rather than pixels; the market tools call Apple's public catalogue directly, which answers with access-control-allow-origin: *.

python3 -m http.server 8000 --directory web

Each browser engine is conformance-tested against its Python original — finding codes, character costs, coverage, winnability, every signal's observation and rationale, streak and reset detection. CI fails if they disagree or if any generated file is stale. See web/README.md.

python3 -m http.server 8000 --directory web

It is kept honest rather than kept in sync by hand: the specification data is generated from the same YAML the CLI reads, and the JavaScript engine is tested against the Python one on 925 cases plus a set of real images parsed by Pillow. CI fails if any generated file is stale. See web/README.md.

Community marketplace (optional, needs a backend)

Get real closed testers before you ship, and real users for a launch or update once you have — the "Get testers" / "Be a tester" pages in the web app (web/views/community.js, web/views/be-tester.js). Two-sided: a developer posts a listing, real people request to join, and completing a test earns the tester a High Five they can spend getting their own app tested. Nothing here ever touches App Store or Play reviews — see SECURITY.md for why that boundary is load-bearing, not incidental.

This is the one part of AppMates that needs an account (magic-link email, no password) and a backend — a separate Cloudflare Worker + D1 database in its own private repo, appmates-community, kept out of this one because its anti-abuse thresholds and rate limits are a real target for gaming the token economy once this repo is public. COMMUNITY_API_URL in web/lib/community.js points at it; null disables the whole feature (the nav items stay hidden and nothing here is ever called).

What's shipped today: listing creation with an optional auto-expiry date, owner-side deletion, a platform filter on the marketplace, a leaderboard, push notifications (new request, accepted, build link unlocked, feedback ready, declined), and admin moderation (ban a user, remove a listing) off the back of a report queue. A closed-test listing's TestFlight/Play link stays hidden from everyone until the owner accepts a tester and confirms adding them on the actual store console — there's no App Store Connect/Play Developer API integration here to do that step automatically.

Architecture

src/appmates/
├── cli/            # Typer commands: argument parsing and rendering only
└── core/
    ├── specs/      # ← versioned YAML spec catalogue + registry
    ├── models/     # Pydantic contracts (Finding, Severity, reports)
    └── services/   # the actual logic

web/                # browser checker, generated from and tested against the above
scripts/            # generators for the web artefacts (run with --check in CI)

Three decisions worth knowing about:

Specs are data, not code. core/specs/*.yaml holds every resolution and constraint, each entry tagged required / accepted / legacy / deprecated with a source URL and verification date. Store rules drift constantly; updating them must never require a code change. registry.py is the only access point, so moving this to a database later touches one module.

Findings carry stable machine codes. APPLE_ALPHA_CHANNEL, PLAY_MAX_TWICE_MIN, and friends are part of the public contract — CI annotations, --ignore, and the fixer all key off them.

The CLI holds no logic. Every command calls a core service that returns a Pydantic model. The CLI renders it; --json serialises it; FastAPI will return it. One implementation, three surfaces.

Development

uv sync --extra dev
uv run pytest              # 174 tests, coverage gate at 85%
uv run ruff check .
uv run ruff format .
uv run mypy                # strict

Test images are generated at run time with Pillow rather than committed — binary fixtures cannot be diffed in review, and they hide the very properties (mode, size, alpha) each test is about.

Roadmap

Open source core

  • Screenshot validation for App Store and Play
  • Automatic repair (alpha, colour space, resolution, weight)
  • Listing text validation for both stores
  • Closed-testing streak tracking with reset detection
  • JSON output and CI exit codes
  • Zero-backend web checker with browser-side repair
  • niche — pre-build market assessment from the public catalogue
  • keywords — 100-character field audit, coverage map and builder
  • Every tool in the browser, conformance-tested against the CLI
  • competitors — the field for a term, and what it does with screenshots
  • rank — search position per keyword, with local history tracking
  • markets — which storefront a term is actually winnable in
  • app — one listing's health, and an Overview page that anchors the session
  • competitors --terms — the vocabulary a field shares, and what you lack
  • appmates init — scaffold a appmates.toml per project
  • Play graphic assets (icon, feature graphic) as first-class checks
  • Device-frame screenshot generation
  • GitHub Action wrapper + SARIF output for inline PR annotations
  • Publish to PyPI

SaaS layer

  • FastAPI service over the same core models — the --json payloads are already the API contract
  • Live store integration: App Store Connect (JWT ES256) and Play Developer API behind the existing TesterDataSource protocol
  • Hosted spec catalogue so rule changes ship without a release
  • Release dashboard: submission status, review times, rejection history across both stores
  • Alerting on closed-testing streak breaks — the failure this tool exists to prevent
  • Multi-app / team workspaces

The open-source CLI stays fully functional standalone. The SaaS adds hosting, history and live store credentials — never a gate on the local checks.

Contributing

Issues and PRs welcome — see CONTRIBUTING.md for dev setup, checks to run before opening a PR, and the code style expected. This project follows the Contributor Covenant.

License

MIT — see LICENSE.

Sources

Specifications are transcribed from, and dated against, the official docs:

Metadata

Release files for appmates 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for appmates 0.1.0
File Size Uploaded
appmates-0.1.0.tar.gz 602.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for appmates 0.1.0
File Interpreter ABI Platform
appmates-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 730.4 kB

Release files / appmates-0.1.0.tar.gz

Download URL appmates-0.1.0.tar.gz
Size 602.5 kB
Tags Source
SHA-256 checksum
How to use checksums
bda1e14544f09a2a41e1a49ac68da1b2c73d0ac9c2e3f7dedf9242dadc196851
BLAKE2b-256 checksum
How to use checksums
2643103bdaedfa01031565cc9dd9f180374c90da567008a3f8e2326f1445e5b7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 23, 2026.

Transparency log

Release files / appmates-0.1.0-py3-none-any.whl

Download URL appmates-0.1.0-py3-none-any.whl
Size 127.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6911e3d2ec66e72e12c741105fe869d9c44755e746d34d3641d3c7ee762845cd
BLAKE2b-256 checksum
How to use checksums
973b921050ff5fbe11dcb5ed81f7a19443347b27cd33a27c2599cf9c3e9df295
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 23, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page