feat(hosts): add a cron bridging helper for macOS/Linux, wire claude_code - #595
feat(hosts): add a cron bridging helper for macOS/Linux, wire claude_code#595Shawn-Dong wants to merge 6 commits into
Conversation
…tes ~1KB lines The Unix registration steps in claude_code, cursor, hermes, and generic BRIDGING_TASK.md all instructed writing the full ~1.2KB pipeline prompt as a single-quoted inline crontab command. macOS/BSD cron truncates crontab lines around 1KB before handing them to /bin/sh, so every tick died instantly with "unexpected EOF while looking for matching quote" — mailed to /var/mail/$USER and visible nowhere else; the agent binary never started. Field-confirmed on two hosts' entries (claude-code, hermes) failing on every tick while the short script-invoking codex entry beside them kept working. Fix mirrors the Windows schtasks/TR solution (memU#539) already in the same docs: the prompt lives verbatim in ~/.memu/hosts/<host>/pipeline-prompt.txt, a small bridge.sh (with an atomic mkdir single-instance lock, so an hourly tick can't race a still-running backlog run) reads it and logs to bridging.log, and the crontab entry is one short line invoking the script. Verified in the field: the fixed entry's next tick executed cleanly and the lock correctly skipped a concurrent run. Also fixed in passing: hermes' inline example invoked `hermes -p`, but Hermes' one-shot flag is `-z`/`--oneshot` — the entry could never have worked even without truncation. UNINSTALL.md Part 1 now matches both the new bridge.sh entry and legacy inline entries; claude_code INSTALL.md's cron-entry detection grep likewise matches both layouts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The two tests locking the docs' pipeline prompt to bridging_pipeline_prompt() grepped for the inline `-p 'Run the memU` cron line, which the previous commit replaced with a prompt-file block. Anchor on the prompt line itself (the single-line fence written to pipeline-prompt.txt) instead — same verbatim-equality guarantee, new location. Also refresh prompt.py's module docstring, which still described the Unix mechanism as pasting the prompt into the crontab. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ridge script cursor-agent refuses headless runs in an untrusted directory (Workspace Trust Required, exit 1) — field-verified in memU#571, which flagged the Unix cron path as still missing the flag. The wrapper now cds into ~/.memu/hosts/cursor and invokes cursor-agent --trust, so trust lands on memU's own tree rather than wherever cron starts, mirroring the wired Windows template. Never --yolo. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…try length, document lock tradeoff, make hermes native-first Four review points, all pre-merge cleanups: - Filenames converge with the shipped Windows helper: bridge-prompt.txt (was pipeline-prompt.txt) and bridge.log (was bridging.log), so the planned Unix schedule helper and every cross-platform doc carry one naming scheme instead of two forever. bridge.sh keeps its name — the extension already distinguishes it from memu-bridge.ps1. - New regression gate: a parameterized test over the four Unix guides asserts every cron entry line stays under 512 chars. The prompt-lock tests catch content drift but not re-inlining — this catches the actual bug class (memU#591) at test time instead of on the next Mac. Generic's entry moved into a fenced block so the gate sees it. - The 3h lock-reclaim tradeoff is now spelled out in the script comment: a legitimate >3h run loses its lock to the next tick and can double-run, accepted deliberately over a crashed run wedging the schedule forever. - Hermes: native cronjob is now the recommended path in so many words; raw crontab is explicitly fallback-only, with the history note that no install is known to have ever succeeded through it (the nonexistent -p flag survived in the guide precisely because nobody ran that path). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…code The Unix half of the schedule helper (NevaMind-AI#546 shipped the Windows half): `memu-claude-code schedule {install,verify,status,uninstall}` now works on macOS/Linux against cron. install writes bridge-prompt.txt + a locked bridge.sh (PATH baked in, cd into the host tree) and registers one short crontab entry — replacing, never duplicating, any previous entry for the host, including a legacy inlined one (memU#591), which install thereby migrates. verify proves the entry can run without waiting for a tick; uninstall removes both layouts and keeps the run log. The scheduling package fronts both backends behind one verb surface, picked by platform. The auth/resolution gates are imported from windows.py — they were always platform-neutral. claude_code's guide now leads with the helper and keeps the manual registration as the fallback. Real-machine check: `schedule status` against a live install correctly reports the crontab entry and a held lock mid-run. 237 tests, mypy and ruff clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
claude code 和 cursor 可以直接这么应用。 但是 hermes 和 generic 不能简单直接推广,特别是 generic。 windows 的 scheduled task 只直接接了 cursor 和 claude code。 |
|
补充一下 @MrXnneHang 上面三点的技术依据,方便对照代码。 为什么 claude_code 和 cursor 能直接接、另外两个不能 —— 这条界线在代码里就是 hermes 有两个卡点:
generic 更难,这也是「特别是 generic」的原因: 顺带一提,generic 的 cron 不是 fallback 而是主路径( 另外有一个和这个 PR 直接相关的点:cursor 在 Unix 上其实已经被打开了,但文档没跟上。 PR 描述写的是 "claude_code only,cursor fan-out is the follow-up",实际不是:
也就是说合并后 |
Stacked on #594 (merge that first — this branch contains its commits).
What
The Unix half of the
schedulehelper. #546 gave Windowsmemu-claude-code schedule {install,verify,status,uninstall}; this PR makes the same four verbs work on macOS/Linux against cron, so registration becomes one deterministic command instead of agent-followed docs.install— writesbridge-prompt.txt+ a lockedbridge.sh(PATH baked in,cdinto the host tree) and registers one short crontab entry. Replaces, never duplicates, any previous entry for the host — including a legacy inlined one ([Bug] macOS cron 会把 bridging 指南里 1320 字符的整行命令截断——record seam 在 macOS 上一次都跑不起来 #591), which install thereby migrates.verify— proves the entry can actually run (files sane, prompt matches the canon, agent resolves and authenticates headless) without waiting for the next tick.status/uninstall— inspect and remove by the script path; uninstall handles both layouts and keeps the run log.One backend per platform behind the same verb surface; the auth/resolution gates are shared from
windows.py(they were always platform-neutral). claude_code only, mirroring #546 — cursor fan-out is the follow-up, like #571.Field evidence
The macOS error stack this automates away (#591, hourly, before the fix):
Real-machine check on the same Mac:
schedule statusagainst a live install correctly reports the crontab entry and a held lock mid-run. 237 tests, mypy + ruff clean.依赖 #594(请先合并;本分支包含其提交)。
做了什么
schedulehelper 的 Unix 半边。#546 给了 Windows 四个动词;本 PR 让同样的schedule {install,verify,status,uninstall}在 macOS/Linux 上对 cron 生效——注册从"agent 照文档手抄"变成一条确定命令。install— 写bridge-prompt.txt+ 带锁的bridge.sh(PATH 内嵌、cd进宿主工作树),注册一行短 crontab 条目。对本宿主的旧条目只替换不重复——包括旧式内联条目([Bug] macOS cron 会把 bridging 指南里 1320 字符的整行命令截断——record seam 在 macOS 上一次都跑不起来 #591),即自动迁移存量坏安装。verify— 不等整点即验证任务真能跑(文件完好、prompt 与规范一致、agent 可解析且无头认证通过)。status/uninstall— 按脚本路径检查/移除;卸载兼容新旧两种布局,保留运行日志。同一动词表面、按平台选后端;认证/解析闸门从
windows.py复用(本就是平台无关的)。只接线 claude_code,与 #546 同构——cursor 扇出是后续 PR,如 #571。实测依据
#591 的 macOS 错误栈(修复前每个整点):
同一台 Mac 真机检查:
schedule status对存量安装正确报告 crontab 条目与运行中的锁。237 个测试,mypy + ruff 干净。🤖 Generated with Claude Code