IncludeCPP 3.2.0__tar.gz → 3.2.2__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.
- {includecpp-3.2.0 → includecpp-3.2.2}/IncludeCPP.egg-info/PKG-INFO +10 -2
- {includecpp-3.2.0 → includecpp-3.2.2}/PKG-INFO +10 -2
- {includecpp-3.2.0 → includecpp-3.2.2}/README.md +9 -1
- {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/__init__.py +1 -1
- {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/cli/commands.py +128 -7
- {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/core/ai_integration.py +134 -17
- {includecpp-3.2.0 → includecpp-3.2.2}/pyproject.toml +1 -1
- {includecpp-3.2.0 → includecpp-3.2.2}/IncludeCPP.egg-info/SOURCES.txt +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/IncludeCPP.egg-info/dependency_links.txt +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/IncludeCPP.egg-info/entry_points.txt +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/IncludeCPP.egg-info/requires.txt +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/IncludeCPP.egg-info/top_level.txt +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/LICENSE +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/MANIFEST.in +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/__init__.pyi +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/__main__.py +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/cli/__init__.py +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/cli/config_parser.py +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/core/__init__.py +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/core/build_manager.py +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/core/cpp_api.py +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/core/cpp_api.pyi +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/core/error_catalog.py +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/core/error_formatter.py +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/core/exceptions.py +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/core/path_discovery.py +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/core/settings_ui.py +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/generator/__init__.py +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/generator/parser.cpp +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/generator/parser.h +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/generator/type_resolver.cpp +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/generator/type_resolver.h +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/py.typed +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/templates/cpp.proj.template +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/requirements.txt +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/setup.cfg +0 -0
- {includecpp-3.2.0 → includecpp-3.2.2}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: IncludeCPP
|
|
3
|
-
Version: 3.2.
|
|
3
|
+
Version: 3.2.2
|
|
4
4
|
Summary: Professional C++ Python bindings with type-generic templates, pystubs and native threading
|
|
5
5
|
Home-page: https://github.com/includecpp/includecpp
|
|
6
6
|
Author: IncludeCPP Team
|
|
@@ -470,6 +470,14 @@ Options:
|
|
|
470
470
|
|
|
471
471
|
# Changelog
|
|
472
472
|
|
|
473
|
+
## v3.2.2
|
|
474
|
+
- Plugin command now detects comma-separated field declarations (e.g., `double x, y, z;` generates 3 FIELD entries)
|
|
475
|
+
- Fixed `ai optimize` timeout for multi-file operations (increased to 5 minutes)
|
|
476
|
+
- AI `ask` now extracts relevant CLI implementation when asking about commands/flags
|
|
477
|
+
|
|
478
|
+
## v3.2.1
|
|
479
|
+
- Fixed encoding error in `ai ask` output on Windows (GPT Unicode characters like non-breaking hyphen)
|
|
480
|
+
|
|
473
481
|
## v3.2.0
|
|
474
482
|
- AI context limits: 3K (standard), 5K (`--think`), 10K (`--think2`), 25K (`--think3`) lines
|
|
475
483
|
- AI now has full IncludeCPP knowledge (namespace requirements, .cp format, common errors)
|
|
@@ -508,4 +516,4 @@ Options:
|
|
|
508
516
|
|
|
509
517
|
---
|
|
510
518
|
|
|
511
|
-
MIT License | v3.2.
|
|
519
|
+
MIT License | v3.2.2 | [GitHub](https://github.com/liliassg/IncludeCPP)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: IncludeCPP
|
|
3
|
-
Version: 3.2.
|
|
3
|
+
Version: 3.2.2
|
|
4
4
|
Summary: Professional C++ Python bindings with type-generic templates, pystubs and native threading
|
|
5
5
|
Home-page: https://github.com/includecpp/includecpp
|
|
6
6
|
Author: IncludeCPP Team
|
|
@@ -470,6 +470,14 @@ Options:
|
|
|
470
470
|
|
|
471
471
|
# Changelog
|
|
472
472
|
|
|
473
|
+
## v3.2.2
|
|
474
|
+
- Plugin command now detects comma-separated field declarations (e.g., `double x, y, z;` generates 3 FIELD entries)
|
|
475
|
+
- Fixed `ai optimize` timeout for multi-file operations (increased to 5 minutes)
|
|
476
|
+
- AI `ask` now extracts relevant CLI implementation when asking about commands/flags
|
|
477
|
+
|
|
478
|
+
## v3.2.1
|
|
479
|
+
- Fixed encoding error in `ai ask` output on Windows (GPT Unicode characters like non-breaking hyphen)
|
|
480
|
+
|
|
473
481
|
## v3.2.0
|
|
474
482
|
- AI context limits: 3K (standard), 5K (`--think`), 10K (`--think2`), 25K (`--think3`) lines
|
|
475
483
|
- AI now has full IncludeCPP knowledge (namespace requirements, .cp format, common errors)
|
|
@@ -508,4 +516,4 @@ Options:
|
|
|
508
516
|
|
|
509
517
|
---
|
|
510
518
|
|
|
511
|
-
MIT License | v3.2.
|
|
519
|
+
MIT License | v3.2.2 | [GitHub](https://github.com/liliassg/IncludeCPP)
|
|
@@ -433,6 +433,14 @@ Options:
|
|
|
433
433
|
|
|
434
434
|
# Changelog
|
|
435
435
|
|
|
436
|
+
## v3.2.2
|
|
437
|
+
- Plugin command now detects comma-separated field declarations (e.g., `double x, y, z;` generates 3 FIELD entries)
|
|
438
|
+
- Fixed `ai optimize` timeout for multi-file operations (increased to 5 minutes)
|
|
439
|
+
- AI `ask` now extracts relevant CLI implementation when asking about commands/flags
|
|
440
|
+
|
|
441
|
+
## v3.2.1
|
|
442
|
+
- Fixed encoding error in `ai ask` output on Windows (GPT Unicode characters like non-breaking hyphen)
|
|
443
|
+
|
|
436
444
|
## v3.2.0
|
|
437
445
|
- AI context limits: 3K (standard), 5K (`--think`), 10K (`--think2`), 25K (`--think3`) lines
|
|
438
446
|
- AI now has full IncludeCPP knowledge (namespace requirements, .cp format, common errors)
|
|
@@ -471,4 +479,4 @@ Options:
|
|
|
471
479
|
|
|
472
480
|
---
|
|
473
481
|
|
|
474
|
-
MIT License | v3.2.
|
|
482
|
+
MIT License | v3.2.2 | [GitHub](https://github.com/liliassg/IncludeCPP)
|
|
@@ -50,11 +50,20 @@ def _safe_echo(text, **kwargs):
|
|
|
50
50
|
('┌', '+'), ('┐', '+'), ('└', '+'), ('┘', '+'),
|
|
51
51
|
('═', '-'), ('─', '-'), ('║', '|'), ('│', '|'),
|
|
52
52
|
('✗', '[X]'), ('•', '*'),
|
|
53
|
-
('→', '->'), ('▶', '>'), ('◆', '*')
|
|
53
|
+
('→', '->'), ('▶', '>'), ('◆', '*'),
|
|
54
|
+
('\u2011', '-'), ('\u2010', '-'),
|
|
55
|
+
('\u2013', '-'), ('\u2014', '--'),
|
|
56
|
+
('\u2018', "'"), ('\u2019', "'"),
|
|
57
|
+
('\u201c', '"'), ('\u201d', '"'),
|
|
58
|
+
('\u2022', '*'), ('\u2026', '...'),
|
|
59
|
+
('\u00a0', ' '),
|
|
54
60
|
]
|
|
55
61
|
for uni, ascii_char in replacements:
|
|
56
62
|
ascii_text = ascii_text.replace(uni, ascii_char)
|
|
57
|
-
|
|
63
|
+
try:
|
|
64
|
+
click.secho(ascii_text, **kwargs)
|
|
65
|
+
except UnicodeEncodeError:
|
|
66
|
+
click.secho(ascii_text.encode('ascii', errors='replace').decode('ascii'), **kwargs)
|
|
58
67
|
|
|
59
68
|
def _render_readme_with_colors(readme_text):
|
|
60
69
|
"""Render README.md with color highlighting in terminal."""
|
|
@@ -2515,6 +2524,107 @@ def plugin(plugin_name, files, private):
|
|
|
2515
2524
|
|
|
2516
2525
|
return methods, constructor_signatures, method_signatures
|
|
2517
2526
|
|
|
2527
|
+
def extract_fields(public_section, class_name):
|
|
2528
|
+
"""Extract member variable declarations from public section.
|
|
2529
|
+
|
|
2530
|
+
v3.2.2: Handles comma-separated field declarations like:
|
|
2531
|
+
double x, y, z; -> [('double', 'x'), ('double', 'y'), ('double', 'z')]
|
|
2532
|
+
"""
|
|
2533
|
+
fields = []
|
|
2534
|
+
|
|
2535
|
+
# Pattern to match field declarations
|
|
2536
|
+
# Matches: type name; OR type name1, name2, name3;
|
|
2537
|
+
# Handles: int x; double x, y, z; std::vector<int> items; const float PI = 3.14;
|
|
2538
|
+
field_pattern = re.compile(
|
|
2539
|
+
r'^\s*' # Start of line, optional whitespace
|
|
2540
|
+
r'(?:static\s+)?' # Optional static
|
|
2541
|
+
r'(const\s+)?' # Optional const (group 1)
|
|
2542
|
+
r'([a-zA-Z_][\w:]*(?:<[^<>]*(?:<[^<>]*>[^<>]*)*>)?)' # Type (group 2) - handles nested templates
|
|
2543
|
+
r'(\s*[&*])?' # Optional reference/pointer (group 3)
|
|
2544
|
+
r'\s+' # Required whitespace
|
|
2545
|
+
r'([^;(=]+)' # Names part - everything until ; ( or = (group 4)
|
|
2546
|
+
r'\s*(?:=\s*[^;]+)?' # Optional initializer
|
|
2547
|
+
r'\s*;', # Semicolon
|
|
2548
|
+
re.MULTILINE
|
|
2549
|
+
)
|
|
2550
|
+
|
|
2551
|
+
cpp_keywords = {
|
|
2552
|
+
'return', 'new', 'delete', 'throw', 'if', 'else', 'for', 'while', 'switch',
|
|
2553
|
+
'case', 'break', 'continue', 'class', 'struct', 'enum', 'typedef', 'using',
|
|
2554
|
+
'namespace', 'template', 'typename', 'virtual', 'override', 'final', 'public',
|
|
2555
|
+
'private', 'protected', 'friend', 'operator', 'sizeof', 'alignof', 'decltype',
|
|
2556
|
+
'auto', 'register', 'extern', 'mutable', 'thread_local', 'constexpr', 'consteval',
|
|
2557
|
+
'constinit', 'inline', 'volatile'
|
|
2558
|
+
}
|
|
2559
|
+
|
|
2560
|
+
# Process line by line to handle function body context
|
|
2561
|
+
lines = public_section.split('\n')
|
|
2562
|
+
brace_depth = 0
|
|
2563
|
+
|
|
2564
|
+
for line in lines:
|
|
2565
|
+
# Track brace depth to skip function bodies
|
|
2566
|
+
brace_depth += line.count('{') - line.count('}')
|
|
2567
|
+
if brace_depth > 0:
|
|
2568
|
+
continue # Inside a function body
|
|
2569
|
+
|
|
2570
|
+
# Skip lines that look like function declarations/definitions
|
|
2571
|
+
stripped = line.strip()
|
|
2572
|
+
if '(' in stripped and ')' in stripped:
|
|
2573
|
+
continue
|
|
2574
|
+
|
|
2575
|
+
match = field_pattern.match(line)
|
|
2576
|
+
if match:
|
|
2577
|
+
is_const = match.group(1) is not None
|
|
2578
|
+
base_type = match.group(2).strip()
|
|
2579
|
+
ref_ptr = (match.group(3) or '').strip()
|
|
2580
|
+
names_part = match.group(4).strip()
|
|
2581
|
+
|
|
2582
|
+
# Skip if type looks like a keyword or constructor
|
|
2583
|
+
if base_type.lower() in cpp_keywords:
|
|
2584
|
+
continue
|
|
2585
|
+
if base_type == class_name:
|
|
2586
|
+
continue # Constructor, not a field
|
|
2587
|
+
|
|
2588
|
+
# Build full type
|
|
2589
|
+
full_type = ''
|
|
2590
|
+
if is_const:
|
|
2591
|
+
full_type = 'const '
|
|
2592
|
+
full_type += base_type
|
|
2593
|
+
if ref_ptr:
|
|
2594
|
+
full_type += ref_ptr
|
|
2595
|
+
|
|
2596
|
+
# Split comma-separated names: "x, y, z" -> ["x", "y", "z"]
|
|
2597
|
+
# Handle cases like: "*ptr1, *ptr2" or "&ref1, &ref2"
|
|
2598
|
+
name_parts = names_part.split(',')
|
|
2599
|
+
|
|
2600
|
+
for name in name_parts:
|
|
2601
|
+
name = name.strip()
|
|
2602
|
+
if not name:
|
|
2603
|
+
continue
|
|
2604
|
+
|
|
2605
|
+
# Handle pointer/reference attached to variable name
|
|
2606
|
+
actual_type = full_type
|
|
2607
|
+
if name.startswith('*'):
|
|
2608
|
+
name = name[1:].strip()
|
|
2609
|
+
if '*' not in actual_type:
|
|
2610
|
+
actual_type += '*'
|
|
2611
|
+
elif name.startswith('&'):
|
|
2612
|
+
name = name[1:].strip()
|
|
2613
|
+
if '&' not in actual_type:
|
|
2614
|
+
actual_type += '&'
|
|
2615
|
+
|
|
2616
|
+
# Remove array brackets if present
|
|
2617
|
+
if '[' in name:
|
|
2618
|
+
name = name[:name.find('[')]
|
|
2619
|
+
|
|
2620
|
+
# Validate field name
|
|
2621
|
+
if name and re.match(r'^[a-zA-Z_]\w*$', name):
|
|
2622
|
+
# Skip if name is a keyword
|
|
2623
|
+
if name.lower() not in cpp_keywords:
|
|
2624
|
+
fields.append((actual_type, name))
|
|
2625
|
+
|
|
2626
|
+
return fields
|
|
2627
|
+
|
|
2518
2628
|
all_files = cpp_files + h_files
|
|
2519
2629
|
|
|
2520
2630
|
for file in all_files:
|
|
@@ -2561,7 +2671,7 @@ def plugin(plugin_name, files, private):
|
|
|
2561
2671
|
public_section = extract_public_section(class_body, is_struct)
|
|
2562
2672
|
|
|
2563
2673
|
if class_name not in classes:
|
|
2564
|
-
classes[class_name] = {'methods': set(), 'constructors': [], 'method_signatures': {}}
|
|
2674
|
+
classes[class_name] = {'methods': set(), 'constructors': [], 'method_signatures': {}, 'fields': []}
|
|
2565
2675
|
|
|
2566
2676
|
methods, constructor_sigs, method_sigs = extract_methods(public_section, class_name)
|
|
2567
2677
|
classes[class_name]['methods'].update(methods)
|
|
@@ -2579,6 +2689,12 @@ def plugin(plugin_name, files, private):
|
|
|
2579
2689
|
classes[class_name]['constructors'].append(sig)
|
|
2580
2690
|
existing_ctors.add(sig)
|
|
2581
2691
|
|
|
2692
|
+
# v3.2.2: Extract fields (including comma-separated declarations)
|
|
2693
|
+
field_list = extract_fields(public_section, class_name)
|
|
2694
|
+
for field_type, field_name in field_list:
|
|
2695
|
+
if (field_type, field_name) not in classes[class_name]['fields']:
|
|
2696
|
+
classes[class_name]['fields'].append((field_type, field_name))
|
|
2697
|
+
|
|
2582
2698
|
# Find free functions (not inside class bodies)
|
|
2583
2699
|
# First, remove all class bodies from content to avoid matching class methods
|
|
2584
2700
|
content_no_classes = content
|
|
@@ -2709,6 +2825,11 @@ def plugin(plugin_name, files, private):
|
|
|
2709
2825
|
for method in sorted(cls_info['methods']):
|
|
2710
2826
|
f.write(f' METHOD({method})\n')
|
|
2711
2827
|
|
|
2828
|
+
# v3.2.2: Write fields
|
|
2829
|
+
if cls_info.get('fields'):
|
|
2830
|
+
for field_type, field_name in cls_info['fields']:
|
|
2831
|
+
f.write(f' FIELD({field_type}, {field_name})\n')
|
|
2832
|
+
|
|
2712
2833
|
f.write(f' }}\n')
|
|
2713
2834
|
|
|
2714
2835
|
if classes and (public_functions or template_functions):
|
|
@@ -2732,6 +2853,9 @@ def plugin(plugin_name, files, private):
|
|
|
2732
2853
|
|
|
2733
2854
|
click.secho(f"Generated plugin: {cp_file}", fg='green', bold=True)
|
|
2734
2855
|
click.echo(f"Classes found: {len(classes)}")
|
|
2856
|
+
total_fields = sum(len(c.get('fields', [])) for c in classes.values())
|
|
2857
|
+
if total_fields:
|
|
2858
|
+
click.echo(f"Fields found: {total_fields}")
|
|
2735
2859
|
click.echo(f"Template functions: {len(template_functions)}")
|
|
2736
2860
|
click.echo(f"Public functions: {len(public_functions)}")
|
|
2737
2861
|
if private_set:
|
|
@@ -5002,10 +5126,7 @@ def ai_ask(question, module_name, files, all_modules, exclude, think_mode, think
|
|
|
5002
5126
|
click.secho(f"AI Error: {response}", fg='red', err=True)
|
|
5003
5127
|
return
|
|
5004
5128
|
click.echo(f"\n{'='*60}")
|
|
5005
|
-
|
|
5006
|
-
click.echo(response)
|
|
5007
|
-
except UnicodeEncodeError:
|
|
5008
|
-
click.echo(response.encode('utf-8', errors='replace').decode('utf-8'))
|
|
5129
|
+
_safe_echo(response)
|
|
5009
5130
|
click.echo(f"{'='*60}\n")
|
|
5010
5131
|
|
|
5011
5132
|
|
|
@@ -29,20 +29,36 @@ CONTEXT_LIMITS = {
|
|
|
29
29
|
|
|
30
30
|
INCLUDECPP_CONTEXT = '''
|
|
31
31
|
CRITICAL KNOWLEDGE FOR INCLUDECPP:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
32
|
+
|
|
33
|
+
1. NAMESPACE REQUIREMENT:
|
|
34
|
+
ALL C++ code MUST be inside `namespace includecpp { }` - this is REQUIRED, not optional.
|
|
35
|
+
|
|
36
|
+
2. PLUGIN FILE (.cp) FORMAT:
|
|
37
|
+
SOURCE(file.cpp) module_name
|
|
38
|
+
PUBLIC:
|
|
39
|
+
module_name CLASS(MyClass) {
|
|
40
|
+
CONSTRUCTOR()
|
|
41
|
+
CONSTRUCTOR(int, double)
|
|
42
|
+
METHOD(foo)
|
|
43
|
+
METHOD_CONST(bar, const std::string&)
|
|
44
|
+
FIELD(double, x)
|
|
45
|
+
}
|
|
46
|
+
module_name FUNC(standalone_function)
|
|
47
|
+
module_name TEMPLATE_FUNC(generic_func) TYPES(int, float, double)
|
|
48
|
+
module_name STRUCT(Point) { FIELD(double, x) FIELD(double, y) }
|
|
49
|
+
|
|
50
|
+
3. BUILD OUTPUT:
|
|
51
|
+
~/.includecpp/builds/ (Windows: %APPDATA%/IncludeCPP/)
|
|
52
|
+
|
|
53
|
+
4. COMMON ERRORS AND FIXES:
|
|
54
|
+
* "undefined reference" -> Code not in namespace includecpp { }, or missing FUNC() in .cp
|
|
55
|
+
* "no matching function" -> Wrong parameter types in .cp METHOD() definition
|
|
56
|
+
* "template instantiation" -> Missing TEMPLATE_FUNC() with TYPES() in .cp file
|
|
57
|
+
* "namespace includecpp not found" -> Source file missing namespace wrapper
|
|
58
|
+
* "no member named X" -> Method not in class public section, or missing METHOD() in .cp
|
|
59
|
+
|
|
60
|
+
5. FIELD DECLARATIONS:
|
|
61
|
+
Comma-separated fields like `double x, y, z;` are parsed as separate fields.
|
|
46
62
|
'''
|
|
47
63
|
|
|
48
64
|
SYSTEM_PROMPT_OPTIMIZE = '''You are a C++ expert specializing in pybind11 bindings and the IncludeCPP framework.
|
|
@@ -313,12 +329,27 @@ REASON: <why>
|
|
|
313
329
|
Be thorough. This is professional-grade analysis.'''
|
|
314
330
|
|
|
315
331
|
|
|
332
|
+
CLI_KEYWORDS = {
|
|
333
|
+
'plugin': ['def plugin', '@cli.command', 'plugin_name', 'extract_fields', 'extract_methods'],
|
|
334
|
+
'rebuild': ['def rebuild', 'build_manager', '--fast', '--clean', '--auto-ai'],
|
|
335
|
+
'build': ['def build', 'build_manager'],
|
|
336
|
+
'auto': ['def auto', 'auto_plugins', '--all'],
|
|
337
|
+
'fix': ['def fix', 'fix_code', '--ai', 'ai_mgr'],
|
|
338
|
+
'init': ['def init', 'cpp.proj', 'plugins/', 'include/'],
|
|
339
|
+
'ai': ['@ai.command', 'ai_mgr', 'get_ai_manager', 'ai ask', 'ai edit', 'ai optimize'],
|
|
340
|
+
'settings': ['def settings', 'settings_ui', 'PyQt6'],
|
|
341
|
+
'flag': ['@click.option', 'is_flag', '--think', '--websearch', '--confirm'],
|
|
342
|
+
'command': ['@cli.command', '@click.argument', '@click.option'],
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
|
|
316
346
|
class AIManager:
|
|
317
347
|
def __init__(self):
|
|
318
348
|
self.secret_dir = Path.home() / '.includecpp'
|
|
319
349
|
self.secret_path = self.secret_dir / '.secret'
|
|
320
350
|
self.config = self._load_config()
|
|
321
351
|
self._doc_cache = None
|
|
352
|
+
self._cli_cache = None
|
|
322
353
|
|
|
323
354
|
def _get_documentation(self) -> str:
|
|
324
355
|
if self._doc_cache:
|
|
@@ -359,6 +390,85 @@ class AIManager:
|
|
|
359
390
|
return '\n\nBUILD CONTEXT (user\'s projects):\n' + '\n'.join(info_parts[:5])
|
|
360
391
|
return ''
|
|
361
392
|
|
|
393
|
+
def _get_cli_context(self, question: str) -> str:
|
|
394
|
+
"""Extract relevant CLI implementation context based on the question.
|
|
395
|
+
|
|
396
|
+
Only extracts code sections matching keywords from the question to save tokens.
|
|
397
|
+
"""
|
|
398
|
+
question_lower = question.lower()
|
|
399
|
+
|
|
400
|
+
# Check if question is about CLI/commands
|
|
401
|
+
cli_terms = ['command', 'flag', 'option', 'cli', 'includecpp', '--', 'how to', 'usage']
|
|
402
|
+
if not any(term in question_lower for term in cli_terms):
|
|
403
|
+
return ''
|
|
404
|
+
|
|
405
|
+
# Find which CLI topics are relevant
|
|
406
|
+
relevant_keywords = set()
|
|
407
|
+
for topic, keywords in CLI_KEYWORDS.items():
|
|
408
|
+
if topic in question_lower:
|
|
409
|
+
relevant_keywords.update(keywords)
|
|
410
|
+
|
|
411
|
+
# Add generic command keywords if asking about flags/options
|
|
412
|
+
if '--' in question or 'flag' in question_lower or 'option' in question_lower:
|
|
413
|
+
relevant_keywords.update(CLI_KEYWORDS['flag'])
|
|
414
|
+
|
|
415
|
+
if not relevant_keywords:
|
|
416
|
+
return ''
|
|
417
|
+
|
|
418
|
+
# Load CLI source if not cached
|
|
419
|
+
if self._cli_cache is None:
|
|
420
|
+
try:
|
|
421
|
+
cli_path = Path(__file__).parent.parent / 'cli' / 'commands.py'
|
|
422
|
+
if cli_path.exists():
|
|
423
|
+
self._cli_cache = cli_path.read_text(encoding='utf-8')
|
|
424
|
+
except:
|
|
425
|
+
return ''
|
|
426
|
+
|
|
427
|
+
if not self._cli_cache:
|
|
428
|
+
return ''
|
|
429
|
+
|
|
430
|
+
# Extract relevant sections (functions/decorators containing keywords)
|
|
431
|
+
lines = self._cli_cache.split('\n')
|
|
432
|
+
extracted = []
|
|
433
|
+
in_relevant_block = False
|
|
434
|
+
block_lines = []
|
|
435
|
+
indent_level = 0
|
|
436
|
+
|
|
437
|
+
for i, line in enumerate(lines):
|
|
438
|
+
stripped = line.strip()
|
|
439
|
+
|
|
440
|
+
# Check for function/decorator start
|
|
441
|
+
if stripped.startswith('@') or stripped.startswith('def '):
|
|
442
|
+
# Save previous block if relevant
|
|
443
|
+
if in_relevant_block and block_lines:
|
|
444
|
+
extracted.extend(block_lines[:50]) # Max 50 lines per block
|
|
445
|
+
extracted.append(' # ... (truncated)\n')
|
|
446
|
+
|
|
447
|
+
# Check if new block is relevant
|
|
448
|
+
in_relevant_block = any(kw in line for kw in relevant_keywords)
|
|
449
|
+
block_lines = [f'{i+1}: {line}'] if in_relevant_block else []
|
|
450
|
+
indent_level = len(line) - len(line.lstrip())
|
|
451
|
+
|
|
452
|
+
elif in_relevant_block:
|
|
453
|
+
# Continue block until dedent
|
|
454
|
+
current_indent = len(line) - len(line.lstrip()) if stripped else indent_level + 1
|
|
455
|
+
if stripped and current_indent <= indent_level and not stripped.startswith('@'):
|
|
456
|
+
# Block ended
|
|
457
|
+
if block_lines:
|
|
458
|
+
extracted.extend(block_lines[:50])
|
|
459
|
+
if len(block_lines) > 50:
|
|
460
|
+
extracted.append(' # ... (truncated)\n')
|
|
461
|
+
in_relevant_block = False
|
|
462
|
+
block_lines = []
|
|
463
|
+
else:
|
|
464
|
+
block_lines.append(f'{i+1}: {line}')
|
|
465
|
+
|
|
466
|
+
# Limit total context
|
|
467
|
+
if extracted:
|
|
468
|
+
context = '\n'.join(extracted[:200]) # Max 200 lines total
|
|
469
|
+
return f'\n\nCLI IMPLEMENTATION (relevant sections):\n```python\n{context}\n```'
|
|
470
|
+
return ''
|
|
471
|
+
|
|
362
472
|
def _get_context_limit(self, think: bool = False, think_twice: bool = False,
|
|
363
473
|
think_three: bool = False) -> int:
|
|
364
474
|
"""Get the appropriate context limit based on thinking mode."""
|
|
@@ -646,7 +756,8 @@ class AIManager:
|
|
|
646
756
|
self.config['daily_usage']['tokens'] = self.config['daily_usage'].get('tokens', 0) + tokens
|
|
647
757
|
self._save_config()
|
|
648
758
|
|
|
649
|
-
def query(self, system_prompt: str, user_prompt: str, temperature: float = 0.3
|
|
759
|
+
def query(self, system_prompt: str, user_prompt: str, temperature: float = 0.3,
|
|
760
|
+
timeout: int = 180) -> Tuple[bool, str]:
|
|
650
761
|
if not self.config.get('api_key'):
|
|
651
762
|
return False, 'No API key configured'
|
|
652
763
|
can_proceed, limit_warning = self._check_daily_limit()
|
|
@@ -672,7 +783,7 @@ class AIManager:
|
|
|
672
783
|
data['max_tokens'] = token_limit
|
|
673
784
|
data['temperature'] = temperature
|
|
674
785
|
try:
|
|
675
|
-
response = requests.post(OPENAI_API_URL, headers=headers, json=data, timeout=
|
|
786
|
+
response = requests.post(OPENAI_API_URL, headers=headers, json=data, timeout=timeout)
|
|
676
787
|
if response.status_code == 200:
|
|
677
788
|
result = response.json()
|
|
678
789
|
content = result['choices'][0]['message']['content']
|
|
@@ -724,7 +835,9 @@ class AIManager:
|
|
|
724
835
|
prompt = f'Optimize the following C++ files for performance, safety, and pybind11 compatibility:\n{file_content}'
|
|
725
836
|
system = SYSTEM_PROMPT_OPTIMIZE
|
|
726
837
|
prompt = self._build_prompt_with_docs(prompt)
|
|
727
|
-
|
|
838
|
+
# v3.2.2: Use longer timeout (5 min) for optimize operations with multiple files
|
|
839
|
+
timeout = 300 if len(files) > 1 else 180
|
|
840
|
+
success, response = self.query(system, prompt, timeout=timeout)
|
|
728
841
|
if not success:
|
|
729
842
|
return False, response, []
|
|
730
843
|
changes = self._parse_file_changes(response)
|
|
@@ -889,6 +1002,10 @@ class AIManager:
|
|
|
889
1002
|
context_parts.append('\nPlugin definitions:')
|
|
890
1003
|
for path, content in plugins.items():
|
|
891
1004
|
context_parts.append(f'\nPLUGIN: {path}\n```\n{content}\n```')
|
|
1005
|
+
# v3.2.2: Add CLI context for questions about commands/flags
|
|
1006
|
+
cli_context = self._get_cli_context(question)
|
|
1007
|
+
if cli_context:
|
|
1008
|
+
context_parts.append(cli_context)
|
|
892
1009
|
context = '\n'.join(context_parts)
|
|
893
1010
|
prompt = f'Question: {question}\n\n{context}'
|
|
894
1011
|
if think_three:
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "IncludeCPP"
|
|
7
|
-
version = "3.2.
|
|
7
|
+
version = "3.2.2"
|
|
8
8
|
description = "Professional C++ Python bindings with type-generic templates, pystubs and native threading"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|