Documentation
¶
Overview ¶
Package setup installs the Claude Code Skill bundle that ADR-0002 decided on, so that Claude Code knows when and how to invoke the syntropy binary.
This is deliberately Claude-only for now: ADR-0002 picked the Skill bundle as the first integration, not the only one. syntropy's Runner interface (ADR-0007) already anticipates other coding agents (Codex, Qwen, OpenHands, ...); adding one there needs a companion integration bundle in that agent's own distribution format, added alongside this package rather than folded into it.
Index ¶
- Constants
- Variables
- func EnsureClaudeSkill(home string) (bool, error)
- func InstallClaudeSkill(home string, force bool) (bool, error)
- func MissingFields(cfg RepoConfig) []string
- func RepoConfigPath(repoDir string) string
- func ResolveModel(flagModel, existing string, interactive bool, ...) (string, error)
- func ResolveRunner(flagRunner string) (string, error)
- func ResolveTitleConvention(flagConvention string, interactive bool, prompt func() (string, error)) (string, error)
- func SkillPath(home string) string
- func WriteRepoConfig(repoDir, convention string, force bool) (bool, error)
- type RepoConfig
Constants ¶
const BlankSentinel = "blank"
BlankSentinel is written to a RepoConfig field, instead of leaving it as an empty string or omitting it, when the user was explicitly asked and chose not to set a value. Plain "" is ambiguous between "never asked" and "asked, declined" — every field in this file needs a way to distinguish the two so an agent following the syntropy Skill knows whether to ask again, and so new fields added in a later syntropy version are correctly recognised as unasked (empty) even in an existing repo's .syntropy.yml, rather than mistaken for a deliberate blank.
Variables ¶
var KnownRunners = []string{"claude"}
KnownRunners lists the coding-agent runners `everflow setup` can offer as a default. Mirrors the runners actually registered in main() (ADR-0007) — "claude" is the only one today. Kept here rather than introspecting a live runner.Registry so setup can validate --runner and auto-select a default without constructing runners (and their subprocess dependencies) just to list their names.
Functions ¶
func EnsureClaudeSkill ¶
EnsureClaudeSkill installs ~/.claude/skills/syntropy/SKILL.md the first time it's called on a host that has Claude Code set up (~/.claude/ present). It's a no-op if Claude Code isn't installed, or if the skill file already exists — the file's own presence is the marker, so a user's local edits to it are never overwritten by a later invocation. It reports whether it installed the file, so callers can surface a one-time summary.
func InstallClaudeSkill ¶
InstallClaudeSkill installs the Skill bundle for the explicit `syntropy setup` command. Unlike EnsureClaudeSkill it doesn't require ~/.claude to already exist — an explicit setup request creates it. When force is true, an existing Skill file is overwritten; otherwise install is skipped and the existing file is left untouched.
func MissingFields ¶
func MissingFields(cfg RepoConfig) []string
MissingFields returns the names of every recognised RepoConfig field that hasn't been decided yet (see IsConfigured) — i.e. that an agent following the syntropy Skill should ask the user about. Pure, cheap, deterministic: no LLM invocation needed to answer "is this repo fully configured" (ADR-0083) — CheckRepoConfig/`syntropy config check` calls this so an agent can gate a conversational ask on its output instead of reading and reasoning about the YAML file itself every time.
Add a case here for every new field RepoConfig grows — this is the one place the "which fields exist" list needs to stay current.
func RepoConfigPath ¶
RepoConfigPath returns the `.syntropy.yml` path for the given repo root.
func ResolveModel ¶
func ResolveModel(flagModel, existing string, interactive bool, prompt func(existing string) (string, error)) (string, error)
ResolveModel picks the default model `everflow setup` should persist. Precedence: --model flag, then (if interactive) the prompt's answer, then the existing persisted value — so a non-interactive re-run (no TTY, no flag; e.g. from a script or cron) leaves a previously configured model untouched rather than clobbering it back to empty.
prompt is called only when flagModel is empty and interactive is true; it receives the existing value to show as the default and returns the raw (untrimmed-by-caller) line the user typed, or an error reading stdin.
func ResolveRunner ¶
ResolveRunner picks the runner `everflow setup` should persist as the default. flagRunner is whatever --runner was passed (empty if not set).
With only one KnownRunners entry, an unset flag auto-selects it — there's nothing to choose between yet. A set flag is validated against KnownRunners so a typo (or a not-yet-supported agent name) fails loudly instead of getting silently persisted.
func ResolveTitleConvention ¶
func ResolveTitleConvention(flagConvention string, interactive bool, prompt func() (string, error)) (string, error)
ResolveTitleConvention picks the free-text title convention `syntropy setup` should write to `.syntropy.yml`. Precedence: --title-convention flag, then (if interactive) the prompt's answer, then empty — a non-interactive run with no flag makes no claim about a convention rather than guessing one.
prompt is called only when flagConvention is empty and interactive is true; it returns the raw line the user typed, or an error reading stdin.
func SkillPath ¶
SkillPath returns the path where the Claude Code Skill bundle lives under the given home directory.
func WriteRepoConfig ¶
WriteRepoConfig writes `.syntropy.yml` into repoDir with the given title convention. It's a no-op (returns false, nil) when convention is empty — there's nothing to persist — or when the file already exists and force is false, so a user's local edits to it are never clobbered by a later `syntropy setup` run.
Types ¶
type RepoConfig ¶
type RepoConfig struct {
// TitleConvention is free-text guidance on how this repo likes its
// PR/MR titles phrased, e.g. "Conventional Commits" or "ticket ID
// prefix like PROJ-123: ...". Empty means the field is missing from
// the file entirely — this repo has never been asked (e.g. an older
// .syntropy.yml predating this field, or the file doesn't exist at
// all) — an agent following the syntropy Skill should ask the user
// and persist an answer. BlankSentinel means the user WAS asked and
// deliberately chose not to set one — don't ask again, and don't
// treat it as a convention to inject anywhere (see
// EffectiveTitleConvention).
TitleConvention string `yaml:"title_convention"`
}
RepoConfig is the on-disk shape of `.syntropy.yml`, a per-repo (not per-user) config file living at the root of a spec's `base_repo`.
func ReadRepoConfig ¶
func ReadRepoConfig(repoDir string) (RepoConfig, error)
ReadRepoConfig reads `.syntropy.yml` from repoDir, returning a zero-value RepoConfig (no error) if the file doesn't exist — absence means "no convention set", not a failure. Mirrors WriteRepoConfig's file-presence convention (ADR-0052).
func (RepoConfig) EffectiveTitleConvention ¶
func (c RepoConfig) EffectiveTitleConvention() string
EffectiveTitleConvention returns the value to actually use when building a runner prompt: the real convention if one was set, or "" for both "never asked" and BlankSentinel — callers that gate a prompt block on non-empty (e.g. the runner's `if req.TitleConvention != ""`) should call this rather than reading TitleConvention directly, so BlankSentinel never leaks into prompt text verbatim.
func (RepoConfig) IsConfigured ¶
func (c RepoConfig) IsConfigured() bool
IsConfigured reports whether TitleConvention has been decided at all — either a real convention or an explicit BlankSentinel — as opposed to being absent, which means an agent following the syntropy Skill should ask the user for it.