Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,47 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [7.10.0] — 2026-06-13 — forward-looking schedule layer (`agenda` + dash UPCOMING)

### Added

- **`agenda` command** (`commands/agenda.zsh`) — a forward-looking view of dated
activity across all projects: deadlines, exams, milestones, and recurring blocks.
Buckets results into **OVERDUE / TODAY / THIS WEEK / LATER** with a calm empty
state. Windows: default/`-w` (7d), `today` (0d), `-m` (30d), `--all` (uncapped +
holidays), `--overdue` (overdue only). Category/type filters
(`research`/`general`/`recurring`/`teach`/`dev`/`r`/`quarto`/`apps`) match the
record **type** OR the project category. Aliases: `agt`/`agw`/`agm`.
- **Shared schedule engine** (`lib/schedule.zsh`) — pure-ZSH, no `yq`/atlas required.
Parses an optional `## Schedule:` section in each project's `.STATUS`
(`- <when> | <label> [| <type>]`, where `<when>` is an ISO date or `weekly:<dow>`),
derives teaching dates from `.flow/teach-config.yml` when `yq` is present, expands
`weekly:` recurrences into concrete dates (month/year-rollover safe), and emits
normalized `date|label|type|project|recurrence|source` records. Opportunistically
pushes to atlas (capability-probed, async, silent no-op when unsupported).
- **`dash` UPCOMING section** — surfaces in-window + overdue items after QUICK WINS;
self-suppresses when nothing is scheduled.
- **`morning`/`today`/`week` enrichment** — dated blocks (next-7d + overdue,
"Due today", "This week's deadlines" grouped by weekday) fed by the same engine.
- **Man pages** for `agenda`, `dash`, `morning`, `today`, `week`; `_agenda`
completion; `docs/guides/AGENDA-SCHEDULE-GUIDE.md`.
- **Tests** — `test-schedule`, `test-agenda`, `test-cadence-agenda`, plus a dedicated
`e2e-agenda` (19 behavioral tests) + `dogfood-agenda` (15 structural tests) pair.

### Fixed

- **`dash` aborted in non-TTY / captured-output contexts** — three
`$(( cond ? 's' : '' ))` arithmetic-string ternaries in `_dash_right_now` were
fatal in non-interactive shells, suppressing the dashboard before the UPCOMING
section rendered. Replaced with the safe `$( (( cond )) && echo s )` idiom.
- **Schedule parser** — labels containing `|` are now preserved (sanitized to `/`)
instead of being mis-split into the type field; the type is only taken from the
last field when it is a known token.
- **Holiday filtering** routes through `_schedule_drop_holidays` (type-field match)
instead of a substring `grep`, so labels mentioning "holiday" are no longer hidden.
- **Schedule cache key** now includes `FLOW_PROJECTS_ROOT`, preventing stale results
when the project root changes within a session.

## [7.9.0] — 2026-06-05 — obs dispatcher removed + binary-precedence guard

### Removed
Expand Down
17 changes: 9 additions & 8 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ This file provides guidance to Claude Code when working with code in this reposi
**flow-cli** - Pure ZSH plugin for ADHD-optimized workflow management. Zero dependencies. Standalone (works without Oh-My-Zsh or any plugin manager).

