invar-tools 1.17.12__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 +58 -2
- invar/shell/commands/guard.py +20 -4
- {invar_tools-1.17.12.dist-info → invar_tools-1.17.14.dist-info}/METADATA +1 -1
- {invar_tools-1.17.12.dist-info → invar_tools-1.17.14.dist-info}/RECORD +9 -9
- {invar_tools-1.17.12.dist-info → invar_tools-1.17.14.dist-info}/WHEEL +0 -0
- {invar_tools-1.17.12.dist-info → invar_tools-1.17.14.dist-info}/entry_points.txt +0 -0
- {invar_tools-1.17.12.dist-info → invar_tools-1.17.14.dist-info}/licenses/LICENSE +0 -0
- {invar_tools-1.17.12.dist-info → invar_tools-1.17.14.dist-info}/licenses/LICENSE-GPL +0 -0
- {invar_tools-1.17.12.dist-info → invar_tools-1.17.14.dist-info}/licenses/NOTICE +0 -0
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(
|
|
434
|
+
parsed = json.loads(stdout)
|
|
432
435
|
return ([TextContent(type="text", text=json.dumps(parsed, indent=2))], parsed)
|
|
433
436
|
except json.JSONDecodeError:
|
|
434
|
-
|
|
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)
|
invar/shell/commands/guard.py
CHANGED
|
@@ -190,12 +190,28 @@ def guard(
|
|
|
190
190
|
ts_result = run_typescript_guard(path if path.is_dir() else find_project_root(path))
|
|
191
191
|
match ts_result:
|
|
192
192
|
case Success(result):
|
|
193
|
-
|
|
193
|
+
if human:
|
|
194
|
+
# Human-readable Rich output
|
|
195
|
+
from invar.shell.prove.guard_ts import format_typescript_guard_v2
|
|
196
|
+
|
|
197
|
+
output = format_typescript_guard_v2(result)
|
|
198
|
+
console.print(f"[bold]TypeScript Guard[/bold] ({project_language})")
|
|
199
|
+
if result.status == "passed":
|
|
200
|
+
console.print("[green]✓ PASSED[/green]")
|
|
201
|
+
elif result.status == "skipped":
|
|
202
|
+
console.print("[yellow]⚠ SKIPPED[/yellow] (no TypeScript tools available)")
|
|
203
|
+
else:
|
|
204
|
+
console.print(f"[red]✗ FAILED[/red] ({result.error_count} errors)")
|
|
205
|
+
for v in result.violations[:10]: # Show first 10
|
|
206
|
+
console.print(f" {v.file}:{v.line}: [{v.severity}] {v.message}")
|
|
207
|
+
else:
|
|
208
|
+
# JSON output for agents
|
|
209
|
+
import json as json_mod
|
|
194
210
|
|
|
195
|
-
|
|
211
|
+
from invar.shell.prove.guard_ts import format_typescript_guard_v2
|
|
196
212
|
|
|
197
|
-
|
|
198
|
-
|
|
213
|
+
output = format_typescript_guard_v2(result)
|
|
214
|
+
console.print(json_mod.dumps(output, indent=2))
|
|
199
215
|
raise typer.Exit(0 if result.status == "passed" else 1)
|
|
200
216
|
case Failure(err):
|
|
201
217
|
console.print(f"[red]Error:[/red] {err}")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: invar-tools
|
|
3
|
-
Version: 1.17.
|
|
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=
|
|
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
|
|
@@ -2681,7 +2681,7 @@ invar/shell/ts_compiler.py,sha256=nA8brnOhThj9J_J3vAEGjDsM4NjbWQ_eX8Yf4pHPOgk,66
|
|
|
2681
2681
|
invar/shell/commands/__init__.py,sha256=MEkKwVyjI9DmkvBpJcuumXo2Pg_FFkfEr-Rr3nrAt7A,284
|
|
2682
2682
|
invar/shell/commands/doc.py,sha256=SOLDoCXXGxx_JU0PKXlAIGEF36PzconHmmAtL-rM6D4,13819
|
|
2683
2683
|
invar/shell/commands/feedback.py,sha256=lLxEeWW_71US_vlmorFrGXS8IARB9nbV6D0zruLs660,7640
|
|
2684
|
-
invar/shell/commands/guard.py,sha256=
|
|
2684
|
+
invar/shell/commands/guard.py,sha256=I1BDqthsAVmz8FnUgwkbhvy2cozmOfjs3rxQwwqFemo,25391
|
|
2685
2685
|
invar/shell/commands/hooks.py,sha256=W-SOnT4VQyUvXwipozkJwgEYfiOJGz7wksrbcdWegUg,2356
|
|
2686
2686
|
invar/shell/commands/init.py,sha256=rtoPFsfq7xRZ6lfTipWT1OejNK5wfzqu1ncXi1kizU0,23634
|
|
2687
2687
|
invar/shell/commands/merge.py,sha256=nuvKo8m32-OL-SCQlS4SLKmOZxQ3qj-1nGCx1Pgzifw,8183
|
|
@@ -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.
|
|
2782
|
-
invar_tools-1.17.
|
|
2783
|
-
invar_tools-1.17.
|
|
2784
|
-
invar_tools-1.17.
|
|
2785
|
-
invar_tools-1.17.
|
|
2786
|
-
invar_tools-1.17.
|
|
2787
|
-
invar_tools-1.17.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|