When the user clicks Report Issue in the system tray, the diagnostics
output and a sanitized log excerpt are automatically collected and pre-filled
into the GitHub issue template (diagnostics and logs fields).
If the resulting URL exceeds the browser length limit, the log excerpt is
dropped to keep the URL valid.
defcollect_diagnostics(*,aw_host:str,data_dir:str,models_dir:str,include_logs:bool=False,log_lines:int=50,)->dict[str,object]:"""Gather environment and runtime info for bug reports. Returns a dict with sections that can be serialised to JSON or pretty-printed for human consumption. """fromtaskclf.core.configimportUserConfigfromtaskclf.core.pathsimporttaskclf_homefromtaskclf.model_registryimportlist_bundleshome=taskclf_home()info:dict[str,object]={}try:info["taskclf_version"]=_pkg_version("taskclf")exceptException:info["taskclf_version"]="unknown"info["python_version"]=sys.versioninfo["os"]=platform.platform()info["architecture"]=platform.machine()info["taskclf_home"]=str(home)# -- ActivityWatch reachability ---aw_url=f"{aw_host.rstrip('/')}/api/0/info"try:withurllib.request.urlopen(aw_url,timeout=5)asresp:info["activitywatch"]={"reachable":True,"status":resp.status}exceptExceptionasexc:info["activitywatch"]={"reachable":False,"error":str(exc)}# -- Model bundles ---bundles=list_bundles(Path(models_dir))info["model_bundles"]=[{"model_id":b.model_id,"valid":b.valid,"created_at":b.created_at.isoformat()ifb.created_atelseNone,}forbinbundles]# -- Config (redact user_id) ---try:cfg=UserConfig(data_dir).as_dict()cfg["user_id"]="[REDACTED]"info["config"]=cfgexceptExceptionasexc:info["config"]={"error":str(exc)}# -- Disk usage ---def_dir_size(p:Path)->int|None:ifnotp.is_dir():returnNonereturnsum(f.stat().st_sizeforfinp.rglob("*")iff.is_file())info["disk_usage"]={"data":_dir_size(home/"data"),"models":_dir_size(home/"models"),"logs":_dir_size(home/"logs"),}# -- Log tail ---ifinclude_logs:log_file=home/"logs"/"taskclf.log"iflog_file.is_file():try:all_lines=log_file.read_text("utf-8").splitlines()info["log_tail"]=all_lines[-log_lines:]exceptException:info["log_tail"]=["<unable to read log file>"]else:info["log_tail"]=["<log file not found>"]returninfo