brieflook 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.
- brieflook-0.1.0/.gitignore +40 -0
- brieflook-0.1.0/LICENSE +22 -0
- brieflook-0.1.0/PKG-INFO +69 -0
- brieflook-0.1.0/README.md +47 -0
- brieflook-0.1.0/pyproject.toml +48 -0
- brieflook-0.1.0/src/brieflook/__init__.py +0 -0
- brieflook-0.1.0/src/brieflook/__main__.py +7 -0
- brieflook-0.1.0/src/brieflook/cli/__init__.py +0 -0
- brieflook-0.1.0/src/brieflook/cli/main.py +140 -0
- brieflook-0.1.0/src/brieflook/core/__init__.py +0 -0
- brieflook-0.1.0/src/brieflook/core/index.py +41 -0
- brieflook-0.1.0/src/brieflook/core/index_builder.py +68 -0
- brieflook-0.1.0/src/brieflook/core/repository.py +38 -0
- brieflook-0.1.0/src/brieflook/entrypoints/__init__.py +2 -0
- brieflook-0.1.0/src/brieflook/entrypoints/detector.py +137 -0
- brieflook-0.1.0/src/brieflook/entrypoints/models.py +16 -0
- brieflook-0.1.0/src/brieflook/git/__init__.py +0 -0
- brieflook-0.1.0/src/brieflook/guide/__init__.py +0 -0
- brieflook-0.1.0/src/brieflook/guide/detector.py +69 -0
- brieflook-0.1.0/src/brieflook/guide/models.py +16 -0
- brieflook-0.1.0/src/brieflook/guide/rules.py +42 -0
- brieflook-0.1.0/src/brieflook/project/__init__.py +0 -0
- brieflook-0.1.0/src/brieflook/project/detector.py +233 -0
- brieflook-0.1.0/src/brieflook/project/models.py +36 -0
- brieflook-0.1.0/src/brieflook/readme/detector.py +31 -0
- brieflook-0.1.0/src/brieflook/readme/models.py +16 -0
- brieflook-0.1.0/src/brieflook/run/__init__.py +2 -0
- brieflook-0.1.0/src/brieflook/run/detector.py +124 -0
- brieflook-0.1.0/src/brieflook/run/models.py +17 -0
- brieflook-0.1.0/src/brieflook/structure/__init__.py +2 -0
- brieflook-0.1.0/src/brieflook/structure/detector.py +65 -0
- brieflook-0.1.0/src/brieflook/structure/models.py +17 -0
- brieflook-0.1.0/src/brieflook/summary/detector.py +32 -0
- brieflook-0.1.0/src/brieflook/summary/models.py +14 -0
- brieflook-0.1.0/tests/test_project_detector.py +31 -0
- brieflook-0.1.0/tests/test_scanner.py +14 -0
- brieflook-0.1.0/uv.lock +184 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# ----------------------------
|
|
2
|
+
# Python
|
|
3
|
+
# ----------------------------
|
|
4
|
+
__pycache__/
|
|
5
|
+
*.py[cod]
|
|
6
|
+
*.pyc
|
|
7
|
+
|
|
8
|
+
# ----------------------------
|
|
9
|
+
# Virtual environments
|
|
10
|
+
# ----------------------------
|
|
11
|
+
.venv/
|
|
12
|
+
venv/
|
|
13
|
+
|
|
14
|
+
# ----------------------------
|
|
15
|
+
# Environment variables
|
|
16
|
+
# ----------------------------
|
|
17
|
+
.env
|
|
18
|
+
.env.*
|
|
19
|
+
!.env.example
|
|
20
|
+
|
|
21
|
+
# ----------------------------
|
|
22
|
+
# Test & quality tool caches
|
|
23
|
+
# ----------------------------
|
|
24
|
+
.pytest_cache/
|
|
25
|
+
.mypy_cache/
|
|
26
|
+
.ruff_cache/
|
|
27
|
+
.coverage
|
|
28
|
+
htmlcov/
|
|
29
|
+
|
|
30
|
+
# ----------------------------
|
|
31
|
+
# IDEs
|
|
32
|
+
# ----------------------------
|
|
33
|
+
.vscode/
|
|
34
|
+
.idea/
|
|
35
|
+
|
|
36
|
+
# ----------------------------
|
|
37
|
+
# OS
|
|
38
|
+
# ----------------------------
|
|
39
|
+
.DS_Store
|
|
40
|
+
Thumbs.db
|
brieflook-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nabil Khan
|
|
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.
|
|
22
|
+
|
brieflook-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: brieflook
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Understand any repository in minutes.
|
|
5
|
+
Project-URL: Homepage, https://github.com/nabilkhan-01/brieflook
|
|
6
|
+
Project-URL: Repository, https://github.com/nabilkhan-01/brieflook
|
|
7
|
+
Project-URL: Issues, https://github.com/nabilkhan-01/brieflook/issues
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: cli,codebase,developer-tools,onboarding,repository
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Software Development
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Requires-Dist: rich>=14.1.0
|
|
20
|
+
Requires-Dist: typer>=0.17.4
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# brieflook
|
|
24
|
+
|
|
25
|
+
> **Understand any repository in minutes.**
|
|
26
|
+
|
|
27
|
+
brieflook is an open-source developer tool that helps you understand unfamiliar codebases faster.
|
|
28
|
+
|
|
29
|
+
Instead of spending hours exploring folders, opening random files, and guessing where to start, brieflook analyzes a repository and provides a clear overview of its technologies, structure, and important entry points.
|
|
30
|
+
|
|
31
|
+
Our goal is simple:
|
|
32
|
+
|
|
33
|
+
> **Reduce repository onboarding from hours to minutes.**
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Status
|
|
38
|
+
|
|
39
|
+
🚧 Early Development
|
|
40
|
+
|
|
41
|
+
brieflook is under active development. The current focus is building a fast, reliable repository understanding engine before expanding into deeper repository analysis.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Vision
|
|
46
|
+
|
|
47
|
+
Every developer has experienced this moment:
|
|
48
|
+
|
|
49
|
+
- You clone a new repository.
|
|
50
|
+
- Hundreds of files appear.
|
|
51
|
+
- You don't know where to begin.
|
|
52
|
+
- You don't know which files matter.
|
|
53
|
+
- You don't know how everything fits together.
|
|
54
|
+
|
|
55
|
+
brieflook exists to remove that uncertainty.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Project
|
|
60
|
+
|
|
61
|
+
**Name:** brieflook
|
|
62
|
+
|
|
63
|
+
**Tagline:** *Understand any repository in minutes.*
|
|
64
|
+
|
|
65
|
+
**Started:** 2 August 2026
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
If brieflook saves you time understanding a repository, then it's doing exactly what it was built for.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# brieflook
|
|
2
|
+
|
|
3
|
+
> **Understand any repository in minutes.**
|
|
4
|
+
|
|
5
|
+
brieflook is an open-source developer tool that helps you understand unfamiliar codebases faster.
|
|
6
|
+
|
|
7
|
+
Instead of spending hours exploring folders, opening random files, and guessing where to start, brieflook analyzes a repository and provides a clear overview of its technologies, structure, and important entry points.
|
|
8
|
+
|
|
9
|
+
Our goal is simple:
|
|
10
|
+
|
|
11
|
+
> **Reduce repository onboarding from hours to minutes.**
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Status
|
|
16
|
+
|
|
17
|
+
🚧 Early Development
|
|
18
|
+
|
|
19
|
+
brieflook is under active development. The current focus is building a fast, reliable repository understanding engine before expanding into deeper repository analysis.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Vision
|
|
24
|
+
|
|
25
|
+
Every developer has experienced this moment:
|
|
26
|
+
|
|
27
|
+
- You clone a new repository.
|
|
28
|
+
- Hundreds of files appear.
|
|
29
|
+
- You don't know where to begin.
|
|
30
|
+
- You don't know which files matter.
|
|
31
|
+
- You don't know how everything fits together.
|
|
32
|
+
|
|
33
|
+
brieflook exists to remove that uncertainty.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Project
|
|
38
|
+
|
|
39
|
+
**Name:** brieflook
|
|
40
|
+
|
|
41
|
+
**Tagline:** *Understand any repository in minutes.*
|
|
42
|
+
|
|
43
|
+
**Started:** 2 August 2026
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
If brieflook saves you time understanding a repository, then it's doing exactly what it was built for.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "brieflook"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Understand any repository in minutes."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
license = { text = "MIT" }
|
|
8
|
+
|
|
9
|
+
keywords = [
|
|
10
|
+
"cli",
|
|
11
|
+
"repository",
|
|
12
|
+
"developer-tools",
|
|
13
|
+
"onboarding",
|
|
14
|
+
"codebase",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 3 - Alpha",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Topic :: Software Development",
|
|
24
|
+
"Environment :: Console",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
dependencies = [
|
|
28
|
+
"rich>=14.1.0",
|
|
29
|
+
"typer>=0.17.4",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.urls]
|
|
33
|
+
Homepage = "https://github.com/nabilkhan-01/brieflook"
|
|
34
|
+
Repository = "https://github.com/nabilkhan-01/brieflook"
|
|
35
|
+
Issues = "https://github.com/nabilkhan-01/brieflook/issues"
|
|
36
|
+
|
|
37
|
+
[project.scripts]
|
|
38
|
+
brieflook = "brieflook.cli.main:app"
|
|
39
|
+
|
|
40
|
+
[dependency-groups]
|
|
41
|
+
dev = [
|
|
42
|
+
"pytest>=8.4.1",
|
|
43
|
+
"ruff>=0.12.8",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[build-system]
|
|
47
|
+
requires = ["hatchling"]
|
|
48
|
+
build-backend = "hatchling.build"
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI entry point for brieflook.
|
|
3
|
+
|
|
4
|
+
Responsibilities:
|
|
5
|
+
- Parse command-line arguments.
|
|
6
|
+
- Invoke the appropriate application services.
|
|
7
|
+
- Format and display results to the terminal.
|
|
8
|
+
|
|
9
|
+
This module should never contain business logic.
|
|
10
|
+
Repository analysis belongs in the core and project modules.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
import typer
|
|
16
|
+
from rich.console import Console
|
|
17
|
+
from rich.table import Table
|
|
18
|
+
|
|
19
|
+
from brieflook.project.detector import ProjectDetector
|
|
20
|
+
from brieflook.guide.detector import GuideDetector
|
|
21
|
+
from brieflook.entrypoints.detector import EntryPointDetector
|
|
22
|
+
from brieflook.run.detector import RunGuideDetector
|
|
23
|
+
from brieflook.structure.detector import ProjectStructureDetector
|
|
24
|
+
from brieflook.core.index_builder import RepositoryIndexBuilder
|
|
25
|
+
|
|
26
|
+
app = typer.Typer(
|
|
27
|
+
no_args_is_help=True,
|
|
28
|
+
help="Understand any repository in minutes.",
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
console = Console()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@app.command()
|
|
35
|
+
def scan(
|
|
36
|
+
path: Path = typer.Argument(
|
|
37
|
+
Path("."),
|
|
38
|
+
exists=True,
|
|
39
|
+
file_okay=False,
|
|
40
|
+
dir_okay=True,
|
|
41
|
+
resolve_path=True,
|
|
42
|
+
help="Path to the repository.",
|
|
43
|
+
),
|
|
44
|
+
) -> None:
|
|
45
|
+
"""
|
|
46
|
+
Scan a repository.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
detector = ProjectDetector()
|
|
50
|
+
project = detector.detect(path)
|
|
51
|
+
|
|
52
|
+
index = RepositoryIndexBuilder().build(path)
|
|
53
|
+
|
|
54
|
+
guide = GuideDetector().detect(index, project)
|
|
55
|
+
|
|
56
|
+
table = Table(title="Repository")
|
|
57
|
+
|
|
58
|
+
table.add_column("Property", style="cyan")
|
|
59
|
+
table.add_column("Value", style="green")
|
|
60
|
+
|
|
61
|
+
table.add_row("Name", path.name)
|
|
62
|
+
table.add_row("Language", project.language)
|
|
63
|
+
table.add_row("Framework", project.framework or "Unknown")
|
|
64
|
+
table.add_row("Package Manager", project.package_manager or "Unknown")
|
|
65
|
+
table.add_row("Project Type", project.project_type or "Unknown")
|
|
66
|
+
|
|
67
|
+
console.print(table)
|
|
68
|
+
|
|
69
|
+
console.print()
|
|
70
|
+
|
|
71
|
+
guide_table = Table(title="Start Here")
|
|
72
|
+
|
|
73
|
+
guide_table.add_column("#", style="cyan", justify="right")
|
|
74
|
+
guide_table.add_column("Path", style="green")
|
|
75
|
+
guide_table.add_column("Reason")
|
|
76
|
+
|
|
77
|
+
for i, item in enumerate(guide, start=1):
|
|
78
|
+
guide_table.add_row(
|
|
79
|
+
str(i),
|
|
80
|
+
item.path,
|
|
81
|
+
item.reason,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
console.print(guide_table)
|
|
85
|
+
|
|
86
|
+
console.print()
|
|
87
|
+
|
|
88
|
+
entry_points = EntryPointDetector().detect(index)
|
|
89
|
+
|
|
90
|
+
entry_table = Table(title="Entry Points")
|
|
91
|
+
|
|
92
|
+
entry_table.add_column("#", style="cyan", justify="right")
|
|
93
|
+
entry_table.add_column("Path", style="green")
|
|
94
|
+
entry_table.add_column("Reason")
|
|
95
|
+
|
|
96
|
+
for i, ep in enumerate(entry_points, start=1):
|
|
97
|
+
entry_table.add_row(
|
|
98
|
+
str(i),
|
|
99
|
+
ep.path,
|
|
100
|
+
ep.reason,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
console.print(entry_table)
|
|
104
|
+
|
|
105
|
+
console.print()
|
|
106
|
+
|
|
107
|
+
run_guide = RunGuideDetector().detect(project, index)
|
|
108
|
+
|
|
109
|
+
run_table = Table(title="Run Guide")
|
|
110
|
+
|
|
111
|
+
run_table.add_column("Step", style="cyan")
|
|
112
|
+
run_table.add_column("Command", style="green")
|
|
113
|
+
|
|
114
|
+
if run_guide.install_command:
|
|
115
|
+
run_table.add_row("Install", run_guide.install_command)
|
|
116
|
+
if run_guide.run_command:
|
|
117
|
+
run_table.add_row("Run", run_guide.run_command)
|
|
118
|
+
if run_guide.test_command:
|
|
119
|
+
run_table.add_row("Test", run_guide.test_command)
|
|
120
|
+
|
|
121
|
+
console.print(run_table)
|
|
122
|
+
|
|
123
|
+
console.print()
|
|
124
|
+
|
|
125
|
+
structure = ProjectStructureDetector().detect(index)
|
|
126
|
+
|
|
127
|
+
structure_table = Table(title="Project Structure")
|
|
128
|
+
|
|
129
|
+
structure_table.add_column("#", style="cyan", justify="right")
|
|
130
|
+
structure_table.add_column("Directory", style="green")
|
|
131
|
+
structure_table.add_column("Purpose")
|
|
132
|
+
|
|
133
|
+
for i, d in enumerate(structure, start=1):
|
|
134
|
+
structure_table.add_row(
|
|
135
|
+
str(i),
|
|
136
|
+
d.path,
|
|
137
|
+
d.reason,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
console.print(structure_table)
|
|
File without changes
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Repository index.
|
|
3
|
+
|
|
4
|
+
Responsibilities:
|
|
5
|
+
- Store an in-memory representation of a repository.
|
|
6
|
+
- Provide fast filesystem lookups.
|
|
7
|
+
- Avoid repeated filesystem traversal.
|
|
8
|
+
|
|
9
|
+
This module should never:
|
|
10
|
+
- Detect technologies.
|
|
11
|
+
- Generate terminal output.
|
|
12
|
+
- Contain business rules.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(slots=True)
|
|
20
|
+
class RepositoryIndex:
|
|
21
|
+
"""
|
|
22
|
+
Fast lookup structure for repository contents.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
root: Path
|
|
26
|
+
|
|
27
|
+
files: dict[str, list[Path]]
|
|
28
|
+
directories: dict[str, list[Path]]
|
|
29
|
+
extensions: dict[str, list[Path]]
|
|
30
|
+
|
|
31
|
+
def find_file(self, filename: str) -> list[Path]:
|
|
32
|
+
"""
|
|
33
|
+
Return every file with the given filename.
|
|
34
|
+
"""
|
|
35
|
+
return self.files.get(filename, [])
|
|
36
|
+
|
|
37
|
+
def has_file(self, filename: str) -> bool:
|
|
38
|
+
"""
|
|
39
|
+
Return True if the repository contains the file.
|
|
40
|
+
"""
|
|
41
|
+
return filename in self.files
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Repository index builder.
|
|
3
|
+
|
|
4
|
+
Responsibilities:
|
|
5
|
+
- Traverse the repository once.
|
|
6
|
+
- Build a RepositoryIndex.
|
|
7
|
+
- Normalize filesystem information.
|
|
8
|
+
|
|
9
|
+
This module should never:
|
|
10
|
+
- Detect technologies.
|
|
11
|
+
- Rank files.
|
|
12
|
+
- Produce CLI output.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from collections import defaultdict
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
from brieflook.core.index import RepositoryIndex
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
IGNORED_DIRECTORIES = {
|
|
22
|
+
".git",
|
|
23
|
+
".venv",
|
|
24
|
+
"__pycache__",
|
|
25
|
+
".pytest_cache",
|
|
26
|
+
".ruff_cache",
|
|
27
|
+
".mypy_cache",
|
|
28
|
+
".idea",
|
|
29
|
+
".vscode",
|
|
30
|
+
"node_modules",
|
|
31
|
+
"dist",
|
|
32
|
+
"build",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class RepositoryIndexBuilder:
|
|
37
|
+
"""
|
|
38
|
+
Builds a RepositoryIndex by traversing the repository once.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def build(self, root: Path) -> RepositoryIndex:
|
|
42
|
+
|
|
43
|
+
files: dict[str, list[Path]] = defaultdict(list)
|
|
44
|
+
directories: dict[str, list[Path]] = defaultdict(list)
|
|
45
|
+
extensions: dict[str, list[Path]] = defaultdict(list)
|
|
46
|
+
|
|
47
|
+
for item in root.rglob("*"):
|
|
48
|
+
|
|
49
|
+
if any(part in IGNORED_DIRECTORIES for part in item.parts):
|
|
50
|
+
continue
|
|
51
|
+
|
|
52
|
+
if item.is_file():
|
|
53
|
+
|
|
54
|
+
files[item.name].append(item)
|
|
55
|
+
|
|
56
|
+
if item.suffix:
|
|
57
|
+
extensions[item.suffix].append(item)
|
|
58
|
+
|
|
59
|
+
elif item.is_dir():
|
|
60
|
+
|
|
61
|
+
directories[item.name].append(item)
|
|
62
|
+
|
|
63
|
+
return RepositoryIndex(
|
|
64
|
+
root=root,
|
|
65
|
+
files=dict(files),
|
|
66
|
+
directories=dict(directories),
|
|
67
|
+
extensions=dict(extensions),
|
|
68
|
+
)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Repository domain models.
|
|
3
|
+
|
|
4
|
+
This module defines the core data structures shared across brieflook.
|
|
5
|
+
|
|
6
|
+
These models represent facts about a repository without making
|
|
7
|
+
assumptions or interpretations.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(slots=True)
|
|
15
|
+
class RepositoryFacts:
|
|
16
|
+
"""
|
|
17
|
+
Raw facts collected from a repository.
|
|
18
|
+
|
|
19
|
+
Responsibilities:
|
|
20
|
+
- Store filesystem facts.
|
|
21
|
+
- Represent the current state of a repository.
|
|
22
|
+
- Serve as input for higher-level analysis.
|
|
23
|
+
|
|
24
|
+
This class should never:
|
|
25
|
+
- Detect frameworks.
|
|
26
|
+
- Infer project types.
|
|
27
|
+
- Perform filesystem scanning.
|
|
28
|
+
- Contain business logic.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
name: str
|
|
32
|
+
root: Path
|
|
33
|
+
|
|
34
|
+
file_count: int
|
|
35
|
+
directory_count: int
|
|
36
|
+
|
|
37
|
+
has_git: bool
|
|
38
|
+
has_readme: bool
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Application entry point detection.
|
|
3
|
+
|
|
4
|
+
Responsibilities:
|
|
5
|
+
- Identify real application entry points from the repository index.
|
|
6
|
+
- Provide a reason for each detected entry point.
|
|
7
|
+
|
|
8
|
+
This module should never:
|
|
9
|
+
- Traverse the filesystem.
|
|
10
|
+
- Generate terminal output.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from brieflook.core.index import RepositoryIndex
|
|
17
|
+
from brieflook.entrypoints.models import EntryPoint
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(frozen=True, slots=True)
|
|
21
|
+
class _Rule:
|
|
22
|
+
filename: str
|
|
23
|
+
reason: str
|
|
24
|
+
root_only: bool = False
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
_RULES: list[_Rule] = [
|
|
28
|
+
# Python
|
|
29
|
+
_Rule("__main__.py", "Python package entry point."),
|
|
30
|
+
_Rule("main.py", "Python application entry point.", root_only=True),
|
|
31
|
+
_Rule("manage.py", "Django management entry point."),
|
|
32
|
+
_Rule("app.py", "Python application entry point.", root_only=True),
|
|
33
|
+
_Rule("wsgi.py", "WSGI application entry point."),
|
|
34
|
+
_Rule("asgi.py", "ASGI application entry point."),
|
|
35
|
+
|
|
36
|
+
# JavaScript / TypeScript
|
|
37
|
+
_Rule("server.js", "Node.js server entry point."),
|
|
38
|
+
_Rule("index.js", "JavaScript application entry point."),
|
|
39
|
+
_Rule("main.js", "JavaScript application entry point."),
|
|
40
|
+
|
|
41
|
+
# Next.js
|
|
42
|
+
_Rule("page.tsx", "Next.js application entry point."),
|
|
43
|
+
|
|
44
|
+
# Rust
|
|
45
|
+
_Rule("main.rs", "Rust application entry point."),
|
|
46
|
+
|
|
47
|
+
# Java
|
|
48
|
+
_Rule("Application.java", "Java application entry point."),
|
|
49
|
+
_Rule("Main.java", "Java application entry point."),
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
# JS/TS files that are only valid inside a src/ directory.
|
|
53
|
+
_SRC_ONLY = {"main.ts", "main.tsx", "index.ts", "index.tsx", "main.js", "main.jsx", "index.js", "index.jsx"}
|
|
54
|
+
|
|
55
|
+
# Next.js page files and the directories they must live under.
|
|
56
|
+
_NEXTJS_DIRS = {"page.tsx": {"app", "pages"}}
|
|
57
|
+
|
|
58
|
+
# Next.js pages/index.tsx pattern.
|
|
59
|
+
_PAGES_INDEX = "index.tsx"
|
|
60
|
+
|
|
61
|
+
# Go entry point pattern: main.go inside a cmd/ tree.
|
|
62
|
+
_GO_ENTRY = "main.go"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class EntryPointDetector:
|
|
66
|
+
"""
|
|
67
|
+
Detect application entry points from a RepositoryIndex.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
def detect(self, index: RepositoryIndex) -> list[EntryPoint]:
|
|
71
|
+
results: list[EntryPoint] = []
|
|
72
|
+
|
|
73
|
+
for rule in _RULES:
|
|
74
|
+
matches = index.find_file(rule.filename)
|
|
75
|
+
for file_path in matches:
|
|
76
|
+
if self._matches_rule(rule, file_path, index.root):
|
|
77
|
+
rel = file_path.relative_to(index.root).as_posix()
|
|
78
|
+
results.append(EntryPoint(path=rel, reason=rule.reason))
|
|
79
|
+
|
|
80
|
+
# src-only JS/TS files
|
|
81
|
+
for filename in sorted(_SRC_ONLY):
|
|
82
|
+
for file_path in index.find_file(filename):
|
|
83
|
+
if self._is_in_src(file_path, index.root):
|
|
84
|
+
rel = file_path.relative_to(index.root).as_posix()
|
|
85
|
+
reason = self._js_ts_reason(filename)
|
|
86
|
+
results.append(EntryPoint(path=rel, reason=reason))
|
|
87
|
+
|
|
88
|
+
# Next.js pages/index.tsx
|
|
89
|
+
for file_path in index.find_file(_PAGES_INDEX):
|
|
90
|
+
if self._is_nextjs_pages_index(file_path):
|
|
91
|
+
rel = file_path.relative_to(index.root).as_posix()
|
|
92
|
+
results.append(EntryPoint(path=rel, reason="Next.js pages entry point."))
|
|
93
|
+
|
|
94
|
+
# Go cmd/**/main.go
|
|
95
|
+
for file_path in index.find_file(_GO_ENTRY):
|
|
96
|
+
if self._is_go_cmd_entry(file_path):
|
|
97
|
+
rel = file_path.relative_to(index.root).as_posix()
|
|
98
|
+
results.append(EntryPoint(path=rel, reason="Go application entry point."))
|
|
99
|
+
|
|
100
|
+
return results
|
|
101
|
+
|
|
102
|
+
def _matches_rule(self, rule: _Rule, file_path: Path, root: Path) -> bool:
|
|
103
|
+
if rule.root_only:
|
|
104
|
+
return file_path.parent == root
|
|
105
|
+
|
|
106
|
+
# Next.js page.tsx must be inside app/ directory.
|
|
107
|
+
if rule.filename in _NEXTJS_DIRS:
|
|
108
|
+
return self._is_in_nextjs_dir(rule.filename, file_path)
|
|
109
|
+
|
|
110
|
+
# Rust main.rs must be inside src/ directory.
|
|
111
|
+
if rule.filename == "main.rs":
|
|
112
|
+
return self._is_in_src(file_path, root)
|
|
113
|
+
|
|
114
|
+
return True
|
|
115
|
+
|
|
116
|
+
def _is_in_src(self, file_path: Path, root: Path) -> bool:
|
|
117
|
+
rel = file_path.relative_to(root)
|
|
118
|
+
return "src" in rel.parent.parts
|
|
119
|
+
|
|
120
|
+
def _is_in_nextjs_dir(self, filename: str, file_path: Path) -> bool:
|
|
121
|
+
valid_dirs = _NEXTJS_DIRS[filename]
|
|
122
|
+
return any(part in valid_dirs for part in file_path.parts)
|
|
123
|
+
|
|
124
|
+
def _is_nextjs_pages_index(self, file_path: Path) -> bool:
|
|
125
|
+
parts = file_path.parts
|
|
126
|
+
return "pages" in parts
|
|
127
|
+
|
|
128
|
+
def _is_go_cmd_entry(self, file_path: Path) -> bool:
|
|
129
|
+
return "cmd" in file_path.parts
|
|
130
|
+
|
|
131
|
+
def _js_ts_reason(self, filename: str) -> str:
|
|
132
|
+
if filename.endswith((".tsx", ".jsx")):
|
|
133
|
+
return "React application bootstrap."
|
|
134
|
+
if filename.endswith(".ts"):
|
|
135
|
+
return "TypeScript application entry point."
|
|
136
|
+
return "JavaScript application entry point."
|
|
137
|
+
|
|
File without changes
|
|
File without changes
|