介绍
# Asana
此技能提供了一个无依赖的 Node.js CLI,使用 **Personal Access Token (PAT)** 调用 Asana REST API (v1)。
- 脚本:`{baseDir}/scripts/asana.mjs` - 认证:`ASANA_PAT`(首选)或 `ASANA_TOKEN` - 输出:**仅 JSON**(标准输出),适用于代理和自动化
## 设置
1. 在您的 Asana 账户中创建一个 Asana PAT(使用 PAT 不需要开发者控制台 Developer Console)。 2. 将作为 `ASANA_PAT` 提供给 OpenClaw/Clawdbot。
### 常见注入模式
- Shell 环境变量(本地测试):
`export ASANA_PAT="..."`
- OpenClaw 配置(推荐):设置 `skills.entries.asana.apiKey`(或 `env.ASANA_PAT`),以便仅在代理运行时注入该密钥。
### 通过 OpenClaw CLI 配置(推荐)
这是设置 PAT 最安全的方式,因为它可以避免机密信息泄露到提示词和临时的 Shell 历史记录中。
**推荐(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` 是一个便捷字段:对于声明了 `metadata.openclaw.primaryEnv` 的技能,OpenClaw 会将 `apiKey` 注入到该环境变量中供代理运行使用(此技能的主要环境变量是 `ASANA_PAT`)。
**备选方案(显式环境变量):**
```bash openclaw config set skills.entries.asana.enabled true openclaw config set skills.entries.asana.env.ASANA_PAT "ASANA_PAT_HERE" ```
**验证存储的内容:**
```bash openclaw config get skills.entries.asana openclaw config get skills.entries.asana.enabled openclaw config get skills.entries.asana.apiKey ```
**移除已存储的令牌:**
```bash openclaw config unset skills.entries.asana.apiKey # or openclaw config unset skills.entries.asana.env.ASANA_PAT ```
#### 重要提示:沙盒运行
当会话处于沙盒环境中时,技能进程会在 Docker 内部运行,并且**不会**继承主机环境。在这种情况下,`skills.entries.*.env/apiKey` 仅适用于主机运行。
通过以下方式设置 Docker 环境变量:
- `agents.defaults.sandbox.docker.env`(或按代理设置 `agents.list[].sandbox.docker.env`) - 或将环境变量烘焙到您的沙盒镜像中
## 首次调用(健全性检查与发现)
- 我是谁:
`node {baseDir}/scripts/asana.mjs me`
- 列出工作区:
`node {baseDir}/scripts/asana.mjs workspaces`
- (推荐)一次性设置默认工作区:
`node {baseDir}/scripts/asana.mjs set-default-workspace --workspace <workspace_gid>`
## ID 解析
当用户提供名称(项目/任务/用户)时,使用以下方法之一将其解析为 GID:
- `typeahead --workspace <gid> --resource_type project|task|user --query "..."`(快速,最佳默认选项) - `projects --workspace <gid> --all`(枚举) - `users --workspace <gid> --all`(枚举)
当存在多个匹配项时,请避免猜测 GID。
## 核心:任务
### 列出分配给用户的任务(个人生产力)
`node {baseDir}/scripts/asana.mjs tasks-assigned --assignee me --workspace <workspace_gid> --all`
### 列出项目中的任务
`node {baseDir}/scripts/asana.mjs tasks-in-project --project <project_gid> --all`
### 搜索任务(高级搜索 API)
规范的基元:`search-tasks`(支持许多过滤器;优先于添加狭窄的“搜索辅助”命令)。
单行示例(在项目中搜索):
`node {baseDir}/scripts/asana.mjs search-tasks --workspace <gid> --project <project_gid> --text "..." --all`
有用的过滤器: - `--assignee me|<gid|email>`(映射到 `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`
### 创建 / 更新 / 完成
- 创建:
`node {baseDir}/scripts/asana.mjs create-task --workspace <gid> --name "..." --projects <project_gid> --assignee me`
- 更新:
`node {baseDir}/scripts/asana.mjs update-task <task_gid> --name "..." --due_on 2026-02-01`
- 完成:
`node {baseDir}/scripts/asana.mjs complete-task <task_gid>`
## 项目经理工作流
此技能支持 Asana 中 PM 通常期望的工作流:
- 保持 **项目简报** 为最新状态 (`upsert-project-brief`)
- 撰写 **状态更新** (`create-status-update`)
- 处理 **时间表**(开始/截止日期)并安全地调整计划
- 将 **自定义字段** 作为一等元数据使用
- 解读 **阻碍因素** 和依赖关系图 (`project-blockers`, `dependencies`, `dependents`)
### 项目简报
- 读取:
`node {baseDir}/scripts/asana.mjs project-brief <project_gid>`
- 更新插入(创建或更新):
`node {baseDir}/scripts/asana.mjs upsert-project-brief <project_gid> --title "Project brief" --html_text "<body>...</body>"`
### 状态更新
- 创建:
`node {baseDir}/scripts/asana.mjs create-status-update --parent <project_gid> --status_type on_track --text "Weekly update..."`
- 列出:
`node {baseDir}/scripts/asana.mjs status-updates --parent <project_gid> --all`
### 分区和移动任务
- 列出分区:
`node {baseDir}/scripts/asana.mjs sections --project <project_gid> --all`
- 创建分区:
`node {baseDir}/scripts/asana.mjs create-section --project <project_gid> --name "Blocked"`
#### 将任务添加到项目
命令:`add-task-to-project`
调用 `POST /tasks/{task_gid}/addProject` 并支持可选的分区放置和排序。
示例:
`node {baseDir}/scripts/asana.mjs add-task-to-project <task_gid> --project <project_gid>`
带分区 + 排序:
`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` 和 `--insert_after` 是可选的;如果提供,它们将被传递到请求正文中。)
#### 从项目中移除任务
命令:`remove-task-from-project`
调用 `POST /tasks/{task_gid}/removeProject`。
示例:
`node {baseDir}/scripts/asana.mjs remove-task-from-project <task_gid> --project <project_gid>`
## 自定义字段
自定义字段对于可靠的 PM 自动化至关重要。
- 列出项目的自定义字段:
`node {baseDir}/scripts/asana.mjs project-custom-fields <project_gid> --all`
- 读取自定义字段定义:
`node {baseDir}/scripts/asana.mjs custom-field <custom_field_gid>`
- 在创建/更新时设置任务自定义字段:
`node {baseDir}/scripts/asana.mjs update-task <task_gid> --custom_fields '{"<custom_field_gid>":"<value>"}'`
注意: - 对于枚举类型,值通常是枚举选项的 GID。 - 对于数字,请发送 JSON 数字类型。
## 富文本、提及和可靠性
Asana 富文本字段是包裹在 `<body>` 根元素中的 **符合 XML 规范的 HTML 片段**。API 会拒绝无效的 XML 或不受支持的标签。
关键点: - 对任务描述使用 `html_notes`。 - 对评论/故事和状态更新使用 `html_text`。 - 避免使用不受支持的标签,如 `<p>` 和 `<br>`;首选字面换行符 (`\n`) 和 `<hr/>` 分隔符。 - 对于提及/链接,使用 `<a data-asana-gid="..."></a>`(或自闭合的 `<a .../>`)。
### 提及通知
如果用户未被分配或未关注,创建提及链接并**不**保证通知送达。
为了可靠地进行通知,请执行以下操作之一: - 先分配用户,然后发布评论,或者 - 将用户添加为关注者,等待几秒钟,然后发布评论
此技能支持“添加关注者 + 等待”模式:
`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`
纯文本评论(`--text`)**不会**通过 API 创建真正的 @提及;它们将保持为纯文本。
## 附件、上传和内联图片
- 上传文件附件到任务:
`node {baseDir}/scripts/asana.mjs upload-attachment --parent <task_gid> --file /path/to/file.png`
- 在行内嵌入现有图片附件(仅适用于任务和项目简报):
`node {baseDir}/scripts/asana.mjs append-inline-image --attachment <attachment_gid> --task <task_gid>`
## 活动源 / “类收件箱”工作流
Asana 没有为所有通知提供单一的通用“收件箱” API。最接近的稳定基元是针对特定资源(项目、任务等)范围的 **Events** 端点。
使用: - `events --resource <gid>` 拉取项目上的增量更改(或用户的“My Tasks”项目) - 该命令会在本地存储同步令牌,以便后续运行仅获取更改
## 时间表调整
- 调整单个任务(可选包括子任务):
`node {baseDir}/scripts/asana.mjs shift-task-dates <task_gid> --delta_days 7 --dry_run true`
- 调整整个项目的任务:
`node {baseDir}/scripts/asana.mjs shift-project-tasks --project <project_gid> --delta_days -3 --dry_run true --all`
请先使用 `--dry_run true` 运行,然后使用 `--dry_run false` 重新运行。
## 超出范围
- 产品组合(Premium 功能)有意省略。 - 此处未嵌入“Bot 个性”;请在您的代理提示词中配置行为。