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.
Files changed (37) hide show
  1. {includecpp-3.2.0 → includecpp-3.2.2}/IncludeCPP.egg-info/PKG-INFO +10 -2
  2. {includecpp-3.2.0 → includecpp-3.2.2}/PKG-INFO +10 -2
  3. {includecpp-3.2.0 → includecpp-3.2.2}/README.md +9 -1
  4. {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/__init__.py +1 -1
  5. {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/cli/commands.py +128 -7
  6. {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/core/ai_integration.py +134 -17
  7. {includecpp-3.2.0 → includecpp-3.2.2}/pyproject.toml +1 -1
  8. {includecpp-3.2.0 → includecpp-3.2.2}/IncludeCPP.egg-info/SOURCES.txt +0 -0
  9. {includecpp-3.2.0 → includecpp-3.2.2}/IncludeCPP.egg-info/dependency_links.txt +0 -0
  10. {includecpp-3.2.0 → includecpp-3.2.2}/IncludeCPP.egg-info/entry_points.txt +0 -0
  11. {includecpp-3.2.0 → includecpp-3.2.2}/IncludeCPP.egg-info/requires.txt +0 -0
  12. {includecpp-3.2.0 → includecpp-3.2.2}/IncludeCPP.egg-info/top_level.txt +0 -0
  13. {includecpp-3.2.0 → includecpp-3.2.2}/LICENSE +0 -0
  14. {includecpp-3.2.0 → includecpp-3.2.2}/MANIFEST.in +0 -0
  15. {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/__init__.pyi +0 -0
  16. {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/__main__.py +0 -0
  17. {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/cli/__init__.py +0 -0
  18. {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/cli/config_parser.py +0 -0
  19. {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/core/__init__.py +0 -0
  20. {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/core/build_manager.py +0 -0
  21. {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/core/cpp_api.py +0 -0
  22. {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/core/cpp_api.pyi +0 -0
  23. {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/core/error_catalog.py +0 -0
  24. {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/core/error_formatter.py +0 -0
  25. {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/core/exceptions.py +0 -0
  26. {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/core/path_discovery.py +0 -0
  27. {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/core/settings_ui.py +0 -0
  28. {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/generator/__init__.py +0 -0
  29. {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/generator/parser.cpp +0 -0
  30. {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/generator/parser.h +0 -0
  31. {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/generator/type_resolver.cpp +0 -0
  32. {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/generator/type_resolver.h +0 -0
  33. {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/py.typed +0 -0
  34. {includecpp-3.2.0 → includecpp-3.2.2}/includecpp/templates/cpp.proj.template +0 -0
  35. {includecpp-3.2.0 → includecpp-3.2.2}/requirements.txt +0 -0
  36. {includecpp-3.2.0 → includecpp-3.2.2}/setup.cfg +0 -0
  37. {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.0
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.0 | [GitHub](https://github.com/liliassg/IncludeCPP)
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.0
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.0 | [GitHub](https://github.com/liliassg/IncludeCPP)
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.0 | [GitHub](https://github.com/liliassg/IncludeCPP)
482
+ MIT License | v3.2.2 | [GitHub](https://github.com/liliassg/IncludeCPP)
@@ -1,7 +1,7 @@
1
1
  from .core.cpp_api import CppApi
2
2
  import warnings
3
3
 
4
- __version__ = "3.2.0"
4
+ __version__ = "3.2.2"
5
5
  __all__ = ["CppApi"]
6
6
 
7
7
  # Module-level cache for C++ modules
@@ -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
- click.secho(ascii_text, **kwargs)
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
- try:
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
- - ALL C++ code MUST be inside `namespace includecpp { }` - this is REQUIRED, not optional
33
- - Plugin files (.cp) define bindings: SOURCE(file.cpp), HEADER(file.h), CLASS(), FUNC(), METHOD(), TEMPLATE_FUNC()
34
- - Build output: ~/.includecpp/builds/ (Windows: %APPDATA%/IncludeCPP/)
35
- - Common errors and fixes:
36
- * "undefined reference" / "unresolved external" -> Code not in namespace includecpp { }, or missing FUNC() in .cp
37
- * "no matching function" -> Wrong parameter types in .cp METHOD() definition
38
- * "template instantiation" -> Missing TEMPLATE_FUNC() with TYPES() in .cp file
39
- * "namespace includecpp not found" -> Source file missing namespace includecpp { } wrapper
40
- - Plugin file (.cp) format example:
41
- SOURCE(module.cpp) module_name
42
- PUBLIC:
43
- module_name CLASS(MyClass) { METHOD(foo) CONSTRUCTOR() }
44
- module_name FUNC(standalone_function)
45
- module_name TEMPLATE_FUNC(generic_func) TYPES(int, float, double)
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) -> Tuple[bool, str]:
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=120)
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
- success, response = self.query(system, prompt)
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.0"
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