code2logic 1.0.34__tar.gz → 1.0.36__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.34 → code2logic-1.0.36}/PKG-INFO +1 -1
  2. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/__init__.py +1 -1
  3. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/cli.py +129 -80
  4. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/function_logic.py +24 -7
  5. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/toon_format.py +64 -6
  6. {code2logic-1.0.34 → code2logic-1.0.36}/pyproject.toml +1 -1
  7. {code2logic-1.0.34 → code2logic-1.0.36}/LICENSE +0 -0
  8. {code2logic-1.0.34 → code2logic-1.0.36}/README.md +0 -0
  9. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/__main__.py +0 -0
  10. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/adaptive.py +0 -0
  11. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/analyzer.py +0 -0
  12. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/base.py +0 -0
  13. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/benchmark.py +0 -0
  14. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/benchmarks/__init__.py +0 -0
  15. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/benchmarks/common.py +0 -0
  16. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/benchmarks/results.py +0 -0
  17. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/benchmarks/runner.py +0 -0
  18. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/chunked_reproduction.py +0 -0
  19. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/code_review.py +0 -0
  20. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/config.py +0 -0
  21. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/core/__init__.py +0 -0
  22. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/dependency.py +0 -0
  23. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/errors.py +0 -0
  24. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/file_formats.py +0 -0
  25. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/formats/__init__.py +0 -0
  26. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/generators.py +0 -0
  27. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/gherkin.py +0 -0
  28. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/integrations/__init__.py +0 -0
  29. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/intent.py +0 -0
  30. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/llm/__init__.py +0 -0
  31. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/llm.py +0 -0
  32. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/llm_clients.py +0 -0
  33. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/llm_clients_new.py +0 -0
  34. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/llm_profiler.py +0 -0
  35. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/logicml.py +0 -0
  36. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/markdown_format.py +0 -0
  37. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/mcp_server.py +0 -0
  38. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/metrics.py +0 -0
  39. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/models.py +0 -0
  40. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/parsers.py +0 -0
  41. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/project_comparison.md +0 -0
  42. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/project_reproducer.py +0 -0
  43. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/prompts.py +0 -0
  44. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/py.typed +0 -0
  45. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/quality.py +0 -0
  46. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/refactor.py +0 -0
  47. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/reproducer.py +0 -0
  48. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/reproduction.py +0 -0
  49. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/schemas/__init__.py +0 -0
  50. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/schemas/json_schema.py +0 -0
  51. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/schemas/logicml_schema.py +0 -0
  52. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/schemas/markdown_schema.py +0 -0
  53. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/schemas/yaml_schema.py +0 -0
  54. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/shared_utils.py +0 -0
  55. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/similarity.py +0 -0
  56. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/terminal.py +0 -0
  57. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/tools/__init__.py +0 -0
  58. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/universal.py +0 -0
  59. {code2logic-1.0.34 → code2logic-1.0.36}/code2logic/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code2logic
3
- Version: 1.0.34
3
+ Version: 1.0.36
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.34"
21
+ __version__ = "1.0.36"
22
22
  __author__ = "Softreck"
23
23
  __email__ = "info@softreck.dev"
24
24
  __license__ = "MIT"
@@ -605,8 +605,9 @@ code2logic [path] [options]
605
605
  help='Detail level - columns to include (default: standard)'
606
606
  )
607
607
  parser.add_argument(
608
- '-o', '--output',
609
- help='Output file path (default: stdout)'
608
+ '-o', '--output-dir',
609
+ dest='output_dir',
610
+ help='Output directory for all generated files. If specified, files are saved instead of stdout. File names are derived from --name and format flags: {name}.{format}, {name}.functions.{ext}, {name}.{format}-schema.json'
610
611
  )
611
612
  parser.add_argument(
612
613
  '--name',
@@ -643,7 +644,29 @@ code2logic [path] [options]
643
644
  parser.add_argument(
644
645
  '--with-schema',
645
646
  action='store_true',
646
- help='Generate JSON schema alongside output'
647
+ help='Generate JSON schema file alongside output (uses project name for filename)'
648
+ )
649
+ parser.add_argument(
650
+ '--stdout',
651
+ action='store_true',
652
+ help='Write all output to stdout instead of files (including schema and function-logic). Useful for piping.'
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.'
647
670
  )
648
671
  parser.add_argument(
649
672
  '--no-install',
@@ -856,23 +879,34 @@ code2logic [path] [options]
856
879
  # Get project name: CLI arg > env var > default
857
880
  project_name = args.project_name if args.project_name else config.get_project_name()
858
881
 
859
- # Determine default output path when using --function-logic or --with-schema without -o
860
- default_output = None
861
- if (args.function_logic or args.with_schema) and not args.output:
862
- # Use project name with appropriate extension
863
- ext_map = {
864
- 'markdown': 'md',
865
- 'compact': 'txt',
866
- 'json': 'json',
867
- 'yaml': 'yaml',
868
- 'hybrid': 'yaml',
869
- 'csv': 'csv',
870
- 'gherkin': 'feature',
871
- 'toon': 'toon',
872
- 'logicml': 'logicml',
873
- }
874
- ext = ext_map.get(args.format, args.format)
875
- default_output = f"{project_name}.{ext}"
882
+ # Determine output mode:
883
+ # - --stdout: all requested output to stdout (with section markers)
884
+ # - -o ./dir with --function-logic or --with-schema: only generate flagged files
885
+ # - -o ./dir without aux flags: generate main file only
886
+ # - no -o: main to stdout (auxiliary files require explicit path)
887
+ use_stdout = args.stdout
888
+ output_dir = args.output_dir
889
+
890
+ # When using output_dir with aux flags, only generate those files (not main)
891
+ has_aux_flags = args.function_logic or args.with_schema
892
+ generate_main = not has_aux_flags or use_stdout
893
+
894
+ # Build output paths based on output_dir
895
+ ext_map = {
896
+ 'markdown': 'md',
897
+ 'compact': 'txt',
898
+ 'json': 'json',
899
+ 'yaml': 'yaml',
900
+ 'hybrid': 'yaml',
901
+ 'csv': 'csv',
902
+ 'gherkin': 'feature',
903
+ 'toon': 'toon',
904
+ 'logicml': 'logicml',
905
+ }
906
+ ext = ext_map.get(args.format, args.format)
907
+ main_output_path = None
908
+ if output_dir and generate_main:
909
+ main_output_path = os.path.join(output_dir, f"{project_name}.{ext}")
876
910
 
877
911
  # Generate output
878
912
  if args.verbose:
@@ -913,16 +947,19 @@ code2logic [path] [options]
913
947
  schema = generator.generate_schema('hybrid')
914
948
  else:
915
949
  schema = generator.generate_schema('compact' if compact else 'full')
916
- effective_output = args.output or default_output
917
- base_name = os.path.splitext(effective_output)[0] if effective_output else project_name
918
- schema_path = f"{base_name}.yaml-schema.json"
919
- parent_dir = os.path.dirname(schema_path)
920
- if parent_dir:
921
- os.makedirs(parent_dir, exist_ok=True)
922
- with open(schema_path, 'w', encoding='utf-8') as f:
923
- f.write(schema)
924
- if args.verbose:
925
- log.success(f"Schema written to: {schema_path}")
950
+
951
+ if use_stdout:
952
+ # Write to stdout with section marker
953
+ print(f"\n=== SCHEMA ===")
954
+ print(schema)
955
+ elif output_dir:
956
+ # Write to file in output directory
957
+ schema_path = os.path.join(output_dir, f"{project_name}.yaml-schema.json")
958
+ os.makedirs(output_dir, exist_ok=True)
959
+ with open(schema_path, 'w', encoding='utf-8') as f:
960
+ f.write(schema)
961
+ if args.verbose:
962
+ log.success(f"Schema written to: {schema_path}")
926
963
 
927
964
  elif args.format == 'toon':
928
965
  generator = TOONGenerator()
@@ -941,22 +978,29 @@ code2logic [path] [options]
941
978
  'standard': 'standard',
942
979
  'full': 'full',
943
980
  }
944
- 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
+ )
945
986
 
946
987
  # Generate schema if requested
947
988
  if args.with_schema:
948
989
  schema_type = 'ultra_compact' if use_ultra_compact else 'standard'
949
990
  schema = generator.generate_schema(schema_type)
950
- effective_output = args.output or default_output
951
- base_name = os.path.splitext(effective_output)[0] if effective_output else project_name
952
- schema_path = f"{base_name}.toon-schema.json"
953
- parent_dir = os.path.dirname(schema_path)
954
- if parent_dir:
955
- os.makedirs(parent_dir, exist_ok=True)
956
- with open(schema_path, 'w', encoding='utf-8') as f:
957
- f.write(schema)
958
- if args.verbose:
959
- log.success(f"Schema written to: {schema_path}")
991
+
992
+ if use_stdout:
993
+ # Write to stdout with section marker
994
+ print(f"\n=== SCHEMA ===")
995
+ print(schema)
996
+ elif output_dir:
997
+ # Write to file in output directory
998
+ schema_path = os.path.join(output_dir, f"{project_name}.toon-schema.json")
999
+ os.makedirs(output_dir, exist_ok=True)
1000
+ with open(schema_path, 'w', encoding='utf-8') as f:
1001
+ f.write(schema)
1002
+ if args.verbose:
1003
+ log.success(f"Schema written to: {schema_path}")
960
1004
 
961
1005
  elif args.format == 'logicml':
962
1006
  generator = LogicMLGenerator()
@@ -970,21 +1014,16 @@ code2logic [path] [options]
970
1014
  if args.function_logic:
971
1015
  logic_gen = FunctionLogicGenerator()
972
1016
 
973
- # Auto-generate path if 'auto' was specified (--function-logic without argument)
1017
+ # Determine path for function logic file
974
1018
  if args.function_logic == 'auto':
975
- effective_output = args.output or default_output
976
- if effective_output:
977
- # Derive from output file: project.c2l.yaml -> project.functions.yaml
978
- base = effective_output.rsplit('.', 1)[0]
979
- if base.endswith('.c2l'):
980
- base = base[:-4]
981
- ext = effective_output.rsplit('.', 1)[-1] if '.' in effective_output else 'logicml'
982
- logic_path = f"{base}.functions.{ext}"
1019
+ if output_dir:
1020
+ # Use output directory with project name
1021
+ logic_ext = ext_map.get(args.format, 'logicml')
1022
+ logic_path = os.path.join(output_dir, f"{project_name}.functions.{logic_ext}")
983
1023
  else:
984
- # Default path based on format using project name from config
985
- ext_map = {'json': 'json', 'yaml': 'yaml', 'toon': 'toon'}
986
- ext = ext_map.get(args.format, 'logicml')
987
- logic_path = f"{project_name}.functions.{ext}"
1024
+ # No output dir - use project name in current directory
1025
+ logic_ext = ext_map.get(args.format, 'logicml')
1026
+ logic_path = f"{project_name}.functions.{logic_ext}"
988
1027
  else:
989
1028
  logic_path = str(args.function_logic)
990
1029
 
@@ -994,17 +1033,26 @@ code2logic [path] [options]
994
1033
  elif lower.endswith(('.yaml', '.yml')):
995
1034
  logic_out = logic_gen.generate_yaml(project, detail=args.detail)
996
1035
  elif lower.endswith('.toon'):
997
- 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
+ )
998
1042
  else:
999
1043
  logic_out = logic_gen.generate(project, detail=args.detail)
1000
1044
 
1001
- parent_dir = os.path.dirname(logic_path)
1002
- if parent_dir:
1003
- os.makedirs(parent_dir, exist_ok=True)
1004
- with open(logic_path, 'w', encoding='utf-8') as f:
1005
- f.write(logic_out)
1006
- if args.verbose:
1007
- log.success(f"Function logic written to: {logic_path}")
1045
+ if use_stdout:
1046
+ # Write to stdout with section marker
1047
+ print(f"\n=== FUNCTION_LOGIC ===")
1048
+ print(logic_out)
1049
+ elif output_dir:
1050
+ # Write to file in output directory
1051
+ os.makedirs(output_dir, exist_ok=True)
1052
+ with open(logic_path, 'w', encoding='utf-8') as f:
1053
+ f.write(logic_out)
1054
+ if args.verbose:
1055
+ log.success(f"Function logic written to: {logic_path}")
1008
1056
 
1009
1057
  gen_time = time.time() - gen_start
1010
1058
 
@@ -1015,25 +1063,26 @@ code2logic [path] [options]
1015
1063
  log.stats("Size", f"{output_size:,} chars (~{tokens_approx:,} tokens)")
