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.
Files changed (89) hide show
  1. invar/__init__.py +1 -0
  2. invar/core/contracts.py +10 -10
  3. invar/core/entry_points.py +105 -32
  4. invar/core/extraction.py +5 -6
  5. invar/core/format_specs.py +1 -2
  6. invar/core/formatter.py +6 -7
  7. invar/core/hypothesis_strategies.py +5 -7
  8. invar/core/inspect.py +1 -1
  9. invar/core/lambda_helpers.py +3 -3
  10. invar/core/models.py +7 -1
  11. invar/core/must_use.py +2 -1
  12. invar/core/parser.py +7 -4
  13. invar/core/postcondition_scope.py +128 -0
  14. invar/core/property_gen.py +8 -5
  15. invar/core/purity.py +3 -3
  16. invar/core/purity_heuristics.py +5 -9
  17. invar/core/references.py +8 -6
  18. invar/core/review_trigger.py +78 -6
  19. invar/core/rule_meta.py +8 -0
  20. invar/core/rules.py +18 -19
  21. invar/core/shell_analysis.py +5 -10
  22. invar/core/shell_architecture.py +2 -2
  23. invar/core/strategies.py +7 -14
  24. invar/core/suggestions.py +86 -0
  25. invar/core/sync_helpers.py +238 -0
  26. invar/core/tautology.py +102 -37
  27. invar/core/template_parser.py +467 -0
  28. invar/core/timeout_inference.py +4 -7
  29. invar/core/utils.py +13 -15
  30. invar/core/verification_routing.py +4 -7
  31. invar/mcp/server.py +100 -17
  32. invar/shell/commands/__init__.py +11 -0
  33. invar/shell/{cli.py → commands/guard.py} +94 -14
  34. invar/shell/{init_cmd.py → commands/init.py} +179 -27
  35. invar/shell/commands/merge.py +256 -0
  36. invar/shell/commands/sync_self.py +113 -0
  37. invar/shell/commands/template_sync.py +366 -0
  38. invar/shell/commands/update.py +48 -0
  39. invar/shell/config.py +12 -24
  40. invar/shell/coverage.py +351 -0
  41. invar/shell/guard_helpers.py +38 -17
  42. invar/shell/guard_output.py +7 -1
  43. invar/shell/property_tests.py +58 -22
  44. invar/shell/prove/__init__.py +9 -0
  45. invar/shell/{prove.py → prove/crosshair.py} +40 -33
  46. invar/shell/{prove_fallback.py → prove/hypothesis.py} +12 -4
  47. invar/shell/subprocess_env.py +393 -0
  48. invar/shell/template_engine.py +345 -0
  49. invar/shell/templates.py +19 -0
  50. invar/shell/testing.py +71 -20
  51. invar/templates/CLAUDE.md.template +38 -17
  52. invar/templates/aider.conf.yml.template +2 -2
  53. invar/templates/commands/{review.md → audit.md} +20 -82
  54. invar/templates/commands/guard.md +77 -0
  55. invar/templates/config/CLAUDE.md.jinja +206 -0
  56. invar/templates/config/context.md.jinja +92 -0
  57. invar/templates/config/pre-commit.yaml.jinja +44 -0
  58. invar/templates/context.md.template +33 -0
  59. invar/templates/cursorrules.template +7 -4
  60. invar/templates/examples/README.md +2 -0
  61. invar/templates/examples/conftest.py +3 -0
  62. invar/templates/examples/contracts.py +5 -5
  63. invar/templates/examples/core_shell.py +11 -7
  64. invar/templates/examples/workflow.md +81 -0
  65. invar/templates/manifest.toml +137 -0
  66. invar/templates/{INVAR.md → protocol/INVAR.md} +10 -7
  67. invar/templates/skills/develop/SKILL.md.jinja +318 -0
  68. invar/templates/skills/investigate/SKILL.md.jinja +106 -0
  69. invar/templates/skills/propose/SKILL.md.jinja +104 -0
  70. invar/templates/skills/review/SKILL.md.jinja +125 -0
  71. {invar_tools-1.2.0.dist-info → invar_tools-1.3.0.dist-info}/METADATA +108 -118
  72. invar_tools-1.3.0.dist-info/RECORD +95 -0
  73. invar_tools-1.3.0.dist-info/entry_points.txt +2 -0
  74. invar/contracts.py +0 -152
  75. invar/decorators.py +0 -94
  76. invar/invariant.py +0 -58
  77. invar/resource.py +0 -99
  78. invar/shell/update_cmd.py +0 -193
  79. invar_tools-1.2.0.dist-info/RECORD +0 -77
  80. invar_tools-1.2.0.dist-info/entry_points.txt +0 -2
  81. /invar/shell/{mutate_cmd.py → commands/mutate.py} +0 -0
  82. /invar/shell/{perception.py → commands/perception.py} +0 -0
  83. /invar/shell/{test_cmd.py → commands/test.py} +0 -0
  84. /invar/shell/{prove_accept.py → prove/accept.py} +0 -0
  85. /invar/shell/{prove_cache.py → prove/cache.py} +0 -0
  86. {invar_tools-1.2.0.dist-info → invar_tools-1.3.0.dist-info}/WHEEL +0 -0
  87. {invar_tools-1.2.0.dist-info → invar_tools-1.3.0.dist-info}/licenses/LICENSE +0 -0
  88. {invar_tools-1.2.0.dist-info → invar_tools-1.3.0.dist-info}/licenses/LICENSE-GPL +0 -0
  89. {invar_tools-1.2.0.dist-info → invar_tools-1.3.0.dist-info}/licenses/NOTICE +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: invar-tools
