Files
amethyst/cli/ROADMAP.md
T
Claude b6c065d421 feat(quartz,cli): add KindNames registry + amy kind
Introduce a canonical, i18n-free event-kind registry in quartz:
`com.vitorpamplona.quartz.kinds.KindNames` maps each of the 145 known
kinds to an English label + the defining NIP. The data is ported from
Amethyst's relay-view `kindDisplayName` mapping (the NIP derived from
each event class's package), so the "what is kind N" knowledge now lives
once in quartz instead of only in the Android UI.

`amy kind <N|NAME>` looks a kind up by number (label + NIP) or searches
labels by name — a thin wrapper over KindNames, dispatched statelessly
(no account/network).

i18n split: quartz holds the canonical English (quartz is intentionally
translation-free); localized front ends overlay their own strings and can
fall back to KindNames.nameFor() for kinds they don't translate. amy
prints the English directly.

Verified: kind 1/0/30023/1059/24133 labels+NIPs, name search ("podcast"
→ 4), unknown → known:false, text + JSON output.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011SapGdtAc1j7woifoCZ9fY
2026-06-21 21:00:54 +00:00

9.7 KiB

Amy Roadmap

The live checklist for growing amy from a Marmot test-bed into a full command-line mirror of Amethyst.

How to use this file: update it in the same PR that ships a feature. Move rows between tables, adjust ordering, add non-goals. This is the single source of truth for "what's left".


North-star goal

For every feature of the Amethyst Android app, there is a way to exercise it through amy, with byte-identical on-relay behaviour.

Why:

  • Interop tests against the ~100 other Nostr clients need a reproducible harness that does not require running Android.
  • Agents and LLMs can script real Amethyst flows without a GUI.
  • Regressions in shared logic (signing, encryption, filter building, event parsing) become shell-scriptable.
  • Power users get a command-line Amethyst for free.

Non-goal: Amy is not a second Nostr client implementation. It is a thin assembly layer over quartz + commons. Protocol and business logic in cli/ are bugs.


Parity matrix

Status legend: shipped · 📦 logic lives in commons/, needs a command · 🆕 needs extraction from amethyst/ first · ⚠️ blocked (see Notes).

Area Status Notes
Identity create / import (nsec, ncryptsec, mnemonic, npub, nprofile, hex, NIP-05) LoginCommand + Quartz NIP-05 / NIP-06 / NIP-49
Account bootstrap (nine events) commons/account/AccountBootstrapEvents.kt
Relay config + NIP-65 / NIP-10050 publish RelayCommands
MLS KeyPackage publish + fetch commons/marmot/MarmotManager
Marmot group create / add / rename / promote / demote / remove / leave commons/marmot/
Marmot message send / list commons/marmot/
await polling (KP / group / member / admin / message / rename / epoch) AwaitCommands
NIP-01 note publish (amy notes post TEXT) PostCommand — outbox via RelayCommands configured set.
NIP-01 feed read (amy notes feed [--following | --author NPUB]) FeedCommand. Hashtag / community feeds still pending.
NIP-02 follow list add / remove / list 🆕 Logic in amethyst/model/nip02FollowLists/.
NIP-09 event deletion 🆕 Builder exists in quartz.
NIP-17 DMs send / list / await DmCommands — reuses Quartz NIP17Factory + RecipientRelayFetcher; filter extracted to commons/relayClient/nip17Dm/. Plan: cli/plans/2026-04-23-nip17-dm.md.
NIP-18 reposts / quotes 🆕
NIP-25 reactions in groups · 🆕 elsewhere marmot message react covers MLS group reactions; outer-event reactions still pending.
NIP-51 lists (bookmarks, mute, follow sets) 🆕 amethyst/model/nip51Lists/
NIP-57 zaps (send + verify) 🆕 Needs LN-URL plumbing; amethyst/service/lnurl/.
NIP-65 outbox model queries 🆕
NIP-72 communities 🆕
NIP-78 app-specific data (settings sync) 🆕
Long-form (NIP-23) publish / read 🆕
Live activities / chess (NIP-53 / NIP-64) 🆕
Blossom uploads (NIP-B7) 🆕
NIP-47 Wallet Connect 🆕
NIP-46 bunker signer 🆕 Needs a signers abstraction in Amy.
Profile view (amy profile show NPUB) + edit ProfileCommands. Cache-first; --refresh forces a relay drain.
Thread view (amy thread show EVENT_ID) ⚠️ Same.
Notifications feed 🆕
Search (NIP-50) 🆕

nak parity — army-knife primitives

Tracking fiatjaf/nak's command surface. amy adapts the verbiage where its own conventions differ (req → one-shot fetch vs streaming subscribe). Stateless verbs run with no account or network.

