Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
da20cb8
Fix weather geolocation and make word of day fully offline
bgreg Dec 22, 2025
40c72f6
Initial plan
Copilot Dec 22, 2025
f15a0e5
Update lib/app/sections/word_of_day.sh
bgreg Dec 22, 2025
f14e2c8
Update goodmorning.sh
bgreg Dec 22, 2025
e988ed1
Initial plan
Copilot Dec 22, 2025
7ab96dc
Update lib/app/sections/word_of_day.sh
bgreg Dec 22, 2025
9e44650
Initial plan
Copilot Dec 22, 2025
ada7e51
Update lib/app/display.sh
bgreg Dec 22, 2025
fd2af2d
Update lib/app/sections/word_of_day.sh
bgreg Dec 22, 2025
e6faffc
Initial plan
Copilot Dec 22, 2025
954558b
Update lib/app/sections/word_of_day.sh
bgreg Dec 22, 2025
9a3b70d
Add documentation for --section flag in README
Copilot Dec 22, 2025
484601f
Make zparseopts section extraction more robust
Copilot Dec 22, 2025
18ebb03
Optimize word of day fallback to use single Swift invocation
Copilot Dec 22, 2025
4d61912
Address code review feedback: fix whitespace and improve parsing
Copilot Dec 22, 2025
0708c13
Merge pull request #2 from bgreg/copilot/sub-pr-1
bgreg Dec 22, 2025
b00d902
Improve awk parsing patterns for better robustness
Copilot Dec 22, 2025
0861841
Merge pull request #3 from bgreg/copilot/sub-pr-1-again
bgreg Dec 22, 2025
15d9ad4
Replace grep-based weather tests with runtime behavior tests
Copilot Dec 22, 2025
0643f38
Add validation to basic mock to verify curl command is passed
Copilot Dec 22, 2025
c66c9b5
Merge pull request #4 from bgreg/copilot/sub-pr-1-another-one
bgreg Dec 22, 2025
b30bc30
Merge pull request #5 from bgreg/copilot/sub-pr-1-yet-again
bgreg Dec 22, 2025
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
38 changes: 34 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,12 +279,41 @@ brew install figlet
### Command Line Options

```bash
./goodmorning.sh # Run with default settings
./goodmorning.sh --noisy # Enable text-to-speech greeting
./goodmorning.sh --doctor # Run system diagnostics and validation
./goodmorning.sh --help # Show help message
./goodmorning.sh # Run with default settings
./goodmorning.sh --noisy # Enable text-to-speech greeting
./goodmorning.sh --doctor # Run system diagnostics and validation
./goodmorning.sh --section cat # Run a single section (skips preflight/updates)
./goodmorning.sh --help # Show help message
```

**Running Individual Sections:**
You can run a single section without preflight checks or background updates:
```bash
./goodmorning.sh --section weather
./goodmorning.sh --section word
./goodmorning.sh --section github-prs
```

Available sections:
- `weather` - Current weather conditions
- `history` - Historical events for today
- `tech-versions` - Latest versions of tools and technologies
- `country` - Random country of the day
- `word` - Word of the day with definition
- `wikipedia` - Wikipedia featured article
- `apod` - NASA Astronomy Picture of the Day
- `cat` - Random cat picture
- `calendar` - Today's calendar events
- `reminders` - Upcoming reminders
- `github` - GitHub notifications
- `github-prs` - GitHub pull requests requiring review
- `github-issues` - GitHub issues assigned to you
- `alias-suggestions` - Shell alias suggestions
- `system-info` - System information and updates
- `learning` - Daily learning resource
- `sanity` - Sanity maintenance tips
- `tips` - AI-powered learning tips

