关于 clockify-tracker
通过 HTTPS 调用 Clockify REST API 的时间追踪技能,支持手动填写工时,也可根据工作日志将仓库活动映射到项目。提交前会预览条目供确认。
主要功能
- 01列出工作区和项目
- 02通过 REST API 创建工时条目
- 03将本地时间转换为 UTC
- 04从环境或偏好文件读取 API 密钥
- 05根据工作日志生成条目
- 06提交前预览条目
适用场景
- 01记录手动描述的工作时段
- 02根据日期日志创建工时条目
- 03将仓库活动分配到 Clockify 项目
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ somnio-software/ somnio-ai-tools --skill clockify-tracker安装后在 Claude Code 中输入/clockify-tracker确认已加载。
详细介绍
Clockify 时间跟踪(REST API)
通过对 https://api.clockify.me/api/v1 发送 HTTPS 请求,与 Clockify 集成。不要假定本地存在 clockify_tracker 二进制文件。
API 密钥
按以下优先级顺序解析密钥:
CLOCKIFY_API_KEY环境变量 — 使用echo $CLOCKIFY_API_KEY检查CLOCKIFY_API_KEY_FILE环境变量 — 指向仅包含密钥的文件;使用cat "$CLOCKIFY_API_KEY_FILE" 2>/dev/null | tr -d '[:space:]'读取- Shell 配置文件 — 在常见的 rc 文件中搜索
CLOCKIFY_API_KEY的导出或赋值语句:
使用以下方式从类似grep -h "CLOCKIFY_API_KEY" ~/.zshrc ~/.zprofile ~/.bashrc ~/.bash_profile ~/.profile 2>/dev/null \ | grep -v '^\s*#' | head -1export CLOCKIFY_API_KEY=abc123或CLOCKIFY_API_KEY="abc123"的行中提取值:
如果找到非空值,则静默使用(与该值存在于当前环境中时相同)。sed 's/.*CLOCKIFY_API_KEY[[:space:]]*=[[:space:]]*//' | tr -d '"'"'"' ' - prefs 文件中的
api_key字段(参见下方的CLOCKIFY_PREFS_PATH)— 使用cat <prefs-path> 2>/dev/null读取 - 未找到 → 请用户粘贴其密钥(Clockify → Profile → API),然后询问是否保存:
“要将此密钥保存到 ,以便下次无需再次输入吗?(yes/no)” 如果是,则将
"api_key": "<key>"合并到 prefs 文件中。
切勿回显或记录密钥值。仅在 curl -H "X-Api-Key: $KEY" 命令中使用。
环境
| 变量 | 用途 |
|---|---|
CLOCKIFY_API_KEY | 当前环境中的 API 密钥值。 |
CLOCKIFY_API_KEY_FILE | 密钥所在文件的路径(例如 ~/.secrets/clockify)。适用于密钥管理器或符号链接的保险库文件。 |
CLOCKIFY_PREFS_PATH | prefs JSON 文件的自定义路径。未设置时默认为 ~/.clockify-prefs.json。解析 prefs 路径:${CLOCKIFY_PREFS_PATH:-~/.clockify-prefs.json}。 |
CLOCKIFY_TZ_OFFSET | 相对 UTC 的本地时差,单位为整小时(例如阿根廷使用 -3)。当 prefs 文件中没有时区时作为回退值。 |
要检查的 Shell 配置文件(上方第 3 步):~/.zshrc、~/.zprofile、~/.bashrc、~/.bash_profile、~/.profile。
身份验证
每个请求都需要:
- 请求头:
X-Api-Key: <resolved key> - 请求头:
Content-Type: application/json(用于 POST 请求体)
端点(与先前 CLI 的行为一致)
列出工作区
GET /workspaces
返回工作区的 JSON 数组;每项包含 id 和 name。
列出工作区中的项目
GET /workspaces/{workspaceId}/projects?page-size=5000
返回项目的 JSON 数组;每项包含 id 和 name。
流程: 如果用户可能有多个工作区,先调用 GET /workspaces 并让用户选择(或使用唯一的工作区)。然后列出所选 workspaceId 中的项目。
创建时间条目
POST /workspaces/{workspaceId}/time-entries
JSON 请求体(字段与之前的 CLI 保持一致):
| 字段 | 必填 | 说明 |
|---|---|---|
start | 是 | ISO 8601 UTC 字符串,例如 2025-03-25T12:00:00.000Z |
description | 是 | 工作说明 |
billable | 否 | 布尔值;省略时默认为 false |
end | 否 | ISO 8601 UTC;省略则创建未结束的条目 |
projectId | 否 | Clockify 项目 ID |
将用户本地时间 HH:mm 转换为 UTC start / end
当用户提供 HH:mm 格式的本地时间(并可选提供 YYYY-MM-DD,若未提供,则根据其所在地区的意图默认为今天)时:
- 解析日期
D(年、月、日)和时间部分hour、minute。 - 将
tzOffset设为该本地时区相对于 UTC 的偏移小时数:UTC+5 为+5,UTC-3 为-3。如果已设置CLOCKIFY_TZ_OFFSET,则使用其值(整数小时);否则根据用户所在地区(例如阿根廷 →-3)或上下文推断。 - 构建 UTC 时间:
utc_hour = hour - tzOffset然后将其规范化为日期D上正确的 UTCDateTime(必要时跨日期进位),并使用Z(ISO 8601 UTC)序列化。
示例:
- 本地时间
09:00,日期为2025-03-10,tzOffset = -3→ UTC 小时9 - (-3) = 12→2025-03-10T12:00:00.000Z。 - 如果用户已提供带
T和时区/Z的完整 ISO 字符串,则直接传递,不进行此转换。
多个日历日
对于日期范围(“March 2–6”)或离散日期列表(“March 23, 24, 25, 26 and 27”),每天都创建一个 POST 请求,使用相同的本地时间,并为每个 start(以及任何 end)设置对应日期。未提供年份时,假定为当前日历年。
操作流程
- 确认执行环境中有
CLOCKIFY_API_KEY。 - 如果工作区未知:调用
GET /workspaces;解析workspaceId。 - 如果需要项目:调用
GET /workspaces/{workspaceId}/projects?page-size=5000;解析projectId。 - 收集描述、开始时间(以及可选的结束时间)、必要时的日期,以及不明确时的时区偏移。
- 构建 JSON 并调用
POST /workspaces/{workspaceId}/time-entries。 - 显示 API JSON 响应或清晰的错误信息(状态码 + 响应正文)。
错误
如果状态码表示请求未成功,则读取响应正文并进行调整(ID 无效、权限不足、请求正文格式错误)。官方参考文档:Clockify API 文档。
基于日志的模式
当用户说 “use logs”、“from work logs”、“usa los logs”、“auto-fill from logs” 或类似表达时触发。现有的手动模式(明确提供描述/项目/日期/时间)保持不变。
偏好设置文件
路径:${CLOCKIFY_PREFS_PATH:-~/.clockify-prefs.json}。始终在运行时解析此路径——先检查环境变量,未设置时再使用 ~/.clockify-prefs.json。使用 cat "${CLOCKIFY_PREFS_PATH:-~/.clockify-prefs.json}" 2>/dev/null 读取;写入时用合并后的 JSON 覆盖文件。
{
"api_key": "<clockify-api-key>",
"timezone": { "name": "Buenos Aires, Argentina", "offset": -3 },
"default_start": "09:00",
"default_end": "17:00",
"workspace_id": "<wid>",
"repo_mappings": {
"mini-meta-repo": { "name": "Nubank", "id": "<pid>" },
"somnio-ai-tools": { "name": "Internal Tools", "id": "<pid>" }
}
}
始终将新键合并到现有偏好设置中,绝不要替换整个文件。
分步流程
1. 加载偏好设置 — 读取 ~/.clockify-prefs.json。文件不存在表示没有已保存状态;继续进行首次设置。
2. 首次设置(没有偏好设置文件) — 在询问其他任何内容之前,先询问:
“我们来设置你的偏好。你目前正在使用哪些仓库/项目?每个仓库/项目对应的 Clockify 项目名称是什么? 示例:mini-meta-repo → Nubank,somnio-ai-tools → Internal Tools”
通过 GET /workspaces/{wid}/projects 将 Clockify 项目名称解析为 ID,并保存到偏好设置中。这是唯一一次完整列出项目范围的操作——之后遇到新仓库时,只需询问一次。
3. 询问时间范围 — 如果用户的消息中未提及:
“你希望我查看哪些日期?(例如:今天、本周、May 22–26)”
4. 读取日志文件 — 对时间范围内的每一天:
cat ~/.work-log/YYYY-MM-DD.md 2>/dev/null
文件不存在 → 跳过该日并告知用户。解析所有 ## HH:MM - … (branch) 标题,收集所选日期中不重复的仓库名称。
5. 确认时间范围 — 默认 09:00–17:00(8 h)。如果已有保存的偏好设置,则显示该设置。仅在没有偏好设置或用户明确提到不同时间段时询问是否更改。
6. 时区选择器 — 如果已保存时区,则将其作为默认值提供:
“使用 Buenos Aires(UTC-3)——按 Enter 保持不变,或选择一个数字 / 输入自定义值:”
如果没有已保存的偏好设置,则打印并等待用户输入数字、名称或自由文本:
1. Buenos Aires, Argentina (UTC-3)
2. Montevideo, Uruguay (UTC-3)
3. São Paulo, Brazil (UTC-3)
4. Quito, Ecuador (UTC-5)
5. Lima, Peru (UTC-5)
6. Colombia (UTC-5)
7. Nicaragua (UTC-6)
8. Other — type your city or UTC offset (e.g. "Madrid" or "UTC+1")
如果用户输入的城市或地区不在列表中,请根据你的知识推断其标准 UTC 时差(例如,“Madrid” → 冬季为 UTC+1/夏季为 UTC+2——使用今天日期对应的当前标准时差)。保存前请与用户确认:
“Madrid 的标准时间为 UTC+1——正确吗?”
7. 解析并确定仓库名称 — 日志标题有两种格式:
- Worktree:
## HH:MM - mini-meta-repo/refactor-flutter_http (branch)— 根仓库是/之前的部分 - 主检出:
## HH:MM - somnio-ai-tools (main)— 整个名称都是根仓库
按 / 拆分,提取根仓库名称(用于 Clockify 映射)和 worktree 名称(用于描述上下文)。然后:
- 根仓库在
repo_mappings中 → 静默使用。 - 根仓库不在
repo_mappings中 → 只询问一次:“我看到 mini-meta-repo 中有日志条目——这是哪个 Clockify 项目?”
创建任何条目之前,先汇总询问所有未知仓库。
8. 保存偏好设置 — 所有问题都得到答复后,将答案合并到 ~/.clockify-prefs.json。
9. 处理跨多个项目的工作日 — 对每一天,统计当天的仓库映射到的不同 Clockify 项目数:
- 单个项目 → 为完整时间段创建一个条目。
- 多个项目 → 请用户拆分工时:
“5 月 26 日,你在 Nubank(mini-meta-repo)和 Internal Tools(somnio-ai-tools)上工作。8 小时该如何拆分?(例如:Nubank 7 小时/Internal Tools 1 小时)”
当天每个项目创建一个条目,并分别设置开始和结束时间。
10. 汇总每个条目的日志 — 对于每个(日期,Clockify 项目)组合,收集相关日志条目并生成一份两句话的执行摘要:
- 第一句:主要成果——功能、决策,以及参与的具体 worktree/分支(请注明名称)
- 第二句:关键发现、结果或下一步
目标是提供足够的上下文,让财务读者了解 8 小时的工作内容,同时避免长篇大论。
11. 预览并确认 — 发布任何内容之前,完整展示每个条目,让用户检查描述。格式为编号列表,每项对应一个条目:
Here's what I'll post to Clockify:
1. May 22 — Nubank — 09:00–17:00
refactor-flutter_http: Completed Flutter HTTP migration by updating LogoutInterceptor
to use IRouter and fixing the redirect_interceptor_test setup. Key finding: the
interceptor correctly dispatches the logout deeplink on 403 responses.
2. May 26 — Nubank — 09:00–16:00
refactor-flutter_http: Diagnosed the native catalyst HTTP bypass on both iOS and
Android — authenticated calls go through URLSession/OkHttp, not Dart/Dio, making
LogoutInterceptor.onError structurally unreachable. Proposed and planned Option A
(synthetic DioException) as the only viable path to trigger the interceptor.
3. May 26 — Internal Tools — 16:00–17:00
somnio-ai-tools: Planned and implemented the clockify-tracker log-based mode,
including hook fix for root-repo logging and the repo-to-project mapping system.
Post these entries? (yes / edit / cancel)
请使用编号列表(不要使用表格),确保完整描述可读且不会被截断。如果用户说“编辑”,请询问要修改的条目编号和新的描述,然后重新展示完整预览,再发布。
12. 创建条目 — 每行调用一次 POST /workspaces/{workspaceId}/time-entries,使用现有的 UTC 转换逻辑。展示每个 API 响应。
13. 清理日志文件 — 所有条目均成功发布后,询问是否删除已处理的文件。
检查 ~/.clockify-prefs.json 中的 "auto_cleanup": true。如果已设置,则直接删除并告知用户删除了哪些文件。否则请询问:
“条目已发布。要删除已处理的日志文件吗?
- ~/.work-log/2026-05-26.md
- ~/.work-log/2026-05-27.md (yes / no / always——‘always’ 会保存此偏好设置,以后不再询问)”
- yes — 使用
rm删除这些文件,并逐一确认已删除 - no — 保留文件,不做更改
- always — 删除文件,然后将
"auto_cleanup": true合并到解析后的偏好设置文件(${CLOCKIFY_PREFS_PATH:-~/.clockify-prefs.json})
只有当某个日期的所有条目都成功发布时,才删除该日期的文件。如果部分发布失败,则跳过删除任何包含失败条目的日期对应的文件,并告知用户保留了哪些文件。