macos-documents-tcc-project-move-recipe.md
4.61 KB
macOS Documents TCC 阻断 Cron 时的项目迁移方案
何时使用
当 Hermes cron(launchd/systemd tick)访问 ~/Documents/... 中的项目目录报:
PermissionError: [Errno 1] Operation not permitted
shell-init: error retrieving current directory: getcwd: cannot access parent directories: Operation not permitted
且无法/不便通过 GUI 给 Python 二进制添加 Full Disk Access 时使用本方案。
核心思路
将项目整体移出受 TCC 保护的 Documents,在原地留下符号链接保持交互式入口,再把所有无人值守 cron job 的 workdir、prompt 和 checkpoint 更新到真实新路径。
执行步骤
1. 迁移目录
# 创建新父目录
mkdir -p /Users/$USER/Projects/lemonAI
# 迁移项目(保留 .git、未提交改动和 untracked 文件)
mv /Users/$USER/Documents/lemonAI/graphagent /Users/$USER/Projects/lemonAI/graphagent
# 在原地留下符号链接,保持交互式入口和旧书签/IDE 可用
ln -s /Users/$USER/Projects/lemonAI/graphagent /Users/$USER/Documents/lemonAI/graphagent
注意:不要把 cron 的 workdir 继续指向符号链接。后台进程经过 Documents 目录解析符号链接时仍可能触发 TCC。
2. 更新 cron job 的 workdir
qa profile:
hermes -p qa cron edit <planning-job-id> --workdir /Users/$USER/Projects/lemonAI/graphagent/doc
hermes -p qa cron edit <code-job-id> --workdir /Users/$USER/Projects/lemonAI/graphagent/source
若 job 分散在 code / planning profile:
hermes -p code cron edit <job-id> --workdir /Users/$USER/Projects/lemonAI/graphagent/source
hermes -p planning cron edit <job-id> --workdir /Users/$USER/Projects/lemonAI/graphagent/doc
3. 更新 prompt 中的旧路径
workdir 只能保证工具默认目录正确;如果 prompt 中硬编码了旧路径,agent 仍可能主动 cd 到旧路径。
批量替换各 profile cron/jobs.json 中的路径字符串:
import json
import glob
import os
old_root = f'/Users/{os.environ["USER"]}/Documents/lemonAI/graphagent'
new_root = f'/Users/{os.environ["USER"]}/Projects/lemonAI/graphagent'
for path in glob.glob(f'/Users/{os.environ["USER"]}/.hermes/profiles/*/cron/jobs.json'):
with open(path, 'r+', encoding='utf-8') as f:
text = f.read()
new_text = text.replace(old_root, new_root)
if new_text != text:
f.seek(0)
f.write(new_text)
f.truncate()
print(f'updated {path}')
4. 更新 checkpoint workdir
Hermes checkpoint 中缓存了会话 workdir。恢复到旧路径的 checkpoint 会让 agent 从 Documents 目录启动:
import json
import glob
import os
old_root = f'/Users/{os.environ["USER"]}/Documents/lemonAI/graphagent'
new_root = f'/Users/{os.environ["USER"]}/Projects/lemonAI/graphagent'
for path in glob.glob(f'/Users/{os.environ["USER"]}/.hermes/profiles/*/checkpoints/store/projects/*.json'):
with open(path, 'r+', encoding='utf-8') as f:
data = json.load(f)
if isinstance(data, dict) and isinstance(data.get('workdir'), str):
old = data['workdir']
new_wd = old.replace(old_root, new_root)
if new_wd != old:
data['workdir'] = new_wd
f.seek(0)
json.dump(data, f, ensure_ascii=False, indent=2)
f.truncate()
print(f'updated {path}: {old} -> {new_wd}')
5. 更新 profile AGENTS.md 中的共享入口
例如 /Users/$USER/.hermes/profiles/qa/AGENTS.md 中若有:
- 共享入口:`/Users/$USER/Documents/lemonAI/graphagent`
替换为新路径。
6. 验证
手动触发一次 cron job:
hermes -p <profile> cron run <job-id>
或 Hermes cron 工具:cronjob 的 action='run'。
检查输出中不再出现 Operation not permitted、getcwd 失败、PermissionError 等 TCC 相关错误。真实证据:
- agent log 中
read_file成功返回新路径下的文件内容; - terminal 工具能正常
cd、执行git status、跑测试; - 等待一个自然周期,确认
last_run_at推进、last_status=ok。
7. 检查被暂停的 cron job
大规模路径变更后,Hermes 可能自动把 job 置为 paused。检查并恢复:
hermes -p qa cron list
hermes -p qa cron resume <job-id>
不要做什么
- 不要把 workdir 继续指向符号链接并期望 TCC 放行;
- 不要只更新 workdir 而忽略 prompt 和 checkpoint;
- 不要把交互式终端的成功当作后台 cron 已通的证据;
- 不要删除旧目录里的 symlink(否则旧入口、IDE 配置、其他脚本会断)。