invar-tools 1.17.13__py3-none-any.whl → 1.17.14__py3-none-any.whl

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.
invar/mcp/handlers.py CHANGED
@@ -427,11 +427,24 @@ async def _execute_command(
427
427
  timeout=timeout,
428
428
  )
429
429
 
430
+ stdout = result.stdout.strip()
431
+
432
+ # Try to parse as JSON
430
433
  try:
431
- parsed = json.loads(result.stdout)
434
+ parsed = json.loads(stdout)
432
435
  return ([TextContent(type="text", text=json.dumps(parsed, indent=2))], parsed)
433
436
  except json.JSONDecodeError:
434
- output = result.stdout
437
+ # Try to fix unescaped newlines in JSON strings
438
+ # Guard/map commands may output multiline JSON with literal newlines
439
+ fixed = _fix_json_newlines(stdout)
440
+ try:
441
+ parsed = json.loads(fixed)
442
+ return ([TextContent(type="text", text=json.dumps(parsed, indent=2))], parsed)
443
+ except json.JSONDecodeError:
444
+ pass
445
+
446
+ # Fall back to text output
447
+ output = stdout
435
448
  if result.stderr:
436
449
  output += f"\n\nStderr:\n{result.stderr}"
437
450
  return [TextContent(type="text", text=output)]
