Skip to content

Repository files navigation

ClaudeCalendar

Česky · English

Malý kalendář pro Claude Code, uložený v git repozitáři místo ve službě. Události jsou obyčejné iCalendar soubory, jedna událost jeden soubor, takže si asistent může přečíst týden, posunout schůzku nebo zrušit jediný výskyt opakované události — a každá změna skončí jako commit.

Není tu server ani účet. Sám od sebe nástroj sahá jen na soubory a na lokální repozitář, žádné spojení neotevírá. Všechno, co opouští stroj, se zapíná zvlášť: pull a push při zápisu (viz Konfigurace) a oddělený jednosměrný push do Googlu, díky kterému může zazvonit telefon (viz Notifikace).

Dvě repozitáře

Tenhle repozitář obsahuje program. Události žijí v druhém, privátním repozitáři — diář není nic, co by se zveřejňovalo, a smíchat obojí by znamenalo, že se každá schůzka objeví v historii samotného nástroje.

ClaudeCalendar/            tenhle repozitář: program, testy, dokumentace
ClaudeCalendarData/        privátní: calendar.json, calendars/, kopie programu
  calendar.json
  calendars/
    osobni/
      3f2a9c1e-….ics       jedna událost na soubor, pojmenovaný podle UID
      7b41d0aa-….ics
    prace/
  .claude/skills/kalendar/ návod, kterým se asistent řídí

Datové repo si nese vlastní kopii ClaudeCalendar.js a node_modules. Je to záměr: díky tomu je soběstačné, takže cloudový sandbox, který naklonoval jen jeho, může nástroj spustit — a právě tak se ke kalendáři dostaneš z telefonu.

Kopii, na kterou dosáhne telefon, nespouštěj

Tahle kopie je zároveň jediná část celého uspořádání, kterou může změnit někdo jiný. Kdo umí pushnout do datového repa, určuje, co v něm je — a úloha, která ho bez dozoru pullne, by pak spouštěla jeho kód. Na stroji, kde leží klíče ke Googlu, se tím z práva zápisu do repozitáře tiše stává spouštění kódu. Šifrování klíčů proti tomu nepomůže: útočník prostě upraví program, který je dešifruje.

Drž proto kopii programu mimo datové repo a spouštěj ji:

node install.js ~/.local/ClaudeCalendar --program-only

Zkopíruje jen program a jeho jedinou závislost — žádný kalendář, žádný git. Na data ho nasměruj přes CLAUDECALENDAR_DIR (nebo --data) a používej ho ve spouštěči i v naplánované úloze. Kopie uvnitř datového repa zůstává, kde je; sandbox ji potřebuje.

Stejnou nedůvěru si zaslouží .gitignore. Drží klíče stranou, ale kdo umí pushnout, může ten řádek smazat — a nejbližší git add -A je commitne. Pojistkou je pre-commit hook v .git/hooks, který se neverzuje, takže ho push neodstraní, a odmítne stagnout google-*.json.

Program hledá data v tomhle pořadí:

  1. $CLAUDECALENDAR_DIR
  2. adresář calendars/ vedle skriptu (uspořádání uvnitř datového repa)
  3. ~/.claudecalendar

--data <adresář> přebije všechny tři. Commit vzniká v repozitáři s daty, nikdy v tomhle.

Proč jeden soubor na událost

Je to rozvržení vdir, stejné, jaké používají vdirsyncer a khal. Jediný velký .ics by kolidoval při každé souběžné úpravě; s jedním souborem na událost se dvě změny srazí jen tehdy, když sahají na tutéž událost. Vedlejším efektem je, že git log se stane záznamem o tom, kdy se která schůzka posunula.

Než začneš

Potřebuješ Node 22 nebo novější, a to, že máš Claude Code, neznamená, že ho máš: nativní instalace si nese vlastní runtime a žádný node v PATH nenechá, zatímco instalace přes npm běží na tom, co je zrovna po ruce. (Cloudový sandbox Node má, proto funguje ta strana s telefonem.)