- **Architecture:** Pure ZSH plugin (no Node.js runtime required)
- **Current Version:** v7.9.0
- **Current Version:** v7.10.0
- **Install:** Homebrew (recommended), or any plugin manager
- **Source:** `source /opt/homebrew/opt/flow-cli/flow.plugin.zsh` (via Homebrew)
- **Optional:** Atlas integration for enhanced state management
Expand Down Expand Up @@ -71,7 +71,8 @@ work <project> # Start session (cd + context, no editor)
work <proj> -e # Start session + open $EDITOR
finish [note] # End session (optional commit)
hop <project> # Quick switch (tmux)
dash [category] # Project dashboard
dash [category] # Project dashboard (shows UPCOMING schedule section)
agenda [window] # Forward-looking schedule (today|-w|-m|--all|--overdue|<type|cat>)
catch <text> # Quick capture
js # Just start (auto-picks project)
flow doctor # Health check
Expand Down Expand Up @@ -122,20 +123,20 @@ at <cmd> # Atlas bridge (project intelligence, optional)
```zsh
flow-cli/
├── flow.plugin.zsh # Plugin entry point
├── lib/ # Core libraries (74 files)
├── lib/ # Core libraries (77 files)
│ ├── core.zsh # Colors, logging, utilities
│ ├── git-helpers.zsh # Git integration + smart commits
│ ├── keychain-helpers.zsh # macOS Keychain secrets
│ ├── tui.zsh # Terminal UI components
│ └── dispatchers/ # 14 smart command dispatchers
├── commands/ # 31 command files (work, dash, doctor, teach-*, etc.)
├── commands/ # 32 command files (work, dash, agenda, doctor, teach-*, etc.)
├── setup/ # Installation & setup
├── completions/ # ZSH completions
├── hooks/ # ZSH hooks
├── docs/ # Documentation (MkDocs)
│ └── internal/ # Internal conventions & contributor templates
├── scripts/ # Standalone validators (check-math.zsh)
├── tests/ # 211 test files, 12000+ test functions
├── tests/ # 213 test files, 12000+ test functions
│ └── fixtures/demo-course/ # STAT-101 demo course for E2E
└── .archive/ # Archived Node.js CLI
```
Expand Down Expand Up @@ -180,7 +181,7 @@ flow-cli/

## Testing

**211 test files, 12000+ test functions.** Run: `./tests/run-all.sh` (59/59 passing, 1 expected interactive/tmux timeout) or individual suites in `tests/`.
**213 test files, 12000+ test functions.** Run: `./tests/run-all.sh` (64/64 passing, 1 expected interactive/tmux timeout) or individual suites in `tests/`.

See `docs/guides/TESTING.md` for patterns, mocks, assertions, TDD workflow.

Expand Down Expand Up @@ -214,8 +215,8 @@ export FLOW_FORCE_DISPATCHER_OBS=1 # Force-keep one dispatcher (FLOW_F

## Current Status

**Version:** v7.9.0 | **Tests:** 12000+ (59/59 suite, 1 interactive timeout) | **Docs:** https://Data-Wise.github.io/flow-cli/
**Version:** v7.10.0 | **Tests:** 12000+ (64/64 suite, 1 interactive timeout) | **Docs:** https://Data-Wise.github.io/flow-cli/

---

**Last Updated:** 2026-06-04 (v7.9.0)
**Last Updated:** 2026-06-13 (v7.10.0)
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ flow goal set 3 # Daily target

```bash
dash # What's happening?
agenda # What's due soon? (deadlines, exams, milestones)
why # Where was I?
pick # Choose a project
```
Expand Down
227 changes: 227 additions & 0 deletions commands/agenda.zsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
# commands/agenda.zsh - Forward-looking schedule view
#
# `agenda` surfaces in-window + overdue dated activity across all projects —
# assignment due dates, exam dates, manuscript/grant deadlines, milestones, and
# recurring blocks — grouped into OVERDUE / TODAY / THIS WEEK / LATER buckets.
#
# Driven entirely by the shared engine (lib/schedule.zsh). Works fully without
# atlas and without yq (the teaching path is the only one needing yq). `agenda`
# is a top-level command (auto-loaded), NOT a dispatcher: it is not subject to
# the binary-precedence guard and not in _FLOW_HELP_FUNCTIONS.

# ============================================================================
# AGENDA COMMAND
# ============================================================================

agenda() {
local arg="${1:-}"
local window=$SCHEDULE_DEFAULT_WINDOW
local category=""
local overdue_only=0
local show_all=0

case "$arg" in
-h|--help|help)
_agenda_help
return 0
;;
""|-w|--week)
window=7
;;
today)
window=0
;;
-m|--month)
window=30
;;
--all)
window=3650
show_all=1
;;
--overdue)
overdue_only=1
window=0
;;
dev|r|research|teach|teaching|general|recurring|quarto|apps)
# Filter matches the record's TYPE (research/teaching/general/recurring)
# OR the project's detected category (dev/r/quarto/apps). teach/teaching
# are synonyms — see _schedule_category_match.
category="$arg"
window=7
;;
*)
_flow_log_warning "Unknown agenda option: $arg"
echo ""
_agenda_help
return 1
;;
esac

