Yandex Tracker (Python Client)
Use yandex_tracker_client to interact with Yandex Tracker API v2.
How to work with this skill
Write and execute Python scripts to fulfill user requests. The workflow:
- 1. Write a self-contained Python script that initializes the client, performs all needed API calls, aggregates and formats the result, then prints it.
- Save to
/tmp/tracker_script.py and run: INLINECODE2 - For simple one-liners it's fine to use
python3 -c "...", but prefer a file for anything multi-step.
Aggregation: The API returns lazy iterables — always collect into lists when you need to count, sort, filter, or display summaries. Combine multiple queries in one script (e.g. fetch issues then fetch comments for each, or join data from two queues) rather than making separate tool calls. Print structured output so the result is easy to read.
CODEBLOCK0
Credentials
Required env (declare in skill metadata; set in openclaw.json → env):
- -
TRACKER_TOKEN — Required. Use a least-privilege OAuth token (oauth.yandex.ru) with only Tracker scope, or a temporary IAM token for Yandex Cloud. Do not use broad admin tokens. - One of:
TRACKER_ORG_ID (Yandex 360, numeric) or TRACKER_CLOUD_ORG_ID (Yandex Cloud, string)
Client initialization (boilerplate — always include)
CODEBLOCK1
Get issue by key
CODEBLOCK2
Custom fields
Real queues almost always have custom fields (story points, business fields, etc.). Their keys look like customFieldId (camelCase) and are queue-specific.
CODEBLOCK3
Use issue.as_dict() on a real issue to discover which custom field keys the queue uses before writing update/filter code.
issues.find() — search
CODEBLOCK4
issues.create()
CODEBLOCK5
issue.update()
CODEBLOCK6
issue.transitions — status changes
CODEBLOCK7
issue.comments
CODEBLOCK8
issue.links
CODEBLOCK9
issue.attachments
CODEBLOCK10
issue.worklog
CODEBLOCK11
Queues
CODEBLOCK12
Bulk operations
CODEBLOCK13
Object fields reference
All objects are dynamic — accessing a missing attribute returns None, not AttributeError.
Call .as_dict() on any object to get a plain dict.
Reference fields (status, priority, assignee, queue, type…) are Reference objects — access .id, .display, .key, or .login directly without a second request.
Issue
| Attribute | Notes |
|---|
| INLINECODE20 | INLINECODE21 — INLINECODE22 |
| INLINECODE23 |
str |
|
description |
str \| None |
|
status | Reference →
.id e.g.
'inProgress',
.display localized name |
|
priority | Reference →
.id e.g.
'normal' /
'critical' |
|
type | Reference →
.id e.g.
'bug' /
'task' |
|
queue | Reference →
.key e.g.
'QUEUE' |
|
assignee | Reference \| None →
.login,
.display |
|
reporter | Reference →
.login,
.display |
|
createdBy | Reference →
.login |
|
createdAt |
str ISO-8601 |
|
updatedAt |
str ISO-8601 |
|
deadline |
str \| None — date
'2026-03-01' |
|
tags |
list[str] |
|
followers |
list[Reference] → each
.login |
|
components |
list[Reference] → each
.display |
|
fixVersions |
list[Reference] → each
.display |
|
sprint |
list[Reference] \| None → each
.display |
|
parent | Reference \| None →
.key |
|
votes |
int |
Comment
| Attribute | Notes |
|---|
| INLINECODE75 | INLINECODE76 |
| INLINECODE77 |
str |
|
textHtml |
str |
|
createdBy | Reference →
.login,
.display |
|
createdAt /
updatedAt |
str ISO-8601 |
|
summonees |
list[Reference] |
|
attachments |
list[Reference] |
Link
| Attribute | Notes |
|---|
| INLINECODE91 | INLINECODE92 |
| INLINECODE93 |
Reference →
.id e.g.
'relates',
'blocks',
'is blocked by' |
|
direction |
str —
'inward' /
'outward' |
|
object | Reference →
.key,
.display (the linked issue) |
|
createdBy | Reference →
.login |
|
createdAt |
str ISO-8601 |
Attachment
| Attribute | Notes |
|---|
| INLINECODE109 | INLINECODE110 |
| INLINECODE111 |
str — filename |
|
content |
str — download URL |
|
mimetype |
str |
|
size |
int — bytes |
|
createdBy | Reference →
.login |
|
createdAt |
str ISO-8601 |
Transition
| Attribute | Notes |
|---|
| INLINECODE123 | INLINECODE124 — transition key, e.g. 'close', INLINECODE126 |
| INLINECODE127 |
Reference →
.id,
.display (target status) |
|
screen | Reference \| None |
Worklog entry
| Attribute | Notes |
|---|
| INLINECODE131 | INLINECODE132 |
| INLINECODE133 |
Reference →
.key |
|
comment |
str \| None |
|
start |
str ISO-8601 datetime |
|
duration |
str ISO-8601 duration e.g.
'PT1H30M' |
|
createdBy | Reference →
.login |
|
createdAt |
str ISO-8601 |
Queue
| Attribute | Notes |
|---|
| INLINECODE146 | INLINECODE147 |
| INLINECODE148 |
str |
|
name |
str |
|
description |
str \| None |
|
lead | Reference →
.login,
.display |
|
assignAuto |
bool |
|
defaultType | Reference |
|
defaultPriority | Reference |
|
teamUsers |
list[Reference] → each
.login |
User
| Attribute | Notes |
|---|
| INLINECODE164 | INLINECODE165 |
| INLINECODE166 |
str |
|
firstName /
lastName |
str |
|
display |
str — full name |
|
email |
str |
BulkChange
| Attribute | Notes |
|---|
| INLINECODE175 | INLINECODE176 |
| INLINECODE177 |
str —
'COMPLETE' /
'FAILED' /
'PROCESSING' |
|
statusText |
str |
|
executionChunkPercent |
int |
|
executionIssuePercent |
int |
Users
CODEBLOCK14
Sprints
CODEBLOCK15
Error handling
CODEBLOCK16
Available exception classes: NotFound, Forbidden, BadRequest, Conflict, TrackerClientError (base).
Notes
- -
org_id must be an int; cloud_org_id is a string. - For Yandex Cloud orgs: use
cloud_org_id= instead of org_id=, and optionally iam_token= for temporary IAM tokens instead of token=. - To get valid resolution IDs for a queue:
[r.id for r in client.resolutions.get_all()]. - Print results clearly — use formatted strings, tables, or JSON so the user gets a readable summary.
Yandex Tracker (Python 客户端)
使用 yandextrackerclient 与 Yandex Tracker API v2 进行交互。
如何使用此技能
编写并执行 Python 脚本 以满足用户请求。工作流程如下:
- 1. 编写一个自包含的 Python 脚本,初始化客户端,执行所有必要的 API 调用,汇总并格式化结果,然后打印输出。
- 保存到 /tmp/trackerscript.py 并运行:python3 /tmp/trackerscript.py
- 对于简单的单行命令,可以使用 python3 -c ...,但对于多步骤操作,建议使用文件。
数据聚合: API 返回惰性可迭代对象——当需要计数、排序、过滤或显示摘要时,务必将其收集到列表中。在单个脚本中组合多个查询(例如,先获取问题,再获取每个问题的评论,或合并两个队列的数据),而不是分别调用工具。打印结构化输出,使结果易于阅读。
python
示例:按负责人汇总队列中的问题
issues = list(client.issues.find(filter={queue: QUEUE}, per_page=100))
from collections import Counter
counts = Counter(str(i.assignee) for i in issues)
for assignee, n in counts.most_common():
print(f{n:3d} {assignee})
凭据
必需的环境变量(在技能元数据中声明;在 openclaw.json → env 中设置):
- - TRACKERTOKEN — 必需。 使用仅具有 Tracker 作用域的最小权限 OAuth 令牌(oauth.yandex.ru),或使用 Yandex Cloud 的临时 IAM 令牌。请勿使用宽泛的管理员令牌。
- 以下之一:TRACKERORGID(Yandex 360,数字型)或 TRACKERCLOUDORGID(Yandex Cloud,字符串型)
客户端初始化(样板代码——始终包含)
python
import os
from yandextrackerclient import TrackerClient
token = os.environ[TRACKER_TOKEN]
orgid = os.environ.get(TRACKERORG_ID)
cloudorgid = os.environ.get(TRACKERCLOUDORG_ID)
if cloudorgid:
client = TrackerClient(token=token, cloudorgid=cloudorgid)
else:
client = TrackerClient(token=token, orgid=int(orgid))
通过键获取问题
python
issue = client.issues[QUEUE-42]
print(issue.key, issue.summary, issue.status.id, issue.assignee.login if issue.assignee else None)
自定义字段
实际队列几乎总是包含自定义字段(故事点、业务字段等)。它们的键看起来像 customFieldId(驼峰命名法),并且是队列特定的。
python
读取——通过属性名称访问(与标准字段相同)
print(issue.storyPoints) # 如果不存在则返回 None
print(issue.as_dict()) # 转储所有字段,包括自定义字段——用于发现键
更新
issue.update(storyPoints=5, myCustomField=value)
在 find() 中按自定义字段过滤
issues = client.issues.find(filter={queue: QUEUE, storyPoints: {from: 3}})
发现队列中所有可用的字段(id 是使用的键)
for f in client.fields.get_all():
print(f.id, f.name)
在编写更新/过滤代码之前,使用 issue.as_dict() 处理真实问题,以发现队列使用的自定义字段键。
issues.find() — 搜索
python
Tracker 查询语言字符串(从 Tracker UI 复制)
issues = client.issues.find(Queue: QUEUE Assignee: me() Status: inProgress)
结构化过滤器(字典)
issues = client.issues.find(
filter={
queue: QUEUE, # 队列键
assignee: user_login, # 登录名或 me()
author: user_login,
status: inProgress, # 状态 .id
type: bug, # 问题类型 .id
priority: critical, # 优先级 .id
tags: [backend, urgent], # 所有标签必须匹配
created: {from: 2026-01-01, to: 2026-02-01},
updated: {from: 2026-01-15},
deadline: {to: 2026-03-01},
followers: user_login,
components: component_name,
},
order=[-updatedAt, +priority], # 前缀:- 降序,+ 升序
per_page=100, # 每页最多 100 条;自动分页
)
迭代会自动获取所有页面;用 list() 包装以具体化
issues = list(issues)
按键批量获取特定问题
issues = list(client.issues.find(keys=[QUEUE-1, QUEUE-2, QUEUE-3]))
issues.create()
python
issue = client.issues.create(
queue=QUEUE, # 必需
summary=Bug: login fails, # 必需
type={name: Bug}, # 或 {id: bug} — 使用 client.issuetypes.getall()
description=Steps...,
assignee=user_login,
priority=critical, # id: blocker,critical,major,normal,minor,trivial
followers=[login1, login2],
tags=[backend, urgent],
components=[component_name],
parent=QUEUE-10, # 父问题键
sprint={id: 123}, # 冲刺 id
)
print(issue.key)
issue.update()
python
issue.update(
summary=New title,
description=Updated text,
assignee=other_login,
priority=minor,
# 列表:传递完整替换或变更字典
tags=[new_tag], # 完全替换
tags={add: [tag1], remove: [tag2]}, # 部分变更
followers={add: [login1]},
components={add: [comp], remove: []},
)
issue.transitions — 状态变更
python
始终先列出——转换 ID 是队列特定的,切勿猜测
for t in issue.transitions.get_all():
print(t.id, t.to.id, t.to.display)
执行——所有关键字参数可选
issue.transitions[close].execute(
comment=Fixed in v2.3,
resolution=fixed, # fixed,wontFix,duplicate,invalid,later — 取决于队列
)
issue.comments
python
for c in list(issue.comments.get_all()):
print(c.id, c.createdBy.login, c.text)
创建
issue.comments.create(
text=Fixed in v2.3,
summonees=[login1, login2], # 触发 @提及 通知
attachments=[path/to/file.png], # 文件路径——自动上传,转换为 ID
)
issue.comments[42].update(text=Corrected note, summonees=[login1])
issue.comments[42].delete()
issue.links
python
for link in issue.links:
print(link.type.id, link.direction, link.object.key)
关系值(标准):
relates, blocks, is blocked by,
duplicates, is duplicated by,
depends on, is dependent of,
is subtask for, is parent task for
issue.links.create(issue=OTHER-10, relationship=relates)
issue.links[42].delete()
issue.attachments
python
for a in issue.attachments:
print(a.id, a.name, a.mimetype, a.size)
issue.attachments.create(/path/to/file.txt) # 按路径上传
a.download_to(/tmp/) # 下载到目录
issue.attachments[42].delete()
issue.worklog
python
创建工作日志条目
issue.worklog.create(
duration=PT1H30M, # ISO 8601: PT30M, PT2H, P1D, P1DT2H30M
comment=Fixed auth bug, # 可选
start=2026-02-24T10:00:00+03:00, # 可选,默认为当前时间
)
for w in list(issue.worklog.get_all()):
print(w.id, w.duration, w.comment, w.createdBy.login)
issue.worklog[42].update(duration=PT2H, comment=Revised estimate)
issue.worklog