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.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