project is a Go CLI scaffolding tool that creates new projects from embedded templates. All templates are compiled into a single binary, so there are no external template files to ship or locate at runtime.
The CLI currently ships with these subcommands:
| Command | Purpose |
|---|---|
project new [project_name] |
Scaffold a new project in a fresh target directory |
project init [target_dir] |
Scaffold a project into the current directory or an explicit target directory |
project list |
List supported template languages |
project inspect <lang> |
Inspect one embedded template and its render/copy behavior |
project version |
Print the build version |
project completion <shell> |
Generate shell completion scripts |
- Go — structured CLI scaffold with
cmd/<projectname-lower>,internal/, tests,.golangci.yml, GitHub Actions CI, GoReleaser config, Codecov config,typos.toml,.editorconfig, Dependabot config, contributor docs,README.md, andjustfile - C++ —
CMakeLists.txt,src/main.cc,include/,dev-tools/, GitHub Actions CI,typos.toml,.editorconfig, Dependabot config,CONTRIBUTING.md,README.md, andjustfile - Rust —
Cargo.toml,src/lib.rs,src/main.rs,.cargo/config.toml,rustfmt.toml,clippy.toml,dprint.json,typos.toml,.editorconfig, CI workflow, Dependabot config,CONTRIBUTING.md,SECURITY.md,.env.example,README.md, andjustfile
Run project list to see all available languages.
Requires Go 1.25+.
# Install directly from GitHub
go install github.com/JackDrogon/project/cmd/project@latestRequires just.
git clone https://github.com/JackDrogon/project.git
cd project
# Build to ./bin/project
just build
# Or install to $GOPATH/bin directly
just installIf you are new to the CLI, this is the shortest path from installation to a generated project:
# 1) See which templates are available
project list
# 2) Inspect one template before generating anything
project inspect go
# 3a) Create a fresh project directory
project new -l go myapp
# 3b) Or scaffold into a target path
project init -l go nested/myappTypical flow:
- Run
project listto choose a language template. - Run
project inspect <lang>to see which files are rendered or copied. - Use
project newwhen you want a brand-new project directory. - Use
project initwhen you want to scaffold into the current empty directory or another target directory that does not exist yet or is already empty.
If you want to preview file operations first, add --dry-run to either project new or project init.
project supports a persistent configuration file to set default flags and template inputs across all commands.
By default, project looks for a configuration file at ${UserConfigDir()}/project/config.toml. The resolved path depends on your operating system:
- Linux/macOS:
~/.config/project/config.toml(or$XDG_CONFIG_HOME/project/config.toml) - Windows:
%AppData%\project\config.toml
You can bypass this discovery and use a specific file with the --config <path> flag.
The configuration file uses TOML format and must include a version field (current version: 1).
version = 1
[new]
lang = "go"
project_name = "myapp"
module = "github.com/user/myapp"
git_mode = "init+commit"
signoff = true
[new.inputs]
go_version = "1.22"
[init]
lang = "rust"
target_dir = "./my-project"
git_mode = "none"
[init.inputs]
author = "custom-author"
[list]
detail = true
sort = "governance"
min_governance = "basic"
[inspect]
format = "toml"
mode = "render"
[version]
verbose = true
[completion]
shell = "zsh"Schema Rules:
- Exclusions: One-shot flags like
--force,--dry-run,--replay, and--write-replaycannot be set in the global config. - Section Match: Settings under
[new]only apply toproject new, and[init]settings only apply toproject init.
project uses two distinct precedence models depending on whether --replay is used.
When not using --replay, the precedence is (lowest to highest):
- Built-in Defaults: Hardcoded fallback values.
- Active Config: Values from
${UserConfigDir()}/project/config.tomlor the file specified by--config. - Explicit CLI Flags/Args: Flags like
--lang,--set, and positional arguments passed directly.
When using --replay, the global configuration is completely ignored. The precedence is (lowest to highest):
- Replay File: Values loaded from the TOML replay file.
- Explicit CLI Flags/Args: Explicit overrides for supported flags.
Boundary Note: --config and --replay are mutually exclusive and cannot be combined. When a replay is active, global configuration logic is bypassed to ensure the replay file remains the single source of truth for project recreation.
# Use a custom config file instead of the default discovery
project list --config ./my-config.toml
# Scaffold a new project using defaults from config
project new myapp
# Scaffold into a directory defined in [init.target_dir]
project init
# Override a config value for a single run
project list --detail=false
# Inspect a template using config-defined format and mode
project inspect go
# Generate completion for a shell defined in [completion.shell]
project completion
# View build metadata with config-defined verbosity
project versionUse --explain-config to see how project resolved its configuration. This prints the active config path and a summary of sources to stderr:
project new myapp --explain-config
# Output on stderr:
# Active config: /home/user/.config/project/config.toml (Source: user-config)
# ...project new -l go myapp
project new -l go github.com/myorg/myapp
project new -l cpp myapp
project new -l rust myappFor Go projects, you can pass either a local project name or a full module path as the positional argument. When you pass a module path directly, project derives the output directory from the repository name: project new -l go github.com/myorg/myapp creates ./myapp with module github.com/myorg/myapp, and project new -l go github.com/myorg/myapp/v2 still creates ./myapp with module github.com/myorg/myapp/v2.
This will:
- Copy template files into the
myapp/directory - Render template variables (e.g., project name, module path) in
.tmplfiles - Run git setup based on
--gitmode (defaultinit+commit)
Use init when you want the project name to come from the directory you are working in, or when you want to scaffold into a specific target path. The destination must either not exist yet or already exist as an empty directory.
# Initialize in the current directory
project init -l go
# Initialize in a nested target directory
project init -l go nested/myapp
# Initialize a C++ starter without git setup
project init -l cpp --git none demo-cppLike new, init supports --module, --git, --dry-run, --replay, --write-replay, and repeated --set key=value overrides.
| Flag | Short | Description |
|---|---|---|
--config |
Path to CLI config file (replaces discovered user config) | |
--explain-config |
Explain resolved config sources on stderr | |
--lang |
-l |
Programming language (required unless provided via config or --replay) |
--module |
-m |
Module path, e.g., github.com/user/project (defaults to project name, or to the positional Go module path when you pass one directly) |
--force |
new only: remove and recreate an existing project directory |
|
--git |
Git workflow: none, init-only, init+commit |
|
--signoff |
Add Signed-off-by trailer to the initial commit |
|
--dry-run |
-n |
Preview files without creating them |
--replay |
Load project configuration from a TOML replay file | |
--write-replay |
Write resolved replay to a TOML file after success | |
--set |
Set a template-specific input value (--set key=value) |
|
--no-git |
Deprecated alias for --git none |
Notes:
--configand--replayare mutually exclusive and cannot be combined--write-replaycannot be combined with--dry-run--signoffis only valid when the git workflow creates an initial commit--no-gitis deprecated; prefer--git none--explain-configoutput is written to stderr only
# Create a Go project with a custom module path
project new -l go myapp -m github.com/myorg/myapp
# Create a project using a replay file
project new --replay .project-replay.toml
# Override replay values with CLI flags
project new --replay .project-replay.toml --set go_version=1.22
# Save resolved replay to a file
project new -l go myapp --write-replay .project-replay.toml
# Preview what files would be created
project new -l go myapp -n
# Preview an init workflow without touching the filesystem
project init -l go --git none -n nested/myapp
# Create a Rust starter
project new -l rust myappThe --dry-run (-n) flag provides a detailed preview of the resolved inputs and the planned file operations:
Creating project with language: go, project name: myapp
Dry-run mode: no files will be created
template: go
description: Production-ready Go CLI starter
target_dir: myapp
resolved inputs:
project_name: myapp
module_path: myapp
go_version: 1.21
git_mode: init+commit
explicit overrides:
(none)
actions:
create myapp/.github/
copy go/.github/workflows/ci.yml -> myapp/.github/workflows/ci.yml
render go/README.md.tmpl -> myapp/README.md
...
Templates may include a .project-template-manifest.toml manifest at their root to define metadata and input variables. This is a reserved file and is never copied to the target project. The current schema version is 2.
Example manifest:
version = 2
name = "go"
description = "Production-ready Go CLI starter"
[[inputs]]
key = "go_version"
template_var = "GoVersion"
required = trueUsing --write-replay produces a TOML file containing the final resolved configuration. This file enables consistent project recreation and can be used with --replay.
Contract Rules:
- Precedence: Explicit CLI flags and
--setarguments always take precedence over values in a replay file. - Derived Defaults: Runtime-derived values (like the current
yearor systemauthor) are not persisted in the replay file unless they were explicitly provided via CLI or replay. - Command Match: A replay captured from
newmust be replayed throughproject new, and a replay captured frominitmust be replayed throughproject init.
The following third-party configuration files are explicitly supported and included in generated templates:
go/.github/workflows/ci.ymlgo/codecov.ymlgo/.golangci.ymlgo/.goreleaser.yml.tmplgo/.github/dependabot.ymlcpp/.github/workflows/ci.ymlcpp/.github/dependabot.ymlrust/.github/workflows/ci.ymlrust/.github/dependabot.yml
Templates (.tmpl files) support the following variables via Go's text/template:
| Variable | Description | Default |
|---|---|---|
{{.ProjectName}} |
Name passed to project new, or the derived directory name from a direct Go module path argument |
— |
{{.ModulePath}} |
From --module flag or a direct Go module path argument |
Same as ProjectName |
{{.Author}} |
System username | "author" |
{{.Year}} |
Current year | — |
Only files ending in .tmpl are rendered, and the suffix is stripped (e.g., go.mod.tmpl → go.mod). Non-.tmpl files are copied as-is. Invalid .tmpl syntax returns an error.
- In structured
list --detailandinspectoutput,description,manifest_version,input_names, andinputscome from.project-template-manifest.toml, whilevariables,file_count,template_count, andfilesare derived from inspecting the embedded template tree. project listprints available language names.project list --detailprints file count, template count, and template variables per language.project list --tomlprints machine-readable TOML output.project inspect <lang>shows per-file mappings (source -> output) and whether each file is rendered or copied.project inspect <lang> --mode render|copyfilters files by render/copy behavior.project inspect <lang> --tomlprints structured output.- Architecture notes for the CLI catalog and create pipelines live in
docs/architecture/catalog-query-and-rendering.md.
Examples:
# Plain language list
project list
# Detailed summary with manifest metadata
project list --detail
# Machine-readable output
project list --toml
# Inspect one template
project inspect go
# Show only rendered files
project inspect go --mode render
# Structured inspection output
project inspect go --tomlproject version prints the human-readable build version. Use project version --verbose to include detailed fields such as the tag, revision, and dirty state.
Generate shell completion scripts with project completion <shell>:
# Current session
source <(project completion bash)
# Persistent (Linux)
project completion bash > /etc/bash_completion.d/project
# Persistent (macOS with Homebrew)
project completion bash > $(brew --prefix)/etc/bash_completion.d/project# Enable completion if not already
echo "autoload -U compinit; compinit" >> ~/.zshrc
# Install completion
project completion zsh > "${fpath[1]}/_project"
# Start a new shell to take effect# Current session
project completion fish | source
# Persistent
project completion fish > ~/.config/fish/completions/project.fish# Current session
project completion powershell | Out-String | Invoke-Expression
# Persistent — add to your PowerShell profile
project completion powershell > project.ps1just build # Build binary
just test # Run all tests
just test-v # Run tests with verbose output
just lint # Run golangci-lint
just fmt # Format code
just spellcheck # Run typos across the repository
just cover # Generate coverage report (coverage.html)
just pre-commit # generate → fmt → lint → test → spellcheck
just run <args> # Build and run (e.g., just run new -l go myapp)
just tidy # go mod tidyThis repository includes a few repo-level governance files inspired by the generated templates:
.editorconfigfor consistent editor defaults.github/dependabot.ymlfor dependency update automationtypos.tomlfor repository-wide spelling checksCONTRIBUTING.mdfor contributor workflow expectations
To run just spellcheck or just pre-commit locally, install typos first.
- Create a directory under
internal/adapters/templatesrc/with the language name (e.g.,internal/adapters/templatesrc/rust/) - Add template files; use
.tmplsuffix for files that need variable substitution - Update the
//go:embeddirective ininternal/adapters/templatesrc/embed.goto include the new directory://go:embed all:cpp all:go all:rust var FS embed.FS
Project names must:
- Start with a letter (
a-z,A-Z) - Contain only letters, digits,
.,_, or- - Be at most 255 characters