codegraph-gen 1.1.0__tar.gz → 1.3.2__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.
- {codegraph_gen-1.1.0 → codegraph_gen-1.3.2}/PKG-INFO +16 -6
- {codegraph_gen-1.1.0 → codegraph_gen-1.3.2}/README.md +14 -5
- {codegraph_gen-1.1.0 → codegraph_gen-1.3.2}/pyproject.toml +4 -1
- {codegraph_gen-1.1.0 → codegraph_gen-1.3.2}/src/codegraph_gen/__main__.py +187 -17
- codegraph_gen-1.3.2/src/codegraph_gen/config.py +132 -0
- {codegraph_gen-1.1.0 → codegraph_gen-1.3.2}/src/codegraph_gen/detect.py +25 -1
- {codegraph_gen-1.1.0 → codegraph_gen-1.3.2}/src/codegraph_gen/engine.py +4 -1
- {codegraph_gen-1.1.0 → codegraph_gen-1.3.2}/src/codegraph_gen/parser/base.py +42 -14
- {codegraph_gen-1.1.0 → codegraph_gen-1.3.2}/src/codegraph_gen/parser/cpp.py +46 -9
- {codegraph_gen-1.1.0 → codegraph_gen-1.3.2}/src/codegraph_gen/parser/go.py +45 -8
- {codegraph_gen-1.1.0 → codegraph_gen-1.3.2}/src/codegraph_gen/parser/javascript.py +45 -8
- {codegraph_gen-1.1.0 → codegraph_gen-1.3.2}/src/codegraph_gen/parser/kotlin.py +45 -8
- codegraph_gen-1.3.2/src/codegraph_gen/parser/ocaml.py +390 -0
- {codegraph_gen-1.1.0 → codegraph_gen-1.3.2}/src/codegraph_gen/parser/python.py +44 -7
- {codegraph_gen-1.1.0 → codegraph_gen-1.3.2}/src/codegraph_gen/parser/rust.py +45 -8
- {codegraph_gen-1.1.0 → codegraph_gen-1.3.2}/src/codegraph_gen/parser/swift.py +45 -8
- codegraph_gen-1.3.2/src/codegraph_gen/resolver.py +379 -0
- codegraph_gen-1.3.2/src/codegraph_gen/resolver_context.py +118 -0
- codegraph_gen-1.3.2/src/codegraph_gen/resolver_steps.py +477 -0
- {codegraph_gen-1.1.0 → codegraph_gen-1.3.2}/src/codegraph_gen/resolver_strategy.py +138 -1
- codegraph_gen-1.3.2/src/codegraph_gen/scope.py +10 -0
- codegraph_gen-1.3.2/src/codegraph_gen/visualizer.py +569 -0
- {codegraph_gen-1.1.0 → codegraph_gen-1.3.2}/src/codegraph_gen/writer.py +27 -2
- codegraph_gen-1.1.0/src/codegraph_gen/config.py +0 -76
- codegraph_gen-1.1.0/src/codegraph_gen/resolver.py +0 -650
- {codegraph_gen-1.1.0 → codegraph_gen-1.3.2}/src/codegraph_gen/__init__.py +0 -0
- {codegraph_gen-1.1.0 → codegraph_gen-1.3.2}/src/codegraph_gen/ai.py +0 -0
- {codegraph_gen-1.1.0 → codegraph_gen-1.3.2}/src/codegraph_gen/analyzer.py +0 -0
- {codegraph_gen-1.1.0 → codegraph_gen-1.3.2}/src/codegraph_gen/builder.py +0 -0
- {codegraph_gen-1.1.0 → codegraph_gen-1.3.2}/src/codegraph_gen/cluster.py +0 -0
- {codegraph_gen-1.1.0 → codegraph_gen-1.3.2}/src/codegraph_gen/parser/__init__.py +0 -0
- {codegraph_gen-1.1.0 → codegraph_gen-1.3.2}/src/codegraph_gen/py.typed +0 -0
- {codegraph_gen-1.1.0 → codegraph_gen-1.3.2}/src/codegraph_gen/renderer.py +0 -0
- {codegraph_gen-1.1.0 → codegraph_gen-1.3.2}/src/codegraph_gen/schema.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: codegraph-gen
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.2
|
|
4
4
|
Summary: AST-based codebase knowledge graph generator in Markdown
|
|
5
5
|
Keywords: knowledge-graph,ast,codebase,markdown,tree-sitter,visualization,static-analysis,ai-agent,obsidian
|
|
6
6
|
Author: twn39
|
|
@@ -28,6 +28,7 @@ Requires-Dist: pydantic>=2.0.0
|
|
|
28
28
|
Requires-Dist: tree-sitter-c>=0.24.2
|
|
29
29
|
Requires-Dist: tree-sitter-cpp>=0.23.4
|
|
30
30
|
Requires-Dist: tree-sitter-kotlin>=1.1.0
|
|
31
|
+
Requires-Dist: tree-sitter-ocaml>=0.25.0
|
|
31
32
|
Requires-Python: >=3.11
|
|
32
33
|
Project-URL: Homepage, https://github.com/twn39/codegraph
|
|
33
34
|
Project-URL: Repository, https://github.com/twn39/codegraph
|
|
@@ -51,7 +52,7 @@ Description-Content-Type: text/markdown
|
|
|
51
52
|
|
|
52
53
|
## 🚀 核心特性
|
|
53
54
|
|
|
54
|
-
- **多语言 AST 解析**:基于 `tree-sitter`,原生支持 **Python, JavaScript, TypeScript, Kotlin, Go, Rust, Swift
|
|
55
|
+
- **多语言 AST 解析**:基于 `tree-sitter`,原生支持 **Python, JavaScript, TypeScript, Kotlin, Go, Rust, Swift, C, C++**。
|
|
55
56
|
- **语义边解析与绑定**:静态解析跨文件的函数/方法调用(`calls`)、类型继承/接口实现(`inherits`/`implements`)以及文件导入关系(`imports`)。
|
|
56
57
|
- **逻辑组件自动聚类**:利用贪心模块度社区发现算法(Louvain Modularity Clustering)将紧密耦合的文件和符号自动聚类为 **Component(逻辑组件)**,并根据组件核心节点智能命名。
|
|
57
58
|
- **架构脆弱性分析**:自动识别 **God Nodes(度数最高的核心抽象)**,并静态检测文件级别的 **循环导入依赖(Circular Imports)**。
|
|
@@ -61,7 +62,16 @@ Description-Content-Type: text/markdown
|
|
|
61
62
|
|
|
62
63
|
## 📦 架构概览
|
|
63
64
|
|
|
64
|
-
|
|
65
|
+
```mermaid
|
|
66
|
+
flowchart LR
|
|
67
|
+
Workspace["工作区源码 Workspace"] --> Detect["detect: 语言识别与过滤"]
|
|
68
|
+
Detect --> Parser["parser: Tree-Sitter AST 符号提取"]
|
|
69
|
+
Parser --> Builder["builder: NetworkX 语义图组装与绑定"]
|
|
70
|
+
Builder --> Cluster["cluster: 社区模块度聚类命名"]
|
|
71
|
+
Cluster --> Analyze["analyze: 上帝节点与循环导入分析"]
|
|
72
|
+
Analyze --> Export["export: 导出至 .codegraph/"]
|
|
73
|
+
Export --> Generate["生成 AGENT_PROMPT.md / AGENTS.md / README.md / nodes / components"]
|
|
74
|
+
```
|
|
65
75
|
|
|
66
76
|
---
|
|
67
77
|
|
|
@@ -83,11 +93,11 @@ uv pip install codegraph-gen
|
|
|
83
93
|
|
|
84
94
|
### 注册 AI Agent 斜杠命令
|
|
85
95
|
|
|
86
|
-
`codegraph-gen` 支持一键将 `/codegraph` 自定义斜杠命令注册到您的 AI Agent(如 Codex 或
|
|
96
|
+
`codegraph-gen` 支持一键将 `/codegraph` 自定义斜杠命令注册到您的 AI Agent(如 Codex、Antigravity 或 Crush)的全局配置中:
|
|
87
97
|
|
|
88
98
|
```bash
|
|
89
|
-
# 为 Codex / Antigravity 注入 /codegraph 全局斜杠命令
|
|
90
|
-
codegraph install --platform
|
|
99
|
+
# 为 Codex / Antigravity / Crush 注入 /codegraph 全局斜杠命令
|
|
100
|
+
codegraph install --platform crush
|
|
91
101
|
```
|
|
92
102
|
|
|
93
103
|
注册完成后,在对应的 Agent 终端中,您只需输入 `/codegraph` 即可全自动运行整个图谱的提取、分析与回写流程。
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
## 🚀 核心特性
|
|
17
17
|
|
|
18
|
-
- **多语言 AST 解析**:基于 `tree-sitter`,原生支持 **Python, JavaScript, TypeScript, Kotlin, Go, Rust, Swift
|
|
18
|
+
- **多语言 AST 解析**:基于 `tree-sitter`,原生支持 **Python, JavaScript, TypeScript, Kotlin, Go, Rust, Swift, C, C++**。
|
|
19
19
|
- **语义边解析与绑定**:静态解析跨文件的函数/方法调用(`calls`)、类型继承/接口实现(`inherits`/`implements`)以及文件导入关系(`imports`)。
|
|
20
20
|
- **逻辑组件自动聚类**:利用贪心模块度社区发现算法(Louvain Modularity Clustering)将紧密耦合的文件和符号自动聚类为 **Component(逻辑组件)**,并根据组件核心节点智能命名。
|
|
21
21
|
- **架构脆弱性分析**:自动识别 **God Nodes(度数最高的核心抽象)**,并静态检测文件级别的 **循环导入依赖(Circular Imports)**。
|
|
@@ -25,7 +25,16 @@
|
|
|
25
25
|
|
|
26
26
|
## 📦 架构概览
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
```mermaid
|
|
29
|
+
flowchart LR
|
|
30
|
+
Workspace["工作区源码 Workspace"] --> Detect["detect: 语言识别与过滤"]
|
|
31
|
+
Detect --> Parser["parser: Tree-Sitter AST 符号提取"]
|
|
32
|
+
Parser --> Builder["builder: NetworkX 语义图组装与绑定"]
|
|
33
|
+
Builder --> Cluster["cluster: 社区模块度聚类命名"]
|
|
34
|
+
Cluster --> Analyze["analyze: 上帝节点与循环导入分析"]
|
|
35
|
+
Analyze --> Export["export: 导出至 .codegraph/"]
|
|
36
|
+
Export --> Generate["生成 AGENT_PROMPT.md / AGENTS.md / README.md / nodes / components"]
|
|
37
|
+
```
|
|
29
38
|
|
|
30
39
|
---
|
|
31
40
|
|
|
@@ -47,11 +56,11 @@ uv pip install codegraph-gen
|
|
|
47
56
|
|
|
48
57
|
### 注册 AI Agent 斜杠命令
|
|
49
58
|
|
|
50
|
-
`codegraph-gen` 支持一键将 `/codegraph` 自定义斜杠命令注册到您的 AI Agent(如 Codex 或
|
|
59
|
+
`codegraph-gen` 支持一键将 `/codegraph` 自定义斜杠命令注册到您的 AI Agent(如 Codex、Antigravity 或 Crush)的全局配置中:
|
|
51
60
|
|
|
52
61
|
```bash
|
|
53
|
-
# 为 Codex / Antigravity 注入 /codegraph 全局斜杠命令
|
|
54
|
-
codegraph install --platform
|
|
62
|
+
# 为 Codex / Antigravity / Crush 注入 /codegraph 全局斜杠命令
|
|
63
|
+
codegraph install --platform crush
|
|
55
64
|
```
|
|
56
65
|
|
|
57
66
|
注册完成后,在对应的 Agent 终端中,您只需输入 `/codegraph` 即可全自动运行整个图谱的提取、分析与回写流程。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "codegraph-gen"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.3.2"
|
|
4
4
|
description = "AST-based codebase knowledge graph generator in Markdown"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -44,6 +44,7 @@ dependencies = [
|
|
|
44
44
|
"tree-sitter-c>=0.24.2",
|
|
45
45
|
"tree-sitter-cpp>=0.23.4",
|
|
46
46
|
"tree-sitter-kotlin>=1.1.0",
|
|
47
|
+
"tree-sitter-ocaml>=0.25.0",
|
|
47
48
|
]
|
|
48
49
|
|
|
49
50
|
[dependency-groups]
|
|
@@ -51,6 +52,8 @@ dev = [
|
|
|
51
52
|
"pytest>=8.0.0",
|
|
52
53
|
"ruff>=0.15.16",
|
|
53
54
|
"ty>=0.0.44",
|
|
55
|
+
"plotly>=5.0.0",
|
|
56
|
+
"numpy>=1.20.0",
|
|
54
57
|
]
|
|
55
58
|
|
|
56
59
|
[project.urls]
|
|
@@ -10,12 +10,26 @@ from rich.progress import (
|
|
|
10
10
|
MofNCompleteColumn,
|
|
11
11
|
)
|
|
12
12
|
|
|
13
|
-
from codegraph_gen.config import
|
|
13
|
+
from codegraph_gen.config import (
|
|
14
|
+
CodegraphConfig,
|
|
15
|
+
DEFAULT_EXCLUSIONS,
|
|
16
|
+
LANGUAGE_EXTENSIONS,
|
|
17
|
+
load_project_config,
|
|
18
|
+
PROJECT_CONFIG_FILE,
|
|
19
|
+
)
|
|
14
20
|
|
|
15
21
|
console = Console()
|
|
16
22
|
|
|
23
|
+
try:
|
|
24
|
+
from importlib.metadata import version
|
|
25
|
+
|
|
26
|
+
__version__ = version("codegraph-gen")
|
|
27
|
+
except Exception:
|
|
28
|
+
__version__ = "1.3.2"
|
|
29
|
+
|
|
17
30
|
|
|
18
31
|
@click.group()
|
|
32
|
+
@click.version_option(version=__version__, prog_name="codegraph")
|
|
19
33
|
def cli():
|
|
20
34
|
"""codegraph - Build a Markdown knowledge graph of your codebase for AI analysis."""
|
|
21
35
|
pass
|
|
@@ -69,26 +83,99 @@ def build(
|
|
|
69
83
|
"""Parses the codebase in SRC_DIR and exports the Markdown graph vault."""
|
|
70
84
|
console.print("[bold blue]Starting codegraph analysis...[/bold blue]")
|
|
71
85
|
|
|
72
|
-
|
|
86
|
+
workspace = src_dir.resolve()
|
|
87
|
+
|
|
88
|
+
# ── Load project config file (.codegraphrc) ────────────────────────────
|
|
89
|
+
project_cfg = load_project_config(workspace)
|
|
90
|
+
|
|
91
|
+
if project_cfg is not None:
|
|
92
|
+
console.print(
|
|
93
|
+
f"[dim]📄 Found project config: [underline]{workspace / PROJECT_CONFIG_FILE}[/underline][/dim]"
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# ── Merge: CLI args take priority over .codegraphrc ────────────────────
|
|
97
|
+
# Exclusions: default set + .codegraphrc extras + CLI --exclude (cumulative)
|
|
73
98
|
exclusions = set(DEFAULT_EXCLUSIONS)
|
|
99
|
+
if project_cfg and project_cfg.exclude:
|
|
100
|
+
exclusions.update(project_cfg.exclude)
|
|
74
101
|
if exclude:
|
|
75
102
|
exclusions.update(exclude)
|
|
76
103
|
|
|
104
|
+
# Output directory: CLI --output > .codegraphrc output > default
|
|
105
|
+
if output != Path(".codegraph"):
|
|
106
|
+
# User explicitly passed --output on CLI
|
|
107
|
+
resolved_output = output.resolve()
|
|
108
|
+
elif project_cfg and project_cfg.output != ".codegraph":
|
|
109
|
+
resolved_output = (workspace / project_cfg.output).resolve()
|
|
110
|
+
else:
|
|
111
|
+
resolved_output = (workspace / ".codegraph").resolve()
|
|
112
|
+
|
|
113
|
+
# Languages: CLI has no language filter option yet; use .codegraphrc if present
|
|
114
|
+
all_languages = set(LANGUAGE_EXTENSIONS.keys())
|
|
115
|
+
if project_cfg and project_cfg.languages:
|
|
116
|
+
valid = {lang for lang in project_cfg.languages if lang in all_languages}
|
|
117
|
+
invalid = set(project_cfg.languages) - valid
|
|
118
|
+
if invalid:
|
|
119
|
+
console.print(
|
|
120
|
+
f"[yellow]⚠ .codegraphrc: unknown languages ignored: {', '.join(sorted(invalid))}[/yellow]"
|
|
121
|
+
)
|
|
122
|
+
languages = valid if valid else all_languages
|
|
123
|
+
else:
|
|
124
|
+
languages = all_languages
|
|
125
|
+
|
|
126
|
+
# Workers: CLI --workers > .codegraphrc workers > CPU count
|
|
77
127
|
import os
|
|
78
128
|
|
|
79
129
|
if not parallel:
|
|
80
130
|
max_workers = 1
|
|
81
131
|
elif workers is not None:
|
|
82
132
|
max_workers = workers
|
|
133
|
+
elif project_cfg and project_cfg.workers is not None:
|
|
134
|
+
max_workers = project_cfg.workers
|
|
83
135
|
else:
|
|
84
136
|
max_workers = os.cpu_count() or 4
|
|
85
137
|
|
|
138
|
+
# Cache: CLI --cache/--no-cache flag always applies; .codegraphrc only when CLI default
|
|
139
|
+
# (click default for cache is True, so we trust project_cfg when CLI wasn't explicitly set)
|
|
140
|
+
effective_cache = (
|
|
141
|
+
cache if cache is not None else (project_cfg.cache if project_cfg else True)
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# Include dirs: from .codegraphrc include whitelist (no CLI equivalent)
|
|
145
|
+
include_dirs = None
|
|
146
|
+
if project_cfg and project_cfg.include:
|
|
147
|
+
include_dirs = []
|
|
148
|
+
for subdir in project_cfg.include:
|
|
149
|
+
resolved = (workspace / subdir).resolve()
|
|
150
|
+
if not resolved.exists():
|
|
151
|
+
console.print(
|
|
152
|
+
f"[yellow]⚠ .codegraphrc include '{subdir}' does not exist, skipping.[/yellow]"
|
|
153
|
+
)
|
|
154
|
+
else:
|
|
155
|
+
include_dirs.append(resolved)
|
|
156
|
+
if not include_dirs:
|
|
157
|
+
include_dirs = None # All entries invalid → fall back to full scan
|
|
158
|
+
|
|
159
|
+
# Print effective config summary when a project config was loaded
|
|
160
|
+
if project_cfg is not None:
|
|
161
|
+
parts = []
|
|
162
|
+
if include_dirs:
|
|
163
|
+
parts.append(f"include: {', '.join(p.name for p in include_dirs)}")
|
|
164
|
+
if project_cfg.exclude:
|
|
165
|
+
parts.append(f"extra exclude: {', '.join(project_cfg.exclude)}")
|
|
166
|
+
if project_cfg.languages:
|
|
167
|
+
parts.append(f"languages: {', '.join(sorted(languages))}")
|
|
168
|
+
if parts:
|
|
169
|
+
console.print(f"[dim] {' | '.join(parts)}[/dim]")
|
|
170
|
+
|
|
86
171
|
config = CodegraphConfig(
|
|
87
|
-
workspace_dir=
|
|
88
|
-
output_dir=
|
|
172
|
+
workspace_dir=workspace,
|
|
173
|
+
output_dir=resolved_output,
|
|
89
174
|
exclusions=exclusions,
|
|
175
|
+
languages=languages,
|
|
90
176
|
max_workers=max_workers,
|
|
91
|
-
use_cache=
|
|
177
|
+
use_cache=effective_cache,
|
|
178
|
+
include_dirs=include_dirs,
|
|
92
179
|
)
|
|
93
180
|
|
|
94
181
|
from codegraph_gen.engine import CodegraphEngine, PipelineStage
|
|
@@ -176,7 +263,7 @@ def build(
|
|
|
176
263
|
"--platform",
|
|
177
264
|
"-p",
|
|
178
265
|
default="codex",
|
|
179
|
-
type=click.Choice(["codex", "antigravity"]),
|
|
266
|
+
type=click.Choice(["codex", "antigravity", "crush"]),
|
|
180
267
|
help="The AI agent platform to integrate with.",
|
|
181
268
|
)
|
|
182
269
|
def install(platform: str):
|
|
@@ -190,6 +277,8 @@ def install(platform: str):
|
|
|
190
277
|
skills_dir = Path.home() / ".codex" / "skills" / "codegraph"
|
|
191
278
|
elif platform == "antigravity":
|
|
192
279
|
skills_dir = Path.home() / ".gemini" / "config" / "skills" / "codegraph"
|
|
280
|
+
elif platform == "crush":
|
|
281
|
+
skills_dir = Path.home() / ".config" / "crush" / "skills" / "codegraph"
|
|
193
282
|
else:
|
|
194
283
|
skills_dir = Path.home() / ".codex" / "skills" / "codegraph"
|
|
195
284
|
|
|
@@ -212,9 +301,41 @@ Build a codebase knowledge graph using `codegraph` for any folder, cluster symbo
|
|
|
212
301
|
/codegraph --exclude <pattern> # Build and exclude specific folders/patterns
|
|
213
302
|
```
|
|
214
303
|
|
|
304
|
+
### Project Configuration File (`.codegraphrc`)
|
|
305
|
+
|
|
306
|
+
Projects can place a `.codegraphrc` JSON file in their root directory to persist build settings. When present, `codegraph build` automatically loads it — no extra flags needed.
|
|
307
|
+
|
|
308
|
+
```json
|
|
309
|
+
{
|
|
310
|
+
"include": ["src", "tests"],
|
|
311
|
+
"exclude": ["dist", "third_party"],
|
|
312
|
+
"output": ".codegraph",
|
|
313
|
+
"languages": ["python", "typescript"],
|
|
314
|
+
"workers": 4,
|
|
315
|
+
"cache": true
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
| Field | Type | Description |
|
|
320
|
+
|---|---|---|
|
|
321
|
+
| `include` | `string[]` | Subdirectory whitelist — only these dirs are scanned. Omit to scan the entire workspace. |
|
|
322
|
+
| `exclude` | `string[]` | Extra directory names to exclude (appended to built-in defaults). |
|
|
323
|
+
| `output` | `string` | Output directory, relative to workspace root. Default: `.codegraph` |
|
|
324
|
+
| `languages` | `string[]` | Language whitelist. Omit to include all supported languages. |
|
|
325
|
+
| `workers` | `int` | Number of parallel worker processes. |
|
|
326
|
+
| `cache` | `bool` | Enable incremental parse cache. Default: `true` |
|
|
327
|
+
|
|
328
|
+
CLI flags always take priority over `.codegraphrc` values. `--exclude` is cumulative (appends to the config file list).
|
|
329
|
+
|
|
215
330
|
## What You Must Do When Invoked
|
|
216
331
|
|
|
217
|
-
|
|
332
|
+
### Check for a project config file first
|
|
333
|
+
|
|
334
|
+
Before deciding which directory or flags to use, check if a `.codegraphrc` file exists in the project root:
|
|
335
|
+
- If it exists, read it to understand which directories the project wants scanned (`include`) and excluded (`exclude`). Honor those settings — do NOT override them with your own guesses.
|
|
336
|
+
- If it does NOT exist, follow the fallback rules below.
|
|
337
|
+
|
|
338
|
+
If the user invoked `/codegraph` with no path and there is NO `.codegraphrc`, do not ask the user for a path. Instead, prioritize targeting the primary source directory (e.g. `src/`, `lib/`, `app/`) and test directory (e.g. `tests/`, `test/`).
|
|
218
339
|
- If specific source or test folders are found, run the build targeting those folders, or build the root `.` but exclude other non-code/non-test directories (e.g., `docs/`, `scripts/`, `examples/`) using the `--exclude` flag to keep the graph focused on code and tests.
|
|
219
340
|
- Otherwise, default to `.` (current directory).
|
|
220
341
|
|
|
@@ -234,7 +355,7 @@ elif [ -f "venv/bin/codegraph" ]; then
|
|
|
234
355
|
CODEGRAPH_BIN="venv/bin/codegraph"
|
|
235
356
|
else
|
|
236
357
|
if ! command -v codegraph >/dev/null 2>&1; then
|
|
237
|
-
uv tool install codegraph
|
|
358
|
+
uv tool install codegraph-gen
|
|
238
359
|
fi
|
|
239
360
|
CODEGRAPH_BIN="codegraph"
|
|
240
361
|
fi
|
|
@@ -243,7 +364,7 @@ echo "Using codegraph binary: $CODEGRAPH_BIN"
|
|
|
243
364
|
|
|
244
365
|
### Step 2 - Build the Knowledge Graph
|
|
245
366
|
|
|
246
|
-
Run the resolved `$CODEGRAPH_BIN` on the specified directory:
|
|
367
|
+
Run the resolved `$CODEGRAPH_BIN` on the specified directory. If a `.codegraphrc` is present in the project root, simply run without extra flags — the config file is picked up automatically:
|
|
247
368
|
```bash
|
|
248
369
|
$CODEGRAPH_BIN build INPUT_PATH
|
|
249
370
|
# Or with additional exclude arguments if provided by the user
|
|
@@ -289,17 +410,66 @@ Finally, reply to the user in English, summarizing:
|
|
|
289
410
|
|
|
290
411
|
|
|
291
412
|
@cli.command()
|
|
292
|
-
|
|
293
|
-
""
|
|
413
|
+
@click.argument(
|
|
414
|
+
"src_dir",
|
|
415
|
+
type=click.Path(exists=True, file_okay=False, path_type=Path),
|
|
416
|
+
default=".",
|
|
417
|
+
)
|
|
418
|
+
@click.option(
|
|
419
|
+
"--output",
|
|
420
|
+
"-o",
|
|
421
|
+
type=click.Path(path_type=Path),
|
|
422
|
+
default=Path(".codegraph"),
|
|
423
|
+
help="Directory where the Markdown vault was written.",
|
|
424
|
+
)
|
|
425
|
+
@click.option(
|
|
426
|
+
"--open/--no-open",
|
|
427
|
+
"open_browser",
|
|
428
|
+
default=True,
|
|
429
|
+
help="Automatically open the interactive HTML page in the browser.",
|
|
430
|
+
)
|
|
431
|
+
def visualize(src_dir: Path, output: Path, open_browser: bool):
|
|
432
|
+
"""Generates an interactive Plotly-based HTML visualization of the graph."""
|
|
433
|
+
import sys
|
|
434
|
+
|
|
435
|
+
console.print(
|
|
436
|
+
"[bold blue]Generating interactive graph visualization...[/bold blue]"
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
workspace = src_dir.resolve()
|
|
440
|
+
|
|
441
|
+
# Resolve output directory using same logic as build
|
|
442
|
+
project_cfg = load_project_config(workspace)
|
|
443
|
+
if output != Path(".codegraph"):
|
|
444
|
+
resolved_output = output.resolve()
|
|
445
|
+
elif project_cfg and project_cfg.output != ".codegraph":
|
|
446
|
+
resolved_output = (workspace / project_cfg.output).resolve()
|
|
447
|
+
else:
|
|
448
|
+
resolved_output = (workspace / ".codegraph").resolve()
|
|
449
|
+
|
|
294
450
|
try:
|
|
295
|
-
from
|
|
451
|
+
from codegraph_gen.visualizer import generate_visualization
|
|
296
452
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
453
|
+
html_path = generate_visualization(
|
|
454
|
+
workspace, resolved_output, open_browser=open_browser
|
|
455
|
+
)
|
|
456
|
+
console.print(
|
|
457
|
+
f"[bold green]Success![/bold green] Interactive graph exported to: [bold underline]{html_path}[/bold underline]"
|
|
458
|
+
)
|
|
459
|
+
except ImportError as e:
|
|
460
|
+
console.print(f"[bold red]Error: {e}[/bold red]")
|
|
461
|
+
sys.exit(1)
|
|
462
|
+
except Exception as e:
|
|
463
|
+
console.print(f"[bold red]Error generating visualization: {e}[/bold red]")
|
|
464
|
+
sys.exit(1)
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
@cli.command()
|
|
468
|
+
def info():
|
|
469
|
+
"""Prints tool info and supported languages."""
|
|
470
|
+
console.print(f"[bold]codegraph v{__version__}[/bold]")
|
|
301
471
|
console.print(
|
|
302
|
-
"Supported languages: Python, JavaScript, TypeScript, Kotlin, Go, Rust, Swift"
|
|
472
|
+
"Supported languages: Python, JavaScript, TypeScript, Kotlin, Go, Rust, Swift, C, C++"
|
|
303
473
|
)
|
|
304
474
|
|
|
305
475
|
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
7
|
+
from codegraph_gen.schema import ExtractionResult
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
PROJECT_CONFIG_FILE = ".codegraphrc"
|
|
12
|
+
|
|
13
|
+
# Default exclusions for files and directories we want to ignore
|
|
14
|
+
DEFAULT_EXCLUSIONS = {
|
|
15
|
+
".git",
|
|
16
|
+
".venv",
|
|
17
|
+
"venv",
|
|
18
|
+
"node_modules",
|
|
19
|
+
"third_party",
|
|
20
|
+
"dist",
|
|
21
|
+
"build",
|
|
22
|
+
".build",
|
|
23
|
+
"__pycache__",
|
|
24
|
+
".pytest_cache",
|
|
25
|
+
".codegraph",
|
|
26
|
+
".idea",
|
|
27
|
+
".vscode",
|
|
28
|
+
"target",
|
|
29
|
+
"out",
|
|
30
|
+
"bin",
|
|
31
|
+
"obj",
|
|
32
|
+
"vendor",
|
|
33
|
+
"Pods",
|
|
34
|
+
"Carthage",
|
|
35
|
+
"DerivedData",
|
|
36
|
+
"build_output",
|
|
37
|
+
".next",
|
|
38
|
+
".nuxt",
|
|
39
|
+
".cache",
|
|
40
|
+
"build_mac",
|
|
41
|
+
"build_ios",
|
|
42
|
+
"build_ios_sim",
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# Mapping of supported languages to file extensions
|
|
47
|
+
LANGUAGE_EXTENSIONS = {
|
|
48
|
+
"python": {".py"},
|
|
49
|
+
"javascript": {".js", ".mjs", ".cjs"},
|
|
50
|
+
"typescript": {".ts", ".tsx"},
|
|
51
|
+
"kotlin": {".kt", ".kts"},
|
|
52
|
+
"go": {".go"},
|
|
53
|
+
"rust": {".rs"},
|
|
54
|
+
"swift": {".swift"},
|
|
55
|
+
"c": {".c", ".h"},
|
|
56
|
+
"cpp": {".cpp", ".cc", ".cxx", ".hpp", ".hxx"},
|
|
57
|
+
"ocaml": {".ml", ".mli"},
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
ALL_EXTENSIONS = {ext for exts in LANGUAGE_EXTENSIONS.values() for ext in exts}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class CacheEntry(BaseModel):
|
|
64
|
+
mtime: float
|
|
65
|
+
size: int
|
|
66
|
+
hash: str
|
|
67
|
+
result: ExtractionResult
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class ProjectConfig(BaseModel):
|
|
71
|
+
"""Schema for the .codegraphrc project-level configuration file."""
|
|
72
|
+
|
|
73
|
+
include: Optional[list[str]] = None
|
|
74
|
+
"""Subdirectory whitelist (relative to workspace root). None = scan entire workspace."""
|
|
75
|
+
|
|
76
|
+
exclude: list[str] = []
|
|
77
|
+
"""Extra directory names/patterns to exclude (appended to DEFAULT_EXCLUSIONS)."""
|
|
78
|
+
|
|
79
|
+
output: str = ".codegraph"
|
|
80
|
+
"""Output directory path (relative to workspace root)."""
|
|
81
|
+
|
|
82
|
+
languages: Optional[list[str]] = None
|
|
83
|
+
"""Language whitelist. None = all supported languages."""
|
|
84
|
+
|
|
85
|
+
workers: Optional[int] = None
|
|
86
|
+
"""Number of parallel worker processes. None = use CPU count."""
|
|
87
|
+
|
|
88
|
+
cache: bool = True
|
|
89
|
+
"""Enable incremental parse cache."""
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def load_project_config(workspace_dir: Path) -> Optional[ProjectConfig]:
|
|
93
|
+
"""
|
|
94
|
+
Loads .codegraphrc from the workspace root directory.
|
|
95
|
+
Returns None (silently) if the file does not exist.
|
|
96
|
+
Returns None (with a warning) if the file is malformed.
|
|
97
|
+
No upward traversal — the file must be in workspace_dir itself.
|
|
98
|
+
"""
|
|
99
|
+
config_path = workspace_dir / PROJECT_CONFIG_FILE
|
|
100
|
+
if not config_path.is_file():
|
|
101
|
+
return None
|
|
102
|
+
try:
|
|
103
|
+
data = json.loads(config_path.read_text(encoding="utf-8"))
|
|
104
|
+
cfg = ProjectConfig.model_validate(data)
|
|
105
|
+
logger.info(f"Loaded project config from {config_path}")
|
|
106
|
+
return cfg
|
|
107
|
+
except json.JSONDecodeError as e:
|
|
108
|
+
logger.warning(
|
|
109
|
+
f"{PROJECT_CONFIG_FILE}: JSON parse error — {e}. Using defaults."
|
|
110
|
+
)
|
|
111
|
+
except Exception as e:
|
|
112
|
+
logger.warning(f"{PROJECT_CONFIG_FILE}: Failed to load — {e}. Using defaults.")
|
|
113
|
+
return None
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class CodegraphConfig(BaseModel):
|
|
117
|
+
"""Configuration class for codegraph parsing and exporting."""
|
|
118
|
+
|
|
119
|
+
workspace_dir: Path
|
|
120
|
+
output_dir: Path = Field(default_factory=lambda: Path(".codegraph"))
|
|
121
|
+
exclusions: set[str] = Field(default_factory=lambda: DEFAULT_EXCLUSIONS)
|
|
122
|
+
languages: set[str] = Field(default_factory=lambda: set(LANGUAGE_EXTENSIONS.keys()))
|
|
123
|
+
max_workers: int = Field(default_factory=lambda: os.cpu_count() or 4)
|
|
124
|
+
use_cache: bool = Field(default=True)
|
|
125
|
+
include_dirs: Optional[list[Path]] = Field(default=None)
|
|
126
|
+
"""Absolute paths of subdirectories to scan. None = scan entire workspace_dir."""
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def absolute_output_dir(self) -> Path:
|
|
130
|
+
if self.output_dir.is_absolute():
|
|
131
|
+
return self.output_dir
|
|
132
|
+
return self.workspace_dir / self.output_dir
|
|
@@ -9,11 +9,20 @@ def discover_files(
|
|
|
9
9
|
workspace_dir: Path,
|
|
10
10
|
languages: set[str],
|
|
11
11
|
exclusions: set[str],
|
|
12
|
+
include_dirs: list[Path] | None = None,
|
|
12
13
|
) -> list[tuple[Path, str]]:
|
|
13
14
|
"""
|
|
14
15
|
Recursively discovers source files in the workspace directory.
|
|
15
16
|
Filters by allowed languages and ignores files/directories in exclusions.
|
|
16
17
|
|
|
18
|
+
Args:
|
|
19
|
+
workspace_dir: Root of the workspace (used to compute relative paths / node IDs).
|
|
20
|
+
languages: Set of language names to include.
|
|
21
|
+
exclusions: Directory names/patterns to exclude.
|
|
22
|
+
include_dirs: Optional whitelist of absolute directories to scan.
|
|
23
|
+
When provided, only these directories are scanned.
|
|
24
|
+
When None, the entire workspace_dir is scanned.
|
|
25
|
+
|
|
17
26
|
Returns:
|
|
18
27
|
List of tuples: (absolute_file_path, language_name)
|
|
19
28
|
"""
|
|
@@ -59,5 +68,20 @@ def discover_files(
|
|
|
59
68
|
except Exception as e:
|
|
60
69
|
logger.error(f"Error scanning {directory}: {e}")
|
|
61
70
|
|
|
62
|
-
|
|
71
|
+
# Determine which root directories to scan
|
|
72
|
+
if include_dirs:
|
|
73
|
+
for root in include_dirs:
|
|
74
|
+
root = root.resolve()
|
|
75
|
+
if not root.exists():
|
|
76
|
+
logger.warning(f"include_dirs entry does not exist, skipping: {root}")
|
|
77
|
+
continue
|
|
78
|
+
if not root.is_dir():
|
|
79
|
+
logger.warning(
|
|
80
|
+
f"include_dirs entry is not a directory, skipping: {root}"
|
|
81
|
+
)
|
|
82
|
+
continue
|
|
83
|
+
scan_dir(root)
|
|
84
|
+
else:
|
|
85
|
+
scan_dir(workspace)
|
|
86
|
+
|
|
63
87
|
return found_files
|
|
@@ -100,7 +100,10 @@ class CodegraphEngine:
|
|
|
100
100
|
if progress_callback:
|
|
101
101
|
progress_callback(PipelineStage.DISCOVERING, None, 0, 0)
|
|
102
102
|
files = discover_files(
|
|
103
|
-
config.workspace_dir,
|
|
103
|
+
config.workspace_dir,
|
|
104
|
+
config.languages,
|
|
105
|
+
config.exclusions,
|
|
106
|
+
config.include_dirs,
|
|
104
107
|
)
|
|
105
108
|
if not files:
|
|
106
109
|
logger.warning("No supported files found.")
|