code2logic 1.0.39__tar.gz → 1.0.41__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. {code2logic-1.0.39 → code2logic-1.0.41}/PKG-INFO +4 -4
  2. {code2logic-1.0.39 → code2logic-1.0.41}/README.md +3 -3
  3. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/__init__.py +1 -1
  4. code2logic-1.0.41/code2logic/base_generator.py +6 -0
  5. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/benchmarks/common.py +49 -6
  6. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/benchmarks/runner.py +135 -39
  7. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/function_logic.py +5 -6
  8. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/parsers.py +12 -12
  9. {code2logic-1.0.39 → code2logic-1.0.41}/pyproject.toml +1 -1
  10. code2logic-1.0.39/code2logic/llm_clients_new.py +0 -44
  11. {code2logic-1.0.39 → code2logic-1.0.41}/LICENSE +0 -0
  12. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/__main__.py +0 -0
  13. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/adaptive.py +0 -0
  14. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/analyzer.py +0 -0
  15. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/base.py +0 -0
  16. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/benchmark.py +0 -0
  17. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/benchmarks/__init__.py +0 -0
  18. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/benchmarks/results.py +0 -0
  19. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/chunked_reproduction.py +0 -0
  20. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/cli.py +0 -0
  21. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/code_review.py +0 -0
  22. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/config.py +0 -0
  23. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/core/__init__.py +0 -0
  24. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/dependency.py +0 -0
  25. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/errors.py +0 -0
  26. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/file_formats.py +0 -0
  27. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/formats/__init__.py +0 -0
  28. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/generators.py +0 -0
  29. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/gherkin.py +0 -0
  30. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/integrations/__init__.py +0 -0
  31. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/intent.py +0 -0
  32. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/llm/__init__.py +0 -0
  33. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/llm.py +0 -0
  34. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/llm_clients.py +0 -0
  35. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/llm_profiler.py +0 -0
  36. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/logicml.py +0 -0
  37. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/markdown_format.py +0 -0
  38. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/mcp_server.py +0 -0
  39. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/metrics.py +0 -0
  40. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/models.py +0 -0
  41. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/project_comparison.md +0 -0
  42. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/project_reproducer.py +0 -0
  43. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/prompts.py +0 -0
  44. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/py.typed +0 -0
  45. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/quality.py +0 -0
  46. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/refactor.py +0 -0
  47. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/reproducer.py +0 -0
  48. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/reproduction.py +0 -0
  49. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/schemas/__init__.py +0 -0
  50. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/schemas/json_schema.py +0 -0
  51. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/schemas/logicml_schema.py +0 -0
  52. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/schemas/markdown_schema.py +0 -0
  53. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/schemas/yaml_schema.py +0 -0
  54. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/shared_utils.py +0 -0
  55. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/similarity.py +0 -0
  56. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/terminal.py +0 -0
  57. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/tools/__init__.py +0 -0
  58. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/toon_format.py +0 -0
  59. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/universal.py +0 -0
  60. {code2logic-1.0.39 → code2logic-1.0.41}/code2logic/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code2logic
3
- Version: 1.0.39
3
+ Version: 1.0.41
4
4
  Summary: Code2Logic - Source code to logical representation converter for LLM analysis, featuring Tree-sitter parsing, dependency graph analysis, and multi-language support.
5
5
  License: Apache-2.0
6
6
  License-File: LICENSE
@@ -95,9 +95,9 @@ pip install code2logic[nlp] # Enhanced intents
95
95
  ## 📖 Quick Start
96
96
  ```bash
97
97
  code2logic ./ -f yaml --compact --function-logic --with-schema -o project.yaml
98
- code2logic ./ -f toon --function-logic --with-schema --name project -o ./
99
- # Optional: include function intent/purpose column in project.functions.toon
100
- code2logic ./ -f toon --function-logic --does --name project -o ./
98
+ code2logic ./ -f toon --function-logic function.toon --with-schema --name project -o ./
99
+ # Optional: include function intent/purpose column in function.toon
100
+ code2logic ./ -f toon --function-logic function.toon --does --name project -o ./
101
101
  ```
