Imported from bubuding0809/banana-split-tma (
apps/cli/AGENTS.md). Install upstream withnpx skills add bubuding0809/banana-split-tma --skill cli. Copyright stays with the author.
@banananasplitz/cli — Agent Instructions
CLI scoped rules. The repo-level AGENTS.md at the root applies too. When they conflict, this file wins for anything under apps/cli/.
What this app is
Agent-first command-line interface for the Banana Split tRPC API. Ships as the npm package @banananasplitz/cli. Runtime surface is a single bundled file (dist/cli.js) produced by tsup. The package also ships a bundled Agent Skill (skills/banana-cli/SKILL.md) that teaches AI agents how to drive the CLI.
The skill-capabilities invariant
Whenever you change what the CLI can do, you MUST update the skill in the same PR.
The skill is not a README. It's a contract shipped to external AI agents: they read it, then shell out to banana <command>. A capability missing from the skill is invisible to every OpenCode/Claude Code/Cursor user who installed it. A stale skill actively causes wrong tool calls.
"Change what the CLI can do" means:
- Adding a command → add a table row in SKILL.md, add a workflow block if the command belongs to a multi-step pattern, add an entry in README.md for humans.
- Removing a command → remove the table row and any workflow block that references it.
- Renaming a flag or option → update every row/example/workflow that uses it.
- Changing required vs optional flags → update the "Key Flags" column.
- Changing output shape → update any workflow block whose bash comments show sample output.
- Adding a common pitfall you encountered → add an entry to "Common Mistakes".
If you're unsure whether a change touches capabilities, re-read SKILL.md and ask: "would an agent relying on this doc be wrong if it acted on my branch?" If yes, update it.
Versioning
Inline bumps only. Every PR that touches runtime CLI code (apps/cli/src/**, excluding *.test.ts / *.spec.ts) MUST do three things in the same commit:
- Bump
apps/cli/package.jsonversion. - Bump
apps/cli/skills/banana-cli/SKILL.mdfrontmatterversion:to match. - Add a CHANGELOG entry in
apps/cli/CHANGELOG.md.
CI enforces all three:
- PR check
cli-version-bumpfails if runtime source changed without a version bump. - The same job fails if
package.jsonandSKILL.mdfrontmatter versions don't match. - The same job fails if runtime source changed without
CHANGELOG.mdbeing updated.
Semver intent:
- Patch (0.x.N+1): bugfix, internal refactor, skill-copy tweak, dependency bump with no behavior change.
- Minor (0.x+1.0): new command, new flag, new output field (additive).
- Major (x+1.0.0): removed command, renamed flag, breaking output shape change. Reserved for coordinated releases — don't major-bump in a drive-by PR.
Writing CHANGELOG entries
Format: Keep a Changelog 1.1.0. Categories: Added / Changed / Deprecated / Removed / Fixed / Security.
Two flows, depending on whether you're bumping the version too:
- Bumping version in this PR (most feature PRs): rename the existing
## [Unreleased]section to## [X.Y.Z] - YYYY-MM-DD, add your entries under the appropriate category, then create a fresh empty## [Unreleased]section above it. Update the link-refs at the bottom. - Not bumping (e.g., stacked PR before a coordinated release): just append your entries under
## [Unreleased]. The release PR that bumps will do the rename.
Entry style:
- User-facing tone — consumers read this, not you.
- One bullet per concept, not one-per-commit.
- Link to the PR or issue when the reason matters (
([#123](https://.../pull/123))). - Omit internal refactors unless they affect observable behavior.
Release pipeline
Merging to main triggers .github/workflows/deploy.yml → publish-cli job. It:
- Runs only if
apps/cli/**orpackages/**changed. - Checks if
apps/cli/package.jsonversion already exists on npm; if yes, skips with a notice. - Otherwise publishes to npm via OIDC Trusted Publishing (no token in repo) with signed
--provenance.
There's no manual npm publish step. There's no npm token to rotate. Don't add one — if you see a PR adding NPM_TOKEN anywhere, push back.
Testing changes locally
For dev iteration, use the local build directly instead of npm install -g:
pnpm --filter @banananasplitz/cli run build
node apps/cli/dist/cli.js <command>
To test against prod API, have a user-level key saved (banana login --api-key <usk_...>) or set BANANA_SPLIT_API_KEY in your env. For local API, pass --api-url http://localhost:8081/api/trpc (matches the Lambda dev server port).
When you touch CLI commands, run the unit tests with the existing mock pattern in apps/cli/src/commands/chat.test.ts as reference. Mocked tRPC + assertions on query() / mutate() call arguments — don't make real network calls in tests.
File map
apps/cli/
├── src/
│ ├── cli.ts # Entry point; argv parsing, dispatch, global options
│ ├── client.ts # tRPC client factory
│ ├── config.ts # ~/.bananasplit.json read/write + resolution order
│ ├── output.ts # success() / error() / run() — JSON-to-stdout helpers
│ ├── scope.ts # resolveChatId for chat-scoped vs user-scoped keys
│ └── commands/
│ ├── types.ts # Command interface
│ ├── chat.ts # chat router commands
│ ├── expense.ts # expense router commands
│ ├── settlement.ts
│ ├── snapshot.ts
│ ├── currency.ts
│ ├── reminder.ts
│ ├── me.ts # user-level cross-chat commands (list-my-balances, list-my-spending)
│ └── *.test.ts # co-located unit tests
├── skills/banana-cli/
│ └── SKILL.md # Agent Skill spec — updated alongside every capability change
├── examples/ # Sample JSON inputs for bulk import etc.
├── package.json
├── README.md # Human-facing docs
└── AGENTS.md # (this file)
Adding a new command — checklist
- Create
src/commands/<name>.tsexporting aCommand[](or adding to an existing domain file). - Register the export in
src/cli.tsALL_COMMANDSarray. - Write
src/commands/<name>.test.tsmatching the pattern inchat.test.ts(mockoutput.js, assert dispatch args, assert validation errors). - Add a row to the Command Reference table in
skills/banana-cli/SKILL.md. - If the command belongs to a multi-step pattern, add a Workflow block in SKILL.md.
- Update README.md usage block + commands table.
- Bump
package.jsonversion andSKILL.mdfrontmatterversion:(match each other). - Add a
CHANGELOG.mdentry under the new version header (or## [Unreleased]if deferring the bump). - Run
pnpm --filter @banananasplitz/cli test+pnpm --filter @banananasplitz/cli check-types.
Common pitfalls for agents editing this app
- Forgetting the skill update. This is the single most common mistake. If you add a command and think "I'll document it later" — there is no later; the CI-enforced capability invariant is here precisely because human memory is unreliable.
- Bumping
package.jsonbut notSKILL.md. The parity check catches this, but you'll waste a CI cycle. Bump both in the same edit. - Assuming
npm install -g @banananasplitz/clireflects your local branch. It doesn't — npm has only the last published version. Usenode apps/cli/dist/cli.jsfor UAT of unpublished changes. - Using
console.logfor output. Always go throughsuccess()/error()fromoutput.jsso exit codes and stdout/stderr split are correct. - Swallowing tRPC errors silently. Wrap every tRPC call through
run()so errors get formatted as{error, message}JSON to stderr with exit 1. - Hardcoding user or chat IDs. These are always strings-of-bigints on the wire. Use the command's options and
resolveChatId()for chat lookup.