Skip to content
projectdetect

pure go · no cgo · fs-based

Detect what kind of project a directory is.

A tiny Go library that answers two questions over a filesystem: what project type(s) does this directory look like, and what project roots live under a tree.

MIT licensed · zero cgo · CEL support is opt-in

detect.go
// what project type(s) is this directory?
for _, m := range projectdetect.Detect(fsys, ".") {
    fmt.Printf("%s via %s\n", m.Type, m.Indicator)
}

// go        via go.mod
// node      via package.json
// docker-compose via docker-compose.yml

what you get

Filesystem-first project detection.

Match on indicators — an exact filename, a basename glob, a subdirectory marker, or an optional CEL expression over the directory's files and subdirs.

Detect & Find

Detect(fsys, dir) reports every type this directory matches — a directory can be several at once. Find(ctx, root, opts) walks a tree and returns every project root under it.

28 built-in types

Go, Node, Rust, Python, Ruby, Maven/Gradle, .NET, Terraform, Compose — plus Swift, PHP, Scala, CMake, Zig and eight static-site generators. Each declares its build-artefact dirs too.

Per-file language

A separate axis: LanguageForPath("src/app.py")"python". A 1:1 extension→language map across 17 recognised languages, C++'s dozen spellings included.

Skip vendored & minified

IsVendored("docs/js/jquery.min.js") catches third-party content anywhere — patterns from GitHub Linguist. IsMinified(content) judges packed bundles by shape.

Custom types in YAML

Load extra project types from YAML — has_file, has_glob, has_subdir_glob, or a cel expression. Register your own stacks into the default registry.

CEL is opt-in

The base package has no CEL dependency. Add one blank import to enable cel: indicators — otherwise cel-go and its transitive deps stay out of your build.

coverage

Built-in project types.

28 types out of the box — languages, build tools, and static-site generators. Extend the set with your own via YAML.

  • go
  • node
  • rust
  • python
  • ruby
  • java-maven
  • java-gradle
  • dotnet
  • terraform
  • docker-compose
  • swift
  • php
  • scala-sbt
  • scala-mill
  • cmake
  • autotools
  • r
  • zig
  • perl
  • matlab
  • hugo
  • jekyll
  • eleventy
  • astro
  • gatsby
  • mkdocs
  • docusaurus
  • pelican

Recognised file languages: go · python · javascript · typescript · java · rust · c · cpp · csharp · kotlin · php · ruby · scala · r · matlab · perl · swift

usage

Two questions, two functions.

Detect the current directory's types, or walk a tree for every project root under it.

// What is *this* directory?
for _, m := range projectdetect.Detect(
        os.DirFS("."), ".") {
    fmt.Printf("%s (via %s)\n",
        m.Type, m.Indicator)
}
// Every project root under a tree.
res, err := projectdetect.Find(
        ctx, "/path/to/code",
        projectdetect.FindOptions{})
if err != nil {
    log.Fatal(err)
}
# Per-file language detection.
projectdetect.LanguageForPath("src/app.py") // "python"
projectdetect.LanguageForExt(".rs")         // "rust"
projectdetect.IsVendored("docs/js/jquery.min.js") // true
# Custom types in YAML.
project_types:
  - name: my-stack
    indicators:
      - has_file: "my.config"
      - cel: '"services" in subdirs'

install

Add it to your module.

One dependency-light import. CEL support ships in a sub-package you enable only if you need it.

go get go get github.com/richardwooding/projectdetect
import import "github.com/richardwooding/projectdetect"
cel import _ "github.com/richardwooding/projectdetect/celindicators"

Requires Go 1.26+. The CEL import is only needed for cel: indicators.