Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 14 additions & 10 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,34 @@ A thin Node (ESM, Node 20+) wrapper around the HEY CLI (`hey`, github.com/baseca

## Code map
- `src/hey-axi.js`: the bin. Answers `-v`/`-V`/`--version` from `src/version.js` (a leaf module) before importing anything else, then loads `src/cli.js`. Keep it that small.
- `src/version.js`: `VERSION`, kept equal to package.json by `test/axi.test.js`.
- `src/cli.js`: `main()`. It parses args, shows the home view (no args), handles `setup hooks`, resolves the command, validates flags (unknown → exit 2), applies policy, picks a run mode, spawns `hey`, and shapes the result.
- `src/shape.js`: default list fields per command (`LIST_FIELDS`, `DETAIL_FIELDS`), `--fields`, truncation, `empty`/`count`, breadcrumbs → `help`. Add a new list command's default fields here.
- `src/version.js`: `VERSION`, kept equal to package.json by `test/axi.test.js`. Record user-visible changes in `CHANGELOG.md`.
- `src/cli.js`: `main()`. It parses args, shows the home view (no args), handles `setup hooks`, `setup scope` and `hook session-end`, resolves the command (unknown → exit 2 with suggestions), validates flags and values, answers `--help` (`commandHelp`), checks arity, applies policy and content checks, picks a run mode, spawns `hey`, and shapes the result or error.
- `src/arity.js`: positional arity and one-of flag groups, from HEY's usage synopsis.
- `src/examples.js`: flag defaults and 2-3 examples per command for `--help`.
- `src/scope.js`: `.hey-axi.json` directory scope (`setup scope`, `findScope`).
- `src/activity.js`: session activity capture (ids and command names only) and `last_session`; enabled only by `setup hooks`.
- `src/shape.js`: default list fields per command (`LIST_FIELDS`, at most `MAX_LIST_FIELDS` = 4; `DETAIL_FIELDS`), `--fields`, truncation, `empty`/`count` (`listSize`), `--quiet`, breadcrumbs → `help`. Add a new list command's default fields here.
- `src/home.js`: the no-args home view (also the session hook's output).
- `src/guide.js`: static home-view text, shared with the generated block in `SKILL.md`.
- `src/hooks.js`: `hey-axi setup hooks` (Claude Code, Codex and OpenCode, via axi-sdk-js).
- `src/hooks.js`: `hey-axi setup hooks`: session-start (via axi-sdk-js) and session-end hooks for Claude Code, Codex and OpenCode.
- `src/text.js`: `heyToAxi()` (rewrites `hey <cmd>` suggestions to `hey-axi <cmd>`) and `shellWord()`.
- `src/router.js`: `normalizeCatalog`, `resolveCommand`, `listCommands`. Routing is **data-driven** from `src/manifest.json`; don't add per-command branches.
- `src/manifest.json`: generated snapshot of `hey commands --json` plus usage/examples parsed from `hey <command> --help`. **Never hand-edit it**; run `npm run refresh-manifest`.
- `src/args.js`: flag-aware scanning (which flags take a value), global and hey-axi flags, `stripAxiFlags`, `validateFlags`, raw output selectors.
- `src/policy.js`: run modes (interactive / stream / raw / json), the send gate, the secret gate, TTY requirements. New special cases go here.
- `src/errors.js`: `heyFailure()` maps a failed run to hey-axi's error shape and keeps HEY's `{ok:false,error,code,hint,meta}`.
- `src/policy.js`: run modes (interactive / stream / raw / json), the send gate, the secret gate, person-only commands (refused unless `--interactive`), content requirements, no-op detection (`noopFor`). New special cases go here.
- `src/errors.js`: `translateFailure()` maps a failed run to `{ok:false,error,kind,code,hint,meta,help}`, an exit code (0/1/2) and stderr warnings.
- `scripts/refresh-manifest.js`: regenerates the manifest.
- `skills/hey-axi/`: the shareable agent skill. `SKILL.md` is hand-written except its `generated:home` block. That block and `references/commands.md` are generated by `scripts/gen-skill.js` (`npm run skill:gen`); `npm run skill:check` runs in CI. `test/skill.test.js` checks the spec rules, that every example command resolves, and that the generated parts are current.
- `test/helpers.js`: `makeFakeHey` / `makeFakeHeyScript` (fake binaries that log their arguments) and `runAxi`.

## Rules
1. **Never run the real HEY CLI against a mailbox in tests, and never send email.** Every test uses the fake `hey` through `HEY_BIN`. The only real-`hey` calls anywhere are `hey version`, `hey commands` and `hey <command> --help` in `refresh-manifest`. Hook tests use a temporary `HOME`/cwd, never your real agent config.
2. Commands that deliver email must never send without `--allow-send`/`HEY_AXI_ALLOW_SEND=1`. Without it, `compose`/`reply` are auto-staged with `--draft` (and marked `sent: false`, `saved_as: draft`), and `forward`/`draft send`/`bulk-reply send` are refused (`src/policy.js`). Don't loosen this without the owner's sign-off.
3. Forward argv to HEY **in its original order**. Strip only hey-axi's own flags (`--allow-send`, `--allow-secret`, `--fields`, `--full`) and the `--json`/`--quiet` that hey-axi adds itself.
4. Output: TOON on stdout for both success and errors (`ok: false`). Default output is shaped (`src/shape.js`), and `--full` returns HEY's envelope untouched. Raw, stream and interactive modes hand stdout to HEY untouched. Suggestions name `hey-axi`, not `hey`.
5. Exit codes: pass HEY's through; hey-axi's own refusals (unknown command/flag/field, missing required flag, blocked send) use 2; a missing binary uses 127. The home view always exits 0 and reports problems as `status`.
7. Agent config is written only by the explicit `hey-axi setup hooks`. Nothing else may touch `~/.claude`, `~/.codex` or OpenCode plugins.
3. Forward argv to HEY **in its original order**. Strip only hey-axi's own flags (`--allow-send`, `--allow-secret`, `--interactive`, `--fields`, `--full`) and `--json`/`--quiet` (hey-axi adds `--json` itself and implements `--quiet`).
4. Output: TOON on stdout for both success and errors (`ok: false`). Default output is shaped (`src/shape.js`), and `--full` returns HEY's envelope untouched. Raw and stream modes relay HEY's stdout but report failures as structured errors. Never pass raw HEY stderr through. Suggestions name `hey-axi`, not `hey`, and HEY's breadcrumbs must stay.
5. Exit codes: 0 success (including no-ops), 1 failure (with `kind`), 2 usage error (hey-axi's refusals and HEY usage errors). The home view always exits 0 and reports problems as `status`. Nothing may wait for input: person-only commands are refused unless `--interactive` is passed.
6. When HEY adds commands, refresh the manifest, then run `npm run skill:gen`. Routing coverage tests (`test/router.test.js`, `test/routing.test.js`) check counts against the manifest, so update the expected numbers on purpose.
7. Agent config is written only by the explicit `hey-axi setup hooks`. Nothing else may touch `~/.claude`, `~/.codex` or OpenCode plugins. `.hey-axi.json` is written only by `hey-axi setup scope`.

## Workflow
- `npm ci && npm test`. Tests must pass before every commit; CI (`.github/workflows/test.yml`) runs them on Node 20 and 22.
Expand Down
41 changes: 41 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Changelog

All notable changes to hey-axi. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow [semver](https://semver.org/).

## 0.3.0 (2026-10-02)

Closes the gaps from the axi.md admission review (AXI principles 2-7, 9 and 10). The rules that were already in place stay as they were: nothing is sent without `--allow-send`, tests use a fake HEY, and HEY's next-command hints (breadcrumbs) are still shown.

### Added
- **Directory scope (principle 7):** `hey-axi setup scope --box|--label|--search [--account] [--limit]` writes `.hey-axi.json`. The home view in that directory tree shows the scoped mailbox view and carries `--account` into its suggestions. It has `--status` and `--remove`, and running it again is a no-op.
- **Session-end hooks (principle 7):** `hey-axi setup hooks` now also installs a session-end hook for Claude Code, Codex and OpenCode. It records a one-line summary of the session (drafts, sends, other changes; command names and numeric ids only), and the next home view shows it as `last_session` (one line, at most 300 characters). Entries are tagged with the agent's session id when the harness provides one, so concurrent sessions stay apart.
- **Counts everywhere (principles 4 and 9):** every list, including bare non-envelope lists, lists of plain values and results with several lists, gets a `count` (`N of T total`, `N total`, `N shown; more available`, or `N shown; no more pages reported`; hey-axi only says `total` when HEY does). Totals and cursors that HEY puts next to the list (box listings) are used too, results with several lists report HEY's total and paging, content next to a list is kept (truncated) rather than dropped, and neither the home view nor any list calls an empty page "nothing" when HEY reports more. The home view shows a thread count, a `--full` hint when it truncates, and how to see the rest of a truncated list.
- **Idempotent mutations (principle 6):** for state-setting commands, a HEY failure that says the command's own end state already holds (per-command patterns), or a delete whose target is already gone, gives `ok: true, noop: true` and exits 0. Reads, sends, generic conflicts, partial failures ("1 already seen; 2 failed"), `move` to a different destination than HEY names, "already exists" on creates, and deletes whose not-found names something other than the target stay errors.
- **Strict argument checks (principle 6):** missing or extra positional arguments, flags with a missing value, and wrongly typed values (for example `--limit abc`) all exit 2 before HEY runs. Unknown commands get "did you mean" suggestions.
- **Content-first writes (principle 6):** `compose`, `reply`, `draft edit`, `journal write`, `contact note set` and `bulk-reply send` must be given their content. `--message -` reads the body from stdin.
- **A next step on every list (principle 9):** when HEY's result has no breadcrumbs, hey-axi suggests the matching `view`/`show` command (or `--fields all`), and after an empty list the matching `create`/`add`.
- **Account carried into suggestions (principle 9):** HEY's next-command hints (object and string breadcrumbs, notices) and error fix-it lines keep `--account`/`--base-url` from the invocation.
- **Complete `--help` (principle 10):** per-command help lists arguments, every flag with its type and default, every global flag with its default, and 2-3 examples. The home view's `bin` is always an absolute path (with `~`). `hey-axi hook session-end --help` exists, and `setup scope --help` / `hey-axi --help` reject unknown flags (`hey-axi --help move` shows `move`'s help).
- `CHANGELOG.md`.

### Changed
- **Minimal default fields (principle 2):** list views show at most 4 columns.
- **Definitive empty states (principles 3 and 5):** bare lists get an explicit empty state, `--quiet` keeps it, long plain-text output is cut with a `--full` hint, and a command that returns no data prints `result: no data returned`, and non-JSON success output is wrapped as `ok: true` + `output`.
- **Structured errors and exit codes (principle 6):** errors have a `kind`; exit codes are now 0 = success, 1 = failure, 2 = usage error (HEY's 1-8 codes are no longer passed through, and `exit_code` was removed from error output). HEY's own error envelopes are translated too (one clean line; no stack traces; debug keys dropped from `meta`). `raw` and `stream` modes report failures as structured errors, and raw output is held until HEY succeeds, so a failed run prints only the error. Switches given a value (`--draft=false`, also on `setup hooks`/`setup scope`) and unknown letters in stacked shorthands (`-vZ`) are refused. A HEY killed by a signal is reported as a failure (exit 1). Refusals carry `--account`/`--base-url` into their suggestions too.
- **Never wait for input (principle 6):** `tui`, `mcp`, the `setup` wizard and browser `auth login` are refused (exit 2) unless the new `--interactive` opt-in is passed, terminal or not, with a hint for the alternative. Other `setup` commands and `upgrade` run captured, with stdin closed and `HEY_NONINTERACTIVE=1`.
- **Edge cases from the admission re-review:** "already done" matching for `label add/remove`, `collection add/remove`, set-aside and workflow commands checks the `--to`/`--from` container (a message naming a different label is still an error), and batch commands like `seen 1 2` are only a no-op when HEY's message covers every id. The next-page hint replaces an existing `--page`/`--all` instead of adding a second one. Scope values (search, label, box, account) are shell-quoted in suggestions. Plain-value lists nested in a container (`{items: [...], total_count}`) get counts and paging hints. `setup hooks --help` and `setup scope --help` show every flag's default. HEY's error line is cleaned further: runtime exceptions (`TypeError`, Go panics) fall back to a plain message for the error kind, network codes (`ECONNRESET`, `ETIMEDOUT`, `ENOTFOUND`, ...) are reworded, and library prefixes and raw URLs are removed.
- `--quiet` now means hey-axi drops HEY's summary, notice, breadcrumbs and meta, but keeps the data, count and hey-axi's hints. It is no longer passed to HEY.
- **`watch` speaks TOON (principle 1):** each event is printed as a TOON block followed by a blank line. **Breaking:** scripts that parse `hey-axi watch` as NDJSON need `--json`.
- **No runtime discovery:** unknown commands are rejected from the manifest snapshot alone, without running `hey commands`.
- `set-aside`, `set-aside group` and `skill` are runnable (154 runnable command paths).
- The manifest records every flag default and flag type.
- The agent skill and the home view suggest `npx -y hey-axi …` (principle 7: skill examples must run without a global install).
- Regenerated `docs/benchmarks.md` and `skills/hey-axi/references/commands.md`.

## 0.2.0

- Home view, `--version` fast path, minimal default fields, truncation, session-start hooks (Claude Code, Codex, OpenCode), unknown flags rejected up front, the agent skill, and npm packaging (MIT).

## Earlier

- Data-driven routing from `hey commands --json`, global flags anywhere, raw output selectors, the send gate (compose/reply staged as drafts), streaming `watch`, and HEY error parsing.
Loading
Loading