invar-tools 1.11.0__py3-none-any.whl → 1.12.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/mcp/handlers.py +28 -0
- invar/mcp/server.py +64 -14
- invar/node_tools/ts-query.js +396 -0
- invar/shell/commands/guard.py +24 -0
- invar/shell/commands/perception.py +302 -6
- invar/shell/py_refs.py +156 -0
- invar/shell/ts_compiler.py +238 -0
- invar/templates/examples/typescript/patterns.md +193 -0
- {invar_tools-1.11.0.dist-info → invar_tools-1.12.0.dist-info}/METADATA +25 -7
- {invar_tools-1.11.0.dist-info → invar_tools-1.12.0.dist-info}/RECORD +15 -11
- {invar_tools-1.11.0.dist-info → invar_tools-1.12.0.dist-info}/WHEEL +0 -0
- {invar_tools-1.11.0.dist-info → invar_tools-1.12.0.dist-info}/entry_points.txt +0 -0
- {invar_tools-1.11.0.dist-info → invar_tools-1.12.0.dist-info}/licenses/LICENSE +0 -0
- {invar_tools-1.11.0.dist-info → invar_tools-1.12.0.dist-info}/licenses/LICENSE-GPL +0 -0
- {invar_tools-1.11.0.dist-info → invar_tools-1.12.0.dist-info}/licenses/NOTICE +0 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"""TypeScript Compiler API wrapper (single-shot subprocess).
|
|
2
|
+
|
|
3
|
+
DX-78: Provides Python interface to ts-query.js for TypeScript analysis.
|
|
4
|
+
|
|
5
|
+
Architecture:
|
|
6
|
+
- Single-shot subprocess: starts, runs query, exits
|
|
7
|
+
- No persistent process, no orphan risk
|
|
8
|
+
- Falls back to regex parser if Node.js unavailable
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import subprocess
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
from returns.result import Failure, Result, Success
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class TSSymbolInfo:
|
|
24
|
+
"""TypeScript symbol information from Compiler API."""
|
|
25
|
+
|
|
26
|
+
name: str
|
|
27
|
+
kind: str
|
|
28
|
+
signature: str
|
|
29
|
+
line: int
|
|
30
|
+
file: str = ""
|
|
31
|
+
contracts: dict[str, list[str]] | None = None
|
|
32
|
+
members: list[dict[str, Any]] | None = None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class TSReference:
|
|
37
|
+
"""A reference to a TypeScript symbol."""
|
|
38
|
+
|
|
39
|
+
file: str
|
|
40
|
+
line: int
|
|
41
|
+
column: int
|
|
42
|
+
context: str
|
|
43
|
+
is_definition: bool = False
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _find_ts_query_js() -> Path:
|
|
47
|
+
"""Find the ts-query.js script bundled with invar-tools."""
|
|
48
|
+
# Look relative to this file's location
|
|
49
|
+
this_dir = Path(__file__).parent.parent
|
|
50
|
+
ts_query_path = this_dir / "node_tools" / "ts-query.js"
|
|
51
|
+
|
|
52
|
+
if ts_query_path.exists():
|
|
53
|
+
return ts_query_path
|
|
54
|
+
|
|
55
|
+
# Fallback: check if installed globally or in node_modules
|
|
56
|
+
raise FileNotFoundError("ts-query.js not found")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# @shell_complexity: Project root discovery with parent traversal
|
|
60
|
+
def _find_tsconfig_root(file_path: Path) -> Path:
|
|
61
|
+
"""Find the project root containing tsconfig.json."""
|
|
62
|
+
current = file_path.parent if file_path.is_file() else file_path
|
|
63
|
+
|
|
64
|
+
while current != current.parent:
|
|
65
|
+
if (current / "tsconfig.json").exists():
|
|
66
|
+
return current
|
|
67
|
+
current = current.parent
|
|
68
|
+
|
|
69
|
+
# Fallback to file's directory
|
|
70
|
+
return file_path.parent if file_path.is_file() else file_path
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# @shell_complexity: Subprocess orchestration with error handling and JSON parsing
|
|
74
|
+
def query_typescript(
|
|
75
|
+
project_root: Path,
|
|
76
|
+
command: str,
|
|
77
|
+
**params: Any,
|
|
78
|
+
) -> Result[dict[str, Any], str]:
|
|
79
|
+
"""Run ts-query.js and return parsed result.
|
|
80
|
+
|
|
81
|
+
Single-shot subprocess: starts, runs query, exits.
|
|
82
|
+
No persistent process, no orphan risk.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
project_root: Project root containing tsconfig.json
|
|
86
|
+
command: Query command (sig, map, refs)
|
|
87
|
+
**params: Command-specific parameters
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Parsed JSON result or error message
|
|
91
|
+
"""
|
|
92
|
+
try:
|
|
93
|
+
ts_query_path = _find_ts_query_js()
|
|
94
|
+
except FileNotFoundError:
|
|
95
|
+
return Failure("ts-query.js not found. Install Node.js to use TypeScript tools.")
|
|
96
|
+
|
|
97
|
+
query = {"command": command, **params}
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
result = subprocess.run(
|
|
101
|
+
["node", str(ts_query_path), json.dumps(query)],
|
|
102
|
+
cwd=str(project_root),
|
|
103
|
+
capture_output=True,
|
|
104
|
+
text=True,
|
|
105
|
+
timeout=30, # Safety timeout
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
if result.returncode != 0:
|
|
109
|
+
error_msg = result.stderr.strip() if result.stderr else "Unknown error"
|
|
110
|
+
return Failure(f"ts-query failed: {error_msg}")
|
|
111
|
+
|
|
112
|
+
output = json.loads(result.stdout)
|
|
113
|
+
|
|
114
|
+
# Check for error in output
|
|
115
|
+
if "error" in output:
|
|
116
|
+
return Failure(output["error"])
|
|
117
|
+
|
|
118
|
+
return Success(output)
|
|
119
|
+
|
|
120
|
+
except subprocess.TimeoutExpired:
|
|
121
|
+
return Failure("TypeScript query timed out (30s)")
|
|
122
|
+
except FileNotFoundError:
|
|
123
|
+
return Failure(
|
|
124
|
+
"Node.js not found.\n\n"
|
|
125
|
+
"To use TypeScript tools, install Node.js:\n"
|
|
126
|
+
"- macOS: brew install node\n"
|
|
127
|
+
"- Ubuntu: apt install nodejs\n"
|
|
128
|
+
"- Windows: https://nodejs.org/"
|
|
129
|
+
)
|
|
130
|
+
except json.JSONDecodeError as e:
|
|
131
|
+
return Failure(f"Invalid JSON from ts-query: {e}")
|
|
132
|
+
except Exception as e:
|
|
133
|
+
return Failure(f"TypeScript query error: {e}")
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def run_sig_typescript(file_path: Path) -> Result[list[TSSymbolInfo], str]:
|
|
137
|
+
"""Get signatures for TypeScript file using Compiler API.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
file_path: Path to TypeScript file
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
List of symbol information or error message
|
|
144
|
+
"""
|
|
145
|
+
project_root = _find_tsconfig_root(file_path)
|
|
146
|
+
result = query_typescript(project_root, "sig", file=str(file_path))
|
|
147
|
+
|
|
148
|
+
if isinstance(result, Failure):
|
|
149
|
+
return result
|
|
150
|
+
|
|
151
|
+
data = result.unwrap()
|
|
152
|
+
symbols = []
|
|
153
|
+
|
|
154
|
+
for sym in data.get("symbols", []):
|
|
155
|
+
symbols.append(
|
|
156
|
+
TSSymbolInfo(
|
|
157
|
+
name=sym.get("name", ""),
|
|
158
|
+
kind=sym.get("kind", ""),
|
|
159
|
+
signature=sym.get("signature", ""),
|
|
160
|
+
line=sym.get("line", 0),
|
|
161
|
+
file=str(file_path),
|
|
162
|
+
contracts=sym.get("contracts"),
|
|
163
|
+
members=sym.get("members"),
|
|
164
|
+
)
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
return Success(symbols)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def run_map_typescript(path: Path, top_n: int) -> Result[dict[str, Any], str]:
|
|
171
|
+
"""Get symbol map with reference counts for TypeScript project.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
path: Project path to scan
|
|
175
|
+
top_n: Maximum number of symbols to return
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
Symbol map data or error message
|
|
179
|
+
"""
|
|
180
|
+
return query_typescript(path, "map", path=str(path), top=top_n)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def run_refs_typescript(
|
|
184
|
+
file_path: Path, line: int, column: int
|
|
185
|
+
) -> Result[list[TSReference], str]:
|
|
186
|
+
"""Find all references to symbol at position.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
file_path: File containing the symbol
|
|
190
|
+
line: 1-based line number
|
|
191
|
+
column: 0-based column number
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
List of references or error message
|
|
195
|
+
"""
|
|
196
|
+
project_root = _find_tsconfig_root(file_path)
|
|
197
|
+
result = query_typescript(
|
|
198
|
+
project_root,
|
|
199
|
+
"refs",
|
|
200
|
+
file=str(file_path),
|
|
201
|
+
line=line,
|
|
202
|
+
column=column,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
if isinstance(result, Failure):
|
|
206
|
+
return result
|
|
207
|
+
|
|
208
|
+
data = result.unwrap()
|
|
209
|
+
references = []
|
|
210
|
+
|
|
211
|
+
for ref in data.get("references", []):
|
|
212
|
+
references.append(
|
|
213
|
+
TSReference(
|
|
214
|
+
file=ref.get("file", ""),
|
|
215
|
+
line=ref.get("line", 0),
|
|
216
|
+
column=ref.get("column", 0),
|
|
217
|
+
context=ref.get("context", ""),
|
|
218
|
+
is_definition=ref.get("isDefinition", False),
|
|
219
|
+
)
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
return Success(references)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def is_typescript_available() -> bool:
|
|
226
|
+
"""Check if TypeScript Compiler API tools are available."""
|
|
227
|
+
try:
|
|
228
|
+
_find_ts_query_js()
|
|
229
|
+
# Also check if Node.js is available
|
|
230
|
+
result = subprocess.run(
|
|
231
|
+
["node", "--version"],
|
|
232
|
+
capture_output=True,
|
|
233
|
+
text=True,
|
|
234
|
+
timeout=5,
|
|
235
|
+
)
|
|
236
|
+
return result.returncode == 0
|
|
237
|
+
except (FileNotFoundError, subprocess.TimeoutExpired):
|
|
238
|
+
return False
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# TypeScript Patterns for Agents
|
|
2
|
+
|
|
3
|
+
Reference patterns for AI agents working with TypeScript under Invar Protocol.
|
|
4
|
+
|
|
5
|
+
## Tool × Feature Matrix
|
|
6
|
+
|
|
7
|
+
| Feature | TypeScript Pattern | Tool Command |
|
|
8
|
+
|---------|-------------------|--------------|
|
|
9
|
+
| Signatures | `function name(params): Return` | `invar sig file.ts` |
|
|
10
|
+
| Contracts | `@pre`, `@post` JSDoc + Zod | `invar sig file.ts` |
|
|
11
|
+
| References | Cross-file symbol usage | `invar refs file.ts::Symbol` |
|
|
12
|
+
| Verification | tsc + eslint + vitest | `invar guard` |
|
|
13
|
+
| Document nav | Markdown structure | `invar doc toc file.md` |
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Pattern 1: Preconditions with Zod
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { z } from 'zod';
|
|
21
|
+
|
|
22
|
+
// Schema IS the precondition
|
|
23
|
+
const UserInputSchema = z.object({
|
|
24
|
+
email: z.string().email(),
|
|
25
|
+
age: z.number().int().positive().max(150),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
type UserInput = z.infer<typeof UserInputSchema>;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Create a user with validated input.
|
|
32
|
+
* @pre UserInputSchema.parse(input) succeeds
|
|
33
|
+
* @post result.id is set
|
|
34
|
+
*/
|
|
35
|
+
function createUser(input: UserInput): User {
|
|
36
|
+
// Zod already validated - safe to use
|
|
37
|
+
return { id: generateId(), ...input };
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Agent workflow:**
|
|
42
|
+
1. Define Zod schema FIRST (the @pre)
|
|
43
|
+
2. Derive TypeScript type from schema
|
|
44
|
+
3. Implement function body
|
|
45
|
+
4. Zod validates at runtime
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Pattern 2: Postconditions
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
/**
|
|
53
|
+
* Calculate discount price.
|
|
54
|
+
* @pre price > 0 && discount >= 0 && discount <= 1
|
|
55
|
+
* @post result >= 0 && result <= price
|
|
56
|
+
*/
|
|
57
|
+
function applyDiscount(price: number, discount: number): number {
|
|
58
|
+
const result = price * (1 - discount);
|
|
59
|
+
|
|
60
|
+
// Postcondition check (development only)
|
|
61
|
+
console.assert(result >= 0 && result <= price,
|
|
62
|
+
`Postcondition failed: ${result}`);
|
|
63
|
+
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Note:** Unlike Python's `@post` decorator, TypeScript postconditions
|
|
69
|
+
are documented in JSDoc and checked manually or via assertion.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Pattern 3: Core/Shell Separation
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
// ─── Core (Pure) ───
|
|
77
|
+
// No I/O, no side effects, only data transformations
|
|
78
|
+
|
|
79
|
+
function validateEmail(email: string): boolean {
|
|
80
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function calculateTotal(items: CartItem[]): number {
|
|
84
|
+
return items.reduce((sum, item) => sum + item.price * item.qty, 0);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ─── Shell (I/O) ───
|
|
88
|
+
// All external interactions, returns Result<T, E>
|
|
89
|
+
|
|
90
|
+
import { Result, ok, err } from 'neverthrow';
|
|
91
|
+
|
|
92
|
+
async function fetchUser(id: string): Promise<Result<User, ApiError>> {
|
|
93
|
+
try {
|
|
94
|
+
const response = await fetch(`/api/users/${id}`);
|
|
95
|
+
if (!response.ok) return err({ code: response.status });
|
|
96
|
+
return ok(await response.json());
|
|
97
|
+
} catch (e) {
|
|
98
|
+
return err({ code: 500, message: String(e) });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Agent checklist:**
|
|
104
|
+
- [ ] Core functions: No imports from `fs`, `http`, `fetch`, etc.
|
|
105
|
+
- [ ] Shell functions: Return `Result<T, E>` for fallible operations
|
|
106
|
+
- [ ] Dependency injection: Pass data to Core, not paths
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Pattern 4: Exhaustive Switch
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
type Status = 'pending' | 'approved' | 'rejected';
|
|
114
|
+
|
|
115
|
+
function getStatusMessage(status: Status): string {
|
|
116
|
+
switch (status) {
|
|
117
|
+
case 'pending': return 'Waiting for review';
|
|
118
|
+
case 'approved': return 'Request approved';
|
|
119
|
+
case 'rejected': return 'Request denied';
|
|
120
|
+
default:
|
|
121
|
+
// TypeScript ensures this is never reached
|
|
122
|
+
const _exhaustive: never = status;
|
|
123
|
+
throw new Error(`Unknown status: ${_exhaustive}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Why:** Adding a new status forces handling in all switches.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Pattern 5: Branded Types
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
// Nominal typing for semantic safety
|
|
136
|
+
type UserId = string & { readonly __brand: 'UserId' };
|
|
137
|
+
type OrderId = string & { readonly __brand: 'OrderId' };
|
|
138
|
+
|
|
139
|
+
function createUserId(id: string): UserId {
|
|
140
|
+
return id as UserId;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Compiler prevents mixing IDs
|
|
144
|
+
function getUser(id: UserId): User { ... }
|
|
145
|
+
function getOrder(id: OrderId): Order { ... }
|
|
146
|
+
|
|
147
|
+
getUser(orderId); // ❌ Type error: OrderId is not UserId
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Tool Usage Examples
|
|
153
|
+
|
|
154
|
+
### View signatures
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
$ invar sig src/auth.ts
|
|
158
|
+
src/auth.ts
|
|
159
|
+
function validateToken(token: string): boolean
|
|
160
|
+
@pre token.length > 0
|
|
161
|
+
@post result indicates valid JWT
|
|
162
|
+
|
|
163
|
+
class AuthService
|
|
164
|
+
method login(email: string, password: string): Promise<Result<Token, AuthError>>
|
|
165
|
+
method logout(): Promise<void>
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Find references
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
$ invar refs src/auth.ts::validateToken
|
|
172
|
+
src/auth.ts:15 — Definition
|
|
173
|
+
src/routes/api.ts:42 — if (validateToken(req.headers.auth)) {
|
|
174
|
+
src/middleware/auth.ts:18 — const isValid = validateToken(token);
|
|
175
|
+
tests/auth.test.ts:8 — expect(validateToken('invalid')).toBe(false);
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Verify code
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
$ invar guard
|
|
182
|
+
TypeScript Guard Report
|
|
183
|
+
========================================
|
|
184
|
+
[PASS] tsc --noEmit (no type errors)
|
|
185
|
+
[PASS] eslint (0 errors, 2 warnings)
|
|
186
|
+
[PASS] vitest (24 tests passed)
|
|
187
|
+
----------------------------------------
|
|
188
|
+
Guard passed.
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
*Managed by Invar - regenerated on `invar update`*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: invar-tools
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.12.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: jedi>=0.19
|
|
27
28
|
Requires-Dist: jinja2>=3.0
|
|
28
29
|
Requires-Dist: markdown-it-py>=3.0
|
|
29
30
|
Requires-Dist: mcp>=1.0
|
|
@@ -146,7 +147,19 @@ Guard passed.
|
|
|
146
147
|
|
|
147
148
|
## 🚀 Quick Start
|
|
148
149
|
|
|
149
|
-
|
|
150
|
+
### Tool × Language Support
|
|
151
|
+
|
|
152
|
+
| Tool | Python | TypeScript | Notes |
|
|
153
|
+
|------|--------|------------|-------|
|
|
154
|
+
| `invar guard` | ✅ Full | ⚠️ Partial | TS: tsc + eslint + vitest |
|
|
155
|
+
| `invar sig` | ✅ Full | ✅ Full | TS: TS Compiler API |
|
|
156
|
+
| `invar map` | ✅ Full | ✅ Full | TS: With reference counts |
|
|
157
|
+
| `invar refs` | ✅ Full | ✅ Full | Cross-file reference finding |
|
|
158
|
+
| `invar doc *` | ✅ Full | ✅ Full | Language-agnostic |
|
|
159
|
+
|
|
160
|
+
**TypeScript Notes:**
|
|
161
|
+
- Requires Node.js + TypeScript (most TS projects have these)
|
|
162
|
+
- Falls back to regex parser if Node.js unavailable
|
|
150
163
|
|
|
151
164
|
### 📦 Two Packages, Different Purposes
|
|
152
165
|
|
|
@@ -303,14 +316,17 @@ function average(items: number[]): number {
|
|
|
303
316
|
|
|
304
317
|
### ✅ Solution 2: Multi-Layer Verification
|
|
305
318
|
|
|
306
|
-
Guard provides fast feedback
|
|
319
|
+
Guard provides fast feedback **on top of standard type checking**. Agent sees errors, fixes immediately:
|
|
307
320
|
|
|
308
321
|
| Layer | Tool | Speed | What It Catches |
|
|
309
322
|
|-------|------|-------|-----------------|
|
|
323
|
+
| **Type Check*** | mypy (Python) / tsc (TypeScript) | ~1s | Type errors, missing annotations |
|
|
310
324
|
| **Static** | Guard rules | ~0.5s | Architecture violations, missing contracts |
|
|
311
|
-
| **Doctest** | pytest | ~2s | Example correctness |
|
|
312
|
-
| **Property** | Hypothesis | ~10s | Edge cases via random inputs |
|
|
313
|
-
| **Symbolic** | CrossHair | ~30s | Mathematical proof of contracts |
|
|
325
|
+
| **Doctest** | pytest / vitest | ~2s | Example correctness |
|
|
326
|
+
| **Property** | Hypothesis / fast-check | ~10s | Edge cases via random inputs |
|
|
327
|
+
| **Symbolic** | CrossHair / (TS: N/A) | ~30s | Mathematical proof of contracts |
|
|
328
|
+
|
|
329
|
+
<sup>* Requires separate installation: `pip install mypy` or configure TypeScript in your project</sup>
|
|
314
330
|
|
|
315
331
|
```
|
|
316
332
|
┌──────────┐ ┌───────────┐ ┌───────────┐ ┌────────────┐
|
|
@@ -525,7 +541,7 @@ Cursor users get full verification via MCP:
|
|
|
525
541
|
|----------------|---------|----------|
|
|
526
542
|
| `INVAR.md` | Protocol for AI agents | Required |
|
|
527
543
|
| `.invar/` | Config, context, examples | Required |
|
|
528
|
-
| `.pre-commit-config.yaml` | Verification before commit | Optional |
|
|
544
|
+
| `.pre-commit-config.yaml` | Verification before commit (Ruff, mypy*, Guard) | Optional |
|
|
529
545
|
| `src/core/`, `src/shell/` | Recommended structure | Optional |
|
|
530
546
|
| `CLAUDE.md` | Agent instructions | Claude Code |
|
|
531
547
|
| `.claude/skills/` | Workflow + extension skills | Claude Code |
|
|
@@ -534,6 +550,8 @@ Cursor users get full verification via MCP:
|
|
|
534
550
|
| `.mcp.json` | MCP server config | Claude Code |
|
|
535
551
|
| `AGENT.md` | Universal agent instructions | Other agents |
|
|
536
552
|
|
|
553
|
+
<sup>* mypy hook included in `.pre-commit-config.yaml` but requires: `pip install mypy`</sup>
|
|
554
|
+
|
|
537
555
|
**Note:** If `pyproject.toml` exists, Guard configuration goes there as `[tool.invar.guard]` instead of `.invar/config.toml`.
|
|
538
556
|
|
|
539
557
|
**Recommended structure:**
|
|
@@ -49,10 +49,11 @@ invar/core/patterns/registry.py,sha256=2rz0wWDRarMkuHN-qM_ZrT3qeGFDSKMABvRvPNZxQ
|
|
|
49
49
|
invar/core/patterns/types.py,sha256=ULAlWuAdmO6CFcEDjTrWBfzNTBsnomAl2d25tR11ihU,5506
|
|
50
50
|
invar/mcp/__init__.py,sha256=n3S7QwMjSMqOMT8cI2jf9E0yZPjKmBOJyIYhq4WZ8TQ,226
|
|
51
51
|
invar/mcp/__main__.py,sha256=ZcIT2U6xUyGOWucl4jq422BDE3lRLjqyxb9pFylRBdk,219
|
|
52
|
-
invar/mcp/handlers.py,sha256=
|
|
53
|
-
invar/mcp/server.py,sha256=
|
|
52
|
+
invar/mcp/handlers.py,sha256=b0LRWKMpu6lnjPV7SozH0cxKYJufVWy3eJhiAu3of-M,15947
|
|
53
|
+
invar/mcp/server.py,sha256=WmVNs2_AcOMbmp3tEgWR57hjqqJkMx3nV3z9fcNuSAA,20109
|
|
54
54
|
invar/node_tools/MANIFEST,sha256=UwtO2AsQ-0UwskG6ZkE2kXqz_hdp-gzRTyp32-X22Mc,131
|
|
55
55
|
invar/node_tools/__init__.py,sha256=HzILh3jtP28Lm2jZwss1SY65ECxbtw2J2uFpXQA6Y94,1740
|
|
56
|
+
invar/node_tools/ts-query.js,sha256=fEc_f0JT_Mb18dEoA4_vJoazvd7Lqv_rsed4eHSAbCg,13303
|
|
56
57
|
invar/node_tools/fc-runner/cli.js,sha256=72_gIhvnx2peKITdzdnFWI5fzGaNTS3BcEqyS628cI0,243277
|
|
57
58
|
invar/node_tools/quick-check/cli.js,sha256=dwV3hdJleFQga2cKUn3PPfQDvvujhzKdjQcIvWsKgM0,66196
|
|
58
59
|
invar/node_tools/ts-analyzer/cli.js,sha256=SvZ6HyjmobpP8NAZqXFiy8BwH_t5Hb17Ytar_18udaQ,4092887
|
|
@@ -71,19 +72,21 @@ invar/shell/mutation.py,sha256=Lfyk2b8j8-hxAq-iwAgQeOhr7Ci6c5tRF1TXe3CxQCs,8914
|
|
|
71
72
|
invar/shell/pattern_integration.py,sha256=pRcjfq3NvMW_tvQCnaXZnD1k5AVEWK8CYOE2jN6VTro,7842
|
|
72
73
|
invar/shell/pi_hooks.py,sha256=ulZc1sP8mTRJTBsjwFHQzUgg-h8ajRIMp7iF1Y4UUtw,6885
|
|
73
74
|
invar/shell/property_tests.py,sha256=N9JreyH5PqR89oF5yLcX7ZAV-Koyg5BKo-J05-GUPsA,9109
|
|
75
|
+
invar/shell/py_refs.py,sha256=Vjz50lmt9prDBcBv4nkkODdiJ7_DKu5zO4UPZBjAfmM,4638
|
|
74
76
|
invar/shell/skill_manager.py,sha256=Mr7Mh9rxPSKSAOTJCAM5ZHiG5nfUf6KQVCuD4LBNHSI,12440
|
|
75
77
|
invar/shell/subprocess_env.py,sha256=9oXl3eMEbzLsFEgMHqobEw6oW_wV0qMEP7pklwm58Pw,11453
|
|
76
78
|
invar/shell/template_engine.py,sha256=eNKMz7R8g9Xp3_1TGx-QH137jf52E0u3KaVcnotu1Tg,12056
|
|
77
79
|
invar/shell/templates.py,sha256=31f5ieoGeWU0qqfLJUMWnz0yyLa1FBc_sOz6UGzToqk,13884
|
|
78
80
|
invar/shell/testing.py,sha256=rTNBH0Okh2qtG9ohSXOz487baQ2gXrWT3s_WECW3HJs,11143
|
|
81
|
+
invar/shell/ts_compiler.py,sha256=nA8brnOhThj9J_J3vAEGjDsM4NjbWQ_eX8Yf4pHPOgk,6672
|
|
79
82
|
invar/shell/commands/__init__.py,sha256=MEkKwVyjI9DmkvBpJcuumXo2Pg_FFkfEr-Rr3nrAt7A,284
|
|
80
83
|
invar/shell/commands/doc.py,sha256=SOLDoCXXGxx_JU0PKXlAIGEF36PzconHmmAtL-rM6D4,13819
|
|
81
|
-
invar/shell/commands/guard.py,sha256
|
|
84
|
+
invar/shell/commands/guard.py,sha256=p0kI63RUzC1OTh_YviviY6dz2bq_SoBksGOp2NyocQM,24512
|
|
82
85
|
invar/shell/commands/hooks.py,sha256=W-SOnT4VQyUvXwipozkJwgEYfiOJGz7wksrbcdWegUg,2356
|
|
83
86
|
invar/shell/commands/init.py,sha256=-FZTYTwJr_NRoluAM4YwcseNju56GSxtnKVkqsXREPg,20461
|
|
84
87
|
invar/shell/commands/merge.py,sha256=nuvKo8m32-OL-SCQlS4SLKmOZxQ3qj-1nGCx1Pgzifw,8183
|
|
85
88
|
invar/shell/commands/mutate.py,sha256=GwemiO6LlbGCBEQsBFnzZuKhF-wIMEl79GAMnKUWc8U,5765
|
|
86
|
-
invar/shell/commands/perception.py,sha256=
|
|
89
|
+
invar/shell/commands/perception.py,sha256=HewSv6Kv8Gw2UQqkGY2rP5YKlnwyC3LBrQ2hFVXXw30,19304
|
|
87
90
|
invar/shell/commands/skill.py,sha256=oKVyaxQ_LK28FpJhRpBDpXcpRdUBK3n6rC0qD77ax1M,5803
|
|
88
91
|
invar/shell/commands/sync_self.py,sha256=nmqBry7V2_enKwy2zzHg8UoedZNicLe3yKDhjmBeZ68,3880
|
|
89
92
|
invar/shell/commands/template_sync.py,sha256=aNWyFPMFT7pSwHrvwGCqcKAwb4dp7S9tvZzy9H4gAnw,16094
|
|
@@ -124,6 +127,7 @@ invar/templates/examples/typescript/README.md,sha256=I6kVTJAk6wqb21eC09hyOZXRP74
|
|
|
124
127
|
invar/templates/examples/typescript/contracts.ts,sha256=X6EeNUAurw9MHJlHPDos_JtnScMu2aDMSkveIaA4wHs,4597
|
|
125
128
|
invar/templates/examples/typescript/core_shell.ts,sha256=VuPl0xmn_s6pA7ZJtq0zNjXzK_DRmq3LexwTS8MEqnQ,11267
|
|
126
129
|
invar/templates/examples/typescript/functional.ts,sha256=bQHfl0EYDC_RziRQLJBgPNBGhWBBO7SVSVchqBJUetM,17347
|
|
130
|
+
invar/templates/examples/typescript/patterns.md,sha256=rlZ6IOC8iYUQzIXaW7fUf95OQbeWPWSjQXDDJNoU05E,4859
|
|
127
131
|
invar/templates/examples/typescript/workflow.md,sha256=5byADjA3WgOgiDbkEtVRKKGvlhvu3EbFCzqEg9lEV7k,2764
|
|
128
132
|
invar/templates/hooks/PostToolUse.sh.jinja,sha256=JHJGMdF3xp2qEqkPC9GaLp0NCa5gdRzqAmgRy4IldBg,3428
|
|
129
133
|
invar/templates/hooks/PreToolUse.sh.jinja,sha256=tZb-FGFxOBtTprUfeChau7rZOMPII69_5HSF-i_WD4Q,3558
|
|
@@ -169,10 +173,10 @@ invar/templates/skills/extensions/security/patterns/typescript.yaml,sha256=qDEg-
|
|
|
169
173
|
invar/templates/skills/investigate/SKILL.md.jinja,sha256=cp6TBEixBYh1rLeeHOR1yqEnFqv1NZYePORMnavLkQI,3231
|
|
170
174
|
invar/templates/skills/propose/SKILL.md.jinja,sha256=6BuKiCqO1AEu3VtzMHy1QWGqr_xqG9eJlhbsKT4jev4,3463
|
|
171
175
|
invar/templates/skills/review/SKILL.md.jinja,sha256=ET5mbdSe_eKgJbi2LbgFC-z1aviKcHOBw7J5Q28fr4U,14105
|
|
172
|
-
invar_tools-1.
|
|
173
|
-
invar_tools-1.
|
|
174
|
-
invar_tools-1.
|
|
175
|
-
invar_tools-1.
|
|
176
|
-
invar_tools-1.
|
|
177
|
-
invar_tools-1.
|
|
178
|
-
invar_tools-1.
|
|
176
|
+
invar_tools-1.12.0.dist-info/METADATA,sha256=jpbC0FFokpDTexuAf5nodUMsRM46nU5-ZC0SmVVN5cQ,28409
|
|
177
|
+
invar_tools-1.12.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
178
|
+
invar_tools-1.12.0.dist-info/entry_points.txt,sha256=RwH_EhqgtFPsnO6RcrwrAb70Zyfb8Mh6uUtztWnUxGk,102
|
|
179
|
+
invar_tools-1.12.0.dist-info/licenses/LICENSE,sha256=qeFksp4H4kfTgQxPCIu3OdagXyiZcgBlVfsQ6M5oFyk,10767
|
|
180
|
+
invar_tools-1.12.0.dist-info/licenses/LICENSE-GPL,sha256=IvZfC6ZbP7CLjytoHVzvpDZpD-Z3R_qa1GdMdWlWQ6Q,35157
|
|
181
|
+
invar_tools-1.12.0.dist-info/licenses/NOTICE,sha256=joEyMyFhFY8Vd8tTJ-a3SirI0m2Sd0WjzqYt3sdcglc,2561
|
|
182
|
+
invar_tools-1.12.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|