code2logic 1.0.35__tar.gz → 1.0.37__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 (59) hide show
  1. {code2logic-1.0.35 → code2logic-1.0.37}/PKG-INFO +1 -1
  2. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/__init__.py +1 -1
  3. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/cli.py +42 -2
  4. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/function_logic.py +114 -21
  5. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/toon_format.py +64 -6
  6. {code2logic-1.0.35 → code2logic-1.0.37}/pyproject.toml +1 -1
  7. {code2logic-1.0.35 → code2logic-1.0.37}/LICENSE +0 -0
  8. {code2logic-1.0.35 → code2logic-1.0.37}/README.md +0 -0
  9. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/__main__.py +0 -0
  10. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/adaptive.py +0 -0
  11. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/analyzer.py +0 -0
  12. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/base.py +0 -0
  13. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/benchmark.py +0 -0
  14. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/benchmarks/__init__.py +0 -0
  15. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/benchmarks/common.py +0 -0
  16. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/benchmarks/results.py +0 -0
  17. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/benchmarks/runner.py +0 -0
  18. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/chunked_reproduction.py +0 -0
  19. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/code_review.py +0 -0
  20. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/config.py +0 -0
  21. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/core/__init__.py +0 -0
  22. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/dependency.py +0 -0
  23. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/errors.py +0 -0
  24. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/file_formats.py +0 -0
  25. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/formats/__init__.py +0 -0
  26. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/generators.py +0 -0
  27. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/gherkin.py +0 -0
  28. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/integrations/__init__.py +0 -0
  29. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/intent.py +0 -0
  30. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/llm/__init__.py +0 -0
  31. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/llm.py +0 -0
  32. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/llm_clients.py +0 -0
  33. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/llm_clients_new.py +0 -0
  34. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/llm_profiler.py +0 -0
  35. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/logicml.py +0 -0
  36. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/markdown_format.py +0 -0
  37. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/mcp_server.py +0 -0
  38. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/metrics.py +0 -0
  39. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/models.py +0 -0
  40. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/parsers.py +0 -0
  41. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/project_comparison.md +0 -0
  42. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/project_reproducer.py +0 -0
  43. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/prompts.py +0 -0
  44. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/py.typed +0 -0
  45. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/quality.py +0 -0
  46. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/refactor.py +0 -0
  47. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/reproducer.py +0 -0
  48. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/reproduction.py +0 -0
  49. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/schemas/__init__.py +0 -0
  50. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/schemas/json_schema.py +0 -0
  51. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/schemas/logicml_schema.py +0 -0
  52. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/schemas/markdown_schema.py +0 -0
  53. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/schemas/yaml_schema.py +0 -0
  54. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/shared_utils.py +0 -0
  55. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/similarity.py +0 -0
  56. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/terminal.py +0 -0
  57. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/tools/__init__.py +0 -0
  58. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/universal.py +0 -0
  59. {code2logic-1.0.35 → code2logic-1.0.37}/code2logic/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code2logic
3
- Version: 1.0.35
3
+ Version: 1.0.37
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
@@ -18,7 +18,7 @@ Example:
18
18
  >>> print(output)
