Files
doc/skills/worklog/SKILL.md
T
jiantw83andClaude Sonnet 5 d31aa53fc3 feat(worklog): worklog.sh 改寫為 worklog.mjs(node 全面模組化)
新增 scripts/worklog/worklog.mjs,逐段對應改寫 worklog.sh:log/nowStr 改
import 自 wiki_api.mjs,extractTurn/turnDuration/turnTokens/redact 改
import 自 transcript.mjs,appendEntry/resolveToken/weekPageName/
weekPageHeader 改 import 自 wiki_api.mjs,取代原本各自開子行程呼叫;
stdin JSON 解析、遞迴防護、啟用檢查、Codex transcript fallback、
WORKLOG_SCOPE 過濾、專案判定、模型決定與快取過期、濃縮 prompt 九條規則
逐字保留,退出碼一律 0。hooks/hooks.json 的 Stop hook 改用 node 執行;
README/worklog SKILL.md/wiki_api.mjs 檔頭註解同步更新指向 worklog.mjs。

舊 worklog.sh 暫未刪除:依規劃需先在 WORKLOG_ENABLED=1 的環境實測 hook
真的會觸發並成功寫入週頁,這個環境目前未設定,留待後續驗證後再處理。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-17 15:12:31 +08:00

172 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: worklog
description: 工作證明自動記錄(worklog)的操作與維護 skill。搭配相容的 Stop hook,把每輪工作內容透過 README 定義的 headless CLI(claude/codex/agy/opencode/copilot)濃縮成精簡條目並追加到 Gitea wiki 的當週工作紀錄頁(Worklog-yyyy-MM-W<週>),工作內容全程不落地。提供 --init(初始化週頁與環境變數指引)、--tune(判定並快取最適合的 Claude 摘要模型)、--diagnose(診斷 hook 為何沒動作)、--append(手動補寫一筆)、--show(讀當週頁回顧)五個模式。當使用者說工作證明、工作紀錄、worklog、週報自動化、把工作內容寫到 wiki、記錄到 Gitea wiki、hook 沒有寫入 wiki、補寫工作紀錄、看本週做了什麼、重新判定摘要模型,或提到 WORKLOG_ENABLED/WORKLOG_HOST/WORKLOG_REPO/WORKLOG_MODEL/WORKLOG_CLI/WORKLOG_SCOPE 時觸發。不適用於:Gitea 議題操作(用 issues-sync/issues)、專案文件化(用 funcs)。
---
# worklog — 工作證明自動記錄
把「每輪做了什麼」濃縮成一則條目,追加到 Gitea wiki 的當週工作紀錄頁。**自動記錄由相容的 `Stop` hook 完成,不需使用者同意、不需人工觸發**;本 skill 負責自動路徑之外的人工操作:初始化、模型判定、診斷、補寫、回顧。
| 元件 | 觸發者 | 職責 |
| --- | --- | --- |
| `hooks/hooks.json` 的 `Stop` hook | harness 自動 | 每輪結束抽本輪內容 → 濃縮 → 遮蔽 → 追加到當週頁 |
| 本 skill `/jsc-doc:worklog` | 使用者/助理手動 | `--init`/`--tune`/`--diagnose`/`--append`/`--show` |
| `scripts/worklog/worklog.mjs` | 上述兩者共用 | 主流程(單一實作,避免漂移):依 `WORKLOG_CLI` 呼叫 headless CLI,每筆整理成七個固定欄位(含 token 用量) |
| `scripts/worklog/wiki_api.mjs` | 上述兩者共用 | token 解析、wiki 讀寫、append 重試、週頁命名 |
| `scripts/worklog/transcript.mjs` | 上述兩者共用 | 抽本輪片段、估算花費時間、統計 token 用量、機密遮蔽 |
### 各助理支援範圍
| 功能 | Claude Code | Codex | Antigravity | OpenCode | GitHub Copilot |
| --- | --- | --- | --- | --- | --- |
| `Stop` hook 自動記錄 | ✅ | ✅ 需可讀 Codex session JSONL | ❌ 無 hook 機制 | ❌ 無 hook 機制 | ✅(2026/08/11 實測確認,見下方說明) |
| `--init`/`--diagnose`/`--append`/`--show` | ✅ | ⚠️ 需 plugin 目錄保留 `scripts/`(安裝後請實測一次) | ⚠️ 同左 | ⚠️ 需完整 plugin 目錄 | ⚠️ 需 plugin 目錄保留 `scripts/` |
| 摘要 CLI | `claude -p` | `codex exec` | `agy -p` | `opencode run` | `copilot -p` |
| `--tune` | ✅ | ❌ 無 `claude-api` skill 可載入 | ❌ 同左 | ❌ | ❌ |
| token 用量統計 | ✅ 輸入/輸出皆可得 | ✅ 輸入/輸出皆可得 | ❌ 無 transcript 解析器 | ❌ 無 transcript 解析器 | ⚠️ 只有輸出,輸入固定「未判定」(見下方說明) |
限制與實測結果的來源:
- **`Stop` hook 只有相容 hook 環境實際執行**;Claude Code 先用 `CLAUDE_PLUGIN_ROOT` 定位腳本,找不到時依序掃 `~/.claude/plugins/cache`、`~/.codex/plugins/cache`、`~/.copilot/installed-plugins`,最後命中 `*/jsc-doc/*/scripts/worklog/worklog.mjs`。`transcript.mjs` 支援 Claude Code transcript JSONL(`type` / `message.content` blocks)、Codex session JSONL(`payload` events / response items)與 Copilot events.jsonl(`type` 為 `user.message`/`assistant.message`/`tool.execution_complete` 的 `data.*` 欄位),其他助理若提供等效 hook,必須先補對應 transcript 解析器。
- **Copilot 支援細節(2026/08/11 實測)**:Copilot CLI 內部事件名稱是 `agentStop`(不是 `Stop`),欄位為 camelCase(`sessionId`/`transcriptPath`,只有 `stop_hook_active` 例外仍是 snake_case);但實測確認 **Copilot 的 plugin 載入器會把 `hooks/hooks.json` 裡的 `Stop` key 自動對應到它自己的 `agentStop` 事件**,本檔不需要另外宣告 `agentStop` key。transcript 路徑固定為 `~/.copilot/session-state/<sessionId>/events.jsonl`,且 hook payload 直接帶 `transcriptPath`,不需要像 Codex 分支那樣自己用 session id 反查檔案。**Copilot 逐輪只記錄 `assistant.message.data.outputTokens`,沒有對應的輸入 token 欄位**;`session.shutdown.modelMetrics` 雖然有完整輸入/輸出,但那是整個 session 結束才寫的累計值,語意不是「本輪」,故 Copilot 的輸入 token 一律固定輸出「未判定」,不得用該欄位冒充本輪數字。
- **Antigravity(`agy`)不支援自動記錄的兩個具體原因**:(1) `agy --help` 沒有任何 hook 相關子指令或設定項,CLI 本身不提供事件觸發點(觸發點缺);(2) 就算有觸發點,`agy` 的對話記錄存在 `~/.gemini/antigravity-cli/conversations/*.db` 內,內容是 `step_payload`/`gen_metadata` 等欄位的 protobuf 二進位 blob、`step_type` 是數字 enum,沒有公開 `.proto` schema 可解析(資料缺)。兩個條件都不成立,之後要支援得兩者都解決,不是單純補一支 transcript 解析器就好。
- **OpenCode 不支援自動記錄的原因**:目前的 skill 目錄安裝法本來就不含 `scripts/`,且其 CLI 同樣未見 hook 機制文件;若之後提供對應的 hook 機制與可讀的 transcript 格式,才有辦法補上,純粹「以完整 plugin 目錄執行」不足以讓自動記錄運作。
- **OpenCode 以「複製 `skills/` 目錄」安裝**時不會帶入 `scripts/`,本 skill 的所有模式都無法執行;若以完整 plugin 目錄執行並能解析 `scripts/worklog`,可用 `WORKLOG_CLI=opencode` 作為摘要 CLI。
- 其他助理若要用 `--append`/`--show` 等純 wiki 操作,只需 `node`;摘要路徑需要 README 定義的任一 headless CLI。`--tune` 仍是 Claude Code 專屬,其他 CLI 使用各自預設模型或手動設定其 CLI 行為。
### 腳本路徑解析(重要)
依 `/jsc-shared:spec-script-path`(寫法一:多行版)解析 plugin 根目錄,全文以 `${WORKLOG_DIR}` 表示該目錄:
```bash
# Claude Code
WORKLOG_DIR="${CLAUDE_PLUGIN_ROOT}/scripts/worklog"
# 其他助理:以 skill base directory 推導(<base>/../.. 即 plugin 根)
WORKLOG_DIR="<skill base directory>/../../scripts/worklog"
```
若解析不到或該目錄不存在,依 spec-script-path 的標準錯誤處理回報並停止,不要改用相對路徑重試。
---
## 共用規範(必要前置)
先載入 `/jsc-shared:spec-preflight` 並依其流程處理;載入不到即代表 shared plugin 未安裝,
依該 spec 詢問使用者是否安裝 `https://gitea.jsc.idv.tw/plugins/shared.git`,不安裝則中斷本 skill。
本 skill 需要的規範:`spec-version-guard`、`spec-output`、`spec-execution`、`spec-gitea`、`spec-time-log`、`spec-no-scratch-files`、`spec-script-path`、`spec-skill-invocation`、`spec-model`
本 skill 特有補充:
- **工作內容不落地**:依 `/jsc-shared:spec-no-scratch-files` 執行(transcript 片段只在程序記憶體與 stdin/stdout 間傳遞、wiki 走 API 不 clone,全程不產生暫存檔)。**本 skill 特有例外**:唯一允許落地的是**模型快取檔** `~/.claude/worklog/model`(僅含模型 id 與判定時間,不含任何工作內容)。
- **絕不阻斷**:hook 路徑任何失敗都以 exit 0 結束,只在 stderr 留訊息。
---
## 環境變數
| 變數 | 必要 | 說明 | 未設定 |
| --- | --- | --- | --- |
| `WORKLOG_ENABLED` | ✅ | 總開關,設為 `1` 才啟用 | hook 立即結束,完全不動作 |
| `WORKLOG_HOST` | ✅ | Gitea 主機,如 `gitea.housefun.com.tw` | 不啟用 |
| `WORKLOG_REPO` | ✅ | wiki 所在 repo,如 `H3285/WorkLog` | 不啟用 |
| `WORKLOG_MODEL` | | 強制指定摘要模型 | 讀快取檔 → 保底 `claude-haiku-4-5-20251001` |
| `WORKLOG_CLI` | | 摘要執行器:`auto`/`claude`/`codex`/`agy`/`opencode`/`copilot` | `auto`,先依目前 hook/session 環境判斷正在使用的 CLI,判斷不到或該 CLI 不可執行時才 fallback 到已安裝工具 |
| `WORKLOG_SCOPE` | | 冒號分隔的路徑前綴,僅這些路徑下的 session 才記 | 全部 session 都記 |
| `WORKLOG_ERRLOG` | | 錯誤訊息額外寫入的檔案路徑(只記錯誤、不含工作內容) | 只走 stderr |
**token 不需另設變數**,依 /jsc-shared:spec-gitea 的『token 解析優先序』自動解析並實際驗證(worklog 沒有專用變數,直接從 GITEA_TOKEN 開始)。
---
## 模式
### `--init`
1. 執行 `node "${WORKLOG_DIR}/wiki_api.mjs" probe`,回報 token 來源、Gitea 版本、當週頁狀態。
2. 當週頁不存在 → 執行 `node "${WORKLOG_DIR}/wiki_api.mjs" init` 建立(wiki 尚未初始化時一併初始化)。
3. 以表格印出應寫入 `~/.bashrc` 的 `WORKLOG_*` 變數清單;**不自動改使用者的 shell profile**(需人工確認的狀態變更)。
### `--tune`(Claude Code 專屬)
決定「目前最適合的摘要模型」並快取,`Stop` hook 只讀快取、**絕不自行呼叫 AI 判斷**(否則就變成雞生蛋,還會拖慢使用者的等待路徑)。
本模式需要 Claude Code 內建的 `claude-api` skill 與 `claude` CLI,**其他助理無法執行**:請改為手動設定 `WORKLOG_MODEL` 環境變數指定模型,或沿用保底模型。
| 步驟 | 動作 |
| --- | --- |
| 1 | 以 Skill 工具呼叫 `/jsc-shared:models --task summary` 取得推薦模型——探測、評分(延遲敏感、輕量摘要、低成本等 `spec-model` 對映表定義的必要標籤)、smoke test 全由該 skill 依 `/jsc-shared:spec-model` 完成,本模式不重寫這套邏輯、不自行載入 `claude-api` 或另跑 smoke test |
| 2 | 取推薦結果的模型 id 與一行理由,寫入 worklog 專屬快取檔 `~/.claude/worklog/model`(`model=<id>`、`tuned_at=<時間>`、`reason=<一行理由>`)——此檔與 `models` skill 自身的 `~/.claude/jsc/models.json` 快取用途不同(前者是 worklog hook 專讀的精簡快取,後者是全模型清單快取),兩者不合併、不互相讀取、不共用格式 |
| 3 | 回報選擇與理由 |
快取超過 **30 天** 視為過期:hook 改用保底模型 `claude-haiku-4-5-20251001`,並在條目標記 `(model: fallback)`,`--diagnose` 會提醒重跑 `--tune`。
### `--diagnose`
逐項檢查並以表格回報,用於「hook 沒有寫入 wiki」時定位:
| 檢查項 | 判準 |
| --- | --- |
| `node`/摘要 CLI | `node` 與 `WORKLOG_CLI` 指定或 auto 選到的 CLI 是否找得到 |
| `WORKLOG_*` 變數 | 必要三項是否齊全、`WORKLOG_SCOPE` 是否把當前路徑排除 |
| `scripts/worklog` 目錄 | `${WORKLOG_DIR}` 是否解析成功且三支腳本存在(不存在=此助理不支援) |
| token | `node "${WORKLOG_DIR}/wiki_api.mjs" probe` 的 token 來源與驗證結果 |
| wiki API | Gitea 版本、`repos/<repo>` 與當週頁狀態 |
| 摘要設定 | `WORKLOG_CLI`、選到的 CLI、Claude 模型快取是否存在與是否過期 |
| hook 註冊 | `hooks/hooks.json` 是否存在且 plugin 已啟用 |
### `--append "<內容>"`
手動補寫一筆(hook 漏記、離線工作、或事後補充)。條目格式與自動路徑一致:
```
## <時間> — <專案> <!-- worklog:<時間戳>-manual -->
- 專案/任務名稱:<專案或任務>
- 執行細節與產出:<做了什麼、動到什麼、產出為何>
- 花費時間:<實際耗時或未判定>
- 任務狀態:<完成/進行中/待確認/受阻>
- 遇到的困難:<困難或未遇到明確困難>
- 解決方式:<處理方式或不需額外處理>
- token 用量:<輸入/輸出或未判定>
```
專案取當前工作目錄的 `<owner>/<repo>`;內容仍會過 `node "${WORKLOG_DIR}/transcript.mjs" redact` 遮蔽後才寫入。手動補寫沒有 transcript 可解析,`token 用量` 固定寫「未判定」。
### `--show`
讀當週頁(`node "${WORKLOG_DIR}/wiki_api.mjs" show`)並以表格摘要本週工作,用於回顧與週報。
---
## 條目與頁面格式
- **週以星期六起算(六~五)**,因此換頁一律發生在星期六,與星期對齊。
- 週頁名稱:`Worklog-<yyyy>-<MM>-W<n>`,`n` =該週起始的星期六是當月第幾個星期六(例:`2026/07/29 三` 屬於 `07/25 六` 那一週 → `Worklog-2026-07-W4`)。
- 跨月的一週歸屬**起始星期六**所在的月份,確保同一週只有一頁(例:`2026/08/29 六 ~ 09/04 五` 全部寫入 `Worklog-2026-08-W5`)。
- 頁首標題:`# <yyyy> 年 <MM> 月 第 <n> 週工作紀錄(<起始日> 六 ~ <結束日> 五)`,日期範圍讓人一眼看出這頁涵蓋哪幾天。
- 每筆條目:`## <時間> — <專案>` +七個固定 bullet(專案/任務名稱、執行細節與產出、花費時間、任務狀態、遇到的困難、解決方式、token 用量);標題行尾帶 HTML 註解 marker(`<!-- worklog:… -->`)供寫後驗證與去重,wiki 渲染時不顯示。
- 多 session 同時寫入:`append_entry` 採「讀取 → 合併 → 寫回 → 寫後讀取驗證 marker」,未落地則重讀最新內容重試,最多 3 次。
---
## 機密與 PII(兩道防線)
| 防線 | 位置 | 內容 |
| --- | --- | --- |
| 1 | 濃縮提示詞 | 明令不得輸出 token/密碼/API key/連線字串/Email/電話/姓名/身分證號 |
| 2 | `transcript.mjs` 的 `redact` | 正則遮蔽:URL 內嵌憑證、40 字元 hex token、`gh?_`/`sk-` token、`token=`/`password=`、`Authorization:`、Email、台灣手機、身分證號 |
第二道防線不可移除 —— 模型有可能沒遵守指令,而 wiki 一旦寫入就留在 git 歷史裡。
---
## 呼叫方式
依 `/jsc-shared:spec-skill-invocation` 的統一呼叫方式,本 skill 的實際參數格式與範例:
| 助理 | 呼叫 |
| --- | --- |
| Claude Code / Antigravity | `/jsc-doc:worklog --init`、`/jsc-doc:worklog --tune`、`/jsc-doc:worklog --diagnose`、`/jsc-doc:worklog --append "修正 X 的 Y 問題"`、`/jsc-doc:worklog --show` |
| Codex | `$worklog --diagnose`,或用 `/skills` 選單;可設 `WORKLOG_CLI=codex` |
| OpenCode / GitHub Copilot | 需完整 plugin 目錄保留 `scripts/`;可設 `WORKLOG_CLI=opencode` 或 `WORKLOG_CLI=copilot` |