Calq Dev

Calq Dev is a durable workflow automation tool. Designed for failure-proof local development automation without manual tool orchestration.

Comparison

Project Scaffolding & Automation

Feature Calq Dev Project Scaffolders Task Runners Monorepo Orchestrators Shell Scripts
Crash recovery (resume after failure) ✅ durable per step ⚠️ input-hash cache hits only
End-to-end project setup (scaffold + CI + repo) ✅ single command ⚠️ scaffold only ❌ user-composed tasks ⚠️ workspace init only ⚠️ manual scripting
Organization-wide consistency ✅ clones shared repos (.github, .license) ⚠️ template repos ⚠️ generators ⚠️ manual setup
Multi-language format pipeline ✅ guard-based step skipping ❌ not a formatter ⚠️ user-defined tasks ⚠️ per-project config ⚠️ manual conditionals
Configuration portability (presets, Codespaces) ✅ named presets + auto dotfiles ⚠️ manual dotfiles
Configurable without code changes ✅ JSON config ⚠️ template editing ⚠️ Makefile/Justfile editing ⚠️ config files ❌ script editing
Custom project types ✅ JSON-defined command lists ✅ custom generators ✅ user-defined tasks ✅ workspace generators ✅ any script
Ecosystem / language reach ⚠️ .NET-centric defaults ✅ language-agnostic ✅ language-agnostic ✅ language-agnostic ✅ any tool

Git Workflow

Feature Calq Dev Git Flow Tools GitHub CLI + Scripts Git Hooks IDE Git Integrations
Crash recovery (resume after failure) ✅ durable per step
Issue-to-branch-to-PR in single commands ✅ built-in (switch, push, merge) ⚠️ branch only ⚠️ multi-command ⚠️ varies by IDE
Compound operations (merge + close + cleanup) ✅ single command ⚠️ manual steps ⚠️ multi-command ⚠️ varies by IDE
Stash-safe branch switching ✅ automatic stash/restore ✅ some IDEs
Configurable workflow (strategy, PR format) ✅ JSON presets + dotfiles sync ⚠️ opinionated ⚠️ manual flags ✅ settings-based
Shell completion (issue numbers) ✅ built-in gh completions ✅ built-in
No dedicated infrastructure ✅ git + gh CLI only ✅ git only ✅ gh + git ✅ git hooks ✅ IDE-native

Table of Contents

Usage - Calq Dev

1. Foundations

1.1 Installation

Calq Dev is distributed as a .NET global tool.

dotnet tool install -g CalqFramework.Dev.Cli

Verify installation:

dev --help

Key points:

  • Requires dotnet, git, and gh CLI on the system PATH
  • All subcommands are available immediately after installation

1.2 Configuration directory

Configuration files are stored in a single directory and created automatically on first use with sensible defaults.

  • Standard: {AppData}/dev (e.g., ~/.config/dev on Linux, %APPDATA%/dev on Windows)
  • Codespaces: ~/dotfiles/.config/dev (see 1.3 Codespaces support)

Key points:

  • No manual setup required — all configuration files are generated with defaults on first access
  • Files follow the naming convention {FullTypeName}.{preset}.json

1.3 Codespaces support

When the CODESPACES environment variable is detected, the configuration directory resolves to ~/dotfiles/.config/dev/ instead of the standard platform path.

Key points:

  • Detection is automatic — no user intervention required
  • Enables persistent configuration across ephemeral Codespaces instances via the dotfiles repository

1.4 Preflight checks

Before executing a multi-step pipeline, Calq Dev verifies that all required external tools are available on PATH.

Key points:

  • Applies to dev new (checks dotnet, gh, git) and dev format (checks tools referenced in pipeline steps)
  • Failures are reported before any work begins — no partial execution from missing dependencies

See also: 1.1 Installation

1.5 Crash recovery

Calq Dev is built on Calq CMD, a durable execution framework. All multi-step operations persist progress after each completed step. If a command is interrupted or fails mid-execution, re-running the same command resumes from the last completed step.

Key points:

  • Applies to all pipelines: scaffolding, formatting, and git workflow commands
  • No manual cleanup required after interruption — re-run the command to continue
  • Completed steps are not re-executed on resume

2. Configuration & Customization

2.1 Preset switching

All configuration POCOs are grouped under a workflow preset. Switching the preset reloads all subcommand configurations from the new preset's JSON files.

# Print current preset
dev config preset

# Switch to a different preset
dev config preset my-workflow

Key points:

  • All config POCOs use [PresetGroup("Workflow")] — switching the master preset's Workflow value cascades to all of them
  • Config files follow the naming convention {FullTypeName}.{preset}.json
  • Enables maintaining separate configurations for different workflows (e.g., personal projects vs team projects)

See also: 1.2 Configuration directory

2.2 Setting individual values

dev config set modifies a single configuration value by class name and dot-separated property path.

dev config set NewConfig Organization my-org
dev config set PushConfig MainBranch develop
dev config set MergeConfig MergeStrategy rebase
dev config set FormatConfig Steps.0.Command "dotnet build {dir}"

Print config directory path:

dev config path

Key points:

  • The first argument is the config class name, the second is the property path, and the third is the value
  • Changes are persisted to the JSON file immediately
  • Edit JSON files directly as an alternative to dev config set

See also: 1.2 Configuration directory, 2.1 Preset switching

2.3 Dotfiles sync

Sync configuration with a dotfiles repository for portability across machines and Codespaces instances.

# Push local config to dotfiles repo
dev config push

# Pull config from dotfiles repo
dev config pull

Key points:

  • push copies config files to ~/dotfiles/.config/dev/, commits, and pushes
  • pull copies from ~/dotfiles/.config/dev/ to the local config directory
  • Enables consistent configuration across ephemeral environments

See also: 1.3 Codespaces support, 1.2 Configuration directory

2.4 PipelineStep model

All configurable pipelines (format, pull, relock, issues) share the same step model.

Property Type Description
Command string Shell command. Supports {dir} and {target} placeholders.
FilePattern string? Glob pattern — step is skipped if no matching files exist.
TargetPatterns list of list of string? Ordered glob-pattern groups for target discovery. First group yielding exactly one file wins; otherwise all results are combined. When null, command runs once with {dir} only.

Key points:

  • FilePattern acts as a guard: if no files match the pattern in the target directory, the step is skipped entirely
  • TargetPatterns enables per-file execution: the step runs once for each discovered target, substituting {target} in the command
  • When TargetPatterns is null, the command executes once with only {dir} available as a placeholder

2.5 Naming conventions

The project name follows the Organization.Project convention.

dev new classlib CalqFramework.Something

Key points:

  • The part after the first dot is kebab-cased into the project subdirectory name (e.g., CalqFramework.Something creates a something/ directory)
  • Projects and solutions use the full name (e.g., CalqFramework.Something.csproj, CalqFramework.Something.sln)

2.6 Custom project types

Edit NewConfig to define custom project types, modify the scaffolding steps, or customize .csproj injection rules.

Add a custom project type:

dev config set NewConfig ProjectTypes.webapp "[\"dotnet new webapp -n {projectFullName} -o {projectFullName}\", \"dotnet new sln -n {projectFullName}\", \"dotnet sln add {projectFullName}\"]"

Or edit the JSON file directly at {configDir}/CalqFramework.Dev.Config.NewConfig.{preset}.json:

{
  "ProjectTypes": {
    "classlib": ["dotnet new classlib -n {projectFullName} -o {projectFullName} {langFlag}", "..."],
    "webapp": ["dotnet new webapp -n {projectFullName} -o {projectFullName}", "dotnet new sln -n {projectFullName}", "dotnet sln add {projectFullName}"]
  }
}

Available template placeholders:

