MCPcopy Create free account
hub / github.com/Graphify-Labs/graphify / extract_bash

Function extract_bash

graphify/extract.py:13904–14124  ·  view source on GitHub ↗

Extract functions, source imports, and cross-function calls from a .sh file.

(path: Path)

Source from the content-addressed store, hash-verified

13902
13903
13904def extract_bash(path: Path) -> dict:
13905 """Extract functions, source imports, and cross-function calls from a .sh file."""
13906 try:
13907 import tree_sitter_bash as tsbash
13908 from tree_sitter import Language, Parser
13909 except ImportError:
13910 return {"nodes": [], "edges": [], "error": "tree-sitter-bash not installed"}
13911
13912 try:
13913 language = Language(tsbash.language())
13914 parser = Parser(language)
13915 source = path.read_bytes()
13916 tree = parser.parse(source)
13917 root = tree.root_node
13918 except Exception as e:
13919 return {"nodes": [], "edges": [], "error": str(e)}
13920
13921 stem = _file_stem(path)
13922 str_path = str(path)
13923 nodes: list[dict] = []
13924 edges: list[dict] = []
13925 seen_ids: set[str] = set()
13926 function_bodies: list[tuple[str, Any]] = []
13927 defined_functions: set[str] = set()
13928
13929 from graphify.security import sanitize_metadata # module-level cached import
13930
13931 def add_node(nid: str, label: str, line: int, kind: str = "code") -> None:
13932 if nid and nid not in seen_ids:
13933 seen_ids.add(nid)
13934 nodes.append({"id": nid, "label": label, "file_type": "code",
13935 "source_file": str_path, "source_location": f"L{line}",
13936 "metadata": sanitize_metadata({"language": "bash", "kind": kind})}) # noqa: E501
13937
13938 def add_edge(src: str, tgt: str, relation: str, line: int,
13939 confidence: str = "EXTRACTED", weight: float = 1.0,
13940 context: str | None = None) -> None:
13941 if not src or not tgt or src == tgt:
13942 return
13943 edge = {"source": src, "target": tgt, "relation": relation,
13944 "confidence": confidence, "source_file": str_path,
13945 "source_location": f"L{line}", "weight": weight}
13946 if context:
13947 edge["context"] = context
13948 edges.append(edge)
13949
13950 file_nid = _make_id(str(path))
13951 # file_nid is fully path-derived and never produced by _make_id(stem, func_name),
13952 # so appending "__entry" guarantees a distinct ID from any function node.
13953 entry_nid = file_nid + "__entry"
13954 add_node(file_nid, path.name, 1, kind="file")
13955 add_node(entry_nid, f"{path.name} script", 1, kind="bash_entrypoint")
13956 add_edge(file_nid, entry_nid, "contains", 1)
13957
13958 _BASH_SOURCE_COMMANDS = frozenset({"source", "."})
13959 # Parent node types that mean a contained command is part of a substitution
13960 # or expansion, not a real function call. Token-level filtering misses
13961 # these because `$(build)` exposes `build` as a child command whose name

Calls 7

_file_stemFunction · 0.90
_make_idFunction · 0.90
_prescan_functionsFunction · 0.85
add_nodeFunction · 0.70
add_edgeFunction · 0.70
walkFunction · 0.70
walk_callsFunction · 0.70