git nutný striktně vzato není — kalendář se čte i zapisuje i v obyčejném adresáři — ale bez něj nejsou commity, historie ani způsob, jak se ke kalendáři dostat odjinud, takže to instalátor řekne a pokračuje. Z registru se neinstaluje nic: program má sedět uvnitř datového repa, takže se používá klonováním, ne přes npm install -g.

1. Získej program a ověř, že funguje.

git clone https://github.com/Lossik/ClaudeCalendar.git
cd ClaudeCalendar
npm install
npm test

2. Založ kalendář. Instalátor vytvoří ten druhý, privátní repozitář a zkopíruje do něj program:

node install.js ../ClaudeCalendarData

Vytvoří calendars/osobni/, calendar.json s časovou zónou tohohle stroje, .gitignore, .gitattributes, který drží .ics soubory na CRLF, jak chce RFC 5545, skill pro asistenta a první commit. Přepínačem --calendar <jméno> zvolíš jiný první kalendář, --timezone <zóna> přebije detekovanou.

3. Ulož si kopii, ze které budeš spouštět. Ne tu, kterou jsi právě vytvořil — ta patří datovému repu, do kterého může pushovat telefon, a jejím spouštěním bys komukoli s právem zápisu otevřel cestu na tenhle stroj. Zdůvodnění je v Kopii, na kterou dosáhne telefon, nespouštěj; samotný krok je jeden příkaz:

node install.js ~/.local/ClaudeCalendar --program-only

Pak z ní udělej tu, kterou voláš, a nasměruj ji na data. Stačí funkce v shellu:

# ~/.bashrc, ~/.zshrc
kalendar() {
  CLAUDECALENDAR_DIR=~/ClaudeCalendarData node ~/.local/ClaudeCalendar/ClaudeCalendar.js "$@"
}

Na Windows totéž jako %USERPROFILE%\.local\bin\ClaudeCalendar.cmd:

@echo off
if not defined CLAUDECALENDAR_DIR set "CLAUDECALENDAR_DIR=%~dp0..\ClaudeCalendarData"
node "%~dp0..\ClaudeCalendar\ClaudeCalendar.js" %*

4. Zapiš první událost.

kalendar --add "Zubař" --at "2026-08-05 09:30" --for 45m
kalendar

Zápis se sám commitne a vypíše (committed). Když ten řádek chybí, událost je sice na disku, ale nic se necommitlo — nejspíš to není git repozitář. Commit, který se pokusil a selhal, to řekne; commit, který nikdy nebyl možný, mlčí — takže se hlídá právě ta nepřítomnost.

5. Dej mu privátní remote. Tím se kalendář stane dosažitelným odjinud, a je to krok, který rozhoduje o tom, jestli tvůj diář zůstane tvůj:

cd ~/ClaudeCalendarData
git remote add origin git@github.com:ty/ClaudeCalendarData.git   # privátní!
git push -u origin main

6. Pojisti klíče. Má smysl, až když kalendář má remote, a jen když budeš zapínat sync do Googlu — ale udělej to dřív, než klíče vzniknou, ne potom. .gitignore je drží stranou; tohle je udrží mimo historii i tehdy, kdyby ten řádek někdo odstranil. Hook žije v .git/hooks, takže není součástí repozitáře a nedá se pushnout pryč:

cat > ~/ClaudeCalendarData/.git/hooks/pre-commit <<'EOF'
#!/bin/sh
keys=$(git diff --cached --name-only --diff-filter=ACM | grep -E '(^|/)google-[^/]*\.json$')
if [ -n "$keys" ]; then
	echo "pre-commit: odmitam commitnout Google credentials:" >&2
	echo "$keys" | sed 's/^/  /' >&2
	exit 1
fi
EOF
chmod +x ~/ClaudeCalendarData/.git/hooks/pre-commit

Všechno další je volitelné: notifikace na telefon a držet obě strany v souladu.

Aktualizace

Po stažení nové verze programu spusť instalátor znovu. Přepíše zkopírované soubory programu a ničeho jiného se nedotkne — ani konfigurace, ani skillu, který sis přeformuloval, ani jediné události.