Placeholder Value
{organization} GitHub organization
{projectFullName} Full project name (e.g., CalqFramework.Dev)
{kebabName} Kebab-case name (e.g., dev)
{name} Name suffix after first dot (e.g., Dev)
{langFlag} -lang "F#" or empty
{initialVersion} Initial version string
{visibility} --public, --private, or --internal
{ghRepoFlags} Additional gh repo create flags
{dir} Current directory (.)
{tempDir} Temporary directory for optional clone steps

Customize .csproj injection:

{
  "ProjXml": {
    "classlib": {
      "PackageId": "{projectFullName}",
      "Version": "{initialVersion}",
      "Authors": "My Team"
    },
    "webapp": {
      "Version": "{initialVersion}"
    }
  }
}

See also: 2.2 Setting individual values, 2.5 Naming conventions

2.7 Custom format pipeline

Edit FormatConfig to replace or extend the formatting steps.

Example: minimal pipeline (dotnet format only):

{
  "Steps": [
    { "Command": "dotnet build --no-restore {target}", "TargetPatterns": [["*.sln", "*.slnx"], ["*.*proj"]] },
    { "Command": "dotnet format {target} --severity info", "TargetPatterns": [["*.sln", "*.slnx"], ["*.*proj"]] },
    { "Command": "dotnet build --no-restore {target}", "TargetPatterns": [["*.sln", "*.slnx"], ["*.*proj"]] }
  ]
}

Example: add a custom linter step:

{
  "Steps": [
    { "Command": "dotnet build --no-restore {target}", "TargetPatterns": [["*.sln", "*.slnx"], ["*.*proj"]] },
    { "Command": "my-custom-linter {dir}", "FilePattern": "*.cs" },
    { "Command": "dotnet format {target} --severity info", "TargetPatterns": [["*.sln", "*.slnx"], ["*.*proj"]] },
    { "Command": "dotnet build --no-restore {target}", "TargetPatterns": [["*.sln", "*.slnx"], ["*.*proj"]] }
  ]
}

Example: multi-language pipeline (Rust + .NET):

{
  "Steps": [
    { "Command": "dotnet build --no-restore {target}", "TargetPatterns": [["*.sln", "*.slnx"], ["*.*proj"]], "FilePattern": "*.csproj" },
    { "Command": "dotnet format {target} --severity info", "TargetPatterns": [["*.sln", "*.slnx"], ["*.*proj"]], "FilePattern": "*.csproj" },
    { "Command": "cargo fmt --manifest-path {target}", "TargetPatterns": [["Cargo.toml"]], "FilePattern": "Cargo.toml" }
  ]
}

See also: 2.4 PipelineStep model, 1.4 Preflight checks

2.8 Custom git workflow

Each git workflow subcommand is driven by its own configuration POCO.

SwitchConfig:

{
  "BranchPrefix": "issues/",
  "AutoCreateIssue": true
}

PushConfig:

{
  "Remote": "origin",
  "MainBranch": "main",
  "ForceWithLeaseOnFeature": true,
  "CreatePr": true,
  "PrTitleFormat": "(#{IssueID}) {IssueTitle}"
}

MergeConfig:

{
  "MergeStrategy": "squash",
  "DeleteBranch": true,
  "CloseIssue": true,
  "PullAfterMerge": true
}

UtilityConfig (pull, relock, issues):

{
  "Pull": [{ "Command": "git pull --rebase --autostash origin main" }],
  "Relock": [{ "Command": "dotnet restore --no-cache --force-evaluate --use-lock-file" }],
  "Issues": [{ "Command": "gh issue list --limit 20 --search \"is:open no:assignee sort:created-desc\"" }],
  "IssueCompletionCommand": "gh issue list --limit 20 --json number --jq \".[].number\""
}

Key points:

  • All utility commands use the same PipelineStep model as FormatConfig
  • Edit via dev config set or directly in the JSON files

See also: 2.4 PipelineStep model


3. Project Scaffolding

3.1 Scaffolding pipeline

dev new executes a full scaffolding pipeline from a single command: creates projects, solutions, references, injects .csproj metadata, seeds GitHub workflow templates, and optionally creates a GitHub repository.

# Scaffold a classlib with test project
dev new classlib CalqFramework.Foo

# Scaffold a console app
dev new console CalqFramework.Bar

# Scaffold a tool (library + CLI + tests)
dev new tool CalqFramework.Baz

# Create a public GitHub repo during scaffolding
dev new classlib CalqFramework.Foo --public

# Override language and organization
dev new classlib CalqFramework.Foo --lang "F#" --organization my-org

What new does automatically:

  1. Clones .github and .license repos from the organization (optional — warns on failure)
  2. Runs dotnet new for each project in the template
  3. Creates the solution and adds project references
  4. Injects .csproj XML properties (PackageId, Version, RootNamespace, etc.)
  5. Seeds .github/workflows/ from workflow-templates/ if present
  6. Optionally runs git init + gh repo create + initial push

Key points:

  • Project types are fully configurable — add your own via NewConfig.ProjectTypes
  • .csproj injection is template-aware: different XML properties for classlib vs console vs xunit

See also: 2.6 Custom project types, 1.5 Crash recovery, 2.5 Naming conventions

3.2 Built-in project types

Type Projects Created
classlib Library + xUnit test project + solution
console Console app + solution
tool Library + CLI console + xUnit test project + solution

Key points:

  • Each type defines an ordered list of shell commands executed during scaffolding
  • All types support the --lang flag for language override

See also: 2.6 Custom project types, 3.1 Scaffolding pipeline

3.3 Organization repo cloning

The first step of the scaffolding pipeline clones .github and .license repositories from the configured organization into the new project directory.

Key points:

  • Provides shared editor configuration, workflow templates, and license files
  • Cloning is optional — the pipeline continues with a warning if the repositories do not exist
  • The organization is configurable via NewConfig.Organization

See also: 2.6 Custom project types

3.4 Workflow template seeding

If the cloned .github repository contains a workflow-templates/ directory, those templates are copied into the new project's .github/workflows/ directory.

Key points:

  • Enables consistent CI/CD configuration across all projects in an organization
  • Templates are copied once during scaffolding — subsequent changes to the templates do not propagate automatically

See also: 3.3 Organization repo cloning

3.5 GitHub repository creation

When the --public, --private, or --internal flag is passed, dev new creates a GitHub repository after scaffolding completes.

dev new classlib CalqFramework.Foo --public

Key points:

  • Runs git init, gh repo create, and pushes the initial commit
  • Visibility defaults to private when no flag is specified and repository creation is triggered
  • Repository creation is skipped entirely when no visibility flag is provided

See also: 2.6 Custom project types


4. Code Formatting

4.1 Format pipeline

dev format executes a multi-step formatting pipeline. Each step is a shell command with optional file-pattern guards and per-target execution.

# Format current directory
dev format

# Format a specific directory
dev format --dir /path/to/project

Default pipeline:

Step Command Condition
1 dotnet build --no-restore {target} Targets: *.sln, *.slnx*.*proj
2 jb cleanupcode {dir} --profile="Built-in: Full Cleanup" Only if *.csproj files exist
3 fantomas {dir} Only if *.fsproj files exist
4 dotnet format {target} --verbosity diag --severity info Targets: *.sln, *.slnx*.*proj
5 dotnet build --no-restore {target} Targets: *.sln, *.slnx*.*proj

Key points:

  • Steps with FilePattern are skipped when no matching files exist in the target directory
  • Steps with TargetPatterns run once per discovered target file, substituting {target}. Patterns are grouped by priority: the first group that yields exactly one file wins; otherwise all results are combined.
  • Placeholders: {dir} = target directory, {target} = individual discovered target file
  • The entire pipeline is configurable via FormatConfig