nak command amy verb Status Notes
decode amy decode NIP-19/21 → JSON. Quartz Nip19Parser.
encode amy encode npub/nsec/note/nevent/nprofile/naddr.
verify / validate amy verify id-hash + signature, reported separately.
key amy key generate|public|encrypt|decrypt generate/derive + NIP-49 encrypt/decrypt (bidirectionally nak-verified). expand/combine/validate/default still 🆕.
event amy event build/sign an arbitrary event, optional --publish/--relay.
publish amy publish broadcast a pre-made event JSON (verified first).
req (one-shot) amy fetch filter → collect-until-EOSE, dedupe, sort, cap.
req (stream) amy subscribe filter → live NDJSON stream to stdout.
count amy count NIP-45, per-relay counts.
encrypt / decrypt amy encrypt|decrypt raw NIP-44 (default) / NIP-04.
gift amy gift wrap|unwrap NIP-59 seal+wrap / unwrap+unseal.
relay (NIP-11) amy relay info stateless NIP-11 doc fetch.
outbox amy outbox NIP-65 read/write relays, cache-first.
filter amy filter stateless — assemble + print a filter JSON.
blossom amy blossom upload/download/list/delete/check/mirror (reuses commons BlossomClient).
nip amy nip repo-first lookup + Nostr wiki/long-form fallback; nip list.
kind amy kind quartz KindNames registry (kind → English label + NIP), ported from the Android relay view; number lookup + name search.
sync amy sync NIP-77 Negentropy reconcile with the local store (down/up/both).
git amy git in part NIP-34 repo announce/list/show/issue. clone/push (packfile transport) out of scope.
podcast amy podcast NIP-F4 show metadata (10154) + episode publish (54) + list.
bunker amy bunker[ connect] + amy login bunker:///--nostrconnect NIP-46 remote signer + login, both the bunker:// and nostrconnect:// flows, each direction, plus auth_url challenge handling (client surfaces the URL + keeps waiting). Interop-verified vs real nak.
serve / admin / wallet / mcp / fs / spell 🆕 (tier 2/3) larger/niche; some pull new deps.

Full nak comparison (introspected both binaries)

nak has 34 functional commands. Coverage:

  • Full / equivalent (19): event, req(→fetch+subscribe), filter, count, decode, encode, verify, relay, bunker(+nostrconnect+auth_url), encrypt, decrypt, gift, publish, sync, profile, podcast, nip, kind, blossom. Protocol-sensitive ones (bunker, sync, key NIP-49, encode/decode) are interop-verified against the real nak binary.
  • Partial / adapted (4): key (no expand/combine/validate/default), git (NIP-34 events only — no packfile transport), outbox (shows NIP-65 vs nak's hints DB), fetch (filter-based, not nip19-hint resolution).
  • Missing (11): admin (NIP-86), serve (geode is a standalone relay), dekey (NIP-4E), wallet (NIP-60 Cashu), mcp, curl (NIP-98), fs (FUSE), group/nip29 (NIP-29 — amy has MLS/Marmot instead), spell (MuSig2/FROST), validate (RoK schema).

Design differences (not gaps): amy is a stateful client (accounts, ~/.amy/, shared event store) with a stable JSON contract; nak is a stateless per-invocation tool that prints bare values for shell substitution. amy also has a large surface nak lacks: Marmot/MLS, NIP-17 DMs, zaps, CLINK offer/debit, NIP-02 follow, NIP-50 search, napplets, profile edit, store management, account management.

Cheap remaining wins: the key expand/combine/validate/default sub-verbs.


Order of operations

Proposed sequencing. Each step is one PR. Each step should extract at least one file from amethyst/ into commons/; if it doesn't move anything, re-audit — you're probably duplicating logic.

  1. Event rendering core in commons/commonMain/.../rendering/ with renderers for kinds 0 / 1 / 3 / 6 / 7 / 10002 / 10050. Unblocks all the 🆕 and ⚠️ read-path rows below. Design: commons/plans/2026-04-21-event-renderer.md.
  2. amy notes post / amy notes show / amy notes react — smallest end-to-end write+read loop outside Marmot. Post + feed shipped; notes show and outer-event react still pending.
  3. amy notes feed home|profile|hashtag|thread reading through the renderer. --following and --author NPUB ; hashtag/thread variants still pending.
  4. amy follow add|remove|list (NIP-02) — proves extraction of list-building logic from amethyst/model/.
  5. amy dm send|list (NIP-17) — shipped. Reuses the gift-wrap path also exercised by Marmot.
  6. amy list bookmarks|mute|pin … (NIP-51).
  7. amy zap send|verify (NIP-57).
  8. Distribution — Homebrew + Scoop + .deb in the same release pipeline as desktop. Plan: cli/plans/2026-04-21-cli-distribution.md.
  9. Test suite — end-to-end against a local relay. Marmot interop is covered by cli/tests/marmot/marmot-interop-headless.sh; NIP-17 DM interop between two amy clients is covered by cli/tests/dm/dm-interop-headless.sh (text + file + strict 10050 + fallback + cursor-advance). Neither runs in CI yet (both need Rust + ~3 min cold relay build).
  10. Everything else in the matrix.

Non-goals

  • Interactive TUI or REPL.
  • Native image (GraalVM) until Quartz has a pure-Kotlin signer fallback — secp256k1-kmp-jni-* needs JNI today.
  • A Gradle dependency on :amethyst or :desktopApp. Ever.
  • Re-implementing any Nostr protocol piece that's already in quartz/ or in another client's library.