Files
Vitor PamplonaandClaude Opus 5.5 395af4dc9c chore(skills): consolidate 33 skills into 14, verified against the tree
The 33 descriptions (~16k chars, ~19k with paths) overran Codex's skill-list
budget (2% of the context window, or 8,000 chars when unknown), and a review
found most bodies stale: paths from before the commons/commonsUI moves, the
deleted legacy desktop app, a callback signer API that no longer exists, and
example APIs that were never in the tree.

Kept, rewritten and checked against the code (every path, class and command):
account-state (+feeds), amy-expert, android-expert, auth-signers,
compose-expert (+API style, Navigation 3 back stacks), desktop-expert
(+run/package), event-store-semantics (+NIP-50 search), find-missing-
translations, kotlin-coroutines (+structured concurrency, flow modeling),
kotlin-multiplatform (+Gradle, composable extraction), ngit-pr,
nip85-trusted-assertions, nostr-expert (+NIP lookup, generated NIP catalog),
relay-client.

Dropped: textbook Compose/Kotlin technique skills, kotlin-expert,
kotlin-types-value-class, gradle-expert, quartz-integration (external-facing;
see the Quartz docs commit), find-non-lambda-logs (audit finished; the rule is
now in AGENTS.md Kotlin Style), and the /nip, /desktop-run, /extract commands.
The skill list is now ~4k chars. Also drops .claude/core-skills-plan.md
(stale history) and repoints SearchableEvent's KDoc to event-store-semantics.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:21:01 -04:00

2.7 KiB

NIP-19 bech32 in Quartz

All under quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip19Bech32/.

File Holds
Nip19Parser.kt object Nip19Parser: uriToRoute(uri): ParseReturn? (entity, nip19raw, additionalChars), tryParseAndClean(uri): String? (bare npub1… out of dirty input), parseAll(content), parseAllStandalone, parseAllEvents, hasAny. Top-level decodePublicKey, decodePublicKeyAsHexOrNull, decodePrivateKeyAsHexOrNull, decodeEventIdAsHexOrNull.
ByteArrayExt.kt toNpub(), toNsec(), toNote(), toNEvent(), toNProfile(), toNAddress(), toNEmbed(), toLnUrl() on raw bytes
entities/ Entity (plain interface) and NPub, NSec, NNote, NEvent, NProfile, NAddress, NRelay, NEmbed; each has parse(bytes) and a companion create(...) that returns the bech32 string
EventExt.kt Event.toNIP19(relayHint) — naddr for addressable events, nevent otherwise
ATagExt.kt ATag.toNAddr(), ATag.parseAny, ATag.parseNAddr, ATag.isATag
ListEntityExt.kt List<Entity>.eventHints()/eventIds()/addressHints()/pubKeys()… for parsed content
tlv/, TlvTypes.kt TlvBuilder (addHex, addString, addInt, …IfNotNull), TlvTypes { SPECIAL, RELAY, AUTHOR, KIND }
bech32/Bech32Util.kt object Bech32 (encodeBytes, decodeBytes), String.bechToBytes()

Encoding

val npub = pubkeyBytes.toNpub()                        // or NPub.create(hex)
val nevent = NEvent.create(idHex, authorHex, kind, relays)   // relays: List<NormalizedRelayUrl> (or one, nullable)
val nprofile = NProfile.create(pubkeyHex, relays)
val link = event.toNIP19(relayHint)

Entities have no toBech32()/toNostrUri() methods; use create(...), the ByteArray extensions, or toNIP19.

Decoding

when (val entity = Nip19Parser.uriToRoute(input)?.entity) {   // accepts nostr: URIs
    is NPub -> entity.hex
    is NEvent -> entity.hex to entity.relay                      // relay: List<NormalizedRelayUrl>
    is NAddress -> entity.aTag()                                  // or entity.address()
    null -> /* not bech32 */
}

Just need a pubkey from hex/npub/nprofile/nsec? decodePublicKeyAsHexOrNull(input), or resolveUserHexOrNull to also accept NIP-05 (see nip05-identifiers.md).

Gotchas

  • Relay hints in nevent/nprofile/naddr are hints; the outbox model decides where to fetch.
  • NEmbed is a gzip-compressed full event (NEmbed.create(event)), an Amethyst extension beyond NIP-19.
  • Never log an NSec or the input string it came from. PubKeysNeverLeakNsecTest (commonTest nip19Bech32/) guards the decode helpers.