用途
amy-expert 是一份面向 Amethyst 项目 CLI 模块的开发技能,主题是“在接触 cli/ 模块时如何不破坏其公共契约”。它适用于以下情形:新增 amy <verb> 子命令;编辑 cli/src/main/kotlin/…/cli/ 下的任何文件;编写驱动 Amy 的 shell 脚本或测试框架;将代码从 amethyst/ 中抽取出来供 CLI 调用(这是 Amy 功能请求停滞最常见的原因);判断某段逻辑应放在 cli/、commons/ 还是 quartz/(答案:几乎从不放在 cli/)。
它不适用于:通用 Nostr 协议工作、通用 Kotlin、Compose UI、仅 Android 的流程、gradle/构建。
核心规则
规则 1:cli/ 是薄装配层。 新的 Nostr 协议、过滤器装配、状态机或加密逻辑永远不得放在 cli/ 中。协议部分(事件 kind、标签、签名)放到 quartz/;业务逻辑(状态、默认值、排序、过滤器装配)放到 commons/,必要时先从 amethyst/ 抽取。commands/*.kt 文件超过约 200 行即是坏味道。
规则 2:默认文本,--json 是机器契约。 默认 stdout 是人类可读文本(结果映射的 YAML 风格渲染,不承诺形状);--json 将 stdout 切换为一行一个 JSON 对象,稳定的 snake_case 键,这个形状是公共 API。stderr 供人类使用,包括进度日志、警告、逐中继 ACK 追踪和错误。退出码:0 成功、1 运行时错误、2 参数错误、124 await 超时。新增 --json 键是安全的;重命名或删除是破坏性变更。命令通过 Output.emit(mapOf(...)) 发出结果,通过 Output.error("code", "detail") 发出错误,绝不直接 println(...) 面向用户的输出。
规则 3:永远非交互。 不使用 readLine(),不使用 TTY 提示,没有隐藏的交互行为。密码、名称、密钥、任何内容都通过标志传入。任何网络等待都是显式的 await 动词并带 --timeout。
规则 4:~/.amy/ 就是整个世界。 每次调用都从 ~/.amy/ 重新加载状态,没有跨运行存活的单例或进程内缓存。布局包括:~/.amy/shared/events-store/(每台机器一个文件支撑的 Nostr 事件存储,跨账户共享)、~/.amy/<account>/(每账户目录:identity.json、state.json、aliases.json、marmot/)、~/.amy/current(由 amy use NAME 写入的标记文件,固定当前活动账户)。账户通过全局 --account NAME 标志选择;--account 不能与子命令标志冲突,因此 --name 保留给子命令使用。测试通过为 amy 子进程覆盖 $HOME 来隔离。
规则 5:先抽取,再添加。 如果要添加的命令需要来自 amethyst/ 的逻辑,先单独提交完成抽取:识别类、列出其仅 Android 的依赖(Context、SharedPreferences、WorkManager、Log、Bitmap、Uri 等)、为每个依赖选择内联、通过 expect/actual 抽象平台或作为构造参数传入、将文件移到 commons/commonMain/…、更新 Android 调用方并添加 JVM 测试,然后再添加 cli/commands/… 文件。
标准命令结构与接线
每个新命令遵循相同形状:解析参数、打开 Context、准备、调用 commons/quartz、发布或排空、通过 Output.emit 发出一个结果。references/command-template.md 提供模板。接线清单:在 cli/commands/ 新建使用 object 模式的文件,子动词分发使用 Router.kt 中的共享 route(...) 辅助函数;在 Main.kt 的分发中添加分支;扩展 Main.kt 的 printUsage();在 cli/README.md 命令表中添加行;更新 cli/ROADMAP.md 状态。如果动词改变可观察的线上行为或 --json 输出形状,需相应更新测试与提交说明。
目录与共享逻辑
cli/ 下包含 README.md(用户导览)、DEVELOPMENT.md(公共契约、架构、设计规则、事件存储、中继路由、完整磁盘布局)、ROADMAP.md、plans/(日期化设计文档)、tests/(针对本地中继的端到端 shell 测试,含 lib.sh、headless/、marmot/、dm/、cache/)以及 src/main/kotlin/…/cli/ 下的 Main.kt、Args.kt、Output.kt、Aliases.kt、Config.kt、Context.kt、SecureFileIO.kt、stores/、secrets/、commands/ 等。共享逻辑位于 commons/:commons/account/、commons/marmot/、commons/cashu/、commons/relayManagement/Nip86Retriever、commons/defaults/。amy serve 依赖 :geode 模块,这是唯一允许的额外模块依赖;:amethyst / :desktopApp 被禁止依赖。
常见需拒绝的错误
在 cli/ 中添加协议逻辑;静默更改 --json 键;使用 println/print 输出命令结果;在命令内使用 runBlocking(顶层 main 已做,命令是 suspend fun);依赖 :amethyst 或 :desktopApp;重新发明标识符解析(应使用 Context.requireUserHex 或 resolveUserHexOrNull);重新发明发布确认(应使用 Context.publish);重新发明一次性订阅(应使用 Context.drain);直接读取 user.home(应使用读取 $HOME 的 DataDir.DEFAULT_ROOT);添加与子命令标志冲突的全局标志。
限制与说明
该技能面向 Amethyst 项目内部 CLI 开发,涉及具体模块(quartz、commons、geode)与文件路径约定,不适用于其他项目或通用 Nostr/Kotlin 开发。文档中提及的模板与参考文件(references/command-template.md、extraction-recipe.md、output-conventions.md)的具体内容暂未提供。