# Pipeline: collect -> window filter -> sort
local records
records=$(_schedule_collect "$window" "$category" | _schedule_filter_window "$window" | _schedule_sort)

# Holidays are noise unless explicitly requested (--all)
if (( ! show_all )) && [[ -n "$records" ]]; then
records=$(print -r -- "$records" | _schedule_drop_holidays)
fi

# Bucketize (THIS WEEK vs LATER split on the fixed 7-day horizon)
local -a b_overdue=() b_today=() b_week=() b_later=()
local rec date class
if [[ -n "$records" ]]; then
while IFS= read -r rec; do
[[ -z "$rec" ]] && continue
date="${rec%%|*}"
class=$(_schedule_classify "$date" "$SCHEDULE_DEFAULT_WINDOW")
case "$class" in
overdue) b_overdue+=("$rec") ;;
today) b_today+=("$rec") ;;
soon) b_week+=("$rec") ;;
later) b_later+=("$rec") ;;
esac
done <<< "$records"
fi

# --overdue narrows to the overdue bucket only
if (( overdue_only )); then
b_today=(); b_week=(); b_later=()
fi

local total=$(( ${#b_overdue[@]} + ${#b_today[@]} + ${#b_week[@]} + ${#b_later[@]} ))

# Header
echo ""
echo " 📅 ${FLOW_COLORS[bold]}AGENDA${FLOW_COLORS[reset]} ${FLOW_COLORS[muted]}$(_agenda_window_label "$window" "$category" "$overdue_only" "$show_all")${FLOW_COLORS[reset]}"
echo ""

# Calm empty state
if (( total == 0 )); then
echo " ${FLOW_COLORS[success]}📅 Nothing scheduled — clear runway${FLOW_COLORS[reset]}"
echo ""
_flow_schedule_to_atlas
return 0
fi

_agenda_render_bucket "OVERDUE" "${FLOW_COLORS[error]}" b_overdue
_agenda_render_bucket "TODAY" "${FLOW_COLORS[warning]}" b_today
_agenda_render_bucket "THIS WEEK" "${FLOW_COLORS[info]}" b_week
_agenda_render_bucket "LATER" "${FLOW_COLORS[muted]}" b_later

echo " ${FLOW_COLORS[muted]}$total item$( (( total != 1 )) && echo s ) • 'agenda -h' for options${FLOW_COLORS[reset]}"
echo ""

# Opportunistic atlas sync (async, no-op when atlas/schedule absent)
if [[ -n "$records" ]]; then
local -a all_records=("${(@f)records}")
_flow_schedule_to_atlas "${all_records[@]}"
fi
}

# ============================================================================
# RENDER HELPERS
# ============================================================================

# Render one labeled bucket (skips empty buckets). $3 = name of a record array.
_agenda_render_bucket() {
local title="$1" color="$2" arr_name="$3"
local -a items=("${(@P)arr_name}")
(( ${#items[@]} == 0 )) && return 0

echo " ${color}${FLOW_COLORS[bold]}$title${FLOW_COLORS[reset]} ${FLOW_COLORS[muted]}(${#items[@]})${FLOW_COLORS[reset]}"
local rec
for rec in "${items[@]}"; do
_schedule_render_line "$rec"
done
echo ""
}

# Build the dim window/scope label shown beside the AGENDA header.
_agenda_window_label() {
local window="$1" category="$2" overdue_only="$3" show_all="$4"
local label=""

if (( overdue_only )); then
label="overdue only"
elif (( show_all )); then
label="everything"
elif (( window == 0 )); then
label="today"
elif (( window == 7 )); then
label="next 7 days"
elif (( window == 30 )); then
label="next 30 days"
else
label="next ${window} days"
fi

[[ -n "$category" ]] && label="$label • $category"
echo "($label)"
}

# ============================================================================
# ALIASES (avoid `ag` — collides with the silver-searcher binary)
# ============================================================================

agt() { agenda today "$@"; }
agw() { agenda -w "$@"; }
agm() { agenda -m "$@"; }

# ============================================================================
# HELP
# ============================================================================

_agenda_help() {
local _C_BOLD="${_C_BOLD:-\033[1m}"
local _C_NC="${_C_NC:-\033[0m}"
local _C_GREEN="${_C_GREEN:-\033[0;32m}"
local _C_CYAN="${_C_CYAN:-\033[0;36m}"
local _C_BLUE="${_C_BLUE:-\033[0;34m}"
local _C_YELLOW="${_C_YELLOW:-\033[0;33m}"
local _C_DIM="${_C_DIM:-\033[2m}"

echo -e "
${_C_BOLD}╭─────────────────────────────────────────────╮${_C_NC}
${_C_BOLD}│ 📅 AGENDA - Forward-Looking Schedule │${_C_NC}
${_C_BOLD}╰─────────────────────────────────────────────╯${_C_NC}

${_C_BOLD}Usage:${_C_NC} agenda [window | category | --overdue | --all]

${_C_GREEN}🔥 MOST COMMON${_C_NC} ${_C_DIM}(80% of daily use)${_C_NC}:
${_C_CYAN}agenda${_C_NC} Next 7 days + overdue (default)
${_C_CYAN}agenda today${_C_NC} Due today + overdue
${_C_CYAN}agenda --overdue${_C_NC} Overdue items only

${_C_YELLOW}💡 QUICK EXAMPLES${_C_NC}:
${_C_DIM}\$${_C_NC} agenda ${_C_DIM}# This week's dated items${_C_NC}
${_C_DIM}\$${_C_NC} agenda -m ${_C_DIM}# Next 30 days (adds LATER)${_C_NC}
${_C_DIM}\$${_C_NC} agenda research ${_C_DIM}# Filter by item type or project category${_C_NC}
${_C_DIM}\$${_C_NC} agenda --all ${_C_DIM}# Everything, incl. holidays${_C_NC}

${_C_BLUE}📋 WINDOWS${_C_NC}:
${_C_CYAN}(none)${_C_NC}, ${_C_CYAN}-w${_C_NC}, ${_C_CYAN}--week${_C_NC} Next 7 days (default)
${_C_CYAN}today${_C_NC} Today only (+ overdue)
${_C_CYAN}-m${_C_NC}, ${_C_CYAN}--month${_C_NC} Next 30 days
${_C_CYAN}--all${_C_NC} No window cap; includes holidays
${_C_CYAN}--overdue${_C_NC} Overdue items only
${_C_CYAN}-h${_C_NC}, ${_C_CYAN}--help${_C_NC} Show this help

${_C_BLUE}📂 FILTERS${_C_NC} ${_C_DIM}(match item type OR project category)${_C_NC}:
${_C_CYAN}research teaching general recurring${_C_NC} ${_C_DIM}(item type)${_C_NC}
${_C_CYAN}dev r teach quarto apps${_C_NC} ${_C_DIM}(project category)${_C_NC}

${_C_BLUE}⚡ ALIASES${_C_NC}:
${_C_CYAN}agt${_C_NC} = agenda today ${_C_CYAN}agw${_C_NC} = agenda -w ${_C_CYAN}agm${_C_NC} = agenda -m

${_C_BLUE}📝 ADDING ITEMS${_C_NC} ${_C_DIM}(per-project .STATUS)${_C_NC}:
${_C_DIM}## Schedule:${_C_NC}
${_C_DIM}- 2026-06-20 | Submit JRSS-B revision | research${_C_NC}
${_C_DIM}- weekly:fri | Grading window | recurring${_C_NC}
${_C_DIM}Teaching dates derive from .flow/teach-config.yml automatically.${_C_NC}

${_C_BLUE}🎨 ICONS${_C_NC}:
🎓 teaching 🔬 research 📌 general 🔁 recurring 🏖️ holiday

${_C_DIM}See also:${_C_NC} dash help, morning, today, week
"
}
Loading