3
- Version: 1.2.0
3
+ Version: 1.3.0
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
@@ -24,6 +24,7 @@ Requires-Python: >=3.11
24
24
  Requires-Dist: crosshair-tool>=0.0.60
25
25
  Requires-Dist: hypothesis>=6.0
26
26
  Requires-Dist: invar-runtime>=1.0
27
+ Requires-Dist: jinja2>=3.0
27
28
  Requires-Dist: mcp>=1.0
28
29
  Requires-Dist: pre-commit>=3.0
29
30
  Requires-Dist: pydantic>=2.0
@@ -31,6 +32,7 @@ Requires-Dist: returns>=0.20
31
32
  Requires-Dist: rich>=13.0
32
33
  Requires-Dist: typer>=0.9
33
34
  Provides-Extra: dev
35
+ Requires-Dist: coverage[toml]>=7.0; extra == 'dev'
34
36
  Requires-Dist: mypy>=1.0; extra == 'dev'
35
37
  Requires-Dist: pytest-cov>=4.0; extra == 'dev'
36
38
  Requires-Dist: pytest>=7.0; extra == 'dev'
@@ -43,9 +45,9 @@ Description-Content-Type: text/markdown
43
45
  [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
44
46
  [![License](https://img.shields.io/badge/License-Apache%202.0%20%2B%20GPL--3.0-blue.svg)](#license)
45
47
 
46
- **Don't hope AI code is correct. Know it.**
48
+ > **Don't hope AI code is correct. Know it.**
47
49
 
48
- Invar is a framework that helps developers ensure AI-generated code is reliable through contracts, verification, and mechanical checks.
50
+ Invar is a verification framework for AI-assisted development. It provides contracts, verification, and a structured workflow methodology.
49
51
 
50
52
  ```python
51
53
  from invar_runtime import pre, post
@@ -62,6 +64,18 @@ def average(items: list[float]) -> float:
62
64
 
63
65
  ---
64
66
 
67
+ ## Experience Tiers
68
+
69
+ | Platform | Experience | Features |
70
+ |----------|------------|----------|
71
+ | **Claude Code** | Full | USBV workflow + skill automation + MCP integration |
72
+ | **Cursor/Windsurf** | Basic | INVAR.md protocol + CLI verification |
73
+ | **Other editors** | Minimal | CLI verification tools only |
74
+
75
+ **Why tiers?** The skill system (`/develop`, `/review`, etc.) requires Claude Code's sub-agent capabilities. Other editors receive the protocol document and CLI tools.
76
+
77
+ ---
78
+
65
79
  ## Installation
66
80
 
67
81
  ### Two Packages, Different Purposes
@@ -77,69 +91,85 @@ def average(items: list[float]) -> float:
77
91
  └─────────────────────────────────────────────────────────────────┘
78
92
  ```
79
93
 
80
- | Package | Install | When to Use |
81
- |---------|---------|-------------|
94
+ | Package | Install | Purpose |
95
+ |---------|---------|---------|
82
96
  | **invar-tools** | `uvx invar-tools <cmd>` | Development: verification, init, MCP server |
83
97
  | **invar-runtime** | `pip install invar-runtime` | Production: add to your project's dependencies |
84
98
 
85
- ### Development Tools (invar-tools)
99
+ ### Quick Install
86
100
 
87
101
  ```bash
88
- # Recommended: use without installing (always latest, isolated)
102
+ # Development tools (recommended: use without installing)
89
103
  uvx invar-tools guard
90
104
  uvx invar-tools init --claude
91
- uvx invar-tools map --top 10
92
-
93
- # Or install globally
94
- pip install invar-tools
95
- invar guard
96
- ```
97
105
 
98
- ### Runtime Contracts (invar-runtime)
99
-
100
- Add to your project's `requirements.txt` or `pyproject.toml`:
101
-
102
- ```bash
106
+ # Runtime contracts (add to your project)
103
107
  pip install invar-runtime
104
108
  ```
105
109
 
106
- ```python
107
- # In your code
108
- from invar_runtime import pre, post
110
+ **Why uvx is recommended:**
111
+ - Always uses latest version
112
+ - Doesn't pollute project dependencies
113
+ - Automatically accesses your project's venv dependencies (numpy, pandas, etc.)
114
+ - If your project has `invar-tools` installed, uvx will detect and use it
109
115
 
110
- @pre(lambda x: x > 0)
111
- @post(lambda result: result >= 0)
112
- def calculate(x: float) -> float:
113
- ...
114
- ```
116
+ **When to use pip install instead:**
117
+ - CI/CD environments where uvx isn't available
118
+ - Projects with C extensions AND Python version mismatch between uvx and project
115
119
 
116
120
  ---
117
121
 
118
122
  ## Quick Start
119
123
 
120
124
  ```bash
121
- # 1. Initialize your project (no install required!)
125
+ # 1. Initialize your project
122
126
  cd your-project
123
- uvx invar-tools init --claude # Creates INVAR.md, CLAUDE.md, configures MCP
127
+ uvx invar-tools init --claude # Full experience (Claude Code)
128
+ uvx invar-tools init # Basic experience (other editors)
124
129
 
125
130
  # 2. Write code with AI (AI follows INVAR.md protocol)
126
131
 
127
132
  # 3. Verify code quality
128
- uvx invar-tools guard # Runs static analysis + doctests
133
+ uvx invar-tools guard # Runs static analysis + doctests + property tests
134
+ ```
135
+
136
+ ---
137
+
138
+ ## The USBV Workflow
139
+
140
+ AI agents follow a four-phase development cycle:
141
+
142
+ ```
143
+ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐
144
+ │ Understand│ → │ Specify │ → │ Build │ → │ Validate │
145
+ │ │ │ │ │ │ │ │
146
+ │ Intent │ │ @pre/@post│ │ Implement │ │ invar │
147
+ │ Inspect │ │ Doctests │ │ leaves │ │ guard │
148
+ │ Constraints │ Design │ │ first │ │ │
149
+ └───────────┘ └───────────┘ └───────────┘ └───────────┘
129
150
  ```
130
151
 
131
- > **Why uvx?** No global install needed. Always uses latest version. Isolated environment.
132
- >
133
- > Alternative: `pip install invar-tools` then use `invar` instead of `uvx invar-tools`
152
+ **Key insight:** Specify contracts BEFORE implementation. The contract becomes the specification.
153
+
154
+ See [INVAR.md](./INVAR.md) for complete protocol.
155
+
156
+ ---
157
+
158
+ ## Session Protocol
134
159
 
135
- **Multi-Agent Support:** `invar init` auto-detects and configures:
160
+ Every AI session follows this format:
136
161
 
137
- | AI Tool | Configuration |
138
- |---------|---------------|
139
- | Claude Code | Creates CLAUDE.md + MCP server |
140
- | Cursor | Adds to .cursorrules |
141
- | Aider | Adds to .aider.conf.yml |
142
- | Others | Add "Follow INVAR.md" to system prompt |
162
+ **First message (Check-In):**
163
+ ```
164
+ Check-In: [project] | [branch] | [clean/dirty]
165
+ ```
166
+
167
+ **Last message (Final):**
168
+ ```
169
+ ✓ Final: guard PASS | 0 errors, 2 warnings
170
+ ```
171
+
172
+ Check-In shows project context. Guard verification runs during VALIDATE phase and Final, not at Check-In.
143
173
 
144
174
  ---
145
175
 
@@ -149,8 +179,8 @@ Invar enforces separation between pure logic and I/O:
149
179
 
150
180
  | Zone | Requirements | Forbidden |
151
181
  |------|--------------|-----------|
152
- | **Core** | `@pre`/`@post` contracts, doctests | I/O imports (os, pathlib, requests...) |
153
- | **Shell** | `Result[T, E]` returns | - |
182
+ | **Core** (`**/core/**`) | `@pre`/`@post` contracts, doctests | I/O imports (os, pathlib, requests...) |
183
+ | **Shell** (`**/shell/**`) | `Result[T, E]` returns | - |
154
184
 
155
185
  ```python
156
186
  # Core: Pure logic, receives data
@@ -170,9 +200,10 @@ def load_config(path: Path) -> Result[Config, str]:
170
200
  ### Guard (Primary)
171
201
 
172
202
  ```bash
173
- invar guard # Full verification (default)
203
+ invar guard # Full verification (static + doctests + property tests)
174
204
  invar guard --changed # Only git-modified files
175
- invar guard --static # Static only (~0.5s)
205
+ invar guard --static # Static analysis only (~0.5s)
206
+ invar guard --coverage # Collect branch coverage (doctest + hypothesis)
176
207
  ```
177
208
 
178
209
  **Flags:**
@@ -180,9 +211,9 @@ invar guard --static # Static only (~0.5s)
180
211
  | Flag | Purpose |
181
212
  |------|---------|
182
213
  | `--strict` | Treat warnings as errors |
183
- | `--explain` | Show rule limitations |
214
+ | `--explain` | Show rule explanations |
184
215
  | `--agent` | JSON output for AI tools |
185
- | `--pedantic` | Show all rules including off-by-default |
216
+ | `--coverage` | Branch coverage from doctest + hypothesis phases |
186
217
 
187
218
  ### Other Commands
188
219
 
@@ -195,14 +226,18 @@ invar update # Update managed files
195
226
 
196
227
  ---
197
228
 
198
- ## Verification Levels (DX-19)
229
+ ## Workflow Skills (Claude Code)
230
+
231
+ `invar init --claude` creates workflow skills in `.claude/skills/`:
199
232
 
200
- | Level | Command | Checks | When to Use |
201
- |-------|---------|--------|-------------|
202
- | STATIC | `--static` | Rules only (~0.5s) | Debugging static analysis |
203
- | STANDARD | (default) | Rules + doctests + CrossHair + Hypothesis (~5s) | Everything else |
233
+ | Skill | Trigger | Purpose |
234
+ |-------|---------|---------|
235
+ | `/investigate` | "why", "explain", vague tasks | Exploration, no code changes |
236
+ | `/propose` | "should we", "compare" | Decision facilitation with options |
237
+ | `/develop` | "add", "fix", "implement" | USBV implementation workflow |
238
+ | `/review` | After develop, `review_suggested` | Adversarial review with fix loop |
204
239
 
205
- **Zero decisions:** Default runs full verification. Incremental mode makes it fast (~5s first, ~2s cached).
240
+ **Note:** Skills are Claude Code exclusive. Other editors use INVAR.md protocol directly.
206
241
 
207
242
  ---
208
243
 
@@ -231,18 +266,10 @@ require_doctests = true
231
266
  # Doctest-heavy code
232
267
  exclude_doctest_lines = true
233
268
 
234
- # Purity overrides
235
- purity_pure = ["pandas.DataFrame.groupby"]
236
- purity_impure = ["my_module.cached_lookup"]
237
-
238
269
  # Rule exclusions
239
270
  [[tool.invar.guard.rule_exclusions]]
240
271
  pattern = "**/generated/**"
241
272
  rules = ["*"]
242
-
243
- # Severity overrides
244
- [tool.invar.guard.severity_overrides]
245
- redundant_type_contract = "off"
246
273
  ```
247
274
 
248
275
  ---
@@ -258,7 +285,6 @@ redundant_type_contract = "off"
258
285
  | `forbidden_import` | ERROR | I/O import in Core |
259
286
  | `shell_result` | WARN | Shell function not returning Result |
260
287
  | `empty_contract` | ERROR | Contract is `lambda: True` |
261
- | `param_mismatch` | ERROR | Lambda params ≠ function params |
262
288
 
263
289
  Full list: `invar rules --explain`
264
290
 
@@ -266,7 +292,7 @@ Full list: `invar rules --explain`
266
292
 
267
293
  ## MCP Integration (Claude Code)
268
294
 
269
- `invar init` automatically creates `.mcp.json` with smart detection of available methods:
295
+ `invar init --claude` automatically configures MCP:
270
296
 
271
297
  ```json
272
298
  {
@@ -279,46 +305,29 @@ Full list: `invar rules --explain`
279
305
  }
280
306
  ```
281
307
 
282
- **Claude Code Integration:**
283
-
284
- ```bash
285
- # Full integration (recommended)
286
- invar init --claude
287
-
288
- # Specify MCP method
289
- invar init --claude --mcp-method uvx # Recommended
290
- invar init --claude --mcp-method command # Use PATH invar
291
- invar init --claude --mcp-method python # Use current Python
292
- ```
293
-
294
308
  **MCP Tools:**
295
309
 
296
310
  | Tool | Replaces | Purpose |
297
311
  |------|----------|---------|
298
- | `invar_guard` | `pytest`, `crosshair` | Smart Guard verification |
299
- | `invar_sig` | Reading entire file | Show contracts and signatures |
312
+ | `invar_guard` | `pytest`, `crosshair` | Smart verification |
313
+ | `invar_sig` | Reading entire files | Show contracts and signatures |
300
314
  | `invar_map` | `grep` for functions | Symbol map with reference counts |
301
315
 
302
- Manual setup: See `.invar/mcp-setup.md` after running `invar init`.
303
-
304
316
  ---
305
317
 
306
- ## Platform Support (DX-31)
307
-
308
- The Independent Adversarial Reviewer (DX-31) has different capabilities depending on your AI coding assistant:
318
+ ## Platform Support
309
319
 
310
- | Feature | Claude Code | Other Agents |
311
- |---------|-------------|--------------|
312
- | Guard `review_suggested` | ✅ | ✅ |
313
- | Automatic sub-agent review | ✅ Task tool | Manual action |
314
- | Context isolation | ✅ Fresh context | N/A |
315
- | Adversarial mindset | ✅ Prompt-based | User responsibility |
320
+ | Feature | Claude Code | Cursor/Windsurf | Others |
321
+ |---------|-------------|-----------------|--------|
322
+ | Smart Guard CLI | ✅ | ✅ | ✅ |
323
+ | INVAR.md protocol | ✅ | | |
324
+ | MCP integration | ✅ | | |
325
+ | Workflow skills | ✅ | | |
326
+ | Sub-agent review | ✅ | ❌ | ❌ |
316
327
 
317
- **Claude Code:** Full independent review with context isolation. When Guard outputs `review_suggested`, the agent automatically spawns a sub-agent reviewer that sees only the code (not the implementation history).
328
+ **Claude Code** provides the full experience with automated workflow and independent review.
318
329
 
319
- **Cursor, Windsurf, Copilot, etc.:** Guard outputs `review_suggested` as a warning. Users must manually invoke review or use external code review tools.
320
-
321
- **Why context isolation matters:** The same agent that wrote code has "author blindness" — it remembers its rationale and unconsciously validates its own decisions. An isolated reviewer sees only the code.
330
+ **Other editors** receive the protocol and CLI tools. Workflow adherence is manual.
322
331
 
323
332
  ---
324
333
 
@@ -328,7 +337,7 @@ The Independent Adversarial Reviewer (DX-31) has different capabilities dependin
328
337
  |------|-------|-------|
329
338
  | `INVAR.md` | Invar | No (`invar update` manages) |
330
339
  | `CLAUDE.md` | You | Yes (project config) |
331
- | `.invar/examples/` | Invar | No (reference only) |
340
+ | `.claude/skills/` | You | Yes (customize workflows) |
332
341
 
333
342
  ---
334
343
 
@@ -337,51 +346,32 @@ The Independent Adversarial Reviewer (DX-31) has different capabilities dependin
337
346
  Contracts are checked at runtime via [deal](https://github.com/life4/deal).
338
347
 
339
348
  ```bash
340
- # Disable in production
349
+ # Disable in production for performance
341
350
  DEAL_DISABLE=1 python app.py
342
351
  ```
343
352
 
344
353
  ---
345
354
 
346
- ## Troubleshooting
347
-
348
- **Guard is slow:**
349
- - Use `--changed` for incremental checks
350
- - Use `--static` to skip doctests during debugging
351
-
352
- **Too many warnings:**
353
- - Add exclusions for generated code
354
- - Override severity for noisy rules
355
-
356
- **CrossHair timeout:**
357
- - Some code patterns don't work well with symbolic execution
358
- - Hypothesis fallback runs automatically
359
-
360
- ---
361
-
362
355
  ## Learn More
363
356
 
364
357
  **In your project** (created by `invar init`):
365
- - `INVAR.md` — Protocol for AI agents
358
+ - `INVAR.md` — Protocol v5.0 for AI agents
366
359
  - `CLAUDE.md` — Project configuration
367
360
  - `.invar/examples/` — Reference patterns
368
361
 
369
362
  **Documentation:**
370
- - [docs/INVAR-GUIDE.md](./docs/INVAR-GUIDE.md) — Detailed guide
371
- - [docs/VISION.md](./docs/VISION.md) — Design philosophy
363
+ - [docs/vision.md](./docs/vision.md) — Design philosophy
364
+ - [docs/design.md](./docs/design.md) — Technical architecture
365
+ - [GitHub Pages](https://tefx.github.io/Invar/) — Visual overview
372
366
 
373
367
  ---
374
368
 
375
369
  ## License
376
370
 
377
- This project uses a dual-license structure:
378
-
379
371
  | Component | License | Purpose |
380
372
  |-----------|---------|---------|
381
- | **invar-runtime** | [Apache-2.0](LICENSE) | Runtime contracts - use freely in your projects |
382
- | **invar-tools** | [GPL-3.0](LICENSE-GPL) | CLI tools - ensures improvements are shared |
383
- | **Documentation** | [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/) | Protocol docs - share with attribution |
384
-
385
- **For users:** You can use `invar-runtime` in any project (including proprietary). The `invar-tools` CLI is for development only and doesn't affect your project's license.
373
+ | **invar-runtime** | [Apache-2.0](LICENSE) | Runtime contracts - use freely |
374
+ | **invar-tools** | [GPL-3.0](LICENSE-GPL) | CLI tools - improvements shared |
375
+ | **Documentation** | [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/) | Share with attribution |
386
376
 
387
377
  See [NOTICE](NOTICE) for third-party licenses.
@@ -0,0 +1,95 @@
1
+ invar/__init__.py,sha256=Bzp8MpdH-uUNe6qY9ITv6LLOn0HUomtIToQ0zUS1mkg,1342
2
+ invar/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ invar/core/__init__.py,sha256=01TgQ2bqTFV4VFdksfqXYPa2WUqo-DpUWUkEcIUXFb4,218
4
+ invar/core/contracts.py,sha256=SOyF1KeJ6hrEwfQ09UzMt881OJKDXRbPTslKA6HzdKg,19085
5
+ invar/core/entry_points.py,sha256=gB6h2O9gp9larxDWVA2IUclLadMF_H8eJErnscYm0GU,12012
6
+ invar/core/extraction.py,sha256=mScqEMEEQdsd-Z0jx9g3scK6Z1vI9l-ESjggXPIWHZ4,6112
7
+ invar/core/format_specs.py,sha256=P299aRHFMXyow8STwsvaT6Bg2ALPs2wSy7SByiRZZ-A,5610
8
+ invar/core/format_strategies.py,sha256=LifL97JbsF8WEkVNmQpq2htyFUC3pW21myAjtRGpSxU,5774
9
+ invar/core/formatter.py,sha256=Ct-8x4wYoMNfi6-ub5IjoQJ80mMAXlAhsXweRrxKkGw,10548
10
+ invar/core/hypothesis_strategies.py,sha256=_MfjG7KxkmJvuPsczr_1JayR_YmiDzU2jJ8fQPoKGgs,16517
11
+ invar/core/inspect.py,sha256=l1knohwpLRHSNySPUjyeBHJusnU0vYiQGj4dMVgQZIo,4381
12
+ invar/core/lambda_helpers.py,sha256=Ap1y7N0wpgCgPHwrs2pd7zD9Qq4Ptfd2iTliprXIkME,6457
13
+ invar/core/models.py,sha256=BbGiUQh6QdhHaw1fUQjFT6fm9bOUiHRam3d95rKOKYE,10089
14
+ invar/core/must_use.py,sha256=7HnnbT53lb4dOT-1mL64pz0JbQYytuw4eejNVe7iWKY,5496
15
+ invar/core/parser.py,sha256=ucVpGziVzUvbkXT1n_SgOrYdStDEcNBqLuRGqK3_M5g,9205
16
+ invar/core/postcondition_scope.py,sha256=ykjVNqZZ1zItBmI7ebgmLW5vFGE-vpaLRTvSgWaJMgM,5245
17
+ invar/core/property_gen.py,sha256=_IvBJNUqd8pwu7wXuIProZn5RZksqFdmtXt3GnLU6Vo,14033
18
+ invar/core/purity.py,sha256=dt5dFy5V8Ch93iBJF5OuKUr1jjfimfY3oHLQD8KmLHw,12036
19
+ invar/core/purity_heuristics.py,sha256=vsgphC1XPIFtsoLB0xvp--AyaJHqlh83LyKXYda4pWc,4546
20
+ invar/core/references.py,sha256=64yGIdj9vL72Y4uUhJsi9pztZkuMnLN-7OcOziyxYMo,6339
21
+ invar/core/review_trigger.py,sha256=4GGHUmgbVsQJAob4OO6A8G7KrLcNMwNOuqHiT6Jc7cs,14085
22
+ invar/core/rule_meta.py,sha256=il_KUTjSlW1MOVgLguuLDS9wEdyqUe3CDvUx4gQjACo,10180
23
+ invar/core/rules.py,sha256=6MvqO4dQXYwYe3ICp1bT2s_33O7FkpGwUzOrgHL16X4,19962
24
+ invar/core/shell_analysis.py,sha256=i2A9SMqBI3Rb4Ai0QNTM7awIkSJIY6yZJVWS72lv0bY,6457
25
+ invar/core/shell_architecture.py,sha256=98EVdBFIs8tO-i9jKuzdmv7fLB4PKnyI-vKh5lxnB98,6538
26
+ invar/core/strategies.py,sha256=2DPl0z2p_CBNd4RlSbZzTeAy6Dq6cpCiBCB2p5qHHkk,8798
27
+ invar/core/suggestions.py,sha256=LCg2Dy9EHh_n1t9jATRZ0gTkgJkAEZk3vp2nuuCyr-s,15129
28
+ invar/core/sync_helpers.py,sha256=kd6VyFAcpKfkVcbDk3GaBi2n0EWOGICz4VmdxwbshfI,7523
29
+ invar/core/tautology.py,sha256=Pmn__a0Bt55W0lAQo1G5q8Ory9KuE23dRknKw45xxbs,9221
30
+ invar/core/template_parser.py,sha256=vH3H8OX55scZ1hWh3xoA8oJMhgleKufCOhkTvsSuu_4,14730
31
+ invar/core/timeout_inference.py,sha256=BS2fJGmwOrLpYZUku4qrizgNDSIXVLFBslW-6sRAvpc,3451
32
+ invar/core/utils.py,sha256=4ani-6XcWF__sD0c_tKcCA2FunaF2pYIfvR5BACWkDg,14168
33
+ invar/core/verification_routing.py,sha256=_jXi1txFCcUdnB3-Yavtuyk8N-XhEO_Vu_051Vuz27Y,5020
34
+ invar/mcp/__init__.py,sha256=n3S7QwMjSMqOMT8cI2jf9E0yZPjKmBOJyIYhq4WZ8TQ,226
35
+ invar/mcp/__main__.py,sha256=ZcIT2U6xUyGOWucl4jq422BDE3lRLjqyxb9pFylRBdk,219
36
+ invar/mcp/server.py,sha256=0a9eAuIZ62UaDGewKhAn8-PhLYvFHta-w_XcygcXCtE,11981
37
+ invar/shell/__init__.py,sha256=FFw1mNbh_97PeKPcHIqQpQ7mw-JoIvyLM1yOdxLw5uk,204
38
+ invar/shell/config.py,sha256=cixlq47h8HYa9Ku-JOo66KCUyrf59R0NX_Yb7A1fAv4,16134
39
+ invar/shell/coverage.py,sha256=m01o898IFIdBztEBQLwwL1Vt5PWrpUntO4lv4nWEkls,11344
40
+ invar/shell/fs.py,sha256=wVD7DPWsCIJXuTyY_pi-5_LS82mXRdn_grJCOLn9zpU,3699
41
+ invar/shell/git.py,sha256=s6RQxEDQuLrmK3mru88EoYP8__4hiFW8AozlcxmY47E,2784
42
+ invar/shell/guard_helpers.py,sha256=QeYgbW0lgUa9Z_RCjAMG7UJdiMzz5cW48Lb2u-qgQi8,15114
43
+ invar/shell/guard_output.py,sha256=hcgamhSI781zUjHmrkNhYAjsy5qgEzCbxdtXuw1I-8w,11838
44
+ invar/shell/mcp_config.py,sha256=-hC7Y5BGuVs285b6gBARk7ZyzVxHwPgXSyt_GoN0jfs,4580
45
+ invar/shell/mutation.py,sha256=Lfyk2b8j8-hxAq-iwAgQeOhr7Ci6c5tRF1TXe3CxQCs,8914
46
+ invar/shell/property_tests.py,sha256=N9JreyH5PqR89oF5yLcX7ZAV-Koyg5BKo-J05-GUPsA,9109
47
+ invar/shell/subprocess_env.py,sha256=9oXl3eMEbzLsFEgMHqobEw6oW_wV0qMEP7pklwm58Pw,11453
48
+ invar/shell/template_engine.py,sha256=IzOiGsKVFo0lDUdtg27wMzIJJKToclv151RDZuDnHHo,11027
49
+ invar/shell/templates.py,sha256=l2En95E8jRVlojdQIqdZgRLVB43f_b1d_AJapKkozwA,15908
50
+ invar/shell/testing.py,sha256=PTrrCB0nIARuDQa_XREoRzbnqjXxju1l9Eb83pivH6c,10634
51
+ invar/shell/commands/__init__.py,sha256=MEkKwVyjI9DmkvBpJcuumXo2Pg_FFkfEr-Rr3nrAt7A,284
52
+ invar/shell/commands/guard.py,sha256=0v4kfDyxIEl3iZiYjCeTwTgAEUnL-DvCKSvXaDwqRX8,17895
53
+ invar/shell/commands/init.py,sha256=K8nz1D3RqWPUr61-gYnMiSg-9cRnnYXPCR-4x37LMQA,17004
54
+ invar/shell/commands/merge.py,sha256=nuvKo8m32-OL-SCQlS4SLKmOZxQ3qj-1nGCx1Pgzifw,8183
55
+ invar/shell/commands/mutate.py,sha256=GwemiO6LlbGCBEQsBFnzZuKhF-wIMEl79GAMnKUWc8U,5765
56
+ invar/shell/commands/perception.py,sha256=TyH_HpqyKkmE3-zcU4YyBG8ghwJaSFeRC-OQMVBDTbQ,3837
57
+ invar/shell/commands/sync_self.py,sha256=nmqBry7V2_enKwy2zzHg8UoedZNicLe3yKDhjmBeZ68,3880
58
+ invar/shell/commands/template_sync.py,sha256=g-IV4X1TGFVIypw9RBToexX7ZCSaCsD9yz-A0Sm-OmM,12155
59
+ invar/shell/commands/test.py,sha256=DKnlSbUydKq6skHLLKhZGg42qTAKtiLxrTU3oyFDmAo,4189
60
+ invar/shell/commands/update.py,sha256=0V5F8vxQ6PHPHPVYDmxdRD7xXeQEFypiJMYpY5ryiek,1349
61
+ invar/shell/prove/__init__.py,sha256=ZqlbmyMFJf6yAle8634jFuPRv8wNvHps8loMlOJyf8A,240
62
+ invar/shell/prove/accept.py,sha256=cnY_6jzU1EBnpLF8-zWUWcXiSXtCwxPsXEYXsSVPG38,3717
63
+ invar/shell/prove/cache.py,sha256=jbNdrvfLjvK7S0iqugErqeabb4YIbQuwIlcSRyCKbcg,4105
64
+ invar/shell/prove/crosshair.py,sha256=4Z_iIYBlkp-I6FqSYZa89wWB09V4Ouw2PduYhTn6rfw,16525
65
+ invar/shell/prove/hypothesis.py,sha256=QUclOOUg_VB6wbmHw8O2EPiL5qBOeBRqQeM04AVuLw0,9880
66
+ invar/templates/CLAUDE.md.template,sha256=W8o6M-wasCMg-7Ef2-RloDqvSKGS_9KUsy8DVbbUwDo,3719
67
+ invar/templates/__init__.py,sha256=cb3ht8KPK5oBn5oG6HsTznujmo9WriJ_P--fVxJwycc,45
68
+ invar/templates/aider.conf.yml.template,sha256=4xzSs3BXzFJvwdhnWbmzSY0yCbfx5oxqnV8ZjehqHBg,853
69
+ invar/templates/context.md.template,sha256=p1-iHl32Z30-FocJa7slcmoA7gtQrUrMO7cE_u5MrSo,2073
70
+ invar/templates/cursorrules.template,sha256=N6AiEJRJHGkHm2tswh3PnZ_07ozeyQQI8iEOGK5Aqoc,1023
71
+ invar/templates/manifest.toml,sha256=5JxaY9nl9hK5d1qWc2OJEGID6bFSX_Dp39lc5kRoIXk,4252
72
+ invar/templates/pre-commit-config.yaml.template,sha256=2qWY3E8iDUqi85jE_X7y0atE88YOlL5IZ93wkjCgQGo,1737
73
+ invar/templates/proposal.md.template,sha256=UP7SpQ7gk8jVlHGLQCSQ5c-kCj1DBQEz8M-vEStK77I,1573
74
+ invar/templates/commands/audit.md,sha256=eXBySlQrVyk054vYQWAZYzj-HgT2QXhpzziw6GlIeGM,4112
75
+ invar/templates/commands/guard.md,sha256=PyeAKfrmlXsgbrTDypQqXmTDKK1JHKhHEQrHqftA7X0,1177
76
+ invar/templates/config/CLAUDE.md.jinja,sha256=bowI-vhIHJvTCL20L4dwFtPODvkTWTllH2TRRVUf-cg,6295
77
+ invar/templates/config/context.md.jinja,sha256=sEQc-9jBqPpovlbnHSHcH7rwXVChfqDtUbYFTanStJI,2284
78
+ invar/templates/config/pre-commit.yaml.jinja,sha256=Qflmii8hngHciSgfa8mIlg3-E3D4b0xflm0-Q-cWcCc,1752
79
+ invar/templates/examples/README.md,sha256=xMcJZ1KEcfLJi5Ope_4FIbqDWKK3mRleAgllvgeNT6I,572
80
+ invar/templates/examples/conftest.py,sha256=uKA4NR7nyZWeSzY0URdZtw5zCcJpU32jNcaSKrI1Mxc,152
81
+ invar/templates/examples/contracts.py,sha256=uqJ6Y1GADo246MjFKoLY-_2E74cfBQsLO4vTqYcR81c,3241
82
+ invar/templates/examples/core_shell.py,sha256=ckor-7ZoaF7n8gVjnIO0CXlBjKGcVwmOGSDJbaXCSrM,4247
83
+ invar/templates/examples/workflow.md,sha256=jAopzQH1xE9_leJFdav4oj1AkI46-a2q4L8RQCkDknw,2334
84
+ invar/templates/protocol/INVAR.md,sha256=5u4q5PTVHHnJzRVEzfp4rXAfTHajIUNioRB4CQAs4JM,6662
85
+ invar/templates/skills/develop/SKILL.md.jinja,sha256=A0Hnx9FlBfPXdk2Mswx0zOnDZbgszI3u5MvLVd2rlM4,9544
86
+ invar/templates/skills/investigate/SKILL.md.jinja,sha256=vY6Hri5Nsy9C0JsLWU64NUitrPnBx49NtG0d4cg_FgI,3058
87
+ invar/templates/skills/propose/SKILL.md.jinja,sha256=b_cw_EiAneSGSOMTJWu_j9UwIlW7h-xab39uQgFb5wY,2734
88
+ invar/templates/skills/review/SKILL.md.jinja,sha256=hkLs5BHlzKNat4BdXNfgB3-3Bvz7C6gFEQJq-0hQHSk,3679
89
+ invar_tools-1.3.0.dist-info/METADATA,sha256=Gwg9kdiPndJzHV5168zGSGKXw9O1IXvIEbIgCashDjQ,11656
90
+ invar_tools-1.3.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
91
+ invar_tools-1.3.0.dist-info/entry_points.txt,sha256=xjkp8_Kipb6KJR6VNfkAEqiOpvrwUnwUG53cegBA6pQ,57
92
+ invar_tools-1.3.0.dist-info/licenses/LICENSE,sha256=qeFksp4H4kfTgQxPCIu3OdagXyiZcgBlVfsQ6M5oFyk,10767
93
+ invar_tools-1.3.0.dist-info/licenses/LICENSE-GPL,sha256=IvZfC6ZbP7CLjytoHVzvpDZpD-Z3R_qa1GdMdWlWQ6Q,35157
94
+ invar_tools-1.3.0.dist-info/licenses/NOTICE,sha256=joEyMyFhFY8Vd8tTJ-a3SirI0m2Sd0WjzqYt3sdcglc,2561
95
+ invar_tools-1.3.0.dist-info/RECORD,,
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ invar = invar.shell.commands.guard:app
invar/contracts.py DELETED
@@ -1,152 +0,0 @@
1
- """
2
- Composable contracts for Invar.
3
-
4
- Provides Contract class with &, |, ~ operators for combining conditions,
5
- and a standard library of common predicates. Works with deal decorators.
6
-
7
- Inspired by Idris' dependent types.
8
- """
9
-
10
- from __future__ import annotations
11
-
12
- from dataclasses import dataclass
13
- from typing import TYPE_CHECKING, Any
14
-
15
- import deal
16
-
17
- if TYPE_CHECKING:
18
- from collections.abc import Callable
19
-
20
-
21
- @dataclass
22
- class Contract:
23
- """
24
- Composable contract with &, |, ~ operators.
25
-
26
- Contracts encapsulate predicates that can be combined and reused.
27
- Works with deal.pre for runtime checking.
28
-
29
- Examples:
30
- >>> NonEmpty = Contract(lambda x: len(x) > 0, "non-empty")
31
- >>> Sorted = Contract(lambda x: list(x) == sorted(x), "sorted")
32
- >>> combined = NonEmpty & Sorted
33
- >>> combined.check([1, 2, 3])
34
- True
35
- >>> combined.check([])
36
- False
37
- >>> combined.check([3, 1, 2])
38
- False
39
- >>> (NonEmpty | Sorted).check([]) # Empty but sorted
40
- True
41
- >>> (~NonEmpty).check([]) # NOT non-empty
42
- True
43
- """
44
-
45
- predicate: Callable[[Any], bool]
46
- description: str
47
-
48
- def check(self, value: Any) -> bool:
49
- """Check if value satisfies the contract."""
50
- return self.predicate(value)
51
-
52
- def __and__(self, other: Contract) -> Contract:
53
- """Combine contracts with AND."""
54
- return Contract(
55
- predicate=lambda x: self.check(x) and other.check(x),
56
- description=f"({self.description} AND {other.description})",
57
- )
58
-
59
- def __or__(self, other: Contract) -> Contract:
60
- """Combine contracts with OR."""
61
- return Contract(
62
- predicate=lambda x: self.check(x) or other.check(x),
63
- description=f"({self.description} OR {other.description})",
64
- )
65
-
66
- def __invert__(self) -> Contract:
67
- """Negate the contract."""
68
- return Contract(
69
- predicate=lambda x: not self.check(x),
70
- description=f"NOT({self.description})",
71
- )
72
-
73
- def __call__(self, *args: Any, **kwargs: Any) -> bool:
74
- """Allow using as deal.pre predicate directly."""
75
- value = args[0] if args else next(iter(kwargs.values()))
76
- return self.check(value)
77
-
78
- def __repr__(self) -> str:
79
- return f"Contract({self.description!r})"
80
-
81
-
82
- def pre(*contracts: Contract) -> Callable[[Callable], Callable]:
83
- """
84
- Decorator accepting Contract objects for preconditions.
85
-
86
- Works with deal.pre under the hood.
87
-
88
- Examples:
89
- >>> from invar.contracts import pre, NonEmpty
90
- >>> @pre(NonEmpty)
91
- ... def first(xs): return xs[0]
92
- >>> first([1, 2, 3])
93
- 1
94
- """
95
-
96
- def combined(*args: Any, **kwargs: Any) -> bool:
97
- value = args[0] if args else next(iter(kwargs.values()))
98
- return all(c.check(value) for c in contracts)
99
-
100
- return deal.pre(combined)
101
-
102
-
103
- def post(*contracts: Contract) -> Callable[[Callable], Callable]:
104
- """
105
- Decorator accepting Contract objects for postconditions.
106
-
107
- Works with deal.post under the hood.
108
-
109
- Examples:
110
- >>> from invar.contracts import post, NonEmpty
111
- >>> @post(NonEmpty)
112
- ... def get_list(): return [1]
113
- >>> get_list()
114
- [1]
115
- """
116
-
117
- def combined(result: Any) -> bool:
118
- return all(c.check(result) for c in contracts)
119
-
120
- return deal.post(combined)
121
-
122
-
123
- # =============================================================================
124
- # Standard Library of Contracts
125
- # =============================================================================
126
-
127
- # --- Collections ---
128
- NonEmpty: Contract = Contract(lambda x: len(x) > 0, "non-empty")
129
- Sorted: Contract = Contract(lambda x: list(x) == sorted(x), "sorted")
130
- Unique: Contract = Contract(lambda x: len(x) == len(set(x)), "unique")
131
- SortedNonEmpty: Contract = NonEmpty & Sorted
132
-
133
- # --- Numbers ---
134
- Positive: Contract = Contract(lambda x: x > 0, "positive")
135
- NonNegative: Contract = Contract(lambda x: x >= 0, "non-negative")
136
- Negative: Contract = Contract(lambda x: x < 0, "negative")
137
-
138
-
139
- def InRange(lo: float, hi: float) -> Contract:
140
- """Create a contract checking value is in [lo, hi]."""
141
- return Contract(lambda x: lo <= x <= hi, f"[{lo},{hi}]")
142
-
143
-
144
- Percentage: Contract = InRange(0, 100)
145
-
146
- # --- Strings ---
147
- NonBlank: Contract = Contract(lambda s: bool(s and s.strip()), "non-blank")
148
-
149
- # --- Lists with elements ---
150
- AllPositive: Contract = Contract(lambda xs: all(x > 0 for x in xs), "all positive")
151
- AllNonNegative: Contract = Contract(lambda xs: all(x >= 0 for x in xs), "all non-negative")
152
- NoNone: Contract = Contract(lambda xs: None not in xs, "no None")