102
102
 
103
103
  ### Command Line
@@ -49,9 +49,9 @@ pip install code2logic[nlp] # Enhanced intents
49
49
  ## 📖 Quick Start
50
50
  ```bash
51
51
  code2logic ./ -f yaml --compact --function-logic --with-schema -o project.yaml
52
- code2logic ./ -f toon --function-logic --with-schema --name project -o ./
53
- # Optional: include function intent/purpose column in project.functions.toon
54
- code2logic ./ -f toon --function-logic --does --name project -o ./
52
+ code2logic ./ -f toon --function-logic function.toon --with-schema --name project -o ./
53
+ # Optional: include function intent/purpose column in function.toon
54
+ code2logic ./ -f toon --function-logic function.toon --does --name project -o ./
55
55
  ```
56
56
 
57
57
  ### Command Line
@@ -18,7 +18,7 @@ Example:
18
18
  >>> print(output)
19
19
  """
20
20
 
21
- __version__ = "1.0.39"
21
+ __version__ = "1.0.41"
22
22
  __author__ = "Softreck"
23
23
  __email__ = "info@softreck.dev"
24
24
  __license__ = "MIT"
@@ -0,0 +1,6 @@
1
+ from typing import Protocol, Any
2
+ from .models import ProjectInfo
3
+
4
+ class ProjectGenerator(Protocol):
5
+ def generate(self, project: ProjectInfo, **kwargs: Any) -> Any:
6
+ ...
@@ -5,6 +5,7 @@ from datetime import datetime
5
5
  from pathlib import Path
6
6
 
7
7
  from ..generators import CSVGenerator, JSONGenerator, YAMLGenerator
8
+ from ..function_logic import FunctionLogicGenerator
8
9
  from ..gherkin import GherkinGenerator
9
10
  from ..logicml import LogicMLGenerator
10
11
  from ..markdown_format import MarkdownHybridGenerator
@@ -32,6 +33,15 @@ def generate_spec(project: ProjectInfo, fmt: str) -> str:
32
33
  if fmt == "gherkin":
33
34
  gen = GherkinGenerator()
34
35
  return gen.generate(project)
36
+ if fmt == "function.toon":
37
+ gen = FunctionLogicGenerator()
38
+ return gen.generate_toon(
39
+ project,
40
+ detail="full",
41
+ no_repeat_name=True,
42
+ no_repeat_details=True,
43
+ include_does=True,
44
+ )
35
45
  if fmt == "csv":
36
46
  gen = CSVGenerator()
37
47
  return gen.generate(project, detail="full")
@@ -179,14 +189,21 @@ Name the test class Test<ClassName> or TestFunctions."""
179
189
 
180
190
  def get_token_reproduction_prompt(spec: str, fmt: str, file_name: str, language: str = "python") -> str:
