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
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 TangentialSolutions

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
17 changes: 13 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@ hey-axi runs `hey`, asks for its JSON response envelope, and prints it as [TOON]
Requirements: Node 20+ and an authenticated HEY CLI (`curl -fsSL https://hey.com/install-cli | bash`, then `hey auth login`).

```bash
git clone https://github.com/TangentialSolutions/hey-axi && cd hey-axi
npm ci
npm link # puts `hey-axi` on your PATH
npm i -g hey-axi # puts `hey-axi` on your PATH
# or run it without installing:
npx -y hey-axi box view imbox

# agent skill (see "Agent integrations" below)
npx skills add TangentialSolutions/hey-axi
```

From source: `git clone https://github.com/TangentialSolutions/hey-axi && cd hey-axi && npm ci && npm link`.

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.

## Usage
Expand Down Expand Up @@ -95,7 +100,7 @@ gh skill install TangentialSolutions/hey-axi hey-axi --agent claude-code --scope
cp -r skills/hey-axi ~/.claude/skills/ # or ~/.agents/skills/, ~/.codex/skills/, …
```

While the repository is private, `npx skills add` and `gh skill install` work only for people with access (they use your git/gh credentials). See [docs/listing.md](docs/listing.md) for the public-listing checklist.
See [docs/listing.md](docs/listing.md) for how hey-axi gets listed on skills.sh and axi.md.

## Keeping up with HEY releases

Expand All @@ -116,3 +121,7 @@ npm test # node:test; every test uses a fake `hey` (test/helpers.js), never
Token benchmark (HEY CLI vs hey-axi output): [docs/benchmarks.md](docs/benchmarks.md), with `npm run bench:tokens`.

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.

## License

[MIT](LICENSE) © 2026 TangentialSolutions. hey-axi is an independent project. It isn't affiliated with or endorsed by Basecamp or HEY.
12 changes: 7 additions & 5 deletions docs/listing.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# Listing hey-axi (draft, nothing submitted)
# Listing hey-axi

This is the prep for getting hey-axi listed on [axi.md](https://axi.md) and the agent-skill directories. **None of these steps has been taken.** Every one of them needs the repo to be public first. Research was done on 2026-10-01; directory rules change, so re-check the linked sources before you submit.
This is the prep for getting hey-axi listed on [axi.md](https://axi.md) and the agent-skill directories. Every step needs the repo to be public first. Research was done on 2026-10-01; directory rules change, so re-check the linked sources before you submit.

## Prerequisites (owner decisions)

Status: MIT license ✅, package.json ready for `npm publish` ✅ (no `private`, `license: MIT`). Making the repo public, publishing to npm and adding topics are the owner's steps.

1. **Make the repo public.** Every directory below reads public GitHub repos. skills.sh telemetry is only sent for public repos ([vercel-labs/skills `src/add.ts`](https://github.com/vercel-labs/skills/blob/main/src/add.ts) sends it only when `isPrivate === false`).
2. **Add a license.** The repo has none today, which legally means "all rights reserved", so nobody may reuse it. HEY CLI is MIT, as are most AXI catalog entries, so MIT is the natural fit, but it's the owner's choice. Once chosen, add `LICENSE`, the `"license"` field in `package.json`, and optionally `license:` in `skills/hey-axi/SKILL.md`.
3. **npm publish (optional, recommended).** The name `hey-axi` is unclaimed on npm (checked 2026-10-01). Publishing allows `npm i -g hey-axi` / `npx -y hey-axi` instead of clone + `npm link`. To publish, drop `"private": true` from `package.json` and run `npm publish`. `files` is already set: the tarball has `src/`, `skills/` and `README.md`.
2. **License:** done. MIT (`LICENSE`, `package.json` `license`, `license:` in SKILL.md).
3. **npm publish.** The name `hey-axi` was unclaimed on npm (re-checked 2026-10-02). `npm login`, then `npm publish` from a clean `main`. The tarball has `src/`, `skills/`, `README.md`, `LICENSE` and `package.json`.
4. **GitHub topics** (help SkillsMP and GitHub search): `agent-skills`, `claude-skills`, `axi`, `hey`, `email`, `cli`.

## axi.md (community catalog)
Expand All @@ -33,7 +35,7 @@ Source: https://github.com/kunchenguid/axi. The catalog in [`catalog.yaml`](http
| 8 Content first (no-arg home view) | ✅ the Imbox's 10 newest threads plus next commands; `status` + fix when HEY is missing or signed out | `src/home.js` |
| 9 Contextual disclosure | ✅ HEY's breadcrumbs → ``help: Run `hey-axi …` ``. ⚠️ HEY also suggests `forward`, which hey-axi refuses without `--allow-send` (the refusal explains) | `shapeEnvelope` |
| 10 Help + `--version` fast path | ✅ `-v`/`-V`/`--version` answered from a leaf module before the CLI loads (latency test); home view shows `bin` + `description`; per-command `--help` has usage, flag descriptions/defaults, examples, notes | `src/hey-axi.js`, `src/version.js` |
| Skill (secondary path) | ✅ SKILL.md's home block generated from the home-view text, `npm run skill:check` in CI. ⚠️ examples use `hey-axi …`, not `npx -y hey-axi …` (not on npm yet) | `scripts/gen-skill.js` |
| Skill (secondary path) | ✅ SKILL.md's home block generated from the home-view text, `npm run skill:check` in CI. Install section offers `npm i -g hey-axi` and the `npx -y hey-axi …` fallback (works once published) | `scripts/gen-skill.js` |

Remaining risk for an `admitted` verdict: the ⚠️ items above. Also, the per-command default fields for less common lists (screener history, account list, clip list, timetrack list) come from HEY's own table columns and the hey-sdk schema, not from real responses. Missing fields drop out automatically.

Expand Down
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"name": "hey-axi",
"version": "0.2.0",
"private": true,
"description": "Agent-ergonomic AXI wrapper for Basecamp's HEY CLI: TOON output, send-to-draft safety, and a shareable agent skill",
"keywords": [
"axi",
Expand All @@ -28,14 +27,16 @@
"url": "git+https://github.com/TangentialSolutions/hey-axi.git"
},
"author": "Trevor Broaddus",
"license": "MIT",
"type": "module",
"bin": {
"hey-axi": "src/hey-axi.js"
},
"files": [
"src",
"skills",
"README.md"
"README.md",
"LICENSE"
],
"scripts": {
"test": "node --test test/*.test.js",
Expand Down
8 changes: 5 additions & 3 deletions skills/hey-axi/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
name: hey-axi
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.
license: MIT
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.
metadata:
author: TangentialSolutions
Expand Down Expand Up @@ -29,7 +30,7 @@ Next steps the home view suggests:
<!-- generated:home:end -->

Problems:
- `hey-axi: command not found`: install it (see "Install" below), or run `node <repo>/src/hey-axi.js`.
- `hey-axi: command not found`: run it as `npx -y hey-axi …`, or install it (see "Install" below).
- `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`).
- `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).
- `hey-axi doctor` finds other login and config problems. `hey-axi version` shows both versions.
Expand Down Expand Up @@ -114,10 +115,11 @@ curl -fsSL https://hey.com/install-cli | bash # or: brew install --cask b
hey auth login # the user runs this once, interactively

