asana

Pass

Manage Asana tasks, projects, briefs, status updates, custom fields, dependencies, attachments, events, and timelines via Personal Access Token (PAT).

@openclaw
MIT2/22/2026
(0)
1.0k
6
6

Install Skill

Skills are third-party code from public GitHub repositories. SkillHub scans for known malicious patterns but cannot guarantee safety. Review the source code before installing.

Install globally (user-level):

npx skillhub install openclaw/skills/asana

Install in current project:

npx skillhub install openclaw/skills/asana --project

Suggested path: ~/.claude/skills/asana/

SKILL.md Content

---
name: asana
description: "Manage Asana tasks, projects, briefs, status updates, custom fields, dependencies, attachments, events, and timelines via Personal Access Token (PAT)."
homepage: https://developers.asana.com/docs/personal-access-token
user-invocable: true
metadata: {"openclaw":{"requires":{"env":["ASANA_PAT"]},"primaryEnv":"ASANA_PAT","homepage":"https://developers.asana.com/docs/personal-access-token"}}
---

# Asana

This skill provides a dependency-free Node.js CLI that calls the Asana REST API (v1) using a **Personal Access Token (PAT)**.

- Script: `{baseDir}/scripts/asana.mjs`
- Auth: `ASANA_PAT` (preferred) or `ASANA_TOKEN`
- Output: **JSON only** (stdout), suitable for agents and automation

## Setup

1. Create an Asana PAT in your Asana account (Developer Console is **not** required for PAT usage).
2. Provide the token to OpenClaw/Clawdbot as `ASANA_PAT`.

### Common injection patterns

- Shell env (local testing):

  `export ASANA_PAT="..."`

- OpenClaw config (recommended): set `skills.entries.asana.apiKey` (or `env.ASANA_PAT`) so the secret is injected only for the agent run.

### Configure via OpenClaw CLI (recommended)

This is the safest way to set the PAT because it keeps secrets out of prompts and ad-hoc shell history.

**Recommended (apiKey → ASANA_PAT):**

```bash
openclaw config set skills.entries.asana.enabled true
openclaw config set skills.entries.asana.apiKey "ASANA_PAT_HERE"
```

`skills.entries.asana.apiKey` is a convenience field: for skills that declare `metadata.openclaw.primaryEnv`, OpenClaw injects `apiKey` into that env var for the agent run (this skill’s primary env is `ASANA_PAT`).

**Alternative (explicit env):**

```bash
openclaw config set skills.entries.asana.enabled true
openclaw config set skills.entries.asana.env.ASANA_PAT "ASANA_PAT_HERE"
```

**Verify what is stored:**

```bash
openclaw config get skills.entries.asana
openclaw config get skills.entries.asana.enabled
openclaw config get skills.entries.asana.apiKey
```

**Remove a stored token:**

```bash
openclaw config unset skills.entries.asana.apiKey
# or
openclaw config unset skills.entries.asana.env.ASANA_PAT
```

#### Important: sandboxed runs

When a session is sandboxed, skill processes run inside Docker and do **not** inherit the host environment. In that case, `skills.entries.*.env/apiKey` applies to host runs only.

Set Docker env via:

- `agents.defaults.sandbox.docker.env` (or per-agent `agents.list[].sandbox.docker.env`)
- or bake the env into your sandbox image

## First calls (sanity + discovery)

- Who am I:

  `node {baseDir}/scripts/asana.mjs me`

- List workspaces:

  `node {baseDir}/scripts/asana.mjs workspaces`

- (Recommended) Set a default workspace once:

  `node {baseDir}/scripts/asana.mjs set-default-workspace --workspace <workspace_gid>`

## ID resolution

When the user provides names (project/task/user), resolve to GIDs using one of:

- `typeahead --workspace <gid> --resource_type project|task|user --query "..."` (fast, best default)
- `projects --workspace <gid> --all` (enumerate)
- `users --workspace <gid> --all` (enumerate)

Avoid guessing a GID when multiple matches exist.

## Core: tasks

### List tasks assigned to a user (personal productivity)

`node {baseDir}/scripts/asana.mjs tasks-assigned --assignee me --workspace <workspace_gid> --all`

### List tasks in a project

`node {baseDir}/scripts/asana.mjs tasks-in-project --project <project_gid> --all`

### Search tasks (Advanced Search API)

Canonical primitive: `search-tasks` (supports many filters; preferred over adding narrow “search helper” commands).

One-liner example (search within a project):

`node {baseDir}/scripts/asana.mjs search-tasks --workspace <gid> --project <project_gid> --text "..." --all`

Useful filters:
- `--assignee me|<gid|email>` (maps to `assignee.any`)
- `--completed true|false`
- `--created_at.after <iso>` / `--modified_at.after <iso>`
- `--due_on.before YYYY-MM-DD` / `--due_at.before <iso>`
- `--is_blocked true|false` / `--is_blocking true|false`

### Create / update / complete

- Create:

  `node {baseDir}/scripts/asana.mjs create-task --workspace <gid> --name "..." --projects <project_gid> --assignee me`

- Update:

  `node {baseDir}/scripts/asana.mjs update-task <task_gid> --name "..." --due_on 2026-02-01`

