invar-tools 1.17.7__py3-none-any.whl → 1.17.9__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
@@ -11,11 +11,14 @@ import json
11
11
  import subprocess
12
12
  import sys
13
13
  from pathlib import Path
14
- from typing import Any, Literal
14
+ from typing import TYPE_CHECKING, Any, Literal
15
15
 
16
16
  from mcp.types import TextContent
17
17
  from returns.result import Success
18
18
 
19
+ if TYPE_CHECKING:
20
+ from mcp.server.lowlevel.server import CombinationContent
21
+
19
22
 
20
23
  # @invar:allow shell_result: Pure validation helper, no I/O, returns tuple not Result
21
24
  # @shell_complexity: Security validation requires multiple checks
@@ -59,7 +62,7 @@ def _validate_path(path: str) -> tuple[bool, str]:
59
62
  # @shell_orchestration: MCP handler - subprocess is called inside
60
63
  # @shell_complexity: Guard command with multiple optional flags
61
64
  # @invar:allow shell_result: MCP handler for guard tool
62
- async def _run_guard(args: dict[str, Any]) -> list[TextContent]:
65
+ async def _run_guard(args: dict[str, Any]) -> list[TextContent] | CombinationContent:
63
66
  """Run invar guard command."""
64
67
  path = args.get("path", ".")
65
68
  is_valid, error = _validate_path(path)
@@ -88,7 +91,7 @@ async def _run_guard(args: dict[str, Any]) -> list[TextContent]:
88
91
 
89
92
  # @shell_orchestration: MCP handler - subprocess is called inside
90
93
  # @invar:allow shell_result: MCP handler for sig tool
91
- async def _run_sig(args: dict[str, Any]) -> list[TextContent]:
94
+ async def _run_sig(args: dict[str, Any]) -> list[TextContent] | CombinationContent:
92
95
  """Run invar sig command."""
93
96
  target = args.get("target", "")
94
97
  if not target:
@@ -106,7 +109,7 @@ async def _run_sig(args: dict[str, Any]) -> list[TextContent]:
106
109
 
107
110
  # @shell_orchestration: MCP handler - subprocess is called inside
108
111
  # @invar:allow shell_result: MCP handler for map tool
109
- async def _run_map(args: dict[str, Any]) -> list[TextContent]:
112
+ async def _run_map(args: dict[str, Any]) -> list[TextContent] | CombinationContent:
110
113
  """Run invar map command."""
111
114
  path = args.get("path", ".")
112
115
  is_valid, error = _validate_path(path)
@@ -125,7 +128,7 @@ async def _run_map(args: dict[str, Any]) -> list[TextContent]:
125
128
 
126
129
  # @shell_orchestration: MCP handler - orchestrates refs command execution
127
130
  # @invar:allow shell_result: MCP handler for refs tool