# hey-axi
git clone https://github.com/TangentialSolutions/hey-axi && cd hey-axi
npm ci && npm link # puts `hey-axi` on PATH
npm i -g hey-axi # puts `hey-axi` on PATH
```

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.

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.

More commands (calendar, todos, habits, journal, time tracking, contacts, collections), with their default fields: [references/commands.md](references/commands.md).
14 changes: 7 additions & 7 deletions test/hey-axi.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,16 +210,16 @@ test("delegates clip listing and renders saved passage data", async () => {
const fakeHey = join(directory, "hey");
await writeFile(fakeHey, `#!/bin/sh
printf '%s\\n' "$*" > "$HEY_ARGS_FILE"
printf '%s' '[{"id":250908,"content":"854093","topic":{"id":1464700104,"name":"Passcode for access"}}]'
printf '%s' '[{"id":4242,"content":"See you at 10","topic":{"id":9001,"name":"Saved snippet"}}]'
`);
await execFile(process.env.SHELL || "/bin/sh", ["-c", `chmod +x "$1"`, "sh", fakeHey]);
const argsFile = join(directory, "args");
const result = await new Promise((resolve) => execFile(process.execPath, ["src/hey-axi.js", "clip", "list"], {
env: { ...process.env, HEY_BIN: fakeHey, HEY_ARGS_FILE: argsFile },
}, (error, stdout, stderr) => resolve({ error, stdout, stderr })));
assert.equal(result.error, null);
assert.match(result.stdout, /Passcode for access/);
assert.match(result.stdout, /854093/);
assert.match(result.stdout, /Saved snippet/);
assert.match(result.stdout, /See you at 10/);
const forwarded = await import("node:fs/promises").then(({ readFile }) => readFile(argsFile, "utf8"));
assert.equal(forwarded.trim(), "clip list --json");
await rm(directory, { recursive: true, force: true });
Expand Down Expand Up @@ -439,15 +439,15 @@ test("delegates collection listing and preserves pagination flags", async () =>
const fakeHey = join(directory, "hey");
await writeFile(fakeHey, `#!/bin/sh
printf '%s\\n' "$*" > "$HEY_ARGS_FILE"
printf '%s' '{"ok":true,"data":[{"id":20671,"name":"AWS Bounce"}]}'
printf '%s' '{"ok":true,"data":[{"id":3141,"name":"Receipts"}]}'
`);
await execFile(process.env.SHELL || "/bin/sh", ["-c", `chmod +x "$1"`, "sh", fakeHey]);
const argsFile = join(directory, "args");
const result = await new Promise((resolve) => execFile(process.execPath, ["src/hey-axi.js", "collection", "list", "--limit", "5", "--all"], {
env: { ...process.env, HEY_BIN: fakeHey, HEY_ARGS_FILE: argsFile },
}, (error, stdout, stderr) => resolve({ error, stdout, stderr })));
assert.equal(result.error, null);
assert.match(result.stdout, /AWS Bounce/);
assert.match(result.stdout, /Receipts/);
const forwarded = await import("node:fs/promises").then(({ readFile }) => readFile(argsFile, "utf8"));
assert.equal(forwarded.trim(), "collection list --limit 5 --all --json");
await rm(directory, { recursive: true, force: true });
Expand All @@ -462,13 +462,13 @@ printf '%s' '{"ok":true,"data":[{"id":812,"topic_id":456,"subject":"Quarterly pl
`);
await execFile(process.env.SHELL || "/bin/sh", ["-c", `chmod +x "$1"`, "sh", fakeHey]);
const argsFile = join(directory, "args");
const result = await new Promise((resolve) => execFile(process.execPath, ["src/hey-axi.js", "collection", "view", "20671", "--page", "cursor-2", "--limit", "5"], {
const result = await new Promise((resolve) => execFile(process.execPath, ["src/hey-axi.js", "collection", "view", "3141", "--page", "cursor-2", "--limit", "5"], {
env: { ...process.env, HEY_BIN: fakeHey, HEY_ARGS_FILE: argsFile },
}, (error, stdout, stderr) => resolve({ error, stdout, stderr })));
assert.equal(result.error, null);
assert.match(result.stdout, /Quarterly planning/);
const forwarded = await import("node:fs/promises").then(({ readFile }) => readFile(argsFile, "utf8"));
assert.equal(forwarded.trim(), "collection view 20671 --page cursor-2 --limit 5 --json");
assert.equal(forwarded.trim(), "collection view 3141 --page cursor-2 --limit 5 --json");
await rm(directory, { recursive: true, force: true });
});

Expand Down
Loading