Recurring tasks allow you to define tasks that repeat on a schedule. When you complete a recurring task, Philo automatically recreates it on the next due date.
Add a hashtag to any task to make it recurring:
- [ ] Morning meditation #daily
- [ ] Review weekly goals #weekly
- [ ] Pay rent #monthly
- [ ] Water plants #3days
- [ ] Team sync #2weeks
Supported Patterns
Philo recognizes these recurrence patterns:
Keywords #daily, #weekly, #monthly
Custom Intervals #2days, #3weeks, #2months, etc.
Pattern Matching
The recurrence parser uses this regex:
const RECURRENCE_TAG = /# ( daily | weekly | monthly | ( \d + )( days ? | weeks ? | months ? )) \b / i ;
Examples
Pattern Matches #dailyEvery 1 day #weeklyEvery 7 days #monthlyEvery 30 days #2days or #2dayEvery 2 days #3weeks or #3weekEvery 21 days #2months or #2monthEvery 60 days
Both singular and plural forms are accepted (e.g., #2day and #2days).
Parsing Logic
interface Recurrence {
intervalDays : number ;
tag : string ;
}
export function parseRecurrence ( text : string ) : Recurrence | null {
const match = text . match ( RECURRENCE_TAG );
if ( ! match ) return null ;
const tag = match [ 0 ];
const keyword = match [ 1 ]. toLowerCase ();
// Handle keyword patterns
if ( keyword === "daily" ) return { intervalDays: 1 , tag };
if ( keyword === "weekly" ) return { intervalDays: 7 , tag };
if ( keyword === "monthly" ) return { intervalDays: 30 , tag };
// Handle custom intervals
const n = parseInt ( match [ 2 ], 10 );
const unit = match [ 3 ]. toLowerCase ();
if ( unit . startsWith ( "day" )) return { intervalDays: n , tag };
if ( unit . startsWith ( "week" )) return { intervalDays: n * 7 , tag };
if ( unit . startsWith ( "month" )) return { intervalDays: n * 30 , tag };
return null ;
}
How Recurrence Works
Complete the task
Check off a task with a recurrence tag (e.g., - [x] Exercise #daily)
Task rollover scans
When rolloverTasks() runs, it scans past notes for completed recurring tasks
Calculate next due date
Adds the interval to the completion date
Check if due
If next due date ≤ today, recreates the task as unchecked
Add to today
Prepends the recreated task to today’s note
Implementation
Recurring tasks are handled during the rollover process:
// Extract checked tasks with recurrence tags
function extractCheckedRecurringTasks ( content : string ) : string [] {
const lines = content . split ( " \n " );
const tasks : string [] = [];
for ( const line of lines ) {
const match = line . match ( CHECKED_TASK );
if ( match && parseRecurrence ( match [ 2 ])) {
tasks . push ( match [ 2 ]);
}
}
return tasks ;
}
// During rollover
const checkedRecurring = extractCheckedRecurringTasks ( markdown );
for ( const taskText of checkedRecurring ) {
const recurrence = parseRecurrence ( taskText ) ! ;
const nextDue = addDaysToDate ( date , recurrence . intervalDays );
if ( nextDue <= today && ! taskMap . has ( taskText )) {
taskMap . set ( taskText , { indent: "" , text: taskText });
}
}
Date Calculation
function dateToString ( d : Date ) : string {
return ` ${ d . getFullYear () } - ${ String ( d . getMonth () + 1 ). padStart ( 2 , "0" ) } - ${ String ( d . getDate ()). padStart ( 2 , "0" ) } ` ;
}
function addDaysToDate ( dateStr : string , days : number ) : string {
const d = new Date ( dateStr + "T00:00:00" );
d . setDate ( d . getDate () + days );
return dateToString ( d );
}
Months are approximated as 30 days. For precise monthly recurrence, consider using a calendar-based system.
Behavior Examples
Daily Task
Monday (March 3)
Tuesday (March 4)
- [ x ] Morning meditation #daily
Weekly Task
Monday (March 3)
Next Monday (March 10)
- [ x ] Review weekly goals #weekly
Custom Interval
Monday (March 3)
Thursday (March 6)
- [ x ] Deep work session #3days
Deduplication
Recurring tasks are deduplicated like regular tasks:
if ( nextDue <= today && ! taskMap . has ( taskText )) {
taskMap . set ( taskText , { indent: "" , text: taskText });
}
This prevents the same recurring task from appearing multiple times if rollover runs multiple times.
Best Practices
Be specific Use descriptive task text: Morning workout #daily not just Workout #daily
Choose appropriate intervals Match recurrence to actual needs—don’t make everything daily
Review periodically Check recurring tasks monthly to remove outdated ones
Use consistent tags Stick with the same recurrence tag to track task history
Advanced Patterns
You can combine recurrence with other tags:
- [ ] Gym workout #daily #health #morning
- [ ] Team standup #weekly #work #meeting
Only the recurrence tag affects rollover behavior.
Skipping Recurrence
To stop a recurring task from repeating:
Complete it as usual: - [x] Old habit #daily
Remove the recurrence tag: - [x] Old habit
The task won’t be recreated
Use recurrence tags for habits and routines you want to maintain consistently. For one-time tasks, use regular task lists.
Limitations
Monthly recurrence is approximated as 30 days
Tasks don’t account for weekends or holidays
No support for complex patterns like “every Monday” or “first of the month”
Recurrence is based on completion date, not original due date
Future Enhancements
Potential improvements to the recurrence system:
Calendar-aware monthly recurrence
Weekday-specific patterns (e.g., #weekdays, #mondays)
Skip weekends option
Recurrence based on original schedule vs. completion date
Custom recurrence rules with more complex logic