**Text-to-Speech:**
By default, the spoken "Good morning" greeting is disabled. Enable it with:
- Runtime flag: `./goodmorning.sh --noisy`
Expand Down Expand Up @@ -315,6 +344,7 @@ Configuration is managed through environment variables. The setup script handles
| `GOODMORNING_LOGS_DIR` | Directory for log files | `$GOODMORNING_CONFIG_DIR/logs` | No |
| `GOODMORNING_OUTPUT_HISTORY_DIR` | Directory for output history | `$GOODMORNING_CONFIG_DIR/output_history` | No |
| `GOODMORNING_SHOW_WEATHER` | Show weather section | `true` | No |
| `GOODMORNING_WEATHER_LOCATION` | City for weather (e.g., San_Francisco, London) | (none) | **Yes** |
Comment thread
bgreg marked this conversation as resolved.

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation marks GOODMORNING_WEATHER_LOCATION as "Required" with Yes, but this is misleading. The script runs successfully without this variable set - it simply skips the weather display with a helpful message (lib/app/display.sh:39-41).

The "Required" column should be "No" or the description should clarify it's "Required if weather is enabled" to accurately reflect the optional nature of this configuration.

Suggested change
| `GOODMORNING_WEATHER_LOCATION` | City for weather (e.g., San_Francisco, London) | (none) | **Yes** |
| `GOODMORNING_WEATHER_LOCATION` | City for weather (e.g., San_Francisco, London) | (none) | If weather enabled (`GOODMORNING_SHOW_WEATHER=true`) |

Copilot uses AI. Check for mistakes.
| `GOODMORNING_SHOW_HISTORY` | Show history section | `true` | No |
| `GOODMORNING_SHOW_LEARNING` | Show daily learning section | `true` | No |
| `GOODMORNING_SHOW_SANITY` | Show sanity maintenance section | `true` | No |
Expand Down
71 changes: 62 additions & 9 deletions goodmorning.sh
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ OPEN_LINKS="${GOODMORNING_OPEN_LINKS:-true}"

# Feature flags - briefing sections (all enabled by default)
SHOW_WEATHER="${GOODMORNING_SHOW_WEATHER:-true}"
GOODMORNING_WEATHER_LOCATION="${GOODMORNING_WEATHER_LOCATION:-}"
SHOW_HISTORY="${GOODMORNING_SHOW_HISTORY:-true}"
SHOW_TECH_VERSIONS="${GOODMORNING_SHOW_TECH_VERSIONS:-true}"
SHOW_COUNTRY="${GOODMORNING_SHOW_COUNTRY:-true}"
Expand Down Expand Up @@ -221,32 +222,84 @@ _setup_output_history() {
OUTPUT_HISTORY_FILE="$day_dir/goodmorning-${count}.txt"
}

# Run a single section without preflight checks or background updates
_run_single_section() {
local section="$1"

# Map section names to functions
case "$section" in
weather) show_weather ;;
history) show_history ;;
tech-versions) show_tech_versions ;;
country) show_country_of_day ;;
word) show_word_of_day ;;
wikipedia) show_wikipedia_featured ;;
apod) show_apod ;;
cat) show_cat_of_day ;;
calendar) show_calendar ;;
reminders) show_reminders ;;
github) show_github_notifications ;;
github-prs) show_github_prs ;;
github-issues) show_github_issues ;;
alias-suggestions) show_alias_suggestions ;;
system-info) show_system_info ;;
learning) show_daily_learning ;;
sanity) show_sanity_maintenance ;;
tips) show_learning_tips ;;
*)
echo_error "Unknown section: $section"
echo "Run with --help to see available sections"
return 1
;;
esac
}

