The awork CLI — built for humans and agents alike
Token or OAuth authentication • Swagger-driven code generation • Structured JSON output
| Problem | Solution |
|---|---|
| Unstable command names across API versions | Commands generated directly from Swagger — always in sync |
| Inconsistent parameter validation | Strict validation at build time via source generation |
| Unparseable output for automation | Every response wrapped in a predictable JSON envelope |
| Manual DTO maintenance | Zero hand-written DTOs — all generated from swagger.json |
$ awork users list --page-size 3
{
"statusCode": 200,
"traceId": "abc123",
"response": [...]
}brew tap awork-io/awork-cli
brew install awork-cliGrab the latest release for your platform from GitHub Releases.
| Platform | Binary |
|---|---|
| macOS (Apple Silicon) | awork-osx-arm64.tar.gz |
| macOS (Intel) | awork-osx-x64.tar.gz |
| Linux (x64) | awork-linux-x64.tar.gz |
| Windows (x64) | awork-win-x64.zip |
1. Set your token (fastest path)
echo "AWORK_TOKEN=your-token-here" > .env2. Or login with OAuth (DCR)
awork auth login3. Verify setup
awork doctor4. Explore
awork --helpSet your awork API token via environment variable or .env file:
export AWORK_TOKEN=your-token-here
# or
echo "AWORK_TOKEN=your-token-here" > .envUse --env <PATH> to load a different .env file.
awork auth loginThis opens a browser and stores an OAuth token + refresh token in the user config file.
If your tenant requires DCR authorization, provide a token via AWORK_DCR_TOKEN.
Override OAuth settings with --redirect-uri, --scopes, or env vars:
AWORK_OAUTH_REDIRECT_URI, AWORK_OAUTH_SCOPES, AWORK_OAUTH_CLIENT_ID.
Default: API token wins. Override with:
awork --auth-mode oauth users listValid modes: auto (default), token, oauth.
Environment variable: AWK_AUTH_MODE or AWORK_AUTH_MODE.
User config is stored at:
- macOS/Linux:
~/.config/awork-cli/config.json - Windows:
%APPDATA%\\awork-cli\\config.json
Override with:
awork --config /path/to/config.json auth statusCommands follow a consistent pattern derived from the Swagger spec:
awork <domain> [resource] <action> [positional-args] [--options]
- Domains are top-level buckets:
users,tasks,projects,times,workspace, ... - Resources appear as sub-branches when needed (e.g.
users invitations,tasks tags) - Actions use predictable verbs:
list,get,create,update,delete - Positional args match path parameters in URL order
- Options are kebab-case query/body parameters
# List all resource groups
awork --help
# List actions for a domain
awork users --help
# Get help for a specific command
awork users list --help# OAuth login
awork auth login
# Save API token
awork auth login --token "$AWORK_TOKEN"
# Status
awork auth status
# Logout (clear tokens)
awork auth logoutThese options are available on all API commands:
| Option | Description |
|---|---|
--select <FIELDS> |
Filter response fields (client-side). Example: --select "id,name,createdOn" |
--output <FORMAT> |
Output format: json (default) or table |
--page <N> |
Page number for paginated endpoints (default: 1) |
--page-size <N> |
Items per page for paginated endpoints |
--env <PATH> |
Load environment variables from a custom .env file |
--token <TOKEN> |
Override the API token for this request |
--auth-mode <MODE> |
Force auth mode: auto, token, or oauth |
--config <PATH> |
Use a custom config file path |
# List users
awork users list
# List with pagination and field selection
awork users list --page-size 5 --select "id,firstName,lastName"
# Table output for quick inspection
awork users list --output table --select "firstName,lastName"
# Get user by ID (positional path param)
awork users get 550e8400-e29b-41d4-a716-446655440000
# Search with filters
awork search get-search \
--search-term "agent" \
--search-types "user" \
--top 3 \
--include-closed-and-stuck true# Create with inline params
awork tasks create \
--name "Welcome" \
--base-type private \
--entity-id 550e8400-e29b-41d4-a716-446655440000
# Create from JSON file
awork tasks create --body @samples/private-task.json
# Merge file + overrides
awork tasks create \
--body @payload.json \
--set name="Override Title"# Inline JSON arrays with --set-json
awork workspace absence-regions users-assign \
--set regionId=550e8400-e29b-41d4-a716-446655440000 \
--set-json userIds='["user-1","user-2"]'
# JSON arrays from file
awork workspace absence-regions users-assign \
--set regionId=550e8400-e29b-41d4-a716-446655440000 \
--set-json userIds=@/tmp/users.json
# Nested properties
awork task-tags tasks-update-tags \
--set newTag.name=PriorityThe consistent JSON envelope makes jq integration seamless:
# Get first user's ID
awork users list --page-size 1 | jq -r '.response[0].id'
# List project names only
awork projects list | jq -r '.response[].name'
# Get task count by status
awork tasks list | jq '.response | group_by(.taskStatusId) | map({status: .[0].taskStatusId, count: length})'
# Chain commands: create task for first active user
USER_ID=$(awork users list --page-size 1 | jq -r '.response[0].id')
awork tasks create --name "Welcome" --base-type private --entity-id "$USER_ID"
# Check if request succeeded
awork users me | jq -e '.statusCode == 200' > /dev/null && echo "OK" || echo "Failed"Step 1 — Invite the user (skip email for programmatic flow)
# samples/invite.json
{
"workspaceId": "<workspace-id>",
"email": "new.user@example.com",
"firstName": "New",
"lastName": "User",
"title": "Engineer",
"position": "Platform",
"roleId": "<role-id>",
"teamIds": ["<team-id>"],
"skipSendingEmail": true
}awork invitations create --body @samples/invite.jsonStep 2 — Accept the invitation programmatically
# samples/accept.json
{ "invitationCode": "<invitation-code>" }awork invitations accept --body @samples/accept.jsonStep 3 — Create a welcome task for the new user
# samples/private-task.json
{
"name": "Welcome to awork",
"description": "Start here",
"baseType": "private",
"entityId": "<user-id>",
"isPrio": true,
"plannedDuration": 1800
}awork tasks create --body @samples/private-task.jsonOr inline with overrides:
awork tasks create \
--body @samples/private-task.json \
--set entityId="$(awork users list | jq -r '.response[0].id')"Every command returns a consistent JSON envelope:
{
"statusCode": 200,
"traceId": "00-abc123...",
"response": { ... }
}| Field | Description |
|---|---|
statusCode |
HTTP status code from the API |
traceId |
Correlation ID from response headers (best effort) |
response |
Parsed JSON body, or raw text if not JSON |
This makes awork trivial to integrate with jq, scripts, and AI agents.
git clone https://github.com/awork-io/awork-cli.git
cd awork-cli
dotnet build
dotnet run --project src/Awk.Cli -- --help# macOS Apple Silicon
dotnet publish src/Awk.Cli -c Release -r osx-arm64
# macOS Intel
dotnet publish src/Awk.Cli -c Release -r osx-x64
# Linux
dotnet publish src/Awk.Cli -c Release -r linux-x64
# Windows
dotnet publish src/Awk.Cli -c Release -r win-x64Output: src/Awk.Cli/bin/Release/net10.0/<rid>/publish/awork
# All tests
dotnet test
# Individual test suites
./scripts/test-build.sh # Build verification
./scripts/test-cli-names.sh # Command naming consistency
./scripts/test-example.sh # Example command validation
./scripts/test-params.sh # Parameter handling
./scripts/test-auth.sh # Auth flows (token)
./scripts/test-unit.sh # Unit testsdotnet pack src/Awk.CodeGen -c ReleaseOutput: src/Awk.CodeGen/bin/Release/Awk.CodeGen.*.nupkg
Push a version tag to trigger the release workflow:
git tag v0.1.0
git push origin v0.1.0This will:
- Build binaries for macOS (ARM64/x64), Linux, and Windows
- Create a GitHub release with all artifacts
- Update the Homebrew formula (requires
HOMEBREW_TAP_TOKENsecret)
┌─────────────────────────────────────────────────────────────┐
│ swagger.json │
└─────────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Awk.CodeGen │
│ (Roslyn Source Generator) │
│ │
│ • Parses OpenAPI 3.0 spec │
│ • Emits DTOs to Awk.Generated namespace │
│ • Generates typed API client methods │
│ • Creates CLI commands grouped by Swagger tags │
└─────────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Awk.Cli │
│ (Spectre.Console.Cli) │
│ │
│ • Token + OAuth authentication (DCR) │
│ • Parameter validation │
│ • JSON envelope output │
└─────────────────────────────────────────────────────────────┘
When the Swagger spec changes, just rebuild. No manual updates required.
awork-cli/
├── .github/
│ └── workflows/
│ ├── ci.yml # Build & test on PRs
│ └── release.yml # Multi-platform release on tags
├── src/
│ ├── Awk.CodeGen/ # Source generator (NuGet-packageable)
│ └── Awk.Cli/ # CLI application
├── tests/
│ ├── Awk.CodeGen.Tests/ # Generator unit tests
│ └── Awk.Cli.Tests/ # CLI integration tests
├── homebrew/ # Homebrew formula template
├── scripts/ # Test helpers
├── samples/ # Example JSON payloads
└── swagger.json # awork OpenAPI spec
The awork CLI ships with a built-in skill command that exports a ready-made skill definition for AI coding agents. This lets agents understand available commands, authentication, and output formats without manual configuration.
Register the awork CLI skill globally so it's available across all your projects:
mkdir -p ~/.claude/skills/awork-cli
awork skill > ~/.claude/skills/awork-cli/SKILL.mdAfter this, Claude Code will automatically pick up the skill and can use the awork CLI on your behalf.
MIT
Built with Spectre.Console • Powered by Roslyn Source Generators