Kopie jsou dvě a zapomenout na tu druhou je snadná chyba: ta uvnitř datového repa je to, co spouští telefon, a ta z kroku 3 je to, co spouští tenhle stroj.

git -C ClaudeCalendar pull
node ClaudeCalendar/install.js ~/ClaudeCalendarData
node ClaudeCalendar/install.js ~/.local/ClaudeCalendar --program-only

Skill pro asistenta

skills/calendar/SKILL.md se do datového repa nainstaluje jako .claude/skills/calendar/SKILL.md a to umístění je záměrné. Skill v ~/.claude/skills by se do cloudového sandboxu nikdy nedostal; tenhle cestuje s klonem, takže i session, která má jen kalendářové repo, se dozví, jak nástroj ovládat — a v sandboxu je to jediná dokumentace, kterou dostane.

Je to výchozí bod, počítá se s tím, že si ho přepíšeš (přeložíš, pojmenuješ kalendáře po svém). Instalátor nikdy nepřepíše skill, který už tam je, a vlastní vynechá, pokud repozitář nějaký učí — pod jakýmkoli jménem.

Z telefonu a z desktopové aplikace

Desktopová aplikace běží na tvém stroji, takže není co zařizovat: otevři datové repo jako pracovní adresář a skill v něm se načte jako každý jiný projektový.

Telefon a webová aplikace jsou ten zajímavý případ a důvod, proč se program do datového repa vůbec kopíruje. Běží v cloudovém sandboxu, který klonuje jediný repozitář, takže ten repozitář musí obsahovat všechno, co session potřebuje: nástroj a skill, který ho vysvětluje.

Dej Claude Code přístup k datovému repozitáři — tomu privátnímu, ne tomuhle — a spusť nad ním session. Pak stačí říct, co chceš:

co mám v úterý? objednej zubaře na úterý na 9:30, na půl hodiny posuň to na desátou

Čerstvý sandbox nemá node_modules, protože v repozitáři nejsou. Spustit nejdřív npm install je úvodní instrukce skillu, takže se o to session postará.

Dvě věci se na té straně chovají jinak:

  • Změna se musí pushnout a skill to bere jako součást úkolu, ne jako úklid po něm. Sandbox se na konci session zahazuje: událost, která zůstala necommitnutá nebo ve větvi, je prostě pryč.
  • V sandboxu nejsou Google klíče — ze stroje záměrně neodcházejí. Překážkou není síť, ale klíče. Událost založená z telefonu je tedy v gitu během pár vteřin, ale v Google kalendáři až po nejbližším běhu úlohy na tvém stroji (viz Držet obě strany v souladu). Pokud čekáš právě na to zazvonění, je to zpoždění, se kterým je potřeba počítat.

Konfigurace

calendar.json leží v datovém adresáři, vedle calendars/. Soubor je volitelný a volitelný je i každý klíč v něm; tohle jsou vestavěné výchozí hodnoty:

{
  "timezone": "Europe/Prague",
  "defaultCalendar": "personal",
  "defaultDuration": 60
}
  • timezone — název IANA zóny. Rozhoduje jak o tom, ve které zóně se zapisují nové události, tak o tom, proti kterým hodinám se čte výpis — takže --today znamená dnešek tam. Události už uložené na disku si nesou vlastní TZID a při změně se nikam nehnou.
  • defaultCalendar — adresář pod calendars/, do kterého --add zapisuje, když není dané --calendar. Tenhle jediný se vytvoří na vyžádání; každý jiný už musí existovat, takže --calendar prace na kalendář, který jsi nikdy nezaložil, je chyba, a ne druhý tiše vytvořený kalendář.
  • defaultDuration — minuty pro časovanou událost, které nebylo dané ani --for, ani --end. Celodenní události to ignorují a trvají jeden den.

Nechat ho dělat práci s gitem

Ve výchozím stavu vypnuté: každý zápis se commitne a zveřejnit ho je na tobě. Dva klíče to předají nástroji:

