invar-tools 1.17.3__py3-none-any.whl → 1.17.6__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/server.py CHANGED
@@ -154,9 +154,11 @@ def _get_guard_tool() -> Tool:
154
154
  name="invar_guard",
155
155
  title="Smart Guard",
156
156
  description=(
157
- "Smart Guard: Verify code quality with static analysis + doctests. "
158
- "Use this INSTEAD of Bash('pytest ...') or Bash('crosshair ...'). "
159
- "Default runs static + doctests + CrossHair + Hypothesis."
157
+ "Smart Guard: Verify code quality with static analysis + tests. "
158
+ "Supports Python (pytest + doctest + CrossHair + Hypothesis) "
159
+ "and TypeScript (tsc + eslint + vitest). "
160
+ "Auto-detects project language from marker files (pyproject.toml, tsconfig.json). "
161
+ "Use this INSTEAD of Bash('pytest ...') or Bash('npm test ...')."
160
162
  ),
161
163
  inputSchema={
162
164
  "type": "object",
@@ -180,7 +182,8 @@ def _get_sig_tool() -> Tool:
180
182
  title="Show Signatures",
181
183
  description=(
182
184
  "Show function signatures and contracts (@pre/@post). "
183
- "Use this INSTEAD of Read('file.py') when you want to understand structure."
185
+ "Supports Python and TypeScript (via TS Compiler API). "
186
+ "Use this INSTEAD of Read('file.py'/'file.ts') when you want to understand structure."
184
187
  ),
185
188
  inputSchema={
186
189
  "type": "object",
@@ -201,7 +204,8 @@ def _get_map_tool() -> Tool:
201
204
  title="Symbol Map",
202
205
  description=(
203
206
  "Symbol map with reference counts. "
204
- "Use this INSTEAD of Grep for 'def ' to find functions."
207
+ "Supports Python and TypeScript projects. "
208
+ "Use this INSTEAD of Grep for 'def ' or 'function ' to find symbols."
205
209
  ),
206
210
  inputSchema={
207
211
  "type": "object",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@invar/eslint-plugin",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
@@ -274,7 +274,7 @@ def guard(
274
274
  changed_result = handle_changed_mode(path)
275
275
  if isinstance(changed_result, Failure):
276
276
  if changed_result.failure() == "NO_CHANGES":
277
- console.print("[green]No changed Python files.[/green]")
277
+ console.print("[green]No changed files to verify.[/green]")
278
278
  raise typer.Exit(0)
279
279
  console.print(f"[red]Error:[/red] {changed_result.failure()}")
280
280
  raise typer.Exit(1)
@@ -31,10 +31,21 @@ if TYPE_CHECKING:
31
31
  console = Console()
32
32
 
33
33
 
34
+ def _has_typescript_files(path: Path) -> bool:
35
+ """Check if directory contains TypeScript files (.ts, .tsx).
36
+
37
+ DX-78b: Fallback language detection when path is subdirectory
38
+ without tsconfig/package.json markers in parent directories.
39
+ """
40
+ from invar.shell.fs import discover_typescript_files
41
+
42
+ return bool(list(discover_typescript_files(path)))
43
+
44
+
34
45
  # @shell_complexity: Symbol map generation with sorting and output modes
35
46
  def run_map(path: Path, top_n: int, json_output: bool) -> Result[None, str]:
36
47
  """
37
- Run the map command.
48
+ Run map command.
38
49
 
39
50
  Scans project and generates perception map with reference counts.
40
51
  LX-06: Supports TypeScript projects (basic symbol listing).
@@ -46,6 +57,14 @@ def run_map(path: Path, top_n: int, json_output: bool) -> Result[None, str]:
46
57
  from invar.shell.commands.init import detect_language
47
58
 
48
59
  project_language = detect_language(path)
60
+
61
+ # DX-78b: Fallback language detection by checking file extensions
62
+ # When path is a subdirectory without tsconfig/package.json markers,
63
+ # try detecting from actual file contents
64
+ if project_language == "python":
65
+ if _has_typescript_files(path):
66
+ project_language = "typescript"
67
+
49
68
  if project_language == "typescript":
50
69
  return _run_map_typescript(path, top_n, json_output)
51
70
 
@@ -174,7 +193,9 @@ def _run_sig_typescript(
174
193
  console.print(f" @post {post}")
175
194
  if s.members:
176
195
  for m in s.members:
177
- console.print(f" [{m['kind']}] {m['name']}: {m.get('signature', '')}")
196
+ console.print(
197
+ f" [{m['kind']}] {m['name']}: {m.get('signature', '')}"
198
+ )
178
199
  console.print()
179
200
 
180
201
  return Success(None)
@@ -236,12 +257,13 @@ def _run_map_python(path: Path, top_n: int, json_output: bool) -> Result[None, s
236
257
 
237
258
  if not file_infos:
238
259
  return Failure(
239
- "No Python symbols found.\n\n"
240
- "Available tools:\n"
241
- "- invar sig <file.py> — Extract signatures\n"
242
- "- invar refs <file.py>::Symbol — Find references\n"
260
+ "No source files found in this directory.\n\n"
261
+ "💡 Available tools:\n"
262
+ "- invar sig <file> — Extract signatures\n"
263
+ "- invar refs <file>::Symbol — Find references\n"
243
264
  "- invar_doc_* — Document navigation\n"
244
- "- invar_guard — Static verification"
265
+ "- invar_guard — Static verification\n\n"
266
+ "Supported languages: Python, TypeScript"
245
267
  )
246
268
 
247
269
  # Build perception map
@@ -274,12 +296,13 @@ def _run_map_typescript(path: Path, top_n: int, json_output: bool) -> Result[Non
274
296
 
275
297
  if not data.get("symbols"):
276
298
  return Failure(
277
- "No TypeScript symbols found.\n\n"
278
- "Available tools:\n"
279
- "- invar sig <file.ts> — Extract signatures\n"
280
- "- invar refs <file.ts>::Symbol — Find references\n"
299
+ "No source files found in this directory.\n\n"
300
+ "💡 Available tools:\n"
301
+ "- invar sig <file> — Extract signatures\n"
302
+ "- invar refs <file>::Symbol — Find references\n"
281
303
  "- invar_doc_* — Document navigation\n"
282
- "- invar_guard — Static verification"
304
+ "- invar_guard — Static verification\n\n"
305
+ "Supported languages: Python, TypeScript"
283
306
  )
284
307
 
285
308
  # Output using TS Compiler API format
@@ -394,16 +417,11 @@ def run_refs(target: str, json_output: bool) -> Result[None, str]:
394
417
  elif suffix in (".py", ".pyi"):
395
418
  return _run_refs_python(file_path, symbol_name, json_output)
396
419
  else:
397
- return Failure(
398
- f"Unsupported file type: {suffix}\n\n"
399
- "Supported: .py, .pyi, .ts, .tsx"
400
- )
420
+ return Failure(f"Unsupported file type: {suffix}\n\nSupported: .py, .pyi, .ts, .tsx")
401
421
 
402
422
 
403
423
  # @shell_complexity: Reference finding with output formatting and error handling
404
- def _run_refs_python(
405
- file_path: Path, symbol_name: str, json_output: bool
406
- ) -> Result[None, str]:
424
+ def _run_refs_python(file_path: Path, symbol_name: str, json_output: bool) -> Result[None, str]:
407
425
  """Find references in Python using jedi."""
408
426
  from invar.shell.py_refs import find_all_references_to_symbol
409
427
 
@@ -460,15 +478,14 @@ def _run_refs_python(
460
478
  @dataclass
461
479
  class _SymbolPosition:
462
480
  """Temporary holder for symbol position during refs lookup."""
481
+
463
482
  line: int
464
483
  column: int
465
484
  name: str
466
485
 
467
486
 
468
487
  # @shell_complexity: TypeScript refs with symbol lookup and output formatting
469
- def _run_refs_typescript(
470
- file_path: Path, symbol_name: str, json_output: bool
471
- ) -> Result[None, str]:
488
+ def _run_refs_typescript(file_path: Path, symbol_name: str, json_output: bool) -> Result[None, str]:
472
489
  """Find references in TypeScript using TS Compiler API."""
473
490
  from invar.shell.ts_compiler import is_typescript_available, run_refs_typescript
474
491
 
@@ -499,9 +516,7 @@ def _run_refs_typescript(
499
516
  # Extract column if available, default to 0
500
517
  column = member.get("column", 0)
501
518
  symbol = _SymbolPosition(
502
- line=member["line"],
503
- column=column,
504
- name=symbol_name
519
+ line=member["line"], column=column, name=symbol_name
505
520
  )
506
521
  break
507
522
  if symbol:
@@ -53,7 +53,7 @@ def test(
53
53
  raise typer.Exit(1)
54
54
  files = list(changed_result.unwrap())
55
55
  if not files:
56
- console.print("[green]No changed Python files.[/green]")
56
+ console.print("[green]No changed files to test.[/green]")
57
57
  raise typer.Exit(0)
58
58
  elif target:
59
59
  files = [Path(target)]
@@ -99,7 +99,7 @@ def verify(
99
99
  raise typer.Exit(1)
100
100
  files = list(changed_result.unwrap())
101
101
  if not files:
102
- console.print("[green]No changed Python files.[/green]")
102
+ console.print("[green]No changed files to test.[/green]")
103
103
  raise typer.Exit(0)
104
104
  elif target:
105
105
  files = [Path(target)]
@@ -13,6 +13,7 @@ import contextlib
13
13
  import json
14
14
  import re
15
15
  import subprocess
16
+ import tempfile
16
17
  from dataclasses import dataclass, field
17
18
  from pathlib import Path
18
19
  from typing import Literal
@@ -104,9 +105,7 @@ def check_ts_complexity_debt(
104
105
  """
105
106
  # Count unaddressed shell-complexity warnings
106
107
  unaddressed = [
107
- v
108
- for v in violations
109
- if v.rule == "@invar/shell-complexity" and v.severity == "warning"
108
+ v for v in violations if v.rule == "@invar/shell-complexity" and v.severity == "warning"
110
109
  ]
111
110
 
112
111
  if len(unaddressed) >= limit:
@@ -142,9 +141,15 @@ def format_typescript_guard_v2(result: TypeScriptGuardResult) -> dict:
142
141
  """
143
142
  # Count violations by source
144
143
  tsc_errors = sum(1 for v in result.violations if v.source == "tsc" and v.severity == "error")
145
- tsc_warnings = sum(1 for v in result.violations if v.source == "tsc" and v.severity == "warning")
146
- eslint_errors = sum(1 for v in result.violations if v.source == "eslint" and v.severity == "error")
147
- eslint_warnings = sum(1 for v in result.violations if v.source == "eslint" and v.severity == "warning")
144
+ tsc_warnings = sum(
145
+ 1 for v in result.violations if v.source == "tsc" and v.severity == "warning"
146
+ )
147
+ eslint_errors = sum(
148
+ 1 for v in result.violations if v.source == "eslint" and v.severity == "error"
149
+ )
150
+ eslint_warnings = sum(
151
+ 1 for v in result.violations if v.source == "eslint" and v.severity == "warning"
152
+ )
148
153
  vitest_failures = sum(1 for v in result.violations if v.source == "vitest")
149
154
 
150
155
  # Count files checked (unique files in violations + estimate from available tools)
@@ -459,14 +464,16 @@ def run_ts_analyzer(project_path: Path) -> Result[dict, str]:
459
464
  )
460
465
  # Extract key metrics from text output
461
466
  output = summary_result.stdout
462
- coverage_match = re.search(r'Contract coverage: (\d+)%', output)
467
+ coverage_match = re.search(r"Contract coverage: (\d+)%", output)
463
468
  coverage = int(coverage_match.group(1)) if coverage_match else None
464
469
 
465
- return Success({
466
- "coverage": coverage,
467
- "summary_mode": True,
468
- "note": "Full JSON output too large, using summary metrics"
469
- })
470
+ return Success(
471
+ {
472
+ "coverage": coverage,
473
+ "summary_mode": True,
474
+ "note": "Full JSON output too large, using summary metrics",
475
+ }
476
+ )
470
477
  except Exception:
471
478
  return Failure(f"JSON parse error: {str(e)[:100]}")
472
479
 
@@ -481,9 +488,7 @@ def run_ts_analyzer(project_path: Path) -> Result[dict, str]:
481
488
 
482
489
 
483
490
  # @shell_complexity: Error handling branches for subprocess/JSON parsing
484
- def run_fc_runner(
485
- project_path: Path, *, seed: int = 42, num_runs: int = 100
486
- ) -> Result[dict, str]:
491
+ def run_fc_runner(project_path: Path, *, seed: int = 42, num_runs: int = 100) -> Result[dict, str]:
487
492
  """Run @invar/fc-runner for property-based testing.
488
493
 
489
494
  Calls the Node component via npx with JSON output mode.
@@ -747,17 +752,35 @@ def run_eslint(project_path: Path) -> Result[list[TypeScriptViolation], str]:
747
752
  cmd = _get_invar_package_cmd("eslint-plugin", project_path)
748
753
  cmd.append(str(project_path)) # Add project path as argument
749
754
 
750
- result = subprocess.run(
751
- cmd,
752
- capture_output=True,
753
- text=True,
754
- timeout=120,
755
- )
755
+ # Use temp file to avoid subprocess 64KB buffer limit
756
+ # ESLint output can be large for big projects
757
+ with tempfile.NamedTemporaryFile(mode="w+", suffix=".json", delete=False) as temp_file:
758
+ temp_path = temp_file.name
759
+
760
+ try:
761
+ # Redirect stdout to temp file
762
+ with Path(temp_path).open("w") as f:
763
+ result = subprocess.run(
764
+ cmd,
765
+ stdout=f,
766
+ stderr=subprocess.PIPE,
767
+ timeout=120,
768
+ cwd=project_path,
769
+ text=True,
770
+ )
771
+
772
+ # Read from temp file
773
+ with Path(temp_path).open("r") as f:
774
+ eslint_json = f.read()
775
+ finally:
776
+ # Clean up temp file
777
+ with contextlib.suppress(OSError):
778
+ Path(temp_path).unlink()
756
779
 
757
780
  violations: list[TypeScriptViolation] = []
758
781
 
759
782
  try:
760
- eslint_output = json.loads(result.stdout)
783
+ eslint_output = json.loads(eslint_json)
761
784
  for file_result in eslint_output:
762
785
  file_path = file_result.get("filePath", "")
763
786
  # Make path relative
@@ -781,6 +804,8 @@ def run_eslint(project_path: Path) -> Result[list[TypeScriptViolation], str]:
781
804
  # ESLint may output non-JSON on certain errors
782
805
  if result.returncode != 0 and result.stderr:
783
806
  return Failure(f"ESLint error: {result.stderr[:200]}")
807
+ return Failure("ESLint output parsing failed: JSON decode error")
808
+ return Failure("ESLint output parsing failed: JSON decode error")
784
809
 
785
810
  return Success(violations)
786
811
 
@@ -11,4 +11,58 @@ A task is complete only when ALL conditions are met:
11
11
 
12
12
  ---
13
13
 
14
+ ## Baseline Protocol (Technical Debt)
15
+
16
+ When working on projects with existing verification failures:
17
+
18
+ ### Establishing Baseline
19
+
20
+ Before starting implementation:
21
+ 1. Run verification from **repository root**: `invar_guard(path=".")`
22
+ 2. If failures exist and are pre-existing debt → **Record as baseline**
23
+ 3. Note the failure count and affected files
24
+
25
+ ### During Implementation
26
+
27
+ **Allowed:**
28
+ - Continue work despite baseline failures
29
+ - Use language-specific local checks (e.g., linters, type checkers, unit tests)
30
+ - Focus on ensuring new code doesn't introduce violations
31
+
32
+ **Required:**
33
+ - New code must not expand the failure surface
34
+ - Changes to existing files must not add new violations
35
+
36
+ ### Final Output Format
37
+
38
+ **When baseline is cleared:**
39
+ ```
40
+ ✓ Final: guard PASS | 0 errors, 2 warnings
41
+ ```
42
+
43
+ **When baseline still exists (known debt):**
44
+ ```
45
+ ✓ Final: guard BASELINE_FAIL (known debt) | local checks PASS
46
+ ```
47
+
48
+ **If new violations introduced:**
49
+ ```
50
+ ✗ Final: guard FAIL | X new errors (baseline: Y)
51
+ ```
52
+
53
+ ### Guard Best Practices
54
+
55
+ **Always run from repository root:**
56
+ ```python
57
+ # ✅ CORRECT - Runs from repo root
58
+ invar_guard(path=".")
59
+
60
+ # ❌ WRONG - May fail language detection in subdirectories
61
+ invar_guard(path="src/components")
62
+ ```
63
+
64
+ **Reason:** Subdirectories may lack language marker files (pyproject.toml, tsconfig.json), causing incorrect detection defaults.
65
+
66
+ ---
67
+
14
68
  *Protocol v5.0 — USBV workflow | [Examples](.invar/examples/)*
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: invar-tools
3
- Version: 1.17.3
3
+ Version: 1.17.6
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
@@ -51,7 +51,7 @@ invar/core/patterns/types.py,sha256=ULAlWuAdmO6CFcEDjTrWBfzNTBsnomAl2d25tR11ihU,
51
51
  invar/mcp/__init__.py,sha256=n3S7QwMjSMqOMT8cI2jf9E0yZPjKmBOJyIYhq4WZ8TQ,226
52
52
  invar/mcp/__main__.py,sha256=ZcIT2U6xUyGOWucl4jq422BDE3lRLjqyxb9pFylRBdk,219
53
53
  invar/mcp/handlers.py,sha256=Kls1aXnYmGcMvib_Mesfz5FjBaL7lmrhKaw_P2JDnyw,16551
54
- invar/mcp/server.py,sha256=WmVNs2_AcOMbmp3tEgWR57hjqqJkMx3nV3z9fcNuSAA,20109
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
@@ -60,7 +60,7 @@ invar/node_tools/eslint-plugin/cli.js,sha256=ImpQUercYO3R8BWkV7k1xxYfWxwqPOBCxyW
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
63
- invar/node_tools/eslint-plugin/node_modules/.package-lock.json,sha256=EJJZf-T7_IAGeFrFPP3TSA2u0WPdZsFvZ2ZeegED1T8,56185
63
+ invar/node_tools/eslint-plugin/node_modules/.package-lock.json,sha256=jsPJ0tWH59w4vqd_UQsLTQXtbdyHC87cuISV3zQfxqg,56185
64
64
  invar/node_tools/eslint-plugin/node_modules/.bin/acorn,sha256=TD628diTJ5Db_wQ4Ofjudoa8YUpZ71d-lv7WGKSksbg,60
65
65
  invar/node_tools/eslint-plugin/node_modules/.bin/eslint,sha256=ZHNmEDMURrQxBRaqXK3GmrjHkacDiEZ8Gd1xFGUEZkc,5458
66
66
  invar/node_tools/eslint-plugin/node_modules/.bin/js-yaml,sha256=bXdJ39gBkJWXD0NmzQnsOr753qo_hwcJ3_7LAn5YkC0,2736
@@ -2681,23 +2681,23 @@ 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=xTQ8cPp-x1xMCtufKxmMNUSpIpH31uUjziAB8ifCnC0,24837
2684
+ invar/shell/commands/guard.py,sha256=ZuqxYkCZHoomUPuk4-HNf0bTk86il0_uRkhOTvD6T3k,24840
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
2688
2688
  invar/shell/commands/mutate.py,sha256=GwemiO6LlbGCBEQsBFnzZuKhF-wIMEl79GAMnKUWc8U,5765
2689
- invar/shell/commands/perception.py,sha256=Vl6zgxkqtS3QRXBat6U_utNhpViyPFoPh899-OtDLgQ,19493
2689
+ invar/shell/commands/perception.py,sha256=mMAf6B0HSJq5WnZc7dlkQ3CJKAqUyVR97VAvTFGe6Jc,20276
2690
2690
  invar/shell/commands/skill.py,sha256=oKVyaxQ_LK28FpJhRpBDpXcpRdUBK3n6rC0qD77ax1M,5803
2691
2691
  invar/shell/commands/sync_self.py,sha256=nmqBry7V2_enKwy2zzHg8UoedZNicLe3yKDhjmBeZ68,3880
2692
2692
  invar/shell/commands/template_sync.py,sha256=aNWyFPMFT7pSwHrvwGCqcKAwb4dp7S9tvZzy9H4gAnw,16094
2693
- invar/shell/commands/test.py,sha256=goMf-ovvzEyWQMheq4YlJ-mwK5-w3lDj0cq0IA_1-_c,4205
2693
+ invar/shell/commands/test.py,sha256=eXTuqjBhxpQrkWQE54blQvbRsILhq5JIAGbl453UoTo,4207
2694
2694
  invar/shell/commands/uninstall.py,sha256=X5wWT41RFCdXkZ2aZEwLWy9oF1ajp9UyiO1O2iR6DrQ,20188
2695
2695
  invar/shell/commands/update.py,sha256=-NNQQScEb_0bV1B8YFxTjpUECk9c8dGAiNVRc64nWhY,1008
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
2699
  invar/shell/prove/crosshair.py,sha256=XhJDsQWIriX9SuqeflUYvJgp9gJTDH7I7Uka6zjNzZ0,16734
2700
- invar/shell/prove/guard_ts.py,sha256=4wY2QyyOXJ6eRjHWd6M4l866i3_o6AmfffFQVq7Q5xU,35886
2700
+ invar/shell/prove/guard_ts.py,sha256=OKw5Pfr4PbTVnFAixe5WhScyxWr4FiHabSTvjiFqLGs,36948
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
@@ -2753,7 +2753,7 @@ invar/templates/protocol/typescript/markers.md,sha256=8mHzYfBlQ2baeQSV2e7uY2p6_E
2753
2753
  invar/templates/protocol/typescript/tools.md,sha256=c0xu0n15iFcNAET8wOqKdcgG2n_OC0hP4xE7rZaFDI0,1691
2754
2754
  invar/templates/protocol/typescript/troubleshooting.md,sha256=kHx4Ykfgv6S1OXMmLZ3jMft6aqy60c9ODEhfs5kcybo,2588
2755
2755
  invar/templates/protocol/universal/architecture.md,sha256=WT6wL0VDPD4n8yXBQEOXXnzh2IkK18oOt76lZelN3As,1379
2756
- invar/templates/protocol/universal/completion.md,sha256=bFo8-Npa7vb1efQLgOdGDSWnho45f0BBxvaD8awFLus,425
2756
+ invar/templates/protocol/universal/completion.md,sha256=6pAG9qxgrulaqtvV11dpoDKWjKjMSFrV7InT--z8zKI,1803
2757
2757
  invar/templates/protocol/universal/contracts-concept.md,sha256=7XDdzz_obI8JbkZfKDD4yJIpo5DmaL4xyjmxRjwSq4Q,780
2758
2758
  invar/templates/protocol/universal/header.md,sha256=ttg9wtbqw3jWUocwMn2AUbqPXT_hOk15VwWosWYnAUQ,965
2759
2759
  invar/templates/protocol/universal/session.md,sha256=bOaHfu-1QsCXaNyL-412W6b0YFuk0-L4sd0VItrf0V0,418
@@ -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.3.dist-info/METADATA,sha256=4TzQBnzpS5lncpGXpZwTGRwwGg-OG4UB_OYIBOdte4E,28595
2782
- invar_tools-1.17.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
2783
- invar_tools-1.17.3.dist-info/entry_points.txt,sha256=RwH_EhqgtFPsnO6RcrwrAb70Zyfb8Mh6uUtztWnUxGk,102
2784
- invar_tools-1.17.3.dist-info/licenses/LICENSE,sha256=qeFksp4H4kfTgQxPCIu3OdagXyiZcgBlVfsQ6M5oFyk,10767
2785
- invar_tools-1.17.3.dist-info/licenses/LICENSE-GPL,sha256=IvZfC6ZbP7CLjytoHVzvpDZpD-Z3R_qa1GdMdWlWQ6Q,35157
2786
- invar_tools-1.17.3.dist-info/licenses/NOTICE,sha256=joEyMyFhFY8Vd8tTJ-a3SirI0m2Sd0WjzqYt3sdcglc,2561
2787
- invar_tools-1.17.3.dist-info/RECORD,,
2781
+ invar_tools-1.17.6.dist-info/METADATA,sha256=2KB-3ghp_lXr5Tokxmprkgu4hbpDVJrCRud1tVkL2L8,28595
2782
+ invar_tools-1.17.6.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
2783
+ invar_tools-1.17.6.dist-info/entry_points.txt,sha256=RwH_EhqgtFPsnO6RcrwrAb70Zyfb8Mh6uUtztWnUxGk,102
2784
+ invar_tools-1.17.6.dist-info/licenses/LICENSE,sha256=qeFksp4H4kfTgQxPCIu3OdagXyiZcgBlVfsQ6M5oFyk,10767
2785
+ invar_tools-1.17.6.dist-info/licenses/LICENSE-GPL,sha256=IvZfC6ZbP7CLjytoHVzvpDZpD-Z3R_qa1GdMdWlWQ6Q,35157
2786
+ invar_tools-1.17.6.dist-info/licenses/NOTICE,sha256=joEyMyFhFY8Vd8tTJ-a3SirI0m2Sd0WjzqYt3sdcglc,2561
2787
+ invar_tools-1.17.6.dist-info/RECORD,,