让 Claude Code 每轮自动记工作日志:asaptodo Stop hook
给 Claude Code 装一个 Stop hook:每轮结束时要求模型往 asaptodo 记一条工作日志,没有实质性工作的回合说明理由而不是硬造。
安装提示词
asaptodo · 装上「每回合自动写工作日志」
我要你在这台机器上给 Claude Code 装一个 Stop hook:每轮结束时拦一次,提醒记一条 asaptodo 工作日志。
按顺序做,一步做完再做下一步。
1. 跑 jq --version 确认有 jq。没有就停下来告诉我(macOS 上是 brew install jq),别自己装系统软件。
2. 跑 mkdir -p ~/.claude/hooks
3. 把下面两行 ===== 之间的内容原样写进 ~/.claude/hooks/worklog-stop.sh,一个字都别改:
=====
#!/usr/bin/env bash
# Stop hook:每个用户回合结束时拦一次,要求记一条 worklog,走哪条接入路线由配置决定。
#
# 为什么必须防循环:Stop hook 无条件 exit 2 会让模型永远停不下来(官方文档明确
# 警告过这一点)。这里三重兜底 ——
# 1) stop_hook_active 为真:说明这一轮已经被 Stop hook 续过一次,不再拦
# 2) 按 prompt_id 的标记文件:同一个用户回合最多拦一次
# 3) 拿不到 prompt_id 就直接放过(宁可漏拦,不可失控)
#
# 注意它只是「逼模型去写」,不代替模型写 —— shell 不知道这一轮干了什么,
# 直接 POST 只能造出没有 title / 正文的空壳,而日志是 append-only 删不掉的。
#
# 文案不提实现方式:hook 的职责是提醒去记,不该知道底下是 MCP 还是 curl ——
# 模型自己看得见有没有 worklog_create 工具。两条接入路线(/worklog/setup 的
# skill 方式、/worklog/mcp-setup 的 MCP 方式)共用这一个脚本,装两个会导致
# 每回合拦两次。
set -uo pipefail
input=$(cat)
active=$(printf '%s' "$input" | jq -r '.stop_hook_active // false' 2>/dev/null || echo false)
prompt_id=$(printf '%s' "$input" | jq -r '.prompt_id // empty' 2>/dev/null || true)
cwd=$(printf '%s' "$input" | jq -r '.cwd // empty' 2>/dev/null || true)
[ "$active" = "true" ] && exit 0
[ -z "$prompt_id" ] && exit 0
marker_dir="${TMPDIR:-/tmp}/claude-worklog-stop"
mkdir -p "$marker_dir" 2>/dev/null || exit 0
marker="$marker_dir/$prompt_id"
[ -e "$marker" ] && exit 0
: > "$marker"
# 顺手清掉一天前的标记,免得无限堆积
find "$marker_dir" -type f -mtime +1 -delete 2>/dev/null || true
cat >&2 <<EOF
worklog hook:这一轮结束前记一条 worklog。
有 asaptodo 的 worklog_create 工具就调它;没有就按
~/.claude/skills/worklog/SKILL.md 的流程走。
working_directory=$cwd
标题要具体(禁止「完成任务」),completed_at 用真实时间不伪造。
这一轮确实没有可记的实质性工作时,说明理由即可,不要凭空造条目 ——
日志是 append-only,写错了删不掉。
(这条拦截每个用户回合只出现一次,不会循环。)
EOF
exit 2
=====
4. 跑 chmod +x ~/.claude/hooks/worklog-stop.sh
5. 把下面这一项并进 ~/.claude/settings.json 的 hooks.Stop:
{"hooks":[{"type":"command","command":"$HOME/.claude/hooks/worklog-stop.sh","timeout":10}]}
- 文件不存在就新建;存在就只往 hooks.Stop 里追加,别动其他键,也别覆盖已有的 hook
- hooks.Stop 里已经有 command 是 $HOME/.claude/hooks/worklog-stop.sh 的项,就别重复加
- 用 jq 改,改完跑 jq empty ~/.claude/settings.json 确认 JSON 合法
- 别把 settings.json 的内容打印出来 —— 里面可能有 token
6. 告诉我装好了。hook 是会话启动时读的,提醒我新开一个会话才生效。
hook 脚本
#!/usr/bin/env bash
# Stop hook:每个用户回合结束时拦一次,要求记一条 worklog,走哪条接入路线由配置决定。
#
# 为什么必须防循环:Stop hook 无条件 exit 2 会让模型永远停不下来(官方文档明确
# 警告过这一点)。这里三重兜底 ——
# 1) stop_hook_active 为真:说明这一轮已经被 Stop hook 续过一次,不再拦
# 2) 按 prompt_id 的标记文件:同一个用户回合最多拦一次
# 3) 拿不到 prompt_id 就直接放过(宁可漏拦,不可失控)
#
# 注意它只是「逼模型去写」,不代替模型写 —— shell 不知道这一轮干了什么,
# 直接 POST 只能造出没有 title / 正文的空壳,而日志是 append-only 删不掉的。
#
# 文案不提实现方式:hook 的职责是提醒去记,不该知道底下是 MCP 还是 curl ——
# 模型自己看得见有没有 worklog_create 工具。两条接入路线(/worklog/setup 的
# skill 方式、/worklog/mcp-setup 的 MCP 方式)共用这一个脚本,装两个会导致
# 每回合拦两次。
set -uo pipefail
input=$(cat)
active=$(printf '%s' "$input" | jq -r '.stop_hook_active // false' 2>/dev/null || echo false)
prompt_id=$(printf '%s' "$input" | jq -r '.prompt_id // empty' 2>/dev/null || true)
cwd=$(printf '%s' "$input" | jq -r '.cwd // empty' 2>/dev/null || true)
[ "$active" = "true" ] && exit 0
[ -z "$prompt_id" ] && exit 0
marker_dir="${TMPDIR:-/tmp}/claude-worklog-stop"
mkdir -p "$marker_dir" 2>/dev/null || exit 0
marker="$marker_dir/$prompt_id"
[ -e "$marker" ] && exit 0
: > "$marker"
# 顺手清掉一天前的标记,免得无限堆积
find "$marker_dir" -type f -mtime +1 -delete 2>/dev/null || true
cat >&2 <<EOF
worklog hook:这一轮结束前记一条 worklog。
有 asaptodo 的 worklog_create 工具就调它;没有就按
~/.claude/skills/worklog/SKILL.md 的流程走。
working_directory=$cwd
标题要具体(禁止「完成任务」),completed_at 用真实时间不伪造。
这一轮确实没有可记的实质性工作时,说明理由即可,不要凭空造条目 ——
日志是 append-only,写错了删不掉。
(这条拦截每个用户回合只出现一次,不会循环。)
EOF
exit 2