diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/docs/avatars.md b/docs/avatars.md new file mode 100644 index 0000000..9f94f57 --- /dev/null +++ b/docs/avatars.md @@ -0,0 +1,96 @@ +# Avatars + +Every person can carry a single avatar image, stored as a real file next to +their `person.md`. Avatars are deliberately first-class — they are how +exported vCards stay recognizable in Apple Contacts, Google Contacts, and +phone dial screens. + +On disk: + +```text +people/sally-o-malley/ + person.md + avatars/ + avatar.jpg # or .png +``` + +The `person.md` frontmatter records the avatar's MIME type, sha256, source +(`manual`, `apple`, `google`), and updated time. The bytes are kept on disk; +the markdown only points at them. + +## Set a manual avatar + +```bash +clawdex person avatar set sally ~/Pictures/sally.jpg +clawdex person avatar set sally ~/Pictures/sally.png --dry-run +``` + +`--dry-run` inspects the source image (MIME, SHA256) and previews the +avatar metadata that would be written, without touching the data repo. + +Manual avatars are sticky: subsequent `clawdex import apple` and +`clawdex import google` runs will **never overwrite a manual avatar**. To +let imports manage the avatar again, clear it first. + +## Show + +```bash +clawdex person avatar show sally +clawdex person avatar show sally --path +clawdex person avatar show sally --json +``` + +`--path` prints the absolute path to the avatar file and nothing else, so +you can pipe it into a viewer: + +```bash +open "$(clawdex person avatar show sally --path)" +``` + +## Clear + +```bash +clawdex person avatar clear sally +``` + +Clears the avatar metadata from `person.md`. The file under `avatars/` is +left alone in case you want to recover it; remove it manually if you're +sure. + +## Avatars from imports + +Avatars are opt-in on imports — the `--avatars` flag is required: + +```bash +clawdex import apple --avatars +clawdex import google --account you@gmail.com --avatars +``` + +- **Apple.** Reads the thumbnail bytes that + [`Contacts.framework`](https://developer.apple.com/documentation/contacts) + hands out. macOS-only. +- **Google.** Calls `gog contacts raw --person-fields photos`, picks the + selected photo URL, fetches the bytes through + [`gog`](https://github.com/steipete/gogcli), and stores them locally. + Only metadata (URL, MIME, SHA256) is written into `person.md`. + +In both cases the bytes live on your machine. Clawdex never silently +re-fetches them on later runs. + +## vCard export + +Avatars are embedded into vCards as base64 `PHOTO;ENCODING=b` payloads when +you opt in: + +```bash +clawdex export vcard --all --include-avatars -o contacts.vcf +``` + +Without `--include-avatars`, the export is text-only — handy when you want a +small file or are emailing the vCard to someone. + +## Related pages + +- [People](people.md), [Imports](imports.md), [vCard Export](vcard-export.md) +- [Doctor](doctor.md) — `clawdex doctor` reports stale avatar metadata + (file gone, hash mismatch) and `--repair` rewrites it from disk. diff --git a/docs/config.md b/docs/config.md new file mode 100644 index 0000000..b80c2fc --- /dev/null +++ b/docs/config.md @@ -0,0 +1,104 @@ +# Config + +Clawdex has two config files, on purpose: + +| File | Scope | What lives here | +|-------------------------------|---------------|----------------------------------------------| +| `~/.clawdex/config.toml` | User-level | Default repo path, default Google account. | +| `/clawdex.toml` | Repo-local | Git remote, branch, repair behavior. | + +The user-level file follows you across data repos. The repo-local file +follows the data — it ships with the repo when you clone it on a second +machine. + +## User-level config + +Default location: `~/.clawdex/config.toml`. Override with `--config PATH` +or `CLAWDEX_CONFIG=PATH`. + +```toml +repo_path = "/Users/you/.clawdex/contacts" + +[google] +default_account = "you@gmail.com" +``` + +## Repo-local config + +Default location: `/clawdex.toml`. Created by `clawdex init`. + +```toml +[git] +remote = "https://github.com/you/backup-clawdex.git" +branch = "main" + +[repair] +backup_before_repair = true +auto_repair = false +``` + +## `clawdex config` + +```bash +clawdex config # default subcommand: show +clawdex config show +clawdex config show --json +clawdex config set repo_path ~/.clawdex/contacts +clawdex config set git.remote https://github.com/you/backup-clawdex.git +clawdex config set git.branch main +clawdex config set google.default_account you@gmail.com +``` + +`clawdex config set` writes the user-level config file. `--dry-run` +echoes the resolved config without writing. + +Supported keys: + +- `repo_path` +- `git.remote` +- `git.branch` +- `google.default_account` + +Other keys are edited by hand in the appropriate TOML file. + +## Per-run overrides + +Every config value can be overridden for a single command: + +```bash +clawdex --config /tmp/alt.toml person list +clawdex --repo /tmp/scratch person add "Test Person" +CLAWDEX_REPO=/tmp/scratch clawdex import apple --dry-run +``` + +These don't persist anything — they're for one-off runs, CI jobs, and +testing. + +## Global flags + +These apply to every subcommand: + +| Flag | Env | Effect | +|------------------|--------------------|---------------------------------------------------------| +| `--config PATH` | `CLAWDEX_CONFIG` | Override the user-level config path. | +| `--repo DIR` | `CLAWDEX_REPO` | Override the contacts data repo for this run. | +| `--json` | | Stable JSON envelope on stdout. | +| `--plain` | | TSV on stdout (script-friendly). | +| `--dry-run`, `-n`| | Preview without writing. | +| `--no-input` | | Never prompt; useful in CI. | +| `--verbose`, `-v`| | Verbose diagnostics on stderr. | +| `--version` | | Print version and exit. | + +## Environment + +- `CLAWDEX_CONFIG` — user-level config path. +- `CLAWDEX_REPO` — contacts data repo path. +- `EDITOR` — used by `clawdex person edit`. Falls back to `code` (Visual + Studio Code) when unset. + +## Related pages + +- [Quickstart](quickstart.md) +- [Markdown Storage](markdown-storage.md) +- [Git Sync](git-sync.md) +- [Doctor](doctor.md) diff --git a/docs/doctor.md b/docs/doctor.md new file mode 100644 index 0000000..e4bd431 --- /dev/null +++ b/docs/doctor.md @@ -0,0 +1,110 @@ +# Doctor + +`clawdex doctor` is a one-shot health check for your data repo. It reports +counts and surfaces problems; with `--repair`, it fixes the ones it knows +how to fix. + +```bash +clawdex doctor +clawdex doctor --json +clawdex doctor --repair --dry-run +clawdex doctor --repair +``` + +## What it reports + +```text +config_path: /Users/you/.clawdex/config.toml +repo_path: /Users/you/.clawdex/contacts +remote: https://github.com/you/backup-clawdex.git +people: 412 +git_dirty: true +avatar_problems: 3 +``` + +Fields: + +- `config_path` — the user-level config clawdex loaded. +- `repo_path` — which contacts data repo is in use (after `--repo`, + `CLAWDEX_REPO`, and config resolution). +- `remote`, `people`, `git_dirty` — sanity numbers. +- `avatar_problems` — count of person records whose avatar metadata + doesn't match what's on disk (file gone, sha256 mismatch). Only printed + when non-zero. + +## What `--repair` repairs + +Two classes of problem: + +### 1. Damaged frontmatter + +If `person.md` or a note's YAML frontmatter is malformed — a stray quote, +a truncated block, a dangling key — clawdex's strict parse fails. The +repair pass: + +1. Salvages known scalar keys: `id`, `name`, `created_at`, and the note + fields (`kind`, `source`, `occurred_at`, `topics`). +2. Infers a missing `id` from the file path and a missing + `created_at`/`updated_at` from the file mtime. +3. Preserves the Markdown body verbatim. +4. Copies the original damaged file under `.clawdex/repairs/` so nothing + is lost. +5. Appends the unsalvageable scrap to the body under a `## Recovered + metadata` heading, so you can finish the cleanup by hand. + +### 2. Stale avatar metadata + +If `person.md` says there's an avatar but the file is missing, the SHA256 +no longer matches, or the MIME type is wrong, repair will: + +- Drop the metadata if the file is gone. +- Recompute MIME and SHA256 if the file is present. +- Leave the bytes themselves untouched. + +Avatar bytes are never *recovered* by repair — only the metadata is +synced to whatever's actually on disk. Use +[`clawdex person avatar set`](avatars.md) to put bytes back. + +## Dry-run + +```bash +clawdex doctor --repair --dry-run +``` + +Walks the repo and reports counts without writing anything. The output +includes: + +```text +repaired: 4 +avatar_repaired: 2 +dry_run: true +``` + +Treat the counts as the worst case — once you re-run without `--dry-run`, +the numbers should drop to zero on the next clean `clawdex doctor`. + +## When to run it + +- After a big import (`apple`, `google`, `birdclaw`, `discrawl`). +- After resolving Git merge conflicts in the data repo. +- After a hand-edit binge. +- As the last step before `clawdex git commit && clawdex git push`. + +`clawdex doctor` is read-only. `clawdex doctor --repair` is the only +variant that writes; it always backs up the original file first when +`repair.backup_before_repair = true` (the default). + +## Exit codes + +- `0` — everything looked fine. +- `1` — runtime error (bad path, IO error, malformed config). +- `2` — usage error (bad flag combination). + +`avatar_problems` and `git_dirty` are diagnostics, not errors. They do not +change the exit code on their own. + +## Related pages + +- [Markdown Storage](markdown-storage.md) +- [People](people.md), [Notes](notes.md), [Avatars](avatars.md) +- [Config](config.md) diff --git a/docs/git-sync.md b/docs/git-sync.md new file mode 100644 index 0000000..c2d19f8 --- /dev/null +++ b/docs/git-sync.md @@ -0,0 +1,120 @@ +# Git Sync + +Your contacts data repo is just a Git repo. Clawdex doesn't run a sync +daemon, doesn't talk to a clawdex.sh server, and doesn't store anything +about you remotely. Backup and multi-device sync are 100% Git. + +The default suggested remote is a *private* GitHub repo: + +```text +https://github.com//backup-clawdex.git +``` + +You set this once in [Quickstart](quickstart.md) with: + +```bash +clawdex config set git.remote https://github.com//backup-clawdex.git +``` + +## `clawdex git` commands + +All four commands run *inside the data repo*, not the clawdex source repo. +They're thin wrappers around `git` so you don't have to `cd` constantly. + +### Status + +```bash +clawdex git status +``` + +A wrapper for `git -C status --short --branch`. Same output, same +exit codes. Use this before committing to see what an import or edit +changed. + +### Commit + +```bash +clawdex git commit +clawdex git commit -m "sync: import google contacts" +``` + +Stages everything modified under the data repo and commits with the +provided message. The default message is +`sync: update clawdex contacts`. Returns whether a commit was actually +created — if there were no changes, `committed: false`. + +### Pull + +```bash +clawdex git pull +``` + +Pulls from the configured remote on the configured branch. Resolve +conflicts the way you'd resolve them in any other repo. + +### Push + +```bash +clawdex git push +``` + +Pushes to the configured remote on the configured branch. The first push +on a fresh repo also sets the upstream. + +## Choosing a remote + +Anything Git-hostable works: + +- **Private GitHub repo** — recommended; integrates with GitHub Mobile if + you want to read your notes on a phone. +- **Self-hosted** — Forgejo, Gitea, sourcehut, a bare repo over SSH. +- **Local-only** — leave `git.remote` unset and clawdex won't push. The + repo is still version-controlled locally. + +Whatever you pick, **the remote should be private**. The data is plain +markdown — names, phone numbers, emails, conversation snippets. Treat it +like a journal. + +## Multi-device + +The flow on a second machine is the same as on the first: + +```bash +brew install steipete/tap/clawdex +git clone https://github.com//backup-clawdex.git ~/.clawdex/contacts +clawdex config set repo_path ~/.clawdex/contacts +clawdex config set git.remote https://github.com//backup-clawdex.git +clawdex git pull +clawdex doctor +``` + +Day-to-day: + +```bash +clawdex git pull +# ... edit, import, add notes ... +clawdex git commit -m "sync: ..." +clawdex git push +``` + +Conflicts are normal Git conflicts. Markdown frontmatter merges +predictably; if a frontmatter merge ends up malformed, run +[`clawdex doctor --repair`](doctor.md) to salvage it. + +## Encryption + +vanilla `clawdex git` does **not** encrypt the repo. The data lives as +plaintext markdown both locally and on the remote. If you need encryption +at rest: + +- Use a private remote you trust. +- Or layer `git-crypt` / `age` over the data repo manually. +- Or back up encrypted snapshots out-of-band, alongside Git. + +A built-in encrypted backup mode (à la `gog backup`) is on the roadmap +but not shipped. + +## Related pages + +- [Quickstart](quickstart.md), [Config](config.md) +- [Markdown Storage](markdown-storage.md) diff --git a/docs/imports.md b/docs/imports.md new file mode 100644 index 0000000..081170b --- /dev/null +++ b/docs/imports.md @@ -0,0 +1,128 @@ +# Imports + +Imports project an external contact graph into the same markdown shape +clawdex uses everywhere else. They are **local-only**: every import only +writes to your data repo. No address book on Apple, Google, X, or Discord +is mutated by any `clawdex import` subcommand. + +If you want to push changes back into Apple Contacts or Google Contacts, see +[Sync](#sync-preview-only) below — it's preview-only today. + +## What gets written + +Each import returns a list of `ImportChange` rows, printed as TSV: + +```text +add Sally O'Malley sally-o-malley +update Bo Burnham bo-burnham +unchanged Frank Booth frank-booth +``` + +Combine with `--dry-run` to preview without writing: + +```bash +clawdex import apple --dry-run +clawdex import google --account you@gmail.com --dry-run +``` + +## Apple Contacts + +```bash +clawdex import apple --dry-run +clawdex import apple +clawdex import apple --avatars +clawdex import apple --input ~/Desktop/contacts.json +``` + +- Default source is the macOS Contacts database via `Contacts.framework`. + The first run prompts for *Contacts* access in System Settings. +- `--input PATH` reads JSON or NDJSON instead — useful on Linux, in CI, + or when round-tripping a snapshot. +- `--avatars` imports thumbnail bytes. Without it, only structured fields + are imported. + +Manual avatars set with [`clawdex person avatar set`](avatars.md) are never +overwritten. Tags, notes, and any custom frontmatter you've added by hand +are preserved. + +## Google Contacts + +```bash +clawdex import google --account you@gmail.com --dry-run +clawdex import google --account you@gmail.com +clawdex import google --account you@gmail.com --avatars +``` + +The Google adapter shells out to [`gog`](https://github.com/steipete/gogcli), +the local-first Google Workspace CLI. You need to be authenticated there +first: + +```bash +gog auth credentials ~/Downloads/client_secret_*.json +gog auth add you@gmail.com --services contacts +``` + +If `--account` is omitted, clawdex falls back to `google.default_account` +from your config — set it once with: + +```bash +clawdex config set google.default_account you@gmail.com +``` + +`--avatars` fetches photo bytes through `gog contacts raw --person-fields photos` +and stores them locally. + +## Birdclaw — X / Twitter DMs + +```bash +clawdex import birdclaw --dry-run +clawdex import birdclaw --min-messages 4 +clawdex import birdclaw --db ~/.birdclaw/birdclaw.sqlite +``` + +Reads from your local [birdclaw](https://github.com/steipete/birdclaw) +SQLite archive. For each DM thread above the `--min-messages` threshold, +clawdex creates or updates a person, stores the X handle as a stable +pointer under `accounts.x`, and adds a source-specific tag. + +The default DB path is `~/.birdclaw/birdclaw.sqlite`. Threads with fewer +than `--min-messages` messages are skipped — most of those are one-shot +spam or intros that died. + +## Discrawl — Discord DMs + +```bash +clawdex import discrawl --dry-run +clawdex import discrawl --min-messages 4 +clawdex import discrawl --db ~/.discrawl/discrawl.db +``` + +Same shape as birdclaw, but reads from +[discrawl](https://github.com/steipete/discrawl)'s SQLite cache. Discord +handles land under `accounts.discord`. + +## Sync (preview-only) + +```bash +clawdex sync apple +clawdex sync google --account you@gmail.com +``` + +These commands exist as placeholders. They report: + +```text +status: remote writes not implemented yet; use import apple for local +markdown projection +``` + +Two-way sync requires a conflict report you can read before anything is +written remotely; that report doesn't exist yet, so the writes don't +either. Until it lands, treat clawdex as a one-way mirror: imports come +in, [vCard export](vcard-export.md) goes out. + +## Related pages + +- [People](people.md), [Avatars](avatars.md) +- [Markdown Storage](markdown-storage.md) — the shape imports project into +- [Git Sync](git-sync.md) — committing the import diff +- [Config](config.md) — `google.default_account`, repo path diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..687f8a6 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,59 @@ +# Clawdex + +`clawdex` is a local-first, markdown-first personal contact index. The CLI +lives in this repo. Your contacts live in a separate private Git-backed +markdown repository that you own. + +The whole thing is a Rolodex you can `grep` — every person is a folder, every +note is a timestamped markdown file, every change is a commit. + +## Try it + +After [installing](install.md) and [pointing clawdex at a data repo](quickstart.md): + +```bash +clawdex person add "Sally O'Malley" --email sally@example.com --tag friend +clawdex note add sally --kind dm --source whatsapp --text "Follow up about dinner" +clawdex timeline sally +clawdex search dinner +clawdex export vcard --all --include-avatars -o contacts.vcf +``` + +`--json` produces a stable JSON envelope on stdout. `--plain` produces TSV. +Human messages go to stderr, so pipes stay parseable. + +## What clawdex does + +- **Markdown is canonical.** People are folders under `people/`. Notes are + timestamped files. Indexes under `index/` are derived and rebuildable. +- **Git is the sync layer.** No proprietary daemon, no opaque database. + `clawdex git push` pushes to your private remote. +- **Imports are local-only.** Apple, Google, Birdclaw (X DMs), and Discrawl + (Discord DMs) all project into the same markdown shape. Address-book writes + are still preview-only — see [Imports](imports.md). +- **Avatars are real files.** Stored next to each person; `vcard` export can + embed them; manual avatars are never overwritten by imports. +- **Notes are local-only.** They are never written to Apple or Google. + +## Pick your path + +- **Trying it.** [Install](install.md) → [Quickstart](quickstart.md). Five + minutes from `brew install` to your first commit. +- **Importing the network you already have.** [Imports](imports.md) covers + Apple Contacts, Google Contacts, X DMs (birdclaw), and Discord DMs + (discrawl). +- **Daily use.** [People](people.md), [Notes](notes.md), + [Timeline](timeline.md), [Search](search.md). +- **Sharing back.** [vCard Export](vcard-export.md) for round-tripping into + Apple/Google/anything-else, [Git Sync](git-sync.md) for the private backup + remote. +- **Repair and storage.** [Markdown Storage](markdown-storage.md), + [Doctor](doctor.md), [Config](config.md). + +## Project + +Source: [openclaw/clawdex](https://github.com/openclaw/clawdex). +[Changelog](https://github.com/openclaw/clawdex/blob/main/CHANGELOG.md) tracks +what shipped recently. Released under the +[MIT license](https://github.com/openclaw/clawdex/blob/main/LICENSE). Not +affiliated with Apple, Google, X, or Discord. diff --git a/docs/install.md b/docs/install.md new file mode 100644 index 0000000..a7760e7 --- /dev/null +++ b/docs/install.md @@ -0,0 +1,80 @@ +# Install + +`clawdex` ships as a single Go binary. The visible version is injected at +build time: release builds use the tag, local builds use `git describe`. + +## Homebrew (macOS, Linux) + +```bash +brew install steipete/tap/clawdex +clawdex --version +``` + +The Homebrew formula lives in `steipete/homebrew-tap` and is updated by the +clawdex release workflow after each tagged release. + +## Go install + +```bash +go install github.com/openclaw/clawdex/cmd/clawdex@latest +clawdex --version +``` + +Source builds require the Go version declared in +[`go.mod`](https://github.com/openclaw/clawdex/blob/main/go.mod). + +## Build from source + +```bash +git clone https://github.com/openclaw/clawdex.git +cd clawdex +go build -o ./bin/clawdex ./cmd/clawdex +./bin/clawdex --version +``` + +## GitHub release archives + +Release assets are published by GoReleaser: + +- `clawdex__darwin_amd64.tar.gz` +- `clawdex__darwin_arm64.tar.gz` +- `clawdex__linux_amd64.tar.gz` +- `clawdex__linux_arm64.tar.gz` +- `clawdex__windows_amd64.zip` +- `checksums.txt` + +Browse the [releases page](https://github.com/openclaw/clawdex/releases) for +the latest tag. + +## Platform notes + +- **macOS** is the most exercised target. `clawdex import apple` reads + Contacts via `Contacts.framework`, so the binary must be allowed in + *Settings → Privacy & Security → Contacts* the first time you run it. +- **Linux** builds support markdown editing, notes, search, Git, Google + imports through `gog`, and vCard export. Apple direct import is macOS-only. +- **Windows** binaries are produced but lightly tested; the Git layer assumes + a working `git` on `PATH`. + +## Verify the install + +```bash +clawdex --version +clawdex --help +clawdex doctor +``` + +After `clawdex init` (see [Quickstart](quickstart.md)), `clawdex doctor` +prints a one-shot health summary: config path, repo path, remote, person +count, and any avatar problems. + +## Updating + +- **Homebrew:** `brew upgrade clawdex`. +- **Go install:** rerun `go install github.com/openclaw/clawdex/cmd/clawdex@latest`. +- **Release archives:** download the new tarball and replace the binary. +- **Source:** `git pull && go build -o ./bin/clawdex ./cmd/clawdex`. + +The on-disk markdown layout is forward-compatible across point releases. A +breaking layout change would ship a `clawdex doctor --repair` migration — +see [Doctor](doctor.md). diff --git a/docs/markdown-storage.md b/docs/markdown-storage.md new file mode 100644 index 0000000..353e928 --- /dev/null +++ b/docs/markdown-storage.md @@ -0,0 +1,140 @@ +# Markdown Storage + +Clawdex stores everything as files on disk. There is no database, no opaque +binary format, no migration step that locks you out. If clawdex disappeared +tomorrow, you would still have your data, in plaintext, in a Git repo. + +## Layout + +```text +/ + clawdex.toml # repo-local settings + people/ + sally-o-malley/ + person.md + avatars/ + avatar.jpg + notes/ + 2026-05-08T09-15-00Z-whatsapp.md + 2026-05-09T18-02-00Z-imessage.md + attachments/ + ... # opt-in; not yet wired into the CLI + index/ + emails.json + phones.json + handles.json + .clawdex/ + repairs/ # backups written by `doctor --repair` +``` + +Slugs are derived from the person's name and are stable: renaming a person +in `person.md` updates the display name but keeps the folder path. To +rename the slug itself, move the folder by hand and re-run +[`clawdex doctor`](doctor.md). + +## `person.md` + +```markdown +--- +id: sally-o-malley +name: Sally O'Malley +emails: + - value: sally@example.com + kind: work +phones: + - value: "+15550100" + kind: mobile +tags: [friend, dinner-club] +accounts: + x: { handle: sally } + discord: { id: "234234234234234234", username: "sally" } +created_at: 2026-05-08T09:15:00Z +updated_at: 2026-05-08T09:15:00Z +avatar: + path: avatars/avatar.jpg + mime: image/jpeg + sha256: "..." + source: manual +--- + +# Sally O'Malley + +Met at the dinner club in 2024. Loves Negronis. +``` + +YAML frontmatter is parsed strictly first; if that fails, clawdex falls +back to a best-effort scalar salvage and copies the original file under +`.clawdex/repairs/` before writing anything new. See +[Doctor](doctor.md). + +The Markdown body is preserved verbatim across reads, edits, and repair. +Your hand-written prose is safe. + +## Note files + +Notes are timestamped markdown files under `notes/`: + +```markdown +--- +id: 2026-05-08T09-15-00Z-whatsapp +kind: dm +source: whatsapp +occurred_at: 2026-05-08T09:15:00Z +created_at: 2026-05-08T09:15:00Z +topics: [dinner, logistics] +--- + +Follow up about dinner next Thursday. +``` + +The filename encodes `occurred_at` as `2006-01-02T15-04-05Z` plus the +source. Sorting by filename and sorting by `occurred_at` produce the same +order, which is intentional — `ls notes/` is a serviceable timeline. + +See [Notes](notes.md) and [Timeline](timeline.md). + +## Index files + +`index/*.json` are derived caches: + +- `emails.json` — email → person ID +- `phones.json` — normalized phone → person ID +- `handles.json` — service handle (X, Discord, …) → person ID + +Clawdex rebuilds these on read whenever they're stale. They are safe to +delete — the next command will regenerate them. They are *not* the source +of truth: markdown is. + +## clawdex.toml + +A small repo-local config file written by `clawdex init`: + +```toml +[git] +remote = "https://github.com/you/backup-clawdex.git" +branch = "main" + +[repair] +backup_before_repair = true +auto_repair = false +``` + +This is *separate* from the user-level config at `~/.clawdex/config.toml`, +which holds your default repo path, default Google account, and editor +preferences. See [Config](config.md). + +## Why markdown + +- **Diffable.** A person rename, a tag change, a note edit — they show up + as readable diffs in `git log`. +- **Editable anywhere.** Your editor, GitHub's web UI, mobile markdown + editors, plain `vim` over SSH. +- **Greppable.** `rg`, `awk`, and `sed` work on the data repo without + needing clawdex on the host. +- **Future-proof.** Plain text outlives every CLI built on top of it. + +## Related pages + +- [People](people.md), [Notes](notes.md), [Avatars](avatars.md) +- [Doctor](doctor.md), [Config](config.md) +- [Git Sync](git-sync.md) diff --git a/docs/notes.md b/docs/notes.md new file mode 100644 index 0000000..54ef346 --- /dev/null +++ b/docs/notes.md @@ -0,0 +1,84 @@ +# Notes + +A *note* is a timestamped event attached to a person. Notes capture what +contacts can't: what you talked about, when, and where it happened. + +Notes live under `people//notes/`. Each note is a single markdown file +named `-.md`, e.g. +`2026-05-08T09-15-00Z-whatsapp.md`. + +**Notes are local-only.** They are never written to Apple Contacts, Google +Contacts, or anywhere else. They are pushed only to your private backup +remote when you run [`clawdex git push`](git-sync.md). + +## Add + +```bash +clawdex note add sally \ + --kind dm \ + --source whatsapp \ + --text "Follow up about dinner next Thursday" \ + --topic dinner --topic logistics +``` + +Flags: + +- `--kind` *(required)* — type of interaction: `dm`, `call`, `meeting`, + `email`, `event`, etc. Free-form; clawdex doesn't enforce a vocabulary. +- `--source` *(required)* — where it happened: `whatsapp`, `imessage`, + `discord`, `x`, `email`, `inperson`, etc. +- `--text` *(required)* — note body. Multiline is fine; quote it. +- `--occurred-at` — ISO 8601 (`2026-05-08T09:15:00Z`), `2026-05-08 09:15`, + or `2026-05-08`. Defaults to *now*. +- `--topic` — repeatable, becomes the `topics:` frontmatter array. + +`--dry-run` shows the resolved note as JSON without writing. + +## List + +```bash +clawdex note list sally +clawdex note list sally --json +``` + +The default output is a TSV of `occurred-atkindsourcebody`. +Note bodies are flattened to a single line for TSV; use `--json` to get the +full body. + +## Note file format + +```markdown +--- +id: 2026-05-08T09-15-00Z-whatsapp +kind: dm +source: whatsapp +occurred_at: 2026-05-08T09:15:00Z +created_at: 2026-05-08T09:15:00Z +topics: [dinner, logistics] +--- + +Follow up about dinner next Thursday. +``` + +You can edit notes directly with `$EDITOR` — clawdex reads them back. If +the frontmatter gets damaged, [`clawdex doctor --repair`](doctor.md) +salvages known fields and preserves the body verbatim under a *Recovered +metadata* heading. + +## Conventions that pay off + +- **Use stable `--source` values.** `whatsapp`, not `WhatsApp`. Search and + imports both rely on lowercase source slugs. +- **Tag topics, not relationships.** Topics are about the *content*; tags + on the person are about the *role*. A `dinner` topic is fine; a + `friend` topic is probably a person tag instead. +- **Don't store secrets in notes.** They sync to your private backup repo, + but they're still in plain markdown on disk. Treat them like a Git repo, + not a vault. + +## Related pages + +- [People](people.md), [Timeline](timeline.md), [Search](search.md) +- [Imports](imports.md) — birdclaw and discrawl create one note per imported + DM thread head. +- [Markdown Storage](markdown-storage.md) diff --git a/docs/people.md b/docs/people.md new file mode 100644 index 0000000..411111b --- /dev/null +++ b/docs/people.md @@ -0,0 +1,101 @@ +# People + +A *person* is the unit of clawdex. On disk, each person is a folder under +`people/` containing a `person.md` file, an optional `avatars/` folder, and +an optional `notes/` folder. The folder slug is derived from the person's +name — for example `Sally O'Malley` becomes `sally-o-malley`. + +## Add + +```bash +clawdex person add "Sally O'Malley" \ + --email sally@example.com -e sally.alt@example.com \ + --phone "+1 555 0100" \ + --tag friend --tag dinner-club +``` + +Flags: + +- `--email`, `-e` — repeatable +- `--phone`, `-p` — repeatable +- `--tag`, `-t` — repeatable + +`--dry-run` previews the planned slug without writing anything. + +## List + +```bash +clawdex person list +clawdex person list --query sally +clawdex person list --json | jq '.[].name' +``` + +`--query` filters by substring match against name, ID, and tags. The default +output is a TSV of `idnamefirst-email`. + +## Show + +```bash +clawdex person show sally +clawdex person show sally@example.com +clawdex person show "+15550100" +clawdex person show sally-o-malley # exact ID +``` + +`show` accepts an ID, a substring of the name, an email, or a phone number. +The first unambiguous match wins. If multiple people share a key, the +command errors and asks you to be more specific. + +## Edit + +```bash +clawdex person edit sally +EDITOR=nvim clawdex person edit sally +``` + +Opens the person's `person.md` in `$EDITOR`, falling back to `code` (Visual +Studio Code) if `EDITOR` is unset. Clawdex re-reads the file on the next +command — your edits are the source of truth. + +You can also edit `person.md` directly in your shell, in another editor, or +in a pull request review. The file is plain markdown: + +```markdown +--- +id: sally-o-malley +name: Sally O'Malley +emails: + - value: sally@example.com +phones: + - value: "+15550100" +tags: [friend, dinner-club] +created_at: 2026-05-08T09:15:00Z +updated_at: 2026-05-08T09:15:00Z +--- + +# Sally O'Malley + +Met at the dinner club in 2024. Loves Negronis. +``` + +If frontmatter gets damaged — a stray quote, a truncated YAML block — +[`clawdex doctor --repair`](doctor.md) salvages what it can and preserves +the body. + +## Avatars + +Avatars are managed under `clawdex person avatar`. They're a feature on +their own: see [Avatars](avatars.md). + +## Bulk import + +To populate clawdex from Apple Contacts, Google Contacts, X DMs, or Discord +DMs in one shot, use [Imports](imports.md). Imports project into the same +markdown shape — they don't bypass any of the rules above. + +## Related pages + +- [Notes](notes.md), [Timeline](timeline.md) +- [Search](search.md) +- [Markdown Storage](markdown-storage.md) +- [Doctor](doctor.md) diff --git a/docs/quickstart.md b/docs/quickstart.md new file mode 100644 index 0000000..e36b3e7 --- /dev/null +++ b/docs/quickstart.md @@ -0,0 +1,139 @@ +# Quickstart + +Five minutes from a fresh install to a populated, committed contact index. + +## 1. Install + +```bash +brew install steipete/tap/clawdex +clawdex --version +``` + +Other paths (Go install, source build, release archives) are documented on +[Install](install.md). + +## 2. Pick a place for your contacts + +The CLI lives in this repo. Your contacts live in **a separate, private +markdown repo** that you own. The default suggested remote is: + +```text +https://github.com//backup-clawdex.git +``` + +Create that empty private repo on GitHub first — it's where your data will +back up to. + +## 3. Initialize a contacts data repo + +```bash +clawdex init ~/.clawdex/contacts +clawdex config set repo_path ~/.clawdex/contacts +clawdex config set git.remote https://github.com//backup-clawdex.git +``` + +`init` writes: + +```text +clawdex.toml +people/ +index/ +.clawdex/repairs/ +``` + +The app config lives at `~/.clawdex/config.toml` by default. `--repo DIR` or +`CLAWDEX_REPO=DIR` overrides the configured repo for one run. See +[Config](config.md) for the full key list. + +## 4. Add your first person + +```bash +clawdex person add "Sally O'Malley" \ + --email sally@example.com \ + --phone "+1 555 0100" \ + --tag friend +``` + +Look at what just appeared in the data repo: + +```text +people/sally-o-malley/ + person.md +``` + +Open `person.md` — it's plain markdown with YAML frontmatter. Edit it by +hand, in your editor, on another machine, in a Pull Request review. Clawdex +will read your edits back. See [People](people.md). + +## 5. Add a note + +```bash +clawdex note add sally \ + --kind dm \ + --source whatsapp \ + --text "Follow up about dinner next Thursday" +clawdex timeline sally +``` + +Notes land in `people/sally-o-malley/notes/` as timestamped files. They are +local-only — never written to Apple, Google, or anywhere else. See +[Notes](notes.md) and [Timeline](timeline.md). + +## 6. Search across everything + +```bash +clawdex search dinner +clawdex search +1555 +clawdex search sally@example.com +``` + +Search hits emails, phones, names, tags, and note bodies. See +[Search](search.md). + +## 7. Import the network you already have + +Optional, but most people start here on day one. All imports are local-only: +they only write to your markdown repo. Address-book writes (Apple Contacts, +Google Contacts) are not implemented yet — see [Imports](imports.md). + +```bash +clawdex import apple --dry-run +clawdex import apple --avatars + +clawdex import google --account you@gmail.com --dry-run +clawdex import google --account you@gmail.com --avatars + +clawdex import birdclaw --min-messages 4 --dry-run +clawdex import discrawl --min-messages 4 --dry-run +``` + +## 8. Commit and push + +```bash +clawdex git status +clawdex git commit -m "sync: import apple + google" +clawdex git push +``` + +`git status` is a thin wrapper over `git -C status --short --branch`. +Commit and push run inside the data repo, not this repo. See +[Git Sync](git-sync.md). + +## 9. Export back into the world + +```bash +clawdex export vcard --all --include-avatars -o ~/Desktop/contacts.vcf +clawdex export vcard --person sally -o - # stdout +``` + +The `.vcf` file imports cleanly into macOS Contacts, Google Contacts, iOS +Contacts, and most other address books. See [vCard Export](vcard-export.md). + +## Where next + +- **The four pillars.** [People](people.md), [Notes](notes.md), + [Avatars](avatars.md), [Imports](imports.md). +- **Flow.** [Search](search.md), [Timeline](timeline.md), + [Git Sync](git-sync.md), [vCard Export](vcard-export.md). +- **Maintenance.** [Doctor](doctor.md), [Markdown Storage](markdown-storage.md), + [Config](config.md). diff --git a/docs/search.md b/docs/search.md new file mode 100644 index 0000000..b48433a --- /dev/null +++ b/docs/search.md @@ -0,0 +1,78 @@ +# Search + +`clawdex search ` finds people and notes that match a substring. It's +a local, offline, full-corpus search — no external service. + +```bash +clawdex search dinner +clawdex search sally@example.com +clawdex search +1555 +clawdex search "negroni recipe" +clawdex search whatsapp --json +``` + +## What gets searched + +- Person names, IDs, and tags +- Person emails and phone numbers (normalized) +- Note bodies, kinds, sources, and topics + +Hits are printed one per line, with the kind, name, a snippet, and the +file path: + +```text +person Sally O'Malley sally@example.com people/sally-o-malley/person.md +note Sally O'Malley ...follow up about dinner... people/sally-o-malley/notes/2026-05-08T09-15-00Z-whatsapp.md +``` + +`--json` returns a list of `SearchHit` objects; `--plain` swaps the +snippet for the stable ID, which is friendlier to scripts. + +## How matching works + +The query is a case-insensitive substring match against indexed fields. +For phone numbers the search normalizes both the query and the stored +value (strips spaces, dashes, parentheses, and a leading `+`), so any of +these find Sally: + +```bash +clawdex search "+1 555 0100" +clawdex search "(555) 0100" +clawdex search 15550100 +``` + +For emails the match is plain substring against the lowercase value, so +`gmail.com` works as a "find everyone on Gmail" query. + +## Combine with timeline and grep + +`search` is for finding the right thread; once you've got it, use +[`timeline`](timeline.md) for the full history of that person, or `rg` for +free-form regex on the data repo: + +```bash +clawdex search "ankara" +clawdex timeline mehmet +rg -n "ankara" ~/.clawdex/contacts/people +``` + +## Indexes + +Derived indexes live under `index/`: + +```text +index/ + emails.json + phones.json + handles.json +``` + +These are rebuilt automatically as the markdown changes. They are +*derivable*, not authoritative — delete the folder and clawdex regenerates +it on the next read. Markdown is canonical; see +[Markdown Storage](markdown-storage.md). + +## Related pages + +- [People](people.md), [Notes](notes.md), [Timeline](timeline.md) +- [Markdown Storage](markdown-storage.md) diff --git a/docs/timeline.md b/docs/timeline.md new file mode 100644 index 0000000..7c74117 --- /dev/null +++ b/docs/timeline.md @@ -0,0 +1,59 @@ +# Timeline + +`clawdex timeline ` prints every note for one person, sorted by +`occurred_at`. It's the fastest way to remember what's been going on with +someone. + +```bash +clawdex timeline sally +clawdex timeline sally --json +clawdex timeline sally@example.com --plain +``` + +Default output is a TSV: + +```text +2026-04-12T19:30:00Z meeting inperson Drinks at Bar Centrale +2026-04-22T08:01:00Z dm whatsapp Sent recipe link +2026-05-08T09:15:00Z dm whatsapp Follow up about dinner +``` + +`--json` returns the full note objects, including bodies, topics, and IDs. +`--plain` is a simpler TSV intended for `awk`/`cut` pipelines. + +## Resolving the person + +The argument is the same query string that +[`clawdex person show`](people.md) accepts: an ID slug, a name substring, +an email, or a phone number. The first unambiguous match wins. + +## Reading flow + +Pair `timeline` with `search` for a quick "what was that about" loop: + +```bash +clawdex search "negroni" # find the conversation +clawdex timeline sally | head -20 # surrounding context +``` + +Or pipe into `less` for long histories: + +```bash +clawdex timeline sally | column -t -s $'\t' | less -S +``` + +## Limits + +- Sort key is `occurred_at`, not file mtime. Edit `occurred_at` in the note + frontmatter to fix ordering after the fact. +- Only one person at a time. To get a multi-person timeline, use + [Search](search.md) with a date-ish term, or grep notes directly: + + ```bash + rg -n "occurred_at: 2026-05" ~/.clawdex/contacts/people + ``` + +## Related pages + +- [Notes](notes.md), [People](people.md), [Search](search.md) +- [Markdown Storage](markdown-storage.md) diff --git a/docs/vcard-export.md b/docs/vcard-export.md new file mode 100644 index 0000000..74b575b --- /dev/null +++ b/docs/vcard-export.md @@ -0,0 +1,75 @@ +# vCard Export + +`clawdex export vcard` writes one or more people as RFC 6350 vCards. The +result imports cleanly into Apple Contacts, Google Contacts, iOS Contacts, +Outlook, and most other address books. + +This is the *outbound* half of clawdex. [Imports](imports.md) bring the +world in; vCard export sends a curated slice back out. + +## Export everything + +```bash +clawdex export vcard --all -o contacts.vcf +clawdex export vcard --all --include-avatars -o contacts.vcf +``` + +Without `--include-avatars`, the file is text-only and small. With it, each +person's avatar is embedded as a base64 `PHOTO;ENCODING=b` payload. A few +hundred avatars adds up — expect a few megabytes. + +## Export one person + +```bash +clawdex export vcard --person sally -o sally.vcf +clawdex export vcard --person sally@example.com -o sally.vcf +``` + +The `--person` argument accepts the same query string that +[`clawdex person show`](people.md) accepts: an ID, a name substring, an +email, or a phone number. + +## Stream to stdout + +`-o -` writes to stdout, so you can pipe directly: + +```bash +clawdex export vcard --person sally -o - | pbcopy # macOS +clawdex export vcard --all -o - | wl-copy # wayland +clawdex export vcard --person sally -o - | mail -a contacts.vcf you@example.com +``` + +## What's in the vCard + +Each vCard includes: + +- `FN` — display name from `person.md` +- `N` — best-effort surname/given split +- `EMAIL` per email entry, with the original `kind` as a `TYPE` parameter + when present +- `TEL` per phone entry, with the original `kind` as a `TYPE` parameter +- `NOTE` — short summary from the person body, when present +- `PHOTO;ENCODING=b` — only when `--include-avatars` is set +- `UID` — the person's stable ID slug, so re-imports update existing + cards instead of duplicating + +## Round-tripping + +Apple Contacts and Google Contacts both treat repeated `UID` as an update +trigger. That means you can: + +1. Import from Apple → markdown. +2. Edit names / emails / tags in markdown. +3. `clawdex export vcard --all -o contacts.vcf`. +4. Drag the `.vcf` into Apple Contacts → it updates existing cards in + place. + +This is the closest thing clawdex has to two-way sync today, and it works +because vCard files are dumb, well-understood text. Programmatic +[Sync](imports.md#sync-preview-only) is still preview-only. + +## Related pages + +- [People](people.md), [Avatars](avatars.md) +- [Imports](imports.md) — the inbound counterpart +- [Markdown Storage](markdown-storage.md) — the source of truth diff --git a/index.html b/index.html index d911f04..5848ce9 100644 --- a/index.html +++ b/index.html @@ -3,20 +3,635 @@ - Clawdex + + Clawdex — local-first contact index + + + + + + + -
-

Clawdex

-

OpenClaw documentation and project index. Source lives at openclaw/clawdex.

-
+
+ +
+
+ +
+

clawdex

+ your Rolodex, in markdown. +
+
+ +
+ + + +
+
+
+ docs/index.md + source: github +
+
+

opening the drawer…

+
+
+
+ + +
+ + + + + + + +