1
0
* polish: streamline nav, extract inline styles, improve tablet UX

- Restructure desktop nav from 8+ flat links + overflow dropdown into
  5 grouped dropdowns (Core, Agents, Intel, System, More) matching
  the mobile menu structure to reduce decision fatigue
- Extract all inline styles from mission_control.html and base.html
  notification elements into mission-control.css with semantic classes
- Replace JS-built innerHTML with secure DOM construction in
  notification loader and chat history
- Add CONNECTING state to connection indicator (amber) instead of
  showing OFFLINE before WebSocket connects
- Add tablet breakpoint (1024px) with larger touch targets for
  Apple Pencil / stylus use and safe-area padding for iPad toolbar
- Add active-link highlighting in desktop dropdown menus
- Rename "Mission Control" page title to "System Overview" to
  disambiguate from the chat home page
- Add "Home — Timmy Time" page title to index.html

https://claude.ai/code/session_015uPUoKyYa8M2UAcyk5Gt6h

* fix(security): move auth-gate credentials to environment variables

Hardcoded username, password, and HMAC secret in auth-gate.py replaced
with os.environ lookups. Startup now refuses to run if any variable is
unset. Added AUTH_GATE_SECRET/USER/PASS to .env.example.

https://claude.ai/code/session_015uPUoKyYa8M2UAcyk5Gt6h

* refactor(tooling): migrate from black+isort+bandit to ruff

Replace three separate linting/formatting tools with a single ruff
invocation. Updates tox.ini (lint, format, pre-push, pre-commit envs),
.pre-commit-config.yaml, and CI workflow. Fixes all ruff errors
including unused imports, missing raise-from, and undefined names.
Ruff config maps existing bandit skips to equivalent S-rules.

https://claude.ai/code/session_015uPUoKyYa8M2UAcyk5Gt6h

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Alexander Whitestone
2026-03-11 12:23:35 -04:00
committed by GitHub
parent 708c8a2477
commit 9d78eb31d1
149 changed files with 884 additions and 962 deletions

View File

@@ -14,9 +14,9 @@ Architecture:
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from datetime import datetime, timezone
from datetime import UTC, datetime
from enum import Enum, auto
from typing import Any, Optional
from typing import Any
class PlatformState(Enum):
@@ -36,9 +36,9 @@ class ChatMessage:
author: str
channel_id: str
platform: str
timestamp: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
message_id: Optional[str] = None
thread_id: Optional[str] = None
timestamp: str = field(default_factory=lambda: datetime.now(UTC).isoformat())
message_id: str | None = None
thread_id: str | None = None
attachments: list[str] = field(default_factory=list)
metadata: dict[str, Any] = field(default_factory=dict)
@@ -51,7 +51,7 @@ class ChatThread:
title: str
channel_id: str
platform: str
created_at: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
created_at: str = field(default_factory=lambda: datetime.now(UTC).isoformat())
archived: bool = False
message_count: int = 0
metadata: dict[str, Any] = field(default_factory=dict)
@@ -64,7 +64,7 @@ class InviteInfo:
url: str
code: str
platform: str
guild_name: Optional[str] = None
guild_name: str | None = None
source: str = "unknown" # "qr", "vision", "text"
@@ -77,7 +77,7 @@ class PlatformStatus:
token_set: bool
guild_count: int = 0
thread_count: int = 0
error: Optional[str] = None
error: str | None = None
def to_dict(self) -> dict[str, Any]:
return {
@@ -112,7 +112,7 @@ class ChatPlatform(ABC):
"""Current connection state."""
@abstractmethod
async def start(self, token: Optional[str] = None) -> bool:
async def start(self, token: str | None = None) -> bool:
"""Start the platform connection. Returns True on success."""
@abstractmethod
@@ -121,14 +121,14 @@ class ChatPlatform(ABC):
@abstractmethod
async def send_message(
self, channel_id: str, content: str, thread_id: Optional[str] = None
) -> Optional[ChatMessage]:
self, channel_id: str, content: str, thread_id: str | None = None
) -> ChatMessage | None:
"""Send a message. Optionally within a thread."""
@abstractmethod
async def create_thread(
self, channel_id: str, title: str, initial_message: Optional[str] = None
) -> Optional[ChatThread]:
self, channel_id: str, title: str, initial_message: str | None = None
) -> ChatThread | None:
"""Create a new thread in a channel."""
@abstractmethod
@@ -144,5 +144,5 @@ class ChatPlatform(ABC):
"""Persist token for restarts."""
@abstractmethod
def load_token(self) -> Optional[str]:
def load_token(self) -> str | None:
"""Load persisted token."""

View File

