code2schema 0.1.1__tar.gz → 0.1.4__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.
- {code2schema-0.1.1 → code2schema-0.1.4}/PKG-INFO +89 -28
- {code2schema-0.1.1 → code2schema-0.1.4}/README.md +88 -27
- {code2schema-0.1.1 → code2schema-0.1.4}/code2schema/__init__.py +1 -1
- {code2schema-0.1.1 → code2schema-0.1.4}/code2schema/analyzer/events.py +29 -23
- code2schema-0.1.4/code2schema/cli.py +207 -0
- {code2schema-0.1.1 → code2schema-0.1.4}/code2schema.egg-info/PKG-INFO +89 -28
- {code2schema-0.1.1 → code2schema-0.1.4}/pyproject.toml +1 -1
- {code2schema-0.1.1 → code2schema-0.1.4}/tests/test_code2schema.py +47 -0
- code2schema-0.1.1/code2schema/cli.py +0 -105
- {code2schema-0.1.1 → code2schema-0.1.4}/LICENSE +0 -0
- {code2schema-0.1.1 → code2schema-0.1.4}/code2schema/analyzer/__init__.py +0 -0
- {code2schema-0.1.1 → code2schema-0.1.4}/code2schema/analyzer/cqrs.py +0 -0
- {code2schema-0.1.1 → code2schema-0.1.4}/code2schema/analyzer/graph.py +0 -0
- {code2schema-0.1.1 → code2schema-0.1.4}/code2schema/codegen/__init__.py +0 -0
- {code2schema-0.1.1 → code2schema-0.1.4}/code2schema/codegen/visualizer.py +0 -0
- {code2schema-0.1.1 → code2schema-0.1.4}/code2schema/core/__init__.py +0 -0
- {code2schema-0.1.1 → code2schema-0.1.4}/code2schema/core/extractor.py +0 -0
- {code2schema-0.1.1 → code2schema-0.1.4}/code2schema/core/models.py +0 -0
- {code2schema-0.1.1 → code2schema-0.1.4}/code2schema.egg-info/SOURCES.txt +0 -0
- {code2schema-0.1.1 → code2schema-0.1.4}/code2schema.egg-info/dependency_links.txt +0 -0
- {code2schema-0.1.1 → code2schema-0.1.4}/code2schema.egg-info/entry_points.txt +0 -0
- {code2schema-0.1.1 → code2schema-0.1.4}/code2schema.egg-info/requires.txt +0 -0
- {code2schema-0.1.1 → code2schema-0.1.4}/code2schema.egg-info/top_level.txt +0 -0
- {code2schema-0.1.1 → code2schema-0.1.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code2schema
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Semantic compiler: Code → AST → CQRS Model → Workflow DAG → Proto/Schema
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Keywords: ast,cqrs,static-analysis,code-quality,schema
|
|
@@ -34,18 +34,16 @@ Dynamic: license-file
|
|
|
34
34
|
|
|
35
35
|
## AI Cost Tracking
|
|
36
36
|
|
|
37
|
-
    
|
|
38
|
+
  
|
|
39
39
|
|
|
40
|
-
- 🤖 **LLM usage:** $0.
|
|
41
|
-
- 👤 **Human dev:** ~$
|
|
40
|
+
- 🤖 **LLM usage:** $0.4500 (3 commits)
|
|
41
|
+
- 👤 **Human dev:** ~$350 (3.5h @ $100/h, 30min dedup)
|
|
42
42
|
|
|
43
|
-
Generated on 2026-05-
|
|
43
|
+
Generated on 2026-05-07 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
44
44
|
|
|
45
45
|
---
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
47
|
**Semantic Compiler for Software Systems**
|
|
50
48
|
|
|
51
49
|
Przekształca kod Python w model semantyczny CQRS → kontrakty API → graf architektury.
|
|
@@ -80,33 +78,81 @@ pip install "code2schema[dev]" # pytest + ruff + black
|
|
|
80
78
|
## Szybki start
|
|
81
79
|
|
|
82
80
|
```bash
|
|
83
|
-
#
|
|
84
|
-
code2schema
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
81
|
+
# Analiza z auto-generowanymi nazwami plików (w katalogu bieżącym)
|
|
82
|
+
code2schema /home/tom/github/maskservice/c2004/backend
|
|
83
|
+
# → c2004_schema.json
|
|
84
|
+
|
|
85
|
+
# Pełna analiza ze wszystkimi formatami wyjściowymi
|
|
86
|
+
code2schema ./backend --proto --md --html
|
|
87
|
+
# → backend_schema.json
|
|
88
|
+
# → backend_api.proto
|
|
89
|
+
# → backend_report.md
|
|
90
|
+
# → backend_viz.html
|
|
91
|
+
|
|
92
|
+
# Pełna analiza ze szczegółami
|
|
93
|
+
code2schema ./backend --proto --md --html --graph-summary --events --cycles
|
|
93
94
|
```
|
|
94
95
|
|
|
95
96
|
Przykładowy output:
|
|
96
97
|
|
|
97
98
|
```
|
|
98
|
-
✅ Gotowe (1.
|
|
99
|
-
Modules :
|
|
100
|
-
Functions:
|
|
101
|
-
Queries :
|
|
102
|
-
Commands :
|
|
103
|
-
Orchest. :
|
|
104
|
-
Workflows:
|
|
105
|
-
Rules :
|
|
106
|
-
Graph :
|
|
107
|
-
→
|
|
99
|
+
✅ Gotowe (1.8s)
|
|
100
|
+
Modules : 607
|
|
101
|
+
Functions: 3142
|
|
102
|
+
Queries : 1518
|
|
103
|
+
Commands : 505
|
|
104
|
+
Orchest. : 1119
|
|
105
|
+
Workflows: 1118
|
|
106
|
+
Rules : 320
|
|
107
|
+
Graph : 2922N / 5612E
|
|
108
|
+
→ c2004_schema.json
|
|
109
|
+
→ c2004_api.proto
|
|
110
|
+
→ c2004_report.md
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Test na dużym projekcie (c2004 backend)
|
|
114
|
+
|
|
115
|
+
Analiza produkcyjnego projektu IoT (607 modułów, 3142 funkcje):
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
code2schema /home/tom/github/maskservice/c2004/backend --proto --md --html
|
|
119
|
+
# Pliki generowane w /home/tom/github/maskservice/c2004/ (katalog projektu)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Wyniki:**
|
|
123
|
+
```
|
|
124
|
+
✅ Gotowe (1.8s)
|
|
125
|
+
Modules : 607
|
|
126
|
+
Functions: 3142
|
|
127
|
+
Queries : 1518
|
|
128
|
+
Commands : 505
|
|
129
|
+
Orchest. : 1119
|
|
130
|
+
Workflows: 1118
|
|
131
|
+
Rules : 320
|
|
132
|
+
Graph : 2922N / 5612E
|
|
133
|
+
→ c2004_schema.json
|
|
134
|
+
→ c2004_api.proto
|
|
135
|
+
→ c2004_report.md
|
|
136
|
+
→ c2004_viz.html
|
|
108
137
|
```
|
|
109
138
|
|
|
139
|
+
## Dokumentacja i przykłady
|
|
140
|
+
|
|
141
|
+
- **[Dokumentacja](docs/)** — przykłady użycia CLI i Python API
|
|
142
|
+
- [CLI examples](docs/examples/cli/basic.sh)
|
|
143
|
+
- [Python API — basic](docs/examples/api/basic.py)
|
|
144
|
+
- [Python API — advanced](docs/examples/api/advanced.py)
|
|
145
|
+
- [Sample outputs](docs/examples/outputs/)
|
|
146
|
+
|
|
147
|
+
Wykryte cykle w architekturze CQRS (do refaktoryzacji):
|
|
148
|
+
- `handle → publish → _execute → publish → handle`
|
|
149
|
+
|
|
150
|
+
**Wygenerowane artefakty:**
|
|
151
|
+
- `c2004_schema.json` (2.9MB) — pełny model
|
|
152
|
+
- `c2004_api.proto` (188KB) — gRPC contracts
|
|
153
|
+
- `c2004_report.md` (49KB) — podsumowanie jakości
|
|
154
|
+
- `c2004_viz.html` (1.1MB) — **interaktywny graf D3.js** z wyszukiwaniem i filtrowaniem
|
|
155
|
+
|
|
110
156
|
## Użycie w kodzie
|
|
111
157
|
|
|
112
158
|
```python
|
|
@@ -138,6 +184,21 @@ print(to_proto(schema))
|
|
|
138
184
|
print(to_markdown(schema))
|
|
139
185
|
```
|
|
140
186
|
|
|
187
|
+
## Wizualizacja HTML (`--html`)
|
|
188
|
+
|
|
189
|
+
Interaktywny graf D3.js z funkcjami:
|
|
190
|
+
- **Kolory ról**: 🟢 Query, 🟠 Command, 🟣 Orchestrator
|
|
191
|
+
- **Wyszukiwanie** — filtruje węzły w czasie rzeczywistym
|
|
192
|
+
- **Hover** — podświetla połączenia (call graph)
|
|
193
|
+
- **Klik** — szczegóły funkcji (fan-out, side effects, reguły jakości)
|
|
194
|
+
- **Filtry** — pokazuj/ukrywaj kategorie ról
|
|
195
|
+
- **Force layout** — przeciąganie węzłów
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
code2schema ./backend --html viz.html
|
|
199
|
+
# Otwórz w przeglądarce: open viz.html
|
|
200
|
+
```
|
|
201
|
+
|
|
141
202
|
## Architektura paczki
|
|
142
203
|
|
|
143
204
|
```
|
|
@@ -3,18 +3,16 @@
|
|
|
3
3
|
|
|
4
4
|
## AI Cost Tracking
|
|
5
5
|
|
|
6
|
-
    
|
|
7
|
+
  
|
|
8
8
|
|
|
9
|
-
- 🤖 **LLM usage:** $0.
|
|
10
|
-
- 👤 **Human dev:** ~$
|
|
9
|
+
- 🤖 **LLM usage:** $0.4500 (3 commits)
|
|
10
|
+
- 👤 **Human dev:** ~$350 (3.5h @ $100/h, 30min dedup)
|
|
11
11
|
|
|
12
|
-
Generated on 2026-05-
|
|
12
|
+
Generated on 2026-05-07 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
16
|
**Semantic Compiler for Software Systems**
|
|
19
17
|
|
|
20
18
|
Przekształca kod Python w model semantyczny CQRS → kontrakty API → graf architektury.
|
|
@@ -49,33 +47,81 @@ pip install "code2schema[dev]" # pytest + ruff + black
|
|
|
49
47
|
## Szybki start
|
|
50
48
|
|
|
51
49
|
```bash
|
|
52
|
-
#
|
|
53
|
-
code2schema
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
50
|
+
# Analiza z auto-generowanymi nazwami plików (w katalogu bieżącym)
|
|
51
|
+
code2schema /home/tom/github/maskservice/c2004/backend
|
|
52
|
+
# → c2004_schema.json
|
|
53
|
+
|
|
54
|
+
# Pełna analiza ze wszystkimi formatami wyjściowymi
|
|
55
|
+
code2schema ./backend --proto --md --html
|
|
56
|
+
# → backend_schema.json
|
|
57
|
+
# → backend_api.proto
|
|
58
|
+
# → backend_report.md
|
|
59
|
+
# → backend_viz.html
|
|
60
|
+
|
|
61
|
+
# Pełna analiza ze szczegółami
|
|
62
|
+
code2schema ./backend --proto --md --html --graph-summary --events --cycles
|
|
62
63
|
```
|
|
63
64
|
|
|
64
65
|
Przykładowy output:
|
|
65
66
|
|
|
66
67
|
```
|
|
67
|
-
✅ Gotowe (1.
|
|
68
|
-
Modules :
|
|
69
|
-
Functions:
|
|
70
|
-
Queries :
|
|
71
|
-
Commands :
|
|
72
|
-
Orchest. :
|
|
73
|
-
Workflows:
|
|
74
|
-
Rules :
|
|
75
|
-
Graph :
|
|
76
|
-
→
|
|
68
|
+
✅ Gotowe (1.8s)
|
|
69
|
+
Modules : 607
|
|
70
|
+
Functions: 3142
|
|
71
|
+
Queries : 1518
|
|
72
|
+
Commands : 505
|
|
73
|
+
Orchest. : 1119
|
|
74
|
+
Workflows: 1118
|
|
75
|
+
Rules : 320
|
|
76
|
+
Graph : 2922N / 5612E
|
|
77
|
+
→ c2004_schema.json
|
|
78
|
+
→ c2004_api.proto
|
|
79
|
+
→ c2004_report.md
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Test na dużym projekcie (c2004 backend)
|
|
83
|
+
|
|
84
|
+
Analiza produkcyjnego projektu IoT (607 modułów, 3142 funkcje):
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
code2schema /home/tom/github/maskservice/c2004/backend --proto --md --html
|
|
88
|
+
# Pliki generowane w /home/tom/github/maskservice/c2004/ (katalog projektu)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Wyniki:**
|
|
92
|
+
```
|
|
93
|
+
✅ Gotowe (1.8s)
|
|
94
|
+
Modules : 607
|
|
95
|
+
Functions: 3142
|
|
96
|
+
Queries : 1518
|
|
97
|
+
Commands : 505
|
|
98
|
+
Orchest. : 1119
|
|
99
|
+
Workflows: 1118
|
|
100
|
+
Rules : 320
|
|
101
|
+
Graph : 2922N / 5612E
|
|
102
|
+
→ c2004_schema.json
|
|
103
|
+
→ c2004_api.proto
|
|
104
|
+
→ c2004_report.md
|
|
105
|
+
→ c2004_viz.html
|
|
77
106
|
```
|
|
78
107
|
|
|
108
|
+
## Dokumentacja i przykłady
|
|
109
|
+
|
|
110
|
+
- **[Dokumentacja](docs/)** — przykłady użycia CLI i Python API
|
|
111
|
+
- [CLI examples](docs/examples/cli/basic.sh)
|
|
112
|
+
- [Python API — basic](docs/examples/api/basic.py)
|
|
113
|
+
- [Python API — advanced](docs/examples/api/advanced.py)
|
|
114
|
+
- [Sample outputs](docs/examples/outputs/)
|
|
115
|
+
|
|
116
|
+
Wykryte cykle w architekturze CQRS (do refaktoryzacji):
|
|
117
|
+
- `handle → publish → _execute → publish → handle`
|
|
118
|
+
|
|
119
|
+
**Wygenerowane artefakty:**
|
|
120
|
+
- `c2004_schema.json` (2.9MB) — pełny model
|
|
121
|
+
- `c2004_api.proto` (188KB) — gRPC contracts
|
|
122
|
+
- `c2004_report.md` (49KB) — podsumowanie jakości
|
|
123
|
+
- `c2004_viz.html` (1.1MB) — **interaktywny graf D3.js** z wyszukiwaniem i filtrowaniem
|
|
124
|
+
|
|
79
125
|
## Użycie w kodzie
|
|
80
126
|
|
|
81
127
|
```python
|
|
@@ -107,6 +153,21 @@ print(to_proto(schema))
|
|
|
107
153
|
print(to_markdown(schema))
|
|
108
154
|
```
|
|
109
155
|
|
|
156
|
+
## Wizualizacja HTML (`--html`)
|
|
157
|
+
|
|
158
|
+
Interaktywny graf D3.js z funkcjami:
|
|
159
|
+
- **Kolory ról**: 🟢 Query, 🟠 Command, 🟣 Orchestrator
|
|
160
|
+
- **Wyszukiwanie** — filtruje węzły w czasie rzeczywistym
|
|
161
|
+
- **Hover** — podświetla połączenia (call graph)
|
|
162
|
+
- **Klik** — szczegóły funkcji (fan-out, side effects, reguły jakości)
|
|
163
|
+
- **Filtry** — pokazuj/ukrywaj kategorie ról
|
|
164
|
+
- **Force layout** — przeciąganie węzłów
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
code2schema ./backend --html viz.html
|
|
168
|
+
# Otwórz w przeglądarce: open viz.html
|
|
169
|
+
```
|
|
170
|
+
|
|
110
171
|
## Architektura paczki
|
|
111
172
|
|
|
112
173
|
```
|
|
@@ -72,39 +72,33 @@ class EventModel:
|
|
|
72
72
|
|
|
73
73
|
# ── Inference ─────────────────────────────────────────────────────────────────
|
|
74
74
|
|
|
75
|
-
def
|
|
76
|
-
"""
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
all_funcs: dict[str, FunctionIR] = {}
|
|
80
|
-
for mod in modules:
|
|
81
|
-
for f in mod.functions:
|
|
82
|
-
all_funcs[f.qualified_name] = f
|
|
83
|
-
all_funcs[f.name] = f # short alias
|
|
84
|
-
|
|
85
|
-
# Krok 1: znajdź emiterów
|
|
86
|
-
emitters: dict[str, DomainEvent] = {}
|
|
75
|
+
def _find_emitters(modules: List[ModuleIR]) -> List[DomainEvent]:
|
|
76
|
+
"""Krok 1: znajdź funkcje emitujące zdarzenia."""
|
|
77
|
+
events: List[DomainEvent] = []
|
|
87
78
|
for mod in modules:
|
|
88
79
|
for f in mod.functions:
|
|
89
80
|
if _EVENT_EMIT_PATTERNS.search(f.name) or any(
|
|
90
81
|
_EVENT_EMIT_PATTERNS.search(c) for c in f.calls
|
|
91
82
|
):
|
|
92
83
|
event_name = _derive_event_name(f.name)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
model.events.append(ev)
|
|
84
|
+
events.append(DomainEvent(name=event_name, emitted_by=f.qualified_name))
|
|
85
|
+
return events
|
|
96
86
|
|
|
97
|
-
|
|
87
|
+
|
|
88
|
+
def _find_handlers(events: List[DomainEvent], modules: List[ModuleIR]) -> None:
|
|
89
|
+
"""Krok 2: dopasuj handlery do zdarzeń (mutuje events in-place)."""
|
|
98
90
|
for mod in modules:
|
|
99
91
|
for f in mod.functions:
|
|
100
92
|
if _EVENT_HANDLER_PATTERNS.search(f.name):
|
|
101
|
-
|
|
102
|
-
for ev in model.events:
|
|
93
|
+
for ev in events:
|
|
103
94
|
keyword = ev.name.lower().replace("event", "").strip()
|
|
104
95
|
if keyword and keyword in f.name.lower():
|
|
105
96
|
ev.handled_by.append(f.qualified_name)
|
|
106
97
|
|
|
107
|
-
|
|
98
|
+
|
|
99
|
+
def _find_command_handlers(modules: List[ModuleIR]) -> List[CommandHandler]:
|
|
100
|
+
"""Krok 3: znajdź command handlery i ich emitowane zdarzenia."""
|
|
101
|
+
commands: List[CommandHandler] = []
|
|
108
102
|
for mod in modules:
|
|
109
103
|
for f in mod.functions:
|
|
110
104
|
if f.role == CQRSRole.COMMAND or _AGGREGATE_PATTERNS.search(f.name):
|
|
@@ -114,24 +108,36 @@ def infer_event_model(modules: List[ModuleIR]) -> EventModel:
|
|
|
114
108
|
if _EVENT_EMIT_PATTERNS.search(c)
|
|
115
109
|
]
|
|
116
110
|
if emits or f.role == CQRSRole.COMMAND:
|
|
117
|
-
|
|
111
|
+
commands.append(
|
|
118
112
|
CommandHandler(
|
|
119
113
|
name=f.qualified_name,
|
|
120
114
|
command=f.name,
|
|
121
115
|
emits=emits,
|
|
122
116
|
)
|
|
123
117
|
)
|
|
118
|
+
return commands
|
|
119
|
+
|
|
124
120
|
|
|
125
|
-
|
|
121
|
+
def _find_aggregates(modules: List[ModuleIR]) -> List[str]:
|
|
122
|
+
"""Krok 4: znajdź moduły będące agregatami (CRUD score >= 2)."""
|
|
123
|
+
aggregates: List[str] = []
|
|
126
124
|
for mod in modules:
|
|
127
125
|
crud_score = sum(
|
|
128
126
|
1 for f in mod.functions
|
|
129
127
|
if _AGGREGATE_PATTERNS.search(f.name)
|
|
130
128
|
)
|
|
131
129
|
if crud_score >= 2:
|
|
132
|
-
|
|
130
|
+
aggregates.append(mod.name)
|
|
131
|
+
return aggregates
|
|
133
132
|
|
|
134
|
-
|
|
133
|
+
|
|
134
|
+
def infer_event_model(modules: List[ModuleIR]) -> EventModel:
|
|
135
|
+
"""Przechodzi po IR i buduje model zdarzeń."""
|
|
136
|
+
events = _find_emitters(modules)
|
|
137
|
+
_find_handlers(events, modules)
|
|
138
|
+
commands = _find_command_handlers(modules)
|
|
139
|
+
aggregates = _find_aggregates(modules)
|
|
140
|
+
return EventModel(commands=commands, events=events, aggregates=aggregates)
|
|
135
141
|
|
|
136
142
|
|
|
137
143
|
def _derive_event_name(func_name: str) -> str:
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"""
|
|
2
|
+
code2schema.cli
|
|
3
|
+
~~~~~~~~~~~~~~~
|
|
4
|
+
CLI v3 — pełny pipeline.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import argparse
|
|
9
|
+
import sys
|
|
10
|
+
import time
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from code2schema.analyzer.cqrs import analyze
|
|
14
|
+
from code2schema.analyzer.events import infer_event_model
|
|
15
|
+
from code2schema.analyzer.graph import (
|
|
16
|
+
build_rich_graph, detect_cycles, graph_summary, write_dot, write_graphml,
|
|
17
|
+
)
|
|
18
|
+
from code2schema.codegen import write_json, write_markdown, write_proto
|
|
19
|
+
from code2schema.codegen.visualizer import write_html
|
|
20
|
+
from code2schema.core.extractor import extract_project
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
_SPECIAL_PROJECT_DIRS = {"backend", "frontend", "src", "app", "api"}
|
|
24
|
+
_GENERIC_PATH_PARTS = {"home", "github", "workspace", "projects", "src"}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _project_name_from_path(path: Path) -> str:
|
|
28
|
+
"""Extract project name from path (e.g., /path/to/c2004/backend -> c2004)."""
|
|
29
|
+
# Try to find a meaningful name from the path
|
|
30
|
+
parts = path.parts
|
|
31
|
+
for i, part in enumerate(reversed(parts)):
|
|
32
|
+
if part in _SPECIAL_PROJECT_DIRS:
|
|
33
|
+
# Use parent directory name
|
|
34
|
+
if i < len(parts) - 1:
|
|
35
|
+
return parts[-(i + 2)]
|
|
36
|
+
# Skip common generic names
|
|
37
|
+
if part not in _GENERIC_PATH_PARTS:
|
|
38
|
+
return part
|
|
39
|
+
return path.name or "project"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _build_parser() -> argparse.ArgumentParser:
|
|
43
|
+
"""Build CLI argument parser."""
|
|
44
|
+
from code2schema import __version__
|
|
45
|
+
parser = argparse.ArgumentParser(prog="code2schema",
|
|
46
|
+
description="Semantic Compiler: Code → CQRS → Schema / Proto / Graph")
|
|
47
|
+
parser.add_argument("path")
|
|
48
|
+
parser.add_argument("-o", "--out", metavar="FILE", help="Output JSON schema (default: <project>_schema.json)")
|
|
49
|
+
parser.add_argument("--proto", metavar="FILE", nargs="?", const=True, default=None,
|
|
50
|
+
help="Output .proto file (default: <project>_api.proto if flag present)")
|
|
51
|
+
parser.add_argument("--md", metavar="FILE", nargs="?", const=True, default=None,
|
|
52
|
+
help="Output Markdown report (default: <project>_report.md if flag present)")
|
|
53
|
+
parser.add_argument("--graphml", metavar="FILE")
|
|
54
|
+
parser.add_argument("--dot", metavar="FILE")
|
|
55
|
+
parser.add_argument("--html", metavar="FILE", nargs="?", const=True, default=None,
|
|
56
|
+
help="Interactive HTML visualization (default: <project>_viz.html if flag present)")
|
|
57
|
+
parser.add_argument("--events", action="store_true")
|
|
58
|
+
parser.add_argument("--cycles", action="store_true")
|
|
59
|
+
parser.add_argument("--graph-summary", action="store_true")
|
|
60
|
+
parser.add_argument("--no-rules", action="store_true")
|
|
61
|
+
parser.add_argument("--exclude", nargs="*", default=[])
|
|
62
|
+
parser.add_argument("-q", "--quiet", action="store_true")
|
|
63
|
+
parser.add_argument("-V", "--version", action="version",
|
|
64
|
+
version=f"%(prog)s {__version__}")
|
|
65
|
+
return parser
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _resolve_output_dir(root: Path) -> Path:
|
|
69
|
+
if not root.is_dir():
|
|
70
|
+
return root.parent
|
|
71
|
+
if root.name in _SPECIAL_PROJECT_DIRS:
|
|
72
|
+
return root.parent
|
|
73
|
+
return root
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _resolve_required_path(out_dir: Path, value: str | None, default_name: str) -> Path:
|
|
77
|
+
return out_dir / (value or default_name)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _resolve_optional_path(
|
|
81
|
+
out_dir: Path,
|
|
82
|
+
value: str | bool | None,
|
|
83
|
+
default_name: str,
|
|
84
|
+
) -> Path | None:
|
|
85
|
+
if isinstance(value, str):
|
|
86
|
+
return out_dir / value
|
|
87
|
+
if value is True:
|
|
88
|
+
return out_dir / default_name
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _resolve_paths(
|
|
93
|
+
args: argparse.Namespace,
|
|
94
|
+
) -> tuple[Path, Path, Path | None, Path | None, Path | None]:
|
|
95
|
+
"""Resolve root, project name and output file paths from CLI args."""
|
|
96
|
+
root = Path(args.path)
|
|
97
|
+
proj_name = _project_name_from_path(root)
|
|
98
|
+
out_dir = _resolve_output_dir(root)
|
|
99
|
+
|
|
100
|
+
out_path = _resolve_required_path(out_dir, args.out, f"{proj_name}_schema.json")
|
|
101
|
+
proto_path = _resolve_optional_path(out_dir, args.proto, f"{proj_name}_api.proto")
|
|
102
|
+
md_path = _resolve_optional_path(out_dir, args.md, f"{proj_name}_report.md")
|
|
103
|
+
html_path = _resolve_optional_path(out_dir, args.html, f"{proj_name}_viz.html")
|
|
104
|
+
return root, out_path, proto_path, md_path, html_path
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _run_extraction(args, root):
|
|
108
|
+
"""Parse project and run CQRS analysis. Returns (modules, schema, G) or None on failure."""
|
|
109
|
+
modules = extract_project(root, exclude=args.exclude or None)
|
|
110
|
+
if not modules:
|
|
111
|
+
print("ERROR: Brak plików .py.", file=sys.stderr)
|
|
112
|
+
return None
|
|
113
|
+
schema = analyze(modules)
|
|
114
|
+
if args.no_rules:
|
|
115
|
+
schema.rules = []
|
|
116
|
+
G = build_rich_graph(schema)
|
|
117
|
+
return modules, schema, G
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _run_reports(args, modules, schema, G):
|
|
121
|
+
"""Print optional reports: cycles, graph summary, events."""
|
|
122
|
+
if args.cycles:
|
|
123
|
+
cycles = detect_cycles(G)
|
|
124
|
+
if cycles:
|
|
125
|
+
print(f"\n⚠️ Cykle ({len(cycles)}):")
|
|
126
|
+
for c in cycles[:5]:
|
|
127
|
+
print(f" {' → '.join(c[:5])}")
|
|
128
|
+
else:
|
|
129
|
+
print("✅ Brak cykli.")
|
|
130
|
+
|
|
131
|
+
if args.graph_summary:
|
|
132
|
+
print("\n── Graph Summary ──────────────────────")
|
|
133
|
+
print(graph_summary(G, schema))
|
|
134
|
+
|
|
135
|
+
if args.events:
|
|
136
|
+
em = infer_event_model(modules)
|
|
137
|
+
print("\n── Event Model ────────────────────────")
|
|
138
|
+
print(em.summary())
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _write_outputs(args, schema, G, out_path, proto_path, md_path, html_path):
|
|
142
|
+
"""Write all output files (JSON, proto, md, html, graphml, dot)."""
|
|
143
|
+
write_json(schema, out_path)
|
|
144
|
+
if proto_path:
|
|
145
|
+
write_proto(schema, proto_path)
|
|
146
|
+
if md_path:
|
|
147
|
+
write_markdown(schema, md_path)
|
|
148
|
+
if html_path:
|
|
149
|
+
write_html(schema, html_path)
|
|
150
|
+
if args.graphml:
|
|
151
|
+
write_graphml(G, Path(args.graphml))
|
|
152
|
+
if args.dot:
|
|
153
|
+
write_dot(G, Path(args.dot))
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _print_summary(modules, schema, G, t0, out_path, proto_path, md_path, html_path):
|
|
157
|
+
"""Print final summary with metrics and output file names."""
|
|
158
|
+
funcs = schema.all_functions()
|
|
159
|
+
outputs = [f" → {out_path.name}"]
|
|
160
|
+
if proto_path:
|
|
161
|
+
outputs.append(f" → {proto_path.name}")
|
|
162
|
+
if md_path:
|
|
163
|
+
outputs.append(f" → {md_path.name}")
|
|
164
|
+
if html_path:
|
|
165
|
+
outputs.append(f" → {html_path.name}")
|
|
166
|
+
print(
|
|
167
|
+
f"\n✅ Gotowe ({time.perf_counter()-t0:.2f}s)\n"
|
|
168
|
+
f" Modules : {len(modules)}\n"
|
|
169
|
+
f" Functions: {len(funcs)}\n"
|
|
170
|
+
f" Queries : {len(schema.queries())}\n"
|
|
171
|
+
f" Commands : {len(schema.commands())}\n"
|
|
172
|
+
f" Orchest. : {len(schema.orchestrators())}\n"
|
|
173
|
+
f" Workflows: {len(schema.workflows)}\n"
|
|
174
|
+
f" Rules : {len(schema.rules)}\n"
|
|
175
|
+
f" Graph : {G.number_of_nodes()}N / {G.number_of_edges()}E\n"
|
|
176
|
+
+ "\n".join(outputs)
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def main(argv=None):
|
|
181
|
+
parser = _build_parser()
|
|
182
|
+
args = parser.parse_args(argv)
|
|
183
|
+
|
|
184
|
+
root, out_path, proto_path, md_path, html_path = _resolve_paths(args)
|
|
185
|
+
if not root.exists():
|
|
186
|
+
print(f"ERROR: {root}", file=sys.stderr)
|
|
187
|
+
return 1
|
|
188
|
+
|
|
189
|
+
t0 = time.perf_counter()
|
|
190
|
+
if not args.quiet:
|
|
191
|
+
print(f"⏳ Parsing: {root}")
|
|
192
|
+
|
|
193
|
+
result = _run_extraction(args, root)
|
|
194
|
+
if result is None:
|
|
195
|
+
return 1
|
|
196
|
+
modules, schema, G = result
|
|
197
|
+
|
|
198
|
+
_run_reports(args, modules, schema, G)
|
|
199
|
+
_write_outputs(args, schema, G, out_path, proto_path, md_path, html_path)
|
|
200
|
+
|
|
201
|
+
if not args.quiet:
|
|
202
|
+
_print_summary(modules, schema, G, t0, out_path, proto_path, md_path, html_path)
|
|
203
|
+
return 0
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
if __name__ == "__main__":
|
|
207
|
+
raise SystemExit(main())
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code2schema
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Semantic compiler: Code → AST → CQRS Model → Workflow DAG → Proto/Schema
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Keywords: ast,cqrs,static-analysis,code-quality,schema
|
|
@@ -34,18 +34,16 @@ Dynamic: license-file
|
|
|
34
34
|
|
|
35
35
|
## AI Cost Tracking
|
|
36
36
|
|
|
37
|
-
    
|
|
38
|
+
  
|
|
39
39
|
|
|
40
|
-
- 🤖 **LLM usage:** $0.
|
|
41
|
-
- 👤 **Human dev:** ~$
|
|
40
|
+
- 🤖 **LLM usage:** $0.4500 (3 commits)
|
|
41
|
+
- 👤 **Human dev:** ~$350 (3.5h @ $100/h, 30min dedup)
|
|
42
42
|
|
|
43
|
-
Generated on 2026-05-
|
|
43
|
+
Generated on 2026-05-07 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
44
44
|
|
|
45
45
|
---
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
47
|
**Semantic Compiler for Software Systems**
|
|
50
48
|
|
|
51
49
|
Przekształca kod Python w model semantyczny CQRS → kontrakty API → graf architektury.
|
|
@@ -80,33 +78,81 @@ pip install "code2schema[dev]" # pytest + ruff + black
|
|
|
80
78
|
## Szybki start
|
|
81
79
|
|
|
82
80
|
```bash
|
|
83
|
-
#
|
|
84
|
-
code2schema
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
81
|
+
# Analiza z auto-generowanymi nazwami plików (w katalogu bieżącym)
|
|
82
|
+
code2schema /home/tom/github/maskservice/c2004/backend
|
|
83
|
+
# → c2004_schema.json
|
|
84
|
+
|
|
85
|
+
# Pełna analiza ze wszystkimi formatami wyjściowymi
|
|
86
|
+
code2schema ./backend --proto --md --html
|
|
87
|
+
# → backend_schema.json
|
|
88
|
+
# → backend_api.proto
|
|
89
|
+
# → backend_report.md
|
|
90
|
+
# → backend_viz.html
|
|
91
|
+
|
|
92
|
+
# Pełna analiza ze szczegółami
|
|
93
|
+
code2schema ./backend --proto --md --html --graph-summary --events --cycles
|
|
93
94
|
```
|
|
94
95
|
|
|
95
96
|
Przykładowy output:
|
|
96
97
|
|
|
97
98
|
```
|
|
98
|
-
✅ Gotowe (1.
|
|
99
|
-
Modules :
|
|
100
|
-
Functions:
|
|
101
|
-
Queries :
|
|
102
|
-
Commands :
|
|
103
|
-
Orchest. :
|
|
104
|
-
Workflows:
|
|
105
|
-
Rules :
|
|
106
|
-
Graph :
|
|
107
|
-
→
|
|
99
|
+
✅ Gotowe (1.8s)
|
|
100
|
+
Modules : 607
|
|
101
|
+
Functions: 3142
|
|
102
|
+
Queries : 1518
|
|
103
|
+
Commands : 505
|
|
104
|
+
Orchest. : 1119
|
|
105
|
+
Workflows: 1118
|
|
106
|
+
Rules : 320
|
|
107
|
+
Graph : 2922N / 5612E
|
|
108
|
+
→ c2004_schema.json
|
|
109
|
+
→ c2004_api.proto
|
|
110
|
+
→ c2004_report.md
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Test na dużym projekcie (c2004 backend)
|
|
114
|
+
|
|
115
|
+
Analiza produkcyjnego projektu IoT (607 modułów, 3142 funkcje):
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
code2schema /home/tom/github/maskservice/c2004/backend --proto --md --html
|
|
119
|
+
# Pliki generowane w /home/tom/github/maskservice/c2004/ (katalog projektu)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Wyniki:**
|
|
123
|
+
```
|
|
124
|
+
✅ Gotowe (1.8s)
|
|
125
|
+
Modules : 607
|
|
126
|
+
Functions: 3142
|
|
127
|
+
Queries : 1518
|
|
128
|
+
Commands : 505
|
|
129
|
+
Orchest. : 1119
|
|
130
|
+
Workflows: 1118
|
|
131
|
+
Rules : 320
|
|
132
|
+
Graph : 2922N / 5612E
|
|
133
|
+
→ c2004_schema.json
|
|
134
|
+
→ c2004_api.proto
|
|
135
|
+
→ c2004_report.md
|
|
136
|
+
→ c2004_viz.html
|
|
108
137
|
```
|
|
109
138
|
|
|
139
|
+
## Dokumentacja i przykłady
|
|
140
|
+
|
|
141
|
+
- **[Dokumentacja](docs/)** — przykłady użycia CLI i Python API
|
|
142
|
+
- [CLI examples](docs/examples/cli/basic.sh)
|
|
143
|
+
- [Python API — basic](docs/examples/api/basic.py)
|
|
144
|
+
- [Python API — advanced](docs/examples/api/advanced.py)
|
|
145
|
+
- [Sample outputs](docs/examples/outputs/)
|
|
146
|
+
|
|
147
|
+
Wykryte cykle w architekturze CQRS (do refaktoryzacji):
|
|
148
|
+
- `handle → publish → _execute → publish → handle`
|
|
149
|
+
|
|
150
|
+
**Wygenerowane artefakty:**
|
|
151
|
+
- `c2004_schema.json` (2.9MB) — pełny model
|
|
152
|
+
- `c2004_api.proto` (188KB) — gRPC contracts
|
|
153
|
+
- `c2004_report.md` (49KB) — podsumowanie jakości
|
|
154
|
+
- `c2004_viz.html` (1.1MB) — **interaktywny graf D3.js** z wyszukiwaniem i filtrowaniem
|
|
155
|
+
|
|
110
156
|
## Użycie w kodzie
|
|
111
157
|
|
|
112
158
|
```python
|
|
@@ -138,6 +184,21 @@ print(to_proto(schema))
|
|
|
138
184
|
print(to_markdown(schema))
|
|
139
185
|
```
|
|
140
186
|
|
|
187
|
+
## Wizualizacja HTML (`--html`)
|
|
188
|
+
|
|
189
|
+
Interaktywny graf D3.js z funkcjami:
|
|
190
|
+
- **Kolory ról**: 🟢 Query, 🟠 Command, 🟣 Orchestrator
|
|
191
|
+
- **Wyszukiwanie** — filtruje węzły w czasie rzeczywistym
|
|
192
|
+
- **Hover** — podświetla połączenia (call graph)
|
|
193
|
+
- **Klik** — szczegóły funkcji (fan-out, side effects, reguły jakości)
|
|
194
|
+
- **Filtry** — pokazuj/ukrywaj kategorie ról
|
|
195
|
+
- **Force layout** — przeciąganie węzłów
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
code2schema ./backend --html viz.html
|
|
199
|
+
# Otwórz w przeglądarce: open viz.html
|
|
200
|
+
```
|
|
201
|
+
|
|
141
202
|
## Architektura paczki
|
|
142
203
|
|
|
143
204
|
```
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Testy jednostkowe code2schema."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
+
import argparse
|
|
4
5
|
import ast
|
|
5
6
|
import textwrap
|
|
6
7
|
from pathlib import Path
|
|
@@ -10,6 +11,7 @@ import pytest
|
|
|
10
11
|
from code2schema.core.models import CQRSRole, SideEffect
|
|
11
12
|
from code2schema.core.extractor import extract_module
|
|
12
13
|
from code2schema.analyzer.cqrs import _infer_role, analyze, build_call_graph
|
|
14
|
+
from code2schema.cli import _resolve_paths
|
|
13
15
|
from code2schema.codegen import to_json, to_proto, to_markdown
|
|
14
16
|
|
|
15
17
|
|
|
@@ -216,3 +218,48 @@ def test_event_model_summary(sample_module):
|
|
|
216
218
|
summary = em.summary()
|
|
217
219
|
assert "Commands" in summary
|
|
218
220
|
assert "Domain Events" in summary
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
# ── CLI Paths ─────────────────────────────────────────────────────────────────
|
|
224
|
+
|
|
225
|
+
def test_resolve_paths_for_special_project_dir(tmp_path: Path):
|
|
226
|
+
backend_dir = tmp_path / "c2004" / "backend"
|
|
227
|
+
backend_dir.mkdir(parents=True)
|
|
228
|
+
|
|
229
|
+
args = argparse.Namespace(
|
|
230
|
+
path=str(backend_dir),
|
|
231
|
+
out=None,
|
|
232
|
+
proto=True,
|
|
233
|
+
md=True,
|
|
234
|
+
html=True,
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
root, out_path, proto_path, md_path, html_path = _resolve_paths(args)
|
|
238
|
+
|
|
239
|
+
assert root == backend_dir
|
|
240
|
+
assert out_path == backend_dir.parent / "c2004_schema.json"
|
|
241
|
+
assert proto_path == backend_dir.parent / "c2004_api.proto"
|
|
242
|
+
assert md_path == backend_dir.parent / "c2004_report.md"
|
|
243
|
+
assert html_path == backend_dir.parent / "c2004_viz.html"
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def test_resolve_paths_for_file_input_with_custom_outputs(tmp_path: Path):
|
|
247
|
+
file_path = tmp_path / "repo" / "main.py"
|
|
248
|
+
file_path.parent.mkdir(parents=True)
|
|
249
|
+
file_path.write_text("def main():\n pass\n")
|
|
250
|
+
|
|
251
|
+
args = argparse.Namespace(
|
|
252
|
+
path=str(file_path),
|
|
253
|
+
out="schema.json",
|
|
254
|
+
proto="api.proto",
|
|
255
|
+
md=None,
|
|
256
|
+
html=None,
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
root, out_path, proto_path, md_path, html_path = _resolve_paths(args)
|
|
260
|
+
|
|
261
|
+
assert root == file_path
|
|
262
|
+
assert out_path == file_path.parent / "schema.json"
|
|
263
|
+
assert proto_path == file_path.parent / "api.proto"
|
|
264
|
+
assert md_path is None
|
|
265
|
+
assert html_path is None
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
code2schema.cli
|
|
3
|
-
~~~~~~~~~~~~~~~
|
|
4
|
-
CLI v3 — pełny pipeline.
|
|
5
|
-
"""
|
|
6
|
-
from __future__ import annotations
|
|
7
|
-
|
|
8
|
-
import argparse
|
|
9
|
-
import sys
|
|
10
|
-
import time
|
|
11
|
-
from pathlib import Path
|
|
12
|
-
|
|
13
|
-
from code2schema.analyzer.cqrs import analyze
|
|
14
|
-
from code2schema.analyzer.events import infer_event_model
|
|
15
|
-
from code2schema.analyzer.graph import (
|
|
16
|
-
build_rich_graph, detect_cycles, graph_summary, write_dot, write_graphml,
|
|
17
|
-
)
|
|
18
|
-
from code2schema.codegen import write_json, write_markdown, write_proto
|
|
19
|
-
from code2schema.core.extractor import extract_project
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def main(argv=None):
|
|
23
|
-
parser = argparse.ArgumentParser(prog="code2schema",
|
|
24
|
-
description="Semantic Compiler: Code → CQRS → Schema / Proto / Graph")
|
|
25
|
-
parser.add_argument("path")
|
|
26
|
-
parser.add_argument("-o", "--out", default="schema.json")
|
|
27
|
-
parser.add_argument("--proto", metavar="FILE")
|
|
28
|
-
parser.add_argument("--md", metavar="FILE")
|
|
29
|
-
parser.add_argument("--graphml", metavar="FILE")
|
|
30
|
-
parser.add_argument("--dot", metavar="FILE")
|
|
31
|
-
parser.add_argument("--events", action="store_true")
|
|
32
|
-
parser.add_argument("--cycles", action="store_true")
|
|
33
|
-
parser.add_argument("--graph-summary", action="store_true")
|
|
34
|
-
parser.add_argument("--no-rules", action="store_true")
|
|
35
|
-
parser.add_argument("--exclude", nargs="*", default=[])
|
|
36
|
-
parser.add_argument("-q", "--quiet", action="store_true")
|
|
37
|
-
args = parser.parse_args(argv)
|
|
38
|
-
|
|
39
|
-
root = Path(args.path)
|
|
40
|
-
if not root.exists():
|
|
41
|
-
print(f"ERROR: {root}", file=sys.stderr)
|
|
42
|
-
return 1
|
|
43
|
-
|
|
44
|
-
t0 = time.perf_counter()
|
|
45
|
-
if not args.quiet:
|
|
46
|
-
print(f"⏳ Parsing: {root}")
|
|
47
|
-
|
|
48
|
-
modules = extract_project(root, exclude=args.exclude or None)
|
|
49
|
-
if not modules:
|
|
50
|
-
print("ERROR: Brak plików .py.", file=sys.stderr)
|
|
51
|
-
return 1
|
|
52
|
-
|
|
53
|
-
schema = analyze(modules)
|
|
54
|
-
if args.no_rules:
|
|
55
|
-
schema.rules = []
|
|
56
|
-
|
|
57
|
-
G = build_rich_graph(schema)
|
|
58
|
-
|
|
59
|
-
if args.cycles:
|
|
60
|
-
cycles = detect_cycles(G)
|
|
61
|
-
if cycles:
|
|
62
|
-
print(f"\n⚠️ Cykle ({len(cycles)}):")
|
|
63
|
-
for c in cycles[:5]:
|
|
64
|
-
print(f" {' → '.join(c[:5])}")
|
|
65
|
-
else:
|
|
66
|
-
print("✅ Brak cykli.")
|
|
67
|
-
|
|
68
|
-
if args.graph_summary:
|
|
69
|
-
print("\n── Graph Summary ──────────────────────")
|
|
70
|
-
print(graph_summary(G, schema))
|
|
71
|
-
|
|
72
|
-
if args.events:
|
|
73
|
-
em = infer_event_model(modules)
|
|
74
|
-
print("\n── Event Model ────────────────────────")
|
|
75
|
-
print(em.summary())
|
|
76
|
-
|
|
77
|
-
write_json(schema, Path(args.out))
|
|
78
|
-
if args.proto:
|
|
79
|
-
write_proto(schema, Path(args.proto))
|
|
80
|
-
if args.md:
|
|
81
|
-
write_markdown(schema, Path(args.md))
|
|
82
|
-
if args.graphml:
|
|
83
|
-
write_graphml(G, Path(args.graphml))
|
|
84
|
-
if args.dot:
|
|
85
|
-
write_dot(G, Path(args.dot))
|
|
86
|
-
|
|
87
|
-
if not args.quiet:
|
|
88
|
-
funcs = schema.all_functions()
|
|
89
|
-
print(
|
|
90
|
-
f"\n✅ Gotowe ({time.perf_counter()-t0:.2f}s)\n"
|
|
91
|
-
f" Modules : {len(modules)}\n"
|
|
92
|
-
f" Functions: {len(funcs)}\n"
|
|
93
|
-
f" Queries : {len(schema.queries())}\n"
|
|
94
|
-
f" Commands : {len(schema.commands())}\n"
|
|
95
|
-
f" Orchest. : {len(schema.orchestrators())}\n"
|
|
96
|
-
f" Workflows: {len(schema.workflows)}\n"
|
|
97
|
-
f" Rules : {len(schema.rules)}\n"
|
|
98
|
-
f" Graph : {G.number_of_nodes()}N / {G.number_of_edges()}E\n"
|
|
99
|
-
f" → {args.out}"
|
|
100
|
-
)
|
|
101
|
-
return 0
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if __name__ == "__main__":
|
|
105
|
-
raise SystemExit(main())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|