SignalMesh / core /intent.py
KyklosIO's picture
Claude Opus 5
feat(intent): read a repo's own declarations, and host the runtime it needs
1d24a7e
Raw History Blame Contribute Delete
14.7 kB
"""
core/intent.py — read what a codebase is *for* out of the codebase.
Every repository already declares its own callable surface, precisely, in a form
meant to be authoritative:
argparse every flag, its type, whether it is required, its help
click / typer the same, as decorators
FastAPI / Flask every route, its method, its path
console_scripts what the package installs as a command
exported functions what a module offers to its importers
A person reading that repo derives its intent from exactly these. So does this.
The alternative — someone writing an adapter per project, naming coordinates by
hand — does not scale past the projects that person has time for, and encodes
their reading rather than the author's declaration.
The rule that keeps it honest: a parameter's name, requiredness and help text
come from the source or are absent. Nothing is invented to make a coordinate
look complete. A CLI that documents nothing produces a coordinate that says it
documents nothing, which is a fact worth having.
"""
from __future__ import annotations
import ast
import json
import re
from pathlib import Path
from typing import Any, Dict, List, Optional, Sequence
SCHEMA = "signalmesh.intent/v1"
SKIP_DIRS = {".git", "node_modules", "__pycache__", ".venv", "venv", "build",
"dist", ".next", "target", "vendor", ".tox", "site-packages"}
MAX_FILES = 3000
def _walk(root: Path, suffix: str) -> List[Path]:
out: List[Path] = []
for p in root.rglob(f"*{suffix}"):
if any(part in SKIP_DIRS for part in p.parts):
continue
out.append(p)
if len(out) >= MAX_FILES:
break
return sorted(out)
def _const(node: ast.AST) -> Any:
try:
return ast.literal_eval(node)
except Exception:
return None
def _kw(call: ast.Call, name: str) -> Any:
for k in call.keywords:
if k.arg == name:
return _const(k.value)
return None
def cli_from_python(path: Path) -> Optional[Dict[str, Any]]:
"""An argparse/click CLI, read from the parser the author actually built."""
try:
tree = ast.parse(path.read_text(encoding="utf-8", errors="ignore"))
except (OSError, SyntaxError):
return None
args: List[Dict[str, Any]] = []
description = ""
kind = ""
for node in ast.walk(tree):
if not isinstance(node, ast.Call):
continue
fn = node.func
# argparse: ap.add_argument("x", "--y", help=..., required=..., action=...)
if isinstance(fn, ast.Attribute) and fn.attr == "add_argument":
kind = kind or "argparse"
flags = [v for v in (_const(a) for a in node.args) if isinstance(v, str)]
if not flags:
continue
positional = not any(f.startswith("-") for f in flags)
name = next((f.lstrip("-") for f in flags if f.startswith("--")),
flags[0].lstrip("-"))
action = _kw(node, "action") or ""
args.append({
"name": name, "flags": flags, "positional": positional,
"help": _kw(node, "help") or "",
"required": bool(_kw(node, "required")) or positional,
"type": "bool" if action in ("store_true", "store_false") else
("list" if _kw(node, "nargs") in ("*", "+") else "str"),
"default": _kw(node, "default"),
})
elif isinstance(fn, ast.Attribute) and fn.attr == "ArgumentParser":
description = _kw(node, "description") or description
# click / typer: @click.option("--x"), @click.argument("y")
elif isinstance(fn, ast.Attribute) and fn.attr in ("option", "argument"):
mod = getattr(fn.value, "id", "")
if mod not in ("click", "typer"):
continue
kind = kind or mod
flags = [v for v in (_const(a) for a in node.args) if isinstance(v, str)]
if not flags:
continue
positional = fn.attr == "argument"
name = next((f.lstrip("-") for f in flags if f.startswith("--")),
flags[0].lstrip("-"))
args.append({
"name": name, "flags": flags, "positional": positional,
"help": _kw(node, "help") or "",
"required": bool(_kw(node, "required")) or positional,
"type": "bool" if _kw(node, "is_flag") else "str",
"default": _kw(node, "default"),
})
if not args:
return None
if not description:
description = (ast.get_docstring(tree) or "").strip().split("\n\n")[0]
# dedupe, keeping the first declaration of each flag
seen, uniq = set(), []
for a in args:
if a["name"] in seen:
continue
seen.add(a["name"])
uniq.append(a)
return {"kind": kind or "argparse", "module": path.stem, "path": str(path),
"summary": description[:400], "args": uniq}
def routes_from_python(path: Path) -> List[Dict[str, Any]]:
"""HTTP routes, read from the decorators that define them."""
try:
tree = ast.parse(path.read_text(encoding="utf-8", errors="ignore"))
except (OSError, SyntaxError):
return []
out = []
for node in ast.walk(tree):
if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
continue
for dec in node.decorator_list:
if not isinstance(dec, ast.Call) or not isinstance(dec.func, ast.Attribute):
continue
method = dec.func.attr.upper()
if method not in ("GET", "POST", "PUT", "PATCH", "DELETE", "ROUTE"):
continue
route = next((v for v in (_const(a) for a in dec.args)
if isinstance(v, str)), None)
if not route:
continue
out.append({"method": "GET" if method == "ROUTE" else method,
"path": route, "handler": node.name,
"summary": (ast.get_docstring(node) or "").strip()
.split("\n\n")[0][:200],
"params": [a.arg for a in node.args.args
if a.arg not in ("self", "request")]})
return out
def scripts_from_manifest(root: Path) -> List[Dict[str, Any]]:
"""Commands the package says it installs — pyproject, setup.cfg, package.json."""
out = []
py = root / "pyproject.toml"
if py.exists():
txt = py.read_text(encoding="utf-8", errors="ignore")
block = re.search(r"\[project\.scripts\](.*?)(\n\[|\Z)", txt, re.S)
if block:
for line in block.group(1).splitlines():
m = re.match(r'\s*([\w.-]+)\s*=\s*["\']([^"\']+)', line)
if m:
out.append({"command": m.group(1), "entry": m.group(2),
"from": "pyproject.toml"})
pkg = root / "package.json"
if pkg.exists():
try:
data = json.loads(pkg.read_text(encoding="utf-8", errors="ignore"))
except json.JSONDecodeError:
data = {}
bins = data.get("bin")
if isinstance(bins, str):
out.append({"command": data.get("name", "cli"), "entry": bins,
"from": "package.json"})
elif isinstance(bins, dict):
out += [{"command": k, "entry": v, "from": "package.json"}
for k, v in bins.items()]
for name, script in (data.get("scripts") or {}).items():
if name in ("start", "serve", "dev", "build"):
out.append({"command": f"npm run {name}", "entry": script,
"from": "package.json scripts"})
return out
def _slug(text: str) -> str:
return re.sub(r"[^a-z0-9]+", "_", str(text).lower()).strip("_") or "op"
def read(root: Path, *, name: str = "") -> Dict[str, Any]:
"""Everything a repository says about how it wants to be called."""
root = Path(root).expanduser().resolve()
slug = _slug(name or root.name)
clis, routes = [], []
for f in _walk(root, ".py"):
got = cli_from_python(f)
if got:
clis.append(got)
routes += routes_from_python(f)
scripts = scripts_from_manifest(root)
coords: List[Dict[str, Any]] = []
for c in clis:
coords.append({
"coord": f"{slug}.{_slug(c['module'])}",
"protocol": "cli", "kind": c["kind"],
"summary": c["summary"], "run": c["path"],
"params": [a["name"] for a in c["args"]],
"required": [a["name"] for a in c["args"] if a["required"]],
"args": c["args"],
})
for r in routes:
coords.append({
"coord": f"{slug}.{_slug(r['handler'])}",
"protocol": "http", "method": r["method"], "path": r["path"],
"summary": r["summary"], "params": r["params"], "required": [],
})
for s in scripts:
coords.append({
"coord": f"{slug}.{_slug(s['command'])}",
"protocol": "command", "summary": f"{s['entry']} ({s['from']})",
"run": s["command"], "params": [], "required": [],
})
return {
"schema": SCHEMA, "repo": str(root), "slug": slug,
"coordinates": coords,
"totals": {"coordinates": len(coords), "clis": len(clis),
"routes": len(routes), "commands": len(scripts)},
"note": ("read from the repository's own declarations — argparse, click, "
"route decorators, packaged entry points. Nothing inferred, and "
"nothing named by hand."),
}
def invocation(coord: Dict[str, Any], values: Dict[str, Any]) -> List[str]:
"""Build the command this coordinate's own parser accepts.
Refuses when a required argument is missing, rather than emitting something
that will fail after the work of getting there.
"""
if coord.get("protocol") != "cli":
raise ValueError(f"{coord['coord']} is {coord.get('protocol')}, not a CLI")
missing = [r for r in coord.get("required", []) if values.get(r) in (None, "", False)]
if missing:
raise ValueError(f"{coord['coord']} requires {missing} — its own parser "
f"will reject the call without them")
cmd = ["python", coord["run"]]
for a in coord.get("args", []):
v = values.get(a["name"])
if v in (None, "", False):
continue
if a["positional"]:
cmd += [str(x) for x in v] if isinstance(v, list) else [str(v)]
elif a["type"] == "bool":
cmd.append(a["flags"][-1])
else:
cmd += [a["flags"][-1], str(v)]
return cmd
if __name__ == "__main__":
import tempfile
src = '''
"""Do a specific job.
Longer prose nobody needs in a summary.
"""
import argparse
def main():
ap = argparse.ArgumentParser(description="Compile and annotate templates.")
ap.add_argument("template", nargs="*", help="template directories")
ap.add_argument("--all", action="store_true", help="process every template")
ap.add_argument("--profile", help="force a profile")
ap.add_argument("--no-overlay", action="store_true")
'''
api = '''
from fastapi import FastAPI
app = FastAPI()
@app.get("/health")
def health():
"""Say whether it is up."""
return {}
@app.post("/convert/{kind}")
async def convert(kind: str, body: dict):
"""Turn one thing into another."""
return {}
'''
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
(root / "tool.py").write_text(src)
(root / "server.py").write_text(api)
(root / "pyproject.toml").write_text(
'[project]\nname="x"\n\n[project.scripts]\nmytool = "x.cli:main"\n')
(root / "node_modules").mkdir()
(root / "node_modules" / "junk.py").write_text(src) # must be skipped
got = read(root, name="demo")
by = {c["coord"]: c for c in got["coordinates"]}
cli = by["demo.tool"]
assert cli["protocol"] == "cli" and cli["kind"] == "argparse"
# the author's own description, not the module docstring's first line
assert cli["summary"] == "Compile and annotate templates."
assert cli["params"] == ["template", "all", "profile", "no-overlay"], cli["params"]
# a positional is required; a flag is not — read from the declaration
assert cli["required"] == ["template"], cli["required"]
assert next(a for a in cli["args"] if a["name"] == "all")["type"] == "bool"
assert next(a for a in cli["args"] if a["name"] == "template")["type"] == "list"
assert by["demo.health"]["method"] == "GET"
assert by["demo.convert"]["method"] == "POST"
assert by["demo.convert"]["path"] == "/convert/{kind}"
assert by["demo.convert"]["summary"] == "Turn one thing into another."
assert by["demo.mytool"]["protocol"] == "command"
# vendored code is not this repository's intent
assert got["totals"]["clis"] == 1, "node_modules must not be read"
# the built command matches the parser that will receive it
cmd = invocation(cli, {"template": ["a", "b"], "all": False, "profile": "news"})
assert cmd[1].endswith("tool.py")
assert cmd[2:] == ["a", "b", "--profile", "news"], cmd
assert "--all" in invocation(cli, {"template": ["a"], "all": True})
assert "--no-overlay" in invocation(cli, {"template": ["a"], "no-overlay": True})
try:
invocation(cli, {})
raise AssertionError("a missing required arg must be refused here")
except ValueError as e:
assert "requires ['template']" in str(e)
assert read(root)["slug"] == _slug(root.name)
# ── against the real checkouts, if they are here ─────────────────────────
for repo, expect in (
("layout-detection-synthetic-data-main", "annotate"),
("ConvertX-main", None),
):
p = Path.home() / "Downloads" / "fixMesh" / repo
if not p.is_dir():
continue
got = read(p)
print(f"{repo}: {got['totals']}")
if expect:
hit = next((c for c in got["coordinates"]
if c["coord"].endswith(expect)), None)
assert hit, f"did not discover {expect} in {repo}"
print(f" discovered: {hit['coord']} params={hit['params']} "
f"required={hit['required']}")
print("intent self-check OK")