mirror of
https://github.com/usestrix/strix.git
synced 2026-08-25 04:12:37 +02:00
Every scan now writes a complete log file at ``{run_dir}/strix.log``
captured from the moment ``run_dir`` is resolved through teardown.
Stdlib ``logging`` only — no parallel framework.
New ``strix/telemetry/logging.py``:
* ``setup_scan_logging(run_dir, debug=)`` attaches a ``FileHandler``
(DEBUG, all ``strix.*``) plus a ``StreamHandler`` (ERROR by
default; DEBUG via ``STRIX_DEBUG=1``).
* ``ContextVar``-backed ``scan_id`` and ``agent_id`` injected by a
``Filter`` so every line is auto-tagged across asyncio tasks
without callers passing them explicitly.
* Third-party noise (``httpx``, ``litellm``, ``openai``,
``anthropic``, ``urllib3``, ``httpcore``) capped at WARNING.
* Returns a teardown handle for ``finally`` cleanup.
Wiring:
* ``orchestration/scan.py`` calls ``setup_scan_logging`` once per
scan after ``run_dir`` resolves; sets scan_id; tears down in
``finally``. Adds INFO logs for sandbox bring-up + scan
start/end.
* ``orchestration/hooks.py`` sets/clears ``agent_id`` ContextVar in
``on_agent_start`` / ``on_agent_end`` and emits INFO for agent
lifecycle, DEBUG for every tool start/end and LLM call.
* ``interface/main.py`` drops the ``setLevel(ERROR)`` silencer.
Coverage expanded across ~20 files (orchestration, agents, runtime,
llm, tools, interface, config, skills) with INFO for lifecycle and
DEBUG for verbose detail. Per the system instructions in
``logger.warning(f"…{e}")`` were converted to module logger calls.
58 lines
2.0 KiB
Python
58 lines
2.0 KiB
Python
import logging
|
|
import re
|
|
|
|
from strix.utils.resource_paths import get_strix_resource_path
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
_FRONTMATTER_PATTERN = re.compile(r"^---\s*\n.*?\n---\s*\n", re.DOTALL)
|
|
|
|
|
|
def load_skills(skill_names: list[str]) -> dict[str, str]:
|
|
"""Load skill markdown bodies (frontmatter stripped) by name.
|
|
|
|
Skill files live at ``strix/skills/<category>/<name>.md``. Names
|
|
can be ``"name"`` (any category), ``"category/name"``, or a bare
|
|
file at the skills root. Missing skills are logged and skipped.
|
|
"""
|
|
skills_dir = get_strix_resource_path("skills")
|
|
if not skills_dir.exists():
|
|
return {}
|
|
|
|
by_category: dict[str, str] = {}
|
|
for category_dir in skills_dir.iterdir():
|
|
if not category_dir.is_dir() or category_dir.name.startswith("__"):
|
|
continue
|
|
for file_path in category_dir.glob("*.md"):
|
|
by_category[file_path.stem] = f"{category_dir.name}/{file_path.stem}.md"
|
|
|
|
skill_content: dict[str, str] = {}
|
|
for skill_name in skill_names:
|
|
rel_path: str | None
|
|
if "/" in skill_name:
|
|
rel_path = f"{skill_name}.md"
|
|
elif skill_name in by_category:
|
|
rel_path = by_category[skill_name]
|
|
elif (skills_dir / f"{skill_name}.md").exists():
|
|
rel_path = f"{skill_name}.md"
|
|
else:
|
|
rel_path = None
|
|
|
|
if rel_path is None or not (skills_dir / rel_path).exists():
|
|
logger.warning("Skill not found: %s", skill_name)
|
|
continue
|
|
|
|
try:
|
|
content = (skills_dir / rel_path).read_text(encoding="utf-8")
|
|
except (OSError, ValueError) as e:
|
|
logger.warning("Failed to load skill %s: %s", skill_name, e)
|
|
continue
|
|
|
|
var_name = skill_name.split("/")[-1]
|
|
skill_content[var_name] = _FRONTMATTER_PATTERN.sub("", content).lstrip()
|
|
logger.debug("Loaded skill: %s -> %s", skill_name, var_name)
|
|
|
|
logger.debug("load_skills: %d skill(s) resolved", len(skill_content))
|
|
return skill_content
|