{"id":"781ee4ec-028f-4c4e-be70-c69874e1b1a3","entityType":"agent","slug":"clawhub-jiangsier-xyz-sync-notes","name":"Sync Notes","canonicalUrl":"https://www.xpersona.co/agent/clawhub-jiangsier-xyz-sync-notes","canonicalPath":"/agent/clawhub-jiangsier-xyz-sync-notes","generatedAt":"2026-10-14T06:39:03.535Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-12T00:24:42.787Z","emptyReason":null},"description":"Sync the local Obsidian vault with a Cloudflare R2 bucket using rclone (S3 + crypt). Triggered by `/sync-notes`. Defaults to bidirectional sync; understands... Skill: Sync Notes Owner: jiangsier-xyz Summary: Sync the local Obsidian vault with a Cloudflare R2 bucket using rclone (S3 + crypt). Triggered by /sync-notes. Defaults to bidirectional sync; understands... Tags: latest:1.0.1 Version history: v1.0.1 | 2026-05-23T02:11:38.242Z | auto - Removed LICENSE file for streamlined distribution. - Updated setup and sync scripts (setup.sh, sync.sh)—maintenance, internal improveme","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 968 downloads reported by the source. Last updated 10/12/2026.","installCommand":"clawhub skill install s173nsfjwe1yqehavmtjgmxnjs86xwmn:sync-notes","sourceUrl":"https://clawhub.ai/jiangsier-xyz/sync-notes","homepage":"https://clawhub.ai/jiangsier-xyz/skills/sync-notes","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/jiangsier-xyz/sync-notes","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/jiangsier-xyz/skills/sync-notes","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":60,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Sync the local Obsidian vault with a Cloudflare R2 bucket using rclone (S3 + crypt). Triggered by `/sync-notes`. Defaults to bidirectional sync; understands... "},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-12T00:24:42.787Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-12T00:24:42.787Z","emptyReason":null},"stars":null,"forks":null,"downloads":968,"likes":null,"task":null,"library":null,"packageName":null,"latestVersion":"1.0.1","tractionLabel":"968 downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-12T00:24:42.724Z","emptyReason":null},"lastUpdatedAt":"2026-10-12T00:24:42.787Z","lastCrawledAt":"2026-10-12T00:24:42.724Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-13T00:24:42.724Z","lastVerifiedAt":null,"highlights":[{"version":"1.0.1","createdAt":"2026-05-23T02:11:38.242Z","changelog":"- Removed LICENSE file for streamlined distribution. - Updated setup and sync scripts (`setup.sh`, `sync.sh`)—maintenance, internal improvements, or bug fixes. - SKILL.md updated for accuracy and alignment with latest implementation. - No changes to user-facing behavior or commands.","fileCount":8,"zipByteSize":17046},{"version":"1.0.0","createdAt":"2026-05-17T09:53:22.442Z","changelog":"Initial release of sync-notes skill: securely sync your Obsidian vault with Cloudflare R2 via rclone. - Triggered by `/sync-notes`, with support for setup, init, status, upload/download (via glob), and bidirectional sync. - Natural language commands (English/Chinese) are parsed for intuitive uploads and downloads. - Built-in safeguards: encrypted storage, pre-sync backups, explicit conflict handling. - Logs every sync; maintains single rolling backup. - Requires `rclone`, `bash`, and the `CLOUD_NOTES_PATH` environment variable. - Interactive setup wizard guides through first-time configuration.","fileCount":8,"zipByteSize":16502}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s173nsfjwe1yqehavmtjgmxnjs86xwmn:sync-notes","setupComplexity":"low","setupSteps":["Setup complexity is LOW. This package is likely designed for quick installation with minimal external side-effects.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-jiangsier-xyz-sync-notes/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-jiangsier-xyz-sync-notes/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-jiangsier-xyz-sync-notes/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-jiangsier-xyz-sync-notes/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-jiangsier-xyz-sync-notes/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-jiangsier-xyz-sync-notes/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-14T06:39:03.535Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-jiangsier-xyz-sync-notes/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-jiangsier-xyz-sync-notes/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-jiangsier-xyz-sync-notes/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-jiangsier-xyz-sync-notes/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-12T00:24:42.787Z","emptyReason":null},"readme":"Skill: Sync Notes\n\nOwner: jiangsier-xyz\n\nSummary: Sync the local Obsidian vault with a Cloudflare R2 bucket using rclone (S3 + crypt). Triggered by `/sync-notes`. Defaults to bidirectional sync; understands...\n\nTags: latest:1.0.1\n\nVersion history:\n\nv1.0.1 | 2026-05-23T02:11:38.242Z | auto\n\n- Removed LICENSE file for streamlined distribution.\n- Updated setup and sync scripts (`setup.sh`, `sync.sh`)—maintenance, internal improvements, or bug fixes.\n- SKILL.md updated for accuracy and alignment with latest implementation.\n- No changes to user-facing behavior or commands.\n\nv1.0.0 | 2026-05-17T09:53:22.442Z | auto\n\nInitial release of sync-notes skill: securely sync your Obsidian vault with Cloudflare R2 via rclone.\n\n- Triggered by `/sync-notes`, with support for setup, init, status, upload/download (via glob), and bidirectional sync.\n- Natural language commands (English/Chinese) are parsed for intuitive uploads and downloads.\n- Built-in safeguards: encrypted storage, pre-sync backups, explicit conflict handling.\n- Logs every sync; maintains single rolling backup.\n- Requires `rclone`, `bash`, and the `CLOUD_NOTES_PATH` environment variable.\n- Interactive setup wizard guides through first-time configuration.\n\nArchive index:\n\nArchive v1.0.1: 8 files, 17046 bytes\n\nFiles: config/filter.txt (417b), README.md (7148b), README.zh-CN.md (6792b), scripts/setup.sh (3449b), scripts/sync.sh (7879b), skill-card.md (2709b), SKILL.md (7450b), _meta.json (129b)\n\nFile v1.0.1:SKILL.md\n\n---\nname: sync-notes\ndescription: Sync the local Obsidian vault with a Cloudflare R2 bucket using rclone (S3 + crypt). Triggered by `/sync-notes`. Defaults to bidirectional sync; understands natural-language flags for partial download/upload by glob.\nversion: 1.0.1\nmetadata:\n  {\"openclaw\":{\"emoji\":\"☁️\",\"requires\":{\"bins\":[\"rclone\",\"bash\"],\"env\":[\"CLOUD_NOTES_PATH\"]}}}\n---\n\n# sync-notes Skill\n\nSync the local notes directory (`$CLOUD_NOTES_PATH`) with a **Cloudflare R2** bucket through an\nend-to-end encrypted **rclone crypt** remote. The skill is a thin orchestrator over a single bash\nentry point at `scripts/sync.sh`; the LLM's job is to translate the user's natural-language intent\ninto the right subcommand.\n\n## Trigger\n\nActivate **only** when the user message starts with `/sync-notes` (with or without trailing\narguments). Examples:\n\n| User message | Intent | Action |\n|---|---|---|\n| `/sync-notes` | full bidirectional sync | `sync.sh bisync` |\n| `/sync-notes setup` | run the configuration wizard | `sync.sh setup` |\n| `/sync-notes init` | establish the bisync baseline (first run only) | `sync.sh init` |\n| `/sync-notes status` | show config + last-sync info | `sync.sh status` |\n| `/sync-notes 下载 welcome` | download files matching `welcome` | `sync.sh download '**welcome**'` |\n| `/sync-notes upload daily/*.md` | upload glob | `sync.sh upload 'daily/*.md'` |\n| `/sync-notes 把 ideas 目录拉下来` | download a directory | `sync.sh download 'ideas/**'` |\n\n## How to interpret natural language\n\nAfter stripping the `/sync-notes` prefix, classify the remainder:\n\n1. **Empty** → run `bisync` (default bidirectional sync).\n2. Contains a recognised keyword:\n   - `setup`, `配置`, `wizard` → `setup`\n   - `init`, `初始化`, `baseline` → `init`\n   - `status`, `状态`, `信息` → `status`\n3. Mentions **download** intent (`下载`, `拉`, `pull`, `download`, `get`, `取回`):\n   - Extract the target token(s) and convert to a glob (see \"Glob rules\" below).\n   - Run `sync.sh download '<glob>'`.\n4. Mentions **upload** intent (`上传`, `推`, `push`, `upload`, `send`):\n   - Same as above with `sync.sh upload '<glob>'`.\n5. Otherwise: ask the user to clarify; do not guess.\n\n### Glob rules\n\nGlobs are passed verbatim to rclone as `--include` filters and apply to *plain* (decrypted) paths.\n\n- A bare keyword such as `welcome` becomes `**welcome**` (matches anywhere in path).\n- A directory name like `ideas` becomes `ideas/**`.\n- Paths already containing wildcards (`*`, `?`, `[`) are passed through unchanged.\n- Filenames without extension default to matching `<name>` and `<name>.md`; combine with `**` if\n  unsure. When in doubt, run `sync.sh download '<glob>' --dry-run` first.\n\n## Workflow contract\n\n1. **First run ever:** the script refuses `bisync` until a baseline exists. Tell the user to run\n   `/sync-notes init` after confirming both sides are in the desired starting state.\n2. **Backups:** before any write-side operation (`bisync`, `download`, `init`), the local notes\n   directory is mirrored to `backups/latest/` (single rolling copy, previous one is replaced).\n3. **Conflicts:** bisync runs with conflict markers. After every run the script scans for\n   `*.conflict*` files; if any are found the script exits non-zero and lists them. **Surface this\n   list to the user verbatim and ask which side to keep**—do not auto-resolve.\n4. **Logs:** every invocation appends to `logs/sync.log` and emits a per-run file\n   `logs/<UTC-timestamp>.log` for the rclone output.\n\n## Filesystem layout\n\n```\nskills/sync-notes/\n├── SKILL.md                  # this file\n├── README.md                 # short user-facing notes (optional)\n├── config/\n│   ├── rclone.conf           # active rclone config (created by setup, gitignored)\n│   ├── rclone.conf.example   # template\n│   ├── .env                  # non-rclone settings (created by setup, gitignored)\n│   └── .env.example          # template\n├── scripts/\n│   ├── sync.sh               # main entry point (dispatcher)\n│   └── setup.sh              # interactive configuration wizard\n├── state/\n│   └── bisync/               # rclone bisync workdir (listings + lockfiles)\n├── logs/                     # rolling logs\n├── backups/\n│   └── latest/               # most recent local snapshot (single copy)\n└── .gitignore\n```\n\n`scripts/sync.sh` derives every path from its own location (`$(dirname \"$0\")/..`); no absolute\npaths are hard-coded.\n\n## Required environment\n\n| Variable | Purpose | Required |\n|---|---|---|\n| `CLOUD_NOTES_PATH` | absolute path of the local notes directory | ✅ |\n\nThe script aborts early with a clear message if `CLOUD_NOTES_PATH` is unset, missing, or not a\ndirectory.\n\n## Configuration files\n\n### `config/rclone.conf`\n\nNative rclone format with **two remotes**:\n\n```ini\n[r2-raw]\ntype = s3\nprovider = Cloudflare\naccess_key_id = <ACCESS_KEY_ID>\nsecret_access_key = <SECRET_ACCESS_KEY>\nendpoint = https://<ACCOUNT_ID>.r2.cloudflarestorage.com\nacl = private\n\n[notes]\ntype = crypt\nremote = r2-raw:<BUCKET>/<OPTIONAL_PREFIX>\nfilename_encryption = standard\ndirectory_name_encryption = true\npassword = <OBSCURED>\npassword2 = <OBSCURED>\n```\n\n### `config/.env`\n\nNon-rclone settings consumed by `sync.sh`:\n\n```sh\n# Name of the crypt remote in rclone.conf\nRCLONE_REMOTE=notes\n# Number of local backup snapshots to retain\nBACKUP_KEEP=1\n# Extra rclone flags (optional)\nRCLONE_EXTRA_FLAGS=\"--transfers=4 --checkers=8\"\n```\n\n`.example` versions ship with the skill; the wizard fills in the real values.\n\n## Subcommands implemented by `sync.sh`\n\n| Subcommand | Behaviour |\n|---|---|\n| `setup` | Run `setup.sh`; collects R2 creds + crypt password(s); writes `rclone.conf` and `.env` with `chmod 600`. Idempotent: confirms before overwriting. |\n| `init` | First-time baseline: `rclone bisync --resync`. Refuses to run if a baseline already exists unless `--force` is passed. |\n| `bisync` (default) | `rclone bisync $LOCAL notes:` with conflict reporting. Backs up first. |\n| `download <glob>` | `rclone copy notes: $LOCAL --include '<glob>'`. Backs up first. Defaults to `--dry-run` only when the user said \"试\" / \"dry\"; otherwise runs for real. |\n| `upload <glob>` | `rclone copy $LOCAL notes: --include '<glob>'`. No local backup needed; remote is authoritative. |\n| `status` | Prints config validity, last-sync timestamp, conflict files (if any), backup state. |\n\n## Safety rules for the LLM\n\n- **Never** write secrets into chat. If the user pastes secrets, immediately move them into\n  `config/rclone.conf` / `config/.env` and remove from any visible context.\n- **Never** invoke `rclone` with `--config` pointing somewhere outside this skill.\n- **Never** run `bisync` if `status` reports unresolved conflicts; ask first.\n- When `download`/`upload` glob match count is ambiguous, run with `--dry-run` first and show the\n  list of matches.\n- Backups are single-copy; if you are about to do something risky (large delete, schema change),\n  copy `backups/latest/` to `backups/manual-<UTC>/` manually before proceeding and tell the user.\n\n## First-time onboarding (what to tell the user)\n\n```\n1. /sync-notes setup           # fill in R2 + crypt creds\n2. export CLOUD_NOTES_PATH=...  # point at the local notes dir\n3. /sync-notes init             # establish the bisync baseline\n4. /sync-notes                  # routine bidirectional sync\n```\n\nFile v1.0.1:README.md\n\n# sync-notes\n\n> End-to-end encrypted, bidirectional sync between a local notes vault (Obsidian, plain Markdown, anything really) and a **Cloudflare R2** bucket — powered by [`rclone`](https://rclone.org/) `bisync` over an `rclone crypt` remote.\n\nThis is an [OpenClaw](https://openclaw.ai) skill, but the heavy lifting is just a pair of bash scripts. You can use them outside of OpenClaw without modification.\n\n[简体中文](./README.zh-CN.md)\n\n---\n\n## Why?\n\n- 🔒 **Zero-trust storage.** Your notes are encrypted client-side via `rclone crypt`. Cloudflare only ever sees ciphertext blobs with encrypted filenames.\n- 🔁 **Real bidirectional sync.** `rclone bisync` propagates additions, modifications and deletions in both directions, with conflict markers when both sides changed.\n- 📱 **Mobile-friendly.** The crypt format is compatible with the [Remotely Save](https://github.com/remotely-save/remotely-save) Obsidian plugin, so your phone can sync the same bucket without holding the rclone config.\n- 🧰 **Self-contained.** All paths derive from the script location. No daemons, no extra services — just `bash` + `rclone`.\n- 🛟 **Backups before writes.** Every write-side run mirrors the local vault to `backups/latest/` first.\n\n---\n\n## Layout\n\n```\nsync-notes/\n├── SKILL.md                      # OpenClaw skill manifest (also the design doc)\n├── README.md / README.zh-CN.md   # this file\n├── config/\n│   ├── rclone.conf.example       # template for the rclone remotes\n│   ├── rclone.conf               # your real config (gitignored, chmod 600)\n│   ├── .env.example              # template for non-rclone settings\n│   ├── .env                      # your real settings  (gitignored, chmod 600)\n│   └── filter.txt                # rclone filter rules (excludes .obsidian/, .DS_Store, etc.)\n├── scripts/\n│   ├── setup.sh                  # interactive configuration wizard\n│   └── sync.sh                   # main entry point / dispatcher\n├── state/                        # rclone bisync workdir (gitignored)\n├── logs/                         # per-run + master logs   (gitignored)\n└── backups/                      # rolling local snapshots (gitignored)\n```\n\n---\n\n## Requirements\n\n- `bash` 4+\n- [`rclone`](https://rclone.org/install/) **v1.65+** (older builds lack some `bisync` flags used here)\n- A [Cloudflare R2](https://developers.cloudflare.com/r2/) bucket and an S3-compatible API token scoped to it\n- A passphrase (and optional salt) for the `crypt` remote — **lose this and the data is unrecoverable**\n\n---\n\n## Quick start\n\n```bash\n# 1. Clone the repo somewhere\ngit clone https://github.com/jiangsier-xyz/jiangsier-skill-sync-notes.git\ncd jiangsier-skill-sync-notes\n\n# 2. Tell the script where your notes live\nexport CLOUD_NOTES_PATH=/absolute/path/to/your/vault\n\n# 3. Run the wizard — fills in rclone.conf and .env (chmod 600)\n./scripts/setup.sh\n\n# 4. Sanity check\n./scripts/sync.sh status\n\n# 5. First-time baseline (must do this once before bisync will run)\n./scripts/sync.sh init\n\n# 6. Routine bidirectional sync from now on\n./scripts/sync.sh\n```\n\n> 💡 **Putting `CLOUD_NOTES_PATH` in your shell rc (`.zshrc` / `.bashrc`) is the most painless option.**\n\n---\n\n## Subcommands\n\n| Command | Behaviour |\n|---|---|\n| `sync.sh setup` | Run the wizard. Idempotent — confirms before overwriting existing config. |\n| `sync.sh init [--force]` | First-run baseline (`rclone bisync --resync`). Refuses to run if a baseline already exists. |\n| `sync.sh bisync` (default) | Bidirectional sync with conflict reporting. Backs up locally first. |\n| `sync.sh download <glob> [--dry-run]` | Pull files matching `<glob>` from the remote into the local vault. Backs up first. |\n| `sync.sh upload <glob> [--dry-run]` | Push files matching `<glob>` to the remote. |\n| `sync.sh status` | Show config validity, last-sync info, conflicts, backup state. |\n\n`<glob>` is passed to `rclone --include` and applies to **plaintext** paths (after decryption). A bare keyword like `welcome` is broadened to `**welcome**` automatically; pre-wildcarded globs (`ideas/**`, `*.md`) pass through unchanged.\n\n---\n\n## Conflict handling\n\n`bisync` runs with `--conflict-suffix conflict1,conflict2`. After every run, the script scans the vault for `*.conflict1*` / `*.conflict2*` files. **If any are found, the script exits non-zero and prints the list — the LLM/human is expected to resolve them manually before the next sync.** No silent automatic merging.\n\n---\n\n## Configuration files\n\n### `config/rclone.conf`\n\nTwo remotes:\n\n```ini\n[r2-raw]\ntype = s3\nprovider = Cloudflare\naccess_key_id = <ACCESS_KEY_ID>\nsecret_access_key = <SECRET_ACCESS_KEY>\nendpoint = https://<ACCOUNT_ID>.r2.cloudflarestorage.com\nacl = private\n\n[notes]\ntype = crypt\nremote = r2-raw:<BUCKET>/<OPTIONAL_PREFIX>\nfilename_encryption = standard\nfilename_encoding = base64        # match Remotely Save (rclone default is base32)\ndirectory_name_encryption = true\npassword = <OBSCURED via `rclone obscure`>\npassword2 = <OBSCURED via `rclone obscure`>   # optional\n```\n\n### `config/.env`\n\n```sh\nRCLONE_REMOTE=notes                       # crypt remote name\nBACKUP_KEEP=1                             # rolling local backup count\nRCLONE_EXTRA_FLAGS=\"--transfers=4 --checkers=8\"\n```\n\n### `config/filter.txt`\n\n`rclone` filter rules. The shipped defaults exclude `.obsidian/`, `.DS_Store`, `Thumbs.db`, swap files and Office lock files — adjust to taste.\n\n---\n\n## Required environment\n\n| Variable | Purpose | Required |\n|---|---|---|\n| `CLOUD_NOTES_PATH` | Absolute path of the local notes directory | ✅ |\n\nThe script aborts immediately with a clear message if `CLOUD_NOTES_PATH` is unset, missing, or not a directory.\n\n---\n\n## Remotely Save interop\n\nThe `crypt` settings above are deliberately tuned to match Remotely Save's defaults:\n\n- `filename_encryption = standard`\n- `filename_encoding = base64`\n- `directory_name_encryption = true`\n- Same passphrase (and salt, if you set one)\n\nWith those aligned, your phone (Remotely Save) and your desktop (this skill) can safely sync to the same R2 bucket.\n\n---\n\n## OpenClaw integration\n\nWhen dropped into an OpenClaw workspace, this skill is triggered by:\n\n```\n/sync-notes              → bisync\n/sync-notes setup        → wizard\n/sync-notes init         → baseline\n/sync-notes status       → status\n/sync-notes 下载 welcome  → download '**welcome**'\n/sync-notes upload daily/*.md\n```\n\nSee [`SKILL.md`](./SKILL.md) for the full natural-language → subcommand mapping that the LLM is expected to follow.\n\n---\n\n## Safety notes\n\n- **Never commit `config/rclone.conf` or `config/.env`** — they're in `.gitignore`, but double-check before pushing.\n- **Never commit `backups/`** — it contains a verbatim copy of your vault. Also gitignored.\n- The `crypt` passphrase is the only thing standing between Cloudflare and your plaintext notes. Back it up offline.\n- `bisync` is powerful and can delete on both sides. Use `--dry-run` and `status` liberally on the first few runs.\n\n---\n\n## License\n\nMIT. See [`LICENSE`](./LICENSE) if present.\n\nFile v1.0.1:_meta.json\n\n{\n  \"ownerId\": \"kn73sdyhq2p2h53qne8t5nvacs86x2bc\",\n  \"slug\": \"sync-notes\",\n  \"version\": \"1.0.1\",\n  \"publishedAt\": 1779502298242\n}\n\nFile v1.0.1:README.zh-CN.md\n\n# sync-notes\n\n> 在本地笔记目录(Obsidian / 纯 Markdown / 任意文件夹)与 **Cloudflare R2** 之间做端到端加密的双向同步,底层用 [`rclone`](https://rclone.org/) 的 `bisync` + `crypt` 远端实现。\n\n这是一个 [OpenClaw](https://openclaw.ai) 技能(skill),但核心其实是两个 bash 脚本,可以脱离 OpenClaw 独立使用。\n\n[English](./README.md)\n\n---\n\n## 为什么用它?\n\n- 🔒 **零信任存储**: 笔记在客户端用 `rclone crypt` 加密,Cloudflare 只看得到密文 blob 和加密过的文件名。\n- 🔁 **真双向同步**: `rclone bisync` 会双向传播新增、修改、删除,两边都改了的话会留下冲突标记,不会静默覆盖。\n- 📱 **手机可用**: crypt 格式与 Obsidian 插件 [Remotely Save](https://github.com/remotely-save/remotely-save) 兼容,手机端不用塞 rclone 配置就能同步同一个 bucket。\n- 🧰 **零依赖、零守护进程**: 所有路径都从脚本位置推导,只需要 `bash` + `rclone`。\n- 🛟 **写入前先备份**: 任何写本地的命令都会先把当前 vault 镜像到 `backups/latest/`。\n\n---\n\n## 目录结构\n\n```\nsync-notes/\n├── SKILL.md                      # OpenClaw 技能清单 + 设计文档\n├── README.md / README.zh-CN.md   # 本文件\n├── config/\n│   ├── rclone.conf.example       # rclone 远端模板\n│   ├── rclone.conf               # 真实配置 (gitignored, chmod 600)\n│   ├── .env.example              # 非 rclone 设置模板\n│   ├── .env                      # 真实设置   (gitignored, chmod 600)\n│   └── filter.txt                # rclone 过滤规则(默认排除 .obsidian/、.DS_Store 等)\n├── scripts/\n│   ├── setup.sh                  # 交互式配置向导\n│   └── sync.sh                   # 主入口 / 调度器\n├── state/                        # rclone bisync 工作目录 (gitignored)\n├── logs/                         # 每次运行和总日志        (gitignored)\n└── backups/                      # 滚动本地快照            (gitignored)\n```\n\n---\n\n## 依赖\n\n- `bash` 4+\n- [`rclone`](https://rclone.org/install/) **v1.65+**(更早版本不支持脚本里用到的部分 `bisync` 选项)\n- 一个 [Cloudflare R2](https://developers.cloudflare.com/r2/) bucket 以及一个限定权限的 S3 兼容 token\n- crypt 远端的密码(以及可选的 salt)—— **丢了就不可恢复**,务必离线备份一份\n\n---\n\n## 快速开始\n\n```bash\n# 1. 克隆仓库\ngit clone https://github.com/jiangsier-xyz/jiangsier-skill-sync-notes.git\ncd jiangsier-skill-sync-notes\n\n# 2. 告诉脚本你的笔记目录在哪里\nexport CLOUD_NOTES_PATH=/absolute/path/to/your/vault\n\n# 3. 跑配置向导,自动写好 rclone.conf 和 .env(权限 600)\n./scripts/setup.sh\n\n# 4. 健康检查\n./scripts/sync.sh status\n\n# 5. 第一次必须先建立基线\n./scripts/sync.sh init\n\n# 6. 之后日常双向同步\n./scripts/sync.sh\n```\n\n> 💡 **建议把 `CLOUD_NOTES_PATH` 写进你的 shell rc(`.zshrc` / `.bashrc`),省心。**\n\n---\n\n## 子命令\n\n| 命令 | 行为 |\n|---|---|\n| `sync.sh setup` | 跑配置向导。幂等,覆盖前会问一次。 |\n| `sync.sh init [--force]` | 首次建立 bisync 基线(`rclone bisync --resync`)。已有基线时拒绝运行,除非加 `--force`。 |\n| `sync.sh bisync`(默认) | 双向同步,带冲突检测;运行前先做本地备份。 |\n| `sync.sh download <glob> [--dry-run]` | 从远端把匹配 `<glob>` 的文件拉下来;运行前先做本地备份。 |\n| `sync.sh upload <glob> [--dry-run]` | 把本地匹配 `<glob>` 的文件推上远端。 |\n| `sync.sh status` | 打印配置状态、上次同步信息、冲突文件、备份状态。 |\n\n`<glob>` 会传给 `rclone --include`,作用在**解密后的明文路径**上。光秃秃的关键词(如 `welcome`)会自动展开成 `**welcome**`;已经带通配符的(`ideas/**`、`*.md`)原样透传。\n\n---\n\n## 冲突处理\n\n`bisync` 运行时使用 `--conflict-suffix conflict1,conflict2`。每次运行结束脚本会扫描 vault,如果发现 `*.conflict1*` / `*.conflict2*` 文件,**会以非零退出码报错并列出冲突清单 —— 由人(或上层 LLM)手动决定保留哪一边再继续同步**,脚本不会静默自动合并。\n\n---\n\n## 配置文件\n\n### `config/rclone.conf`\n\n两个远端:\n\n```ini\n[r2-raw]\ntype = s3\nprovider = Cloudflare\naccess_key_id = <ACCESS_KEY_ID>\nsecret_access_key = <SECRET_ACCESS_KEY>\nendpoint = https://<ACCOUNT_ID>.r2.cloudflarestorage.com\nacl = private\n\n[notes]\ntype = crypt\nremote = r2-raw:<BUCKET>/<OPTIONAL_PREFIX>\nfilename_encryption = standard\nfilename_encoding = base64        # 与 Remotely Save 对齐(rclone 默认是 base32)\ndirectory_name_encryption = true\npassword = <用 `rclone obscure` 加密后的串>\npassword2 = <用 `rclone obscure` 加密后的串>   # 可选\n```\n\n### `config/.env`\n\n```sh\nRCLONE_REMOTE=notes                       # crypt 远端名\nBACKUP_KEEP=1                             # 滚动本地备份数量\nRCLONE_EXTRA_FLAGS=\"--transfers=4 --checkers=8\"\n```\n\n### `config/filter.txt`\n\n`rclone` 的过滤规则。默认排除 `.obsidian/`、`.DS_Store`、`Thumbs.db`、各类 swap / lock 文件,可按需调整。\n\n---\n\n## 必需的环境变量\n\n| 变量 | 用途 | 必填 |\n|---|---|---|\n| `CLOUD_NOTES_PATH` | 本地笔记目录的绝对路径 | ✅ |\n\n如果 `CLOUD_NOTES_PATH` 未设置、不存在或不是目录,脚本会立刻退出并给出明确报错。\n\n---\n\n## 与 Remotely Save 互通\n\n`crypt` 配置是按 Remotely Save 默认值刻意调过的:\n\n- `filename_encryption = standard`\n- `filename_encoding = base64`\n- `directory_name_encryption = true`\n- 同一个密码(以及 salt,如果设了的话)\n\n只要这些参数对齐,手机端(Remotely Save)和桌面端(本 skill)就能安全地同步同一个 R2 bucket。\n\n---\n\n## OpenClaw 集成\n\n把这个目录放进 OpenClaw 工作空间后,可以用斜杠命令触发:\n\n```\n/sync-notes              → bisync\n/sync-notes setup        → 向导\n/sync-notes init         → 建立基线\n/sync-notes status       → 状态\n/sync-notes 下载 welcome  → download '**welcome**'\n/sync-notes upload daily/*.md\n```\n\n完整的「自然语言 → 子命令」映射规则在 [`SKILL.md`](./SKILL.md) 里。\n\n---\n\n## 安全提醒\n\n- **不要提交 `config/rclone.conf` 和 `config/.env`** —— 已经写进 `.gitignore`,push 前再扫一眼更稳。\n- **不要提交 `backups/`** —— 里面是 vault 的明文副本。也已经 gitignore。\n- crypt 密码是 Cloudflare 与你的明文笔记之间唯一的一道墙,请离线另存。\n- `bisync` 威力很大,可以双向删除文件。前几次同步多用 `--dry-run` 和 `status` 看一眼再实跑。\n\n---\n\n## 协议\n\nMIT。如有 [`LICENSE`](./LICENSE) 文件以其为准。\n\nFile v1.0.1:skill-card.md\n\n## Description:\n\nSync the local Obsidian vault with a Cloudflare R2 bucket using rclone (S3 + crypt). Triggered by `/sync-notes`. Defaults to bidirectional sync; understands natural-language flags for partial download/upload by glob.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[jiangsier-xyz](https://clawhub.ai/user/jiangsier-xyz)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and note-taking users use Sync Notes to configure and run encrypted bidirectional sync between a local Obsidian or Markdown vault and a Cloudflare R2 bucket. The agent translates `/sync-notes` requests into setup, status, baseline, full sync, upload, or download commands.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Bidirectional sync can change or delete notes on both local and remote storage.\n\nMitigation: Run status and dry-run checks before first or broad syncs, resolve conflicts manually, and keep an independent backup of the vault.\n\nRisk: R2 credentials and rclone crypt secrets are stored locally in configuration files.\n\nMitigation: Keep configuration files private with restrictive permissions, never expose secrets in chat, and back up the crypt passphrase offline.\n\nRisk: Crafted shell configuration values or unrestricted extra rclone flags could cause unintended command execution or unsafe sync behavior.\n\nMitigation: Review generated configuration before syncing, validate setup inputs, and remove or allowlist extra rclone flags before deployment.\n\n## Reference(s):\n\n- [Sync Notes ClawHub Page](https://clawhub.ai/jiangsier-xyz/skills/sync-notes)\n- [rclone Documentation](https://rclone.org/)\n- [rclone Install Documentation](https://rclone.org/install/)\n- [rclone Filtering Documentation](https://rclone.org/filtering/)\n- [Cloudflare R2 Documentation](https://developers.cloudflare.com/r2/)\n- [Remotely Save Obsidian Plugin](https://github.com/remotely-save/remotely-save)\n- [Default rclone Filter Rules](config/filter.txt)\n\n## Skill Output:\n\n**Output Type(s):** [shell commands, configuration, guidance]\n\n**Output Format:** [Markdown with inline shell commands and concise status or conflict summaries]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [May create local rclone configuration, logs, state, and backup files when its shell scripts are executed.]\n\n## Skill Version(s):\n\n1.0.1 (source: frontmatter and server release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nFile v1.0.1:config/filter.txt\n\n# rclone filter rules for sync-notes\n# Format: each line is \"+ pattern\" (include) or \"- pattern\" (exclude).\n# First match wins; unmatched files are included by default.\n# See: https://rclone.org/filtering/\n\n# Obsidian internal state — managed per-device, not synced (matches Remotely Save default)\n- .obsidian/**\n\n# Obsidian trash\n- .trash/**\n\n# OS / editor cruft\n- .DS_Store\n- Thumbs.db\n- *.swp\n- *.swo\n- .~lock.*\n\nArchive v1.0.0: 8 files, 16502 bytes\n\nFiles: config/filter.txt (417b), LICENSE (1070b), README.md (7148b), README.zh-CN.md (6792b), scripts/setup.sh (3546b), scripts/sync.sh (7976b), SKILL.md (7450b), _meta.json (129b)\n\nFile v1.0.0:SKILL.md\n\n---\nname: sync-notes\ndescription: Sync the local Obsidian vault with a Cloudflare R2 bucket using rclone (S3 + crypt). Triggered by `/sync-notes`. Defaults to bidirectional sync; understands natural-language flags for partial download/upload by glob.\nversion: 1.0.0\nmetadata:\n  {\"openclaw\":{\"emoji\":\"☁️\",\"requires\":{\"bins\":[\"rclone\",\"bash\"],\"env\":[\"CLOUD_NOTES_PATH\"]}}}\n---\n\n# sync-notes Skill\n\nSync the local notes directory (`$CLOUD_NOTES_PATH`) with a **Cloudflare R2** bucket through an\nend-to-end encrypted **rclone crypt** remote. The skill is a thin orchestrator over a single bash\nentry point at `scripts/sync.sh`; the LLM's job is to translate the user's natural-language intent\ninto the right subcommand.\n\n## Trigger\n\nActivate **only** when the user message starts with `/sync-notes` (with or without trailing\narguments). Examples:\n\n| User message | Intent | Action |\n|---|---|---|\n| `/sync-notes` | full bidirectional sync | `sync.sh bisync` |\n| `/sync-notes setup` | run the configuration wizard | `sync.sh setup` |\n| `/sync-notes init` | establish the bisync baseline (first run only) | `sync.sh init` |\n| `/sync-notes status` | show config + last-sync info | `sync.sh status` |\n| `/sync-notes 下载 welcome` | download files matching `welcome` | `sync.sh download '**welcome**'` |\n| `/sync-notes upload daily/*.md` | upload glob | `sync.sh upload 'daily/*.md'` |\n| `/sync-notes 把 ideas 目录拉下来` | download a directory | `sync.sh download 'ideas/**'` |\n\n## How to interpret natural language\n\nAfter stripping the `/sync-notes` prefix, classify the remainder:\n\n1. **Empty** → run `bisync` (default bidirectional sync).\n2. Contains a recognised keyword:\n   - `setup`, `配置`, `wizard` → `setup`\n   - `init`, `初始化`, `baseline` → `init`\n   - `status`, `状态`, `信息` → `status`\n3. Mentions **download** intent (`下载`, `拉`, `pull`, `download`, `get`, `取回`):\n   - Extract the target token(s) and convert to a glob (see \"Glob rules\" below).\n   - Run `sync.sh download '<glob>'`.\n4. Mentions **upload** intent (`上传`, `推`, `push`, `upload`, `send`):\n   - Same as above with `sync.sh upload '<glob>'`.\n5. Otherwise: ask the user to clarify; do not guess.\n\n### Glob rules\n\nGlobs are passed verbatim to rclone as `--include` filters and apply to *plain* (decrypted) paths.\n\n- A bare keyword such as `welcome` becomes `**welcome**` (matches anywhere in path).\n- A directory name like `ideas` becomes `ideas/**`.\n- Paths already containing wildcards (`*`, `?`, `[`) are passed through unchanged.\n- Filenames without extension default to matching `<name>` and `<name>.md`; combine with `**` if\n  unsure. When in doubt, run `sync.sh download '<glob>' --dry-run` first.\n\n## Workflow contract\n\n1. **First run ever:** the script refuses `bisync` until a baseline exists. Tell the user to run\n   `/sync-notes init` after confirming both sides are in the desired starting state.\n2. **Backups:** before any write-side operation (`bisync`, `download`, `init`), the local notes\n   directory is mirrored to `backups/latest/` (single rolling copy, previous one is replaced).\n3. **Conflicts:** bisync runs with conflict markers. After every run the script scans for\n   `*.conflict*` files; if any are found the script exits non-zero and lists them. **Surface this\n   list to the user verbatim and ask which side to keep**—do not auto-resolve.\n4. **Logs:** every invocation appends to `logs/sync.log` and emits a per-run file\n   `logs/<UTC-timestamp>.log` for the rclone output.\n\n## Filesystem layout\n\n```\nskills/sync-notes/\n├── SKILL.md                  # this file\n├── README.md                 # short user-facing notes (optional)\n├── config/\n│   ├── rclone.conf           # active rclone config (created by setup, gitignored)\n│   ├── rclone.conf.example   # template\n│   ├── .env                  # non-rclone settings (created by setup, gitignored)\n│   └── .env.example          # template\n├── scripts/\n│   ├── sync.sh               # main entry point (dispatcher)\n│   └── setup.sh              # interactive configuration wizard\n├── state/\n│   └── bisync/               # rclone bisync workdir (listings + lockfiles)\n├── logs/                     # rolling logs\n├── backups/\n│   └── latest/               # most recent local snapshot (single copy)\n└── .gitignore\n```\n\n`scripts/sync.sh` derives every path from its own location (`$(dirname \"$0\")/..`); no absolute\npaths are hard-coded.\n\n## Required environment\n\n| Variable | Purpose | Required |\n|---|---|---|\n| `CLOUD_NOTES_PATH` | absolute path of the local notes directory | ✅ |\n\nThe script aborts early with a clear message if `CLOUD_NOTES_PATH` is unset, missing, or not a\ndirectory.\n\n## Configuration files\n\n### `config/rclone.conf`\n\nNative rclone format with **two remotes**:\n\n```ini\n[r2-raw]\ntype = s3\nprovider = Cloudflare\naccess_key_id = <ACCESS_KEY_ID>\nsecret_access_key = <SECRET_ACCESS_KEY>\nendpoint = https://<ACCOUNT_ID>.r2.cloudflarestorage.com\nacl = private\n\n[notes]\ntype = crypt\nremote = r2-raw:<BUCKET>/<OPTIONAL_PREFIX>\nfilename_encryption = standard\ndirectory_name_encryption = true\npassword = <OBSCURED>\npassword2 = <OBSCURED>\n```\n\n### `config/.env`\n\nNon-rclone settings consumed by `sync.sh`:\n\n```sh\n# Name of the crypt remote in rclone.conf\nRCLONE_REMOTE=notes\n# Number of local backup snapshots to retain\nBACKUP_KEEP=1\n# Extra rclone flags (optional)\nRCLONE_EXTRA_FLAGS=\"--transfers=4 --checkers=8\"\n```\n\n`.example` versions ship with the skill; the wizard fills in the real values.\n\n## Subcommands implemented by `sync.sh`\n\n| Subcommand | Behaviour |\n|---|---|\n| `setup` | Run `setup.sh`; collects R2 creds + crypt password(s); writes `rclone.conf` and `.env` with `chmod 600`. Idempotent: confirms before overwriting. |\n| `init` | First-time baseline: `rclone bisync --resync`. Refuses to run if a baseline already exists unless `--force` is passed. |\n| `bisync` (default) | `rclone bisync $LOCAL notes:` with conflict reporting. Backs up first. |\n| `download <glob>` | `rclone copy notes: $LOCAL --include '<glob>'`. Backs up first. Defaults to `--dry-run` only when the user said \"试\" / \"dry\"; otherwise runs for real. |\n| `upload <glob>` | `rclone copy $LOCAL notes: --include '<glob>'`. No local backup needed; remote is authoritative. |\n| `status` | Prints config validity, last-sync timestamp, conflict files (if any), backup state. |\n\n## Safety rules for the LLM\n\n- **Never** write secrets into chat. If the user pastes secrets, immediately move them into\n  `config/rclone.conf` / `config/.env` and remove from any visible context.\n- **Never** invoke `rclone` with `--config` pointing somewhere outside this skill.\n- **Never** run `bisync` if `status` reports unresolved conflicts; ask first.\n- When `download`/`upload` glob match count is ambiguous, run with `--dry-run` first and show the\n  list of matches.\n- Backups are single-copy; if you are about to do something risky (large delete, schema change),\n  copy `backups/latest/` to `backups/manual-<UTC>/` manually before proceeding and tell the user.\n\n## First-time onboarding (what to tell the user)\n\n```\n1. /sync-notes setup           # fill in R2 + crypt creds\n2. export CLOUD_NOTES_PATH=...  # point at the local notes dir\n3. /sync-notes init             # establish the bisync baseline\n4. /sync-notes                  # routine bidirectional sync\n```\n\nFile v1.0.0:README.md\n\n# sync-notes\n\n> End-to-end encrypted, bidirectional sync between a local notes vault (Obsidian, plain Markdown, anything really) and a **Cloudflare R2** bucket — powered by [`rclone`](https://rclone.org/) `bisync` over an `rclone crypt` remote.\n\nThis is an [OpenClaw](https://openclaw.ai) skill, but the heavy lifting is just a pair of bash scripts. You can use them outside of OpenClaw without modification.\n\n[简体中文](./README.zh-CN.md)\n\n---\n\n## Why?\n\n- 🔒 **Zero-trust storage.** Your notes are encrypted client-side via `rclone crypt`. Cloudflare only ever sees ciphertext blobs with encrypted filenames.\n- 🔁 **Real bidirectional sync.** `rclone bisync` propagates additions, modifications and deletions in both directions, with conflict markers when both sides changed.\n- 📱 **Mobile-friendly.** The crypt format is compatible with the [Remotely Save](https://github.com/remotely-save/remotely-save) Obsidian plugin, so your phone can sync the same bucket without holding the rclone config.\n- 🧰 **Self-contained.** All paths derive from the script location. No daemons, no extra services — just `bash` + `rclone`.\n- 🛟 **Backups before writes.** Every write-side run mirrors the local vault to `backups/latest/` first.\n\n---\n\n## Layout\n\n```\nsync-notes/\n├── SKILL.md                      # OpenClaw skill manifest (also the design doc)\n├── README.md / README.zh-CN.md   # this file\n├── config/\n│   ├── rclone.conf.example       # template for the rclone remotes\n│   ├── rclone.conf               # your real config (gitignored, chmod 600)\n│   ├── .env.example              # template for non-rclone settings\n│   ├── .env                      # your real settings  (gitignored, chmod 600)\n│   └── filter.txt                # rclone filter rules (excludes .obsidian/, .DS_Store, etc.)\n├── scripts/\n│   ├── setup.sh                  # interactive configuration wizard\n│   └── sync.sh                   # main entry point / dispatcher\n├── state/                        # rclone bisync workdir (gitignored)\n├── logs/                         # per-run + master logs   (gitignored)\n└── backups/                      # rolling local snapshots (gitignored)\n```\n\n---\n\n## Requirements\n\n- `bash` 4+\n- [`rclone`](https://rclone.org/install/) **v1.65+** (older builds lack some `bisync` flags used here)\n- A [Cloudflare R2](https://developers.cloudflare.com/r2/) bucket and an S3-compatible API token scoped to it\n- A passphrase (and optional salt) for the `crypt` remote — **lose this and the data is unrecoverable**\n\n---\n\n## Quick start\n\n```bash\n# 1. Clone the repo somewhere\ngit clone https://github.com/jiangsier-xyz/jiangsier-skill-sync-notes.git\ncd jiangsier-skill-sync-notes\n\n# 2. Tell the script where your notes live\nexport CLOUD_NOTES_PATH=/absolute/path/to/your/vault\n\n# 3. Run the wizard — fills in rclone.conf and .env (chmod 600)\n./scripts/setup.sh\n\n# 4. Sanity check\n./scripts/sync.sh status\n\n# 5. First-time baseline (must do this once before bisync will run)\n./scripts/sync.sh init\n\n# 6. Routine bidirectional sync from now on\n./scripts/sync.sh\n```\n\n> 💡 **Putting `CLOUD_NOTES_PATH` in your shell rc (`.zshrc` / `.bashrc`) is the most painless option.**\n\n---\n\n## Subcommands\n\n| Command | Behaviour |\n|---|---|\n| `sync.sh setup` | Run the wizard. Idempotent — confirms before overwriting existing config. |\n| `sync.sh init [--force]` | First-run baseline (`rclone bisync --resync`). Refuses to run if a baseline already exists. |\n| `sync.sh bisync` (default) | Bidirectional sync with conflict reporting. Backs up locally first. |\n| `sync.sh download <glob> [--dry-run]` | Pull files matching `<glob>` from the remote into the local vault. Backs up first. |\n| `sync.sh upload <glob> [--dry-run]` | Push files matching `<glob>` to the remote. |\n| `sync.sh status` | Show config validity, last-sync info, conflicts, backup state. |\n\n`<glob>` is passed to `rclone --include` and applies to **plaintext** paths (after decryption). A bare keyword like `welcome` is broadened to `**welcome**` automatically; pre-wildcarded globs (`ideas/**`, `*.md`) pass through unchanged.\n\n---\n\n## Conflict handling\n\n`bisync` runs with `--conflict-suffix conflict1,conflict2`. After every run, the script scans the vault for `*.conflict1*` / `*.conflict2*` files. **If any are found, the script exits non-zero and prints the list — the LLM/human is expected to resolve them manually before the next sync.** No silent automatic merging.\n\n---\n\n## Configuration files\n\n### `config/rclone.conf`\n\nTwo remotes:\n\n```ini\n[r2-raw]\ntype = s3\nprovider = Cloudflare\naccess_key_id = <ACCESS_KEY_ID>\nsecret_access_key = <SECRET_ACCESS_KEY>\nendpoint = https://<ACCOUNT_ID>.r2.cloudflarestorage.com\nacl = private\n\n[notes]\ntype = crypt\nremote = r2-raw:<BUCKET>/<OPTIONAL_PREFIX>\nfilename_encryption = standard\nfilename_encoding = base64        # match Remotely Save (rclone default is base32)\ndirectory_name_encryption = true\npassword = <OBSCURED via `rclone obscure`>\npassword2 = <OBSCURED via `rclone obscure`>   # optional\n```\n\n### `config/.env`\n\n```sh\nRCLONE_REMOTE=notes                       # crypt remote name\nBACKUP_KEEP=1                             # rolling local backup count\nRCLONE_EXTRA_FLAGS=\"--transfers=4 --checkers=8\"\n```\n\n### `config/filter.txt`\n\n`rclone` filter rules. The shipped defaults exclude `.obsidian/`, `.DS_Store`, `Thumbs.db`, swap files and Office lock files — adjust to taste.\n\n---\n\n## Required environment\n\n| Variable | Purpose | Required |\n|---|---|---|\n| `CLOUD_NOTES_PATH` | Absolute path of the local notes directory | ✅ |\n\nThe script aborts immediately with a clear message if `CLOUD_NOTES_PATH` is unset, missing, or not a directory.\n\n---\n\n## Remotely Save interop\n\nThe `crypt` settings above are deliberately tuned to match Remotely Save's defaults:\n\n- `filename_encryption = standard`\n- `filename_encoding = base64`\n- `directory_name_encryption = true`\n- Same passphrase (and salt, if you set one)\n\nWith those aligned, your phone (Remotely Save) and your desktop (this skill) can safely sync to the same R2 bucket.\n\n---\n\n## OpenClaw integration\n\nWhen dropped into an OpenClaw workspace, this skill is triggered by:\n\n```\n/sync-notes              → bisync\n/sync-notes setup        → wizard\n/sync-notes init         → baseline\n/sync-notes status       → status\n/sync-notes 下载 welcome  → download '**welcome**'\n/sync-notes upload daily/*.md\n```\n\nSee [`SKILL.md`](./SKILL.md) for the full natural-language → subcommand mapping that the LLM is expected to follow.\n\n---\n\n## Safety notes\n\n- **Never commit `config/rclone.conf` or `config/.env`** — they're in `.gitignore`, but double-check before pushing.\n- **Never commit `backups/`** — it contains a verbatim copy of your vault. Also gitignored.\n- The `crypt` passphrase is the only thing standing between Cloudflare and your plaintext notes. Back it up offline.\n- `bisync` is powerful and can delete on both sides. Use `--dry-run` and `status` liberally on the first few runs.\n\n---\n\n## License\n\nMIT. See [`LICENSE`](./LICENSE) if present.\n\nFile v1.0.0:_meta.json\n\n{\n  \"ownerId\": \"kn73sdyhq2p2h53qne8t5nvacs86x2bc\",\n  \"slug\": \"sync-notes\",\n  \"version\": \"1.0.0\",\n  \"publishedAt\": 1779011602442\n}\n\nFile v1.0.0:README.zh-CN.md\n\n# sync-notes\n\n> 在本地笔记目录(Obsidian / 纯 Markdown / 任意文件夹)与 **Cloudflare R2** 之间做端到端加密的双向同步,底层用 [`rclone`](https://rclone.org/) 的 `bisync` + `crypt` 远端实现。\n\n这是一个 [OpenClaw](https://openclaw.ai) 技能(skill),但核心其实是两个 bash 脚本,可以脱离 OpenClaw 独立使用。\n\n[English](./README.md)\n\n---\n\n## 为什么用它?\n\n- 🔒 **零信任存储**: 笔记在客户端用 `rclone crypt` 加密,Cloudflare 只看得到密文 blob 和加密过的文件名。\n- 🔁 **真双向同步**: `rclone bisync` 会双向传播新增、修改、删除,两边都改了的话会留下冲突标记,不会静默覆盖。\n- 📱 **手机可用**: crypt 格式与 Obsidian 插件 [Remotely Save](https://github.com/remotely-save/remotely-save) 兼容,手机端不用塞 rclone 配置就能同步同一个 bucket。\n- 🧰 **零依赖、零守护进程**: 所有路径都从脚本位置推导,只需要 `bash` + `rclone`。\n- 🛟 **写入前先备份**: 任何写本地的命令都会先把当前 vault 镜像到 `backups/latest/`。\n\n---\n\n## 目录结构\n\n```\nsync-notes/\n├── SKILL.md                      # OpenClaw 技能清单 + 设计文档\n├── README.md / README.zh-CN.md   # 本文件\n├── config/\n│   ├── rclone.conf.example       # rclone 远端模板\n│   ├── rclone.conf               # 真实配置 (gitignored, chmod 600)\n│   ├── .env.example              # 非 rclone 设置模板\n│   ├── .env                      # 真实设置   (gitignored, chmod 600)\n│   └── filter.txt                # rclone 过滤规则(默认排除 .obsidian/、.DS_Store 等)\n├── scripts/\n│   ├── setup.sh                  # 交互式配置向导\n│   └── sync.sh                   # 主入口 / 调度器\n├── state/                        # rclone bisync 工作目录 (gitignored)\n├── logs/                         # 每次运行和总日志        (gitignored)\n└── backups/                      # 滚动本地快照            (gitignored)\n```\n\n---\n\n## 依赖\n\n- `bash` 4+\n- [`rclone`](https://rclone.org/install/) **v1.65+**(更早版本不支持脚本里用到的部分 `bisync` 选项)\n- 一个 [Cloudflare R2](https://developers.cloudflare.com/r2/) bucket 以及一个限定权限的 S3 兼容 token\n- crypt 远端的密码(以及可选的 salt)—— **丢了就不可恢复**,务必离线备份一份\n\n---\n\n## 快速开始\n\n```bash\n# 1. 克隆仓库\ngit clone https://github.com/jiangsier-xyz/jiangsier-skill-sync-notes.git\ncd jiangsier-skill-sync-notes\n\n# 2. 告诉脚本你的笔记目录在哪里\nexport CLOUD_NOTES_PATH=/absolute/path/to/your/vault\n\n# 3. 跑配置向导,自动写好 rclone.conf 和 .env(权限 600)\n./scripts/setup.sh\n\n# 4. 健康检查\n./scripts/sync.sh status\n\n# 5. 第一次必须先建立基线\n./scripts/sync.sh init\n\n# 6. 之后日常双向同步\n./scripts/sync.sh\n```\n\n> 💡 **建议把 `CLOUD_NOTES_PATH` 写进你的 shell rc(`.zshrc` / `.bashrc`),省心。**\n\n---\n\n## 子命令\n\n| 命令 | 行为 |\n|---|---|\n| `sync.sh setup` | 跑配置向导。幂等,覆盖前会问一次。 |\n| `sync.sh init [--force]` | 首次建立 bisync 基线(`rclone bisync --resync`)。已有基线时拒绝运行,除非加 `--force`。 |\n| `sync.sh bisync`(默认) | 双向同步,带冲突检测;运行前先做本地备份。 |\n| `sync.sh download <glob> [--dry-run]` | 从远端把匹配 `<glob>` 的文件拉下来;运行前先做本地备份。 |\n| `sync.sh upload <glob> [--dry-run]` | 把本地匹配 `<glob>` 的文件推上远端。 |\n| `sync.sh status` | 打印配置状态、上次同步信息、冲突文件、备份状态。 |\n\n`<glob>` 会传给 `rclone --include`,作用在**解密后的明文路径**上。光秃秃的关键词(如 `welcome`)会自动展开成 `**welcome**`;已经带通配符的(`ideas/**`、`*.md`)原样透传。\n\n---\n\n## 冲突处理\n\n`bisync` 运行时使用 `--conflict-suffix conflict1,conflict2`。每次运行结束脚本会扫描 vault,如果发现 `*.conflict1*` / `*.conflict2*` 文件,**会以非零退出码报错并列出冲突清单 —— 由人(或上层 LLM)手动决定保留哪一边再继续同步**,脚本不会静默自动合并。\n\n---\n\n## 配置文件\n\n### `config/rclone.conf`\n\n两个远端:\n\n```ini\n[r2-raw]\ntype = s3\nprovider = Cloudflare\naccess_key_id = <ACCESS_KEY_ID>\nsecret_access_key = <SECRET_ACCESS_KEY>\nendpoint = https://<ACCOUNT_ID>.r2.cloudflarestorage.com\nacl = private\n\n[notes]\ntype = crypt\nremote = r2-raw:<BUCKET>/<OPTIONAL_PREFIX>\nfilename_encryption = standard\nfilename_encoding = base64        # 与 Remotely Save 对齐(rclone 默认是 base32)\ndirectory_name_encryption = true\npassword = <用 `rclone obscure` 加密后的串>\npassword2 = <用 `rclone obscure` 加密后的串>   # 可选\n```\n\n### `config/.env`\n\n```sh\nRCLONE_REMOTE=notes                       # crypt 远端名\nBACKUP_KEEP=1                             # 滚动本地备份数量\nRCLONE_EXTRA_FLAGS=\"--transfers=4 --checkers=8\"\n```\n\n### `config/filter.txt`\n\n`rclone` 的过滤规则。默认排除 `.obsidian/`、`.DS_Store`、`Thumbs.db`、各类 swap / lock 文件,可按需调整。\n\n---\n\n## 必需的环境变量\n\n| 变量 | 用途 | 必填 |\n|---|---|---|\n| `CLOUD_NOTES_PATH` | 本地笔记目录的绝对路径 | ✅ |\n\n如果 `CLOUD_NOTES_PATH` 未设置、不存在或不是目录,脚本会立刻退出并给出明确报错。\n\n---\n\n## 与 Remotely Save 互通\n\n`crypt` 配置是按 Remotely Save 默认值刻意调过的:\n\n- `filename_encryption = standard`\n- `filename_encoding = base64`\n- `directory_name_encryption = true`\n- 同一个密码(以及 salt,如果设了的话)\n\n只要这些参数对齐,手机端(Remotely Save)和桌面端(本 skill)就能安全地同步同一个 R2 bucket。\n\n---\n\n## OpenClaw 集成\n\n把这个目录放进 OpenClaw 工作空间后,可以用斜杠命令触发:\n\n```\n/sync-notes              → bisync\n/sync-notes setup        → 向导\n/sync-notes init         → 建立基线\n/sync-notes status       → 状态\n/sync-notes 下载 welcome  → download '**welcome**'\n/sync-notes upload daily/*.md\n```\n\n完整的「自然语言 → 子命令」映射规则在 [`SKILL.md`](./SKILL.md) 里。\n\n---\n\n## 安全提醒\n\n- **不要提交 `config/rclone.conf` 和 `config/.env`** —— 已经写进 `.gitignore`,push 前再扫一眼更稳。\n- **不要提交 `backups/`** —— 里面是 vault 的明文副本。也已经 gitignore。\n- crypt 密码是 Cloudflare 与你的明文笔记之间唯一的一道墙,请离线另存。\n- `bisync` 威力很大,可以双向删除文件。前几次同步多用 `--dry-run` 和 `status` 看一眼再实跑。\n\n---\n\n## 协议\n\nMIT。如有 [`LICENSE`](./LICENSE) 文件以其为准。\n\nFile v1.0.0:config/filter.txt\n\n# rclone filter rules for sync-notes\n# Format: each line is \"+ pattern\" (include) or \"- pattern\" (exclude).\n# First match wins; unmatched files are included by default.\n# See: https://rclone.org/filtering/\n\n# Obsidian internal state — managed per-device, not synced (matches Remotely Save default)\n- .obsidian/**\n\n# Obsidian trash\n- .trash/**\n\n# OS / editor cruft\n- .DS_Store\n- Thumbs.db\n- *.swp\n- *.swo\n- .~lock.*\n\nFile v1.0.0:LICENSE\n\nMIT License\n\nCopyright (c) 2026 jiangsier-xyz\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.","readmeExcerpt":"Skill: Sync Notes Owner: jiangsier-xyz Summary: Sync the local Obsidian vault with a Cloudflare R2 bucket using rclone (S3 + crypt). Triggered by /sync-notes. Defaults to bidirectional sync; understands... Tags: latest:1.0.1 Version history: v1.0.1 | 2026-05-23T02:11:38.242Z | auto - Removed LICENSE file for streamlined distribution. - Updated setup and sync scripts (setup.sh, sync.sh)—maintenance, internal improveme","codeSnippets":[],"executableExamples":[{"language":"text","snippet":"skills/sync-notes/\n├── SKILL.md                  # this file\n├── README.md                 # short user-facing notes (optional)\n├── config/\n│   ├── rclone.conf           # active rclone config (created by setup, gitignored)\n│   ├── rclone.conf.example   # template\n│   ├── .env                  # non-rclone settings (created by setup, gitignored)\n│   └── .env.example          # template\n├── scripts/\n│   ├── sync.sh               # main entry point (dispatcher)\n│   └── setup.sh              # interactive configuration wizard\n├── state/\n│   └── bisync/               # rclone bisync workdir (listings + lockfiles)\n├── logs/                     # rolling logs\n├── backups/\n│   └── latest/               # most recent local snapshot (single copy)\n└── .gitignore"},{"language":"ini","snippet":"[r2-raw]\ntype = s3\nprovider = Cloudflare\naccess_key_id = <ACCESS_KEY_ID>\nsecret_access_key = <SECRET_ACCESS_KEY>\nendpoint = https://<ACCOUNT_ID>.r2.cloudflarestorage.com\nacl = private\n\n[notes]\ntype = crypt\nremote = r2-raw:<BUCKET>/<OPTIONAL_PREFIX>\nfilename_encryption = standard\ndirectory_name_encryption = true\npassword = <OBSCURED>\npassword2 = <OBSCURED>"},{"language":"sh","snippet":"# Name of the crypt remote in rclone.conf\nRCLONE_REMOTE=notes\n# Number of local backup snapshots to retain\nBACKUP_KEEP=1\n# Extra rclone flags (optional)\nRCLONE_EXTRA_FLAGS=\"--transfers=4 --checkers=8\""},{"language":"text","snippet":"1. /sync-notes setup           # fill in R2 + crypt creds\n2. export CLOUD_NOTES_PATH=...  # point at the local notes dir\n3. /sync-notes init             # establish the bisync baseline\n4. /sync-notes                  # routine bidirectional sync"},{"language":"text","snippet":"sync-notes/\n├── SKILL.md                      # OpenClaw skill manifest (also the design doc)\n├── README.md / README.zh-CN.md   # this file\n├── config/\n│   ├── rclone.conf.example       # template for the rclone remotes\n│   ├── rclone.conf               # your real config (gitignored, chmod 600)\n│   ├── .env.example              # template for non-rclone settings\n│   ├── .env                      # your real settings  (gitignored, chmod 600)\n│   └── filter.txt                # rclone filter rules (excludes .obsidian/, .DS_Store, etc.)\n├── scripts/\n│   ├── setup.sh                  # interactive configuration wizard\n│   └── sync.sh                   # main entry point / dispatcher\n├── state/                        # rclone bisync workdir (gitignored)\n├── logs/                         # per-run + master logs   (gitignored)\n└── backups/                      # rolling local snapshots (gitignored)"},{"language":"bash","snippet":"# 1. Clone the repo somewhere\ngit clone https://github.com/jiangsier-xyz/jiangsier-skill-sync-notes.git\ncd jiangsier-skill-sync-notes\n\n# 2. Tell the script where your notes live\nexport CLOUD_NOTES_PATH=/absolute/path/to/your/vault\n\n# 3. Run the wizard — fills in rclone.conf and .env (chmod 600)\n./scripts/setup.sh\n\n# 4. Sanity check\n./scripts/sync.sh status\n\n# 5. First-time baseline (must do this once before bisync will run)\n./scripts/sync.sh init\n\n# 6. Routine bidirectional sync from now on\n./scripts/sync.sh"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: sync-notes\ndescription: Sync the local Obsidian vault with a Cloudflare R2 bucket using rclone (S3 + crypt). Triggered by `/sync-notes`. Defaults to bidirectional sync; understands natural-language flags for partial download/upload by glob.\nversion: 1.0.1\nmetadata:\n  {\"openclaw\":{\"emoji\":\"☁️\",\"requires\":{\"bins\":[\"rclone\",\"bash\"],\"env\":[\"CLOUD_NOTES_PATH\"]}}}\n---\n\n# sync-notes Skill\n\nSync the local notes directory (`$CLOUD_NOTES_PATH`) with a **Cloudflare R2** bucket through an\nend-to-end encrypted **rclone crypt** remote. The skill is a thin orchestrator over a single bash\nentry point at `scripts/sync.sh`; the LLM's job is to translate the user's natural-language intent\ninto the right subcommand.\n\n## Trigger\n\nActivate **only** when the user message starts with `/sync-notes` (with or without trailing\narguments). Examples:\n\n| User message | Intent | Action |\n|---|---|---|\n| `/sync-notes` | full bidirectional sync | `sync.sh bisync` |\n| `/sync-notes setup` | run the configuration wizard | `sync.sh setup` |\n| `/sync-notes init` | establish the bisync baseline (first run only) | `sync.sh init` |\n| `/sync-notes status` | show config + last-sync info | `sync.sh status` |\n| `/sync-notes 下载 welcome` | download files matching `welcome` | `sync.sh download '**welcome**'` |\n| `/sync-notes upload daily/*.md` | upload glob | `sync.sh upload 'daily/*.md'` |\n| `/sync-notes 把 ideas 目录拉下来` | download a directory | `sync.sh download 'ideas/**'` |\n\n## How to interpret natural language\n\nAfter stripping the `/sync-notes` prefix, classify the remainder:\n\n1. **Empty** → run `bisync` (default bidirectional sync).\n2. Contains a recognised keyword:\n   - `setup`, `配置`, `wizard` → `setup`\n   - `init`, `初始化`, `baseline` → `init`\n   - `status`, `状态`, `信息` → `status`\n3. Mentions **download** intent (`下载`, `拉`, `pull`, `download`, `get`, `取回`):\n   - Extract the target token(s) and convert to a glob (see \"Glob rules\" below).\n   - Run `sync.sh download '<glob>'`.\n4. Mentions **upload** intent (`上传`, `推`, `push`, `upload`, `send`):\n   - Same as above with `sync.sh upload '<glob>'`.\n5. Otherwise: ask the user to clarify; do not guess.\n\n### Glob rules\n\nGlobs are passed verbatim to rclone as `--include` filters and apply to *plain* (decrypted) paths.\n\n- A bare keyword such as `welcome` becomes `**welcome**` (matches anywhere in path).\n- A directory name like `ideas` becomes `ideas/**`.\n- Paths already containing wildcards (`*`, `?`, `[`) are passed through unchanged.\n- Filenames without extension default to matching `<name>` and `<name>.md`; combine with `**` if\n  unsure. When in doubt, run `sync.sh download '<glob>' --dry-run` first.\n\n## Workflow contract\n\n1. **First run ever:** the script refuses `bisync` until a baseline exists. Tell the user to run\n   `/sync-notes init` after confirming both sides are in the desired starting state.\n2. **Backups:** before any write-side operation (`bisync`, `download`, `init`), the local notes\n   directory is mirrored to `backups/latest/`"},{"path":"README.md","content":"# sync-notes\n\n> End-to-end encrypted, bidirectional sync between a local notes vault (Obsidian, plain Markdown, anything really) and a **Cloudflare R2** bucket — powered by [`rclone`](https://rclone.org/) `bisync` over an `rclone crypt` remote.\n\nThis is an [OpenClaw](https://openclaw.ai) skill, but the heavy lifting is just a pair of bash scripts. You can use them outside of OpenClaw without modification.\n\n[简体中文](./README.zh-CN.md)\n\n---\n\n## Why?\n\n- 🔒 **Zero-trust storage.** Your notes are encrypted client-side via `rclone crypt`. Cloudflare only ever sees ciphertext blobs with encrypted filenames.\n- 🔁 **Real bidirectional sync.** `rclone bisync` propagates additions, modifications and deletions in both directions, with conflict markers when both sides changed.\n- 📱 **Mobile-friendly.** The crypt format is compatible with the [Remotely Save](https://github.com/remotely-save/remotely-save) Obsidian plugin, so your phone can sync the same bucket without holding the rclone config.\n- 🧰 **Self-contained.** All paths derive from the script location. No daemons, no extra services — just `bash` + `rclone`.\n- 🛟 **Backups before writes.** Every write-side run mirrors the local vault to `backups/latest/` first.\n\n---\n\n## Layout\n\n```\nsync-notes/\n├── SKILL.md                      # OpenClaw skill manifest (also the design doc)\n├── README.md / README.zh-CN.md   # this file\n├── config/\n│   ├── rclone.conf.example       # template for the rclone remotes\n│   ├── rclone.conf               # your real config (gitignored, chmod 600)\n│   ├── .env.example              # template for non-rclone settings\n│   ├── .env                      # your real settings  (gitignored, chmod 600)\n│   └── filter.txt                # rclone filter rules (excludes .obsidian/, .DS_Store, etc.)\n├── scripts/\n│   ├── setup.sh                  # interactive configuration wizard\n│   └── sync.sh                   # main entry point / dispatcher\n├── state/                        # rclone bisync workdir (gitignored)\n├── logs/                         # per-run + master logs   (gitignored)\n└── backups/                      # rolling local snapshots (gitignored)\n```\n\n---\n\n## Requirements\n\n- `bash` 4+\n- [`rclone`](https://rclone.org/install/) **v1.65+** (older builds lack some `bisync` flags used here)\n- A [Cloudflare R2](https://developers.cloudflare.com/r2/) bucket and an S3-compatible API token scoped to it\n- A passphrase (and optional salt) for the `crypt` remote — **lose this and the data is unrecoverable**\n\n---\n\n## Quick start\n\n```bash\n# 1. Clone the repo somewhere\ngit clone https://github.com/jiangsier-xyz/jiangsier-skill-sync-notes.git\ncd jiangsier-skill-sync-notes\n\n# 2. Tell the script where your notes live\nexport CLOUD_NOTES_PATH=/absolute/path/to/your/vault\n\n# 3. Run the wizard — fills in rclone.conf and .env (chmod 600)\n./scripts/setup.sh\n\n# 4. Sanity check\n./scripts/sync.sh status\n\n# 5. First-time baseline (must do this once before bisync will run)\n./scripts/sync.sh init\n\n# 6. Routine"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn73sdyhq2p2h53qne8t5nvacs86x2bc\",\n  \"slug\": \"sync-notes\",\n  \"version\": \"1.0.1\",\n  \"publishedAt\": 1779502298242\n}"},{"path":"README.zh-CN.md","content":"# sync-notes\n\n> 在本地笔记目录(Obsidian / 纯 Markdown / 任意文件夹)与 **Cloudflare R2** 之间做端到端加密的双向同步,底层用 [`rclone`](https://rclone.org/) 的 `bisync` + `crypt` 远端实现。\n\n这是一个 [OpenClaw](https://openclaw.ai) 技能(skill),但核心其实是两个 bash 脚本,可以脱离 OpenClaw 独立使用。\n\n[English](./README.md)\n\n---\n\n## 为什么用它?\n\n- 🔒 **零信任存储**: 笔记在客户端用 `rclone crypt` 加密,Cloudflare 只看得到密文 blob 和加密过的文件名。\n- 🔁 **真双向同步**: `rclone bisync` 会双向传播新增、修改、删除,两边都改了的话会留下冲突标记,不会静默覆盖。\n- 📱 **手机可用**: crypt 格式与 Obsidian 插件 [Remotely Save](https://github.com/remotely-save/remotely-save) 兼容,手机端不用塞 rclone 配置就能同步同一个 bucket。\n- 🧰 **零依赖、零守护进程**: 所有路径都从脚本位置推导,只需要 `bash` + `rclone`。\n- 🛟 **写入前先备份**: 任何写本地的命令都会先把当前 vault 镜像到 `backups/latest/`。\n\n---\n\n## 目录结构\n\n```\nsync-notes/\n├── SKILL.md                      # OpenClaw 技能清单 + 设计文档\n├── README.md / README.zh-CN.md   # 本文件\n├── config/\n│   ├── rclone.conf.example       # rclone 远端模板\n│   ├── rclone.conf               # 真实配置 (gitignored, chmod 600)\n│   ├── .env.example              # 非 rclone 设置模板\n│   ├── .env                      # 真实设置   (gitignored, chmod 600)\n│   └── filter.txt                # rclone 过滤规则(默认排除 .obsidian/、.DS_Store 等)\n├── scripts/\n│   ├── setup.sh                  # 交互式配置向导\n│   └── sync.sh                   # 主入口 / 调度器\n├── state/                        # rclone bisync 工作目录 (gitignored)\n├── logs/                         # 每次运行和总日志        (gitignored)\n└── backups/                      # 滚动本地快照            (gitignored)\n```\n\n---\n\n## 依赖\n\n- `bash` 4+\n- [`rclone`](https://rclone.org/install/) **v1.65+**(更早版本不支持脚本里用到的部分 `bisync` 选项)\n- 一个 [Cloudflare R2](https://developers.cloudflare.com/r2/) bucket 以及一个限定权限的 S3 兼容 token\n- crypt 远端的密码(以及可选的 salt)—— **丢了就不可恢复**,务必离线备份一份\n\n---\n\n## 快速开始\n\n```bash\n# 1. 克隆仓库\ngit clone https://github.com/jiangsier-xyz/jiangsier-skill-sync-notes.git\ncd jiangsier-skill-sync-notes\n\n# 2. 告诉脚本你的笔记目录在哪里\nexport CLOUD_NOTES_PATH=/absolute/path/to/your/vault\n\n# 3. 跑配置向导,自动写好 rclone.conf 和 .env(权限 600)\n./scripts/setup.sh\n\n# 4. 健康检查\n./scripts/sync.sh status\n\n# 5. 第一次必须先建立基线\n./scripts/sync.sh init\n\n# 6. 之后日常双向同步\n./scripts/sync.sh\n```\n\n> 💡 **建议把 `CLOUD_NOTES_PATH` 写进你的 shell rc(`.zshrc` / `.bashrc`),省心。**\n\n---\n\n## 子命令\n\n| 命令 | 行为 |\n|---|---|\n| `sync.sh setup` | 跑配置向导。幂等,覆盖前会问一次。 |\n| `sync.sh init [--force]` | 首次建立 bisync 基线(`rclone bisync --resync`)。已有基线时拒绝运行,除非加 `--force`。 |\n| `sync.sh bisync`(默认) | 双向同步,带冲突检测;运行前先做本地备份。 |\n| `sync.sh download <glob> [--dry-run]` | 从远端把匹配 `<glob>` 的文件拉下来;运行前先做本地备份。 |\n| `sync.sh upload <glob> [--dry-run]` | 把本地匹配 `<glob>` 的文件推上远端。 |\n| `sync.sh status` | 打印配置状态、上次同步信息、冲突文件、备份状态。 |\n\n`<glob>` 会传给 `rclone --include`,作用在**解密后的明文路径**上。光秃秃的关键词(如 `welcome`)会自动展开成 `**welcome**`;已经带通配符的(`ideas/**`、`*.md`)原样透传。\n\n---\n\n## 冲突处理\n\n`bisync` 运行时使用 `--conflict-suffix conflict1,conflict2`。每次运行结束脚本会扫描 vault,如果发现 `*.conflict1*` / `*.conflict2*` 文件,**会以非零退出码报错并列出冲突清单 —— 由人(或上层 LLM)手动决定保留哪一边再继续同步**,脚本不会静默自动合并。\n\n---\n\n## 配置文件\n\n### `config/rclone.conf`\n\n两个远端:\n\n```ini\n[r2-raw]\ntype = s3\nprovider = Cloudflare\naccess_key_id = <ACCESS_KEY_ID>\nsecret_access"},{"path":"skill-card.md","content":"## Description:\n\nSync the local Obsidian vault with a Cloudflare R2 bucket using rclone (S3 + crypt). Triggered by `/sync-notes`. Defaults to bidirectional sync; understands natural-language flags for partial download/upload by glob.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[jiangsier-xyz](https://clawhub.ai/user/jiangsier-xyz)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and note-taking users use Sync Notes to configure and run encrypted bidirectional sync between a local Obsidian or Markdown vault and a Cloudflare R2 bucket. The agent translates `/sync-notes` requests into setup, status, baseline, full sync, upload, or download commands.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Bidirectional sync can change or delete notes on both local and remote storage.\n\nMitigation: Run status and dry-run checks before first or broad syncs, resolve conflicts manually, and keep an independent backup of the vault.\n\nRisk: R2 credentials and rclone crypt secrets are stored locally in configuration files.\n\nMitigation: Keep configuration files private with restrictive permissions, never expose secrets in chat, and back up the crypt passphrase offline.\n\nRisk: Crafted shell configuration values or unrestricted extra rclone flags could cause unintended command execution or unsafe sync behavior.\n\nMitigation: Review generated configuration before syncing, validate setup inputs, and remove or allowlist extra rclone flags before deployment.\n\n## Reference(s):\n\n- [Sync Notes ClawHub Page](https://clawhub.ai/jiangsier-xyz/skills/sync-notes)\n- [rclone Documentation](https://rclone.org/)\n- [rclone Install Documentation](https://rclone.org/install/)\n- [rclone Filtering Documentation](https://rclone.org/filtering/)\n- [Cloudflare R2 Documentation](https://developers.cloudflare.com/r2/)\n- [Remotely Save Obsidian Plugin](https://github.com/remotely-save/remotely-save)\n- [Default rclone Filter Rules](config/filter.txt)\n\n## Skill Output:\n\n**Output Type(s):** [shell commands, configuration, guidance]\n\n**Output Format:** [Markdown with inline shell commands and concise status or conflict summaries]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [May create local rclone configuration, logs, state, and backup files when its shell scripts are executed.]\n\n## Skill Version(s):\n\n1.0.1 (source: frontmatter and server release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment."}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"Sync the local Obsidian vault with a Cloudflare R2 bucket using rclone (S3 + crypt). Triggered by `/sync-notes`. Defaults to bidirectional sync; understands... Skill: Sync Notes Owner: jiangsier-xyz Summary: Sync the local Obsidian vault with a Cloudflare R2 bucket using rclone (S3 + crypt). Triggered by /sync-notes. Defaults to bidirectional sync; understands... Tags: latest:1.0.1 Version history: v1.0.1 | 2026-05-23T02:11:38.242Z | auto - Removed LICENSE file for streamlined distribution. - Updated setup and sync scripts (setup.sh, sync.sh)—maintenance, internal improveme","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":1515,"uniquenessScore":45,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-12T00:24:42.787Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-12T00:24:42.787Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-14T06:39:03.535Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}