128
- async def _run_refs(args: dict[str, Any]) -> list[TextContent]:
131
+ async def _run_refs(args: dict[str, Any]) -> list[TextContent] | CombinationContent:
129
132
  """Run invar refs command.
130
133
 
131
134
  DX-78: Find all references to a symbol.
@@ -155,7 +158,7 @@ async def _run_refs(args: dict[str, Any]) -> list[TextContent]:
155
158
  # @shell_orchestration: MCP handler - calls shell layer directly
156
159
  # @shell_complexity: MCP input validation + result handling
157
160
  # @invar:allow shell_result: MCP handler for doc_toc tool
158
- async def _run_doc_toc(args: dict[str, Any]) -> list[TextContent]:
161
+ async def _run_doc_toc(args: dict[str, Any]) -> list[TextContent] | CombinationContent:
159
162
  """Run invar_doc_toc - extract document structure."""
160
163
  from dataclasses import asdict
161
164
 
@@ -203,7 +206,7 @@ def _section_to_dict(section: Any) -> dict[str, Any]:
203
206
  # @shell_orchestration: MCP handler - calls shell layer directly
204
207
  # @shell_complexity: MCP input validation + result handling
205
208
  # @invar:allow shell_result: MCP handler for doc_read tool
206
- async def _run_doc_read(args: dict[str, Any]) -> list[TextContent]:
209
+ async def _run_doc_read(args: dict[str, Any]) -> list[TextContent] | CombinationContent:
207
210
  """Run invar_doc_read - read a specific section."""
208
211
  from invar.shell.doc_tools import read_section
209
212
 
@@ -232,7 +235,7 @@ async def _run_doc_read(args: dict[str, Any]) -> list[TextContent]:
232
235
 
233
236
  # @shell_complexity: Multiple arg validation branches + error handling
234
237
  # @invar:allow shell_result: MCP handler for doc_read_many tool
235
- async def _run_doc_read_many(args: dict[str, Any]) -> list[TextContent]:
238
+ async def _run_doc_read_many(args: dict[str, Any]) -> list[TextContent] | CombinationContent:
236
239
  """Run invar_doc_read_many - read multiple sections."""
237
240
  from invar.shell.doc_tools import read_sections_batch
238
241
 
@@ -264,7 +267,7 @@ async def _run_doc_read_many(args: dict[str, Any]) -> list[TextContent]:
264
267
  # @shell_orchestration: MCP handler - calls shell layer directly
265
268
  # @shell_complexity: MCP input validation + result handling
266
269
  # @invar:allow shell_result: MCP handler for doc_find tool
267
- async def _run_doc_find(args: dict[str, Any]) -> list[TextContent]:
270
+ async def _run_doc_find(args: dict[str, Any]) -> list[TextContent] | CombinationContent:
268
271
  """Run invar_doc_find - find sections matching pattern."""
269
272
  from invar.shell.doc_tools import find_sections
270
273
 
@@ -308,7 +311,7 @@ async def _run_doc_find(args: dict[str, Any]) -> list[TextContent]:
308
311
  # @shell_orchestration: MCP handler - calls shell layer directly
309
312
  # @shell_complexity: MCP input validation + result handling
310
313
  # @invar:allow shell_result: MCP handler for doc_replace tool
311
- async def _run_doc_replace(args: dict[str, Any]) -> list[TextContent]:
314
+ async def _run_doc_replace(args: dict[str, Any]) -> list[TextContent] | CombinationContent:
312
315
  """Run invar_doc_replace - replace section content."""
313
316
  from invar.shell.doc_tools import replace_section_content
314
317
 
@@ -342,7 +345,7 @@ async def _run_doc_replace(args: dict[str, Any]) -> list[TextContent]:
342
345
  # @shell_orchestration: MCP handler - calls shell layer directly
343
346
  # @shell_complexity: MCP input validation + result handling
344
347
  # @invar:allow shell_result: MCP handler for doc_insert tool
345
- async def _run_doc_insert(args: dict[str, Any]) -> list[TextContent]:
348
+ async def _run_doc_insert(args: dict[str, Any]) -> list[TextContent] | CombinationContent:
346
349
  """Run invar_doc_insert - insert content relative to section."""
347
350
  from invar.shell.doc_tools import insert_section_content
348
351
 
@@ -382,7 +385,7 @@ async def _run_doc_insert(args: dict[str, Any]) -> list[TextContent]:
382
385
  # @shell_orchestration: MCP handler - calls shell layer directly
383
386
  # @shell_complexity: MCP input validation + result handling
384
387
  # @invar:allow shell_result: MCP handler for doc_delete tool
385
- async def _run_doc_delete(args: dict[str, Any]) -> list[TextContent]:
388
+ async def _run_doc_delete(args: dict[str, Any]) -> list[TextContent] | CombinationContent:
386
389
  """Run invar_doc_delete - delete a section."""
387
390
  from invar.shell.doc_tools import delete_section_content
388
391
 
@@ -411,13 +414,11 @@ async def _run_doc_delete(args: dict[str, Any]) -> list[TextContent]:
411
414
 
412
415
  # @shell_complexity: Command execution with error handling branches
413
416
  # @invar:allow shell_result: MCP subprocess wrapper utility
414
- async def _execute_command(cmd: list[str], timeout: int = 600) -> list[TextContent]:
415
- """Execute a command and return the result.
416
-
417
- Args:
418
- cmd: Command to execute
419
- timeout: Maximum time in seconds (default: 600, accommodates full Guard cycle)
420
- """
417
+ async def _execute_command(
418
+ cmd: list[str],
419
+ timeout: int = 600,
420
+ ) -> list[TextContent] | CombinationContent:
421
+ """Execute a command and return result."""
421
422
  try:
422
423
  result = subprocess.run(
423
424
  cmd,
@@ -426,18 +427,14 @@ async def _execute_command(cmd: list[str], timeout: int = 600) -> list[TextConte
426
427
  timeout=timeout,
427
428
  )
428
429
 
429
- output = result.stdout
430
- if result.stderr:
431
- output += f"\n\nStderr:\n{result.stderr}"
432
-
433
- # Try to parse as JSON for better formatting
434
430
  try:
435
431
  parsed = json.loads(result.stdout)
436
- output = json.dumps(parsed, indent=2)
432
+ return ([TextContent(type="text", text=json.dumps(parsed, indent=2))], parsed)
437
433
  except json.JSONDecodeError:
438
- pass
439
-
440
- return [TextContent(type="text", text=output)]
434
+ output = result.stdout
435
+ if result.stderr:
436
+ output += f"\n\nStderr:\n{result.stderr}"
437
+ return [TextContent(type="text", text=output)]
441
438
 
442
439
  except subprocess.TimeoutExpired:
443
440
  return [TextContent(type="text", text=f"Error: Command timed out ({timeout}s)")]
@@ -6,7 +6,6 @@ Shell module: handles user interaction and file I/O.
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
- import os
10
9
  from pathlib import Path
11
10
 
12
11
  import typer
@@ -14,13 +13,6 @@ from returns.result import Failure, Result, Success
14
13
  from rich.console import Console
15
14
  from rich.table import Table
16
15
 
17
-
18
- def _detect_agent_mode() -> bool:
19
- """Detect agent context: INVAR_MODE=agent OR non-TTY (pipe/redirect)."""
20
- import sys
21
- return os.getenv("INVAR_MODE") == "agent" or not sys.stdout.isatty()
22
-
23
-
24
16
  from invar import __version__
25
17
  from invar.core.models import GuardReport, RuleConfig
26
18
  from invar.core.rules import check_all_rules
@@ -95,12 +87,14 @@ def _scan_and_check(
95
87
  for rule, reason, line in extract_escape_hatches(file_info.source):
96
88
  all_escapes.append((file_info.path, rule, reason))
97
89
  # DX-66: Add to escape hatch summary
98
- report.escape_hatches.add(EscapeHatchDetail(
99
- file=file_info.path,
100
- line=line,
101
- rule=rule,
102
- reason=reason,
103
- ))
90
+ report.escape_hatches.add(
91
+ EscapeHatchDetail(
92
+ file=file_info.path,
93
+ line=line,
94
+ rule=rule,
95
+ reason=reason,
96
+ )
97
+ )
104
98
 
105
99
  # DX-22: Check project-level complexity debt (Fix-or-Explain enforcement)
106
100
  for debt_violation in check_complexity_debt(
@@ -115,6 +109,10 @@ def _scan_and_check(
115
109
  return Success(report)
116
110
 
117
111
 
112
+ def _determine_output_mode(human: bool, agent: bool = False, json_output: bool = False) -> bool:
113
+ return not human
114
+
115
+
118
116
  # @invar:allow entry_point_too_thick: Main CLI entry point, orchestrates all verification phases
119
117
  @app.command()
120
118
  def guard(
@@ -133,7 +131,7 @@ def guard(
133
131
  False, "--static", help="Static analysis only, skip all runtime tests"
134
132
  ),
135
133
  human: bool = typer.Option(
136
- False, "--human", help="Force human-readable output (for testing/debugging)"
134
+ False, "--human", help="Force Rich human-readable output (opt-in, default is JSON)"
137
135
  ),
138
136
  # DX-26: Deprecated flags kept for backward compatibility
139
137
  no_strict_pure: bool = typer.Option(
@@ -192,23 +190,12 @@ def guard(
192
190
  ts_result = run_typescript_guard(path if path.is_dir() else find_project_root(path))
193
191
  match ts_result:
194
192
  case Success(result):
195
- if json_output or agent:
196
- import json as json_mod
197
-
198
- from invar.shell.prove.guard_ts import format_typescript_guard_v2
199
-
200
- output = format_typescript_guard_v2(result)
201
- console.print(json_mod.dumps(output, indent=2))
202
- else:
203
- console.print(f"[bold]TypeScript Guard[/bold] ({project_language})")
204
- if result.status == "passed":
205
- console.print("[green]✓ PASSED[/green]")
206
- elif result.status == "skipped":
207
- console.print("[yellow]⚠ SKIPPED[/yellow] (no TypeScript tools available)")
208
- else:
209
- console.print(f"[red]✗ FAILED[/red] ({result.error_count} errors)")
210
- for v in result.violations[:10]: # Show first 10
211
- console.print(f" {v.file}:{v.line}: [{v.severity}] {v.message}")
193
+ import json as json_mod
194
+
195
+ from invar.shell.prove.guard_ts import format_typescript_guard_v2
196
+
197
+ output = format_typescript_guard_v2(result)
198
+ console.print(json_mod.dumps(output, indent=2))
212
199
  raise typer.Exit(0 if result.status == "passed" else 1)
213
200
  case Failure(err):
214
201
  console.print(f"[red]Error:[/red] {err}")
@@ -295,6 +282,7 @@ def guard(
295
282
  run_pattern_detection,
296
283
  suggestions_to_violations,
297
284
  )
285
+
298
286
  # Run pattern detection on checked files
299
287
  files_to_check = list(only_files) if only_files else None
300
288
  pattern_result = run_pattern_detection(path, files_to_check)
@@ -331,6 +319,7 @@ def guard(
331
319
  # DX-37: Check coverage availability if requested
332
320
  if coverage:
333
321
  from invar.shell.coverage import check_coverage_available
322
+
334
323
  cov_check = check_coverage_available()
335
324
  if isinstance(cov_check, Failure):
336
325
  console.print(f"[yellow]Warning:[/yellow] {cov_check.failure()}")
@@ -342,14 +331,19 @@ def guard(
342
331
 
343
332
  # Phase 1: Doctests (DX-37: with optional coverage)
344
333
  doctest_passed, doctest_output, doctest_coverage = run_doctests_phase(
345
- checked_files, explain, timeout=config.timeout_doctest,
334
+ checked_files,
335
+ explain,
336
+ timeout=config.timeout_doctest,
346
337
  collect_coverage=coverage,
347
338
  )
348
339
 
349
340
  # Phase 2: CrossHair symbolic verification
350
341
  # Note: CrossHair uses subprocess + symbolic execution, coverage not applicable
351
342
  crosshair_passed, crosshair_output = run_crosshair_phase(
352
- path, checked_files, doctest_passed, static_exit_code,
343
+ path,
344
+ checked_files,
345
+ doctest_passed,
346
+ static_exit_code,
353
347
  changed_mode=changed,
354
348
  timeout=config.timeout_crosshair,
355
349
  per_condition_timeout=config.timeout_crosshair_per_condition,
@@ -357,7 +351,9 @@ def guard(
357
351
 
358
352
  # Phase 3: Hypothesis property tests (DX-37: with optional coverage)
359
353
  property_passed, property_output, property_coverage = run_property_tests_phase(
360
- checked_files, doctest_passed, static_exit_code,
354
+ checked_files,
355
+ doctest_passed,
356
+ static_exit_code,
361
357
  collect_coverage=coverage,
362
358
  )
363
359
  elif verification_level == VerificationLevel.STATIC:
@@ -382,20 +378,31 @@ def guard(
382
378
  if property_coverage and property_coverage.get("collected"):
383
379
  coverage_output["phases_tracked"].append("hypothesis")
384
380
  if "overall_branch_coverage" in property_coverage:
385
- coverage_output["overall_branch_coverage"] = property_coverage["overall_branch_coverage"]
381
+ coverage_output["overall_branch_coverage"] = property_coverage[
382
+ "overall_branch_coverage"
383
+ ]
386
384
 
387
385
  # DX-26: Unified output (agent JSON or human Rich)
388
386
  if use_agent_output:
389
387
  output_agent(
390
- report, strict, doctest_passed, doctest_output, crosshair_output, level_name,
388
+ report,
389
+ strict,
390
+ doctest_passed,
391
+ doctest_output,
392
+ crosshair_output,
393
+ level_name,
391
394
  property_output=property_output,
392
395
  coverage_data=coverage_output, # DX-37
393
396
  )
394
397
  else:
395
398
  output_rich(report, config.strict_pure, changed, pedantic, explain, static)
396
399
  output_verification_status(
397
- verification_level, static_exit_code, doctest_passed,
398
- doctest_output, crosshair_output, explain,
400
+ verification_level,
401
+ static_exit_code,
402
+ doctest_passed,
403
+ doctest_output,
404
+ crosshair_output,
405
+ explain,
399
406
  property_output=property_output,
400
407
  strict=strict,
401
408
  )
@@ -405,7 +412,9 @@ def guard(
405
412
  overall = coverage_output.get("overall_branch_coverage", 0.0)
406
413
  console.print(f"\n[bold]Coverage Analysis[/bold] ({' + '.join(phases)})")
407
414
  console.print(f" Overall branch coverage: {overall}%")
408
- console.print(" [dim]Note: CrossHair uses symbolic execution; coverage not applicable.[/dim]")
415
+ console.print(
416
+ " [dim]Note: CrossHair uses symbolic execution; coverage not applicable.[/dim]"
417
+ )
409
418
 
410
419
  # Exit with combined status
411
420
  all_passed = doctest_passed and crosshair_passed and property_passed
@@ -413,28 +422,6 @@ def guard(
413
422
  raise typer.Exit(final_exit)
414
423
 
415
424
 
416
- # @shell_orchestration: Output mode decision helper for CLI
417
- def _determine_output_mode(human: bool, agent: bool = False, json_output: bool = False) -> bool:
418
- """Determine if agent JSON output should be used (DX-26).
419
-
420
- DX-26: TTY auto-detection with --human override.
421
- - --human flag → human output (for testing/debugging)
422
- - TTY (terminal) → human output
423
- - Non-TTY (pipe/redirect) → agent JSON output
424
- - Deprecated --agent/--json flags → still work for backward compat
425
- """
426
- # --human flag always forces human output
427
- if human:
428
- return False # use_agent = False
429
-
430
- # Deprecated flags (backward compat)
431
- if json_output or agent:
432
- return True # use_agent = True
433
-
434
- # TTY auto-detection
435
- return _detect_agent_mode() # Returns True if non-TTY
436
-
437
-
438
425
  def _show_verification_level(verification_level) -> None:
439
426
  """Show verification level in human-readable format.
