Changelog¶
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[4.5.9] - 2026-05-31¶
Security¶
- Import data loss fixed —
_sanitize_remindernow preservesatandtimezonefields. Daily-at reminders previously had their schedule silently stripped onkim import, leaving reminders with no schedule at all. - Export file permissions —
kim export --output FILEnow creates the output file with0o600permissions on Unix. Previously the file was world-readable, potentially exposing Slack tokens and webhook URLs on multi-user systems. - Slack webhook HTTPS enforcement — the webhook notifier now refuses to send if the URL does not start with
https://, preventing accidental cleartext transmission of reminder content. - Self-update SHA256 verification — the CI build now generates a
.sha256checksum for each binary asset.kim self-updatedownloads and verifies the checksum before replacing the binary, mitigating supply chain attacks via a compromised GitHub release. - Magic bytes check tightened — the script-update integrity check now matches
b"from "andb"import "instead of the too-looseb"fr"/b"im", which could accept arbitrary text files.
Fixed¶
kim remindcrash on corruptoneshots.json—oneshots.jsonis now validated as alistbefore.append(). A hand-edited or corrupt file containing a JSON object instead of an array caused anAttributeErrorcrash. Fixed at all 4 read sites.kim startwith only one-shots pending — the daemon no longer refuses to start when all recurring reminders are disabled but one-shot reminders are pending.- Uninstall now cancels pending one-shots —
kim uninstallkills sleeping one-shot fork children (Unix) and_remind-firesubprocesses (Windows), reports how many pending one-shots were cancelled, and wipesoneshots.jsonbefore removing files.
Stability¶
- Threading race condition in timezone resolution —
_get_utc_offsetstrategy-3 temporarily mutatesos.environ["TZ"]and callstime.tzset(). This mutation is now wrapped in athreading.Lockwith afinallyblock, preventing a race between the scheduler thread and a concurrent config-reload thread. _kill_remind_fire_orphansfalse positives fixed — the Linux/procscanner now resolves the exe path against the actual kim binary before sendingSIGTERM. The old substring check ("kim" in cmdline and "remind" in cmdline) could match unrelated processes.
Code quality¶
save_configcentralised — single canonical implementation incore.py; the three command modules (management,config,misc) that each maintained a verbatim copy now delegate to it.cmd logsmemory usage — usescollections.deque(maxlen=n)to stream the last N lines without loading the full 5 MB log file into memory.
[4.5.8] - 2026-04-07¶
Fixed¶
kim uninstallnow removes install-script binary —_detect_install_type()verifies the binary on PATH is in pip's RECORD before returning"pip". Orphaned pip metadata from a priorpip installno longer causes the uninstaller to skip~/.local/bin/kim.install.sh --uninstallworks when piped from curl — reads confirmation from/dev/ttyinstead of stdin.- Both shell uninstall paths clean up orphaned pip metadata — silently runs
pip uninstall kim-reminderas part of cleanup.
Added¶
uninstall.sh— standalone uninstall script. Works independently ofinstall.sh, supports-y/--yesto skip confirmation. Usage:curl -fsSL https://raw.githubusercontent.com/pratikwayal01/kim/main/uninstall.sh | bash-y/--yesflag forinstall.sh --uninstallskips the confirmation prompt.
[4.5.7] - 2026-04-07¶
Added¶
kim remove <N>— remove a recurring reminder by 1-based index as shown inkim list. Existing name-based removal still works unchanged.kim listshows#index column — recurring reminders now display a 1-based index so users know what number to pass tokim remove.
[4.5.6] - 2026-04-07¶
Added¶
kim export --oneshots— include pending one-shot reminders in the export (JSON:"oneshots"array; CSV: appended section). Use with-o fileto save to a file.kim import --oneshots— restore pending one-shot reminders from a file produced bykim export --oneshots. Only future fire times are imported; duplicates are skipped.- Help footer now shows
oneshots: ~/.kim/oneshots.jsonalongside the config and log paths.
Fixed¶
kim uninstallnow kills orphanedkim remindfork children — on Linux the uninstall reads/procdirectly to find and SIGTERM every sleepingkim remindchild process. On macOS/other Unix it falls back topkill -f. This prevents a one-shot reminder from firing after kim has been uninstalled.kim uninstallclearsoneshots.jsonbefore removing~/.kim/— even if a fork child survives the SIGTERM (e.g. already woken), the cleared file means it cannot write back or be replayed on any futurekim start.
[4.5.5] - 2026-04-05¶
Fixed¶
- Scheduler race condition —
_wakeup.clear()is now called inside the lock so a wakeup signal set between reading the heap and enteringwait()is never lost. - Scheduler: notifier now runs in a daemon thread — a slow or blocking notifier (e.g. Slack network call) no longer stalls the scheduler loop and delays all future firings.
- One-shot reminders: remove from
_liveafter firing — fired one-shots are now removed from the scheduler's internal_livedict, preventing a memory leak for long-running daemons. - One-shot fork child now calls
remove_oneshot()— after the Unixos.fork()child fires akim remindone-shot, it removes its entry fromoneshots.jsonso the reminder does not re-fire on the next daemon start. oneshots.jsontmp file permissions — all atomic writes tooneshots.json(via.tmprename) nowchmod 0o600the tmp file on Unix before the rename, preventing world-readable exposure.- Negative
sleep_secondson clock jump —kim remindnow clampssleep_secondstomax(0.0, ...)so a backward clock adjustment never causestime.sleep()to crash. kim update --intervalnow validates the value — passing an invalid interval string (e.g.--interval foo) is rejected with an error message instead of silently writing bad data to config.kim validatenow checksatfield format — reminders with anatfield are validated to be inHH:MMformat; invalid values are reported as errors.- Slack webhook response body checked —
_notify_slack_webhooknow reads the response body and logs a warning if it is not"ok"._notify_slack_botreads the JSON response and logs the API error ifokisfalse. kim interactiveedit: clearingatwhen switching to interval — editing a reminder to set an interval now removes any existingatandtimezonekeys, preventing an invalid mixed-schedule state.kim interactiveadd one-shot: urgency no longer hardcoded tocritical— the user is now prompted for urgency (default:normal), consistent withkim remind.load_configprints a warning on JSON corruption — callers (including the daemon and CLI) now see a stderr message when the config file is invalid JSON, not just a silent log entry.kim(bare, no subcommand) now exits 0 — printing help is not an error condition.sound.py:validate_sound_filenow checks read permission — a file that exists but is not readable is rejected with a clear error message.import removed to module level incli.py— the deferredimport reinside_Formatter._format_actions_usageis now a top-level import.import datetimemoved to module level inmisc.py— the deferredimport datetime as _dtinsidecmd_remindis now a top-level import.cmd_removeexception handling narrowed — the bareexcept Exceptionwhen readingoneshots.jsonincmd_removeis nowexcept (json.JSONDecodeError, OSError).- Dead-code removal:
KimScheduler.update_reminderandKimScheduler.disable_reminder(thin wrappers never called externally) removed;core.parse_intervalmarked deprecated (kept for backward compatibility).
[4.5.0] - 2026-04-05¶
Added¶
kim remind --urgency— one-shot reminders now accept--urgency low|normal|critical(default:normal), matchingkim addbehaviour. Urgency is persisted to~/.kim/oneshots.jsonand correctly restored if the daemon restarts while the reminder is pending.
Fixed¶
kim startdaemon detection — replacedINVOCATION_IDenv-var check with a TTY check (/dev/tty). The old check causedkim startto block the terminal on some Linux distros because systemd inheritsINVOCATION_IDto all child shells, not just supervised processes.kim start/kim stopmessages now include PID:kim started. (PID 1234)kim is already running. (PID 1234)kim stopped. (PID 1234)kim is already stopped.kim remindoutput consistent withkim add— confirmation now uses the✓prefix instead of leaking the notification title into the CLI output line._remind-firehidden from usage line — internal subcommand no longer appears inkimusage/help output.--break-system-packagesadded to all pip calls —kim self-updateandkim uninstallnow work correctly on Arch Linux, Debian 12+, Ubuntu 23.04+, and other distros with externally managed Python environments.
Changed¶
- Shell completions for bash, zsh, and fish updated to include
--urgencyforkim remind.
[4.1.8] - 2026-04-05¶
Fixed¶
_find_assetwas orphaned dead code inselfupdate.py— the function body appeared after_parse_version'sreturnstatement making it unreachable, causingNameErrorat runtime duringself-update. Extracted into a proper top-level function.cmd_listdid not displayat-schedule reminders correctly — theINTERVALcolumn fell back to"30 min"for daily-at reminders. Now shows"at HH:MM"and the column is renamedSCHEDULE.- Interactive mode:
cancel_oneshotname-shadowing crash — the local helper insidecmd_interactivewas namedremove_oneshot, shadowing the module-level import of the same name, causing aTypeErrorwhen cancelling a one-shot. Renamed tocancel_oneshot.
Tests¶
- Added 77 new feature tests in
tests/test_features.pycovering:cmd_remind, oneshot load/remove,cmd_list,cmd_status, config reload, sanitize, export/import,_find_asset, scheduler reschedule, parse_interval/parse_datetime edge cases, andcmd_validate. - Added regression tests in
tests/test_regression.pyfor_parse_version, downgrade guard, install-type detection order, interactivecancel_oneshotrename, andcmd_validateat-reminder acceptance.
[4.1.7] - 2026-04-05¶
Added¶
- Interactive mode: List/Add/Remove One-shots — three new menu items in
kim interactivefor managing one-shot reminders without touching the CLI. - Interactive mode:
--at HH:MMschedule — "Add Reminder" now prompts for interval or daily-at schedule type. - Status bar one-shot count — interactive header shows
One-shots: N pendingalongside the reminder count. - Config auto-reload after every action — interactive mode reloads config from disk after each mutation so the display is always current.
Fixed¶
kimhelp output cleanup — removed duplicate positional-args block, replaced long{start,stop,...}metavar with<command>, suppressed internal_remind-firesubcommand from help, removed redundant "Short flags" section, and tightened epilog formatting.- Interactive daemon signal on mutations — all mutating actions (
add,edit,toggle,remove) now call_signal_reload()so the running daemon picks up changes immediately.
[4.1.6] - 2026-04-05¶
Fixed¶
kim uninstallWinError 32 — explicitly close all logging handlers before removing~/.kim, releasing the file lock onkim.logon Windows.kim uninstall"The batch file cannot be found." — the currently-runningkim.batis no longer deleted in-process. A detachedcmdis spawned to remove it ~2 s after the process exits, so cmd.exe can return cleanly.- "Thank you for using kim!" is now always the last line of uninstall output.
[4.1.5] - 2026-04-05¶
Added¶
kim list -o/--oneshots— appends pending one-shot reminders to the list output, showing index, message, fire time, and time remaining.kim remove <index|msg> -o/--oneshot— cancels a pending one-shot by its list index (fromkim list -o) or message substring, instead of removing a config reminder.
Removed¶
kim remind --listandkim remind --cancelflags removed in favour of the cleanerkim list -o/kim remove -ointerface.
[4.1.4] - 2026-04-05¶
Added¶
kim remind --list— show all pending one-shot reminders with index, message, fire time, and time remaining.kim remind --cancel <index|message>— cancel a pending one-shot by its list index (from--list) or a message substring match.
[4.1.3] - 2026-04-05¶
Fixed¶
- Windows script install:
kim startcrash (WinError 193) —_spawn_detached()was callingPopen(sys.argv)which fails whensys.argv[0]is a.pyfile (script install viainstall.ps1). Now prependssys.executablewhen argv[0] ends in.pyon Windows.
[4.1.2] - 2026-04-05¶
Fixed¶
- Windows PATH instructions — README now shows a copy-paste one-liner to fix PATH after
pip install.install.ps1also auto-fixes the pip Scripts PATH for users who already installed via pip.
[4.1.1] - 2026-04-05¶
Fixed¶
- Windows PATH auto-fix — on Windows,
kimnow detects if the pip Scripts directory is missing from the userPATHand adds it automatically viasetxon first run. Users no longer need to manually update their PATH afterpip install kim-reminder.
[4.1.0] - 2026-04-05¶
Added¶
--everyflag — alias for-I/--intervalonkim addandkim update(e.g.kim add "drink water" --every 30m)--at HH:MMonkim add/kim update— schedule a reminder to fire every day at a fixed time (e.g.kim add standup --at 10:00). Mutually exclusive with--interval.--tz TZflag — IANA timezone override for--atandkim remind ... at(e.g.--tz Asia/Kolkata). Defaults to local system timezone.kim remind ... at <datetime>— fire a one-shot reminder at an absolute datetime. Accepts natural language and ISO formats:at 14:30— today at 14:30 (or tomorrow if already past)at tomorrow 10am— tomorrow at 10:00at friday 9am— next Friday at 09:00at 2026-04-06 09:00— specific date and timeparse_datetimeutility — pure stdlib datetime parser incore.pysupporting both relative (in 10m,2h 30m) and absolute (at tomorrow 9am) formatsparse_at_timeutility — validates and normalises--at HH:MMvalues- Scheduler support for daily at-time reminders —
KimSchedulernow handles reminders with anatfield; re-schedules them for the same wall-clock time the next day after firing - Live config reload —
kim add,kim remove,kim enable,kim disable, andkim updatenow take effect in the running daemon within 1 second — no restart needed. Implemented via a~/.kim/kim.reloadflag file polled by the daemon loop. On Linux/macOS,kill -HUP <pid>also triggers a reload.
Changed¶
kim startno longer blocks the terminal — when run interactively it spawns a detached background process and returns immediately. Process supervisors (systemd, launchd, Windows Task Scheduler) are detected automatically and still run in-process.kim startoutput — showskim started. (PID 1234)on fresh start; showskim is already running. (PID 1234)if the daemon is already up.kim stopoutput — showskim stopped. (PID 1234)on success; showskim is already stopped.if not running.kim statusrunning line — now shows● kim running (PID 1234)for consistency with start/stop output.kim remindtime-parsing refactored to useparse_datetime(behaviour-compatible for existing relative syntax)
[4.0.0] - 2026-04-02¶
Added¶
- Per-reminder sound overrides — each reminder can specify its own
sound_fileor disable sound - Per-reminder Slack overrides — route individual reminders to different Slack channels or webhooks
- Log rotation —
RotatingFileHandlerwith 5 MB max, 3 backups (prevents unbounded log growth) - Full shell completions — bash/zsh/fish now complete subcommand flags, reminder names, and file paths
- One-shot reminder persistence docs — documented
~/.kim/oneshots.jsonformat and behavior - Interval seconds support —
90snow a valid interval format alongside30m,1h,1d
Changed¶
- Atomic PID file writes — writes to
.tmpthen renames, eliminating partial-write corruption - Direct Python spawn on Windows — one-shot reminders no longer go through PowerShell (saves 1-2s startup overhead)
kim update --enable/--disable— fixed broken flag logic (was usingis not Noneon booleans)kim update -I/--interval— changed from int to string to matchkim add(accepts30m,1h, etc.)kim remindparser — bare numbers treated as minutes, max duration clamped to 365 dayskimwith no command — now exits with code 1 instead of silently returning
Fixed¶
- Critical: Slack bot urgency bug —
_notify_slack_bot()referenced undefinedurgencyvariable (NameError crash) - Critical: Slack emoji bug —
emoji = urgency_emoji.get("normal", ":bell:")hardcoded "normal" instead of using actual urgency - Critical: Missing platform sound functions —
_play_system_sound_mac()and_play_system_sound_linux()were called but never defined (crash on default sound) - PID file TOCTOU race — stale PIDs from crashed processes caused false "already running" errors; now verifies process is alive
- Config shallow copy bug —
DEFAULT_CONFIG.copy()shared nested dict references between callers; now usescopy.deepcopy() - Config defaults missing —
load_config()now fills in all missingsound,sound_file, andslacksub-fields - Interval parser edge cases — empty string, zero, and negative values now return safe default (30 min) instead of crashing
- One-shot child process crash — forked child on Unix now wrapped in
try/except/finallyto prevent silent daemon crashes - One-shot Windows spawn — added
FileNotFoundErrorhandling for missing PowerShell - Startup notification crash — wrapped in
try/exceptso notification failure doesn't kill the daemon - Config write permissions — all 8+ config write paths now set
0o600on Unix and useencoding="utf-8" - F-string log calls — 26 instances converted to
%-formattingfor lazy evaluation (no string formatting when log level is suppressed) - Redundant imports — removed duplicate
import urllib.request/import urllib.errorinside functions - Dead code — removed
platform_notifier(),start_daemon()shim,--HELPpseudo-flag,-V/--VERSIONaliases - Fish completion — removed broken shebang, added full subcommand flag completions
- Bash/Zsh completions — removed broken shebangs, added
remindand_remind-firecommands - Duplicate FISH_COMPLETION — removed second definition that overwrote the first
Removed¶
- Non-standard CLI flags:
-V,--VERSION,--HELP - Stale "Config changes are detected automatically" claim from README (no file watcher exists)
- Completed roadmap items (all features now shipped)
Security¶
- All config writes set
0o600permissions on Unix (Slack tokens protected) - PID file written atomically with
0o600permissions - Log file set to
0o600on Unix - Secrets (webhook URLs, bot tokens) never appear in log messages
[3.0.0] - 2026-03-28¶
Added¶
- Modular package structure (multiple folders)
- Cross-platform symbol compatibility (ASCII fallbacks for Windows)
- Unit tests for all platforms
- GitHub Actions CI/CD pipeline
- Documentation website (MkDocs Material theme)
- Comprehensive test suite with CI on Windows, macOS, Linux
- Updated installers to download entire package
- Case-insensitive commands and flags (e.g.,
STATUS,StAtUs) -v/-V/--VERSION/--HELPaliases for version and help
Changed¶
- Refactored monolithic
kim.pyinto focused modules - Improved Windows console compatibility
- Updated test suite to work across OS
- Separated CLI, commands, scheduler, notifications, sound, etc.
Fixed¶
- Unicode encoding errors on Windows console
- Box drawing character rendering in tests
- File locking issues in tests on Windows
- Installer now downloads complete package structure
[2.1.0] - 2026-03-28¶
Added¶
- One-shot reminders (
kim remind) - Interactive mode TUI
- Self-update command
- Uninstall command
- Export/import functionality
- Config validation
- Slack integration (webhook and bot)
- Custom sound files
- Shell completions (bash, zsh, fish)
- Memory-optimized heapq scheduler
Changed¶
- Single-threaded scheduler replaces per-reminder threads
- Platform-specific notification backends
- Config auto-reload
Fixed¶
- Windows PowerShell toast notifications
- macOS notification sound handling
- Linux environment variables for notifications
[2.0.0] - 2026-02-15¶
Added¶
- Cross-platform daemon (Linux systemd, macOS launchd, Windows Task Scheduler)
- Config-driven reminders
- Platform notifications (notify-send, osascript, PowerShell toast)
- Sound notifications
- Logging system
- PID file management
Changed¶
- Complete rewrite in Python
- Pure stdlib (no external dependencies)
[1.0.0] - 2026-01-01¶
Added¶
- Initial release
- Basic reminder functionality
- Linux support only
Versioning¶
Given a version number MAJOR.MINOR.PATCH:
- MAJOR: Incompatible API changes
- MINOR: Add functionality in a backward-compatible manner
- PATCH: Backward-compatible bug fixes