feat: enhance Mattermost integration with focused context for standups and improved channel filtering
This commit is contained in:
@@ -11,6 +11,8 @@ from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import shlex
|
||||
from datetime import date, datetime, timedelta
|
||||
from pathlib import Path
|
||||
|
||||
@@ -19,6 +21,35 @@ ROOT = Path(__file__).resolve().parents[2]
|
||||
MIRROR_DIR = ROOT / "ai" / "inbox" / "mattermost-mirror"
|
||||
LEGACY_LATEST = ROOT / "ai" / "inbox" / "mattermost-latest.md"
|
||||
LEGACY_GENERATED = ROOT / "scripts" / "mattermost" / "generated" / "mattermost_context.jsonl"
|
||||
LOCAL_ENV = Path(__file__).resolve().parent / ".env"
|
||||
|
||||
|
||||
def load_local_env(path: Path = LOCAL_ENV) -> None:
|
||||
"""Load simple KEY=VALUE pairs from the connector-local .env.
|
||||
|
||||
Existing process environment values win. This keeps the reusable reader
|
||||
project-agnostic while allowing each workspace/profile to provide its own
|
||||
channel filters without hardcoding them in Python.
|
||||
"""
|
||||
if not path.is_file():
|
||||
return
|
||||
for raw_line in path.read_text(encoding="utf-8").splitlines():
|
||||
line = raw_line.strip()
|
||||
if not line or line.startswith("#") or "=" not in line:
|
||||
continue
|
||||
if line.startswith("export "):
|
||||
line = line[len("export ") :].strip()
|
||||
key, value = line.split("=", 1)
|
||||
key = key.strip()
|
||||
value = value.strip()
|
||||
if not key or key in os.environ:
|
||||
continue
|
||||
try:
|
||||
parsed = shlex.split(value, comments=False, posix=True)
|
||||
value = parsed[0] if parsed else ""
|
||||
except ValueError:
|
||||
value = value.strip('"\'')
|
||||
os.environ[key] = value
|
||||
|
||||
|
||||
def previous_workday(today: date) -> date:
|
||||
@@ -54,6 +85,35 @@ def read_jsonl(path: Path) -> list[dict]:
|
||||
return records
|
||||
|
||||
|
||||
def parse_channels(raw: str | None) -> set[str]:
|
||||
if not raw:
|
||||
env_raw = os.getenv("AIW_MATTERMOST_CONTEXT_CHANNELS", "") or os.getenv(
|
||||
"AIW_MATTERMOST_PROJECT_CHANNELS", ""
|
||||
)
|
||||
raw = env_raw
|
||||
if not raw:
|
||||
return set()
|
||||
return {item.strip() for item in raw.split(",") if item.strip()}
|
||||
|
||||
|
||||
def filter_channels(records: list[dict], channels: set[str] | None) -> list[dict]:
|
||||
if not channels:
|
||||
return records
|
||||
lowered = {channel.lower() for channel in channels}
|
||||
return [
|
||||
record
|
||||
for record in records
|
||||
if str(record.get("channel_name", "")).lower() in lowered
|
||||
or str(record.get("channel_id", "")).lower() in lowered
|
||||
]
|
||||
|
||||
|
||||
def trim_records(records: list[dict], limit: int | None) -> list[dict]:
|
||||
if limit is None or limit <= 0 or len(records) <= limit:
|
||||
return records
|
||||
return records[-limit:]
|
||||
|
||||
|
||||
def print_jsonl(records: list[dict]) -> bool:
|
||||
if not records:
|
||||
return False
|
||||
@@ -81,24 +141,46 @@ def mode_latest() -> None:
|
||||
fallback()
|
||||
|
||||
|
||||
def mode_previous_workday(today_raw: str | None) -> None:
|
||||
def print_records_section(title: str, records: list[dict], limit: int | None = None) -> bool:
|
||||
print(title)
|
||||
records = trim_records(records, limit)
|
||||
if print_jsonl(records):
|
||||
return True
|
||||
print("No matching Mattermost mirror records.")
|
||||
return False
|
||||
|
||||
|
||||
def mode_previous_workday(today_raw: str | None, channels: set[str] | None = None, limit: int | None = None) -> None:
|
||||
today = datetime.strptime(today_raw, "%Y-%m-%d").date() if today_raw else date.today()
|
||||
day = previous_workday(today)
|
||||
path = daily_by_date_path(day)
|
||||
print(f"## Mattermost mirror previous-workday context ({day.isoformat()})")
|
||||
records = read_jsonl(path)
|
||||
if print_jsonl(records):
|
||||
records = filter_channels(read_jsonl(path), channels)
|
||||
if print_records_section(f"## Mattermost mirror previous-workday context ({day.isoformat()})", records, limit):
|
||||
return
|
||||
print("No proxy mirror previous-workday context available; falling back to latest context.")
|
||||
mode_latest()
|
||||
if channels:
|
||||
print("Filtered to project channels: " + ", ".join(sorted(channels)))
|
||||
print("No proxy mirror previous-workday project context available; not falling back to broad latest context.")
|
||||
|
||||
|
||||
def mode_standup(today_raw: str | None) -> None:
|
||||
mode_previous_workday(today_raw)
|
||||
print("\n## Mattermost mirror latest context")
|
||||
latest_md = MIRROR_DIR / "latest.md"
|
||||
if latest_md.is_file() and latest_md.stat().st_size > 0:
|
||||
print(latest_md.read_text(encoding="utf-8"))
|
||||
def mode_standup(today_raw: str | None, channels: set[str], limit: int | None) -> None:
|
||||
"""Print a compact standup-focused view.
|
||||
|
||||
Standup mode intentionally avoids latest.md because latest.md is a bounded
|
||||
global window and can contain stale or unrelated channels. Use date-bucketed
|
||||
mirror files filtered to known project channels instead.
|
||||
"""
|
||||
today = datetime.strptime(today_raw, "%Y-%m-%d").date() if today_raw else date.today()
|
||||
day = previous_workday(today)
|
||||
previous_records = filter_channels(read_jsonl(daily_by_date_path(day)), channels)
|
||||
today_records = filter_channels(read_jsonl(daily_by_date_path(today)), channels)
|
||||
|
||||
print("## Mattermost mirror standup context")
|
||||
if channels:
|
||||
print("Filtered to configured context channels: " + ", ".join(sorted(channels)))
|
||||
else:
|
||||
print("No context channel filter configured; using all mirrored date-bucket records.")
|
||||
print_records_section(f"\n### Previous workday ({day.isoformat()})", previous_records, limit)
|
||||
print_records_section(f"\n### Today so far ({today.isoformat()})", today_records, limit)
|
||||
|
||||
|
||||
def mode_focused() -> None:
|
||||
@@ -116,17 +198,27 @@ def mode_focused() -> None:
|
||||
|
||||
|
||||
def main() -> None:
|
||||
load_local_env()
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--mode", choices=["latest", "previous-workday", "standup", "focused"], default="latest")
|
||||
parser.add_argument("--today", default="")
|
||||
parser.add_argument(
|
||||
"--channels",
|
||||
default="",
|
||||
help="Comma-separated channel names or IDs. Defaults to AIW_MATTERMOST_CONTEXT_CHANNELS from environment/.env when set.",
|
||||
)
|
||||
parser.add_argument("--limit", type=int, default=80, help="Max records per section; use 0 for no limit.")
|
||||
args = parser.parse_args()
|
||||
channels = parse_channels(args.channels or None)
|
||||
limit = args.limit if args.limit > 0 else None
|
||||
|
||||
if args.mode == "latest":
|
||||
mode_latest()
|
||||
elif args.mode == "previous-workday":
|
||||
mode_previous_workday(args.today or None)
|
||||
mode_previous_workday(args.today or None, channels=None, limit=limit)
|
||||
elif args.mode == "standup":
|
||||
mode_standup(args.today or None)
|
||||
mode_standup(args.today or None, channels, limit)
|
||||
elif args.mode == "focused":
|
||||
mode_focused()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user