@@ -440,3 +453,46 @@ async def _execute_command(
440
453
  return [TextContent(type="text", text=f"Error: Command timed out ({timeout}s)")]
441
454
  except Exception as e:
442
455
  return [TextContent(type="text", text=f"Error: {e}")]
456
+
457
+
458
+ # @invar:allow shell_too_complex: Simple state machine, 6 branches is minimal
459
+ # @invar:allow shell_pure_logic: No I/O, but called from shell context
460
+ # @invar:allow shell_result: Pure transformation, returns str not Result
461
+ def _fix_json_newlines(text: str) -> str:
462
+ """Fix unescaped newlines in JSON strings.
463
+
464
+ When subprocess outputs multiline JSON, newlines inside string values
465
+ are not escaped, causing json.loads() to fail. This function escapes them.
466
+
467
+ DX-33: Escape hatch for complex pure logic helper.
468
+ """
469
+ result = []
470
+ i = 0
471
+ while i < len(text):
472
+ if text[i] == '"':
473
+ # Inside a string - collect until closing quote
474
+ result.append('"')
475
+ i += 1
476
+ while i < len(text):
477
+ c = text[i]
478
+ if c == "\\" and i + 1 < len(text):
479
+ # Escaped character - keep as is
480
+ result.append("\\")
481
+ result.append(text[i + 1])
482
+ i += 2
483
+ elif c == '"':
484
+ # End of string
485
+ result.append('"')
486
+ i += 1
487
+ break
488
+ elif c == "\n" or c == "\r":
489
+ # Unescaped newline - escape it
490
+ result.append("\\n")
491
+ i += 1
492
+ else:
493
+ result.append(c)
494
+ i += 1
495
+ else:
496
+ result.append(text[i])
497
+ i += 1
498
+ return "".join(result)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: invar-tools
3
- Version: 1.17.13
3
+ Version: 1.17.14
4
4
  Summary: AI-native software engineering tools with design-by-contract verification
5
5
  Project-URL: Homepage, https://github.com/tefx/invar
6
6
  Project-URL: Documentation, https://github.com/tefx/invar#readme
@@ -50,7 +50,7 @@ invar/core/patterns/registry.py,sha256=2rz0wWDRarMkuHN-qM_ZrT3qeGFDSKMABvRvPNZxQ
50
50
  invar/core/patterns/types.py,sha256=ULAlWuAdmO6CFcEDjTrWBfzNTBsnomAl2d25tR11ihU,5506
51
51
  invar/mcp/__init__.py,sha256=n3S7QwMjSMqOMT8cI2jf9E0yZPjKmBOJyIYhq4WZ8TQ,226
52
52
  invar/mcp/__main__.py,sha256=ZcIT2U6xUyGOWucl4jq422BDE3lRLjqyxb9pFylRBdk,219
53
- invar/mcp/handlers.py,sha256=3khWBATWxV4TwHPm6Gnr9pWfGaPyxf5p8yR-pnJhDtg,16756
53
+ invar/mcp/handlers.py,sha256=VQGpFG6voBOXML2TtBFyU_lkCTn83yF4GwmMvl2gxvI,18762
54
54
  invar/mcp/server.py,sha256=zSpY9bCFuq4mWe7XfolTnwHffhdmoyN40aFL4L7dFrE,20407
55
55
  invar/node_tools/.gitignore,sha256=M2kz8Iw7Kzmi44mKo1r7_HOZMh79a7dFDdRrqXyaEhI,530
56
56
  invar/node_tools/MANIFEST,sha256=2Z2at-27MK8K7DSjOjjtR4faTbt6eCiKQuEfvP_lwH8,145
@@ -2778,10 +2778,10 @@ invar/templates/skills/invar-reflect/template.md,sha256=Rr5hvbllvmd8jSLf_0ZjyKt6
2778
2778
  invar/templates/skills/investigate/SKILL.md.jinja,sha256=cp6TBEixBYh1rLeeHOR1yqEnFqv1NZYePORMnavLkQI,3231
2779
2779
  invar/templates/skills/propose/SKILL.md.jinja,sha256=6BuKiCqO1AEu3VtzMHy1QWGqr_xqG9eJlhbsKT4jev4,3463
2780
2780
  invar/templates/skills/review/SKILL.md.jinja,sha256=ET5mbdSe_eKgJbi2LbgFC-z1aviKcHOBw7J5Q28fr4U,14105
2781
- invar_tools-1.17.13.dist-info/METADATA,sha256=o7GKebTKZWJJJj3bYPMllH1YTrqhDqlbY5JUdngK6XA,28596
2782
- invar_tools-1.17.13.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
2783
- invar_tools-1.17.13.dist-info/entry_points.txt,sha256=RwH_EhqgtFPsnO6RcrwrAb70Zyfb8Mh6uUtztWnUxGk,102
2784
- invar_tools-1.17.13.dist-info/licenses/LICENSE,sha256=qeFksp4H4kfTgQxPCIu3OdagXyiZcgBlVfsQ6M5oFyk,10767
2785
- invar_tools-1.17.13.dist-info/licenses/LICENSE-GPL,sha256=IvZfC6ZbP7CLjytoHVzvpDZpD-Z3R_qa1GdMdWlWQ6Q,35157
2786
- invar_tools-1.17.13.dist-info/licenses/NOTICE,sha256=joEyMyFhFY8Vd8tTJ-a3SirI0m2Sd0WjzqYt3sdcglc,2561
2787
- invar_tools-1.17.13.dist-info/RECORD,,
2781
+ invar_tools-1.17.14.dist-info/METADATA,sha256=TC_Puck2FidWoZbAcQOGw7HmWg5F9fjAqY0mY-yRX-g,28596
2782
+ invar_tools-1.17.14.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
2783
+ invar_tools-1.17.14.dist-info/entry_points.txt,sha256=RwH_EhqgtFPsnO6RcrwrAb70Zyfb8Mh6uUtztWnUxGk,102
2784
+ invar_tools-1.17.14.dist-info/licenses/LICENSE,sha256=qeFksp4H4kfTgQxPCIu3OdagXyiZcgBlVfsQ6M5oFyk,10767
2785
+ invar_tools-1.17.14.dist-info/licenses/LICENSE-GPL,sha256=IvZfC6ZbP7CLjytoHVzvpDZpD-Z3R_qa1GdMdWlWQ6Q,35157
2786
+ invar_tools-1.17.14.dist-info/licenses/NOTICE,sha256=joEyMyFhFY8Vd8tTJ-a3SirI0m2Sd0WjzqYt3sdcglc,2561
2787
+ invar_tools-1.17.14.dist-info/RECORD,,