#!/usr/bin/env bash # ============================================================================== # 用途:SessionStart hook 主程式。CLI 工具啟動時載入角色設定與記憶: # 非睡眠時段注入角色定義+重要/興趣記憶全文+其餘記憶的總結與標籤; # 睡眠時段(預設 22:00 至隔日 06:00)只回報角色正在睡覺,不載入角色。 # 白天發現昨夜未整理記憶時,於背景補跑一次睡眠整理。 # 實際的 context 組裝在 role_context.sh,與對話中點名載入(role_call.sh)共用。 # 更新時間:2026/07/29 13:25:00 # 相依:bash、node、同目錄的 role_lib.sh、role_context.sh 與 memory.js。 # 退出碼:一律 0 —— hook 絕不可阻斷使用者啟動 CLI。 # ============================================================================== ROLE_STAGE="role-load" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # shellcheck source=./role_lib.sh . "${SCRIPT_DIR}/role_lib.sh" # shellcheck source=./role_context.sh . "${SCRIPT_DIR}/role_context.sh" role_is_child && exit 0 role_enabled || exit 0 command -v node >/dev/null 2>&1 || role_quit "找不到 node,略過角色載入" "WRN" ROLE="$(role_resolve_name)" [ -n "$ROLE" ] || role_quit "未指定角色(ROLE_NAME 與 .active 皆無),略過角色載入" ROLE_DEF="$(role_file "$ROLE")" [ -f "$ROLE_DEF" ] || role_quit "找不到角色定義檔:${ROLE_DEF}" "WRN" # ------------------------------------------------------------------------------ # 讀取 hook 輸入(cwd/transcript/session),並套用 ROLE_SCOPE 範圍限制 # ------------------------------------------------------------------------------ HOOK_INPUT="$(cat 2>/dev/null)" HOOK_CWD="$PWD" HOOK_TRANSCRIPT="" HOOK_SESSION="" if [ -n "$HOOK_INPUT" ]; then HOOK_FIELDS="$(printf '%s' "$HOOK_INPUT" | node -e ' let raw = ""; process.stdin.setEncoding("utf8"); process.stdin.on("data", (chunk) => { raw += chunk; }); process.stdin.on("end", () => { let data = {}; try { data = JSON.parse(raw); } catch {} process.stdout.write([ data.cwd || "", data.transcript_path || data.session_path || data.conversation_path || data.path || "", data.session_id || data.thread_id || data.conversation_id || "", ].join("\n")); }); ' 2>/dev/null)" HOOK_CWD="$(printf '%s' "$HOOK_FIELDS" | sed -n '1p')" HOOK_TRANSCRIPT="$(printf '%s' "$HOOK_FIELDS" | sed -n '2p')" HOOK_SESSION="$(printf '%s' "$HOOK_FIELDS" | sed -n '3p')" [ -n "$HOOK_CWD" ] || HOOK_CWD="$PWD" fi role_in_scope "$HOOK_CWD" || role_quit "cwd 不在 ROLE_SCOPE 範圍內:${HOOK_CWD}" # 本階段的角色狀態:供 Stop hook 判斷記憶該寫給誰(點名載入後會被 role_call.sh 覆寫) SESSION_KEY="$(role_session_key "$HOOK_SESSION" "$HOOK_TRANSCRIPT")" SLEEP_START="$(role_sleep_start)" SLEEP_END="$(role_sleep_end)" # ------------------------------------------------------------------------------ # 單一載入實例:角色已在另一個仍活躍的工作階段時,本次不載入人格 # 放在睡眠判斷之前,因為「已在別處使用」與「睡覺中」是互斥狀態,且不該佔用鎖 # ------------------------------------------------------------------------------ if ! role_instance_acquire "$ROLE" "$HOOK_TRANSCRIPT" "$HOOK_CWD"; then LOCK_FILE="$(role_instance_lock_path "$ROLE")" HOLDER_TIME="$(role_instance_lock_field "$LOCK_FILE" loaded)" HOLDER_CWD="$(role_instance_lock_field "$LOCK_FILE" cwd)" # 明確記成「本階段沒有人格」,點名載入才不會誤以為原角色還在 role_session_set "$SESSION_KEY" "" "$HOOK_TRANSCRIPT" role_log "INF" "角色 ${ROLE} 已被其他工作階段載入(${HOLDER_TIME:-時間未知}),本次不載入" role_context_emit "SessionStart" "$(cat <,…」),該角色未被佔用時會即時接手 EOF_BUSY )" exit 0 fi # ------------------------------------------------------------------------------ # 睡眠時段:不載入角色,只說明目前狀態 # ------------------------------------------------------------------------------ if role_in_sleep_window; then # 明確記成「本階段沒有人格」,點名載入才不會誤以為原角色還在 role_session_set "$SESSION_KEY" "" "$HOOK_TRANSCRIPT" role_context_emit "SessionStart" "$(cat <