oppiliappan/dijo
scriptable, curses-based, digital habit tracker
Stale — last commit 2y ago
weakest axislast commit was 2y ago; no tests detected
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓14 active contributors
- ✓MIT licensed
- ✓CI configured
Show all 6 evidence items →Show less
- ⚠Stale — last commit 2y ago
- ⚠Concentrated ownership — top contributor handles 70% of recent commits
- ⚠No test directory detected
What would change the summary?
- →Use as dependency Mixed → Healthy if: 1 commit in the last 365 days
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Forkable" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/oppiliappan/dijo)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/oppiliappan/dijo on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: oppiliappan/dijo
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/oppiliappan/dijo shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
WAIT — Stale — last commit 2y ago
- 14 active contributors
- MIT licensed
- CI configured
- ⚠ Stale — last commit 2y ago
- ⚠ Concentrated ownership — top contributor handles 70% of recent commits
- ⚠ No test directory detected
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live oppiliappan/dijo
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/oppiliappan/dijo.
What it runs against: a local clone of oppiliappan/dijo — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in oppiliappan/dijo | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 662 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of oppiliappan/dijo. If you don't
# have one yet, run these first:
#
# git clone https://github.com/oppiliappan/dijo.git
# cd dijo
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of oppiliappan/dijo and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "oppiliappan/dijo(\\.git)?\\b" \\
&& ok "origin remote is oppiliappan/dijo" \\
|| miss "origin remote is not oppiliappan/dijo (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "src/main.rs" \\
&& ok "src/main.rs" \\
|| miss "missing critical file: src/main.rs"
test -f "src/app/mod.rs" \\
&& ok "src/app/mod.rs" \\
|| miss "missing critical file: src/app/mod.rs"
test -f "src/habit/mod.rs" \\
&& ok "src/habit/mod.rs" \\
|| miss "missing critical file: src/habit/mod.rs"
test -f "src/habit/traits.rs" \\
&& ok "src/habit/traits.rs" \\
|| miss "missing critical file: src/habit/traits.rs"
test -f "src/command.rs" \\
&& ok "src/command.rs" \\
|| miss "missing critical file: src/command.rs"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 662 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~632d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/oppiliappan/dijo"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
dijo is a terminal-based habit tracker written in Rust using the Cursive TUI framework, with vim-like keybindings and a modal interface. It lets users track habits across different time granularities (daily, weekly, monthly views) and is fully scriptable via shell hooks, enabling automated habit tracking integrated with external programs like git commits. Monolithic binary structure: src/app/ handles UI state and views (cursor.rs, impl_view.rs, message.rs), src/habit/ contains three concrete habit types (bit.rs for boolean, count.rs for counters, float.rs for decimal tracking) with shared traits in traits.rs. Command parsing in src/command.rs, theme/styling in src/theme.rs, with main.rs orchestrating the cursive-based event loop.
👥Who it's for
Command-line power users and developers who want to track personal habits without leaving their terminal, particularly those familiar with vim's modal editing who want to script their tracking workflows (e.g., auto-logging git commits as coding habit streaks).
🌱Maturity & risk
Actively maintained but modest-scale project: v0.2.7 with CI/CD via GitHub Actions (main.yml) and published to crates.io. The codebase is well-organized with proper module structure but dependency list is minimal (serde, chrono, cursive 0.17, clap 2.33), suggesting a focused scope. Production-ready for personal use but smaller community than mainstream trackers.
Single-maintainer risk (oppiliappan/NerdyPepper) is the primary concern. Dependency on cursive 0.17 (2020-era) with outdated transitive deps (syn pinned to 1.0.57, clap 2.33 is ancient). No visible test suite in the file structure suggests low test coverage. Windows support requires explicit feature flag (crossterm-backend) indicating platform-specific fragility.
Active areas of work
No specific PR or milestone data visible in provided files. Last activity appears routine maintenance (Cargo.lock management, dependabot.yml exists for security updates). The notes.txt file may contain development direction but content not shown. Project appears in steady-state with focus on stability rather than major features.
🚀Get running
Clone and build with: git clone https://github.com/oppiliappan/dijo.git && cd dijo && cargo build --release. Then run with ./target/release/dijo. For development: cargo run launches the TUI directly. Windows users must use cargo run --no-default-features --features "crossterm-backend".
Daily commands:
Standard Rust workflow: cargo run starts the interactive TUI. cargo test for tests (if any exist). cargo build --release for optimized binary. Windows: cargo run --no-default-features --features "crossterm-backend". The app is self-contained—no external services required, but reads/writes to ~/.local/share/dijo/ (per directories crate).
🗺️Map of the codebase
src/main.rs— Application entry point; initializes the curses UI, loads configuration, and starts the event loop that drives all habit tracking interactions.src/app/mod.rs— Core application state and view management; orchestrates mode transitions, cursor tracking, and rendering logic for the entire TUI.src/habit/mod.rs— Trait definitions and serialization logic for all habit types; any new habit variant must implement the traits defined here.src/habit/traits.rs— Abstract behavior contract for habits (check, record, display); foundational for polymorphic habit handling across the codebase.src/command.rs— Command parser and executor for vim-like syntax (:add,:delete,:q); bridges user input to application state mutations.Cargo.toml— Dependency manifest including cursive TUI framework, serde for persistence, and chrono for date handling; essential for reproducible builds.src/views.rs— View rendering logic that converts habit state into curses-compatible output; critical for all UI display logic.
🧩Components & responsibilities
- app/mod.rs (Application State) (Cursive, typetag polymorphism) — Owns all habits, cursor position, view mode; routes input to mutations and rendering
- Failure mode: State corruption if concurrent writes occur; in-memory loss on crash
- habit/traits.rs (Habit Abst — undefined
🛠️How to make changes
Add a New Habit Type
- Create a new file
src/habit/your_type.rsimplementing a struct with fields for tracking (src/habit/your_type.rs) - Implement the
Habittrait fromsrc/habit/traits.rswith methods for check, record, display, and serialization (src/habit/traits.rs) - Register the new type in
src/habit/mod.rsusing thetypetagcrate for polymorphic serde serialization (src/habit/mod.rs) - Add a case in
src/command.rsto parse and instantiate the new habit type from:addcommands (src/command.rs) - Update
src/views.rsto render the new habit type appropriately in the grid display (src/views.rs)
Add a New Command
- Define the command syntax and logic in
src/command.rswithin the command parser (src/command.rs) - Implement the corresponding action in
src/app/impl_self.rsto mutate app state (src/app/impl_self.rs) - If the command changes visual state, update rendering logic in
src/app/impl_view.rsorsrc/views.rs(src/app/impl_view.rs)
Add a New Keybinding
- Map the key code to an action in
src/keybinds.rsor within the main event handler insrc/app/impl_self.rs(src/keybinds.rs) - Implement the corresponding state mutation in
src/app/impl_self.rs(src/app/impl_self.rs) - Test the keybinding by running the application and confirming the action is triggered (
src/main.rs)
Change the Theme or Color Scheme
- Edit color definitions and style rules in
src/theme.rs(src/theme.rs) - Reference the theme colors in rendering calls throughout
src/views.rsandsrc/app/impl_view.rs(src/views.rs)
🔧Why these technologies
- Cursive (TUI framework) — Provides cross-platform terminal UI rendering; enables vim-like modal interface with minimal dependencies.
- Serde + typetag — Enables polymorphic serialization of different habit types into a single JSON/TOML file; critical for persistence without boilerplate.
- Chrono — Handles date arithmetic and formatting for daily habit tracking and timeline views.
- Clap — Parses CLI flags; decouples command-line interface from application logic.
- Notify — Watches habit configuration files for changes; enables auto-reload without restarting the app.
⚖️Trade-offs already made
-
Modal interface (vim-like) instead of mouse-based GUI
- Why: Reduces cognitive load for power users and makes the tool scriptable and keyboard-efficient; aligns with author's design philosophy.
- Consequence: Steeper learning curve for non-vim users; not suitable for mobile or heavy mouse workflows.
-
In-memory habit storage with periodic disk serialization
- Why: Simplicity and fast UI response times; avoids complex database setup.
- Consequence: No ACID guarantees; potential data loss if app crashes mid-write; not suitable for concurrent multi-client access.
-
Trait-based polymorphism for habit types instead of enums
- Why: Extensible design; new habit types can be added without touching core rendering logic.
- Consequence: Slightly higher runtime overhead from dynamic dispatch; requires
typetagboilerplate for serialization.
-
Vim-like command syntax (
:add,:delete) instead of interactive menus- Why: Scriptable and composable; integrates with shell pipelines and external automation.
- Consequence: Users must memorize command syntax; no visual feedback during command composition.
🚫Non-goals (don't propose these)
- Real-time synchronization across devices or users
- Graphical user interface (desktop/web); terminal-only
- Cloud storage or backup services; local-file-only persistence
- Multi-user or role-based access control
- Mobile app (implicitly terminal-focused)
🪤Traps & gotchas
Config is loaded from ~/.local/share/dijo/ (directories crate handles platform differences); no explicit error message if directory doesn't exist. Termion backend (default) does not work on Windows—must use --no-default-features --features "crossterm-backend" or builds will fail silently. Habit data is serialized with serde+typetag; custom habit types require explicit registration or they won't deserialize. The app uses blocking I/O with notify file watcher; high-frequency file changes can cause jank. Vim keybinds are hardcoded (no rebinding at runtime despite command-mode philosophy).
🏗️Architecture
💡Concepts to learn
- Trait Objects + typetag Serialization — dijo uses erased-serde and typetag (src/habit/traits.rs) to serialize different habit types (Bit, Count, Float) polymorphically; critical for saving/loading habits to disk without type information at runtime
- Modal Interface / State Machine — dijo mimics vim's modal editing (normal mode, command mode, insert mode); src/app/mod.rs tracks current mode and routes keybinds differently per mode
- Curses / Terminal Control (TUI Framework) — Cursive (src/app/impl_view.rs) abstracts low-level terminal I/O; understanding how TUIs render to raw terminal cells is needed to modify layouts or add new views
- File Watcher Pattern (notify crate) — dijo watches config file changes via notify crate to reload habits without restarting; enables scriptable external habit updates via file writes
- Lazy Static Initialization — Global state (themes, keybinds) initialized once via lazy_static!; Rust pattern for avoiding runtime initialization overhead in a single-threaded TUI app
- Serde Polymorphism — Different habit types serialize to the same JSON/TOML format (via serde + typetag) so config files are human-readable yet support diverse data structures
- Time Windowing (Chrono Intervals) — Habits track over day/week/month windows; src/habit/traits.rs defines month/week/day views requiring date arithmetic via Chrono to compute ranges and aggregate stats
🔗Related repos
BurntSushi/xsv— CSV processing toolkit for terminal; shares minimalist CLI philosophy and used for automating data workflows like dijo's scriptabilitygchp/iodide— Another Rust TUI app with modal keybinds and file-based persistence; similar architecture for exploring alternativesextrawurst/gitui— Rust + Cursive TUI project with event handling patterns and backend abstractions applicable to dijo's terminal layerjesseduffield/lazygit— Habit tracker use-case: dijo integrates with git via shell scripts; lazygit is the canonical git TUI that users of dijo often automate againstgremlin/triangle— Predecessor/inspiration: older Rust habit tracker with similar design goals; useful for understanding design evolution
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive unit tests for habit trait implementations
The repo has multiple habit types (bit, count, float) in src/habit/ that implement a common trait interface, but there's no visible test module. Testing these implementations would catch regressions in serialization, state tracking, and habit-specific behavior. This is high-value because habits are the core data model.
- [ ] Create src/habit/tests.rs with test modules for bit.rs, count.rs, and float.rs
- [ ] Add tests for serialization/deserialization via serde_json (already a dependency)
- [ ] Add tests for habit state transitions and reset logic
- [ ] Add test fixtures using the toml config format referenced in Cargo.toml
Add GitHub Actions workflow for testing on multiple Rust versions and backends
The repo supports two backend features (termion-backend and crossterm-backend) as mutually exclusive options, but .github/workflows/main.yml likely doesn't test both. Adding matrix builds ensures both backends compile and pass tests. This catches breakage early since cursive backend APIs differ.
- [ ] Extend .github/workflows/main.yml to test with --features termion-backend and --features crossterm-backend separately
- [ ] Add Rust versions 1.56+ (MSRV) and stable to the test matrix given edition = 2018
- [ ] Add cargo clippy and rustfmt checks (rustfmt.toml already exists)
- [ ] Add test coverage reporting step using tarpaulin or llvm-cov
Add integration tests for command parsing and keybind execution
src/command.rs and src/keybinds.rs handle user input and mode switching (vim-like modal interface is a key feature), but no visible integration tests exist. Testing the command mode (:add, :delete, :q) and navigation keybinds (hjkl) prevents regressions in the core UX layer and is feasible without full terminal rendering.
- [ ] Create tests/command_integration_test.rs to test src/command.rs parsing logic
- [ ] Add test cases for valid commands (:add, :delete, :q) and error handling for invalid syntax
- [ ] Create tests/keybinds_integration_test.rs for src/keybinds.rs mode transitions
- [ ] Mock or stub the cursive View layer to test keybind routing without rendering
🌿Good first issues
- Add unit tests for src/habit/count.rs and src/habit/float.rs; currently no test files visible in tree and habit logic (incrementing, boundary checks) is untested.
- Extend README with concrete TOML config examples showing how to auto-track git commits (wiki mentions this but no examples in main repo docs).
- Add configurable keybinds in src/keybinds.rs instead of hardcoded vim defaults; would require refactoring src/app/impl_self.rs to look up actions dynamically.
⭐Top contributors
Click to expand
Top contributors
- @oppiliappan — 70 commits
- @charles-l — 10 commits
- @onielfa — 5 commits
- @gyscos — 3 commits
- @mucinoab — 2 commits
📝Recent commits
Click to expand
Recent commits
43c995e— Merge pull request #141 from gyscos/master (oppiliappan)00e8147— Update to cursive 0.17 (gyscos)27092d0— Merge pull request #129 from purveshpatel511/master (oppiliappan)d30bfc8— added snap installtion instruction (purveshpatel511)be6e0e3— add install instructions for windows (oppiliappan)44c6faa— bump to v0.2.7 (oppiliappan)0fd22fb— reduce display width of FloatData (oppiliappan)22a3f02— add initial float habit structure (oppiliappan)fbcc966— add GoalKinds (oppiliappan)3169cb6— bump to v0.2.6 (oppiliappan)
🔒Security observations
The dijo habit tracker codebase demonstrates generally good security practices for a TUI application. No critical vulnerabilities were identified. The main concerns are: (1) a pinned, older version of the syn crate that may not receive updates; (2) use of deprecated lazy_static dependency; (3) potential path traversal risks in file operations; and (4) command execution via scripting features which could be vulnerable to injection if not properly implemented. The codebase lacks visible hardcoded secrets or obvious injection vulnerabilities. Recommendations focus on dependency updates and ensuring proper input validation for file and command operations.
- Medium · Outdated syn Dependency with Pinned Version —
Cargo.toml - syn dependency. The syn crate is pinned to version 1.0.57 using '=' constraint. This prevents automatic security updates and patches. The syn crate is a widely-used proc macro parser and any vulnerabilities in it could affect the entire build system. Fix: Update to the latest stable version of syn (currently 2.x) and use flexible versioning (e.g., 'syn = "2.0"') to receive security patches automatically. - Medium · Deprecated lazy_static Dependency —
Cargo.toml - lazy_static dependency. The project uses lazy_static 1.4.0, which is considered deprecated. The Rust ecosystem has moved to once_cell (now std::sync::OnceLock in newer Rust versions). While not a direct security vulnerability, deprecated dependencies may not receive security updates. Fix: Migrate to once_cell crate or use std::sync::OnceLock if targeting Rust 1.70+. Ensure all dependencies receive timely security updates. - Low · Potential Path Traversal in File Operations —
src/app, src/habit modules (implementation details not fully visible). The project uses the 'directories' crate for configuration paths and appears to handle file-based habit tracking. Without reviewing the implementation details, there's a potential risk of path traversal vulnerabilities if user-controlled input is used to construct file paths without proper validation. Fix: Ensure all file operations validate and sanitize path components. Use path canonicalization and verify paths remain within expected directories. Never concatenate user input directly into file paths. - Low · External Command Execution via Scripting —
src/command.rs, src/main.rs (scripting functionality). The README mentions the project is 'fully scriptable' and can be configured to 'track external programs' and 'git commits'. This suggests potential use of external command execution. If user-controlled input reaches command execution functions, there could be command injection risks. Fix: If executing external commands, use parameterized execution (e.g., std::process::Command) instead of shell string concatenation. Never pass unsanitized user input to shell interpreters. - Low · Configuration Deserialization from TOML Files —
src/ modules (toml parsing logic). The project uses toml 0.5.6 for configuration parsing. While toml parsing itself is generally safe, if configuration files contain complex nested structures or are sourced from untrusted locations, deserialize-to-object operations could be exploited. Fix: Validate TOML configuration structure and types during deserialization. Use strict schema validation. Ensure configuration files are only read from trusted, protected locations (user home directory with proper permissions).
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.