hey-axi 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +127 -0
- package/package.json +60 -0
- package/skills/hey-axi/SKILL.md +125 -0
- package/skills/hey-axi/references/commands.md +161 -0
- package/src/args.js +117 -0
- package/src/cli.js +345 -0
- package/src/errors.js +61 -0
- package/src/guide.js +28 -0
- package/src/hey-axi.js +10 -0
- package/src/home.js +50 -0
- package/src/hooks.js +93 -0
- package/src/manifest.json +3572 -0
- package/src/policy.js +91 -0
- package/src/router.js +82 -0
- package/src/shape.js +251 -0
- package/src/text.js +14 -0
- package/src/version.js +3 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 TangentialSolutions
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# hey-axi
|
|
2
|
+
|
|
3
|
+
An agent-friendly wrapper for [HEY CLI](https://github.com/basecamp/hey-cli) (`hey`), Basecamp's command line for HEY email, calendars, todos, habits, time tracking and journals.
|
|
4
|
+
|
|
5
|
+
hey-axi runs `hey`, asks for its JSON response envelope, and prints it as [TOON](https://github.com/toon-format/toon). It follows the [AXI](https://axi.md) principles: a live home view, minimal default fields, truncated long text, definitive empty states, and unknown flags rejected up front. On our synthetic benchmark that's **~89% fewer tokens than `hey --json`** ([docs/benchmarks.md](docs/benchmarks.md)). It also adds a few safety rails. It covers **every command in HEY CLI v1.7.0** (144 commands, plus the `login`/`logout` aliases and the `box <id>`-style shortcuts) by routing from HEY's own `hey commands --json` catalog.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
Requirements: Node 20+ and an authenticated HEY CLI (`curl -fsSL https://hey.com/install-cli | bash`, then `hey auth login`).
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm i -g hey-axi # puts `hey-axi` on your PATH
|
|
13
|
+
# or run it without installing:
|
|
14
|
+
npx -y hey-axi box view imbox
|
|
15
|
+
|
|
16
|
+
# agent skill (see "Agent integrations" below)
|
|
17
|
+
npx skills add TangentialSolutions/hey-axi
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
From source: `git clone https://github.com/TangentialSolutions/hey-axi && cd hey-axi && npm ci && npm link`.
|
|
21
|
+
|
|
22
|
+
hey-axi uses `$HEY_BIN` if it's set, and otherwise finds `hey` on `PATH`. launchd/cron jobs usually have a minimal PATH, so set `HEY_BIN=$HOME/.local/bin/hey` there.
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
hey-axi # home view: who/what, the Imbox (10 threads), next commands
|
|
28
|
+
hey-axi box view imbox # TOON: summary, a few columns per thread, notice, help
|
|
29
|
+
hey-axi box imbox # HEY's shortcut forms work too
|
|
30
|
+
hey-axi --account 2 search --from a@b.com # global flags anywhere
|
|
31
|
+
hey-axi thread read 123 # bodies cut at 1000 chars with "(truncated, N chars total)"
|
|
32
|
+
hey-axi thread read 123 --full # HEY's complete, untouched result
|
|
33
|
+
hey-axi box view imbox --fields id,subject,creator.email_address # choose columns (--fields all = every field)
|
|
34
|
+
hey-axi thread read 123 --json # the same shaped output as compact JSON
|
|
35
|
+
hey-axi box view imbox --quiet # data only, no envelope
|
|
36
|
+
hey-axi --version # bare hey-axi version (fast path); `hey-axi version` adds HEY's
|
|
37
|
+
hey-axi box view imbox --ids-only # raw HEY output (also --count/--markdown/--html/--styled/--jq)
|
|
38
|
+
hey-axi set-aside group view 4 --limit 5 # three-word commands
|
|
39
|
+
hey-axi move --help # usage, flags, examples, notes (no HEY call)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Output shaping
|
|
43
|
+
|
|
44
|
+
- **Home view.** `hey-axi` with no arguments prints `bin`, `description`, the Imbox summary and its 10 newest threads, and `help` lines with next commands. If HEY is missing, signed out or failing, it prints `status` with the fix and still exits 0.
|
|
45
|
+
- **Minimal fields.** List commands show a few columns. The defaults per command are in `src/shape.js` and in each command's `--help`. `--fields a,b,c` picks columns, using default aliases (`from`, `subject`, `at`) or dotted paths (`creator.email_address`, `messages.0.summary`); `--fields all` keeps everything. An unknown field exits 2 and lists what's available.
|
|
46
|
+
- **Truncation.** Strings longer than 1000 characters (120 in list cells) are cut and marked `… (truncated, N chars total)`, and a `help` line suggests `--full`.
|
|
47
|
+
- **Aggregates and empty states.** `count: N of T total` when HEY reports a total; ``empty: 0 results for `…` `` when a list is empty.
|
|
48
|
+
- **Help lines.** HEY's breadcrumbs become ``help: Run `hey-axi …` to …``, and HEY's hints are rewritten to name `hey-axi`.
|
|
49
|
+
- **Fail loud.** Unknown flags and missing required flags (`move` without `--to`) exit 2 before HEY runs, listing the valid flags.
|
|
50
|
+
- `--full` turns all of this off, and `--json` prints the same shaped result as compact JSON.
|
|
51
|
+
|
|
52
|
+
### How each kind of command runs
|
|
53
|
+
|
|
54
|
+
| Kind | Commands | Behavior |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| JSON (default) | almost everything | `hey … --json` → TOON (or compact JSON with `--json`) |
|
|
57
|
+
| Raw output | `--ids-only`, `--count`, `--markdown`, `--html`, `--styled`, `--jq`; `shell-completion generate`; `timetrack export` without `--output` | HEY's output is printed as-is |
|
|
58
|
+
| Stream | `watch` | NDJSON relayed line by line as events arrive; Ctrl-C/SIGTERM forwarded to HEY |
|
|
59
|
+
| Interactive | `tui`, `auth login`/`login`, `setup …`, `mcp`, `upgrade` | HEY gets the terminal (inherited stdio). `tui` refuses to start without a TTY |
|
|
60
|
+
|
|
61
|
+
### Safety rails
|
|
62
|
+
|
|
63
|
+
- **Nothing is sent without an opt-in** (`--allow-send` or `HEY_AXI_ALLOW_SEND=1`):
|
|
64
|
+
- `compose` and `reply` are **saved as drafts**. hey-axi adds `--draft`, and the output starts with `sent: false`, `saved_as: draft` and an `axi_notice` (also printed to stderr).
|
|
65
|
+
- `forward`, `draft send` and `bulk-reply send` have no draft mode in HEY, so they're refused (exit 2) with the safe alternative (`reply … --to`, `draft show`, `bulk-reply preview`).
|
|
66
|
+
- If you pass `--draft` or `--dry-run` yourself, hey-axi adds and marks nothing.
|
|
67
|
+
- **Credentials stay out of agent context.** `auth token` needs `--allow-secret` or `HEY_AXI_ALLOW_SECRETS=1`.
|
|
68
|
+
- hey-axi's own flags (`--allow-send`, `--allow-secret`, `--fields`, `--full`) are never forwarded to HEY.
|
|
69
|
+
|
|
70
|
+
### Errors and exit codes
|
|
71
|
+
|
|
72
|
+
Errors are printed as structured TOON: `ok: false`, `error`, and, when HEY gave them, `code`, `hint`, `meta`, plus a `help` next step (when HEY gave no hint) and `exit_code`. hey-axi passes HEY's exit code through: 1 usage, 2 not found, 3 auth, 4 forbidden, 5 rate limit, 6 network, 7 API, 8 ambiguous (`hey help exit-codes`). hey-axi's own refusals (unknown/incomplete command, unknown flag or field, missing required flag, blocked send, missing TTY) exit **2**. If `hey` can't be found, hey-axi exits **127**.
|
|
73
|
+
|
|
74
|
+
## Agent integrations: session hook or skill
|
|
75
|
+
|
|
76
|
+
There are two ways to give an agent hey-axi. You only need one:
|
|
77
|
+
|
|
78
|
+
1. **Session hook (recommended where supported):** `hey-axi setup hooks` registers the home view as session-start context for **Claude Code** (`~/.claude/settings.json`), **Codex** (`~/.codex/hooks.json`, and it turns on `[features].hooks` in `~/.codex/config.toml`) and **OpenCode** (a managed plugin in `~/.config/opencode/plugins/`). Every new session then starts with your Imbox summary and the next commands, and no tool call is needed.
|
|
79
|
+
- `--project` writes to the current directory's `.claude/`, `.codex/` and `.opencode/` instead.
|
|
80
|
+
- `--status` reports what's installed, and `--remove` removes only hey-axi's entries.
|
|
81
|
+
- Re-running is a no-op, or repairs the path if hey-axi moved. It uses [axi-sdk-js](https://www.npmjs.com/package/axi-sdk-js)'s installer.
|
|
82
|
+
- Note that the hook puts Imbox senders and subjects into every session's context.
|
|
83
|
+
2. **Agent skill (broader support, loads on demand):** see below.
|
|
84
|
+
|
|
85
|
+
### Agent skill
|
|
86
|
+
|
|
87
|
+
[`skills/hey-axi/SKILL.md`](skills/hey-axi/SKILL.md) is an [Agent Skills](https://agentskills.io/specification) package. It tells coding agents (Claude Code, Codex, Cursor, Gemini CLI, Copilot, …) when and how to use hey-axi: setup checks, triage commands, which id goes where, drafts-by-default, output modes, `watch` and exit codes. [`references/commands.md`](skills/hey-axi/references/commands.md) lists every command and is generated from the manifest (`npm run skill:gen`).
|
|
88
|
+
|
|
89
|
+
Installing the skill doesn't install the CLI. Install `hey` and `hey-axi` first (see [Install](#install)).
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# with the skills CLI (https://skills.sh), from GitHub
|
|
93
|
+
npx skills add TangentialSolutions/hey-axi # this project
|
|
94
|
+
npx skills add TangentialSolutions/hey-axi -g -a claude-code # user-wide, Claude Code only
|
|
95
|
+
|
|
96
|
+
# with GitHub CLI (gh skill, preview)
|
|
97
|
+
gh skill install TangentialSolutions/hey-axi hey-axi --agent claude-code --scope user
|
|
98
|
+
|
|
99
|
+
# manually
|
|
100
|
+
cp -r skills/hey-axi ~/.claude/skills/ # or ~/.agents/skills/, ~/.codex/skills/, …
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
See [docs/listing.md](docs/listing.md) for how hey-axi gets listed on skills.sh and axi.md.
|
|
104
|
+
|
|
105
|
+
## Keeping up with HEY releases
|
|
106
|
+
|
|
107
|
+
`src/manifest.json` is a snapshot of `hey commands --json`. To regenerate it from whichever `hey` you have installed:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
npm run refresh-manifest # runs only `hey version` and `hey commands`
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
If the snapshot doesn't know a command, hey-axi asks the installed `hey commands --json` at runtime before reporting `unknown command`. So newer HEY releases work even before the snapshot is refreshed. A weekly GitHub Action (`manifest-drift`) fails when the snapshot falls behind the latest HEY release.
|
|
114
|
+
|
|
115
|
+
## Development
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
npm test # node:test; every test uses a fake `hey` (test/helpers.js), never the real CLI
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Token benchmark (HEY CLI vs hey-axi output): [docs/benchmarks.md](docs/benchmarks.md), with `npm run bench:tokens`.
|
|
122
|
+
|
|
123
|
+
See [AGENTS.md](AGENTS.md) for the code map and conventions, and [SCHEDULED_HEY_CLI.md](SCHEDULED_HEY_CLI.md) for running triage on a schedule.
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
[MIT](LICENSE) © 2026 TangentialSolutions. hey-axi is an independent project. It isn't affiliated with or endorsed by Basecamp or HEY.
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hey-axi",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Agent-ergonomic AXI wrapper for Basecamp's HEY CLI: TOON output, send-to-draft safety, and a shareable agent skill",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"axi",
|
|
7
|
+
"agent",
|
|
8
|
+
"ai-agent",
|
|
9
|
+
"agent-skills",
|
|
10
|
+
"skill",
|
|
11
|
+
"cli",
|
|
12
|
+
"hey",
|
|
13
|
+
"hey.com",
|
|
14
|
+
"email",
|
|
15
|
+
"basecamp",
|
|
16
|
+
"toon",
|
|
17
|
+
"llm",
|
|
18
|
+
"claude-code",
|
|
19
|
+
"codex"
|
|
20
|
+
],
|
|
21
|
+
"homepage": "https://github.com/TangentialSolutions/hey-axi#readme",
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/TangentialSolutions/hey-axi/issues"
|
|
24
|
+
},
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/TangentialSolutions/hey-axi.git"
|
|
28
|
+
},
|
|
29
|
+
"author": "Trevor Broaddus",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"type": "module",
|
|
32
|
+
"bin": {
|
|
33
|
+
"hey-axi": "src/hey-axi.js"
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"src",
|
|
37
|
+
"skills",
|
|
38
|
+
"README.md",
|
|
39
|
+
"LICENSE"
|
|
40
|
+
],
|
|
41
|
+
"scripts": {
|
|
42
|
+
"test": "node --test test/*.test.js",
|
|
43
|
+
"refresh-manifest": "node scripts/refresh-manifest.js",
|
|
44
|
+
"bench:tokens": "node bench/tokens.mjs",
|
|
45
|
+
"bench:capture": "node bench/capture.mjs",
|
|
46
|
+
"bench:fixtures": "node bench/generate-api-fixtures.mjs",
|
|
47
|
+
"skill:gen": "node scripts/gen-skill.js",
|
|
48
|
+
"skill:check": "node scripts/gen-skill.js --check"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@toon-format/toon": "^4.1.1",
|
|
52
|
+
"axi-sdk-js": "^0.1.12"
|
|
53
|
+
},
|
|
54
|
+
"engines": {
|
|
55
|
+
"node": ">=20"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"js-tiktoken": "^1.0.21"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: hey-axi
|
|
3
|
+
description: Read, triage and draft HEY email (and HEY calendars, todos, habits, journal, time tracking) from the shell through hey-axi, a token-efficient TOON wrapper around Basecamp's HEY CLI that saves outgoing mail as drafts unless sending is explicitly allowed. Use when the user asks you to check, search, read, summarize, sort, label, screen, archive or reply to their HEY (hey.com) email, watch for new mail, or manage HEY todos, events, habits, journal entries or time tracks.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Requires Node.js 20+, the HEY CLI (`hey`, v1.7.0 or newer) signed in to a HEY account, and network access to app.hey.com.
|
|
6
|
+
metadata:
|
|
7
|
+
author: TangentialSolutions
|
|
8
|
+
repository: https://github.com/TangentialSolutions/hey-axi
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# hey-axi: HEY email for agents
|
|
12
|
+
|
|
13
|
+
`hey-axi` runs Basecamp's HEY CLI (`hey`) and prints compact TOON. Lists show a few columns, long text is cut (the total size is shown), and suggestions name `hey-axi` commands. Every `hey` command works: `hey-axi <same args as hey>`.
|
|
14
|
+
|
|
15
|
+
## 1. Start here
|
|
16
|
+
|
|
17
|
+
Run `hey-axi` with no arguments. It shows the Imbox and what to do next. If HEY is missing or signed out, it prints a `status` line and the fix instead.
|
|
18
|
+
|
|
19
|
+
<!-- generated:home:start -->
|
|
20
|
+
<!-- Generated by scripts/gen-skill.js from src/guide.js, the same text the home view prints. Do not edit by hand. -->
|
|
21
|
+
What hey-axi is: HEY email, calendars and todos for agents: compact TOON over Basecamp's HEY CLI; outgoing mail is saved as a draft unless --allow-send
|
|
22
|
+
|
|
23
|
+
Next steps the home view suggests:
|
|
24
|
+
- Run `hey-axi thread read <topic_id>` to read a thread
|
|
25
|
+
- Run `hey-axi box view imbox --limit 50` for more (also feed, papertrail, aside, later)
|
|
26
|
+
- Run `hey-axi search "<query>"` to search mail
|
|
27
|
+
- Run `hey-axi screener list` for senders waiting to be screened
|
|
28
|
+
- Run `hey-axi reply <topic_id> --message "..."` to draft a reply (not sent without --allow-send)
|
|
29
|
+
- Run `hey-axi --help` for all commands; lists take --fields a,b / --fields all, long text --full
|
|
30
|
+
<!-- generated:home:end -->
|
|
31
|
+
|
|
32
|
+
Problems:
|
|
33
|
+
- `hey-axi: command not found`: run it as `npx -y hey-axi …`, or install it (see "Install" below).
|
|
34
|
+
- `status: HEY CLI not found` / exit **127**: install `hey`, or set `HEY_BIN=/path/to/hey` (cron/launchd jobs usually need `HEY_BIN=$HOME/.local/bin/hey`).
|
|
35
|
+
- `status: not signed in` / exit **3**: ask the **user** to run `hey auth login` in their own terminal. It opens a browser. Don't try to sign in for them, and never print tokens (`auth token` is blocked unless `--allow-secret` is passed).
|
|
36
|
+
- `hey-axi doctor` finds other login and config problems. `hey-axi version` shows both versions.
|
|
37
|
+
|
|
38
|
+
## 2. Read and triage
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
hey-axi box list # boxes and their ids
|
|
42
|
+
hey-axi box view imbox --limit 25 # rows in a box (imbox, feed, papertrail, aside, later, …)
|
|
43
|
+
hey-axi thread read <topic_id> # the thread (long bodies cut; --full for all)
|
|
44
|
+
hey-axi search "invoice" --from a@b.com --date last_30_days
|
|
45
|
+
hey-axi screener list # senders waiting to be screened
|
|
46
|
+
hey-axi set-aside view | hey-axi bubble list | hey-axi draft list
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Which id to use.** Each `box view` row has an `id` (the box item) and a `topic_id` (the thread):
|
|
50
|
+
- `topic_id` goes to `thread read`, `reply` and `forward`.
|
|
51
|
+
- `id` goes to `seen`, `unseen`, `move`, `label`, `bubble`, `trash`, `spam` and `ignore`.
|
|
52
|
+
- A row with kind `bundle` has no `topic_id`. Open it with `hey-axi bundle view <id>`.
|
|
53
|
+
- Screener commands take the clearance `id` from `screener list`. These aren't contact ids.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
hey-axi seen <id>... hey-axi unseen <id>...
|
|
57
|
+
hey-axi move <id>... --to feed # imbox | feed | aside | later | papertrail
|
|
58
|
+
hey-axi label add <id>... --to "Receipts"
|
|
59
|
+
hey-axi bubble up <id>... --tomorrow # or --now | --on <date> | --weekend | --next-week
|
|
60
|
+
hey-axi screener approve <clearance-id> hey-axi screener deny <clearance-id>
|
|
61
|
+
hey-axi trash <id>... hey-axi spam <id>... hey-axi ignore <id>...
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`trash`, `spam`, `ignore`, `screener deny --spam` and `screener clear` (which clears the whole queue) run without any confirmation. Confirm with the user before you run them in bulk.
|
|
65
|
+
|
|
66
|
+
Output includes HEY's `next`/breadcrumb hints. Follow them for paging (`--page N`, `--all`) and follow-up commands instead of guessing. `hey <command> --help` explains a command in depth.
|
|
67
|
+
|
|
68
|
+
## 3. Writing mail: drafts by default
|
|
69
|
+
|
|
70
|
+
- `compose` and `reply` are **saved as drafts** automatically. The output starts with `sent: false`, `saved_as: draft` and an `axi_notice`. Tell the user that a draft is waiting in HEY. Don't claim it was sent.
|
|
71
|
+
```bash
|
|
72
|
+
hey-axi reply <topic_id> --message "Thanks, Thursday works."
|
|
73
|
+
hey-axi compose --to a@b.com --subject "Hi" --message "…"
|
|
74
|
+
hey-axi draft list | hey-axi draft show <draft-id>
|
|
75
|
+
```
|
|
76
|
+
- `forward`, `draft send` and `bulk-reply send` are **refused** (exit 2) because HEY has no draft mode for them. The error names the safe alternative.
|
|
77
|
+
- To actually send, the user must explicitly ask you to, and you add `--allow-send` (or they set `HEY_AXI_ALLOW_SEND=1`). Don't add it on your own initiative.
|
|
78
|
+
|
|
79
|
+
## 4. Output: fields, truncation, modes
|
|
80
|
+
|
|
81
|
+
- **Lists show a few columns** (for example `box view`: `id, topic_id, from, subject, seen, at`). Pick others with `--fields id,subject,creator.email_address`; aliases and dotted paths both work. `--fields all` keeps every field. An unknown field fails with exit 2 and lists the ones available. `hey-axi <command> --help` shows the defaults.
|
|
82
|
+
- **Long text is cut** at 1000 characters in detail views (thread bodies, drafts) and 120 in list cells, marked `… (truncated, N chars total)`. The output then includes a `Run … --full` help line. Use `--full` only when you need the whole text.
|
|
83
|
+
- Lists say `empty: 0 results for …` when nothing matched, and `count: N of T total` when HEY knows the total. Follow `notice`/`help` for paging (`--page`, `--all`).
|
|
84
|
+
|
|
85
|
+
| Want | Use |
|
|
86
|
+
|---|---|
|
|
87
|
+
| Default: summary, data, help lines, as TOON | (nothing) |
|
|
88
|
+
| The same as compact JSON | `--json` |
|
|
89
|
+
| Data only | `--quiet` |
|
|
90
|
+
| HEY's complete, untouched result | `--full` (combine with `--json` for raw JSON) |
|
|
91
|
+
| Raw HEY output | `--ids-only`, `--count`, `--markdown`, `--html`, `--styled`, `--jq '<expr>'` |
|
|
92
|
+
| Help for one command: usage, flags, examples, notes (no network) | `hey-axi <command> --help` |
|
|
93
|
+
|
|
94
|
+
Unknown flags are rejected before anything runs (exit 2, with the valid flags listed), and so are missing required flags such as `move --to`. Global flags like `--account <id|all>` can go anywhere.
|
|
95
|
+
|
|
96
|
+
## 5. Watching for changes
|
|
97
|
+
|
|
98
|
+
`hey-axi watch` streams HEY's NDJSON one line at a time until it's interrupted. Use bounded forms in agent sessions:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
hey-axi watch --box imbox --events new --exit-on-first --timeout 10m
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Lines also include `ready`, `disconnected` and `resync`. On `resync`, re-read that box.
|
|
105
|
+
|
|
106
|
+
## 6. Errors and exit codes
|
|
107
|
+
|
|
108
|
+
Errors are TOON on stdout: `ok: false`, `error`, plus `code`/`hint`/`meta` when HEY provided them, a `help` line with the next step, and `exit_code`. Exit codes: 0 ok · 1 usage · 2 not found, or a hey-axi refusal (unknown flag or field, blocked send) · 3 auth · 4 forbidden · 5 rate limit · 6 network · 7 API · 8 ambiguous · 127 `hey` missing. Read `hint`/`help` before you retry.
|
|
109
|
+
|
|
110
|
+
## Install
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# HEY CLI (macOS/Linux)
|
|
114
|
+
curl -fsSL https://hey.com/install-cli | bash # or: brew install --cask basecamp/tap/hey
|
|
115
|
+
hey auth login # the user runs this once, interactively
|
|
116
|
+
|
|
117
|
+
# hey-axi
|
|
118
|
+
npm i -g hey-axi # puts `hey-axi` on PATH
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
No global install? Prefix any command with `npx -y`, e.g. `npx -y hey-axi box view imbox`. It's slower on the first run, so install it if you'll run many commands.
|
|
122
|
+
|
|
123
|
+
Optional: `hey-axi setup hooks` (user-invoked) shows the home view at the start of every Claude Code, Codex and OpenCode session. `--project` limits it to one directory, and `--remove` undoes it.
|
|
124
|
+
|
|
125
|
+
More commands (calendar, todos, habits, journal, time tracking, contacts, collections), with their default fields: [references/commands.md](references/commands.md).
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# hey-axi command reference
|
|
2
|
+
|
|
3
|
+
<!-- Generated by scripts/gen-skill.js from src/manifest.json (hey 1.7.0). Do not edit by hand. -->
|
|
4
|
+
|
|
5
|
+
Every command from HEY CLI 1.7.0 (151 runnable paths, including the `login`/`logout` aliases and shortcut forms such as `box <name>`). Run each as `hey-axi <command> [args] [flags]`.
|
|
6
|
+
`hey-axi <command> --help` shows usage, flag descriptions, examples and notes, offline.
|
|
7
|
+
Flags every command accepts: `--account <id|all>`, `--json`, `--quiet`, `--fields <a,b|all>`, `--full`, `--ids-only`, `--count`, `--markdown`, `--html`, `--styled`, `--jq <expr>`. Anything else unknown is rejected (exit 2).
|
|
8
|
+
|
|
9
|
+
| Command | What it does | Flags | hey-axi behavior |
|
|
10
|
+
|---|---|---|---|
|
|
11
|
+
| `account list` | List linked mail accounts | | fields: id, email, name, active |
|
|
12
|
+
| `account senders` | List configured sender addresses | | |
|
|
13
|
+
| `account use` | Set the default linked mail account | | |
|
|
14
|
+
| `attachment list` | List a thread's attachments | `--allow-partial` | fields: id, filename, content_type, byte_size |
|
|
15
|
+
| `attachment save` | Save an attachment to disk | `--force` `--output <v>` | |
|
|
16
|
+
| `auth login` | Authenticate with the HEY server | `--cookie <v>` `--no-browser` `--token <v>` | interactive (user runs it) |
|
|
17
|
+
| `auth logout` | Clear stored credentials | | |
|
|
18
|
+
| `auth refresh` | Force token refresh | | |
|
|
19
|
+
| `auth status` | Show authentication status | | |
|
|
20
|
+
| `auth token` | Print access token to stdout | `--stored` | refused unless `--allow-secret` |
|
|
21
|
+
| `box` | List HEY boxes and their email threads | `--all` `--limit <v>` `--page <v>` | fields: id, topic_id, from, subject, seen, at |
|
|
22
|
+
| `box list` | List your HEY boxes | `--all` `--limit <v>` | fields: id, kind, name |
|
|
23
|
+
| `box view` | List email threads in a box | `--all` `--limit <v>` `--page <v>` | fields: id, topic_id, from, subject, seen, at |
|
|
24
|
+
| `bubble list` | List bubbled-up and scheduled email threads | `--all` `--limit <v>` | fields: id, topic_id, from, subject, seen, at, bubbles_up |
|
|
25
|
+
| `bubble pop` | Cancel bubble-ups | | |
|
|
26
|
+
| `bubble up` | Bubble email threads up | `--next-week` `--now` `--on <v>` `--tomorrow` `--weekend` | |
|
|
27
|
+
| `bulk-reply preview` | Preview threads and recipients without sending | | |
|
|
28
|
+
| `bulk-reply send` | Send one reply to multiple threads | `--attach <v>` `--message <v>` `--message-html <v>` | refused unless `--allow-send` |
|
|
29
|
+
| `bulk-reply undo` | Recall a delayed bulk reply | | |
|
|
30
|
+
| `bundle` | List the unseen threads a bundle groups | `--all` `--limit <v>` `--page <v>` | fields: id, topic_id, from, subject, seen, at |
|
|
31
|
+
| `bundle view` | List the unseen threads a bundle groups | `--all` `--limit <v>` `--page <v>` | fields: id, topic_id, from, subject, seen, at |
|
|
32
|
+
| `calendar list` | List calendars | | fields: id, name, kind, owned |
|
|
33
|
+
| `clip create` | Save text from an email entry | `--content <v>` | |
|
|
34
|
+
| `clip delete` | Delete a saved clip | | |
|
|
35
|
+
| `clip list` | List the newest page of passages clipped from email | | fields: id, content, topic_id, at |
|
|
36
|
+
| `collection` | List and manage email collections | `--all` `--limit <v>` `--page <v>` | fields: id, topic_id, from, subject, seen, at |
|
|
37
|
+
| `collection add` | Add email threads to a collection | `--to <v>` | |
|
|
38
|
+
| `collection create` | Create a collection | `--summary <v>` | |
|
|
39
|
+
| `collection list` | List your email collections | `--all` `--limit <v>` | fields: id, name |
|
|
40
|
+
| `collection remove` | Remove email threads from a collection | `--from <v>` | |
|
|
41
|
+
| `collection update` | Update a collection | `--name <v>` `--summary <v>` | |
|
|
42
|
+
| `collection view` | List email threads in a collection | `--all` `--limit <v>` `--page <v>` | fields: id, topic_id, from, subject, seen, at |
|
|
43
|
+
| `commands` | List all available commands | | |
|
|
44
|
+
| `compose` | Write and send a new email | `--attach <v>` `--bcc <v>` `--cc <v>` `--draft` `--from <v>` `--message <v>` `--message-html <v>` `--no-name-tag` `--subject <v>` `--thread-id <v>` `--to <v>` | saved as a draft unless `--allow-send` |
|
|
45
|
+
| `config set` | Set a configuration value in the global config | | |
|
|
46
|
+
| `config show` | Show current configuration with sources | | |
|
|
47
|
+
| `config trust-local` | Trust this repository's local HEY settings | | |
|
|
48
|
+
| `config trusted-locals` | List trusted repository-local HEY settings | | |
|
|
49
|
+
| `config untrust-local` | Remove trust for this repository's local HEY settings | | |
|
|
50
|
+
| `contact add` | Add a contact | `--account-user-id <v>` `--alias <v>` `--email <v>` `--name <v>` | |
|
|
51
|
+
| `contact bundle` | Bundle a contact's mail | | |
|
|
52
|
+
| `contact hide` | Hide a contact | | |
|
|
53
|
+
| `contact list` | List contacts | `--all` `--page <v>` | fields: id, name, email_address |
|
|
54
|
+
| `contact note delete` | Delete a private contact note | | |
|
|
55
|
+
| `contact note set` | Write or edit a private contact note | `--note <v>` `--note-html <v>` | |
|
|
56
|
+
| `contact note show` | Read a private contact note | | |
|
|
57
|
+
| `contact show` | View a contact | | |
|
|
58
|
+
| `contact show-again` | Show a hidden contact again | | |
|
|
59
|
+
| `contact threads` | List every thread with a contact | `--all` `--limit <v>` `--page <v>` | fields: id, topic_id, from, subject, seen, at |
|
|
60
|
+
| `contact unbundle` | List a contact's mail separately | | |
|
|
61
|
+
| `contact update` | Edit a contact | `--alias <v>` `--email <v>` `--name <v>` | |
|
|
62
|
+
| `doctor` | Find login and configuration problems | | |
|
|
63
|
+
| `draft delete` | Trash drafts | | |
|
|
64
|
+
| `draft edit` | Change a draft | `--bcc <v>` `--cc <v>` `--from <v>` `--message <v>` `--message-html <v>` `--subject <v>` `--to <v>` | |
|
|
65
|
+
| `draft list` | List draft emails | `--all` `--limit <v>` `--page <v>` | fields: id, subject, summary, at |
|
|
66
|
+
| `draft send` | Deliver a draft | | refused unless `--allow-send` |
|
|
67
|
+
| `draft show` | Read a draft back | | |
|
|
68
|
+
| `event add` | Create an event | `--all-day` `--calendar <v>` `--circle` `--countdown <v>` `--countdown-unit <v>` `--end-time <v>` `--ends-on <v>` `--invite <v>` `--link <v>` `--location <v>` `--notes <v>` `--remind <v>` `--repeat <v>` `--repeat-times <v>` `--repeat-until <v>` `--start-time <v>` `--starts-on <v>` `--time-zone <v>` `--title <v>` | |
|
|
69
|
+
| `event day` | List the events of one day, as HEY draws it | `--all` `--limit <v>` | fields: id, title, starts_at, ends_at, calendar |
|
|
70
|
+
| `event delete` | Delete an event | | |
|
|
71
|
+
| `event edit` | Change an event | `--all-day` `--allow-plain-notes` `--apply-to <v>` `--calendar <v>` `--circle` `--countdown <v>` `--countdown-unit <v>` `--end-time <v>` `--ends-on <v>` `--invite <v>` `--link <v>` `--location <v>` `--notes <v>` `--occurrence <v>` `--remind <v>` `--repeat <v>` `--repeat-times <v>` `--repeat-until <v>` `--start-time <v>` `--starts-on <v>` `--time-zone <v>` `--title <v>` | |
|
|
72
|
+
| `event list` | List calendar events | `--all` `--calendar <v>` `--ends-on <v>` `--limit <v>` `--starts-on <v>` | fields: id, title, starts_at, ends_at, calendar |
|
|
73
|
+
| `event week` | List the events of one week, as HEY draws it | `--all` `--limit <v>` | fields: id, title, starts_at, ends_at, calendar |
|
|
74
|
+
| `forward` | Forward the latest message in a thread | `--bcc <v>` `--cc <v>` `--message <v>` `--message-html <v>` `--to <v>` | refused unless `--allow-send` |
|
|
75
|
+
| `habit complete` | Mark a habit as complete for a date | `--date <v>` | |
|
|
76
|
+
| `habit create` | Create a habit | `--color <v>` `--days <v>` `--icon <v>` `--name <v>` | |
|
|
77
|
+
| `habit delete` | Delete a habit and its history | | |
|
|
78
|
+
| `habit edit` | Edit a habit | `--color <v>` `--days <v>` `--icon <v>` `--name <v>` | |
|
|
79
|
+
| `habit list` | List habits | `--all` `--date <v>` `--limit <v>` | fields: id, title, days |
|
|
80
|
+
| `habit uncomplete` | Remove a habit completion for a date | `--date <v>` | |
|
|
81
|
+
| `ignore` | Ignore email threads | | |
|
|
82
|
+
| `journal list` | List journal entries | `--all` `--calendar <v>` `--ends-on <v>` `--limit <v>` `--starts-on <v>` | fields: id, starts_at, content |
|
|
83
|
+
| `journal read` | Read a journal entry (default: today) | | |
|
|
84
|
+
| `journal write` | Write or edit a journal entry (default: today) | `--content <v>` `--content-html <v>` | |
|
|
85
|
+
| `label` | List and manage email labels | `--all` `--limit <v>` `--page <v>` | fields: id, topic_id, from, subject, seen, at |
|
|
86
|
+
| `label add` | Add a label to email threads | `--to <v>` | |
|
|
87
|
+
| `label create` | Create a label and add it to email threads | | |
|
|
88
|
+
| `label list` | List your email labels | `--all` `--limit <v>` | fields: id, name |
|
|
89
|
+
| `label remove` | Remove labels from email threads | `--from <v>` | |
|
|
90
|
+
| `label view` | List email threads with a label | `--all` `--limit <v>` `--page <v>` | fields: id, topic_id, from, subject, seen, at |
|
|
91
|
+
| `login` | Authenticate with the HEY server | `--cookie <v>` `--no-browser` `--token <v>` | interactive (user runs it) |
|
|
92
|
+
| `logout` | Clear stored credentials | | |
|
|
93
|
+
| `mcp` | Serve HEY to MCP clients over stdio | `--domains <v>` `--read-only` | interactive (user runs it) |
|
|
94
|
+
| `move` | Move email threads to another box | `--to <v>` | |
|
|
95
|
+
| `reply` | Reply to a thread | `--attach <v>` `--bcc <v>` `--cc <v>` `--draft` `--dry-run` `--message <v>` `--message-html <v>` `--replace-recipients` `--to <v>` | saved as a draft unless `--allow-send` |
|
|
96
|
+
| `screener approve` | Let a sender through | `--box <v>` `--seen` | |
|
|
97
|
+
| `screener clear` | Empty the Screener without deciding | | |
|
|
98
|
+
| `screener deny` | Turn a sender away | `--spam` | |
|
|
99
|
+
| `screener history` | List the senders already screened | `--all` `--page <v>` | fields: id, status, name, email_address, decided |
|
|
100
|
+
| `screener list` | List the senders waiting to be screened | `--all` `--page <v>` | fields: id, name, email_address, subject, topic_id |
|
|
101
|
+
| `search` | Search email threads and messages | `--all` `--any <v>` `--attachment <v>` `--date <v>` `--exact <v>` `--from <v>` `--in <v>` `--label <v>` `--none <v>` `--page <v>` `--required <v>` `--subject <v>` `--to <v>` | fields: id, topic_id, subject, from, at |
|
|
102
|
+
| `search filters` | List available search refinement values | | |
|
|
103
|
+
| `seen` | Mark email threads as seen | | |
|
|
104
|
+
| `set-aside group add` | Add email threads to a Set Aside group | `--to <v>` | |
|
|
105
|
+
| `set-aside group create` | Gather email threads into a new Set Aside group | | |
|
|
106
|
+
| `set-aside group delete` | Break up a Set Aside group | | |
|
|
107
|
+
| `set-aside group list` | List the groups in Set Aside | | fields: id, thread_count |
|
|
108
|
+
| `set-aside group remove` | Take email threads out of their Set Aside group | | |
|
|
109
|
+
| `set-aside group view` | List email threads in a Set Aside group | `--all` `--limit <v>` `--page <v>` | fields: id, topic_id, from, subject, seen, at |
|
|
110
|
+
| `set-aside view` | List email threads in Set Aside | `--all` `--limit <v>` `--page <v>` | fields: id, topic_id, from, subject, seen, at |
|
|
111
|
+
| `setup` | Set up HEY for first use | `--silent-success` `--skip-agents` `--skip-omarchy` | interactive (user runs it) |
|
|
112
|
+
| `setup agents` | Install or remove HEY coding-agent integrations | `--remove` | interactive (user runs it) |
|
|
113
|
+
| `setup claude` | Connect Claude Code to HEY | | interactive (user runs it) |
|
|
114
|
+
| `setup codex` | Connect Codex to HEY | | interactive (user runs it) |
|
|
115
|
+
| `setup omarchy` | Install hey into the Omarchy desktop | `--no-notify` `--notify` `--remove` | interactive (user runs it) |
|
|
116
|
+
| `share` | Get a sharing link for an email thread | | |
|
|
117
|
+
| `shell-completion generate` | Print a completion script to stdout | | raw output |
|
|
118
|
+
| `shell-completion install` | Install shell completions where your shell will find them | `--force` | |
|
|
119
|
+
| `skill install` | Install the hey skill globally for your coding agents | | |
|
|
120
|
+
| `snippet create` | Create a reusable email snippet | `--content <v>` `--content-html <v>` `--name <v>` | |
|
|
121
|
+
| `snippet delete` | Delete a reusable email snippet | | |
|
|
122
|
+
| `snippet list` | List reusable email snippets | | fields: id, name, content |
|
|
123
|
+
| `snippet update` | Update a reusable email snippet | `--content <v>` `--content-html <v>` `--name <v>` | |
|
|
124
|
+
| `spam` | Mark email threads as spam | | |
|
|
125
|
+
| `stop-ignoring` | Stop ignoring email threads | | |
|
|
126
|
+
| `thread read` | Read a thread | `--allow-partial` | fields: id, at, from, email, to, cc, body |
|
|
127
|
+
| `timetrack categories` | List time track categories | | fields: id, title |
|
|
128
|
+
| `timetrack category create` | Create a time track category | | |
|
|
129
|
+
| `timetrack category delete` | Delete a time track category | | |
|
|
130
|
+
| `timetrack category rename` | Rename a time track category | | |
|
|
131
|
+
| `timetrack current` | Show current time tracking status | | |
|
|
132
|
+
| `timetrack delete` | Delete a time track | | |
|
|
133
|
+
| `timetrack edit` | Edit a completed time track | `--category <v>` `--end <v>` `--notes <v>` `--start <v>` | |
|
|
134
|
+
| `timetrack export` | Export completed time tracks as CSV | `--force` `--output <v>` | raw CSV unless `--output <file>` |
|
|
135
|
+
| `timetrack list` | List completed time tracks | `--all` `--category <v>` `--limit <v>` | fields: id, starts_at, ends_at, category, notes |
|
|
136
|
+
| `timetrack start` | Start time tracking | | |
|
|
137
|
+
| `timetrack stop` | Stop time tracking | `--category <v>` | |
|
|
138
|
+
| `todo add` | Create a new todo | `--date <v>` `--title <v>` | |
|
|
139
|
+
| `todo complete` | Mark a todo as complete | | |
|
|
140
|
+
| `todo delete` | Delete a todo | | |
|
|
141
|
+
| `todo list` | List todos | `--all` `--calendar <v>` `--ends-on <v>` `--limit <v>` `--starts-on <v>` | fields: id, title, starts_at, completed_at |
|
|
142
|
+
| `todo uncomplete` | Mark a todo as incomplete | | |
|
|
143
|
+
| `trash` | Move email threads to Trash | | |
|
|
144
|
+
| `tui` | Launch the interactive terminal UI | `--instance <v>` `--remote` `--screener` `--topic <v>` `--topic-title <v>` | interactive, needs a TTY (user runs it) |
|
|
145
|
+
| `unseen` | Mark email threads as unseen | | |
|
|
146
|
+
| `unshare` | Turn off an email thread's sharing link | | |
|
|
147
|
+
| `upgrade` | Upgrade hey to the latest release | | interactive (user runs it) |
|
|
148
|
+
| `version` | Show the installed hey version | | |
|
|
149
|
+
| `watch` | Follow email threads and calendars as they change | `--box <v>` `--events <v>` `--exit-on-first` `--run-async <v>` `--run-sync <v>` `--since <v>` `--timeout <v>` | streams NDJSON |
|
|
150
|
+
| `workflow` | List and manage email workflows | | |
|
|
151
|
+
| `workflow add` | Add email threads to a workflow stage | `--stage <v>` `--to <v>` | |
|
|
152
|
+
| `workflow create` | Create an email workflow | | |
|
|
153
|
+
| `workflow delete` | Delete an email workflow | | |
|
|
154
|
+
| `workflow list` | List your email workflows | `--all` `--limit <v>` | fields: id, name |
|
|
155
|
+
| `workflow move` | Move email threads to another workflow stage | `--to <v>` `--workflow <v>` | |
|
|
156
|
+
| `workflow remove` | Remove email threads from a workflow | `--from <v>` | |
|
|
157
|
+
| `workflow stage create` | Add an Untitled stage to a workflow | | |
|
|
158
|
+
| `workflow stage delete` | Delete a workflow stage | | |
|
|
159
|
+
| `workflow stage update` | Rename a workflow stage | `--name <v>` | |
|
|
160
|
+
| `workflow update` | Rename an email workflow | `--name <v>` | |
|
|
161
|
+
| `workflow view` | View a workflow and its stages | | |
|
package/src/args.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// Command-line parsing that knows which HEY flags take a value, so global flags such as
|
|
2
|
+
// `--account 5` can appear anywhere (before, between, or after the command words)
|
|
3
|
+
// without their values being mistaken for command words.
|
|
4
|
+
|
|
5
|
+
// HEY's persistent (global) flags, from `hey --help`. Only these three take a value.
|
|
6
|
+
export const GLOBAL_VALUE_FLAGS = ["account", "jq", "base-url"];
|
|
7
|
+
export const GLOBAL_BOOLEAN_FLAGS = ["json", "quiet", "ids-only", "count", "markdown", "styled", "html", "stats", "verbose", "help"];
|
|
8
|
+
export const GLOBAL_SHORTHANDS = ["-v", "-h"];
|
|
9
|
+
|
|
10
|
+
// hey-axi's own flags. Never forwarded to HEY.
|
|
11
|
+
export const AXI_BOOLEAN_FLAGS = ["allow-send", "allow-secret", "full"];
|
|
12
|
+
export const AXI_VALUE_FLAGS = ["fields"];
|
|
13
|
+
|
|
14
|
+
// Output selectors that make HEY print something other than a JSON envelope. When any is
|
|
15
|
+
// present, hey-axi hands the command to HEY untouched and prints HEY's output as-is.
|
|
16
|
+
export const RAW_OUTPUT_FLAGS = ["ids-only", "count", "markdown", "html", "styled", "jq"];
|
|
17
|
+
|
|
18
|
+
export function valueFlagSet(commands) {
|
|
19
|
+
const names = new Set([...GLOBAL_VALUE_FLAGS, ...AXI_VALUE_FLAGS].map((name) => `--${name}`));
|
|
20
|
+
const walk = (nodes) => {
|
|
21
|
+
for (const node of nodes || []) {
|
|
22
|
+
for (const flag of node.flags || []) {
|
|
23
|
+
if (!flag.value) continue;
|
|
24
|
+
names.add(`--${flag.name}`);
|
|
25
|
+
if (flag.shorthand) names.add(`-${flag.shorthand}`);
|
|
26
|
+
}
|
|
27
|
+
walk(node.subcommands);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
walk(commands);
|
|
31
|
+
return names;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Split argv into positional words (for routing) and flag names (for mode selection).
|
|
35
|
+
// The argv itself is never reordered: HEY (cobra) accepts flags anywhere.
|
|
36
|
+
export function scanArgs(argv, valueFlags) {
|
|
37
|
+
const words = [];
|
|
38
|
+
const flags = new Set();
|
|
39
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
40
|
+
const arg = argv[i];
|
|
41
|
+
if (arg === "--") {
|
|
42
|
+
words.push(...argv.slice(i + 1));
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
if (arg.startsWith("-") && arg !== "-") {
|
|
46
|
+
const [name] = arg.split("=", 1);
|
|
47
|
+
flags.add(name);
|
|
48
|
+
if (!arg.includes("=") && valueFlags.has(name)) i += 1;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
words.push(arg);
|
|
52
|
+
}
|
|
53
|
+
return { words, flags };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function hasAny(flags, names) {
|
|
57
|
+
return names.some((name) => flags.has(`--${name}`));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Remove hey-axi's own flags (and the value of --fields) from argv, keeping order.
|
|
61
|
+
export function stripAxiFlags(argv) {
|
|
62
|
+
const out = [];
|
|
63
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
64
|
+
const arg = argv[i];
|
|
65
|
+
if (arg === "--") {
|
|
66
|
+
out.push(...argv.slice(i));
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
const [name] = arg.split("=", 1);
|
|
70
|
+
if (AXI_BOOLEAN_FLAGS.some((flag) => name === `--${flag}`)) continue;
|
|
71
|
+
if (AXI_VALUE_FLAGS.some((flag) => name === `--${flag}`)) {
|
|
72
|
+
if (!arg.includes("=")) i += 1;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
out.push(arg);
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Value of a value flag (`--fields a,b` or `--fields=a,b`); the last one wins.
|
|
81
|
+
export function flagValue(argv, name) {
|
|
82
|
+
let value;
|
|
83
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
84
|
+
if (argv[i] === "--") break;
|
|
85
|
+
if (argv[i] === `--${name}`) value = argv[i + 1];
|
|
86
|
+
else if (argv[i].startsWith(`--${name}=`)) value = argv[i].slice(name.length + 3);
|
|
87
|
+
}
|
|
88
|
+
return value;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const flagName = (flag) => (flag.startsWith("--") ? flag : flag.slice(0, 2));
|
|
92
|
+
|
|
93
|
+
export function flagLabel(flag) {
|
|
94
|
+
const short = flag.shorthand ? `, -${flag.shorthand}` : "";
|
|
95
|
+
return `--${flag.name}${short}${flag.value ? " <value>" : ""}`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// AXI: reject unknown flags (and missing required ones) before HEY is ever called.
|
|
99
|
+
// Returns null, or { unknown: [...] } / { missing: [...] }.
|
|
100
|
+
export function validateFlags(node, flags) {
|
|
101
|
+
const allowed = new Set([
|
|
102
|
+
...[...GLOBAL_VALUE_FLAGS, ...GLOBAL_BOOLEAN_FLAGS, ...AXI_BOOLEAN_FLAGS, ...AXI_VALUE_FLAGS].map((name) => `--${name}`),
|
|
103
|
+
...GLOBAL_SHORTHANDS,
|
|
104
|
+
]);
|
|
105
|
+
for (const flag of node.flags || []) {
|
|
106
|
+
allowed.add(`--${flag.name}`);
|
|
107
|
+
if (flag.shorthand) allowed.add(`-${flag.shorthand}`);
|
|
108
|
+
}
|
|
109
|
+
const unknown = [...flags].map(flagName).filter((flag) => !allowed.has(flag));
|
|
110
|
+
if (unknown.length) return { unknown: [...new Set(unknown)] };
|
|
111
|
+
if (flags.has("--help") || flags.has("-h")) return null;
|
|
112
|
+
const missing = (node.flags || [])
|
|
113
|
+
.filter((flag) => /\(required\)$/.test(flag.desc || ""))
|
|
114
|
+
.filter((flag) => !flags.has(`--${flag.name}`) && !(flag.shorthand && flags.has(`-${flag.shorthand}`)))
|
|
115
|
+
.map((flag) => `--${flag.name}`);
|
|
116
|
+
return missing.length ? { missing } : null;
|
|
117
|
+
}
|