A stack pack teaches depgraf how to handle a specific language/framework combination. This guide covers how stack packs work, how to author new ones, and how to publish them for the community.
A stack pack is a directory containing declarative YAML files that define:
- How to detect the stack from project files
- How to generate OpenAPI specs using the framework's native tooling
- How to extract consumed endpoints, env vars, entities, routes, and test mappings from source code
depgraf ships with 8 built-in packs: python-fastapi, python-django, react-typescript, angular-typescript, java-spring, kotlin-ktor, node-express, node-nestjs. Community packs extend this to any language or framework.
my-stack-pack/
detect.yaml # Stack detection rules
generate.yaml # Framework-native generator config
rules/
consumed-endpoints.yaml # ast-grep rules for outbound HTTP calls
entities.yaml # ast-grep rules for ORM/data model definitions
env-vars.yaml # ast-grep rules for environment variable access
routes.yaml # ast-grep rules for route/endpoint definitions
test-map.yaml # ast-grep rules for test scenario IDs
All files are optional except detect.yaml. If a rule file is missing, that extraction category is skipped for workspaces using this stack.
detect.yaml tells depgraf how to identify a workspace using this stack. Detection checks for the presence of indicator files and specific content within them.
id: python-fastapi
name: "Python FastAPI"
type: backend
indicator_files:
- path: "pyproject.toml"
contains:
any_of:
- section: "[project.dependencies]"
pattern: "fastapi"
- section: "[tool.poetry.dependencies]"
pattern: "fastapi"
- path: "requirements.txt"
contains:
pattern: "fastapi"
priority: 100 # Higher priority wins when multiple stacks matchid: react-typescript
name: "React TypeScript"
type: frontend
indicator_files:
- path: "package.json"
contains:
all_of:
- json_path: "$.dependencies.react-router-dom"
- json_path: "$.devDependencies.typescript"
- path: "tsconfig.json"
exists: true
priority: 100| Field | Required | Description |
|---|---|---|
id |
Yes | Unique stack identifier (used in depgraf.yaml) |
name |
Yes | Human-readable name |
type |
Yes | backend or frontend (affects which extraction rules apply) |
indicator_files |
Yes | List of files to check |
indicator_files[].path |
Yes | Relative path from workspace root |
indicator_files[].exists |
No | Just check if the file exists (no content match) |
indicator_files[].contains |
No | Content matching rules |
contains.pattern |
No | Substring to find anywhere in the file |
contains.any_of |
No | At least one of these patterns must match |
contains.all_of |
No | All patterns must match |
contains.section |
No | Limit the search to a specific section (TOML/INI) |
contains.json_path |
No | Match against a JSON/YAML path |
priority |
No | Disambiguation when multiple stacks match (default: 50) |
generate.yaml defines how to produce an OpenAPI spec using the framework's native tooling.
openapi:
command: "python -c 'from app.main import app; import json; print(json.dumps(app.openapi()))'"
output: "docs/api/openapi.json"
timeout: 30 # seconds
fallback: trueopenapi:
command: "./gradlew generateOpenApiDocs"
output: "build/openapi.json"
timeout: 60
fallback: trueopenapi: null # No OpenAPI generation for frontend stacks
# depgraf will use ast-grep route extraction from rules/routes.yaml instead| Field | Required | Description |
|---|---|---|
openapi.command |
Yes | Shell command to run (working directory = workspace root) |
openapi.output |
Yes | Where the command writes the OpenAPI spec (relative to workspace) |
openapi.timeout |
No | Max execution time in seconds (default: 30) |
openapi.fallback |
No | Fall back to ast-grep route extraction on failure (default: true) |
The generator command must produce a valid OpenAPI 3.0+ JSON file. If it fails and fallback is true, depgraf falls back to ast-grep extraction using rules/routes.yaml.
Extraction rules are the core of a stack pack. Each rule file contains one or more ast-grep rules that match specific patterns in source code and transform the matches into structured JSON.
Find outbound HTTP calls to other services.
Python (requests/httpx):
rules:
- id: python-httpx-call
language: Python
rule:
kind: call
has:
kind: attribute
pattern: $CLIENT.$METHOD
has:
kind: identifier
regex: "^(get|post|put|patch|delete)$"
field: attribute
arguments:
has:
kind: string
pattern: $PATH
transform:
method:
source: $METHOD
uppercase: true
path:
source: $PATH
strip_quotes: true
target_service:
source: $CLIENT
infer_service: true # INVENTORY_CLIENT -> inventory-serviceTypeScript (fetch/axios):
rules:
- id: ts-fetch-call
language: TypeScript
rule:
pattern: fetch($URL, { method: $METHOD, $$$REST })
transform:
method:
source: $METHOD
strip_quotes: true
uppercase: true
path:
source: $URL
strip_quotes: true
extract_path: true # Extract path from full URL or template literal
- id: ts-axios-call
language: TypeScript
rule:
pattern: $CLIENT.$METHOD($URL, $$$ARGS)
has:
kind: member_expression
has:
kind: property_identifier
regex: "^(get|post|put|patch|delete)$"
transform:
method:
source: $METHOD
uppercase: true
path:
source: $URL
strip_quotes: trueFind env var access patterns.
Python:
rules:
- id: python-env-access
language: Python
rule:
any:
- pattern: os.environ[$KEY]
- pattern: os.environ.get($KEY, $$$DEFAULT)
- pattern: os.getenv($KEY, $$$DEFAULT)
transform:
name:
source: $KEY
strip_quotes: true
default:
source: $DEFAULT
strip_quotes: true
optional: trueTypeScript:
rules:
- id: ts-env-access
language: TypeScript
rule:
pattern: process.env.$NAME
transform:
name:
source: $NAMEFind ORM model definitions.
Python (SQLAlchemy):
rules:
- id: python-sqlalchemy-model
language: Python
rule:
kind: class_definition
has:
kind: argument_list
inside:
kind: class_definition
has:
pattern: Base
has:
kind: expression_statement
has:
kind: assignment
pattern: __tablename__ = $TABLE
transform:
name:
source: class_name
table:
source: $TABLE
strip_quotes: trueTypeScript (TypeORM):
rules:
- id: ts-typeorm-entity
language: TypeScript
rule:
kind: class_declaration
has:
kind: decorator
pattern: "@Entity($$$ARGS)"
transform:
name:
source: class_nameFind route/endpoint definitions.
React Router:
rules:
- id: react-route
language: TypeScript
rule:
pattern: <Route path=$PATH component={$COMPONENT} $$$PROPS />
transform:
path:
source: $PATH
strip_quotes: true
component:
source: $COMPONENTFastAPI:
rules:
- id: fastapi-route
language: Python
rule:
kind: decorated_definition
has:
kind: decorator
pattern: "@$ROUTER.$METHOD($PATH, $$$ARGS)"
transform:
method:
source: $METHOD
uppercase: true
path:
source: $PATH
strip_quotes: true
handler:
source: function_nameFind test scenario IDs (e.g., TS-001-021) in test files.
rules:
- id: python-test-scenario-id
language: Python
rule:
kind: comment
regex: "TS-\\d{3}-\\d{3}"
transform:
id:
regex_extract: "TS-\\d{3}-\\d{3}"
function:
source: enclosing_functionCreate a minimal project under tests/fixtures/your-stack/ with:
- The indicator files for detection
- Source files containing known patterns (consumed endpoints, env vars, entities)
- Document the expected extraction output in a comment or companion file
cargo test test_detect_your_stackVerify that depgraf init correctly identifies your stack from the fixture's indicator files.
cargo test test_extract_your_stackUse snapshot tests (insta) to capture and verify extraction output:
#[test]
fn test_extract_your_stack() {
let output = run_extract("tests/fixtures/your-stack");
insta::assert_yaml_snapshot!(output);
}Common edge cases to test:
- Multi-line function calls
- String concatenation / template literals for URLs
- Aliased imports (e.g.,
import httpx as http) - Decorated / annotated methods
- Nested class definitions (entities with inner classes)
- Comments and docstrings that look like code
The simplest distribution: push your stack pack to a git repo. Users install it with:
depgraf pack install https://github.com/you/depgraf-pack-ruby-rails.gitThe repo root should contain the stack pack files directly (detect.yaml, generate.yaml, rules/).
Use depgraf-pack-<language>-<framework> for discoverability:
depgraf-pack-ruby-railsdepgraf-pack-go-chidepgraf-pack-elixir-phoenix
detect.yaml-- detection rulesgenerate.yaml-- generator configrules/-- extraction rules for all applicable categoriesREADME.md-- what the pack supports, known limitations, example outputtests/-- fixture project and tests (so users can verify it works)LICENSE-- use MIT for maximum compatibility with depgraf's MIT license