181
191
  format_hints = {
182
- "json": "Parse the JSON structure and implement all classes and functions.",
183
- "json_compact": "Parse the compact JSON and implement all elements.",
192
+ "json": "Parse the JSON structure and implement all classes and functions with exact signatures.",
193
+ "json_compact": "Parse the compact JSON and implement all elements with exact signatures.",
184
194
  "yaml": "Parse the YAML structure and implement all classes and functions with exact signatures.",
185
- "gherkin": "Implement scenarios as SIMPLE, MINIMAL code. NO over-engineering. Keep code short and direct.",
186
- "markdown": "Parse embedded Gherkin (behaviors) and YAML (structures).",
195
+ "gherkin": """Parse Gherkin/BDD scenarios and implement them as working code:
196
+ - Each Feature maps to a class or module
197
+ - Each Scenario maps to a function
198
+ - Given/When/Then steps describe the logic flow
199
+ - Implement actual logic, not just stubs
200
+ Focus on the described behavior and implement it directly.""",
201
+ "markdown": "Parse embedded Gherkin (behaviors) and YAML (structures). Implement all described classes and functions.",
187
202
  "logicml": """Parse LogicML and generate VALID code:
188
203
  - 'sig:' lines describe function signatures (translate to the target language)
189
204
  - 'type: re-export' means this module primarily re-exports symbols
205
+ - 'attrs:' = instance attributes to set in constructor
206
+ - 'bases:' = parent classes to inherit from
190
207
  CRITICAL: Ensure valid syntax - balanced brackets, proper indentation, no undefined variables.""",
191
208
  "toon": """Parse TOON (Token-Oriented Object Notation) format carefully:
192
209
 
@@ -206,9 +223,32 @@ DECORATORS:
206
223
  - 'decorators: @staticmethod|@cache' = multiple decorators
207
224
 
208
225
  CRITICAL: Use imports[], function_docs, and exact signatures to reproduce code accurately.""",
226
+ "csv": """Parse the CSV table where each row describes a code element:
227
+ - Columns: path, type (class/method/function), name, signature, language, intent, category, domain, imports
228
+ - 'method' rows belong to the class in the preceding 'class' row
229
+ - Implement all elements with the exact signatures shown
230
+ Generate complete code with all classes, methods, and functions.""",
231
+ "function.toon": """Parse the function-logic TOON format:
232
+ - 'modules[N]{path,lang,items}:' lists files
233
+ - 'function_details:' contains per-module function listings
234
+ - Each function has: line number, name, signature, description
235
+ - 'ClassName.method_name' = method of that class
236
+ - 'cc:N' after name = cyclomatic complexity
237
+ Implement all listed functions with matching signatures and described behavior.""",
209
238
  }
210
239
 
211
- max_spec = 5000
240
+ # Language-specific guidance appended to prompt
241
+ lang_hints = {
242
+ "javascript": "Use ES6+ syntax (const/let, arrow functions, classes). Use module.exports or export.",
243
+ "typescript": "Use TypeScript syntax with interfaces, type annotations, and export statements.",
244
+ "go": "Use proper Go syntax: package declaration, func receivers for methods, error returns.",
245
+ "rust": "Use proper Rust syntax: impl blocks for methods, pub fn, Result/Option types, ownership.",
246
+ "java": "Use proper Java syntax: public class, access modifiers, typed parameters, semicolons.",
247
+ "csharp": "Use proper C# syntax: namespaces, access modifiers, typed parameters, semicolons.",
248
+ "sql": "Use standard SQL: CREATE TABLE/VIEW/FUNCTION, proper column types, constraints.",
249
+ }
250
+
251
+ max_spec = 8000
212
252
  spec_truncated = spec[:max_spec] if len(spec) > max_spec else spec
213
253
 
214
254
  language_norm = (language or "python").strip().lower()
@@ -224,8 +264,11 @@ CRITICAL: Use imports[], function_docs, and exact signatures to reproduce code a
224
264
  }
225
265
  lang_label = lang_label_map.get(language_norm, language_norm)
226
266
 
267
+ lang_hint = lang_hints.get(language_norm, '')
268
+ lang_hint_line = f"\n{lang_hint}" if lang_hint else ''
269
+
227
270
  prompt = f"""Generate {lang_label} code from this {fmt.upper()} specification.
228
- {format_hints.get(fmt, '')}
271
+ {format_hints.get(fmt, '')}{lang_hint_line}
229
272
 
230
273
  {spec_truncated}
231
274
 
@@ -15,12 +15,14 @@ Usage:
15
15
  result.save('output/benchmark.json')
16
16
  """
17
17
 
18
- import sys
19
- import time
20
18
  import difflib
21
19
  import re
20
+ import sys
21
+ import time
22
22
  from pathlib import Path
23
- from typing import List, Optional
23
+ from typing import Any, Dict, List, Optional
24
+
25
+ from concurrent.futures import ThreadPoolExecutor, as_completed
24
26
 
25
27
  from ..analyzer import analyze_project
