scripts.gen_brief_pack
Reference › scripts › gen_brief_pack
scripts/gen_brief_pack.py builds a dispatch-time context pack for swarm agent briefs, so a builder does not have to re-grep the repository. A human or the orchestrating session runs it before writing a brief; nothing imports it.
Options: --files takes one or more repository-relative glob patterns (** is supported), --symbols takes symbol names, --source is a comma-separated list of source directories passed to the AST toolkit (default src/orchestrata_ultimate,tests, so test consumers appear), and --out writes to a file instead of standard output. At least one of --files and --symbols is required. build_pack composes a markdown document from pure pieces: resolve_files expands the globs, first_docstring_line and file_to_dotted_module give a file inventory, and run_ast_tool shells out to serialize_codebase_ast for refs_for_symbol and who_imports_for_module. extract_file_paths pulls the files that tool output mentions so related test modules can be listed. It writes only the pack, either to standard output or to the --out path.
At a glance
| Source | scripts/gen_brief_pack.py |
| Lines of code | 340 |
| Classes | 0 |
| Functions | 9 |
| Imports (internal) | none |
| Imported by | none |
| Tests that import it | none |
Class diagram
No classes; see Functions.
Dependency diagram
No internal imports and no importers.
API
Functions
| Function | Signature | Summary |
|---|---|---|
resolve_files |
resolve_files(patterns: list[str], repo_root: Path = REPO_ROOT) -> list[str] |
Expand --files glob pattern(s) (** supported) to a sorted, deduped, repo-relative file list. |
first_docstring_line |
first_docstring_line(repo_root: Path, rel_path: str) -> str |
First non-blank line of rel_path's module docstring, or "" when there is none / it isn't parseable Python. |
file_to_dotted_module |
file_to_dotted_module(rel_path: str) -> str | None |
src/orchestrata_ultimate/x/y.py -> orchestrata_ultimate.x.y. |
run_ast_tool |
run_ast_tool(args: list[str], repo_root: Path = REPO_ROOT, timeout: float = 120.0) -> str |
Shell out to serialize_codebase_ast.py and return its stdout, with any subprocess error appended inline rather than raised — a brief pack with one degraded section beats an orchestrator getting… |
refs_for_symbol |
refs_for_symbol(name: str, source: str, repo_root: Path = REPO_ROOT) -> str |
|
who_imports_for_module |
who_imports_for_module(dotted: str, source: str, repo_root: Path = REPO_ROOT) -> str |
|
extract_file_paths |
extract_file_paths(text: str) -> set[str] |
Every path/to/x.py this AST-tool output mentioned on a line of REAL evidence — a DEF/IMP/USE or who-imports hit. |
build_pack |
build_pack(files_patterns: list[str], symbols: list[str], *, source: str = 'src/orchestrata_ultimate,tests', repo_root: Path = REPO_ROOT) -> str |
Assemble the full markdown brief pack. |
main |
main(argv: list[str] | None = None) -> int |
Source
#!/usr/bin/env python3
"""gen_brief_pack.py — dispatch-time context pack for swarm agent briefs.
A retro of swarm-orchestration cost centers found builders re-grepping the
same "where is this defined / who uses it / what tests cover it" questions
an orchestrator could answer once, at dispatch time, from tooling that
already exists. This CLI composes THREE already-built tools — no new static
analysis of its own:
- ``scripts/serialize_codebase_ast.py refs`` (symbol definitions +
import sites + use sites)
- ``scripts/serialize_codebase_ast.py who-imports`` (reverse import graph,
module granularity)
- plain ``glob`` + ``ast.get_docstring`` for the file inventory section
into one markdown "brief pack" an orchestrator pastes (or ``--out``-writes)
into a delegated agent's prompt.
CLI:
python scripts/gen_brief_pack.py --files "src/orchestrata_ultimate/knowledge_graph/graph_client.py"
python scripts/gen_brief_pack.py --symbols GraphClient BoltSyncRepository
python scripts/gen_brief_pack.py --files "src/orchestrata_ultimate/knowledge_graph/*.py" \\
--symbols GraphClient --out /tmp/pack.md
Output sections:
1. File inventory — every file matched by ``--files`` glob(s),
with its module docstring's first line.
2. Symbol references — ``refs`` output for each ``--symbols`` name.
3. Module reverse-imports — ``who-imports`` for the dotted module path
of every ``--files`` match under ``src/``.
4. Related test modules — every ``tests/*.py`` file that showed up as a
reference/import site in sections 2-3 (no
separate name-matching heuristic — this is
exactly the same data, filtered).
"""
from __future__ import annotations
import argparse
import ast
import glob as glob_mod
import re
import subprocess
import sys
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
AST_TOOL = REPO_ROOT / "scripts" / "serialize_codebase_ast.py"
#: A ``path/to/file.py:123`` reference inside AST-toolkit output — used to
#: harvest every file path the ``refs``/``who-imports`` calls surfaced, so
#: the "related test modules" section needs no separate name-matching pass.
_FILE_LINE_RE = re.compile(r"(\S+\.py):\d+")
#: Evidence the AST toolkit itself labels as weak. ``refs`` prefixes a bare
#: string-literal occurrence with ``STR`` and tags the line
#: ``[STRING-LITERAL — weak evidence]``; such a line names a file that merely
#: MENTIONS the symbol's name, never one that references the symbol. Matching
#: on the toolkit's own marker (not on a re-derived heuristic) keeps this
#: filter honest if the toolkit's wording changes: the marker is the contract.
#: Both hyphen forms are accepted so an em-dash/hyphen change cannot silently
#: disable the filter.
_WEAK_EVIDENCE_RE = re.compile(r"\bSTRING-LITERAL\b|\bWEAK\b", re.IGNORECASE)
# ---------------------------------------------------------------------------
# File inventory (section 1)
# ---------------------------------------------------------------------------
def resolve_files(patterns: list[str], repo_root: Path = REPO_ROOT) -> list[str]:
"""Expand ``--files`` glob pattern(s) (``**`` supported) to a sorted,
deduped, repo-relative file list."""
matched: set[str] = set()
for pattern in patterns:
for hit in glob_mod.glob(str(repo_root / pattern), recursive=True):
p = Path(hit)
if not p.is_file():
continue
try:
matched.add(str(p.relative_to(repo_root)))
except ValueError:
matched.add(hit)
return sorted(matched)
def first_docstring_line(repo_root: Path, rel_path: str) -> str:
"""First non-blank line of ``rel_path``'s module docstring, or ``""``
when there is none / it isn't parseable Python."""
path = repo_root / rel_path
if path.suffix != ".py":
return ""
try:
tree = ast.parse(path.read_text(encoding="utf-8"))
except SyntaxError, UnicodeDecodeError, OSError:
return ""
doc = ast.get_docstring(tree)
if not doc:
return ""
for line in doc.splitlines():
stripped = line.strip()
if stripped:
return stripped
return ""
# ---------------------------------------------------------------------------
# AST-toolkit composition (sections 2-3)
# ---------------------------------------------------------------------------
def file_to_dotted_module(rel_path: str) -> str | None:
"""``src/orchestrata_ultimate/x/y.py`` -> ``orchestrata_ultimate.x.y``.
``None`` for anything not under a ``src/`` directory (that's the only
tree ``who-imports`` reasons about as an importable package) or that
isn't itself a ``.py`` file.
"""
parts = Path(rel_path).parts
if "src" not in parts:
return None
idx = parts.index("src")
mod_parts = list(parts[idx + 1 :])
if not mod_parts:
return None
if mod_parts[-1] == "__init__.py":
mod_parts = mod_parts[:-1]
elif mod_parts[-1].endswith(".py"):
mod_parts[-1] = mod_parts[-1][:-3]
else:
return None
return ".".join(mod_parts) if mod_parts else None
def run_ast_tool(
args: list[str], repo_root: Path = REPO_ROOT, timeout: float = 120.0
) -> str:
"""Shell out to ``serialize_codebase_ast.py`` and return its stdout,
with any subprocess error appended inline rather than raised — a brief
pack with one degraded section beats an orchestrator getting nothing
because one lookup failed.
Deliberately does NOT force ``--no-daemon``: this tool composes, so it
must still be correct with no resident ``scripts/ast_daemon.py`` up —
``query_ast_daemon`` degrades to the identical local computation on any
daemon miss (see its docstring) — but when the daemon IS resident (the
SessionStart hook keeps one warm for this repo) delegating to it turns
a ~10-15s cold reparse into a sub-2s socket round trip, so there is no
reason to pay that tax by opting out.
"""
cmd = [sys.executable, str(AST_TOOL), *args]
try:
result = subprocess.run(
cmd,
cwd=str(repo_root),
capture_output=True,
text=True,
timeout=timeout,
)
except subprocess.TimeoutExpired:
return f"[gen_brief_pack] AST tool timed out after {timeout}s: {' '.join(args)}"
out = result.stdout
if result.returncode != 0:
out += (
f"\n[gen_brief_pack] AST tool exited {result.returncode}: "
f"{result.stderr.strip()[-500:]}\n"
)
return out
def refs_for_symbol(name: str, source: str, repo_root: Path = REPO_ROOT) -> str:
return run_ast_tool(["refs", name, "--source", source], repo_root=repo_root)
def who_imports_for_module(
dotted: str, source: str, repo_root: Path = REPO_ROOT
) -> str:
return run_ast_tool(
["who-imports", dotted, "--source", source], repo_root=repo_root
)
def extract_file_paths(text: str) -> set[str]:
"""Every ``path/to/x.py`` this AST-tool output mentioned on a line of
REAL evidence — a ``DEF``/``IMP``/``USE`` or ``who-imports`` hit.
Lines the AST toolkit itself marks as weak are skipped. ``refs`` reports a
bare string-literal occurrence as::
STR tests/scripts/test_x.py:264 [STRING-LITERAL — weak evidence]
and harvesting that put a test module under "Related test modules" purely
because the file mentions the symbol's NAME inside a string. A brief pack
exists to point an agent at code that actually touches the symbol; a module
promoted on evidence the producing tool labelled WEAK is false confidence,
which is the one thing a context pack must not manufacture.
"""
return {
m.group(1)
for line in text.splitlines()
if not _WEAK_EVIDENCE_RE.search(line)
for m in _FILE_LINE_RE.finditer(line)
}
# ---------------------------------------------------------------------------
# Pack assembly
# ---------------------------------------------------------------------------
def build_pack(
files_patterns: list[str],
symbols: list[str],
*,
source: str = "src/orchestrata_ultimate,tests",
repo_root: Path = REPO_ROOT,
) -> str:
"""Assemble the full markdown brief pack. Pure composition — see module
docstring for what each section reuses."""
lines: list[str] = ["# Context Brief Pack", ""]
all_text_parts: list[str] = []
matched_files = resolve_files(files_patterns, repo_root) if files_patterns else []
lines.append("## File inventory")
if matched_files:
for f in matched_files:
doc = first_docstring_line(repo_root, f)
lines.append(f"- `{f}`" + (f" — {doc}" if doc else ""))
else:
lines.append("_(no `--files` patterns given, or no matches)_")
lines.append("")
lines.append("## Symbol references")
if symbols:
for name in symbols:
out = refs_for_symbol(name, source, repo_root)
all_text_parts.append(out)
lines.append(f"### `{name}`")
lines.append("```")
lines.append(out.strip())
lines.append("```")
lines.append("")
else:
lines.append("_(no `--symbols` given)_")
lines.append("")
lines.append("## Module reverse-imports (who-imports)")
dotted_seen: set[str] = set()
any_module = False
for f in matched_files:
dotted = file_to_dotted_module(f)
if dotted is None or dotted in dotted_seen:
continue
dotted_seen.add(dotted)
any_module = True
out = who_imports_for_module(dotted, source, repo_root)
all_text_parts.append(out)
lines.append(f"### `{dotted}`")
lines.append("```")
lines.append(out.strip())
lines.append("```")
lines.append("")
if not any_module:
lines.append("_(no `src/` modules resolved from `--files`)_")
lines.append("")
lines.append("## Related test modules")
test_files = sorted(
{
p
for text in all_text_parts
for p in extract_file_paths(text)
if p.startswith("tests/")
}
)
if test_files:
for t in test_files:
lines.append(f"- `{t}`")
else:
lines.append("_(none found)_")
lines.append("")
return "\n".join(lines)
# ---------------------------------------------------------------------------
# CLI
# ---------------------------------------------------------------------------
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(
description=(
"Dispatch-time context pack for swarm agent briefs — composes "
"the AST toolkit's refs/who-imports + a file inventory + related "
"test modules so builders don't re-grep."
)
)
parser.add_argument(
"--files",
nargs="+",
default=[],
metavar="GLOB",
help="Repo-relative glob pattern(s) for the file inventory / "
"who-imports sections (** supported).",
)
parser.add_argument(
"--symbols",
nargs="+",
default=[],
metavar="NAME",
help="Symbol name(s) to look up via `refs`.",
)
parser.add_argument(
"--source",
default="src/orchestrata_ultimate,tests",
help="Comma-separated source dirs passed through to the AST "
"toolkit (default: src+tests, so test consumers show up).",
)
parser.add_argument(
"--out", metavar="PATH", help="Write the pack here instead of stdout."
)
args = parser.parse_args(argv)
if not args.files and not args.symbols:
parser.error("at least one of --files or --symbols is required")
pack = build_pack(args.files, args.symbols, source=args.source)
if args.out:
out_path = Path(args.out)
out_path.write_text(pack, encoding="utf-8")
print(f"[gen_brief_pack] wrote {out_path} ({len(pack)} bytes)")
else:
print(pack)
return 0
if __name__ == "__main__":
sys.exit(main())