66import sys
77from zoneinfo import ZoneInfo
88
9+ from pathlib import Path
10+
911from limitless_tools .config .config import default_config_path , get_profile , load_config
1012from limitless_tools .config .env import load_env
1113from limitless_tools .config .logging import setup_logging
12- from limitless_tools .config .paths import default_data_dir
14+ from limitless_tools .config .paths import default_data_dir , expand_path
1315from limitless_tools .services .lifelog_service import LifelogService
1416
1517
@@ -93,6 +95,14 @@ def _build_parser() -> argparse.ArgumentParser:
9395 return parser
9496
9597
98+ def _normalize_data_dir (value : str | None , * , base_dir : str | None = None ) -> str :
99+ """Return a data_dir resolved relative to an optional base directory."""
100+ normalized = expand_path (value , base_dir = base_dir )
101+ if normalized :
102+ return normalized
103+ return default_data_dir ()
104+
105+
96106def main (argv : list [str ] | None = None ) -> int :
97107 # Ensure .env and related environment variables are loaded before parsing
98108 load_env ()
@@ -108,11 +118,13 @@ def main(argv: list[str] | None = None) -> int:
108118
109119 # Load config and resolve profile
110120 # Allow env var overrides for config path and profile
111- config_path = args .config or os .getenv ("LIMITLESS_CONFIG" )
121+ config_path_arg = args .config or os .getenv ("LIMITLESS_CONFIG" )
122+ resolved_config_path = expand_path (config_path_arg ) or default_config_path ()
112123 profile_name = args .profile or os .getenv ("LIMITLESS_PROFILE" ) or "default"
113124
114- cfg = load_config (config_path )
125+ cfg = load_config (resolved_config_path )
115126 prof = get_profile (cfg , profile_name )
127+ config_base_dir = str (Path (resolved_config_path ).expanduser ().parent )
116128
117129 # Precedence: CLI flags > environment variables > config > defaults
118130 argv_list = argv or []
@@ -121,9 +133,11 @@ def _provided(opt: str) -> bool:
121133 return opt in argv_list
122134
123135 # data_dir precedence
136+ data_dir_from_config = False
124137 if not _provided ("--data-dir" ) and not os .getenv ("LIMITLESS_DATA_DIR" ):
125138 if isinstance (prof .get ("data_dir" ), str ):
126139 setattr (args , "data_dir" , prof ["data_dir" ])
140+ data_dir_from_config = True
127141
128142 # batch_size precedence for fetch/sync
129143 if not _provided ("--batch-size" ) and isinstance (prof .get ("batch_size" ), (int , float )):
@@ -139,6 +153,11 @@ def _provided(opt: str) -> bool:
139153 resolved_api_key = os .getenv ("LIMITLESS_API_KEY" ) or (prof .get ("api_key" ) if isinstance (prof .get ("api_key" ), str ) else None )
140154 resolved_api_url = os .getenv ("LIMITLESS_API_URL" ) or (prof .get ("api_url" ) if isinstance (prof .get ("api_url" ), str ) else None )
141155
156+ args .data_dir = _normalize_data_dir (
157+ getattr (args , "data_dir" , None ),
158+ base_dir = config_base_dir if data_dir_from_config else None ,
159+ )
160+
142161 if args .command == "fetch" :
143162 service = LifelogService (
144163 api_key = resolved_api_key ,
@@ -257,7 +276,11 @@ def _provided(opt: str) -> bool:
257276 # Combined per-date export to a single file
258277 if args .combine :
259278 # Determine effective output directory: CLI --write-dir > config profile output_dir
260- cfg_output_dir = prof .get ("output_dir" ) if isinstance (prof .get ("output_dir" ), str ) else None
279+ cfg_output_dir = (
280+ expand_path (prof .get ("output_dir" ), base_dir = config_base_dir )
281+ if isinstance (prof .get ("output_dir" ), str )
282+ else None
283+ )
261284 eff_write_dir = args .write_dir or cfg_output_dir
262285 if not args .date or not eff_write_dir :
263286 sys .stderr .write ("--combine requires --date and a write directory (provide --write-dir or set output_dir in config)\n " )
@@ -283,7 +306,11 @@ def _provided(opt: str) -> bool:
283306 )
284307 csv_text = service .export_csv (date = args .date , include_markdown = bool (getattr (args , "include_markdown" , False )))
285308 # Determine effective output file: CLI --output > config profile output_dir + default filename; else stdout
286- cfg_output_dir = prof .get ("output_dir" ) if isinstance (prof .get ("output_dir" ), str ) else None
309+ cfg_output_dir = (
310+ expand_path (prof .get ("output_dir" ), base_dir = config_base_dir )
311+ if isinstance (prof .get ("output_dir" ), str )
312+ else None
313+ )
287314 eff_output = getattr (args , "output" , None )
288315 if not eff_output and cfg_output_dir :
289316 import os as _os
@@ -327,7 +354,7 @@ def _provided(opt: str) -> bool:
327354 if args .command == "configure" :
328355 # Compute target config path and profile
329356 from limitless_tools .config .config import load_config as _load_cfg , save_config as _save_cfg
330- target_path = config_path or default_config_path ()
357+ target_path = resolved_config_path
331358 target_profile = profile_name
332359 # Load existing config
333360 current = _load_cfg (target_path )
0 commit comments