sync-now 알고리즘
🚧 설계 단계 — 아직 구현 안 됨
이 페이지는 ainote 의 향후 기능을 미리 문서화한 것입니다. 현재 @ainote/mcp v1.1.x 에는 vault / sync 도구가 포함돼 있지 않습니다. 도구 호출 시 Tool not found 에러를 받게 됩니다.
~/.claude/ainote-sync/tools/sync-now.sh 의 의사코드.
한 사이클
python
def sync_now(dry_run=False):
manifest = load("manifest.yml")
state = load("state.json")
log = open("log/{YYYY-MM}.jsonl", "a")
# 1. 로컬 스캔
local_files = expand(manifest.sync) # glob 풀어서 (source, target) pair 리스트
# 2. 원격 list
remote_files = mcp_call("sync_list", {}) # hub 에서 manifest 매칭 파일들
# 3. 양쪽 sha256 + mtime + hlc 수집
pairs = match_pairs(local_files, remote_files)
# 4. 각 페어별 케이스 결정
actions = []
for p in pairs:
case = decide_case(p, threshold_minutes=5)
actions.append((p, case))
# 5. Safety guard
delete_count = sum(1 for _, c in actions if c == "delete")
if delete_count / len(actions) > manifest.safety.max_delete_pct / 100:
abort("max_delete_pct exceeded")
# 6. dry-run 모드
if dry_run:
print_actions(actions)
return
# 7. 실행
for p, case in actions:
match case:
case "no-op": pass
case "push": push_to_hub(p)
case "pull": pull_from_hub(p)
case "conflict_save_both":
save_conflict_files(p)
ABORT_PROMPT(f"수동 해결: {p.target}")
case "lww_remote_wins":
pull_from_hub(p)
case "lww_local_wins":
push_to_hub(p)
case "delete_local":
delete_local(p)
case "delete_remote":
delete_remote(p)
log.write(json.dumps({
"ts": now_iso(),
"device": DEVICE_ID,
"path": p.target,
"action": case,
"local_sha": p.local_sha,
"remote_sha": p.remote_sha,
"hlc": p.new_hlc,
}) + "\n")
update_state(state, p, case)
save(state)decide_case
python
def decide_case(pair, threshold_minutes):
local_changed = (pair.local_sha != state[pair.target].local_sha)
remote_changed = (pair.remote_hlc > state[pair.target].hlc)
if not local_changed and not remote_changed:
return "no-op"
if local_changed and not remote_changed:
if not pair.local_exists:
return "delete_remote"
return "push"
if not local_changed and remote_changed:
if not pair.remote_exists:
return "delete_local"
return "pull"
# 양쪽 변경
time_diff = abs(pair.local_mtime - pair.remote_mtime)
if time_diff <= timedelta(minutes=threshold_minutes):
return "conflict_save_both"
if pair.local_hlc > pair.remote_hlc:
return "lww_local_wins"
return "lww_remote_wins"sub-procedures
push_to_hub
python
def push_to_hub(p):
new_hlc = compute_hlc(now(), state[p.target].hlc, DEVICE_ID)
mcp_call("sync_push", {
"path": p.target,
"content": read_file(p.source),
"sha256": p.local_sha,
"hlc": new_hlc,
"device_id": DEVICE_ID,
})
p.new_hlc = new_hlcpull_from_hub
python
def pull_from_hub(p):
res = mcp_call("sync_pull", {"path": p.target})
write_file(p.source, res.content)
set_mtime(p.source, res.remote_mtime)
p.new_hlc = res.hlcsave_conflict_files
python
def save_conflict_files(p):
ts = now().strftime("%Y-%m-%dT%H-%M-%S")
safe_path = p.target.replace("/", "__")
out = f"~/.claude/ainote-sync/conflicts/{ts}__{safe_path}.diff"
write(out, format_diff(
local=read_file(p.source),
local_hlc=p.local_hlc,
remote=mcp_call("sync_pull", {"path": p.target}).content,
remote_hlc=p.remote_hlc,
))트리거
수동
bash
~/.claude/ainote-sync/tools/sync-now.sh
~/.claude/ainote-sync/tools/sync-now.sh --dry-runCron (15분마다)
bash
*/15 * * * * /Users/seunghan/.claude/ainote-sync/tools/sync-now.sh >> /tmp/ainote-sync.log 2>&1launchd (macOS, 더 안정적)
vault_sync 와 동일 패턴 — .plist 에 StartInterval: 900.
File watcher (실시간)
bash
fswatch ~/CLAUDE.md ~/.claude/projects/ | xargs -I{} sync-now.shaudit (주간)
bash
~/.claude/ainote-sync/tools/audit.sh체크:
- manifest 의 source 가 모두 존재
- state.json 과 hub 의 file 목록 차이
- conflicts/ 의 미해결 파일
- 최근 7일 변경 통계 (디바이스별)