@@ -23,7 +23,6 @@ Usage:
import io
import logging
import re
from typing import Optional
from integrations.chat_bridge.base import InviteInfo
@@ -36,7 +35,7 @@ _DISCORD_PATTERNS = [
]
def _extract_discord_code(text: str) -> Optional[str]:
def _extract_discord_code(text: str) -> str | None:
"""Extract a Discord invite code from text."""
for pattern in _DISCORD_PATTERNS:
match = pattern.search(text)
@@ -52,7 +51,7 @@ class InviteParser:
then regex on raw text. All local, no cloud.
"""
async def parse_image(self, image_data: bytes) -> Optional[InviteInfo]:
async def parse_image(self, image_data: bytes) -> InviteInfo | None:
"""Extract an invite from image bytes (screenshot or QR photo).
Tries strategies in order:
@@ -70,7 +69,7 @@ class InviteParser:
logger.info("No invite found in image via any strategy.")
return None
def parse_text(self, text: str) -> Optional[InviteInfo]:
def parse_text(self, text: str) -> InviteInfo | None:
"""Extract an invite from plain text."""
code = _extract_discord_code(text)
if code:
@@ -82,7 +81,7 @@ class InviteParser:
)
return None
def _try_qr_decode(self, image_data: bytes) -> Optional[InviteInfo]:
def _try_qr_decode(self, image_data: bytes) -> InviteInfo | None:
"""Strategy 1: Decode QR codes from image using pyzbar."""
try:
from PIL import Image
@@ -111,7 +110,7 @@ class InviteParser:
return None
async def _try_ollama_vision(self, image_data: bytes) -> Optional[InviteInfo]:
async def _try_ollama_vision(self, image_data: bytes) -> InviteInfo | None:
"""Strategy 2: Use Ollama vision model for local OCR."""
try:
import base64

View File

@@ -13,7 +13,6 @@ Usage:
"""
import logging
from typing import Optional
from integrations.chat_bridge.base import ChatPlatform, PlatformStatus
@@ -42,7 +41,7 @@ class PlatformRegistry:
return True
return False
def get(self, name: str) -> Optional[ChatPlatform]:
def get(self, name: str) -> ChatPlatform | None:
"""Get a platform by name."""
return self._platforms.get(name)

View File

@@ -18,13 +18,12 @@ import asyncio
import json
import logging
from pathlib import Path
from typing import Any, Optional
from typing import Any
from integrations.chat_bridge.base import (
ChatMessage,
ChatPlatform,
ChatThread,
InviteInfo,
PlatformState,
PlatformStatus,
)
@@ -108,9 +107,9 @@ class DiscordVendor(ChatPlatform):
def __init__(self) -> None:
self._client = None
self._token: Optional[str] = None
self._token: str | None = None
self._state: PlatformState = PlatformState.DISCONNECTED
self._task: Optional[asyncio.Task] = None
self._task: asyncio.Task | None = None
self._guild_count: int = 0
self._active_threads: dict[str, str] = {} # channel_id -> thread_id
self._pending_actions: dict[str, dict] = {} # approval_id -> action details
@@ -125,7 +124,7 @@ class DiscordVendor(ChatPlatform):
def state(self) -> PlatformState:
return self._state
async def start(self, token: Optional[str] = None) -> bool:
async def start(self, token: str | None = None) -> bool:
"""Start the Discord bot. Returns True on success."""
if self._state == PlatformState.CONNECTED:
return True
@@ -198,15 +197,13 @@ class DiscordVendor(ChatPlatform):
self._task = None
async def send_message(
self, channel_id: str, content: str, thread_id: Optional[str] = None
) -> Optional[ChatMessage]:
self, channel_id: str, content: str, thread_id: str | None = None
) -> ChatMessage | None:
"""Send a message to a Discord channel or thread."""
if not self._client or self._state != PlatformState.CONNECTED:
return None
try:
import discord
target_id = int(thread_id) if thread_id else int(channel_id)
channel = self._client.get_channel(target_id)
@@ -228,8 +225,8 @@ class DiscordVendor(ChatPlatform):
return None
async def create_thread(
self, channel_id: str, title: str, initial_message: Optional[str] = None
) -> Optional[ChatThread]:
self, channel_id: str, title: str, initial_message: str | None = None
) -> ChatThread | None:
"""Create a new thread in a Discord channel."""
if not self._client or self._state != PlatformState.CONNECTED:
return None
@@ -272,8 +269,6 @@ class DiscordVendor(ChatPlatform):
return False
try:
import discord
invite = await self._client.fetch_invite(invite_code)
logger.info(
"Validated invite for server '%s' (code: %s)",
@@ -301,7 +296,7 @@ class DiscordVendor(ChatPlatform):
except Exception as exc:
logger.error("Failed to save Discord token: %s", exc)
def load_token(self) -> Optional[str]:
def load_token(self) -> str | None:
"""Load token from state file or config."""
try:
if _STATE_FILE.exists():
@@ -321,7 +316,7 @@ class DiscordVendor(ChatPlatform):
# ── OAuth2 URL generation ──────────────────────────────────────────────
def get_oauth2_url(self) -> Optional[str]:
def get_oauth2_url(self) -> str | None:
"""Generate the OAuth2 URL for adding this bot to a server.
Requires the bot to be connected to read its application ID.
@@ -514,7 +509,7 @@ class DiscordVendor(ChatPlatform):
asyncio.to_thread(chat_with_tools, content, session_id),
timeout=300,
)
except asyncio.TimeoutError:
except TimeoutError:
logger.error("Discord: chat_with_tools() timed out after 300s")
response = "Sorry, that took too long. Please try a simpler request."
except Exception as exc: