Reference

Starter Packs

How explicit ota init --pack ... works, when to use each pack, and what the built-in packs really seed.

referenceautomation buildersintermediatestable2026-05-30

When to use this page

Use this page when the question is not whether ota init exists, but which explicit starter pack should seed the first draft.

Starter packs keep the pack-led path explicit and review-first. They do not replace detector-led init, and they do not silently switch based on repo signals.

  • use plain ota init --dry-run when ota should shape the first draft from repo signals
  • use ota init --packs when you want to compare the built-in conventional starters first
  • use ota init --pack <name> --dry-run when you want one explicit starter path you will review before writing
  • keep --pack authoritative; advisories exist to warn, not to auto-switch the chosen pack

Review-first flow

The safe path is compare, preview, then write.

Start with the catalog when you are choosing a pack. Start with detector-led init when you want ota to infer the first draft instead.

Detector-led preview now also calls out the Agent boundary outcome directly, so a missing agent block is explained as Inferred, Partially inferred, or Omitted instead of being left implicit in the YAML.

Pack-led pathbash
ota init --packsota init --pack node --dry-runota init --pack python --dry-runota init --pack ruby --dry-runota init --pack go --dry-runota init --pack rust --dry-runota init --pack dotnet --dry-runota init --pack php-composer --dry-runota init --pack java-maven --dry-runota init --pack java-gradle --dry-run
Detector-led pathbash
ota init --dry-runota init

How to compare packs

Do not choose by language label alone.

Compare the starter by the exact loop it seeds, the few conventional knobs it allows, and the boundary it deliberately leaves for review.

  • seeded loop: start with the pack whose setup, build, dev, or test path already matches the repo's normal first draft
  • explicit knobs: use Node or Python pack knobs only when the package-manager or test-runner boundary is real and known up front
  • does-not-infer boundary: prefer the pack whose deliberate non-goals still leave the least repo-specific editing behind
  • when two packs are plausible, preview both with --dry-run instead of stretching one pack past its declared boundary

Available packs

These are the built-in conventional starters today.

Choose the pack whose execution boundary already matches the repo instead of picking the closest language label and hoping the rest works out later.

node

Seeds toolchain-owned Node (toolchains.node) plus setup, dev, and test around one explicit package manager. Knob: --package-manager npm|pnpm|yarn|bun. Does not infer custom script names or package manager unless you choose it.

When to use it

Use this when the repo is intentionally Node-first and the package-manager boundary is already known.

Example

python

Seeds toolchain-owned Python (toolchains.python via uv), uv-based setup (uv sync), and uv-run test. Knob: --test-runner pytest|unittest. Does not infer repo-specific dependency-group layout or repo-specific test layout for you.

When to use it

Use this when the repo is intentionally Python-first and the test entrypoint should be explicit from the first draft.

Example

ruby

Seeds toolchain-owned Ruby (toolchains.ruby) plus Bundler setup (bundle install) and test (bundle exec rake test), and seeds Bundler version governance under toolchains.ruby.package_managers.bundler. No starter knobs. Does not infer framework-specific server commands or repo-specific test wrappers/flags.

When to use it

Use this when the repo is intentionally Ruby/Bundler-first and the standard Bundler setup/test loop is already the honest first draft.

Example

go

Seeds go mod download, go build ./..., and go test ./.... No starter knobs. Does not infer workspace layout, code generation, or custom build flags.

When to use it

Use this when the standard Go module loop is already the correct first draft and extra repo-specific flow can stay a review edit.

Example

rust

Seeds toolchain-owned Rust (toolchains.rust) plus Cargo fetch, build, and test. No starter knobs. Does not infer workspace members, feature flags, or custom cargo aliases.

When to use it

Use this when the standard Cargo loop is already the correct first draft and the repo does not need a more opinionated wrapper.

Example

dotnet

Seeds dotnet restore, dotnet build, and dotnet test. No starter knobs. Does not infer solution-specific target selection, test filtering, or custom dotnet CLI flags.

When to use it

Use this when the standard .NET restore/build/test loop is already the correct first draft and the repo does not need a more specialized solution-level template.

Example

php-composer

Seeds composer install and reuses composer run test only when the repo already declares scripts.test. No starter knobs. Does not infer framework-specific entrypoints, web server commands, or another PHP test wrapper for you.

When to use it

Use this when the repo is intentionally Composer-managed PHP and the explicit Composer install boundary plus any existing Composer test script is already the honest first draft.

Example

java-maven

Seeds setup, build, and test for Maven. Wrapper-aware: prefers ./mvnw when present, otherwise seeds the global Maven prerequisite. Does not infer multi-module reactor details or plugin-specific goals.

