aja-codeintel 0.1.7__tar.gz → 0.1.9__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.
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/PKG-INFO +98 -5
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/README.md +97 -4
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/aja_codeintel.egg-info/PKG-INFO +98 -5
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/aja_codeintel.egg-info/SOURCES.txt +10 -1
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/nav/open_cmd.py +1 -3
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/project/__init__.py +5 -1
- aja_codeintel-0.1.9/codeintel_cli/commands/project/fastapi_cmd.py +144 -0
- aja_codeintel-0.1.9/codeintel_cli/commands/project/overview_cmd.py +1356 -0
- aja_codeintel-0.1.9/codeintel_cli/commands/project/trace_cmd.py +473 -0
- aja_codeintel-0.1.9/codeintel_cli/endpoints/fastapi_scanner.py +1220 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/endpoints/java_spring.py +471 -364
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/endpoints/python_web.py +18 -18
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/lang/java/ast_engine.py +442 -481
- aja_codeintel-0.1.9/codeintel_cli/lang/java/auth.py +50 -0
- aja_codeintel-0.1.9/codeintel_cli/lang/java/base_path.py +79 -0
- aja_codeintel-0.1.9/codeintel_cli/lang/java/enums.py +54 -0
- aja_codeintel-0.1.9/codeintel_cli/lang/java/pagination.py +60 -0
- aja_codeintel-0.1.9/codeintel_cli/lang/java/validation.py +78 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/scanner/scanner.py +24 -1
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/pyproject.toml +1 -1
- aja_codeintel-0.1.9/tests/test_fastapi_scanner.py +544 -0
- aja_codeintel-0.1.7/codeintel_cli/commands/project/overview_cmd.py +0 -332
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/LICENSE +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/aja_codeintel.egg-info/dependency_links.txt +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/aja_codeintel.egg-info/entry_points.txt +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/aja_codeintel.egg-info/requires.txt +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/aja_codeintel.egg-info/top_level.txt +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/__init__.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/__main__.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/cli.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/__init__.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/graph/__init__.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/graph/deps_cmd.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/graph/related_cmd.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/graph/relsymbols_cmd.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/graph/reverse_related_cmd.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/nav/__init__.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/nav/copy_cmd.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/nav/where_cmd.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/project/context_cmd.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/project/debug_cmd.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/project/endpoints_cmd.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/project/errors_cmd.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/project/folder_cmd.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/project/frontend_cmd.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/project/imports_cmd.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/project/models_cmd.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/project/modeltree_cmd.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/project/new.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/project/resolve_cmd.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/project/scan_cmd.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/project/servicemap_cmd.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/project/tree_cmd.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/project/types_cmd.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/commands/project/version_cmd.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/context/java_context.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/context/java_rel.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/context/java_service.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/context/python_context.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/context/python_rel.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/context/python_service.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/core/fuzzy.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/core/opener.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/core/project.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/core/resolve_folder.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/core/resolve_model_target.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/core/resolve_target.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/core/timing.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/core/where.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/db/__init__.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/db/cache.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/db/operations.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/db/schema.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/endpoints/__init__.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/endpoints/models.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/endpoints/openapi_spec.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/endpoints/scan.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/errors.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/frontend/__init__.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/frontend/server.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/graph/__init__.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/graph/builder.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/graph/query.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/graph/traverse.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/lang/__init__.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/lang/java/__init__.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/lang/java/call_graph.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/lang/java/engine.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/lang/java/method_index.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/lang/java/models.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/lang/java/resolve.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/lang/java/types.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/lang/python/__init__.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/lang/python/engine.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/lang/python/models.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/lang/router.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/parser/imports.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/parser/resolve.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/parser/symbols.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/scanner/__init__.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/terminal/__init__.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/terminal/error_parser.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/codeintel_cli/terminal/printer.py +0 -0
- {aja_codeintel-0.1.7 → aja_codeintel-0.1.9}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aja-codeintel
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.9
|
|
4
4
|
Summary: CodeIntel CLI tool
|
|
5
5
|
Author: Shiva Areti
|
|
6
6
|
License: MIT License
|
|
@@ -340,6 +340,99 @@ aja models
|
|
|
340
340
|
|
|
341
341
|
---
|
|
342
342
|
|
|
343
|
+
### `aja fastapi-scan .`
|
|
344
|
+
|
|
345
|
+
**What it does:** Full FastAPI project scan — resolves complete route paths including `APIRouter` prefixes and `include_router` prefix stacking, extracts Pydantic DTOs (with required/optional fields, default values, nested models), and extracts SQLAlchemy entity models (columns, types, primary keys, foreign keys, relationships). Produces structured JSON matching the same schema as the Java Spring Boot scanner.
|
|
346
|
+
|
|
347
|
+
**When to use it:** Deep analysis of a FastAPI codebase. Generating API + data model inventory. Feeding structured metadata into AI tools or documentation pipelines.
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
# JSON report (default) — same schema as Java scanner
|
|
351
|
+
aja fastapi-scan .
|
|
352
|
+
|
|
353
|
+
# Human-readable text
|
|
354
|
+
aja fastapi-scan . --format text
|
|
355
|
+
|
|
356
|
+
# Only paths under /api
|
|
357
|
+
aja fastapi-scan . --only-api
|
|
358
|
+
|
|
359
|
+
# Skip DTOs or models from output
|
|
360
|
+
aja fastapi-scan . --no-dtos
|
|
361
|
+
aja fastapi-scan . --no-models
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
**Output schema (JSON):**
|
|
365
|
+
```json
|
|
366
|
+
{
|
|
367
|
+
"endpoints": [
|
|
368
|
+
{
|
|
369
|
+
"method": "POST",
|
|
370
|
+
"path": "/api/v1/users",
|
|
371
|
+
"file": "app/routers/users.py",
|
|
372
|
+
"line": 24,
|
|
373
|
+
"handler": "create_user",
|
|
374
|
+
"framework": "fastapi",
|
|
375
|
+
"controller": "CreateUserRequest"
|
|
376
|
+
}
|
|
377
|
+
],
|
|
378
|
+
"dtos": [
|
|
379
|
+
{
|
|
380
|
+
"name": "CreateUserRequest",
|
|
381
|
+
"file": "app/schemas/user.py",
|
|
382
|
+
"kind": "pydantic",
|
|
383
|
+
"bases": ["BaseModel"],
|
|
384
|
+
"fields": [
|
|
385
|
+
{ "name": "name", "type": "str", "required": true, "default": null },
|
|
386
|
+
{ "name": "email", "type": "str", "required": true, "default": null },
|
|
387
|
+
{ "name": "age", "type": "Optional[int]","required": false, "default": "None" }
|
|
388
|
+
]
|
|
389
|
+
}
|
|
390
|
+
],
|
|
391
|
+
"models": [
|
|
392
|
+
{
|
|
393
|
+
"name": "User",
|
|
394
|
+
"file": "app/models/user.py",
|
|
395
|
+
"kind": "sqlalchemy",
|
|
396
|
+
"table": "users",
|
|
397
|
+
"columns": [
|
|
398
|
+
{ "name": "id", "type": "Integer", "primary_key": true, "foreign_key": null },
|
|
399
|
+
{ "name": "email", "type": "String", "primary_key": false, "foreign_key": null }
|
|
400
|
+
],
|
|
401
|
+
"relationships": [
|
|
402
|
+
{ "name": "posts", "target": "Post", "kind": "relationship" }
|
|
403
|
+
]
|
|
404
|
+
}
|
|
405
|
+
]
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
**What the FastAPI scanner resolves:**
|
|
410
|
+
|
|
411
|
+
| Pattern | Resolved path |
|
|
412
|
+
|---|---|
|
|
413
|
+
| `APIRouter(prefix="/users")` + `@router.get("/")` | `/users` |
|
|
414
|
+
| `app.include_router(router, prefix="/api")` | `/api/users` |
|
|
415
|
+
| `app.include_router(router, prefix="/v1")` + router prefix `/api` | `/v1/api/users` |
|
|
416
|
+
| `@router.api_route("/ping", methods=["GET","HEAD"])` | `GET /ping` + `HEAD /ping` |
|
|
417
|
+
|
|
418
|
+
**What the Pydantic extractor captures:**
|
|
419
|
+
|
|
420
|
+
- `class X(BaseModel)` — direct BaseModel subclass
|
|
421
|
+
- `class X(SomeBase)` — inherited from another Pydantic model in the same project
|
|
422
|
+
- Required vs optional fields (`Optional[T]`, `T | None`, `Field(...)`, no default)
|
|
423
|
+
- Default values including `Field(default=…)`
|
|
424
|
+
- List types (`List[str]`, `list[int]`)
|
|
425
|
+
|
|
426
|
+
**What the SQLAlchemy extractor captures:**
|
|
427
|
+
|
|
428
|
+
- Classic declarative style: `Column(Integer, primary_key=True)`
|
|
429
|
+
- Modern mapped style: `id: Mapped[int] = mapped_column(primary_key=True)`
|
|
430
|
+
- Foreign keys: `Column(Integer, ForeignKey("users.id"))`
|
|
431
|
+
- Relationships: `relationship("Post", back_populates="author")`
|
|
432
|
+
- `__tablename__`
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
343
436
|
### `aja copy <file>`
|
|
344
437
|
|
|
345
438
|
**What it does:** Copies the full contents of a file (or multiple files) to your clipboard — ready to paste into a code review, chat, or AI tool.
|
|
@@ -377,10 +470,10 @@ Prints the current AJA version.
|
|
|
377
470
|
|
|
378
471
|
## Supported Languages
|
|
379
472
|
|
|
380
|
-
| Language | Analysis | Imports | Symbols | Services | Models |
|
|
381
|
-
|
|
382
|
-
| Java | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
383
|
-
| Python | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
473
|
+
| Language | Analysis | Imports | Symbols | Services | Models | Endpoints |
|
|
474
|
+
|----------|----------|---------|---------|----------|--------|-----------|
|
|
475
|
+
| Java (Spring Boot) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
476
|
+
| Python (FastAPI) | ✅ | ✅ | ✅ | ✅ | ✅ (Pydantic + SQLAlchemy) | ✅ (prefix-resolved) |
|
|
384
477
|
|
|
385
478
|
> Static analysis only — no compilation, no runtime, no build tools required.
|
|
386
479
|
|
|
@@ -303,6 +303,99 @@ aja models
|
|
|
303
303
|
|
|
304
304
|
---
|
|
305
305
|
|
|
306
|
+
### `aja fastapi-scan .`
|
|
307
|
+
|
|
308
|
+
**What it does:** Full FastAPI project scan — resolves complete route paths including `APIRouter` prefixes and `include_router` prefix stacking, extracts Pydantic DTOs (with required/optional fields, default values, nested models), and extracts SQLAlchemy entity models (columns, types, primary keys, foreign keys, relationships). Produces structured JSON matching the same schema as the Java Spring Boot scanner.
|
|
309
|
+
|
|
310
|
+
**When to use it:** Deep analysis of a FastAPI codebase. Generating API + data model inventory. Feeding structured metadata into AI tools or documentation pipelines.
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
# JSON report (default) — same schema as Java scanner
|
|
314
|
+
aja fastapi-scan .
|
|
315
|
+
|
|
316
|
+
# Human-readable text
|
|
317
|
+
aja fastapi-scan . --format text
|
|
318
|
+
|
|
319
|
+
# Only paths under /api
|
|
320
|
+
aja fastapi-scan . --only-api
|
|
321
|
+
|
|
322
|
+
# Skip DTOs or models from output
|
|
323
|
+
aja fastapi-scan . --no-dtos
|
|
324
|
+
aja fastapi-scan . --no-models
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
**Output schema (JSON):**
|
|
328
|
+
```json
|
|
329
|
+
{
|
|
330
|
+
"endpoints": [
|
|
331
|
+
{
|
|
332
|
+
"method": "POST",
|
|
333
|
+
"path": "/api/v1/users",
|
|
334
|
+
"file": "app/routers/users.py",
|
|
335
|
+
"line": 24,
|
|
336
|
+
"handler": "create_user",
|
|
337
|
+
"framework": "fastapi",
|
|
338
|
+
"controller": "CreateUserRequest"
|
|
339
|
+
}
|
|
340
|
+
],
|
|
341
|
+
"dtos": [
|
|
342
|
+
{
|
|
343
|
+
"name": "CreateUserRequest",
|
|
344
|
+
"file": "app/schemas/user.py",
|
|
345
|
+
"kind": "pydantic",
|
|
346
|
+
"bases": ["BaseModel"],
|
|
347
|
+
"fields": [
|
|
348
|
+
{ "name": "name", "type": "str", "required": true, "default": null },
|
|
349
|
+
{ "name": "email", "type": "str", "required": true, "default": null },
|
|
350
|
+
{ "name": "age", "type": "Optional[int]","required": false, "default": "None" }
|
|
351
|
+
]
|
|
352
|
+
}
|
|
353
|
+
],
|
|
354
|
+
"models": [
|
|
355
|
+
{
|
|
356
|
+
"name": "User",
|
|
357
|
+
"file": "app/models/user.py",
|
|
358
|
+
"kind": "sqlalchemy",
|
|
359
|
+
"table": "users",
|
|
360
|
+
"columns": [
|
|
361
|
+
{ "name": "id", "type": "Integer", "primary_key": true, "foreign_key": null },
|
|
362
|
+
{ "name": "email", "type": "String", "primary_key": false, "foreign_key": null }
|
|
363
|
+
],
|
|
364
|
+
"relationships": [
|
|
365
|
+
{ "name": "posts", "target": "Post", "kind": "relationship" }
|
|
366
|
+
]
|
|
367
|
+
}
|
|
368
|
+
]
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
**What the FastAPI scanner resolves:**
|
|
373
|
+
|
|
374
|
+
| Pattern | Resolved path |
|
|
375
|
+
|---|---|
|
|
376
|
+
| `APIRouter(prefix="/users")` + `@router.get("/")` | `/users` |
|
|
377
|
+
| `app.include_router(router, prefix="/api")` | `/api/users` |
|
|
378
|
+
| `app.include_router(router, prefix="/v1")` + router prefix `/api` | `/v1/api/users` |
|
|
379
|
+
| `@router.api_route("/ping", methods=["GET","HEAD"])` | `GET /ping` + `HEAD /ping` |
|
|
380
|
+
|
|
381
|
+
**What the Pydantic extractor captures:**
|
|
382
|
+
|
|
383
|
+
- `class X(BaseModel)` — direct BaseModel subclass
|
|
384
|
+
- `class X(SomeBase)` — inherited from another Pydantic model in the same project
|
|
385
|
+
- Required vs optional fields (`Optional[T]`, `T | None`, `Field(...)`, no default)
|
|
386
|
+
- Default values including `Field(default=…)`
|
|
387
|
+
- List types (`List[str]`, `list[int]`)
|
|
388
|
+
|
|
389
|
+
**What the SQLAlchemy extractor captures:**
|
|
390
|
+
|
|
391
|
+
- Classic declarative style: `Column(Integer, primary_key=True)`
|
|
392
|
+
- Modern mapped style: `id: Mapped[int] = mapped_column(primary_key=True)`
|
|
393
|
+
- Foreign keys: `Column(Integer, ForeignKey("users.id"))`
|
|
394
|
+
- Relationships: `relationship("Post", back_populates="author")`
|
|
395
|
+
- `__tablename__`
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
306
399
|
### `aja copy <file>`
|
|
307
400
|
|
|
308
401
|
**What it does:** Copies the full contents of a file (or multiple files) to your clipboard — ready to paste into a code review, chat, or AI tool.
|
|
@@ -340,10 +433,10 @@ Prints the current AJA version.
|
|
|
340
433
|
|
|
341
434
|
## Supported Languages
|
|
342
435
|
|
|
343
|
-
| Language | Analysis | Imports | Symbols | Services | Models |
|
|
344
|
-
|
|
345
|
-
| Java | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
346
|
-
| Python | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
436
|
+
| Language | Analysis | Imports | Symbols | Services | Models | Endpoints |
|
|
437
|
+
|----------|----------|---------|---------|----------|--------|-----------|
|
|
438
|
+
| Java (Spring Boot) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
439
|
+
| Python (FastAPI) | ✅ | ✅ | ✅ | ✅ | ✅ (Pydantic + SQLAlchemy) | ✅ (prefix-resolved) |
|
|
347
440
|
|
|
348
441
|
> Static analysis only — no compilation, no runtime, no build tools required.
|
|
349
442
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aja-codeintel
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.9
|
|
4
4
|
Summary: CodeIntel CLI tool
|
|
5
5
|
Author: Shiva Areti
|
|
6
6
|
License: MIT License
|
|
@@ -340,6 +340,99 @@ aja models
|
|
|
340
340
|
|
|
341
341
|
---
|
|
342
342
|
|
|
343
|
+
### `aja fastapi-scan .`
|
|
344
|
+
|
|
345
|
+
**What it does:** Full FastAPI project scan — resolves complete route paths including `APIRouter` prefixes and `include_router` prefix stacking, extracts Pydantic DTOs (with required/optional fields, default values, nested models), and extracts SQLAlchemy entity models (columns, types, primary keys, foreign keys, relationships). Produces structured JSON matching the same schema as the Java Spring Boot scanner.
|
|
346
|
+
|
|
347
|
+
**When to use it:** Deep analysis of a FastAPI codebase. Generating API + data model inventory. Feeding structured metadata into AI tools or documentation pipelines.
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
# JSON report (default) — same schema as Java scanner
|
|
351
|
+
aja fastapi-scan .
|
|
352
|
+
|
|
353
|
+
# Human-readable text
|
|
354
|
+
aja fastapi-scan . --format text
|
|
355
|
+
|
|
356
|
+
# Only paths under /api
|
|
357
|
+
aja fastapi-scan . --only-api
|
|
358
|
+
|
|
359
|
+
# Skip DTOs or models from output
|
|
360
|
+
aja fastapi-scan . --no-dtos
|
|
361
|
+
aja fastapi-scan . --no-models
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
**Output schema (JSON):**
|
|
365
|
+
```json
|
|
366
|
+
{
|
|
367
|
+
"endpoints": [
|
|
368
|
+
{
|
|
369
|
+
"method": "POST",
|
|
370
|
+
"path": "/api/v1/users",
|
|
371
|
+
"file": "app/routers/users.py",
|
|
372
|
+
"line": 24,
|
|
373
|
+
"handler": "create_user",
|
|
374
|
+
"framework": "fastapi",
|
|
375
|
+
"controller": "CreateUserRequest"
|
|
376
|
+
}
|
|
377
|
+
],
|
|
378
|
+
"dtos": [
|
|
379
|
+
{
|
|
380
|
+
"name": "CreateUserRequest",
|
|
381
|
+
"file": "app/schemas/user.py",
|
|
382
|
+
"kind": "pydantic",
|
|
383
|
+
"bases": ["BaseModel"],
|
|
384
|
+
"fields": [
|
|
385
|
+
{ "name": "name", "type": "str", "required": true, "default": null },
|
|
386
|
+
{ "name": "email", "type": "str", "required": true, "default": null },
|
|
387
|
+
{ "name": "age", "type": "Optional[int]","required": false, "default": "None" }
|
|
388
|
+
]
|
|
389
|
+
}
|
|
390
|
+
],
|
|
391
|
+
"models": [
|
|
392
|
+
{
|
|
393
|
+
"name": "User",
|
|
394
|
+
"file": "app/models/user.py",
|
|
395
|
+
"kind": "sqlalchemy",
|
|
396
|
+
"table": "users",
|
|
397
|
+
"columns": [
|
|
398
|
+
{ "name": "id", "type": "Integer", "primary_key": true, "foreign_key": null },
|
|
399
|
+
{ "name": "email", "type": "String", "primary_key": false, "foreign_key": null }
|
|
400
|
+
],
|
|
401
|
+
"relationships": [
|
|
402
|
+
{ "name": "posts", "target": "Post", "kind": "relationship" }
|
|
403
|
+
]
|
|
404
|
+
}
|
|
405
|
+
]
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
**What the FastAPI scanner resolves:**
|
|
410
|
+
|
|
411
|
+
| Pattern | Resolved path |
|
|
412
|
+
|---|---|
|
|
413
|
+
| `APIRouter(prefix="/users")` + `@router.get("/")` | `/users` |
|
|
414
|
+
| `app.include_router(router, prefix="/api")` | `/api/users` |
|
|
415
|
+
| `app.include_router(router, prefix="/v1")` + router prefix `/api` | `/v1/api/users` |
|
|
416
|
+
| `@router.api_route("/ping", methods=["GET","HEAD"])` | `GET /ping` + `HEAD /ping` |
|
|
417
|
+
|
|
418
|
+
**What the Pydantic extractor captures:**
|
|
419
|
+
|
|
420
|
+
- `class X(BaseModel)` — direct BaseModel subclass
|
|
421
|
+
- `class X(SomeBase)` — inherited from another Pydantic model in the same project
|
|
422
|
+
- Required vs optional fields (`Optional[T]`, `T | None`, `Field(...)`, no default)
|
|
423
|
+
- Default values including `Field(default=…)`
|
|
424
|
+
- List types (`List[str]`, `list[int]`)
|
|
425
|
+
|
|
426
|
+
**What the SQLAlchemy extractor captures:**
|
|
427
|
+
|
|
428
|
+
- Classic declarative style: `Column(Integer, primary_key=True)`
|
|
429
|
+
- Modern mapped style: `id: Mapped[int] = mapped_column(primary_key=True)`
|
|
430
|
+
- Foreign keys: `Column(Integer, ForeignKey("users.id"))`
|
|
431
|
+
- Relationships: `relationship("Post", back_populates="author")`
|
|
432
|
+
- `__tablename__`
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
343
436
|
### `aja copy <file>`
|
|
344
437
|
|
|
345
438
|
**What it does:** Copies the full contents of a file (or multiple files) to your clipboard — ready to paste into a code review, chat, or AI tool.
|
|
@@ -377,10 +470,10 @@ Prints the current AJA version.
|
|
|
377
470
|
|
|
378
471
|
## Supported Languages
|
|
379
472
|
|
|
380
|
-
| Language | Analysis | Imports | Symbols | Services | Models |
|
|
381
|
-
|
|
382
|
-
| Java | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
383
|
-
| Python | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
473
|
+
| Language | Analysis | Imports | Symbols | Services | Models | Endpoints |
|
|
474
|
+
|----------|----------|---------|---------|----------|--------|-----------|
|
|
475
|
+
| Java (Spring Boot) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
476
|
+
| Python (FastAPI) | ✅ | ✅ | ✅ | ✅ | ✅ (Pydantic + SQLAlchemy) | ✅ (prefix-resolved) |
|
|
384
477
|
|
|
385
478
|
> Static analysis only — no compilation, no runtime, no build tools required.
|
|
386
479
|
|
|
@@ -26,6 +26,7 @@ codeintel_cli/commands/project/context_cmd.py
|
|
|
26
26
|
codeintel_cli/commands/project/debug_cmd.py
|
|
27
27
|
codeintel_cli/commands/project/endpoints_cmd.py
|
|
28
28
|
codeintel_cli/commands/project/errors_cmd.py
|
|
29
|
+
codeintel_cli/commands/project/fastapi_cmd.py
|
|
29
30
|
codeintel_cli/commands/project/folder_cmd.py
|
|
30
31
|
codeintel_cli/commands/project/frontend_cmd.py
|
|
31
32
|
codeintel_cli/commands/project/imports_cmd.py
|
|
@@ -36,6 +37,7 @@ codeintel_cli/commands/project/overview_cmd.py
|
|
|
36
37
|
codeintel_cli/commands/project/resolve_cmd.py
|
|
37
38
|
codeintel_cli/commands/project/scan_cmd.py
|
|
38
39
|
codeintel_cli/commands/project/servicemap_cmd.py
|
|
40
|
+
codeintel_cli/commands/project/trace_cmd.py
|
|
39
41
|
codeintel_cli/commands/project/tree_cmd.py
|
|
40
42
|
codeintel_cli/commands/project/types_cmd.py
|
|
41
43
|
codeintel_cli/commands/project/version_cmd.py
|
|
@@ -58,6 +60,7 @@ codeintel_cli/db/cache.py
|
|
|
58
60
|
codeintel_cli/db/operations.py
|
|
59
61
|
codeintel_cli/db/schema.py
|
|
60
62
|
codeintel_cli/endpoints/__init__.py
|
|
63
|
+
codeintel_cli/endpoints/fastapi_scanner.py
|
|
61
64
|
codeintel_cli/endpoints/java_spring.py
|
|
62
65
|
codeintel_cli/endpoints/models.py
|
|
63
66
|
codeintel_cli/endpoints/openapi_spec.py
|
|
@@ -73,12 +76,17 @@ codeintel_cli/lang/__init__.py
|
|
|
73
76
|
codeintel_cli/lang/router.py
|
|
74
77
|
codeintel_cli/lang/java/__init__.py
|
|
75
78
|
codeintel_cli/lang/java/ast_engine.py
|
|
79
|
+
codeintel_cli/lang/java/auth.py
|
|
80
|
+
codeintel_cli/lang/java/base_path.py
|
|
76
81
|
codeintel_cli/lang/java/call_graph.py
|
|
77
82
|
codeintel_cli/lang/java/engine.py
|
|
83
|
+
codeintel_cli/lang/java/enums.py
|
|
78
84
|
codeintel_cli/lang/java/method_index.py
|
|
79
85
|
codeintel_cli/lang/java/models.py
|
|
86
|
+
codeintel_cli/lang/java/pagination.py
|
|
80
87
|
codeintel_cli/lang/java/resolve.py
|
|
81
88
|
codeintel_cli/lang/java/types.py
|
|
89
|
+
codeintel_cli/lang/java/validation.py
|
|
82
90
|
codeintel_cli/lang/python/__init__.py
|
|
83
91
|
codeintel_cli/lang/python/engine.py
|
|
84
92
|
codeintel_cli/lang/python/models.py
|
|
@@ -89,4 +97,5 @@ codeintel_cli/scanner/__init__.py
|
|
|
89
97
|
codeintel_cli/scanner/scanner.py
|
|
90
98
|
codeintel_cli/terminal/__init__.py
|
|
91
99
|
codeintel_cli/terminal/error_parser.py
|
|
92
|
-
codeintel_cli/terminal/printer.py
|
|
100
|
+
codeintel_cli/terminal/printer.py
|
|
101
|
+
tests/test_fastapi_scanner.py
|
|
@@ -13,6 +13,4 @@ def register_open(app: typer.Typer) -> None:
|
|
|
13
13
|
yes: bool = typer.Option(False, "--yes", "-y", help="Open without confirmation"),
|
|
14
14
|
):
|
|
15
15
|
target, _root = resolve_target_file(query)
|
|
16
|
-
|
|
17
|
-
if yes or typer.confirm(f"Open this file?\n{target}"):
|
|
18
|
-
open_file(target)
|
|
16
|
+
open_file(target)
|
|
@@ -19,6 +19,8 @@ def register_project_commands(app: typer.Typer) -> None:
|
|
|
19
19
|
from .frontend_cmd import register_frontend
|
|
20
20
|
from .errors_cmd import register_errors
|
|
21
21
|
from .debug_cmd import register_debug
|
|
22
|
+
from .fastapi_cmd import register_fastapi_scan
|
|
23
|
+
from .trace_cmd import register_trace
|
|
22
24
|
|
|
23
25
|
register_overview(app)
|
|
24
26
|
register_models(app)
|
|
@@ -35,4 +37,6 @@ def register_project_commands(app: typer.Typer) -> None:
|
|
|
35
37
|
register_version(app)
|
|
36
38
|
register_frontend(app)
|
|
37
39
|
register_errors(app)
|
|
38
|
-
register_debug(app)
|
|
40
|
+
register_debug(app)
|
|
41
|
+
register_fastapi_scan(app)
|
|
42
|
+
register_trace(app)
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"""
|
|
2
|
+
fastapi-scan command – full FastAPI project scan with prefix resolution.
|
|
3
|
+
|
|
4
|
+
Outputs a JSON report matching the same schema as the Java Spring Boot scanner:
|
|
5
|
+
{
|
|
6
|
+
"endpoints": [ { method, path, file, line, handler, framework, ... } ],
|
|
7
|
+
"dtos": [ { name, file, kind, bases, fields: [{name, type, required, default}] } ],
|
|
8
|
+
"models": [ { name, file, kind, table, columns: [...], relationships: [...] } ]
|
|
9
|
+
}
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import time
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
import typer
|
|
19
|
+
|
|
20
|
+
from ...core.project import find_project_root
|
|
21
|
+
from ...endpoints.fastapi_scanner import scan_fastapi_project
|
|
22
|
+
from ...endpoints.scan import EndpointScanOptions, iter_supported_source_files
|
|
23
|
+
from ...errors import InvalidPathError
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _validate_folder(path: str) -> Path:
|
|
27
|
+
folder = Path(path).resolve()
|
|
28
|
+
if not folder.exists() or not folder.is_dir():
|
|
29
|
+
raise InvalidPathError(message="Invalid folder", path=folder)
|
|
30
|
+
return folder
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _norm(p: str) -> str:
|
|
34
|
+
return p.replace("\\", "/")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def register_fastapi_scan(app: typer.Typer) -> None:
|
|
38
|
+
@app.command("fastapi-scan", help="Scan a FastAPI project (endpoints + DTOs + models).")
|
|
39
|
+
def fastapi_scan(
|
|
40
|
+
path: str = typer.Argument(".", help="FastAPI project root folder"),
|
|
41
|
+
fmt: str = typer.Option("json", "--format", "-f", help="json | text"),
|
|
42
|
+
hidden: bool = typer.Option(False, "--hidden", help="Include hidden dotfiles/folders"),
|
|
43
|
+
no_gitignore: bool = typer.Option(False, "--no-gitignore", help="Do not apply .gitignore rules"),
|
|
44
|
+
only_api: bool = typer.Option(False, "--only-api", help="Only show paths starting with /api"),
|
|
45
|
+
dedupe: bool = typer.Option(True, "--dedupe/--no-dedupe", help="Remove duplicate method+path pairs"),
|
|
46
|
+
no_dtos: bool = typer.Option(False, "--no-dtos", help="Omit Pydantic DTOs from output"),
|
|
47
|
+
no_models: bool = typer.Option(False, "--no-models", help="Omit SQLAlchemy models from output"),
|
|
48
|
+
) -> None:
|
|
49
|
+
folder = _validate_folder(path)
|
|
50
|
+
project_root = find_project_root(folder)
|
|
51
|
+
|
|
52
|
+
opts = EndpointScanOptions(
|
|
53
|
+
show_hidden=hidden,
|
|
54
|
+
use_gitignore=not no_gitignore,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
t0 = time.perf_counter()
|
|
58
|
+
|
|
59
|
+
# Collect .py files via the shared scanner (respects gitignore / hidden)
|
|
60
|
+
py_files = [
|
|
61
|
+
f for f in iter_supported_source_files(project_root, opts)
|
|
62
|
+
if f.suffix.lower() == ".py"
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
endpoints, dtos, sqla_models = scan_fastapi_project(
|
|
66
|
+
project_root, files=py_files
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Post-processing
|
|
70
|
+
if only_api:
|
|
71
|
+
endpoints = [e for e in endpoints if e.path.startswith("/api")]
|
|
72
|
+
|
|
73
|
+
if dedupe:
|
|
74
|
+
seen: set[tuple[str, str]] = set()
|
|
75
|
+
uniq = []
|
|
76
|
+
for e in endpoints:
|
|
77
|
+
key = (e.method, e.path)
|
|
78
|
+
if key not in seen:
|
|
79
|
+
seen.add(key)
|
|
80
|
+
uniq.append(e)
|
|
81
|
+
endpoints = uniq
|
|
82
|
+
|
|
83
|
+
endpoints.sort(key=lambda e: (e.path, e.method))
|
|
84
|
+
|
|
85
|
+
elapsed = time.perf_counter() - t0
|
|
86
|
+
fmt_norm = fmt.strip().lower()
|
|
87
|
+
|
|
88
|
+
# ------------------------------------------------------------------
|
|
89
|
+
# JSON output (default) — same schema as Java scanner
|
|
90
|
+
# ------------------------------------------------------------------
|
|
91
|
+
if fmt_norm == "json":
|
|
92
|
+
report: dict = {
|
|
93
|
+
"endpoints": [e.to_dict() for e in endpoints],
|
|
94
|
+
"dtos": [] if no_dtos else [d.to_dict() for d in dtos],
|
|
95
|
+
"models": [] if no_models else [m.to_dict() for m in sqla_models],
|
|
96
|
+
}
|
|
97
|
+
typer.echo(json.dumps(report, indent=2))
|
|
98
|
+
return
|
|
99
|
+
|
|
100
|
+
# ------------------------------------------------------------------
|
|
101
|
+
# Text output
|
|
102
|
+
# ------------------------------------------------------------------
|
|
103
|
+
typer.echo(f"FastAPI project: {project_root}")
|
|
104
|
+
typer.echo(f"Scanned {len(py_files)} Python files in {elapsed:.3f}s")
|
|
105
|
+
typer.echo("")
|
|
106
|
+
|
|
107
|
+
if endpoints:
|
|
108
|
+
typer.echo(f"ENDPOINTS ({len(endpoints)})")
|
|
109
|
+
typer.echo("─" * 60)
|
|
110
|
+
for e in endpoints:
|
|
111
|
+
handler_info = f" → {e.handler}"
|
|
112
|
+
if e.controller: # request body model stored here
|
|
113
|
+
handler_info += f" [body: {e.controller}]"
|
|
114
|
+
typer.echo(f" {e.method:7} {e.path}")
|
|
115
|
+
typer.echo(f" {handler_info} ({_norm(e.file)}:{e.line})")
|
|
116
|
+
typer.echo("")
|
|
117
|
+
else:
|
|
118
|
+
typer.echo("No FastAPI endpoints found.")
|
|
119
|
+
typer.echo("")
|
|
120
|
+
|
|
121
|
+
if not no_dtos and dtos:
|
|
122
|
+
typer.echo(f"PYDANTIC DTOS ({len(dtos)})")
|
|
123
|
+
typer.echo("─" * 60)
|
|
124
|
+
for d in dtos:
|
|
125
|
+
bases = ", ".join(d.bases) if d.bases else ""
|
|
126
|
+
typer.echo(f" {d.name}({bases}) {_norm(d.file)}")
|
|
127
|
+
for f_def in d.fields:
|
|
128
|
+
req_marker = "*" if f_def.required else " "
|
|
129
|
+
default_str = f" = {f_def.default}" if f_def.default is not None else ""
|
|
130
|
+
typer.echo(f" {req_marker} {f_def.name}: {f_def.type}{default_str}")
|
|
131
|
+
typer.echo("")
|
|
132
|
+
|
|
133
|
+
if not no_models and sqla_models:
|
|
134
|
+
typer.echo(f"SQLALCHEMY MODELS ({len(sqla_models)})")
|
|
135
|
+
typer.echo("─" * 60)
|
|
136
|
+
for m in sqla_models:
|
|
137
|
+
typer.echo(f" {m.name} table={m.table_name} {_norm(m.file)}")
|
|
138
|
+
for col in m.columns:
|
|
139
|
+
pk_marker = " [PK]" if col.primary_key else ""
|
|
140
|
+
fk_marker = f" [FK→{col.foreign_key}]" if col.foreign_key else ""
|
|
141
|
+
typer.echo(f" {col.name}: {col.col_type}{pk_marker}{fk_marker}")
|
|
142
|
+
for rel in m.relationships:
|
|
143
|
+
typer.echo(f" {rel.name}: → {rel.target}")
|
|
144
|
+
typer.echo("")
|