83da59fa78
An inline comment on the WATCH_DETAIL value line (e.g. `WATCH_DETAIL=balanced # ...`) parsed as the literal "balanced # ..." value, failed validation, and silently reverted to the default mode. - setup.py: move the allowed-values list off the value line in the env template so the wizard no longer models a trailing inline comment. - config.py: strip a whitespace-preceded inline comment from unquoted values so a stray comment can't silently break selection again (preserves '#' inside quotes / API keys). - SKILL.md: note to write the bare key with no trailing comment. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
75 lines
2.1 KiB
Python
75 lines
2.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Shared /watch configuration helpers."""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from pathlib import Path
|
|
|
|
|
|
CONFIG_DIR = Path.home() / ".config" / "watch"
|
|
CONFIG_FILE = CONFIG_DIR / ".env"
|
|
|
|
DEFAULT_DETAIL = "balanced"
|
|
|
|
DETAILS = {"transcript", "efficient", "balanced", "token-burner"}
|
|
|
|
|
|
def read_env_file(path: Path | None = None) -> dict[str, str]:
|
|
if path is None:
|
|
path = CONFIG_FILE
|
|
values: dict[str, str] = {}
|
|
if not path.exists():
|
|
return values
|
|
try:
|
|
lines = path.read_text(encoding="utf-8").splitlines()
|
|
except OSError:
|
|
return values
|
|
for line in lines:
|
|
raw = line.strip()
|
|
if not raw or raw.startswith("#") or "=" not in raw:
|
|
continue
|
|
key, _, value = raw.partition("=")
|
|
value = value.strip()
|
|
if len(value) >= 2 and value[0] in ('"', "'") and value[-1] == value[0]:
|
|
value = value[1:-1]
|
|
else:
|
|
# Strip an inline comment (a '#' preceded by whitespace) from an
|
|
# unquoted value. Without this, `WATCH_DETAIL=balanced # note`
|
|
# parses as "balanced # note", fails validation, and silently
|
|
# falls back to the default. Keeps '#' inside quotes / API keys.
|
|
for i, ch in enumerate(value):
|
|
if ch == "#" and i > 0 and value[i - 1] in " \t":
|
|
value = value[:i].rstrip()
|
|
break
|
|
values[key.strip()] = value
|
|
return values
|
|
|
|
|
|
def get_config() -> dict[str, object]:
|
|
file_values = read_env_file()
|
|
|
|
detail = (
|
|
os.environ.get("WATCH_DETAIL")
|
|
or file_values.get("WATCH_DETAIL")
|
|
or DEFAULT_DETAIL
|
|
)
|
|
if detail not in DETAILS:
|
|
detail = DEFAULT_DETAIL
|
|
|
|
return {
|
|
"detail": detail,
|
|
"config_file": str(CONFIG_FILE),
|
|
}
|
|
|
|
|
|
def frame_cap(detail: str) -> int | None:
|
|
if detail == "efficient":
|
|
return 50
|
|
if detail == "balanced":
|
|
return 100
|
|
if detail == "token-burner":
|
|
return None
|
|
if detail == "transcript":
|
|
return None
|
|
return 100
|