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
# Get tasks/projects by UUID or human-readable key
awork tasks get BUG-1
awork projects get BUG
# 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"Build web links for awork app routes:
# Known entity type and ID/key
awork links get task <task-key-or-id>
awork links get project <project-key-or-id>
awork links get user <user-id>
awork links get company <company-id>
awork links get document <document-id>
awork links get time-report <report-id>
awork links get task-view <view-id>
# Task-scoped links
awork links get comment <comment-id> --task <task-key-or-id>
awork links get task-list <task-list-id> --project <project-key-or-id>
# Static app destinations
awork links get dashboard
awork links get account-settings
awork links get planner
awork links get whats-new
# Resolve a task key or probe a UUID across supported entity types
awork links resolve NIKE-42
awork links resolve <entity-id>Links use https://app.awork.com as the app base URL.
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 testsThe CLI generator reads the checked-in swagger.json. To pick up API changes from the awork app, refresh that file first:
./scripts/update-swagger.sh
dotnet buildBy default the script reads ../app/backend/services/ai-service/service/Assets/AworkOpenApiV1.json.
Set AWORK_OPENAPI_SOURCE=/path/to/AworkOpenApiV1.json to use another file.
Set API_BASE_URL=https://app.cwork.io to fetch the live docs endpoint instead, matching the app repo's API client generation flow.
dotnet pack src/Awk.CodeGen -c ReleaseOutput: src/Awk.CodeGen/bin/Release/Awk.CodeGen.*.nupkg
Every push to main runs the release workflow. Push-triggered releases use an automatic
version based on the GitHub Actions run number, for example 0.2.1234. The same
version is stamped into the binary, so awork --version matches the GitHub release.
You can also run the release workflow manually when you need an explicit version:
gh workflow run release.yml \
-f version=0.2.0 \
-f prerelease=falseThis 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 │
└─────────────────────────────────────────────────────────────┘
Rebuilding regenerates the CLI from the current swagger.json; it does not fetch a newer app spec by itself.
Refresh swagger.json first when the app OpenAPI contract changes. No manual DTO updates are 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 and Swagger 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