invar-tools 1.2.0__py3-none-any.whl → 1.3.0__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/__init__.py +1 -0
- invar/core/contracts.py +10 -10
- invar/core/entry_points.py +105 -32
- invar/core/extraction.py +5 -6
- invar/core/format_specs.py +1 -2
- invar/core/formatter.py +6 -7
- invar/core/hypothesis_strategies.py +5 -7
- invar/core/inspect.py +1 -1
- invar/core/lambda_helpers.py +3 -3
- invar/core/models.py +7 -1
- invar/core/must_use.py +2 -1
- invar/core/parser.py +7 -4
- invar/core/postcondition_scope.py +128 -0
- invar/core/property_gen.py +8 -5
- invar/core/purity.py +3 -3
- invar/core/purity_heuristics.py +5 -9
- invar/core/references.py +8 -6
- invar/core/review_trigger.py +78 -6
- invar/core/rule_meta.py +8 -0
- invar/core/rules.py +18 -19
- invar/core/shell_analysis.py +5 -10
- invar/core/shell_architecture.py +2 -2
- invar/core/strategies.py +7 -14
- invar/core/suggestions.py +86 -0
- invar/core/sync_helpers.py +238 -0
- invar/core/tautology.py +102 -37
- invar/core/template_parser.py +467 -0
- invar/core/timeout_inference.py +4 -7
- invar/core/utils.py +13 -15
- invar/core/verification_routing.py +4 -7
- invar/mcp/server.py +100 -17
- invar/shell/commands/__init__.py +11 -0
- invar/shell/{cli.py → commands/guard.py} +94 -14
- invar/shell/{init_cmd.py → commands/init.py} +179 -27
- invar/shell/commands/merge.py +256 -0
- invar/shell/commands/sync_self.py +113 -0
- invar/shell/commands/template_sync.py +366 -0
- invar/shell/commands/update.py +48 -0
- invar/shell/config.py +12 -24
- invar/shell/coverage.py +351 -0
- invar/shell/guard_helpers.py +38 -17
- invar/shell/guard_output.py +7 -1
- invar/shell/property_tests.py +58 -22
- invar/shell/prove/__init__.py +9 -0
- invar/shell/{prove.py → prove/crosshair.py} +40 -33
- invar/shell/{prove_fallback.py → prove/hypothesis.py} +12 -4
- invar/shell/subprocess_env.py +393 -0
- invar/shell/template_engine.py +345 -0
- invar/shell/templates.py +19 -0
- invar/shell/testing.py +71 -20
- invar/templates/CLAUDE.md.template +38 -17
- invar/templates/aider.conf.yml.template +2 -2
- invar/templates/commands/{review.md → audit.md} +20 -82
- invar/templates/commands/guard.md +77 -0
- invar/templates/config/CLAUDE.md.jinja +206 -0
- invar/templates/config/context.md.jinja +92 -0
- invar/templates/config/pre-commit.yaml.jinja +44 -0
- invar/templates/context.md.template +33 -0
- invar/templates/cursorrules.template +7 -4
- invar/templates/examples/README.md +2 -0
- invar/templates/examples/conftest.py +3 -0
- invar/templates/examples/contracts.py +5 -5
- invar/templates/examples/core_shell.py +11 -7
- invar/templates/examples/workflow.md +81 -0
- invar/templates/manifest.toml +137 -0
- invar/templates/{INVAR.md → protocol/INVAR.md} +10 -7
- invar/templates/skills/develop/SKILL.md.jinja +318 -0
- invar/templates/skills/investigate/SKILL.md.jinja +106 -0
- invar/templates/skills/propose/SKILL.md.jinja +104 -0
- invar/templates/skills/review/SKILL.md.jinja +125 -0
- {invar_tools-1.2.0.dist-info → invar_tools-1.3.0.dist-info}/METADATA +108 -118
- invar_tools-1.3.0.dist-info/RECORD +95 -0
- invar_tools-1.3.0.dist-info/entry_points.txt +2 -0
- invar/contracts.py +0 -152
- invar/decorators.py +0 -94
- invar/invariant.py +0 -58
- invar/resource.py +0 -99
- invar/shell/update_cmd.py +0 -193
- invar_tools-1.2.0.dist-info/RECORD +0 -77
- invar_tools-1.2.0.dist-info/entry_points.txt +0 -2
- /invar/shell/{mutate_cmd.py → commands/mutate.py} +0 -0
- /invar/shell/{perception.py → commands/perception.py} +0 -0
- /invar/shell/{test_cmd.py → commands/test.py} +0 -0
- /invar/shell/{prove_accept.py → prove/accept.py} +0 -0
- /invar/shell/{prove_cache.py → prove/cache.py} +0 -0
- {invar_tools-1.2.0.dist-info → invar_tools-1.3.0.dist-info}/WHEEL +0 -0
- {invar_tools-1.2.0.dist-info → invar_tools-1.3.0.dist-info}/licenses/LICENSE +0 -0
- {invar_tools-1.2.0.dist-info → invar_tools-1.3.0.dist-info}/licenses/LICENSE-GPL +0 -0
- {invar_tools-1.2.0.dist-info → invar_tools-1.3.0.dist-info}/licenses/NOTICE +0 -0
invar/decorators.py
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Invar contract decorators.
|
|
3
|
-
|
|
4
|
-
Provides decorators that extend deal's contract system for additional
|
|
5
|
-
static analysis by Guard.
|
|
6
|
-
|
|
7
|
-
DX-12-B: Added @strategy for custom Hypothesis strategies.
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
|
-
from __future__ import annotations
|
|
11
|
-
|
|
12
|
-
from collections.abc import Callable
|
|
13
|
-
from typing import Any, TypeVar
|
|
14
|
-
|
|
15
|
-
F = TypeVar("F", bound=Callable)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def must_use(reason: str | None = None) -> Callable[[F], F]:
|
|
19
|
-
"""
|
|
20
|
-
Mark a function's return value as must-use.
|
|
21
|
-
|
|
22
|
-
Guard will warn if calls to this function discard the return value.
|
|
23
|
-
Inspired by Move's lack of drop ability and Rust's #[must_use].
|
|
24
|
-
|
|
25
|
-
Args:
|
|
26
|
-
reason: Explanation of why the return value must be used.
|
|
27
|
-
|
|
28
|
-
Returns:
|
|
29
|
-
A decorator that marks the function.
|
|
30
|
-
|
|
31
|
-
>>> @must_use("Error must be handled")
|
|
32
|
-
... def may_fail() -> int:
|
|
33
|
-
... return 42
|
|
34
|
-
>>> may_fail.__invar_must_use__
|
|
35
|
-
'Error must be handled'
|
|
36
|
-
|
|
37
|
-
>>> @must_use()
|
|
38
|
-
... def important() -> str:
|
|
39
|
-
... return "result"
|
|
40
|
-
>>> important.__invar_must_use__
|
|
41
|
-
'Return value must be used'
|
|
42
|
-
"""
|
|
43
|
-
|
|
44
|
-
def decorator(func: F) -> F:
|
|
45
|
-
func.__invar_must_use__ = reason or "Return value must be used" # type: ignore[attr-defined]
|
|
46
|
-
return func
|
|
47
|
-
|
|
48
|
-
return decorator
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def strategy(**param_strategies: Any) -> Callable[[F], F]:
|
|
52
|
-
"""
|
|
53
|
-
Specify custom Hypothesis strategies for function parameters.
|
|
54
|
-
|
|
55
|
-
DX-12-B: Escape hatch when automatic strategy inference fails or
|
|
56
|
-
when you need precise control over generated values.
|
|
57
|
-
|
|
58
|
-
Args:
|
|
59
|
-
**param_strategies: Mapping of parameter names to Hypothesis strategies.
|
|
60
|
-
|
|
61
|
-
Returns:
|
|
62
|
-
A decorator that attaches strategies to the function.
|
|
63
|
-
|
|
64
|
-
Examples:
|
|
65
|
-
>>> from hypothesis import strategies as st
|
|
66
|
-
>>> @strategy(x=st.floats(min_value=1e-10, max_value=1e10))
|
|
67
|
-
... def sqrt(x: float) -> float:
|
|
68
|
-
... return x ** 0.5
|
|
69
|
-
>>> hasattr(sqrt, '__invar_strategies__')
|
|
70
|
-
True
|
|
71
|
-
>>> 'x' in sqrt.__invar_strategies__
|
|
72
|
-
True
|
|
73
|
-
|
|
74
|
-
>>> # NumPy array with specific shape
|
|
75
|
-
>>> @strategy(arr="arrays(dtype=float64, shape=(10,))")
|
|
76
|
-
... def normalize(arr):
|
|
77
|
-
... return arr / arr.sum()
|
|
78
|
-
>>> 'arr' in normalize.__invar_strategies__
|
|
79
|
-
True
|
|
80
|
-
|
|
81
|
-
Note:
|
|
82
|
-
Strategies can be either:
|
|
83
|
-
- Hypothesis strategy objects (e.g., st.floats())
|
|
84
|
-
- String representations (e.g., "floats(min_value=0)")
|
|
85
|
-
|
|
86
|
-
String representations are useful when you don't want to import
|
|
87
|
-
Hypothesis at module load time.
|
|
88
|
-
"""
|
|
89
|
-
|
|
90
|
-
def decorator(func: F) -> F:
|
|
91
|
-
func.__invar_strategies__ = param_strategies # type: ignore[attr-defined]
|
|
92
|
-
return func
|
|
93
|
-
|
|
94
|
-
return decorator
|
invar/invariant.py
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Loop invariant support for Invar.
|
|
3
|
-
|
|
4
|
-
Provides runtime-checked loop invariants inspired by Dafny.
|
|
5
|
-
Checking controlled by INVAR_CHECK environment variable (default: ON).
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from __future__ import annotations
|
|
9
|
-
|
|
10
|
-
import os
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class InvariantViolation(Exception):
|
|
14
|
-
"""Raised when a loop invariant is violated."""
|
|
15
|
-
|
|
16
|
-
pass
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
# Read once at module load time - effectively a constant
|
|
20
|
-
_INVAR_CHECK = os.environ.get("INVAR_CHECK", "1") == "1"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
# @invar:allow entry_point_too_thick: False positive - .get() matches router.get pattern
|
|
24
|
-
def invariant(condition: bool, message: str = "") -> None:
|
|
25
|
-
"""
|
|
26
|
-
Assert loop invariant. Checked at runtime when INVAR_CHECK=1.
|
|
27
|
-
|
|
28
|
-
Place at the START of loop body to check condition each iteration.
|
|
29
|
-
Invariants document what must remain true throughout loop execution.
|
|
30
|
-
|
|
31
|
-
Args:
|
|
32
|
-
condition: Boolean condition that must hold
|
|
33
|
-
message: Optional message describing the invariant
|
|
34
|
-
|
|
35
|
-
Raises:
|
|
36
|
-
InvariantViolation: When condition is False and INVAR_CHECK=1
|
|
37
|
-
|
|
38
|
-
Examples:
|
|
39
|
-
>>> invariant(True) # OK
|
|
40
|
-
|
|
41
|
-
>>> invariant(True, "x is positive") # OK with message
|
|
42
|
-
|
|
43
|
-
>>> try:
|
|
44
|
-
... invariant(False, "x must be positive")
|
|
45
|
-
... except InvariantViolation as e:
|
|
46
|
-
... print(str(e))
|
|
47
|
-
Loop invariant violated: x must be positive
|
|
48
|
-
|
|
49
|
-
Typical usage in a binary search:
|
|
50
|
-
|
|
51
|
-
while lo < hi:
|
|
52
|
-
invariant(0 <= lo <= hi <= len(arr))
|
|
53
|
-
invariant(target not in arr[:lo]) # Already searched
|
|
54
|
-
...
|
|
55
|
-
"""
|
|
56
|
-
if _INVAR_CHECK and not condition:
|
|
57
|
-
msg = f"Loop invariant violated: {message}" if message else "Loop invariant violated"
|
|
58
|
-
raise InvariantViolation(msg)
|
invar/resource.py
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Resource management decorators for Invar.
|
|
3
|
-
|
|
4
|
-
Provides @must_close for marking classes that require explicit cleanup.
|
|
5
|
-
Inspired by Move language's resource semantics.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from __future__ import annotations
|
|
9
|
-
|
|
10
|
-
from typing import Any, TypeVar
|
|
11
|
-
|
|
12
|
-
T = TypeVar("T")
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class ResourceWarning(UserWarning):
|
|
16
|
-
"""Warning raised when a resource may not be properly closed."""
|
|
17
|
-
|
|
18
|
-
pass
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class MustCloseViolation(Exception):
|
|
22
|
-
"""Raised when a @must_close resource is not properly managed."""
|
|
23
|
-
|
|
24
|
-
pass
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def must_close(cls: type[T]) -> type[T]:
|
|
28
|
-
"""
|
|
29
|
-
Mark a class as a resource that must be explicitly closed.
|
|
30
|
-
|
|
31
|
-
The decorated class should have a `close()` method. The decorator:
|
|
32
|
-
1. Adds __invar_must_close__ marker for Guard detection
|
|
33
|
-
2. Adds context manager protocol if not present
|
|
34
|
-
|
|
35
|
-
Examples:
|
|
36
|
-
>>> @must_close
|
|
37
|
-
... class TempFile:
|
|
38
|
-
... def __init__(self, path: str):
|
|
39
|
-
... self.path = path
|
|
40
|
-
... self.closed = False
|
|
41
|
-
... def write(self, data: str) -> None:
|
|
42
|
-
... if self.closed:
|
|
43
|
-
... raise ValueError("File is closed")
|
|
44
|
-
... def close(self) -> None:
|
|
45
|
-
... self.closed = True
|
|
46
|
-
|
|
47
|
-
>>> # Preferred: use as context manager
|
|
48
|
-
>>> with TempFile("test.txt") as f:
|
|
49
|
-
... f.write("hello")
|
|
50
|
-
>>> f.closed
|
|
51
|
-
True
|
|
52
|
-
|
|
53
|
-
>>> # Also works: explicit close
|
|
54
|
-
>>> f2 = TempFile("test2.txt")
|
|
55
|
-
>>> f2.write("world")
|
|
56
|
-
>>> f2.close()
|
|
57
|
-
>>> f2.closed
|
|
58
|
-
True
|
|
59
|
-
"""
|
|
60
|
-
# Mark for Guard detection
|
|
61
|
-
cls.__invar_must_close__ = True # type: ignore[attr-defined]
|
|
62
|
-
|
|
63
|
-
# Add context manager protocol if not present
|
|
64
|
-
if not hasattr(cls, "__enter__"):
|
|
65
|
-
|
|
66
|
-
def __enter__(self: Any) -> Any:
|
|
67
|
-
return self
|
|
68
|
-
|
|
69
|
-
cls.__enter__ = __enter__ # type: ignore[attr-defined]
|
|
70
|
-
|
|
71
|
-
if not hasattr(cls, "__exit__"):
|
|
72
|
-
|
|
73
|
-
def __exit__(self: Any, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
74
|
-
if hasattr(self, "close") and callable(self.close):
|
|
75
|
-
self.close()
|
|
76
|
-
|
|
77
|
-
cls.__exit__ = __exit__ # type: ignore[attr-defined]
|
|
78
|
-
|
|
79
|
-
return cls
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def is_must_close(cls_or_obj: Any) -> bool:
|
|
83
|
-
"""
|
|
84
|
-
Check if a class or instance is marked with @must_close.
|
|
85
|
-
|
|
86
|
-
>>> @must_close
|
|
87
|
-
... class Resource:
|
|
88
|
-
... def close(self): pass
|
|
89
|
-
>>> is_must_close(Resource)
|
|
90
|
-
True
|
|
91
|
-
>>> is_must_close(Resource())
|
|
92
|
-
True
|
|
93
|
-
>>> class Plain: pass
|
|
94
|
-
>>> is_must_close(Plain)
|
|
95
|
-
False
|
|
96
|
-
"""
|
|
97
|
-
if isinstance(cls_or_obj, type):
|
|
98
|
-
return getattr(cls_or_obj, "__invar_must_close__", False)
|
|
99
|
-
return getattr(type(cls_or_obj), "__invar_must_close__", False)
|
invar/shell/update_cmd.py
DELETED
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Update command for Invar.
|
|
3
|
-
|
|
4
|
-
Shell module: handles updating Invar-managed files to latest version.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
import re
|
|
10
|
-
from pathlib import Path
|
|
11
|
-
|
|
12
|
-
import typer
|
|
13
|
-
from returns.result import Failure, Result, Success
|
|
14
|
-
from rich.console import Console
|
|
15
|
-
|
|
16
|
-
from invar.shell.templates import copy_examples_directory, get_template_path
|
|
17
|
-
|
|
18
|
-
console = Console()
|
|
19
|
-
|
|
20
|
-
# Version pattern: matches "v3.23" or "v3.23.1"
|
|
21
|
-
VERSION_PATTERN = re.compile(r"v(\d+)\.(\d+)(?:\.(\d+))?")
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
# @shell_orchestration: Version parsing helper for update command
|
|
25
|
-
def parse_version(text: str) -> tuple[int, int, int] | None:
|
|
26
|
-
"""
|
|
27
|
-
Parse version string from text.
|
|
28
|
-
|
|
29
|
-
>>> parse_version("Protocol v3.23")
|
|
30
|
-
(3, 23, 0)
|
|
31
|
-
>>> parse_version("v3.23.1")
|
|
32
|
-
(3, 23, 1)
|
|
33
|
-
>>> parse_version("no version here")
|
|
34
|
-
"""
|
|
35
|
-
match = VERSION_PATTERN.search(text)
|
|
36
|
-
if match:
|
|
37
|
-
major = int(match.group(1))
|
|
38
|
-
minor = int(match.group(2))
|
|
39
|
-
patch = int(match.group(3)) if match.group(3) else 0
|
|
40
|
-
return (major, minor, patch)
|
|
41
|
-
return None
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def get_current_version(path: Path) -> Result[tuple[int, int, int], str]:
|
|
45
|
-
"""Get version from current INVAR.md file."""
|
|
46
|
-
invar_md = path / "INVAR.md"
|
|
47
|
-
if not invar_md.exists():
|
|
48
|
-
return Failure("INVAR.md not found. Run 'invar init' first.")
|
|
49
|
-
|
|
50
|
-
try:
|
|
51
|
-
content = invar_md.read_text()
|
|
52
|
-
version = parse_version(content)
|
|
53
|
-
if version is None:
|
|
54
|
-
return Failure("Could not parse version from INVAR.md")
|
|
55
|
-
return Success(version)
|
|
56
|
-
except OSError as e:
|
|
57
|
-
return Failure(f"Failed to read INVAR.md: {e}")
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def get_template_version() -> Result[tuple[int, int, int], str]:
|
|
61
|
-
"""Get version from template INVAR.md."""
|
|
62
|
-
template_result = get_template_path("INVAR.md")
|
|
63
|
-
if isinstance(template_result, Failure):
|
|
64
|
-
return template_result
|
|
65
|
-
|
|
66
|
-
template_path = template_result.unwrap()
|
|
67
|
-
try:
|
|
68
|
-
content = template_path.read_text()
|
|
69
|
-
version = parse_version(content)
|
|
70
|
-
if version is None:
|
|
71
|
-
return Failure("Could not parse version from template")
|
|
72
|
-
return Success(version)
|
|
73
|
-
except OSError as e:
|
|
74
|
-
return Failure(f"Failed to read template: {e}")
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
def format_version(version: tuple[int, int, int]) -> str:
|
|
78
|
-
"""Format version tuple as string."""
|
|
79
|
-
if version[2] == 0:
|
|
80
|
-
return f"v{version[0]}.{version[1]}"
|
|
81
|
-
return f"v{version[0]}.{version[1]}.{version[2]}"
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
def update_invar_md(path: Path, console: Console) -> Result[bool, str]:
|
|
85
|
-
"""Update INVAR.md by overwriting with template."""
|
|
86
|
-
template_result = get_template_path("INVAR.md")
|
|
87
|
-
if isinstance(template_result, Failure):
|
|
88
|
-
return template_result
|
|
89
|
-
|
|
90
|
-
template_path = template_result.unwrap()
|
|
91
|
-
dest_file = path / "INVAR.md"
|
|
92
|
-
|
|
93
|
-
try:
|
|
94
|
-
dest_file.write_text(template_path.read_text())
|
|
95
|
-
return Success(True)
|
|
96
|
-
except OSError as e:
|
|
97
|
-
return Failure(f"Failed to update INVAR.md: {e}")
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
def update_examples(path: Path, console: Console) -> Result[bool, str]:
|
|
101
|
-
"""Update .invar/examples/ directory."""
|
|
102
|
-
import shutil
|
|
103
|
-
|
|
104
|
-
examples_dest = path / ".invar" / "examples"
|
|
105
|
-
|
|
106
|
-
# Remove existing examples
|
|
107
|
-
if examples_dest.exists():
|
|
108
|
-
try:
|
|
109
|
-
shutil.rmtree(examples_dest)
|
|
110
|
-
except OSError as e:
|
|
111
|
-
return Failure(f"Failed to remove old examples: {e}")
|
|
112
|
-
|
|
113
|
-
# Copy new examples
|
|
114
|
-
return copy_examples_directory(path, console)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
# @shell_complexity: Update command with template comparison
|
|
118
|
-
def update(
|
|
119
|
-
path: Path = typer.Argument(Path(), help="Project root directory"),
|
|
120
|
-
force: bool = typer.Option(
|
|
121
|
-
False, "--force", "-f", help="Update even if already at latest version"
|
|
122
|
-
),
|
|
123
|
-
check: bool = typer.Option(
|
|
124
|
-
False, "--check", help="Check for updates without applying"
|
|
125
|
-
),
|
|
126
|
-
) -> None:
|
|
127
|
-
"""
|
|
128
|
-
Update Invar-managed files to latest version.
|
|
129
|
-
|
|
130
|
-
Updates INVAR.md and .invar/examples/ from the installed python-invar package.
|
|
131
|
-
User-managed files (CLAUDE.md, .invar/context.md) are never modified.
|
|
132
|
-
|
|
133
|
-
Use --check to see if updates are available without applying them.
|
|
134
|
-
Use --force to update even if already at latest version.
|
|
135
|
-
"""
|
|
136
|
-
# Get current version
|
|
137
|
-
current_result = get_current_version(path)
|
|
138
|
-
if isinstance(current_result, Failure):
|
|
139
|
-
console.print(f"[red]Error:[/red] {current_result.failure()}")
|
|
140
|
-
raise typer.Exit(1)
|
|
141
|
-
current_version = current_result.unwrap()
|
|
142
|
-
|
|
143
|
-
# Get template version
|
|
144
|
-
template_result = get_template_version()
|
|
145
|
-
if isinstance(template_result, Failure):
|
|
146
|
-
console.print(f"[red]Error:[/red] {template_result.failure()}")
|
|
147
|
-
raise typer.Exit(1)
|
|
148
|
-
template_version = template_result.unwrap()
|
|
149
|
-
|
|
150
|
-
current_str = format_version(current_version)
|
|
151
|
-
template_str = format_version(template_version)
|
|
152
|
-
|
|
153
|
-
# Compare versions
|
|
154
|
-
needs_update = template_version > current_version
|
|
155
|
-
|
|
156
|
-
if check:
|
|
157
|
-
# Check mode: just report status
|
|
158
|
-
if needs_update:
|
|
159
|
-
console.print(f"[yellow]Update available:[/yellow] {current_str} → {template_str}")
|
|
160
|
-
else:
|
|
161
|
-
console.print(f"[green]Up to date:[/green] {current_str}")
|
|
162
|
-
return
|
|
163
|
-
|
|
164
|
-
if not needs_update and not force:
|
|
165
|
-
console.print(f"[green]Already at latest version:[/green] {current_str}")
|
|
166
|
-
console.print("[dim]Use --force to update anyway[/dim]")
|
|
167
|
-
return
|
|
168
|
-
|
|
169
|
-
# Perform update
|
|
170
|
-
console.print("\n[bold]Updating Invar files...[/bold]")
|
|
171
|
-
console.print(f" Version: {current_str} → {template_str}")
|
|
172
|
-
console.print()
|
|
173
|
-
|
|
174
|
-
# Update INVAR.md
|
|
175
|
-
result = update_invar_md(path, console)
|
|
176
|
-
if isinstance(result, Failure):
|
|
177
|
-
console.print(f"[red]Error:[/red] {result.failure()}")
|
|
178
|
-
raise typer.Exit(1)
|
|
179
|
-
console.print(f"[green]Updated[/green] INVAR.md ({template_str})")
|
|
180
|
-
|
|
181
|
-
# Update examples
|
|
182
|
-
result = update_examples(path, console)
|
|
183
|
-
if isinstance(result, Failure):
|
|
184
|
-
console.print(f"[yellow]Warning:[/yellow] {result.failure()}")
|
|
185
|
-
else:
|
|
186
|
-
console.print("[green]Updated[/green] .invar/examples/")
|
|
187
|
-
|
|
188
|
-
# Remind about user-managed files
|
|
189
|
-
console.print()
|
|
190
|
-
console.print("[dim]User-managed files unchanged:[/dim]")
|
|
191
|
-
console.print("[dim] ○ CLAUDE.md[/dim]")
|
|
192
|
-
console.print("[dim] ○ .invar/context.md[/dim]")
|
|
193
|
-
console.print("[dim] ○ pyproject.toml [tool.invar][/dim]")
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
invar/__init__.py,sha256=gvCf_VzCH3gMfq6dvsvncxicAEv7_FhgBoQTBdbqcFM,1256
|
|
2
|
-
invar/contracts.py,sha256=xAqk901hd3ep7ItxCln7Q0fID3rm47OROgEugmFnRY4,4631
|
|
3
|
-
invar/decorators.py,sha256=EaZUgt4JzlhSgRTeRmKIEb-KgU8L5Npn8-oJuMuygRU,2677
|
|
4
|
-
invar/invariant.py,sha256=V-qd2yMDmomz-eiTxvTaFbtsEwqNnNix1tpRfhSfsPo,1698
|
|
5
|
-
invar/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
-
invar/resource.py,sha256=uX3V8R8_e5uFRO6ksOt_RN62g6nmf_wzpjyr9JXNhoc,2680
|
|
7
|
-
invar/core/__init__.py,sha256=01TgQ2bqTFV4VFdksfqXYPa2WUqo-DpUWUkEcIUXFb4,218
|
|
8
|
-
invar/core/contracts.py,sha256=hdpMieO5I0BSuW4n5lLtiintTPqhutqB0UkdbkxB-Ao,19081
|
|
9
|
-
invar/core/entry_points.py,sha256=6wqofuXOXWjOKX0aSl7WM8_R8rSGkfTSGRqMcS6UVDE,9082
|
|
10
|
-
invar/core/extraction.py,sha256=NEGtpmPLIg0O_EnXGEDQcTRXudwJneysYCM3rpy0a2g,6058
|
|
11
|
-
invar/core/format_specs.py,sha256=pjr_6zl81IAOJnptr5LuPTvCwfom-c5abQ95d_vdI_g,5640
|
|
12
|
-
invar/core/format_strategies.py,sha256=LifL97JbsF8WEkVNmQpq2htyFUC3pW21myAjtRGpSxU,5774
|
|
13
|
-
invar/core/formatter.py,sha256=wB4J9b-CMoooeYzPbRR2UTo5GuDueSzEXybyeVzdI14,10566
|
|
14
|
-
invar/core/hypothesis_strategies.py,sha256=KTk0X_pJ-9AVduYm_xiv6AdGtIAd01VjOfk2Z4mXYKA,16468
|
|
15
|
-
invar/core/inspect.py,sha256=5OX-c3BJ7GEiJWqIY77GYoP0Ox15cTcrmlNfjh9Hvfg,4380
|
|
16
|
-
invar/core/lambda_helpers.py,sha256=pt_5GezT3kQoOiyJDAdLDLzRl-xEN1yWMQ7NYAVuWzI,6371
|
|
17
|
-
invar/core/models.py,sha256=K6_-t7mOXa8k3TQK2aR-qm2G0lLHu3vmJpilGaTzTlc,9653
|
|
18
|
-
invar/core/must_use.py,sha256=R8VsIIWqLXtVov1kf5RqiKfkvqI1o2Bn4rKKNo5KFSw,5376
|
|
19
|
-
invar/core/parser.py,sha256=hlxLtqmS8eaXmcLO5BnD7HfTfRpxbpfTBmFSHGW2tzc,9079
|
|
20
|
-
invar/core/property_gen.py,sha256=PpPYcKpPHqvx0XmCb8ktliigcFkxvKPbhAXm8qksokE,13990
|
|
21
|
-
invar/core/purity.py,sha256=Wi_6wRLshSFLzaNxJfjoa0-4frCWJkQJliPpQfdUeq8,11980
|
|
22
|
-
invar/core/purity_heuristics.py,sha256=dcdYqJJh-RLZHZR_G1j1gViH3Cqe37RfE3leaA2MU34,4748
|
|
23
|
-
invar/core/references.py,sha256=iOIivmQ3Qcp38XiFpzaa7EZa01Ab75Q-G7KR2PRn-tI,6107
|
|
24
|
-
invar/core/review_trigger.py,sha256=sCsBX9YOv9xnEhJqVJDhyAE5YzIC3p1MFrKbjCiD3Hk,11377
|
|
25
|
-
invar/core/rule_meta.py,sha256=5bTrr6I7-Qi-ubqUQi-3Hva0E3mJBc-MadV0K77dwWs,9772
|
|
26
|
-
invar/core/rules.py,sha256=3HY9A_9sVw72XNTA7CV8gWSG5xBqBqfS1v_agQzIj_s,19699
|
|
27
|
-
invar/core/shell_analysis.py,sha256=IumoT0qjfZ4ElYBq6etwQL7ETSFS-a3sJnPKgOvUprg,6676
|
|
28
|
-
invar/core/shell_architecture.py,sha256=41JE0hM8-PCSLyNlJB0fy6l1zPSnd9t_r3HxUTqZ4qQ,6521
|
|
29
|
-
invar/core/strategies.py,sha256=AhiEJl-5SxXKSmprpeT4OdCmEk7dS3Di7Eb90I7QENQ,8797
|
|
30
|
-
invar/core/suggestions.py,sha256=Y3b9hprG4M2fCNGgo2fNfMox4TDPKD9nyfmzCsBbF-Q,11500
|
|
31
|
-
invar/core/tautology.py,sha256=Vk_FSP8q7uHeR5ZcyE28NWqxq1_WG-sJXwyKiClxdv4,5610
|
|
32
|
-
invar/core/timeout_inference.py,sha256=NOLwNuadLAKvGTtAnd3qppZikVfKlo2ZPxHFVg4Vupk,3544
|
|
33
|
-
invar/core/utils.py,sha256=aImcrQaiz1LkH_eJIgPAgl0HSPI3UBeE-tdUu92bIFc,14481
|
|
34
|
-
invar/core/verification_routing.py,sha256=09qqOac3DjOWACmRAY4CJiOuri7J2cN2kXztBJ9ch0I,5126
|
|
35
|
-
invar/mcp/__init__.py,sha256=n3S7QwMjSMqOMT8cI2jf9E0yZPjKmBOJyIYhq4WZ8TQ,226
|
|
36
|
-
invar/mcp/__main__.py,sha256=ZcIT2U6xUyGOWucl4jq422BDE3lRLjqyxb9pFylRBdk,219
|
|
37
|
-
invar/mcp/server.py,sha256=EYXZZQ8IPLVer0Cv5vw8wHu4OuLQxqK6uRQo1byE45k,8832
|
|
38
|
-
invar/shell/__init__.py,sha256=FFw1mNbh_97PeKPcHIqQpQ7mw-JoIvyLM1yOdxLw5uk,204
|
|
39
|
-
invar/shell/cli.py,sha256=wDFQsLnO--S1Au4AKo3ayNXEJY6JJhv-DdIWnk9tcOk,13674
|
|
40
|
-
invar/shell/config.py,sha256=TxCuEQIjKbutpSCU2SxY7H7jPA6CIWNL9QDCe2KjNqc,16163
|
|
41
|
-
invar/shell/fs.py,sha256=wVD7DPWsCIJXuTyY_pi-5_LS82mXRdn_grJCOLn9zpU,3699
|
|
42
|
-
invar/shell/git.py,sha256=s6RQxEDQuLrmK3mru88EoYP8__4hiFW8AozlcxmY47E,2784
|
|
43
|
-
invar/shell/guard_helpers.py,sha256=XPWpWUovKDnD100WUop7PqdOzxODXvE84ALPng2ujHo,14007
|
|
44
|
-
invar/shell/guard_output.py,sha256=pxOTp7jGfpjOwOX2FaATDcmM_rsaLQ8CrZDqEMa0zSQ,11523
|
|
45
|
-
invar/shell/init_cmd.py,sha256=a0Lk45wP1RF71jyVFDSrBTJgPjIeSstrKWbpCJiPPw4,10622
|
|
46
|
-
invar/shell/mcp_config.py,sha256=-hC7Y5BGuVs285b6gBARk7ZyzVxHwPgXSyt_GoN0jfs,4580
|
|
47
|
-
invar/shell/mutate_cmd.py,sha256=GwemiO6LlbGCBEQsBFnzZuKhF-wIMEl79GAMnKUWc8U,5765
|
|
48
|
-
invar/shell/mutation.py,sha256=Lfyk2b8j8-hxAq-iwAgQeOhr7Ci6c5tRF1TXe3CxQCs,8914
|
|
49
|
-
invar/shell/perception.py,sha256=TyH_HpqyKkmE3-zcU4YyBG8ghwJaSFeRC-OQMVBDTbQ,3837
|
|
50
|
-
invar/shell/property_tests.py,sha256=1ycvPXA0_2cnJP5lIP4HukL8VQshWcc7xJ3Md-iacLQ,7408
|
|
51
|
-
invar/shell/prove.py,sha256=jPxLKgxAjako2dqgcxwe2bcfdbZA_Cj1acayTWRVtrg,15644
|
|
52
|
-
invar/shell/prove_accept.py,sha256=cnY_6jzU1EBnpLF8-zWUWcXiSXtCwxPsXEYXsSVPG38,3717
|
|
53
|
-
invar/shell/prove_cache.py,sha256=jbNdrvfLjvK7S0iqugErqeabb4YIbQuwIlcSRyCKbcg,4105
|
|
54
|
-
invar/shell/prove_fallback.py,sha256=UOhD6c6YIVcR1Q3wG6W9ySMnuJXZ6x8hTL6O1HG5zrU,9628
|
|
55
|
-
invar/shell/templates.py,sha256=joJA6ASSCGi8GcApPY6kxoSbrFElpsxVhHVzJGAnsXc,15083
|
|
56
|
-
invar/shell/test_cmd.py,sha256=DKnlSbUydKq6skHLLKhZGg42qTAKtiLxrTU3oyFDmAo,4189
|
|
57
|
-
invar/shell/testing.py,sha256=2aG_4-VTRmvFKGz7IOdY_o0QqP29fs5yUgV00QUL0Js,8766
|
|
58
|
-
invar/shell/update_cmd.py,sha256=PbXN0UwmUcFmy0IKFmyO9oBXEjwy77gZG3j7gFJA8lA,6317
|
|
59
|
-
invar/templates/CLAUDE.md.template,sha256=dBZkhmBQhSZ-uEDWozlZsrrt4Qo8xhzx4XYJiJ9gi8A,2943
|
|
60
|
-
invar/templates/INVAR.md,sha256=BDihvntGUXHeaSlwVNA8HSTidfQFH4dCqmzZtEIPPWo,6635
|
|
61
|
-
invar/templates/__init__.py,sha256=cb3ht8KPK5oBn5oG6HsTznujmo9WriJ_P--fVxJwycc,45
|
|
62
|
-
invar/templates/aider.conf.yml.template,sha256=hPWt5-_q53Ex00v-fNL-80db8wNdxT2q8PLbpbO6GQw,842
|
|
63
|
-
invar/templates/context.md.template,sha256=D4mIAbj33io5_xe0uTcZh5JuSfpzXpv_t5h6k0B6UIA,1168
|
|
64
|
-
invar/templates/cursorrules.template,sha256=AYZumL5oSXuvfs3HZ6fziDvbaAWaDCvGf-CFSXmFHsg,987
|
|
65
|
-
invar/templates/pre-commit-config.yaml.template,sha256=2qWY3E8iDUqi85jE_X7y0atE88YOlL5IZ93wkjCgQGo,1737
|
|
66
|
-
invar/templates/proposal.md.template,sha256=UP7SpQ7gk8jVlHGLQCSQ5c-kCj1DBQEz8M-vEStK77I,1573
|
|
67
|
-
invar/templates/commands/review.md,sha256=fIOt_aFxn5KlDptJmE1USlmNbJs-_5sZgn6_ABl-S6w,6037
|
|
68
|
-
invar/templates/examples/README.md,sha256=dkS5QI1mzDsQp23dJGCoTZzM_1SQg1vos37sZAS-Wsc,485
|
|
69
|
-
invar/templates/examples/contracts.py,sha256=VFlu9N1Zg8YcTO3UYeSvh1HCBPGZWFktpQN_jtE8ue0,3204
|
|
70
|
-
invar/templates/examples/core_shell.py,sha256=HhTAxo3M7MhiQn05I3BClx43pshtxOnEjtVh974PFXc,3820
|
|
71
|
-
invar_tools-1.2.0.dist-info/METADATA,sha256=WbedCOQ1a6lN9Px4qJScVlzd5xLnnvzHDdC7Stymg1g,11534
|
|
72
|
-
invar_tools-1.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
73
|
-
invar_tools-1.2.0.dist-info/entry_points.txt,sha256=C7R-M4tvNd6UHDOcG98RQVS9plLyJ-c939z8qLLzTj8,46
|
|
74
|
-
invar_tools-1.2.0.dist-info/licenses/LICENSE,sha256=qeFksp4H4kfTgQxPCIu3OdagXyiZcgBlVfsQ6M5oFyk,10767
|
|
75
|
-
invar_tools-1.2.0.dist-info/licenses/LICENSE-GPL,sha256=IvZfC6ZbP7CLjytoHVzvpDZpD-Z3R_qa1GdMdWlWQ6Q,35157
|
|
76
|
-
invar_tools-1.2.0.dist-info/licenses/NOTICE,sha256=joEyMyFhFY8Vd8tTJ-a3SirI0m2Sd0WjzqYt3sdcglc,2561
|
|
77
|
-
invar_tools-1.2.0.dist-info/RECORD,,
|
|
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
|