Languages
Cascade ships with eight built-in language profiles. Each profile bundles the conventions, tooling, and LLM guidance for that language. The pipeline reads the profile and adapts: it puts new files in the right directories, names test files the right way, runs the right test command, and passes language-specific guidance into the LLM prompts.
Adding a ninth language is a single registry entry. There is no plugin system to learn; it is one file in one Python module.
Built-in languages
Section titled “Built-in languages”| Language | Detection | Test command | Source layout |
|---|---|---|---|
| Python | pyproject.toml, setup.py, requirements.txt | pytest | src/, tests in tests/ |
| TypeScript | tsconfig.json | npx vitest run | src/, tests next to source or in tests/ |
| JavaScript | package.json | npx vitest run | src/, tests next to source |
| Go | go.mod | go test ./... | flat, tests in same package as *_test.go |
| Rust | Cargo.toml | cargo test | src/, tests in tests/ or inline #[cfg(test)] |
| Java | pom.xml, build.gradle | mvn test | src/main/java, tests in src/test/java |
| Ruby | Gemfile, Rakefile | bundle exec rspec | lib/, tests in spec/ |
| C# | *.csproj, *.sln | dotnet test | src/, tests in separate *.Tests project |
How detection works
Section titled “How detection works”When you run a Cascade command, the language is resolved in this order, first match wins:
- Explicit override in
cascade.yaml:language: go - CLI flag on the build command:
Terminal window cascade build stories/sprint.yaml --language rust - Auto-detection from marker files in the repo root.
Auto-detection priority
Section titled “Auto-detection priority”For polyglot repos, the language with the highest detection priority wins. Current priorities (higher beats lower):
| Priority | Language | Marker files |
|---|---|---|
| 100 | Rust | Cargo.toml |
| 95 | Go | go.mod |
| 90 | Java | pom.xml or build.gradle |
| 80 | C# | *.csproj or *.sln |
| 70 | Ruby | Gemfile or Rakefile |
| 65 | TypeScript | tsconfig.json |
| 60 | Python | pyproject.toml, setup.py, requirements.txt |
| 50 | JavaScript | package.json (without tsconfig.json) |
So a repo with both pyproject.toml (Python, 60) and go.mod (Go, 95) is detected as Go. If that is wrong for the work you are doing, set language explicitly in cascade.yaml or pass --language on the command line.
What each profile defines
Section titled “What each profile defines”Every language profile is a LanguageProfile dataclass with these fields:
| Field | What it does |
|---|---|
name | Internal identifier (lowercase, used in flags and config) |
display_name | Human-friendly name (used in CLI output) |
file_extensions | Tuple of extensions Cascade considers part of the language. Drives repo scanning and code grouping. |
source_dir_default | Where new source files go by default (src/ for Python, . for Go) |
test_dir_default | Where tests go (tests/ for Python, same dir as source for Go) |
test_file_glob | How test files are named (test_*.py, *_test.go, *Test.kt) |
test_command | What runs to validate generated code (pytest, go test ./..., etc.) |
install_command | How to install dependencies (pip install -e ., npm install, cargo build) |
type_check_command | Optional type check before running tests (mypy ., tsc --noEmit, none for Go) |
formatter_command | Optional formatter run before commit (black ., prettier --write, cargo fmt) |
detection_files | Marker filenames for auto-detection |
detection_priority | Tiebreaker for polyglot repos (higher wins) |
notes_for_llm | Language-specific guidance passed into the LLM prompt at plan and code stages |
What notes_for_llm looks like
Section titled “What notes_for_llm looks like”This is the field that makes the generated code feel native rather than generic. Examples from the built-in profiles:
Python:
Use type hints on all public functions. Prefer
dataclassor Pydantic models over dicts. Tests use pytest, not unittest. Import order: stdlib, third-party, local. Usepathlib.Pathoveros.path. Usefrom __future__ import annotationsat the top of every file.
Go:
Error handling is explicit; never
_an error. Useerrors.Isanderrors.Asfor unwrapping. Tests live next to source in*_test.goand use the standardtestingpackage; reach fortestifyonly for assertions. Prefer interfaces defined where they are consumed, not where they are implemented.
Rust:
Prefer
Result<T, E>over panicking. Use?for propagation. Tests at the bottom of the file in a#[cfg(test)] mod tests { ... }block; integration tests intests/. Usecargo clippyconventions. Avoidunwrap()in production code paths.
These notes are LLM-grade guidance: short, opinionated, specific. The profile-bundled notes apply to everyone. Add team-specific overrides in team-memory/conventions.md.
Per-language behavior in the pipeline
Section titled “Per-language behavior in the pipeline”File discovery (repo scan)
Section titled “File discovery (repo scan)”Cascade’s repo scanner uses file_extensions to know which files are “yours” vs vendor/build artifacts. So in a Rust project, it lists *.rs files in the planning prompt but skips target/** automatically.
File generation defaults
Section titled “File generation defaults”If a story does not specify file paths, the planner uses source_dir_default and test_dir_default to place new files in the conventional location for the language. For Python: new code lands in src/, new tests in tests/. For Go: new code and tests live side by side in the package directory.
Test naming
Section titled “Test naming”Generated test files follow the language’s test_file_glob pattern, so they get picked up by the test runner automatically. This means:
- Python:
tests/test_users_pagination.py(matchespytest’s default discovery) - Go:
users_pagination_test.gonext tousers.go - Rust: tests added inline as
#[cfg(test)] mod tests { ... }or intests/users_pagination.rs - Java:
UsersPaginationTest.javainsrc/test/java/...
Install step
Section titled “Install step”The install command runs after apply and before test. It is best-effort: failure here does not abort the pipeline (Cascade just logs a warning and moves on to tests). This is intentional, because many repos do not have install commands that work cleanly in CI without setup.
Test step
Section titled “Test step”The test command is the one place where a non-zero exit code matters for the PR body but does not abort the pipeline. Cascade always commits, pushes, and opens the PR even if tests fail; the failure is recorded in the PR body so a human can see it. Reasoning: a failed test PR is much more useful than no PR at all, because the human can apply a small fix and merge.
You can override the test command in cascade.yaml:
test_command: pytest tests/unit -x --tb=shortThis wins over the profile default.
Adding a language
Section titled “Adding a language”A new language is a single entry in src/cascade/languages.py. Here is what adding Kotlin would look like:
KOTLIN = LanguageProfile( name="kotlin", display_name="Kotlin", file_extensions=(".kt", ".kts"), source_dir_default="src/main/kotlin", test_dir_default="src/test/kotlin", test_file_glob="*Test.kt", test_command=("gradle", "test"), install_command=("gradle", "build", "-x", "test"), type_check_command=None, formatter_command=("ktlint", "--format"), detection_files=("build.gradle.kts", "settings.gradle.kts"), detection_priority=75, notes_for_llm=( "Idiomatic Kotlin: prefer val over var, use data classes for " "value types, expressions over statements, scope functions " "(let, also, apply) over manual null checks. Tests with JUnit 5 " "and MockK. Use coroutines for async; never block the main thread." ),)Then add it to the PROFILES registry at the bottom of the file:
PROFILES = { "python": PYTHON, "typescript": TYPESCRIPT, # ... "kotlin": KOTLIN,}Submit a PR with that change plus a test in tests/test_languages.py. That is the whole process. No plugin system, no separate package, no registration ceremony.
Polyglot repos
Section titled “Polyglot repos”Cascade is single-language-per-run by default. If your repo has both a Python backend and a TypeScript frontend, and you want Cascade to work on either, you have two options:
- Per-command override. Run
cascade build stories/backend.yaml --language pythonfor backend stories and--language typescriptfor frontend ones. - Per-repo split. Treat the two languages as separate Cascade projects with two
cascade.yamlfiles in different subdirectories. Run Cascade from the appropriate subdirectory.
We may add per-story language detection in a future release. Right now, simpler-is-better.
What is next
Section titled “What is next”- Pipeline: the stages every language goes through
- CLI reference: every command and flag
- Configuration: the
cascade.yamlschema