# Winkers

## Architecture

- `src/winkers/models.py` — Pydantic models: Graph, FunctionNode, FileNode, CallEdge, ImportEdge
- `src/winkers/parser.py` — TreeSitterParser (tree-sitter 0.25 API, QueryCursor)
- `src/winkers/graph.py` — GraphBuilder: parse all files → build Graph
- `src/winkers/resolver.py` — CrossFileResolver: raw calls → CallEdges with confidence
- `src/winkers/store.py` — GraphStore: save/load .winkers/graph.json
- `src/winkers/semantic.py` — SemanticLayer: Claude API enrichment → .winkers/semantic.json
- `src/winkers/ui_map.py` — UIMap: scan HTML/Jinja2 templates → link Flask routes to UI elements (panels, tables, forms)
- `src/winkers/languages/` — language profiles: Python, TypeScript, JavaScript, Java, Go, Rust, C#
- `src/winkers/mcp/` — MCP server (tools: orient, scope, convention_read, rule_read) + auto-rebuild on file changes
- `src/winkers/dashboard/` — aiohttp + Cytoscape.js browser dashboard
- `src/winkers/cli/` — CLI (click): init, serve, dashboard

## Commands

```bash
.venv/Scripts/pytest tests/ -v       # run tests
.venv/Scripts/ruff check src/ tests/ # lint
winkers init                          # build graph + semantic + rules audit
winkers init --no-semantic            # graph only, no API call
winkers init --force/-f               # force re-enrichment even if graph unchanged
winkers init --yes/-y                 # auto-accept all rule proposals (no interactive review)
winkers serve                         # MCP server (stdio)
winkers dashboard                     # browser graph
winkers conventions-migrate           # import conventions from existing semantic.json
```

## Rules

- All models: Pydantic BaseModel with type hints
- Graph helpers: use `graph.is_locked(fn_id)`, `graph.callers(fn_id)`, `graph.callees(fn_id)` — methods on Graph
- tree-sitter: use QueryCursor for captures/matches (tree-sitter 0.25 API). Do NOT use tree-sitter-languages
- Language packages: individual (tree-sitter-python, tree-sitter-java, etc.)
- Functions < 40 lines, one class per concern
- No dynamic imports

## Three-file model

- **graph.json** — structure: files, functions, call edges, import edges, complexity
- **semantic.json** — intent: zone intents, data_flow, domain_context, checklist (generated by Claude API, one call)
- **rules.json** — conventions/rules (ConventionRule with stats; generated by LLM audit + detectors, accepted by user)

Zero duplication. Graph = facts. Semantic = meaning. Rules = standards.

## Semantic schema

- `ZoneIntent` — why zone exists + wrong_approach
- `MonsterFile` — sections (prefix, purpose, count) + where_to_add
- `SemanticLayer` — data_flow, domain_context, zone_intents, monster_files, new_feature_checklist
- `proposed_rules` — returned by SemanticEnricher but NOT saved to semantic.json → goes to rules.json after user confirmation

`conventions[]` and `constraints[]` are removed — replaced by rules.json.

## MCP tools (4 total)

- `orient(include, zone?, min_callers?)` — single entry point; include: "map", "conventions", "rules_list", "functions_graph", "hotspots", "routes", "ui_map"
- `scope(function?, file?)` — deep context: callers, callees, related_rules, recent changes
- `convention_read(target)` — zone name as in conventions (e.g. "app.py") / "data_flow" / "domain_context" / "checklist"
- `rule_read(category)` — all rules for a category + wrong_approach; use orient rules_list for available categories

## Test fixtures

- `tests/fixtures/python_project/` — modules/pricing.py, modules/inventory.py, api/prices.py, models.py
- `tests/fixtures/typescript_project/` — src/pricing.ts, src/inventory.ts, src/api/prices.ts
- `tests/fixtures/java_project/`, `go_project/`, `rust_project/`, `csharp_project/`
- `tests/fixtures/flask_project/` — app.py, templates/index.html, templates/products/list.html

<!-- winkers-snippet-version: 0.7.4 -->
## Architectural context (Winkers)

This project uses [Winkers](https://github.com/n1cke1/winkers) for
architectural context: dependency graph, semantic zones, coding conventions,
data flow, and UI mapping. MCP server `winkers` is connected.

### Before modifying code

1. `orient(["map","conventions"])` — project structure, zones, hotspots, data flow, zone intents. **Call first.**
2. `scope(file="<path>")` — locked/free functions, callers, related rules for that file.
3. `orient(["rules_list"])` — available coding rule categories; then `rule_read("<category>")` for details.

### After modifying code

- `scope(function="<name>")` — verify callers are not broken by your change.

### Key concepts

- **locked** = function has callers depending on its signature. Do not change
  param types, order, or return type without updating all callers.
- **free** = no callers. Modify freely.

### Other tools

- `orient(["functions_graph"])` — full indexed function list with caller counts.
- `orient(["routes"])` — HTTP endpoints (Flask/FastAPI): method, path, handler, callees.
- `orient(["ui_map"])` — Flask route→template links with UI elements (panels, tables, forms, headings).
- `orient(["hotspots"])` — functions with many callers; high-impact changes.
- `convention_read("<zone>")` — zone intent details (e.g. "app.py", "data_flow", "checklist").