main() {
# Load zsh utilities module for zparseopts
zmodload zsh/zutil

# Parse command line arguments using zparseopts
local -A opts
zparseopts -D -E -A opts -- \
local -a section_arg
zparseopts -D -E -A opts -section:=section_arg -- \
h -help \
-noisy \
-doctor \
-offline

# Handle help
if [[ -n "${opts[--help]}" || -n "${opts[-h]}" ]]; then
if (( ${+opts[--help]} || ${+opts[-h]} )); then
echo "Usage: goodmorning.sh [OPTIONS]"
show_new_line
echo "Options:"
echo " --noisy Enable text-to-speech greeting"
echo " --doctor Run system diagnostics and validation"
echo " --offline Run in offline mode (skip network features)"
echo " -h, --help Show this help message"
echo " --section NAME Run a single section (skips preflight/updates)"
echo " --noisy Enable text-to-speech greeting"
echo " --doctor Run system diagnostics and validation"
echo " --offline Run in offline mode (skip network features)"
echo " -h, --help Show this help message"
show_new_line
echo "Available sections:"
echo " weather, history, tech-versions, country, word, wikipedia, apod,"
echo " cat, calendar, reminders, github, github-prs, github-issues,"
echo " alias-suggestions, system-info, learning, sanity, tips"
return 0
fi

# Handle single section mode (skip all preflight and run just one function)
if [[ ${#section_arg[@]} -gt 0 ]]; then
# zparseopts stores option name at index 1 and value at index 2
# Use the last element to be more robust against zparseopts behavior changes
if [[ ${#section_arg[@]} -lt 2 ]]; then
echo "Error: --section requires a value" >&2
return 1
fi
local section_name="${section_arg[-1]}"
_run_single_section "$section_name"
return $?
fi

# Handle doctor mode
if [[ -n "${opts[--doctor]}" ]]; then
if (( ${+opts[--doctor]} )); then
# Source validation for doctor functions
if [ -f "$SCRIPT_DIR/lib/validation.sh" ]; then
source "$SCRIPT_DIR/lib/validation.sh"
Expand All @@ -259,12 +312,12 @@ main() {
fi

# Handle offline mode
if [[ -n "${opts[--offline]}" ]]; then
if (( ${+opts[--offline]} )); then
export GOODMORNING_FORCE_OFFLINE=true
fi

# Handle TTS mode
if [[ -n "${opts[--noisy]}" ]]; then
if (( ${+opts[--noisy]} )); then
export GOODMORNING_ENABLE_TTS=true
fi

Expand Down
23 changes: 20 additions & 3 deletions lib/app/display.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,28 @@ show_banner() {

show_weather() {
print_section "🌤️ Weather:" "yellow"
local weather=$(fetch_with_spinner "Fetching weather..." curl -s --max-time 10 "wttr.in/?format=3")
if [ -n "$weather" ]; then
local location="${GOODMORNING_WEATHER_LOCATION:-}"

if [ -z "$location" ]; then
show_setup_message "Set GOODMORNING_WEATHER_LOCATION to your city (e.g., San_Francisco)"
show_new_line
return 0
fi

Comment thread
bgreg marked this conversation as resolved.
# Validate location to avoid unsafe characters in the URL
if ! [[ "$location" =~ ^[A-Za-z0-9_-]+$ ]]; then

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The validation regex allows uppercase letters (A-Z), but the setup script (setup.sh:610) explicitly converts the location to lowercase using ${location_input:l}. This creates an inconsistency where the setup prevents uppercase input from ever being stored, yet the validation allows it.

Either remove A-Z from the regex to match the actual expected format, or remove the lowercase conversion in setup.sh if uppercase should be supported.

Suggested change
if ! [[ "$location" =~ ^[A-Za-z0-9_-]+$ ]]; then
if ! [[ "$location" =~ ^[a-z0-9_-]+$ ]]; then

Copilot uses AI. Check for mistakes.
echo "Invalid weather location: '$location'" >&2
echo "Use only letters, numbers, underscores, and hyphens (e.g., San_Francisco)." >&2
show_new_line
return 1
fi
local url="wttr.in/${location}?format=3&u"
local weather=$(fetch_with_spinner "Fetching weather..." curl -s --max-time 10 "$url")

if [ -n "$weather" ] && [[ "$weather" != "not found:"* ]]; then
echo "$weather"
else
echo "Weather unavailable"
echo "Weather unavailable for: $location"
fi
show_new_line
}
Expand Down
Loading
Loading