DeepSeek Harness Remote
DeepSeek Harness Remote 可將手機或另一台電腦與你運行 DeepSeek Harness 的電腦安全連接,隨時隨地繼續你的工作,方便隨時查看進度並繼續工作。
AI 翻譯
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證A local, read-only lifelong archive of your Apple Notes, Reminders, Calendar, WhatsApp, ChatGPT, Claude and Claude Code, as Markdown plus an offline viewer.
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗A local, read-only lifelong archive of your Apple Notes, Reminders and Calendar, your WhatsApp chats, Instagram and Facebook Messenger messages, your Facebook posts, comments, groups, events and Marketplace activity, ChatGPT and Claude exports, and Claude Code transcripts. It keeps everything it has ever seen, including messages that later expire or are deleted, as Markdown files plus an offline HTML viewer that you can browse, search, or give to an AI tool of your choice. Lifelog does not change your Apple or WhatsApp data and does not need an API key, web service, MCP server, or ChatGPT subscription. It also still has the original export mode, a plain Markdown mirror of Notes, Reminders, Calendar and WhatsApp.
Lifelog was called icloud-exporter. It now has its own bundle identifier, com.djui.lifelog, so macOS treats it as a new app: grant Calendar, Reminders, Notes and Full Disk Access again (Settings → Permissions), and choose your archive folder again. The archive and export folders themselves keep working as they are. The app still recognizes launchd agents named com.djui.icloud-exporter*, so Take Over from launchd… and Stop the Export Agent… work for agents set up before the rename.
Requires macOS 14 or newer and Swift 5.9 or newer to build. There are no third-party package dependencies.
The easiest way to run it is the menu bar app. Build it, open dist/Lifelog.app, choose an empty archive folder and click Start Archiving Here…. It then keeps a lifelong archive on a schedule and shows what it does in the menu bar; see Menu bar app. The CLI below does the same from Terminal or launchd.
./scripts/build.sh
./dist/lifelog --help
./dist/lifelog export --output "$PWD/demo-export" --demo
The demo uses synthetic data and does not request access to Notes, Reminders, Calendar, or WhatsApp. The build script packages a signed dist/Lifelog.app (required for macOS permissions) and a convenience symlink at dist/lifelog. Keep the .app together when copying the exporter elsewhere.
To export your data, run the packaged app in the foreground first:
./dist/Lifelog.app/Contents/MacOS/lifelog export \
--output "$HOME/Documents/iCloud Export"
macOS may ask for Calendar and Reminders access, permission to control Notes, and permission to access data from other apps (WhatsApp’s local database, and any export folder owned by another app such as Google Drive). Grant those prompts once for Lifelog. If a scheduled run keeps asking, macOS is not recognizing a stable app identity: use the .app path above, not swift run or a rebuilt unsigned binary. Notes uses macOS automation; Calendar and Reminders use EventKit; WhatsApp is an unofficial read of WhatsApp Desktop’s ChatStorage.sqlite. If access was denied, review System Settings → Privacy & Security (Calendar, Reminders, Automation, and Full Disk Access) and add Lifelog.app there. Use that same .app for scheduled runs.
lifelog export --output <directory>
[--sources notes,reminders,calendar,whatsapp]
[--past-days 365]
[--future-days 365]
[--completed-days 30]
[--demo]
--output is required. All four sources are selected by default. Use a comma-separated list to limit the export:
./dist/Lifelog.app/Contents/MacOS/lifelog export \
--output "$HOME/Documents/iCloud Export" \
--sources reminders,calendar \
--past-days 30 --future-days 60 --completed-days 14
--past-days and --future-days set the Calendar look-back and look-ahead windows, measured from the time the export starts. Events that overlap a window are included; an ongoing event can appear in both files. --past-days also limits WhatsApp message history. WhatsApp exports the 20 most recently active 1:1 and group chats in that window. --completed-days limits recently completed reminders; incomplete reminders are exported separately. --help displays usage and --version displays the exporter version.
With the default windows, the directory looks like this:
iCloud Export/
_index.md
Notes/
<account>/
<folder>/
<title>--<stable-hash>.md
Reminders/
current-reminders.md
completed-recent.md
Calendar/
upcoming-365-days.md
recent-365-days.md
WhatsApp/
<chat>--<stable-hash>.md
Calendar filenames reflect the configured windows: upcoming-<future-days>-days.md and recent-<past-days>-days.md. Note and WhatsApp filenames include a stable identifier hash to distinguish items with the same title. Path components are sanitized and lowercased so case-only renames work on macOS; titles and metadata retain their original case.
The hidden .icloud-exporter-manifest.json tracks files managed by the exporter so that subsequent runs can remove stale exports. Unrelated files are preserved. Exporting selected sources preserves the previous exports and timestamps for unselected sources in the index. If an existing destination file has been edited or is not managed by the exporter, the run refuses to overwrite it. Treat generated files as snapshots: keep your own annotations separately, or export into a new directory when resolving a conflict.
Each selected source is written as soon as it has been read. Files are replaced atomically, and an interrupted write leaves a .icloud-exporter-pending.json journal so the next run can recover. The directory as a whole is not an atomic snapshot; a sync client may briefly observe files from two runs, and a failed source leaves previously written sources in place. An output lock prevents overlapping exporter writes. Demo and live exports must use separate directories.
Choose any writable output directory. It can be a local folder or a folder synced by your own Google Drive or other sync client. The exporter does not connect to Drive or configure syncing. Exports contain readable personal data, so choose the destination and sharing settings accordingly. How an AI product can access these files depends on that product and your setup.
~/Library/Group Containers/group.net.whatsapp.WhatsApp.shared). It is unofficial, does not talk to WhatsApp servers, and is not a full chat backup. It writes the 20 most recently active chats, limited to --past-days. Images, audio, video, and other attachments become placeholders; media files are not copied. Missing WhatsApp data is skipped with a warning. If the database exists but cannot be read, allow access to other apps’ data, or grant Full Disk Access to Lifelog.app..app is the identity to grant. After rebuilding with a different bundle identifier or an ad-hoc signature, macOS treats the exporter as a new app and asks again.This schedules the export mirror. For the archive, use the menu bar app. Once the archive has run for a while, the app offers Stop the Export Agent… to retire this agent.
Scheduling is manual. First complete a successful foreground export using dist/Lifelog.app/Contents/MacOS/lifelog, the same path you will schedule. Then:
~/Library/LaunchAgents if needed, then copy launchd/com.djui.lifelog.plist.example to ~/Library/LaunchAgents/com.djui.lifelog.plist..app executable, output directory, and log files. Leave AssociatedBundleIdentifiers as com.djui.lifelog. Create the parent log directory if it does not exist. A plist does not expand ~, $HOME, or shell commands. Escape XML characters such as & as & when they occur in paths.plutil -lint "$HOME/Library/LaunchAgents/com.djui.lifelog.plist"
launchctl bootstrap "gui/$(id -u)" \
"$HOME/Library/LaunchAgents/com.djui.lifelog.plist"
The example runs when loaded and then approximately every ten minutes while your user session is active. A sleeping or logged-out Mac does not provide an exact ten-minute schedule. Check the configured logs if a scheduled export fails.
To stop the agent before changing or removing it:
launchctl bootout "gui/$(id -u)" \
"$HOME/Library/LaunchAgents/com.djui.lifelog.plist"
After editing an unloaded agent, validate it and load it again. Building the project does not install or load a launch agent.
export is a mirror: each run rewrites the selected sources and removes files for items that are gone, or that fall outside the time windows. archive keeps everything it has ever seen. When an item is deleted at the source, it expires, or its sender deletes it, the item stays in the archive with its last text and is marked with its new state. Nothing is dropped. The design and its decisions are in docs/plans/lifelong-archive.md.
Try it with synthetic data first. The demo needs a new, empty folder:
./dist/lifelog archive --output "$PWD/demo-archive" --demo
./dist/lifelog open --output "$PWD/demo-archive"
The demo replays a few runs, so it shows deleted, expired, revoked, edited and locked items. A demo folder and a real archive cannot be mixed: a real run refuses a demo folder, and the demo refuses a real one.
lifelog archive --output <directory>
[--sources notes,reminders,calendar,whatsapp,chatgpt,claude,claudecode,instagram,messenger,facebook]
[--backfill-since 2000-01-01]
[--rescan-days 100]
[--full-sweep-days 7]
[--ai-inbox ~/Downloads]
[--notify]
[--no-viewer]
[--demo]
lifelog rebuild --output <directory> [--no-viewer]
lifelog verify --output <directory>
lifelog dedupe --output <directory> [--apply] [--no-viewer]
lifelog open --output <directory>
archive reads the selected sources, records what changed, and rewrites the pages that show changed items. --sources defaults to notes,reminders,calendar,whatsapp. Add chatgpt, claude and claudecode to archive AI chats as well, instagram and messenger for Instagram direct messages and Facebook Messenger chats, and facebook for the rest of your Facebook activity (see Sources).rebuild regenerates every page from the ledger. Use it after deleting or damaging pages, or when an archive run reports that its pages were not written.verify checks the ledger, the run log and the index cache without changing anything. It prints Problem: lines and exits non-zero when the archive cannot be trusted. It prints Note: lines for harmless issues, such as a stale cache.dedupe lists Claude Code messages that older archives hold twice: once under <chat>/<uuid>#<uuid>, from a read that saw a repeated transcript line, and once under <chat>/<uuid>. It writes nothing unless you add --apply. Then it appends one rekeyed event per duplicate, which folds it into its plain twin with the earlier first sighting, and rebuilds the pages. No ledger line is changed or removed. A # item without a plain twin is the only copy and stays.open opens the offline viewer, _viewer/index.html, in your default browser.Each source is read in one of three modes:
--backfill-since (default 2000-01-01): all calendar history, every chat in the local WhatsApp database, and all completed reminders. Expect the first run to take a while.--full-sweep-days (default 7). It reads everything again, so that old deletions show up.--rescan-days (default 100). That is longer than WhatsApp's longest disappearing-message timer (90 days).Notes are always read in full. A failed source is logged as incomplete and the other sources continue. The run exits non-zero when any source failed.
--no-viewer skips the HTML viewer and removes an existing one. --notify posts a macOS notification when capture falls behind (see Health).
Use a new, empty folder for the archive, separate from any export folder. Each command refuses the other's folders. A Google Drive-synced folder works: past months are never rewritten, and the current month's ledger file is only appended to.
Archive/
_index.md # last complete read per source, health warnings, links
Timeline/2026/2026-10.md # per day, across sources, links into the files below
_Disappeared/2026/2026-10.md # everything that was deleted, expired, revoked or became unknown that month
WhatsApp/<chat>--<hash>/
_chat.md
2026/2026-10.md # messages by month; states marked inline
Calendar/2026/2026-10.md # events by start date
Reminders/
open.md # open reminders
2026/2026-10.md # by completion (or deletion) month
Notes/<account>/<folder>/<title>--<hash>.md
ChatGPT/2026/2026-10/<title>--<hash>.md # by the month a chat started
Claude/… ClaudeCode/… Instagram/… Messenger/… # same as ChatGPT
Facebook/2026/2026-10/posts 2026-10--<hash>.md # one page per category and month
_viewer/index.html # offline viewer, plus app.js, style.css and data/
_archive.sqlite # every item as one read-only SQLite database
.archive/
ledger/<source>/<YYYY-MM>.<host>.jsonl # append-only source of truth
runs/<YYYY-MM>.<host>.jsonl # run log: one line per source per run
pages.json # which pages the archive owns
The ledger is the source of truth. Everything else is a view of it and can be regenerated with rebuild. Ledger files are plain JSON lines, partitioned by the month they were written. Each Mac writes only files carrying its own host id, so two Macs never write the same file. Don't edit pages by hand: like export, the archive refuses to overwrite a page that was changed outside it.
Every ledger and run log line carries a format version. When two Macs share an archive and one runs a newer Lifelog, the older one may find lines it can't fully read, such as a newer format or a source it doesn't know. It never treats those lines as damage and never changes them. Until you update Lifelog on that Mac, it does this:
rebuild and dedupe --apply.After the update, the next run writes every page again. verify lists such lines as problems that say to update Lifelog.
Caches live outside the synced folder, in ~/Library/Caches/com.djui.lifelog/<archive-hash>/. They hold the folded index (index.sqlite), the Claude Code read positions, the AI inbox digests and the notifications already sent. The index remembers how far it has read each ledger file, so a run reads only the lines added since and saves only the items that changed. It reads the whole ledger again when a ledger file was changed or removed, when another Mac's lines arrive that are older than ones already read, or when the folder holds unexpected files. All of the caches can be deleted. The next run rebuilds the index from the ledger, and reads every Claude Code transcript again without adding duplicates.
_archive.sqlite holds every item, with its state, dates, text and page, plus the run log, for sqlite3, Datasette, DuckDB or an AI tool. The items_fts table is a full-text index of the text, for example:
sqlite3 "$HOME/Archive/_archive.sqlite" "SELECT day, source, who, text FROM items_fts JOIN items ON items.rowid = items_fts.rowid WHERE items_fts MATCH 'birthday' ORDER BY day"
Like the pages, it is regenerated from the ledger: Lifelog builds it in a local temporary folder and moves it into place, so it never syncs half-written. It can reach a few hundred megabytes, and each rewrite syncs in full. An archive run therefore rewrites it at most once a day, so it can be up to a day behind the pages. rebuild always rewrites it. Treat it as read-only, because the next rewrite replaces any changes.
| State | Meaning |
|---|---|
active | Seen in the latest read. |
| edited | Not a state but a flag: the item's text changed since it was first seen. Only the latest text is kept. Calendar events also record rescheduled from … and changes to status and invitation response. |
completed | A completed reminder that still exists. Reopening it makes it active again. |
archived | Older than --backfill-since, so no read covers it any more. It is frozen, not deleted. |
trashed | In a recoverable bin at the source. No source reports this today: Notes scripting does not expose Recently Deleted, so a trashed note goes missing and becomes deleted. |
locked | A note that was locked. Its title, folder and dates are still read, and the archive keeps the last text it saw before the lock. |
deleted | Gone from the source, according to the rules below. |
expired | A WhatsApp message that disappeared on a timer. WhatsApp has no timer column, so this is inferred: the message vanished at an age close to 24 hours, 7 days or 90 days (±10%), and at least one other message in the same chat vanished at the same age. Otherwise it is deleted. The reason always says "inferred". |
revoked | A WhatsApp message its sender deleted for everyone. The archive keeps the text it saw before. |
unknown | Went missing in a mass disappearance (see the circuit breaker below). It is never turned into deleted automatically. |
Every vanished item stays on its page with its last text, and is also listed in that month's _Disappeared page. The archive records when it last saw an item and when it noticed the change. Apple and WhatsApp don't report deletion times, so the truth is "gone between these two times".
If an item that was deleted, expired or unknown appears again, it becomes active again with the reason reappeared.
Something missing from a read is only marked as gone when all of these hold:
deleted or expired. If it is seen again in between, the pending mark is cleared.unknown, never deleted.Some sources never tombstone:
A missing item plus a new item with the same container and the same text, seen in the same run, is treated as one item whose ID changed, not as a deletion plus a duplicate.
export (see above). Unlike export, the archive reads every WhatsApp chat (not just the 20 most active ones), all message types, and all calendar history in chunks of at most four years. Locked notes are kept as stubs with title, folder and dates.chatgpt, claude) are imported from the official account exports: in ChatGPT, Settings → Data controls → Export data; in Claude, Settings → Privacy → Export data. Leave the downloaded zip in the AI inbox: ~/Downloads by default, or the folder you set with --ai-inbox. Each run looks at the zips directly in that folder, not in subfolders. Exports are recognized by their contents, not by their file name. Each zip is imported once, tracked by its SHA-256 digest in the run log, and left where it is. A complete export counts as a complete read of that service, so a chat missing from two newer exports becomes deleted. Exports are manual, so a chat created and deleted between two exports is never seen.instagram) is imported from the official export: in Accounts Center → Your information and permissions → Export your information → Create export, pick the Instagram profile, choose Export to device and set the format to JSON (an HTML export is refused with a warning). Meta emails you when it is ready. Leave the zip in the same inbox as the AI exports. Direct messages are imported from every message folder (inbox, requests and so on); one page per conversation, with each message under the sender's name. Photos, videos, voice messages, stickers and shared posts become placeholders. Meta's broken text encoding (é for é) is repaired. Large accounts get their export in several zips; each part is imported on its own. Because no single zip is a complete read, Instagram imports only add: an unsent or deleted message stays active in the archive.messenger) is imported from the Facebook export the same way: the same Create export, with the Facebook profile, Export to device and format JSON. If you archive only Messenger, customizing the export down to Messages keeps the zip small; the facebook source below needs the rest. Everything said about Instagram above applies. Chats with end-to-end encryption, the default in Messenger since 2024, are not in this export; Lifelog does not read Messenger's separate secure storage download.facebook) is read from the same Facebook export as Messenger, so keep the export whole rather than customizing it down to . Each zip is imported once for Messenger and once for Facebook, so turning on later still imports zips Messenger already read, as long as they are still in the inbox. Lifelog reads your posts and check-ins, photos and videos, stories and reels, places you were tagged at, comments and reactions, group posts, comments and memberships, event responses, invitations and events you hosted, Marketplace listings, conversations and searches, saved items, pages you liked or followed, pokes, friends and friend requests, follows, and profile updates. Each category gets one page per month, such as , with one heading per entry. Lifelog 0.4 stores these as generic entries rather than chat messages. Your next Facebook import converts the entries archived before. Their pages stay the same and they aren't marked as edited. Photos, videos and links become placeholders; the media are not copied. Ads, security, login and device logs, settings, payments and entries without a date (such as poll votes) are left out. Everything said about Instagram above applies.Each archive and rebuild writes a static viewer to _viewer/. It shows coverage per source, the disappeared items, a timeline and full-text search. It is plain HTML, JavaScript and data files, works without a network connection or server, and needs nothing to keep running. Open it with:
./dist/lifelog open --output "$HOME/Archive"
You can also double-click _viewer/index.html.
The run log records every read, so the archive can tell when capture falls behind:
_index.md and the output of each run warn after 20 hours without a complete WhatsApp read, and again with the possible-loss window once the gap passes 24 hours. They also warn when WhatsApp Desktop (net.whatsapp.WhatsApp) is not running.With --notify, each of these is also posted once as a macOS notification from Lifelog. Allow notifications when macOS asks.
Run verify from time to time, and after anything unusual such as a sync conflict:
./dist/Lifelog.app/Contents/MacOS/lifelog verify --output "$HOME/Archive"
Run the first archive in the foreground from Terminal.app, using the .app path, so that the macOS permission prompts appear and can be granted to Lifelog. Archive one source at a time into a new, empty folder, so that a prompt or a slow backfill is easy to tell apart. The examples use $HOME/Archive. A Google Drive folder works the same way.
./dist/Lifelog.app/Contents/MacOS/lifelog archive --output "$HOME/Archive" --sources reminders
./dist/Lifelog.app/Contents/MacOS/lifelog archive --output "$HOME/Archive" --sources calendar
./dist/Lifelog.app/Contents/MacOS/lifelog archive --output "$HOME/Archive" --sources whatsapp
./dist/Lifelog.app/Contents/MacOS/lifelog archive --output "$HOME/Archive" --sources claudecode
./dist/Lifelog.app/Contents/MacOS/lifelog archive --output "$HOME/Archive" --sources chatgpt,claude,instagram,messenger,facebook
./dist/Lifelog.app/Contents/MacOS/lifelog archive --output "$HOME/Archive" --sources notes
Each line of output names the source, the read mode and what changed. Then check the result:
./dist/Lifelog.app/Contents/MacOS/lifelog verify --output "$HOME/Archive"
Open dist/Lifelog.app from Finder, or run lifelog app, to put Lifelog in the menu bar. It's the same app and binary as the CLI, so the permissions you granted carry over.
Choose the archive folder in the menu. In a folder that doesn't hold an archive yet, click Start Archiving Here… first; the app warns if the folder isn't empty. From then on the app archives on two schedules, the same split as the two launchd agents below:
It also runs right after the Mac wakes if a schedule is due. Each run is a separate lifelog archive process, so a long Notes read never blocks the menu, and runs share the archive lock with any you start in Terminal. Turn on Open at Login so archiving continues after a restart.
The menu shows the next run, the source being read, each source's last read (✓, or ⚠ with the reason it failed) and the same health warnings as _index.md. It has Run Now, Pause and Show Log (~/Library/Logs/Lifelog/archive.log). The menu bar icon changes with the state:
| Icon | Meaning |
|---|---|
| Cloud in a tray | All fine |
| Turning arrow over the tray | A run is in progress; it turns, unless Reduce Motion is on |
| Dot badge | A health warning or a source whose last read failed; open the menu for the reason |
| Dashed tray | Paused |
| Exclamation mark in the tray | The last run archived nothing, such as a run that was stopped for taking too long |
Settings… (⌘,) holds everything the launchd agents' arguments did:
--backfill-since), re-read days (--rescan-days), full re-read interval (--full-sweep-days), the HTML viewer (--no-viewer), and Verify Archive, Rebuild Pages and Show Ledger in Finder.The app sends the same notifications as archive --notify (WhatsApp falling behind, repeated read failures, an AI export due), each once, so --notify isn't needed with the app.
While an archive launchd agent (com.djui.lifelog.archive*.plist) is installed, the app leaves scheduling to it, so nothing is read twice. Choose Take Over from launchd… in the menu to switch; see Headless scheduling with launchd.
The app reads the run log as it grows (only new lines on each check) and keeps the last 60 days plus what the health checks need, so it stays light however old the archive gets.
The packaged binary starts the menu bar app only when it has no arguments and wasn't started from a terminal. With arguments, or typed in Terminal, it stays the CLI.
The menu bar app schedules the archive for you. Use launchd instead only to run without the app. If you set up the agents below before the app, open the app and choose Take Over from launchd…: it stops the agents, moves their plists to ~/Library/Application Support/com.djui.lifelog/Disabled LaunchAgents (move them back to restore them), and takes over their folder, sources, schedules and options.
Use two launchd agents that share one archive folder. They share its lock, so when one is running, the other fails fast and tries again at its next interval.
whatsapp,reminders,calendar,claudecode every 10 minutes, because disappearing messages can expire after 24 hours.notes,chatgpt,claude,instagram,messenger,facebook every hour, because a full Notes read can take minutes.launchd/com.djui.lifelog.archive.plist.example to ~/Library/LaunchAgents/com.djui.lifelog.archive.plist. Replace every /ABSOLUTE/PATH/TO/… placeholder, as for the export agent above.~/Library/LaunchAgents/com.djui.lifelog.archive-slow.plist. In it, change the Label to com.djui.lifelog.archive-slow, --sources to notes,chatgpt,claude,instagram,messenger,facebook, StartInterval to 3600, and the log file names to archive-slow.log and archive-slow.error.log.--notify to ProgramArguments in the fast agent if you want notifications.plutil -lint "$HOME/Library/LaunchAgents/com.djui.lifelog.archive.plist" "$HOME/Library/LaunchAgents/com.djui.lifelog.archive-slow.plist"
launchctl bootstrap "gui/$(id -u)" "$HOME/Library/LaunchAgents/com.djui.lifelog.archive.plist"
launchctl bootstrap "gui/$(id -u)" "$HOME/Library/LaunchAgents/com.djui.lifelog.archive-slow.plist"
Use launchctl bootout with the same arguments to stop an agent.
exportKeep your existing export agent running for about a week alongside the archive, writing to its own folder. Then compare the two: the same notes, reminders and recent events should appear in both, and the archive should have more WhatsApp chats and history. Check the _index.md health section and verify. Unload the export agent only after that.
The archive keeps other people's WhatsApp messages after they expired or were deleted for everyone, in plain text, in the folder you choose. If that is a Google Drive folder, they sync there. Those people chose to make the messages disappear, and the archive overrides that choice for your copy. There is no extra encryption: the archive relies on your Google account security and FileVault. Choose the folder's sharing settings accordingly, and don't share the archive with an AI tool or person you wouldn't show those messages to. Claude Code transcripts can contain work code in the text turns, even though tool output is reduced to placeholders.
expired states can be wrong, for example when a message happens to be deleted at about the age of a timer.unknown rather than false deletions.conversations.json, but have not yet been checked against real exports. Branches and edits in ChatGPT conversations and attachments in Claude exports are the most likely to differ. Look at the imported chats after your first import. The same goes for the Instagram and Messenger importers, which follow the JSON layout of current exports (your_instagram_activity/messages/…, your_facebook_activity/messages/…); older exports with messages/ at the root are recognized only when the zip has other files named after the service. The Facebook importer reads the files of a 2026 export by their paths (your_facebook_activity/posts/…, connections/friends/… and so on); a file Meta renames or reshapes is skipped without a warning.swift test
./scripts/build.sh
To add an archive source, add an ExportSource case and describe it in SourceSpec.swift: its reader, its default schedule, its view, and whether it is imported from exports and only ever added to. Items that aren't notes, reminders, events or chat messages use the generic entry payload (ArchiveEntry), which the chat view already renders. Ledger lines carry a format version (FormatVersion.swift). A new payload kind, source or event type needs no version bump. Renaming or retyping a stored field does: raise currentVersion and add a Tests/LifelogCoreTests/Fixtures/format-<n> folder.
CI runs tests on macOS and checks that the packaged executable can produce a demo export and a demo archive. Tests and the demo do not require access to your Apple or WhatsApp data.
To publish a release, store notarytool credentials once (xcrun notarytool store-credentials lifelog-notary --apple-id <apple id> --team-id <team id>), then run ./scripts/release.sh. It builds with the hardened runtime and a secure timestamp, notarizes and staples dist/Lifelog.app, and writes release/Lifelog-<version>.zip.
MIT. See LICENSE.
facebookFacebook/2024/2024-05/posts 2024-05--….mdclaudecode) reads the session transcripts in ~/.claude/projects/ incrementally, picking up only new lines on each run. User and assistant text is kept. Tool calls, tool results and reasoning become one-line placeholders, so work code and tool output stay out of the archive.