The problem
If you keep meeting notes in Obsidian, the calendar and the vault drift apart fast. Every meeting needs a note with the right title, the right people linked, the right recurring series, and a place for the agenda. Doing that by hand is small work that never stops, and it is the first thing to slip on a busy week.
Most calendar plugins solve this by dumping events into a generic daily note. That works until you already have a schema you like, with its own people notes, company notes, and recurring meeting definitions. Then the plugin is one more thing that does not match how you write.
Graft brings the schedule to your notes, in the shape your notes already have.
What it does
Graft reads two calendars, a Google Calendar and an Outlook or Microsoft 365 calendar, each through its own private ICS address. There is no sign-in flow, no client secret, and no external service in the middle. It filters out what you have told it to ignore, matches attendees and recurring series against notes already in your vault, and creates or updates one Meeting note per occurrence.
Before anything is written, a preview shows exactly what would change: new notes, field-level updates, new stubs, and everything that was skipped and why.
The vocabulary
Feeds are the two calendar sources. Meetings are the notes, one per dated occurrence. Series are the recurring meeting definitions a Meeting can link back to, and they can carry a standing agenda that new occurrences pick up automatically. People and Companies are the entity notes attendees are matched against. An attendee with no match gets a minimal stub, never a guess at a job title or department.
Read-only, one direction
Graft never writes to either calendar. It cannot create, move, or delete an event, because it only ever reads a feed. Ignoring an event is a deliberate, manual edit to a plain list. Nothing is filtered by RSVP status, and nothing is decided by fuzzy matching or AI.
The same rule applies inside the vault. Graft owns the calendar facts on a meeting note, like the title, time, and organizer. Everything you write yourself stays yours, byte for byte.
Built with
TypeScript against the Obsidian plugin API, ical.js for parsing calendar data, and Luxon for time zones. Feeds are fetched through Obsidian's own request layer, and note reads and writes go through the metadata cache and front matter APIs instead of hand-parsing files.