fix(worklog): 修正 Codex stop hook 執行路徑 #40
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "jsc",
|
"name": "jsc",
|
||||||
"version": "0.2.4",
|
"version": "0.2.5",
|
||||||
"description": "JSC 文件化 skills(Claude Code / Codex / Antigravity / OpenCode):doc-docker 會整理 docker-compose.yaml 的行內註解與標題日期;doc-funcs 會為專案 functions 建立 .docs 草稿、補齊 XML 文件註解並重建 README 功能列表與使用範例;doc-issues-analyze-to-file 會讀取 Gitea issue、彙整需求、拆成多階段 issue 並產生實作草稿與交付留言;doc-issues-analyze 會把專案/議題/文件來源(議題連同留言與附件一起讀取)拆成小功能議題(母議題須待所有子議題關閉後才可關閉)、分析完成後把屬於專案看板的議題移到「待處理」欄位並依到期日實作;doc-issues-sync 會讀取 Gitea 專案或議題、依工作目錄檔案勾稽並同步議題的 TODO 進度、標籤與專案看板進度欄位並產生進度留言(指定關閉專案/專案完成時改為批次把專案所有議題搬到「已完成」並關閉);worklog 會在 Claude Code Stop hook 將每輪工作整理成六欄工作紀錄並追加到 Gitea wiki。所有 skills 以 SKILL.md 為共通標準,於 Claude Code 以 /jsc: 前綴呼叫。",
|
"description": "JSC 文件化 skills(Claude Code / Codex / Antigravity / OpenCode / GitHub Copilot):doc-docker 會整理 docker-compose.yaml 的行內註解與標題日期;doc-funcs 會為專案 functions 建立 .docs 草稿、補齊 XML 文件註解並重建 README 功能列表與使用範例;doc-issues-analyze-to-file 會讀取 Gitea issue、彙整需求、拆成多階段 issue 並產生實作草稿與交付留言;doc-issues-analyze 會把專案/議題/文件來源(議題連同留言與附件一起讀取)拆成小功能議題(母議題須待所有子議題關閉後才可關閉)、分析完成後把屬於專案看板的議題移到「待處理」欄位並依到期日實作;doc-issues-sync 會讀取 Gitea 專案或議題、依工作目錄檔案勾稽並同步議題的 TODO 進度、標籤與專案看板進度欄位並產生進度留言(指定關閉專案/專案完成時改為批次把專案所有議題搬到「已完成」並關閉);worklog 會以 README 定義的 headless CLI 將每輪工作整理成六欄工作紀錄並追加到 Gitea wiki。所有 skills 以 SKILL.md 為共通標準,於 Claude Code 以 /jsc: 前綴呼叫。",
|
||||||
"skills": "./skills",
|
"skills": "./skills",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "JSC"
|
"name": "JSC"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jsc",
|
"name": "jsc",
|
||||||
"version": "0.2.4",
|
"version": "0.2.5",
|
||||||
"description": "JSC 文件化 skills:doc-docker 會整理 docker-compose.yaml 的行內註解與標題日期;doc-funcs 會為專案 functions 建立 .docs 草稿、補齊 XML 文件註解並重建 README 功能列表與使用範例;doc-issues-analyze-to-file 會讀取 Gitea issue、彙整需求、拆成多階段 issue 並產生實作草稿與交付留言;doc-issues-analyze 會把專案/議題/文件來源(議題連同留言與附件一起讀取)拆成小功能議題(母議題須待所有子議題關閉後才可關閉)、分析完成後把屬於專案看板的議題移到「待處理」欄位並依到期日實作;doc-issues-sync 會讀取 Gitea 專案或議題、依工作目錄檔案勾稽並同步議題的 TODO 進度、標籤與專案看板進度欄位並產生進度留言(指定關閉專案/專案完成時改為批次把專案所有議題搬到「已完成」並關閉);worklog 會將每輪工作整理成六欄工作紀錄並追加到 Gitea wiki(自動 Stop hook 僅 Claude Code 支援)。所有 skills 以 SKILL.md 為共通標準。",
|
"description": "JSC 文件化 skills:doc-docker 會整理 docker-compose.yaml 的行內註解與標題日期;doc-funcs 會為專案 functions 建立 .docs 草稿、補齊 XML 文件註解並重建 README 功能列表與使用範例;doc-issues-analyze-to-file 會讀取 Gitea issue、彙整需求、拆成多階段 issue 並產生實作草稿與交付留言;doc-issues-analyze 會把專案/議題/文件來源(議題連同留言與附件一起讀取)拆成小功能議題(母議題須待所有子議題關閉後才可關閉)、分析完成後把屬於專案看板的議題移到「待處理」欄位並依到期日實作;doc-issues-sync 會讀取 Gitea 專案或議題、依工作目錄檔案勾稽並同步議題的 TODO 進度、標籤與專案看板進度欄位並產生進度留言(指定關閉專案/專案完成時改為批次把專案所有議題搬到「已完成」並關閉);worklog 會以 README 定義的 headless CLI 將每輪工作整理成六欄工作紀錄並追加到 Gitea wiki(自動 Stop hook 僅相容 hook 環境支援)。所有 skills 以 SKILL.md 為共通標準。",
|
||||||
"skills": "./skills"
|
"skills": "./skills"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# jsc — 跨 AI 助理文件化 Skill 集合
|
# jsc — 跨 AI 助理文件化 Skill 集合
|
||||||
|
|
||||||
一個可同時被 **Claude Code、Codex、Antigravity、OpenCode、GitHub Copilot** 使用的文件化 skill 集合。
|
一個可同時被 **Claude Code、Codex、Antigravity、OpenCode、GitHub Copilot** 使用的文件化 skill 集合。
|
||||||
目前內含六個實作型 skills:`doc-docker` 用於整理 `docker-compose.yaml` 的行內註解與標題日期;`doc-funcs` 用於掃描專案 functions、建立 `.docs/` 草稿、補齊 XML 文件註解並重建 README 功能列表與使用範例;`doc-issues-analyze-to-file` 用於讀取 Gitea issue、彙整需求並拆成多階段 issue、產生實作草稿與交付留言;`doc-issues-analyze` 用於把專案/議題/文件來源(議題連同留言與附件一起讀取)拆成小功能議題(母議題須待所有子議題關閉後才可關閉)、分析完成後把屬於專案看板的議題移到「待處理」欄位並依到期日排序留言(不實作程式碼,實作交由 code plugin 的 code-issues);`doc-issues-sync` 用於讀取 Gitea 專案或議題,依工作目錄檔案勾稽並同步議題的 TODO 進度、標籤與專案看板進度欄位、產生進度留言;指定「關閉專案/專案完成」時改為批次把專案所有議題搬到「已完成」並關閉;`worklog` 用於把 Claude Code 每次 session stop 的內容整理成六欄工作紀錄並追加到 Gitea wiki。
|
目前內含六個實作型 skills:`doc-docker` 用於整理 `docker-compose.yaml` 的行內註解與標題日期;`doc-funcs` 用於掃描專案 functions、建立 `.docs/` 草稿、補齊 XML 文件註解並重建 README 功能列表與使用範例;`doc-issues-analyze-to-file` 用於讀取 Gitea issue、彙整需求並拆成多階段 issue、產生實作草稿與交付留言;`doc-issues-analyze` 用於把專案/議題/文件來源(議題連同留言與附件一起讀取)拆成小功能議題(母議題須待所有子議題關閉後才可關閉)、分析完成後把屬於專案看板的議題移到「待處理」欄位並依到期日排序留言(不實作程式碼,實作交由 code plugin 的 code-issues);`doc-issues-sync` 用於讀取 Gitea 專案或議題,依工作目錄檔案勾稽並同步議題的 TODO 進度、標籤與專案看板進度欄位、產生進度留言;指定「關閉專案/專案完成」時改為批次把專案所有議題搬到「已完成」並關閉;`worklog` 用於把 session stop 的內容透過 README 定義的 headless CLI 整理成六欄工作紀錄並追加到 Gitea wiki。
|
||||||
核心是以 [Agent Skills(`SKILL.md`)](https://agentskills.io) 標準撰寫的共用 skills(唯一真實來源放在 `skills/`),
|
核心是以 [Agent Skills(`SKILL.md`)](https://agentskills.io) 標準撰寫的共用 skills(唯一真實來源放在 `skills/`),
|
||||||
搭配各助理各自的 plugin manifest,讓**同一個 repo** 可用各家**原生 plugin CLI** 安裝。
|
搭配各助理各自的 plugin manifest,讓**同一個 repo** 可用各家**原生 plugin CLI** 安裝。
|
||||||
在 Claude Code 與 Antigravity 中,skill 以 **`/jsc:` 前綴**呼叫(例如 `/jsc:doc-docker`)。
|
在 Claude Code 與 Antigravity 中,skill 以 **`/jsc:` 前綴**呼叫(例如 `/jsc:doc-docker`)。
|
||||||
@@ -37,7 +37,7 @@ doc/
|
|||||||
│ └── marketplace.json # Codex marketplace(name: "doc",url source 指向本 repo)
|
│ └── marketplace.json # Codex marketplace(name: "doc",url source 指向本 repo)
|
||||||
├── plugin.json # Antigravity 外掛定義(name: "jsc",skills: "./skills/")
|
├── plugin.json # Antigravity 外掛定義(name: "jsc",skills: "./skills/")
|
||||||
├── hooks/
|
├── hooks/
|
||||||
│ └── hooks.json # Stop → worklog;只有 Claude Code 環境實際執行,其他助理 no-op
|
│ └── hooks.json # Stop → worklog;Claude 用 plugin root,Codex fallback 到安裝 cache
|
||||||
├── scripts/
|
├── scripts/
|
||||||
│ └── worklog/ # worklog 自動記錄的可執行元件(skill 與 hook 共用)
|
│ └── worklog/ # worklog 自動記錄的可執行元件(skill 與 hook 共用)
|
||||||
│ ├── worklog.sh # 主流程:抽本輪 → 濃縮成六欄 → 遮蔽 → 追加到 wiki
|
│ ├── worklog.sh # 主流程:抽本輪 → 濃縮成六欄 → 遮蔽 → 追加到 wiki
|
||||||
@@ -62,10 +62,11 @@ doc/
|
|||||||
|
|
||||||
| 功能 | Claude Code | Codex | Antigravity | OpenCode | GitHub Copilot |
|
| 功能 | Claude Code | Codex | Antigravity | OpenCode | GitHub Copilot |
|
||||||
| --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- |
|
||||||
| `Stop` hook 自動記錄 | ✅ | ⚠️ 會載入但 no-op | ❌ | ❌ | ❌ |
|
| `Stop` hook 自動記錄 | ✅ | ✅ 需可讀 Codex session JSONL | ❌ | ❌ | ❌ |
|
||||||
| `worklog` 手動模式 | ✅ | ⚠️ 需安裝後保留 `scripts/` | ⚠️ 同左 | ❌ | ❌ |
|
| `worklog` 手動模式 | ✅ | ⚠️ 需安裝後保留 `scripts/` | ⚠️ 同左 | ⚠️ 需完整 plugin 目錄 | ⚠️ 需安裝後保留 `scripts/` |
|
||||||
|
| 摘要 CLI | `claude -p` | `codex exec` | `agy -p` | `opencode run` | `copilot -p` |
|
||||||
|
|
||||||
`worklog` 自動記錄依賴 Claude Code 的 Stop hook 與 transcript JSONL 結構;Codex 會載入 plugin hook 定義,因此 hook command 會先檢查 `CLAUDE_PLUGIN_ROOT`,非 Claude Code 環境直接 no-op。
|
`worklog` 自動記錄仍依賴相容的 Stop hook 與 transcript JSONL 結構;Claude Code 會用 `CLAUDE_PLUGIN_ROOT` 定位腳本,Codex 會從 `~/.codex/plugins/cache/doc/jsc` 找已安裝的 worklog 腳本並解析 Codex session JSONL。摘要執行器可用 `WORKLOG_CLI=auto|claude|codex|agy|opencode|copilot` 指定,預設依 README 的 headless CLI 順序自動挑選已安裝工具。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -157,7 +158,7 @@ cp -r ~/jsc-plugin/skills/* ~/.config/opencode/skills/
|
|||||||
rm -rf ~/.config/opencode/skills/doc-docker ~/.config/opencode/skills/doc-funcs ~/.config/opencode/skills/doc-issues-analyze-to-file ~/.config/opencode/skills/doc-issues-analyze ~/.config/opencode/skills/doc-issues-sync ~/.config/opencode/skills/worklog
|
rm -rf ~/.config/opencode/skills/doc-docker ~/.config/opencode/skills/doc-funcs ~/.config/opencode/skills/doc-issues-analyze-to-file ~/.config/opencode/skills/doc-issues-analyze ~/.config/opencode/skills/doc-issues-sync ~/.config/opencode/skills/worklog
|
||||||
```
|
```
|
||||||
|
|
||||||
> **worklog 在 OpenCode 不可用**:上面的複製只帶 `skills/`,不含 `scripts/` 與 `hooks/`,worklog 的所有模式都會失敗。
|
> **worklog 在 OpenCode 的 skills 目錄安裝不可用**:上面的複製只帶 `skills/`,不含 `scripts/` 與 `hooks/`,worklog 的所有模式都會失敗;若以完整 plugin 目錄執行並能解析 `scripts/worklog`,可用 `WORKLOG_CLI=opencode` 作為摘要 CLI。
|
||||||
|
|
||||||
> **Windows PowerShell**:`cp -r A B` → `Copy-Item A B -Recurse -Force`、`rm -rf X` → `Remove-Item X -Recurse -Force`、`~` → `$HOME`。
|
> **Windows PowerShell**:`cp -r A B` → `Copy-Item A B -Recurse -Force`、`rm -rf X` → `Remove-Item X -Recurse -Force`、`~` → `$HOME`。
|
||||||
|
|
||||||
@@ -184,7 +185,7 @@ copilot plugin marketplace remove doc
|
|||||||
- 安裝 token `jsc@doc` = plugin 名(plugin manifest 的 `name`)@ marketplace 名。
|
- 安裝 token `jsc@doc` = plugin 名(plugin manifest 的 `name`)@ marketplace 名。
|
||||||
- `copilot plugin marketplace add` 支援 GitHub `owner/repo`、git URL 與本地路徑;Gitea repo 可用上方 HTTPS URL。
|
- `copilot plugin marketplace add` 支援 GitHub `owner/repo`、git URL 與本地路徑;Gitea repo 可用上方 HTTPS URL。
|
||||||
- **呼叫**:在 Copilot CLI 中用自然語言描述需求,例如 `copilot -i "請使用 doc-docker 整理 docker-compose 註解"`。
|
- **呼叫**:在 Copilot CLI 中用自然語言描述需求,例如 `copilot -i "請使用 doc-docker 整理 docker-compose 註解"`。
|
||||||
- `worklog` 的 `Stop` hook 自動記錄仍只有 Claude Code 環境會實際執行;Copilot CLI 可安裝 skill,但不會執行 Claude Code hook。
|
- `worklog` 的 `Stop` hook 自動記錄仍只有相容 hook 環境會實際執行;Copilot CLI 可作為 `WORKLOG_CLI=copilot` 摘要執行器,但不會執行 Claude Code hook。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -256,11 +257,11 @@ copilot plugin marketplace remove doc
|
|||||||
|
|
||||||
### `worklog`
|
### `worklog`
|
||||||
|
|
||||||
工作證明自動記錄與手動維護流程。Claude Code 的 `Stop` hook 會把每輪工作整理成六個固定欄位:專案/任務名稱、執行細節與產出、花費時間、任務狀態、遇到的困難、解決方式,並追加到 Gitea wiki 當週頁(`Worklog-yyyy-MM-W<週>`)。手動模式提供 `--init`、`--tune`(Claude Code 專屬)、`--diagnose`、`--append`、`--show`。
|
工作證明自動記錄與手動維護流程。相容的 `Stop` hook 會把每輪工作透過 `WORKLOG_CLI` 指定的 headless CLI 整理成六個固定欄位:專案/任務名稱、執行細節與產出、花費時間、任務狀態、遇到的困難、解決方式,並追加到 Gitea wiki 當週頁(`Worklog-yyyy-MM-W<週>`)。手動模式提供 `--init`、`--tune`(Claude Code 專屬)、`--diagnose`、`--append`、`--show`。
|
||||||
|
|
||||||
- **Claude Code / Antigravity**:`/jsc:worklog --diagnose`
|
- **Claude Code / Antigravity**:`/jsc:worklog --diagnose`
|
||||||
- **Codex**:`$worklog --diagnose`,或用 `/skills` 選單
|
- **Codex**:`$worklog --diagnose`,或用 `/skills` 選單
|
||||||
- **OpenCode / GitHub Copilot**:不支援(不會帶入或執行 `scripts/` 與 `hooks/`)
|
- **OpenCode / GitHub Copilot**:需完整 plugin 目錄保留 `scripts/`;可用 `WORKLOG_CLI=opencode` 或 `WORKLOG_CLI=copilot` 作為摘要 CLI
|
||||||
|
|
||||||
<!-- JSC-SKILLS:END -->
|
<!-- JSC-SKILLS:END -->
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "if [ -n \"${CLAUDE_PLUGIN_ROOT:-}\" ]; then \"${CLAUDE_PLUGIN_ROOT}/scripts/worklog/worklog.sh\"; fi",
|
"command": "root=\"${CLAUDE_PLUGIN_ROOT:-}\"; if [ -z \"$root\" ]; then script=$(find \"$HOME/.codex/plugins/cache/doc/jsc\" -path '*/scripts/worklog/worklog.sh' -type f 2>/dev/null | sort -V | tail -n 1); [ -n \"$script\" ] && exec \"$script\"; else exec \"$root/scripts/worklog/worklog.sh\"; fi",
|
||||||
"timeout": 60
|
"timeout": 60
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jsc",
|
"name": "jsc",
|
||||||
"version": "0.2.4",
|
"version": "0.2.5",
|
||||||
"description": "JSC 文件化 skills:doc-docker 會整理 docker-compose.yaml 的行內註解與標題日期;doc-funcs 會為專案 functions 建立 .docs 草稿、補齊 XML 文件註解並重建 README 功能列表與使用範例;doc-issues-analyze-to-file 會讀取 Gitea issue、彙整需求、拆成多階段 issue 並產生實作草稿與交付留言;doc-issues-analyze 會把專案/議題/文件來源(議題連同留言與附件一起讀取)拆成小功能議題(母議題須待所有子議題關閉後才可關閉)、分析完成後把屬於專案看板的議題移到「待處理」欄位並依到期日實作;doc-issues-sync 會讀取 Gitea 專案或議題、依工作目錄檔案勾稽並同步議題的 TODO 進度、標籤與專案看板進度欄位並產生進度留言(指定關閉專案/專案完成時改為批次把專案所有議題搬到「已完成」並關閉);worklog 會在 Claude Code Stop hook 將每輪工作整理成六欄工作紀錄並追加到 Gitea wiki。所有 skills 以 SKILL.md 為共通標準;於 Antigravity 以 /jsc: 前綴呼叫。",
|
"description": "JSC 文件化 skills:doc-docker 會整理 docker-compose.yaml 的行內註解與標題日期;doc-funcs 會為專案 functions 建立 .docs 草稿、補齊 XML 文件註解並重建 README 功能列表與使用範例;doc-issues-analyze-to-file 會讀取 Gitea issue、彙整需求、拆成多階段 issue 並產生實作草稿與交付留言;doc-issues-analyze 會把專案/議題/文件來源(議題連同留言與附件一起讀取)拆成小功能議題(母議題須待所有子議題關閉後才可關閉)、分析完成後把屬於專案看板的議題移到「待處理」欄位並依到期日實作;doc-issues-sync 會讀取 Gitea 專案或議題、依工作目錄檔案勾稽並同步議題的 TODO 進度、標籤與專案看板進度欄位並產生進度留言(指定關閉專案/專案完成時改為批次把專案所有議題搬到「已完成」並關閉);worklog 會以 README 定義的 headless CLI 將每輪工作整理成六欄工作紀錄並追加到 Gitea wiki。所有 skills 以 SKILL.md 為共通標準;於 Antigravity 以 /jsc: 前綴呼叫。",
|
||||||
"skills": "./skills/"
|
"skills": "./skills/"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
# 用途:worklog 的 transcript 處理工具。負責 (1) 從 Claude Code transcript
|
# 用途:worklog 的 transcript 處理工具。負責 (1) 從 Claude Code/Codex
|
||||||
# JSONL 抽出「本輪」對話片段(最後一筆使用者訊息之後的全部內容),
|
# JSONL 抽出「本輪」對話片段(最後一筆使用者訊息之後的全部內容),
|
||||||
# (2) 估算本輪花費時間,(3) 對文字做機密遮蔽(token/密碼/PII),
|
# (2) 估算本輪花費時間,(3) 對文字做機密遮蔽(token/密碼/PII),
|
||||||
# 作為寫入 wiki 前的第二道防線。
|
# 作為寫入 wiki 前的第二道防線。
|
||||||
# 更新時間:2026/07/27 15:09:00
|
# 更新時間:2026/07/27 22:16:00
|
||||||
# 相依:Python 3 標準庫。全程僅走 stdin/stdout,不寫任何檔案。
|
# 相依:Python 3 標準庫。全程僅走 stdin/stdout,不寫任何檔案。
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
|
|
||||||
@@ -42,7 +42,11 @@ def redact(text):
|
|||||||
|
|
||||||
|
|
||||||
def _is_real_user_message(entry):
|
def _is_real_user_message(entry):
|
||||||
"""判斷 transcript 條目是否為真正的使用者輸入(排除 tool_result 回填的 user 條目)。"""
|
"""判斷 transcript 條目是否為真正的使用者輸入(排除工具回填與環境注入)。"""
|
||||||
|
payload = entry.get("payload")
|
||||||
|
if isinstance(payload, dict) and entry.get("type") == "event_msg":
|
||||||
|
return payload.get("type") == "user_message" and bool(str(payload.get("message") or "").strip())
|
||||||
|
|
||||||
if entry.get("type") != "user":
|
if entry.get("type") != "user":
|
||||||
return False
|
return False
|
||||||
content = entry.get("message", {}).get("content")
|
content = entry.get("message", {}).get("content")
|
||||||
@@ -61,8 +65,77 @@ def _blocks(entry):
|
|||||||
return content if isinstance(content, list) else []
|
return content if isinstance(content, list) else []
|
||||||
|
|
||||||
|
|
||||||
|
def _payload_text_blocks(content):
|
||||||
|
"""把 Codex response_item 的 content blocks 轉成純文字片段。"""
|
||||||
|
if isinstance(content, str):
|
||||||
|
return [content]
|
||||||
|
if not isinstance(content, list):
|
||||||
|
return []
|
||||||
|
texts = []
|
||||||
|
for block in content:
|
||||||
|
if not isinstance(block, dict):
|
||||||
|
continue
|
||||||
|
if block.get("type") in ("input_text", "output_text", "text"):
|
||||||
|
text = (block.get("text") or "").strip()
|
||||||
|
if text:
|
||||||
|
texts.append(text)
|
||||||
|
return texts
|
||||||
|
|
||||||
|
|
||||||
|
def _render_codex_payload(entry):
|
||||||
|
"""將 Codex session JSONL 的 payload 格式轉為摘要輸入用純文字。"""
|
||||||
|
payload = entry.get("payload")
|
||||||
|
if not isinstance(payload, dict):
|
||||||
|
return []
|
||||||
|
|
||||||
|
lines = []
|
||||||
|
entry_type = entry.get("type")
|
||||||
|
payload_type = payload.get("type")
|
||||||
|
|
||||||
|
if entry_type == "event_msg":
|
||||||
|
if payload_type == "user_message":
|
||||||
|
message = (payload.get("message") or "").strip()
|
||||||
|
if message:
|
||||||
|
lines.append(f"[user] {message}")
|
||||||
|
elif payload_type == "agent_message":
|
||||||
|
message = (payload.get("message") or "").strip()
|
||||||
|
if message:
|
||||||
|
phase = payload.get("phase") or "assistant"
|
||||||
|
lines.append(f"[assistant:{phase}] {message}")
|
||||||
|
return lines
|
||||||
|
|
||||||
|
if entry_type != "response_item":
|
||||||
|
return lines
|
||||||
|
|
||||||
|
if payload_type == "message":
|
||||||
|
role = payload.get("role") or "assistant"
|
||||||
|
if role in ("system", "developer"):
|
||||||
|
return lines
|
||||||
|
for text in _payload_text_blocks(payload.get("content")):
|
||||||
|
# Codex 會把 skill 內容以 user role 注入;避免把整份 SKILL.md 當成本輪工作。
|
||||||
|
if role == "user" and text.lstrip().startswith("<skill>"):
|
||||||
|
continue
|
||||||
|
if role == "user" and text.lstrip().startswith("<environment_context>"):
|
||||||
|
continue
|
||||||
|
lines.append(f"[{role}] {text}")
|
||||||
|
elif payload_type == "function_call":
|
||||||
|
name = payload.get("name") or "?"
|
||||||
|
raw = str(payload.get("arguments") or "").strip().replace("\n", " ")
|
||||||
|
lines.append(f"[tool:{name}] {raw[:TOOL_INPUT_LIMIT]}")
|
||||||
|
elif payload_type == "function_call_output":
|
||||||
|
raw = str(payload.get("output") or "").strip().replace("\n", " ")
|
||||||
|
if raw:
|
||||||
|
lines.append(f"[result] {raw[:TOOL_RESULT_LIMIT]}")
|
||||||
|
|
||||||
|
return lines
|
||||||
|
|
||||||
|
|
||||||
def _render(entry):
|
def _render(entry):
|
||||||
"""將單一 transcript 條目轉為摘要輸入用的純文字行(工具結果僅取前段)。"""
|
"""將單一 transcript 條目轉為摘要輸入用的純文字行(工具結果僅取前段)。"""
|
||||||
|
codex_lines = _render_codex_payload(entry)
|
||||||
|
if codex_lines:
|
||||||
|
return codex_lines
|
||||||
|
|
||||||
role = entry.get("type")
|
role = entry.get("type")
|
||||||
lines = []
|
lines = []
|
||||||
for block in _blocks(entry):
|
for block in _blocks(entry):
|
||||||
|
|||||||
+88
-27
@@ -1,16 +1,17 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
# 用途:工作證明自動記錄(worklog)。由 Claude Code 的 Stop hook 觸發,
|
# 用途:工作證明自動記錄(worklog)。由支援 hook 的 CLI 觸發,
|
||||||
# 抽出本輪工作內容 → 呼叫模型濃縮成精簡條目 → 機密遮蔽 →
|
# 抽出本輪工作內容 → 呼叫已安裝 CLI 濃縮成精簡條目 → 機密遮蔽 →
|
||||||
# 追加到 Gitea wiki 的當週工作紀錄頁。工作內容全程不落地。
|
# 追加到 Gitea wiki 的當週工作紀錄頁。工作內容全程不落地。
|
||||||
# 更新時間:2026/07/27 15:09:00
|
# 更新時間:2026/07/27 22:16:00
|
||||||
# 相依:python3、claude CLI、curl(wiki 走 Python urllib,不需 curl 亦可)。
|
# 相依:python3、README 定義的任一 headless CLI、curl(wiki 走 Python urllib,不需 curl 亦可)。
|
||||||
# 機密:token 僅由環境變數/本機憑證讀取,不 echo、不寫檔;輸出前套用遮蔽規則。
|
# 機密:token 僅由環境變數/本機憑證讀取,不 echo、不寫檔;輸出前套用遮蔽規則。
|
||||||
# 退出碼:一律 0 —— hook 絕不可阻斷使用者的工作流程。
|
# 退出碼:一律 0 —— hook 絕不可阻斷使用者的工作流程。
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
STAGE="worklog"
|
STAGE="worklog"
|
||||||
|
SUPPORTED_CLIS="claude codex agy opencode copilot"
|
||||||
FALLBACK_MODEL="claude-haiku-4-5-20251001"
|
FALLBACK_MODEL="claude-haiku-4-5-20251001"
|
||||||
MODEL_CACHE="${HOME}/.claude/worklog/model"
|
MODEL_CACHE="${HOME}/.claude/worklog/model"
|
||||||
CACHE_MAX_AGE_DAYS=30
|
CACHE_MAX_AGE_DAYS=30
|
||||||
@@ -36,7 +37,7 @@ die_quiet() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# 遞迴防護:摘要用的子 claude 行程會再次觸發 Stop hook,必須在此擋掉
|
# 遞迴防護:摘要用的子 CLI 行程可能再次觸發 Stop hook,必須在此擋掉
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
[ -n "${WORKLOG_CHILD:-}" ] && exit 0
|
[ -n "${WORKLOG_CHILD:-}" ] && exit 0
|
||||||
|
|
||||||
@@ -48,7 +49,52 @@ die_quiet() {
|
|||||||
[ -n "${WORKLOG_REPO:-}" ] || die_quiet "未設定 WORKLOG_REPO,略過記錄" "WRN"
|
[ -n "${WORKLOG_REPO:-}" ] || die_quiet "未設定 WORKLOG_REPO,略過記錄" "WRN"
|
||||||
|
|
||||||
command -v python3 >/dev/null 2>&1 || die_quiet "找不到 python3,略過記錄" "WRN"
|
command -v python3 >/dev/null 2>&1 || die_quiet "找不到 python3,略過記錄" "WRN"
|
||||||
command -v claude >/dev/null 2>&1 || die_quiet "找不到 claude CLI,略過記錄" "WRN"
|
|
||||||
|
select_worklog_cli() {
|
||||||
|
# 依 README 定義的 headless CLI 選擇摘要執行器;可用 WORKLOG_CLI 強制指定。
|
||||||
|
local requested="${WORKLOG_CLI:-auto}" cli
|
||||||
|
if [ "$requested" != "auto" ]; then
|
||||||
|
case " ${SUPPORTED_CLIS} " in
|
||||||
|
*" ${requested} "*) ;;
|
||||||
|
*) die_quiet "WORKLOG_CLI 不支援:${requested}(可用:auto ${SUPPORTED_CLIS})" "WRN" ;;
|
||||||
|
esac
|
||||||
|
command -v "$requested" >/dev/null 2>&1 || die_quiet "找不到 ${requested} CLI,略過記錄" "WRN"
|
||||||
|
printf '%s' "$requested"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
for cli in $SUPPORTED_CLIS; do
|
||||||
|
if command -v "$cli" >/dev/null 2>&1; then
|
||||||
|
printf '%s' "$cli"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
die_quiet "找不到可用摘要 CLI(需要其一:${SUPPORTED_CLIS})" "WRN"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_summary_cli() {
|
||||||
|
# 各 CLI 依 README 的 headless 指令呼叫;不把工作內容寫入檔案。
|
||||||
|
local cli="$1" prompt="$2" model="$3"
|
||||||
|
case "$cli" in
|
||||||
|
claude)
|
||||||
|
WORKLOG_CHILD=1 timeout 45 claude -p "$prompt" --model "$model" 2>/dev/null
|
||||||
|
;;
|
||||||
|
codex)
|
||||||
|
WORKLOG_CHILD=1 timeout 45 codex exec "$prompt" 2>/dev/null
|
||||||
|
;;
|
||||||
|
agy)
|
||||||
|
WORKLOG_CHILD=1 timeout 45 agy -p "$prompt" 2>/dev/null
|
||||||
|
;;
|
||||||
|
opencode)
|
||||||
|
WORKLOG_CHILD=1 timeout 45 opencode run "$prompt" 2>/dev/null
|
||||||
|
;;
|
||||||
|
copilot)
|
||||||
|
WORKLOG_CHILD=1 timeout 45 copilot -p "$prompt" 2>/dev/null
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
SUMMARY_CLI="$(select_worklog_cli)"
|
||||||
|
[ -n "$SUMMARY_CLI" ] || exit 0
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# 讀取 hook 傳入的 JSON(session_id/transcript_path/cwd/stop_hook_active)
|
# 讀取 hook 傳入的 JSON(session_id/transcript_path/cwd/stop_hook_active)
|
||||||
@@ -64,8 +110,8 @@ try:
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
d = {}
|
d = {}
|
||||||
print(
|
print(
|
||||||
d.get("session_id", "-") or "-",
|
d.get("session_id") or d.get("thread_id") or d.get("conversation_id") or "-",
|
||||||
d.get("transcript_path", "-") or "-",
|
d.get("transcript_path") or d.get("session_path") or d.get("conversation_path") or d.get("path") or "-",
|
||||||
"1" if d.get("stop_hook_active") else "0",
|
"1" if d.get("stop_hook_active") else "0",
|
||||||
d.get("cwd", "") or "-",
|
d.get("cwd", "") or "-",
|
||||||
)
|
)
|
||||||
@@ -73,6 +119,12 @@ print(
|
|||||||
EOF_HOOK
|
EOF_HOOK
|
||||||
|
|
||||||
[ "$STOP_ACTIVE" = "1" ] && die_quiet "stop_hook_active 為 true,避免迴圈不重複記錄"
|
[ "$STOP_ACTIVE" = "1" ] && die_quiet "stop_hook_active 為 true,避免迴圈不重複記錄"
|
||||||
|
|
||||||
|
if [ ! -f "$TRANSCRIPT_PATH" ] && [ -n "${CODEX_THREAD_ID:-}" ]; then
|
||||||
|
TRANSCRIPT_PATH="$(find "${HOME}/.codex/sessions" -type f -name "*${CODEX_THREAD_ID}.jsonl" -print -quit 2>/dev/null)"
|
||||||
|
[ -n "$TRANSCRIPT_PATH" ] || TRANSCRIPT_PATH="-"
|
||||||
|
fi
|
||||||
|
|
||||||
[ -f "$TRANSCRIPT_PATH" ] || die_quiet "找不到 transcript:${TRANSCRIPT_PATH}" "WRN"
|
[ -f "$TRANSCRIPT_PATH" ] || die_quiet "找不到 transcript:${TRANSCRIPT_PATH}" "WRN"
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
@@ -111,29 +163,35 @@ DURATION="$(python3 "${SCRIPT_DIR}/transcript.py" duration "$TRANSCRIPT_PATH" 2>
|
|||||||
[ -n "$DURATION" ] || DURATION="未判定"
|
[ -n "$DURATION" ] || DURATION="未判定"
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# 模型決定:WORKLOG_MODEL → 快取檔(/jsc:worklog --tune 產生)→ 保底
|
# 模型決定:只有 claude CLI 使用 WORKLOG_MODEL/快取檔;其他 CLI 使用各自預設模型
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
MODEL=""
|
MODEL=""
|
||||||
MODEL_NOTE=""
|
MODEL_NOTE=""
|
||||||
if [ -n "${WORKLOG_MODEL:-}" ]; then
|
if [ "$SUMMARY_CLI" = "claude" ]; then
|
||||||
MODEL="${WORKLOG_MODEL}"
|
if [ -n "${WORKLOG_MODEL:-}" ]; then
|
||||||
elif [ -f "$MODEL_CACHE" ]; then
|
MODEL="${WORKLOG_MODEL}"
|
||||||
if [ -n "$(find "$MODEL_CACHE" -mtime "+${CACHE_MAX_AGE_DAYS}" 2>/dev/null)" ]; then
|
elif [ -f "$MODEL_CACHE" ]; then
|
||||||
MODEL="$FALLBACK_MODEL"
|
if [ -n "$(find "$MODEL_CACHE" -mtime "+${CACHE_MAX_AGE_DAYS}" 2>/dev/null)" ]; then
|
||||||
MODEL_NOTE=" (model: fallback)"
|
MODEL="$FALLBACK_MODEL"
|
||||||
log "WRN" "模型快取已超過 ${CACHE_MAX_AGE_DAYS} 天,改用保底模型,建議重跑 /jsc:worklog --tune"
|
MODEL_NOTE=" (cli: claude, model: fallback)"
|
||||||
else
|
log "WRN" "模型快取已超過 ${CACHE_MAX_AGE_DAYS} 天,改用保底模型,建議重跑 /jsc:worklog --tune"
|
||||||
MODEL="$(grep -m1 -E '^model=' "$MODEL_CACHE" 2>/dev/null | cut -d= -f2- | tr -d '[:space:]')"
|
else
|
||||||
|
MODEL="$(grep -m1 -E '^model=' "$MODEL_CACHE" 2>/dev/null | cut -d= -f2- | tr -d '[:space:]')"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
if [ -z "$MODEL" ]; then
|
||||||
if [ -z "$MODEL" ]; then
|
MODEL="$FALLBACK_MODEL"
|
||||||
MODEL="$FALLBACK_MODEL"
|
MODEL_NOTE=" (cli: claude, model: fallback)"
|
||||||
MODEL_NOTE=" (model: fallback)"
|
log "WRN" "無模型快取,改用保底模型,建議執行 /jsc:worklog --tune"
|
||||||
log "WRN" "無模型快取,改用保底模型,建議執行 /jsc:worklog --tune"
|
elif [ -z "$MODEL_NOTE" ]; then
|
||||||
|
MODEL_NOTE=" (cli: claude)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
MODEL_NOTE=" (cli: ${SUMMARY_CLI})"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# 濃縮:交給模型產出精簡條目(子行程帶 WORKLOG_CHILD=1 阻斷遞迴)
|
# 濃縮:交給選定 CLI 產出精簡條目(子行程帶 WORKLOG_CHILD=1 阻斷遞迴)
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
PROMPT="$(cat <<EOF_PROMPT
|
PROMPT="$(cat <<EOF_PROMPT
|
||||||
你是工作紀錄濃縮器。輸入是一段 AI 助理與使用者的對話片段(含工具呼叫)。
|
你是工作紀錄濃縮器。輸入是一段 AI 助理與使用者的對話片段(含工具呼叫)。
|
||||||
@@ -156,12 +214,15 @@ PROMPT="$(cat <<EOF_PROMPT
|
|||||||
6. 若沒有遇到明確困難,遇到的困難寫「未遇到明確困難」,解決方式寫「不需額外處理」。
|
6. 若沒有遇到明確困難,遇到的困難寫「未遇到明確困難」,解決方式寫「不需額外處理」。
|
||||||
7. 嚴禁輸出任何憑證與個資:token、密碼、API key、連線字串、Email、電話、姓名、身分證號。
|
7. 嚴禁輸出任何憑證與個資:token、密碼、API key、連線字串、Email、電話、姓名、身分證號。
|
||||||
8. 若這段對話沒有實質工作產出(純閒聊、純提問、僅讀取資訊而未產生結論),只輸出一行:SKIP
|
8. 若這段對話沒有實質工作產出(純閒聊、純提問、僅讀取資訊而未產生結論),只輸出一行:SKIP
|
||||||
|
|
||||||
|
對話片段:
|
||||||
|
${TURN}
|
||||||
EOF_PROMPT
|
EOF_PROMPT
|
||||||
)"
|
)"
|
||||||
|
|
||||||
SUMMARY="$(printf '%s' "$TURN" | WORKLOG_CHILD=1 timeout 45 claude -p "$PROMPT" --model "$MODEL" 2>/dev/null)"
|
SUMMARY="$(run_summary_cli "$SUMMARY_CLI" "$PROMPT" "$MODEL")"
|
||||||
if [ -z "$SUMMARY" ]; then
|
if [ -z "$SUMMARY" ]; then
|
||||||
log "WRN" "摘要產出為空(模型 ${MODEL}),略過本輪"
|
log "WRN" "摘要產出為空(CLI ${SUMMARY_CLI}),略過本輪"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
printf '%s' "$SUMMARY" | grep -qiE '^\s*SKIP\s*$' && die_quiet "模型判定本輪無實質工作產出"
|
printf '%s' "$SUMMARY" | grep -qiE '^\s*SKIP\s*$' && die_quiet "模型判定本輪無實質工作產出"
|
||||||
@@ -184,7 +245,7 @@ export WORKLOG_HOST WORKLOG_REPO
|
|||||||
if printf '%s' "$ENTRY" | python3 "${SCRIPT_DIR}/wiki_api.py" append "$MARKER" 2>&1 | grep -q '\[ERR\]'; then
|
if printf '%s' "$ENTRY" | python3 "${SCRIPT_DIR}/wiki_api.py" append "$MARKER" 2>&1 | grep -q '\[ERR\]'; then
|
||||||
log "ERR" "寫入 wiki 失敗(專案 ${PROJECT})"
|
log "ERR" "寫入 wiki 失敗(專案 ${PROJECT})"
|
||||||
else
|
else
|
||||||
log "INF" "已記錄工作條目(專案 ${PROJECT},模型 ${MODEL})"
|
log "INF" "已記錄工作條目(專案 ${PROJECT},CLI ${SUMMARY_CLI})"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
+18
-16
@@ -1,33 +1,34 @@
|
|||||||
---
|
---
|
||||||
name: worklog
|
name: worklog
|
||||||
description: 工作證明自動記錄(worklog)的操作與維護 skill。搭配 Claude Code 的 Stop hook,把每輪工作內容濃縮成精簡條目並追加到 Gitea wiki 的當週工作紀錄頁(Worklog-yyyy-MM-W<週>),工作內容全程不落地。提供 --init(初始化週頁與環境變數指引)、--tune(判定並快取最適合的摘要模型)、--diagnose(診斷 hook 為何沒動作)、--append(手動補寫一筆)、--show(讀當週頁回顧)五個模式。當使用者說工作證明、工作紀錄、worklog、週報自動化、把工作內容寫到 wiki、記錄到 Gitea wiki、hook 沒有寫入 wiki、補寫工作紀錄、看本週做了什麼、重新判定摘要模型,或提到 WORKLOG_ENABLED/WORKLOG_HOST/WORKLOG_REPO/WORKLOG_MODEL/WORKLOG_SCOPE 時觸發。**僅支援 Claude Code/Codex/Antigravity(需 plugin 目錄保留 scripts/);不支援 OpenCode**(skills 目錄安裝不會帶入 scripts/,且自動記錄依賴 Claude Code 的 transcript 格式)。不適用於:Gitea 議題操作(用 doc-issues-sync/code-issues)、專案文件化(用 doc-funcs)。
|
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 議題操作(用 doc-issues-sync/code-issues)、專案文件化(用 doc-funcs)。
|
||||||
---
|
---
|
||||||
|
|
||||||
# worklog — 工作證明自動記錄
|
# worklog — 工作證明自動記錄
|
||||||
|
|
||||||
把「每輪做了什麼」濃縮成一則條目,追加到 Gitea wiki 的當週工作紀錄頁。**自動記錄由 Claude Code 的 `Stop` hook 完成,不需使用者同意、不需人工觸發**;本 skill 負責自動路徑之外的人工操作:初始化、模型判定、診斷、補寫、回顧。
|
把「每輪做了什麼」濃縮成一則條目,追加到 Gitea wiki 的當週工作紀錄頁。**自動記錄由相容的 `Stop` hook 完成,不需使用者同意、不需人工觸發**;本 skill 負責自動路徑之外的人工操作:初始化、模型判定、診斷、補寫、回顧。
|
||||||
|
|
||||||
| 元件 | 觸發者 | 職責 |
|
| 元件 | 觸發者 | 職責 |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `hooks/hooks.json` 的 `Stop` hook | harness 自動 | 每輪結束抽本輪內容 → 濃縮 → 遮蔽 → 追加到當週頁 |
|
| `hooks/hooks.json` 的 `Stop` hook | harness 自動 | 每輪結束抽本輪內容 → 濃縮 → 遮蔽 → 追加到當週頁 |
|
||||||
| 本 skill `/jsc:worklog` | 使用者/助理手動 | `--init`/`--tune`/`--diagnose`/`--append`/`--show` |
|
| 本 skill `/jsc:worklog` | 使用者/助理手動 | `--init`/`--tune`/`--diagnose`/`--append`/`--show` |
|
||||||
| `scripts/worklog/worklog.sh` | 上述兩者共用 | 主流程(單一實作,避免漂移):每筆整理成六個固定欄位 |
|
| `scripts/worklog/worklog.sh` | 上述兩者共用 | 主流程(單一實作,避免漂移):依 `WORKLOG_CLI` 呼叫 headless CLI,每筆整理成六個固定欄位 |
|
||||||
| `scripts/worklog/wiki_api.py` | 上述兩者共用 | token 解析、wiki 讀寫、append 重試、週頁命名 |
|
| `scripts/worklog/wiki_api.py` | 上述兩者共用 | token 解析、wiki 讀寫、append 重試、週頁命名 |
|
||||||
| `scripts/worklog/transcript.py` | 上述兩者共用 | 抽本輪片段、估算花費時間、機密遮蔽 |
|
| `scripts/worklog/transcript.py` | 上述兩者共用 | 抽本輪片段、估算花費時間、機密遮蔽 |
|
||||||
|
|
||||||
### 各助理支援範圍
|
### 各助理支援範圍
|
||||||
|
|
||||||
| 功能 | Claude Code | Codex | Antigravity | OpenCode |
|
| 功能 | Claude Code | Codex | Antigravity | OpenCode | GitHub Copilot |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- |
|
||||||
| `Stop` hook 自動記錄 | ✅ | ⚠️ 會載入但 no-op | ❌ | ❌ |
|
| `Stop` hook 自動記錄 | ✅ | ✅ 需可讀 Codex session JSONL | ❌ | ❌ | ❌ |
|
||||||
| `--init`/`--diagnose`/`--append`/`--show` | ✅ | ⚠️ 需 plugin 目錄保留 `scripts/`(安裝後請實測一次) | ⚠️ 同左 | ❌ 缺 `scripts/` |
|
| `--init`/`--diagnose`/`--append`/`--show` | ✅ | ⚠️ 需 plugin 目錄保留 `scripts/`(安裝後請實測一次) | ⚠️ 同左 | ⚠️ 需完整 plugin 目錄 | ⚠️ 需 plugin 目錄保留 `scripts/` |
|
||||||
| `--tune` | ✅ | ❌ 無 `claude-api` skill 可載入 | ❌ 同左 | ❌ |
|
| 摘要 CLI | `claude -p` | `codex exec` | `agy -p` | `opencode run` | `copilot -p` |
|
||||||
|
| `--tune` | ✅ | ❌ 無 `claude-api` skill 可載入 | ❌ 同左 | ❌ | ❌ |
|
||||||
|
|
||||||
兩個限制的來源:
|
兩個限制的來源:
|
||||||
|
|
||||||
- **`Stop` hook 只有 Claude Code 環境實際執行**;Codex 會載入 plugin 的 `hooks/hooks.json`,因此 hook command 會先檢查 `CLAUDE_PLUGIN_ROOT`,非 Claude Code 環境直接 no-op。`worklog.sh` 解析的是 **Claude Code 專屬的 transcript JSONL 結構**(`type` / `message.content` blocks),所以即使其他助理提供等效 hook 機制,自動記錄也不能直接沿用。
|
- **`Stop` hook 只有相容 hook 環境實際執行**;Claude Code 會用 `CLAUDE_PLUGIN_ROOT` 定位腳本,Codex 會從 `~/.codex/plugins/cache/doc/jsc` 找已安裝的 worklog 腳本。`transcript.py` 目前支援 Claude Code transcript JSONL(`type` / `message.content` blocks)與 Codex session JSONL(`payload` events / response items),其他助理若提供等效 hook,必須先補對應 transcript 解析器。
|
||||||
- **OpenCode 以「複製 `skills/` 目錄」安裝**,不會帶入 `scripts/`,本 skill 的所有模式都無法執行 —— 在 OpenCode 環境請不要觸發本 skill。
|
- **OpenCode 以「複製 `skills/` 目錄」安裝**時不會帶入 `scripts/`,本 skill 的所有模式都無法執行;若以完整 plugin 目錄執行並能解析 `scripts/worklog`,可用 `WORKLOG_CLI=opencode` 作為摘要 CLI。
|
||||||
- 其他助理若要用 `--append`/`--show` 等純 wiki 操作,只需 `python3`(不需 `claude` CLI),但 `--tune` 必須改為手動設定 `WORKLOG_MODEL`。
|
- 其他助理若要用 `--append`/`--show` 等純 wiki 操作,只需 `python3`;摘要路徑需要 README 定義的任一 headless CLI。`--tune` 仍是 Claude Code 專屬,其他 CLI 使用各自預設模型或手動設定其 CLI 行為。
|
||||||
|
|
||||||
### 腳本路徑解析(重要)
|
### 腳本路徑解析(重要)
|
||||||
|
|
||||||
@@ -61,7 +62,7 @@ WORKLOG_DIR="<skill base directory>/../../scripts/worklog"
|
|||||||
|
|
||||||
本 skill 特有補充:
|
本 skill 特有補充:
|
||||||
|
|
||||||
- **工作內容不落地**:transcript 片段以 pipe 傳遞、wiki 走 API 不 clone,全程不產生暫存檔。唯一允許落地的是**模型快取檔** `~/.claude/worklog/model`(僅含模型 id 與判定時間,不含任何工作內容)。
|
- **工作內容不落地**:transcript 片段只在程序記憶體與 stdin/stdout 間傳遞、wiki 走 API 不 clone,全程不產生暫存檔。唯一允許落地的是**模型快取檔** `~/.claude/worklog/model`(僅含模型 id 與判定時間,不含任何工作內容)。
|
||||||
- **絕不阻斷**:hook 路徑任何失敗都以 exit 0 結束,只在 stderr 留訊息。
|
- **絕不阻斷**:hook 路徑任何失敗都以 exit 0 結束,只在 stderr 留訊息。
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -74,6 +75,7 @@ WORKLOG_DIR="<skill base directory>/../../scripts/worklog"
|
|||||||
| `WORKLOG_HOST` | ✅ | Gitea 主機,如 `gitea.housefun.com.tw` | 不啟用 |
|
| `WORKLOG_HOST` | ✅ | Gitea 主機,如 `gitea.housefun.com.tw` | 不啟用 |
|
||||||
| `WORKLOG_REPO` | ✅ | wiki 所在 repo,如 `H3285/WorkLog` | 不啟用 |
|
| `WORKLOG_REPO` | ✅ | wiki 所在 repo,如 `H3285/WorkLog` | 不啟用 |
|
||||||
| `WORKLOG_MODEL` | | 強制指定摘要模型 | 讀快取檔 → 保底 `claude-haiku-4-5-20251001` |
|
| `WORKLOG_MODEL` | | 強制指定摘要模型 | 讀快取檔 → 保底 `claude-haiku-4-5-20251001` |
|
||||||
|
| `WORKLOG_CLI` | | 摘要執行器:`auto`/`claude`/`codex`/`agy`/`opencode`/`copilot` | `auto`,依序挑 `claude`→`codex`→`agy`→`opencode`→`copilot` |
|
||||||
| `WORKLOG_SCOPE` | | 冒號分隔的路徑前綴,僅這些路徑下的 session 才記 | 全部 session 都記 |
|
| `WORKLOG_SCOPE` | | 冒號分隔的路徑前綴,僅這些路徑下的 session 才記 | 全部 session 都記 |
|
||||||
| `WORKLOG_ERRLOG` | | 錯誤訊息額外寫入的檔案路徑(只記錯誤、不含工作內容) | 只走 stderr |
|
| `WORKLOG_ERRLOG` | | 錯誤訊息額外寫入的檔案路徑(只記錯誤、不含工作內容) | 只走 stderr |
|
||||||
|
|
||||||
@@ -114,12 +116,12 @@ GITEA_TOKEN →(對目標 host 驗證失敗時)→ tea 設定檔中該 host
|
|||||||
|
|
||||||
| 檢查項 | 判準 |
|
| 檢查項 | 判準 |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `python3`/`claude` CLI | `command -v` 是否找得到 |
|
| `python3`/摘要 CLI | `python3` 與 `WORKLOG_CLI` 指定或 auto 選到的 CLI 是否找得到 |
|
||||||
| `WORKLOG_*` 變數 | 必要三項是否齊全、`WORKLOG_SCOPE` 是否把當前路徑排除 |
|
| `WORKLOG_*` 變數 | 必要三項是否齊全、`WORKLOG_SCOPE` 是否把當前路徑排除 |
|
||||||
| `scripts/worklog` 目錄 | `${WORKLOG_DIR}` 是否解析成功且三支腳本存在(不存在=此助理不支援) |
|
| `scripts/worklog` 目錄 | `${WORKLOG_DIR}` 是否解析成功且三支腳本存在(不存在=此助理不支援) |
|
||||||
| token | `python3 "${WORKLOG_DIR}/wiki_api.py" probe` 的 token 來源與驗證結果 |
|
| token | `python3 "${WORKLOG_DIR}/wiki_api.py" probe` 的 token 來源與驗證結果 |
|
||||||
| wiki API | Gitea 版本、`repos/<repo>` 與當週頁狀態 |
|
| wiki API | Gitea 版本、`repos/<repo>` 與當週頁狀態 |
|
||||||
| 模型快取 | 是否存在、是否過期、目前會用哪支模型 |
|
| 摘要設定 | `WORKLOG_CLI`、選到的 CLI、Claude 模型快取是否存在與是否過期 |
|
||||||
| hook 註冊 | `hooks/hooks.json` 是否存在且 plugin 已啟用 |
|
| hook 註冊 | `hooks/hooks.json` 是否存在且 plugin 已啟用 |
|
||||||
|
|
||||||
### `--append "<內容>"`
|
### `--append "<內容>"`
|
||||||
@@ -169,5 +171,5 @@ GITEA_TOKEN →(對目標 host 驗證失敗時)→ tea 設定檔中該 host
|
|||||||
| 助理 | 呼叫 |
|
| 助理 | 呼叫 |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Claude Code / Antigravity | `/jsc:worklog --init`、`/jsc:worklog --tune`、`/jsc:worklog --diagnose`、`/jsc:worklog --append "修正 X 的 Y 問題"`、`/jsc:worklog --show` |
|
| Claude Code / Antigravity | `/jsc:worklog --init`、`/jsc:worklog --tune`、`/jsc:worklog --diagnose`、`/jsc:worklog --append "修正 X 的 Y 問題"`、`/jsc:worklog --show` |
|
||||||
| Codex | `$worklog --diagnose`,或用 `/skills` 選單 |
|
| Codex | `$worklog --diagnose`,或用 `/skills` 選單;可設 `WORKLOG_CLI=codex` |
|
||||||
| OpenCode | **不支援**(skills 目錄安裝不含 `scripts/`) |
|
| OpenCode / GitHub Copilot | 需完整 plugin 目錄保留 `scripts/`;可設 `WORKLOG_CLI=opencode` 或 `WORKLOG_CLI=copilot` |
|
||||||
|
|||||||
Reference in New Issue
Block a user