- Complete:

  `node {baseDir}/scripts/asana.mjs complete-task <task_gid>`

## Project manager workflows

This skill supports the workflows commonly expected from a PM in Asana:

- Keep a **project brief** up to date (`upsert-project-brief`)
- Write **status updates** (`create-status-update`)
- Work with **timelines** (start/due dates) and shift schedules safely
- Use **custom fields** as first-class metadata
- Interpret **blockers** and dependency graphs (`project-blockers`, `dependencies`, `dependents`)

### Project brief

- Read:

  `node {baseDir}/scripts/asana.mjs project-brief <project_gid>`

- Upsert (create or update):

  `node {baseDir}/scripts/asana.mjs upsert-project-brief <project_gid> --title "Project brief" --html_text "<body>...</body>"`

### Status updates

- Create:

  `node {baseDir}/scripts/asana.mjs create-status-update --parent <project_gid> --status_type on_track --text "Weekly update..."`

- List:

  `node {baseDir}/scripts/asana.mjs status-updates --parent <project_gid> --all`

### Sections and moving tasks

- List sections:

  `node {baseDir}/scripts/asana.mjs sections --project <project_gid> --all`

- Create section:

  `node {baseDir}/scripts/asana.mjs create-section --project <project_gid> --name "Blocked"`

#### Add a task to a project

Command: `add-task-to-project`

Calls `POST /tasks/{task_gid}/addProject` and supports optional section placement and ordering.

Examples:

`node {baseDir}/scripts/asana.mjs add-task-to-project <task_gid> --project <project_gid>`

With section + ordering:

`node {baseDir}/scripts/asana.mjs add-task-to-project <task_gid> --project <project_gid> --section <section_gid> --insert_before null --insert_after null`

(`--section`, `--insert_before`, and `--insert_after` are optional; when provided they are passed through in the request body.)

#### Remove a task from a project

Command: `remove-task-from-project`

Calls `POST /tasks/{task_gid}/removeProject`.

Example:

`node {baseDir}/scripts/asana.mjs remove-task-from-project <task_gid> --project <project_gid>`

## Custom fields

Custom fields are critical for reliable PM automation.

- List a project’s custom fields:

  `node {baseDir}/scripts/asana.mjs project-custom-fields <project_gid> --all`

- Read a custom field definition:

  `node {baseDir}/scripts/asana.mjs custom-field <custom_field_gid>`

- Set task custom fields on create/update:

  `node {baseDir}/scripts/asana.mjs update-task <task_gid> --custom_fields '{"<custom_field_gid>":"<value>"}'`

Notes:
- For enums, the value is typically the enum option GID.
- For numbers, send a JSON number.

## Rich text, mentions, and reliability

Asana rich text fields are **XML-valid HTML fragments** wrapped in a `<body>` root element. The API rejects invalid XML or unsupported tags.

Key points:
- Use `html_notes` for task descriptions.
- Use `html_text` for comments/stories and status updates.
- Avoid unsupported tags like `<p>` and `<br>`; prefer literal newlines (`\n`) and `<hr/>` separators.
- For mentions/links, use `<a data-asana-gid="..."></a>` (or a self-closing `<a .../>`).

### Mention notifications

Creating a mention link does **not** guarantee notification delivery if the user is not already assigned or following.

For reliable pings, do one of:
- Assign the user first, then post the comment, OR
- Add the user as a follower, wait a few seconds, then post the comment

This skill supports the “add follower + wait” pattern:

`node {baseDir}/scripts/asana.mjs comment <task_gid> --html_text "<body>Hi <a data-asana-gid=\"<user_gid>\"/>...</body>" --ensure_followers <user_gid> --wait_ms 2500`

Plain text comments (`--text`) do **not** create real @-mentions via the API; they remain plain text.

## Attachments, uploads, and inline images

- Upload a file attachment to a task:

  `node {baseDir}/scripts/asana.mjs upload-attachment --parent <task_gid> --file /path/to/file.png`

- Embed an existing image attachment inline (tasks + project briefs only):

  `node {baseDir}/scripts/asana.mjs append-inline-image --attachment <attachment_gid> --task <task_gid>`

## Activity feed / “inbox-like” workflows

Asana does not provide a single universal “inbox” API for all notifications. The closest stable primitive is the **Events** endpoint scoped to a specific resource (project, task, etc.).

Use:
- `events --resource <gid>` to pull incremental changes on a project (or a user's "My Tasks" project)
- The command stores a sync token locally so subsequent runs fetch only changes

## Timeline shifting

- Shift one task (optionally include subtasks):

  `node {baseDir}/scripts/asana.mjs shift-task-dates <task_gid> --delta_days 7 --dry_run true`

- Shift an entire project’s tasks:

  `node {baseDir}/scripts/asana.mjs shift-project-tasks --project <project_gid> --delta_days -3 --dry_run true --all`

Run with `--dry_run true` first, then re-run with `--dry_run false`.

## Out of scope

- Portfolios (premium) are intentionally omitted.
- “Bot personality” is not embedded here; configure behavior in your agent prompt.
asana | SkillHub | SkillHub