@abe-ing/cli
v0.5.0
Published
Command-line interface for driving ABE project boards and tasks, and reading the worksheets behind them.
Readme
@abe-ing/cli — the abe command
Drive ABE project boards and tasks from the command line, and read the worksheets (docs) behind them. Built for humans and agents — run engineering issues inside ABE instead of GitHub, and let Claude Code (or any shell-driven agent) create boards, file issues, and move cards via abe ... --json.
It is a thin, dependency-light wrapper over the ABE REST API, authenticated with a Personal Access Token.
Install
npm i -g @abe-ing/cliRequires Node ≥ 18.
Authenticate
Generate a Personal Access Token in the ABE web app (Settings → Developer → Generate a token), then:
abe auth login --token abe_pat_xxxx --api-url https://api.abe.ingThis stores the token at ~/.abe/config.json (created 0600). The token is bound to the workspace you generated it in.
Headless / CI — skip the config file and use environment variables:
export ABE_TOKEN=abe_pat_xxxx
export ABE_API_URL=https://api.abe.ingConfiguration precedence: flag (--token/--api-url) > environment (ABE_TOKEN/ABE_API_URL) > ~/.abe/config.json > default. The token is never printed in full (always redacted to the last 4 chars) and abe config set token is intentionally refused (it would leak into shell history).
abe auth status # show current config + a live auth check
abe auth logout # remove the stored token
abe config path # print the config file location
abe config set apiUrl https://api.abe.ingProject boards
These are the Project Boards in the ABE web app (the REST resource is task_boards).
abe board create "Engineering" --columns backlog,todo,in_review,done [--description "..."]
abe board listColumns are normalized to lowercase_with_underscores. New boards are custom boards; app/GitHub-backed boards are managed inside ABE.
Tasks
# Create — all properties in one shot
abe task create "Fix login race" \
--board engineering \ # slug, name, or numeric id
--type bug \ # task | feature | bug | suggestion | improvement | ops
--priority P1 \ # P0 | P1 | P2 | urgent | high | normal
--size M \ # XS | S | M | L | XL
--assignee [email protected] \ # email OR name — resolved server-side (or --assignee-id <n>)
--labels auth,regression \
--column todo \
--due 2026-07-01 \
--description "Race on concurrent login"
# Update any field (send only what changes)
abe task update 91 --priority P0 --labels auth,p0
abe task update 91 --board other-board # move to another board
abe task update 91 --body-file spec.md # replace the task DOC from markdown (- = stdin); --body "..." inline
abe task move 91 --column in_review
abe task complete 91
# Read
abe task list --board engineering # your OPEN tasks on that board
abe task list --board engineering --all --include-done # full set (admin/full token)
abe task board --board engineering # full board state incl. done — best for reconciliation
abe task board --board engineering --with-bodies # …plus every card's doc as body_markdown, in ONE call
abe task show 91 # header + the task BODY (its worksheet doc, rendered as markdown)Writing the doc. --body <markdown> / --body-file <path> (- reads stdin) replace the whole task doc: the markdown is converted server-side (headings, lists, code, tables, rules, bold/italic), the previous body is kept as a version in ABE, and the markdown source is mirrored into the task's description. --description on update means the same thing it does on create — it is the doc — so it also replaces the doc. You need edit permission on the doc itself (creator, admin, an edit share, or a workspace-editable doc) — exit 6 otherwise, and nothing else in the command is applied. Read the current body first (abe task show) if you mean to edit rather than rewrite. Saves are last-write-wins with the web editor: don't rewrite a doc someone has open.
Supported markdown: #–###### headings, paragraphs, **bold** / *italic* / `code` / [links](url), flat - and 1. lists, > quotes, fenced code blocks, pipe tables, --- rules. Nested lists flatten to one level and anything else lands as literal text — so on a read → edit → write round trip, check the result once for docs that use exotic formatting. A blank body is refused (exit 2 / 8) rather than emptying the doc.
task show prints the task's ref (ABE-91, the form to cite in PRs), its worksheet id, and the task's body — the linked worksheet doc rendered to markdown — under the header block. --json carries the body as task.body_markdown (plus task.description, the create-time text, and task.worksheet_id). Prefer body_markdown: description stops tracking the doc once it has been edited in the editor. The CLI requests the body with ?render=markdown; it is null when the doc is hidden from your token (e.g. a token without the tasks scope, or a private doc you can't see).
Invalid --priority/--type/--size/--status fail fast (exit 2) before any network call. --column is not client-validated (columns are per-board) — an invalid column returns the server's 422 listing the valid set.
Comments, history & delete
Leave PR-style progress notes on a task instead of a GitHub issue comment. Comments you add via the CLI are recorded as authored by the token's user (agent tokens are stamped origin: "agent"). You may only edit or delete your own comments (the server returns 403 otherwise).
abe task comment 91 "Root-caused: race in the session refresh. Fix in review."
abe task comments 91 # list all comments (chronological)
abe task comment-edit 91 42 "Updated: shipped in #628." # edit your comment #42
abe task comment-delete 91 42 # delete your comment #42
abe task history 91 # full timeline: field/status events + comments + worksheet edits
abe task delete 91 --yes # PERMANENT — refuses without --yes (exit 2)task delete is irreversible; prefer task complete for finished work. It refuses without --yes so an agent can't destroy a task by accident.
Worksheets
Worksheets are ABE's docs. Every task's body is a worksheet (task.worksheet_id); pages, specs, and plans are worksheets too. Read one directly — by a task's worksheet_id, or any other doc id:
abe worksheet show 6325 # "# <title>" then the body as markdown
abe --json worksheet show 6325 | jq -r '.worksheet.bodyMarkdown'
abe worksheet update 6325 --body-file plan.md # replace the whole doc from markdown (- = stdin)
abe worksheet update 6325 --title "Q3 plan" --body "# Q3 plan\n\n..."The server renders the markdown (GET /worksheets/:id?render=markdown), so the CLI never carries its own Tiptap converter. Task docs need a token with the tasks scope (or full) — exit 6 otherwise. Private docs are readable only by their creator, people they're shared with, and workspace admins, exactly as in the web app (exit 7 when not visible). update replaces the whole body (previous version kept) and/or retitles; same edit permission and last-write-wins caveat as task update --body-file.
--json emits a stable subset of the server's worksheet payload — id, title, body (Tiptap JSON), bodyMarkdown, visibility, lastEditedAt, createdByName — never the share token, owner email, or share/mention rosters the web editor receives.
Members
abe members list # requires an admin/full tokenYou rarely need this — assigning by email/name on task create/update resolves members server-side.
JSON output (for scripts & agents)
Add --json to any command. In --json mode, stdout carries only the JSON payload; all human/status text goes to stderr — so piping stdout to a parser never breaks.
abe --json task list --board engineering | jq '.tasks[] | {id,title,status}'
BOARD_ID=$(abe --json board list | jq '.boards[] | select(.slug=="engineering") | .id')Exit codes
| Code | Meaning | |------|---------| | 0 | Success | | 2 | Usage / parse error (bad flag, invalid enum) | | 4 | Auth (no token, or token invalid/expired) | | 5 | Workspace billing locked | | 6 | Forbidden (missing scope / role) | | 7 | Not found | | 8 | Validation error | | 9 | Server error | | 10 | Network error |
Global flags
--json, --api-url <url>, --token <pat>, --no-color. Environment: ABE_TOKEN, ABE_API_URL, ABE_TENANT (display only), ABE_CONFIG_DIR (override ~/.abe), ABE_NO_CONFIG (ignore the config file).
Develop
npm install
npm run build # tsc → dist/
npm test # vitest
npm run typecheck