440
427
 
@@ -464,8 +451,7 @@ def map_command(
464
451
  """Generate symbol map with reference counts."""
465
452
  from invar.shell.commands.perception import run_map
466
453
 
467
- # Phase 9 P11: Auto-detect agent mode
468
- use_json = json_output or _detect_agent_mode()
454
+ use_json = True
469
455
  result = run_map(path, top, use_json)
470
456
  if isinstance(result, Failure):
471
457
  console.print(f"[red]Error:[/red] {result.failure()}")
@@ -480,8 +466,7 @@ def sig_command(
480
466
  """Extract signatures from a file or symbol."""
481
467
  from invar.shell.commands.perception import run_sig
482
468
 
483
- # Phase 9 P11: Auto-detect agent mode
484
- use_json = json_output or _detect_agent_mode()
469
+ use_json = True
485
470
  result = run_sig(target, use_json)
486
471
  if isinstance(result, Failure):
487
472
  console.print(f"[red]Error:[/red] {result.failure()}")
@@ -504,8 +489,7 @@ def refs_command(
504
489
  """
505
490
  from invar.shell.commands.perception import run_refs
506
491
 
507
- # Auto-detect agent mode
508
- use_json = json_output or _detect_agent_mode()
492
+ use_json = True
509
493
  result = run_refs(target, use_json)
510
494
  if isinstance(result, Failure):
511
495
  console.print(f"[red]Error:[/red] {result.failure()}")
@@ -529,8 +513,7 @@ def rules(
529
513
 
530
514
  from invar.core.rule_meta import RULE_META, RuleCategory, get_rules_by_category
531
515
 
532
- # Phase 9 P11: Auto-detect agent mode
533
- use_json = json_output or _detect_agent_mode()
516
+ use_json = True
534
517
 
535
518
  # Filter by category if specified
536
519
  if category:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: invar-tools
3
- Version: 1.17.7
3
+ Version: 1.17.9
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=Kls1aXnYmGcMvib_Mesfz5FjBaL7lmrhKaw_P2JDnyw,16551
53
+ invar/mcp/handlers.py,sha256=3khWBATWxV4TwHPm6Gnr9pWfGaPyxf5p8yR-pnJhDtg,16756
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=ZuqxYkCZHoomUPuk4-HNf0bTk86il0_uRkhOTvD6T3k,24840
2684
+ invar/shell/commands/guard.py,sha256=6Ik3DJ8x6f8ZE-Y9UnzZXf2Lo2e3ZgiWUmN08cRldyE,23264
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.7.dist-info/METADATA,sha256=v4uFpW9-ouRRiRqmkIR-GRWDTDsLa5304LnjooU67sA,28595
2782
- invar_tools-1.17.7.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
2783
- invar_tools-1.17.7.dist-info/entry_points.txt,sha256=RwH_EhqgtFPsnO6RcrwrAb70Zyfb8Mh6uUtztWnUxGk,102
2784
- invar_tools-1.17.7.dist-info/licenses/LICENSE,sha256=qeFksp4H4kfTgQxPCIu3OdagXyiZcgBlVfsQ6M5oFyk,10767
2785
- invar_tools-1.17.7.dist-info/licenses/LICENSE-GPL,sha256=IvZfC6ZbP7CLjytoHVzvpDZpD-Z3R_qa1GdMdWlWQ6Q,35157
2786
- invar_tools-1.17.7.dist-info/licenses/NOTICE,sha256=joEyMyFhFY8Vd8tTJ-a3SirI0m2Sd0WjzqYt3sdcglc,2561
2787
- invar_tools-1.17.7.dist-info/RECORD,,
2781
+ invar_tools-1.17.9.dist-info/METADATA,sha256=OvB2f3yrJg-G-p5y5DRWqtFxuqI2g2gKHnuh2rJWnH4,28595
2782
+ invar_tools-1.17.9.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
2783
+ invar_tools-1.17.9.dist-info/entry_points.txt,sha256=RwH_EhqgtFPsnO6RcrwrAb70Zyfb8Mh6uUtztWnUxGk,102
2784
+ invar_tools-1.17.9.dist-info/licenses/LICENSE,sha256=qeFksp4H4kfTgQxPCIu3OdagXyiZcgBlVfsQ6M5oFyk,10767
2785
+ invar_tools-1.17.9.dist-info/licenses/LICENSE-GPL,sha256=IvZfC6ZbP7CLjytoHVzvpDZpD-Z3R_qa1GdMdWlWQ6Q,35157
2786
+ invar_tools-1.17.9.dist-info/licenses/NOTICE,sha256=joEyMyFhFY8Vd8tTJ-a3SirI0m2Sd0WjzqYt3sdcglc,2561
2787
+ invar_tools-1.17.9.dist-info/RECORD,,