Files
hooks/tools/scan-logs.sh
jiantw83andClaude Fable 5 bb91cc2832 feat(wrapper): 新增無 Hook CLI 的包裝啟動器與日誌回填
What:新增 tools/jsc-wrap.sh 包裝啟動器與 tools/scan-logs.sh 離線日誌回填腳本。
Why:copilot、antigravity 沒有 hook 系統,計時與技能用量原本完全缺漏,需要 hook 之外的落地方案。
How:jsc-wrap.sh 匯出 JSC_CLI 與 JSC_SESSION_ID,啟動前後各呼叫一次 session-timer.sh,結束時自動執行 scan-logs.sh 並原樣傳遞子行程退出碼;scan-logs.sh 解析 copilot、antigravity、codex 的原生日誌,把技能用量與階段界線補進 $JSC_HOME,重掃不產生重複記錄。
Who:無 hook 系統的 CLI(copilot、antigravity)與僅有 notify 的 codex,資料供 jsc-log:worklog 與 jsc-log:stats 使用。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 11:36:24 +08:00

92 lines
3.8 KiB
Bash
Executable File
Raw Permalink 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.
#!/usr/bin/env sh
# scan-logs.sh — 離線回填:解析各 CLI 的原生日誌,把可辨識的技能使用與工作階段界線
# 補進 $JSC_HOME/usage/skills.jsonl(與 skill-usage.sh 同一種紀錄格式)與 $JSC_HOME/sessions/。
# 用法: scan-logs.sh --cli {name} [--session {sid}]
# 支援: copilot(~/.config/copilot/)、antigravity(全域日誌目錄)、codex(~/.codex/sessions/、~/.codex/log/)。
# 去重: 以 $JSC_HOME/usage/scan-state/ 記住每個日誌檔已掃描的位元組數,重掃只讀新增段落。
HERE=$(cd "$(dirname "$0")" && pwd); . "$HERE/../hooks/lib.sh"
cli=""; sid=""
while [ $# -gt 0 ]; do
case "$1" in
--cli) cli="${2:-}"; shift; shift ;;
--session) sid="${2:-}"; shift; shift ;;
*) shift ;;
esac
done
if [ -z "$cli" ]; then
echo "用法:scan-logs.sh --cli {name} [--session {sid}]" >&2
exit 2
fi
STATE="$JSC_HOME/usage/scan-state"
mkdir -p "$STATE" 2>/dev/null || true
# 檔案修改時間(epoch),取不到就用現在
mtime_epoch() {
stat -c %Y "$1" 2>/dev/null || date -r "$1" +%s 2>/dev/null || now_epoch
}
# 回填工作階段界線:start 只補缺,end 取較新值(不覆蓋較新的即時紀錄)
record_session() { # $1=sid $2=epoch
[ -f "$JSC_HOME/sessions/$1.start" ] || echo "$2" > "$JSC_HOME/sessions/$1.start"
old=$(cat "$JSC_HOME/sessions/$1.end" 2>/dev/null || echo 0)
if [ "$2" -gt "$old" ] 2>/dev/null; then echo "$2" > "$JSC_HOME/sessions/$1.end"; fi
}
# 印出日誌檔自上次掃描後的新增內容,並更新位移(位移即去重機制)
new_content() { # $1=file
key=$(printf '%s' "$1" | cksum | tr ' \t' '--')
off_f="$STATE/$cli-$key.offset"
off=$(cat "$off_f" 2>/dev/null || echo 0)
size=$(wc -c < "$1" 2>/dev/null || echo 0)
[ "$size" -gt "$off" ] 2>/dev/null || return 0
tail -c +"$((off + 1))" "$1" 2>/dev/null
echo "$size" > "$off_f"
}
# 從新增內容萃取 jsc 技能代號,逐筆追加(同一批先去重;格式與 skill-usage.sh 完全相同)
append_skills() { # stdin=內容 $1=sid
grep -oE 'jsc-[a-z]+:[a-z0-9][a-z0-9-]*' 2>/dev/null | sort -u | while IFS= read -r sk; do
printf '{"ts":"%s","cli":"%s","session":"%s","skill":"%s"}\n' \
"$(now_iso)" "$cli" "$1" "$sk" >> "$JSC_HOME/usage/skills.jsonl"
done
}
# 掃描一個日誌目錄;$2=預設 sid(空字串時用檔名當 sid);$3=sessions 時回填階段界線
scan_dir() {
find "$1" -maxdepth 3 -type f \( -name '*.json' -o -name '*.jsonl' -o -name '*.log' \) 2>/dev/null \
| while IFS= read -r f; do
s="$2"
[ -n "$s" ] || s="$cli-$(basename "$f" | sed 's/\.[^.]*$//')"
[ "${3:-}" = "sessions" ] && record_session "$s" "$(mtime_epoch "$f")"
new_content "$f" | append_skills "$s"
done
return 0
}
case "$cli" in
copilot)
d="$HOME/.config/copilot"
if [ ! -d "$d" ]; then echo "[jsc] 找不到 copilot 日誌目錄($d),略過回填。"; exit 0; fi
[ -d "$d/history-session-state" ] && scan_dir "$d/history-session-state" "$sid" sessions
[ -d "$d/history" ] && scan_dir "$d/history" "$sid" sessions
[ -d "$d/logs" ] && scan_dir "$d/logs" "$sid" logs ;;
antigravity)
found=""
for d in "$HOME/.antigravity/logs" "$HOME/.config/antigravity/logs" "$HOME/.antigravity"; do
if [ -d "$d" ]; then found=1; scan_dir "$d" "$sid" sessions; break; fi
done
if [ -z "$found" ]; then echo "[jsc] 找不到 antigravity 日誌目錄,略過回填。"; exit 0; fi ;;
codex)
d1="$HOME/.codex/sessions"; d2="$HOME/.codex/log"
if [ ! -d "$d1" ] && [ ! -d "$d2" ]; then
echo "[jsc] 找不到 codex 日誌目錄($d1、$d2),略過回填。"; exit 0
fi
[ -d "$d1" ] && scan_dir "$d1" "$sid" sessions
[ -d "$d2" ] && scan_dir "$d2" "$sid" logs ;;
*)
echo "[jsc] 尚未支援 $cli 的日誌回填,略過。" ;;
esac
exit 0