code2llm 0.3.7__py3-none-any.whl
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.
- code2flow/__init__.py +47 -0
- code2flow/__main__.py +6 -0
- code2flow/analysis/__init__.py +23 -0
- code2flow/analysis/call_graph.py +210 -0
- code2flow/analysis/cfg.py +293 -0
- code2flow/analysis/coupling.py +77 -0
- code2flow/analysis/data_analysis.py +249 -0
- code2flow/analysis/dfg.py +224 -0
- code2flow/analysis/pipeline_detector.py +445 -0
- code2flow/analysis/side_effects.py +313 -0
- code2flow/analysis/smells.py +192 -0
- code2flow/analysis/type_inference.py +306 -0
- code2flow/cli.py +493 -0
- code2flow/core/__init__.py +36 -0
- code2flow/core/analyzer.py +765 -0
- code2flow/core/config.py +177 -0
- code2flow/core/models.py +194 -0
- code2flow/core/streaming_analyzer.py +666 -0
- code2flow/exporters/__init__.py +35 -0
- code2flow/exporters/base.py +13 -0
- code2flow/exporters/context_exporter.py +207 -0
- code2flow/exporters/flow_exporter.py +570 -0
- code2flow/exporters/json_exporter.py +17 -0
- code2flow/exporters/llm_exporter.py +12 -0
- code2flow/exporters/map_exporter.py +218 -0
- code2flow/exporters/mermaid_exporter.py +67 -0
- code2flow/exporters/toon.py +982 -0
- code2flow/exporters/yaml_exporter.py +108 -0
- code2flow/llm_flow_generator.py +451 -0
- code2flow/llm_task_generator.py +263 -0
- code2flow/mermaid_generator.py +481 -0
- code2flow/nlp/__init__.py +23 -0
- code2flow/nlp/config.py +174 -0
- code2flow/nlp/entity_resolution.py +326 -0
- code2flow/nlp/intent_matching.py +297 -0
- code2flow/nlp/normalization.py +122 -0
- code2flow/nlp/pipeline.py +388 -0
- code2flow/patterns/__init__.py +0 -0
- code2flow/patterns/detector.py +168 -0
- code2flow/refactor/__init__.py +0 -0
- code2flow/refactor/prompt_engine.py +150 -0
- code2flow/visualizers/__init__.py +0 -0
- code2flow/visualizers/graph.py +196 -0
- code2llm-0.3.7.dist-info/METADATA +604 -0
- code2llm-0.3.7.dist-info/RECORD +49 -0
- code2llm-0.3.7.dist-info/WHEEL +5 -0
- code2llm-0.3.7.dist-info/entry_points.txt +2 -0
- code2llm-0.3.7.dist-info/licenses/LICENSE +201 -0
- code2llm-0.3.7.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import sys
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
5
|
+
|
|
6
|
+
import yaml
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _strip_bom(text: str) -> str:
|
|
10
|
+
return text[1:] if text.startswith("\ufeff") else text
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _ensure_list(value: Any) -> List[Any]:
|
|
14
|
+
if value is None:
|
|
15
|
+
return []
|
|
16
|
+
if isinstance(value, list):
|
|
17
|
+
return value
|
|
18
|
+
return [value]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _deep_get(d: Dict[str, Any], path: Tuple[str, ...]) -> Any:
|
|
22
|
+
cur: Any = d
|
|
23
|
+
for key in path:
|
|
24
|
+
if not isinstance(cur, dict) or key not in cur:
|
|
25
|
+
return None
|
|
26
|
+
cur = cur[key]
|
|
27
|
+
return cur
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def normalize_llm_task(data: Dict[str, Any]) -> Dict[str, Any]:
|
|
31
|
+
task = data.get("task") or {}
|
|
32
|
+
context = data.get("context") or {}
|
|
33
|
+
deliverables = data.get("deliverables") or {}
|
|
34
|
+
interfaces = data.get("interfaces") or {}
|
|
35
|
+
rules = data.get("rules") or {}
|
|
36
|
+
acceptance = data.get("acceptance") or {}
|
|
37
|
+
examples = data.get("examples")
|
|
38
|
+
notes = data.get("notes_for_llm") or {}
|
|
39
|
+
|
|
40
|
+
normalized: Dict[str, Any] = {
|
|
41
|
+
"task": {
|
|
42
|
+
"title": task.get("title") or "",
|
|
43
|
+
"one_line_goal": task.get("one_line_goal") or "",
|
|
44
|
+
},
|
|
45
|
+
"context": {
|
|
46
|
+
"product_area": context.get("product_area") or "",
|
|
47
|
+
"current_behavior": context.get("current_behavior") or "",
|
|
48
|
+
"desired_behavior": context.get("desired_behavior") or "",
|
|
49
|
+
},
|
|
50
|
+
"deliverables": {
|
|
51
|
+
"language": deliverables.get("language") or "any",
|
|
52
|
+
"must_generate": _ensure_list(deliverables.get("must_generate")),
|
|
53
|
+
"files_to_create_or_edit": _ensure_list(deliverables.get("files_to_create_or_edit")),
|
|
54
|
+
},
|
|
55
|
+
"interfaces": {
|
|
56
|
+
"inputs": _ensure_list(interfaces.get("inputs")),
|
|
57
|
+
"outputs": _ensure_list(interfaces.get("outputs")),
|
|
58
|
+
},
|
|
59
|
+
"rules": {
|
|
60
|
+
"must": _ensure_list(rules.get("must")),
|
|
61
|
+
"must_not": _ensure_list(rules.get("must_not")),
|
|
62
|
+
"assumptions": _ensure_list(rules.get("assumptions")),
|
|
63
|
+
"edge_cases": _ensure_list(rules.get("edge_cases")),
|
|
64
|
+
"performance": _ensure_list(rules.get("performance")),
|
|
65
|
+
},
|
|
66
|
+
"acceptance": {
|
|
67
|
+
"tests": _ensure_list(acceptance.get("tests")),
|
|
68
|
+
"done_definition": _ensure_list(acceptance.get("done_definition")),
|
|
69
|
+
},
|
|
70
|
+
"examples": _ensure_list(examples),
|
|
71
|
+
"notes_for_llm": {
|
|
72
|
+
"constraints": _ensure_list(notes.get("constraints")),
|
|
73
|
+
"style": _ensure_list(notes.get("style")),
|
|
74
|
+
"language_specific_hints": _ensure_list(notes.get("language_specific_hints")),
|
|
75
|
+
},
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return normalized
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
_SECTION_KEYS = {
|
|
82
|
+
"TITLE": ("task", "title"),
|
|
83
|
+
"GOAL": ("task", "one_line_goal"),
|
|
84
|
+
"PRODUCT_AREA": ("context", "product_area"),
|
|
85
|
+
"CURRENT": ("context", "current_behavior"),
|
|
86
|
+
"DESIRED": ("context", "desired_behavior"),
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _parse_bullets(lines: List[str]) -> List[str]:
|
|
91
|
+
items: List[str] = []
|
|
92
|
+
for raw in lines:
|
|
93
|
+
s = raw.strip()
|
|
94
|
+
if not s:
|
|
95
|
+
continue
|
|
96
|
+
if s.startswith("-"):
|
|
97
|
+
items.append(s[1:].strip())
|
|
98
|
+
else:
|
|
99
|
+
items.append(s)
|
|
100
|
+
return items
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def parse_llm_task_text(text: str) -> Dict[str, Any]:
|
|
104
|
+
text = _strip_bom(text)
|
|
105
|
+
lines = text.replace("\r\n", "\n").replace("\r", "\n").split("\n")
|
|
106
|
+
|
|
107
|
+
sections: Dict[str, List[str]] = {}
|
|
108
|
+
current: Optional[str] = None
|
|
109
|
+
|
|
110
|
+
def start_section(name: str) -> None:
|
|
111
|
+
nonlocal current
|
|
112
|
+
current = name
|
|
113
|
+
sections.setdefault(name, [])
|
|
114
|
+
|
|
115
|
+
for line in lines:
|
|
116
|
+
stripped = line.strip()
|
|
117
|
+
if not stripped:
|
|
118
|
+
if current is not None:
|
|
119
|
+
sections[current].append("")
|
|
120
|
+
continue
|
|
121
|
+
|
|
122
|
+
upper = stripped.upper()
|
|
123
|
+
if upper.endswith(":"):
|
|
124
|
+
key = upper[:-1].strip()
|
|
125
|
+
if key in {
|
|
126
|
+
"TITLE",
|
|
127
|
+
"GOAL",
|
|
128
|
+
"CURRENT",
|
|
129
|
+
"DESIRED",
|
|
130
|
+
"INPUTS",
|
|
131
|
+
"OUTPUTS",
|
|
132
|
+
"RULES (MUST)",
|
|
133
|
+
"RULES (MUST NOT)",
|
|
134
|
+
"EDGE CASES",
|
|
135
|
+
"ACCEPTANCE TESTS",
|
|
136
|
+
"DELIVERABLES",
|
|
137
|
+
}:
|
|
138
|
+
start_section(key)
|
|
139
|
+
continue
|
|
140
|
+
|
|
141
|
+
if current is None:
|
|
142
|
+
continue
|
|
143
|
+
sections[current].append(line)
|
|
144
|
+
|
|
145
|
+
data: Dict[str, Any] = {
|
|
146
|
+
"task": {"title": "", "one_line_goal": ""},
|
|
147
|
+
"context": {"product_area": "", "current_behavior": "", "desired_behavior": ""},
|
|
148
|
+
"deliverables": {"language": "any", "must_generate": [], "files_to_create_or_edit": []},
|
|
149
|
+
"interfaces": {"inputs": [], "outputs": []},
|
|
150
|
+
"rules": {"must": [], "must_not": [], "assumptions": [], "edge_cases": [], "performance": []},
|
|
151
|
+
"acceptance": {"tests": [], "done_definition": []},
|
|
152
|
+
"examples": [],
|
|
153
|
+
"notes_for_llm": {"constraints": [], "style": [], "language_specific_hints": []},
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
for section_name, path in _SECTION_KEYS.items():
|
|
157
|
+
content_lines = sections.get(section_name)
|
|
158
|
+
if not content_lines:
|
|
159
|
+
continue
|
|
160
|
+
value = "\n".join(content_lines).strip()
|
|
161
|
+
if value:
|
|
162
|
+
parent = data
|
|
163
|
+
for key in path[:-1]:
|
|
164
|
+
parent = parent[key]
|
|
165
|
+
parent[path[-1]] = value
|
|
166
|
+
|
|
167
|
+
if sections.get("INPUTS"):
|
|
168
|
+
data["interfaces"]["inputs"] = _parse_bullets(sections["INPUTS"])
|
|
169
|
+
if sections.get("OUTPUTS"):
|
|
170
|
+
data["interfaces"]["outputs"] = _parse_bullets(sections["OUTPUTS"])
|
|
171
|
+
if sections.get("RULES (MUST)"):
|
|
172
|
+
data["rules"]["must"] = _parse_bullets(sections["RULES (MUST)"])
|
|
173
|
+
if sections.get("RULES (MUST NOT)"):
|
|
174
|
+
data["rules"]["must_not"] = _parse_bullets(sections["RULES (MUST NOT)"])
|
|
175
|
+
if sections.get("EDGE CASES"):
|
|
176
|
+
data["rules"]["edge_cases"] = _parse_bullets(sections["EDGE CASES"])
|
|
177
|
+
|
|
178
|
+
if sections.get("ACCEPTANCE TESTS"):
|
|
179
|
+
tests: List[Dict[str, str]] = []
|
|
180
|
+
raw_items = _parse_bullets(sections["ACCEPTANCE TESTS"])
|
|
181
|
+
for idx, item in enumerate(raw_items, 1):
|
|
182
|
+
tests.append({"name": f"test_{idx}", "given": "", "when": "", "then": item})
|
|
183
|
+
data["acceptance"]["tests"] = tests
|
|
184
|
+
|
|
185
|
+
if sections.get("DELIVERABLES"):
|
|
186
|
+
data["deliverables"]["must_generate"] = _parse_bullets(sections["DELIVERABLES"])
|
|
187
|
+
|
|
188
|
+
return data
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def load_input(path: Path) -> Dict[str, Any]:
|
|
192
|
+
raw = path.read_text(encoding="utf-8")
|
|
193
|
+
raw = _strip_bom(raw)
|
|
194
|
+
|
|
195
|
+
if path.suffix.lower() in {".yaml", ".yml"}:
|
|
196
|
+
loaded = yaml.safe_load(raw) or {}
|
|
197
|
+
if not isinstance(loaded, dict):
|
|
198
|
+
raise ValueError("YAML input must be a mapping/object at top level")
|
|
199
|
+
return loaded
|
|
200
|
+
|
|
201
|
+
if path.suffix.lower() == ".json":
|
|
202
|
+
import json
|
|
203
|
+
|
|
204
|
+
loaded = json.loads(raw)
|
|
205
|
+
if not isinstance(loaded, dict):
|
|
206
|
+
raise ValueError("JSON input must be an object at top level")
|
|
207
|
+
return loaded
|
|
208
|
+
|
|
209
|
+
return parse_llm_task_text(raw)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def dump_yaml(data: Dict[str, Any]) -> str:
|
|
213
|
+
return yaml.safe_dump(
|
|
214
|
+
data,
|
|
215
|
+
sort_keys=False,
|
|
216
|
+
allow_unicode=True,
|
|
217
|
+
width=100,
|
|
218
|
+
default_flow_style=False,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def create_parser() -> argparse.ArgumentParser:
|
|
223
|
+
p = argparse.ArgumentParser(
|
|
224
|
+
prog="llm-task-generator",
|
|
225
|
+
description="Generate normalized llm_task.yaml from simplified task spec (text/YAML/JSON).",
|
|
226
|
+
)
|
|
227
|
+
p.add_argument("-i", "--input", required=True, help="Input file: .txt/.md/.yaml/.yml/.json")
|
|
228
|
+
p.add_argument("-o", "--output", required=True, help="Output YAML file path")
|
|
229
|
+
p.add_argument(
|
|
230
|
+
"--validate-only",
|
|
231
|
+
action="store_true",
|
|
232
|
+
help="Only validate/normalize input; do not write output file",
|
|
233
|
+
)
|
|
234
|
+
return p
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def main(argv: Optional[List[str]] = None) -> int:
|
|
238
|
+
args = create_parser().parse_args(argv)
|
|
239
|
+
|
|
240
|
+
input_path = Path(args.input)
|
|
241
|
+
if not input_path.exists():
|
|
242
|
+
print(f"Error: input file not found: {input_path}", file=sys.stderr)
|
|
243
|
+
return 2
|
|
244
|
+
|
|
245
|
+
data = load_input(input_path)
|
|
246
|
+
|
|
247
|
+
if "task" not in data:
|
|
248
|
+
data = {"task": data}
|
|
249
|
+
|
|
250
|
+
normalized = normalize_llm_task(data)
|
|
251
|
+
|
|
252
|
+
if args.validate_only:
|
|
253
|
+
sys.stdout.write(dump_yaml(normalized))
|
|
254
|
+
return 0
|
|
255
|
+
|
|
256
|
+
output_path = Path(args.output)
|
|
257
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
258
|
+
output_path.write_text(dump_yaml(normalized), encoding="utf-8")
|
|
259
|
+
return 0
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
if __name__ == "__main__":
|
|
263
|
+
raise SystemExit(main())
|