{
  "git": { "pull": "writes", "push": true, "maxFetchAge": "5m" },
  "sync": { "google": { "afterWrite": true } }
}
  • git.pull"writes" (nebo true) před každým zápisem fetchne a fast-forwardne, protože právě tam se ze zastaralého klonu stává divergence. "always" to dělá i před čtením, ale nejvýš jednou za maxFetchAge, protože fetch stojí kolečko po síti u příkazu, který spouštíš nejčastěji.
  • git.push — pushnout, jakmile se zápis commitl.
  • sync.google.afterWrite — spustit sync-google.js, jakmile je změna uvnitř, aby telefon nečekal na nejbližší běh naplánované úlohy.

Tři pravidla, která to celé dodržuje:

  • Síť nikdy nezastaví lokální operaci. Čtení funguje offline vždycky. Zápis, který nejde pushnout, je pořád zapsaný a commitnutý a řekne, co neprošlo — stejně, jako to už dřív dělal selhaný commit.
  • Dělá fast-forward a nikdy rebase. Přehrávat cizí diář na jinou historii bez dozoru není rozhodnutí, které by měl dělat příkaz kalendáře.
  • Na rozejitých větvích odmítne zápis a při čtení varuje. Číst neúplně se dá; psát na to zvětšuje zmatek. Hláška řekne, co spustit, a výpis se stejně vypíše.

Klíč google konfiguruje samotný sync; viz Notifikace.

Používání

node ClaudeCalendar.js                       # nejbližších 7 dní
node ClaudeCalendar.js --today
node ClaudeCalendar.js --next 24h            # 90m, 6h, 7d, 2w, 3M — velké M jsou měsíce
node ClaudeCalendar.js --date 2026-08-05
node ClaudeCalendar.js --from 2026-08-01 --until 2026-08-31
node ClaudeCalendar.js --search zubar
node ClaudeCalendar.js --calendar prace
node ClaudeCalendar.js --show osobni:3f2a9c1e-…
node ClaudeCalendar.js --show osobni:3f2a9c1e-… --json   # syrová poznámka, k úpravám

Výstup:

# 3 events - next 7d (2026-08-04 09:15 → 2026-08-11 09:15)

2026-08-05 Wed
  09:30-10:15       Zubař
                    @ Vinohradská 12
                    ref=osobni:3f2a9c1e-…

2026-08-10 Mon
  all day →08-14    Dovolená
                    ref=osobni:e2782667-…

Výstup nástroje je anglicky; česky je tahle dokumentace a to, co ti o kalendáři řekne asistent.

ref= identifikuje událost pro --show, --edit a --rm. Je to <kalendář>:<uid> pro celou událost a <kalendář>:<uid>:<YYYYMMDDTHHMMSS> pro jeden výskyt opakované.

Zápis

node ClaudeCalendar.js --add "Zubař" --at "2026-08-05 09:30" --for 45m --location "Vinohradská 12"
node ClaudeCalendar.js --add "Dovolená" --at 2026-08-10 --for 5d          # celodenní
node ClaudeCalendar.js --add "Běh" --at "2026-10-19 07:00" --repeat weekly --count 8
node ClaudeCalendar.js --edit osobni:3f2a9c1e-… --at "2026-08-05 11:00"
node ClaudeCalendar.js --rm osobni:3f2a9c1e-… --yes

Datum bez času (--at 2026-08-10) udělá celodenní událost. --for bere 90m, 2h, 1h30m, 3d. --repeat bere daily, weekly, monthly, yearly, weekdays nebo syrové pravidlo FREQ=….

--alarm 15m připojí připomínku, --alarm none ji zase sundá. Poznámka se dá doplňovat místo přepisování (--desc-append) a jakýkoli text může přijít ze souboru místo přes shell (--desc-file, --desc-append-file, --summary-file) — což je přesně to, co chtějí dlouhé poznámky plné $ a zpětných uvozovek. Zápis vypíše výslednou událost zpátky a upozorní, když nový čas koliduje s něčím, co už tam je:

