gnost 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. gnost-0.1.0/LICENSE +21 -0
  2. gnost-0.1.0/PKG-INFO +126 -0
  3. gnost-0.1.0/README.md +113 -0
  4. gnost-0.1.0/gnost/__init__.py +1 -0
  5. gnost-0.1.0/gnost/cli/__init__.py +3 -0
  6. gnost-0.1.0/gnost/cli/app.py +111 -0
  7. gnost-0.1.0/gnost/cli/commands/__init__.py +3 -0
  8. gnost-0.1.0/gnost/cli/commands/onboard.py +94 -0
  9. gnost-0.1.0/gnost/config/__init__.py +0 -0
  10. gnost-0.1.0/gnost/config/languages.py +23 -0
  11. gnost-0.1.0/gnost/core/__init__.py +0 -0
  12. gnost-0.1.0/gnost/core/counter.py +23 -0
  13. gnost-0.1.0/gnost/core/flow.py +130 -0
  14. gnost-0.1.0/gnost/core/graph.py +124 -0
  15. gnost-0.1.0/gnost/core/ranker.py +115 -0
  16. gnost-0.1.0/gnost/languages/base.py +80 -0
  17. gnost-0.1.0/gnost/languages/java.py +82 -0
  18. gnost-0.1.0/gnost/languages/javascript.py +83 -0
  19. gnost-0.1.0/gnost/languages/python.py +105 -0
  20. gnost-0.1.0/gnost/languages/typescript.py +6 -0
  21. gnost-0.1.0/gnost/plugins/__init__.py +3 -0
  22. gnost-0.1.0/gnost/plugins/base.py +5 -0
  23. gnost-0.1.0/gnost/reporters/__init__.py +3 -0
  24. gnost-0.1.0/gnost/reporters/files.py +18 -0
  25. gnost-0.1.0/gnost/reporters/folders.py +18 -0
  26. gnost-0.1.0/gnost/reporters/loc_summary.py +19 -0
  27. gnost-0.1.0/gnost/reporters/markdown.py +138 -0
  28. gnost-0.1.0/gnost/reporters/mermaid.py +75 -0
  29. gnost-0.1.0/gnost/reporters/stats.py +13 -0
  30. gnost-0.1.0/gnost/reporters/summary.py +144 -0
  31. gnost-0.1.0/gnost/scanner/__init__.py +4 -0
  32. gnost-0.1.0/gnost/scanner/classify.py +13 -0
  33. gnost-0.1.0/gnost/scanner/engine.py +107 -0
  34. gnost-0.1.0/gnost/scanner/filters.py +82 -0
  35. gnost-0.1.0/gnost/scanner/loc.py +78 -0
  36. gnost-0.1.0/gnost/scanner/models.py +22 -0
  37. gnost-0.1.0/gnost/utils/__init__.py +0 -0
  38. gnost-0.1.0/gnost/utils/printer.py +46 -0
  39. gnost-0.1.0/gnost/utils/progress.py +20 -0
  40. gnost-0.1.0/gnost.egg-info/PKG-INFO +126 -0
  41. gnost-0.1.0/gnost.egg-info/SOURCES.txt +45 -0
  42. gnost-0.1.0/gnost.egg-info/dependency_links.txt +1 -0
  43. gnost-0.1.0/gnost.egg-info/entry_points.txt +2 -0
  44. gnost-0.1.0/gnost.egg-info/requires.txt +2 -0
  45. gnost-0.1.0/gnost.egg-info/top_level.txt +1 -0
  46. gnost-0.1.0/pyproject.toml +18 -0
  47. gnost-0.1.0/setup.cfg +4 -0
