From d4bf31421d28b635b38368233c10a06a2015d42b Mon Sep 17 00:00:00 2001 From: Jeffery Date: Tue, 25 Aug 2026 14:58:54 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix(log):=20=E8=A3=9C=E9=BD=8A=E7=A8=BD?= =?UTF-8?q?=E6=A0=B8=E7=BC=BA=E5=A4=B1=E4=B8=A6=E4=BF=AE=E6=8E=89=E8=AD=B7?= =?UTF-8?q?=E6=AC=84=E5=A4=B1=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What:依 jsc-meta:skill-check 的稽核結果修正技能與工具——補上每個步驟的可檢核完成條件、 把留在內文的標準輸入輸出流程下放 tools/、修正查表與退碼路由造成的誤判。 Why:稽核發現這些缺失會讓技能在實際執行時走錯分支或靜默通過。 完成條件缺漏是最常被違反的一項;退碼誤判與查表錯誤則會讓良性狀況被當成失敗。 How:逐項對照 references/guidelines.md 的審核檢查清單修正,新增的工具都有 documented exit codes,並以真實執行驗證每條路徑。 Who:jsc-meta:skill-check 例行稽核(2026-08-25)。 Co-Authored-By: Claude Opus 5 --- skills/learn/SKILL.md | 2 +- skills/stats/SKILL.md | 4 +-- skills/worklog/SKILL.md | 36 ++++++++----------- tools/token-usage.sh | 77 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 25 deletions(-) create mode 100755 tools/token-usage.sh diff --git a/skills/learn/SKILL.md b/skills/learn/SKILL.md index a0e3610..223efbc 100644 --- a/skills/learn/SKILL.md +++ b/skills/learn/SKILL.md @@ -10,7 +10,7 @@ Close the loop on skill runs: record what a run taught you, consult it before th ## Target pages - Directory page: `LEARN_CONTENTS`. Content page: `LEARN_{HASH}`, one page per repository. -- Compute `{HASH}` from `{owner}/{repo}` with `jsc-gitea/tools/hash-id` (shared wiki hash rule: first 8 uppercase SHA-1 hex chars; `H` plus the first 7 chars when the raw hash starts with `0-9`, `A`, `B`, or `C`). +- Compute `{HASH}` from `{owner}/{repo}` with `jsc-gitea/tools/hash-id`. - Wiki repo resolution: `JSC_WIKI_REPO_LEARN` first, then `JSC_WIKI_REPO`. Inspect the inherited shell environment variables first; ask the user per the `jsc-ask:ask` rules only when neither resolves. Never borrow another type's `JSC_WIKI_REPO_{TYPE}`. - All wiki reads and writes go through `jsc-gitea:wiki`. diff --git a/skills/stats/SKILL.md b/skills/stats/SKILL.md index 2cec2f0..9a1f396 100644 --- a/skills/stats/SKILL.md +++ b/skills/stats/SKILL.md @@ -17,5 +17,5 @@ Data is recorded continuously by `jsc-hooks/hooks/skill-usage.sh` under `$JSC_HO ## Reporting -1. Run the tool directly and present the output as a table. -2. When there is no data, explain that `jsc-hooks` must be installed and wired first via `jsc-hooks:hooks-install`. +1. Run the tool directly and present the output as a table. Done when every line the tool printed appears as one table row. +2. When the tool prints no rows, explain that `jsc-hooks` must be installed and wired first via `jsc-hooks:hooks-install`. Done when that instruction is reported and no table is shown. diff --git a/skills/worklog/SKILL.md b/skills/worklog/SKILL.md index 2b7a90c..e7aa0e3 100644 --- a/skills/worklog/SKILL.md +++ b/skills/worklog/SKILL.md @@ -11,32 +11,24 @@ After work completes, collect the ten items below and append a `templates/log-en | # | Item | Source | | --- | --- | --- | -| 1 | Repository name | Parse `{owner}/{repo}` from `git remote get-url origin` | +| 1 | Repository name | Parse `{owner}/{repo}` from `git remote get-url origin`. This is the code repo — never pass it to `wiki-url`, which takes the wiki-hosting repo | | 2 | Branch name | `git branch --show-current` | -| 3 | Plan name | Absolute link to the plan page: `[PLAN_{HASH}]()`, where `` comes from `jsc-gitea/tools/gitea.sh wiki-url {owner}/{repo} PLAN_{HASH}` — PLAN and LOG may live in different wiki repos, and `[[...]]` only resolves inside one wiki | -| 4 | Work package id | Absolute link to the work package heading: `[WP-xx](#wp-xx)`, `` from `gitea.sh wiki-url {owner}/{repo} ANALYZE_{HASH}` | +| 3 | Plan name | Absolute link to the plan page: `[PLAN_{HASH}]()`. Resolve the hosting repo with `jsc-gitea/tools/gitea.sh wiki-repo PLAN`, then take `` from `gitea.sh wiki-url PLAN_{HASH}` — PLAN and LOG may live in different wiki repos, and `[[...]]` only resolves inside one wiki. `wiki-repo` exit 3 (no wiki repo configured for that type) or `wiki-url` exit 4 (page not found) → fill the literal 「無」 for this row and carry on; a `worklog` run triggered from `maintain` normally has no plan page | +| 4 | Work package id | Absolute link to the work package heading: `[WP-xx](#wp-xx)`. Resolve the hosting repo with `gitea.sh wiki-repo ANALYZE`, then take `` from `gitea.sh wiki-url ANALYZE_{HASH}`. Same fallback as row 3: `wiki-repo` exit 3 or `wiki-url` exit 4 → fill 「無」 and carry on | | 5 | Elapsed time | `jsc-hooks/hooks/session-timer.sh report {session_id}` (seconds; convert to h/m) | -| 6 | Token usage | See the table below; per-CLI methods differ. Fill `N/A` when unavailable | -| 7 | Task status | One of the literal values 「完成」、「部分完成」、「阻塞」 (with reason when blocked) | -| 8 | Details and outputs | Summarize what changed and which files or pages were produced | -| 9 | Difficulties and resolutions | One pair per line | +| 6 | Token usage | `tools/token-usage.sh {session_id}` per CLI that ran; it prints `inputoutput`. Pass the same `{session_id}` as row 5 so the elapsed time and the token count describe one session. Fill `N/A` in both columns when it prints `N/A`; exit 2 means the CLI name is not one of claude / codex / copilot / antigravity / kiro, so fix the name and rerun | +| 7 | Task status | One of the literal values 「完成」, 「部分完成」, 「阻塞」 (with reason when blocked). Derive it from the session when the session shows it; otherwise ask via `jsc-ask:ask`, offering those three literals as the options and stating each option's impact scope (「完成」 closes the work package, 「部分完成」 leaves the remainder open for the next run, 「阻塞」 records the blocker and hands it back to the operator) | +| 8 | Details and outputs | One line per changed file or produced page: what changed there and why | +| 9 | Difficulties and resolutions | One pair per line. Ask via `jsc-ask:ask` when the session does not show them | | 10 | PR target branch | Link to the PR page | -### How to get token usage - -| CLI | Method | -| --- | --- | -| claude | Sum the `usage` fields in transcript JSONL (`~/.claude/projects/**/*.jsonl`), or read `usage` from `claude -p --output-format json` | -| codex | Token counters under `~/.codex/sessions/**`, or `/status` inside the session | -| copilot | `/usage` inside the session | -| antigravity | Value shown in the UI; `N/A` when unreadable | -| kiro | No public source; fill `N/A` | +The `{HASH}` in every page name above is computed with `jsc-gitea/tools/hash-id`. ## Target page and work week -- Page name: `LOG_{HASH}`. -- Compute `{HASH}` from `{owner}/{repo}` with `jsc-gitea/tools/hash-id` (shared wiki hash rule: first 8 uppercase SHA-1 hex chars; `H` plus the first 7 chars when the raw hash starts with `0-9`, `A`, `B`, or `C`). -- Compute the target page by running `tools/worklog-target.sh "{HASH}" all`. Use `PAGE` for `LOG_{HASH}` and `CONTENTS` for `LOG_CONTENTS`. -- The work-week Friday still drives the page content and the row dates. -- Read the page via `jsc-gitea:wiki`. If it does not exist, create it with the structure described in `templates/log-contents.md`; otherwise APPEND the new entry at the end. -- Update `LOG_CONTENTS` in the same pass (apply `templates/log-contents.md`; add the row if missing). +1. Resolve the wiki repo hosting LOG pages: `JSC_WIKI_REPO_LOG` first, then `JSC_WIKI_REPO`, via `gitea.sh wiki-repo LOG`. Inspect the inherited shell environment variables first; ask the user per the `jsc-ask:ask` rules only when neither resolves. Never borrow another type's `JSC_WIKI_REPO_{TYPE}`. Done when the hosting `{owner}/{repo}` is known. +2. Compute `{HASH}` from the code repo's `{owner}/{repo}` with `jsc-gitea/tools/hash-id`. Done when the 8-character `{HASH}` is known. +3. Run `tools/worklog-target.sh "{HASH}" all`. Use `PAGE` for `LOG_{HASH}` and `CONTENTS` for `LOG_CONTENTS`. Done when both page names are known. +4. Fix the work week: the Friday of the current work week drives the page content and the row dates. Done when that Friday is fixed as a `yyyy-MM-dd` date. +5. Read `PAGE` via `jsc-gitea:wiki`. If it does not exist, create it with the structure described in `templates/log-entry.md`; otherwise APPEND the new entry at the end and never overwrite existing entries. Done when the new entry exists on `PAGE`. +6. Update `CONTENTS` in the same pass (apply `templates/log-contents.md`; add the row if missing, otherwise refresh its 條目數 and 最後更新). Done when the row for `PAGE` carries this week's Friday date. diff --git a/tools/token-usage.sh b/tools/token-usage.sh new file mode 100755 index 0000000..5ed80b1 --- /dev/null +++ b/tools/token-usage.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env sh +# token-usage.sh — 讀出單一 CLI 這次工作的 token 用量。 +# 用法: +# token-usage.sh [session-id] # cli = claude|codex|copilot|antigravity|kiro +# 輸出: 一行「(tab)」。任何來源讀不到就印「N/A(tab)N/A」並 exit 0。 +# 護欄: 沒給 cli 或 cli 名稱不認得,回傳 2。 +# +# 各 CLI 的取得方式(原本寫在 worklog 的 SKILL.md,現在收在這裡): +# claude 加總 transcript JSONL(`$CLAUDE_CONFIG_DIR` 或 `~/.claude` 底下的 +# `projects/**/*.jsonl`)每筆訊息的 `usage.input_tokens` 與 +# `usage.output_tokens`。也可以改讀 `claude -p --output-format json` +# 回應裡的 `usage` 欄位。 +# codex 讀 `~/.codex/sessions/**` 的 token 計數欄位;session 內打 `/status` +# 也看得到同一組數字。 +# copilot 只有 session 內的 `/usage` 看得到,沒有可讀檔案 → N/A。 +# antigravity 只顯示在 UI,沒有可讀檔案 → N/A。 +# kiro 沒有公開來源 → N/A。 +# +# 有給 session id 就優先取檔名含該 id 的 session 檔,因為日誌的花費時間也是按同一個 +# session id 取的(session-timer.sh report {sid}),兩個數字必須描述同一個工作階段。 +# 沒給 session id 才退回「最後修改的那一份」——那份可能是別的工作階段,或同一階段的 +# sub agent 紀錄,數字只能當粗估。 +set -u + +na() { printf 'N/A\tN/A\n'; exit 0; } + +cli="${1:-}" +sid="${2:-}" +if [ -z "$cli" ]; then + echo 'usage: token-usage.sh [session-id]' >&2 + exit 2 +fi + +newest() { # -> 最後修改的檔案路徑;找不到回非零 + [ -d "$1" ] || return 1 + f=$(find "$1" -type f -name "$2" -exec ls -t {} + 2>/dev/null | head -1) + [ -n "$f" ] || return 1 + printf '%s\n' "$f" +} + +pick() { # -> 先找 session id 相符的檔案,找不到才退回最後修改的 + if [ -n "$sid" ]; then + if f=$(newest "$1" "*$sid*.jsonl"); then printf '%s\n' "$f"; return 0; fi + echo "warn: 找不到 session $sid 的 transcript,改用最後修改的那一份,數字可能不是本階段的。" >&2 + fi + newest "$1" '*.jsonl' +} + +sum_field() { # <欄位名> -> 該欄位所有出現值的總和;沒有值就印空字串 + grep -o "\"$2\"[[:space:]]*:[[:space:]]*[0-9][0-9]*" "$1" 2>/dev/null \ + | grep -o '[0-9][0-9]*$' \ + | awk '{ s += $1 } END { if (NR > 0) print s }' +} + +case "$cli" in + claude) + src=$(pick "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/projects") || na ;; + codex) + src=$(pick "$HOME/.codex/sessions") || na ;; + copilot|antigravity|kiro) + na ;; + *) + echo "unknown cli: $cli" >&2 + exit 2 ;; +esac + +[ -r "$src" ] || na +# input 要把快取的部分加回來:Claude 的 usage 只在 input_tokens 記未快取的那幾個 token, +# 真正的輸入量落在 cache_creation_input_tokens 與 cache_read_input_tokens。只加第一欄, +# 報表會出現輸入幾百、輸出幾十萬的假數字。 +input=$(sum_field "$src" input_tokens) +cache_new=$(sum_field "$src" cache_creation_input_tokens) +cache_hit=$(sum_field "$src" cache_read_input_tokens) +output=$(sum_field "$src" output_tokens) +[ -n "$input" ] && [ -n "$output" ] || na +input=$((input + ${cache_new:-0} + ${cache_hit:-0})) +printf '%s\t%s\n' "$input" "$output" From 80f05ea1e30224f07ece2f5ff5479f0bdd36acb7 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Tue, 25 Aug 2026 14:58:54 +0800 Subject: [PATCH 2/3] =?UTF-8?q?docs(log):=20=E5=90=8C=E6=AD=A5=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E8=88=87=E5=8F=83=E8=80=83=E8=B3=87=E6=96=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What:更新 README、AGENTS.md、templates 與 references,讓文件敘述與實際行為一致。 Why:稽核發現多處文件與程式行為分歧,違反「每個意義只有單一真實來源」。 How:以實際程式行為為準改寫敘述,重複的規則收成單一來源並以一行指引指過去。 Who:jsc-meta:skill-check 例行稽核(2026-08-25)。 Co-Authored-By: Claude Opus 5 --- README.md | 1 + templates/log-entry.md | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index f63a5a3..3335079 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安 | --- | --- | | `tools/usage-stats.sh` | 聚合 `$JSC_HOME/usage/*.jsonl`:`skills` 列技能使用次數、`chains` 列呼叫鏈次數(皆降冪),`--cli ` 過濾 | | `tools/worklog-target.sh` | 接收已由 `jsc-gitea/tools/hash-id` 算好的 `HASH`,組出 `LOG_{HASH}`、`LOG_CONTENTS`(本身不再計算 SHA-1) | +| `tools/token-usage.sh` | 讀單一 CLI 這次工作的 token 用量,印出「input(tab)output」;來源讀不到就印「N/A(tab)N/A」並正常結束。第二個參數傳 session id,就只讀該階段的 transcript,數字才會跟花費時間對得上。各 CLI 的取得方式寫在腳本開頭註解 | ## Skills 目錄 diff --git a/templates/log-entry.md b/templates/log-entry.md index 8aa2954..7a046eb 100644 --- a/templates/log-entry.md +++ b/templates/log-entry.md @@ -3,6 +3,7 @@ > 由 `jsc-log:worklog` 維護。每完成一項工作附加一個條目在文末。 > `HASH` 依共享規則計算;頁名不再寫入年月週數,但頁內仍依本工作週的週五整理內容。 > {PLAN 頁絕對網址}、{ANALYZE 頁絕對網址} 由 `jsc-gitea/tools/gitea.sh wiki-url` 取得——LOG 與 PLAN/ANALYZE 可能分屬不同存取庫,`[[頁名]]` 跨庫不通。 +> 解不出 wiki 存取庫或頁面不存在時,該欄填「無」,其他欄照填。由 `maintain` 觸發的日誌本來就沒有計畫頁與分析頁。 --- From 42e98f11def10cba7d7176213974c4cb44af9541 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Tue, 25 Aug 2026 14:58:54 +0800 Subject: [PATCH 3/3] =?UTF-8?q?chore(log):=20=E4=B8=89=E4=BB=BD=20manifest?= =?UTF-8?q?=20=E5=90=8C=E6=AD=A5=E5=8D=87=E7=89=88=E4=B8=A6=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=20marketplace=20=E6=AD=A3=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What:三份 plugin manifest 版本同步 bump,兩份 marketplace 檔與 plugins/meta 正本對齊。 Why:準則要求技能異動必須同步升版;marketplace 副本必須與正本完全一致。 How:以 jsc-meta 的 tools/sync-skill-manifest.sh 升版,marketplace 檔由正本複製。 Who:jsc-meta:skill-check 例行稽核(2026-08-25)。 Co-Authored-By: Claude Opus 5 --- .agents/plugins/marketplace.json | 6 +++--- .claude-plugin/marketplace.json | 6 +++--- .claude-plugin/plugin.json | 2 +- .codex-plugin/plugin.json | 2 +- plugin.json | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json index a9b4e66..8a2f91b 100644 --- a/.agents/plugins/marketplace.json +++ b/.agents/plugins/marketplace.json @@ -43,7 +43,7 @@ "source": "url", "url": "https://gitea.jsc.idv.tw/plugins/hooks.git" }, - "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄" + "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查" }, { "name": "jsc-log", @@ -75,7 +75,7 @@ "source": "url", "url": "https://gitea.jsc.idv.tw/plugins/review.git" }, - "description": "程式碼審查:Refactoring 壞味道六組 + 註解規範 + 淺模組" + "description": "程式碼審查:Refactoring 壞味道六組、註解規範、淺模組" }, { "name": "jsc-sdlc", @@ -83,7 +83,7 @@ "source": "url", "url": "https://gitea.jsc.idv.tw/plugins/sdlc.git" }, - "description": "開發生命週期:規劃/分析/實作/維護(wiki 追蹤)" + "description": "開發生命週期:規劃、分析、實作、維護(wiki 追蹤)" } ] } diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index a9b4e66..8a2f91b 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -43,7 +43,7 @@ "source": "url", "url": "https://gitea.jsc.idv.tw/plugins/hooks.git" }, - "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄" + "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查" }, { "name": "jsc-log", @@ -75,7 +75,7 @@ "source": "url", "url": "https://gitea.jsc.idv.tw/plugins/review.git" }, - "description": "程式碼審查:Refactoring 壞味道六組 + 註解規範 + 淺模組" + "description": "程式碼審查:Refactoring 壞味道六組、註解規範、淺模組" }, { "name": "jsc-sdlc", @@ -83,7 +83,7 @@ "source": "url", "url": "https://gitea.jsc.idv.tw/plugins/sdlc.git" }, - "description": "開發生命週期:規劃/分析/實作/維護(wiki 追蹤)" + "description": "開發生命週期:規劃、分析、實作、維護(wiki 追蹤)" } ] } diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 3536cff..73faddd 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-log", - "version": "0.0.6", + "version": "0.0.8", "description": "工作日誌(LOG_{HASH} wiki 頁)、技能使用統計與教訓紀錄(LEARN_{HASH} wiki 頁)", "skills": "./skills", "author": { diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index dfde5e2..09756e6 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-log", - "version": "0.0.6", + "version": "0.0.8", "description": "工作日誌(LOG_{HASH} wiki 頁)、技能使用統計與教訓紀錄(LEARN_{HASH} wiki 頁)", "skills": "./skills" } diff --git a/plugin.json b/plugin.json index 9389bb4..bbed3be 100644 --- a/plugin.json +++ b/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-log", - "version": "0.0.6", + "version": "0.0.8", "description": "工作日誌(LOG_{HASH} wiki 頁)、技能使用統計與教訓紀錄(LEARN_{HASH} wiki 頁)", "skills": "./skills/" }