Documentation
¶
Overview ¶
Package setup owns clawtool's project-setup layer: the recipe framework, the wizard, and the .clawtool.toml repo-scoped config.
The user-facing CLI verb is `clawtool init`, but the internal package is named `setup` to avoid colliding with Go's reserved init() lifecycle function and to read clearly in imports.
Recipes are organized into 9 fixed categories. The category set is part of clawtool's public API — at v1.0 it freezes. Adding a category is a major bump; adding recipes within a category is always free.
Index ¶
- Constants
- Variables
- func AllSatisfied(outcomes []PrereqOutcome) bool
- func CategoryDescriptions() map[Category]string
- func FileExists(path string) (bool, error)
- func GetOption[T any](o Options, key string) (T, bool)
- func HasMarker(content []byte, marker string) bool
- func IsForced(o Options) bool
- func ReadIfExists(path string) ([]byte, error)
- func Register(r Recipe)
- func WriteAtomic(path string, content []byte, mode os.FileMode) error
- type AlwaysInstall
- type AlwaysSkip
- type ApplyOptions
- type ApplyResult
- type Category
- type ClawtoolMeta
- type CommandRunner
- type Options
- type Platform
- type Prereq
- type PrereqOutcome
- type PromptDecision
- type Prompter
- type Recipe
- type RecipeEntry
- type RecipeMeta
- type Registry
- type RepoConfig
- func (c *RepoConfig) FindRecipe(name string) *RecipeEntry
- func (c *RepoConfig) HasRecipe(name string) bool
- func (c *RepoConfig) RecipesByCategory() map[Category][]RecipeEntry
- func (c *RepoConfig) RemoveRecipe(name string)
- func (c *RepoConfig) Save(repoRoot string) error
- func (c *RepoConfig) UpsertRecipe(entry RecipeEntry)
- type Stability
- type Status
Constants ¶
const ForceOption = "force"
ForceOption is the canonical key recipes consult to decide whether to overwrite a file that exists but is not clawtool- managed. The wizard surfaces this as an "overwrite anyway?" prompt; the CLI exposes it via `--force` (which parseKV translates to opts[force]=true).
const ManagedByMarker = "managed-by: clawtool"
ManagedByMarker is the canonical marker every clawtool-generated file embeds (typically as a YAML/TOML/HTML comment). Detect and Apply both check for it before refusing to touch unmanaged files.
const RepoConfigName = ".clawtool.toml"
RepoConfigName is the canonical filename. Lives in the repo root, committed to git. Keeps the user-global config (~/.config/clawtool) uncoupled from per-repo state.
Variables ¶
var ErrSkippedByUser = errors.New("recipe skipped by user")
ErrSkippedByUser is returned when the prompter votes PromptSkip for a missing prereq. Carries through ApplyResult.SkipReason.
Functions ¶
func AllSatisfied ¶
func AllSatisfied(outcomes []PrereqOutcome) bool
AllSatisfied is the convenience predicate over PrereqCheck output.
func CategoryDescriptions ¶
CategoryDescriptions surfaces the one-line description shown in the wizard category header and in `recipe_categories()` MCP output. Kept here, not on each recipe, because the category boundary is a contract, not a recipe-author concern.
func FileExists ¶
FileExists is the boolean predicate. Returns (false, err) on fs errors that aren't IsNotExist so callers can surface them.
func GetOption ¶
Get is a typed helper that returns the option's value as T or the zero value if missing. Recipes use this instead of touching the map directly so the keys stay grep-able.
func HasMarker ¶
HasMarker reports whether content includes the given marker substring. Recipes use this to label generated files ("managed-by: clawtool") and refuse to overwrite anything without the marker.
func IsForced ¶
IsForced reports whether opts requests an overwrite of unmanaged files. Defaults to false. Recipes call this in their Apply to gate the "exists but no marker → refuse" check.
func ReadIfExists ¶
ReadIfExists returns nil bytes + nil error when the file is absent. Recipes use this in Detect to fingerprint existing content without juggling not-found errors.
func Register ¶
func Register(r Recipe)
Register adds r to the global registry. Panics on:
- empty/duplicate Name
- unknown Category
- empty Upstream (wrap-don't-reinvent enforcement)
- empty Description
Always called from a recipe package's init(); panics fail the binary at boot, not at the user's first run.
func WriteAtomic ¶
WriteAtomic writes content to path via temp+rename so a crash mid- write never leaves the user with a half-finished file. Recipes use this for every file mutation; mode is typically 0o644 for repo files, 0o755 for scripts. Thin wrapper over atomicfile.WriteFileMkdir so all 94 recipe callsites share the project-wide canonical helper — one place to tune crash-window invariants going forward.
Types ¶
type AlwaysInstall ¶
type AlwaysInstall struct{}
AlwaysInstall is the non-interactive Prompter. Used by `clawtool init --yes` and by the MCP recipe_apply tool when the caller passed `auto_install: true`.
func (AlwaysInstall) OnMissingPrereq ¶
func (AlwaysInstall) OnMissingPrereq(context.Context, Recipe, Prereq, error) (PromptDecision, error)
type AlwaysSkip ¶
type AlwaysSkip struct{}
AlwaysSkip refuses installation. The MCP path uses it as a default safety guard when `auto_install` is false: a missing prereq surfaces as an error to Claude rather than silently shelling out `apt install`.
func (AlwaysSkip) OnMissingPrereq ¶
func (AlwaysSkip) OnMissingPrereq(context.Context, Recipe, Prereq, error) (PromptDecision, error)
type ApplyOptions ¶
type ApplyOptions struct {
// Repo is the absolute path to the target repository.
Repo string
// RecipeOptions is the per-recipe parameter bag (vault path,
// license SPDX id, etc.).
RecipeOptions Options
// Prompter handles missing prereqs. Required.
Prompter Prompter
// Runner executes install commands. Required if any prereq
// has an Install entry.
Runner CommandRunner
}
ApplyOptions bundles everything Apply needs that isn't recipe- specific Options.
type ApplyResult ¶
type ApplyResult struct {
Recipe string
Category Category
Skipped bool
SkipReason string
Installed []string // prereq names that were auto-installed
ManualHints []string // prereq names where the user picked manual
UpstreamUsed string
VerifyOK bool
VerifyErr error
}
ApplyResult captures what a single recipe Apply call did. Used by the wizard's summary screen and by recipe_apply MCP output.
func Apply ¶
func Apply(ctx context.Context, recipe Recipe, ao ApplyOptions) (ApplyResult, error)
Apply runs the full apply sequence for one recipe:
- Detect (skip if already StatusApplied and ApplyOptions.Force is false; force is wired in v0.10).
- Prereqs: check each, prompt on missing, install if accepted.
- Recipe.Apply.
- Recipe.Verify (post-condition; non-fatal warning if it fails).
Returns the ApplyResult either way; errors are returned alongside (Result.Skipped + non-nil err on user-skip; Result.VerifyErr + nil err on apply-ok-but-verify-failed).
type Category ¶
type Category string
Category is the typed enum for recipe grouping. Defined as an exhaustive set so a recipe authored against a category that doesn't exist literally cannot compile.
const ( // CategoryGovernance covers files & policies that govern // collaboration: LICENSE, CODEOWNERS, CONTRIBUTING.md, // SECURITY.md, issue/PR templates, code of conduct. CategoryGovernance Category = "governance" // CategoryCommits covers commit-time discipline: format // conventions, message linting, pre-commit hooks, secret // scanning at commit time. CategoryCommits Category = "commits" // CategoryRelease covers version cutting + publishing: release // automation, changelog generation, artifact distribution. CategoryRelease Category = "release" // CategoryCI covers PR/push pipeline scaffolding: test runners, // build matrix, coverage uploads. CategoryCI Category = "ci" // CategoryQuality covers code quality enforcement: linters, // formatters, type checkers, test scaffolds. CategoryQuality Category = "quality" // CategorySupplyChain covers dependencies & security: // dependency updates, SBOM generation, vulnerability scanning. CategorySupplyChain Category = "supply-chain" // CategoryKnowledge covers project memory & docs: brain, // ADR tooling, documentation sites, changelog tooling. CategoryKnowledge Category = "knowledge" // CategoryAgents covers AI agent integration: agent claims, // project-scoped MCP sources, skill bindings. clawtool's USP. CategoryAgents Category = "agents" // CategoryRuntime covers dev environment & containers: // devcontainers, Docker, Nix, direnv, mise. CategoryRuntime Category = "runtime" )
func Categories ¶
func Categories() []Category
Categories returns the categories in repo-maturity walk order (the order the wizard surfaces them). Frozen at v1.0.
type ClawtoolMeta ¶
type ClawtoolMeta struct {
// Version is the clawtool semver that wrote this file.
Version string `toml:"version"`
}
ClawtoolMeta is the toolchain stamp. Helps future migrations know which schema version they're reading.
type CommandRunner ¶
CommandRunner is the abstraction for executing prereq install commands. Real callers pass an exec.Command-backed implementation; tests pass a recording fake. Returning an error aborts the recipe.
type Options ¶
Options is the per-Apply parameter bag. Free-form because each recipe has its own knobs (vault path, license SPDX id, default branch). The wizard fills these via prompts; the MCP surface fills them via the recipe_apply call's arguments map.
type Platform ¶
type Platform string
Platform is what we dispatch installer commands on. Kept narrow: linux/darwin/windows. ARM-vs-x86 is the recipe's problem if it matters.
func CurrentPlatform ¶
func CurrentPlatform() Platform
CurrentPlatform returns the host's Platform. Recipes consult this when picking install commands; runtime/setup callers use it to route prereq install offers.
type Prereq ¶
type Prereq struct {
// Name is human-readable: "Node.js 18+", "GitHub CLI", "Obsidian".
Name string
// Check returns nil if the prereq is satisfied. Implementations
// typically exec.LookPath the binary or shell out a version
// probe.
Check func(ctx context.Context) error
// Install is the command (or commands) clawtool offers to run
// if Check fails and the user consents. Empty means "manual
// install only" — the wizard will print ManualHint instead.
Install map[Platform][]string
// ManualHint is shown when no Install entry matches the host
// platform or when the user picks the manual route. One short
// paragraph; usually a URL plus one-liner.
ManualHint string
}
Prereq describes one external dependency the recipe needs before Apply can run. The wizard surfaces missing prereqs and offers to install them via the platform-canonical command.
type PrereqOutcome ¶
PrereqOutcome is the per-prereq state after a check.
func PrereqCheck ¶
func PrereqCheck(ctx context.Context, recipe Recipe) []PrereqOutcome
PrereqCheck runs Check() against every prereq and returns the outcome list in declared order. Never returns an error itself — individual failures land in Outcome.Err.
type PromptDecision ¶
type PromptDecision int
PromptDecision is what an interactive prompter (TTY wizard or MCP-driven Claude) returns for one prerequisite.
const ( // PromptInstall: caller offered to install, user accepted. // Runner should run Prereq.Install for the current platform. PromptInstall PromptDecision = iota // PromptManual: user wants to install themselves; runner skips // the auto-install and emits ManualHint. PromptManual // PromptSkip: user said skip this recipe entirely. PromptSkip )
type Prompter ¶
type Prompter interface {
OnMissingPrereq(ctx context.Context, recipe Recipe, p Prereq, checkErr error) (PromptDecision, error)
}
Prompter is the abstraction over wizard / Claude / non-interactive auto modes. The runner calls it once per missing prereq.
PromptDefault is what `clawtool init --yes` and the MCP code path use: returns PromptInstall on every prereq.
type Recipe ¶
type Recipe interface {
// Meta returns the static descriptor. Called at registration
// (validated for required fields) and on every list call.
Meta() RecipeMeta
// Detect probes the repo at `repo` and returns the recipe's
// current state plus an explanation string the wizard prints.
// Returning StatusError surfaces the error verbatim.
Detect(ctx context.Context, repo string) (Status, string, error)
// Prereqs lists what must exist on the host before Apply is
// safe to call. Empty slice = no prereqs.
Prereqs() []Prereq
// Apply executes the recipe against repo with opts. May shell
// out, write files, fetch network resources. Atomic in the
// success case; on partial-fail the error wraps what was done
// so the user can recover.
Apply(ctx context.Context, repo string, opts Options) error
// Verify is the post-Apply sanity check, also re-runnable
// later. Returns nil if the recipe's installed state is
// healthy; otherwise an error describing what's missing.
Verify(ctx context.Context, repo string) error
}
Recipe is the single interface every entry implements.
func All ¶
func All() []Recipe
All returns every registered recipe, sorted by category then by name within category. Stable order so wizard output and tests don't flake.
func InCategory ¶
InCategory returns recipes for the given category in name order.
type RecipeEntry ¶
type RecipeEntry struct {
Name string `toml:"name"`
Category Category `toml:"category"`
AppliedAt time.Time `toml:"applied_at"`
UpstreamVersion string `toml:"upstream_version,omitempty"`
Options map[string]any `toml:"options,omitempty"`
}
RecipeEntry is one applied-recipe row. Schema is forward-only — new optional fields are free to add; renames need a migration.
type RecipeMeta ¶
type RecipeMeta struct {
// Name is kebab-case, unique within the category. Surfaced as
// the CLI selector and the MCP recipe_apply argument.
Name string
// Category is the typed enum. Registry refuses unknown values.
Category Category
// Description is one line shown in the wizard and recipe_list.
Description string
// Upstream is the canonical URL of the project this recipe
// wraps. REQUIRED — registry refuses an empty Upstream so a
// from-scratch reimplementation can't be smuggled in.
//
// Use the project's primary repo URL (e.g.
// "https://github.com/googleapis/release-please") or — for
// recipes that wrap a spec rather than a project — the spec
// URL with a "spec:" prefix (e.g.
// "spec:https://www.conventionalcommits.org").
Upstream string
// Stability defaults to StabilityStable if zero-valued.
Stability Stability
// Core marks a recipe as part of clawtool's curated default
// install. When true, the setup wizard pre-checks the row and
// `clawtool init --all` applies it without prompting (regardless
// of Stability — Beta recipes can be Core too). Defaults to
// false so unset / experimental recipes stay opt-in.
Core bool
}
RecipeMeta is what every recipe declares about itself. Every field except OptionalArgs is required at compile time — a recipe missing Upstream literally cannot be valid, which makes the wrap-don't-reinvent rule code-level enforced.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry is the in-process catalog of available recipes. Recipes register themselves at package init time via Register().
type RepoConfig ¶
type RepoConfig struct {
Clawtool ClawtoolMeta `toml:"clawtool"`
Recipes []RecipeEntry `toml:"recipe,omitempty"`
}
RepoConfig is the on-disk record of which recipes were applied to this repo. Recipes are stored flat (queryable, easy to diff) but the wizard groups them by category for human reading.
func LoadRepoConfig ¶
func LoadRepoConfig(repoRoot string) (*RepoConfig, error)
LoadRepoConfig reads .clawtool.toml from repoRoot. A missing file returns an empty config with no error — Apply paths can call AppendRecipe + Save without an explicit init step.
func (*RepoConfig) FindRecipe ¶
func (c *RepoConfig) FindRecipe(name string) *RecipeEntry
FindRecipe returns the entry for name, or nil.
func (*RepoConfig) HasRecipe ¶
func (c *RepoConfig) HasRecipe(name string) bool
HasRecipe reports whether a recipe with the given name has been recorded as applied. Wizard uses this to decide which checkboxes to pre-check.
func (*RepoConfig) RecipesByCategory ¶
func (c *RepoConfig) RecipesByCategory() map[Category][]RecipeEntry
RecipesByCategory groups c.Recipes by Category in walk order. Empty categories are not present in the result. Used by recipe_status MCP tool and the wizard's "already applied" header.
func (*RepoConfig) RemoveRecipe ¶
func (c *RepoConfig) RemoveRecipe(name string)
RemoveRecipe drops the entry with name from c. No-op if absent.
func (*RepoConfig) Save ¶
func (c *RepoConfig) Save(repoRoot string) error
Save writes c to repoRoot/.clawtool.toml atomically (temp+rename). Mode 0644 — the file is meant to be committed.
func (*RepoConfig) UpsertRecipe ¶
func (c *RepoConfig) UpsertRecipe(entry RecipeEntry)
UpsertRecipe records that recipe `name` was applied. If an entry already exists, AppliedAt is refreshed and Options/UpstreamVersion are replaced. If absent, a new entry is appended in alpha order.
type Stability ¶
type Stability string
Stability marks how settled a recipe is. The wizard hides experimental recipes by default; recipe_list({stability: "stable"}) is the default filter.
type Status ¶
type Status string
Status is what Detect() returns: the recipe's current state in the target repo.
const ( // StatusAbsent: no trace of the recipe in the repo. StatusAbsent Status = "absent" // StatusPartial: some artifacts exist but config is incomplete // or stale; Apply will reconcile. StatusPartial Status = "partial" // StatusApplied: recipe is fully configured and Verify passes. StatusApplied Status = "applied" // StatusError: detection itself failed (e.g. permission denied // reading a file). Treat as opaque; show the error to the user. StatusError Status = "error" )
Directories
¶
| Path | Synopsis |
|---|---|
|
Package recipes is a thin import-aggregator: it pulls in every recipe subpackage so their init() functions register with the global setup.Registry.
|
Package recipes is a thin import-aggregator: it pulls in every recipe subpackage so their init() functions register with the global setup.Registry. |
|
agentclaim
Package agentclaim hosts the AI-agent integration recipe — the `agents` category in clawtool's setup taxonomy.
|
Package agentclaim hosts the AI-agent integration recipe — the `agents` category in clawtool's setup taxonomy. |
|
agents
Package agents holds setup recipes that integrate external AI- agent harnesses (Archon, future entrants) with clawtool's playbook layer.
|
Package agents holds setup recipes that integrate external AI- agent harnesses (Archon, future entrants) with clawtool's playbook layer. |
|
bridges
Package bridges hosts the bridge recipes for the `agents` category — connectors from Claude Code to other coding-agent CLIs (Codex, OpenCode, Gemini).
|
Package bridges hosts the bridge recipes for the `agents` category — connectors from Claude Code to other coding-agent CLIs (Codex, OpenCode, Gemini). |
|
ci
Package ci hosts recipes for the `ci` category — PR/push pipeline scaffolding.
|
Package ci hosts recipes for the `ci` category — PR/push pipeline scaffolding. |
|
commits
Package commits hosts recipes for the `commits` category — commit- time discipline.
|
Package commits hosts recipes for the `commits` category — commit- time discipline. |
|
governance
Package governance hosts recipes for the `governance` category — files & policies that govern collaboration.
|
Package governance hosts recipes for the `governance` category — files & policies that govern collaboration. |
|
knowledge
Package knowledge hosts recipes for the `knowledge` category — project memory and documentation tooling.
|
Package knowledge hosts recipes for the `knowledge` category — project memory and documentation tooling. |
|
productivity
Package productivity hosts recipes that ship curated, daily-use playbooks for AI-assisted development.
|
Package productivity hosts recipes that ship curated, daily-use playbooks for AI-assisted development. |
|
quality
Package quality hosts recipes for the `quality` category — code quality enforcement (lint, format, test scaffolds).
|
Package quality hosts recipes for the `quality` category — code quality enforcement (lint, format, test scaffolds). |
|
release
Package release hosts recipes for the `release` category — version cutting and publishing automation.
|
Package release hosts recipes for the `release` category — version cutting and publishing automation. |
|
runtime
Package runtime hosts recipes for the `runtime` category — dev environment + container scaffolding.
|
Package runtime hosts recipes for the `runtime` category — dev environment + container scaffolding. |
|
sources
Package sources — recipes for AI source / gateway integrations.
|
Package sources — recipes for AI source / gateway integrations. |
|
supplychain
Package supplychain hosts recipes for the `supply-chain` category — dependency updates and security tooling.
|
Package supplychain hosts recipes for the `supply-chain` category — dependency updates and security tooling. |