gnost-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Mohd Zain
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
gnost-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,126 @@
1
+ Metadata-Version: 2.4
2
+ Name: gnost
3
+ Version: 0.1.0
4
+ Summary: GNOST — Codebase Knowledge
5
+ Author-email: Mohd Zain <zainmohd1998@gmail.com>
6
+ License: MIT
7
+ Requires-Python: >=3.8
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: rich
11
+ Requires-Dist: tqdm
12
+ Dynamic: license-file
13
+
14
+ # GNOST — Codebase Knowledge
15
+ ![Status](https://img.shields.io/badge/status-v0.1.0--pre--release-yellow)
16
+ ![License](https://img.shields.io/badge/license-MIT-blue)
17
+
18
+
19
+
20
+ GNOST helps developers understand unfamiliar codebases by automatically identifying **entry points**, **execution flow**, and **core logic**.
21
+
22
+ It is designed for **first-day onboarding**, not just code statistics.
23
+
24
+ ---
25
+
26
+ ## What GNOST Does
27
+
28
+ - Detects **where execution starts**
29
+ - Infers **high-level execution flow**
30
+ - Identifies **hotspot files** (most important code)
31
+ - Generates **onboarding documentation**
32
+ - Produces **Mermaid flow diagrams**
33
+ - Works across multiple languages
34
+
35
+ Supported languages:
36
+ - Python
37
+ - JavaScript
38
+ - TypeScript
39
+ - Java
40
+
41
+ ---
42
+
43
+ ## Installation
44
+
45
+ ```bash
46
+ pip install gnost
47
+ ```
48
+
49
+ ## Quick Start
50
+ ```bash
51
+ gnost onboard .
52
+ ```
53
+ This will:
54
+ - Print a human-readable onboarding summary
55
+ - Generate ONBOARD.md
56
+ - Generate an execution flow diagram
57
+
58
+ ## Usage
59
+ ```bash
60
+ gnost summary [path]
61
+ gnost stats [path]
62
+ gnost folders [path]
63
+ gnost files [path] --top 10
64
+ gnost onboard [path]
65
+ ```
66
+
67
+ ## Key Commands
68
+ - `summary` Show a high-level project summary
69
+ - `stats` Show detailed language statistics
70
+ - `folders` Show LOC grouped by folder
71
+ - `files` Show largest files by LOC
72
+ - `onboard` Generate onboarding summary and flow diagrams
73
+ - `version` Display GNOST version
74
+
75
+ ## Onboarding & Flow Analysis
76
+ - Generate onboarding documentation:
77
+ ```bash
78
+ gnost onboard .
79
+ ```
80
+ - Generate only a Mermaid flow diagram:
81
+ ```bash
82
+ gnost onboard . --mermaid
83
+ ```
84
+ This produces:
85
+ - ONBOARD.md — onboarding guide
86
+ - FLOW.mmd — pure Mermaid flow diagram
87
+
88
+ ### Options
89
+ - `--include` Comma-separated folders to include
90
+ - `--exclude` Comma-separated folders to exclude
91
+ - `--top` Number of files to show with files
92
+ - `--version` Show version and exit
93
+ - `--help` Show help
94
+
95
+ ## examples
96
+ ```bash
97
+ gnost summary .
98
+ gnost stats .
99
+ gnost onboard .
100
+ gnost onboard . --mermaid
101
+ gnost files src --top 20
102
+ ```
103
+
104
+ ## Philosophy
105
+ GNOST is not a **static analyzer or linter**.
106
+
107
+ It focuses on:
108
+ - Mental model generation
109
+ - Execution understanding
110
+ - Developer onboarding
111
+
112
+ It uses **heuristic-based static analysis** to stay fast, simple, and language-agnostic.
113
+
114
+ ## Roadmap
115
+
116
+ ### v0.2.0 (Planned)
117
+
118
+ - Hotspot visualization in Mermaid diagrams
119
+ - Standalone `gnost hotspots` command
120
+ - Improved JavaScript / TypeScript import resolution
121
+ - Better framework awareness (Express, Spring MVC)
122
+ - Optional Tree-sitter based parsing for deeper analysis
123
+ - GitHub Action enhancements (PR comments, annotations)
124
+
125
+ ## License
126
+ MIT License
gnost-0.1.0/README.md ADDED
@@ -0,0 +1,113 @@
1
+ # GNOST — Codebase Knowledge
2
+ ![Status](https://img.shields.io/badge/status-v0.1.0--pre--release-yellow)
3
+ ![License](https://img.shields.io/badge/license-MIT-blue)
4
+
5
+
6
+
7
+ GNOST helps developers understand unfamiliar codebases by automatically identifying **entry points**, **execution flow**, and **core logic**.
8
+
9
+ It is designed for **first-day onboarding**, not just code statistics.
10
+
11
+ ---
12
+
13
+ ## What GNOST Does
14
+
15
+ - Detects **where execution starts**
16
+ - Infers **high-level execution flow**
17
+ - Identifies **hotspot files** (most important code)
18
+ - Generates **onboarding documentation**
19
+ - Produces **Mermaid flow diagrams**
20
+ - Works across multiple languages
21
+
22
+ Supported languages:
23
+ - Python
24
+ - JavaScript
25
+ - TypeScript
26
+ - Java
27
+
28
+ ---
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ pip install gnost
34
+ ```
35
+
36
+ ## Quick Start
37
+ ```bash
38
+ gnost onboard .
39
+ ```
40
+ This will:
41
+ - Print a human-readable onboarding summary
42
+ - Generate ONBOARD.md
43
+ - Generate an execution flow diagram
44
+
45
+ ## Usage
46
+ ```bash
47
+ gnost summary [path]
48
+ gnost stats [path]
49
+ gnost folders [path]
50
+ gnost files [path] --top 10
51
+ gnost onboard [path]
52
+ ```
53
+
54
+ ## Key Commands
55
+ - `summary` Show a high-level project summary
56
+ - `stats` Show detailed language statistics
57
+ - `folders` Show LOC grouped by folder
58
+ - `files` Show largest files by LOC
59
+ - `onboard` Generate onboarding summary and flow diagrams
60
+ - `version` Display GNOST version
61
+
62
+ ## Onboarding & Flow Analysis
63
+ - Generate onboarding documentation:
64
+ ```bash
65
+ gnost onboard .
66
+ ```
67
+ - Generate only a Mermaid flow diagram:
68
+ ```bash
69
+ gnost onboard . --mermaid
70
+ ```
71
+ This produces:
72
+ - ONBOARD.md — onboarding guide
73
+ - FLOW.mmd — pure Mermaid flow diagram
74
+
75
+ ### Options
76
+ - `--include` Comma-separated folders to include
77
+ - `--exclude` Comma-separated folders to exclude
78
+ - `--top` Number of files to show with files
79
+ - `--version` Show version and exit
80
+ - `--help` Show help
81
+
82
+ ## examples
83
+ ```bash
84
+ gnost summary .
85
+ gnost stats .
86
+ gnost onboard .
87
+ gnost onboard . --mermaid
88
+ gnost files src --top 20
89
+ ```
90
+
91
+ ## Philosophy
92
+ GNOST is not a **static analyzer or linter**.
93
+
94
+ It focuses on:
95
+ - Mental model generation
96
+ - Execution understanding
97
+ - Developer onboarding
98
+
99
+ It uses **heuristic-based static analysis** to stay fast, simple, and language-agnostic.
100
+
101
+ ## Roadmap
102
+
103
+ ### v0.2.0 (Planned)
104
+
105
+ - Hotspot visualization in Mermaid diagrams
106
+ - Standalone `gnost hotspots` command
107
+ - Improved JavaScript / TypeScript import resolution
108
+ - Better framework awareness (Express, Spring MVC)
109
+ - Optional Tree-sitter based parsing for deeper analysis
110
+ - GitHub Action enhancements (PR comments, annotations)
111
+
112
+ ## License
113
+ MIT License
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
@@ -0,0 +1,3 @@
1
+ from gnost.cli.app import main
2
+
3
+ __all__ = ["main"]
@@ -0,0 +1,111 @@
1
+ import argparse
2
+ from gnost import __version__
3
+ from gnost.scanner.engine import scan
4
+ from gnost.reporters import stats, folders, files, loc_summary
5
+ from gnost.cli.commands.onboard import run as onboard_run
6
+
7
+
8
+ def main():
9
+ parser = argparse.ArgumentParser(
10
+ prog="gnost",
11
+ usage="gnost [command]",
12
+ description="GNOST — Code Knowledge Scanner",
13
+ formatter_class=argparse.RawTextHelpFormatter,
14
+ epilog=(
15
+ "Command Options:\n"
16
+ " summary|stats|folders|files:\n"
17
+ " --include Comma-separated folder names to include\n"
18
+ " --exclude Comma-separated folder names to exclude\n"
19
+ " --progress Show a progress bar while scanning\n"
20
+ " onboard:\n"
21
+ " --progress Show a progress bar while onboarding\n"
22
+ " --mermaid Generate only Mermaid flow diagram\n"
23
+ " files:\n"
24
+ " --top Number of files to show (default: 5)\n"
25
+ "Use `gnost <command> --help` for full command options."
26
+ ),
27
+ )
28
+ parser.add_argument("--version", action="version", version=f"gnost {__version__}")
29
+
30
+ sub = parser.add_subparsers(
31
+ dest="cmd",
32
+ required=True,
33
+ title="Available Commands",
34
+ metavar="command",
35
+ )
36
+
37
+ base = argparse.ArgumentParser(add_help=False)
38
+ base.add_argument("path", nargs="?", default=".", help="Directory to scan")
39
+ base.add_argument(
40
+ "--exclude",
41
+ help="Comma-separated folder names to exclude (e.g. node_modules,dist)",
42
+ )
43
+ base.add_argument(
44
+ "--include",
45
+ help="Comma-separated folder names to include (only these are scanned)",
46
+ )
47
+ base.add_argument(
48
+ "--progress",
49
+ action="store_true",
50
+ help="Show a progress bar while scanning",
51
+ )
52
+
53
+ sub.add_parser("summary", parents=[base], help="Show a summary table")
54
+ sub.add_parser("stats", parents=[base], help="Show detailed stats per language")
55
+ sub.add_parser("folders", parents=[base], help="Show LOC grouped by folder")
56
+
57
+ files_parser = sub.add_parser(
58
+ "files", parents=[base], help="Show the largest files by LOC"
59
+ )
60
+ files_parser.add_argument(
61
+ "--top",
62
+ type=int,
63
+ default=5,
64
+ help="Number of files to show (default: 5)",
65
+ )
66
+
67
+ sub.add_parser("version", help="Display gnost version")
68
+ onboard = sub.add_parser("onboard", help="Onboard a new codebase")
69
+ onboard.add_argument("path", nargs="?", default=".")
70
+ onboard.add_argument(
71
+ "--mermaid",
72
+ action="store_true",
73
+ help="Generate only Mermaid flow diagram (FLOW.mmd)",
74
+ )
75
+ onboard.add_argument(
76
+ "--progress",
77
+ action="store_true",
78
+ help="Show a progress bar while onboarding",
79
+ )
80
+
81
+ args = parser.parse_args()
82
+
83
+ if args.cmd == "version":
84
+ print(f"gnost {__version__}")
85
+ return
86
+
87
+ if args.cmd == "onboard":
88
+ onboard_run(
89
+ args.path,
90
+ diagram_only=getattr(args, "mermaid", False),
91
+ progress=getattr(args, "progress", False),
92
+ )
93
+ return
94
+
95
+ include = args.include.split(",") if args.include else []
96
+ exclude = args.exclude.split(",") if args.exclude else []
97
+
98
+ data = scan(args.path, include, exclude, progress=args.progress)
99
+
100
+ if args.cmd == "stats":
101
+ stats.render(data)
102
+ elif args.cmd == "folders":
103
+ folders.render(data)
104
+ elif args.cmd == "files":
105
+ files.render(data, args.top)
106
+ else:
107
+ loc_summary.render(data)
108
+
109
+
110
+ if __name__ == "__main__":
111
+ main()
@@ -0,0 +1,3 @@
1
+ from gnost.cli.commands.onboard import run as onboard
2
+
3
+ __all__ = ["onboard"]
@@ -0,0 +1,94 @@
1
+ # gnost/cli/commands/onboard.py
2
+
3
+ import os
4
+
5
+ from gnost.languages.python import PythonAdapter
6
+ from gnost.languages.javascript import JavaScriptAdapter
7
+ from gnost.languages.typescript import TypeScriptAdapter
8
+ from gnost.languages.java import JavaAdapter
9
+ from gnost.scanner.engine import ScannerEngine
10
+ from gnost.core.graph import DependencyGraph
11
+ from gnost.core.flow import FlowBuilder
12
+ from gnost.reporters.summary import SummaryReporter
13
+ from gnost.reporters.markdown import MarkdownReporter
14
+ from gnost.reporters.mermaid import MermaidFlowReporter
15
+ from gnost.utils.progress import progress_bar
16
+
17
+
18
+ def run(path: str | None = None, diagram_only: bool = False, progress: bool = False):
19
+ """
20
+ gnost onboard [path] [--mermaid]
21
+ Generates a high-level onboarding summary for a codebase.
22
+ """
23
+ root = os.path.abspath(path or ".")
24
+
25
+ with progress_bar(enabled=progress, total=4, desc="Onboarding") as bar:
26
+ # -------------------------
27
+ # 1. Language adapters
28
+ # -------------------------
29
+ adapters = [
30
+ PythonAdapter(),
31
+ JavaScriptAdapter(),
32
+ TypeScriptAdapter(),
33
+ JavaAdapter(),
34
+ ]
35
+
36
+ # -------------------------
37
+ # 2. Scan repository
38
+ # -------------------------
39
+ scanner = ScannerEngine(adapters=adapters)
40
+ scan_result = scanner.scan(root)
41
+ if bar is not None:
42
+ bar.update(1)
43
+
44
+ # -------------------------
45
+ # 3. Build dependency graph
46
+ # -------------------------
47
+ graph = DependencyGraph.from_scan(scan_result)
48
+ if bar is not None:
49
+ bar.update(1)
50
+
51
+ # -------------------------
52
+ # 4. Build execution flow
53
+ # -------------------------
54
+ flow_builder = FlowBuilder(graph=graph, scan=scan_result)
55
+ flow_result = flow_builder.build()
56
+ if bar is not None:
57
+ bar.update(1)
58
+
59
+ # -------------------------
60
+ # 5. Diagram-only mode
61
+ # -------------------------
62
+ if diagram_only:
63
+ diagram = MermaidFlowReporter(
64
+ flow=flow_result,
65
+ root=scan_result.root,
66
+ ).render(markdown=False)
67
+
68
+ output_path = os.path.join(root, "FLOW.mmd")
69
+ with open(output_path, "w", encoding="utf-8") as f:
70
+ f.write(diagram + "\n")
71
+
72
+ if bar is not None:
73
+ bar.update(1)
74
+
75
+ print(f"Mermaid flow diagram written to {output_path}")
76
+ return
77
+
78
+ # -------------------------
79
+ # 6. Normal onboarding
80
+ # -------------------------
81
+ SummaryReporter(
82
+ scan=scan_result,
83
+ flow=flow_result,
84
+ graph=graph,
85
+ ).render()
86
+
87
+ MarkdownReporter(
88
+ scan=scan_result,
89
+ flow=flow_result,
90
+ output_file="ONBOARD.md",
91
+ ).write()
92
+
93
+ if bar is not None:
94
+ bar.update(1)
File without changes
@@ -0,0 +1,23 @@
1
+ LANG_EXTENSIONS = {
2
+ "py": "Python",
3
+ "js": "JavaScript",
4
+ "ts": "TypeScript",
5
+ "java": "Java",
6
+ "go": "Go",
7
+ "cpp": "C++",
8
+ "c": "C",
9
+ "cs": "C#",
10
+ "rs": "Rust",
11
+ "php": "PHP",
12
+ "html": "HTML",
13
+ "css": "CSS",
14
+ }
15
+
16
+ LANGUAGES = {
17
+ "py": {"name": "Python", "comment": "#"},
18
+ "js": {"name": "JavaScript", "comment": "//"},
19
+ "ts": {"name": "TypeScript", "comment": "//"},
20
+ "java": {"name": "Java", "comment": "//"},
21
+ "c": {"name": "C", "comment": "//"},
22
+ "cpp": {"name": "C++", "comment": "//"},
23
+ }
File without changes
@@ -0,0 +1,23 @@
1
+ import os
2
+ from gnost.config.languages import LANG_EXTENSIONS
3
+
4
+
5
+ def count_loc(path: str = "."):
6
+ results = {}
7
+ file_count = {}
8
+
9
+ for root, _, files in os.walk(path):
10
+ for f in files:
11
+ ext = f.split(".")[-1].lower()
12
+ if ext in LANG_EXTENSIONS:
13
+ file_count.setdefault(ext, 0)
14
+ results.setdefault(ext, 0)
15
+ file_count[ext] += 1
16
+
17
+ try:
18
+ with open(os.path.join(root, f), "r", errors="ignore") as fh:
19
+ results[ext] += sum(1 for _ in fh)
20
+ except:
21
+ pass
22
+
23
+ return results, file_count
@@ -0,0 +1,130 @@
1
+ # gnost/core/flow.py
2
+
3
+ from typing import List, Dict, Set
4
+ from dataclasses import dataclass
5
+
6
+ from gnost.scanner.models import ScanResult
7
+ from gnost.languages.base import EntryPoint
8
+ from gnost.core.graph import DependencyGraph
9
+
10
+
11
+ @dataclass
12
+ class FlowPath:
13
+ """
14
+ Represents one execution path.
15
+ """
16
+
17
+ start: str
18
+ path: List[str]
19
+
20
+
21
+ @dataclass
22
+ class FlowResult:
23
+ """
24
+ Complete flow analysis result.
25
+ """
26
+
27
+ entry_points: List[EntryPoint]
28
+ paths: List[FlowPath]
29
+ layers: Dict[str, Set[str]] # entry, core, leaf
30
+
31
+
32
+ class FlowBuilder:
33
+ """
34
+ Builds execution flow using:
35
+ - Entry points
36
+ - Dependency graph
37
+ """
38
+
39
+ def __init__(self, graph: DependencyGraph, scan: ScanResult):
40
+ self.graph = graph
41
+ self.scan = scan
42
+
43
+ # -------------------------
44
+ # Public API
45
+ # -------------------------
46
+
47
+ def build(self) -> FlowResult:
48
+ entry_files = self._resolve_entry_files()
49
+ paths: List[FlowPath] = []
50
+
51
+ for entry in entry_files:
52
+ self._walk(
53
+ start=entry,
54
+ current=entry,
55
+ visited=set(),
56
+ path=[],
57
+ paths=paths,
58
+ )
59
+
60
+ layers = self._classify_layers(paths)
61
+
62
+ return FlowResult(
63
+ entry_points=self.scan.entry_points,
64
+ paths=paths,
65
+ layers=layers,
66
+ )
67
+
68
+ def _walk(
69
+ self,
70
+ start: str,
71
+ current: str,
72
+ visited: Set[str],
73
+ path: List[str],
74
+ paths: List[FlowPath],
75
+ ):
76
+ if current in visited:
77
+ return
78
+
79
+ visited.add(current)
80
+ path.append(current)
81
+
82
+ dependencies = self.graph.dependencies_of(current)
83
+
84
+ if not dependencies:
85
+ paths.append(FlowPath(start=start, path=list(path)))
86
+ else:
87
+ for dep in dependencies:
88
+ self._walk(
89
+ start=start,
90
+ current=dep,
91
+ visited=set(visited),
92
+ path=list(path),
93
+ paths=paths,
94
+ )
95
+
96
+ def _resolve_entry_files(self) -> List[str]:
97
+ """
98
+ Convert EntryPoint objects into concrete files.
99
+ """
100
+ resolved = []
101
+
102
+ for ep in self.scan.entry_points:
103
+ resolved.append(ep.file)
104
+
105
+ # Fallback: graph roots if no explicit entry point
106
+ if not resolved:
107
+ resolved.extend(self.graph.roots())
108
+
109
+ return list(set(resolved))
110
+
111
+ def _classify_layers(self, paths: List[FlowPath]) -> Dict[str, Set[str]]:
112
+ entry = set()
113
+ core = set()
114
+ leaf = set()
115
+
116
+ for flow in paths:
117
+ if not flow.path:
118
+ continue
119
+
120
+ entry.add(flow.path[0])
121
+ leaf.add(flow.path[-1])
122
+
123
+ for mid in flow.path[1:-1]:
124
+ core.add(mid)
125
+
126
+ return {
127
+ "entry": entry,
128
+ "core": core,
129
+ "leaf": leaf,
130
+ }