note: overlaps Stand-up (09:00-09:15)

Kolize je poznámka, ne odmítnutí: jestli dvě věci opravdu běží současně, je na tvém posouzení, ne na nástroji.

Každý zápis se commitne, pokud není dané --no-commit; --dry-run vypíše, co by se stalo, a nezapíše nic.

Jeden výskyt série

Posunout stand-up příští pondělí nesmí posunout všechny, takže ref, který pojmenovává výskyt, se odmítne, dokud --this-one neřekne, že to tak bylo myšleno:

node ClaudeCalendar.js --edit osobni:d90b2a80-…:20261026T070000 --at "2026-10-26 18:00" --this-one
node ClaudeCalendar.js --rm  osobni:d90b2a80-…:20261102T070000 --this-one --yes

První zapíše výjimku (RECURRENCE-ID) vedle hlavní události, druhý přidá EXDATE. Zbytek série zůstane nedotčený.

Čas

Časy se ukládají jako hodinové čtení plus TZID — „09:00 v Europe/Prague" — nikdy jako absolutní okamžik. Právě díky tomu zůstane týdenní schůzka v 9:00 v 9:00 i po změně času: opakování se rozvíjí v hodinových hodnotách a teprve výsledek se převádí na okamžik. Node Intl nese celou databázi časových zón, takže žádný blok VTIMEZONE není potřeba.

Dva důsledky, které je dobré znát:

  • Trvání je hodinové. Událost ve 23:00 v noci, kdy se hodiny vracejí, s --for 2h skončí v 01:00, i když uplynou tři skutečné hodiny.
  • Soubory neobsahují VTIMEZONE. Google i tenhle nástroj čtou TZID bez problémů; přísnější cizí importér by ho možná chtěl dogenerovat.

Notifikace

Git repozitář neumí zazvonit. Notifikace na telefon obstarává sync-google.js, jednosměrný push do vyhrazeného Google kalendáře: každá lokální událost se importuje pod vlastním UID, změněné se přeimportují a události smazané tady se smažou i tam. Nic se nikdy nečte zpátky, takže změnu provedenou v Googlu příští běh vrátí — zdrojem pravdy zůstává repozitář.

ClaudeCalendar.js sám žádné spojení neotevírá. Se zapnutým sync.google.afterWrite spustí tenhle program jako samostatný proces; ten, s vlastními credentials, je tu jediný, kdo mluví s Googlem — a klidně se nemusí používat vůbec.

node sync-google.js --login      # jednou: autorizace v prohlížeči
node sync-google.js --list       # najít id cílového kalendáře
node sync-google.js --dry-run    # co by se pushlo
node sync-google.js              # push

Jak nastavit stranu u Googlu

Ta otravná část, jednou. Celé je to v Google Cloud konzoli a na pořadí záleží víc, než to vypadá — zvlášť u kroku 5.

  1. Nový projekt na https://console.cloud.google.com. Jméno libovolné.

  2. APIs & Services → Library → povolit „Google Calendar API". Bez toho nic nefunguje a chyba, kterou dostaneš, se o tom nezmíní.

  3. Google Auth Platform → Clients → Create client → Desktop app. Desktop, ne Web: jen ten umožňuje loopback redirect použitý níž.

  4. Stáhnout jeho JSON a uložit ho jako google-credentials.json vedle calendar.json.

  5. Google Auth Platform → Audience → Publish app — ještě před přihlášením. Dokud aplikace sedí v Testing, Google zneplatní každý vydaný refresh token po 7 dnech, a publikace až potom token, který už držíš, nespraví. Publikuj první a token vydrží; publikuj potom a sync ti umře o týden později, přičemž to bude vypadat jako cokoli jiného než skutečná příčina.

    Publikovat není totéž co požádat Google o ověření aplikace. Publikovaná neověřená aplikace je v pořádku; jen při přihlášení přidá jednu obrazovku „Google hasn't verified this app", kterou jednou proklikneš přes Advanced. Ověření potřebuješ, jen když chceš tu obrazovku pryč nebo aplikaci pro cizí lidi.

    (Když na Testing trváš, přidej svou adresu pod Audience → Test users, jinak přihlášení skončí na 403 access_denied — a počítej s tím, že krok 7 budeš opakovat každý týden.)

  6. V Google kalendáři založ nový, prázdný kalendář pro tohle. Ne ten, který už používáš: sync maže vzdálené události, které vytvořil.

  7. Autorizuj se a zjisti id:

    node sync-google.js --login    # otevře se prohlížeč, potvrdíš
    node sync-google.js --list     # vypíše kalendáře i s jejich id
  8. Vlož id do calendar.json jako mapování lokálního kalendáře na vzdálený:

    { "google": { "calendars": { "osobni": "…@group.calendar.google.com" } } }