26
28
  from ..llm_clients import BaseLLMClient, get_client
@@ -193,7 +195,7 @@ class BenchmarkRunner:
193
195
  """
194
196
  self.client = client
195
197
  self.config = config or BenchmarkConfig()
196
- self._metrics = ReproductionMetrics()
198
+ # Note: avoid sharing ReproductionMetrics instance across threads.
197
199
 
198
200
  def _should_use_llm(self) -> bool:
199
201
  """Return whether this runner should call an LLM."""
@@ -215,12 +217,37 @@ class BenchmarkRunner:
215
217
  classes: List[str] = []
216
218
  functions: List[str] = []
217
219
 
218
- # Common patterns
220
+ # Common patterns across formats
219
221
  classes.extend(re.findall(r"\bclass\s+([A-Za-z_][A-Za-z0-9_]*)", spec))
220
- classes.extend(re.findall(r"^([A-Za-z_][A-Za-z0-9_]*)\s*:\s*$", spec, re.MULTILINE))
222
+ classes.extend(re.findall(r'"name":\s*"([A-Z][A-Za-z0-9_]*)"', spec))
221
223
  functions.extend(re.findall(r"\bdef\s+([A-Za-z_][A-Za-z0-9_]*)\s*\(", spec))
222
224
  functions.extend(re.findall(r"\bFunction:\s*([A-Za-z_][A-Za-z0-9_]*)", spec))
223
225
  functions.extend(re.findall(r"\bScenario:\s*([A-Za-z_][A-Za-z0-9_]*)", spec))
226
+ # TOON format: function lines like " 42,func_name,(params)"
227
+ functions.extend(re.findall(r'^\s+\d+,([A-Za-z_][A-Za-z0-9_]*),', spec, re.MULTILINE))
228
+ # TOON class.method: " 42,ClassName.method_name,"
229
+ for cm in re.findall(r'^\s+\d+,([A-Z][A-Za-z0-9_]*)\.([a-z_][A-Za-z0-9_]*),', spec, re.MULTILINE):
230
+ classes.append(cm[0])
231
+ functions.append(cm[1])
232
+ # YAML/JSON: "n: ClassName" or name patterns
233
+ classes.extend(re.findall(r'(?:^|\s)n:\s*([A-Z][A-Za-z0-9_]*)', spec, re.MULTILINE))
234
+ # CSV format: path,type,name columns
235
+ for row_m in re.findall(r',class,([A-Z][A-Za-z0-9_]*),', spec):
236
+ classes.append(row_m)
237
+ for row_m in re.findall(r',(?:function|method),([a-z_][A-Za-z0-9_]*),', spec):
238
+ functions.append(row_m)
239
+ # Gherkin: Feature/Scenario names
240
+ functions.extend(re.findall(r'Scenario(?:\s+Outline)?:\s*(?:Test\s+)?([A-Za-z_][A-Za-z0-9_]*)', spec))
241
+ # Non-Python: func/fn/function declarations
242
+ functions.extend(re.findall(r"\bfunc\s+([A-Za-z_][A-Za-z0-9_]*)", spec))
243
+ functions.extend(re.findall(r"\bfn\s+([A-Za-z_][A-Za-z0-9_]*)", spec))
244
+ functions.extend(re.findall(r"\bfunction\s+([A-Za-z_][A-Za-z0-9_]*)\s*\(", spec))
245
+ # Java/C# style: "public static Type methodName("
246
+ functions.extend(re.findall(r"(?:public|private)\s+(?:static\s+)?\w+\s+([a-z][A-Za-z0-9_]*)\s*\(", spec))
247
+ # Interfaces/structs/enums
248
+ classes.extend(re.findall(r"\binterface\s+([A-Za-z_][A-Za-z0-9_]*)", spec))
249
+ classes.extend(re.findall(r"\bstruct\s+([A-Za-z_][A-Za-z0-9_]*)", spec))
250
+ classes.extend(re.findall(r"\benum\s+([A-Za-z_][A-Za-z0-9_]*)", spec))
224
251
 
225
252
  # Deduplicate while preserving order
226
253
  def uniq(items: List[str]) -> List[str]:
@@ -395,11 +422,41 @@ class {cls}:
395
422
  original_size=len(original),
396
423
  )
397
424
 
398
- for fmt in formats:
399
- fmt_result = self._test_format(
400
- single_project, original, fmt, abs_file.name, client, verbose, language=module_info.language
401
- )
402
- file_result.format_results[fmt] = fmt_result
425
+ max_workers = int(getattr(self.config, "workers", 1) or 1)
426
+ if max_workers < 1:
427
+ max_workers = 1
428
+
429
+ # Parallelize per-format reproduction (LLM calls) for the same file.
430
+ if len(formats) > 1 and max_workers > 1:
431
+ with ThreadPoolExecutor(max_workers=max_workers) as ex:
432
+ futs = {
433
+ ex.submit(
434
+ self._test_format,
435
+ single_project,
436
+ original,
437
+ fmt,
438
+ abs_file.name,
439
+ client,
440
+ verbose,
441
+ module_info.language,
442
+ ): fmt
443
+ for fmt in formats
444
+ }
445
+ for fut in as_completed(futs):
446
+ fmt = futs[fut]
447
+ file_result.format_results[fmt] = fut.result()
448
+ else:
449
+ for fmt in formats:
450
+ fmt_result = self._test_format(
451
+ single_project,
452
+ original,
453
+ fmt,
454
+ abs_file.name,
455
+ client,
456
+ verbose,
457
+ language=module_info.language,
458
+ )
459
+ file_result.format_results[fmt] = fmt_result
403
460
 
404
461
  # Set best result as file score
405
462
  if file_result.format_results:
@@ -472,7 +529,8 @@ class {cls}:
472
529
  # Calculate metrics
473
530
  if original and generated:
474
531
  if language_norm == 'python':
475
- analysis = self._metrics.analyze(original, generated, spec, format_name=fmt)
532
+ metrics = ReproductionMetrics()
533
+ analysis = metrics.analyze(original, generated, spec, format_name=fmt)
476
534
  result.score = analysis.overall_score
477
535
  result.similarity = analysis.text.char_similarity
478
536
  else:
@@ -816,33 +874,71 @@ Requirements:
816
874
  if verbose:
817
875
  print(f"\n--- Format: {fmt.upper()} ---")
818
876
 
819
- for i, module in enumerate(modules):
820
- file_result = self._reproduce_module(
821
- module, fmt, project_path, client, verbose
822
- )
823
-
824
- # Add format to result
825
- if file_result.format_results:
826
- fmt_result = list(file_result.format_results.values())[0]
827
- else:
828
- fmt_result = FormatResult(format_name=fmt, score=file_result.score)
829
- file_result.format_results[fmt] = fmt_result
830
-
831
- # Find existing file result or create new
832
- existing = None
833
- for fr in result.file_results:
834
- if fr.file_path == file_result.file_path:
835
- existing = fr
836
- break
837
-
838
- if existing:
839
- existing.format_results[fmt] = fmt_result
840
- else:
841
- result.file_results.append(file_result)
842
-
843
- if verbose:
844
- status = "✓" if file_result.score > 50 else "○"
845
- print(f" [{i+1}/{len(modules)}] {Path(file_result.file_path).name}: {file_result.score:.1f}% {status}")
877
+ max_workers = int(getattr(self.config, "workers", 1) or 1)
878
+ if max_workers < 1:
879
+ max_workers = 1
880
+
881
+ # Parallelize per-module reproduction for a given format.
882
+ if len(modules) > 1 and max_workers > 1:
883
+ with ThreadPoolExecutor(max_workers=max_workers) as ex:
884
+ futs = {
885
+ ex.submit(self._reproduce_module, module, fmt, project_path, client, verbose): (i, module)
886
+ for i, module in enumerate(modules)
887
+ }
888
+ for fut in as_completed(futs):
889
+ i, _module = futs[fut]
890
+ file_result = fut.result()
891
+
892
+ # Add format to result
893
+ if file_result.format_results:
894
+ fmt_result = list(file_result.format_results.values())[0]
895
+ else:
896
+ fmt_result = FormatResult(format_name=fmt, score=file_result.score)
897
+ file_result.format_results[fmt] = fmt_result
898
+
899
+ # Find existing file result or create new
900
+ existing = None
901
+ for fr in result.file_results:
902
+ if fr.file_path == file_result.file_path:
903
+ existing = fr
904
+ break
905
+
906
+ if existing:
907
+ existing.format_results[fmt] = fmt_result
908
+ else:
909
+ result.file_results.append(file_result)
910
+
911
+ if verbose:
912
+ status = "✓" if file_result.score > 50 else "○"
913
+ print(f" [{i+1}/{len(modules)}] {Path(file_result.file_path).name}: {file_result.score:.1f}% {status}")
914
+ else:
915
+ for i, module in enumerate(modules):
916
+ file_result = self._reproduce_module(
917
+ module, fmt, project_path, client, verbose
918
+ )
919
+
920
+ # Add format to result
921
+ if file_result.format_results:
922
+ fmt_result = list(file_result.format_results.values())[0]
923
+ else:
924
+ fmt_result = FormatResult(format_name=fmt, score=file_result.score)
925
+ file_result.format_results[fmt] = fmt_result
926
+
927
+ # Find existing file result or create new
928
+ existing = None
929
+ for fr in result.file_results:
930
+ if fr.file_path == file_result.file_path:
931
+ existing = fr
932
+ break
933
+
934
+ if existing:
935
+ existing.format_results[fmt] = fmt_result
936
+ else:
937
+ result.file_results.append(file_result)
938
+
939
+ if verbose:
940
+ status = "✓" if file_result.score > 50 else "○"
941
+ print(f" [{i+1}/{len(modules)}] {Path(file_result.file_path).name}: {file_result.score:.1f}% {status}")
846
942
 
847
943
  result.total_time = time.time() - start_time
848
944
 
@@ -157,14 +157,13 @@ class FunctionLogicGenerator:
157
157
  import json
158
158
 
159
159
  schema = {
160
- "$schema": "https://json-schema.org/draft/2020-12/schema",
161
- "title": "Code2Logic Function-Logic TOON Schema",
160
+ "$schema": "http://json-schema.org/draft-07/schema#",
161
+ "title": "Function Logic TOON",
162
+ "type": "object",
162
163
  "description": (
163
- "Schema for function-logic TOON output (e.g. project.functions.toon) — compact function/method index. "
164
- "Conventions: name containing '.' = method (Class.method), "
165
- "~prefix = async, 'cc:N' suffix = cyclomatic complexity (only when >1)."
164
+ "Schema for function-logic TOON output (e.g. function.toon) — compact function/method index. "
165
+ "Generated by code2logic with --function-logic and TOON format."
166
166
  ),
167
- "type": "object",
168
167
  "properties": {
169
168
  "project": {"type": "string", "description": "Project name"},
170
169
  "generated": {"type": "string", "description": "ISO timestamp"},
@@ -2212,30 +2212,30 @@ class UniversalParser:
2212
2212
  for m in re.finditer(r'^import\s+([^;]+);', content, re.MULTILINE):
2213
2213
  imports.append(m.group(1).strip())
2214
2214
 
2215
- for m in re.finditer(r'^(?:public\s+)?(?:abstract\s+)?class\s+(\w+)', content, re.MULTILINE):
2215
+ for m in re.finditer(r'^\s*(?:public\s+)?(?:abstract\s+)?class\s+(\w+)', content, re.MULTILINE):
2216
2216
  name = m.group(1)
2217
2217
  classes.append(ClassInfo(name=name, is_abstract='abstract' in m.group(0)))
2218
2218
  exports.append(name)
2219
2219
 
2220
- for m in re.finditer(r'^(?:public\s+)?interface\s+(\w+)', content, re.MULTILINE):
2220
+ for m in re.finditer(r'^\s*(?:public\s+)?interface\s+(\w+)', content, re.MULTILINE):
2221
2221
  name = m.group(1)
2222
2222
  classes.append(ClassInfo(name=name, is_interface=True))
2223
2223
  exports.append(name)
2224
2224
  types.append(TypeInfo(name=name, kind='interface', definition=''))
2225
2225
 
2226
- for m in re.finditer(r'^(?:public\s+)?enum\s+(\w+)', content, re.MULTILINE):
2226
+ for m in re.finditer(r'^\s*(?:public\s+)?enum\s+(\w+)', content, re.MULTILINE):
2227
2227
  name = m.group(1)
2228
2228
  types.append(TypeInfo(name=name, kind='enum', definition=''))
2229
2229
  exports.append(name)
2230
2230
 
2231
- for m in re.finditer(r'^(?:public\s+)?record\s+(\w+)\s*\(', content, re.MULTILINE):
2231
+ for m in re.finditer(r'^\s*(?:public\s+)?record\s+(\w+)\s*\(', content, re.MULTILINE):
2232
2232
  name = m.group(1)
2233
2233
  classes.append(ClassInfo(name=name))
2234
2234
  types.append(TypeInfo(name=name, kind='record', definition=''))
2235
2235
  exports.append(name)
2236
2236
 
2237
- # Very rough method detection (only top-level class members are not tracked here)
2238
- for m in re.finditer(r'^(?:public|protected|private)\s+(?:static\s+)?([\w<>\[\]]+)\s+(\w+)\s*\(([^)]*)\)\s*\{', content, re.MULTILINE):
2237
+ # Method detection allow indented declarations inside classes
2238
+ for m in re.finditer(r'^\s*(?:@\w+\s+)*(?:public|protected|private)\s+(?:static\s+)?(?:final\s+)?(?:synchronized\s+)?([\w<>\[\],\s]+?)\s+(\w+)\s*\(([^)]*)\)\s*(?:throws\s+[\w,\s]+)?\s*\{', content, re.MULTILINE):
2239
2239
  ret_type = m.group(1)
2240
2240
  name = m.group(2)
2241
2241
  params = [p.strip() for p in (m.group(3) or '').split(',') if p.strip()][:8]
@@ -2260,7 +2260,7 @@ class UniversalParser:
2260
2260
  is_private=False,
2261
2261
  ))
2262
2262
 
2263
- for m in re.finditer(r'^(?:public\s+)?static\s+final\s+[\w<>\[\]]+\s+([A-Z][A-Z0-9_]*)\b', content, re.MULTILINE):
2263
+ for m in re.finditer(r'^\s*(?:public\s+)?static\s+final\s+[\w<>\[\]]+\s+([A-Z][A-Z0-9_]*)\b', content, re.MULTILINE):
2264
2264
  constants.append(m.group(1))
2265
2265
 
2266
2266
  lines = content.split('\n')
@@ -2290,24 +2290,24 @@ class UniversalParser:
2290
2290
  for m in re.finditer(r'^using\s+([^;]+);', content, re.MULTILINE):
2291
2291
  imports.append(m.group(1).strip())
2292
2292
 
2293
- for m in re.finditer(r'^(?:public\s+)?interface\s+(I\w+)', content, re.MULTILINE):
2293
+ for m in re.finditer(r'^\s*(?:public\s+)?interface\s+(I\w+)', content, re.MULTILINE):
2294
2294
  name = m.group(1)
2295
2295
  classes.append(ClassInfo(name=name, is_interface=True))
2296
2296
  exports.append(name)
2297
2297
  types.append(TypeInfo(name=name, kind='interface', definition=''))
2298
2298
 
2299
- for m in re.finditer(r'^(?:public\s+)?(?:abstract\s+)?class\s+(\w+)', content, re.MULTILINE):
2299
+ for m in re.finditer(r'^\s*(?:public\s+)?(?:abstract\s+)?class\s+(\w+)', content, re.MULTILINE):
2300
2300
  name = m.group(1)
2301
2301
  classes.append(ClassInfo(name=name, is_abstract='abstract' in m.group(0)))
2302
2302
  exports.append(name)
2303
2303
 
2304
- for m in re.finditer(r'^(?:public\s+)?record\s+(\w+)', content, re.MULTILINE):
2304
+ for m in re.finditer(r'^\s*(?:public\s+)?record\s+(\w+)', content, re.MULTILINE):
2305
2305
  name = m.group(1)
2306
2306
  classes.append(ClassInfo(name=name))
2307
2307
  exports.append(name)
2308
2308
  types.append(TypeInfo(name=name, kind='record', definition=''))
2309
2309
 
2310
- for m in re.finditer(r'^(?:public|private|protected|internal)\s+(?:static\s+)?([\w<>\[\]?]+)\s+(\w+)\s*\(([^)]*)\)\s*\{', content, re.MULTILINE):
2310
+ for m in re.finditer(r'^\s*(?:public|private|protected|internal)\s+(?:static\s+)?(?:async\s+)?(?:override\s+)?(?:virtual\s+)?([\w<>\[\]?]+)\s+(\w+)\s*\(([^)]*)\)\s*\{', content, re.MULTILINE):
2311
2311
  ret_type = m.group(1)
2312
2312
  name = m.group(2)
2313
2313
  params = [p.strip() for p in (m.group(3) or '').split(',') if p.strip()][:8]
@@ -2332,7 +2332,7 @@ class UniversalParser:
2332
2332
  is_private=False,
2333
2333
  ))
2334
2334
 
2335
- for m in re.finditer(r'^(?:public\s+)?const\s+[\w<>\[\]]+\s+([A-Z][A-Z0-9_]*)\b', content, re.MULTILINE):
2335
+ for m in re.finditer(r'^\s*(?:public\s+)?const\s+[\w<>\[\]]+\s+([A-Z][A-Z0-9_]*)\b', content, re.MULTILINE):
2336
2336
  constants.append(m.group(1))
2337
2337
 
2338
2338
  lines = content.split('\n')
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "code2logic"
7
- version = "1.0.39"
7
+ version = "1.0.41"
8
8
  description = "Code2Logic - Source code to logical representation converter for LLM analysis, featuring Tree-sitter parsing, dependency graph analysis, and multi-language support."
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -1,44 +0,0 @@
1
- from .llm_clients import (
2
- DEFAULT_MODELS,
3
- DEFAULT_PROVIDER_PRIORITIES,
4
- RECOMMENDED_MODELS,
5
- BaseLLMClient,
6
- LiteLLMClient,
7
- LLMConfig,
8
- LLMManager,
9
- OllamaLocalClient,
10
- OpenRouterClient,
11
- get_client,
12
- get_provider_model,
13
- list_available_providers,
14
- )
15
-
16
-
17
- def get_provider_priorities_from_litellm() -> dict[str, int]:
18
- try:
19
- from lolm import get_provider_priorities_from_litellm as _get
20
- except ImportError:
21
- return {}
22
-
23
- try:
24
- raw = _get() or {}
25
- return {str(k): int(v) for k, v in raw.items()}
26
- except Exception:
27
- return {}
28
-
29
- __all__ = [
30
- 'DEFAULT_MODELS',
31
- 'DEFAULT_PROVIDER_PRIORITIES',
32
- 'RECOMMENDED_MODELS',
33
- 'BaseLLMClient',
34
- 'LiteLLMClient',
35
- 'LLMConfig',
36
- 'LLMManager',
37
- 'OpenRouterClient',
38
- 'OllamaLocalClient',
39
- 'get_client',
40
- 'get_provider_model',
41
- 'get_provider_priorities_from_litellm',
42
- 'list_available_providers',
43
- ]
44
-
File without changes