When to use it

Use this when the repo is intentionally Maven-based and wrapper-or-global Maven is already the honest execution boundary.

Example

java-gradle

Seeds setup, build, and test for Gradle. Wrapper-aware: prefers ./gradlew when present, otherwise seeds the global Gradle prerequisite. Does not infer multi-project build logic or custom Gradle task structure.

When to use it

Use this when the repo is intentionally Gradle-based and wrapper-or-global Gradle is already the honest execution boundary.

Example

Pack-specific knobs

Only add a knob when the boundary is truly conventional. The goal is explicit starter control, not open-ended templating.

  • the Node pack keeps toolchains.node ownership and changes package-manager lane details from the selected manager
  • the Python pack keeps toolchains.python ownership and changes the seeded uv-run test task and description to the selected test runner
  • packs that do not expose knobs stay fixed on one explicit conventional path
Node and Python knobsbash
ota init --pack node --package-manager npm --dry-runota init --pack node --package-manager pnpm --dry-runota init --pack node --package-manager yarn --dry-runota init --pack node --package-manager bun --dry-runota init --pack python --test-runner pytest --dry-runota init --pack python --test-runner unittest --dry-run

What packs do not infer

Starter packs are explicit templates, not hidden repo-specific synthesis.

The catalog now says this directly in both text and JSON so humans and automation can see where review and follow-up editing still matter.

  • Node does not infer the repo's package manager unless --package-manager says so, and it does not absorb custom script names beyond the seeded setup/dev/test loop
  • Python stays uv-managed in the starter and does not infer repo-specific dependency-group layout or repo-specific test layout beyond the selected test runner
  • Go, Rust, and Dotnet stay on their conventional module, Cargo, or restore/build/test loop; they do not infer workspace structure, code generation, feature flags, solution-level target selection, or custom CLI aliases
  • Php-composer stays on composer install and only reuses scripts.test when it already exists; it does not invent a framework-specific PHP test entrypoint for you
  • Java Maven and Gradle packs stay on the standard wrapper-or-global build/test path; they do not infer multi-module or multi-project build logic, plugin goals, or org-specific bootstrap scripts

Java wrapper behavior

The Java packs are wrapper-aware on purpose.

A Maven or Gradle repo that already ships its own wrapper should not be forced onto a global host tool assumption just because the pack is explicit.

  • if mvnw exists, java-maven uses ./mvnw commands and does not seed the global maven tool or maven-installed check
  • if gradlew exists, java-gradle uses ./gradlew commands and does not seed the global gradle tool or gradle-installed check
  • if the wrapper is absent, the starter seeds the global tool requirement and matching precondition check because the repo has no wrapper-owned execution path yet
  • the explicit dry-run preview is the authoritative view once ota has seen the repo, because wrapper presence changes the final Java starter details

Advisory mismatch behavior

Explicit pack choice stays authoritative, but ota can still tell you when the repo signals point somewhere else.

The advisory path is read-only. It does not auto-switch packs and it does not merge detector output into the chosen pack.

  • advisories appear only when one other pack is the clear stronger match from distinct repo signals
  • incidental signals on the selected pack do not suppress a stronger advisory anymore
  • human text output now keeps the pack choice explicit while adding Why, weighted Signals, Selected signals, Strength, Gap, and Next rows for the review step
  • detection failures do not block ota init --pack ...; the explicit starter path still previews or writes
Text advisorybash
ota init --pack python --dry-run
JSON advisory shapejson
{  pack: "python",  pack_advisory: {    selected_pack: "python",    suggested_pack: "node",    selected_pack_score: 0,    suggested_pack_score: 3,    score_gap: 3,    signals: ["package.json"],    signal_details: [      {        signal: "package.json",        weight: 3      }    ],    selected_signal_details: [],    next: "ota init --pack node --dry-run ."  }}

JSON surfaces

Use JSON when automation or hosted tooling needs the same starter-pack truth the CLI is showing to a human.

  • mode: "catalog" plus packs comes from ota init --packs --json
  • each catalog entry includes identity fields, the exact command to run, safe next steps, explicit inference boundaries, and the seeded contract surface
  • pack appears on ota init --json when an explicit pack seeded the starter contract
  • pack_options appears when a selected Node package manager or Python test runner changes the seeded starter
  • pack_advisory appears only when strong detected repo signals disagree with the chosen pack without overriding it, and now includes score gap plus weighted-signal detail fields for both the suggested and selected pack

What this page is not

  • not a new top-level command surface separate from ota init
  • not detector-led init with prettier copy
  • not hidden auto-detection that silently switches your chosen pack
  • not a template marketplace or arbitrary starter generator