Pak by node sync-google.js --dry-run měl vypsat, co by pushl, a node sync-google.js to pushnout.

Autorizace používá loopback redirect, takže client secret nikdy neopustí stroj. google-credentials.json a google-token.json patří do .gitignore — a k tomu viz krok 6 v Než začneš, kde je hook, který je nepustí do commitu ani tak.

Když sync někdy selže na invalid_grant, token je pryč: obvykle kvůli tomu sedmidennímu pravidlu výš, občas kvůli odvolanému oprávnění. --login to spraví.

Sync si nedrží žádný lokální záznam o tom, co už pushl. Každá importovaná událost je označkovaná privátní značkou a hashem souboru, ze kterého vznikla, a další běh si je vylistuje zpátky, aby zjistil, co se změnilo a co zmizelo. Stavem je tedy sám kalendář: druhý stroj — nebo ten samý po přeinstalaci — naváže tam, kde minulý běh skončil, místo aby importoval celý svět, a není se s čím rozejít.

Ta značka zároveň ohraničuje škody: sweep bere v úvahu jen události, které ji nesou. Cokoli jiného na kalendáři nechá být.

Napojení na kalendář, který už události má

Nový prázdný kalendář je nejčistší start, ale není podmínka. Když máš rozjetý kalendář a chceš ho začít vést odsud, počítej se dvěma věcmi.

Existující události sync nesmaže. Nemají naši značku, takže je sweep vůbec nevidí. To platí i pro události, které si v Googlu založíš později.

Ale sám od sebe je do repa nedostane. Sync čte jedním směrem, takže by ti nástroj o nich nevěděl a na dotaz „co mám v pátek" by sebejistě odpověděl neúplně — což je horší než odmítnout odpovědět. Na to je jednorázový import:

node sync-google.js --import --dry-run          # co by přišlo
node sync-google.js --import                    # od dneška dál
node sync-google.js --import --since 2026-01-01 # i kus historie

Vezme události, které tenhle nástroj nevytvořil, a založí z nich .ics soubory. Nechává si přitom UID, pod kterým je zná Google — díky tomu je nejbližší push pozná jako tytéž události a neudělá druhou kopii. Necommituje: je to hromadný zápis odjinud a zaslouží si, aby ses na něj podíval dřív, než se stane historií.

Co import nevezme: výjimky opakovaných událostí (patří dovnitř souboru hlavní události) a události, jejichž UID by nešlo použít jako název souboru a část refu. Ty vypíše a přeskočí, místo aby je přejmenoval — přejmenované UID je totiž při dalším pushi duplikát.

Je to opravdu jednorázový krok. Průběžně se zpátky nečte nikdy, protože zdrojem pravdy má zůstat repozitář; vzít si kopii jednou, při zakládání, je něco jiného než nechat obě strany zapisovat.

Proč prostě nevystavit .ics a neodebírat ho

