account-state
Account state and in-memory event store patterns in Amethyst. Use when working with `Account.kt` (per-user state objects — `kind3FollowList`, `nip65RelayList`,…
Patterns for extending `amy`, the Amethyst CLI in `cli/`. Use when adding an `amy <verb>` command, touching files under `cli/src/main/kotlin/…/cli/`, wiring a new subcommand into `Main.kt`, writing an interop test script that drives Amy, or extracting logic out of `amethyst/`
$ npx -y skills add vitorpamplona/amethyst --skill amy-expert --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/amy-expertContext preview
The summary Claude sees to decide when to auto-load this skill.
Patterns for extending `amy`, the Amethyst CLI in `cli/`. Use when adding an `amy <verb>` command, touching files under `cli/src/main/kotlin/…/cli/`, wiring a new subcommand into `Main.kt`, writing an interop test script that drives Amy, or extracting logic out of `amethyst/`
name: amy-expert description: Patterns for extending `amy`, the Amethyst CLI in `cli/`. Use when adding an `amy <verb>` command, touching files under `cli/src/main/kotlin/…/cli/`, wiring a new subcommand into `Main.kt`, writing an interop test script that drives Amy, or extracting logic out of `amethyst/` into `commons/` so a CLI command can call it. Enforces the thin-assembly-layer rule (no Nostr protocol or business logic inside `cli/`), the dual-output contract (text by default, single-line JSON object on stdout under `--json`, exit codes 0/1/2/124), and the extract-from-Android recipe. Complements `nostr-expert` (protocol in Quartz), `kotlin-multiplatform` (expect/actual for extraction), and `feed-patterns` / `account-state` / `relay-client` (where the business logic should end up). NOT for general Nostr or Kotlin work — those have their own skills.
Practical patterns for touching the `cli/` module without breaking its public contract.
the single most common reason an Amy feature request stalls).
vs `quartz/` (answer: almost never `cli/`).
**Not for:** general Nostr protocol work (`nostr-expert`), general Kotlin (`kotlin-expert`), Compose UI (`compose-expert`), Android-only flows (`android-expert`), gradle/build (`gradle-expert`).
Amy has a small number of hard rules. Any change that breaks them is a breaking change to the CLI's public API, and breaks the interop- test harnesses that depend on it.
No new Nostr protocol, filter assembly, state machines, or encryption lives in `cli/`. Ever. If you need logic that doesn't exist yet:
Add it to `commons/` — extract from `amethyst/` first if needed (see Rule 5).
A `commands/*.kt` file longer than ~200 lines is a code smell. Either the command is doing too many things, or the logic has leaked in from where it should have lived.
amy ships a dual-output contract:
result map. No shape promise — the renderer can change between releases.
snake_case keys; this shape is the public API.
traces. Errors go here too — `error: <code>: <detail>` by default, JSON `{"error":"…","detail":"…"}` under `--json`.
await timeout.
breaking change and needs the commit message to say so.
Commands emit results via `Output.emit(mapOf(...))` and errors via `Output.error("code", "detail")`. The `Output` object (in `cli/src/main/kotlin/…/cli/Output.kt`) handles the text-vs-JSON branching automatically. Never `println(...)` user-facing output directly — `System.err.println(...)` is fine for progress logs only.
See `references/output-conventions.md`.
No `readLine()`, no TTY prompts, no hidden interactive behaviour. Passwords, names, keys, anything — all flags. Any network wait is an explicit `await` verb with `--timeout`.
State is reloaded from `~/.amy/` on every invocation. No singletons, no in-process caches that survive across runs. This is what lets 100 parallel interop scenarios share a harness safely.
The layout:
per machine, shared across every account.
`state.json`, `aliases.json`, `marmot/`.
the active account.
Account selection is via the global `--account NAME` flag (required when more than one account exists; auto-picked when exactly one does). `--account` cannot collide with subcommand flags, so commands like `marmot group create --name "Group"` or `profile edit --name "Alice"` keep their own `--name` parameter.
Tests isolate by overriding `$HOME` for the amy subprocess (`HOME=$(mktemp -d) amy --account alice init`). amy reads `$HOME` directly (not `user.home`, which JDK 21 derives from `getpwuid` and ignores `$HOME`), so the same convention `git`/`gpg`/`npm`/`ssh` follow Just Works.
If you need new persisted state, add it to `Config.kt`, `stores/FileStores.kt`, or a new helper (e.g. `Aliases.kt`) with a named JSON schema. Don't smuggle state into `~/.amy/` outside the documented files.
If the command you're about to add needs logic from `amethyst/`, land the extraction first, in its own commit:
1. Identify the class in `amethyst/src/main/java/…/`. 2. List its Android-only dependencies (`Context`, `SharedPreferences`, `WorkManager`, `Log`, `Bitmap`, `Uri`, …). 3. For each, choose: inline, platform-abstract via expect/actual, or take-as-constructor-arg. 4. Move the file to `commons/commonMain/…`. 5. Update the Android caller to use the new location. Add a JVM test. 6. **Then** add the `cli/commands/…` file.
Full checklist: `references/extraction-recipe.md`.
Every new command follows the same shape — parse args, open Context, prepare, call into commons/quartz, publish or drain, emit one result via `Output.emit`. The template is in `references/command-template.md`; copy it rather than re-deriving it.
Wire-up checklist: 1. New file in `cli/commands/` wit
Account state and in-memory event store patterns in Amethyst. Use when working with `Account.kt` (per-user state objects — `kind3FollowList`, `nip65RelayList`,…
Android platform patterns for the `amethyst/` module. Use when working with (1) Android navigation (Navigation Compose, type-safe routes, bottom nav), (2)…
Signer abstraction patterns in Amethyst. Use when working with event signing, choosing between a local keypair (`NostrSignerInternal`), a remote NIP-46 bunker…
Advanced Compose Multiplatform UI patterns for shared composables. Use when working with visual UI components, state management patterns (remember,…
Use when writing or reviewing Jetpack Compose layout APIs, modifier parameters, modifier chain construction, hardcoded root layout decisions, or layout…
Use when investigating Jetpack Compose recomposition performance, skippable/restartable composables, composables.txt or compiler reports, Layout Inspector…