19
19
  """
20
20
 
21
- __version__ = "1.0.35"
21
+ __version__ = "1.0.37"
22
22
  __author__ = "Softreck"
23
23
  __email__ = "info@softreck.dev"
24
24
  __license__ = "MIT"
@@ -651,6 +651,23 @@ code2logic [path] [options]
651
651
  action='store_true',
652
652
  help='Write all output to stdout instead of files (including schema and function-logic). Useful for piping.'
653
653
  )
654
+ parser.add_argument(
655
+ '--no-repeat-module',
656
+ action='store_true',
657
+ dest='no_repeat_module',
658
+ help='Reduce repeated directory prefixes in TOON outputs by using ./file for consecutive entries in the same folder (applies to function-logic TOON and TOON module lists).'
659
+ )
660
+ parser.add_argument(
661
+ '--no-repeat-name',
662
+ action='store_true',
663
+ dest='no_repeat_module',
664
+ help=argparse.SUPPRESS
665
+ )
666
+ parser.add_argument(
667
+ '--no-repeat-details',
668
+ action='store_true',
669
+ help='Reduce repeated directory prefixes in function-logic TOON section function_details by using ./file for consecutive entries in the same folder.'
670
+ )
654
671
  parser.add_argument(
655
672
  '--no-install',
656
673
  action='store_true',
@@ -961,7 +978,11 @@ code2logic [path] [options]
961
978
  'standard': 'standard',
962
979
  'full': 'full',
963
980
  }
964
- output = generator.generate(project, detail=detail_map.get(args.detail, 'standard'))
981
+ output = generator.generate(
982
+ project,
983
+ detail=detail_map.get(args.detail, 'standard'),
984
+ no_repeat_name=args.no_repeat_module,
985
+ )
965
986
 
966
987
  # Generate schema if requested
967
988
  if args.with_schema:
@@ -1012,14 +1033,27 @@ code2logic [path] [options]
1012
1033
  elif lower.endswith(('.yaml', '.yml')):
1013
1034
  logic_out = logic_gen.generate_yaml(project, detail=args.detail)
1014
1035
  elif lower.endswith('.toon'):
1015
- logic_out = logic_gen.generate_toon(project, detail=args.detail)
1036
+ logic_out = logic_gen.generate_toon(
1037
+ project,
1038
+ detail=args.detail,
1039
+ no_repeat_name=args.no_repeat_module,
1040
+ no_repeat_details=args.no_repeat_details,
1041
+ )
1016
1042
  else:
1017
1043
  logic_out = logic_gen.generate(project, detail=args.detail)
1018
1044
 
1045
+ # Generate function-logic schema if requested and format is TOON
1046
+ func_schema = None
1047
+ if args.with_schema and lower.endswith('.toon'):
1048
+ func_schema = logic_gen.generate_toon_schema()
1049
+
1019
1050
  if use_stdout:
1020
1051
  # Write to stdout with section marker
1021
1052
  print(f"\n=== FUNCTION_LOGIC ===")
1022
1053
  print(logic_out)
1054
+ if func_schema:
1055
+ print(f"\n=== FUNCTION_LOGIC_SCHEMA ===")
1056
+ print(func_schema)
1023
1057
  elif output_dir:
1024
1058
  # Write to file in output directory
1025
1059
  os.makedirs(output_dir, exist_ok=True)
@@ -1027,6 +1061,12 @@ code2logic [path] [options]
1027
1061
  f.write(logic_out)
1028
1062
  if args.verbose:
1029
1063
  log.success(f"Function logic written to: {logic_path}")
1064
+ if func_schema:
1065
+ schema_path = logic_path.replace('.toon', '-schema.json')
1066
+ with open(schema_path, 'w', encoding='utf-8') as f:
1067
+ f.write(func_schema)
1068
+ if args.verbose:
1069
+ log.success(f"Function logic schema written to: {schema_path}")
1030
1070
 
1031
1071
  gen_time = time.time() - gen_start
1032
1072
 
@@ -59,35 +59,57 @@ class FunctionLogicGenerator:
59
59
  return self.generate(project, detail)
60
60
  return yaml.dump(data, default_flow_style=False, allow_unicode=True, sort_keys=False, width=120)
61
61
 
62
- def generate_toon(self, project: ProjectInfo, detail: str = 'full') -> str:
62
+ def generate_toon(
63
+ self,
64
+ project: ProjectInfo,
65
+ detail: str = 'full',
66
+ no_repeat_name: bool = False,
67
+ no_repeat_details: bool = False,
68
+ ) -> str:
63
69
  if detail == 'detailed':
64
70
  detail = 'full'
65
71
  toon = TOONGenerator()
66
72
  delim = toon.delimiter
67
73
  dm = toon.delim_marker
68
74
 
75
+ # Pre-filter: only modules with at least one function/method
76
+ all_modules = list(project.modules or [])
77
+ modules_with_items = [(m, self._module_items(m)) for m in all_modules]
78
+ modules_with_items = [(m, items) for m, items in modules_with_items if items]
79
+
69
80
  lines: List[str] = []
81
+
82
+ # Format header — helps LLM understand the structure
83
+ lines.append(f"# {project.name} function-logic | {len(modules_with_items)} modules")
84
+ lines.append("# Convention: name with . = method, ~name = async, cc:N shown only when >1")
85
+
70
86
  lines.append(f"project: {toon._quote(project.name)}")
71
87
  if getattr(project, 'generated_at', None):
72
88
  lines.append(f"generated: {toon._quote(project.generated_at)}")
73
89
 
74
- modules = list(project.modules or [])
75
- lines.append(f"modules[{len(modules)}]{{path{dm}lang{dm}items}}:")
76
- for m in modules:
77
- items = self._module_items(m)
78
- lines.append(f" {toon._quote(m.path)}{delim}{m.language}{delim}{len(items)}")
90
+ lines.append(f"modules[{len(modules_with_items)}]{{path{dm}lang{dm}items}}:")
91
+ prev_dir: str | None = None
92
+ for m, items in modules_with_items:
93
+ if no_repeat_name:
94
+ compressed_path, prev_dir = toon._compress_module_path(m.path, prev_dir)
95
+ path_out = compressed_path
96
+ else:
97
+ path_out = m.path
98
+ lines.append(f" {toon._quote(path_out)}{delim}{toon._short_lang(m.language)}{delim}{len(items)}")
79
99
 
80
100
  lines.append("")
81
101
  lines.append("function_details:")
82
102
 
83
- for m in modules:
84
- items = self._module_items(m)
85
- if not items:
86
- continue
103
+ prev_dir = None
104
+ for m, items in modules_with_items:
105
+ if no_repeat_details:
106
+ compressed_path, prev_dir = toon._compress_module_path(m.path, prev_dir)
107
+ details_key = compressed_path
108
+ else:
109
+ details_key = m.path
110
+ lines.append(f" {toon._quote(details_key)}:")
87
111
 
88
- lines.append(f" {toon._quote(m.path)}:")
89
-
90
- header = f"name{dm}kind{dm}sig{dm}loc{dm}async{dm}lines{dm}cc"
112
+ header = f"line{dm}name{dm}sig"
91
113
  if detail in ('standard', 'full'):
92
114
  header += f"{dm}does"
93
115
  if detail == 'full':
@@ -97,16 +119,20 @@ class FunctionLogicGenerator:
97
119
 
98
120
  for kind, qname, func in items:
99
121
  sig = self._build_sig(func, include_async_prefix=False, language=m.language)
100
- loc = self._build_loc(func)
101
- is_async = 'true' if getattr(func, 'is_async', False) else 'false'
122
+ start_line = str(getattr(func, 'start_line', 0) or 0)
123
+
124
+ # Encode async as ~ prefix, cc as suffix (only when >1)
125
+ display_name = qname
126
+ if getattr(func, 'is_async', False):
127
+ display_name = f"~{qname}"
128
+ cc = getattr(func, 'complexity', 1) or 1
129
+ if cc > 1:
130
+ display_name = f"{display_name} cc:{cc}"
131
+
102
132
  row = [
103
- toon._quote(qname),
104
- toon._quote(kind),
133
+ start_line,
134
+ toon._quote(display_name),
105
135
  toon._quote(sig),
106
- toon._quote(loc),
107
- is_async,
108
- str(getattr(func, 'lines', 0) or 0),
109
- str(getattr(func, 'complexity', 1) or 1),
110
136
  ]
111
137
 
112
138
  if detail in ('standard', 'full'):
@@ -125,6 +151,73 @@ class FunctionLogicGenerator:
125
151
 
126
152
  return "\n".join(lines).rstrip() + "\n"
127
153
 
154
+ def generate_toon_schema(self) -> str:
155
+ """Generate JSON Schema describing the function-logic TOON format."""
156
+ import json
157
+
158
+ schema = {
159
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
160
+ "title": "Code2Logic Function-Logic TOON Schema",
161
+ "description": (
162
+ "Schema for project.functions.toon — compact function/method index. "
163
+ "Conventions: name containing '.' = method (Class.method), "
164
+ "~prefix = async, 'cc:N' suffix = cyclomatic complexity (only when >1)."
165
+ ),
166
+ "type": "object",
167
+ "properties": {
168
+ "project": {"type": "string", "description": "Project name"},
169
+ "generated": {"type": "string", "description": "ISO timestamp"},
170
+ "modules": {
171
+ "type": "array",
172
+ "description": "Modules with at least one function/method. Rows: path,lang,items. Use ./file for same-dir compression (--no-repeat-module).",
173
+ "items": {
174
+ "type": "object",
175
+ "properties": {
176
+ "path": {"type": "string", "description": "Relative path or ./basename if same dir as previous"},
177
+ "lang": {"type": "string", "description": "Short language code (py, js, ts, ...)"},
178
+ "items": {"type": "integer", "description": "Number of functions+methods in module"}
179
+ }
180
+ }
181
+ },
182
+ "function_details": {
183
+ "type": "object",
184
+ "description": "Per-module function tables. Keys are module paths (or ./basename with --no-repeat-details).",
185
+ "patternProperties": {
186
+ ".*": {
187
+ "type": "object",
188
+ "properties": {
189
+ "functions": {
190
+ "type": "array",
191
+ "description": "Tabular rows: line,name,sig[,does][,decorators,calls,raises]",
192
+ "items": {
193
+ "type": "object",
194
+ "properties": {
195
+ "line": {"type": "integer", "description": "Start line number"},
196
+ "name": {
197
+ "type": "string",
198
+ "description": (
199
+ "Function or method name. "
200
+ "Contains '.' if method (e.g. Class.method). "
201
+ "Prefixed with ~ if async. "
202
+ "Suffixed with ' cc:N' if cyclomatic complexity > 1."
203
+ )
204
+ },
205
+ "sig": {"type": "string", "description": "Signature: (params) [-> return_type]"},
206
+ "does": {"type": "string", "description": "Intent/purpose (standard+full detail)"},
207
+ "decorators": {"type": "string", "description": "Pipe-separated decorators (full detail)"},
208
+ "calls": {"type": "string", "description": "Pipe-separated function calls (full detail)"},
209
+ "raises": {"type": "string", "description": "Pipe-separated exceptions (full detail)"}
210
+ }
211
+ }
212
+ }
213
+ }
214
+ }
215
+ }
216
+ }
217
+ }
218
+ }
219
+ return json.dumps(schema, indent=2, ensure_ascii=False)
220
+
128
221
  def _build_data(self, project: ProjectInfo, detail: str) -> dict:
129
222
  modules_data = []
130
223
  for m in project.modules or []:
@@ -33,6 +33,37 @@ class TOONGenerator:
33
33
  SPECIAL_CHARS = re.compile(r'[:\"\\\[\]\{\}\n\t\r,]')
34
34
  LOOKS_LIKE_LITERAL = re.compile(r'^(true|false|null|-?\d+\.?\d*([eE][+-]?\d+)?|-)$')
35
35
 
36
+ LANGUAGE_ABBREVIATIONS = {
37
+ 'python': 'py',
38
+ 'javascript': 'js',
39
+ 'typescript': 'ts',
40
+ 'tsx': 'tsx',
41
+ 'jsx': 'jsx',
42
+ 'java': 'java',
43
+ 'kotlin': 'kt',
44
+ 'go': 'go',
45
+ 'rust': 'rs',
46
+ 'c': 'c',
47
+ 'cpp': 'cpp',
48
+ 'c++': 'cpp',
49
+ 'csharp': 'cs',
50
+ 'c#': 'cs',
51
+ 'php': 'php',
52
+ 'ruby': 'rb',
53
+ 'swift': 'swift',
54
+ 'scala': 'scala',
55
+ 'bash': 'sh',
56
+ 'shell': 'sh',
57
+ 'sql': 'sql',
58
+ 'yaml': 'yaml',
59
+ 'json': 'json',
60
+ 'toml': 'toml',
61
+ 'markdown': 'md',
62
+ 'html': 'html',
63
+ 'css': 'css',
64
+ 'dockerfile': 'docker',
65
+ }
66
+
36
67
  def __init__(self, delimiter: str = ',', use_tabs: bool = False):
37
68
  """