Je to první nápad, který každého napadne, tenhle repozitář je .ics souborů plný a nahradilo by to všechno výše jednou URL. Nefunguje to, a to ze tří nezávislých důvodů, z nichž každý sám o sobě stačí:

  • Ta URL by musela být čitelná bez přihlášení. U privátního repozitáře syrové soubory nejsou; u veřejného bys vyvěsil diář na internet. To problém neřeší, jen ho přeformuluje.
  • Odebírané kalendáře se obnovují podle poskytovatele. Google si odebíraný .ics načítá po svém — v hodinách, ne v minutách, a ne když si řekneš. Kalendář, který se o zítřejší schůzce dozví dnes večer, není moc kalendář.
  • Odebírané kalendáře spolehlivě nezvoní. Tenhle důvod o tom rozhoduje. Telefony berou odběr jako referenční materiál, ne jako tvoje vlastní události, a připomínky na nich sahají od nespolehlivých po žádné. Přitom zvonění bylo jediné, kvůli čemu se z repozitáře vůbec někam odchází.

Proto sync tlačí události do kalendáře, který je na druhé straně opravdu tvůj — a tam je připomínka připomínkou.

Pokud chceš totéž bez Googlu, poctivá odpověď je CalDAV — malý vlastní server (Radicale, Baïkal). Tomuhle návrhu sedí dobře: calendars/je rozvržení vdir, které takové servery ukládají nativně, VALARM klienti CalDAVu respektují správně a není tu žádné OAuth ani token, co by vypršel. Cenou je provozovat server a dosáhnout na něj, a proto v repozitáři je ta cesta přes Google.

Držet obě strany v souladu

Událost přidaná z telefonu se dostane do gitu, ale ne ke Google tokenu, který nikdy neopustí stroj, kde leží. Posledním krokem je tedy úloha na tom stroji, spouštěná každou chvíli:

git fetch origin main
git merge --ff-only origin/main    # viz níž
git push origin main
node sync-google.js

--ff-only schválně. Když commitly obě strany, je to divergence, na kterou se má podívat člověk — rebase bez dozoru by potichu přepsal historii kalendáře. Ať se úloha radši zastaví a řekne to.

Se zapnutým git.pull/git.push a sync.google.afterWrite (viz Konfigurace) už ty čtyři řádky běží při každém zápisu na tomhle stroji. Úloha pokrývá ten druhý směr: změnu provedenou jinde, které by si tady jinak nikdo nevšiml, dokud bys kalendář sám nepoužil.

Ze stejného důvodu skill trvá na commitování rovnou do main: úloha sleduje origin/main a nic jiného, takže událost, která zůstala ve větvi nebo v nesloučeném PR, nikdy nezazvoní.

Dej si to do skriptu a spouštěj každou čtvrthodinu. Na Linuxu nebo macOS:

*/15 * * * * CLAUDECALENDAR_DIR=$HOME/ClaudeCalendarData $HOME/.local/bin/calendar-sync.sh >> $HOME/.cache/calendar-sync.log 2>&1

Na Windows naplánovaná úloha mířící na .ps1, který dělá totéž. Dej jí principal Spustit, ať je uživatel přihlášen nebo ne, jinak se pustí jen když jsi u stroje — což je přesně tehdy, kdy ji potřebuješ nejmíň.

Dvě věci, které je potřeba v tom skriptu trefit správně:

  • Program spouštěj z kopie podle kroku 3, ne z té v datovém repu, kterou skript právě stáhl. Jinak je tahle úloha to, co spouští, cokoli tam někdo pushl.
  • Loguj výstup. Sync, který potichu přestal fungovat, vypadá úplně stejně jako kalendář, ve kterém nic není. Právě to, že log při běhu bez změn mlčí, dělá hlučné řádky čitelnými.

Meze

  • Žádný CalDAV, žádné odběry, žádné pozvánky — účastníci ani dotazy na volno/obsazeno nejsou modelované.
  • Jedna připomínka na událost, jako DISPLAY VALARM s pevným odstupem před začátkem. Alarm vztažený ke konci nebo opakovaný modelovaný není.
  • Opakovaná událost se rozvíjí nejvýš do 10 000 výskytů na jeden dotaz.

Licence

MIT — viz LICENSE. Jediná běhová závislost, ical.js, je pod Mozilla Public License 2.0 a používá se nemodifikovaná.

About

Git-backed calendar for Claude Code

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages