Skip to content

Commit 66fdf3d

Browse files
refactor(goal): CLI interface improvements
changes: - file: cli.py area: cli modified: [main] - file: function_logic.py area: core added: [generate_toon_schema] modified: [generate_toon, FunctionLogicGenerator] dependencies: flow: "cli→function_logic" - cli.py -> function_logic.py stats: lines: "+3560/-3516 (net +44)" files: 3 complexity: "Large structural change (normalized)"
1 parent 1a3b7f4 commit 66fdf3d

11 files changed

Lines changed: 3580 additions & 3523 deletions

File tree

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
## [1.0.37] - 2026-02-25
2+
3+
### Summary
4+
5+
refactor(goal): CLI interface improvements
6+
7+
### Other
8+
9+
- update code2logic/cli.py
10+
- update code2logic/function_logic.py
11+
- update project.functions.toon
12+
13+
114
## [1.0.36] - 2026-02-25
215

316
### Summary

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.0.36
1+
1.0.37

code2logic/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
>>> print(output)
1919
"""
2020

21-
__version__ = "1.0.36"
21+
__version__ = "1.0.37"
2222
__author__ = "Softreck"
2323
__email__ = "info@softreck.dev"
2424
__license__ = "MIT"

code2logic/cli.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,17 +1042,31 @@ def _maybe_print_pretty_help() -> bool:
10421042
else:
10431043
logic_out = logic_gen.generate(project, detail=args.detail)
10441044

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+
10451050
if use_stdout:
10461051
# Write to stdout with section marker
10471052
print(f"\n=== FUNCTION_LOGIC ===")
10481053
print(logic_out)
1054+
if func_schema:
1055+
print(f"\n=== FUNCTION_LOGIC_SCHEMA ===")
1056+
print(func_schema)
10491057
elif output_dir:
10501058
# Write to file in output directory
10511059
os.makedirs(output_dir, exist_ok=True)
10521060
with open(logic_path, 'w', encoding='utf-8') as f:
10531061
f.write(logic_out)
10541062
if args.verbose:
10551063
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}")
10561070

10571071
gen_time = time.time() - gen_start
10581072

code2logic/function_logic.py

Lines changed: 91 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -72,16 +72,24 @@ def generate_toon(
7272
delim = toon.delimiter
7373
dm = toon.delim_marker
7474

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+
7580
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+
7686
lines.append(f"project: {toon._quote(project.name)}")
7787
if getattr(project, 'generated_at', None):
7888
lines.append(f"generated: {toon._quote(project.generated_at)}")
7989

80-
modules = list(project.modules or [])
81-
lines.append(f"modules[{len(modules)}]{{path{dm}lang{dm}items}}:")
90+
lines.append(f"modules[{len(modules_with_items)}]{{path{dm}lang{dm}items}}:")
8291
prev_dir: str | None = None
83-
for m in modules:
84-
items = self._module_items(m)
92+
for m, items in modules_with_items:
8593
if no_repeat_name:
8694
compressed_path, prev_dir = toon._compress_module_path(m.path, prev_dir)
8795
path_out = compressed_path
@@ -93,19 +101,15 @@ def generate_toon(
93101
lines.append("function_details:")
94102

95103
prev_dir = None
96-
for m in modules:
97-
items = self._module_items(m)
98-
if not items:
99-
continue
100-
104+
for m, items in modules_with_items:
101105
if no_repeat_details:
102106
compressed_path, prev_dir = toon._compress_module_path(m.path, prev_dir)
103107
details_key = compressed_path
104108
else:
105109
details_key = m.path
106110
lines.append(f" {toon._quote(details_key)}:")
107111

108-
header = f"line{dm}name{dm}kind{dm}sig{dm}async{dm}cc"
112+
header = f"line{dm}name{dm}sig"
109113
if detail in ('standard', 'full'):
110114
header += f"{dm}does"
111115
if detail == 'full':
@@ -115,15 +119,20 @@ def generate_toon(
115119

116120
for kind, qname, func in items:
117121
sig = self._build_sig(func, include_async_prefix=False, language=m.language)
118-
is_async = 'true' if getattr(func, 'is_async', False) else 'false'
119122
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+
120132
row = [
121133
start_line,
122-
toon._quote(qname),
123-
toon._quote(kind),
134+
toon._quote(display_name),
124135
toon._quote(sig),
125-
is_async,
126-
str(getattr(func, 'complexity', 1) or 1),
127136
]
128137

129138
if detail in ('standard', 'full'):
@@ -142,6 +151,73 @@ def generate_toon(
142151

143152
return "\n".join(lines).rstrip() + "\n"
144153

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+
145221
def _build_data(self, project: ProjectInfo, detail: str) -> dict:
146222
modules_data = []
147223
for m in project.modules or []:

logic2code/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@
1414
from .generator import CodeGenerator, GeneratorConfig, GenerationResult
1515
from .renderers import PythonRenderer
1616

17-
__version__ = '1.0.36'
17+
__version__ = '1.0.37'
1818
__all__ = ['CodeGenerator', 'GeneratorConfig', 'GenerationResult', 'PythonRenderer']

logic2test/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@
1515
from .parsers import LogicParser
1616
from .templates import TestTemplate
1717

18-
__version__ = '1.0.36'
18+
__version__ = '1.0.37'
1919
__all__ = ['TestGenerator', 'GeneratorConfig', 'GenerationResult', 'LogicParser', 'TestTemplate']

lolm/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
)
7777
from .clients import LLMRateLimitError
7878

79-
__version__ = '1.0.36'
79+
__version__ = '1.0.37'
8080
__all__ = [
8181
# Config
8282
'LLMConfig',

0 commit comments

Comments
 (0)