invar-tools 1.17.12__py3-none-any.whl → 1.17.24__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.
@@ -11,6 +11,7 @@ This module provides three phases of dependency injection:
11
11
  from __future__ import annotations
12
12
 
13
13
  import os
14
+ import shutil
14
15
  import subprocess
15
16
  import sys
16
17
  from datetime import datetime, timedelta
@@ -24,6 +25,7 @@ __all__ = [
24
25
  "detect_project_python_with_invar",
25
26
  "detect_project_venv",
26
27
  "find_site_packages",
28
+ "get_uvx_respawn_command",
27
29
  "get_venv_python_version",
28
30
  "maybe_show_upgrade_prompt",
29
31
  "should_respawn",
@@ -135,13 +137,17 @@ def build_subprocess_env(cwd: Path | None = None) -> dict[str, str]:
135
137
  if site_packages is None:
136
138
  return env
137
139
 
138
- # Prepend to PYTHONPATH (project packages have priority)
139
140
  current = env.get("PYTHONPATH", "")
140
141
  separator = ";" if os.name == "nt" else ":"
141
- if current:
142
- env["PYTHONPATH"] = f"{site_packages}{separator}{current}"
143
- else:
144
- env["PYTHONPATH"] = str(site_packages)
142
+
143
+ src_dir = project_root / "src"
144
+ prefix_parts: list[str] = []
145
+ if src_dir.exists():
146
+ prefix_parts.append(str(src_dir))
147
+ prefix_parts.append(str(site_packages))
148
+
149
+ prefix = separator.join(prefix_parts)
150
+ env["PYTHONPATH"] = f"{prefix}{separator}{current}" if current else prefix
145
151
 
146
152
  return env
147
153
 
@@ -196,6 +202,53 @@ def detect_project_python_with_invar(cwd: Path) -> Path | None:
196
202
  return None
197
203
 
198
204
 
205
+ def _detect_venv_python(venv: Path) -> Path | None:
206
+ python_path = venv / "bin" / "python"
207
+ if not python_path.exists():
208
+ python_path = venv / "Scripts" / "python.exe"
209
+ return python_path if python_path.exists() else None
210
+
211
+
212
+ def get_uvx_respawn_command(
213
+ project_root: Path,
214
+ argv: list[str],
215
+ tool_name: str,
216
+ invar_tools_version: str,
217
+ ) -> list[str] | None:
218
+ if os.environ.get("INVAR_UVX_RESPAWNED") == "1":
219
+ return None
220
+
221
+ venv = detect_project_venv(project_root)
222
+ if venv is None:
223
+ return None
224
+
225
+ venv_version = get_venv_python_version(venv)
226
+ if venv_version is None:
227
+ return None
228
+
229
+ current_version = (sys.version_info.major, sys.version_info.minor)
230
+ if venv_version == current_version:
231
+ return None
232
+
233
+ uvx_path = shutil.which("uvx")
234
+ if uvx_path is None:
235
+ return None
236
+
237
+ project_python = _detect_venv_python(venv)
238
+ if project_python is None:
239
+ return None
240
+
241
+ return [
242
+ uvx_path,
243
+ "--python",
244
+ str(project_python),
245
+ "--from",
246
+ f"invar-tools=={invar_tools_version}",
247
+ tool_name,
248
+ *argv,
249
+ ]
250
+
251
+
199
252
  @pre(lambda cwd: isinstance(cwd, Path))
200
253
  def should_respawn(cwd: Path) -> tuple[bool, Path | None]:
201
254
  """Check if MCP server should re-spawn with project Python.
invar/shell/testing.py CHANGED
@@ -119,6 +119,7 @@ def run_doctests_on_files(
119
119
  verbose: bool = False,
120
120
  timeout: int = 60,
121
121
  collect_coverage: bool = False,
122
+ cwd: Path | None = None,
122
123
  ) -> Result[dict, str]:
123
124
  """
124
125
  Run doctests on a list of Python files.
@@ -143,17 +144,16 @@ def run_doctests_on_files(
143
144
  parts = f.parts
144
145
  # Check for consecutive "templates/examples" or ".invar/examples"
145
146
  for i in range(len(parts) - 1):
146
- if (parts[i] == "templates" and parts[i + 1] == "examples") or \
147
- (parts[i] == ".invar" and parts[i + 1] == "examples"):
147
+ if (parts[i] == "templates" and parts[i + 1] == "examples") or (
148
+ parts[i] == ".invar" and parts[i + 1] == "examples"
149
+ ):
148
150
  return True
149
151
  return False
150
152
 
151
153
  py_files = [
152
- f for f in files
153
- if f.suffix == ".py"
154
- and f.exists()
155
- and f.name != "conftest.py"
156
- and not is_excluded(f)
154
+ f
155
+ for f in files
156
+ if f.suffix == ".py" and f.exists() and f.name != "conftest.py" and not is_excluded(f)
157
157
  ]
158
158
  if not py_files:
159
159
  return Success({"status": "skipped", "reason": "no Python files", "files": []})
@@ -162,16 +162,26 @@ def run_doctests_on_files(
162
162
  if collect_coverage:
163
163
  # Use coverage run to wrap pytest
164
164
  cmd = [
165
- sys.executable, "-m", "coverage", "run",
165
+ sys.executable,
166
+ "-m",
167
+ "coverage",
168
+ "run",
166
169
  "--branch", # Enable branch coverage
167
170
  "--parallel-mode", # For merging with hypothesis later
168
- "-m", "pytest",
169
- "--doctest-modules", "-x", "--tb=short",
171
+ "-m",
172
+ "pytest",
173
+ "--doctest-modules",
174
+ "-x",
175
+ "--tb=short",
170
176
  ]
171
177
  else:
172
178
  cmd = [
173
- sys.executable, "-m", "pytest",
174
- "--doctest-modules", "-x", "--tb=short",
179
+ sys.executable,
180
+ "-m",
181
+ "pytest",
182
+ "--doctest-modules",
183
+ "-x",
184
+ "--tb=short",
175
185
  ]
176
186
  cmd.extend(str(f) for f in py_files)
177
187
  if verbose:
@@ -184,18 +194,21 @@ def run_doctests_on_files(
184
194
  capture_output=True,
185
195
  text=True,
186
196
  timeout=timeout,
187
- env=build_subprocess_env(),
197
+ cwd=str(cwd) if cwd is not None else None,
198
+ env=build_subprocess_env(cwd=cwd),
188
199
  )
189
200
  # Pytest exit codes: 0=passed, 5=no tests collected (also OK)
190
201
  is_passed = result.returncode in (0, 5)
191
- return Success({
192
- "status": "passed" if is_passed else "failed",
193
- "files": [str(f) for f in py_files],
194
- "exit_code": result.returncode,
195
- "stdout": result.stdout,
196
- "stderr": result.stderr,
197
- "coverage_collected": collect_coverage, # DX-37: Flag for caller
198
- })
202
+ return Success(
203
+ {
204
+ "status": "passed" if is_passed else "failed",
205
+ "files": [str(f) for f in py_files],
206
+ "exit_code": result.returncode,
207
+ "stdout": result.stdout,
208
+ "stderr": result.stderr,
209
+ "coverage_collected": collect_coverage, # DX-37: Flag for caller
210
+ }
211
+ )
199
212
  except subprocess.TimeoutExpired:
200
213
  return Failure(f"Doctest timeout ({timeout}s)")
201
214
  except Exception as e:
@@ -204,7 +217,11 @@ def run_doctests_on_files(
204
217
 
205
218
  # @shell_complexity: Property test orchestration with subprocess
206
219
  def run_test(
207
- target: str, json_output: bool = False, verbose: bool = False, timeout: int = 300
220
+ target: str,
221
+ json_output: bool = False,
222
+ verbose: bool = False,
223
+ timeout: int = 300,
224
+ cwd: Path | None = None,
208
225
  ) -> Result[dict, str]:
209
226
  """
210
227
  Run property-based tests using Hypothesis via deal.cases.
@@ -225,20 +242,25 @@ def run_test(
225
242
  return Failure(f"Target must be a Python file: {target}")
226
243
 
227
244
  cmd = [
228
- sys.executable, "-m", "pytest",
229
- str(target_path), "--doctest-modules", "-x", "--tb=short",
245
+ sys.executable,
246
+ "-m",
247
+ "pytest",
248
+ str(target_path),
249
+ "--doctest-modules",
250
+ "-x",
251
+ "--tb=short",
230
252
  ]
231
253
  if verbose:
232
254
  cmd.append("-v")
233
255
 
234
256
  try:
235
- # DX-52: Inject project venv site-packages for uvx compatibility
236
257
  result = subprocess.run(
237
258
  cmd,
238
259
  capture_output=True,
239
260
  text=True,
240
261
  timeout=timeout,
241
- env=build_subprocess_env(),
262
+ cwd=str(cwd) if cwd is not None else None,
263
+ env=build_subprocess_env(cwd=cwd),
242
264
  )
243
265
  test_result = {
244
266
  "status": "passed" if result.returncode == 0 else "failed",
@@ -274,6 +296,7 @@ def run_verify(
274
296
  json_output: bool = False,
275
297
  total_timeout: int = 300,
276
298
  per_condition_timeout: int = 30,
299
+ cwd: Path | None = None,
277
300
  ) -> Result[dict, str]:
278
301
  """
279
302
  Run symbolic verification using CrossHair.
@@ -302,23 +325,28 @@ def run_verify(
302
325
  return Failure(f"Target must be a Python file: {target}")
303
326
 
304
327
  cmd = [
305
- sys.executable, "-m", "crosshair", "check",
306
- str(target_path), f"--per_condition_timeout={per_condition_timeout}",
328
+ sys.executable,
329
+ "-m",
330
+ "crosshair",
331
+ "check",
332
+ str(target_path),
333
+ f"--per_condition_timeout={per_condition_timeout}",
307
334
  ]
308
335
 
309
336
  try:
310
- # DX-52: Inject project venv site-packages for uvx compatibility
311
337
  result = subprocess.run(
312
338
  cmd,
313
339
  capture_output=True,
314
340
  text=True,
315
341
  timeout=total_timeout,
316
- env=build_subprocess_env(),
342
+ cwd=str(cwd) if cwd is not None else None,
343
+ env=build_subprocess_env(cwd=cwd),
317
344
  )
318
345
 
319
346
  # CrossHair format: "file:line: error: Err when calling func(...)"
320
347
  counterexamples = [
321
- line.strip() for line in result.stdout.split("\n")
348
+ line.strip()
349
+ for line in result.stdout.split("\n")
322
350
  if ": error:" in line.lower() or "counterexample" in line.lower()
323
351
  ]
324
352
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: invar-tools
3
- Version: 1.17.12
3
+ Version: 1.17.24
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
@@ -23,13 +23,14 @@ Classifier: Typing :: Typed
23
23
  Requires-Python: >=3.11
24
24
  Requires-Dist: crosshair-tool>=0.0.60
25
25
  Requires-Dist: hypothesis>=6.0
26
- Requires-Dist: invar-runtime>=1.0
26
+ Requires-Dist: invar-runtime>=1.3.0
27
27
  Requires-Dist: jedi>=0.19
28
28
  Requires-Dist: jinja2>=3.0
29
29
  Requires-Dist: markdown-it-py>=3.0
30
30
  Requires-Dist: mcp>=1.0
31
31
  Requires-Dist: pre-commit>=3.0
32
32
  Requires-Dist: pydantic>=2.0
33
+ Requires-Dist: pytest>=7.0
33
34
  Requires-Dist: questionary>=2.0
34
35
  Requires-Dist: returns>=0.20
35
36
  Requires-Dist: rich>=13.0
@@ -38,7 +39,6 @@ Provides-Extra: dev
38
39
  Requires-Dist: coverage[toml]>=7.0; extra == 'dev'
39
40
  Requires-Dist: mypy>=1.0; extra == 'dev'
40
41
  Requires-Dist: pytest-cov>=4.0; extra == 'dev'
41
- Requires-Dist: pytest>=7.0; extra == 'dev'
42
42
  Requires-Dist: ruff>=0.1; extra == 'dev'
43
43
  Description-Content-Type: text/markdown
44
44
 
@@ -14,7 +14,7 @@ invar/core/hypothesis_strategies.py,sha256=_MfjG7KxkmJvuPsczr_1JayR_YmiDzU2jJ8fQ
14
14
  invar/core/inspect.py,sha256=l1knohwpLRHSNySPUjyeBHJusnU0vYiQGj4dMVgQZIo,4381
15
15
  invar/core/lambda_helpers.py,sha256=Ap1y7N0wpgCgPHwrs2pd7zD9Qq4Ptfd2iTliprXIkME,6457
16
16
  invar/core/language.py,sha256=aGUcrq--eQtAjb5bYE40eFmDhRs_EbBNGQ1sBYgTdt0,2637
17
- invar/core/models.py,sha256=DP4nfQ5O58eg1NCsBdw9OsQrVY1nSvo9GIlRdV2QjkU,16758
17
+ invar/core/models.py,sha256=WLHCd9M8anw5mALTMboTjxt5PcXO7eJajiYA8b6FGHk,16881
18
18
  invar/core/must_use.py,sha256=7HnnbT53lb4dOT-1mL64pz0JbQYytuw4eejNVe7iWKY,5496
19
19
  invar/core/parser.py,sha256=ucVpGziVzUvbkXT1n_SgOrYdStDEcNBqLuRGqK3_M5g,9205
20
20
  invar/core/postcondition_scope.py,sha256=ykjVNqZZ1zItBmI7ebgmLW5vFGE-vpaLRTvSgWaJMgM,5245
@@ -24,7 +24,7 @@ invar/core/purity_heuristics.py,sha256=vsgphC1XPIFtsoLB0xvp--AyaJHqlh83LyKXYda4p
24
24
  invar/core/references.py,sha256=64yGIdj9vL72Y4uUhJsi9pztZkuMnLN-7OcOziyxYMo,6339
25
25
  invar/core/review_trigger.py,sha256=4GGHUmgbVsQJAob4OO6A8G7KrLcNMwNOuqHiT6Jc7cs,14085
26
26
  invar/core/rule_meta.py,sha256=il_KUTjSlW1MOVgLguuLDS9wEdyqUe3CDvUx4gQjACo,10180
27
- invar/core/rules.py,sha256=XnFEDm4PSblGiCG14bfnhl-OcMmNsy7Slx7DAUbfaF4,22226
27
+ invar/core/rules.py,sha256=y_NIpO8GAcw8WNPZjbJniu9FU7ofgfn3kZ_keexTqA8,23481
28
28
  invar/core/shell_analysis.py,sha256=i2A9SMqBI3Rb4Ai0QNTM7awIkSJIY6yZJVWS72lv0bY,6457
29
29
  invar/core/shell_architecture.py,sha256=98EVdBFIs8tO-i9jKuzdmv7fLB4PKnyI-vKh5lxnB98,6538
30
30
  invar/core/strategies.py,sha256=2DPl0z2p_CBNd4RlSbZzTeAy6Dq6cpCiBCB2p5qHHkk,8798
@@ -50,13 +50,13 @@ 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
57
57
  invar/node_tools/__init__.py,sha256=HzILh3jtP28Lm2jZwss1SY65ECxbtw2J2uFpXQA6Y94,1740
58
58
  invar/node_tools/ts-query.js,sha256=fEc_f0JT_Mb18dEoA4_vJoazvd7Lqv_rsed4eHSAbCg,13303
59
- invar/node_tools/eslint-plugin/cli.js,sha256=ImpQUercYO3R8BWkV7k1xxYfWxwqPOBCxyWxZD2K890,6952
59
+ invar/node_tools/eslint-plugin/cli.js,sha256=jgRHHHDRbyQiVbIWgmaxlsA9PlDvonWFEEYf9_XPaZ4,8481
60
60
  invar/node_tools/eslint-plugin/cli.js.map,sha256=AZt_DYAD6NnHGjR6oxXfGYaOHc6-vfDs0ENvYsbw5Ro,4489
61
61
  invar/node_tools/eslint-plugin/index.js,sha256=atrI37dn1yDznQrgKY-1xIhLnCdvJQTU53123ZI_9J4,5132
62
62
  invar/node_tools/eslint-plugin/index.js.map,sha256=MlFuNso9sZ4v9ggGJLyMq4Pt_JdEzP1OyouyCCki1wA,2344
@@ -2616,7 +2616,7 @@ invar/node_tools/eslint-plugin/rules/require-complete-validation.js,sha256=ZGeAE
2616
2616
  invar/node_tools/eslint-plugin/rules/require-complete-validation.js.map,sha256=UhEr4fql0TObHt3ya8DydqbFlig5CSDFKIIJZSf74rA,2497
2617
2617
  invar/node_tools/eslint-plugin/rules/require-jsdoc-example.js,sha256=Uql3FKdnTHFBMh_OkwGuOdMBzf4k94QHOaWo9frAcSM,4488
2618
2618
  invar/node_tools/eslint-plugin/rules/require-jsdoc-example.js.map,sha256=00cKhvu890f-8UXPZcEAaX44IjCxxw12E4l_gsi93JM,2960
2619
- invar/node_tools/eslint-plugin/rules/require-schema-validation.js,sha256=gdJaRr9-j8ZmUr8-u_MnafeQ4I92OoIq3RzUXeqrpBg,12372
2619
+ invar/node_tools/eslint-plugin/rules/require-schema-validation.js,sha256=6kRnMrTBuqeHmNOdBz78jZ0WBZgTmIUBoR_0ozWlK4k,12155
2620
2620
  invar/node_tools/eslint-plugin/rules/require-schema-validation.js.map,sha256=akTSZQq-OC6ZvwMklIBRaxsesJ9w6GHaPhRTJ_ihmaE,8211
2621
2621
  invar/node_tools/eslint-plugin/rules/shell-complexity.js,sha256=Bhc9i7ILYMNNAUvxf9l7PlwhC_Neb69Jl1nUNABu2jg,11260
2622
2622
  invar/node_tools/eslint-plugin/rules/shell-complexity.js.map,sha256=0oJoDxXpXg2VTrwGMa-UDZ6te15Sv9vq-LgwWjJB5Q4,7140
@@ -2657,31 +2657,31 @@ invar/node_tools/quick-check/cli.js,sha256=dwV3hdJleFQga2cKUn3PPfQDvvujhzKdjQcIv
2657
2657
  invar/node_tools/ts-analyzer/cli.js,sha256=SvZ6HyjmobpP8NAZqXFiy8BwH_t5Hb17Ytar_18udaQ,4092887
2658
2658
  invar/shell/__init__.py,sha256=FFw1mNbh_97PeKPcHIqQpQ7mw-JoIvyLM1yOdxLw5uk,204
2659
2659
  invar/shell/claude_hooks.py,sha256=hV4DfG3cVng32f0Rxoo070tliVlYFC5v9slIWEbAD7E,18899
2660
- invar/shell/config.py,sha256=6-kbo6--SxfROXoyU-v7InSLR8f_U1Mar_xEOdCXFkY,17633
2660
+ invar/shell/config.py,sha256=Q8HI_bYz3mwKTAuG4JYjiXt0kXCdQdWZ0hZn3xy9r-M,18570
2661
2661
  invar/shell/contract_coverage.py,sha256=81OQkQqUVYUKytG5aiJyRK62gwh9UzbSG926vkvFTc8,12088
2662
2662
  invar/shell/coverage.py,sha256=m01o898IFIdBztEBQLwwL1Vt5PWrpUntO4lv4nWEkls,11344
2663
2663
  invar/shell/doc_tools.py,sha256=16gvo_ay9-_EK6lX16WkiRGg4OfTAKK_i0ucQkE7lbI,15149
2664
2664
  invar/shell/fs.py,sha256=ctqU-EX0NnKC4txudRCRpbWxWSgBZTInXMeOUnl3IM0,6196
2665
- invar/shell/git.py,sha256=s6RQxEDQuLrmK3mru88EoYP8__4hiFW8AozlcxmY47E,2784
2666
- invar/shell/guard_helpers.py,sha256=QeYgbW0lgUa9Z_RCjAMG7UJdiMzz5cW48Lb2u-qgQi8,15114
2665
+ invar/shell/git.py,sha256=R-ynlYa65xtCdnNjHeu42uPyrqoo9KZDzl7BZUW0oWU,2866
2666
+ invar/shell/guard_helpers.py,sha256=IWiQEhDXnvN7QizGFrTNgfkSRN3ZVRF66gj-CeTjuJE,17261
2667
2667
  invar/shell/guard_output.py,sha256=v3gG5P-_47nIFo8eAMKwdA_hLf2KZ0cQ-45Z6JjKp4w,12520
2668
2668
  invar/shell/mcp_config.py,sha256=-hC7Y5BGuVs285b6gBARk7ZyzVxHwPgXSyt_GoN0jfs,4580
2669
2669
  invar/shell/mutation.py,sha256=Lfyk2b8j8-hxAq-iwAgQeOhr7Ci6c5tRF1TXe3CxQCs,8914
2670
2670
  invar/shell/pattern_integration.py,sha256=pRcjfq3NvMW_tvQCnaXZnD1k5AVEWK8CYOE2jN6VTro,7842
2671
2671
  invar/shell/pi_hooks.py,sha256=ulZc1sP8mTRJTBsjwFHQzUgg-h8ajRIMp7iF1Y4UUtw,6885
2672
2672
  invar/shell/pi_tools.py,sha256=a3ACDDXykFV8fUB5UpBmgMvppwkmLvT1k_BWm0IY47k,4068
2673
- invar/shell/property_tests.py,sha256=N9JreyH5PqR89oF5yLcX7ZAV-Koyg5BKo-J05-GUPsA,9109
2673
+ invar/shell/property_tests.py,sha256=qt0CP5RH9Md2ZZV64ziNsjQ_-x0onCYtZwbQfqw9gbY,12586
2674
2674
  invar/shell/py_refs.py,sha256=Vjz50lmt9prDBcBv4nkkODdiJ7_DKu5zO4UPZBjAfmM,4638
2675
2675
  invar/shell/skill_manager.py,sha256=Mr7Mh9rxPSKSAOTJCAM5ZHiG5nfUf6KQVCuD4LBNHSI,12440
2676
- invar/shell/subprocess_env.py,sha256=9oXl3eMEbzLsFEgMHqobEw6oW_wV0qMEP7pklwm58Pw,11453
2676
+ invar/shell/subprocess_env.py,sha256=hendEERSyAG4a8UFhYfPtOAlfspVRB03aVCYpj3uqk4,12745
2677
2677
  invar/shell/template_engine.py,sha256=eNKMz7R8g9Xp3_1TGx-QH137jf52E0u3KaVcnotu1Tg,12056
2678
2678
  invar/shell/templates.py,sha256=ilhGysbUcdkUFqPgv6ySVmKI3imS_cwYNCWTCdyb5cY,15407
2679
- invar/shell/testing.py,sha256=rTNBH0Okh2qtG9ohSXOz487baQ2gXrWT3s_WECW3HJs,11143
2679
+ invar/shell/testing.py,sha256=mRk22-B3pkNWqPxQ_kjcnK8S6X9G6BQQd3bgdxS0zl8,11534
2680
2680
  invar/shell/ts_compiler.py,sha256=nA8brnOhThj9J_J3vAEGjDsM4NjbWQ_eX8Yf4pHPOgk,6672
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=jlAExTLnOVUI_FCIViLHlqo1B_hVVC91h-Ac6xfv6HA,24433
2684
+ invar/shell/commands/guard.py,sha256=iGqy6F41ojTYVk8EvGCbRMHDBEcS0tEu3kV0DRjdyqg,26105
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
@@ -2696,8 +2696,8 @@ invar/shell/commands/update.py,sha256=-NNQQScEb_0bV1B8YFxTjpUECk9c8dGAiNVRc64nWh
2696
2696
  invar/shell/prove/__init__.py,sha256=ZqlbmyMFJf6yAle8634jFuPRv8wNvHps8loMlOJyf8A,240
2697
2697
  invar/shell/prove/accept.py,sha256=cnY_6jzU1EBnpLF8-zWUWcXiSXtCwxPsXEYXsSVPG38,3717
2698
2698
  invar/shell/prove/cache.py,sha256=jbNdrvfLjvK7S0iqugErqeabb4YIbQuwIlcSRyCKbcg,4105
2699
- invar/shell/prove/crosshair.py,sha256=XhJDsQWIriX9SuqeflUYvJgp9gJTDH7I7Uka6zjNzZ0,16734
2700
- invar/shell/prove/guard_ts.py,sha256=OKw5Pfr4PbTVnFAixe5WhScyxWr4FiHabSTvjiFqLGs,36948
2699
+ invar/shell/prove/crosshair.py,sha256=w0p7aT_TscHpAOKKZtlbta46O9EUWRfvQ2qG0OMXjCE,21970
2700
+ invar/shell/prove/guard_ts.py,sha256=8InEWFd6oqFxBUEaAdlqOvPi43p0VZKxJIrAaF0eReU,37936
2701
2701
  invar/shell/prove/hypothesis.py,sha256=QUclOOUg_VB6wbmHw8O2EPiL5qBOeBRqQeM04AVuLw0,9880
2702
2702
  invar/templates/CLAUDE.md.template,sha256=eaGU3SyRO_NEifw5b26k3srgQH4jyeujjCJ-HbM36_w,4913
2703
2703
  invar/templates/__init__.py,sha256=cb3ht8KPK5oBn5oG6HsTznujmo9WriJ_P--fVxJwycc,45
@@ -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.12.dist-info/METADATA,sha256=yPbRFLkoRTEBUOo-aLsAq9rf0UC3OzdMYWjdxXR-RFM,28596
2782
- invar_tools-1.17.12.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
2783
- invar_tools-1.17.12.dist-info/entry_points.txt,sha256=RwH_EhqgtFPsnO6RcrwrAb70Zyfb8Mh6uUtztWnUxGk,102
2784
- invar_tools-1.17.12.dist-info/licenses/LICENSE,sha256=qeFksp4H4kfTgQxPCIu3OdagXyiZcgBlVfsQ6M5oFyk,10767
2785
- invar_tools-1.17.12.dist-info/licenses/LICENSE-GPL,sha256=IvZfC6ZbP7CLjytoHVzvpDZpD-Z3R_qa1GdMdWlWQ6Q,35157
2786
- invar_tools-1.17.12.dist-info/licenses/NOTICE,sha256=joEyMyFhFY8Vd8tTJ-a3SirI0m2Sd0WjzqYt3sdcglc,2561
2787
- invar_tools-1.17.12.dist-info/RECORD,,
2781
+ invar_tools-1.17.24.dist-info/METADATA,sha256=1X_j-_QQoFhmxpzH-VwKO8QgQNWp_oDyV8qxZJFoXdY,28582
2782
+ invar_tools-1.17.24.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
2783
+ invar_tools-1.17.24.dist-info/entry_points.txt,sha256=RwH_EhqgtFPsnO6RcrwrAb70Zyfb8Mh6uUtztWnUxGk,102
2784
+ invar_tools-1.17.24.dist-info/licenses/LICENSE,sha256=qeFksp4H4kfTgQxPCIu3OdagXyiZcgBlVfsQ6M5oFyk,10767
2785
+ invar_tools-1.17.24.dist-info/licenses/LICENSE-GPL,sha256=IvZfC6ZbP7CLjytoHVzvpDZpD-Z3R_qa1GdMdWlWQ6Q,35157
2786
+ invar_tools-1.17.24.dist-info/licenses/NOTICE,sha256=joEyMyFhFY8Vd8tTJ-a3SirI0m2Sd0WjzqYt3sdcglc,2561
2787
+ invar_tools-1.17.24.dist-info/RECORD,,