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>
This commit is contained in:
Executable
+24
@@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# jsc-wrap.sh — 無 hook 系統 CLI 的包裝啟動器(例:copilot、antigravity)。
|
||||||
|
# 用法: jsc-wrap.sh {cli} [args...]
|
||||||
|
# 行為: 匯出 JSC_CLI 與 JSC_SESSION_ID → session-timer start → 執行 CLI →
|
||||||
|
# 結束後 session-timer mark 並以 scan-logs.sh 回填用量,最後回傳 CLI 的結束碼。
|
||||||
|
HERE=$(cd "$(dirname "$0")" && pwd)
|
||||||
|
HOOKS="$HERE/../hooks"
|
||||||
|
cli="${1:-}"
|
||||||
|
if [ -z "$cli" ]; then
|
||||||
|
echo "用法:jsc-wrap.sh {cli} [args...]" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
JSC_CLI="$cli"
|
||||||
|
# 未提供 session id 就自動產生({cli}-時間戳-PID),讓計時與用量共用同一個 session
|
||||||
|
[ -n "${JSC_SESSION_ID:-}" ] || JSC_SESSION_ID="$cli-$(date +%Y%m%d%H%M%S)-$$"
|
||||||
|
export JSC_CLI JSC_SESSION_ID
|
||||||
|
sh "$HOOKS/session-timer.sh" start </dev/null
|
||||||
|
"$cli" "$@"
|
||||||
|
rc=$?
|
||||||
|
# 收尾:補記結束時間,並從原生日誌回填技能用量
|
||||||
|
sh "$HOOKS/session-timer.sh" mark </dev/null
|
||||||
|
sh "$HERE/scan-logs.sh" --cli "$cli" --session "$JSC_SESSION_ID" </dev/null || true
|
||||||
|
exit "$rc"
|
||||||
Executable
+91
@@ -0,0 +1,91 @@
|
|||||||
|
#!/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
|
||||||
Reference in New Issue
Block a user