See also: 2.7 Custom format pipeline, 2.4 PipelineStep model, 1.4 Preflight checks

4.2 Multi-language support

The format pipeline supports multiple languages in a single repository by combining FilePattern guards with language-specific commands.

# A repository with both .NET and Rust code uses separate steps for each
dev format

Key points:

  • Each step can target a different language via its FilePattern (e.g., *.csproj for .NET, Cargo.toml for Rust)
  • Steps for languages not present in the target directory are automatically skipped

See also: 2.7 Custom format pipeline, 2.4 PipelineStep model


5. Git Workflow

5.1 Issue listing

dev issues lists open issues from GitHub.

dev issues

Default behavior: gh issue list --limit 20 --search "is:open no:assignee sort:created-desc"

Key points:

  • The command is configurable via UtilityConfig.Issues
  • IssueCompletionCommand provides issue number completion for shell auto-complete

See also: 2.8 Custom git workflow, 2.4 PipelineStep model

5.2 Issue-driven branching

dev switch creates or switches to an issue branch. Pass an issue number to branch from an existing issue, or a title to create a new issue automatically.

# Switch to branch issues/42
dev switch 42

# Create a new issue titled "Add logging" and switch to its branch
dev switch "Add logging"

Key points:

  • Branch naming: {BranchPrefix}{issueId} (default prefix: issues/)
  • When AutoCreateIssue is enabled (default), passing a string creates a GitHub issue via gh and branches from its ID
  • Uses stash-apply safety: dirty working tree is stashed before switching and restored after

See also: 2.8 Custom git workflow, 5.1 Issue listing

5.3 Push

dev push pushes to remote with branch-aware behavior.

dev push

On main branch:

  • Pulls with rebase first, then pushes to origin

On feature branch:

  • Pulls with rebase first
  • Pushes with --force-with-lease (configurable)
  • Auto-creates a PR linking the issue from the branch name (configurable)
  • PR title format: (#42) Issue Title (configurable via PrTitleFormat)

Key points:

  • Force-with-lease prevents overwriting remote commits not present locally
  • PR creation and title format are individually configurable

See also: 2.8 Custom git workflow, 5.2 Issue-driven branching

5.4 Merge

dev merge squash-merges the current feature branch PR and performs full cleanup.

dev merge

What merge does automatically:

  1. Pushes the current branch (calls push internally)
  2. Squash-merges the PR via gh pr merge --squash --delete-branch
  3. Closes the linked issue (if branch name contains an issue ID)
  4. Switches to main and deletes the local feature branch
  5. Pulls latest main

Key points:

  • Cannot be run from main branch
  • Merge strategy is configurable (squash, merge, rebase)
  • Each step (delete branch, close issue, pull after merge) is individually toggleable

See also: 2.8 Custom git workflow, 5.3 Push, 5.2 Issue-driven branching

5.5 Pull

dev pull pulls from remote with rebase, regardless of current branch.

dev pull

Default behavior: git pull --rebase --autostash origin main

Key points:

  • The pull command is configurable via UtilityConfig.Pull using the same PipelineStep model as the format pipeline

See also: 2.4 PipelineStep model

5.6 Dependency relocking

dev relock forces re-evaluation of the dependency graph and updates lock files.

dev relock

Default behavior: dotnet restore --no-cache --force-evaluate --use-lock-file

Key points:

  • The command is configurable via UtilityConfig.Relock

See also: 2.8 Custom git workflow, 2.4 PipelineStep model

Quick Start

dotnet tool install -g CalqFramework.Dev.Cli
dev --help

Scaffold a project:

dev new classlib MyOrg.MyLib

Format code:

dev format

Issue-driven workflow:

dev issues              # list open issues
dev switch 42          # create branch issues/42
# ... make changes ...
dev push               # push + auto-create PR
dev merge              # squash-merge + cleanup

License

Calq Dev is licensed under the PolyForm Strict License with a Commercial Use Grant.

An unhandled error has occurred. Reload 🗙