38
69
  Initialize TOON generator.
@@ -46,7 +77,27 @@ class TOONGenerator:
46
77
  # parsers/LLMs can read them. Use comma-separated headers regardless of row delimiter.
47
78
  self.delim_marker = ','
48
79
 
49
- def generate(self, project: ProjectInfo, detail: str = 'standard') -> str:
80
+ def _short_lang(self, lang: str) -> str:
81
+ lang_norm = (lang or '').strip().lower()
82
+ return self.LANGUAGE_ABBREVIATIONS.get(lang_norm, lang)
83
+
84
+ def _compress_module_path(self, path: str, prev_dir: str | None) -> tuple[str, str]:
85
+ """Compress repeated directory prefixes for module summary tables.
86
+
87
+ If consecutive modules are in the same directory, emit './<basename>' instead
88
+ of repeating '<dir>/<basename>'. The first entry for a directory stays as the
89
+ full path.
90
+ """
91
+ p = (path or '')
92
+ if '/' not in p:
93
+ return p, ''
94
+
95
+ cur_dir, base = p.rsplit('/', 1)
96
+ if prev_dir is not None and cur_dir == prev_dir:
97
+ return f"./{base}", cur_dir
98
+ return p, cur_dir
99
+
100
+ def generate(self, project: ProjectInfo, detail: str = 'standard', no_repeat_name: bool = False) -> str:
50
101
  """
51
102
  Generate TOON format from ProjectInfo.
52
103
 
@@ -71,26 +122,33 @@ class TOONGenerator:
71
122
 
72
123
  # Languages as primitive array
73
124
  if project.languages:
74
- lang_items = [f"{k}:{v}" for k, v in project.languages.items()]
125
+ lang_items = [f"{self._short_lang(k)}:{v}" for k, v in project.languages.items()]
75
126
  lines.append(f" languages[{len(lang_items)}]: {self.delimiter.join(lang_items)}")
76
127
 
77
128
  # Modules - tabular format for efficiency
78
129
  if project.modules:
79
130
  lines.append("")
80
- lines.extend(self._generate_modules(project.modules, detail))
131
+ lines.extend(self._generate_modules(project.modules, detail, no_repeat_name=no_repeat_name))
81
132
 
82
133
  return '\n'.join(lines)
83
134
 
84
- def _generate_modules(self, modules: List[ModuleInfo], detail: str) -> List[str]:
135
+ def _generate_modules(self, modules: List[ModuleInfo], detail: str, no_repeat_name: bool = False) -> List[str]:
85
136
  """Generate modules section."""
86
137
  lines = []
87
138
 
88
139
  # Module summary as tabular array
89
140
  lines.append(f"modules[{len(modules)}]{{path{self.delim_marker}lang{self.delim_marker}lines{self.delim_marker}kb}}:")
141
+ prev_dir: str | None = None
90
142
  for m in modules:
91
- path = self._quote(m.path)
143
+ if no_repeat_name:
144
+ path_out, prev_dir = self._compress_module_path(m.path, prev_dir)
145
+ else:
146
+ path_out = m.path
147
+ path = self._quote(path_out)
92
148
  kb = round((getattr(m, 'file_bytes', 0) or 0) / 1024, 1)
93
- lines.append(f" {path}{self.delimiter}{m.language}{self.delimiter}{m.lines_code}{self.delimiter}{kb}")
149
+ lines.append(
150
+ f" {path}{self.delimiter}{self._short_lang(m.language)}{self.delimiter}{m.lines_code}{self.delimiter}{kb}"
151
+ )
94
152
 
95
153
  # Detailed module info
96
154
  if detail in ('standard', 'full'):
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "code2logic"
7
- version = "1.0.35"
7
+ version = "1.0.37"
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"
File without changes
File without changes