Sync
Claude + Obsidian knowledge companion. A persistent, compounding wiki vault based on Karpathy's LLM Wiki pattern — drop sources, ask questions, knowledge compounds.
npx -y skills add eliransu/digital-brain --skill syncAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Pull mentions, DMs, hot channel topics, calendar events, and Linear updates from the last 48h (24h for Slack DMs) into the wiki daily folder, and snapshot active vault notes. Sources: Gmail, Slack, Linear, Google Calendar, local `wiki/notes/`. Creates `wiki/daily/<YYYY-MM-DD>/{mentions.md, topics.md, linear.md, calendar.md, notes.md}`. If the date folder already exists, dedupes by stable ID and appends new items under an `## Update <HH:MM>` section. Prints all collected items as bullets in chat. Triggers on: "/digital-brain:sync", "/sync", "sync my day", "pull mentions", "what did I miss".
SKILL.md
15.0 KB, as published. Nobody here has run it
sync: Daily Mentions + Hot Topics Sync
Pull everything that needs your attention from the last 48h across Gmail, Slack, Linear, Calendar into the wiki daily folder. Surface as bullets in chat at the end.
User identity: [email protected] (Slack handle resolved from this).
Scope
| Source | What to pull | Window | Filed under |
|---|---|---|---|
| Gmail | Threads where you is in To/Cc OR appears in body as a mention | 48h | daily/<date>/mentions.md |
| Slack | @your-handle mentions in any public/private channel | 48h | daily/<date>/mentions.md |
| Slack | @your-handle mentions in groups (group DMs / mpdm) | 48h | daily/<date>/mentions.md |
| Slack | DMs received from any user | 24h | daily/<date>/mentions.md |
| Slack | Hot topics from configured channels (high reply or reaction count) | 48h | daily/<date>/topics.md |
| Linear | Issues assigned to you (any status except Done/Canceled) | open | daily/<date>/linear.md |
| Linear | Comments mentioning you | 48h | daily/<date>/linear.md |
| Calendar | Events you is invited to | next 48h | daily/<date>/calendar.md |
| Notes | Active vault notes (status: open, not expired) — from wiki/notes/ | open | daily/<date>/notes.md |
Config
Read wiki/daily/_config.json. If missing, create it with:
{
"user_email": "[email protected]",
"slack_user_handle": "your-handle",
"slack_hot_channels": ["CXXXXXXXXXX", "CXXXXXXXXXX"],
"calendar_id": "primary",
"linear_user_email": "[email protected]",
"linear_default_team_key": "TEAM",
"linear_default_team_id": "",
"linear_user_id": ""
}
linear_default_team_key / linear_default_team_id / linear_user_id are used by /task and the notes sweep. The two _id fields start empty and get cached after the first MCP lookup.
Config is the source of truth for which channels count as "hot". User edits this file to add/remove channels.
Workflow
-
Compute time windows. Use
date(Bash) or built-in date math.today=YYYY-MM-DDin local timesince_48h_iso= now - 48h in RFC3339 UTCsince_24h_iso= now - 24h in RFC3339 UTCnext_48h_iso= now + 48h in RFC3339 UTC
-
Load or scaffold config. Read
wiki/daily/_config.json. If missing, write the default above and proceed. -
Resolve user IDs once.
- Slack:
slack_search_userswith queryyour-handle→ cache the user ID - Linear:
list_usersfiltered by email[email protected]→ cache the user ID
- Slack:
-
Sweep expired notes. Before pulling anything else, scan
wiki/notes/*.md. For each file wherestatus: openandtoday > expires(date compare), flipstatus: expiredand bumpupdated:to today. Drop those rows fromwiki/notes/_index.md. Cheap, idempotent, runs every sync. -
Pull from all sources in parallel (issue tool calls in one assistant turn).
Token discipline (mandatory). Every Slack call MUST pass
response_format: "concise"andinclude_context: false. Context chains account for >half the token cost of /sync — Slack repeats the same message 18 times across context-before/after blocks of nearby results. Never bulk-read 100 messages out of a channel to find 2-3 hot threads; search for the threads, then read them. Target total /sync turn cost: under 15k tokens of tool output. If you blow past that, you skipped these flags somewhere.- Gmail —
search_threadswith query(to:me OR cc:me OR "your-handle") newer_than:2d -category:promotions -category:socialandpageSize: 50. Snippet + headers are enough; don't follow up withget_threadunless the snippet is unreadable. - Slack channel/group mentions —
slack_search_public_and_privatewithquery: "@<slack_user_id> after:<YYYY-MM-DD of 2d ago>",channel_types: "public_channel,private_channel",response_format: "concise",include_context: false,limit: 20. The result already contains the full text of each hit. - Slack DMs (24h) —
slack_search_public_and_privatewithquery: "is:dm after:<YYYY-MM-DD of 1d ago>",channel_types: "im,mpim",response_format: "concise",include_context: false,limit: 20. If DM filtering returns junk, fall back to a second searchto:me after:<date>with the same options. - Slack hot topics — DO NOT bulk-read each channel. For each channel ID in
slack_hot_channels, issueslack_search_public_and_privatewithquery: "in:<channel_id> is:thread after:<date>",response_format: "concise",include_context: false,limit: 10,sort: "score". Take the top 5 per channel. Only callslack_read_threadon a specific message if the snippet is too truncated to summarize. - Linear assigned —
list_issueswithassignee: "me",updatedAt: "-P14D",limit: 50. The 14-day window cuts stale backlog that doesn't change daily. If the response still exceeds the tool limit, narrow to-P7Dand addstate: "started"(in-progress only). On the first /sync of a day, reuse yesterday'slinear.mdbacklog rows verbatim and only overwrite issues that appear in the active set — backlog issues with noupdatedAtchange carry forward. - Linear mentions — skip the dedicated lookup. Linear's notification emails already surface
@mentionevents via Gmail. Only calllist_commentson a specific issue when the chat surfaces an unresolved question on it. - Calendar —
list_eventswithcalendarId=primary,startTime=<now>,endTime=<next_48h_iso>,orderBy=startTime,pageSize: 50. Don't request full event descriptions; summary + attendees + time are enough.
- Gmail —
-
Flywheel: recognize and file new people BEFORE writing the daily files. Read
wiki/entities/people/_index.md. For every distinct person name extracted from this batch — Gmail senders/recipients (excluding notification bots), Slack message authors, Slack@mentions, Linear comment authors, Calendar attendees — check:- Is there a page at
wiki/entities/people/<Full Name>.mdor an alias match in_index.md? - If no, and the name appears either (a) ≥2x in this batch, or (b) once with a clear role signal (email signature, channel pin, "Head of X",
@<role>ping), then file them now via thepplskill's add flow. Use best-guess role from context; if uncertain, file withrole: "—"+ a> [!todo] Confirm rolecallout. Defaultcompany: Your Organizationunless the source domain says otherwise (e.g.@external1.com,@external2.com,@external3.com→ External). - Track the list of newly-filed people; surface it in the chat summary so the user can correct roles.
Why: the wiki's compounding value is the link graph. Each
/syncshould add nodes, not just edges. Never emit a[[Name]]wikilink to a non-existent page (phantom link rots the graph), and never silently skip a recognizable person. File or don't link — those are the only two options. - Is there a page at
-
Snapshot active notes. After the sweep, glob
wiki/notes/*.mdand load every file withstatus: open. Sort bydueascending. These rows go intodaily/<today>/notes.md(template below) and feed the Top-10 alongside Gmail / Slack / Linear / Calendar. -
Check for existing date folder.
wiki/daily/<today>/:- If missing: create folder. Write all five files from scratch using the templates below.
- If present: dedupe each item against the existing file by stable ID (see "Dedup keys" below). Append new items under a heading
## Update HH:MMat the bottom of each file.notes.mdis regenerated whole each run (not append-dedup) — it's a live snapshot of the working set.
-
Print chat output. Show all items collected this run as bullets, grouped by source (notes included). End with the absolute paths written/updated.
Dedup keys
| Source | Stable key |
|---|---|
| Gmail thread | gmail:<threadId> |
| Slack message | slack:<channel_id>:<ts> or the permalink |
| Linear issue | linear:<identifier> (e.g. ENG-123) |
| Linear comment | linear-comment:<comment_id> |
| Calendar event | gcal:<event_id> |
| Vault note | note:<note_id> (filename without .md) |
Embed the key as an HTML comment on each bullet so future runs can grep:
- [ ] ... <!-- key:gmail:18c3... -->
When appending, read the existing file, extract all <!-- key:... --> markers, and skip any new item whose key already exists.
File templates
mentions.md
# Mentions — <date>
## Gmail
- [ ] **<subject>** — from <sender> — <relative time> — [open](<gmail link>) <!-- key:gmail:<threadId> -->
## Slack — channel mentions
- [ ] **#<channel-name>** — <author>: "<preview, ~120 chars>" — <relative time> — [open](permalink) <!-- key:slack:<channel>:<ts> -->
## Slack — group mentions
- [ ] **<group name or members>** — <author>: "<preview>" — <relative time> — [open](permalink) <!-- key:slack:<channel>:<ts> -->
## Slack — DMs (24h)
- [ ] **<author>**: "<preview>" — <relative time> — [open](permalink) <!-- key:slack:<channel>:<ts> -->
topics.md
# Hot topics — <date>
> Channels: #<name1>, #<name2>, #<name3> (from `_config.json`)
## #<channel-name>
- **<one-line topic summary>** — <N> replies, <N> reactions — started by <author> <relative time> — [open](permalink) <!-- key:slack:<channel>:<ts> -->
linear.md
# Linear — <date>
## Assigned to me — open
- [ ] **<ID>** <title> — <status> — <priority> — [open](url) <!-- key:linear:<ID> -->
## Mentions (48h)
- <comment author> on **<ID>** <title>: "<preview>" — <relative time> — [open](<comment url>) <!-- key:linear-comment:<comment_id> -->
calendar.md
# Calendar — next 48h, as of <date> <HH:MM>
- **<start local time>** (<duration>) — <title> — <attendee count> attendees — [open](<event url>) <!-- key:gcal:<event_id> -->
notes.md
Snapshot of every wiki/notes/*.md with status: open (sweep already removed expired). Regenerated whole each run — not append-dedup.
# Notes — <date> <HH:MM>
> Live snapshot of `wiki/notes/` — vault TTL 7d. Manage with `/note`, `/task`, or `/note done <id>`.
## Active Tasks
- [ ] **<title>** — assignee <Full Name or me> — due <YYYY-MM-DD HH:MM> — <Linear ID or —> — [open](obsidian://open?vault=digital-brain&file=notes%2F<note_id>) <!-- key:note:<note_id> -->
## Active Reminders
- [ ] **<title>** — due <YYYY-MM-DD HH:MM> — [open](obsidian://open?vault=digital-brain&file=notes%2F<note_id>) <!-- key:note:<note_id> -->
If a section is empty, write the header with _none_.
Chat output (after writing)
Lead with a Top-10 table. Persistence still happens in the four files; the chat output is for triage. Use this shape:
Synced <today>. Wrote/updated:
- wiki/daily/<today>/mentions.md (<N> new)
- wiki/daily/<today>/topics.md (<N> new)
- wiki/daily/<today>/linear.md (<N> new)
- wiki/daily/<today>/calendar.md (<N> new)
- wiki/daily/<today>/notes.md (<N> active · <N> expired this sweep)
### Top 10
| # | Priority | Source | Item | Deadline / age | Action |
| - | -------- | ----------------- | --------------------------------------- | ---------------- | ----------------- |
| 1 | 🔥 | Slack group DM | Dan: review red-team demo video | by 16:00 today | Watch + post take |
| … | … | … | … | … | … |
### Counts (full detail in the daily files)
- Gmail: <N> threads · Slack channel mentions: <N> · Slack DMs: <N> · Linear assigned: <N> open · Calendar: <N> next 48h · Notes: <N> active (<N> tasks, <N> reminders; <N> expired this sweep)
Top-10 selection rules (same as /sup):
- Drop
[email protected],[email protected],[email protected],[email protected], and any "calendar invitation accepted/declined" emails — they don't get rows even though they still land inmentions.mdfor the historical record. - If a Linear mention already appears in the table, do NOT also add the matching Linear notification email.
- Slack DMs only earn a row if they ask for a decision, review, or name a deadline. Reaction emojis, links, "ok"/"thanks"/"got it", and banter never get a row.
- Use 🔥 for hard deadlines in next 4h, High for explicit asks-of-you in last 24h, Med for older / context items.
- Notes earn rows. Every
note_kind: taskwithstatus: openalways gets a row (source:Note (task)).note_kind: remindergets a row only ifdue ≤ now + 24h(source:Note (reminder)). Score 🔥 ifdue ≤ now + 4h, High otherwise. If a note has alinearID and the matching Linear issue already appears in the table, dedupe — keep one row (prefer the note row, since it carries your own framing).
Be terse: title + 1-line context + link per item in the table. Do not paste full message bodies in chat — the daily files already have the full detail.
Failure modes
- No MCP for a source (e.g. Gmail not authenticated): skip that source, note
(skipped: not authenticated)in chat output. Do not block the rest. - Rate limit / API error: log it, continue with what you have.
- No items in a section: still write the section header with
_none_so future updates have an anchor.