wythe 0.1.0__tar.gz
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.
- wythe-0.1.0/.gitignore +28 -0
- wythe-0.1.0/LICENSE +21 -0
- wythe-0.1.0/PKG-INFO +230 -0
- wythe-0.1.0/README.md +186 -0
- wythe-0.1.0/backend/app/__init__.py +3 -0
- wythe-0.1.0/backend/app/adapters/__init__.py +50 -0
- wythe-0.1.0/backend/app/adapters/a11y_inspect.py +134 -0
- wythe-0.1.0/backend/app/adapters/autodetect.py +195 -0
- wythe-0.1.0/backend/app/adapters/base.py +353 -0
- wythe-0.1.0/backend/app/adapters/db_catalog_probe.py +179 -0
- wythe-0.1.0/backend/app/adapters/django_truth.py +249 -0
- wythe-0.1.0/backend/app/adapters/drizzle_truth.py +191 -0
- wythe-0.1.0/backend/app/adapters/frontend_common.py +209 -0
- wythe-0.1.0/backend/app/adapters/go_http.py +360 -0
- wythe-0.1.0/backend/app/adapters/legacy_snapshot_bridge.py +165 -0
- wythe-0.1.0/backend/app/adapters/postgres_sqlalchemy.py +311 -0
- wythe-0.1.0/backend/app/adapters/prisma_truth.py +215 -0
- wythe-0.1.0/backend/app/adapters/python_ast_inspect.py +331 -0
- wythe-0.1.0/backend/app/adapters/python_django.py +394 -0
- wythe-0.1.0/backend/app/adapters/python_fastapi.py +311 -0
- wythe-0.1.0/backend/app/adapters/redis_ephemeral.py +454 -0
- wythe-0.1.0/backend/app/adapters/rust_http.py +393 -0
- wythe-0.1.0/backend/app/adapters/rust_inspect.py +144 -0
- wythe-0.1.0/backend/app/adapters/rust_sql_truth.py +240 -0
- wythe-0.1.0/backend/app/adapters/rust_wasm_display.py +279 -0
- wythe-0.1.0/backend/app/adapters/sql_ddl_truth.py +165 -0
- wythe-0.1.0/backend/app/adapters/sveltekit_display.py +168 -0
- wythe-0.1.0/backend/app/adapters/treesitter_engine.py +388 -0
- wythe-0.1.0/backend/app/adapters/typescript_nextjs.py +522 -0
- wythe-0.1.0/backend/app/adapters/typescript_node.py +472 -0
- wythe-0.1.0/backend/app/adapters/typescript_react.py +143 -0
- wythe-0.1.0/backend/app/adapters/vue_sfc.py +122 -0
- wythe-0.1.0/backend/app/api/__init__.py +1 -0
- wythe-0.1.0/backend/app/api/routes/__init__.py +28 -0
- wythe-0.1.0/backend/app/api/routes/adapters.py +38 -0
- wythe-0.1.0/backend/app/api/routes/ask.py +38 -0
- wythe-0.1.0/backend/app/api/routes/focus.py +48 -0
- wythe-0.1.0/backend/app/api/routes/mcp_http.py +53 -0
- wythe-0.1.0/backend/app/api/routes/rules.py +32 -0
- wythe-0.1.0/backend/app/api/routes/rulings.py +44 -0
- wythe-0.1.0/backend/app/api/routes/scans.py +229 -0
- wythe-0.1.0/backend/app/api/routes/targets.py +73 -0
- wythe-0.1.0/backend/app/api/routes/vocabularies.py +48 -0
- wythe-0.1.0/backend/app/cli.py +431 -0
- wythe-0.1.0/backend/app/core/clock.py +20 -0
- wythe-0.1.0/backend/app/core/config.py +45 -0
- wythe-0.1.0/backend/app/core/policy_context.py +49 -0
- wythe-0.1.0/backend/app/db/base.py +9 -0
- wythe-0.1.0/backend/app/db/seed.py +429 -0
- wythe-0.1.0/backend/app/db/session.py +45 -0
- wythe-0.1.0/backend/app/main.py +73 -0
- wythe-0.1.0/backend/app/models/__init__.py +18 -0
- wythe-0.1.0/backend/app/models/audit_ruling.py +35 -0
- wythe-0.1.0/backend/app/models/policy_vocabulary.py +28 -0
- wythe-0.1.0/backend/app/models/rule_policy.py +31 -0
- wythe-0.1.0/backend/app/models/scan_snapshot.py +64 -0
- wythe-0.1.0/backend/app/models/stack_profile.py +28 -0
- wythe-0.1.0/backend/app/models/target_project.py +37 -0
- wythe-0.1.0/backend/app/schemas/__init__.py +49 -0
- wythe-0.1.0/backend/app/schemas/atlas.py +595 -0
- wythe-0.1.0/backend/app/services/__init__.py +32 -0
- wythe-0.1.0/backend/app/services/apple_fm_bridge.swift +154 -0
- wythe-0.1.0/backend/app/services/apple_fm_service.py +179 -0
- wythe-0.1.0/backend/app/services/diff_and_sarif_service.py +307 -0
- wythe-0.1.0/backend/app/services/gate_service.py +152 -0
- wythe-0.1.0/backend/app/services/governance_service.py +347 -0
- wythe-0.1.0/backend/app/services/llm_context_service.py +367 -0
- wythe-0.1.0/backend/app/services/mcp_service.py +352 -0
- wythe-0.1.0/backend/app/services/on_device_llm_service.py +377 -0
- wythe-0.1.0/backend/app/services/portable_config_service.py +264 -0
- wythe-0.1.0/backend/app/services/scanner_service.py +455 -0
- wythe-0.1.0/backend/app/services/source_peek_service.py +99 -0
- wythe-0.1.0/backend/app/services/ui_focus_service.py +322 -0
- wythe-0.1.0/backend/app/ui_dist/404.html +1 -0
- wythe-0.1.0/backend/app/ui_dist/_next/static/chunks/255-15b9cc804e0d9c7d.js +1 -0
- wythe-0.1.0/backend/app/ui_dist/_next/static/chunks/271.0e22de97daf8bd5d.js +1 -0
- wythe-0.1.0/backend/app/ui_dist/_next/static/chunks/484-3d0aa024c79e43c2.js +1 -0
- wythe-0.1.0/backend/app/ui_dist/_next/static/chunks/4bd1b696-c023c6e3521b1417.js +1 -0
- wythe-0.1.0/backend/app/ui_dist/_next/static/chunks/587.bfba9c61b4b8b4dd.js +1 -0
- wythe-0.1.0/backend/app/ui_dist/_next/static/chunks/854-20e64eb36f1ea9bd.js +1 -0
- wythe-0.1.0/backend/app/ui_dist/_next/static/chunks/app/_not-found/page-34ccb94a53b07c2e.js +1 -0
- wythe-0.1.0/backend/app/ui_dist/_next/static/chunks/app/layout-2a1afe0e8af2e7d3.js +1 -0
- wythe-0.1.0/backend/app/ui_dist/_next/static/chunks/app/page-db1dd94b92580ee1.js +1 -0
- wythe-0.1.0/backend/app/ui_dist/_next/static/chunks/b536a0f1.6112d0b47631385b.js +1 -0
- wythe-0.1.0/backend/app/ui_dist/_next/static/chunks/framework-28b60818f5cffd83.js +1 -0
- wythe-0.1.0/backend/app/ui_dist/_next/static/chunks/main-304d3bdb216e1c53.js +1 -0
- wythe-0.1.0/backend/app/ui_dist/_next/static/chunks/main-app-d5f696847cf7b405.js +1 -0
- wythe-0.1.0/backend/app/ui_dist/_next/static/chunks/pages/_app-7d307437aca18ad4.js +1 -0
- wythe-0.1.0/backend/app/ui_dist/_next/static/chunks/pages/_error-cb2a52f75f2162e2.js +1 -0
- wythe-0.1.0/backend/app/ui_dist/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- wythe-0.1.0/backend/app/ui_dist/_next/static/chunks/webpack-c2a1ca9f6b7eac69.js +1 -0
- wythe-0.1.0/backend/app/ui_dist/_next/static/css/635f7fcdb3fbfb46.css +1 -0
- wythe-0.1.0/backend/app/ui_dist/_next/static/nVGQZh6kCJlURomjaOCjL/_buildManifest.js +1 -0
- wythe-0.1.0/backend/app/ui_dist/_next/static/nVGQZh6kCJlURomjaOCjL/_ssgManifest.js +1 -0
- wythe-0.1.0/backend/app/ui_dist/icon.svg +11 -0
- wythe-0.1.0/backend/app/ui_dist/index.html +1 -0
- wythe-0.1.0/backend/app/ui_dist/index.txt +21 -0
- wythe-0.1.0/backend/schema.sql +124 -0
- wythe-0.1.0/backend/tests/conftest.py +38 -0
- wythe-0.1.0/backend/tests/test_api_and_cli.py +155 -0
- wythe-0.1.0/backend/tests/test_db_domain_truth.py +218 -0
- wythe-0.1.0/backend/tests/test_diff_sarif_portable_and_redis.py +434 -0
- wythe-0.1.0/backend/tests/test_llm_and_mcp.py +408 -0
- wythe-0.1.0/backend/tests/test_multilang_django_and_go.py +237 -0
- wythe-0.1.0/backend/tests/test_multilang_ts_and_db.py +200 -0
- wythe-0.1.0/backend/tests/test_queues_a11y_and_compare.py +203 -0
- wythe-0.1.0/backend/tests/test_rust_stacks.py +249 -0
- wythe-0.1.0/backend/tests/test_three_tier_scanner.py +308 -0
- wythe-0.1.0/backend/tests/test_treesitter_engine.py +191 -0
- wythe-0.1.0/pyproject.toml +96 -0
wythe-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
.venv/
|
|
6
|
+
.pytest_cache/
|
|
7
|
+
.ruff_cache/
|
|
8
|
+
.mypy_cache/
|
|
9
|
+
*.egg-info/
|
|
10
|
+
dist/
|
|
11
|
+
build/
|
|
12
|
+
*.db
|
|
13
|
+
*.sqlite3
|
|
14
|
+
|
|
15
|
+
# Node / Next.js
|
|
16
|
+
node_modules/
|
|
17
|
+
.next/
|
|
18
|
+
out/
|
|
19
|
+
coverage/
|
|
20
|
+
next-env.d.ts
|
|
21
|
+
*.tsbuildinfo
|
|
22
|
+
|
|
23
|
+
# OS / Local state
|
|
24
|
+
.DS_Store
|
|
25
|
+
.local/
|
|
26
|
+
.env
|
|
27
|
+
.env.*
|
|
28
|
+
!.env.example
|
wythe-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aaron Montgomery
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
wythe-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: wythe
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Why -> Therefore: 3-tier architecture verifier, cross-boundary lineage solver, and 3D WebGL atlas.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Euda1mon1a/wythe
|
|
6
|
+
Project-URL: Repository, https://github.com/Euda1mon1a/wythe
|
|
7
|
+
Project-URL: Issues, https://github.com/Euda1mon1a/wythe/issues
|
|
8
|
+
Author: Aaron Montgomery
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: accessibility,architecture,lineage,mcp,openapi,sarif,static-analysis,three-tier
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Environment :: Web Environment
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
20
|
+
Requires-Python: >=3.12
|
|
21
|
+
Requires-Dist: alembic>=1.14.0
|
|
22
|
+
Requires-Dist: fastapi>=0.115.0
|
|
23
|
+
Requires-Dist: httpx>=0.27.0
|
|
24
|
+
Requires-Dist: psycopg[binary]>=3.2.0
|
|
25
|
+
Requires-Dist: pydantic>=2.9.0
|
|
26
|
+
Requires-Dist: pyyaml>=6.0.2
|
|
27
|
+
Requires-Dist: sqlalchemy>=2.0.35
|
|
28
|
+
Requires-Dist: uvicorn>=0.32.0
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=8.3.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: ruff>=0.7.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: tree-sitter-go>=0.23.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: tree-sitter-javascript>=0.23.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: tree-sitter-rust>=0.23.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: tree-sitter-typescript>=0.23.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: tree-sitter>=0.23.0; extra == 'dev'
|
|
37
|
+
Provides-Extra: treesitter
|
|
38
|
+
Requires-Dist: tree-sitter-go>=0.23.0; extra == 'treesitter'
|
|
39
|
+
Requires-Dist: tree-sitter-javascript>=0.23.0; extra == 'treesitter'
|
|
40
|
+
Requires-Dist: tree-sitter-rust>=0.23.0; extra == 'treesitter'
|
|
41
|
+
Requires-Dist: tree-sitter-typescript>=0.23.0; extra == 'treesitter'
|
|
42
|
+
Requires-Dist: tree-sitter>=0.23.0; extra == 'treesitter'
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
|
|
45
|
+
<p align="center">
|
|
46
|
+
<picture>
|
|
47
|
+
<source media="(prefers-color-scheme: light)" srcset="docs/icon/icon-light.svg">
|
|
48
|
+
<img src="docs/icon/icon-dark.svg" width="96" height="96" alt="Wythe icon — three architectural wythe bars mortised by a Why (?) triskelion and Therefore (∴) core">
|
|
49
|
+
</picture>
|
|
50
|
+
</p>
|
|
51
|
+
|
|
52
|
+
# Wythe
|
|
53
|
+
|
|
54
|
+
**Why (`?`) → Therefore (`∴`)** — Generalizable 3-tier architecture verifier, cross-boundary lineage solver, interactive 3D WebGL latticework visualizer, and fail-closed CI gate built around three masonry wythe invariants:
|
|
55
|
+
|
|
56
|
+
1. **Database holds truth** — Relational schemas (`PostgreSQL`, `MySQL`, `SQLite` via `SQLAlchemy 2.0` + `Alembic`, `Prisma`, `Drizzle ORM`, `Django ORM`, Rust `sqlx`/`Diesel`/`SeaORM`, or raw SQL DDL/`sqlc` + optional read-only catalog probes) own domain policy, thresholds, temporal validity, and write-lane guards, while `Redis` keyspaces and worker queues are modeled explicitly as ephemeral stores/lanes rather than durable shadow truth.
|
|
57
|
+
2. **Backend manipulates truth** — Services, background workers, solvers, and routers (`Python/FastAPI`, `TypeScript/Node/Express/Hono/Fastify/NestJS/Next.js/SvelteKit`, `Python/Django/DRF/Ninja`, `Go net/http/chi/gin/echo`, `Rust axum/actix-web/rocket/Tauri`) execute mechanisms over database rows behind explicit transactional service/SQL doors without embedding shadow policy literals or host-clock drift.
|
|
58
|
+
3. **Frontend displays truth** — UI surfaces (`Next.js`, `React/Vite/TanStack Router/Remix`, `Vue 3/Nuxt`, `Svelte 5/SvelteKit`, `Rust WASM Leptos/Dioxus/Yew` + Tauri `invoke()`) bind to typed contracts, centralized clock utilities, and WCAG-accessible markup without inventing client-side policy maps or unbacked HTTP/IPC calls.
|
|
59
|
+
|
|
60
|
+
Wythe supports **18 full-stack profiles** out of the box via `--profile auto`, pairs Python's native `ast` with an optional **Tree-sitter CST grammar engine** (`wythe[treesitter]` for TypeScript/TSX, JavaScript, Rust, and Go), traces background worker & scheduled beat queues (`Celery` / `Beat`, `BullMQ`, `Asynq`), enforces **WCAG accessibility semantics** (`DISP-005`), renders both **Simple Solids** and **120 fps Breathing Atomic Wythe Polyhedra** (with adaptive `System | Dark | Light` polarity and custom user palettes), and **self-dogfoods** its own 3-tier codebase (`wythe gate --strict wythe` passes with zero blocking and zero warning findings).
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Instant Usage (`uvx` / `uv`)
|
|
65
|
+
|
|
66
|
+
Run Wythe directly against any repository without polluting project dependencies (add `[treesitter]` to enable prebuilt Tree-sitter CST wheels for TS/TSX, JS, Rust, and Go):
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Instant CLI gate & lineage scan from PyPI (once published) or directly from GitHub
|
|
70
|
+
uvx "wythe[treesitter]" gate --strict /path/to/repo
|
|
71
|
+
uvx --from "git+https://github.com/Euda1mon1a/wythe[treesitter]" wythe scan /path/to/repo
|
|
72
|
+
uvx --from "git+https://github.com/Euda1mon1a/wythe[treesitter]" wythe gate --strict /path/to/repo
|
|
73
|
+
|
|
74
|
+
# Launch single-port API + interactive 3D WebGL UI on http://127.0.0.1:8780 (no Node.js runtime needed)
|
|
75
|
+
uvx --from "git+https://github.com/Euda1mon1a/wythe[treesitter]" wythe serve /path/to/repo
|
|
76
|
+
|
|
77
|
+
# Or install persistently as an isolated CLI tool
|
|
78
|
+
uv tool install "git+https://github.com/Euda1mon1a/wythe[treesitter]"
|
|
79
|
+
wythe serve .
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### GitHub Actions Integration
|
|
83
|
+
|
|
84
|
+
```yaml
|
|
85
|
+
- uses: actions/checkout@v4
|
|
86
|
+
with:
|
|
87
|
+
fetch-depth: 0
|
|
88
|
+
- uses: Euda1mon1a/wythe@main
|
|
89
|
+
with:
|
|
90
|
+
mode: diff
|
|
91
|
+
base-ref: origin/main
|
|
92
|
+
sarif-file: wythe.sarif.json
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Persisted Rule Catalog (`atlas_rule_policies`)
|
|
98
|
+
|
|
99
|
+
Rule severity, enablement, gate-blocking behavior, and effective-date windows live in database truth (`atlas_rule_policies`) and round-trip cleanly to Git via `.wythe/governance.json`:
|
|
100
|
+
|
|
101
|
+
| Code | Tier | Default | Description |
|
|
102
|
+
| :--- | :--- | :--- | :--- |
|
|
103
|
+
| `TRUTH-001` | `truth` | Warning | Persisted input table has zero verified backend readers |
|
|
104
|
+
| `TRUTH-002` | `truth` | Blocking | ORM model, `schema.sql`/migrations, or live DB catalog table drift |
|
|
105
|
+
| `TRUTH-003` | `truth` | Blocking | Table or Redis store mutated directly inside HTTP route without service/DB door |
|
|
106
|
+
| `TRUTH-004` | `truth` | Warning | Unbounded Redis `SET`/`HSET` write without TTL risks volatile shadow truth |
|
|
107
|
+
| `MANIP-001` | `manipulate` | Blocking | Mutable domain policy/threshold constant hardcoded in code (`atlas_policy_vocabularies`) |
|
|
108
|
+
| `MANIP-002` | `manipulate` | Blocking | Direct host `date.today()` or naive `datetime.utcnow()` call |
|
|
109
|
+
| `MANIP-003` | `manipulate` | Warning | Service/solver module has zero verified inbound route, CLI, or worker callers |
|
|
110
|
+
| `DISP-001` | `display` | Blocking | Display tier defines domain policy threshold shadows in frontend code |
|
|
111
|
+
| `DISP-002` | `display` | Blocking | Raw `new Date()` or UTC `.toISOString().slice(0, 10)` date slicing |
|
|
112
|
+
| `DISP-003` | `display` | Warning | Frontend API client omits `api-generated` OpenAPI/schema types |
|
|
113
|
+
| `DISP-004` | `display` | Blocking | Frontend HTTP/IPC call targets a path absent from backend router graph |
|
|
114
|
+
| `DISP-005` | `display` | Warning | Display markup violates core WCAG semantics (missing `alt`, unlabeled form input, nameless icon button, non-keyboard `<div onClick>`) |
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## CLI Commands
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# Scan any supported full-stack repository (--profile auto)
|
|
122
|
+
wythe scan /path/to/repo --json report.json --sarif report.sarif.json
|
|
123
|
+
|
|
124
|
+
# Optional metadata-only live DB & Redis keyspace probes (zero row or value reads)
|
|
125
|
+
wythe scan /path/to/repo \
|
|
126
|
+
--dsn "postgresql://readonly@127.0.0.1:5432/appdb" \
|
|
127
|
+
--redis-dsn "redis://127.0.0.1:6379/0"
|
|
128
|
+
|
|
129
|
+
# PR diff gate & directional 3-tier blast-radius slice
|
|
130
|
+
wythe diff /path/to/repo --base origin/main --sarif diff.sarif.json
|
|
131
|
+
|
|
132
|
+
# Git-portable governance bundle (.wythe/governance.json) for CI runners
|
|
133
|
+
wythe export-config /path/to/repo
|
|
134
|
+
wythe gate --sync-config /path/to/repo
|
|
135
|
+
|
|
136
|
+
# Single-port API + 3D WebGL UI (auto-registers target directory on startup)
|
|
137
|
+
wythe serve /path/to/repo --host 127.0.0.1 --port 8780
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## First-Class LLM & Human ↔ Vision/MCP Bridge (On-Device AI, Streamable HTTP MCP, OpenAPI & WebMCP)
|
|
143
|
+
|
|
144
|
+
Full JSON scans on large repositories span megabytes; Wythe exposes five zero-copy-paste Human ↔ LLM surfaces spanning bare-minimum on-device models through remote Tailnet agents:
|
|
145
|
+
|
|
146
|
+
1. **Zero-Setup On-Device AI (`Ask AI [A]` / `⌘J`, `/api/v1/ask`)** — Zero-cloud, zero-API-key cascade with real-time Three.js camera-frustum & screen-color telemetry, `1024px` WebP vision frames, and rolling multi-turn sessions across:
|
|
147
|
+
- **Apple Intelligence (`apple`)**: macOS 26+/27 native `FoundationModels` helper (`arm64-apple-macos26.0`, serialized with `modelmanagerd` backoff).
|
|
148
|
+
- **Chrome Built-In (`chrome`)**: Client-side browser `LanguageModel` (Gemini Nano via Prompt API, including 1-click download and multimodal WebGL `<canvas>` vision when enabled).
|
|
149
|
+
- **Local OpenAI `/v1` (`local`)**: Auto-probes browser-host & server loopback `/v1/models` & `/v1/chat/completions` (`:8080`/`:8084`/`:8083` MLX, `:11434` Ollama, `:1234` LM Studio) without triggering weight reloads.
|
|
150
|
+
2. **Streamable HTTP MCP (`POST /api/v1/mcp` & `GET /api/v1/mcp`) + Stdio MCP (`wythe mcp`)** — Connect local or remote Tailnet coding agents (`http://<host>:8780/api/v1/mcp`) with zero local install.
|
|
151
|
+
3. **OpenAPI 3.1 (`/openapi.json`) + Remote CLI (`wythe focus --remote <url>`)** — Universal HTTP tool/Actions compatibility plus cross-host CLI X-Ray inspection.
|
|
152
|
+
4. **In-Tab WebMCP (`navigator.modelContext` & `window.wythe`)** — Exposes `whatIsThis()`, `getLineageCone(query)`, `selectNode(nodeId)`, and `ask(question)` directly to browser agents and extensions.
|
|
153
|
+
5. **Token-Budgeted `llms.txt` & Lineage Cone Slicer** — Deterministic ~2,000-token repo maps and ~300-token 3-tier cones.
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
# 1. Token-budgeted llms.txt pointer map (~2,000 tokens regardless of repo size)
|
|
157
|
+
wythe llms /path/to/repo --max-tokens 2200 --out llms-wythe.txt
|
|
158
|
+
|
|
159
|
+
# 2. Surgical ~300-token 3-tier Lineage Cone around any table, file, route, or queue
|
|
160
|
+
wythe context /path/to/repo audit_exports
|
|
161
|
+
wythe context /path/to/repo /api/v1/scans --json
|
|
162
|
+
|
|
163
|
+
# 3. Zero-copy-paste Human <-> LLM Bridge (local state or remote private/Tailnet server)
|
|
164
|
+
wythe focus
|
|
165
|
+
wythe focus --remote http://100.64.0.10:8780
|
|
166
|
+
|
|
167
|
+
# 4. Zero-dependency read-only stdio MCP server (or connect directly to http://127.0.0.1:8780/api/v1/mcp)
|
|
168
|
+
wythe mcp
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
MCP client configuration (Stdio or Streamable HTTP — exposes `wythe_what_is_this`, `wythe_list_targets`, `wythe_overview`, `wythe_lineage_cone`, `wythe_blast_radius`, `wythe_findings`):
|
|
172
|
+
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"mcpServers": {
|
|
176
|
+
"wythe-http": {
|
|
177
|
+
"url": "http://127.0.0.1:8780/api/v1/mcp"
|
|
178
|
+
},
|
|
179
|
+
"wythe-stdio": {
|
|
180
|
+
"command": "uvx",
|
|
181
|
+
"args": ["--from", "git+https://github.com/Euda1mon1a/wythe[treesitter]", "wythe", "mcp"]
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Supported Multi-Language & Multi-DB Adapters
|
|
190
|
+
|
|
191
|
+
| Stratum | Adapter Key | Stack Coverage |
|
|
192
|
+
| :--- | :--- | :--- |
|
|
193
|
+
| **Tier 1 (Truth)** | `postgres-sqlalchemy` | SQLAlchemy 2.0 + Alembic + `schema.sql` + PG/MySQL/SQLite live catalog |
|
|
194
|
+
| **Tier 1 (Truth)** | `prisma-schema` | Prisma `schema.prisma` (`@@map`) + `prisma/migrations/*.sql` |
|
|
195
|
+
| **Tier 1 (Truth)** | `drizzle-orm` | Drizzle `pgTable`/`mysqlTable`/`sqliteTable` + Drizzle SQL migrations |
|
|
196
|
+
| **Tier 1 (Truth)** | `django-orm` | Django `models.Model` (`Meta.db_table`) + `migrations/*.py` |
|
|
197
|
+
| **Tier 1 (Truth)** | `sql-ddl` | Multi-dialect SQL DDL + `golang-migrate`/`goose`/`Flyway` + `sqlc` |
|
|
198
|
+
| **Tier 1 (Truth)** | `rust-sql` | Rust `sqlx` (`migrations/*.sql`, `query_file!`), `Diesel` (`table!`), `SeaORM` entities |
|
|
199
|
+
| **Tier 1 (Ephemeral)** | `redis-ephemeral` | Cross-language Redis keyspaces + `Celery`/`Beat`, `BullMQ`, & `Asynq` named queues |
|
|
200
|
+
| **Tier 2 (Manipulate)** | `python-fastapi` | FastAPI routers, Python services/workers/CLI, SQLAlchemy/SQL AST |
|
|
201
|
+
| **Tier 2 (Manipulate)** | `typescript-node` | Next.js `route.ts`, SvelteKit `+page.server.ts`/`+server.ts`, Express, Hono, Fastify, NestJS |
|
|
202
|
+
| **Tier 2 (Manipulate)** | `python-django` | Django `urls.py` (`path`/`re_path`), DRF `ViewSet`/routers, Django Ninja + `Model.objects` |
|
|
203
|
+
| **Tier 2 (Manipulate)** | `go-http` | Go 1.22+ `net/http`, `chi`, `gin`, `echo` (nested router groups) + `sqlc`/GORM/SQL |
|
|
204
|
+
| **Tier 2 (Manipulate)** | `rust-http` | Rust Cargo workspaces, `axum`, `actix-web`, `rocket`, Tauri `#[tauri::command]` + `sqlx`/Diesel/SeaORM |
|
|
205
|
+
| **Tier 3 (Display)** | `typescript-nextjs` | Next.js App & Pages Router + custom fetch/RTK Query + Tauri `invoke()` + WCAG `DISP-005` |
|
|
206
|
+
| **Tier 3 (Display)** | `typescript-react` | React SPA, Vite, React Router, TanStack Router, Remix + barrel exports + WCAG `DISP-005` |
|
|
207
|
+
| **Tier 3 (Display)** | `vue-sfc` | Vue 3 & Nuxt 3 `.vue` SFCs + composables (`$fetch`, `useFetch`) + WCAG `DISP-005` |
|
|
208
|
+
| **Tier 3 (Display)** | `sveltekit` | Svelte 5 & SvelteKit `+page.svelte`, `+page.ts`, `$lib` + WCAG `DISP-005` |
|
|
209
|
+
| **Tier 3 (Display)** | `rust-wasm` | Rust WASM `Leptos`, `Dioxus`, `Yew` (`gloo-net` / `reqwest` / Tauri WASM) + WCAG `DISP-005` |
|
|
210
|
+
|
|
211
|
+
### Architecture & Ecosystem Docs
|
|
212
|
+
- [`docs/architecture/ecosystem-and-positioning.md`](docs/architecture/ecosystem-and-positioning.md) — Ecosystem comparison (`tach`, `import-linter`, `dependency-cruiser`, `sqllineage`, `OpenLineage`, `CodeCharta`, `Semgrep`) and pairing `tach` + Wythe in CI.
|
|
213
|
+
- [`docs/architecture/multi-language-extensibility.md`](docs/architecture/multi-language-extensibility.md) — Language-neutral intermediate contract, adapter matrix, read-only DB/Redis probes, and `--profile auto` detection.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Local Development
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
make types # Export OpenAPI 3.1 -> generate TypeScript schema types -> run tsc --noEmit
|
|
221
|
+
make build-ui # Build static Next.js export into backend/app/ui_dist
|
|
222
|
+
make lint # Run ruff check
|
|
223
|
+
make test # Run serial pytest suite
|
|
224
|
+
make gate # Run strict self-dogfood 3-tier + WCAG gate
|
|
225
|
+
make package # Build standalone wheel & sdist containing bundled 3D WebGL UI
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## License
|
|
229
|
+
|
|
230
|
+
MIT — see [`LICENSE`](LICENSE).
|
wythe-0.1.0/README.md
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<picture>
|
|
3
|
+
<source media="(prefers-color-scheme: light)" srcset="docs/icon/icon-light.svg">
|
|
4
|
+
<img src="docs/icon/icon-dark.svg" width="96" height="96" alt="Wythe icon — three architectural wythe bars mortised by a Why (?) triskelion and Therefore (∴) core">
|
|
5
|
+
</picture>
|
|
6
|
+
</p>
|
|
7
|
+
|
|
8
|
+
# Wythe
|
|
9
|
+
|
|
10
|
+
**Why (`?`) → Therefore (`∴`)** — Generalizable 3-tier architecture verifier, cross-boundary lineage solver, interactive 3D WebGL latticework visualizer, and fail-closed CI gate built around three masonry wythe invariants:
|
|
11
|
+
|
|
12
|
+
1. **Database holds truth** — Relational schemas (`PostgreSQL`, `MySQL`, `SQLite` via `SQLAlchemy 2.0` + `Alembic`, `Prisma`, `Drizzle ORM`, `Django ORM`, Rust `sqlx`/`Diesel`/`SeaORM`, or raw SQL DDL/`sqlc` + optional read-only catalog probes) own domain policy, thresholds, temporal validity, and write-lane guards, while `Redis` keyspaces and worker queues are modeled explicitly as ephemeral stores/lanes rather than durable shadow truth.
|
|
13
|
+
2. **Backend manipulates truth** — Services, background workers, solvers, and routers (`Python/FastAPI`, `TypeScript/Node/Express/Hono/Fastify/NestJS/Next.js/SvelteKit`, `Python/Django/DRF/Ninja`, `Go net/http/chi/gin/echo`, `Rust axum/actix-web/rocket/Tauri`) execute mechanisms over database rows behind explicit transactional service/SQL doors without embedding shadow policy literals or host-clock drift.
|
|
14
|
+
3. **Frontend displays truth** — UI surfaces (`Next.js`, `React/Vite/TanStack Router/Remix`, `Vue 3/Nuxt`, `Svelte 5/SvelteKit`, `Rust WASM Leptos/Dioxus/Yew` + Tauri `invoke()`) bind to typed contracts, centralized clock utilities, and WCAG-accessible markup without inventing client-side policy maps or unbacked HTTP/IPC calls.
|
|
15
|
+
|
|
16
|
+
Wythe supports **18 full-stack profiles** out of the box via `--profile auto`, pairs Python's native `ast` with an optional **Tree-sitter CST grammar engine** (`wythe[treesitter]` for TypeScript/TSX, JavaScript, Rust, and Go), traces background worker & scheduled beat queues (`Celery` / `Beat`, `BullMQ`, `Asynq`), enforces **WCAG accessibility semantics** (`DISP-005`), renders both **Simple Solids** and **120 fps Breathing Atomic Wythe Polyhedra** (with adaptive `System | Dark | Light` polarity and custom user palettes), and **self-dogfoods** its own 3-tier codebase (`wythe gate --strict wythe` passes with zero blocking and zero warning findings).
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Instant Usage (`uvx` / `uv`)
|
|
21
|
+
|
|
22
|
+
Run Wythe directly against any repository without polluting project dependencies (add `[treesitter]` to enable prebuilt Tree-sitter CST wheels for TS/TSX, JS, Rust, and Go):
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Instant CLI gate & lineage scan from PyPI (once published) or directly from GitHub
|
|
26
|
+
uvx "wythe[treesitter]" gate --strict /path/to/repo
|
|
27
|
+
uvx --from "git+https://github.com/Euda1mon1a/wythe[treesitter]" wythe scan /path/to/repo
|
|
28
|
+
uvx --from "git+https://github.com/Euda1mon1a/wythe[treesitter]" wythe gate --strict /path/to/repo
|
|
29
|
+
|
|
30
|
+
# Launch single-port API + interactive 3D WebGL UI on http://127.0.0.1:8780 (no Node.js runtime needed)
|
|
31
|
+
uvx --from "git+https://github.com/Euda1mon1a/wythe[treesitter]" wythe serve /path/to/repo
|
|
32
|
+
|
|
33
|
+
# Or install persistently as an isolated CLI tool
|
|
34
|
+
uv tool install "git+https://github.com/Euda1mon1a/wythe[treesitter]"
|
|
35
|
+
wythe serve .
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### GitHub Actions Integration
|
|
39
|
+
|
|
40
|
+
```yaml
|
|
41
|
+
- uses: actions/checkout@v4
|
|
42
|
+
with:
|
|
43
|
+
fetch-depth: 0
|
|
44
|
+
- uses: Euda1mon1a/wythe@main
|
|
45
|
+
with:
|
|
46
|
+
mode: diff
|
|
47
|
+
base-ref: origin/main
|
|
48
|
+
sarif-file: wythe.sarif.json
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Persisted Rule Catalog (`atlas_rule_policies`)
|
|
54
|
+
|
|
55
|
+
Rule severity, enablement, gate-blocking behavior, and effective-date windows live in database truth (`atlas_rule_policies`) and round-trip cleanly to Git via `.wythe/governance.json`:
|
|
56
|
+
|
|
57
|
+
| Code | Tier | Default | Description |
|
|
58
|
+
| :--- | :--- | :--- | :--- |
|
|
59
|
+
| `TRUTH-001` | `truth` | Warning | Persisted input table has zero verified backend readers |
|
|
60
|
+
| `TRUTH-002` | `truth` | Blocking | ORM model, `schema.sql`/migrations, or live DB catalog table drift |
|
|
61
|
+
| `TRUTH-003` | `truth` | Blocking | Table or Redis store mutated directly inside HTTP route without service/DB door |
|
|
62
|
+
| `TRUTH-004` | `truth` | Warning | Unbounded Redis `SET`/`HSET` write without TTL risks volatile shadow truth |
|
|
63
|
+
| `MANIP-001` | `manipulate` | Blocking | Mutable domain policy/threshold constant hardcoded in code (`atlas_policy_vocabularies`) |
|
|
64
|
+
| `MANIP-002` | `manipulate` | Blocking | Direct host `date.today()` or naive `datetime.utcnow()` call |
|
|
65
|
+
| `MANIP-003` | `manipulate` | Warning | Service/solver module has zero verified inbound route, CLI, or worker callers |
|
|
66
|
+
| `DISP-001` | `display` | Blocking | Display tier defines domain policy threshold shadows in frontend code |
|
|
67
|
+
| `DISP-002` | `display` | Blocking | Raw `new Date()` or UTC `.toISOString().slice(0, 10)` date slicing |
|
|
68
|
+
| `DISP-003` | `display` | Warning | Frontend API client omits `api-generated` OpenAPI/schema types |
|
|
69
|
+
| `DISP-004` | `display` | Blocking | Frontend HTTP/IPC call targets a path absent from backend router graph |
|
|
70
|
+
| `DISP-005` | `display` | Warning | Display markup violates core WCAG semantics (missing `alt`, unlabeled form input, nameless icon button, non-keyboard `<div onClick>`) |
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## CLI Commands
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
# Scan any supported full-stack repository (--profile auto)
|
|
78
|
+
wythe scan /path/to/repo --json report.json --sarif report.sarif.json
|
|
79
|
+
|
|
80
|
+
# Optional metadata-only live DB & Redis keyspace probes (zero row or value reads)
|
|
81
|
+
wythe scan /path/to/repo \
|
|
82
|
+
--dsn "postgresql://readonly@127.0.0.1:5432/appdb" \
|
|
83
|
+
--redis-dsn "redis://127.0.0.1:6379/0"
|
|
84
|
+
|
|
85
|
+
# PR diff gate & directional 3-tier blast-radius slice
|
|
86
|
+
wythe diff /path/to/repo --base origin/main --sarif diff.sarif.json
|
|
87
|
+
|
|
88
|
+
# Git-portable governance bundle (.wythe/governance.json) for CI runners
|
|
89
|
+
wythe export-config /path/to/repo
|
|
90
|
+
wythe gate --sync-config /path/to/repo
|
|
91
|
+
|
|
92
|
+
# Single-port API + 3D WebGL UI (auto-registers target directory on startup)
|
|
93
|
+
wythe serve /path/to/repo --host 127.0.0.1 --port 8780
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## First-Class LLM & Human ↔ Vision/MCP Bridge (On-Device AI, Streamable HTTP MCP, OpenAPI & WebMCP)
|
|
99
|
+
|
|
100
|
+
Full JSON scans on large repositories span megabytes; Wythe exposes five zero-copy-paste Human ↔ LLM surfaces spanning bare-minimum on-device models through remote Tailnet agents:
|
|
101
|
+
|
|
102
|
+
1. **Zero-Setup On-Device AI (`Ask AI [A]` / `⌘J`, `/api/v1/ask`)** — Zero-cloud, zero-API-key cascade with real-time Three.js camera-frustum & screen-color telemetry, `1024px` WebP vision frames, and rolling multi-turn sessions across:
|
|
103
|
+
- **Apple Intelligence (`apple`)**: macOS 26+/27 native `FoundationModels` helper (`arm64-apple-macos26.0`, serialized with `modelmanagerd` backoff).
|
|
104
|
+
- **Chrome Built-In (`chrome`)**: Client-side browser `LanguageModel` (Gemini Nano via Prompt API, including 1-click download and multimodal WebGL `<canvas>` vision when enabled).
|
|
105
|
+
- **Local OpenAI `/v1` (`local`)**: Auto-probes browser-host & server loopback `/v1/models` & `/v1/chat/completions` (`:8080`/`:8084`/`:8083` MLX, `:11434` Ollama, `:1234` LM Studio) without triggering weight reloads.
|
|
106
|
+
2. **Streamable HTTP MCP (`POST /api/v1/mcp` & `GET /api/v1/mcp`) + Stdio MCP (`wythe mcp`)** — Connect local or remote Tailnet coding agents (`http://<host>:8780/api/v1/mcp`) with zero local install.
|
|
107
|
+
3. **OpenAPI 3.1 (`/openapi.json`) + Remote CLI (`wythe focus --remote <url>`)** — Universal HTTP tool/Actions compatibility plus cross-host CLI X-Ray inspection.
|
|
108
|
+
4. **In-Tab WebMCP (`navigator.modelContext` & `window.wythe`)** — Exposes `whatIsThis()`, `getLineageCone(query)`, `selectNode(nodeId)`, and `ask(question)` directly to browser agents and extensions.
|
|
109
|
+
5. **Token-Budgeted `llms.txt` & Lineage Cone Slicer** — Deterministic ~2,000-token repo maps and ~300-token 3-tier cones.
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# 1. Token-budgeted llms.txt pointer map (~2,000 tokens regardless of repo size)
|
|
113
|
+
wythe llms /path/to/repo --max-tokens 2200 --out llms-wythe.txt
|
|
114
|
+
|
|
115
|
+
# 2. Surgical ~300-token 3-tier Lineage Cone around any table, file, route, or queue
|
|
116
|
+
wythe context /path/to/repo audit_exports
|
|
117
|
+
wythe context /path/to/repo /api/v1/scans --json
|
|
118
|
+
|
|
119
|
+
# 3. Zero-copy-paste Human <-> LLM Bridge (local state or remote private/Tailnet server)
|
|
120
|
+
wythe focus
|
|
121
|
+
wythe focus --remote http://100.64.0.10:8780
|
|
122
|
+
|
|
123
|
+
# 4. Zero-dependency read-only stdio MCP server (or connect directly to http://127.0.0.1:8780/api/v1/mcp)
|
|
124
|
+
wythe mcp
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
MCP client configuration (Stdio or Streamable HTTP — exposes `wythe_what_is_this`, `wythe_list_targets`, `wythe_overview`, `wythe_lineage_cone`, `wythe_blast_radius`, `wythe_findings`):
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"mcpServers": {
|
|
132
|
+
"wythe-http": {
|
|
133
|
+
"url": "http://127.0.0.1:8780/api/v1/mcp"
|
|
134
|
+
},
|
|
135
|
+
"wythe-stdio": {
|
|
136
|
+
"command": "uvx",
|
|
137
|
+
"args": ["--from", "git+https://github.com/Euda1mon1a/wythe[treesitter]", "wythe", "mcp"]
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Supported Multi-Language & Multi-DB Adapters
|
|
146
|
+
|
|
147
|
+
| Stratum | Adapter Key | Stack Coverage |
|
|
148
|
+
| :--- | :--- | :--- |
|
|
149
|
+
| **Tier 1 (Truth)** | `postgres-sqlalchemy` | SQLAlchemy 2.0 + Alembic + `schema.sql` + PG/MySQL/SQLite live catalog |
|
|
150
|
+
| **Tier 1 (Truth)** | `prisma-schema` | Prisma `schema.prisma` (`@@map`) + `prisma/migrations/*.sql` |
|
|
151
|
+
| **Tier 1 (Truth)** | `drizzle-orm` | Drizzle `pgTable`/`mysqlTable`/`sqliteTable` + Drizzle SQL migrations |
|
|
152
|
+
| **Tier 1 (Truth)** | `django-orm` | Django `models.Model` (`Meta.db_table`) + `migrations/*.py` |
|
|
153
|
+
| **Tier 1 (Truth)** | `sql-ddl` | Multi-dialect SQL DDL + `golang-migrate`/`goose`/`Flyway` + `sqlc` |
|
|
154
|
+
| **Tier 1 (Truth)** | `rust-sql` | Rust `sqlx` (`migrations/*.sql`, `query_file!`), `Diesel` (`table!`), `SeaORM` entities |
|
|
155
|
+
| **Tier 1 (Ephemeral)** | `redis-ephemeral` | Cross-language Redis keyspaces + `Celery`/`Beat`, `BullMQ`, & `Asynq` named queues |
|
|
156
|
+
| **Tier 2 (Manipulate)** | `python-fastapi` | FastAPI routers, Python services/workers/CLI, SQLAlchemy/SQL AST |
|
|
157
|
+
| **Tier 2 (Manipulate)** | `typescript-node` | Next.js `route.ts`, SvelteKit `+page.server.ts`/`+server.ts`, Express, Hono, Fastify, NestJS |
|
|
158
|
+
| **Tier 2 (Manipulate)** | `python-django` | Django `urls.py` (`path`/`re_path`), DRF `ViewSet`/routers, Django Ninja + `Model.objects` |
|
|
159
|
+
| **Tier 2 (Manipulate)** | `go-http` | Go 1.22+ `net/http`, `chi`, `gin`, `echo` (nested router groups) + `sqlc`/GORM/SQL |
|
|
160
|
+
| **Tier 2 (Manipulate)** | `rust-http` | Rust Cargo workspaces, `axum`, `actix-web`, `rocket`, Tauri `#[tauri::command]` + `sqlx`/Diesel/SeaORM |
|
|
161
|
+
| **Tier 3 (Display)** | `typescript-nextjs` | Next.js App & Pages Router + custom fetch/RTK Query + Tauri `invoke()` + WCAG `DISP-005` |
|
|
162
|
+
| **Tier 3 (Display)** | `typescript-react` | React SPA, Vite, React Router, TanStack Router, Remix + barrel exports + WCAG `DISP-005` |
|
|
163
|
+
| **Tier 3 (Display)** | `vue-sfc` | Vue 3 & Nuxt 3 `.vue` SFCs + composables (`$fetch`, `useFetch`) + WCAG `DISP-005` |
|
|
164
|
+
| **Tier 3 (Display)** | `sveltekit` | Svelte 5 & SvelteKit `+page.svelte`, `+page.ts`, `$lib` + WCAG `DISP-005` |
|
|
165
|
+
| **Tier 3 (Display)** | `rust-wasm` | Rust WASM `Leptos`, `Dioxus`, `Yew` (`gloo-net` / `reqwest` / Tauri WASM) + WCAG `DISP-005` |
|
|
166
|
+
|
|
167
|
+
### Architecture & Ecosystem Docs
|
|
168
|
+
- [`docs/architecture/ecosystem-and-positioning.md`](docs/architecture/ecosystem-and-positioning.md) — Ecosystem comparison (`tach`, `import-linter`, `dependency-cruiser`, `sqllineage`, `OpenLineage`, `CodeCharta`, `Semgrep`) and pairing `tach` + Wythe in CI.
|
|
169
|
+
- [`docs/architecture/multi-language-extensibility.md`](docs/architecture/multi-language-extensibility.md) — Language-neutral intermediate contract, adapter matrix, read-only DB/Redis probes, and `--profile auto` detection.
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Local Development
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
make types # Export OpenAPI 3.1 -> generate TypeScript schema types -> run tsc --noEmit
|
|
177
|
+
make build-ui # Build static Next.js export into backend/app/ui_dist
|
|
178
|
+
make lint # Run ruff check
|
|
179
|
+
make test # Run serial pytest suite
|
|
180
|
+
make gate # Run strict self-dogfood 3-tier + WCAG gate
|
|
181
|
+
make package # Build standalone wheel & sdist containing bundled 3D WebGL UI
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## License
|
|
185
|
+
|
|
186
|
+
MIT — see [`LICENSE`](LICENSE).
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Default adapter registry factory with multi-language 3-tier adapters."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from app.adapters.base import AdapterRegistry
|
|
6
|
+
from app.adapters.django_truth import DjangoOrmAdapter
|
|
7
|
+
from app.adapters.drizzle_truth import DrizzleOrmAdapter
|
|
8
|
+
from app.adapters.go_http import GoHttpAdapter
|
|
9
|
+
from app.adapters.postgres_sqlalchemy import PostgresSqlAlchemyAdapter
|
|
10
|
+
from app.adapters.prisma_truth import PrismaSchemaAdapter
|
|
11
|
+
from app.adapters.python_django import PythonDjangoAdapter
|
|
12
|
+
from app.adapters.python_fastapi import PythonFastApiAdapter
|
|
13
|
+
from app.adapters.rust_http import RustHttpAdapter
|
|
14
|
+
from app.adapters.rust_sql_truth import RustSqlTruthAdapter
|
|
15
|
+
from app.adapters.rust_wasm_display import RustWasmDisplayAdapter
|
|
16
|
+
from app.adapters.sql_ddl_truth import SqlDdlTruthAdapter
|
|
17
|
+
from app.adapters.sveltekit_display import SvelteKitDisplayAdapter
|
|
18
|
+
from app.adapters.typescript_nextjs import TypeScriptNextJsAdapter
|
|
19
|
+
from app.adapters.typescript_node import TypeScriptNodeAdapter
|
|
20
|
+
from app.adapters.typescript_react import TypeScriptReactSpaAdapter
|
|
21
|
+
from app.adapters.vue_sfc import VueSfcDisplayAdapter
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def build_default_registry() -> AdapterRegistry:
|
|
25
|
+
"""Instantiate an AdapterRegistry populated with all implemented 3-tier adapters."""
|
|
26
|
+
registry = AdapterRegistry()
|
|
27
|
+
|
|
28
|
+
# Tier 1: Database & Schema/ORM Truth
|
|
29
|
+
registry.register_truth(PostgresSqlAlchemyAdapter())
|
|
30
|
+
registry.register_truth(PrismaSchemaAdapter())
|
|
31
|
+
registry.register_truth(DrizzleOrmAdapter())
|
|
32
|
+
registry.register_truth(DjangoOrmAdapter())
|
|
33
|
+
registry.register_truth(SqlDdlTruthAdapter())
|
|
34
|
+
registry.register_truth(RustSqlTruthAdapter())
|
|
35
|
+
|
|
36
|
+
# Tier 2: Backend Manipulation
|
|
37
|
+
registry.register_manipulator(PythonFastApiAdapter())
|
|
38
|
+
registry.register_manipulator(TypeScriptNodeAdapter())
|
|
39
|
+
registry.register_manipulator(PythonDjangoAdapter())
|
|
40
|
+
registry.register_manipulator(GoHttpAdapter())
|
|
41
|
+
registry.register_manipulator(RustHttpAdapter())
|
|
42
|
+
|
|
43
|
+
# Tier 3: Frontend Display
|
|
44
|
+
registry.register_display(TypeScriptNextJsAdapter())
|
|
45
|
+
registry.register_display(TypeScriptReactSpaAdapter())
|
|
46
|
+
registry.register_display(VueSfcDisplayAdapter())
|
|
47
|
+
registry.register_display(SvelteKitDisplayAdapter())
|
|
48
|
+
registry.register_display(RustWasmDisplayAdapter())
|
|
49
|
+
|
|
50
|
+
return registry
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Tier 3 Accessibility (`DISP-005`) static markup inspector for JSX/TSX, Vue, Svelte, and Rust WASM."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
|
|
7
|
+
from app.adapters.base import RawCandidateFinding
|
|
8
|
+
|
|
9
|
+
# Match opening tags (<img ...>, <div ...>, <span ...>, <input ...>) without stopping early on JSX `=>` arrows
|
|
10
|
+
_IMG_TAG_RE = re.compile(r"<img\b(?P<attrs>(?:=>|[^>\"']|\"[^\"]*\"|'[^']*')*)>", re.IGNORECASE)
|
|
11
|
+
_NON_INTERACTIVE_CLICK_RE = re.compile(
|
|
12
|
+
r"<(?P<tag>div|span)\b(?P<attrs>(?:=>|[^>\"']|\"[^\"]*\"|'[^']*'){0,500}?"
|
|
13
|
+
r"\b(?:onClick|on:click|@click|onclick)\s*=[\s\S]{0,240}?)>",
|
|
14
|
+
)
|
|
15
|
+
_INPUT_TAG_RE = re.compile(
|
|
16
|
+
r"<(?P<tag>input|select|textarea)\b(?P<attrs>(?:=>|[^>\"']|\"[^\"]*\"|'[^']*')*)>",
|
|
17
|
+
)
|
|
18
|
+
_EMPTY_BUTTON_RE = re.compile(
|
|
19
|
+
r"<button\b(?P<attrs>(?:=>|[^>\"']|\"[^\"]*\"|'[^']*')*)>\s*"
|
|
20
|
+
r"(?:<[A-Z][A-Za-z0-9_.]*\b[^>]*/\s*>\s*|<(?:svg|i)\b[\s\S]{0,400}?/\s*>\s*|<(?:svg|i)\b[\s\S]{0,400}?</(?:svg|i)>\s*)+"
|
|
21
|
+
r"</button>",
|
|
22
|
+
)
|
|
23
|
+
_LABEL_FOR_RE = re.compile(r"""<label\b[^>]*?\b(?:htmlFor|for)\s*=\s*(?:['"](?P<id>[^'"]+)['"]|\{(?P<expr>[^}]+)\})""")
|
|
24
|
+
_WRAPPING_LABEL_RE = re.compile(r"<label\b(?:(?!</label>)[\s\S]){0,1500}?</label>")
|
|
25
|
+
_ID_ATTR_RE = re.compile(r"""\bid\s*=\s*(?:['"](?P<id>[^'"]+)['"]|\{(?P<expr>[^}]+)\})""")
|
|
26
|
+
_HIDDEN_INPUT_RE = re.compile(r"""\btype\s*=\s*['"](?:hidden|submit|reset|button)['"]""", re.IGNORECASE)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def scan_a11y_markup_findings(
|
|
30
|
+
raw_source: str,
|
|
31
|
+
node_id: str,
|
|
32
|
+
rel_repo: str,
|
|
33
|
+
) -> list[RawCandidateFinding]:
|
|
34
|
+
"""Inspect raw UI template/JSX/RSX markup for deterministic WCAG (`DISP-005`) violations."""
|
|
35
|
+
findings: list[RawCandidateFinding] = []
|
|
36
|
+
seen_lines: set[tuple[int, str]] = set()
|
|
37
|
+
|
|
38
|
+
def emit(lineno: int, kind: str, title: str, detail: str, remediation: str) -> None:
|
|
39
|
+
if (lineno, kind) in seen_lines or len(findings) >= 15:
|
|
40
|
+
return
|
|
41
|
+
seen_lines.add((lineno, kind))
|
|
42
|
+
findings.append(
|
|
43
|
+
RawCandidateFinding(
|
|
44
|
+
rule_code="DISP-005",
|
|
45
|
+
node_id=node_id,
|
|
46
|
+
source_file=rel_repo,
|
|
47
|
+
line=lineno,
|
|
48
|
+
title=title,
|
|
49
|
+
detail=detail,
|
|
50
|
+
remediation=remediation,
|
|
51
|
+
)
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# 1. <img> without alt attribute (unless decorative aria-hidden="true" or role="presentation"/"none")
|
|
55
|
+
for m in _IMG_TAG_RE.finditer(raw_source):
|
|
56
|
+
attrs = m.group("attrs")
|
|
57
|
+
if re.search(r"\balt\s*=", attrs) or re.search(r"""\baria-hidden\s*=\s*['"]true['"]""", attrs):
|
|
58
|
+
continue
|
|
59
|
+
if re.search(r"""\brole\s*=\s*['"](?:presentation|none)['"]""", attrs):
|
|
60
|
+
continue
|
|
61
|
+
if "{..." in attrs:
|
|
62
|
+
continue
|
|
63
|
+
lineno = raw_source.count("\n", 0, m.start()) + 1
|
|
64
|
+
emit(
|
|
65
|
+
lineno,
|
|
66
|
+
"img_alt",
|
|
67
|
+
"Image element `<img>` missing `alt` text (`WCAG 1.1.1`)",
|
|
68
|
+
f"{rel_repo}:{lineno} renders `<img>` without an `alt` attribute (use `alt=\"\"` if purely decorative).",
|
|
69
|
+
"Provide descriptive `alt` text or `alt=\"\"` with `aria-hidden=\"true\"` for decorative images.",
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# 2. Click handlers on non-interactive <div>/<span> without role + keyboard focusability
|
|
73
|
+
for m in _NON_INTERACTIVE_CLICK_RE.finditer(raw_source):
|
|
74
|
+
tag = m.group("tag")
|
|
75
|
+
attrs = m.group("attrs")
|
|
76
|
+
if re.search(r"""\baria-hidden\s*=\s*['"]true['"]""", attrs):
|
|
77
|
+
continue
|
|
78
|
+
has_role = bool(re.search(r"\brole\s*=", attrs))
|
|
79
|
+
has_tab = bool(re.search(r"\btab[Ii]ndex\s*=", attrs))
|
|
80
|
+
if has_role and has_tab:
|
|
81
|
+
continue
|
|
82
|
+
lineno = raw_source.count("\n", 0, m.start()) + 1
|
|
83
|
+
emit(
|
|
84
|
+
lineno,
|
|
85
|
+
"div_click",
|
|
86
|
+
f"Non-interactive `<{tag}>` has click handler without keyboard/ARIA semantics (`WCAG 2.1.1`)",
|
|
87
|
+
f"{rel_repo}:{lineno} attaches a click handler to `<{tag}>` without `role` and `tabIndex`.",
|
|
88
|
+
"Replace with a semantic `<button>` element, or add `role=\"button\"`, `tabIndex={0}`, and `onKeyDown`.",
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# 3. Icon-only <button> without aria-label / aria-labelledby / title
|
|
92
|
+
for m in _EMPTY_BUTTON_RE.finditer(raw_source):
|
|
93
|
+
attrs = m.group("attrs")
|
|
94
|
+
if re.search(r"\b(?:aria-label|aria-labelledby|title)\s*=", attrs) or "{..." in attrs:
|
|
95
|
+
continue
|
|
96
|
+
lineno = raw_source.count("\n", 0, m.start()) + 1
|
|
97
|
+
emit(
|
|
98
|
+
lineno,
|
|
99
|
+
"icon_btn",
|
|
100
|
+
"Icon-only `<button>` missing accessible name (`WCAG 4.1.2`)",
|
|
101
|
+
f"{rel_repo}:{lineno} renders an icon-only `<button>` with no `aria-label`, `aria-labelledby`, or visible text.",
|
|
102
|
+
"Add `aria-label=\"...\"` or visually-hidden `<span className=\"sr-only\">` text inside the button.",
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# 4. Orphaned form controls (<input>/<select>/<textarea> without label[for], wrapping <label>, or aria-label)
|
|
106
|
+
labeled_ids = {
|
|
107
|
+
(lm.group("id") or lm.group("expr") or "").strip()
|
|
108
|
+
for lm in _LABEL_FOR_RE.finditer(raw_source)
|
|
109
|
+
}
|
|
110
|
+
wrapped_spans = [wm.span() for wm in _WRAPPING_LABEL_RE.finditer(raw_source)]
|
|
111
|
+
for m in _INPUT_TAG_RE.finditer(raw_source):
|
|
112
|
+
tag = m.group("tag")
|
|
113
|
+
attrs = m.group("attrs")
|
|
114
|
+
if _HIDDEN_INPUT_RE.search(attrs) or "{..." in attrs:
|
|
115
|
+
continue
|
|
116
|
+
if re.search(r"\b(?:aria-label|aria-labelledby|title)\s*=", attrs):
|
|
117
|
+
continue
|
|
118
|
+
if any(ws <= m.start() and m.end() <= we for ws, we in wrapped_spans):
|
|
119
|
+
continue
|
|
120
|
+
idm = _ID_ATTR_RE.search(attrs)
|
|
121
|
+
if idm:
|
|
122
|
+
id_val = (idm.group("id") or idm.group("expr") or "").strip()
|
|
123
|
+
if id_val and id_val in labeled_ids:
|
|
124
|
+
continue
|
|
125
|
+
lineno = raw_source.count("\n", 0, m.start()) + 1
|
|
126
|
+
emit(
|
|
127
|
+
lineno,
|
|
128
|
+
"orphan_input",
|
|
129
|
+
f"Form control `<{tag}>` lacks associated `<label>` or `aria-label` (`WCAG 1.3.1`)",
|
|
130
|
+
f"{rel_repo}:{lineno} renders `<{tag}>` without a matching `<label htmlFor=...>` or `aria-label`.",
|
|
131
|
+
"Associate a `<label htmlFor=\"...\">`, wrap in `<label>`, or add `aria-label=\"...\"`.",
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
return findings
|