1016
1064
  log.stats("Lines", output.count('\n') + 1)
1017
1065
 
1018
- # Write output
1019
- if args.output:
1020
- parent_dir = os.path.dirname(args.output)
1021
- if parent_dir:
1022
- os.makedirs(parent_dir, exist_ok=True)
1023
- with open(args.output, 'w', encoding='utf-8') as f:
1024
- f.write(output)
1025
- if args.verbose:
1026
- log.success(f"Output written to: {args.output}")
1027
- else:
1028
- if not args.quiet:
1029
- try:
1030
- print(output, flush=True)
1031
- except BrokenPipeError:
1066
+ # Write main output (only if generate_main is True)
1067
+ if generate_main:
1068
+ if output_dir:
1069
+ # Write to file in output directory
1070
+ os.makedirs(output_dir, exist_ok=True)
1071
+ with open(main_output_path, 'w', encoding='utf-8') as f:
1072
+ f.write(output)
1073
+ if args.verbose:
1074
+ log.success(f"Output written to: {main_output_path}")
1075
+ else:
1076
+ # Write to stdout
1077
+ if not args.quiet:
1032
1078
  try:
1033
- sys.stdout.close()
1034
- except Exception:
1035
- pass
1036
- os._exit(0)
1079
+ print(output, flush=True)
1080
+ except BrokenPipeError:
1081
+ try:
1082
+ sys.stdout.close()
1083
+ except Exception:
1084
+ pass
1085
+ os._exit(0)
1037
1086
 
1038
1087
  # Final summary
1039
1088
  if args.verbose:
@@ -59,7 +59,13 @@ 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()
@@ -73,21 +79,33 @@ class FunctionLogicGenerator:
73
79
 
74
80
  modules = list(project.modules or [])
75
81
  lines.append(f"modules[{len(modules)}]{{path{dm}lang{dm}items}}:")
82
+ prev_dir: str | None = None
76
83
  for m in modules:
77
84
  items = self._module_items(m)
78
- lines.append(f" {toon._quote(m.path)}{delim}{m.language}{delim}{len(items)}")
85
+ if no_repeat_name:
86
+ compressed_path, prev_dir = toon._compress_module_path(m.path, prev_dir)
87
+ path_out = compressed_path
88
+ else:
89
+ path_out = m.path
90
+ lines.append(f" {toon._quote(path_out)}{delim}{toon._short_lang(m.language)}{delim}{len(items)}")
79
91
 
80
92
  lines.append("")
81
93
  lines.append("function_details:")
82
94
 
95
+ prev_dir = None
83
96
  for m in modules:
84
97
  items = self._module_items(m)
85
98
  if not items:
86
99
  continue
87
100
 
88
- lines.append(f" {toon._quote(m.path)}:")
101
+ if no_repeat_details:
102
+ compressed_path, prev_dir = toon._compress_module_path(m.path, prev_dir)
103
+ details_key = compressed_path
104
+ else:
105
+ details_key = m.path
106
+ lines.append(f" {toon._quote(details_key)}:")
89
107
 
90
- header = f"name{dm}kind{dm}sig{dm}loc{dm}async{dm}lines{dm}cc"
108
+ header = f"line{dm}name{dm}kind{dm}sig{dm}async{dm}cc"
91
109
  if detail in ('standard', 'full'):
92
110
  header += f"{dm}does"
93
111
  if detail == 'full':
@@ -97,15 +115,14 @@ class FunctionLogicGenerator:
97
115
 
98
116
  for kind, qname, func in items:
99
117
  sig = self._build_sig(func, include_async_prefix=False, language=m.language)
100
- loc = self._build_loc(func)
101
118
  is_async = 'true' if getattr(func, 'is_async', False) else 'false'
119
+ start_line = str(getattr(func, 'start_line', 0) or 0)
102
120
  row = [
121
+ start_line,
103
122
  toon._quote(qname),
104
123
  toon._quote(kind),
105
124
  toon._quote(sig),
106
- toon._quote(loc),
107
125
  is_async,
108
- str(getattr(func, 'lines', 0) or 0),
109
126
  str(getattr(func, 'complexity', 1) or 1),
110
127
  ]
111
128
 
@@ -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.34"
7
+ version = "1.0.36"
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