refactor(role): 將角色記憶工具改寫為 Node.js

This commit is contained in:
Jeffery
2026-07-28 12:30:28 +08:00
parent e71f165e9a
commit fec3e1d0cb
8 changed files with 1090 additions and 1125 deletions
+27 -23
View File
@@ -4,8 +4,8 @@
# 非睡眠時段注入角色定義+重要/興趣記憶全文+其餘記憶的總結與標籤;
# 睡眠時段(預設 22:00 至隔日 06:00)只回報角色正在睡覺,不載入角色。
# 白天發現昨夜未整理記憶時,於背景補跑一次睡眠整理。
# 更新時間:2026/07/28 11:28:29
# 相依:bash、python3、同目錄的 role_lib.sh 與 memory.py
# 更新時間:2026/07/28 12:21:11
# 相依:bash、node、同目錄的 role_lib.sh 與 memory.js
# 退出碼:一律 0 —— hook 絕不可阻斷使用者啟動 CLI。
# ==============================================================================
@@ -16,7 +16,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
role_is_child && exit 0
role_enabled || exit 0
command -v python3 >/dev/null 2>&1 || role_quit "找不到 python3,略過角色載入" "WRN"
command -v node >/dev/null 2>&1 || role_quit "找不到 node,略過角色載入" "WRN"
ROLE="$(role_resolve_name)"
[ -n "$ROLE" ] || role_quit "未指定角色(ROLE_NAME 與 .active 皆無),略過角色載入"
@@ -29,27 +29,31 @@ ROLE_DEF="$(role_file "$ROLE")"
HOOK_INPUT="$(cat 2>/dev/null)"
HOOK_CWD="$PWD"
if [ -n "$HOOK_INPUT" ]; then
HOOK_CWD="$(printf '%s' "$HOOK_INPUT" | python3 -c '
import json, sys
try:
data = json.load(sys.stdin)
except ValueError:
data = {}
print(data.get("cwd") or "")
HOOK_CWD="$(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 || "");
});
' 2>/dev/null)"
[ -n "$HOOK_CWD" ] || HOOK_CWD="$PWD"
fi
role_in_scope "$HOOK_CWD" || role_quit "cwd 不在 ROLE_SCOPE 範圍內:${HOOK_CWD}"
emit_context() {
# 以 JSON 輸出 additionalContext(由 python 負責跳脫,避免內容含引號或換行破壞格式)
printf '%s' "$1" | python3 -c '
import json, sys
context = sys.stdin.read()
print(json.dumps(
{"hookSpecificOutput": {"hookEventName": "SessionStart", "additionalContext": context}},
ensure_ascii=False,
))
# 以 JSON 輸出 additionalContext(由 node 負責跳脫,避免內容含引號或換行破壞格式)
printf '%s' "$1" | node -e '
let context = "";
process.stdin.setEncoding("utf8");
process.stdin.on("data", (chunk) => { context += chunk; });
process.stdin.on("end", () => {
process.stdout.write(JSON.stringify({
hookSpecificOutput: { hookEventName: "SessionStart", additionalContext: context },
}));
});
'
}
@@ -77,11 +81,11 @@ fi
DEFINITION="$(cat "$ROLE_DEF" 2>/dev/null)"
[ -n "$DEFINITION" ] || role_quit "角色定義檔為空:${ROLE_DEF}" "WRN"
MEMORY="$(python3 "${SCRIPT_DIR}/memory.py" load --role "$ROLE" 2>/dev/null)"
MEMORY="$(node "${SCRIPT_DIR}/memory.js" load --role "$ROLE" 2>/dev/null)"
# 補跑判斷:cron 未執行(例如 WSL 沒開 cron 服務)時,白天啟動 CLI 補做一次整理
CATCHUP_NOTE=""
if [ "$(python3 "${SCRIPT_DIR}/memory.py" need-sleep --role "$ROLE" 2>/dev/null)" = "yes" ]; then
if [ "$(node "${SCRIPT_DIR}/memory.js" need-sleep --role "$ROLE" 2>/dev/null)" = "yes" ]; then
nohup "${SCRIPT_DIR}/role_sleep.sh" --catchup >/dev/null 2>&1 &
CATCHUP_NOTE=$'\n> 偵測到上個睡眠時段未整理記憶,已在背景補跑整理,結果會在下次載入時反映。\n'
role_log "INF" "已於背景補跑記憶整理(角色 ${ROLE}"
@@ -105,13 +109,13 @@ ${DEFINITION}
${MEMORY:-(尚無已整理的記憶。)}
${CATCHUP_NOTE}
> 記憶載入規則:重要與興趣記憶載入全文;技能、新知、日常、其他僅載入總結與標籤,
> 需要細節時可自行讀取 $(role_memory_home)/${ROLE}/ 下對應分類的記憶檔。
> 記憶載入規則:為節省模型額度,只載入高優先度全文與中高優先度摘要,並受 ROLE_LOAD_LIMIT
> 字元預算限制;需要細節時可自行讀取 $(role_memory_home)/${ROLE}/ 下對應分類的記憶檔。
> 主動補記:每輪對話結束後系統會自動記錄記憶,不需你動手。但若使用者明確要求記住某件事,
> 或你察覺到值得長期記住的偏好、決策、規範,可執行下列指令補一則記憶(下次睡眠時整理歸檔):
>
> \`printf 'CATEGORY: important\nSUMMARY: <一句話總結>\nTAGS: <標籤1,標籤2>\nCONTENT:\n- <要點>\n' | python3 "${SCRIPT_DIR}/memory.py" write --role "${ROLE}"\`
> \`printf 'CATEGORY: important\nSUMMARY: <一句話總結>\nTAGS: <標籤1,標籤2>\nCONTENT:\n- <要點>\n' | node "${SCRIPT_DIR}/memory.js" write --role "${ROLE}"\`
>
> CATEGORY 六選一:importantinterestnewsskilldailyother。切勿把憑證或個資寫進記憶。
EOF_CONTEXT